diff options
Diffstat (limited to 'mac')
57 files changed, 29448 insertions, 0 deletions
diff --git a/mac/AppleScript.html b/mac/AppleScript.html new file mode 100644 index 0000000..32b2e9f --- /dev/null +++ b/mac/AppleScript.html @@ -0,0 +1,312 @@ +<HTML> + +<HEAD> + +<TITLE>tclOSAScript -- OSA</TITLE> + +</HEAD> + +<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#FF0000" ALINK="#00FF00"> + +<H2 ALIGN="CENTER">TclAppleScript Extension Command</H2> + +<H3>NAME</H3> +<DL> +<DT> +AppleScript - Communicate with the AppleScript OSA component to run + AppleScripts from Tcl. +</DL> +<H3>SYNOPSIS</H3> +<DL><DT> +<B>AppleScript <A NAME="compile">compile</A> </B><I>?-flag value?</I> <I>scriptData1 + ?ScriptData2 ...?</I><I>componentName</I> +<BR> +<B>AppleScript <A NAME="decompile">decompile</A></B> <I>scriptName</I> +<BR> +<B>AppleScript <A NAME="delete">delete</A> </B><I>what scriptName</I> +<BR> +<B>AppleScript <A NAME="execute">execute</A> </B><I>?flags value?</I> <I>scriptData1 + ?scriptData2 ...?</I> +<BR> +<B>AppleScript <A NAME="info">info</A> </B><I>what</I> +<BR> +<B>AppleScript <A NAME="load">load</A></B> <I>?flag value? fileName</I> +<BR> +<B>AppleScript <A NAME="run">run</A></B> <I>?flag value?</I> + <I>scriptName</I> +<BR> +<B>AppleScript <A NAME="store">store</A></B> <I>?flag value? scriptName fileName</I> +<BR> +</DL> + +<H3>DESCRIPTION</H3> +<DL> +<DT> + + +This command is used to communicate with the AppleScript OSA component. +You can <A HREF="#compile"><B>compile</B></A> scripts, <A +HREF="#run"><B>run</B></A> compiled scripts, <A +HREF="#execute"><B>execute</B></A> script data (i.e. compile and run at a +blow). You can get script data from a compiled script (<A +HREF="#decompile"><B>decompile</B></A> it), and you can <A +HREF="#load"><B>load</B></A> a compiled script from the scpt resource of a +file, or <A HREF="store"><B>store</B></A> one to a scpt resource. You can +also get <A HREF="#info"><B>info</B></A> on the currently available scripts +and contexts. It has the general form + +<DL> +<DT> +<P> +<I>AppleScript option ?arg arg ...?</I> +<P> +</DL> +The possible sub-commands are: +<P> +<DL> + <DT> + <I>AppleScript</I> <A NAME="compile"><B>compile</A> </B><I>?-flag value?</I> <I>scriptData1 + ?ScriptData2 ...?</I> + <BR> + + <DD> + The scriptData + elements are concatenated (with a space between each), and + sent to AppleScript + for compilation. There is no limitation on the size of + the scriptData, beyond the available memory of the Wish interpreter. + <P> + If the compilation is successful, then the command will return a token + that you can pass to the <A HREF="#run">"run"</A> subcommand. If the + compilation fails, then the return value will be the error message from + AppleScript, and the pertinent line of code, with an "_" to indicate + the place where it thinks the error occured. + <P> + The + compilation is controlled by flag value pairs. The available flags + are: + <P> + <DL> + <DT> + <A NAME="first compile switch"><B>-augment Boolean</B></A> + <DD> + To be used in concert with the <A HREF="#-context">-context</A> flag. + If augment is yes, + then the scriptData augments the handlers and data already in the + script context. If augment is no, then the scriptData replaces the + data and handlers already in the context. The default is yes. + <P> + <!-- I'm leaving this flag out for now, since I can't seem to get the + AE manager to obey it. Even when I hard code the value, applications + still switch to the foreground. Oh, well... + + <DT> + <B>-canswitch Boolean </B> + <DD> + If yes, then applications activated by the code in scriptData will + be allowed to switch to the foreground. If no, then they will use + the notification manager to indicate they need attention (this + usually means they blink the Finder icon, and put a check in the + application's entry in the Finder menu). + --> + + <DT> + <B><A NAME="-context">-context</A> Boolean</B> + <DD> + This flag causes the code given in the scriptData to be compiled + into a "context". In AppleScript, this is the equivalent of creating an Tcl + Namespace. The command in this case returns the name of the context as + the its result, rather than a compiled script name. + <P> + You can store data and procedures (aka + handlers) in a script context. Then later, you can + run other scripts in this context, and they will see all the data and + handlers that were set up with this command. You do this by passing the + name of this context to the -context flag of the run or execute subcommands. + <P> + Unlike the straight compile command, the code compiled into a + script context is run immediatly, when it is compiled, to set up the context. + <DT> + <P> + <B>-name string</B> + <DD> + Use <I>string</I> as the name of the script or script context. If there is + already a script + of this name, it will be discarded. The same is true with script + contexts, unless the <I>-augment</I> flag is true. If no name is provided, then a + unique name will be created for you. + <DT> + <P> + <B>-parent contextName </B> + <DD> + This flag is also to be used in conjunction with the <A HREF="#-context">-context</A> flag. + <I>contextName</I> must be the name of a compiled script context. Then + the new script context will inherit the data and handlers from the + parent context. + </DL> + <P> + <DT> + <I>AppleScript</I> <B><A NAME="decompile">decompile</A></B> <I>scriptName</I> + <BR> + <DD> + This decompiles the script data compiled into the script scriptName, + and returns the source code. + <P> + <DT> + <I>AppleScript</I> <B><A NAME="delete">delete</A> </B><I>what scriptName</I> + <BR> + <DD> + This deletes contexts or script data. The allowed values for "what" are: + <P> + <DL> + <DT> + <P> + <B>context</B> + <DD> + This deletes the context scriptName, + and frees up all the resources associated with it. + <DT> + <P> + <B>script</B> + <DD> + This deletes the script data compiled into the script scriptName, + and frees up all the resources associated with it. + </DL> + <P> + <DT> + <I>AppleScript</I> <B><A NAME="execute">execute</A> </B><I>?flags value?</I> <I>scriptData1 + ?scriptData2 ...?</I> + <BR> + <DD> + This compiles and runs the script in scriptData (concatenating first), and + returns the results of the script execution. It is the same as doing + <I>compile</I> and then <I>run</I>, except that the compiled script is + immediately discarded. + <P> + <DT> + <I>AppleScript</I> <B><A NAME="info">info</A> </B><I>what</I> + <DD> + This gives info on the connection. The allowed values for "what" are: + <P> + <DL> + <DT> + <P> + <B>contexts </B> <I>?pattern?</I> + <DD> + This gives the list of the script contexts that have been. + If <I>pattern</I> is given, it only reports the contexts + that match this pattern. + <DT> + <!-- <P> + <B>language</B> + <DD> + Returns the language of this OSA component + <DT> + --> + <P> + <B>scripts</B> <I>?pattern?</I> + <DD> + This returns a list of the scripts that have been compiled in the + current connection. If <I>pattern</I> is given, it only reports the + script names that match this pattern. + </DL> + <P> + <DT> + <I>AppleScript</I> <B><A NAME="load">load</A></B> <I>?flag value? fileName</I> + <DD> + This loads compiled script data from a resource of type 'scpt' in the + file fileName, and returns a token for the script data. As with the + <I>compile</I> command, the script is not actually executed. Note that all + scripts compiled with Apple's "Script Editor" are stored as script + contexts. However, unlike with the "<I>compile -context</I>" command, the <I>load</I> + command does not run these scripts automatically. If you want to set up + the handlers contained in the loaded script, you must run it manually. + <P> + <I>load</I> takes the following flags: + <P> + <DL> + <DT> + <B>-rsrcname string</B> + <DD> + load a named resource of type 'scpt' using the rsrcname + flag. + <DT> + <P> + <B>-rsrcid integer</B> + <DD> + load a resource by number with the rsrcid flag. + </DL> + <DD> + <P> + If neither the <I>rsrcname</I> nor the <I>rsrcid</I> flag is provided, then the load + command defaults to -rsrcid = 128. This is the resource in which + Apple's Script Editor puts the script data when it writes out a + compiled script. + <P> + <DT> + <I>AppleScript</I> <B><A NAME="run">run</A></B> <I>?flag value?</I> <I>scriptName</I> + <DD> + This runs the script which was previously compiled into <I>scriptName</I>. If the script + runs successfully, the command returns the return value for this command, + coerced to a text string. + If there is an error in + the script execution, then it returns the error result from the + scripting component. It accepts the following flag: + + <DL> + <DT> + <P> + <B>-context contextName</B> + <DD> + <I>contextName</I> must be a context created by a previous call to <I>compile</I> with + the -<I>context</I> flag set. This flag causes the code given in the + <I>scriptData</I> to be run in this "context". It will see all the data and + handlers that were set up previously. + <!-- <DT> + <B>-canswitch Boolean </B> + <DD> + If yes, then applications activated by the code + in scriptData will be allowed to switch to the foreground. If no, then + they will use the notification manager to indicate they need attention + (this usually means they blink the Finder icon, and put a check in the + application's entry in the Finder menu). --> + </DL> + <P> + <DT> + <I>AppleScript </I> <B> <A NAME="store">store</A></B> <I>?flag value? scriptName fileName</I> + <DD> + This stores a compiled script or script context into a resource of type 'scpt' in the + file fileName. + <P> + store takes the following flags: + <P> + <DL> + <DT> + <B>-rsrcname string</B> + <DD> + store to a named resource of type 'scpt' using the rsrcname + flag. + <DT> + <P> + <B>-rsrcid integer</B> + <DD> + store to a numbered resource with the rsrcid flag. + </DL> + <P> + <DD> + If neither the rsrcname nor the rsrcid flag is provided, then the load + command defaults to -rsrcid = 128. Apple's Script Editor can read in files written by + tclOSAScript with this setting of the <I>-rsrcid</I> flag. +</DL> +</DL> +<H2>Notes:</H2> + +The AppleScript command is a stopgap command to fill the place of exec + on the Mac. It is not a supported command, and will likely change + as we broaden it to allow communication with other OSA languages. +<H2>See Also:</H2> + + +</BODY> + +</HTML> diff --git a/mac/Background.doc b/mac/Background.doc new file mode 100644 index 0000000..d23235a --- /dev/null +++ b/mac/Background.doc @@ -0,0 +1,92 @@ +Notes about the Background Only application template +==================================================== + +RCS: @(#) $Id: Background.doc,v 1.2 1998/09/14 18:40:03 stanton Exp $ + +We have included sample code and project files for making a Background-Only + application (BOA) in Tcl. This could be used for server processes (like the +Tcl Web-Server). + +Files: +------ + +* BOA_TclShells.¼ - This is the project file. +* tclMacBOAAppInit.c - This is the AppInit file for the BOA App. +* tclMacBOAMain - This is a replacement for the Tcl_Main for BOA's. + +Caveat: +------- + +This is an unsupported addition to MacTcl. The main feature that will certainly +change is how we handle AppleEvents. Currently, all the AppleEvent handling is +done on the Tk side, which is not really right. Also, there is no way to +register your own AppleEvent handlers, which is obviously something that would be +useful in a BOA App. We will address these issues in Tcl8.1. If you need to +register your own AppleEvent Handlers in the meantime, be aware that your code +will probably break in Tcl8.1. + +I will also improve the basic code here based on feedback that I recieve. This +is to be considered a first cut only at writing a BOA in Tcl. + +Introduction: +------------- + +This project makes a double-clickable BOA application. It obviously needs +some Tcl code to get it started. It will look for this code first in a +'TEXT' resource in the application shell whose name is "bgScript.tcl". If +it does not find any such resource, it will look for a file called +bgScript.tcl in the application's folder. Otherwise it will quit with an +error. + +It creates three files in the application folder to store stdin, stdout & +stderr. They are imaginatively called temp.in, temp.out & temp.err. They +will be opened append, so you do not need to erase them after each use of +the BOA. + +The app does understand the "quit", and the "doScript" AppleEvents, so you can +kill it with the former, and instruct it with the latter. It also has an +aete, so you can target it with Apple's "Script Editor". + +For more information on Macintosh BOA's, see the Apple TechNote: 1070. + +Notifications: +-------------- + +BOA's are not supposed to have direct contact with the outside world. They +are, however, allowed to go through the Notification Manager to post +alerts. To this end, I have added a Tcl command called "bgnotify" to the +shell, that simply posts a notification through the notification manager. + +To use it, say: + +bgnotify "Hi, there little buddy" + +It will make the system beep, and pop up an annoying message box with the +text of the first argument to the command. While the message is up, Tcl +is yielding processor time, but not processing any events. + +Errors: +------- + +Usually a Tcl background application will have some startup code, opening +up a server socket, or whatever, and at the end of this, will use the +vwait command to kick off the event loop. If an error occurs in the +startup code, it will kill the application, and a notification of the error +will be posted through the Notification Manager. + +If an error occurs in the event handling code after the +vwait, the error message will be written to the file temp.err. However, +if you would like to have these errors post a notification as well, just +define a proc called bgerror that takes one argument, the error message, +and passes that off to "bgnotify", thusly: + +proc bgerror {mssg} { + bgnotify "A background error has occured\n $mssg" +} + +Support: +-------- + +If you have any questions, contact me at: + +jim.ingham@eng.sun.com diff --git a/mac/MW_TclAppleScriptHeader.h b/mac/MW_TclAppleScriptHeader.h new file mode 100755 index 0000000..6ce3853 --- /dev/null +++ b/mac/MW_TclAppleScriptHeader.h @@ -0,0 +1,7 @@ +#if __POWERPC__ +#include "MW_TclAppleScriptHeaderPPC" +#elif __CFM68K__ +#include "MW_TclAppleScriptHeaderCFM68K" +#else +#include "MW_TclAppleScriptHeader68K" +#endif diff --git a/mac/MW_TclAppleScriptHeader.pch b/mac/MW_TclAppleScriptHeader.pch new file mode 100644 index 0000000..12fec3f --- /dev/null +++ b/mac/MW_TclAppleScriptHeader.pch @@ -0,0 +1,36 @@ +/* + * MW_TclAppleScriptHeader.pch -- + * + * This file is the source for a pre-compilied header that gets used + * for TclAppleScript. This make compilies go a bit + * faster. This file is only intended to be used in the MetroWerks + * CodeWarrior environment. It essentially acts as a place to set + * compiler flags. See MetroWerks documention for more details. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: MW_TclAppleScriptHeader.pch,v 1.5 2001/11/23 01:26:17 das Exp $ + */ + +/* + * To use the compilied header you need to set the "Prefix file" in + * the "C/C++ Language" preference panel to point to the created + * compilied header. The name of the header depends on the + * architecture we are compiling for (see the code below). For + * example, for a 68k app the prefix file should be: MW_TclHeader68K. + */ + +#if __POWERPC__ +#pragma precompile_target "MW_TclAppleScriptHeaderPPC" +#elif __CFM68K__ +#pragma precompile_target "MW_TclAppleScriptHeaderCFM68K" +#else +#pragma precompile_target "MW_TclAppleScriptHeader68K" +#endif + +#include "tclMacCommonPch.h" + +#define USE_TCL_STUBS diff --git a/mac/MW_TclBuildLibHeader.h b/mac/MW_TclBuildLibHeader.h new file mode 100644 index 0000000..f6a6f61 --- /dev/null +++ b/mac/MW_TclBuildLibHeader.h @@ -0,0 +1,7 @@ +#if __POWERPC__ +#include "MW_TclBuildLibHeaderPPC" +#elif __CFM68K__ +#include "MW_TclBuildLibHeaderCFM68K" +#else +#include "MW_TclBuildLibHeader68K" +#endif diff --git a/mac/MW_TclBuildLibHeader.pch b/mac/MW_TclBuildLibHeader.pch new file mode 100644 index 0000000..a727451 --- /dev/null +++ b/mac/MW_TclBuildLibHeader.pch @@ -0,0 +1,35 @@ +/* + * MW_TclBuildLibHeader.pch -- + * + * This file is the source for a pre-compilied header that gets used + * for all files in the Tcl projects. This make compilies go a bit + * faster. This file is only intended to be used in the MetroWerks + * CodeWarrior environment. It essentially acts as a place to set + * compiler flags. See MetroWerks documention for more details. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id$ + */ + +/* + * To use the compilied header you need to set the "Prefix file" in + * the "C/C++ Language" preference panel to point to the created + * compilied header. The name of the header depends on the + * architecture we are compiling for (see the code below). For + * example, for a 68k app the prefix file should be: MW_TclHeader68K. + */ +#if __POWERPC__ +#pragma precompile_target "MW_TclBuildLibHeaderPPC" +#elif __CFM68K__ +#pragma precompile_target "MW_TclBuildLibHeaderCFM68K" +#else +#pragma precompile_target "MW_TclBuildLibHeader68K" +#endif + +#define BUILD_tcl 1 + +#include "MW_TclHeaderCommon.h" diff --git a/mac/MW_TclHeader.h b/mac/MW_TclHeader.h new file mode 100755 index 0000000..43a9029 --- /dev/null +++ b/mac/MW_TclHeader.h @@ -0,0 +1,7 @@ +#if __POWERPC__ +#include "MW_TclHeaderPPC" +#elif __CFM68K__ +#include "MW_TclHeaderCFM68K" +#else +#include "MW_TclHeader68K" +#endif diff --git a/mac/MW_TclHeader.pch b/mac/MW_TclHeader.pch new file mode 100644 index 0000000..14bf41a --- /dev/null +++ b/mac/MW_TclHeader.pch @@ -0,0 +1,33 @@ +/* + * MW_TclHeader.pch -- + * + * This file is the source for a pre-compilied header that gets used + * for all files in the Tcl projects. This make compilies go a bit + * faster. This file is only intended to be used in the MetroWerks + * CodeWarrior environment. It essentially acts as a place to set + * compiler flags. See MetroWerks documention for more details. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: MW_TclHeader.pch,v 1.8 2001/11/23 01:26:20 das Exp $ + */ + +/* + * To use the compilied header you need to set the "Prefix file" in + * the "C/C++ Language" preference panel to point to the created + * compilied header. The name of the header depends on the + * architecture we are compiling for (see the code below). For + * example, for a 68k app the prefix file should be: MW_TclHeader68K. + */ +#if __POWERPC__ +#pragma precompile_target "MW_TclHeaderPPC" +#elif __CFM68K__ +#pragma precompile_target "MW_TclHeaderCFM68K" +#else +#pragma precompile_target "MW_TclHeader68K" +#endif + +#include "MW_TclHeaderCommon.h" diff --git a/mac/MW_TclHeaderCommon.h b/mac/MW_TclHeaderCommon.h new file mode 100644 index 0000000..56ea59c --- /dev/null +++ b/mac/MW_TclHeaderCommon.h @@ -0,0 +1,54 @@ +/* + * MW_TclHeaderCommon.h -- + * + * Common includes for precompiled headers + * + * Copyright (c) 1998 by Scriptics Corporation. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id$ + */ + +#pragma once + +#include "tclMacCommonPch.h" + +/* + * Place any includes below that will are needed by the majority of the + * and is OK to be in any file in the system. + */ + +#include "tcl.h" + +#ifdef BUILD_tcl +# undef TCL_STORAGE_CLASS +# define TCL_STORAGE_CLASS DLLEXPORT +#endif +#include "tclMac.h" +#undef TCL_STORAGE_CLASS +#define TCL_STORAGE_CLASS DLLIMPORT + +#include "tclInt.h" + + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#include <MoreFiles.h> +#include <MoreFilesExtras.h> +#include <FSpCompat.h> +#include <FileCopy.h> +#include <FullPath.h> +#include <IterateDirectory.h> +#include <MoreDesktopMgr.h> +#include <DirectoryCopy.h> +#include <Search.h> + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif diff --git a/mac/MW_TclStaticHeader.h b/mac/MW_TclStaticHeader.h new file mode 100644 index 0000000..0c1abc2 --- /dev/null +++ b/mac/MW_TclStaticHeader.h @@ -0,0 +1,7 @@ +#if __POWERPC__ +#include "MW_TclStaticHeaderPPC" +#elif __CFM68K__ +#include "MW_TclStaticHeaderCFM68K" +#else +#include "MW_TclStaticHeader68K" +#endif diff --git a/mac/MW_TclStaticHeader.pch b/mac/MW_TclStaticHeader.pch new file mode 100644 index 0000000..f23021f --- /dev/null +++ b/mac/MW_TclStaticHeader.pch @@ -0,0 +1,35 @@ +/* + * MW_TclStaticHeader.pch -- + * + * This file is the source for a pre-compilied header that gets used + * for all files in the Tcl projects. This make compilies go a bit + * faster. This file is only intended to be used in the MetroWerks + * CodeWarrior environment. It essentially acts as a place to set + * compiler flags. See MetroWerks documention for more details. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id$ + */ + +/* + * To use the compilied header you need to set the "Prefix file" in + * the "C/C++ Language" preference panel to point to the created + * compilied header. The name of the header depends on the + * architecture we are compiling for (see the code below). For + * example, for a 68k app the prefix file should be: MW_TclHeader68K. + */ +#if __POWERPC__ +#pragma precompile_target "MW_TclStaticHeaderPPC" +#elif __CFM68K__ +#pragma precompile_target "MW_TclStaticHeaderCFM68K" +#else +#pragma precompile_target "MW_TclStaticHeader68K" +#endif + +#define STATIC_BUILD 1 + +#include "MW_TclHeaderCommon.h" diff --git a/mac/MW_TclTestHeader.h b/mac/MW_TclTestHeader.h new file mode 100755 index 0000000..c47bb97 --- /dev/null +++ b/mac/MW_TclTestHeader.h @@ -0,0 +1,7 @@ +#if __POWERPC__ +#include "MW_TclTestHeaderPPC" +#elif __CFM68K__ +#include "MW_TclTestHeaderCFM68K" +#else +#include "MW_TclTestHeader68K" +#endif diff --git a/mac/MW_TclTestHeader.pch b/mac/MW_TclTestHeader.pch new file mode 100755 index 0000000..dbb297a --- /dev/null +++ b/mac/MW_TclTestHeader.pch @@ -0,0 +1,41 @@ +/* + * MW_TclTestHeader.pch -- + * + * This file is the source for a pre-compilied header that gets used + * for all files in the Tcl projects. This make compilies go a bit + * faster. This file is only intended to be used in the MetroWerks + * CodeWarrior environment. It essentially acts as a place to set + * compiler flags. See MetroWerks documention for more details. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: MW_TclTestHeader.pch,v 1.2 2001/11/23 01:26:23 das Exp $ + */ + +/* + * To use the compilied header you need to set the "Prefix file" in + * the "C/C++ Language" preference panel to point to the created + * compilied header. The name of the header depends on the + * architecture we are compiling for (see the code below). For + * example, for a 68k app the prefix file should be: MW_TclHeader68K. + */ +#if __POWERPC__ +#pragma precompile_target "MW_TclTestHeaderPPC" +#elif __CFM68K__ +#pragma precompile_target "MW_TclTestHeaderCFM68K" +#else +#pragma precompile_target "MW_TclTestHeader68K" +#endif + +#define BUILD_tcl 1 + +#define STATIC_BUILD 1 + +#define TCL_DEBUG 1 + +#define TCL_THREADS 1 + +#include "MW_TclHeaderCommon.h" diff --git a/mac/README b/mac/README new file mode 100644 index 0000000..99dc71d --- /dev/null +++ b/mac/README @@ -0,0 +1,69 @@ +Tcl 8.5 for Macintosh + +RCS: @(#) $Id: README,v 1.18 2003/03/04 23:46:08 dgp Exp $ + +1. Introduction +--------------- + +This is the README file for the Macintosh version of the Tcl +scripting language. The home page for the Mac/Tcl info is + http://www.tcl.tk/software/mac/ + +A summary of what's new in this release is at + http://www.tcl.tk/software/tcltk/8.5.html + +A summary of Macintosh-specific features is at + http://www.tcl.tk/software/mac/features.html + +2. The Distribution +------------------- + +Macintosh Tcl is distributed in three different forms. This should +make it easier to only download what you need. Substitute <version> +with the version you wish to use. The packages are as follows: + +mactk<version>.sea.hqx + + This distribution is a "binary" only release. It contains an + installer program that will install a 68k, PowerPC, or Fat + version of the "Tcl Shell" and "Wish" applications. In addition, + it installs the Tcl & Tk libraries in the Extensions folder inside + your System Folder. + +mactcltk-full-<version>.sea.hqx + + This release contains the full release of Tcl and Tk for the + Macintosh plus the More Files packages which Macintosh Tcl and Tk + rely on. + +mactcl-source-<version>.sea.hqx + + This release contains the complete source for Tcl. In + addition, Metrowerks CodeWarrior libraries and project files + are included. However, you must already have the More Files + package to compile this code. + +The "html" subdirectory contains reference documentation in +in the HTML format. You may also find these pages at: + + http://www.tcl.tk/man/ + +3. Compiling Tcl +---------------- + +In order to compile Macintosh Tcl you must have the +following items: + + CodeWarrior Pro 5+ + Mac Tcl (sources) + More Files 1.4.9 + +The included project files should work fine. However, for +current release notes please check this page: + + http://www.tcl.tk/doc/howto/compile.html#mac + +If you have comments or Bug reports, please use the SourceForge +Bug tracker to report them: + + http://tcl.sourceforge.net/ diff --git a/mac/bugs.doc b/mac/bugs.doc new file mode 100644 index 0000000..730f115 --- /dev/null +++ b/mac/bugs.doc @@ -0,0 +1,44 @@ +Known bug list for Tcl 8.0 for Macintosh + +by Ray Johnson +Sun Microsystems Laboratories +rjohnson@eng.sun.com + +RCS: @(#) $Id: bugs.doc,v 1.4 2000/02/10 08:39:37 jingham Exp $ + +This was a new feature as of Tcl7.6b1 and as such I'll started with +a clean slate. I currently know of no reproducable bugs. I often +get vague reports - but nothing I've been able to confirm. Let +me know what bugs you find! + +The Macintosh version of Tcl passes most all tests in the Tcl +test suite. Slower Macs may fail some tests in event.test whose +timing constraints are too tight. If other tests fail please report +them. + +Ray + +Known bugs in the current release. + +* With the socket code you can't use the "localhost" host name. This + is actually a known bug in Apple's MacTcp stack. However, you can + use [info hostname] whereever you would have used "localhost" to + achive the same effect. + +* Most socket bugs have been fixed. We do have a couple of test cases + that will hang the Mac, however, and we are still working on them. + If you find additional test cases that show crashes please let us + know! + +* In Tcl 8.2, the new Regexp code seems to be more deeply recursive than +the older version in Tcl8.0. As a result, I have had to increase the Stack +size of Tcl to 1Meg. If you are not doing regexps with many subexpressions, +this is probably more stack than you will need. You can relink with the +stack set to 512K, and you will be fine for most purposes. +* This regexp problem is fixed in Tcl8.3. If you are going to do complex +regexp's, it is probably a good idea to keep the stack size big. But normal +regexps will not cause crashes. + +* The "clock scan -base" command does not work. The epoch is wrong. +* The file mtime command does not work when setting the time, it is off +by 4 years. diff --git a/mac/libmoto.doc b/mac/libmoto.doc new file mode 100644 index 0000000..2183651 --- /dev/null +++ b/mac/libmoto.doc @@ -0,0 +1,39 @@ +Notes about the use of libmoto +------------------------------ + +RCS: @(#) $Id: libmoto.doc,v 1.2 1998/09/14 18:40:04 stanton Exp $ + +First of all, libmoto is not required! If you don't have it, you +can simply remove the library reference from the project file and +everything should compile just fine. + +The libmoto library replaces certain functions in the MathLib and +ANSI libraries. Motorola has optimized the functions in the library +to run very fast on the PowerPC. As I said above, you don't need +this library, but it does make things faster. + +Obtaining Libmoto: + + For more information about Libmoto and how to doanload + it, visit the following URL: + + http://www.mot.com/SPS/PowerPC/library/fact_sheet/libmoto.html + + You will need to register for the library. However, the + library is free and you can use it in any commercial product + you might have. + +Installing Libmoto: + + Just follow the instructions provided by the Motorola + README file. You need to make sure that the Libmoto + library is before the ANSI and MathLib libraries in + link order. Also, you will get several warnings stateing + that certain functions have already been defined in + Libmoto. (These can safely be ignored.) + +Finally, you can thank Kate Stewart of Motorola for twisting my +arm at the Tcl/Tk Conference to provide some support for Libmoto. + +Ray Johnson + diff --git a/mac/morefiles.doc b/mac/morefiles.doc new file mode 100644 index 0000000..58f0929 --- /dev/null +++ b/mac/morefiles.doc @@ -0,0 +1,74 @@ +Notes about MoreFiles, dnr.c & other non-Tcl source files +--------------------------------------------------------- + +RCS: @(#) $Id: morefiles.doc,v 1.2 1998/09/14 18:40:04 stanton Exp $ + +The Macintosh distribution uses several source files that don't +actually ship with Tcl. This sometimes causes problems or confusion +to developers. This document should help clear up a few things. + +dnr.c +----- + +We have found a way to work around some bugs in dnr.c that +Apple has never fixed even though we sent in numerous bug reports. +The file tclMacDNR.c simply set's some #pragma's and the includes +the Apple dnr.c file. This should work the problems that many of +you have reported with dnr.c. + +More Files +---------- + +Macintosh Tcl/Tk also uses Jim Luther's very useful package called +More Files. More Files fixes many of the broken or underfunctional +parts of the file system. + +More Files can be found on the MetroWerks CD and Developer CD from +Apple. You can also down load the latest version from: + + ftp://members.aol.com/JumpLong/ + +The package can also be found at the home of Tcl/Tk for the mac: + + ftp://ftp.sunlabs.com/pub/tcl/mac/ + +I used to just link the More Files library in the Tcl projects. +However, this caused problems when libraries wern't matched correctly. +I'm now including the files in the Tcl project directly. This +solves the problem of missmatched libraries - but may not always +compile. + +If you get a compiliation error in MoreFiles you need to contact +Jim Luther. His email address: + + JumpLong@aol.com + +The version of More Files that we use with Tcl/Tk is 1.4.3. Early +version may work as well.. + +Unfortunantly, there is one bug in his library (in 1.4.3). The bug is +in the function FSpGetFullPath found in the file FullPath.c. After +the call to PBGetCatInfoSync you need to change the line: + + if ( result == noErr ) + + to: + + if ( (result == noErr) || (result == fnfErr) ) + + +The latest version of More Files is 1.4.6. Unfortunantly, this +version has a bug that keeps it from working with shared libraries +right out of the box. If you want to use 1.4.6 you can but you will +need to make the following fix: + + In the file "Opimization.h" in the More Files package you + need to remove the line "#pragma internal on". And in the + file "OptimazationEnd.h" you need to remove the line + "#pragma internal reset". + +Note: the version of MoreFile downloaded from the Sun Tcl/Tk site +will have the fix included. (If you want you can send email to +Jim Luther suggesting that he use Tcl for regression testing!) + +Ray Johnson diff --git a/mac/porting.notes b/mac/porting.notes new file mode 100644 index 0000000..5e71969 --- /dev/null +++ b/mac/porting.notes @@ -0,0 +1,23 @@ +Porting Notes +------------- + +RCS: @(#) $Id: porting.notes,v 1.2 1998/09/14 18:40:04 stanton Exp $ + +Currently, the Macintosh version Tcl only compilies with the +CodeWarrior C compilier from MetroWerks. It should be straight +forward to port the Tcl source to MPW. + +Tcl on the Mac no longer requires the use of GUSI. It should now +be easier to port Tcl/Tk to other compiliers such as Symantic C +and MPW C. + +If you attempt to port Tcl to other Macintosh compiliers please +let me know. I would be glad to help with advice and encouragement. +If your efforts are succesfull I wold also be interested in puting +those changes into the core distribution. Furthermore, please feel +free to send me any notes you might make about your porting +experience so I may include them in this file for others to reference. + +Ray Johnson +ray.johnson@eng.sun.com + diff --git a/mac/tclMac.h b/mac/tclMac.h new file mode 100644 index 0000000..b937342 --- /dev/null +++ b/mac/tclMac.h @@ -0,0 +1,28 @@ +/* + * tclMac.h -- + * + * Declarations of Macintosh specific public variables and procedures. + * + * Copyright (c) 1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMac.h,v 1.5 2001/11/23 01:27:05 das Exp $ + */ + +#ifndef _TCLMAC +#define _TCLMAC + +#ifndef _TCL +# include "tcl.h" +#endif +#include <Types.h> +#include <Files.h> +#include <Events.h> + +typedef int (*Tcl_MacConvertEventPtr) _ANSI_ARGS_((EventRecord *eventPtr)); + +#include "tclPlatDecls.h" + +#endif /* _TCLMAC */ diff --git a/mac/tclMacAETE.r b/mac/tclMacAETE.r new file mode 100644 index 0000000..be9dd11 --- /dev/null +++ b/mac/tclMacAETE.r @@ -0,0 +1,58 @@ +/* + * tclMacAETE.r -- + * + * This file creates the Apple Event Terminology resources + * for use Tcl and Tk. It is not used in the Simple Tcl shell + * since SIOUX does not support AppleEvents. An example of its + * use in Tcl is the TclBGOnly project. And it is used in all the + * Tk Shells. + * + * Copyright (c) 1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacAETE.r,v 1.2 1998/09/14 18:40:04 stanton Exp $ + */ + +#define SystemSevenOrLater 1 + +#include <Types.r> +#include <SysTypes.r> +#include <AEUserTermTypes.r> + +/* + * The following resources defines the Apple Events that Tk can be + * sent from Apple Script. + */ + +resource 'aete' (0, "Wish Suite") { + 0x01, 0x00, english, roman, + { + "Required Suite", + "Events that every application should support", + 'reqd', 1, 1, + {}, + {}, + {}, + {}, + + "Wish Suite", "Events for the Wish application", 'WIsH', 1, 1, + { + "do script", "Execute a Tcl script", 'misc', 'dosc', + 'TEXT', "Result", replyOptional, singleItem, + notEnumerated, reserved, reserved, reserved, reserved, + reserved, reserved, reserved, reserved, reserved, + reserved, reserved, reserved, reserved, + 'TEXT', "Script to execute", directParamRequired, + singleItem, notEnumerated, changesState, reserved, + reserved, reserved, reserved, reserved, reserved, + reserved, reserved, reserved, reserved, reserved, + reserved, + {}, + }, + {}, + {}, + {}, + } +}; diff --git a/mac/tclMacAlloc.c b/mac/tclMacAlloc.c new file mode 100644 index 0000000..3e6a948 --- /dev/null +++ b/mac/tclMacAlloc.c @@ -0,0 +1,412 @@ +/* + * tclMacAlloc.c -- + * + * This is a very fast storage allocator. It allocates blocks of a + * small number of different sizes, and keeps free lists of each size. + * Blocks that don't exactly fit are passed up to the next larger size. + * Blocks over a certain size are directly allocated by calling NewPtr. + * + * Copyright (c) 1983 Regents of the University of California. + * Copyright (c) 1996-1997 Sun Microsystems, Inc. + * + * Portions contributed by Chris Kingsley, Jack Jansen and Ray Johnson + *. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacAlloc.c,v 1.5 2001/11/23 01:27:09 das Exp $ + */ + +#include "tclInt.h" +#include "tclMacInt.h" +#include <Memory.h> +#include <Gestalt.h> +#include <stdlib.h> +#include <string.h> + + +/* + * Flags that are used by ConfigureMemory to define how the allocator + * should work. They can be or'd together. + */ +#define MEMORY_ALL_SYS 1 /* All memory should come from the system +heap. */ +#define MEMORY_DONT_USE_TEMPMEM 2 /* Don't use temporary memory but system memory. */ + +/* + * Amount of space to leave in the application heap for the Toolbox to work. + */ + +#define TOOLBOX_SPACE (512 * 1024) + +static int memoryFlags = 0; +static Handle toolGuardHandle = NULL; + /* This handle must be around so that we don't + * have NewGWorld failures. This handle is + * purgeable. Before we allocate any blocks, + * we see if this handle is still around. + * If it is not, then we try to get it again. + * If we can get it, we lock it and try + * to do the normal allocation, unlocking on + * the way out. If we can't, we go to the + * system heap directly. */ + +static int tclUseMemTracking = 0; /* Are we tracking memory allocations? + * On recent versions of the MacOS this + * is no longer necessary, as we can use + * temporary memory which is freed by the + * OS after a quit or crash. */ + +static size_t tclExtraHdlSize = 0; /* Size of extra memory allocated at the start + * of each block when using memory tracking + * ( == 0 otherwise) */ + +/* + * The following typedef and variable are used to keep track of memory + * blocks that are allocated directly from the System Heap. These chunks + * of memory must always be freed - even if we crash. + */ + +typedef struct listEl { + Handle memoryHandle; + struct listEl * next; + struct listEl * prec; +} ListEl; + +static ListEl * systemMemory = NULL; +static ListEl * appMemory = NULL; + +/* + * Prototypes for functions used only in this file. + */ + +static pascal void CleanUpExitProc _ANSI_ARGS_((void)); +void ConfigureMemory _ANSI_ARGS_((int flags)); +void FreeAllMemory _ANSI_ARGS_((void)); + +/* + *---------------------------------------------------------------------- + * + * TclpSysRealloc -- + * + * This function reallocates a chunk of system memory. If the + * chunk is already big enough to hold the new block, then no + * allocation happens. + * + * Results: + * Returns a pointer to the newly allocated block. + * + * Side effects: + * May copy the contents of the original block to the new block + * and deallocate the original block. + * + *---------------------------------------------------------------------- + */ + +VOID * +TclpSysRealloc( + VOID *oldPtr, /* Original block */ + unsigned int size) /* New size of block. */ +{ + Handle hand; + void *newPtr; + int maxsize; + OSErr err; + + if (tclUseMemTracking) { + hand = ((ListEl *) ((Ptr) oldPtr - tclExtraHdlSize))->memoryHandle; + } else { + hand = RecoverHandle((Ptr) oldPtr); + } + maxsize = GetHandleSize(hand) - sizeof(Handle); + if (maxsize < size) { + HUnlock(hand); + SetHandleSize(hand,size + tclExtraHdlSize); + err = MemError(); + HLock(hand); + if(err==noErr){ + newPtr=(*hand + tclExtraHdlSize); + } else { + newPtr = TclpSysAlloc(size, 1); + if(newPtr!=NULL) { + memmove(newPtr, oldPtr, maxsize); + TclpSysFree(oldPtr); + } + } + } else { + newPtr = oldPtr; + } + return newPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclpSysAlloc -- + * + * Allocate a new block of memory free from the System. + * + * Results: + * Returns a pointer to a new block of memory. + * + * Side effects: + * May obtain memory from app or sys space. Info is added to + * overhead lists etc. + * + *---------------------------------------------------------------------- + */ + +VOID * +TclpSysAlloc( + long size, /* Size of block to allocate. */ + int isBin) /* Is this a bin allocation? */ +{ + Handle hand = NULL; + ListEl * newMemoryRecord; + int isSysMem = 0; + static int initialized=0; + + if (!initialized) { + long response = 0; + OSErr err = noErr; + int useTempMem = 0; + + /* Check if we can use temporary memory */ + initialized=1; + err = Gestalt(gestaltOSAttr, &response); + if (err == noErr) { + useTempMem = response & (1 << gestaltRealTempMemory); + } + tclUseMemTracking = !useTempMem || (memoryFlags & MEMORY_DONT_USE_TEMPMEM); + if(tclUseMemTracking) { + tclExtraHdlSize = sizeof(ListEl); + /* + * We are allocating memory directly from the system + * heap. We need to install an exit handle + * to ensure the memory is cleaned up. + */ + TclMacInstallExitToShellPatch(CleanUpExitProc); + } + } + + if (!(memoryFlags & MEMORY_ALL_SYS)) { + + /* + * If the guard handle has been purged, throw it away and try + * to allocate it again. + */ + + if ((toolGuardHandle != NULL) && (*toolGuardHandle == NULL)) { + DisposeHandle(toolGuardHandle); + toolGuardHandle = NULL; + } + + /* + * If we have never allocated the guard handle, or it was purged + * and thrown away, then try to allocate it again. + */ + + if (toolGuardHandle == NULL) { + toolGuardHandle = NewHandle(TOOLBOX_SPACE); + if (toolGuardHandle != NULL) { + HLock(toolGuardHandle); + HPurge(toolGuardHandle); + } + } + + /* + * If we got the handle, lock it and do our allocation. + */ + + if (toolGuardHandle != NULL) { + HLock(toolGuardHandle); + hand = NewHandle(size + tclExtraHdlSize); + HUnlock(toolGuardHandle); + } + } + if (hand == NULL) { + /* + * Ran out of memory in application space. Lets try to get + * more memory from system. Otherwise, we return NULL to + * denote failure. + */ + if(!tclUseMemTracking) { + /* Use Temporary Memory instead of System Heap when available */ + OSErr err; + isBin = 1; /* always HLockHi TempMemHandles */ + hand = TempNewHandle(size + tclExtraHdlSize,&err); + if(err!=noErr) { hand=NULL; } + } else { + /* Use system heap when tracking memory */ + isSysMem=1; + isBin = 0; + hand = NewHandleSys(size + tclExtraHdlSize); + } + } + if (hand == NULL) { + return NULL; + } + if (isBin) { + HLockHi(hand); + } else { + HLock(hand); + } + if(tclUseMemTracking) { + /* Only need to do this when tracking memory */ + newMemoryRecord = (ListEl *) *hand; + newMemoryRecord->memoryHandle = hand; + newMemoryRecord->prec = NULL; + if(isSysMem) { + newMemoryRecord->next = systemMemory; + systemMemory = newMemoryRecord; + } else { + newMemoryRecord->next = appMemory; + appMemory = newMemoryRecord; + } + if(newMemoryRecord->next!=NULL) { + newMemoryRecord->next->prec=newMemoryRecord; + } + } + + return (*hand + tclExtraHdlSize); +} + +/* + *---------------------------------------------------------------------- + * + * TclpSysFree -- + * + * Free memory that we allocated back to the system. + * + * Results: + * None. + * + * Side effects: + * Memory is freed. + * + *---------------------------------------------------------------------- + */ + +void +TclpSysFree( + void * ptr) /* Free this system memory. */ +{ + if(tclUseMemTracking) { + /* Only need to do this when tracking memory */ + ListEl *memRecord; + + memRecord = (ListEl *) ((Ptr) ptr - tclExtraHdlSize); + /* Remove current record from linked list */ + if(memRecord->next!=NULL) { + memRecord->next->prec=memRecord->prec; + } + if(memRecord->prec!=NULL) { + memRecord->prec->next=memRecord->next; + } + if(memRecord==appMemory) { + appMemory=memRecord->next; + } else if(memRecord==systemMemory) { + systemMemory=memRecord->next; + } + DisposeHandle(memRecord->memoryHandle); + } else { + DisposeHandle(RecoverHandle((Ptr) ptr)); + } +} + +/* + *---------------------------------------------------------------------- + * + * CleanUpExitProc -- + * + * This procedure is invoked as an exit handler when ExitToShell + * is called. It removes any memory that was allocated directly + * from the system heap. This must be called when the application + * quits or the memory will never be freed. + * + * Results: + * None. + * + * Side effects: + * May free memory in the system heap. + * + *---------------------------------------------------------------------- + */ + +static pascal void +CleanUpExitProc() +{ + ListEl * memRecord; + + if(tclUseMemTracking) { + /* Only need to do this when tracking memory */ + while (systemMemory != NULL) { + memRecord = systemMemory; + systemMemory = memRecord->next; + DisposeHandle(memRecord->memoryHandle); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * FreeAllMemory -- + * + * This procedure frees all memory blocks allocated by the memory + * sub-system. Make sure you don't have any code that references + * any malloced data! + * + * Results: + * None. + * + * Side effects: + * Frees all memory allocated by TclpAlloc. + * + *---------------------------------------------------------------------- + */ + +void +FreeAllMemory() +{ + ListEl * memRecord; + + if(tclUseMemTracking) { + /* Only need to do this when tracking memory */ + while (systemMemory != NULL) { + memRecord = systemMemory; + systemMemory = memRecord->next; + DisposeHandle(memRecord->memoryHandle); + } + while (appMemory != NULL) { + memRecord = appMemory; + appMemory = memRecord->next; + DisposeHandle(memRecord->memoryHandle); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * ConfigureMemory -- + * + * This procedure sets certain flags in this file that control + * how memory is allocated and managed. This call must be made + * before any call to TclpAlloc is made. + * + * Results: + * None. + * + * Side effects: + * Certain state will be changed. + * + *---------------------------------------------------------------------- + */ + +void +ConfigureMemory( + int flags) /* Flags that control memory alloc scheme. */ +{ + memoryFlags = flags; +} diff --git a/mac/tclMacAppInit.c b/mac/tclMacAppInit.c new file mode 100644 index 0000000..65f79e8 --- /dev/null +++ b/mac/tclMacAppInit.c @@ -0,0 +1,213 @@ +/* + * tclMacAppInit.c -- + * + * Provides a version of the Tcl_AppInit procedure for the example shell. + * + * Copyright (c) 1993-1994 Lockheed Missle & Space Company, AI Center + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacAppInit.c,v 1.9 2001/11/23 01:27:13 das Exp $ + */ + +#include "tcl.h" +#include "tclInt.h" +#include "tclPort.h" +#include "tclMac.h" +#include "tclMacInt.h" + +#if defined(THINK_C) +# include <console.h> +#elif defined(__MWERKS__) +# include <SIOUX.h> +EXTERN short InstallConsole _ANSI_ARGS_((short fd)); +#endif + +#ifdef TCL_TEST +extern int Procbodytest_Init _ANSI_ARGS_((Tcl_Interp *interp)); +extern int Procbodytest_SafeInit _ANSI_ARGS_((Tcl_Interp *interp)); +extern int TclObjTest_Init _ANSI_ARGS_((Tcl_Interp *interp)); +extern int Tcltest_Init _ANSI_ARGS_((Tcl_Interp *interp)); +#endif /* TCL_TEST */ + +/* + * Forward declarations for procedures defined later in this file: + */ + +static int MacintoshInit _ANSI_ARGS_((void)); + +/* + *---------------------------------------------------------------------- + * + * main -- + * + * Main program for tclsh. This file can be used as a prototype + * for other applications using the Tcl library. + * + * Results: + * None. This procedure never returns (it exits the process when + * it's done. + * + * Side effects: + * This procedure initializes the Macintosh world and then + * calls Tcl_Main. Tcl_Main will never return except to exit. + * + *---------------------------------------------------------------------- + */ + +void +main( + int argc, /* Number of arguments. */ + char **argv) /* Array of argument strings. */ +{ + char *newArgv[2]; + + if (MacintoshInit() != TCL_OK) { + Tcl_Exit(1); + } + + argc = 1; + newArgv[0] = "tclsh"; + newArgv[1] = NULL; + Tcl_Main(argc, newArgv, Tcl_AppInit); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AppInit -- + * + * This procedure performs application-specific initialization. + * Most applications, especially those that incorporate additional + * packages, will have their own version of this procedure. + * + * Results: + * Returns a standard Tcl completion code, and leaves an error + * message in the interp's result if an error occurs. + * + * Side effects: + * Depends on the startup script. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_AppInit( + Tcl_Interp *interp) /* Interpreter for application. */ +{ + if (Tcl_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + +#ifdef TCL_TEST + if (Tcltest_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + Tcl_StaticPackage(interp, "Tcltest", Tcltest_Init, + (Tcl_PackageInitProc *) NULL); + if (TclObjTest_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + if (Procbodytest_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + Tcl_StaticPackage(interp, "procbodytest", Procbodytest_Init, + Procbodytest_SafeInit); +#endif /* TCL_TEST */ + + /* + * Call the init procedures for included packages. Each call should + * look like this: + * + * if (Mod_Init(interp) == TCL_ERROR) { + * return TCL_ERROR; + * } + * + * where "Mod" is the name of the module. + */ + + /* + * Call Tcl_CreateCommand for application-specific commands, if + * they weren't already created by the init procedures called above. + * Each call would loo like this: + * + * Tcl_CreateCommand(interp, "tclName", CFuncCmd, NULL, NULL); + */ + + /* + * Specify a user-specific startup script to invoke if the application + * is run interactively. On the Mac we can specifiy either a TEXT resource + * which contains the script or the more UNIX like file location + * may also used. (I highly recommend using the resource method.) + */ + + Tcl_SetVar(interp, "tcl_rcRsrcName", "tclshrc", TCL_GLOBAL_ONLY); + /* Tcl_SetVar(interp, "tcl_rcFileName", "~/.tclshrc", TCL_GLOBAL_ONLY); */ + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * MacintoshInit -- + * + * This procedure calls initalization routines to set up a simple + * console on a Macintosh. This is necessary as the Mac doesn't + * have a stdout & stderr by default. + * + * Results: + * Returns TCL_OK if everything went fine. If it didn't the + * application should probably fail. + * + * Side effects: + * Inits the appropiate console package. + * + *---------------------------------------------------------------------- + */ + +static int +MacintoshInit() +{ +#if GENERATING68K && !GENERATINGCFM + SetApplLimit(GetApplLimit() - (TCL_MAC_68K_STACK_GROWTH)); +#endif + MaxApplZone(); + +#if defined(THINK_C) + + /* Set options for Think C console package */ + /* The console package calls the Mac init calls */ + console_options.pause_atexit = 0; + console_options.title = "\pTcl Interpreter"; + +#elif defined(__MWERKS__) + + /* Set options for CodeWarrior SIOUX package */ + SIOUXSettings.autocloseonquit = true; + SIOUXSettings.showstatusline = true; + SIOUXSettings.asktosaveonclose = false; + SIOUXSettings.wasteusetempmemory = true; + InstallConsole(0); + SIOUXSetTitle("\pTcl Interpreter"); + +#elif defined(applec) + + /* Init packages used by MPW SIOW package */ + InitGraf((Ptr)&qd.thePort); + InitFonts(); + InitWindows(); + InitMenus(); + TEInit(); + InitDialogs(nil); + InitCursor(); + +#endif + + Tcl_MacSetEventProc((Tcl_MacConvertEventPtr) SIOUXHandleOneEvent); + + /* No problems with initialization */ + return TCL_OK; +} diff --git a/mac/tclMacApplication.r b/mac/tclMacApplication.r new file mode 100644 index 0000000..280c4ad --- /dev/null +++ b/mac/tclMacApplication.r @@ -0,0 +1,115 @@ +/* + * tclMacApplication.r -- + * + * This file creates resources for use Tcl Shell application. + * It should be viewed as an example of how to create a new + * Tcl application using the shared Tcl libraries. + * + * Copyright (c) 1996-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacApplication.r,v 1.6 2002/09/12 17:33:20 das Exp $ + */ + +#include <Types.r> +#include <SysTypes.r> + +/* + * The folowing include and defines help construct + * the version string for Tcl. + */ + +#define RC_INVOKED +#include "tcl.h" + +#if (TCL_RELEASE_LEVEL == 0) +# define RELEASE_LEVEL alpha +#elif (TCL_RELEASE_LEVEL == 1) +# define RELEASE_LEVEL beta +#elif (TCL_RELEASE_LEVEL == 2) +# define RELEASE_LEVEL final +#endif + +#if (TCL_RELEASE_LEVEL == 2) +# define MINOR_VERSION (TCL_MINOR_VERSION * 16) + TCL_RELEASE_SERIAL +# define RELEASE_CODE 0x00 +#else +# define MINOR_VERSION TCL_MINOR_VERSION * 16 +# define RELEASE_CODE TCL_RELEASE_SERIAL +#endif + +resource 'vers' (1) { + TCL_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, RELEASE_CODE, verUS, + TCL_PATCH_LEVEL, + TCL_PATCH_LEVEL ", by Ray Johnson & Jim Ingham" "\n" "© 2001 Tcl Core Team" +}; + +resource 'vers' (2) { + TCL_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, RELEASE_CODE, verUS, + TCL_PATCH_LEVEL, + "Tcl Shell " TCL_PATCH_LEVEL " © 1993-2001" +}; + +#define TCL_APP_CREATOR 'Tcl ' + +type TCL_APP_CREATOR as 'STR '; +resource TCL_APP_CREATOR (0, purgeable) { + "Tcl Shell " TCL_PATCH_LEVEL " © 1993-2001" +}; + +/* + * The 'kind' resource works with a 'BNDL' in Macintosh Easy Open + * to affect the text the Finder displays in the "kind" column and + * file info dialog. This information will be applied to all files + * with the listed creator and type. + */ + +resource 'kind' (128, "Tcl kind", purgeable) { + TCL_APP_CREATOR, + 0, /* region = USA */ + { + 'APPL', "Tcl Shell", + } +}; + +/* + * The following resource is used when creating the 'env' variable in + * the Macintosh environment. The creation mechanisim looks for the + * 'STR#' resource named "Tcl Environment Variables" rather than a + * specific resource number. (In other words, feel free to change the + * resource id if it conflicts with your application.) Each string in + * the resource must be of the form "KEYWORD=SOME STRING". See Tcl + * documentation for futher information about the env variable. + * + * A good example of something you may want to set is: "TCL_LIBRARY=My + * disk:etc." + */ + +resource 'STR#' (128, "Tcl Environment Variables") { + { + /* + "SCHEDULE_NAME=Agent Controller Schedule", + "SCHEDULE_PATH=Lozoya:System Folder:Tcl Lib:Tcl-Scheduler" + */ + }; +}; + +data 'alis' (1000, "Library Folder") { + $"0000 0000 00BA 0002 0001 012F 0000 0000" /* .....†...../.... */ + $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */ + $"0000 0000 0000 985C FB00 4244 0000 0000" /* ......ò\š.BD.... */ + $"0002 1328 5375 7070 6F72 7420 4C69 6272" /* ...(Support Libr */ + $"6172 6965 7329 0000 0000 0000 0000 0000" /* aries).......... */ + $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */ + $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */ + $"0000 0076 8504 B617 A796 003D 0027 025B" /* ...vÖ..ßñ.=.'.[ */ + $"01E4 0001 0001 0000 0000 0000 0000 0000" /* .”.............. */ + $"0000 0000 0000 0000 0001 2F00 0002 0015" /* ........../..... */ + $"2F3A 2853 7570 706F 7274 204C 6962 7261" /* /:(Support Libra */ + $"7269 6573 2900 FFFF 0000" /* ries)... */ +}; + diff --git a/mac/tclMacBOAAppInit.c b/mac/tclMacBOAAppInit.c new file mode 100644 index 0000000..0f7687b --- /dev/null +++ b/mac/tclMacBOAAppInit.c @@ -0,0 +1,257 @@ +/* + * tclMacBOAAppInit.c -- + * + * Provides a version of the Tcl_AppInit procedure for a + * Macintosh Background Only Application. + * + * Copyright (c) 1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacBOAAppInit.c,v 1.5 2001/06/14 00:48:51 dgp Exp $ + */ + +#include "tcl.h" +#include "tclInt.h" +#include "tclPort.h" +#include "tclMac.h" +#include "tclMacInt.h" +#include <Fonts.h> +#include <Windows.h> +#include <Dialogs.h> +#include <Menus.h> +#include <Aliases.h> +#include <LowMem.h> + +#include <AppleEvents.h> +#include <SegLoad.h> +#include <ToolUtils.h> + +#if defined(THINK_C) +# include <console.h> +#elif defined(__MWERKS__) +# include <SIOUX.h> +short InstallConsole _ANSI_ARGS_((short fd)); +#endif + +void TkMacInitAppleEvents(Tcl_Interp *interp); +int HandleHighLevelEvents(EventRecord *eventPtr); + +#ifdef TCL_TEST +EXTERN int TclObjTest_Init _ANSI_ARGS_((Tcl_Interp *interp)); +EXTERN int Tcltest_Init _ANSI_ARGS_((Tcl_Interp *interp)); +#endif /* TCL_TEST */ + +/* + * Forward declarations for procedures defined later in this file: + */ + +static int MacintoshInit _ANSI_ARGS_((void)); + +/* + *---------------------------------------------------------------------- + * + * main -- + * + * Main program for tclsh. This file can be used as a prototype + * for other applications using the Tcl library. + * + * Results: + * None. This procedure never returns (it exits the process when + * it's done. + * + * Side effects: + * This procedure initializes the Macintosh world and then + * calls Tcl_Main. Tcl_Main will never return except to exit. + * + *---------------------------------------------------------------------- + */ + +void +main( + int argc, /* Number of arguments. */ + char **argv) /* Array of argument strings. */ +{ + char *newArgv[3]; + + if (MacintoshInit() != TCL_OK) { + Tcl_Exit(1); + } + + argc = 2; + newArgv[0] = "tclsh"; + newArgv[1] = "bgScript.tcl"; + newArgv[2] = NULL; + Tcl_Main(argc, newArgv, Tcl_AppInit); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AppInit -- + * + * This procedure performs application-specific initialization. + * Most applications, especially those that incorporate additional + * packages, will have their own version of this procedure. + * + * Results: + * Returns a standard Tcl completion code, and leaves an error + * message in the interp's result if an error occurs. + * + * Side effects: + * Depends on the startup script. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_AppInit( + Tcl_Interp *interp) /* Interpreter for application. */ +{ + Tcl_Channel tempChan; + + if (Tcl_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + +#ifdef TCL_TEST + if (Tcltest_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + Tcl_StaticPackage(interp, "Tcltest", Tcltest_Init, + (Tcl_PackageInitProc *) NULL); + if (TclObjTest_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } +#endif /* TCL_TEST */ + + /* + * Call the init procedures for included packages. Each call should + * look like this: + * + * if (Mod_Init(interp) == TCL_ERROR) { + * return TCL_ERROR; + * } + * + * where "Mod" is the name of the module. + */ + + /* + * Call Tcl_CreateCommand for application-specific commands, if + * they weren't already created by the init procedures called above. + * Each call would loo like this: + * + * Tcl_CreateCommand(interp, "tclName", CFuncCmd, NULL, NULL); + */ + + /* + * Specify a user-specific startup script to invoke if the application + * is run interactively. On the Mac we can specifiy either a TEXT resource + * which contains the script or the more UNIX like file location + * may also used. (I highly recommend using the resource method.) + */ + + Tcl_SetVar(interp, "tcl_rcRsrcName", "tclshrc", TCL_GLOBAL_ONLY); + + /* Tcl_SetVar(interp, "tcl_rcFileName", "~/.tclshrc", TCL_GLOBAL_ONLY); */ + + /* + * We have to support at least the quit Apple Event. + */ + + TkMacInitAppleEvents(interp); + + /* + * Open a file channel to put stderr, stdin, stdout... + */ + + tempChan = Tcl_OpenFileChannel(interp, ":temp.in", "a+", 0); + Tcl_SetStdChannel(tempChan,TCL_STDIN); + Tcl_RegisterChannel(interp, tempChan); + Tcl_SetChannelOption(NULL, tempChan, "-translation", "cr"); + Tcl_SetChannelOption(NULL, tempChan, "-buffering", "line"); + + tempChan = Tcl_OpenFileChannel(interp, ":temp.out", "a+", 0); + Tcl_SetStdChannel(tempChan,TCL_STDOUT); + Tcl_RegisterChannel(interp, tempChan); + Tcl_SetChannelOption(NULL, tempChan, "-translation", "cr"); + Tcl_SetChannelOption(NULL, tempChan, "-buffering", "line"); + + tempChan = Tcl_OpenFileChannel(interp, ":temp.err", "a+", 0); + Tcl_SetStdChannel(tempChan,TCL_STDERR); + Tcl_RegisterChannel(interp, tempChan); + Tcl_SetChannelOption(NULL, tempChan, "-translation", "cr"); + Tcl_SetChannelOption(NULL, tempChan, "-buffering", "none"); + + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * MacintoshInit -- + * + * This procedure calls initalization routines to set up a simple + * console on a Macintosh. This is necessary as the Mac doesn't + * have a stdout & stderr by default. + * + * Results: + * Returns TCL_OK if everything went fine. If it didn't the + * application should probably fail. + * + * Side effects: + * Inits the appropiate console package. + * + *---------------------------------------------------------------------- + */ + +static int +MacintoshInit() +{ + THz theZone = GetZone(); + SysEnvRec sys; + + + /* + * There is a bug in systems earlier that 7.5.5, where a second BOA will + * get a corrupted heap. This is the fix from TechNote 1070 + */ + + SysEnvirons(1, &sys); + + if (sys.systemVersion < 0x0755) + { + if ( LMGetHeapEnd() != theZone->bkLim) { + LMSetHeapEnd(theZone->bkLim); + } + } + +#if GENERATING68K && !GENERATINGCFM + SetApplLimit(GetApplLimit() - (TCL_MAC_68K_STACK_GROWTH)); +#endif + MaxApplZone(); + + InitGraf((Ptr)&qd.thePort); + + /* No problems with initialization */ + Tcl_MacSetEventProc(HandleHighLevelEvents); + + return TCL_OK; +} + +int +HandleHighLevelEvents( + EventRecord *eventPtr) +{ + int eventFound = false; + + if (eventPtr->what == kHighLevelEvent) { + AEProcessAppleEvent(eventPtr); + eventFound = true; + } else if (eventPtr->what == nullEvent) { + eventFound = true; + } + return eventFound; +} diff --git a/mac/tclMacBOAMain.c b/mac/tclMacBOAMain.c new file mode 100644 index 0000000..ff1a943 --- /dev/null +++ b/mac/tclMacBOAMain.c @@ -0,0 +1,304 @@ +/* + * tclMacBGMain.c -- + * + * Main program for Macintosh Background Only Application shells. + * + * Copyright (c) 1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacBOAMain.c,v 1.4 2001/12/28 23:36:31 dgp Exp $ + */ + +#include "tcl.h" +#include "tclInt.h" +#include "tclMacInt.h" +#include <Resources.h> +#include <Notification.h> +#include <Strings.h> + +/* + * This variable is used to get out of the modal loop of the + * notification manager. + */ + +int NotificationIsDone = 0; + +/* + * The following code ensures that tclLink.c is linked whenever + * Tcl is linked. Without this code there's no reference to the + * code in that file from anywhere in Tcl, so it may not be + * linked into the application. + */ + +EXTERN int Tcl_LinkVar(); +int (*tclDummyLinkVarPtr)() = Tcl_LinkVar; + +/* + * Declarations for various library procedures and variables (don't want + * to include tclPort.h here, because people might copy this file out of + * the Tcl source directory to make their own modified versions). + * Note: "exit" should really be declared here, but there's no way to + * declare it without causing conflicts with other definitions elsewher + * on some systems, so it's better just to leave it out. + */ + +extern int isatty _ANSI_ARGS_((int fd)); +extern char * strcpy _ANSI_ARGS_((char *dst, CONST char *src)); + +static Tcl_Interp *interp; /* Interpreter for application. */ + +/* + * Forward references for procedures defined later in this file: + */ + +void TclMacDoNotification(char *mssg); +void TclMacNotificationResponse(NMRecPtr nmRec); +int Tcl_MacBGNotifyObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv); + + +/* + *---------------------------------------------------------------------- + * + * Tcl_Main -- + * + * Main program for tclsh and most other Tcl-based applications. + * + * Results: + * None. This procedure never returns (it exits the process when + * it's done. + * + * Side effects: + * This procedure initializes the Tk world and then starts + * interpreting commands; almost anything could happen, depending + * on the script being interpreted. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_Main(argc, argv, appInitProc) + int argc; /* Number of arguments. */ + char **argv; /* Array of argument strings. */ + Tcl_AppInitProc *appInitProc; + /* Application-specific initialization + * procedure to call after most + * initialization but before starting to + * execute commands. */ +{ + Tcl_Obj *prompt1NamePtr = NULL; + Tcl_Obj *prompt2NamePtr = NULL; + Tcl_Obj *commandPtr = NULL; + char buffer[1000], *args, *fileName; + int code, tty; + int exitCode = 0; + + Tcl_FindExecutable(argv[0]); + interp = Tcl_CreateInterp(); + Tcl_InitMemory(interp); + + /* + * Make command-line arguments available in the Tcl variables "argc" + * and "argv". If the first argument doesn't start with a "-" then + * strip it off and use it as the name of a script file to process. + */ + + fileName = NULL; + if ((argc > 1) && (argv[1][0] != '-')) { + fileName = argv[1]; + argc--; + argv++; + } + args = Tcl_Merge(argc-1, argv+1); + Tcl_SetVar(interp, "argv", args, TCL_GLOBAL_ONLY); + ckfree(args); + TclFormatInt(buffer, argc-1); + Tcl_SetVar(interp, "argc", buffer, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "argv0", (fileName != NULL) ? fileName : argv[0], + TCL_GLOBAL_ONLY); + + /* + * Set the "tcl_interactive" variable. + */ + + tty = isatty(0); + Tcl_SetVar(interp, "tcl_interactive", + ((fileName == NULL) && tty) ? "1" : "0", TCL_GLOBAL_ONLY); + + /* + * Invoke application-specific initialization. + */ + + if ((*appInitProc)(interp) != TCL_OK) { + Tcl_DString errStr; + + Tcl_DStringInit(&errStr); + Tcl_DStringAppend(&errStr, + "application-specific initialization failed: \n", -1); + Tcl_DStringAppend(&errStr, Tcl_GetStringResult(interp), -1); + Tcl_DStringAppend(&errStr, "\n", 1); + TclMacDoNotification(Tcl_DStringValue(&errStr)); + Tcl_DStringFree(&errStr); + goto done; + } + + /* + * Install the BGNotify command: + */ + + if ( Tcl_CreateObjCommand(interp, "bgnotify", Tcl_MacBGNotifyObjCmd, NULL, + (Tcl_CmdDeleteProc *) NULL) == NULL) { + goto done; + } + + /* + * If a script file was specified then just source that file + * and quit. In this Mac BG Application version, we will try the + * resource fork first, then the file system second... + */ + + if (fileName != NULL) { + Str255 resName; + Handle resource; + + strcpy((char *) resName + 1, fileName); + resName[0] = strlen(fileName); + resource = GetNamedResource('TEXT',resName); + if (resource != NULL) { + code = Tcl_MacEvalResource(interp, fileName, -1, NULL); + } else { + code = Tcl_EvalFile(interp, fileName); + } + + if (code != TCL_OK) { + Tcl_DString errStr; + + Tcl_DStringInit(&errStr); + Tcl_DStringAppend(&errStr, " Error sourcing resource or file: ", -1); + Tcl_DStringAppend(&errStr, fileName, -1); + Tcl_DStringAppend(&errStr, "\n\nError was: ", -1); + Tcl_DStringAppend(&errStr, Tcl_GetStringResult(interp), -1); + TclMacDoNotification(Tcl_DStringValue(&errStr)); + Tcl_DStringFree(&errStr); + } + goto done; + } + + + /* + * Rather than calling exit, invoke the "exit" command so that + * users can replace "exit" with some other command to do additional + * cleanup on exit. The Tcl_Eval call should never return. + */ + + done: + if (commandPtr != NULL) { + Tcl_DecrRefCount(commandPtr); + } + if (prompt1NamePtr != NULL) { + Tcl_DecrRefCount(prompt1NamePtr); + } + if (prompt2NamePtr != NULL) { + Tcl_DecrRefCount(prompt2NamePtr); + } + sprintf(buffer, "exit %d", exitCode); + Tcl_Eval(interp, buffer); +} + +/*---------------------------------------------------------------------- + * + * TclMacDoNotification -- + * + * This posts an error message using the Notification manager. + * + * Results: + * Post a Notification Manager dialog. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +void +TclMacDoNotification(mssg) + char *mssg; +{ + NMRec errorNot; + EventRecord *theEvent = NULL; + OSErr err; + char *ptr; + + errorNot.qType = nmType; + errorNot.nmMark = 0; + errorNot.nmIcon = 0; + errorNot.nmSound = (Handle) -1; + + for ( ptr = mssg; *ptr != '\0'; ptr++) { + if (*ptr == '\n') { + *ptr = '\r'; + } + } + + c2pstr(mssg); + errorNot.nmStr = (StringPtr) mssg; + + errorNot.nmResp = NewNMProc(TclMacNotificationResponse); + errorNot.nmRefCon = SetCurrentA5(); + + NotificationIsDone = 0; + + /* + * Cycle while waiting for the user to click on the + * notification box. Don't take any events off the event queue, + * since we want Tcl to do this but we want to block till the notification + * has been handled... + */ + + err = NMInstall(&errorNot); + if (err == noErr) { + while (!NotificationIsDone) { + WaitNextEvent(0, theEvent, 20, NULL); + } + NMRemove(&errorNot); + } + + p2cstr((unsigned char *) mssg); +} + +void +TclMacNotificationResponse(nmRec) + NMRecPtr nmRec; +{ + int curA5; + + curA5 = SetCurrentA5(); + SetA5(nmRec->nmRefCon); + + NotificationIsDone = 1; + + SetA5(curA5); + +} + +int +Tcl_MacBGNotifyObjCmd(clientData, interp, objc, objv) + ClientData clientData; + Tcl_Interp *interp; + int objc; + Tcl_Obj **objv; +{ + Tcl_Obj *resultPtr; + + resultPtr = Tcl_GetObjResult(interp); + + if ( objc != 2 ) { + Tcl_WrongNumArgs(interp, 1, objv, "message"); + return TCL_ERROR; + } + + TclMacDoNotification(Tcl_GetString(objv[1])); + return TCL_OK; + +} + diff --git a/mac/tclMacChan.c b/mac/tclMacChan.c new file mode 100644 index 0000000..8986e6c --- /dev/null +++ b/mac/tclMacChan.c @@ -0,0 +1,1300 @@ +/* + * tclMacChan.c + * + * Channel drivers for Macintosh channels for the + * console fds. + * + * Copyright (c) 1996-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacChan.c,v 1.22 2003/12/24 04:18:21 davygrvy Exp $ + */ + +#include "tclInt.h" +#include "tclPort.h" +#include "tclMacInt.h" +#include <Aliases.h> +#include <Errors.h> +#include <Files.h> +#include <Gestalt.h> +#include <Processes.h> +#include <Strings.h> +#include <FSpCompat.h> +#include <MoreFiles.h> +#include <MoreFilesExtras.h> +#include "tclIO.h" + +#ifdef __MSL__ +#include <unix.mac.h> +#define TCL_FILE_CREATOR (__getcreator(0)) +#else +#define TCL_FILE_CREATOR 'MPW ' +#endif + +/* + * This structure describes per-instance state of a + * macintosh file based channel. + */ + +typedef struct FileState { + short fileRef; /* Macintosh file reference number. */ + Tcl_Channel fileChan; /* Pointer to the channel for this file. */ + int watchMask; /* OR'ed set of flags indicating which events + * are being watched. */ + int appendMode; /* Flag to tell if in O_APPEND mode or not. */ + int volumeRef; /* Flag to tell if in O_APPEND mode or not. */ + int pending; /* 1 if message is pending on queue. */ + struct FileState *nextPtr; /* Pointer to next registered file. */ +} FileState; + +typedef struct ThreadSpecificData { + int initialized; /* True after the thread initializes */ + FileState *firstFilePtr; /* the head of the list of files managed + * that are being watched for file events. */ + Tcl_Channel stdinChannel; + Tcl_Channel stdoutChannel; /* Note - these seem unused */ + Tcl_Channel stderrChannel; +} ThreadSpecificData; + +static Tcl_ThreadDataKey dataKey; + +/* + * The following structure is what is added to the Tcl event queue when + * file events are generated. + */ + +typedef struct FileEvent { + Tcl_Event header; /* Information that is standard for + * all events. */ + FileState *infoPtr; /* Pointer to file info structure. Note + * that we still have to verify that the + * file exists before dereferencing this + * pointer. */ +} FileEvent; + + +/* + * Static routines for this file: + */ + +static int CommonGetHandle _ANSI_ARGS_((ClientData instanceData, + int direction, ClientData *handlePtr)); +static void CommonWatch _ANSI_ARGS_((ClientData instanceData, + int mask)); +static int FileBlockMode _ANSI_ARGS_((ClientData instanceData, + int mode)); +static void FileChannelExitHandler _ANSI_ARGS_(( + ClientData clientData)); +static void FileCheckProc _ANSI_ARGS_((ClientData clientData, + int flags)); +static int FileClose _ANSI_ARGS_((ClientData instanceData, + Tcl_Interp *interp)); +static int FileEventProc _ANSI_ARGS_((Tcl_Event *evPtr, + int flags)); +static ThreadSpecificData *FileInit _ANSI_ARGS_((void)); +static int FileInput _ANSI_ARGS_((ClientData instanceData, + char *buf, int toRead, int *errorCode)); +static int FileOutput _ANSI_ARGS_((ClientData instanceData, + CONST char *buf, int toWrite, int *errorCode)); +static int FileSeek _ANSI_ARGS_((ClientData instanceData, + long offset, int mode, int *errorCode)); +static void FileSetupProc _ANSI_ARGS_((ClientData clientData, + int flags)); +static Tcl_Channel OpenFileChannel _ANSI_ARGS_((CONST char *fileName, + int mode, int permissions, int *errorCodePtr)); +static int StdIOBlockMode _ANSI_ARGS_((ClientData instanceData, + int mode)); +static int StdIOClose _ANSI_ARGS_((ClientData instanceData, + Tcl_Interp *interp)); +static int StdIOInput _ANSI_ARGS_((ClientData instanceData, + char *buf, int toRead, int *errorCode)); +static int StdIOOutput _ANSI_ARGS_((ClientData instanceData, + CONST char *buf, int toWrite, int *errorCode)); +static int StdIOSeek _ANSI_ARGS_((ClientData instanceData, + long offset, int mode, int *errorCode)); +static int StdReady _ANSI_ARGS_((ClientData instanceData, + int mask)); + +/* + * This structure describes the channel type structure for file based IO: + */ + +static Tcl_ChannelType consoleChannelType = { + "file", /* Type name. */ + (Tcl_ChannelTypeVersion)StdIOBlockMode, /* Set blocking/nonblocking mode.*/ + StdIOClose, /* Close proc. */ + StdIOInput, /* Input proc. */ + StdIOOutput, /* Output proc. */ + StdIOSeek, /* Seek proc. */ + NULL, /* Set option proc. */ + NULL, /* Get option proc. */ + CommonWatch, /* Initialize notifier. */ + CommonGetHandle /* Get OS handles out of channel. */ +}; + +/* + * This variable describes the channel type structure for file based IO. + */ + +static Tcl_ChannelType fileChannelType = { + "file", /* Type name. */ + (Tcl_ChannelTypeVersion)FileBlockMode, /* Set blocking or + * non-blocking mode.*/ + FileClose, /* Close proc. */ + FileInput, /* Input proc. */ + FileOutput, /* Output proc. */ + FileSeek, /* Seek proc. */ + NULL, /* Set option proc. */ + NULL, /* Get option proc. */ + CommonWatch, /* Initialize notifier. */ + CommonGetHandle /* Get OS handles out of channel. */ +}; + + +/* + * Hack to allow Mac Tk to override the TclGetStdChannels function. + */ + +typedef void (*TclGetStdChannelsProc) _ANSI_ARGS_((Tcl_Channel *stdinPtr, + Tcl_Channel *stdoutPtr, Tcl_Channel *stderrPtr)); + +TclGetStdChannelsProc getStdChannelsProc = NULL; + + +/* + *---------------------------------------------------------------------- + * + * FileInit -- + * + * This function initializes the file channel event source. + * + * Results: + * None. + * + * Side effects: + * Creates a new event source. + * + *---------------------------------------------------------------------- + */ + +static ThreadSpecificData * +FileInit() +{ + ThreadSpecificData *tsdPtr = + (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); + if (tsdPtr == NULL) { + tsdPtr = TCL_TSD_INIT(&dataKey); + tsdPtr->firstFilePtr = NULL; + Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL); + Tcl_CreateThreadExitHandler(FileChannelExitHandler, NULL); + } + return tsdPtr; +} + +/* + *---------------------------------------------------------------------- + * + * FileChannelExitHandler -- + * + * This function is called to cleanup the channel driver before + * Tcl is unloaded. + * + * Results: + * None. + * + * Side effects: + * Destroys the communication window. + * + *---------------------------------------------------------------------- + */ + +static void +FileChannelExitHandler( + ClientData clientData) /* Old window proc */ +{ + Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL); +} + +/* + *---------------------------------------------------------------------- + * + * FileSetupProc -- + * + * This procedure is invoked before Tcl_DoOneEvent blocks waiting + * for an event. + * + * Results: + * None. + * + * Side effects: + * Adjusts the block time if needed. + * + *---------------------------------------------------------------------- + */ + +void +FileSetupProc( + ClientData data, /* Not used. */ + int flags) /* Event flags as passed to Tcl_DoOneEvent. */ +{ + FileState *infoPtr; + Tcl_Time blockTime = { 0, 0 }; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (!(flags & TCL_FILE_EVENTS)) { + return; + } + + /* + * Check to see if there is a ready file. If so, poll. + */ + + for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; + infoPtr = infoPtr->nextPtr) { + if (infoPtr->watchMask) { + Tcl_SetMaxBlockTime(&blockTime); + break; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * FileCheckProc -- + * + * This procedure is called by Tcl_DoOneEvent to check the file + * event source for events. + * + * Results: + * None. + * + * Side effects: + * May queue an event. + * + *---------------------------------------------------------------------- + */ + +static void +FileCheckProc( + ClientData data, /* Not used. */ + int flags) /* Event flags as passed to Tcl_DoOneEvent. */ +{ + FileEvent *evPtr; + FileState *infoPtr; + int sentMsg = 0; + Tcl_Time blockTime = { 0, 0 }; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (!(flags & TCL_FILE_EVENTS)) { + return; + } + + /* + * Queue events for any ready files that don't already have events + * queued (caused by persistent states that won't generate WinSock + * events). + */ + + for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; + infoPtr = infoPtr->nextPtr) { + if (infoPtr->watchMask && !infoPtr->pending) { + infoPtr->pending = 1; + evPtr = (FileEvent *) ckalloc(sizeof(FileEvent)); + evPtr->header.proc = FileEventProc; + evPtr->infoPtr = infoPtr; + Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); + } + } +} + +/*---------------------------------------------------------------------- + * + * FileEventProc -- + * + * This function is invoked by Tcl_ServiceEvent when a file event + * reaches the front of the event queue. This procedure invokes + * Tcl_NotifyChannel on the file. + * + * Results: + * Returns 1 if the event was handled, meaning it should be removed + * from the queue. Returns 0 if the event was not handled, meaning + * it should stay on the queue. The only time the event isn't + * handled is if the TCL_FILE_EVENTS flag bit isn't set. + * + * Side effects: + * Whatever the notifier callback does. + * + *---------------------------------------------------------------------- + */ + +static int +FileEventProc( + Tcl_Event *evPtr, /* Event to service. */ + int flags) /* Flags that indicate what events to + * handle, such as TCL_FILE_EVENTS. */ +{ + FileEvent *fileEvPtr = (FileEvent *)evPtr; + FileState *infoPtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (!(flags & TCL_FILE_EVENTS)) { + return 0; + } + + /* + * Search through the list of watched files for the one whose handle + * matches the event. We do this rather than simply dereferencing + * the handle in the event so that files can be deleted while the + * event is in the queue. + */ + + for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; + infoPtr = infoPtr->nextPtr) { + if (fileEvPtr->infoPtr == infoPtr) { + infoPtr->pending = 0; + Tcl_NotifyChannel(infoPtr->fileChan, infoPtr->watchMask); + break; + } + } + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * StdIOBlockMode -- + * + * Set blocking or non-blocking mode on channel. + * + * Results: + * 0 if successful, errno when failed. + * + * Side effects: + * Sets the device into blocking or non-blocking mode. + * + *---------------------------------------------------------------------- + */ + +static int +StdIOBlockMode( + ClientData instanceData, /* Unused. */ + int mode) /* The mode to set. */ +{ + /* + * Do not allow putting stdin, stdout or stderr into nonblocking mode. + */ + + if (mode == TCL_MODE_NONBLOCKING) { + return EFAULT; + } + + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * StdIOClose -- + * + * Closes the IO channel. + * + * Results: + * 0 if successful, the value of errno if failed. + * + * Side effects: + * Closes the physical channel + * + *---------------------------------------------------------------------- + */ + +static int +StdIOClose( + ClientData instanceData, /* Unused. */ + Tcl_Interp *interp) /* Unused. */ +{ + int fd, errorCode = 0; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + /* + * Invalidate the stdio cache if necessary. Note that we assume that + * the stdio file and channel pointers will become invalid at the same + * time. + * Do not close standard channels while in thread-exit. + */ + + fd = (int) ((FileState*)instanceData)->fileRef; + if (!TclInThreadExit()) { + if (fd == 0) { + tsdPtr->stdinChannel = NULL; + } else if (fd == 1) { + tsdPtr->stdoutChannel = NULL; + } else if (fd == 2) { + tsdPtr->stderrChannel = NULL; + } else { + Tcl_Panic("recieved invalid std file"); + } + + if (close(fd) < 0) { + errorCode = errno; + } + } + return errorCode; +} + +/* + *---------------------------------------------------------------------- + * + * CommonGetHandle -- + * + * Called from Tcl_GetChannelHandle to retrieve OS handles from inside + * a file based channel. + * + * Results: + * The appropriate handle or NULL if not present. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +CommonGetHandle( + ClientData instanceData, /* The file state. */ + int direction, /* Which handle to retrieve? */ + ClientData *handlePtr) +{ + if ((direction == TCL_READABLE) || (direction == TCL_WRITABLE)) { + *handlePtr = (ClientData) ((FileState*)instanceData)->fileRef; + return TCL_OK; + } + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * StdIOInput -- + * + * Reads input from the IO channel into the buffer given. Returns + * count of how many bytes were actually read, and an error indication. + * + * Results: + * A count of how many bytes were read is returned and an error + * indication is returned in an output argument. + * + * Side effects: + * Reads input from the actual channel. + * + *---------------------------------------------------------------------- + */ + +int +StdIOInput( + ClientData instanceData, /* Unused. */ + char *buf, /* Where to store data read. */ + int bufSize, /* How much space is available + * in the buffer? */ + int *errorCode) /* Where to store error code. */ +{ + int fd; + int bytesRead; /* How many bytes were read? */ + + *errorCode = 0; + errno = 0; + fd = (int) ((FileState*)instanceData)->fileRef; + bytesRead = read(fd, buf, (size_t) bufSize); + if (bytesRead > -1) { + return bytesRead; + } + *errorCode = errno; + return -1; +} + +/* + *---------------------------------------------------------------------- + * + * StdIOOutput-- + * + * Writes the given output on the IO channel. Returns count of how + * many characters were actually written, and an error indication. + * + * Results: + * A count of how many characters were written is returned and an + * error indication is returned in an output argument. + * + * Side effects: + * Writes output on the actual channel. + * + *---------------------------------------------------------------------- + */ + +static int +StdIOOutput( + ClientData instanceData, /* Unused. */ + CONST char *buf, /* The data buffer. */ + int toWrite, /* How many bytes to write? */ + int *errorCode) /* Where to store error code. */ +{ + int written; + int fd; + + *errorCode = 0; + errno = 0; + fd = (int) ((FileState*)instanceData)->fileRef; + written = write(fd, (void*)buf, (size_t) toWrite); + if (written > -1) { + return written; + } + *errorCode = errno; + return -1; +} + +/* + *---------------------------------------------------------------------- + * + * StdIOSeek -- + * + * Seeks on an IO channel. Returns the new position. + * + * Results: + * -1 if failed, the new position if successful. If failed, it + * also sets *errorCodePtr to the error code. + * + * Side effects: + * Moves the location at which the channel will be accessed in + * future operations. + * + *---------------------------------------------------------------------- + */ + +static int +StdIOSeek( + ClientData instanceData, /* Unused. */ + long offset, /* Offset to seek to. */ + int mode, /* Relative to where should we seek? */ + int *errorCodePtr) /* To store error code. */ +{ + int newLoc; + int fd; + + *errorCodePtr = 0; + fd = (int) ((FileState*)instanceData)->fileRef; + newLoc = lseek(fd, offset, mode); + if (newLoc > -1) { + return newLoc; + } + *errorCodePtr = errno; + return -1; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_PidObjCmd -- + * + * This procedure is invoked to process the "pid" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + + /* ARGSUSED */ +int +Tcl_PidObjCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST *objv; /* Argument strings. */ +{ + ProcessSerialNumber psn; + char buf[20]; + Tcl_Channel chan; + Tcl_Obj *resultPtr; + + if (objc > 2) { + Tcl_WrongNumArgs(interp, 1, objv, "?channelId?"); + return TCL_ERROR; + } + if (objc == 1) { + resultPtr = Tcl_GetObjResult(interp); + GetCurrentProcess(&psn); + sprintf(buf, "0x%08x%08x", psn.highLongOfPSN, psn.lowLongOfPSN); + Tcl_SetStringObj(resultPtr, buf, -1); + } else { + chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), + NULL); + if (chan == (Tcl_Channel) NULL) { + return TCL_ERROR; + } + /* + * We can't create pipelines on the Mac so + * this will always return an empty list. + */ + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclpGetDefaultStdChannel -- + * + * Constructs a channel for the specified standard OS handle. + * + * Results: + * Returns the specified default standard channel, or NULL. + * + * Side effects: + * May cause the creation of a standard channel and the underlying + * file. + * + *---------------------------------------------------------------------- + */ + +Tcl_Channel +TclpGetDefaultStdChannel( + int type) /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */ +{ + Tcl_Channel channel = NULL; + int fd = 0; /* Initializations needed to prevent */ + int mode = 0; /* compiler warning (used before set). */ + char *bufMode = NULL; + char channelName[16 + TCL_INTEGER_SPACE]; + int channelPermissions; + FileState *fileState; + + /* + * If the channels were not created yet, create them now and + * store them in the static variables. + */ + + switch (type) { + case TCL_STDIN: + fd = 0; + channelPermissions = TCL_READABLE; + bufMode = "line"; + break; + case TCL_STDOUT: + fd = 1; + channelPermissions = TCL_WRITABLE; + bufMode = "line"; + break; + case TCL_STDERR: + fd = 2; + channelPermissions = TCL_WRITABLE; + bufMode = "none"; + break; + default: + Tcl_Panic("TclGetDefaultStdChannel: Unexpected channel type"); + break; + } + + sprintf(channelName, "console%d", (int) fd); + fileState = (FileState *) ckalloc((unsigned) sizeof(FileState)); + channel = Tcl_CreateChannel(&consoleChannelType, channelName, + (ClientData) fileState, channelPermissions); + fileState->fileChan = channel; + fileState->fileRef = fd; + + /* + * Set up the normal channel options for stdio handles. + */ + + Tcl_SetChannelOption(NULL, channel, "-translation", "cr"); + Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode); + + return channel; +} + +/* + *---------------------------------------------------------------------- + * + * TclpOpenFileChannel -- + * + * Open a File based channel on MacOS systems. + * + * Results: + * The new channel or NULL. If NULL, the output argument + * errorCodePtr is set to a POSIX error. + * + * Side effects: + * May open the channel and may cause creation of a file on the + * file system. + * + *---------------------------------------------------------------------- + */ + +Tcl_Channel +TclpOpenFileChannel( + Tcl_Interp *interp, /* Interpreter for error reporting; + * can be NULL. */ + Tcl_Obj *pathPtr, /* Name of file to open. */ + int mode, /* POSIX open mode. */ + int permissions) /* If the open involves creating a + * file, with what modes to create + * it? */ +{ + Tcl_Channel chan; + CONST char *native; + int errorCode; + + native = Tcl_FSGetNativePath(pathPtr); + if (native == NULL) { + return NULL; + } + chan = OpenFileChannel(native, mode, permissions, &errorCode); + + if (chan == NULL) { + Tcl_SetErrno(errorCode); + if (interp != (Tcl_Interp *) NULL) { + Tcl_AppendResult(interp, "couldn't open \"", + Tcl_GetString(pathPtr), "\": ", + Tcl_PosixError(interp), (char *) NULL); + } + return NULL; + } + + return chan; +} + +/* + *---------------------------------------------------------------------- + * + * OpenFileChannel-- + * + * Opens a Macintosh file and creates a Tcl channel to control it. + * + * Results: + * A Tcl channel. + * + * Side effects: + * Will open a Macintosh file. + * + *---------------------------------------------------------------------- + */ + +static Tcl_Channel +OpenFileChannel( + CONST char *fileName, /* Name of file to open (native). */ + int mode, /* Mode for opening file. */ + int permissions, /* If the open involves creating a + * file, with what modes to create + * it? */ + int *errorCodePtr) /* Where to store error code. */ +{ + int channelPermissions; + Tcl_Channel chan; + char macPermision; + FSSpec fileSpec; + OSErr err; + short fileRef; + FileState *fileState; + char channelName[16 + TCL_INTEGER_SPACE]; + ThreadSpecificData *tsdPtr; + + tsdPtr = FileInit(); + + /* + * Note we use fsRdWrShPerm instead of fsRdWrPerm which allows shared + * writes on a file. This isn't common on a mac but is common with + * Windows and UNIX and the feature is used by Tcl. + */ + + switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) { + case O_RDWR: + channelPermissions = (TCL_READABLE | TCL_WRITABLE); + macPermision = fsRdWrShPerm; + break; + case O_WRONLY: + /* + * Mac's fsRdPerm permission actually defaults to fsRdWrPerm because + * the Mac OS doesn't realy support write only access. We explicitly + * set the permission fsRdWrShPerm so that we can have shared write + * access. + */ + channelPermissions = TCL_WRITABLE; + macPermision = fsRdWrShPerm; + break; + case O_RDONLY: + default: + channelPermissions = TCL_READABLE; + macPermision = fsRdPerm; + break; + } + + err = FSpLocationFromPath(strlen(fileName), fileName, &fileSpec); + if ((err != noErr) && (err != fnfErr)) { + *errorCodePtr = errno = TclMacOSErrorToPosixError(err); + Tcl_SetErrno(errno); + return NULL; + } + + if ((err == fnfErr) && (mode & O_CREAT)) { + err = HCreate(fileSpec.vRefNum, fileSpec.parID, fileSpec.name, TCL_FILE_CREATOR, 'TEXT'); + if (err != noErr) { + *errorCodePtr = errno = TclMacOSErrorToPosixError(err); + Tcl_SetErrno(errno); + return NULL; + } + } else if ((mode & O_CREAT) && (mode & O_EXCL)) { + *errorCodePtr = errno = EEXIST; + Tcl_SetErrno(errno); + return NULL; + } + + err = HOpenDF(fileSpec.vRefNum, fileSpec.parID, fileSpec.name, macPermision, &fileRef); + if (err != noErr) { + *errorCodePtr = errno = TclMacOSErrorToPosixError(err); + Tcl_SetErrno(errno); + return NULL; + } + + if (mode & O_TRUNC) { + SetEOF(fileRef, 0); + } + + sprintf(channelName, "file%d", (int) fileRef); + fileState = (FileState *) ckalloc((unsigned) sizeof(FileState)); + chan = Tcl_CreateChannel(&fileChannelType, channelName, + (ClientData) fileState, channelPermissions); + if (chan == (Tcl_Channel) NULL) { + *errorCodePtr = errno = EFAULT; + Tcl_SetErrno(errno); + FSClose(fileRef); + ckfree((char *) fileState); + return NULL; + } + + fileState->fileChan = chan; + fileState->nextPtr = tsdPtr->firstFilePtr; + tsdPtr->firstFilePtr = fileState; + fileState->volumeRef = fileSpec.vRefNum; + fileState->fileRef = fileRef; + fileState->pending = 0; + fileState->watchMask = 0; + if (mode & O_APPEND) { + fileState->appendMode = true; + } else { + fileState->appendMode = false; + } + + if ((mode & O_APPEND) || (mode & O_APPEND)) { + if (Tcl_Seek(chan, 0, SEEK_END) < 0) { + *errorCodePtr = errno = EFAULT; + Tcl_SetErrno(errno); + Tcl_Close(NULL, chan); + FSClose(fileRef); + ckfree((char *) fileState); + return NULL; + } + } + + return chan; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_MakeFileChannel -- + * + * Makes a Tcl_Channel from an existing OS level file handle. + * + * Results: + * The Tcl_Channel created around the preexisting OS level file handle. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Channel +Tcl_MakeFileChannel(handle, mode) + ClientData handle; /* OS level handle. */ + int mode; /* ORed combination of TCL_READABLE and + * TCL_WRITABLE to indicate file mode. */ +{ + /* + * Not implemented yet. + */ + + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * FileBlockMode -- + * + * Set blocking or non-blocking mode on channel. Macintosh files + * can never really be set to blocking or non-blocking modes. + * However, we don't generate an error - we just return success. + * + * Results: + * 0 if successful, errno when failed. + * + * Side effects: + * Sets the device into blocking or non-blocking mode. + * + *---------------------------------------------------------------------- + */ + +static int +FileBlockMode( + ClientData instanceData, /* Unused. */ + int mode) /* The mode to set. */ +{ + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * FileClose -- + * + * Closes the IO channel. + * + * Results: + * 0 if successful, the value of errno if failed. + * + * Side effects: + * Closes the physical channel + * + *---------------------------------------------------------------------- + */ + +static int +FileClose( + ClientData instanceData, /* Unused. */ + Tcl_Interp *interp) /* Unused. */ +{ + FileState *fileState = (FileState *) instanceData; + int errorCode = 0; + OSErr err; + + err = FSClose(fileState->fileRef); + FlushVol(NULL, fileState->volumeRef); + if (err != noErr) { + errorCode = errno = TclMacOSErrorToPosixError(err); + Tcl_Panic("error during file close"); + } + + ckfree((char *) fileState); + Tcl_SetErrno(errorCode); + return errorCode; +} + +/* + *---------------------------------------------------------------------- + * + * FileInput -- + * + * Reads input from the IO channel into the buffer given. Returns + * count of how many bytes were actually read, and an error indication. + * + * Results: + * A count of how many bytes were read is returned and an error + * indication is returned in an output argument. + * + * Side effects: + * Reads input from the actual channel. + * + *---------------------------------------------------------------------- + */ + +int +FileInput( + ClientData instanceData, /* Unused. */ + char *buffer, /* Where to store data read. */ + int bufSize, /* How much space is available + * in the buffer? */ + int *errorCodePtr) /* Where to store error code. */ +{ + FileState *fileState = (FileState *) instanceData; + OSErr err; + long length = bufSize; + + *errorCodePtr = 0; + errno = 0; + err = FSRead(fileState->fileRef, &length, buffer); + if ((err == noErr) || (err == eofErr)) { + return length; + } else { + switch (err) { + case ioErr: + *errorCodePtr = errno = EIO; + case afpAccessDenied: + *errorCodePtr = errno = EACCES; + default: + *errorCodePtr = errno = EINVAL; + } + return -1; + } + *errorCodePtr = errno; + return -1; +} + +/* + *---------------------------------------------------------------------- + * + * FileOutput-- + * + * Writes the given output on the IO channel. Returns count of how + * many characters were actually written, and an error indication. + * + * Results: + * A count of how many characters were written is returned and an + * error indication is returned in an output argument. + * + * Side effects: + * Writes output on the actual channel. + * + *---------------------------------------------------------------------- + */ + +static int +FileOutput( + ClientData instanceData, /* Unused. */ + CONST char *buffer, /* The data buffer. */ + int toWrite, /* How many bytes to write? */ + int *errorCodePtr) /* Where to store error code. */ +{ + FileState *fileState = (FileState *) instanceData; + long length = toWrite; + OSErr err; + + *errorCodePtr = 0; + errno = 0; + + if (fileState->appendMode == true) { + FileSeek(instanceData, 0, SEEK_END, errorCodePtr); + *errorCodePtr = 0; + } + + err = FSWrite(fileState->fileRef, &length, buffer); + if (err == noErr) { + err = FlushFile(fileState->fileRef); + } else { + *errorCodePtr = errno = TclMacOSErrorToPosixError(err); + return -1; + } + return length; +} + +/* + *---------------------------------------------------------------------- + * + * FileSeek -- + * + * Seeks on an IO channel. Returns the new position. + * + * Results: + * -1 if failed, the new position if successful. If failed, it + * also sets *errorCodePtr to the error code. + * + * Side effects: + * Moves the location at which the channel will be accessed in + * future operations. + * + *---------------------------------------------------------------------- + */ + +static int +FileSeek( + ClientData instanceData, /* Unused. */ + long offset, /* Offset to seek to. */ + int mode, /* Relative to where should we seek? */ + int *errorCodePtr) /* To store error code. */ +{ + FileState *fileState = (FileState *) instanceData; + IOParam pb; + OSErr err; + + *errorCodePtr = 0; + pb.ioCompletion = NULL; + pb.ioRefNum = fileState->fileRef; + if (mode == SEEK_SET) { + pb.ioPosMode = fsFromStart; + } else if (mode == SEEK_END) { + pb.ioPosMode = fsFromLEOF; + } else if (mode == SEEK_CUR) { + err = PBGetFPosSync((ParmBlkPtr) &pb); + if (pb.ioResult == noErr) { + if (offset == 0) { + return pb.ioPosOffset; + } + offset += pb.ioPosOffset; + } + pb.ioPosMode = fsFromStart; + } + pb.ioPosOffset = offset; + err = PBSetFPosSync((ParmBlkPtr) &pb); + if (pb.ioResult == noErr){ + return pb.ioPosOffset; + } else if (pb.ioResult == eofErr) { + long currentEOF, newEOF; + long buffer, i, length; + + err = PBGetEOFSync((ParmBlkPtr) &pb); + currentEOF = (long) pb.ioMisc; + if (mode == SEEK_SET) { + newEOF = offset; + } else if (mode == SEEK_END) { + newEOF = offset + currentEOF; + } else if (mode == SEEK_CUR) { + err = PBGetFPosSync((ParmBlkPtr) &pb); + newEOF = offset + pb.ioPosOffset; + } + + /* + * Write 0's to the new EOF. + */ + pb.ioPosOffset = 0; + pb.ioPosMode = fsFromLEOF; + err = PBGetFPosSync((ParmBlkPtr) &pb); + length = 1; + buffer = 0; + for (i = 0; i < (newEOF - currentEOF); i++) { + err = FSWrite(fileState->fileRef, &length, &buffer); + } + err = PBGetFPosSync((ParmBlkPtr) &pb); + if (pb.ioResult == noErr){ + return pb.ioPosOffset; + } + } + *errorCodePtr = errno = TclMacOSErrorToPosixError(err); + return -1; +} + +/* + *---------------------------------------------------------------------- + * + * CommonWatch -- + * + * Initialize the notifier to watch handles from this channel. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +CommonWatch( + ClientData instanceData, /* The file state. */ + int mask) /* Events of interest; an OR-ed + * combination of TCL_READABLE, + * TCL_WRITABLE and TCL_EXCEPTION. */ +{ + FileState *infoPtr = (FileState *) instanceData; + Tcl_Time blockTime = { 0, 0 }; + + infoPtr->watchMask = mask; + if (infoPtr->watchMask) { + Tcl_SetMaxBlockTime(&blockTime); + } +} + +/* + *---------------------------------------------------------------------- + * + * TclpCutFileChannel -- + * + * Remove any thread local refs to this channel. See + * Tcl_CutChannel for more info. + * + * Results: + * None. + * + * Side effects: + * Changes thread local list of valid channels. + * + *---------------------------------------------------------------------- + */ + +void +TclpCutFileChannel(chan) + Tcl_Channel chan; /* The channel being removed. Must + * not be referenced in any + * interpreter. */ +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + Channel *chanPtr = (Channel *) chan; + FileState *infoPtr; + FileState **nextPtrPtr; + int removed = 0; + + if (chanPtr->typePtr != &fileChannelType) + return; + + infoPtr = (FileState *) chanPtr->instanceData; + + for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL; + nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { + if ((*nextPtrPtr) == infoPtr) { + (*nextPtrPtr) = infoPtr->nextPtr; + removed = 1; + break; + } + } + + /* + * This could happen if the channel was created in one thread + * and then moved to another without updating the thread + * local data in each thread. + */ + + if (!removed) { + Tcl_Panic("file info ptr not on thread channel list"); + } +} + +/* + *---------------------------------------------------------------------- + * + * TclpSpliceFileChannel -- + * + * Insert thread local ref for this channel. + * Tcl_SpliceChannel for more info. + * + * Results: + * None. + * + * Side effects: + * Changes thread local list of valid channels. + * + *---------------------------------------------------------------------- + */ + +void +TclpSpliceFileChannel(chan) + Tcl_Channel chan; /* The channel being removed. Must + * not be referenced in any + * interpreter. */ +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + Channel *chanPtr = (Channel *) chan; + FileState *infoPtr; + + if (chanPtr->typePtr != &fileChannelType) + return; + + infoPtr = (FileState *) chanPtr->instanceData; + + infoPtr->nextPtr = tsdPtr->firstFilePtr; + tsdPtr->firstFilePtr = infoPtr; +} diff --git a/mac/tclMacCommonPch.h b/mac/tclMacCommonPch.h new file mode 100755 index 0000000..c0deb9d --- /dev/null +++ b/mac/tclMacCommonPch.h @@ -0,0 +1,71 @@ +/* + * tclMacCommonPch.h -- + * + * Macintosh Tcl must be compiled with certain compiler options to + * ensure that it will work correctly. The following pragmas are + * used to ensure that those options are set correctly. An error + * will occur at compile time if they are not set correctly. + * + * Copyright (c) 1998 by Scriptics Corporation. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacCommonPch.h,v 1.3 2001/11/23 01:27:24 das Exp $ + */ + +#if !__option(enumsalwaysint) +#error Tcl requires the Metrowerks setting "Enums always ints". +#endif + + +#if !defined(__POWERPC__) +#if !__option(far_data) +#error Tcl requires the Metrowerks setting "Far data". +#endif +#endif + + +#if !defined(__POWERPC__) +#if !__option(fourbyteints) +#error Tcl requires the Metrowerks setting "4 byte ints". +#endif +#endif + + +#if !defined(__POWERPC__) +#if !__option(IEEEdoubles) +#error Tcl requires the Metrowerks setting "8 byte doubles". +#endif +#endif + + +/* +* The define is used most everywhere to tell Tcl (or any Tcl +* extensions) that we are compiling for the Macintosh platform. +*/ + + +#define MAC_TCL + + +/* +* Define the following symbol if you want +* comprehensive debugging turned on. +*/ + + +/* #define TCL_DEBUG */ + + +#ifdef TCL_DEBUG +# define TCL_MEM_DEBUG +# define TCL_TEST +#endif + + +/* +* for Metrowerks Pro 6 MSL +*/ + +#include <UseDLLPrefix.h> diff --git a/mac/tclMacDNR.c b/mac/tclMacDNR.c new file mode 100644 index 0000000..fa7058b --- /dev/null +++ b/mac/tclMacDNR.c @@ -0,0 +1,23 @@ +/* + * tclMacDNR.c + * + * This file actually just includes the file "dnr.c" provided by + * Apple Computer and redistributed by MetroWerks (and other compiler + * vendors.) Unfortunantly, despite various bug reports, dnr.c uses + * C++ style comments and will not compile under the "ANSI Strict" + * mode that the rest of Tcl compiles under. Furthermore, the Apple + * license prohibits me from redistributing a corrected version of + * dnr.c. This file uses a pragma to turn off the Strict ANSI option + * and then includes the dnr.c file. + * + * Copyright (c) 1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacDNR.c,v 1.2 1998/09/14 18:40:04 stanton Exp $ + */ + +#pragma ANSI_strict off +#include <dnr.c> +#pragma ANSI_strict reset diff --git a/mac/tclMacEnv.c b/mac/tclMacEnv.c new file mode 100644 index 0000000..8f6b58f --- /dev/null +++ b/mac/tclMacEnv.c @@ -0,0 +1,536 @@ +/* + * tclMacEnv.c -- + * + * Implements the "environment" on a Macintosh. + * + * Copyright (c) 1995-1996 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacEnv.c,v 1.2 1998/09/14 18:40:04 stanton Exp $ + */ + +#include <Gestalt.h> +#include <Folders.h> +#include <TextUtils.h> +#include <Resources.h> +#include <string.h> + +#include "tcl.h" +#include "tclInt.h" +#include "tclMacInt.h" +#include "tclPort.h" + +#define kMaxEnvStringSize 255 +#define kMaxEnvVarSize 100 +#define kLoginnameTag "LOGIN=" +#define kUsernameTag "USER=" +#define kDefaultDirTag "HOME=" + +/* + * The following specifies a text file where additional environment variables + * can be set. The file must reside in the preferences folder. If the file + * doesn't exist NO error will occur. Commet out the difinition if you do + * NOT want to use an environment variables file. + */ +#define kPrefsFile "Tcl Environment Variables" + +/* + * The following specifies the Name of a 'STR#' resource in the application + * where additional environment variables may be set. If the resource doesn't + * exist no errors will occur. Commet it out if you don't want it. + */ +#define REZ_ENV "\pTcl Environment Variables" + +/* Globals */ +char **environ = NULL; + +/* + * Declarations for local procedures defined in this file: + */ +static char ** RezRCVariables _ANSI_ARGS_((void)); +static char ** FileRCVariables _ANSI_ARGS_((void)); +static char ** PathVariables _ANSI_ARGS_((void)); +static char ** SystemVariables _ANSI_ARGS_((void)); +static char * MakeFolderEnvVar _ANSI_ARGS_((char * prefixTag, + long whichFolder)); +static char * GetUserName _ANSI_ARGS_((void)); + +/* + *---------------------------------------------------------------------- + * + * RezRCVariables -- + * + * Creates environment variables from the applications resource fork. + * The function looks for the 'STR#' resource with the name defined + * in the #define REZ_ENV. If the define is not defined this code + * will not be included. If the resource doesn't exist or no strings + * reside in the resource nothing will happen. + * + * Results: + * ptr to value on success, NULL if error. + * + * Side effects: + * Memory is allocated and returned to the caller. + * + *---------------------------------------------------------------------- + */ + +#ifdef REZ_ENV +static char ** +RezRCVariables() +{ + Handle envStrs = NULL; + char** rezEnv = NULL; + short int numStrs; + + envStrs = GetNamedResource('STR#', REZ_ENV); + if (envStrs == NULL) return NULL; + numStrs = *((short *) (*envStrs)); + + rezEnv = (char **) ckalloc((numStrs + 1) * sizeof(char *)); + + if (envStrs != NULL) { + ResType theType; + Str255 theName; + short theID, index = 1; + int i = 0; + char* string; + + GetResInfo(envStrs, &theID, &theType, theName); + for(;;) { + GetIndString(theName, theID, index++); + if (theName[0] == '\0') break; + string = (char *) ckalloc(theName[0] + 2); + strncpy(string, (char *) theName + 1, theName[0]); + string[theName[0]] = '\0'; + rezEnv[i++] = string; + } + ReleaseResource(envStrs); + + rezEnv[i] = NULL; + return rezEnv; + } + + return NULL; +} +#endif + +/* + *---------------------------------------------------------------------- + * + * FileRCVariables -- + * + * Creates environment variables from a file in the system preferences + * folder. The function looks for a file in the preferences folder + * a name defined in the #define kPrefsFile. If the define is not + * defined this code will not be included. If the resource doesn't exist or + * no strings reside in the resource nothing will happen. + * + * Results: + * ptr to value on success, NULL if error. + * + * Side effects: + * Memory is allocated and returned to the caller. + * + *---------------------------------------------------------------------- + */ + +#ifdef kPrefsFile +static char ** +FileRCVariables() +{ + char *prefsFolder = NULL; + char *tempPtr = NULL; + char **fileEnv = NULL; + FILE *thePrefsFile = NULL; + int i; + FSSpec prefDir; + OSErr err; + Handle theString = NULL; + Tcl_Channel chan; + int size; + Tcl_DString lineRead; + + err = FSpFindFolder(kOnSystemDisk, kPreferencesFolderType, + kDontCreateFolder, &prefDir); + if (err != noErr) { + return NULL; + } + err = FSpPathFromLocation(&prefDir, &size, &theString); + if (err != noErr) { + return NULL; + } + (void) Munger(theString, size, NULL, 0, kPrefsFile, strlen(kPrefsFile)); + + HLock(theString); + chan = Tcl_OpenFileChannel(NULL, *theString, "r", 0); + HUnlock(theString); + DisposeHandle(theString); + if (chan == NULL) { + return NULL; + } + + /* + * We found a env file. Let start parsing it. + */ + fileEnv = (char **) ckalloc((kMaxEnvVarSize + 1) * sizeof(char *)); + + i = 0; + Tcl_DStringInit(&lineRead); + while (Tcl_Gets(chan, &lineRead) != -1) { + /* + * First strip off new line char + */ + if (lineRead.string[lineRead.length-1] == '\n') { + lineRead.string[lineRead.length-1] = '\0'; + } + if (lineRead.string[0] == '\0' || lineRead.string[0] == '#') { + /* + * skip empty lines or commented lines + */ + Tcl_DStringSetLength(&lineRead, 0); + continue; + } + + tempPtr = (char *) ckalloc(lineRead.length + 1); + strcpy(tempPtr, lineRead.string); + fileEnv[i++] = tempPtr; + Tcl_DStringSetLength(&lineRead, 0); + } + + fileEnv[i] = NULL; + Tcl_Close(NULL, chan); + Tcl_DStringFree(&lineRead); + + return fileEnv; +} +#endif + +/* + *---------------------------------------------------------------------- + * + * MakeFolderEnvVar -- + * + * This function creates "environment" variable by taking a prefix and + * appending a folder path to a directory. The directory is specified + * by a integer value acceptable by the FindFolder function. + * + * Results: + * The function returns an *allocated* string. If the folder doesn't + * exist the return string is still allocated and just contains the + * given prefix. + * + * Side effects: + * Memory is allocated and returned to the caller. + * + *---------------------------------------------------------------------- + */ + +static char * +MakeFolderEnvVar( + char * prefixTag, /* Prefix added before result. */ + long whichFolder) /* Constant for FSpFindFolder. */ +{ + char * thePath = NULL; + char * result = NULL; + OSErr theErr = noErr; + Handle theString = NULL; + FSSpec theFolder; + int size; + Tcl_DString pathStr; + Tcl_DString tagPathStr; + + Tcl_DStringInit(&pathStr); + theErr = FSpFindFolder(kOnSystemDisk, whichFolder, + kDontCreateFolder, &theFolder); + if (theErr == noErr) { + theErr = FSpPathFromLocation(&theFolder, &size, &theString); + + HLock(theString); + tclPlatform = TCL_PLATFORM_MAC; + Tcl_DStringAppend(&pathStr, *theString, -1); + HUnlock(theString); + DisposeHandle(theString); + + Tcl_DStringInit(&tagPathStr); + Tcl_DStringAppend(&tagPathStr, prefixTag, strlen(prefixTag)); + Tcl_DStringAppend(&tagPathStr, pathStr.string, pathStr.length); + Tcl_DStringFree(&pathStr); + + /* + * Make sure the path ends with a ':' + */ + if (tagPathStr.string[tagPathStr.length - 1] != ':') { + Tcl_DStringAppend(&tagPathStr, ":", 1); + } + + /* + * Don't free tagPathStr - rather make sure it's allocated + * and return it as the result. + */ + if (tagPathStr.string == tagPathStr.staticSpace) { + result = (char *) ckalloc(tagPathStr.length + 1); + strcpy(result, tagPathStr.string); + } else { + result = tagPathStr.string; + } + } else { + result = (char *) ckalloc(strlen(prefixTag) + 1); + strcpy(result, prefixTag); + } + + return result; +} + +/* + *---------------------------------------------------------------------- + * + * PathVariables -- + * + * Creates environment variables from the system call FSpFindFolder. + * The function generates environment variables for many of the + * commonly used paths on the Macintosh. + * + * Results: + * ptr to value on success, NULL if error. + * + * Side effects: + * Memory is allocated and returned to the caller. + * + *---------------------------------------------------------------------- + */ + +static char ** +PathVariables() +{ + int i = 0; + char **sysEnv; + char *thePath = NULL; + + sysEnv = (char **) ckalloc((12) * sizeof(char *)); + + sysEnv[i++] = MakeFolderEnvVar("PREF_FOLDER=", kPreferencesFolderType); + sysEnv[i++] = MakeFolderEnvVar("SYS_FOLDER=", kSystemFolderType); + sysEnv[i++] = MakeFolderEnvVar("TEMP=", kTemporaryFolderType); + sysEnv[i++] = MakeFolderEnvVar("APPLE_M_FOLDER=", kAppleMenuFolderType); + sysEnv[i++] = MakeFolderEnvVar("CP_FOLDER=", kControlPanelFolderType); + sysEnv[i++] = MakeFolderEnvVar("DESK_FOLDER=", kDesktopFolderType); + sysEnv[i++] = MakeFolderEnvVar("EXT_FOLDER=", kExtensionFolderType); + sysEnv[i++] = MakeFolderEnvVar("PRINT_MON_FOLDER=", + kPrintMonitorDocsFolderType); + sysEnv[i++] = MakeFolderEnvVar("SHARED_TRASH_FOLDER=", + kWhereToEmptyTrashFolderType); + sysEnv[i++] = MakeFolderEnvVar("TRASH_FOLDER=", kTrashFolderType); + sysEnv[i++] = MakeFolderEnvVar("START_UP_FOLDER=", kStartupFolderType); + sysEnv[i++] = NULL; + + return sysEnv; +} + +/* + *---------------------------------------------------------------------- + * + * SystemVariables -- + * + * Creates environment variables from various Mac system calls. + * + * Results: + * ptr to value on success, NULL if error. + * + * Side effects: + * Memory is allocated and returned to the caller. + * + *---------------------------------------------------------------------- + */ + +static char ** +SystemVariables() +{ + int i = 0; + char ** sysEnv; + char * thePath = NULL; + Handle theString = NULL; + FSSpec currentDir; + int size; + + sysEnv = (char **) ckalloc((4) * sizeof(char *)); + + /* + * Get user name from chooser. It will be assigned to both + * the USER and LOGIN environment variables. + */ + thePath = GetUserName(); + if (thePath != NULL) { + sysEnv[i] = (char *) ckalloc(strlen(kLoginnameTag) + strlen(thePath) + 1); + strcpy(sysEnv[i], kLoginnameTag); + strcpy(sysEnv[i]+strlen(kLoginnameTag), thePath); + i++; + sysEnv[i] = (char *) ckalloc(strlen(kUsernameTag) + strlen(thePath) + 1); + strcpy(sysEnv[i], kUsernameTag); + strcpy(sysEnv[i]+strlen(kUsernameTag), thePath); + i++; + } + + /* + * Get 'home' directory + */ +#ifdef kDefaultDirTag + FSpGetDefaultDir(¤tDir); + FSpPathFromLocation(¤tDir, &size, &theString); + HLock(theString); + sysEnv[i] = (char *) ckalloc(strlen(kDefaultDirTag) + size + 4); + strcpy(sysEnv[i], kDefaultDirTag); + strncpy(sysEnv[i]+strlen(kDefaultDirTag) , *theString, size); + if (sysEnv[i][strlen(kDefaultDirTag) + size - 1] != ':') { + sysEnv[i][strlen(kDefaultDirTag) + size] = ':'; + sysEnv[i][strlen(kDefaultDirTag) + size + 1] = '\0'; + } else { + sysEnv[i][strlen(kDefaultDirTag) + size] = '\0'; + } + HUnlock(theString); + DisposeHandle(theString); + i++; +#endif + + sysEnv[i++] = NULL; + return sysEnv; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacCreateEnv -- + * + * This function allocates and populates the global "environ" + * variable. Entries are in traditional Unix format but variables + * are, hopefully, a bit more relevant for the Macintosh. + * + * Results: + * The number of elements in the newly created environ array. + * + * Side effects: + * Memory is allocated and pointed too by the environ variable. + * + *---------------------------------------------------------------------- + */ + +int +TclMacCreateEnv() +{ + char ** sysEnv = NULL; + char ** pathEnv = NULL; + char ** fileEnv = NULL; + char ** rezEnv = NULL; + int count = 0; + int i, j; + + sysEnv = SystemVariables(); + if (sysEnv != NULL) { + for (i = 0; sysEnv[i] != NULL; count++, i++) { + /* Empty Loop */ + } + } + + pathEnv = PathVariables(); + if (pathEnv != NULL) { + for (i = 0; pathEnv[i] != NULL; count++, i++) { + /* Empty Loop */ + } + } + +#ifdef kPrefsFile + fileEnv = FileRCVariables(); + if (fileEnv != NULL) { + for (i = 0; fileEnv[i] != NULL; count++, i++) { + /* Empty Loop */ + } + } +#endif + +#ifdef REZ_ENV + rezEnv = RezRCVariables(); + if (rezEnv != NULL) { + for (i = 0; rezEnv[i] != NULL; count++, i++) { + /* Empty Loop */ + } + } +#endif + + /* + * Create environ variable + */ + environ = (char **) ckalloc((count + 1) * sizeof(char *)); + j = 0; + + if (sysEnv != NULL) { + for (i = 0; sysEnv[i] != NULL;) + environ[j++] = sysEnv[i++]; + ckfree((char *) sysEnv); + } + + if (pathEnv != NULL) { + for (i = 0; pathEnv[i] != NULL;) + environ[j++] = pathEnv[i++]; + ckfree((char *) pathEnv); + } + +#ifdef kPrefsFile + if (fileEnv != NULL) { + for (i = 0; fileEnv[i] != NULL;) + environ[j++] = fileEnv[i++]; + ckfree((char *) fileEnv); + } +#endif + +#ifdef REZ_ENV + if (rezEnv != NULL) { + for (i = 0; rezEnv[i] != NULL;) + environ[j++] = rezEnv[i++]; + ckfree((char *) rezEnv); + } +#endif + + environ[j] = NULL; + return j; +} + +/* + *---------------------------------------------------------------------- + * + * GetUserName -- + * + * Get the user login name. + * + * Results: + * ptr to static string, NULL if error. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static char * +GetUserName() +{ + static char buf[33]; + short refnum; + Handle h; + + refnum = CurResFile(); + UseResFile(0); + h = GetResource('STR ', -16096); + UseResFile(refnum); + if (h == NULL) { + return NULL; + } + + HLock(h); + strncpy(buf, (*h)+1, **h); + buf[**h] = '\0'; + HUnlock(h); + ReleaseResource(h); + return(buf[0] ? buf : NULL); +} diff --git a/mac/tclMacExit.c b/mac/tclMacExit.c new file mode 100644 index 0000000..347ff4e --- /dev/null +++ b/mac/tclMacExit.c @@ -0,0 +1,333 @@ +/* + * tclMacExit.c -- + * + * This file contains routines that deal with cleaning up various state + * when Tcl/Tk applications quit. Unfortunantly, not all state is cleaned + * up by the process when an application quites or crashes. Also you + * need to do different things depending on wether you are running as + * 68k code, PowerPC, or a code resource. The Exit handler code was + * adapted from code posted on alt.sources.mac by Dave Nebinger. + * + * Copyright (c) 1995 Dave Nebinger. + * Copyright (c) 1995-1996 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacExit.c,v 1.4 1999/04/16 00:47:19 stanton Exp $ + */ + +#include "tclInt.h" +#include "tclMacInt.h" +#include <SegLoad.h> +#include <Traps.h> +#include <Processes.h> + +/* + * Various typedefs and defines needed to patch ExitToShell. + */ + +enum { + uppExitToShellProcInfo = kPascalStackBased +}; + +#if GENERATINGCFM +typedef UniversalProcPtr ExitToShellUPP; + +#define CallExitToShellProc(userRoutine) \ + CallUniversalProc((UniversalProcPtr)(userRoutine),uppExitToShellProcInfo) +#define NewExitToShellProc(userRoutine) \ + (ExitToShellUPP)NewRoutineDescriptor((ProcPtr)(userRoutine), \ + uppExitToShellProcInfo, GetCurrentArchitecture()) + +#else +typedef ExitToShellProcPtr ExitToShellUPP; + +#define CallExitToShellProc(userRoutine) \ + (*(userRoutine))() +#define NewExitToShellProc(userRoutine) \ + (ExitToShellUPP)(userRoutine) +#endif + +#define DisposeExitToShellProc(userRoutine) \ + DisposeRoutineDescriptor(userRoutine) + +#if defined(powerc)||defined(__powerc) +#pragma options align=mac68k +#endif +struct ExitToShellUPPList{ + struct ExitToShellUPPList* nextProc; + ExitToShellUPP userProc; +}; +#if defined(powerc)||defined(__powerc) +#pragma options align=reset +#endif + +typedef struct ExitToShellDataStruct ExitToShellDataRec,* ExitToShellDataPtr,** ExitToShellDataHdl; + +typedef struct ExitToShellUPPList ExitToShellUPPList,* ExitToShellUPPListPtr,** ExitToShellUPPHdl; + +#if defined(powerc)||defined(__powerc) +#pragma options align=mac68k +#endif +struct ExitToShellDataStruct{ + unsigned long a5; + ExitToShellUPPList* userProcs; + ExitToShellUPP oldProc; +}; +#if defined(powerc)||defined(__powerc) +#pragma options align=reset +#endif + +/* + * Static globals used within this file. + */ +static ExitToShellDataPtr gExitToShellData = (ExitToShellDataPtr) NULL; + + +/* + *---------------------------------------------------------------------- + * + * TclPlatformExit -- + * + * This procedure implements the Macintosh specific exit routine. + * We explicitly callthe ExitHandler function to do various clean + * up. + * + * Results: + * None. + * + * Side effects: + * We exit the process. + * + *---------------------------------------------------------------------- + */ + +void +TclpExit( + int status) /* Ignored. */ +{ + TclMacExitHandler(); + +/* + * If we are using the Metrowerks Standard Library, then we will call its exit so that it + * will get a chance to clean up temp files, and so forth. It always calls the standard + * ExitToShell, so the Tcl handlers will also get called. + * + * If you have another exit, make sure that it does not patch ExitToShell, and does + * call it. If so, it will probably work as well. + * + */ + +#ifdef __MSL__ + exit(status); +#else + ExitToShell(); +#endif + +} + +/* + *---------------------------------------------------------------------- + * + * TclMacExitHandler -- + * + * This procedure is invoked after Tcl at the last possible moment + * to clean up any state Tcl has left around that may cause other + * applications to crash. For example, this function can be used + * as the termination routine for CFM applications. + * + * Results: + * None. + * + * Side effects: + * Various cleanup occurs. + * + *---------------------------------------------------------------------- + */ + +void +TclMacExitHandler() +{ + ExitToShellUPPListPtr curProc; + + /* + * Loop through all installed Exit handlers + * and call them. Always make sure we are in + * a clean state in case we are recursivly called. + */ + if ((gExitToShellData) != NULL && (gExitToShellData->userProcs != NULL)){ + + /* + * Call the installed exit to shell routines. + */ + curProc = gExitToShellData->userProcs; + do { + gExitToShellData->userProcs = curProc->nextProc; + CallExitToShellProc(curProc->userProc); + DisposeExitToShellProc(curProc->userProc); + DisposePtr((Ptr) curProc); + curProc = gExitToShellData->userProcs; + } while (curProc != (ExitToShellUPPListPtr) NULL); + } + + return; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacInstallExitToShellPatch -- + * + * This procedure installs a way to clean up state at the latest + * possible moment before we exit. These are things that must + * be cleaned up or the system will crash. The exact way in which + * this is implemented depends on the architecture in which we are + * running. For 68k applications we patch the ExitToShell call. + * For PowerPC applications we just create a list of procs to call. + * The function ExitHandler should be installed in the Code + * Fragments terminiation routine. + * + * Results: + * None. + * + * Side effects: + * Installs the new routine. + * + *---------------------------------------------------------------------- + */ + +OSErr +TclMacInstallExitToShellPatch( + ExitToShellProcPtr newProc) /* Function pointer. */ +{ + ExitToShellUPP exitHandler; + ExitToShellUPPListPtr listPtr; + + if (gExitToShellData == (ExitToShellDataPtr) NULL){ + TclMacInitExitToShell(true); + } + + /* + * Add the passed in function pointer to the list of functions + * to be called when ExitToShell is called. + */ + exitHandler = NewExitToShellProc(newProc); + listPtr = (ExitToShellUPPListPtr) NewPtrClear(sizeof(ExitToShellUPPList)); + listPtr->userProc = exitHandler; + listPtr->nextProc = gExitToShellData->userProcs; + gExitToShellData->userProcs = listPtr; + + return noErr; +} + +/* + *---------------------------------------------------------------------- + * + * ExitToShellPatchRoutine -- + * + * This procedure is invoked when someone calls ExitToShell for + * this application. This function performs some last miniute + * clean up and then calls the real ExitToShell routine. + * + * Results: + * None. + * + * Side effects: + * Various cleanup occurs. + * + *---------------------------------------------------------------------- + */ + +static pascal void +ExitToShellPatchRoutine() +{ + ExitToShellUPP oldETS; + long oldA5; + + /* + * Set up our A5 world. This allows us to have + * access to our global variables in the 68k world. + */ + oldA5 = SetCurrentA5(); + SetA5(gExitToShellData->a5); + + /* + * Call the function that invokes all + * of the handlers. + */ + TclMacExitHandler(); + + /* + * Call the origional ExitToShell routine. + */ + oldETS = gExitToShellData->oldProc; + DisposePtr((Ptr) gExitToShellData); + SetA5(oldA5); + CallExitToShellProc(oldETS); + return; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacInitExitToShell -- + * + * This procedure initializes the ExitToShell clean up machanism. + * Generally, this is handled automatically when users make a call + * to InstallExitToShellPatch. However, it can be called + * explicitly at startup time to turn off the patching mechanism. + * This can be used by code resources which could be removed from + * the application before ExitToShell is called. + * + * Note, if we are running from CFM code we never install the + * patch. Instead, the function ExitHandler should be installed + * as the terminiation routine for the code fragment. + * + * Results: + * None. + * + * Side effects: + * Creates global state. + * + *---------------------------------------------------------------------- + */ + +void +TclMacInitExitToShell( + int usePatch) /* True if on 68k. */ +{ + if (gExitToShellData == (ExitToShellDataPtr) NULL){ +#if GENERATINGCFM + gExitToShellData = (ExitToShellDataPtr) + NewPtr(sizeof(ExitToShellDataRec)); + gExitToShellData->a5 = SetCurrentA5(); + gExitToShellData->userProcs = (ExitToShellUPPList*) NULL; +#else + ExitToShellUPP oldExitToShell, newExitToShellPatch; + short exitToShellTrap; + + /* + * Initialize patch mechanism. + */ + + gExitToShellData = (ExitToShellDataPtr) NewPtr(sizeof(ExitToShellDataRec)); + gExitToShellData->a5 = SetCurrentA5(); + gExitToShellData->userProcs = (ExitToShellUPPList*) NULL; + + /* + * Save state needed to call origional ExitToShell routine. Install + * the new ExitToShell code in it's place. + */ + if (usePatch) { + exitToShellTrap = _ExitToShell & 0x3ff; + newExitToShellPatch = NewExitToShellProc(ExitToShellPatchRoutine); + oldExitToShell = (ExitToShellUPP) + NGetTrapAddress(exitToShellTrap, ToolTrap); + NSetTrapAddress((UniversalProcPtr) newExitToShellPatch, + exitToShellTrap, ToolTrap); + gExitToShellData->oldProc = oldExitToShell; + } +#endif + } +} diff --git a/mac/tclMacFCmd.c b/mac/tclMacFCmd.c new file mode 100644 index 0000000..a1307b4 --- /dev/null +++ b/mac/tclMacFCmd.c @@ -0,0 +1,1721 @@ +/* + * tclMacFCmd.c -- + * + * Implements the Macintosh specific portions of the file manipulation + * subcommands of the "file" command. + * + * Copyright (c) 1996-1998 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacFCmd.c,v 1.20 2003/05/14 19:21:23 das Exp $ + */ + +#include "tclInt.h" +#include "tclMac.h" +#include "tclMacInt.h" +#include "tclPort.h" +#include <FSpCompat.h> +#include <MoreFilesExtras.h> +#include <Strings.h> +#include <Errors.h> +#include <FileCopy.h> +#include <DirectoryCopy.h> +#include <Script.h> +#include <string.h> +#include <Finder.h> +#include <Aliases.h> +#include <Resources.h> + +/* + * Callback for the file attributes code. + */ + +static int GetFileFinderAttributes _ANSI_ARGS_((Tcl_Interp *interp, + int objIndex, Tcl_Obj *fileName, + Tcl_Obj **attributePtrPtr)); +static int GetFileReadOnly _ANSI_ARGS_((Tcl_Interp *interp, + int objIndex, Tcl_Obj *fileName, + Tcl_Obj **readOnlyPtrPtr)); +static int SetFileFinderAttributes _ANSI_ARGS_((Tcl_Interp *interp, + int objIndex, Tcl_Obj *fileName, + Tcl_Obj *attributePtr)); +static int SetFileReadOnly _ANSI_ARGS_((Tcl_Interp *interp, + int objIndex, Tcl_Obj *fileName, + Tcl_Obj *readOnlyPtr)); + +/* + * These are indeces into the tclpFileAttrsStrings table below. + */ + +#define MAC_CREATOR_ATTRIBUTE 0 +#define MAC_HIDDEN_ATTRIBUTE 1 +#define MAC_READONLY_ATTRIBUTE 2 +#define MAC_TYPE_ATTRIBUTE 3 +#define MAC_RSRCLENGTH_ATTRIBUTE 4 + +/* + * Global variables for the file attributes code. + */ + +CONST char *tclpFileAttrStrings[] = {"-creator", "-hidden", "-readonly", + "-type", "-rsrclength", (char *) NULL}; +CONST TclFileAttrProcs tclpFileAttrProcs[] = { + {GetFileFinderAttributes, SetFileFinderAttributes}, + {GetFileFinderAttributes, SetFileFinderAttributes}, + {GetFileReadOnly, SetFileReadOnly}, + {GetFileFinderAttributes, SetFileFinderAttributes}, + {GetFileFinderAttributes, SetFileFinderAttributes}}; + +/* + * File specific static data + */ + +static long startSeed = 248923489; + +/* + * Prototypes for procedure only used in this file + */ + +static pascal Boolean CopyErrHandler _ANSI_ARGS_((OSErr error, + short failedOperation, + short srcVRefNum, long srcDirID, + ConstStr255Param srcName, short dstVRefNum, + long dstDirID,ConstStr255Param dstName)); +static int DoCopyDirectory _ANSI_ARGS_((CONST char *src, + CONST char *dst, Tcl_DString *errorPtr)); +static int DoCopyFile _ANSI_ARGS_((CONST char *src, + CONST char *dst)); +static int DoCreateDirectory _ANSI_ARGS_((CONST char *path)); +static int DoRemoveDirectory _ANSI_ARGS_((CONST char *path, + int recursive, Tcl_DString *errorPtr)); +static int DoRenameFile _ANSI_ARGS_((CONST char *src, + CONST char *dst)); +OSErr FSpGetFLockCompat _ANSI_ARGS_((const FSSpec *specPtr, + Boolean *lockedPtr)); +static OSErr GetFileSpecs _ANSI_ARGS_((CONST char *path, + FSSpec *pathSpecPtr, FSSpec *dirSpecPtr, + Boolean *pathExistsPtr, + Boolean *pathIsDirectoryPtr)); +static OSErr MoveRename _ANSI_ARGS_((const FSSpec *srcSpecPtr, + const FSSpec *dstSpecPtr, StringPtr copyName)); +static int Pstrequal _ANSI_ARGS_((ConstStr255Param stringA, + ConstStr255Param stringB)); + +/* + *--------------------------------------------------------------------------- + * + * TclpObjRenameFile, DoRenameFile -- + * + * Changes the name of an existing file or directory, from src to dst. + * If src and dst refer to the same file or directory, does nothing + * and returns success. Otherwise if dst already exists, it will be + * deleted and replaced by src subject to the following conditions: + * If src is a directory, dst may be an empty directory. + * If src is a file, dst may be a file. + * In any other situation where dst already exists, the rename will + * fail. + * + * Results: + * If the directory was successfully created, returns TCL_OK. + * Otherwise the return value is TCL_ERROR and errno is set to + * indicate the error. Some possible values for errno are: + * + * EACCES: src or dst parent directory can't be read and/or written. + * EEXIST: dst is a non-empty directory. + * EINVAL: src is a root directory or dst is a subdirectory of src. + * EISDIR: dst is a directory, but src is not. + * ENOENT: src doesn't exist. src or dst is "". + * ENOTDIR: src is a directory, but dst is not. + * EXDEV: src and dst are on different filesystems. + * + * Side effects: + * The implementation of rename may allow cross-filesystem renames, + * but the caller should be prepared to emulate it with copy and + * delete if errno is EXDEV. + * + *--------------------------------------------------------------------------- + */ + +int +TclpObjRenameFile(srcPathPtr, destPathPtr) + Tcl_Obj *srcPathPtr; + Tcl_Obj *destPathPtr; +{ + return DoRenameFile(Tcl_FSGetNativePath(srcPathPtr), + Tcl_FSGetNativePath(destPathPtr)); +} + +static int +DoRenameFile( + CONST char *src, /* Pathname of file or dir to be renamed + * (native). */ + CONST char *dst) /* New pathname of file or directory + * (native). */ +{ + FSSpec srcFileSpec, dstFileSpec, dstDirSpec; + OSErr err; + long srcID, dummy; + Boolean srcIsDirectory, dstIsDirectory, dstExists, dstLocked; + + err = FSpLLocationFromPath(strlen(src), src, &srcFileSpec); + if (err == noErr) { + FSpGetDirectoryID(&srcFileSpec, &srcID, &srcIsDirectory); + } + if (err == noErr) { + err = GetFileSpecs(dst, &dstFileSpec, &dstDirSpec, &dstExists, + &dstIsDirectory); + } + if (err == noErr) { + if (dstExists == 0) { + err = MoveRename(&srcFileSpec, &dstDirSpec, dstFileSpec.name); + goto end; + } + err = FSpGetFLockCompat(&dstFileSpec, &dstLocked); + if (dstLocked) { + FSpRstFLockCompat(&dstFileSpec); + } + } + if (err == noErr) { + if (srcIsDirectory) { + if (dstIsDirectory) { + /* + * The following call will remove an empty directory. If it + * fails, it's because it wasn't empty. + */ + + if (DoRemoveDirectory(dst, 0, NULL) != TCL_OK) { + return TCL_ERROR; + } + + /* + * Now that that empty directory is gone, we can try + * renaming src. If that fails, we'll put this empty + * directory back, for completeness. + */ + + err = MoveRename(&srcFileSpec, &dstDirSpec, dstFileSpec.name); + if (err != noErr) { + FSpDirCreateCompat(&dstFileSpec, smSystemScript, &dummy); + if (dstLocked) { + FSpSetFLockCompat(&dstFileSpec); + } + } + } else { + errno = ENOTDIR; + return TCL_ERROR; + } + } else { + if (dstIsDirectory) { + errno = EISDIR; + return TCL_ERROR; + } else { + /* + * Overwrite existing file by: + * + * 1. Rename existing file to temp name. + * 2. Rename old file to new name. + * 3. If success, delete temp file. If failure, + * put temp file back to old name. + */ + + Str31 tmpName; + FSSpec tmpFileSpec; + + err = GenerateUniqueName(dstFileSpec.vRefNum, &startSeed, + dstFileSpec.parID, dstFileSpec.parID, tmpName); + if (err == noErr) { + err = FSpRenameCompat(&dstFileSpec, tmpName); + } + if (err == noErr) { + err = FSMakeFSSpecCompat(dstFileSpec.vRefNum, + dstFileSpec.parID, tmpName, &tmpFileSpec); + } + if (err == noErr) { + err = MoveRename(&srcFileSpec, &dstDirSpec, + dstFileSpec.name); + } + if (err == noErr) { + FSpDeleteCompat(&tmpFileSpec); + } else { + FSpDeleteCompat(&dstFileSpec); + FSpRenameCompat(&tmpFileSpec, dstFileSpec.name); + if (dstLocked) { + FSpSetFLockCompat(&dstFileSpec); + } + } + } + } + } + + end: + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *-------------------------------------------------------------------------- + * + * MoveRename -- + * + * Helper function for TclpRenameFile. Renames a file or directory + * into the same directory or another directory. The target name + * must not already exist in the destination directory. + * + * Don't use FSpMoveRenameCompat because it doesn't work with + * directories or with locked files. + * + * Results: + * Returns a mac error indicating the cause of the failure. + * + * Side effects: + * Creates a temp file in the target directory to handle a rename + * between directories. + * + *-------------------------------------------------------------------------- + */ + +static OSErr +MoveRename( + const FSSpec *srcFileSpecPtr, /* Source object. */ + const FSSpec *dstDirSpecPtr, /* Destination directory. */ + StringPtr copyName) /* New name for object in destination + * directory. */ +{ + OSErr err; + long srcID, dstID; + Boolean srcIsDir, dstIsDir; + Str31 tmpName; + FSSpec dstFileSpec, srcDirSpec, tmpSrcFileSpec, tmpDstFileSpec; + Boolean locked; + + if (srcFileSpecPtr->parID == 1) { + /* + * Trying to rename a volume. + */ + + return badMovErr; + } + if (srcFileSpecPtr->vRefNum != dstDirSpecPtr->vRefNum) { + /* + * Renaming across volumes. + */ + + return diffVolErr; + } + err = FSpGetFLockCompat(srcFileSpecPtr, &locked); + if (locked) { + FSpRstFLockCompat(srcFileSpecPtr); + } + if (err == noErr) { + err = FSpGetDirectoryID(dstDirSpecPtr, &dstID, &dstIsDir); + } + if (err == noErr) { + if (srcFileSpecPtr->parID == dstID) { + /* + * Renaming object within directory. + */ + + err = FSpRenameCompat(srcFileSpecPtr, copyName); + goto done; + } + if (Pstrequal(srcFileSpecPtr->name, copyName)) { + /* + * Moving object to another directory (under same name). + */ + + err = FSpCatMoveCompat(srcFileSpecPtr, dstDirSpecPtr); + goto done; + } + err = FSpGetDirectoryID(srcFileSpecPtr, &srcID, &srcIsDir); + } + if (err == noErr) { + /* + * Fullblown: rename source object to temp name, move temp to + * dest directory, and rename temp to target. + */ + + err = GenerateUniqueName(srcFileSpecPtr->vRefNum, &startSeed, + srcFileSpecPtr->parID, dstID, tmpName); + FSMakeFSSpecCompat(srcFileSpecPtr->vRefNum, srcFileSpecPtr->parID, + tmpName, &tmpSrcFileSpec); + FSMakeFSSpecCompat(dstDirSpecPtr->vRefNum, dstID, tmpName, + &tmpDstFileSpec); + } + if (err == noErr) { + err = FSpRenameCompat(srcFileSpecPtr, tmpName); + } + if (err == noErr) { + err = FSpCatMoveCompat(&tmpSrcFileSpec, dstDirSpecPtr); + if (err == noErr) { + err = FSpRenameCompat(&tmpDstFileSpec, copyName); + if (err == noErr) { + goto done; + } + FSMakeFSSpecCompat(srcFileSpecPtr->vRefNum, srcFileSpecPtr->parID, + NULL, &srcDirSpec); + FSpCatMoveCompat(&tmpDstFileSpec, &srcDirSpec); + } + FSpRenameCompat(&tmpSrcFileSpec, srcFileSpecPtr->name); + } + + done: + if (locked != false) { + if (err == noErr) { + FSMakeFSSpecCompat(dstDirSpecPtr->vRefNum, + dstID, copyName, &dstFileSpec); + FSpSetFLockCompat(&dstFileSpec); + } else { + FSpSetFLockCompat(srcFileSpecPtr); + } + } + return err; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpObjCopyFile, DoCopyFile -- + * + * Copy a single file (not a directory). If dst already exists and + * is not a directory, it is removed. + * + * Results: + * If the file was successfully copied, returns TCL_OK. Otherwise + * the return value is TCL_ERROR and errno is set to indicate the + * error. Some possible values for errno are: + * + * EACCES: src or dst parent directory can't be read and/or written. + * EISDIR: src or dst is a directory. + * ENOENT: src doesn't exist. src or dst is "". + * + * Side effects: + * This procedure will also copy symbolic links, block, and + * character devices, and fifos. For symbolic links, the links + * themselves will be copied and not what they point to. For the + * other special file types, the directory entry will be copied and + * not the contents of the device that it refers to. + * + *--------------------------------------------------------------------------- + */ + +int +TclpObjCopyFile(srcPathPtr, destPathPtr) + Tcl_Obj *srcPathPtr; + Tcl_Obj *destPathPtr; +{ + return DoCopyFile(Tcl_FSGetNativePath(srcPathPtr), + Tcl_FSGetNativePath(destPathPtr)); +} + +static int +DoCopyFile( + CONST char *src, /* Pathname of file to be copied (native). */ + CONST char *dst) /* Pathname of file to copy to (native). */ +{ + OSErr err, dstErr; + Boolean dstExists, dstIsDirectory, dstLocked; + FSSpec srcFileSpec, dstFileSpec, dstDirSpec, tmpFileSpec; + Str31 tmpName; + + err = FSpLLocationFromPath(strlen(src), src, &srcFileSpec); + if (err == noErr) { + err = GetFileSpecs(dst, &dstFileSpec, &dstDirSpec, &dstExists, + &dstIsDirectory); + } + if (dstExists) { + if (dstIsDirectory) { + errno = EISDIR; + return TCL_ERROR; + } + err = FSpGetFLockCompat(&dstFileSpec, &dstLocked); + if (dstLocked) { + FSpRstFLockCompat(&dstFileSpec); + } + + /* + * Backup dest file. + */ + + dstErr = GenerateUniqueName(dstFileSpec.vRefNum, &startSeed, dstFileSpec.parID, + dstFileSpec.parID, tmpName); + if (dstErr == noErr) { + dstErr = FSpRenameCompat(&dstFileSpec, tmpName); + } + } + if (err == noErr) { + err = FSpFileCopy(&srcFileSpec, &dstDirSpec, + (StringPtr) dstFileSpec.name, NULL, 0, true); + } + if ((dstExists != false) && (dstErr == noErr)) { + FSMakeFSSpecCompat(dstFileSpec.vRefNum, dstFileSpec.parID, + tmpName, &tmpFileSpec); + if (err == noErr) { + /* + * Delete backup file. + */ + + FSpDeleteCompat(&tmpFileSpec); + } else { + + /* + * Restore backup file. + */ + + FSpDeleteCompat(&dstFileSpec); + FSpRenameCompat(&tmpFileSpec, dstFileSpec.name); + if (dstLocked) { + FSpSetFLockCompat(&dstFileSpec); + } + } + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpObjDeleteFile, TclpDeleteFile -- + * + * Removes a single file (not a directory). + * + * Results: + * If the file was successfully deleted, returns TCL_OK. Otherwise + * the return value is TCL_ERROR and errno is set to indicate the + * error. Some possible values for errno are: + * + * EACCES: a parent directory can't be read and/or written. + * EISDIR: path is a directory. + * ENOENT: path doesn't exist or is "". + * + * Side effects: + * The file is deleted, even if it is read-only. + * + *--------------------------------------------------------------------------- + */ + +int +TclpObjDeleteFile(pathPtr) + Tcl_Obj *pathPtr; +{ + return TclpDeleteFile(Tcl_FSGetNativePath(pathPtr)); +} + +int +TclpDeleteFile( + CONST char *path) /* Pathname of file to be removed (native). */ +{ + OSErr err; + FSSpec fileSpec; + Boolean isDirectory; + long dirID; + + err = FSpLLocationFromPath(strlen(path), path, &fileSpec); + if (err == noErr) { + /* + * Since FSpDeleteCompat will delete an empty directory, make sure + * that this isn't a directory first. + */ + + FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); + if (isDirectory == true) { + errno = EISDIR; + return TCL_ERROR; + } + } + err = FSpDeleteCompat(&fileSpec); + if (err == fLckdErr) { + FSpRstFLockCompat(&fileSpec); + err = FSpDeleteCompat(&fileSpec); + if (err != noErr) { + FSpSetFLockCompat(&fileSpec); + } + } + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpObjCreateDirectory, DoCreateDirectory -- + * + * Creates the specified directory. All parent directories of the + * specified directory must already exist. The directory is + * automatically created with permissions so that user can access + * the new directory and create new files or subdirectories in it. + * + * Results: + * If the directory was successfully created, returns TCL_OK. + * Otherwise the return value is TCL_ERROR and errno is set to + * indicate the error. Some possible values for errno are: + * + * EACCES: a parent directory can't be read and/or written. + * EEXIST: path already exists. + * ENOENT: a parent directory doesn't exist. + * + * Side effects: + * A directory is created with the current umask, except that + * permission for u+rwx will always be added. + * + *--------------------------------------------------------------------------- + */ + +int +TclpObjCreateDirectory(pathPtr) + Tcl_Obj *pathPtr; +{ + return DoCreateDirectory(Tcl_FSGetNativePath(pathPtr)); +} + +static int +DoCreateDirectory( + CONST char *path) /* Pathname of directory to create (native). */ +{ + OSErr err; + FSSpec dirSpec; + long outDirID; + + err = FSpLocationFromPath(strlen(path), path, &dirSpec); + if (err == noErr) { + err = dupFNErr; /* EEXIST. */ + } else if (err == fnfErr) { + err = FSpDirCreateCompat(&dirSpec, smSystemScript, &outDirID); + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpObjCopyDirectory, DoCopyDirectory -- + * + * Recursively copies a directory. The target directory dst must + * not already exist. Note that this function does not merge two + * directory hierarchies, even if the target directory is an an + * empty directory. + * + * Results: + * If the directory was successfully copied, returns TCL_OK. + * Otherwise the return value is TCL_ERROR, errno is set to indicate + * the error, and the pathname of the file that caused the error + * is stored in errorPtr. See TclpCreateDirectory and TclpCopyFile + * for a description of possible values for errno. + * + * Side effects: + * An exact copy of the directory hierarchy src will be created + * with the name dst. If an error occurs, the error will + * be returned immediately, and remaining files will not be + * processed. + * + *--------------------------------------------------------------------------- + */ + +int +TclpObjCopyDirectory(srcPathPtr, destPathPtr, errorPtr) + Tcl_Obj *srcPathPtr; + Tcl_Obj *destPathPtr; + Tcl_Obj **errorPtr; +{ + Tcl_DString ds; + int ret; + ret = DoCopyDirectory(Tcl_FSGetNativePath(srcPathPtr), + Tcl_FSGetNativePath(destPathPtr), &ds); + if (ret != TCL_OK) { + *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); + Tcl_DStringFree(&ds); + Tcl_IncrRefCount(*errorPtr); + } + return ret; +} + +static int +DoCopyDirectory( + CONST char *src, /* Pathname of directory to be copied + * (Native). */ + CONST char *dst, /* Pathname of target directory (Native). */ + Tcl_DString *errorPtr) /* If non-NULL, uninitialized or free + * DString filled with UTF-8 name of file + * causing error. */ +{ + OSErr err, saveErr; + long srcID, tmpDirID; + FSSpec srcFileSpec, dstFileSpec, dstDirSpec, tmpDirSpec, tmpFileSpec; + Boolean srcIsDirectory, srcLocked; + Boolean dstIsDirectory, dstExists; + Str31 tmpName; + + err = FSpLocationFromPath(strlen(src), src, &srcFileSpec); + if (err == noErr) { + err = FSpGetDirectoryID(&srcFileSpec, &srcID, &srcIsDirectory); + } + if (err == noErr) { + if (srcIsDirectory == false) { + err = afpObjectTypeErr; /* ENOTDIR. */ + } + } + if (err == noErr) { + err = GetFileSpecs(dst, &dstFileSpec, &dstDirSpec, &dstExists, + &dstIsDirectory); + } + if (dstExists) { + if (dstIsDirectory == false) { + err = afpObjectTypeErr; /* ENOTDIR. */ + } else { + err = dupFNErr; /* EEXIST. */ + } + } + if (err != noErr) { + goto done; + } + if ((srcFileSpec.vRefNum == dstFileSpec.vRefNum) && + (srcFileSpec.parID == dstFileSpec.parID) && + (Pstrequal(srcFileSpec.name, dstFileSpec.name) != 0)) { + /* + * Copying on top of self. No-op. + */ + + goto done; + } + + /* + * This algorthm will work making a copy of the source directory in + * the current directory with a new name, in a new directory with the + * same name, and in a new directory with a new name: + * + * 1. Make dstDir/tmpDir. + * 2. Copy srcDir/src to dstDir/tmpDir/src + * 3. Rename dstDir/tmpDir/src to dstDir/tmpDir/dst (if necessary). + * 4. CatMove dstDir/tmpDir/dst to dstDir/dst. + * 5. Remove dstDir/tmpDir. + */ + + err = FSpGetFLockCompat(&srcFileSpec, &srcLocked); + if (srcLocked) { + FSpRstFLockCompat(&srcFileSpec); + } + if (err == noErr) { + err = GenerateUniqueName(dstFileSpec.vRefNum, &startSeed, dstFileSpec.parID, + dstFileSpec.parID, tmpName); + } + if (err == noErr) { + FSMakeFSSpecCompat(dstFileSpec.vRefNum, dstFileSpec.parID, + tmpName, &tmpDirSpec); + err = FSpDirCreateCompat(&tmpDirSpec, smSystemScript, &tmpDirID); + } + if (err == noErr) { + err = FSpDirectoryCopy(&srcFileSpec, &tmpDirSpec, NULL, NULL, 0, true, + CopyErrHandler); + } + + /* + * Even if the Copy failed, Rename/Move whatever did get copied to the + * appropriate final destination, if possible. + */ + + saveErr = err; + err = noErr; + if (Pstrequal(srcFileSpec.name, dstFileSpec.name) == 0) { + err = FSMakeFSSpecCompat(tmpDirSpec.vRefNum, tmpDirID, + srcFileSpec.name, &tmpFileSpec); + if (err == noErr) { + err = FSpRenameCompat(&tmpFileSpec, dstFileSpec.name); + } + } + if (err == noErr) { + err = FSMakeFSSpecCompat(tmpDirSpec.vRefNum, tmpDirID, + dstFileSpec.name, &tmpFileSpec); + } + if (err == noErr) { + err = FSpCatMoveCompat(&tmpFileSpec, &dstDirSpec); + } + if (err == noErr) { + if (srcLocked) { + FSpSetFLockCompat(&dstFileSpec); + } + } + + FSpDeleteCompat(&tmpDirSpec); + + if (saveErr != noErr) { + err = saveErr; + } + + done: + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + if (errorPtr != NULL) { + Tcl_ExternalToUtfDString(NULL, dst, -1, errorPtr); + } + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * CopyErrHandler -- + * + * This procedure is called from the MoreFiles procedure + * FSpDirectoryCopy whenever an error occurs. + * + * Results: + * False if the condition should not be considered an error, true + * otherwise. + * + * Side effects: + * Since FSpDirectoryCopy() is called only after removing any + * existing target directories, there shouldn't be any errors. + * + *---------------------------------------------------------------------- + */ + +static pascal Boolean +CopyErrHandler( + OSErr error, /* Error that occured */ + short failedOperation, /* operation that caused the error */ + short srcVRefNum, /* volume ref number of source */ + long srcDirID, /* directory id of source */ + ConstStr255Param srcName, /* name of source */ + short dstVRefNum, /* volume ref number of dst */ + long dstDirID, /* directory id of dst */ + ConstStr255Param dstName) /* name of dst directory */ +{ + return true; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpObjRemoveDirectory, DoRemoveDirectory -- + * + * Removes directory (and its contents, if the recursive flag is set). + * + * Results: + * If the directory was successfully removed, returns TCL_OK. + * Otherwise the return value is TCL_ERROR, errno is set to indicate + * the error, and the pathname of the file that caused the error + * is stored in errorPtr. Some possible values for errno are: + * + * EACCES: path directory can't be read and/or written. + * EEXIST: path is a non-empty directory. + * EINVAL: path is a root directory. + * ENOENT: path doesn't exist or is "". + * ENOTDIR: path is not a directory. + * + * Side effects: + * Directory removed. If an error occurs, the error will be returned + * immediately, and remaining files will not be deleted. + * + *--------------------------------------------------------------------------- + */ + +int +TclpObjRemoveDirectory(pathPtr, recursive, errorPtr) + Tcl_Obj *pathPtr; + int recursive; + Tcl_Obj **errorPtr; +{ + Tcl_DString ds; + int ret; + ret = DoRemoveDirectory(Tcl_FSGetNativePath(pathPtr),recursive, &ds); + if (ret != TCL_OK) { + *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); + Tcl_DStringFree(&ds); + Tcl_IncrRefCount(*errorPtr); + } + return ret; +} + +static int +DoRemoveDirectory( + CONST char *path, /* Pathname of directory to be removed + * (native). */ + int recursive, /* If non-zero, removes directories that + * are nonempty. Otherwise, will only remove + * empty directories. */ + Tcl_DString *errorPtr) /* If non-NULL, uninitialized or free + * DString filled with UTF-8 name of file + * causing error. */ +{ + OSErr err; + FSSpec fileSpec; + long dirID; + int locked; + Boolean isDirectory; + CInfoPBRec pb; + Str255 fileName; + + + locked = 0; + err = FSpLocationFromPath(strlen(path), path, &fileSpec); + if (err != noErr) { + goto done; + } + + /* + * Since FSpDeleteCompat will delete a file, make sure this isn't + * a file first. + */ + + isDirectory = 1; + FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); + if (isDirectory == 0) { + errno = ENOTDIR; + return TCL_ERROR; + } + + err = FSpDeleteCompat(&fileSpec); + if (err == fLckdErr) { + locked = 1; + FSpRstFLockCompat(&fileSpec); + err = FSpDeleteCompat(&fileSpec); + } + if (err == noErr) { + return TCL_OK; + } + if (err != fBsyErr) { + goto done; + } + + if (recursive == 0) { + /* + * fBsyErr means one of three things: file busy, directory not empty, + * or working directory control block open. Determine if directory + * is empty. If directory is not empty, return EEXIST. + */ + + pb.hFileInfo.ioVRefNum = fileSpec.vRefNum; + pb.hFileInfo.ioDirID = dirID; + pb.hFileInfo.ioNamePtr = (StringPtr) fileName; + pb.hFileInfo.ioFDirIndex = 1; + if (PBGetCatInfoSync(&pb) == noErr) { + err = dupFNErr; /* EEXIST */ + goto done; + } + } + + /* + * DeleteDirectory removes a directory and all its contents, including + * any locked files. There is no interface to get the name of the + * file that caused the error, if an error occurs deleting this tree, + * unless we rewrite DeleteDirectory ourselves. + */ + + err = DeleteDirectory(fileSpec.vRefNum, dirID, NULL); + + done: + if (err != noErr) { + if (errorPtr != NULL) { + Tcl_UtfToExternalDString(NULL, path, -1, errorPtr); + } + if (locked) { + FSpSetFLockCompat(&fileSpec); + } + errno = TclMacOSErrorToPosixError(err); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * GetFileSpecs -- + * + * Gets FSSpecs for the specified path and its parent directory. + * + * Results: + * The return value is noErr if there was no error getting FSSpecs, + * otherwise it is an error describing the problem. Fills buffers + * with information, as above. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static OSErr +GetFileSpecs( + CONST char *path, /* The path to query. */ + FSSpec *pathSpecPtr, /* Filled with information about path. */ + FSSpec *dirSpecPtr, /* Filled with information about path's + * parent directory. */ + Boolean *pathExistsPtr, /* Set to true if path actually exists, + * false if it doesn't or there was an + * error reading the specified path. */ + Boolean *pathIsDirectoryPtr)/* Set to true if path is itself a directory, + * otherwise false. */ +{ + CONST char *dirName; + OSErr err; + int argc; + CONST char **argv; + long d; + Tcl_DString buffer; + + *pathExistsPtr = false; + *pathIsDirectoryPtr = false; + + Tcl_DStringInit(&buffer); + Tcl_SplitPath(path, &argc, &argv); + if (argc == 1) { + dirName = ":"; + } else { + dirName = Tcl_JoinPath(argc - 1, argv, &buffer); + } + err = FSpLocationFromPath(strlen(dirName), dirName, dirSpecPtr); + Tcl_DStringFree(&buffer); + ckfree((char *) argv); + + if (err == noErr) { + err = FSpLocationFromPath(strlen(path), path, pathSpecPtr); + if (err == noErr) { + *pathExistsPtr = true; + err = FSpGetDirectoryID(pathSpecPtr, &d, pathIsDirectoryPtr); + } else if (err == fnfErr) { + err = noErr; + } + } + return err; +} + +/* + *------------------------------------------------------------------------- + * + * FSpGetFLockCompat -- + * + * Determines if there exists a software lock on the specified + * file. The software lock could prevent the file from being + * renamed or moved. + * + * Results: + * Standard macintosh error code. + * + * Side effects: + * None. + * + * + *------------------------------------------------------------------------- + */ + +OSErr +FSpGetFLockCompat( + const FSSpec *specPtr, /* File to query. */ + Boolean *lockedPtr) /* Set to true if file is locked, false + * if it isn't or there was an error reading + * specified file. */ +{ + CInfoPBRec pb; + OSErr err; + + pb.hFileInfo.ioVRefNum = specPtr->vRefNum; + pb.hFileInfo.ioDirID = specPtr->parID; + pb.hFileInfo.ioNamePtr = (StringPtr) specPtr->name; + pb.hFileInfo.ioFDirIndex = 0; + + err = PBGetCatInfoSync(&pb); + if ((err == noErr) && (pb.hFileInfo.ioFlAttrib & 0x01)) { + *lockedPtr = true; + } else { + *lockedPtr = false; + } + return err; +} + +/* + *---------------------------------------------------------------------- + * + * Pstrequal -- + * + * Pascal string compare. + * + * Results: + * Returns 1 if strings equal, 0 otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +Pstrequal ( + ConstStr255Param stringA, /* Pascal string A */ + ConstStr255Param stringB) /* Pascal string B */ +{ + int i, len; + + len = *stringA; + for (i = 0; i <= len; i++) { + if (*stringA++ != *stringB++) { + return 0; + } + } + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * GetFileFinderAttributes -- + * + * Returns a Tcl_Obj containing the value of a file attribute + * which is part of the FInfo record. Which attribute is controlled + * by objIndex. + * + * Results: + * Returns a standard TCL error. If the return value is TCL_OK, + * the new creator or file type object is put into attributePtrPtr. + * The object will have ref count 0. If there is an error, + * attributePtrPtr is not touched. + * + * Side effects: + * A new object is allocated if the file is valid. + * + *---------------------------------------------------------------------- + */ + +static int +GetFileFinderAttributes( + Tcl_Interp *interp, /* The interp to report errors with. */ + int objIndex, /* The index of the attribute option. */ + Tcl_Obj *fileName, /* The name of the file (UTF-8). */ + Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */ +{ + OSErr err; + FSSpec fileSpec; + FInfo finfo; + CInfoPBRec pb; + CONST char *native; + + native=Tcl_FSGetNativePath(fileName); + err = FSpLLocationFromPath(strlen(native), + native, &fileSpec); + + if (err == noErr) { + if (objIndex != MAC_RSRCLENGTH_ATTRIBUTE) { + err = FSpGetFInfo(&fileSpec, &finfo); + } else { + pb.hFileInfo.ioCompletion = NULL; + pb.hFileInfo.ioNamePtr = fileSpec.name; + pb.hFileInfo.ioVRefNum = fileSpec.vRefNum; + pb.hFileInfo.ioFDirIndex = 0; + pb.hFileInfo.ioDirID = fileSpec.parID; + err = PBGetCatInfo(&pb, 0); + } + } + + if (err == noErr) { + switch (objIndex) { + case MAC_CREATOR_ATTRIBUTE: + *attributePtrPtr = Tcl_NewOSTypeObj(finfo.fdCreator); + break; + case MAC_HIDDEN_ATTRIBUTE: + *attributePtrPtr = Tcl_NewBooleanObj(finfo.fdFlags + & kIsInvisible); + break; + case MAC_TYPE_ATTRIBUTE: + *attributePtrPtr = Tcl_NewOSTypeObj(finfo.fdType); + break; + case MAC_RSRCLENGTH_ATTRIBUTE: + *attributePtrPtr = Tcl_NewLongObj(pb.hFileInfo.ioFlRLgLen); + break; + } + } else if (err == fnfErr) { + long dirID; + Boolean isDirectory = 0; + + err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); + if ((err == noErr) && isDirectory) { + if (objIndex == MAC_HIDDEN_ATTRIBUTE) { + *attributePtrPtr = Tcl_NewBooleanObj(0); + } else { + /* Directories only support attribute "-hidden" */ + errno = EISDIR; + Tcl_AppendResult(interp, "invalid attribute: ", + Tcl_PosixError(interp), (char *) NULL); + return TCL_ERROR; + } + } + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "could not read \"", Tcl_GetString(fileName), "\": ", + Tcl_PosixError(interp), (char *) NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * GetFileReadOnly -- + * + * Returns a Tcl_Obj containing a Boolean value indicating whether + * or not the file is read-only. The object will have ref count 0. + * This procedure just checks the Finder attributes; it does not + * check AppleShare sharing attributes. + * + * Results: + * Returns a standard TCL error. If the return value is TCL_OK, + * the new creator type object is put into readOnlyPtrPtr. + * If there is an error, readOnlyPtrPtr is not touched. + * + * Side effects: + * A new object is allocated if the file is valid. + * + *---------------------------------------------------------------------- + */ + +static int +GetFileReadOnly( + Tcl_Interp *interp, /* The interp to report errors with. */ + int objIndex, /* The index of the attribute. */ + Tcl_Obj *fileName, /* The name of the file (UTF-8). */ + Tcl_Obj **readOnlyPtrPtr) /* A pointer to return the object with. */ +{ + OSErr err; + FSSpec fileSpec; + CInfoPBRec paramBlock; + CONST char *native; + + native=Tcl_FSGetNativePath(fileName); + err = FSpLLocationFromPath(strlen(native), + native, &fileSpec); + + if (err == noErr) { + if (err == noErr) { + paramBlock.hFileInfo.ioCompletion = NULL; + paramBlock.hFileInfo.ioNamePtr = fileSpec.name; + paramBlock.hFileInfo.ioVRefNum = fileSpec.vRefNum; + paramBlock.hFileInfo.ioFDirIndex = 0; + paramBlock.hFileInfo.ioDirID = fileSpec.parID; + err = PBGetCatInfo(¶mBlock, 0); + if (err == noErr) { + + /* + * For some unknown reason, the Mac does not give + * symbols for the bits in the ioFlAttrib field. + * 1 -> locked. + */ + + *readOnlyPtrPtr = Tcl_NewBooleanObj( + paramBlock.hFileInfo.ioFlAttrib & 1); + } + } + } + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "could not read \"", Tcl_GetString(fileName), "\": ", + Tcl_PosixError(interp), (char *) NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * SetFileFinderAttributes -- + * + * Sets the file to the creator or file type given by attributePtr. + * objIndex determines whether the creator or file type is set. + * + * Results: + * Returns a standard TCL error. + * + * Side effects: + * The file's attribute is set. + * + *---------------------------------------------------------------------- + */ + +static int +SetFileFinderAttributes( + Tcl_Interp *interp, /* The interp to report errors with. */ + int objIndex, /* The index of the attribute. */ + Tcl_Obj *fileName, /* The name of the file (UTF-8). */ + Tcl_Obj *attributePtr) /* The command line object. */ +{ + OSErr err; + FSSpec fileSpec; + FInfo finfo; + CInfoPBRec pb; + CONST char *native; + + native=Tcl_FSGetNativePath(fileName); + err = FSpLLocationFromPath(strlen(native), + native, &fileSpec); + + if (err == noErr) { + if (objIndex != MAC_RSRCLENGTH_ATTRIBUTE) { + err = FSpGetFInfo(&fileSpec, &finfo); + } else { + pb.hFileInfo.ioCompletion = NULL; + pb.hFileInfo.ioNamePtr = fileSpec.name; + pb.hFileInfo.ioVRefNum = fileSpec.vRefNum; + pb.hFileInfo.ioFDirIndex = 0; + pb.hFileInfo.ioDirID = fileSpec.parID; + err = PBGetCatInfo(&pb, 0); + } + } + + if (err == noErr) { + switch (objIndex) { + case MAC_CREATOR_ATTRIBUTE: + if (Tcl_GetOSTypeFromObj(interp, attributePtr, + &finfo.fdCreator) != TCL_OK) { + return TCL_ERROR; + } + break; + case MAC_HIDDEN_ATTRIBUTE: { + int hidden; + + if (Tcl_GetBooleanFromObj(interp, attributePtr, &hidden) + != TCL_OK) { + return TCL_ERROR; + } + if (hidden) { + finfo.fdFlags |= kIsInvisible; + } else { + finfo.fdFlags &= ~kIsInvisible; + } + break; + } + case MAC_TYPE_ATTRIBUTE: + if (Tcl_GetOSTypeFromObj(interp, attributePtr, + &finfo.fdType) != TCL_OK) { + return TCL_ERROR; + } + break; + case MAC_RSRCLENGTH_ATTRIBUTE: + { + long newRsrcForkSize; + + if (Tcl_GetLongFromObj(interp, attributePtr, + &newRsrcForkSize) != TCL_OK) { + return TCL_ERROR; + } + if(newRsrcForkSize != pb.hFileInfo.ioFlRLgLen) { + short rf; + /* + * Only setting rsrclength to 0 to strip + * a file's resource fork is supported. + */ + if(newRsrcForkSize != 0) { + Tcl_AppendResult(interp, + "setting nonzero rsrclength not supported", + (char *) NULL); + return TCL_ERROR; + } + + if ((rf = FSpOpenResFile(&fileSpec, fsWrPerm)) >= 0) { + err = SetEOF(rf, 0); + CloseResFile(rf); + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + Tcl_AppendResult(interp, + "could not truncate resource fork of \"", + Tcl_GetString(fileName), "\": ", + Tcl_PosixError(interp), (char *) NULL); + return TCL_ERROR; + } + } + break; + } + } + if (objIndex != MAC_RSRCLENGTH_ATTRIBUTE) { + err = FSpSetFInfo(&fileSpec, &finfo); + } + } else if (err == fnfErr) { + long dirID; + Boolean isDirectory = 0; + + err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); + if ((err == noErr) && isDirectory) { + Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); + Tcl_AppendStringsToObj(resultPtr, "cannot set ", + tclpFileAttrStrings[objIndex], ": \"", + Tcl_GetString(fileName), "\" is a directory", (char *) NULL); + return TCL_ERROR; + } + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "could not read \"", Tcl_GetString(fileName), "\": ", + Tcl_PosixError(interp), (char *) NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * SetFileReadOnly -- + * + * Sets the file to be read-only according to the Boolean value + * given by hiddenPtr. + * + * Results: + * Returns a standard TCL error. + * + * Side effects: + * The file's attribute is set. + * + *---------------------------------------------------------------------- + */ + +static int +SetFileReadOnly( + Tcl_Interp *interp, /* The interp to report errors with. */ + int objIndex, /* The index of the attribute. */ + Tcl_Obj *fileName, /* The name of the file (UTF-8). */ + Tcl_Obj *readOnlyPtr) /* The command line object. */ +{ + OSErr err; + FSSpec fileSpec; + HParamBlockRec paramBlock; + int hidden; + CONST char *native; + + native=Tcl_FSGetNativePath(fileName); + err = FSpLLocationFromPath(strlen(native), + native, &fileSpec); + + if (err == noErr) { + if (Tcl_GetBooleanFromObj(interp, readOnlyPtr, &hidden) != TCL_OK) { + return TCL_ERROR; + } + + paramBlock.fileParam.ioCompletion = NULL; + paramBlock.fileParam.ioNamePtr = fileSpec.name; + paramBlock.fileParam.ioVRefNum = fileSpec.vRefNum; + paramBlock.fileParam.ioDirID = fileSpec.parID; + if (hidden) { + err = PBHSetFLock(¶mBlock, 0); + } else { + err = PBHRstFLock(¶mBlock, 0); + } + } + + if (err == fnfErr) { + long dirID; + Boolean isDirectory = 0; + err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); + if ((err == noErr) && isDirectory) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "cannot set a directory to read-only when File Sharing is turned off", + (char *) NULL); + return TCL_ERROR; + } else { + err = fnfErr; + } + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "could not read \"", Tcl_GetString(fileName), "\": ", + Tcl_PosixError(interp), (char *) NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpObjListVolumes -- + * + * Lists the currently mounted volumes + * + * Results: + * The list of volumes. + * + * Side effects: + * None + * + *--------------------------------------------------------------------------- + */ +Tcl_Obj* +TclpObjListVolumes(void) +{ + HParamBlockRec pb; + Str255 name; + OSErr theError = noErr; + Tcl_Obj *resultPtr, *elemPtr; + short volIndex = 1; + Tcl_DString dstr; + + resultPtr = Tcl_NewObj(); + + /* + * We use two facts: + * 1) The Mac volumes are enumerated by the ioVolIndex parameter of + * the HParamBlockRec. They run through the integers contiguously, + * starting at 1. + * 2) PBHGetVInfoSync returns an error when you ask for a volume index + * that does not exist. + * + */ + + while ( 1 ) { + pb.volumeParam.ioNamePtr = (StringPtr) &name; + pb.volumeParam.ioVolIndex = volIndex; + + theError = PBHGetVInfoSync(&pb); + + if ( theError != noErr ) { + break; + } + + Tcl_ExternalToUtfDString(NULL, (CONST char *)&name[1], name[0], &dstr); + elemPtr = Tcl_NewStringObj(Tcl_DStringValue(&dstr), + Tcl_DStringLength(&dstr)); + Tcl_AppendToObj(elemPtr, ":", 1); + Tcl_ListObjAppendElement(NULL, resultPtr, elemPtr); + + Tcl_DStringFree(&dstr); + + volIndex++; + } + + Tcl_IncrRefCount(resultPtr); + return resultPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpObjNormalizePath -- + * + * This function scans through a path specification and replaces + * it, in place, with a normalized version. On MacOS, this means + * resolving all aliases present in the path and replacing the head of + * pathPtr with the absolute case-sensitive path to the last file or + * directory that could be validated in the path. + * + * Results: + * The new 'nextCheckpoint' value, giving as far as we could + * understand in the path. + * + * Side effects: + * The pathPtr string, which must contain a valid path, is + * possibly modified in place. + * + *--------------------------------------------------------------------------- + */ + +int +TclpObjNormalizePath(interp, pathPtr, nextCheckpoint) + Tcl_Interp *interp; + Tcl_Obj *pathPtr; + int nextCheckpoint; +{ + #define MAXMACFILENAMELEN 31 /* assumed to be < sizeof(StrFileName) */ + + StrFileName fileName; + StringPtr fileNamePtr; + int fileNameLen,newPathLen; + Handle newPathHandle; + OSErr err; + short vRefNum; + long dirID; + Boolean isDirectory; + Boolean wasAlias=FALSE; + FSSpec fileSpec, lastFileSpec; + + Tcl_DString nativeds; + + char cur; + int firstCheckpoint=nextCheckpoint, lastCheckpoint; + int origPathLen; + char *path = Tcl_GetStringFromObj(pathPtr,&origPathLen); + + { + int currDirValid=0; + /* + * check if substring to first ':' after initial + * nextCheckpoint is a valid relative or absolute + * path to a directory, if not we return without + * normalizing anything + */ + + while (1) { + cur = path[nextCheckpoint]; + if (cur == ':' || cur == 0) { + if (cur == ':') { + /* jump over separator */ + nextCheckpoint++; cur = path[nextCheckpoint]; + } + Tcl_UtfToExternalDString(NULL,path,nextCheckpoint,&nativeds); + err = FSpLLocationFromPath(Tcl_DStringLength(&nativeds), + Tcl_DStringValue(&nativeds), + &fileSpec); + Tcl_DStringFree(&nativeds); + if (err == noErr) { + lastFileSpec=fileSpec; + err = ResolveAliasFile(&fileSpec, true, &isDirectory, + &wasAlias); + if (err == noErr) { + err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); + currDirValid = ((err == noErr) && isDirectory); + vRefNum = fileSpec.vRefNum; + } + } + break; + } + nextCheckpoint++; + } + + if(!currDirValid) { + /* can't determine root dir, bail out */ + return firstCheckpoint; + } + } + + /* + * Now vRefNum and dirID point to a valid + * directory, so walk the rest of the path + * ( code adapted from FSpLocationFromPath() ) + */ + + lastCheckpoint=nextCheckpoint; + while (1) { + cur = path[nextCheckpoint]; + if (cur == ':' || cur == 0) { + fileNameLen=nextCheckpoint-lastCheckpoint; + fileNamePtr=fileName; + if(fileNameLen==0) { + if (cur == ':') { + /* + * special case for empty dirname i.e. encountered + * a '::' path component: get parent dir of currDir + */ + fileName[0]=2; + strcpy((char *) fileName + 1, "::"); + lastCheckpoint--; + } else { + /* + * empty filename, i.e. want FSSpec for currDir + */ + fileNamePtr=NULL; + } + } else { + Tcl_UtfToExternalDString(NULL,&path[lastCheckpoint], + fileNameLen,&nativeds); + fileNameLen=Tcl_DStringLength(&nativeds); + if(fileNameLen > MAXMACFILENAMELEN) { + err = bdNamErr; + } else { + fileName[0]=fileNameLen; + strncpy((char *) fileName + 1, Tcl_DStringValue(&nativeds), + fileNameLen); + } + Tcl_DStringFree(&nativeds); + } + if(err == noErr) + err=FSMakeFSSpecCompat(vRefNum, dirID, fileNamePtr, &fileSpec); + if(err != noErr) { + if(err != fnfErr) { + /* + * this can occur if trying to get parent of a root + * volume via '::' or when using an illegal + * filename; revert to last checkpoint and stop + * processing path further + */ + err=FSMakeFSSpecCompat(vRefNum, dirID, NULL, &fileSpec); + if(err != noErr) { + /* should never happen, bail out */ + return firstCheckpoint; + } + nextCheckpoint=lastCheckpoint; + cur = path[lastCheckpoint]; + } + break; /* arrived at nonexistent file or dir */ + } else { + /* fileSpec could point to an alias, resolve it */ + lastFileSpec=fileSpec; + err = ResolveAliasFile(&fileSpec, true, &isDirectory, + &wasAlias); + if (err != noErr || !isDirectory) { + break; /* fileSpec doesn't point to a dir */ + } + } + if (cur == 0) break; /* arrived at end of path */ + + /* fileSpec points to possibly nonexisting subdirectory; validate */ + err = FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); + if (err != noErr || !isDirectory) { + break; /* fileSpec doesn't point to existing dir */ + } + vRefNum = fileSpec.vRefNum; + + /* found a new valid subdir in path, continue processing path */ + lastCheckpoint=nextCheckpoint+1; + } + wasAlias=FALSE; + nextCheckpoint++; + } + + if (wasAlias) + fileSpec=lastFileSpec; + + /* + * fileSpec now points to a possibly nonexisting file or dir + * inside a valid dir; get full path name to it + */ + + err=FSpPathFromLocation(&fileSpec, &newPathLen, &newPathHandle); + if(err != noErr) { + return firstCheckpoint; /* should not see any errors here, bail out */ + } + + HLock(newPathHandle); + Tcl_ExternalToUtfDString(NULL,*newPathHandle,newPathLen,&nativeds); + if (cur != 0) { + /* not at end, append remaining path */ + if ( newPathLen==0 || (*(*newPathHandle+(newPathLen-1))!=':' && path[nextCheckpoint] !=':')) { + Tcl_DStringAppend(&nativeds, ":" , 1); + } + Tcl_DStringAppend(&nativeds, &path[nextCheckpoint], + strlen(&path[nextCheckpoint])); + } + DisposeHandle(newPathHandle); + + fileNameLen=Tcl_DStringLength(&nativeds); + Tcl_SetStringObj(pathPtr,Tcl_DStringValue(&nativeds),fileNameLen); + Tcl_DStringFree(&nativeds); + + return nextCheckpoint+(fileNameLen-origPathLen); +} + diff --git a/mac/tclMacFile.c b/mac/tclMacFile.c new file mode 100644 index 0000000..de5f422 --- /dev/null +++ b/mac/tclMacFile.c @@ -0,0 +1,1402 @@ +/* + * tclMacFile.c -- + * + * This file implements the channel drivers for Macintosh + * files. It also comtains Macintosh version of other Tcl + * functions that deal with the file system. + * + * Copyright (c) 1995-1998 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacFile.c,v 1.30 2004/01/29 10:28:22 vincentdarley Exp $ + */ + +/* + * Note: This code eventually needs to support async I/O. In doing this + * we will need to keep track of all current async I/O. If exit to shell + * is called - we shouldn't exit until all asyc I/O completes. + */ + +#include "tclInt.h" +#include "tclPort.h" +#include "tclMacInt.h" +#include <Aliases.h> +#include <Resources.h> +#include <Files.h> +#include <Errors.h> +#include <Processes.h> +#include <Strings.h> +#include <Types.h> +#include <MoreFiles.h> +#include <MoreFilesExtras.h> +#include <FSpCompat.h> + +static int NativeMatchType(Tcl_Obj *tempName, Tcl_GlobTypeData *types, + HFileInfo fileInfo, OSType okType, OSType okCreator); +static OSErr FspLocationFromFsPath _ANSI_ARGS_((Tcl_Obj *pathPtr, + FSSpec* specPtr)); +static OSErr FspLLocationFromFsPath _ANSI_ARGS_((Tcl_Obj *pathPtr, + FSSpec* specPtr)); + +static OSErr CreateAliasFile _ANSI_ARGS_((FSSpec *theAliasFile, FSSpec *targetFile)); + +static OSErr +FspLocationFromFsPath(pathPtr, specPtr) + Tcl_Obj *pathPtr; + FSSpec* specPtr; +{ + CONST char *native = Tcl_FSGetNativePath(pathPtr); + return FSpLocationFromPath(strlen(native), native, specPtr); +} + +static OSErr +FspLLocationFromFsPath(pathPtr, specPtr) + Tcl_Obj *pathPtr; + FSSpec* specPtr; +{ + CONST char *native = Tcl_FSGetNativePath(pathPtr); + return FSpLLocationFromPath(strlen(native), native, specPtr); +} + + +/* + *---------------------------------------------------------------------- + * + * TclpFindExecutable -- + * + * This procedure computes the absolute path name of the current + * application, given its argv[0] value. However, this + * implementation doesn't need the argv[0] value. NULL + * may be passed in its place. + * + * Results: + * None. + * + * Side effects: + * The variable tclExecutableName gets filled in with the file + * name for the application, if we figured it out. If we couldn't + * figure it out, Tcl_FindExecutable is set to NULL. + * + *---------------------------------------------------------------------- + */ + +char * +TclpFindExecutable( + CONST char *argv0) /* The value of the application's argv[0]. */ +{ + ProcessSerialNumber psn; + ProcessInfoRec info; + Str63 appName; + FSSpec fileSpec; + int pathLength; + Handle pathName = NULL; + OSErr err; + Tcl_DString ds; + + TclInitSubsystems(argv0); + + GetCurrentProcess(&psn); + info.processInfoLength = sizeof(ProcessInfoRec); + info.processName = appName; + info.processAppSpec = &fileSpec; + GetProcessInformation(&psn, &info); + + if (tclExecutableName != NULL) { + ckfree(tclExecutableName); + tclExecutableName = NULL; + } + + err = FSpPathFromLocation(&fileSpec, &pathLength, &pathName); + HLock(pathName); + Tcl_ExternalToUtfDString(NULL, *pathName, pathLength, &ds); + HUnlock(pathName); + DisposeHandle(pathName); + + tclExecutableName = (char *) ckalloc((unsigned) + (Tcl_DStringLength(&ds) + 1)); + strcpy(tclExecutableName, Tcl_DStringValue(&ds)); + Tcl_DStringFree(&ds); + return tclExecutableName; +} + +/* + *---------------------------------------------------------------------- + * + * TclpMatchInDirectory -- + * + * This routine is used by the globbing code to search a + * directory for all files which match a given pattern. + * + * Results: + * + * The return value is a standard Tcl result indicating whether an + * error occurred in globbing. Errors are left in interp, good + * results are lappended to resultPtr (which must be a valid object) + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- */ + +int +TclpMatchInDirectory(interp, resultPtr, pathPtr, pattern, types) + Tcl_Interp *interp; /* Interpreter to receive errors. */ + Tcl_Obj *resultPtr; /* List object to lappend results. */ + Tcl_Obj *pathPtr; /* Contains path to directory to search. */ + CONST char *pattern; /* Pattern to match against. NULL or empty + * means pathPtr is actually a single file + * to check. */ + Tcl_GlobTypeData *types; /* Object containing list of acceptable types. + * May be NULL. In particular the directory + * flag is very important. */ +{ + OSType okType = 0; + OSType okCreator = 0; + Tcl_Obj *fileNamePtr; + + if (types != NULL && types->type == TCL_GLOB_TYPE_MOUNT) { + /* The native filesystem never adds mounts */ + return TCL_OK; + } + + fileNamePtr = Tcl_FSGetTranslatedPath(interp, pathPtr); + if (fileNamePtr == NULL) { + return TCL_ERROR; + } + + if (types != NULL) { + if (types->macType != NULL) { + Tcl_GetOSTypeFromObj(NULL, types->macType, &okType); + } + if (types->macCreator != NULL) { + Tcl_GetOSTypeFromObj(NULL, types->macCreator, &okCreator); + } + } + + if (pattern == NULL || (*pattern == '\0')) { + /* Match a single file directly */ + Tcl_StatBuf buf; + CInfoPBRec paramBlock; + FSSpec fileSpec; + + if (TclpObjLstat(fileNamePtr, &buf) != 0) { + /* File doesn't exist */ + Tcl_DecrRefCount(fileNamePtr); + return TCL_OK; + } + + if (FspLLocationFromFsPath(fileNamePtr, &fileSpec) == noErr) { + paramBlock.hFileInfo.ioCompletion = NULL; + paramBlock.hFileInfo.ioNamePtr = fileSpec.name; + paramBlock.hFileInfo.ioVRefNum = fileSpec.vRefNum; + paramBlock.hFileInfo.ioFDirIndex = 0; + paramBlock.hFileInfo.ioDirID = fileSpec.parID; + + PBGetCatInfo(¶mBlock, 0); + } + + if (NativeMatchType(fileNamePtr, types, paramBlock.hFileInfo, + okType, okCreator)) { + int fnameLen; + char *fname = Tcl_GetStringFromObj(pathPtr,&fnameLen); + if ((fnameLen > 1) && (strchr(fname+1, ':') == NULL)) { + Tcl_ListObjAppendElement(interp, resultPtr, + Tcl_NewStringObj(fname+1, fnameLen-1)); + } else { + Tcl_ListObjAppendElement(interp, resultPtr, pathPtr); + } + } + Tcl_DecrRefCount(fileNamePtr); + return TCL_OK; + } else { + char *fname; + int fnameLen, result = TCL_OK; + int baseLength; + CInfoPBRec pb; + OSErr err; + FSSpec dirSpec; + Boolean isDirectory; + long dirID; + short itemIndex; + Str255 fileName; + Tcl_DString fileString; + Tcl_DString dsOrig; + + Tcl_DStringInit(&dsOrig); + Tcl_DStringAppend(&dsOrig, Tcl_GetString(fileNamePtr), -1); + baseLength = Tcl_DStringLength(&dsOrig); + + /* + * Make sure that the directory part of the name really is a + * directory. + */ + + Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&dsOrig), + Tcl_DStringLength(&dsOrig), &fileString); + + err = FSpLocationFromPath(Tcl_DStringLength(&fileString), + Tcl_DStringValue(&fileString), &dirSpec); + Tcl_DStringFree(&fileString); + if (err == noErr) { + err = FSpGetDirectoryID(&dirSpec, &dirID, &isDirectory); + } + + if ((err != noErr) || !isDirectory) { + /* + * Check if we had a relative path (unix style relative path + * compatibility for glob) + */ + Tcl_DStringFree(&dsOrig); + Tcl_DStringAppend(&dsOrig, ":", 1); + Tcl_DStringAppend(&dsOrig, Tcl_GetString(fileNamePtr), -1); + baseLength = Tcl_DStringLength(&dsOrig); + + Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&dsOrig), + Tcl_DStringLength(&dsOrig), &fileString); + + err = FSpLocationFromPath(Tcl_DStringLength(&fileString), + Tcl_DStringValue(&fileString), &dirSpec); + Tcl_DStringFree(&fileString); + if (err == noErr) { + err = FSpGetDirectoryID(&dirSpec, &dirID, &isDirectory); + } + + if ((err != noErr) || !isDirectory) { + Tcl_DStringFree(&dsOrig); + Tcl_DecrRefCount(fileNamePtr); + return TCL_OK; + } + } + + /* Make sure we have a trailing directory delimiter */ + if (Tcl_DStringValue(&dsOrig)[baseLength-1] != ':') { + Tcl_DStringAppend(&dsOrig, ":", 1); + baseLength++; + } + + /* + * Now open the directory for reading and iterate over the contents. + */ + + pb.hFileInfo.ioVRefNum = dirSpec.vRefNum; + pb.hFileInfo.ioDirID = dirID; + pb.hFileInfo.ioNamePtr = (StringPtr) fileName; + pb.hFileInfo.ioFDirIndex = itemIndex = 1; + + while (1) { + pb.hFileInfo.ioFDirIndex = itemIndex; + pb.hFileInfo.ioDirID = dirID; + err = PBGetCatInfoSync(&pb); + if (err != noErr) { + break; + } + + /* + * Now check to see if the file matches. + */ + + Tcl_ExternalToUtfDString(NULL, (char *) fileName + 1, fileName[0], + &fileString); + if (Tcl_StringMatch(Tcl_DStringValue(&fileString), pattern)) { + Tcl_Obj *tempName; + Tcl_DStringSetLength(&dsOrig, baseLength); + Tcl_DStringAppend(&dsOrig, Tcl_DStringValue(&fileString), -1); + fname = Tcl_DStringValue(&dsOrig); + fnameLen = Tcl_DStringLength(&dsOrig); + + /* + * We use this tempName in calls to check the file's + * type below. We may also use it for the result. + */ + tempName = Tcl_NewStringObj(fname, fnameLen); + Tcl_IncrRefCount(tempName); + + /* Is the type acceptable? */ + if (NativeMatchType(tempName, types, pb.hFileInfo, + okType, okCreator)) { + if ((fnameLen > 1) && (strchr(fname+1, ':') == NULL)) { + Tcl_ListObjAppendElement(interp, resultPtr, + Tcl_NewStringObj(fname+1, fnameLen-1)); + } else { + Tcl_ListObjAppendElement(interp, resultPtr, tempName); + } + } + /* + * This will free the object, unless it was inserted in + * the result list above. + */ + Tcl_DecrRefCount(tempName); + } + Tcl_DStringFree(&fileString); + itemIndex++; + } + + Tcl_DStringFree(&dsOrig); + Tcl_DecrRefCount(fileNamePtr); + return result; + } +} + +static int +NativeMatchType( + Tcl_Obj *tempName, /* Path to check */ + Tcl_GlobTypeData *types, /* Type description to match against */ + HFileInfo fileInfo, /* MacOS file info */ + OSType okType, /* Acceptable MacOS type, or zero */ + OSType okCreator) /* Acceptable MacOS creator, or zero */ +{ + if (types == NULL) { + /* If invisible, don't return the file */ + if (fileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) { + return 0; + } + } else { + Tcl_StatBuf buf; + + if (fileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) { + /* If invisible */ + if ((types->perm == 0) || + !(types->perm & TCL_GLOB_PERM_HIDDEN)) { + return 0; + } + } else { + /* Visible */ + if (types->perm & TCL_GLOB_PERM_HIDDEN) { + return 0; + } + } + if (types->perm != 0) { + if ( + ((types->perm & TCL_GLOB_PERM_RONLY) && + !(fileInfo.ioFlAttrib & 1)) || + ((types->perm & TCL_GLOB_PERM_R) && + (TclpObjAccess(tempName, R_OK) != 0)) || + ((types->perm & TCL_GLOB_PERM_W) && + (TclpObjAccess(tempName, W_OK) != 0)) || + ((types->perm & TCL_GLOB_PERM_X) && + (TclpObjAccess(tempName, X_OK) != 0)) + ) { + return 0; + } + } + if (types->type != 0) { + if (TclpObjStat(tempName, &buf) != 0) { + /* Posix error occurred */ + return 0; + } + /* + * In order bcdpfls as in 'find -t' + */ + if ( + ((types->type & TCL_GLOB_TYPE_BLOCK) && + S_ISBLK(buf.st_mode)) || + ((types->type & TCL_GLOB_TYPE_CHAR) && + S_ISCHR(buf.st_mode)) || + ((types->type & TCL_GLOB_TYPE_DIR) && + S_ISDIR(buf.st_mode)) || + ((types->type & TCL_GLOB_TYPE_PIPE) && + S_ISFIFO(buf.st_mode)) || + ((types->type & TCL_GLOB_TYPE_FILE) && + S_ISREG(buf.st_mode)) +#ifdef S_ISSOCK + || ((types->type & TCL_GLOB_TYPE_SOCK) && + S_ISSOCK(buf.st_mode)) +#endif + ) { + /* Do nothing -- this file is ok */ + } else { + int typeOk = 0; +#ifdef S_ISLNK + if (types->type & TCL_GLOB_TYPE_LINK) { + if (TclpObjLstat(tempName, &buf) == 0) { + if (S_ISLNK(buf.st_mode)) { + typeOk = 1; + } + } + } +#endif + if (typeOk == 0) { + return 0; + } + } + } + if (((okType != 0) && (okType != + fileInfo.ioFlFndrInfo.fdType)) || + ((okCreator != 0) && (okCreator != + fileInfo.ioFlFndrInfo.fdCreator))) { + return 0; + } + } + return 1; +} + + +/* + *---------------------------------------------------------------------- + * + * TclpObjAccess -- + * + * This function replaces the library version of access(). + * + * Results: + * See access documentation. + * + * Side effects: + * See access documentation. + * + *---------------------------------------------------------------------- + */ + +int +TclpObjAccess(pathPtr, mode) + Tcl_Obj *pathPtr; + int mode; +{ + HFileInfo fpb; + HVolumeParam vpb; + OSErr err; + FSSpec fileSpec; + Boolean isDirectory; + long dirID; + int full_mode = 0; + + err = FspLLocationFromFsPath(pathPtr, &fileSpec); + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return -1; + } + + /* + * Fill the fpb & vpb struct up with info about file or directory. + */ + FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); + vpb.ioVRefNum = fpb.ioVRefNum = fileSpec.vRefNum; + vpb.ioNamePtr = fpb.ioNamePtr = fileSpec.name; + if (isDirectory) { + fpb.ioDirID = fileSpec.parID; + } else { + fpb.ioDirID = dirID; + } + + fpb.ioFDirIndex = 0; + err = PBGetCatInfoSync((CInfoPBPtr)&fpb); + if (err == noErr) { + vpb.ioVolIndex = 0; + err = PBHGetVInfoSync((HParmBlkPtr)&vpb); + if (err == noErr) { + /* + * Use the Volume Info & File Info to determine + * access information. If we have got this far + * we know the directory is searchable or the file + * exists. (We have F_OK) + */ + + /* + * Check to see if the volume is hardware or + * software locked. If so we arn't W_OK. + */ + if (mode & W_OK) { + if ((vpb.ioVAtrb & 0x0080) || (vpb.ioVAtrb & 0x8000)) { + errno = EROFS; + return -1; + } + if (fpb.ioFlAttrib & 0x01) { + errno = EACCES; + return -1; + } + } + + /* + * Directories are always searchable and executable. But only + * files of type 'APPL' are executable. + */ + if (!(fpb.ioFlAttrib & 0x10) && (mode & X_OK) + && (fpb.ioFlFndrInfo.fdType != 'APPL')) { + return -1; + } + } + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return -1; + } + + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * TclpObjChdir -- + * + * This function replaces the library version of chdir(). + * + * Results: + * See chdir() documentation. + * + * Side effects: + * See chdir() documentation. Also the cache maintained used by + * Tcl_FSGetCwd() is deallocated and set to NULL. + * + *---------------------------------------------------------------------- + */ + +int +TclpObjChdir(pathPtr) + Tcl_Obj *pathPtr; +{ + FSSpec spec; + OSErr err; + Boolean isFolder; + long dirID; + + err = FspLocationFromFsPath(pathPtr, &spec); + + if (err != noErr) { + errno = ENOENT; + return -1; + } + + err = FSpGetDirectoryID(&spec, &dirID, &isFolder); + if (err != noErr) { + errno = ENOENT; + return -1; + } + + if (isFolder != true) { + errno = ENOTDIR; + return -1; + } + + err = FSpSetDefaultDir(&spec); + if (err != noErr) { + switch (err) { + case afpAccessDenied: + errno = EACCES; + break; + default: + errno = ENOENT; + } + return -1; + } + + return 0; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpGetNativeCwd -- + * + * This function replaces the library version of getcwd(). + * + * Results: + * The input and output are filesystem paths in native form. The + * result is either the given clientData, if the working directory + * hasn't changed, or a new clientData (owned by our caller), + * giving the new native path, or NULL if the current directory + * could not be determined. If NULL is returned, the caller can + * examine the standard posix error codes to determine the cause of + * the problem. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +ClientData +TclpGetNativeCwd(clientData) + ClientData clientData; +{ + FSSpec theSpec; + int length; + Handle pathHandle = NULL; + OSErr err; + + err = FSpGetDefaultDir(&theSpec); + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return NULL; + } + err = FSpPathFromLocation(&theSpec, &length, &pathHandle); + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return NULL; + } + + if ((clientData != NULL) + && strcmp((CONST char*)(*pathHandle), (CONST char*)clientData) == 0) { + /* No change to pwd */ + DisposeHandle(pathHandle); + return clientData; + } else { + char *newCd; + + HLock(pathHandle); + newCd = (char *) ckalloc((unsigned) + (strlen((CONST char*)(*pathHandle)) + 1)); + strcpy(newCd, (CONST char*)(*pathHandle)); + HUnlock(pathHandle); + DisposeHandle(pathHandle); + return (ClientData) newCd; + } +} + +/* + *---------------------------------------------------------------------- + * + * TclpGetCwd -- + * + * This function replaces the library version of getcwd(). + * (Obsolete function, only retained for old extensions which + * may call it directly). + * + * Results: + * The result is a pointer to a string specifying the current + * directory, or NULL if the current directory could not be + * determined. If NULL is returned, an error message is left in the + * interp's result. Storage for the result string is allocated in + * bufferPtr; the caller must call Tcl_DStringFree() when the result + * is no longer needed. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +CONST char * +TclpGetCwd( + Tcl_Interp *interp, /* If non-NULL, used for error reporting. */ + Tcl_DString *bufferPtr) /* Uninitialized or free DString filled + * with name of current directory. */ +{ + FSSpec theSpec; + int length; + Handle pathHandle = NULL; + + if (FSpGetDefaultDir(&theSpec) != noErr) { + if (interp != NULL) { + Tcl_SetResult(interp, "error getting working directory name", + TCL_STATIC); + } + return NULL; + } + if (FSpPathFromLocation(&theSpec, &length, &pathHandle) != noErr) { + if (interp != NULL) { + Tcl_SetResult(interp, "error getting working directory name", + TCL_STATIC); + } + return NULL; + } + HLock(pathHandle); + Tcl_ExternalToUtfDString(NULL, *pathHandle, length, bufferPtr); + HUnlock(pathHandle); + DisposeHandle(pathHandle); + + return Tcl_DStringValue(bufferPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TclpReadlink -- + * + * This function replaces the library version of readlink(). + * + * Results: + * The result is a pointer to a string specifying the contents + * of the symbolic link given by 'path', or NULL if the symbolic + * link could not be read. Storage for the result string is + * allocated in bufferPtr; the caller must call Tcl_DStringFree() + * when the result is no longer needed. + * + * Side effects: + * See readlink() documentation. + * + *--------------------------------------------------------------------------- + */ + +char * +TclpReadlink( + CONST char *path, /* Path of file to readlink (UTF-8). */ + Tcl_DString *linkPtr) /* Uninitialized or free DString filled + * with contents of link (UTF-8). */ +{ + HFileInfo fpb; + OSErr err; + FSSpec fileSpec; + Boolean isDirectory; + Boolean wasAlias; + long dirID; + char fileName[257]; + char *end; + Handle theString = NULL; + int pathSize; + Tcl_DString ds; + + Tcl_UtfToExternalDString(NULL, path, -1, &ds); + + /* + * Remove ending colons if they exist. + */ + + while ((Tcl_DStringLength(&ds) != 0) + && (Tcl_DStringValue(&ds)[Tcl_DStringLength(&ds) - 1] == ':')) { + Tcl_DStringSetLength(&ds, Tcl_DStringLength(&ds) - 1); + } + + end = strrchr(Tcl_DStringValue(&ds), ':'); + if (end == NULL ) { + strcpy(fileName + 1, Tcl_DStringValue(&ds)); + } else { + strcpy(fileName + 1, end + 1); + Tcl_DStringSetLength(&ds, end + 1 - Tcl_DStringValue(&ds)); + } + fileName[0] = (char) strlen(fileName + 1); + + /* + * Create the file spec for the directory of the file + * we want to look at. + */ + + if (end != NULL) { + err = FSpLocationFromPath(Tcl_DStringLength(&ds), + Tcl_DStringValue(&ds), &fileSpec); + if (err != noErr) { + Tcl_DStringFree(&ds); + errno = EINVAL; + return NULL; + } + } else { + FSMakeFSSpecCompat(0, 0, NULL, &fileSpec); + } + Tcl_DStringFree(&ds); + + /* + * Fill the fpb struct up with info about file or directory. + */ + + FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); + fpb.ioVRefNum = fileSpec.vRefNum; + fpb.ioDirID = dirID; + fpb.ioNamePtr = (StringPtr) fileName; + + fpb.ioFDirIndex = 0; + err = PBGetCatInfoSync((CInfoPBPtr)&fpb); + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return NULL; + } else { + if (fpb.ioFlAttrib & 0x10) { + errno = EINVAL; + return NULL; + } else { + if (fpb.ioFlFndrInfo.fdFlags & 0x8000) { + /* + * The file is a link! + */ + } else { + errno = EINVAL; + return NULL; + } + } + } + + /* + * If we are here it's really a link - now find out + * where it points to. + */ + err = FSMakeFSSpecCompat(fileSpec.vRefNum, dirID, (StringPtr) fileName, + &fileSpec); + if (err == noErr) { + err = ResolveAliasFile(&fileSpec, true, &isDirectory, &wasAlias); + } + if ((err == fnfErr) || wasAlias) { + err = FSpPathFromLocation(&fileSpec, &pathSize, &theString); + if (err != noErr) { + DisposeHandle(theString); + errno = ENAMETOOLONG; + return NULL; + } + } else { + errno = EINVAL; + return NULL; + } + + Tcl_ExternalToUtfDString(NULL, *theString, pathSize, linkPtr); + DisposeHandle(theString); + + return Tcl_DStringValue(linkPtr); +} + +static int +TclpObjStatAlias _ANSI_ARGS_((Tcl_Obj *pathPtr, Tcl_StatBuf *bufPtr, + Boolean resolveLink)); + + +/* + *---------------------------------------------------------------------- + * + * TclpObjLstat -- + * + * This function replaces the library version of lstat(). + * + * Results: + * See lstat() documentation. + * + * Side effects: + * See lstat() documentation. + * + *---------------------------------------------------------------------- + */ + +int +TclpObjLstat(pathPtr, buf) + Tcl_Obj *pathPtr; + Tcl_StatBuf *buf; +{ + return TclpObjStatAlias(pathPtr, buf, FALSE); +} + +/* + *---------------------------------------------------------------------- + * + * TclpObjStat -- + * + * This function replaces the library version of stat(). + * + * Results: + * See stat() documentation. + * + * Side effects: + * See stat() documentation. + * + *---------------------------------------------------------------------- + */ + +int +TclpObjStat(pathPtr, bufPtr) + Tcl_Obj *pathPtr; + Tcl_StatBuf *bufPtr; +{ + return TclpObjStatAlias(pathPtr, bufPtr, TRUE); +} + + +static int +TclpObjStatAlias (Tcl_Obj *pathPtr, Tcl_StatBuf *bufPtr, Boolean resolveLink) +{ + HFileInfo fpb; + HVolumeParam vpb; + OSErr err; + FSSpec fileSpec; + Boolean isDirectory; + long dirID; + + if (resolveLink) + err = FspLocationFromFsPath(pathPtr, &fileSpec); + else + err = FspLLocationFromFsPath(pathPtr, &fileSpec); + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return -1; + } + + /* + * Fill the fpb & vpb struct up with info about file or directory. + */ + + FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); + vpb.ioVRefNum = fpb.ioVRefNum = fileSpec.vRefNum; + vpb.ioNamePtr = fpb.ioNamePtr = fileSpec.name; + if (isDirectory) { + fpb.ioDirID = fileSpec.parID; + } else { + fpb.ioDirID = dirID; + } + + fpb.ioFDirIndex = 0; + err = PBGetCatInfoSync((CInfoPBPtr)&fpb); + if (err == noErr) { + vpb.ioVolIndex = 0; + err = PBHGetVInfoSync((HParmBlkPtr)&vpb); + if (err == noErr && bufPtr != NULL) { + /* + * Files are always readable by everyone. + */ + + bufPtr->st_mode = S_IRUSR | S_IRGRP | S_IROTH; + + /* + * Use the Volume Info & File Info to fill out stat buf. + */ + if (fpb.ioFlAttrib & 0x10) { + bufPtr->st_mode |= S_IFDIR; + bufPtr->st_nlink = 2; + } else { + bufPtr->st_nlink = 1; + if (fpb.ioFlFndrInfo.fdFlags & 0x8000) { + bufPtr->st_mode |= S_IFLNK; + } else { + bufPtr->st_mode |= S_IFREG; + } + } + if ((fpb.ioFlAttrib & 0x10) || (fpb.ioFlFndrInfo.fdType == 'APPL')) { + /* + * Directories and applications are executable by everyone. + */ + + bufPtr->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH; + } + if ((fpb.ioFlAttrib & 0x01) == 0){ + /* + * If not locked, then everyone has write acces. + */ + + bufPtr->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + } + bufPtr->st_ino = fpb.ioDirID; + bufPtr->st_dev = fpb.ioVRefNum; + bufPtr->st_uid = -1; + bufPtr->st_gid = -1; + bufPtr->st_rdev = 0; + bufPtr->st_size = fpb.ioFlLgLen; + bufPtr->st_blksize = vpb.ioVAlBlkSiz; + bufPtr->st_blocks = (bufPtr->st_size + bufPtr->st_blksize - 1) + / bufPtr->st_blksize; + + /* + * The times returned by the Mac file system are in the + * local time zone. We convert them to GMT so that the + * epoch starts from GMT. This is also consistent with + * what is returned from "clock seconds". + */ + + bufPtr->st_atime = bufPtr->st_mtime = fpb.ioFlMdDat + - TclpGetGMTOffset() + tcl_mac_epoch_offset; + bufPtr->st_ctime = fpb.ioFlCrDat - TclpGetGMTOffset() + + tcl_mac_epoch_offset; + } + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + } + + return (err == noErr ? 0 : -1); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_WaitPid -- + * + * Fakes a call to wait pid. + * + * Results: + * Always returns -1. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Pid +Tcl_WaitPid( + Tcl_Pid pid, + int *statPtr, + int options) +{ + return (Tcl_Pid) -1; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacFOpenHack -- + * + * This function replaces fopen. It supports paths with alises. + * Note, remember to undefine the fopen macro! + * + * Results: + * See fopen documentation. + * + * Side effects: + * See fopen documentation. + * + *---------------------------------------------------------------------- + */ + +#undef fopen +FILE * +TclMacFOpenHack( + CONST char *path, + CONST char *mode) +{ + OSErr err; + FSSpec fileSpec; + Handle pathString = NULL; + int size; + FILE * f; + + err = FSpLocationFromPath(strlen(path), path, &fileSpec); + if ((err != noErr) && (err != fnfErr)) { + return NULL; + } + err = FSpPathFromLocation(&fileSpec, &size, &pathString); + if ((err != noErr) && (err != fnfErr)) { + return NULL; + } + + HLock(pathString); + f = fopen(*pathString, mode); + HUnlock(pathString); + DisposeHandle(pathString); + return f; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpGetUserHome -- + * + * This function takes the specified user name and finds their + * home directory. + * + * Results: + * The result is a pointer to a string specifying the user's home + * directory, or NULL if the user's home directory could not be + * determined. Storage for the result string is allocated in + * bufferPtr; the caller must call Tcl_DStringFree() when the result + * is no longer needed. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +char * +TclpGetUserHome(name, bufferPtr) + CONST char *name; /* User name for desired home directory. */ + Tcl_DString *bufferPtr; /* Uninitialized or free DString filled + * with name of user's home directory. */ +{ + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacOSErrorToPosixError -- + * + * Given a Macintosh OSErr return the appropiate POSIX error. + * + * Results: + * A Posix error. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclMacOSErrorToPosixError( + int error) /* A Macintosh error. */ +{ + switch (error) { + case noErr: + return 0; + case bdNamErr: + return ENAMETOOLONG; + case afpObjectTypeErr: + return ENOTDIR; + case fnfErr: + case dirNFErr: + return ENOENT; + case dupFNErr: + return EEXIST; + case dirFulErr: + case dskFulErr: + return ENOSPC; + case fBsyErr: + return EBUSY; + case tmfoErr: + return ENFILE; + case fLckdErr: + case permErr: + case afpAccessDenied: + return EACCES; + case wPrErr: + case vLckdErr: + return EROFS; + case badMovErr: + return EINVAL; + case diffVolErr: + return EXDEV; + default: + return EINVAL; + } +} + +int +TclMacChmod( + CONST char *path, + int mode) +{ + HParamBlockRec hpb; + OSErr err; + Str255 pathName; + strcpy((char *) pathName + 1, path); + pathName[0] = strlen(path); + hpb.fileParam.ioNamePtr = pathName; + hpb.fileParam.ioVRefNum = 0; + hpb.fileParam.ioDirID = 0; + + if (mode & 0200) { + err = PBHRstFLockSync(&hpb); + } else { + err = PBHSetFLockSync(&hpb); + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return -1; + } + + return 0; +} + + +/* + *---------------------------------------------------------------------- + * + * TclpTempFileName -- + * + * This function returns a unique filename. + * + * Results: + * Returns a valid Tcl_Obj* with refCount 0, or NULL on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Obj* +TclpTempFileName() +{ + char fileName[L_tmpnam]; + + if (tmpnam(fileName) == NULL) { /* INTL: Native. */ + return NULL; + } + + return TclpNativeToNormalized((ClientData) fileName); +} + +#ifdef S_IFLNK + +Tcl_Obj* +TclpObjLink(pathPtr, toPtr, linkAction) + Tcl_Obj *pathPtr; + Tcl_Obj *toPtr; + int linkAction; +{ + Tcl_Obj* link = NULL; + + if (toPtr != NULL) { + if (TclpObjAccess(pathPtr, F_OK) != -1) { + /* src exists */ + errno = EEXIST; + return NULL; + } + if (TclpObjAccess(toPtr, F_OK) == -1) { + /* target doesn't exist */ + errno = ENOENT; + return NULL; + } + + if (linkAction & TCL_CREATE_SYMBOLIC_LINK) { + /* Needs to create a new link */ + FSSpec spec; + FSSpec linkSpec; + OSErr err; + CONST char *path; + + err = FspLocationFromFsPath(toPtr, &spec); + if (err != noErr) { + errno = ENOENT; + return NULL; + } + + path = Tcl_FSGetNativePath(pathPtr); + err = FSpLocationFromPath(strlen(path), path, &linkSpec); + if (err == noErr) { + err = dupFNErr; /* EEXIST. */ + } else { + err = CreateAliasFile(&linkSpec, &spec); + } + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return NULL; + } + return toPtr; + } else { + errno = ENODEV; + return NULL; + } + } else { + Tcl_DString ds; + Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); + if (transPtr == NULL) { + return NULL; + } + if (TclpReadlink(Tcl_GetString(transPtr), &ds) != NULL) { + link = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); + Tcl_IncrRefCount(link); + Tcl_DStringFree(&ds); + } + Tcl_DecrRefCount(transPtr); + } + return link; +} + +#endif + + +/* + *--------------------------------------------------------------------------- + * + * TclpFilesystemPathType -- + * + * This function is part of the native filesystem support, and + * returns the path type of the given path. Right now it simply + * returns NULL. In the future it could return specific path + * types, like 'HFS', 'HFS+', 'nfs', 'samba', 'FAT32', etc. + * + * Results: + * NULL at present. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ +Tcl_Obj* +TclpFilesystemPathType(pathPtr) + Tcl_Obj* pathPtr; +{ + /* All native paths are of the same type */ + return NULL; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpUtime -- + * + * Set the modification date for a file. + * + * Results: + * 0 on success, -1 on error. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ +int +TclpUtime(pathPtr, tval) + Tcl_Obj *pathPtr; /* File to modify */ + struct utimbuf *tval; /* New modification date structure */ +{ + long gmt_offset=TclpGetGMTOffset(); + struct utimbuf local_tval; + local_tval.actime=tval->actime+gmt_offset; + local_tval.modtime=tval->modtime+gmt_offset; + return utime(Tcl_FSGetNativePath(Tcl_FSGetNormalizedPath(NULL,pathPtr)), + &local_tval); +} + +/* + *--------------------------------------------------------------------------- + * + * CreateAliasFile -- + * + * Creates an alias file located at aliasDest referring to the targetFile. + * + * Results: + * 0 on success, OS error code on error. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ +static OSErr +CreateAliasFile(FSSpec *theAliasFile, FSSpec *targetFile) +{ + CInfoPBRec cat; + FInfo fndrInfo; + AliasHandle theAlias; + short saveRef, rsrc = -1; + OSErr err; + + saveRef = CurResFile(); + /* set up the Finder information record for the alias file */ + cat.dirInfo.ioNamePtr = targetFile->name; + cat.dirInfo.ioVRefNum = targetFile->vRefNum; + cat.dirInfo.ioFDirIndex = 0; + cat.dirInfo.ioDrDirID = targetFile->parID; + err = PBGetCatInfoSync(&cat); + if (err != noErr) goto bail; + if ((cat.dirInfo.ioFlAttrib & 16) == 0) { + /* file alias */ + switch (cat.hFileInfo.ioFlFndrInfo.fdType) { + case 'APPL': fndrInfo.fdType = kApplicationAliasType; break; + case 'APPC': fndrInfo.fdType = kApplicationCPAliasType; break; + case 'APPD': fndrInfo.fdType = kApplicationDAAliasType; break; + default: fndrInfo.fdType = cat.hFileInfo.ioFlFndrInfo.fdType; break; + } + fndrInfo.fdCreator = cat.hFileInfo.ioFlFndrInfo.fdCreator; + } else { + /* folder alias */ + fndrInfo.fdType = kContainerFolderAliasType; + fndrInfo.fdCreator = 'MACS'; + } + fndrInfo.fdFlags = kIsAlias; + fndrInfo.fdLocation.v = 0; + fndrInfo.fdLocation.h = 0; + fndrInfo.fdFldr = 0; + /* create new file and set the file information */ + FSpCreateResFile( theAliasFile, fndrInfo.fdCreator, fndrInfo.fdType, smSystemScript); + if ((err = ResError()) != noErr) goto bail; + err = FSpSetFInfo( theAliasFile, &fndrInfo); + if (err != noErr) goto bail; + /* save the alias resource */ + rsrc = FSpOpenResFile(theAliasFile, fsRdWrPerm); + if (rsrc == -1) { err = ResError(); goto bail; } + UseResFile(rsrc); + err = NewAlias(theAliasFile, targetFile, &theAlias); + if (err != noErr) goto bail; + AddResource((Handle) theAlias, rAliasType, 0, theAliasFile->name); + if ((err = ResError()) != noErr) goto bail; + CloseResFile(rsrc); + rsrc = -1; + /* done */ + bail: + if (rsrc != -1) CloseResFile(rsrc); + UseResFile(saveRef); + return err; +} diff --git a/mac/tclMacInit.c b/mac/tclMacInit.c new file mode 100644 index 0000000..f132577 --- /dev/null +++ b/mac/tclMacInit.c @@ -0,0 +1,802 @@ +/* + * tclMacInit.c -- + * + * Contains the Mac-specific interpreter initialization functions. + * + * Copyright (c) 1995-1998 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacInit.c,v 1.9 2002/02/08 02:52:54 dgp Exp $ + */ + +#include <AppleEvents.h> +#include <AEDataModel.h> +#include <AEObjects.h> +#include <AEPackObject.h> +#include <AERegistry.h> +#include <Files.h> +#include <Folders.h> +#include <Gestalt.h> +#include <TextUtils.h> +#include <Resources.h> +#include <Strings.h> +#include "tclInt.h" +#include "tclMacInt.h" +#include "tclPort.h" +#include "tclInitScript.h" + +/* + * The following string is the startup script executed in new + * interpreters. It looks on the library path and in the resource fork for + * a script "init.tcl" that is compatible with this version of Tcl. The + * init.tcl script does all of the real work of initialization. + */ + +static char initCmd[] = "if {[info proc tclInit]==\"\"} {\n\ +proc tclInit {} {\n\ +global tcl_pkgPath env\n\ +proc sourcePath {file} {\n\ + foreach i $::auto_path {\n\ + set init [file join $i $file.tcl]\n\ + if {[catch {uplevel #0 [list source $init]}] == 0} {\n\ + return\n\ + }\n\ + }\n\ + if {[catch {uplevel #0 [list source -rsrc $file]}] == 0} {\n\ + return\n\ + }\n\ + rename sourcePath {}\n\ + set msg \"Can't find $file resource or a usable $file.tcl file\"\n\ + append msg \" in the following directories:\"\n\ + append msg \" $::auto_path\"\n\ + append msg \" perhaps you need to install Tcl or set your\"\n\ + append msg \" TCL_LIBRARY environment variable?\"\n\ + error $msg\n\ +}\n\ +if {[info exists env(EXT_FOLDER)]} {\n\ + lappend tcl_pkgPath [file join $env(EXT_FOLDER) {Tool Command Language}]\n\ +}\n\ +if {[info exists tcl_pkgPath] == 0} {\n\ + set tcl_pkgPath {no extension folder}\n\ +}\n\ +sourcePath init\n\ +sourcePath auto\n\ +sourcePath package\n\ +sourcePath history\n\ +sourcePath word\n\ +sourcePath parray\n\ +rename sourcePath {}\n\ +} }\n\ +tclInit"; + +/* + * The following structures are used to map the script/language codes of a + * font to the name that should be passed to Tcl_GetEncoding() to obtain + * the encoding for that font. The set of numeric constants is fixed and + * defined by Apple. + */ + +typedef struct Map { + int numKey; + char *strKey; +} Map; + +static Map scriptMap[] = { + {smRoman, "macRoman"}, + {smJapanese, "macJapan"}, + {smTradChinese, "macChinese"}, + {smKorean, "macKorean"}, + {smArabic, "macArabic"}, + {smHebrew, "macHebrew"}, + {smGreek, "macGreek"}, + {smCyrillic, "macCyrillic"}, + {smRSymbol, "macRSymbol"}, + {smDevanagari, "macDevanagari"}, + {smGurmukhi, "macGurmukhi"}, + {smGujarati, "macGujarati"}, + {smOriya, "macOriya"}, + {smBengali, "macBengali"}, + {smTamil, "macTamil"}, + {smTelugu, "macTelugu"}, + {smKannada, "macKannada"}, + {smMalayalam, "macMalayalam"}, + {smSinhalese, "macSinhalese"}, + {smBurmese, "macBurmese"}, + {smKhmer, "macKhmer"}, + {smThai, "macThailand"}, + {smLaotian, "macLaos"}, + {smGeorgian, "macGeorgia"}, + {smArmenian, "macArmenia"}, + {smSimpChinese, "macSimpChinese"}, + {smTibetan, "macTIbet"}, + {smMongolian, "macMongolia"}, + {smGeez, "macEthiopia"}, + {smEastEurRoman, "macCentEuro"}, + {smVietnamese, "macVietnam"}, + {smExtArabic, "macSindhi"}, + {NULL, NULL} +}; + +static Map romanMap[] = { + {langCroatian, "macCroatian"}, + {langSlovenian, "macCroatian"}, + {langIcelandic, "macIceland"}, + {langRomanian, "macRomania"}, + {langTurkish, "macTurkish"}, + {langGreek, "macGreek"}, + {NULL, NULL} +}; + +static Map cyrillicMap[] = { + {langUkrainian, "macUkraine"}, + {langBulgarian, "macBulgaria"}, + {NULL, NULL} +}; + +static int GetFinderFont(int *finderID); + +/* Used to store the encoding used for binary files */ +static Tcl_Encoding binaryEncoding = NULL; +/* Has the basic library path encoding issue been fixed */ +static int libraryPathEncodingFixed = 0; + + +/* + *---------------------------------------------------------------------- + * + * GetFinderFont -- + * + * Gets the "views" font of the Macintosh Finder + * + * Results: + * Standard Tcl result, and sets finderID to the font family + * id for the current finder font. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +static int +GetFinderFont(int *finderID) +{ + OSErr err = noErr; + OSType finderPrefs, viewFont = 'vfnt'; + DescType returnType; + Size returnSize; + long result, sys8Mask = 0x0800; + static AppleEvent outgoingAevt = {typeNull, NULL}; + AppleEvent returnAevt; + AEAddressDesc fndrAddress; + AEDesc nullContainer = {typeNull, NULL}, + tempDesc = {typeNull, NULL}, + tempDesc2 = {typeNull, NULL}, + finalDesc = {typeNull, NULL}; + const OSType finderSignature = 'MACS'; + + + if (outgoingAevt.descriptorType == typeNull) { + if ((Gestalt(gestaltSystemVersion, &result) != noErr) + || (result >= sys8Mask)) { + finderPrefs = 'pfrp'; + } else { + finderPrefs = 'pvwp'; + } + + AECreateDesc(typeApplSignature, &finderSignature, + sizeof(finderSignature), &fndrAddress); + + err = AECreateAppleEvent(kAECoreSuite, kAEGetData, &fndrAddress, + kAutoGenerateReturnID, kAnyTransactionID, &outgoingAevt); + + AEDisposeDesc(&fndrAddress); + + /* + * The structure is: + * the property view font ('vfnt') + * of the property view preferences ('pvwp') + * of the Null Container (i.e. the Finder itself). + */ + + AECreateDesc(typeType, &finderPrefs, sizeof(finderPrefs), &tempDesc); + err = CreateObjSpecifier(typeType, &nullContainer, formPropertyID, + &tempDesc, true, &tempDesc2); + AECreateDesc(typeType, &viewFont, sizeof(viewFont), &tempDesc); + err = CreateObjSpecifier(typeType, &tempDesc2, formPropertyID, + &tempDesc, true, &finalDesc); + + AEPutKeyDesc(&outgoingAevt, keyDirectObject, &finalDesc); + AEDisposeDesc(&finalDesc); + } + + err = AESend(&outgoingAevt, &returnAevt, kAEWaitReply, kAEHighPriority, + kAEDefaultTimeout, NULL, NULL); + if (err == noErr) { + err = AEGetKeyPtr(&returnAevt, keyDirectObject, typeInteger, + &returnType, (void *) finderID, sizeof(int), &returnSize); + if (err == noErr) { + return TCL_OK; + } + } + return TCL_ERROR; +} + +/* + *--------------------------------------------------------------------------- + * + * TclMacGetFontEncoding -- + * + * Determine the encoding of the specified font. The encoding + * can be used to convert bytes from UTF-8 into the encoding of + * that font. + * + * Results: + * The return value is a string that specifies the font's encoding + * and that can be passed to Tcl_GetEncoding() to construct the + * encoding. If the font's encoding could not be identified, NULL + * is returned. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +char * +TclMacGetFontEncoding( + int fontId) +{ + int script, lang; + char *name; + Map *mapPtr; + + script = FontToScript(fontId); + lang = GetScriptVariable(script, smScriptLang); + name = NULL; + if (script == smRoman) { + for (mapPtr = romanMap; mapPtr->strKey != NULL; mapPtr++) { + if (mapPtr->numKey == lang) { + name = mapPtr->strKey; + break; + } + } + } else if (script == smCyrillic) { + for (mapPtr = cyrillicMap; mapPtr->strKey != NULL; mapPtr++) { + if (mapPtr->numKey == lang) { + name = mapPtr->strKey; + break; + } + } + } + if (name == NULL) { + for (mapPtr = scriptMap; mapPtr->strKey != NULL; mapPtr++) { + if (mapPtr->numKey == script) { + name = mapPtr->strKey; + break; + } + } + } + return name; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpInitPlatform -- + * + * Initialize all the platform-dependant things like signals and + * floating-point error handling. + * + * Called at process initialization time. + * + * Results: + * None. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +void +TclpInitPlatform() +{ + tclPlatform = TCL_PLATFORM_MAC; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpInitLibraryPath -- + * + * Initialize the library path at startup. We have a minor + * metacircular problem that we don't know the encoding of the + * operating system but we may need to talk to operating system + * to find the library directories so that we know how to talk to + * the operating system. + * + * We do not know the encoding of the operating system. + * We do know that the encoding is some multibyte encoding. + * In that multibyte encoding, the characters 0..127 are equivalent + * to ascii. + * + * So although we don't know the encoding, it's safe: + * to look for the last colon character in a path in the encoding. + * to append an ascii string to a path. + * to pass those strings back to the operating system. + * + * But any strings that we remembered before we knew the encoding of + * the operating system must be translated to UTF-8 once we know the + * encoding so that the rest of Tcl can use those strings. + * + * This call sets the library path to strings in the unknown native + * encoding. TclpSetInitialEncodings() will translate the library + * path from the native encoding to UTF-8 as soon as it determines + * what the native encoding actually is. + * + * Called at process initialization time. + * + * Results: + * None. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +void +TclpInitLibraryPath(argv0) + CONST char *argv0; /* Name of executable from argv[0] to main(). + * Not used because we can determine the name + * by querying the module handle. */ +{ + Tcl_Obj *objPtr, *pathPtr; + CONST char *str; + Tcl_DString ds; + + TclMacCreateEnv(); + + pathPtr = Tcl_NewObj(); + + /* + * Look for the library relative to default encoding dir. + */ + + str = Tcl_GetDefaultEncodingDir(); + if ((str != NULL) && (str[0] != '\0')) { + objPtr = Tcl_NewStringObj(str, -1); + Tcl_ListObjAppendElement(NULL, pathPtr, objPtr); + } + + str = TclGetEnv("TCL_LIBRARY", &ds); + if ((str != NULL) && (str[0] != '\0')) { + /* + * If TCL_LIBRARY is set, search there. + */ + + objPtr = Tcl_NewStringObj(str, Tcl_DStringLength(&ds)); + Tcl_ListObjAppendElement(NULL, pathPtr, objPtr); + Tcl_DStringFree(&ds); + } + + objPtr = TclGetLibraryPath(); + if (objPtr != NULL) { + Tcl_ListObjAppendList(NULL, pathPtr, objPtr); + } + + /* + * lappend path [file join $env(EXT_FOLDER) \ + * "Tool Command Language" "tcl[info version]" + */ + + str = TclGetEnv("EXT_FOLDER", &ds); + if ((str != NULL) && (str[0] != '\0')) { + Tcl_DString libPath, path; + CONST char *argv[3]; + + argv[0] = str; + argv[1] = "Tool Command Language"; + Tcl_DStringInit(&libPath); + Tcl_DStringAppend(&libPath, "tcl", -1); + argv[2] = Tcl_DStringAppend(&libPath, TCL_VERSION, -1); + Tcl_DStringInit(&path); + str = Tcl_JoinPath(3, argv, &path); + objPtr = Tcl_NewStringObj(str, Tcl_DStringLength(&path)); + Tcl_ListObjAppendElement(NULL, pathPtr, objPtr); + Tcl_DStringFree(&ds); + Tcl_DStringFree(&libPath); + Tcl_DStringFree(&path); + } + TclSetLibraryPath(pathPtr); +} + +/* + *--------------------------------------------------------------------------- + * + * TclpSetInitialEncodings -- + * + * Based on the locale, determine the encoding of the operating + * system and the default encoding for newly opened files. + * + * Called at process initialization time, and part way through + * startup, we verify that the initial encodings were correctly + * setup. Depending on Tcl's environment, there may not have been + * enough information first time through (above). + * + * Results: + * None. + * + * Side effects: + * The Tcl library path is converted from native encoding to UTF-8, + * on the first call, and the encodings may be changed on first or + * second call. + * + *--------------------------------------------------------------------------- + */ + +void +TclpSetInitialEncodings() +{ + CONST char *encoding; + Tcl_Obj *pathPtr; + int fontId, err; + + fontId = 0; + GetFinderFont(&fontId); + encoding = TclMacGetFontEncoding(fontId); + if (encoding == NULL) { + encoding = "macRoman"; + } + + err = Tcl_SetSystemEncoding(NULL, encoding); + + if (err == TCL_OK && libraryPathEncodingFixed == 0) { + + /* + * Until the system encoding was actually set, the library path was + * actually in the native multi-byte encoding, and not really UTF-8 + * as advertised. We cheated as follows: + * + * 1. It was safe to allow the Tcl_SetSystemEncoding() call to + * append the ASCII chars that make up the encoding's filename to + * the names (in the native encoding) of directories in the library + * path, since all Unix multi-byte encodings have ASCII in the + * beginning. + * + * 2. To open the encoding file, the native bytes in the file name + * were passed to the OS, without translating from UTF-8 to native, + * because the name was already in the native encoding. + * + * Now that the system encoding was actually successfully set, + * translate all the names in the library path to UTF-8. That way, + * next time we search the library path, we'll translate the names + * from UTF-8 to the system encoding which will be the native + * encoding. + */ + + pathPtr = TclGetLibraryPath(); + if (pathPtr != NULL) { + int i, objc; + Tcl_Obj **objv; + + objc = 0; + Tcl_ListObjGetElements(NULL, pathPtr, &objc, &objv); + for (i = 0; i < objc; i++) { + int length; + char *string; + Tcl_DString ds; + + string = Tcl_GetStringFromObj(objv[i], &length); + Tcl_ExternalToUtfDString(NULL, string, length, &ds); + Tcl_SetStringObj(objv[i], Tcl_DStringValue(&ds), + Tcl_DStringLength(&ds)); + Tcl_DStringFree(&ds); + } + Tcl_InvalidateStringRep(pathPtr); + } + libraryPathEncodingFixed = 1; + } + + /* This is only ever called from the startup thread */ + if (binaryEncoding == NULL) { + /* + * Keep the iso8859-1 encoding preloaded. The IO package uses + * it for gets on a binary channel. + */ + binaryEncoding = Tcl_GetEncoding(NULL, "iso8859-1"); + } +} + +/* + *--------------------------------------------------------------------------- + * + * TclpSetVariables -- + * + * Performs platform-specific interpreter initialization related to + * the tcl_library and tcl_platform variables, and other platform- + * specific things. + * + * Results: + * None. + * + * Side effects: + * Sets "tcl_library" and "tcl_platform" Tcl variables. + * + *---------------------------------------------------------------------- + */ + +void +TclpSetVariables(interp) + Tcl_Interp *interp; +{ + long int gestaltResult; + int minor, major, objc; + Tcl_Obj **objv; + char versStr[2 * TCL_INTEGER_SPACE]; + CONST char *str; + Tcl_Obj *pathPtr; + Tcl_DString ds; + + str = "no library"; + pathPtr = TclGetLibraryPath(); + if (pathPtr != NULL) { + objc = 0; + Tcl_ListObjGetElements(NULL, pathPtr, &objc, &objv); + if (objc > 0) { + str = Tcl_GetStringFromObj(objv[0], NULL); + } + } + Tcl_SetVar(interp, "tcl_library", str, TCL_GLOBAL_ONLY); + + if (pathPtr != NULL) { + Tcl_SetVar2Ex(interp, "tcl_pkgPath", NULL, pathPtr, TCL_GLOBAL_ONLY); + } + + Tcl_SetVar2(interp, "tcl_platform", "platform", "macintosh", + TCL_GLOBAL_ONLY); + Tcl_SetVar2(interp, "tcl_platform", "os", "MacOS", TCL_GLOBAL_ONLY); + Gestalt(gestaltSystemVersion, &gestaltResult); + major = (gestaltResult & 0x0000FF00) >> 8; + minor = (gestaltResult & 0x000000F0) >> 4; + sprintf(versStr, "%d.%d", major, minor); + Tcl_SetVar2(interp, "tcl_platform", "osVersion", versStr, TCL_GLOBAL_ONLY); +#if GENERATINGPOWERPC + Tcl_SetVar2(interp, "tcl_platform", "machine", "ppc", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "tcl_platform", "machine", "68k", TCL_GLOBAL_ONLY); +#endif + + /* + * Copy USER or LOGIN environment variable into tcl_platform(user) + * These are set by SystemVariables in tclMacEnv.c + */ + + Tcl_DStringInit(&ds); + str = TclGetEnv("USER", &ds); + if (str == NULL) { + str = TclGetEnv("LOGIN", &ds); + if (str == NULL) { + str = ""; + } + } + Tcl_SetVar2(interp, "tcl_platform", "user", str, TCL_GLOBAL_ONLY); + Tcl_DStringFree(&ds); +} + +/* + *---------------------------------------------------------------------- + * + * TclpCheckStackSpace -- + * + * On a 68K Mac, we can detect if we are about to blow the stack. + * Called before an evaluation can happen when nesting depth is + * checked. + * + * Results: + * 1 if there is enough stack space to continue; 0 if not. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclpCheckStackSpace() +{ + return StackSpace() > TCL_MAC_STACK_THRESHOLD; +} + +/* + *---------------------------------------------------------------------- + * + * TclpFindVariable -- + * + * Locate the entry in environ for a given name. On Unix and Macthis + * routine is case sensitive, on Windows this matches mixed case. + * + * Results: + * The return value is the index in environ of an entry with the + * name "name", or -1 if there is no such entry. The integer at + * *lengthPtr is filled in with the length of name (if a matching + * entry is found) or the length of the environ array (if no matching + * entry is found). + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclpFindVariable(name, lengthPtr) + CONST char *name; /* Name of desired environment variable + * (native). */ + int *lengthPtr; /* Used to return length of name (for + * successful searches) or number of non-NULL + * entries in environ (for unsuccessful + * searches). */ +{ + int i, result = -1; + register CONST char *env, *p1, *p2; + Tcl_DString envString; + + Tcl_DStringInit(&envString); + for (i = 0, env = environ[i]; env != NULL; i++, env = environ[i]) { + p1 = Tcl_ExternalToUtfDString(NULL, env, -1, &envString); + p2 = name; + + for (; *p2 == *p1; p1++, p2++) { + /* NULL loop body. */ + } + if ((*p1 == '=') && (*p2 == '\0')) { + *lengthPtr = p2 - name; + result = i; + goto done; + } + + Tcl_DStringFree(&envString); + } + + *lengthPtr = i; + + done: + Tcl_DStringFree(&envString); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_Init -- + * + * This procedure is typically invoked by Tcl_AppInit procedures + * to perform additional initialization for a Tcl interpreter, + * such as sourcing the "init.tcl" script. + * + * Results: + * Returns a standard Tcl completion code and sets the interp's result + * if there is an error. + * + * Side effects: + * Depends on what's in the init.tcl script. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_Init( + Tcl_Interp *interp) /* Interpreter to initialize. */ +{ + Tcl_Obj *pathPtr; + + if (tclPreInitScript != NULL) { + if (Tcl_Eval(interp, tclPreInitScript) == TCL_ERROR) { + return (TCL_ERROR); + }; + } + + /* + * For Macintosh applications the Init function may be contained in + * the application resources. If it exists we use it - otherwise we + * look in the tcl_library directory. Ditto for the history command. + */ + + pathPtr = TclGetLibraryPath(); + if (pathPtr == NULL) { + pathPtr = Tcl_NewObj(); + } + Tcl_SetVar2Ex(interp, "auto_path", NULL, pathPtr, TCL_GLOBAL_ONLY); + return Tcl_Eval(interp, initCmd); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_SourceRCFile -- + * + * This procedure is typically invoked by Tcl_Main or Tk_Main + * procedure to source an application specific rc file into the + * interpreter at startup time. This will either source a file + * in the "tcl_rcFileName" variable or a TEXT resource in the + * "tcl_rcRsrcName" variable. + * + * Results: + * None. + * + * Side effects: + * Depends on what's in the rc script. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_SourceRCFile( + Tcl_Interp *interp) /* Interpreter to source rc file into. */ +{ + Tcl_DString temp; + CONST char *fileName; + Tcl_Channel errChannel; + Handle h; + + fileName = Tcl_GetVar(interp, "tcl_rcFileName", TCL_GLOBAL_ONLY); + + if (fileName != NULL) { + Tcl_Channel c; + CONST char *fullName; + + Tcl_DStringInit(&temp); + fullName = Tcl_TranslateFileName(interp, fileName, &temp); + if (fullName == NULL) { + /* + * Couldn't translate the file name (e.g. it referred to a + * bogus user or there was no HOME environment variable). + * Just do nothing. + */ + } else { + + /* + * Test for the existence of the rc file before trying to read it. + */ + + c = Tcl_OpenFileChannel(NULL, fullName, "r", 0); + if (c != (Tcl_Channel) NULL) { + Tcl_Close(NULL, c); + if (Tcl_EvalFile(interp, fullName) != TCL_OK) { + errChannel = Tcl_GetStdChannel(TCL_STDERR); + if (errChannel) { + Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); + Tcl_WriteChars(errChannel, "\n", 1); + } + } + } + } + Tcl_DStringFree(&temp); + } + + fileName = Tcl_GetVar(interp, "tcl_rcRsrcName", TCL_GLOBAL_ONLY); + + if (fileName != NULL) { + Str255 rezName; + Tcl_DString ds; + Tcl_UtfToExternalDString(NULL, fileName, -1, &ds); + strcpy((char *) rezName + 1, Tcl_DStringValue(&ds)); + rezName[0] = (unsigned) Tcl_DStringLength(&ds); + h = GetNamedResource('TEXT', rezName); + Tcl_DStringFree(&ds); + if (h != NULL) { + if (Tcl_MacEvalResource(interp, fileName, 0, NULL) != TCL_OK) { + errChannel = Tcl_GetStdChannel(TCL_STDERR); + if (errChannel) { + Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); + Tcl_WriteChars(errChannel, "\n", 1); + } + } + Tcl_ResetResult(interp); + ReleaseResource(h); + } + } +} diff --git a/mac/tclMacInt.h b/mac/tclMacInt.h new file mode 100644 index 0000000..ab7bc7f --- /dev/null +++ b/mac/tclMacInt.h @@ -0,0 +1,77 @@ +/* + * tclMacInt.h -- + * + * Declarations of Macintosh specific shared variables and procedures. + * + * Copyright (c) 1996-1998 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacInt.h,v 1.7 2001/11/23 01:27:36 das Exp $ + */ + +#ifndef _TCLMACINT +#define _TCLMACINT + +#ifndef _TCLINT +#include "tclInt.h" +#endif +#ifndef _TCLPORT +#include "tclPort.h" +#endif + +#include <Events.h> +#include <Files.h> + +/* + * Defines to control stack behavior. + * + * The Tcl8.2 regexp code is highly recursive for patterns with many + * subexpressions. So we have to increase the stack space to accomodate. + * 512 K is good enough for ordinary work, but you need 768 to pass the Tcl + * regexp testsuite. + * + * For the PPC, you need to set the stack space in the Project file. + * + */ + +#ifdef TCL_TEST +# define TCL_MAC_68K_STACK_GROWTH (768*1024) +#else +# define TCL_MAC_68K_STACK_GROWTH (512*1024) +#endif + +#define TCL_MAC_STACK_THRESHOLD 16384 + +#ifdef BUILD_tcl +# undef TCL_STORAGE_CLASS +# define TCL_STORAGE_CLASS DLLEXPORT +#endif + +/* + * This flag is passed to TclMacRegisterResourceFork + * by a file (usually a library) whose resource fork + * should not be closed by the resource command. + */ + +#define TCL_RESOURCE_DONT_CLOSE 2 + +/* + * Typedefs used by Macintosh parts of Tcl. + */ + +/* + * Prototypes of Mac only internal functions. + */ + +EXTERN char * TclMacGetFontEncoding _ANSI_ARGS_((int fontId)); +EXTERN int TclMacHaveThreads _ANSI_ARGS_((void)); +EXTERN long TclpGetGMTOffset _ANSI_ARGS_((void)); + +# undef TCL_STORAGE_CLASS +# define TCL_STORAGE_CLASS DLLIMPORT + +#include "tclIntPlatDecls.h" + +#endif /* _TCLMACINT */ diff --git a/mac/tclMacInterupt.c b/mac/tclMacInterupt.c new file mode 100644 index 0000000..7f37d2f --- /dev/null +++ b/mac/tclMacInterupt.c @@ -0,0 +1,289 @@ +/* + * tclMacInterupt.c -- + * + * This file contains routines that deal with the Macintosh's low level + * time manager. This code provides a better resolution timer than what + * can be provided by WaitNextEvent. + * + * Copyright (c) 1996 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacInterupt.c,v 1.2 1998/09/14 18:40:05 stanton Exp $ + */ + +#include "tclInt.h" +#include "tclMacInt.h" +#include <LowMem.h> +#include <Processes.h> +#include <Timer.h> + +/* + * Data structure for timer tasks. + */ +typedef struct TMInfo { + TMTask tmTask; + ProcessSerialNumber psn; + Point lastPoint; + Point newPoint; + long currentA5; + long ourA5; + int installed; +} TMInfo; + +/* + * Globals used within this file. + */ + +static TimerUPP sleepTimerProc = NULL; +static int interuptsInited = false; +static ProcessSerialNumber applicationPSN; +#define MAX_TIMER_ARRAY_SIZE 16 +static TMInfo timerInfoArray[MAX_TIMER_ARRAY_SIZE]; +static int topTimerElement = 0; + +/* + * Prototypes for procedures that are referenced only in this file: + */ + +#if !GENERATINGCFM +static TMInfo * GetTMInfo(void) ONEWORDINLINE(0x2E89); /* MOVE.L A1,(SP) */ +#endif +static void SleepTimerProc _ANSI_ARGS_((void)); +static pascal void CleanUpExitProc _ANSI_ARGS_((void)); +static void InitInteruptSystem _ANSI_ARGS_((void)); + +/* + *---------------------------------------------------------------------- + * + * InitInteruptSystem -- + * + * Does various initialization for the functions used in this + * file. Sets up Universial Pricedure Pointers, installs a trap + * patch for ExitToShell, etc. + * + * Results: + * None. + * + * Side effects: + * Various initialization. + * + *---------------------------------------------------------------------- + */ + +void +InitInteruptSystem() +{ + int i; + + sleepTimerProc = NewTimerProc(SleepTimerProc); + GetCurrentProcess(&applicationPSN); + for (i = 0; i < MAX_TIMER_ARRAY_SIZE; i++) { + timerInfoArray[i].installed = false; + } + + /* + * Install the ExitToShell patch. We use this patch instead + * of the Tcl exit mechanism because we need to ensure that + * these routines are cleaned up even if we crash or are forced + * to quit. There are some circumstances when the Tcl exit + * handlers may not fire. + */ + + TclMacInstallExitToShellPatch(CleanUpExitProc); + interuptsInited = true; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacStartTimer -- + * + * Install a Time Manager task to wake our process up in the + * future. The process should get a NULL event after ms + * milliseconds. + * + * Results: + * None. + * + * Side effects: + * Schedules our process to wake up. + * + *---------------------------------------------------------------------- + */ + +void * +TclMacStartTimer( + long ms) /* Milliseconds. */ +{ + TMInfo *timerInfoPtr; + + if (!interuptsInited) { + InitInteruptSystem(); + } + + /* + * Obtain a pointer for the timer. We only allocate up + * to MAX_TIMER_ARRAY_SIZE timers. If we are past that + * max we return NULL. + */ + if (topTimerElement < MAX_TIMER_ARRAY_SIZE) { + timerInfoPtr = &timerInfoArray[topTimerElement]; + topTimerElement++; + } else { + return NULL; + } + + /* + * Install timer to wake process in ms milliseconds. + */ + timerInfoPtr->tmTask.tmAddr = sleepTimerProc; + timerInfoPtr->tmTask.tmWakeUp = 0; + timerInfoPtr->tmTask.tmReserved = 0; + timerInfoPtr->psn = applicationPSN; + timerInfoPtr->installed = true; + + InsTime((QElemPtr) timerInfoPtr); + PrimeTime((QElemPtr) timerInfoPtr, (long) ms); + + return (void *) timerInfoPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacRemoveTimer -- + * + * Remove the timer event from the Time Manager. + * + * Results: + * None. + * + * Side effects: + * A scheduled timer would be removed. + * + *---------------------------------------------------------------------- + */ + +void +TclMacRemoveTimer( + void * timerToken) /* Token got from start timer. */ +{ + TMInfo *timerInfoPtr = (TMInfo *) timerToken; + + if (timerInfoPtr == NULL) { + return; + } + + RmvTime((QElemPtr) timerInfoPtr); + timerInfoPtr->installed = false; + topTimerElement--; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacTimerExpired -- + * + * Check to see if the installed timer has expired. + * + * Results: + * True if timer has expired, false otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclMacTimerExpired( + void * timerToken) /* Our token again. */ +{ + TMInfo *timerInfoPtr = (TMInfo *) timerToken; + + if ((timerInfoPtr == NULL) || + !(timerInfoPtr->tmTask.qType & kTMTaskActive)) { + return true; + } else { + return false; + } +} + +/* + *---------------------------------------------------------------------- + * + * SleepTimerProc -- + * + * Time proc is called by the is a callback routine placed in the + * system by Tcl_Sleep. The routine is called at interupt time + * and threrfor can not move or allocate memory. This call will + * schedule our process to wake up the next time the process gets + * around to consider running it. + * + * Results: + * None. + * + * Side effects: + * Schedules our process to wake up. + * + *---------------------------------------------------------------------- + */ + +static void +SleepTimerProc() +{ + /* + * In CFM code we can access our code directly. In 68k code that + * isn't based on CFM we must do a glorious hack. The function + * GetTMInfo is an inline assembler call that moves the pointer + * at A1 to the top of the stack. The Time Manager keeps the TMTask + * info record there before calling this call back. In order for + * this to work the infoPtr argument must be the *last* item on the + * stack. If we "piggyback" our data to the TMTask info record we + * can get access to the information we need. While this is really + * ugly - it's the way Apple recomends it be done - go figure... + */ + +#if GENERATINGCFM + WakeUpProcess(&applicationPSN); +#else + TMInfo * infoPtr; + + infoPtr = GetTMInfo(); + WakeUpProcess(&infoPtr->psn); +#endif +} + +/* + *---------------------------------------------------------------------- + * + * CleanUpExitProc -- + * + * This procedure is invoked as an exit handler when ExitToShell + * is called. It removes the system level timer handler if it + * is installed. This must be called or the Mac OS will more than + * likely crash. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static pascal void +CleanUpExitProc() +{ + int i; + + for (i = 0; i < MAX_TIMER_ARRAY_SIZE; i++) { + if (timerInfoArray[i].installed) { + RmvTime((QElemPtr) &timerInfoArray[i]); + timerInfoArray[i].installed = false; + } + } +} diff --git a/mac/tclMacLibrary.c b/mac/tclMacLibrary.c new file mode 100644 index 0000000..59d4612 --- /dev/null +++ b/mac/tclMacLibrary.c @@ -0,0 +1,248 @@ +/* + * tclMacLibrary.c -- + * + * This file should be included in Tcl extensions that want to + * automatically open their resource forks when the code is linked. + * These routines should not be exported but should be compiled + * locally by each fragment. Many thanks to Jay Lieske + * <lieske@princeton.edu> who provide an initial version of this + * file. + * + * Copyright (c) 1996 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacLibrary.c,v 1.5 2001/11/23 01:27:39 das Exp $ + */ + +/* + * Here is another place that we are using the old routine names... + */ + +#include <CodeFragments.h> +#include <Errors.h> +#include <Resources.h> +#include <Strings.h> +#include "tclMacInt.h" + +#if defined(TCL_REGISTER_LIBRARY) && defined(USE_TCL_STUBS) +#error "Can't use TCL_REGISTER_LIBRARY and USE_TCL_STUBS at the same time!" +/* + * Can't register a library with Tcl when using stubs in the current + * implementation, since Tcl_InitStubs hasn't been called yet + * when OpenLibraryResource is executing. + */ +#endif + +/* + * These function are not currently defined in any header file. The + * only place they should be used is in the Initialization and + * Termination entry points for a code fragment. The prototypes + * are included here to avoid compile errors. + */ + +OSErr TclMacInitializeFragment _ANSI_ARGS_(( + struct CFragInitBlock* initBlkPtr)); +void TclMacTerminateFragment _ANSI_ARGS_((void)); + +/* + * Static functions in this file. + */ + +static OSErr OpenLibraryResource _ANSI_ARGS_(( + struct CFragInitBlock* initBlkPtr)); +static void CloseLibraryResource _ANSI_ARGS_((void)); + +/* + * The refnum of the opened resource fork. + */ +static short ourResFile = kResFileNotOpened; + +/* + * This is the resource token for the our resource file. + * It stores the name we registered with the resource facility. + * We only need to use this if we are actually registering ourselves. + */ + +#ifdef TCL_REGISTER_LIBRARY +static Tcl_Obj *ourResToken; +#endif + +/* + *---------------------------------------------------------------------- + * + * TclMacInitializeFragment -- + * + * Called by MacOS CFM when the shared library is loaded. All this + * function really does is give Tcl a chance to open and register + * the resource fork of the library. + * + * Results: + * MacOS error code if loading should be canceled. + * + * Side effects: + * Opens the resource fork of the shared library file. + * + *---------------------------------------------------------------------- + */ + +OSErr +TclMacInitializeFragment( + struct CFragInitBlock* initBlkPtr) /* Pointer to our library. */ +{ + OSErr err = noErr; + +#ifdef __MWERKS__ + { + extern OSErr __initialize( CFragInitBlock* initBlkPtr); + err = __initialize((CFragInitBlock *) initBlkPtr); + } +#endif + if (err == noErr) + err = OpenLibraryResource( initBlkPtr); + return err; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacTerminateFragment -- + * + * Called by MacOS CFM when the shared library is unloaded. + * + * Results: + * None. + * + * Side effects: + * The resource fork of the code fragment is closed. + * + *---------------------------------------------------------------------- + */ + +void +TclMacTerminateFragment() +{ + CloseLibraryResource(); + +#ifdef __MWERKS__ + { + extern void __terminate(void); + __terminate(); + } +#endif +} + +/* + *---------------------------------------------------------------------- + * + * OpenLibraryResource -- + * + * This routine can be called by a MacOS fragment's initialiation + * function to open the resource fork of the file. + * Call it with the same data passed to the initialization function. + * If the fragment loading should fail if the resource fork can't + * be opened, then the initialization function can pass on this + * return value. + * + * If you #define TCL_REGISTER_RESOURCE before compiling this resource, + * then your library will register its open resource fork with the + * resource command. + * + * Results: + * It returns noErr on success and a MacOS error code on failure. + * + * Side effects: + * The resource fork of the code fragment is opened read-only and + * is installed at the head of the resource chain. + * + *---------------------------------------------------------------------- + */ + +static OSErr +OpenLibraryResource( + struct CFragInitBlock* initBlkPtr) +{ + /* + * The 3.0 version of the Universal headers changed CFragInitBlock + * to an opaque pointer type. CFragSystem7InitBlock is now the + * real pointer. + */ + +#if !defined(UNIVERSAL_INTERFACES_VERSION) || (UNIVERSAL_INTERFACES_VERSION < 0x0300) + struct CFragInitBlock *realInitBlkPtr = initBlkPtr; +#else + CFragSystem7InitBlock *realInitBlkPtr = (CFragSystem7InitBlock *) initBlkPtr; +#endif + FSSpec* fileSpec = NULL; + OSErr err = noErr; + + + if (realInitBlkPtr->fragLocator.where == kDataForkCFragLocator) { + fileSpec = realInitBlkPtr->fragLocator.u.onDisk.fileSpec; + } else if (realInitBlkPtr->fragLocator.where == kResourceCFragLocator) { + fileSpec = realInitBlkPtr->fragLocator.u.inSegs.fileSpec; + } else { + err = resFNotFound; + } + + /* + * Open the resource fork for this library in read-only mode. + * This will make it the current res file, ahead of the + * application's own resources. + */ + + if (fileSpec != NULL) { + ourResFile = FSpOpenResFile(fileSpec, fsRdPerm); + if (ourResFile == kResFileNotOpened) { + err = ResError(); + } else { +#ifdef TCL_REGISTER_LIBRARY + ourResToken = Tcl_NewObj(); + Tcl_IncrRefCount(ourResToken); + p2cstr(realInitBlkPtr->libName); + Tcl_SetStringObj(ourResToken, (char *) realInitBlkPtr->libName, -1); + c2pstr((char *) realInitBlkPtr->libName); + TclMacRegisterResourceFork(ourResFile, ourResToken, + TCL_RESOURCE_DONT_CLOSE); +#endif + SetResFileAttrs(ourResFile, mapReadOnly); + } + } + + return err; +} + +/* + *---------------------------------------------------------------------- + * + * CloseLibraryResource -- + * + * This routine should be called by a MacOS fragment's termination + * function to close the resource fork of the file + * that was opened with OpenLibraryResource. + * + * Results: + * None. + * + * Side effects: + * The resource fork of the code fragment is closed. + * + *---------------------------------------------------------------------- + */ + +static void +CloseLibraryResource() +{ + if (ourResFile != kResFileNotOpened) { +#ifdef TCL_REGISTER_LIBRARY + int length; + TclMacUnRegisterResourceFork( + Tcl_GetStringFromObj(ourResToken, &length), + NULL); + Tcl_DecrRefCount(ourResToken); +#endif + CloseResFile(ourResFile); + ourResFile = kResFileNotOpened; + } +} diff --git a/mac/tclMacLibrary.r b/mac/tclMacLibrary.r new file mode 100644 index 0000000..5fd1cbe --- /dev/null +++ b/mac/tclMacLibrary.r @@ -0,0 +1,209 @@ +/* + * tclMacLibrary.r -- + * + * This file creates resources used by the Tcl shared library. + * Many thanks go to "Jay Lieske, Jr." <lieske@princeton.edu> who + * wrote the initial version of this file. + * + * Copyright (c) 1996-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacLibrary.r,v 1.7 2002/09/12 17:33:20 das Exp $ + */ + +#include <Types.r> +#include <SysTypes.r> + +/* + * The folowing include and defines help construct + * the version string for Tcl. + */ + +#define RC_INVOKED +#include "tcl.h" + +#if (TCL_RELEASE_LEVEL == 0) +# define RELEASE_LEVEL alpha +#elif (TCL_RELEASE_LEVEL == 1) +# define RELEASE_LEVEL beta +#elif (TCL_RELEASE_LEVEL == 2) +# define RELEASE_LEVEL final +#endif + +#if (TCL_RELEASE_LEVEL == 2) +# define MINOR_VERSION (TCL_MINOR_VERSION * 16) + TCL_RELEASE_SERIAL +# define RELEASE_CODE 0x00 +#else +# define MINOR_VERSION TCL_MINOR_VERSION * 16 +# define RELEASE_CODE TCL_RELEASE_SERIAL +#endif + +resource 'vers' (1) { + TCL_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, RELEASE_CODE, verUS, + TCL_PATCH_LEVEL, + TCL_PATCH_LEVEL ", by Ray Johnson & Jim Ingham" "\n" "© 2001 Tcl Core Team" +}; + +resource 'vers' (2) { + TCL_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, RELEASE_CODE, verUS, + TCL_PATCH_LEVEL, + "Tcl Library " TCL_PATCH_LEVEL " © 1993-2001" +}; + +/* + * Currently the creator for all Tcl/Tk libraries and extensions + * should be 'TclL'. This will allow those extension and libraries + * to use the common icon for Tcl extensions. However, this signature + * still needs to be approved by the signature police at Apple and may + * change. + */ +#define TCL_CREATOR 'TclL' +#define TCL_LIBRARY_RESOURCES 2000 + +/* + * The 'BNDL' resource is the primary link between a file's + * creator/type and its icon. This resource acts for all Tcl shared + * libraries; other libraries will not need one and ought to use + * custom icons rather than new file types for a different appearance. + */ + +resource 'BNDL' (TCL_LIBRARY_RESOURCES, "Tcl bundle", purgeable) +{ + TCL_CREATOR, + 0, + { /* array TypeArray: 2 elements */ + /* [1] */ + 'FREF', + { /* array IDArray: 1 elements */ + /* [1] */ + 0, TCL_LIBRARY_RESOURCES + }, + /* [2] */ + 'ICN#', + { /* array IDArray: 1 elements */ + /* [1] */ + 0, TCL_LIBRARY_RESOURCES + } + } +}; + +resource 'FREF' (TCL_LIBRARY_RESOURCES, purgeable) +{ + 'shlb', 0, "" +}; + +type TCL_CREATOR as 'STR '; +resource TCL_CREATOR (0, purgeable) { + "Tcl Library " TCL_PATCH_LEVEL " © 1993-2001" +}; + +/* + * The 'kind' resource works with a 'BNDL' in Macintosh Easy Open + * to affect the text the Finder displays in the "kind" column and + * file info dialog. This information will be applied to all files + * with the listed creator and type. + */ + +resource 'kind' (TCL_LIBRARY_RESOURCES, "Tcl kind", purgeable) { + TCL_CREATOR, + 0, /* region = USA */ + { + 'shlb', "Tcl Library" + } +}; + + +/* + * The -16397 string will be displayed by Finder when a user + * tries to open the shared library. The string should + * give the user a little detail about the library's capabilities + * and enough information to install the library in the correct location. + * A similar string should be placed in all shared libraries. + */ +resource 'STR ' (-16397, purgeable) { + "Tcl Library\n\n" + "This is the core library needed to run Tool Command Language programs. " + "To work properly, it should be placed in the ŒTool Command Language¹ folder " + "within the Extensions folder." +}; + +/* + * The following are icons for the shared library. + */ + +data 'icl4' (2000, "Tcl Shared Library", purgeable) { + $"0FFF FFFF FFFF FFFF FFFF FFFF FFFF 0000" + $"F000 0000 0000 0000 0000 0000 000C F000" + $"F0CC CFFF CCCC CCC6 66CC CCCC CCCC F000" + $"F0CC CFFF FFFF FF66 F6CC CCCC CCCC F000" + $"F0CC CFFF 2000 0D66 6CCC CCCC CCCC F000" + $"F0CC CFFF 0202 056F 6E5C CCCC CCCC F000" + $"F0CC CFFF 2020 C666 F66F CCCC CCCC F000" + $"F0CC CFFF 0200 B66F 666B FCCC CCCC F000" + $"F0FC CFFF B020 55F6 6F52 BFCC CCCC F000" + $"FF0F 0CCC FB02 5665 66D0 2FCC CCCC F0F0" + $"F00F 0CCC CFB0 BF55 F6CF FFCC CCCC FFCF" + $"000F 0CCC CCFB 06C9 66CC CCCC CCCC F0CF" + $"000F 0CCC CCCF 56C6 6CCC CCCC CCCC CCCF" + $"000F 0CCC CCCC 6FC6 FCCC CCCC CCCC CCCF" + $"000F 0CCC CCCC 65C5 65CC CCCC CCCC CCCF" + $"000F 0CCC CCCC 55D6 57CC CCCC CCCC CCCF" + $"000F 0CCC CCCC 65CF 6CCC CCCC CCCC CCCF" + $"000F 0CCC CCCC 5AC6 6CFF CCCC CCCC CCCF" + $"000F 0CCC CCCC 65C5 6CF0 FCCC CCCC CCCF" + $"000F 0CCC CCCC CECF CCF0 0FCC CCCC CCCF" + $"000F 0CCC CCCC C5C6 CCCF 20FC CCCC FCCF" + $"F00F 0CCC CCCF FFD5 CCCC F20F CCCC FFCF" + $"FF0F 0CCC CCCF 20CF CCCC F020 FCCC F0F0" + $"F0F0 CCCC CCCF B2C2 FFFF 0002 0FFC F000" + $"F00C CCCC CCCC FBC0 2000 0020 2FFC F000" + $"F0CC CCCC CCCC CFCB 0202 0202 0FFC F000" + $"F0CC CCCC CCCC CCCF B020 2020 2FFC F000" + $"F0CC CCCC CCCC CCDC FBBB BBBB BFFC F000" + $"F0CC CCCC CCCC CCCC CFFF FFFF FFFC F000" + $"F0CC CCCC CCCC CCCC CCCC CCCC CFFC F000" + $"FCCC CCCC CCCC CCCC CCCC CCCC CCCC F000" + $"0FFF FFFF FFFF FFFF FFFF FFFF FFFF 0000" +}; + +data 'ICN#' (2000, "Tcl Shared Library", purgeable) { + $"7FFF FFF0 8000 0008 8701 C008 87FF C008" + $"8703 8008 8707 E008 8707 F008 870F F808" + $"A78F EC08 D0CF C40A 906F DC0D 1035 C009" + $"101D 8001 100D 8001 100D C001 100D C001" + $"100D 8001 100D B001 100D A801 1005 2401" + $"1005 1209 901D 090D D011 088A A018 F068" + $"800C 0068 8005 0068 8001 8068 8000 FFE8" + $"8000 7FE8 8000 0068 8000 0008 7FFF FFF0" + $"7FFF FFF0 FFFF FFF8 FFFF FFF8 FFFF FFF8" + $"FFFF FFF8 FFFF FFF8 FFFF FFF8 FFFF FFF8" + $"FFFF FFF8 DFFF FFFA 9FFF FFFF 1FFF FFFF" + $"1FFF FFFF 1FFF FFFF 1FFF FFFF 1FFF FFFF" + $"1FFF FFFF 1FFF FFFF 1FFF FFFF 1FFF FFFF" + $"1FFF FFFF 9FFF FFFF DFFF FFFA FFFF FFF8" + $"FFFF FFF8 FFFF FFF8 FFFF FFF8 FFFF FFF8" + $"FFFF FFF8 FFFF FFF8 FFFF FFF8 7FFF FFF0" +}; + +data 'ics#' (2000, "Tcl Shared Library", purgeable) { + $"FFFE B582 BB82 B3C2 BFA2 43C3 4381 4381" + $"4381 4763 4392 856E 838E 81AE 811E FFFE" + $"FFFE FFFE FFFE FFFE FFFE FFFF 7FFF 7FFF" + $"7FFF 7FFF 7FFF FFFE FFFE FFFE FFFE FFFE" +}; + +data 'ics4' (2000, "Tcl Shared Library", purgeable) { + $"FFFF FFFF FFFF FFF0 FCFF DED5 6CCC CCF0" + $"FCFF C0D6 ECCC CCF0 FCFF 2056 65DC CCF0" + $"FDFE D256 6DAC CCFF FFCC DDDE 5DDC CCEF" + $"0FCC CD67 5CCC CCCF 0FCC CC5D 6CCC CCCF" + $"0FCC CC5D 5CCC CCCF 0FCC CCD5 5CCC CCCF" + $"FFCC CFFD CCFF CCFF FCCC CF2D DF20 FCFC" + $"FCCC CCFD D202 FEF0 FCCC CC0D 2020 FEF0" + $"FCCC CCCD FBBB FEF0 FFFF FFFF FFFF FFE0" +}; + diff --git a/mac/tclMacLoad.c b/mac/tclMacLoad.c new file mode 100644 index 0000000..379b450 --- /dev/null +++ b/mac/tclMacLoad.c @@ -0,0 +1,380 @@ +/* + * tclMacLoad.c -- + * + * This procedure provides a version of the TclLoadFile for use + * on the Macintosh. This procedure will only work with systems + * that use the Code Fragment Manager. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacLoad.c,v 1.16 2002/10/09 11:54:26 das Exp $ + */ + +#include <CodeFragments.h> +#include <Errors.h> +#include <Resources.h> +#include <Strings.h> +#include <FSpCompat.h> + +/* + * Seems that the 3.0.1 Universal headers leave this define out. So we + * define it here... + */ + +#ifndef fragNoErr + #define fragNoErr noErr +#endif + +#include "tclPort.h" +#include "tclInt.h" +#include "tclMacInt.h" + +#if GENERATINGPOWERPC + #define OUR_ARCH_TYPE kPowerPCCFragArch +#else + #define OUR_ARCH_TYPE kMotorola68KCFragArch +#endif + +/* + * The following data structure defines the structure of a code fragment + * resource. We can cast the resource to be of this type to access + * any fields we need to see. + */ +struct CfrgHeader { + long res1; + long res2; + long version; + long res3; + long res4; + long filler1; + long filler2; + long itemCount; + char arrayStart; /* Array of externalItems begins here. */ +}; +typedef struct CfrgHeader CfrgHeader, *CfrgHeaderPtr, **CfrgHeaderPtrHand; + +/* + * The below structure defines a cfrag item within the cfrag resource. + */ +struct CfrgItem { + OSType archType; + long updateLevel; + long currVersion; + long oldDefVersion; + long appStackSize; + short appSubFolder; + char usage; + char location; + long codeOffset; + long codeLength; + long res1; + long res2; + short itemSize; + Str255 name; /* This is actually variable sized. */ +}; +typedef struct CfrgItem CfrgItem; + +/* + * On MacOS, old shared libraries which contain many code fragments + * cannot, it seems, be loaded in one go. We need to look provide + * the name of a code fragment while we load. Since with the + * separation of the 'load' and 'findsymbol' be do not necessarily + * know a symbol name at load time, we have to store some further + * information in a structure like this so we can ensure we load + * properly in 'findsymbol' if the first attempts didn't work. + */ +typedef struct TclMacLoadInfo { + int loaded; + CFragConnectionID connID; + FSSpec fileSpec; +} TclMacLoadInfo; + +static int TryToLoad(Tcl_Interp *interp, TclMacLoadInfo *loadInfo, Tcl_Obj *pathPtr, + CONST char *sym /* native */); + + +/* + *---------------------------------------------------------------------- + * + * TclpDlopen -- + * + * This procedure is called to carry out dynamic loading of binary + * code for the Macintosh. This implementation is based on the + * Code Fragment Manager & will not work on other systems. + * + * Results: + * The result is TCL_ERROR, and an error message is left in + * the interp's result. + * + * Side effects: + * New binary code is loaded. + * + *---------------------------------------------------------------------- + */ + +int +TclpDlopen(interp, pathPtr, loadHandle, unloadProcPtr) + Tcl_Interp *interp; /* Used for error reporting. */ + Tcl_Obj *pathPtr; /* Name of the file containing the desired + * code (UTF-8). */ + Tcl_LoadHandle *loadHandle; /* Filled with token for dynamically loaded + * file which will be passed back to + * (*unloadProcPtr)() to unload the file. */ + Tcl_FSUnloadFileProc **unloadProcPtr; + /* Filled with address of Tcl_FSUnloadFileProc + * function which should be used for + * this file. */ +{ + OSErr err; + FSSpec fileSpec; + CONST char *native; + TclMacLoadInfo *loadInfo; + + native = Tcl_FSGetNativePath(pathPtr); + err = FSpLocationFromPath(strlen(native), native, &fileSpec); + + if (err != noErr) { + Tcl_SetResult(interp, "could not locate shared library", TCL_STATIC); + return TCL_ERROR; + } + + loadInfo = (TclMacLoadInfo *) ckalloc(sizeof(TclMacLoadInfo)); + loadInfo->loaded = 0; + loadInfo->fileSpec = fileSpec; + loadInfo->connID = NULL; + + if (TryToLoad(interp, loadInfo, pathPtr, NULL) != TCL_OK) { + ckfree((char*) loadInfo); + return TCL_ERROR; + } + + *loadHandle = (Tcl_LoadHandle)loadInfo; + *unloadProcPtr = &TclpUnloadFile; + return TCL_OK; +} + +/* + * See the comments about 'struct TclMacLoadInfo' above. This + * function ensures the appropriate library or symbol is + * loaded. + */ +static int +TryToLoad(Tcl_Interp *interp, TclMacLoadInfo *loadInfo, Tcl_Obj *pathPtr, + CONST char *sym /* native */) +{ + OSErr err; + CFragConnectionID connID; + Ptr dummy; + short fragFileRef, saveFileRef; + Handle fragResource; + UInt32 offset = 0; + UInt32 length = kCFragGoesToEOF; + Str255 errName; + StringPtr fragName=NULL; + + if (loadInfo->loaded == 1) { + return TCL_OK; + } + + /* + * See if this fragment has a 'cfrg' resource. It will tell us where + * to look for the fragment in the file. If it doesn't exist we will + * assume we have a ppc frag using the whole data fork. If it does + * exist we find the frag that matches the one we are looking for and + * get the offset and size from the resource. + */ + + saveFileRef = CurResFile(); + SetResLoad(false); + fragFileRef = FSpOpenResFile(&loadInfo->fileSpec, fsRdPerm); + SetResLoad(true); + if (fragFileRef != -1) { + if (sym != NULL) { + UseResFile(fragFileRef); + fragResource = Get1Resource(kCFragResourceType, kCFragResourceID); + HLock(fragResource); + if (ResError() == noErr) { + CfrgItem* srcItem; + long itemCount, index; + Ptr itemStart; + + itemCount = (*(CfrgHeaderPtrHand)fragResource)->itemCount; + itemStart = &(*(CfrgHeaderPtrHand)fragResource)->arrayStart; + for (index = 0; index < itemCount; + index++, itemStart += srcItem->itemSize) { + srcItem = (CfrgItem*)itemStart; + if (srcItem->archType != OUR_ARCH_TYPE) continue; + if (!strncasecmp(sym, (char *) srcItem->name + 1, + strlen(sym))) { + offset = srcItem->codeOffset; + length = srcItem->codeLength; + fragName=srcItem->name; + } + } + } + } + /* + * Close the resource file. If the extension wants to reopen the + * resource fork it should use the tclMacLibrary.c file during it's + * construction. + */ + HUnlock(fragResource); + ReleaseResource(fragResource); + CloseResFile(fragFileRef); + UseResFile(saveFileRef); + if (sym == NULL) { + /* We just return */ + return TCL_OK; + } + } + + /* + * Now we can attempt to load the fragment using the offset & length + * obtained from the resource. We don't worry about the main entry point + * as we are going to search for specific entry points passed to us. + */ + + err = GetDiskFragment(&loadInfo->fileSpec, offset, length, fragName, + kLoadCFrag, &connID, &dummy, errName); + + if (err != fragNoErr) { + p2cstr(errName); + if(pathPtr) { + Tcl_AppendResult(interp, "couldn't load file \"", + Tcl_GetString(pathPtr), + "\": ", errName, (char *) NULL); + } else if(sym) { + Tcl_AppendResult(interp, "couldn't load library \"", + sym, + "\": ", errName, (char *) NULL); + } + return TCL_ERROR; + } + + loadInfo->connID = connID; + loadInfo->loaded = 1; + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclpFindSymbol -- + * + * Looks up a symbol, by name, through a handle associated with + * a previously loaded piece of code (shared library). + * + * Results: + * Returns a pointer to the function associated with 'symbol' if + * it is found. Otherwise returns NULL and may leave an error + * message in the interp's result. + * + *---------------------------------------------------------------------- + */ +Tcl_PackageInitProc* +TclpFindSymbol(interp, loadHandle, symbol) + Tcl_Interp *interp; + Tcl_LoadHandle loadHandle; + CONST char *symbol; +{ + Tcl_DString ds; + Tcl_PackageInitProc *proc=NULL; + TclMacLoadInfo *loadInfo = (TclMacLoadInfo *)loadHandle; + Str255 symbolName; + CFragSymbolClass symClass; + OSErr err; + + if (loadInfo->loaded == 0) { + int res; + /* + * First thing we must do is infer the package name from the + * sym variable. We do this by removing the '_Init'. + */ + Tcl_UtfToExternalDString(NULL, symbol, -1, &ds); + Tcl_DStringSetLength(&ds, Tcl_DStringLength(&ds) - 5); + res = TryToLoad(interp, loadInfo, NULL, Tcl_DStringValue(&ds)); + Tcl_DStringFree(&ds); + if (res != TCL_OK) { + return NULL; + } + } + + Tcl_UtfToExternalDString(NULL, symbol, -1, &ds); + strcpy((char *) symbolName + 1, Tcl_DStringValue(&ds)); + symbolName[0] = (unsigned) Tcl_DStringLength(&ds); + err = FindSymbol(loadInfo->connID, symbolName, (Ptr *) &proc, &symClass); + Tcl_DStringFree(&ds); + if (err != fragNoErr || symClass == kDataCFragSymbol) { + Tcl_SetResult(interp, + "could not find Initialization routine in library", + TCL_STATIC); + return NULL; + } + return proc; +} + +/* + *---------------------------------------------------------------------- + * + * TclpUnloadFile -- + * + * Unloads a dynamically loaded binary code file from memory. + * Code pointers in the formerly loaded file are no longer valid + * after calling this function. + * + * Results: + * None. + * + * Side effects: + * Does nothing. Can anything be done? + * + *---------------------------------------------------------------------- + */ + +void +TclpUnloadFile(loadHandle) + Tcl_LoadHandle loadHandle; /* loadHandle returned by a previous call + * to TclpDlopen(). The loadHandle is + * a token that represents the loaded + * file. */ +{ + TclMacLoadInfo *loadInfo = (TclMacLoadInfo *)loadHandle; + if (loadInfo->loaded) { + CloseConnection((CFragConnectionID*) &(loadInfo->connID)); + } + ckfree((char*)loadInfo); +} + +/* + *---------------------------------------------------------------------- + * + * TclGuessPackageName -- + * + * If the "load" command is invoked without providing a package + * name, this procedure is invoked to try to figure it out. + * + * Results: + * Always returns 0 to indicate that we couldn't figure out a + * package name; generic code will then try to guess the package + * from the file name. A return value of 1 would have meant that + * we figured out the package name and put it in bufPtr. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclGuessPackageName( + CONST char *fileName, /* Name of file containing package (already + * translated to local form if needed). */ + Tcl_DString *bufPtr) /* Initialized empty dstring. Append + * package name to this if possible. */ +{ + return 0; +} diff --git a/mac/tclMacMath.h b/mac/tclMacMath.h new file mode 100644 index 0000000..6366dc3 --- /dev/null +++ b/mac/tclMacMath.h @@ -0,0 +1,145 @@ +/* + * tclMacMath.h -- + * + * This file is necessary because of Metrowerks CodeWarrior Pro 1 + * on the Macintosh. With 8-byte doubles turned on, the definitions of + * sin, cos, acos, etc., are screwed up. They are fine as long as + * they are used as function calls, but if the function pointers + * are passed around and used, they will crash hard on the 68K. + * + * Copyright (c) 1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacMath.h,v 1.3 2001/11/23 01:27:50 das Exp $ + */ + +#ifndef _TCLMACMATH +#define _TCLMACMATH + +#include <math.h> + +#if defined(__MWERKS__) && !defined(__POWERPC__) +#if __option(IEEEdoubles) + +# ifdef cos +# undef cos +# define cos cosd +# endif + +# ifdef sin +# undef sin +# define sin sind +# endif + +# ifdef tan +# undef tan +# define tan tand +# endif + +# ifdef acos +# undef acos +# define acos acosd +# endif + +# ifdef asin +# undef asin +# define asin asind +# endif + +# ifdef atan +# undef atan +# define atan atand +# endif + +# ifdef cosh +# undef cosh +# define cosh coshd +# endif + +# ifdef sinh +# undef sinh +# define sinh sinhd +# endif + +# ifdef tanh +# undef tanh +# define tanh tanhd +# endif + +# ifdef exp +# undef exp +# define exp expd +# endif + +# ifdef ldexp +# undef ldexp +# define ldexp ldexpd +# endif + +# ifdef log +# undef log +# define log logd +# endif + +# ifdef log10 +# undef log10 +# define log10 log10d +# endif + +# ifdef fabs +# undef fabs +# define fabs fabsd +# endif + +# ifdef sqrt +# undef sqrt +# define sqrt sqrtd +# endif + +# ifdef fmod +# undef fmod +# define fmod fmodd +# endif + +# ifdef atan2 +# undef atan2 +# define atan2 atan2d +# endif + +# ifdef frexp +# undef frexp +# define frexp frexpd +# endif + +# ifdef modf +# undef modf +# define modf modfd +# endif + +# ifdef pow +# undef pow +# define pow powd +# endif + +# ifdef ceil +# undef ceil +# define ceil ceild +# endif + +# ifdef floor +# undef floor +# define floor floord +# endif +#endif +#endif + +#if (defined(THINK_C)) +#pragma export on +double hypotd(double x, double y); +#define hypot hypotd +#pragma export reset +#endif + +#endif /* _TCLMACMATH */ diff --git a/mac/tclMacNotify.c b/mac/tclMacNotify.c new file mode 100644 index 0000000..f2704be --- /dev/null +++ b/mac/tclMacNotify.c @@ -0,0 +1,581 @@ +/* + * tclMacNotify.c -- + * + * This file contains Macintosh-specific procedures for the notifier, + * which is the lowest-level part of the Tcl event loop. This file + * works together with ../generic/tclNotify.c. + * + * The Mac notifier only polls for system and OS events, so it is process + * wide, rather than thread specific. However, this means that the convert + * event proc will have to arbitrate which events go to which threads. + * + * Copyright (c) 1995-1996 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacNotify.c,v 1.9 2003/03/21 03:23:24 dgp Exp $ + */ + +#include "tclInt.h" +#include "tclPort.h" +#include "tclMac.h" +#include "tclMacInt.h" +#include <signal.h> +#include <Events.h> +#include <LowMem.h> +#include <Processes.h> +#include <Timer.h> +#include <Threads.h> + + +/* + * This is necessary to work around a bug in Apple's Universal header files + * for the CFM68K libraries. + */ + +#ifdef __CFM68K__ +#undef GetEventQueue +extern pascal QHdrPtr GetEventQueue(void) + THREEWORDINLINE(0x2EBC, 0x0000, 0x014A); +#pragma import list GetEventQueue +#define GetEvQHdr() GetEventQueue() +#endif + +/* + * Need this for replacing Tcl_SetTimer and Tcl_WaitForEvent defined + * in THIS file with ones defined in the stub table. + */ + +extern TclStubs tclStubs; +extern Tcl_NotifierProcs tclOriginalNotifier; + +/* + * The follwing static indicates whether this module has been initialized. + */ + +static int initialized = 0; + +/* + * The following structure contains the state information for the + * notifier module. + */ + +static struct { + int timerActive; /* 1 if timer is running. */ + Tcl_Time timer; /* Time when next timer event is expected. */ + int flags; /* OR'ed set of flags defined below. */ + Point lastMousePosition; /* Last known mouse location. */ + RgnHandle utilityRgn; /* Region used as the mouse region for + * WaitNextEvent and the update region when + * checking for events. */ + Tcl_MacConvertEventPtr eventProcPtr; + /* This pointer holds the address of the + * function that will handle all incoming + * Macintosh events. */ +} notifier; + +/* + * The following defines are used in the flags field of the notifier struct. + */ + +#define NOTIFY_IDLE (1<<1) /* Tcl_ServiceIdle should be called. */ +#define NOTIFY_TIMER (1<<2) /* Tcl_ServiceTimer should be called. */ + +/* + * Prototypes for procedures that are referenced only in this file: + */ + +static int HandleMacEvents _ANSI_ARGS_((void)); +static void InitNotifier _ANSI_ARGS_((void)); +static void NotifierExitHandler _ANSI_ARGS_(( + ClientData clientData)); + +/* + *---------------------------------------------------------------------- + * + * Tcl_InitNotifier -- + * + * Initializes the platform specific notifier state. There is no thread + * specific platform notifier on the Mac, so this really doesn't do + * anything. However, we need to return the ThreadID, since the generic + * notifier hands this back to us in AlertThread. + * + * Results: + * Returns the threadID for this thread. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +ClientData +Tcl_InitNotifier() +{ + +#ifdef TCL_THREADS + ThreadID curThread; + if (TclMacHaveThreads()) { + GetCurrentThread(&curThread); + return (ClientData) curThread; + } else { + return NULL; + } +#else + return NULL; +#endif + +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_FinalizeNotifier -- + * + * This function is called to cleanup the notifier state before + * a thread is terminated. There is no platform thread specific + * notifier, so this does nothing. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_FinalizeNotifier(clientData) + ClientData clientData; /* Pointer to notifier data. */ +{ + /* Nothing to do on the Mac */ +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AlertNotifier -- + * + * Wake up the specified notifier from any thread. This routine + * is called by the platform independent notifier code whenever + * the Tcl_ThreadAlert routine is called. This routine is + * guaranteed not to be called on a given notifier after + * Tcl_FinalizeNotifier is called for that notifier. + * + * Results: + * None. + * + * Side effects: + * Calls YieldToThread from this thread. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_AlertNotifier(clientData) + ClientData clientData; /* Pointer to thread data. */ +{ + +#ifdef TCL_THREADS + if (TclMacHaveThreads()) { + YieldToThread((ThreadID) clientData); + } +#endif + +} + +/* + *---------------------------------------------------------------------- + * + * InitNotifier -- + * + * Initializes the notifier structure. Note - this function is never + * used. + * + * Results: + * None. + * + * Side effects: + * Creates a new exit handler. + * + *---------------------------------------------------------------------- + */ + +static void +InitNotifier(void) +{ + initialized = 1; + memset(¬ifier, 0, sizeof(notifier)); + Tcl_CreateExitHandler(NotifierExitHandler, NULL); +} + +/* + *---------------------------------------------------------------------- + * + * NotifierExitHandler -- + * + * This function is called to cleanup the notifier state before + * Tcl is unloaded. This function is never used, since InitNotifier + * isn't either. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +NotifierExitHandler( + ClientData clientData) /* Not used. */ +{ + initialized = 0; +} + +/* + *---------------------------------------------------------------------- + * + * HandleMacEvents -- + * + * This function checks for events from the Macintosh event queue. + * + * Results: + * Returns 1 if event found, 0 otherwise. + * + * Side effects: + * Pulls events off of the Mac event queue and then calls + * convertEventProc. + * + *---------------------------------------------------------------------- + */ + +static int +HandleMacEvents(void) +{ + EventRecord theEvent; + int eventFound = 0, needsUpdate = 0; + Point currentMouse; + WindowRef windowRef; + Rect mouseRect; + + /* + * Check for mouse moved events. These events aren't placed on the + * system event queue unless we call WaitNextEvent. + */ + + GetGlobalMouseTcl(¤tMouse); + if ((notifier.eventProcPtr != NULL) && + !EqualPt(currentMouse, notifier.lastMousePosition)) { + notifier.lastMousePosition = currentMouse; + theEvent.what = nullEvent; + if ((*notifier.eventProcPtr)(&theEvent) == true) { + eventFound = 1; + } + } + + /* + * Check for update events. Since update events aren't generated + * until we call GetNextEvent, we may need to force a call to + * GetNextEvent, even if the queue is empty. + */ + + for (windowRef = FrontWindow(); windowRef != NULL; + windowRef = GetNextWindow(windowRef)) { + GetWindowUpdateRgn(windowRef, notifier.utilityRgn); + if (!EmptyRgn(notifier.utilityRgn)) { + needsUpdate = 1; + break; + } + } + + /* + * Process events from the OS event queue. + */ + + while (needsUpdate || (GetEvQHdr()->qHead != NULL)) { + GetGlobalMouseTcl(¤tMouse); + SetRect(&mouseRect, currentMouse.h, currentMouse.v, + currentMouse.h + 1, currentMouse.v + 1); + RectRgn(notifier.utilityRgn, &mouseRect); + + WaitNextEvent(everyEvent, &theEvent, 5, notifier.utilityRgn); + needsUpdate = 0; + if ((notifier.eventProcPtr != NULL) + && ((*notifier.eventProcPtr)(&theEvent) == true)) { + eventFound = 1; + } + } + + return eventFound; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_SetTimer -- + * + * This procedure sets the current notifier timer value. The + * notifier will ensure that Tcl_ServiceAll() is called after + * the specified interval, even if no events have occurred. + * + * Results: + * None. + * + * Side effects: + * Replaces any previous timer. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_SetTimer( + Tcl_Time *timePtr) /* New value for interval timer. */ +{ + /* + * Allow the notifier to be hooked. This may not make sense + * on the Mac, but mirrors the UNIX hook. + */ + + if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) { + tclStubs.tcl_SetTimer(timePtr); + return; + } + + if (!timePtr) { + notifier.timerActive = 0; + } else { + /* + * Compute when the timer should fire. + */ + + Tcl_GetTime(¬ifier.timer); + notifier.timer.sec += timePtr->sec; + notifier.timer.usec += timePtr->usec; + if (notifier.timer.usec >= 1000000) { + notifier.timer.usec -= 1000000; + notifier.timer.sec += 1; + } + notifier.timerActive = 1; + } +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_ServiceModeHook -- + * + * This function is invoked whenever the service mode changes. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_ServiceModeHook(mode) + int mode; /* Either TCL_SERVICE_ALL, or + * TCL_SERVICE_NONE. */ +{ +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_WaitForEvent -- + * + * This function is called by Tcl_DoOneEvent to wait for new + * events on the message queue. If the block time is 0, then + * Tcl_WaitForEvent just polls the event queue without blocking. + * + * Results: + * Always returns 0. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_WaitForEvent( + Tcl_Time *timePtr) /* Maximum block time. */ +{ + int found; + EventRecord macEvent; + long sleepTime = 5; + long ms; + Point currentMouse; + void * timerToken; + Rect mouseRect; + + /* + * Allow the notifier to be hooked. This may not make + * sense on the Mac, but mirrors the UNIX hook. + */ + + if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) { + return tclStubs.tcl_WaitForEvent(timePtr); + } + + /* + * Compute the next timeout value. + */ + + if (!timePtr) { + ms = INT_MAX; + } else { + ms = (timePtr->sec * 1000) + (timePtr->usec / 1000); + } + timerToken = TclMacStartTimer((long) ms); + + /* + * Poll the Mac event sources. This loop repeats until something + * happens: a timeout, a socket event, mouse motion, or some other + * window event. Note that we don't call WaitNextEvent if another + * event is found to avoid context switches. This effectively gives + * events coming in via WaitNextEvent a slightly lower priority. + */ + + found = 0; + if (notifier.utilityRgn == NULL) { + notifier.utilityRgn = NewRgn(); + } + + while (!found) { + /* + * Check for generated and queued events. + */ + + if (HandleMacEvents()) { + found = 1; + } + + /* + * Check for time out. + */ + + if (!found && TclMacTimerExpired(timerToken)) { + found = 1; + } + + /* + * Check for window events. We may receive a NULL event for + * various reasons. 1) the timer has expired, 2) a mouse moved + * event is occuring or 3) the os is giving us time for idle + * events. Note that we aren't sharing the processor very + * well here. We really ought to do a better job of calling + * WaitNextEvent for time slicing purposes. + */ + + if (!found) { + /* + * Set up mouse region so we will wake if the mouse is moved. + * We do this by defining the smallest possible region around + * the current mouse position. + */ + + GetGlobalMouseTcl(¤tMouse); + SetRect(&mouseRect, currentMouse.h, currentMouse.v, + currentMouse.h + 1, currentMouse.v + 1); + RectRgn(notifier.utilityRgn, &mouseRect); + + WaitNextEvent(everyEvent, &macEvent, sleepTime, + notifier.utilityRgn); + + if (notifier.eventProcPtr != NULL) { + if ((*notifier.eventProcPtr)(&macEvent) == true) { + found = 1; + } + } + } + } + TclMacRemoveTimer(timerToken); + + /* + * Yield time to nay other thread at this point. If we find that the + * apps thrash too switching between threads, we can put a timer here, + * and only yield when the timer fires. + */ + + if (TclMacHaveThreads()) { + YieldToAnyThread(); + } + + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_Sleep -- + * + * Delay execution for the specified number of milliseconds. This + * is not a very good call to make. It will block the system - + * you will not even be able to switch applications. + * + * Results: + * None. + * + * Side effects: + * Time passes. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_Sleep( + int ms) /* Number of milliseconds to sleep. */ +{ + EventRecord dummy; + void *timerToken; + + if (ms <= 0) { + return; + } + + timerToken = TclMacStartTimer((long) ms); + while (1) { + WaitNextEvent(0, &dummy, (ms / 16.66) + 1, NULL); + if (TclMacHaveThreads()) { + YieldToAnyThread(); + } + if (TclMacTimerExpired(timerToken)) { + break; + } + } + TclMacRemoveTimer(timerToken); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_MacSetEventProc -- + * + * This function sets the event handling procedure for the + * application. This function will be passed all incoming Mac + * events. This function usually controls the console or some + * other entity like Tk. + * + * Results: + * None. + * + * Side effects: + * Changes the event handling function. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_MacSetEventProc( + Tcl_MacConvertEventPtr procPtr) +{ + notifier.eventProcPtr = procPtr; +} diff --git a/mac/tclMacOSA.c b/mac/tclMacOSA.c new file mode 100644 index 0000000..fdcd56e --- /dev/null +++ b/mac/tclMacOSA.c @@ -0,0 +1,2958 @@ +/* + * tclMacOSA.c -- + * + * This contains the initialization routines, and the implementation of + * the OSA and Component commands. These commands allow you to connect + * with the AppleScript or any other OSA component to compile and execute + * scripts. + * + * Copyright (c) 1996 Lucent Technologies and Jim Ingham + * Copyright (c) 1997 Sun Microsystems, Inc. + * + * See the file "License Terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacOSA.c,v 1.11 2003/12/24 04:18:21 davygrvy Exp $ + */ + +#define MAC_TCL + +#include <Aliases.h> +#include <string.h> +#include <AppleEvents.h> +#include <AppleScript.h> +#include <OSA.h> +#include <OSAGeneric.h> +#include <Script.h> + +#include <FullPath.h> +#include <components.h> + +#include <resources.h> +#include <FSpCompat.h> +/* + * The following two Includes are from the More Files package. + */ +#include <MoreFiles.h> +#include <FullPath.h> + +#include "tcl.h" +#include "tclInt.h" + +/* + * I need this only for the call to FspGetFullPath, + * I'm really not poking my nose where it does not belong! + */ +#include "tclMacInt.h" + +/* + * Data structures used by the OSA code. + */ +typedef struct tclOSAScript { + OSAID scriptID; + OSType languageID; + long modeFlags; +} tclOSAScript; + +typedef struct tclOSAContext { + OSAID contextID; +} tclOSAContext; + +typedef struct tclOSAComponent { + char *theName; + ComponentInstance theComponent; /* The OSA Component represented */ + long componentFlags; + OSType languageID; + char *languageName; + Tcl_HashTable contextTable; /* Hash Table linking the context names & ID's */ + Tcl_HashTable scriptTable; + Tcl_Interp *theInterp; + OSAActiveUPP defActiveProc; + long defRefCon; +} tclOSAComponent; + +/* + * Prototypes for static procedures. + */ + +static pascal OSErr TclOSAActiveProc _ANSI_ARGS_((long refCon)); +static int TclOSACompileCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + CONST char **argv)); +static int tclOSADecompileCmd _ANSI_ARGS_((Tcl_Interp * Interp, + tclOSAComponent *OSAComponent, int argc, + CONST char **argv)); +static int tclOSADeleteCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + CONST char **argv)); +static int tclOSAExecuteCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + CONST char **argv)); +static int tclOSAInfoCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + CONST char **argv)); +static int tclOSALoadCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + CONST char **argv)); +static int tclOSARunCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + CONST char **argv)); +static int tclOSAStoreCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + CONST char **argv)); +static void GetRawDataFromDescriptor _ANSI_ARGS_((AEDesc *theDesc, + Ptr destPtr, Size destMaxSize, Size *actSize)); +static OSErr GetCStringFromDescriptor _ANSI_ARGS_(( + AEDesc *sourceDesc, char *resultStr, + Size resultMaxSize,Size *resultSize)); +static int Tcl_OSAComponentCmd _ANSI_ARGS_((ClientData clientData, + Tcl_Interp *interp, int argc, CONST char **argv)); +static void getSortedHashKeys _ANSI_ARGS_((Tcl_HashTable *theTable, + CONST char *pattern, Tcl_DString *theResult)); +static int ASCIICompareProc _ANSI_ARGS_((const void *first, + const void *second)); +static int Tcl_OSACmd _ANSI_ARGS_((ClientData clientData, + Tcl_Interp *interp, int argc, CONST char **argv)); +static void tclOSAClose _ANSI_ARGS_((ClientData clientData)); +/*static void tclOSACloseAll _ANSI_ARGS_((ClientData clientData));*/ +static tclOSAComponent *tclOSAMakeNewComponent _ANSI_ARGS_((Tcl_Interp *interp, + char *cmdName, char *languageName, + OSType scriptSubtype, long componentFlags)); +static int prepareScriptData _ANSI_ARGS_((int argc, CONST char **argv, + Tcl_DString *scrptData ,AEDesc *scrptDesc)); +static void tclOSAResultFromID _ANSI_ARGS_((Tcl_Interp *interp, + ComponentInstance theComponent, OSAID resultID)); +static void tclOSAASError _ANSI_ARGS_((Tcl_Interp * interp, + ComponentInstance theComponent, char *scriptSource)); +static int tclOSAGetContextID _ANSI_ARGS_((tclOSAComponent *theComponent, + CONST char *contextName, OSAID *theContext)); +static void tclOSAAddContext _ANSI_ARGS_((tclOSAComponent *theComponent, + char *contextName, const OSAID theContext)); +static int tclOSAMakeContext _ANSI_ARGS_((tclOSAComponent *theComponent, + CONST char *contextName, OSAID *theContext)); +static int tclOSADeleteContext _ANSI_ARGS_((tclOSAComponent *theComponent, + CONST char *contextName)); +static int tclOSALoad _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *theComponent, CONST char *resourceName, + int resourceNumber, CONST char *fileName,OSAID *resultID)); +static int tclOSAStore _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *theComponent, CONST char *resourceName, + int resourceNumber, CONST char *scriptName, CONST char *fileName)); +static int tclOSAAddScript _ANSI_ARGS_((tclOSAComponent *theComponent, + char *scriptName, long modeFlags, OSAID scriptID)); +static int tclOSAGetScriptID _ANSI_ARGS_((tclOSAComponent *theComponent, + CONST char *scriptName, OSAID *scriptID)); +static tclOSAScript * tclOSAGetScript _ANSI_ARGS_((tclOSAComponent *theComponent, + CONST char *scriptName)); +static int tclOSADeleteScript _ANSI_ARGS_((tclOSAComponent *theComponent, + CONST char *scriptName,char *errMsg)); + +/* + * "export" is a MetroWerks specific pragma. It flags the linker that + * any symbols that are defined when this pragma is on will be exported + * to shared libraries that link with this library. + */ + + +#pragma export on +int Tclapplescript_Init( Tcl_Interp *interp ); +#pragma export reset + +/* + *---------------------------------------------------------------------- + * + * Tclapplescript_Init -- + * + * Initializes the the OSA command which opens connections to + * OSA components, creates the AppleScript command, which opens an + * instance of the AppleScript component,and constructs the table of + * available languages. + * + * Results: + * A standard Tcl result. + * + * Side Effects: + * Opens one connection to the AppleScript component, if + * available. Also builds up a table of available OSA languages, + * and creates the OSA command. + * + *---------------------------------------------------------------------- + */ + +int +Tclapplescript_Init( + Tcl_Interp *interp) /* Tcl interpreter. */ +{ + char *errMsg = NULL; + OSErr myErr = noErr; + Boolean gotAppleScript = false; + Boolean GotOneOSALanguage = false; + ComponentDescription compDescr = { + kOSAComponentType, + (OSType) 0, + (OSType) 0, + (long) 0, + (long) 0 + }, *foundComp; + Component curComponent = (Component) 0; + ComponentInstance curOpenComponent; + Tcl_HashTable *ComponentTable; + Tcl_HashTable *LanguagesTable; + Tcl_HashEntry *hashEntry; + int newPtr; + AEDesc componentName = { typeNull, NULL }; + char nameStr[32]; + Size nameLen; + long appleScriptFlags; + + /* + * Perform the required stubs magic... + */ + + if (!Tcl_InitStubs(interp, "8.2", 0)) { + return TCL_ERROR; + } + + /* + * Here We Will Get The Available Osa Languages, Since They Can Only Be + * Registered At Startup... If You Dynamically Load Components, This + * Will Fail, But This Is Not A Common Thing To Do. + */ + + LanguagesTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); + + if (LanguagesTable == NULL) { + Tcl_Panic("Memory Error Allocating Languages Hash Table"); + } + + Tcl_SetAssocData(interp, "OSAScript_LangTable", NULL, LanguagesTable); + Tcl_InitHashTable(LanguagesTable, TCL_STRING_KEYS); + + + while ((curComponent = FindNextComponent(curComponent, &compDescr)) != 0) { + int nbytes = sizeof(ComponentDescription); + foundComp = (ComponentDescription *) + ckalloc(sizeof(ComponentDescription)); + myErr = GetComponentInfo(curComponent, foundComp, NULL, NULL, NULL); + if (foundComp->componentSubType == + kOSAGenericScriptingComponentSubtype) { + /* Skip the generic component */ + ckfree((char *) foundComp); + } else { + GotOneOSALanguage = true; + + /* + * This is gross: looks like I have to open the component just + * to get its name!!! GetComponentInfo is supposed to return + * the name, but AppleScript always returns an empty string. + */ + + curOpenComponent = OpenComponent(curComponent); + if (curOpenComponent == NULL) { + Tcl_AppendResult(interp,"Error opening component", + (char *) NULL); + return TCL_ERROR; + } + + myErr = OSAScriptingComponentName(curOpenComponent,&componentName); + if (myErr == noErr) { + myErr = GetCStringFromDescriptor(&componentName, + nameStr, 31, &nameLen); + AEDisposeDesc(&componentName); + } + CloseComponent(curOpenComponent); + + if (myErr == noErr) { + hashEntry = Tcl_CreateHashEntry(LanguagesTable, + nameStr, &newPtr); + Tcl_SetHashValue(hashEntry, (ClientData) foundComp); + } else { + Tcl_AppendResult(interp,"Error getting componentName.", + (char *) NULL); + return TCL_ERROR; + } + + /* + * Make sure AppleScript is loaded, otherwise we will + * not bother to make the AppleScript command. + */ + if (foundComp->componentSubType == kAppleScriptSubtype) { + appleScriptFlags = foundComp->componentFlags; + gotAppleScript = true; + } + } + } + + /* + * Create the OSA command. + */ + + if (!GotOneOSALanguage) { + Tcl_AppendResult(interp,"Could not find any OSA languages", + (char *) NULL); + return TCL_ERROR; + } + + /* + * Create the Component Assoc Data & put it in the interpreter. + */ + + ComponentTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); + + if (ComponentTable == NULL) { + Tcl_Panic("Memory Error Allocating Hash Table"); + } + + Tcl_SetAssocData(interp, "OSAScript_CompTable", NULL, ComponentTable); + + Tcl_InitHashTable(ComponentTable, TCL_STRING_KEYS); + + /* + * The OSA command is not currently supported. + Tcl_CreateCommand(interp, "OSA", Tcl_OSACmd, (ClientData) NULL, + (Tcl_CmdDeleteProc *) NULL); + */ + + /* + * Open up one AppleScript component, with a default context + * and tie it to the AppleScript command. + * If the user just wants single-threaded AppleScript execution + * this should be enough. + * + */ + + if (gotAppleScript) { + if (tclOSAMakeNewComponent(interp, "AppleScript", + "AppleScript English", kAppleScriptSubtype, + appleScriptFlags) == NULL ) { + return TCL_ERROR; + } + } + + return Tcl_PkgProvide(interp, "OSAConnect", "1.0"); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_OSACmd -- + * + * This is the command that provides the interface to the OSA + * component manager. The subcommands are: close: close a component, + * info: get info on components open, and open: get a new connection + * with the Scripting Component + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Depends on the subcommand, see the user documentation + * for more details. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_OSACmd( + ClientData clientData, + Tcl_Interp *interp, + int argc, + CONST char **argv) +{ + static unsigned short componentCmdIndex = 0; + char autoName[32]; + char c; + int length; + Tcl_HashTable *ComponentTable = NULL; + + + if (argc == 1) { + Tcl_AppendResult(interp, "Wrong # of arguments, should be \"", + argv[0], " option\"", (char *) NULL); + return TCL_ERROR; + } + + c = *argv[1]; + length = strlen(argv[1]); + + /* + * Query out the Component Table, since most of these commands use it... + */ + + ComponentTable = (Tcl_HashTable *) Tcl_GetAssocData(interp, + "OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL); + + if (ComponentTable == NULL) { + Tcl_AppendResult(interp, "Error, could not get the Component Table", + " from the Associated data.", (char *) NULL); + return TCL_ERROR; + } + + if (c == 'c' && strncmp(argv[1],"close",length) == 0) { + Tcl_HashEntry *hashEntry; + if (argc != 3) { + Tcl_AppendResult(interp, "Wrong # of arguments, should be \"", + argv[0], " ",argv[1], " componentName\"", + (char *) NULL); + return TCL_ERROR; + } + + if ((hashEntry = Tcl_FindHashEntry(ComponentTable,argv[2])) == NULL) { + Tcl_AppendResult(interp, "Component \"", argv[2], "\" not found", + (char *) NULL); + return TCL_ERROR; + } else { + Tcl_DeleteCommand(interp,argv[2]); + return TCL_OK; + } + } else if (c == 'o' && strncmp(argv[1],"open",length) == 0) { + /* + * Default language is AppleScript. + */ + OSType scriptSubtype = kAppleScriptSubtype; + char *languageName = "AppleScript English"; + char *errMsg = NULL; + ComponentDescription *theCD; + + argv += 2; + argc -= 2; + + while (argc > 0 ) { + if (*argv[0] == '-') { + c = *(argv[0] + 1); + if (c == 'l' && strcmp(argv[0] + 1, "language") == 0) { + if (argc == 1) { + Tcl_AppendResult(interp, + "Error - no language provided for the -language switch", + (char *) NULL); + return TCL_ERROR; + } else { + Tcl_HashEntry *hashEntry; + Tcl_HashSearch search; + Boolean gotIt = false; + Tcl_HashTable *LanguagesTable; + + /* + * Look up the language in the languages table + * Do a simple strstr match, so AppleScript + * will match "AppleScript English"... + */ + + LanguagesTable = Tcl_GetAssocData(interp, + "OSAScript_LangTable", + (Tcl_InterpDeleteProc **) NULL); + + for (hashEntry = + Tcl_FirstHashEntry(LanguagesTable, &search); + hashEntry != NULL; + hashEntry = Tcl_NextHashEntry(&search)) { + languageName = Tcl_GetHashKey(LanguagesTable, + hashEntry); + if (strstr(languageName,argv[1]) != NULL) { + theCD = (ComponentDescription *) + Tcl_GetHashValue(hashEntry); + gotIt = true; + break; + } + } + if (!gotIt) { + Tcl_AppendResult(interp, + "Error, could not find the language \"", + argv[1], + "\" in the list of known languages.", + (char *) NULL); + return TCL_ERROR; + } + } + } + argc -= 2; + argv += 2; + } else { + Tcl_AppendResult(interp, "Expected a flag, but got ", + argv[0], (char *) NULL); + return TCL_ERROR; + } + } + + sprintf(autoName, "OSAComponent%-d", componentCmdIndex++); + if (tclOSAMakeNewComponent(interp, autoName, languageName, + theCD->componentSubType, theCD->componentFlags) == NULL ) { + return TCL_ERROR; + } else { + Tcl_SetResult(interp,autoName,TCL_VOLATILE); + return TCL_OK; + } + + } else if (c == 'i' && strncmp(argv[1],"info",length) == 0) { + if (argc == 2) { + Tcl_AppendResult(interp, "Wrong # of arguments, should be \"", + argv[0], " ", argv[1], " what\"", + (char *) NULL); + return TCL_ERROR; + } + + c = *argv[2]; + length = strlen(argv[2]); + + if (c == 'c' && strncmp(argv[2], "components", length) == 0) { + Tcl_DString theResult; + + Tcl_DStringInit(&theResult); + + if (argc == 3) { + getSortedHashKeys(ComponentTable,(char *) NULL, &theResult); + } else if (argc == 4) { + getSortedHashKeys(ComponentTable, argv[3], &theResult); + } else { + Tcl_AppendResult(interp, "Error: wrong # of arguments", + ", should be \"", argv[0], " ", argv[1], " ", + argv[2], " ?pattern?\".", (char *) NULL); + return TCL_ERROR; + } + Tcl_DStringResult(interp, &theResult); + return TCL_OK; + } else if (c == 'l' && strncmp(argv[2],"languages",length) == 0) { + Tcl_DString theResult; + Tcl_HashTable *LanguagesTable; + + Tcl_DStringInit(&theResult); + LanguagesTable = Tcl_GetAssocData(interp, + "OSAScript_LangTable", (Tcl_InterpDeleteProc **) NULL); + + if (argc == 3) { + getSortedHashKeys(LanguagesTable, (char *) NULL, &theResult); + } else if (argc == 4) { + getSortedHashKeys(LanguagesTable, argv[3], &theResult); + } else { + Tcl_AppendResult(interp, "Error: wrong # of arguments", + ", should be \"", argv[0], " ", argv[1], " ", + argv[2], " ?pattern?\".", (char *) NULL); + return TCL_ERROR; + } + Tcl_DStringResult(interp,&theResult); + return TCL_OK; + } else { + Tcl_AppendResult(interp, "Unknown option: ", argv[2], + " for OSA info, should be one of", + " \"components\" or \"languages\"", + (char *) NULL); + return TCL_ERROR; + } + } else { + Tcl_AppendResult(interp, "Unknown option: ", argv[1], + ", should be one of \"open\", \"close\" or \"info\".", + (char *) NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_OSAComponentCmd -- + * + * This is the command that provides the interface with an OSA + * component. The sub commands are: + * - compile ? -context context? scriptData + * compiles the script data, returns the ScriptID + * - decompile ? -context context? scriptData + * decompiles the script data, source code + * - execute ?-context context? scriptData + * compiles and runs script data + * - info what: get component info + * - load ?-flags values? fileName + * loads & compiles script data from fileName + * - run scriptId ?options? + * executes the compiled script + * + * Results: + * A standard Tcl result + * + * Side Effects: + * Depends on the subcommand, see the user documentation + * for more details. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_OSAComponentCmd( + ClientData clientData, + Tcl_Interp *interp, + int argc, + CONST char **argv) +{ + int length; + char c; + + tclOSAComponent *OSAComponent = (tclOSAComponent *) clientData; + + if (argc == 1) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + argv[0], " option ?arg ...?\"", + (char *) NULL); + return TCL_ERROR; + } + + c = *argv[1]; + length = strlen(argv[1]); + if (c == 'c' && strncmp(argv[1], "compile", length) == 0) { + return TclOSACompileCmd(interp, OSAComponent, argc, argv); + } else if (c == 'l' && strncmp(argv[1], "load", length) == 0) { + return tclOSALoadCmd(interp, OSAComponent, argc, argv); + } else if (c == 'e' && strncmp(argv[1], "execute", length) == 0) { + return tclOSAExecuteCmd(interp, OSAComponent, argc, argv); + } else if (c == 'i' && strncmp(argv[1], "info", length) == 0) { + return tclOSAInfoCmd(interp, OSAComponent, argc, argv); + } else if (c == 'd' && strncmp(argv[1], "decompile", length) == 0) { + return tclOSADecompileCmd(interp, OSAComponent, argc, argv); + } else if (c == 'd' && strncmp(argv[1], "delete", length) == 0) { + return tclOSADeleteCmd(interp, OSAComponent, argc, argv); + } else if (c == 'r' && strncmp(argv[1], "run", length) == 0) { + return tclOSARunCmd(interp, OSAComponent, argc, argv); + } else if (c == 's' && strncmp(argv[1], "store", length) == 0) { + return tclOSAStoreCmd(interp, OSAComponent, argc, argv); + } else { + Tcl_AppendResult(interp,"bad option \"", argv[1], + "\": should be compile, decompile, delete, ", + "execute, info, load, run or store", + (char *) NULL); + return TCL_ERROR; + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclOSACompileCmd -- + * + * This is the compile subcommand for the component command. + * + * Results: + * A standard Tcl result + * + * Side Effects: + * Compiles the script data either into a script or a script + * context. Adds the script to the component's script or context + * table. Sets interp's result to the name of the new script or + * context. + * + *---------------------------------------------------------------------- + */ + +static int +TclOSACompileCmd( + Tcl_Interp *interp, + tclOSAComponent *OSAComponent, + int argc, + CONST char **argv) +{ + int tclError = TCL_OK; + int augment = 1; + int makeContext = 0; + char c; + char autoName[16]; + char buffer[32]; + char *resultName; + Boolean makeNewContext = false; + Tcl_DString scrptData; + AEDesc scrptDesc = { typeNull, NULL }; + long modeFlags = kOSAModeCanInteract; + OSAID resultID = kOSANullScript; + OSAID contextID = kOSANullScript; + OSAID parentID = kOSANullScript; + OSAError osaErr = noErr; + + if (!(OSAComponent->componentFlags && kOSASupportsCompiling)) { + Tcl_AppendResult(interp, + "OSA component does not support compiling", + (char *) NULL); + return TCL_ERROR; + } + + /* + * This signals that we should make up a name, which is the + * default behavior: + */ + + autoName[0] = '\0'; + resultName = NULL; + + if (argc == 2) { + numArgs: + Tcl_AppendResult(interp, + "wrong # args: should be \"", argv[0], " ", argv[1], + " ?options? code\"",(char *) NULL); + return TCL_ERROR; + } + + argv += 2; + argc -= 2; + + /* + * Do the argument parsing. + */ + + while (argc > 0) { + + if (*argv[0] == '-') { + c = *(argv[0] + 1); + + /* + * "--" is the only switch that has no value, stops processing + */ + + if (c == '-' && *(argv[0] + 2) == '\0') { + argv += 1; + argc--; + break; + } + + /* + * So we can check here a switch with no value. + */ + + if (argc == 1) { + Tcl_AppendResult(interp, + "no value given for switch: ", + argv[0], (char *) NULL); + return TCL_ERROR; + } + + if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) { + if (Tcl_GetBoolean(interp, argv[1], &makeContext) != TCL_OK) { + return TCL_ERROR; + } + } else if (c == 'a' && strcmp(argv[0] + 1, "augment") == 0) { + /* + * Augment the current context which implies making a context. + */ + + if (Tcl_GetBoolean(interp, argv[1], &augment) != TCL_OK) { + return TCL_ERROR; + } + makeContext = 1; + } else if (c == 'n' && strcmp(argv[0] + 1, "name") == 0) { + strncpy(autoName, argv[1], 15); + autoName[15] = '\0'; + resultName = autoName; + } else if (c == 'p' && strcmp(argv[0] + 1,"parent") == 0) { + /* + * Since this implies we are compiling into a context, + * set makeContext here + */ + if (tclOSAGetContextID(OSAComponent, + argv[1], &parentID) != TCL_OK) { + Tcl_AppendResult(interp, "context not found \"", + argv[1], "\"", (char *) NULL); + return TCL_ERROR; + } + makeContext = 1; + } else { + Tcl_AppendResult(interp, "bad option \"", argv[0], + "\": should be -augment, -context, -name or -parent", + (char *) NULL); + return TCL_ERROR; + } + argv += 2; + argc -= 2; + + } else { + break; + } + } + + /* + * Make sure we have some data left... + */ + if (argc == 0) { + goto numArgs; + } + + /* + * Now if we are making a context, see if it is a new one... + * There are three options here: + * 1) There was no name provided, so we autoName it + * 2) There was a name, then check and see if it already exists + * a) If yes, then makeNewContext is false + * b) Otherwise we are making a new context + */ + + if (makeContext) { + modeFlags |= kOSAModeCompileIntoContext; + if (resultName == NULL) { + /* + * Auto name the new context. + */ + resultName = autoName; + resultID = kOSANullScript; + makeNewContext = true; + } else if (tclOSAGetContextID(OSAComponent, + resultName, &resultID) == TCL_OK) { + } else { + makeNewContext = true; + } + + /* + * Deal with the augment now... + */ + if (augment && !makeNewContext) { + modeFlags |= kOSAModeAugmentContext; + } + } else if (resultName == NULL) { + resultName = autoName; /* Auto name the script */ + } + + /* + * Ok, now we have the options, so we can compile the script data. + */ + + if (prepareScriptData(argc, argv, &scrptData, &scrptDesc) == TCL_ERROR) { + Tcl_DStringResult(interp, &scrptData); + AEDisposeDesc(&scrptDesc); + return TCL_ERROR; + } + + /* + * If we want to use a parent context, we have to make the context + * by hand. Note, parentID is only specified when you make a new context. + */ + + if (parentID != kOSANullScript && makeNewContext) { + AEDesc contextDesc = { typeNull, NULL }; + + osaErr = OSAMakeContext(OSAComponent->theComponent, + &contextDesc, parentID, &resultID); + modeFlags |= kOSAModeAugmentContext; + } + + osaErr = OSACompile(OSAComponent->theComponent, &scrptDesc, + modeFlags, &resultID); + if (osaErr == noErr) { + + if (makeContext) { + /* + * For the compiled context to be active, you need to run + * the code that is in the context. + */ + OSAID activateID; + + osaErr = OSAExecute(OSAComponent->theComponent, resultID, + resultID, kOSAModeCanInteract, &activateID); + OSADispose(OSAComponent->theComponent, activateID); + + if (osaErr == noErr) { + if (makeNewContext) { + /* + * If we have compiled into a context, + * this is added to the context table + */ + + tclOSAAddContext(OSAComponent, resultName, resultID); + } + + Tcl_SetResult(interp, resultName, TCL_VOLATILE); + tclError = TCL_OK; + } + } else { + /* + * For a script, we return the script name. + */ + tclOSAAddScript(OSAComponent, resultName, modeFlags, resultID); + Tcl_SetResult(interp, resultName, TCL_VOLATILE); + tclError = TCL_OK; + } + } + + /* + * This catches the error either from the original compile, + * or from the execute in case makeContext == true + */ + + if (osaErr == errOSAScriptError) { + OSADispose(OSAComponent->theComponent, resultID); + tclOSAASError(interp, OSAComponent->theComponent, + Tcl_DStringValue(&scrptData)); + tclError = TCL_ERROR; + } else if (osaErr != noErr) { + sprintf(buffer, "Error #%-6ld compiling script", osaErr); + Tcl_AppendResult(interp, buffer, (char *) NULL); + tclError = TCL_ERROR; + } + + Tcl_DStringFree(&scrptData); + AEDisposeDesc(&scrptDesc); + + return tclError; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSADecompileCmd -- + * + * This implements the Decompile subcommand of the component command + * + * Results: + * A standard Tcl result. + * + * Side Effects: + * Decompiles the script, and sets interp's result to the + * decompiled script data. + * + *---------------------------------------------------------------------- + */ + +static int +tclOSADecompileCmd( + Tcl_Interp * interp, + tclOSAComponent *OSAComponent, + int argc, + CONST char **argv) +{ + AEDesc resultingSourceData = { typeChar, NULL }; + OSAID scriptID; + Boolean isContext; + long result; + OSErr sysErr = noErr; + + if (argc == 2) { + Tcl_AppendResult(interp, "Wrong # of arguments, should be \"", + argv[0], " ",argv[1], " scriptName \"", (char *) NULL ); + return TCL_ERROR; + } + + if (!(OSAComponent->componentFlags && kOSASupportsGetSource)) { + Tcl_AppendResult(interp, + "Error, this component does not support get source", + (char *) NULL); + return TCL_ERROR; + } + + if (tclOSAGetScriptID(OSAComponent, argv[2], &scriptID) == TCL_OK) { + isContext = false; + } else if (tclOSAGetContextID(OSAComponent, argv[2], &scriptID) + == TCL_OK ) { + isContext = true; + } else { + Tcl_AppendResult(interp, "Could not find script \"", + argv[2], "\"", (char *) NULL); + return TCL_ERROR; + } + + OSAGetScriptInfo(OSAComponent->theComponent, scriptID, + kOSACanGetSource, &result); + + sysErr = OSAGetSource(OSAComponent->theComponent, + scriptID, typeChar, &resultingSourceData); + + if (sysErr == noErr) { + Tcl_DString theResult; + Tcl_DStringInit(&theResult); + + Tcl_DStringAppend(&theResult, *resultingSourceData.dataHandle, + GetHandleSize(resultingSourceData.dataHandle)); + Tcl_DStringResult(interp, &theResult); + AEDisposeDesc(&resultingSourceData); + return TCL_OK; + } else { + Tcl_AppendResult(interp, "Error getting source data", (char *) NULL); + AEDisposeDesc(&resultingSourceData); + return TCL_ERROR; + } +} + +/* + *---------------------------------------------------------------------- + * + * tclOSADeleteCmd -- + * + * This implements the Delete subcommand of the Component command. + * + * Results: + * A standard Tcl result. + * + * Side Effects: + * Deletes a script from the script list of the given component. + * Removes all references to the script, and frees the memory + * associated with it. + * + *---------------------------------------------------------------------- + */ + +static int +tclOSADeleteCmd( + Tcl_Interp *interp, + tclOSAComponent *OSAComponent, + int argc, + CONST char **argv) +{ + char c,*errMsg = NULL; + int length; + + if (argc < 4) { + Tcl_AppendResult(interp, "Wrong # of arguments, should be \"", + argv[0], " ", argv[1], " what scriptName", (char *) NULL); + return TCL_ERROR; + } + + c = *argv[2]; + length = strlen(argv[2]); + if (c == 'c' && strncmp(argv[2], "context", length) == 0) { + if (strcmp(argv[3], "global") == 0) { + Tcl_AppendResult(interp, "You cannot delete the global context", + (char *) NULL); + return TCL_ERROR; + } else if (tclOSADeleteContext(OSAComponent, argv[3]) != TCL_OK) { + Tcl_AppendResult(interp, "Error deleting script \"", argv[2], + "\": ", errMsg, (char *) NULL); + ckfree(errMsg); + return TCL_ERROR; + } + } else if (c == 's' && strncmp(argv[2], "script", length) == 0) { + if (tclOSADeleteScript(OSAComponent, argv[3], errMsg) != TCL_OK) { + Tcl_AppendResult(interp, "Error deleting script \"", argv[3], + "\": ", errMsg, (char *) NULL); + ckfree(errMsg); + return TCL_ERROR; + } + } else { + Tcl_AppendResult(interp,"Unknown value ", argv[2], + " should be one of ", + "\"context\" or \"script\".", + (char *) NULL ); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAExecuteCmd -- + * + * This implements the execute subcommand of the component command. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Executes the given script data, and sets interp's result to + * the OSA component's return value. + * + *---------------------------------------------------------------------- + */ + +static int +tclOSAExecuteCmd( + Tcl_Interp *interp, + tclOSAComponent *OSAComponent, + int argc, + CONST char **argv) +{ + int tclError = TCL_OK, resID = 128; + char c,buffer[32], + *contextName = NULL,*scriptName = NULL, *resName = NULL; + Boolean makeNewContext = false,makeContext = false; + AEDesc scrptDesc = { typeNull, NULL }; + long modeFlags = kOSAModeCanInteract; + OSAID resultID = kOSANullScript, + contextID = kOSANullScript, + parentID = kOSANullScript; + Tcl_DString scrptData; + OSAError osaErr = noErr; + OSErr sysErr = noErr; + + if (argc == 2) { + Tcl_AppendResult(interp, + "Error, no script data for \"", argv[0], + " run\"", (char *) NULL); + return TCL_ERROR; + } + + argv += 2; + argc -= 2; + + /* + * Set the context to the global context by default. + * Then parse the argument list for switches + */ + tclOSAGetContextID(OSAComponent, "global", &contextID); + + while (argc > 0) { + + if (*argv[0] == '-') { + c = *(argv[0] + 1); + + /* + * "--" is the only switch that has no value. + */ + + if (c == '-' && *(argv[0] + 2) == '\0') { + argv += 1; + argc--; + break; + } + + /* + * So we can check here for a switch with no value. + */ + + if (argc == 1) { + Tcl_AppendResult(interp, + "Error, no value given for switch ", + argv[0], (char *) NULL); + return TCL_ERROR; + } + + if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) { + if (tclOSAGetContextID(OSAComponent, + argv[1], &contextID) == TCL_OK) { + } else { + Tcl_AppendResult(interp, "Script context \"", + argv[1], "\" not found", (char *) NULL); + return TCL_ERROR; + } + } else { + Tcl_AppendResult(interp, "Error, invalid switch ", argv[0], + " should be \"-context\"", (char *) NULL); + return TCL_ERROR; + } + + argv += 2; + argc -= 2; + } else { + break; + } + } + + if (argc == 0) { + Tcl_AppendResult(interp, "Error, no script data", (char *) NULL); + return TCL_ERROR; + } + + if (prepareScriptData(argc, argv, &scrptData, &scrptDesc) == TCL_ERROR) { + Tcl_DStringResult(interp, &scrptData); + AEDisposeDesc(&scrptDesc); + return TCL_ERROR; + } + /* + * Now try to compile and run, but check to make sure the + * component supports the one shot deal + */ + if (OSAComponent->componentFlags && kOSASupportsConvenience) { + osaErr = OSACompileExecute(OSAComponent->theComponent, + &scrptDesc, contextID, modeFlags, &resultID); + } else { + /* + * If not, we have to do this ourselves + */ + if (OSAComponent->componentFlags && kOSASupportsCompiling) { + OSAID compiledID = kOSANullScript; + osaErr = OSACompile(OSAComponent->theComponent, &scrptDesc, + modeFlags, &compiledID); + if (osaErr == noErr) { + osaErr = OSAExecute(OSAComponent->theComponent, compiledID, + contextID, modeFlags, &resultID); + } + OSADispose(OSAComponent->theComponent, compiledID); + } else { + /* + * The scripting component had better be able to load text data... + */ + OSAID loadedID = kOSANullScript; + + scrptDesc.descriptorType = OSAComponent->languageID; + osaErr = OSALoad(OSAComponent->theComponent, &scrptDesc, + modeFlags, &loadedID); + if (osaErr == noErr) { + OSAExecute(OSAComponent->theComponent, loadedID, + contextID, modeFlags, &resultID); + } + OSADispose(OSAComponent->theComponent, loadedID); + } + } + if (osaErr == errOSAScriptError) { + tclOSAASError(interp, OSAComponent->theComponent, + Tcl_DStringValue(&scrptData)); + tclError = TCL_ERROR; + } else if (osaErr != noErr) { + sprintf(buffer, "Error #%-6ld compiling script", osaErr); + Tcl_AppendResult(interp, buffer, (char *) NULL); + tclError = TCL_ERROR; + } else { + tclOSAResultFromID(interp, OSAComponent->theComponent, resultID); + osaErr = OSADispose(OSAComponent->theComponent, resultID); + tclError = TCL_OK; + } + + Tcl_DStringFree(&scrptData); + AEDisposeDesc(&scrptDesc); + + return tclError; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAInfoCmd -- + * + * This implements the Info subcommand of the component command + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Info on scripts and contexts. See the user documentation for details. + * + *---------------------------------------------------------------------- + */ +static int +tclOSAInfoCmd( + Tcl_Interp *interp, + tclOSAComponent *OSAComponent, + int argc, + CONST char **argv) +{ + char c; + int length; + Tcl_DString theResult; + + if (argc == 2) { + Tcl_AppendResult(interp, "Wrong # of arguments, should be \"", + argv[0], " ", argv[1], " what \"", (char *) NULL ); + return TCL_ERROR; + } + + c = *argv[2]; + length = strlen(argv[2]); + if (c == 's' && strncmp(argv[2], "scripts", length) == 0) { + Tcl_DStringInit(&theResult); + if (argc == 3) { + getSortedHashKeys(&OSAComponent->scriptTable, (char *) NULL, + &theResult); + } else if (argc == 4) { + getSortedHashKeys(&OSAComponent->scriptTable, argv[3], &theResult); + } else { + Tcl_AppendResult(interp, "Error: wrong # of arguments,", + " should be \"", argv[0], " ", argv[1], " ", + argv[2], " ?pattern?", (char *) NULL); + return TCL_ERROR; + } + Tcl_DStringResult(interp, &theResult); + return TCL_OK; + } else if (c == 'c' && strncmp(argv[2], "contexts", length) == 0) { + Tcl_DStringInit(&theResult); + if (argc == 3) { + getSortedHashKeys(&OSAComponent->contextTable, (char *) NULL, + &theResult); + } else if (argc == 4) { + getSortedHashKeys(&OSAComponent->contextTable, + argv[3], &theResult); + } else { + Tcl_AppendResult(interp, "Error: wrong # of arguments for ,", + " should be \"", argv[0], " ", argv[1], " ", + argv[2], " ?pattern?", (char *) NULL); + return TCL_ERROR; + } + Tcl_DStringResult(interp, &theResult); + return TCL_OK; + } else if (c == 'l' && strncmp(argv[2], "language", length) == 0) { + Tcl_SetResult(interp, OSAComponent->languageName, TCL_STATIC); + return TCL_OK; + } else { + Tcl_AppendResult(interp, "Unknown argument \"", argv[2], + "\" for \"", argv[0], " info \", should be one of ", + "\"scripts\" \"language\", or \"contexts\"", + (char *) NULL); + return TCL_ERROR; + } +} + +/* + *---------------------------------------------------------------------- + * + * tclOSALoadCmd -- + * + * This is the load subcommand for the Component Command + * + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Loads script data from the given file, creates a new context + * for it, and sets interp's result to the name of the new context. + * + *---------------------------------------------------------------------- + */ + +static int +tclOSALoadCmd( + Tcl_Interp *interp, + tclOSAComponent *OSAComponent, + int argc, + CONST char **argv) +{ + int tclError = TCL_OK, resID = 128; + char c, autoName[24], + *contextName = NULL, *scriptName = NULL; + CONST char *resName = NULL; + Boolean makeNewContext = false, makeContext = false; + AEDesc scrptDesc = { typeNull, NULL }; + long modeFlags = kOSAModeCanInteract; + OSAID resultID = kOSANullScript, + contextID = kOSANullScript, + parentID = kOSANullScript; + OSAError osaErr = noErr; + OSErr sysErr = noErr; + long scptInfo; + + autoName[0] = '\0'; + scriptName = autoName; + contextName = autoName; + + if (argc == 2) { + Tcl_AppendResult(interp, + "Error, no data for \"", argv[0], " ", argv[1], + "\"", (char *) NULL); + return TCL_ERROR; + } + + argv += 2; + argc -= 2; + + /* + * Do the argument parsing. + */ + + while (argc > 0) { + + if (*argv[0] == '-') { + c = *(argv[0] + 1); + + /* + * "--" is the only switch that has no value. + */ + + if (c == '-' && *(argv[0] + 2) == '\0') { + argv += 1; + argc--; + break; + } + + /* + * So we can check here a switch with no value. + */ + + if (argc == 1) { + Tcl_AppendResult(interp, "Error, no value given for switch ", + argv[0], (char *) NULL); + return TCL_ERROR; + } + + if (c == 'r' && strcmp(argv[0] + 1, "rsrcname") == 0) { + resName = argv[1]; + } else if (c == 'r' && strcmp(argv[0] + 1, "rsrcid") == 0) { + if (Tcl_GetInt(interp, argv[1], &resID) != TCL_OK) { + Tcl_AppendResult(interp, + "Error getting resource ID", (char *) NULL); + return TCL_ERROR; + } + } else { + Tcl_AppendResult(interp, "Error, invalid switch ", argv[0], + " should be \"--\", \"-rsrcname\" or \"-rsrcid\"", + (char *) NULL); + return TCL_ERROR; + } + + argv += 2; + argc -= 2; + } else { + break; + } + } + /* + * Ok, now we have the options, so we can load the resource, + */ + if (argc == 0) { + Tcl_AppendResult(interp, "Error, no filename given", (char *) NULL); + return TCL_ERROR; + } + + if (tclOSALoad(interp, OSAComponent, resName, resID, + argv[0], &resultID) != TCL_OK) { + Tcl_AppendResult(interp, "Error in load command", (char *) NULL); + return TCL_ERROR; + } + + /* + * Now find out whether we have a script, or a script context. + */ + + OSAGetScriptInfo(OSAComponent->theComponent, resultID, + kOSAScriptIsTypeScriptContext, &scptInfo); + + if (scptInfo) { + autoName[0] = '\0'; + tclOSAAddContext(OSAComponent, autoName, resultID); + + Tcl_SetResult(interp, autoName, TCL_VOLATILE); + } else { + /* + * For a script, we return the script name + */ + autoName[0] = '\0'; + tclOSAAddScript(OSAComponent, autoName, kOSAModeCanInteract, resultID); + Tcl_SetResult(interp, autoName, TCL_VOLATILE); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSARunCmd -- + * + * This implements the run subcommand of the component command + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Runs the given compiled script, and returns the OSA + * component's result. + * + *---------------------------------------------------------------------- + */ + +static int +tclOSARunCmd( + Tcl_Interp *interp, + tclOSAComponent *OSAComponent, + int argc, + CONST char **argv) +{ + int tclError = TCL_OK, + resID = 128; + char c, *contextName = NULL, + *scriptName = NULL, + *resName = NULL; + AEDesc scrptDesc = { typeNull, NULL }; + long modeFlags = kOSAModeCanInteract; + OSAID resultID = kOSANullScript, + contextID = kOSANullScript, + parentID = kOSANullScript; + OSAError osaErr = noErr; + OSErr sysErr = noErr; + CONST char *componentName = argv[0]; + OSAID scriptID; + + if (argc == 2) { + Tcl_AppendResult(interp, "Wrong # of arguments, should be \"", + argv[0], " ", argv[1], " scriptName", (char *) NULL); + return TCL_ERROR; + } + + /* + * Set the context to the global context for this component, + * as a default + */ + if (tclOSAGetContextID(OSAComponent, "global", &contextID) != TCL_OK) { + Tcl_AppendResult(interp, + "Could not find the global context for component ", + OSAComponent->theName, (char *) NULL ); + return TCL_ERROR; + } + + /* + * Now parse the argument list for switches + */ + argv += 2; + argc -= 2; + + while (argc > 0) { + if (*argv[0] == '-') { + c = *(argv[0] + 1); + /* + * "--" is the only switch that has no value + */ + if (c == '-' && *(argv[0] + 2) == '\0') { + argv += 1; + argc--; + break; + } + + /* + * So we can check here for a switch with no value. + */ + if (argc == 1) { + Tcl_AppendResult(interp, "Error, no value given for switch ", + argv[0], (char *) NULL); + return TCL_ERROR; + } + + if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) { + if (argc == 1) { + Tcl_AppendResult(interp, + "Error - no context provided for the -context switch", + (char *) NULL); + return TCL_ERROR; + } else if (tclOSAGetContextID(OSAComponent, + argv[1], &contextID) == TCL_OK) { + } else { + Tcl_AppendResult(interp, "Script context \"", argv[1], + "\" not found", (char *) NULL); + return TCL_ERROR; + } + } else { + Tcl_AppendResult(interp, "Error, invalid switch ", argv[0], + " for ", componentName, + " should be \"-context\"", (char *) NULL); + return TCL_ERROR; + } + argv += 2; + argc -= 2; + } else { + break; + } + } + + if (tclOSAGetScriptID(OSAComponent, argv[0], &scriptID) != TCL_OK) { + if (tclOSAGetContextID(OSAComponent, argv[0], &scriptID) != TCL_OK) { + Tcl_AppendResult(interp, "Could not find script \"", + argv[2], "\"", (char *) NULL); + return TCL_ERROR; + } + } + + sysErr = OSAExecute(OSAComponent->theComponent, + scriptID, contextID, modeFlags, &resultID); + + if (sysErr == errOSAScriptError) { + tclOSAASError(interp, OSAComponent->theComponent, (char *) NULL); + tclError = TCL_ERROR; + } else if (sysErr != noErr) { + char buffer[32]; + sprintf(buffer, "Error #%6.6d encountered in run", sysErr); + Tcl_SetResult(interp, buffer, TCL_VOLATILE); + tclError = TCL_ERROR; + } else { + tclOSAResultFromID(interp, OSAComponent->theComponent, resultID ); + } + OSADispose(OSAComponent->theComponent, resultID); + + return tclError; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAStoreCmd -- + * + * This implements the store subcommand of the component command + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Runs the given compiled script, and returns the OSA + * component's result. + * + *---------------------------------------------------------------------- + */ + +static int +tclOSAStoreCmd( + Tcl_Interp *interp, + tclOSAComponent *OSAComponent, + int argc, + CONST char **argv) +{ + int tclError = TCL_OK, resID = 128; + char c, *contextName = NULL, *scriptName = NULL; + CONST char *resName = NULL; + Boolean makeNewContext = false, makeContext = false; + AEDesc scrptDesc = { typeNull, NULL }; + long modeFlags = kOSAModeCanInteract; + OSAID resultID = kOSANullScript, + contextID = kOSANullScript, + parentID = kOSANullScript; + OSAError osaErr = noErr; + OSErr sysErr = noErr; + + if (argc == 2) { + Tcl_AppendResult(interp, "Error, no data for \"", argv[0], + " ",argv[1], "\"", (char *) NULL); + return TCL_ERROR; + } + + argv += 2; + argc -= 2; + + /* + * Do the argument parsing + */ + + while (argc > 0) { + if (*argv[0] == '-') { + c = *(argv[0] + 1); + + /* + * "--" is the only switch that has no value + */ + if (c == '-' && *(argv[0] + 2) == '\0') { + argv += 1; + argc--; + break; + } + + /* + * So we can check here a switch with no value. + */ + if (argc == 1) { + Tcl_AppendResult(interp, + "Error, no value given for switch ", + argv[0], (char *) NULL); + return TCL_ERROR; + } + + if (c == 'r' && strcmp(argv[0] + 1, "rsrcname") == 0) { + resName = argv[1]; + } else if (c == 'r' && strcmp(argv[0] + 1, "rsrcid") == 0) { + if (Tcl_GetInt(interp, argv[1], &resID) != TCL_OK) { + Tcl_AppendResult(interp, + "Error getting resource ID", (char *) NULL); + return TCL_ERROR; + } + } else { + Tcl_AppendResult(interp, "Error, invalid switch ", argv[0], + " should be \"--\", \"-rsrcname\" or \"-rsrcid\"", + (char *) NULL); + return TCL_ERROR; + } + + argv += 2; + argc -= 2; + } else { + break; + } + } + /* + * Ok, now we have the options, so we can load the resource, + */ + if (argc != 2) { + Tcl_AppendResult(interp, "Error, wrong # of arguments, should be ", + argv[0], " ", argv[1], "?option flag? scriptName fileName", + (char *) NULL); + return TCL_ERROR; + } + + if (tclOSAStore(interp, OSAComponent, resName, resID, + argv[0], argv[1]) != TCL_OK) { + Tcl_AppendResult(interp, "Error in load command", (char *) NULL); + return TCL_ERROR; + } else { + Tcl_ResetResult(interp); + tclError = TCL_OK; + } + + return tclError; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAMakeNewComponent -- + * + * Makes a command cmdName to represent a new connection to the + * OSA component with componentSubType scriptSubtype. + * + * Results: + * Returns the tclOSAComponent structure for the connection. + * + * Side Effects: + * Adds a new element to the component table. If there is an + * error, then the result of the Tcl interpreter interp is set + * to an appropriate error message. + * + *---------------------------------------------------------------------- + */ + +tclOSAComponent * +tclOSAMakeNewComponent( + Tcl_Interp *interp, + char *cmdName, + char *languageName, + OSType scriptSubtype, + long componentFlags) +{ + char buffer[32]; + AEDesc resultingName = {typeNull, NULL}; + AEDesc nullDesc = {typeNull, NULL }; + OSAID globalContext; + char global[] = "global"; + int nbytes; + ComponentDescription requestedComponent = { + kOSAComponentType, + (OSType) 0, + (OSType) 0, + (long int) 0, + (long int) 0 + }; + Tcl_HashTable *ComponentTable; + Component foundComponent = NULL; + OSAActiveUPP myActiveProcUPP; + + tclOSAComponent *newComponent; + Tcl_HashEntry *hashEntry; + int newPtr; + + requestedComponent.componentSubType = scriptSubtype; + nbytes = sizeof(tclOSAComponent); + newComponent = (tclOSAComponent *) ckalloc(sizeof(tclOSAComponent)); + if (newComponent == NULL) { + goto CleanUp; + } + + foundComponent = FindNextComponent(0, &requestedComponent); + if (foundComponent == 0) { + Tcl_AppendResult(interp, + "Could not find component of requested type", (char *) NULL); + goto CleanUp; + } + + newComponent->theComponent = OpenComponent(foundComponent); + + if (newComponent->theComponent == NULL) { + Tcl_AppendResult(interp, + "Could not open component of the requested type", + (char *) NULL); + goto CleanUp; + } + + newComponent->languageName = (char *) ckalloc(strlen(languageName) + 1); + strcpy(newComponent->languageName,languageName); + + newComponent->componentFlags = componentFlags; + + newComponent->theInterp = interp; + + Tcl_InitHashTable(&newComponent->contextTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&newComponent->scriptTable, TCL_STRING_KEYS); + + if (tclOSAMakeContext(newComponent, global, &globalContext) != TCL_OK) { + sprintf(buffer, "%-6.6ld", globalContext); + Tcl_AppendResult(interp, "Error ", buffer, " making ", global, + " context.", (char *) NULL); + goto CleanUp; + } + + newComponent->languageID = scriptSubtype; + + newComponent->theName = (char *) ckalloc(strlen(cmdName) + 1 ); + strcpy(newComponent->theName, cmdName); + + Tcl_CreateCommand(interp, newComponent->theName, Tcl_OSAComponentCmd, + (ClientData) newComponent, tclOSAClose); + + /* + * Register the new component with the component table + */ + + ComponentTable = (Tcl_HashTable *) Tcl_GetAssocData(interp, + "OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL); + + if (ComponentTable == NULL) { + Tcl_AppendResult(interp, "Error, could not get the Component Table", + " from the Associated data.", (char *) NULL); + return (tclOSAComponent *) NULL; + } + + hashEntry = Tcl_CreateHashEntry(ComponentTable, + newComponent->theName, &newPtr); + Tcl_SetHashValue(hashEntry, (ClientData) newComponent); + + /* + * Set the active proc to call Tcl_DoOneEvent() while idle + */ + if (OSAGetActiveProc(newComponent->theComponent, + &newComponent->defActiveProc, &newComponent->defRefCon) != noErr ) { + /* TODO -- clean up here... */ + } + + myActiveProcUPP = NewOSAActiveUPP(TclOSAActiveProc); + OSASetActiveProc(newComponent->theComponent, + myActiveProcUPP, (long) newComponent); + return newComponent; + + CleanUp: + + ckfree((char *) newComponent); + return (tclOSAComponent *) NULL; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAClose -- + * + * This procedure closes the connection to an OSA component, and + * deletes all the script and context data associated with it. + * It is the command deletion callback for the component's command. + * + * Results: + * None + * + * Side effects: + * Closes the connection, and releases all the script data. + * + *---------------------------------------------------------------------- + */ + +void +tclOSAClose( + ClientData clientData) +{ + tclOSAComponent *theComponent = (tclOSAComponent *) clientData; + Tcl_HashEntry *hashEntry; + Tcl_HashSearch search; + tclOSAScript *theScript; + Tcl_HashTable *ComponentTable; + + /* + * Delete the context and script tables + * the memory for the language name, and + * the hash entry. + */ + + for (hashEntry = Tcl_FirstHashEntry(&theComponent->scriptTable, &search); + hashEntry != NULL; + hashEntry = Tcl_NextHashEntry(&search)) { + + theScript = (tclOSAScript *) Tcl_GetHashValue(hashEntry); + OSADispose(theComponent->theComponent, theScript->scriptID); + ckfree((char *) theScript); + Tcl_DeleteHashEntry(hashEntry); + } + + for (hashEntry = Tcl_FirstHashEntry(&theComponent->contextTable, &search); + hashEntry != NULL; + hashEntry = Tcl_NextHashEntry(&search)) { + + Tcl_DeleteHashEntry(hashEntry); + } + + ckfree(theComponent->languageName); + ckfree(theComponent->theName); + + /* + * Finally close the component + */ + + CloseComponent(theComponent->theComponent); + + ComponentTable = (Tcl_HashTable *) + Tcl_GetAssocData(theComponent->theInterp, + "OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL); + + if (ComponentTable == NULL) { + Tcl_Panic("Error, could not get the Component Table from the Associated data."); + } + + hashEntry = Tcl_FindHashEntry(ComponentTable, theComponent->theName); + if (hashEntry != NULL) { + Tcl_DeleteHashEntry(hashEntry); + } + + ckfree((char *) theComponent); +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAGetContextID -- + * + * This returns the context ID, given the component name. + * + * Results: + * A context ID + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ + +static int +tclOSAGetContextID( + tclOSAComponent *theComponent, + CONST char *contextName, + OSAID *theContext) +{ + Tcl_HashEntry *hashEntry; + tclOSAContext *contextStruct; + + if ((hashEntry = Tcl_FindHashEntry(&theComponent->contextTable, + contextName)) == NULL ) { + return TCL_ERROR; + } else { + contextStruct = (tclOSAContext *) Tcl_GetHashValue(hashEntry); + *theContext = contextStruct->contextID; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAAddContext -- + * + * This adds the context ID, with the name contextName. If the + * name is passed in as a NULL string, space is malloc'ed for the + * string and a new name is made up, if the string is empty, you + * must have allocated enough space ( 24 characters is fine) for + * the name, which is made up and passed out. + * + * Results: + * Nothing + * + * Side effects: + * Adds the script context to the component's context table. + * + *---------------------------------------------------------------------- + */ + +static void +tclOSAAddContext( + tclOSAComponent *theComponent, + char *contextName, + const OSAID theContext) +{ + static unsigned short contextIndex = 0; + tclOSAContext *contextStruct; + Tcl_HashEntry *hashEntry; + int newPtr; + + if (contextName == NULL) { + contextName = ckalloc(16 + TCL_INTEGER_SPACE); + sprintf(contextName, "OSAContext%d", contextIndex++); + } else if (*contextName == '\0') { + sprintf(contextName, "OSAContext%d", contextIndex++); + } + + hashEntry = Tcl_CreateHashEntry(&theComponent->contextTable, + contextName, &newPtr); + + contextStruct = (tclOSAContext *) ckalloc(sizeof(tclOSAContext)); + contextStruct->contextID = theContext; + Tcl_SetHashValue(hashEntry,(ClientData) contextStruct); +} + +/* + *---------------------------------------------------------------------- + * + * tclOSADeleteContext -- + * + * This deletes the context struct, with the name contextName. + * + * Results: + * A normal Tcl result + * + * Side effects: + * Removes the script context to the component's context table, + * and deletes the data associated with it. + * + *---------------------------------------------------------------------- + */ + +static int +tclOSADeleteContext( + tclOSAComponent *theComponent, + CONST char *contextName) +{ + Tcl_HashEntry *hashEntry; + tclOSAContext *contextStruct; + + hashEntry = Tcl_FindHashEntry(&theComponent->contextTable, contextName); + if (hashEntry == NULL) { + return TCL_ERROR; + } + /* + * Dispose of the script context data + */ + contextStruct = (tclOSAContext *) Tcl_GetHashValue(hashEntry); + OSADispose(theComponent->theComponent,contextStruct->contextID); + /* + * Then the hash entry + */ + ckfree((char *) contextStruct); + Tcl_DeleteHashEntry(hashEntry); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAMakeContext -- + * + * This makes the context with name contextName, and returns the ID. + * + * Results: + * A standard Tcl result + * + * Side effects: + * Makes a new context, adds it to the context table, and returns + * the new contextID in the variable theContext. + * + *---------------------------------------------------------------------- + */ + +static int +tclOSAMakeContext( + tclOSAComponent *theComponent, + CONST char *contextName, + OSAID *theContext) +{ + AEDesc contextNameDesc = {typeNull, NULL}; + OSAError osaErr = noErr; + + AECreateDesc(typeChar, contextName, strlen(contextName), &contextNameDesc); + osaErr = OSAMakeContext(theComponent->theComponent, &contextNameDesc, + kOSANullScript, theContext); + + AEDisposeDesc(&contextNameDesc); + + if (osaErr == noErr) { + char name[24]; + strncpy(name, contextName, 23); + name[23] = '\0'; + tclOSAAddContext(theComponent, name, *theContext); + } else { + *theContext = (OSAID) osaErr; + return TCL_ERROR; + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAStore -- + * + * This stores a script resource from the file named in fileName. + * + * Most of this routine is caged from the Tcl Source, from the + * Tcl_MacSourceCmd routine. This is good, since it ensures this + * follows the same convention for looking up files as Tcl. + * + * Returns + * A standard Tcl result. + * + * Side Effects: + * The given script data is stored in the file fileName. + * + *---------------------------------------------------------------------- + */ + +int +tclOSAStore( + Tcl_Interp *interp, + tclOSAComponent *theComponent, + CONST char *resourceName, + int resourceNumber, + CONST char *scriptName, + CONST char *fileName) +{ + Handle resHandle; + Str255 rezName; + int result = TCL_OK; + short saveRef, fileRef = -1; + char idStr[16 + TCL_INTEGER_SPACE]; + FSSpec fileSpec; + Tcl_DString ds, buffer; + CONST char *nativeName; + OSErr myErr = noErr; + OSAID scriptID; + Size scriptSize; + AEDesc scriptData; + + /* + * First extract the script data + */ + + if (tclOSAGetScriptID(theComponent, scriptName, &scriptID) != TCL_OK ) { + if (tclOSAGetContextID(theComponent, scriptName, &scriptID) + != TCL_OK) { + Tcl_AppendResult(interp, "Error getting script ", + scriptName, (char *) NULL); + return TCL_ERROR; + } + } + + myErr = OSAStore(theComponent->theComponent, scriptID, + typeOSAGenericStorage, kOSAModeNull, &scriptData); + if (myErr != noErr) { + sprintf(idStr, "%d", myErr); + Tcl_AppendResult(interp, "Error #", idStr, + " storing script ", scriptName, (char *) NULL); + return TCL_ERROR; + } + + /* + * Now try to open the output file + */ + + saveRef = CurResFile(); + + if (fileName != NULL) { + OSErr err; + + if (Tcl_TranslateFileName(interp, fileName, &buffer) == NULL) { + return TCL_ERROR; + } + nativeName = Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&buffer), + Tcl_DStringLength(&buffer), &ds); + err = FSpLocationFromPath(strlen(nativeName), nativeName, &fileSpec); + + Tcl_DStringFree(&ds); + Tcl_DStringFree(&buffer); + if ((err != noErr) && (err != fnfErr)) { + Tcl_AppendResult(interp, + "Error getting a location for the file: \"", + fileName, "\".", NULL); + return TCL_ERROR; + } + + FSpCreateResFileCompatTcl(&fileSpec, + 'WiSH', 'osas', smSystemScript); + myErr = ResError(); + + if ((myErr != noErr) && (myErr != dupFNErr)) { + sprintf(idStr, "%d", myErr); + Tcl_AppendResult(interp, "Error #", idStr, + " creating new resource file ", fileName, (char *) NULL); + result = TCL_ERROR; + goto rezEvalCleanUp; + } + + fileRef = FSpOpenResFileCompatTcl(&fileSpec, fsRdWrPerm); + if (fileRef == -1) { + Tcl_AppendResult(interp, "Error reading the file: \"", + fileName, "\".", NULL); + result = TCL_ERROR; + goto rezEvalCleanUp; + } + UseResFile(fileRef); + } else { + /* + * The default behavior will search through all open resource files. + * This may not be the behavior you desire. If you want the behavior + * of this call to *only* search the application resource fork, you + * must call UseResFile at this point to set it to the application + * file. This means you must have already obtained the application's + * fileRef when the application started up. + */ + } + + /* + * Load the resource by name + */ + if (resourceName != NULL) { + strcpy((char *) rezName + 1, resourceName); + rezName[0] = strlen(resourceName); + resHandle = Get1NamedResource('scpt', rezName); + myErr = ResError(); + if (resHandle == NULL) { + /* + * These signify either the resource or the resource + * type were not found + */ + if (myErr == resNotFound || myErr == noErr) { + short uniqueID; + while ((uniqueID = Unique1ID('scpt') ) < 128) {} + AddResource(scriptData.dataHandle, 'scpt', uniqueID, rezName); + WriteResource(resHandle); + result = TCL_OK; + goto rezEvalCleanUp; + } else { + /* + * This means there was some other error, for now + * I just bag out. + */ + sprintf(idStr, "%d", myErr); + Tcl_AppendResult(interp, "Error #", idStr, + " opening scpt resource named ", resourceName, + " in file ", fileName, (char *) NULL); + result = TCL_ERROR; + goto rezEvalCleanUp; + } + } + /* + * Or ID + */ + } else { + resHandle = Get1Resource('scpt', resourceNumber); + rezName[0] = 0; + rezName[1] = '\0'; + myErr = ResError(); + if (resHandle == NULL) { + /* + * These signify either the resource or the resource + * type were not found + */ + if (myErr == resNotFound || myErr == noErr) { + AddResource(scriptData.dataHandle, 'scpt', + resourceNumber, rezName); + WriteResource(resHandle); + result = TCL_OK; + goto rezEvalCleanUp; + } else { + /* + * This means there was some other error, for now + * I just bag out */ + sprintf(idStr, "%d", myErr); + Tcl_AppendResult(interp, "Error #", idStr, + " opening scpt resource named ", resourceName, + " in file ", fileName,(char *) NULL); + result = TCL_ERROR; + goto rezEvalCleanUp; + } + } + } + + /* + * We get to here if the resource exists + * we just copy into it... + */ + + scriptSize = GetHandleSize(scriptData.dataHandle); + SetHandleSize(resHandle, scriptSize); + HLock(scriptData.dataHandle); + HLock(resHandle); + BlockMove(*scriptData.dataHandle, *resHandle,scriptSize); + HUnlock(scriptData.dataHandle); + HUnlock(resHandle); + ChangedResource(resHandle); + WriteResource(resHandle); + result = TCL_OK; + goto rezEvalCleanUp; + + rezEvalError: + sprintf(idStr, "ID=%d", resourceNumber); + Tcl_AppendResult(interp, "The resource \"", + (resourceName != NULL ? resourceName : idStr), + "\" could not be loaded from ", + (fileName != NULL ? fileName : "application"), + ".", NULL); + + rezEvalCleanUp: + if (fileRef != -1) { + CloseResFile(fileRef); + } + + UseResFile(saveRef); + + return result; +} + +/*---------------------------------------------------------------------- + * + * tclOSALoad -- + * + * This loads a script resource from the file named in fileName. + * Most of this routine is caged from the Tcl Source, from the + * Tcl_MacSourceCmd routine. This is good, since it ensures this + * follows the same convention for looking up files as Tcl. + * + * Returns + * A standard Tcl result. + * + * Side Effects: + * A new script element is created from the data in the file. + * The script ID is passed out in the variable resultID. + * + *---------------------------------------------------------------------- + */ + +int +tclOSALoad( + Tcl_Interp *interp, + tclOSAComponent *theComponent, + CONST char *resourceName, + int resourceNumber, + CONST char *fileName, + OSAID *resultID) +{ + Handle sourceData; + Str255 rezName; + int result = TCL_OK; + short saveRef, fileRef = -1; + char idStr[16 + TCL_INTEGER_SPACE]; + FSSpec fileSpec; + Tcl_DString ds, buffer; + CONST char *nativeName; + + saveRef = CurResFile(); + + if (fileName != NULL) { + OSErr err; + + if (Tcl_TranslateFileName(interp, fileName, &buffer) == NULL) { + return TCL_ERROR; + } + nativeName = Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&buffer), + Tcl_DStringLength(&buffer), &ds); + err = FSpLocationFromPath(strlen(nativeName), nativeName, &fileSpec); + Tcl_DStringFree(&ds); + Tcl_DStringFree(&buffer); + if (err != noErr) { + Tcl_AppendResult(interp, "Error finding the file: \"", + fileName, "\".", NULL); + return TCL_ERROR; + } + + fileRef = FSpOpenResFileCompatTcl(&fileSpec, fsRdPerm); + if (fileRef == -1) { + Tcl_AppendResult(interp, "Error reading the file: \"", + fileName, "\".", NULL); + return TCL_ERROR; + } + UseResFile(fileRef); + } else { + /* + * The default behavior will search through all open resource files. + * This may not be the behavior you desire. If you want the behavior + * of this call to *only* search the application resource fork, you + * must call UseResFile at this point to set it to the application + * file. This means you must have already obtained the application's + * fileRef when the application started up. + */ + } + + /* + * Load the resource by name or ID + */ + if (resourceName != NULL) { + strcpy((char *) rezName + 1, resourceName); + rezName[0] = strlen(resourceName); + sourceData = GetNamedResource('scpt', rezName); + } else { + sourceData = GetResource('scpt', (short) resourceNumber); + } + + if (sourceData == NULL) { + result = TCL_ERROR; + } else { + AEDesc scriptDesc; + OSAError osaErr; + + scriptDesc.descriptorType = typeOSAGenericStorage; + scriptDesc.dataHandle = sourceData; + + osaErr = OSALoad(theComponent->theComponent, &scriptDesc, + kOSAModeNull, resultID); + + ReleaseResource(sourceData); + + if (osaErr != noErr) { + result = TCL_ERROR; + goto rezEvalError; + } + + goto rezEvalCleanUp; + } + + rezEvalError: + sprintf(idStr, "ID=%d", resourceNumber); + Tcl_AppendResult(interp, "The resource \"", + (resourceName != NULL ? resourceName : idStr), + "\" could not be loaded from ", + (fileName != NULL ? fileName : "application"), + ".", NULL); + + rezEvalCleanUp: + if (fileRef != -1) { + CloseResFile(fileRef); + } + + UseResFile(saveRef); + + return result; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAGetScriptID -- + * + * This returns the context ID, gibven the component name. + * + * Results: + * A standard Tcl result + * + * Side effects: + * Passes out the script ID in the variable scriptID. + * + *---------------------------------------------------------------------- + */ + +static int +tclOSAGetScriptID( + tclOSAComponent *theComponent, + CONST char *scriptName, + OSAID *scriptID) +{ + tclOSAScript *theScript; + + theScript = tclOSAGetScript(theComponent, scriptName); + if (theScript == NULL) { + return TCL_ERROR; + } + + *scriptID = theScript->scriptID; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAAddScript -- + * + * This adds a script to theComponent's script table, with the + * given name & ID. + * + * Results: + * A standard Tcl result + * + * Side effects: + * Adds an element to the component's script table. + * + *---------------------------------------------------------------------- + */ + +static int +tclOSAAddScript( + tclOSAComponent *theComponent, + char *scriptName, + long modeFlags, + OSAID scriptID) +{ + Tcl_HashEntry *hashEntry; + int newPtr; + static int scriptIndex = 0; + tclOSAScript *theScript; + + if (*scriptName == '\0') { + sprintf(scriptName, "OSAScript%d", scriptIndex++); + } + + hashEntry = Tcl_CreateHashEntry(&theComponent->scriptTable, + scriptName, &newPtr); + if (newPtr == 0) { + theScript = (tclOSAScript *) Tcl_GetHashValue(hashEntry); + OSADispose(theComponent->theComponent, theScript->scriptID); + } else { + theScript = (tclOSAScript *) ckalloc(sizeof(tclOSAScript)); + if (theScript == NULL) { + return TCL_ERROR; + } + } + + theScript->scriptID = scriptID; + theScript->languageID = theComponent->languageID; + theScript->modeFlags = modeFlags; + + Tcl_SetHashValue(hashEntry,(ClientData) theScript); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAGetScriptID -- + * + * This returns the script structure, given the component and script name. + * + * Results: + * A pointer to the script structure. + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ + +static tclOSAScript * +tclOSAGetScript( + tclOSAComponent *theComponent, + CONST char *scriptName) +{ + Tcl_HashEntry *hashEntry; + + hashEntry = Tcl_FindHashEntry(&theComponent->scriptTable, scriptName); + if (hashEntry == NULL) { + return NULL; + } + + return (tclOSAScript *) Tcl_GetHashValue(hashEntry); +} + +/* + *---------------------------------------------------------------------- + * + * tclOSADeleteScript -- + * + * This deletes the script given by scriptName. + * + * Results: + * A standard Tcl result + * + * Side effects: + * Deletes the script from the script table, and frees up the + * resources associated with it. If there is an error, then + * space for the error message is malloc'ed, and passed out in + * the variable errMsg. + * + *---------------------------------------------------------------------- + */ + +static int +tclOSADeleteScript( + tclOSAComponent *theComponent, + CONST char *scriptName, + char *errMsg) +{ + Tcl_HashEntry *hashEntry; + tclOSAScript *scriptPtr; + + hashEntry = Tcl_FindHashEntry(&theComponent->scriptTable, scriptName); + if (hashEntry == NULL) { + errMsg = ckalloc(17); + strcpy(errMsg,"Script not found"); + return TCL_ERROR; + } + + scriptPtr = (tclOSAScript *) Tcl_GetHashValue(hashEntry); + OSADispose(theComponent->theComponent, scriptPtr->scriptID); + ckfree((char *) scriptPtr); + Tcl_DeleteHashEntry(hashEntry); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclOSAActiveProc -- + * + * This is passed to each component. It is run periodically + * during script compilation and script execution. It in turn + * calls Tcl_DoOneEvent to process the event queue. We also call + * the default Active proc which will let the user cancel the script + * by hitting Command-. + * + * Results: + * A standard MacOS system error + * + * Side effects: + * Any Tcl code may run while calling Tcl_DoOneEvent. + * + *---------------------------------------------------------------------- + */ + +static pascal OSErr +TclOSAActiveProc( + long refCon) +{ + tclOSAComponent *theComponent = (tclOSAComponent *) refCon; + + Tcl_DoOneEvent(TCL_DONT_WAIT); + InvokeOSAActiveUPP(theComponent->defRefCon, theComponent->defActiveProc); + + return noErr; +} + +/* + *---------------------------------------------------------------------- + * + * ASCIICompareProc -- + * + * Trivial ascii compare for use with qsort. + * + * Results: + * strcmp of the two input strings + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ +static int +ASCIICompareProc(const void *first,const void *second) +{ + int order; + + char *firstString = *((char **) first); + char *secondString = *((char **) second); + + order = strcmp(firstString, secondString); + + return order; +} + +#define REALLOC_INCR 30 +/* + *---------------------------------------------------------------------- + * + * getSortedHashKeys -- + * + * returns an alphabetically sorted list of the keys of the hash + * theTable which match the string "pattern" in the DString + * theResult. pattern == NULL matches all. + * + * Results: + * None + * + * Side effects: + * ReInitializes the DString theResult, then copies the names of + * the matching keys into the string as list elements. + * + *---------------------------------------------------------------------- + */ + +static void +getSortedHashKeys( + Tcl_HashTable *theTable, + CONST char *pattern, + Tcl_DString *theResult) +{ + Tcl_HashSearch search; + Tcl_HashEntry *hPtr; + Boolean compare = true; + char *keyPtr; + static char **resultArgv = NULL; + static int totSize = 0; + int totElem = 0, i; + + if (pattern == NULL || *pattern == '\0' || + (*pattern == '*' && *(pattern + 1) == '\0')) { + compare = false; + } + + for (hPtr = Tcl_FirstHashEntry(theTable,&search), totElem = 0; + hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { + + keyPtr = (char *) Tcl_GetHashKey(theTable, hPtr); + if (!compare || Tcl_StringMatch(keyPtr, pattern)) { + totElem++; + if (totElem >= totSize) { + totSize += REALLOC_INCR; + resultArgv = (char **) ckrealloc((char *) resultArgv, + totSize * sizeof(char *)); + } + resultArgv[totElem - 1] = keyPtr; + } + } + + Tcl_DStringInit(theResult); + if (totElem == 1) { + Tcl_DStringAppendElement(theResult, resultArgv[0]); + } else if (totElem > 1) { + qsort((VOID *) resultArgv, (size_t) totElem, sizeof (char *), + ASCIICompareProc); + + for (i = 0; i < totElem; i++) { + Tcl_DStringAppendElement(theResult, resultArgv[i]); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * prepareScriptData -- + * + * Massages the input data in the argv array, concating the + * elements, with a " " between each, and replacing \n with \r, + * and \\n with " ". Puts the result in the the DString scrptData, + * and copies the result to the AEdesc scrptDesc. + * + * Results: + * Standard Tcl result + * + * Side effects: + * Creates a new Handle (with AECreateDesc) for the script data. + * Stores the script in scrptData, or the error message if there + * is an error creating the descriptor. + * + *---------------------------------------------------------------------- + */ + +static int +prepareScriptData( + int argc, + CONST char **argv, + Tcl_DString *scrptData, + AEDesc *scrptDesc) +{ + char * ptr; + int i; + char buffer[7]; + OSErr sysErr = noErr; + Tcl_DString encodedText; + + Tcl_DStringInit(scrptData); + + for (i = 0; i < argc; i++) { + Tcl_DStringAppend(scrptData, argv[i], -1); + Tcl_DStringAppend(scrptData, " ", 1); + } + + /* + * First replace the \n's with \r's in the script argument + * Also replace "\\n" with " ". + */ + + for (ptr = scrptData->string; *ptr != '\0'; ptr++) { + if (*ptr == '\n') { + *ptr = '\r'; + } else if (*ptr == '\\') { + if (*(ptr + 1) == '\n') { + *ptr = ' '; + *(ptr + 1) = ' '; + } + } + } + + Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(scrptData), + Tcl_DStringLength(scrptData), &encodedText); + sysErr = AECreateDesc(typeChar, Tcl_DStringValue(&encodedText), + Tcl_DStringLength(&encodedText), scrptDesc); + Tcl_DStringFree(&encodedText); + + if (sysErr != noErr) { + sprintf(buffer, "%6d", sysErr); + Tcl_DStringFree(scrptData); + Tcl_DStringAppend(scrptData, "Error #", 7); + Tcl_DStringAppend(scrptData, buffer, -1); + Tcl_DStringAppend(scrptData, " creating Script Data Descriptor.", 33); + return TCL_ERROR; + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAResultFromID -- + * + * Gets a human readable version of the result from the script ID + * and returns it in the result of the interpreter interp + * + * Results: + * None + * + * Side effects: + * Sets the result of interp to the human readable version of resultID. + * + * + *---------------------------------------------------------------------- + */ + +void +tclOSAResultFromID( + Tcl_Interp *interp, + ComponentInstance theComponent, + OSAID resultID ) +{ + OSErr myErr = noErr; + AEDesc resultDesc; + Tcl_DString resultStr; + + Tcl_DStringInit(&resultStr); + + myErr = OSADisplay(theComponent, resultID, typeChar, + kOSAModeNull, &resultDesc); + Tcl_DStringAppend(&resultStr, (char *) *resultDesc.dataHandle, + GetHandleSize(resultDesc.dataHandle)); + Tcl_DStringResult(interp,&resultStr); +} + +/* + *---------------------------------------------------------------------- + * + * tclOSAASError -- + * + * Gets the error message from the AppleScript component, and adds + * it to interp's result. If the script data is known, will point + * out the offending bit of code. This MUST BE A NULL TERMINATED + * C-STRING, not a typeChar. + * + * Results: + * None + * + * Side effects: + * Sets the result of interp to error, plus the relevant portion + * of the script. + * + *---------------------------------------------------------------------- + */ + +void +tclOSAASError( + Tcl_Interp * interp, + ComponentInstance theComponent, + char *scriptData ) +{ + OSErr myErr = noErr; + AEDesc errResult,errLimits; + Tcl_DString errStr; + DescType returnType; + Size returnSize; + short srcStart,srcEnd; + char buffer[16]; + + Tcl_DStringInit(&errStr); + Tcl_DStringAppend(&errStr, "An AppleScript error was encountered.\n", -1); + + OSAScriptError(theComponent, kOSAErrorNumber, + typeShortInteger, &errResult); + + sprintf(buffer, "Error #%-6.6d\n", (short int) **errResult.dataHandle); + + AEDisposeDesc(&errResult); + + Tcl_DStringAppend(&errStr,buffer, 15); + + OSAScriptError(theComponent, kOSAErrorMessage, typeChar, &errResult); + Tcl_DStringAppend(&errStr, (char *) *errResult.dataHandle, + GetHandleSize(errResult.dataHandle)); + AEDisposeDesc(&errResult); + + if (scriptData != NULL) { + int lowerB, upperB; + + myErr = OSAScriptError(theComponent, kOSAErrorRange, + typeOSAErrorRange, &errResult); + + myErr = AECoerceDesc(&errResult, typeAERecord, &errLimits); + myErr = AEGetKeyPtr(&errLimits, keyOSASourceStart, + typeShortInteger, &returnType, &srcStart, + sizeof(short int), &returnSize); + myErr = AEGetKeyPtr(&errLimits, keyOSASourceEnd, typeShortInteger, + &returnType, &srcEnd, sizeof(short int), &returnSize); + AEDisposeDesc(&errResult); + AEDisposeDesc(&errLimits); + + Tcl_DStringAppend(&errStr, "\nThe offending bit of code was:\n\t", -1); + /* + * Get the full line on which the error occured: + */ + for (lowerB = srcStart; lowerB > 0; lowerB--) { + if (*(scriptData + lowerB ) == '\r') { + lowerB++; + break; + } + } + + for (upperB = srcEnd; *(scriptData + upperB) != '\0'; upperB++) { + if (*(scriptData + upperB) == '\r') { + break; + } + } + + Tcl_DStringAppend(&errStr, scriptData+lowerB, srcStart - lowerB); + Tcl_DStringAppend(&errStr, "_", 1); + Tcl_DStringAppend(&errStr, scriptData+srcStart, upperB - srcStart); + } + + Tcl_DStringResult(interp,&errStr); +} + +/* + *---------------------------------------------------------------------- + * + * GetRawDataFromDescriptor -- + * + * Get the data from a descriptor. + * + * Results: + * None + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +GetRawDataFromDescriptor( + AEDesc *theDesc, + Ptr destPtr, + Size destMaxSize, + Size *actSize) + { + Size copySize; + + if (theDesc->dataHandle) { + HLock((Handle)theDesc->dataHandle); + *actSize = GetHandleSize((Handle)theDesc->dataHandle); + copySize = *actSize < destMaxSize ? *actSize : destMaxSize; + BlockMove(*theDesc->dataHandle, destPtr, copySize); + HUnlock((Handle)theDesc->dataHandle); + } else { + *actSize = 0; + } + + } + +/* + *---------------------------------------------------------------------- + * + * GetRawDataFromDescriptor -- + * + * Get the data from a descriptor. Assume it's a C string. + * + * Results: + * None + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static OSErr +GetCStringFromDescriptor( + AEDesc *sourceDesc, + char *resultStr, + Size resultMaxSize, + Size *resultSize) +{ + OSErr err; + AEDesc resultDesc; + + resultDesc.dataHandle = nil; + + err = AECoerceDesc(sourceDesc, typeChar, &resultDesc); + + if (!err) { + GetRawDataFromDescriptor(&resultDesc, (Ptr) resultStr, + resultMaxSize - 1, resultSize); + resultStr[*resultSize] = 0; + } else { + err = errAECoercionFail; + } + + if (resultDesc.dataHandle) { + AEDisposeDesc(&resultDesc); + } + + return err; +} diff --git a/mac/tclMacOSA.r b/mac/tclMacOSA.r new file mode 100644 index 0000000..c994e60 --- /dev/null +++ b/mac/tclMacOSA.r @@ -0,0 +1,78 @@ +/* + * tkMacOSA.r -- + * + * This file creates resources used by the AppleScript package. + * + * Copyright (c) 1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacOSA.r,v 1.5 2001/12/27 22:46:24 das Exp $ + */ + +#include <Types.r> +#include <SysTypes.r> + +/* + * The folowing include and defines help construct + * the version string for Tcl. + */ + +#define SCRIPT_MAJOR_VERSION 1 /* Major number */ +#define SCRIPT_MINOR_VERSION 1 /* Minor number */ +#define SCRIPT_RELEASE_SERIAL 0 /* Really minor number! */ +#define RELEASE_LEVEL final /* alpha, beta, or final */ +#define SCRIPT_VERSION "1.1" +#define SCRIPT_PATCH_LEVEL "1.1.0" +#define FINAL 1 /* Change to 1 if final version. */ + +#if FINAL +# define MINOR_VERSION (SCRIPT_MINOR_VERSION * 16) + SCRIPT_RELEASE_SERIAL +# define RELEASE_CODE 0x00 +#else +# define MINOR_VERSION SCRIPT_MINOR_VERSION * 16 +# define RELEASE_CODE SCRIPT_RELEASE_SERIAL +#endif + +#define RELEASE_CODE 0x00 + +resource 'vers' (1) { + SCRIPT_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, RELEASE_CODE, verUS, + SCRIPT_PATCH_LEVEL, + SCRIPT_PATCH_LEVEL ", by Jim Ingham © Cygnus Solutions" "\n" "© 2001 Tcl Core Team" +}; + +resource 'vers' (2) { + SCRIPT_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, RELEASE_CODE, verUS, + SCRIPT_PATCH_LEVEL, + "Tclapplescript " SCRIPT_PATCH_LEVEL " © 1996-2001" +}; + +/* + * The -16397 string will be displayed by Finder when a user + * tries to open the shared library. The string should + * give the user a little detail about the library's capabilities + * and enough information to install the library in the correct location. + * A similar string should be placed in all shared libraries. + */ +resource 'STR ' (-16397, purgeable) { + "TclAppleScript Library\n\n" + "This library provides the ability to run AppleScript " + " commands from Tcl/Tk programs. To work properly, it " + "should be placed in the ŒTool Command Language¹ folder " + "within the Extensions folder." +}; + + +/* + * We now load the Tk library into the resource fork of the library. + */ + +data 'TEXT' (4000,"pkgIndex",purgeable, preload) { + "# Tcl package index file, version 1.0\n" + "package ifneeded Tclapplescript 1.1 [list tclPkgSetup $dir Tclapplescript 1.1 {{Tclapplescript" + ".shlb load AppleScript}}]\n" +}; diff --git a/mac/tclMacPanic.c b/mac/tclMacPanic.c new file mode 100644 index 0000000..d916cab --- /dev/null +++ b/mac/tclMacPanic.c @@ -0,0 +1,172 @@ +/* + * tclMacPanic.c -- + * + * Source code for the "Tcl_Panic" library procedure used in "Simple + * Shell"; other Mac applications will probably call Tcl_SetPanicProc + * to set a more robust application-specific panic procedure. + * + * Copyright (c) 1993-1994 Lockheed Missle & Space Company, AI Center + * Copyright (c) 1995-1996 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacPanic.c,v 1.6 2001/11/23 01:28:08 das Exp $ + */ + + +#include <Events.h> +#include <Controls.h> +#include <ControlDefinitions.h> +#include <Windows.h> +#include <TextEdit.h> +#include <Fonts.h> +#include <Dialogs.h> +#include <Icons.h> +#include <Sound.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "tclInt.h" +#include "tclMacInt.h" + +/* + * constants for panic dialog + */ +#define PANICHEIGHT 150 /* Height of dialog */ +#define PANICWIDTH 350 /* Width of dialog */ +#define PANIC_BUTTON_RECT {125, 260, 145, 335} /* Rect for button. */ +#define PANIC_ICON_RECT {10, 20, 42, 52} /* Rect for icon. */ +#define PANIC_TEXT_RECT {10, 65, 140, 330} /* Rect for text. */ +#define ENTERCODE (0x03) +#define RETURNCODE (0x0D) + + +/* + *---------------------------------------------------------------------- + * + * TclpPanic -- + * + * Displays panic info, then aborts + * + * Results: + * None. + * + * Side effects: + * The process dies, entering the debugger if possible. + * + *---------------------------------------------------------------------- + */ + + /* VARARGS ARGSUSED */ +void +TclpPanic TCL_VARARGS_DEF(CONST char *, format) +{ + va_list varg; + char msg[256]; + WindowRef macWinPtr, foundWinPtr; + Rect macRect; + Rect buttonRect = PANIC_BUTTON_RECT; + Rect iconRect = PANIC_ICON_RECT; + Rect textRect = PANIC_TEXT_RECT; + ControlHandle okButtonHandle; + EventRecord event; + Handle stopIconHandle; + int part; + Boolean done = false; + + va_start(varg, format); + vsprintf(msg, format, varg); + va_end(varg); + + /* + * Put up an alert without using the Resource Manager (there may + * be no resources to load). Use the Window and Control Managers instead. + * We want the window centered on the main monitor. The following + * should be tested with multiple monitors. Look and see if there is a way + * not using qd.screenBits. + */ + + macRect.top = (qd.screenBits.bounds.top + qd.screenBits.bounds.bottom) + / 2 - (PANICHEIGHT / 2); + macRect.bottom = (qd.screenBits.bounds.top + qd.screenBits.bounds.bottom) + / 2 + (PANICHEIGHT / 2); + macRect.left = (qd.screenBits.bounds.left + qd.screenBits.bounds.right) + / 2 - (PANICWIDTH / 2); + macRect.right = (qd.screenBits.bounds.left + qd.screenBits.bounds.right) + / 2 + (PANICWIDTH / 2); + + macWinPtr = NewWindow(NULL, &macRect, "\p", true, dBoxProc, (WindowRef) -1, + false, 0); + if (macWinPtr == NULL) { + goto exitNow; + } + + okButtonHandle = NewControl(macWinPtr, &buttonRect, "\pOK", true, + 0, 0, 1, pushButProc, 0); + if (okButtonHandle == NULL) { + CloseWindow(macWinPtr); + goto exitNow; + } + + SelectWindow(macWinPtr); + SetCursor(&qd.arrow); + stopIconHandle = GetIcon(kStopIcon); + + while (!done) { + if (WaitNextEvent(mDownMask | keyDownMask | updateMask, + &event, 0, NULL)) { + switch(event.what) { + case mouseDown: + part = FindWindow(event.where, &foundWinPtr); + + if ((foundWinPtr != macWinPtr) || (part != inContent)) { + SysBeep(1); + } else { + SetPortWindowPort(macWinPtr); + GlobalToLocal(&event.where); + part = FindControl(event.where, macWinPtr, + &okButtonHandle); + + if ((kControlButtonPart == part) && + (TrackControl(okButtonHandle, + event.where, NULL))) { + done = true; + } + } + break; + case keyDown: + switch (event.message & charCodeMask) { + case ENTERCODE: + case RETURNCODE: + HiliteControl(okButtonHandle, 1); + HiliteControl(okButtonHandle, 0); + done = true; + } + break; + case updateEvt: + SetPortWindowPort(macWinPtr); + TextFont(systemFont); + + BeginUpdate(macWinPtr); + if (stopIconHandle != NULL) { + PlotIcon(&iconRect, stopIconHandle); + } + TETextBox(msg, strlen(msg), &textRect, teFlushDefault); + DrawControls(macWinPtr); + EndUpdate(macWinPtr); + } + } + } + + CloseWindow(macWinPtr); + + exitNow: +#ifdef TCL_DEBUG + Debugger(); +#else + abort(); +#endif +} + diff --git a/mac/tclMacPkgConfig.c b/mac/tclMacPkgConfig.c new file mode 100644 index 0000000..6b28d3e --- /dev/null +++ b/mac/tclMacPkgConfig.c @@ -0,0 +1,107 @@ +/* + * tclMacPkgConfig.c -- + * + * This file contains the Mac configuration information to + * embed into the tcl binary library. + * + * Copyright (c) 2002 Daniel Steffen <das@users.sourceforge.net> + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacPkgConfig.c,v 1.2 2003/06/09 22:49:29 andreas_kupries Exp $ + */ + +#include "tclInt.h" + +#ifdef __MWERKS__ + +/* define DEBUG/OPTIMIZED macros depending on the value of + * Metrowerks specific precompiler functions. + * (traceback is for PPC, macsbug for 68k) */ +# if __option(traceback) || __option(macsbug) +# define TCL_CFG_DEBUG +# else +# define TCL_CFG_OPTIMIZED +# endif + +/* define PROFILED macros depending depending on the value of + * Metrowerks specific precompiler functions. */ +# if __option(profile) +# define TCL_CFG_PROFILED +# endif + +#else + +# ifdef TCL_DEBUG +# define TCL_CFG_DEBUG +# else +# define TCL_CFG_OPTIMIZED +# endif + +#endif + +/* the CFG_*DIR values need to be built up dynamically at runtime + * because the name of the Macintosh Extension directory on a user's + * system is not known at build time */ +#define CFG_RUNTIME_PREFIX "${::env(EXT_FOLDER)}Tool Command Language" +#define CFG_RUNTIME_LIBDIR CFG_RUNTIME_PREFIX +#define CFG_RUNTIME_BINDIR CFG_RUNTIME_PREFIX +#define CFG_RUNTIME_SCRDIR CFG_RUNTIME_PREFIX":tcl"TCL_VERSION +#define CFG_RUNTIME_INCDIR CFG_RUNTIME_PREFIX +#define CFG_RUNTIME_DOCDIR CFG_RUNTIME_PREFIX +#define CFG_INSTALL_LIBDIR CFG_RUNTIME_LIBDIR +#define CFG_INSTALL_BINDIR CFG_RUNTIME_BINDIR +#define CFG_INSTALL_SCRDIR CFG_RUNTIME_SCRDIR +#define CFG_INSTALL_INCDIR CFG_RUNTIME_INCDIR +#define CFG_INSTALL_DOCDIR CFG_RUNTIME_DOCDIR + +/* use system encoding */ +#define TCL_CFGVAL_ENCODING NULL + +/* We want to include tclPkgConfig.c to get the remaining CFG_* macros + * and the cfg array declaration, but need our own definition of + * TclInitEmbeddedConfigurationInformation, so we rename this routine + * for the duration of the inclusion and declare it static */ +static void TclUnusedInitEmbeddedConfigurationInformation + _ANSI_ARGS_((Tcl_Interp *interp)); +#define TclInitEmbeddedConfigurationInformation \ + TclUnusedInitEmbeddedConfigurationInformation +#include "tclPkgConfig.c" +#undef TclInitEmbeddedConfigurationInformation + + +void +TclInitEmbeddedConfigurationInformation (interp) + Tcl_Interp* interp; /* Interpreter the configuration + * command is registered in. */ +{ + Tcl_Config *cfgp; + Tcl_Obj *valObj, *substObj; + char *subst; + int len; + + valObj = Tcl_NewObj(); + + /* Call Tcl_SubstObj on all values in the cfg array and replace + * the existing value by the result if any substitution has + * occurred. This is needed because on the Mac the CFG_*DIR + * macros contain variables that are not known until runtime */ + for (cfgp = cfg; + (cfgp->key != (CONST char*) NULL) && (cfgp->key [0] != '\0') ; + cfgp++) + { + Tcl_SetStringObj(valObj, cfgp->value, -1); + substObj = Tcl_SubstObj(interp, valObj, TCL_SUBST_VARIABLES); + if( substObj ) { + subst = Tcl_GetStringFromObj(substObj, &len); + if ( strcmp(cfgp->value, subst) ) + cfgp->value = strcpy(ckalloc((unsigned)len+1), subst); + Tcl_DecrRefCount(substObj); + } + } + + Tcl_DecrRefCount(valObj); + + Tcl_RegisterConfig (interp, "tcl", cfg, TCL_CFGVAL_ENCODING); +} diff --git a/mac/tclMacPort.h b/mac/tclMacPort.h new file mode 100644 index 0000000..482f6e8 --- /dev/null +++ b/mac/tclMacPort.h @@ -0,0 +1,279 @@ +/* + * tclMacPort.h -- + * + * This header file handles porting issues that occur because of + * differences between the Mac and Unix. It should be the only + * file that contains #ifdefs to handle different flavors of OS. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacPort.h,v 1.17 2003/03/03 20:22:44 das Exp $ + */ + + +#ifndef _MACPORT +#define _MACPORT + +#ifndef _TCLINT +# include "tclInt.h" +#endif + +/* + *--------------------------------------------------------------------------- + * The following sets of #includes and #ifdefs are required to get Tcl to + * compile on the macintosh. + *--------------------------------------------------------------------------- + */ + +#include "tclErrno.h" + +#ifndef EOVERFLOW +# ifdef EFBIG +# define EOVERFLOW EFBIG /* The object couldn't fit in the datatype */ +# else /* !EFBIG */ +# define EOVERFLOW EINVAL /* Better than nothing! */ +# endif /* EFBIG */ +#endif /* !EOVERFLOW */ + +#include <float.h> + +#ifdef THINK_C + /* + * The Symantic C code has not been tested + * and probably will not work. + */ +# include <pascal.h> +# include <posix.h> +# include <string.h> +# include <fcntl.h> +# include <pwd.h> +# include <sys/param.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +#elif defined(__MWERKS__) +# include <time.h> +# include <unistd.h> +# include <utime.h> +# include <fcntl.h> +# include <stat.h> + +#if __MSL__ < 0x6000 +# define isatty(arg) 1 + +/* + * Defines used by access function. This function is provided + * by Mac Tcl as the function TclpAccess. + */ + +# define F_OK 0 /* test for existence of file */ +# define X_OK 0x01 /* test for execute or search permission */ +# define W_OK 0x02 /* test for write permission */ +# define R_OK 0x04 /* test for read permission */ +#endif + +#endif /* __MWERKS__ */ + +#if defined(S_IFBLK) && !defined(S_ISLNK) +#define S_ISLNK(m) (((m)&(S_IFMT)) == (S_IFLNK)) +#endif + +/* + * Many signals are not supported on the Mac and are thus not defined in + * <signal.h>. They are defined here so that Tcl will compile with less + * modification. + */ + +#ifndef SIGQUIT +#define SIGQUIT 300 +#endif + +#ifndef SIGPIPE +#define SIGPIPE 13 +#endif + +#ifndef SIGHUP +#define SIGHUP 100 +#endif + +/* + * waitpid doesn't work on a Mac - the following makes + * Tcl compile without errors. These would normally + * be defined in sys/wait.h on UNIX systems. + */ + +#define WAIT_STATUS_TYPE int +#define WNOHANG 1 +#define WIFSTOPPED(stat) (1) +#define WIFSIGNALED(stat) (1) +#define WIFEXITED(stat) (1) +#define WIFSTOPSIG(stat) (1) +#define WIFTERMSIG(stat) (1) +#define WIFEXITSTATUS(stat) (1) +#define WEXITSTATUS(stat) (1) +#define WTERMSIG(status) (1) +#define WSTOPSIG(status) (1) + +#ifdef BUILD_tcl +# undef TCL_STORAGE_CLASS +# define TCL_STORAGE_CLASS DLLEXPORT +#endif + +/* + * Make sure that MAXPATHLEN is defined. + */ + +#ifndef MAXPATHLEN +# ifdef PATH_MAX +# define MAXPATHLEN PATH_MAX +# else +# define MAXPATHLEN 2048 +# endif +#endif + +/* + * Define "NBBY" (number of bits per byte) if it's not already defined. + */ + +#ifndef NBBY +# define NBBY 8 +#endif + +/* + * These functions always return dummy values on Mac. + */ +#ifndef geteuid +# define geteuid() 1 +#endif +#ifndef getpid +# define getpid() -1 +#endif + +/* + * Variables provided by the C library. + */ + +extern char **environ; + +/* + *--------------------------------------------------------------------------- + * The following macros and declarations represent the interface between + * generic and mac-specific parts of Tcl. Some of the macros may override + * functions declared in tclInt.h. + *--------------------------------------------------------------------------- + */ + +/* + * The default platform eol translation on Mac is TCL_TRANSLATE_CR: + */ + +#define TCL_PLATFORM_TRANSLATION TCL_TRANSLATE_CR + +/* + * Declare dynamic loading extension macro. + */ + +#define TCL_SHLIB_EXT ".shlb" + +/* + * The following define is defined as a workaround on the mac. It claims that + * struct tm has the timezone string in it, which is not true. However, + * the code that works around this fact does not compile on the Mac, since + * it relies on the fact that time.h has a "timezone" variable, which the + * Metrowerks time.h does not have... + * + * The Mac timezone stuff is implemented via the TclpGetTZName() routine in + * tclMacTime.c + * + */ + +#define HAVE_TM_ZONE + + +/* + * If we're using the Metrowerks MSL, we need to convert time_t values from + * the mac epoch to the msl epoch (== unix epoch) by adding the offset from + * <time.mac.h> to mac time_t values, as MSL is using its epoch for file + * access routines such as stat or utime + */ + +#ifdef __MSL__ +#include <time.mac.h> +#ifdef _mac_msl_epoch_offset_ +#define tcl_mac_epoch_offset _mac_msl_epoch_offset_ +#define TCL_MAC_USE_MSL_EPOCH /* flag for TclDate.c */ +#else +#define tcl_mac_epoch_offset 0L +#endif +#else +#define tcl_mac_epoch_offset 0L +#endif + +/* + * The following macros have trivial definitions, allowing generic code to + * address platform-specific issues. + */ + +#define TclpGetPid(pid) ((unsigned long) (pid)) +#define TclSetSystemEnv(a,b) +#define tzset() + +char *TclpFindExecutable(const char *argv0); +int TclpFindVariable(CONST char *name, int *lengthPtr); + +#define fopen(path, mode) TclMacFOpenHack(path, mode) +#define readlink(fileName, buffer, size) TclMacReadlink(fileName, buffer, size) +#ifdef TCL_TEST +#define chmod(path, mode) TclMacChmod(path, mode) +#endif + +/* + * Prototypes needed for compatability + */ + +/* EXTERN int strncasecmp _ANSI_ARGS_((CONST char *s1, + CONST char *s2, size_t n)); */ + +/* + * These definitions force putenv & company to use the version + * supplied with Tcl. + */ +#ifndef putenv +# define unsetenv TclUnsetEnv +# define putenv Tcl_PutEnv +# define setenv TclSetEnv +void TclSetEnv(CONST char *name, CONST char *value); +/* int Tcl_PutEnv(CONST char *string); */ +void TclUnsetEnv(CONST char *name); +#endif + +/* + * Platform specific mutex definition used by memory allocators. + * These are all no-ops on the Macintosh, since the threads are + * all cooperative. + */ + +#ifdef TCL_THREADS +typedef int TclpMutex; +#define TclpMutexInit(a) +#define TclpMutexLock(a) +#define TclpMutexUnlock(a) +#else +typedef int TclpMutex; +#define TclpMutexInit(a) +#define TclpMutexLock(a) +#define TclpMutexUnlock(a) +#endif /* TCL_THREADS */ + +typedef pascal void (*ExitToShellProcPtr)(void); + +#include "tclMac.h" // contains #include "tclPlatDecls.h" +#include "tclIntPlatDecls.h" + +# undef TCL_STORAGE_CLASS +# define TCL_STORAGE_CLASS DLLIMPORT + +#endif /* _MACPORT */ diff --git a/mac/tclMacProjects.sea.hqx b/mac/tclMacProjects.sea.hqx new file mode 100644 index 0000000..178f9d6 --- /dev/null +++ b/mac/tclMacProjects.sea.hqx @@ -0,0 +1,3759 @@ +(This file must be converted with BinHex 4.0) +:%R4ME%eKBe"bEfTPBh4c,R0PB3""8&"-BA9cG#!!!!%Z[J!"NlA,LP0dG@CQ5A3 +J+'-T-6Nj0bda16Ni)%&XB@4ND@iJ8hPcG'9YFb`J5@jM,L`JD(4dF$S[,hH3!bj +KE'&NC'PZFhPc,Q0[E5p6G(9QCNPd,`d+'J!&%!!",Vi!N!0b!!%!N!0bca`0TD9 +5CA0PFRCPC+@P!+@3"!%!!$i!4,GD)G+jbMBk!*!0$Jc@!*!$e!!Q*!F!!5hU!!* +dBfa0B@03FQpUC@0dF`!!#f3#@3&G![-$"J(!rj!%!Klrq2r`bd!!!)!!N!3",JZ +PN!3"!!!i!%5fm8lcY[&2-`#3!h)!!5fN!*!$FJ!)#A-!!!%`!#BL)3!",(3!!Ld +q)%*eD@aN!!!A8J"&!#)"8`*#!F$rN!3$([rirr$,4!!!J!#3"k@3"!%!!$B!4,3 +GFA1i)pb!!*!$e!!",)d!N!28!!CX5!!!!BS!*L!l!!%V!`!),6iJG'0X!!#-6J" +p!"!"f3(d!F$rN!3#([rirr$,3!!!J!#3"k@3"!%!!%!!J,B'`L+hF!IL!!!"-!! +!)5`!!!%`!"#N(J!#@aS!!"mq!*!%$`"#6d&I9'0X8fKPE'ac,Xq!!!"!XNe08(* +$9dP&!3$rN!3!N!U!F!#3"N,"e'PFK"b(4aK@[R1qJ(P@i%H83GqET6BT*14UHT, +eK$hlfi9(H0B6dI!$U#L1N8h0Cq2k3TGqDC@Vm@q&Q`J@9lZrhZCT8r+TZ$)k8++ +`8fpXhmSZf!"JIDUP)VGk*fJIQeXaYV@6!j!!8jZi&[XVa+YZ4J!'$$#05VZ6LBN +H%K++"cX0PA2T`+8mhb"PQqPfA-PJN!#P,fj)deA`mk95kQPPLSmUV*&SQfY6VS9 ++[h9UI2`FS"#!A#9'H'3E"PX[YZR"kNXXmL6'lU[,'Pb9M!+dP0,06aAeV!lIhTT ++G0K+l0RbT6mX(&T'mabLP"a2NrJ*qG`+IS*'mKCHZY*5NQ+PEIE"&E1`,F*H0VJ +(ZB(BYiLJmIJDpU*#meb#JPqY!1q)-RE&EB&8#2YD$k'@eV119U'f6HR%q%B&KVZ ++Xh#p99K4kd'DkQ!6-jIVe6qH@U)JNYGb0TBUGXF4b*T@S*!!ec@'8C3cS8cSXP% +V'NDMlL!B@G'%1(5*4l)pKZ3S&lCArdL%*&6H6pVcd'"j-$L*Hh#RlGaLJe@#YQ) +iq'Q)J5JdA*+e0ljlXI)3KVmY8i2FB6MBrPq(M#[qjC+%3DL9E$2)K90Qr20HB53 +cjUQjK2'"jSCpme@6YT*@0(Q,+P1hEZ3b1@ZjcDB60jph83pRb5X%Sk@fQJV@Z2i +K&)$`9$+8Y"TDdef4l30NZU@ZUAY&C)Urhd"%ff&(P`-JEBT-fbqDP%@VHGNFFR' +L*Blb4R0GQV9BjQ'pYTf2UZ#%fM063U0PQGdLrl1Qrc108I,)#!pCjX([LC!!fCp +[8+4QR@Z0(P3SjDmGN`b(RMDR-f*dX(96bf0V&U*)+C+9'ljS'&c#rp-*)B2AmeZ +E&1!39hic*VHTLp[bY5XjZ%-PCE)V8*Qi#EMB#LNYBJr'&XhKrVrNQK!c$+k&KUA +keiV"cKUC[kL1pAAhL2d0&AUQNV'imp#25d1K4Sm@imRTD[RB2XfiFJ(`@'CrKEY +$4eKCJ!A%kmj`#L)*jR6)*15r8&&S@+FX-jPF-kl(SrhD'G2)bYIrj*0SlJMZGYK +ZpFd$j*T#`0beN!"mhRqZh6,T2q"hI@f3!+"(,SCA0%pSU2c@#EYS@0Thh9Bml"5 +l4k5$eA-RqfHqU!plEV"[pGQ"H98*rBLXEe)NjDJ"+d3TlbN3,Z0Pdh!V-HEib4C +#TKZ05*KJhEaM,(`,V$&0N!$!YJlr22fi"VE'[-LeTF9E51c91,G4mBSH(U4+fE3 +j#QJNH[YPVMm"G-c5A&kFfE(CPDHVfB"-`1N19a#`-h4-3F!)Br'[Gm6K*ak8LEi +@!K8bjB1*iQ!2KJNIj*YLZ!hRP"dZemV-c"#fj&h+%-)FTU&Z-RLl4ZF0,[IXX9I +RJ,qPYL0kS5`JfSqUJ@Ad[1HKYiLGh*Lc[a)8%RL)!pV5Th0S08qjr"S8apHRq54 +dX#`ZX",N3!AEi1"FR1TrrI%q8+2Fq+`,aQQD#k$!L(4k#*XVb1ErG!QpY5$6SY@ +d83ePf"GfB)@q[Bmrk(Pd8NqdT`-b*)'P",BcRi3!im(I)d0PSJa82iTKGHm,krb +ir*Z3!*iJZUXH5X3Mhrpam#lXmY$FmH"9fASdSAM+hE(QdmPZYeXcVX&JU$V!00p +H0HbI"0[5lJ&eBKYfX!+a3B)!3I-B&fr[f-fS6T@ilrP8fp)KlJli*KPTbM5*SjI +Bp8S$)LP$JPX+`J*+Qh+B2XhI`9XcbAREbp!!r29CCbK)$kXqqq"#C@[-T'$e!HE +C"qSB!dMC(Z9bK-5rpmIi+))YXqL'b'[8Y&&-MEXDQh`+)HD[!'kULh!Sl6Me1TB +NBfTI*Bpra%@iK*pp*l1KE)fQkV6GD!pl424(R-`3XLpNb6"[Ci,bE%R`&(#I$,( +G'`pGrblMK,CiJ[-9RemS9(b9315)XpdRhPck45XmEklB*YQHQ#qYG5V`aB#lN@K +4UjhrPULm'LSG"*dq@1kS03fSJ6lF56dU4Gd92DAAl0GECdq#A(T3pB&UC"!N3m4 +VdH(1Fh5E!P1GP9FcXe$Ed1p`R06QMhNl6hlm61`JV8NQ[(V!($6bbfV#b"EaS)6 +5VDV!VPfCC45lFGc-YJ9)E[$"4G6,GkJ,HJ8KlR8%j*T,T(ZD8cZecZ-60@dS8aR +h'M08'Kpdi-#120URl([H(#1qE`!$6b0U"Fj[J2F*pG1B`(KGaf3QF$*d9j!!"X@ +fPGM&4,5K9GI$bM`Q-+d2Nmlp)J$#GcU&JX8Ea5cp&$K3F&#)+96Tf'C193lK2A! +c'R&&Acq5J,94KS3AAUB#2Nh#M#61jH[H+#@Sb))2-5rB-SbMSbemZf(eC6aLV-* +%F6PSG(cNU*GZkQReYb2-5GCfUVce[iY0X4"T)aJcP52fAmaB94K&[P9P09Y6dZN +e00S-GlL3!#aX"6P@,RZ!p1LSC6B!NFT0E)5ei!(#Dl1qAK[U'J8HpbPRd$RdhAJ +cR*HR0LQV`$,)iQ,aC`$a46RhUZ9RiCbE*0&&(QGpC655UUcb1$Z!D!XGZPYR+q, +E2[l4*l@3!(VfJHR4hl-5KBbX$LDLc$MK0GdpN!$+@ridD@EajBq&6rq)Q@DK&%j +K,I!Jhd1A,i4Vb$D9YHUhPmAJZfq"3)(kQjZQI*-HfNc*S-RkZH5A4QACbEAl+Uk +h&[m[8))UAFeBJVc64BaQdk00%N"lrDUd!D(A'3,!N!"4NIlfkaD0RDT82GD"9kL +"6*GB8kpd3N4`SdQk&RcE$,'VU$mFD-d)3"RG5ThL&P'iZd!lD9C$9G12@U[l6*V +A6RK#j'DC`M2Kc3a9T&bJGKqKm#i$+Q@#kj!!F'dq90UYjPC"6Kcj!jqk@VBqZIh +N"K2QNNCH5V3EcY69@HZB(Sh$$-Lir3RUPGf,`Xmk81a"bF!'c8-r+eS*C%34@mZ +PZP"Z)'9c$)-59'ePaa[V*mBk1FX84f-bBVCG&S-dB(d%YJaS5L)#AN#IH#q21Hh +kc$RE`,(Pf*`3"J)Zf$CT+5e-+ERZ%KGFZCihV+P2qI5k*PhJ98SAecDc(Ub)I[5 +#Vr8LpT!!i%bEZNVmC!EDPjq!U8R2!A-qDhN`rLV"iEF#l'D$`AqdYY2[a0AR"Ne +b5m1qE(U!qM&)SHapc$lTCQPT$X%$qMhTJ8EBFQA$e1)TT9!N0P"m'8-p0a5h8`@ +ccr6KA@l2-e-3``!iBic*0*3(Z!m[H#3#qj6EMe2QX1A4D6-53j1HkBPV-q-24@a +"b)$*eTG*UKN8+4,KkN)5U"%cY"diF2HI#[jrTJ*[*NZ"PAZ"B@94MHDmZ'(-[(8 +$+l@`68[pqhRjF)U$m[dadi(G$CRL-X$UX#D3!2F6M'6rB-#20A#X991hMS+-*S+ +B%5F%YIfaYG9D`U(FN!!GYk8km`)rX%HVE-SMIXZGZfk92Zb9JFKY5Cd`Up[1dBH +rKlXHf#MZ6*[GbhEX[G30`G1rPK3`)A"`0d-[,"Q&9PBjm8q"TH'J'UGG`H"U3b( +IC%RXa&Ti4UF4QF`hM0LL4V42VGK5NM*TPZ%Q6GcD&eeR84UN6@8'cTBeDmp9SUj +RP9I%A#3Y1KN)b6MFKENMFHcPl)FDNj(F8jm`qTV3LLK`j#f%U!!99jIPYU13!%a ++8#daH881[H'l6kcZ91#6a01ljIA,(Hrr+qFB1cLHPTB*1riNa)kaBGb!9)TQ[IQ +$DVUhS*(+lKAdek,Mr8QT#IaA%#qq!S%qCrEhQidQFZPE#V1p(ZC6`4LY!c*dUE1 +QmM9JNTh2bpTG$k0-m3``UBCH9rB#0$GRBqFAa1bHaP#qUiV&PXI#l(i+3L+2Ve1 +)BQlLR0KQ"J#r4l*fAEF1U`$CL@dikV!1QN6QiQ&FNI(VeVSkQFd)drG'98E%@dY +pT3bIr2K38'dLG#-P552[pakjcH0dKEMfCl'imC'fU`jAQQqKbbRj[l0aNXmr$[A +18i28&9NmC(hKSc0jBP1MB&U`f,Bq04!jeXPELr,XV*RXLNbNbrJcVE[FP4RF`f( +,4PHXpUe!%dNpCDRQ+Nql$P5V3f5CF8%HMbE"h#K1'qljV#bSYJMH8%-YR1)Jr)6 +mkdPmb8r2PH[8A&9J0)!`#hm@IUN',)dmV)2G&cL%!VfA,+Ra,q8,Q0de6Kr1Ne5 +l9fe*GA5e+(@ZLG*"K-h2bq`4C-+E+m#e#1G!,%p13RIPJ5C5eYHKf+JTYJ&rjNV +CY*X$Ak+8QFNGk`U&VCa39K[p4i,[@&Aer#iQ%AHUc2e+-*U2,FK%[rfrd+I3hC3 +[lDBU)3[`F!CICC!!*Jbmae9JllV!$88%jQ$8!R&TZM4`iI#H#!+db`Fe11'T"UI +C(IVDJ-4+C3Z"6*lp&VKKD6DrHb[QrcDD8(0!q`dL++Ir(8dhh*CXpYFSelRi!4` +LU6P`MFA@mX0CRS4F4G'SaV9Q%&B$+!+eCSmk5ef'Yl,9AV19X$NcY(%$[G"Y4Rr +k@rrM1R,P(H3rFpq"h@D)--rE-+K`J-hBk-j(cd&ekU"A#B4#L9lh0*AAdEdPS*0 +B[BhD1QFTD+V5hbB2SVN*!S`HQKG5YN[-JUf+#e[Z$HdeBDahibjlZ3Nm1e)r6-F +dXVfLm%me*2G`3AZ&!h9ZYpV,V$H*'QY2%k(BRRpXa$J'H1c@[b-ra6pLJ%fjVa3 +19)QNHTr'6"0cEUpFVP)fc8P(K")CTXSLccLmLkRV)4f-Z(c2q-E6F'JMbpEr+Y[ +,!kLIePjSP2[%-SScFpYf!h-$ZCJ2PBeE"Q$Q3IjYa(ld2Tj%R@i+3p*-H@R-edJ +(5&AqSr#kdilSISdh'I8+0I"a*NHZ#1d3[M8p(&*+J',UP*,8Z&1!Q%kJqJaXHkR +0+NZKC`ACSC5F2&*NUjQPCq8jGUKSlG"9f5$p&Zq[NrMbX&$!Hf$jQQM5UY%5!h0 +H#blD1$Z8h%[AMe#L)YYH'q5m%*cZMEL'`VF`N!!Hj6"a%QBV&l#D#LK'*9NKeB3 +NT3hQ4R%c#N@4Q!18R#1X6&'%406jVQRR6[@BU))-+V$8-"VFT&U3!!3%ZZdQSMr +TmR3*V`%CKPQk&8%'E&h-Tb46R&*"b(DBr#m0a*VE%Gp9FE-YIYF(aE)[601l,XK +)XhKLl-'A`"'UAN6hkZB@@&S0kH$1C$E@FXNKGB,aL)6S!b1hVG8#kG3604AdCUJ +e#R+U"*%3SPjZ1RCM%Qa4[KP,Mci8bb(J5P2c33r0,GQJI`(NfMPl%S-iQK(N+h3 +h&PRG*B02A!!iKI,m!B&!2Umibm(HJdT)V998H*)&#jl-F6f'(b@T(FpC&f1q[e# +eDH`[PPpilM+CV$NR$1IG+XpX0VG9m*6)K`3LlVV,"kG%*m8ij4K%cK1!1*LaR@C +f0,8KLN%TVNQ!p2MdMbQ',J8Kq#i1'Z5lGbA@I"Fq+KY!hHUp&C-)-U['9%"8EK6 +VGe3dP-VkZi,qG'U3!2f5&&!)Tm)b[)S#C6Gp`Feh&)&0hY@$"m8IdqdU8Mh&X1P +$kdP6K,cGrlf6"8PebC9YaTRjiV[5KL+6L5X(!Q!hpK+!P0()%VV,IdiFH'mV&9k +XLcJib'#9FjCpUk#LF)U+6"8$AA@BS*6qf1dCIhK"rV`m(5aIRZ-U'HELMi4BG*q +!#Xr8j'Cb1Y(CSTY'DQDQ9@FH%0efcDYHZ,$bq4X8JjfSFA)-E!Xp',FkZ1NhK32 +MSY"(b@pGJRTDR+HV0*CJ!d4CVi@m[A$m#5"$L4,N,#dGa9i@U,)a"VTp,p#)k)L +k8%-U8(+BV*')2LreL*b1)%iCXB3rbGI#-GA")Q!IBMPDSj!!dA$R3rp0&Le"`-J +("XCfT8i'C6[5V+B3'QlG&pQKBB[[!h!S`1C)LYkXl#2QT"[r%Arb)qRcC6*`5r! +kS#PLI`-)aa1L+$*BEa)RqTAGpe-jQ2eZ9MMm#MfLdN2YId[Q$l+-9IbqUZ31#EC +(l`MAp5`V(29c-pPVGN0fapQ!`PkFGT6QADT"JiaeVQ&R3,3(c2aidmXDCD,6f,N +d(AAk0qCjaS#(8$FeM(%ES4$YdKa(!TL5Th,qZr6AHkCRjXPl+j(ZbR*P$N`HQ5T +J88#$FGUdXR`B,G8l+bBXlTK()[[c4M'D5X''Rjh)f*E56FlH&ia[!j38"CZL33m +CN@"+)hLRCXjDLDlT2!VkQ"N@+a!ES9N"qSAQ@'G6!b2*aA5SSGHcbiL`ZmXRFCK +!$D0dj@&#NeY4qI2r6F)#N!!B#-,JfBXq5f$G+2mk6XBR2XYS,kfQQP$SQP1"8eS +9FpeB2BFSY54FK1-9YqH3!2!PY*K[Xr$HkPR8+qJkCdK%G3a6@$F)EB%4Z!5I@1R +%'MDUbEe3VhE6I*`0VK9+SPb+V@`2KbL,q)'mhlZNJ%`5RVK2Q#3!!rN4Ck-)ej[ +Z%jQ0FepC8qm@RTRNFcH@VMSAbI0r[Xk6pk3D8-i2e4&,Ia2lR*j$clBb(M1RdQA +CC4K[-$J(AV5TaQP"4Ar,4F&UB$f4#kim6#!K8HpNA[$'`4b0iG,Q!PT'+I(LKK* +D2!cHl0%KT'6$(TbHE9(jZ*p%qF3(+RCp[Qrm'BA(ml9(F%LXBHZ(DNXchNAVpQQ +XE@H*3(1Jb%l5l5cTFEXV+J#aI&#,LQiml2hjX3HffH&AV3MJ9rkG@2Y-aJ01UTa +rqVFiHrZ6Gp0U[lX(442LXh9%[paqqY%dkJ3iPH"VEF@kK#adN!#@cDSh3'LMZ8A +'(,6"4$(H[#l$NYk+3q$YKN9$9NR1ejJ6G8j9%5Pq,PSA2TSPr`*H&PjAHL1J"Z( +K2QAR4Sff%eb8"$iETR,+TL+lF@cVNKP6Q!ZJr9'h[hTaSq!+SQIaB+bD"+6V'L, +!V-aZ$!Hmfh(eJ9`Ua5'E,#)bGi32!eadACE!CBBY#4&+FGahJj&'%P(pe%8'(9b +M%M6a*8H4+Sk("H3lpP6DFV8mN6jk&JV"0M@rkhMaJNm)e%N6jC!!8CSp'0C*p5d +XZmUIU5SQIi-A8-q'0Ab!CTrKa`Z3!$I#G!+fSTVXT*)Ui6[RHif%9r+-ZCRm($A +'DEF0f-ZVC6iDZJ93,09XU+TEDV3ZfbpYI@[PB2BKHdia+e3!GG*`bB*-+VJfpNY +m[,N3i#deR(p,A[L(C$Jd'Ic+EDh18Zq"jIKLGNildLSNFUidB-P)e3FV64Tjc#) +U&H25QYI!-i*'`fb((aTHPT80dM3U3h$C+HJUK&rSphAmA5KD6UI'TFq!k4cbk`a +YfIN!,a3hK@MmZ'NX)8cR8qCk-q#BG)eR4Z6mM&%XBR$2!kU8L@U[fAF*9SF"R+R +R40M1mCfXI(kd4QA(ld$j66c5LVAU0KNPV(0m9[q$i!j35faRT,mqN!!RR,c)[48 +j$%0!H@IbK&BVH8TqS'*BB%Y6hI6lc*R%Y19R-!#J(`kG*dp94ELNbr*$T5VSEhh +9HbqX+'Rl+5BCiRk%2j)"6Jq&XkQ&96$da3YV#bRGrJ2clF8jFHjHrL")Bh$IPPI +T6bd90&'[2c![3NLFZDRplq+*T[r1[(DRL1,eiEG"ESa19AeRIT!!p8-Z!ie[NDB +HpCYhjqM-Uck&f4rd-&Si*N"4!3Dii-9d0H-M'C*lMMQJ)KkB`PGqUN*V-CN3lB! +NLP5PfQJ+[cdH,eZ)ADh@$Hr@VYI0N!"69[q+(,VX%JpdhQJ-@UHbjIPG*b2Xjk0 +DH&I5YfZpaf!Q(-6S2'[IES5@X8#EBZ!8F@(YaF@0Q(A,+ah"!ZHDr-*2p$XfXkR +QS%rT4)Ic*X%&(Z$1rmI[V1d`&e$@AK3LP'HDP03!4G0@TBEMTqbHeb'U#9*9mGH +pBr6EZ$@FQIpC,MkLRNpB2elHKh$KPl`5pFM&kCdKIEVY))0GGX,X5-Ll1,-dfr" +,q,JQA#i((VQbFlqq+H@2BKF-Kip8bl[%KcK%684+-d()EJAc(&%Gk*IK4UbNh3* +h[#9NPm4AbH-%*Z9d+`TFpRA$j!8HX%EIri`,PRcXYTp*+Cq)ElSfc2&IRUa+edq +@MSLU@+l#j`H-(YrbM8,`"Z8@MCVh%AG!e19"*D1-1Pcj'#+"3,0(`q%C@,20QK` +YhU*prPL21,Jj4E0D`CQbeYMAb-*C)U3SjP4`30cSrY1U`l@IATb+m3Q4,@hFMIZ +!USPPf9Z$,fKj*eAeb0h1$`KFm5iRJ)4,HN`Y"BfT+TKal6p31P(4Al)TiC[da&H +!T"L@f,NmiYG[AT[fQJ'P4a2mPh(6%X"C9$"eA,)8CYZdE+GPdJHlj(4Ehj-"Al& +kF&rN,U@pR@bFD[DDJA6BX9D$Iee[NCcN`Ma#XLrSMGTP$+,KR1H9"QV85TF8M*V +#Y+HN(Q*'1,e08TDFJN90mf3TmBf#LJJQ$ST)T#[AGNUa$%0,j(mZb,c%2Fa#-pj +Fp1EkaaK"U5S!N@+-#CS&MHUFT'X(limlR3KU,&c1KP@mQ*C*RMZrY-6%4-jpceM +*E3eVHRXfA`"0PTA5C"GjABqEj*9D"Mqfj$Pf5AQ8VTd+VaZK0C,AjfYPqhB16,L +H%M#lY(,pKC%6'l(l+aZ8e-TVZiUP)6c"+P2-'f`#KefmfdY45EJd4aUp+rGHqk% +#(d*6+8ic15PkSX4Kj8d(m--e#Z2d!TN9l)M4P8ZK%%E$*kTFBMaepNi-PY"cEbE +5@QH8*UcJ[jfM$E3AGS%q1*MV9l$FeKD@QMB48L8p@CQ())i9p[rNUebCEm!Yhb3 +lZ9ESm3G[$*Q6K6chFR'L9`5mHjHR[-e8%YGUce[ar@pYGqc1NYYCil-hj`$+9Bl +jH4RXZ#9M@YlH1f1@%m)9E,0IZ0+DP3!3%rbUi`kdC5!-6"eGCQZf,(&d+'eM5EM +`flh*-1"Ml!YFLf3KkGcicL(554Vmc[c86XffSf!9L&X#-%$"qpeih8$*8Hb#9`I +3b`6PlA!2*(DUEMqUcQmr+%9GXc*4RYA[SNi[6m*l-5Pfbk@a9`38drlYK45ceYB +NqUPS,X@+NI-Dj&mDHD)YjCqZJSre+q!#@RL5cX`K3ahmI!@G-XaLZ,,qLj,'jDc +!2Qp+HD%k2d8"bID[6l[LL'E`U0",qB1SPVY*DNYDkJrc(3qqBDc@bG21`hAKMd@ +Lp-E$3d0Z4lBhkX-0P"V")([8mcCJbL9QaVLrM4[9h#k"pd&VF4rUMLmA*$+jbVp +K,JA&-UHj-TeKQd[RKZ041)@eq[,Ma38&Y(AhAQqY[af[@11MF1`H6V@aZ@e$+I% +h"KC3`d5#*)R%#Yk)@#Rjl#mNfbIZBMfVHZeV14cp'q-8,$fe$U`fqf+`PeX3[Fr +P&G*G!fHUl5H2K!(U25DMX$Ed)+0kjCRKRi,&*1AfXlIjE(r)BbV)jE9C*'NMFbF +Q3+lTbi%dPFK@44EMkA3i+!G'0G0L0+k(Fmm,-GD%LCI*CiS("Bjr!Mer6SUH+EP +mepA'BPKR5#M2XH(A%'!+cj6Z$3"lmE[c8kBd"0hNkZSLNB)m4ECLAGV"EVM$X$@ +j)0R3F@Z$8jQ,C0N8(a3kbq2,@HPcPrAB#LbR'mZ8Q5X-e%2)XU`+bpYM@q0Ee)p +3!8HfJ*fM45#D0kcCQ+M@r5`MShZ4YphA$d'aMBP`S*E$hRlA%MTQmTq2YaValdd +l$6JP0b6*"CpQEad+!4%C6FG6RK+0A8i[459Bi*!!Bmr''aqp''GBbG@&4U,5`F1 +,pfREV48KqC1'6UBT"Dd-U'%N@6c,&P2[284AHS$&**TSV4"V"9I#-`8D$8j`Ij, +"f4mSID(lY5@l#2HdPkDhbL1Nam%EL#E6@PMaXZCU2hdclm%H+)eJFKB!61[Q+QL +"2(qb%LV&6jD4'89L38@LCYq+YC!!R[)-88l$ZS'&[Z"!(hBSQ"M2BU@+6F,BmK( +bZK#4#@D`QC!!&Akm&IhVV)5($)l(-%,J5ANJB!S"DkSVLYkk40jmkST-#2lpTbJ +2hfFe')M1E3YPrEM9kETI3ZaVK&,)"DQ14FX4)6D)%X(89Ze[5ZFVZ-1NXAID$*L +c,!A@I[CGMhE8+PTkJZC,BmQ`R"6bbKJB!RDYV[qNKFCEe[ZDm$-6)0NMQcT*cIe +iX[*NSe[904PicU9mbp%ZrIRSkY*`j+Z!S0LFI5DEMj!!Ta9M+-6VLpc4Pb5*[BV +1l[9lp&IF@1qr-*C3QI!PkXF(+*)"K'l&qQbp`4#4Jp`24@ELXH$,pU,fI*XBfdd +KhBCDTiL!(iKU#C4UT4MLb*,10U2r)S!)(E2X12G%&2X8,iX3VF"!99epAB`Yl&I +Tk'%TPeDX-Li,B%4hIphIlcfafKaflmFVLpS2J`8[JcaZ3lTMqXE5c28*jU4$H[B +"iG!BBQ-K*k%'mif-#jLRqf3,pp#8`EJY@Km)h*KMiV#5QK9J$Gkec%aY"h[Qd@E +ED!CRMP6KkbTd1I#ZBLr9fZL6dC!!")#[5(!AU2f+ZFc')DPZ245mArZQdUT5a"q +q-XBM-Yk(bhedR(ViCmX'`%I1V)Ge0[BEPTc@-b3eZDd9SMVF3TSU+T&R+4!B4"e +9%SY-1)`@jkHFY(e#5li!K&@aQ5$e-URJ9a)!e'"2(3[(RD59G)Lcil+A4X`Jj1) +!1K(T-VNlcH-'r#cB"N1[9FE4Mj9SlN1fF+cA'mb$MT9DaU,YR,qJd3+kHep2F1R +&%3jE9,Lp3XrEH9F(ID2IXph3YUR"fD2ipbdC#43XDcr!Z&'VhFc'TcN-`8&4e&S +B5I5KZ92PK!h@JQ'lbMNcC84riDQ6G-paKX0Mek-1PUjm`+@3"!%!!%3!%,JMfUZ +i)pUV!!!"LJ!!1aJ!!!%`!"3h*J!$lbd!!"Nr!*!%$`"#6d&I9'0X8fKPE'ac,Xq +!,RKYE!!"0cC849K83eG*43%!rj!%!*!+J!#3#3%k!*!$0`#3"!m!3X(9#he9rJK +eEik-8V@kZdUV2E5CM[,Q!QPqceI`p4Mi%Ir,pA,iSf5%CNQ6JE&I4Gf(b46X!%, +"e3qeE'il$r6I&A"ADfRMa1#5dEk6cNSf[XQ!TmiKlIrJQhrjCrdk19B[NbM#Yb+ +G@RAa!kLGZ(1MD9kqS9ApeVSdPap3d`N1Br2+I"Q*J@!+rL"ETEVKLPZ&Yp38NEN +D*9c)B*3B#cBVKMePJqq+N54CZF6kC8ebd)VTreliJRK9)+flaCX@,8MC[(98m+D +Bc1NQ-BS8$+DZjFk2`-1AHq9iKPS-c(i)#8mpp!$DeeM!ZU3RYlKqEf*m,E#q3jj +%C1GS)Z'G+ZA!Em)8MHeh",ZcUVb5T!"q@Y963YbCK%RNrDZ@KZ)@C2($kP*bifB +YUiYiqFrb+FE'@4A!ekR862BZ-&RM&93LAqX,M4m5Vf44"c-Eh1LaQi0[@lA%XaQ +LS,#(*RrYhRjS@09-1'4CBTe4aQe98m"I5MQ6,MXa93&DKE[qbNmV!NI,"[h!(hV +'4(R3[KDKJT4Uq-(eeZ+YLcdfF1"TVibCijf+VAddTYiDF`0FF`4-5K9&88eB$"R +8&B&3NIkPPd9QZSCD(G3)18pAV,`)XDmr0+5B5)+kBVe,26(eAK[#@(I`YLAPl&S +5H6*Fj6VF+$-3aCr%%Y*EMYC,#iKCpem535(irl4I&jqYaM*f0YcR%)!b&S)f"de +#8`3TH#qUY(["SQlKIqV0NR'fIbJFQlNIE@!H(-UkfT[P@lGic0X$lPRkLY`VAkk +b@1am!KK8YrhM5(b5$c84(kjdb8')k9$R"j)#)IDN[i(HTURph-I*JZV-@T,+,Tm +U&0d+(3kp-6jZAJ853LEXAE4Z5D@A&1X@rM#SZUCqe[9RKEQ)8Ul(R6m5MT3P6L5 +)h3'f$ef!--fq@+TNbVH)dITkR3P#@)Y"ePPHBl8br$624dXMb[l59L3fk4Qie[f +1VEbYK8F5L`M6mcScBqmED4"C6%'rpaZ4l0IJp1pAJc'4K'2KCicl2Y1bQLlGFBc +A9@rm+RPV9QZpMG[GTY)YPm&K[M+[X,2%JG,-QBheqIjlEc2#b5pBA'QN32kXT1m +3,5&a0Uh,@*!!K3#F!+qT+!@5Aj(r`Ic1EZ@YrJ98FUk'(q+a,hr'IHJEm5(6BVF +XE-Tm@lX0SiJpa+aadrc36+K9*cKi1p3F1KBq"EH%Le-#XTVaQlBD"L+(U),2h9- +h31$Y%4elh[HiJAY3@@-aHeB+!e1Ua-9K'ZXPc03)3bH`j!`h*8c1iX5q!bJl3D8 +0DRdj-F)hGDIMCQjP,SVhU&XT,E[Qd8(BQ%JJEJ23M5-5HBD*+X[QG'Q2M*5rZIS +bT[e)!QBqT!,Mc1G'lIJm40hD!-5AAl'15EHX9-qZm'd58i23'iTDN!"XAKAV(rM +ikQJ'jS!U9fa0JJ-cRT[PjY#r-Q8I+$m8-`L!3RpDekGN#RA)h-(fAM%bMGr$L*2 +)rQk4jkMX#Z%@!,9+8JfGq5K+heHYZ5S6eZGce!)TU4m5Yq01Yh,ek(2&TL8cipK +Va1-X)"P*2V-Sp%h`hBmkS)@V-ZFljqJ#S2$J3$Yec@G`Tc4TA8k6X(&M3PeAPjM +NRGUC%*hHeqkpP4rc5S2K%p5kDCIVki,@#YhL+1DMG&!5Q*XiKq%0"b@1[&V2d!M +NTC!!MrTh6qR$`8N%C4EP[HY)k(p@8m`aN!#[c9m1hp,-m+e(`Sl,+VBX$$FEklI +qhQ9bV!)P`i83j%S[&R[PrU*HlSaP92SUU6Per$,e5),i[$3j@!J)YRpeZBIa5@1 +-eYE3l@jPT5@SFfdrce-S0qFJ4P8dl8Q,(U$PkH8"C%H"b!6JHH4GbN''1$M%1bc +@d[Y5Y6pCqFFDjLfYYH[VI%Si4@BLXl'$-DS`qcXA*RFkCNSJji"93Fa5K(Z6,3) +j0DCSa3YI+$PAbR60j*qQ*jF([YIEH!3Z*i&"H88L&4M-J08k,c3YC[BK(E)MT31 +8KU%DFp#R0)mXp9S5[&MJ8ZSVb@8#*Da%AHQP$PP&RdhipKp3!`h"JhilM2RIb#Z +#+@9j#1fMlqd9D5lcHc,$8iMhNmKVARZ5qAeY)Q"dCD9ECRjj,@DNqD4IFNLl,P8 +dD2i8*NB$ajP!M4kLEGq@r4"f98YPe&++bAYd(&e'$46*($L835HQ5l`4BYiDAaZ +p!r+)Gk98+P6lbX`4ZP!%mZ%Rq984*&-4IM,SF"QLCdkKed[-a@PZkKK9BZQLC0h +&K4CTl!I!pX4q'(9Mm@T,diA60Ki*%iL24pq@LS*U6(#HkN,PJ`35q!NIl5Yb`2D +M`*!!X2(RB#`eJDI'`Fa6cPaSi62IZ@B+F#L[ADmM"3JPiRe'cej"X19P@'TPhBH +%*e5d+5[&P2LA"Z%"d(@@+MrXM#)D92qP@D*(faN5R-G&BB0MHeUGLphC+NBS2JV +FMDU0&p!pdNYNj-+X$%lD@486G411QS'mbDhmm)Tcp'#ZeiQCD'Zb*2eZHbAahG) +-EM`D43Z,[VmppYkTX4HVI`p3a,J@1Mkkj3[laNe,h6l,,@$cY2QSNp93cC3,+f5 +bMBlbib@Bj5eHJ5GS+Shqh#M9@&1#kCl-M"6rkmd6J'1"([[2$9ZaUYj5j2b!6F* +AT*KNI6-@i9QB(LfD8k1!K4*"DUCpm+MReY&SV(fAk)m4S+X[q3SVr6!&9&Ul-mF +VPC%,e("H&Hqf3BJG@qJ'Vi2r59k+aVLUT@)CAp5FG&c"AlLM)JJqc&FmL-B%#"$ +%*4MbKe[kVmI,"3@de`f4fTh2ABUMBlN#8q,6D#aG9AX*hi&G'riZ&b8NkAk'#1P +lZ"p%PH2&*S`DQ&&)&YehPQC6"D)M4(J)*9!9r93Rqb$P9'`MY+EhE1(b(Ah,@Sf +C(Lid3R!@364@LJSLFI,aS32DZii!U&CpDb)Hr!%6())3*[&2j@(R[,P'-P)L0pf +b@HkAF0!FJRX32`[E&Nq`*e$LH9@$A'%F3D@YGIKVAB2HB,4NTRVN@A%4'ZM2D0- +@ZEp5@TNkKT2H*!0CbUU2clZp5X"DZNpL$(Xkaa5@Z+&(1Yff-rG2ip&MMIX14r& +m6VpPc*ZE0J)GA'L*V9`NM0q6%!QG$p[A)MbFiNQ-M@e[6FVq[5!QME2IVHjL,A4 +r'VcQUk4hp4Vcq4e906aPpelE3lEm5A$(+6D9'lkRFfm6"VPjUrD3!!eVNUH"4H` +hmV(,4dp22ShQp&LV9pmI'qD@`4-r[A+3!"a4894-hH31RJ'cmmSkSIc[fk10-kM +3*GQHc2E)L*CaGj%f'$+*0%l`M[+83GBL%22c'h'6C&'))845S*ZaEqV-AdjG`H` +Dp2f@Ue*IJ0A)'qrZSGUTTMe-klYBYeZeMj3Mfm,)$dA0f&905YJY4db-a-JfZXP +1[+EN6Dmka"f0$4!Xmj5$[HG"fL!2@D*AI&,dVm@"Gd#)fXV"B49qq+GDG+0XD!P +C@bVq9NK30Nra*JR1G`*25B*kJSZk`"iMm14V0G"Y$P%%jbNBdd[AF1fCD+a5I%' +TE#SUD9pqmQMIRIXL%"MZdI#$dMRC`8I)'5lBF+ieF[P!qXkRcE$N(Pj!2(&0Q+T +RTiT[m)[CcdC`dJ'503Yq%6pjcTVp-&#i'FKdBbJilqfQ'XqSS8G"@-#Qh*hpB2q +!Q,M364iaEB5)mG1f@e'L`@5bc4+Qd2V`BVM&a&)HJp1m)804d-hbdlF22Jm5ZM9 +H"JbFLl5F#j2TJA8hRTGRXfe-qVBcHBYdYeSIUGIbL6bA(1lSm"!@'Rp!8`%mF%K +XMA29RLa"Lk!HNX%I+mK`CAbP9Q5fUD$#*@JH`E@TB,rdMZ'IfPAii,F+kD%BcMA +cri@*[Nl*cI66@&@*f@*%K4X@8!jeRjLbjjqX05@cif`BKXS1+l`RG`QdR38ji6$ +Dm@5XUl[j)01GJFZBVKK`B1MlhHB8pFl4ZfdY5j5NQ*P5VEZrCF(pq2fVF"pTJm, +B,2N2$RGAiK&(DCT*IQD2UcpApd*!VEeIkRe1)P4iMbKMr!Z-h1I[hPeBqYm'G6X +ffP-"HccL,0Lf4DT+6m*(8f3a@3HH,$ZMDL8fPU+JU@lVmAmZMSB&0rlm,*JFd`H +#&5ilC,CK*E(12e%kjRSN0-)pV)rB&9qQl[a@BBDj(YAaf)JD2cFKrA!LNID`mbP +@mlj%I8[C)@%IVf(B3[(H2#![ShDC)R"Vm%M9erEm[A0[!*YHXA0C@BM*1khZaZ# +rqYJ-C'%(@lrC)qiJh`BYU%1K!c%JVb0i2QRq0qCh"A@Q)"LeXQAQ4"m41Ac[Gl" +Ic&Phm*R!acV%A0%+6)`pFqSqm5E*lF%(EKCM!ldM5Sr)kM`VVp2q-'jeF'RjL,3 +Z"jD&RaAZ8Q%lfTIl4-&"2&Nk#VjhrXCV@lD,92F%LP)#EBJf-e43DDVqA4,SQLp +jJBZm*TpTUcQ@A9a5@Pa@2"5R!L6kaS'[GKYA65ERdmRPIej6d)cAl&C"%ZhmFcd +452Li1)EUC#,MRiVCp383#F3Ej@)%H'SchbpUq3HRR)cMD#+2a!b,mX+K[',Y30Y +AkcA%(JFA"hP6lQdmBbpR%J25$X40dKPPM3Ba1%0&[)P$G)"8"imm4D5bDG$&T&B +eCa)3@fTC-D"l+EqFKQZkFS"2icra6!b&a-DK,Jb,ZFN&)aFH!MZDfhhH)4A2FDK +$CT8)f+PZ`(iG@[1-pMG8"H4+-a5$AP5DikaSX@+1FHHp(cQpQqijKl6mr#N'VkT +J"@KFkUF4U,fS-QAL8fkc$$#facNA[[Y"YHA[L(#@XDV#qEm5U6b89!2'6rq0,jP +c3IiL@%PU,,'*5a2X'"AQdla%U@Qd4D`!hk**6$#MCCd,kUH[80T1,`Ze"`JVmA[ +GCB5pU`pX4KNC!G)DP2e"+1%HhiS&C&9"'3m59N5eZEb*`L3*ca1+aYFT2#ml(US +C*iPNqc8RKRC#P!a)UjJI&44KJhm,NLL*QC2MZe[8%,1V8pVUhhE*+pDEa#GA!qA +JC!`9mGC`cNBZ&!4'Mf65XECD!6jRP*k8mIVJ@!)j*&e-,-0*6pL4XCFEMmFS!4S +mQfZj!cfNj'kjY*6dA*!!L',Jmi-URMEM&S*lL[`HZVbfGJVMek,QY!hDRa0%Y4% +rH`ccSZT0Ylal-Z8K"fk@Xm)0H#!K-EDAq0k,Q)iDd2B%lj!!FKRM,5C[DY-hlkU +F[T!!5j)")eq!`XHb-J,8lp5+jI)iqLXi-jbpllG[f*V*+*T1"FHJIpQ!bf+*KSa +"*+$9NkD(kVN3fpQ!6[MZ0@QqA1#SiRqSj+ZK#(+245VJFSUp&cVli%DY16l`N30 +K0e"apf[Xkpq9a%#QcemN-A!h-H@mQr+adZqDQU(R*G(PFjCpr*($JIA#KB9F@RZ +![!G)P,[b1,JEKB9hj5'6,IUGNc4hPB8lqH6IpE+F6-GGaX"#6B-f81c2R@-S8er +`5%'CRf8YcaD%#mR'"YLj-6L%NMa*3!N,@@P*`MT"TKmcX8R,-0RDk"FECZ5M@*( +A1Q$&&jX)%!Ajf[ZH@05C9`5A6baIC'*$B1&0KCL2ihEprR-#*BbmZUMPN@*34YP +rRUBG#&Y',36IAXAlII'!'i`B5T*ACjlGj-P3qUjVG+KA@6K@j-JNM!&kV0XXP9h +%PV"C"d+6D*5X`PBSA%QQRdaS,F3ZS3PE[MV!X6@EqMpeFZ-BQmPBEQmZQ[b1@lU +`-,8"[Id-Q$mmQUA1-)06!4@V`9PZ,+a[p(82N!$lq+8NeaZbbQ6rV8JCRFcJ*K4 +5%2+ij'!+-1`a#UTBiEb(XE3+M9ra2)D*&%bfYUYJS&%Z3XKq2aMeh5R49M)!8(J +%mMDUN!!f-XiC+&r5EJZcc!fPrNAdVE2)Mcb9EdH(0P!miV-%&FUp#bTZ,hHcbb$ +l(Up"#*Pb'$l+dN0!%%LmP3$pfLq,b"G%+252EM,PTfK)U$ffR%amJq`aP)aVC%N +dQ,lm%%HMjZZQ1kc82V3bP6E%,$$X)69$R3c#b(i)PbibXCbGH0'!j%*D#E9qDU3 +qV`!aG4AkkPr['ah&`q6Zm8a&Hh46Rpjp9CZ+([d$EK1&M9P#83l@I[k+K`bN0KS +bH@kGD&92k45BZX31jFd(PCce9CH`4&B[R(5H[aEZPMieY(DN!!RN'`5[,XF41&2 +li[S&fG(`-3e%L(-&lKI-D@0K'+-+64I36Hc01%0&5Y`khQVdJaiN4KbZlDSET+A +'IT'2'2plQ)['Z&hq5I#1BRVRq2CNNIVpia+A2h%UU$ejXI8``Ec(lr%iE'')ClT +lr-r5B@9B+,L`Z%jL6[DG%%5e3I*"M+SGkL!PjS!#CSFUBD4+e6[I5+TX)CiF@HX +GcUFIb8&`6qhfp-iVR&'*'B!Cf4j&L6k$K!d"-0`6(jmRD,@dAV5Z$BN6dQHFlG` +XiA%biFfbGA3Q@'TE8fbThYF#G#@cZABN6i@HPBFfG%QA5NV$'j`(CBiYcTik&Rl +a-qXTm'`j5083%laY*NA-Y2UN'EfrIRfjZ[``YmG"GVCNapici`4d*5Y"RPeCNDd +MT-,edHGme#*b"D"B3jE,6dkUGMA"f2M2NGBbm)qi*TP"eB**)GlNHf(Mj1&J2-+ +CK!AK-CT9`&JP-CX9q8VQC825,1!9"H@+EBHXSXqaJ(f0$[(X@CffkkaHF&SPXkN +D@,FC1i,Qe564jfrNFB+`C4!2&T4p(k%)JHDBIh8$@D[S,"fKIXAG,VQ@$-hEQ0G ++jqj5l`3%0C)BD"Q26ieYEpUj5j(lqP#aHXS8Ci-CBS5qQ$mbh8BaDC&UijJaXUQ +@BqN$SA6L9PhX6&-f9$YCTh(NU[hIH(JLj(Z`['bc&#&Y3e8Y4Aj8b#6*9TCPlJX +bi6!Gdac3"15kZ!&6%SqdBEhTjJTq)B[`C['GqEP)P&+P0RL[&P9"&FH6Y3(5%XD +5I$%KXeB#[rl"EMZN0p[AGl3*DB3PdmZCd(Pd$KplPmL+LIjflj`!(C%rhEXQ(R@ +B4fK1X)5f[B0mej9-jhKZ9KLLUX(JQ,Bm6[$Pp3r#3PFU@-*S6Vp!Hb4jp*8b69- +&d$CT&)0&A21FZTKL4'Ld"rTDMcR%KA8V0V9#!mGf8GkPL(M"JV`[GFN6*@iU@&h +(PKSp`a3dXZYGF(Sh*[`lU6[2lYIk`[8EYp&h@6**9p'LZ5H'+j&,SN@EpVQG2Nh +-iYMriq1'&9-JeP#&%%jcI"q@#Uc"ZRBANj,l!$665af0"VIE5`08lV!3RD8"*iL +LDqF5`iM-Kdr%'e3*HZ26kL+NLXDHG26a(#LV9mkr`+b&qX2r*`,ippjljAE`q&a +#qd)*)+Bm!lfpB#d`hFpB!`b(C4G@!@f)Ih9QIG86CJ+*0ZJTLD+"@CHJFfkck!B +ZJlq#T+8*`1NmD+9e`-4ZdXlBMV8Fj"p0N!#9@1UN[XKVK,aI#DaGq2eMSDZV9Ma +K%$LBRP`51e%HfEq+UmjF0#1jJZaKl2Tp3@S'QEHIa0kmN!"P!5#AZ)#%VZGNSbU +G$TGeb)Q5[r4U0dJL-P-!jZ(bVF5i'!B)h5)2f'!fF@eV,F2"@'h31X,%2Z%4TTM +#!JTbL4l[jY3#hF-P`%GHelH$3p#YHG-3F-e6Yfh5mb&f$IGhRIGlK4LdAVfR4Nd +*5MiRZfB#Z0rCJ6McJpQS`*Kr,mlMC4qUEi0I3[JpN9S`GAC$PLceq5JCj[bZ5N6 +m"ek#4ilr5U#0,c(#LcL5SFN3I#H*pN6-HRrXH-pa,6,446Dap1AE-'%A(F8+YIM +62$Y%kiZ@`5*f!2Zf$[YBQ)8qj0RIE4*%kH`0Jp(HSf$T`TZRP$[(M)e95"U!+h2 +R69flG,%Y!J054UL#MCEpF0ZSR[lG%3+aSdELDXhDXSp36`DiRLL5djAXkb)ZpBl +BBA+I&MUKaTIk1NGP3B8YqqS"D@(#$i,emBNNBakq[UUHDl2JDZmpq&!hCGp9--# +VZ"TEM[C,b+CQhhZdUCrM5(#'')-b4"6"K3AT-Pk$E$kkd#92IG9FPr1Rj1!E0mf +XYi)j5,9fH8`LpiMJPK+jeT!!QU[Q!cCEZA-KjBb(G+C[U"3fLD!G2-&&Ql)Z[hL +d2+Y%`ciGN6NYK%j*M'+%Z)"Aji25Bd%4$KqUF*8&K$Fce!a)Z)GS*+RdNi(iQ)i +PcV+X8-iS!%UU!rlCi1r3MR-Mk!+cRM$QJk1UI(GIb(kiKHM-fS1h,Pj$`TVfmUR +$a1-PdhA24TkXl,8Z(P1NpH+V+i6ij6m[mIh8qZ(-U*lrlV-9)%4Kq@BfE,Y8aPU +#NBKiMi!P$JX0d63+mfUKLj!!q1*E$V)(lcRDXFCikZbE"M0jbfMQZDCMPA6@bfj +%KE#cE34@%[YAAV*b1C'IB86#4+FK"+kGrU!q,ba"SHIXe))X-S4I5idbV3ZMmT! +!rFhR'3'D2rqAPkI36@f8bY9r)6-5#f-`0lm6l-@A8e#N5kQSf(+4)NmT#Fp)C8S +IM,38Jr#JpA!m'`Q5TLb+dIlHcZercL2N(c4TT*DIhPTfKULSQ(QaCYFFUK!VMdf +6fBD"-4%0r)A'82fA94d!C#9FIPr'C0--R'45LdB#UcliP-kVr`lkRVdlK4i84D! +B+I26"#Ek)@-dXDEd(%#M`jRU0H)3hCk53!NX([eLSaeeHRJZEf%GAJKe0Yl#NjD +p#100'rCb$rX&@Q4cU+B&KQ,2ZST[PjID@mIM[9N`raEQ-RMPfFfJ`Hp!8e`@3+@ +3"!%!!%%!J,5KU6Uh66Z#!!!K,!!!@-F!!!%`!"%p$3!"iq3!!"e+!*!%$`"8Bfa +KF("XCA0MFQP`G#l2J!!!3,*069"b3eG*43%!rj!%!*!+J(!!N!C#`G6%@f`GLi0 +)4DkN[b4RC2,[p#G'&Ba5`H5melD4TILkXNc(dUD4AADeYh`"ae8f(MfSk)LA3PP +1c[3#,a2j``qGXElqPTKk3KGX'TDk!hVB4Ff`[h*lLUD'L`+BN6c+ph@'P!&+HQC +-r)R0'I*m-,@$l!'[Q8GFUd@%2X2,T"Lj"'+11`*Pc2PXHmZMq,k0rdC$J%*!aGk +V-6Z`A**1B`f)kF9I0hGA"S9eM5UU&Q0DH&Nce%$r6!CET+h4Ij+E*AKY)T4rN!! +eeR69mlA95SVK,X6LPP3-CEEURhK&'T1aXj4@*5k&L*3eldG'S6jC&9E10a(FPQQ +mKqIrX93df1k#Zb4FkJU8eN&Jm[+fM`+19c'+,b8P2)XA80L+X%#62HeHbH!&`TT +k,1mMj*HCmA!qY+j18l4Sj8`$D"&6-N,Pq*YIlAa3$HJp'#hFE["N-$K#*FR2%2q +5[N9Vi3+dQ&$A)-KC%B'e[PPB$k6*+f+Qb6bH%bAYARAL+02j[aX%p8j%jDD@[FU ++(0q"3dhB0PqX'Fhq,4qVi*G1RrlF1&NGh3H[CM$D8!!I0VHGIf#5E)MjG#k1R,d +#@hZ!1erY&#"A9lb[6r2ZR&'[lHrT25rGd`e@GdhCrqpN[jiDMdMSValBrm`'"*! +!LZSPm5meDEVi*LSbYb(b#K(ZMSjadYT)j4Ue,hHQK(XLGM%i6JV)#`2$Ik#q,0+ +E5-%YL2[&X4Cbqm,r5b6JAL`P)MYH+USBi-`1G*B!c8mQmq@PGI)D0*2-aqP"hLi +FXPr)e!ZZVA%ka)V+FDTJBL#(4K!+pa+5pSC)9K$#TeJVpNrF-H@+*B[ffalK+R3 +dB1(B9QYQId@H"rAq&TE2V!2A1cDFdMJ`PHBjD0bk0EKHHU#CBr(j@aq[!8U%XBU +ZlSDp1X9E"2TTT'eAdJ61[5B*I,4@QKA%YY*qfaJab4Gk-,k6N@Vre+JLI*qh623 +4`RM&*d1pac@LC*B-YU6CNID4fFFpA&)fU'$[G&G)q1lLPjK`@5Vp9N`4PM#lCNS +Pd&Mb@SD6d%'YX'2[db,DT`MAr@805'5%bY9&)S-1elM)C@c#AS(FDlPQYJ#X$6H +2CK33[!+fC)UEhS)*&*p$&EXbjDp5mU3+m5Yiq@fNMBJ*I[M6*110iP)qUGj$hZI +88Q)JF`1&@A2'6p(5K46d3`Nb!M'0S%YTkKj4A3`TlTJfl+Eb6X!+)rdVX'41@Ub +NSHcZY)Bq@`hDPBjqUBR9@FRmBdUdJZP)h@rEaa3!KEaaQYX1LAEYSkdDj[IZ!+b +'b#VLf)qF1a[1k2KPC!dl4PZH4q+b)00d!HTkfL@Z2UMI5,9JBl%,ejmh$NDer$L +FhBi*KU!2EN*9JE%MTp5Ze`MMD'*M)ZC$T5%P4,8kX#Qj%Q[`QX#H8,2bHhA'Z') +N)CJkRc)LpFb"p,%i4RfiQ-+C6`-pCVU&P+bQH*)L5J`(EcMUTYrXi0ZaFh9lf#q +VA()+5MjeP*[$@TYJIm-!$CHIRRHi8jEc3m52b0H%&Q9b[aRQQd8B`[5mjJdIK9c +,p!P%dC*9!#aZ@[2`UaZV+R8##YiBMkGMh&bMqT8rl&#"+BV2GV48c``8G+Vq$A% +imGd1b&AV[-MJTdb0i*dmlCCGIp,X*Mb06c-4%GZeQPlGm99jZe%l)@[+e23iTBV +id+$454R"TT`UHbKAfN*HFLSmY8a26pQp$dXKQ2b"9"er"%(#Ri-!#VM`EjHBC+, +I(+U2M1XEFaE)$q"[,VBpp%b,,Tb1'CY8$Td,eMaia%'YFMB@J$Z1Ml`KrT!!!R* +0T!V*(U',df0@T-S"-!Z[ieD*k%N&9G'-SM6AXXBZqdZdNYIDi!Q)[)`TN`D#(G$ +llHDEG-`AffqpE%d$+%*jK`!ETm[P6mcEbQ8EZVJPK-eK2FNl1(rcf-8F[j[LVl1 +YT9j+CdBZ'*JDVSZ(d#JRelLN'-S3-qBe[ql[mdD'qIJYVLMBrhlH#QCEKB"SamP +VB8e84Uaji3"Sia4aAeHZKVkZQ9Bqb'k*9bi9(0$H5a[bG3q-G0U$h*&*j0S+eD* +fYDAqKXISJ%dNMbp*!9fL`cH$#YdCT!+b!pE+C%)kqbY$1,"iKhm0I1UhrMm["I9 +1RY5`*d!1h#eY)$VI@QX@4e3&p#D+FaHT[FUllBTS4LUkPKBlS""HFM#00)$ZAN) +XrK0j)G2Ad0L#23Mcj+TPEY,iBP$!aqMJj#m)['%RT*-+'e&qbdIQ!+%*L`Zp +%(lQIB(m$jj[2leU4bGMcIL8)kHH2b3U*!R,-1j5dGR%S@4C0)#F-1BL@I6!qUU0 +Jp3AmBBmYlF,JIcBS9pi8RdEF'%TkkFjKNc#faNlP4M`RBq5'APV@!SB3kh!TY,U +Hk0S)EmI*f5@*,Jm9-#Jh#G,&1Gq1F)'Tf#hP!2p$Y%Z"&-YPZI"AKJZTm*D3!"V +q0(-mK8PLKRKLkmD9rX,K%15EI1(G@-53!#QK9)+Jj4#'%U$V6c`L)!*1pKp*ZJf +S(+,Q$[`*BADjZ+!a*(fPZ-PN5r2H@IApm@'N"TRQ08UBHRSD-m2p'523!rf0SPM +C05DB`,N*-R84-L+K+R98-2DZr5'i%'')1l@PEAk+K(1J9SSSGAlE&i36L$k&6CQ +&-M"Eccq,1L#3!24I428,eE)'GTLcSPc3,YL+YZ@PSQC0lck3!+FbDRXQ,Ul)b&E +PFXb6!AmDF+V94'ImM&88ki,ipGa0@i6qSD,0)F")Pk&0fT50bU-PSQiaVBD%P2, +XcA4mI!%Q-r#BhNE+"9m`(iRpPPlU3`IFH8$1J#f6PLBCf-qHeYL+C@,TH@P@TPQ +Uh'"8ENi"eNjCP`Ph0#Ze$PVqc9pjYLj(Q6qZLU9ZIAVE1fE%TD`E2I3P4k$qI#C +m+ef62J6h0VADr$b*,A"X05%SCBF"68V)T4r9Mhd"aDjh0i4mXX"bT29d3kDaCHR +D#cp2HrqhbH&D16U%P[UF8M3@@'`8*qjHUV#GAmP`r$S8LF4q5`8i$a08PmI+ImL +&%N0GD$`%HB%qcILq9jd93H0dF*hB!i4iKR"%6p$eTk39hZ0@d8c+2ZIreDFNi!B +P+35eMle`YZV$K@PXFIm[`81CBk-c#AJ"KiMbdch8kmfP,HQr,P3lB@TT[pE0GZ, +!D,(afA),PkMKRM34PBN`lE,S-Nqh$ZQL!`@XBTiT[(i)MLBbqf4b!GZjVf68i0S +@!pLJAZdS,6$eS,5qX$f0$@f&aS8ahPXl-2**i*b&J1+h*YBYFB2Y9*8L#@q)9Ld +!ap+lkXXXmK!N"$ELVBj%)J-`&X#j#,S+!!e+Z#&pqQ*2!iAC`CTqi@b[2-SL4,I +-ca2`"c8B'X0S9hYmX)2Rbe"MN5NQclrR*8A-4E(YNfd'Zrf8aJ2!e,0,imlMHB) +8U"PEPkmNLSI!*pk4+b@lGMKKc'llU&6-i@BbCPeLLF9@(QfX*T8r#TG(4+G)ZCB +RU[)P6FTR849TMNdT0mcpXGFrYI&pR9(mh4qBbT+,eAFXTK'R$N`Fd*hSCRSH*D+ +YQN1X[T,QMf`@,"GS$*YT8@jX`&CZPaCdH`jI-@RAAdRVP289qmICQ#8#,XMhJQ6 +)[G1!j5V(',Z@JYhbqb9Te%qhSq+G5lNP8K8Vf+2qYmrN6Sp*MHF1RVXqY(0e6%5 +9b0`R$%I8ZTr6,IYm*U+(PGlH[bcG8U6f1*!!Zm*B)$NbrBK"5`m%l6eAN!"'bD- +@BB`B,lQ1EdS@T14,)8E,$TpJeaH,PTUcqYcpE6#fPbjRk@Cpmc&,Gr%*QHc($qF +38S@fAAJ"AeiSkpR5kq5Nfm"*KZ#*lkAifcDYS8Uh-lAFk3@,8J$p!hlS,6rE[%5 +Lql"AV#(l#LhI#!X(3Afe39Bqk4HaZmDZ'5GYR`V5Im(Y6jUhVaYBc-B8l`ME!8T +Mrcld&L,F0hjbND[XlMN1A+6REPb1%YY@fHq8kTq434f$#c&5jd4(L[4(R0SULX( +"Q$**jAB4ck1KVV68`hdPTHlah)B8rjG,R13#fdRdB(ce1EI`'r(#I3J'%-j0Yr@ +L)9rkmPb&Yl$i&6bk%dQSRJI5ASr$[GJ&GQbM(ieCZpf+4&84$,PN%#1e#%#mZJR +!dHFHSLJ!D5r[cb@1C@mQC-N#-jlIf8Aq`r0')&PPI980MNYQcm@I!28J$5EM-)1 +pbc@F`RkF&RmlB+#qFK5Kmdi9U86SA(Ef2H"c2YcI580bA2IHi$Ke-,F2A0Kc +)-Ie63bTlfrq"IiH"qCJ`U6Sk19%RTj(qedHXlDCP(9I[!(5-+2,2Cfb*#P3(Cc0 +hdJkEl#Z$1mSN1JkeiGMc3Qf#cbUAIdB"i)#K&mAaH[Ype#(B`p-G%-VrL%%MqcK +pT0I!AaMd1fNPpQC4M+-dZ2lAb)`9U5dZcD0G2phM%rLlbUJR@p23RUAAm(pkdJc +U@E+c,+@$jR353k8c+ZZfkhPQc)HHFHdrqXJ@h4#jP3k))pd-([YPBVc@P)H`,+a +SK4,*Mimj1G#)Af,F((bEKP[UriFD,iTrei*[eD$F"P2Z(`KqhiCCe1bhT48*@TU +XqQH*hZINRPa38YVjfkfMUDXj(@),9Ci8ShqSaU1hMF*5"-("DeSeCK2kqr96[US +ThB5i+@*%21%6fkK5TJ"FM5IRqdECL,Qk[$fAJ0CK1480q#*"qEp+aI$8SrU&FPB +01pjkbDiiFIeSZM6Yl4a3!*jec&(bhpcVM8EUr)'Hrl@rH*d[h#Cfb@2KbljiP@M +1bENG6PViS0HKi13((!DSE+!R3dl$8pMT6$56IbU+(#CrflmAJ1+I3[G!j'C6U)p +XlMRG[a2&S6FF5[-hJ[4MB&0!fBblLh'THH,)qI&ri%AkEl+NeCH'lrjjjpPF,mS +CNPfjE(!KV,aejI%hKDrZhECfdQJb%ZEZQA5aMl,[90S")Y4F"31AL!0E8I8)'ek +UM%*FUST`C-9-iTT'(6Si1Z$(-NEAk8@Req!PGa)5VcAiKZiJA6ehUCjlIFdb0`H +PEBeP25!E`eqL3CcRE$S$13C%4Sm6@M0JlpE(#YP!HDDa9La6T*cCaS(#JAeBDp1 +Pfc-RbCQKchqm8ecEYUIJ,'&lEM%`G-eQ*N@K8I@ciH3M`X9h)#i"[5iire8!Iq, +'lU!+i)fhaKr2V0*,i1Kl53lf6`'fZ!-N+SII$SM*X0US00#NB3N!64Cq+EP345i +Xq`-EFYM35Gl)qkEjekZQK!,IN!!Q&Cbe9'c'&`Sh8kb1kllV%mXD(ZQlBRRMG'& +$dqAdl!+4V`hBK,fZ(A0K&+BRAB9Z@q%Re+f`QmMG0KH9hd6[8%*`9SH93Y!DfQ8 +A+H4fVkVR[Xd8Zq1![fEHQE@pemQS'([GNNT%lTZF3hT!YdKHH9V*2Kb)[KP6ldM +APDqJ(Z*DTR0VRbP'e*pEj8Y$*ZD4Q%%2M*(Se#QVVib3!1KG`r"FlY9lTF(bM%P +Vpq'`Hj!!Br8Cbb*LdZ#B8ZR,NB8aXqj#I&JMQK(lID3ABY4Q10Cc'i&8LV@LUa4 +9[i(1[5SZq(QmSph5SBc1j'A@lC3%2UqC1*EE4,If+[#C%amlk2JM[me[PaC$6P` +9e,1rmN[B9'lji0332bZlce,'R&i%KVSCqfL)3pLA[*-F#5jT"`)3IR,dHM%V2YU +T`qf-ZjXee-M4Y)pR1pIS5AA-#U0l)fQF+f[DY840[)N9Cj-Mk5RBK8&T0[V`k!F +TRZDj#R+2*Y0(kD[,U$3MJ2G$-DVQpL-Ha"$G$"04HMCI"pCC(QefGQF0QE8$CDb +HAXY1H2&,GEU8bXl1mCYRMK[iUiV3jZfC$+!Uf@`4rm-%[kQ%`FD%NP@ki#,9!"e +bfKi2iZU&1#Q#&i260@Idp!GjIjcdkIF3"ZX)#+F9!!Zr(q1NfX[Lf(#PK+ja2@4 +&EI&mZU`'Kd281Ra'hX[KE,Sp4V#($0Hhh3ik"0dXX!q!lL-YC4r$Ha@NXZb!fHF +R9FVDV&-%UUr09lK[hjlV"k6VGmDDfMQpjSCCIRC1q2B@F`$AXK`VrE@kNP`VB(4 +XhLLT9B4qBS4Sa"-HFGiH26Z"TX,9[rIDJ)F-"`DmaCHD'jNb*(C1HkRT!QfDGCf +8)-fl+X58kV[[l)Z%"'XJTM(5R&+,%D'k4EP0CKCCYY1b`l8Eb`BjC4@"5%1"BiD +RdLV3hI88bJ1@%0PJK(5YqLFA(eYSq&UEk-$3qR-*6d6b%jcF!8XZN58NBE`PGi[ +9-[Tj(JZZ5NT$IK)AlV$5LkK4d4k&K1DYQdfcRU%'@6mh@AU5$&UZV9'Pkickq&` +U*eDB&"T4eU1jXq'Ce4q!N!"CB,P[DcrRB4Fq1,bCh4PiHhBLQq0+Vr)V5jaf!0R +rMqBp9#PDYKV`Jc*CGahpreNB2&A4B3V"%GYId`P2&jjl-T!!LhcipmJJHUU0l%! +K6e#p[jJ4k9EaeSSAkmKRKNBYQ'MCr2`Cj9)P2U'EmlM5'U6PY3UT5k*4i$jQk3Y +jMC)B%MN6'8FY-qXfi#qLkKIlS26F9L6P4@fU'0bAb'6Bhrb3!%%rH'&[*ij!b,F +!R&&p)1cFdd%TR*!!S'''c(&h59,DLaEdb(Kf@cLb(JXH)`5LMrCG'!"bZJ09-R* ++bBSqp01rGh`DN!$)T)lJb&N2ZX--K8D'aI5kmVej`+2a[#N$R0HBHpmB5Y"hr"Q +mBYqM(KKT`fHN5MQ2Rl[NDkBT@P'&"Z-cV"Gi4!Fr#"f@d`JGcLd4fRUk@YF$D@0 +9qm$&"EY-&NSdDJ!"5B5P`U2q5Q#eA')!3&+"*EKPM,2ID*%9A96al3QHRXpC*ZI ++&5N+#jNB$6!,*`[a6La0(E1`BQM'cF(ilQME%XmI*8F)',)L0kRcq&1X5r14KP, +a8##DecE&Njl40$R4M*YcE(Cd!,9PDH[LJqjhq)50)%E)(Ta*pBJpIpcA#5$X$dU +edqJFS(BSH'D2@)(jE*L68eEd2DM%kQMkh6q3!)FB[TkqelG*9mM-LNd+Njp'@6@ +9G'Bf6E690UfY&Rhl5$X6Gd6Kd`KP,Y3IK3Cf81'a5hU!NL(P0C0`E"@#16iV1Bc +C$b@IEf'A5ZT0kD(!2Pr0@ZRpThBU14[c4+Iq5I#,3+1@ZQ,YAQdd4*J(iZfFI!` +UT!NL[,Phh9Z-Zriq%UC1AI!da,1BlibB%#(4GM1V[L3YQ'fG)i*B$UDbBRU9c"T +mV+UZPBCHI6$&CLeXDTdJcFm$jCTHJ"!-a6GF`R2jYX!mej9QDL3A%#2FbmMi#99 +5(5H2Q!3h@1ZD8!4aICXfJlLU"@HXUMKbp-[A9#G"TVF@%U2$YAh9-#0i"19pCT6 +MRCb-ml8"e*keaR%%4i6GiYPI29c@pH1VL#NT0jlNa8ZRYrbq[bJ(U#(da@[RkJQ +(Ma,bIf"#-i*K@5b)VU4,QGQdhRRk41SG-UPbN96NDp"%kdmL5YpJaFGU"eBUVpZ +9Drdq5dqhiSeFCq1fG,46amR6D002-F2LPHM+!CpBjX%rRMFUXH`!#pUd*5"9&GB +EX9@F,D1[K#K*#X*,M$39b*Eq52k[Pk3QEU%KLjZjD4%ESA(@M1q#eK1qaP0FpPH +`2j)p10-Z%(k&5f(!mC3hIUA[e-SkbNTcfJjk2-TqPB1bUqqkI[6erFSQi$ADYG, +&CN4hm%H(T3fhVNMaY'DEf$bBi(Sr5kTacDkTIpDD,Vmq8NP2$2SjqNPj9V8cm9" +j4SkV)F$a8KY"'(%FP%8$F3GpppZ*UHicK2m4`jr1"HI[PC,d3NZ!mL"c'U"1b!" +eVSaY)YDj(@PSC$99f1Lj9Rqf+J@&8%D4cC08KIGfS'MeURYYpQrpXI(JK24,9pJ +432aeKL@a8MC182eYU)aX%1)E+3)9ULU6HqFDhT+3!2b9Rpa#h0"lHB4R&hCGD!5 +LaiqMEmc2-(VQ2@P#X"%daQ`15cQG#FM,U'EbPG!VpX90kHNe%6U#+!!F'F+RTj% +M9Zc*(R4&8C%9%Ra,-M4,6Kp*l[0VX6'CM0Z6USJ&rfqYR#pCGHY&bL4$RRGG`Vp +aL$AE'GG"S4lXK`2V*$5lm&Ib4V*jFN1HM[hfNcS"8`G"4H@TZV`QTp3hpecGrqa +dDZU[c28ADS6U9BNp03XZ$bfVU*kXV*!!i1Y8p!6Tfhfrb0jjHXJ5E2!AAQE9kCQ +N"VZKmC&YdjSTA0bM06e60M'0@L-b@&kec6T,K3JI(Xdj+Tm"eaCir#+bYAXVHI3 +h2R)'9Bfq-D95lcb@I"XQG!-KCq6(meN%$GP#,K2-L['Kfl8,XRQ)B23f(GA)Er, +PDpB-[)rPKbCRa-,XLmL6KQeN[2(8ZrVGFT3epD8dR%6bSNb8A#!FqA9$Ej-H4kT +K5aT,BTXS*1jThXB-pqZ*EFU3!'A`6@GY5SM0j$@RQ6K6p6aNi%0$G6J"GVMm6pD +pe)Rcd8j*)he-UPb"IM#6B3l%$+kcUrePhi%d!(9X#CRh5"G%Eqcbake0'l%&eFp +J34h(1[#BlQ+kB!I@JDDD8ZV8B6IVM"@UG,QGdijH$F1k2+9M`EI!&(`$L[3+#'D +I6crX*",#1*@FPfL-Ha-UU$IC[bY[GXIDUiJ2*4"bec[i+Q"IqF!@km0qqm3-9%` +KiTqkI1r`2-SP+Z0hM(A#dfNQ2b5@!`a9jN"CB#V$GpF'iBNJ$b+C!fTEkYjmicM +rYec+r4&Lf!m,k$[*34Uf-A+U%a[ikSUFX"-IRH6XpVV$"6!KG1l2@qLk)!*"R&J +IB%Hja1pTAfQ5Pbp51[Qam(UJ%jP(mYAe#D"[1D-'4K9LIq#mVfphA`EehQhEGIK +1eI$Qm4ce`0Fd$MLp-LBC@#8qSZVKi,Z8A!8Z'4P%rh5[kk0Nj#FBU$N!k2U%3A3 +rPfhC0kU!4c1Gq+ZkLb+-T(##&-VecNUl*Y[1fl#[MB-#qNMaZ1m,"*J*GII3Z2N +TVQl!KQ9QqJhP$KfSX%pRNbMa+e1,UE$(ck@[ZJ8$edP@R!AF#S@4PPF0'&'(GjD +5`pA+cU#KY$c*hDS@dr94!e+3!%AND-0+I(5M`4UXq+05I&TAcS`d&U8B6G1LEEV +Lm&dkPd+V-)8)$eM8[hpd(Q$,5r![+PRd6AhA3bUClF-XX8QBleEQ$9-'!9,85F& +%9(M[BdJI%2"Tb24A$D,*,6DlXjqUQ`C-&A9B1'2hUJZf"K2(5i%6I!Qq+#G%V!9 +GII"-MJbcb1l5@F5A4HlQTQB)B*bk2$J`+Dm&S`bd,hM+3XFdf!d0'A1a@eq*,XM +K,SEeI%m[-cT*)1QZPIV089RcTl@,(3ehcDK*D81+4bICrVlXVrkklL&&h5'K$'j +fPq!KqU*4([+0lQDSCmTaS)[k-r9iharrb5)3PALLGQHUI%fa%2VP0#3*j+r2hiZ +PRcLa%-#V`"KlGJK4lN3AB)G!jG``3%(-#015IH+('kKR1hi+AV&[`&"X#0EU0Iq +FF!4BpU0@kl9UZG-hrd(CY&2iU`[l@50Z5pCUA!#r[HFKYjp'%Lh[i0S)jH#*pTY +NTM-0N!"ei&+M,&!F0RSY6P'q&+5BY2!kL(VJJM4feLS"3rSiG@QGceS'am-!L", +FHeda9p*8V#SeNF!`#Y0qYMJ*,hZa-Y`+k"kjQe(,RT90@kp,`m[EA9HD*+16Mep +J2A9`6%3eASH2LEIKMckf6RlR-kN3r[I0PYqaFK$R8l)dR!"S3N#Kp!Z6(KZK[Pd +HA+a3kKfPGH(j21FPZ4MXPp("NPaMD@qc%p9@3GkZ-3pjPp+ilSNN%&PL@TFh&90 +T!!LFTUfJZeiVfV#q42TkX2r+TQhIl[-P9!8T+l"a3,1fh-aTAmi6Xabd,&jIEYf +T%ZQ+C-$)INQ'JGJ&SHMJG8p1,jY52DDqEX-2KER!8B96+V"%Jd+*[YkKkJqDFQ' +S'q[&ejGJJHlZcaiLIS-X9Kr++R9XX`VMX[qc61f1SJ!@R%qpG%HDSZ9HPcUC6G# +&'(Gp%cC3FqJ-2Y*$@baMifc#4G'%p2Eq#%a[i)&#f`YG%ebGML"32##J3GhXXPP +c,EXKUpL1'R"p!Br`-SGaQ[KaBAF!K4#PN!3"!!"&!"#i)pUKZ#2DS3!!1aJ!!'r +F!!!"-!!9liJ!!U4E!!!@V!#3"!m!9'0XBA"`E'9cBh*TF(3Zci!ZH'eX!!"c`94 +&@&4$9dP&!3$rN!3!N!U!!*!(3X(8iXXZp(-krTf8IY"Piq#rik,"FeHi09PXX+H +qVbNSXImj5m86%'83J!02008HiH%4L*(c)IEi3[$9Kb@DaNZ9#L-cE+j"D*)*rmG +r(q0I)pYVB36,65&C4R80PLm1C2mB3lkV%ia3d$05+DF9T9(2i%Pp,R',&KYJ5,8 +pj#*e3YhJ#L0Mf&4Gp4Q%0'T!3ILQ#B-T-jY5j`%ZRqjkrfZ1lfpfq)`E8jjB-8p +ISm4Ip*,XqE2XPr#qY-K8-r#P[N'(59M%(GA+I*(6##2)`q5bS5'#JAp@46CK9(I +[J4*chDR4c-YSr*[Y3J3i$8me-aQrJN4@'NZA+hXiDl3flG[-G[8UNf&h,Gr29Nf +*K`L3!1l%$bUl9K+i(`bpEC[Mc`LKjCmB,L'X1ipjTEqf,FM!f$$p6C4,@h+EMJb +i$U$6@S'459-K3`pQi0bmDcVRLSfIJJNp-aDhkif2D1!4@Kqm!)h*lqP4eQZ98EU +"*QmTNdd++&fjfqPL'3p'*DG&GZ"CKf!T%V$`ki1X#eSjLD&&'D`F3PMM'rJ'@@' +hF5Z05'cp%DmQ4a+l*-&T964*"V)AeUpmCpR2h1%mSMF(q+-'Ti6G#E5R4q%"a1i +D*-E0Kbeqc&YI!p[e,ZcFpEm'9D+(Iq#bHE5T,AD2@Xf#%arVJ@EGdqDc5""D%EN +c(ep%!9*"`h@iM,Xl*I5MPpEe%Z*phC%ffD%B4%k4l'BBAf16e1jp!qCLJ+9i6b3 +6qj(%hGb3!+SI*rHid3rN8#ClH%eLG%ST1Xh(&`Ii$pi%ZTSN2S""dEL2,3)Z9JU +9UA51mhqh,DYD$X+#`[qZqXC"Rd$ejC@MU`eaG1M'1IJ9@!@,rN9MPA'-PA8$Y8# +ILi$`@eSpYb1LN4"D-b'IJd'5@'#hEA&MhaZ4'SSLLISbddS##T[rd+Jk5L0T$A% +"m$UC#S1fCHaED)hNjNf$bCA,04m83Dl+!!TfDN*CY0LDdmQZAf'ArmUG98lIBhC +JEJ9&HpR"2LN-dLSe01V"E3I*&pl-3m2eh1Sb)Dd'[lH0HC6QI(@d1d25#"8U+#F +#(#&2b'L**[Tqr9JcJ1a-$NL1$I96Ae+'Cpp8'PPmAk1C&LId&i[[c,$H%$MYZK) +`,k3U-+'U5KHCY)`"8'fQIL&'jIljFj4jK'hiEI#8q9-Kd4p#`"*TUcZiBUeG'Cd +NB&c[VBQmb3i!1la(*QINT5i15"bR2PmV0$(`V`0#RIY[KB&'pp#8[Aqpi%$&8dR +D*bpI#c9b0#Y+!Xll+k6'`EXZM2ac"H+j2drQiC!!%QeT1N!*`Ci6C#krmAPl)6p +4LND96"hB8%["-3Rhl[-,0lA6[QRh((GX0BqXCCYm9GcT4J&kV*-Dp201bp(fP5E +p6kpAMfJ-HBVI"![-U6qbp%j!IA`UM++9Ml#HEJRZeAC6`SDI#KlFf`4Vm5$F&UL +P&H1K`LHqaZHh6@m(GA$ID,)KNfD-QA0*jNj5$K8Bmd3L9rpd%"9A(('RQ[kq%6b +1dLiEc1cqK#i2XfTrI5)JU"+!S$$HM'rr#1A2j*)N%jh&D@M0rL@cQ#"3hBr#SI- +6J$Tb3N0"`NE#'JQKY2F'r!)aGYq-pL-4MGfA0J$L`c03iRf4"1T'd!c9%er2XIi +qIq"e30JjT#J$FNpb820IS9-4BdN3AR@Je6GVeFh@9T0+pll%L-DXVI%#X)NVS2R +`ZqJ$6+*EAVNXK#bb4cCk+8aD3d''l2@(FYR8IR&fV%3V8JMG[%b'!*01'CJ(F+l +m)U5MQZ,(@T)Y4NhmqPL`qfS0EHl@32U5NreY-X8MYIANPeG0AU%hf!eMD"Lh,S6 +6&6$#,!`NAJlG`rGVL5+DZIX3G&1*JrX3bTq5'iB4MGjGFe#fD'UP2(D4J'lS+@R +3DRcPqN,$SRch"Da[Hb6!GXM+*"bQa@0`dl#GFhLZ!%la1,TF'Dh[&TC-mTAm+D" +R"18K+KYe@kB[Y$hBp$#41T1mlJFb83q'BH&H@Tc3KhBN(D2T&K*aD&RBhe)q0c5 +k1FqPSp)EF4Tk%Df4IZ)BG!fMCQ954TRK19d2U@&,mkGd'rdqdP&ZYlD#(9rAeYX +`Z&0FM&`f!24EG6SdJSiD`0,%eqcV9r6!LJYMpqDqP6MT$S'UGaMU[&43JC3f1Q' +LidP%2VjJb64$'@2&"(N3Vfj!1Qe$*VS@jdFharZpFE*!!IcM6aecDZ+@lE@!iLN +K,a6fZH*m[,lH'r`91Rk2aBr(0X1VYGXe,9mN&,1U2Gb,C2Sk"L11F*'@`"@!jh+ +M9kJ8%a1eJK"$8rR%lCC9F5mPC4!4S!+VTh-d8J2$G,ifVAC9HqECdXm$*r0%GBc +IFA!AJ3)ej1%Ri4"MQic0kl2CmDYDA4i(6)Qdp3hEmB)M+i3R,3d(Ghd%Pb+FS%2 +qjbDFB5N!aYrKY#f&HSGBAYJhY@hJR8mHTr!RjNdA$C5&eXla1B[rEUk`)Nb&Xqj +)55Eh5UN1!"dQrN3B)D5)H"ZqZKr1[-Y@Dr+6*F!4BJ&3@9Cc$-*9jlT&8+-A&a# +KCd'&2hN1Nl@VpZei(a+SF3CA3%L(H8q3!2Dd2eQ@(k$5GA#C@[[ER9(#@C9(*%c +1k0&(fSKdSHk%CE1ZCA1J*BR2'AIadGCkF@5rikr(SY9RT8TIP0XiU8)3ZPk*UU3 +BTP9hh1#+J[RRG`QjJpZT*NYMSYLr1T,PmQC3dIe5[p&X9QbGKm+b6["0YafcQ)! +(4q@cK28qNcQ9H&[F*Gb@Aecf`YV'2ri#qC-cB!A4+X20U1D5EekA[De!%Bf-MDj +0HrU,dFPk`1,C#eUX[LeE1GrL0fMhYRF4$#YH1FQA1E'JiQj3Mh8F$j(9)Na[mhb +4QiP%MC@L6`r"lDlVR*3a*H$%c2pC'P,@fjUK'62LN!")-VlCR+TjM2`"9RYc3Ie +[jUDX3r5@b-&lHD[CjK6*dD4iP5Q%fr'L[`-%6"[&I)+f49&4`ALCj4B)*"KFK!1 +Vj4rLJ"!ESPadE0YI3,eERRVe*M%TfP,#i6r)-!lEpeMk9,0fq!'8)EJ$1!D+X*p +qAdHkQE0cJM@UiC`R%kI,QMr+QM''45)$Krph*,(*3Ia8P'IT@"T!HHK3GJqEd4" +N(+lTKJCcpXJHh`"a&J,QTE(C8jQ4Yf#V$&"Z0I"2!fl8rZ13!#c1ehJqV-U)3d% +-BSS9hPS!M9Q5LcUE$N8"l66hK3U'RGKm&l&TDm*&1!P4PQlYbDGN$1BqZaG"a8r +53MTS[0c1TK3X)aX#DR6SM!-Z2,a&iGCQJ'@$V9#C`dfZk4G()[aadj!!-KHem-B +GIPI(2FSZHQb9e#5AC,Dl1$9re8`k+(&"r@VFl0&f54V$F%c6E6"(BZ!hC6k'PQi +4H$Vq@MSH*AXT(b)(e$*MN!"-XpT9D,G25SQ$EGiA[)Q@f`3@"m6jPfhe+lUKEh2 +Y)h`@4fX1AGG'""&h@ec3hPU)bVd%C#&D86D6%[YlF0&E@YL*mbFGQN"6BRK!kl# +hpRUBleqM8H3qVh+hjXhDEm@9h!1IZaN2I4`#FZca*TAKqYCDiBIq@kJGd'd1XP& +!-jd'FG%p69L@Xd"F"%jUYkQ#%l(9arp$2H#3!#i!1Re*28BDFk"c25"dJE[TU`F +BjMYRk-D'(Ca'q0#NeIX5GUQY#V&XDpc!4PdZe,G&l55m6krmC3'M(0eLG`c-$Ze +ZTVA+8SYS+$BBSDI[S4SV`k[0A8A"CGkT-CGGd61Ke8"jr8PTTaEM6K$iRh0e[&b +,@f5[1h)2GAd1%"DqjAY&!bRp+T9R*P#+c6(DR"&)cdQrhZFVXciH4XZcfU&@NCX +6AcR0P+ciJHcXjHm0dj-C6bC'`@RbcEca&5*CFpc#,C3-Ra$lP&#kl1XZ(k`9Gf8 +dRGl0!5&kKqbb4K5YDp2CAq('HX0[Y3RK9H8U%rf"V0M&'V#SYlm5&(MI1N`*!cU +"`*QBrr-#-Q3'556pfD`2'cXcKhHYdLS@+0&keRBQe35"SRF-Lma8[h1j3Z+ipqa +P%Y8LfXZYF`18ABfV&iBJNQbZ1'iidA1(RI2G666a,YZAlSULp%Ta%9QrLFHEMC4 +r9QRp)UrH&K5l(Yd1A52XQ-S&GB#aBe"d[i"[kSbIaS*c$QciLr-K-FKQ`Ukb6Mm +Rca!c2M##l6$epilpM3dA`la2IR,!jX-jI#E2$X$Nk&BG+m)YXB$TdV2"PfeBfCM +EqKEP*U*(MaQ(aUJ&lMDDh+Z&pZJKTbc*)&'6,L5Q`9VaRc-G)Y"Be!T!MB5#!T- +T1bIAqL4!GI4V!3&V*6blm!23(S#emQAqC`4pMQrmQmTHf!`HS9dc9NfPiQ6[bqr +@bS[)GK#H['m*XImbD[IJYK"r-cCCVVMal5$Ae()BVMbr-CVX-BC4FpHP)lJD"ED +@apUBLqT9KA`Y6Nlq,mh@AE@'FdB-0bNDR5ek$rjJH0ajj64MLH%PG")VYPFVf,J +6rVaYfVYLi&f`)'IjK2HcK+,9HEfFQ,I*ZM8Y4'c9QiSI-eqZr'TY`9m'A+I(ZEe +T30+hjpa9l!Z[*`M'Em#I,dQEYCKZrM4pdhlY2YTXDcbBJRqebDk@RP'`J(FkGc& +Zq1JY[b-3[-RV6,4T,PqVV*+QejcB1hMLri(+L$pM4#h"QfIaj%+XFK$AB69a(TC +*Ef``*#EkVlck%85ck`bdGd9r'FDBfFi'"fKmdTeUaIH%A8+(qCaBAAFkV%2PNl# +B+*)+I!KX"Am'1M(3lTS'CmEPI@bj@S"#CicPA`K4lJ&)NJ*X3c2(DV02*0#"e&Y +!l9pTj2@I(4Fr6hk-F&01-aRq(qYrrj!!6bESB'035kQRdGR)idH,$`m-9&-qRj- +mli$QcAU18AfJiSrB[`D@[6pPpX[6)k`NMmmKU,k"#G@F6me!"KIY0aDdBd'EJ"0 +)dpb#M4YPGLhNXQKImTdABHacR$jU2HUVmAG,XI2rhmDG#$d!!c3fUCI3Jc8)p)k +!P`AK$45Hq$"SchXaBpf'b0H(F!YFKJS9j#(@ZfA"keKF3CdZP(lTfLTA-djYSU9 +[[-pB6YaXq,9Y8+qL"N(qpMmlM%EfeLV*Ip-I1paKGXkK$5*'X6k-*fp+bpp#,&% +TG)llUX8XX01qq9k@P(FEb@[-"S1Y+K1'%!fh6P0pV(`q&CiEllV3`R0lN8*Z,Ac +$p%BE408dhJT4`iB@a50-P"m08#FVkV`[q@#[l1p@bmfl'L&C!rrH10&)*arB`AR +f2Gd!mE*&!KDXEF)hXX@bPejHcFSLQm%UJ1D9+,&c!RUDiDFk6NlUF%NYEF&6pl- +Q!#+,E#6L)h@-j'MpAl#UK(G'l0k8h+A@hSF95mZI9GerZ6NPApph,[IlmSXIR%6 +"ScBe5Ief(VmJmFj&hIV9r1ML"+3-+1fDF$b2brIN3QBU0Q(L$KUBeE8XcNm'+a6 +A%rM5[i%jcr80UK`hX@Vj[,"0*1)4KVrE,#4M*qPEZ2+#(iRP3d2V`C@8"m,Br+T +C'LH1j"0X-"DI&%B-XC3mk&qd9[dR'K2[0eea9J-Y"()Xh"0l(`jKH#DK@0Aah)U +iKCCaF,bZhp0!)MULqpV6pM4S+fHf)&rfJV(G0``"$&8%a1"NaRKZYd9MZihm2fL +)dp`CpE#UE"f26MX1JK`L[i"TS%UeEJ(Qma2(ikKLU34&T@YXQ%c8fL+,if,*"dL +4KIR"kd(N09IST6d-f-,DX[QLN!$05TM%'XPScN'+F'8q#CXXPPGc"jPE94VCR&C +P&NqQM1Y&I@8R#dR6cP$8fb5))mQk6lCb&qB,pcAU6eZ66rFX'm4UZHam[hM4HSh +SURSNNm3A92)+NlGQSchm5EdM!j!!4E*p[lU%,p8K&H4cE@rkrQ&#l`HmaVE,k[K +3YV!Vq6`(pfDJ2RYbJf9NSQl*!2XKd03K'B[b6#PYdlp!fTr[e(eqUdDLF5(&Ip2 +&'0)V##"rfa1,0F2ilYN30mr`@PFACp)K6k,d4pJq)YVBI0@DcRcJcZ(3+4S@J-L +')2Z5qKZIKD[VDAKG9eU0$`h6"jC,(KcZf(bpdBIBqBKTbY8eN!$Ar,6SFHT5"3U +TZ59d[(H2Z&#UIpIBfRIj0I#Z+3aiB1"K9(q-GNU+bpBMfk0b8Z"DQ"(V8abXNMR +5f@ppHP22Zq)JM'p#LBjjiC+VYG)l0l9&m,DED,la$4J-dQ8k-S'3!(04AfXG1-& +!NdJf@%BbpNZ88NQF[i"qfrh%e99SrQDIk!%KXiAF[rITj&9((f+K)PXN!6QMUkF +lGeUiYH999$pjCkm9$kGU&PDaK%Ur3kLAfeY[&#Bb*ld4RhhH"IIVIZA#$6X6YfI +jN!!&b9DTcU48-6(6N!!iD+`Z+VZK(CNNhk$Pk3H%+VEd(JZIj9*JX8a+0m6hld4 +eKBNUcJ""3CUcY&9%fPFC+2FT[*dRD[D[b8E-#h0P@m'9'`2a-@RD14(*T01N30* +pqjG&iI*fAYr!AYfZCr"RYKmIMY4+%J+0,DQMi8I3kNil'%L68[Mp[$Q($&-VbTY +q"SD8jrU+1r8#UUIjJdJT*mP%0I*8["AF98SA(E$RXXhR+kf5@0Je$4TI#klk-IM +*iS4'(BRG(@*cGdQ"4p"QE@%SHSB9b!3HPq"l60fE1SK8'G-0G82[I#h6mEpb!Z) +QcK`fVVR598F*VV'3!2ME,6I5-GfPJZSNB#iEP46Z'F%BlC[DHrbXZp"+kESk(3Z +IAC*3b3H6DmdK"HZSI[VH0XdeRm1UDB#+ZbhI%)mIcfiL(3!M,EP(1ITD2L(Kep@ +N)+H)"3P3d!66L$pBeLZ3!)i4BL6QbC,EKIITd$p,FlK-'bbe)%T#4CYfjr2X$*8 +&(RNCP#9*!$Nrp`6L@ZD-cS[,#d+-2$p@(5#YH'cMQaLQa1PMb*8aTIbhcHP"#,H +#d0QY*aK`AV[MM@-)B(YF5bR,Ra[pV&#dmlS[T3mS-8Hkf8,3kB#eUh(+e(#9EBZ +A6[iPr0I#ie'lla+i`(mVPI0R(q4VcibXjl[1H`[k'KMrkN[aB5T9Fhmp$jTQi,U +ALGaa0FmUHr9G&!qY`&rkQTJV*'hd4fK8Z0iFCp@keR!JKiHKJ%raGIB0DQ9'k@r +q$lq[1Yk'5cliEd'4GFqFJ6j@a'CQkRA(3I8#kEFQ3dGlVTjNZ[IeGNC,DS-L((b +)1r6![lTk8r80Up5)bV)R!8eREC(4AdU*`C+Z$IPd93+FP#MPJ8RD5,(P[BH)cR! +p&(CJANqR+GNGkT(SS#0Zd1(R'De4E#2[#1M*j#HV4NVD)P@,!F[PpAKIZ*&8YQ3 +cb48ZMbDM!,YmraJpLq0@&DaalYqa8RGSL1pf+Xj+)XrAYpiLN@@1PGpk+65Mf'K +eKRA[3JX#SXklC)[4ScJSeR6ca)T2fD)[kXjr`9r`0+Y"8Uqk6V3,A`LA%a!N4(3 +4C%pYM,dLb%H$`3[afBalFd#'[JKdC3XMN!$,(95KFcN9JY08heDX-Ti`'&6a4df +IQl!UGjhVCba)E!($03!'e$j4aJ8kZaS(L!Phf[jI#pN8)m!"0"QBYb`bEl0Y3[p +IT[l[V0@$)2c"2daCrF6epKEc8,%0dI@E-6De3NQ9h&QN)XlY0N54p+CZk%DVkpa +aE8TpN!!FB6q!XMLJ`ak!cQFl!%BHQA8P$!8pe56DEG,jGKCb(V`K"1'q+I-%lk3 +K8(Y0P*!!`5elB-m`@D-qqK9#mAVB`FQC#N*33d1[pjV!)ch@)1j4aDd%()LKaLZ +!j5*2+SY0hb8IT$$F,3Ef'X+BD"&)RmIDejh3T&bb%qFaMS#PN!3"!!!r!)#dSD1 +fZ#0piJ!!@-F!!,L#!!!"-!!21mF!"@Tj!!")3`#3"!m!9'0X6'PLFQ&bD@9c,Xq +!!!"!XNe08(*$9dP&!3$rN!3!N!U!F!#3"N,"e-&U[TX*XY+6YAlamJKQZS(1"N" +RGSJ$2JY`9L(8jLldKr!KEIrqiA('+SKEH-lJ$H6Qc3@D!XhKHQI4#BF(daA$k@% +2#p(X2K%3Q'$XQ$qZhfVM%3e2p,jHD'c30qqe,**l(R6PcR!EVjM&DSifaEX"KH& +LJEqqU90djX11RPmaIed$1D$AmHL("J"3%I*34XlaiDb2Xrj69NfEL5Rfq@8YDYd +MeFHl`eJh!*PF-UP9r#YL!4SXfU9Jpcr+E8MRQieA!+KA4*F!JJc9p!ehJpYm4*p +dR'[NMa2"l@+dYr%T,CGA$YM9icaH,*BKjjJ09'6)"M)l&6Q2,pLqPkDhFUZGA)L +3!-Kcbk5S19rY6Pd1j'J[5FYjrJ)'#(aB1GqI!aB8CAdNrf)0VIY(qIr#K#P[hDT +ki!eDcJb!bLe@Bfl*&FB1NB`GSa@`'JYTi%$RjkaC)8&VI[!+TiL#d(DL8V-@++r +FeP#C[FNhV4ji4X`R640d5b%Hd-ZXj48$`CA+R-UGV5P#$fDB9ZC+T0cUY8cJJ"% +f,"MX&ibNh--&Nd)FD3RJRS`#JVS,RP2prBi$HrYffUcUlZ@elRa!YQ0F#'BKLjU +F68q"X&eJ5E3P56rJ3-#i!&$1G[D3!-S#kF[2f+r((*K*%eiAPPX('bl#69TVVYq +lGKP-#j&&eq!9jRF`"ff5QhfBXRJaHL&CX1R3'G1B10LkIT3Z$h`8Kq*)U1-Y%FE +8aRq&-d3m-mZ69ScTTJ$f"6i@,qcHrU[(KA!5cKAQKk1mpXI%28&)hjM8!95Gm!( +#6m0TmM9T@cd8d@V[NfbL$%ed(,TL)YDd*2@qPr$ep(fqGj,906[`l+IbcJlqSCE +Jh3`ibU45QK!B2#BhF#[IT*ZRHrXVbVE!eK&+l@bMmqh"&ZdJE-kSAPH9mae,Tq1 +6T$IjL0*d0Q[p(Kc&"!r@dTISYipJG*!!ES+Hjh[i)0p'IB(@2XmcXN*9#9[iTS3 +d)1T5CkE$jqmpGD'iB*Df#)J%S$D&T"ra,S%e@bTBfq!UZ"I0N`Z"`EQl+adRLC6 +ZFSF)4fbN99H4+*!!14PH29'6*9iI0,,4#6cNj!#,Uj`l+2rVbLjKjIQYh`HkNde +*BkD`93jSrb5GG&iIR$V,"q@1*QNY$03d9P4K4M&aL+cFl#hhGR!ZZN1qbF8Q!Fp +F9db!%iHBKT!!LmYI#4ePU'8&M(R[%ZMa0kD*ClHheDMS9X!$$6(NjMZ'PYY-XH` +1&p[pN5i#PXeF9`KmZSb(S)6fPm"KD'FH&-ZM$RBG(B%KI@%(AM'Y8N`B&"LUI9q ++%ja5CR9(mqhT2erEk$0dlMJZbCNM$Z3P8RL9Pre`qK%kDrCXDk&HE'lh,D4!bHp +@%Iik)SCi!dF$el"!9-[lF5JfqQDpr3rKFLkJCNk91*p81Mjm'%#rGP9b2U3`8hN +,@)Yl'M)Cp0qSQp9N)XdB,NC&V2@k[hqHP0p$,(85I6XX-XV1qHTV[9XiJ9f-1!q +r&Y2ai9(-EV`9aH)JjN@Qk3-ISfJk"ZQ#KbCUM8Ed90+DTq2cN6m!fKRll"#6NDp +3B*2SfRN'3QA3A2%BMrMJHXVL-3`QX6cBLR34P(F[T[Pd@)36UYc!KG2k0PI+e(U +LPV2)ZqZr[e`6YHeFN4P!SmR35Pi4MK0f6*MTk8E%DrNR`FA#UPhKkL9bMNcJhp* +!9LX6h2l4Pq0Npl$G[MSj@(Mj@af4+e"%U896`,8rk9P[Y0*'Km,K5UL$+ci)P)Q +65XM2[f[!pel$mdIFjJ0@+pYplRqeKCbb8E0[dh6'Zh)j$69#*4G'Y`PD0p+V806 +f1F!11lC`Cf6k5T*rH[3*cDKjdVHXL8(GACq'0NT+CMHkB,"(86RjH%HRc-ebSSN +)l-l+fBT5NIP8J-10p@`fPbq9STmUq0)88fL0F0qbclkY@@VcTj!!"D*dAUL9H"9 +0`U)NdY'&Gib2*Vl2@KBKP%ji(3EdA6AHeXhF'V6LP3KL,ZFX&dJ8VeX&PZh-VbD +BL$HR)0,d)L-0I4F%6lNi8ph+fjS'm(rqk%ScG(#c-53N2)E2S1*!fl#Yl8C%UGd +mMH%0)(4k3Y"qSN%Ja+DX&FeCe&-6$IU1i#lY9,!D8U#Y!rr[+43EVZ6J28i4"qL +++di[8C%pR()6$k,lJ4')ke25(fSl#iH[pN[T)ee@cZ(fiKF'``TFkBXk4%6HPe) +,(iE8AQSK-q`Bm'34k*@685#Kh3!50BCG4Mace(#SX3(5AK2%r2RLZ1qaN9ZF5+Q +MFcUCSfPQ#,$,3(3ED-[T)1!X$4*ZlpIT4`Ei9''"`NaSSQJ+2PQ*V&H9kkrSE'" +hefPf@aXZKJb+-22"0KP1FZB-`hAcb)Z'iZSbP+%$qY4r9(l26pFZ&`kX[H4EYX) +e$ZFAX1"5)I13!0+6@LhYbFG$Xfr5mf&`IQIjQHA+0ld39-d2XK%h%`mBQ[DGHRD +-9!AZLTM+X(Z4"H63f2Z[BER3@FALXpchkUd'V8QI[@V#3BY!j5JfH1IPQPII!(G +#$d-VQZZ%dMRVmKQDr1U)&P8RePYQ2%''ca&U9P(SkH&EPPT8U+,2S#BfZCmp*mj +!PFVHR"GjC9lI&AhNBl*1jESS1%Yc[YeB-B%%Uf'qD0md38!'fX&PD%#aDMlEQ0* +mQp&4d%P*,H!XD`+&UJ(UXAQ!ec-6X2Z@F9MHZ)FGSi!UqiYla"ZIriPjN!"fqc) +DFGZkp%*aPmSjX4Pq6[XI6Y)9,CE-#-*`UD`"JK*)Mce642!i8[8Xp"1d+e0V(J( +0(2J@ZDfqELISmE0d,5@k0k@mFqEDd(c@Te(jke"eUBmRr`CrbAL09Z'6dih$D!, +Up8Ae6[+kcDpp06#QEDZ18pC%Ad38,FXVR1kUEZDiKVlA[16DVQP+1@qDB6T8UY& +BmpE9Pm1,Q-Y-VTBaD5[G2ipf,LT'GClL0B-V(r)m@#T0k%IQ0Fp4!m5)C5X-$A6 +&QQf-CCkM)R!kU$&48E1Ak,T%`hiTbfC4+R%H[&PYcZ1Mb4LDHAB3DefL[$K23eX +CG+J0*(CaBNDkT`2kEiXi"5620Uq63D`A'+m31@@B9UbPb++m0M2jfraD5"UAd+i +UN4+[!I%B6AVZDFqM#N-`4KP(VXPjc8NFFEihGPH+J8`-$*!!,h23*,8M*`a,298 +p`@iL2c)[XJbir*ACEX1k2F(Bde+1jUUf1[l4B5q+JD4mY)i+[86#2X"TrX!h[5L +Ka4diMf!!j3"F$2Cce5`r-6qZqK%eP'U8ilZbVQM'hYlNqX#HP`r(KQ#LF9iff'q +a$ZQ&CrTmeMUR"GG#`8NmjIkS2YXT&0!C9a'pPkQ@l%5Tdb4ULb4GM)(VKQ8RJ*' +"`*G2QF6Z)6kU0GGj5eJ+U5-D4MT#C9amqJ46K"UhqE*+YkpD8%L48i69HI91ZrC +j%&eYkjYqRQ[[)CmfkHSSEd,RXLSDA"X9Ekh-CkXf9#,+VG9)2(,ipb@@cDC&EKS +CK-M$Ki-Qq$[)kMZZSkGE6KJ6phlai,LLaC1X`SDA2!RPC!Bf`GF,2MahEkhLF** +1@V'Bdd6NA8e(A@95&-HlNrZ'il$L2kE`@RIXGiU*4`-PLeiK[lqSh4eI1Gi`rJ$ +2AJElR*V!&r5+aD54UQN0$X2Vq`NP%IIf8IjERD6!Yc9CdMH$6jbGpB9SYT-BJY@ +lcjLkA#Dhdq0(Tj@A2A9arr%DM%)TX'fUdlUAbhB@-Ceh2e63A2'8!Lmr[8L@UZM +8%edSpmZm$)S3kl1UbLKDHb(22`Y"50dP`Ua00j6$@hmGH'Nr-FR1jicN"VP8BPH +,a'jZ2C-L3N59Q"Jkf[k2-)TGV,N#)VNC9r%a80ri''p$V5!5NFBKb,UkG5@m&4a +jkkYXlP)QQCpG*eGpY166K$0[N!#2[r3+d&h!)NN-U``'!EbQT',dVeYcm'1"VTq +k@&b4j*heI9fEpZS*h@Jk,m+dFLZPFCHE1(K2+)+I4q(C+1+dAB8p$e4FN!#''** +)N@J8'FSjT!&M,I15&%4HV9`bFkCJdkI++KkLFAXk6Njd-TN`YiCUH63Qc,CTDm9 +)!`EaF5aV&Q2*%pSck$fALEbh0*AUG$b$mb#eX6DP6c6-hbk1,`FDE,GD@36qM4j +bG,c+!(I(1PARBCp[8G4(UeVeB!Y%5+fQDS0*b@SBHI2bJ[X*SlkKkqXFPfeV'AR +rD`[0M'H)(a$APST1Lk`Bk&p&Ta[1(ThSk-GpIMZTNXC)T0Y,V@p2M0QjbX41[48 +[rj4-m`5F-"`9NfTFjKRFQlljSrj6!fYPDH2+!lHM2QSFBlqFZEDm'ff-LeC&&kH +@6Apa5l*h-F'a$G$q*@`H8!@i#-`@iK4ADl%ECd*4J#EbX#)R+(`mHh(R4f6h8#@ +qTaMcdZ*5'UiFbVXf3FFEd$Ie&YZYTT*ZFV&C+e`G+"TBeiJ9eNPI(k1#TIJCYqR +X%l21GDp1p0)6a*dLHQ#21)H@TC01%%9-P@j2r"-+jAiT'1LUEG8FI4-&ab[X-D- +TS4c@@je!8Hq$%Q-LH24kd@M58#ZdIhMee`Df(Z(I8!`JIQ9Br-[+%SX[K1(12US +#62U@Yj1)$V@d-G)#i[Td[jdT4BK-,C5KB2BA#[Q8!ekF4TX95HI8L&X#pARRRc) +ZF8*BplB0Ercb2UcP`'YP29IkA@3l"DA3VrEJrM&B,fi+)L8"KT12JL'([Q`0FrS +6@+mZ'0FGi1NIhIhDm%UY-38b,,3HBQChpTJfCN,lk4h9LCKR%q1[)3j)3@9*3fJ +&+0VXGY!ZE#$4e+cQ5KEf%Kq"0BJXlB%[6DKmU2YaH-GDpq8$)Cf'$YB)36i1JcU +SPL1r"-T2)pE(85[eT#T%L&K49C5G(kZZlY"AS#2K5EM$K32m!Ff66jJ5rh5+Ui3 +fHC+P,#XGa*!!T50pX)'%4k[iK'M)8L`2K'JRf,9aqV2+SmcUX5QYUkBB@(NRB"8 +m-3LLC[a!R+qaQjHA2@br`Qk*a9a"kSjmjEmF"%0V6"PHe*C'K#f#1hRJAi#4q#M +QQ61Z9+!@)I#5Zpal"U*hQlGV)Ll-Z'aI3d5le!V9c6)BC8+mmaJG9GJqM1S[#ZL +8(1@![M[+aMeLZ`ANM08f8F1L3-6$%d&p(5QlRp*$,!-a$'!Ze!TP$")`&fjBj!U +UYZ8p3HiQGTb0Mi*F''E&#j(qkJC`T!3N)iS0*!B4!M4AY2[MraIAPlH(JUQDR5S +)``p1lq"24US#j3F#5)T8lUmAhdG6aT(E69[[@XaJAqRhdG[3Fq)H@N#C"dB,IkH +3!1hb5kf)dQZ4HARGSiAETbr`3(,$M&,Gj,fXE+L%+&XrQ'BjXk#9kED@EMTb(+a +%&,Hd1QM@5FcHTmZA[Yb8B&0F*QjS8hP)JHU!e$FDpi2&1%@,SJ'@*K9pGiY!AQ` +0kiUdX8(XC$,b%B&%[LIAMlQ4ZX&m4aA%`he8QJqqajhU*,UE`3ZZ44B5cKCK5YA +I2Nd'B$`X4FfZK0,4Pc#)&leXL1PcMJF2U+[!XRQTLhLqXP4e$JfB%Z[k"lqQTEm +-0H2f#Kfhkl(R393SC`$C6aBX`%lP'42"4!8&l*(#rh)UiKJK@mKL[R0T5SAFJi@ +*!S5dRh3jMCZiij-F0#,!PX5jfcT#l09dSZ(S9KD22-1fhMIX3J$SmQ[0m60"h!F +NDU96EPi,JR`JEdi!$CbT4Ea2cV-(ElZlKJfqSdCC"NK5H`q1"(5m+A42KmEG#-V +c!%"$*@iU-ei*S%Ak,0M%mXShKKR)NmIBHjYU),&92RZ*Z)C-k,)9#E#4-mS!l5Z +kQ[GQel3$FHN%ql0AX90r+[iEeIj5!R2Y8PeE2!a5m"9JqVI6P+k2NYJQpqfqLcT +-Z(4fmMqrr$Y0N!"m!I*5"'8bM1A'RTRNfE%#eIB#DG#1m8`me!HG'TY+p,3JF)r +1i&IUjZ(f8+$)RNSX"[S+QBNV(JlQYec(EYmHUc#950L$8KaX[!f(fY,9#S8bf4k +1V,#R+$h!EA2Df#-qbakIVU4q$-0UNdHf[L1JDY%R3Y#p"Fq6BQ"60L5UZZ9h8jL +IV))%cSKmHB0`5GFjTN*C-AZ,V0R9R4(aKamfccKqD3+qmdfpEB'he-@X%kAXer` +2+p)3Q'RVjI&E2l@"YeVmF2QDJQ[LN!"Qfk*b!Q[-NP'e*h$QA@MpGcM',6Bi@EQ +2Gq@Ql-'M$p3B4kmLlRBjLXY0HM)1aP1+Jr['YiIaDT&0Y"6b3,mF`2lcG0RI8G' +qQiI3rXhaTVK9(fPKGA)YP8iMGI@Ph-4GpPBelX$pB,-ramNRB-Fa`2FAhM#T5mG +lG$2JAQ8(2jJjCqQ3!2Se$(,dQK$PI)@8ALd-N85HLpjT43QkHR-8E#+&ibr8[*E +9Epka(+iD%!cK5)J1M`*C##)HRC0feS0'b-%!GD4X$c4+'HrjJe1c5S86LMpGS2P +e(dXPRK#&d+fQrj'[qi*pQ5$TU)m+ZlPjH"CFRQd,lq%45Xiq1iT(%kP6bLTfaYf +$I+UKB%G"J&94ipS'$(b[*'@*FaZ0jRFNZZ[%XF9jJkSQ(A+X4EfKD+iVafLB'i- +e%UT6X)!JdSQ$EB-F8'KZ"bNa5j-kG'GU)[-,-c(j,265-86k4UfJ$Ka3KDQ@1YT +)CGj"rq4mp86Y#*!!TK%%ai#ABVD,Fd6mJ`A9EFQSrVN#jkM)6T,6AjVHR*TLSd$ +GG!L,P3lC9T,M6K$DrC4D1)PcUFi2b4@JD&*L-H'EEII0mb"%N!$%SJ1Fq'NR6CD +Zm10R5h8TYRQf2#8Vcrb9'3h$*"1S3*%V$d023KqADc&ES`XTK0%B,iJX5QEh,Ad +fYR9faC(caZ%5U,[d5+G9DlIHPPE''VpX5'3[UPjmBAcEIX)A&3pZVZ1%bF'3!%@ +T"+ZK@*-606P44I)5VaieF-0q@rE*'rS-10`!(rE*,RFe3Q2AA@[YG&!"V6TD6PS +hX46(#HS)MPH6"3&L'II`!T&NTRd2f+f5`)-PiLh!#6Z#"C6r(G@'f+e!*VeCJd- +8ReaBV+L0jFEljDQq3RXJ'HA`CZif%9F,pG+LCIjKRpp[5+J%!da4Xr(4hNR+am9 +Mm*qh"KcMSqNEm(XYq&h-f8MSI6&N3ClS`*HZdXe0,lcfP9+E)QIeq#m2cRk"PUL +')'rD4kNRTM@jYq[[P#-G83Ack$p8(32-Al@ed0aL2$9FR*d1H-#K4P1kPBHX`04 +VEqDK(PZ,1(aYiXPR)@G+'ENhZN#15-Y*e2'KJ2EfdACKa9ENC!HiA'AVQ2k14LH ++M2bN8Re5F2(G-*9h!55CJMAcK8($f$##GGMM)1YS@D6-5c)k)q@4*#8&dS!G+q4 +%p)8P5$36i("K@#ClZPcq$YQlCfEfI`bTGR1-(RR*I`A($X!6RVkNA3T[dYRedK4 +9E49)VK%%-Y0Kk5q(JRM)0CQNEk%F%%K9ZDBq4E$XmF!5p'cF2DKf!"5LPiGYPXG +8K#64ZC3SIQ31,b'*m&rR6,Ph+Y[-Zb*+d*!!rmRj%#jMZ&HmGMd35,6A5CMq"jU +#`+e[*1PXf3KT6(3(piP'$'V*GKdr'iDU(+Mq!9E[I6YKde"hD`0hBRL`h+i`KT@ +Ea30b(NiLa&QbHmDbaM6aUS03'Y('ipd9$24FCYr0k#5+T[Z'MX[P2cB4F*kN5R` +T5VKmeL`$R#j%qYkXDc)@EU5#+&m"![drY,dT(qd&S[Q'@eK4jN9eCS5RLKMQhED +J,f35q96G8pp6Ld4Ee6VTf-E%+9QCrT@QQ-,XFC)ePH%)#N1UF+Nbb2'6c(Kd+L@ +X1ZE4,Me,1F95k*cGP%V*iU%bXK'Yh8DSRNjYQq'ljeqQj(UI*DRbH',GJh,*E!i +L@)FdFTY-Y!5bFc9F300&)9iHj)bba`E(TVEdCSP22VHp4%RR`0IiI0R9V@XI&e6 +i3d+1Y1&,8Y%SHXQdH[+52rX+0MdM,SIL`458U3Y4`YZSf2ca06ER%I2jXGD+8F6 +ChKa"f9Nr3)QSDT!!lXA-NMkh*"X-1jmeHm%%(VQKHr00l[U+SX1BjTDF'fB4dp- +'ZXEK"IqPPk4NfI3H,`PZ'Iahbm,91EVrM)p1jD6rJD5jBU-IYZSi*GqD@6Kr!fG +F("J3MSV%Vh6FJ0$-05&'!@"U5#,9NV[QRZG5+`&TS-Y*mjReAh#jXXNGB5r`["! +F1BLrl%,R1`JXA0Mp31K[C&I6EaiSHZ2A0a@DH+fHDDh3BG9LUN%kU+K5bL!iS)@ +CjSVP((Q2N!#iaENkkE$fU*SNr,VN*#kIJff6UMTERIc'!pkSQjMjGJk5ZBR&k%G +&SakX9TDkM!&eS&X,CPJ-e*3+*i-fq3IFj,k$Y*1mRAJ`pfkV34KSk9XffrkpQVZ +Y($5di%4E6VYbHiVG@@-&$'ZjYc@JDI6"Ip3haB(`aG*Ki+Jm)"0%d$UZCrNHlb` +JZ2p-rRpK1pe!cY%P0&pIRE5SZF1I3"+`5lRDJ$kPf$[1B%@f+NS@iMA6#Y8iVjI +D4Xl[fD*8@H&AY3(KDIm4rPJJGjMDp&fPd3YVVfAD1J`Vm&!0rlX$U5+9`3f8ESd +-`46Z(9,&3h!L3m9f,k%0T-k9AZXGF`NA&)6Y9i9De()N!A@Li[bQL,K)X@-)@6U +I&d#Gk,*Mk,q$EPr2F'9h3YKpDI#p$eYU)LrifTCNF`ECY9VU[f%r"fm)QmjCK3G +qGeJEdk[KVV`r8hXjm%%Q@S#U`RcN"Sf"+0jiGBRCr)"QCJ,hF*)GT$rJ`Y-%I*[ +ZN5AZ2)KMm(V)c6$!LY+Db[hP3UT0H8VUDFN!!ZhK#TTrp6B%%X`2*bi2,ie3SAh +F"J)JC"28CPb*DZD1bUm055qN2B2YC2AJPkfSe$0E`LY+ja`q@K*f`lPh3aZiII( +p34AS3T9!MYpY9NCjNV*H[6a-16NAd9@lS#5FQbL-#*mDFk8j8TX4eFC[#+cQXBA +c0L6hc1L!KD,hqJ0f`Bh8P#SbU1%(12@QI!KPijVcb5$0a)qLSBAMYcpkPGHKGep +Ch+h4+A[mL4'Zc#2%UK"A6rdNbD8ir3PiU"FYG)T-KNMRf6UVdYd5Xl31EjF[4*r +dX-4+ef6[AU6q&TV"`5dQZbpI`daaLT1ch8hV-XUfd2*9*bap3#CVGbehEEZ1*Vd +i6R$jVRjRGTbaUEH-ZXdI,1jmeGIUQT6E4(G4dCI#"3R"ZFKfbi8J``fefqBMBV, +!50ALD)i8@!X,@B,R+MMBNjHV`BXZi1USEfbQq"G(VSNQlNVhDcmGbjfbl5d6b!E +F1CVj"IX[+LRk+qq1)H`,'Sr&)M$&Epm,*UZPCdQ0Pc`Z@0qSYl8,R!h9GIJTk8K +,60,jRDcf2VmhGmGK54ZrNIYM2RdGiLV!Vc+J@RNQXQ3eC9e2XEdIk8DAL0hD2"5 +ZB34"TjBZC01FHl0dE#fm"0rqGZla'"ih`@c43AJdN!$VV13$BAd(Nj!!EX!aRi% +h,)*9GhkQG+IJLe$fVkKPdUl[4i%2a%q@6(!8-PE`lm2B%CAaj'8H2BS#em65NVr +4Kce#`iA)cJ++!2C6Ypc*N!!!DqK3)e!B2HAET1Sa`&i'DQ8pVRL!MGbD)86m*1+ +A2AKAm1i42Le6$0Kl8i8+BLX2AbLaihc&8(EZ6G4l9[6V0ikpYV`34+L())fD,K` +QTaa-`iFfD$NRE)93ZXMT9+%h!FeGI`dTNXbhF&UCJ,#k%iN[Ce-PBDj)AD@KAD1 +(P*B08i#!`FZSFhG!k-8U$`&@F[eXcq!rcJeH#2l[Y1dc,H%U,M&PbdSR-E*'P$k +l6qlb0pEe+3LM5Sck-%AiZAGC)!1Ma+d4#YKV@+JDIh9!Pf'a@CrdVr-5E"1#iX( +NT&[65!@L%3X)A)'A3l$2Ej)$LYZK5F*G0jb%LImF@#[Me1dLXY(a`4$H9&j31+H +pe&Kk0Ja$RAQh&!l"!8qU&aINNX0-+NHDhm&JZK%cHZ',CB5@BNDHT1,d(K("HBS +c$Tkr)FEC5`VNSdBj[fa1l-,!-Am`QYqGjA+1+keL'MGipVDGQS&G%A!5`$YlrET +CXi`FQSTTC%VJAZYG0LbC,biI'lr12#TbVd-[$"HTa$e@crk0"%hDMj!!YSM!3[Q +)U-BkLf6d88r"6b6dQ!(C&-$mMDD0mRqbQ-HM9+'I9%FFQB!+`5C5AZX)pLl&@(9 +&BL1A*QcdRaJ*QQh"Ujk@k0Kb9j!!'KD3!,"mcFA*bYj"dEFB"k-h20LE5f'&E$Y +DIfAfb&*LlF305V`iBmJ#JHU0lG984+%,kGH$kq%c2(9lXI%LE1`D!G`Rc(Ub+rH +jH[KYVTL4Aqq$U`JIEL2cXT8#-98TM0[4JfcABKZ@$"$V%[#EqU84$efe-+k0rLc +cP(H6),eGSkG-P+&dpIZq4Cq1LAm&YKUSLjN,Mlea[r"r45,K*+if4fAlZ'KhS,Q +"-hlAI1+VbCG+K[8-@a0qD5M5YG`3cL85`A@5[B"+T@'4HPM(lk3q64SVDDrM83d +G(Qb++#h!ZGUHR%R$53&Y(@TT4I1jr$KMd"$$q(Q3!0Z+l4'8*5*6+(AIreF[qM' +3!!9'&I8-$RL)k*&10b"&4AM-p`B6UHE5!b4QJZS%`AZ!S-6p3rNPD#-"9Qj43F& +AE"MV)'h`H!B3qi`644jl,6Fi5A)RBpAKQVVkRk[96SGdqb[f2UX1CKZ2Cri2[eS +%`jJGMJ0,RID+[MdShTR2#LEQCM0V*[$M"`kRr#S5K1,LG+93Xdr2MIRa$DCD'Kk +dBi#lV0KUHA!P(Q[&*#6AjGMSmpNFTN$b(F56Z"bR`+fF`rRGCCK(4"F&0aQpLHi +9X8U[r#AmID6C5bjA1@4pBEk@5Q#$F#CYN!#XY"!FQ`MNYHhU2eUUVKr[X-#aFQI +(8BS'5[hV$"PQ69QZ+$`9-+L1[T2X%[ahU(`4U8$9Z5)X*SGGJqBJ'[R+a0E"p&3 +fqBf0JiHXplJCMVI*R`AVp&V83hYeV1J*[AD!030q%9VbFU1B-%$UGBV0rUX4K"B +4SFShN!#5B+HEe-LdJ1I0P-bkG4P6AMH6i-k#aTTAIh(`&[jN'c+$@freN3U-Aji +ef'r)99PL$M1ZcpAPh98iHe*H2H55FPL06EX94kmb!rb%1pEVh#PYL+qR@`qj1BY +'F4A9X1)ZSF@Hijahc(*3aCDFT3"3V*S35&hk*(%G*qNSER%#a6L-l,!0,fEr!Kj +'6)(mF$,[Z*&#fR*S+T-@9AQF6%mF6+FG1k"[5"M&pr5SjP)%-q,MNrGFerN@,&L ++c'kCAIiP!PBBC"eQ-4`J`(J0YI[fN5Gqm'!*%BAJh*TIpJUZ@He9bhRBHG$'-IY +iI)lFY-aF`aEaYkh1RY1B`!Dj,G-Zq0f-ZQ(E534kN3"ViYJEfAq-9A-LVT!!+Yj +AZ[AimZ!6-JB!#0Zb&49C95mUb,*TAd,p2ICFmN9AHAD9K*-r*N'6"JqC"HNmQZP +FNr&Z+#i4Bl6[kSRXQXMhHB5%M0ce8ejVB[FS4SNkSmGN5JqMhIE+%BN23AlqFh0 ++#erXKJ1`jD5M-8EKM@C(hQpVH9-3d[&!eJ`rBJe5qr+kSmAU!,jQ8*LN&`P4A,L +8m*IFCNq(fNIdApe`U6Fh5R(*hLZS![a"q6iKiP)HFCih'Br`VP,@*r`@JFKd22H +ei#QE'1M%j%#`a)X8f3UVl*3+ee+UPU#qUI99k6Hr[a5[4iR`+[L@K)G2Pd9FK-* +qT4BFNbCSQ,T0Q6aS2ZG8VXU8jE'mq5CTUT69hVkj4#)N-XQYdZ'HpajP,pZahjm +k8Hl0'ekhSP,%arrKJmR6%LU%+PcfXHk'MfDI*Ea4S*!!89$[BS+I,8b(#ZE+"83 +JUEUhpRV`*)5`[b!pYFZ[a"-h-"*KNcU,'&p4RP6iHm3(Ua8$M&1kqdXAJr"XmmP +LfV'IRCD,d,@1PPK5T532#M-6[hXKfj-2!F43NUJ,,Np,,maMEL4(*h6NAD-Q3I! +mhGrSFpYpfq6J3m+efE@`[LR1)ZK$"pUmVDMYRckMQ!`SPTlC+F`p!jF'[UbV2jL +`55dPJfP6m'BVj"XlE8hqb)PB3!5`BQqAp-8*E2prMFDD4&Zj#p#AR)CdDJJQQre +DI3DBS(E9[k-K2AVX@DH5pqI6%mQ6Y&&IkQB4kaVpk8rIaAh@8KS3A`19@ibb$P2 +I`3$NN!"de$!eX0+6Q1k2#%(PPRa'Ij*CZ#VDRYPjK5&DLZRCTp-`bK`ke0cMRaV +&T39E$[d$cE"($TYJiQhA-DYBj$RZ((Q1d%AIX1N')FGB&0N,!*LPRTXSb5(Si6R ++"QSi@CVA9dd(#%!X'%j'9V6(f`Qip%dN-cK-h0Ga"Ldp2Ej`H+!jYhS9Z)d0M"! +Be2#L9%&aFG&Ha66SMeB*4(FCGN$r1R"phah@V+m%3%-IUBFiG6ZC)8`mralD)5' +k%*Amk"RCB'RL88JL'N6N!2d`ElBMTUCPjb$ZKi8M"B54LNZU[4qi3(`m6Apq$3b +6fq5'@[+@%a&XMq)0L4[aB!D`QUH0C0b@jJ'Vr"P9NkR#0H@CDEUj)MFG2EeUF)E +K!*'QQ4l(I,D9#ER6!@Jpp1)a0Ke8!ech,(&)lfTK$`r8k[j28pA'VJ'Blc-IB3P +!SaT*8$HNpQiiCm*f[q[BPTb6fN5P2*@6j1+J("!e([SGIH,Q-fdi)4)3c95diIV +981!324bEaLXJcYdl4RDeh`dql*EZm"%*Db)8[+[$)RkI'a8U(rSrEpFqB-Y)fZa +ee*TU,NN%%3i2!kpa(f)AQP$3`Eh!J!9jc%!-N!"([J4Kc9qjRCh%eem&LD9)j2` +#l!br$f6)Z2ap3,4lRIiCiVJU8LDF&!4!RT'pkrhj2#,`N!!i[,G&J(!TB,#qLRC +#eRafJfH+&QX&!6YZ5))LCPLI,LG,IG9bYFJP[e`P1Mm3q4pT&jfL"FbGRLib3ZY +-&dJJS-fHcZ@j!Bc1Q2SFe,EkB4G8RcPeM4Fq%mB%M#qN(c$p1B1UZbaqMMl*+0K +1kA4YVh`4&DYK+a*BMfUkVl+#h3!C!*GINITm*c*eVp33fYD22CQ9`GBiTHQ!QlH +fif-0$cK96lUc)ZIqfpHJNpU4)e+#SHj'i[d!a&1BCc'CP#M"INQc3[1hHfr9L4@ +46P98+4[9m1D15FFZRNjDa553!,(aG6[Q(3BZY2i!)1&VJK2k`,`RHQ03I+AJedM +eLfq&a'laqp`TkB#mJM851FUMj'2XkKl&0&dGb+"bA-E'bm[*iCeCYBLA"q0$$Tb +'9&p8pEpJP2$MlLhi)DRmmj51j[XC-!RpfU#qkB,+bT@0[HNMU*,Kj,2Li813!2T +%e4[8#KU1a+Hc@(6Ra2IM9kf0[l5GjphLdK&b-(,pc'U$S,CA`GV'J+#bP-c9%Y9 +8m3Edp9V0PSJhY3q",)PFA15iR!rhT(dhN!"aq!ehqIiKAKbHf`KmaVN%kC!!@VJ +j12Q)X3[rkEQ[jP@jPU*MHIH-!GblddHCIKmhqJQ$"mimeGRZK`Q!F-Z*d,G5qje +MG,1jA0Ab,Mb622%Te@T`&!,*fI3Zaq)l9!QJ2&2IEfFiarlf#N9C&9BBX#94c@F +ZkE8eLC+@d"k"%6hp0FKfN44cC,lj`k@Eb3Mpml&mJiSQApVYHEA8EmQQL2AImbk +C%8pe)(3ah#[U2aZE@`mEf)mZ31Ub5,P8&*c@F8prq#ra2!Mlj%SL'4Gh8lZI@2& +rfKAA$"MrFmB%lA#&q3U&LB4U'KYEd4#arMhU6f*p4pX*eh4+%l(i6rNe(US`C(0 +UUM[d&2l68`jLr(0Dmp+SL%pm!JAbZr-Y0QHpB5PrbF'6+"[hpUHi*mbmq(YpIb( +iB(r"*pf86AUZ-!l)V$HAl',dpB[biK4XhPiIjeL!5F39C-K"Vlh%H0YNG5@r1(+ +G,N)iGfhN'@3U8KNN-i+$E%A$DNf,$-9iY$!#JXXB9%0f(UlRSJk)YaU3!)9Q-9l +m!fYq(q6f3KeHlAT9l1Nb1)&rMiMRj&8bBT(jp)#G2A$ab1Fp`PB)aT`D1("f1S6 +26c!6r6aRcr9f5kP@43340EDIJ(,)EBRUZ$bFB)Bp$DHrl'!hQ%Y-8mmSfIAML3m +IEFL@-m8b)9pah0(ieY1bIBEqVcVNKYQ0+$Ap*8BdPJD+BeNI$!pU@Bj8P"`Pf2E +QT5K*@Kb#Gr58CV@JmK2YA9$2Ifi-JQ-SHpUa,L*j)3l5J#iE"%33)#@jmFZeDfG +5ZKdH'[Y+PIb18DRh@)K!($648I6KkdBF%kccR[drlc513fX%5V0'I8fedac*a'R +kDTmjjJcTbpfMm4AHT6D4PqYYL`F!rH[k0)9b$baE3G1b3MrY6$$l@bQIQdqV9#! +dFcf!)EF%VL12F'fJJYZfXZYQhZbBA&P6mj+R'GCL'dSf1-%0FRm*Ml+ImF'HHPN +PrEP3K,+MJ0Pef+f*pS-$AH8-S2P#Sqda)Df$@rQh%Kl3EJ6QQdG"*bF9ZjU[Xhi +-ZMVIe`aD+P`me228S,XK((,kN!!0H9LGMKXBG,)(X2cNCad'D3#[jiG*hb$eNm, +1CEkGjB@XqbSS+kaTKJ@qNm&*lJUr5QYDh$PHJ0I''f*JM)Kmlq#kPEZ*Hi'+d`2 +MqF"4+p9Rq8l6+*RE4!@AVi@3!1`8rVjR3)SG,l(6$a82Rl,e8hl8&B(XpI#M)cM +-8lFZi+MCVAc[*,"BIQ2,Qhk#B&qZA%)9FKqUXYk@Qp!TNJX!,lGd$mNUlD0UfpP +Adh"381Y!63!r$fP0Mq)&B"rP-&@rf*@bDH9G8IFbp!he3()LEmFj[`'DrLm-Zlm ++lai03DpBX9b8RaUCSQerK"cdG@2'[SC5"0&AL)$DaAJ9r9hhQSX)1A612C($fMR +CiP(GZC'LTDmZf%q%lJeZ$kS5VjNKdY6dX10dUEX5-0q&`@G-DUJ8f&aNYXp[FHc +1lJ(HBlPX!F@Vrm(X%0T5fm[0&(c*5ldY1[(X'k24LEl#LaFf%EA((pN(hIreK#2 +35Rj2#FV*[$b(1XLqNJ8+1QXR'@lrBkXLD&))e*ci0B6M'Q(QUcd3)1S6Ic&T"5G +PPl8UpC,qJ@HSBURlieK45*h&QccXmf2fl*`[h*cfpi`M%5Cc%Mfa+N8KaMYjjI! +q-Z9-$V!SaLTM[ZIf3IFFIV0AJqH+YNYV+aQ'j&GlmY5jS"""@9(qkjiHE%J0LC! +!MSZ09P$I(41NerEN(RQpNC4JGLr[M$,EikU`T-UMk**Mm!4!SBPA6qilBe-"Mq- +AqG2I+V908X6!FMT)T9dd0UPG!9#!*YXLDiC3'%6%T2ZVL[!eY)c'5*eQ"kM"9iq +!KXP-BmMdp*EFRcD&r5SZT[IM%1JqJbP!2S1H4QJA``#p`92C!f$BEkqai'23eTS +%"D6c%X6b@cG5)e@hf-*9PbR1qhMeLVS)9RFRGR!L4NF%`dd+bS[5ir,FMEppf(! +ap!5qAYDjMHmhHRdLi'@%c+5-`,%YEP1rEBrmh"YQqAa)S3Ij#c1UJj+jKMASeMa +8UcBYIj%ADYrmimbFjNdf-!c`D'3H4K!8H1Y5r@B(C"'l#k0XEMaimf-NUG[T$Vh +$h[`pDl[3e!Vl'#C@K@h!#&[XB[4f03E-lBfJQf('f$`'$4q"ZYMSj(Q@F3Z3!-# +&!aq2hpXI@T(@ENr!9Q2-CYDdU,1qS&`CeGdlPX,(DS!cJr#GPP3H4R8#[cQSIcm +q,QqPHb`SlK8%c'"L8LJ3dH&e'bHRNFaZC*6j(V1SSD5'bjKSp81G))Pd,0ViS-j +rlM2*'PmMKKj!`Irl"M1fBmS9mJ[SRDGCeVQH9pZfH@q3!&,+pq2Cae6KPVYCC8V +jCJRL`j()%Ndp!H*&i)Th$12"j1HSG,TkeA%cL11j9!Br3NV-%0Na'`(AIdh'@,@ +M#0lk"RFYiUh,jkc`Uef(M)q#T!%bpGFKeb()L[fY)-F#`SL)X$Vlei`BUE5,VIq +dJ[GZNmddYYkKjH"T(H%*KJ+Bd'S3ARUHV&CVeBrrkcNUlEKV*XVNLSqC5M*b!T' +0#6mfji82iRk*JI#C$e0E%(9X,H98ePL2EVHeiilFCp,hCFIAcqQD,d'&R2BcQ8( +k4""3jhN`UN!Aim8DXa,#hUJK9XiAPU0cXPG-AB5#mbcr6lZ82CB51ir$41b4eE, +A1@c"1Tp(r@rKS@-I,4F1a`5,`E'3!('ir5`8AFb8#"qCSc@E")X[ejVr$Xp8bR( +J!Q3Fa#H+0+$T5#Dq@*Je4DY@M0,j,Sd`XX5LT+Jl(ccmG-aXlc,U[1dSMNLDT,' +&i4QES@EKU,c*ciYcq4(i&eY`2e8[(p,US+fUSRd(#D`UNYXHJB!XHLIJ5q@'NrN +4rQUI9I6$h3T"8jrDj-+kdG2M%*HR06IbE,GfC2GB*h0`UmMm$2MTLji!Lrj+INZ +&),G`!R!D,bb*XP`1aj9l`99a*&qR36bLT+c&LYBF-FX"@D"UPJ$)0URXSc#mNp1 +CGHTKL'AXq$'0Y$PaJQ[a8,9F9X6Bb4m8UTVI*lVK2lh0pdhISkc2i)qkS1U@**[ +E(2bfV40k`cblEG)4EY6"[r"c[JcK3@4T%I1'bNP2`rCfe"8XimbQ%pTe+kB-cH5 +mK'5%)H01SA(5Uiq$,VjZHL#Clr9181&b(AeejpS[X-+QVE5k&c8SGPrqFAA8a*0 +a8-1I5PPMT*Fb!K(d1#QCFq)Ba%9'e@T*Q8r"`0b!$kJcXAR@+&%%C6H0A$Q&GBA +'0QjD,YAB2`'Uhcec-1)!5NqedJ-VLq4K+i8mi"GbEpr"VYUJFS0FY`k1BlaYpiG +UNmUaDPcY1bdNMrPCP"3DYT!!AUAATp[Tbb,4C`fC5BE`5q'U'irZmP-ej&Q'!$Z +6eH'b!X18[LJec'G8+E#J)CP)rj[,U"8i@Ap3ic`&kTCdRq84F&T!9,#d%qPc`F5 +XdZ3I'8TQ#F'bk'94RU9ZckA"U%ALP)HC@6FHreBRLAi6UM4UT'M+4q0Zpm$+k`b +0MJ9KV[U5iS1'+NJTUcJHMDaFMbVAhIfeU@&G6@!dj*)YZr2Fe-2`8fPQ`D9Rr,- +83Gi),&08RI#P9#2ef54kSP6JCFd*rCae(5Ke-Z(mi1#!VpULVpFN@-!,0ShSq1@ +&,M+MiqkHXL1C9QjUFIP%aYVQU2(#U#4ZRTaE5P'-mX(dh[8G1@Rc5jD2Idh$3j& +k"X&-lM1!rR$%-qIr(IaG$pRMZ%K!hU2AqN@dZ[EV*-3[4bP2ch0F)!5Qj2$DDi[ +rT*dr@8I1CPRQea-"(4VZCpp86brNpLl2NETkcJBESXPjXXFI%(*2Q'jJSUUAkD( +V(k9#[LR3U$iYZ`+@*lU6h9epSA2QE#!AKb!LNrleHq+DC@k1iI+Y#(-4Xb[L@aR +kPThIX!9`eU[*Af6LRd%aC8+G)*ap[$FU0p-G(M"f858V2)afR-)$lEM84f[%!QI +U4eNE361!ch555m1b(dD0N`!ZBRl,6UmJ2YVaKIJAT8,q'-V[+b2QSH58kCl0A#, +EZAiqG590(llL,)`ZFXX+AYZ+#kjEQe$'AC@KE([-$G"(l[i3)2XE+FRXkTe2rXN +N-0Mq)3AIHKTPDed,A5jH0k+LaRRiZ%@Y!MS!CJ3TElZ3!+%D#X4q$CFQNX-I6mZ +(rqqYZ2eC!m8P@pmBdNG86Z-`"I)5!`-YNPPr"C6D%AmB`),k)ErhGhN#!@S6Kh6 +2H*KPGmZVNI'c0!aKS+!$(3+K+h1P+(M"3+h@UqLbI&Lec3`(qB!$YKJYL`4V51l +kB[eYJF!fa'A46TLqRjJIcmcd!jTN[&+[GD6U+D@FhCp9DpVj5($JaQ)*H3T0P`) +FeAQ'$Mqk`KCfAlVfe$pT9"2(rV13!+1-1jCIN!"'Ch"hYUU29-Edf%(UeTp)QZ" +ZPSQ28*mVZ2[,cDSpXMd'$PmbJ(H-j1Kqh*eKjlR"c450H$3rM%+X-0i9%BBl$8' +cBE%L+PV#E'AXB4&eS++1Tj-4EJEF4$rA5Vm@A5D,92[ULe2T+6B4E*8c8Mb!kf! +)V+9cqeHRaFUT!6+fYGY8MGS,NH!5KIG%pVCP'm`SAZ`,$#Zfj#650m!LNZcUqM0 ++#CJLI4-&*Gr+!(cl"+@-28j2fZ0T)RKHj&FJ1+YMNC[h`jD9J32`!a#ATDQUc`i +M3B'1LPX@H`FYSI-'i4q%arj!S+2"BXGeV&1UHP3'6I-Phmh@9kpI0ESXl,PE$hX +)iAeb(CBCGRA"SCiKSP@0)&eGQU2l2iJcka1AEMCV)9*"X$E2iC&5MHT8)5'EY,! +caJ$dN4RBL!IZHVmi`9MV0%-*m-eTh3N6&TXC-kqT`qGQpLJTQ+q91FbRFYkjqPK +6#jjYjGiHSG#G`$@8D[UqQ[Spp6JV6l5FTL1bAlm@lILq5+&IJ9G-!51,1l`Gb+G +,6&jLem!B0$+e4ZBcV-k24IYpLHQcAafk8K'aV*XFq6R'*(``()rIHRh[NTZ$rPc +iB0(D'e6'jbECIfbZkJ(QUNj3TGT'[#h2ajST4eQI1!dm4l)iS#lB91pfLch!CC, +bUB`TJA$N)-i6$224eG)@jIYS#22VU(F`%LCi"TAM0F-RHElQH#pB+C+E'D"T6Xl +mGK+Y9VQm-MMMhJ$eJ!JePf)VLp'4(k5a!H[(015-h`TLAC!!qNf2ah3mF93%qCm +!YRG#kXfrQhCUA&f&XA"k8Gm39XKie+XV)aJf3#VSCqLAKZ)6J&9-8ch[)!%Q@k* +eF)15X6"[CJ#YQA*fh%MkXj2(lGT[8(`RP-lG6EicfF`2hcc2%*+Hfa%3jd"'JX( +E[B@3!(V1BiRRKJ&D8hkGDKC,p%6bi$q&$,Q`@i+hBib6Y*(cG#!2V5"N06ZH'"E +!+ef[L-KZV6l(kqH!0#lL0,6,"Nl1kdd`,`V!#N$)@mDfj,4*%HSIQ3KVch$daK" +q&`9bqCaS4K5AGJ@iaLQcqNdIESam-B-8$9Zj%ciG4b1q*llb[M`e3%X)QB)24S8 +V1ZceULkb(3Yl1q'R!Pc20iCr4K(mR$`Ka!602NDSRGKLDh+QHqdZC%4lSKmTr38 +Uf+jLZ6a8M&Q9'Q29Qq+-1d*Q4hM0j&X&RSBZd`NbD)4Y-Kerf#&&%9Ri#Hf!iVl +ZcGa4j4KC3BL,TA`h@0UG#kZl39Spr*5F+Z+GMD8e*MU[jZT%KZ08&MTIrhTp&(' +UA2H'59aSr,$&D8%-AH)#(B8'1Je9)Gf)5AH99MpCf5EAK,Pm+IZhaj5Cjh(b1Jk +peI+V(+R@+ZC*IBL",[A4d%#HF1K-ZRr%-8JM3INlVdMIhD,m$iq!iZb$G0NEJqX +@!LBibiB3q1CaS,22Vr#&,#45[!flm9!CF(a*MI"a9IrhR+RpBS%TlbpL,`1qUIB +YbPKFIRb"9q001b69Z'5[Hlaq3Ec5!c!MA40AMSa23EcH!Cca@rr3M6BJP"TX,AC +`$VmNZFPrhq0H!6GL@q$9EYPp1ND`#lK+3-##0aMPMNQrik+Kk*!!GDhSbELhC2i +lmdAU-6$-*&KBkP4X(1INKVcI,2Kpm,cH1)q4Pq*!-jlP`-q1Hc&f)IV)jjq6pS' +[lBIf[Y'#GCHRiU`qG%!hQlH'J-5I3Zd6-`6CS2T+8a!-Ta3X6IGLHTC2a@1Tcf) +jcmA8+,jdc1c9[#2B4!ME'l1Q68%6-mIl')I"9mI92Qhm!H4qX2E4I#-r$MLX5p` +K"kKS[DLjFY@dR[iiDd53!1"48P%(l6-BSXP%Zr(GrZK1Jlp+$aHP#Cdp"Cc&*Rp +TXh*MQi9P[CPG0&1dT&Jm0j!!Qm1"6"LTD+V,(IaYE'VC%+2h0Z&AqB!8j*6"&'K +VLSd`HAe'rSPh%L[QU%Ppei,'HEb-+,VmD3#49"FK3lPCQC2&q,NhKBkp9NUQh@, +Aml+@2kI'*DdhiMe8a!YAF&8ZP8kS#KR"KdU[e`+k5BjT%`Kip9eJ*qbCp@c%%rM +ERh$N(QSNa'!LZUZA)bkJ)N`QmK6a%`8eq`NqlbkJY3p,(hc(Ur(V[VE0H!IV%jm +923N(`X+)C3dlRTjk[Di,G"FXN[qUHdR8d`C%LMN5m%)&XQ(F'TpbFDU8U2BI%m1 +8,&F3p*jrU0#q5RPYRk%j-p)+mIbFF0f,j%QL%SC6qdIrmG@c5jEc+A%M9[[FlX! +X&GdJMJ+1U'R5F&[KG1&l&+Z,iDKhYEJVLel!ejDTaadaV3)d4,3SqaT5fBT)iCD +rR1Nf&GI#m@T[GdSJ&Cm3jc#Y`jTlHAeZXPG&b@*S,m2kBNqpV2K5Jh'R$YFh"Y2 +9K,-PDG#9B-DH+j!!T*4fac0*b!c&3adJhAeY9L2l+R`#i'@')aL1&P3b%5f0CTb +*dQ8f#Y+!KFrAA#%e9E%eBQDl5dkNHMi["'"C'A`UmV5h(A#LK`9ppZ6l6U!C9(i +I3HXP,8"R&1@Nr(cfbBqYaGKQj$&'"KlTq,`JcV4NaS%LXTH0hh%(T[AkceBa0XX +ICp9NB,3$AdQj'%#k21b%'D-'@"$MlfMlF9d+kSq)$VbdfSA(P"[eQ-M4!1Jkc81 +f-kBc2!A@r1FY,EYL,(CZef-j'9*JjJQ[#F`Xfq6X-q6VQ84dBGYj+*PVh@N&[b" +m!Yb[AS4['TmDGQRGLa)$EMmb+d"Z`,A#[-+($f)c&p30D+294lIfZaP9,GQ6bP9 +VDZDVEq[RFa9$AaP-T#CMKNN*V'M$k3AYkK)#[dCI@8J""ADkjh(mKNbC)T,h-l# +jXMUi+D&2$(@RZ-pdpVdbp'G'jF)BGVe%)QlI&mN0V"4-4mC!F2QrTU5)!Lpd)1) +!(S@NNDk3!,bJpE3hD1H'Je`Q$+4reCBhBZ)ihliFeU"m6f48MpiKm(2p5CJm*SY +LC%Xe`[hQa!ZK@Da+#pj1Tfeqe#bce8V'[h9AfV4!GKUN'BE$AkAeq@9r)S2)!Bf +M,Ia,H8S55pjDmh6+ddfh)&iYDm-1GMYH#&j"%f,e53V4%kAhq2+MK`k"I@'EJ`p +"K*pea8*TlE48GM`T'5e'&"D%TY(N853EYpjq`89*Q1+$2A4YX`X2l*GMZK$eSA4 +H`IUQZM9T8GAmeGD&$U-e,9M0B3!@ECQjj-rB,55m'h-d"S!da`*lqA8Vj0["rp8 +TC64E0)bK[cC8h&@bDY0F`-NeaYK#[80Y298&q`a(K`PCaSHC,jX"1L)(lMAaU(d +FNrHMe8LNE%Zb9*4aD'XEIL#THjj(Y3(jH5YJKf8!Gj&VA41,9a)pdb*``mF`SR) +j2MYFe39[4H(p9R&3me8&r55B+FHkk@MB3BP#bfPYBXbV3b9hT6)4f%"6QrQ%3(" +E'NH,@lA*jR4EH$mAm*hJq'5)ZC9*ciH(ReH[#a`[ai0rf,UpjNQN@ZAXUlZiZ`5 +A`ehdUk8BU[IEi'kFaLN)f-cmcb+UC1T0er#!hike[m&95MF,A2$Q@ra&EQ+9HKD +j'Uf66bCZ4b!NhHb,3q"edT@icIkl$c8-88C-HEZlY3j'cFmR`hHIS)Z2d`hDaK! +I9!j$IU%9YQQPZ,fK"TBUQ$jQBe-iL6HSHjEZIHr'XE*VQidM)[[h''RXj"&a@,J +bG-i&0HT+kFcfd$2*(0fG&@#qYGKZcp)LR+TUCc5%cVjRrIFjKLF*)8Aem4(aQhE +hMpE*EQf99EN)@jY,b-iiZIK34KM)qRcNkAk3!2Y9+&%TqKcC63G,1BC0kl@k9SC +#f2p6[rV*1rU*0F11j1&NFBN59XXeT,aX*(`G8AJ$H`RF%Y9-5RdJKLk9`&Ab2Mq +RN!#2#E4bGB2ic%I8p6N6@Vk5GYR#ME!PZa`0RG(II!lc'Q!82*&1K@r3!-5)fj0 +6"[e4b1Le`aIl+pYkb2063#mHrM2!d$1#A@`6!X&r+kb*F5TUm)ab,HS3ST3iGlR +J%Ei!-SpG6fB![L%Y$Z[Z@biqYA3'hS`FjqG#iiRCN!#)EGSS[%p3Z,2eieMe4lZ +4$a0PF8XN0"qCFkiUT'%PDHerL4Da5QX2,TMIc@H&bq,rFLK3)#QcSV6"2LJDK@+ +'GaFUP#QmE@feA6XYKrNHA[LeK8TADeFl!U"LDQXr&d%qJ3GLU*2PVBSe8ZAL[!K +Pe"BDA'mQEj-EiThV`1SUcC`-laqV%V5aj3IV#$frY23"MclAMdZRPi2`6EpVT+b +3!*!!J5k1f@"H#hKAhhdUaX1"LU!L2!V-Jq@+4%`,0R"B[')Q+Z1%XBY(KCI!jdG +iTAY)qR"pkZITN4JUm$pHaIRZ"*U&$Fa"ISRqJkJ"4C[f(1`#['&F+4Nq"'h"idh +kPF(5BbUjk8T4CV[I@G*SL#Alm61)X3GS!LVHf-YT44h96dreUJTPfp(4JVk+jTp +,jIeXrK2A@PTPXC!!q"KM6MjZS9r"XLUh&(aYHJ'jEP#LGhFl[LZM-`TEU%cUB9c +2KCK(QPP*r`f5ArY#QSSQI@IcL3VK5AfAVm!2e`SAe%YrQ#4Yp&#$d60$VJT'3H3 +8+8'"Ub#DR+Z1a3jL&6m#VeijFE1afD1SR&4kJZHk-Q+ikI#iH&Fk#F6MUQ`L4rV +p8p'f0i0$&!46"S&lHUF6Aj8&FJ22m%akdieVN!!r4BDF##fNACEK8eIfNFS@FRb +!Z0i4T9!894SjmlGabH`kL8VeNAY!@3S6R4J5JRXJ`YM9K`&F0pS#&!'UATdf4+X +5HN%QP!'8FR1U0FM*@lUGM6#B`X$+a06JF%-hZ`PX@Y1b[Fl&X5cUhr9Y0ZihfAq +@&HDM#mMKPPp%YY-B3M&A11c5JD+"*`8F6F4k*QU0!5@59A&Gplm3EjCBVS`j%p6 +ji6mj0rAS(Z16Zhk6'I%5RqVHKB8`&Da`IM#%"`SA"-1`blUD*6#T%1JFTkUIh[C +ErrqrKjej,@dI8Y'liFD3!'U3!'2$5HcfR0@5ASiJb(m`##J$N!#3!)S#"'!Eld5 +Yp5a$J`KHlrB4Xj(Q4DfNJZi-2Y#'ce4"f8S@4kj6P[K,L('"XfHd-U3-qYV3KCD +pb3Z3!'Ue35p8-cbf`2kN3(d2-0!E-*DkAT05YPTmP@UL52!rK*ZAiYIL-Lc$9[$ +)4#p$$Y##%&,`GU"8J3(k1*SZ+TV$+H`QcF49m)4AJCa%3EQ5&1pdh%RiC%%+-D2 +99$kc,2hb'JYpN!!JcmSfjM5q9bFG811dTZe-S[#hqHRD(VS'r$MNqCRe)aLZEE* +MirM)366CD2'(5EPm-5b[c6&pUBh`XC0JEbM$l)kd&m`XX1+mrC@mRK6'YE%kNiU ++qRAF'Rb-!DX'Sh@@9MD9q(9B(CJh[[3F3R"8b,@SIr&c8XR`lSAVMb+DqCNV,cE +&)RY3RAD'ApXSZ5G6hJ-NMj)&#Kbmh#Ar*h9M1rfS,[mEBbfAEC+EIIH&XQjk-XZ +EI9p*"NTkh8CC(hPZBb#9,p,@4Ni@eEEZqN!Ncf6k23j[*STT$bEjQJ`20D0T"TT +k$GU-8`lSZZ-JHepEDdqkr1)Ne9!erE8(@al4VaXcm&4UbKXVq9r`dZmZKip)8f0 +ZJ$eVdV-fEm9DCL(h,ffKia!eMVGBG3Qjqb,d+%C2D5jflqj@-0`HQF("l1)i%Pa +!ZR"T@DXUB"fS`bA02SG1jAqil4`XB1,1%59cB`AMMRK9kF$%+CD3!'"GSV4m9Gb +Q)5r"2(2PkZ4`*Bd,Zlp6'SUE4klDpJ!dG5&cj2`#1HFb4bKq4r,EJ,q4Ld3+[-E +q@,KQS#PGB)I2+6i(25JJk-dqI[40PjGMb,e9BU8PjY-S!RXNF(T@a*8UlI!0Ul) +Ma!M#!FeR(-rr9@Ge8rT8cR&X6"30CMN'biqZJL8R6ek662a'19-6h5Vl6f5RE0i +6IPc4C`mkcI#q6VjTbRKq"3I)8+-EXPL8D4V"AjIhECrTd#Hr*['PH[mIHcAk'@X +2-0*cT&afh,f0&MjRJN$6q&YFB6LLDSp,09e@h('dKc(meGDcZJr4GFcYR-r`,i8 +[LBl(%bQmFQL&HGGND1bGV)fS6!cQAS(Zah(A858MNPK!Ce5'E%CDma$FRj@CrMq +lMRN%d`Z%!IhVr1el(8N9rfe46jj-$DTZr(TCKd'mM#%DGBBAJ8(e3VedLmIcTjk +Z8QrhX0#GK$4"XDQh0j*E%bT+rHS%$@h5K'*QP,@Um#iVXTrpQmQ5b41VMDi$0%H +4&*XIpAJfI'$%(10+mDb064"5PG43jZ34ZJE3QT!!X@edA0+A1+,SHLc#!N,"pJ- +dJl%F[68l3fNPV+[Ah[0)iIX8BeiDf6hb1PeaeSI%2%B#Si,R6ACS1h%KPiCb$`K +KTp&bXbrb(S5S(&f"FMr"A8kk!c'BdIV$Bbfqr#8)SBcZQEIJ2GY[)$bBYG[cNrl +!D!Vl8lK3QH)*XqXJF(I'RSc,!rkCB$S4qKKd5X4CdFP2Nq%GF4)K,)N*0%&p+DI +A%qd@5Ge5j@r!'Xq`42F-8"I81+@#a6(QC5LCVa`TN@hiRd#j"j5Y',1D3E6DAl* +ZQ3G*80FRH!0@29L3!&iQb3PLcCGVE-e0T!Li##m[%(J[qZP[QJ!3aD)HQX19mF" +IT!B0[eQeZV5aP#ZCKE823S'R8PXqKf2T5%B0V0V$5+QCLHZ[`eU"hrX'LqN%i5d +Iqp)ISCkM#ail$CbB*5i"kAhKf8F2FpSqB,JS61('1mRp0dCBKqMAr,YlDqK1pH' +PQ@+#b2F9hNM1d9"!eUM"[jH*SfJ5qEET2fUJ(NU)$bkh!NZ,lAb)!)d$1iZXYd2 +#VI5HHhJ![2UiYB+bJ[Qhl,$#emcI@e3F!lFT0U)ED#*QliSf%$"&AD$e![cj[qM +"eqIQi2Z8rMcaQI&*"Fal#(0ZRq*Z8BhD&d!cHlh&*K)Dr8Ld$RkV(Y)IU%c6A6J +S12UB51T!![5ml*Jjpa@peRUVSP2IH!#PN!3"!!"$!"#i)pScZ#2D-`!!Ep`!!0` +F!!!"-!!6bAi!#akj!!!LZ3#3"!m!9'0X6'PLFQ&bD@9c,Xq!,RKYE!!"m0j849K +83eG*43%!rj!%!*!+J!#3#3'D!*!$E!#3"!m!3X(9$qA8,*!!8NT&S"6e"mU%bYA +[Y1QHX&BMjQ2ahc,$m!MIA$$R'`VL@F"A4pDH)kaKa""@D8%C'RA`LF4NCHCM(6( +Cq'LiNaR8%2m4p5Z`YAR`Bm)BX@"bF6THhaG330G5!cVEkD69VGF%!%,"e+8,`$l +pV*PjG4R`R3eDNISlN!"qd5Cb#i8Y,r$"DB[+lC9*bYc66$Hfr%NFkmV#h6!AlFl +I!kibBp(9a4[JB@kG4rjYTq6[59V*FIMU"U(hK'fhLKAZEF1IEe2q(,JM$YPAKbY +#Q`*%JlAK&88iXK3%UiN*lP&92Z#bBBX2HCBblr(MfqPG1-q9%CN%eSJjfpmq6CL +cJrLiE(e"lcq!6`fH8*J#hXRbA+jKUI)aZI#TUI6*$pe*[bbq,)FYLHHYJfB&+Z[ +'bm'kJ6qFPM-+3Y"dI"6DAe12-,5F[`Sfr6Q%80,PQc`!'2(P6ERC-lc$BH$@qS% +X4EjGh*Q[bEc`)*dR)'H%qDdkHkh%1'C$KDk*b$dDV*`REjF9$fElUrKQ[d&4$eE +B%&PpdQ'6JkG@Z+aH(rj`3JKS+8DDhiIFI@&eR5)a%ZHmAX4T8,ha$0kdZhIQkTb +b3QRT-EqC`N1BXCLl2RDd+@a+3mUTf,)MU-i,"MG($8NR!iHQTJ6kajfB`!Pa18$ +FNKerI)QK("6Nc*Sr2V95UFK-&UMEJIVAlpRf#ZlpMc,+BabY2jcSDQl"[(0d$XK +Q#@UR0UrIMcN`5Y9@YC!!j0AaK*V(Da&CY[4r%!l-P+q-(!lSV9bf4b3%pFF-YHK +h2i2EDmQ&@9cBBjJa15JP`Sh)e$NrLZX"iNlTj[r#+@8@eAaqebY%eE!,eL9l(%r +r,,BLm-Ma9(Q01$URIdJ,DAM6hP#$Z#*d'ECGKQ[6)KX4Z,-kEqh&cUN6ZT!!3rX +bB3XeEXR6@,"SMY2l!9#R&3RkJXGEJL`"1!YS%XLU"`Fr9HaAbk[$Vb'G4H"kLrY +k40H5f(ck0[aKZaYYGCJGfileS(0+6Gk+E-R0LXG,K41`4J0HGJKY,(S)8`"Vmj2 +hYhR$[@dI((9RcB!-NiM!Q"aU80ASqd8mM!MCDfDj$A%['4,$*f4pZ+%F8fIe0N4 +GG-FKCTSI3l8j1%F)d`lmAQ"@DYUkp*d%N86G#ek8*RR%9qh(ah%4RcdF)-`fk!- +%5eaMlm9Tq2qr"YUC)N4SP,-86l)U5'#4Vb3D[ilbJFe(0X0+3$M4T0Eb1'kIhdX +hA0P1[IKS1!QEN!!eb6,pF`8b"i[Bh6K5BJhP"V'Pqf!2N!#291e%d6`U+@i"[!9 +63m+N9hF9P,jV6!&@"9QDkcC%189ld"!)HKEEDkqr9NGB95+LTeY@%%&aI-M4)YK +NlqYc)ZY)iqmVmF`+9$+eLhPp!Vai668U0#h28k`[JV@T,$I`X[k*F0[e4ebCX`8 +pSH8@L0`"iL9fQlKjU-pNE2*F12FAkL0KAk#8P3@$de-E#YV`c9[KaMbTke@0YaP +VM#c6P%T&&+*$LI,4MCMXcaXm(JKq0AJ@2jqALJDL('F-*G!FdkE)RRZjRRJVabN +dqKA4D2U["iR,!!i%0ED4Yp'M23qe`&2%jM)UMJS,2c"e81J8[kb&`E0'&cdJ'0r +$A6ET5D9)J-K%$kN1TV3J(kiT03iJq5B6AER+mj!!VLlE"Ge6M$#Rmie5,)eeZV( +UdCllh0)&"G0J!1aE!@'PQ!0dCDG-Q9Dm8NX3rkZaN!!ATJIpX-38ClaVFMd+,hI +"bl*R@Fa94QFU4D9ejhYU9!18U`pY6VHITrYJQ@,9A+Y9qXlAS-`3bX[-BS*9ecA +0!LLN@'3,eE3k5e2d#51bDjr$5!"IaFI$ecBQ3l85emcTdLpR@@ILlQG0fQF-q`B +2-X18!58)f)PCr%5,-QllXmYj")2JbIrBJV'Ip&a2"NdAFpX$`,kUk)mG6iU`A"V +q-8ehJ&,llIBR#DaRch$)[!fmb0[cI*L[86EC'Z+Z3)(`9Y18IiZ"aU("k$q+&0Y +2(`1M6[i$m(T,GVM+L4cQcY*Vlh8X[1p#@QN6m*rHi56c96Y`f9E4$V"*K*iSK%I +04rVr%2A+JP"Ql!U8,6eLL@19Z2%A8JQHRYe0b498D%b@bDi+dafFkC1TbZH99E, +`mRHkPjFAS+F29LZTZNhQEr6`5NY+&BdmhIc8"p%q!aU$c$K[NY2+4mb#+29KXCi +Y&pbeh%i3IZH&Dc)1!54"mZ8l2BlL#HB*1MZ*AT%#Ma1,SN[DAQ8fQ%IahNL8IZC +8T+eDpA*#lbIi#3MJG)1"SfS)pFU"UZ@Q#,FEdrZ@R4Fm-[)UALb,jc8AE189`hJ +-"YTji6Zp*4-ELAA,)bbmIEKrBV)D$`VE%%pDap[SNeG%HPZUV"(1V'PB)PqjNE4 +'"5H(5X84!9mB!0jj)kUk,cU%LA%[Z4pSkP1JhR*&iL'f#U!,F,frAaMGb`XfiA8 +!f#jF1C`5Yb`kYTD%+9EGKijM9mh4Mc&`2,,8H"+dVLjll&%prQdUYT+2)h5JmEa +A%+me'e`r1`M[hArd`6A%ANA,a#,ED*5NF!9M%MAi8%Z3!*)UEFT4p-TpJe0!S0M +9HIIlhIk-mXE8@84X%!SSANf`%'kq)HILTdk"b#!B8he#UdCL%S6L9I6rFiM!F@# +`LhF06SJ'b`&0#4SVj9p6m#h+dLN"S-E+#NQK(YccYqN6F`d[fpNIRa4Z%a1T8l@ +*l(pe*p!KXK65SI'c2maSbN`GPk['iX*IHT2IZ-rArrS"S)H[H1b8)[%mB!+2e*) +0ZjQc3MGl`+MCT1AFqTkF0bhkH'K@aAHC#Aj12K@'`9[48l#U&)FI3Q%Q4SY9hZ5 +D266mJMT5mZF#jd)S9d!c'j5`I3*$D5')fj*@J+)Z4Y@9#DD1`'434a2dB*XGf(U +&5CYU,eMpEE$qE3d`SPmEZp'9NbNhY9,YBc(LmN1@Ql#iT9LVbLrS"MN[&&0-+@i +"'id`DMc#jA!LQC9SIlfTimGY)Elq8#AL41jLe-9adhmNMmVIfZl60$Y,&5f@X!! +,S1Ei[XAR,"j5iL1qheVA`-[YFHiXGqLj6JQShhhkU63iHj,HNUi@dE-6M+RKHGL +l8(c0H%59DLbB9kVZhU(5)4ZllqRlJqm@f2"rehpYEV)h&X[&LmMa@5jJeED1LRG +"F0Hq8j9q[&%md@Nf$I08-Hle[)j@Kk@8K5@X5DQV%(VJ+1@ES2S&bhBrq1[BQ8( +'"I%XkHSrYP&PShSe,jme$pm'NT4$9Hk(lk[%#pk$m6+a)dhQLjBVXcbTh6FrJfj +HT0FYPf##pYd93ZJ8L6-e[bMeJj!!IjjM3Yj"VJ+lpkA$Ur1!3crK0Fq"V1ipj+B +HK396h!e4bL(!YIX['jc6N!$dj6Ip81k2Ulk5caLrJXk%T,cN-L#-+QN("1GYd2A +AT88T$)UGT,`eYiqL4&ErK-985YMP"mA5)Q(LP`Ec9ikChK9NI8+JMYd$aDdZk,q +TcF+1pl`mNb!D2JpA",!PUI+h0aZ2m(88X`(d!#9FZF+,Rr6+%c0C@lp8a+J&%!5 +c1G"QEc2-KC),21m3p*R)c5d*3rU&m-58Za,(NXZ4GeXLR+9Mf'504LFUd0)-p1a +2#BD66)@-aNAXd[LiI6qBUp5P2S#M`cRNX"k`GrAIdqfh8,iU6r40TlHad-`'+#! +,Y5UJRZfKI`G$TB+0*kh#FA,&@b(r(lJ[E",*,cMJ3jS(rD3eb#`3`h!8E@Vp(9N +0@-AHq$G((SXXIBble91Jj"IFBKpTQmAB1kQE3,$-RIV+33JBTA`0VfiJR*!!%kZ +fS)bph`*da+0`f34UkN83lYU1)Ja&EHhaEYlG'jP*ST@MB!)mcEXQ')FXl$dDKIp +H0)TSSI*l[S"l()D8@r@-5RjZRMj'lcfl"f8"@Z-bUkqr4I@8mZ9BNQ!8-C`"D%* +lhi%3#a&l2`R"`A'@XddRXYNUq,9QMGFVS#Xi-lrcf6)(Nb%JVK92a!XNKMF#E,N +V"Q%"Xchk0*i`)G,8N!$G3IRR&ffECI9F`eaQ&f$ABbLTKhGBjM1r43R''d*+c*1 +F38MSR1e(ki+m5MQ9'GN1"G0e'2bj96%r95`*QaSE[SHH*TU"hfJF'bjMYG,L&qY +JThHkaer)S'NIBhmJeVd&)H$8J!$KhU+*UQ+Ci2K9I&2"5hB"(,l-q`P)61ciJ82 +"#(TF!&5!J`KIPpddXMC!*l$Ehc00HXhL,hCqG'cIe$#1I!m'Uj4["SqTp29$ae( +4ErB&,bPKA,!8dS49a8K%#U4'&fH)j%HCb-b&rmA2N4XlpA!Q$!cpedr4hdfjGd! +`-X9q#IT'rjeaI'T23EaAYi#b1c5`hqXSD@k[98lIZ`c0c-Sd+L@GEe9$VX*dIk& +4,dPdd$dAme@Z+L1UD%3l1R)(&Q3B'rSK2lU&3%U$IXBm-SR#3q2mJah,DB'NZD) +YNYJ@k#cZ''VXT$V"E`jH9NS2Pm&@S'h)8d-BHeU(%18dej4*VCBm1[H[laT(%09 +H)UKJM[#eK@RlUZHa'fm(Kq-c*mh"D'ApZ4EU1#M,+*EPF*Yqli&ClpA#0)&a`MQ +DSP$98Q4"9&%Q-6!mr(kXF8643'F)l6FB4T!!bMRTb92R3'(@(8$i#3FdTS%fUU& +U[Uh2`jT$(4(KjINdGDi6Dl'T#,Y+#iNr,"j,9FC6*#PUA'D,&02UTFk5qS[B$fX +8[[`FPU%3*1&GGLdXFcq3!!8$AF"08GlKUVL08'MP6X@3!'+lkZY)l$LBXC!!$+l +"i8%UY39-ICVm`V6a@4'`Ci$P)eedifadR$FA*qi41E#F[VD*j1[M%d`'4k%d'N2 +a@$ljQ3`V!cS*2e`%ia+PG5Ei%*JHIQB[K!i@B10+-Q&pG4I&@(h3)0CJb5jie+* +iFZHcCSaXqA28+ANd+X(3F!2VRZ'AkcDP[jbV[(D%Q32NQp@K6@9E&E10QqpSSH$ +(6KL[4lKCe&a(YJPl$$NimDh1U%Q6C1TSCYh-r%UemDdCA1C`R-NjP&`Iff0JT$- +5BQLXK0+m69Bre[Epp"e44kNiT"&-aV%+#-3Z4`Up4Qq8Sp[1NhaB6fIA+piSb"3 +qQTbir@dbTeP*fmNbJRhV2k$APS+`@18'mdGHl6jM)()pZA"SVAEqXh)S-hY46S, +5E@KLSpaak3#8rKUf1NfXLINfhci((r#A'UJP!$GU"a)LPmA)XF9c*'-VeF#Vf8h +6jVCcmXP8Dh)F`X1MjIUHD0FLfK%06S9lbKmGGI&P'BZ"B$CBB-RZaPDNY+J8P'` +)P!85Ub#4[8YB$MQI@,L#LhBHSh@PRIH&Nb6lP,[(E,'6,K3hL0lbRQ*-#k,3LCU +3!"D+K)5dZJailX69dL&kJ2A06KcMDPKS"qf+YfiG#+!F#E[$N!"KEDM3V5p!(#+ +[hF-fleVfGZ2TSQ'6!ZJN*UY8ANp"%[-hVRL(3"Bkl@P-%,NA`q`8"r#&(Sl%1*f +*Yk@QRD2q%l["XGa&dJ#DF[-)c!KV,rp-TdQd9&rejN)MpLeZJ)+ZF#TIDLV5*UH +2,MS!C"34Aai622T@!LrHG'ZK!fejY'fQCTM@(fmaj1rhKMAK4CkDbe#U[83V1XV +(VlAHJDmNl$Z`d#B4L)!c8QZBSX0"#fBcX64@dYi8V0%jp@$Ea)E5[`0LFDUVc,h +50lpIT4'I(k(d'-dKL@AddVlG*6fkmk8228qc$r9[b)TLMc!FrX-V'IMDJNbU`K" +pqR$p&Bq`rd+h,d(i,Z1jXdph0pN[+$dq-KbiTPI5S9TRa@%&l"J8[VM1B"&j*Ee +(Z8Gjd3p*36eb!BJ`i1i)Q"qaGc3r`e@MT(SaG-be)6(kr,@@(),'+aXY86feIdK +6LmKMRiSY*6%Rh`[8[#[[aB-Eiq)49@b&,@LPf'*bE5U6YJCIMSqhRjG*220%1#m +#M)ZN434@Cj)8iIEE6#X0LjK21mY'&6,PD8Hl!hDTJeNqiAAZm-Qr"Z8@JF59P@9 +PdIHRr)+9@HcTK[)r6mDP,5S1F*@i%l)8-N"e$L8e,SfA@JPEmDSUK`RSKfk*3)N +pLr'``VEQ3'q[mLhFRBIb2F'r#!#TbIij*Y1cQk+N1-kXF4Cfi*9@0iUZAb@T2lX +QB44IKeb+"jfSh"5Q2aerh8Rk@krQp5'eKRQ)0$eaeBH'iDG21H`A`U+P*6(L4Gk +j)L#%8NhiG)-GdI0mX&QG+LTjY"&"fSQSJVh$pX'*bfiU[&2r'kB#N!!-f1YH#+b +&bdp*PU,T@$fQK&AL25'4G[kVXAA04U*101F!3Tcmb8Tk4r[32E,lmJ"k[EMLii* +"I)EC!q0TDS@A!Yq*hFd%'aXGU%V$'BVfMdHS%"cNi-eIAkKd(GTZUkB!*k!@%5B +&!ie(0[5EFp6GSD$TaLqR1RdZ%'f"LEFq9jl#5BSrjfI)TA&C!f0G[B%N)Ck0k-# +V+&UJf$4eIi,fLRdBS#e2CPIFJVR,@ZXiaN"aKSAYH+TRi[XaH[V9VlNf-UIC-ei +pfAdKXQ"cdH$dTA(bH&UGher5)6M!l[,lh-8*"``r&Zba2XLHU8lNm'()#hK%GYG +eYf,2p&NX)qfi-d19CMpGfQi89+0p)icDXFr#I&3NY!M3%(Pdr2AifG4ALGj`6De +*EC@Klm!"jQQp+fY#$aLc@`L&+@UI(EpA(*RMfGY@hX4Y`La'R&5K9Lj6VQ@jp#r +@p)Z9C&[9$aM`K,m-"peJ,MqUL(,ja$3m208mmFMr*#lD@pHhI'G6pAk+@T@8dCK +C4!+[Y#@RLGqN3'VJKe3V3,mAc!803L!FQ')f"`V4DY3c"rV)044LqDmBhq#A$p- +-B"0HlmC-%'#f#4BfhDaI!YSdBEjQfd*&ZQlDcH9QA2BU(5md,&V[0%&(b*LHJbH +rM846XlS+"N6q+a`YM3R[DXAF@$d5`h(Drm8ZXqITPPU6jk&J-T%GVY(&%`CHf38 +H5`FFJG`Km+LmT'6ekKGS[XXJdNc0!P680LBmXQ8D#02jF)CcCEdaMhr54$U18j, +9,RYkGM%%8I'caJTa!F6!2CaLj9,3[h(IHEf(KpYY3A4!9TAV4AhAkH,6r#j+b0E +!r*`EG5KjGKGMBrLcECJFKE$VUh`1M$#L8%MJ19ahU48$DkGRPZADAR#hMBCb$MC +`DNR24QF2fd0UZeEQMY"6SQJblG+(QA3+*`YUAU#D!LTTGA*!f8qlF`)jFEX'Q'D +V4hkTkDS0$T1INi``CYl5#Y2kCh)+HXb()4Q*aJDbBP*f[-U&jI8A%B$2`LVb&Fi +IGiZii1i#TA6G5Lj+m3P51Y+Y9Kbd"5TM35%A+fdLY-X!r5Dd*9"JJdBqGk8)X2" +![*PZ$q6Xr"@ki`rbh8(8qp"GK'[G"Ah+fcQZ((p9R&!FJR$"a""P0rVVG#Z86%- +iK,3&b(JUE%1AjR4Z9Ga$4dUKqGjFe)5ACGX!i&[Ha$FmB0Z5qb"L+ZdRBi'@K8G +6B*9QXrFk63a%Hi-4GXFSB-K-L$R`[KCDGKZAC4!HC#LBEH'U-`RlFh415pUCZBR +33jkG(*R`IfCJ,Qf52EXL9TrXi0j6%+K@@5&ik)&$*VD[2Le3h#$*$d1*cZjkUI` +M64Q&dS+D[kY4if#"KR9-m*UbCcd,,IG'Dl&qLj,6Ui%3D2E+HQUb!q&F'[2j"9` +dFR0-A)p)kpQ,f8D-6Iai*,PBGEU@IN)P%0'8S*i(ip+*q)r`$A)`ja"HXTlb%Re +MhVS[V$!K1TPh3IrU&f%Fl53''VNA@XdU$KpYQm1*lfpa15(dB$eM[d(!!rU,cpP +4F+B6E+LD5fN`@c#$!JGh"m"$Xdp#C1rk4X[V&HAck-2k)m&bj6"GZqjXYFN#Vcp +3G-jXF9RR'PS2CFPQ`BPANdL',k0iXZ,@!0fbI+F"E@SbD)3b)TDG`L1lYMGL,"H +Z(3dHIf`IIG)#LJc3(4`QLdBMe$bQ61+aq$hG!-(k@)VCQ8pE)cSp-j*%5qh%D@m +TiYc)1hX0Dc)r"QZjYQL,GF9#2HU#j`hQLHahfK&3`R-$Z!M3DPHQdC2Vk)Sqc,m +QMJ*U%!hZ2E6@`qmS"H`iIq@KJib01KF(!DI0186$Hb!R(6MZ`H)#4Fk4mS-`9`` +D#`*ZB52AfRd*j`j4[(kE`P1)kL[H,'M)0D5aDXDf3q,L'ljIhXdfVR6GR9q@NAi +NNf5N'Q23&q`S9PG9SXEZ"HbRMU@&!Pj`BSQ1JQU)*QLj9ai!YL20RN*hZHG@4FD +RV-8T%1ck2Q*[FXQ@PPTCUN3H-j6J'm8bBQNCZ9*%ICcqr$-A(ii-Tmi(9la0i!a +T2GI$X6a%*,[Hle#'M-ljS*!!HHf+A)p[mD`F*rjY3!)@!Nd!-(j"2HQbKH&#ePH +m9$1'mUA'lTbPNc9JJcm5BQ2rZX,X8DXci'[jNlc!-H*2P8SHrHLLlKNAY'$aXDB +,M9m0[P%9hlii5Hc#,"DpiiU2i)kq"-A2UHkD"KTlSiUSGIe"'dpMK`3ai8c8jpm +*9210TC!!"38K##-3"P-N`!0"qKVkV,MF,X#085c%%(,$(Gji(ND*P@)C[ejC,'- +CE-a#Vij%[*hmK'3c9#G8`QaPF'[Y9[(("#i-Ma8T2CKN%V-'*eY*G*)f!b(F&6) +CVTK(@'&Aj(G2fKMmarFiUh0B0B,p[hJC`M!SZEli'+"j`)*A4$UHI5UAk5BKT+' +6)%9EETZI4"J88F!))M#P6fX!UJV'cI5GhJMp!HIS)S1a6#rlUkRcQjZiZJG(NB! +TXS,TVQ2i0C'eQK3%rhmQS3UdpSeYBCNLRKpIdJi2fH)SR&GamS*,3ZZaF%UHDf` +S",lL#28QmhX%#h3[-qqCZ5,M),3qR"jf"[fG6hbkel0JL93!j0b#Y5q@H(m6BFK +eMXG,,*Z@Pj92db(!'r1mmaq268YSV$d9FmBa45%-ePKb3'BBRD9Xl)BJ&U280aj +e@mr[R`CC!j80)UNbiBRGb(eLeLmfZ#[G#TdcFqCr9eJ99mG&l!JL1A-r#5*Ea+4 +[)A,crH-,1q+!bkrb`Ee,i-aK4BULK42G1',!iq$XJHGKD@AC&C5(85[kpCp`)8[ +%PACEIMl@f9SHeIF4VXIKlA!L1'a%4LALVm*K9A*5!%&UVpl$i&1XbraK#12rTSi +MkfjRNB)HC-qcRSZ%eS#p4U204B"b%Gkc*q"iHSdXEPaAHjhhakU#-9PZMGkK%e3 +#iGD'[A"jj1NbH8[(+SHiN8ih$rC%!1X$#ji(Gh0QP8qX-Z6K(FF(`B1A1%3)j%, +TVa5TLXdN&SXe)@rGV94%1E&HQQS'J@pfrC*BhVYX+l"DBlFqB(2qcd8b2*V60di +ARYBl4S$ZfNMY-TDFmf3Fr'-GKlZY#MR-`ZbZPki"Z9A"b0!jlC&`H&L6Y9CGK4' +kjb,ZqhTrX&l&lR%ef[FGcfa-6E!XRhBh)*NUMd4bT"#EN!$$$%XMj3'8&Iq5YLb +)8R[9996B9c-RP[GaX6KKAAU6SBQFD*@NkN6`)lYhq-GDU(q9Fe(9Pq2a+pC0'1+ +06Yj#b8iKXlF`9YeX!j3X`9N5Dl#FB9RY5QTQQdc@i)ca&0T[5+@,3NJM,0!chZJ +aekKVD9'r2k0AlNAX@cS9fZ*$++(I$Id6"'$&pFcjR'SE@5U([jfUJbCbeQ[QTrk +`qNX!0)fSq!C&8NjIcaL1-T394XZr(IXPNmbjfY)5I0iSPeY$93hG15mHZRZYIqp +pT@VQ%bK1+U'&Z+GlSmG,+-)R@h`DC3jS0D&jT6BJDJQ%KBq$C)rMKcHRDF&+TUG +jY8S"11YR$RBfIqLFVej!'e6)&'prA5d1NrA@)ddaLB`-ZAj1THRFhK35ppcMqDr +3*)h)'VG+("IXYVG)UIT&Pm'KEP+mYTm-6"8l0VfkK!C)R)I3+96r+@eRQPbZ!V@ +m$,1C1YV*hN`q2CDa4C)1"hD&4j&9q+32YTEbJPD&6)9J`Ue4rlNLHQh%1!UM5`Z +b2bmj%"+9ZUPTf%r0-GT[T+`+ll%b1[GH$BI[GaGCXeNVYHApL4MMk`GrjkYMckL +P'"G[DF8"1(,3AE4hdTcVU['UF63VQkE5,YjfjQ+A+N&HFZX&l5BB3UbhP93C(Vh +Q!*!!P'XcVSrJ)2MDhI#+,Pp6Z9EYT5e&D#A6P4BfHJ0XPEU3!+4d(CqjfEe+5[T +E$%BiC0E5B6e@Hm3)KhM"'6[S3(9a"H1,)j9NG@Y5`r&,Q4BPADRA")8``@UF#2K +6a8d,-PkGm)k+HVlLKD4+c8Lq@3T"IlRTM3aDZT[P,CUMmA![VNA,p'Q-[hTdqEV +NUkQj-MqIKqrDX9m[6qm(kR`)#U$RaD')[`aMQiNX6@[c6HaLUKXlce+R@*mqJaj +a[ZL6X*++!kqHjY$8#m3KF)qq-Z%"S1`b#$d!B0Qb%dFAc"K*fc4l9B9"q4!ajjC +*N!$C%Z$&q`$!k*PB5G5S1cE!"r6-N`,!PD8HcCeaj%KHCYlFK)!GK95q)EXVC*p +m4iRqhk@c01&8eZ*GfCD'-mXE-Laak#pBHRP@%mVY%LiiI4V3r*("(ASB(TX,r!A +rQG0-4QEqpP@-DIT983QpQleJFd+Y4B85A&l8YC+qEmmQ3RY!-rb!2$@[9+3!TPG +C""jS,A4`66j(0Y8$(6iD8h(cFa1p5B#m0"mpBpM24RK@(H0ZB&Y&jZS1d-Xh#Qq +h`0)!5Q&$"NP%hESGQ0Cq*&hfVhFppB$fM3A9h6p%SArADeZ"[Q(@Z1(F)d`X&8B +mPUrQB'3Ch(JCmJDpRI60hNk)H%5A5lY-eT%DbFJ%p@ULHCGLG59l%l5V1+l1YL" +Vj5BD-8N,Pa[*jJca%cPh-K#6'TYepeH''2QMDFZ0M(BKdH&#Bf5I*15LX%C60UE +JJlYAd52ELYYkFNHD)0`aFiQ)clNNN5$9@2Jk*E'JX@FDTRjVcj1YUr&qD%mK"dl +Mp-'L[)P8UC@QkG&cDKpriLT'lckbP*N(60(`@CSM2F68mBFBL-jb2d@9ZNDK2-# +&X@VHM50K5[$YU6lS+@0225)&3P3-N5P6@QH)cfC$1*j*UT!!F*1-!'BV4@f2`16 +$j*'jcG@#4aim5Gm0ce%23bS*LR3JpQ`k8chbi+Q*"01)NP(ZDPf'0D4a!*!!M`P +Te!bFCaSCe8CGT'P*j((JdqJ)"4Z#,m*b,3@ZlRejJLN,AY(&D@pNK*b)T)jaBH# +RDHrFPd,LcX$Q,phmlEm%Rm1&pJN&DKK`%@9Yp4j$F,VmY[#4J$CQmCq#(HR"k,@ +hY[p-QcmjCNG0HIHL'@qr4qdB[qa%B1-JA%mh@8QYB8EJa'B'(ECTB0iilEcTR"B +AEQ-Db0KeApNCaZ*Sr'HaQlBF%8Hja9%(&[jU#kR`BkA09MVLC`2NZ+U2c(8@fBQ +2(%Sl!fl$'Y@*ejT*q4CS@`mbj*D)dkMV4U9"jSfbF(4lPX1'-QGUfX9[@$E822` +fqJV4'jLp+b`QLY(8SD2FEYY%'pkj04C(Y,(lLaYeD6P$"('c[Q8i,e#ES51*ieN +aP'mY[1YHq,TEj4irq5AK,`$eHCV-E(cHJ8FbAmiq$BkKPe3@mqKN8BI-`h2+`qS +,qA,h#KL5KMjeKT&9bqCPC#jaD*j9jNPhFbC@"li36rAb)MXAdcHCZqUUlJ2CBb" +mm&8rKbrf'T9ka[*YrK!SLr"DD(%SF@EVi#4AaD!#eFrlH!-40Vmi8,5kGdlG,9& +rrSEPPCLe[)YTA$di$`9f#*,8lGjVTIUrQ51qmDX#HYA)lV1Z)Ck5+UL"NQR@NJq +85d0)U[k#pYadbJjk+eb4'&Z'1)V+@jSk&*BKCB2N[cB@BR#Z1+Hdd2c)PY3P#ae +SemSHr)3El'i[XZ'Im2a[hEHIAUb*ZK!#,b3HRAiNYcGjjRN0ZRXQqjVmaNc--bH +ErBVXUG3a9TP@B(ed3Sh'm9(pB1Cahmacr8miCG)KI9JJE@hXL!'fpcHSA!94Df" +[p)%%Q4++f34B&Q)VS+Y'[rMHYDF-),b6Bk@al-'P!3@VaCk%%6066eQZ$Hh2Y0% +pi&FRe84GmhSeD$mRKN"L!KCYJE$$SRFjG,"Z!+@3"!%!!$`!J,5Ka+Qh@5'%!!# +iJJ!"$Xd!!!%`!!a)XJ!%(XF!!$*4!*!%$`"8Bfa6D'9XE(-Zci!!!%#b68e3FN0 +A588"!2q3"!#3#S"`!*!'3X(9(r(GKeJVd0rI0G5c(rZ&qH)39'X#pD,Sp#64@B" +*bj!!1bA*X!$h45M4#Upl&elcXXe$8r50`$F,#8T0bhNTqfT0rL[CBY+KBXcEc!, +Z`4$@J)cf`Fje)0&(kI#)6XYRq%PhJXF2j4diff0bZ"TT)VeBVc3(V!p"+2m[*kT +(G0P8pM`XcRGR`KC,+Bp,F1#DET4jDY#QFi'(bYf6*$IA8H39Ga!d,3a(#4KF2*+ +C0j@d#IYb#M1b`Sh'1'e8lfM'f,%#,b&BmLbhU6Mf6M6)lJrZiH[l'Tf$QV-elZE +i@TVM0@N35!T6cLPR+3#Qc-i)b%ISaffjhM("ZCYC#SFA!YN0f!+H#E*@rp@FQGK +mka`bQV0-b6`GK0#hed13!1F8I`Y4p6&m!*hZ4Ia9d&04CQQ1`Rjr)PKS956c8,T +a`2#ZTP4NaAF'@XHCF2I#IE)%MYYam9a(#HSX02'%lpj#$q+YMAU[3Y"0ZENldLK +&5NDa+6$lQf#MP1Ipqd#$%Ie"*"`)1NP&c%6$Tep'`Lk`J5f1+DN1j3kGi3kjFHk +5*bSccReA`5VfdmU%UcMp+5E6,mMd,l6U'E(P%#r5SlSSm,H0qJ*&DGMNqRXS4I1 +)#Mr9cR"lYfl4ZTiDX#9q6flM$IRLJCl"`P$4l!RLIYNjGCN[rLL1fkcf`EelDC* +"PEVhM'2T*2%3E-0Ec3m61,T)!J*rr6@Kb8Ur94Ycr(&DX#)cemR[([)@-P`+3XT +'9IHIEFKKcL%VlAhP+jq!4,k,$9dA8ihdZ"AjGB33[YK*aAjiqTT590NCR`TDR58 +qH8R[c)5NJmC'eBBSV4DY&hNCj*!!4BijPPl4mIFYcj,G[BlU4jilZ'FNN8SkmcK +KMjkJ#5kiGkeijh8,b,q2h[Lh0UbUA9D3!$*iD'3#jhV(2)`qqbHYDmRreC6c3Xe +KPJI"cL9+XF4VSeDRk0P%C+5XV!26a!#fm&49QkZ"V'm#&ZY&SITUXGhQCFUNf[h +-m+BJRE+eI-9DM0IpY,c(EE(+JedXl2,+@8*N#V+NAf4YPHLdZ*)f8$LDJe13!&b +T@8F1'dmmiSfFB!Ue"QRp6kI@-a0[EcJJLke(j`I9d(Y1M9EM(A4X&Ej2FbZML(K +CV(j@"HEa*iKC)e6f[Gp4JFprT!aL[3Re-$Q#0MSQd'Z$$Nk-6$J@[b(0d,Gari6 +FVdP&8eA%Zkr%rf@(VcbPkYRKGaZfMFGDDI@+GTKfSJf!SldiNS(VYQU@ABXJMP3 +61V[bF$9mdRAQ6@B@[rS98G"`1DddBj!!hZ%e+hq6M&-I@S918Vdp[LFGf#i+2ST +&SLp+3&,G9JhLDqiDlarUI#mbCJC$c5DYFGd[Cqmkh)@Gmmql(ha65-b!kNH!#ZN +P!'p,J+(*,UF5fBN2M9i1R$ArU[B0IC)PRQ`@*c@ZP&B1EHAJVAV"h,bZiCke*F+ +)!MGBHlr0iIFd,D&6S(,lL`lpfN6Nre%!&Z'-p1qMEEX8JHYm+IF$2h&ZPk`U8F# +SN@jSH3(KMm#"1+)5AS'!8bZLXJ*D$9VB[")A1CiEZp0V$c#L2S%!4'El`HUqX%a +PdC9IS2)5iU"9C4lC0bFp+!JLBCRcdj)A5@TJqb2qfN[RdHJXZ)-T8FP[&S5ekm2 +QZp#PI[TdjN"US@@XBQZ&P-f8[$q@G@-UH-iaU[&iJhcYR@A%T80A)SGY93Q#C+3 +Q8hJZ$YDAL%4,Tm8`GBr`X(-Z3-ZB(Cp-SSX-c-T'I@dUI@NhlX(qSXb#08r$HTp +IQ"IS$lYQ@0Q0r0%[bRNm+pR%[$mmDq3#@2rX(Z`09jEUV#fAX5Ej,-j0%lNhr'` +-HSKi%N'5c6F5c24cjP4e-$'X&@'N#F0k8`-X[0lM2-hDL'3@3MqF**j&emF,J$V +D[!#,#MriKb,D!ElY%UNHZkMJS,qkGeX)`AVk3bLTlPbU90mF5%%d)($mBCD(ihS +@[YaPBcQ91h+'V3X,AXB2"#[FQHIZ-T@''H+aBUp*-+8rHP2JUD4-MP6J"mE*Ij4 +)Aj`+T-dl4NQNHmRh)BZl[D)FQ@"(F5-IHGdQ0b+F,5fX1UD@a#(CfhDlJlcrpVh +T8LMSfpl#JVl)*p!6'0bkN!#+li8Iqq(%PB[NfHH)M(K3`pa'P5bk'BD$I#,lp!@ +5+FQ"Y#J-lP%%+PBa''hR@iE3-E2F'mq-FbmB0liUhF4"j3[UXj0b5%1jJ-3$mCP +(hG99NaJE2U+NYbjVMA40V+BBUq8eI(cTA$DZJI-DPYp$5Z(fc5qrr5KP#,pAYZZ +lV0GZJ@iBa[pkGB0NST@[YP3P)!h$IlHd(LE`Iqj$9rQhXM!'f158*3rrYSmKeFD +GIqH$fX%IRjjeSQFEeaq0ac2H"fC!b)CSikCY9lY91JB@eqIS@Fh4M$5E`05q%48 +QHCZ'9GJqE'*'15m)-TQ1Zhr'A9ch14S[bC3NID0&#r2+B!hF[f%a5C(,fVV,"iH +SbC%PG%2K$#1mbPR`A8DN[1lTrlf"RSVX*f9&41K4EQl#$B[1Q*U(J(-B1%6!KUf +QH(mHa5R5EUZ'**,35cH[SM*`)i0(K"b',,`&rAdXQ`ja!0MHVJ$G#F#@%hA5EQ9 +#8%%h!rVN0F)LIA6Lr1C@(QCkAeEUJ@E59MAhrckb6j6VI1k*3iL,!hFIDI8dHAE +Jea(#!+EY8E`r!eNLF,eIKEXeX),YX%U&bQdqJZ(-F)$QmC+l0U@jF[Y1KAGYEk` +G8eCmkN'd(I[KpVdaA,I6B0k(hRhP2HIYMVGT4-rifRKeUB8Y)hX%#[er59`D,q& +N5$D*CM!8$GUSpKhEX@'Y-GT'd@DcijbpbGd!eCFH,8krK`lL-RY)95m#+(efM[8 +mamGiVGQGI5-c#,M!M42CZr9lY`b,Fp16p#X)S%#`K"0*P4c[iZUi@lQq'!2Pl#f +19FKZr-E+[5HcF#[cS5*m1HiJIHQA3Q'HC$FPj)93Xi`R"`"C6F2eA0@"JT1e85h +dI-*"j8kZm'5,MT&6$+bc6[cHa36M&%%+%cVEN!$+2G$kSb2pR"5KCB[[GV4@l11 +N8Pra-DYr(UPZ3+69C&+KrI2ael0MefV3!iQ,VTaJ-e+P)Y1iD`cE!`Y"Ck8Q)Ij +`+(C#h[BZ%"D23NUC',F,i!%4U@6PdVmV%jdCUaM8B`RU66BX`Y-`5"15d1%mb1A +H"Z2Sj'16-FcfbJEpU[HZpbI&`Z3RN!#`+$LFFZ0)4iI*B`mQF0@Q)J2hVeQhH(% +i",PqR)%"CE846[@F3'J2"T!!C95Ii"4ZNe,-aJ3aG',r+DPJh-#kaKEj[4-r89E +E"1d3G84a#dENhh-Gi(GBi(DCV83ie"!(4GiCf2L6Zmhr$l9'+l(J5)6K4miNX85 +VA,KKmf2cErPCFI6A!mJ"2ee5#,HVCBjY9IFJKf$BiQ[HiEU2R-er`aRf$+31&S$ +Ya,Xi"d#i(f-kYaE)cdZrHeh(h@I8mAVEbEZq+&r!r%ARK8'2(-!TATI9r$C6iN( +5Za5PL5b[Lc@L88*K8eNYBEi&I`24R%H%MS+I-BU))TKFpTjepG"B`LlS52El2U% +RI9fRh`26PUMPqDcK8%rhKk-!1SrPVHmdRPHCS$#0j6#B6YFh+KP931LZX+)'BFd +L49`SJ*I`r-%k,[FiNE3GZ&9iU(9XJ!)f@!SR@XbQ`9DCL'3XCYm5m!a*2KRP9k+ +aNR*Q+G6(XV8K*l9KA!0kL@MYr#6Tc+b)V'r%'CVe#Z[ar,rUX0p()JS8'T)-FD0 +PAYZS'@h1)LL"FV'@b)j%PrbbD3&dpVC@UJfXrAdaH*r3RAdq-qI8ch3m$*Uci8$ ++,I1E!&5f$VZN1!ME-IC'Y8KeTKdJ8aN,2$N2m+c&FVM,bNmhITmqc9IdXYef(S- +%I#$e&@)X"-5L[r%RF'qba%)K)Xc@P31ifT`AUA%rRf&LS'9C%`Z`QfTRJk%cP(L +XpNBVUI%GKjYHPSCA$P'!iF!QQ"CE!a"Kb4Gdf9@PEF2XH%QjZi@[U"G"[+CLbTC +2pC'&-3i@!)AcB5UNQY!)$2X8VJEkSNE%ZB5I&U*,AhY$PVMV2%8i0d3k2i#PTF# +(Sk4GXGHY[*A@AdKe2E-68YJaD*&10)G@L9,*jH!(f&*&iXVcIj(R'T8P#&Aad&V +F(jcY8`E2+-8FVr0b8YPh`m4`c&FM4)@q,jemlq00k&!DQ9&EpYaE#E#0,[2`%Z& +0*G43MLMqNNX0%bYl8P`("DB48YfGG@8SMr)S*IPN%rC+MAD)$65DNDPBcmr+R(2 +V0cf,6Rce$H)J1j!!PBF$mkVblA(@U&V9*CUEL+PD-L0p%'D5H+QD"JL$H@laIU( +mF"XeJhMY2-JjaAr)l0#MJcMJZP)4lIiMr"KqpFPTCC!!A()DYrrf1,1bQc'YG'Y +q*fJp5[K4)q%B014PfiPk4iX`*hVjQ(V[)M[kFPR4l&@CjLY#%`Y"*jNpKS")[bb +C1TNbP`%TRXK!Z%RjGE[F"iNADi1`X2S`U$`HP&6lM+Fif!Ae@YCZjT'-hd580QE +`EZbLJ"KG54H+L6-q)V"#i&p3)i"%+S,+[2$U!EF'$UBC)'1V4RXch#r1SjbNEET +QD%amC8B"#iDBS1+U$095BRANa)C&-U41kU6UQJ6l4fib(!a#-*HVLGFkiPqEJCT +`LfT0l!fNf3ZKT4JY2*FRah3e0X1YUJNQ`d+TaBNVA8SS#(ra1frB+i-9@lE%pX[ +F[R"*ra)35(imh+aSY$J+#AKCZUiRahCp`lm`&[XKUKQ&`H,m1hFB`jiDE16q[83 +0GCpf&,@A[a'04"60JIF&KB(Z(Q!A,CdV3,!Lc'Hj+XKbCSjBIBKZ2fcq%Kqb+8X +IJVL*P'Y#9ISZJk6ASiDL+i(&)d@0iXBL%J'GNdJKIcU(qXPFR"mLkElk)c-R+K6 +1aYpla*eML6YXd`2N#BQa+$pZkH8,j%M$eDKGq)GD3!Rer9H,0i$+42DC`'L#jX8 +IQXf)1,EX@%RIq&cVV4B+&YXAFQF5k8G)Fc#TqeNm-3GY-k1pfG+$2Pabe`jr+(" +,c#ZHf[NSkIM5fq3#("9bf+@1bN@I*ME'FKJep`c2XPPJ#lA`AL%S2S0CU&8dL+6 +N5dC'9&H'%XXQCHD5a9IN*kP8acArm%kZrGPm%V@5MjE3Yp4pjl89NL&&DT!!bU3 +ADY1[A-e`L)iqIkbV+NZ3!-`KLFK2@*9!VKUUX+9aK(SeSiNF8"R!#kZbVTC%iqX +KLpM&`0SH%(E0c[8+iIM9b0eDTNI",@+e5`+@cLYl0[P!kGF&Yh`HR$pVjRb4#[A +ab"aLLXbCqTY4)*Mak3UMRFIiVL'jJTQ1#h1D3%S%bEfMP3EDjjRBQ[l@FeH3!$9 +QDEAYZ%`3"$Q*6YK)CI#+Cd)H(N6Ka"1dM(Yr"0PC424%*Qc+h,NZ"MMhGQ9V"D@ +4!r&(rD@GY@q#VVZAEY6KJp61XDh4mbNXF0(M(9PpH$-2`)CRS@,@+k&1T*ej*8K +pkA16J#r[J-J[)&Y&K@-hcK)@X#)0HJ5f+(aB,ZmNY[AS!6pedJhVG8%fQM*F0j- +1%,N1jQKU,IEb0&Xr`eVeY@5l"Z@U*0q6+DSD6eLG3"'!eBBPM9d@GB93*D8c&hX +*Yifm[XCXGJHPe#9l`hZHr4p1*NlEABLq5M3ph)SlG%8E5f0Li##[,f&SUUBZa#Z +,*f8&ajU(f(e3+[#(9Uqf-j!!iih,E%2)Fl0b9CLSJl"`+Sm,Y-i5ZH6MkYEkKKZ +Z%ephme@&V",!V3+Vfrleq,DB,1U%M4*kc2!j`BFqANiFfTNdEeDe6RQ)Z"b)I9` +XI&&F[fKU4ZIZT3$aLeGjc!erA'%4dJ%I*6#m`kZd!B$M"G)@$q1eJ#RU"phe2(Y +DAUS)D'-HH#&5#l)*2@!aN6'AHMX)lkBI)e5X&qJ(qKS@1mYf0YIZ4l$`Q!3fP%p +hNKr5(!d'FqFl2RIT%f("rE&)"&ReH(pI0E3S$)82TYJRQ9(Y1++FN`2HG6DMTm8 +qI"#NLf5)"YHR!!c6ZKb!-Y`NKe"UbG$&'e$K'RIKV"HkQ4jV`!TNQb[lIDSL,MB +F[8'Fka+kG,0"XEiZ$hq`Kf#!ZpY+d@G6#mr$9(P9CKA,"6Fp%eL4m*)iA46Mq[' +Ti39QCKL8!JCA8AfUSX5pFl&dX"iYf#l5%d+rklHpja9QZ8iH0HFhk#p8'P(NqcZ +PhC9h`L)Tq3ba5DqX30B))(SBTQ84[qbr5i2d6,F,&JX$jXfS5QYhprDAFP%L699 +LjJ[9(MJ"Q4ZH[C`@GDd4V&0K-`"XDlE,#54e#1--Q31PGjkcABc!*906VqX0V6# +C#-c[PMD&5SDEJLQcS`,i61X4KMj#rjrm9NlS+"28XI"3SQ6'Xb""jrN-q#C,m4G +)F9$G+0QPMe-P@`[`Q`YhKBJV"6*$MM"Fkkm34RCFhbj*qr#G'(ZZ[ck6cXLb3)2 +2FVAS$jZID2&3a',dDRGd9+,1jXj!$Pre9jaX1)E0hcJh!Ea8L$kf%2AibjKSYUd +0h8NF0XXr,HQJJUeEBYGHYT(f!UB8!5faZUV0-418G6ekF*5X5aGXlkDA8-SYV96 +1)cF*B[@aIrb0c#3pc6%$lRC&p#ALSJG`8fH5aITjaY&Db%*Y8Q4+lrVHTF`e+mG +J3eJ!%Rm&08&Z%"idD(Kb(c[d!GF)%e)Ifi[P)0rcArF%-UG-'PXc9U5(-M'J`EK +4CqN9F0XVH%kd)*c1)lh!8lhqcC6J+TR(5Vi[rDGk5$`DTQZ4QclL9f1HS,RK`5- +C#$MND2DY@DK+9IXDGPDq64fk+L5r$jc(K)3%-Ka8&R#p#AX9&irbDRM5)!&`XK! +U@Ne-dd1MAB('8+$(QcV!bpFL"+DUb+hf2Fa,)dCI$XF)&kb`lS2jh3JL3%Z4(lp +cI)IDUCpAV@!$1!G(YQQA8%58,C!!)2eV"!4dYV0FQq%UVU,h*$lLC-G-83rJV8K +l-jBBf%Gp$E"P2RZJdq!ic0I+K"Z4aceXh&UR3SEkQPCc1GCjCQ2!"iX1,f0H!Hf +&C1ZI&e-*VYNVCaq,3(YUGA+CBaVMjE0[kIaN10*'13BeLY0*9NMABMB2UITf(AE +3+P&Vmr!Ipp1#RH6C[6bX23)[fl'MBqi8F1TSj@JQFhAYKlBE2ZJ0AXelCi9TelK +6+G`q0IJM-ZRMhCUX!FJm0C**KQb680ZH"Nq)Ji[aKU*`T(6#l@SSTJq%+ki[6d9 +M[Uf4DidRlLLZ69[["3diQ*hZ-fq,l&9M%2iPk&&JUjkCB`iEYjbe*l#H0b['Zj@ +#5Me)DE68`k0D4BiLR5rhIijA2MMYKEhM#PlQ3Sd3JkMK3""j-S0&)41`&!Aj1pi +HQM9"ckR&IXiLXREUGCQ@)5e$XRbHYPeJlM##T"04Kf+)KBQa-iU6&,SKV'jc4QU +!6+"EAciXJ"jeE2V&4Sc4qMZ2"G(ijhT*EbGQ&T!!)*0eGG%6qFY(R#EPcrAfpfU +U,3U-ee$edL9)YVrlTc$5jpm)SEldkIaV[-a6iXq6!dLMl%NCeK4Hf94ZA,CN@VY +CE3mAqe1b@lG@PNV,1RCQB8eMF4,*B`"F5*qh$A23MM%6'2j8Sd`V6LFhR2%+@pp +A1Q*APbLI-rc,[fhcLk9'leU1Eja1djNqTp4b15aGF`2&LS9D`UXUEIKYj9XM"D- +blrSUj29R!JaCkZRUVJa,lD[)@-eE(,SDTaHBPFNmBe&)ma'GPlKG2IK,48X6SfP +G"I+CShM!(HJR@VDErULca0-6Ae4N5SAP[j)0%Yb5dAVPld-eDCZ[JkaEQfF"425 +!0q-d2-!k,qAI41r#eJGJBd`GHie`%epJ(UdD)(jUqe&XE[@DiYMk5R!AiUYLZP& +jE&CTVI9b`$N+SF6h8amQG3#m4i3k'[TU%pK6+,0j5bJU(Z[LAI0r"b#Jk*iG+1H +YA9'H5KKB$#SSPU`jdDEM"'Q"r5SQjX@[)G+DlGl"1rP5Njpj4Td#k4T%m(!L4@M +0Tpe3Sl&f+SSeP%[2!C,d)!XH!@(ZeI%ZfVYfDG0M)SekrXVD@J`e#cm#5HG`@4d +p1&Q4fqi11GVAr5X[HP0bdU$!5E#a#BdfLXlkZ1SV"q8'I'6qjD3JZI,LlPbHP5N +rX@C2*H4)+lKE3M[%rDC2GpZY6f`fYU#8a-b'pN%$al"d+9$LeTHbX)d0(djPVl+ +EEbK@B(a!$PedGf35iQm[MhBqG+iUdLThLR#IHU#R`SPENkSU0D@V$KbPPp@jV+H +I9pfMj6edd9r4$#'N6J3dRRC&ij`dUmXN&6T)p1H()lMY#UJkD6J"6[T@CXXBccL +l$BQCcXCapCSCQQmKcfIDB@,FdmFN2q9$@%HPFMI-qSCa80k&(ML42813!$G@&1J +%aAdfcBIMQ5,8i#a"&BadB0%&UVKM%8*lpAapP)+eN9(Z1@%2fM8PJLATp9Bp`1Y +d#`DR[,N-*T,`0J)dmqiJ6H8JBeJIM!k@CSMqRN%h2&`BX%J*a2lSVE(bb8%,"p- +UDY!P-5EcQi3Hj3fMF*%DR2B"h#-2p1MH"qQEFC!!9%Icj'F3(S$,"I(qCFfD5V5 +Xb(hM)LeRTaa1jqr[HR2'TrCVP@,kN!$!@f'8(BC)r40d`ZFHqE5qbfbXCe,R6-4 +F*rJ2a&KSiGl-"%9q1k"J6@5L`RAb-3Fhi`GUiIa`TJ'JAPb9B#E5Ni%D-RKX'`T +([2)amID2Hlc`4fq4a!PkmA&SjI*l!ipP(iIMN8,&eQb[b-rFIcEUC,M$U&,jV[q +RS1ScZD3T2+6HCfSkR#KX-aKjH5N*-M"q#5'P&hRHH9NaSh*ImlYMr,mjF2Nc+`@ +P*X1D!Qc)59EbYm*#-&,Sd8JP$fa@1LK[q585Q!N[%4mc%AG'ELQ)@h4"`J9Pd0" +1&P6rC+Gl*Nd'RH42066iH+ET,p3j9#2N)[T0[qB1Jl0QMXi3VS'2XhQCKf[F9ar +pB8M9ZN0a*#kZh$,&p4eq68U#hkd(m#Y*KL@VTJp[!aQ3!&ahL`S+qB"#Qj5em%3 +d8'p+V'hV[6%$i24+&9$DbM-XdPe26r-)rU*I9`mkUBGr*k10$H$mVEaBR%kVKcp +2T34f)2KX'IY([@S,+5EDDC+)dYiaFJFC1NR0-8ekqIN2,`LmZ1Y``b+m@NeGbF4 +VLr"(E$ScUR241-,8iq8V,HrSXjCN@hJ[r*r,1[liei8"eI#&VAJ1($QM-($,6`V +m56SbI0F@4SkL!XGM-)$cfC)`QVTYdbYapS)hl%$LEbXE,BUq[1'Kc*dX&rcP'85 +-b"@CD6+ifHZJXU%LQYJ@e3qCcfT(dTXS'N-SY[Z$Y,CGNiR@emPTZTJiJP5rr*p +j#Y%06HijFYrZ((N4X[CZY0Er5N&4#1Ur%DcX&9IpMqTC@`[I(#G396)PFG+I8j* +biI$$qcTZQ4*aP8K'h10J)9YTaQApKLRR49!$c@%VpKSNfMHVjG'[j)Q!d@*%PaG +B#db5URPiDTbA0,lNDAAP6XjK%,`a2DIhl,U0TL-%HE4SV16#ql5bV9+r*rX!qE# +(m@Q[dL&r`pc8m2h*+A-C($V'Imb$j0I#U0-V46+*5r1Vj%N"!SG-H`[3Dj0j8SJ +GT1rJrLm6h2)TG5X-6#)BU6@1RIZ9QUIVd#)`BYTXN!"lcr0B3l&KZ@Q%,hI$beE +lCIr6Q*0d%2`BN5L99D('10`SbR)k2A``Bb$jPmrS(ZU))$MNIPU&BGBV-bERCaX +3[KY(P3dJqRP6fej`K8%q9'1fQD#Gf"@Sk'k%+8aci'aLGYrF`L&pe$cTiES&R[V +Y9,cLa5p42HPYFfcdZ1J*IU"6"h2-LLB4ibIlLM-+jbh`'"mRc4aTc*JQhm,a-jZ +kPi-#QRp!CaQEih!eqKTDLJKlKLBam`8V6)`cHjYjB2!pHUV(RqV&"r2'Al1Gh([ +5hU%BRP$m,IU8DEP1!bL*R[5S@Y$Lb!eP#!MHj0UjkRH(-$,1Glk*))$2GqjBkeV +eUDr`+iIPaKr(G6p2,lYJ6*%"i!pbkQ5E+a!3!`Q%I8dDf!&i8(VblV8c*AIZl&P +BL-'!-iaRl"YCLAP6"eYP2Z3rY19"lp[$h,rqR*ZY&GI@8P3k)A0$D%9UX9Tq309 +TZ%IK2['0P--j1bD`MjFM@cbDJhD$*X*6q$f'`L"'"+0N#`4C(X-M$mp[BZE,HAP +6#!(h9NShIT[Jdb5HVb4qQT)KMN*9AFBZpA0+AFT5PRHfZ"-C38)RF8T,IGCqA1L +PAGkI0!Dk,eNE$UqPU&kERp!2jhX+8(L'cQ[NC1lk@,Q-(LL,bih%8YD!*8*LP*8 +mh[5"ic+Y@3BC@!D0`@5j[[0pPQrARETV%qp'R0II(P#cS$T0FY,N[mV2jH'b363 +GU2ZEH*hmE3&QM4C+qmCX#2R[dd[9[9pGmNHARJa$9)R(rdHUqX*%epFrQBEl6Z8 +E%h*dc*ANML,efcc*a"5jicR$"4S&pl)ledh),jQ4G#&qpYCUlB40+4'&$Ddi!+S +-I@'k#C[j9#F!I4J8*elSFF'0MIAF"0++"T%U9RfCU"U*kDEd0@-G3[$FrE+A[2P +Ql"IAAjq*a)B8LC1R#De$30,AjJa*A$m`8[b("$Vr9j!!h(j-4X"Z6j'*"@#dJ"9 +)AR(cRfTe@1cBG@m6Ai8B5'pCi,D6HdcdpJ+$iLaHTrT'J%2l"cdcDH"`QV#U'p+ +m88$NU043+F#6"lC(+Q8hiYi!K5C)P%%[%6ppehJ%e8LYR@NGp*@P,%-,`1HSZPc +(&*+6YJ!'Hf`@R`c[R1N)XTK4hB%@mSJJ`LKZ0Tf+6l'5YhSFC&*)Ibec[CLKY*U +VZI9ea8lPG0P"4Rbd!*bX0*IM1`9!Y2$UPqSNX"Ur`(GXf1Q,`IPrlND06HTMkD* +DBDQ%5L$RlC'QLZDlTTTrAQedXV@%fDRQ'0cT2V3NL'hrUQ*0QJYl[q0()pG0-H2 +,21i#506El-4KVTP[Mf"aURGkZcDSqlVF-,XEj5VpN!$TU)mfM'G%qUZSkVa4Xcf +MaFN',)9ZUJB'$JNmCG69HC*GXfdLH%'ZaCNeBmd`m-HZeSL8mSSApb"i"[h56jG +,j9JS-HKk@BIZip(jXF,RV)dZYp6JDJab&YV5)HSJJ!Pq#h2N$2M5%!+Dhjp6e`( +'hrK4iDIS*Kr`6R,Je&SX1LY)$[*0TLrB5fhS36"VM#Q&-MMK5YBbk'eLNXh3E"$ +6qP,QV4ilV8R0U[lc,$LaE&d@"F'l8Ci"5cAHSqQNMEA,+8[mch[hD-X3km9DU@X +D6+KHECr,8FQhPT!!ArkeRBh%q"AiF32qH5(VYpIUA011%@T-@CXefaAQc"ZpC$c +ac[EY(#-d-f,,QX22Pf"Iikha`F5@ThRd200VA+5cSMhKU%9$rMp%qMY3S2a*R5k +am*-f*V@E08U&-+$UdiDbbBl2N!$2*[-8#V*iBXkE46Kl(3*m*0S3&Z68-3Hm5`i +8JcPfehQQar#HGD$e@q!cirlTqH6&G5M3BkqrLcd`erB3"$F@G"dZF1QVq[@Vi5U +U&JTDT*!!-GpmCflG94U0S6$q6DfmI0&CJ)+NcTmi2hkb1mYr"XJ*G%!-BICP$db +6dGBSf'beEYA0m-0@@UBrmm&$"ik%'4TGdd&NBj[aJQLBLXlEIr0k4Nj+N9iMEfN +QJf0K,XLI"21PC()M2,#pJmI)D-L%,Ti,qfVXi80[XLSB@k9e$PNBj5R*8K%S[k` +0fAGMVH#-E'LINSQ#2c(q32ZJ&*E*KFd0K)K(TD@eaPCk#e10P9i#[XM)9XHP@GM +e9PA&#-*M2IQjS%p3Z1B'@1P$ia-HIH&Eh6[aI9SGIfT!`BXlpUrUL'aH*)JiG-9 +H($`!3Q1T(`(e!cMU#5ec6%)!a5b%l9qrE@D!q-50-+LR,a18amcBDa+@`epQmE0 +Zdpi@NNVhT4T"b,@E'G*k62',@G!,*+bcl19`kD@0(GD+1c6AH0F`+5%BVFCe5&T +kBA*EjFR[j"FRPr4V$MQZiXIYK%AM-6BKi&6TieZ4ckGP19Ra`@lY'FhI'Y)-`ai +6qrr(`E8H,8bebG9RLNRB,Gi3h4Ebial,3L058bDAIXXXkV*Q69IIQ6lIJ[I2139 +A`-I)#)"A@ZUUIX&S12*2A!8NUpa"q0T!-a`%Ed3Q`%iRB)09((b[86rHiE30N!$ +c`L"IGef#h!kB5TQdA6QFpQhkcb0AKMAV1GFlUC+USFUqH2CYNal,(aK35MjId)I +kA1jkJ2E*FI9ZcbIU2QKQRi9-EIUP%10VUkchHSG@qB@PZ!8eR8CXS3M2b"`KJZ+ +3!1HfEk-MV-9b1'$@ZN0b4fd(HXPjQ(lRjr)U@hFFX03UkD`0eq[kQ@AEEk2aA+@ +eh&&4F!P3RB8-4'26TRmIBmiPhJUHLMq,laqe52El43h+q4TSA2MSLP'qacFKKBq +rBG#i&R3$HK305I'22U`)eBDCB8M%kbZb`,'1Vaa[p%9KP-5cRP#km6%mNRRZJh[ +flr#$$!&Xc!,i#QRB)d2d!RF`5hRD+ddYfjPrL2ARYm9iGl6cZ$YR4iG6c-R9dI) +%M4X'$8YBP-mRX9Lh#$(BF'MP-r,"F#DAJPp%#"iKEMjF!ck8*rU4M#ElBkH96Fh +E8ZH46d+qBDRT#l-AU9fZ9`-6BZVRMZQ8Qk2lYM&HLN6dp!C"dVUMN!!QNFXfd+B +%mrc#PE@Tc8CcN41hj0S+9`[0E%mf9f+5p4DVT+-p[Sa6#ZaNE233(&N#"*R`0'G +Hik*5A2Imblk@rNXY0"j8VX'f5T,Zl10%l"S6#qPD('a#*(1C"QL+q#aa(#3&2I1 +e2NUYF!Mmd1*#c+Q9I4ad24GS5Gc,*DKieI68mE3NN!!CF8&i!Dp&+DdA(Y'T0Na +pXB38X[Rb@NQ3!0S#((Nd8LZ"Cr21'+2bJb%!Zlm4BTGaq(NjQQjTXf%,Li8`$BY ++JS,T'pq0KXT,f6i1LE3"jG(!H4AQ*8mPlKj9"X(5(Q"ZEqpcPP(mGMEqdjZBc3e +R0J"%qhKQI[m9JbX$@LH)&-9P"A6)!69!eH29f(ak&jbl%0-ASq(5SlK)aJ5A)2` +AXP2m6BR%6Y&B8-[c0!R,r$!KUQ'XPR-H56cj'eEmUP'8'K+iiEH`pqCPiX`)2h3 +a,5P+rYT6+QD!8h"0)9Lqj9*)#0"(*+,F&,1@ee%@1%Sj5fbM-EQ)6"#YA2%Mm3V +(!Ni$CKr%krXqHP[E%ilbE4mj@C1Z)r[D98GVG$mb'"HMd@UX,LD29)dI*B#4'"& +&Yrd5'1F#Gq@65*!!GTep5+DGU2pHeKj3#ID$0!C%8R+8XL6NIN%1EQ%Zj5AD&c" +jKj+M96qR5[Fb+A*$)GdV-)L8q''bq5X)0NE$ZdkRE0hXhGMMU9`d8-b#i,6XMJ6 +aBQqq$Kqr#TbGQraBj[qjX'+AF)@ZcQ0GTSJbPJUk'*)jZc-cMUacj0#Sb!S%1PH +Lr!,VlfD6Ii8P(T)TfQA*8La&Y!)V*05bZRRNLQQ!RFcVH,Bbl(4DDfPAEABpBZE +#ih`'&3mJ9Hc#P[!(01p(hpAaPcJ),Pa*YBMC!a@JNE-,i"dm91NlM*JFTemMKLY +@J2,rj0e$UkQNfmeF$Qr8'CYZqDGSUGP968SBZqNP2dU*FH5KqKl8Sbi0c1f[#FX +Y9&3e(AUaIF,NAVk(AANbcla)RaA4c$hkDKVNr[CmJ3C,1IUffSFkeQR0AkNQLm@ +Ua[PLZ"5c"$(BeTba5Z)$`AlcF35YA+,RIE"0Tf%aQL'G5Kp0#-SZq3XjQpYKqrU +Z'"22-MIUZAiDIJVG&S%6k`A$Y"AMXDXIVH1&e)K)1(V3L'IZIDXr8f5E"1EQIF, +j8*V"NR+lP)eZS,mF@`V9Em"C'mA6UIr!5q""CjAIAUTD+MG8T9-Vq4TB@h3G6%5 ++h"@1rFa(4Sh4d%FDIML)5CNC-Q4*GAf1jZAB)2!YDH%Z$@"GHcI&@H)UL%XDhq) +DLLIQHMQ48p3LB*E`1iII6q9XSM)P1f',9+F%'#!YqX'R$NbY0!Y,`RA+PKXZHUC +VJ#6K'PUJKb3"1bPBR*p%mmk4EYR,fb-UEBr19Ibk1Yr2akNAp9SF39e3UZ0$mLF +22rr13m`4f%NK11T4Y2lCfJB4RPPD1fPI2eq-KL0@X4!,B!B-EdMR!HM13MNXqcM +QZqi*VTc5L,GVL@G9Q+CVH&)UPZ)Ce6'rYZCHc,pI('a&&p0lQbq!6P4`'jmYM69 +)hjpSLj!!mZU3!%ehhppUG%*-0rK'Ur+cP'Bj8b"LYIM@Ur0fVTJ@AXXhr*aiSB0 +1I2`jABm-HI"&lRr9j,Ybr1Q$NXU`4Mq8aeFP*!8lkKKKT!0)2TqUji2JcLeNp`N +)mH!DQ#4ha,2d$"ZNi&qL'c[NS-Z6m5iljR,lQKG9)0eckbeMlJS0I0jk(fZjYU* +8b[&jHN4`SGHbiTA)6"Va%$9eACU-rNl"mHRiG)&pT!9&q"3V&1[ed21e4VVBfJ( +#da0MPYIPr'PIB,p@$SEcHj&-DKK",N5F2hZ5Zck#RVlP2dTAA'rd#['!Sp(mEDj +i2VYcLrh5M*F*8k9NZeSY8`f%CK)@&23B1-QPlqfJ'!eQ`UCEf%MPC"a-`Zr40Ua +2Q$@Li9-'TERGjcQ)hAiTPbA'P--j,fDlZ5Vf)Kq#r8mhXcbrmZMrA-UkDjf'T3f +%i+X&DchhkaYa5[Y2r%''J)5C"`di'5K-9ZP)AULV[a-l+[4YV,)FJbL4r2R6FlH +ZXB48!B"kGe9Pf,2C%0f&)Il8-KD!1rp`j[XGF4@)6@2c[KN$%p2,EpZ1aFc8P[U +Rd(Q"ah!4d"5XDN'"5X0lkQ[@3Z1L6l6d16$6l5+hpU+%8+fcIr$2d%XbAA2l*!L +Y20b,j#F*Y-QIS*2D&@lcRIA&M%RMcJS9QR*1j1R3KP*ICpVB$bd#X+Mj5%UGVH6 +%C9D&4M*'Y"Dpami+-6dkcb&q@m&m6#G*dHXm"jmN5BlbRXJIcQr8i6Vd"F0ALjd +rhH6F5a!DD"q`biRb%[GFlPRJ4(H%@kZZ"fhlrQGZSk9Il#krXF#KLRZeXac`0Vp +Jk'QI-AS)E,aj(Uf#26R3$NNFl*ZFe0kNElF+)jeZ-rBdLA$fKA2h*UL62)-ARMm +(`LL#IZ1`Z*TpQ2IaiFMkN!#$[9kABhY,RD)A+JNRQi8`A98p9iNL5f4!,,h4-4' +r8dcTM-I8ScB9U-m%6C[A"L(D6!EQ+(5`)k9d+r*h[GiqD5CX"KaAIXL[%2!Kr$! +kc52EKr$#9(l,NdI@P%8(p8X59bNC,2f0B$EZf!)Y!CH,bRc$0iGD+P9@0h-RF84 +*aaMC,35rD0-af)DTA9hZ,HhY"EC+RdBei`+M2ZjP)c+qUG6bLaZ$6+ZT'[6j$F8 +V-NiK$CA*6fVGeLqkHEC@JIC36A`cNqL+&8'N3dKQ`h'MQPB'*P",GXA(VX($Mb9 +aGP1`J$r2rUH58QkeN!"I"#)T#KIPBdDGjPmJFkJ-QAI583`eBMUkdl1!M,8QEh5 +KD,@QMq'HNS&9+1)cRRCj("dlZ@bM,bk8)TLT&+KA4)YLC)0Z3k$KU"F"0RQGl[X +jcjMqB(pP3qH1MIf6ZjbP8!$JbT+qIpaN*)2H0d&[D!HKR+JSUZ8Yrh&)1@N#q5f +aYV'SpE-MEDfk8CI+E-b0-k2iQ15afA`884+49(pS6A!Hm+q6!d9`QXBN"Uc1H(, +ARCmF8*P!$cm(lK$lG8HILL%'-JpZ#`J9cpdF1C*HXerSGRXB*`PLLD[F8YX&0aG +R$Vpm+Fic,VlUr3Yd9TY2Xf0*jYQFUTM)5V88kHr8Z&ipciSh1[)S'ihH5+$UmrV +H5@C`P(S$-a3SY,a#DVT'5q$CJ,,Z!fK9[`2Dj8pIf6!9I5Pc!!H%T`DI'k*mE#I +Jl(*LDDaBK3KC*cN#9@#l,+F&1I#!b#k5MF"MKjdlcf2SXa&UG*M@XX@PQ!&qR[+ +TADfDK6T!`%-%phZHf5L@[6m#Rb!YQT`A#!A5Q`L08aMBT(09dGSQ[QE&jr24Zhp +Nr)&h3rP[&qGi)80bdq")5*6T3qk0pkDqYYVQl9'hFi![4I-l&Q+9cpM3KkSh8"% +hrd@XBd1CddAr&hB2J*Dj*r)0-I&FD@b3!0&N,V8cQPCZ,cZbd0remh+Kr`-p%XV +VGr6kV$Tb(6X`kbj`15mY$3"j+"*VV#B4*f''eE`#2bFFfr-#eb@&"N'&XP-bd34 +1&8!-J*6,02r!P5+C*bNiBJ4NqGD9iL-)ZXaMN!"TB3JG9G9U$,4@BYf1Lr'(&P) +3-XqS!ai0YFSKT"1'C5HP'IYk6L!%a%e!rqbKX2`+!`RH-aYBi%J`c6ehpJDKAT! +!A`R3@!JhPI3I('q2&Ld`dc#@bfVQAMpl%MFC--)b0j6THbUGU@!TF4cNB#"Cd8@ +M`fh8m8X)[5iGqYcm,SZhLPaqK-"q[qpeNaA[Gfmbmb!a!%&B(klUL()Ld9ar"mG +E1UC&QFDE!XZ@&N6cKB@28$G93mJZp$!3Ik+KPeSL*XdF"hCC@&HVmem+%%hAD'' +&4*Flq+Lq5VSSepP(h5QYQ6"&!C3DkHmThZk[`K%IiIABqSb#GkDr)4pH6Z40'#) +Jh#f9H1QLTYK)FA318MU''X,q[0SUHK4F1p*pKG![k(8D'ei68Q-(+kK*e04Q+pH +eK0$$U5DQj0!`)bZZBLmU+4pf-8RpUH(m0%f5q[*hkidPG-NSek1QR%RK4M`r*Pf +Mk)Z%bl8pSd!,AH,j8(dQLLcihDYl4pF56cP)m9R9,J[(j[[N,V@@2BZ84RGci*[ +m5IC9a586IUSTB$9kbRpACJMAQi9qN8'FHi+i!#RI5RB%9TEY['(fM+M"&-Z-)b& +K8RTpdVebHX%`rPf+aKF,(B'lBHA(E%FpcC&De&f8U8YH[2-6jVlRIVTPELq1FS9 +4,JkR!Lr*S"1G1H52[Q*X%YhR3qi8CFCNKFhC*$)MC+aNP4B&L0f&cfPY@fMQI3J +N!63',SBNkMf##G6eXSmZ9C8#*!D'J$%SpIIm4fAVqXFMX$1IUQ(3U[ilVBP"dFf +Y&R9k'jF@M$!fI-GMkjk5UU6f0V13!28ZN!#`K%cK1fHcpa%c'-)N*3(*PKE%f@A +!$e-aD[6,lqX8jB`KXGNcZ4&cN@NE,1HrVpZ0kU5HJ8ED*QlqI%p4R%$)TbcpV2) +([)9hPD9@1Ia2$bpk&)"i44h*1X8mfTeiqclLLcRYAh%bJ#deaBE2(0r8BUdfQe4 +(VU"8*@J65-VRX4cPVi9PADfAEfY8BpZa#H+3!+@3"!%!!%!!%,JMfVZi)pUl!!$ +F(!!",&d!!!%`!"$kD3!'SDi!!"cR!*!%$`"8Bfa6D'9XE(-Zci!ZH'eX!!%h0P4 +&@&4$9dP&!3$rN!3!N!U!!*!*!6S!N!-h!*!%$`"#`G8,I9Aq#(9[MSa5YEUl5UX +pY*Q1mZC@MAl29r$e'2J4rm[eF[LMC)4Jl&H"X@*aBC!!jGX&-%,"e0*&TEMEM'a +Ap)XDl+*0iq00A"9P3fJ*)FB3UC!!TepBrSpBCMc#ckdj0m'"Lm60!02&ifLCTr8 +a+"T`hr)YJNiAa@28a4a"+lhPV[!FA8-p%VJCZ*+X,i2ea3QDM5aiBR,`A!!`+"T +[R*-l,1@FSqjqr8PU`,B-fbR1'QZ-9FV6d0lI`IrS&KbhRXCrqIkVmhJBVX%GK0p +mNAUT1jfjI-$!!pJHdTAiqd'$a`KbQ9KQ)L9dl6A-[Q6Pcc@&51F(l0kP!a8,TkY +8YB%hBrGEJ#'KH%S@ZKFTQ[,VEIciK#P-69@fNFm0eCrVHU-d`@F-QpT9D!ZlZF% +aMN1c@4KZmqVrTM5G'Ul'MjA[FbRL[DUV2Yp+Y$mjR-ib"#Zm-C3'&CQjF-Q9`Am +%429Tk8mp`)Ph&N)e([fZpk%YA`Xp[ABec)&kIVciX5(&)2`V4h*ckeQFGXjqLak +R5C!!XHL@)"6qI2-Kc'GaE'F#@A([T0q2Y1H'$2feM@@l!rFK6d[L1`BZpUYp@KB +T10jjXIbHQRbYZM-Z1@j-(J'j#[rfQAf#4Dbj#bH3!$GG'0XcBAFaFa6Jl(M&jK4 +LI6*N"*VP2&YDb26LM[Q4$'-Va"b(fA8eQ&"+B9a!a,TrGT%3AeDNhF4#RjhfHf8 +de!"lMY$[C2-ahX1QA--49D!TN!"!@11j95Y$$@JZQArN+$,qC"@6S+APebb3!$m +V&Y*)#XL`&'XmRb-Y[DE@!["R39LUF@-FjEYGQDA!EN'qR*HTSF8RlY-Y*bNCGUe +)rTX939pR+"0IBAq9eL#VQcj9PL(S%(L9F03$bX`@Nj1S-Z(m-Ph#M4P*)a&`qM" +22&4[q$8RGl$*l-qBqJ(EhcE6Ib-h,EGa(L@3!'P+(N2LcU[*!"-a9FTV8E$4N!$ +1(1P,8IS'Vh852crEYkB0)hB(ZF4Tb,SRPAi5CNe8N!"jh&!@QN+V!-"8lREclHa +U1@mb6!@&T*5MDrL`rVPSl&#lNBYIb!9S)#9J[@L!r5$#E5h[,I[5`S2aEj*I[K[ +Ck2KcrCdH6q)#PbdZ9PlP2r+SYdKKKK6DG`L'p0'fLXV9L(RqCG"`CIj63PCUZGL +rI0ia[caIb(Rd4TTb-)PS("1mPC[jcJEUA2*"99Z(LJ2K6k,UYHb9-BdY4+9)R6E +C-IEdiREl1h2J6EQF!QB5jB52E&rmbXZ*FVidVY"mTKV%mYac#!E*r13!XMLhJ40 +JHmk2rd4V!kTJTEIl4X5*FiJPBS-AYYe1YH03cHF*m[FTpT(EIB1-#6X08,3b0&d +[YKA&mlNiXDfNRd+fSlr-m+m64E(-eqK+JkBiL#d@Z&,8D"#A@&)M42qrBqR# +ee$j2DAT%,bf)IqU-MRJ1*'0'LYY6cE(lCJA2JYP)&R*+qr"Tmc32L@hEMfJfmbG +[Z0SJC3DHLaNSqlh!bf9EQJqU(kf1RiA%$1++F@5,S!KC@b,NXe*B*#IcSV5VCUF +0+5dMLeH%er@-'V4CH-Tj*ak@HZ98MBZFB)3J!D)eDr5d"qpffpm)"QDd#1LJPk` +qTYk*pi!dSI%J!B0%H6(fCZGP2Np0HicFd+G*%41XV'X5JQ@`pYNh'Q[FJ[R-Vl6 +d1K0-$MM3H958i"fle8b`AUj2G"DYceiLAr98(jRkh0BGY1G3P[r*c1emqRqFYmh +hd3ChD#m,rM0HYE,*icdiL(SUHZa&qm!0MlDKS5ELif44GPAGfpEc[kh9k`F2p$- +TFjBaU,D8ETkSF,hqQ$P4XEU88236KM*k*I)1am8S`24qeb[63Kld'3BB$m'DP,K +G*-PUV!)VP%[LM&5D3,EphQS(r(UkIbPTeEqG`134R`Lc8JfmDSE,bhU*+34ZNEp +![2@0H-a#[1%LFJVkf!J(QN,"P[XX4!`mlI2e$'R*Kej$i+9Rdhi4)kSBc+F$YVD +95AdZL,mJa`ijS!j#)2pmL[lCIm%2QiGbMK1GIBl`lf-d[%(e8NlIaIVNG2XY9I` +r6&9X2kEmU$k,$AJ0`-c!bIED2QYCr*CT6,5BQkl8dB8P-&lpU6lBYEhiH[[5FL2 +AhaKIMhj1DdmF&+8G1rFaHFd[M,4KK68YSHrJdf@j42``r84Nfq0k"TrH$eJk1RR +F`6$3@a9@1eA`AhXl"U&MlJNPcelaib!!X+ZG9NkGi*LYqaq9L(rf2(YFKhDcamE +3pd)Z&[5PYH!3T0P5E+E1S8N4kE8cMMXD4Feja#@VGNC#LK53!'Sbp[R1KXTrY*H +(aiHj8q[FhkmCBFBCRCbGQP,EBq6IU(3dcTeh#R!1j3BMK@PMKBI$eB3H`1("B1T +$Zq)V`GN&RjNC8@)269$ade5JTJ#c*6IhkATMT9[,Y@irJr0i2bZ0h#T02FQ42+e +rF*!!P606R,I`32"*IkhmjB6j89Z%r#e24Bm#NB8i3I4YX2DU65`JK#[j49m!Tb' +#C#,KJAJL41i8SIrlX6LbMMjeaIQAf-DTI*c"2GEG1`(lXI(elDV$mYV28VcBEcZ +Nd@-cadT!3"rHF*DA)ThbDP6ahja&V90&'NbQ0U*(M1AMYa6r#ZVeFc1lj#TME$S +[TYK!iU1Rmr"3[EJE62ECb0VEBHLCr3[@!9NND,U@-CCF,FIp$XL421qf&Z"Q1Za +3(hK2-@Ki%XM"$SRQk%mf(Gic6D*-l&8HRc"J6jir,dZ8TNi8V)ipheIS*`513h& +Zr4)b9f06&-QE`,MB3h$(r-lGIrdSKaeGYiQB5mbYbhiLLKZL$bXC%M*kN9mPpXZ +2U[D+A[,GBHkP4[Ai+h,841fQ1hPVHPrlHpjFUaDfeAFH0Nqr6k"6Q@TTaY0U%'P +i$&#)%B%!4(*d#"rMUDY#KSBaXM4ZbIrea!pj8r[`ih5eGh(EZB*(%Ecj+`+LU4e +@pbdD(A,IrfDC'6jFAUhP`Dk''bq"`0@3!2'UT2%B%j!!Lh`MACF'S$(I'*1P!F2 +$Qr)*8MlS"(Vl-[dX8M+$"BZf5e6)DKiZcHrD)i42j,RN'bl8QjcA[`b6effTJpr +GdpDD9R"Z5[Y0"a6$FUTQ8f,BN!#,YV1hCQ$eTDEMACAX8`XSJdB$266P%2J8p3U +cJJJh(EVf+9(Nh*4J-99eU4d6*Ya"'FE10miEXZNI#eD$rfM!R&DLVNGmr#@$[X' +XCP#`p[rT#`rS'6ZMT4aj`rhEeVkV95#3!)FHK"V4T3VL!kMGR1!+XD)%*,3#Qp( +cqN`hXp,0DrCG8R5ffF,GG``r%!1%4H(-lY[S%HFb%0(ckcEH$mD9VPcm2K3X43j +,56qmZpKf')D8XQ68aKq+haZS'Kp%S&pj+ep51Rfp1S2$b`@C[Qp%%Tf0P6e-GHL +IYL6h,$5*1kY*[I44cL5Q'CR03dZ'UZaEk6"El@Ld8[im[ihdkQ82NYce'ddY2M3 +DrcPh[RZ@0[LdhZkId(X8U5`THChbi5UHTNrPb`Y'FSp2!Ufd0)TE@LmVG'#mQSY +cHM&f&jHiBa3h%qNDcAQM#lC#D@1KY$RdSpIEe!UE@U[5Db9Ypa3B#6(2ICPbED9 +Z+m[mp1$K[B2-MT!!rhp0Z&qA&4p1XC0r6e4DajG1&DfFirY-("LN"0&AQQ*'YIN +09SHhf&a[R)`l9*Rqd*,j&+3@Nh[i+(PfIF+i02`,"aGI*XBl-4h-CAS%3EUq#Fq +EH-q`AJaKEK[D#1m6k1C2eQ&Req1e0RYS4S&BbeSHYr$H`1e82'Bm1d!B+BM2Mh, +UU#GkPS-HZAUrFR2HJ3F0#EKaT8S)Z2K!-T!!1ZV+j%3$&r02,+JE$QfGlpEVQjA +!RM[E*r"fa`Ma@%,XY!c636A5@4M(61-HI9,(eYBMIc4NYr&qE'3aA!dNkhrB`I- +"&lYPqBUGE&'@CJFMMCm6+N'AdRiCqf0(Krc%MTNE`@0Q5B[JYf6E,%+kapcN6(p +CXdKKriRBc[YkKS'QYRXqr4cF(C6kh53M@-D3!$RGU41"FdmDd$j*L$K)%#6N[Fe +E333L$$djTVZ!XSVRIA89TYA#fhd044KCiDfU#!X4*UNR!FEU5jZT1P$%&J+`[&F +&[G4E0mc$6X8LDDD45i9a+,Z#F!bq+qDV%(1(31TVq41R-QD9rQYZE6TZ4'X"['G +pZXe"pkJd0@Q'rVA!)XSh#pp*Ye&))3eh94+L$qN%''l3PAbX8HjKCV5c0kj$"-U +%&&3,k)[@'k#90L$i%M3P,,2Y0HCUMPT$'@l@mVQ0mUQJb3F)h4cHVP#iZ%S(JFp +V+%HCD6))+jTYkR4dNbaQa3k,Uq`!l@fc1[HI1j0lS03R!8CN4aC2$RCG8ZeJmUh ++(-SICRFCmdES,@),Y6YIeA!)Sl*#@cJ-#[6G,TP2L#fDE[Ua$Pe'Vk56)!#K(@- +rLbcV8*!!`bDU"A$QG!jYc1hkYL)!4M"*aETbIlfdKjkT9h!4X)2E)FY'!2PUY2k +M2+hpq8$Z891lB2dF6S[#63P#Si'4%Cqb,9r1$&cXLkd9'iGU[B[cp%`V#AJc[89 +NN!!m#P'D3[Q3!$LdXJ&aB`-,DTKh6!C@,fh%Ulfb!bEXDL$`,,V1F1B6K4)d+B+ +bi,8TlchY-4JfJ$1!X8pURN'P'mIl(+j+HX8!GRdp@B!,UR!I@k$M`10kG,fV1HY +`KiL!DqS%8C(%3+CdJ(cm16ErKkNPGTJfeFEjHBjUNiK"p[DNG#(I055!bQc#`Y& +TY`RkMqHBKPS+a'8%9pcbjH0TMK%R8jkFLJXUS5Q-d+$'+XhYF42dmc8Tl",M#IV +`kC&-BmJ(PKjj!4Ed'q3B&HhV$liq&6J6Bb%(j3R3I3L0DLJ1dr1*[I60-+m!Aba +1h#K4UmjR@l@P!(Ai#JVKVB&3+,S9ME6k0P#miH'ARGH'`pilLI9&&+jIE45'"9! +U&r$$[4i+NqT%5IT"bT291cC1Rj!!cFqBLUAP)e9)Efm09Yh[*'NK#V4T6mTYNH[ +Kid0RR[P%K`@bmM'fPkk9#DRNB[DA00-iBjKH4Y3&BE44,0PN)ZmJ(fZe$#G`d[9 +j,I&i$"rm10H6Vak!-RF4b5#YhX"-eE)e6!UE54&jH8FHFj%Em$9Mj)`V,(8cEkG +6Q!8)"YIlEGPX30Y#L)020K*YQ'XI[`K!i-Yekpm'Em9Bpb&p1XlV1cfXkc08cD# +8Dqh3GKE5U3SY5U`DZF988BZ2,8$P[hITmA)M)qpV()%AC@8R+@bjFmQD"4dEA!K +6N!#)"GNcPQ!Ea65X6Jh[kpDF&jM9Z5NL46qp-I!9BDm%*pG'L!i4#[@&l1k$&`! +5,E4meC)KXI1*[L#SqVlXSGq08NYf6j[*(GZa[@m'h0(KiRG*j1jB!Q8hkp0f8TU +16BeFq4lJ#-,Fj4I$,-6C)Gq#P*Y#PrCCB[qIF(M(4Gk*L'$Dj"Kk&h%F-,TZQTG +6+TQA2lH[@*80DAIQDc@5mQf[X8*R8SjU*SR(MGE0(IT+22XjRYHRYZhFFj6VJ*3 +XSMhKj6J+0hB!J6A4RPfa,dLAbh!)JeRmY*f$YZTQY(rQ"#SDeKP%rr3UNXCf1Nj +l)R%(B9ThBEae3aD%9BU@$JemikpMlN!,[epU5L%NH4M+KQV#!(*["`T5MN4H,`E +6Eh)YKhQmS+QP0PLREIi)(GSbjC!!+$hS*kC5i&E*b[m9,3TX0BM!JD#X+Ta!b8$ +$%#j0'Amlpa@M1mK`3LD+JUdDUmVl#EIk'9iB5!p+PZ1)NDBk[,FL*5)+cFRlbdL +@*AIG*D!i("%hZD[)2FmXeeQcFVG2T98`D(ih*[Jq3mkSTmS)RYU&%VS'`6flY@% +plfcZXGMKU+%kmR1J@0P0p!&pa%$4KMY@EHYkBh[+HD[5pB9-+NQhk,jTLrqIFB# +8%446'GeqjG*'c$6BJSq)MEV[i*Nh1hQ9`E)%QeT0&hkp-q**#C3'6MG5SU!!qDH +PFh!$*)(q#N2i1!`HfKZQ+QV1AmASjR"$jJ0*l51X98J1V-,`I4DMJjV$T"N+$U' +4Z'KGKV@+r%,'IKJTbf"-##"q95$l'%Pk[l@[1p*d&2,XXmVP5Rp8QS'`XS6Ae%4 +FiYEY!jPTN!"ZA,*FdMpY&(m*KdJf[iKEY`'IJidPC-"Di9G3fVc[eED[J`CBI#R +Q-eq5rj`)MR16)J(m0DQUc&k([45p!-TBr9cB!XCV#I4bZNqITYaZ%"-5PQSZF1L +G1f+E'*+P$%PqTcT+i(T"PGj-eI@*h+fNGjk+X,+`ji)#VVSVp%"Q4`Fa$FN-++k +BNiq-,Dfhp[VZfbX(dS[l*X([V!fjc4Ha-USb*AXm[GX!G$Z8L-+-ihMXQ,)[!kb ++FL-@)fkUR"3[Pl0Q@405Z$mkIb@Da+cmm5dKRrk0U"l-)EEG6V#6%,aYq8jUEd1 +c2[6d$QS)24!e6I'rZaf-#QfVUmeB@Pb'FKGcDIa03@lM8eX[G5I4RZ%-TUUKr$r +8Na#DlUd@3qfmBKl$r4e+jdmfa`0"8$k[eGhl5IUB#2a%U-qGbNd)I38[)cPLk&G +'Xi3YK*6-2d5i&XrGR#9S3b(1aH!cRr#`dTdTlp@--c)*pR!iI'421*)f*0*`0Ap +J@`qUT,M[MR2kiRdc+"l(QL0iA[caYbi$bKq29RILaq@A'&rJ2IH2`Zp&c9YUZ(k +j+a+bFT&fE-KrE9'KBVPS'CE2PlXQM!*ErXU60["D*Kb9e98Y4G,rPCX"Ce)%&m% +q1pjr@-0PK8jSY-PQ-6NF"M1E%-D%4VUBZ4b4!`L8#5A9&BjG18&1brMRcVY8e[Y +q-U,f`p@I,A-rLac)Yrk*JTAQVB#%iIleX!fUlYBQmBm3Xb#`'+L0+2Vdb2""f6d +"QSBQ)Si%d8`Y228*LaE-rDrrj2-!'iKG,-6r`8BZqT&23*Z)S9h1$rAMX1$h2eQ +MHMBD'I)#N6Hk4*S[kZ@`**Bla9YHG94k5"d9P-lU$KAK20TV6T!!!JC1h5Dd@M* +je!0pAM0A`"frX[9K6VpI"A3q"G)XbmmDLX2B0ZP#6qX+Uk*U6jdlLGU`ERB4rkd +IDedYh8-4VfjT*mj`lDbr#FFjm0h!L#9Pci2lZ%pd%!39fST,em"fMRTbqNK8T9+ +"6Y,N*KU5HeX013hY`$aimV[BMkDrBXSkNFeBc(#NIU$a%rNGr!M+kF&X[DCThXX +SHH(c%hD#*EbXeLDlpa"3@G0QDAam'08Z5Jf@h(G#jR-"1D@0#9+[C0iVQe"''Qp +#G@kT4%fc*h(ibjAY`#$qerB%9T!!hbF,cal4,V'U%dCL5V`kbD'CLYZN0kmJ$V, +'r44IIZMbG1KhRMHV&dTUkJK'@X-dS")I,#BYL*!!HmL20US*+3KD"`-V$BDlp#E +2Qrpj%TVD"&0m&FQ*k'P3b8UBU&*Q$FRG)"9)rP&iApDf0SZfqXmJl+Iiij+PDD* +rPTlVL$T`@G(AP+RAX6T)26bZM$40l*Q8-l,0elkhelp&TT3GaG+&9PX9%X89Z%D +fpeSYHpXl0mb%"aiFL"A-Eb`qp3er9"!!)hFikGqKKI*(*(H3!*R[M%pqP5pIPcP +8cX9!*6*rpR%hECB`d(J`T*cbNRB[[1A@TTP`lFQc#Ej"eVV3#*`$,jD*T4m08(m +V5Z8p&@rH"hUZJ*5li!S6$mH803Q$ERJi6iDR`%$Y9FqV,36f3E*9,K(pC+-i+'( +1#*-*!(5cCIk'eV$V1l'1b5B&mQ![9iGIk"&4V%-Z'ZB(ieSGJN*"S3,$qd'A8!S +(Ek#5KQrpG9BN$)FJYcFVkCa!M2550)&p&p)p'lc!F9Np2`N98SM$P#IbJc%cmR1 +`mTY%XlE%m@N3,BBB#a'PEk&((%4HRVVPk%Im,F5Kfi9m%D'EL)m[(ahjXRR@aT` +Y62eJ"BmPHX2qM`Li!CcESF@$eF$1DqHUjB$UheM1-FIlA+CidPQ,Hi&pq"PP*E1 +GJ0JH#4%,j3&Jd6[@6k"+PVkIaA9Shb&&eEI@i[rMYHefN!$qqph2,$PXBE+QlU$ +FD[&D1A5S2a(b'!F%*ZM86NQXG%@p3eG8dj`Xj-Z!6c88qDmR2hEG2('UmG-6Xc6 +HEclFTiL)bk@%S`Pf!TcjF'-ECaY1ISa#[QUhXj9GX)dR,mRrY-E%kpq*NjZ-See +K'F8NJZA#HlA+mNMX52(b-S-a@Y5$J3q3!2VUTF0ai!FpN!#G'JPFKXL4YU0icm6 +$@L`GU*k'MLhL3r6q4A`,#1dPE$kVZ(bDpBHP9DPch%X4@9p!Tp1kd3!99+&AX6e +64p2)dM+#NeJaBd3-2eh1J6r)f,bL*`N@$4(MFirKd!`qPSI8JME`IPEMirVF1UV +3Q`!Be)[-2D1ea-6deXT2AZmUe+rCESGBCk0P6Hl92V)EF5FC&mU2&!'h#E))l6- +ecRL8B$RL%C1Sp-[9X-IQR1X4Fi1Z8e6pd#9iXDLeAL9Z8hLTB8R"H[+X(`p"PCj +%)Crp,f(A"YP#pBQ9K`(43Up0E!,8Ym*BKAeE0$8"rY,0Aj!!#Kp#5%JU,ZK$5Gj +A[3N4``VQjkmJ-+C2!'MXSBKHbEU+MpRh9I"k(5$MM)aa"8ba)`1I5i"130!hm-c +rQrQClRJ-EDJD)331F"AHr(k4P(1hJh02BrNh-B%62Jd8bXXcQjpTI2*c49$NZP& +P(8FREhlF8HTZ2JAN@E#B5eG%SU,q)p6qqa"BU)$aqPfU5BXIiVe9K%NeYadfUN, +2ceGpIJSiT-#R,HD%pJTTG9E8Pa5Sb4if'%dVlq-JT0$"AKX($P`kYCJ!154($61 +AJRDJMIU!pP#+'rAVkPDK,224f5S"kYi##XL@RU*e$3j2F80*SI(rjb)`LcTbVG1 +HQr,'H`$LSbeh6GpJ98cH!R**!F(TUcSmpkV'9Vk$aGQNXh&U"ek,&&e*!)%,SRd +#`U&b@a$J28CH(GPU%!T,D6HUCBYr&CR*P)rKb!`NR)$B+%fRQ)Fe658RFLIR8Y9 +FVFSZFC,2ZbHMXIb2rS2#LETKc(6jmc1eZ+XB,&GIJF&Qlmk(a6X'hBk&PDA,24" +UhMPA@aiJiF%FKBPI[(lFK2-66-Aj!82N4NYk5!kq@TDM59Y[fjm*Q%F*Pf4K9kc +fB,1"rDKCY3k+H9mSqhSp*hG3'TpK2"ULCUdCJ5qq*Ta'!c0EfH3Hj,LelZMa%@- +*4*!!LlVlEhMQQlR`D4%""e[MG(MHfDNhBG5S1(jY5C&dqe%YMTc%*&#PJ`*FJZ+ +`MK2((l5Z'[94d)LLX1G&@PjR%AHhjMh@H"9A'Tr+2FbkVV+deM08p6RE8Sl$62q +"+B@02AKH"6RMhdf(cG-hA#KD@c-IjBY[Vkj9f6+Y8`bV8(QNMS58d%fD1$jjC"r +!TRACafmrA[)G8Q9jSb,D`Bp$f#YbUKAELT'K,dUch)EF1,N3%XKXPZR,rm-D@Pe +Q(qla9d"N$D!&Bb#!Y0*8kc-8+Hf)1`%5%aKcP9'(ClXqKViUq)r9RkH9f@FY1!R +XfRRDU)A&RdmZLVp525#FYjYp%Tk1e65ESF*JhVIlC[[J&0LJKlbfB!mEK3%lFbe +Z`3&P@$hfERG6bVRN)@E2)@@HmXB)rmX9mim&8$aRk,0iBEM6[1TA8hCpV"PGbIe +Y`%(S"VfcUV`&S'qQ((HMT""-bqPe[YcUNjj8ZUCA5CKHSRVI5+"%i,!SQBQ$ff+ +lP6&cN!#dKbcMBebi-TXIa5hKUBEB`p3#$J`J"$rE[Ibj5K&p8!FrX-IFQp(SM4% +5aPrE1j,K$!R&cp+&[H`LGYRp[Tp&hkFK#lj30+-)F3+&0d211[j+dl+T`2*2&HN +6X$VhK-GppdS-aHIlK+*Rl@`BKk!,k2P2NA*UL&3D%VZld$SSCK&VF6$)a2IUa,H +`-N#DZh1QJKI1!bc-ilPX"0E4Z1R[GK`4Z',!6m[B"9qh$5Pl$%@f0B@V63PNjN2 +CXqe8bVQRXd35PS3a5-a,I2Na`T'#3!i))Bp0%43!TC!%!3!!-!"!!*!*!3l0!*! +'!6!!!)0Rrj!%!*!+TC!%!3!!03!!Y[&1mlEa6[-!!!%`!!%YG!#3!p3!"E`U!*! +15@0[EJd!!GZmD@0[ENe"3e0"!*!2J!#3#3(Q!*!$J!#3"!m!3X(8iHr%)(JHNZX +l)T0$PMe1&X9%h,U"-`j,RCS+U(NFRSIR`'$MA4ifZ1eNp(bc"k[8Tf29`V5Ebi- +dlUp1aif&(6j6c4PRTLP1eK5a-h2EPVY&cfClKmZkIGS2aXQ*%PIPjC5M%Hph@9& +a(ZfDKkIUBkh$)JJi(L3&)ZG6@!#PN!3"!!!`!%!!N!N",)d!N!I8!!#,Z2q3"!# +3#U@3"!%!!$8!!,GD)G+h@L(6!*!$e!!",Si!N!0b!!@JdJ#3$NPMEfi0!!(EMfP +MEfj0380633#3$i!!N!N"jJ#3!i-!N!32!%,"e(hUjNKc6hS*X&!ZqFG%dkqC`#& +3,a$2e2#THbFLNpi5*Z4VJD@If`"I'V#EIfh'MSlG""q1a88iE&-14)Qqr-Mh6Z) +ZSeCSpTee"5pRNpe,5q3Re3-HYimLk883BP`hF8paMJYi,IjQFS4aSC!!3[jdX&9 +S8p#SmYla(hQ@e-dU3+@3"!%!!$!!3!#3#3%YT!#3"h)!!&"[rj!%!*!+h0)!!!% +!!!'253!"MNN!!!4X!*$cI!!"!*!&D3"M!(d!R`3#6dX!N!Fp!'!!miKF9'KPFQ8 +JDA-JEQpd)'9ZEh9RD#"bEfpY)'pZ)0*H-0-JG'mJBfpZG'PZG@8J9@j6G(9QCQP +ZCbiJ)%&Z)'&NC'PdD@pZB@`JAM%JBRPdCA-JBA*P)'jPC@4PC#i!N!05!!%!N!9 +Y!'B!J3#L"!*25`#3"33!5!"R!31)-P0[FR*j,#"LGA3JB5"NDA0V)(*PE'&dC@3 +JCA*bEh)J+&i`+5"SBA-JEf0MGA*bC@3Z!*!$6!!#!*!&-3"R!%8!V33%8A9TG!# +3"3S!8!!F!4#)'P9Z8h4eCQCTEQFJGf&c)(0eBf0PFh0QG@`K!*!&#!!1!#J!,U! +#!!%!N!0p384$8J-!!(i08`U6K!'ME3$X#h)$Y,)b+b[M@dhH@qpUpkCZ*YH!-3" +!!`#3!lUe$)!!#@NUrZ!"94)XqdV)@`lMjA1kK9'1XMr2MrqZ)$NhV"Vi%FU'0AQ +'BU0RDr#XAMm&lZ`,`,#T"L)i6&Fq[H[,VD-C!m8F@8XE1!X!N!0D!!%!N!9G!(! +!F3#X"!*25`#3"dS!93%6L$T6Eh*bH5iJ)%PZFh4KE'aKG'P[EL"MB@iJEfjXH5" +LC5"`CA*QEh*YC@3JEfiJ5%C6)(C[E(9YCA-Z!*!$EJ!"!*!&D!"k!(`!YJ3#6dX +!N!G)!&i"*BK18fpYC5"TG'9YFb"hCA*P)(0VDA"`C@3JBQ9MBA9cC5"dD'9j)'& +bC5"ZEh3JFh9`F'pbG'9N)'*j)(4SDA-JFf9XCLePH(4bB@0dEh)Z!*!$@J!"!*! +&A3"`!(%!V!3#6dX!N!G+!&8"%iJk9'KP)'CTE'8JdPi`db"YBANJBQ8JC'&YB@G +PC#iJ)&"XC@&cC5"eFf8JDA3JGfPdD#"MBA9dD@pZ,J#3!bJ!!3#3"D3!M3#i!0% +%#%0[ER4TER9P!*!*RJ&H`!)$k!#3!p4"4%05!`!"%Je6#TXN!$Z+L)S9caE3Fka +%E"$e,$pr2qcARErRlXi-TeMBB58U@)999@,P[r%%XDS&#l*P1diJqC!!(`&8**M +k0Eb&Tph&fGe0dXKkNVep(bj$h-@Aak8,&[Q01&G2PI8,*$a+MT*"[ZKdYI"dDK@ +D)Mi&jNl(,(@,TA1"CHpm&"bi0FV-TR9!6`FK$%aAP&QFVF'lCA-L&paq$(JIm$a +!SNrM'Ub)p-`20hNS80Z-b('VTjc&BeY4ZFc0eZQ"Uj3hhmRl$1Rr92r*E3#3"$S +!!3#3"9!!@3"N!*-%!Np,!*!&!`"%!%J!k)JC9'KTFb"KFQ0SDACP)'Pc)'4KE@& +RC@3Z)!#3"%J!!3#3"8F!@J"E!*3%!Np,!*!&!J"&!$%!k)JR@@pe)'KKGQ8JC@j +dCA*PC#"KEL"TEQ0[FR*PBh3JF'&cFhG[FQ3Z!*!%$!!S!#J!YJ%F"!&993#3!`` +!)!!)!+)"(!##998!N!--!')!NJ$`!CJ!K999!*!$$!!S!#J!G3%m!)G993#3!`` +!4J#Q!,S"eJ#'998!N!--!#!!#!#L!4`!J&99!*!$$!!S!#J!VJ&1!,9993#3!`i +!+!!S!-)"T!#)998S#J#3!``!+!!S!*3"%J)!998!N!--!#J!+!#0!4F#!999!*! +$$!Y9EP0dG@CQ)'&c1J#3!`J()'C[E'4PFJ!!"$0"4%05!`!'G`e9$8-L%K(QAQi +3C#dC4'Vb4#3,%&QVTLBRYcf,M1"fjmK*Yc06mTPrGlr[fiSm'pr-Yl9!NYA1l-R +N5GLq1j-4NZ9@j)QXb1mIN6q6RmQmfGCf%8N@%l)h,FNL+%$L"rp1@BD49%3iU!X +lH1)RGL%XdS$Y8-@K&RB5AKr2MQN-Tdqr@5Tb%b2*lEeNEa2deYr0KTa,b#P((lQ +rdKDbpHCeqFBN#8XTDGMHT"F9Nj*A@5m3r1$*iF)A(Ra+`bCSd@*%bYh0[UE$mLb +8Z8NZL1FKb4cpaH#,'S2Z2"A0G593mh5B(ilNbH!U(HDq*03V2L&LN!#Glm-GLj) +350pE&JffVadV0j9c-)PcYTmmT-U'cCf2[lLr"Zb,Dr,j*UQJ&eL!@HMeXIT'U5K +XK+Zi)G(%4'NH4P'P*SBS*l2%Ke)*Td69NE&*%bSLi2'$"P$iBJ#e%QfCd)!P0"& +UFSY0QF0BUD3IT`bXGDL,fZ$B!U8fSh08@--BYZrp*)$#,J2%1@+BZ'k&r-qa*h( +XSZeGPi*0bi'TkX2@JbVR0)ahG0E&J(3EJ`[@YV@,-D*-cb(Q-Y`6mPcZfjMcBMp +"cGLljK3Rb&aVTMai@-PP[RfUT+62k1U0klXYd-Kcq8@f`RZ!4-@X%K,E)89)*H! +)2HGQk+!L4L(61rDKE[-93T%lT4&h#Yj6*Gq@)$NpfZfT-bIQ[*a$ZHi9U@$-fpT +GbR)EqIHh6JN-ELhIr*F2iT-R9S%)rHb*!9X*2'JQcVG5aT+bk)"66"T!8Hh@RKi +MNZDb1RCPrpLBBZE'f'*+5Z1k(+rRjMiNk%bc`$2Y2dPbk)d[lP"05[[rh(fm5+9 +i5KpCGN!BDPI(P8iGH))aB&$'@AMak[HhZkICP"IAk`20U"30ED$cl3ANPB5bhE" +dKJq'UT!!'MJ$K%Ef[#TaY#Qi5[JiZdp@*Ek(V6S2"5LGMGP9XcSTH`kX!)Q$@e` +Lj!Hc!YeF"G@qBdCpV(kpX(EYJl32qi0AAhd*'kUV,i&`%M,25lL3!)9HJTA-Hq- +JNiRVb1C`%N0V@&,9LJXedG30VZTedYBKmQ8[pD@$C2LX$T!!US[$LD0[BBPS9ak +e"Bi4i"NpQ[B5,S$!l!aZkDY"[CV-MSM(ZdcHrYqi$lHRmXQ0Uj1Q&hdZmV+6dc$ +Q@CReZ"0&fVV&S"Ua`C3d8JpM+h9[1peJ#QP[H2ESABrbfDQ9EXl,)rR"Y(aV,(D +K,&e(qXHCLJYTG`ceq9R+jqIUrhj)D3VcIeGqcX4Q-IlfpqABR&9R,NpTEKEkTP6 +FE&CkmZ##0U6Epj,8cI&CH25LN[RmcjI[Q)F-e2@eAFBV*qph(["`34I,9E@$fDI +YZ+ZVJh8&%Tl"cXY,fjl%2ZKNB,XDjI@RH3*a"UE`8,`*ALRF`jGi-!Gh[Rked&R +$@pIN$J#3!aJ!0!#3!r-"AJ!"!3!"!*!&!qJ!N!18!*!$2!!&"#"[CL!()'PdC@e +c,J46G'p`'dPdC@ec)(*PE@&TEQPZCb"dEb"9EP0dG@CQ1JY9EP0dG@CQD@jR1J! +!"b*"4%05!`!-3`jG#kZ#*$lU%1prGjb%8-8Ke%4U9$M4)k"mV`1cHE"X35+++e) +@+mTLVcDH@@K*2-mfY,@Gq([e)E3@F+Y3`CU6#q4l%rB@IfqViqmTMVm(#i0NeC) +kXl[86Qa45-+m*-Y)P`SKT2Fr)CeBA(YZrId!$HEQ0$%"*$3M04p9j!kQI8Mr1XJ +2KmNe8STE4UDQIi,S@Rhe[I@qarqjqdZ#CYHDLRYFmcXRh'Fqe)[)rKRQcr8Z)jY +$mqMAB4Z*`8GNQ$3LNF,eb%X++LNJ[Q-))Z%`N@8J`NC%+3H8XKIX1QSI*qQMdQD +IpGLhEpjV%lmY1l+5fDcpH'C-jhq['[`3jSrETKXX9fHMKlcNER4riBTCqG'#)HT +h9+c"*XN$@Cl+I(h)QjMblcphCF`KE5hCkEmEJ'dKrURKM#aIRB*N3e24C0KHQ)T +l$%Br@CYXcVBTrfSiNkeV5'3-b4AlqD@&QA@GlC-5"V-KqeISpU@)!9VF#TrPGVe +3`$j(GLp+(M+%)bJKr&Sfl6c(iZ1Y!9iHNp3EPSIpCePFTPi6XLqMR56Ea[SbUjG +DGG,V$qpI@A3Hj0jCf4[JHkF,BhCr0'i62[6hH)6PrqmTD$jIpH[T![HreCf6"(c +N`HmpTrP+(EbH[''!b&!IJmY(HX)"[R"cV$$bkT!!BFkfqZ%`*Q+HH1mQpFh)rJ@ +qePkBpb0ELS5`[FqP*d'Lda$-8qj%Tj%SG+&FF'&0$-QD@&h34--"6@3i6E!+&5C +N6k*#4QZTHTiUAUNT`YdPZGKX@*2jFEfX5HRlGNQ6`Y[fJLBRhkjk5j-GMr1P!HB +l[3'Qm)HHJ"HPEDQ!T2NIX`%2j1DEYFhii,)JXp(b1Mbrp)q9Zl25JF%)$&*Jm!+ +$kJjf`(I`)SH&YIF)dciHP@*rMBUa4$IDcXH3!-q&V(Q`BSJadb$'CXSYUpXb)Mq +,Xi0GD)T&6U4-SpFIe6rLf`lAZj!!4!fm'%+q8%hL1133Cp,eVX%T8F2L###cAI$ +mL&00G&-%Cd*2iB%iCf`MfF-jf!FJ1%J4[MVRbX'20Z1CcLbH@N1)6)kDFS+SqQb +1EX+TQJDrTfh#kJ*5aM-q0k+Q%VF[a'CXI#96N@%'546j5+F6eCZ`Y)TX2A,NUk) +B-Ej,R0VL%(mE&PXrq-#p#2e$Mh`2m[mZZU6[&E)M(T+mmZ5*C[*Naii8P6'D6(c +bha3TS[J5ABK&@0UBMc(1MV#HAk%1$dlX0f$d-%mhXhTHI(EKj`HhGF*SlVcm3"f +0R8KeA4MGl##9e5FlJEb)E"IQpI`h[mJ1R[b#0H)%!d,9%Uh1GmIjmI'(P'YJ%Xl +MTk6lU'PlpV!H4GrmYK0%M4LGSRTmK3Gb$BLAff+Tl2CbF&6Y![$@m5BA+)6!`8@ +ee[Q9(MN+4[41XU3cmMk'DQ'S6h&HI"C4IqZm$K%CM(`cNh-UZ['2M!%2")E[(Qe +ki1c3Sd5C@fSUeH@m*6B09&BcS)f1L@L#PQ*+KA6-TD%G95+1T&jmbi%QIa"&$XC +A8%pjZ(#6lN3Mjf1EiJUFd@m[mhpEDRiY`BBack+-$RSC#LEPF![d"pU$S5L'jPl +JNJjCi#!A!`%J-IrFfDFJj'I2C[Bd0UCE2jZlIl&[h22GqrIq+,l2YIaTXma5M'B +r@GG*k@i'TkPPB`@qB46N'fV8i#e-mqY#FQQhm,*pdPaSPhIj$G'iC,9Ub5XUlPr +MUJ4eJF&a@NLbIaHiI1&RqABj*aPfCDA)c9$b&Q1&B+d6")mEj`"hp!LF[E!INQr +i$5f+0$%85TjqUk*%EUa+@q*c!aF,QlbR+TRT&K*AHmBm`H+'b@!Ybe[DD%JNC+Y +K,rT32-h-5[2H&`Vq&@3lX4p3qFD#JN-be"TjPN2HL'*jq3e('@KA62r,S8m@)G* +`Y9bQVCUKQ*,F46fP#De(dpG(,*GddTH@hAh9Cmq89@fhA6q[&HaSDHi-`A&rBc" +SL*VAklaba4[#Y3N[eaKF#TAbf#jhZMBU6rSp`@$*6JDqIde3IqrP2-(&8*'2lGS +HTLKVm%m-dEa@`90(@8HSq#r2kiERdSZ#C9dhN3qf5G9@i9,MEQj4F)61!R+`NF* +EQ#T+d!MBVHi6(K1%BpVk5r4F!%DirSj!pl[YiQ83Vp(a)p)(#6++Y%mhNNJ2e#* +CJ3JADBr0GlNJA$EL+aRZ3114C2lUKK@1Nj`-Cd`A*&$Smk@bIplmk&rjj*%N!V4 +E+8QIVZ9DXkIkqYr-K5K(p'2[p%4qhlC$P5dE9Uqbd&iR9pd`'&Kc0*!!U["1F`I +#QD4#UF6[J,+V(BCCFebM0LLP5'&(NU-8pp2f1U[GXf#3!0bTkLKeI+$XpBE#8R' +ID@5dE+#fMGY9GNqZYN`D+Xf6iI+JTG!R)B'`%6K'3j9)bMeQTe#4CN8`4!JVG%2 +hqIS((dfBili2!*!$'JB!J!#3!`-f,M!28h4eCQC*G#"648%J0Li`!*!$$JB!J!# +3!`-f,M!$0Li`!*!$&3"8!'3!L`''!!%"!*!("%X!N!3B!$`!3!#`!CJ!!3%!N!F +""`#3"J%L384$8J-!!iS08`UE*!"rKN@`h6Nj%l$&$Pe,6NmGf`%9!pZaqYJ-9[A +12LLS@(eX`@kXkXE!2f0r925rrfq2mhC@'FEU!!ZC03eF9Shd`Bj'pkj6'Z`%Xr- +S&0c&iM*#YY5j)-Pc#j!!hfq#GS,Td84dcbPjXa2G[-RZ+i@%-ma,@ZUD8SSG#ci +IQp0r"2"krMRUbY2UD[qIAfl(Ujrp3rrlNCBP!VJcDU1#E9E"5#Dm4DYXM&@eAPX +qBTZhKHeK&AhF&mE5@NbNP,3#F4p-ISc$ekiEjSHQ'HT6frC0h3qk%'KDJ#F%b!# +F!%bpqLd!rZS*!a&r!2#LFZ%#%"b#J!'#BQ!Y1)Z43BcI$%,N%MVLkQ15c,dSF4p +-hh6j4F3VE-XB!*!$Gd&%3e)$!!#!$9-,@b!$!kCJ`kTUXc#`!5*LB$G,XGYCPD! +JBX-HkYR!Q&8a#f-UZ[HR8k`+#iPMb,ELGpB!8LMpiEh!JNUia8#RBdJbMUrCpbB +L$VrTa[llf*mk9dmSTT%&(C'kJKQiSm8DVUKU*k42-JV[4Fi&!*!$6!!#!*!&#!! +d!"S"'iJE8'aPBA0P)'PZFf9bG#"NDA0V)&i`)(GTG'Jk!*!'#`!,!#X!+k!#"%X +!N!8G!$3!,3%BL!*H-3#3!cS!!3#3"6B!K`"+!-%%!Np,!*!&!J"&!#m"2iJCAM! +JBA"`C@&bFb"dEb"LC5"NB@eKCf9N,NX!N!1U384$8J-!!,B0@`Y6-!0hFbeQ"Z` +CdmT9aMFdilke99E'2fp2lp9kYqiprq)E'J!!m!d!!1Jf$3!!#l)Y'i'Pq!GfCr[ +jjDdYFp0@cGpCf*-4E6ZY!bFUCeRCbDlbH0Gh4)AJ8X4rJKJ8[N3-RI0#5DL'!59 +#J#kS$Yl9"F#6K4bJ',6dJeNIl`L5Cd'q)0q+c@'mi[eVN`@PK4)VLVPbh1Hj`Y* +8H1AaB3%!N!--!#J!+!"r!A!%Ve99!!!"!*!$J!!Ird!!)!)J!#)%N!!!*JR)!#) +6j!!L)!)!)N!"!##(i)!K$r"!)K``)#3Cra!S'SS)-M++*#BbmM*10!Bj*QAd-K* +P)Cr`)"($!%!)ri#!"!B"!!)E!J!"!!3!!)!)!!"2N!!!*b!!!"*!!!!!#)!! +!!8!!N!1!!*!(J!!Irm!!2rrJ!$rrm!!rrrJ!2rrm!$rrrJ!rrrm!2rrrJ$rrrm! +rrrrJ2rrrm$rrrrJrrrrm2rrrrRrrN!-rrrrq(rrrr!rrrrJ(rrr`!rrri!(rrm! +!rrq!!(rr!!!rrJ!!(r`!!!ri!!!(m!!!!q!!!!(!!*!$J!#3"`%!"rrq!!J!J`! +*J3+!#N)#3!L%!L!*#!)3#p!$q!JJ!!J)3!!)#)!!#!N!!!J+!!!)$!!!#!J!!!J +)!IJ)#!2m#!J($!J)"Rr)#!DJL!J-S)J)$!')#!d"L!JCI3J)'8F)#"Rr#!JF-!J +)$rJ)#!"J#!J"X!J)!!!)#!!!#!rrrrJ(rri!$rrr!!rrri!2rrr!$rrri!rrrr! +2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ +2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ +2rrri$rrrq!!!!3!(rri!#!#$!!Z"!S!)3J*!#B3#)!K)!K!,N!!$q!JJ!!J)3!! +)#)!!#!N!!!J+!!!)$!!!#!J!!!J)!IJ)#!2m#!J($!J)"Rr)#!DJL!J-S)J)$!' +)#!d"L!JCI3J)'8F)#"Rr#!JF-!J)$rJ)#!"J#!J"X!J)!!!)#!!!#!rrrrJ(rri +!$rrr!!rrri!2rrr!$rrri!rrrr!2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrr +i$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrr +i$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrri$rrrq!!!!3!(rri!#!#$!!Z"!S!+3J* +!#N3#)!T)!K!+8!2i##!!#!K!!!J)J!!)#3!!#!S!!!J-!!!)#!!!#!J"q!J)!r` +)#!F-#!J'ImJ)"U#)#!bJL!J-!BJ)$3')#"Pp#!JC4`J)'Im)#"``#!J2q!J)!'! +)#!'`#!J!!!J)!!!)$rrrq!IrrJ!2rrm!$rrrJ!rrrm!2rrrJ$rrrm!rrrrJ2rrr +i$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrr +i$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrri$rrrq!rrrrJ2rrr +i!!!"!*!$J!!!!8!!!!)J!!!%N!!!!!R)!!!6j!!!)!)!!%!"!!#(i)!"$r"!!K` +`)!3Cra!)'SS)%M++*#BbmM*10!Bj*QAd-K*P)Cr`)"($!%!)ri#!"!B"!!)E +!J!"!!3!!)!)!!"2N!!!*b!!!"*!!!!!#)!!!!8!!N!1!!*!(J!!!!F!!!!2J!!! +(m!!!$rJ!!"rm!!!rrJ!!Irm!!2rrJ!(rrm!$rrrJ"rrrm!rrrrJIrrrm2rrrrRr +rN!-rrrrq(rrrr!rrrrJ(rrr`!rrri!(rrm!!rrq!!(rr!!!rrJ!!(r`!!!ri!!! +(m!!!!q!!!!(!!*!$J!#3#!G"8&"-!*!'"e0PCc)!!3#3"!G6C@Fc!!*r!*!$"e0 +PCdi!!rm!N!-(39"36!#3"KaKGA0d!*!$!8P$6L-!N!@%4P*&4J#3"B3!N!-d399 +c-J#3!`&*3diM!!-!N!1!!!%!J3!#!))!!`#$4P*&4J!$!*!$J!!"!)%!!J##!!- +!J`#3!b!IU5!a16N`,6Ni)%&XB@4ND@iJ8hPcG'9YFb`J5@jM,J#3"eG"4%05!`! +"!3e6!Yc@"T2hdNE0440Y!,6j0bkmfddECX*X[UX,hi6GX0[NhAE9eA9K!!NiTBM +rG!Ak'M5Q0Klla*eVf8k#LE$6%2V!XqJ!D*!!aElq",i'!!!%!*!4J3#3(S%!r`# +3()%!9#[r!*!DJ3"8re3Vr`#3')%!92q3!e3Vr`#3&S%!92q3"93Vr`#3&)%!pID +3!e6fN!3Vr`#3%S%!pID3"2MfN!8Vr`#3%)%!pIEfJC!'9[IfpL[r!*!1J3$ep[E +prj!'r&EfN!-Vr`#3$)%!pIEf9[prpj!%JIrhpT!%+rm!N!U"!2AfN!2mrIG@Ij! +&Uj!$IrEf+rm!N!L"!&6fN!6rIrCr+Rm!N!089(p@+rC8+rm!N!D"!&6rpT!$9[r +iphmUI`#3!e48UrFVp[p8+rm!N!5"!&6rrrD3!rcppeC8+P53"AqVprEfrrp8+rm +!!)%!92q3!e6ip[prpRmUN!989(prprK8rj!$92Mr!!$r+e6rrrEf9[rhphmUJC! +%V&5V9[D3!rrr92Mr!*!%rbY8rrEfr2hf9UXUJID3!i&rrrIfN!2r92Mr!*!'rbY +8p[EprIG@Uk[rN!CrprD3!e6ir`#3#2mVp[C@rRrhN!6rJIH3"2D3!rIir`#3#[m +Vp[C@rIq3"S(fN!Ahq2m!N!cr+rD3"[q"pj!$pT!$prMr!*!1rb[fN!2rrrMrrrM +fN!2hq2m!N"$r+rD3!rIhq2H3!rEhq2m!N",r+rD3"&6fN!2hq2m!N"6r+e6rN!9 +8q2m!N"Er+e6rN!08q2m!N"Mr+e6r92Mr!*!DrbY8q2m!N"crq2m!N"lr!*!a3Ej +"4%05!`#)P""9$@99%3!K9Hj'RckIP9+A1THPQ3[p@HHXJkd919ilR0"ecjZZ,P[ +Y"Nk#VV#Y"GcclQX-A$KHA-'"#b!1j!a($*R6`fd),X3[m6M2QjaRL1F40-C6ipF +JAjmh-ES33mJFfrVqqr`qEAmG!b'AerIr14e@0K)4!J'3""%"N!-43Ji!A32IQ[E +j"hUL-`Km2d5"rJbZdI4iYUQE[`3Tm6XAhiZ&e28rqjHmNSQ(K@lSkfQ#6r`iVi5 +1%R'3!"ppNiMDBVVe&PPM2LpJ(+*"mm&3LHU40Ie0Ta2I!E(6NmKV1Sd&"12cP@Q +J+GX')Y9EJeDr$H6H,`GrpKGRmP13!#bEXSf"1qIEaTUh[$@iaYbd'mYIm@8%bje +mf8Q[*dT#jjHGTGF6*F&@[Y`*SR$2XV1Gr`B3[)iqlT-J)#BF!mrN18-Q*db"*Xq +Iq-$d9ZHrJBj8$b*c9I013I3G00mrlfl2%H2BSK"2V[aQr(XLJ*m3IS+Y18,qdhm +2IfTqeKB%XFVLV!J0LIqCqZ[NN!"M++XipJ#IGMNVq'*Ia'@Zre0pE#iIbXiNQ11 +4SC&2Ba+$fJQ#RIM'!GA82c-"bfc"+$+J6QE-GGQH%Vl1l5")aN45M)YY`T4rCDT +3TH)rIjhk+cA-f04IfF3j$*[aXfAd#Ne&V[$T8Iedj)V%bPk-l4X6hfMqFD3[fLH +#H8,PN@r2lqBNJi0@pqIGL4`4AIQImFqaqFqaqFp&X11(k5,@e[)$GB"[2AT(3"d +`@mceeeYLZT9(L*@rFMQMYKmqBGlUiUmUp-GEIrM%PDe(j4`8,PBaG9H$6N`HFKc ++EC!!qr1l1q*%0'hYYpjL69*@L-TL(@Jkc3ET@%K,bNjT*8+Ta0chAIBL3ZcDBCm +eU4edkBrm4iR3f818(5LU1JkJXC(AY0Z",GUp@%D3!-QBE2[,'f+d'b4Jr)GXZ4, +jimk$3im+-C9HAbM8cpIjYh3HK-S#%2[#D,IjrSdE6ldY2$!)Mcc3&(-QCGFq%-% +lDdBrd"3Rlp8(*jUN!'8I"PX@-MqJFJ0Vl#C(elVC6UrXUXk3!0qXrNM1PM@b[rS +8b2jq'c(081e*Q1rD,,#CSSebGZ)lIp`X[#!lKa,q@25H[Ce!m)r0)U6jkFYc9-m +i%*3SNq61qEJX3iP5fer+bKaGJ3&RU&R&Yd(&F5QbFT,BJ$pkMJ6C$d4"`"Hq[*5 +,FpMPSG@fiFhr)"DKJN',eRi%H8ePa*Uh#2'5&M&XV3-&8hmBJZH9#,CQpr`'&F( +"MV5eAJUqL*41(T!!Ye"%5B'E)JT"'T%IQeS6D6,Z'e1TV-L!N!#4!G&CFk+l0Dr +*+#IY6e*dQqqhLI43jrD%L#AE!S,)fp(1Jkh#E(VrXrD!HHLpqZZ(c#D6UIkcZ8H +PPf&(*V4(!LD6f@3IRi@QRYRPp&Nqf`3KYL!Yj*!!a,5*[c5EfJ-ThlMqk&f@KQF +c[6A24N)[raKECdbGMkT8*4iL`eQ$P+dBX#'Uae4&&M'aa5qU"kT6cAR0KjYbQiU +XL"b9LM)SQPZDEFeh)j2M[jSdeE1UCmPMaT5F9'6)&0kj1&%h5pM(UB+HY$Kpq!R +&r'5FcA0L845jkTK2'QUc$1CEJf[bQMjX["HIDBdR1aGrZaJN[+MT,6$@)C0`ciP +ZBf!#i(Cec9-!YKY2I*UVqRVMT$aQ2b2,qNXG,V[I'+S('8qILIHTUc%#TYb-P&8 +b[5b2R8R2X)Fa"5Sar9hRiZ+06qeG+aV(q'IT8hY9UXE$TVc'[UIfLQLMUA2aVXe +!r$Pc@NKJ-3F(c-JDbPY5KjqUcKNrZ-B@0beZRR[iZG#8,GlRHXV9I,dTVrNQ)SR +JCbj8r%AR$2-e!hXRqU$NqDC&UNI`8if+HE2U4E"8dmqq-9Y&mkm03lqkqGGJDac +)Dbj9,F*N)reSfGL3!0MB%#3*[d%%$Eemq!*q[Y#bIS'Ipl5XmpR3hrcV8r9pBPM +B"TUIT!`9LiA!&@GSIq"8Yh*ML+VVejLE(cG1VA%eEm@f2h3%l5d6$cGrBE69aU$ +JBFFLeH010CN"-ZkRc'NLp-CIJXK+ACF#a4QbNRCC+4X*3"G3++%XAh0HifMcaeD +H05Q5c8p6&U*))ZTf96q9X0!,9202'l*F$-6r,MK*c)mX4C!!RSH#a!'5QR2%pUU +jITZ`V4&HFhh0XEDpILTGIA12a6E4UalpUpF5BZ02k9rT6qq`E$eL`f5Vf(L&rT9 +HZGkb&5$R(DrC!V2S3-KFVh0KiF2"bp[%aU[dVr6UcCDYafcZbJFI1AVcj8fSUZI +fr6fUeEmI&#'EEfi["Vj`JMePK!+)DHXq#fAiQkYEN6f()YP4r9&lT!Y)cTpFLL[ +Rh5C#B`00(jVIfc(30-hmfI*MBelcHmZ2E3YSd6Lbp9K+mZBMK`HDlMCr(fac`IE +DBEIjqmYIk`PXr)$qPAl`q*'YVmdk"P6kj-J03!bIXd#YE'i[Rr"aC2T(fq6)2Te +SDDiHb1ECpiA9$XS1Skbli4S!GJ10NdkG,N,CeL)*iY9!I8PmQdMZfbD3!$d0%M) +b5#R#'QqAXf1kpKE''#4'H3JjIdQ)$f0Yr[rPl(BcYYdKhfj3%,)NNi654&YNY,* +kb-$-+&LX,848PKd`eb3+B1e"Dj!!MU!UQG&UTL@Kd(aX9T`ZX3AEiq8bAe8`[hB +Gc#F9c)G@CeBp3`EHG%CKjXr`c14F9A(car5aMUN+9B90c5KiJb,Bp%eT8UL&ZUN +"L`$!d'rUK9('+*'-@mRF35i'10l5,Tkf109'rh5,A8De@8'C0mlKRpmR3RBHr2C +jLG#YfUrTMZ8EJp04A"[GB'k$Se*)bM*Y&bQh)%[&4krYQkcVpRNCQS@%D[2(&2@ +b*US$0Ia@*jVS3QfVNX3@ZSb"%pdaRRdEp5M93eQAD#KAQ2p"$NET'[G*QqfTN!! +%8(D++2lTk'G0T2(m'P5JM9Pb8Q14A@XdMFBe!0XEdf2Ym4ia!X80Bc&P68*p&A, +HiQ%dCR4L&-fjaF8k4,ID-E$N8AY#$MSpRdY9N6T@J8,%L*rcB*)eh36+0cV$)K" +439-5-+XYR+&,8j%E%La6D"Pb#TKki0$Q80mc"`(A@+-6'0&FQKqMkV2YJU(l +$iA'C-Uj"dNrLF!81Z0%mAIcSq9Q#60-q5ch3I(1+'P'NYJidPe,60H#I*D$%m92 +G8+UEXQaq8@@,+iBMXq+f)$+P&0RMFClj$NHPj*l!hX"6JmdhVHPV,ReU#!Ac`dY ++8$$2j8Aa@Pi8#c&2HqhT$P`,T#MEIVV$FU,EZ9FDLP*8BL9mX2K4)l+$[$3"0ip +LMSaKiV)YcT9Ilk!YUCe()i0ld%*3l3eMQeRTf1kPF@5Qj1pcdp%p)M)ifSe$CPZ +CZ4i!5V&#JAaVT-!d`4#Yi)LD'++$P4!GD%b`5cVCTGUT9cIDiPe#3TH3!,9j`jd +-aX95TNDUYNPPY922,@iZYEI-b'Yqh"iP!)"8f[a&p5qXAN,qd65+,+H(,X+959Y +FqF+q@AQ"P8c!r`T-#0R4MA!dJN'*6E,$q%TY6''+kb4'Q*ZDf16EUcpkC9,)+2! +3`#U+AmN@VEm!)Q*!%[$+AZ2aJeQL#i)5*+Kp09'mQ*e#F2%daGZcjAA1VR)%9h2 +"ZH*(@X'%+"-,jJiiThbT&%qQhDV2V1VKJCXUp3!82YBre2QmPCQVDMp9p64p)69 +VNhBkKZNJQql56MX`lC@QcaK%E-+dQddE4+c%Y)p0'd43"QD9)A!3H,[rCUNa$l* +P)CBI[$8)%Q-KJ(ebij!!Df1ca,iK-F8"N!"d!1kl#akJkPFCE'A(8'N"XTl9,4& +,EFpH!I!VLTm@%2L'*dS,(S"Sf6J&!NNV`#""!-mXiI6CJM&!(A*2`@BU9YA#$QF +"6(AK5#'1m1-JMYP-ZDTaIR#YiQ!,J%"CC3&"Ta9A%@Li!!GUHA9l'4YYIbHlS-5 +4@e!#0p'Ya-,%,S#iC5$'e82NCH1lY6&Fm3NQUP$dp+LUT1-&+HDd!"TFqkEM5"d +jQNQ&#hc2!NdMlf3A&G+bqCZ1h+*#bPJ#-6-0lQ4MTXkqYUCE`&$d$VP[(3"j29" +8P#F#VXrCDf4,(B#M@MB%lq@ZK-1S,2DL'BP2UfBJSUU+0T*j#,rGSh0S,YZf%EM +e4&e+mSl,GeL3!&@q-qD4(CSk)XbMmDCr@AH(46H"V4AEm#2lRkL$`*hF[E!%"*' +[k2V"51mJTNX)K'K#d5iRRHRYNBjAiG+QfJqRQbjF&4ZKCQ6$EF*VMh)(00AK6C[ +m-ArqPFM3p,kFV3i`1pY*(#'jVi`*QYTA"[8Be"FLTFaBT,)i@q#UjmU+iZ+kK1E +j3aNSb2)AU8SLK#C3"+'aZ!'99ij3hFX9d3%aHjZr"!0PB"pSb#dUbK&&j14CClK +JHmQqRU,#jRRF841LUjqhp458K)%r343iU2lfX@UVFdEdLePaK4Rl*8FPU`5)4%h +[aMQC),P&cF$XNLH*U'4D9!LRJE5!j*B)-NmJ6iJV"(M[mcCpJd"mS&9#6SRmCBb +l)jEX#cC9Y'CENf!X-ZF@f[#cUENTZSM!-9@*[4eQmCUU+-q2a5998BikQZXHa%3 +&&ML5kq11L&i!4Sl)N!$KLZQZR1T)Ri6+FbkSl)!*&6Nf1V#NcMU`T-UFYPC0fk! +U(f3m&cCpNk(U'HefmZ$PEM!YAR+9Q'j!-dbBhN"$626K`#JBeI6#e#3b!&`1,,P +U(9JmM'Qr`m8V,[q"J$&MM@[*eA*H4qh#hF'Q2&b-,[!5`)j6('##Fk46*T!!&Dp +G$[%Q'NbIZTBm5S$)#6eL(SFE4-M,TlVT1)c8CFllGc34NkJBM4NA&rple4T-,,N +bi#Vd'TJ)A)2B'894j9EY`VY!)5LhN!!UeH@G-pDqASNYl5Qm@,),Ri@0D9'%db8 +,R49SU'TL`R[#U&VM!JU,(kfC+&kV(J+BJ!+ZDkfDLMKclRB["cmY3(%D$F(Ba5[ +D-H(Pdmra+RAaSfSflHE6Y@cDRbED[B4Lp$`!eY0FK3QJGKlS8P3BL4'$df#f(!J +3!`*bd`("Ab#Uk89d3(LAP'Lk8`H"fX#5NMFY"-`3H@+SY2$[MGlU(J4LEq%QU,+ +1M"""-qN3QQjl#kDZ5P1&a66Pp(i1!45`*dFMmVka8)#D$-5#CJ+Z`J8pKCY5iXX +YdP804c6G18PM[!E6L%T#GC1dY@+0TPXK52#Q4f6SiFB$2&Y!e2E!FCj8dS*$T*` +pq$Q+J`dF2"UfFc,bNNG4a0qd"UqA($$R)M##Q4#C+#hF4-!DSZbSRP43k-G29#) +9G1f0jTRkQSXJDXSjc%NP82[AG**3!)dLC$'U'Q"1@@C#XcI01%N1fkhC01`mQ$G +)`hBUe04b4+HqCP&KG,+U,GIP,B5,)+V%bAf%#-b()UpG*hE(pLUMc3&6ccQ(+Ue +flU+L&qE5F8YEVK#&d@Z3!-e4X!c6cee,bp-)'#ke!+$[!KP[LJP)bqmQ!k8,i-V +@8h#&JSp,f*1FL-PI++r+rFVa,+U3!%A2SCK+A5TlDl"Skk(VV%)#k-T+p9&MHJY +NU8ZN%Lm+ZQH'$5S*j$Tkl6&5-B,e(K3R4`RmjX%kD8q%[CLaXj1QPB%SB10&d&& +LdNFTJYU*m+!@5*+DH!&3E!8J@V`&PmLpTB@M*kkJQ5A%HijHTmmdm2BV-@iB!&8 +3hB6NfkpSZM(`*+#q1k!q9(5"e43iSN56U@JZ98+a&@e-&lkaVqd2hjM6cma3Bqd +TD,$f&-dXjiSjC,"@,fd+SQe`!+H'9BXqLR5#4a*DLhc,a+6,afD+0aX+*j!!mBT +XF5TN81biZ!#*`2GDji`Y(i,!*`cZJ0aCPS18aN"l4m"Arkc6(qd3UZ!KZm2Sae$ +Yk3MP4EfS[*ILilC[`ZZb8*CcQp'QCDkbVc4kLAQi)r#pD"$-&ILilI5k,($DMZb +@Ya--L51G-rj3HU*E8J@2M)6Ne!c`N6GGj6M-+54h@dAe*ClC(1fFkdJ,AEdYB4X +609m1*XaB!,$mj4bRQV+j!2b[VDlC"r)*$ZH!q!bc6TKM[Z#Vkj!!hJ9$p9EAT-Z +8$`*diBj24!J*HKEGXeG9"2GPDNU[GCSF!&jbjAX)[!raUfch8-)L$0BPcV9L%"+ +Kf,MT!Mm5!M1U,8al-Hh$)LK#H1Q0GSP3`*UE9(F&V"(AUbj4aS99q,Z%Geh#kAZ +9K2UMDIY8SDXRTH&DG9GS*E)B[VF$"Ab$r1D2NDNkFFq%5$Uk3KG[ml4d"ECM$pq +)J[KJ6`,aQemp,mV%,IHNKG5L`T%@h*AAKkXVF(c-j!5CZ9$Z[L%YQ5mm4RpYc0j +k`hRih3hAPYP(hMJ[6J[E'hYl)rfhLh&M!+pppj`(IE%##MiJpbIJP(hiNrZU1Zm +LNk"#9FU'SfAeBTi49`%AbbL)YM#94&fX(GPUXfaMpld-9m[Sl2P1Rf1qXb@fDkK +%b(qFLma&BV0`Y`%eZ3aS2ZLF#rC)V(4rCXSC90ka[11Tjl()122l-rZaU#150KE +,lFD$F!i3#rkI$*N@QfmlQB&&B14iHYCY@*MbmSJKN!#4FMb2&R9C@0pJ6%F +6L'+M0hT"(%35X*ESKG"q!,lHL#[Q8hG&IhQ#-Y0M)0Pd"8jMZCXUB)Sb$"b)4'r +!kZaLNEC*lRi'KHAAhVApX-FFGF8pcZ'SDjr(UFDRbeQ"U5ki6)KB"Dj0'Q%1aL# +C-N3*+D*cak9'8#YAjC,J@i05KI9#RGf()b,Qiqk$P&%q66)'-C``YhLFSZL1fF+ +qkq+2C6pHcZE'l#[cZ[eVi6,-c@-ic$cmC"i!MFV!AL@CBKk4D,UL2i[L4QF8Rf# +HGp)P)9J88b*'M61j+q"rS3j*R6j!)"eZqN#1JfhTljD0M'!B4DBK9+2fQ&hU(cL +X5%KAJBr2kAea,+DZ(SY&TH'b-4`C5`,&1%a&(pc4J*`QGlq"*U"SZB'Da3$ma!K +q2J8B1@iFUBj&ADTTpP#e)%2Ed53m-,3@jTU5e![!86q5`m!T!T0i13SMI2Qc@%M +cQIB#[j!!Ek!'Y`H#SQqm,&T0jm8fA0,M#0LpeD&BU$T)SZPeSJR14IFQT#X36&6 +eA%55SqJA`p@r!'X&XCciJ$-dF)B@cU#@YP$YY`rML[(UU,eLD$1FDdS46"D*D28 +Yle#L-8Pm"6mq,9`m2lkfdKA5*U2Bj+hdLJFk++KF&Sq#T@FePJVKiBcEZ&*kU[Z +NJ*ZJJ%[4k89'V4rNrP)%C56HJ&R%Hp!-3f)cS9E([1V0)S$LYF5NCZbDjXGJ!0j +%(BTP2mr33l(#!,MRb4@NZCU8DVT9MMX6P)PSmm"phHmFRZdk#8H-b3QR6qfjUck +f5kf1Z+Tlp%8)%,lE'S4ENeVe1"VDGpG1c4)J*d6K[,q9*`C"4T+0SS`hb"6QG$F +19DJHI`I-UUdBq&@&A-![9M1F+QX3E-1UH@"V8Eh!$hk&aZb#(p(Khh#RM@SFVM+ +1%0Q!VXAeMr"V2GTVe4XQV$CjAD*35BYhb+PZ(T&"39KiJDZ"Lq-L8',CGG`*84+ +*-X$Z'D#,Q[L'Ak4$r08Y5(cJa58M)**jf$8Y#S5R1-*UIN98p4AIV%qr@DMi"Bl +LX"C&+4PEe6YdD"irp!+rdS$FfYA9[!VfL3eL0kh'I[Fla,+9)YJBUSeTJhqhhS$ +i*VGbC6h!)i%ENl'-E9*Y)dGe5MK%L45N6@mZGp-2)D%UC3-N3BLV!!Qc4Cr`,ZV +bLEaC9-6H15JPpfSDN4*HPA`K*IILT%Xhb'IViT3Y-@+,NpNL!G#F[5VP+dUUL!E +@#l2LpLJ4(fLi26%V$QFNIh4)p4-3PDqFNT,&PC*V**TH0i-jR61AkM-iT+`L93L +[6K-lA3$@cCJ!)[+kkP0@*)@M#j[I(`2aY[RY@I(S)Y5Q)9@aIBSIJ6L@+'&f9ca +@SCr%PRCe!4elD"B)L$Im*4Qcq@JV!$$6`S,J3d6ii!NiT%3RkM"PGB3Tke0*@Bm +ZCmU+5JTTqK3JIEeH2DCh*H88cPFUTm3(e)G8Il+hD"RISUCmi5HN02Z"F'9YEm" +KHhR++VJ@VpS-bYS1!ACN1M6rE0AUfEi`A$f2AK1Z(JR`0TJV,aBj+DRNG6J#m!b +-BmBGU@I58i58r*!!#Qdb4mR4,$,a`Udm5Gf),6!j!FFap5rpI#)T6GLh'!-+8V% +K%GcN8R@NGjBl1L3#@A4JE,p`p1CX5K1YNhpkIr3+-Jf*M&[$QqdmDdPZ&$iXTV" +!SQBN%P0IB`'jVMG['S(UiiVQ3p4$f648S"m3)e0GlNeH4ep1*,T*p-M#2UKfei3 +LSX@5&QK[ZhCQj&+XeGEAkRpeL8T9*dCZ30k6@ac2M1*ci+62H`15iiR)T8JI+TC +EGhM0Rmffj-c&pEj*8qiLZYKMdermHEFjlh@3!%!QqdFcNF93MkV%CeVmHNPX%e3 +km(S*A,X3m92hbR&lLc3P%H0q-4HJM9UD`X%518l(H,0paqM[ZH1DPYMZ`#YNCc( +Q(hVNZ2EJ6$P1%3I(0BQ`9dY*$PPfP4V0UDRpd[&@kBL9CcQ@E)@B&PhFAY'qU'4 +ckk+5Kl!jN!#Rj#L)9#"$PY35)`K266`Lf[3'b9r4L`k@f40Y4q(8ZNK9[$m3FfM +Cm[jp)@Ic-MDEJUfe-P["ii`YD@"6"`d*'C(5%4XNm0iSSNIhi6pl0(U%,bJ4kXV +SdG(Hr&'Dc-JjFja2*Ke(l6l(%53L$HLc!8a1kjJp*bY$#PCIHp'4r@Bp*C2hpj! +!Fm[ZcqS$I3**3lRKAq`I1Q@Hf0dlf0rC0pMGeYFEi5krA-MaV!YRQ*!!f+U%[-5 +%6,6e6daqCk*ciUaH$!8LJ&&HYR#cKBm[cNZ"c#M3U855[DTJVcVCK)DbaSNdM+J +m,b)`!TZ8(5qL%j[&FM4pfSeZq8CUCYSZ8XA#QMkSH1VkKb+6Sa1485&'2%-TQFI +6-qA8R&cl"HI"P12Tm(#@MjpC5QJ@@FZ`A9iJq,BKLDYhcl4Bb(5@XZFJ-0U$#SM +!UGHi`'i0hpT9UU6JPP'5Z'C($9aU5e$C3e@U,b3!iaddDe@0))rPJM@E-MX1@bK +a1JQ8a&hH$AAeN!0(*M@p3lcB89faPN&X4CMB@Llf%lVZ"'88,1$J9"-AI9N[qR$ +FILm$C@TTm3B+@K	(P9ME[V%b$`6aKR`qPmpfF@0"Mf"')fFX8Y'X&Qfk@Y8c# +&b%[iZ@+-0Mer1,klhY+MZJ4h`S!cY#I`cPR4dX-ViXF,U4$pk@$h+4K-IeX[4ae +Cmf8F+03M,dZ&NrT%hG"[4F6i$5Q+10SPHQ8MDc2lcHrhPDR,l$el!h5%6%0LfZH +6Q)VZ)l9e)+["''Jij,c*FBL1r1JX9ACL)d-)P4hbSbFkpIE,Lh"Z1"@d-ISaC"k +S%`2C%[!LE38")%,'2p6G1GKr9VZPqmch"kJ3pj1U#Pq(UPCDaiMqEB8E@+DGK&U +MjLb"KKfT+kB$@4K*ekQDkja41JG4-QabSMSUP0M`iVl1'EYf(Bj$PG'f[kJYK!* +)5(1[dZdDj1FSd&FY3kBF[ji5Lqi421K4`ZX@%%dd&2J3+G-FVr%A+aep6hq'TY1 +8Sfrh6%I)@G%HII-IhJmH$99QHj-1$L0TUMU*aQYp!00qq!"9%*YT8-rih1#pR0( +dPXhh@5(FI0,*@E'JSLG1bB8GV`(iqF)dD(+$XEl&,BPbfRLNYKL1B-2,F1b"NEJ +d5DjY3h$[P@qdAB5JQ@Sh06paB4G38$Yhm'Kr"K&qM#+m4pKP554ca5"KmdELA%` +Th2SJ4K,"0j!!3&"j#"Q"8,kULG3%TYG42-$`%YH3!1%G#+MGGPm2("B,lX8@e0` +i@L6"&A8pFDlDebJVieB3Y!E18SF4kQ*Q*!!ljm0)c[CfpNBQfLVE#NAA,KL,4er +Tla'1E8)MUGiqpXkNDYI!E35dF""S&i#9ph[LpK'j691A3&F*qU,V"d5BFTZf#Cq +%b!mN405SI#hk!QdK&8!S,IS(2kq%JZ$0""5'0U1hr-*`13lhp3j1R'SET@1G%pr +"dH'TrYcdc*6Fc(4C!k216-R-6Fdm``@U#3bf6-,P%Mr*Q*Ub3*&S)2S1%Gk5-Dm +Te`$JApEd4VicfMdi53X&3Q5XF$5F4S8HL$6#82KG'M,h$YhHhcRChbF&0lL!"F@ +i90$l6@6U&CrJq*[e+D(CY2JX2I##lmfC-d1m!"1aEF6#,aKR!4hZXVGNmDMd5bT +%!KXTbM1Kb"B8&r@'3(pTX$Xbe'Y!30U`8c)-J'39,k+3!+KJ5&IS1dri10,EeMX +a'AE35NPAr46PZqYjK4KeC)P1*cQ"G6E2-f'!50ASX[8EEqZ'J-&Z6@mRhhLaqIj +[N!$C*%H!J'3kZqXGlMHTS*C-b+p'9UipY1H[21LK5Z(ZCA!c3l&5JHD$'i&'BiX +6k2l-Nf0aJ&*[5d()q+F'+#,PaYQ[FA'jDVFf3[faQ)RF9Nk(Ll2bLN,kG-liPHL +++em$#'JSlrU#1pMeGZAe3$IkGi1Lm`HpU-+h#EHYc1Y5IHMdIFm&9eNTBSiMBY! +jKAU,AX&8BCYkcrCh5X'UK%F(LZb#"UVbpG9'dC8ICDZf8l@S0kGM10MEfFH2&9& +5*4iB66e&aGYH4DFG&J+XM5SCf9dk&aIUNDUlSYp#H&92r-ek5YMP!0$"LdJ*26X +[%"H*+9lF8Q)EC1`8&40`ER)4JF4FEa9bR%KeY6fF'4HqqCRMf0-cF5bk1k`)4Y5 +p#1@JQ8*A)E1JXhQq39PiI3b)3I%4$9h5*1Zb$DrPU&f,%LU9Y`L-r2A'MVM4fh! +S4hU9J9H(CIIR&QafP+VG4!e(SYla%NFrmFP,@Al1HM)@f[DU2*9[X8T1B$lp&[C +jQqF$)2C5Kba-56fNS-)RSIaLA1$Y3IBqSQ`F4C+qD*a#aFZ,aP'U`)3q+,1+%de +!1AjRSUG1paZ6%mkc-Y[Z-,pJLLG3)L5QN!#9DZq*4eqM6GURV&0d%!639JNiP#e +l"#lfk#Y0A!cb6)hKB[-%A$&B88b!#,pq$bN1bZIJf%iH"FZNLaQSmc88a5MkB9C +4A(CKT@8m$VD4F15NMQq3!!9fE`qpAXQC1hJbjCd5NXUYJ8AQ,"p9hL)6iqQ*@k0 +aRjU1RQ2Sp("8pR63"D%HYdc4pKU20VL8kPA"&,$4S!!pf+,8!)'b62fNZ&'eZ[i +6FR#-@!i,+k(M*j!!eQ#f0,aVS9ka9%em205Yq4c8`[l1XpfR4V8UT3!e"IFJYHQ +X[)l!",[1m4)9+hECFHKT@RLX"($"YHUhJ`PdQ2AdNGRm"6IrXrD)V[I'2c+'pQe +a8L"3p"P3i9%p$1H4d'i5[c*KTQjZCVpm,`QRkL#k3fhql*-MA+Mhm*MU,-5L1bj +*&#G'kXJa19bN0$Rh-NcJ'3j))1X&"3JjT$mM05J#02'KB5*%%c--%d'DD$4-H'P +L(KH1ETk3!'cXQH@ADij`FTAP(4j*(N8NA6V9UCNBZVfhFc6bZ9kChb"PUSNF8ii +bUm+9HAm*8fC9Q$,lp-UmrkB15CRq5XVdVe+CIP,QM("P$NU#H#0LG9@bb@hLjXY +@(kN4jM9S-#m$mcXF-2GX-3$QP`FR*NF48TN@p*8pUB&AR5Bb+jIk9G0%bp4X9fk +DIP!c-3DJIdm!%e-0Cr-L)#bie2I%i-i)pa`"iS83&9Mm&j))qVb!0`jE1-XQibT +BE2''E+TqN!"XlfaXQ56kDDRD*N#VAKm-V[C2ST(GLfUqZh2h"!2*898(rDbELLJ +9,ccbfKd,HH5Y4-&2Lf%HH5LL(cK-N6Il@,DK+[G3B@2P$4XL#k"`VC8+$KDmk`f +QFe8UhVicS6%SVNK[2Vbc$&4[KG3`5+cMKBA(ZJj94b'T,laUBSLf-%56U+B+&H" +&PQR`J1#)Z9PPHpU4EGr0`6!A-VD'2h%p6Ff#,TVbSb%[IX%V54f4,ZP+HX%#9RX +,YSbA%K!h1)lM1MFh(`5@QbbaqDb*8H%B$+pSl91X`15Gh8R0m*kiT"DY-8%YI`Z +e-*hdpdiDi&K%cdidi8Q)'SN-4r9%4@6)l`H`,8Z[8'JA@lA03$H+G*T#mISmVTV +50QkMN[TaNH%'IpPhMKZ0&h+GG"8c""5a1JX459+0ZHP5!Epe$-UMcBa4X5`"8XB +hQMTd[h%XY+-#YJK,)+B$!bq-ZA-4XK@E1RV3"4G%64Rp64eN%YX%#CI!Ih@pJT` +aa-JCHS18'VNN#NdK5C3%*KP%m@V&0m!ER33#-d,'N!#@N!#KVl#MK$MN,%HP[A+ +e5VX(9G'SeFp!X"+JA+(I#*Ul(USH8@&03Cea"I,DLJ9A-Yb9))*M)5@!)J"!CHZ +*aj*5d#Ne8M9*SJDd6@8'jNPBkaXB&HK!%T&,5kYDhm$(*Rbm8Gj"cIbB"'`SA!I +rm%"imaq!4$P01Rj"#bl3m3YDV'U3!1RTTd*a89+XC"F0aeC+6FV+E($mf%3-KUE +NGrZ(9Y#5C0G'FHdi[cCU9@X0*FLZ8XGD*)+B*2jA965T9c2)F1@d$eFTH0d+"#p +5#JD`X9VENBP0DX8QZC,mR!cLCQ)VQ0KNR)[pQTSQpH!qM)*"qdbB4kY`$dI4!%" +*JYA#a`52-i9l+V2KNL*L-!#S2b@&-rL8A65&LeVj492m)Z8P8G(+,[(%+J'$dJ9 +IIi4HKJ'LPH'`pEYSC08AlC`HIY%(fSdB`$h@c+jaH[JeIPa$48'5LBl'+[40U'G +Qdi38N!!1p3raeK)6S0B')l99L5)bmCQJ+4D-&1L9A+8AKL,cirkKX"*c[93`[QS +92&S4VS+&r*,95[reZfaUeCF9bq'AIFc9EDKr@E)6'lV!S%3a&G6!h#r5a!(K1*Y +M(hmekK2rFp+2K0e$NDTB"-1j'He`k!J4Q,DfI3S*11a45[Jp@M8kG%!9Xd@Z4+T +',id1aBJYb0N%CkZ3!"+)lklD6BaqBYapDII3p,0j"(Dfb&8f0c+c&!P'9YV'&%J +G%2Z&c4hTE4M-%[i&(M4re9i[GpVPLC!!rZHNm+ITNb'"FPF4'!pd",S#Ji(4`-Q +!-E!RF$N`,p!8Q"ji2h"(B'1J0&!B5SBmSIE3TT!!+D3*h4[+#Kd)eB@q#VdBQKf +U$pd4fKJU$48'Nm(aB%H`+cJB(!hDJqR"Sm'&`GTJ3h"QF'j`Dh#R0q49Ha2H6Gj +"lkMAlNhh([A@H"GiArI1m-laAZ[Gl0hPpVY(h$Vh,,IC[GZGi8ja@paAhBqi&lR +VhAHiRh6[m[Pp`ckGEjD[clIEGpjh`(ICYm$Ai*[TQq[EkYYCjLZE+NZ8fFS1Pl@ +9R5ilAlDrV+TXH9PefHbbqV*TC6HAh9Hfbarb9rK(r1hq(Il$ISGrYrrN9+(i"ra +8M6iE%f9f0Mc%KrPXq"SI1YM`Th`iaSBAqE#($ErL3jN0(q($&MDmN`qpE&K03pm +Z0UcP`meXq#)IhX5'Ar,Kh@ciVhci"4ZqaBF0E0M-Kl9Xf-5(#pQ`N3q2XH%52Na +M`m9mH)i0AqI$%fbiL!mlfI"Y2R5aiFriF!FErT)2@pP`0Km1Xq%,I1KR`qNdG*H +`iIrQ`iIBF!BI2Xk'rmD(FpR`2rK`*KYq`SF[B,N92aC5kQkD)J838`0R@-L'A-A +ZBfc)PHT1Bd1Z4[FjKDKD,LUI-8J)k`hJ0h`iaPjb"EPlf*!!4i&ECU+UY+)U'!0 +AK&Z`)BmLlc1-rDIk,AJhXUZ1FBBlf(!q(ll2f1[`Xi#ccfELH(4iAe5)Hj%c,&G +-r5ZIXV!Mh*Lm+@c))m+ESEMJ+'H2++BZmLQABZS$2N8+q!NTM`Z4&3a,1)0H!G` +-[AS&m-J1NJ+QkC8AI**YGaNIAXZ'cr(K((E`(rP`"K[bJ"9mR3fjq3BA+&#B4bJ +%,f2j(Rk@FiBp#UA8FBBXaC&MI1Sd%mN$5l#0$EN4"JmVM'8CCpr''(M!$UjNL(1 +&"09Xq#`IKYM`EfNBfSRP2qX4#ch!KLrai6@+DekLDd,[+DB@mLPP!(L%6p8UTVl +&TfS88r2je"l&e(rbU5b'eXrjeIFbKZrLj`TRD'--A2QK``a4(M%K'f-r3NE2f8N +"Ib$ceaYe++UimYpS+P#LQ*V"TcDcBa5C4rM8YBSTESk"paA(lZ46qJ$!83qmU'$ +J84pBVTML35CJ88`em+PFYVfMA%L-$Dr`BB30Mr"K(aYHiX0CE&M(KcSfr*J24pM +`+Kp'fI!$'SSL0Vc-Ke[BN!"(1dH0KJ[jm(UQJLp*qB5Xd"H!h'6%#f`iM`qAXH% +hqA!q'riR(k+,'[%q+bMfB[&2V)JaXJ&99aP-Q963jl-A92#dX3'*FV%"AEQ*(5" +$d,%Ahm*JR!d)%68E8$AURASrmQcNNJ5Fj)P[fcNimb`Pl#@ATE#%dj58+N5*N!# +mGNU%b!'BQ4b!ZH@Nm,CTNe'2`h0L0(JqZ-"VN[i&Cr2&L,F("cY%J"qYjNIMk"6 +['Zh4k@+"-!Z0Q)BH1kH*h5*GR"HA4D0i%F16BLD5)4c!FTV)&FI%#6&(I),"FY( +!*kH*qF0MiNj5,"De@"c&iX[B!AaHM+AMXbb@Ldp6l$`qcE'6q#b*RF$RpGKZI(i +@dq$c3Xb"crq1pH(c(l($q(`5km+R2VB$RmpLfr#C'@[(jrXa'CmjX4&m2Sa0i9- +BUm#R11E(jl%B[+c95%,T0LXf,B&lqdk+N!"1#qiY#m`&@2FkYP`(YqfD`-eLIZ$ +$`,@"RB'YJFf"dY"+84hb"8T##&LL+E3b8!2!,aTU%3fKP4KD4,ei$UUi4X`9'`2 +ca@C4,,B!K23(21Kc5!lS`'3,Z!"-QJ2j!8hJZ8"ei(`J"U!bM3[i!XM#!6``$iX +M&$La1)S&+C'##5Qa#JY5ib%X8V!iKX8j,&l$JY4BKm9T,(j+J41,+eL3!"S[B@( +#!Pd-"TCLm6%@T-DV@(4Jm3%@#5aU+("LF4',&Lb@Bj(%iL8X3PJX4'5@B2'h@$b +$a9GBh)I&GbP`BM%ILjZ`3*HEiJiXjQ(a)4EIT-#*a50Bc-$L@eM-aQ)"&SeBr!D +,@LcH`S)8X!J,8X$Ec)LqB%EdMpb)rSNEdGhFL2k&'p'eh)LZi8Ed"$HL"lJ4hFq +0D$-hSYpb)lU2'p'[Z4%pa)hSB@j%TGb)rX#0k&IFL*lK4[3eEN3lp8B8p#,Kb-r +JeZj6MiQ[5blVS5d+`eVZV+!1"[MJ&5HDpcJqMB[k#4GeSej8D!C%,@HLhQHLPLP +%(A-Q)-V-"lIb35Yqf[(6JamNY(,+-4Y%lZ,Eq$EIaU0m'b9m'p[j0JVij8AmFTA +fmX1i[*PGrZrXmJj-@YMN[l(*BIj"JNeNDD1i#AQ(@j!!I'BV%MK9SkYUS2VB*Bj +b#)QmJS5Uj58Nd1UBT)H2G(a1ReQ6KXrS+ITi*JdI-df*'Mk`d8GFTQ0LkmX@cYc +cXS@rp%XIVS`2UGXh@JCA1M2iBL-jUI'$,dS(4"@1TqN(H0AfXZ9l&QF+&r"rRI[ +j`Z2X`DZPB$T-%lFm!@Bbm(USUTSCkVmb3feNKVUB'@S$-p6Cc&"rb3ae1M28Rh0 +$IBiEkJaZU2r!$I@IZD'qa`heapa3jh*$[BXEkKhF82r)$I9kEUKriSEkhcb+Eq* +4I$12i[r,ShJMMq+Y2)Vr&irL*eN8Rd!drF'ZU4a*d@H&H2fSFaYI(('ZT-@ArqK +-dU+f'TRH(JMC`XAqPBYpR)[p#a2ViadK#8AA*8K-car")T%+3#B3+hNh&P&lP,N +h8I)%'h9h)KdT1%E1Rkh-%B3bHJE1`39prM[Ri)lq#"*demU1fV*CP1&lEI5FU3m +GYIQ-dkJB3GFpIHLiai6N-4hLX-l#&chY&NVUMXl0(d&LH$KH&ab$!pJbkQJk*&5 +02&(kE"Tk"AFb2eBGJ[#dD+jj69()RR,GKh&06j'hVFF[#ZECFJYU%rl4)qPVRfM +Q5G1e)TeH*0&[4`,'BqCkUPM-DjcHrG0L,DrN&L`50YfLJZRYL`TQNaUT1YHlU&* +@T%HQa`P9)bF!b4QI48,0HEN*U2&DC!+3!(-SUEF)"*!!(c@Kql&BK4cmed3V%T! +!bF%lXC!!(IU%D3`dN!"FaTV`+SN%b'V6ZjbiJfj[H')U2#DM9G&C3p6[3E3K%pc +[[$I"ZQf)H9[HT`l[d&P--,&fTX@q"3kZKekYbKI'H2dKqdkmfS%Z*,ebk,'k+SX +F-VY-,LckGIdhrV-ZqX[h-kENQH(rIRChjN-J80X-"L12j5Lkkd*d*+Q,-(SJMEk +M,@h(6jZPlLU3!$LeG3+G[lhUDUh$`-m(4e[Vd(e60!FGVP$RL0qZbqq@(HM@"4e +PmBRcqGfa)QR$hmV1cNM2cRjjachSi1Z'HdiHcmM16%[00**riq"qUI0@l9@H$48 +rAbRqlm)N#dQBP1#%NTp`pY49XUGe([c2"mmI,iGTL,Jidil1J`Z86*8fa6R&kJT +XkIcG&"Lq4EVVH&C@6LUl4N+UTBpB`e(AXJj*DYChH!)N9Nimr*2@eMTNa5$KkhB +0SA+L1mG23`N9C8G+G)J6)SRC,"hPRC(id6&%Gf9@5Ibd"9VaYJd3Vj11AUIidGp +Cr1KeLrqAKr6L#m4kL-HV`Q,T'1qS"4dTj+p)p(iQHNC"G,e%cbJi,"d,&dS&"K, +lI53DRCl%4f+C(+KT&VA26iKDZ4p1K5ZI0iX&Ub(3!9'LYBiqB'V&JpQQT'f@A,0 +LC1"iV#G",bkC5iHPC*jk-66%G9155bm81m+c9TY%,9XXSm@Yj*VA5%Y#MaeD`!p +4eh%@L4fCY,LQm$b5ha44`FXQDr&`U`9iI09)*cSdJlYUNB,Cb+Xjmr0JNkjLD0q +REkJj1U#NT+04e0Up*Jcia"5kJe`3D43,IU44U5BEH40T2TK'l0a35MEQ8a,a'L- +p6'@RqC`@V8*pKe[i88%)8*T&+"8b*U0ML*ccNFcN4ZHJj%iVPZAMmA&)[1N`DhL +'lq)5**AIAXHFlKq#@9SdB%'QF!6+jae@5Sb2&U25@%N2DQ&GJ,&U(dlL'A*Q5NT +1LR`K9qj05aqmN!#9fCpV2*JqQBkAH8ZA%S[Z)b(H3#Q`G+QfA(VMM9l$ZVpE-G* +Qe$$e)8Q1fUE[XUqGh"q3!"#JA&AZS+lIfcY%lA5`C2'1i%QjLGZT8N(@2Mjh3G' +dZ#JTHKPAG!ZkQ&[(PEfCG6@a(0dG),%[G6!JMT)JKH*,p"d2@YFTe&kb!V8r4*Q +e61epA1fl&'VIaGfI$@V29kKpPelYX[3302m'+$cer2'8MQhbQ3X(-V262pSJjDX +++9Q3!*4"*P0hb$9`j4l(`qjQ5JNEYTIQ5mK59b&'RR!+L+efaDNcQ5P+b5a,5-d +69fNcNpCKmkjf$L3k4eT[id8$3"d1V%5Y3CiS#KRXFP#[!!P3BQ,SH&Hl-+UQ8aF +V(6mD8UQBN!#9*)3#"R8jT4Fc61El,*P[Q%#!)@'C@`D8d,N$)F&8iGHVSZ3j548 +P6c*9Y1K9)623GRh8`5lfKfeL"cT`iLL[GQNIZp6i0lR8%!88N!#BXUBqlqE1q3U +9YmGKqQU1S"4*-EV+S$3)5L1R3%PPmTX*6H)#XJ%[+V[,Sm*E2m%l[Z9*KJcGjQQ +VJRCd,1JPTbKdM4XeRq-GF)p6*8#AVU"+UR5"SBS5J`k4hffA"X2jh5K3ND3H`%c +SiTc`lKKjN8+2A[A+P"JqJ%FlqT9Gr['XiLEK*cIPmUV2Re6VUdmaFYh9*`lei0$ +*eMT5KZaJ(Bfh5L+qI[Nk0U`A-X*958@9Ile"M4QL48+h%T!!)@A2Ca&D8MA+A!2 +p$,QNS4"Q+TmK6NGJTPEUc,D'$*&9aBU-BG&",bSIecIrC,rk4MXHdUKm)@Z83b2 +Fia`HHiPbdUU'`%e--"VrqJB"KP1a+!G5&dXJU[kBi3&l*##r,Rc6US8V8&5&DAQ +iNK69d'D&DQ"Bj9r-,QJ2QeMmlIQ'aeF+E9*(rM#Tq@5Z6RpN#44aHf`3lV0TP%` +'R6`qd'SQJaMSTSS'"N[UYr(UD59RiP1&#bR!8XFbhcBbK0+S)6JZ`KU#84b"%J2 +(U$')kLR+#ccDFY+`AIk!6fhAZ+d[@9m&`eId`,2q8TiSjcB#*TN+1U8!Ji++"9i +@F0X"b%AP&KkFef#6(GaX$["Z9R9KL%P"f,)LNbAhLYDD&88(SMKB[iJRqr2[2V, +F)JF86&%bjjBD*AM+NdPMdbPb`j&-Efe2$&e%1T1*Y5hA6&VX*BN*Z6r-*-p,P4S +[T0GT8B-+9FG))@G,q3X+,N`KUX2P+d3ec"9#fapAGSj)#'N(%26E1KkSp+CGSVq +@NSDU@JbA%ZL"4&%E+6$S,bqD@IlP4I2SFR2H6li[48249M*[UA2jpZ`rIc%FMVC +SU)bfQ+j%@m4@B%+E'0V[-q0T+3pYI66[U&'UFDj&UFBrUe&a*YrE5+,i%0!XfER +0-2Mk[BE"eeC@,[,RUX1+I)"d8T(rKhRAU9ab[TY')U6L@9%!QDqVU'D&XD',fM5 +95Z'ZYVK%T`@Qd,5"mp494A(H&4BBG&j[dS)0KK@TdX9PkXV&G1%AUdfp[YV822f +8e)Qc%[3U-CGAeFj0eeHek#CY[DZp4mHPSkY(1"jBS$B8b4)"XTfk+@fCAT84D[H +L`kSCfapT&hL-ZRIkSIcZM!J9`qhRb196$KU-GAZASVKE*aRXpU[DJ,41B[ZfjT8 +Ki586MQRdKA4XF2UK9*0Z0C%P$bK%XZ*cqjK@*!ZBZiVd)T8*h282Q99@0P*h@3d +!JM*p!),3M'49$#r1YKpY9e3b8L@arD61,%G45E6`LJ-A8N96XSXB#CKbd'2'"kP +c4`Vm*6BC(3+LmF+,$5Vb`AbjI6Q[U'4qe6VTXUrI9dj9GEZb5T!!*@3"FSdU3#j +PmBdQA,r5c)%%J3BY"**)"U6D'#B1REj80VqLDa@ZB,`l-A*2KaYB)%diMZ@-@j- +-R29dAU31c*bK+mpHqfbNkVPMH8r+F#AcTPAZYS"AG'Y`B%DJ4#Gd!+TEfcUQfYd +cUM6G*p&ek,A,TeHP&ZZ#mjGc9R4a,,',p[EN9kMiRhb@QTm')&S13RfHm+i,T-H +JpJ"Fl5KVki&,Eh5E@pH"VUDKp[R(5$N05(L988&Jr9G)b04@CJ2ap+Y$FXXTG*r +if1*YlZR(6UkmpPP%NjGG&HfmQ2$L-6hS8)k-2(8VM#U+4jZV$3mc03$+h)M819% +*p!$b#8hhrX"lMB,qH)(SeMS8j@3HDMQX)h0pKff8H!2'9a9qIEX!dF-[0AcXJe* +LeLdqeP3qcj[+KBUQ-LGe+*V+'BUQFU'fU4b8b"-6$pmiLQiSE1Jh#@TkV2PCS+S +"JXrK"eh5THP*j4HSj5cEdBD@-h-ccXJ(Fc26M*QTkCNC@ErI8*)&+l+J%1Vb%-T +dmD'kl4b'ILZ[SLDTLJS$ZhM9j8Id460mY%6(TceDm-0!XKhQK(XG&ArjBF9I1#" +U%,Y2DY)45D)kV)KB*&8GIr-VT+-mbk#&AkF21[l1LbFU&lPrYQX9'hYZa9G3pfS +dV+p40Yr+Qjaiq(UASB)0UqBI,YQS''a@$+k4M[jT,2bSSH)ZILKFe(DK'2J9!ip +#E$%*L'ZVld"22RpXIQ!@,F+VG#6Ii#Vm"C&iGi#)e@VR#IG4B1r!NEi6hAKUqMh +iS@%&Ef3N%'M1)@JPH0"#iJY4cB*5Kjk8)+Rj3XkCl*2(cf6+pZ-AXN"`)N*6DPE +QmC69!`5Id"pBZT5G#!m3(!bk[M5miIZVE9+&rX$#p3'$(TK$)XTVT2l+AVQCL53 +i1l346SQ8m4!i*1$h(l@S!q@pQ+Pi`36FS`Aqq4BS5Z)`8p8L20Da%!mD$b0q-c, +bMR#8$D!ADe#dVJEB1%!m$2'QE3SR@@cE0bB,l@3Rl`Te"MTiFe(Q(FLE5)U%!M5 +%)V!DA@hQVNKPP%A*(hc(0fF,fa`5T@QlT@(G2&"$i9Sb-'+)Nr*Q&+($&8-3d3G +@8`[)V!'HT4!LJ%',kM2F[$6DL6YNIhih*fT,%c0j`jfMVChm3QVf6RdLU&ZCqB5 +)rQ9K)AA++UV#*MGMdULF+QU4!NI4$UimRMaCqr+F4#SS1Ra#HfA"9Vj*2FZ8G,a +J*dHN`J#DN!!T'KV5P3SaG&)C4Kb#ZYj8&#AUmJTZ,F&QHp2US1Z+#MN1V#!21*a +N4Jp*eT)SHAFKQR,*%%T5a4qb9N5U-*!!5"3N[(%$#FmS9LpbYiF9ZC%QhNQ0IcA +6RN,41-)b&-Xe8MSfLH0fkY6'6k5j969HU%*mSNj2%,fVAX'X*0V@Vc))DJ(MbN3 +R42fl,0((BPADUK$C%16#,f&NSB`9NldF0ACrC2(2MmVdSej0TCi0)M31'jMc9m5 +XIiJ%-Lf3!094B@hK'5#5Dd!dM%"3LG5U*8`V!I52K"GUZ"#Q!UkDm!Bj2k`Z2k+ +)R2I3i*0e+b#Xk6GA8EjC86@k[CLUd8+rBU"@$)55F*!!U#RAG$GFdBX,G#Y@Y,j +j,@CTQpH'$4G*64J`,#l`Dl60l`*Aj5TY4@LUdPH98EUH9kZ[qfUH#(4+bL,N$Dl +G'ZU8[jLjqY"4)NbUf84d"B'Q-N'i[)KJMb2!jQl@%LAeei$BUqNfA++UV8b3!+K +mHAPJS3%Ke3(&&KAAU"D&AA2hDNFA&mLV,",d@h[jPh-9e@JK5m4F5*8l1p5L80R +5X!$eFS)+erj9&QND6GhUL"5p@2kf`T9AC+p8e6#e&heBcRDl9U$!UA+feX&HGLQ +f0PbT'(5Y,UbJG!@4F@mBfUlV3NqTI'A9[&Tf%BM[#BA6QIcb'fh#UlZBZ24Z[r# +fCqX-i&581T6(G@%&@9KNM2e2*H+BV&ke-C3Ab9*#DriJ,dNPZY8$fBUL2c`59ad +!bJpii8&)pIi+#+JH1YjDm`TB+5YJ4@68$51ldKpmScbJQNeGr,e#RG98ZX5kVPf +IM66jaJ@1"$lb5mT($T@*(UQ"J%iS@hN(eKf"QijZ2E60r89$PXMd*Yba8+YE$Y8 +F!H(r56NJGBPQL*C@1Rj"S+YY'r`*(k0+68H%"*diUL!)Fa9+V,TE&iPA296GU6F +5qqiE,#0K*#diRC4AU'l@9m%Sd2G222br2L4AQ0pZ3iAFq[-j)(TZ&KSXKQN5JfS +-Q'Y@`@B8p%RR$K`b%A%6VmGPhJ4RcDYaBY[H"*!!ZC,leZS1A[U+h[,r+RfklS' +VeTN,UGP%&MPjd*lpS0k[E"0j&@5RTabrN!"q"Y2C"XHbX-eXIAqp0e1Xhmc@rd1 +E55eh-kRDc4K!)b#fHq,KadT"N!$edN*6PeqAL#6UfL2dq+',rr5Z@5`P02"!E%` +!E3rBVY+IPi3reFE"(B*SLh01G'mSXMPRZ#I(G5Sq$0R6Z(irA8q,F'6cPHJQT#N +&`P''F24h4IM#li)`P,YaTU6FM62A4lNEcrmjPEY"KJ$PEY4)bYfS@9rPFS6rE-T +G$i6ek#ESFTCK`UpPB'+PDrQPpi4Gq[IXdT`c'hMKBeIrTKHZ4b'M[p$djbNZ1%* +rTd4Sd`d53T,1ekHi#%IVce%JV`pDUeD@fNVG%(BHI#3M*r["#aQCZAT*ADRT[cG +QJR&S-&ASRB%mA@`$IFL-#V@2$Um41cc"&2HF)`k[ACkeKTceLD3Z#qV#QC'KZ$N +A2CGIMMRVZq@e('DEZ'H$LVCCCc*cde0q(qD'V5IhM8XAEFHI8P&CP4``NC3hjXa +-Kirala#T3rpIAV[#,I0VShLD'@A4)I,3853qDq(5M3M-23j([`I,LF%(+3VT)@q +cV1LbD5J!d!C*$Y6m$d2!3l"+X`ihq)AYKrK6YZ+5U25FV*`(-e2+Nl9+89[+%AA +3D&bK+&idH2QMELXii%TrU)"a@#+j4Y1%*cM(S[B5-(`#)RAFFFeLJp`,CmS4+CR +2$6CTLSM-Qr+1RlRY`8T&N!!mmI!6ipbChDFJ2C86Y,kV$&RDLbJ+b"dI3AHTV(l +CFYIE)"M!LIh!U(,kFqf,rbUArEmDbQIIHk)bX[FAVJqbPRH9f1ki*rYXCQjQ#U` +U05FM-h9r1M"(f9P1J8aAY2jCVNJ0[i,!D[bK(A$L!J!m61Dr3RFD)K63&DqXb)T +6%EfDS96mRkT*CFA@+SbHq&06bd0NDVd3HAi&TD%"$id8T)")P2im"'41@T!!!%9 +&38%GR(Mi[LaU"Z$b`+U$9#SVAPFXkYIPL#S[51P&SB[bJaBGq2+1Tf9RCHBkdmp +R'dU6X+M5&f(&",`-d%-iLRY,9E`$6LK-6Bmhj3ijH$`!$jK0XTSD@!`!kF%Peh9 +"C1L#T+p6@3be06Kf21["ilmrNjfEJkl#pkm51qP31`kKhpbF$232YNTqBa$X!f! +rQ([Qr!9lqLS`Zk""(%Ci0#)J6XP[`d$`CdpJiH&LGP!KNlY+#AaR%@i-6&DjSZk +"U*b$k4XS5e41r($r94j)eHXEA"H%*9[D9%iJA5p6HG#JhTHlHpi'X))r"Z)L(KH +)2bI#+rq`S5l,1r(I-Hr%(c#%H9b(B6aBVQ')p61-"cI--"lF%-0iX"c$U"`&Qmp +TSk$`Ea!&LS)B!!!`!d&%3e)$!&D`$e8,,#N!+Qec[jGh@9G%A&BL)U)9%G(8M(( +-'%IIppepbI2,b%c06%(1,4J"`S*QTXqlZ`)LRXh8'M1RF4c(DFbDaM'RS(%FTm` +m[aV(2$HH1b8cN!-MT19qRJ9F2(I6I2r[ZVl[ZmlcAGF&'ab""%%3"%'3!%L+C%3 +VeYA9r@Ep8rTj4l$i&@M"-f#1`jb'k3V$B8*K,X0dJZPlX-ec+UE*63K"8H6*hDG +bXi0(@j,U0%2kK5(Mmp@lkQ3M%$f1re)YTPmRYG-mdLbM0RSCIdmp3"mRcG'+T$c +k)1P6EE6d@f0-Y*R29'FB*G'2m6qSMp"h5BZdE#QGPL3Yd"kAPKU24QIaa@ST68p +k3hYDqV2a9(3CIdfGBRLM$r+TDJ&05jUYQD6"aZRS&qVi2HUrkHkNJ9UPG)D'*0f +[h5NpB2`cqL`IT"kPQU5l003$id6d-@j93icbk$raSHTYG%G5X(CFDU#8T)He2G* +$a[24qrK`GB4a+[SjIUpkNKj-'URYPqBD1G&lq6IUEKU@0%UVPh*TIY*#lC!!0-m +i('hKN@SR)c`kUSirSbE6[j)kDcZNCZU6p(GYXr4()bJkJGqZCP#rT,pUldKr-r+ +M-rP2DRpMBr6Vr(GU%rdPUDr@+1fLRNNrehk8AMAHMVl!HkZI'cHMEr![e@rT0dR +GYB[5*iBl1TEr9pe#rdKkAdZ6)UK(dSGDM25$X5QkPIG58qPbdUelY-qN!FCAd5[ +j1E@EX5*k&Ap#rCkH61UL,CGH0VC&Im6rSqkNCj-kD0ZPVG3qk4AY6DQMm9EdDYj +9$6@14'rJep@eG#8T6&XMVD1V5CHd,k4VaYI4khQEkL*RdRP0Pa+0qk,MH,aU0j4 +S"lGC9-QQ*A(0)YR3p$#8rJE*0)i1d10dJQj3R9%ReifV1e$hH0f*1M*lDJ[c!NA +'Q1*PmZKa"e4lmBc4ZVEDV'X56,3jML2Z'UGVc`GdVE4BefTKXQ!HJ4N0T!NG#cV +(`93#-f1-bcjk4Uh,E[!mPch!4l[XXV!(1*!!ii6eF#`KF)AX9$!I%(DFX'2BhKQ +eZZD"C+03Ya6UfMKCef52VTQ,*52k$Vl-mXI%P8%a34&T33Q@2dVfVpbaPZEi$@j +,3r+H&bbhfHmmLH!mEP%cSL`*jjSb,9rp'1-1HLGQ[f9$C[+TrCC'LeYV,EFmGhL +,l958B'TVbScj-HB8XVcRQ,#`$HlRPZ9Pdl,l-S)XQI%@06E'hC5Tr+p!RGV*qBd +E0bd'pqMF8#f'a@-a*,Z(d&0%G'JHXKM-@MbUc51ZqJSHc@2Sh+0D22%'#Da(e3` +2j!Y1b-%3M+Laj-%!aXQiG#"aRGM$$8I&FJVhX"eL-B8E,VB8dd#a3CUKBKAZFGJ +p6#"R#U*RKl5UC6MMUMlQH$'Y9NXrchSH&k3QHMKbF8jFi9aeSNmQeh8,0j&!@VJ +U"P`BX0KK9@jb*6)'-R%e[S,4`EQV3J!h+6D"9VR&TCQiNPLahH63J!TP,'+hHTk +YB&,Y1&GXJN89+cK#fC)3Ee'iKH&F6LB%DkYFU%69kL@b!CKGGVq'Z,BIaV6hbe" +iSF9X+A6CcB9N-8YFZ"V@86@"8I&[0e-KmP*,S3dp)(QK3qc5a8EX9bf&NQBZ9*d +3B5Dfh9iSHLS1a4kc)PJj*)&$%C)Nj-aLT0S+Ui4,BTQiUR8Va)!&qhLeHPAm6!U +L*YCK&L*9SCTr498Sb!X9aQ8afrbDJ95Lm#fD-%SE)a!q8@N`!JmS!MNe4P)`F-d +9,mijJc!8&NJVk'H`8RCJJhVH,d#FqrZL609i*KFLVQK8)B('5cf#&C6VIRDffbQ +1aq[Pp'rQ@X@UGU(GGBY4TDh39,9C+JUc3KZrI*5fJG3,SpS983P!CS-N44-Bb5` +`kV9#k)-"JB-8Xf)c#aB3#cQG+R+l@a)lmCY#0X"Z-PaF8Ca-[-)9KqBA*pC4l!c +TAkG0UC!!cpC401b1&lZ!mc-+P"fb9E'bSTaRbl"m9SAFke9EQIS-(FVf#`HLf[( ++i)!b&SXNqUJpV%Z5DVI!9Ja!MH[Si)d"&P9--VRLr828$X%QfF4!#B8-BXCPNCJ +Xa5kKCNJ5Yc#V#,'1mibR#Lq@J!iQS3d@FrSAB4JQ3f1Xm*!!5S93PdhX9e!cE@! +9H-ldU0,*8E'D)l3#Ji3QHLa8k#&cS6l!6+)$i#rd@$#`L)(`S+,%#Mf+6E#+UJF +fp3SlCP[K@1hBSYUa4@L*J30e)C6*%I)Ff1k`#CN!CE-JfL@a%M1-(f9ehLp11(6 +')U3*'4AU++L9(K5[Kid9b,R2b8BZ*d-jl%)2Qa#+#U['3jL*6"*c8Kji3Sp*BTl +3"$J")H$U*C0($4-(j!(H!hIRB4X%a6dUSb%1&#IM8+qc(F+B&$3,lR1bNCm*'BI +J8[`4KH84CH"`XKe#e(Qa2-(PBJX6lprZJ'T-*dHSAa'UBNJ%QYK4I)@+b+$Bb[$ +aINj&V)2-46jmUN#l(qmNprG9IM)1IimQQViV1DM9M@m)KqQ`cd$IjBjVbJMDN!$ +C'Z[qbKd4&2@1HX9R4!ADV+X1Qq"4dHrXZ$3"Zq"SVr"5a,i1BeB[G+*pF-DXk8" +I3(ma6,#ZIIp*J9E6!)1AU*VMUVBU"CdM-+Q#`FY(UYU10KJh$$+V&IeMh!Q@mY# +J+T@KaRY((GGD*!P+[,IHBEbh(*dG-*PBjk`kB19m*5VjpFd4qE'C#HZEd2$-$)T +3%kZli'c[Z+3Q0lTM-cGASB@Xfb'V(cVGaF"iEbE'mHM2KTQVDmXAUApjDl8VbKd +&8VL$)Rb*-5P+d-*Kc2%@D(0ba-#BFaVbDM$'Lq5FpC!!Ge(9hJT@%prUN4U4[cN +Lb"*cDq*Lf0BjMGKQKqQ'VHQUYM9DI@,VNI(DI"(VpLR)&N2XqX"aDb*m1*N"dC( +S$")$if2#Z%H"pV%C*N[A[ST@Y6HKjjYR#ja$)eQ"a'INEfj+hKPNLG$b'5P"riq +2JRmGc&CGfpC(G@l2C,b*'8'CQpf0JYQHc`Lmh[Ki+rJM`1Z%k3Vq#&AERURD$V- +YcKe4#6ZM,"Rfr16$qN53!+Tp8U$iH*(MZLp!I$`F5`a%"l5H$6HqSk[U6%kEU2+ +#GlDP`$QlNTfhjFHkGqBhlib)bY`Fa%3CXiq"*3k59X0Nk&VbBM9aHfC9J3h3di) +bh8GM-f1D0QIL25JM2h2c-3LHl64QGcC#,)k*TH'U'K%6P0Q8A29eca)6#ZSi+JD +hmL1B9&G66'Z81lQ4!HEX5'0f#$5C9D"0+i3T-N+J8-JZQ(#B4L1NG@*,D3VHSei +*LJK+m!@TPZ!eECYMSQ!hVG&K6'Y&abi'aV5V'+1b6ZX'dpm)`EH2N!$,"HG$ZSj +Yh8YLhCF%bQNMaJ+8dfSFaK42J6DP4!b-+6PB'ppITZb(NBb3!,jBZbG-G-%c)Ed +Hl'Qc61LA@UCNLV[DL8d*0DC`jTkQ)$e6%Y""HUBJ260mA41%qlLQ+IfCDpS)30f +i&DDji&C)T*!!q&+Tie0G9DY1,61Q&V&9TfCKe4(S`"p-2@k%",GFY8BUd)D@q&H +GHYbrkNbBAM$GB@l(1HVP8&2"Xd-RiQpD*RYUHf0U(iFa&38ep5HBhXC3V$2dH-' +!SG+,F"K6KaY6Jl(rY3*Y1X40Vc@'3UfKJ,UKi3AAKNDfA'hkIQ2kF@0SSc'dpHU +',mCAaNNVkr6`PT9eHU[$Q!iiRQiA!f-kbQ8kI2PdP-[drXE3d1UUQpL8(l0c4e4 +-%'TaFe464Qb%)#lErbikLf"'kGVV(94RIJREF6iM2cN+A`l+RFe4b8&Z`@deh[$ +Uer+[6T!!f$IUM6I1'N0R'd-""N-M&6j#i5X8(Uc`AbMm2`Tr9Z%r+Abr`[qUm2F +8RX1`II$kTr!5KIpFiFmSh+l`@)8rc,Lr9hLc`MFU2&lKQa5q@1&1*V%E#mq`m5k +&rd2KPBa29[K#d3EQ"8`SfLXZKGY8mEVTB'GC#Kr-a)(lGB8IB(Y1-Y46#RmEE3Z +&$m(l)C0lJDda6q(h+Vb0(53UI+h#rkl`q3VIS[!3KIGN,I`4E"c0dV0(%DpcZjR +'AS9(XT@Q+IbL`[-8IPhKhc*"lCL+53Tr3H&r8rKaTNUM`Kp4H*V#Ie6i#B9rTr" +I-Y3CTKa@6DjDVjB0VM"&XIN*KEqLm$mVr*,#PbTmS-,raA4UBk5rMHRd)91S31& +&,,8+5bF+Sjh#cbRm,BA2C%Y+6)-%KAr##L"Ei4d8EPAi6S8(&(j8i5UMb(5&peG +i9jCJP1AMM1jp@GUQ+,b6`JH`ihZCVU$qJi`klbVm)CD'KpNDKeLDda@q9q'V&,j +%i@MbI+c`'`Vr4Z'pf@,$fImCTNS$)`d5Xje"c$T'eUX+Ame+#i[GBRTJl9HV3'3 +$Sq#2,*&h-pB[&,j5i5mceLf-q[XBiF!k@q%G&Iie9'I&%`jC$(TL@6R0B8`@*RS +9+im(&&k[m1F9(UE`,eN#)-+Qm,-+(k68289FLVCd#SmdA8kGIETAkf"ck*'C$Ad +E8dUkVTja[1HZB)r6e5lRpS5jT9f@6crlLAY3EG[k@I[lCBc-HRREiU2G,pB8fT9 +hFc[[Q1mpYh*+CBq)JAPKDclBehrMm1cf@aH9pli3%SL2QecIThP8@EF9Vjej2fe +)dG9eFdlpP$NL[F2f*FFq[ePR063ekI!c8I-+ERdflB8I0Ydcj[U'hqlj5p2$Mh9 +mk`qhrIc(1iV2klrHqmHJEjjqm[Xhr[RIf!H@AI[k[C0rIHI"4rrcdG,IrqEEZf5 +EiaH(rT@mm1#!VkEqqm1BZdGI@I[TmlplrD&(ARRcZcqpq[E3FBRhrA,hhcF2HqU +*9AqqmapElMp`kBZ2RrYErVf22lYc`BN[EjLZ+8hZPfJ8,dUBH)kj#-jS%AcCSM! +a-"Ce`aLqE&%(Q"l'd-'UYRN(A,IN@1RiGk@[$k)*(0+Lb,&NM)Y'31D3!),%lr+ +B%#ckAEUaD!4H"C!!,haA9Y&"GVPS$MV),VmlBRb(E'aSX+T&e--8`@3,*iGF"YY +Vc!ARDd`9qG4h&ih[l%D0CG*-#6RKjCV859SYAI-M3+Q[ma1#-QKV8%6q&a%*34& +X,-4pi*LdR41I2jjKNF2iEMik0@*J,$CMM(aUX3NQakM"eC8D0,TV4,iFU@V00pA +,cFI',[$D*!SXEMB@TcQ-aH[4[`N6CY4FRN6SqHCM%fQq6%4D1f2aG+0QCV@%Dj! +!i(pjB'de`62)@&TSe!aqBX+A#TphMUAeaP)dj*H@S3rD,R8CGp&DhjH5T3R'dNc +MVY5*YHA1953r,LV'r5Zd(*IfQdK"q"[M5bG$'8$+d[PLB#`&G#hYJ[k3!!*Y5Cj +a9d8$mbl@`%5R%DfpeTI3l+Z-r#frJQC,YSe9Xb8SZ58SY5ARa-"BdJ&M&rTpB(S +EGqfUd)aAD"CDF#QNpHZSC*q@am4H%jB-E0Rd@&$S-"EN&9aE81VEC"%(aJ),cP! +,&TbY'"X,MN!*%'S"@N%,-SblaVp5K&Be6-4HTl(JR((A6)Grr@l!ifd$"5`Um0( +NaUL)r4CfG%N-GVSMSTVF-Hc!FUa+CepjLbXNRDZ3!*3Iml1NB"GHD4EJl5I5)JE +)b0"(IPPV,1L2,P)8H86A,Qi6(5mZ9QMfFi*$GhjEkkYdrSY6fVq'fEq'fBKdSiY +LLZbYDfrh%Kd[$eDGpSY-3XH@p0ML&eJKrTb[q)UcDUSm1Ci!,fUER`ccm1Bp,kk +D$20@#M*%SU8iEbX-2K5m6D,M*d1#i!!C@ZLDrk[S@Nd-XC,CQ0FHh5N`D*CH@#i +kIQ+XBd+kqPI`,l!MY[PR8hTq`*KIVQ[IMLc3V2$T9N#L&5l0'UTUq5K1+qUV&9A +#L[TU4E@`iYA0ZJX[lEembl8C[[l&,9IZ-1DM$Xj(-Fl[VQXriMh#'Pfa,TbJ&Fl +35MlV$SE"5j-edVqZG3D6rXU%kAaaT13,*@-K8RX6E`9@N!$8LQc"#P@XH2@ciJh +*#Z#fiJZA&CA8LTc+#RGUlBTP*GrLUdVYLbbqK8Mc3U4jiIX`lq*b+li"@B2&kPj +ZpL)I['Y`eG*HhZ$P&LmAZR6eGM"9[jY8[*PBF,8c15-S+S)16q#),)IYBhZGX4a +@*ZEiR`AeQM)h@j,cm8dP)MECR4p4EXQd(,1m%l9VCdc6KD#U6bH6VY,LDdIedZ1 +rb6"d44fEp(9TEXm+ph-VfIeMDe!9XBljhqV#',E+9Fe0-HB'-qLI1mYKc*eHS(d +$p$HPZUCC98hVjZ9iRj@mr!K'kldm&E(3beHVfSd5,`r(9BpCAYjiRfdI1@LhFDM +`0#k(R63hR0jVcZP8bA2-1H'98UkC'cGZf#HbqUJ%apJ5-HUL`hF,!h"4f+2F60Y +4Pah'+!$1+2qAaNilla-j-@L@M*`r1FeG89(mS5R6AH@3!0SU'+T9''%-Sr%L*aG +S`c!BKZpRYYShrAZkMNdQ&I"K$[pHBaJq%!a$NfAB4TKiAB[(Uhpm!NbfUTfI#r- +qM"fQ4(8k8jMJfbG4ZKVH*lS)9J#!$d-p(6C)eqcp+r4Vra,E99m9"cQ-&(bq5)& +M6GQKDk'9AYl9bcZT@Q)0c"bBpM!+$0c[U-q01UjUcJjH$MrPA1lPPe8YmkUUfBE +!c&(2fc,rCm"l2Le)e!K,HCIUqL'%q2NXIVlla,YF+AY[GcQd22kCdAZrZ!4ES2A +Hri94Pq,PJq'DMBF[1M3[6PqY1(feaHNX"bk9IQCdVcMYlRXk21Y'DTe4+(Z+!q2 +-Br*'ebiV1Q!Um"iXIEVXUC,(XKr*HM6pFF[Kh%-jHqYh0qcCprcqNkHH1re#jEr +2r[2-RFG[+rr6dGmI1b&&l8K1#'VHh0LdmI@-Gc,c@cG&a,KMdlEXq[(#QpXqfVi +cI1`-EermpZD0X&PYekjfDrpXBZMeX#[SAlTmkpb!,NpfHk*VarD[[2bI$XpfHUE +c[flrBjqrpre,rpreqq[RArEkSFH(RrchrArdr(R[9l[rj[-[Sj2Hr8@lAdrqjFc +IIK"kj4A,aiQcTdfC1[f0erjXQ9'hk,[&5jFXL*`hIq(FkjF6EedkeqA*!8pd(0c +ej3lrH@$)rF&hK!bYZFYD4rp6'UL+`kA(hAGN`jUeklpHpdAUCbZr@[lpLP@VhlT +lUjm)BqABbSM!0E[0H6jqf+IIm&RS2c`UCIL$$iedMVMRh[2[$4adGha,'[cdYjB +dq1P[2M6iG0ClPMRMDI!(AaTm-fTBbX2$(aVji)Kl"pmcm1j",@J`bDq`qQHmL*q +iGME4Re6RljfHb+mPEbHI(hq42em"i6irbHGA*p(BIe@lM3NN5lrbVf9UI"%J`B6 ++QLIKPmCb0&kM56IaX3LBN!"-RDZS06'k9IaFGD,-CC4N-38`hFY-BbL24P-Y,D- +L62ib83(ZpML)ZmQHTM*kLNVS-FUQ4bL,(U9d6!fcd'(+T8183hZTRRC6!qfKII3 +mlDH6Z,[L146h#jLAqfrF4r42h*Yf*afRfkLFrS5ja,qRBjKBKS++SKf86!N84-f +dQ4UTL6E5kj4"le!QjH0'Zdd836(NTPK+SbfdLhkN#r3QED12D$[Y4+Rr[`KiQbl +5Yh36mpV#U+k0VY&9kNEYk9P+T&#k$Z`9mU-[d@@k4HGS!(@K*m(d"(@PMQ"pK9k +Qre!(E1P%ce"RqKIG6RqN2[4hkNYrSIld1qT(IkA2k8[U46p3$rU3!$kKrp,lp!r +U56qRh[3UGDII-)CSc,kLGqNAe)jq6C2TPc56INXI3)mV@19Mr'E60*T#8fNk[8' +[dCpT"Rk,k$YD6%YT#5fJ5*T(mfNKcBAQPj'#@p$k(00h!26Y5)1Kmm[3p6rd!!f +KqbQBlU!3'NSeG"GCU8lj[`m#+QCA1-K&1XA4IA5%0Y!D@N[Vk@YD4ep3+Re'+qN +V@NlId`TD4D[T,GVD!J6qA`5-"`(FG+@4R@cNT2-86m2S8rS'f&RN4cp-SbL&KY1 +$p"#0"0-)ZSIZ"HYl0*!!"Y(Gf2*5)2!6rHdP38!`4&2Ga%$J8qM`(Xh"Ed)3q!0 +q%`H"Ek$Y-1Ml-$3@qMi)IHm�`$EHq'[K-&!6,@m6Y8flMJG3MQ'K(bPV2!44L +G`m)()Y4QX0"1"&-F#aG%1*JY`Q1$4FKfLT!!EN+`T'YX8-N#fj!!(Xr#C"BL@CJ +V`Z0(@'$(MrGRB4B,)d5`P,+JX,#D"DX)KpH`d)@&%"&b%eL)CQ'*#(Xc4DM[)m, +Z2"E1L0"J&Z&%*a'NXb+NcKEK+lEjUd%L,%pRB58,&eRSaF)L%Ei[BS&YrVk4K9! +@2Q'"L9Le@)3hfl2!%[EQjb*XBkTX+fGK1`X4)Rc%52N4)mTf4[VYM(cE@H+hGfD +KK`MKM#MKp5b`!JYRL3r[c3)MF6K60-V&`Q84GR4PJDQ9c!k5`ePiQB@I@'!N5'C +-#5B@@#)6f)BJPZ`J4TUJ15``GB-Bk$6[&k&a[JK0TePJfjTB!63aj6BbdQr-BS% +aE@4-'fqb`)Tcih34-SkcN!$+!P-dJi&A"Q2+B)4qTeQ%r+-X-#$,pk-BD1EhC'% ++#`aS@XYBB)4VCD$3bTC[hFS#+kV@[Lb`cDf-P+d-@$Fa%QpbXm"8fF3)YiQ"a5C +@Y*Z'La$"LLf#JA8%+lB)"XSa,0Na$&aL'$PL'2RF!4BB@Gd-V0fXXVPC*A5c",[ +lXF")idjKJ9AD@%E3@!X,$&"L@@*L'8M&XNS9biSfPUNBqaS,E(-D+q#d"KBBZG0 +fX,#,KA-XX%U5aXL4aUTPfK!4YM#PYl#NEQ&*hF)+D8Xh&KMKYV"Nla,,m&d@i@N +XZjKAf#@NDVYkb94E+&0rQ(D&XP&D+"FZKh%Ab[*0Q!r351pE+"F(B#S,j6&(#q8 +m$*DY+C5,qK6+*4d+j82E#ZAG10MG!fC%SGa`$!B#'VS8bR[+BF#`"m+I6i!"m[R +EBAV#c)5C!C05+1mR,"U3!1A#5&N1R*,PNM4ChKdYbmrhPqApdfAj4*JXUd@bV'6 +)XYjCPZq,Jh(,mK'6,'m!``CXA1Z&k5(,kl0J9X)XPZ@[JIbkQbar!8'THE,mfAi +B#([VXLb(Mj!!jDKd'!L+kLl,1bl+FR+U,!H9`4b"!5+S6CDE&9RH$%&01'K&Cj0 +9PL1JC%3cc%qb(0-H"XTZk3S$j5jdJ*NXbeRR22,TH)rm*jG(rRfj4ck"cSPq(YQ +eh52VPchb+S,"!G8@bj4G,-YZQ(E&XUFrc)KL19!+d`rQY@,C["AQ!Nb2BRR-l'+ +jGP5aA03"CNLaA&"B,"qF85`rA9mXPi3AbpPGLZ9(MX0!B&CHXCbZ`-b%'9iX2ei +#FlPB2R`'jL)-0ZB@`C6$""I,Kl$3S4d`@1a3CjMHaA*1*FaUQ$DB2M!3PJ-&pqE +#5$$BX"H(HaFAbrA,BAE"32(k16$cLqAG8'Jh&QT)+CEh4"6,*j'`NpK`mRdB,(U ++B"B9bmp"NHFJr(3C$!5IKS)[R),T@5aAGLq@cd,aIiB@bfI#LZ8lSH$a"TK2LZA +Ec$")f'hlBD"%Z3N''mS(&mY(Qi[P%p(&XZ5#Z3N68L`V5+4b&3E%GZb$@3H$MDi +!$)MSJR"A2-bjBMQZY9Lq,keB2J,PMX`YPMId,CEAG#Z@eqE!J-"VSF"k&0JA8$B +9KCJ+iCqP&X[,)Ial,2jpHaJ3B3819m&3@8#Q5N!k`I3-b)'iJ$`Z,5#E!IhQ9TL +DJ$aQ)mcmJ*bh$bB$TJG-5%!H$FE4R`INfJ$-6CM*!APC,Fa+Q0N"Z3M#$`"a!%* +-%'BD%C!!#qB'C1pV!IRJQS$m9'T!cZSIN!!IE3[)k9-#XJ9#$Zf!53R)$GN"qIP +5Q#%"qH64J(`+R4F'"q4+-0eT#XJU&(8FJjNCN!"Gkf%J3#q(k3`c-L$(V3kJ-X* +J`j()J,`"'cEd#mKVHJINp8K3+K#Ir33c25"[KH*EV3(jc48"1H&-3'l1JpN1dbH +!*`#J!5ZX&,U`G"d9eG1ir5iq$R0EF2%'Xd"j,6kLU0V+VCLd@AK86-IRim4X6Dd +edK8DQA@@mR*SA"NB#lcf&'k+ifZmL5Pdf*a$biT8@bQC6bND0r!9"Pqkqr)m6)$ +1+iLr2j6#McH3!-8VAFmV!bGC625S)Q4%-QE9PYQH$VViD9q-1*r*J8l`*Xj%)fi +E&CGMGV0ICLKN6S#c#Cb&akqRTqV*9+R'Vk1F0A5i6)hI5Xql+Eh--E&dqT25AGM +T(&qejkl("k9+Re'KepZK1d8YGp&6Q1fC6DZD+6H2(Ld6-h22P0!H%q%k2b6MXZR +3[K$(0,Eh)d0FaK$TU'AdZfPhmB-1cFMefY[ji,$ZBPm@&`pTL5V`hF#dEHY(GEb +r9NBPARSk@h9UjC4HJPBTT4p4E9Q8hU`kbbLGprBRE69E0Phd$+&Pfah"H(S4Ek- +b[21i9#IrJ)T1UFiX+X,N&Fh2C,XM@1cY*26TiJhV4(8P1C4RSR((UJSH*rZmB5P +dEdNF1dRh19R[E8XK5dNfQB[`aP@06aFESP+cUDb)$TV8@hP8j+8X%ceI5RY0H-& +U8I#pr&[mRDd#,+UJiDES&(Jl42l2bq6ENC18LF$jP!PMD9%Q&4cM0rL8#3qT,T- +59LBPV%bb@CQ8SN`kq*C*BA@CA"&PX[eQGC&-CN@5,BV%dE*)1JYeiVeKRImh,*) +f&)QjN@UcD9`ZlPiG[p[-*P$9JPSAP6Cm0H+G9p$[q5JU,e)((+Ikj94C4)Ij9FT +ZS)-jG$L9ZD2"STkk4$@YUDLQ%$K%YGh!T(MFrYE9Djmp(PAJ1a5+p45Hk30[@mq +IU91Yd#R+R2Dc&+rpQ9be)Pi[+CZ!bYPHRM*4I*JS%palA%6M[$k&9IQ5*q%P#C1 +F0)lpj$*1cKkKV'`U-"(ZfcY8kUUZ'!bdl9lE,",qFl6`Ri*XhEcfZD$ST2L`Z65 +XC$APPG#i)Tp9QVhA8kM1QB9,4B&M`S0!q-L#@c@$DAHp3Yib1T!!+cBG3'RkJ[* +Fi9qAB,CGUMGd,X$ChJe2V2-Amq!AZAfTA@b(AT%0fbP[6AAL)iA#REfKN@6*UUF +!GeE)9Dp(i$'Y@EQq1#eLK')[mFRQ)NDiVPVU+mPVHUQe&rG3H,R3h&+NAD"!598 +plBLUE-((RcZckA3flFQQA(a)bUC5h$'TX"8`9q@bBZp,i`GY2S0@Rm&bRm&aRd& +@Y5$G@544F8Q9e`l&iSG,0e*aGP81l%GGR""PDUa'e45F&bJYem)PV5KEYEPS6,D +r&('K954CG'X+VM-fEbB&XKeD"9+l)eJj[ipUc95X9LN9MPY!+JBiaJedZ'&'%0I +[,6F0ekqAi*0XV@p0U"AYKbKmXbV1Uml+Eb%V2jbH5mY+bCY&HFGT@4CcZr1&dL( +LYCK3aNHU-9V'*kKMTJP%PrUV([!QAh`*1VKKU%5+0fIj(Zcheccie63DYm1RKG- +'3BUphLFP+lI'fE1U%8LiC8+%Uk0apKL9jG+M*F+plLQPJlP89%)(c95QqJX$j#8 +'V[j03e0p!'dm$Y!fE6)Q(EEJr0@PRjj8qQQrp$-YTCpfK9P+d)aV3CX'I`Bb+Il +R+66-'Gj!P@Bk@8,IiS&XCQV)SLB[0AMTd%ANJR5)pk8lDqR3$NV&U)3DDZPN,9A +@1TLVQ-98&Ge2a(F8eZdT[U+`lY`#@`daaYN&YU'$'6+k`"CLqC@fG(r4LcLaTGV +Yr8U-[XHAF&aRFSQfp4JqLiTU'I%(#FAprZdDr0[%b)QDF+KSUdqG&[@Z6N[I4iH +mkQAiNE0dS*C+&AUXK%Tj9dC#kbLrB(6RLkp5j+qG9A9h[ZTXR1r6CRj113+``@e +pd%"Y1dfjkI4d,TP+k'PcPCTq2lHi'T!!aN-1i'8fjY*D[EJPqe,p"ASXTpT*iR' ++eBc%h+3IS1iUF9AYkqD,PJU2mS(5qG&m#CQVmba`IScCp8Bl848M5ij5hQUI()d +GBiDeS01`%S@+Mi01&GJ)88I2)fXfMIG'm%#iSlkUaZ,PTF4&HGi**-+4Va(,c5Z +4+'rjT+HK,(20UK5jTmpT()qVpLi6FcV`FRQqq$cQ3dj9Cc5LG4-hJ8paa8H9mT6 +Uh'5)hqZ@m9l9Z'#4+Y`rPqEMDd*Q5)ADe[h5afi6jCYSiaP+A8%rPP&U%IhG4+R +VDDfAi[*)VD@M*VV65j8PG,)Hp8p8bU1eiL#ZPYE@8ZSqHZ-B"9A5aPV+Vb@h(fV +E!EVkLREcB[(9@26k!a8XHQN!qA$4Z`"8U1LG%eq64Dq$q*4-)1'XlAKr1L8FVKK +PBP614VT@'#pk!N6I&9q6@EGrPFK-mIQC`@qI!QG0,jqkp[pAD1hr4k%f##eiNFc +a0EdUjK0TqFQ+jYf1QlT1UpTAh960LeYD[-d`p[[X,@BHJ4@c)F)F%dkM91#[6dq +3!-@G%BU%@l)NTfLYPIM"GUlr&F%jGfcc1hb@XKc',I$+pC+L#9UYAMpiPdk!,r& +H5a'lafZP(8c9YFr3[[SUS'S(-9rQ)'B@(6aANDLf&YaUU1r'FcVU!ZV$I'aS2fP +bRI0)0#QZF5r,UeXQI*mrRjU8(VIJ6mjQ8lD*LXj333Q0,N(,c5%iCM%fmHdKXfS +NURHkejNb0N+e9"qdUTMY0H'"DV+9jV+UkX@XR#kU9SVj3Z0#GHh8,PA,bX!8Ida +$R2keYVVXDfe9Ae9lp#KQLkISfQC-hFR0!4#ZIS[4m1"XPG(Ni#ISI1+(&@pl!5m +k+LAZ%mD9("'@Tl1`NS@,,24LBC%)haHaF*D&4J6Yqe!GB-UkQ'jT@U&U+rUViZj +9EG9L(6H#BV"2GEkeQZ1Z@GYE2AL8`rP@-)mU9*aEMh!$Gp*ZM4E4pZCCR0RI[)M +R#EcCAYMqh)9,3*!!E(mc"2eYH@+4EH8kV[q)jEIeB@'i#"pPLm12MQ'a8PAl+%2 +9$PK9qdF3SAhdVQlrD$B%IM3AcdRBVQJ(!"2EXhAlpNT)aAdEf,NGmje`Sk*fB,* +Uhhi1c0XlBq[f(N#&UlDG1@"blNcPN@L)lZb1E6YRL@dl"q[fm(T`KLrARH%*20b +MD1'pJCb-TbD%,q,K!38AP-3$'U)X3%9G&LJY+N5hlkL&P"d@)@@(5mH9*YCGM2A ++9AYb'64)hSFYb5iZlQY2$YIYb@N#'bEXbm,q*'`da#3[&J4)-!N4#@Ge,F%&-EQ +U&P5NDd(T+ZiSdS,Qk2BJT0iH9!0YQl0dHr0qE'dqaTpcf*T[BJUBV6Q&Rh$B0Zr +JKad-F*E03#2JUK$D1&RA'N'bCGe9He-@"$5GeV8QV&+l!ffDQi+RU3p3[B'+!mm +Jm'`dkrD0A86[G[5Q)bNEDlLP9V'phN&XH"hb4MHJY@h5R4RK()rFd$+FZMe$E-Q +iA8HcA,#p8kVEhcN+h$[`Bk04rc-hkPSQ!$C[Z'V2R)'66%"e(ZDPjD-KNSI#bMq +Ufr2MF*+r$EeQdEZJfr*C1[*l!MG&i%DKC`8e@N&RVE9HYlHka(#VX*N#C`HZVqM +ee(%Y5j!!Ga-$qNhP20aKhj5!XdeZhElTCG(VKGlRSJG&mZ,%*5faA%54MJYDBPG +%QJ#HL-kL**d4Rl-#MCL2ia(L)#D,(F3FC`Fafh4R6#-QRbMf'!'!-3#Q')#Z-fD +ZU$afG`"BGlTZG`0dRHieIQ`'`0TY%8GGG+HlRm"UlZRJ5K()B0dHD`ClV)@aili +KPce@*$Bf3U$Mr@J"6V'I#maVIN``0UHCG(YDJqJG4fm(H0*fLD&6GkE0%QTUD80 +dqaDKe4CSYH8)J'T,Z%MrPM!G9lCBYlIZh$+CijCqEFX)-I%@VJ[3q52Q$)l$%c8 +Z,!G0E@m6#le%Z,K0K'pV"I'q&Gi(RmC8,6"II,9%jedaVe3AAfF@1V3EZ0A$Jb- +1Re-B,+DDkMDY'jFG0NaFaPEl1YeQlbp%fB4!Zdf8V`fe`6B%25GUlIN5(JlGcYY +e@la`FTMDU0Y#+d8hY-[AcXI1m$U3!$2lFphfL"RH5FYDSpZb1[-kKrCS$ee,amh +6T4Y9Cqi-2JpmKlE"Z8,ER"*GbeQ13Ar9ZGI$&cBSfZkMZQdhUbblHk!l3Y5c"RL +VJf@U[F%0E4VJelei!@fBM-llURe21HLpCjZ`0iAY"+BplGCac"BeCDJff3db1H9 +f2!V2#I"dd'fHrX"S(Y`[G1"f95X'43i)HTAUYN!r!@Q"erK#m*T6GDIj!XI$9Cc +Q([a3VD+03Dj3"-p6LlRXbp"m+EUUfSTB05h#4-iLj#8&H($+-U6Vi!add,4i'RF +EeGj8YC*`2)i+b0(`&GPG"*QcTr"$$YXMamAJ%6II$B,Pi9&@d'6d4NaH%+k@TeX +iZ1cT-`%YkFah2ei#M2Bi2N2J`5V*+!j,AedlI!DlPQ1QJS!"jq%Ch#K8l,P&)%- +Z*SrRSVD22SCC#m&#dd0EGIXK!Cq(,JJVUXkKhVSY4j5M,@He+2UF0KcPp-'L16- +C@P3rqpjFS2G+31rG)G"lfrKQKlChX@kVAbi3pEZ%r2TqZV0q$UZKpB#f[$6-LZJ +LA-&ZiB4f)c21JbYXb16c(,D'&!#QE8m%#YLfCcK3fMj8kRe1ATHPD2X(klD6E8, +bbIFj@Q%RJhAE+3DITd6@UMd(PreF215G9Qh2#6hYTq'ME+I4KR*STbrUfZP"UL9 +2YEe`5QakJ@Rh3Np9'i0XYa)eD`amd&R-`Kd$Ara2e,!aI96Y6"Jkje6YcJ[SS05 +10qLfimbr(TqKfiq24"*Z-qZffjJHYiQm4#XhkEEb8)%Slmi3JhAY6ihBM`TlY&Q +JMZ*6URQNDMXfA!$bL@cGGZ+8+-36AA@E*$)m6@T%9`#m6HSXMU5"ZUDL5@VZVGU +8VB*&ZDVE(#c[FJ9BU"GS&pSRCN#`#ilEG8k8[@X+FiJZNAATH!5''91-pFr4fD8 +kieTjH+RL[#q0Kq24(8H+4051S%6-+e6RKVim('@fBC!!EPXM()bf"ZfFY5)VYke +Gai*E,,N@V5%c@NRVd@Baiakk,blUYLpB1bL9Y5P5SG)i9*!!cc#aI4aUjr*fD1d +FCDfICKED#iIerAbKl)TXIJJUV-THab["hNF4hjc(+DV051'4$Ll(fH5HF#1f3*c +B1NjN$6CcS3!@FbXEe!L#MYQ)QSZb'c0IGqD&L)D50MS06i9$&KJ)%h92GbjEbD2 +JFCE0aUHl-Mi-EDJ$YA`K8!GUG+GT"%-9c'8SlfZkGK![``&-a5q&K`QJCT9KUR- +!RjUI3U++RDU@MH@+YkV14clJ#e%"Xd!,$qEf2iVRGAMJFG1RS0-C6`F#!(QJh5' +dPJkK@HI*!GaR#lrBJ'DRh+SkpjeP%Tj('db'H4iZT"!ZiL6F5#%JpC4!B0,i#`# +TbT[Ll8Leh@N5%ZlXJ0IeH09jfck@)GlQpXI*V0U9)hmhFJ&plk*qfRkI)Ic[-EJ +!!q9eSNB4hqh81!!BEJRq#FmR`-h"BDV0F8a84FG-%9cV4AEKQU,Ep*'L'lGDYae +Kl[K)*)##Y@[Aj`V%HV3a[XB0Y)fUE9ffJ*TeZ)9k*F$L&'le9EA81!HZH"BfZr[ +@p-2[mS`CdXS+Xhc'M"XGDfTU"PP$V0D"6ZG8Uh@)X1Lr)IV"mf$[(ALM&8`e9QX +`3`krF6l8DMd(4(b`04M)N!$je[&r)6@I6R$mMIqNKTe'KJJKLd*B$,''Y'1pTG@ +)EaEAe$a3`e3B!K3dZhqZdaRLG!kXi"JiTH8B'qkZ%D,[UDQjafkh$kPC+)*!,I$ +h'11pLqhXEh$0r8jVc4$r%S1'3X*`Ch!&dphIER4#`0de)l"pF%d&q[k4cZ#DJ8k +VN`Ppf#PS&9)c,HbFe6NA%Thf%B`XGl`@HRQi'&FK8[alr'SZB2[mbh`RYJd88U$ +m``*p$a3%i[l[a1JKG"qShVP3p,kT'-kYk!ak@$"&9LeA3Dc"GbbaAPKqde&9&#M +ep"lRHi8pfI'16P,M!1Q#X1HNeekqjiG,mCFZa8Ykr+AZGl#rpm2#`JB-[4b@q%I +TR5Zhhp2aqX""Rl-JZ6[1NAD1l(M2b'($T+dX**r[fL8XV223i-X$VPdIf1&@fp9 +ZYhTh[A,pNl!`UAR+$e1Q5-N`S3p23IJGJR3Kc1Um+Ze%H$)86iVGpRE%AIp(rlf +p9i"c66!$pprqZB,FRpl`eJMXM98hZLIpGXKVm4m-LVr4+IipK+XhRl"qE,eae@Q +pmF1hTdCDjpjB"H#imD(6H[-pqbclC2Xm+iSib@TGB,A1XIlDrZRPP(01P&pHHHa +$9b6P!FR9mm-KG`qj@rV`0a@G[bFqmdV`S2k*9r&629HZ5j0l$T*5T4U8ceT45(m +G)MdV"8PEZjbAVSNbq&CBQehDBRr@6[krfb5Z&UNjK0QmmD'KciD'GR[Lh-[SGPh +je`'*eb@(X1Y#TG43IrJ`Y&GScm6VV`,p9ZJRLCmN5Npm+Dhi33e)dk3Nb"XRCGV +#L066E9IDK(`!!"`4384$8J-!,[%293eP44%!-@AZ3lpYEjBjAm,,$2Q'N!#6[)3 +jadbbiC%d4b8Z@4,R-T+a4!RFI-R8C"2*NMA'019kF$%aK'JKKKBXPd-$TDIaq3` +X8+XHJ3'@`a8[0r*mV29i15)8T)MPYPb[f04prRl[Zf3*PR,[kAhq!4ZQ*q9"%!4 +!%!#L!4IRm4'G$LmqA-KF8)PNK!'qrQI6#bh1aZi@2YRY$6j1PIakU[aLI1'41jh +[rIf,p4#*3eUE'RPf1QaCD(,k902TFL3G1!DH1-f%9(bZ(jeRSipam@1B@&8)ca, +l@K4#KqjM"FhS[%2lm&%YIL6%$lq[$HiadFdM1B##IR)i1IK19aFh(#8A*#XScD" ++5+5F10cV-!jCIK@ZH*QhAMX5qZq#8!%jU8C[qH#CpDP@ERNR%&Ul0d5Z3pehHhX +G'GH2l`fdq9BkPBelDLhcZErlar5hm8,hMmr[jp[lR(pfD)06rH5H+b($6`24R%" +6fC39Ie99C-IPPpbp(M6hSfECJHp"9V$K`0h9cJVVU0'UF$Q,AT1KX&pTNN("C"P +HVI&U['*&IV`q1a$+jEhEii)Tk$QM")IDp)RY9STGB@d6`m1-M$)#4rd11'bKN[8 +l`ME9AlpML+ZQihYmX8"-hG#NK0YpCP0qh0[iN4cK#8p#&[KR(U%T%Y@`LPKC1Im +-4D(XhDC)lel2DR@$NUBQ'V++&4MAaR4SlZB4)5@9#DhmXj'`48dJD@A9"T'mLkN +Q`eBbP`3MNjTJ)"91(56-r0SESjcIEAS$)J@QmVT9'CmhqXcK(RMVJmN+DMJ'Tla +FUj[PJ$)R)'keH$9aHIQAIE&*DIMrH1'8RRA&NediCr@9qklNPUdZJ*YFU!r[k"q +E&9,(fq,YKZ31ZYGbYI0C(cV#mRJU+I3jrk6e@U4P2IfHD8cI#ZIh!Qce1E80DD` +H*,%kker`QG80kK@6hC2`pSE0fX6Tc9a!d8X*51Y@(K[4A(C-bfSl%RdDDphP[G9 ++P)TIpXP)h,@VHe-4II$"CF5F-`5+2$4*2)+[)ZT&XLmXAKUZ"$krq0*)F9TpU6i +jP0m'*kJ+CX,%@c*6`Dbfqkc)DPU2PCqa8USbUD61PU%a3("NT%HG2I*0$KaA5)d +KYGVVL)p&2DD#!dX$S68&[AYArAUi3Ndp,iFF40!jKdG3+k)@0A9e5G@N-&)4&F) +9eZhI&K4GLJB`f&Fmq0b+ja8fU1cFLUZ98S[AD(AQreA@kPe[3[pkm6&X6mA'RhV +EI&C[Tqr`I4G88rh$Xr+4SUP)4V54PT&N"4))'9a'-G3bJP6!&kQf&f%f'U%%p3' +&0U,NiQJ#9Y$&FcQZVG#e!pq%+`!mB53e(&1$J6C[+I4P+3*6rA12I!Jb%S3N[VC +#14"ji0G$&DVI6)PU$R%PdF!5+K$*%q5QNe*JDQAcAP*%eApICR1D!6P6ECjUVe8 +effp(hM[be,$2T#k)YiCl`hX[Yh1N8pAeeB)%9+[M-LP8CfP%QQD*!5RC3-J$SYl +fYNRT#%2&80ZX,%q-Z6"ie%mI!JqJ)j35$SCd``5AZlLB8U"da)Up4Q%Fb35@3"# +)88"Lr$X$BerEmR`bb%JQ&"V!4*d*J5Q2GpFKQHH%N4J`mClqH*1k[pImR"B*T[* +RR51C*T[((`C`mS*YGYiN'[`F,T4QB[SDkIe'5NeD#L2hpY@C-'3Rfdah[a,4lbE +%33N-5F5E6!"+h*!!4N'+"F2rfPIRlLmHSL[Id3pa"BNpRX"209e+06SEBI4Y-!T +)*-EKISCaf8E9Ai%Fh[d5*Z"GfNRDA!chFL3lHE$la`$'9QaNkijq@N`(M'6MPMS +FV3@VXZ)M9+e*YB(e9'T`bfp@',F@)UddZ+9ZKA(,aC8ZA+I(e[K"'Jlfi@$bZ21 +$0$TSF'kTD8#([G#fj49em)1ddkM3"SX44*Q3!""Nq"a5JcYp-pkEH5KX"VTVYLh +K&L5N!V`['JYEB6L189US0UD2m#`0D1lI3+$D@"p'%UYqNK61CjeBb)A&[5F@F[q +MB*NDE,PNFHdIE,R4mXR6MT4JF6hY-"qCliFkq,@*-ika`CBpP[pB2pM5E(QRlSf +a#XYre,da(&cl"2dGHq,1JP9[M'HPr-eGU[8VK'2JY3&-+jXl@)@IN6'lakaD!3E +0a[V"'CEH,Umh8ES)U5hRRa3)2S0EHq0HVjA5ZYK816RSfY8fa"2RKpJf9e-#%B* +KqGC1G5EUp@R%K4&DU0U4'8Z!IDQP'rp9RI&Cb'b89MZ6dGND9e1$cPCX@fDmXGZ +jXIN8+MGH2hV,a[IUrcpl9BZ!A%[*Za05S!0$YD4`[U,2HGYmNieeCl8PPF[rZ-6 +%QC'1B8%Qd,F(L+b*Qr1#Tq`EebcXfRMX9!FQ1DCEZp#bXCQQdFjj+JT9#LL$'mr +N+Ui###e`fiVXDUZ`me[Yd@hkU6rGfj3NB%"m'ilSlYDf)pa8%5Hh'&ANeI+"IJa +ZU)U@91b'STJ'Ypb'3*Uj9U!aQ$4E#ZFS'1#3!)dmZ,A3hCpZ5H%S'F2K*pJNFK3 +2kciTR&jX'a05P8LGF`dFE9mLiIN[A4KZI2mCBY2Rh1HQ)BB#,U&dRk8L6BcdH"G +)k8EciNPf39QkB*3(pd[,)#kF-2'TIT!!&X"e&*0IZP,h@6J"0ddC,J-@`@CQQQT +T!kRke(cHAAS6Sp0d5'S1J+`iC5Yph00[)&35%Lf&1Kd*LN)RPVmAYL36,5F4'&" +!fi#Vc%+5AdC&rNIS+,EF&Xf"%GQ[dj'cJaLd4*(Z*%Fm'Lmc$Xq0AL6@9F&LXlL +`a5PUGT82Mh-b9"+`HBM5`ApmKS'K1Pj0V!N8Tqc1eQJ63dTlPH[!Y3q3!'J!%R* +c[Xd-qI*GEdrekb9K,P+A5ilb4R5j"X2KR9(hHp)c*Q'q,iVQ'i&%VUpe&`hK(8d +m-!66dS(1-pX%da*IfKC*,Q"N`X5!q)[kf`9d0*Q%q"K!J12NrXQLNcHcl6Z)Ree +Dh[)#$+T+,%$S,VKM$i,&Ye9B5'DVMTNY%D82CT(*krjD9cGkl'!@K,h&$q)UZUH +lXh923A'bRUT%-ia)(N2S`DILp@*kCM%-XqMh0$8'G(q%-"b,qr'bH+3%UQ(+-rU +'%NB@D+rI6"#qJ*UmA+T4!9"DY,)S0(2rbS3EG$@IHNfqMS%Vbhie'9Bd&NGMQ5A +r)GY%a059A8D#C`S!AS'3!)Y*2bdU1[2kKCpI1K#JT8f(,@Q,EH-35)fYTLk2NPS +S,He&9cREPHfRT8Xh,IY`-ZBVmclZbimqq[0,(Q'J*YVMdi44SAB1C)jd22kT1QH +9BfdQr5h+E(lYESIDHIc55'a9`GVAk'r4DpGNhPh`T50$`EC3UQXXh$P3iqk29LF +V,$HMkekZ'`SXJ,1ZirJPVI!,ee+Z5qN,@bN5,R(GATXaeVfiHh'[,D2%T*lI(q2 +*3HGk-0R8Yha1MKk)+"$J6UKRk"HhE$lSSFf(5N#i4TA9k83(VbMMA$*&I"F*,[1 +3!(J9YpN2)3G#d`NHX[0+K%CpFRkr3K1G!00XFG*a&*Cf'jhKEUFcB1TL+0DReE` +cemG(Eh&HR8c&3&j`9"SfTe@UR"#S%N5FX-bhJF3j)6UB5%h)1)bG&b6'X89J0ad +G-(A%`m9-r-YTi!Kql2ID1)PM0[l8-"U9UYkLKdT2i-mL`V5*C[YjN@KrcP6r3S$ +6U3+P%RT'rAEUZ'NS0EkK![Zm+14a'18QCcM$PX*9Q@2f,8&+EUQl@K`!b8+E-q$ +'))Q"d48BG!DS#IfjcNX*I`%Tdh9bD-`[Eq"bGLfU+LE@b6c9VQ)KN!#RRKMm1aK +%Ak@UA&QU%SaFGSCTqESF'@,pICPKSh2i1PGd#'B&dmC*-"%3+"0!eXeeAbD)'cl +Y3Y-56&BjL%8[%@A9kH)h6QMNVL#I(&BMeR`Xp(f6&Z0b2YVi1U-Q'B(K(AM%k93 +(qSdmp&iH*h$!'I9R#@9#QlhCZReJ-$DC1#(i1h34+',pp`TBa0JcE3"C4-G!N!! +S!8%#B"R')TY(PJJGk)rb8+YK`#&T`1FP!cjhK[dGI24l4Gi36C6&YT)#,33-'hq +H6kINd)-EG!G,0Y3rNF+'EY$Id0r4KPkpdJheBb2(eqGL8qImjcB9SNKA[("-k0d +!FXU2"0GAhTAp[ETkDS-hCVrPrDG5Ap9`LiZ)Zf)M-ZD9A#rPYi,K-&q4RkkSP3h +j&KFPGh@kaBjS3MVd4KBl8K8@e`JlU(--"@F9C!MEXSaMUQ!PdG`h!S3FX4aH%H0 +0"S485L(N-03bM4f%#RP[KqH6+#pr&8$cAIHZ$kprc%eeE-SXqbmFlF+469m%238 +4i[hkpB2[emZMP8,'G'T8M4&!ZlA9!Rj#Ajh6kDUjGC51MD[dh868h08bBjbN$UN +DkFeAMr"#RXGITJ#SDZR3Ci0K0jp*QpBSPE0G&16eT52HfPKTbCq)b&dkR8FS4," +CbUj9'c),JrGX0YR6@p'93b4*qQ'3!-)$r@![ih-@'Dk"'JTM3C2bJR&c+5H5(`i +mG)SjRmmY',hPI9FCiZYQ[ch[0HhRVJ)&KRHfDbKdlbYR(2T9!Iqp[ej6ULTFF@r +0pD8AK6lq)LA99,*!$3AD$#ifh5k!+f8)G%+U5qC+GZ$P)!*`P,Z-HCbkQZB,ARi +r`NjNIXIAr"YcD&1(E&Hi%5jDIU1kJ*VARqYm(%liAGi&8*A0fYZEiDCmd-B#E5, +bHYiQKq+e616EjE2GFBiUaV'T9Z2lpIIRRq24K(3i[PkQMh)N--V4YAQJ8'PaAmS +HL+%E'4llkK8K$S%kUU6%`B3+AcQZ&XCjG#KP(f,,([c*+b(G340H8%3)9&@ACiI +'qDSVaG0i(Q8(Z0iia"ejRhkcX*!!!deVmaLU3"9X2K[%lc5SP`,eh#qUCrN29-p +bK!ij5e36Jiec53dP`!ec,&pr!XB(A8cU51LUA3UKP%""L'db[5U3!)LFPcS9m[I +p%hi1"0(b9X3h6I*kDaH"qiidQCVcer6U&qHb!T5&&EH*KNT14Daf5K$GN9E,$FY +,QbNXchbjmK+@pqbPS`IBdBZ-V0i1E&+C2K1TdV1SC'V6bqGTF'jL63%0Am5'rlq +21ea@+U`Faj%l(,@F%K90kmhFNQDeda)S'6qBPmGp*UmFMAhIcN-$&m5M0100bF4 +j8RdQL2lc'$9-5LNNQX0jl-VhCAEFmRlcGDlc%39'fq+!f+'4",6b"p`e9VYXjjG +*YB[Ze(I#L!'Ce9k'(Q%cL#249ViIC*4pPq)`"(EQ@NSc,Q,*)J-TNb)T,,`b+A[ +6i9"0)LN&TjbDcZ84XkNJlYHXlLh`l&,0F-RYcTUC1C!!V[dYpMdTM'!jNM(AUL% +@NZU)Tl3ferCiUrEmA!G3l9T[k*iVQH`c,0a6NqD)#SDMNG!pVfehK)-dLETGRHe +Ykp[p5%IKq%6Rc)DLR(k2jf44cXa-ZrZ!HdF'5db@'d`S"&QjZ`'*%VK,bGRHF4[ +RC6P3!p9RK312"mZ@I9L%4dc8cY2[,[Yd+3m+TUeF4YSbTTU2IqL0q3*FIZ##qPN +cA-Fqfa%HL1cBUdCf1)V@qIbEpUkk-"a4+qr,R29GM"HeUYJ1-(*j,E0DH'a5m*Q +MbP$%QK89!R$2RpYlB-mM(f*5[fN,&c`#820*eAVk"L4'j"I``-ELap@&&$a,X)& +ERN6p6+MLCmeB6YG3(@6JI,dp&kJ)pjF!jrFbcT[f)%BIhB+EZ9JJm)Z9j)*cpmF +Eeq"MHGV5DS6CYGl6#A#fCYQLeb0peXK5E!l'h(hD*4)#00!UelP`FF($$jiaS*d +M*F'lrERXbBl#!dAD('5#V8J,jdj+N!$rk[l(AL`jQChY55FdaBE&`[1jr#d56Kj +*d0G3)$'&9$Zm%N[llYcFSZbFd3-dNTMC[eSRP@ER&18Hc8R2034q+j+CAd+"[(& +jr,$N'(9`*!+AF-9()%P)3,Pfh!3RSLH'4ePd!26eY1#bQbZdZI(h8$@,'AD#p@' +BSbHEEDP6A$*)Lf'58RK030UhF$FIJ-@C$eecSj,q#M(MBTk&a!+B4E1eeHc$)eC +FVG@`MjhL4jqiS&fX'0-fXSpaFF#`Z-!RINa2dDX*j+&Tq,I-A,QjJ"hif985Z)L +F8JL%-2@XBHX)5f$X+bV8Jq&93BLY%&**(R195aIpXf,KIE[aFY"V0L[-CJ86+jK +BSC-91P(iJc62BqPj46hTlHj#YZXR$9XMTBX)3C%BQXmRe0ja"FQKTm2Q2pc++C` +eJMBa`"eC'rG1),!qcqrEX1&@hK4J!UeVEpcrHXeC+eGI[p"VAf@'d3TP2+"YFfA +&`hP+B59EA-X@4l"BH,h'9iHPADXki3L2C-a6@5*`2im(*[DV2+qbK+hbCXhV04" +eQ&,8QUEk4IC#aThUKTGk3+MCR6Qb!@LrAmhJ3JQbIeqkV6YGfjYcp-$0lEP(M88 +-VZYljZHQrmLHRTd$H+m-jaQDqY+"MNNNq)#Jb!G`Q5QeCfFSRa+DfXTYMZb%'[N +E-aE4DMAbTN-UM-lMDYL#E@UbZ(BiiKVmY-+X4b`1LiYYq1i8R!9U6#US'ZNMI$1 +IZ1lGNQ%+IKV*f9!bb&TeL'1Na(EZ2*UaHb9hbf"ELdG"a&"-+*lX*F@626k$NY8 +qjBAM$-+MU($Nr[1XSH,$cdH['5mELdJ+bR8'4BYX,PDd5$d8V3q[9lJV@"Dm-43 +,[4Uk&0SAI#K5crl')QDm-RdbZGj8q`LZLBF,3TDIa$JHUr(h08JUK1BiMla+bf$ +6lA!kqLAbrZ9,Nj4'DedTJd4I`e5rU(3mdLT0!bB#Ba)b-#%@5DlDUdiCQMH%T#' +F840rQFG$!8`l#+DkFKqFdrLX4Q2HLm"SK$b3!2K%c)4VrZ)bjDGVIMqATX,M-"e +p$Ehj8rhCad66`%1r'C)C#Hhk*(KY*C8P9r2aNXU%S6*3A&PG-TLV`NLNH(TFarZ +@*'KmFRImUQ!!q'`!(J9A*[3fS0S!MZ#D(`E!(cLdRQeGD+9E0r3@a&6VXC3f8la +%X)qfKQh()KTHDV$Y0-,@["DeAGbbDYhYf,*UI'Td0f(,k!'%QkJj@JTSLC8#66, +NR0rTNcmHiT!!di#[d%294*8,Mr`J4J-'P*frl"L5[APLm989L(Kk%3p"A)QTA5B +)(r%4eHje,lX9,Y(BfIP)L8%8%Dp!c')%-YE96$0@3c#B3k8l(%Q48j9L&m[,3ph +X`[rF9G9-lXMq%B3#CLNrMT&djh'$L+B`%Bfk$55-B4$$RA"#U%b-JGJPQCD($iV +1C%S5+K"-mkA1FC[$Z+RB[HY)5X6-2MNlZeGEG1!jch-(MKTk3`JRid@&-bIe$b3 +LpY#MXH6UK2"30YJ[UHV$pGLSlFbK[BJU4PDE("#N40Fa9ChG6b6"D3kjYUa(Ej' +YiaM#jePhBref&+,QB9Xidf[#ePIM@R[Si)8T&!r5d$G[5XU-aA!a8`S5Cm8[T3d +`2)ClC@CQGhC4)Dk3!&-dbDAZR-4TmcbqMm[QHBJaprDG0KKfFhTfh,6EU&h*IZ5 +9#&2-X2[%%(XNi5)5dNdIB4h+)KMp,Gabq@EHa3@mc$b'Kd(,S(j!(h8)63dSRc3 +Nh!65bdUfiM'*#-[4l-Q-SKdP9(mXh2KK'l@D'L`Nr$Ap'bCd('D"6'%(`(8'U') +6Zk#[G@FJp)QS#e'[#ijQT"GdC`b25Y)I+KDj-pfC-!0Eq33pHNS$@YmE4`T06bL +XIG&Qp-8E"4Y2'+G4N!$M6EffH"JrS@MeiENd[GM9+3BQ(m@)5A3j%TllI$hr3-F +!`!Qmkh)[Yd"jc*FlZ6XUi00GPDM"*A`##[($L-N!!APa$b-Q'i@TH!+%61,(bY4 +P08h*c%D!GqNVMFFD(C-Z2*!!kE-N&F-##9dp)mR$$)3-BF-p0+6+j649,XGe5mJ +0Qj5,"diIJ8!qmBp&21(EbLeRPa$#i!@%JE$D*5Xm`%KT96X*l'I4`-#"K4iSd+h +'Bmd8I)-+'1E9G#K0M`[G$56c@l4!-SQm*RHrSGPT%(NC-NPiPRY9p1,mQA,HBrN +lRP6KPVrVk"(",`$15EirQFjlZ!f[!Gk&ea5hi"APjZ6R009PiBfCQ4cL*+P+-N' +)aibR"U3bdbNeT"mAfbp(F`l-*r6YBS)f"N!aJAimASCAM0*p6+@I(qM(jR1%)C8 +K2IrLX+f['@TGP!h91%!mZ'(#C)3HY[H%mJ3i)CG"J2`8VYIB3,qRe85J1!)h$6k +mRk!jm&ZUk'Z)EP*0KZEpDc-cf6m#2-5'm$`@Q#%ehiPCXfL-jK8E)h(BC!C"863 +3dJ#3!(f-MQZ)k9'4+f0V@1!G@cR$'Q*i914)3qkQ9*3*kU[CamaNZEkCe25&-hd +'-hPhD62CQj)-V@C6XCN-cdLKJ,mAYfTQkG(*QD-C'HN(CQEqlf61C,Yi$AEP@E6 +dq'61I!1UP&j#L#iYfEh5*DTaMl@[J5e"BmViA2E4R++V,0NT,DREV@I[$'XiFeS +i@l3[!6'iJ*I!B`XY,9SbiRfl&mr-j"E&RcX*lM2aM25P4c0QdTFZ!4-ZT4h4N!# +SKCY&$54j@1hSEN"iQd`TACD`faffbfAFl9P[[5!IjRC2L(hX&$rb2%fU[8P"d+! +Ek9&%%K82""XmVN@0"1DBHbQ1IhL"d$&b5V6mFN%m*UCUGbZD&4pjeXQYr#A2lAN +mfU4'FK15F+4TY44BhVR'S@fqaZ%,iZQbPcbVe!iehc!&'l4,HTNdRK+@2V2e3V6 +&@L15R-"3)K`9M468D#pHbUk53191D3'@lL`qCX[G,1eVp64Z1acI(&9-NH)V5La +am&I$N!!r'0#&kGX-"E3Ic5d5%4lSUGe*SE))kd)J"S8bKZB1p#0`GX`3rQ9!1cC +Jd$N'ThQ,IT!!T9LjYFULEkY8I&UPiT+KJX3,28C[P`5a1*eM,2K%`Bq-8-'548( +UHMA4NN-@NPM-BKFGkkHhmE"Ia&@3!$[RVK,#Y4'ipH@F1AG[I0["PK38ZaM*m&Q +@8,ViZK0h8-"FU"Z0dB(qdJ'rhp"4X&5`UjF(UiDlXJBN"!f%eT6"Y46'[fj4I4e +!CEqh`iZ+@IN(3Yi)rU8&`Lk55!$cpeUT#e2L!X31&B%pNIfFKBLdHq5(d[iapl+ +GKp'KUUL-S9)`j@HI5rQV"'k@L*HB9J1MfQLL%-P`f0ca9a5XfH8Q+pca'S4$QUF +bl6GS$kYrHI0601,-8aHfEH(K!i*[)3mRLD`&,%85D1@Q0%D@aP6K58$X9NHk+"6 +`,2j9KYVjC+Q!482)!"'!X!'PN!!hDN`"(S`["FS'ie-SK1+Y30P'J$@8T+N@iM' +1j6F-h'(M%AFr&N3mK0iK6`V"YiN!MJG[jQ&HKQDRQUa!U%+C*3FGT#aYdJe2SMX +0&`D*3%8S&6UCN!##)FPPL)A(J'B)#MJBkPl#'pQRKVfk94-*FY,f`LKmBplDiAZ +LEM6cR80elj52&MkalhHfdCUkTh%@Fhp`b221k,lMhGZ2fcj%cF4M(efdeB`QE#e +CSlFqAc[kj*rZrA$9RrpfejQ,&bmHH[r1dEZA2AVXZdh22*krr2FfZfhMSESpYUX +2,EFe(+UcG66822hG,cHh2(@miBfIAYN+!!"(DN&%3e)$!(VJ%&80C&B"%$%elNA +r[fh1-9FC-ZaLV2ibPQ84RhHKLEA%i*JLbl"UTU@'FQ#PX&QLXN-mAJmIElU"2'q +!jP$#5HQjZId5QT1U9#A2HbKI5h)-d)45$YHN0!&+%mSK+D@%FJNPKSTlrrjq[cf +[K[6fHcr2rrIc!"ZYTbJRJL!)J%!dcjH+4h8k[-6P,8i'd"qrJ-a9XX3A%@"*9Mi +G6"K')#U9T-H5N[qrSrK8*6e3YQRQpCN2TE65rT*pdNPT[mcX84KCHP-9P$&ec,! +jZ&eApN%NBIKkqY&j)8R6&E0+@UKSp"Qq3"`r)5Jc6$-[J!8QJm0L9JDdi!)3b[@ +Fq8X89QD2G%LHM*2)H)kDKJ+4R$UKaU,-SPH[2RP#K%aRSE6TPBT&A,)(r*&L)!R +9#0!*0RqR51iBQmV$T%`M)V`kjNrX0bCq2ceJ0IN$$JaSPkk)d2aL%ESH5Z`20Yp +4T$dCl&MJ+pb#aM%6KJhBRdNhA"Aal-TRjdPG,LaK@DmXkl&F4-Yk&bk'5-JeK+C +k93AcXPk%-'&$l3RGiQI5(4d[&c&*Rpf4kJ*6eH2kXH[(5R0T8-GCBR4NGUGmf+$ +8&8MbF@$CR,)emB(cdG5jpU[a4G!KYIZ3!&#YFiAkNmNN-kGY,$5TAQKU+ehD`NU +Ab3a4jNeQ`',`UC%ZI(lb"&M*PBH`U9i%TLeA$*AC8K*J-H4-@N044,JU+l10p5- +G6(!Pd6TG3cE51+rS6TUGk0*VU)m$55ceBG"H'V6CFP#CP5RDQXCc3b)jq"p5ea2 +QbJaD(*U`U$AP"S#5`)EcJj9J!Bj0RJaXDKMLL3Q4DVq,YeKEH3LLbeN6,K$30p9 +@mVCk5Y,j3PM-$KbB!(UprrI"`@+l16JS'aRp68bSNR4-chIK)XcJG3B"m"U*cDR +'`HBd#h'UU%GBES@C[`KHHP`3a4`cLqh-IQdrS0@m&)TRI`eXE"2q1d5,5##I-LT +FTVSIQ'+2MJLM"Y(pX6T%8iJ1a#4%@a(pBU`0d5&%Xf0f4*1)TXG#)d#3!*BAFi1 ++!dJ#$)kY)*3T8$GL649@QK,l`B$V!fUc5N2cAldH5TN1EI-XN!!ABeZC*CrkAi3 +QNZk"@PPY5)JIUVpQ'R3f$ATMa!VbQj3KSrpMQr$(($PA4A0-2kpB*'2qP-PSYcM +J!DNK%(FIdMU"SFeCIERa%3J4`SrAN!")IJmCIl#@pQIZcG`,SDG0-bkf5qZIQkS +Vdk`2Aa&Y30hqXA'Kqp$-8Dd[XIml4BHfBD*m+EfV46PmN5AU#fkGI$55b@iU9[@ +"YUBk9Gpd4AdrZr,jF%TNTJIm6rDTHM3f8f2cLaUEd6K%M8-[DKb5kQ33F3Jl!8U +X*9@RRJTBQcc1b4&jFSpkbRe&A4SQ%$0+k)&5dK"T4P%0&H6Uh$D"!R)S&MHK)C! +!41'GCLJbaU9aR5lrHDF#!N$)a6ZcZV%j3i()iNKr4*L4H$0XNrbXmJ8,cQj-5Lp +V2h)j-NkE6N2Bp%4Ac[(&YNC#Zh)PIdBZ!PDcp(,*4kkEmQGXHG!cc5H&-*fDZ+[ +m'9XHFDT!dLa*#m"a'1SpRa,$lDcpLZJ5SFad-0HrBa5HLi`#15!qPJG1H#Hr""3 +H$L$mak$U2rK,%HVEaJL@Pl5q'Bh*+k*G*!**83b1LP$MED[Y(L%&((Jp"99[IVl +(M[8$RCKJYNei+K$(kq%BG`0-UI%JL`LV8bUffT,dL2DBPCDZ#*8@ila)KdJmL![ +NjU3!BT8-pk(!b!QICH,2[dp%@@#4"rC0R$V-,,6`H@ALX(ZL'4-e9d5#96+VG)` +D9Tk9Qb3d19CI-N0&fFB,`NF,4Lc%JfC[X4T2VXE&iX%NIi!NUM`j+0N@j-N1QXb +N8[(TjmmRla-A96eHBfScAMI8)E`'e$Dm-Y8@1&E%eABJfhr%`P(e&&iPDMCH4PA +lf(f3!'[1iG@MMZ-98irLG9deif95ac#T9ah&+k8fiP@RAX4J(5UVcRXSfDjH#S2 +TA+bkk2-qJpPPYRe@JqhXr#Ld44YrCr"H93hddF44Sa#PG$%qb%8(pDSAj3XSaie +"4%JCc)DLamad`BKUaP+GXZ6#8ShDXjZhdJ[LDk"(e1dfUiFa9&E)D#S1'30QI"` +2AHI0K3SFUd0#YfUh1940"l3(@SMd53a4'#SdaRIRL9#S%*m&SIZ#`IcF*mfKBCP +S3!MC!B10G$(e&#H-2*ak6KQZ98NF([!qp5rbJ1TIm2NVp4NH-,miG2e*XhVmb4l +2*3`cl"R"+qUTCe@J$9%-+Q,j8*k!X[T2VLVGp-CD,UVP`&D'E+KSF6PH38$8hAY +`K@lcc2!B)jbiaQ-8b9E6QCK9,YErA0c)4ASmZ"!0ISM#CESF"&[pKLV4$,(k(HU +mCNc%4(X9a1TY!20GmV#eKh8VQ,NC&lVPVl!DH`Q,iEM+aJM%EmN2iAhjr*1hp*, +RCLl%Tq9j+Jrf53L(V0$0JE2$LXMqPlH+U,6ri(B4V8[qilj8adB8RIKTaK$,Cjm +mM!kCqc,hMDdH,M2ei3Sek$C(fQqrqGri&iL2!)fdKdeA4%FJ16DH$lHBGMf"MKq +LUa8r$[cBmF20caY&I#Z+jVD!#`Lb$F&8`m(81SmrJ6bUG)ZD")+i"U,m&4'rP*r +[E4*"$6*SH[`d3h&!L0Lk#m%jqXTpb-V2Fpe(5%'b$9E0Rrm6Jme'Xi3-l$`HP+l +jCebc9qi'0XULi6Jj#2c*1N[i!5)G3*EhLD3DabX1P)QA#!(Jp"5BQp8@[)BSU,G +48'p"8"r@[81!e#RmcN**&P[!mTdD'+m&F1PH#qVQca,4Q0k**)eKG(b0N!""LT! +!34f"G+0k,[2)m52"jSfqf6lM964%U6P&c5MJGR0J+m'V9ReQ0hA!UdRpbpqZUL@ +ke`0K1E'$cIPl*A2I0Y@)j'V8[6kMN5DmLFmkYH45FljhI4%e[CR[6CPaf$i6J$b +@-X9M83dF[Ja&mh-,`jbK*[!--AKf3,d*N!##bP%$Y`GffPQH$`E0G"1#4NPkS*Q +ATD3d!!E1p"Q0r*NbV*qkFCmbH"5$G`Epmh`(*JhXRqFF'!(B3iKb6e#0fZG!G0j +pA*8S'bcYIMqTiM0+Re(q0$UNJD`MMf9LLKb*PKal*PIUZ!N!32"1Q+kUd3"BeCS +k9FS[9K0BM,b#4Gem@ScL3p*9)C1*#AIk8JFhHY(3U*Z2T8CGeCfqpHr1hSG*RET +"kY#*)&BFlEK@TAXRGI"0hbd'TN8SC*ImIS@ZZ0U!,#1"(UEiD+$"QdaDE(rp&k5 +'"c%3N8%#'9T0ZF16L1"jc8N%"T!!)J!)6d"3AqiBHVN)!D`YJaf[CY0V'lp#0i` +LY(HL%133X53ki9UKB@H4adPUc`A4,KjSQA5SfANS5'Qr@D9Em50d"[*BT&ZZ*)" +G'ZMEUY0*rKQ0'8G5!bDV%`eir0M1e&J,&BY3f3BFFZ&bk5+jcb`haS-*MM#DQ8I +"$+KVN6%qXKkAQcS+F30"1m6&2GJ6LcKS1kl0`bFAj-jJ'TTCV`E6C-%"BN0Bf5b ++p)$L&!+a+&3[G[HS0FL%lifDqRkU0V,$$[qRJ'#PaD214EUd8,@fD`Ze&i%V%6, +L&D(LU5C8`-%UG*@$9HJq,Y4#eAJp(#S-ei@ZSSX9BY2&[QfK5b`@cfK%4M[d3XN +JYM2j#HKEQIK"2a2&R36U1j`%4Mde,1*'%(bEjfb3!!B@a5mim%dD@,)0,%dD1+B +-,%dD@-,!fKF0'kGKEp#JcEC"QbF01U3-f[cL33RTKi%)&J9BX'kM4,9Rjc5rSl$ +'5Kq%Ha54Iic%J5M3!mMQZC5rP`Yh+YCETEV3X,0S0MCE6P'2-cU*X)-U3$T@!%f +V'V-#PI@5i`f+(rjiZBL"!NMhmaD"BQ`k&pQ'UfSF)Z2dIL#eTJC%2iHJhmDCDJD +k&DGi+&bb,@E0DG4p(QMHM5PU,aIY$SM1((#%lPDF&*`k-)LR3eaZjq#`Z`ICAKb +!M(1MEMU'XX0Y$4RR@,+T26[RrV55G,N)9iVYB9f1%[4k0"i,eYpQCMT`0RDL'!& +Q!aAa)0C`i"`,"*,Ti2E3*D$E&Bc#3i@"CJ8T$(dG'39M'k1+S)")G49*a!iMbrP +$2Yj*@!6ajFaNX5AUH4$qH+$%HB"UG"kJrZ@&$P",h!HS*Fi$e10!X0V*"d`J3Bm +P!CbX#Cf,K5``ZXJaJef1S*!!YMBfL[#)$ZbDZUD1IRp62$YRfCD83@T!3L%6+@G +BqqlNl'Td!fGA,d**$PDPm+"!%md[&kA-Z$K[32m04TFKS*)jF%BQB5Bd$kcb5`G +PaF[PB"!AITZZH&EUb$q$cleS2#H$I1K)82ep85b2!3NrFqb0%%PZ"X+)D""a)2) +8)NP%(YEGK)!Ac$r'))KK0[-(XeEaXa8,ldQpm`I&UGYq$23H#2kQJAmi`c1XZ`C +RL!mYXcD)3%AEF"SE$MGe01RPKHA*-8(C(+XLjYUEp-aqATSc*SkR[bK3#h@c#aP +VS+Ihr[L&Ekl$)-mJD#E!3'PRXN)Xl4`6#*,2%2Jc3k89"Hmf@a'm!m@RK-L'VkI +hqp%p+lYbHCUQ5bj1XD8M!2`Mc"a0'CK40GD9P4ic)dJFZA@$Zi!eTH*5LI`cc2" +EAMc@&BXU3aeT@SfP(@0GcSNBLSF["VZf,aDM)YS91%q&U-[bU-K21LUbeU1X,hJ +8YSlYqC!!)*Ri'F#2!6mpB+0ST2h-f+h0d'@#5GQ!$R2"S$5!DGJQ$9"6PQi!6AD +PUBF)fmJ4h3"q$2M*C$)hkEZBZ8!J[1*H!Z!)LY6p-qZB!BR"kSMX9P,rlm1("2% +0#LcT#!VN"TTk3CPk!D54%#4DJ9am("L!D29bm#"%&QE'K59UN9PPV&6!pDc-D'f +U%pHGV!Yi3QB"*CST)h1C!%Z+fEQ*PeEdC+CE-Vk)iF"S#U`aCRB!4GM%IJ3aU$2 +8b%J3VqL)KJIBHY3NQ)9*D$dTLjahq5)!8FP[#D3"!bqSF21-M4&+RiM'GAaYlLl +Lr3+XaABN+G`lp6X-4AIjH-2'T0aX-AJ5`X'3!#cHbB*GLIG1heeH4BKLT(!82pR +i-4S[6EV1J@$L,Kp[i*G[d`Dd9&30%-+pbL,cA6lMd8QG'NY1'ZXQ,EB%*8be6PV +f"j[[mNNH5lBXXiJZMUYMdXZ*Ki4$SfBF*,CD@)l'&Pe`#FG%KK,)EcIkY4pTpeV +A"**K4m$rQMq`c1%`q)C#"ZmeXdD0Z0P8(Hj13+X1Z@2'XB!pM-j@G*iA#QbPcQZ +FRHZF,+BCl'3lS[YkHZFmrIP`&cc&i3J1e$L$+LA!cBL3!2Y'4"r!mTaJFSEaKJJ +NqcXH[NG[00EeX`#!!Ja*0Q2`L5lrQR[dja%),CB4025$,@0GFL1$VY-&9eINc(J +6iT@chY1i1pKY`lTTM)L1XQ)!bdE4-Fr@9)5QdU0Je(GmBPRdN!"JALbid#VMc,3 +IQ@P@ANNHbMjAkBU3!)&Ze"AG2-R&'Lki2$[RlP,1f%3-FYBQCTHCm8HlF+!"KIP +dh3UjN!!lbYQL-I[#M*HcRN+"F6-eF&QeIqA5`#rq8qM4e'V,@MBc%qj*bNa#b25 +c%N#Y`dmMILi#a)B8CC!!`pJ5E%29J#5pl%Bl1MQFRb4Q$5PCDcm(r)lcA#5`LN1 +IQ6S"4qq&iehXc-m**QH!daiG%h,bfIYd1Q66K,T6lL#GH1X,XG"P11YecfB(pic +Ej3'ZFN%$9@KBiI%T%)d3-jeS-0*eUec+H+`!`Da9bRJb%a8H#V#CF&!r$pNK,"8 +)[HJ-1ZEjUQmJ%pQU-1EM"fTXM2N%rMIMIaXbbmeJ4iC%Uj%M(BM%j3AAMi'DJ1L +f$i&&fASmh4KfA3@MjDVJ5"YqV2KTK@+K%dlXhhhKCN5L8%)akNKNjfbVEKVri[l +ErbC[N6H)BU31'd`jYbGeiF#SP!%4,m%S4%4R02)4SSjjMkkV+PFD'AGGqRqq-B& +-#3`L#DiiTEU('J#JiCm9c,%)$i,Z+I`NL0#r+ZqE-!5Mmai-)9N1JDi*lXTG2L[ +rYqS*AGUi!*J`1F@664d"2j,c(r[MhE-KkR`j08$XRqX!&iHKm&dlJmcAlM@C[`( +@4&KFGZ6j'Ab-VCS1lD'f(`S"*UkQN!"CZGqkTEL`6a93%5Dj8JJ3dJCm)-q'SCJ +PBm9J"5CcCMS!rp'a+q)!-R3()PI%H63H`*(AqEM31r`CI&pQed#!AJ''qA8Xj)* +CFjfB03LD6QC0D1Y4,SUL%&jf,N$(32)SC369,ED!T%9@,)1#8KB&TH1#@DmP##, +A1BKmHm#`hHK-%%j@*5KHj1#)M'('*8H4&f`m)bUef,(Cmaamq!I%hSaU&''jS5K +YBN2!(US6Ia8U-fq-RG*!jK&h!*(C%`Jd!b@mh"PXIV"Bf1N`#CR3FB9pN9!L+96 +MXjIiN!$YDD5L,!(KZK'I&qNc439EJJZf[cZ(B6CeSp)p!KF*L%-$ik-%JPNRll! +m,(N("X224@+4CJ5B$Gc+J!qP9hchqjU%YNm'IUl+m+D[F1X0I'J2MA9SqU6NK'X +hD`kPNV`S$`LPP2ekr#Lc4"+SeL3MUaC'9L4mq&RG)LHcbUkbECDCTahj*HNb'PH +%Y'Q+N!!'GJT8YP#"Dmk!9I8RYhK6GXSThSaX)L%J#mLc&cqZEl"#I4c$)8JDKXY +1FA%"NE!1Pi!J4!+3!#`)fG&mcP)3H[U5,!K092NCIQYRaaZUH0&hrl[9l$!lDT8 +k-Sj"3F2-4hm#1K9(1"Tkba[b8L@3!)GEK1&G+E6$*eF+H4fZlh+(@2jSlX!p$Se +&3DFj+PIkDiBkTq8ITZF*8fk-eGe#ABj0X-Mc(QrK4KH8,k`JI`p0-d#BS8"b(NM +mFk(QTP#"C["rrEJA49D4C[b$bZrf`UfTL*80d[LYcb3*PCLQIe!Ch8X+lE0"4a' +62NmD(bQFd(JdU&,6Q$4qk4H&PNd*Ce9!&*26Epk-qh3k!AaP2iEUPH*@21GNG5M +9dH54(5@iS,c4*HQcS(Jc(3U%GTk8p0L5ZX"A[FQdKjQLVA#PL*P5IQFAGU`)k*h +UlSPG%*AF8q8T'&STN!"[S%!@#dha3$0PNUB"I$S(rjAF+4iL*[TKG`CFk&(mDm( +!d-"pD2QmNpIpb*)Ul)dE8"df066*iXN+&*V*f8"B'')*)dM6SCJIb6*e`N'5FY# +&IjKZG+!c!rJ3-Q1EUFSMXUPVR`iQ-$+!b&3MF49JIdm'GNd(9!p1YX"'Td-3XXE +XmZAijVSrIZ'Sji($$r4G9V0clXN#m+(S0[80Yb,Vbq!J3LJiXh1qDSE)0a6G@j+ +1!N$r0khZ!FH3!-)LdBFEG3q%r1*RaTE*MJ0K-,*iNY@RD6F8!F`fj[ZiJNFMR0( +[rPG0(lXVSR!CSUTSC9h)2(leA!X(&VfT!mT5IibVG6C`"N+h1$YRdpb8S55G&jb +*#U"p@l,(p-`DfI6P#$IE*c3E-9!E"LpYDN"P!$J@2-e94j&jTFUJ%0EBKDNTcUL +%,V%,cXSF1,E,RH"%-"-C-DJHG&[Kd)),L5heF2l3HH%HC!!ClVNX)#lZS-il))B +Z)9E328"'GQB&X3["2-T5a+6,DMd)HbX-"m`G8Up$BaEcKjMBfMhDRRPpKAc)$N* +F1l#C-*Jq(B&B[8MX4q@p*HLS-3h&%KU`caQ"A[0*kX&Y3S9)%CV4D#d64IMaDIZ +bHL#-Bm!&2F2jP(cR0Af4*$28[q56%a"CJN`+'$h5rUL*'8jJcc&+CA'q$5T&1GP +6YSk'54he8Ur5%8i-b%3QQ4hhT3*1'(bJD2l5F6KIj(aee`4f&$QLJ4AMH&YmFpf +BripIS))h$8%"&HBf2F#98#'3!+HCHSCEkjf!PmQ!Yh'U"H!GC@6CiR"d++"R4e5 +)Rh'@cYJj'IKiQDGa*cL-)LRUQ)'b9HUiQbSC)D1H&Q$J@1Ek-5VZ)31im3&d5Q2 +`Bb$6p$!"0KV@!R3,0$d5CAqLqfFdML#46$fhc*`i05,$a`P8TllTUdiKkeN8Nq` +NjXA#5$"@LDY5r1Da$#M%A5dC2GSq5V#qHAh$43bkFJ@@p9[@!XSZU2f8J,$q09E +lmB,&K'JE0[(bP-#BIEJG&B)#M36#pRj')'!S2(hp"JHXjPJE#+dMSXc9q9l*imP +`'L4bJ"&`-YBkZD2*6"e4*FbB583cb9@FQ8"0$4C"TefThL`((R4qGRCG9k!9JrC +5FMZ2pGU1IAZ8JCZ6q`!kPfERE0Ji#Ga-82UBf2N3!(H8!&92J(U6UR"beiLQ*d@ +JZZ(,NcVE+BP-NSH"&@#&bN@-+$E-CH,+J,YK[KY`0hc#J2ZP'ShI8Kb!baa91VX +X8)3@UqmVKHJ3#4mSh"m`M4,c$-TC2p$+NY49pAf!5c15[80NACHAGf'TMUH,R6` +)-RiYF#**2QP3hjH6F,4,'5$RXTUR`LQ&R8`d+@B@&+6UK0&i3#BdL4LHb3,',iG +'C8C,,`XQQ(#9"R$)9iYTZ!-%e#``B$i'Uk"aU9&@*%,%4JBelChmUU9IZFYRkSR +&&a5$PEJ383P((9FZ%SB!I0c8!G%e6*[BK-$BM'*),q'JQ4FJhVl24,0EZ,YeMXU +13C`G6#MC4a409i5VkBVSD@TJmCBckCaeK%"afTCaR-i&HBXYkrKbqX%#"'[IB"F +08c%+!5$@D1r4Q314$eSL"VFTJ4HB2M$c#@c(LS,6JDVMel#"bZG((c&`)PKQDYc +1HVU&cL5hc2cHk#,"c`@bp)$j0Lim&YGIJ#*J*YcT2hhPUR$TLS(8jYGH&6fkD4! +#&JCAcM#kXm@R+)2%fe-UaPYF1R8#B-+&,E%q8I6E'TYULjCdcb*3YD*"6TC)+bF +J-Z[GPA)f(*QS1'r)b0[kjX4,@9a)2m2Bhm&EYNp`6b5'(LG[[XQ-`-0UX0j8lC6 +U51f8i"AYP1SV8*0-#FUL"4"$MVKS-Km(5&cT3f22P#"-E441UBDbl8B`K+be(Je +jD2#aii10+EqXbF0$+&-GF[8$&YhAa-$m@!CA69CAP6N(@l@#83RrB%S2@#Z,&`" +a!+KEkB+G*J6`30Z9`LRP8h,aUTqbp`U'Q9+1`q[GeEV"X1++D+`q(XMmb''G(kC +Ue2'hrR4lF9#0R(#c0CN)lJZZ#L)V!*)iTNZe8mVIZSCAr9[&3QEhSQ&9*V%eL4b +16rPD0VEQ0KU-K63f,E#Cf6$%D&59T6)j%TU2b$Ej`l--bE10#`Ji9U'+El"M4Q- +rh-18,PrQ!1iqJ+[#-ZX0`FbU6#PJTh!jqd%,Xj9$"L`119+2bUL(F"#'Dc%k(%2 +cIkJ4R'QIbQ*)(fA*89$12hClPmAK8BIrH,UlbU[XIKG'"JaJi'09[f!A,$m8hDU +NIY$*JRH!&9e$8C1Xb14Ahh*b[V6,(@Bd'ZVp("LCjCaN&6pFjr5-KTrSBD4eCAp +P&X6DVJQ1cieFD8b`1bP3@DZ0J3RP@&DJQC+&QTlS2-k1TPN6%aD-J`)q-,'ASd# +P3pQ9%-5d6q8&$Pd!TjLMIrf[@4p"b%CdGXkQ0'60%(9Fc"S49Pc+iFkkb8",e@D +5%ipK!Cf(IRi[!fG*qUdNS4b),j,*PRc%KQ53!)3kbMi1FJ6l8eQ4eIFHQFVUrAL +Gq1hHkhiYR%DNMYrZPE)H[#Qc53,edQ(Y'BeIfa20@Re6K1dp`K`lIPR8#lMD&#@ +b1-ZBiLcMaG%@GYC#SP`FID)mI(IhD4mY2rkl[6i[9qk!5rh&F$MAPcTGXNplU+3 +(KM`FYppK4UH8"6B+-dcXp8![#RYQVi8J0pqCJC!!Y,@CZK9iJ4%""9krT+eah2[ +a1PpYVfiq+Lb0kYkXYQ[L-0XabX+U88mIpIb"bK*F'I!e",-M(*!!dPB!C5l+%`J +!#j!!)5KP!5bPC9,3GRjSfmi2,EEc3pYfiYM1$hNlB!+95UG,UR5PU+UbU+3AefM +@,EVp'MDf30,+'e-ICA8BLjfCk4%Y+B,58N*ke*k8Xp6NS&(fZ#pF%KQ5HL60H(, +(AXZQ'R@0,ra-hC!!G)JEA@D$@@jHibXiecJN(HCPYp%',2m&PdNV`B'iMPqAGRZ +2"6(KMVmUc!8P""Gl1Hb@4-$UA$hI8')30d,D3iCImfG*5BNB$fAe'YlMci+5!R% +dG1U`iA2qV#q"le+S"+jb-"Z#KDNP8l&`cNaCV6+B"K+VjbG,`PJm,Lq@@LkHPaF +AZaFcAABS"JU&EX6eX!6%(9kQA(8ZNMh-ef53!'pdT'!QSLk#6#4-U%LHAPr4fDK +RcV(6[YVRG&[[r&(FHmI96qSIm%VYpBp[(ibdrrA)AYq'Nj)I%ip&rA01BZ+[3)c +51lZ&2qlGr[R[6fhb5[&,eTf$dALjR4-iGI#1D9)$1Tb-0Xc*T3j*h64d5-Dp#5R +iTBeHU50[eGE"QSlb0AGA(I&YfLI&-6dAdmr5G,XZ(p2YFHqlEfaHFCGAmPc@EaZ +Xmj5hh9fjalF4#YPHAl5Kb"[Y8$SiG*[3`4(hrMP[9F8'Vq3IHIcH`C6rVr[1qZi +k0Q',-*DPfil*eVMhm*RhkZji9fTiflTLX,%"9krif2I!5DQG0YUZE$5-DNIG)Kc +h$QTk9p0'9p&'ebJErGbfd628S99AK!kYFHr5hRp3DD0kfQLEa8E2f$BUkGCKZK6 +hrXShH`0[P%MZ)C+ICC,60[h+p)4Z!kBRiYjp2FpAF!)4dGYPSU1#IpPkaC!!!'d +B%BAi%GdZG)c%[6VGApkL,DqL,DqK,A1RGL*rqaam8*FDh6*dUBPl2cTqacZmE8U +!Z*)!CcJ"LYJ33iHbEBeZ,MTSiYllEUk*VM8"cYJ5i&(G9%aqDrFq[rPfj-Qd +&%ANcNB2VTdMfAP5b39,BPD3Be@e"ap'ipp0rkrZBYVq+YZp-LR@F&)f#%b0KRh1 +D1ShVGU$6H0clmQ[*TpD5(!hBYTh*LiT%TA([SXiR$e!b2-(*m%Xj'H+8B,NdDA( +F@f4qhEcqAFPqk6FihPkqNSq2([cl0!@%9bV$,BYl8qI228IN(L*b0a1j2k*NKD1 +he&k8kr1b%d$Fpp-&RaG[mS(-GL+c95(c5L,F5LBFKY`Hppl9j2Kh)Zm660jIbZ5 +9!GE$5B&TZq,H8)meiqr[5L[TLL[P+a**1`K8lFU!1q,H$4rFkb95$K%TQjA0V1- +NBbF[D@A45EVNTVM[YhrrfY502T!!d%iNY")*2q*NSZhBLml5e!eahe1rqkpl@D% +jJB`V#5KA+S6F%[G1q9llAC-*5BM$!K9JDRlFZrJl2cM$'b*#VP3)Z8m1(KD%A"I +h(6TR[AmYK'a3!TfmmDPaErFrrE6Mri+3!02LhV+rIVH8%j!!VYKJ5fSR)Cd*8a6 +h"Qk&eM-KmiC!(#DN6"b23Tbi3XDjFGrhk`Ef-"RVl5!1Nr%VFP"r!e2ImXV*V&[ +b,4FK[9C'HMrp2MB"emAriB['DFUkZ2I-2brb8K,[TL4Z8)MhDkRK$lkSRkB"!![ +rrep%1I"b-GFB0aaf(5E#Y8FEA[G&1fJD%Q,ThD0f)XC+*NEjEqkH"A+FN6cr![, +5*!5-6plCeX2"Tlk9Vre6T8$i[5rD6P-faEhl(rMrZSPBZiPB$ECJX4)App!d%'V +4(rBEQ9"mTCTf3kqVP`J9MkjmhAPa!2!lrc)h5!4B58Mr0dkNEb-5NM$brllp(Pf +lPDrp8`A4r-&*T+eallB2(PP#B,1E`+E"LGjrlp`m3'[,AcA(R#5+aJd(A3ITfTp +,+rr&H@d%HIrH[h83mPK*S2mE*ARPDmXNf"$hVYq5rbe#(N`Ncdq26%VBZA([[9r +kjYd8h(C6F'Y`&j@fa%@Jl*U49mSATf4E15RCC#)"1+GpjG4Y3X`V#G4ri`Cehc( +P8L$#iSHL285Q&LE6r6C!pje8L)"%kD[6rC'5VSf56Qq*,U,fd`M)Ve1eq,K[eX) +,2hi!!Hl5'YV!UJRSBL@QfQRUpVL[l0@Chb*Nd8!Nfqe'&VkcES,FrXXc8iKN,8b +bqff)`THVN!!$5I5[pRAA#%fd%CV3@k+*D!-1pG1KZk"kq1[Lrq2[48M`0C6JUpa +S`RI'$FV(F[rp35*F!a&ZpeS)K`"fDr$Yp*p)1#48@Xfp1bB6cSNHL"`bi4E(IGp +9qka-Z,`eK"j@ZG'$M4`!kHcNQJmSN!!e%'VBVD!'5i)JS$fImICT#K`Y($MZPj& +$R+j[#rK[h&Mq')235"ZZ[a*AQd8Em&YX!#"8GmHGIf"5%-(L#X&1m[9"-)m6V(p +jpZmrBP*38[U9T$ccTCYI(46qH`C&mSj"BBHJ@6V*35-j)S+V)2)2EII1!HY@fiI +-3G&6AVp`Y&aD9H4pb[HSd2ECqb#XEUJAPe&r"1+ebJjXc)5pY1UDldl[h4mGZRr ++ZZqmDp3c`jFqLU69ZrCH@[@Shc!R&FSmIHld0#m2R0$8L,[hj2S5Th&S`jdRjrK +Ue+LQ9Hck+1%jIRUD6jRJrGfHBpHLRP1i8U-bS9brjD4lJX(PF['86%b*((31SE8 +B)KMqAI'aI`cfAeT9[K*G[G(92rhSp2`D-FHAm&jDPI"Ni8T43r6KDZ&Xq0e(*kp +KHM00Khe@C`-M'fj+H(JEdBIAfCT`52I*DeR(N!#ekd%@%H*mfLk,6!Ha8q3-V'L +4@5YUA'%Ei%2XYA3K8M,XMbYC)&3(NRcZ"DS3G01pJ#T"&TrHfHk2Ek[M'UAMcFP +AN!$@Z,08ML*TM6[EaCr1$"Sq(RF[IeXGP#-G2MGMSiYC+P$(6PQA@TfR4MhPAEZ +kjjbYm@`kHm3,&`6rC6AUap+a1[r'BhZmEd2pZRd[JPXV"cITmEGbJre&Z3I[Rck +3!0,B4E!rkYrad4cI`IZR$TbGhbULIYG"`d&HH%pC+!pM-#a-Si9)4r6aeh0jJ#p +J`A+!2ddDi(0P3HlqDlPl[+DMc[rlBpV$pX18LBY*(6[h4M[+Z`MT[5[&m4QR$qr +!28Qh-jpG8VBFH4NZ(ElSKYIAh9jL`4DE0%&DEcP"9im+Um6-B$0e!+*k-%DQeqD +T&`i&G+@eH4r0qUqr%N12`+ZX%kB65RmcV(Y@U(JY%5'm[LBmH*8*2ej,43GHD5+ +1PdkdMl!$YGiB5Hb(%i(GB@9PceXYVPB+r-PU[mNDf`D'+"Zm5Sf!BB'&,A"Rc&F +Z%Ld"fkT9BG(S&V%C3kl55&8E&9BIe(hk*Mm8T@G0T"i*k,[B#2*&X2MK`+!C5JQ +SrC0FC4fUb)Y`R`M*3!-Q4TVG,"UK*US6cEbS#KM!8bESf$922!EPlqKF%F*")6T +NQ!BI"AX8lV"fGL4,LcP-YE1@J)Nm2`-40!bLi3Nd*&hb!S`$`9X198B4JEYNl*c +l`%"f8(`36E&ck4*ff@NDccT8NSA0,qPH+X(&3EI%lB+8kV+IP"h18l,$H8EIrE1 +qaUU4*rTQ,@&h8'-0MZM5TGe+f'Z&EYD5kfeDA!%"%kq$pmpDXUD+e@R'1*1YHfS +6Xd(p"YQeH*'KX$LVqr[dLVLdVLb&L(jANlLS'(-X"j[d90SL`h"Df@!jXr%'2i- +LLUX*LINiiRUR&P'U0X5-),&QV[K[B@Zq[G2Tj)%"PZ$(iXM"$rM9K'Y"TG#YU5d +qC5M+m[@MXReaLD(S'iJ1[ST,LX&F0RQ*b"%SqXd2&Q*L2C6k"fL3!"RiH'S35LK +Flc1kEK`JXa9%C5EZXX&b9(&1T%i`ST39-Ghl1)%-pHa+#V0Bl5"bHF,3K'h"G9S +Q$60jYl*KJHl[bi[pV'kZIH-k,E*a1E!bhbKJ)RErL!X`*CP$51ClQH5%!,P+NS# +lQ,ll*&MpADai'ra-RXE+K*H,%LG55Q"-@qaNj,I99[aF*MAm`!XVZETeK"aCh+l +VTk&1rC!!'dd4pG@!#a1qVLK9aq3Z--%k`P&8GDR[@j'f'#cEiE6&['3S8"GQjra +fUNbZS*V*UPNY0lQ(LE9#8@0RiS6BGIVbmA4P1T4RS45j-B#B-BGNJMZ)F'l6#$- +"hlYQU&r*UJ%fE**RmN(*B69jf26!pc,GJkJlPF9a9Zd`b"#c'3$$9BfTHqA6[Fl +VbX0p68Y1D5Pj@qT#2KVZTBd!a15NU4'B3R*[p4e@LF5QIRZ%ZAFqZDijH'N0!S, +kDMdEc+JMBUJb-E$3BL0qf),iGLG4M@aIr0XQUGMbNdJkCRD6G'QG*8NI5l-Bj+D +bZ1f&51S'*6BkmpX@HG1LALC+%jXk9Z8ZE'cjYqF)TEB&1QN`6%CeUBA"1%b9@j, +'BGZ@DVNYa`3M%aC''k4*4KXm-+U"J"l32#1lZ3iC8iTD*SN"V`%ifmB`aER)KRe +rqaUaQ#f-rX))dVKpSqD`P2bJ%fBbcV&LX@Nmml2EjDaqkjkCkNS0)#Me*6*(e)c +$K5"@F"@c@%'b*$Z!ErFCrBk@PiZBU98$pDA$`dU90S&U6j%ARMTEY%ec6M@1f6[ +%UTmdIB2Pp+5Mii6JTBc$e8*AIQ@J-LJ0!+J2i[VP(!fG4V6qT6c48*PhKBe9kbV +ITSCVD$M08FrI%6h,d3iVSRX`q3H9(r2N"PhP6@T)S'%K4q1h%Ch0dIC2%AdINjX +VCr,NeEV+'G5`!!fIFM6j2+,&(0@2)PT&PhK6ZF4Ve'"!`cb10Rb%k&b1fUFM@N5 +6TbQ6hl1XFL!jfH5UX9R,&3)GDpi#,!JY9caIFc(%,S@hlV98D%C)b%eZ"i)h`)Q +UYM+9AEQSe&6Eh5HVkef&9DH-@6#%N6,PhKT)qEQ!e#N&T%jha939Zp*AG49ANUS +Z[94B1EhU[+Zb5JXe9@ePh&"Be3ZecM)HLJ2$bKk6J00[r$Y6CTmFIUfrYZS8&M@ +m''"c+0-R'%b"Xfa8-DDF8&4iNNYb4+h)S+pjbj[bXK(QD)e!eM6kN!"!YZb4Je! +&V8QXrJpI!L,S@kHMB*c$THjd3,pcMcM0f-%BfVN(,[hqe)R)LFQQ*"4K[h'YbUT +Q9P1Y68R&4'`2mb@KT(*F&Z@#"H1XI6b-Dp!IadrSNSMZMhSP9HZl!)-2NTV&ND5 +NRZ+)A9*,11+3!04c(,&+kR'1K#8B$%@NYHqR3XVdfVf-U!VRjI&JDj!!Q)q@L(c +[&PrS0)XTdGGVa#2H'U%pPR1'(FT,2Y+HYCqP,RI+cL3#CRE#1pbAAX@%*9$KLbG +Ur+&)M6m1"pDpaNEYDARB1Q6(0K`,83@m'Xf!H1SBIXl@3*M22mY'0$RVp[VM8R[ +''6K@6Rr)j3mpe112`lebQI-JJ%+4'`84+Lm3jmJ!rcNiiU'C2V*0(0AcK#X&`PK +9a)R49)[11H)'&a0341Z-N45@H$Y0%E@C*b2##RlTLVQU+*-VpqXi+RRF+)YG9&1 +b)A'ldB(*"SL4cm*JK!k)r%1q`KZjJIL#bPPlURGqRBSb0L*#kPbUDR9AjDb2P@P +F`4rSj8IiJ&+8PE#eP9$XVQXKYam(AG(P(N4@IV0T%NabjCr-6#Fc9Rj8hj)(Y$E +TPB+"TdJmF'Ek4"-m6M&P4-#9iC6PPYQS#JRN1ES@Q8!`H($GA3%YiM6()V&*PiP +Gb8M*M,[-9-MRS(SP9eAM!4*`E4lM3Y3XBL&YhiZlST!!f$2CT0'BA'N`b5iHX6T +6BGA8J!-"HbSIdhMk*A29HmI6X6`03DLfq&ZSQXNLA[[J)kE+#Q[!#QF4N!!5(Gk +6#LZXf6IKS"A5H2Va`!+&%#4J#X'19-,Hr4ZTbqllV2akM-dpp5X"&pXT4dHr"J[ +S1N6Gp(4"L+@TB6NM3mIhdPA-R'&jcKFEj5`#98&'*31HJ%68`q@q(D*`C2+5+i" +V6ZYq'qK`'Ll[#,!61JkJ`P,HJ%2H3&"dQGR&)AA#+Fj$pBk,"P)34r9`%1ZNDh! +P%KL0%AVh`'cDUU,"f3d9B@3(DqXcr'J(pqE2Q@SV'J,MMYUUE9c-'AaM`S8P)Ld +l6La4K1VP%+LjUNpKeEEZmkLFUEGpRZ#(466C#*LfaG'&5cG54U644RU(E2kHYmM +GA)pBZ0UhQ@UVTJGS!$41arEYf6H0c8j%+Lr3-%PX4DYN+C-)M(CkN!$$32TK)R# +5$IYX[bdEq&RfLhl4260aJMI3l8KTB4iL"$G1')NJ4diX3i!D-K3p'N(eQcN,I3Z +pF2(HbrpAl-dr[G1(c1B-GK0V+'D6A4!3Y1j2lSj("cJ-lk+l&Gfp#hd4VFD+!e# +%m!'6K`068BXKClk%kS"9dr#+9Nfp![1I&9Dm)K81UMMSi1eMbkJX%hN!!F6[!Q% +i14$CPTe6Ck4Y3LaPTm[Z%`S*je`j8@&(TXCRX6K9FBlR"lKmdMqqr6D5DbBQPH- +R$i!L%+PQ!9#dGfpVUUf`@hD@HQ")[PMUJe'qVa"J`E8+9aU!N!$j#DlPSX'8!c" +3%"01pBm[q`8qkM'N"d0kQMMMG6`ljj(TbK"'$+((%+IB,GIpU!'JGFlD"4!!@mQ +P2facklF9SfPfQp-*)9aNMTr&Xi&b)#Z,!%bb!%6`!!`#q-Ij0HYCRM+KfQ5p1Q# +DT`i%HUJb4*`B$(&L-+5`B&@bpLhmZ#@&lGE)Mab4Xph)'$6r4kQD'BJRY"#Fjdj +%V,)j1MbDSl+LjABGVP@$)28F9qH4'bl9jRZ[Q#YDM-h1TH-qAR!qYU6*Bc&%jqd +#)+2aVrd,1h3bD3D0E9fa835"'hJXbQ8T%iiI)E"i#N3F!!`h6KK#5DX1Fi9m4-Z +UQq%H(pGYVd8(hEB4&5)YCkAaS*XI`(e``J8k,4beC!0X-R&K(XcS!MZ(Lp)Ef6N +2A3BbpXAm$Sqa4Y2AK-!lD`Q+(Me(E*Pb"U4MR*&3b1mjRZiF2Qd(Zb8k$I@Rj)S +43e34Xf95!FHZ6H3k$aFcVQJDIY'%&RN#3'NZ'@!DNee)Ki)`F9GEN@#"D[PmVZU +*+VJ`[X*9j$P6Y#D,hCMpD93T*%)6MV)"-XX#L60S6L-86jJV%Xk-BQcL8(SD#ND +cGjq&fB'0,0M'U0)b3"kX)+ifL#c-,bk9JdJ464HC#IdbLD36"R,-SB'UL4"#T1% ++F*-fe(-QmrQT2#bb[Th1J4%`3dK'L0*24j9"mIJKUXCDrC+jZ2I+eG$eL[0i'5[ +10HPG!@UkVer)KJANL1HN%MQQ0&3MmS8L',TUiU,0`aA)2EZ#CPj#%1d-a+pFpEa +GFBl"!Jl3$3M1+CMRFU%!jNhEUI)RVV4m)kSke"DEjPm0&34UD(V+kCkXJ-CT621 +"H)Zi+j`Aic+B0(A`CYRjGr8"5R#KYY%!H(5C6"!kDQeND*A*X,4(*S0k6LD$UQ8 +bU0P-"JBCDRa'hV$k&i8!Tj9)VY*`$Sm%)e%(6erjRTXBS3fS@'0*M-X9j`"UmkK +DXpkC9("RR8#Jhd54PHk`5-5e%!S'lAiLS4bE*a)+alU)&#)d5S'd4EPNjI,CR+9 +Q8T6Gb',f4KN(J8A,6%*fji@CrdpK(+%B&@5QXAZXdc'@6%-dJQ'aN!#c!dj6+L' +J%YeF)+m,Z2LML$`CD)5cEKe9FANR%%AKme`JQQ`%GT`ED!N0TqQJ$KaqD`I8NIa +kaaLA&a'd,Z+R%3PehX6QXb4%"b)'*0imr,b'kl1$H`QpQ$PNa6'GLPQb0VVF8H9 +b@Fl,e9j9fh5Em'V4lH!XHShi1L0`cebBSq-(HQAR*0Jic5Dj'NFLM)mGQHQmFDI +c,4d`&ML+E@3'kRLEXF3V2@NlD[[5GS+9G#mMNUHZSLL-i"1$228q#q)33Tc%rN5 +Z(K0S`e$Y-+M3l!4-E1046CGLCU%HE1%)1HC(')@`ZC1EJh!H&l+!rkH`,1$(-X% +SMJ4-Q(i85H3L*QN0M[Nb+S-LdkL,N!"CJ[fkSr`"Plm"F[k2+!B+-V'eipKD66! +b`dM6,mJ)%5bQfR9IP0dbXh2XMb,BlFe#%,XVFk*Ce#i*U(J)9G9DBDLb%kLCX`C +4b81&hl*!%J*+Z0J-!D5`f1`UV%LjDSX$3A[4bCLG*S%(J1$kk%Z(+dZF#kJfG"L +,#CPpTViU#j2MJLXJ1Bf+)G'5L'3bJph8Jm)'CJ6!DN&Pr$DZdQaM"hCjd*b8$CK +!Q#0$+BE-IQBSe9DFGaB+)-B4"KqMDI*J2+8V!VCLJIS2rh1$8F"-iV%R3!%rk*@ +CA&6PZH2!Q+h+-l,Cq0r)2bmGUNJK@2Ib`kf!3&BKfqMi8@A&HFdK+FN4ia"0FAi +b@k,%iS%`'jd9"YN`-*+JHB5I10,eFcqFNEZiDK-A@CeE+"V[4b@+PL)#S#&8#rN +3lUFEYIYXdkUdI94!ie%MA%!65h"MIHGXm`frTJ2U@6'%`2%QLVPErVrK`5mdB3H +!D!HBrSf`S06SA06kH,P[@9,Ji@-dc)a'HjrRYr6BVKTHH,#4(JMS[&30A8U2ajZ +8%q-H`DG6#L,SZb[!,68i9F`X"M5b5r'N!MKPcZ1-4i)GQ-(J,A#b8Q'+T5ZYJ-e +ISM),SR)4d3%63'N&-"daJS3U8j!!,3bN4)!S'%dX06L4h0+YPJKZk@)RLS33N!" +Ne,Cd8FcfF+qP[f5d&d19U+@r("%@8jZ9c$!J24!D8Ir@Yr49L&C$6Q5eG+(c%,j +@8cZC%)m5)eBId-YS(-@pPP"'1dm#QB*)TXDaVS2B,0a[C84pH#+LCY5qb%K-,Z' +h(X4Q*L0Y%kiDL01eie5Gp"dX[4VJb3YIK%32@e4&i+(MmY#H(!9j`5c1dM*'AN[ +,eSDmTKeAN!"APJN6!pQZUV3KS0!f4PjT3N&H&b9dCq3PEbaYL"GU-p-N4Q",bf3 +%PTDS2CbQ`43K)c"dZF!XiMqA+eH+!"3H[S(#H6+V)L9)c(q5`B!UUUFCl8j'$$k +A6@$#h$Z"#92`!NbB!TN*dqKaXf&b&[*'Q8L,GmTA#RfiGQC-cS,m-cqC'C1c8'E +'6'D!T!M!RKF6*KI3B$Ra6db(L04Xj06-TJAGc*K'*c1Q-H-3YP91CTYc@VK+!a4 +mZL"!M%h!I(`%JBN$&#X$)"TXYTp4M!J9Q2B&f$aXh#RUXY,,"S)!60fd9r+!c#D +!iG-EC8+S(c1!SKTT+B-6M0K[UKeHDT!!#q@88LJ[X"A+V6IGKA*VVP`SVa@-NGb +"Z"L')Bqj,`c!,`Cf5h"qHP5qSYL+kMc49hU@IP*lH1QR$)B)S-9"-eGpB)E'dNm +JTZ9"D9eC%3Q-Fi%2mPP[#&5l2B5+!q(aMZGkiCLI[+A2l&[3@lM%#1E9JN1&5q5 +TMpHld*("Nif$9G3%fR$8!)l+aS3D$$q!53H3!%QqRR&S![K9bq!ReFlk'#EjUMR +Cj16#ihG+h3NChJj!33@`d2$%"&dqcb*")b3'LU9@#`F62&+L9@([-81+c3Q+c8L +3!"(mA-+R&K(T"+XJMcb(5Tm!L5*Qfq"aM8m-YQQk4PMe95A"30YEpeIF3%,2`)C +Z$(lQC'V*5Npqk+*G2k+5X1QcQ&iZ6`ISlU'KVNGB*8qXAME`aTAqlAl4#%@VAcb +"$Mf+1VZfJS*6plrcJchKlV"#YhMQ5e%I0lJ#hImZSdiD%L`k9!+MUMhG06HkZ%L +,S0*R$&@IZD(l&6C($k&UXD+UJENVCY,)&a4EQ&N&K4[-pX8Z)X%rV5dFR-lCj-( +0p*Lb-QA`lGe2ZJVI'-"2YJ'4`5!QDieC33mk`X4jbc!UG1PPCjl"IkD(U*BUjQq +@G9GVZZ#DS'G6995j'Bd3l3Z8CEZmCDG)rhJCYMA1C*,S-C%C#0"3,Z3')Ucb4T8 +e[RLV6&Bd`15k1`RdHcN*('aib&Be5-NJ*a@QLF2#Y8,*e))PCl8a!p1)f6VZfHj +NRr9lq-'mJBZ+DYeMS9L[IF-S+mYC'3j'VN@(iqQacCDX%'3qDb[k%6"Z)1Y3@@( +')kZ4P0eI4k!amfDkVBMHN!#hL1T+5@6Ip95*$Jb[aiFX"ZS!`f2X+B#c@)M(Nbj +-4-6lVhPMc9'$p*!!Q!QcDl-"hYGHJr%,RZJD&*p`*'Ui8#!@L[IaQLeQ`J$6l0I +39&KJ'K3,Ecf+LK-,*94JeLf$m0S@$Hh`$Sb(fD$'Vk@1(Ei"FeJe$N9$cX@"3eL +!US89*H,AYq,4FCU+Baiq++DcmQ8J!XY8#`IbNY3"Mr(qT+jV,4ZBrErM"RJL'1$ +*"`[%cE8XAV2B+#p99KL3!0b'@d!j2%fUV6!m1#K[r8e%U[eiX2PXfZ,r0X3!S"V +UZLC92'X'@f#Hfh`A+b8jJ2*3Bq0UeiF&iQ-8j0Abp0@Gh'#2`(Kl0BZ[8,dMLZa +RHS#0fAQiQDHl!NhMTj1k(B2r`C@4i6USiV'q!a#U#YGP$aiClH*L4*l+,M6cBC! +!i--H)a%V)b+U*cF2hmXGRB[C16[(-m'+Xc!KCVXb*Jc)9aeLak!N$a!(UL,#Q-A +(NlU`HJp0A'a$D'Z8'IQ61TNXJ38DRYNf96p3-4l'$+DN+5+'Bif%$(B$D*!!0F0 +($bVP%V+$S`eH%1GGcJIZGJR,*LjLN!!%1D&,F*q%QCN3-i&2@D#D3KR41ihF%5U +5k*%!k)kY'S#-rFFTDkTFHKe%,MiJLJ3q4G%,,bB@e)cV!X035Rq[1!X[II%T[&c +&*AJp@(`1489@X99fLP*)ZePHF,T(8H#"Le4qJCL13kH#AA9+FB!UI10FXI8PZ%8 +9Rf-e[,2"D(VKibBlAp(5GpGbL$cCpJ!$0KS&JX@GMRP3SGC@$-"N8j,0[pUBd$# +8FMXQId+a-##6!BH2dR81&TI`85m9L+R&Ti)0I"`I05B@B#R3q(#IQ#SCL'$XPVL +Nq(&jLr*9E-5-8k29cB,'d(qcf'kYlCL2P@em&pYp#1jVVcU"Gc"hBN)KD'9JB%S +-pcDG3pZ'8iCr+8ppb$NdN5k#`&Yd5Cr[lHI#p#(E3G@6$m)!+bfZ'VCGe@4a9D[ +&95AR%1`+fEU@kmT$fSC4KX89`jCA()f)Di@2M[Q"CQlHm*J1&8D8l2iLcZiVQc" +CE-*+$RL,"R-Y$2%YN9d3+"296`MAiBU)!Ia8idGk')+r-0$V'P!aA)N"5MF048# +*#fPj1L0Sr-c%crX2Xj%h%ldqaUY"6!@l+@%[e1RH')KP!KL08)I9&PpRG4KABNZ +l3e*Y+M([6Yp,jMHbh5Se6$d2G9TYm3&@TehUhZ)Gb)Z%f0MF&Yp,VMF'D'!Y@,% +3c0l)I!N[*!-%0B$K0r*"*S"K#3H!Z&PpL%'5!cp%[f)@0`$)286bLr6`G8`RpF8 +MpX*f8Abmd"rXB*@K%(Sm6*p%)$b+[IJ3ZYd`DVlp,YfVZBZ-l14NPP',M&CNe%4 +%MiM"Kr-q(a@EkC92T,f6Ji5-TRi`im9S5N&'HV"6Me-``L#a8B$+$`NB$a0kf-6 +!SrkYq0`cK03)5C!!J%82EF+V1`YA0"XCkF+!4IF*"83(1BYK(dDfTG8jZ&0dFek +B46JBV$X$`,)2lV&9U5kch#l8dK$T#,LHBp9PaA1-mR'!ScXb%6%lf9kF34SRpY[ +c'r#6$hDM!3kHceiTV"bU$2(V+fPJ-Ni(ff`61`kj#VZcm,2C9GPGMCpl,9P[!5M +Krm*'e'ZqcLqi%SIH6S(&Cbm8rXS50RXV'd1TmdJG(h4bpAH!arXlMm(SPIKCDI8 +f21kRZ@kF2pK3#c)3[NK(cXPESBM(80Mp$Y5CC*E9Xrc3rGh[C1l*h%0rqN$1,Q( +5$TIf`e$8VRQ8!69qQ)[(-C83Zc`3D!fLQPY!V`%,&Bpff'Z%Z0p%CUShc-e-ChH +Qr[BQ-[[S4aEjReiM"kFFIa@cq%`RCC&r#!6jaVXpkX[[`Q!k(Xm08c!`IU%@`AQ +BcFr-B`HURj8@*ZA,'d0e(Q@B!VTi"bjH@iQVHNBr2#C2jZ%,Na'P!fq`FKdkM+* +$$ffdYK)!i8PqQ)Y,5DCMh!(#AC&ckpKb@ApYpfBd6e-bhCr3TK1dk8jXqTLbk3l +DG!HaN!"`L6[HXffl3lR@GP-ZNr+@%F2h@!`2GXJra@MihNQ$[jLJG1%lV[,JPHZ +)T2)@MR"'Q6*")QfM6"D29YkZa`4`[`c!23I!+i%bEKJJIJU#'9LDKX*RMIL"@F4 +R-r&6!S-kbmCB!)&lfCqAmd36f)f"FB#ISGF-C'1!@l`[9Q1TJ$Ebi`6e%p3lCpd ++8[cT!*A2ld#aEbJf@4J*+f8$$mj(c`1"R4mQeb)94e@N$%Aj@'*Q0-$96L3!H)- +)K)Kq9XV9c"2lS&!S8pK3-DRfM9`8jkA9afX,hmM9P8ed55)(G4K5$PLKi&d2P3% +BF,pE(04ajPj-Rq6FP-19IF'i+(h`'KX&iJ0Za@RBdJR$PV)4KZ8l4TfXX#42C`C +BBKp(q5(`,l!C(("c,dbU)C)5Id5cY2r"+A!SFd6h`T`J'fB['ak#!CVe-%N,TXR +[@!'UJbD,JF01@AJ%`Zr%)-jK)dpIcmjj(Sq*i)S)EN-@Z#JB&Pp*JkQP`Up!*+` +F!M$Jd84TCiQaD5HR+cY2deC@mL19('jA959K&2Ae!NSF[UE4l1,AME@5@#m6)hD +jLIimKH@ER@3&XcVf)ED%am'@Z9Q@Eq31cb-`Y`,-6mEmNXHB*1*im[HQK'Z@X(q +2(r89KPR1'[c"'p)MI5)"0""5K[$r%5mB4YV)*&Kq!!aHqd3#N!$c'KadlKNJjdb +2l+aViCcT4h@2+0a4NYd2A1RVhXP1Q0hES!TbG0r&b!6Ahci'SM+Sh$-p+ph#JB$ +%#QI`94kH"@(cRNr`Cf#FX(c%'KX"TZ0YcX+6,R!aCH+(ejJbKX['kS3$TMlajhJ +J)bdpbCY2`+#X*'SHimHFKBQ"ZPfj%"5JPSpK3f%8MV83H&L*U-f+5`Q%`lr$aDf +*KfJKKXL3!)diE2jGjBj3$dehGdLEaJA6#,(cQrU%l$K"(EN5[,!M!h`Uk!")DqA +((GS%kE,EB4PYr*pc'@dS)V8@4YUQGaA1HPCKb#59S-qQQrL26E"c)"jE$C2NT8a +@ed)jb&@F[f@%@mGjADQaa!PZrD[GJ)DJR1c[%aS%efXb`#dll`Bi01ETeU1jR%& +dE&#Ba`#QcQ$0bUR[&$fid-BB9e"(iBHF9D@!V9pl`+BJ1339@c-(bkqqeYmM,J+ +&0-X6+XirZ"",'X[++%)!a*UlmepL30Z#$hhh#K6U'`qLH'&fXc-*AccY*3K+,jl +Bj#'$L8PbS4XIrXrUDM,BaApbaRMbbF'fX5k0aa*8&51M-&P*ACJ&kZGT-6)kT+Y +L%*pS-06CbA3YPJcb3cApcZbTK8(e0q9!J-5ELjqLi$L!B4UL8cNjMH@Ni*fV')q +m-IL[8*8Bl5L4HV5k,E&6pUS[(Zc,d'f)$C!!DpmS6Am68lp(8b2bJM,%(p$`#M8 +i)!cm!HE&V`9$-"AQ0qS61"k2)#S5I-%fq9"-(qAT8-rPDFDA$Ni-N!$XAXb%#aE +miZG`4hK%-pj45q4S5@5!4&CESjiD9CQJPN'Yb30RjL6F%2RaM@&E"blqZ)[J,NE +PXBX+XI,*&$NEF+6+1JKLSfc)$K%09p*""%Ti9L#N59fb%q)@I,a2D,U0rY3GHHP +kQ*9c3p)2kZTAqZP22XP(%UEBp#bZmGN`CrfhX(XBrDNX#MM%HVNj0Rcp%Plr'H4 +U39[Ck*,m4jPSaKZX0%f5VlCd(3-%1iM+3-8",KJD[$$'$cD!)@dZQ#"FcQCQmmb +(H1KI92rU-rjXFY$LNe4"BjIYq"JPbq+Qm9VmNj[L$QSDN!#*X23F(AP8RXL%B'5 +*C(6)c&RAMcAM2rL0R%JbQCC'U)YG*Yl5&[kN`q[iBE"!"[Q-)Y1bT)jh[`rh(Ej +bR#CFN!!(+0XZGbhE)9qKE)1mqE*mMBI98*Cr5SR6'*4Xq"G+-N,L6pr3f*bNCE1 +r6E*4V'ECqIPTXmB@")0LJRN`UZBB(A!UHC6r0qe@dAA,(iBD2$,K%D)c,eJmD*- +HB!!`M6-#J"VhBk1,R9[PLRGeRS'mc!jN)([VHf(f!+BcCbCNShp!PHbJh"EX(HK +!bD*pX+J[Rak#d)Y(A%eJUj1MF(+#!I"HYm,&,Dc95ki-cQ$+4JV&Qf0c#@ca"mJ +`#adUXm9XDSR*2"L8rc`A2$4kXDk8rc`BrZ0Ji,kh1"r'VH%J@SE(0m2809GdI#D +pq+(%#@c`2!&$+4P6LR,L$"E$DADFd4F!i`I&0IKX9$jAdDH%aa%AL$BT%fKHiLC +AJ"YL)9I'JTlUd+L(#99mKBH2c5!MJSUBX(360T*0lY9I$(!5hi5,CFKi(LVH69b +G#B!%BiG,0c(c5Ef#T'hM6fl1c[Q[I$JIJT9*"RUh)AK[a3r-Uk9YiJIPik18Jcb +8GTYQRQIJFB(44jp1*)Sr&N0)*K&VBaG)F[CDe[h2$VMl`I&'",BTdeB319B%G8h +-j[J@)cFH2"MLiPFQ&8r!cr019!QRKHIGaTpD"8bi*A(dBSM1GV&VjR2mm)$35Il +c@b)HHXJ$2j!!+Lj1XKh6MDKF2b%)S$LJLP%&iRd3f*(94dU8Pl-'qYQTI#BPf4! +TBcK"m+MNj3[iMqHC@+L3!0&8IrIrJ%"`(GPN+-cjeH3BI)kb%6ph6Kc-4%DD2SA +&2`!!-BP"4%05!`"B)Jp9$@C'%4!KiqjKrffrPUVdjZqk[VQHj['jVUYkUVCk3I0 +%GP)h"Zc+!VeZ*V1cb*r(e[+3!)lVQlQ&%-)9lHHA(A$+M)K1McJA$K0VN[%mkf3 +HYa"Hcm*kK("k!b1%j9Q(dB#"*YdNlh`c(Z[[hprrph8G),ql*1mP,cm!&AGH*2- +J-L)5)I%!%5)'AhPG*Q-ar-LqCJ'NQ"c!*L')CV-(JC2-S[20R!1c&,&8JZr0qr1 +q#52iqmk#M2G`pECDKMbLd51Y3amCHVZK48lF$KFUBV(IrB)9$i+jGMADr*`4l'r +h@haI,j*'%1&V@$'3!$VG3"D&JAKK))8c#+3,I1N**R'qR0U+J,#d[d@@iSMNQ$K +F4Yka2ZGiShr!2`#`r@&r($qAeDh0F5L,bQK3@RaM-0(+iVm'I"EQ!l&N"+cJFTK +`k@35*Qc#G2I$E""9ifAHl#RqDeZ%Lf9SP9)$-MJpFG4(,I%a,MkL'[&(2c,M3"S +Y[YDM,QA4,b@h!L#Z%jG$)#S[5Y8d3Vr[c#R(PeXFh5Q'2B1c&p9G9m3$fZml-mS +$RIS$fR6*8CIT%CIaF6+LbT-UD*qdb*i0(EjQGF,3E#fb'F&d-d$N%%((G3LakR` +CBrj4Qm0[*)ZeU58Cp6rXD9CVUILMYU@lYJ%jEKQh`A#VM&"r&`##-#p$%1HMj5% +5b%2'aX&Z,k$'M)!D@DbS3(m(PYUc23a'$(r+"[qKa90L"&8#+92@`D@PLaQ&*2" +5K(&fK("@jrR#mUhGNd&1A5$mrLahGlrP#DHa(%U`eCPAHiaN@cY!iqA+)aH94k* +B'YKlIc6Bhf@%("E!ZZGZKp5GMhkNDJAYqIZ65@@"Ab`iZmY)#PLI$,5'$C6$b%* +1DM"$ZkfpXGehK8d@#85Y+,,VLqj)JfA"Cq5NfAD#LLNRl@dR#[RK(!F6fFLNa$5 +9Q#E&G(+j#d$#C!#AjV$$bU$*'!cfcqq6UVYPj#X)%Ph-b2#+dp1Ke1PBc*FdU,! +",Cj!+UX($1R$aXEGV*TU[$PThAQLX14G[Y0L,"'`a[#@YY-#k+6ZSVbX!9+bmX8 +rDe&65br&k[aCP`6jk9l@[9$VeTUk@9K)h@MH1)SSE)KZjX8Q8XjV-J`$#MLkHD' +CP5J(a36hXrM53F25SiX,ZFVd!K-&a"[%e%V6ZQf`IPD*Na&-cF4d3N`RT9jBkej +8#UXUKf,k,+Y2*X0G*(Mab-1+i+@m6SANB`(jl+1Kh9r-aU(ZjI!m&m2V&R8E95, +U8dG$[-%XNKld`+IL!EIjqc9)K*!!`,E0meDDcQfhB01Y,&iBAVH4cjNH,eq'J2' +EFcNC&U$JP#CPf`RHQ(rH*`Vc)G@Ib8R$Nc45E4dXEM8Ni%)Zq%PF[!q9j1+H00' +P@A@'HBT8%J6h$!`(B"+JPFd(8%&[QSP@!CBmZY$[FT8b+2+p*a)&A[8R+QCdKXS +VA-a+SI+(-9'S(VdU&a19GSmZ[p%#SmIPTUkj!P)E,!4[B#U#BYLN#,N5e@r&%DK +q)DpLF5(r5kZq2+hLP0EibNkPkS[T%,@H''#Bja9@&#J+L*+KjpS-EAIQ6TqINkC +3[6[R!Z-Il@kRi)HCSAXC*"b8`+I+@%)+512ZiZr`D(iNfX3"@2Lk9f2QbENd&pm +@j1,bRr2B,dAHHpL6Z46&6m@P)(jFkV@V+*KR0Nk'5+e!A,'[BG2fM&4lMhe9I9T +jYI,B9j8VP&I'XDqqkPCHK@KVMcVZT$TiLlj`!`+TIj%aVk%09F1N["J+Xh0K#i2 +)jQ6TZ+faBlUdS1fqEd`"k,*Yl5cFbldEUaTl0eETld,eEk8TQ`mJ0*Mb+r1,%1( +`4KBZ+h-#M(-%(Jq`@[@-U%9A@@MDC)$)%SXcKDVHj@dAT2VD`9k[Hd9jR6rBkkp +Z8PjlMhN0#(EcJ[)b41c'SmaBT&IH&)Nq4m&9'*9Q9iZdcc`,!U)F6N)SCh)jb)j +`NSi)R`'f!da-%2U"M`pKNQ9@1@9EBT!!hK1@lG"UH)N*k,d4e&0iJqi5P-jb&9c +'ZqJ!p`&6S@-B8hhMNq&*1mMeZl,e9-2A(aFE5$K,V!$K0'(5GQ&k@+`94G'GFc$ +CCFBmZY'+l,Z5`hmE+K(QbL#%"R`PV3SK-625h`&J--SGbV#S5+M3Y"drjUr[389 +SEb%p2r@hBDX5r0NTF9h$56ERf8(#Zl3jZTi$qkA0N3maUIV$!55eB!k4-hmii,2 +6X9BkYY9SGdZmm+'6e3"3V6A1piiM`,B[b%K@U$L4CK+Gc'U1GiJ8"5H!(11r2A" +EQ0Tra5&HmDSi!(-U,GE)`0Ld9T+CMC2*hBAaqG5cJKNkSU3HAQE'Z[,rhNHaXD, +Sl[8L"$(-(q0cUN9bVV*b)66h8UaD(FF(NcrF$Xe9aYDTLhT4c"Dd&MBBFAKaFQp +p+,pGcYR!S&C(mm&B+&q1@e8RZQ"PQKKHjb+f'-ZE10cJJVB3X59@+G)UraIr,r) +rq#pkc(p"j6m0r`A`Rhj3eS)8NL@icl"XK[29`rK$d%+0`SSN@&!BY-D'r!q(&%1 +ZqBG$"cAN(CcDC82q,b`k'ENah+SNkHT51(blR+lJb3pM#p[qi4!HZ$L-BqBLUAB +Tbq@d`C1rM5eB3jCB,#1,NmrAYl@(X-$*AD'ZGiU04PS!F@NA)kU,Em15EU2U6%U +B#Gm'`Ci!0&GZ0E@3!'V4T()E8+fTPVq'TFAU&V5,)69j-FTTJYLk@&e&!#a-L!, +DqmD!k)Cb1j[G)cCf([%iUA1YT-lPSXlPSmjeQ6UAP"G!lKV"f2mEEa1klIreRcI +18rKG9d1TdIrI,j9d-bZ2['PDm+Ki*0&G[1+44cMerX,GKYJPP,@dlDQYUL4AN!$ +*j6#-C230p&K1V)DjXa0Kif+CS!)aUA$LDV0m!r'B9#Gi%Q(&MdP8&Mk6&["`%YD +dYmL2fB%MHLmN'B+`JQa`JT*[K[R&8N6L@$im5BG(eDJM,Pl4NNMaah[Aqj*f!QE +LH1dP"@kH3rV81TMb*6)44M#AZT6kfF0PIrQp*8lU8J@1fISA,XGL-USLmIL&2#B +KGi3RFqr+k#)+F-1!akZNi`%qI"GK,C,jH-P-cTCcR+,qEPk'`1kC2Jll+SpJbVH +#J0"fhQFfiia%3"Q-[FpN,m[TS$qmaD$a#K5+hBJ6l,%-,T`J2M&Ei5Dl8RK5R*4 +MYdjm2)$$rq#6d@SiX2`"j-aJmJD5CJH"5[*aUScZ"DRE-4PmrMk!'CYalaQ&4IJ +R*Z)6Ba+ZR4X"rMmFaa&F&)!mpf`M*RFU%TEfXGJMA+"YjiSLT-6,1-$#NAr!RrH +cpP3Hi48cY15d`Xi`fi)rQf'"1#aAj6-K*E41$BHL5iY#8-[hVLH(`I98i+08i0I +)H1$S4ae-bVh8h@5NP6SI!!J!$HkX[IL2'TG!(ReCHfqqZ0Uf"),H0S1$'3+m'(h +eFm5JhMIZI4IKq,#G`6N!H@&M"Kqd43BbFMKNJJlaCZf1I((%FV"$USBF8MAd%&Q +LEd5qQ,k4iXL3!%2UKKa5Gm`KDr50b"[T'bQ1$MQNGXJKY8-18BIYJk39,r*`[G, +i%"D*CfE4`XRQh@Yhh!H-Q5($K0[,H`K&h&(m4&8$KUI"3+a$6DIb1Ged@'3f)E+ +*[HXpeTQPJYc5[[@qmpl65l(UB6Yq+[[cLjE8'"X`3ZVhB$SPF#+p#G-SChG2r0Q +(2kX"#qlMp"FcNE&pB0m1FCJ%X'81kQSIM'Sl6(NIEVErQ*0NF+p%Z&X2pQ%C`ZE +9"VTVE-AZ0h[206%FD&16U9ZX`(cj-a*`h`Sea`UNPd192FbXpBf$A*8a#HZGiXl +HGakIlHi8ZB1@B$-V0Z$SD9QjApRk034r4R(HGXlh'LkqST&IpqVGJ3q(+ep8la$ +8$F#!V84akpe!J4ZLU3'P-b5(JrZ@8(3DG3)%eepYjNk!)Qkp5JR6c3dM$`Bl(,U +q*4d'%UPiVJ)!Vj1AC@'UllacbZ9AHh'jebG$KG$cpmR8V`pfjiN*lXiC#G$J*T6 +#IbFi$Kd!4IMCli+d!VDeim9*Aj)!V*0[R8DDpM354&h'I'2A`MIi*6VD'"DG3"% +diFD,`MVKD-mJ13`JE"[IASbmqU8l1$)lR*@[EKSHcX['*@3N#(Thq9I''HChRFr +1El2-GF2-p`"X1&'BFTh(cB(&#KNE"@bL0KF#cprRMFePXh,$`+bf'"`$40+!bbj +-k#rplUcdE69#+2"kF8R3e`&fEmTXh,d*K,U%#68"q18-'[-$Z(PaG+0D83KFL4c +qNNK))%N(pm4#J$l8m3FSjM#$(lM-$Ha!FS+"LXPLb[lZKUi&Qe,!0Gcpi"$&&kL +K4eTL,dVTk&U'V8l,@(C3C0AEG6L@PIAhmY9P4I+YHp8PSm'ai'8j-eCG4H69,!l +DB-69YIK6``'@FIk4%XN+IF!lIZ5`0''L,K)3a!f$,QFbd+HEYQl%CAcMBM-[JAS +IJ1LQ2,-J,SB("KmVAhUTZY*Y'Mrmihe`9iE*FC!!Jr![LFlR%N&FV"bJ!-1UV6R +T+I!#GN9hrDREp'lRC8B)")%A@i@EC&9Uj!&-FBU&`q4KDj0)ID,DCk[h6!DHNC- +%SLkrcNB)V'5XHLqE0UTC,N**``A!`'SIUbjMBM)9b@X!K,3YRHMm!iScfrMhcXE +1ar*JlFIK3JQMpYJ*CSVi1#4-ZH2'Fl`j*!6(P8"DK$81)L!M4$Km4%EF4$`Z%ZQ +i8Vb4#-S0K*rR2-A&(q1RU$K*(lZ+Pr0(eFe&APd&9[#R8PEAUBmR2N6T`dMLJdB +ITK)I`[5K)[%K3KpbB`%e!j0ZU5j4ME'!1mSL9jXaMG!dP@-'l(GL'DVYVbjcG*X +`%Pm48VaP"3H)r10'B%XRM-lekrY5+XTd"aGq`A6RbC3Y&1l1IpJa'@5QVkeM8U* +T!94`fdQ!T2&6$8r0XTV059qc%HIdYT1C`adrCPHDl8IHJ"VY)MADI&RUc!EFDm+ +m%BpFYiNf*'8Y40(+bY'6T'hr$ETbZ3M4K9!Kl$)@pT6-DflV#iNe6@`$*,Ca%YY +8'M[G5f,V)l(G*E%9Np!F*,5l*$5940DV1K,#1T83dd"#3#X6SXNQK&+!8,b+8(S +53M%63JN*S6LTQceUJ`0lTK$SEpp`#ijphCj@JiV)Zh(,Aajr+GClr*ZQE(VM5$f ++BGjTmAIj*P+hf"Pf@Kcc0rJNj9TMSm2#h4i0G,3V8R4PYC5KFe,H&lJ6#3J0US( +CaE#MBm@Yfbp5S"1T,VNpS8!ZJ#Lq8Z0kJ#1X!2dd*R,#T4X11A"EBjAcI#F$d0M +L@(A0kA1fFjD@kPUR*@U*eFCLZ+Pc@N"ZmMUji5fT9P)CmB0erk(!0,N3Nd-B+c8 ++$Za`JUpM*hM[GQDA&-8A4RGMiBk4F--NeKRe-KcPN9SJKh9b@Z!+[A3UFe@"i%D +-m`cZ*"j)#8C%GLU3!&h&83m!`T)ha@[P%1[",[,bI[dLR[Khm*Q+c[lVXl%U9"r +%%+!U&l'G!DfA,Di("!!*J2#h"!HQT9C-)XIFjIa)5AL'[mhL5,Pdq"A-!!N,%02 +N[0J`B#[qD$9k6c89VUV4SSF)CKX%c$L)3P-M0$%T*HM-Y$%mpLA*F@dT"4dAkA@ +4(Lc&4K6BeS!b9Y2K+Ib-b8ba"5a-d565`a[AiY,E6M9mIX6e!!T!DVZJ8Zjb9kB +IUmFe&P6R#KZhh0FCjS+*p'0f&JNN6ZJ#$"j`YPq-I0,0,LKbQq(bCb*VI0(q#ij +ED*!!j&c92[dc%IJM`BB2D1lc%-#a46KA26ccYVMiBhd`69l-DQ!%651Zb99'dM1 +""b$!aeDkf%9rBHCYU,`4Q(8a2KESifdm-KTT`BGQqM#,iKRPK*U2652$bkYYh"e +L)hIfbiHS3Fp$I3riTF'(C@6TUBCAcr,PBb0XkVajcaL1cFLH8`f,0[(+Hdhf'2q +%"j!!DX*2mYGRC8ifibFV%Icam6rJQq32q!8H3JI62pZP$cpKHGB2SrG0*$DBF2' +2IH[1rXJDN!"YG[3EF5H*E+qQQ`QkfCIKQqE[L`ILp2&!J@p+(k'25b9b&6kfd32 +&p!#Vra%LqpYj5QaZ16hJS`IfL)@4&X%Li)9AX,ad8k#E'Z@K$a9RSa(h6G&)LQl +1dJhJrQLBa&SUQ*E'pJ8l'CXT)))q'!K5bNiXX[P0c`!BLYA-"U0ipKRaiRcE15C +!"S$cf)'f#lLaRfVB[edK4M+bcl!c('[B35EHcc"4H[r(&m!@Ii[%Zjb2`jmf((Q +0(3d4,Uc&iKjDl)ET,m1&Yq2#cX*mY*h""3D1!!lBH4+9RT3!*J63J)I,+4QdKii +Z1p9`Z*6"A[6L,P)Ga9ae0*l`XY+k2F[`,iU-(CIf'DiX1`I9C#dJa0IJJ,6$(9G +8T+Kk+cHU-ZcS0#[J"2&q8mH4*JiJP)SGGHQ508-Z28#'Jc5pCq@9b&@0&aLQY)" +b544DBU&`JGjFK-A01IQpJGJb1Z#h2Jdh+P)1!"PLbcbpbUBQ5#(B!H+X10,%5FM +BDLGA+IlM*UT8r%UNT2j`2EE+$mCj%YZ$4qU84kV%4iKE6%a&0CLiP&bhAe`F8ei +diKp&edSK16$+AH[TY528YC5ZRS*3&lKl2Repj!&ZEJpfpDH[d+GCdG'I[JRfmPQ +K0TjqEH3"%e485[@%LCcq9Qe@@"@UR,r**mMX6P*S59*%-&GX2m4++-dj+bZDeic +[ELBhIb5k*SU-CHfc`d`b'lYmSk2ajlSDfUHM0pS,J5hh1Cb#!SL$Q!@Hlq3JllX +S#0RTq%##('MAdKScQ)UEp!3h@10N!eLR#VI4B*QGbma[lbib9Z$BepZCf&-3NC' +Gl,6ZXiTQCm3NXFX+8Y6Nk'%4$G(#D3f16+1)NVr1pKrFS`f%del5"U#+8hrr*DN +pmSBaAf3diT*rr`B1#3qQ0kh@c+#$5*`53"22D%KF+Ha$E*JCGM5XBp8ekcrT'dD +hUk+%bq-JAbBl-[hd$$p8N!#q`XcmMp!B38E`!f!-2qYN819dSmCGA(+U-eQ3!2j +fRh9brYK,9A0LG)**S09kM)0!Z'+-NrbFCRAf)Z&N,rMGVBrBcL!je'rK"2eSF1p +I8F%pKH+Z"32D@3MX[Xp!4$EHH(V"cNR,aY26+AUS&JmpTFiL"!`HrK)9[YPiqRD +TXI&`24FjhbkB4N!j@MjhDqpIJ4312TlK`fEC$DP'@8+ITLI!20j#8iFD@U,aC5# +dTh`bGJQ6@XqX%GLY#jA"ZVUSK9YrHC*4"A"@lfC$R#6h"RDR0@"jhH,XK8YR(CN +%6+jJ-NQ1YY0LN`2iU)82S+BepVF!5IkZ2&C&a9@(#UGLAcHi)BK)jeHP-@i%iqI +BN4pX(X6[4&&RLJ$RC'"X-SZ1-bCcP%k[B4FICJYC@#!!PA!iDhC($ZA[rQ3J-0i +prK-81C0ldi%NMA$G)RHb5VeC%%'!V@#6*rlM-U$dfS,SYXQ#RJ3q3CIm'KXAXfL +LBBI-kHcDUBEGebPY,RdU-lPJ[RZ-HHlBAV2a(*b&Q2bdJZ`!NZkr`"8pFT+f$fJ +EA6kR'SDpep,BXF!%+YIE)R-)fQYl6kUk-eA5m3D$(!MC9r*@I!A96"aT2Yr*abS +A6JeHH,DF'$EG@@!2QqXB#K-&QEZ-JX5$'8-8*UIQEk%Spp"RTm%165K1DR$-l(* +iU85c-jQ&bQpQPCrZ'J43dUXA,c8(+,0+"Vkh+-Pf00kja`iJ0bj3)bf#qQ"Nf$J +Re!Vjl,b!B1(X&',&0BS'm6iR16(C55#l%,,XZKK9P'NKTiZP)8l03%afRc[U@&3 +K8iK6pECa-f'P%F8H0#GPjALIhA"10@cCUC!!pFHj`abjJ)*!)L'69je82'CEGq1 +Yfd+0rd)GjimrE&+ea4rqTKZEV&)+[C8+c8`)2*836M[%MCH1*[&)BiY-6F[U(8- +#P$T&M44QlLdb-cbDECm*BM*QYRm2B6*3aV#H4Q!(,Sm$P@hQK!J$C-2#F6E5eXj +"Uq`Tj%f'm+0`I"1TeGmKL9rPadH2q2!S2T6JS5!%9CHGT`YDp"3)1C@@NXS`f9R +,hdi0e1&NaZj9&CrkQKI4c&jkMI[qAXmP4A#2U`jf"MZ(VSVL6Tf4T9*e1FrhRPr +jU4R(CIjGQ!-E`bHhf!3f@(b&a@lPS$ZHR$M)Nc*(CB3UEP'Y-i"PeEL5!,S2)d% +$EVJIe[YlI38BS8PXKpdGAE633G5!6(4Da8dP1X3jU9iaIQl)Q)%bb9k@C&SVb1" +c"UN2N!$p9T!!r9D$Qjr"PElB22)A-2Tk)`q'Ni'Y6@U5!XKkr0QNZ!eCS8!DXI8 +H928pE+f((!JdGaCE82(a(e%Sfe$il+VQa-([X`+MlU8djbI&e5-8&aKA(mM2M'k +`bX!$jH5d-"EEK$reHY)Il-FrSJP,)iH%k(VfUkEL#$ZDJmX%3)ZDf(X!,lI$4A9 +2V"`1ZVYLCHbQ'b["bjGM0@#(NJSM*L09KYeKDE`e1i$MAmDIrILcLiFmq#@A,KP +bk8@q0#UiR8M,bmhS6YDaf(XE00q%2`Jc1V!"Rc&C2A2(VhQX'c52I6MJZ#1D,S! +pAIEGCA"@NM#P'Xm`rc4LZ5F$J$X5Uf%h+U!6Ed&"m'4X'jB+*eTD#YJ6S&[ChR& +ZE+Xb8e[H#p!cDalm3EL)Pm'BYY2&[-V&A[CB$lDK'3'F,Z03KCZ1mL8J[P`KVMG +"Ll1!&`F$ppMlNmY*#C86%`bJm[GRpG""-RYG"c@cA#JTGB$-cd6$KM,4)"-U`TP +31#UreLm%CYJFV0UFl6HLL9qCEcmfe!SeC#8bBb48N!!"&@3)&45V!N(K-+S5cAY +U(#F,pJfhTZ@k$iFSS[@k5jAaK'c0X2-+KcRVL95[SHkbU8KdPhC5MZh$l`UR3Ma +3,aJP&02laT65A9BFT$XXSqiJJpq!8XNUAD*4PF%2UG&0bf9*,0"U"YlC[%ccLV4 ++a@8%3BQZpP#-p8,Pa9B8mR'S0b8"fL-DXK*m8b"SET4!Z9I82MVk%`KdYATh*JM +9qJRqV&EpZTS-RP*8IKHk!NMCG+RZQK,FGX`V0[(9iN!eS9D$c2*riU%ZiHL#D&T +qc`ECmTGRBbh#lC!!#dT4[6r"`k["HVA1(!F4Dp'*'%*J-&")A&`hi-!1TMH2elp +3QJ2XJq1FqBJ0k9X0$4EfE4KBD)9$8+KaB0Sq#YJ'M34U$lA%eR0LS-%bf&JrGXJ +[ICUrhrFcX-P`beR*a49Tb(Y#lQK$h[FD*hPJI%&+64fJMf#YBJHHjm6Y!CK"LVI +#D5[aq8J,J3+fV%PU'bR%-p'X)$T,-2%frc83'bRhRG'BlH*Nmj8J8XeKE[c168h +e)jd-UHiM"dY85EKBEV%mDQ1S&daI(SY#rc%5Dr1&AfQ&@iC0C6Ce'C`!IS*+lH3 +'Kh$A+eHh1DGkTeDf%URjH4UT%8N2*MEK9`*d`$D&R*c)-%6I"MA3"RIp8R89E6q +!lFGjFfJqe()QLNl2-)60IpaAi+B#(MXqEH*2Ck,%a2ifYNC0dI3K6'8NTDL&M3Q +P-+1$hP!$M9#F@9),0P),YP-0[qY6h&RA`,%G#T*!l3ZLBh!k8@E9diTkf*LQ(-4 +&Im%APDq6JYJSe!-,"FV"aL'rc)+*c'BY[cQ1#Za5&)5KQh'rjB88KFcmMJmUJEV +i3"fPT)ASeRZS-`pPr-S`,)G9"fMiD1SFXT*6SQZ'0Tfh@NPeQ+3k8JVBpeQ!I6i +#qciR3ZHIZKR#"$PUi(5QCi$#i`'#qel*"&9R392J2Yk`$[Jj&DE5#[D[hG("i"j +#ead("rFL2RD5,BVBfMhK*)(8dMi)c[&3-Eb4Mc%!%!0r5'ip!AECC3Kh4bZT&6r +"qaG&FPpZBP9ep9hjUH+5Pe)!EcX65Jabi54)#+`q'm!NNTcIc$#Kc("6J*Brbm+ +k[,+`k%qqM"RFIp1`F!`,ArQc,Ia+VE*3F6FP'$S0`-JF(-$3")"KYDB$')$#P+C +H02M)++Q60Lj##0[1$TVM,mP`E!m)`q@-R`I@Z#`jC-i%qc[8-35mafp2C!,p(D2 +DFef1EJ5[28L'hPF5Sp('il20ZB#MLer[r5[peE+1k8q-`1&1GYI1cMFHAj!!`H[ +Fd4Z26mZJTF!+%iN!0*!!Q'#DKJcGCRG,J5kHJi-X94B@KBY3i+IYbGFcr")B +4bl!DTN0VP50,P!2VP!2VF1!1p4!89MNU&GH2B[D),N+U-[Ukk,UkHrPZ#e6VGV# +!&NUID&"pB"MQq+(J(XY+'Ef$0V["k!"DM`@8*N-i9!3pbND6rRk2M+lP$@*,e+" +-f9)9YV5Ah8FE"aDH`b3*XV(cQ)dT+MbU%&bSjI$3V89fdGD#Y$8E05B#!dc#U`4 +TX8'dCY[e3[kLc!Ah[M0%I+(qMTa@hZhSf[X1a"FkI$m"98"mR&,Reh[Id9me3(b +&d"@0)3d#55$!b#'#55$!L+h2Em4[KX%qlm#fA)12-LbQ9'Q(8Vaa8BQT#[)B6+% +U[+L9UK+PUU`H@XJ9`HKT'I2ImE&K*p%(l1SB9hB1eGY`"aZC`8ELMAH`N88K#N% +qaD!Q@'$ehf+h(eldk2qq+$2q[LR9QQD-TaI#C$LRCm11EL2Hf,f`PhrJ'&$#30c +0*+GI'#jA,r'(4)9#kHBbT%,#X6##kUa-E28i0pQPVD,)j$ZL1-4@pF@mBEGG321 ++5pfMkZ0`NQKc"cCB`#"S-"YZ`,XGkE8iTmV8#KiJ,"YR-'l1JV!RqL-iP'%5a#5 +)53!6$C-`*J&-2[i4Zj!!S5%&*KXC+-43,H*5j80$0(*BYbSTUK5D#fKJDV5FI@a +qf5h2"&3ch,#-94MkkCE2ZD+E2J(D-cEaB&#qH)%"2aJXYTedacCB"SGZXUE"5@J +md$$4`)#4NFqih1EhE9IJ'Q!bf&8&jV&Jj0GrKjPPT`X*DRqX8SFri&jh@53C#Rj +eN4e&j3k#)KQ!21hLBEeDPD3aZ3HrfXQ$E,Mh(,%CB6aFldipBJ0T![RDbSrFi`( +k8Kd33@"k"4a"Qr@%BI9Q2XaRCa8)dd!)Ka3e!&B-Ge@HIMXp$M&8&4c(Aj8A`0L +piSl--9KIkad[5[hm)hF@!r)K(2'1pb%KbMG`&X4V)bKH'0dcHF1"C1d&ESL")Ep +`f2T4)jA@Z++1#fZPD)JeF88D+5j3$2Ph"`eVeTjUq+0G$0l0"AKBU-2R8'8AX`N +l1`Y,B1q6[XHK*T1qVj(5V()q!B)2+0f)$eKqE(&B`+C9Q3cY-#K6q3bR'b(SbAN +-3[HHF2r*"XCYKbfd94H(i'fGKIbcH&LQ%2lKZ29qKKNmiZBl5(K11(""Jcp"S5& +YS3e@463&B+I-*eCLif212!r"KH-GqY'm@Br@MarDYTfU-!(@*L9Jhq6Q[JGT64B +VGHJDA6J1KSJCE(Bc3DS!JE"%K3!N9km53,+[K`NY[4UMbMEMj9VaNU'jF&T$b-& +"VKK85MmH0pDEF93`!rH85LjZ`maBe*j*[QLVFM55aGAMbQ8[+TI9Ab+a8Ce6AVi +f5GY*[%4$K'T6H6Q$6J9"I+m[Fc8"G&CFFKHlZDYYC2F[%iqe8YSja!*#Gi&S-6K +JRG4q`%RT-!'1#9G9N5"1VdbLL(ap$p)+TDr[35*&I0!LTS9FbE(%3Uk@NEMPLX4 +jqkdh#PH9E@-BYBf(A!m'#aTT+3D0`fJ5bJaK5QdQf-FeNhmk,kdc!(%if)8)2X2 +X5pm$[A'%Z@J)Nq2&JTNd`8cf`M`HChALCcA*5Qf9mQUPfrPhhX+iXGbAqXf$9eC +%MEQViamKJFkT"5QI02U0CFcf)U%,9U[R58ScKihlQAj12I-J3Md[8Z+i8L6Tf4A +Qdc8Bd"%-U'm'J8LYm4crrf9f$IRdMj8K(cre'eFRT6i!j+GhqAEQE4a@*3Bpj)9 +[MD2DfNb1@%'`PCY,qM"%SC(d#D$j4EAA5!T9`afNUA2U-LIcT2%GGXh`HkIJBT' +#'J%,KdpS8L-D%%fLFBZlP5HF,#2h5k4Ef+%"BVRHakNaVZKT*-E!H#14VaRRehq +H4DG80iHd@j!!#dqi&aLIF6h3%er#')VNXrFL(q,2$EGm0KrjmjPh)fYrL@#h#F& +5bVZ8a!Y("0`8dH"0p@$[r8K22m3GI-[G[JF(%AXCLje*%EClV1$hR'S)Aq*'h8F +*rK0Uf2l*hhNcidEUB-+2G"Lp*2be*2)6*(!-`'PpL34H*EVJPeFS`TiecZY$0AU +h'lhr")&(eT!!`&&XArjD3Z"mkp-&rZ8`#E`'S(Q01rphAQr[jmIK+"!qHN0`%@G +cl*f"H`'!d&ii%I5`U+TfdZ#LCGcJ@M%-+3pa'1c-Se0Gd98qQUSN6)5A4lk"#X' +XV!Bj+*4jHqmGMVa(*S0E%+m80T12V$*-RXK$F,@Q&"*prQ$Q@@F[Nefi4+bD6)* +N9@*jbq6(iXCGr(-EEL#bc@fF5#j)Q"8ZpU0D0L[I+"*[0b-Yk&TJf0)$eIm$Tk4 +[2a$0#J&6lZJ64JL5CNb4%9B*%IbSrZ"'5!T*`',,2A&R2a%cKf&b-j!!MCrcD%8 +2UGP6$D&C%0)8f!!(*mpHl64k"B(KKN3mq1IQ*f&drHXrciNUEJ62"UGIU#rY3X` +!iD"iSCHGj&%GFD'eG+&A-*KX*AGAA!J9MA`$&h)FFb%E1R3r*E9$aNhCJk'%HrQ +bQ4Hi,)1TNJCT)AFi5P#!f$qN3Z8%QQ!!JPPEaFcDi!#MVBNKYTL"#Ud@3pRk3X3 +Dl4*$d[)2ZU!9)UdM4SVKacTh6MKaC2VGelj[8iF(3hSmF!mEk-B!!a63-j0QQ)F +&+e@VX&,A%fa8c9!fkNT%6@&SiQk2b@bGSi--3@hXQ0f[6kIc2+JV-8e0B*VQ&+D +TC*!!68*('l`T-33$eU%`5dd*CURfB-`5&dB4#N2,-'Z%EX$$h2B)*Bl2'jM&9$j +Vp(QG#-9MG9#4Q`"l`(c51Y[192HPXprk&`$dh8c+2'%eKHDA+IUC8%0&R2aU`6! +%mUB9!M3"VJPia8*(2),A69P0BD1U"YNS02j-dU!+8m6XR#B@VISJ,&Uemcbl%)* +4UrBd8i2&-0a[SCSL%d+jkk`BZTk6f#rH4T)C-,%4JiCD0R,#B3e$CVGlV+PfC)% +I3NSij8RK11%L0qTkS"!#&)AG58i9+*aB&@kEZ8ZQ&68$5@$KSkYC$38AD$$ET+H +BLK++IGfX+%L2bZ3kC9'+d+)8SCq+81-Lp!mm86'R+D`JP%YE8b(23I-ac+!e8@" +T$+(Fc`aKkj!!)3Xi(1+ZM3&T6eCFa4EI36I&YT4!kh6EK8cH0BA"K&2XM1cqS`m +F+kqkB`"DSC,Ir-ALeA%'[$ZI#EcjZ[l"0d*J&!qeB[*Jd38,GmD-%'mqmGJFP)T +rF[l0#r`*3iP(hicKd`m+8UKkHLL1*JX3h8qV'&b$fQ-e&&Z@pfM2K0jm[H%d*S% +hEk3GS!fQM(8A2rmE''3hM+@Mq21R#9E0j)%Hq)&[BcMAf$TFj-A#94!jGZGC"dD +RqMFBTYBL*bZQVe`k1r-X(VU)2jIGX[lc'0b@h%JSZA!#DDQ,X3m"ZYbBi3%E2`Y +RqC93'4H3!,UkM!6AFXqFG4i$YTchqJ'&Sd(#qM&q$!b0C@d+L4`dL2[9YFFABZ[ +Ka0SbIICf8@b0Mb&k'KVCJkl1JeQTcX4MCZ+a%$m'T`5k%)BB5KcUf8Q,3@$-#qj +("90"fpa'$mqabffX&Bp[JcSc8#d3P`K-Bdk1Iq3"Zfb1k1T@$*1VUeZmQK&$#@2 +"#9,Q)5BRV'!63`Ac5hIL*4,EcXmN&[A4Ll!J0%FY@kQrrM8lpfPT#ifdKH'M&ND +(,%blS#a*@jJkGU%m0'4KbTPS%NH[0`ejEDBGHb(Y@2Y4aeiDXY!qH#`2hEjm[I% +CIK'P,UKr-M,iN!!BI)6*@3%TD2KmcKGK80aX2*@2`-R"jAHdEqLBVYh3-DXCbI6 +"4hKJD,#'la%cc1""-l'+M3K-mX3#[LZ+QiD#9Z#%m1+9Q$*XX4JqPD$jf'-mJ'a +Nkfm%d`%@ae3krH,9Cc64aAP!F5B8!"e#9f*i["jNS)5'$m&3m-2,)PP",#Df#a* +#!r(HP1B)'Mq#'DVbrR(IR`Sh&Zi-LJ+&bT`SJmU%dJPHHhX)#mSAj1f,Lk+,,M, +VY1iNA%iaLC36ZbGH0EZca!M1mN&*mff['-EYKBk5Z`6lZHj-Rm+)"Um0(LGAZ9e +%@&eJpe,%("Y[$`Q-MMV`K"K!&1`5+ZcY4m"aHZdBKTA[4e01+#pfKZ@$0,'efKI +FfT`)JpB4HdrJL-YVFHGT+aS#PHAN`S2U4fXiX)9SEXcNe4`(*Xa%9ql5+b3[L'+ +5Vq'Sf!T@R(LBKbH'SM66JeCKGQi66@%Ub3@fXQ#U2848dJM)jJrk([5h%lPTpqe +0$f,B#)R4l(GLN!"iU'Sr$`-[0[69%S)Kj#pjq21*Kjp-IjJCP0*&FQR'j90G)YJ +XZ(MSprqFrYAX-aA5S[ac"!$""%PDHC9GTL*R"IY!JhKc+M8j+L"#r1-'&(C8RIV +LNmYb9ih3fk@CF5%'AZV4"e`DSr!'KTNbK5(cJ9k6"pR!F"Y64LMGH&1CN3H&%dA +1lhpNT1jDVP6D1M*jZ)jVlMiH9[ZZaGEKc'F!qlDGH$B[QLM4P2jT%*UD(*E5e%j +!0%qYQ0TTfSUTPDB'TUddG@*Ud$5$UC1Q18`c0-eLQU2T0Nbc0&h%G"Y0+c"GT'N +Hd`UD$Q#DTqNiTJ-dRF*dR+BZ6+GSfSZTLkCpQ2E5G!61Cpj-J*S,bAr9-S'"MQF +(T)NrG[baiNmVrKK'B!Kj6T)BIF@r%Q*dINCKj+qc8H!I+lLJUaYGL9CI5JaTa-e +%Z,XFQ&bZQdH3!*0-U3,B"$$PqJXB[eDErN)RkkX[69mKXVi@IpB`@HI3dP1-fcE +HN!!lJZ(K[[M0CYi%[d,hlY)ArmZhE[rUk-@q@Gj506IiDd2hMS,NVSf&b$8J432 +L'QMU8H[qH+h+K0BhKim'%U(E$VE-(6[i-LMc)%,e$U5iLTajiFVrXCX-P#'50Uq +Mf2Rc[iJc#$kSB'[TXDi4JJUU,Ab*kZYS%[F1-aI968Xd$'Ce&eqZQT2"cIpaY[T +kE)3ZraUr4KLrJ%2VaD&Va`5Vb-2J`fNp35")`5"TVK-+TGKZR*(1AU8"GGeS(!- +D,IDamfHPcQ#k,%3mTYBl"d0mN!!M#)Z9&CM,#e$U$+&Uf-a))6"9NHA468j[2HQ +p*e3X&+,#NNFUa(!r[UMMP[+CeH4,Jjq4hNJD[3k%4(b*3@Cba@P2R#kB5L0b"d5 +K1-jb-99I)ZC+9ZpP9i+K+H+VeM%aC(0F$"Z#4'lciIXCqpD1`Me@PmC!MSF$DdB +UKCf9-830TE21FZ$Kk1C('VUQbaUkCYNGZFIS9Re',`r`Xkc$YcLUZBhR,*`DG*X +BXQl+#1T&T@bcNNN'#ZQ1e#J&G9*FY!$QkpRM8[X#,S1'V0bJ*FN!&)8j28BA&i0 +"EXrTDBDVj,6+AI25YGRL0!A,3#%FD'%J(c*CA1mA8&X"$!4F*$UQY3dGYm8$ERT +J+#12I`CN8KPLA-XTLI,aa"%Q-3Q1V9r#b%([bYR'GMJ!qihJiFlLDfhRp1SU"mA +"@Yf6XcVB'#SQmkr6e8"SJ6lm1am1JQEA#4T$DM"$&8(jp8bH!hJ+pPbMl*C8LF5 +AbB'R-3m)(L*Lb($QhQ2hC"KrN!#8H3c&"QH3!,!R3fj5E&JQ"RHUi$#0Nc[V0M1 +XNj'2BH"qQ&U(la3YQX#Lj&','0lP4CqN,CV!STiLmj'23%*)$6VU84JEqfmaBa' +lH98-')N+"eC4Q2R[SVU"aK%Lp@pq,Rhi*$%iU5l)ra`Ub"*GN!$4'``9N!!J5a4 +"KY)&'Ee%$a`Mb1KV*%L!6Y&C)FMS91))Hd+3!*c35ZQ#I,@cf$a@N!$4CK*N5JF +4Y3q'#P+lN4"NL3,&L*4@68+Bi9H&--fMK'Q('6Bjf35Q#Xrarf4U'3*q8dMFpN! +ijN`H2h%5P(d1"Z!C(5USrlcdVlpL39@@TJR+$N(K(qULCFem,*E'$l+8c1+VC@P +,QfQT4NZYbP,l88Y"$QRT"fP,VEa8(U+P2FV5jS-X[8e,h@P,HfKT%beY9CCDMc9 +),"AER8YEfSUPGfPKk3X[r*pdbFh9D3Y,XE!8F(U,q#G'%XBFeSfj@MGQ0U%Rdid +j@NE'A$,8Q-8!*3@#kiaJBTK(!HeY93eK$S"dHVCE[Yd1Y[6Vf!)(35(ab0$"4ki +Z-ZMhC#G",D2-'MpjA3Q6@X(X4Mh0!)4i!+U`Z3bDM+"e%0`mIjT"Q2cN+3+l@Yf +'#&)m,*JJ"4dD(@IYSb"'RL3@[j,r-5eC$hMAlcSr&Vr-iGbm%XkG4c2f@p1ebfl +jAS-MJVEKPXme'ZcYKS)2&!9helXX3cT-NE2h"e$`2'3L1bQicZFfU['CHh2YAlM +(lS2-E0+4hG2lm9#RH'J1`CMM!U8I06&i#QeZ$F+BX-IN&006F33'!ra"$d8)d!R +65k5V-*aF"4-*cbKh,F-&*f0lJQAA2jRTRr!c*9MjTe)S$%f"EDYd))%('F2,%)A +*!!+C1ANUT!K"bMmR-8Mj)B!XFXb$!9r2`('&#YYbUU%f3d[Y[2#E!j`-%CHV(FA +a$IqmNekRp-rrA%kI1d436SXXp1Q9Y%mc1+Ch509kKe4VLN&S,Q)DlN`N"j2L(f) +#9#2KE1pIrpmi4+$K8hR4GbMP&MELBLJcKQ25i)8J`GE@CMc%Jld##"l$[bV"+6S +FZRjdF%!fIV$iTlCca6h2er1!1G&cl,pbbGcbTJA$)NZ62k"j-9`INCB8,NZT3@# +Frr%MEVL#MmG!jP&,-#CPXFRrh-R"AV19&2FFr4TG8I)rKmI0rQ)a0T+A[mkK2J1 +K'@8aQLC*VjDf0BKI@SmjKSG*6BQZ#fJ!#6*b-c(45!30J!QZQ)!jYb2Ga)dI5`B +E!kF2``8!!#-E384$8J-!9q)293eQ4!)3)H,Z91r"58kmfLZjQ#[$k#Fj,aH#(%f +Yie+UJ-DF*cfZB#RMZ[%8lNk-r2i@N!"5amfF!5eMhDiD0q0f-PNhQdN6'`aLaY" +EkeVMC"a$l@QXieLEB4cUd&3003FPS[$hrhl[ppeh`1'Cp0PRRhPq!"eQ2L34%#m +3!"!3!""4,j@*[fKU`KraSeFSN!$5B481A*FXl*'maYH8V$&P0li@53akdN3GL+E +--E2PNq8[P9Pl!Cbc)d@IcCmB%HfQ69D%%KXA4a*(HX!#39VJCaFkE"jBfEKVS%` +%Tq`3CDrTE`C@0LfQJGZQaXh)P-kiD1Zq2BBLBUrZ*)9pq,3#dkC$#)GD4FIA)a3 +XkM6PI[BDN!!*)0m!`QikdABZ8[6&*"jS[eejIY+Pkm+2-@i+&Ef6CMDEkX4jG(! +C6CSpF`b"@a3l3XpQMMehpCddeUP$kD4A1RNLL8JT1[@JNf&JAP[3j2j'%hYkSqc +TG[ATh(I51Pi4RD3-kqehdL+*I@pc%HM#d(ZB-S-VkMhm6-HbaGS2a``&ADIMjd3 +Nm@#Hp8emBZ+I"HN$2e$LSRCF!U+dF6-'pQ$L88aXia2hi'Gh*0'qJclS%RT@qe% +h3X(G1F)6@5Tek&c*TbDQ9V-VNqTmqrZEQKJka"CdH4TES*Y@aKBid-PTE)&MHJq +b"@hL9EDJl9%4p)(iCE"iV,(JC,p`a50mXB38V*Y`UU9MqLJqD0JpTJ[ZKaUX0p4 +Mfb0,6iee!$PH9(NqDDJBImaGD62LE@Qdh6)SZbkdcc`ce)`#MB3@F`,mLCQb*bE +FBp,41!2PX`@fpIY9l-M%VMpJ#,$dYbrXhqGEJLeiQ4K"NKHP*'Idp8AVdU0*bDL +,&Lhk9NSdp42R@SM15pPTC`rQC'JCmNT,5dNqQaaG*2Cq!3reAV(f5N&Qh8RV-)k +eap"L(0HZJ!QpK-43!-cTX`lcaABF-KK#&CKPTi%[YQ51D5($'H-CCbM1-D'%iSM +pX0NYNB6Bj!DldT5B"qaI&1Xb#RlK[)dZA1P)l`CVVi36jCM*6B&fS'*ZcZ!LSM! +i!XB9r-U8)rcBF%4dDb'JKK&LXY-"8T*6Fl*e@RT@0Mi2T#GRTICTfGSa,IGXhCQ +8R"0489-TI1"JPLiRr8`d068V94Q+CZZ5,bTMNUSSk+Z%H@9U'cA#qD"Gd38RRcF +l5)@aB5YFYD3FpeJ9UQT9,M%X3@@`@&J)m-#0dj&%3EC@+0h#(APer$)#3(Cma)f +QZ9H&K435pdV2GAbGrLFe+5aUma!)&`F(RN5iI(HPF)"-RJjh"Q8UC8LrS39EFbb +Y0&)Sb%2(X!i$l3$b*%$@&BL*`cT%S*4Rh34e`i#(2cmU(5aQd-De@P2Z,aBU`kD +N)`+I@iPKiL0b&UTji2K-J+EFGcl&"+m"DS-3U+S'iU)TGdiT2Jj%LZDXCBSS$Ye +qKeHMCV$11bG-Z6%62QlLLSM5jBrJF*)kSI-Xh[%"X0N'pP5(6FGCe"'2[Y680-p +0$j1VH'"jB(&hbiY3R,$JM`-CINPP!B8"JY60d&)cUjdLi9@$dhS#b+6'@rR)8Q, +lf0YJqd,1pK9!0%YXlqY9f&lUIUfC!T@A9T)l&-k))*Kre-LZNN6)I)S*K6dNN!" +P%HY68(CC5%#3!$[dippp`GbPKH*#0+0Li8+Z(pFmA-Jpp3ZjYCHZN!!b*"lkKiI +0R9C,apFjbp3Y)+!j4-q*fHLm1MlDm6iE@)m21YC"BJ%6&r*BUX)Ne&N,+5k,bfG +6)(D5d2EeR8e+LQCRD%PC+9V@J@3YbkbPRXh@4Id@@F`kNf8q`fFT)T[N8l+,K,H +EA$*I+lk@dB@P"kB,UN!h08VC598G-U)4!9#($'$*Q*-$()HDZ6&N,JpSNm'!"0# +8M1AJl&S0F&DJCL`*bUH83HEJ!Q+,AVLjDI"$3`X2qG$L!AlG9QPK&"ZE-mFbZME +8EQb1cS1LD4J&890Zi,Y1X60aXB-i0R0`ITmH3cLl(T9,3%lM@Kc4Y2%SB#6"6a[ +p2`'3!!QL3((,M($@$h+&JQ'GV0pfKkYSRC2efd+ULRMhDl8%$N'SYBlBL8!"B%S +dMS`k&(96KPcZP*CF6(DcLIG310rY9M1MEC3rYF1C'@hpeCkk&fLH%NV'3)KQFX6 +,fqM!`KH8!dfM53@@9a4J#G,0JfQVV9hm%N&X6q+QJN`9B()488&RU*Nkk0V""Pi +``48E!%T%3Dpd`1kK&IHZh59#9,TUf8I8,90)8k3Pchi$@1*ejd8A18XBEbV9qqh +aTP)L0A6Yh+5LA[%hr"6!iSGmq%TBX(e`,`FEVRD`9kTYGEMQ9R(dI#ILSX,-0a& +Z#5N&$&Xf6SdQ(6bE(Ud*1Cidk+Mr!EQi2$&#!Z$NC9DPr5CXPEE,cbTYP9p9QJ[ +LZ!EJ$3Bq*lF-,&qfQUUh&dh#5d"`%F,f$T5iC)X,bQ*3L"!Y8,$3f%FRqiL`Mh( +fB@!IHTMkM$@9Q1SXb,i[AQ02698EQ+"U"Dbd"XbeQ`i1F(Q)LbL-!XVcXIecY*! +!#JDam`amRL)'C#3dG4`N%SQ62!bB-aE)MHCQTIj%bmR@"P*6SYNjkFkS$SM#[RT +'DfCT69*CUI)l5)8`221#d$2Jq@Rid,12+IBa34mG1US8Ijp2a!&9I[85#*YKG+C +@)LG%d[&m9fjANMIIPA8"fp[P9*!!ckJS+$C0TU"e6JAj1Qd9&02`l&BB%'UkmD6 +MfrM&fk5,0mNZY[#,5c"KN!"GA)+I5TRL'f8ASq0R1qMLr-9#ba,LCR8eK!$+Xl, +6I&rq"KLK`MT%f3je'G42rHM3QRZ-!6E,''!MUbi6RbN-%11&41G"0XN13Kd(*3B +"!pa!*9G(+XeKCD,Xk)qeb`HcdMr3`T!!rMBCV%b,eZ@N*&-Y+*YEp96r@#iR1bG +Cl*A*'Zr,R-dHJ0qRLI3h5#%"Mr8jFC)BD[5BH`c'FmF`F!c&arY3d+DM[ZlRK2C +A5c#SiDF2"9ShLV1GND8M!PRF"a-UNE5rHqdhGk#3!2%0%H%NSpj8ep4%KNf01b& +-([UJd-5G1kaJ5X1VdE@pB`Bp&NNmPjNjKSTKplBCPm&%M9YN6,5@-Um462I9f9, +Q0B)j'f-bjPcMc$cHN5U$VG,&YlGb3Y8S,DG+MF)ldL4#Y4M-di,M-l32K2jVcAB +a@@ZZL"pLh4*'!E*!Y%mF%GiEDVGlGQQLia-JIrC4F(&6dq3Y6#cmbd003TSk1Kf +(PKifm)G&d`3l'#NcSEejDN`eaI+e$9PZG,eT2pjlaI1PQ9p`I'hiUH!cUardrU( +pMcVrFZX#E9(hLiDja[Ie$idq0h9la$VaUdGqIZIh*rrXb(rUqfhrYirY(acSf(I +SN`[[QLk&V[Cm*c,QrZEij,P[jIdSmmr2rlp6rrIC-r'cPp-cSQEGaCc$(ac-cFj ++qZqT+FN(dRjFpj-6Ih(d[lhpakklVpBfhl[jrCBrZIA6krrPKrrMfY,TcmriR`r +mml6rqS0rrI$[h[[rmrr2TrrlihpmlHZ[[,Ai'fm8,AQRH'("V*IrBH2VqGp0r)G +Yhp[mRcIpCYG(ZrrqXErCmGIE(klmhDTI"*DYH'(GccEmlGTr@r2,2Epqr*p+rqA +qAl9qEZHrPhhjkDqXAllbdBU[E[NX9PliaC+AjMA-RY2B*-P"9KTUX!2C@XB"AX# +f#6IXRDfGjMQM8Q$9%4S9Gm$30cMS5,5rl4j,%q5SpJPH$%TB%`TVNTK[5G8I&SD +T'k&la,BS[UjYYp3*EqINc38JPaLV)p`d[KL9dDJBlcQ-Qbk%lTiD-hZid`C#-e( +J(KX"8RZ6Sd1KZfE(lAFjS)Y4Kem0ZXF3)Vf4$`220&mmHl`@aNqZA2-*dBk(Y"Z +6pf"XNRFD$hDiaicdi*!!l-&pcJFR!L"Tq,AQa@NLG0-XQUDFJ1fVmpN#bKMC,C( +`5TLkB6j-HiiiLbX0J8ML$`JmVBK,iCD'9ULd$m!MU)3F1E&(4ITfkNiL2NV&0Jm +##5H4"5FaR!r'AdX,#(NlZRE+*MZ3!&9!d84$PhX-drS`d)GT,f0D*EVEDF!*FVb +LjQ!l$9TX"e12"qiDBBENX+31&0mPeijj0@9#DJSCe#BGhmJVV)fm`QVi9!CX&K1 +`DDL-**CINi"03b9qhT!!!C[&6Q$$1hSjX%%493hd15*,hehCd2VZ9bafr9984TQ +b`hNM5bGG2pTd5$`c+1j`%f6Ra'"N+8*AC#SM(mB823@&,,*"E"(2'Q`[$U%`SCY +"l!h%1)kh+C*iG$Tp%&Z-*0LKDIK$#J'[[NrCbLl!ie#MCUY'Sq#CAK3cmd$!L)l +809p@V@lMkYS@5Dbrc05e$6mIbkV9(6*eSH0ARZEUiZc`d#Q'S-aqM#f)A9D`"GM +01j-Y`)S2"LQ-$REP*PrC@FRD@E9+e"5`%Fe!M$)eHMChEm"MhrJq,IeL02f$P!1 +j@Qj15ZlCj25,[b8k2`R9EXECj,0D6SV'+Qmr5$J3bQXBkIh[B8R[-Mp*ljKIT2G +m!-"+kj!!NBVi2*!!6N6iP3XJ)3Z)&1jZiD6f2Z&Jj,-p91XH-a1*YUS'DB4UmqR +h6,N0,BcdJh(rPlDb6&aR5rSpED4-"!%@NmJZ4RD$N!$JC&HPl2,G#[RhG)H-r![ +)''52,IRhjHf8Z%!KZh(S)!q[Q&km#%YK$bdN)q%6dG3X,5X9T*MZE(*1@P5lI-" +XMUCbUSSST3kqMQCFbNCf6R+1N!$(EU[-d%YMmYbY3B69FhGIAcfMTd2fkbR6$#d +3$-VU&SQi2riT)qjlkb2ZZ6N`*qj486MZYk"+#'jdA4lZ2SbK`e$@d+FMF`q6f[4 +!8DGVc9B"P*d(ca"'CS,Z)%&2DZ)LlY&CrRDa##T"(HFK3aJf$h1$[jZd)2GB8P- +m1)N&2BF+bq0`5#E5mL0fDLa)ikJ6XNXkC*GN)!&5-&'EBG5'VYfc8MM0`i[i0NI +8L`F`KBl)3femJkPDmGfSjG14q%a8S,hGaiIA6VV-mlS(X!A22k2lfPTkV('$CXN +%d@9aQRrR&JY*9)Zr&%j8LfIk*kV&lAk*kSlk40Aqb-$b&DBR)kSlBlDLHMbCLG- +'@e(GHG4[8Ae-%G@Gdm+)DUQYU1l8j+)U&e&X*(MkSMV')@mpX[Vj%&Ejl8jTP3q +ITVLZ-&8AekjErSNVa0("KE9CTpFXefUYa1Tf@hD(i$U3!05)Lkmmb-U2`CQRD3Q +T'%PR,-i+["k4YA0aHa9(8B38$L%5fTS()qE8mi1PDhaD$hI-H'b24N4Er!Lj+(N +R"'6LSpf(HFEl)C,bM8"iM%`-Iaj@$1ri+BD$ISPK"F5`K"*r'&RJf&KE*`aA1f# +f(@V@RGFXX$!&4Be!EdQ5$b(H%L*i)%K&X9CHQ1XPJj(l,DqliPh5*cP1Z$J8a4l +RA5b[5bEJ*IGGcQ[-#53DQbdXQEI'Qch0p%6ZG8kQJHb&fIG9FNTj[(*h`L+Sdf* +4-j5l%`akLd9V+ff"kE[RdQrdEE1DfjS*h1SRHL`,QaFGXPMd#p3T50DQCfi*FJX +CTL`@E,&EkmPXFIcKdUEZYS9hfql5G,UbHk*6c()Ca'5cqIj$(A54rSb-h4LiqN) +a(F0dNBMrf0-3!8CNd#I-(!`-Z($4lp",iEmD$Ud,e0fJ6N,!*SJ#QeI('AAikF` +a*&d+m8F53dXN`IT#+K1XqPeJMGBK#L8M'jUNJcle)DScRe+GSB,TT33$8Y9N9&8 +8JL$S1[((JD#k$9KqLT[k8()N'D[f-&Epml#XHYj29MAl`kS0QUhbCpk4+EpLB2N +,JiVbAaMd@rNpe0fJ6P+92j-A,"J3N@G"Ejk8"9qUU#S,K&042#084Dk@-a%&mhN +BAeS8FPF&lcLm8YC4PK($'f3CN!$+-Z#2`fD!bmm-Z1jA"Y3j9FFG%@$i&hBC@ZC +bGkd@bKacEPp9m&DTNk3d`C9fDUaqP98$hKiCm'C+U0E&84fmmf1"&GB&phiXkL3 +GbpjFeE%@e(ci54a0FPZ['`c[YZD%)3I#Q1!CjF3KTNdKkEFH!BYlU)"NS4!pL&0 +b9HTkRfNq)XCV0pk,PZU(0)Xck3pRN@Q-4Iia,)ZmjLH,&2M&)MYNQF%FY,(cf(J +"1A,GiE+LJ'9&8HcFhKL%%lT,&q8)Ll#Jf'r4MZ[DaTFUB!m'+if5S2J84r0pZ(Q +(*1B!1*`A+5ShmQ5a$H6d+QrR!4H2,LP0J#!XP!DPlC8r-Ji&QbmJ10A"hHq%$$, +N6(8)C#-5@4D9Yi8%N6QkQ8kh(9f04qpc%kBKR8HC&*YpLKNVm30T@ZM8Q('"6$P +9Q,U8(r((e%A3NDDFM1AJM'A+I4e"KI+E4Zl!,'F',Z8R0VVFBaPHbBbJ2*N0CaZ +3!"!Cj2Ba!,#CXU!iIl$m)VVemJQAqB5%k+jj!C&&j%6df'9(19,28I,S#&'Be@K +$ibkV01KQ,PaDK!`Q-Tk!UeFMpflj)6+5`@''Z5QC)KEFN!"-!UAPa``YQEC',6! +*SFTI3fEc5r,d!Q)&phlk56+YiFr`,HXd[X8U(KNRFi$YP)9D$bS&")h@M0+5Ah0 +G9Lei@0Ed1*1-%ZY,$'PN!P*qLV[RpH4SP`DkKkC6jR$(GrPlQC*C4%*Xi'6QP(0 +5KQE6TDKm'VP`q`A[2T-1)@FV8VkHJ4F+#F2!"1!a(Z((IQrT`jVp[UZdKIjIha* +SDA8CLA4EMH0ke%H)000c`mHV5lGARl$d$3+hFU9E19JNa`V%eV'`%X6[YFb@c$p +C@'NZe#lTce3h!YBc`)IM"3(fK#r'Ye9X&!Ni'#lh8P'i''CE'Fc!`,H(&mpDR3B +'[Teb!i2baG6daiTl5"`iZ`!+Zj`R+@aTCEfEe4!1X(bZKB)"bcETlGSK8p)TN!# +C&"6d28DAFM$FUhpq[$Ec1J"Zi5paQ,M`4@jM5P--`@ilQ3E$f9ma0Dce))P$6'q +rheakRIjIIheY5fYc#+E"jVDM[QdJL$1kpYcFH22b2*MiG2'$I8`0Dc#(IC!!LMr +DZkDN(Q&p%mAe![8!5'DKhILQ5cqBC%qiX,bT@3+eG1K&chX%#9#JfENmCl2-XeP +QfLc2fba2f5bIP5hGY4Z[4kr(CmUIjGA25K6I,2Qe'3B05L3bAe)N+C%fdLp-5AB +BJmmTipA+pd&J$9[Ij"ZIcN2M[$"JZ+Q%hG1Lh*J3EMbB8#EEfJ)m%9-!UiH$!5G +6GVfSJ5(R14Q59jbE@FAjF0L+Xp,2LR1YAa9R@A95i@Ip9C-+AT98S+)-*R@M+,1 +93&UkGm%MC+iTFjX!B*dDLjE*#e5bcEDbcAiel'Dhq,RC4Vmf@k)Bqb'CVLF9lSM +3UqRYf2+j3('R''G&X0jI#CpG&,mDh9Q0a*2!CU)mE@$jQJ3)KmAFP01"(hYi%S0 +hjF4HH8C9*)BTGf'[T)k&$iC6ad+[IqTBD2"((E1p-M)5&@P&XN*1QLjD-&"Z'&M +q5lG8P,%iD1&ZB8Yl[3@D)p4TVYSCRhDTm%,Aa@C,K&EjaUS,,dkLeq*a-Y)#Y4K +5i9)IPX*h3lE(MZd-eA*!0Xc#*G+dSe+P*&2h"&2hYm1UqjLIkJljTHj-HKLUZH8 +--+)+"d%9LcZh3!'XmQh5SF@jHY9mKbiaY(!L)-5R*0idUdXNM9RKV9V&dP9)m(# +B6hilFfbr`!+9lCD5L38U`X`3-(IEdMLK9ZH0+a8b!NJkrLdNX49XJ@)NM!r3#!D ++q1,6%954D2S%VRB3Qe[@R9a`ENb(-'IM5PQBmlaL5V!-C2kFDIZ(Q$%"R+%[HVN +j()8kcpQD%bbMC$eEpimdcT-1fl@G"lr)j,4A)6c5cfD64bS,KUDD1HFJ$feH`!G +-`$"f'N(-lja0Y`eU3JQ2Rki5S)!YD+U(,kcpqr'Ip!H)p5FAF,@8bY55VkJPm+' +0@T!!K,ql69A,4PZe",J"%#9NB@S[P061Vhqma[8)'3H1UGHAF)1)a++2eHYIPPf +2cVrBaC4H+#Rp"T%dL6R&p5Jp(I(Ej'JeY8ZM6d6aiE2dL9p1KJZb6-8',5Gj!$& +C%3K,(JN%9,VQj!)fT!rYbq3X6FEFURTl&+I!UP*&[6`SLB34fJ99[4&Eam#U$&) +[Udime49k-#Z&YNYqd'Sl*p36h2KkCH0)aZP!X(48H+lK)%C"cAXFj-Pa36c1cNT +69$(8`d%$ZFTjN@G,+CM-lGRAp'pF'HYPbR!V6ScIV9193@k-PC(%JMj9'BGX(4Q +rbb4P8%"h'3j#(iT#N!$2'4)PjY3"$Akq9-#$N!!`QqGKb+153T4YYq99frC15E4 +X0MbZE,LbYIU'YiE#EEMbSRc$h0@JXX#28h-J5Y+HT3hcS5Hfi5eUCC2`ZEP4qJA +1S28BCeCfbKaE-&CCq5&c2KfcCH#(2qDEN!!D5[,)6I0X1AE[jRR9MmXVkca@@DH +(VD`cr+bXNrbUV1Z8S)qf4+UDDe6$-m16b"T[2UVi@Kk-5Q3%4`ZUp'&L)PDpVjC +23,0HEFV!"*RkE1,2Zf3A($APcSk6Qd+kV1PC0cXq"Ui6%LC8Z3[4G&h6)4RU3fN +D!X'j#emK"j%-q3C$YQ'"aLHD2!S5UTKJUQKaETYI8qHMX(+E3Jb,632,pdbaT", +"qNPJkX##X)rYK36HaM,kVm0Qp(Br-hU$AaNpcBDmpr*N6caj"SL+!)iqR39c[EC +*3DTYF,Ub`6HEpl,"qfb$MiEGB)@I'jcYe`CI83p,"UCf-X['"i*mc'RC*KYZJpY +"!JbG+2DAm54T%XULSSi!Y8U'XUZSFd#p,%-j9&358$rJE#DK0"9e(5LA$$@SSX# +UM@D*i6M+TD+3!)LdXBH,Q!m0XjBbph`VFl58d3F8-,qHV%*V05UC60IdU8YkENT +GdV'fUNXk[N0GdMEEe#A8-5HQ,U'f16Yj-)i)Scf-#*qSedQfKEQ%4d@CfB)U[e9 +eqAM*jH-KjeM[CH%c6[bfV5[5J#5i'PScb"5Aa9(aJ#J@P@)R8[1Z6QT#JjDP#!3 +GQNY0-D,49AkPm0hKLF1fN`'[6mq4E5V5bj&,10+6j'0AY+'Dkh3HiTb,%0`!Z!b +S)l,M19'VJ$VR4,QGU*H"5R+L8Kf"ZbTb'T!!ef9)688H"HS9'@T349d'DVF6e88 ++8TkVARNG8bU[[ir,$1GPaXR#E9YjlDE!d&BTFj4U+q8RZCHc$SDam2l1Z3[IbFU +)(ZcVddAp-24f"Pij%03iBHP"d+U9XPiE3Z+d89%U$jJ5'iI!eXr+Ua"Q&X!c[6A +0p``e59`'!i&5lX,fQE[Je'q01rLbc@cAKJ)Z%"5Y'lM4cCcVcUjiISI86EHT!d` +"-i4@@`-M4Ji"BDDUE,%S9CTAY!RVjKEB*B"@m%`iJ&D`fMq!9Y$Y&d$E9#0TVTm +9-LTbab,ZPMZGUDT"er#N+pAh,fr0(C!!3Mi`B"l@,*20-+VSP*PM)&!4U0hJNP! +JUeVR`S")VPUCkNDBkRiE9RApIUV1j*IUbRLL),jT9&Pl&#FPrJ5T)9dbEk'%2'M +Hc[1j&UYJL6F8`k3e0BKF#-iZ%5EK6Ya@`(l6SL6F8F%F919dMpB`%MS2#k@ae)a +S+K-cfi(6-3cD+Qf@'3C0Skc1V-,&4iempZJ"A"bSA$a`mYQlbE(UQlb,V2CdPk% +i`KIQQ00K4Z%Fl5(K#E`P(#pLQJCA$DPaS!)"l)H%BpNe3KHr*B,T)ae[JE4(Kqk +(e'[EYDI3('D,dF8[q9GqKI6)6UG,MBHk2+(DA&i0ERa&@(*9JC3ae6R'9'I$-Y9 +P2jNUfaqQQQ153PUrlR@'Y1Tc2IqkYkU!XZSq6h603qFH)LE$1Fi*cBbl@UXLY6K +j8e5qMK%0j%ED)6pHZ)A6qDP@kc4j&k[@LmTA9@Rk8N,'&P5SB`IBkDC'R%YBSC' +'eUMCTCKP&*8hUNd%F%+0KU@3!%*K(f[DQaBMlM'fK2P6SCi+RK++RQ&,3PQi-pL +V-h'R-`ee)J-G6X(M#5h+'0+aPiXkcIPUiJ4Xrj5D-FTa&4@cBciV1h+QcC((C8q +9f$`e@12)298FqF*H,V)pmLlCNGH6FeYaAI#N'q6JAFm[UC!!M-E+U4(epB3S[+0 +H(Q1)%QP4f-X1,-4L0Y5YZ4HZ!-!r`4+ac@C6k&LVfG6CkY5JESS0+"-l`hEfQ0I +aa!G5iqJaFS1f25Bl6)Cd'20@0V#kaN%p5'abAYjNKk+'d20%%V0V&8A%C)USN!! +TSN+f,GXMHRA8E0TXG3XmdE6T!`U&&KkZfGfmSZ0j`pA-@h0$F,RGUZpJr0QMNf5 +Z*ahK935L+Ta*Zd9QM@6fFFAe+LADCXqR`@`!eGYF0i%AAZ4"+,X`DIaj[L$"TJB +H0H83"j%@m22Yp+2QC)AUB$EPrMSZ0F"3Q'mk5#jVb3&3@'!-CIldI66dBI`T9DA +NpLjmJbYqMj3KKDm4Dj2C-*NAm`lcHBHe6,f[)9(,9VEiJH+8,jc"KRl)c%'8c'L +K"1rQ3*`A"HDXYdRX,4N3Kf6r6%+MNMAB`#PHm+GXbAFfRb"i8%'lbVDcfCR"fP8 +T,'!m8bqiCFBQe-3&6qV2'CSD)+%KPNQPf&UF-Y,UB&Qi@cC"BLZ(MPb5$MBS-H% +9+4aIH(KbHSd*AR2!pJRj%9(pR@!9kII$9U3YIPDN-rbU5*0B4CUfPiSdcEq+G&I +E`2,(KbR*-SM9[$$9kDM5#F44RM2jmNGG94P00ke4%K$`*M!XI-U889$LB#3$2UT +F4XBY@GNRI&mZiN6P$4P*Q8I,$Djk#-Z3!%*B[[j+'-)bdjD`I(e#)5cTRh4!iMZ +@T#"F)Yd,Ce15Rda#ADNa30Tf'LAAQ1SSlPFq,1c$cMiml-2"2S,X`mXq1YfeRPT +ZF,`aMCZm`SbLNXM&ElTimqhfiLA8p,)S`!3pA%K)lNT6aiXp3Khbe4KUUc%d9'2 +)8Q2S4SfKVKT$EbT$-,j!mfp4dA3a(Uaj1,MjFI$S6Ja-G&a,me(6`++ND6e-b#D +SNrEcrYQ"i[lClk2TEldbH+GIB&!mj1UhS($+"lX2DErUY`4)XBF@ZIVYS`VL%b$ +X!9,d*d"iTK6%"5!m!9,m"5!F)`VLA5!F!FU)Gi%)`NaQ!Ui03S&eJ!`'+(-Z!HR +Pb"+',!(5'k!-)f3RhrEkA$4('pr+1mCBaaJkmLhaMP!!bpBe,&ZRR%V5NjSQB*+ +IechK%5r@G[+"mH)H4@hV-*!!Tj(a%6UF3&0hr!Sh$#ST-@N(%T0f5iP$Bp19"+I +P+ja*4Q0Na$*-$fShaZrK)F06a8H3!%k"VM91i($0qI4--5@D9j+9FM!hRi'jSV" +JESQIB#lK&jLlCJZQ[RF+)'LA(f"U9dd`Y6PqZQ"UmmfpJkQ!5`kQ!UkU`G5X0[r +!9-'Yd`06N`HcI[aN`&4$krmD-0A3qKmhQ'TS26d`e9$fT`&-0C5G(TK#FZJ+8S4 +4BT1*pj2`lm&Bf+HG*eLN6`p2fNHI$Z&JRd'H`*!!2Vh#bcil4HHlAi&i+8pmc"M +R%FS'DV5"NRVVdB4$3i89B)ih(eD%IhF(!dj35-!LZSShQQEK(3*'N!#$V(0JP!` +Lf86pN!$a"X#DJmK,mq-dj5JPM)Cja+2CdY-`Dc@aj2J%3LRTr(S#""A6F#8bJ$f +aR9r,(`#JlKUrL3-EU&N*p8M"+#Y3mZZ0@THcZ3J'5"pMJ25&X)"dRCq!Y03[3*T +Idb4Y@GI!mRp+0V6mAQUf,U`a'R@53'LJT-S!#L20CFf@V6D1Fb0b0"6('jZ509a +@Z%GUq'hC5Z'a*DDPDp"F($YS`EB&5T)5H90a5fcGKl`4258*R%Gh3QNk%QDdlDc +TVeTH21308ZfeH&KRfr#AAY"'b)M8bXf9#r3,*$-bGL6@I"9[RT-+3TE`cGF"3Km +iQ%aTdMA@'+@XpFNReQK8`AV'9Lq&CDYjrV(9V+kUf5TdHpD$CJFk8PEH*m5X"c2 +(',-&!#)Ih(GSAmiRha[ifF"(!pmDh$6`YAdI$UB0IRr`pAdE"Zm0CJdQG9`Fr,Z +"dAdIGk4dI$a`8V$r!*!$+aY"4%05!`"TZa"9#f5C!&PYNqr@ET4DPQB-deZ'[)a +DkTcEN!$&')l!fdfPeV'9EXK#5&UhYGedep1cke-)Ke9h23b[Mmej$"J1-BD)'03 +&G8iLdSJh%I%(`aJ3BN!%3BKkHh*c2$YLl22prqrEkpZQQd$1ZArh04e@0L32%4! +3!"!3!"!3)HlIe5Y4M`Gr&088FU5%&GQP)Yq%Lpa5T@cDaeaN%2ice%ZZ'4-CTXf +5B#)f+F5%8G(bk(!3cX'Ep2@(&'GpE2"1-5'Pc6d+"-3dmh3eF-)FFrHTG,GFmRM +UE5NV2*klTV'EraLQ3Bi@-QEjKr+N1-I#qKV&Pl*#86kM@krTeXPZh4!0K0Ud'-) +ZcL3T6R3$-$`!rShGATCMYhpeqZVdR,*pT@RTp[`MkGUpf9bmV+qI@aqMSq`#%b8 +b@+0FlCkJf'3ReUf#F[T2chZ(i,!GTe5'mq#feZ&@2"MRDBdH9Xam19F`+TU6iP3 +#3S@YdFb9*q[*a8D(DS03hHT'Tk@*%+RUKI9QpK++26X3aiqjhRV-A6X9d4'ME36 +qmE!E5fRbEU8YVp0BeSNI-hjXq2(LTamr2[`d4Ha"1ahejG+-dJU1'X"CCFHQb9d +MKRlb0pA01L"*Y!&P*c401MMFP8B*(38-[$*#)Xfl$LK1a8P)&HHUKN,0(-"F)9` +Vl2BMkDCe'q`KVDBR,6FM4eX@b%Q9[Mrd*lZp1+h`5,(@ATSCYMmNMlrkY$fK0%1 +&led0Y1m0p6aH[#A!e-IU,Hk$8jr9T)3YKi5Tjm+0q(-jH($UZ4"8ZGF&aca(G#4 +kb$heT1KZQ(l0q-(T8lX[,TRD(FeFlSXqTbK(MNR+Ph8h%$D94)dA6*GVh*cTH-j +d(*L1$ih@@i`B6K$GhZRA2!Q'Nm"`%KK'L1%[9[XaA%i-JlG(#0(r"YpR!d1SYm& +pF1ecQXTSjYTLQ[EFF$6c-ATmJC4-0bf!65iVQlA286A"P,rb+9qDTMb2abd96[N +VQe*b+ddT-CZQV-IMfBUQP0`U65RN8il6P-1V'&NPC66PcfmC6#Q8TRc$TcaVQJ+ +b5[396[Q'69Pj0deCf@+D!V*@0P8dCHAGI$2Zi2+'d26P$@iAYR`l,bjCANHJiQS +ISfR[EX((T`JBeSpR+bmZ@41MirTrA,lCm+-DNcXcPpIG%9AUf-1D'$f)Z&(2aTC +#G$X*F3YLG898JZikPc4P-`h$-J%#NC9Prf*m(%0!X(im-rlLNV9"1UEracAc$$q +U-4NN""Jjp,!fb-KC'qb+($jP(Jd$Z!,NE)F6d8b9r%fl[C!!4-[9IU+PEJF%cYj +X8c8bG3Q,%`"QK%+cS-j2FB%b*3S`b,I,0l0E%[piV"1rl5aAc*fP%+JGdXHRq%H +RmFFeXD)IPfmfrSK"BqMih`FSe`EPfcAcf+h%*P"dZ69MLV*H'kb!K(R'(`'N+(8 +prYd1Vk)%X,+U!3a#2U*aGQaaE(IA+#1UX`b"0A9Z*5Q1[cL&6QN%iTKh)Bj(h0J +J90',2)MqL,bJDepKF9UkeL6lrS$e5J[YQY*LEAkZaVjIPS&eKH9(8ZdjU8G+Fp, +afFkk35B##H'4FL@b"ZiP93XMj%Jq@c$XBCY3dR*r6RmZSh#X9Z[Uk0I,4lplJpr +4Vhq&4bqXN!#D`XIRk*0GM3FcpE4&K1@Lj8U6RaPJC1c`KD+PQIQjTQ292F4@5Xp +*e4l*R$b8jHjb1q[X8,h6d*ARZUj0eqE3e,(J'GKIV($c,pIe"f-!e)C*YXcLq[6 +bY2fC4c+H'eXq5"la%+2FM$*YUUBiEIpB-J*#&P)A5SlJqVQSfJ&8E&e+"`+PK6E +"ii%DjQh(BSjBj)"LM[mT0RAD'bbb5(6GR2X+&TF2%Fir8RK!Fk5dH'`Bl6FaZMB +YHqaKXIr99XkZkm&B+fIA2!1&aE94VFR@dA"9&@XLBS8fh)SU&Y[bSUdk%R$C(SH +0&#X%aF,f9,YBGG@U`L'US&KGXbQ9$m'#2,kPIP6aG6b#6C)SY[(-@A!5&1HTPhU +f1TcUTGMfpDC9)M6Slp`TMSM19$&VYpBjUGV4JSh#@1H,l3X5A*V&RHSE%E,FZ2N +P6%D)j8hEHlB*E2YrmSML+fZRfa!'"+2H8B$MAFIC&j3cP[V%qTJ%`(dT(8%P%"I +fP,K8YJ3`#284Qi`[4DFFk,QZ2j`HcXh*'#XekL%pBXIf'4rEKQ1E*hYXFqF)dR# +k2[CQ(0aX1VB!TQBFfPE@3T0aD$-rX0Gde%SkDK)lDRY`*(6Kl+M4JIAm`$ce`(A +YiA59GL`2c+X0UKr5+j!!JML09d1B+0S$iE6dXH()A[aC3r0,$3fII@PKE8CQ@TQ +fk+)(D0%dc9M9r@+$GIj-c-T+8bHcI&H+,CVR$G1rTF1p`l0F9&8V3(H%P"a*F6C +XAd*!UU$X[DkJmMBTpJH%BKk@%*S(YeZ9fdZhm9bj'dDh[q[S[pPbLjpLEeFqMl2 +eH$aqLVfk0SSpQDklCrpN&,[+acEMf(2*0@!ppShkQb6&0KhlHT0bffJ5$QhHEU$ +Dfk(Dr,"3lIJ,fie9He0Y9(YLaNUY9EYVMSqIDRHpk0LTGLYh#!$"UNlUb!2$5F1 +8H0Xj6)J2R%XkaaaekLAcb[QI!,e'Z'HQ"p2`VcU(Z8(-2h9X3+,[cI5"MX9H`&# +3!)aNC5Y-m6UB#+hX3mB#HZ$"hF["+2)V49-(f*65F9EKB43IJX&MEZe4pK&,fP` +`GAlP8U6TCf#A-U"06U$9@Rc8$Q6hAHCrXUdJZbrle35CS[bKaJ"N[,ViJqa2Y3E +CC#T+VGA5!'5R'-L-!@DJB*XNF$RTS5*`@B(eIk&@DBqA@[f50M4dT!*)6i-`A43 +DeTN([A3E2`aRm3@iMpH,#8%[8Y*dbQP0qF&bTFlc&2pc`SfY1P8l0JbH3P)#*5Y +`j`9F#`Q9AR%[Ra$M+Ef,()TbeF'pRJRLhR[,PG1H(i548YCHlIR"(YHpDXmE520 +m!``Q%)1M@cq2`r!XE"f0%h[im%R5m%Pmq'8-[icKiib'la%6a!"F4DHVcIF'2&I +ZcI"Fm6b*G*XR+FRCV@M1LKrMaBr#MCc4MrDi$RlXbI$Z&K-1"V#P'q$,A-#d#jj +Ri!DIJ`pAHkCK1k6Dm`bI-Qf6#aX4hX&Rk"m2-F,8P3#9"FcpMHfG98UM'!r!4$` +[m$r$!-m`dZCr#G$q0m"i2`$eJQF0T@CB93)G@4mhY2YJD-GcB2fhBd5-Tm28Q-@ +,)(Zm42CiN!"pdC-+XZ-jJFY"hR*+`8-+GN@Np8U1H5`20pH#52bp4h(-+4+l+AY +FYkU9dpbaFBDrHTQrH(Q25h66mM!NIqRGM9GAHplL!hl-!I)@(rCM$K!!(SU56bD +RHXQlHl!C[,86P@RG8RC,'`T*`l5"(Xfm2ir3pI2H%"`9NSZk$EFm2CdH&hYT%([ +%`%e*h#@+fh%!feE2'(+E(RJ,!q"N[&md(E6TB))RQk0aYS6'Pd$N5kQ*qYe)i4R +&i8Fe05"NpVAP5S2RG@ca[6l%RBMBe'SAMf%VM9*TXNf+1b*[8,RfDBTcdmZlUSF +6#pDV*(SN3Eh8+RVZ@rV+4FqV,Cl6(NIac0+JXaYY&U84,YLq(8XpV8NAS&5YT&3 +)15YCi3iL2(T!dCR-Xrk8$Kr5*+'5&KbK(f&-Hi*,eB+I[!5Hm)NYXHdA`3M*kc! +3bcUaY4IaRSQkSRT52lMS6S4Ff1ULVE-@T(pXe9j&4+Mk+$9H-ZaZcXe),br9j)b +PZmk648T!+#GR4I5SJD-LbqUSX#)aHCi*L6P!HU89DE6TL+1fmmRcV*0K[&EkGc& +*E)%YeN(SCQia)AQ[Rd1NkSFUiJjK@kZR1!Ta6"ZP*ETrUN48hV#2K)6L#cD5Q0$ +B`Tk)LlB%)DM(ki"8&(jJ"2Y51R3b`8D1Ql"A2`UKeZNq"&GVNlZ[f-H#'-RG'M- +a2)k85)JqK0k(Q11ec1YfNIK6@3HG$TXjZe2L6YT+Z"-9cA4V0YhD"V&B"+EpVVf ++Na@#!E$X"UCieF&9C80F6KSa!U2A%%M"#S"8Bq(!0VZl99lhD)BaN!$[Ye@MF%L +raSQA%3l%8ql4iM0q3$5HX)B%Zp+2LK1MRkpG+Y(4H4K&-JihiXAC9$2[BXk)"a# +)r9%a[cRXD$Nmj,##1"AXKej3kTeLBLFAmmk0pFTN8$*Sa,B+D*&CjDqT9L*2%iZ +N1*1B,M8)A+U1SV%,@bE*k4pmSr[kJdI4d@r@j-(aVJ`p44fjBY2d5cFNA*a)r,E +KC6kf$@j4kG$CE#Ca`%331[m&[20I`%@4ac5m$S`V2C33mT&9$6"mSk3)Ni`U5b- +BE+4%[RXcP1pij[1T'c")MjqVm20hr+b5EZC$$"%4'b5fYT31G*2[iHFQhVRI40f +"T!JN5-dSI)'M0Ei1dfLqjqqd[8%Tc%A%+!G!ie1-r-EX3EJ0XGbAJhFLlF"#)T` +'hqcM*0l&MCa(SD6i3qQT)(i"42jG)6)9CT(4%#aAE*dlrpCEq5,50+J,@&5I9cq +A$J6LCq(Q,Rk3!%5rBrbA$ij9Vj11J1lJ%XL2PilKFa4,JT3R1[aEDpM$MFD2B%M +H"4)Ej9IIRFE61Di(%BXk2UNf$pjCrkP-rU90r'8R%BMdAeH)A"#r`a!U41BhL"d +0+H(Eb6A"#dpLJ!k(FP4c%2BA!@"r"34@KbM0EcBChM!RE8L(JQ(kYpi9lfQqp"p +1B2rF@LAqeG#pA[8k!9mUhf++T#'eU4j13"L(UTMN+((Q`f(!YjCb%mD4m`!Z[ca +kk4fP"eCB8V5`)Dj,4CbkYD)$9HfiA09SBieAY$DQD*D0*YG01lNNk4'SehRSC3Q +[D1KSj($%6QP-(r&UpP'5T+U@0M"dHMC,VL8RN!"d!6mhK&X`l)+Q%S$Ic!hACf& +mE[D8!23Pq2KX1)CL`8Mk6l83kd3R@0r!MH@'A56!h'm[pGb!aiF`GCL+(+1)kE" +L3q9jfr-HNIRl$-pl(Jq[DZq"M"L[V"-Sm2'VjPG*e6c*50aAHkj#94p!+X#3!,c +*b)qfJ4pYJhbdS4)XprmpXcMVF@!5PSVj2S#I(1PQPL3'aNR,HRN&HJNr2q2%rDa +L-6$d'CM2mMc!JDFh!PiIIUlM`1[M`00ci%d#m25Hm3$HH(bF*!([1L2JA@F![1X +Ni,NNi,Qimir!pS!%YJFJAYEK#-GPp9qmM`[!U`*aaAZYJ!NUT*)!bYQ++RlMbGX +YIY8q+f%Jh2JPG91aM#qjH&V'+r%bL+D0*Y%dalU!$JX8G,h!k5ERlARq5mKL6EH +6"0["(ClIF43YNJT"c1&#M4HmmIbZhLBKD*%IJ[lV&!Qi6d&QcGHbB009+0LHjF9 +3fG*6%%$H&BKM"i*3ZX64YmS)IFrMCb0(hr-FIDXiqXB!IDZ3!%*0DFG)cTA3Yp% +)I4X0d,G43YpK#Af(6HLl5d,IACKf`P"iH8iB#DppZ@-RZE,PP$&QF*2)B"[f8&b +qlE'5a!8+jARIG66jEei`99%j5DJCE@YG+V+a9P[cr`QC(2@5@B1GEL3TGj%8a`( +C4!&2VG2aRV#+##bcQ3@C&0EGaK-%*FHHMYrSN!$9613!DA,!DI$"E2#"`LiNT#T +Q8fKb$`Y0T!&bH(+23AJbhdS@XB6L9abLE2BIq(Z%+FCT2+kHLX,4LF0I#NIrC0q +lBq5Lm[p%U$NaGm0BK*UZikC3mjeA0G4m[iUKCLf53ViRSHD)FDJTT63pAU(Q5+e +$c9GKbqGa#$9rCa"UMKL(QK)3M5GX+K*U9MXD%'TZp`XeGA+SkErPpkEVHaTiMVb +L`229hLkEiKGiMR#$H*2'6#Ne--e1fM)m(S5FCk3EXiJA%$e8$*f&P'%BaZqVUY' +&E')ICH&%KKJh)UqM3)icG%U6,R%MC6dQVIFc8(XP*CPL%)Ef3V#fA3[9mYc!Tfl +"S09'BHLA2!4&J9R*`0RL&m$p&6rrj)E#2`d-8#6BXi-fMS-*qSr*"+)53"VhX#- +d[L#&SJ@'S5JRFh%&SHJ0k")@aeFp&&dmd9$dHKcKZ(58-ii-I`1)#1C'@qrM()c +@&3P''lS)4Xp@5',Ai@L"`B4DKU166r@5`p(Y4F24'*('`Xf[B9EaMASHN!!fM0+ +$U4*3i`&9$dXR5ip"@$SLKD@5fMNE6)'T0ei+6"YdRS9iGCCA2`K![l"d2Dpikj0 +-LQYCbXhc6E*jAL6Pi3*h'E%`G6X3JSe+)$8,a[SQcaUS6``,C2'L[j6m3%@-&KL +Cl!Yi@VR9E-IVAQcf[+fAYRZ-JPB8IIV)m`iGiD%-ccZHKGJiH3F%T[#+r35@H-* +2*-b[-'5G,i@X%Eq3!(8,2rB@kl'(@P'Y2r(FajP2-`TDCdP"+phF*`Q0DAj"d@r +`Xi),M49&K%Dla(i-AYmAG5&4FKB(lqSZ`GZ(Rh95)%[JAFh"1`AJAHejA3,[&#V +FD3,[1L2`VLX#hR9qi$8)DaqLB*S!1`Z#DP%&3Hem",A4#S*DAm8LT2(CbB5e"3C +KV9A-946@GV9%B+`#fm@e$Qc21+Ul%*-I'BHfALp(kf+Mm,D1SrM,,P(m2(9I8V" +,+2k5SrKPS2K,cjX5LPr'cjXQ&2FDSELh#)Tlr9"X%2S#aBXp#kqP2bF-K@3Y`Yk +*TX-EKEdM21aPhAL0+I"e0KL%[GX9%T%8pT,SE+K0f$Za8-3Sl(hJHa,f2V"K)Q( +[b#X*HhQBa-,HGakhX2IpUSHp%pf%03jl&m+S@#JIjGTLcViK&m820%F+Y6Pb%EM +#R1I#@Z*KYp1(#X-[2eEA2mX+&aLb!P'`9`ZcFa#')kQRDij&@0kbG@)XpdqFT8X +1EY!dS`eT&mI##dYF+*9p'F,Z(NT#S"3&+YL[A[*K2M8qb"-8PR&heaceN[Yr'ID +%A'kICM'UqXXKPd'5`Kb@T-"Z+8%"dhK$'MfB+TUp0fU3!!,ai45q!"DXiBA22Nq +Y6d5kJXZ8VZ#$N!$aXZ*d9&JqL%)i5!bCM53'Pbi"k5)ZeHQ`46rDD6Qqqk4VQeT +"FfVE"+KSTH,N4Pi"Eqj$CY#f$HV+Nfh3m"i2VDMaU3[ee%62KASH%T9Y4f,-+*U +dK*!!IRIkr6[h+56%)-aMeHr@ImS+!KQRMN""el'd%BME"ph6Tij6YB6`4ih#IMS +c'DV@)d5LR2aHASLU")dIC%93,"!(`*r1Je-Rd5(3b"%+i%dG,lU4U-m,`0UAF4- +$4pY&4kZ4MMD01RI6dISJ5QeL1hrejX(-U423$81SHelRM5iJp)$`IE1V*'Cqa!J +)@X0-S3S1ZBSG8KF,pTR%F+&aHG3rrF'8bi"5Q9elQk6[Tim8NblQT+9b&p6AaB@ +bM[Ur-a9YCFeZm5EB3N'[cR)l&BC,C%@c&JXP!p4YPh8'3d'%`MGpJ+4V)&G-E1Y +$J'YKiA8C,k)Z0j[PLN0E"+8[D&qe!jK)4A)BUUPlqXSX65`dIH@H3jNVpkc-#Kj +FQ58H#`BL1d9,&N3mNZ)Y-iF4Z*XMB6-DUU%QC+e&ML1Xb,&A[H5ICb+m5e![QGN +8'4D2eAL$&h8A$GRAJ(ePSTLBp92&U6eER4Kk'Xb8qNqPiVjkhK(RX3AIePHEN5M +eX5k!SX("K#F8TmBTMML'1VB%SlUG+2lEcJS+5k)B4H%qMm1f%4Uk&FpaSSq*!5T +L&rNA%1ifUF0HZlffSP)'*J4iH4T+HD@Le'i4-"BB(E3!B#bB#"LcLS$4"MF8Gq' +0G+*j%5X)r9J$K!Arab"m[bJ)UpK&9!a*3e#Z-MV`+S"beGL$FZF0IU$dB`e3V[U +I$-VpB`R+fKFMRjPN,8DqmZJV,dBqmaI@BZ3VXkY3M$b,'PqQ*K"3Q2SCc9(CJ6h +ieX8P+kI*VQdb)S0ZhJNB1,#a$%mMc"K$)T5@kF,TEH63jX@aRe&M!91c"BA&4p* +*YYVp3#dAQQ`P`"ThN[kUPU*(8HqB62Vl363Ge-T5YfIQb`C)%%)qQMNcAjTbKVS ++iq-9)EY6,[$jljP($QMYA9-S0ce+95JS05B(NkNrdP0bS8ah5hXplk,h*SRZMJm +kcI0HShp,ApXi2'[hpedq*IlFf[@IafNAm#4*YR'a[5`0fGYf3`RC3d8jk(rQe(b +SJEhKTBScMk3I-#+&0I$3MN1fV0a3'$QXq%!B5,%a8Xj5F'M4plP4l%56"a)qdFl +L982D1!%"kB9(-LUQSS[P'A&M4i5U+"(r+K14NCC6HbTi%lCIam(*G5DBTTaDqdP +CF&rlGUIY@+1LG6CB(,cBHbq+ZbmV[Ml,V$4m[ml&5EK"h"NIaaZXcA9eJS5FY0a +`'"@m!Pb`PDUkd0+*,B6SF(),3Ec"BDG,8lbGD5K8Y,iXj[9&ADIe2eQ[A551k-J +0G`FBl5mYdfC8U$Q&p2pqa9pGDF1#e"@'8$q3!(9flISbhqf@@ml!qFq%D4e#,2h +DpB%il@`M"Gf&T6+1T+CD!pSL5PSB+14SZ&f"Nl(*eY1SP2A,`*K6h0"J9QM,J)T +9`m`dKa'S+@K'[X+"&RRJFIdaaDG81#M2HFNEajRf,@h,$YMZMZk0rUFC@&M9 +ecaEb*Y+V5N@2CMc$3p*(Tca&hih2cXQ3!#5D52qX`J&HSk-e6)D4B$63)C&a5CP +5a5Q#dF#crJ0j8m`ZD@J-6&eX-'rQ'6G[-QQQHDi#"*+53-QFC&Cf$Yq4TV63`qb +6r-'XQ0q&)U+`ej1+'3f[,jPC4X(qi$CdecZJ'UH0`fb*Db%93HZL`ZCrA@c3F-4 +T@L`b$0HZ6@@$'idC&bmM))ZTmTLTfe@$#rk0r*K@cjDF1DJiU+3q9&,GfIESD23 +$+F(+3pA8De%[q@-aYMdEN!$'hYM[#P%eA5$Zj!k%b3Q$3Y*jUPe9AHD4L5e6N5M +`@qE6"LbK`e*e%!U4$VKSrEX9I'l!jdSbQb+0cbZVGjFjRfq-*!DR+Q*NUP+CUVM +hJ&0hcG"A!f(c'PFK8[hMQcY$SC2Yde$BiR&i#1aqGN!laY&Lr$'dqpNpfUAH2"a +q(Jjr03kIPQ[#Rk%TeV-h9-T%5iMq3Vk%*"(cU4IJL%"P'h"`%3G[kM6MYK+hZKV +c[#[dEqQ9'iCR$93R'S#MUA1UST2!-FB%$N8j*bD%QXN4+Ri9hi&Qd"lh4`kJF`k +[rfF1SMN%)M1"k$5C"4Q6Ka$T5)L,qJ!JYGqiFP%5Ja6LM%%P1iT!d4f)BeARamE +eeT!!p9lr5QV%D*jkbD1AUm#Sd-U)#[X$@@EapePYaAP8A",TZE`"J1C-R+l8DR+ +AiRm$8@5QKdHT#!`*'jhaDUH[YRqXcG#Q`N+VJ2`+f%5Ub+E3'!M86"eV2JZT"Z4 +iGF*9Xa@AI`L&UA(Zqf'L1[Nfbf'Ti5aANY`%N8-Aa@8ZR-b0%Q60E-ePc@`CZ&& +Q5d@IV)eVlIF6UkpqieU",Yl*33f#!h)"dPBS'[@Ebp`TBN+%YR(G)K8QaSZZ(%I +L6YjiQ'6SZlkLRNH9mHUi9+$UK"RZqT5k%2GJQq`FKlRrZ99GikS"A)3V-ae5!aF +me&bX5-9eI&,M,U+I$KZ#4&D%1E+*Uf20&ATYIEFJPS,`'+kDBh6f)QYJ)PSQlKK +&b12Z`64rTbJ5T1k4A+*SRQVfDUY,&!hV9HVkYKe$'1bF1@`01lHjBCBl)m8)pP% +d6Q@10#HG5aM9Y*U-Zhh'$VZVkl+eK36Y3%qf#69@(0"RJNQS"lI@!"ME5GKmN!" +#X5JHZU22mFEpT$!8J5CYQ[CVmRJJZ!!IET3HEX6$$G,$$AMiKr6`$cc-PalQiq( +[dX2ImA#9p(!9(Uk4(Ul"`ccTB4iHlT-HlX2$,1PK&KiHN!!H(UKGd2kBcbKSelr +bS2faZif#p[86#GTcDE13!-#V[84X9pi[IrKUA5!Z*AIJ5(jDDSkL`19b[fDlHhV +"l,3c#IJM(LbB24-&ieE1&Z'm381-q*Ma)%r`QH#H[Q)CNJ!b9ba,D4[%3hdpFbZ +(JNPJfDFpA!U1B,PL$M&E-8FmZ')1-5ZB)c0E-5F$MZd9ph!fph!fpcaj6dTZYpd +Z6CI-)bp5i2[$&YFS(0@fAGLqfYFDG!I1"mk6%+b2K@IhMX)Gbjfam@&k*)GfZ&9 +2'eh2QVH5-++A#4P86*%C6Qm-3L-23'4R3)cN&ZE3BUIf)9J5+qZ9##BrSGJL1HD +Y36GF9ZD1,6`p,$Hb-l[eRHBDLbkaT,XX%S%M("p1ePJ+F-%BR@AY*aSEUSpTH%Q +hGVjSDHY1$8$-fC!!VX,*L&q&jNMi!YY!)-L,XI!f2NDG)TYkQC'ART[2laJSEJC +j2KJZ684%q,YrD4Ch[[e"*"UqkbpYZp$JXN6db$re[cTAkAE281d63hH@NrP0$V( +EGLUf1d2Nj-DY1A)HblE6XPr(4iC0$`q5bU$CS[0hBK"qE1&@-D4[#bTC6bMQ3ZH +f(YSFqacJ1mF),#l0B-3TCq3`-l9e8dGiD(G(f,Rl*dLqi83&4fl5rqTmjFlPefQ +F`5F9fr+rDFb$cB0JLXD$NDTc@bNR%hp`Db0AHK+DjER!&XR0N!#@i@C!24$Ek*U ++5EMJQVME29AaSUNYjqifh2DVE&Lk$BpC2L5Eh,EEYc8Ppf5#"%4MA(2R*CQ!eI@ ++lS9@hEXEqX*$qMijQ!XUcc[A0dGfr[1[iB9k1"Yaf5+,r&+R30Abd*6(Tl,#SBN +,01f",B'RmBUK1-`E,0@TcU,a5aYV0Z-f#0&G"&BhE9acKf,ZC40akH&L5H&EcJ# +-69"#-Hc8GfMbDlBK*6B4j0%Ph(B!01'8h1#1hf`+IrUAjR!VJA[MRNfZd@B-DGE +%$VGZk9L`jmkqk&CaEfAJLrIJp)64B3@b$VFL"2PY)h5a'mC1CYYX)LG6QkV5fY' +NFMC$,N$P"B,lJFlQL),d!RhP$L`i3a0L"$"Lp,Yh-c+FG`,dr0B-j9cZSq3hjfl +F"10AJL6G6k%#kc4$JbHk4ViK-4RD`cR-*)2!D%A+B1D+eXifmD[EGLM11r!!CF! +ImE9$1eDd1Q,dTr1e1hD3!)RB*kN20Ei5*(F)QL2Y(&RaL@DSqEf95eDdDT`dA(a +4CHjXCJ`(JI,"CR&(d%iZNQk6!P)h"8&NbaY9&1rhKdBpRQ@+E43TJ%TC(Llad%J +AB5,dDeDKiie48JfFc6cC#4Y&A$&#CHcQH+V)hD,Xp@9D!%IFEmqh9[B'+[3$"(p +RiqJQ9l!a(#KaP3eP"rMamqL`MKKEl[LbT,KJ&-lfZ8a-NA!VJ5'6lc-YfiZQ!qB +'i$!lj-E%"SJYjeSkqJ+CKC9JaX"i+Yca1`2$l-9[T'iF$[$8Y%al0UbYF&TDF6S +RQ5T8reIR@CA"K40-3TH*DYe1U-3**"1d-m&-&iq$qM3rkj+Ec+9dEJP!%pL0CL- +AL&MkT6,YC"Br4BZ,#-iLGb,)K6,8+jA8e6c@p,Bq%Vdc!21P2@P8Cm',ZNiR5b+ +T9'S8+$95AN8NZX!)Fchr(r-(a-3%&bk8XlZ`(),d!pejQ$j1RI[9Z3hd&A4VTcP +DM&Q(qQM*!TIf%Gej)M[9aT*Re8Y@E(dEKAGJI!8!cUP+!qXB6ZiY29b4122GV)2 +,,&ZjL%-lR%N`3JT1jVH+&Rq'`@M5X#lUEY-FMalqcK4kpHeRaXMr(+j&A',2rD[ +KbUJHbA6LF9`bbqcJADQRp5XAdT,ZkFA'*j+(EGf9AHY*`m"`hKq0e-+1TB6, +4R66-',lNMcK0k@&YQ49eR($4N!"`G2P0[cTR5,i1j&G'G`%JPHH`S@2@L-&i8Ra +92c8Z,*&LSdmid*c"%i-c'!Qr0P3H2b*iT6CVmULbi[M(5Pcj0PBaIR81f`j50I[ +f-e3c$1M1j,`NCc%jEZ"LZTi",FL!PZ!2Y+#LXh#f,I85NCm0"SLa4(#5j0,Ydk4 +U+Af#+Jl!8GP`U9'[am(Q4&A5B-FM)-+0kMZ#p1i@PG5Be'F,3HDCS3-,Ed%cb`F +JKC`J+3CA@)Z+40N)c*Fe2*!!bMf2+)@hH)Nc-(('!@qmP0H[#FZKE!mY"aEcjF+ +%B-,DBZ"-5#"61"laLN-*He#"1pYrEfY8(YENYDp#8Q`-$c'0%`m2KejM3c4AFCF +EY"HjU$N-(a"p%)(Dp#iCaMM$KcR$KcR$Q$($rl3bc%NcBNF'`feSNP#*i)q)*#5 +GiY02Jj-(#8Rij)1M1[H+RIUjX"d*24TYUT("#G3bB(b14MUbi2DKBRflMK)M1"L +M2(fZ4QQUT`ES,UA!4!S2P9##(jU$4aLTJeSkJlKaMkVNSNbj5,$3SFN[(bi@De% +509kC#9mQ"+03B31[82aiT-b)+*r*4BMP)NLRDp+CJ`M'9+&G2BUZ3SC$f0`b*&3 +J3X@j4+B"H6iVHDCM+l*UATD4,P0%*)N%*MSU91eKMFfU41Md2EJB&bBL93+Zf%I +QSJ)d&U#B6RLI(LNQ$jUTQ4401dqHIjKBb"2#PK*AjRB[66)R+(mdijU+miCUP0X +9mfjmT0FF"3d!Vq9E&abLTmfr&a[CC$C3SF[`QPP3`)qa,XL6E$4j5-E6D@T`'6Z +AaQCGfZ&P%rNRQc%a#B4%!"YE`K'iB,hXmcQl(Df,fFR0`!d#!-%F2Bb'k&Ph0GZ +0T1e-FYY'#*c%"%VFBXa)V[JrAja%lUrF!3di&M+3!%)8HMQLkH)&qRF)R$Y0)(D +k4kfJ*@A6m+3Ll5N'-)5-3kRl0K'!Ak3#1+UEB6k1UNiKc"Z5`@%UKX%[eqX(0K% +Z1JRa5A%b#P39)5Q2N!!ibrbe`Jj'bSJdDe(6+5rcmh)#3d@6MB(UlC0CR9A!V-& +Y8H@&B`@ZUM$L$dMU0b%&EGcj)F9@)9+UMC!!dQmLC+Sb&mcUPEN101@#%*Ch1ID +4J)3Hm#kd+Mb&JM)CILS[!93@RpJfTJTh,+80eldm5U*jm#5UU4-0cX6a$BSQ@A3 +$V8j8C+QaB-DQ@m2PY&(+24EQK2eL042$&98kpNUZC!N+NF8q-S6DF%eAVFfiSV! +AAbY-C*R3hX)'bG2jT65NKH#FNaUmb9r$LbF0C5Z63aKM`4K#92NBkYMREJfA+R6 +-4(,ZRXCa0T9LQ4eeMEBK&-V,JlJI3YAGTMb-`1k13m)AP@&2$6VKrQdJ&DJ3YbN +qqS$J)G,a'"ccfj5kMF1BE0(L-RYRKViH1$NJ0Y+3!'kl*NG,2CBL1bRbSq5N'*! +![TY%,)`S+eJ"a$`#085AY$`6L$+S0$U$`rZ-$ZmhK3XHA%T3!Pj8Tpc)Za5Ub+S +b'@RI+USNaYa(P5,2jP3XQVd`#he)Sr'9aA$aLlU#UBU[d-a!kQLJPfep8$8BZNM +6)l(MDm-3ZYL%-DSGHHa4%KE(B$$Y3aG&65"%NH!!iaN1J'BZ(VaG(`0*cECGk)4 +N4,,2!#k9QP)N8mS'%(YK(0DcLfhNEf#"$J'EMDYRJfabC5[l("deE`U5c#YY*KV +dj9h-+6)(qTPCHlCHZ6XX`Nb'Uc4XUARaf-!'9f@Lb[EbJ,JMp*Th0'&!X`LGf9R +4%K8'K8j,MQrQNfJkbh,IX'YhYk['qFJ9$+m6Gb5pKJ6-+[m"fr8[h,kKlhkD(M0 +pA"5fE('KdXDNaIXdLl""HTL,)Z3'&b0NcF34e*3QXq"3&1iakN,K"5e0Xm-'06Q +fpR'R&P3M-bqF9q*+ik+&JKe+2e)[qD,KE6dCPET!C#6FZ%+['KTX$MHZe@2KQ+D +&Ua3A2(a+6"1Mc3"bjm%CE%NjbET[HMN!GcK*%c,hB-T-rb++jJND#59ZHUKQh3J +ZFN20@0RF3R`FE@m$m9&(1ahLZhSFiKjb'Ri4c@iN3a!Q5Q'j9ST"dLMRPiYGMR$ +Y""KP@)#U1&f)J3R%qRDJZjm[SBZi81MN*1q@T3X8mb$BENG$9@9@RPd`jDciCq8 +8Bm@,mjSB[@'hldmYKD9[iN302,mKQlkQckJkcKqGiJCYqR2TGXQU*j0abZdFPI4 +C-XRTF`4"!KF0j9cJ1SPYZ!(HS-6CCPl3T3%r1NHPHdpDbj!!6[P3583J+LU*$Up +l6l(0p-QV*(B11I*53A#U4fbNDN(C&eE6&i6CHPeGESYJmqRTr9#``["cKT23+!" +H$p2V6"6d8[Kr&i5Hm9F0#epGqFpcU`H%6fBYq0N#BFZMp*rDIGGG3UVk0RA+XHq +V$kQcNLmN,aILm62QDI92PmkD)D3+*p91YH@aj+`VDVI`KR"0mM2khLmq&$+%[`X +jFj,A#MeU44lBV[jdG[,8bqUI#e1%CFN[*hrci6FI#@(KMp*)BGR6`UcNbmN6K"2 +VNVqC*ka-[Lcm,hj@#qA6K0q1&j,(#b@FYSprm1$jTmH0HhT`bjA"Z)%Vh30#3#J +62RlkeeHq%Akp),P$k&Z``b2J8R(+3qSI6"2HIr44iAmIII6MHfB)Sc1%jfF)Nai +6PTiBk"EHQ#'X9aIf,%Zq,6PrF,m`@hhdf-(Nlb4r0ILQfYBXR"YmF0Vb[42DK,p +-+qQa#h(6"0Ud'Lmi!"*lYlU-X4AHIf[4SLr`)r`lAKbj8,$L3X&bGFTJQr"EGCl +`dK9K3$hcUGI9Dki)*eHG%-je#"d&2e3I6ABN[b8F&[jPk@"LEh*1F[0J@IFh`X[ +5L'pqU-j*rQAbQm*[KG5PJc8I*KFQ$kJhFXVb-Fla`bH&kF)5GD2`3q%DiIM6*eF +)&iA"RY@[Ulm9[K5Q$Mrjc@@Kjm,2ZSAA&kPI[0)RI(c0K!Hq'G-Rr13(`XAahH1 +lCj`I*h3[RcC&+&`jir8C*i@,mi@2IrhM%bm*&em6[RhQ0d*fKr$-VrYqG+jlc(N +!N!-B!!!NL!!!9lJ!N!-)!*!$)!!!2c`!"kR`!*!$#PM!!&h!!!"G`!#3"#j"9A- +b!*!&!e0PCc)(Ff9RE@9ZG&0PCc-(Ff9RE@9ZG&0PCc3(Ff9RE@9ZG!!!'Qi!N!- +"GJ"1F8U$CL*"l3!J)$`r2!!!)MbTm!!"5N&Q"%T!C`T)3%K"))!K33!%3UG"q[r +1d2`"!#m),c`!!"PZ,`0K!!+X9)pR3%)i#Pj#Tbmm4%&836mm"0@S(h!"%F!+ANU +ICaK1F6!mUA#R4N2k!#SLL%(k!#!`2+P`TNG+JfF%F!&1G8lY!#*1F8U$CJ+Tp(! +!6R9J"J#3"3&1F4mkrrC+(fB551IJi%(krqT3d%kk"Dj-h`F(,cVrhNjeB(*"6%& +%4%008!!$!*!d8(*"E8MRB2"d8*r#,dJ!)#"2)P3aD3!8!"JK3!!N-A`!!3!XdT% +K33!ZS!,I`NcI$`C1G8Si#PjR$#!U!!KR$#"!)""R"Lmkri41G8MR(`C"q[qHF!` +L+J!%`VJ$'Q'NCJ!"2NKkrij1ZJGZ@%q`H[pDCJ!"$U%D,JJ)+J"!!!4R"L"i!UD +J'b`U!!3U+J!)'#S!"*I8PG3J1[p@S4ir1!)JCJ!!l&42,%JJ1[p%)JE#Z!-D@%& +K!2p-)$Vr2-#i!aT"q[mi))"+K@B%S5*J"#"&S#GQ!!#d+NJJ$P#!3IVr###!)$V +r#P'!3IVr!##!3QG)HJ#m,a9)H[m#,cVqiLmkrZ)[1[l+,cVqbLmkrXTK!!e1-"p +R)$m!5S9R##"0S#UJ+f!%)%fJ)b"1S"mJ4k!E-Gm#)'"J)%kJ(b"(S"Yb!")%j`R +M'H34!!%!)!)"!1!J6D"T!J!!(i!")%fJDYA8ep4"q[jf5T!!C`K`!D#BF!1JQ#" +0*8J!#(!!60pJq%je60pJq'!!rVir!#"1S"mJ4k!E-Gm#)0A8ep3`1!)J-F!+B*( +)*8J!#%cIB2K1G8j@!!")j`!i+'i!$%IkrLT&q[iU)"5`NQd%)")SJ%U!Ea)J8b* +Z!!LL,L!8dC14NR!!B!3`22rC60mF!%jH6R919[r)51FH1#BZ!!JS,J!-+'i!%%( +krESY52r83Llrb+%D,8Mrc&92U"``(cS!$%8!!'pF5'lrl$!&8d8r!+J298m[,[r +XU!d`(c`!$%B!!'rF3LHTQeP2,blrl$!'8dBr!+J1)"mY32r`FJ%I!DQE)'lrm%U +3!'F398m[#+QQ-"p)`()%`)&Ra#mZrr#TSf#m%#i!&'F+@8mZZ!+Q)&qJ'cmmS2a +1ZJ5'9%mY32r35S"R!!'H,`"1ZJ2@@%p+!'F+F!%G3!!@6[S#'%KZrq4)E[rJ5'l +rf%kk"@T2l`!-)#lri+%H,8Mrh#!)C`!"C#!Zrq5K(Le)rqJJ#'F!!93[,[rN,`K +1ZJ@i8%mJ!fB!!+CC6bmm3dp%48*RU"mJ(be!rr"+J'F!!)iJ3#*3FKM6`5m*6VS +%Y&K2FJ1`3@Cf)!dJ3(!SdF!Y52rd)Qlrm#44F"M9`#e+rrJ[#Nkk",TB6be!rr` +JE[r`S#P35LCZrp3R5J!S@8m[,[r`6VS9c#!IFZM3J9'!*d!!,&925(Vq-LmZrr4 +)E[rm,``["#mZrp`[,[rJ,blrk%kk#XC86bmZrr#TSb4Zrp3PE[r3!!`PE[rF!"! +PE[rJ!"3PE[rS!"Jr2+'B6VS$9P42*N!r2+LI6VS$5P42)J!J#l#"CJ4`!'!#F!% +J!#9!!"`P4!!J*8`!*%Kkqm`r2+$m2cbJr%kk![K86am!6VS98MmmSCK1ZJ--9%p ++J'F%F!'JQ#"m!!!"@M!35-$JJ()'X)&Q$%(k!+iLI!!!!c`LL"em!!(rb#"Zrmb +J'e92U"``(cS!$%8!!'pd5'lrl$!&8d8r!+J298m[,[rXU!d`(c`!$%B!!'rF3LH +TQeP2,blrl$!'8dBr!+J1)"mY32r`FJ%I!DQE98m[,[r`UDB`(dM!FJ6!J@F),bl +rm+QLB-)NE[r`5T*R%&92,`UTTM!I5-"b"-#"CkS[#UQMB+33,[r)(8!!&NcI((K +1AL"I6qm!$Nl36PErr%MR!$"#,[rm2cbJr%kk!La86b4!5S"RA#m!6VS"J&K25J" +R8#!+*N!J3#mS!!`r2+$m2cbJr%kk!Gj86am!6VS81#",)'J!%+!I)%XJD!!BS"m +JI!!!!9S`%%M!i)"b"V#"CJT`!#"m!!!$2##!(A`!!Irm%#lrr%cI$!"1ANje6PE +rk%MR(cKC6kPe)"mU!%KZrqLSG#!0)%!J%(+'d)%Y32rm)%"F5%2Zrq`Lf#,B@8m +[2%4"9%%r2!69UD!J(bK!)%!N8$)U!!L5DJ!%2!%d+J!'P'S!!Mi#0LlrmNM$1#l +rlNM%PS3i!8M%PS4U!P+$iS-p3rrf0Llrm%M$1#lrl%M%PS3i!NM%PS4U!P+$iS- +p3rrd0LlrpYC"282rqM)Zrr653Me"rrKC6d+R5'lrp%Kk!'Cb!4m"FJ%r!A,r,`& +#*d+RU4-J(bC!,`#SF`D&!*!$H#m-)%Y`%0(!,`LSpPP2UA8J(l#&C!*Jp&92UA3 +3(fB#B2C`rcm!3QFJ(k!b,`ZT&#m-UD-[,[rSU(0-haci6Pj1G3!#!!"19J!!51F +!-#4Z!!JJ#LC!)%!L+!!#$)&"6%&%CKBL+!!'$)&%3de3CJS`+!!+FJ1`3@F%F!" +J!R!"60m-!%jH6R8[#PP22cbSER!"(`"1ZK*i)&mN5&P22cbUER!"(`"1ZK*Q)Pm +J5V(*CJB`2!)!B!3`2!3!*&p1G8j@!!![!cBZ!!J`!dM!!S!!!!J!5S"["(!"B!* +`!#BI6Pj1G8j@rra)ja`!0Li!#$m$6VVrc&42(8$rr()"X!&Q%!*$"rp1Z[q!X%0 +Z"(!!B#KC6cmmU*p`!4m!6VS4m#!I+J"C6cm$(blrr%kk%H!J(bJ!X)9Q!R!!60m +!1%jH6R919J!!51FB-$JZ!!JNEJ!+)%SJ%#C!)%!b%!a"384Q+$)S!!)-3805CKi +f"(,rYN&R'L!S!!4b'1+S!S!!N!2r-J0)`E#"C`4`!'!#F!%G3!!160m-'%jH)&p +F6dl36PB!!&925'i!#($r2`"1Z[q5%"pR%L"Z!!JJ+!!%FKMLU!*!!2pJ!R$r6Pj +1G8j@!!"96dKZ!!K`rcm!6VVrC"!ICa!JEJ!))#J!"!+!!2q3!f!#F2p1ANje6PB +!!%MR'$JQEJ!)+'i!$#",-,`$!A!!*%`NJ#Bm!!!"*0H5"T)!!!*)"T)!N!-J+$` +!N!1!fC,CNLJm!!!%N!$CNYH5fC)'NJ#3!h`'NJ!!J!"`!#4Z!"!NJ!D5!*!$*!D +5!*!$)!D5!*!$5!D5!*!$2N*!60mF'%jH6R919[rN51FI1#CZ!!JU,J!-)!XS3#e +!rqK`*0R!,8crl(!JfF!Y62r`F%MC`#e-rr4`2YR!)!b3!)Z`K@-'F'91qJ#b3N! +q!%*!28$rj$B(F#5f3'4)F!5f3'3%F!"J$(!!-!0CJ'S#9S$NJ(J!1!-Y42rif+l +rk#4%&)!J,[rid)$3V[r`)%!`V[rNF!%8%R)!%J,MB0&Zrq454f#`3N!q!(!"2!! +f"h!IYN"N4(!"YN"N"(!!B!a`!$!$8i"U!P+!iS"i!$J$,86rr0LZrq`N4"5!)#l +rr0#!d+lrp#"!-)C`!435FJ!5!Z0Jh%"54f#d3N"-haci6Pj1G8j@rr")jami*'i +!##CZ!!`k,J!3+'i!%N*!2!!b"A!!-!(3J$3'FJ!b!V#"Ea4#3$3'FJ!b!Y+"dS` +J36#!8NCJfN*!2!"`!Me!rr)f"VC&C!!!Z%*!2J"#3$e!rr"`!$!$jB$3LL"!)"! +Y32rd-JC`!$!"d)XJ3"J3GJ!@",C(B`!!JM!ZrrCb!F""d@lrm(!!-!06J$3(FJ! +b!V#"Ecii,[r`GJ!f"#e$rrc@JpD-)%0+8'B5)#lrr0#!d)`J3$#Zrr*8E[rb-Ll +rm(!!-!(3J0#-)%!`%$e!rr"J&M!&d%$34M3Zrr"b!$)#dS(5M#""-)"54b!Zrr6 +LL#e!rr4J!2pX8NCJ!2p%60mFq%jH6R919[rm51FF-#4Z!!Jf,J!-*Qi!$M)$F!! +`!HD!1!!`!h)(`%%k!(!!,8$rr$3%FJ!b!Y++)%%5%(!!%!%d"A)!-J,LS()"`)( +4V[rm)Llrr11*dSXJ36)3F!!`!5e!rra546!&FJL`3@B'3N!k!&*%-Li!%R!!-!( +3J,#ZrraM!Q#U%#lrra)Z!"25!C!!!8cI$$K1ANje6PErr%MR(b!NEJ!)1#i!$$S +Z!!ib"(!!-!(QJ$`!-J4d"m*#2J&f!$B!eSSJ3a!3G!!8!#e#rra`!$!"0!9b!$) +#d)(QJ()#X)&R$()"X)&R)%U!Cc4J-M3'FJ!b!P5"dSSJ34)3F!!3!A)3ikL"V[r +m0!Cb!$)#8S(5LL""%K"`!"!"iBL"V[rm)#lrr$3(FJ!b!Z+S,8$rr($rFL!f"A3 +!0!15JZ+S`'lrrNcI"2K1ANje6PErf%MR(cJQEJ!)+'i!$L!m!!!"*0'Z!")J2!! +!!NM4VJ!5)#i!%Le!rqab)01Z!")L,J!5,8(rm#3m!*!$J0@Z!")N,J!5,8,rp#4 +,'"*f!"B%,82rq1D$HJI'49*$282rh#BZrrMLJhS$aN983ce$rqCf!6SZrqEVBce +$rqKq3-J(I!!F"$e'rq*i!HYN8d3p42rJ+Llrq(i"bSGR#(S!1J46K@!#H[mp4Ir +HH!Jp42rN5NCR4LmZ!")[!$m$8NS[#NkkrcT2l`!1jd$4E[rN,bi!%LmZrr!r!bm +Zrqa1ZJX)6qm!$LmZrr3r!bmZrq`[,[r`6VVmV%r[!!j#3$e!rpJ`,[rBX'i!$'3 +!!6S`,[rLCcSN3$mZrqJ[,[rd2blrj#m,6VVpMNr[!!`5!#!+F!!3!6e!rpTd!$3 +!e+lrl#"#%""b!")!dflrj'!F2blrjMmZrq3[#dkkrI"36ce!rpS`,[rQd@lrj$! +ZrpU`E[rHCKBb,[rB8Qlrf(!!-!(3M#"!3K"J!2pk-#lrfV"Zrq"Q!!#8-#lriQF +k*%!r,[rS,blrp$mZrq3[#dkkr3C2l`!-%J!J#R!!%!%p32rDG!!d!05Zrq`J3K! +3FJ!5!00Zrq4J($mZrqBr,[rN,`Y1Z[eS8%mp32rD-#lrjY&Zrq4@E[rD-#lrfP0 +ZrpT+3'F!r`!i,[rBGJ!f"#e$rra6JpD-)%-3%#)Zrrc5M#""%)"5E[rBB-i3,[r +Gd#lrfc3ZrpK5E[rBFJ!b!Y+-)%%3J'!!rVib,[rNF!!`!9k!jS"-haci6Pj1G8j +@ria)jami*Qi!##SZ!!`SEJ!3,#i!&#e,rmK`*0I!,8[ri(!Jem!Y5rr-F%MA`#e +,rq3Y62q8*M`!!!%NeklrP#!m!!!#50'Zrj4`)0'Zrj3S2!#3!i$CV[q8fDlrP#e +Zrj6rY#Jm!!!%N!$CV[q8,@lrP2qieklrP#eZrj6r[0QZrj3YE[q8rp4`I0'Zrj3 +YE[q8rk3J2!!!J!$4V[q8)#lrP*!!M,#&B`T`C6e!!#K1qJCkF!!Z!%*!28$rM#4 +Zrk69r!!!J!!Y5[qS,@lrT2q3!#em!!#!!2rS5'lrk#mZrk3JEJ!N6T!!8%mJ,[r +SCJT`Cce!!#K1qJBd*'lrN!"55VAZrkKMD#"Zrj!!8NL4l[qS,8Mrp#"Zrj!!NHl +rT#e)rr!JE[qSNHlrN!!Y52rX)!KR$L"Zrj!!)QlrT#!ZrqbL,L4Zrk69l[rX,8V +rN!")E[r`,blrT#"Z!#41N!"36b!Zrr#`V[rdC!T`Cce!!#K1qJA!)'lrN!"5V[q +3!"!3(8$rS()!%J$5390"28(rd$!Zrp$33$e!rp)JEJ!F)"$3VJ!J,8$rX#4!,`` +[,[qi2c`"*#mZrj!!6VVlmNr[!!ib!#!+F!!`!G'Zrj!!,``[,[qd2c`"*#mZrlK +1ZJHk6qm!$LmZrl`r2!%N,blrZ#mZrl41Z[PF6qm!$L4!,``[,[qi2blrd#mZrj! +!6VVlS%r[!!ib!#!+F!!`!G'Zrj!!,``[,[qd2blrd#mZrlK1ZJGS6qm!$LmZrp3 +r,[r3,blrZ#mZrl41Z[N+6qm!$R!!,J"#3$e!ri`YEJ!Jrk`JE[qXXHlrX'3!"+K +#3$e!rjJ-EJ*)rjKN!!$#-#lrM'B!!)`NE[q3!&*+YHlrU'0S)'lrN!"55*(ZrkJ +Y52rd)'lrN!#4l[qN,8Mrm#"ZrkL4l[q3!#e)rq`J#'F1)'lrN!!LE[qN)#lrl+) +Z*'lrT0AZrq`Y5[q3!%KZrr![,[qN)'i!*%k3!&"2)#lrm,#Zrr4N#R"R28!!+%l +k"%)JE[q3!&+Zrj!!%K"`!"!",J"`#$e!ri``"h)"`%(4E[qB-LlrQ(!!-!(3J0# +Zrl`J3$!328$rQ#!(iSJZ!&0ZriaJ!2mi"'i#52qB$'i"!2qBC"!JE[qX8UlrV"# +ZrjPJ!2m)"'i"!2qB1#lrQ(B!0J3Y3rrieS2@V[r-)%-`%$e!rjSL,[ridUlrb#" +"%K"`!"!"28$rR%T!C`!!`JaZ!"MrM')!!*JNE[q3!&*+YHlrU'0S)'lrN!"55*( +ZrkJY52rd)'lrN!#4l[qN,8Mrm#"ZrkL4l[q3!#e)rq`J#'F1)'lrN!!LE[qN)#l +rl+)Z*'lrT0AZrq`Y5[q3!%KZrr![,[qN)'i!*%k3!&"2)#lrm,#Zrr4N#R"R28! +!+%lk!bJJE[q3!&+Zrj!!%K"`!"!"0#lrM()!-J,MU)k!8'lrM'!!rf*`rh)J1#l +rR(B!0J55Jq+S`%I4E[qD)!IQU#i!Q@lrM%*!28$rQ$!ZrjL`E[r5C!!!`M!Zria +Q!!#-*'lrN!"55VAZrkKMD#"Zrj!!8NL4l[qS,8Mrp#"Zrj!!NHlrT#e)rr!JE[q +SNHlrN!!Y52rX)!KR$L"Zrj!!)QlrT#!ZrqbL,L4Zrk69l[rX,8VrN!")E[r`,bl +rT#"Z!#41N!"36b!Zrr#`V[rdC!T`Cce!!#K1qJ*B)'lrN!"5V[q3!")3F!!3!5i +!F!Jp32q--!Gb!F""d@lrQ$)ZrjK`!$!"d)$3V[r8)%!`%$e!rjJJ"q+),J"6E[q +-B!$r0M!Zrp+4E[qB1#lrQ(B!0J3Y3rrmeS2@V[rN)%-`%$e!rjiL,[rmdUlri#" +"%K"`!"!"28$rR%T!C`!!`JaZ!"MrM')!!*JNE[q3!&*+YHlrU'0S)'lrN!"55*( +ZrkJY52rd)'lrN!#4l[qN,8Mrm#"ZrkL4l[q3!#e)rq`J#'F1)'lrN!!LE[qN)#l +rl+)Z*'lrT0AZrq`Y5[q3!%KZrr![,[qN)'i!*%k3!&"2)#lrm,#Zrr4N#R"R28! +!+%lk!9SJE[q3!&+Zrj!!%K"`!"!"0#lrM()!-J,MU)k!8'lrM'!!rf*`rh)J1#l +rR(B!0J55Jq+S`%I4E[qH)!IQU#i!Q@lrM#"Zrk`b,[qHF!!`!C(!,8MrP,(Z!#" +PB#"Zrj45V[q8%"!JE[qX8UlrV"#!)'lrP&+Zrj33%#"Zrka5V[qX%)!JE[q88Ul +rP"!3)'lrV&+Zrk`3J$!ZrjT6E[qD5N"R!2[H)'lrP&+Zrj33%#"Zrka5V[qX%)" +Jh&CZrjSJEJ!BdFBb,[qHF!!`!5*Zrkb6lJ!JN!#*NF!Y52q8-#lrQQFQ)'i!'0( +'XHlrP'-D)'lrP&+Zrj33%#"Zrka5V[qX%)"6E[qDB03YEJ!Jrj3`,[qD8flrQNT +!C`$lCL"Zrj45V[q8%"!JE[qX8UlrV"#!B0`JE[qXXHlrX'F)F'Fp3!!SB"3JE[q +XNHi!)#*Z!"`LL%*!28!!+%cI(2K1AL"I6qm!)%l3!(!m!$iJ!!"i)$i`)#BQ)(J +J2$dc-J!!1N0[EA"bCA0cD@pZ1N4PBfpYF(*PFh0TEfi`-c!a,Q-!!$`!2L!!!(J +J2M!J*LBJH#!m26-b!!!k3fpYF(*PFh0TEfik4'9MEfe`FQ9cFfP[EM!c-$%ZB`! +!6PErk%MR(cJq,J!)+'i!$$BZ!!T`!$!$1!Gb!$)%N!#"FJ'`J@m!!E3p42rS282 +rkP*ZrqJ`,[rSX'i!#Q3FFJ!b!0+-)%%3%$3(FJ!b!Y+-)%%5%,!"C!*JeP0ZrqS +`,[rUX%GM(()!-J$5M#""%"!d"h)!-J,5M#""%K#`!@-#B0J`,[rSX'lrkQ8#B() +i,[rSGJ!f"#e$rr$@M#4$%K*`!"!"28$rl$SZrqTi!$J&,86rp0L-*N33%a5!&Ul +rl5!Zrr$3J0#Z!"!J3$!328$rl#)Zrr65JG+Z!"!J36)3*#lrm05#e+i!%#"#-)% +L,[rddS(5VJ!3)%%`J'!!rc)`,[rUX%GQ"P*(B!$r"$J(GJ!f"#e$rrM@M#4$%K* +`!"!"28$rl$`ZrqTk!$S',8Arr0U-*N83%a5!&Ulrl5!ZrrM3J0#Z!"!J3$!328$ +rl#)Zrrc5JG+Z!"!J36)3*#lrq05#e+i!%#"#-)%L,[rmdS(5VJ!3)%%`J#!Zrr` +L,[riN!#"0#i!#R)!-J)N,[rm8S+5JV#"E"i[,J!3,``r"Mm%6VVqA%r[!!``,[r +U8N!q!'!!rP`[,J!3,``r,J!+-#lrkP*!2`"1Z[if6qm!$$eZrqS!#Q!!rMK-hac +i6Pj1G8j@rq4)jami*'i!#$SZ!!`QEJ!1+'i!%Le-rr!J2!!!!56C`#e-rr4#3$` +!0JDf4@3XH!!i!be%rrMBLL"%%"!L,[ridUlrm#""%)!J,[rid)$3V[rd)%!`Je* +'B-i[,[rd,blrm$m&3QG1Z[fb6qm!$%*!2!!f"VC&C"*`!$!$d+lrm#"!5K"Q"&* +'B1K`!#e!rq3f"VC&C!!!U%T$Cc)J,[rNH!!i!be%rrcBV[r`)%38%()!%J)Q,[r +m8i2@V[r`)%-@%(3!&!15JZ1S,8$rj$3'FJ!b!Y+Zrr!J34)3F!!3!6i!,@lrj2r +XF!!Y32rS-!G64dT!Cb!J,[rSiiJL,[rXG!(#JS#",8$rk#!ZrqcLL#e!rqaJf$3 +'FJ!b!Y+"dUlrp#""-K"`!$!"jB$3Lb"!)+lrk&*'8Ulrj'!!re4-haci6Pj1G5* +I)&qJ*5k!DJ*#Pdl4)Pm5(c!I5J&R"+G'B!+M4Lk)6Y%LAa)I-"mJAdS"C`5Q4f! +#SNG1d3#3!`S!1+!"!!8!N!B"!!!"MdN!!Bj*!!!%E&028P3&PJ#!!"`$dJ!838a +59!!+!+T"9A-b!!!",N*14%`!!3%k3dp%43!(!9*%394"!!!"XN4*9%`!$3'q4%a +24`!#!QC'8N9'!!3#LNCPBA3!!!,'5801)`!%!Y**3dp1!!!$$P"*3e3!!!-D8(0 +PG!!!!bC659T&!!!$-P088L!!!3-q8e45)`!!!eCKGA0d!!%$BQ0TBfi!!!0kD@0 +X1!!!!iCVD@jN!!!$NRCPFR-!!31H!)$rrb!!",-!N!@"rrmJ!!6$!*!&J[rr)!! +%F`#3"BArrb3!")--l8hS!)Errb3!"+--mjFi!)Irrb!!"*-!N!@)rrmJ!!66!*! +%!J$rrb!!"18!N!3#!Irr)!!%p3#3"!3"rrmJ!!4M!*!%"+rrr`!!&"`!N!G!!!! +Cc`#3"B$rr`!!'CF!N!3"!2rr!!!CG`#3"[rr+!&cE3#3"3%!AK`!(PB-mjFX!!) +!D"`!N!!I$21A*!!$!()F!+`d$21A"!!%!(`F!21L$21A4!!&!)BF!58[$1raK!! +'!*!!(!&)6JcaVlJ!"rrr!!&cb3#3"[rr+!"J'!#3"B$rr`!!!DF!N!@#rrm!!!* +h!*!&KIrr*!!!J!cY6NJ!K[rr*!!"*JcY6H3!Krrr!*!$eJ#3"BMrr`!!!`%!N!@ +errmJ!!)&!*!%!3F!0#!!%fi!N!3#!2rr!!!$f3#3"!)"rrm!!!3A!*!%!qMrrb! +!!Y8!N!3%!Irr)!#3"`4,!#J%!",J$21A3!5[rrm!!"-`!*!%!3F!(#!!%5-!N!3 +$k2rr)!!*@!#3"!4,!"!%!"%+$21A!!#!rrm!!"P!!*!&JIrr!!!C5`#3"B,rr`! +!'9B!N!@$rrm!!"PK!*!&K2rr!!!CE!#3"[rr!!'11`#3"B$rr`!!&#`!N!@"rrm +!!"8`!*!&J[rr!!!@0!#3"B2rr`!!&cJ!N!@%rrm!!"Jm!*!%"%[rr`3!%Q8-mjE +S!qMrr`!!#E3!N!@!rrm!!!8K!*!%rj!%!!&cL3#3"!)!N!-J!!8&!*!%!J%!"b! +!"48!N!@!rrm!!!Pd!*!(6`!!'IF!N!@%rrm!!"Rc!*!%"%[rr`3!%6m-l8hX!)6 +rr`!!'P)!N!@!rrm!!A1A!*!&!Irr)!!3q!#3"3,rrb!!%0S!N!3'F(*[EA"d#-3 +JFh9QCQPi#dPZFf9bG#"%DA0V#d9iDA0dD@jR)&"A#dPZFf9bG#"%DA0V#d9iDA0 +dD@jR)&"A$NphEQ9b)(*PFfpeFQ0P$NphEQ9b)(*PFfpeFQ0P#90PCfePER3J-3P +6C@GYC@jd)$)*8f9RE@9ZG#!c#90PCfePER3J03P6C@GYC@jd)$B*8f9RE@9ZG#! +fS33: diff --git a/mac/tclMacResource.c b/mac/tclMacResource.c new file mode 100644 index 0000000..3d228d2 --- /dev/null +++ b/mac/tclMacResource.c @@ -0,0 +1,2258 @@ +/* + * tclMacResource.c -- + * + * This file contains several commands that manipulate or use + * Macintosh resources. Included are extensions to the "source" + * command, the mac specific "beep" and "resource" commands, and + * administration for open resource file references. + * + * Copyright (c) 1996-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacResource.c,v 1.20 2003/12/24 04:18:21 davygrvy Exp $ + */ + +#include <Errors.h> +#include <FSpCompat.h> +#include <Processes.h> +#include <Resources.h> +#include <Sound.h> +#include <Strings.h> +#include <Traps.h> +#include <LowMem.h> + +#include "FullPath.h" +#include "tcl.h" +#include "tclInt.h" +#include "tclMac.h" +#include "tclMacInt.h" +#include "tclMacPort.h" + +/* + * This flag tells the RegisterResource function to insert the + * resource into the tail of the resource fork list. Needed only + * Resource_Init. + */ + +#define TCL_RESOURCE_INSERT_TAIL 1 +/* + * 2 is taken by TCL_RESOURCE_DONT_CLOSE + * which is the only public flag to TclMacRegisterResourceFork. + */ + +#define TCL_RESOURCE_CHECK_IF_OPEN 4 + +/* + * Pass this in the mode parameter of SetSoundVolume to determine + * which volume to set. + */ + +enum WhichVolume { + SYS_BEEP_VOLUME, /* This sets the volume for SysBeep calls */ + DEFAULT_SND_VOLUME, /* This one for SndPlay calls */ + RESET_VOLUME /* And this undoes the last call to SetSoundVolume */ +}; + +/* + * Hash table to track open resource files. + */ + +typedef struct OpenResourceFork { + short fileRef; + int flags; +} OpenResourceFork; + + + +static Tcl_HashTable nameTable; /* Id to process number mapping. */ +static Tcl_HashTable resourceTable; /* Process number to id mapping. */ +static Tcl_Obj *resourceForkList; /* Ordered list of resource forks */ +static int appResourceIndex; /* This is the index of the application* + * in the list of resource forks */ +static int newId = 0; /* Id source. */ +static int initialized = 0; /* 0 means static structures haven't + * been initialized yet. */ +static int osTypeInit = 0; /* 0 means Tcl object of osType hasn't + * been initialized yet. */ +/* + * Prototypes for procedures defined later in this file: + */ + +static void DupOSTypeInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr, + Tcl_Obj *copyPtr)); +static void ResourceInit _ANSI_ARGS_((void)); +static void BuildResourceForkList _ANSI_ARGS_((void)); +static int SetOSTypeFromAny _ANSI_ARGS_((Tcl_Interp *interp, + Tcl_Obj *objPtr)); +static void UpdateStringOfOSType _ANSI_ARGS_((Tcl_Obj *objPtr)); +static OpenResourceFork* GetRsrcRefFromObj _ANSI_ARGS_((Tcl_Obj *objPtr, + int okayOnReadOnly, const char *operation, + Tcl_Obj *resultPtr)); + +static void SetSoundVolume(int volume, enum WhichVolume mode); + +/* + * The structures below defines the Tcl object type defined in this file by + * means of procedures that can be invoked by generic object code. + */ + +static Tcl_ObjType osType = { + "ostype", /* name */ + (Tcl_FreeInternalRepProc *) NULL, /* freeIntRepProc */ + DupOSTypeInternalRep, /* dupIntRepProc */ + UpdateStringOfOSType, /* updateStringProc */ + SetOSTypeFromAny /* setFromAnyProc */ +}; + +/* + *---------------------------------------------------------------------- + * + * Tcl_ResourceObjCmd -- + * + * This procedure is invoked to process the "resource" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_ResourceObjCmd( + ClientData clientData, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[]) /* Argument values. */ +{ + Tcl_Obj *resultPtr, *objPtr; + int index, result; + long fileRef, rsrcId; + FSSpec fileSpec; + char *stringPtr; + char errbuf[16]; + OpenResourceFork *resourceRef; + Handle resource = NULL; + OSErr err; + int count, i, limitSearch = false, length; + short id, saveRef, resInfo; + Str255 theName; + OSType rezType; + int gotInt, releaseIt = 0, force; + char *resourceId = NULL; + long size; + char macPermision; + int mode; + + static CONST char *switches[] = {"close", "delete" ,"files", "list", + "open", "read", "types", "write", (char *) NULL + }; + + enum { + RESOURCE_CLOSE, RESOURCE_DELETE, RESOURCE_FILES, RESOURCE_LIST, + RESOURCE_OPEN, RESOURCE_READ, RESOURCE_TYPES, RESOURCE_WRITE + }; + + static CONST char *writeSwitches[] = { + "-id", "-name", "-file", "-force", (char *) NULL + }; + + enum { + RESOURCE_WRITE_ID, RESOURCE_WRITE_NAME, + RESOURCE_WRITE_FILE, RESOURCE_FORCE + }; + + static CONST char *deleteSwitches[] = {"-id", "-name", "-file", (char *) NULL}; + + enum {RESOURCE_DELETE_ID, RESOURCE_DELETE_NAME, RESOURCE_DELETE_FILE}; + + resultPtr = Tcl_GetObjResult(interp); + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[1], switches, "option", 0, &index) + != TCL_OK) { + return TCL_ERROR; + } + if (!initialized) { + ResourceInit(); + } + result = TCL_OK; + + switch (index) { + case RESOURCE_CLOSE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "resourceRef"); + return TCL_ERROR; + } + stringPtr = Tcl_GetStringFromObj(objv[2], &length); + fileRef = TclMacUnRegisterResourceFork(stringPtr, resultPtr); + + if (fileRef >= 0) { + CloseResFile((short) fileRef); + return TCL_OK; + } else { + return TCL_ERROR; + } + case RESOURCE_DELETE: + if (!((objc >= 3) && (objc <= 9) && ((objc % 2) == 1))) { + Tcl_WrongNumArgs(interp, 2, objv, + "?-id resourceId? ?-name resourceName? ?-file \ +resourceRef? resourceType"); + return TCL_ERROR; + } + + i = 2; + fileRef = -1; + gotInt = false; + resourceId = NULL; + limitSearch = false; + + while (i < (objc - 2)) { + if (Tcl_GetIndexFromObj(interp, objv[i], deleteSwitches, + "option", 0, &index) != TCL_OK) { + return TCL_ERROR; + } + + switch (index) { + case RESOURCE_DELETE_ID: + if (Tcl_GetLongFromObj(interp, objv[i+1], &rsrcId) + != TCL_OK) { + return TCL_ERROR; + } + gotInt = true; + break; + case RESOURCE_DELETE_NAME: + resourceId = Tcl_GetStringFromObj(objv[i+1], &length); + if (length > 255) { + Tcl_AppendStringsToObj(resultPtr,"-name argument ", + "too long, must be < 255 characters", + (char *) NULL); + return TCL_ERROR; + } + strcpy((char *) theName, resourceId); + resourceId = (char *) theName; + c2pstr(resourceId); + break; + case RESOURCE_DELETE_FILE: + resourceRef = GetRsrcRefFromObj(objv[i+1], 0, + "delete from", resultPtr); + if (resourceRef == NULL) { + return TCL_ERROR; + } + limitSearch = true; + break; + } + i += 2; + } + + if ((resourceId == NULL) && !gotInt) { + Tcl_AppendStringsToObj(resultPtr,"you must specify either ", + "\"-id\" or \"-name\" or both ", + "to \"resource delete\"", + (char *) NULL); + return TCL_ERROR; + } + + if (Tcl_GetOSTypeFromObj(interp, objv[i], &rezType) != TCL_OK) { + return TCL_ERROR; + } + + if (limitSearch) { + saveRef = CurResFile(); + UseResFile((short) resourceRef->fileRef); + } + + SetResLoad(false); + + if (gotInt == true) { + if (limitSearch) { + resource = Get1Resource(rezType, rsrcId); + } else { + resource = GetResource(rezType, rsrcId); + } + err = ResError(); + + if (err == resNotFound || resource == NULL) { + Tcl_AppendStringsToObj(resultPtr, "resource not found", + (char *) NULL); + result = TCL_ERROR; + goto deleteDone; + } else if (err != noErr) { + char buffer[16]; + + sprintf(buffer, "%12d", err); + Tcl_AppendStringsToObj(resultPtr, "resource error #", + buffer, "occured while trying to find resource", + (char *) NULL); + result = TCL_ERROR; + goto deleteDone; + } + } + + if (resourceId != NULL) { + Handle tmpResource; + if (limitSearch) { + tmpResource = Get1NamedResource(rezType, + (StringPtr) resourceId); + } else { + tmpResource = GetNamedResource(rezType, + (StringPtr) resourceId); + } + err = ResError(); + + if (err == resNotFound || tmpResource == NULL) { + Tcl_AppendStringsToObj(resultPtr, "resource not found", + (char *) NULL); + result = TCL_ERROR; + goto deleteDone; + } else if (err != noErr) { + char buffer[16]; + + sprintf(buffer, "%12d", err); + Tcl_AppendStringsToObj(resultPtr, "resource error #", + buffer, "occured while trying to find resource", + (char *) NULL); + result = TCL_ERROR; + goto deleteDone; + } + + if (gotInt) { + if (resource != tmpResource) { + Tcl_AppendStringsToObj(resultPtr, + "\"-id\" and \"-name\" ", + "values do not point to the same resource", + (char *) NULL); + result = TCL_ERROR; + goto deleteDone; + } + } else { + resource = tmpResource; + } + } + + resInfo = GetResAttrs(resource); + + if ((resInfo & resProtected) == resProtected) { + Tcl_AppendStringsToObj(resultPtr, "resource ", + "cannot be deleted: it is protected.", + (char *) NULL); + result = TCL_ERROR; + goto deleteDone; + } else if ((resInfo & resSysHeap) == resSysHeap) { + Tcl_AppendStringsToObj(resultPtr, "resource", + "cannot be deleted: it is in the system heap.", + (char *) NULL); + result = TCL_ERROR; + goto deleteDone; + } + + /* + * Find the resource file, if it was not specified, + * so we can flush the changes now. Perhaps this is + * a little paranoid, but better safe than sorry. + */ + + RemoveResource(resource); + + if (!limitSearch) { + UpdateResFile(HomeResFile(resource)); + } else { + UpdateResFile(resourceRef->fileRef); + } + + + deleteDone: + + SetResLoad(true); + if (limitSearch) { + UseResFile(saveRef); + } + return result; + + case RESOURCE_FILES: + if ((objc < 2) || (objc > 3)) { + Tcl_SetStringObj(resultPtr, + "wrong # args: should be \"resource files \ +?resourceId?\"", -1); + return TCL_ERROR; + } + + if (objc == 2) { + stringPtr = Tcl_GetStringFromObj(resourceForkList, &length); + Tcl_SetStringObj(resultPtr, stringPtr, length); + } else { + FCBPBRec fileRec; + Handle pathHandle; + short pathLength; + Str255 fileName; + Tcl_DString dstr; + + if (strcmp(Tcl_GetString(objv[2]), "ROM Map") == 0) { + Tcl_SetStringObj(resultPtr,"no file path for ROM Map", -1); + return TCL_ERROR; + } + + resourceRef = GetRsrcRefFromObj(objv[2], 1, "files", resultPtr); + if (resourceRef == NULL) { + return TCL_ERROR; + } + + fileRec.ioCompletion = NULL; + fileRec.ioFCBIndx = 0; + fileRec.ioNamePtr = fileName; + fileRec.ioVRefNum = 0; + fileRec.ioRefNum = resourceRef->fileRef; + err = PBGetFCBInfo(&fileRec, false); + if (err != noErr) { + Tcl_SetStringObj(resultPtr, + "could not get FCB for resource file", -1); + return TCL_ERROR; + } + + err = GetFullPath(fileRec.ioFCBVRefNum, fileRec.ioFCBParID, + fileRec.ioNamePtr, &pathLength, &pathHandle); + if ( err != noErr) { + Tcl_SetStringObj(resultPtr, + "could not get file path from token", -1); + return TCL_ERROR; + } + + HLock(pathHandle); + Tcl_ExternalToUtfDString(NULL, *pathHandle, pathLength, &dstr); + + Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&dstr), Tcl_DStringLength(&dstr)); + HUnlock(pathHandle); + DisposeHandle(pathHandle); + Tcl_DStringFree(&dstr); + } + return TCL_OK; + case RESOURCE_LIST: + if (!((objc == 3) || (objc == 4))) { + Tcl_WrongNumArgs(interp, 2, objv, "resourceType ?resourceRef?"); + return TCL_ERROR; + } + if (Tcl_GetOSTypeFromObj(interp, objv[2], &rezType) != TCL_OK) { + return TCL_ERROR; + } + + if (objc == 4) { + resourceRef = GetRsrcRefFromObj(objv[3], 1, + "list", resultPtr); + if (resourceRef == NULL) { + return TCL_ERROR; + } + + saveRef = CurResFile(); + UseResFile((short) resourceRef->fileRef); + limitSearch = true; + } + + Tcl_ResetResult(interp); + if (limitSearch) { + count = Count1Resources(rezType); + } else { + count = CountResources(rezType); + } + SetResLoad(false); + for (i = 1; i <= count; i++) { + if (limitSearch) { + resource = Get1IndResource(rezType, i); + } else { + resource = GetIndResource(rezType, i); + } + if (resource != NULL) { + GetResInfo(resource, &id, (ResType *) &rezType, theName); + if (theName[0] != 0) { + + objPtr = Tcl_NewStringObj((char *) theName + 1, + theName[0]); + } else { + objPtr = Tcl_NewIntObj(id); + } + /* + * If the Master Pointer of the returned handle is + * null, then resource was not in memory, and it is + * safe to release it. Otherwise, it is not. + */ + if (*resource == NULL) { + ReleaseResource(resource); + } + result = Tcl_ListObjAppendElement(interp, resultPtr, + objPtr); + if (result != TCL_OK) { + Tcl_DecrRefCount(objPtr); + break; + } + } + } + SetResLoad(true); + + if (limitSearch) { + UseResFile(saveRef); + } + + return TCL_OK; + case RESOURCE_OPEN: { + Tcl_DString ds, buffer; + CONST char *str, *native; + int length; + + if (!((objc == 3) || (objc == 4))) { + Tcl_WrongNumArgs(interp, 2, objv, "fileName ?permissions?"); + return TCL_ERROR; + } + str = Tcl_GetStringFromObj(objv[2], &length); + if (Tcl_TranslateFileName(interp, str, &buffer) == NULL) { + return TCL_ERROR; + } + native = Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&buffer), + Tcl_DStringLength(&buffer), &ds); + err = FSpLocationFromPath(Tcl_DStringLength(&ds), native, &fileSpec); + Tcl_DStringFree(&ds); + Tcl_DStringFree(&buffer); + + if (!((err == noErr) || (err == fnfErr))) { + Tcl_AppendStringsToObj(resultPtr, "invalid path", (char *) NULL); + return TCL_ERROR; + } + + /* + * Get permissions for the file. We really only understand + * read-only and shared-read-write. If no permissions are + * given we default to read only. + */ + + if (objc == 4) { + stringPtr = Tcl_GetStringFromObj(objv[3], &length); + mode = TclGetOpenMode(interp, stringPtr, &index); + if (mode == -1) { + /* TODO: TclGetOpenMode doesn't work with Obj commands. */ + return TCL_ERROR; + } + switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) { + case O_RDONLY: + macPermision = fsRdPerm; + break; + case O_WRONLY: + case O_RDWR: + macPermision = fsRdWrShPerm; + break; + default: + Tcl_Panic("Tcl_ResourceObjCmd: invalid mode value"); + break; + } + } else { + macPermision = fsRdPerm; + } + + /* + * Don't load in any of the resources in the file, this could + * cause problems if you open a file that has CODE resources... + */ + + SetResLoad(false); + fileRef = (long) FSpOpenResFileCompat(&fileSpec, macPermision); + SetResLoad(true); + + if (fileRef == -1) { + err = ResError(); + if (((err == fnfErr) || (err == eofErr)) && + (macPermision == fsRdWrShPerm)) { + /* + * No resource fork existed for this file. Since we are + * opening it for writing we will create the resource fork + * now. + */ + + HCreateResFile(fileSpec.vRefNum, fileSpec.parID, + fileSpec.name); + fileRef = (long) FSpOpenResFileCompat(&fileSpec, + macPermision); + if (fileRef == -1) { + goto openError; + } + } else if (err == fnfErr) { + Tcl_AppendStringsToObj(resultPtr, + "file does not exist", (char *) NULL); + return TCL_ERROR; + } else if (err == eofErr) { + Tcl_AppendStringsToObj(resultPtr, + "file does not contain resource fork", (char *) NULL); + return TCL_ERROR; + } else { + openError: + Tcl_AppendStringsToObj(resultPtr, + "error opening resource file", (char *) NULL); + return TCL_ERROR; + } + } + + /* + * The FspOpenResFile function does not set the ResFileAttrs. + * Even if you open the file read only, the mapReadOnly + * attribute is not set. This means we can't detect writes to a + * read only resource fork until the write fails, which is bogus. + * So set it here... + */ + + if (macPermision == fsRdPerm) { + SetResFileAttrs(fileRef, mapReadOnly); + } + + Tcl_SetStringObj(resultPtr, "", 0); + if (TclMacRegisterResourceFork(fileRef, resultPtr, + TCL_RESOURCE_CHECK_IF_OPEN) != TCL_OK) { + CloseResFile(fileRef); + return TCL_ERROR; + } + return TCL_OK; + } + case RESOURCE_READ: + if (!((objc == 4) || (objc == 5))) { + Tcl_WrongNumArgs(interp, 2, objv, + "resourceType resourceId ?resourceRef?"); + return TCL_ERROR; + } + + if (Tcl_GetOSTypeFromObj(interp, objv[2], &rezType) != TCL_OK) { + return TCL_ERROR; + } + + if (Tcl_GetLongFromObj((Tcl_Interp *) NULL, objv[3], &rsrcId) + != TCL_OK) { + resourceId = Tcl_GetStringFromObj(objv[3], &length); + } + + if (objc == 5) { + stringPtr = Tcl_GetStringFromObj(objv[4], &length); + } else { + stringPtr = NULL; + } + + resource = Tcl_MacFindResource(interp, rezType, resourceId, + rsrcId, stringPtr, &releaseIt); + + if (resource != NULL) { + size = GetResourceSizeOnDisk(resource); + Tcl_SetByteArrayObj(resultPtr, (unsigned char *) *resource, size); + + /* + * Don't release the resource unless WE loaded it... + */ + + if (releaseIt) { + ReleaseResource(resource); + } + return TCL_OK; + } else { + Tcl_AppendStringsToObj(resultPtr, "could not load resource", + (char *) NULL); + return TCL_ERROR; + } + case RESOURCE_TYPES: + if (!((objc == 2) || (objc == 3))) { + Tcl_WrongNumArgs(interp, 2, objv, "?resourceRef?"); + return TCL_ERROR; + } + + if (objc == 3) { + resourceRef = GetRsrcRefFromObj(objv[2], 1, + "get types of", resultPtr); + if (resourceRef == NULL) { + return TCL_ERROR; + } + + saveRef = CurResFile(); + UseResFile((short) resourceRef->fileRef); + limitSearch = true; + } + + if (limitSearch) { + count = Count1Types(); + } else { + count = CountTypes(); + } + for (i = 1; i <= count; i++) { + if (limitSearch) { + Get1IndType((ResType *) &rezType, i); + } else { + GetIndType((ResType *) &rezType, i); + } + objPtr = Tcl_NewOSTypeObj(rezType); + result = Tcl_ListObjAppendElement(interp, resultPtr, objPtr); + if (result != TCL_OK) { + Tcl_DecrRefCount(objPtr); + break; + } + } + + if (limitSearch) { + UseResFile(saveRef); + } + + return result; + case RESOURCE_WRITE: + if ((objc < 4) || (objc > 11)) { + Tcl_WrongNumArgs(interp, 2, objv, + "?-id resourceId? ?-name resourceName? ?-file resourceRef?\ + ?-force? resourceType data"); + return TCL_ERROR; + } + + i = 2; + gotInt = false; + resourceId = NULL; + limitSearch = false; + force = 0; + + while (i < (objc - 2)) { + if (Tcl_GetIndexFromObj(interp, objv[i], writeSwitches, + "switch", 0, &index) != TCL_OK) { + return TCL_ERROR; + } + + switch (index) { + case RESOURCE_WRITE_ID: + if (Tcl_GetLongFromObj(interp, objv[i+1], &rsrcId) + != TCL_OK) { + return TCL_ERROR; + } + gotInt = true; + i += 2; + break; + case RESOURCE_WRITE_NAME: + resourceId = Tcl_GetStringFromObj(objv[i+1], &length); + strcpy((char *) theName, resourceId); + resourceId = (char *) theName; + c2pstr(resourceId); + i += 2; + break; + case RESOURCE_WRITE_FILE: + resourceRef = GetRsrcRefFromObj(objv[i+1], 0, + "write to", resultPtr); + if (resourceRef == NULL) { + return TCL_ERROR; + } + limitSearch = true; + i += 2; + break; + case RESOURCE_FORCE: + force = 1; + i += 1; + break; + } + } + if (Tcl_GetOSTypeFromObj(interp, objv[i], &rezType) != TCL_OK) { + return TCL_ERROR; + } + stringPtr = (char *) Tcl_GetByteArrayFromObj(objv[i+1], &length); + + if (gotInt == false) { + rsrcId = UniqueID(rezType); + } + if (resourceId == NULL) { + resourceId = (char *) "\p"; + } + if (limitSearch) { + saveRef = CurResFile(); + UseResFile((short) resourceRef->fileRef); + } + + /* + * If we are adding the resource by number, then we must make sure + * there is not already a resource of that number. We are not going + * load it here, since we want to detect whether we loaded it or + * not. Remember that releasing some resources in particular menu + * related ones, can be fatal. + */ + + if (gotInt == true) { + SetResLoad(false); + resource = Get1Resource(rezType,rsrcId); + SetResLoad(true); + } + + if (resource == NULL) { + /* + * We get into this branch either if there was not already a + * resource of this type & id, or the id was not specified. + */ + + resource = NewHandle(length); + if (resource == NULL) { + resource = NewHandleSys(length); + if (resource == NULL) { + Tcl_Panic("could not allocate memory to write resource"); + } + } + HLock(resource); + memcpy(*resource, stringPtr, length); + HUnlock(resource); + AddResource(resource, rezType, (short) rsrcId, + (StringPtr) resourceId); + releaseIt = 1; + } else { + /* + * We got here because there was a resource of this type + * & ID in the file. + */ + + if (*resource == NULL) { + releaseIt = 1; + } else { + releaseIt = 0; + } + + if (!force) { + /* + *We only overwrite extant resources + * when the -force flag has been set. + */ + + sprintf(errbuf,"%d", rsrcId); + + Tcl_AppendStringsToObj(resultPtr, "the resource ", + errbuf, " already exists, use \"-force\"", + " to overwrite it.", (char *) NULL); + + result = TCL_ERROR; + goto writeDone; + } else if (GetResAttrs(resource) & resProtected) { + /* + * + * Next, check to see if it is protected... + */ + + sprintf(errbuf,"%d", rsrcId); + Tcl_AppendStringsToObj(resultPtr, + "could not write resource id ", + errbuf, " of type ", + Tcl_GetStringFromObj(objv[i],&length), + ", it was protected.",(char *) NULL); + result = TCL_ERROR; + goto writeDone; + } else { + /* + * Be careful, the resource might already be in memory + * if something else loaded it. + */ + + if (*resource == 0) { + LoadResource(resource); + err = ResError(); + if (err != noErr) { + sprintf(errbuf,"%d", rsrcId); + Tcl_AppendStringsToObj(resultPtr, + "error loading resource ", + errbuf, " of type ", + Tcl_GetStringFromObj(objv[i],&length), + " to overwrite it", (char *) NULL); + goto writeDone; + } + } + + SetHandleSize(resource, length); + if ( MemError() != noErr ) { + Tcl_Panic("could not allocate memory to write resource"); + } + + HLock(resource); + memcpy(*resource, stringPtr, length); + HUnlock(resource); + + ChangedResource(resource); + + /* + * We also may have changed the name... + */ + + SetResInfo(resource, rsrcId, (StringPtr) resourceId); + } + } + + err = ResError(); + if (err != noErr) { + Tcl_AppendStringsToObj(resultPtr, + "error adding resource to resource map", + (char *) NULL); + result = TCL_ERROR; + goto writeDone; + } + + WriteResource(resource); + err = ResError(); + if (err != noErr) { + Tcl_AppendStringsToObj(resultPtr, + "error writing resource to disk", + (char *) NULL); + result = TCL_ERROR; + } + + writeDone: + + if (releaseIt) { + ReleaseResource(resource); + err = ResError(); + if (err != noErr) { + Tcl_AppendStringsToObj(resultPtr, + "error releasing resource", + (char *) NULL); + result = TCL_ERROR; + } + } + + if (limitSearch) { + UseResFile(saveRef); + } + + return result; + default: + Tcl_Panic("Tcl_GetIndexFromObj returned unrecognized option"); + return TCL_ERROR; /* Should never be reached. */ + } +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_MacSourceObjCmd -- + * + * This procedure is invoked to process the "source" Tcl command. + * See the user documentation for details on what it does. In + * addition, it supports sourceing from the resource fork of + * type 'TEXT'. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_MacSourceObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[]) /* Argument objects. */ +{ + char *errNum = "wrong # args: "; + char *errBad = "bad argument: "; + char *errStr; + char *fileName = NULL, *rsrcName = NULL; + long rsrcID = -1; + char *string; + char *encodingName = NULL; + int length; + + if (objc < 2 || objc > 4) { + errStr = errNum; + goto sourceFmtErr; + } + + if (objc == 2) { + return Tcl_FSEvalFile(interp, objv[1]); + } + + /* + * The following code supports a few older forms of this command + * for backward compatability. + */ + string = Tcl_GetStringFromObj(objv[1], &length); + if (!strcmp(string, "-rsrc") || !strcmp(string, "-rsrcname")) { + rsrcName = Tcl_GetStringFromObj(objv[2], &length); + } else if (!strcmp(string, "-rsrcid")) { + if (Tcl_GetLongFromObj(interp, objv[2], &rsrcID) != TCL_OK) { + return TCL_ERROR; + } + } else if (!strcmp(string, "-encoding")) { + if (objc != 4) + goto sourceFmtErr; + encodingName = Tcl_GetString(objv[2]); + } else { + errStr = errBad; + goto sourceFmtErr; + } + + if (objc == 4) { + fileName = Tcl_GetStringFromObj(objv[3], &length); + } + + if (encodingName) { + return Tcl_FSEvalFileEx(interp, fileName, encodingName); + } + + return Tcl_MacEvalResource(interp, rsrcName, rsrcID, fileName); + + sourceFmtErr: + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), errStr, "should be \"", + Tcl_GetString(objv[0]), " fileName\" or \"", + Tcl_GetString(objv[0]), " -rsrc name ?fileName?\" or \"", + Tcl_GetString(objv[0]), " -rsrcid id ?fileName?\" or \"", + Tcl_GetString(objv[0]), " -encoding name fileName\"", + (char *) NULL); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_BeepObjCmd -- + * + * This procedure makes the beep sound. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Makes a beep. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_BeepObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[]) /* Argument values. */ +{ + Tcl_Obj *resultPtr, *objPtr; + Handle sound; + Str255 sndName; + int volume = -1, length; + char * sndArg = NULL; + + resultPtr = Tcl_GetObjResult(interp); + if (objc == 1) { + SysBeep(1); + return TCL_OK; + } else if (objc == 2) { + if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-list")) { + int count, i; + short id; + Str255 theName; + ResType rezType; + + count = CountResources('snd '); + for (i = 1; i <= count; i++) { + sound = GetIndResource('snd ', i); + if (sound != NULL) { + GetResInfo(sound, &id, &rezType, theName); + if (theName[0] == 0) { + continue; + } + objPtr = Tcl_NewStringObj((char *) theName + 1, + theName[0]); + Tcl_ListObjAppendElement(interp, resultPtr, objPtr); + } + } + return TCL_OK; + } else { + sndArg = Tcl_GetStringFromObj(objv[1], &length); + } + } else if (objc == 3) { + if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-volume")) { + Tcl_GetIntFromObj(interp, objv[2], &volume); + } else { + goto beepUsage; + } + } else if (objc == 4) { + if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-volume")) { + Tcl_GetIntFromObj(interp, objv[2], &volume); + sndArg = Tcl_GetStringFromObj(objv[3], &length); + } else { + goto beepUsage; + } + } else { + goto beepUsage; + } + + /* + * Play the sound + */ + if (sndArg == NULL) { + /* + * Set Volume for SysBeep + */ + + if (volume >= 0) { + SetSoundVolume(volume, SYS_BEEP_VOLUME); + } + SysBeep(1); + + /* + * Reset Volume + */ + + if (volume >= 0) { + SetSoundVolume(0, RESET_VOLUME); + } + } else { + strcpy((char *) sndName + 1, sndArg); + sndName[0] = length; + sound = GetNamedResource('snd ', sndName); + if (sound != NULL) { + /* + * Set Volume for Default Output device + */ + + if (volume >= 0) { + SetSoundVolume(volume, DEFAULT_SND_VOLUME); + } + + SndPlay(NULL, (SndListHandle) sound, false); + + /* + * Reset Volume + */ + + if (volume >= 0) { + SetSoundVolume(0, RESET_VOLUME); + } + } else { + Tcl_AppendStringsToObj(resultPtr, " \"", sndArg, + "\" is not a valid sound. (Try ", + Tcl_GetString(objv[0]), " -list)", NULL); + return TCL_ERROR; + } + } + + return TCL_OK; + + beepUsage: + Tcl_WrongNumArgs(interp, 1, objv, "[-volume num] [-list | sndName]?"); + return TCL_ERROR; +} + +/* + *----------------------------------------------------------------------------- + * + * SetSoundVolume -- + * + * Set the volume for either the SysBeep or the SndPlay call depending + * on the value of mode (SYS_BEEP_VOLUME or DEFAULT_SND_VOLUME + * respectively. + * + * It also stores the last channel set, and the old value of its + * VOLUME. If you call SetSoundVolume with a mode of RESET_VOLUME, + * it will undo the last setting. The volume parameter is + * ignored in this case. + * + * Side Effects: + * Sets the System Volume + * + * Results: + * None + * + *----------------------------------------------------------------------------- + */ + +void +SetSoundVolume( + int volume, /* This is the new volume */ + enum WhichVolume mode) /* This flag says which volume to + * set: SysBeep, SndPlay, or instructs us + * to reset the volume */ +{ + static int hasSM3 = -1; + static enum WhichVolume oldMode; + static long oldVolume = -1; + + /* + * The volume setting calls only work if we have SoundManager + * 3.0 or higher. So we check that here. + */ + + if (hasSM3 == -1) { + if (GetToolboxTrapAddress(_SoundDispatch) + != GetToolboxTrapAddress(_Unimplemented)) { + NumVersion SMVers = SndSoundManagerVersion(); + if (SMVers.majorRev > 2) { + hasSM3 = 1; + } else { + hasSM3 = 0; + } + } else { + /* + * If the SoundDispatch trap is not present, then + * we don't have the SoundManager at all. + */ + + hasSM3 = 0; + } + } + + /* + * If we don't have Sound Manager 3.0, we can't set the sound volume. + * We will just ignore the request rather than raising an error. + */ + + if (!hasSM3) { + return; + } + + switch (mode) { + case SYS_BEEP_VOLUME: + GetSysBeepVolume(&oldVolume); + SetSysBeepVolume(volume); + oldMode = SYS_BEEP_VOLUME; + break; + case DEFAULT_SND_VOLUME: + GetDefaultOutputVolume(&oldVolume); + SetDefaultOutputVolume(volume); + oldMode = DEFAULT_SND_VOLUME; + break; + case RESET_VOLUME: + /* + * If oldVolume is -1 someone has made a programming error + * and called reset before setting the volume. This is benign + * however, so we will just exit. + */ + + if (oldVolume != -1) { + if (oldMode == SYS_BEEP_VOLUME) { + SetSysBeepVolume(oldVolume); + } else if (oldMode == DEFAULT_SND_VOLUME) { + SetDefaultOutputVolume(oldVolume); + } + } + oldVolume = -1; + } +} + +/* + *----------------------------------------------------------------------------- + * + * Tcl_MacEvalResource -- + * + * Used to extend the source command. Sources Tcl code from a Text + * resource. Currently only sources the resouce by name file ID may be + * supported at a later date. + * + * Side Effects: + * Depends on the Tcl code in the resource. + * + * Results: + * Returns a Tcl result. + * + *----------------------------------------------------------------------------- + */ + +int +Tcl_MacEvalResource( + Tcl_Interp *interp, /* Interpreter in which to process file. */ + CONST char *resourceName, /* Name of TEXT resource to source, + NULL if number should be used. */ + int resourceNumber, /* Resource id of source. */ + CONST char *fileName) /* Name of file to process. + NULL if application resource. */ +{ + Handle sourceText; + Str255 rezName; + int result, iOpenedResFile = false; + short saveRef, fileRef = -1; + char idStr[64]; + FSSpec fileSpec; + Tcl_DString ds, buffer; + CONST char *nativeName; + + saveRef = CurResFile(); + + if (fileName != NULL) { + OSErr err; + + if (Tcl_TranslateFileName(interp, fileName, &buffer) == NULL) { + return TCL_ERROR; + } + nativeName = Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&buffer), + Tcl_DStringLength(&buffer), &ds); + err = FSpLocationFromPath(strlen(nativeName), nativeName, + &fileSpec); + Tcl_DStringFree(&ds); + Tcl_DStringFree(&buffer); + if (err != noErr) { + Tcl_AppendResult(interp, "Error finding the file: \"", + fileName, "\".", NULL); + return TCL_ERROR; + } + + fileRef = FSpOpenResFileCompat(&fileSpec, fsRdPerm); + if (fileRef == -1) { + Tcl_AppendResult(interp, "Error reading the file: \"", + fileName, "\".", NULL); + return TCL_ERROR; + } + + UseResFile(fileRef); + iOpenedResFile = true; + } else { + /* + * The default behavior will search through all open resource files. + * This may not be the behavior you desire. If you want the behavior + * of this call to *only* search the application resource fork, you + * must call UseResFile at this point to set it to the application + * file. This means you must have already obtained the application's + * fileRef when the application started up. + */ + } + + /* + * Load the resource by name or ID + */ + if (resourceName != NULL) { + Tcl_DString ds; + Tcl_UtfToExternalDString(NULL, resourceName, -1, &ds); + strcpy((char *) rezName + 1, Tcl_DStringValue(&ds)); + rezName[0] = (unsigned) Tcl_DStringLength(&ds); + sourceText = GetNamedResource('TEXT', rezName); + Tcl_DStringFree(&ds); + } else { + sourceText = GetResource('TEXT', (short) resourceNumber); + } + + if (sourceText == NULL) { + result = TCL_ERROR; + } else { + char *sourceStr = NULL; + + HLock(sourceText); + sourceStr = Tcl_MacConvertTextResource(sourceText); + HUnlock(sourceText); + ReleaseResource(sourceText); + + /* + * We now evaluate the Tcl source + */ + result = Tcl_Eval(interp, sourceStr); + ckfree(sourceStr); + if (result == TCL_RETURN) { + result = TCL_OK; + } else if (result == TCL_ERROR) { + Tcl_Obj *errorLine = Tcl_NewIntObj(interp->errorLine); + Tcl_Obj *msg = Tcl_NewStringObj("\n (rsrc \"", -1); + Tcl_IncrRefCount(errorLine); + Tcl_IncrRefCount(msg); + TclAppendLimitedToObj(msg, resourceName, -1, 150, ""); + Tcl_AppendToObj(msg, "\" line ", -1); + Tcl_AppendObjToObj(msg, errorLine); + Tcl_DecrRefCount(errorLine); + Tcl_AppendToObj(msg, ")", -1); + TclAppendObjToErrorInfo(interp, msg); + Tcl_DecrRefCount(msg); + } + + goto rezEvalCleanUp; + } + + rezEvalError: + sprintf(idStr, "ID=%d", resourceNumber); + Tcl_AppendResult(interp, "The resource \"", + (resourceName != NULL ? resourceName : idStr), + "\" could not be loaded from ", + (fileName != NULL ? fileName : "application"), + ".", NULL); + + rezEvalCleanUp: + + /* + * TRICKY POINT: The code that you are sourcing here could load a + * shared library. This will go AHEAD of the resource we stored away + * in saveRef on the resource path. + * If you restore the saveRef in this case, you will never be able + * to get to the resources in the shared library, since you are now + * pointing too far down on the resource list. + * So, we only reset the current resource file if WE opened a resource + * explicitly, and then only if the CurResFile is still the + * one we opened... + */ + + if (iOpenedResFile && (CurResFile() == fileRef)) { + UseResFile(saveRef); + } + + if (fileRef != -1) { + CloseResFile(fileRef); + } + + return result; +} + +/* + *----------------------------------------------------------------------------- + * + * Tcl_MacConvertTextResource -- + * + * Converts a TEXT resource into a Tcl suitable string. + * + * Side Effects: + * Mallocs the returned memory, converts '\r' to '\n', and appends a NULL. + * + * Results: + * A new malloced string. + * + *----------------------------------------------------------------------------- + */ + +char * +Tcl_MacConvertTextResource( + Handle resource) /* Handle to TEXT resource. */ +{ + int i, size; + char *resultStr; + Tcl_DString dstr; + + size = GetResourceSizeOnDisk(resource); + + Tcl_ExternalToUtfDString(NULL, *resource, size, &dstr); + + size = Tcl_DStringLength(&dstr) + 1; + resultStr = (char *) ckalloc((unsigned) size); + + memcpy((VOID *) resultStr, (VOID *) Tcl_DStringValue(&dstr), (size_t) size); + + Tcl_DStringFree(&dstr); + + for (i=0; i<size; i++) { + if (resultStr[i] == '\r') { + resultStr[i] = '\n'; + } + } + + return resultStr; +} + +/* + *----------------------------------------------------------------------------- + * + * Tcl_MacFindResource -- + * + * Higher level interface for loading resources. + * + * Side Effects: + * Attempts to load a resource. + * + * Results: + * A handle on success. + * + *----------------------------------------------------------------------------- + */ + +Handle +Tcl_MacFindResource( + Tcl_Interp *interp, /* Interpreter in which to process file. */ + long resourceType, /* Type of resource to load. */ + CONST char *resourceName, /* Name of resource to find, + * NULL if number should be used. */ + int resourceNumber, /* Resource id of source. */ + CONST char *resFileRef, /* Registered resource file reference, + * NULL if searching all open resource files. */ + int *releaseIt) /* Should we release this resource when done. */ +{ + Tcl_HashEntry *nameHashPtr; + OpenResourceFork *resourceRef; + int limitSearch = false; + short saveRef; + Handle resource; + + if (resFileRef != NULL) { + nameHashPtr = Tcl_FindHashEntry(&nameTable, resFileRef); + if (nameHashPtr == NULL) { + Tcl_AppendResult(interp, "invalid resource file reference \"", + resFileRef, "\"", (char *) NULL); + return NULL; + } + resourceRef = (OpenResourceFork *) Tcl_GetHashValue(nameHashPtr); + saveRef = CurResFile(); + UseResFile((short) resourceRef->fileRef); + limitSearch = true; + } + + /* + * Some system resources (for example system resources) should not + * be released. So we set autoload to false, and try to get the resource. + * If the Master Pointer of the returned handle is null, then resource was + * not in memory, and it is safe to release it. Otherwise, it is not. + */ + + SetResLoad(false); + + if (resourceName == NULL) { + if (limitSearch) { + resource = Get1Resource(resourceType, resourceNumber); + } else { + resource = GetResource(resourceType, resourceNumber); + } + } else { + Str255 rezName; + Tcl_DString ds; + Tcl_UtfToExternalDString(NULL, resourceName, -1, &ds); + strcpy((char *) rezName + 1, Tcl_DStringValue(&ds)); + rezName[0] = (unsigned) Tcl_DStringLength(&ds); + if (limitSearch) { + resource = Get1NamedResource(resourceType, + rezName); + } else { + resource = GetNamedResource(resourceType, + rezName); + } + Tcl_DStringFree(&ds); + } + + if (resource != NULL && *resource == NULL) { + *releaseIt = 1; + LoadResource(resource); + } else { + *releaseIt = 0; + } + + SetResLoad(true); + + + if (limitSearch) { + UseResFile(saveRef); + } + + return resource; +} + +/* + *---------------------------------------------------------------------- + * + * ResourceInit -- + * + * Initialize the structures used for resource management. + * + * Results: + * None. + * + * Side effects: + * Read the code. + * + *---------------------------------------------------------------------- + */ + +static void +ResourceInit() +{ + + initialized = 1; + Tcl_InitHashTable(&nameTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&resourceTable, TCL_ONE_WORD_KEYS); + resourceForkList = Tcl_NewObj(); + Tcl_IncrRefCount(resourceForkList); + + BuildResourceForkList(); + +} +/***/ + +/*Tcl_RegisterObjType(typePtr) */ + +/* + *---------------------------------------------------------------------- + * + * Tcl_NewOSTypeObj -- + * + * This procedure is used to create a new resource name type object. + * + * Results: + * The newly created object is returned. This object will have a NULL + * string representation. The returned object has ref count 0. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Obj * +Tcl_NewOSTypeObj( + OSType newOSType) /* Int used to initialize the new object. */ +{ + register Tcl_Obj *objPtr; + + if (!osTypeInit) { + osTypeInit = 1; + Tcl_RegisterObjType(&osType); + } + + objPtr = Tcl_NewObj(); + objPtr->bytes = NULL; + objPtr->internalRep.longValue = newOSType; + objPtr->typePtr = &osType; + return objPtr; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_SetOSTypeObj -- + * + * Modify an object to be a resource type and to have the + * specified long value. + * + * Results: + * None. + * + * Side effects: + * The object's old string rep, if any, is freed. Also, any old + * internal rep is freed. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_SetOSTypeObj( + Tcl_Obj *objPtr, /* Object whose internal rep to init. */ + OSType newOSType) /* Integer used to set object's value. */ +{ + register Tcl_ObjType *oldTypePtr = objPtr->typePtr; + + if (!osTypeInit) { + osTypeInit = 1; + Tcl_RegisterObjType(&osType); + } + + if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) { + oldTypePtr->freeIntRepProc(objPtr); + } + + objPtr->internalRep.longValue = newOSType; + objPtr->typePtr = &osType; + + Tcl_InvalidateStringRep(objPtr); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_GetOSTypeFromObj -- + * + * Attempt to return an int from the Tcl object "objPtr". If the object + * is not already an int, an attempt will be made to convert it to one. + * + * Results: + * The return value is a standard Tcl object result. If an error occurs + * during conversion, an error message is left in interp->objResult + * unless "interp" is NULL. + * + * Side effects: + * If the object is not already an int, the conversion will free + * any old internal representation. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_GetOSTypeFromObj( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + Tcl_Obj *objPtr, /* The object from which to get a int. */ + OSType *osTypePtr) /* Place to store resulting int. */ +{ + register int result; + + if (!osTypeInit) { + osTypeInit = 1; + Tcl_RegisterObjType(&osType); + } + + if (objPtr->typePtr == &osType) { + *osTypePtr = objPtr->internalRep.longValue; + return TCL_OK; + } + + result = SetOSTypeFromAny(interp, objPtr); + if (result == TCL_OK) { + *osTypePtr = objPtr->internalRep.longValue; + } + return result; +} + +/* + *---------------------------------------------------------------------- + * + * DupOSTypeInternalRep -- + * + * Initialize the internal representation of an int Tcl_Obj to a + * copy of the internal representation of an existing int object. + * + * Results: + * None. + * + * Side effects: + * "copyPtr"s internal rep is set to the integer corresponding to + * "srcPtr"s internal rep. + * + *---------------------------------------------------------------------- + */ + +static void +DupOSTypeInternalRep( + Tcl_Obj *srcPtr, /* Object with internal rep to copy. */ + Tcl_Obj *copyPtr) /* Object with internal rep to set. */ +{ + copyPtr->internalRep.longValue = srcPtr->internalRep.longValue; + copyPtr->typePtr = &osType; +} + +/* + *---------------------------------------------------------------------- + * + * SetOSTypeFromAny -- + * + * Attempt to generate an integer internal form for the Tcl object + * "objPtr". + * + * Results: + * The return value is a standard object Tcl result. If an error occurs + * during conversion, an error message is left in interp->objResult + * unless "interp" is NULL. + * + * Side effects: + * If no error occurs, an int is stored as "objPtr"s internal + * representation. + * + *---------------------------------------------------------------------- + */ + +static int +SetOSTypeFromAny( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + Tcl_Obj *objPtr) /* The object to convert. */ +{ + Tcl_ObjType *oldTypePtr = objPtr->typePtr; + char *string; + int length; + OSType newOSType = 0UL; + Tcl_DString ds; + + /* + * Get the string representation. Make it up-to-date if necessary. + */ + + string = Tcl_GetStringFromObj(objPtr, &length); + Tcl_UtfToExternalDString(NULL, string, length, &ds); + + if (Tcl_DStringLength(&ds) > sizeof(OSType)) { + if (interp != NULL) { + Tcl_ResetResult(interp); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "expected Macintosh OS type but got \"", string, "\"", + (char *) NULL); + } + Tcl_DStringFree(&ds); + return TCL_ERROR; + } + memcpy(&newOSType, Tcl_DStringValue(&ds), + (size_t) Tcl_DStringLength(&ds)); + Tcl_DStringFree(&ds); + + /* + * The conversion to resource type succeeded. Free the old internalRep + * before setting the new one. + */ + + if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) { + oldTypePtr->freeIntRepProc(objPtr); + } + + objPtr->internalRep.longValue = newOSType; + objPtr->typePtr = &osType; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * UpdateStringOfOSType -- + * + * Update the string representation for an resource type object. + * Note: This procedure does not free an existing old string rep + * so storage will be lost if this has not already been done. + * + * Results: + * None. + * + * Side effects: + * The object's string is set to a valid string that results from + * the int-to-string conversion. + * + *---------------------------------------------------------------------- + */ + +static void +UpdateStringOfOSType( + register Tcl_Obj *objPtr) /* Int object whose string rep to update. */ +{ + char string[sizeof(OSType)+1]; + Tcl_DString ds; + + memcpy(string, &(objPtr->internalRep.longValue), sizeof(OSType)); + string[sizeof(OSType)] = '\0'; + Tcl_ExternalToUtfDString(NULL, string, -1, &ds); + objPtr->bytes = ckalloc(Tcl_DStringLength(&ds) + 1); + memcpy(objPtr->bytes, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds) + 1); + objPtr->length = Tcl_DStringLength(&ds); + Tcl_DStringFree(&ds); +} + +/* + *---------------------------------------------------------------------- + * + * GetRsrcRefFromObj -- + * + * Given a String object containing a resource file token, return + * the OpenResourceFork structure that it represents, or NULL if + * the token cannot be found. If okayOnReadOnly is false, it will + * also check whether the token corresponds to a read-only file, + * and return NULL if it is. + * + * Results: + * A pointer to an OpenResourceFork structure, or NULL. + * + * Side effects: + * An error message may be left in resultPtr. + * + *---------------------------------------------------------------------- + */ + +static OpenResourceFork * +GetRsrcRefFromObj( + register Tcl_Obj *objPtr, /* String obj containing file token */ + int okayOnReadOnly, /* Whether this operation is okay for a * + * read only file. */ + const char *operation, /* String containing the operation we * + * were trying to perform, used for errors */ + Tcl_Obj *resultPtr) /* Tcl_Obj to contain error message */ +{ + char *stringPtr; + Tcl_HashEntry *nameHashPtr; + OpenResourceFork *resourceRef; + int length; + OSErr err; + + stringPtr = Tcl_GetStringFromObj(objPtr, &length); + nameHashPtr = Tcl_FindHashEntry(&nameTable, stringPtr); + if (nameHashPtr == NULL) { + Tcl_AppendStringsToObj(resultPtr, + "invalid resource file reference \"", + stringPtr, "\"", (char *) NULL); + return NULL; + } + + resourceRef = (OpenResourceFork *) Tcl_GetHashValue(nameHashPtr); + + if (!okayOnReadOnly) { + err = GetResFileAttrs((short) resourceRef->fileRef); + if (err & mapReadOnly) { + Tcl_AppendStringsToObj(resultPtr, "cannot ", operation, + " resource file \"", + stringPtr, "\", it was opened read only", + (char *) NULL); + return NULL; + } + } + return resourceRef; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacRegisterResourceFork -- + * + * Register an open resource fork in the table of open resources + * managed by the procedures in this file. If the resource file + * is already registered with the table, then no new token is made. + * + * The behavior is controlled by the value of tokenPtr, and of the + * flags variable. For tokenPtr, the possibilities are: + * - NULL: The new token is auto-generated, but not returned. + * - The string value of tokenPtr is the empty string: Then + * the new token is auto-generated, and returned in tokenPtr + * - tokenPtr has a value: The string value will be used for the token, + * unless it is already in use, in which case a new token will + * be generated, and returned in tokenPtr. + * + * For the flags variable: it can be one of: + * - TCL_RESOURCE__INSERT_TAIL: The element is inserted at the + * end of the list of open resources. Used only in Resource_Init. + * - TCL_RESOURCE_DONT_CLOSE: The resource close command will not close + * this resource. + * - TCL_RESOURCE_CHECK_IF_OPEN: This will check to see if this file's + * resource fork is already opened by this Tcl shell, and return + * an error without registering the resource fork. + * + * Results: + * Standard Tcl Result + * + * Side effects: + * An entry may be added to the resource name table. + * + *---------------------------------------------------------------------- + */ + +int +TclMacRegisterResourceFork( + short fileRef, /* File ref for an open resource fork. */ + Tcl_Obj *tokenPtr, /* A Tcl Object to which to write the * + * new token */ + int flags) /* 1 means insert at the head of the resource + * fork list, 0 means at the tail */ + +{ + Tcl_HashEntry *resourceHashPtr; + Tcl_HashEntry *nameHashPtr; + OpenResourceFork *resourceRef; + int new; + char *resourceId = NULL; + + if (!initialized) { + ResourceInit(); + } + + /* + * If we were asked to, check that this file has not been opened + * already with a different permission. It it has, then return an error. + */ + + new = 1; + + if (flags & TCL_RESOURCE_CHECK_IF_OPEN) { + Tcl_HashSearch search; + short oldFileRef, filePermissionFlag; + FCBPBRec newFileRec, oldFileRec; + OSErr err; + + oldFileRec.ioCompletion = NULL; + oldFileRec.ioFCBIndx = 0; + oldFileRec.ioNamePtr = NULL; + + newFileRec.ioCompletion = NULL; + newFileRec.ioFCBIndx = 0; + newFileRec.ioNamePtr = NULL; + newFileRec.ioVRefNum = 0; + newFileRec.ioRefNum = fileRef; + err = PBGetFCBInfo(&newFileRec, false); + filePermissionFlag = ( newFileRec.ioFCBFlags >> 12 ) & 0x1; + + + resourceHashPtr = Tcl_FirstHashEntry(&resourceTable, &search); + while (resourceHashPtr != NULL) { + oldFileRef = (short) Tcl_GetHashKey(&resourceTable, + resourceHashPtr); + if (oldFileRef == fileRef) { + new = 0; + break; + } + oldFileRec.ioVRefNum = 0; + oldFileRec.ioRefNum = oldFileRef; + err = PBGetFCBInfo(&oldFileRec, false); + + /* + * err might not be noErr either because the file has closed + * out from under us somehow, which is bad but we're not going + * to fix it here, OR because it is the ROM MAP, which has a + * fileRef, but can't be gotten to by PBGetFCBInfo. + */ + if ((err == noErr) + && (newFileRec.ioFCBVRefNum == oldFileRec.ioFCBVRefNum) + && (newFileRec.ioFCBFlNm == oldFileRec.ioFCBFlNm)) { + /* + * In MacOS 8.1 it seems like we get different file refs even + * though we pass the same file & permissions. This is not + * what Inside Mac says should happen, but it does, so if it + * does, then close the new res file and return the original + * one... + */ + + if (filePermissionFlag == ((oldFileRec.ioFCBFlags >> 12) & 0x1)) { + CloseResFile(fileRef); + new = 0; + break; + } else { + if (tokenPtr != NULL) { + Tcl_SetStringObj(tokenPtr, "Resource already open with different permissions.", -1); + } + return TCL_ERROR; + } + } + resourceHashPtr = Tcl_NextHashEntry(&search); + } + } + + + /* + * If the file has already been opened with these same permissions, then it + * will be in our list and we will have set new to 0 above. + * So we will just return the token (if tokenPtr is non-null) + */ + + if (new) { + resourceHashPtr = Tcl_CreateHashEntry(&resourceTable, + (char *) fileRef, &new); + } + + if (!new) { + if (tokenPtr != NULL) { + resourceId = (char *) Tcl_GetHashValue(resourceHashPtr); + Tcl_SetStringObj(tokenPtr, resourceId, -1); + } + return TCL_OK; + } + + /* + * If we were passed in a result pointer which is not an empty + * string, attempt to use that as the key. If the key already + * exists, silently fall back on resource%d... + */ + + if (tokenPtr != NULL) { + char *tokenVal; + int length; + tokenVal = Tcl_GetStringFromObj(tokenPtr, &length); + if (length > 0) { + nameHashPtr = Tcl_FindHashEntry(&nameTable, tokenVal); + if (nameHashPtr == NULL) { + resourceId = ckalloc(length + 1); + memcpy(resourceId, tokenVal, length); + resourceId[length] = '\0'; + } + } + } + + if (resourceId == NULL) { + resourceId = (char *) ckalloc(15); + sprintf(resourceId, "resource%d", newId); + } + + Tcl_SetHashValue(resourceHashPtr, resourceId); + newId++; + + nameHashPtr = Tcl_CreateHashEntry(&nameTable, resourceId, &new); + if (!new) { + Tcl_Panic("resource id has repeated itself"); + } + + resourceRef = (OpenResourceFork *) ckalloc(sizeof(OpenResourceFork)); + resourceRef->fileRef = fileRef; + resourceRef->flags = flags; + + Tcl_SetHashValue(nameHashPtr, (ClientData) resourceRef); + if (tokenPtr != NULL) { + Tcl_SetStringObj(tokenPtr, resourceId, -1); + } + + if (flags & TCL_RESOURCE_INSERT_TAIL) { + Tcl_ListObjAppendElement(NULL, resourceForkList, tokenPtr); + } else { + Tcl_ListObjReplace(NULL, resourceForkList, 0, 0, 1, &tokenPtr); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacUnRegisterResourceFork -- + * + * Removes the entry for an open resource fork from the table of + * open resources managed by the procedures in this file. + * If resultPtr is not NULL, it will be used for error reporting. + * + * Results: + * The fileRef for this token, or -1 if an error occured. + * + * Side effects: + * An entry is removed from the resource name table. + * + *---------------------------------------------------------------------- + */ + +short +TclMacUnRegisterResourceFork( + char *tokenPtr, + Tcl_Obj *resultPtr) + +{ + Tcl_HashEntry *resourceHashPtr; + Tcl_HashEntry *nameHashPtr; + OpenResourceFork *resourceRef; + char *resourceId = NULL; + short fileRef; + char *bytes; + int i, match, index, listLen, length, elemLen; + Tcl_Obj **elemPtrs; + + + nameHashPtr = Tcl_FindHashEntry(&nameTable, tokenPtr); + if (nameHashPtr == NULL) { + if (resultPtr != NULL) { + Tcl_AppendStringsToObj(resultPtr, + "invalid resource file reference \"", + tokenPtr, "\"", (char *) NULL); + } + return -1; + } + + resourceRef = (OpenResourceFork *) Tcl_GetHashValue(nameHashPtr); + fileRef = resourceRef->fileRef; + + if ( resourceRef->flags & TCL_RESOURCE_DONT_CLOSE ) { + if (resultPtr != NULL) { + Tcl_AppendStringsToObj(resultPtr, + "can't close \"", tokenPtr, "\" resource file", + (char *) NULL); + } + return -1; + } + + Tcl_DeleteHashEntry(nameHashPtr); + ckfree((char *) resourceRef); + + + /* + * Now remove the resource from the resourceForkList object + */ + + Tcl_ListObjGetElements(NULL, resourceForkList, &listLen, &elemPtrs); + + + index = -1; + length = strlen(tokenPtr); + + for (i = 0; i < listLen; i++) { + match = 0; + bytes = Tcl_GetStringFromObj(elemPtrs[i], &elemLen); + if (length == elemLen) { + match = (memcmp(bytes, tokenPtr, + (size_t) length) == 0); + } + if (match) { + index = i; + break; + } + } + if (!match) { + Tcl_Panic("the resource Fork List is out of synch!"); + } + + Tcl_ListObjReplace(NULL, resourceForkList, index, 1, 0, NULL); + + resourceHashPtr = Tcl_FindHashEntry(&resourceTable, (char *) fileRef); + + if (resourceHashPtr == NULL) { + Tcl_Panic("Resource & Name tables are out of synch in resource command."); + } + ckfree(Tcl_GetHashValue(resourceHashPtr)); + Tcl_DeleteHashEntry(resourceHashPtr); + + return fileRef; + +} + + +/* + *---------------------------------------------------------------------- + * + * BuildResourceForkList -- + * + * Traverses the list of open resource forks, and builds the + * list of resources forks. Also creates a resource token for any that + * are opened but not registered with our resource system. + * This is based on code from Apple DTS. + * + * Results: + * None. + * + * Side effects: + * The list of resource forks is updated. + * The resource name table may be augmented. + * + *---------------------------------------------------------------------- + */ + +void +BuildResourceForkList() +{ + Handle currentMapHandle, mSysMapHandle; + Ptr tempPtr; + FCBPBRec fileRec; + char fileName[256]; + char appName[62]; + Tcl_Obj *nameObj; + OSErr err; + ProcessSerialNumber psn; + ProcessInfoRec info; + FSSpec fileSpec; + + /* + * Get the application name, so we can substitute + * the token "application" for the application's resource. + */ + + GetCurrentProcess(&psn); + info.processInfoLength = sizeof(ProcessInfoRec); + info.processName = (StringPtr) &appName; + info.processAppSpec = &fileSpec; + GetProcessInformation(&psn, &info); + p2cstr((StringPtr) appName); + + + fileRec.ioCompletion = NULL; + fileRec.ioVRefNum = 0; + fileRec.ioFCBIndx = 0; + fileRec.ioNamePtr = (StringPtr) &fileName; + + + currentMapHandle = LMGetTopMapHndl(); + mSysMapHandle = LMGetSysMapHndl(); + + while (1) { + /* + * Now do the ones opened after the application. + */ + + nameObj = Tcl_NewObj(); + + tempPtr = *currentMapHandle; + + fileRec.ioRefNum = *((short *) (tempPtr + 20)); + err = PBGetFCBInfo(&fileRec, false); + + if (err != noErr) { + /* + * The ROM resource map does not correspond to an opened file... + */ + Tcl_SetStringObj(nameObj, "ROM Map", -1); + } else { + p2cstr((StringPtr) fileName); + if (strcmp(fileName,appName) == 0) { + Tcl_SetStringObj(nameObj, "application", -1); + } else { + Tcl_SetStringObj(nameObj, fileName, -1); + } + c2pstr(fileName); + } + + TclMacRegisterResourceFork(fileRec.ioRefNum, nameObj, + TCL_RESOURCE_DONT_CLOSE | TCL_RESOURCE_INSERT_TAIL); + + if (currentMapHandle == mSysMapHandle) { + break; + } + + currentMapHandle = *((Handle *) (tempPtr + 16)); + } +} diff --git a/mac/tclMacResource.r b/mac/tclMacResource.r new file mode 100644 index 0000000..664b715 --- /dev/null +++ b/mac/tclMacResource.r @@ -0,0 +1,44 @@ +/* + * tclMacResource.r -- + * + * This file creates resources for use in a simple shell. + * This is designed to be an example of using the Tcl libraries + * statically in a Macintosh Application. For an example of + * of using the dynamic libraries look at tclMacApplication.r. + * + * Copyright (c) 1993-94 Lockheed Missle & Space Company + * Copyright (c) 1994-97 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacResource.r,v 1.8 2002/09/12 17:33:20 das Exp $ + */ + +#include <Types.r> +#include <SysTypes.r> + +/* + * The folowing include and defines help construct + * the version string for Tcl. + */ + +#define RC_INVOKED +#include "tcl.h" + +/* + * The mechanisim below loads Tcl source into the resource fork of the + * application. The example below creates a TEXT resource named + * "Init" from the file "init.tcl". This allows applications to use + * Tcl to define the behavior of the application without having to + * require some predetermined file structure - all needed Tcl "files" + * are located within the application. To source a file for the + * resource fork the source command has been modified to support + * sourcing from resources. In the below case "source -rsrc {Init}" + * will load the TEXT resource named "Init". + */ + +#ifndef TCLTK_NO_LIBRARY_TEXT_RESOURCES +#include "tclMacTclCode.r" +#endif + diff --git a/mac/tclMacSock.c b/mac/tclMacSock.c new file mode 100644 index 0000000..4321ea3 --- /dev/null +++ b/mac/tclMacSock.c @@ -0,0 +1,2881 @@ +/* + * tclMacSock.c + * + * Channel drivers for Macintosh sockets. + * + * Copyright (c) 1996-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacSock.c,v 1.16 2003/12/24 04:18:21 davygrvy Exp $ + */ + +#include "tclInt.h" +#include "tclPort.h" +#include "tclMacInt.h" +#include <AddressXlation.h> +#include <Aliases.h> +#undef Status +#include <Devices.h> +#include <Errors.h> +#include <Events.h> +#include <Files.h> +#include <Gestalt.h> +#include <MacTCP.h> +#include <Processes.h> +#include <Strings.h> + +/* + * The following variable is used to tell whether this module has been + * initialized. + */ + +static int initialized = 0; + +/* + * If debugging is on we may drop into the debugger to handle certain cases + * that are not supposed to happen. Otherwise, we change ignore the error + * and most code should handle such errors ok. + */ + +#ifndef TCL_DEBUG + #define Debugger() +#endif + +/* + * The preferred buffer size for Macintosh channels. + */ + +#define CHANNEL_BUF_SIZE 8192 + +/* + * Port information structure. Used to match service names + * to a Tcp/Ip port number. + */ + +typedef struct { + char *name; /* Name of service. */ + int port; /* Port number. */ +} PortInfo; + +/* + * This structure describes per-instance state of a tcp based channel. + */ + +typedef struct TcpState { + TCPiopb pb; /* Parameter block used by this stream. + * This must be in the first position. */ + ProcessSerialNumber psn; /* PSN used to wake up process. */ + StreamPtr tcpStream; /* Macintosh tcp stream pointer. */ + int port; /* The port we are connected to. */ + int flags; /* Bit field comprised of the flags + * described below. */ + int checkMask; /* OR'ed combination of TCL_READABLE and + * TCL_WRITABLE as set by an asynchronous + * event handler. */ + int watchMask; /* OR'ed combination of TCL_READABLE and + * TCL_WRITABLE as set by TcpWatch. */ + Tcl_TcpAcceptProc *acceptProc; /* Proc to call on accept. */ + ClientData acceptProcData; /* The data for the accept proc. */ + wdsEntry dataSegment[2]; /* List of buffers to be written async. */ + rdsEntry rdsarray[5+1]; /* Array used when cleaning out recieve + * buffers on a closing socket. */ + Tcl_Channel channel; /* Channel associated with this socket. */ + int writeBufferSize; /* Size of buffer to hold data for + * asynchronous writes. */ + void *writeBuffer; /* Buffer for async write data. */ + struct TcpState *nextPtr; /* The next socket on the global socket + * list. */ +} TcpState; + +/* + * This structure is used by domain name resolver callback. + */ + +typedef struct DNRState { + struct hostInfo hostInfo; /* Data structure used by DNR functions. */ + int done; /* Flag to determine when we are done. */ + ProcessSerialNumber psn; /* Process to wake up when we are done. */ +} DNRState; + +/* + * The following macros may be used to set the flags field of + * a TcpState structure. + */ + +#define TCP_ASYNC_SOCKET (1<<0) /* The socket is in async mode. */ +#define TCP_ASYNC_CONNECT (1<<1) /* The socket is trying to connect. */ +#define TCP_CONNECTED (1<<2) /* The socket is connected. */ +#define TCP_PENDING (1<<3) /* A SocketEvent is on the queue. */ +#define TCP_LISTENING (1<<4) /* This socket is listening for + * a connection. */ +#define TCP_LISTEN_CONNECT (1<<5) /* Someone has connect to the + * listening port. */ +#define TCP_REMOTE_CLOSED (1<<6) /* The remote side has closed + * the connection. */ +#define TCP_RELEASE (1<<7) /* The socket may now be released. */ +#define TCP_WRITING (1<<8) /* A background write is in progress. */ +#define TCP_SERVER_ZOMBIE (1<<9) /* The server can no longer accept connects. */ + +/* + * The following structure is what is added to the Tcl event queue when + * a socket event occurs. + */ + +typedef struct SocketEvent { + Tcl_Event header; /* Information that is standard for + * all events. */ + TcpState *statePtr; /* Socket descriptor that is ready. */ + StreamPtr tcpStream; /* Low level Macintosh stream. */ +} SocketEvent; + +/* + * Static routines for this file: + */ + +static pascal void CleanUpExitProc _ANSI_ARGS_((void)); +static void ClearZombieSockets _ANSI_ARGS_((void)); +static void CloseCompletionRoutine _ANSI_ARGS_((TCPiopb *pb)); +static TcpState * CreateSocket _ANSI_ARGS_((Tcl_Interp *interp, + int port, CONST char *host, CONST char *myAddr, + int myPort, int server, int async)); +static pascal void DNRCompletionRoutine _ANSI_ARGS_(( + struct hostInfo *hostinfoPtr, + DNRState *dnrStatePtr)); +static void FreeSocketInfo _ANSI_ARGS_((TcpState *statePtr)); +static long GetBufferSize _ANSI_ARGS_((void)); +static OSErr GetHostFromString _ANSI_ARGS_((CONST char *name, + ip_addr *address)); +static OSErr GetLocalAddress _ANSI_ARGS_((unsigned long *addr)); +static void IOCompletionRoutine _ANSI_ARGS_((TCPiopb *pb)); +static void InitMacTCPParamBlock _ANSI_ARGS_((TCPiopb *pBlock, + int csCode)); +static void InitSockets _ANSI_ARGS_((void)); +static TcpState * NewSocketInfo _ANSI_ARGS_((StreamPtr stream)); +static OSErr ResolveAddress _ANSI_ARGS_((ip_addr tcpAddress, + Tcl_DString *dsPtr)); +static void SocketCheckProc _ANSI_ARGS_((ClientData clientData, + int flags)); +static int SocketEventProc _ANSI_ARGS_((Tcl_Event *evPtr, + int flags)); +static void SocketExitHandler _ANSI_ARGS_((ClientData clientData)); +static void SocketFreeProc _ANSI_ARGS_((ClientData clientData)); +static int SocketReady _ANSI_ARGS_((TcpState *statePtr)); +static void SocketSetupProc _ANSI_ARGS_((ClientData clientData, + int flags)); +static void TcpAccept _ANSI_ARGS_((TcpState *statePtr)); +static int TcpBlockMode _ANSI_ARGS_((ClientData instanceData, int mode)); +static int TcpClose _ANSI_ARGS_((ClientData instanceData, + Tcl_Interp *interp)); +static int TcpGetHandle _ANSI_ARGS_((ClientData instanceData, + int direction, ClientData *handlePtr)); +static int TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData, + Tcl_Interp *interp, CONST char *optionName, + Tcl_DString *dsPtr)); +static int TcpInput _ANSI_ARGS_((ClientData instanceData, + char *buf, int toRead, int *errorCodePtr)); +static int TcpOutput _ANSI_ARGS_((ClientData instanceData, + CONST char *buf, int toWrite, int *errorCodePtr)); +static void TcpWatch _ANSI_ARGS_((ClientData instanceData, + int mask)); +static int WaitForSocketEvent _ANSI_ARGS_((TcpState *infoPtr, + int mask, int *errorCodePtr)); + +pascal void NotifyRoutine ( + StreamPtr tcpStream, + unsigned short eventCode, + Ptr userDataPtr, + unsigned short terminReason, + struct ICMPReport *icmpMsg); + +/* + * This structure describes the channel type structure for TCP socket + * based IO: + */ + +static Tcl_ChannelType tcpChannelType = { + "tcp", /* Type name. */ + (Tcl_ChannelTypeVersion)TcpBlockMode, /* Set blocking or + * non-blocking mode.*/ + TcpClose, /* Close proc. */ + TcpInput, /* Input proc. */ + TcpOutput, /* Output proc. */ + NULL, /* Seek proc. */ + NULL, /* Set option proc. */ + TcpGetOptionProc, /* Get option proc. */ + TcpWatch, /* Initialize notifier. */ + TcpGetHandle /* Get handles out of channel. */ +}; + +/* + * Universal Procedure Pointers (UPP) for various callback + * routines used by MacTcp code. + */ + +ResultUPP resultUPP = NULL; +TCPIOCompletionUPP completeUPP = NULL; +TCPIOCompletionUPP closeUPP = NULL; +TCPNotifyUPP notifyUPP = NULL; + +/* + * Built-in commands, and the procedures associated with them: + */ + +static PortInfo portServices[] = { + {"echo", 7}, + {"discard", 9}, + {"systat", 11}, + {"daytime", 13}, + {"netstat", 15}, + {"chargen", 19}, + {"ftp-data", 20}, + {"ftp", 21}, + {"telnet", 23}, + {"telneto", 24}, + {"smtp", 25}, + {"time", 37}, + {"whois", 43}, + {"domain", 53}, + {"gopher", 70}, + {"finger", 79}, + {"hostnames", 101}, + {"sunrpc", 111}, + {"nntp", 119}, + {"exec", 512}, + {"login", 513}, + {"shell", 514}, + {"printer", 515}, + {"courier", 530}, + {"uucp", 540}, + {NULL, 0}, +}; + +typedef struct ThreadSpecificData { + /* + * Every open socket has an entry on the following list. + */ + + TcpState *socketList; +} ThreadSpecificData; + +static Tcl_ThreadDataKey dataKey; + +/* + * Globals for holding information about OS support for sockets. + */ + +static int socketsTestInited = false; +static int hasSockets = false; +static short driverRefNum = 0; +static int socketNumber = 0; +static int socketBufferSize = CHANNEL_BUF_SIZE; +static ProcessSerialNumber applicationPSN; + +/* + *---------------------------------------------------------------------- + * + * InitSockets -- + * + * Load the MacTCP driver and open the name resolver. We also + * create several UPP's used by our code. Lastly, we install + * a patch to ExitToShell to clean up socket connections if + * we are about to exit. + * + * Results: + * 1 if successful, 0 on failure. + * + * Side effects: + * Creates a new event source, loads the MacTCP driver, + * registers an exit to shell callback. + * + *---------------------------------------------------------------------- + */ + +#define gestaltMacTCPVersion 'mtcp' +static void +InitSockets() +{ + ParamBlockRec pb; + OSErr err; + long response; + ThreadSpecificData *tsdPtr; + + if (! initialized) { + /* + * Do process wide initialization. + */ + + initialized = 1; + + if (Gestalt(gestaltMacTCPVersion, &response) == noErr) { + hasSockets = true; + } else { + hasSockets = false; + } + + if (!hasSockets) { + return; + } + + /* + * Load MacTcp driver and name server resolver. + */ + + + pb.ioParam.ioCompletion = 0L; + pb.ioParam.ioNamePtr = "\p.IPP"; + pb.ioParam.ioPermssn = fsCurPerm; + err = PBOpenSync(&pb); + if (err != noErr) { + hasSockets = 0; + return; + } + driverRefNum = pb.ioParam.ioRefNum; + + socketBufferSize = GetBufferSize(); + err = OpenResolver(NULL); + if (err != noErr) { + hasSockets = 0; + return; + } + + GetCurrentProcess(&applicationPSN); + /* + * Create UPP's for various callback routines. + */ + + resultUPP = NewResultProc(DNRCompletionRoutine); + completeUPP = NewTCPIOCompletionProc(IOCompletionRoutine); + closeUPP = NewTCPIOCompletionProc(CloseCompletionRoutine); + notifyUPP = NewTCPNotifyProc(NotifyRoutine); + + /* + * Install an ExitToShell patch. We use this patch instead + * of the Tcl exit mechanism because we need to ensure that + * these routines are cleaned up even if we crash or are forced + * to quit. There are some circumstances when the Tcl exit + * handlers may not fire. + */ + + TclMacInstallExitToShellPatch(CleanUpExitProc); + } + + /* + * Do per-thread initialization. + */ + + tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); + if (tsdPtr == NULL) { + tsdPtr->socketList = NULL; + Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL); + Tcl_CreateThreadExitHandler(SocketExitHandler, (ClientData) NULL); + } +} + +/* + *---------------------------------------------------------------------- + * + * SocketExitHandler -- + * + * Callback invoked during exit clean up to deinitialize the + * socket module. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +SocketExitHandler( + ClientData clientData) /* Not used. */ +{ + if (hasSockets) { + Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL); + /* CleanUpExitProc(); + TclMacDeleteExitToShellPatch(CleanUpExitProc); */ + } +} + +/* + *---------------------------------------------------------------------- + * + * TclpHasSockets -- + * + * This function determines whether sockets are available on the + * current system and returns an error in interp if they are not. + * Note that interp may be NULL. + * + * Results: + * Returns TCL_OK if the system supports sockets, or TCL_ERROR with + * an error in interp. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclpHasSockets( + Tcl_Interp *interp) /* Interp for error messages. */ +{ + InitSockets(); + + if (hasSockets) { + return TCL_OK; + } + if (interp != NULL) { + Tcl_AppendResult(interp, "sockets are not available on this system", + NULL); + } + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * SocketSetupProc -- + * + * This procedure is invoked before Tcl_DoOneEvent blocks waiting + * for an event. + * + * Results: + * None. + * + * Side effects: + * Adjusts the block time if needed. + * + *---------------------------------------------------------------------- + */ + +static void +SocketSetupProc( + ClientData data, /* Not used. */ + int flags) /* Event flags as passed to Tcl_DoOneEvent. */ +{ + TcpState *statePtr; + Tcl_Time blockTime = { 0, 0 }; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (!(flags & TCL_FILE_EVENTS)) { + return; + } + + /* + * Check to see if there is a ready socket. If so, poll. + */ + + for (statePtr = tsdPtr->socketList; statePtr != NULL; + statePtr = statePtr->nextPtr) { + if (statePtr->flags & TCP_RELEASE) { + continue; + } + if (SocketReady(statePtr)) { + Tcl_SetMaxBlockTime(&blockTime); + break; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * SocketCheckProc -- + * + * This procedure is called by Tcl_DoOneEvent to check the socket + * event source for events. + * + * Results: + * None. + * + * Side effects: + * May queue an event. + * + *---------------------------------------------------------------------- + */ + +static void +SocketCheckProc( + ClientData data, /* Not used. */ + int flags) /* Event flags as passed to Tcl_DoOneEvent. */ +{ + TcpState *statePtr; + SocketEvent *evPtr; + TcpState dummyState; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (!(flags & TCL_FILE_EVENTS)) { + return; + } + + /* + * Queue events for any ready sockets that don't already have events + * queued (caused by persistent states that won't generate WinSock + * events). + */ + + for (statePtr = tsdPtr->socketList; statePtr != NULL; + statePtr = statePtr->nextPtr) { + /* + * Check to see if this socket is dead and needs to be cleaned + * up. We use a dummy statePtr whose only valid field is the + * nextPtr to allow the loop to continue even if the element + * is deleted. + */ + + if (statePtr->flags & TCP_RELEASE) { + if (!(statePtr->flags & TCP_PENDING)) { + dummyState.nextPtr = statePtr->nextPtr; + SocketFreeProc(statePtr); + statePtr = &dummyState; + } + continue; + } + + if (!(statePtr->flags & TCP_PENDING) && SocketReady(statePtr)) { + statePtr->flags |= TCP_PENDING; + evPtr = (SocketEvent *) ckalloc(sizeof(SocketEvent)); + evPtr->header.proc = SocketEventProc; + evPtr->statePtr = statePtr; + evPtr->tcpStream = statePtr->tcpStream; + Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * SocketReady -- + * + * This function checks the current state of a socket to see + * if any interesting conditions are present. + * + * Results: + * Returns 1 if an event that someone is watching is present, else + * returns 0. + * + * Side effects: + * Updates the checkMask for the socket to reflect any newly + * detected events. + * + *---------------------------------------------------------------------- + */ + +static int +SocketReady( + TcpState *statePtr) +{ + TCPiopb statusPB; + int foundSomething = 0; + int didStatus = 0; + int amount; + OSErr err; + + if (statePtr->flags & TCP_LISTEN_CONNECT) { + foundSomething = 1; + statePtr->checkMask |= TCL_READABLE; + } + if (statePtr->watchMask & TCL_READABLE) { + if (statePtr->checkMask & TCL_READABLE) { + foundSomething = 1; + } else if (statePtr->flags & TCP_CONNECTED) { + statusPB.ioCRefNum = driverRefNum; + statusPB.tcpStream = statePtr->tcpStream; + statusPB.csCode = TCPStatus; + err = PBControlSync((ParmBlkPtr) &statusPB); + didStatus = 1; + + /* + * We make the fchannel readable if 1) we get an error, + * 2) there is more data available, or 3) we detect + * that a close from the remote connection has arrived. + */ + + if ((err != noErr) || + (statusPB.csParam.status.amtUnreadData > 0) || + (statusPB.csParam.status.connectionState == 14)) { + statePtr->checkMask |= TCL_READABLE; + foundSomething = 1; + } + } + } + if (statePtr->watchMask & TCL_WRITABLE) { + if (statePtr->checkMask & TCL_WRITABLE) { + foundSomething = 1; + } else if (statePtr->flags & TCP_CONNECTED) { + if (!didStatus) { + statusPB.ioCRefNum = driverRefNum; + statusPB.tcpStream = statePtr->tcpStream; + statusPB.csCode = TCPStatus; + err = PBControlSync((ParmBlkPtr) &statusPB); + } + + /* + * If there is an error or there if there is room to + * send more data we make the channel writeable. + */ + + amount = statusPB.csParam.status.sendWindow - + statusPB.csParam.status.amtUnackedData; + if ((err != noErr) || (amount > 0)) { + statePtr->checkMask |= TCL_WRITABLE; + foundSomething = 1; + } + } + } + return foundSomething; +} + +/* + *---------------------------------------------------------------------- + * + * InitMacTCPParamBlock-- + * + * Initialize a MacTCP parameter block. + * + * Results: + * None. + * + * Side effects: + * Initializes the parameter block. + * + *---------------------------------------------------------------------- + */ + +static void +InitMacTCPParamBlock( + TCPiopb *pBlock, /* Tcp parmeter block. */ + int csCode) /* Tcp operation code. */ +{ + memset(pBlock, 0, sizeof(TCPiopb)); + pBlock->ioResult = 1; + pBlock->ioCRefNum = driverRefNum; + pBlock->csCode = (short) csCode; +} + +/* + *---------------------------------------------------------------------- + * + * TcpBlockMode -- + * + * Set blocking or non-blocking mode on channel. + * + * Results: + * 0 if successful, errno when failed. + * + * Side effects: + * Sets the device into blocking or non-blocking mode. + * + *---------------------------------------------------------------------- + */ + +static int +TcpBlockMode( + ClientData instanceData, /* Channel state. */ + int mode) /* The mode to set. */ +{ + TcpState *statePtr = (TcpState *) instanceData; + + if (mode == TCL_MODE_BLOCKING) { + statePtr->flags &= ~TCP_ASYNC_SOCKET; + } else { + statePtr->flags |= TCP_ASYNC_SOCKET; + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * TcpClose -- + * + * Close the socket. + * + * Results: + * 0 if successful, the value of errno if failed. + * + * Side effects: + * Closes the socket. + * + *---------------------------------------------------------------------- + */ + +static int +TcpClose( + ClientData instanceData, /* The socket to close. */ + Tcl_Interp *interp) /* Interp for error messages. */ +{ + TcpState *statePtr = (TcpState *) instanceData; + StreamPtr tcpStream; + TCPiopb closePB; + OSErr err; + + tcpStream = statePtr->tcpStream; + statePtr->flags &= ~TCP_CONNECTED; + + /* + * If this is a server socket we can't use the statePtr + * param block because it is in use. However, we can + * close syncronously. + */ + + if ((statePtr->flags & TCP_LISTENING) || + (statePtr->flags & TCP_LISTEN_CONNECT)) { + InitMacTCPParamBlock(&closePB, TCPClose); + closePB.tcpStream = tcpStream; + closePB.ioCompletion = NULL; + closePB.csParam.close.ulpTimeoutValue = 60 /* seconds */; + closePB.csParam.close.ulpTimeoutAction = 1 /* 1:abort 0:report */; + closePB.csParam.close.validityFlags = timeoutValue | timeoutAction; + err = PBControlSync((ParmBlkPtr) &closePB); + if (err != noErr) { + Debugger(); + goto afterRelease; + /* Tcl_Panic("error closing server socket"); */ + } + statePtr->flags |= TCP_RELEASE; + + /* + * Server sockets are closed sync. Therefor, we know it is OK to + * release the socket now. + */ + + InitMacTCPParamBlock(&statePtr->pb, TCPRelease); + statePtr->pb.tcpStream = statePtr->tcpStream; + err = PBControlSync((ParmBlkPtr) &statePtr->pb); + if (err != noErr) { + Tcl_Panic("error releasing server socket"); + } + + /* + * Free the buffer space used by the socket and the + * actual socket state data structure. + */ + afterRelease: + + /* + * Have to check whether the pointer is NULL, since we could get here + * on a failed socket open, and then the rcvBuff would never have been + * allocated. + */ + + if (err == noErr) { + ckfree((char *) statePtr->pb.csParam.create.rcvBuff); + } + FreeSocketInfo(statePtr); + return 0; + } + + /* + * If this socket is in the midddle on async connect we can just + * abort the connect and release the stream right now. + */ + + if (statePtr->flags & TCP_ASYNC_CONNECT) { + InitMacTCPParamBlock(&closePB, TCPClose); + closePB.tcpStream = tcpStream; + closePB.ioCompletion = NULL; + err = PBControlSync((ParmBlkPtr) &closePB); + if (err == noErr) { + statePtr->flags |= TCP_RELEASE; + + InitMacTCPParamBlock(&closePB, TCPRelease); + closePB.tcpStream = tcpStream; + closePB.ioCompletion = NULL; + + err = PBControlSync((ParmBlkPtr) &closePB); + } + + /* + * Free the buffer space used by the socket and the + * actual socket state data structure. However, if the + * RELEASE returns an error, then the rcvBuff is usually + * bad, so we can't release it. I think this means we will + * leak the buffer, so in the future, we may want to track the + * buffers separately, and nuke them on our own (or just not + * use MacTCP!). + */ + + if (err == noErr) { + ckfree((char *) closePB.csParam.create.rcvBuff); + } + + FreeSocketInfo(statePtr); + return err; + } + + /* + * Client sockets: + * If a background write is in progress, don't close + * the socket yet. The completion routine for the + * write will take care of it. + */ + + if (!(statePtr->flags & TCP_WRITING)) { + InitMacTCPParamBlock(&statePtr->pb, TCPClose); + statePtr->pb.tcpStream = tcpStream; + statePtr->pb.ioCompletion = closeUPP; + statePtr->pb.csParam.close.userDataPtr = (Ptr) statePtr; + err = PBControlAsync((ParmBlkPtr) &statePtr->pb); + if (err != noErr) { + Debugger(); + statePtr->flags |= TCP_RELEASE; + /* return 0; */ + } + } + + SocketFreeProc(instanceData); + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * CloseCompletionRoutine -- + * + * Handles the close protocol for a Tcp socket. This will do + * a series of calls to release all data currently buffered for + * the socket. This is important to do to as it allows the remote + * connection to recieve and issue it's own close on the socket. + * Note that this function is running at interupt time and can't + * allocate memory or do much else except set state. + * + * Results: + * None. + * + * Side effects: + * The buffers for the socket are flushed. + * + *---------------------------------------------------------------------- + */ + +static void +CloseCompletionRoutine( + TCPiopb *pbPtr) /* Tcp parameter block. */ +{ + TcpState *statePtr; + OSErr err; + + if (pbPtr->csCode == TCPClose) { + statePtr = (TcpState *) (pbPtr->csParam.close.userDataPtr); + } else { + statePtr = (TcpState *) (pbPtr->csParam.receive.userDataPtr); + } + + /* + * It's very bad if the statePtr is nNULL - we should probably panic... + */ + + if (statePtr == NULL) { + Debugger(); + return; + } + + WakeUpProcess(&statePtr->psn); + + /* + * If there is an error we assume the remote side has already + * close. We are done closing as soon as we decide that the + * remote connection has closed. + */ + + if (pbPtr->ioResult != noErr) { + statePtr->flags |= TCP_RELEASE; + return; + } + if (statePtr->flags & TCP_REMOTE_CLOSED) { + statePtr->flags |= TCP_RELEASE; + return; + } + + /* + * If we just did a recieve we need to return the buffers. + * Otherwise, attempt to recieve more data until we recieve an + * error (usually because we have no more data). + */ + + if (statePtr->pb.csCode == TCPNoCopyRcv) { + InitMacTCPParamBlock(&statePtr->pb, TCPRcvBfrReturn); + statePtr->pb.tcpStream = statePtr->tcpStream; + statePtr->pb.ioCompletion = closeUPP; + statePtr->pb.csParam.receive.rdsPtr = (Ptr) statePtr->rdsarray; + statePtr->pb.csParam.receive.userDataPtr = (Ptr) statePtr; + err = PBControlAsync((ParmBlkPtr) &statePtr->pb); + } else { + InitMacTCPParamBlock(&statePtr->pb, TCPNoCopyRcv); + statePtr->pb.tcpStream = statePtr->tcpStream; + statePtr->pb.ioCompletion = closeUPP; + statePtr->pb.csParam.receive.commandTimeoutValue = 1; + statePtr->pb.csParam.receive.rdsPtr = (Ptr) statePtr->rdsarray; + statePtr->pb.csParam.receive.rdsLength = 5; + statePtr->pb.csParam.receive.userDataPtr = (Ptr) statePtr; + err = PBControlAsync((ParmBlkPtr) &statePtr->pb); + } + + if (err != noErr) { + statePtr->flags |= TCP_RELEASE; + } +} +/* + *---------------------------------------------------------------------- + * + * SocketFreeProc -- + * + * This callback is invoked in order to delete + * the notifier data associated with a file handle. + * + * Results: + * None. + * + * Side effects: + * Removes the SocketInfo from the global socket list. + * + *---------------------------------------------------------------------- + */ + +static void +SocketFreeProc( + ClientData clientData) /* Channel state. */ +{ + TcpState *statePtr = (TcpState *) clientData; + OSErr err; + TCPiopb statusPB; + + /* + * Get the status of this connection. We need to do a + * few tests to see if it's OK to release the stream now. + */ + + if (!(statePtr->flags & TCP_RELEASE)) { + return; + } + statusPB.ioCRefNum = driverRefNum; + statusPB.tcpStream = statePtr->tcpStream; + statusPB.csCode = TCPStatus; + err = PBControlSync((ParmBlkPtr) &statusPB); + if ((statusPB.csParam.status.connectionState == 0) || + (statusPB.csParam.status.connectionState == 2)) { + /* + * If the conection state is 0 then this was a client + * connection and it's closed. If it is 2 then this a + * server client and we may release it. If it isn't + * one of those values then we return and we'll try to + * clean up later. + */ + + } else { + return; + } + + /* + * The Close request is made async. We know it's + * OK to release the socket when the TCP_RELEASE flag + * gets set. + */ + + InitMacTCPParamBlock(&statePtr->pb, TCPRelease); + statePtr->pb.tcpStream = statePtr->tcpStream; + err = PBControlSync((ParmBlkPtr) &statePtr->pb); + if (err != noErr) { + Debugger(); /* Ignoreing leaves stranded stream. Is there an + alternative? */ + } + + /* + * Free the buffer space used by the socket and the + * actual socket state data structure. + */ + + ckfree((char *) statePtr->pb.csParam.create.rcvBuff); + FreeSocketInfo(statePtr); +} + +/* + *---------------------------------------------------------------------- + * + * TcpInput -- + * + * Reads input from the IO channel into the buffer given. Returns + * count of how many bytes were actually read, and an error + * indication. + * + * Results: + * A count of how many bytes were read is returned. A value of -1 + * implies an error occured. A value of zero means we have reached + * the end of data (EOF). + * + * Side effects: + * Reads input from the actual channel. + * + *---------------------------------------------------------------------- + */ + +int +TcpInput( + ClientData instanceData, /* Channel state. */ + char *buf, /* Where to store data read. */ + int bufSize, /* How much space is available + * in the buffer? */ + int *errorCodePtr) /* Where to store error code. */ +{ + TcpState *statePtr = (TcpState *) instanceData; + StreamPtr tcpStream; + OSErr err; + TCPiopb statusPB; + int toRead, dataAvail; + + *errorCodePtr = 0; + errno = 0; + tcpStream = statePtr->tcpStream; + + if (bufSize == 0) { + return 0; + } + toRead = bufSize; + + /* + * First check to see if EOF was already detected, to prevent + * calling the socket stack after the first time EOF is detected. + */ + + if (statePtr->flags & TCP_REMOTE_CLOSED) { + return 0; + } + + /* + * If an asynchronous connect is in progress, attempt to wait for it + * to complete before reading. + */ + + if ((statePtr->flags & TCP_ASYNC_CONNECT) + && ! WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) { + return -1; + } + + /* + * No EOF, and it is connected, so try to read more from the socket. + * If the socket is blocking, we keep trying until there is data + * available or the socket is closed. + */ + + while (1) { + + statusPB.ioCRefNum = driverRefNum; + statusPB.tcpStream = tcpStream; + statusPB.csCode = TCPStatus; + err = PBControlSync((ParmBlkPtr) &statusPB); + if (err != noErr) { + Debugger(); + statePtr->flags |= TCP_REMOTE_CLOSED; + return 0; /* EOF */ + } + dataAvail = statusPB.csParam.status.amtUnreadData; + if (dataAvail < bufSize) { + toRead = dataAvail; + } else { + toRead = bufSize; + } + if (toRead != 0) { + /* + * Try to read the data. + */ + + InitMacTCPParamBlock(&statusPB, TCPRcv); + statusPB.tcpStream = tcpStream; + statusPB.csParam.receive.rcvBuff = buf; + statusPB.csParam.receive.rcvBuffLen = toRead; + err = PBControlSync((ParmBlkPtr) &statusPB); + + statePtr->checkMask &= ~TCL_READABLE; + switch (err) { + case noErr: + /* + * The channel remains readable only if this read succeds + * and we had more data then the size of the buffer we were + * trying to fill. Use the info from the call to status to + * determine this. + */ + + if (dataAvail > bufSize) { + statePtr->checkMask |= TCL_READABLE; + } + return statusPB.csParam.receive.rcvBuffLen; + case connectionClosing: + *errorCodePtr = errno = ESHUTDOWN; + statePtr->flags |= TCP_REMOTE_CLOSED; + return 0; + case connectionDoesntExist: + case connectionTerminated: + *errorCodePtr = errno = ENOTCONN; + statePtr->flags |= TCP_REMOTE_CLOSED; + return 0; + case invalidStreamPtr: + default: + *errorCodePtr = EINVAL; + return -1; + } + } + + /* + * No data is available, so check the connection state to + * see why this is the case. + */ + + if (statusPB.csParam.status.connectionState == 14) { + statePtr->flags |= TCP_REMOTE_CLOSED; + return 0; + } + if (statusPB.csParam.status.connectionState != 8) { + Debugger(); + } + statePtr->checkMask &= ~TCL_READABLE; + if (statePtr->flags & TCP_ASYNC_SOCKET) { + *errorCodePtr = EWOULDBLOCK; + return -1; + } + + /* + * In the blocking case, wait until the file becomes readable + * or closed and try again. + */ + + if (!WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) { + return -1; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TcpGetHandle -- + * + * Called from Tcl_GetChannelHandle to retrieve handles from inside + * a file based channel. + * + * Results: + * The appropriate handle or NULL if not present. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +TcpGetHandle( + ClientData instanceData, /* The file state. */ + int direction, /* Which handle to retrieve? */ + ClientData *handlePtr) +{ + TcpState *statePtr = (TcpState *) instanceData; + + *handlePtr = (ClientData) statePtr->tcpStream; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TcpOutput-- + * + * Writes the given output on the IO channel. Returns count of how + * many characters were actually written, and an error indication. + * + * Results: + * A count of how many characters were written is returned and an + * error indication is returned in an output argument. + * + * Side effects: + * Writes output on the actual channel. + * + *---------------------------------------------------------------------- + */ + +static int +TcpOutput( + ClientData instanceData, /* Channel state. */ + CONST char *buf, /* The data buffer. */ + int toWrite, /* How many bytes to write? */ + int *errorCodePtr) /* Where to store error code. */ +{ + TcpState *statePtr = (TcpState *) instanceData; + StreamPtr tcpStream; + OSErr err; + int amount; + TCPiopb statusPB; + + *errorCodePtr = 0; + tcpStream = statePtr->tcpStream; + + /* + * If an asynchronous connect is in progress, attempt to wait for it + * to complete before writing. + */ + + if ((statePtr->flags & TCP_ASYNC_CONNECT) + && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) { + return -1; + } + + /* + * Loop until we have written some data, or an error occurs. + */ + + while (1) { + statusPB.ioCRefNum = driverRefNum; + statusPB.tcpStream = tcpStream; + statusPB.csCode = TCPStatus; + err = PBControlSync((ParmBlkPtr) &statusPB); + if ((err == connectionDoesntExist) || ((err == noErr) && + (statusPB.csParam.status.connectionState == 14))) { + /* + * The remote connection is gone away. Report an error + * and don't write anything. + */ + + *errorCodePtr = errno = EPIPE; + return -1; + } else if (err != noErr) { + return -1; + } + amount = statusPB.csParam.status.sendWindow + - statusPB.csParam.status.amtUnackedData; + + /* + * Attempt to write the data to the socket if a background + * write isn't in progress and there is room in the output buffers. + */ + + if (!(statePtr->flags & TCP_WRITING) && amount > 0) { + if (toWrite < amount) { + amount = toWrite; + } + + /* We need to copy the data, otherwise the caller may overwrite + * the buffer in the middle of our asynchronous call + */ + + if (amount > statePtr->writeBufferSize) { + /* + * need to grow write buffer + */ + + if (statePtr->writeBuffer != (void *) NULL) { + ckfree(statePtr->writeBuffer); + } + statePtr->writeBuffer = (void *) ckalloc(amount); + statePtr->writeBufferSize = amount; + } + memcpy(statePtr->writeBuffer, buf, amount); + statePtr->dataSegment[0].ptr = statePtr->writeBuffer; + + statePtr->dataSegment[0].length = amount; + statePtr->dataSegment[1].length = 0; + InitMacTCPParamBlock(&statePtr->pb, TCPSend); + statePtr->pb.ioCompletion = completeUPP; + statePtr->pb.tcpStream = tcpStream; + statePtr->pb.csParam.send.wdsPtr = (Ptr) statePtr->dataSegment; + statePtr->pb.csParam.send.pushFlag = 1; + statePtr->pb.csParam.send.userDataPtr = (Ptr) statePtr; + statePtr->flags |= TCP_WRITING; + err = PBControlAsync((ParmBlkPtr) &(statePtr->pb)); + switch (err) { + case noErr: + return amount; + case connectionClosing: + *errorCodePtr = errno = ESHUTDOWN; + statePtr->flags |= TCP_REMOTE_CLOSED; + return -1; + case connectionDoesntExist: + case connectionTerminated: + *errorCodePtr = errno = ENOTCONN; + statePtr->flags |= TCP_REMOTE_CLOSED; + return -1; + case invalidStreamPtr: + default: + return -1; + } + + } + + /* + * The socket wasn't writable. In the non-blocking case, return + * immediately, otherwise wait until the file becomes writable + * or closed and try again. + */ + + if (statePtr->flags & TCP_ASYNC_SOCKET) { + statePtr->checkMask &= ~TCL_WRITABLE; + *errorCodePtr = EWOULDBLOCK; + return -1; + } else if (!WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) { + return -1; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TcpGetOptionProc -- + * + * Computes an option value for a TCP socket based channel, or a + * list of all options and their values. + * + * Note: This code is based on code contributed by John Haxby. + * + * Results: + * A standard Tcl result. The value of the specified option or a + * list of all options and their values is returned in the + * supplied DString. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +TcpGetOptionProc( + ClientData instanceData, /* Socket state. */ + Tcl_Interp *interp, /* For error reporting - can be NULL.*/ + CONST char *optionName, /* Name of the option to + * retrieve the value for, or + * NULL to get all options and + * their values. */ + Tcl_DString *dsPtr) /* Where to store the computed + * value; initialized by caller. */ +{ + TcpState *statePtr = (TcpState *) instanceData; + int doPeerName = false, doSockName = false, doError = false, doAll = false; + ip_addr tcpAddress; + char buffer[128]; + OSErr err; + Tcl_DString dString; + TCPiopb statusPB; + int errorCode; + size_t len = 0; + + /* + * If an asynchronous connect is in progress, attempt to wait for it + * to complete before accessing the socket state. + */ + + if ((statePtr->flags & TCP_ASYNC_CONNECT) + && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, &errorCode)) { + if (interp) { + /* + * fix the error message. + */ + + Tcl_AppendResult(interp, "connect is in progress and can't wait", + NULL); + } + return TCL_ERROR; + } + + /* + * Determine which options we need to do. Do all of them + * if optionName is NULL. + */ + + if (optionName == (CONST char *) NULL || optionName[0] == '\0') { + doAll = true; + } else { + len = strlen(optionName); + if (!strncmp(optionName, "-peername", len)) { + doPeerName = true; + } else if (!strncmp(optionName, "-sockname", len)) { + doSockName = true; + } else if (!strncmp(optionName, "-error", len)) { + /* SF Bug #483575 */ + doError = true; + } else { + return Tcl_BadChannelOption(interp, optionName, + "error peername sockname"); + } + } + + /* + * SF Bug #483575 + * + * Return error information. Currently we ignore + * this option. IOW, we always return the empty + * string, signaling 'no error'. + * + * FIXME: Get a mac/socket expert to write a correct + * FIXME: implementation. + */ + + if (doAll || doError) { + if (doAll) { + Tcl_DStringAppendElement(dsPtr, "-error"); + Tcl_DStringAppendElement(dsPtr, ""); + } else { + Tcl_DStringAppend (dsPtr, "", -1); + return TCL_OK; + } + } + + /* + * Get status on the stream. Make sure to use a new pb struct because + * the struct in the statePtr may be part of an asyncronous call. + */ + + statusPB.ioCRefNum = driverRefNum; + statusPB.tcpStream = statePtr->tcpStream; + statusPB.csCode = TCPStatus; + err = PBControlSync((ParmBlkPtr) &statusPB); + if ((err == connectionDoesntExist) || + ((err == noErr) && (statusPB.csParam.status.connectionState == 14))) { + /* + * The socket was probably closed on the other side of the connection. + */ + + if (interp) { + Tcl_AppendResult(interp, "can't access socket info: ", + "connection reset by peer", NULL); + } + return TCL_ERROR; + } else if (err != noErr) { + if (interp) { + Tcl_AppendResult(interp, "unknown socket error", NULL); + } + Debugger(); + return TCL_ERROR; + } + + + /* + * Get the sockname for the socket. + */ + + Tcl_DStringInit(&dString); + if (doAll || doSockName) { + if (doAll) { + Tcl_DStringAppendElement(dsPtr, "-sockname"); + Tcl_DStringStartSublist(dsPtr); + } + tcpAddress = statusPB.csParam.status.localHost; + sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24, + tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff, + tcpAddress & 0xff); + Tcl_DStringAppendElement(dsPtr, buffer); + if (ResolveAddress(tcpAddress, &dString) == noErr) { + Tcl_DStringAppendElement(dsPtr, dString.string); + } else { + Tcl_DStringAppendElement(dsPtr, "<unknown>"); + } + sprintf(buffer, "%d", statusPB.csParam.status.localPort); + Tcl_DStringAppendElement(dsPtr, buffer); + if (doAll) { + Tcl_DStringEndSublist(dsPtr); + } + } + + /* + * Get the peername for the socket. + */ + + if ((doAll || doPeerName) && (statePtr->flags & TCP_CONNECTED)) { + if (doAll) { + Tcl_DStringAppendElement(dsPtr, "-peername"); + Tcl_DStringStartSublist(dsPtr); + } + tcpAddress = statusPB.csParam.status.remoteHost; + sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24, + tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff, + tcpAddress & 0xff); + Tcl_DStringAppendElement(dsPtr, buffer); + Tcl_DStringSetLength(&dString, 0); + if (ResolveAddress(tcpAddress, &dString) == noErr) { + Tcl_DStringAppendElement(dsPtr, dString.string); + } else { + Tcl_DStringAppendElement(dsPtr, "<unknown>"); + } + sprintf(buffer, "%d", statusPB.csParam.status.remotePort); + Tcl_DStringAppendElement(dsPtr, buffer); + if (doAll) { + Tcl_DStringEndSublist(dsPtr); + } + } + + Tcl_DStringFree(&dString); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TcpWatch -- + * + * Initialize the notifier to watch this channel. + * + * Results: + * None. + * + * Side effects: + * Sets the watchMask for the channel. + * + *---------------------------------------------------------------------- + */ + +static void +TcpWatch(instanceData, mask) + ClientData instanceData; /* The file state. */ + int mask; /* Events of interest; an OR-ed + * combination of TCL_READABLE, + * TCL_WRITABLE and TCL_EXCEPTION. */ +{ + TcpState *statePtr = (TcpState *) instanceData; + + statePtr->watchMask = mask; +} + +/* + *---------------------------------------------------------------------- + * + * NewSocketInfo -- + * + * This function allocates and initializes a new SocketInfo + * structure. + * + * Results: + * Returns a newly allocated SocketInfo. + * + * Side effects: + * Adds the socket to the global socket list, allocates memory. + * + *---------------------------------------------------------------------- + */ + +static TcpState * +NewSocketInfo( + StreamPtr tcpStream) +{ + TcpState *statePtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState)); + statePtr->tcpStream = tcpStream; + statePtr->psn = applicationPSN; + statePtr->flags = 0; + statePtr->checkMask = 0; + statePtr->watchMask = 0; + statePtr->acceptProc = (Tcl_TcpAcceptProc *) NULL; + statePtr->acceptProcData = (ClientData) NULL; + statePtr->writeBuffer = (void *) NULL; + statePtr->writeBufferSize = 0; + statePtr->nextPtr = tsdPtr->socketList; + tsdPtr->socketList = statePtr; + return statePtr; +} + +/* + *---------------------------------------------------------------------- + * + * FreeSocketInfo -- + * + * This function deallocates a SocketInfo structure that is no + * longer needed. + * + * Results: + * None. + * + * Side effects: + * Removes the socket from the global socket list, frees memory. + * + *---------------------------------------------------------------------- + */ + +static void +FreeSocketInfo( + TcpState *statePtr) /* The state pointer to free. */ +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (statePtr == tsdPtr->socketList) { + tsdPtr->socketList = statePtr->nextPtr; + } else { + TcpState *p; + for (p = tsdPtr->socketList; p != NULL; p = p->nextPtr) { + if (p->nextPtr == statePtr) { + p->nextPtr = statePtr->nextPtr; + break; + } + } + } + + if (statePtr->writeBuffer != (void *) NULL) { + ckfree(statePtr->writeBuffer); + } + + ckfree((char *) statePtr); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_MakeTcpClientChannel -- + * + * Creates a Tcl_Channel from an existing client TCP socket. + * + * Results: + * The Tcl_Channel wrapped around the preexisting TCP socket. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Channel +Tcl_MakeTcpClientChannel( + ClientData sock) /* The socket to wrap up into a channel. */ +{ + TcpState *statePtr; + char channelName[20]; + + if (TclpHasSockets(NULL) != TCL_OK) { + return NULL; + } + + statePtr = NewSocketInfo((StreamPtr) sock); + /* TODO: do we need to set the port??? */ + + sprintf(channelName, "sock%d", socketNumber++); + + statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, + (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE)); + Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize); + Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf"); + return statePtr->channel; +} + +/* + *---------------------------------------------------------------------- + * + * CreateSocket -- + * + * This function opens a new socket and initializes the + * SocketInfo structure. + * + * Results: + * Returns a new SocketInfo, or NULL with an error in interp. + * + * Side effects: + * Adds a new socket to the socketList. + * + *---------------------------------------------------------------------- + */ + +static TcpState * +CreateSocket( + Tcl_Interp *interp, /* For error reporting; can be NULL. */ + int port, /* Port number to open. */ + CONST char *host, /* Name of host on which to open port. */ + CONST char *myaddr, /* Optional client-side address */ + int myport, /* Optional client-side port */ + int server, /* 1 if socket should be a server socket, + * else 0 for a client socket. */ + int async) /* 1 create async, 0 do sync. */ +{ + ip_addr macAddr; + OSErr err; + TCPiopb pb; + StreamPtr tcpStream; + TcpState *statePtr; + char * buffer; + + /* + * Figure out the ip address from the host string. + */ + + if (host == NULL) { + err = GetLocalAddress(&macAddr); + } else { + err = GetHostFromString(host, &macAddr); + } + if (err != noErr) { + Tcl_SetErrno(EHOSTUNREACH); + if (interp != (Tcl_Interp *) NULL) { + Tcl_AppendResult(interp, "couldn't open socket: ", + Tcl_PosixError(interp), (char *) NULL); + } + return (TcpState *) NULL; + } + + /* + * Create a MacTCP stream and create the state used for socket + * transactions from here on out. + */ + + ClearZombieSockets(); + buffer = ckalloc(socketBufferSize); + InitMacTCPParamBlock(&pb, TCPCreate); + pb.csParam.create.rcvBuff = buffer; + pb.csParam.create.rcvBuffLen = socketBufferSize; + pb.csParam.create.notifyProc = nil /* notifyUPP */; + err = PBControlSync((ParmBlkPtr) &pb); + if (err != noErr) { + Tcl_SetErrno(0); /* TODO: set to ENOSR - maybe?*/ + if (interp != (Tcl_Interp *) NULL) { + Tcl_AppendResult(interp, "couldn't open socket: ", + Tcl_PosixError(interp), (char *) NULL); + } + return (TcpState *) NULL; + } + + tcpStream = pb.tcpStream; + statePtr = NewSocketInfo(tcpStream); + statePtr->port = port; + + if (server) { + /* + * Set up server connection. + */ + + InitMacTCPParamBlock(&statePtr->pb, TCPPassiveOpen); + statePtr->pb.tcpStream = tcpStream; + statePtr->pb.csParam.open.localPort = statePtr->port; + statePtr->pb.ioCompletion = completeUPP; + statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr; + statePtr->pb.csParam.open.ulpTimeoutValue = 100; + statePtr->pb.csParam.open.ulpTimeoutAction = 1 /* 1:abort 0:report */; + statePtr->pb.csParam.open.commandTimeoutValue = 0 /* infinity */; + + statePtr->flags |= TCP_LISTENING; + err = PBControlAsync((ParmBlkPtr) &(statePtr->pb)); + + /* + * If this is a server on port 0 then we need to wait until + * the dynamic port allocation is made by the MacTcp driver. + */ + + if (statePtr->port == 0) { + EventRecord dummy; + + while (statePtr->pb.csParam.open.localPort == 0) { + WaitNextEvent(0, &dummy, 1, NULL); + if (statePtr->pb.ioResult != 0) { + break; + } + } + statePtr->port = statePtr->pb.csParam.open.localPort; + } + Tcl_SetErrno(EINPROGRESS); + } else { + /* + * Attempt to connect. The connect may fail at present with an + * EINPROGRESS but at a later time it will complete. The caller + * will set up a file handler on the socket if she is interested in + * being informed when the connect completes. + */ + + InitMacTCPParamBlock(&statePtr->pb, TCPActiveOpen); + + statePtr->pb.tcpStream = tcpStream; + statePtr->pb.csParam.open.remoteHost = macAddr; + statePtr->pb.csParam.open.remotePort = port; + statePtr->pb.csParam.open.localHost = 0; + statePtr->pb.csParam.open.localPort = myport; + statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr; + statePtr->pb.csParam.open.validityFlags = timeoutValue | timeoutAction; + statePtr->pb.csParam.open.ulpTimeoutValue = 60 /* seconds */; + statePtr->pb.csParam.open.ulpTimeoutAction = 1 /* 1:abort 0:report */; + statePtr->pb.csParam.open.commandTimeoutValue = 0; + + statePtr->pb.ioCompletion = completeUPP; + if (async) { + statePtr->flags |= TCP_ASYNC_CONNECT; + err = PBControlAsync((ParmBlkPtr) &(statePtr->pb)); + Tcl_SetErrno(EINPROGRESS); + } else { + err = PBControlSync((ParmBlkPtr) &(statePtr->pb)); + } + } + + switch (err) { + case noErr: + if (!async) { + statePtr->flags |= TCP_CONNECTED; + } + return statePtr; + case duplicateSocket: + Tcl_SetErrno(EADDRINUSE); + break; + case openFailed: + case connectionTerminated: + Tcl_SetErrno(ECONNREFUSED); + break; + case invalidStreamPtr: + case connectionExists: + default: + /* + * These cases should never occur. However, we will fail + * gracefully and hope Tcl can resume. The alternative is to panic + * which is probably a bit drastic. + */ + + Debugger(); + Tcl_SetErrno(err); + } + + /* + * We had error during the connection. Release the stream + * and file handle. Also report to the interp. + */ + + pb.ioCRefNum = driverRefNum; + pb.csCode = TCPRelease; + pb.tcpStream = tcpStream; + pb.ioCompletion = NULL; + err = PBControlSync((ParmBlkPtr) &pb); + + if (interp != (Tcl_Interp *) NULL) { + Tcl_AppendResult(interp, "couldn't open socket: ", + Tcl_PosixError(interp), (char *) NULL); + } + + ckfree(buffer); + FreeSocketInfo(statePtr); + return (TcpState *) NULL; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_OpenTcpClient -- + * + * Opens a TCP client socket and creates a channel around it. + * + * Results: + * The channel or NULL if failed. On failure, the routine also + * sets the output argument errorCodePtr to the error code. + * + * Side effects: + * Opens a client socket and creates a new channel. + * + *---------------------------------------------------------------------- + */ + +Tcl_Channel +Tcl_OpenTcpClient( + Tcl_Interp *interp, /* For error reporting; can be NULL. */ + int port, /* Port number to open. */ + CONST char *host, /* Host on which to open port. */ + CONST char *myaddr, /* Client-side address */ + int myport, /* Client-side port */ + int async) /* If nonzero, attempt to do an + * asynchronous connect. Otherwise + * we do a blocking connect. + * - currently ignored */ +{ + TcpState *statePtr; + char channelName[20]; + + if (TclpHasSockets(interp) != TCL_OK) { + return NULL; + } + + /* + * Create a new client socket and wrap it in a channel. + */ + + statePtr = CreateSocket(interp, port, host, myaddr, myport, 0, async); + if (statePtr == NULL) { + return NULL; + } + + sprintf(channelName, "sock%d", socketNumber++); + + statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, + (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE)); + Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize); + Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf"); + return statePtr->channel; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_OpenTcpServer -- + * + * Opens a TCP server socket and creates a channel around it. + * + * Results: + * The channel or NULL if failed. + * + * Side effects: + * Opens a server socket and creates a new channel. + * + *---------------------------------------------------------------------- + */ + +Tcl_Channel +Tcl_OpenTcpServer( + Tcl_Interp *interp, /* For error reporting - may be + * NULL. */ + int port, /* Port number to open. */ + CONST char *host, /* Name of local host. */ + Tcl_TcpAcceptProc *acceptProc, /* Callback for accepting connections + * from new clients. */ + ClientData acceptProcData) /* Data for the callback. */ +{ + TcpState *statePtr; + char channelName[20]; + + if (TclpHasSockets(interp) != TCL_OK) { + return NULL; + } + + /* + * Create a new client socket and wrap it in a channel. + */ + + statePtr = CreateSocket(interp, port, host, NULL, 0, 1, 1); + if (statePtr == NULL) { + return NULL; + } + + statePtr->acceptProc = acceptProc; + statePtr->acceptProcData = acceptProcData; + + sprintf(channelName, "sock%d", socketNumber++); + + statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, + (ClientData) statePtr, 0); + Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize); + Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf"); + return statePtr->channel; +} + +/* + *---------------------------------------------------------------------- + * + * SocketEventProc -- + * + * This procedure is called by Tcl_ServiceEvent when a socket event + * reaches the front of the event queue. This procedure is + * responsible for notifying the generic channel code. + * + * Results: + * Returns 1 if the event was handled, meaning it should be removed + * from the queue. Returns 0 if the event was not handled, meaning + * it should stay on the queue. The only time the event isn't + * handled is if the TCL_FILE_EVENTS flag bit isn't set. + * + * Side effects: + * Whatever the channel callback procedures do. + * + *---------------------------------------------------------------------- + */ + +static int +SocketEventProc( + Tcl_Event *evPtr, /* Event to service. */ + int flags) /* Flags that indicate what events to + * handle, such as TCL_FILE_EVENTS. */ +{ + TcpState *statePtr; + SocketEvent *eventPtr = (SocketEvent *) evPtr; + int mask = 0; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (!(flags & TCL_FILE_EVENTS)) { + return 0; + } + + /* + * Find the specified socket on the socket list. + */ + + for (statePtr = tsdPtr->socketList; statePtr != NULL; + statePtr = statePtr->nextPtr) { + if ((statePtr == eventPtr->statePtr) && + (statePtr->tcpStream == eventPtr->tcpStream)) { + break; + } + } + + /* + * Discard events that have gone stale. + */ + + if (!statePtr) { + return 1; + } + statePtr->flags &= ~(TCP_PENDING); + if (statePtr->flags & TCP_RELEASE) { + SocketFreeProc(statePtr); + return 1; + } + + + /* + * Handle connection requests directly. + */ + + if (statePtr->flags & TCP_LISTEN_CONNECT) { + if (statePtr->checkMask & TCL_READABLE) { + TcpAccept(statePtr); + } + return 1; + } + + /* + * Mask off unwanted events then notify the channel. + */ + + mask = statePtr->checkMask & statePtr->watchMask; + if (mask) { + Tcl_NotifyChannel(statePtr->channel, mask); + } + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * WaitForSocketEvent -- + * + * Waits until one of the specified events occurs on a socket. + * + * Results: + * Returns 1 on success or 0 on failure, with an error code in + * errorCodePtr. + * + * Side effects: + * Processes socket events off the system queue. + * + *---------------------------------------------------------------------- + */ + +static int +WaitForSocketEvent( + TcpState *statePtr, /* Information about this socket. */ + int mask, /* Events to look for. */ + int *errorCodePtr) /* Where to store errors? */ +{ + OSErr err; + TCPiopb statusPB; + EventRecord dummy; + + /* + * Loop until we get the specified condition, unless the socket is + * asynchronous. + */ + + do { + statusPB.ioCRefNum = driverRefNum; + statusPB.tcpStream = statePtr->tcpStream; + statusPB.csCode = TCPStatus; + err = PBControlSync((ParmBlkPtr) &statusPB); + if (err != noErr) { + /* + * I am not sure why it is right to return 1 - indicating success + * for synchronous sockets when an attempt to get status on the + * driver yeilds an error. But it is CERTAINLY wrong for async + * sockect which have not yet connected. + */ + + if (statePtr->flags & TCP_ASYNC_CONNECT) { + *errorCodePtr = EWOULDBLOCK; + return 0; + } else { + statePtr->checkMask |= (TCL_READABLE | TCL_WRITABLE); + return 1; + } + } + statePtr->checkMask = 0; + + /* + * The "6" below is the "connection being established" flag. I couldn't + * find a define for this in MacTCP.h, but that's what the programmer's + * guide says. + */ + + if ((statusPB.csParam.status.connectionState != 0) + && (statusPB.csParam.status.connectionState != 4) + && (statusPB.csParam.status.connectionState != 6)) { + if (statusPB.csParam.status.amtUnreadData > 0) { + statePtr->checkMask |= TCL_READABLE; + } + if (!(statePtr->flags & TCP_WRITING) + && (statusPB.csParam.status.sendWindow - + statusPB.csParam.status.amtUnackedData) > 0) { + statePtr->flags &= ~(TCP_ASYNC_CONNECT); + statePtr->checkMask |= TCL_WRITABLE; + } + if (mask & statePtr->checkMask) { + return 1; + } + } else { + break; + } + + /* + * Call the system to let other applications run while we + * are waiting for this event to occur. + */ + + WaitNextEvent(0, &dummy, 1, NULL); + } while (!(statePtr->flags & TCP_ASYNC_SOCKET)); + *errorCodePtr = EWOULDBLOCK; + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * TcpAccept -- + * Accept a TCP socket connection. This is called by the event + * loop, and it in turns calls any registered callbacks for this + * channel. + * + * Results: + * None. + * + * Side effects: + * Evals the Tcl script associated with the server socket. + * + *---------------------------------------------------------------------- + */ + +static void +TcpAccept( + TcpState *statePtr) +{ + TcpState *newStatePtr; + StreamPtr tcpStream; + char remoteHostname[255]; + OSErr err; + ip_addr remoteAddress; + long remotePort; + char channelName[20]; + + statePtr->flags &= ~TCP_LISTEN_CONNECT; + statePtr->checkMask &= ~TCL_READABLE; + + /* + * Transfer sever stream to new connection. + */ + + tcpStream = statePtr->tcpStream; + newStatePtr = NewSocketInfo(tcpStream); + newStatePtr->tcpStream = tcpStream; + sprintf(channelName, "sock%d", socketNumber++); + + + newStatePtr->flags |= TCP_CONNECTED; + newStatePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, + (ClientData) newStatePtr, (TCL_READABLE | TCL_WRITABLE)); + Tcl_SetChannelBufferSize(newStatePtr->channel, socketBufferSize); + Tcl_SetChannelOption(NULL, newStatePtr->channel, "-translation", + "auto crlf"); + + remoteAddress = statePtr->pb.csParam.open.remoteHost; + remotePort = statePtr->pb.csParam.open.remotePort; + + /* + * Reopen passive connect. Make new tcpStream the server. + */ + + ClearZombieSockets(); + InitMacTCPParamBlock(&statePtr->pb, TCPCreate); + statePtr->pb.csParam.create.rcvBuff = ckalloc(socketBufferSize); + statePtr->pb.csParam.create.rcvBuffLen = socketBufferSize; + err = PBControlSync((ParmBlkPtr) &statePtr->pb); + if (err != noErr) { + /* + * Hmmm... We can't reopen the server. We'll go ahead + * an continue - but we are kind of broken now... + */ + Debugger(); + statePtr->tcpStream = -1; + statePtr->flags |= TCP_SERVER_ZOMBIE; + } + + tcpStream = statePtr->tcpStream = statePtr->pb.tcpStream; + + InitMacTCPParamBlock(&statePtr->pb, TCPPassiveOpen); + statePtr->pb.tcpStream = tcpStream; + statePtr->pb.csParam.open.localHost = 0; + statePtr->pb.csParam.open.localPort = statePtr->port; + statePtr->pb.ioCompletion = completeUPP; + statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr; + statePtr->flags |= TCP_LISTENING; + err = PBControlAsync((ParmBlkPtr) &(statePtr->pb)); + /* + * TODO: deal with case where we can't recreate server socket... + */ + + /* + * Finally we run the accept procedure. We must do this last to make + * sure we are in a nice clean state. This Tcl code can do anything + * including closing the server or client sockets we've just delt with. + */ + + if (statePtr->acceptProc != NULL) { + sprintf(remoteHostname, "%d.%d.%d.%d", remoteAddress>>24, + remoteAddress>>16 & 0xff, remoteAddress>>8 & 0xff, + remoteAddress & 0xff); + + (statePtr->acceptProc)(statePtr->acceptProcData, newStatePtr->channel, + remoteHostname, remotePort); + } +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_GetHostName -- + * + * Returns the name of the local host. + * + * Results: + * A string containing the network name for this machine, or + * an empty string if we can't figure out the name. The caller + * must not modify or free this string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +CONST char * +Tcl_GetHostName() +{ + static int hostnameInited = 0; + static char hostname[255]; + ip_addr ourAddress; + Tcl_DString dString; + OSErr err; + + if (hostnameInited) { + return hostname; + } + + if (TclpHasSockets(NULL) == TCL_OK) { + err = GetLocalAddress(&ourAddress); + if (err == noErr) { + /* + * Search for the doman name and return it if found. Otherwise, + * just print the IP number to a string and return that. + */ + + Tcl_DStringInit(&dString); + err = ResolveAddress(ourAddress, &dString); + if (err == noErr) { + strcpy(hostname, dString.string); + } else { + sprintf(hostname, "%d.%d.%d.%d", ourAddress>>24, ourAddress>>16 & 0xff, + ourAddress>>8 & 0xff, ourAddress & 0xff); + } + Tcl_DStringFree(&dString); + + hostnameInited = 1; + return hostname; + } + } + + hostname[0] = '\0'; + hostnameInited = 1; + return hostname; +} + +/* + *---------------------------------------------------------------------- + * + * ResolveAddress -- + * + * This function is used to resolve an ip address to it's full + * domain name address. + * + * Results: + * An os err value. + * + * Side effects: + * Treats client data as int we set to true. + * + *---------------------------------------------------------------------- + */ + +static OSErr +ResolveAddress( + ip_addr tcpAddress, /* Address to resolve. */ + Tcl_DString *dsPtr) /* Returned address in string. */ +{ + int i; + EventRecord dummy; + DNRState dnrState; + OSErr err; + + /* + * Call AddrToName to resolve our ip address to our domain name. + * The call is async, so we must wait for a callback to tell us + * when to continue. + */ + + for (i = 0; i < NUM_ALT_ADDRS; i++) { + dnrState.hostInfo.addr[i] = 0; + } + dnrState.done = 0; + GetCurrentProcess(&(dnrState.psn)); + err = AddrToName(tcpAddress, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState); + if (err == cacheFault) { + while (!dnrState.done) { + WaitNextEvent(0, &dummy, 1, NULL); + } + } + + /* + * If there is no error in finding the domain name we set the + * result into the dynamic string. We also work around a bug in + * MacTcp where an extranious '.' may be found at the end of the name. + */ + + if (dnrState.hostInfo.rtnCode == noErr) { + i = strlen(dnrState.hostInfo.cname) - 1; + if (dnrState.hostInfo.cname[i] == '.') { + dnrState.hostInfo.cname[i] = '\0'; + } + Tcl_DStringAppend(dsPtr, dnrState.hostInfo.cname, -1); + } + + return dnrState.hostInfo.rtnCode; +} + +/* + *---------------------------------------------------------------------- + * + * DNRCompletionRoutine -- + * + * This function is called when the Domain Name Server is done + * seviceing our request. It just sets a flag that we can poll + * in functions like Tcl_GetHostName to let them know to continue. + * + * Results: + * None. + * + * Side effects: + * Treats client data as int we set to true. + * + *---------------------------------------------------------------------- + */ + +static pascal void +DNRCompletionRoutine( + struct hostInfo *hostinfoPtr, /* Host infor struct. */ + DNRState *dnrStatePtr) /* Completetion state. */ +{ + dnrStatePtr->done = true; + WakeUpProcess(&(dnrStatePtr->psn)); +} + +/* + *---------------------------------------------------------------------- + * + * CleanUpExitProc -- + * + * This procedure is invoked as an exit handler when ExitToShell + * is called. It aborts any lingering socket connections. This + * must be called or the Mac OS will more than likely crash. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static pascal void +CleanUpExitProc() +{ + TCPiopb exitPB; + TcpState *statePtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + while (tsdPtr->socketList != NULL) { + statePtr = tsdPtr->socketList; + tsdPtr->socketList = statePtr->nextPtr; + + /* + * Close and Release the connection. + */ + + exitPB.ioCRefNum = driverRefNum; + exitPB.csCode = TCPClose; + exitPB.tcpStream = statePtr->tcpStream; + exitPB.csParam.close.ulpTimeoutValue = 60 /* seconds */; + exitPB.csParam.close.ulpTimeoutAction = 1 /* 1:abort 0:report */; + exitPB.csParam.close.validityFlags = timeoutValue | timeoutAction; + exitPB.ioCompletion = NULL; + PBControlSync((ParmBlkPtr) &exitPB); + + exitPB.ioCRefNum = driverRefNum; + exitPB.csCode = TCPRelease; + exitPB.tcpStream = statePtr->tcpStream; + exitPB.ioCompletion = NULL; + PBControlSync((ParmBlkPtr) &exitPB); + } +} + +/* + *---------------------------------------------------------------------- + * + * GetHostFromString -- + * + * Looks up the passed in domain name in the domain resolver. It + * can accept strings of two types: 1) the ip number in string + * format, or 2) the domain name. + * + * Results: + * We return a ip address or 0 if there was an error or the + * domain does not exist. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static OSErr +GetHostFromString( + CONST char *name, /* Host in string form. */ + ip_addr *address) /* Returned IP address. */ +{ + OSErr err; + int i; + EventRecord dummy; + DNRState dnrState; + + if (TclpHasSockets(NULL) != TCL_OK) { + return 0; + } + + /* + * Call StrToAddr to get the ip number for the passed in domain + * name. The call is async, so we must wait for a callback to + * tell us when to continue. + */ + + for (i = 0; i < NUM_ALT_ADDRS; i++) { + dnrState.hostInfo.addr[i] = 0; + } + dnrState.done = 0; + GetCurrentProcess(&(dnrState.psn)); + err = StrToAddr((char*)name, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState); + if (err == cacheFault) { + while (!dnrState.done) { + WaitNextEvent(0, &dummy, 1, NULL); + } + } + + /* + * For some reason MacTcp may return a cachFault a second time via + * the hostinfo block. This seems to be a bug in MacTcp. In this case + * we run StrToAddr again - which seems to then work just fine. + */ + + if (dnrState.hostInfo.rtnCode == cacheFault) { + dnrState.done = 0; + err = StrToAddr((char*)name, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState); + if (err == cacheFault) { + while (!dnrState.done) { + WaitNextEvent(0, &dummy, 1, NULL); + } + } + } + + if (dnrState.hostInfo.rtnCode == noErr) { + *address = dnrState.hostInfo.addr[0]; + } + + return dnrState.hostInfo.rtnCode; +} + +/* + *---------------------------------------------------------------------- + * + * IOCompletionRoutine -- + * + * This function is called when an asynchronous socket operation + * completes. Since this routine runs as an interrupt handler, + * it will simply set state to tell the notifier that this socket + * is now ready for action. Note that this function is running at + * interupt time and can't allocate memory or do much else except + * set state. + * + * Results: + * None. + * + * Side effects: + * Sets some state in the socket state. May also wake the process + * if we are not currently running. + * + *---------------------------------------------------------------------- + */ + +static void +IOCompletionRoutine( + TCPiopb *pbPtr) /* Tcp parameter block. */ +{ + TcpState *statePtr; + + if (pbPtr->csCode == TCPSend) { + statePtr = (TcpState *) pbPtr->csParam.send.userDataPtr; + } else { + statePtr = (TcpState *) pbPtr->csParam.open.userDataPtr; + } + + /* + * Always wake the process in case it's in WaitNextEvent. + * If an error has a occured - just return. We will deal + * with the problem later. + */ + + WakeUpProcess(&statePtr->psn); + if (pbPtr->ioResult != noErr) { + return; + } + + if (statePtr->flags & TCP_ASYNC_CONNECT) { + statePtr->flags &= ~TCP_ASYNC_CONNECT; + statePtr->flags |= TCP_CONNECTED; + statePtr->checkMask |= TCL_READABLE & TCL_WRITABLE; + } else if (statePtr->flags & TCP_LISTENING) { + if (statePtr->port == 0) { + Debugger(); + } + statePtr->flags &= ~TCP_LISTENING; + statePtr->flags |= TCP_LISTEN_CONNECT; + statePtr->checkMask |= TCL_READABLE; + } else if (statePtr->flags & TCP_WRITING) { + statePtr->flags &= ~TCP_WRITING; + statePtr->checkMask |= TCL_WRITABLE; + if (!(statePtr->flags & TCP_CONNECTED)) { + InitMacTCPParamBlock(&statePtr->pb, TCPClose); + statePtr->pb.tcpStream = statePtr->tcpStream; + statePtr->pb.ioCompletion = closeUPP; + statePtr->pb.csParam.close.userDataPtr = (Ptr) statePtr; + if (PBControlAsync((ParmBlkPtr) &statePtr->pb) != noErr) { + statePtr->flags |= TCP_RELEASE; + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * GetLocalAddress -- + * + * Get the IP address for this machine. The result is cached so + * the result is returned quickly after the first call. + * + * Results: + * Macintosh error code. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static OSErr +GetLocalAddress( + unsigned long *addr) /* Returns host IP address. */ +{ + struct GetAddrParamBlock pBlock; + OSErr err = noErr; + static unsigned long localAddress = 0; + + if (localAddress == 0) { + memset(&pBlock, 0, sizeof(pBlock)); + pBlock.ioResult = 1; + pBlock.csCode = ipctlGetAddr; + pBlock.ioCRefNum = driverRefNum; + err = PBControlSync((ParmBlkPtr) &pBlock); + + if (err != noErr) { + return err; + } + localAddress = pBlock.ourAddress; + } + + *addr = localAddress; + return noErr; +} + +/* + *---------------------------------------------------------------------- + * + * GetBufferSize -- + * + * Get the appropiate buffer size for our machine & network. This + * value will be used by the rest of Tcl & the MacTcp driver for + * the size of its buffers. If out method for determining the + * optimal buffer size fails for any reason - we return a + * reasonable default. + * + * Results: + * Size of optimal buffer in bytes. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static long +GetBufferSize() +{ + UDPiopb iopb; + OSErr err = noErr; + long bufferSize; + + memset(&iopb, 0, sizeof(iopb)); + err = GetLocalAddress(&iopb.csParam.mtu.remoteHost); + if (err != noErr) { + return CHANNEL_BUF_SIZE; + } + iopb.ioCRefNum = driverRefNum; + iopb.csCode = UDPMaxMTUSize; + err = PBControlSync((ParmBlkPtr)&iopb); + if (err != noErr) { + return CHANNEL_BUF_SIZE; + } + bufferSize = (iopb.csParam.mtu.mtuSize * 4) + 1024; + if (bufferSize < CHANNEL_BUF_SIZE) { + bufferSize = CHANNEL_BUF_SIZE; + } + return bufferSize; +} + +/* + *---------------------------------------------------------------------- + * + * TclSockGetPort -- + * + * Maps from a string, which could be a service name, to a port. + * Used by socket creation code to get port numbers and resolve + * registered service names to port numbers. + * + * Results: + * A standard Tcl result. On success, the port number is + * returned in portPtr. On failure, an error message is left in + * the interp's result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclSockGetPort( + Tcl_Interp *interp, /* Interp for error messages. */ + char *string, /* Integer or service name */ + char *proto, /* "tcp" or "udp", typically - + * ignored on Mac - assumed to be tcp */ + int *portPtr) /* Return port number */ +{ + PortInfo *portInfoPtr = NULL; + + if (Tcl_GetInt(interp, string, portPtr) == TCL_OK) { + if (*portPtr > 0xFFFF) { + Tcl_AppendResult(interp, "couldn't open socket: port number too high", + (char *) NULL); + return TCL_ERROR; + } + if (*portPtr < 0) { + Tcl_AppendResult(interp, "couldn't open socket: negative port number", + (char *) NULL); + return TCL_ERROR; + } + return TCL_OK; + } + for (portInfoPtr = portServices; portInfoPtr->name != NULL; portInfoPtr++) { + if (!strcmp(portInfoPtr->name, string)) { + break; + } + } + if (portInfoPtr != NULL && portInfoPtr->name != NULL) { + *portPtr = portInfoPtr->port; + Tcl_ResetResult(interp); + return TCL_OK; + } + + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * ClearZombieSockets -- + * + * This procedure looks through the socket list and removes the + * first stream it finds that is ready for release. This procedure + * should be called before we ever try to create new Tcp streams + * to ensure we can least allocate one stream. + * + * Results: + * None. + * + * Side effects: + * Tcp streams may be released. + * + *---------------------------------------------------------------------- + */ + +static void +ClearZombieSockets() +{ + TcpState *statePtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + for (statePtr = tsdPtr->socketList; statePtr != NULL; + statePtr = statePtr->nextPtr) { + if (statePtr->flags & TCP_RELEASE) { + SocketFreeProc(statePtr); + return; + } + } +} + + +/* + *---------------------------------------------------------------------- + * + * NotifyRoutine -- + * + * This routine does nothing currently, and is not being used. But + * it is useful if you want to experiment with what MacTCP thinks that + * it is doing... + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +pascal void NotifyRoutine ( + StreamPtr tcpStream, + unsigned short eventCode, + Ptr userDataPtr, + unsigned short terminReason, + struct ICMPReport *icmpMsg) +{ + StreamPtr localTcpStream; + unsigned short localEventCode; + unsigned short localTerminReason; + struct ICMPReport localIcmpMsg; + + localTcpStream = tcpStream; + localEventCode = eventCode; + localTerminReason = terminReason; + localIcmpMsg = *icmpMsg; + +} + +/* + *---------------------------------------------------------------------- + * + * TclpCutSockChannel -- + * + * Remove any thread local refs to this channel. See + * Tcl_CutChannel for more info. + * + * Results: + * None. + * + * Side effects: + * Changes thread local list of valid channels. + * + *---------------------------------------------------------------------- + */ + +void +TclpCutSockChannel(chan) + Tcl_Channel chan; +{ + ThreadSpecificData *tsdPtr; + TcpState *infoPtr; + TcpState **nextPtrPtr; + int removed = 0; + + if (Tcl_GetChannelType(chan) != &tcpChannelType) + return; + + tsdPtr = TCL_TSD_INIT(&dataKey); + infoPtr = (TcpState *) Tcl_GetChannelInstanceData (chan); + + for (nextPtrPtr = &(tsdPtr->socketList); (*nextPtrPtr) != NULL; + nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { + if ((*nextPtrPtr) == infoPtr) { + (*nextPtrPtr) = infoPtr->nextPtr; + removed = 1; + break; + } + } + + /* + * This could happen if the channel was created in one thread + * and then moved to another without updating the thread + * local data in each thread. + */ + + if (!removed) + Tcl_Panic("file info ptr not on thread channel list"); + return; +} + +/* + *---------------------------------------------------------------------- + * + * TclpSpliceSockChannel -- + * + * Insert thread local ref for this channel. + * Tcl_SpliceChannel for more info. + * + * Results: + * None. + * + * Side effects: + * Changes thread local list of valid channels. + * + *---------------------------------------------------------------------- + */ + +void +TclpSpliceSockChannel(chan) + Tcl_Channel chan; +{ + ThreadSpecificData *tsdPtr; + TcpState *infoPtr; + + if (Tcl_GetChannelType(chan) != &tcpChannelType) + return; + + InitSockets (); + + tsdPtr = TCL_TSD_INIT(&dataKey); + infoPtr = (TcpState *) Tcl_GetChannelInstanceData (chan); + + infoPtr->nextPtr = tsdPtr->socketList; + tsdPtr->socketList = infoPtr; +} + diff --git a/mac/tclMacTclCode.r b/mac/tclMacTclCode.r new file mode 100644 index 0000000..13b23e2 --- /dev/null +++ b/mac/tclMacTclCode.r @@ -0,0 +1,37 @@ +/* + * tclMacTclCode.r -- + * + * This file creates resources from the Tcl code that is + * usually stored in the TCL_LiBRARY + * + * Copyright (c) 1996-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * SCCS: @(#) tclMacTclCode.r 1.1 98/01/21 22:22:38 + */ + +#include <Types.r> +#include <SysTypes.r> + +#define TCL_LIBRARY_RESOURCES 2000 + +/* + * The mechanisim below loads Tcl source into the resource fork of the + * application. The example below creates a TEXT resource named + * "Init" from the file "init.tcl". This allows applications to use + * Tcl to define the behavior of the application without having to + * require some predetermined file structure - all needed Tcl "files" + * are located within the application. To source a file for the + * resource fork the source command has been modified to support + * sourcing from resources. In the below case "source -rsrc {Init}" + * will load the TEXT resource named "Init". + */ + +read 'TEXT' (TCL_LIBRARY_RESOURCES, "init", purgeable) "::library:init.tcl"; +read 'TEXT' (TCL_LIBRARY_RESOURCES + 1, "auto", purgeable) "::library:auto.tcl"; +read 'TEXT' (TCL_LIBRARY_RESOURCES + 2, "package", purgeable,preload) "::library:package.tcl"; +read 'TEXT' (TCL_LIBRARY_RESOURCES + 3, "history", purgeable) "::library:history.tcl"; +read 'TEXT' (TCL_LIBRARY_RESOURCES + 4, "word", purgeable,preload) "::library:word.tcl"; +read 'TEXT' (TCL_LIBRARY_RESOURCES + 5, "parray", purgeable,preload) "::library:parray.tcl"; diff --git a/mac/tclMacTest.c b/mac/tclMacTest.c new file mode 100644 index 0000000..58154e3 --- /dev/null +++ b/mac/tclMacTest.c @@ -0,0 +1,213 @@ +/* + * tclMacTest.c -- + * + * Contains commands for platform specific tests for + * the Macintosh platform. + * + * Copyright (c) 1996 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacTest.c,v 1.6 2002/10/09 11:54:42 das Exp $ + */ + +#define TCL_TEST +#define USE_COMPAT_CONST +#include "tclInt.h" +#include "tclMacInt.h" +#include "tclMacPort.h" +#include "Files.h" +#include <Errors.h> +#include <Resources.h> +#include <Script.h> +#include <Strings.h> +#include <FSpCompat.h> + +/* + * Forward declarations of procedures defined later in this file: + */ + +int TclplatformtestInit _ANSI_ARGS_((Tcl_Interp *interp)); +static int DebuggerCmd _ANSI_ARGS_((ClientData dummy, + Tcl_Interp *interp, int argc, CONST char **argv)); +static int WriteTextResource _ANSI_ARGS_((ClientData dummy, + Tcl_Interp *interp, int argc, CONST char **argv)); + + +/* + *---------------------------------------------------------------------- + * + * TclplatformtestInit -- + * + * Defines commands that test platform specific functionality for + * Unix platforms. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Defines new commands. + * + *---------------------------------------------------------------------- + */ + +int +TclplatformtestInit( + Tcl_Interp *interp) /* Interpreter to add commands to. */ +{ + /* + * Add commands for platform specific tests on MacOS here. + */ + + Tcl_CreateCommand(interp, "debugger", DebuggerCmd, + (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "testWriteTextResource", WriteTextResource, + (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * DebuggerCmd -- + * + * This procedure simply calls the low level debugger. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +DebuggerCmd( + ClientData clientData, /* Not used. */ + Tcl_Interp *interp, /* Not used. */ + int argc, /* Not used. */ + CONST char **argv) /* Not used. */ +{ + Debugger(); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WriteTextResource -- + * + * This procedure will write a text resource out to the + * application or a given file. The format for this command is + * textwriteresource + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +WriteTextResource( + ClientData clientData, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int argc, /* Number of arguments. */ + CONST char **argv) /* Argument strings. */ +{ + char *errNum = "wrong # args: "; + char *errBad = "bad argument: "; + char *errStr; + CONST char *fileName = NULL, *rsrcName = NULL; + CONST char *data = NULL; + int rsrcID = -1, i, protectIt = 0; + short fileRef = -1; + OSErr err; + Handle dataHandle; + Str255 resourceName; + FSSpec fileSpec; + + /* + * Process the arguments. + */ + for (i = 1 ; i < argc ; i++) { + if (!strcmp(argv[i], "-rsrc")) { + rsrcName = argv[i + 1]; + i++; + } else if (!strcmp(argv[i], "-rsrcid")) { + rsrcID = atoi(argv[i + 1]); + i++; + } else if (!strcmp(argv[i], "-file")) { + fileName = argv[i + 1]; + i++; + } else if (!strcmp(argv[i], "-protected")) { + protectIt = 1; + } else { + data = argv[i]; + } + } + + if ((rsrcName == NULL && rsrcID < 0) || + (fileName == NULL) || (data == NULL)) { + errStr = errBad; + goto sourceFmtErr; + } + + /* + * Open the resource file. + */ + err = FSpLocationFromPath(strlen(fileName), fileName, &fileSpec); + if (!(err == noErr || err == fnfErr)) { + Tcl_AppendResult(interp, "couldn't validate file name", (char *) NULL); + return TCL_ERROR; + } + + if (err == fnfErr) { + FSpCreateResFile(&fileSpec, 'WIsH', 'rsrc', smSystemScript); + } + fileRef = FSpOpenResFile(&fileSpec, fsRdWrPerm); + if (fileRef == -1) { + Tcl_AppendResult(interp, "couldn't open resource file", (char *) NULL); + return TCL_ERROR; + } + + UseResFile(fileRef); + + /* + * Prepare data needed to create resource. + */ + if (rsrcID < 0) { + rsrcID = UniqueID('TEXT'); + } + + strcpy((char *) resourceName, rsrcName); + c2pstr((char *) resourceName); + + dataHandle = NewHandle(strlen(data)); + HLock(dataHandle); + strcpy(*dataHandle, data); + HUnlock(dataHandle); + + /* + * Add the resource to the file and close it. + */ + AddResource(dataHandle, 'TEXT', rsrcID, resourceName); + + UpdateResFile(fileRef); + if (protectIt) { + SetResAttrs(Get1Resource('TEXT', rsrcID), resProtected); + } + + CloseResFile(fileRef); + return TCL_OK; + + sourceFmtErr: + Tcl_AppendResult(interp, errStr, "error in \"", argv[0], "\"", + (char *) NULL); + return TCL_ERROR; +} diff --git a/mac/tclMacThrd.c b/mac/tclMacThrd.c new file mode 100644 index 0000000..4d98d9c --- /dev/null +++ b/mac/tclMacThrd.c @@ -0,0 +1,871 @@ +/* + * tclMacThrd.c -- + * + * This file implements the Mac-specific thread support. + * + * Copyright (c) 1991-1994 The Regents of the University of California. + * Copyright (c) 1994-1998 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * SCCS: @(#) tclMacThrd.c 1.2 98/02/23 16:48:07 + */ + +#include "tclInt.h" +#include "tclPort.h" +#include "tclMacInt.h" +#include <Threads.h> +#include <Gestalt.h> + +#define TCL_MAC_THRD_DEFAULT_STACK (256*1024) + + +typedef struct TclMacThrdData { + ThreadID threadID; + VOID *data; + struct TclMacThrdData *next; +} TclMacThrdData; + +/* + * This is an array of the Thread Data Keys. It is a process-wide table. + * Its size is originally set to 32, but it can grow if needed. + */ + +static TclMacThrdData **tclMacDataKeyArray; +#define TCL_MAC_INITIAL_KEYSIZE 32 + +/* + * These two bits of data store the current maximum number of keys + * and the keyCounter (which is the number of occupied slots in the + * KeyData array. + * + */ + +static int maxNumKeys = 0; +static int keyCounter = 0; + +/* + * Prototypes for functions used only in this file + */ + +TclMacThrdData *GetThreadDataStruct(Tcl_ThreadDataKey keyVal); +TclMacThrdData *RemoveThreadDataStruct(Tcl_ThreadDataKey keyVal); + +/* + * Note: The race evoked by the emulation layer for joinable threads + * (see ../win/tclWinThrd.c) cannot occur on this platform due to + * the cooperative implementation of multithreading. + */ + +/* + *---------------------------------------------------------------------- + * + * TclMacHaveThreads -- + * + * Do we have the Thread Manager? + * + * Results: + * 1 if the ThreadManager is present, 0 otherwise. + * + * Side effects: + * If this is the first time this is called, the return is cached. + * + *---------------------------------------------------------------------- + */ + +int +TclMacHaveThreads(void) +{ + static initialized = false; + static int tclMacHaveThreads = false; + long response = 0; + OSErr err = noErr; + + if (!initialized) { + err = Gestalt(gestaltThreadMgrAttr, &response); + if (err == noErr) { + tclMacHaveThreads = response | (1 << gestaltThreadMgrPresent); + } + } + + return tclMacHaveThreads; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_CreateThread -- + * + * This procedure creates a new thread. + * + * Results: + * TCL_OK if the thread could be created. The thread ID is + * returned in a parameter. + * + * Side effects: + * A new thread is created. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_CreateThread(idPtr, proc, clientData, stackSize, flags) + Tcl_ThreadId *idPtr; /* Return, the ID of the thread */ + Tcl_ThreadCreateProc proc; /* Main() function of the thread */ + ClientData clientData; /* The one argument to Main() */ + int stackSize; /* Size of stack for the new thread */ + int flags; /* Flags controlling behaviour of + * the new thread */ +{ + if (!TclMacHaveThreads()) { + return TCL_ERROR; + } + + if (stackSize == TCL_THREAD_STACK_DEFAULT) { + stackSize = TCL_MAC_THRD_DEFAULT_STACK; + } + +#if TARGET_CPU_68K && TARGET_RT_MAC_CFM + { + ThreadEntryProcPtr entryProc; + entryProc = NewThreadEntryUPP(proc); + + NewThread(kCooperativeThread, entryProc, (void *) clientData, + stackSize, kCreateIfNeeded, NULL, (ThreadID *) idPtr); + } +#else + NewThread(kCooperativeThread, proc, (void *) clientData, + stackSize, kCreateIfNeeded, NULL, (ThreadID *) idPtr); +#endif + if ((ThreadID) *idPtr == kNoThreadID) { + return TCL_ERROR; + } else { + if (flags & TCL_THREAD_JOINABLE) { + TclRememberJoinableThread (*idPtr); + } + + return TCL_OK; + } + +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_JoinThread -- + * + * This procedure waits upon the exit of the specified thread. + * + * Results: + * TCL_OK if the wait was successful, TCL_ERROR else. + * + * Side effects: + * The result area is set to the exit code of the thread we + * waited upon. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_JoinThread(threadId, result) + Tcl_ThreadId threadId; /* Id of the thread to wait upon */ + int* result; /* Reference to the storage the result + * of the thread we wait upon will be + * written into. */ +{ + if (!TclMacHaveThreads()) { + return TCL_ERROR; + } + + return TclJoinThread (threadId, result); +} + +/* + *---------------------------------------------------------------------- + * + * TclpThreadExit -- + * + * This procedure terminates the current thread. + * + * Results: + * None. + * + * Side effects: + * This procedure terminates the current thread. + * + *---------------------------------------------------------------------- + */ + +void +TclpThreadExit(status) + int status; +{ + ThreadID curThread; + + if (!TclMacHaveThreads()) { + return; + } + + GetCurrentThread(&curThread); + TclSignalExitThread ((Tcl_ThreadId) curThread, status); + + DisposeThread(curThread, NULL, false); +} + + +/* + *---------------------------------------------------------------------- + * + * Tcl_GetCurrentThread -- + * + * This procedure returns the ID of the currently running thread. + * + * Results: + * A thread ID. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_ThreadId +Tcl_GetCurrentThread() +{ +#ifdef TCL_THREADS + ThreadID curThread; + + if (!TclMacHaveThreads()) { + return (Tcl_ThreadId) 0; + } else { + GetCurrentThread(&curThread); + return (Tcl_ThreadId) curThread; + } +#else + return (Tcl_ThreadId) 0; +#endif +} + + +/* + *---------------------------------------------------------------------- + * + * TclpInitLock + * + * This procedure is used to grab a lock that serializes initialization + * and finalization of Tcl. On some platforms this may also initialize + * the mutex used to serialize creation of more mutexes and thread + * local storage keys. + * + * Results: + * None. + * + * Side effects: + * Acquire the initialization mutex. + * + *---------------------------------------------------------------------- + */ + +void +TclpInitLock() +{ +#ifdef TCL_THREADS + /* There is nothing to do on the Mac. */; +#endif +} + + +/* + *---------------------------------------------------------------------- + * + * TclpInitUnlock + * + * This procedure is used to release a lock that serializes initialization + * and finalization of Tcl. + * + * Results: + * None. + * + * Side effects: + * Release the initialization mutex. + * + *---------------------------------------------------------------------- + */ + +void +TclpInitUnlock() +{ +#ifdef TCL_THREADS + /* There is nothing to do on the Mac */; +#endif +} + +/* + *---------------------------------------------------------------------- + * + * TclpMasterLock + * + * This procedure is used to grab a lock that serializes creation + * and finalization of serialization objects. This interface is + * only needed in finalization; it is hidden during + * creation of the objects. + * + * This lock must be different than the initLock because the + * initLock is held during creation of syncronization objects. + * + * Results: + * None. + * + * Side effects: + * Acquire the master mutex. + * + *---------------------------------------------------------------------- + */ + +void +TclpMasterLock() +{ +#ifdef TCL_THREADS + /* There is nothing to do on the Mac */; +#endif +} + + +/* + *---------------------------------------------------------------------- + * + * TclpMasterUnlock + * + * This procedure is used to release a lock that serializes creation + * and finalization of synchronization objects. + * + * Results: + * None. + * + * Side effects: + * Release the master mutex. + * + *---------------------------------------------------------------------- + */ + +void +TclpMasterUnlock() +{ +#ifdef TCL_THREADS + /* There is nothing to do on the Mac */ +#endif +} + + +/* + *---------------------------------------------------------------------- + * + * Tcl_GetAllocMutex + * + * This procedure returns a pointer to a statically initialized + * mutex for use by the memory allocator. The alloctor must + * use this lock, because all other locks are allocated... + * + * Results: + * A pointer to a mutex that is suitable for passing to + * Tcl_MutexLock and Tcl_MutexUnlock. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Mutex * +Tcl_GetAllocMutex() +{ + /* There is nothing to do on the Mac */ + return NULL; +} + +#ifdef TCL_THREADS + +/* + *---------------------------------------------------------------------- + * + * Tcl_MutexLock -- + * + * This procedure is invoked to lock a mutex. This procedure + * handles initializing the mutex, if necessary. The caller + * can rely on the fact that Tcl_Mutex is an opaque pointer. + * This routine will change that pointer from NULL after first use. + * + * Results: + * None. + * + * Side effects: + * May block the current thread. The mutex is aquired when + * this returns. Will allocate memory for a pthread_mutex_t + * and initialize this the first time this Tcl_Mutex is used. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_MutexLock(mutexPtr) + Tcl_Mutex *mutexPtr; /* Really (pthread_mutex_t **) */ +{ +/* There is nothing to do on the Mac */ +} + + +/* + *---------------------------------------------------------------------- + * + * TclpMutexUnlock -- + * + * This procedure is invoked to unlock a mutex. The mutex must + * have been locked by Tcl_MutexLock. + * + * Results: + * None. + * + * Side effects: + * The mutex is released when this returns. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_MutexUnlock(mutexPtr) + Tcl_Mutex *mutexPtr; /* Really (pthread_mutex_t **) */ +{ +/* There is nothing to do on the Mac */ +} + + +/* + *---------------------------------------------------------------------- + * + * TclpFinalizeMutex -- + * + * This procedure is invoked to clean up one mutex. This is only + * safe to call at the end of time. + * + * This assumes the Master Lock is held. + * + * Results: + * None. + * + * Side effects: + * The mutex list is deallocated. + * + *---------------------------------------------------------------------- + */ + +void +TclpFinalizeMutex(mutexPtr) + Tcl_Mutex *mutexPtr; +{ +/* There is nothing to do on the Mac */ +} + + +/* + *---------------------------------------------------------------------- + * + * TclpThreadDataKeyInit -- + * + * This procedure initializes a thread specific data block key. + * Each thread has table of pointers to thread specific data. + * all threads agree on which table entry is used by each module. + * this is remembered in a "data key", that is just an index into + * this table. To allow self initialization, the interface + * passes a pointer to this key and the first thread to use + * the key fills in the pointer to the key. The key should be + * a process-wide static. + * + * There is no system-wide support for thread specific data on the + * Mac. So we implement this as an array of pointers. The keys are + * allocated sequentially, and each key maps to a slot in the table. + * The table element points to a linked list of the instances of + * the data for each thread. + * + * Results: + * None. + * + * Side effects: + * Will bump the key counter if this is the first time this key + * has been initialized. May grow the DataKeyArray if that is + * necessary. + * + *---------------------------------------------------------------------- + */ + +void +TclpThreadDataKeyInit(keyPtr) + Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, + * really (pthread_key_t **) */ +{ + + if (*keyPtr == NULL) { + keyCounter += 1; + *keyPtr = (Tcl_ThreadDataKey) keyCounter; + if (keyCounter > maxNumKeys) { + TclMacThrdData **newArray; + int i, oldMax = maxNumKeys; + + maxNumKeys = maxNumKeys + TCL_MAC_INITIAL_KEYSIZE; + + newArray = (TclMacThrdData **) + ckalloc(maxNumKeys * sizeof(TclMacThrdData *)); + + for (i = 0; i < oldMax; i++) { + newArray[i] = tclMacDataKeyArray[i]; + } + for (i = oldMax; i < maxNumKeys; i++) { + newArray[i] = NULL; + } + + if (tclMacDataKeyArray != NULL) { + ckfree((char *) tclMacDataKeyArray); + } + tclMacDataKeyArray = newArray; + + } + /* TclRememberDataKey(keyPtr); */ + } +} + +/* + *---------------------------------------------------------------------- + * + * TclpThreadDataKeyGet -- + * + * This procedure returns a pointer to a block of thread local storage. + * + * Results: + * A thread-specific pointer to the data structure, or NULL + * if the memory has not been assigned to this key for this thread. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +VOID * +TclpThreadDataKeyGet(keyPtr) + Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, + * really (pthread_key_t **) */ +{ + TclMacThrdData *dataPtr; + + dataPtr = GetThreadDataStruct(*keyPtr); + + if (dataPtr == NULL) { + return NULL; + } else { + return dataPtr->data; + } +} + + +/* + *---------------------------------------------------------------------- + * + * TclpThreadDataKeySet -- + * + * This procedure sets the pointer to a block of thread local storage. + * + * Results: + * None. + * + * Side effects: + * Sets up the thread so future calls to TclpThreadDataKeyGet with + * this key will return the data pointer. + * + *---------------------------------------------------------------------- + */ + +void +TclpThreadDataKeySet(keyPtr, data) + Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, + * really (pthread_key_t **) */ + VOID *data; /* Thread local storage */ +{ + TclMacThrdData *dataPtr; + ThreadID curThread; + + dataPtr = GetThreadDataStruct(*keyPtr); + + /* + * Is it legal to reset the thread data like this? + * And if so, who owns the memory? + */ + + if (dataPtr != NULL) { + dataPtr->data = data; + } else { + dataPtr = (TclMacThrdData *) ckalloc(sizeof(TclMacThrdData)); + GetCurrentThread(&curThread); + dataPtr->threadID = curThread; + dataPtr->data = data; + dataPtr->next = tclMacDataKeyArray[(int) *keyPtr - 1]; + tclMacDataKeyArray[(int) *keyPtr - 1] = dataPtr; + } +} + +/* + *---------------------------------------------------------------------- + * + * TclpFinalizeThreadData -- + * + * This procedure cleans up the thread-local storage. This is + * called once for each thread. + * + * Results: + * None. + * + * Side effects: + * Frees up all thread local storage. + * + *---------------------------------------------------------------------- + */ + +void +TclpFinalizeThreadData(keyPtr) + Tcl_ThreadDataKey *keyPtr; +{ + TclMacThrdData *dataPtr; + + if (*keyPtr != NULL) { + dataPtr = RemoveThreadDataStruct(*keyPtr); + + if ((dataPtr != NULL) && (dataPtr->data != NULL)) { + ckfree((char *) dataPtr->data); + ckfree((char *) dataPtr); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TclpFinalizeThreadDataKey -- + * + * This procedure is invoked to clean up one key. This is a + * process-wide storage identifier. The thread finalization code + * cleans up the thread local storage itself. + * + * On the Mac, there is really nothing to do here, since the key + * is just an array index. But we set the key to 0 just in case + * someone else is relying on that. + * + * Results: + * None. + * + * Side effects: + * The keyPtr value is set to 0. + * + *---------------------------------------------------------------------- + */ + +void +TclpFinalizeThreadDataKey(keyPtr) + Tcl_ThreadDataKey *keyPtr; +{ + ckfree((char *) tclMacDataKeyArray[(int) *keyPtr - 1]); + tclMacDataKeyArray[(int) *keyPtr - 1] = NULL; + *keyPtr = NULL; +} + + +/* + *---------------------------------------------------------------------- + * + * GetThreadDataStruct -- + * + * This procedure gets the data structure corresponding to + * keyVal for the current process. + * + * Results: + * The requested key data. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TclMacThrdData * +GetThreadDataStruct(keyVal) + Tcl_ThreadDataKey keyVal; +{ + ThreadID curThread; + TclMacThrdData *dataPtr; + + /* + * The keyPtr will only be greater than keyCounter is someone + * has passed us a key without getting the value from + * TclpInitDataKey. + */ + + if ((int) keyVal <= 0) { + return NULL; + } else if ((int) keyVal > keyCounter) { + Tcl_Panic("illegal data key value"); + } + + GetCurrentThread(&curThread); + + for (dataPtr = tclMacDataKeyArray[(int) keyVal - 1]; dataPtr != NULL; + dataPtr = dataPtr->next) { + if (dataPtr->threadID == curThread) { + break; + } + } + + return dataPtr; +} + + +/* + *---------------------------------------------------------------------- + * + * RemoveThreadDataStruct -- + * + * This procedure removes the data structure corresponding to + * keyVal for the current process from the list kept for keyVal. + * + * Results: + * The requested key data is removed from the list, and a pointer + * to it is returned. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TclMacThrdData * +RemoveThreadDataStruct(keyVal) + Tcl_ThreadDataKey keyVal; +{ + ThreadID curThread; + TclMacThrdData *dataPtr, *prevPtr; + + + if ((int) keyVal <= 0) { + return NULL; + } else if ((int) keyVal > keyCounter) { + Tcl_Panic("illegal data key value"); + } + + GetCurrentThread(&curThread); + + for (dataPtr = tclMacDataKeyArray[(int) keyVal - 1], prevPtr = NULL; + dataPtr != NULL; + prevPtr = dataPtr, dataPtr = dataPtr->next) { + if (dataPtr->threadID == curThread) { + break; + } + } + + if (dataPtr == NULL) { + /* No body */ + } else if ( prevPtr == NULL) { + tclMacDataKeyArray[(int) keyVal - 1] = dataPtr->next; + } else { + prevPtr->next = dataPtr->next; + } + + return dataPtr; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_ConditionWait -- + * + * This procedure is invoked to wait on a condition variable. + * On the Mac, mutexes are no-ops, and we just yield. After + * all, it is the application's job to loop till the condition + * variable is changed... + * + * + * Results: + * None. + * + * Side effects: + * Will block the current thread till someone else yields. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_ConditionWait(condPtr, mutexPtr, timePtr) + Tcl_Condition *condPtr; /* Really (pthread_cond_t **) */ + Tcl_Mutex *mutexPtr; /* Really (pthread_mutex_t **) */ + Tcl_Time *timePtr; /* Timeout on waiting period */ +{ + if (TclMacHaveThreads()) { + YieldToAnyThread(); + } +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_ConditionNotify -- + * + * This procedure is invoked to signal a condition variable. + * + * The mutex must be held during this call to avoid races, + * but this interface does not enforce that. + * + * Results: + * None. + * + * Side effects: + * May unblock another thread. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_ConditionNotify(condPtr) + Tcl_Condition *condPtr; +{ + if (TclMacHaveThreads()) { + YieldToAnyThread(); + } +} + + +/* + *---------------------------------------------------------------------- + * + * TclpFinalizeCondition -- + * + * This procedure is invoked to clean up a condition variable. + * This is only safe to call at the end of time. + * + * This assumes the Master Lock is held. + * + * Results: + * None. + * + * Side effects: + * The condition variable is deallocated. + * + *---------------------------------------------------------------------- + */ + +void +TclpFinalizeCondition(condPtr) + Tcl_Condition *condPtr; +{ + /* Nothing to do on the Mac */ +} + + + +#endif /* TCL_THREADS */ + diff --git a/mac/tclMacThrd.h b/mac/tclMacThrd.h new file mode 100644 index 0000000..22f2c83 --- /dev/null +++ b/mac/tclMacThrd.h @@ -0,0 +1,20 @@ +/* + * tclUnixThrd.h -- + * + * This header file defines things for thread support. + * + * Copyright (c) 1998 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * SCCS: @(#) + */ + +#ifndef _TCLMACTHRD +#define _TCLMACTHRD + +#ifdef TCL_THREADS + +#endif /* TCL_THREADS */ +#endif /* _TCLMACTHRD */ diff --git a/mac/tclMacTime.c b/mac/tclMacTime.c new file mode 100644 index 0000000..d585ee7 --- /dev/null +++ b/mac/tclMacTime.c @@ -0,0 +1,436 @@ +/* + * tclMacTime.c -- + * + * Contains Macintosh specific versions of Tcl functions that + * obtain time values from the operating system. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacTime.c,v 1.8 2003/05/18 19:48:26 kennykb Exp $ + */ + +#include "tclInt.h" +#include "tclPort.h" +#include "tclMacInt.h" +#include <OSUtils.h> +#include <Timer.h> +#include <time.h> + +/* + * Static variables used by the Tcl_GetTime function. + */ + +static int initalized = false; +static unsigned long baseSeconds; +static UnsignedWide microOffset; + +static int gmt_initialized = false; +static long gmt_offset; +static int gmt_isdst; +TCL_DECLARE_MUTEX(gmtMutex) + +static int gmt_lastGetDateUseGMT = 0; + +typedef struct _TABLE { + char *name; + int type; + time_t value; +} TABLE; + + +#define HOUR(x) ((time_t) (3600 * x)) + +#define tZONE 0 +#define tDAYZONE 1 + + +/* + * inverse timezone table, adapted from tclDate.c by removing duplicates and + * adding some made up names for unusual daylight savings + */ +static TABLE invTimezoneTable[] = { + { "Z", -1, HOUR( 36) }, /* Unknown */ + { "GMT", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "BST", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "WAT", tZONE, HOUR( 1) }, /* West Africa */ + { "WADST", tDAYZONE, HOUR( 1) }, /* West Africa Daylight*/ + { "AT", tZONE, HOUR( 2) }, /* Azores Daylight*/ + { "ADST", tDAYZONE, HOUR( 2) }, /* Azores */ + { "NFT", tZONE, HOUR( 7/2) }, /* Newfoundland */ + { "NDT", tDAYZONE, HOUR( 7/2) }, /* Newfoundland Daylight */ + { "AST", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "ADT", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "EST", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "EDT", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "CST", tZONE, HOUR( 6) }, /* Central Standard */ + { "CDT", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "MST", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "MDT", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "PST", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "PDT", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "YST", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "YDT", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "HST", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "HDT", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "NT", tZONE, HOUR(11) }, /* Nome */ + { "NST", tDAYZONE, HOUR(11) }, /* Nome Daylight*/ + { "IDLW", tZONE, HOUR(12) }, /* International Date Line West */ + { "CET", tZONE, -HOUR( 1) }, /* Central European */ + { "CEST", tDAYZONE, -HOUR( 1) }, /* Central European Summer */ + { "EET", tZONE, -HOUR( 2) }, /* Eastern Europe, USSR Zone 1 */ + { "EEST", tDAYZONE, -HOUR( 2) }, /* Eastern Europe, USSR Zone 1 Daylight*/ + { "BT", tZONE, -HOUR( 3) }, /* Baghdad, USSR Zone 2 */ + { "BDST", tDAYZONE, -HOUR( 3) }, /* Baghdad, USSR Zone 2 Daylight*/ + { "IT", tZONE, -HOUR( 7/2) }, /* Iran */ + { "IDST", tDAYZONE, -HOUR( 7/2) }, /* Iran Daylight*/ + { "ZP4", tZONE, -HOUR( 4) }, /* USSR Zone 3 */ + { "ZP4S", tDAYZONE, -HOUR( 4) }, /* USSR Zone 3 */ + { "ZP5", tZONE, -HOUR( 5) }, /* USSR Zone 4 */ + { "ZP5S", tDAYZONE, -HOUR( 5) }, /* USSR Zone 4 */ + { "IST", tZONE, -HOUR(11/2) }, /* Indian Standard */ + { "ISDST", tDAYZONE, -HOUR(11/2) }, /* Indian Standard */ + { "ZP6", tZONE, -HOUR( 6) }, /* USSR Zone 5 */ + { "ZP6S", tDAYZONE, -HOUR( 6) }, /* USSR Zone 5 */ + { "WAST", tZONE, -HOUR( 7) }, /* West Australian Standard */ + { "WADT", tDAYZONE, -HOUR( 7) }, /* West Australian Daylight */ + { "JT", tZONE, -HOUR(15/2) }, /* Java (3pm in Cronusland!) */ + { "JDST", tDAYZONE, -HOUR(15/2) }, /* Java (3pm in Cronusland!) */ + { "CCT", tZONE, -HOUR( 8) }, /* China Coast, USSR Zone 7 */ + { "CCST", tDAYZONE, -HOUR( 8) }, /* China Coast, USSR Zone 7 */ + { "JST", tZONE, -HOUR( 9) }, /* Japan Standard, USSR Zone 8 */ + { "JSDST", tDAYZONE, -HOUR( 9) }, /* Japan Standard, USSR Zone 8 */ + { "CAST", tZONE, -HOUR(19/2) }, /* Central Australian Standard */ + { "CADT", tDAYZONE, -HOUR(19/2) }, /* Central Australian Daylight */ + { "EAST", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "EADT", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "NZT", tZONE, -HOUR(12) }, /* New Zealand */ + { "NZDT", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { NULL } +}; + +/* + * Prototypes for procedures that are private to this file: + */ + +static void SubtractUnsignedWide _ANSI_ARGS_((UnsignedWide *x, + UnsignedWide *y, UnsignedWide *result)); + +/* + *----------------------------------------------------------------------------- + * + * TclpGetGMTOffset -- + * + * This procedure gets the offset seconds that needs to be _added_ to tcl time + * in seconds (i.e. GMT time) to get local time needed as input to various + * Mac OS APIs, to convert Mac OS API output to tcl time, _subtract_ this value. + * + * Results: + * Number of seconds separating GMT time and mac. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +long +TclpGetGMTOffset() +{ + if (gmt_initialized == false) { + MachineLocation loc; + + Tcl_MutexLock(&gmtMutex); + ReadLocation(&loc); + gmt_offset = loc.u.gmtDelta & 0x00ffffff; + if (gmt_offset & 0x00800000) { + gmt_offset = gmt_offset | 0xff000000; + } + gmt_isdst=(loc.u.dlsDelta < 0); + gmt_initialized = true; + Tcl_MutexUnlock(&gmtMutex); + } + return (gmt_offset); +} + +/* + *----------------------------------------------------------------------------- + * + * TclpGetSeconds -- + * + * This procedure returns the number of seconds from the epoch. On + * the Macintosh the epoch is Midnight Jan 1, 1904. Unfortunatly, + * the Macintosh doesn't tie the epoch to a particular time zone. For + * Tcl we tie the epoch to GMT. This makes the time zone date parsing + * code work. The epoch for Mac-Tcl is: Midnight Jan 1, 1904 GMT. + * + * Results: + * Number of seconds from the epoch in GMT. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +unsigned long +TclpGetSeconds() +{ + unsigned long seconds; + + GetDateTime(&seconds); + return (seconds - TclpGetGMTOffset() + tcl_mac_epoch_offset); +} + +/* + *----------------------------------------------------------------------------- + * + * TclpGetClicks -- + * + * This procedure returns a value that represents the highest resolution + * clock available on the system. There are no garantees on what the + * resolution will be. In Tcl we will call this value a "click". The + * start time is also system dependant. + * + * Results: + * Number of clicks from some start time. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +unsigned long +TclpGetClicks() +{ + UnsignedWide micros; + + Microseconds(µs); + return micros.lo; +} + +/* + *---------------------------------------------------------------------- + * + * TclpGetTimeZone -- + * + * Get the current time zone. + * + * Results: + * The return value is the local time zone, measured in + * minutes away from GMT (-ve for east, +ve for west). + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclpGetTimeZone ( + unsigned long currentTime) /* Ignored on Mac. */ +{ + long offset; + + /* + * Convert the Mac offset from seconds to minutes and + * add an hour if we have daylight savings time. + */ + offset = -TclpGetGMTOffset(); + offset /= 60; + if (gmt_isdst) { + offset += 60; + } + + return offset; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_GetTime -- + * + * Gets the current system time in seconds and microseconds + * since the beginning of the epoch: 00:00 UCT, January 1, 1970. + * + * Results: + * Returns the current time (in the local timezone) in timePtr. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_GetTime( + Tcl_Time *timePtr) /* Location to store time information. */ +{ + UnsignedWide micro; +#ifndef NO_LONG_LONG + long long *microPtr; +#endif + + if (initalized == false) { + GetDateTime(&baseSeconds); + /* + * Remove the local offset that ReadDateTime() adds. + */ + baseSeconds -= TclpGetGMTOffset() - tcl_mac_epoch_offset; + Microseconds(µOffset); + initalized = true; + } + + Microseconds(µ); + +#ifndef NO_LONG_LONG + microPtr = (long long *) µ + *microPtr -= *((long long *) µOffset); + timePtr->sec = baseSeconds + (*microPtr / 1000000); + timePtr->usec = *microPtr % 1000000; +#else + SubtractUnsignedWide(µ, µOffset, µ); + + /* + * This lovely computation is equal to: base + (micro / 1000000) + * For the .hi part the ratio of 0x100000000 / 1000000 has been + * reduced to avoid overflow. This computation certainly has + * problems as the .hi part gets large. However, your application + * would have to run for a long time to make that happen. + */ + + timePtr->sec = baseSeconds + (micro.lo / 1000000) + + (long) (micro.hi * ((double) 33554432.0 / 15625.0)); + timePtr->usec = micro.lo % 1000000; +#endif +} + +/* + *---------------------------------------------------------------------- + * + * TclpGetDate -- + * + * Converts raw seconds to a struct tm data structure. The + * returned time will be for Greenwich Mean Time if the useGMT flag + * is set. Otherwise, the returned time will be for the local + * time zone. This function is meant to be used as a replacement + * for localtime and gmtime which is broken on most ANSI libs + * on the Macintosh. + * + * Results: + * None. + * + * Side effects: + * The passed in struct tm data structure is modified. + * + *---------------------------------------------------------------------- + */ + +struct tm * +TclpGetDate( + TclpTime_t time, /* Time struct to fill. */ + int useGMT) /* True if date should reflect GNT time. */ +{ + const time_t *tp = (const time_t *)time; + DateTimeRec dtr; + unsigned long offset=0L; + static struct tm statictime; + static const short monthday[12] = + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + + if(useGMT) + SecondsToDate(*tp - tcl_mac_epoch_offset, &dtr); + else + SecondsToDate(*tp + TclpGetGMTOffset() - tcl_mac_epoch_offset, &dtr); + + statictime.tm_sec = dtr.second; + statictime.tm_min = dtr.minute; + statictime.tm_hour = dtr.hour; + statictime.tm_mday = dtr.day; + statictime.tm_mon = dtr.month - 1; + statictime.tm_year = dtr.year - 1900; + statictime.tm_wday = dtr.dayOfWeek - 1; + statictime.tm_yday = monthday[statictime.tm_mon] + + statictime.tm_mday - 1; + if (1 < statictime.tm_mon && !(statictime.tm_year & 3)) { + ++statictime.tm_yday; + } + if(useGMT) + statictime.tm_isdst = 0; + else + statictime.tm_isdst = gmt_isdst; + gmt_lastGetDateUseGMT=useGMT; /* hack to make TclpGetTZName below work */ + return(&statictime); +} + +/* + *---------------------------------------------------------------------- + * + * TclpGetTZName -- + * + * Gets the current timezone string. + * + * Results: + * Returns a pointer to a static string, or NULL on failure. + * The static string is in UTF-8 encoding. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +char * +TclpGetTZName(int dst) +{ + register TABLE *tp; + long zonevalue=-TclpGetGMTOffset(); + + if (gmt_isdst) + zonevalue += HOUR(1); + + if(gmt_lastGetDateUseGMT) /* hack: if last TclpGetDate was called */ + zonevalue=0; /* with useGMT==1 then we're using GMT */ + + for (tp = invTimezoneTable; tp->name; tp++) { + if ((tp->value == zonevalue) && (tp->type == dst)) break; + } + if(!tp->name) + tp = invTimezoneTable; /* default to unknown */ + + return tp->name; +} + +#ifdef NO_LONG_LONG +/* + *---------------------------------------------------------------------- + * + * SubtractUnsignedWide -- + * + * Subtracts one UnsignedWide value from another. + * + * Results: + * The subtracted value. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +SubtractUnsignedWide( + UnsignedWide *x, /* Ptr to wide int. */ + UnsignedWide *y, /* Ptr to wide int. */ + UnsignedWide *result) /* Ptr to result. */ +{ + result->hi = x->hi - y->hi; + if (x->lo < y->lo) { + result->hi--; + } + result->lo = x->lo - y->lo; +} +#endif diff --git a/mac/tclMacUnix.c b/mac/tclMacUnix.c new file mode 100644 index 0000000..81eb225 --- /dev/null +++ b/mac/tclMacUnix.c @@ -0,0 +1,425 @@ +/* + * tclMacUnix.c -- + * + * This file contains routines to implement several features + * available to the Unix implementation, but that require + * extra work to do on a Macintosh. These include routines + * Unix Tcl normally hands off to the Unix OS. + * + * Copyright (c) 1993-1994 Lockheed Missle & Space Company, AI Center + * Copyright (c) 1994-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacUnix.c,v 1.5 2002/10/09 11:54:45 das Exp $ + */ + +#include <Files.h> +#include <Strings.h> +#include <TextUtils.h> +#include <Finder.h> +#include <FSpCompat.h> +#include <Aliases.h> +#include <Errors.h> + +#include "tclInt.h" +#include "tclMacInt.h" + +/* + * The following two Includes are from the More Files package + */ +#include "FileCopy.h" +#include "MoreFiles.h" +#include "MoreFilesExtras.h" + +/* + * The following may not be defined in some versions of + * MPW header files. + */ +#ifndef kIsInvisible +#define kIsInvisible 0x4000 +#endif +#ifndef kIsAlias +#define kIsAlias 0x8000 +#endif + +/* + * Missing error codes + */ +#define usageErr 500 +#define noSourceErr 501 +#define isDirErr 502 + + +/* + *---------------------------------------------------------------------- + * + * Tcl_EchoCmd -- + * + * Implements the TCL echo command: + * echo ?str ...? + * + * Results: + * Always returns TCL_OK. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_EchoCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int argc, /* Number of arguments. */ + CONST char **argv) /* Argument strings. */ +{ + Tcl_Channel chan; + int mode, result, i; + + chan = Tcl_GetChannel(interp, "stdout", &mode); + if (chan == (Tcl_Channel) NULL) { + return TCL_ERROR; + } + for (i = 1; i < argc; i++) { + result = Tcl_WriteChars(chan, argv[i], -1); + if (result < 0) { + Tcl_AppendResult(interp, "echo: ", Tcl_GetChannelName(chan), + ": ", Tcl_PosixError(interp), (char *) NULL); + return TCL_ERROR; + } + if (i < (argc - 1)) { + Tcl_WriteChars(chan, " ", -1); + } + } + Tcl_WriteChars(chan, "\n", -1); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_LsObjCmd -- + * + * This procedure is invoked to process the "ls" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ +int +Tcl_LsObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[]) /* Argument strings. */ +{ +#define STRING_LENGTH 80 +#define CR '\n' + int i, j; + int fieldLength, len = 0, maxLen = 0, perLine; + OSErr err; + CInfoPBRec paramBlock; + HFileInfo *hpb = (HFileInfo *)¶mBlock; + DirInfo *dpb = (DirInfo *)¶mBlock; + char theFile[256]; + char theLine[STRING_LENGTH + 2]; + int fFlag = false, pFlag = false, aFlag = false, lFlag = false, + cFlag = false, hFlag = false; + char *argv; + Tcl_Obj *newObjv[2], *resultObjPtr; + + /* + * Process command flags. End if argument doesn't start + * with a dash or is a dash by itself. The remaining arguments + * should be files. + */ + for (i = 1; i < objc; i++) { + argv = Tcl_GetString(objv[i]); + if (argv[0] != '-') { + break; + } + + if (!strcmp(argv, "-")) { + i++; + break; + } + + for (j = 1 ; argv[j] ; ++j) { + switch(argv[j]) { + case 'a': + case 'A': + aFlag = true; + break; + case '1': + cFlag = false; + break; + case 'C': + cFlag = true; + break; + case 'F': + fFlag = true; + break; + case 'H': + hFlag = true; + break; + case 'p': + pFlag = true; + break; + case 'l': + pFlag = false; + lFlag = true; + break; + default: + Tcl_AppendResult(interp, "error - unknown flag ", + "usage: ls -apCFHl1 ?files? ", NULL); + return TCL_ERROR; + } + } + } + + objv += i; + objc -= i; + + /* + * No file specifications means we search for all files. + * Glob will be doing most of the work. + */ + if (!objc) { + objc = 1; + newObjv[0] = Tcl_NewStringObj("*", -1); + newObjv[1] = NULL; + objv = newObjv; + } + + if (Tcl_GlobObjCmd(NULL, interp, objc + 1, objv - 1) != TCL_OK) { + Tcl_ResetResult(interp); + return TCL_ERROR; + } + + resultObjPtr = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(resultObjPtr); + if (Tcl_ListObjGetElements(interp, resultObjPtr, &objc, (Tcl_Obj ***)&objv) != TCL_OK) { + Tcl_DecrRefCount(resultObjPtr); + return TCL_ERROR; + } + + Tcl_ResetResult(interp); + + /* + * There are two major methods for listing files: the long + * method and the normal method. + */ + if (lFlag) { + char creator[5], type[5], time[16], date[16]; + char lineTag; + long size; + unsigned short flags; + Tcl_Obj *objPtr; + char *string; + int length; + + /* + * Print the header for long listing. + */ + if (hFlag) { + sprintf(theLine, "T %7s %8s %8s %4s %4s %6s %s", + "Size", "ModTime", "ModDate", + "CRTR", "TYPE", "Flags", "Name"); + Tcl_AppendResult(interp, theLine, "\n", NULL); + Tcl_AppendResult(interp, + "-------------------------------------------------------------\n", + NULL); + } + + for (i = 0; i < objc; i++) { + strcpy(theFile, Tcl_GetString(objv[i])); + + c2pstr(theFile); + hpb->ioCompletion = NULL; + hpb->ioVRefNum = 0; + hpb->ioFDirIndex = 0; + hpb->ioNamePtr = (StringPtr) theFile; + hpb->ioDirID = 0L; + err = PBGetCatInfoSync(¶mBlock); + p2cstr((StringPtr) theFile); + + if (hpb->ioFlAttrib & 16) { + /* + * For directories use zero as the size, use no Creator + * type, and use 'DIR ' as the file type. + */ + if ((aFlag == false) && (dpb->ioDrUsrWds.frFlags & 0x1000)) { + continue; + } + lineTag = 'D'; + size = 0; + IUTimeString(dpb->ioDrMdDat, false, (unsigned char *)time); + p2cstr((StringPtr)time); + IUDateString(dpb->ioDrMdDat, shortDate, (unsigned char *)date); + p2cstr((StringPtr)date); + strcpy(creator, " "); + strcpy(type, "DIR "); + flags = dpb->ioDrUsrWds.frFlags; + if (fFlag || pFlag) { + strcat(theFile, ":"); + } + } else { + /* + * All information for files should be printed. This + * includes size, modtime, moddate, creator type, file + * type, flags, anf file name. + */ + if ((aFlag == false) && + (hpb->ioFlFndrInfo.fdFlags & kIsInvisible)) { + continue; + } + lineTag = 'F'; + size = hpb->ioFlLgLen + hpb->ioFlRLgLen; + IUTimeString(hpb->ioFlMdDat, false, (unsigned char *)time); + p2cstr((StringPtr)time); + IUDateString(hpb->ioFlMdDat, shortDate, (unsigned char *)date); + p2cstr((StringPtr)date); + strncpy(creator, (char *) &hpb->ioFlFndrInfo.fdCreator, 4); + creator[4] = 0; + strncpy(type, (char *) &hpb->ioFlFndrInfo.fdType, 4); + type[4] = 0; + flags = hpb->ioFlFndrInfo.fdFlags; + if (fFlag) { + if (hpb->ioFlFndrInfo.fdFlags & kIsAlias) { + strcat(theFile, "@"); + } else if (hpb->ioFlFndrInfo.fdType == 'APPL') { + strcat(theFile, "*"); + } + } + } + + sprintf(theLine, "%c %7ld %8s %8s %-4.4s %-4.4s 0x%4.4X %s", + lineTag, size, time, date, creator, type, flags, theFile); + + Tcl_AppendResult(interp, theLine, "\n", NULL); + + } + + objPtr = Tcl_GetObjResult(interp); + string = Tcl_GetStringFromObj(objPtr, &length); + if ((length > 0) && (string[length - 1] == '\n')) { + Tcl_SetObjLength(objPtr, length - 1); + } + } else { + /* + * Not in long format. We only print files names. If the + * -C flag is set we need to print in multiple coloumns. + */ + int argCount, linePos; + Boolean needNewLine = false; + + /* + * Fiend the field length: the length each string printed + * to the terminal will be. + */ + if (!cFlag) { + perLine = 1; + fieldLength = STRING_LENGTH; + } else { + for (i = 0; i < objc; i++) { + argv = Tcl_GetString(objv[i]); + len = strlen(argv); + if (len > maxLen) { + maxLen = len; + } + } + fieldLength = maxLen + 3; + perLine = STRING_LENGTH / fieldLength; + } + + argCount = 0; + linePos = 0; + memset(theLine, ' ', STRING_LENGTH); + while (argCount < objc) { + strcpy(theFile, Tcl_GetString(objv[argCount])); + + c2pstr(theFile); + hpb->ioCompletion = NULL; + hpb->ioVRefNum = 0; + hpb->ioFDirIndex = 0; + hpb->ioNamePtr = (StringPtr) theFile; + hpb->ioDirID = 0L; + err = PBGetCatInfoSync(¶mBlock); + p2cstr((StringPtr) theFile); + + if (hpb->ioFlAttrib & 16) { + /* + * Directory. If -a show hidden files. If -f or -p + * denote that this is a directory. + */ + if ((aFlag == false) && (dpb->ioDrUsrWds.frFlags & 0x1000)) { + argCount++; + continue; + } + if (fFlag || pFlag) { + strcat(theFile, ":"); + } + } else { + /* + * File: If -a show hidden files, if -f show links + * (aliases) and executables (APPLs). + */ + if ((aFlag == false) && + (hpb->ioFlFndrInfo.fdFlags & kIsInvisible)) { + argCount++; + continue; + } + if (fFlag) { + if (hpb->ioFlFndrInfo.fdFlags & kIsAlias) { + strcat(theFile, "@"); + } else if (hpb->ioFlFndrInfo.fdType == 'APPL') { + strcat(theFile, "*"); + } + } + } + + /* + * Print the item, taking into account multi- + * coloum output. + */ + strncpy(theLine + (linePos * fieldLength), theFile, + strlen(theFile)); + linePos++; + + if (linePos == perLine) { + theLine[STRING_LENGTH] = '\0'; + if (needNewLine) { + Tcl_AppendResult(interp, "\n", theLine, NULL); + } else { + Tcl_AppendResult(interp, theLine, NULL); + needNewLine = true; + } + linePos = 0; + memset(theLine, ' ', STRING_LENGTH); + } + + argCount++; + } + + if (linePos != 0) { + theLine[STRING_LENGTH] = '\0'; + if (needNewLine) { + Tcl_AppendResult(interp, "\n", theLine, NULL); + } else { + Tcl_AppendResult(interp, theLine, NULL); + } + } + } + + Tcl_DecrRefCount(resultObjPtr); + + return TCL_OK; +} diff --git a/mac/tclMacUtil.c b/mac/tclMacUtil.c new file mode 100644 index 0000000..ab65815 --- /dev/null +++ b/mac/tclMacUtil.c @@ -0,0 +1,514 @@ +/* + * tclMacUtil.c -- + * + * This contains utility functions used to help with + * implementing Macintosh specific portions of the Tcl port. + * + * Copyright (c) 1993-1994 Lockheed Missle & Space Company, AI Center + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclMacUtil.c,v 1.7 2003/03/03 20:22:44 das Exp $ + */ + +#include "tcl.h" +#include "tclInt.h" +#include "tclMacInt.h" +#include "tclMath.h" +#include "tclMacPort.h" + +#include <Aliases.h> +#include <Errors.h> +#include <Files.h> +#include <Folders.h> +#include <FSpCompat.h> +#include <Strings.h> +#include <TextUtils.h> +#include <MoreFilesExtras.h> + +/* + * The following two Includes are from the More Files package. + */ +#include <FileCopy.h> +#include <MoreFiles.h> + +/* + *---------------------------------------------------------------------- + * + * hypotd -- + * + * The standard math function hypot is not supported by Think C. + * It is included here so everything works. It is supported by + * CodeWarrior Pro 1, but the 68K version does not support doubles. + * So we hack it in. + * + * Results: + * Result of computation. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +#if defined(THINK_C) +double hypotd(double x, double y); + +double +hypotd( + double x, /* X value */ + double y) /* Y value */ +{ + double sum; + + sum = x*x + y*y; + return sqrt(sum); +} +#endif + +/* + *---------------------------------------------------------------------- + * + * FSpGetDefaultDir -- + * + * This function gets the current default directory. + * + * Results: + * The provided FSSpec is changed to point to the "default" + * directory. The function returns what ever errors + * FSMakeFSSpecCompat may encounter. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +FSpGetDefaultDir( + FSSpecPtr dirSpec) /* On return the default directory. */ +{ + OSErr err; + short vRefNum = 0; + long int dirID = 0; + + err = HGetVol(NULL, &vRefNum, &dirID); + + if (err == noErr) { + err = FSMakeFSSpecCompat(vRefNum, dirID, (ConstStr255Param) NULL, + dirSpec); + } + + return err; +} + +/* + *---------------------------------------------------------------------- + * + * FSpSetDefaultDir -- + * + * This function sets the default directory to the directory + * pointed to by the provided FSSpec. + * + * Results: + * The function returns what ever errors HSetVol may encounter. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +FSpSetDefaultDir( + FSSpecPtr dirSpec) /* The new default directory. */ +{ + OSErr err; + + /* + * The following special case is needed to work around a bug + * in the Macintosh OS. (Acutally PC Exchange.) + */ + + if (dirSpec->parID == fsRtParID) { + err = HSetVol(NULL, dirSpec->vRefNum, fsRtDirID); + } else { + err = HSetVol(dirSpec->name, dirSpec->vRefNum, dirSpec->parID); + } + + return err; +} + +/* + *---------------------------------------------------------------------- + * + * FSpFindFolder -- + * + * This function is a version of the FindFolder function that + * returns the result as a FSSpec rather than a vRefNum and dirID. + * + * Results: + * Results will be simaler to that of the FindFolder function. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +OSErr +FSpFindFolder( + short vRefNum, /* Volume reference number. */ + OSType folderType, /* Folder type taken by FindFolder. */ + Boolean createFolder, /* Should we create it if non-existant. */ + FSSpec *spec) /* Pointer to resulting directory. */ +{ + short foundVRefNum; + long foundDirID; + OSErr err; + + err = FindFolder(vRefNum, folderType, createFolder, + &foundVRefNum, &foundDirID); + if (err != noErr) { + return err; + } + + err = FSMakeFSSpecCompat(foundVRefNum, foundDirID, "\p", spec); + return err; +} + +static int +FSpLocationFromPathAlias _ANSI_ARGS_((int length, CONST char *path, + FSSpecPtr fileSpecPtr, Boolean resolveLink)); + +/* + *---------------------------------------------------------------------- + * + * FSpLocationFromPath -- + * + * This function obtains an FSSpec for a given macintosh path. + * Unlike the More Files function FSpLocationFromFullPath, this + * function will also accept partial paths and resolve any aliases + * along the path. + * + * Results: + * OSErr code. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +FSpLocationFromPath( + int length, /* Length of path. */ + CONST char *path, /* The path to convert. */ + FSSpecPtr fileSpecPtr) /* On return the spec for the path. */ +{ + return FSpLocationFromPathAlias(length, path, fileSpecPtr, TRUE); +} + +/* + *---------------------------------------------------------------------- + * + * FSpLLocationFromPath -- + * + * This function obtains an FSSpec for a given macintosh path. + * Unlike the More Files function FSpLocationFromFullPath, this + * function will also accept partial paths and resolve any aliases + * along the path expect for the last path component. + * + * Results: + * OSErr code. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +FSpLLocationFromPath( + int length, /* Length of path. */ + CONST char *path, /* The path to convert. */ + FSSpecPtr fileSpecPtr) /* On return the spec for the path. */ +{ + return FSpLocationFromPathAlias(length, path, fileSpecPtr, FALSE); +} + +static int +FSpLocationFromPathAlias( + int length, /* Length of path. */ + CONST char *path, /* The path to convert. */ + FSSpecPtr fileSpecPtr, /* On return the spec for the path. */ + Boolean resolveLink) /* Resolve the last path component? */ +{ + Str255 fileName; + OSErr err; + short vRefNum; + long dirID; + int pos, cur; + Boolean isDirectory; + Boolean wasAlias=FALSE; + FSSpec lastFileSpec; + + /* + * Check to see if this is a full path. If partial + * we assume that path starts with the current working + * directory. (Ie. volume & dir = 0) + */ + vRefNum = 0; + dirID = 0; + cur = 0; + if (length == 0) { + return fnfErr; + } + if (path[cur] == ':') { + cur++; + if (cur >= length) { + /* + * If path = ":", just return current directory. + */ + FSMakeFSSpecCompat(0, 0, NULL, fileSpecPtr); + return noErr; + } + } else { + while (path[cur] != ':' && cur < length) { + cur++; + } + if (cur > 255) { + return bdNamErr; + } + if (cur < length) { + /* + * This is a full path + */ + cur++; + strncpy((char *) fileName + 1, path, cur); + fileName[0] = cur; + err = FSMakeFSSpecCompat(0, 0, fileName, fileSpecPtr); + if (err != noErr) return err; + FSpGetDirectoryID(fileSpecPtr, &dirID, &isDirectory); + vRefNum = fileSpecPtr->vRefNum; + } else { + cur = 0; + } + } + + isDirectory = 1; + while (cur < length) { + if (!isDirectory) { + return dirNFErr; + } + pos = cur; + while (path[pos] != ':' && pos < length) { + pos++; + } + if (pos == cur) { + /* Move up one dir */ + /* cur++; */ + strcpy((char *) fileName + 1, "::"); + fileName[0] = 2; + } else if (pos - cur > 255) { + return bdNamErr; + } else { + strncpy((char *) fileName + 1, &path[cur], pos - cur); + fileName[0] = pos - cur; + } + err = FSMakeFSSpecCompat(vRefNum, dirID, fileName, fileSpecPtr); + if (err != noErr) return err; + lastFileSpec=*fileSpecPtr; + err = ResolveAliasFile(fileSpecPtr, true, &isDirectory, &wasAlias); + if (err != noErr) { + /* ignore alias resolve errors on last path component */ + if (pos < length) return err; + else *fileSpecPtr=lastFileSpec; + } + FSpGetDirectoryID(fileSpecPtr, &dirID, &isDirectory); + vRefNum = fileSpecPtr->vRefNum; + cur = pos; + if (path[cur] == ':') { + cur++; + } + } + + if(!resolveLink && wasAlias) + *fileSpecPtr=lastFileSpec; + + return noErr; +} + +/* + *---------------------------------------------------------------------- + * + * FSpPathFromLocation -- + * + * This function obtains a full path name for a given macintosh + * FSSpec. Unlike the More Files function FSpGetFullPath, this + * function will return a C string in the Handle. It also will + * create paths for FSSpec that do not yet exist. + * + * Results: + * OSErr code. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +OSErr +FSpPathFromLocation( + FSSpec *spec, /* The location we want a path for. */ + int *length, /* Length of the resulting path. */ + Handle *fullPath) /* Handle to path. */ +{ + OSErr err; + FSSpec tempSpec; + CInfoPBRec pb; + + *fullPath = NULL; + + /* + * Make a copy of the input FSSpec that can be modified. + */ + BlockMoveData(spec, &tempSpec, sizeof(FSSpec)); + + if (tempSpec.parID == fsRtParID) { + /* + * The object is a volume. Add a colon to make it a full + * pathname. Allocate a handle for it and we are done. + */ + tempSpec.name[0] += 2; + tempSpec.name[tempSpec.name[0] - 1] = ':'; + tempSpec.name[tempSpec.name[0]] = '\0'; + + err = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]); + } else { + /* + * The object isn't a volume. Is the object a file or a directory? + */ + pb.dirInfo.ioNamePtr = tempSpec.name; + pb.dirInfo.ioVRefNum = tempSpec.vRefNum; + pb.dirInfo.ioDrDirID = tempSpec.parID; + pb.dirInfo.ioFDirIndex = 0; + err = PBGetCatInfoSync(&pb); + + if ((err == noErr) || (err == fnfErr)) { + /* + * If the file doesn't currently exist we start over. If the + * directory exists everything will work just fine. Otherwise we + * will just fail later. If the object is a directory, append a + * colon so full pathname ends with colon, but only if the name is + * not empty. NavServices returns FSSpec's with the parent ID set, + * but the name empty... + */ + if (err == fnfErr) { + BlockMoveData(spec, &tempSpec, sizeof(FSSpec)); + } else if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) { + if (tempSpec.name[0] > 0) { + tempSpec.name[0] += 1; + tempSpec.name[tempSpec.name[0]] = ':'; + } + } + + /* + * Create a new Handle for the object - make it a C string. + */ + tempSpec.name[0] += 1; + tempSpec.name[tempSpec.name[0]] = '\0'; + err = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]); + if (err == noErr) { + /* + * Get the ancestor directory names - loop until we have an + * error or find the root directory. + */ + pb.dirInfo.ioNamePtr = tempSpec.name; + pb.dirInfo.ioVRefNum = tempSpec.vRefNum; + pb.dirInfo.ioDrParID = tempSpec.parID; + do { + pb.dirInfo.ioFDirIndex = -1; + pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID; + err = PBGetCatInfoSync(&pb); + if (err == noErr) { + /* + * Append colon to directory name and add + * directory name to beginning of fullPath. + */ + ++tempSpec.name[0]; + tempSpec.name[tempSpec.name[0]] = ':'; + + (void) Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1], + tempSpec.name[0]); + err = MemError(); + } + } while ( (err == noErr) && + (pb.dirInfo.ioDrDirID != fsRtDirID) ); + } + } + } + + /* + * On error Dispose the handle, set it to NULL & return the err. + * Otherwise, set the length & return. + */ + if (err == noErr) { + *length = GetHandleSize(*fullPath) - 1; + } else { + if ( *fullPath != NULL ) { + DisposeHandle(*fullPath); + } + *fullPath = NULL; + *length = 0; + } + + return err; +} + +/* + *---------------------------------------------------------------------- + * + * GetGlobalMouseTcl -- + * + * This procedure obtains the current mouse position in global + * coordinates. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +GetGlobalMouseTcl( + Point *mouse) /* Mouse position. */ +{ + EventRecord event; + + OSEventAvail(0, &event); + *mouse = event.where; +} + +pascal OSErr FSpGetDirectoryIDTcl (CONST FSSpec * spec, + long * theDirID, Boolean * isDirectory) +{ + return(FSpGetDirectoryID(spec, theDirID, isDirectory)); +} + +pascal short FSpOpenResFileCompatTcl (CONST FSSpec * spec, SignedByte permission) +{ + return(FSpOpenResFileCompat(spec,permission)); +} + +pascal void FSpCreateResFileCompatTcl ( + CONST FSSpec * spec, OSType creator, + OSType fileType, ScriptCode scriptTag) +{ + FSpCreateResFileCompat (spec,creator,fileType,scriptTag); +} diff --git a/mac/tcltkMacBuildSupport.sea.hqx b/mac/tcltkMacBuildSupport.sea.hqx new file mode 100644 index 0000000..0f39e28 --- /dev/null +++ b/mac/tcltkMacBuildSupport.sea.hqx @@ -0,0 +1,3970 @@ +(This file must be converted with BinHex 4.0) +:'(4ME(4V6@&M3R9TE'46GA"`Eh*d,R0PB3""8&"-BA9cG#!!!!&F-!!"NlA#He0 +dG@CQ5A3J+'-T-6Nj0bda16Ni)%&XB@4ND@iJ8hPcG'9YFb`J5@jM,L`JD(4dF$S +[,hH3!bjKE'&NC'PZFhPc,Q0[E5p6G(9QCNPd,`d+'J!&%!!"A$!!N!0b!!%!N!0 +b+G30TD95CA0PFRCPC+@P!+@3"!%!!%3!4,GD)HHjbMAT!*!0&'J"!*!$fJ!E+'8 +!!9Y@!!*dBfadDdeKBd*eD@aN8h9`F'pbG!!!dCB!mJ'%!Y8$FJ(!rj!%!Klrq2r +`bd!!!)!!N!3",JZPN!3"!!!e!!#h@L(RYeSKj`#3!h)!!!(%!*!$FJ!&Qcd!N!j +*BfpZ$3!"fipTBfpZ68&$8d%!N!q!!*!*!HB!N!1$!*!%$`"#`G4pkZC)Fdpk#E" +3,[R(401[QF!K8#m3cp6`UEN(l!F(kE$mF3UPMQ%Cji[M2`6'l@`N5(%)p!jpec$ +iXXJI,GDb'reFiYi1&`4QA#$cfMm*U3HrSJU)q3Y-NAL(FV%[6'K([88c@M9ehcr +RHNJC`RVr%f@Ki9pVfJ5KjNGZr)mi!+@3"!%!!$d!4,5KD[qjbM&r!*!$fJ!"A!# +3"()!$B+V!!!#*3!E*Rm!!9RE!"48Bf`[9'XJ4QpXC'9b!!$YV!