diff options
Diffstat (limited to 'mac')
45 files changed, 22794 insertions, 0 deletions
diff --git a/mac/AppleScript.html b/mac/AppleScript.html new file mode 100644 index 0000000..4a73fbb --- /dev/null +++ b/mac/AppleScript.html @@ -0,0 +1,298 @@ +<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 delete </B><I>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>delete </B><I>scriptName</I> + <BR> + <DD> + This deletes the script data compiled into the script scriptName, + and frees up all the resources associated with it. + <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..8c4409d --- /dev/null +++ b/mac/Background.doc @@ -0,0 +1,92 @@ +Notes about the Background Only application template +==================================================== + +SCCS: @(#) Background.doc 1.1 97/11/03 17:05:54 + +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.pch b/mac/MW_TclAppleScriptHeader.pch new file mode 100644 index 0000000..9575a8d --- /dev/null +++ b/mac/MW_TclAppleScriptHeader.pch @@ -0,0 +1,46 @@ +/* + * 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. + * + * SCCS: @(#) MW_TclAppleScriptHeader.pch 1.1 97/09/09 16:38:07 + */ + +/* + * 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" +#include "MW_TclHeaderPPC" +#elif __CFM68K__ +#pragma precompile_target "MW_TclAppleScriptHeaderCFM68K" +#include "MW_TclHeaderCFM68K" +#else +#pragma precompile_target "MW_TclAppleScriptHeader68K" +#include "MW_TclHeader68K" +#endif + + +#define TCL_REGISTER_LIBRARY 1 +/* + * Place any includes below that will are needed by the majority of the + * and is OK to be in any file in the system. The pragma's are used + * to control what functions are exported in the Tcl shared library. + */ + +#pragma export on +#pragma export off + diff --git a/mac/MW_TclHeader.pch b/mac/MW_TclHeader.pch new file mode 100644 index 0000000..6a27544 --- /dev/null +++ b/mac/MW_TclHeader.pch @@ -0,0 +1,112 @@ +/* + * 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. + * + * SCCS: @(#) MW_TclHeader.pch 1.27 97/11/20 18:45:25 + */ + +/* + * 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 + +/* + * 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. + */ + +#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 + +/* + * The following defines control the behavior of the Macintosh + * Universial Headers. + */ + +#define SystemSevenOrLater 1 +#define STRICT_CONTROLS 1 +#define STRICT_WINDOWS 1 + +/* + * 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 a while, we will continue to use the old routine names, so that + * people with older versions of CodeWarrior will still be able to compile + * the source (albeit they will have to update the project files themselves). + * + * At some point, we will convert over to the new routine names. + */ + +#define OLDROUTINENAMES 1 + +/* + * Place any includes below that will are needed by the majority of the + * and is OK to be in any file in the system. The pragma's are used + * to control what functions are exported in the Tcl shared library. + */ + +#pragma export on +#include "tcl.h" +#include "tclMac.h" +#include "tclInt.h" +#pragma export off + diff --git a/mac/README b/mac/README new file mode 100644 index 0000000..81cdac6 --- /dev/null +++ b/mac/README @@ -0,0 +1,187 @@ +Tcl 8.0p1 for Macintosh + +by Ray Johnson +Sun Microsystems Laboratories +rjohnson@eng.sun.com + +SCCS: @(#) README 1.30 97/11/20 22:01:16 + +1. Introduction +--------------- + +This is the README file for the Macintosh version of the Tcl +scripting language. The file consists of information specific +to the Macintosh version of Tcl. For more general information +please read the README file in the main Tcl directory. + +2. What's new? +-------------- + +The main new feature is the Tcl compilier. You should certainly +notice the speed improvements. Any problems are probably +generic rather than Mac specific. If you have questions or +comments about the compilier feel free to forward them to the +author of the compilier: Brian Lewis <btlewis@eng.sun.com>. +Several things were fixed/changed since the a1 release so be +sure to check this out. + +The largest incompatible change on the Mac is the removal of the +following commands: "rm", "rmdir", "mkdir", "mv" and "cp". These +commands were never really supported and their functionality is +superceded by the file command. + +I've also added in a new "AppleScript" command. This was contributed +by Jim Ingham who is a new member of the Tcl group. It's very cool. +The command isn't actually in the core - you need to do a "package +require Tclapplescript" to get access to it. This code is officially +unsupported and will change in the next release. However, the core +functionality is there and is stable enough to use. Documentation +can be found in "AppleScript.html" in the mac subdirectory. + +The resource command has also been rewacked. You can now read and +write any Mac resource. Tcl now has the new (and VERY COOL) binary +command that will allow you to pack and unpack the resources into +useful Tcl code. We will eventually provide Tcl libraries for +accessing the most common resources. + +See the main Tcl README for other features new to Tcl 8.0. + +3. Mac specific features +------------------------ + +There are several features or enhancements in Tcl that are unique to +the Macintosh version of Tcl. Here is a list of those features and +pointers to where you can find more information about the feature. + +* The "resource" command allows you manipulate Macintosh resources. + A complete man page is available for this command. + +* The Mac version of the "source" command has an option to source from + a Macintosh resource. Check the man page from the source command + for details. + +* The only command NOT available on the Mac is the exec command. + However, we include a Mac only package called Tclapplescript that + provides access to Mac's AppleScript system. This command is still + under design & construction. Documentatin can be found in the mac + subdirectory in a file called "AppleScript.html". + +* The env variable on the Macintosh works rather differently than on + Windows or UNIX platforms. Check out the tclvars man page for + details. + +* The command "file volumes" returns the available volumes on your + Macintosh. Check out the file command for details. + +* The command "file attributes" has the Mac specific options of + -creator and -type which allow you to query and set the Macintosh + creator and type codes for Mac files. See file man page for details. + +* We have added a template for creating a Background-only Tcl application. + So you can use Tcl as a faceless server process. For more details, see + the file background.doc. + +If you are writing cross platform code but would still like to use +some of these Mac specific commands, please remember to use the +tcl_platform variable to special case your code. + +4. The Distribution +------------------- + +Macintosh Tcl is distributed in three different forms. This +should make it easier to only download what you need. The +packages are as follows: + +mactk8.0.1.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-8.0.1.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-8.0.1.sea.hqx + + This release contains the complete source for Tcl 8.0. In + addition, Metrowerks CodeWarrior libraries and project files + are included. However, you must already have the More Files + package to compile this code. + +5. Documentation +---------------- + +The "html" subdirectory contains reference documentation in +in the HTML format. You may also find these pages at: + + http://sunscript.sun.com/man/tcl8.0/contents.html + +Other documentation and sample Tcl scripts can be found at +the Tcl ftp site: + + ftp://ftp.neosoft.com/tcl/ + +The internet news group comp.lang.tcl is also a valuable +source of information about Tcl. A mailing list is also +available (see below). + +6. Compiling Tcl +---------------- + +In order to compile Macintosh Tcl you must have the +following items: + + CodeWarrior Pro 1 + Mac Tcl 8.0 (source) + More Files 1.4.3 + +There are two sets of project files included with the package. The ones +we use for the release are for CodeWarrior Pro 1, and are not compatible +with CodeWarrior Gold release 11 and earlier. We have included the files +for earlier versions of CodeWarrior in the folder tcl8.0:mac:CW11 Projects, +but they are unsupported, and a little out of date. + +As of Tcl8.0p2, the code will also build under CW Pro 2. The only +change that needs to be made is that float.mac.c should be replaced by +float.c in the MacTcl MSL project file. + +However, there seems to be a bug in the CFM68K Linker in CW Pro 2, +which renders the CFM68K Version under CW Pro 2 very unstable. I am +working with MetroWerks to resolve this issue. The PPC version is +fine, as is the Traditional 68K Shell. But if you need to use the +CFM68K, then you must stay with CW Pro 1 for now. + +The project files included with the Mac Tcl source should work +fine. The only thing you may need to update are the access paths. +Unfortunantly, it's somewhat common for the project files to become +slightly corrupted. The most common problem is that the "Prefix file" +found in the "C/C++ Preference" panel is incorrect. This should be +set to MW_TclHeaderPPC, MW_TclHeader68K or MW_TclHeaderCFM68K. + +To build the fat version of TclShell, open the project file "TclShells.¼", +select the "TclShell" target, and build. All of the associated binaries will +be built automoatically. There are also targets for building static 68K +and Power PC builds, for building a CFM 68K build, and for building a +shared library Power PC only build. + +Special notes: + +* There is a small bug in More Files 1.4.3. Also you should not use + MoreFiles 1.4.4 - 1.4.6. Look in the file named morefiles.doc for + more details. + +* You may not have the libmoto library which will cause a compile + error. You don't REALLY need it - it can be removed. Look at the + file libmoto.doc for more details. + +* Check out the file bugs.doc for information about known bugs. + +If you have comments or Bug reports send them to: +Jim Ingham +jingham@eng.sun.com + diff --git a/mac/bugs.doc b/mac/bugs.doc new file mode 100644 index 0000000..5f4d45e --- /dev/null +++ b/mac/bugs.doc @@ -0,0 +1,32 @@ +Known bug list for Tcl 8.0 for Macintosh + +by Ray Johnson +Sun Microsystems Laboratories +rjohnson@eng.sun.com + +SCCS: @(#) bugs.doc 1.6 97/08/13 18:09:12 + +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! + diff --git a/mac/libmoto.doc b/mac/libmoto.doc new file mode 100644 index 0000000..50b98e1 --- /dev/null +++ b/mac/libmoto.doc @@ -0,0 +1,39 @@ +Notes about the use of libmoto +------------------------------ + +@(#) libmoto.doc 1.1 96/07/17 14:29:48 + +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..b7c7118 --- /dev/null +++ b/mac/morefiles.doc @@ -0,0 +1,74 @@ +Notes about MoreFiles, dnr.c & other non-Tcl source files +--------------------------------------------------------- + +@(#) morefiles.doc 1.4 97/08/13 12:57:08 + +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..f1f36e5 --- /dev/null +++ b/mac/porting.notes @@ -0,0 +1,23 @@ +Porting Notes +------------- + +@(#) porting.notes 1.5 96/07/31 14:59:28 + +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..eec480c --- /dev/null +++ b/mac/tclMac.h @@ -0,0 +1,101 @@ +/* + * 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. + * + * SCCS: @(#) tclMac.h 1.8 97/06/24 18:59:08 + */ + +#ifndef _TCLMAC +#define _TCLMAC + +#ifndef _TCL +# include "tcl.h" +#endif +#include <Types.h> +#include <Files.h> +#include <Events.h> + +/* + * "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 + +typedef int (*Tcl_MacConvertEventPtr) _ANSI_ARGS_((EventRecord *eventPtr)); + +/* + * This is needed by the shells to handle Macintosh events. + */ + +EXTERN void Tcl_MacSetEventProc _ANSI_ARGS_((Tcl_MacConvertEventPtr procPtr)); + +/* + * These routines are useful for handling using scripts from resources + * in the application shell + */ + +EXTERN char * Tcl_MacConvertTextResource _ANSI_ARGS_((Handle resource)); +EXTERN int Tcl_MacEvalResource _ANSI_ARGS_((Tcl_Interp *interp, + char *resourceName, int resourceNumber, char *fileName)); +EXTERN Handle Tcl_MacFindResource _ANSI_ARGS_((Tcl_Interp *interp, + long resourceType, char *resourceName, + int resourceNumber, char *resFileRef, int * releaseIt)); + +/* These routines support the new OSType object type (i.e. the packed 4 + * character type and creator codes). + */ + +EXTERN int Tcl_GetOSTypeFromObj _ANSI_ARGS_((Tcl_Interp *interp, + Tcl_Obj *objPtr, OSType *osTypePtr)); +EXTERN void Tcl_SetOSTypeObj _ANSI_ARGS_((Tcl_Obj *objPtr, + OSType osType)); +EXTERN Tcl_Obj * Tcl_NewOSTypeObj _ANSI_ARGS_((OSType osType)); + + + +/* + * The following routines are utility functions in Tcl. They are exported + * here because they are needed in Tk. They are not officially supported, + * however. The first set are from the MoreFiles package. + */ + +EXTERN pascal OSErr FSpGetDirectoryID(const FSSpec *spec, + long *theDirID, Boolean *isDirectory); +EXTERN pascal short FSpOpenResFileCompat(const FSSpec *spec, + SignedByte permission); +EXTERN pascal void FSpCreateResFileCompat(const FSSpec *spec, + OSType creator, OSType fileType, + ScriptCode scriptTag); +/* + * Like the MoreFiles routines these fix problems in the standard + * Mac calls. These routines is from tclMacUtils.h. + */ + +EXTERN int FSpLocationFromPath _ANSI_ARGS_((int length, char *path, + FSSpecPtr theSpec)); +EXTERN OSErr FSpPathFromLocation _ANSI_ARGS_((FSSpecPtr theSpec, + int *length, Handle *fullPath)); + +/* + * These are not in MSL 2.1.2, so we need to export them from the + * Tcl shared library. They are found in the compat directory + * except the panic routine which is found in tclMacPanic.h. + */ + +EXTERN int strncasecmp _ANSI_ARGS_((CONST char *s1, + CONST char *s2, size_t n)); +EXTERN int strcasecmp _ANSI_ARGS_((CONST char *s1, + CONST char *s2)); +EXTERN void panic _ANSI_ARGS_(TCL_VARARGS(char *,format)); + +#pragma export reset + +#endif /* _TCLMAC */ diff --git a/mac/tclMacAETE.r b/mac/tclMacAETE.r new file mode 100644 index 0000000..17fb6fe --- /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. + * + * SCCS: @(#) tclMacAETE.r 1.1 97/11/03 17:06:22 + */ + +#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..59d1417 --- /dev/null +++ b/mac/tclMacAlloc.c @@ -0,0 +1,340 @@ +/* + * 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. + * + * SCCS: @(#) tclMacAlloc.c 1.13 97/07/24 14:42:19 + */ + +#include "tclMacInt.h" +#include "tclInt.h" +#include <Memory.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. */ + +/* + * Amount of space to leave in the application heap for the Toolbox to work. + */ + +#define TOOLBOX_SPACE (32 * 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. */ + + +/* + * 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; +} ListEl; + +ListEl * systemMemory = NULL; +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; + + hand = * (Handle *) ((Ptr) oldPtr - sizeof(Handle)); + maxsize = GetHandleSize(hand) - sizeof(Handle); + if (maxsize < size) { + newPtr = TclpSysAlloc(size, 1); + memcpy(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; + + 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) { + HPurge(toolGuardHandle); + } + } + + /* + * If we got the handle, lock it and do our allocation. + */ + + if (toolGuardHandle != NULL) { + HLock(toolGuardHandle); + hand = NewHandle(size + sizeof(Handle)); + HUnlock(toolGuardHandle); + } + } + if (hand != NULL) { + newMemoryRecord = (ListEl *) NewPtr(sizeof(ListEl)); + if (newMemoryRecord == NULL) { + DisposeHandle(hand); + return NULL; + } + newMemoryRecord->memoryHandle = hand; + newMemoryRecord->next = appMemory; + appMemory = newMemoryRecord; + } else { + /* + * Ran out of memory in application space. Lets try to get + * more memory from system. Otherwise, we return NULL to + * denote failure. + */ + isBin = 0; + hand = NewHandleSys(size + sizeof(Handle)); + if (hand == NULL) { + return NULL; + } + if (systemMemory == NULL) { + /* + * This is the first time we've attempted to allocate memory + * directly from the system heap. We need to now install the + * exit handle to ensure the memory is cleaned up. + */ + TclMacInstallExitToShellPatch(CleanUpExitProc); + } + newMemoryRecord = (ListEl *) NewPtrSys(sizeof(ListEl)); + if (newMemoryRecord == NULL) { + DisposeHandle(hand); + return NULL; + } + newMemoryRecord->memoryHandle = hand; + newMemoryRecord->next = systemMemory; + systemMemory = newMemoryRecord; + } + if (isBin) { + HLockHi(hand); + } else { + HLock(hand); + } + (** (Handle **) hand) = hand; + + return (*hand + sizeof(Handle)); +} + +/* + *---------------------------------------------------------------------- + * + * 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. */ +{ + Handle hand; + OSErr err; + + hand = * (Handle *) ((Ptr) ptr - sizeof(Handle)); + DisposeHandle(hand); + err = MemError(); +} + +/* + *---------------------------------------------------------------------- + * + * 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; + + while (systemMemory != NULL) { + memRecord = systemMemory; + systemMemory = memRecord->next; + DisposeHandle(memRecord->memoryHandle); + DisposePtr((void *) memRecord); + } +} + +/* + *---------------------------------------------------------------------- + * + * 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; + + while (systemMemory != NULL) { + memRecord = systemMemory; + systemMemory = memRecord->next; + DisposeHandle(memRecord->memoryHandle); + DisposePtr((void *) memRecord); + } + while (appMemory != NULL) { + memRecord = appMemory; + appMemory = memRecord->next; + DisposeHandle(memRecord->memoryHandle); + DisposePtr((void *) memRecord); + } +} + +/* + *---------------------------------------------------------------------- + * + * 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..8906270 --- /dev/null +++ b/mac/tclMacAppInit.c @@ -0,0 +1,205 @@ +/* + * 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. + * + * SCCS: @(#) tclMacAppInit.c 1.20 97/07/28 11:03:58 + */ + +#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> +short InstallConsole _ANSI_ARGS_((short fd)); +#endif + +#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[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 interp->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; + } +#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; + 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..90d3456 --- /dev/null +++ b/mac/tclMacApplication.r @@ -0,0 +1,75 @@ +/* + * 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. + * + * SCCS: @(#) tclMacApplication.r 1.2 97/06/20 11:27:07 + */ + +#include <Types.r> +#include <SysTypes.r> + +/* + * The folowing include and defines help construct + * the version string for Tcl. + */ + +#define RESOURCE_INCLUDED +#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 +#else +# define MINOR_VERSION TCL_MINOR_VERSION * 16 +#endif + +resource 'vers' (1) { + TCL_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, 0x00, verUS, + TCL_PATCH_LEVEL, + TCL_PATCH_LEVEL ", by Ray Johnson © Sun Microsystems" +}; + +resource 'vers' (2) { + TCL_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, 0x00, verUS, + TCL_PATCH_LEVEL, + "Tcl Shell " TCL_PATCH_LEVEL " © 1996" +}; + +#define TCL_APP_CREATOR 'Tcl ' + +type TCL_APP_CREATOR as 'STR '; +resource TCL_APP_CREATOR (0, purgeable) { + "Tcl Shell " TCL_PATCH_LEVEL " © 1996" +}; + +/* + * 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", + } +}; diff --git a/mac/tclMacBOAAppInit.c b/mac/tclMacBOAAppInit.c new file mode 100644 index 0000000..db9890b --- /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. + * + * SCCS: @(#) tclMacBOAAppInit.c 1.1 97/11/03 17:06:21 + */ + +#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 interp->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..76689de --- /dev/null +++ b/mac/tclMacBOAMain.c @@ -0,0 +1,360 @@ +/* + * 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. + * + * SCCS: @(#) tclMacBOAMain.c 1.1 97/11/03 17:06:22 + */ + +#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. */ + +#ifdef TCL_MEM_DEBUG +static char dumpFile[100]; /* Records where to dump memory allocation + * information. */ +static int quitFlag = 0; /* 1 means "checkmem" command was called, + * so the application should quit and dump + * memory allocation information. */ +#endif + +/* + * Forward references for procedures defined later in this file: + */ + +#ifdef TCL_MEM_DEBUG +static int CheckmemCmd _ANSI_ARGS_((ClientData clientData, + Tcl_Interp *interp, int argc, char *argv[])); +#endif +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(); +#ifdef TCL_MEM_DEBUG + Tcl_InitMemory(interp); + Tcl_CreateCommand(interp, "checkmem", CheckmemCmd, (ClientData) 0, + (Tcl_CmdDeleteProc *) NULL); +#endif + + /* + * 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, interp->result, -1); + Tcl_DStringAppend(&errStr, "\n", 1); + TclMacDoNotification(Tcl_DStringValue(&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, interp->result, -1); + + TclMacDoNotification(Tcl_DStringValue(&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_GetStringFromObj(objv[1], (int *) NULL)); + return TCL_OK; + +} + + +/* + *---------------------------------------------------------------------- + * + * CheckmemCmd -- + * + * This is the command procedure for the "checkmem" command, which + * causes the application to exit after printing information about + * memory usage to the file passed to this command as its first + * argument. + * + * Results: + * Returns a standard Tcl completion code. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +#ifdef TCL_MEM_DEBUG + + /* ARGSUSED */ +static int +CheckmemCmd(clientData, interp, argc, argv) + ClientData clientData; /* Not used. */ + Tcl_Interp *interp; /* Interpreter for evaluation. */ + int argc; /* Number of arguments. */ + char *argv[]; /* String values of arguments. */ +{ + extern char *tclMemDumpFileName; + if (argc != 2) { + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " fileName\"", (char *) NULL); + return TCL_ERROR; + } + strcpy(dumpFile, argv[1]); + tclMemDumpFileName = dumpFile; + quitFlag = 1; + return TCL_OK; +} +#endif diff --git a/mac/tclMacChan.c b/mac/tclMacChan.c new file mode 100644 index 0000000..b05d2f5 --- /dev/null +++ b/mac/tclMacChan.c @@ -0,0 +1,1356 @@ +/* + * 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. + * + * SCCS: @(#) tclMacChan.c 1.43 97/06/20 11:27:48 + */ + +#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> + +/* + * The following variable is used to tell whether this module has been + * initialized. + */ + +static int initialized = 0; + +/* + * The following are flags returned by GetOpenMode. They + * are or'd together to determine how opening and handling + * a file should occur. + */ + +#define TCL_RDONLY (1<<0) +#define TCL_WRONLY (1<<1) +#define TCL_RDWR (1<<2) +#define TCL_CREAT (1<<3) +#define TCL_TRUNC (1<<4) +#define TCL_APPEND (1<<5) +#define TCL_ALWAYS_APPEND (1<<6) +#define TCL_EXCL (1<<7) +#define TCL_NOCTTY (1<<8) +#define TCL_NONBLOCK (1<<9) +#define TCL_RW_MODES (TCL_RDONLY|TCL_WRONLY|TCL_RDWR) + +/* + * 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; + +/* + * The following pointer refers to the head of the list of files managed + * that are being watched for file events. + */ + +static FileState *firstFilePtr; + +/* + * 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 void FileInit _ANSI_ARGS_((void)); +static int FileInput _ANSI_ARGS_((ClientData instanceData, + char *buf, int toRead, int *errorCode)); +static int FileOutput _ANSI_ARGS_((ClientData instanceData, + 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 int GetOpenMode _ANSI_ARGS_((Tcl_Interp *interp, + char *string)); +static Tcl_Channel OpenFileChannel _ANSI_ARGS_((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, + 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. */ + 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. */ + 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; + +/* + * Static variables to hold channels for stdin, stdout and stderr. + */ + +static Tcl_Channel stdinChannel = NULL; +static Tcl_Channel stdoutChannel = NULL; +static Tcl_Channel stderrChannel = NULL; + +/* + *---------------------------------------------------------------------- + * + * FileInit -- + * + * This function initializes the file channel event source. + * + * Results: + * None. + * + * Side effects: + * Creates a new event source. + * + *---------------------------------------------------------------------- + */ + +static void +FileInit() +{ + initialized = 1; + firstFilePtr = NULL; + Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL); + Tcl_CreateExitHandler(FileChannelExitHandler, NULL); +} + +/* + *---------------------------------------------------------------------- + * + * 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); + initialized = 0; +} + +/* + *---------------------------------------------------------------------- + * + * 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 }; + + if (!(flags & TCL_FILE_EVENTS)) { + return; + } + + /* + * Check to see if there is a ready file. If so, poll. + */ + + for (infoPtr = 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 }; + + 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 = 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; + + 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 = 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; + + /* + * Invalidate the stdio cache if necessary. Note that we assume that + * the stdio file and channel pointers will become invalid at the same + * time. + */ + + fd = (int) ((FileState*)instanceData)->fileRef; + if (fd == 0) { + fd = 0; + stdinChannel = NULL; + } else if (fd == 1) { + stdoutChannel = NULL; + } else if (fd == 2) { + stderrChannel = NULL; + } else { + panic("recieved invalid std file"); + } + + if (close(fd) < 0) { + errorCode = errno; + } + + return errorCode; +} + +/* + *---------------------------------------------------------------------- + * + * CommonGetHandle -- + * + * Called from Tcl_GetChannelFile 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. */ + 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, 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_GetStringFromObj(objv[1], NULL), + 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; +} + +/* + *---------------------------------------------------------------------- + * + * TclGetDefaultStdChannel -- + * + * 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 +TclGetDefaultStdChannel( + 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[20]; + 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: + 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; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_OpenFileChannel -- + * + * Open an File based channel on Unix 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 +Tcl_OpenFileChannel( + Tcl_Interp *interp, /* Interpreter for error reporting; + * can be NULL. */ + char *fileName, /* Name of file to open. */ + char *modeString, /* A list of POSIX open modes or + * a string such as "rw". */ + int permissions) /* If the open involves creating a + * file, with what modes to create + * it? */ +{ + Tcl_Channel chan; + int mode; + char *nativeName; + Tcl_DString buffer; + int errorCode; + + mode = GetOpenMode(interp, modeString); + if (mode == -1) { + return NULL; + } + + nativeName = Tcl_TranslateFileName(interp, fileName, &buffer); + if (nativeName == NULL) { + return NULL; + } + + chan = OpenFileChannel(nativeName, mode, permissions, &errorCode); + Tcl_DStringFree(&buffer); + + if (chan == NULL) { + Tcl_SetErrno(errorCode); + if (interp != (Tcl_Interp *) NULL) { + Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ", + 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( + char *fileName, /* Name of file to open. */ + 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[64]; + + /* + * 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 & (TCL_RDONLY | TCL_WRONLY | TCL_RDWR)) { + case TCL_RDWR: + channelPermissions = (TCL_READABLE | TCL_WRITABLE); + macPermision = fsRdWrShPerm; + break; + case TCL_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 TCL_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 & TCL_CREAT)) { + err = HCreate(fileSpec.vRefNum, fileSpec.parID, fileSpec.name, 'MPW ', 'TEXT'); + if (err != noErr) { + *errorCodePtr = errno = TclMacOSErrorToPosixError(err); + Tcl_SetErrno(errno); + return NULL; + } + } else if ((mode & TCL_CREAT) && (mode & TCL_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 & TCL_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->volumeRef = fileSpec.vRefNum; + fileState->fileRef = fileRef; + fileState->pending = 0; + fileState->watchMask = 0; + if (mode & TCL_ALWAYS_APPEND) { + fileState->appendMode = true; + } else { + fileState->appendMode = false; + } + + if ((mode & TCL_ALWAYS_APPEND) || (mode & TCL_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; +} + +/* + *---------------------------------------------------------------------- + * + * 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); + 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. */ + 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 **nextPtrPtr, *ptr; + FileState *infoPtr = (FileState *) instanceData; + int oldMask = infoPtr->watchMask; + + if (!initialized) { + FileInit(); + } + + infoPtr->watchMask = mask; + if (infoPtr->watchMask) { + if (!oldMask) { + infoPtr->nextPtr = firstFilePtr; + firstFilePtr = infoPtr; + } + } else { + if (oldMask) { + /* + * Remove the file from the list of watched files. + */ + + for (nextPtrPtr = &firstFilePtr, ptr = *nextPtrPtr; + ptr != NULL; + nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) { + if (infoPtr == ptr) { + *nextPtrPtr = ptr->nextPtr; + break; + } + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * GetOpenMode -- + * + * Description: + * Computes a POSIX mode mask from a given string and also sets + * a flag to indicate whether the caller should seek to EOF during + * opening of the file. + * + * Results: + * On success, returns mode to pass to "open". If an error occurs, the + * returns -1 and if interp is not NULL, sets interp->result to an + * error message. + * + * Side effects: + * Sets the integer referenced by seekFlagPtr to 1 if the caller + * should seek to EOF during opening the file. + * + * Special note: + * This code is based on a prototype implementation contributed + * by Mark Diekhans. + * + *---------------------------------------------------------------------- + */ + +static int +GetOpenMode( + Tcl_Interp *interp, /* Interpreter to use for error + * reporting - may be NULL. */ + char *string) /* Mode string, e.g. "r+" or + * "RDONLY CREAT". */ +{ + int mode, modeArgc, c, i, gotRW; + char **modeArgv, *flag; + + /* + * Check for the simpler fopen-like access modes (e.g. "r"). They + * are distinguished from the POSIX access modes by the presence + * of a lower-case first letter. + */ + + mode = 0; + if (islower(UCHAR(string[0]))) { + switch (string[0]) { + case 'r': + mode = TCL_RDONLY; + break; + case 'w': + mode = TCL_WRONLY|TCL_CREAT|TCL_TRUNC; + break; + case 'a': + mode = TCL_WRONLY|TCL_CREAT|TCL_APPEND; + break; + default: + error: + if (interp != (Tcl_Interp *) NULL) { + Tcl_AppendResult(interp, + "illegal access mode \"", string, "\"", + (char *) NULL); + } + return -1; + } + if (string[1] == '+') { + mode &= ~(TCL_RDONLY|TCL_WRONLY); + mode |= TCL_RDWR; + if (string[2] != 0) { + goto error; + } + } else if (string[1] != 0) { + goto error; + } + return mode; + } + + /* + * The access modes are specified using a list of POSIX modes + * such as TCL_CREAT. + */ + + if (Tcl_SplitList(interp, string, &modeArgc, &modeArgv) != TCL_OK) { + if (interp != (Tcl_Interp *) NULL) { + Tcl_AddErrorInfo(interp, + "\n while processing open access modes \""); + Tcl_AddErrorInfo(interp, string); + Tcl_AddErrorInfo(interp, "\""); + } + return -1; + } + + gotRW = 0; + for (i = 0; i < modeArgc; i++) { + flag = modeArgv[i]; + c = flag[0]; + if ((c == 'R') && (strcmp(flag, "RDONLY") == 0)) { + mode = (mode & ~TCL_RW_MODES) | TCL_RDONLY; + gotRW = 1; + } else if ((c == 'W') && (strcmp(flag, "WRONLY") == 0)) { + mode = (mode & ~TCL_RW_MODES) | TCL_WRONLY; + gotRW = 1; + } else if ((c == 'R') && (strcmp(flag, "RDWR") == 0)) { + mode = (mode & ~TCL_RW_MODES) | TCL_RDWR; + gotRW = 1; + } else if ((c == 'A') && (strcmp(flag, "APPEND") == 0)) { + mode |= TCL_ALWAYS_APPEND; + } else if ((c == 'C') && (strcmp(flag, "CREAT") == 0)) { + mode |= TCL_CREAT; + } else if ((c == 'E') && (strcmp(flag, "EXCL") == 0)) { + mode |= TCL_EXCL; + } else if ((c == 'N') && (strcmp(flag, "NOCTTY") == 0)) { + mode |= TCL_NOCTTY; + } else if ((c == 'N') && (strcmp(flag, "NONBLOCK") == 0)) { + mode |= TCL_NONBLOCK; + } else if ((c == 'T') && (strcmp(flag, "TRUNC") == 0)) { + mode |= TCL_TRUNC; + } else { + if (interp != (Tcl_Interp *) NULL) { + Tcl_AppendResult(interp, "invalid access mode \"", flag, + "\": must be RDONLY, WRONLY, RDWR, APPEND, CREAT", + " EXCL, NOCTTY, NONBLOCK, or TRUNC", (char *) NULL); + } + ckfree((char *) modeArgv); + return -1; + } + } + ckfree((char *) modeArgv); + if (!gotRW) { + if (interp != (Tcl_Interp *) NULL) { + Tcl_AppendResult(interp, "access mode must include either", + " RDONLY, WRONLY, or RDWR", (char *) NULL); + } + return -1; + } + return mode; +} diff --git a/mac/tclMacDNR.c b/mac/tclMacDNR.c new file mode 100644 index 0000000..b42b4dd --- /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. + * + * SCCS: @(#) tclMacDNR.c 1.2 97/01/28 10:37:21 + */ + +#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..afb6028 --- /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. + * + * SCCS: @(#) tclMacEnv.c 1.29 96/12/06 14:19:57 + */ + +#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..f5f403d --- /dev/null +++ b/mac/tclMacExit.c @@ -0,0 +1,317 @@ +/* + * 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. + * + * SCCS: @(#) tclMacExit.c 1.6 97/11/20 18:37:38 + */ + +#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 +TclPlatformExit( + int status) /* Ignored. */ +{ + TclMacExitHandler(); + ExitToShell(); +} + +/* + *---------------------------------------------------------------------- + * + * 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..0dcac1c --- /dev/null +++ b/mac/tclMacFCmd.c @@ -0,0 +1,1408 @@ +/* + * tclMacFCmd.c -- + * + * Implements the Macintosh specific portions of the file manipulation + * subcommands of the "file" command. + * + * 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: @(#) tclMacFCmd.c 1.22 97/05/20 15:44:26 + */ + +#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> + +/* + * Callback for the file attributes code. + */ + +static int GetFileFinderAttributes _ANSI_ARGS_((Tcl_Interp *interp, + int objIndex, char *fileName, + Tcl_Obj **attributePtrPtr)); +static int GetFileReadOnly _ANSI_ARGS_((Tcl_Interp *interp, + int objIndex, char *fileName, + Tcl_Obj **readOnlyPtrPtr)); +static int SetFileFinderAttributes _ANSI_ARGS_((Tcl_Interp *interp, + int objIndex, char *fileName, + Tcl_Obj *attributePtr)); +static int SetFileReadOnly _ANSI_ARGS_((Tcl_Interp *interp, + int objIndex, char *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 + +/* + * Global variables for the file attributes code. + */ + +char *tclpFileAttrStrings[] = {"-creator", "-hidden", "-readonly", + "-type", (char *) NULL}; +CONST TclFileAttrProcs tclpFileAttrProcs[] = { + {GetFileFinderAttributes, SetFileFinderAttributes}, + {GetFileFinderAttributes, SetFileFinderAttributes}, + {GetFileReadOnly, SetFileReadOnly}, + {GetFileFinderAttributes, SetFileFinderAttributes}}; + + +/* + * Prototypes for procedure only used in this file + */ + +static pascal Boolean CopyErrHandler _ANSI_ARGS_((OSErr error, + short failedOperation, + short srcVRefNum, long srcDirID, + StringPtr srcName, short dstVRefNum, + long dstDirID,StringPtr dstName)); +OSErr FSpGetFLockCompat _ANSI_ARGS_((const FSSpec *specPtr, + Boolean *lockedPtr)); +static OSErr GenerateUniqueName _ANSI_ARGS_((short vRefNum, + long dirID1, long dirID2, Str31 uniqueName)); +static OSErr GetFileSpecs _ANSI_ARGS_((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)); + +/* + *--------------------------------------------------------------------------- + * + * TclpRenameFile -- + * + * 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 +TclpRenameFile( + char *src, /* Pathname of file or dir to be renamed. */ + char *dst) /* New pathname for file or directory. */ +{ + FSSpec srcFileSpec, dstFileSpec, dstDirSpec; + OSErr err; + long srcID, dummy; + Boolean srcIsDirectory, dstIsDirectory, dstExists, dstLocked; + + err = FSpLocationFromPath(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 (TclpRemoveDirectory(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, + 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; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpCopyFile -- + * + * 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 +TclpCopyFile( + char *src, /* Pathname of file to be copied. */ + char *dst) /* Pathname of file to copy to. */ +{ + OSErr err, dstErr; + Boolean dstExists, dstIsDirectory, dstLocked; + FSSpec srcFileSpec, dstFileSpec, dstDirSpec, tmpFileSpec; + Str31 tmpName; + + err = FSpLocationFromPath(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, 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; +} + +/* + *--------------------------------------------------------------------------- + * + * 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 +TclpDeleteFile( + char *path) /* Pathname of file to be removed. */ +{ + OSErr err; + FSSpec fileSpec; + Boolean isDirectory; + long dirID; + + err = FSpLocationFromPath(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; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpCreateDirectory -- + * + * 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 +TclpCreateDirectory( + char *path) /* Pathname of directory to create. */ +{ + 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; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpCopyDirectory -- + * + * 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 +TclpCopyDirectory( + char *src, /* Pathname of directory to be copied. */ + char *dst, /* Pathname of target directory. */ + Tcl_DString *errorPtr) /* If non-NULL, initialized DString for + * error reporting. */ +{ + 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, 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, 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_DStringAppend(errorPtr, dst, -1); + } + 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 */ + StringPtr srcName, /* name of source */ + short dstVRefNum, /* volume ref number of dst */ + long dstDirID, /* directory id of dst */ + StringPtr dstName) /* name of dst directory */ +{ + return true; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpRemoveDirectory -- + * + * 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 +TclpRemoveDirectory( + char *path, /* Pathname of directory to be removed. */ + int recursive, /* If non-zero, removes directories that + * are nonempty. Otherwise, will only remove + * empty directories. */ + Tcl_DString *errorPtr) /* If non-NULL, initialized DString for + * error reporting. */ +{ + 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_DStringAppend(errorPtr, path, -1); + } + if (locked) { + FSpSetFLockCompat(&fileSpec); + } + 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, + 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; +} + +/* + *--------------------------------------------------------------------------- + * + * GetFileSpecs -- + * + * Generate a filename that is not in either of the two specified + * directories (on the same volume). + * + * Results: + * Standard macintosh error. On success, uniqueName is filled with + * the name of the temporary file. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static OSErr +GenerateUniqueName( + short vRefNum, /* Volume on which the following directories + * are located. */ + long dirID1, /* ID of first directory. */ + long dirID2, /* ID of second directory. May be the same + * as the first. */ + Str31 uniqueName) /* Filled with filename for a file that is + * not located in either of the above two + * directories. */ +{ + OSErr err; + long i; + CInfoPBRec pb; + static unsigned char hexStr[16] = "0123456789ABCDEF"; + static long startSeed = 248923489; + + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioFDirIndex = 0; + pb.hFileInfo.ioNamePtr = uniqueName; + + while (1) { + startSeed++; + pb.hFileInfo.ioNamePtr[0] = 8; + for (i = 1; i <= 8; i++) { + pb.hFileInfo.ioNamePtr[i] = hexStr[((startSeed >> ((8-i)*4)) & 0xf)]; + } + pb.hFileInfo.ioDirID = dirID1; + err = PBGetCatInfoSync(&pb); + if (err == fnfErr) { + if (dirID1 != dirID2) { + pb.hFileInfo.ioDirID = dirID2; + err = PBGetCatInfoSync(&pb); + } + if (err == fnfErr) { + return noErr; + } + } + if (err == noErr) { + continue; + } + return err; + } +} + +/* + *--------------------------------------------------------------------------- + * + * 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( + 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. */ +{ + char *dirName; + OSErr err; + int argc; + 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. */ + char *fileName, /* The name of the file. */ + Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */ +{ + OSErr err; + FSSpec fileSpec; + FInfo finfo; + + err = FSpLocationFromPath(strlen(fileName), fileName, &fileSpec); + + if (err == noErr) { + err = FSpGetFInfo(&fileSpec, &finfo); + } + + 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; + } + } 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 { + *attributePtrPtr = Tcl_NewOSTypeObj('Fldr'); + } + } + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "couldn't get attributes for file \"", 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. */ + char *fileName, /* The name of the file. */ + Tcl_Obj **readOnlyPtrPtr) /* A pointer to return the object with. */ +{ + OSErr err; + FSSpec fileSpec; + CInfoPBRec paramBlock; + + err = FSpLocationFromPath(strlen(fileName), fileName, &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), + "couldn't get attributes for file \"", 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. */ + char *fileName, /* The name of the file. */ + Tcl_Obj *attributePtr) /* The command line object. */ +{ + OSErr err; + FSSpec fileSpec; + FInfo finfo; + + err = FSpLocationFromPath(strlen(fileName), fileName, &fileSpec); + + if (err == noErr) { + err = FSpGetFInfo(&fileSpec, &finfo); + } + + 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; + } + 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], ": \"", + fileName, "\" is a directory", (char *) NULL); + return TCL_ERROR; + } + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "couldn't set attributes for file \"", 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. */ + char *fileName, /* The name of the file. */ + Tcl_Obj *readOnlyPtr) /* The command line object. */ +{ + OSErr err; + FSSpec fileSpec; + HParamBlockRec paramBlock; + int hidden; + + err = FSpLocationFromPath(strlen(fileName), fileName, &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), + "couldn't set attributes for file \"", fileName, "\": ", + Tcl_PosixError(interp), (char *) NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpListVolumes -- + * + * Lists the currently mounted volumes + * + * Results: + * A standard Tcl result. Will always be TCL_OK, since there is no way + * that this command can fail. Also, the interpreter's result is set to + * the list of volumes. + * + * Side effects: + * None + * + *--------------------------------------------------------------------------- + */ + +int +TclpListVolumes( + Tcl_Interp *interp) /* Interpreter to which to pass the volume list */ +{ + HParamBlockRec pb; + Str255 name; + OSErr theError = noErr; + Tcl_Obj *resultPtr, *elemPtr; + short volIndex = 1; + + 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; + } + + elemPtr = Tcl_NewStringObj((char *) name + 1, (int) name[0]); + Tcl_AppendToObj(elemPtr, ":", 1); + Tcl_ListObjAppendElement(interp, resultPtr, elemPtr); + + volIndex++; + } + + Tcl_SetObjResult(interp, resultPtr); + return TCL_OK; +} + diff --git a/mac/tclMacFile.c b/mac/tclMacFile.c new file mode 100644 index 0000000..3d4a22b --- /dev/null +++ b/mac/tclMacFile.c @@ -0,0 +1,840 @@ +/* + * 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-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: @(#) tclMacFile.c 1.57 97/04/23 16:23:05 + */ + +/* + * 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 <Errors.h> +#include <Processes.h> +#include <Strings.h> +#include <Types.h> +#include <MoreFiles.h> +#include <MoreFilesExtras.h> +#include <FSpCompat.h> + +/* + * Static variables used by the TclMacStat function. + */ +static int initalized = false; +static long gmt_offset; + +/* + * The variable below caches the name of the current working directory + * in order to avoid repeated calls to getcwd. The string is malloc-ed. + * NULL means the cache needs to be refreshed. + */ + +static char *currentDir = NULL; + +/* + *---------------------------------------------------------------------- + * + * TclChdir -- + * + * Change the current working directory. + * + * Results: + * The result is a standard Tcl result. If an error occurs and + * interp isn't NULL, an error message is left in interp->result. + * + * Side effects: + * The working directory for this application is changed. Also + * the cache maintained used by TclGetCwd is deallocated and + * set to NULL. + * + *---------------------------------------------------------------------- + */ + +int +TclChdir( + Tcl_Interp *interp, /* If non NULL, used for error reporting. */ + char *dirName) /* Path to new working directory. */ +{ + FSSpec spec; + OSErr err; + Boolean isFolder; + long dirID; + + if (currentDir != NULL) { + ckfree(currentDir); + currentDir = NULL; + } + + err = FSpLocationFromPath(strlen(dirName), dirName, &spec); + if (err != noErr) { + errno = ENOENT; + goto chdirError; + } + + err = FSpGetDirectoryID(&spec, &dirID, &isFolder); + if (err != noErr) { + errno = ENOENT; + goto chdirError; + } + + if (isFolder != true) { + errno = ENOTDIR; + goto chdirError; + } + + err = FSpSetDefaultDir(&spec); + if (err != noErr) { + switch (err) { + case afpAccessDenied: + errno = EACCES; + break; + default: + errno = ENOENT; + } + goto chdirError; + } + + return TCL_OK; + chdirError: + if (interp != NULL) { + Tcl_AppendResult(interp, "couldn't change working directory to \"", + dirName, "\": ", Tcl_PosixError(interp), (char *) NULL); + } + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * TclGetCwd -- + * + * Return the path name of the current working directory. + * + * Results: + * The result is the full path name of the current working + * directory, or NULL if an error occurred while figuring it + * out. If an error occurs and interp isn't NULL, an error + * message is left in interp->result. + * + * Side effects: + * The path name is cached to avoid having to recompute it + * on future calls; if it is already cached, the cached + * value is returned. + * + *---------------------------------------------------------------------- + */ + +char * +TclGetCwd( + Tcl_Interp *interp) /* If non NULL, used for error reporting. */ +{ + FSSpec theSpec; + int length; + Handle pathHandle = NULL; + + if (currentDir == NULL) { + if (FSpGetDefaultDir(&theSpec) != noErr) { + if (interp != NULL) { + interp->result = "error getting working directory name"; + } + return NULL; + } + if (FSpPathFromLocation(&theSpec, &length, &pathHandle) != noErr) { + if (interp != NULL) { + interp->result = "error getting working directory name"; + } + return NULL; + } + HLock(pathHandle); + currentDir = (char *) ckalloc((unsigned) (length + 1)); + strcpy(currentDir, *pathHandle); + HUnlock(pathHandle); + DisposeHandle(pathHandle); + } + return currentDir; +} + +/* + *---------------------------------------------------------------------- + * + * 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; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_FindExecutable -- + * + * This procedure computes the absolute path name of the current + * application, given its argv[0] value. However, this + * implementation doesn't use of 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. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_FindExecutable( + 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; + + 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); + + tclExecutableName = (char *) ckalloc((unsigned) pathLength + 1); + HLock(pathName); + strcpy(tclExecutableName, *pathName); + HUnlock(pathName); + DisposeHandle(pathName); +} + +/* + *---------------------------------------------------------------------- + * + * TclGetUserHome -- + * + * This function takes the passed in user name and finds the + * corresponding home directory specified in the password file. + * + * Results: + * On a Macintosh we always return a NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +char * +TclGetUserHome( + char *name, /* User name to use to find home directory. */ + Tcl_DString *bufferPtr) /* May be used to hold result. Must not hold + * anything at the time of the call, and need + * not even be initialized. */ +{ + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * TclMatchFiles -- + * + * This routine is used by the globbing code to search a + * directory for all files which match a given pattern. + * + * Results: + * If the tail argument is NULL, then the matching files are + * added to the interp->result. Otherwise, TclDoGlob is called + * recursively for each matching subdirectory. The return value + * is a standard Tcl result indicating whether an error occurred + * in globbing. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- */ + +int +TclMatchFiles( + Tcl_Interp *interp, /* Interpreter to receive results. */ + char *separators, /* Directory separators to pass to TclDoGlob. */ + Tcl_DString *dirPtr, /* Contains path to directory to search. */ + char *pattern, /* Pattern to match against. */ + char *tail) /* Pointer to end of pattern. Tail must + * point to a location in pattern. */ +{ + char *dirName, *patternEnd = tail; + char savedChar; + int result = TCL_OK; + int baseLength = Tcl_DStringLength(dirPtr); + CInfoPBRec pb; + OSErr err; + FSSpec dirSpec; + Boolean isDirectory; + long dirID; + short itemIndex; + Str255 fileName; + + + /* + * Make sure that the directory part of the name really is a + * directory. + */ + + dirName = dirPtr->string; + FSpLocationFromPath(strlen(dirName), dirName, &dirSpec); + err = FSpGetDirectoryID(&dirSpec, &dirID, &isDirectory); + if ((err != noErr) || !isDirectory) { + return TCL_OK; + } + + /* + * 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; + + /* + * Clean up the end of the pattern and the tail pointer. Leave + * the tail pointing to the first character after the path separator + * following the pattern, or NULL. Also, ensure that the pattern + * is null-terminated. + */ + + if (*tail == '\\') { + tail++; + } + if (*tail == '\0') { + tail = NULL; + } else { + tail++; + } + savedChar = *patternEnd; + *patternEnd = '\0'; + + 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. If there are more + * characters to be processed, then ensure matching files are + * directories before calling TclDoGlob. Otherwise, just add + * the file to the result. + */ + + p2cstr(fileName); + if (Tcl_StringMatch((char *) fileName, pattern)) { + Tcl_DStringSetLength(dirPtr, baseLength); + Tcl_DStringAppend(dirPtr, (char *) fileName, -1); + if (tail == NULL) { + if ((dirPtr->length > 1) && + (strchr(dirPtr->string+1, ':') == NULL)) { + Tcl_AppendElement(interp, dirPtr->string+1); + } else { + Tcl_AppendElement(interp, dirPtr->string); + } + } else if ((pb.hFileInfo.ioFlAttrib & ioDirMask) != 0) { + Tcl_DStringAppend(dirPtr, ":", 1); + result = TclDoGlob(interp, separators, dirPtr, tail); + if (result != TCL_OK) { + break; + } + } + } + + itemIndex++; + } + *patternEnd = savedChar; + + return result; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacStat -- + * + * This function replaces the library version of stat. The stat + * function provided by most Mac compiliers is rather broken and + * incomplete. + * + * Results: + * See stat documentation. + * + * Side effects: + * See stat documentation. + * + *---------------------------------------------------------------------- + */ + +int +TclMacStat( + char *path, + struct stat *buf) +{ + HFileInfo fpb; + HVolumeParam vpb; + OSErr err; + FSSpec fileSpec; + Boolean isDirectory; + long dirID; + + err = FSpLocationFromPath(strlen(path), path, &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 && buf != NULL) { + /* + * Files are always readable by everyone. + */ + + buf->st_mode = S_IRUSR | S_IRGRP | S_IROTH; + + /* + * Use the Volume Info & File Info to fill out stat buf. + */ + if (fpb.ioFlAttrib & 0x10) { + buf->st_mode |= S_IFDIR; + buf->st_nlink = 2; + } else { + buf->st_nlink = 1; + if (fpb.ioFlFndrInfo.fdFlags & 0x8000) { + buf->st_mode |= S_IFLNK; + } else { + buf->st_mode |= S_IFREG; + } + } + if ((fpb.ioFlAttrib & 0x10) || (fpb.ioFlFndrInfo.fdType == 'APPL')) { + /* + * Directories and applications are executable by everyone. + */ + + buf->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH; + } + if ((fpb.ioFlAttrib & 0x01) == 0){ + /* + * If not locked, then everyone has write acces. + */ + + buf->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + } + buf->st_ino = fpb.ioDirID; + buf->st_dev = fpb.ioVRefNum; + buf->st_uid = -1; + buf->st_gid = -1; + buf->st_rdev = 0; + buf->st_size = fpb.ioFlLgLen; + buf->st_blksize = vpb.ioVAlBlkSiz; + buf->st_blocks = (buf->st_size + buf->st_blksize - 1) + / buf->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 consistant with + * what is returned from "clock seconds". + */ + if (initalized == false) { + MachineLocation loc; + + ReadLocation(&loc); + gmt_offset = loc.u.gmtDelta & 0x00ffffff; + if (gmt_offset & 0x00800000) { + gmt_offset = gmt_offset | 0xff000000; + } + initalized = true; + } + buf->st_atime = buf->st_mtime = fpb.ioFlMdDat - gmt_offset; + buf->st_ctime = fpb.ioFlCrDat - gmt_offset; + + } + } + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + } + + return (err == noErr ? 0 : -1); +} + +/* + *---------------------------------------------------------------------- + * + * TclMacReadlink -- + * + * This function replaces the library version of readlink. + * + * Results: + * See readlink documentation. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclMacReadlink( + char *path, + char *buf, + int size) +{ + HFileInfo fpb; + OSErr err; + FSSpec fileSpec; + Boolean isDirectory; + Boolean wasAlias; + long dirID; + char fileName[256]; + char *end; + Handle theString = NULL; + int pathSize; + + /* + * Remove ending colons if they exist. + */ + while ((strlen(path) != 0) && (path[strlen(path) - 1] == ':')) { + path[strlen(path) - 1] = NULL; + } + + if (strchr(path, ':') == NULL) { + strcpy(fileName, path); + path = NULL; + } else { + end = strrchr(path, ':') + 1; + strcpy(fileName, end); + *end = NULL; + } + c2pstr(fileName); + + /* + * Create the file spec for the directory of the file + * we want to look at. + */ + if (path != NULL) { + err = FSpLocationFromPath(strlen(path), path, &fileSpec); + if (err != noErr) { + errno = EINVAL; + return -1; + } + } else { + FSMakeFSSpecCompat(0, 0, NULL, &fileSpec); + } + + /* + * 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 -1; + } else { + if (fpb.ioFlAttrib & 0x10) { + errno = EINVAL; + return -1; + } else { + if (fpb.ioFlFndrInfo.fdFlags & 0x8000) { + /* + * The file is a link! + */ + } else { + errno = EINVAL; + return -1; + } + } + } + + /* + * 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) || (pathSize > size)) { + DisposeHandle(theString); + errno = ENAMETOOLONG; + return -1; + } + } else { + errno = EINVAL; + return -1; + } + + strncpy(buf, *theString, pathSize); + DisposeHandle(theString); + + return pathSize; +} + +/* + *---------------------------------------------------------------------- + * + * TclMacAccess -- + * + * This function replaces the library version of access. The + * access function provided by most Mac compiliers is rather + * broken or incomplete. + * + * Results: + * See access documentation. + * + * Side effects: + * See access documentation. + * + *---------------------------------------------------------------------- + */ + +int +TclMacAccess( + const char *path, + int mode) +{ + HFileInfo fpb; + HVolumeParam vpb; + OSErr err; + FSSpec fileSpec; + Boolean isDirectory; + long dirID; + int full_mode = 0; + + err = FSpLocationFromPath(strlen(path), (char *) path, &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; +} + +/* + *---------------------------------------------------------------------- + * + * 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), (char *) 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; +} + +/* + *---------------------------------------------------------------------- + * + * 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; + } +} diff --git a/mac/tclMacInit.c b/mac/tclMacInit.c new file mode 100644 index 0000000..9dc6bd0 --- /dev/null +++ b/mac/tclMacInit.c @@ -0,0 +1,284 @@ +/* + * tclMacInit.c -- + * + * Contains the Mac-specific interpreter initialization functions. + * + * 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. + * + * SCCS: @(#) tclMacInit.c 1.39 97/09/23 13:17:30 + */ + +#include <Files.h> +#include <Gestalt.h> +#include <TextUtils.h> +#include <Resources.h> +#include <Strings.h> +#include "tclInt.h" +#include "tclMacInt.h" + +/* + *---------------------------------------------------------------------- + * + * TclPlatformInit -- + * + * Performs Mac-specific interpreter initialization related to the + * tcl_platform and tcl_library variables. + * + * Results: + * None. + * + * Side effects: + * Sets "tcl_library" & "tcl_platfrom" Tcl variable + * + *---------------------------------------------------------------------- + */ + +void +TclPlatformInit( + Tcl_Interp *interp) /* Tcl interpreter to initialize. */ +{ + char *libDir; + Tcl_DString path, libPath; + long int gestaltResult; + int minor, major; + char versStr[10]; + + /* + * Set runtime C variable that tells cross platform C functions + * what platform they are running on. This can change at + * runtime for testing purposes. + */ + tclPlatform = TCL_PLATFORM_MAC; + + /* + * Define the tcl_platfrom variable. + */ + 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 + + /* + * The tcl_library path can be found in one of two places. As an element + * in the env array. Or the default which is to a folder in side the + * Extensions folder of your system. + */ + + Tcl_DStringInit(&path); + libDir = Tcl_GetVar2(interp, "env", "TCL_LIBRARY", TCL_GLOBAL_ONLY); + if (libDir != NULL) { + Tcl_SetVar(interp, "tcl_library", libDir, TCL_GLOBAL_ONLY); + } else { + libDir = Tcl_GetVar2(interp, "env", "EXT_FOLDER", TCL_GLOBAL_ONLY); + if (libDir != NULL) { + Tcl_JoinPath(1, &libDir, &path); + + Tcl_DStringInit(&libPath); + Tcl_DStringAppend(&libPath, ":Tool Command Language:tcl", -1); + Tcl_DStringAppend(&libPath, TCL_VERSION, -1); + Tcl_JoinPath(1, &libPath.string, &path); + Tcl_DStringFree(&libPath); + Tcl_SetVar(interp, "tcl_library", path.string, TCL_GLOBAL_ONLY); + } else { + Tcl_SetVar(interp, "tcl_library", "no library", TCL_GLOBAL_ONLY); + } + } + + /* + * Now create the tcl_pkgPath variable. + */ + Tcl_DStringSetLength(&path, 0); + libDir = Tcl_GetVar2(interp, "env", "EXT_FOLDER", TCL_GLOBAL_ONLY); + if (libDir != NULL) { + Tcl_JoinPath(1, &libDir, &path); + libDir = ":Tool Command Language:"; + Tcl_JoinPath(1, &libDir, &path); + Tcl_SetVar(interp, "tcl_pkgPath", path.string, + TCL_GLOBAL_ONLY|TCL_LIST_ELEMENT); + } else { + Tcl_SetVar(interp, "tcl_pkgPath", "no extension folder", + TCL_GLOBAL_ONLY|TCL_LIST_ELEMENT); + } + Tcl_DStringFree(&path); +} + +/* + *---------------------------------------------------------------------- + * + * 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; +} + +/* + *---------------------------------------------------------------------- + * + * 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 interp->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. */ +{ + static char initCmd[] = + "if {[catch {source -rsrc Init}] != 0} {\n\ + if [file exists [info library]:init.tcl] {\n\ + source [info library]:init.tcl\n\ + } else {\n\ + set msg \"can't find Init resource or [info library]:init.tcl;\"\n\ + append msg \" perhaps you need to\\ninstall Tcl or set your \"\n\ + append msg \"TCL_LIBRARY environment variable?\"\n\ + error $msg\n\ + }\n}\n\ + if {[catch {source -rsrc History}] != 0} {\n\ + if [file exists [info library]:history.tcl] {\n\ + source [info library]:history.tcl\n\ + } else {\n\ + set msg \"can't find History resource or [info library]:history.tcl;\"\n\ + append msg \" perhaps you need to\\ninstall Tcl or set your \"\n\ + append msg \"TCL_LIBRARY environment variable?\"\n\ + error $msg\n\ + }\n}\n\ + if {[catch {source -rsrc Word}] != 0} {\n\ + if [file exists [info library]:word.tcl] {\n\ + source [info library]:word.tcl\n\ + } else {\n\ + set msg \"can't find Word resource or [info library]:word.tcl;\"\n\ + append msg \" perhaps you need to\\ninstall Tcl or set your \"\n\ + append msg \"TCL_LIBRARY environment variable?\"\n\ + error $msg\n\ + }\n}"; + + /* + * 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. + */ + + 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; + char *fileName; + Tcl_Channel errChannel; + Handle h; + + fileName = Tcl_GetVar(interp, "tcl_rcFileName", TCL_GLOBAL_ONLY); + + if (fileName != NULL) { + Tcl_Channel c; + 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_Write(errChannel, interp->result, -1); + Tcl_Write(errChannel, "\n", 1); + } + } + } + } + Tcl_DStringFree(&temp); + } + + fileName = Tcl_GetVar(interp, "tcl_rcRsrcName", TCL_GLOBAL_ONLY); + + if (fileName != NULL) { + c2pstr(fileName); + h = GetNamedResource('TEXT', (StringPtr) fileName); + p2cstr((StringPtr) fileName); + if (h != NULL) { + if (Tcl_MacEvalResource(interp, fileName, 0, NULL) != TCL_OK) { + errChannel = Tcl_GetStdChannel(TCL_STDERR); + if (errChannel) { + Tcl_Write(errChannel, interp->result, -1); + Tcl_Write(errChannel, "\n", 1); + } + } + Tcl_ResetResult(interp); + ReleaseResource(h); + } + } +} diff --git a/mac/tclMacInt.h b/mac/tclMacInt.h new file mode 100644 index 0000000..d4d43b4 --- /dev/null +++ b/mac/tclMacInt.h @@ -0,0 +1,79 @@ +/* + * tclMacInt.h -- + * + * Declarations of Macintosh specific shared variables and procedures. + * + * 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: @(#) tclMacInt.h 1.24 97/09/09 16:22:01 + */ + +#ifndef _TCLMACINT +#define _TCLMACINT + +#ifndef _TCL +# include "tcl.h" +#endif +#ifndef _TCLMAC +# include "tclMac.h" +#endif + +#include <Events.h> +#include <Files.h> + +#pragma export on + +/* + * Defines to control stack behavior + */ + +#define TCL_MAC_68K_STACK_GROWTH (256*1024) +#define TCL_MAC_STACK_THRESHOLD 16384 + +/* + * 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. + */ +typedef pascal void (*ExitToShellProcPtr)(void); + +/* + * Prototypes for functions found in the tclMacUtil.c compatability library. + */ + +EXTERN int FSpGetDefaultDir _ANSI_ARGS_((FSSpecPtr theSpec)); +EXTERN int FSpSetDefaultDir _ANSI_ARGS_((FSSpecPtr theSpec)); +EXTERN OSErr FSpFindFolder _ANSI_ARGS_((short vRefNum, OSType folderType, + Boolean createFolder, FSSpec *spec)); +EXTERN void GetGlobalMouse _ANSI_ARGS_((Point *mouse)); + +/* + * Prototypes of Mac only internal functions. + */ + +EXTERN void TclCreateMacEventSource _ANSI_ARGS_((void)); +EXTERN int TclMacConsoleInit _ANSI_ARGS_((void)); +EXTERN void TclMacExitHandler _ANSI_ARGS_((void)); +EXTERN void TclMacInitExitToShell _ANSI_ARGS_((int usePatch)); +EXTERN OSErr TclMacInstallExitToShellPatch _ANSI_ARGS_(( + ExitToShellProcPtr newProc)); +EXTERN int TclMacOSErrorToPosixError _ANSI_ARGS_((int error)); +EXTERN void TclMacRemoveTimer _ANSI_ARGS_((void *timerToken)); +EXTERN void * TclMacStartTimer _ANSI_ARGS_((long ms)); +EXTERN int TclMacTimerExpired _ANSI_ARGS_((void *timerToken)); +EXTERN int TclMacRegisterResourceFork _ANSI_ARGS_((short fileRef, Tcl_Obj *tokenPtr, + int insert)); +EXTERN short TclMacUnRegisterResourceFork _ANSI_ARGS_((char *tokenPtr, Tcl_Obj *resultPtr)); + +#pragma export reset + +#endif /* _TCLMACINT */ diff --git a/mac/tclMacInterupt.c b/mac/tclMacInterupt.c new file mode 100644 index 0000000..97620f8 --- /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. + * + * SCCS: @(#) tclMacInterupt.c 1.16 96/12/12 19:22:01 + */ + +#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..c49aae6 --- /dev/null +++ b/mac/tclMacLibrary.c @@ -0,0 +1,241 @@ +/* + * tclMacLibrary.c -- + * + * This file should be included in Tcl extensions that want to + * automatically oepn 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. + * + * SCCS: @(#) tclMacLibrary.c 1.6 97/11/20 19:29:42 + */ + +/* + * Here is another place that we are using the old routine names... + */ + +#define OLDROUTINENAMES 1 + +#include <CodeFragments.h> +#include <Errors.h> +#include <Resources.h> +#include <Strings.h> +#include "tclMacInt.h" + +/* + * 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 == kOnDiskFlat) { + fileSpec = realInitBlkPtr->fragLocator.u.onDisk.fileSpec; + } else if (realInitBlkPtr->fragLocator.where == kOnDiskSegmented) { + 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..b83118d --- /dev/null +++ b/mac/tclMacLibrary.r @@ -0,0 +1,223 @@ +/* + * 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. + * + * SCCS: @(#) tclMacLibrary.r 1.5 97/09/23 12:53:28 + */ + +#include <Types.r> +#include <SysTypes.r> + +/* + * The folowing include and defines help construct + * the version string for Tcl. + */ + +#define RESOURCE_INCLUDED +#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 +#else +# define MINOR_VERSION TCL_MINOR_VERSION * 16 +#endif + +resource 'vers' (1) { + TCL_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, 0x00, verUS, + TCL_PATCH_LEVEL, + TCL_PATCH_LEVEL ", by Ray Johnson © Sun Microsystems" +}; + +resource 'vers' (2) { + TCL_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, 0x00, verUS, + TCL_PATCH_LEVEL, + "Tcl Library " TCL_PATCH_LEVEL " © 1996" +}; + +/* + * 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 " © 1996" +}; + +/* + * 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 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, "History", purgeable) "::library:history.tcl"; +read 'TEXT' (TCL_LIBRARY_RESOURCES + 2, "Word", purgeable,preload) "::library:word.tcl"; + +/* + * 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..060a734 --- /dev/null +++ b/mac/tclMacLoad.c @@ -0,0 +1,245 @@ +/* + * 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-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. + * + * SCCS: @(#) tclMacLoad.c 1.20 97/11/20 18:39:20 + */ + +#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; + +/* + *---------------------------------------------------------------------- + * + * TclLoadFile -- + * + * 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 + * interp->result. + * + * Side effects: + * New binary code is loaded. + * + *---------------------------------------------------------------------- + */ + +int +TclLoadFile( + Tcl_Interp *interp, /* Used for error reporting. */ + char *fileName, /* Name of the file containing the desired + * code. */ + char *sym1, char *sym2, /* Names of two procedures to look up in + * the file's symbol table. */ + Tcl_PackageInitProc **proc1Ptr, + Tcl_PackageInitProc **proc2Ptr) + /* Where to return the addresses corresponding + * to sym1 and sym2. */ +{ + CFragConnectionID connID; + Ptr dummy; + OSErr err; + CFragSymbolClass symClass; + FSSpec fileSpec; + short fragFileRef, saveFileRef; + Handle fragResource; + UInt32 offset = 0; + UInt32 length = kCFragGoesToEOF; + char packageName[255]; + Str255 errName; + + /* + * First thing we must do is infer the package name from the sym1 + * variable. This is kind of dumb since the caller actually knows + * this value, it just doesn't give it to us. + */ + strcpy(packageName, sym1); + *packageName = (char) tolower(*packageName); + packageName[strlen(packageName) - 5] = NULL; + + err = FSpLocationFromPath(strlen(fileName), fileName, &fileSpec); + if (err != noErr) { + interp->result = "could not locate shared library"; + return TCL_ERROR; + } + + /* + * See if this fragment has a 'cfrg' resource. It will tell us were + * 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(&fileSpec, fsRdPerm); + SetResLoad(true); + if (fragFileRef != -1) { + 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(packageName, (char *) srcItem->name + 1, + srcItem->name[0])) { + offset = srcItem->codeOffset; + length = srcItem->codeLength; + } + } + } + /* + * 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); + } + + /* + * Now we can attempt to load the fragement 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. + */ + + c2pstr(packageName); + err = GetDiskFragment(&fileSpec, offset, length, (StringPtr) packageName, + kLoadCFrag, &connID, &dummy, errName); + if (err != fragNoErr) { + p2cstr(errName); + Tcl_AppendResult(interp, "couldn't load file \"", fileName, + "\": ", errName, (char *) NULL); + return TCL_ERROR; + } + + c2pstr(sym1); + err = FindSymbol(connID, (StringPtr) sym1, (Ptr *) proc1Ptr, &symClass); + p2cstr((StringPtr) sym1); + if (err != fragNoErr || symClass == kDataCFragSymbol) { + interp->result = + "could not find Initialization routine in library"; + return TCL_ERROR; + } + + c2pstr(sym2); + err = FindSymbol(connID, (StringPtr) sym2, (Ptr *) proc2Ptr, &symClass); + p2cstr((StringPtr) sym2); + if (err != fragNoErr || symClass == kDataCFragSymbol) { + *proc2Ptr = NULL; + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * 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( + 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/tclMacMSLPrefix.h b/mac/tclMacMSLPrefix.h new file mode 100644 index 0000000..ce569da --- /dev/null +++ b/mac/tclMacMSLPrefix.h @@ -0,0 +1,24 @@ +/* + * tclMacMSLPrefix.h -- + * + * A wrapper for the MSL ansi_prefix.mac.h file. This just turns export on + * after including the MSL prefix file, so we can export symbols from the MSL + * and through the Tcl shared libraries + * + * + * 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. + * + * SCCS: @(#) tclMac.h 1.2 97/03/18 10:58:49 + */ + +#include <ansi_prefix.mac.h> +/* + * "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 diff --git a/mac/tclMacMath.h b/mac/tclMacMath.h new file mode 100644 index 0000000..7ec3257 --- /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. + * + * SCCS: @(#) tclMacMath.h 1.2 97/07/28 11:04:02 + */ + +#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) || defined(__MWERKS__)) +#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..1537f0c --- /dev/null +++ b/mac/tclMacNotify.c @@ -0,0 +1,416 @@ +/* + * 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. + * + * 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. + * + * SCCS: @(#) tclMacNotify.c 1.36 97/05/07 19:09:29 + */ + +#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> + + +/* + * 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 + +/* + * 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)); + +/* + *---------------------------------------------------------------------- + * + * InitNotifier -- + * + * Initializes the notifier structure. + * + * 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. + * + * 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. + */ + + GetGlobalMouse(¤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)) { + GetGlobalMouse(¤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. */ +{ + if (!timePtr) { + notifier.timerActive = 0; + } else { + /* + * Compute when the timer should fire. + */ + + TclpGetTime(¬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_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; + + /* + * 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. + */ + + GetGlobalMouse(¤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); + 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 (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..110cfe2 --- /dev/null +++ b/mac/tclMacOSA.c @@ -0,0 +1,2937 @@ +/* + * 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. + * + * SCCS: @(#) tclMacOSA.c 1.7 97/06/18 14:29:58 + */ + +#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, + char **argv)); +static int tclOSADecompileCmd _ANSI_ARGS_((Tcl_Interp * Interp, + tclOSAComponent *OSAComponent, int argc, + char **argv)); +static int tclOSADeleteCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + char **argv)); +static int tclOSAExecuteCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + char **argv)); +static int tclOSAInfoCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + char **argv)); +static int tclOSALoadCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + char **argv)); +static int tclOSARunCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, + char **argv)); +static int tclOSAStoreCmd _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *OSAComponent, int argc, 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, char **argv)); +static void getSortedHashKeys _ANSI_ARGS_((Tcl_HashTable *theTable, + 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, 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, 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, + char *contextName, OSAID *theContext)); +static void tclOSAAddContext _ANSI_ARGS_((tclOSAComponent *theComponent, + char *contextName, const OSAID theContext)); +static int tclOSAMakeContext _ANSI_ARGS_((tclOSAComponent *theComponent, + char *contextName, OSAID *theContext)); +static int tclOSADeleteContext _ANSI_ARGS_((tclOSAComponent *theComponent, + char *contextName)); +static int tclOSALoad _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *theComponent, char *resourceName, + int resourceNumber, char *fileName,OSAID *resultID)); +static int tclOSAStore _ANSI_ARGS_((Tcl_Interp *interp, + tclOSAComponent *theComponent, char *resourceName, + int resourceNumber, char *fileName,char *scriptName)); +static int tclOSAAddScript _ANSI_ARGS_((tclOSAComponent *theComponent, + char *scriptName, long modeFlags, OSAID scriptID)); +static int tclOSAGetScriptID _ANSI_ARGS_((tclOSAComponent *theComponent, + char *scriptName, OSAID *scriptID)); +static tclOSAScript * tclOSAGetScript _ANSI_ARGS_((tclOSAComponent *theComponent, + char *scriptName)); +static int tclOSADeleteScript _ANSI_ARGS_((tclOSAComponent *theComponent, + 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; + + /* + * 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) { + 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) { + 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, + 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, + 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, + 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) { + resultName = argv[1]; + } 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) { + makeNewContext = false; + } else { + makeNewContext = true; + resultID = kOSANullScript; + } + + /* + * Deal with the augment now... + */ + if (augment && !makeNewContext) { + modeFlags |= kOSAModeAugmentContext; + } + } + + /* + * 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 #%-6d 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, + 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, + 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, + 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 #%-6d 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, + 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, + char **argv) +{ + int tclError = TCL_OK, resID = 128; + char c, autoName[24], + *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; + 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, + 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; + 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, + char **argv) +{ + int tclError = TCL_OK, resID = 128; + char c, *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; + 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.6d", 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 = NewOSAActiveProc(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) { + 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, + 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(24 * sizeof(char)); + 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, + 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, + 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) { + tclOSAAddContext(theComponent, contextName, *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, + char *resourceName, + int resourceNumber, + char *scriptName, + char *fileName) +{ + Handle resHandle; + Str255 rezName; + int result = TCL_OK; + short saveRef, fileRef = -1; + char idStr[64]; + FSSpec fileSpec; + Tcl_DString buffer; + 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; + + Tcl_DStringInit(&buffer); + nativeName = Tcl_TranslateFileName(interp, fileName, &buffer); + if (nativeName == NULL) { + return TCL_ERROR; + } + err = FSpLocationFromPath(strlen(nativeName), nativeName, &fileSpec); + + Tcl_DStringFree(&buffer); + if ((err != noErr) && (err != fnfErr)) { + Tcl_AppendResult(interp, + "Error getting a location for the file: \"", + fileName, "\".", NULL); + return TCL_ERROR; + } + + FSpCreateResFileCompat(&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 = FSpOpenResFileCompat(&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, + char *resourceName, + int resourceNumber, + char *fileName, + OSAID *resultID) +{ + Handle sourceData; + Str255 rezName; + int result = TCL_OK; + short saveRef, fileRef = -1; + char idStr[64]; + FSSpec fileSpec; + Tcl_DString buffer; + char *nativeName; + + saveRef = CurResFile(); + + if (fileName != NULL) { + OSErr err; + + Tcl_DStringInit(&buffer); + nativeName = Tcl_TranslateFileName(interp, fileName, &buffer); + if (nativeName == NULL) { + return TCL_ERROR; + } + err = FSpLocationFromPath(strlen(nativeName), nativeName, &fileSpec); + 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); + } 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, + 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, + 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, + 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); + CallOSAActiveProc(theComponent->defActiveProc, theComponent->defRefCon); + + 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, + 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, + char **argv, + Tcl_DString *scrptData, + AEDesc *scrptDesc) +{ + char * ptr; + int i; + char buffer[7]; + OSErr sysErr = noErr; + + 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) = ' '; + } + } + } + + sysErr = AECreateDesc(typeChar, Tcl_DStringValue(scrptData), + Tcl_DStringLength(scrptData), scrptDesc); + + 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.exp b/mac/tclMacOSA.exp new file mode 100644 index 0000000..4cde512 --- /dev/null +++ b/mac/tclMacOSA.exp @@ -0,0 +1 @@ +Tclapplescript_Init diff --git a/mac/tclMacOSA.r b/mac/tclMacOSA.r new file mode 100644 index 0000000..7975a19 --- /dev/null +++ b/mac/tclMacOSA.r @@ -0,0 +1,76 @@ +/* + * 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. + * + * SCCS: @(#) tclMacOSA.r 1.6 97/11/20 18:40:02 + */ + +#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 0 /* Minor number */ +#define SCRIPT_RELEASE_SERIAL 2 /* Really minor number! */ +#define RELEASE_LEVEL alpha /* alpha, beta, or final */ +#define SCRIPT_VERSION "1.0" +#define SCRIPT_PATCH_LEVEL "1.0a2" +#define FINAL 0 /* Change to 1 if final version. */ + +#if FINAL +# define MINOR_VERSION (SCRIPT_MINOR_VERSION * 16) + SCRIPT_RELEASE_SERIAL +#else +# define MINOR_VERSION SCRIPT_MINOR_VERSION * 16 +#endif + +#define RELEASE_CODE 0x00 + +resource 'vers' (1) { + SCRIPT_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, 0x00, verUS, + SCRIPT_PATCH_LEVEL, + SCRIPT_PATCH_LEVEL ", by Jim Ingham & Ray Johnson © Sun Microsystems" +}; + +resource 'vers' (2) { + SCRIPT_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, 0x00, verUS, + SCRIPT_PATCH_LEVEL, + "Tclapplescript " SCRIPT_PATCH_LEVEL " © 1996-1997" +}; + +/* + * 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.0 [list tclPkgSetup $dir Tclapplescript 1.0 {{Tclapplescript" + ".shlb load AppleScript}}]\n" +}; diff --git a/mac/tclMacPanic.c b/mac/tclMacPanic.c new file mode 100644 index 0000000..13219d8 --- /dev/null +++ b/mac/tclMacPanic.c @@ -0,0 +1,235 @@ +/* + * tclMacPanic.c -- + * + * Source code for the "panic" library procedure used in "Simple Shell"; + * other Mac applications will probably override this with 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. + * + * SCCS: @(#) tclMacPanic.c 1.14 97/11/20 18:41:06 + */ + + +#include <Events.h> +#include <Controls.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" + +/* + * 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) + +/* + * The panicProc variable contains a pointer to an application + * specific panic procedure. + */ + +void (*panicProc) _ANSI_ARGS_(TCL_VARARGS(char *,format)) = NULL; + +/* + *---------------------------------------------------------------------- + * + * Tcl_SetPanicProc -- + * + * Replace the default panic behavior with the specified functiion. + * + * Results: + * None. + * + * Side effects: + * Sets the panicProc variable. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_SetPanicProc(proc) + void (*proc) _ANSI_ARGS_(TCL_VARARGS(char *,format)); +{ + panicProc = proc; +} + +/* + *---------------------------------------------------------------------- + * + * MacPanic -- + * + * Displays panic info.. + * + * Results: + * None. + * + * Side effects: + * Sets the panicProc variable. + * + *---------------------------------------------------------------------- + */ + +static void +MacPanic( + char *msg) /* Text to show in panic dialog. */ +{ + 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; + + + /* + * 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 ((inButton == 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); + } + TextBox(msg, strlen(msg), &textRect, teFlushDefault); + DrawControls(macWinPtr); + EndUpdate(macWinPtr); + } + } + } + + CloseWindow(macWinPtr); + + exitNow: +#ifdef TCL_DEBUG + Debugger(); +#else + abort(); +#endif +} + +/* + *---------------------------------------------------------------------- + * + * panic -- + * + * Print an error message and kill the process. + * + * Results: + * None. + * + * Side effects: + * The process dies, entering the debugger if possible. + * + *---------------------------------------------------------------------- + */ + +#pragma ignore_oldstyle on +void +panic(char * format, ...) +{ + va_list varg; + char errorText[256]; + + if (panicProc != NULL) { + va_start(varg, format); + + (void) (*panicProc)(format, varg); + + va_end(varg); + } else { + va_start(varg, format); + + vsprintf(errorText, format, varg); + + va_end(varg); + + MacPanic(errorText); + } + +} +#pragma ignore_oldstyle reset diff --git a/mac/tclMacPort.h b/mac/tclMacPort.h new file mode 100644 index 0000000..366b7a0 --- /dev/null +++ b/mac/tclMacPort.h @@ -0,0 +1,263 @@ +/* + * 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. + * + * SCCS: @(#) tclMacPort.h 1.75 97/08/11 10:18:07 + */ + +#ifndef _MACPORT +#define _MACPORT + +#ifndef _TCL +#include "tcl.h" +#endif + +#include "tclErrno.h" +#include <float.h> + +/* Includes */ +#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> +/* + * The following definitions are usually found if fcntl.h. + * However, MetroWerks has screwed that file up a couple of times + * and all we need are the defines. + */ +#define O_RDWR 0x0 /* open the file in read/write mode */ +#define O_RDONLY 0x1 /* open the file in read only mode */ +#define O_WRONLY 0x2 /* open the file in write only mode */ +#define O_APPEND 0x0100 /* open the file in append mode */ +#define O_CREAT 0x0200 /* create the file if it doesn't exist */ +#define O_EXCL 0x0400 /* if the file exists don't create it again */ +#define O_TRUNC 0x0800 /* truncate the file after opening it */ + +/* + * MetroWerks stat.h file is rather weak. The defines + * after the include are needed to fill in the missing + * defines. + */ +# include <stat.h> +# ifndef S_IFIFO +# define S_IFIFO 0x0100 +# endif +# ifndef S_IFBLK +# define S_IFBLK 0x0600 +# endif +# ifndef S_ISLNK +# define S_ISLNK(m) (((m)&(S_IFMT)) == (S_IFLNK)) +# endif +# ifndef S_ISSOCK +# define S_ISSOCK(m) (((m)&(S_IFMT)) == (S_IFSOCK)) +# endif +# ifndef S_IRWXU +# define S_IRWXU 00007 /* read, write, execute: owner */ +# define S_IRUSR 00004 /* read permission: owner */ +# define S_IWUSR 00002 /* write permission: owner */ +# define S_IXUSR 00001 /* execute permission: owner */ +# define S_IRWXG 00007 /* read, write, execute: group */ +# define S_IRGRP 00004 /* read permission: group */ +# define S_IWGRP 00002 /* write permission: group */ +# define S_IXGRP 00001 /* execute permission: group */ +# define S_IRWXO 00007 /* read, write, execute: other */ +# define S_IROTH 00004 /* read permission: other */ +# define S_IWOTH 00002 /* write permission: other */ +# define S_IXOTH 00001 /* execute permission: other */ +# endif + +# define isatty(arg) 1 + +/* + * Defines used by access function. This function is provided + * by Mac Tcl as the function TclMacAccess. + */ + +# 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 + +/* + * 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 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) + +/* + * 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 + +#define NO_SYS_ERRLIST +#define WAIT_STATUS_TYPE int + +/* + * Make sure that MAXPATHLEN is defined. + */ + +#ifndef MAXPATHLEN +# ifdef PATH_MAX +# define MAXPATHLEN PATH_MAX +# else +# define MAXPATHLEN 2048 +# endif +#endif + +/* + * The following functions are declared in tclInt.h but don't do anything + * on Macintosh systems. + */ + +#define TclSetSystemEnv(a,b) + +/* + * 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 + +extern char **environ; + +/* + * Prototypes needed for compatability + */ + +EXTERN int TclMacCreateEnv _ANSI_ARGS_((void)); +EXTERN int strncasecmp _ANSI_ARGS_((CONST char *s1, + CONST char *s2, size_t n)); + +/* + * The following declarations belong in tclInt.h, but depend on platform + * specific types (e.g. struct tm). + */ + +EXTERN struct tm * TclpGetDate _ANSI_ARGS_((const time_t *tp, + int useGMT)); +EXTERN size_t TclStrftime _ANSI_ARGS_((char *s, size_t maxsize, + const char *format, const struct tm *t)); + +#define tzset() +#define TclpGetPid(pid) ((unsigned long) (pid)) + +/* + * The following prototypes and defines replace the Macintosh version + * of the POSIX functions "stat" and "access". The various compilier + * vendors don't implement this function well nor consistantly. + */ +EXTERN int TclMacStat _ANSI_ARGS_((char *path, struct stat *buf)); +#define stat(path, bufPtr) TclMacStat(path, bufPtr) +#define lstat(path, bufPtr) TclMacStat(path, bufPtr) +EXTERN int TclMacAccess _ANSI_ARGS_((const char *filename, int mode)); +#define access(path, mode) TclMacAccess(path, mode) +EXTERN FILE * TclMacFOpenHack _ANSI_ARGS_((const char *path, + const char *mode)); +#define fopen(path, mode) TclMacFOpenHack(path, mode) +EXTERN int TclMacReadlink _ANSI_ARGS_((char *path, char *buf, int size)); +#define readlink(fileName, buffer, size) TclMacReadlink(fileName, buffer, size) +#ifdef TCL_TEST +#define chmod(path, mode) TclMacChmod(path, mode) +EXTERN int TclMacChmod(char *path, int mode); +#endif + +/* + * Defines for Tcl internal commands that aren't really needed on + * the Macintosh. They all act as no-ops. + */ +#define TclCreateCommandChannel(out, in, err, num, pidPtr) NULL +#define TclClosePipeFile(x) + +/* + * 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 + +/* + * 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 should really be in tclInt.h, but tclInt.h does + * not include tclPort.h, which includes the "struct stat" definition. + */ + +EXTERN int TclpSameFile _ANSI_ARGS_((char *file1, char *file2, + struct stat *sourceStatBufPtr, + struct stat *destStatBufPtr)) ; + +#endif /* _MACPORT */ diff --git a/mac/tclMacProjects.sit.hqx b/mac/tclMacProjects.sit.hqx new file mode 100644 index 0000000..212d433 --- /dev/null +++ b/mac/tclMacProjects.sit.hqx @@ -0,0 +1,3157 @@ +(This file must be converted with BinHex 4.0) +:%R4ME%eKBe"bEfTPBh4c,R0TG!"6594%8dP8)3#3!`*39!#3"(@,8dP8)3!"!!* +39(*-BA8#!*!%&J!!)#!1G'0X6@&M8(*[DQ9MG(-!N"'MU!#3!p%"J!*L!hF!N!- +(!*!2KJ!!!J$rN!3$!+df8!b`K1qP!*!&$&1Y!*!&!Nr1rpIrq`#3"Tfh!!d23Np +"Ae4ME&0SC@aXFbkj!*!3IPF!N"%@!!![#`#3!aErN!4069"b3eG*43%!VllJ3l# +%lG3!N!8"UPS!N!BZ&3!!BV3!N!CrmJB!kh(#A3JIG)6GpKBPYieXKYrQ8hkaMLE +2)i``3VPN(l19QGqQFLURPpb,r$jfDiCR+Y1h[AU8HjYe2GZ%fbAKC*maF[YXB%F +fiB4h$Q+EeE(YNAeq4RKQ6ik6jdrB(K#fb6EGK'GQN[fkMr#1l)'%Aa6Sm!Hd!Uc +(,MplhDIbmcBMqmKY`XNQ[mNQQp`QYmPqm6`+X(jR$I)kXr20JH`FYFpciANj@0J +qHrCjA@cKTPY[DfFPffaV(5[GCPSA[r)k[Nc-M"I2kc+rXc-[-ehXq"@rHXqV-c1 +q[#i$hR@m%H6,HjhIQBfd33-6!![JV-[VE0LJAfZcVENK)pbHl$MCj"D6,3#Fi$X +!,pp"&AbrC)a*ihr%T$kb!IprEXa,,d6mQK31`CGT409+r&q&#hr%Q*-8VhcFQ)j +ZM3YTI2[ZD-qhqQ2K-hFm)5TG$R*qFQTGkT!!p,P2pDBMCFE(kfPI'8ra)p3E(rP +Hmf&MhM$1RdjK8X*Cf0Zec*pZfI@Pc%(Qd!5!+Pc(I!*cG2rd+(2)P+aIb4Im[6h +U""C06pf@AQ22j5BUmil8pe1E$cV"c%3EEM6Q*pPNLNQM8iUK%$-+Z1KhQ+!ATkj +2E8NrXQf#JYi8F#iGYF@fa[I3Y6Bc$ImMD6CS5G3Ph`#L9A%PAlcq&QSkq%lKE%M +Xl(TjqVXBrqf+DCE80N@a0q)rUpM[4"fhh`Ia2dfa[m1r)*!!BMdc&&Z%`dc&(Sr +$BBTY`Q'@BXr$BECLeq"`Z'+rUVNKp[Xic&(XicJFUGKri6"A)%L2Me,X1"b19Q` +0$[-8Z`L(qBVp)!i,&(Xe$Pf+rCVQKGJIip#Yf0rLd#0SB6Ck&9Z-3jpLMm8KTpL +j10"rBXr&JAZ)r4J1rBUp!BF"aAiEKd(&rKm13iVp-`l$JQ(,H@f-[La4M*PqK'* +2aZ'4LJ&Gd&eLe(fXBY5p9,&hik"a`m3qM!1c3qaIF(K8('[Rh"k['$@IS0LT1$a +DXA0`1&%a-1dNaDl'i@6&IPli3qbG1(bRBTUrYl8VpSmi2#D1G4"(Q#PLc!2e%L2 +RFBTY`!&X)FCm'mc8X4IKX&bakh"BSGJ[il"5XIILF*TLp'#9BTr!BE9LAm,Kp$L +@*6DGS4Kcm%6&U"qF)FEmICGLcm,K6-9q'!I"j5cajLc&IJ@(GBVp(JkMLYf&`j- +8q`GK0I!,"UaA$!bJrm5B@fSR4YqC4f+F0bU'EJ)Z%32rcP2XTh%!NiQ"IqFV"[i +p4E(2i2$GF@`D-IkTLS'*Qa9McTqQ',hBSYJ1(*kZf%Y`Z%!akYkUf+rMF+&LIiE +$-a3$KjiCakD65fa6$%aNhSR4EfD0'2A$CBKGL3-p*rB+(*kYQ1C)E4A&k,&3[1Q +FqdX9qdmF`'hpc5!IZ%`am)jq%Q2QKEr0B"rK-X5q(iIR+rCD(%4YQR%M$PFS"Yl +3(f+I`Z%&F@`Q1CZ`ZjQXqi@+JF9J'M&`iIX8SpI85`b-%3Bk%ebflCf+86Ic6Za +2F2JKaIiD"qiNGMF1m$lp(8BqKTj'$&laSiU"PF`[-I!#6N'-hVe8-@Ekaa9l"`l +J2l'2i[!baIi0Kjq)Bl2)cH"pa*LYPbX'EPfV',-(KK2M,QDCf!di[&+a2mAK*aA +l0!k[LQ1cb@Pr5M%i'0K,$*l!M"!$"jP6BX`E256f1Kaq4V(I`S%iXBrKm,1+I4Q +(RiYMKj02[Piaq"*mKaMi$ZFL"JE"UBNa*lqJ',cN6BVp!3krU0MIi2$Q1(B%XH' +A&!2lhk)BFh1pBY4+,F6!bVFU"Ll!0BJa$r"VBMIK`"`4qd-F`%CLIiA$fa6lGac +J"IUE3ll0RF6JC$FV"KplZf*J2hK1M(lrKQ,d!#`LaLb"$m6)J3m5!rY[83c1F@X +F1j+Fqef+dA[QN4MF&Hj-$-i'Rb8'2d'[)!B@[dFa-1*h&D1rF%"LGq!!rb)'hlS +YMXfPaS(q3!bZcQ`5!`ZC&f,8qMl&`26E&@21rNJaCJ1Z3BamF*JB@!9fkHmSFM[ +k3!apiIf+`DXrS"Lib)`3Jfr!)iL"JhFUaMbMca#MYJmTKNi$apHIN8FqSKJm!h` +Q"SF$hiM"-p%LL-&ji+6%`1Lr8!bmq%[&k#Qi3Hc[F8#VdGmmkNG`+f,S&r!1B[" +`m)dB(2+6LX&Yi%6%`!PQK"KBJ`j$M0QJRm5B9l"#Ir1T3e%l-I$qXiSaVpa$$+k +'cN!-r[02LS(Gk#h%`!ki*c&Qi&m9JbGp,SiYS"j!R"MDbZF9JaGq36(`liZ+`Ar +3%iJa0r!JB[35M#9'cIm4alTB(cb8'"J2eb#'&S$f43b1+VTQ&hL4NrbkJ$G1fH` +#"LMTIehSYC,qYa!DR0(VL'Np5%Rr@`KG3dRr@`LHV+6r,33I8p,r&Q)@PI5rKF! +M*Ie[)AUYT2pe3ip6d[qkS8mSkArGi#K+qPmhZ,H5rYF0EUHNrh8$`j6d[fjJU*, +qeiej9Y,rHP#cN[lA!ce!5IrV!DiSkAmpQ(8PrDm(2&P*rqX"ae25rhV!2C6d[aj +JST,qe`-X80,rHY%l*If[&pU2N[lA#mkKT2reJJFSkAqpi$T'rb5QF89*rqX&2e( +5rhU"+dVkAbpQ@dRrkd12P25r2Xk0k(ppd*+8p,mqD"p+qPmIZ,U5rYF(VULNrr@ +"cbMTIhh!8bAp,iFj9p,rFZL&N[kA!bG@d[pbd'Z8p,mFm&K*rmYaEN6rbi'R+HP +r1A!'*Id["ha8d[m@BBD9p,p&a"M4raDaPk,r,B,ZSk6r,B+qS+6r,3+r9G,r&S& +I+HPrLm!(P25raF!JSbd6dr1MT2mY4Vk5rVFBHTD5rVFBr%a*reX-IUDNrbeQ,dA +r@mbj&2f289A5rrSa`dVkAcma4[5rIZKC5[TI2rXRqPmrCe(d[hj`5#ApVapF48R +r'b#ZL2ihJ0P@d[m'L-'Lr`e`eNAr'i$1SU6r$8!M80,r"SL4S[m0%,G%raX%2LV +TIi1B95ApEa#DKj,q0`K0bQMba$5290,r"SRaS[m0JPXUkAq$i$C+qYm3F&P*raX +#lLMTId1B'5ApE`KR*Ie[#&U4N[ih"%e(5ImE)Yk,rMF%lUfNr`f"CbVTId2J5dV +khc$l+[VI-,&Ip,pKBTMSIm2SYC,q0`bHT+6r$82R8Y,rKU(&+1Pr`p!4P25rBH+ +Ck(r$a$24rdE!6j6d[a(@,IVI#$$,H"Q)kIP8d[p'8(r$EHBlq-!,qNhYm"2`!*` +"JjJ*CSFHd60`(Vi"$f!'dI1B$l4+F"01!1D!4q!(H!,@J!(-1l-0AX,Mi$0`+AJ ++Fi8H!Jiccr"#H!4m"Vi)Ii3AJBAJ*"J+4i3(JHp`9VJ'(!$-KNI!(q!+i$Di$pl +$#q!5m!Ei#,`)MJXIJ4I"0H&dm%#i)p`%rL0BDc4dH"*F$ii(Gi4$J5R-0p`6[JI +f``8J"I"DZ"ki#Hq%4m1h`94i(c`@VJQRJ#H!Sq!NQ!L1JQ0J(eS[I-(jEm"'H#R +B!aD"1q!0A"JH$Ym&Rq(RF('`8AKGr+&MDJkDK8CL+,[Sb-926`QZ*bdK'`dKIp* +q+I[PAC3FXM49@@P"rq[5TPa-Zlr(ieZHbj%2AV9NPhrUSAU#B0NY4Gq5+UI*N68 +lZhMTSN+Tfmd2Zl9F6M4ar"-,QIRd2-jHQ$mRRbRPJm!0@PGlBK,KYkNRjbcX1)Y +SU1qGPR9V)kl6pmrC#HD'*Q6QphBY+Bak`lQD[f,a!&VRr&jfc&2bp-9Z3`I"`,+ +$,b$)@Eq!8-S!2i!Hi"dS"GU!DC&f2!DIUjXHZGl`d40k3mrS&IfMPr5$[SMmj%( +iQ)@4JpN@5!B!IH`80Q3K'M5-rb(m$q*r%IiAilmIrchicq)IVEFArdIMIcVqCq0 +r&[ke69$6P`GK1E4Y&J$kQp!@fbIc-"I$LQKiYJ26"h-pVG@DZpb6aD@%PR+5XNb +h+-FB)lrfKIG55pi0jcj'8Im[`EmH6#0dCI(IMrm"r!rLIaMr2IM[aIpdr2GC"f5 +a8EkIEr25)-cArN8,I3#IqJ1-1P0`IZ!M8Z&)%13$%J)"J@"!,"cjJ3a"1"`'qK8 +qqN4rc2R'Y3p"Iqq6PbRm()[c[(hhU4)G)SlLM3p3-L5A2J6GiTZFdRK8,&%9!Ua +MN!"arCr#re(iRiGrc0XCr-r(rdcm(iEr,[bhilm$reRm6m2r,2`IBDE)0[Brhrm +a)FhDeqB,RDZmX$YI'5kj%U+rrL@E%&!3Ll`jT[J(ek!m)(aj"0BHq!-@0&Kec+' +HrCaZ-QHEEV*b3AE@E*Nl'PmXC34kKA4qkP2QUY4A8SqPYfjIjB#P$LXG&MUXFpM +4X0aKRF-bKe81bak@2+ab@15`aQ'*``U("3lV'jBhV'jBh,#CB@R$bSD&69[lX!4 +K"F)#K28(5a'@)D`r@(k`qQ!j`P+%e3H,$pBH,$eBHE$`B0h"XS09"iX1eK`X18` +!&KaM68,kaRk![)lXM68!'4YE!TB&V!IB%*!!bV%[B#r!QS`P'ANF1c)fC14dl!F +!!*L$C1lNF[!&H4ej(SX$9JGNFf4ij(HX#eMZX0TKXF0DKf825al@1K%*M+92@rE +-)1[CaMU(C3kV("BjV(&BiV$#BB($qSEP$DZEXEKKKp1f!Da#@)5`"Q%j`P+%03K +,%&BJ,%PBMV!#B3(#qS2P"kX2&KqX29KkX2*JiF'kJf8(U`i@(@0G`P+024Tl*YC +T,0PB$V&1BjR')STP'dXf0PJXdPK8X83M(f#"a[U-j4RE*4CRV-hB)V%bBf('XSa +p'GXbGQ9Xb[3C134T""A8kE$B*V"E3$L3!%kJD0JVN!"8X)9J-d&D!G@a*cMC"6X +&&JcX'PJSX%0J+A&,EV"bB09)C"Xi)VCGl,VBG,(R`Yq`p@,2aAD,(4GV*lCIl,K +B9V()BV[&KSR0&RXYYPVXY0KSXFpLQm8ZLde@fiHa(@)ha'D)[4$E)VC%l)AB#V% +6BQ[%YSLG%"XKpN&XJpJ&X3PL$m3@L"d3'b$f2farf2f`q4Rl)lB8l"hB9E$RS$5 +$S9KbX1eJTm(ZSZmfpKGp[eN6J)9@ef&@!m"jG)h'QS1G"SX-eKKX--j5!b(%iS+ +0"RX0PL(X,9KCX1KJ)mFqMQdFHc%fG'cQf&La8@-2akD1$4el1&CG,-rB`,&rBr( +'lSh0'hXhYQlXh0LiX@pMfmEZKqd31aff6@b"@#UaMfQ1J!d-qaFf0'aQf,q`I@( +h`ZD&[3YE&hBZE&cBYl"YBGI#TS8p#eZ@XDYK0B,*B6Z#Bf(Y`DD$&3QE&0BJV$i +)j9KkX"Za6S%e#Q!Y+a5`$X2XX%KKMF*bK8d+1a(f+#aAf++`,f'C`MU&93PVNl- +ZlIN15Z3H")&'UM3R`j&eDd!+3p6G'!4H$6F233+$F4kZl$eBE0Y0KVdKj1,lqUe +["Cm19"mPQ1TeX#pUKkp'-p@cGH5!9kk@A!G55'jHAmF*F*!!L2RBIYrM"FABa-@ +5Nc38#+-db6)'Vm'RQRD[De2hT9-(4Afj#GaQRT0)X9l00p%4ej8CrAep-K,EVlf +VIl$ZMP5(F1VA&pd5"CVXJTlH6[TTj,a%jhkG"h8HdIN42'0XbPH8QTVe3lrQPr* +1VcG8bpHS"U4i%8+XA(K8mm,diGR&ZZSLPr*1TkUEVXZ1eZ@cG$j#qEh+0hNkhU2 +il'CaQCH`[fHlJ6p@+d69TA9E6QIAmq(QVGR$H'[B[S`U-a9$++36-a9IR"MLPEb +2MR,AD1cjmX(BUZDfC2VQGlBi,I-l&RAJ10U6@D69$Vk8Va1DQ5@5QN-#Rb1aSKj +6b9fI[[A-1Y(d!p$,23J)QBdFBaJipE&,%#K@bq"6+@dqd[R'P"VHipBZ5N*i$`, +UmMhh"-A"@[6ae!2h)+$11(!2)LZQdpBpL,Cc*[UG[`F"m6`U+dB5+Y3MZmJN9`Y +[B6ee-U'c%"r0`V[GG5-!BDTYhB-)1Zh!2BLTPqqj*qS2BZZ1!rF!mYX1h)-)qm` +$pi#AfrIGil(q[ZP`a)ZifDYiSCF[HDZe+qHqc`2JbYiFZV@b9mQ(rbMr`%II-@m +P6N8NHX6q*!p1MX'VQ(GTYA"P,$PSj3$e3)6-NJ!C#iajD5m(df9H(,`mFQ-ZVeC +l-)K,#ia[F!0&G'BB*,*ZD!@#flfK-XTG1ZcE@&Y2"C2dM(c"4CPY!cepJdYDSDN +[CA4Y2La'jifCrV&+k*AG+'hj'3-p6XEHSPYd,'1k)#RG0@PTc36&CY0(#hkjkT8 +mGpL*EBbQ[hB-)JdcE(,NH9aD,44jc5QYEDe"X93[9Nr!GUq3!!mp[i*kG64UeGV +&LK)FVEP20-$9QM[HQ69le-Ri`kjT@Ja')P&)CaCRPh[dP@'RQ!*pMCN#hC9e!e( +%cT1Z)4LFeIN66[0QcKiq-@26C-ad1'Hc6H+bEBd`4JHGPHXT9reDk#$H0LpI+[P +q"FhFAL9ZV*bCQ6%MbY6"dAa3b*HLk0RZDS2cmU"XPQ6k&LcB9X0SjrC`A211iXQ +!V5@QhF*9$Y0AZ---5,i[1Yk@c5hH(RDGND"dKQLqHT1m[KI)%Nc#j[SNf$a3jV6 +HhLKDQ+``hd@,5+Bc%QM3+4)[iPfpI,NVUMKr2@fa%Q)H+'41BRfad!QE&438`C9 +0&F#6H`cX-B`@-b[P9DIAPN#Ac2e`!jfZkeek%h-$XU($BDi&!dK,rMil*-%9p'F +C@jkr!Y6M9SIPG#ZJ"q"SR1J3NaY0@`hX&4Y'K8Pb-K!"+6")C6%j`HF#VE,Pk-! +@%)J,Jd,0UiEa3136`TUM@ebKq4c6MiYhhK4eUC!!DDDVP&,N06#l&9hJEIUL&9h +ac%IBZk)VCJ8Aq*A!,lQY36Jf&*!!mef-qmrSl6c(VB44R1[AjVd+BZlq&9f2Jd4 +!e&Cd%EUN"kX@GB*8*AF@1C'p85ia`4#2BNb-5JXDiX)aNb9MAFIZ&,Ndl*D4lm- +1VH[B`9b5a+4hmIf*'D$3[8+TX3'0!9hA8AFQeEq1jldU2T@[!d2Krp#5[!*[kbL +A*QYF[6kI,f)$e1'4+J5YeMcdNY'rE8r"jHFiaJ@PhIHlaE9qYip0)#bk#%hS1Q& +QhH#qci3H*"6YeJ'TcRrN&Hc@-*ecNarXeKi!Y*fp3f(`e+'Y6q,c8k`AD$YU3T5 +6,h,3KUXD0L4(F!GX!2r0(Bjm8MD-krI!F3FmBPZHA&$RQrRFZMlqCfIGX1D[G'Z +MJF-YIlQ"BrUG%e0Y'kp*Y5fI1pMRp(FAcE(1!%mV@UE2`6Bc[HCTd4e6Tbmd)mj +Jr,)"B8QTkGr50SR8T[5F$IHBLbkkU2k[lVpkQGQbCF[frpGJHfX8[hlpqPeh(2b +[pV4f9e+lrq(rPTY[6NLSdhhqL,k$$meF,`%qh&-l1f(dM%cYJ0$h&(C!G$bp!d, +I`ik&drEF%a5Rlf((!RYQ@[H``'fe[5Ilp#i'I3p'KkRG$V*NDfTRJlk((3Y61a[ +d25bMQpVCS1pK&m28cJCp$cX@TRBLk([`"drYBY$hS%e-lAD3!0A[8lXLj*k,%Va +h0[J+2NF#f!MK#"8,6aeCJi-iEaV,1TeIK"eNEP8K#h$GqmaBU1[HCmELA2Fq-aC +91SfCjE$ZJ3`XIR@2H'#CUlc2aLc`G&XU@0BTll-a5e6PI6CQ'DUmcmBX)CEhfCJ +PZZjpC[44hQGMhQ-Mll-alk@4ppQBKF$b2K[cIKKjRiejIiZmcmDmI8AHCf2HK5, +[Xc([,C(hf5JZjT6hf5JZYCAhf5JZ+CAhf5JZ(49MRf)[jAdfLXZ8jAdfLJZ6jAd +fLXYrjAdf+IC5hQH6BLrNI6BT,P59*9iT,JD9r4-T1Rj%FdpaNE'mcbE&jFEb2TX +8Ppl+qfc-BQTjadH+#ilPI6EQE@Vb2TX8&kc+qfa5l+@mcbE&aEEb2TX8&4ajRdf ++LjrPI6BT,MU@ppQNL4(b2TXdje$HCj0QIq9p0ZRkQdU)X5K9a2-dPbI,qfc5A0i +Xll0*FqQX[-mQ6Bb4ppQNZ5aB92mdHbC,6p0FZL[[XdPcXE@mcbE0KG6b1SN-F8h +HCj2Kh-VlE$,XPlc2*X1PVI)qQ`bG9[)qQ`aa3YjRNq'LBAQIMAN(Qkc`cR!"XVc +2*N2mNrICC,Lm9YjRNq%LBAQI6BD,J'ArAiBi)HqcbI"p4r)qQhELYEc2TTeB+@X +9fiP0mMkEGQ+"[-qQRA-Pll0TCarPI6EYh(dLll0Tjh*XHCp01aIkb[YXfVRF@Gj +RdmjHb[YXfSN6mMkEGZ+h[-qQR8Z1jAdflF4XHCq0S4,b2TX1iVLmckD$IC,hfCJ +hYiP4Yi0pN[ICG,!AmMkE$Z+"[-qQJrjDHCp0"r&-hQI6`BA5mMkE$Li6&Sp'"rX +Nll2TB*rNI6BGA(iZ'pilb#e%Zmq5fmMlE,,N$')AbE*RmMkE,2&)$+KCcVDmcbE +,1C(hf@4jP[ICC,NB@pE`C,N!@pjRPL8Hb2[-XX3$m3kE0lV9hp@bji022!qNch% +k5r5H[h(YMp$0Br4D+NrEp`+Tbb%UA!P8d$chU)Bklf2lAXKh5m5Sm,dEcpq!0H4 +RL8Ali$k0%+L)Bki+jcE4pm!qae`DqAX!QM(h4IiH@#c2f'RGBjlVdp`iQVm(rM1 +e19AIJ`S`YX%dI`mUja&ll[Q"jMhQf3TIl52Uk'2fE8ie,`M45YKAld%aRA,Ak(Y +38*ZZP+JH&0@Qbb@k"q@Nk9k*lN(aE,TVSRY3CTXE6$haPKG2`QII4S6M*3X#1bX +4c[P[elHVp[B4Yq,@[%*L'b!BB89[E9R@AXiRYf'!`3(lR$L0!m[#85`apEI"JD, +!2E1*!eGJ,DkA$3J-$Kc@a!&[QdaJheIIPQk2Gj'Y(kYb4rSbEDhhUD'"+[(0BL8 +F#$DCqp13!%"aAB&qlFkbjXD`p5&H",'P0Par0m1bk"8%bqCjPAEcaa[mA6LJp3( +$#kG``*bH8bllPIEFf"$H5*(VU44+Bm-*BD6"i%$f,cLJfD1A$!3F$ZMA,#eVjp0 +'YS6*E9B5G64`3$m98Z['DrfDZm$$Uc*bTl5HeRTUdKSL'$i`23&U[h#ZQmp4*', +h#hpP2(iC[6kErF,jH2#Ia&YjZr1V2CfUZ4U2[XNqY(V9V+@9Pp4eS)llqrH0qTV +phhYIYHlQ2II2cB'C(Dr$,rGU1V[0`$ab@l2-f5pk(lBCq14Fl&kiV4Ek-fj&lpX +fJlX5ilBCJ!2hE$0`lQAlTVpPjXqhq,0`J-1d&l82@bfZ6icEDR&AY0A#[VLBEHV +6AJ3[q)qeBK"SL2Q3!(QipFCmVHEjYEql6raC1)"qb'XVr(0EpAec2[4(RC+i"J5 +bPq*KqmC@IIefGAR6ZAl3qE*Fc9rK&N)Rk`8&mhMU4Ur5Rk&+F#N!il)Gb9I-RK@ +Cd%jh+lXcjF(Tj83TYjJjQJm+qG*%UC+Yl@kAH*@4B%IfUQ%[p(GHZGU[l+c4,-0 +[P#mcX'T9@-[[V'rP@"$kCHH0lPYAqVAKRGR,#`8h#*aF2LcZ6&dajT@'Rkk*0lR +AKiI-dl`J(`4ZHHL*8FJrjVfF@'rZbTQC'6-Q4XXQ0bE,[[mpQC943N+eG@41Y)l +d@ZY)(1eFm'4@I9T(-ae"'50hL9[DNAUfZhULda[bY4%hG!EF-0cGDLL4eN%ZV8D +j1iS6!"PR&[%acShJXC3SBe$+FrdZe&r%aQTb--"aXL"MR'1!-Fk)FE(@HB'`1$G +"a6Jj"X8i-mE%1$1#a&*L$#ZPl1Si4AKBD&`"$LZjp@%5-#`-5"i,5iRM2FXMB5N +2)#`ReDBcJX%i-dE"1$-"`3)da"LS-pBAhG)1r!ZbLYJAj%@i&bG*`q-FepH`eL, +@*BR"D!D*JR&"h1"E%)faVG44JD%J,m'd)$('Xb!VaV)J+m+a1#R'JcJc'Bd)Zm, +Q&(#VQ&FH$-'VX1PjV)U6kRh*Be5F!ci9%NV6&H&5N!!9Be+3!*AJ86M'-4DG0(Y +dJT!!&9+,U&4)MU#TR#R0,LHk"KHE8%5U@RB`PB9X`Da#NJ'Z3NU-A[8C%23T*#F +i9XL1`Db3!"SM@L%eJV9bCS`KjI6DE%8S9qaI!HUUbBh*%Y!VcNSHqFUC%kh,Bf! +j%5#XC0@R0B,%3QU-LiA8""b,H"%MT--$Nh@Q&qc86*lBlr493krXVGiPM"L8@6F +@9XHH8Xc1bHl)XPl$Y5kD8b@IFD9PS8%+,5IU1Z1NFTN"1BK,,(!p+Dq3!+4,#a, ++C4A'+UkY!XG5B#969eR)+TGUi@N#U8XAa'"GbNi3SR4"!54+k6(HPl)Mb+mPap" +CZd(3XjEVS,)m&!AiEq3($+#FEdP!+5mHl9*f00hej,''ZI%[*Ai9!4)HfEaJ[0d +4[DNPlaV62-QTjF*ckSR0FBMC6LNl*MbPl#+",q9((,k@h'aP8@jDdI9lbFHP*U+ +2bkYS&LkeT+#ja%3UFRP&X5K+5f5C+$F8CU+XDRY,SP%K-j+0JXa!1()C#GpbH8A +'&DG9QT(RBLkP4[*FFN8-,+C@feI8ZD+djNJ9Y+iS5p5Z1+AFf%6aFRPe(AC&ek4 +qEY2V#VV0(40aEAV$bQ#6krUlcDdTm'&UAFm1ma0&1m`XbGFb!JdP2XSZDr'5A96 +ME9CGHl#j0I8KRpTS8NQEX'N6)SLpB-be%DFhfeXc&S5TNk0B04H%QE'p)*p@Ehl +GBQ"c'm*9(Gf5r)B'@FHh*,qT$9F3,NQZUjJ0M+YH8*)$bbKAQS@QQPR$Z9*kSQK +@N!!Z5Di,94@XUbA(1PB6lC)EaZA`1Yk9mQXDE32aUKFd"V1Xe*C"Vj*E9@XVX"H +V$(ANUe`aVZR9mDpbaB5Z9d("5[kBYYI!`XBG9E@PM)M9-CR3q'Ui@,fKS[09d,' +52kEe96#bQCrSI8fNV&bb8r1VifAeLUEZed$0aKhM)ea6rmV`@8p[+)!9%!ejjEU +1Cj84Qpq3!%CXmVJfB[1EiSM0EUJM0VNZMi5j$9)HAP"Lj@&UQAr,,$3PNLLp)L* +)HL+5f,5'5Q+6kc**2VICUV*3B[-QP4*l`lK8%ZH2YENZPS5j1`Hc*TH%U3@p**r +AQ)+'BQ+6Qj**(IG+&c4&NcVbP5iBNddUf&I+EJJR$I5Vh9"Qk'Am+`r&Q(K53m" +bINNqU@"J+EXKS&43X*jGN!"3QMKBZQ*#4+NMBIQ#ZSc5`-,D$FdaV5JTC6LX*YH +dP!SJ&RKL(41VGdc)+A9NV0ia+DK8m,&k`ELNdN$*jL8e4Pl'bYUm6-SU0F5XA9% +99LUi@EeJA&UTS1IB"59aTBQKe9Yfb5Ye*+hG-5D`02#dHFR%30FePM+`0[+E+NX +&AR@#6KYh2ERFNZh**9Cp6bkhE(abQ5ARNdY-V%p4AXQS%bARR6T4@Z6,#ITFYMm +98Q-$8C!!'KUJA%V*!H85%`Y8R&GY5@5#FMPe&j6,VYUJLVQe0LC'U#K[E-#+9UJ +ScAQKiTa+KdYZ+*FB`j(Iq8ZC`kE'R#-2pm@[Ub1AkhfKd+IKIhQe@NS'U'0YYRI +H[R[dDXYF6jNlN!$TE2f1YERH"3N"G@c-c%m'k1[C!rel+Vb[MJfG5aDe$K90)3M +D61UhDmhKrF2fJXlZpr1i)PmUqAl&kAC,pN@ekTF24c&ea5@AmfcUZ1$CHYFZGQ, +q2$BGVSj(cNJ+U'0A3ljHakk(j2lr1RB@XVq1#eG9cFc9Fq0j[H(Hi[L[jkBCRhX +hhRY`j3B$bQcRpr4SPVF%S`lblEaqGM1@q8NGfricViIAkpJe(Y3KLX0iBfdG@Zc +a"04a`@4R@hfpUPR(*",YVf1hEZ('GI)cGH``aEdiE5@8YJLSA@U#dqGQq[Ec'1j +Br4b6fHSAa[BhEIF(rlJXU$h('ZkTBdHa0,6[(Le&6I'c(H&B3N!GI%Xa*0rUkbM +qKA(R&FcLjrpJh'LQYHmJiak[`a[!Z0I0@qMXZfGYE[(qIRJqQ2YB9`mbpkNkpM( +hU6Vf-IFpGGc$h&YeY*LlHd1e2i#j0rVa)(-IEqapc,e9aclQhLMM!HEHUQ-hFpG +e6*&hbp`[((PKlVdIM&Zlq1Th('6FBhA`H3)BYeiQGF,VR1C@U1)hlDq-5GHa%aR +ZC8a6GHaM1R[UZ)ITY1Ui&mNEG6b!j+dkGS1HVX-X%@XKa&)mR@Bb2kp)6%b)VZ- +j3b[feV&ZD-Aq1UCP*f`BmR9QXjRZjq&[l2XDiQRI'*1iTilYch*&AFGff-bDTUp +JSVHkba-#qRU)-`4FHMFRhep(#cPhceN`(jG2MYRA[KQ4lh!#BfC&kZ10apm&9@P +-KDTqS3VefXZYqZJ&U'0P+3J-)%B$b&JG'Z[TDpYHRGbSKPk!IKLec`Y3Ke2l*Z[ +BVrCY#mXPdlXZVZ2&Qf$YbUDA)(ld@pV5dpkdbI`i2HZ#hr6HQRBfhiXVj`1E2SA +mGkGRREFf2HfGEED1Ch'+a[ZaGmj%CEQSqXbm)V%B)*USBe02cM'YUbIVf0X2Sl* +i!HE9U#aHJ$Vi,*berEZ[TJip(QhrRBqcr)Sl54P80TY,!2$TkRITf)QIT,G(*%m +i(eEf[IXCEFTqEXBQ2m0K[!"eV&P4@pk+rfre&9EUVr!X6Rj9,T4m)4IkpRb%A1M +EmqS2m)SC,R3)2d$J69je[`0REerp%mSeRh#UrqfVU301G@Jq@KD[YU16hcaHNpp +qTZ0AmRJGqMa,(Up,Uj2XY09A(i(,@h[&$RdH%+pB!d!Hm)U0)E8[`#Z@Am&8[b5 +E2-(F24A-e!Z!R(i&T0VlY4EPH%!d(Br,TTc*cd1JaIL"0Z8FqMa,Qh,fDc(e3V` +%'hGfIV*aCqIRhpUidl,rYde0U[$rlf0`rSdp![FbVdBG$c+[mBEFalaDGAJRpL2 +XCele3[`#HaCX(4G1cKP&k0F)@P)`FBmIJ"'B1V`5HarmDCXiec5!VA0p&+Xjr&p +e@446!j[AmHaKdKI[Z'(0YVe1L[&S%fF[hQ8ZHjh8TKZR)[r'e+DEPU8Z1H)d@iI +RDa0R'DGVQcL,11dhhm6T4@$ZIL#ApL4cja2QVLiiFPN#!(2h5fhL2$6h$fXC0R& +ZUG`#9@P-KDTqS3VeLA9@II3#e&(Ha&QZSlCaX+L+'GA3#p!2SrCj!HS)0R&1e,& +Il62Ejpk@DfcL9"HQ[SfihX5TeYe@0HY6epjpifIAT#irR@#L0h(1VL$rUY5eAjq +I@[G$h"GYRa[[apij+frL,0G4fm4CV+1mLI1KIKL9a3X`VdCPm3,8`9IBa2P3Add +Gm5E1KqTiH+@cLI2KD-qE1!r@mA#)C"1Rf3F6IACjM1rRfl`d#216@1*"q*6j2jb +fl2-8qQ!q(+jV1XlUi2rXV"[@r*9ZE65)V*DjJ@2kR406E4Z[5E8YRc[Bjr4h&mf +acJ"2+eUQcc'T-fibjY9h6*fqd)`iJhfD221PR1k0LYb4RV2K(M-q2Kk&p9RGIr@ +bEI(J2iQhmRER9hXk9A-e(Rf6I@MeUPP,+bqTkd!GprI['r8eqlrh[QVGcA[ZRjX +$-cYH4dIb#dHSI5N3HU@Ipq@Efj,TQprCiV6-leM8JH0S6fC45k*+rc0Ecm),&fr +rZ6%RQA)30RAHc0BPf(R6IU`a,f0QhPf9fAVf*i`jrQH%9jejbDkLMXIr#$)[fRE +RfDMJFDeBXb*eTKqdeGaXc*ZrUQVd,IP-,DM03`G5UY0P5Rih$Y1V0@c!5Yhf'X3 +#0&1[af%JcVb&cS58[I3,M6Z2(83XEHp%fESZcVck+TBAC0k0`jSimq6I)GCZ1i! +fG9bFq9+D+6TY*[Y'j-iE[S6BY(TQ1mABZA(QJL-4Qe(26&-,PGjm%P2C0Yefl5@ +0ITji-Q+K2hp6imjVIS$BSRTLkSj'2ppr&f+,lCeX@T6HA2)BBXb1l3!#T[6cQQX +3'l+C+!*bjafh)"D!C2U44Mq[B'a*K"hDY6Bi"p(jj6!JGXJ%kTR@[6`*GAE-,BH +4Cbr)h)4SpmF4dTRErHe65ciGQ)8l3C8i8h&@C!D1Z3qa8fdUfheNEYjq2Q,JJUe +Q9Z21mcq'f12VQFG44jDjH384K58JpXlE'M2`[LX3@fjRMETNEYl)rJ5H[4648Hl +md0X3@f8cIl-a0fqP%RbklGT)ScGhpL"fIMeaN!#EGD5IXlL*k#Pf#N&ZZI-kS'c +EQf`rhpASjq9%SUIDDXL8hR36"cEEL6)Afm`q6RZJqJj4Qj-lAd5m#A6PSHmfq[R +aKBKT-l"Q&hT2ePCLqqTb1,)Gfmr&QqB!Up4*j@U!QFPRDPar&"3TjH9kJp#NMq" +dA9Q[F`(iJ'-AHDC`0FbN60$VJ4"SjJ'UR!1d1r8"K-i3%JPkDlc4H([ZfLB5"CR +RI4(9I,TC6B$iI0h[DL*4F1G'90*,4[*SS9Gmjp!0GR,6Fbj'6$-SQ8*d,CRF'i' +GE9I@-dGjMjhFp0R%0&L*qFiJcj!!+A`('FfEkjR([SFA2cYi!L@ANQHic(!'G#@ +pC$S1Td8ad91SqF2&Vi"jZe26%@5H"hNi4pIZcCU1@'AT[V3j(F'GQfj&qipTBRY +mTpkk&[DQ8!dBda9eZR2DKi!*X#Ich3"eZ(Y0R(N5J%40UfHZ[3bYhaTPCMrj+$, +Re$1r#*jl`XAaRCRr4qD4&YY"Z8amjqI14LE-c(B!C*`IhrRNcb06hfZr(NEXj(D +q%M1NAQicB@kciX`Rh)c-HIA-iiRkYm9eI[$6b03F@qk%ejd5hrR)Eb"cJFh%9Mm +c[[2,$b066k@&kHXZ`H'MmChU8iJYY(FqYh(RYH$MLXKN[Rj1iG,icUGr"TNpp@T +H#6c-a+L5hB)T9+qbdr'HaR6FfSr-J*RfRdGfB+[C#)44PTRfIkr4cp@()M0JTMh +N3BI&QG213@E!6)I1Y"`Lqm#Gb!bBD3qCTFcD@8!Q44CJ[Y2*YGiECmi!qe2(eI[ +jkZXiNlDDflq-@-"QRm4TH'HFH42BRl*XpKLS'M)Gf8I"i4@jQ+RQ0EF$9Ha%GID +FL`5`fRbjecB`UK2)ULclZ`bGl9S9Cji#VUBXqh[5jJC'I36SUZVZ5,8851)`UKH +9+-X,-m3+QF+2JD)TZp@dRbU!aDMXJjJZ4GCS-d0Hf(NbBSV-c(`$4#["U&AX@V! +PiX3[@)cU2!SST+a$IiK)*$0p-QGJV@8al%18k9Mf8@5HCEN+H'%a+[Y"SV(e'R$ +RKJC'h3NqVHSfSlEMhY[!U$QS@4%Ac(FeX&d`U[0dSTHZ6UB$eFNKZ'DrDX"b)K$ +1BP6fDq"8LR@Ell&J*DkI@iNh+%+fdkpVB03MhSI-Ff`eGeQ-kP`$VU-@fcZ[Emc +DQ8"-YF6@q5@,8GRV`"i9')Ep9S1V#dCeRJN'V6M*PK1aGG*L9'F*h%CC&H#%YcH +QihLS0-UU!#qr[i&4XmNl!K9J!@IBBP6f6XbhXLV!GH"%`U1b[`*l9&B&@!1Q)"M +9H43QAGAAKUNCd,S%Sl+hI`i*`@S[44b3!#Rmm%05e@`(8-3%Sfkp(TQ"FT!!CLp +NEVDKYmVZC&c!6JY'c5FV#aD-,!$IGaLe"Ja0fIeqA83%QHNP`&YPpmKNL2mb(DH +#Mb[V2H`&mML-HJZkV+abf881,KMeQ@mL-r#YGAfJ`46H"!9+fDf('pSE21Vc[d" +QS*fQbCEXG(3H6FBBZ+DlS*%i(RAq6j%CU+Spl)$YCrC5GKVfE,k&l+I&U1`Q6+5 +bIUQ&TcCie"Y3Xb)EY0A!j'6@MZ-8"SV[!,XN'28"U+,+1JqA!RNF4Qe$Pj@eKhd +(ka)HGIGhN!!CZ#GlU$l+G,`-+Sd#ca2ddUcABG4dG&QY01Cr!*!$)#!03eFa-5" +3FQpUC@0dF`#3%[IA!*!$i3'3!!*b!iF!N!-4!*!$KJ!"U18!N!-@!!![H`!!!J$ +rN!3$!,#$ZID`JlX)!*!&!r1h!*!&!APUrr$rq!#3"[bX$C!$0MKVT8aTBR*KFRN +ZZ5kj!*!3G0B!N"![#`!!3hN!!#m,rj!%68e38N0A588"!+YZchk`'-Mj!!""!J! +!)e`!!!ZT!!!(jCKeMGB!N!D&K3l!VCPCfU#Adq`k(mr0SZAPZP"1[iZfF[HTVFc +M0$Q98cFG*-pc%hTikXNmfDe[I,AMjaA,h#c+Ml$ERN8jZAhQGA3IXb+FE!Ql1LP +2XXNqNr"1`[B4(YYNRfQAq8Qf%8iii4hKQE&Y%Nliff3&[VUqHZXm0l0EQDeXRad +l`Vrc#+IEc&mNr!$)"ZJ%!Y"[3J!B!(ciL`#cmCqKa'*6XUlr#6l"%iYCXqk`al% +Tr*ilXh,*jI@qMV!FAQDreS3[A#jhSl0eIMA$',(B(r%GCf2`jK8!4b`%U$*9dS2 +$!6j*F3ia!Sb1a5SY$cr$X3c%XZkkJ'-fa+#pPQ1()'CGG4r(LLP'f`-F'dA[6LM +M@!(&qfSAamBJaR)QF'`XaEMQ&a`EKhm6@2!f!X`$)NhQY[M,XB!*!f8JdS1h[mG +-(41b'#%!B"%rGD4)TQaASj%p8HE3KBZ2&q2H`RTBYZN6fm-8c%Bf$()3BQ`T+J@ +CYI0QP*EZ*,AKSV1HR$Z2BT8rQZd0KAh"b#+ljfdG@IPMk`qr[U5jqZ4QPlY*-[, ++b1HMC'KK*-M!5TcaIc0U0)j,j'K528Z8L!-$1**DC--9F)@m+"63Sd-ac)+2343 +q#eqbZEYN6C(+T&28S2+L)Q-Dj@95RGS9e06Jcc6**AZ3!*5UkFN'6mSD131a$aM +!b+1-RIkDll+#5X6EmBlVL-2Mkh-IZ@lBGm8[q#kZK3M&G@&PUH,jVqZfAIDpqRX +&!Dk,Db(QF0drDJbVaN9rehDXr)Pefcjkb,$VaVlJZU3@BJlIVAM0GhQ'ePV%ke2 +rj6j@rQ6IjL-['2EGa&fq5fJKjR$ImYIFCc-8MhK#rmjmV2bTGG[UcaPfhrKGlM1 +d%+Ki,qTpahY2L`HmCfJK32&HEPc[L-rrP[PBq60p0lN1(IEHK%([k9S)85VYCCT +IlIcr5MYf@GA8kD+!5MZKK3$&GpPB@(AkSmUlAG3+k,ZZV&F3d%90DL&#m9ddk&[ +UplhE4f89$20GK5$!GdNY4"bq+`8,cB3NAc%k"%h3Vh3Z[46Jq"Kpr!P0-'4EA'T +3pUJTLPDB!L2B*Y2+a(f(#HP#ibS!-m'frG&"&q-ref#3!(J6J8`qB('Q,r4fFK# +QR&2QZk@kUJ99p-ph+C'`HSB5AU**GI,TXY3dU9%kLTAfAm&+&meTDC!!'QZlB,, +84,Hj*40al15NDh&+k-F&%qZJ8fTT3$U&D-FfNpP8R6'Hf&Rk!3)6%4K(ScL83J$ +1jSPQqbhc-"aX#G#)$GX+,0[4U359X-rMZ09,#96@PNKi2bcKlDh04@aVLaRrL6F +!kbG"hZ1##SG4@E&+Rq8Cd+qhN!$dLfZSiqYUmEkD,35@k3M)(NIl3PEfK4#-(Vf +3!!mIBD*MCEdp-'V93c"qkS0GXFIQi2Z9-,V!#N8rH!SHDUp%["p'AGR0bYC2jVc ++HLp1m#lVVHI[-3i8MD5D)K[a'iK2l,C2pm,$V)dldR69R8Cmc+'ShqEIqM(1rAV +mhS*D-*QK+(FeXL"1"9b2S[aZeJ8MEPi1Rb,m&Rb*SIR-8ZbQMEq%"fjGE@K!4V$ +"eXG@`pC(!EBq83Zr(iYCBM+1RI(hI9K!hF#k`66'JH923j28&!f&e($%8D[)AL@ +X15"hA3AVRN"m'0[8A-V12`)p8V13!&eidKc%h@a6'fPRaEm*mAE)[E5(G8X5BRI +L2k6M8apK&jli)1%82hEEQXq`YAqi"2&%JF)fY4b0H!m(4khGM2KXa2XS[Skh68D +mAqG6I)N4r`ELJhH+4r%IdZ1[Z4raj4cRmFqrciLI`(8p@l#B36iKp()hX&%1KdX +0+c8q[k**apJVl181dBS(-"GD(4ie%*)M$Q&,,X3jbK["8U!R`jQ9*dVe[Z!5*Eb +Mc-Jd)VMGcMp(5*pa9@jhr3I96M[qUd)K[aL!KmY92cFG*`p(AUAj!DS352UABVM +Fp6@#!"kY6QSp#3"dE@aUh%R`G4l8Z-U`Gh4j0+d8f,CZX08PadQY'cKQQb[lrDS +DP'S9Il`9`Llq2)GBdqdmXa-2dliZQXh9+MRrccPYfU!lG"jQZfID0#%!M`blCkm +JVr,Bf`&k9GG3LXJV2*5PS5hIA+RlpDTlH19#H1&G5',CPG#SR-REEYB6U2X"E-Z +h#["p2pYbE6ZlF(4&dUq*TZ"EHK3NH!`eb(+4Ke[@2,*rYl#kVL&C%`A`m+586HY ++T5[aX"#22CRS)!q6IDL1XL!2XQcUk$bd[ARDJM*8D3'HTq02Gq6TNjd0qqZB!JT +5-e6*H&920-dMIDMq#221Z!L!KpEPldM(bA0Ale-9H85LJJ!HG-afG-PlZVV*LqQ +D2Ad%)"8j5[p5M$FUmN%H`S#+['&Z(I@BKr4`Z9[6HSJq+[YGUVj4f3raH+@bhmr +M3'@rApF$PAfDall+RSj4fE0c#pY&!*Ap6Mf'+rY"B9q[l0-m$P6f1fQm@GN2m4! +(92CQZkCd[Zplm8G&MVZ(ASVa4N@qL`FG38"&hZS,PTG*5bYR[Pf4CrqcSN+LHc2 +(`BTUL-FVPG"q23j83QNH"c2p6Kj[C[SK(KRfSDaS44kB'2DAlQBl6RLQ-YP!af+ +h3iL(fV%iaD1KBh'D4lEV2f-H#G&G6RePer[RjBaS4E@i8rIa`!cN6p@D)iQ(ApB +dbBNc36J9pEckFFGMVD!X%J,SqN(0NDc&KfVkJcc5Q6AYXf4'M+4XpVH6`dG%hmY +"HEbl+3HrJefV`5lALbICjEUh*p(G&!,`F2Se$6!$BAiCj*'&Y3)AINqIRRFPK3" +km'kL%)"([*ZBiR'`QjKTliS%r&HlG4lREF(Ajl%l%#mkVC6eE!V"*,BKY2RUPDc +[1%Sfa3$j3AbrNQh`hXCkISAaiM`@SiYfkr'bci`ZMLqdakmCf-**mk!CD+"4mIG +pclXi3J#rmLk1%)!((BX3)*r4-3X"r%&ba%Il6DIrGFKrH+A"FiDJ(ej-c2&@K5) +31@3%mBX(%UF)-VNIk%mhRSKi-X+jB[dP4r-J5`q4H(mM[XM)DIEiTf1MFPmcD2! +FEaaaJ*c2FJQ`ZpRGTPX)ZJBE4f0f6E98iQ5a,QZqUl80&G@RkhG21!`F'be8*C2 +Sbd(BAFmZD)hR'`(3D@[6)R)iY4p@J($)aS@dZB1RaHIH8Uj-*X[[Jf@6%%4a9X4 +A$SYTcVFTbkGA&P-YSXmX-Zcqd*m@Rjb-1`c%"mBGU),qY#)Cl(a"N!$Tq69"Hil +*KV'`bf9m`TG6@1*XQ&GG)TA-UfUZ`UYl[V1j4,4L!CZq!54qRP[1ETVLKU-Sfah +'ESH-d!,H%Cm8#Z-3`J5`ec490f2%bjUD'ir!qpHfmc$hmqBqVcUFa8"EN`S*#ed +#4q0e0Q'0,9L[!r#*fEDI!ifTi'JCCQ-[I"aR)[K!3+J+F&r!b&l#ZXcJ3ia[A6P +V)bc$e5Y6HHbeX!Dai`JEQ3Qi$@Ad0B5&li(,%IX4BE11K'mJpKZ1M3)XkXCQ%hE +5lq#(!-ImP,!9Tm,YL0e0@&5&H`!U@JNEf`%2)RBDB611JDF41j[crLl$6639&a' +fH$P$I@HF5YLCKc2d`ic2%AEfPfP,bSb[%2DKm@`@`,(R%MDpL&8MKLj$99D`"BK +Y*'cQVjQ-E3`qaA$FeaRU2@XYB92'-*bNFr!faV((Xe@)RF@0YTN@R-hQ-6jb$Z# +Z+XEhjKqkNYk0j%[UmpeKGE(LL8MSAmfR"Qeah1A62(Pm3LJaGj*$-f41eDZFS!4 +(*1I*q$-MV,%,FhVBT`6NS+ADkiZSBA10'ScN'r15F6*a[&81"hh"6Xh'1kN'MEb +i#Y9,)f&CbhG'YBJDN!"19*DGSBDpQUh+ie&`)-BY4lSd5l-FlP3LYVP4RppV"-a +Hi-3P5NSN'XUK5A4$X`,#j[Ndl!dVJ3l8B%4b6*h(LE2A"kcL5KP`A#L,XmD&`A* +Sk8bF''(2%NYZT-NL#-IpH$b$S%eII)$k"4@r$E[e#Dlr!`!!$3$VX9lc'QeDjQh +fDGmCjE5hD#FrQqQeCJ2#k8$#2(j'f%@1%caI6rTiTQm%Rr6)bXL)(4R`)jb-'6P +1MK01KS`FLa`R$"mCm#182#I(Rl!MM($##6pbP$$b,)m`bI(c#2H58-)*Hm+12#1 +-X#2(J!!DZ'D%%NkHPRLf!P$m@FF*NMJ!%`qm!JdkMm9908N)#H([1N)fh54Nmf, +-B[Ml"2lqL+qA5*M8emBd1DkQ-NT5N40eRE+8N!!emMSK(hr3mJPj!dM1#`12N!$ +kK6e5['iJRL5K#kICEaIChpA6F$CH6BHFPMA&`R6HF[Yla5`Hp&MGSfTbAEZ5P,2 +EkL)34Y,VYJ!mXip8QBcX&`UEQY36X[i,U"`LBH0mD2JQeCHqUQRP(2f)UK2bQk* +fN3@%e$D3!(EmS`kK(Q'8)%l2rJmKPQ@&aNamq'mUpR[CrVCB@1#8U-PRL4j2GUY +5SPG0biha4a6`9JN@-&aV-UR'jeQm`PNXYPK%KUAd2!QbDBj&b'*4Bl&SkphRN5# +YA!ZE463pjV1Sj9VBLVC(8JQ2aZk+,qLbm9KJJ`BXZY++lV&iKV0BlV$3C@dd8f+ +bTD*&0#HdD1!XPPSXHP9G'6c-H@bSl+kBP&DmhI9La4Ilj+`kUX9,2RfYiSYq06l +LqH+&#SX"*HA[LfFV,0j0+cQ2KA-GcE23PD6(iRmc,($j&&P8DI+3!*c,q+[B4#k +aU)B@VGR$DFrQ&'k1KEh664E"SB-A&9VXPE,qbX&cF1HdS&I+AL8YDI2V$fm[ei* +k0$)LP69elQ&Ad3JSq*X,Kh$!)T9Sl43XD$SJ@(4e#aC29PMd("!XRJVFC5BArMC +'Q['B"Ee5+)YS,U20dpM$@5`#LcC*piA!fmaC9)&&q6$'@bEF&4f6dri&5%LcF&F +d*mG(KDK14Z6kSRbNildU2%U2p&l*2lc@#(Geb)%V#&NS&1f8XX-"$5H"FYh9U@4 +e94`B6V,QqU)VRC!!FhdI(CURdF*Cd&[(M"h%'EL5XeK)@I59c8Aq,hCA9epPBD` +0&1dVRmEN1H(dEL8GR&dXpA5GhJfR#iZ50`-YN!!Q"PSm,9Md5)SI!Z)K9IAF9Bk +'m&E2XCLqX1NqciUYdmDeS$XpF"CpEiQY%j1dE(#qe3JYBL0$C5hU1!Zkdf0U9XR +ekpipqRc!3T1cXMEQLET4l)ZB9JjN#'d@Z#aSQJJaPI53!1r8YrQqS"kP`CYR#Vb +)8,4b&m"!`ZR[5B%5N!"qKS@6V$J88fJk$0+L3f0#j94f2@4K1cfVDqQiP*AM+@q +6ETeMBEYV@CZ#'JJp4b0UKZreE9b,DRUNq`6-YjfcU'R[ceM&$jp'SkI&D"*,A3m +1p4fFaA,D4QQ6Xb1kQZNC+MRHDDBm9*5b-#XY[KK0rhM8r+b'd`Ie)(Zfd&fRVf" +D4(1k*Me@C+H$Dk@f)LNfMRep+A4d)2Vq!*d!dd39b6#`,M28bpLc$*9RZ2R`cYm +Y9$U"`Jb9*jc!QQ5SNdJSLkKZSTPI-2UYMBS*%XNLUTYJ'RrqX-I@&4-N5TD(r!6 ++1(laTSf+#4)N%e8N6R$1GHDKkdL-6&54-)(K,FEe&M)-aT9R(Q$SS'+#HjLjJGr +2B(LEFEf0Dj&ajGHPFHlF1cBU*VM)'#Uri)al8adf+LDi`*Mcq-8'EjpL,Mq&I*@ +KmMc@q2,XpcCA6*!!iM2RmG3IcVR#2(3&,3rcZK5Y%12Bq(i(GA`r@KdcU)m-N3m +hMcNlVhN-R5S69A5``2!JihS3ZCqTUmJ*iEFmFeiH594KSj55+qcL+ED9Tp#'-e& +&Hmii2KPb0XTN#-fUSK[F*KBmN!"JENJJRf+kmMc,Q1KU#"daZ@+#M*NTa$0Tk,, +%4X8%UDH*+P*5E*pa#j91N!"bX[h+8e%ijbmE&41deBTZF0YYaVPIepS+BB,f51' +b+E90M&pqEV94-8(6UFM9E8BC&cipDD0LJYkBL5TkCXC%kSEM[030j'CX+r1F$Aj +EbTbh&!NAmbY2a)aMhfb`86&"%l+SUpZFa%DBB,YK!Zd1Y[0i'`3-9c'ZUj!!3KI +fDbQe"YC'KVS4q9p"ee*H#+ae$(8GfS1QVU*YL,drk+#1$k)99h5$fk,$9Y@F2G5 +X)89QEZ#T-h4Cca4DMcDJb9@d"k(,!DE3!I3CL`Ujr8GJ(@+SKmM@&M-G%4NDX0B +`e$9N@i[*9@4Q*mr@G&LSG%+f@kJL)jY&EE4345BfLlV$iHTPB,1S,eQS)[1D4@f +b&I)cVPR8R4DU5+0Q8AICZm&26Sd6AqQfAc&"ciPG0V`ACC`iFYj"2A)Hl@lE$9i +E2"rHIFICHE[[S)GGh&jZR4(l8@@E8N9,HHCLHh6T'5H1jKf&MZE4Q@,lPAHXN!# +jh(2ZSB[hd*-YAJ"ZVpDiIqN2'a86G1J+f8DTFiIlmbkl41qL'mC3HCF-@0FBkM8 +dB4JUEmiB2ehrcPB)%c3mf(A)H`E)icTY9%c316"448F"ZYaR#Ye(&j!!h9km1`K +GcM#&cU#&a9"jD`YA8"1lKjV3TLpQ0Qll(TG*MYdS1I45f8h*Hkcjm#lLk)UD3D5 +`+ARA![[a+YZ89p(3+H3ST8D2-IAC'cBU*QMa&K,L8N-@ZS5C3Q(56Lqfe1IrCZ% +@+Tf%kf-*c0(+VQf)G@Ia[d#B[m%UApN*ljq1Y5HcdlLdl4q1pFY$$ak`(q%h)8* +r3j[3r`%!!!d0$cBiDk96D@e`E'98Bf`ZZ3#3%!E$!*!3,hX!!&&h!!![#rq3"%e +08&*$9dP&!3#VEXprX!1PB!!!-k-!!!I`!!!+Y3!!!YN"qhUm!*!'L-m0`+hAQEh +-NjQYl19'`fq@C02M0&[CZQQhfDClhB`FmeUCPmQTR%US9alKCi5H'Cl6NpfDcr& +Aj@qfbDCF3VH%pHPHb@h@a3CZBj&pNLhKX51FFX)PYpRAJ"&+'1'a66E,cCj`XMh +###HFF,,2kpKQ(D'FENBifICNNld#Ar8eF,000VZYlmPAYXq,59MN0M[#pT-Y18k +HK5FEH$f"!23kH`!'!*CQJ*RiVl9&)S9Thpq26c!L%AhD#qBB9XLrXqH8,R[-kHd +)qN5AafIHUF%h$VF8FLjbI8,*G!Vp5r41U,)Sb+a[PY)RCVSRJYrMS`R'45+PZQ0 +riTJ@XE6$AqDB!6&SVZ,BH-6dDelPf%6+dA58BcRdEQ%*aib8EeXlabBJaM,c16D +*FMcp%XIbm&m$1[c+J"5UK'K-ifAapp#""K0T%3RMecpc$SF'@@5S!,#)468TNZS +f"cT$&l+8d8G+p#2kAFr#,&hcqC0jc+#2)U-k'iBb@8$(X*2%i%L%84#8Vha5E0` +)-#p#2rk%0%cAf30qYbF`c&82KC!!`ISdAI([SH"XU&d"#d%UViY9hU#+3+fTP-4 +YLaa#GIRLF[V2YSXK+A#h+#f6K@VhFVIJR&SRh-`X[BmcbjePpE9#A98l6"1Fp$@ +rB-TFB,IZa1DqccLP'YU%qPUNNiY&Z%T6`3jUJl!+Y$GX`$H6m$q21LM92F!AHAF +imD8[a(iNB)C8Z!-BXmkHaZTbYc,TU`13!2I,PhLra0*QdN0'',pT0daqjR[YNEI +,Q,3HHIeL,BcIfS2jeL*Z!92RGLCYr"E[!G*$TRKRN!$@lk2hP!GbIdfUT#0H4R` +LchqP"ika*Uk'jXNASrQaIf$rf[Zk$r1d4r1(Ud#6!VQ[l8!@a#R-pFMpeAE@$KR +eqf!0i6rZBP+Xh8DHqml,V1JR[+YL[Yh8-CRjf#[-XVq4@CiVCM0cd#P0H6LUP`$ +Ta@@X(eLkY8hdLj,ABch8LNqeERQCJ0q6ihVfZdbX[ci&rq0[!"kG#PQr8kRS-K' +-0BS01DlI*KIT&p03`4pjLrAhk0N5B+R@$VI(fV#DP@`1`VKa5q*pS+3R$$PV"Z$ +'SMGi(bMCh!AMM(S`rHL2-0$3L(J[j'cYCL@26Z1m5RTiI8@a'[iHmi"T,$R(G-4 +h%jr6I8$*2pJ(5MBI8I,h'(NI-)eCLbb)Nj(VBFVZTMl`dp9`2q%(mH@*2J"($f( +Ai2QS%!c3rrCDr#q'rYm#[$QT&bhTNZMl,C!!$VYC0fJQ@1eZ6ke6F(B'J`%TC+d +5hDfL*&YKc$GQXqjmiX0BRm[#(VJ*Dk4b#9YhDaRL$YEA40T4hp%JhJaM0SCCYb! +JpL,q"a@mk#ffEY(MK&2qb22hV@F2rQB$iR&RaIVULa%2Fc$R`Ef)cd9m#q9Am+C +TL2FUI#CZL1EI6Ac`Qr*4rJ%Prhe(%&r0FClrJ9HMqH1iSQFpF$l"jP+UJ8P$09$ +MEC(FNPH8V@SU0C!!0e3$L-CDJ9T+$D![e&XpJBkJ1f49Er'&hF"bV&Cl3")V[6j +4&MjPRQfH&EF%+LPp!%H"ci,1U$6$1D@h#$9Hrc+4bZTNT%Bc1"bf[f9)4Pkj`e( +cXH[&F5Z8"i-qG3!HGR[0r'5H,,4'`U)1FJJNrCNFGNG0T8S!M`EE!R8!AHZFG@F +*AZ(KUQKdDFdYl4jCYJ$lI6FBUJFRS[SR1'DBlrEj!J'r8#AkBQ0"p[!Q$M(RchK +A)aiDXfH%Vm(H)0KQfUC22eFG#Sm8XfIkG&8!(PUcCd53!!piM%a$2p!e1%cN-Jp +a4I$!GlZ8HRhb&HlH#-mpM#4@EZ@*D'kXAdK6-'!(GKMaI5mlX,1CV4XhHl"HP4@ +!$r8`aRNNKX9MN!#(`belh,lc`LUk"Yfb@J!2cl#b59h*YK%2(I'id)P'H@M-#4q +P3ajeiUU4(!S2HD42ke#'FVQ$pqRBdcrhk8CEl88IBk3NP3NRdaV`G#Cj*)2mKb4 +,#G0`LBIFlQY*jXPb9,bV+[))GDS%m+")-@19I+5VJfSakGQ6S3,Lb&(k-cQZ1[* +c2&3$((RYr'VD08MSBAFd*293qh$flkKkeGNRH&afpKGjA(,f&h@pj1b62#ilHrD +Ph'Be!'Gr9Spr1rYc`PjapNNHPjcp@4VAR(f#4p,CDj1f2UkV,,CGVh[e$dH1kl0 +RFPaej1r`S&!*F130A[qX%Q&&kCcr1I,dIcNU*$V518BG9B,(C5Gd8Bp,6LM*Bl6 +6Rq9aVG-RH'M0LDkS4alB'#jDpa5ccc[FcNj-,-jA#2%)Y#`GjP(EXM6*)phqiCT +(A(5l6GNk[al[G83pUX8Vp9dHf)&m`ejc,2(`Z@9CX1&H&'i%$DNIUhMd#Z+GUJ# +kIZ`j"Vei`Y12mNKfeQ5G$AE%d(#C[4qCI&hkSak8aDHEE[m2F'TeEXVeeaLFF[d +m(*pZUJ,`X2PN'E!$BAmjab-0[3)ArX+FRNmP93(di00%93!HX@RL-)r4D@+UZ6h +8iA[+SI#ip`#q[THpJ,MT,JX,p`@KL'dV+(fULfhj($@ELAKE`ir[ZpLfr$dXr"V +QLr&BLP9dASrhkL`kaI%',p3V6A'521JH!0$CK1YeckFiUJ$ebUFiUJ!m+(5U!2f +-)N89S$j)MYMY$-hbIpcEb+acVa5U!qeq1H$rMm0!KS`q0-ckAiHL)K*8#1S[04! +2%k6bHU"rqZ+0L$FMh+e@AR)d#p)31Ci$RS8dp1,+R5!"$`8*K6KL[[N+IFUVl29 +NlQJmIFrP@Xc$L0Gj'1V`B-mA#(XD"dF6q&D,pXMTT+9iE8'40G[Hd)55+#[9jcF +FMSF"Y,`SP-)!GPMClalU0bT!dG3NKr$SP,U$"&6'ZA6&KPIlUEI8+`HEj4l3pDQ +#+-j-r'U@1NhXSLkI[,T&AN6a0JbG$Ih6!Ca'Z[@JHP#iaeh3f5YIG#0,`lrH$id +"Fq'8+rT6IBRF!P[YJSS#S@""ZDXF2af,E+i#YC85-2#T36a1h4GNU`H#-mKP6fC +pS!NZjK2a[+#%5`Mjm%QRUqiQc,MT"*f8AMl8jfjM@6IF5%9'Q2dJ&12RA-)qZ`9 +p1J$IP(@*31XTZ&+'bf4@`,YMM*mZ$1d([0E#EL0Xc'1!aq3BAbeCkS6PL$e#@1S +m`'l)MK&feal!LbPDRX2iGIJDBQX)#ab&Eb1fKE#UJl!GTa2l#2-e`$0iTBPICCP +q#2BLaR1BASG$!!9dD4#Nfq&Pa2JQ@r8Gm#CLI%YibJ$m!6%AB@0h-G5kN!$I+Ta +Db[!UBL(A,(mZ`bXYK9b26rq3!1(L3K(I5PPi$rX-BRb-fANr`cG&I!4B8mHFU!4 +ILTQhLpd18%b,AC!!pJ4V!jL44CLY&QrK`3cPp0CHZX`hNqHiSBX`pJCKf3iTX&6 +dK!5X9GNEm"YLZ0dVHl,i0P"mab56pX9XJ9CaSHM2'0`GimqLDE1M'j!!XC3a[-% +YqEhq0PPAdHS0"D58bS!rC1"cdQMLV"M[LK8Kb5eRfcVP8+"$Z%9FHAG!DT80j4k +2L1XZ$RHSAGDjh&+E'$,-lr6k@U-*daIEm%55'1S-CY+HH93P)f%,[$*1IX@1&Y3 +MBh!*RHH*X9I@Tf+U4H'iD,C+1bE,T*-b-@+%R53fH'mQM5"FjZ2jSJ30bPN$e-m +[qJ`iLipcr6m!#J!plGD6HZb-8-+HV13CH6alFSbXl!R&Xb1-8%)**60kC'9+EL9 +XPATNY50--Q2V5I'%VE,$8ra&9LpfK"%DBB3H18CSj"JC8h+-c0L6!C-F*EFQqC@ +`)d0fC-D1(#1h4ZD-M#Jj4LKj4SiG'6"bM"`ME!9Zd"&+D**RbXM+E'@2ChEN'8% +1!b$i!`lV5J$0YNf%%!II)`L&EX2h2Xbbm1f$lcdNS,1)4h%aVjQ5@YC0m`-`f1( +C[q$I3$a2H3Fa,f&2kp)*+DFlGUfUk3jBpVAJaq05"m+,+KHPY&'U&UZ'lV6'lR! +29Pq-,5C[[@*RdrN[MfZ@%6VpNM(rm*Ef!qqjad-Ifp5Cmr,qG)1,FK`rq$DfN4i +lcF*!bR*02fJCh$qIjF9R6mQ@d510`Fid#q'*DlVPIX&pT4jPe,4pAG&RrP'28@T +r`VEFUQefEEHSabNeT1K@E5FJ5J@5%%B4P#40iM[`C#+kQTRA(9I@ZJlXp,2rMN@ +8@@4+9hk+L)'&8Y4fipMEXaLJ&Z195XSbHL+i3BQ&m"SFr0DM&K&Sb+'4dDZbDC4 +ql)A(T9PX@4bJ@AM0)eHl&RXfXr!EQqU)S*!!8AmR3Gm*faENhMieP5P-b51M&f5 +SfZm@(VGMFAKm8NdPLr&KimcSp-R%MNkSCa&6LQiCX%#AI*PYMh$(JU-@JiUDPR) +ebc9QG,bUhf+0H&cFQV[l9&prr*kE9l,T**l!%T)e$"UK)FcHT93m33*e$EDZVKS +*jJS6k#655N'(0IKKMC[26dcPm36DJe#$YPPTT9Dj18c&%fK43JeDGf9TITP4BB* +#00HJ"qUUZ-$0d9c&"45Qe'$[kqY2+S`+%hJY%'V`ZPKjG1S35`JQm&+K@rlVCG2 +Jcke4+TkJ#%dSk*6kQiFePK#Hc-E@S01Jqi9`0ZfiT'4Z[VX%K6i*ph,CT1PX2"A +1ijqXUPpZYre,m+lRirJ1EZrr!!d!#8*eD@aN)&4ME'98Bf`ZZ3#3%#8F!*!33hN +!!)3!N!-[#rq3"%&38%aKF'ad)3#[M5YCVkGYX!!!BLm!N!Bb'3#3"0ZT!*!)9Z` +1`)qRR'Ei#i"qD&MCjqARf6IbXh0NqINHGj!!Smj@jRGHCckRJ`C0[0lY`!BfY*R +A'h,-CQPR#,#hJBeYCZm"(""!VPpfCCqRQh8,Ej!!4@BJHC!!c##CE!,a"-#"66` +m%424JFZ)*Z2PKA2QAIc4R"j,DH"JH$Yl42f1T%L2!iN(NpGMrL&8Zhp98A&qG(4 +HX5"[EUbmFY53!0'Yi8TqIikTqDj[0,LEmdcT#S`(`K((fGKLYlKYM--X$mjR'ML +D-RXG1Sqb1VJmdIQ`1b02[b1SrU1YU8FL*qcYVArH3er1kE'FKbIL$Xe[0QGqc0A +8BNaLZY#@S@RIRdYP[cQKPEBPb1#$Z3[HlDdr@NST5bCEDfQ(*@e(9!dBA4SYVbU +*9KB@983VmXU,bLUMXE+biU+m@'94D8RRk0c5UZM-USV+k05LFPK&*4@9XH,LD$r +N+"J9jXXLNPNMUdSU!++mY'TDB63@V5JS,bUSL*C1MHBAaBT,Tb'f0&T3NKqY+S[ +1,UUd+DD%l9P@(#Z*6LdYYhfD&Bf1,L`SL4E-+FLVUN6kbX+#-%'@8L1bqQG&ak- +@HE'5NY,+RfSfYEadCTM2e55DR9p8#95E5JPd0RpB5jZpD&25388Pq3APJ!RmdG) +bQkiL'LX[k+@Lbk*M#f198E4*1%pk48F9&K3A9h51$LZD8Kjc95k2pLqY,,5jqTA +R&4C9&Z499L&j0,GdGN&jlS$1dGekc+M*-US3f2*Ya+K+e#D[1KEIL0,+JQLAk1b +#D(jTe*BkVkUm[+#NXRKZ['8'$"Uq@ippE'P4KBV5**S5C!MVE6ZR0!(HiKf%!J, +LL0+5,R$9S"b3!-$@fI8"%XdU++p!r@h[SLHMD,&B!NZd`N'0T`M4frN`Zl4NKmV +SY),+X#9$I$@IDkKS98Pa389&Q$#[X,5d!VdD`SNhCU`BD!TMX`TXm9bPE8PXU8Y +3Z+NS6B9Y24Z1cV")-AQSBEJ@%+@66jQ`)mR4CmFq8@1m(H)4GMC'kC)q[BQ[@!I +IahKja(jQI9G[&RAeZ[9HHXePJpUhkM,B4R6VIIQ&eK[0-ZY[@$iYfY@,pXiV+kR +XpkRCd'E`dQ[D(0ZqeG#q8p1'AQXf6*0CR1Qecc*IM6KJ40@)rBLk4XB[kYTl4-l +S)8!lrr*@(5C()e26bV`qG-@M"hES'VRLe1MSD0T6Pe2MeRfTFGcA(,lQBaCeV4q +G[15![HD2cZP3BMC8)aTaTaNpP+CeXXKLCF@9AAYAj*99PV@q[*0&0kdZiL*GHpZ +DPY(PRDliDP`1-PBQ-##ZZFeUi`H0c"i8GX6m)30'Y!dpCpSikkM9,cGh@"J#Yif +3!2P%+GJmjN5*&Q355aU*T&"+LP"D'P0QICSIE80pqrDMk,Vjmc12SljT4e-dC3& +&T5pPFT3b*C-b8p)S-bf&STP#ID0-mr[+%63rj8LQY)(!-6L&dXDP88Tq*RNVJ*2 +YTJ2F&Vm(ZpCbSRPVX-kX)cEVb6-EU*E4P)(PTT8amrXBXhbH-@Z`l+c$Zal["V` +D-3Da'MNf)0Ik@SMhN!#(N3rfI2L"`H,BJ(FpeAT[(AN2!#F$Vm8I(b,i-RlFYYE +C!9,pC@a1N!#"1Y'Q"1Y@[P'$))-L+fX5V+196m#C5'$$N5qC)"kH5"#'Kr%ZJ3h +Iq&Sb34MqQJ0J%aL$m0A,N`MdkJd)Md35#6DZA)A`jFN%'eCQ2)&`LQad#6*Sq8U +%NdXB@8AV)[L)(%,8%3PXH"L2F+5diF[$H&YQQ`$K6fb)KiG93IM+H(KB-S5[6)6 +6aR8fI(Nbr,80@m*AVkS*Ya@X#EF9I'0$G6KP,&qe,Q0P-YbfK%hbB`0AKbHk,[R +ejIRTIHY%@fIf6eZEX[M)Nr[eE4eYNcQ`mE(Y-YY(@r5Pq6`rBhQV0Ah@c9Z2ZD1 +0Q@Ikk&BE@Uh[Xkl2QKE,`d&Lh`b(bD#RV$H5%B*FYhb9lFZ-5#5F#@EGUR"BE(a +MBcJN0QDXYUA,X'f'(JhlliPeb*@"&N4US(TLJdfpF9ASTJfV3MH'8,+Qm9UDq(* +XPrIi9Uc8Ie,Ch"p$#p5YRZKB,P)TYGjam2MMeT!!KlA$[N6eZB3fb#hH&D'pfV[ +#TGZ[cD"B4IkBIU1LhE1kGl0[1U*5l!l#319KU5"5m2'-1%"X5#Q)h)j5&3bC(JE +9`Ej4+i$4Z"[jPE&bV-h&44@9#Gm`q"69)YiRc*C*DDRB@cfcN@U6(m$dZe&UFG% +8R"3XfYV%-m05#J84#UJH+@c-bpaaBQl@(EEFU9L0lFB8)4A!c(3!bXVb3MXA0`+ +%KM!me#XpJPdD-"$r"aJ+-(l!h-N-B0E[4QN9KA%Jm#"A##A$38(4U8k%kP"p5VG +3-#9'f4`*,&Mdc2GSahS"c'Jh5XGG#DH3!($&Yl@0qm0V#r`+U8*FYG!0p50!eB! +bE+9(&Fh%*FV1S$L`HJ$f(D)E"M"r"'C,93-X,&J$"`cY4BdLe-J#X`Al#9K$!&Z +2Hd+6!'B),$aIE!)@qUZ"0AE!!R4A8cbf%bS+riLV#A"p5mh-0`&-fe8iGB5J3Nm +)4e%cKd@K0jY(U$RDa`ml#X&a(1BEZkfQiQe",48-+ADccRaY[Q,c0BD%qFUQD)Q +h&889M(!NeD8'jNYU(F#`9FdV,-LE89CDK+Z3!"e,cTpVr@idY5BH'QDYEli3mi9 +&&-AEKYSU'#'LKY6BI%lE"c#DGk-)'UkmXUbmG$VZBR&[V[-U(#A$ZMDN4RBcahB +[e"CSfP&l"81+%0X%jGk"1LJBhT!!H%Nr-jqD6hcc'6A1LKA-ULc&YP"5KBi*ekb +6U4-q0TqL0ZB6fc3G%M-jPcS+G35)6V5MJL(PBCL2)fdA"3-pXeei+-Sb(b[+5J" +1-ap49r1K$l0a9XAFLY,LdPLq"CPB*@,S&89G5HbK&B2II%$GI"JTrY5LiJ*&h9b +CX593p`Ke4rG%HVRlp*M+SZ)+#e,-KlC&FC05e)@i0,kb[4qBpkPf0pUZ+Nb%6$[ +LhFQmTfJRNTa`[Z65cV5,JK&@1B0bD&HKAC&Q0pTG`8LC%)CeT4l8dlaVeLSiC%r +8)J-cUKIYS@")-@iY'@M%hZBG4Ee*5X+F$@P2fN["i1jf-k+'jQhI[)fk&KH86&1 +dPbY8"SE"@i&jkem$K-dlYX*l92IpQjjjdiEdG0hB$!AZ3hd9$(5IpEFcDkLIH51 +!fDBEC4a899!q&iYRI0Z,"``U,Fpa!8,p82VqjR9&r9ejQP0VmeTJAYZfFSPjhEB +J0Qp&IDZlEh9J9[qVU',HYEfleVERlRJ(d%!&)kaR0c460Je5--*kGU0HjPADfl` +5`,6M-9bRA58UiYl4cLZd0m!-0LmV%!E#QR5RhFa,JARThc9jf9Ed&9[43G89I6% +`,e,$EP3Efh0j!Hl8*D&VC1J5'JM!3m`,LSDiUE%RKXP3fXFmEjj6F-KHQ"*lSN1 +'dA!&)i5&#@'H$Fbc@f%T'ZlDI5rUBjlac$1f*[ZipKJ!a`M+86$#[@)!CCZRa6a +Y8q48PqfT`$celk)rEa[a19[j&bb%A4`%l'"Q9@"@ECkNjMhEVqhajTSR&DCSFQ0 +k`UaNmi4&JL19Q#IYH4Q4'9KDb6H2dlidNNDChp0S'Q-H-iqD4aK"f-RfaDE`X(R +)2'JH-#[-r@DjZ5m`$fmE1i&jk&p9$Fb$[be5[RPJfbS8Q"8eK3r-r9YEP3dZRfc +ZXi[T5#bQpk*epJYJeT`G8QNrK0k$TKJE`2cM35'9aL,"hGMqa`8`rh)U5+9a5() +APSVa!FarhJ"5D6`5hSN9I8)!mjqlI5T03-)lX(T2$'$qFfG2TBP)H$Xe0lF&-2q +bM+HDfq`m'%@5BQieYhMQ9YXaYq"kJSR!k4J+Y6,0cG6"h'4ZT%Qd[lR"KrZA4I8 +Q1f4ZY!0NNMAfaa5jRVUEkmbejKTcYER+A1NM`1f5[VPZf`iDQ'YV1Ydhem4AYF" +FrHrP+6"ArED8"1E+VG1MMVQ"pZfdjXfZdb-NfFrQ2&#AqNqa&l&h1Nr(4D*"*qZ +N'6[5GYPj0JcpA*J6"M'#8V0Ic(PN1VFJ0Ae@GIbBM(3DBmpX9p!"'-+6'3i-pJ1 +Ucf`(#Kf)b"K086$#Qi%pCH94[S)4,PefU&p1"HDb!'E,EU5Ub[*MP3@*@DV'K1l +i-"8U!*bTjP*&8kXhN!"PJ9N@6S6#dTN&CE(+3ZI*K8I-TED$,l0eb8r8TC!!TJP +0!j!!3LT5-1+R"SqQ%kjCdpe#C#I(*CkjK&M4M'UX&`IQiV$*A)I0LK8RA2[&3'F +V!UTLQUPJK"H!&"5XK%S9$'rIm(b46f9d%*8VQ')C(`%1,4@%HdZ&+ir#PD9+U!T +!CY&X"51mG#LXiaI4(,28KaQI@2QPHG86+c0qkCMM,Kf+kTNP00H(NH+lb,RZKT+ +"%mI"jN*&"j2NK4H#ZR3)(DTJa#m'QHD#`&c`eij5G+KVN!"-h%[2pmhj`*GA13I +A!a$Ka#beLpYX[)I4i3T'@,c'e-+FCmiejrMQ2"4[q2$FNF0M-`UbbmZ(ii52Srp +mXS9NFkk&F)k&F$MHHHCX4I-FK*E8eL`fCjNcIE-i$Q&!F8@ZUlrP$53JR'8KJ$) +MjQcE"CA*5jmjSjBj!`h*Ndp@k%"[C,bM6kIjjM3I*VS+Kqq#m[*SpHafHi"#Kb8 +R`+Q"169FV*!!D@E&Y0!HAM&0c'N@ElRViXlSmb1%MN$PMU5M&)b`LTeTQ$Q&&Y$ +[I*KZ9mM(*6p@-F10)(`1l`+5+5'-2HKS1NE"L2YldV&dR))4pqp1#qPi"52Zhj9 +1S"-9M,KrCcU*&LNBFAphP238"52ZldURdQN+4YJQZ,A4k4%kh9jDlGRHm3R!1!# +0#&ZfSY0F(h6"&HlN`*bmG8DFi["da8hPM!LGB8qXED,PXD)+F03X13!p8eVHbei +R!@T4iS+bNeRNQd8B+*LSZ)+Hk1*`36-R"HDN2c@GSZ0GMPeaI6R40bFQFarRiRD +R(ZB%hjb3!)`laJl"fQLMAR5Q3H1Fk3TPr@G&k#`-ijDM#NZV828Ki-)P9[q`CQ% +cp,%PBh1mAE9rKleb)5fQXhfB+Ik8bK*-fFAZ@0)EFqdFSA13!1jF1Nr"#((faNR +Qr!LGMa*ii`[FGC(1`hZ"39GIi*,JDN-A4ZK#E!XbSM6-BF#&5+@c!IKB@Q+1m@' +Qq2P6Ll%h,(&*"q#8Y$4#5cFK5cA(@(K(iEh)(+hS)RFi'SlCG$&GBRj(ba3FiEP +a1$Eq5qNb"5-m0`l(VRbj@D$SFRGZY2iVk%S&)c`hjY*SFj4[MV*P"ke5dC@*[,P +dP6P5d98Z$rcQ#0mFBIZ[SKK,`T'fHaEBVVr-e@dd$K*A4qMU6A96G)REb5CLPlb +'VP8``P`6DC+Clf(8)m@ee40jAQ$Qr@dR3jf4ETP$&%2YVk2V&B`3%6C6FlKR$MG +'dIA9L!i,c'(E%"eY%C8k"#PBU@l)S"ZNC9TL'5&q`fjNra(J'c"f5$Ci+H&U&`* +EE[[$mJZkMTi4lC%9*d&[qUiE'JcU2p#HbP1[Ec#J0,pJE+bm[!LXR5%$Xm'1f6h +,RF(rcjIkqE8I"DQ@GQJV%@FXerXa5F2K"CAPPSBkSb+k#6'Z"'"eTPk2&pX*pDm +Z@UrUK*YF0EPlrD9bY6'1,Sp0,A2lZBHpXLF16eAB,bk)j"E'`&[G+6UKY+6Jck9 +-(eSdFiF+ajFG(V1,ljE[PkEbN6XkT'4DBF`KrhIVN!#I0@T!eb%j`l2hlQIl4QJ +QhKX00ZdEL5f2FbTZ$cFTZXR0RDNdc4`5Q%1fcCe$,BSTH'qQ@a5-j&AdB$1AcF& +fY-ke+@l"Hb[GTQ#%0jF8F(0ZTcX8M160CBkCE@B&CNji8l,ASj!!ij9I9"jk(H0 +VB&'jQ0PfrCKP*alB,2DQB8pXGp*G#NBi8e-3HMIGBih``*!!JQhlAP1Pk&kh%eM +rI3Bl`ReZPl$qjA5rJK'Rp`5Q`MF9i5'K"*H4qa0jDp-+!rq+"+lDp)!j50%$ETQ +'hj64JkE8KpNiUb"@AM&eDRjjpI)HT4MmLKkdc491DM(S0$B(@31PBi-,$"X3F"A +G8chp5`*6XR@c[5Yj(U+(-ZJKI[dr%ScpiId'M0SmpZIE2UmeT+*`CUc!BIh$f+p +Bm36'[XfeIkTMh[eIr(,q0Dfqk$YSa-#4YK,aX9rRab4Ue0b+bS+Cd8'Pa5&D4S- +"25V92&kSATXcp)UAj6mE$2&I@Z6r(q*ZE0q'pf&k4-&)$YbC"UFQi'!$V%+2i(f +8(P-``S'EKK(hHhTF`8J1h"QddN`2B2kG@E)5B*i`Z%`q86f5#J06q#Y("UM&J1' +Pk2(U-qH63Nm#c#Tk5X%!ppap6`XpM@c6c06!62Z0[L!'Se2S+Ec2d,-+4[aD@TZ +HSqF9M$JaUaDpB%#RHX%G&Dhr4AT*`BJh`(BQhq5C+B(*rmI'!3"LF*j5p&+5X@K +L[SNP6idB$SUHVfk2!`0ci-mAB(S@YAZCAP%``YVjb2dUVEC'#-$(rM%CccmBFDZ +V!4m3Q!0#`$J-jC9@P95'RJ(@)r3+!,p')$QpjJ#Rim$q"Ue4--)&+"h-U6IT,3A +$fjdC&pGQCRpkQplaBD,%T91Q+lK$X29!!C`8Q%Rr+-ilVK%EiY5feNa8Y0BY8GE +rVTQJk&f(hIVISrF9M2"!djLDQ['q'CrNMEh[F&ZL)"JLilB0(6Di*V'CD([a,CF +e(G4Q%%r'rXUJA92G4Z#[l%G0Ze&33l'"XjSe4+qMQ6kJ$a8-EaabYX60jL-cKN! +#r-K0ZLa`-ci4qJ5*2UA2&)b`kl*!T9Y(RbXBi3M+`SP`0(eK4J8`rdNdq!*`[M3 +M&AhT6U9G3HVlLVj@--,biE4TpJh-[PXAaDpGRQkJLq3'*MI"c(#G%,VFa"H$5l3 +B,(b+2UpZMCc!j'aMJhf'mRaM4LMkaKheHU)*[UAep"epVq!)MhSpF@lmJ6BS'1% +XX%I1M3EXX)d1*[aQ@'#'ED-Fi6k[D)1E`U!MQRdm!rDc!Y#`pIU!1UH&0)TK''( +'Y9iIkXr-SM$E3U!J$CUKJ4RkeiQT3#e0eRe)B)EmI5G",B8p-eLajbiTS+CaLQ) +FppdTG+$Cfc0lflD#A)qJ+B5hie6&S(VEaKK-qh!Y6P---VI&24M%ZN'"'I3hQKb +R1@K$D+M*pJa1BF)Jp(*Y-e!aQ0kf'8E3D2D&IB3&(&%FZ'BB3IZbiR3&(NN)!C3 +k-m!c!d!X!58T@IIqJHRrMlU$Zm-CTTpL8,PYh8F#FUCL5(pBC*EjeYFcI9%Fdmm +@(`F"4Gql-SbP59b(kbUZ3i+4$Im%VXIe&GGc04K,idbI`26CfU2e(FKa00lXj4R +35a@$q*dShjk"fI2RKFL-X-bf-9XRS),F5`LN0E8a[326qlH9i%0d330ZU,L"+qd +8+Z"'h&Ka)eHL+C4Rp[!-L+b+`I41P+KAB(VpSm-D!PX6de0aNq50MTXUEZU3!0N +EA3r2p!!Cf[5dNq8aT'['c48hUli0l'jfBl1lABefXh1U1Gi@h&+"24AH"L+8`Dd +iUVK9p@eJ9fjYGJR-V[r,SZA@J06'l+bi6A9eG`V-6Z(D"3fSa'("qK*R"E1cR3L +irLVS[VJ,JGc,EB9"kZEYZChLlDY,f0edBi2eN!!0MU(#S(YcHpj"-4LCa8k,aR6 +P$Q`J[KAK$U#V0iRfUjJ4M8dTVDU-cVED$JQP)0#A,B)Gm(EN6SSl*SMEbQ6aMYc +C0eQr%'%BP'V,ed!TZ)[TSKMmkrMjME-LR!A'H0Y35b,H`"9@J#J82STCeNDm#(( +54KGFCENc1"5GZ5Z$"0ij3GTJd-1,F3F3UXAGcBi+Qh&)iV$qRB6"jZDGH4I&S(( +(ZH+mDi4h45ZR1XdT9q4Gm1l'Zb[H,DRC`$dLh!-,IZfN*T9,"5Sbpc5SEmmNLi" +l4EJALT!!B[@Q`L`'Gd`f1pUUG8291[)HTS0[1LDB(aaRDUG4EHiGiGieZ9-0P*@ +%3D2R2AN[aAXQ,K(eZ!rh9Bb1X"YK1YAKIJD0$9kfZdc8iIim3$(SfIBkN!!"MPG +lhl42d$`B@iZ0XhYq1pqd5p)kGV"cC4-l'`S$fiG6h!k$bY+`#jecG'R)VKIH#l8 +Cb0Q+3@bfFkFqQ1TY64Xf8$eLdmD@,a[[)0jEm5!hGfb+eMbB$B6K)P"fDdh00Xd +Gbp'*E9,jFTd'RMB2iD'+KlM*8aq-mbM[`m0m%reemS!&E5F2j#aiZ'QP1%k8Y[i +4%4k"Hd+(F2*JNFL$HKD)#RmH29D(*6jq@YP'(SC'EXNjR1ZEPXRaNq2'6d0Ua[Z +D&SVhGH2(qNF+Md3&4r&SaD15c(`H%q%af1eUaH@rA+P(ipf2abVHcb9U4)ejA)6 +(@@U0*Hf'5FEL(@q`XBah5D#D`4-L2'(,j'PZ#pE#&L`A"@['%de6hc4,6Tk*,RG +cDX'6)MaTbq4TDQ'#N!$1qr-"bKk%E@1eS4ei-KqSH,+E2'fS(FG-%m@JKGV*Bre +61%ma++Gf`V5&YN"Mhc416Kk`6QdFa#a-)pmd5NkH*REb31F[-ANJ,Y+3!0+k8BS +G"EB5S+Cb2KFScRGcTJ2i[`e-I6B0E)hVfa3B&6b9TbQHkZD-69'2#pQ!kK,K`Kr +RM'-b@pNf4e&fI38q+4IaG-9&lL$6JBlM'9aXk[*-KA9,F)e!6%mZi9)&pCV`$Y' +"ZR!C(k4`Q3hVei%kQMU"UE1TISS2FVh@N6TcHB6,IqakKF3KPQkd+eF`'J8mdX6 +G#@S`8+RCK#A1KZa1Zh"9K+YUqPCaX5YFEcU#Cr&XNm&c&-pbi(T62jl,"bZHkm# +"aQE5!j2qkiKAI(##cpDA$iR`)6rY3BTRZe9d)-Ai81&$d81(mH'+ilc4J664+*l +(mhh3XhkCeI2FV"j)Hr-4"UFAm#i6rL00S2K)9`MV2bV#4d&iSp[!2`rVDY'rZ(4 +IXPca'4jB!aTDUD"NH-ER"3`U*lSS2XK"fl5$I$#0iU00EF9(Zd&ZrFF)Jh[)ar* +aLSpejE"RU)84AKMZN5&b0dU1`hXmRk!Bj%DE"c)-I'+%6h6lC[A)!FQ66c)iBjf +8Z#cYbiXL['M,+!F*L`dfk&5fI-9DI,**p8fYj#Jr18R$ie-LI-U@8CjUBB++bkH +Dl45IkNEj*$6VDAbkiY2F+*m%j[FC"ZHh-p`SYricq5c&i&EDNEdr66DHEl!0adI +j@5l1%[$&0b$ba%Fjq#1+6kmHj4cJU'K[5[QPm8N,MfX!-H!FL)%iPq)jESD!F-Z +,K4HM@QIc1BT"PV3c**m1J3V8ZAbH$c,(,c2NA$FcmQNDRkmaGXk[pPqJYH)i1G2 +k,ic`K6KUp[crCNLdI9bLdJd6VDf"UU6bHH6TMEb%PrTkBh+B,(($T*!!+[JL[8( +a4@kB@2r&`L"ZmL@m6$%BkVBZK65$,ih`TAmB*L!qmQ9mZH)ihh)'PI!9%ElL$m- +%4%qq8[qJq%UATi31iUXLI0AQBD*rX#@$A&`U,dA*[ZHVpAHqrMij6+jfZ5YT0Pm +6i@Xf$a2pRB8*3LaIUpFV[YB0Nd24YGIapBU[Fm2N8*V(0qK[&GrJKSRehmJh+3C +RcJk0`fLHrXEAZ(V(K`RBFcEZ8$T-IqhVVa2$4(pV4d-eT90r&HL[IKiQHVdG*R9 +Y4R$dE&N@B(ZlQ@p4I,1Vk!)kKQq0m+eEZN[a,G8![`cdPcm$j1NSqfemZf+3!)R +X%VU3!"EU,r6RV+(5a2TcfaQhilf$le3-QTeG3Qf+GA`ADdM%4ILZ(ipX@i4$A3Z +$MmGhmcf+lhDEh%*kMqrPqr4R[&`ab(K@@()KhFIhm`V&)0NjrfAm!$qSq!%hE4E +5)Rj)IkVi)FFRXIk(p5H+3AkbQk$e2m+2+Rl%94lXFreaS$rH['BqkTVQH$U*(i[ +`Bc9MJ$9NReK$!%VaJ`lbUA3arejrT2Mh$V,e2kir92bifjUYIb8rS4M%+&Z#8qN +XIT*A+Al5P3!%G[e"S$riE4N&!FS@j63kNjq+m&0EMRH+Rh$JcU%,q@Pq4[(6$Y` +jG+jq2p$[r`EZ'3IZA,U!Rih`XeXUEY@Mp%F@C*c*H#AGaFrTpa5$2@8VE2h2m`Z ++RhG&Z*+ZiaIj*F8[ZL*F59ITG`2plPr(Lq)i6qSUZTCIM[$,@fDlBT!!XLbX'qN +fIS9I9Ib+Jh8MhD6A"RVY9PJJ5&PB0p'Y[$V#UlG8&4)pLZpcZpF+HS0I%`EIL9r +R0a5rlRD[&I5bISIAm*ZqIZIAh@Z0fl9@J%rjPRjE-HK%&VMe[ahKYlIZkjY8F80 +Ka&#XX'E&HYZZ+U"CkEIi(9lVklH5feA)Pr,"kAZDhp9[+RlAE9I@rjl`HkMNqrb +"BY#KE#8HTXIi``KrD%pTbARl!Gk2q'2&)#,C*)r45[iN`TrB%mhQ`I`ahNre'X@ +IZN3VD49r&Z(2YZa@9Ya83fmVPGHLB'r`1[fkVpp)lPC3B,1jRk(RqI-)IljPYl) +5@3ak'AqKAe--lT*YX&I3P9rb9iT"BE+le5[d'RqY9b[qfZe@e[m0BpIkaZe3Vp* +VqP9I[jVFVEjeFDr3UrS9Alq5h+e@fpD2mk$Xk[0bS&qQG)MEj*G#LXHD+,hSefa +92l1j3&Zh"AN,#mPkrNiak%1fPQr4@[iq`Yrrf$#+[k['pP+JArSM0Vi(YIk"X49 +EEK15[Nr[kaIe#kaIY&d%J5RK$AJh-MEhMG8TRYI2XEC#90S+86')5i`$K',M"YE +lA+bI&4EapE1r$#``N!"#8YMl2&%m59(L*IcMC6Y*93+LNI12N9U5TU4@`Vq[e"C +I5Hf%IlJ%%P%#VT!!m`m4*HP+9-+I,4Q5U53MiHmRGD5Z%T!!H*ar6kNRpCA85rK +l5J0TU!6-'1II94T*Bb@0%[lZdN5D+QQ5m(H4CY*F#HJVcYp4@NK,*H#U12m1dNU +L5L")k2aYTE@d83)'L[1hNVDb[4+3!%LF[jQdNrC+`"KarLDbJh43![k(mcH3!)k +#rS(3Rr2AN4fPXa,32*aI54I*JT(`"p*9ZLN"3F2jDdPhf8P*pi6INjeP&b8lare +NC&IC63NNpTcr"pPGHLJ"hm,j[j@HdNX*&1fFrd[C3hSVf52KrdcfP,d8eU9`3,e +2(dQIL23"BE2"b)*BrYcUeD1USUKNQK-`NEM@hNIdUAl'emmNa%B%@RefarL-[T! +![[TT"G(P-!rmqLPI2aA2`pT+UNN["rP,qNEk4D5I&IUd%Yd*BNY)T&%#kT%GbGr +5phU9VeF"3Q9X#U##FQ,MIL#YRr6eNmNiU+cC1-1LRr$e%mQiRF)ipMK9Vr6ebQ3 +Fj""YA#hfpH0iNR&CBHNXX8hr2Y#rrqpe@%+j4KrdUdcTVap6iY6qV&mrkZY(Ni@ +(lUZ56Q(K38kV,`-L-J#-K,Ufm#!m9A-ViY@(@U5Y6J0ZV"rap52*kV4hq*T`8re +`S"qZ18C)5'[b`G*U+32e3dS'ZPc`k`Gpr@#b&JrCC#"8f9UdiMD5(C(X4#f'&CA +-X1dq%l)8aI&DJ%9PDp'@fqX(I2e!XKBY(EiGZ)0H%HJ92brA!PkBV8Y(lLb$p2d ++ejJ`,rakZDqA*qYb[`AVq'IFKE[*hK(F$0)T,Hb4@'@m'Z#jf@TdjehdIEkq,eN +0L,$EZ&fjKllAerFQim$QXh%pZEHqapIh*10!kE0aHh*IIEH[F8L+ai&LD12km8" +pPkr[5XD"`@MMXRQ`[Y2AGbEM)&YViiE`-(f(Vqp)a[NZEMMRkYYpIAXb$[a+'lF +[MpDhqIUfC&bULa[$ir5Y[Vie'3IeAYXpihQ#$0Dh+(%UM0D[E`ldc6pYM+`K!jF +US'lTQf5)$2Ae6BP6J'$cXeJQFElXSfp8XNpi#JMp`d5'390pZ)a3iJK!#*XX14% +SBH$)-L"@NJHe!EZqb`LmZIS'*ENZcf5H)[Y'"-+$N!$0!*R),X-hf-d6FR+T-K5 +&Z&j'kZYmEIAM`K9I4VV-"9`SSb)bUMTcUVl13V6[D"QMC(4ii11CI,MX*f2eYIS +D*IZ&4al%9-Si'DpNA,Md`emQ%r695LD%5hrSRbL6&%LJB@H9F*QqbYGA*CCqQH6 +LCR+*[Y,A9bDArU[YQ('N)C!!9LTNriMX[k8(P)`05C-mQ`q@!j4!#-lfd@bHUkm +3$@didGID"VM'eR%-hXPbS"*,#-)D2SrRkF[eCD`[YbNJf#B#k6L*b43Pi!KCJBP +jI*6N#8BRZ%!@f$`q3PmU'JT[5L$ZPVJB,![dXT!!DcTcES))#9H#"iNp6k4!TLS +"k`Gh9P!i&ZK,C"VV5h"RP@R3YQd9(BA*#e9YHdk+#bdR&+bcXV,#T8m%A#%TP#) +PKDiC&q"Z-&eQk)[e43TDZrD`"faR5,(-9,E#YMNAm(&5)U8+R4L1+p"(p0*!,re +YRA*53r`l2PE+)P,f4ak0i'aYmCh!TmK"8Ui%T#',l`3q85m*p*,Im%'Vaq)lN8q +@LSL!0lU*&L33[m1*MaIc4UN850D*9-NX*9@ZkS[j$TNYFr5&qJ)Pd(fc99r-9mK +F19J*&1GX94EcqA+)(+V%+Fc"IlBq2p$REel6R+SFU"[Rb@%41H`20#H"@Th&XS5 +AbH%b6mRK$JXi%rUm3*qh'FXmKf8TAb,c)rK*JXheK%#H(5CAmbebK"bTm,-$G[@ +(rbBj5KBS1FS"ZjU[dHF'qYbYNd3*f$jfSPl$0mV[j'JP[h1CVq8Ep6Q"2ZI2FYi +#4T!!cA%05""R"rVXEBaG*@!Y*@EDiN![rYY-dj!!hQ0pJCe+)'c*-A+XNQ0FIpl +*VmTaXP#ITFp8!Q+4lFmlqA%jANj3FVcVU6[j2MP46P,L"0IJ[dZI%HJc0[H8iaL +"Mh#[,)V)SNfF3L8J)PNF+rJ419P183*1NF@aJKr3T`Ikp-diR$)F1!F2bkN41A9 +,Ed)Sb[EQNrbLR#DR+`($b2EQNrbmR#%Si"N1'+lrqV4!RlDY0mpd[EQ+Rj1cC,% +5F)jXjUG`Gcmed)"GSp8Z%'fcdD[i+Ae+S#(0YkdVU`P+qZ4!RlbY+b&&b0T+%3V +NqZ4X[8M*fDiV9f1BRL2RkT2dL8T!LE&GZCSrNI2NI#@3!)1chE5Dhj3,j%)P)"l +CiUcQer3*J6jKkd(K3YGCVr%D@4+4*6q5!!4m*J[V(Ij!PJT@P+81&XlLq[K!(lm +9&QK(&YCDIPmZMXM&@aS@T#REX1[i1lP%PLQ"F*YYf(AmV9`UPbQje)&FajrVKB& +HZ+eK,h-0qcPr)jI,&8SZGjQri'rdFB%qlQG4Hi%JRFhc15lIa`Ekf&pEGePekai +6k'1fYHj*YR92Y"[V)QYFE0dAf8k'`+GF+9FTXB*hN!!"`+H2PUYC3a-Z)PH(,*e +0ff0)4SM'LIYZEl`+lc9bV4))`')@!-!GFTeFVhqRXAb!"@9P+`4Ah4X%T`L`S0# +dm0H5Qq4Q*8iV$(j2(aASShlEkaaj#4IU9,NP)VIm5(Q4'adi(bIQ@q8f*EFkF,i +%qNJm[i'lcB%,*&eZMmMY@qB#L&FBV0*3EN&e"B`QZ92Z8S,lN!!YH8-FCHq@Hr3 +4HVi5U)ECNMI%SIKHZ8m*L%Uf+JeaQ&iZZ)Y"*Fa@TD%dd[-#2@rc-R'rUd%MD5S +V)J)Tj8e,#G6',)k@1&!r)!mUHF$KD#QYp1'"2R`cMJFGMPE54Kk+b%0EDJQU!@D +FG-+Pq@&j4!QB8*Kam(HA4`A(6E#G,,"1XU-q,0"10r![-`jU%CKaZ(KhNpr,idS +J5Q`cGjCZqY"!(rSr2eXJN!"hXMPa0GH("2U3!&rRACbMCHIG`B%qH0Zm1m)11+L +ALGb&Gk8mS34X+e`YT)Idd(2e(0C3PQ102jN4!99+RY5cPH$-E0Zj"lTmP6bPCqN +U*DYF1rI!NHeTH8B*P+CX'rE!AIeCH8l*Xkjq2D5RVJ`dC%)fYH&cVJel5Qpj2L, +2rh3$J2L8aG*AXZ8&H9(*#`j,AqQR+`*GX4N,U&-@5cmC+#p&j+8Y,If8'dr$-'4 +I&S%ZP,`LVbS"`mA@FjM-P0AbQLlA"bPClHSj62DAeq80*G!MXM8B*[[+'RP6L9- +cJRqi,JYdfGBekNeAMq'5+fp&j+drE'IJlPKBSf@m[#h[+((b6r#2dD@",Yd+kad +(D`aZIQXMXRC,QChqP"`S4I+Z[+F%'P"fFKdSdq4pq8#*dhU#2kC,!PfbEA+"kQ3 +R9dbQbSIbNC)2AHBT-PA2$26-rrRT"J&,bZE%48mA"lViemN&GDV%j*S4k"RE*TG +G2c8%r84HaIZaI+,NBpI'*E*)2TA2p(50)ae%S@`EPmJm@5HI+eRRqUe%+Z3,q9, +*&kii1-lT`N!AEZfh,efrP8UjI"@4VhlNJmVR$YBXh,ZqPQq81"%Sq'IVDB'HYK8 +@'&J@eQ`j@,k0b,GEfKKD6lD0Mm64CVemTf5pDq-MFGMjARj3mVd$HD3FTDF'HZU +f0JCCbVEa8A+XE*!!M8UJl'3c,j!!Bh9"S![qjkFXa)P(!F-#R4rSr&rEH"12+br +3HG[D'0*eV+f)R8$(5l6'e9Hl0MiC3aVNF4h61"C$B-UfmFQbe!2e4hRJBGPq1eR +1m$`[4AR3Jl,&`49+6`ldj*pS!-Tc'P&bLTcZE4IaYYY%k&1H1$5,jA`[eDZP2*! +!$bbDaEMG("$S!rk%"J*5&XhCFTkA&[(50[FYIM!Tl0Y,j"U[YZFVcj'qi,r+#lb +)mJ)($iF6[AqJprpEhhV3IE*pZdbZp*5AVMa)5GR-PmU9HP+J*rhmEaUHdjG#hN[ +ea%"2r+9A26!c%Vdk)G!6Y[9Uc2BUrSP*Y#fRKMBSDfKSLVC5S4TbZkbKm#X#LVH +Ai@8U$q`eZdVH+AIUmASFkr%f"F6Fa-[%@mHVUl`k)A-!+HlfkLR2D9,"HjFH'qL +aIkZ&Klp'mHVVrC4Ahf'm"fH&-ASdkc%@ifKE%2bKMc"qVF0VS%FT$mbiZ-U4(UR +hC3ha0GD3!'BA$3@f6*S-VN@Zep"Vj$AfQRK0@HFLbQY)+6T(Mp$$pE"!jra$25l +3)hi613cdm,q,qHYKreD$5[FDiDm'p$k8ViIU)9icVlNH(-$ppaUPDdLdT1Lp[4D +HhKXr4q#e!1GF$`VdS*q&&$A8UM+pCL#ZC1Z"HN#JXrqD+G!$YiVB"RV!Rh5m-lh +Q8+Zj3IIArA4Ih8I[TII8[I8H%#)r3d1B@rH%B[D&ZJFYeE[VhI5ZHKHkZTEZlm6 +FIGd[,R([kljaK8KIppNQNZrV[EDTd2Ykcfd#mi(ZrDIk#@T&2NVP5&Xq#ZE)46k ++jYJpRYiG(VhE0PUTVhH0Xfem90MaFHVS`93d)rZ'R$9ekB$ke2,0NNj[,AQl[If +0Jj5F6V#f#kh8dV(8Cjcp"CF2PV6lX0hMl5l#qhLlMpYpmZPRaE0LP"QM1M'U'k& +k1C1TIS`DG)j"4kJ&T8h2[Q%mE9H8IF1BR+8C'HPHBcY-GUBlp%kkZpI5Dk@l"A$ +rTYLTGl,c%kbK6!rX0pded&hr1KSb2DLHd8-k5hIaY9AqqPQeepGGiRV!G65Q)+S +IG%5GqDh1fGIM[a1m*VC1RHPa[D2Zj%@peVTM!2GrMp4d$CTTLZlJYI%dQ)6T(YK +qT(F)p!kr5+YUe#86[jq6UY[VGRTlh9DhdDd$hAjEcN#hfcSDYrqV5PkJfriUHaI +S0PX&p!,GHUYdBDEAQY*d9,I5,A8,h9`hddeeNd"(IqYTAlH+cp*!Yrcl,flS&R( +C8PmhM`Z*qYUU4FdScF-mEBSqcbXY,mJVU5bSl[0-p`-FZXNr9T8kZL-9GCS1)E3 +hff-5VXLq-@I*a,FR6Cb`BY+%*HpdhLRlTTa'e2rLL@[I,XBNR&%)j*f@AY)1rbe +4+rZ@R'AfrbD#Ll+IbCQH-CiD,LZblKP`&m&Gkk)C,E0[R*JchUc-X6%cl!aXDQG +,BilU4VUKepEEAMF)G12rAFBdL0BTZVlAcY0J@+CllHb%U4ISHVp)mqQ'GX+daB5 +TUq[S6*fKdl8+G0eY130GjpIcI+"VK*m#MGq!qDr,8k$6IlTU"&TYAEJc[HdTPl- +de1qi"rI5!ILM[UkYdh3Y(Q&CQM`""-PbVY+TI!JIa3Y"+P[%Tr#&I#PI`9H"ph3 +V2`B@cV2m-JJYEr1(r!Pr"[,'pp*(EiFcESS-`(&hEmR"HA886UjPZ-mG*[1eKe[ +HU9TNL9bX'HIQff@&2!6fr[2bNV`PDcFDq8UqhDLplEbdM4YpMH8S[Ri&bIA,UM6 +TfVqZArM0VIMkCI@4l2VPke6V+5h&')-#HAbl`bpfK6683(Yr)RN&8%5YS4F&fUN +BrC'p%N!"k*mhrf#MqGGp0GLSrh@q#cCZr1ZaU)jZ)2HYHI1YYpqC1@[YZqp&+#h +RX3mlBpaqp1E(Rhb+Q-q5-6IQA)50dZSQ`iC+1G9Yhi+',Sf"XYk#fX5SrTYB-,& +IaUJ4mXDSF6,c6H1T9BbD),jTM*V&U(NBhk)Q[PD-@Q+J!eNVh1BD!9A8)SAG1N4 +UAm4G()-8(1*JYdh'KHLh"lTf-8,!$L'k$NPd0iIS1QEIR"1M6T0TahEfM9&Rf&e +qHTI'Sl0#ld9aEpI3Hh(Ffqfr-[rj[D3GUY)p4MX9fj,XR#K*$(p"BjI`@9K&m,m +Pq,Q5@h*3k9hM&GmYhP[6d3flarZY4c`QIGQ5'2AXh#llPM!e)UeiT-f`4df'hXN +-k*3piafc9df'2SN-B4DJlKY(hbqHj4*JkKr(0U!Qmm!Dp0Ra$)-50321[H0i"pG +N(9*6ND(a$2[8C"L@c("*$(m3J3b`4p4Nb+QT@CKT@8C1ESCR(J1KecaU&FmH`95 +hrr1""8aprrVVi"a[q8P"r&G'MdEi'`PU$PpQ49i&ITq!&YVIJS0pT[ep0pJAMFi +H0aU*VkfSR'[23!pAP&9JRk*R"Z3-c%EFkre($,3UXGqihhGMFEr[aZRZjpfiD9& +H-54AZ6dQX[A["0[kpbV%L32fd&&$*Z!((hLrXEQP82U(q''S14rrjK[)4N2`1Zk +$j[SmcQrH2+8`c)(I*)%mq((@PaEqID#,`b%dU9h[fGq`#he4mYb22q"AZ1U6Pm[ +jM3Bk62Eri4,j8Zb2$XDaTMJ"rM0$Re11$hhEfEk)qfV036ekaheh`lI,"m[$1Je +`L%)IMVh*qNfcV[m($3!,3R9TE'3J9A4TE(0ME#kj!*!3Uq8!N""4G`!!XKN!!#m +,rj!%39"36'&`E(3K!+q0'8#[Thr$!!"AC!#3"LfT!*!%6+X!N!M[k3l!6ir6j+X +"d'qBSj9pARkH@@rRCqI)FZ4cch,8fFVmcZ[-jr6lE1*P0[#b0j[Bc-[[c-j"0V1 +*fG6,%1"eCK1EfFcH!cJJJ&`rlXQcKr*cBCXYE1&3XT!!#5JjN!!m*#KB#-36!!G +fGSBRBL)Dr#940PkH0'r(*%5-AA&H[fYKeeNalkHVB6GE-HrRJf!I[-,V2V'J-$F +dZ,bJX%b3!$NVA&SqU%pfBlJLhpmM+Vq9YAZhFjiKZ3jY&1A&L!P3A0[T4&8q*4* +E'9,P1B9Ca@8&T`iU,dh0q5HJ3%rI(,lL[,ir`CkpV-[bEi!QIpPYlei1ZcAmhm& +1AGCPa81`TklB9@0mGE[q@5VlRHTELC9""Kr-)r!HE2fKBSUI2mTD#eV-EjSaX8G +fFDKdBP'S2,qJ,&5@8eT38Ki+Pj38&Z5%b`Z+LeU(TK42$%fB@&BH'P03#UZJU+` +mA&JBkSBFHB2mI+P%!j*kKFYb"hFE&'UAfLl0[NR!'Tm-l%a#F@b,S1$MmC'ka![ +&)r)!5P!`T-!2LU-UP+KJa2@*9%r6J@DR"l0K'UQ**ERKmVb5dZ*aH6RPT!EllLc +R&MS3F!*QKd*MFR%Nr5qHqB9UT&&LI['%[**`HElcC-%MCJFDeqbdG8Q-eL@I2#% +23)+N&!`TMG3PLC)9$*lJqpRm('Gq*PD8A)(e*mrm4,A5k-$5[2+*T8@6`S94e`R +K3J&+S45UUQ")TQd!bU0U9&h"L$[HlpYFUN%eUCD#+H@)m0#aYDQ1JZ'A4e&61NM +S)!#T5r88$,5*"9EEE+IkjXF!c$UTiEa*jF@ja6P&%`X,LDBKE`U&#`[+&08RX65 +JU,VjJ4S%B-3(A'3$NN*dB6*UdG"mVkJKLH8[kfp%)3A$Vh)bTCM[222G(afP+13 +D"'8fh`E-Ym#A8hjU1C[[EDIp#)2Ui@e-643-[hKeU)(jaRaYYJA-0bMHJ!&C!`H +%aqHPPjB1)1S+pTT'YT!!E,kf%,CC#%h`0M9If@l`)65%pd[cKINmB,k-31K4@*E +PkQm*-!VK#`[KFfYmCEZJMLZ@4d(c@4Ac'4U54me4k-#iJC'1rT310Tm%B++V3)e +jTD@K9[KFp"bb2S8HM6(!9XpXTC3d5N#Q#@9MIAY!f9JaReLmY9`AYdDI0a0UKXS +eTd-8$,q+VDQrqCKD8-X!c$UTC92+LR-,LmH'bmBl#X,Rm,BJ'Hh$1*TDdD%+4X6 +I%@mE"52L2iT5UDf#%I'hTc4UTf"%r)I6BA5iJK(aYm13!0"H`BMifp+4G*5#iEF +*X&'()(9!Ib4RjqH&4[[$9I'B8-Mf)mTlP1Z$0T4U2[,-4hXiSVh$daD&k"K%dHT +5M5DKdR""@9iZKT!![""kTVLd8mL"1Yb"DNH(Q3m$jN-3#KJ9K061a4e14jJ2222 +"2jT1!D'IScdGDGi2Q2GMZGZiZ+1SJpN5-&YLFBGD%M`3&HP%RFal#UCI+1Xr1SK +Z1jJD$XS[RSLUp`RPj1IPM#mT,LJUpf[Q0d-A@c)fla%P8%Z+-jZT-ad6J"NI'&e +H"*EY60`AJ$U$eii91KETZP"A"F2(L@68,8MG8)+iBAPP&S`JNe"hmkkLlLl*-8M +E)dJp-#e)4V'I`laV!4m$`1p36l-T!$-qN!!lTV"F88qAY!IeS[3JTIq+,-&XX[! +1`G[,E&4)%$mFQ3D!QikMhZCYkU2J%+"#c&$U5rd8$#R%R$Z!XN&9EbRU6e,Njmc +'Qk&JX*f@XbME["N`EpUbjaHJ#KR4[&Q8DGj3P1Rb`'pH$jMAEIq9&@*)H-0fceZ +fkrZjZQA6%-S+)Qe&h46eGM2C#$U*MUH"#SDID`500+r&QGGXLS%9M2bUCelppjN +-G8BkX+p&&%EY"e'fJZ%M#Y0SmdUFHF8BK8V%%,hXQCIh)YVSceF136a'UX(*0&J +D*ND(%H*hTpQ4-f&Pc4l&ZAP$`U@P"F@PS6ipdc%i(j9k"'CAM%qFrFM*rKJejq@ +j2$MTD,VKb8[p5DA$#a[KEmf$JpIe'0)RRDLU2pVj`0EBrJKNja5fc4iIkT!!Q[E +lHQ"&AkpApjkSbEqMrrqqK+qAIqBPG-[+kQmV%GNb92mY5Dd"HH@PaC2c5XHAKAj +&M$d!YK3*+r(1XJ4A8E41&3PrGHh+hHRI+RFJ,D%Pi6%PEMk2`h$ANBE44,U#EJK +QjBI,mN+(KBBA&qApXj4*I3XQ(&)@bV*J"i6r[VMj9e-&N!!le+GSE(lB)Ir[eU& +!kU!HEIYN$NJrVT[Y'd%I#jeJAP*d!R&rT"L$6F33"IlcH@F-M68[HZE&[EccNN@ +4J(FS$9-`--VBR4HC&m`'0LpBDYeJ8`c$1ja'+"MqcL8HbjD4G++#%GZj2'r@Qh@ +HHClUTe&`E&kjf`VP&T6kAVIck9P3+QDp(6r@@FBl-ET!D83R#DKCD"5GV'"`5D6 +SB3'MLRR121ZCjrcKF%aaD8kH4H8m2U*REBe1aMZDFK3-P0&IVCPR+*GKB#@6L`' +L6kKA*'8)@kY`+$qF-cj89SaC,&`HQPL@9eTQ*e0rT99B-,SdA$SPP"-Z#[NirB% +0X,%c+LkGiXDX(,aj0%E"L#&qQXBb$#!HLe'S@kLm1$3k$b!Rj!&AdGK3-A#8@X4 +fGiE!heGRS*3Bc048M1%@k4Lmq95JB2KE(m[bifLmH8V"p([)lX#`,V,p1i'+&!b +fV"@(9Le@91`B!9lcT'HHr,14U!MT5m`6LNSFS4`!9+FS1X@PapE021kCarIdi41 +fUFGAE%)H-f[0S`(cf0j0#*ZePJiHY96cP#ee!Gj5XdC4U9Yi938jP"%'3NabGZ# +ULFh04*UNB2METjTB8%dfMbLDl!C'kcr92+b`er3(6HZI3UFT'2ji94FlViF#jL& +rbe48UZLdD0kkG,Tj80(T89aek3ccJ+)ch+B&IV1DcM6h"f$@5Fd,PjD0'C0E@P( +2%)AK9h5QC9k,TjDBqfeC(l$'JpCif"U2f#E"IMBk'YlRQI[fV$h+AG'UBU9f9M+ +GaHp8cS5B![M%K&i&4EPjTFhiqe%c#)XHqr,J!qHEap1f1$qq+lG13mb$j[(MCN@ +S0C%cFckali"Z23EYRJQR@G+TdUFXId)icf(pfdaBYRBpCN+E+iVqr`b"[hebrE* +'hh6YPG&cS+e%C#DXqPX50@K+@ARH"$"iSBq@3@a!MdV9Ma5Udqi-R5*PqCm0*Va +rYFMrRI$mQFkXX53i!Zp8QUCJa%DbHmdpE1ke$(+26@'Ep'bDVQ$i)eNL"[ecD)D +#%4[*lUCcc9dH6$Z5qGZQmR#T(FdLhQcR&6SAB-icGbSkVi+A9RPQ&G9*Si$,#A) +SMrVk`bIQ6PZZZbbEcDKBK*m[G$l!A%!c&BcB($G,#$1lZF1Xp-`GIMQJ"bJYMbl +PR6HkNcFBmB9QiVf3!'BV'*&pqS&d%9fXB2J%MIkM5m`+4CHiYE2eck&,&Ba)!aa +JPTYPjRE2,2pV*PeQDhQlVH@PMR(YV(0E`0`@@dD[X-%A9l6(8XmXr9-M3,04ZlP +dQB,Kebk!h*I6&GE`!36)-lILfGm*#M39!lc%-dYm`*JEFiSR&TAlRKl@)h3C!&p +*9bNB2Z!N,1IRdG8+KMm&*@'&IJeGUf$%(F@-RA`pF`YG4pF(B+,%aD2(+EKpX0@ +TTVRC-cIr9CcVA52@`U4dJeQXk!Bh59RrM@D4SKXGGZZI6`X8$(q&9`H6e-+!@3M +FKAP&@,NZF,KV84ecNfGZfNXkE"CC!PaXHa(b(CX9%JZc`$-,pQC9"2&0Y)hQHfB +qe8dM,l+*pC2"Q@@G2V(49@LQQfLKJK%h&$NEBJHab0a)+2%Lah5T@(cF,(3c%Ye +#5a3-[qY5XHLqPCBU'$i&T@+*I!2GCUlhB2j[+FTYJ(1lZ8l4l@kChKCEpQ@dA-( +`biIPYlR@-pIZQ4DAZcaBf*YV2(10[rJ"4lK1m&f1mF9FCrRZHYY'5bYDifV2A"h +0kCC%[QZJla)-8N)Vc$a&+pcDYb1DB#AG3DX)Jm0+YrEYL)AdAA5hJZ&cJ9f$hf2 +!5rFiQ2#E+cecj4kBE+kb9EREX6"fk1D+1(1&$ER$Y9iAE+6[&ESAaEL2lPF`r0E +VJPhhDX+i[YS"aBlFA1kCbrpJ6%82906p-XpFpTmc#Gd2`!qDZBSHG+Z4(UM$3iS +JZ(,,mTlQdML$IDZBZACk@BAhBF*DiQ(A',faRPj$MbSB2ZlHe-I-mFbF2BfJk&% +(V3re0CI%Q8Z-%AS%U0BDc%"VA60NB-ImQ0"M#(ZFX,Cjh$9$"[EE6a+@3dmk#"P +BS9m8CbkbdU1R+ZSqfc1crkVl%m$fY,P3dG1ZlJ-"q4P&ccKN!fQ3!*N9CfDK11C +#@ha)h"6GkFS`"!6a,$fRB-J)hcqFeY&k"F1[`4!DDQCkCZDH(Ph[3')jD5k)-aJ +R&6eA8Elc2A2qR`14QBI&0bMl6`C8Y0J"JDV)R1HCmrD2"![4"Fr6"JA$,qeSl2a +HS"F9$,p%@$bDFq2-ZEC%,eD8D)CRC[c9B4Z!l59cMU+A+VDi,bYkf5'c@pcTF@B +kXCKc,,0-arX+[DTJa(B$CjYTE-kfSa'8@d+[iRf0APF`r0e!%22H'r5QJK(E$8b +PYmaC(X`QDC4mbX5mdLRBia@Af"9N@53!fp*-&b$d&L#pEFj8p(C&GFr`c"Rqf!9 +PAh5aB(h4YB,"+Nc-@CB4hSaX#14KfLLd%@!fd6X+4Ub%Tj[6f*aZ5hLD,H%lH0q +Pc3T'E,FkKGjM'#MRHeJ+(K6U9MBq&"jG2,%m00PZF1fUe@ie1VQGk@Dm@qKp"F0 +Ii+%-jP6kJ+!)1R@I91S$TqLa1kD2c'4&(mAdHr4aN!!qKNURk4#,0p,!f0F@qh, +(b@'VkiN8)5,VQ@chpKp#C61*YK*%BC0LXTkY&Q8!@DV3TfDLSNqGc-Ik2a2k$)l +2k3X&``F2-54p'D3[8IZ%3IPjKB84TFmAH,qLE3U'RmIZHlm1dYGSq!2lqc[XJUJ +SD"[HE`bB$pV4U-lNfb"pLb,%GmGDfXpLS#"N-p&@l408VBbq-k8"Q&&Y%(5Q0RF +LqZ2l)(fr+hH#34m*[Brh"rT4`BKX)UV6G[T*`I!R`L5XpRif@#Vr(0e-9+9I#2V +,Apaf)"NU`*+!+BN*JADi1$[R&`F-&+J4iFmTPPGqUQ#M)Xm8q5aZbD#mf1p#jm` +ZpM@p3MqL0MY*+aJqlp5J'QD#JFB20@@$2Ck3!-CV'-L-iafEBM`c'p"A%)Z6aP6 +[9pka+ra`D3lU"#+B@!SGJ&2e#J[(+8EA@ZDT3Bh01)lR!`*Qh,qBKb&YYF`$25F +RQ!,&#DlEV,p+N!#VB*r3`QFH$")jH@9PN!#br*0kV'!K3MmJr`3q!)fFciPmB-$ +N4qQ(S35cp&1,kR(!M&8-pDZP(q[hK+&Pj5!Va8&AJ9T8Qj1#R)5qU1+[3l0kZ&* +$RXV*R+)BLPDE#-*6VKVNUPCmG@5(m5i*T+YFcBa4A-dPU8-(FI8J9kpN(Lc0fBb +e"80ZNmFe6'l!j%@CKfZih2@T!GF-FXe+jXQe-12`eZ,DLQXjjQP#Kh!G2NKa(FF +m6DJCec8jLZXkjV(qHPaIF6h(-%fTQ4NG-+1Mc-2eA9`6+%A$!411-3qfMBUK&Sm +bcmQH1CN5dbMH8S'Y4'fm$ELKBPmK+pM"Yc#Mc%PX4YNDRf463#[,M6LNZ*(M'C[ +L4'l-jN6,-ieriaN-M18&15%dI&PqZ$3[er89e)lFK*XUEZ)@-LeS*Kr-cFa)EUl +iB*,Mr*L1I!Lh8!c4RYe$Y+!fh*,"05eGr5!G05-m-q,AqLPZjAUY*EAQ3i0mk'p +GVaKL8SXPMGTcDfkMZ(A&hQQiCiE[aJ,GT-8#Q5LR"MPe9pmUEZB+ejR1jVDFCSC +a1m9Y(EM1e)d2im-9(qE!3HKSKRTQk$k+9`cTTe-mGZ8MJRc%lh13!1)d0iVfT$# +h&fk2(MU5Me*mT12URM6#$1%1h$&JKZcMkJk1Uh[5FGc*R+!B8fI8Il3CV2KS9`M +VlacNcK$%T[Am*eN2LVAf),qe3l&b46KmX$91X,cB%EbBcFI`X3'6(52bBab4pkC +"h-8-8JaGU#9bkqmU$#8SGq2ZLVZjFQ!0a6f#h-1I)hhNMNUkiqh*kBSMiXpqP-' +pJYc,cCX9P*11pcJc82&adFh5mG`lb,dVU4ck(cE3P#6`X5MCmGc(C!A-m6%Ula- +6DR,I)+UeQmUc,-bMm2BcQBVl15SIL@EYc`-8pL-qPBqN8CaK-K4$*@UTh2Sc18Y +a4#9k)Sdb!`*Q3)c+Xe`F0'ZQIm$dMe&jKQ8%5&#M90l2-rhmR9*ZFB4TiA%0)!C +b*$($E%B)L5f(j)+hMKFq(Y8Db1LpJBj$FL%NkX[C2$KJqZlMN!"XaaQjd)bGB2S +S2U(#2m6d9J`KRfd8kamDj+&BDREmrcNNe"`KGPd3)C2HeX!++)%(Sc12if%m2'# +1Lj%*G(k@62+TM%HBAST(1$+arT(#)e(R%rNN"GQ0AjGm'XqMJMcUEf3#)4qIc'( +&*lXmikQ)4`Gjp0r)"-SqcM%JR4bATiK1iG`JjeD5#I5UE(VCNJe(bATbRZN4-$e +MC*,RFTI6C"i6j$'9C0,$`Kb%Gk`""ijeC()'ZMDI-9iML5@6-fJUMc2G&)pcC', +pilP3SF9pdML6TTUZ!G-e4LDBefcF'9#$G3QB,M%bk@DTSD##6)lec,&lb+5lVHe +)Ql'j+mX-6'm6Z%LKR(j&Cp$jA"`%Q0hGTELS!Z!aRMRQ6i$F&'8[i9-80$Mq%$U +,CTR1jQJfR@e$(@dlia5mTBbqKpE1$U%f45G'*6[C)E6mYb9E,f`H3$%Ca89Yi() +YM*Q+*r)Na42G*$H,2ZE*I+VTb&-83j'([NA-)h`DRkiB5M[R[iA2i$-9Rq(BCKC +G`QHC$SV2FRS5kjpU-**"r'3R3HZIaQFVk0,mbXqL#mf4RMPbpjKjYQZD#qPLRKl +NkEYSJ-e4eZKJdjcT)-qPKAb1DDri(!ICqQHB)a62F&1cpCr,jbQ'--U@B#jGaHI +c"BV2Gb@!YXFFlTR$p`qMf&MESPa'9r,-)-qXA0iT2Xq"ZiCZj&PmSH*C$Kad1ZB +`cabf(pb&$YbeG!22$[,XbST$P-fQ[38CN6-ZT3Ii)S2a'ZST@f(V[jJ[8Aba+m* +5@X&cq&,&Fe`4PN+fNZDCY$rS4A&%*h8E,HHj3CjEbHf+)FUbX&E4[A`CAkli-JF +,@K(6eM0Ypm##3-V#ZT2Zi5Z#I%9P9F'kLNpeXpGDHTH[&)EHLDrLHBU[FV2A@RV +6T2,9I%h!T1kE[DjfXpCDHS+[0GKK3%jNJ9[rG8'qEXqmEPP`%U6#GLhZj-*jP50 +@'cZU3'CP@[2eI%2!Y)j09ljH+J"4aSYmScP8mBeZZV,qqF,c8FN&I*0Lb+&X*Ck +NChKKN!!AfP9DM'p[`VZ)&bZ'%-NQHBE@mFe"[YQZD(B6mf+mYaM8q4DAD"eYi#9 +"AP)j@d'kbZC3@l!E8,#@I+YT%6!YBl29V5lh5r3U,`hbdXVC#X*5BFM,q$CcL'* +SPfb$[B@Z[*fA+BD%bFj@Ep%QAQk`MPVZCL[VAm%V&Dp`-p6EY-Nd#aMXJ5+ce8S +ApaE%"JF(c-'afDUjEIf)$XU12Ndpdj55dZL!h1)aU$P-P&l-)EDU(@dZD,4Y3EC +J),Q$9bQ'I-M@FJYpb(F'qFlI'NEaUJTX66c6j1rBH"*UI4IIVGKUQj!!G#YY0Be +0L!dZ3E%*fADi'qmpI+rLHbT50$)0f65b+4VD&"!ZmAem[q,l('&Yj8,6J&Ic!`( +6B"pKVADLX+dmJKrNKa3r'282iiIj%F83'MRrB&l$MbTlfF(jMqHer*MLY9(r!(k +FRe!-VC!!mrIK*rNTa8p'rHRm0$qMq1QS[aXrbmmTKSM(qBrKGEaHmEUS[b-rcaX +83aRMr1hj"Aj4m3Y4IcYqL9p@r&,8hiCIi9F93llLr#hj0AjG-E3UcRm)[m&[+Ri +MkQr+Er(ELU&!FIj'[*%h+BD)a2RVm6[mVQ)S4Tcr)0l-lbQ'rX2jDr)@IPraPUL +r+Rr!(bU'c-2j&Ar%(m1)qMhHbTmSKN$$qD[`TrbCiNqMrMMqR,p3r(R%$lR!Prb +9iLqMrPpi'hqY'(S,jrq4[q&[&Am6pAr,hr(hLVq,qVrL(rK(a6miJYT+Rr(f)'q +(B,2Q`,a`lT5+d@0LQEdJi1MU4dGARp'ATRl!e)qHSq([hBca&Ah$2jPkLL-##IK +0hB#T'mR$TTi&mDf$r#hp`$m(q@FS093)Ae6BiJYT&(rY+2P(b$B1#TL$!+%m2"T +3)6QaFEq3!$Ce!UC1,1i,&fFJ@+SG-,9MFCrjF@LI"&-VB'V&iMjaF98J4+L**aE +hX9mkY'23e2"-MIhMm)GqiG(-+Ib,UDiBFK+E(Aj6,@#Ua3U2Bb1+hrF,$bDS`6Z +#[!1+K'Ufm"!m9@JV)Y9rce@R*YFa931QDU`klcTm"d&5N!$LQC6Gb`KIeK3!-6A +NR5CCm8kA#hk6&$"*X9SNff3390PD0-)UA`GC4f[4[k"S['hh#6KF8KLT"948YKC +0ZEP4!4bBLGELGBF2Hhi6p%a`ch!0ACLY5dYXlBha&12-QXd,[`N%$+Dl5&dm#pE +Tcm$@DEKV"[K*P1Mh5,Jm8JhSh'`ef[%4"Y+5!f29f1$LfR-(NaJ`LE%iU2PXA%I +ZE+S%6*9B(#4p0ZiBlQS5!LBK&JH*SBhVaMd0P$-(a1+JB,4akGcEa!G-I#cZ#4I +AKrZEZ)#"%LS5pjL,'m"C4J)388EMS+qdFFGcYJ(ei+"!*1i4&cHBKaTFKS03+K, +hN!$V(Ua-KE94d2Rih31reTl@[dq-5')Rk`FS6Zm8NEL!hKPG"BJi,#-j9q,e$L@ +i)SC9J1mr3!5Al54"ULKa!L#%MC,%S#6D*8Z2F&%16JlBm9fUi$e3rk,N3*GR&)q +@3&!#pXj6*PERb+&r3Cc'cES%L8-KIKC2ra632dG(I2&FjMc1Pf"3JK@C%c4Z$3T +$pLT+NT5!EE$J`iEb,%Q@&,eGrkJNf9rb)+CFUNSe*EK(Jk%IrK+TVRp38YdIqRe +r$DQT""SHfeP&A++r$qM[Sd1re(4af(lUl`,kZqM3Vhm!cBJ6$@'9@5DeJP+VXJH +8T2LL55`G6j2D5R$cc[E4C*kL[aApV@f!lEB"l"8j5F*E4`j5BJ9"U1e8RUUrd9q +crXDQX+IL"&IbT+l88`+0N!#p-M'9cj(kdN!*Y%!@f&3q@fm6MBYb5KV%0JEk+dp +rj@Y0*db*#L(KLZSJ"HIVT+%d8J,9$rDX2)0Rk#mPa"SA+B-5`P@h4U&"B0k**Ii +kb@h[Ti3LDNaFJI'(2K&SKD5a0&(5f$AM$1M-QXV"qJ[pZ4,XHl(B!lBVT*Nd9i* +6FEBjCr"-184D+$R%dG8-2PGrjZR2rQ1F%T`3X9eh,PmJ,B25mZmk'X(DfZ+EcCG +++cP8#84$&YpX[NKrkZP2pq-le1'lL1G)kk#dhLd,%Kc(`iU2jr&1D525"P92PEC ++8Ph9jr(pNLEYp#GkUa,FTE09RmHhbQ&bZ",FTE09QFIAba(5AZ&dS9q9HAberYM +6(qmDdm6GP-1bm$Sj-LK(rNhQ*)Fl,20jX4`P(C5i@hA`,p!IHIUMh9Jk1#`,H*& +d$%V(bRUfFf4b1pmYRH4S*4LJ-2V$IkGdPQ18i-5E"BE&QIl3darZB4+&H[L-ZSa +AbE(54FQa,[0bAU8rm,5lD)IE458P1EkGPG9$#44"0XFbAUlIpr6lHa@l5U"DLR, +D&Np[qAG1dcK1b(UVC58)YU5VG&-#KB6YcpAmYR5A([Sp[9N*K%@f2eIcFp*6dTA +dG$feQKq4AR+FJL$!,`lfArTG6lqlZkHFaSJIi)HPGe"krkST9!)KNX@aPTq52Y* +A#64&&JFfAISG6lqc'dGIKq-aIP,k"D9IC@qLefe[2Xq[5hmCS!3+)pZEcr1VNL' +C5M)F-+bip#C2EpVEQjQZ0cI`+j)PabZ"jXKQIS&Id4Xp[C%#VMGY#B%E&qKXp!C +q3ErYkEIhG@@&3%QrjHQhpRBPcJLbhQblXK[HJIT0*30G9fi%Q3k5E2f'IPd**$' +f+cIb&c*B6P!bf(A64Rj2KXK3*Hk#'2bEp'ZHIZh2KB)-GCfeL6I,X+!-qdd%)0! +c@9JIm#FbA%BS'HjJBIfQArAdUhYJ3ACNBAh)@f9N8%C@0La%8lCKYr&2FU+FT!3 +hJ'c$EZ-ICC5FV!6A`ba)l1$d+jjqC@r$iQ+BEGL[q3F*bfJPBCIj'rj"[qcTPrh +bP19(k"3H$%!qSBjfHElQEr4,RRjTAqZH90'k,hVDh@,lYpDeK`cekhCLIG-D1$[ ++fTiG&9c+P"a"@d!!K1N4U`64,dJHDpaR#dUHVp,jGAVda3LKL($IcBeS4aNMBj@ +-mEN!!1kAI#R3'r6c5Y"@i!,%9*Ga-Pi*9&"S@[LV5+&-8"Bb+JKrR&l[kIAljcS +RAT)il#q+JQL[5XQ,J,dYZ!"f#-95SX3G*S2Idq[`l!H(Uf3@R#G*FNT36URN"3L +[3+a55qk@8J&&L*3*GJ[3VYZ5em))0e%QkHIdXdT`1Xb@["B'imPbUK))P@a9DNN +pQ5+R+F'T-&Z9@P*E2q2TChB2%kHj'Y6'QZ$dS*bqHbM"b6',Sb''mM2N6#9R1"` +BrIA6RRjk0iic(Bj'dN61#XTCPE@%e-#+k9[*i6*9TLQ"%JSF"hml19ZQ+i(Db3, +$h+fIm[46HcN1eb,!FA)S*ZCcC)D5FecQeT+QRr6dNa4+Sb5R,LkcE@T,'I(lE3Z +r%TahXMN2PGEk#8mrXBr[)KSYbhH2HrVa[AchR#8iHlN29iY&cTAcP%"YKEf-G*! +!$[Sa[CEeBcD&[3mRN!"+bIRk85ARZhEZJ#kr3'EU0IS4*4HiGZk!5A#@A+J%PkC +X'hD3!'0NYPbNC,DVA`ITU"rfp-1lfa!h8@`EGX3JIR&3,[jp"i$M8aC,9dQA5f5 +1NNXF&XblqL&22l3E#k46&NXh6,UA"ZA5bTDHkHLT[p`KFd9`&dSZNmZ9A1EUf9m +Qb"9bTAj32k$N#PI2rTK[VT*j5R#2b0DJ[a`[9mXe5Y`e)cZZkY@HAVeRM,V'e@- +!aY&VJh,YhkBcD(FXV'`C*YI*p8VFq5Ii"q[l2Ahr(PMA1eL$CDMF%*3E+X[XlNr +*b9)J0mTm*EJ"CCRVC"NV#q3Q*Hl@%raKICqRlp[,A*!!1PRQ#Q0b@5L,P#adQ8I +,'(f[Tqpec1@Aa'FZ1k0&r$jcqC-D9&)f*kBlIBqRlpR(A,K1&@@ZZceppelQXVI +m0+lkL9b1Gl(FV'5aDq-LN!$%,E*%hkA[9)+M8,D0Ld$KYmT5*EHkIL[#A(+Eh+l +N0PHF)LR@UcbpDNqrhHlkV4KMcE+J,2Y0$bT,(Da*FVSXPa9+h"%Sq#IV1capaaj +B8'"C@*2P0&NCP*@9EBaE6lD0TmZ&i+Y95Zj`E6aGCXUGFTH51ah)kA+1AZRTPA[ +E'')TfmERJ+l[PRZ8i,+6c6a$,Y!V2,hLE`1)Dq1+!F4[BhFmbJi`HVQRPqpVi`S +GPelQk@9lfaLRkeME)hD#1ejbVdC,hZ[DH!k+ITrFVfr6D&XFQ,*Y2!ImY9V!cY" +KfAkE!ljq8"j5JRY3YMKcj&*pUkG[r9d'S-6GL**,jA*j1)J,1"@#2L821$6cj(T +j40BSJIM!STNR9qXPRPlb$c3i)'A4A!h5IM3SMeEf,GEeYQmAb6*C+imTFD)[q'q +6aq8**BmlH)YNXEl&dlIXl9[FIE*pZeL@bT2bP"+FNV+CEjDPqQC2hqaZJ)E,rDE +e2Akl+R(hT5`VkX@HAVb[9k(-L2EU)Nm[fYZVYpPHA@Vh)MM5*aT(%PRMN!#QD"` +1&BdEYDbIYjd-LEFm,FmSJAV0MT+VCE9HU'pL[G#QZ-QQH!E[Xr+FNQGpj3"52#M +VP,LE9,E&p3*2,pLlcAm1!0CVc$EV(FD(j#&pSlk"pBd@i`ff)20Kd1Z)I9jMX)) +b,R,P5&qRVf@0ifZXm@F'SR'"$D)%(,e*J9Bf39mM'q3&H9&HNTGCAi-MVl+"i[A +9HTkq5PrTkD[rZL(RkARl6aekqUVpeb!pIH9rhi4+NKHS5SUqJK,ejISbH89HeA- +pZ2qc4NND8e1m[P4HLp-3%5I*De#HkcQHR[2R189pQ@f&9b"IZ84IV#rbp#9rC2, +daAZ1(A[kSRrFHdq49h'cCV#HV5r8Xr4-IB%qAjqRcm8G[ijk"KfYcm&Pp4jk1UA +VXr8d29@I49P9p'ahp$qJ,icm#N&!ciVFL3cSQAYrTL#J,pMlY`)"IIlH(a(`p(R +rU*qJ9K4!UCad+i##1BP4!%9c'TmiI6BmHYTHF@P!6ieSEJ+SX&2P909cU@"mqKf +CQk[456@SiAY&VEE-Ilpjfh&"LXpX"HX!hdSS(N*GKYSIF2KNIV02QchAl#DmccA +l[0NAAhj91#P-+@'U'UCU3DUH1BTUK+PQkc#Z#6@Ja((TG`bM!`V5laLFZ5!j18P +HY'4b*TfScp#Rbq[bKMl0Jh[rh8jpKQ94A!e,%@MJp"426rQ$'P,N$4$XUAUbRN4 +RkBND9(RU(hH-@H0UPb!2'Q[LhP1r!9dH15*F9Cp'S4Tdi*V-0FP$+"&EFC,mc2H +VNGI5YXZ(VG0A$ND*Al)P,U-CZP5I)Qr+@lV%JrXrf$K*Bj-CVi[Pl6L0C@Q53#j +)ZXM64IqkcDTa(#Y&hN6&*qK#29k2d`8Dl$KKEdj2&qjKeI&rR0Rcp,KpGr-mAE$ +R!TqRmrIF2Nb4Yc"9M09MG*l1e6PkY!lVNcdpGMmG"$6ffci0HcT[rpQpJ-k0h$d +0k*c)*G+!YVHIaKIRJ)V$D2bFBYcK,LV2UfMm&*mZ2AhbAe0199e#"Dh'39[rAR1 +3!1MDp&@CmdHm2h,%m,8MKmrrS29KkAGQeUEZ#dGmq(iK5(4mIMf+El9J8E2aKe+ +9p,Xc&im$4AShTEq815jj'09DA'$Giq%ZJ,[+6H-ETUmDN6R-V-Zd-H-YIEjXZ@8 +8[DP2dLI+4YQN4hT`rrFFTh'[*9k2N!"hiM5%&dRbMQ@BiCiHrUrEIKThUP*N)aK +QQ"kUKqJ6p'#GlHPKHh0kqMq10hNDh"PEThVkK$p@9jiHr2YDa02CHbEf&0Q%[d( +i@!r#qEb[k9XpN!#qemIV,*fT-b!k6S)+Z$VN[BGbUKl!4h"RlX'pZ$IhjD&@6mQ +j2)D,H6V2K,*Y,Pr"er&#[TQAm&+qNlIVr[bclXFl@%23Q3JC!`5+dK,Ep51aDqi +V[D@IlS2ekNMG'fZq8l#K1dXI*aGM%h5Yh+"lB8Qc8UI,`r+SlKP!HD+6'a3lNFR +0hNc5@IXQYmcBj*B4QG`#fVpl9&`-'[-[(IQ$BEq)MYA6IIqK%[0dRphk*%rhrN2 +qiZRMrNXdi1PHrl@JpA6kIqhr20hcMfe696e5(YRmhTEh2jJ`kF12F*`a-I1C6eZ +$EMpllr-[[N6-9l'B9CNhB4LeKjGKipBj9@[HJ2SZ#%2chS#DK+R'HjK0-CU'U6E +bJ[eMQHmF4Sh#G"$LkiDTATMUqr%0GX9A#90$%$U3!$A#FUmf8)8X8YL0ID6f4Gc +#-'l*)3jfdeLFMrjJS'X@*J3FiU0V%80hPiqZCITGQ@&U0BS1E@EI-,@'hHEhGd% +N1YAhhK6aY[@p#b2HY2q9qCr[SQDS5VX`(9CS5h*iY#4Kr1Z(RGmRB45*bmmF0`R +c#LVG2P,a)b1p03lGF&5NhcT%BT)@c`p6apE0dZrf8b1b8b6$dEXbG)jP3+FF%qQ +BBhGPk",0i'F"kUi4p0dL@4B"8rF)YKkl-[IFK6ipNU&AY'E!H9`%EqpG@I[XUSJ +p@'Bcp0Z9SAmX`k)`IP)$'@"Rl-U3!,QVCRkQaFQC@FRfa`J`F!9rIZFG(#'R,IE +(F`V+lHNZc'bqS#[b(a(Z[h6FX&ZQfV3*99bB$ah4p[#dYKf28UTEdC4)LYKI,C6 +YrNNErjpXF[2'&"6KFU8pGP#59eTZrh1KX""h`hC0b!l!j!+)bI!(#lB3*B9Kqb- +-a5k01dU"%`@U`2h,3Q33aVmcP*@RKN+$lDqCf"VkIchLl`53!-cqdS+p*qPIG-A +b-`"5YKGGSemGrbDKqq`rkB4S84I)Bfq&iKUh8,%ic6M"E'mE0iRDaU9eAV$XPPl +0'lATE5251LqjdAT$U@El(@['KYV'K6VRP"59Gr[5l'M5Hm'b*KFdEp5hkjM%[X[ +0MV%bL92LQUHDlc*1bTLBJH0kEB2$,QRE15-cZ`r36P[5U-@S8("-BNPF&lVekC0 +EY!hH1MH8(8TmB3R9DGb9kN4mpH'V2rL5YM9#SqDIG1bdl-`@4@C("D+-e5Dl,ie +YCC'&5`V,fhBZbbNT,fQmT*9&0lBDiS*YFBc,AXPIdZV@liCQ)Q0j&!2LkYZX0Vl +A`(4l1!ZVa$ip-TVkRLYYR(98X6r6%6P2kdG!JNAaq11I8h'4CJE8m6JE),J[%)p +IjdP-C%UT3G2`mbeGZhDMd,CTde*Q8YI%mbJ82i0#%2#Qi+KN#M6e+I'*P*)B6k% +8r%e+L'PD9fM1TX92abpip!51h[(BFZ'b3blfeQZ"%hF'r6fRa4m(Z`S@p&-K3m@ +[ec"q,bF12me6"ArMBhrXTa%@QPf-@629Q-hieB0YH,IMhB&A)mBJ9L2($Z6DAJA +aFFL$Da9VB%q$(aJXMKeiYe19MlG4h'2!L@-hEXrUEpRYKp-DZlpYPN!U[Z6G#C* +4*pc(V8Lf$Rr(8T%JQB,VGLAB4Z[@`aP0B-14,jBJ%Kj0i)Ilm5k"$Gqj+CE!$pr +N!0J%aL"mijSB!Vea"m+$`@L#RHXf)(a0,-'1GFRV%8j"r"q+6C!!6'[@)CaF`Z! +'fKE%K`03[Kpe4!)ElXFM(#PYq"SrhTEC*N$iHYb-m-2pUL!FIqcKKrXP3rLkD$M +Yh'E$em6#0qfS$0qiB9HiVH#ZF&["GhG8K&2bQJhENYI&`Yf2-Hb)K8H59)4(Zbl +fGH9T59fVKKURG%rm-(lHp$RGZMB108RT@HH#CLR03`fkdM5HPVbQdHBZfkCZ"qp +SBkDD,VV4MNEEZfcVXVR"'Tp)l*[X-"RdP28'Nhf3!0[@E,"pQ4`-qTaJYQh`b@, +RZcYpNYLC[0'@,YQf'AV8llrefj!!+aNYL04!YAk(6EecJqqQ(4Ym0dJS9Y0),Aq +)6(,fTjNLI`HLe2q3!$)I9UXJFX`8#C43I5BmJD'E+3l6KRhY9H3"Z$TI$U82cY( ++6XKbSP23VhplaAhlAe!I4bqT2Xjh*CIPP1'i1%fhFalXLqdm"[ZDl25Kq&FEZVQ +XI)V9A$cF)l0R1[c2GXrSD8HV,C%jl-[)(2DcQm)iS5#Rd"lDUiEGPI8hK'hpVI+ +K$S"pj+!q`qhebZj$m(pAGR4e9!C2hb(A%IlH!lG1'm,hZIAC1X#A%Tp[I30Zjll +$lUXqdrS'hBHiG5iZBbjmq!F'JhYM'FJa"1ScJpY)'8r$YphKb%5qBGLJqVih%2G +#*'i6I,hT5Yqh&6PQ1er@jiMEl(`$Ym'ha2N'(3(I+jqXXElX9I"PZArcb,CBlh+ +BXPq$Vrcr!3!!$3!-3fKKEQGP)&"KG'KcE#kj!*!3p2J!N"#%!*!$lF)!!#m,rj! +%39"36'&`E(3K!+qR+)f[TbcC!!"a+`#3"MXj!*!%*c`!N!JT"`i!Cj66$(m"d1p +bNU19I9jq$Z`kFj!!RF2mI+p*CPjR+r0l1r1+ITp0[0lH$`8fmr1DfF"VC"1cUCd +K`0l,lqaXB(2a(P"iekp"hhN,bmdk`MZb#IMN3$)!b81#JS@!H),A2,2c!)53!%+ +)i[P#&1%[jaJKDQ3*,eCB@"BV'CYI9KV'KVLB9XG(ZSq,&Bh0MqE%bXD94JQjFf) +PCB2l$+Q282Mp%[rPZleklcBZFXbq@TdLk"!KU)LQUjRU0+eQ#MT)+-4,eHRU$+e +1&l)i[&mFE2'$,D*+PNM,+biG&b[*cq2)i$#Le4P#6N5HBLT4Xb*UPSL)e-&PXE+ +#A!'-Y$V0P6@&MPHce4bYCVZbTM#!0E[m!49raiDRY"fA,$iFpmGrEij(LdA+[&& +mQYpNAX-"NlX2+Bk@6#k+PSdV+)f@jTB86#U,aLC0+Lc)"56&4FfMdiSR4bG1,Lf +,MLNS`DQJU,3-E4EYLKcjJm0m,G'0H`r+,md[LrGDLaDkEm(%D*qLXH0L%k-YSQe +EYFPUeD'GeN1f&9PBPPp5LVVc89PKIR45Q,T*,,GX-ZUE&LdZ`U'!JFQ2PNiV,FZ +I'%maTVJN@P3mY@QdH!b++!`6$-NYE$9N3R455I(ir0bbdTCk--$)$IXrcf&59Kb +043X,J#$5PZ6R&TINP8CcLi[+BJ9&"89M`bU+#r2#'U1aSVc`3P(qe2"#bfLdcaL +1FLe4B0LJ3I0S39PdDJ(U(jdIcFX[c#r,cd1DE+3YQ9T3QVrYHK`)p)QS&T+B%1R +#%jNi4j+0b[d04X1r5I`'Gh*8,1cF5FJE0b2f#Ijb`'(fqeCULQLPXMV0AhapVmE +e@[6Q'eQGEVL'Sp'@p[XlPSq0YP,46VQ6LXUkIQDh01JpIh'$@BhVpHdb*Uh[VAE +,@*SL-eAMP[EV!8F0Q$cJ-#&D4BDIfkV6J1`KI9$YM"[U04N9MBa*Qk3kLaZI1VT +*UmL0&d5(400HZ%(8U0p&e)M(DL0@HqLjVDT%4mdlkZ!C3l+E&0NYj480@'D(p"9 +MQh&PX8Q&CDdkPHC1+TY8riCQA0hBbVJADG@*-CdNEQKfipH(Cb0M@D)'h+[0@IP +qVd%pHi8G-D02p`%0`mJPI)m$&EVQj23,Vb$-0fL'%#RJb@1M3Xc-&*,5"&'+5%N +KNCBQ4@B9-52D3(6TdP9%0mqBN6PEG%NlA846CSSSG4'C-LSb+90NTU5*c,38%Fd +Nd58UaB`ZG,+BNA++&'Np8%I[&*&fH*T)bFX8DJAUP-c,U*[V9cKA@#l%p$8Jhme +#fZq&XPY%"@Y%"ULiRV8c1PZlI,UeDd$0Qr(r([mYq"[FXEKVN!"M#h*pA`(h&I* +)j-0j"Z+SJH[BJ[rhSX+(Qi9k&(9+e-[eadN%AmE2Xm&Q*T!!mLpMHi)-i#5f*YL +mmVeY&@5)b-TY#6D,PFmKQ%M!ej%[Q5"q2C%J["lHG`RiH["1-N&ir4eA!#H`&YG +A,dp@B&C[`I9)**%J@,N+ejFR%faCQI%FVSY)i"*NL18VF9fiK*&9BR-%Ra#Z3Z# +)"(`p[)rV5-RAPiIh'@C1J1[2EBPI$e("pCAakb&NZ,ibF9d%QrRkmZ6eGlEXZ,j +keEEVM1#fkicJHe[+ViZ-jDXfCka-AZH@i#3r0h$jp86A*EmZFNCkPdV4qTRGdMD +Nc$hP[+jGkNFEC2DS-DY4CZ0SR5jLKTb4XEcHQXkEThm2hM(@6VHG6EdYpEl[[,R +cQMV,3b,KIiDVbD+R1"V*#)[F[(`9pf9'*"*bJYfm+L5,i,dJ*)NJBc9$Pm&YKKi +0qqqjcFL9J4C%DP6eh"C1(D`+`f,,UM!-%NTL'XI5Z[%b($AMikM@rj[+R2Nd@U" +b1D0MZ%J9UA[14X3lI)e3'$[i,d39X83XT5I8HH)NR!1F`fr-T&kadVbKA3G(@lG +XRFArG0a+i4P%SLU&S3,V"X6NK(L"Q*!!8R"c$j'UFD$ai58ImdB&2Y60%VSX0lj +VBB$$X&Zhp%0BL`T#pJecHL)03a*+U5JmM32[1d)!rBM`aCj#(p$qd%Ap#NDAa%U +QYEb2)5EN*!c$@Z-3TQ1-dL1BNj&ZdU6FAp*Tr$0%TXBK6!G!4D@)U)4Z51GdD06 +"ij!!-j%[%rr+BNq03jK2!HSU%@5V+M)BMF%&%l(230C%`MhaVbUUD4c#K#PSLHS +48Cd6FS'r*Ub'I`faPmBK6)Jf%M8MSLBM8MVZ(rRf`Vq@U+ea#21PSRrU4%3G31@ +&k18A*R29aVqZ$E5Skh*93(I9LiKkZ&L0`GLkM0Q'ZX9-4#L-X%DSVh%)HaUBB(j +Xi1031dY%bLCXE@P%Yh9d!pI4%B$9N!"%3a5bYfLNF3K43,Z+aK(410&$3bEmh+5 +0m0p(00%iK!NefVPT4$40p0#[#C[JhdcXUh%)%kDM[CY(42-%#3bCX,fTpm@rK@L +TF3M6J4#`Ga#YZ1@f0[@%4,k@q'H*eKU(-&mQq+*04,4*N!$',rPDilqIf&rM%1D +V"2CS'a&Y'BlbPNkQfarr!qa2@KcJdP8'cZdLSKe[q)B9P)j,G192R+`qrZe&"ie +$f*@e4%[lSqMSim"G'CmT`NNN'3dR$#dkZUkX"GJ2*(%J#ZPNIp!#Dk*$`f[lLS0 +5"AD)f"!F,$Vl1&E1%K8`G"6&*ZD(J3%)D&`1BDS,,,T%"2BkSR,(M[f,5r*lB8i +UaGL`ImX$1M*NUD)cU[P1G,AIqMKb0CK4A$8)Z'UkZQVf4V0hLiKZ[e@cRk['IXY +Gm!0h33ImZiXH'JFUG,11rFCq,HdhQ!$XejbL"rip45q0!aAJIL[dkb'LYmC"pBQ +Rr-TqDEr`l&HL4XYBrT5bBL``LbBA&VSapMc4$*qdAh,9Ac!,p8l-2$(4K`3Ad9F +FUR'JN[MmdNrdecJ!Nch#4G`!ZeQ,!BQ#dqcR)YYqjZ&BSb9QaZ,#iPJH&jNBe@) +J8bfb"I(D&E1$r96NH$LNH$b#DT&62V3-M)L"B)T)afk6#c!@$LdV+#ae02%CY`" +'B5hkKb[J%1P2I2Z*U*JPpTJF*N+Q3r%I*!CV(#LECbc-+%2%8)e$#'F'*T2$a$# +0!q$MH"@lbAjX2r,YTT!!DRKG(Si1H38PBG30%Md+5XKqc%"ma"J-+mGJSfmhLX` +XNCSlZB4cmKRCY"MUF[JLBMrdlBFlF"U-rq&LZ-C"(4jZ1Jm84pJ2a!JYMR$)0!G +lEK!MlASI4elYPr#XlbM3a8)#*$%5T4aTefPaT+$450`#P(#8'+9a#$Zf"3MqD2Z +q&NF,bJh[Ck(94fXF3S!`-pLe[Phl1iLMA@qf&+hX'XqZ3FrQPKeE*Zhlh!@MA!% +B)Zal[Rd[T%)-m0c9BB#jMZ`klTVe2$Yrm(X',8Di1QU*U(hAYqrqClJQ-4bBjpT +hY-KelG-*B1ECe3,&j,Rfb8(V[#h'f,Gm(2pXRc%SCDap8iZaVRd'SLA(L3+03ca +qQ"J[*QJF`RBD##STY'pS!BEQGZ,i4&'NF3K4(#J'fGGpqrV[l96NfJN-C9rcl'[ +*GRU$ffQ#Z`bk%X848B`E&6U@6@MI-LXF&,3SF!@#[ZbV[ReeClZpbHhf&VIEkTh +YPZrUk#akf&GmqmVIFbrCGjL`HZ%r54bMF8M1*LrEPk4pQBRj*8ja$2iP!P4IiQD +6r6$kPBR*'SH`*6RPLf++IF((mFq@R)*#TYT9@N`YTpRRIIYm+#*K--0K+ic`Y%8 +@Uc+b,h#26'EDjqmK#%#QD4c#i5C&2#L1)h%F#MYHR+"a#)HA&($cFq*%Zp,$-6i +Yj4ARPNp,QI(Kj83h['$4ECm9*hNiT%"H`cG2FK"KAf'ImHdcZb$#`T6%#IK2&c- +d$Z%d`LZ-Nm8T'JFU#Z1eaDPLTXC"YNDmTUKYRrEXdkJfTf[@IPT!CF-FT((c02Z +8&UFj("'h6pSRa1QHI4)ipZqI-qL3!*,m-Ea`Fb[6'B)aP8J"T%l(q2Ui1%2-mR" +%CB2bFcA#i4L0H8M-*M%E4FfaMfNaa`'4)5VE4chlD"`)XSpa)E03b!TaTRh%`a% +hJE3@CcS@`lC)R"84Cr%dec8h0lqde+dThFcl#*2'8`ci+H8Y[Ybhbm2q`8`q#I# +9KT%FMK#kNF6CiKb03pJkpE"219HFTh%)DEJH&M,RL`Xd$L'3!29%IIX`fBHjaJX +F*29&!rZ3!'FI!P4-FaUcF,,f"hhlB&Jl*[KilBM%Dcm(Y9pS(p$L3MGY0!DEA'6 +[&aGVFC(MP`2%[H)5JJk$a+9LVXBK6(3!-PjQPiR,YEK-T"b"'he4c"AL5RZIZ%S +M3$h$1`2&eH)DM3-93M64&b2Z2(Z[&[-F+h"m[PLJF3LEm9$4hplMfAZ!ib63M"B +,()iBfqhG[VdlT(e-()aM''!BTEfA@q%D4b$p-4pGDqr5iPTA)XFALN8DKl#8!5, +(hZRE1hIZ5KDj,2fa#9MUfk@L@TDSL2(#,@E#N!"6QdPl&aGpTF0q#1MV1R'pA@, +[d!M3`F"k#)Dr'm50'SF3Q#%BV@qbYfYaN`1'icH,@c31BFP$-IcFjY[EGUjGER& +C-%,C@helkfjJEQGJER6%-!`cpq+)@!a+#d8M@Pc[f[SSd-kY*'l&TGX%3,R0FFp +4'#[Z%%Xd$J#Y)Z*6a&+l@)ZPEZR%m6Y6aCfJae[%AH*Z$mG%VplPF"k&bISHHl- +@pcLF1Ak[Z%rM%#*iY"KYEr,Y66Ya[XpP'BA"rNEIhVJEjjZCU1m'%MH)CH*q$mF +8$i52U@bC3b)IJqd$pRSY(R$X`r%(lA8D("FL`r'(a--DKj!!IFD)mAD4CaFPkhM +BBC'2BAZKEaIZC*rVQ2'[Cc$Z"aMALZ9fJBGMLTGG8M"@3f35JM%4Xq-MGVi@McJ +`1,l#cY0LK31$iim+c#@21M#+4)QpaV2A*1Yic)'3!paHlGZVGi)aMm'!E$,9,Z$ +)BQlj*HA$`&@q[5V4H#jcI"BJF6ZSi(&lT4D21bUB"SCi3MbTF3Lh"G-`%$iPRYB +iK19-%mIC+halaFlC3SZRhEKb(%E-CdJmJr+HY4JARRATMa2(fmYmHpRrS'%[Cq# +H,!GZVQrRrM(&f#Yj5&l#H0r"NIXipe81a6-`PD`8cfNF3K62`0cf[-!!rVbV$E1 +L[G5hPqj#FC9$F4DQ[aG)[!!8Al5AD2'L5cp,c,BAqrELhlZ4,04V@MaAMZ*&[Ve +SCk8qKUNrZ%k,bed0(F6"pN,IA[MhkTM%A1$dNRKCia#1d4GK*VV!RLpHmH`&m6' +DUhEVJ'eMp2R-Bkq!ami6V`SXFXj,6Vq[ZSRL%XalVdI%klp2[q)eC$jA['(2mA" +-$1"[Z0RKFSaSEkD+0j(LE2'@2F[$-6(p[q@kl!TaP6f6l*R-AfIai4bH@Pl'rff +a@Z-3JVP3,,&cl'caMQIRr!8Q0$5TiKh81dZm+plcF%b!qDi$mcV-5@XLBXhrJ2N +H-TmKePVX(Xj)JVR@JAN64UIh8mAl5('D@'GRHMJQ`&cRQKA$R6h9YkIZE2K81j- +42Td4AShrHSYYbAU(m&*aVchCcV$62AYb(1&"qEebrS%`0MPNTr2K&0k,hIml$fP +aX81NU@KQ6r,Y5AqX)Z`$c&I6bR8m'c,%"UUEPUKEb2GiGI5r!RcGKrA"IU"5Z)- ++#m18K(emA(D&aH#['@rVkrIUeS2Ap+Qh9qeHR*Fr,&C58J$T9TmH25%kD0I5VH$ +rRbreLeXhqDNX+@8NiYVT2Ap18UerIPP*mG6mNJQPdDd9SbqJf%h&Q*dk"kKe+`H +YBhR#VD&YZ6[q$h)9X55i)6CQNZXlKCej"bb#*k0[VilNM)Y"CG`QHN4a8Ij[8+C +$'VF2H)Z,l4rMKG'1lmqQmXTPGIp[k`L[jH$ZVITNpqpj5&IZ'a,-ParB%lAiS(b +$HS)pAYS6H#BlRZRT4"Eji'B'P,4B64iR2K3Ea8ILBl%*FZG2a@GfQMh@6T@iL-h +NKm+c8qaN@fC,EBNpaNkbaEE)6[6YP2mXd(dlqHqYVfr,rKkI2&Zk@f$Lfj,G+el +I(V00$Z(E5IrFPNX,0TB@BlqdD,adY!+Pf%,aZF)K,9emcJ6Mial,h+TM)eT(e%Y +(Yb$*"!'Pm!44)4hLb3UmVfX+139,CGU+GZPS*k3B,lj31-Kdm38,PEp-&9pL5LQ +!m1FV(mIIpPkTiLZN'#HkfE%qMVpYCP,Y@%CUNkL3!'R(L0ifhqD*Vm8h0YG$q%m +j$NL,N!!,EIdei"lYfp'r,qmca6IJKCJBD)qfSqa4pNJldSk`4pMKpR"4l1'Ldp4 +ipZMG@KcIMYV4I8IpVAcalC(rl%r2MN5j%iTc#cdl!T(FiY(M2AX%#SI--cqhU#b +r[2$-%'E2$SmV(RcJp5pkU'4caHKQDpDf'Kr"K[6Pl%FVLiV0aZmV8RUq1M5lZQJ +!8BS3)cliX$!LdLD-Ub9%XmFrDMb[dB4pK6FLHlMGM"alF`lj5ij0MFUcI*D4$P* +&R`i6NqeKGUMi9RaRKrJ)r`ZBG(XBFmGJmEh#!Gca2DUdJh`lD0IHIbL$rbe'f)% +fafEE!EDrlHIEJ6[cq6CRjpMTfqaG1j%"[`qd[ZfrDeILfhkrVj3baAFBUcEB3fe +IfmIfYSH)Xf`[fp2fX0eY0p[9GS%Lrf#af"jN1pN$aHZfSeKM1hMfd,JF`V0pGmX +S20XR[L$`E1ri(X"$L@lGS'`[4'c2hIS&crD)lqipfch*2Gf5h02e,qlT%Yr"qVE +clr6Z!pRI10fc"m9hRClY&&md+U#IkJ(qIbe@20YKpe+LNKdL(Pj6@GcIE1fmN!$ +QXTZYDlDqqB3T'cli%)be[RR21l,(6rQSFFmP),0,jSriH"1),-*%TUU,N8aZY29 +1bV@I0Pri@I1d1U,Y`XqEaf$F8%IXmfNBUR3YrMKAaVNbcRZQpl`VHm'L#9-Dp9` +bA+Ka`dA&"GH'85E&Zl1EEBL*+Z[@Mjp5Q!iJUSEaDZ[@,dL%J99ecR6X82!R0(` +CYVhi!62VMa)"l%9q+&G3rd6L*pcF)L$9hH)N36bf'f%e$NP*8$Y3k!'qE4G1VC- +RjFA+mZ1+FU'(KZ%F&bD`2NPTffS*ENpX#rIhlIiK-iiVRTM2M1dLc0GNfr*BG3$ +M!Tfh`f@F**+%3T4-d9)P9)j+lL&6YG`MZGDaqbN,TDZ@UH@eY[&YQj!!8d[bbbD +A&%f*BI[[3SI&#NP#,biVb$3Y+cJ98SV)Pa@PTf9&S3D'bXNmkFZ)e&VkJYM-`aG +lbh5CS6&+K2"Sd9"QNS3q@eD5PE@XP0$h9,HYjCifbl1Yrp3T5HLdHF1MaCkfPDc +Lf9B*RC+XiKEX%2,)UVDPPP@GE)EMe@4e,@(&`LK$0'aEq,E&cSl5XRT5,'bEHlC +j8X%'1aHbf*f3!)4+ApD3!(YT@F1"9d28XI[DCVDTCrH0-f,rf)6mRL8PrH(1X(9 +0"liQfj4,J"TFeV40Y+cT5UJV'YTpE'2Eb,2la%[SAPMkcdeIBbkK%4pJ'k0PA!l +2%Xbp+pLpdC!!FY4j@ND%'K6[k)DbPQhJfBEF9CKqmdY+SZ@6Ja[1YB3Q2F%!pAe +E2jb&N@PLkGM`h,pd,0N'A#m-",L,Qk22Dj1%6Ph@NA8eb$K%XERSCk1bRSak0KS +Icr-+LmI'5LFi#X,RkUhRY[$0aB'b[QbJ*G64,Yj"0T4lD`RTZBZhNieNBbfK,(I +aYR)ILDlC*a(I6cD9U+GT)YjDlLZED`QPZ)ZhNLdNZ#!Z''mZ@XK@%GQ+"HP$i-- +`1Y6J`VJ"$M'XLT%Yb`@MpAaEEaG(0(IeY"*C-LXLXeMehb"D%S-2N!!cH%$2B-N +(!6iA"A%l&p9DY,&e29XAK!*'M@VCa0f$iYc@m5f-!(jY1LdEZaaY)C@YlGRDbGb +3!16c[ADL[DhP@Fa(mA[!NI8k(84(fGV@e*!!N!#%3('m685fJ9bllZ"aaC1"HKr +i@16R6TK8A&"8&Q)@0N0RKNcDQYM[`4T(fEhNIR*rcqk&!NHA&B&P-6$`KVi6A%I +DNQ`,!MK!YY-b,S([*!k5l51b25"3`r1GX&Qf`lq$VD&P"jIN)('`l"L4(GNbBN" +aQ-2#"bP9lSq#UmX$E6A29NI"H@-+bl3md#AY,RV*6K(CD@YPUEBDPeFAri0X95d +aa,))Ucqik@$Cf9D4AE3m1(6A`Th$C9IC6FZZ6RA6A`b4hHfH@N+(lY4K3f32f92 +,(NjeNb1'f-UHVFb`MbX!#K#MZl`jXTHYT'8[P`GaQqRC61krdN)-#C@iHl"!eV* +E3ZXk6"i5NBGXa8e,G"N6iJKaP1`YqfJ*J6VR'J&&EiDbF&[4XNmj)kIl0[eI-jQ +Y`ZQkZ)UJ%CCpjD%D24T@""@deFTUDcAN#XQ+)Vk0l+kS+PINPIYJE##*9TApV+p +P2i%IDk6'bIjDaTAUBk![pRcVlDl*j`9aJ#S'b'`Y"j3[L#[D0'NVFYYJf8N5)RU +C)`GUQH2Qk44433k5Jl8F9$j29l#TGJrI9[L2R"eU0l+3!!TS#6@k8mfQbb&bU*C +aXA`+VKiQKq(JKXF8iF[$,DEQ`efrFhbi99T#MXdm`I%Mj!JYMh$G84'l4I)XG,B +B%SY+Y"b4b&Y4MV3BV5&GGh99P%GD&(YN3TCEd9KjP$'HX4L8mQ-PT@2'j*@8$fC +4%80Fbk1iZF)Z*!-$2-Rk"'P4SE5+$bN-EVP@h`5q#AC0,G$VadGr15T$MT,[rUm +%LSlqAEX2hUlSB&QVU0#RG0c%@,kVpHI[YY)9cd(4`EQ161e98*5ARa`@rmp(9bf +Zpf@AAJ0k$')NiSS1X2,f6`pfaMqpX"MMDL8D$08$UGTaS$TZcp!a$X[rEU$2q,0 +&rSmq)e4Nb)(i(beM@X)C)8kiCS[j54UiA8R$aL3bK[pSQD[PD%HiDD#i2*Q[CGa +QJ&2q+-HB(hcMV%V#16&K`HLLF40'NV!DN!"McIGDMLfRT1pmJj8V0XjEP4Ma@+L +q0pm$"J0l%#fKfdqXX-D4K$f",*!!il@%,F!N9ibF3-K!jP[cM@qq$H'!2@&*@@+ +GjU+*ECU"U`K*Z"V)3MP45c5!BjD+XNJ@DeQ8m2ZS)#HCVc9U$"G'($p'JRZ2566 +!(ZBVmkAj`MGIrA[L-'`kBYKd4%)I&,F&-TXpXcQa4M)`1G(SM@4lI1kEcrrBlU% +#NU85mbF'4mE13ql*FJS1VJ"2q1Bcr2lZ"#fRP"ImU@mq$3[QYARaj++b-0+G)b6 +,82"8HDb@8eh"kD+1R#D2dh+D'i$545ej[$a"5rJ,Y-15-N28-Tr)%q9*R[NN[UA +@mN4Al*kLUYRN'qFkmLFi*lP'V#CUbZRQBbhK(F"$&-GRQ)mdMa"F1mG2PUGSHE) +E[QZ)QQDMCcEbaMbr#-Z5Z0+qQUKK2[60KlY*4aUBR%J$Za-Y6h"Ci6eN2[$0"lZ +cDRPFH4YYm-d'860,q2%G5TJ-`4`1KX3QMd8cR5TRDRQU%m[A&6&jQPN[6pIb0-G +d,F9)H3E*-j!!D*DFVH8Xeh8Y4AXj4jkTj4a(35h&!@DG2-ZmljYeredLRi9bcMC +VYB56!+r"S!Z5jmKcY6c(`BHeP9RMQc@l"X9cNlBRjMhI[*F3@EK1#%11mFQXCEj +MUa3*9i4%DlcVQhF61Gd+)!`0#N-NBEdJcc2[D!P4+5pX1U!*cTFAb![P49UHlaB +f(83RHE'm4-Z,(4I`!ZY5XeV,5a-+f)lQEGqm[DY-D9Bc+TFi&ZiS$M4[+326%#h +KPF#YeeRdP(0*3L%V,j1ADhQCDlh1STZm3PkTj48*$j%ZjNhI[,Q6-E@mXKch0hc +caVpR%RNj#Vl+[+lP9@k6dKdiA+hPeBNe9`rcQM+[F9ZpcY2,KIKI)qGT#9m$ESc +HiP!jAbl3%PB*A(G[dFHmkTYAGc@#PM"2i0,kL,lQ&@9HXCBNI"INYHCP,Dpec6! +!qkq&*"ILfL*jRCD,A$--%!2PpI)',H%@`#8-%0RQ*@9HBY(!$H@i[qLE&rq$1q` +3j)hQ"5eK5F#i$d,*0fPj8p)[aUa5CKA!-E!j)326&5d[FM!-!d(F,'r4mQCS3X, +i%A+a[&A,a3k$BH*`mla[R([+EcekUbX5GN6Q1@8Ja0BblU2!m+hdcFSr"L)$DaM +IV0r&J&Sk'f#fhM$2qZEC[dF#f*[)fq6Y@XD0&NC$3('(A+,P(3kLd5,A2+2--`a +4Z9lI21fETrr6B9$ZbkAQ+5eKdT!!f0(GUH@GVM,Xk-b6bN",6`Dq*#6K2bc[NRG +V'4SfZ0h!%qCaD@#K)XhMR1*Zr1q4pfS*$`IH$84%KVa2,Y2b[[,G`'2bI[1SEai +6$E*%aM'6mdZQ`8LaH"+l0jE',r3U,XPf&dMHMj)H-#ZdI+!Fh8GmmmKZEfFb+jJ +4(Q9'@"EI%0"$mN'5-(#3!!r*Kl9mU"c#jHCKDH#2+JfF88JqM2pbqBKQCA'KXj! +!03r*&G)m"$MP#VLSl"AY@MSK'KYG2"N1[Z0L@)BAZeeiD%T%%NB0mP(jQ*D2*[c +!Y(P32LkIm-b$IiNFB+$!ZhKHYceT(Y!5VCGB[cd9N8p"XYP`'0FEEq$5D&jak,i +m0FBEq6J)EL&[S-K1P8m)CHkA6mYR2(0rFL(r0&I*ESS9j,-'V3PI"9l3Fh`P5GJ +Gb1INmeU#p1)@C(*94+i+ADMCFM#qShmHraINLeUqN!$d5j3[4H4,'2!V1Y1qJX3 +krdAmAcEhDIPbFN-XAiR)9`"#5VGL$*+FaF"m3CTPM0Sc31eHqDUjac2h*VIkF4H +"01aLAS[)elEP6MAhF*Q2iIqkI%2,ea1EL$hPQr)Y,Gpd%b&8XI*Y!rTk1l'CU#4 +A5db9Ua1'2TA-ACkj+lR#ImIGicRr6XrFQ9MCQlZC9f!mN!"JSk@q@4Ub1*0"@A( +BK5iiT$JdE52j"V"j9lkRjEZ1GkU)+QD*Z8-D0Tm`E$iKhm0rMF3-[XEa$UHiAEi +[$I6L%INqjS9D@hQ(p4HaNPcJ"#+BA*,[1QdYrZ[NHLhA1HDT)ZUEfq3'qB&RE[Z +,H6BijS(ETIc3B%lkd(8EacG'j%EX%jU%c)0"JXd1S%(rMAVB%6912lGb)hq!4Pi +X2j)IHfCaNRkJ4QEkUBDpd#D$%@f6Saq1Id,b%b$iUIa-bdmG!Y9%GIPj4(l1cPr +K*M3(!Jb'qM2m0mX[Y)5$-LHU,QV),b2b5jC0(0!H+cp1mJAqAjQEYIc+*B(pT[` +k)VrH`6c`Jj(Q&JEXB`"fNrc'h1LCQj,-mih,A4Z,V@mMmYXGc(-MPlNHrqmNPTM +I1HCT)2D42mJIYB3+N!#CTi&S*(mbQ)UKr@2QiIJ@'@Lja6&-3p()A1qCkj2-%lK +l$84$FjeRVNXbc`h-2$q@-mmLhb`5D9NLKDQ!NIJHIb1Kh6#1CjU)*QDKZ9DDKBc +aYCc#`ZdD4mh3-mp`LJA3raRi[N4`G3I2B'#%mA`8$4rhUHHd8"d5+8V4"0ERK8` +6''6Y3DPQ2PA3"'GBf1$M6JG+SiUDlE9j$p&%Y##2I%h3L6"q6846-mmhmlELTmP +h[3CY1N8La2kKflTH8d9A5jCS5jV508&GPYJlAH1EDlEA![dJep*Dl%mC%FVBeVH +D8Ke`RF6*P%Q9c098@F291bbZNqK+He)96AZkiL"4-PIjjUUr+&j6PB4@U3Y9M4$ +dBMr23CSUZ9'dKiK405,B`9*eUU'TZZ2U(Q+%ZC,fSTUHZI*2VLBS!CQVHiK$U*D +j3P1YmRKYFlQQfJi)MYH*8"diEQAeq)fXhCX#Z,AG``1L5EML(!iV(@QZ!-X40)I +Q-UT,p6`$dbj(j!3C&40jEc'BSQDZTUJMFSlA*i*!MaT33df`L'8iX)DL[51dpil +A$"""Z%H0U,'Q4Ll2S@)!l41KIGbm@8ijMI&[BLl9""'Ffb`0T+B4DVU$bLpPb1B +bC28!f5A8c&cXQ8X59%jaeeK)V'MI#%(e[Sh+,qBbDq$Ih&bNUEQMmT&SeKE88P- +,4q8Ma5KUC5l8e-T41FHcU,8Q1-BbC4mT4TN,2(0"JXS*HMUq0e)FDFlhc2P*+Vq +3!"NKVKjN+Mr20qIYI"%#QBXBfkXj)pb*Q82b`&YYL0S!VIeSIdhl13l*%mHEFkN +Y(H#CFrrL%-M`Q$2ba&KUCml4"-9G)YlHR+fT[@X8MRH)8!FX06[mIa`5EB`V[#k +)NmRCI)!V6bSGJ-imLcV5JCij+dNQ@*d`QB`6TG6*R+N*AV9-*K`rL%$,4!G6Cde +a4pTaBJ*eLF#1rfFb3Dh8PETTLR[06K"&e$e#hAmKNflipc"c0%'pahQ+a$(8-`, +RZHeN-SFKBdXK1K#3!-fQAQD@CfBRb35+2XjG*UE5)4'-20[*C"DA#EpIkQh1d06 +ENFN*k0Sqe&F6HTA*j!3aR3ieTfZ#45Z6#FIl8Ap0d-`aDC`STT[62--'3Bj-S*l +MHbH)%me-cma-NJQmJ$4"`CFJNe0pipb$IL16-aKE1(eTJLDEBCQ*k@d!C@XDi"# +G+FkJR!MPl1JZcHDaL3*2mFdTIa5)Ya33$D4"QL!QiL&dMTKM6MBcT$QC'iUGK`K +@Y$5BKQL#c)k(8%iaRBC+!dZL#!hpHFR@#jX(8-b!iU)@#,N@KKk2$U0KQJjcNp` +Fm5%G6X205A5%*SMaq$d)Fm6$0)*'DZb!ir(Vk8Jk5K1%@m`fFm5j0-UFU'Q8dj0 +`r'KcJLD)RhJ5j(L-4QZ+fm,1%@HDihecr2Ba-fi(HkBiKh)Md!0YT3&Tf-I'`-* +'8qJmkm&hm9V+-mGT!K0ab4c20p-dBB2$8c2(ap"B64"'-33AL%YT("9S'ZFJZ%" +FD)lecE&r$k-33$%S&m+"FA`%pY(EPhHDi1[+aF&BLbC3S5EBV(*aPiXVc&6I62f +l1$3X&hH&Z*SQ4QML$X5Rm3&Q2*VL5XDEa2e8C+CS-&b)--H,DC)Q#(NCK*[%EA3 +-P@JkaS&`NlMC62E0j$qHa`(+#XZr'6D%T4'#SQ`EYfZ#+)[,@L,ZS6+DV1%T%*B +&#ha6jTZbA@9")-9P,49hdj3)6GQ"kK5ZE,LE[9D)pfJU%I41G#bKFipeXpF+mDB +TTH2SH-q`#p#rCLmiV[+XY8)m6LFBi!ij%4I1m4-MG1+ZHCeCF!TH+X"VFCDJm`T +`qiL&1P-*-LYc$*e%dcec6(+k#[95(JcjAU3C"Udh`deA($qCm(!*SP2S9%f3!%- +a%Nq)TfPQK'Eb+Lh*YkILIaTK9S%3LC-m,9E5'4%kJeFdfiRjG2aRQ@*0XebLP@) +9cBl3l"fc&IXB'@Md8QNk!#ZL1@DLCiU5Xa8m9MRh5q*91M0#CqkBVA#6#2)b1XZ +!Zk"GiJCl#eej0Q'8KB5*CkZha$YdVTQJ#@DS2&Yar$`kAa-F6RQ'HPZmBmClCRa +bYS**+Ypl5laY#Ma6N!#FVG$bQZ)k+"jpa[PQR%L(9e"Hm4KJML1J*`-C*TQ610F +4$T!!pc'3!&a!f"6%V9EIKbVkSJKGp(2$D,U`[,Da[KRlMpTS',#qQ#l4a0SQ*0d +S0TSa*PmD+(fPBCmNJXdUA8VB`PaDRL,2j%S$!b9TS&)NJR#*,L-XL#jcK,94&TV +4G!9GkCR4Ia%@2&PC&,C4MU#Vk'T0F%KdmH&d$Fh6"+'4L`qPqE4!dra%I#"G5`X +e`9$9aI[6)VT1%l4#,Yk(VUFE0&fIL2HN'qNQ66FQiPhTCVT&%d3m,Ri3,DCE05e +1a$[3EA5l*LKMA,`Yh8&,0-'#e-9Ede+k8p254,`&h89hDi*maF@EdMedVbCS99a +m(lU2PQQ##lq,0k6lk3&08+#iH$ekN!!HdJ34LB[ASSGTZ5BS4Pam,hU%9QL#rX2 +&Up+Mp*LQ4a2a5[3i2D%*-JmAer3N2B9$)Zl6dr5-*JJdA,`#28XV06fEL#YkMTl +Ap&`m,LbYSKFdV8V%Ik)Ak590d&Ziq(Id-VfL#6D),[i9[8U[DASe%IqFAUFh0,h +Z#'UMf%4[4ZK0Q)jA(C3IbjY@2RT-,Z@A56LkJLNMdp8QmCQ*HBEYY8)M#AV0c4L +ILbrT,A1dTVK!!R%cbM1MiRQN1CU,H-@9r*AiPYk1d0[mAS)S[S5`aEPl%f4M6-R +IL4r08Cij#L@8a8DM9%K1q0j2`TJM2A0NmKkX1IQHK@"TT'IBLX[G@aRHJlY0UKR +K'6EQF[FJQZ*l&D4RMX![HFmCPE)[M4RZQq&rMm1`13A`N!"ICG*UFlJQb%Ni1q* +QQ'H'*B'(S&-6c&J"2*aXUY!l%AU((d(!`%2`9+kYL+1r`U&69GB`KhN'Diii1XY +GIA[*QQDSEiCZAdD%XLC2eT*ek9f$p3VX+MNAiQD`C`BRX4M#b5#SBLcUb3EdAS6 +H5f$4Vk"S!VIl4$J1&XDaJ)U+X@JS'jY"RKQ8a-*Cml,&LKRS'fIKpGY`$9dBip* +80UFe*NI6'TFAFC2YQH`N,MPFV01I`BFPLpC'D#d%1'PKMm6+iQK!jmCSY*ElQ`' +H'C!!410fGkqYE'rkHkCrmKl8I(b[JqaNqRQQAr)H*(emlb$Ca4cU'AEXF[FJ-H4 +lA@82dpFcIC2hS'$NHcePEp2(-qcFjHjGjqleNIe-Emq`MjHl"qGS[YGIjTK$2-1 +ZAZiHp*9mEk!FBRTjKMfqh,ejlYj3HEMTkCQHbAYXJShZ'5k2S2G0$dh[Zqj"h(6 +h6IGI*dCTi)k3!%TABV,Z4ZYS[@IB%-bY!YDj@NE+20TJZQVD%+i#`[J(42"KT!p +TSbBR!-+e8I44K$lL*8[h@&%Zh!E#)4qqJr5akD,TBjGRP"a0Qb+dL4eDX[R"#4L +'mH3CDESb%$!R0ChT%h1`CcSR4ra2A1Cm#2FrMG#RjCP6cF&F)Q5[p"PpVZQcF-% +R*mU6D$0pB3ibR64Y$TFmZ&0'Ap*AQVi-Khl%*p(AjN"0AiG$IaMrKVl9"!d2GeB +4G2XG2G-a1I4rkqj0P%@QJfFk*)Iq!jPQR'K)6T+Pp&f%[Y[4!jTJNJM4*-6[ap( +hQQ"lahdd98iclFR!pi[-3G`!H,m1dHIird!rDQ*"%-3hdb&EEfF1N!#Q(DH!'4I +4MrMr4&Xd352%"K26jDN8N!$4"#d3&cCGRQcDNS&lPbC6[M(BhcIlKeV6LG-53NL +%%MT)JXFB`IG$%e3r@0[*QA+Qf8p*DIE$RP9*'-EALr),95C2#YG*mGGf410Uc*B +Y@iC$(f3qq*05@X'*K*Ya*Vc#8Y3HTSeTV4Af[9MXSED,9DUUS"AH%m,019219QQ +USPCTMUjQ3XUHj4Yi(rdj6LRh'K4jQTbP[)MbrU'M89KEFheRbI19Vb*D366%pCd +&#AXVhl6kZclhI"4jYMa2kBM5ff9"#PjRf2I)Z6*3kD4J2+Sb9+C@'3leZI)q98P +90Le0#kdU1G6RbK[9RUU+9[!CBe6QbUY89990+c`,K9'C+bmccAh6I0ZBTZ!XaKK +F*Up8e50`VrYCjU6J6-Dec*1,9!fePeCi-3VA-Nr10r[kCYrYYHcPDTN[&kUD%99 +c"ji`(Q-bZ8AHT@UTfPT"+i64(r'PUSkUUe8G9pJYFV&TjTYQZjK%+kKpQ&%Abb@ +URSTU9FpP[P8Z-8epdc6dSLSX')eANS6RR*cZ'Uq2#A0!H@fDq+E*AkqaJQST`@R +lq'DIIh'DiGG%Q4E-5""XUIUUJ9EeAAmZNfqVKQT[dpJddJV#)Zl2CI*Ce8JeeJU +1B0a6bq6$DKr94#Xm1BA"@3BemYkqfAYl6cQ0NEaI2U5D4P66VCT#V5"%iMT@b#G +9-i9AAcQc,X3I03epdh"l(IZk1Kk96kMQ%G9m4frZlAVcHIQkDU&DDJ@&%IIQmr* +9KGFkDGA+&IDmA'8Dq+E"lYl-FVfj5VkL@UXf@N&ca*PIN!#[Q2UqU5mmejX-)HT +Zifk[NLqBU'pJZlDl+lF+P1VjTYlZVS60R$4X-kIJ++Ef-b#Ar9aAVJDClUrDQMS +'"!4*$(IPD[QT1N#edqS!edfVj9V9ARA3#X)M"JHkA&2,0l9f[C(,1B2*Gq3De6' +L1[iX!P$3-h&Ckq9(kN$955YiJR&Ckq8'8p-h-&rE@4CN4ec@"VP4(444"qeS@)L +QZ'%hbar8`DUc9V!+iiEG,,p6A94AVEUi)U&+0A[jCUrG$3Y6-@lB,q5hUTX#8F, +kLc0r+Eme0Aa6)i5RG&bF6K("!"35DRHAj`[jTDRZ'dKHGlGZjr,@VHDEDVYEY`k +hEQfH@1[b!5mDN`C6-HBpr(ZSRPVKL5fB(JQIUDTk531rViMU&DTdYNk2S4JK'KI +ZZlN4,QMU%09E+b`r`!8Si$l94r8e9FbHQTI3i!,Ff9-GU[TTKHNI6BYi"G9I$G$ ++HB8KVNaPhe6qHkjciL@-UDNU1k+bIjDm+,c9KB[c+%2PU)&DiB8YA"`8UDB5IRm +A0p!9je1k'K5"8q*fAS$`#X3+6HGGDM!T+*V8%$98Ub%1mQV86KfQKTP-Nk%9A-- +BmQUdMcTF$GF`H3P4U8Dee"&UK&C`#@08UP&eNqiE,,kf$K0i%JTM8*eUUT%40A, +l8!+h-DkM,M984kUMY$V5e3(&TY'qdG[V1-V98BmDU&%40@S(PT!!'S$MU"RYTij +@-DfJK!,()GjDM9Dj@N(Ya)8eShe0a$I1%qjr1!kL-A!Fl8YC+NrPDjAR-MHR,12 +laKI4,$al+&5APA+E-T6aH0LfL'[PRQ@#'TSEccIHAh`AefJaheAd6FAGI)GRP8N +$cca53r%ISmCU"E89pM,8(ZV(0&0"'MM,531EE&+3!%UTF5C9Uh'ZRGZMb`[8H,1 +(5G'U`,9cHqUM*UK#VG#0h)EYk5!e849TjGkrJRJ(ShbMYVHKHrF+GD"1UMLL`UH +YE0m"!!qZT3[e9*28-9T0FV9dSDi'R)AR-ffY"G)TVU8Vp9!P%9@bSkAKk-hde)r +Z8+@N3)HU6%h@UXcKfBmQULPUUS&LA129D#'HrHK)GDbDTKAmL"L$IM43(DH1emU +j'5(H2l"qB(H08FFl22T6MMSKSNliC6U$GSI,'N,$eBRU*+fFr42L3`2M"fCA@AK +B#TFeP!jAdb0UqJkBRIm8(8d&DSBk@H19C5&c(8eMe5RU9(kGA9MNd43,!Mm)GM- +AT%l-A$%DNhM(ACKj0)eallB,Q5Z%*'3ZRY(LmC!!ZF**$5STcJReB2#6(rcd&h2 +"R5V1A-'2I[$MEZCL'cqBqU'&mDLAa+2b`MBZSR29,$8lq#(iAZ2*H-Q(k-e4CfU +&e`BN(U*hPMTEUl2+(k,hR4pmYk[IcLjrL0ij%AA1,`r41l2m)AVRU[1dFLC3L%m +0[[@$EhH9"38@Pc@9MP2R4p6j1pSBANrFaUI3QHS#GD&@&lJf2S9QUi[8a9VKG5P +F*24U`6Gqm-hZ0SCBLY[i9*UP,P'ADJ9R*mimNfB&Ar["elm-)+k0b`H3!,#0RAN +8DTJCI18(H$l5lMBZeh%&ArV"PrpUi`$@G6*J%cXmpBr8h1!,VHDk0Mi2*(fCZMc +B((bZ&3bQZ)h2SrRU#R@P9Y"KFEqG4aHVUp69@X%2LX'"ULhic!mqqe8'S*AcL+, +ck5*e684GXeA3Tp@9VTUjG*@DTqCV"I%"9c1A,JXqpB02reN0$+5iQX[S5V8JSKE +Xk&ZXklP[&p*LGDeDU*86I5&qXeUNVY2+[Bm&m8A"*hl`bHkqKHm6pqdLZNPGVfl +3#PC5R2NkZLRBj!HER%emV#aXfM!5YUY@cPm+HDm,2[D$MrrUeILEAVKA2r+$MhE +h+YlA*)2289F!NcibF2q6"Yk2C0JUe-"+94UiYj+#a&[GU'l5#ZSe(L@AdE*JBr# +K$$Cb!4pb#VcI4GfXEY%+$hH"FJ!T(P#,YA+H9)MH(hcJ"arm#`Ye#`Ui0GLJ&9l +I`M8q5!m'ki0e-X"6NQ6!MdS+0Z!Jld@Hfi,hYB)b,Zjb&+`0eXJ!jQXb`(2`+)! +$@kEi8D3'lkREe4eUL9UUlT3"(ZU@U@iA+F'l`6["kZ"Y2h![6rV62Fi2h[Rh@`Q +#eArl32V"frqA'e5kZJ22%`MH%MCi-hK$hDAZ$Plh%Iih4ZN"2)"5JYI825Ti$Bm +882G!FakmkJH[rQ'N'1$j5CRU,U'#9i+AJjImi*@GQIcJj9dQYRl`dMmpQM29hA# +Vf4#m',`3V!UH$ji,9JE2"Xr)9M)VH&Uf#Ck5l@A(i%RC+AJLH$ai,(K8(P)KH0% +CGA["#iPh"35ViJk4A[$mRbm-#2JC4hmiM(["bYhQiAl`l$raSq!Cq%J'r!)M&Qe +j`90aFC%A2"PApkMJ#85#ahI,5VhJXEMDaJ[i$8@XakN8[#i+*X$Q(km!1+U+U,Z +fU0RlmpBejXF!T)3fqhZ%Tp6LBD,ciIc'YBrQ0IUidE10&Z$rE+02'RhkfHH&8f$ +e$hYr12Y(a*lCSd59Q+J+MrpUH!T&f[LHG``AHa6d['0Sp[b-M(5eK-PNK4`F2") +X9rHUqi+(r@$&[adl!cc8L!,iK@8UU0q#KrcJSCh8N!#Ti(SQ4`82"Jpi!6YrrH( +Dk`82a2f!+`82L`bJlcF&c[,pjMe[ajX#e&,'kAkC(b`,lP2,e2h"[Aj`rpq8QKl +J-6XT`6hU!4@!lG)9e(iLZ0X2l[l6@M@!#eHQ@SDji+lJcQ"TX#5i)lMG$qlDRG- +2lYa&M8YhZZ6j`C+rE1rmi)jG"RTqF2XZkm*-GEp)#fi,EJd@"lF%0`Fh"6F'0rM +"EArhY"IFQRL%4E$iEpFm,q#(#i@2D`KZMKZ*HJ'l4B92E`KZh2hJ"Mqii6qM5UA +JAP(!,lX3DaZ$#9IJY3[c4U`E1H+)&512Q,HqHCZH5r(kK@lAMYL`$LpG5!eIZY" +XrN*q`N8&['*KdAM`R,qJjd[Cic1'LfU,#MJm!H%#K#XXQ&$A[@PKC6EIQF!FH#G +cbr9b@A"GX%JpU"i+&[V"pIrh-"CJ8Nm*VP82U`#ZN!$T#UmH%X%#2eM`Tc9IX)J +CjN%`c2aJAR"0F(9`9A#P(mcIRG-2j[feR[H$EFC2IR$ecXf6(ecekeE$$klF0A" +RUSIJh[j8F!@d-Lr*9i,,j@["CF(Fi0,J%VP4IJkjaYIb@i+e9(!a9D8kY$IY3df +aV1k!*@ehkNQ(8!lPdRLD5+9BLTf)cG-C0*[1T)[ScH!LHMZiN!$HSIGS,Ae%QqK +6qNjjd0G99c@$#e46e6`iAh98"`ARU@`e5)e8Si*c9E%U85HSkF%jkKaeIR#fZNB +Y#-lbJLZ5ipIPbI',ACU#ZAq0AjFQabrf4q,ab`XZjNKa-@MXSZ4dGf&FKZS(&ra +6j18(f0pYP4IjJA-aqSGka3r1rHr1h`r1qHpqe3r1rZrkcJr1fVNXUK3XT)IAV(e +rhIU*mGGPC$rp-Em[Bp2D6clp$(Fq6pjCNVd!%bAl*Z--Ph*4ZA%GdAFqASd"DQf +!Kf'XaB#*q6)QUL0[604)CPikA05,LEe`[fC-e)U*fZ(p1Y[Z9iL*ZL"d9&B2ZlR +UU#V+PH*F2kb8rlL(GfSdi(Xi0dcH#k[I'p8eLJPFf#HXVNQbZM[$kTVf[$-l*TU +0%[XfiRp-0-HjaDrrqI(E,F2SJRLd94Lp0Kl0qMhcErq&MB"+kjKS8mL3!1bAJ#5 +'p`rb%$i&SiJDKkGmB&i"dQhML"m3lkhak)CfmAjV(lq6[QKH6(4ShUMRA@&Uh1` +Bch$JYJbGNKR3+5cHiSijH&Z'cSN-B4C8h59HIGGiPS@SU9ZmYZlE-[IB9MdE4A' +'AJR-8#FE3('p[EGPlE-0NEla$)GZbp![Q@&K$+p$3!DF"fc,N!#p$E-`dk+-l"` +m0H3c)DZSjHPUZB"+p*(J6,8LA6h#X8H$1HUaG!Ald4VUm@#fHL*G`A1L+PB4Xlb +!hl*8QPFk`3[BbLTh6(&K4$d"C[0lj%r*,bbH0$%INcUrU5LBNlcr''[`%frDG*I +26&jH`ES![+@(h`N8[J@#,B!XhTSVl93m+!%c0qE#b)r[[SXhCHeiS#qH-Y'q'Xa +M4@h%-NYc5q%E*1E`)f0a[S3I!i[cJL%p$aq#a,H@PNhMlG36TC0+-HD*PlTRpqL +*Hqpf'p#$c@ZrGBq"PH3H!b[6h90JCFf#h%)m!%3faU$!m6BiFrcJFGLmi0ahF*m +Mm+3NHGL`R')m3!#ZpU%9I[bEBI(m'2K`af-`)'dKahHTR6)Zc!'(2,L@cqCB+Pj +M#'[6m"jFBT1@qR!AMFHJKXA@&4mHeJQc3#2((cM%e35*Hc)I4-I*@U%TjZq5-1B +`#'-TH&P()TED'(KdLXI')lEr4mXj9J(VkX6MIY1JC%VL"qfH%2mr$3d@F("MT8& +`F'aP8f0bDA"d8fK-D@)ZZ3#3#4DG!*!3XKN!!2[B!!![#rq3"%e08&*$9dP&!3# +Y,$I#X!1PI3!!-Tm!!!I`!!!+Y3!!![&bjHld!*!'*a%0`09PJeCQCLEhj1EKYlU +P$G`VNe0ZENC@`XKQALXl1Ee1Q@jkPXI2+hCiDRK16hEV`)fr+Rr6IFb18,+2pB6 +YSi6(kYLPC'[NpKZjcFKa5TjYFPZ6E%)j*C[`Fa2Z&8[##0X5IK&11$QfcpKqYQQ +hIA)mYXNqXJ*IA9Hff@D%RlIerGEfXD)4&VQYK'ebQffb*Fr#N`e-m!,KJHfm(S! +"`,3P!!lm@Pc4+&LAlm9I!(j(f)T8JU+c1IC*J'a&mAfR@&%#FT92p5Z4UXCbIjh +MU8B"XhJN0D+rUXUmRq!FhSQ1QdS,GD6`-E$L%c1YLq*lY2jARTfi"T!!#@,$1ED +4X"E'X8l#MRXiGSJRG")@[)9MAq6BE*ilK@2e(%[Pf'UH)m5alEbZVq"$!"ZqdX" ++R@"%,Qq,[iF0"%aN3D30Arr-f4F#XNJc!@""3E@B6kmNb4&ZiYhdYbLL"c8L2Ba +h0@YMUF)A6ZCa3-S*ETI#ILT6!GJBB2FC%Bdb#S,'kdq+,9X!CU2Jc)lbAkL'U6C +h1#6j`ReF8f!5T,%ZS5AqIUFKX,m)L8!5liXeIX8NS0G-61+H-Smi[hK4-Ae(Z1@ +)'VjI9PGSiRaTT54@h9ST6QB&(GpM"IF999H)PD@0N!!V9Y&V6Yk%@F!@2SAL[LG +M`RaS%+XVN!"1&MEK-qa0BBc&#@[!F[0Qr'FXIR0)3DR[!GCbGHKp*HHL(Uh#$&D +i&aKc6XpP"3r[BZVAMd(1kcrRHSQYcG4(-Q$dSjd`lZPGMG'hLjLk#ANGfJ#MYlG +M[Xf)&d"fd`kQE[Nfe`$eNHbi-ULEpY$rP!HbINe958@mL2K%$hbY(GjJ5hNeK#G +I-[+MIU"qlIjY!2-d'[RE5N'`3YB[IiJXL&-EVdI@VhD`4NLVhJ2V#Ip*#e0MFK[ +GqreAfC6RZDTL2R)fGPD`YjN9l-0D[,#-6FXX!*M`$D0H)U6Q&l&ZB+R1"MNNUhk +IFcqj'iZNV4$a25jHcfj[0ZZZYZ)hrJr!ilG#qKp-+P3QLQ(BN!!6p@[I4I@,e9$ +(f`rJGcGE!Lc*'C4mcYVYV(#V!U0',BRV3'&l'f5Z2`Dh6$R#GD"`D`Z-bNL"l1I +q"%GV0b$H!CRE@eRKilQF9f%llbm$+qIrBal)(YR)GD"`DbIa1Dd$H[iH(5MFHPM +2hjl"G5"lq!CN3C`bH$fb4l55$[bd'4iNr%AmXeF(i$Il-4[24ieJKqkh0m#aJp1 +KqhJq("hEJCB8r6RrIaZN3LGV"H%QTe[b995*98f+%PBMcP*CUTG9c3R$(j[1@XF +6(mDk[!AXSBRB)b9,f&FA&L(ZB9e,UADN1`,Lbf$iPME@+SU)[B4I4FHR[-@8CIQ +%8rlSJ3FfXBfrhiaih&QaVQVk[if$Q4Yh)ci,m@f8AmHAjL,HSI-CXpR)hdPmm%h +j+2ma2Im$Ka&[jMM2rp![M2aaA+pR0A!qbV+Ce!0M6r8!6L085IA,QY-dT3G3#e1 +F[R"3N5*1-aFYC*P1TcZXbLAqJ+b*YcZQ1qj`QUcS!-#BCm'@SBZKaq-5brfK&6* +CcC140*JK%6R&(Nrjaij['AjTL'J1`-2Y,Tq6b*11YN!X#j*$S0+IbH(fP*HB"2# +SFFde"kKVC9AP@B)Im[$1Ur9D((@02NhV!(Dm&Ha12K2PND+m48rl(#N3#)G$BUN +FL)h%f!pqa0@)q4E6G)(c%"bqIVjfGihSQZDD1[9FGqJmV!lIe+QQ!$`X$Ppr36l +Jd6m*r+#Z5Kq4+ccN9FUd089k[clj'VNhJI#XPj(%kZe3+DrK-p18ZhJ[ilrlm+X +)djT&GZ3JGe*k[j*aqemp-Z)m"JDP`j'(4p*m8Z"mBI@k+T*Q&X$$eeIC4&h*YK% +2'r'iS%5AH3L1!4pPSAkp'$S2V9qRE9L'BLh)G6VfkepeZYC9-HKM-LK*bB#6U3r +lQK)m%N(q3pA8!G-`a%0V$03PmU4ljPfX+[+)0*N%m+#`1V",2UUVKhT4Gq3cCLi +`Eh(N@2Sc1DikmR-m6!-FHF@Fq9aKqq[KpY3NkQ(fiHcIUqT9Ccr!iiUc(q3aj1` +(kcVNl"-m,MTl#X2CXd-r+c!$F2CRkr&[ChqZX"mkq`52)@GrPXBeCcr!)q(XEFJ +MBHleZQTb`ifq0rp`j$0QVML6ikSMIim(K8Q!)kraKqiS&&I0R((GNDIqbe%KdAl +PZ1bS"RKFF8+$p4Kb3JNHPjAq,)pV5Mr!`q)B8-88j)(#-'MGVBk![dr1HLF@jcZ +%H)6VP[IaU+KERZ#4k[l[QNHmk'jAkIAFTcXqSBJT@#hHU4GjS!)&qVcQ51)4N!! +d6A6KAK4Z"*fUIUcMd5[)pjN#e29Mcp(Ma3Fmr@8H#@90p&Q2)NEkfZcp'-CAK6r +5S(3qh96HA)K6Uh06VRHMCmV9)-DRQkB!2&`"6308)05AFcb5d5[``PqBdr1TT#P +!2IJdd45!4fbDf-IMmM3abG%B#3E+fh3HApk('bV*3LhLfCm[%%EHZ`pbK$YIQ96 +H+BJ(Am18Be#lAXIr1i8lAp`KM&b(@mma(XZaLmlAie+I'9-F[h+KAfNr)m'$GZ' +"6JEFk(Xqa6%&k&Fqa6%&i%&K-`A3-`UV+8"r8$PLGb1%PCGZ694VXKU5J[*eLi) +-'6d%jVc1`$3NU"(-AhSJ(YQ3!"56RKi*iJMZ9[01dG&d5%D%IicrRiANIeaGZ4L +c-I5p(YC5HrU@bAria-&hVlXJcp0*Cq*fY9&@A&)bcJX-$5RXB1((AB`$,qaPIEr +lP0kB!-A5T9U%RedbF`KUibb`#)BXRZaUdXSHXI`af,V!k`ZJQ*5&r"'r&2#[N8Y +8U5%SKkk8m%E&GIjH@3hk3e,N'RfXCRD23qf0N4&Ii-4TV0XGI)jKXK)l8HAC+'` +aLIT!ihf!1kf"1ScH$LbrY$YL3K$XK!XkHq'+lN-*r29q#(E-jH(M!m-1QVY%9Tk +VBZkm2$&[EV'h'*qH-TFhcfbP"Hck&C4Br1@f(Q[ZpRcU-2ibMR@"S#cL%r%F4F8 +PK2&`@j@hFL*QI,3h[l@$$rAT2KeBR`#kFCG&@0+$N!#2ceQ%TI`CI6S!hj49fS$ +@8h#P$1",D`&[EV&*K$AA!&jVBIF39[KG`%0UM+q@&$d(+a(l&Q(H-N!eC'm3GTX +#H$(&`R2-q!Tm%l(e2-IRi!R%Y[&ZI`&fS-(E6GLB)$`0-2&K`Zj1!raRBMYK#jk +(rFL'ApT+fJQ[)XBpdH3S(%AX-i60fJ"r4)`@Vf$#@SDecXXKV(3R`iZ!HBYi@ae +NH+8PMbpVI2Bi`m@&bAbM9eh(2SdB[`D6XirKTXTNAV19M&8"61&h"Y@lf@+!UAa +$H[%cV!%JRerBbfc'1h#3!-pl#(E6"5S(ch&c#f(X#'%M2'TiZHb,L0LVQMmFXXG +`YercTI0YS2L1b6$D&h1&kq@lj&"DcqiBrme)1m,BJ)bPM1%eNKVbKaSdflakIb5 +X@N[#SBLGcdQ0a1Nah[0@494*'q&UdL,KS,K!ARer@+hAl-8qRicV,KiTdUMC[*, +D)%IXFjVmJASMBHSL&jj)NL00bM$D-cHUP%(BA,q'Nemj@)Ie51YC3ZGjBZcepDP +Be3`iAM4AL4Z6$D16-M&LK*dNeR0[*TNJA1EMq3b#G[fX!GB[*!IX1)Z2Frdr#X" +ep*@YdXjV3#LC82)VB8H1NT@4CbYCbB345KLK%EBH1HD4Sq6`-I&-I15BC,LH$-p +12#0iHTSIS4HK&lQ9N@2NQ'6%*-q1M#QC8d*PC,D5#5-d-Q,Nf40'MT%"*30'MT& +Mj#PKj"QC-A+-(*-F"6km9eeQK*'M(D%RSB6L'@%%4B``!!)F8%ib+a8(!"M1IS$ +%!ja,L1Ni1h"qJ8l)JJ4TGAM@VAK"5V2pB(0pU2q**&9a$q$L-`eNr%G2iRapaU4 +QPe0QjDD9'NKjPPqTHDEP!r49Xpri*-'R&Nd(aC@pNRIh(EjY4GqIi1XZh""dYZA +rjHX+6'I-b#q#p2`&,P9$rlU%IiNG2iJc6CP!PMEIT6F!2HH)MLAC)jD8(*cc11m +a&6%9-F9IjDVB)R`qY3"*1)0JRVh&$D@kb*5!VjpGb0*qVf&2ZBk9SLJJpr[cZeC +H`CIAG3hCkha#$Q"#D2bR`@1NA'"CDC3Yb808')-R!5jm3!$c,#ZJ`NMB*,6+Xa3 +Lac&jKC+*iFYBXfjEC9,mZ3S+eVD+(Uj#0%6'h&I4'DQ3!0G44DYTZ!TUQF&-0Z0 +21Z9$airYU+!Qh&Ca)ZpL6!h6Xpd!$6P3%IA+RSVHXI)Gb`b-QN[&fYDKr&B4rVT +(T`2,Ze8bV8-RH-2[Hj%SP!,Ukcp0%+AbPk0KjirATJ0lbXUdj85mHbU`fB@%)d1 +LE21qL(,240+h@ZHJ+qK4Trp8%IA&PJV[8)8DUFq&VX5TA,Rq1XIZqd'Y6!K)R$9 +1i5kV,,3Hj@APBl2*kY4iK%!(CifcLP`09LrSfJJKQ)X`TA%,0aihUkaH(*iS%S, +C#PRM,$CH[I`U@"'""(FSEYRPZB&9lLXKS!KIMeTefEmFX5+#24mf@R`A2*8IVR& +@3["5#Af0,aYdFk2Pk`Cfa)mdl(8+*QHQPD%C0RGk!GX)fdP@G-d2$dBfrfN0$je +VZ$HZMcKi9,GDkbTpG-1k[ERC@X+lANV6$Yd!r`%!!!f3!h"`Bk9-D@*bBA*j,VP +`G&0S6'PL,VN!N!QCLJ#3%1h#!!%0rJ!!,`[rN!4069"53eG*43%!V5`hTE!Bb43 +!!$#J!!!MA!!!#H%!!!I9lB`j4J#3"TI&$F$9*A0J!dk6dfkccm0[YHJJ@cIPY#- +X5PCZK2NbMp02QA)kb-JqYSmHRKUHkAAQEr`92q@a66MG6lH%ZaPKYqNQq`L2AG& +Y,2+mJG&0119N5flV2R+F8%)**fc66BiIf5`*)ca'H-r)*V`Ra`R[fAGNNp[(pNP +Z"EjUX0PY@K(ZE@Qh*P[EANH2X-KY*E`4fB`m2ccC`1X*"+$Af3-`!&Me3B"Lr$I +BJN%`GMq&6`"q49K2#N("p4aE!C!!l[Fl(kahGbL5dQmj)1"EZph4D'ZYV@)3a2J +6IMDq8Ac6J`$,Y`+8l!FM2RJ[`-FSclADqh`cFI8%1*E1XGf%$6#1M4&fbX'aRr+ +%9X*kVq2B4cQfRZG1jPJRae)ieXpcH$Nf`RAp)Ai)B-+[9+j&12*i@I`P6#"J)J- +L3rMeYjba%*!!4DS1!)Y`e*%L5C,&emHVkFp46KpFI2`)IEH`)CBLI14-RK)`88f +(i@#3!&%3Y$3Ur,jp!1ZaAD`2mMHN3)V*l[0+6Pp-+5-83#SE&`BLhj2"f9#l!9J +,jY12CS0bTBFB40i%))NAb*eZ[ij!dHQ8a2YV(@*GaHB+qXqdb`(&GiHXp+KLRA5 +l*$CGhbJ@XT,4Vl+5EH8Y$@*MM3[ba#EkUXaIYJlBTJ2BjCr-@PB(A@*,!p,*`A* +8f10#R[!5X610![3Z3f!4Y9*U)3"hm8CcqLYT#EB*"l9)GKaBLV9,pXU+ffNp6#h +H)+Np)Ri[LG6fmHCFGVc&L2q40`!2A!mC[p@TU,#cCNR6lri*dLqXSBEIrbMq2m' +f!NZbpNT1DiZ(PAl"$r2REqAQ!4XG+adHJZb2Ri6VLRlT#[kQ(0m2`2bXC-MplXY +`SU8'm9()(KPNT3rNF9kP`rG'H*F1er2hQ!GbVh(KNa6%aiK2m-LRKq%PeXBV8RM +iD#JrpP$8Eq)AT-2c@[lKV"S3M*!!Qhi2XL"1@9b2h-a"jS,8lqq!6a(q0,l%e0a +c"*rkqM&iiI!p)3h)P*MKj0'pF2a8!Ik2`BRhB*I)3p[)hqq(&"KMJb!XX0SPCd1 +6f06RprZ8J,9'PMTP4E9#qTI@X-'Pa)HamHB5YQXjeNMe9VCR8cRL$MEH4YSPilq +!H$ZNlaYLJk+)f&(mpfYidDrCRSd[%dljJdGfISlYIR%[iK'$`XCELK%IiQ$flJR +%eb'qRr*VH&XHiU-DRi9l3rR(L!pq8cl+Ie,,[r0ja(G`R1IIpE03rJLZkGQ#CJE +jq0[,U!mN@jfqAVm8X1Ujp!'@EEADIBTFlIE)URLMCBePY99RT3m!,$`)TLbY'6S +F0V(HlHf4PE-f)fNQ3b)@96JFpHqkRAEmVr$l2IS!21cfqXT%RJ`F@BQe[H3352T +c1Hb1qQUG!"kYYJhk!,Sf0M@H*rJ@MqDU,Fd'5iI,UDSP`%i0JVNZ1Jj+ISKMjNV +*ir(j['+0l!Q23YLpph')06h$ZaVa%#c1DEjQHkYSHjpYjFUTkY"i'#h1P5Ye!AJ +B,-jT3GlN-6d"HP0AIic)C4lbG[qKE`eSpIV`6lKl)ccR@5642`+0mTeml*Cm+ap +`XN12CH(l8AES3$[E-hp0Y&l*Z2e2Mk`)MrL!,"ej1#69+ANZ#+[TkTG8[3!HcTL +b#9h*YK%2%r'if)RQH!L@Z)mb)3mUf9KS206T2Qe#'5V8AYkR`dr2pZNYYSBC(j0 +&5DVM6UE6jqa,m%J%q3p&9H+QBCD(k[*d*2*N1+SZUBSm!Rdk!6`SM"DXNUZk1UJ +@08HqYQbMISXM4qR2jEMLb+Gik!BimSE+1TSaar@`1eS6HZKp12[A9,hLl1-m,M[ +l'4kccRj'eePRRq"abGP6K*`pqf41Zak!Xcq[acqFrC5`EcRl")pCChqHaY[12Xj +$2m$C'bfUh291hHYr12+eC6hRFPaaj+raS0!*F15YEZrU8R&lfGVr12+8[cXU*$V +G1HBF9Cc(C5FdSmHX%dV`Q1[djhQmhHRM2!b@H&G-4KlB''DXZp(LFFIDfHQ*aB8 ++)4kqMZiBMiD1lJ52&2Yrec`LSYYYZ,QM"a"[G-4N9)YAkL8Hf)%m-Dpj$I(`5+S +UfR![#MH#*Y829cak"AQE,S#ZlhU1U"H2HrSj(SR1QULcD%F-a-VXVj('9d5[pU! +-2Yf8[)rMe'TUb[9U4+GFc`e&TTZk!$aX(P8&l%$BAkCic%1[`)@r1+IR8dPG!$h +i0&%AJ%GiQKMM-6G06,+i!VfH4a`DMlX2iHZlf3m3cre`#4XDpi19MI6A2$,!pYp +-c@BK3+BAh`q`%Hm40[4cc"IQdBe9G%'20qSX0-9aqbr@U`&(1!NHY!-0Y#Vq6Yh +c+BiZ3,hb+BiZ!!m+Nbj!2k-`kJ,8"mN4AZdAERppb6qY8HSAkh`ZVqVcrY0K)%0 +'(`+crXZKk)J%&B,q5`e%)KH5`UdRfS)iJV[9[&)d0!2Q)F,r3Zm2`VcAcfjFL[8 +BfPi2'pMbbMQ-Ir1*J+qHpd#HVb3Y`qeU6GC-HfYEXp1MEGI(0U)LBDD$+,`ZH&' +`CmmF@*QX+cfID'Y6!j*bYG(S!359F3iBK&"E2&29e#ZMcI)*-)hVJLM1F[R*)(f +Dq$CfH8-D1S99D"*S%&k!hi@65FL,R2%M1JU&q`I@LP$1RM1X*ZLE1$aI3*Yp&Ri +J5*Jm%h3a"$2Q`LPAk+Il%MRjYSB09IPLrSD+jJVmG06DQ[2e9NV!V&e"#FFIMkX +*"EI9&*,,AX,'3I"[jK2a4Ai&Pa#@`Je0cBh,-H0pTr-E4rP3RlZ06C9!4mjb#0Y +@"FAiZBi`ef2Sd`(iTUaT*j!!0FH9-Y`$E`5mYF3+#-Yq'MBMaUqQp0`&(d!-VkM +Je5@%%AZ"X-`A!5F#l2H%hA`-m'++J8rr&hm#2SrB,X*@ZH%VL(f0X-+$m#J1pVj +(f2)I`AI`aY0R#8Yq%LB3qb*Kk4)FaQXY#lKQAiCML0e)Q,X!6L$'Hr%Y4q&hL0h +'H43ae(V&BX)bFKLHK&[45TMXCALPC88AB6A$$"FAmUZiPM[B6BKKGH%@fBmCEUV +Nmp@`['GB%d!"lqkf$c(8Zp"1f!fIBFLK+)hA9LhHri+L@hLK6G$eT@+HBr%!BH` +KAK-1aGFY1`-LeUVUpRR0BGcZ9TdCI"XSXQ154[YL0PqRI+[X6BhZM[&RSE5QUNj +h`+FBUhhH3'CS'c+F2Sbh5SVAlHe5cAa1'NUF%HCGY6fJ5'UQV8m0q(V&MA,r(6k +P8c9A1*dbVVXiT)",069,5TFF-&IfZ6fGSB3TQfei)NN1p2R6D-mmT&)@B4[F+Nj +qjGi1e#!eZS61mi6CDqY6BD9#F&JSNkhDMXR5k+4-Q"KKCiK&lmh-)`LAqALq%%' +cGYB!pI2+(M21iL0Fr`m0!&6@1I#bXlbm6%[lcLLR[CfGr1cYG@)$`ZQPl$aq4YL +4jq,jHY,(-hdMq+G(9NB'R,!M!hk%Nc%MamNa-QEN@13BBIM)J"pKj$NjrS4&'1' +%%hlN+1(N@8rBNHFHi9j('''%2@&(RK&'f*&M3!#GZE)e6[Q4ij36KQFV!&AAC`4 +*()!T-"jSd(NXUZYa3NJ&rYB5XZB')@[RBaE"hbI`pc[mfNa#T(j*a&#MHL+Pa68 +e9YHY+M(9-(H&$J,MVaF#(L(eFrU8D0eJ0'k1e!fNe+JfT%8*QFF`+Kr!-EbU,M@ +T'TU$b6pHbRi[Qm+$(X[kG%2Ye1+UZD'Z(ESS9YdkB+FqUQhjQ'c+iP@%(%h+H"p +H*@6f83V(hba#&MH3!%lmS`kK(Z'8#+RTfr-qa(+Yd*L+M[aM"IDDf@qA"5FBF[K +8@p%iV0)DMq[44PM%Ibp2XkK`@Fah@E52+-NC%Q50d+,+C4&1MKG)N!$&3J['SQ2 +RlS"&5eR4c[C%V%$MPFFXi2X##qbq4lkSF9Rd*#h9'%YCMjLX+f[4Np6b$(*EEeS +,aL+FPLbf#"B,A"BlG8XE1LKiV#V[VSL5e!UlUd(iJVPVYfVUBdBdlp0AbqiDd+1 +M"9qm@(EAS*B)pXA+XKE[*,9dJF@Lab`X,9jJm95C4DqZ"([dT8N@Z(ab[TKVU-0 +U1K8XG!IjAaC-LdTS86Tdm-*##iH&H6"CC1%j`GHL66'$aBARi8kcS,G1QjC8M*N +PLYFU&+8HE4p95XkSNBUfJd+`r`KjVXJL%@[YPLaS1L"Cp24+&Nq@@I6YP5aQ&ph +P*"I"6Rp0X+#h$Q846UH-'4T[#4Ec`+*$X3)Km0B+&R2"SR5Nibf8lJU2UmRJMXb +Q,p-XU,[#D68k*N9pAE#J[LLGqRME"![U8AVUle5#mffjG&HA@R3&)A1NdlX9Fk4 +)BklFAGfDDHRb`2#50GmA2FQBQZlri-!-M4f#"EeeR!K%RS&,")XjP%9rb9cN@H% +,4i[qdQP-RTFEX+Hr[(C@5+IhDXRLfF966prT[A#kY#MCAY5LH1VM25eCp#PD%#A +LeFYp36Ha+IG&Zh4k+DE#HdCS3AGkd9Rd[5'dS2XLSKKQmAbV%LbS&T(4iC)@GB) +&hHQ)j%h9'#r)X9Sk2@+8!KP#1`%&,A465`pBKG[i"F'#CT)JS#@(!kHq+Ca128V +MZi)3H'h5&qAV!Y*,TlqV&*@!p*-X['5&T@R9#63AKQKcS6'Q#bTETeN`AbcXd&$ +JS)GNZji5Zl4*X+LNjh9!`(Qddq'cU1SF5,NGM)$'q[pmi@Na&XFkYSSRpSBC,AJ +[*+#a8E#SS5`k9(28dP0p`hR(G`J@LlJ@iE4P+)m8D43XUNh,5%B98idQ#THAej, +jQiAcXa)XKUaL$ZkLZl[+!CC*XAhNm`X9K`I$l`h5#6!G9*RT!ZXL4lf)2FY44BD +E#@hjc8@P%bM-88A##Dcc(28m%XSFUTpSCQD0IFP3-8%LQ82e%dclMfpf-&da3D, +NHLK)S1aMjfi`9%b3!#!jU$*aJR1ZF3pG3f,NS-U%#3a[FUihN@&`VL,c!%-2&42 +F`p`0iRi'`eZFkbhFR*bVZ&(YXfIICULBi#,MU1+#XqpHk@+SQ1!#imi6&aZmICU +lr$6b9BiUmPMlXjqqCP`a3BV2R5G5IcMR%[I3*A4&R1Y5GN[X)kIfH+LRpU!E-SR +kd"#C821iYr1Dap([FP"P(``-ph'Zqj!!qcQkbT`3IXY`jf@3!%4P0dSqZF)Z[X+ +hmK9dT(+kqTdUU"RMZXD3!'6c2555Er[iS9mU$MN+BB*NLHXUNLKlSUH"S@+#M0P +"PCQdIHam082&"+QRJbT68QbI8biUR5#Ij,U+2"21ZFT3-8(R,HFK[b0RRrej"9- +)%c5IFUKq8mVqpC16$"86G%kbpe#qSf,rq%-V3m8%l61(UfbVf41*kjlc%YH4Qr' +Y,()fq'd"Gpi#j'6FVb*AXipmXBUKBS)qCFi0I[m5'f'#liB*Y$[icK0Y%$"FbVN +Z4CDGhDrjl"YBUcRUDQ4Z@9hc'4f`DMPU,CU-MUkbqBLp2q5KRKT#YblR"Vq,Kee +XH(ZSf8#+c0dJ8QISXT)VY"*pY4aArh5!,[Zj3[[44(43CA-4@(XjkPkb[X9*4f4 +ZGI+RULiAP8l)"KG9jP46U"XCeb#AQN*YG&&PJM5&ZUR&G813!"M"!mZj'jD6cCk +ZK5a`LQZ6LbTcb#R8,C-ZpeZ[d18!9qJ!NP"Aeb!jYBqIX,`lli5&RVDlmi*HGbD +dlEDh[EEG4LmlYe(mHL-fRFjhRSl@mZ6YpI"QXimIcRJ+(FkJrF3hT@K,)6fjked +fjqkL0j[EjAl2eVjhiAH'LJRDF0Q8)YqH`b9jKpq8Gp$biULL&3DX2cRURqLdF&6 +4JE'r[rB98`J6G$AiR5FD!dM@ZKNU*QJ21+LbE3"GlR'&lU(9akmSd3+%,QHi3QI +3Tq+SSRq&HkD*AcC0k-ARdKHr4ip0PqBl,if'+Em145-e%pT+2&e4''M,hZ@L0B% +Fj$*24#kMXC0042)0(r[+TpXC+LDN`ld!C&BqG3'%(b6%qBBXe!aaA81NNeifLD1 +28f`AP8j#pE[3D+T!2hCa3k6AY0Jhk!mXkFE'q$E5'6FIi0,r-b!8'9#(lprR(q' +E#N+rSGf0r`%!!!d0$h"`Bk96D@e`E'98Bf`ZZ90S6'PL,VN!N!Qq+!#3%2[B!!% +FA!!!,`[rN!4069"53eG*43%!V5`h`V!$TDd!!$+I!!!)k!!!#V%!!!-pe*!!&38 +!N!EC*!h!VCp0C(j[0XAc8klAbeIQTYc00Y[VCQ3cVlAR0&01*G3ZfmBfSBHRKZI +dV$@IikmkZmdfiA3c3VH%j4&fHph'ZYK!`Xr)IX)M2"EK0,)CZFeZ+pQ%%NSfiHF +QQh@a*)c`''&E`XNq"ahEccElMQabqpJHN!!9q1TkpTYZYNp[kjZYE5efK%9Z+q% +0M'c*mm16$EbH3!"kR6d!!i$TL`#Xq'qdKm0JDRN+R`$mNV$&b35&jh$X*S$d3-$ +p9CHR0H#9UYeHkdi$[RD+FY!eVrS'RV82rA0dMb`V8*'#"m'%RjMTXf(m(U'qbV% +39fq3!'0$1,D@X!l'X4l#MMXjpL*2D#1XG6c(2X1a16ah%XFD1CE-X@8mKipM@lQ +Z2m32!jMa+`9-9!PDC21bq&ZB`B#*M)L%m1[[1I[$J#a5G!"B8*!!&ZAdP5"Dr@f +mQ[iDKI4"K8JIfRF0#l&N`kG2jE&#dJPZPm25PbNIc!b`qV3)KaN&34286iU0'`( +QB-1C%qC25-0NXm2[%phqIUj*N!!,+DcAd"(prNY"B(d4%S3%AKI,23'GJ&V6-BR +EjcQ&mU)&4I5IlT!!JV*rU53[9S4bFBNSZ'kX%UD`r1k[XI`l#QXUKDUbCXJ@A23 +e0fILE'#hlF6Q[LGMBMNd#6@95#F6Lr!ZpQ2fV$%APS0al!Cm-aVrae!(TES(Z)Y +hKj0ILGRBMpSaJ`Nq"BcCCQ5cr(Yh-IQ,af$-6jlMr4*,QmRhCF#)66d`l[&GcH& +I&c*j2I*kF4@-f0U&q6BJRJpCEGZC[2%V[!I)pf9&1i1mIJqpTcb3!2Nc8L8Cm8, +L%plrq5jiPG9a03b2(G$bBrr!rVAl&el-dkcP$j@"`35C,qp!&X3Ta2A)r1PfeJ` +T0AYJ*H(Il@"bT0f'RrVkmfcUphKAaAcNE#`XrqPZPVmAYALkRNdIPJm`mAj0,`' +5m`VC%@$*YLE**mNHYfdIZ4ZMU#`@m(YF9-mMe9RX5)d*rk0[!"kk%G*qSe24CF) +BQJdjS9rA,Y)[SU'+Gqh(rpeX%E!%@k[SYMANXB,0!4JqI&'d$a4dK@$BbQ-`IZT +KhJF+0RI!m)`Nb(Vbph#d)3RaEKLfYC-92*60H49dmIV5X!Vq([0!eY"QhJF+0[F +3Rp0p3-dIk`-&Q`qTqEXbH"r)'V)+@4#R$+j(9RSRpB([Vi$9K$q$,drf!IMj[P@ +D"P3)&MKfB!-F1Ck,rce`G(3h@P,djrcp&NL'(YB*KT%fKqLZG!QZYN$!,`GYCC, +B+-Q+$BBm1)0e6L!qM29@jl-eNl"'5KHaGEF9)ZjN[A@N(I8G!q,e-'4ML(8+!Q) +(m$qJiP0raGE0$a&1qF2lleR2eVkb!I'SXf+p0AQ)KcJiE1eZa'FM[SAbUhKG0Z, +G+Tp4'l6m2F3([bNIj6qQjVrR%1)V1-lcVhP*bar&96eVJ2-*e-qL'KMG9`-9RJC +CP$f5BY00U3(XK8NfYlme)!CYHLkpN!!0XpNFIPNUpAJP4IL3!(@'p4DEcNSI!"M +e"*Jce'ESG0U&#Sp[X84@me3N$'D)ajJLTl2L!mGAMrp&JB"A(i#(`e%a0jiR$@f +"-+q9(!**IbD(`eP4UK2!SpCHV!qJDj@VkLc"YhP8PbbX0PSEQYf+NJrXH#GBbQ2 +6`+4(1'DC+hUpIVp2+*1mNC%BZhm6KjMV@Gl9L)I"kVl)eq+S&Hc6lG1QRDX1PBI +*kTif64H!Kp(U[LM)@c`Z6J,IdMA36q3D$kNpX2HE(@Up2[BMlYi)ccb)**CYK5T +T1CqC*Yh+jpYXlii-I0r0pZkXCqZ'ciM9+aQhrqQ4%H8a-#JGJMbFSZ)@[HH&9A8 +0L)TH!!ph[l*aAFQf%3mcmEM3LDlb-&J(I*54k[95U$b8LhhDM$)8+Dfm6dHHrUP +2,l4A$[UB$%T51Z"N'[hZYML2H*!!rj!!&AR!0&cKS64l'q*jdT`PPe4&(X%fR3! +H&#BV9XPlZMUT&P9(2R2@I2d@4il5RmPahC'Iik%Ei-JVjjE6R(e!$iHc0Uk(hSH +cIeh9kmjqJ-FeCcr)iiUc(p6eLV12mlMNl#NdCmmqPeQ["q$XcqVa6fGr6YLhRAf +Faa9RIjE'1mjqJ%IFfCZ44pcFUlSU8Y2lGDrriFKRcPTm*XGe4rik$`UG!%GHkr( +G8L#dcjVjVL02rSHM3U)A1mG94cA!ijS6'Y6MLK1+mlMDkFrbH+I6$r!`@JHkBK, +b`-B`D0e09UqR[jfGR&LFVa$Li@pSkHG4fG!5jj(Xq1qD4e4dKlhXhGbR+clH%C0 +3,9kTPhKJ"r,fHmfKa--V+STJaldSh!MU8cp5mHJ9T$Yd!A6p`(2%[2L!Tlr+)pj +Cih8@kiM"rM*l-e,jU["l25L06cG&hlGaDR9Zb[9Da+CF,i5LddeG!"jfVk)!GL$ +X,qGi*+*Ai-*IQ02cUD3ZJ"jmQUJ,`#-b6HcRFA@DQ'"Y$VCkYcP9(RI[aGGhXam +JRR9R2J[e"Z!4pZM"(GXkf*D28E-CKAFPI2LqJchk6$%,[Bcj)MaDX)V1kh'jcV3 +TMLG`S9jT2b21JhEKJ8i'[&rhI)UM#e#[I)UM#m#$`U`,d-mS6,S!p8&b41j''*D +mF@XLY8TF*T6lQhf+hrF[Ki%-'AdBQ1hI$N9(*+J3p&pU)"TCN!!3D6fa&X34h+h +QPD+LDC!!L!Mrdpir!BP[A&fj&(-`e,dHeV(`p#f6rr#*JUpGGd'HTj21`ZeU9GC +d4fdGh[Y4,`cdEd4&``*'IYa&1r$#$UVlhAhp4JFSkZU8)"jFdRH3!)$+1"1-"V8 +,aJlj8&#[M$A,li#j9`G#F@aMZ(&AdZi*PSQqaY2$`FZ#Cr',8ISdm5hUmKRUI6! +"+dh)4E-`T5r*8Sr5V(UEe"diHcQ%6p"l,15A[A31JTe`39pJ,aLBN8[c$4bHMk6 +02LZr$fAJAfq'`B+jF-UPrA4I)M2(APPFNL2N&"G9&q'RFjkp1NG[T33XXDN"a4p +[kl%9Vp6I60eZ(1X&3f!"YajM!M)Z)8b!bDlUUNQBFG0*1UCZ2Y6REX2d-0#0Zdc +#%PC$(Rl1*LcT$qM6!ILQE#!%C-eaT3cJ%`m!hYaLZB50V3HmeX*Z*qcQMB#(e"K +I,5Pl#CBJpJ"K`Gm"6J6BUi5eZ`![TKKj$[N`I!QaPB40fJB2)lD&X,BeX"d0hQl +#XTq%ar(e[B3PMJ0m-kQ,X')4pJ&-jTHf@Zk%ja($LeB!0lA#8F3q6YMXjq#hL0( +L&8ci-N1YFmB3YR3e`iZ!13X)beh1m%T,$Pr@8-BcA&bB`MGkE82C4a$Mef$FfaP +ZUNcKQJADQ!YJ+Vmcq1'2XNm#61-EdJ@6@40!(Vq`*pIK(6M)ic8%ZqN#PCAR'0Y +"'$Y-@,T6pVG)lU#!YDTir$j,"(Gi&(FDh`D+lTLNdVkBhGmSh5Vj8Q+lBrbCPMC +Gfi#-T)cJYD,XmrLD&(0*SbISPdfPIPr3`ZHN@Z+d#1q5pU!X+ZRf0LASEaAQ5mZ +@qZ9'a9,NGNZilZ)8Jmf+Z9U8Qk5JC@kEapZS*8aHB-F659+`,C!!5R[QQNSCK"9 +l&*cm5Ud0U%G+E!QGjiQ`9pHR)UTTF&3dHkN$NkA55CN)-F*1%B[GQdNN#*IjH$k +0S%8pDi$kq55["@Ia8Dlr"`#3!`[!hGUPNlU9(@%R18D1(EQ9-$+K(6PU4aKKj0Q +4Ba*kNTAG5QiPYai66`mI)e3bA%q+CbHH(CjkR4iPc`KE'EReb$&bp-JcFNc###A +2b$2b0$*K4ii4YMiC-FQ%%AEN'''%NQH%%RD%(AP''$P+D16B%AD%N@IN'"P3mZc +)J!+AYaicb8TSK0Q!($e2*N%5)`b!!11"dRUmNXRS!%#`lJ0`jV"H4dh#fS4e$Vq +kJ%+l+klSIRPBeIA2Pl'22bUq(G81S*6lq@)C-j20k%Pr9%YPNpN,V(QTkVKLb2f +mX8(KT+R'`T!$QNr0Ebr,3k$jNqNKri"UC-DcLQUXqE(D$V$r"RcTQ3&DHJQ2Mce +LVH6&a(HmUYqp((3rH3Idi4cYR["C[@0A2[f)K,M*9H+Q1YBFeN[%JjS(0DIa3$K +-`G+Z5A$$EK5$j$Pf1-HQL02Nl3FQZeLr6pC'4RA9ck*&lpF(Dj`pq)+5&%Ah2+r +3#j!!%)cr0(L%N3ZYLdE)M1-i@aK(G`*d[1(cljL'9JKEQi5emPQbP"fQSXH55R" +d0*,@c)$bKh+Eq&kRF#&&A$8fr2RcVP-36Z(P@@b"Y'jN`5JB3PrUA*h#XdVaGIG +C&"jG5ihJNJ`-CHUCZLX8M[II+(J@,B,Ll`bl9LQU'j[MH#0T8mfH55SUVY606I# +,`ZTVP50pTa-"53S&kKM0'a3YXD3jh)M"HKAIGBVYXF(3`(MDe%E8HKL9HIT*%6` +P4rbKV8LGUa6XFPUKm1"#lf!)1+ZEL9CmqEkS8IMilUVF1B(XEiU@bUcf@QRDXeh +XRqmP"F-F6c%&+(HejkIiBIBD+F5ND*JTi1#ZpZc@@*[ip0Rc8AEN5Tb9+G$-@He +Y8j5eIT%3+RKG@20NAb0PHN3KKIM*4*`T1%Z@UcelTDH4Cb62A*N#6M&$MHe4P$X +A5CiRe,N),M&$MBeqpqCm'mNR3ReKTZ"eB#9NAa1PkGRc)L&8m$,KZk&abC4Z(GS +MA&("fmTbY@qa-MhmPVXb"@mT+b(lpLVG,Y`4V%c*I5cMjX00k*L@SSCe+*1,EHa +iH)9ppk@`[R,Tda2X3j,9Xm[,e5EmRd(E@3ql@[i$!*!$$3d1F("MT94ME&0S6'P +L,VQj8fK-D@)ZZ3#3#@#V!*!2!3hq!!%UFJ!!,`[rN!4069"53eG*43%!V5`h`V! +$TF%!!$+I!!!)E!!!#Ud!!!,jPjp3V!#3"L0r$F$9CB2@,M16HhCjH*CZCT1pXRA +6,MIG+f&N-epfFYT&C8VS)#2m2-)16`h2k9RV`)fr+Xp0pdQjK**0@%pB5[Da1RB +Tf4Ujr4kjcFKa5Qkc6EDA4cDKK1iMr0c25T-``Q1%2q'%Nf2l22HccAkEj2DaHV) +#AeeA[pPQTEIerGEfXD)4&VQYK'ebQffb*Fr#N`e-m!,KJEh1(S!"`-aP!!lm@Yc +4+&LERX9I!(j0f+T8JU,c12BaJ!a&mAfpfKHSDLchecQHD"6`EkqN4US@9NrN@8q +LEdIA$@@cG+6J)E$L%c2G(FAh+2f[I$Ya$83iPXka$B5e-Bje%hE8bl(p2+',X1# +0(2XFaqEah#NFUqGB+XI@mK`KMQhRZVk%$`&Xq%S$+e@#%AQm,2iH0K!`N3@4$Rc +p-fGI#-JLc35!"39TXBKH5C)Mh-bVk@p44!mU4(SBlaV@`9+&cjl)ii#8Bp`ZK[e +N*LIB''$e'4'0-JU#aZY2LLeE!1CK`jNAjEq3!)DT0Nmi*2R#I9a6B!UNX4kK,Il +qDd&JI4%5J54H&beqa55JeNa-iVD&AR&4mC*LqJlhb"%eI+HXVY,%4G*U5DbkU9+ +FbTaGMc,R(88e&@*P@52NL9Ad+XQI-"IBiLH`ZHr+R,!)'X5D#U56M8Ai)q&E`N6 +,@p!#PV'EmCmaq-fP$NTe$h!Al`l(AmPjf)r@B!BV,!I'A,2cQ,1MPDPI1J+jVlh +-qb@@0P-Ib)44$hE$Z#HIDScqVSLTQj!!erle-'Tl*qCELVJ6FTTh-(A,ehJ28"r +)LAF'GG-ZqTrb32B[5*98a)Z)6h6[aNjiJkhJDJL2l62bBrr!rVAc9`(-dfMNlbJ +$`3VC2hmF@4#R$Uj(pZXl@#1NeHb#GB6rX)fTXABEIIDEVl"T2qCG&I14Xl%cjdp +@-ZGZe1+j+@aQPK0J`TF0[84)R9(%$J&,G6A))9Reqealb0eB*'f9L1paF6d29HH +`3c9@r-Er!AMi*XMi[8P&PiPL'$ENQ(kG6j&q-3ee[(-[IRHbCF#5A%(*jkTkQ49 +X9@$Nb'Aa2P$3f3&Ckil!MG-1mMj3X,804QDQ3-icIi6$9BmMhJ9CfpYC`F0jR&G +"*km[!b[Rrf-Hb"R4b2Y!`GCZiR1U$qMjHrY!`GB$H[l160i(FY,A)`[LP-Reb"R +H6RhJZ9DiPr$RmFrMI3"qZ@HpS3%9JKf1l#[$lfBiG,3E$SrT3NZ+rTcr[`e5SCZ +eJh#$bb2j+UV%UQC&#DX49jNXeFZUjS,dKfDcp[(%Kl'HDLHlEa,@51NbG[rL)X5 +pV'F&D8Gp4d"m*D4[k@$YSSMB2[`U1MlY6ACrf4(#+ApdlcfEf)EIE%BmlUaB6md +-a$XiQ,9K*q*c%Gp'qA9m44lLA6UIdCZ0r0h%"pq8Mr)IdI2IF`$a9Slcr2Ipc-J +Iah8pDi$c89B@8Jf-19N$1*93*G8[Dbl6P"V!ATMLmS@$LK4aQERd3TEPFRR#UPc +U$mLDq!R(E-FXPmP+(`!Br66B-[9Qk2@kaA*rD*9-9[0%*!eQ5%4ZXGGErT(M@iR +IBN8*Q!2`m(M+5a*j-Y!@L!Z$j"")qY-j20lb8T-!(VAZqHB!ZPC@9CiKq!'2kJ9 +,Ubf1ZNDITR8"1pS1GKHILI*)8GkNTle%#J6#iC!!@#B(BL-apZh[m@l%I,I6G)( +c%"bqIVjf6khSRZQH2[eXGHJmV!lIp1QQ!$`X$Pqr)1rck*m%[UqVdNIN-JpjM6+ +cT8L[emGH*IFQ%*lp)T*BZadUj4Bq-dfjKGFbrVXE[iS`Xe9N"erJ6NU[9c*Zrp- +M-mjMB&#DMMbmNZD6!ZH%eA99*-dXJ)H[6pQ%VQ6EL)H0H*c[4*Gi#)i"(f@KHVd +31JqY[drE8)CL,FMlG1cA[r6TTHk+34q658P+"ja-IGMAR1#4#2)IUUB1Q)BK(PT +MS#k4*m1li)+Ub#25E",!Jm,U`#Vj8&F[eD,Zb1F8hQVHiXK4qY-jVMMbXca-!aa +j4FNLhQ(lpI"iDa0kQ(diqhG9[H,X"hKFG[D$2)DFrD#Z3miq`H1#XkF`R$hEre1 +R'B#c2k2([jhp@@%rF2B*(N21rJb0UmjqJ%I#fGZ3!%I#h1ZkDR,$pEShrh$NF`T +ARFjaaC'rbi2#*-#4erT$X`V%0B9cVMRbe(mj+L6DhcNZ1DS"(THGd+!H3diS`H0 +5Tcr$ifUR(q"KF3ada46NJBeKd,TE(3&rAcXl2V%i9b(%)ecAe-HMSUiT`529mpm +eMlMS(RICYGbR+Ml4%903,9kT&hKJ"`VdHFd4a#-JDCVSaVdSh!JkUAkXiY%Vb(H +B!ZMkNHISpH)$R[i5Md4R6G4CEdH-p*ACHc'-V`Trf)-bq(46qHeLR&UGRA+p%le +6VJBa2Ydd"H$K$QJDB!I#rR+@4c*k"5lmq6NpRdUD!ZM"TiQQ!$aLdm3q(THQL8Q +1aNJ`80kKmrM#EYa354D@)Tlc'DF`B[PZb"9ZIQP+HEFJ[[!UTKb0[HXer,pEZ2R +j(F+)Zh(V1FDM#D[SR"iAkmbBi[L9mr9+qaN*(V3,$h3bi(VGmbQ1+8#pmLQ1+3! +2#TXT3$qMX*S#e!I*%EXE)DbqH'ZL4T29N!!8P+pC&'6)k#%`ec8'TL&"K@$q8J2 +ab)'N@1[TE8%F`GeUALNkQJ(*L2#2mIr6N!$mMkXV&f)HKVlA`pU@RVTPmKmqFI# +Gkbl)me653YbZ0Q6&*5AM[-$3N!$#$KCqh-8im-*He2Hl6rBE%k"BX8+,i-%PF`F +*U)bc`5)BEI&%991[l'f@2`"E$q$a+'`Q#d2qL&m+q&[N8P9U#-UKba*H9ecRAbf +V3Ap)LPbPMfVQp$V8if',q!+&$Mkm-'q*lf+ACm1`a,!1F*Fe8$F&ebHR[Pf"j4G +h4d`)JKec3@FZA0&p+)'rhJ["MVQmI(aJf%&cPmM1GeI-Aj!!,qE2,kiZaUGhSEX +khfbP"1ck&C4Br2Qf(QXp[2RM"r#AFD`("'8*RiMR+LSZ)Bb(b9A9PC-`ii2(meZ +lq&#IlY1"p4'J'hICK#AG#c2`1CH`P$qK6`IJQl*+"p"k#Uk8!Acq,X#E@f`+BDf +eJ0GDf'f%&A`$m*!!'Z1V*8A2`'V%[NTBmdV!EXMHi$)k!5qQ@(L1U6[J+iLY)fc +j1RJ%X@f%TEm11p$Jl56-p`Gi%Q$5&`QE'!EmCe)RB@)&l!'Bc#pYP86J&F5i24V +lI6L-f+F*@hd6[)8B,9l"a$D'@ZIR%KBqc2!LB2i5`LCR-Ec5NXqA09SU'#iZ618 +E[FNEf5F4ipGJJX8-0e@QFXfD[X1U!+E494QBi'Hh!dcR'p+IfXJD!'E`#hXjMq) +G1*M"D`KfdJ8U"mmaYSd`GT!!X1&H0G`Nqb)LeUVQ$iIX-GcMeh`CI"XS[Q-bM2E +&h1&kq4BjP0Dl1mCr-p)10cBJBbPMH+fNK[bK"XffS0iI#D[@dR!SBZGc8L0a4Sc +hJM849G+'ZjZe5$JShLU[[61XeQ[fBTp2aR8AVa4Te'c9NYSJ4q`PcIj![C%`GBN +E6b6*N@CP'1fC'bTP%MEIVq(N9`l@S4jT[8[S2%q-[Eiq&920J11LZ8XpQ'`BRC5 +*%52X",(HHc2*"1%b(mpR%,6VC`e3[j!!(,$M,$l1pIm!N!-,`(Ad2ECHRFFPR+a +N`XMM#502#Hh)8GP+RT&Rj#NjTS5Qj&Cbkh9i'Tj'f%S'M2ak8M`lmB6KkH%lRj+ +M%@ENeXJaFN`bSNHHNMNl-UGfC%E*M*%j)j3-'*Nb`Ti`mS``-QINf*%*"EkqSjR +A5GK+ISh32$b6))N4"N!!!JlVJ-r`TZXk!-$`[J53!-62*(k$MRFAhPqJ'h+J`'" +kE+(ZHS&@X[d!3(R2Q5V!cME81#NmLZ"PM*Q5AG0-GpV5"M62mYejcl4mJ%`M4p` +)*hRT+G0"ZCTAp4kp`I20D2p)AJ*CXflJ![[k&0BK$p$h#P,8PBNe5"rbHXYhTCj +Yq5GqG*raf-!U`2!+,kAB#NXT$Yk,H$pKDDbPXCEdAdVB+RbqZ)cpq4q,HIBDAb3 +EUbb*Sd6RmR+1h[FEpQcGX63D"@5rZh+JI"02AYG,5'q+'dB!+Y,aV`D(NEQXjC3 +LHki18f$Fq!pJk!-@F*l9FHL&!Nm&HXUT#Urd"DC6VTSm$l,@3VeMf5ZrK3A0iD' +&6)LXfE%)ChrI!YS@AXH#NS4EU"p&cZeC8&B0kITSeV&V[qFQj!U,+#@&43,T2-( +L3k'mfp(GV"@pN!!@f@RhD#ESR2[HLqlpAPc)MaY&E64,6FAK1,))lGX@jihLa*h ++Q86%r@%KBU!i&eMHrDTTR8K%1G2Z4D*F$5KarNaAQ""YLhkH@VIRj`*leMTZDZ, +B!RF*UA$DN!#HX,-M2$RL5@K0ESq`*6qBVe%&&%'01hp!9F8%a*QfU5jZX+@bALT +3"EU%Dj`E,H2D@pBN+P@J@e$MR'JCpQ4)Y5HK4dl$k45e2QeYXDBrip5S!JP"M5F +B@4[5&5Z3!*3cG*V0kbqf-kaC'CdS8!@cPHGLR-8EDeX2@A0UV$*&&G`a1$AH5C! +!9BqSGGa[IP&rqL+V%9%EZ&[Y86Zlf+EkE&03HHAH93dc$2F1G9`[dGk2dI3iFad +$I!hI,HX&"aIk+1YZdBGZ@!pfGU*(q#p"'D3hP0Er!*!$$3d@8N9"4%e&,QpXC#" +$9b"`FQpUC@0dF`#3#4-H!*!2!4aF!!%[f!!!,`[rN!4849K838a'33%!V6CC3V# +$Z[`!!!'D!!!)h3!!!58!!!24cTJFa!#3"USQ%3!)(03FJKJ31)9R4#2T%CD+3bR +TZ$mA@+C4l#FT[VLi0T(LAC-5%[e*2TiHaUeli+MJ!fL+c-cdk!)VHcSGN!$9G'U +6J%!jMpQ56kaeF[e0@`(NIY"TIBYZaPSZ#Jh%Q6a,T&"-4%TDC"$eBGaHiK6#F$r +UE,#$K6'4MH2L[2ePY((0QJiVbFa)MrLFX4C&XrV9P%k*E55PCMmU5Cp"9'4'1-Z +-Lf[I(`peHXRTR!r8LlC+CmbYQ6M-8l)b+*0iU$1*`cN"2!)9[(l15!V2AXTrX0k +2IkXDVJbafq3iprDLfarSC$QJ-RJmccPl4rQGfdfBlQaj86C'lbPj8raYH`fTHV# +M8'J$b0qMPE-r&2B(JPdGlSZC1ZG+q#P'FmIFrq"C&J!!#diTH3##$'"#b9-bSBI +R4`Nr`JJPP%cibZNASCa`)j6`p5+FF$)$k)'TbJ6)L`0mp2K+d-F*1MK"1P"Pe1[ +NbSmFIm+0m#2SiRLb(S#H2IR@*kE8kB3r6(I'FL6Mm'JLY5C`NhcBN!!1Z2@",Rc +,hqX3$0kqmVDPdp0MHZa-dbRSe@cFQPSH)+X6Ya@YRMap8,m*RNkcVqb@YfD9Gdb +@(pL5V[f$d)%h(&9dR3K2-3A[eRC$%24pcklPGN+im510NG9[4XAqhCK'q!eX@8H +1&G%ekCkF*`Xr(+MAaLAm#N8JL8`U`c['UarA(D35p@268DFM0Cf'DJ[UkC(Cd9R +e+9JlEBp*ZeDHKp0+UC8A*"NaZmEk#-NKq2H!(61`k%F%UCp)Fdr*Cl%"fA"*TFD ++XDGdJ0S2%(dd&['`dC0MSHr*ei)Z5lrcJGrQe*e@ReHIU8%hphS0Hji3Q-&B-r' +!mp$N))LP#-B!)B`&f'`LHhLMDN4DcNc+m%'`YYc2QFGPLb"(dc,j1eJ"[*DXUB- +1"Jl95M)mFbK1DmPq60TDD*8#LVTr#Rr-&km"3+6fj@33Ul%&lGaJhPVr+%cQN!# +G+hA`jZcqjfmbM8hebm%*,9!L#+N1,H'dJ0VNPPJe9Kd-3r1AhXh0a4pkH&Kf%#f +#S[15JjfFi&LDIV#-jfH$L,5dRc$"*5dlYXAG2h*LG5FAZeHe&i@T+a(C+6&bY`H +P$M*,5%%qklpGV,EkjjR2$X!aJ9$a-0&D$,#rE))C8XN%c4LZINU-S[#S+LmCIC! +!cK!11deUqPDTKG4E#6HFSVUI*E5mS8,K!P#'dQb6&3RCYY``3[+#19G%q2Pb0#J +ISIRVG`)P&)Z1ppJB(MhdimJaJ93H!'Mi@T5Rd"MAQVaaB1MMKa)()M6L(,6@B,f +BHK4EN!#q(9UB3)UXA%[2l9N8ah+I0m'm'dV4rqeSET!!ClSCr)6[ZI3NaM(h`Qa +[Df2ZM6pYS!15DA,kLjYTqqaZXl1YS@d*(H%rQQ&[D0Y6m!1*dN[C42ALBTH99bc +,bIS$"qQCTrb*Z50"rfpQi4b*Qd1"l6*AaQdb0RkSM+88"F*R6F)JpAKak0@bKl, +Y2',&a88HM(5M8lHhF9*1#le06*L"L`EY&BZD,)Hj"+6$2Ri5Rb0DhFpTR[Dhf-I +XGEF$AHi+SpLrp2"q'$L2d"SE9NEE)@cJ*[*N!9d'%SFBi[m"%%kr&p8R32,F9C9 +DmY6@hedY,YpG65D,AUqEhF!6qMhQ++lae#2F)Uqq0MeGZh@RHrAHp"8f(jkqB,H +ZiZJUl!(e2`d0$R0ST90TEA"XC94ME#kjF(*[DQ9MG(-!N!N1MJ#3$`%UFJ!"2CB +!!#m,rj!%68e38N0A588"!+dX0m+`!k9%!!!bR`!!"h3!!!Ua!!!#REUK4DF!N!B +@d3h!e@86QCrCbJDCKprUCMD3!+rGTYfQR*+9-,+C,cXjlD*b+U'$[1dCSH'Ti6R +ef+f6MEmU6qiQQh)*h4,@5GKYZSh9XB&NDj&pK%GZ-l+PQh$*E9C+RK*+0Z(R*T[ +PCTX44KMKK1fAF-+[-pDlf@p'1,R0,X)M+r$9GI@EEEC2EqZEVHhcBNGBj,B5(YR +(pNZH(jjXB))A#!rXGIB!$!!+&J$Bm@Yb4U0JA[3XrJ,`'m)@Ta)8RF@a6`*NU0j +[eIS#BEpFjr(ERj3&r0XY+C(DZA@Ii&P2SAq+RP'9dc@Nk"%`ia-ch4I&pdMYVc` +VFI9(1$D-BkX*kf!FkbAXQ*YM,r'%$X)#0h,XmabEaA1RF+b&BkNF@mCc"$QfQH[ +k-h`)B-&A'TLT%[6)j@Aael#!J)P-L(6Kkqmj"d*!&QN'!#`S5)Yjp%U5l+%fANe +rL9*k8#(53hrAXbk@+Yal-SmG8SjcHbHXTc)9JS8"9TmHd5LM)'LmpU4B[ajJ&MD +F@9(q#fQBDR'&JT)R0-!e"5C$'ZX61Z,[2aF%eKFK%8MLGG(Z#aX%e*U"5G`aebh +1+jYI4Yp-Paa43[I)bQ*9R#FYNF6DQfV%+Db`jpZXm1l5qQUaTY),Z@)Y[FVc*X` +%G[Z6f0ahC%fB"keLI6A5bF%L[*mp`AiK[!(YB,TK(Iic"VpMUB05h30mJAH(%kr +NA1a(5c'$'Hi#aK`cFPRK3pZBmTA$-2EPRr0qLDA0P)HcB15'AKMhe$C[p(HP6&Q +,[&jD#5-hGf1qGBJAJUeY#e2@Ij2h!19K@l`c+'Yhd2q8"h*H)995%5mP2Y%pAqU +')kb*Ub%mrTbH(rX(pUrYVrNaMeI2he8*JKPbIV89@4#R,Uj(cUqh-#qNeHq!&B6 +rU)-TXABEIIBlcl1T2qCG&I14Xl'b`Th,@H%Ze',R3PD3!&d)-1'VZPiLT1DAXS2 +!8KfYFP"@I"l(ER)h*NPG,1*lA&c2Jh8fGV$HM0ri2`#2hJ3C4`dUZN`83lFKarA +VhNEka668m1ipq0h1&J",FJ3NMk0K!5[D')B4)aE%qd"4GaGNVcJ-0dip`2Y!dFB +1'*'9!VCRhS4$$5@)pd$fjNj@p'JZjeA8cHY,akVirjJ(E-1p[!m8EH`P2UIlJ*D +r[`m8EGb[jHr1iRh!0Q`PXL"1@9`2@fBRpB'I,)F(#ImTrRQL$m#VZeIU'P!K@1( +JXA`i[(F'((`c"3k0k8&,L[kFrlm*8U'AGB)`bZ'520@eBQeE1"a5)Sj+@@U4&G8 +"`akC`6V(%ar'qZS+fDU*@#-9#pLDfdX4Gl1q*Y+1qSk!q%)BYVk,GBSLBXrK0kc +K8epRDqE52f(+(phc`&UfqVIV%)ml+pCARipi&`Hc9fp(I#ELQbLrKMIP)YkMm4Q +p6XrI5hc`6INSrf%Yr`2l%9r1FCjre5rer(&Fdl-H1*r``K+UJ6'RDU$+eka)LNp +@(BBT0B#p--AK#3A#8X4Kj0),@EE$i3STFSA2,k[LTq`cl0-G"LYp!'$ddf$*dTU +Kfqd8Uhc"a6*CcC140*3K%@2,h1kUMahI3[b@KF0qB`!H,PG9H5*2"YS#F@k!(!* +*IbD(bee9B4$!Sm%jfaK!ejVDQV-%hqG40kHacQ4[pRT8Y4$BX8k`e[#C+)q85Kb +6B)XYPrcq8#JS9XVqf%L-[Ak%3q`EDcE4QhJ)GXp&[PCAJqJXF%kEGUik0"jQZfI +D0%-!(LDljk)Jlr'i1!Pm6pI`!*'V215PiB,f8UeH(hq"h*Y!H-iq*,&X-p6)l9V +phXTVQ5C!q,p@V`IfFLHPe5XCYrrTN4AR-6JS(BBmh*,UNIcRKG9d$8ZU83!2ci# +b#9h*YK%2#r'id)QZm"$XJcl+&+[A#k(a8#rfD3[+8+B'H*q1rIV(2YhSV"lb-9Q +8T',3bE5%2'd*(SNJrk'SbU"TZ-a$pIUE%hNbh(-ZUBSm)Qd'!6`Sc(DXNJpeG9- +YDSkmZ13fiaC(MY+IbA(0NCrMB4MJb+[,jp'FI9!2PlXKSBI4Kl0r@p9VcRk3!-G +9Ccr%il+c(p,eXV02m,MNl'2E93B#c[kX([pdpZH%IGrC*hKFG[CRDAcJl!Gj**b +p"ANNc,fQUbUhIP6haKq1[,KNmCNFeacjfc`S$!)FHB-[1,e)A&T5I0f4Trl$85( +4LjhMLU-Dj((9#3hTFGN**AKFkI4RHAc3k3GjQ1b$A6%&H@"M',,ZCV[I0p$16N` +XcPF)m3Je,aVJ8Gfm+-%MeI@I0Bqik#jRjIAFTbXqd4&68#eHUCGiB!Ib$hM0iF6 +$,kQUk-5p+0`)1U9qV1,4+mKh'`,SqV(Rk2ILJjlq#Sp%Cdh8@Ap(M!b8fEZ4cPH +&2qa"'Abkf60T*dkYcNfjhSVq+GH'6I(TTL%!$kGI93%l%2DAFcb5d5Y`i5r-kIP +8dK"!$cj00!6J%CXQ$[#i-Ne-XRXM!Ap9PmEMrPfiA*FX0#*ZqebK-2bZAI#U82c +&Se@pJVMh"8`j'R[AbrKrVe#m[&-BIKpZ2FGi,-)U1Ur(1h@Q6h&mi3[e5[XC#4k +d#`pd-Z#MZZG6(%1!HZ96(%-!(K3@3i"q4Q%f"+J2NL0f0d*BmXkYLAT99S*53,j +Z8C!!)D1(`"cA'4L'""@#m8X0a--'5E(@dpq#1)+leEa50$3$NK(K(rhrTb(jEeG +A,X8X$'f[Kh8dRVjPmQmqFI#Ykbl)mh65%YbZeQ60G$8diF%IlF,!`%C82+aJiXG +Gp!-[E*qfhhfUhaJ!4916'X'$5mB1%P!Cji"*d0[LbDUQAYRI,(m)PMk8c"Id4Ab +5hpIq,mGQ!+"i8d4@!VkJ&2Q[BE6aLe('02%$k[)MY!0K)KS'FE,U[6NFpN`jNB6 +qdla0qPDF[HcA@NJM[qaPF"$XZ![k-RY4B#DZba-i2"p&QhefIKp+i+ph3l"L,MF +I(qKfd0JPF[+FeE2Rj)PjXm[Ub[$TRZZXbc0D+3'VGJ8P&RqiVFIZY4fp'EXGM'0 +p))6RmiRif,##5`MMB9*YAFe%c,MK4(jc$arUFlGKIJcSaPd1B8N23MiqCa+@mR[ +dk3"m8cEF"@60FD8-1DF$hYaLN`PVh!TiVBAG3GM)l`-H8Q0mYF6r'La"l1Z%YGm +*1"&J4`JVAJ9i-FA%FpLq"ep$E!9K`@ai$$%qT`ZBB!XD[1f%M5k!T`!Q2N5BHJ6 +`RiRGK*9e`@k!5Ic59R-c2)mBRqf@[`L(%2X-BER$i3h%D2%+4MBae$T[,''Z()B +A!I2Q%rET@aKHDFRMbaS6[XY`F@%+hqLGEQ+h)-D[`FamKZ'QbK5Z@F&X9JX`PDl ++32dVl%k!DAa$fJQX&5#IApMll$km!`Ij[)CJ1efJX[-F0h33aJi3PZP@3SYN6d6 +%@P9pSD!eKVYmULH$E`2&Gdc5D9r-'@U4Ej@$DIflBr`h2@fQ[J%C5aR$'b3Pk!Z +fUTBj,Ej)5$&AK))4+jq6kSNcBVcR,)dSNTVTE&-MSB"iQlcXRT!!dU*Dbc`H'GG +Gh&,%UeVU*+99MPM,fhcq&MeKkR`RRNL5)fhKG0SceeA+)Qbf6mA*VaaS4Mh5qTI +3HCiBHfep+UDD$XG&FeDi-&NkRC5*%52X*,(qHc2*"1%b(mqR%l4UC`e3[k$XYq) +X2Xler`#3!`V!hGVlRB5GMFL%NPm**i``3MYb$%p@`JNP6mNcFQ`p`RiPE#@h(Q& +iTNH15BEiC(KfUicJUFr`(hP'+$P'eL2(b$(*-r+-c"Pj4Tk4BdH1ND-HQ9%bBi5 +5#5-64KJj4JD-$"Jj4SiG1IEN'"NbmSa-+2$ea9E#EL8$YK*'f"(k!K"'RK(N8-) +!#("!!%#@!(DMi3%!`cN(%%rJ2)DDLA-#jc1mCB$$I+*LHq@DihRXbfem*`pApeM +r"A!Zr@E3,eedUllPhhb0jk[$m#Lrj*)60&Uql36TiqQcM5X1KC*L`1jrq[$$,e@ +kV2"F*pMPSlNRJGr%3lc3l[ceBlPCJ!Xc3RU*-h(M(NZ%mZerDaRiI("YYZbZ0$d +R6D[$%X'E%d1%-hKbTPP%plDFN!!&@(lk+mZp!`iMFU9PH)%pL*fQ`PJm!($UVGa +r[JR6N!!A68*2j9S)*4RDAX8*3X-H8dBYmTf#5BU8SVKB[Ei,-UdT5TEp*iliL%) +PZPp5j*V03YhG"U%'&45apmJ3YD@LS)CF0$*'821U1hZ4r*C&[fXPaD&q&TjV@k( +EU"[qL!+EIC!!4C5-SYL(rNZYHZLZ1!Dff1mX*VFTX!-9a!i$`042#R&,&HUKieq +eE%d4EH8SLhM*#QYMJ!%ddQ6&CQRkp8[2Xk`6K+dU+F#PUkBGZXEN&ZYQl2(rhl& +1jIabK44-@'5XfkI(&qc)GF''5CQ3!0l`pBmEYeLlC"EcT-#8fSEa4U2A"QY6Jj' +#V5N5dLhEIGaj`YU8%#RB6-*90eRh4H'9FN8&@e&XVQl4AZ`1ZY*V8["h)(-GrbD +kMdiH8DbNV"l&MaV(rZ@ECM%3RcDfH[JZeL'f+Tmcmei`+*4cG$(,cV@YVIiMr&[ +cHAT$VI82!!!0$3T8Bfa6D'9XE#kjBf`ZZA"bEfTPBh4c!*!*k0X!N!m",pJ!!8X +m!!![#rq3"%e08&*$9dP&!3#Y,$I#X!1P*J!!-Tm!!!I`!!!+F3!!!X8d$l-C!*! +'Jc!0`08PmeZChjX0mK+rfFQeb9k6f4&fNT8``[9DQ8@lU*Zkk5$EaSVGQZ%jpGL +Yq4Yre56h56[#pV20)SaXZNhhX8[*2PCNNq0(EM1bTH3f)lGCHH3TB@36(YYNXbk +ffCD`,H'ac3M[f$jMQffff@mMccr#)a6imGI9*2ITEIfeDfdI+hD%4@iViC(EE*- +YHAjiXS%*AL!mX0IC!`J!+&`!i-#[aGhC#GE&6q-[!,mRE%N'3Cdc',X0),2'(kS +1++'3!12aJ)4rHReUV(T1cBFi)mGIrMJGlB2,*qY)dGI!LNr-Y,S6hi2d[`VXa$8 +8Bk`IBaX)Da'-G4$fUTHaJjc349M6#-BqaGJ-cTh1@!0M'BbYi"aKaVDaVMr$K`3 +fI'@#P5V"L(`ZLlq($54-C%'N&9rrc*N-#9PNQJ#`S#!YjY)VcHH)0(-er5e+k%' +&5!rMA5YD4BCdlkNm$NJr`He+f-pNFS*0!&DI%CfGJS+J@r8RaHE0!$1`iFcSj&p +)``bE*a,fq50*VZN`&M,&6UNPmIjV3@"p%4+$0+k,PF'S58#YQCM%AA1mmYc5HDA +dcIBS-6@b6&'AD2*Fhe+IA$fU5KiRR1f2#ZFp*E@9FP9j!2,PDRV0,"Jj(F4((XI +Q[MYRj&aSP'XVN8iZ&Z&piKGLRf8XSTCE0Z&V+(k(83HPZJHiMl[$b9HII1a(bc' +$&Hi')9a6mSAcJIe#rG*a'2ELIZkA@0T#hCJ$JalUJ1&22"RSI,P%U!mLVi2VB0# +f0Xbh!h%Rj$9[&qVQEh!28$IQ*6U$qZ"ZqTrb31j[5C8-a%Z)6qHcAfL$Pm4#9N0 +kl$NM2rB2l&qlIKI#2!%MIfXj5&E)rI81C%'F@PQ2h0pX&`()V0d0D`Mr3BY3iqf +fmqP[(K$MImKG&I14Xl%,jc-9`VN(YAKQN5JFi!3Bq@9$,aNb*TD)Sb!bA)e+@&' +$IYGHFMF@RlC%a[I`K*j(Dr,%d9SVIK2r!$`m#V*H-DRS-TdBKJdjS9rENk4IA%- +GEhX@[l[%!K"TVLDIhe@p@a4YLF,!J3X5ID#SV48'V$N1)mBIi6j3Y+8&"ZDN3pl +hAi0MeBmLhJi$YUd943rR-kqL0Ui[!k[Jrc%2j282F"mSfY*"I-lf!6er9amSfR* +)cpq@`hdJVpmkC%'FFPL2[1bee!GqY!V@%rjMr20N(i$$HpFC'P!Kf1(SDqP`I0m +812lF9MJfY"dY+ITcrRmVC%!(5Ld0GRPmrXTUZESj'SfS-9HjiQY39-f9V$@aXmC +l"Pm%r6DhRNVb1RikAm*CLCfe%a(AF`hBX![akBK[jIb-,ma([*e`XAE)*V(K$jX +3la$hMilLQr*4rZ0krXmI3R`9ijcrrPmCq4-i8FBFV)q),LUQ'KKkTJBUJ[@U6`d +Ub5S`'DN"l)AT,RqN+HU,ZFaFHU%Bi(*j)USb1aK50(Q5BiTMXXYNT3m!$(N+E$P +k-r4khA*&-,a%)DYj+Y*k-k4L@+RA@r'"ieZ%hp*S0'31`-2MUCLCbT1&YN#Hdd3 +1JD3rPm2MVCKY%X#McMh,(%$AUZUUm`6Ii9&60Vr'iUJ2q$A0#H,9Y@#[iTNS4hS +jMNQ`aFldK8+45&JZ9d,aNCK8Z*)KmI-ml,Jk$mRKlqCVpp6*lN,hK!NAUN2RBAA +i*d``"H"KFILl"AQE4rFNm'eGSdNLehNSbk1&+d[dHRhX"A*[@)mPN!$l2**BX3f +UP*8m-dfrJfXCrpe$p5S9VT,&NAhXT24k*H2f2ceb%Mak"UApN!#(ekIjID',`ZU +k4RfD@3!2Ie,CP+jNfiL(MAKFkN6AH%L1(KpPSAUp($S2VEY2fe#'8Uf*qh6mepI +lp(ahCDq2bD%NXhZF6%2%hjcLN3Vb(kUQpTL'Ucbd3+JqP5I,@hCC9H34DcB*i%& +KG@#9[+FVM4m-4ckeq%lc&NH1dTr,FF14Aq"K'Z$)+fI1T6PlMaiHEee+$l-2Crq +QUMHFI3q2kmkqPmG9CpqVke9RRq*afGP6'-jH(2bTd`c!fCrAipr1rS+`lcMl&)q +Vc[imMAHGI3q2P,1h)Bq8ZGGeeC6'pq[Hr-146beHFLl($8Iq*Jm+N`"(AKF-6bk +5PaG2[HR)-rlPU*!!D(IRZ1DSHRKFGd+pHPae3LNHecVpH4l[G[SH(KC(6eG-4al +B'(UYZp84#LEEfFQ*aF8+)4k4qX9*(TAeLe-m-McrAI0)L1jaPpr-IEEL8ada(GA +L5Vh-!cY3+1Neqa12N!"2df3hlNAK4Y!CpH-9Mej"ZFF830F22%HA&qrap0GiT$T +VUXkk1Q)X@@C[49pH&Ak["fAaG22`Y`kJ'li`jASMZUCFSpS6dde6!"lZN!#Q!AB +Jl#mAH24"Vm$#AjV6me65&%!2RLDD![#)6a162+j0%p-FJ9K6U+*9jr(C2CLPMc3 +ImEa21UAqGqq"`p+d5Dp8G%Mb[KF`j4$XA5rLrah5Y,&VTIkVFHXjcQ-a9Y&&2Dl +8Q6(B[e5[YCk4id#imd-Q!pqZHTcLQ!2A+8aa6!"i80P1!INCK03@S$j)MIMG +#@RVPeN5YTUKKAj0bdk)J3d%25A4[6TZK""@#q8X0*#)2dZ+YTkX&-B+le9`T1TS +&I4$KMr(r8p$R(eGA,XF-$(f[4l6-2h[,j$pm%Z!Eeef3!1ICT-@iADh,QZfT@iL +AIr3,!mQ0U%6B`F,(AB`$,q*jIErl6,ma!BU&#l8B(P`bGj!!J-Si&bb5d4C296A +ebUjQq6f`l63&89cNmF8SFjVi,RAjIZJ8C$i5*KFlR2@6aTe*XLbS"A4[daF[j9J +1k5eN2Prf-MXSh"-Zk)[LPj+`X$6IaZ(jB0VXFr"p+)PIEi9NaeaH(KmBGY$F*A) +,h*@cbJVNJPQP0DAip-jaea5BVC5!AEq#%Sr6YrA%[B0AI4Ll(3`A1d'+cZ1*q,# +SLNX)Ym+BkTUUdCMaSC2jVHdme'HhBAd%k-CG,Q&Tkf%L2UF6P[jRp1N![#NEE3@ +bjVK5K[qe!YlF%R4T#qSP`'XYiLl#bVi1H%K0m'V*lDYK+@*I*@ckB-#*J(L*-28 +@`!XX&XkamQ2`&F6@%&Eb'AJ%XDf%6IS*E%H$YiX`*2N%`1J('2X1i$qMf`JEXJ2 +f!ScK5eZC)q!!BMcE(I-b(-2AlDc(2[J6BRb**AZp3+d,KM(A-3)[!KE-)qbMIa4 +iBDD!Pc8qFC[!aB9a[0'lE+LBKKKIJaRDAq#QbMM@l0-E46A!H,SU!pTqmA'!#E` +KAEKD0!*-j!Yldcq(Gq"J)YF3l+),9!l1F8X,BH))kq&9)iX9Idc'@Y@#NE!pMRZ +#QMq,Yi%51bCpD9r-(@P3lP$#Q9flBrbEN6EEf)#-TicMG6ie(!`hDVDbKQ!XSPT +R4m)a1mp*MF4CFGjPbf1U6mYf0fZa5*0mTl*L@84Yd1bPIVq#kbjHAbbJf@TmDU- +5XmpX$SBDM)3Cmpai)NQ*08IldTkjS9)1BE1#'NjqPDCke#1cD`QGmm6CkqY6FG8 +-1#'DHlB(Nr@PNc*aBS5G)YCeEkB23EM-arN-JREpV!(U&eC#GTc&*lMq(`V!GFa +@'a!fm$TbM$`M[a*'RT'9(6QfNT8`3JNP+hP'MUe(f+rN9X,@BiGRkj0MNZ&k-J" +fUq`!U-p@,r+-d1[)HZ3B15BC-3ND+"Qa)mI)8Br-+*NamS`m)a0''$P'"S`-'$P +'MKdj4KLC-I+-($XbSF#(ppDV@pR+b'!PM$"b9,C+f"%88F)!#("!1FQUe`8!-*a +(!3,hFDiMCZ,FJr-jrK+J35aBXN5d@19#I%81p66ee`FES'Q5,dam1E[5,$I[[-' +hc6"kLLq5[kVSE1lmijXVF+IHDPSmHMbkN!#m&6eA[mEK)$Q*rX`1q@MZFX$(3Z` +K#fN#j`V1Zbb-@"Ja`hNU5GJDI*jGK4!F3M$0AZ''FAZ0'DjF2lDDS2d$4AZT)AL +8SS2FEdq-V$q,,ffD1@4[b`NTJ%@PF9H$adLj`K*DPUhVCkJ`NTL(8qpNrR8$`T! +!mCU%9Q8d2'5rDiPmf8Sh'YQDlFDYhbUTEqL093444BNl1r`rZQLX)L+pq#YNCUb +#EDXJ#CFV0rqS#2G9k"p33lmYP4I8N!$*H#,Z9%9PiQKSU',3Y9*1*&YcHI0kfH, +BRT--re,Kl3AbCEFk&6"Xr,'+ICMa3U[QfNXm2K%6f&'"Pi!5-6A%'+VSYiG5mEe +,B`[*LcqlG2$f$T-q8M%hk#jK@fAAVYILcE'+B$pP+Fp02j8E9ekN@-Ga@a9#3*1 +XIJT(V,T-MYq-2HhdHpBTA9JX%B)1HallVGc6jUdqklb&IHLaq[hCIGaj`YV%5JK +QbqX,2iXpIINCDp0P3`J%C"Vm0YRiZ(Q2YI0Q,N-)'#TjdrC!VNh@TXiP"(2VqHV +R[2XbqeVjLJJfXZHVhq!pr3'bdMBKH!e)KkEA3rI4bF0++b(,4ljJ'f%lkBDCFlb +$NDem`MYbjK,Z&Fb-F,l9mhRkQ%9qBfYVX)4h[4DM(ES"rJ-0!!j9F'4KG'8J0MK +V)&4ME("bEfTPBh4c!*!*I`B!N!m"2CB!!9A4!!![#rq3"%&38%aKF'ad)3#Y,$L +SVkM%6J!!%4!!N!B+*3#3"1iL!*!),Vm-`2MVQ#GEcq[CbjrbTd8"k(946[[dqF3 +S0mV91Tr62N*rUedQfB6rbMa,QABH@8pDjbDFE,CZRh$#R`ciN5hC%NifqDfh#Ip +Sj&Q%4fi6hNYZNeZh(GR(HRUFmM2G6KaBEjHAH5XIC*6r@X#'[lj00b2l*-I*E3Q +2(#Hc6G#cm34!"jdGRNJMSX#G4(kmfMhki1-2%c%6'3rTJdrm5M"&aL2!dU3aqcF +RqX*@4+eU(&3p[E&'(HVGiD5eDAe2&D"rSepqMjr9FDQ$,!Jqm6!p)&iAV)5rdAF +b0[DZ2[M,PaNId8)#laH8YM%rX2ZSiYE&dk(3E@3dhN683D5ZqmhdpcFb(lcr%D, +'#(NfmX`"8!NF)kh4eXBUkVl9ihVdpK1FZrfh2(hl2[kN-dhU9Srqk2dR)(41k$q +YNZm@Zr+F*MRG)HRM8X&9R0alEj4UAqhH*CdMkhVL+MNbV+b"D%UPHT24K+A#L83 +XfKZfS[(K#pAZq)JD'NPCUMqD4"8G6PRK@%be3#1bbGDVJqQ+pR#UEh2,*R94h88 +0m[VKe4f!GiedFQ%FL8aJfQ#q,fkGh'$1Sa)6KEl6*XhM8e6U3h&Q!mdIfQf&NcX +L9NUJ(JFbUC5d$Bi"mZKSYNlcb@ZLd)CXQNBqJhbdL-a9M4XIZ5Dk24P1lUll0A6 +`H2%D2'15iFK*Ed`$M6Q$!L+h+6U%VL)XmS)m)kSPH2d8-&(S-H&KUR,mKFBj11! +[4#+!0dJ,6"4f+d[)`jr63Kq+LJBb-$**+j'-licd@RQdfd&0")IGLK)UjFpFr"P +T1Lf!Qd98CU+`hF`R(dr6'9)XE+$5@(4lIc3@XB&f!0)qf`-QJFidk%`Bm+dGLFE +ke'BV'NXj[5r$HaD9QbM34BP5iVr6f4S+M-2Cp"hDU$B2pmD(KL,$&Z)JSQ*Kc,Y +eFec&SX14P,,L$L[Z*)1+ppXiTNAeaf0pND5+pYYTFA0if()mPq-pKmie843pIdS +9'JTiVU!VkE)j8E(iVbb+#K`TS*'qU"92eMRZcX@lQ-ic846GrBdU044`9dNYG+A +#d#8L5@Xh@T6XME4'NfUeUPkI'KJ+4f+V@b1l)V&i3KUq1MqGUG8@9S@kKYA9MXI +cm#UU-P%82Aj#e4S+H+bQ@UT@UBL&%E&(86TBG&T6B$P@U[!ZSD8QLQ+-I-`IDIb +aa-K()V%8lrP8Bk,3Sq!E#*B,U0C%i9UIPrb3!,2mJCFrT2+kF'5A&8GB$ir%BN4 +TX1qMjIJdcNTiI5#4@eZ)h(TDTY-bQ&K1+d`8HY+QqHP#@QQL3%rQfCEVq(f6kJU +'$IiVeI0lAT6PGDRGUAJX(Zi6Ni8&)ab,)TEV5AG@*3rrK4Um+0aH#5Q6'NL,JkF +MhplemEYI*CM1lmN-[#m0@qRS5D[ImI%l0,q"jShB-3B2+r"H4"HE+23ZM'-!FhN +*A@ULX2Zj#'0r'Ddb8D"rJLrQ2p2Pr,B2T56aGSR&`PlKS2R93UI,BDk4hc+Td@P +"'Ch0*hemmZYY41HhC-lHPVDYQQhEQcjqNi)09*+-@,[#-E[H%SkC@-*XM3!&q3d +I[h'kY4IM[B*I0qN+FQq$5K9QDM9Gb5GibJ5JVd(i9#%KQZJU%iAYT)UUqEL2Mhr +PpLTR+DLQ*Ac-aFG)-j'hVQY"ZB"@m&&D`dHm+"%M#0G)-UPQTpU*%C2@1"D@)5k +D$@V'PV$)f5Z3!2f5qqeK$'qIXbSGN3*,UXC6JV`Z2Dh"'k)@%d8amPlM9c9q6A4 +H&BN@['[j&C2@cNUmc#pTr,*)[#4@AT'8!M-3*"H9m*pS(EAb*0DF0JdJ9Ujej1E +$r#)IiKGmI2MVFIAaLppZBciqp&8qHIQ&Eq,@6kfNZrPjDRHKd2h8,YhbNHP#Pc3 +rY9&TN!!RU*DIih'kQMTic![i[r(pR,4pA&TlY43Gk2Ucr!`rc3IjMp6Xj@IckHV +PClj*C4mr24Fa2MliE36ki1l,*&R!Bp5`I'UlE-[e1l%jYahUHQSKqCF,J`CAN!# +VlI$QVYm2DSYTrV+G*mqiRP`$!C*3!Grmr0La!h*lb"pMC*H6J``'5aCc&4lZXj- +N04#4)F"3fcX@"Yk,aXXk8[MXD-"'59KTR%mZ(BSHENBBlXI#3pJK51[F`Y2eVPe +8lfTSfRIJCqdeP5XlK0(302U3!+#UMUGrRYQKkPfUU6FaE,@mclRUMRd(UZqUUG` +3k[GXH)ac1r4G@Y"98iIVb`fG)jeEX$`CeppEhp6CeE-HEY1MPE8h+U2INh!edrk +$0pA@'rYrU(U8jrP4+Um+8ANHU`"@XIRHqM*eipiEeU4lZQU(16IVU20*lYP!1jD +,Xh!LCY8hTAS69U*UG,Qifl%32+1q5AUDS0(PqcrCfJ9&Uq!"[!T4&AllY@hYpN5 +NekrVA')M$`K2J0+@lZjVmTF4Qk%MD0bi)pfLL1i)iYb!'iVZ*MF1-Kk24X%b5UY +U#S9D5'A6kH$G&2,F5FTp"bNp4%&089!28Y$YSD$(65U)J&FDT82k$bMY[NdM6bY +mG1!iXp9$lMiNpP2`UFRG#Vl&[`YeDBCS$pB%l+NDMKSZA&*+FI@4Fe%PFlUC1E1 +(H3URUbcHDE`j[+I!BA"235-(VHP5m&h3`A8TJcS0("l%4`l[0*@qR5AAlq"6Jem +lB*d3XAH`dep@!Q6f#ra6)#$lp$m%XQ2(ja`%b"LE%mM5f$M!JS$3S9F8b0-,!ME +GjMX#3Tmj@K5`k8FG!b,!$2U460("U5-jd!fM)$!c0J&kTLL3!"X,M)01"NjC)K# +Jc"MSj!JD%j3em"%j$Y&(#!MGjS-15D&RE,kd@34!(mIabkEEA3&p,%qh@`Ek@)& +1-eQKCiVdSlR6p#-6Fh6Ti"aG1RJm0dZR3'BL'aJVdTd6F+j)ci[-dJY69ra#@YS +I@U#UJQXp*pd2hRCI5kK+93GEbqmk2eLM&SFSVD8$QFUTjZbHDH61+HBph(bU-PF +jhCaYRPUFXB0%hS$ML6&6JKS"ff3f-b&c'6!-1a-i1f'(aFca'6XNCJ*(T(8"'62 +-U$eriePS"6##N!#'Ur'F5-p-f$$P*Q`B)96XDEkAMFlje6l&jXqeT[Qr23[RkZ) +mD$JqPLbk'iKhka5jN!$AmXVpidI8U*r35qcDTIqN)$GhKGFQ@dFVCGZSd1kKB+S +hp6eJpmLQ*&Z)E$5SIpV6YV8(`SqPV0hLrJqT4'S!pD&eADeYi"eEfpNU5mURcND +MkFj'SrQGI8Bl*pSE`hP*UiRfTJ5r',AJD`Ekqh$fd6CX@VqY$I@@klVMf(5el8i +S!*RFeL-hq@kLmi!e1[GjqV%fHFf$lJ("2%pUNqe0Lqi@c1q(4PQHplSf'FD8feJ +12'bHmRr"I"hBGqArJM+D,jk8imNVfMMQfeJG2#Ab[%[JpARRh`(['Qfb,HGJ[VA +3f1YJCMZ`MMb'LGUfe2Qr`,m"@+ec`III"1a#aj0r@*YFGrMr$3!49A"NBA4P)'& +XE#"8Bf`i,M"UC@0dF`#3#3i-!*!2!8Xm!!')DJ!!,`[rN!4"8&"-BA"XG#%!V5` +iU,!DCEd!!&q3!!#3"M)T!*!%Sb-!N!JD3Jl!MkIji5m!qJecY,,23BkX0`IC1E+ +F@3jbe0R+r-lVc1IdqfcLp6DaL8eciR9HFd1#fG61FQjqCcQe3Ckp"h"!!,Pq$FU +qiqGQhF+lML`GQ8(bN!#-)"NY"1)*`*PfGKkHL)RSN!"8SS0amX*SV+#b)1Z)dT+ +#D$k1SPKakE4C"G4Up)+QZR!"FBdaCIQabS*SV,Ji1MU[Z&[(E#'K2L0'$)f9&9F +ffaicq!"ZqpG[qBhYZehMBT-AE4NrqY#8rGX35HrM+,YEM,S0T-+Rj[HKf[-HqHV +`)@E,V$BH8CrMU2CmTQj(HG5lpl'8[ZmkSRR[rR!ihScVeSa'(*AZAAAmHV2jq)I +0TZ(c+AT8ZPbeL#Rph'CQaAb28VSY*J*'Kb128JC5D[9$%6qUS1(KZ0U6+(e(RH+ +efa[RlMBH,D@8aC2YE8QEa5f'cqShZM4D2UXN@PPB9"'Yb#X[+UZ-aXV+LS[bBT9 +&T5AYSh0,Cd9RcUUSM%iY+XHYU+5Ld[C-(q3S'"AQk`KB$AVrT8'fI8fh0P[T9'! +YX$dA,5X[R9k3!&GCB3(8$IZA+)0mbX)pNN`ZiDdq6P[l4)QMY,4A6q*V0b,f#8i +HIT!!fG6*QdfG[1bH5fkmHN#VTKd'fKIC2Dqjh%DM(FfQj5ZQ46YjdCjjC5@9I6i +cQjX2A(*Mmj0E04hFHfVki*[-jQNbQl1m9Kh0em-2'6jVq%&%R5,Mcqc8FhMZk%& +!1rqDTQdQ4b06dmZmAR6YNiHfk45jpZcSk'MkmpG3r@DpUAimeJLa4Q21l&3l1RR +a)I[2(jhETX4XVN)dr&icHM"0DfZ4@B,Te,-LVkbbV0NeE5fkD6Aa,Y+TTkeT'9h +6pYU[aq8LBf8#!pieXPRYq`%MF`D%(6&r8,rK,F,)qIDG$D4CrJUI)'aIb(bL&,6 +Ti9'L"9R%NNiL+C55)T5HcT49QqC(Qi1BqP"dircj@DG3lr36+CUbJ+,5Ql)i5PQ +549NTkC59RN,4,+(H8DEj[H9BQTpb(&LU2h!-6+(dFHQ8NTp&hNVJC%X,`'haHlL +RV3$MJIA-4Q+cL6bcQG+-TNa`9&0Mj[FbCX8mBpD"XcELh)4c-dk00`C[0A*X4Uj +0DAM[)3mM(qlc%3F'Lf-ccNf8p[j'mKi"6JCHLcp1)[JbIk5QMCC!UVl-l3Nb85I +DQQ$MUVAE%'45C0@f""YTeE-)*K,BjmLA6""rRNJ32JrIZ`6fqCBeb36Kmc81J%e +J$*krY5+*3,qe'FmMN85#,DY@irQ+C),0Uc+IaA1+E(%*-QR&+M`RPc#bQMC'm"% +jK+JM%YMRiAXm4dVlI%AihTEC*X$cCcI(RiG9`I09mHGKbI"m9H)jEGPSRkp)2Pq +cHFIcYeC[HfiVZ1fjVH$Dc9A2+A2&kSfCUj,2E8[B*$mfF0Ac40FP[pim2k0hM@L +cV,lTlk9FH0bL2VfE4CYRpDprFXZX9Y('[@NqcmpFdA4GVihc0S&hY$(c6#rGG(2 +66EdfpPVAH%9)*2E-G*J-HXT')jNKb)dV9YZqc)a%3Ni`'eH(C,&PlCD3!#5fC,j +P5jGTf``p'[EIXaZ4+a-YL04!pHaQQhV,kM"-QeH(BC!!8,+Qm9VZjHDZF!D,cfP ++rHH)9E1+"8()UC4Dka4%r((Vb!09fj1S0Kh1IH4N,a,H@hYAZh3(A68J9T%rTXq +SD1H1RE2YQB&A+CDh'DJm%$'43SaRa!&LUNM"bfU8UR#4kH%M(abG&Z"50jZUcja +E'5ZI9P"CB81MA8K4'[(J-&Ge5XG!!3M9b9Him-aiiB))"95,e$lGKL`E@M5P2&B +qYq-pYV5#d9%`1#U&La6ER)KRQ#d+8f@BhXBc)qJiT#mVbpZ4RJfB8P"&`3"D3q% +5BN"eUDECV0!q)3BEVa8"JMU8D8X`UQJQ*Q(d@J)&q&'S"XlD9%IK%U,!p%jec3r ++cVN@KBhALe!pLm*@iLF82eJ8GA$@Tpd8,L'+&,45!r1pSJB1KBdhM&"$S-b`+*! +!Ie3KLTS!mEd&X4[14Y4Bi4+#3%Y5%r1GSLB1K)dhM9"6&0`2!438*k[`R3A3''I +8E&+BZ8-!U@M5CZCE4FdF!"Y[(U(QYJS9KAmYjEF!JD%-`95F,@KhK3Z3!)4XC,i +aIfEcMFhcCjYLGj`YUCA#*550J*6jQPS(Z$6+TJL@$q@9F9+,4dHiU+,@VK`"4Fa +AR[N+%bke!TSfY)I#*85639RQ5fSEi&)cQp++LkC-,5SZ#!-$%&$8eQ()!%HdLe! +lp((3GeC4FAjd6'94FB9MQ6e`YUF1#KG8-4bfc4I8NA%"mA5N!fP)G%a*AZR-Q38 +PP5%6&mH`1DQF8aSY,LSTU)K@PVTATBklYr(ee0,Lr),bD0(8F1mb*eC5k6"h`0Q +*XK8Z5F`EU62E!69#R@PIkV)YUBAiPke1N3dA*+)&q8@9TH8G(ETXR([5AJUA*,V +2D@r'"HMfTMkdVjdNbJV++qHL419j"If,bU-pSXd(9461M"88pqKI-,ZJZ,6-&Va +(BZ6S84QZeASdGaMh`YQ&pP'i*$&q4PdC&f$XLJjY(UdSU%5,K+eS+jK%fLVabN( +C"fFhkUj`5G,)TqB60TpD'X%f5+JlcKkdVm*&L[#q"XLT*qfRF2%'a90qE$ib(rV +QBkVI-9B`Zl)8Dkq5@FA&EXCE4'haXIR)NYH(GVMC,c(FG+,pKIB(L&l8@q%LjI( +094rUUh""6DU&'kaqjJ0&r4+!)qCpkQrqj10D[f2&h)V5iY*B[J@CQ-PLa88BJ2U +6f'8GYPcQ2FVaF8Ra,8NTbL%Z$3H3!$5c)6!ErSh"a2c*pX!(YQ"pA6jEkRF$mbj +9ckCUXd)D!iEH1!I3!3SAb88lCS,G"p)JK8YBceSB#!E6%)8,kQIMMFdl005X$h# +e6$c&dQ*LJR24q"!R0"6JKTPeLSDj%Y5QhFcD`+cG1IH*`E*'c(TEYL&9CAXl-'p +69MDPPKG8cSi9KrH$BX@+"VNFQD$c0B&CXk1d"q!FEYj50*a5*L",-h4X,Sd`EjS +h&!+b2mLR'3D0!fQN`L9%dSbDQpF$mrUrS4hTKS,Qe-+mjTRAL"@0)'mNRV5QGZC +9'Q9HmA%&MB"F#mV,Se9Gl@K%d5J(B3m-'+-M0"T$@5dh9i$l,Hm2L+&jmd0k-kr +BbjZfMGq`NEGX6II&1FDmV'K-&H@pC&jNmj,0mk,0ml+P),c-a1i0NrB,G"#00Ar +%$$11aT[Rc@Vc(1-4TVb$3#h2QPAQ'I1dHFSmDCi`M`IQfCfp'CK9[ipHJARQhmM +)0dr[iYE!2,@YE`2cj0qDMmd6YX#2fpU1*8NaMp%%$jHd$*U!X42$RKhQkf%%ENV +02C5E-e$[Y#cc+1eR(M%VD5*0-Jrl#2p+mSrBrPPTHf1L[8bL9,2#2'3H0!qBqfQ +dEeE%1GJh$qhLlX!mZ+1J$ra1P!(3rEAN0Fc$P0efh45lVHJd28+Fmd,ZbTU8dGD +qS"RYb-YjD8cZSc1i-9AIBrTlGFD69jLC3H2Y1ZBq1KJ8F!JM!#BpZ'SG-ePS-Pi +H5M'&5cJ5f5A%&-T6Z)3-EbRPAXShp`5i0XNQ05[F+#8k2VjJLVHmN!!PU`*cYk+ +#+Vkq+c"h8HeX5LmXR9P3&UXXG*%4L)LjfcEC2EBZHBQk&0*8SDN!-Sd+&5la5FU +M)TUZF%RXI-bGRVR6dZld+Uah"1D1N!#cd$@cbNYX!m9$k#%"0+%C"2+HBHFEZbS +TS*P8SR$a$J`e"2P85Q9dQ-*9l$Bp`%K36TM!bPejl*B#GEB$q#bDVA!*"cP&pFc +Y0-IFjZ-D*jAmdV`U8XQ+$h*ch##RU*DjP3lhF8RahF[$hBLBLCA5A,0Fd9b5[(# +DU8P(d*%+Pq4dFdYJE[QjSa3Gk4S%fbTcXfpZ"VkmbX-Vf5bhRADE(4YQicb+MPD +iK-@VMbRZ*R1MZF%h0k&i`iD0'$NX0U-JTlam'0DM@+M1*eY)0MGD#$GB#%IM2-C +FVqJB"k%*TU$Vc,AQ'YpF&iI3VlKLK+ZrhFNQ)&aV)8!K)qCkf`899GZCUp2-e@K +)RVa)S32$3FTQ[SVQQ@8qVVm18[1U''"TB*D'r)P--bZQKIGK&G2%,,0i$h0Gh"j +p2Pr3Q%,(dR%+Pl#+l6(+ANR(d`)I9dITq9!)a5TQ1!V#jr!H6c)PK,%[R8!R+Pc +LmHjd%TfXF)R(Zp)TY&$K%SphS92T0)9,2,iAR8jR+&cLmFjd*U(qCbELRHJX1P[ +K%VB*pNYd6S61XI["dGMDK'1$(4BKdF#-TqKXe`FGU+1j)M"Al15)43i2GNGdES6 +1aHD`G[0SHDbSSL!rA!HKCdV,Hf#cCd'Gi8"eTMh0%YmX!D'!8D1+6R2[pU+pcH, +!,2jEdbPDk(*Ja@-ZpmhPbG`RZhGGUCZjc$HA*GqGk*D*hE'K1FpFUZJm9bJE2cm +#'F[Ze'48BHNX9(e30+q`)'p'@@P4I*d@0J2f,RBbZ06Z(aG!5A)*A8!AqVLQq&- +U5m#b&lK0FNr`fN9#&b(G(qKLK8Z)%mXJZL4#PpMGl2L#q'E[BTbA'L5je#AC$fZ +Hbb*dQGe$$mHBB-RXBJ[i3J$q!eeZ,[*a6I(cTaCMrhLj5pS2+jA&%9Um&9QUZFM +#1`lR%R1K)R5@A8`-!cGG39HD#fLT3N$f`q3c$12V-VT+i5,&Q2H'B9P`Y6PIdG8 +N*@(1dA30ADY`iFk)Mk$4jMcIR'I,APL%+PbEb$Z#VM2R+VV1j8(FR11EFfcr944 +M5$MAGJr83SUZFR8EM3(lqJKG[l9ZLUjd-pP%1S4ZS"X9,Q%Z1e@IlCQcEBSEUaM +jV-#FpBqC$(9'ZU8183be[iPZ9VL%L')da5cbc#*M&0eFKHM-`*bj#p'&&P&*e6, +mPNbk4CUN*iB4iV9f)[XI!EjqB`IPf#fL(H"#B#YXIpKpHDI4-k*BFrk8mHE"`B# +qrHdQ-2@@1[e+m`['aXV,LdV,Si2kjd"&dE@Mfr,plbreLjXq$P+YT-Y@)LlUUr9 +MNVV$#LV,5qF8P-qSL'j&M,8"&(1TYq"FL+VeV5TDMkU%@d2EF[IipmT9"reF%jY +DjZCc$h0PGb`JCS(%,iZ-+)a9&%6hM%k!p[,[TF`BA$5cG89dK!8l,'B(hahIVdh +P)hGd8-Qd`TK$rYqY3hl(8Idk$FSGPR0!(pXh3RD$YYaJ'PY1E$9b8l%ZZ&A4VBj +hTY)dFhTJ6Yr&1fGB&$'FYp(Y#TIN6ZidFbUEdbbeRQT6h)lc$VT6i4,ZA&+`XlU +,X$5jUfVRXY#FBNi1c-*`@@6A3Z'#2VqS2)`kU3q@i'+JeK&cXQ@mZa-,P+CdMj! +!P9RF5rFTA+!BG0rp3[FMfdRQa-#F&!k(i6,GSR+4%0'*YNEhiAb!(P5i*"Ic*p" +$M!Yff!pKBcSS1L#H-JSj35aD'-ZE%Df`)SCBCA4@48&jKC-`i$%fIeC4%mf,PEM +eI6L`!6Cf4UAP@(4Ba!rLA%%2+eb5L"I35XB&L&GLJG[(V[qR3+b!14Di5UC&5i' +Mh#,HVQ,BUP3&T54KGZ`B&cBmM2-4HP6K%Qjpl,,N-AVF(+p`$A[)3dFq)I3%%Me +*6bPFf,+@eHFmVHKTa`L)QZ-#!pRY[cF52BAdcjKM&6hM#+8D8+e5Y-UPKm,'c!q +`'2QP$iqe6Ieie5CNRMR'(1fEHEXf)@b1XA4`Y+@DiffT(mAjV$P+dE0Zi98$j2! +FV9DiK!0A(@aZRUFr+Pc#l9-Gk)&H-0Jf[H!'4KYrd4bKk%8hD0Vi5i50mdYZ['T +!MFaFhm`0Yd`PjBTH6Z4Y3+qB`a@pNX$9J&ieFa5pkMBYL*[Cp*UCjH0D[f0"V,a +LkY6mmUTk4LQ'Z+,A,20D2(A&31A+CSkp306-jJKl1G)f#4DmLG%3DqI+R@Z2eDj +S0E"5Hcf6AZHhrdH#QA"BRhkMYXq%GXP%DA&0c0mchPbamPR-K$EA`DN$LNSJpIP +rm,R$i6HpYHq+!F2lMl59L-q%0Al-SNE0VDJXQ!N'Ym)LC!@a!6dUe5JKhYQHS8H +m,2qc`B6hDi[mh`N[R1R-8CB%lm6j"VfTF%Q1C"@QR!dDP!h+,r3QcVGSMF)P(-R +5XC*mQpBUA*)Mf@'dcT3&Z2j6k,)1B0DE8NAVUhLT*$!P9$qEI*F6j&#CL!e&6!` +5LF'JTQKYe5,m(D&h!1CGfU"`5Fjal`QpKf`c6A&JC[iZ`a#$MK,DJ202p,l#*Ej +2VdiId)F+PlJb+Bdq-M-8IH6@cMEq-AfLF)Nh3$8ch45C`X"-rpG-LJi58fKVq8P +56@LQq@CDFKQ0!L[kX+SpTJCQkLmD!ASIYIZ82P1iK,AcNIYcfQJ[)3!IUSS#(2r +5!N*IRJ#F(jMm%$$QaVc5@9$8f8Jr'a(k$)#rS#m9,L(J$+JG[k+[&5lK&*3"CFQ +Ik4Z&LpH9V8bUSFQMEfQ6MbY+A$S&3S9[(9K)3Xf8`%cj9h%fZ8DXLdRT1`0"aRG +ZNV,almfKLVjhf'hm"i,bp!Hh`UZ25@UbEbB$Gh&"#9DZQahZZP6I("+B3hD4$K[ +i4,#*f9lmaQ@&a0%F(*L$Gf99p(99'dd+c#4UN!"03A`6'bC$F)30KX4'Ak+CYT! +!9VKiij!!X`P@*-C-C)#'6YJbA8HDa#cS)m(08e"SKPhANETa#PG6R1)SU#1@b"- +ieB`2c)6rP+)`C+@FCXBT6R2,p%kd*kGcGFATVRaBITZaJ4Rlbl6)8!VE20R8f4` +8Q)2#a3mi`R9#'(+-,`CP&6-HEF69UPTM6'$'*(+k*9%B'KQ'8'H8acHM&IYZlGX +G64"`K"9R+!lFfVFlpH4-cP+FkEJ!Dh#ZB8BTVZ&J)Qj'"QEN6TKX-)XUKUq$C@& +)(mf"RX(+3c%84,EeHP%1ea5'#*aVF@h&Y9cVpD+qA)IV+UlMJ%,6D%B%CX62M+N +B!ZC%hA-$Nr[2QB3KJ1CkCVMLHQiedJpeU+qiIQ*ChYm-m`c8*f+'i`+0Mr"Zh%! +aC-Zf-3E5%'l)M43hG,LK864$!c0dCb-SEZ5J$D,"CSKRKKJMh!#S'T["#TUTX"Q +'dfKZ)Y`%cjTb9(&6e`c$k8"ZaXd9ajA0`bRA$2)-e)D+QeI9I@"J"[kVlT!!9(! +,Fi$L&UlZ)`&jGm@l1f3M)4SFi*N"+)ij`"BI8JA`J5[$@""%5fkP'"TS#0)3Rm# +YZBeL+*aY$FE51*-6Q*bG2GV'JE6#[rkHk@r,ekUUI2d#dqqAJFL-YX+eL6XC8&N +cNB4JXQpJq[iq%QKd`4lF9M&%dlDd8kL!fh&laHeFL5"!-hdmdmH@U(e9LAS("KB +U[hGB@f$VB(STlT!!h1*b4m8G(6+laGhI-rY$"QjkfEPi$G*eiQc&SB6Dl3Ef-ch +Cl'G()rLY#%-(cCej6i9a+p`0`#U!pq+p&Hp9Y4[BPlZB(S(CPjTR8qCKX`V+jf+ +29eTQ9j!!&I%(f*EQZJI#8$[c2UDliRfUUYXY-0h#X3YQ$BR&JSdPaE$G,52dX)b +`Gha$)!pb9q'Z!010!@DVm,QVfBG09e[#I@`*)AcQ(Vb[BJLI%l[9,S`UGd%jZ5H +@JVY&qe6-L-DQP-kUM-ka'eblDV9E$@5d##!ejIeiIm@JAkGF9QC[lX@pIE2hVe) +TKL,(+RT3#ZjMpP+-TSlVplK[K2Y#r0TLV-8EEf$XDdZh#pDhbAVf`YiHjM'HfC2 +lFArIl*Q3!29`2i[5'MfNFBlTV"LUD#[cXI%"`P!cm`%m82%"5EF'(K6K3@MPe0$ +C`#Pp'#TR(Xa$&()Rr"KiD!3dNNR9R5Y%89`8a%0`$M2J$BLCicS6(KlKi5K#5Pq +XTF-X"PS&0TM58lNrUYD*Fde(hh4+D)-B%QZE1jfUmiJ)MpL@1pA!R%-BJR)qN!" +(+Mi`XBQSaD-B8q-S0a&Q8!dHBcSSKJ,BE5CUm%%m9[&"EMZ3!!N9B([IY%m)JAL +XHfIRr(DqDCF8rR5`[$+kLSdJa'dEXVJPJmV5X!YGF(4TU&%@(SRDM12aLXFjhUP +0YFdHTJfE2@c9VHd9MmFjJ5FURZ"iakCScC2BY,Dm-`Rc3X1Y['0Aq,(b203*4$# +Vh1RkH5,1JrN3KI9Nb$beB624LLIcSEjTp4[c30TUQ3H''a`c,4A(A,ICq*3)6m% +qS8h)2"JNmJSU+L"Pq6[e@-&#R(jDfNBq&)fm1qGa[Qpf6p*2RU1IZY53!!Y-#`@ +a3%Jr0Mj9'&T@RXEB+%ac&DK,pEJS`NABJ+5&kp!4S#pEDR3q6qFCLU&SYBRU8Ad +ZMV#YSVG20fK@E4*FHDE"1$V6*B%9#*G%Z'3(mc5h"B2j8bVRSf$0Z04%IG-Xb6b +P,MFF3,JXJVhLGZD"ji[`)6J2ih,&KcRQD8kYZB+4'J4SQDFjYH4CTURL@BjjE(` +fce%mfc&-#fTTQ[LQ5C*jjVKh9LrIf$H0Nmc6e$*2C4Ac0!T-)dV2TK4,"EB5j6J +2jlQ+3i@XYHjSBaUD"Q`DfKShX#NJ)H%Mq%M&4cLHX5Pfik2Bl'CjjUJIH3B$Bf9 +4AK30Ae%B+im,l2P)R%Ic-BU2GJZC0R3+cq2jTMiIUhJH#3Cb[1R1ar(aLL(DXhZ +)0Y5"&r!*LZ%GCqX(9a&6,c$eYYC2m3N*PApl2M(#*rlBpBSK*V9BXUN,Rm3R+ck +TDZp80c"eYf1"EY*Lk3`EMP-LI-Uf[P93'S5&kdR(mN)qeG6Kda4$ffA"pD3qI$U +ISIKd"`j#4e-l-,9rShM&N!"q1X9MEcic`QIq0!FT2Y@0S[dTaSZ%&k'(cZ+c&Cr +PZ,Sr665eq"`qecHeIZ2UFaaApkF$q$a68r&j9I(c63h&jlY#f2J&%Ei!CKrCrIp +1eU15V6dUE1eSXPaa$SF5JNe0bi[RJKHcq%+'@M-V5H3A1L)I5+2i$bC6-A5KPXK +Yr'*K+%(j%Vj8m5@Z((B0G9Q%,`[Rb"#jSj*,F9l1LaA(aCp$D$J[LI!50fp@8Fj +LR&FBl2ZZ5'b@$Z3V)hcP$LU(U4)EH%LPmN8SQH+P*Z*$$jQJmU9*S5B[Lr#b(93 +1VblKXh&HC3,&9cNURi4Q[CU[83ceU+Ab565CVc@qBUK%,CAEq(9m[H+i5[4JQQb +Uq`C13h%U[pkpQd3(Qh6I`0NY6Z@qC34)8"08$PHRY(#RP&mDCeT%A!1)#@aYkpL +-%"*E$XN(EpdJI!1UG52IT2K'ab(jG+4*jC[j&YqNrXBK0c[1b+GT[0aJEEbm+Rk +V59%-)CpY&"Zr,F+hBDRCrIp`##aRh,SJ6LC@0'1XPT0[3@GkI$[Ii4XX$H*N!Tf +I*C0#UZ!lM5L'@B%P%aZr5rJZe2PZ[NFaG(Zf,S8dJqq0m,er)4-)qIJq[PraI5l +2$#VK"b,m`&r)"-SqIY"JBrDJbe0#Kr&$%AjS"jRB6Cfae-"hS'43V'VM3bQ6)*- +9,RFPcH'()rc`GM,4b#0m%mk9@LYHkFMN+(6Y)rbSiNFFQ4a&mrJa[8AaBij-E2a +aIN,aiiidMUCjHV1[0bI*j!Rh$SB%qJGIrj!!)"-0hbh&MbE*4(mIk1pr)41YE@h +K"URi@&H@"CMHRZ5R&%1kCbZkJ%lLTb2mp)lZ8[a8&F$[![hG,`$j'*6p'9kPq"N +hK#kNKAU6rTBe,+aB@c-VASAc@Aj1-E4fGJLe+ElKeDcKBaAKe6mZf3CJm`#+'9j +DdJ%Kem)3jI(cr%I&clY*EL'pcbr`LrV2r**L+2+XVH0#L$"IjPF83fRRiPIcUrb +DiPFGfbbN-rPerEALejfHa-EId&mTK[M*6S)frLDrTIK09rQ&G+Vq-Y"IEKmchh* +0FbUG`@XL['BE$E$qbPkqYQPHFj!!ckBVq@hpKH+h(@3EAkXh+PlVTQBEAmIV&8- +BC8Y`0Ph!lr#lLYpa*6LEcY'I"rVchiG4#+"X8FkKmhP$K$IX@0iTAZr!3B$2lr' +I&,rR`%(HVcm,p'HrJrZ6!hFaAFE[4rMp(4@(R4CVq(JTMXXCVk2lq!2pU@+STfb +&EIa$rNMaKki)ep(0r$&rS[KM9i6Vk(Vp5D!rqCPH&-Ge8YI66IaTK$rG`Hf+)FU +bX'kPZrJcrPcaC`l@VA5ErMM3(qq%"B'8KA8EhFNE)laa4eAKLkAi46GlVD5er)8 +`p%lm*B-X[R5cedTkAAr%Ar1IIIh4El2Aefl@@NQ2m6Ik3m@3!"0Ci$EqEB5rh6Q +[@aDF$DQ`ABXR(-qfM9JD*PZT$*Q9rS!hmAHqrL!jAB9k+4m#f6rbprTpaGqlkFV +'Ia$q!CAFc"K$))HbPALFRQ)GB@eAD8QqhB)6`j!!BJa#0XP6Y%Si)PDDP,D0Q-0 +6p*m8)Q'L9E4D[)M!8@RVE+@YbCCqhaEX1a6X28R4'haYREV#f3UZUQ(Z&qKPU4D +4DMYQU`ff(T!!PdQUIPF*Y%Zf`GkJYC)Qk8SJBE+ce4Zd4UVVGj6!J0$19MEZ5k$ +%Gc28Ql4'VrIeqX4X*B&l"r'MAZIVGFRCkKfd2TaHNk22fN#[T3bi&1@A6NA0F8A +T4EpVU`S$2X8[ZB+m3qp,4*35b)GX,Gq"&$%M)KNr0S`5Z$iQX,dGk,IrLShrL&T +R5TB5Ufe#dJrS!le'[m9kMHdLDfm&BeQ4'P*6#A`6%bRHe'q`YMCBfYTJ#B4,8NY +U+kRP#1X$,YD[5afTkq[AIb8XJBQL&B9p`"1PRY4A8Lm4(bql53-P%"Ujq"KT+)f +80%c%$j6'dN3*l!pGI*JdPDJ5D)9FI*!!0*2Q5Z!9lH)jdN*f9`*[3aI[)bfPP4+ +)H&am2fNYEC5d6X5lbak#qN!Ciq*GT*fd9p)Z%HmX(D5M%PMrZAJ(k56C5L"IFI% +pT,2XU34D&4G[,A[*hNTJY1IL,D5,l+-%#K3AEbTGTCX5L%KF[+&dPaj+S"KamGe +NAqQT"2S2&kmMqmRq#Z0)2&j$HNP[*D"V&eI54rVLNSJ(dNrk+m'ZdmA6*%F'+-P +*a$dj3!BU6&%Z6NB'b@!PD'BArd''b&!Pd&Ziq,Fb6)BV'CD)Ib@j-N*"h"k2Ibi +(bNJPF(#c"28"I5bM)M)+JXdk)`YLqA1V4SpC&GC"`0%9[0`XAAe-RqRAI2eD`Sj +'4VJCih2k8NEV9jA%"4+)keGmr8Sm$qYA,BMK$[*Ap)f-LFJBDbFDaCF3YS4#'L9 +$(59r5prVPhhp-L"8aUB!+L3RpYd2T29,[RiTq@kJHfGBp)ZqIM(jEN$iMMe1e5r +iqSAN1qLkl,Xdp[8IF56I1Gmr+c(8c`IkqImBKq(YE!Z2C9+@(+4A+i'Fa'C(A$r +RkqH5KBF(V4+SU9"iVX'eC@a%aN+480-@(S+R+Qe&[2S`jlE9UF2epE1qIMCCR4i +1(c4qHP@J9feE4NJSDr+KpfXLir3c5XDjA)MVThhpG,)@cpKN%&6C@M6PjM)q)Z- +6Y4KD9$,$Y[Y-'*F8afX"&C@Y43YZTCrbp92*@ZcTm%&ETjm-p*1r$0F#ACLYbal +FALES*j4-F(N4eirlq[&NAH"9Tm6TclJ$Cm[%#24l'C3HpNLX-Pi0k0aX06Vch[S +aAcq@V!Dm4qfl,Ya02qVV4j2[S1DcllTc6rf)Vap*[S1NcllEMh[VPEjHQA`(LD& +peiIlkiGprA$b(45-pPd1$p3VI'dplGblU(XhL)IUKhcp82)Gr'(YZf%m3MrSk`H +6lk#[Y1m1j0(k!9mrN!"m"e0Dqfi-Mp2hqrVqj,[3[0ERm6a"*ZRlP%abhB1i[MI +3prid-E+'HefUe-9NIBmF,)IiqTl%+N"JA'#a61*mQDc[9M)jA!@%m80&i'`R-CQ +La!Q!m'bbj%@JFXH5T9qX*!q@!q'!2`9R[Vj,5El,-jQR5%&%#Uc28blQ$cX-hf8 +R6hM@TFSK+-5G-PAIiHXlNb2q9*HjJ!YP@N5Q9@91eAGBL*!![8UK&#NT$"Gm2*1 +2NHNb3pqZEe1`SV9,(VbTP'+CUHc)Lk%Im6)TdEFU62afk!rMT9+Q""SHfeNPA+D +AqhTjFZJ[FqpQFSQqaGHh*)Iq@bh010%3Ph'&("D4`hEdJ*)CS@L5jr!48Ui`mSG +p0)IRkTY&hf`Ei(EE!0C&$P*)N3S"ALX)`[*L(Xr60qNE@GpN8eLV1"MSLFb5f8U +J%E)Z%r2iH*NMKbZ"&XJ#QmI(kKY%`e&1bH&9'i2V!hhp2da""ICe-PH18!,9$rD +X%1!Xd0I*NDb[`jj9MS4(F02S+$$[V,*`R45hFil'eCK`J3Q(2K&SKH3S19V*8Di +C&d"qISc-dpIUDj4Jhi[&(V#G*r2P@!@Er,!j&r!TFT`FVq3i4eF3rqLV!hhelq2 +8mDlV6Z#6C8&%&[a94`1rr"$IDAb@R#!R+S&Sb1)lM8rA9`AkUYraRHM`R3i0cNN +41@Ql,%KJMSF9(j3Z@q4NNC04p90NSC*6A08[j([N9$P0,p0,PF#AcPEp3Vj@6TF +cP-#AcPEP3Vj8cT4&5Xjd9B&S4PmCk#ZhMfR18ii[iN[NV)LFp4HCNjcKX#cQCA+ +fR+2%HG8K[N4I%HJVYQ-jaf&C`N[Ph)LFZk1HTcNbZB([N!$cj(`Pd!TKp%Im0VP +!,P3#LcF,l!Dq85m*p**rf)S,e$k@8@rN@q8LqB15LecQQrK@[6M3cY%1hN8`d3l +[)dEd8`*&N!$0!B@+[Mc3Pqp5l#U"DLR"DCF&qV*rF*U'15(VTCD9)0L5Lq85*4H +lrVbAhj4,j6*pUFBE#)YXIpl,cmMPXPM*jDkRlZ@(C)PFS@5*+mkpI*qq10!AEqm +TTc'#P14"Z6)L9fl9&#U"%-RL@-P2b&*CTJ5D)SYM*6qLra$S2fc(XFcKH)3IPkX +LFY@1hVc-pHCcr+TF,D"4+)aXEcl(,mZeFTf5Daf`jhLe[LM3&qhUcHYFEklQPq4 +kZ8%*0%Ffmr2mNVi`d"H5lhV6PK#ii8"RAkrQjr8&JElJYkkX%LMTm`0prUkZK)d +JkdYX9ek#mdCpRT)EA9Hq"6+p5@l@jfS`'53aYL[IiNrP&PQZj"EA6@raHVP9-0J +j"c(%eqLc!hhfcSA#EDkce[!kZ6dLYrmS!K$SQ5bX$IbKh#&h+VR$`GV!lqQc!Rh +@6PL3!"eC@1ra"h*A41lDdE!36GQ'hFMIbGebMj+lAF0Zj'rPAX&B$[F`#h)MIk% +A"AV4VSD&BjKYf#ri'lPI(P"b[m[m*33"C`Ekc,!m&B9a1N8%!e")U!qi2&r`PrU +-3*ra@q[H8p@kT`IDHE(pHqYD)d0pMTeBcl-Af)kbYVDM!ZG5H9!H8J+r-8b2f1Z +,2Ne@X)Br@d4@K#UGVG0M+%D)aSAlEQjm#1I$XP)*PLMJ!J#i4ak44r@T'P-99&$ +@ZF&ZT4q6aj9!"B@Q46a0RT!!*j9!lB3+)ZlT8`*pbZpcR4-[L5HTmP4%R[T4mL+ +21h!q&ZP2#cCMcTJ-m8#IM10hF(!PXq!#bC!!94&CYB-A),`#X8TG8-fc)P!dbA1 +#25!fIlENGE%dIPlqU%r5'&9K(@C,AKH,l"IN4583+YQUe-ALr#9j@3QX`Qa9kNS +pI8+J6pJq6,cXDP"2'XJV%APPqe!#bc',S`N@k+r+DdTHG6LD5&1p)0!,YZ1!rEE +&d95DbqX4HAe(,5%eX#C2EE%*Id2H9!)P&$J1mFlbPUa4!V@6"GC@fZRM!hhm,Sk +$@`3i$K[jE(PEeLTjff9Z,pRkZ%!I4p&Xr1a!U#kVX'eU5aQ2Kff,Z",B1pQFf1V +VB`0pl'pm&pGS@EkE(qMjZrMZ*%Y`eVP29Z0F*qZ93'f&[BadNfjkRMk'p6bE`[V +$#D45mSl'jZ-GemlGd1A[bJCpP$j5bEZZREYK$rqH32)#TbREKYf`phpI2P$b[UY +I0qQZM`Md%G[Em!2AKYfPTh`BN3prfJ(!I-TLk3d0`%IbXC+2(*EHdNI2$I6Fl9J +JRE*BqNKrq53LRqaSk3f1RSE+F[P8",j3mTPmVZ3c9mqK-P-fbKIkF$e(b8CAck& +B'RiTAbQ"(j'Y`9$S",k@2bYaEND)$p1c!celjaMeCeH2B6*#[SR)0hqCcU$GXE" +'bhMj9MBTFIC2L)r4X`)pDbHX63l@'"NRhdANZaePG[j6@')@bIIbJa*i3&RQ1P5 +Qb@ECSX4j25%Hdj@"VYc&A*!!1PRQLXP8d@+8D*Gj#MDB&B'ZF-`9PL4N,MZMaH- +KFi@6'P450LGfUESmd1@r-4IFU4,-G9LJ$p[&A0E,6m296q4c6$SBF"9qLLCXia) +jda22df@k9(N`KE*YA#,c["5[Q[,`(aQfhdUN`N[edT5AkST6)U@k*0!P[r5EKer +*X2e@+Z9HHX4,re%2kZ'[0#bXfA+N9phcPHG-S"#ISfF'HZC1@&"J@9Kcj!J[L(M +"pMEfi29NfrJi1G@,H%VK2cV#0Mj16[%b[%cPi8Fd,%JX&R9aS)[rdFBHa&+fMBr +(chKNH6@8"fFRQhQ"R+aR"(V'A`B3emC9!iKYBmqC4`($!Mdpd00rE@0[UikV+0" +&ZpSBhR5X,5i22PjH69fS[*UZM4I*FUq@9eY2de19"i-TfmD,C)PAakZV21L`E,m +YN[1mHPjpjF%2bS*B*'ITJN!Ar#3$8*lcL*+cj&a[YiLhfeC"Rr,U1M3AbU9H!kq +KmL!qX'JZP)YdIU$criB'"P)@c89bLGFSiMADdEGBepZqA5ShHSfp*XTcSLr%VrH +DHP(P0AA`X$,6HB(1fp@h@$RE[PdQehR0[1E+Jj@8cAb9A+HR"(U+m`#09BC0'dE +#GP@HmjG#hUYd,0#ahhS9bSa%VaiDk%0hpHSdfkZ`e44Y$6Bd6"*C`dK60)a$4F1 +MP[9#5mL3!(Kl,EcGPHIqc!-ESA[eC(d)kmNfa5'ffI&R(Pj,VjAb@SE+!D5ihfZ +Y21G*KHKpqZ"!(rb2@RMi'3q[MCkN22b-KmAiJ$bJ*qS*V1&aaGVbKjk%#q-R0,` +pp(MP34NAGcR5ir4BeM"IBch@jS%$@aBXU92e39jEVjhAhZ[JG@50[q[*mYT5LKk +M4qY4HQ5Jarc,25l3Shmh13cdU(rqai%HqGpZ8"PH1rbDJMk3!2,d#*hViGq%p2! +!iAr@+%0$RCDLKhQG23d6SJb[-c6RHQLJKrjLT+MK!*[PGB*`CBJHV!F&HXM2Q3) +pH+I0FD!(rFhT2F[,KP[0,AUJ2N!2d$Qk[qkRqqSq%'ZIUh[6qESA208[dr[6BVf +IlURhe6hSqM3pd0RpqrU!q%m3q(T!h#(5ecQlIU2!erehrDH!Vr[YqJ@"32Ip@rd +%Y5)ITA+L,4m&Fq)L(d9ckKj2liH)lVP,9ZVVIH0U'am9GRUF'RSi&Fh)@CklVLB +G8TZDV#pTqmlLGe[CIhK)b@f,@lA`PPSkPRVK(b8SrF2&,6pUq8c,+h!qdr+6PTp +qpRRal"KPaDK'M'VL&f0b*e2Y'09T(i128'0+RjkcI$a9+mTC2LCh5@CQKYIHNNP +hZPYhdefp2Efpp$i"`VmlGZTZPMrK&jEP3IfQZ`5kbmr8N!$Pl3@#h9[[TIH%0Vb +ccJldhMml',1'AjFJ$aUVmbk6AepMka2D"pI3qe#d0P9INEXLFbcq'FRql%4KlVX +e+GM$YXYllA0Z'B-5Gl!PlN4VG8IG`G[Ekk,E"`Mr"aYRD*JrTqKfhMkHKXYFKJH +K)1QfJ@llUbZVKLe@PVFh+Vk(EU0EkeDkTGipd([XbKRS0MYCYIA2"RZ"E[@EBek +J@qldhJ[dlMYG$l1m,T5Z@qMQZTQ1kUDkL@kX'`@kaHpdi1[QF4S1G,2I$IGm(Bd +lR[UkDGb$e0I@p@P'D4kSZ$%D2km8$Y`PP399MCrPr[9$0rVAP&0$YkHLYY2E%De +["4*GQA0VlZ+*ldkD1'(PT!Q,0l6I-qHfh(V8pmU*lleE$"+G8GL3!&,D,PRD%Mr +%NCCc4qkbkD$)i!Vm+mIdc2&8GeQ4$Fp!Z!MKY#YQ0-QjG@,ZH,-UelkCBHQcSq@ +@KK"K0Y#lH9fpEVTqS"[qVcP1`dmb4GIcZRXD@i`-VlYPQ,U"KK2V2ecp0"bUXVb +ZB*JkZVDZT@[U'MSVd(9fj3cdIpJf"EV@eN9US'[q[,3+G)fI&L+"cYSjUfGjh@J +%pp@C9K6+`h8'*+"+4h5JICl#49aXTBKm)TqLUr1CI!&I"[h@PEb-Ei-Hk3&qL"r +QTk&YfF$[mkG@Am)DHMY2UN&q-%URBdfD*Q1a2*dSH9+!rHCK@'UGC,Fm1P@Za2U +MQY`ZGqN8H8T@b5[bZ[EN3rP%[T([Y(MTAU$CfmeVT$&UC#BRYicNj'EGNR6NYmN +Y5%jZ@!'&NjZ[3mHMdP,3@1Ka&!k'DA%&Dk"6rkB2#c4m(EBUN`+GmV2`*G$HImN +&!Sfr,2VADKE9rkr0A`#6R(rI-pA3pH@KGH[IHAI$c0R[rHRp#+AR2[94Hp$YaqX +rqI3c[2NmqHE@h#X`M&V,CGcKFNie@c@Q`8YLpPG`U(Q-DUr(E)V40%EeN!!A#kY +NjY['8p-BlBEh$@,8-%D0`[H0Yle2Le%6%$U3!$A&@UmH8%8Y8YbEK8MYLAGAaZ! +LKhHiYdLq#p([$R3YBi3(V80dEC,SEJr4lC&cHfk-fNkQGLhY'D2fZ(Iik9`5Ipd +aM&i4MhB+SeI'SpRrP[R[jp+@U%VR'1eCE%Zb9k)N-Ia#NChICf-8m3TcTmr'[)* ++GiPAI*piEde(0h50peZhq*Z-CBYMe,epbj`l`Y4if51HBGpY'ASQ-k"6pSYhc2l +E-[4+C!Lc!(A[12Sqm5a,JDP[(&ZrECRlEd1I%mm`)&%ci$`JMRIJYUb$YP9NF$c +$N!"Y'BBQ-bb0iIFdN!!"pq(E-Z4ZUeQBD9PQlSK-cc`2,E#")3'Ejr$R'*KY-Ap +&[RrlE8L4Gr`&RT311,QaGBYS"(Zm,2XEEiJYV-LV`"m&d2RfYpY`[f*dcMMB2G" +0&C9cl3,TmBUb#Sa6p%+rh2j`&k@hq`l[EreP[h'rhFELIVZ0-pa2Yh'$SVaLr-` +&Y`)KfrLHZ0[irS9BHH!qH05J#IKj$$jSl)K5kd-rKHBE'#a[Xkq(*jEpm!i#FLa +")BXS$0rp`FCUR@*Me@#3!*0mpm$@h,#NF$'i1Dk4dV1aDM2`qi1YH")6h)U5q@! +TNh`(2aIlR4r'-*mQBYKr*f29QQf0KAlr(ki)ka6q-*MllB"8H-mQkaIqi-$r"`! +!$3!19A"NBA4P)&"33b"8Bf`i,M"UC@0dF`#3#9Z8!*!2!9A4!!'5l`!!,`[rN!4 +"8&"-BA"XG#%!V5`iU+qS`VB!!"%@!*!'#K8!N!36(3#3#"+i$-$ikjKR[Y4M,cI ++RaB&S0G&1Ffkp9NMSjabY4`3CY[SEqPeQh@%%rjiDRLDXP[cbAV5iqFQa`PEYi4 +hK&pN`)pXmTaXXXPJXilX)jY'RNIfNprNpT(*5RK(0YRRdH18jbR[I!IbJ9d1BUe +m&1@r&J"3eEFTGlrN1,NYiC(Mj$C"e!BJ!(Tjf4%LMBJ#0P%YIZf4M)LP*Y2a4$b +jAic%%Q)mP4(fK#@qEXC#KY%AVpf6(M0Y5``0G60r5%Ih)60M$rH20!(k0rVCqrR +jZkjbN!#e'eJC9)4($STXDLS6Xi5CL8h%Tkf3!"$pYSLC56&U`B1Tj*J`lAE$U"Q +hdqhK-+T3GLUC-%Hc)IJ96Nq0KQhSQ$4MBF2S(aH(S%BN8UQ$l+0T`m*i9SbPBP1 +69Y*QYTJ`Tbf4cX#'#k2rXBVIADVbR#0*2*6XifE'4BVF4flRkZ(@)jX'TVT(8L) +cPA4NCf1CH0S@CMUGL-G-1jj+AUBFQ*c+fQ)mRN%96fCY-j%3AHKK$DYq)FMblPZ +N+VMM%Lcb8l4kUFr-MZhT'KEE3YXLr0H#j3l!&iedFQPXN!!"6$YBmXbYNa[-094 +PS0!2+0)DHCDUI5Jf4+KQmT!!E@Ef@hD@S4%(-ULDY0f1!2,S'!5GDXKVS0!Q&8d +MRjpmY*k-G$VfNa[MSaNcFbMdDr6"jmA[PmX'qCefl)hKKc(R8B$E$FFRi6JLS04 +3,R2A+[be&$"3k!RQBH++mK00&U&!IX)Y![L$Y0C!SDbX)SrmQ0Ej8$4%b)pabYM +T61U!&E0,k*!!JaUdcV'LLUVP4blj%@NkVB@Dp94RS&"UDXJRPqJm,YC&U$S4(af +2*b`&p!&Jqj3'6!*Ym0-'#2$YR)SRaX3H1jl)1YlAi6qIkJd8F*&MPZ6Ik3)0"FE +K![S'I8hX55+q9#Ca&#C-C)&pCdSJIUfXX&-1+q@NKNL0+pb*jF5BP4&aPA6L6M0 +T1jVVm9p)&aNS+TSrT!B0"63hd29dp@T6P[L[R)ScE*94DbaZTc)K4pe&q$I5a3D ++LVS2U&&$!A@0e%AA)e*6D5YM(q,0)@Eea$1LA66hCbFQ65[4hQ00@iP8QJe[,de +RYKh"[6d8D@pf0&k-Ae#6JD+LmAeUeP"!Bc1e8V2)@MC'4)dL1eK4fP*Q19+Dm'q +Lc3D+5Sbm*pr9j(XF)qpbLmhi,k%@!i8H"pq2B,Q8@Jd8V[j5bhGN3ElYPHp3IFL +dTZd8JM`jP8J3jF"qN!$Dm$4Ci2"kQb1hY4bjBGULdaD)D+1Y"JUGP`hX@A3CA@k +JJ#GVP153!$aM8+JXf#rr4Q(jPKGPI5Kl+*Y+T-`a&PPH2Xa%(,%F*YeCSccb6BT +i8ELp(&)'48K,JDFMhpl`b6Fq6c"G[X8cF)B0ZpcTaeDrlT1[8df%eNbT')1'VIL +hd48'#Rd3iaM!A&j*9aNSP*rV-ICAdc8'#[M(q%Ej9lT@[ZC$b8NmbV&BhLXFY,4 +Dk(3Ya'fAVaUdhE'JMLk3!+Gpm[3AfiJZAq8jHieYZfE&YPGmmK8+4UJUBpR6CN, +9Hmf%J390p3K38,lXNbqIXrB+r0I*P`bkMYbhS%X6CUUGVTF[bN8$J,i$iG1%K1L +J'``85NN60FY62RRUFl8h1%Y"-ff5*ehb*'N'mYCe%bLAdPCjJRE)iek8L"'%UjA +*L*@TGQ,%S"f1K#f)LdirG@*,@1rX&FKqc[dq%m-ljUa+alPiNFGiNC'Af0-@r&( +jJN(4PFKlAMkRbHHjch2Fj`@1)$!$3A*4PI`,8RZRr$0fQ'i0)2DT,R,,Cq@#2#D +ImFPR[jK&ReciDYAbb@1IKipA2[0PPYE56Y,GmQRUFD(3DkQ(hI+4iB*,@Lee8h9 +3rSPDj90bRRUT6clT"IcIE(k+ECpRDhZjk)2VFh*@2L%IPhqN6UqF+d@R9mjq'EN +qqF4UI[MNierPQ`rU2SZ*YI**LV3YM[)U($k!YEMhkF((eP&Y'c2Si&Cbp4lE-rL +lJpT'UYPbi24j0j0V)X#"!EVrij-R(i8GMD8M$1pT`cD@`eMj(L1'*bbf(q1X9LH +-ZKH@mjC4ILS8X#J50KARmA&$d)mlNA&(XFF39J25"[E+TE"VQX+Z5-I$MrkdVkA +amPh-L(6-r)K4%C*,[mM[&f'Ak)LPNhEA'9PXh[A`SmhhY66ZMSjlG[p-&[IVdeV +3e4+5l`rF0M!eX"FlNIrQ"m)G!i-Mr9#EQfPX[9hiaceT9bFGrF-GV@(rdHq,%H& +jDSEUQk*8Am)DJ$AXH5"F*firFYZ1h-KJDe)@9a30r%D1l+EpEDc-6#IXF%FfPVE +6660YV'lr1[$mi3lf0%dcE8IIhcH)MRCC!hJ0h*AjI6IepUQ*b29h$fa5b%2-Bk# +kDfMSaY,"3c&d4)`EjjLlF)kj0iJV!NiMZT[FZ,0i2"S&kbJRQLNDl5*4b1@#pe2 +8med5lRY*k&%+DS+#HT!!JQi2"6eZ%N(NYY!S&p@r66Rh24TjHU"M&fiZqccN(N0 +@2`DG'TqUS*[eZe"AjiN1)rfaIQUi9EK`)+R'-BH[3)e5jMUPc"q@FK(AUJ,q*Ia +&r'I"NH#H4BmLHLe9Jqp#(ab0mUKc`+'"G46a,e(eD`9br4Bk0HK9dHU%L&UYcVd +#"mM+#hcD)-"VmMmD&'C2V5S)N!"rGV9"J@ER!*BE-"hp+Je+p()$49GmT`(6Pdp +8'LMk#8F!0j!!%[6MqBU#XmH,S2[pj3E,Xr1JjbX0LV1"1G$*M`X90`K3IKCdFKV +kjkRJab0b&-*(0'#kiS11PNc2+clEc!e!Rm005p'9+k$2PZM+-Y"Rbh4D,M!pAk' +I+*kM(jpITE1$Uh4fm&4aK8k"r(`K-&ZKmdK`Nhm1m!Up2(@9&p9bYG'eSLQidh2 +DrB0l(Zb+0SRQB%rpICF%@m6'+1@dA#$IZ0KC1,b%h$NVj@(CHEDaf,M8@HKFh*K +A3F*r`0%N-91-qJ0+C#%rch-Cm2Y9*XM#[!U,j92,+L5@!mICZJ#2'@C8cGpF!Ed +#'%'dKUUj)VGHRPF`&HF9M"#UH&VbmNVRiUUZVk8,V@(mEm9DYj+##13UUPTrIqP ++baGDjfCE4hI6Gr4p1*fJeNlUEjEEVGlFYB@HQ8CH0"Ud4bLBM@@r"Hal[#6a!M, +FIdX[P$l#5`h`hirdlKZ"J+HcpL&fiBeX1MZ"qX2Z`CjHdM4pjd!2pK6Y3QHTd9U +FT8Dl`PPTY"ha@!+()feh2*CPI#pUaNFRaXG`d0'5haa+BEh9i)'kFC1fF-YPP*0 +bL1KLB$K[+Gi2JD'[`QD!EAGZiV8$`$cF$laI!G[XRQ$-mdYJNIAhUakDYY"G+2% +@`-0@Sl#hJ@&4Ne+3!1F$B,YBDah9V!9fUk2*blf21Ifm6H"Kc9*BLlCJjZJKK@d +$Ejq$qDi'liL$'GH"GdF*ZaYB`qYjjFGfB(j(4qfY`)Eq$`#3!`d!'&9`C'&dC5" +8Bf`J8fKPE'`J*L"6D%aTBJ#3"hL-!*!2!BKU!!'GS!!!,`[rN!4"8&"-BA"XG#% +!V5`iU+qS`d3!!"&B!*!'#N%!N!42``#3#2IC$-$9-Fpmq`EF+$GD&)"H(H@HT9q +$2%fZPJ01Vil3jdH[fk`MR2"If4UHTUbemrCkdMUkb5E(eLhKC"2qC-#2F2+m)jX +-q%HfC0-f1AjN(aPXF[[)C#@m)rYB6i[b-fmlL!hN!lH$2&YjAKlP[aB!80@hf@E +lb#E(b'd*Ma`RU0J%&4Y2!(63fH'*""%&RbF+i"82f0CBLab`TA@hE8f0TD8pBFR +akDQS(8Y-4H)aZhClFLaL@h)d'TFM%eBm,TY3ha$E*3hi')l`Ql*("NBE!*rlIR& +qrj818VP6aUDd`ejfQiE&Y"a2a-HXP%b-bjR$Date$U1c+4YDe,fD9B,89PZpP%V498 +HY+h#f10L(d*3&GIMSRUNfY%cKREYX5UMpSeL1PCT@kl0CRFL191CLSRG%j@fG-h ++MCXfA9%YZb"lYfk'jfbA&$k8h+Q,'CF*FMpf1eH2VheXcH"dpfK#TUDjHl'd6%G +6XD3Y)mPN2"D0m+KFaYf6Np0T@il(8UKL8fNlJQRTJSBeS[A369S9rXqBF%Z'qb, +TXHeG)h*MbmBfIJ0JZB2SM##$A)*EC!)6GaB8h3Dj`9a"C5B+BimQPDR691j$XE+ +0+LCRl%KUYf@R'4Te)*2+5@c6@L[)Bf!8$+SJViP#6'UD)*qII&4$J@3bqJ4QAip +mbkmKK-H,edqQLF+)XbM`J&Sb+H$S-ald8j!!3Z69fSJ04eZS*6CJiJfT[!N"E3# +pT8VeT8Q9MJ('Ur`BFM3J2I(%5'`53`3R440I3N,P@DF-EcA9Q#MJK#1*e"IUFk' +qB*h2@D)'lhQddN5K"k5F+Y3LRHp$8GG'ISaTbNkQ%RZXU&e!Kah8T21GGT564hh +Q8Tq4-'JPh065"5B+lFC,I[8TVI+KU'UMmRKXehJXEQQJ$i"*Ua`2ASc6KAkk%#h +fECf1aFINGMX@6cX$G3(H1PTYSN!A1D"*rBdZ%LJ`BaI4MA5ph$i968a1@P1f$Y" +i""PMhj@3!2(BP)A86MLXK*0'R+D-FfJAmM@QmeAH&CQb(FqVmGD60&'82(p#$3) +&2$I3CVVUM#KEr&Iq&F+mJ&TM-6Z"b0EZ*0j'@Q1L+,RlQ#i@+1$ZBZULc4*$Pl4 +5pJaDP)TD2E'8E*H0!qQ*bBJ9EqqapPVa4*)EhPkBcR5l(BeIdp,@$NrXF3hH5kM +*4&(bq&Hk9+#!adYT,6A+Y'9M423SFJG,6TZ+,-G+%pkeY-j%8BU4Me41U)mi4R) +XX3j[-kdh84Jam$Qm,U-0*JVA3%(b3h9+IH"9(e*Y5m6DDbH`)%a0aq0%'E!ISQC +m3ThLm2U!$@`S"RNVY4M%Np0+E5B+)k9T!GT)PjXSd*-9`2edKAVIT#Z+K[hU,h5 +PHXq,XVBP2C01a"14-6CCA#L``#(XVb6$@F!mkPfkbS[#lH@3!$,T+K)*(EIPkKf +IHZGFJKRU2Ck"plPKPcYkh1UhIHTYUQLM&G-kaZ#K$Hqhk'S6K6'%F3`L"+kK65B ++hFpU*%dlE6C4S(q-VeCrTJjedSH5NhJAaf*a@h(3`X*L8!I-AD[H-ZPDT`8eG)& +kdkIHr'V(-G4E2'FRZ@fEPp[fKNqp3D%f+NYCpYj)A0Fl)R'60MNDf&R8kcleqYR +@ASehLeS`D3ZjEi&+!fDdNm,UK$TZ!M#f)(`DG+TY09&S*`h8U)lje,&cEVFk5`% +58KeeUD-N6!U6kbC3,UAekNr8V9lcSN5-)&bY9%SZ6l86)bCe1aE@)Bpkr05$(D, +Df5Z3!2fFqhd4$1qBMMIe'KFRH)b2-l,!29f2YeHpDP,[FZ5pSPi@kKA@HCPeAZ8 +)!M-B`Mj@T[j)IA5G1S)GTPm!42leN9XG9LqT3qT&RcVme5cke%[IV&SqGHKFq(M +9LepRDB#Z)m1YAU!"&`TAJ!E3*qaC3DTbS8mL32e8(P,cY%%pV`l50VTHcAN"rcH +GF43aS)AQBN%1dIASqh2U@I@-1U$q3$eHp9`K2,hUfDp$ekHH1C-J2RAJQi6c`Gf +rJk*5c9&Emm)ZhUKEprK*p"iDHVU+!Xh-S$[ANk[hm2DKhpdT9P2&ZMdRclZCA"0 +"MJc3r9mF2riNfY&511$S6@dLNV,'j&iVaEHAiTBi20bY%b20HcS89cUl&'EIL`R +JYD2ikC6!jN[BNjb2VbL5IYVC3@)reL6#QN"LF)GDE(AYT9CA@mIM6rkXVkPq3cm +cfMTQ(f98YUM&AfChbeDAl)JQTqbZ8bVIf2riNihh0G9[#iplYMfPmVZ0[5,NDQT +4R`cH0MJpZ!0,N[rQ"eXl"SG'"q!f-eZrpRET(rFNACfdrm!GDe[pqaq@Sp,c`Lc +90S5TYS$9!D[ErQ"VMEcpXGZfC%D(eNkTr,+M`GqSd@fdZjQG4C*aZl8M(8hDbBE +CCRDhZ`SmIfX(pc4*Xmhl2pNj"%@lk!'m1PCPIYp0[Aek)M)$hB0V02))maJSlaS +H[U&`9p%-!iRMaLASENPdE`KR"9aJ$$HjFFlai0!9UU'-E+4`Z)YN,T-*h8pKc`p +)ZZmPDB3T*#5&M"#&h"i+HG`N3`D&TD"-f2JqCGch#2,d`%HrQc`l2H3H3hJr$Cq +#,dr`cIjGU-Zc42Z`#@#l&6L&Z("2+FI0L)p0p8TP1TA+lP0U!@Ha(0j&[(QmTm& +4i*k'4KjDLqAJZk#$fe3@G3Bi2,#220j&+Rml4klI`UH!Ajfd6SMSAH[XPq-!@Ik +#ra!)mKEqGi(F(2DCCB%JqHI1#14SlL$!SJ$6S9F5+0#,!TUZqBi!djH1P33drCK +MJ!@8![eSYZ6Jp0%mk(jr8@"TEKldE%NJ2aFm#$VjFIaLJ5"Pjd!R4p!r6cNr2L, +()IS)!DCV2ZL3!'4k9[1jc5`!qN'F`$4GG`AdZ3*GY`cdZ5+GPR*-cjESar*Rk8I +Rcp#jJfISh-%6q@8k"E2cZH"FLFiM`5,r(1"PHR(U5PpBC!,K5YN3fZSjkIl423p +eK4YNBkLRpVj,3NebGCJb)K2-eLpdj[BY)RG1+l92GCkZcpF[GZBk&eCRGC!!m"Y +d2#R-&+2qS$DCbmlcA!EpITd*+MH[`f,Ta*)1LDAJ8@jGN!$($$1UjqpJ$PT"M## +NiHTJRU@AjM9-qAN0)i4+25hdFUYcfY9RhX)Tf$6rYh&9,BFLmVQ-bUV["q,GZ8! +Z*$HrI2&ST6A'$mA(A)ZI'ka3@$#@VrRL5-pX2HmGGH)"#['Z"1b"G$6pAGj(H,G +"rC24hTfM%(iUEFq`qpqRNqN*e)HkKhTk`6ZqGE#(ej92RGe''-jZ)`,1CL0@aD* +a(*4%8bbDC[abe)a[Q4JI`k&(E"XCZ+8ApBj[$bH`r)TGP&&UQ(Yej"EXM[V1rMe +JNRR!ILb1p(@i*aMcR!*[Y2TqaJ)h!+YcH"85'#*3Bqh!B%FT549KF555`3+MDUM +L9[#`1E''&pVGKadpEabm@`Zml`$VTdFdaMhSF$"I"PMB`FalSCdVB!I!DhXhUr[ +a-$#2mjp!B"EB0BkR`+q!hI&r!!d!$99`C'&dC5"8Bf`i,M"PE'`J*L"6D%aTBJ# +3"lG*!*!2!C,[!!'SG3!!,`[rN!4"8&"-BA"XG#%!V5`iU+qS`r-!!"(%!*!'#Q8 +!N!3Z@3#3#&'+$-$ikjKR[MelZ9&ZY#LHAKIP4NZYFQ!dZGTPF[TGp2PTYeP(11' +8Vfc0ARCVjj(eT(9ZXXQaG8XifB3rf4*qC*0RK*00"TX9f8Hi4jj&pT%C2r+EV)6 +RN!!@V8[+"qP!2SXe@[Pb2+2me`lBUQqcl$TbR0b@m-Ka-Yd%04Y2!(63fH'*0#) ++0"2jm@Th,eUT2j+ZTJ2-4)Y'J&@HcJT@YJSba'aX6`k&VDJj%)Q[Efc5`HN2Tka +Yh31e`TI['rbElrIRGPeQ)iXIdqM`3A1`ELalB'8A"8c85Di08D8JlBHSUP"A5hh +`jUlXL&XK0He(XJGkEqkGq-93BR!mBF@'"`Flpm*4l`kL(R"(P)13!ehCiDm9ldP +&alk5*,T09DkcA@*m++96U[0QJT`("k9kB2A"PEd6Q`B5CQTLh,4'BfNc(8R&NTB +C6LEMX8MBLLA',c(h*5E-XBQdC3l(8UKLifNV()qEEG#)EP0kME#e226[-C'@r,% +cR"lDhVE0A0HiVNPH(eK12cUMN8i169TN!02f&"5G1MR"A%4P"JTpYb+9mcb9He! +XDk++XAe@1$85YG)#$GL3!%(PT28SV6*bk4J&R5V)ED$3aK40)iqA2,58M#[@ErR +GeYLZ9$LeVr&Kk1"air@5BD$3ik)*h-Gc"[PXHF(pA[+,I$)C15Z[mCai-2!'D,' +"3RP!GfN*j`eDBRX3I+NA$XiK[l4J@f`-Fi33+VV)LmCL[*AmT8'9YJX(TZ%FrX+ +!UR)Kq$)[,4-AdSPrZ[J#,[K,X9+'pebU-P$!LZ3FmHFmUr(RSM-V%P9ick2P"JS +eD5jbmprTI!q+kLEbBYj69M+9f"f0@!@dhdB01YrZKiXUq$-(IdDD6X[KTTT@'#L +8'`mCr$Hk`)0L5419af1lKQ2aU!)k!4KdJHh"Jl'V`B2QHpSRB[%KFlX9LkIYd9b +"ek4D!`@k+%P2r#R9D5JJ@dIAd4CcqhJN-6B@(EH3!'94-aj'@&Qh*XaiE$bD0Uf +%c8VBX@BQKK@1N6'(%r'KD-U-$DZSZc8mEYQHDr'ZT&8'LT,RrkF,046`I#&YS-[ +2L)V&Ei)d*R#dL%D(BPBLe@Ll@i@hRLib8*6Fr4qYeP$!h@TUS`dQKLiC69Rld+* +8*0S45jR0CPehHR3X()dhGd6h4Z1*T$5mZ6#Gk@C,E620GEE(Lr"H6!d'LT,(6fL +0KJ)Hem"PRCQ1@KJ40BV5`C,6qL,,YY+!pa*DDk!Sa8L12pBi*c(bX8LXaGY)331 +&(J2IKdPZSR8'#NGh3I)MrT!!2h$c4e69')lZY4,B0-BRiR'L$0Mh83-qM6q8m2T +!0S*eaBdJ5*IUG#P-r)JZ-e$S+8AcdH9dKB%#29N%h%YAm[X'A9Ndl1AhD$fIGU1 +XDNc[5bILLI#3!*JXELEKH!aE`hV5l8h1aAqPUp`SR'i*+B1Z)LfK8VZFhrA`Zpm +QQ-kRC3EHPiCGBHY*Upram$Y8d85,*P5-`F0PH*YTJi&#lm-iqT&Q,A5eJ8,eFbR +#Eb1e'LM32m&Am0X8iVFm+#@*GdNX&YFH'beX2MU&B+k0hc33(+S&PA3H[q(K0lj +EPR4q8qEX,@PEkd,EA[I`ka4SSV*8e0SEMUYk4cKZd0@fKTm#212KQE1YhB#hR8m +Ce%l1'k&5LiRG4"emNNmB!250#*pDC0jQkM43+#He9-I(2Acm@lHGpP*34b[j03H +r4TT"(H5i(T5,D!hrKDlK9pdS%5-)efJUC5j-Y4dM"PeM@d!!8THAZV$G,,Ah#Q5 +rj(jR'--lT1+0AjALT)ca#8&158q$H,ZTad"4LVaAq'@0Aa'GPd@L"qm@IXQJ,3X +5drbLaY-LmD*BH8P5#Nar!&YE'IqCYY+er!)Q[9F$L$9a+cRjH6l'8rbFKjrrEP` +pI1b(EFc$8prQNjZIqcjZIA3Yk8iq5Rd1&%iIp@'ha$)Q@qib"cUPqDLAbJ-m5H[ +i@Ak'qZNkIYS0q$m4rUbdrKPTElm8ek(c6r'6r!3rcSp4PjZI+L5XQjrm2TNpr-5 +CQ2(`icr%S!IZrT8QLrPTDQUBf58,I(#hPl60,r3pZS4m$F+J2@[)XIR&lAf2lG& +@8-A&Zpmqj`CbM2SP9d$hIR(bj"'dBfhKA#6VA(rr*M-m2Q4LIcGa!dJVF*Z&a6* +LTNHMFHN9GR1eEf(ih4J!@8f+RiS*,0f%rF[qj'4MdQpE@dJlM(f+X2Z3!0DlJfH +$MVd8G$5e2($N`FlkQV9G`QKU1I4V3Fe'R[e$GX3-1Xb@5(,FD[Z)mh9G$abTZl1 +qTLFdl1TjL2-MqPiYi+K[j%rPC#4(TD$hKRZ$,Eep!pe`QcP8XhV3p!klNSj@1[c +8,DZ$hX1r0!G-ec'FV(#qULTJeF#UYpmEV$3($pkm-623YhUFm`Z1HKrKJ4iDD4" +Ri@6F#VDN)dNV@AZS3Gb0,!(2'fb4RLET8-2K6hIf3G%UHJ#[@P5&hhRpjNie%CR +Z6EdV&A*!H!+8YrAhEbdFF44$4q!iFADkc55k)i$6!mipZT1FZ"fjA"S&+LPMeP% +Se%CQ,T-*h%8Kemr*G0j"TKkLJ'C53!p3`1QLJ-Y*CJ!VP+P4*U6rM$,1fc9bGF" +(Pj0F1ehN(%*k2`UIQTbji&[m1e#ACiRfcmJZV)iT$Ta[j%SPPkdDjN`VFhBrm`a +ZF$QmXhMcH1I"BA$RSC'(eQ`jq!lSi"#@4Cd"$JrL)iphPXVIcC(M6r#T`Dm+@MY +%e$jfpXY*J#amrUm%r,+Zrd-J0iP0Cd(!6pl*-`)jQM`+X#JJG1L9"!VdSS#L+li +Y)25j%b8"46pK'a!"CY#2CdX1jSrR3IGkL`*cNe1JCdX#q8Rr8G$*LcZE#2JT1`N +kfB,H+FTjm4(C$Y&(#!KGm8'(T0#cLLpY&J(3Mq,LTZLU+k"2&ZLUCD"2&ZNdPa0 +kYN3rN6p,2cjeKLiG2%1A$Tl+,p$*Rjh+q5G,G"N*%IPkJ"ISaDNVI5%Yi`XY0QX +$lDkhRIIIIPpEU0DX#h48hAPKS0jF%D+-P[&RDfCDFrYRN6[cc2ZjGEiQAc2EQQZ +G@C&935+[hrE%Q#P"[AjP-THGNVRdHldU%cJhTF*LlY5F#SNjrh&TR9r'$$1UjZp +S$PTqM##NiHTSAU6RTK4-q5N&)i4+25hdXXfq)kZEFZ(ZE"6[mrpGZ*BXC#,LZBc ++PYi&a,ecKK`)ERRP+$*+&qM[kEe5Dh2k(8@j-hm1D0-GKfTNlDM@lUD!V%V!lNj +(dMq4G84@'p5r'GLmF`$#$k@YIGKpkBPd-Mf+qS90I4fiEp$*pYi1f9BqXeFE6EG +A'meR,cEDmPJNMTZ69Kq,T!@r&,AJ'dH(Kh!,dRUfGGqi'I@1(rFRd&eY&f@BX8Z +60RdMGK"edrmTX&A#!rBVEAVVrFj4`5TZ!QrpdVX%mcd-E+$!ZdHElX3DTV!(`F- ++bQa5a4&Y1Tc"rX+992%dH&LF4--0l8fiB5MX9I"Z+["1`ZYppTm9lY2J0GQBjd2 +Sj@c-q!4HNcEQZa`DYpKrArMQSAh-rLI"A`QHDA[be`(crJm!)5%1G'0X6@&M8(* +[DQ9MG(-!N"E4!B!#BJ0h!*!$!3!"RD!!N!B[#`#3!iB!!!)!rj!%!`#Y0P!-X)6 +[T3#3"3@H%3#3"3'RIrrArrX!N!D6%3d0$%eKBe4ME#"08d`ZZ6"PE'`J*L"6D%a +TBJ#3"`M!!*!3,`X!!F`h!*!$&[q3"%e08(*$9dP&!3#[Pr(PX)6YiJ!!!4i!!6% +D!*!$G3!!)QfB5M6Q!*!'TA)4!!LbdAJqT3$Ldi`fC,5RjQK+Ai"mIT)55NR(rQ! +RhrRBieJEr!(4ZNIl*!6+H@RGIKmki-8R!-9[k,6f!G5lBNkrLTCcYI[RD%XEcYA +ZQU-YUc9A1cT(1l[bA%fISeALZGUYfCUp2jR1V['eF45UEf%5!!!1q+X'1FM2@@C +*qAQG[4hPGVfac3TZce[V1Qmc1cHcbkm"fbYEXmkLlqhef55ihq[YK0Z%qq'Hj+" +1(Y`2ph8La@YL)kqa$5(C6q"qi6j)i1f$Qq`MQ`&M!35-[miZZpCQH1EK+EGhX`d +*lmJq-YRNf(kfm4X#j$Rb1h`bR@kc,#XQppQ@[@LZh*GCeS5[L6eKbm-lBbaEcdh +@10XkF-"e2(!!*f0k3#(LLepX@9YIc#FqYM9KA%1k)j&-qaP96N`U-0'q0ADGpqi +m*K2H0V@SH'rEPXXRX[NJ`q%M26!f8CIHI5L&-6`ZNq42F'VKP-@YcH5'42++9+) +jPFh9lHa0`*E)AP66Gc8chf&091q*ic1*j0@*(DRqr#HTIeeZIh[9rm5QrEPmUMh +HPXMP8VPZrh%VGRFdY`fN*p9`RY2N1c"bU"p0LXepGrSER`*5H$CEEA5"EaZM0YV +-YrA(LlPek-a[Q[1mpX$UY4ZfUGqdX*l4"BGD+CMjYA04`'#!eRNX,9TSaZmj%HE +qXfA09hX988"%+bChMGaMj"iVYi#d04kJNRZ#h"2PRJ4'bAfLh&2NRLVh0,P2NRZ +kh$2NRLRh,,PRbce(lVPbRbch2,P2NAZqh+I+$GLI*RHYh+I,IBEF#q4H+2HCFTm +Ppb+jcjEl(,R2PAZah1I*IElFGA*I)2F5Z5q8qb)T*AH$S,YM@5rhik5S8R8$ZpK +lbUrdTA)Z**XBcJpdZX1K*F"#j`Y[HJKQS5"6"-1*hhL9f-XIh9A%FFmL(@ISL@- +AU*mQP+"fI90YBf1m0Rj"I2(LZVIGcdSZPE1mXA%Y',+PLa,pkC!!MfIFC$r(RK$ +lpjd0j@Ij2`U8`(%Z%Lm$ZXAbAaKED,fQCT'K5HGCpKBhVKYaHM(QJQ*Hp,TTF$Q +V&33jX"3A$*p!EkRYfA)l*-UbrPAZ#@Vl-,fUYKr!P64pFTkXYJI!NG4fX6bQU+e +"(P29eP`J%2BqHCbNY[qN0F6f+RR-80Ylj6&6ECq6abber8JHXl99U1NFY3(PFpA +f3(QFV,CemTLRYU3m6P(EIRNBq"IEFq9aUYTH)`rULBdDR+Df,mQM9QdrPFITbZc +ST628KJmpL)dq@+Lf+q4aTYSH+iqce,CE(UDcaAC6!@eVrTXmcP%E06KAEG4JXGV +ZNXGjMXfdqIPUSkCeDJ-$,P"E[6b@U#fK1)i0R#)1YKI*Jpl"pKCj2%KYRjE(JpA +fIAP3@b#&a"qL0[*HUMCL294YMj6(-V9"VLp4fj2Pm6#e[93HrdjYEjA(`pAf'D" +9EE!d%9%iim#Hj@UM2eHSMAcLDYXN$fU1$FUq8Qh2P-FUYGdXMm[9pLjjd+IB2Uq +BLHdRmPMMf-D$LBp3'cd[r-(BU#pBKke*(Q!10[*@IMAq1I*S90XEj8&IB+2''p3 +'YK%I+!DA0kS0A+#Hf+M[P@VE*Sr0DRZ52,DSl6r,Bk[Dk+HVe2CCH6a+EG6edBj +Y![Mq',949c!+'re$MYM!Eq'0aRDp2-!3E263GVAG)Jm`&Y[Aj8&-E2pEH3G8!Jl +3SMB`DiIDk$&9GbDfbS-Efp2NX8YYj(UefX"&4$CXhj!!K`Sh%hmPM`l(0JP-8R9 +X%VL684ZpmALelC&(9QhJRmSlNqJE&DdQd6GJ0EDIbB1BFLE$BIDU$Eb'ef!$8q! +Af1L"Dp6f&(NS,j`-jX'rX,eI(Np8flIN35r,-C6U@VA"6j4$RJJQJTrBk'Ib`ND +pRUSfH"9pLZd0mU#2X)(P6eFE'(#$BjX#"[`(YF&2RU%fm1C'YG'hi#FfFS#cB)- +[rNHeI8)Hce)E2"GH+@FUf!N'BU2[U3mfm"6qKBdqIjlDb1hjDRZp2&kJYY[Nm8+ +er9!Hrq6BTS'Ap#mf1*A4*-3'0hL*fX!DqJiE048&b0KH*`m`%pZRj2&Ie!E2qaI +(GK+F&[c"4Uqr6'e`K[qU0[$Vj@UMPekKYRq6ab[9pR&j`1q``DIK[(+Q`b0IV6D +i)Mf&$Cj$I'cJ,r`8'hMa@V94DfU)$GbLAl"p34ld2MEi)Va$cJakm8eUSeIHV$B +i%V`2'lK#,YM!1"9BCY#VEeFEHEj$E4q4ac[9"NH%6mQC#8GrYpVJ`qp4'aJ'KQ1 +$Bla2EH!QH)J0[2L!fUMl"pAf-AR!0E$"#fpeE!KXeSI8"[DMA@#$Ve)IE2#ZMkS +02N!Zf-!EX!8EZG+Rf2!"dl$pGhPm8QdSA'#&R0P`8(!9'l`A6SF0rRDlfZ!BGkL +0ZX)TX)%&k"[BU#2F%aZFK[l&pNYjI0'acB%rS30JJm0r@@e`bkqS$Flc9E@"F`` +HB!0(lP3E[3&(`iB2q@,lZ6bqkGMQSX[!4l#K+AaEEI$HlkJ062fZfZ!N2@TUa0! +C9&`,DUZVhTf86lD*9YV3Y,BaQfTTh9HhNcYCRD23+4!["AZK5NH4,JFL#i%&X#' +XJ#P%&a)%8B@J3T3K$a"8L#Q%&#*+Ld)m)C`363JQa"*##C'%3%)F)G53!%E))L3 +43J#aJp""X#"`%%0)#331iJCKJcJ#k"!fL"S%$@)')B1)3F!JAK!ZL"B%#f)&SB* +)36!K8C!!$NJ6K!"#K$!#@8%)3@#"6#!X))!JI#$!),"!#K%m)'`)(!JE#"S38`3 +-K!X%#dJ"!JA#"))%3JeL"!)#iJ0%'B)&XB*33D3JD"!`L"3%#Z)%JB1-3C`J6"! +P#",%#%)%%B)!3A`J2*!!'JJ1a!C#!p'$c%"L)#q3!"D%084Ma'*%BK@@MDL-8!b +*4(K"I!BI%@)4(e&Q%$%3"5"'#,&!,L)+!MQLGfAk")%#`4b4(B%CdSLJM'L-k)C +`$%''r#0m)m*!cK%E)(f)64!b"(+%@84@L#d#,-)Z`Ld#,!3834,d4YK&q%)B4@" +&@)9i)k3L,##1)2!Jp%(q%8B4Va!e)*H)S!KcL$53!$l%Cm4+4%V),D)NBLHN'P% +5-4)4$RCSfile$83SV3K[L)Q)$BL-L)Z!MK4ma!4%3m4$4%+%-N4'4#0)9B3P-3!P8!M&S +i3TTUih8bBQ)YApHdZMCHZlCeHcD4h@pYkQMG9pZd1j0*bf4FBVZmYUAfYHEVNPD +LV5fGj0f4Dph@dYU@bQ(-YZChmTEKX5a4YZG5L@`5Pqfl@eT5f@fYDE%PNqRfpN4 +(-kDG#GFThC&,Yk8`YLI),#R*Y#B6EGZbU4fYiZbkjIGR8[*ZEXfQNRNRGLUElH" +0(4`2eq,%00C-QK+eY+86HGF0flEf4(lRYKeYZe-dJ(K*A40YC0Lq2CI2YREX`*4 +UGelE@RCh*-QM[6@AG&,052b,@R)iC@3DFT[E'8@MNfj'NXQhL1(a1@NNH@HG(XJ +P%afij9ThG#6D-,KT1`BR6E(PdfdQ(TEG*Y$qA+TMMaMbVHe8c$beYXQ1I*[DC,$ +4,IRZMYCF[VPJh1HDLU&hjh-G#C22-JmBkJ34,,PV9lH$"B`M@5XSEVUMpST8@mC +Uf1b-*aP2-6BQT+aYaVSKGBdPlq@jGQY,I2fU9Ai1M5Y,jQ80qf[,16GX,6KB(X9 +Aqc+aENbf98*f1PP0UpG[fL,P01r,fp,E%fedUc%hT$TfHmC'1M59GFfE@cZDdh[ +a,UCdmG*(e'@bZdUjip0@cPaFr(l"h0#iZ@4f1dD0fM&BLef"H9NQNhaP)EG+N8d +R5"`r"0A)TI+lfM0e&%G(&8D11fJ&Yh-(VH#,lU!92-dGY)+$ZB0@m#jhd!Sqj3j +D`B[F35[iN5[98!ehd!V1VB0@KP[VS*AKccTSCI3'(E3bHS%1@KNY3!HY$-rA35[ +$RAA3b["R(E3bI&J(V5`'8(63b[T&J3rEj+b$9MBq1QKP`eeed-U'UqUJP8dIk+# +9$Hr835ZE2Y""+a[ZU)0@0Ta2"keXDUq$9MBDJ`jDf@J+1QKPSahSS*@02U1$9MD +kL`jDfHJT1QKPSk2SS*@00U+$9MEkK`jDfI5($PVCD!XkD'@M'HLJPFe!J`jDfI5 +($PV&`!99bf,dT!jDaHJ6(E5+NEm1@XAJl6TS&B0rkk"9$'c33DXB2&N(V@*`C"f +dLX'(GG!U"MESS&8-(8S(V@*S6MTS&8-ldN'V',AA3DXBfSS1@X@SX3jDa4K+d%' +V''+`$PV9J(FkD&8$MZLJP4Pkdd'V'ZUSJeCQk%d(V@V3"A63USCHed%V-`#RJeB +eD%3kD&@$hUD$9MASC6TS9B2@TB0@0HK@1QKP"Z0k"eQM2JkeTVISAc#&AUF[A'd +*EZ,V5q!,h-,ASZ!1i$Hi"%H!'i"c[Ti%rS2lp#Yi"FE6ar3iH%(rJIqq9UEpE(S +C,+12kAm`#1bRPm%T9d-$bhcG#j`$1qKKF,fU6B("[LlQDQ*J'aJ1GS20i#qi$"k +$`iU9%3#(['4dZM#8DiMHfMGHrhe4UXl5-C+)YV0+)+'-#Z[HH2e2[RRJ`+H8VdD +d(AJCA-bm"3pNaL#Q3hH&8jl`Mf`lih5KKDljF@p(*63ZcUU,QZRG'LEhd84C69& +D6h(mh,hU`MR&98)6Y,cG`fLFRQ%dFrTAC!M#6Pf6f*23I6aeel6UB(IKA,@kXAE +0mL[4%56ZK)C8GNHUG[h`&U,KQTYe)44K62hDblFNVfjYEXbQGfeZ%TQj"1Je-He +VeXEia606GG%6TA-&j+l&6ch(LF,,Uh+j,Ipq)J$@6Ihpr,NIJ!#c&1I)Z5FrGJi +j)"*eN!"G0%MIMT'&AceM,i8628#%IYH0aDbl#C0"d`hVeqZZ$qI8A,jKNbi,XH` +TRH0R)S'Z@VefTF#"VHqB[Q[d28EIBr8p6YrMpAf#[LISHk+q*qPlXVj2e2F8I8r +9pc4pRk6[kEbPN!!ce$j6hl2d29[IFr3p9pmRkhZH[Nr4phapRkV["qMl0(hAk[Y +dICqKl`AkAUM[-r9pPViA&GXU0VNdaPGSNV2eIBkqcpAhBRfITqrcLmR8R0#4fQZ +QSm6SGPfG[Lr3pa*pAkK[(3BC@&H'&(#2Q&m9mK(`+i+p2p!D$U#k3VXVXM-`J"L +2f1i,l3`Pk)",9-)jfP[X%V1GIT+(,&TFYN`'qA4!*TVY'+ilaSSYDdMPXqQpUHc +9ZGUQ[!bE*V,0,VGb4MZMcN$#E+i60B9499NKU!4`Q@iBAFC+`DC-+YRDdKTeaKC +X*XhX!4`3G@#2['Aff2@),X1"Q`8(Z[P!BHP`C*TQB+m8(+LaDYJYUkYPelCfA*h +5jB"(cj6b%J)CXUJN'ZNQ3MR(A4IKRI['6D*Z6J6%$!TY(!N`5a0*FQ3kChVhYTE +YrRa1C-9dMX`04!"N'"0@k5jfL35Seq(YjfCpKb%CC[6"[(XQMb)$-[PGk6UY"eI +-L`X+,5Y9C-M3'L2h@,RCq6aHlK2NRL$h4,NRb6eClK2PRL,h9,QRbAf5h02PRLR +h,,PRbce(lVPbRbch2,P2NAZqh+I+r3#j6j1l9Zl6j6j$lJ9b,j6l6,R2NRZ4h'I +,IBlFjmUp@1lcj$jIlMUj,j!!HiRF&jVKNjKXqA'AfNHqR*Q,iZ[V9bkUA95rI10 +bH6DZMQr896a4+FF9P'511`T!8(,hE*914*fJT#Zd)Re1a*@J&$dKbd3c(+-Z4!P +)1,qAHCb*Eef'&l8c`qaY+&Lf3JC4C5PXd@0PFfXql6ZX5RINLqD'eBe0YI&dFqV +b9%I&c9%AC'9Q4kUYiX['$pN-klYPiir+C"j6Hp$PB(lUZ6Q4lC!!jDXjhc@aSc9 +C'dpNDXe5%`RHRT(0XRi0#b&8!+Qk$PDS8Y$9'fVACf3eDqXeT96BUe,0)Kl[bN" +fQ'i3Z1bZe,kP&aqX2PjGPF1T1e[eU&fj,jp0q'd5hjh,TpYV(j(D[cHGEIBpPbH +6+HQE4YQBklZYf0hDeYbG"c*5I@Z1p6(Yfm[&+bT-CDIZ-MNl9JmfJ10iX-hMUaV +-5YC#TCh0V!Gc8-q$54L5dY9HH2@9Y$L&9AD5CE!9MqjH01YeZjTI0Zp@UpD8hTe +0TTS5,5P**GA5f8,G9GZBN!$&22RDTP3qheRP9C*CE8-LNc&q,L)BVl@bD,L%ILL +$cSUI5TN-R-RHRibXrZRdZV+KY!'i'eYmjc+mq*jG&I5Gbd8XZCD4bIFXG8V&XGc +L&@qhbbYqAPZA#Yi*Apfq"I!Tq5U)q6jP0Z&lPMU`bl'R-$hYHE$,IIFZ)$hSh&[ +1%Ta@(!IEc3I9LTr"e5kRDUR,k1TlPJ(@pkc`,Yqh4,dUMY9bq35XF`Gf&b*@r%Z +J@('Zi',&[`b0&Hm51PDFIB#XqTDDYKT!'k(U1Y!,CE$XGIF!TX[GJFb+@`Ne+mi +qF2EipTI+KFq+AcH#9L*83,62Ik$-2T4@I3meCK&3UkjJDSpIAaH8N!#eiP`#eiT +c*lj@h(f)VIVfeE#NG(3ZfHVQRT8!CIjCmHlLS*8!R6bdiPlQSKA[%KqY1TG*8M@ +#bcbU[S00dFP2Hrd,T+l,AhPUaDr-95[H*ElDica3-)qh9K`2FYG+L#lqfKGJX0` +P(PYe(Qj6Rmp@I3fRlA(XEiFbYkeiPrPYaE[#F5[q*CjEGHi[C4m-kdKB!-4ZL(i +SG[f(`0J0-3$(ES"q3(Epqb$CGqr[GMp'Gj2ihN'E$%"c18)9VE`)AI$XH[B$Y1[ +I"p%Ppk(L(B4Ter838,Y"KU#k-d43rMkipYh$&Zk&E0ql%l4,VS-0dJrFVRmrG,[ +qJq$Y4ZL$EpppX,#$%$i#h`&dMm$f)FJ1i(S)UN1B(S6S)AJHKZCK@1k&j!#1Kk! +iJ1%q#"k&ha(S(B(G)FJ0iAB3D[YJGJKL!hJ0S(8B9SFJG4"1qk&d4*)+"+N41HU +3!"J95&&$3P3S3`f+8%-5e,!!05`rpBT2JI3d*$`&XP1Ik$3U1Bd)6L0bdj!!f"4 ++6B0#8jr-0#3b"4*6)$!0bdY$iY+JY&34PL+5Ml1Ef-a)bl$d1"Ci4-,Y*KiEJEH +E'-%R-QdhXGR$%9fhQpKKK+fTk,69a!iI-2T!40b1DVBU*DVr$K-eFp`rUH'H@P6 +@Q$Z4rpZ5RcqV[m@kr[1eeK2[@$L6IcfG(pYMcGRCh-D["plDY(($!ZQecc5XA,I +*rG-ATrpLcrqCqi0RGm5Hrk[5TVhBm`ki2`85FA"kDaTGFS`Z0[L["P%qrq`3+EH +SPXA,mQ6K-NIrYlY`D[Ml@5F1)9M+(-Ba[m&e1!iE!eRZ(-CKBSB&d'%F0Jqb*$U +-!ipQNA3BK`f',*X1il$PN!#&e'%F0L'bY$U-`lC%&PZ(FGLSb2,V-!jE&eQ3!"h +'B6-M5l6$1'a[C0&f')F0Mc-1af%,*%Zl`cKXLQ5aGaL(6C)XrJlMX'f5jH"K($C +5XN!mM-2@5TD-Kh(BE-NLmM!1fbpC9Kl'B8-Q#mh$1'c4C1Pj')G0Qba'$q1`MC2 +Pk@%F0RDbB$f-`eC2PV#(F9"H@03HaQ%l+-[F`cKX%'AKHaL(,D-XK3rMX)Q8aI& +K(,D9XP`qM-0'8aE3Kh'JV5bT$q1`'C9&pQ%FmjZ&Kq1`BC@&q'%FYV#b0$q-`kC +@&ZZ(FGMQb[,p-!iEAbrYMq-Hm@FVl'@(ik!#,MmFKqfb+`l(iBqJiSIMm,Z0km- +irb!pR(mN%#E"12b5qFD9@cE@EHrr*8rRP2jaZqqB2(EQfrQ&q$mIjNC-(VZ'-8h +q%hX8p8`H)lrH`MpjK`FU%FB)km%[@9c9QY'rBrjEAC[65IkL13+Jll1jl(hr`cf +5"kFZbCp!r+fZ*Sm4BRY[(T%Q[h"NeNJ0MZcClZL$rX&brmLH(bFBfI2M"#0lITa +JC-q2%icXqA'#N6dr6M#bjmF*4[Em1-()RKmR'0Rci`3MHhkFB'62Ma1-l2PaJT% +p2di`XZI(#8Efr$M"b*iIKap!iAqG`MM"D*mI*aMYmq-%SheqR'#dcim6M2EjFB, +42Mp1-0VRa`P'qr`i`@LI(bFBlI2M"+0pITaJY-q2%icfqA'#d6ir6M$DjmF*4[[ +m1-&SRamR'1hci`5MIAkFB,62Ma1-p[PaJY%q2di`fZI(#8Elr$M"D*mI*aMYmq2 +F1pSRA[H1pSRA[D0piRA[D*pih6[D&q&rBYD8dGq@R'Gq!r[i3H&GRXRm4G6hmQK +S@-XrrKmkXZQZGR8l)aj8YMG'3q2D85AphM`faqZM!HUkS@P$AiDrcm1Ga%MQFMq +el&mh@p-qdZ`&'$[Ef+U6'2B0kme38Zc(Yj1'NmH)YLp+ZV-8IU!lh$`@`r!M!I) +B,XM[mKLHI2YrHB56"[IPNGVRj1%5I2Zbkfjarh+aaRkLYG#qBFeYP[fMqCGGpj+ +*pR@aci[p*fimq`B[Mq%"R0&kH*-c)i1"NSHZbamXV*1(r00!4%!Hb819$HYkA6' +2!)Q1jT'ek-1Q'ppVqR(VdJrC'kFHm*!!G#X63IBkHpeEfZb'@jE3T1,r'iPcUlA +4rZJCCPc)j*'cGJBil@clY`a3HpPei[5@q2T4(N1-950-*RTL)LLhXfhdGrAPh`P +'q9NZ[cXL)!p1R66*AqV+C1ISIae'Ic"ZU@a[M$m`lX%m)J-BprS9DqkHb@[F2&U +2L!rQ2P$92c$hd6b1-2I42)i`pl[cZ)qjKhNBj[lcQq3@c+jjKB`*1-cGr+#p`p` +IqI3LFfm5Zq(V6ZGkHIb@ZII9idr-IE#`[fIZB4j(Q(YI'RpNlQ%HRFcpdPRh-IG +,CeDBHfU(0B66$!@E(aATBHjh'Tb1r'$F&bqpZMI'(aMh3"kFL!$',6mrmX#,DSY +r-Y*j6VbI-@eCHR&rM2XCdfJH4jM1hARFah6#2$*@1YPfmh1Xf$[1YQCYIk1iEE2 +hLRh@[*[&GRf"%8LMLF1UpB*iHfrqS2(IpGTA&r)3CK%4N!$(%@E49pFr-SX`Ma% +3PMc-6lJ%-C`m8MPVmH)"CN%5UCb#TpL"XiS#i#(R8"iqKF0FcZ21alPE51VDL[p +EI2!8&4ZRBIZ42,fpm`GNbfIppPfMH8`bP'VSGfLR'1eX`cP6l"[@$9Eqi2%VlfP +RN3"ja0YNSi'cf'!J$hFI3D$b'XdV%U!H4UZ+"-M$eDU#2)jU9@EIab!MN!!TjUH +GGB8bJ*PEAe&Q"1pK*V5ilf1`([IdQ@S%VCQKIN8M'16f6KjQh`H,+)lh[G%))J( +ke@J%N3"j'#4hpR6mYDiQ$rCd(1q2#3dMrpXq2Ejf8j13!+&8bl(+XeE$U1#3!+U +K2+VUG4H5Si*(!Z4Ke'YTaGa3(KAeZXS99IXBcU06A4[-3jKd4%!H3PdM![*Sbaf +UKka4+IppDMN2MpZ,&K-98!r4BU)"mK!i#r))mDb3!)GS"-Ibm&6RR9EZ,rMUe50 +M)FB%r9&5R3Ik!idT)L!2dCL1p@ZRaK3&N!"(2QIp[FmQ&ArkYLIJ4'8%GpNr[[( +E9ZbY@kaCGel[HFpqbPY,&5-EXI1`Ira--dm6Zf3Z[,h!##)#mK"'%"'3!)FJH94 +!(TQ)J,VZY$+CBhN8jMi%UBlPdBP8Imf$8jFBaSq9rN5LjP,)BrXGAKlT+)!m*K9 +r$Dd(mFBEeE$K-kq[HGQfbi`+0Ir"$r+mjhppi2r'**iTe*L9@r+HDKJ*N!#(8HN +L!I*`9ESJMkT+9mKM0@XYA9AX8$fU@r!UH4J9+K+JVQC5jAJH[+GZf*R)j0,0qr[ +jd&`('@jrH&3!-N3"j1'T8%0j9,F@9$V48k(qRJHkG#4!A4h%L`,)`d8m)6&(%Br +5pL"H8LCHKZT4R4E[c8-B984!(X+")`,b%!iF&C!!4bBLS+l-8"j(FSp,(kY(L8X +Ibk165dF"j*%eUYJKjL9V!Ja3(qUch(!HZQhJ8"jQfi#-1J9j9,F092)`F`ddrR' +m0i`b%L!2`bLGKKhXMi0E!LTe0E12N3"jF)`kHkbZ6(#Y5hIdDKlZX4XD'NIffpj +pc(c%b&S$kj6SYld'`6Qk'cTLDDr"hdl%dPk$61E3,S'`VN@YiG#jCpHjYarKq)N +!fBr3"b"rh)m`J044!IX4`[%)RBq)"1LJk!$Q(P("6#-"N!!c@J'TlMRKFXJ)%%d +R`Q8lC(!L#,5Bk+$YN!$(6X65GXLM@Na[)P%%@Xa`$*hl1(5LYlC-KLZ[V'Rh-lL +Kc6lh-lM42)i`Z0%mMM#iZr1iMm'&HIb@HIAPm5IQ09L3!0mcVc#2)mbV,idr-Ur +42(l,[(S6L5MBj69d+VZmqNmd!#-`H84+l2)+9hPC*d92D)[ap6F0E5VdN!"Kp0p +Lc%k`5)!meZc+YY6*rCHk`L`L6M6+D-Kr#IU(bI!r'mEFYI8*-SPlQJcZ@ZfjI"% +"aYbe@9lM(Q0CNr%X!Y@BZlE)eTiC(a8,RKf9R'TH+SqcaE29MhQPc!l2F@0+RM8 +rp#SJQiP2`I-k*mq#jjDjPP8V1'$M+AQkRRV1PRYb*L[lTUEGqQDaZAmSB&R-)bp +a2"mp4fba!L$)[FEaA2)0LZ9j[PlZ%ac2Lcp$qTlRf`&)ar2"+m8feR@d*mVMBXG +crjYS&mm6%+Yc2+qG,EEaRLIpSRQqCbBfch02`E2Pkf+Ei(NqLASiRX[ALBfm(Fq +R&$cIqN+a6I)mRbk2'ah2P9qL8ec2Q"!(qjQ1jbAI%SF6AFqaTmTMRZ0j)eJ!a6" +Rh'Tjc(3mEB"XPZXjrF1568clKL%Yk8VTGQ%"mB9LRGZHci%GfT[5dmqaV1F,JCT +m3R[HF&r2mbVTK*S2LHQfpRaEZY62q$p6(P-N*UMLaG`U(A3kf,'N2EqMM"fbMIA +-RdZecf[2j#4QdI24dTULk0LrEPbEbe[6fUQkfG4Sr&pF3+p#c#X&mGEq5')q5#S +!GRX9f#!B21D@BU89D`3rC9[N%pj@V,6RZ98%P5@,LYQiRJZq*6,2Q+m@+qh&[2+ +edP%2%p1(Y6X+Q%BA#NjVjpDIqJbaQ9fdR&f#lC-[-jla2C[%i9pGce@##,&Qar0 +5UBEeEklRc9,6q%l(mdA-r,r'kiiIH(J6ra@bN!"84F(i$(V!mIcZR@*lR9HE[5# +@irNcYL5"Y%l-C96DmG`26Vr"mla#(KpekRR4Xm8Q[H5FFkE)BkN6mbD`%0ae2'' +-$Kq)[ffEf1!%jP`KpE5rjRMH$SkmaBZjS4$c&qm4QpRFC$`Ikb&ir&U`9$VC1AX +NYc%1KiLrJ$qGJ"miq%N0RHkSA`!lHBHAM@aKYlBlRX[SlhHkRS[I9DMRZbi5'hB +RjPeJXq2j[(m4flXpcepjD"brjFGLSh,QR$Zed'[ASCbmer8mMhlmX&1"'RM(qec +20@ZN1fBl-4ZH*3l[Gch2IcaC1jk[HkMB2Z"9l@FH`iVrXm5h2ZKkAL2i%Y11qKC ++(0MS91"dH5ad2,ra'l(GkRR#UV4Uc9[%"JX`ja&`MHFkRMqJ!X)fR,2LP4k6LjX +YFKra-%T`fXA#HI2!%GIc`UG+QNk[e9mU0ZYMVZFPdN'+KI82T2khZClIqdS"#jm +%@RcFkhCiS',KEA#G6hLH$r'`X(iP+24*ca01je5k[RDTf$lPHFVQ@"F,hrNraIC +Tca0XGl!`rJA3$REU)1Z62HbSEi6*hHlaY38&A2Zr+&phZ*i2&ckJ@&Kr&5cZXel +-#`SaVbIQjca81EQ!K@m'p6r[HYiN(-A&`TDALm-A[+V0+(6(8q%GAr5bZGc$`RL ++[S'G1Tj`%mA#&"cRbejY,LPJiD[TI1P+MING$`[V,`)G3&HR1d"5lE@PF2#[H6( +rMiH&m64L(Rc&R%Yrk@&KrCAJ#$cFR18JUf,KFd%me!-([HKFaF,pF*e[ZTi!X)Z +&Yp$Yp+J6mpS#&MiG$H,ERLGGT&9VqPpLSlj1"8!HaF)XUYCh25am9!%,hrN#3-Z +brMm!$3d28N9"4%e&,R"bEfTPBh4cE#!Q)&0S6'PL!*!(1mB!N!m"U18!!G(0!*! +$&[q3"&4&@&4"6%C"!3#[f%,pX)6`$!!!!CS!!!YU!*!$T3!!")%fNF*0!*!'CE% +4!!JFe"b#'($`eCIG05NKdCrNL`SqJ+E)6!pi)*68-j63U8d#!UAH950UIVlBrG4 +l@Vd0N!"hJ02kPZrDRLEK1C`6`#03`H[RM+4`Z1Aj6J#9Vp$lmDqrI9S&L0dfabQ +YLkVT6TB$UN%jcd32RXA[h(jUIS$FZfBMU@,Z+AP6r-hpHPDq`P'SFer%hk09+dH +6r)&J9iIlBUE1Z4*qLY(F-IHFNQB"#djAmJ!%'F#FF(+EE8U1KSmI'A$###-66PX +*Tq3S2qAd#&rMK&eN`JR!#V!#V+Idm1I&!3Edq%V3a`Nk1%%k8&hIGFDTK*2M*q' +%45DF(#I(bA%bi8H3!-!T@F-,p(CpTH@be9ARhE9@-G"c)F5C$G%R&BfcJ9E1Nh* +YCaTMeh5PQ[`LeTV1TDU%1$8q4()VNNec6"ZAU*8E@KQl*"0*bf!dj"aG*mJXhCe +YR&`1qXC'&fTaUhf!@AE"$f&bHR9$`5@[0%Q[DR1V+k+c5%TD@QK%N!$J6mDA3Na +@XAXjRH+r+L6Eb%@S%0Hd5iYTK)p@ULN5AA%bRKVREMK''@(8"15K8UYYc,R9mPB +63&M)4E0"R"jSE#MC+,eH#Ji'KPj8(ih")))%90*UT6P4q1Na3Ml0%V%MdQal6,S +5hm$SR@&8%,E#ik#2i68iXTTGZ$dMb-Ci26L1AQIEST8hH*6`NN@8@fU#Ha#B%GT +dB1LcYeHJX`326rCCT"[VlUM'EhB*D@#3!2-*ZPR4mbfLi+6i&f0bp-KS534p!FU +I"@U6UR[Bi9TFe6S`Z5@IN3V63*(VBiE%hNM[$EjGHNFIJfjSEXKBe55NbE*#,P! +kFF0a,C*T!$c06XmrHI(&I4JSL`qb1!b2,DDrlpJ`#i--5jPH9-%!*YU$@bNr`R0 +iB+l,ib%+TV53!0NeNR((CrCK22YFSKm$@pXlUbV%2h-f!Yb!GJ)rA*RRmpI9Mf# +&V3(8R#Cir3NUb0"VXr$5'i#5q855cV06r!VSlrr#TPfi*P!+h'TXiF21bh8VUG@ +UPYD%&R&bj`cY8N$+C58QNdPfXH)+,l%6bQkYi4AX[K`&j"&A@"mNA9l1%#@Ej5q +2%Cjm16qM'Id[Zm[Uml1,Vpp5-m4DMDi@$kiBkq,U%I#rA%dQ!Qj'J$*aQEHKJ"6 +!"1p!UdqCZiLjlPI(&VlU`0V2G%`BMDj62a6Kc@!B2faT(Q8d+Q05NYS88552N!" +r&f8+(N49F6U[)BXipU+KjT&bF,SX6Yq,2MR94`c$S-Pr@ee*TbGAJh+[LZSJG$j +,kUCKlVD5!hTpjGf#,TI`Iq%ll#BICI8$H1qjQjZfDiVm8l`lqJ*E'pN,GDrEbci +*Ef(D#B1rXMlZ6Em$DLFm-2KE9'(,r%jii"!2YMMmJQ+@,5e)iYA&bEIh1$kb+1Q +99$GVcb2kqBA&f2dA[j%LZ2dE*"$i!0+Jr`G,J""D82b(#JM,$Rk$mUD,"bckLZV +Vk)5PjPN+)`alM[[UQ"I$B4S3AdMDC)d#aKKLGbE@CEIX6&c-6eLQFaEEX-rN9lc +bj0VRa4#9R-[ZfPI`1eRQCj!!lFIcRmUPri[bSI2j5N5c0aM[jHM$KjNmPS11UD1 +PlM3fYP8mC'AVqRAD*pe[6E&A`E@!YaJ-81U1F@()m`ac,8UVF#8ajCEKS$P!1MD +Fk#(',39AQA`HZCISMH0e2rDmrLlKX$,%bIlchKFj3a`0Z!`LrErF)iCa[3GN@`Y +('2fIQjE1l,U@VEJfE39*I2T8fc@ITIJNp4m!N!-0%&4ME'&`F'aPFf0bDA"d,VN +J*L"6D%aTBJ#3"`Gm!*!2!F`h!!(TaJ#3!aErN!4069"b3eG*43%!VpP[Pl#%ldN +!N!EYrJ#3"KH*!*!$DJ#3"T1b$Z[RpGR%Dl+`cXfZGAXjmPGqpJXrSpZ,EGV+*Pl ++UCaqClR2@#YENrh+EThXpGZH,FHAiiZaC5*K1hk4ECe2GPlfjN8@[KMEK*-G@6a +bLjIQ)YQphj(&b('bm'AL'GQ56IDjX*EMC,'"f6'J!cMaecIT(16CfHFeb(fakmN +Yj"Bl4YM#!0J#3!$1L9pH1*Q-14`1&qjV()jRrXAKq*VAiDMk'pMh12(S2TE,C4+ +6jl@ibm5f&EGEE2IKpSMYREM,aIC4h"9Lqa,Z5V&p!rFd5C2j9)RYeAK-&pXU2'D +)E4-H-mAfCMaQL@dr(V2&pK%mUXAfD6aUa2B921D)l4!HFlA0Y352H@+l$BrjBVX +EM`9Lbq$a3f*l'ai,aIBK2#i4ffI`Z&4XIih()QdV)hjH*EE,m9JXYR9i-(IDGJQ +qD2Xj2)"(CIX02)J&fSJEaUIYlr#i3Y[Fa-0Va(B$(PH+V3@2Um5@`10UX6fXFDp +X[ih(Y@,l8cbZ%pYcH#c90JmaXdaXYq#aA'c%[8pX$q"a[GJqJ!IV30ZRm&JKYUI +aZ&&Xhm(M*QdV*djZ&Y[YH2b`f%*i-&rDIK+2@m9'I+m8farM3Fc54Nc8DPX&HB% +F44YcBekdYH*aKpJkm(LGf0k$"hQ'YYr$ir9Lq`XmkX6fEhLXeVC+iYN[0ZD`4Qa +[`'1Yf0k+"c&1fiIa@#qf,q#a3@bX@Eff65-I0BL0f0JSYL!HM@)Mj`A%4Rkp8f` +Iak0*E%rK`CLdr4-HGfPE&6QV@@c%$rQ"YMBmL![DL!Pb,'h[aQ1cf*JIip$fC6b +fL1dIm5"2NUl*Qq3%fXJ,a!*YV0FEaEB(Mh[%4KjpNpKq$BpYB[Xm([H+lCYiN!! +(+!'S!ED,M6`9&K[cDaIEEM`LBYZ(a`kar3SH1m9'RLEIdNC0%"8YBUBja!Re((8 +Fp4Ye'r8-p4je'r@Dk$5P"iP4kK(b,r8BG4Me&c8%qBdkLca"A88p48kLIU+Z*)q +6[mREPJiJ6j'[bE(NCh)jG3$jQEa-2LB2Nhr*Zq4EmLcjPEa+2L@2NMr*%p36j$h +b(AQ1r%EH*#q5hmKVj$2b+RQ6I%BH)hq4YmKAj#Rb%hQ*I%3H)[q3!(I)0q3CmLr +e((8Fp4Ye'h81p4je'r8DG4Ve)[8JG4Ve'A8Cp4MjMIU,ZSYkLha+I89G46e&(8@ +0),V5B($)M3m"eLq)T1lRbYC`,*4+a5+CF$UDbMV@4G1CV,FPXM-H5@3GY5&IXL2 +VD)aZppE(8mPdeR[,bSf13,#YB&iGLX@5bB4h3b5@FJ6D[2lVr8ZAkN"YhQ!S%`l +&P,8jmS!MZ(CG)A4Y)*,H'I%f)FHZ1V6X3XlG2[jeJ9Y@lZPf#JEpMQ`i&JL&%6! +G5ZreKFAHe&,AD8XlkKLf4B9PG[@*E#5p)a51d"J)C9A+JCC'Eh0()KZ04j!!QQp +0Bk0b8I832emq(SVSA4HkjZESYTAYefT(4!Zd55JiU+S"#Ye*D$F9-BmZ9Y0'N!! +B"8'diUl,a"eEr%hVE"JT[abUD!C#0"LLNBc[%ij8+[`K!BHM&Vl,@GJB%JHDZKc +KS"`C%AB03RUZp0h!S*PG-8EGf1XXe96qfN2Ea$eRl%*Z[emK&d'CGZN'fY(-lEa +3bAJbQqaMN9c!bB,daI+f*$[5iBLMC@mQ'iPlEEJaM%C'S'dEE(R+f4!*Y8I5[P4 +Bj+XjiP!48X%G9L28K&iR!eKG3DZE5(9SG41TJ+aZ)T@4e8fN`V(L8ZPCh83U0UZ +E5'9QG41TrU@Ek'$(5lU*MJIc+[9R4DAb[%r8+Xr(4,AbI&EU`m0ZKh36(AmVDTE +R@k*UHGM0NQkLNh@4EU+618Jhd8N&+0e%*a@XG"1G9)V56A4518ShdFRZNA36RHm +3p8MEqh-iFciQf+(YF9'AY$dTkT-feNqkLFjR4)h5GP"8+@hXE%NhdFAk56I4a6T ++0p(&Z0*0G&'"5MI44@8Zh83A&E0d%ee8bp*0G"'AdNedXDl56A6p%Kl56A4p!Jr +T*VVBeC0ZSSYG3qNQZPJrk5Dk[SZ(G"2,L$hT*TDa&Y*0,'0YT*YB4Xk3!'jL'9@ +qG"-0#iGi)iE)Hq3QBSki)QD)*f+0059@L"eb$IQ2R%QZ)8H5(iJ0eTam3Lb4,mN +R9RH&I%+Z*(m3cq31eTlF5)iJ(a$Mj"&b*r&#h&LG"SYlbC2%(MQ5Z#1(NPF+h4h +L5cKSiNMRTL`V&(m8Jmc6kb*-UPRc(fHhr[#,+"T@(TDhUH(NXLIqL(9,Sa#A9Y@ +)C`+"',GdLhQJ$00acm3p#rGX)JVh$0b9Z#Y`9q&f1j`S#@kqG'!A2FV+q(+2BG" +5CIQMNXMP9)'AZT6R0-Pa"VSU@Y&QI&1#eP+)1VFFYT(rl)E3@d,q@#L63E[LJ@L +U*r6@qU#hS@ic1iB@p[+PmV$),(Had%H,h&9DJ0bMqedr1+Y`kS*"0#bG$fe"kqh +#B6kfFC2c%HFdeipdel%R5i",9jAFj!4Bb%f+X+e@TUh+V9kQl8!*Sf5IjD#kQZ3 +KMiBXHC!!r'laA4mA@YeQmUDPeFd"Kpq-AUSR3PC*qH`ZVBkJZFcTZ(5`9m0H@Mq +A+3,6+4@iV-"-MR*KPKbrp"+VCFi6Up1aH,!BdNGmX4KRL,@cG%#-VP`2CkZkj5a +P,UPQ2ZeCZ5S$GjZ#pEj81V)MH[mJRZ9PSd2J3MjC)6@"4d%mX+0Y"bF2,,8D6X8 +iK(Y0,Kq6Jl3dj*p+KrZ5i8m,a`r#0jk1GZ`FDd6SNeG@e,5jlcdrb&Ue8e3RDdh +MqLhK2G(fB$UjZkd&fVcm#30!AEGYbf4$kIrYq&`i&+@U@AVm[0`Y-aiJm,r9rTQ +Ukck59hAiDR#lFAY`6mGGKEXFG`AZ5YbcF&IMRXeB1HYJNm4ZPNPRPjiZ0,R4f*! +!cmJ'Yb0p1CF*MJHJ"p$VR+kqNAU9RSSQpN65Cr1BfrI"Y604ma[6J'8mGbB6-J( +KD)a!)2J5pI3HIX"R'`(r9SHCV%CrC$Prq0@HAaR-!(RiQrCc!XAS`ApT&RBdKMq +@i83B!d!H$E[61hbicp@9Rr!#EAkC3()H(mdYc32r9FlQdETf5kY[HqP69qHIrm' +MmYL9MCm$2r'KmYKG3Z-8AP8HBpc*'3AMKaff#ra""YiD6FQ%QI0eE8q'1IR&!)$ +lG#BY8eR1jm(MJpJGBGQTZUSmMR3iAmT$#hF1%"L81i[-%dSR%'L8@8A$ThXQad$ +PJid6$(-m$f1!3QPDhA#-1EZ'Lr3I3iA5'H21cX%SShNXPGrf&r)S&H4d(U-%r2d +maTRm4"k4qdX&Q6aDQCl(Uke-akV4031R@)p8D,3VF6U2dS#4mEVbfk59a`J6RFi +M0DT1aqYUjf%%81iUMda%CSRpcc&2+1kTRelGNjIk6bEEB3M)JmF(N!#FUbY(Uq4 +ElJ0"+X`6bYe3S8b0!-aT9Q#UUD1C!4rdc58pRD1c"elUk3cRB6ET1TrUa3bKif` +[CL+2NlfBL6a1pQ+1jA'L&c1HKhQK&c0BMeGk-F1&2G1,'FrMC#pQ-)fAHc%6HCc +Ua3`NBL,SaC4Lb,c$dF0KL11jpJj-2(+-(cfG)5)kfp-T-ViTS+GM$"QcC!qGX51 +B!-TJJ$N'L'N-HJD!XrZ[l[IQEZKM6pm`$8a0+-eP-S0-``#VMQZ$![F,,q-chCE +I0b%`3eiPp'N%$TrL&!Re2V$[H8b`R[PX6a+G%kT-ENG0@bp3ZC+60aeBq1jl$ah +k-fXbVCRYF0UR24G(XF+b!rX1AR[Sd'1RTq@Bb!lR'(9TKD@D&6iTNkZ0E+HXHZ) +691R8(0kNS1XBi#E5U1R5Q&jZ6djeGID[E%8#I`kcA6JCKa1%,jQ-`bQrQ#)ZITa +M2&!I6KPH2TN2TaEl"LI+rN3q$LI0cCh-Ke291C&EmKRSrX1IdpBR*Xr#Ra2CEjf +-`kp1R!)q'XFJI-TGr-X"R9fIL'DMS9MdJFLkG%M[4R,Ur0qI#TerDb3GMbC#f9I +59hm5A'S#YKXI*6"Qh"JhfG*C@lXcNSLNSf&M'b%S(LJcb%diGGAUIR*,4iSrJ@Y +PZ`Z6'MXi$i%(U!F#N@`kH9mN[5IMEFQ'%ZfKG,YXZ,1hPRX-qI8V'BmR%lA"MZf +aD0KER`M(1YSMCM#k8(V!r5)23$8Bb8M!iJ%2qd5eJ@3kXLk+Rd,H&EkEI6HCeMS +'LJFUlIpQa8'UT61[Ijf[3U)'Z!RSDPZK`R&LhDAZIiSVI,*4P$RP'%"qS"$'eXC +I4VF6D+PI@k1@VNY%XYkepkGLk#UF&C(eQ$M(X*Jm9pU6`'"fMZabCB3j*Sr"55j +-K90$Yd``%p6QAHe[@V2fDZr9DqTDkr!-e[YEC9-lBp-aQ8hjZiK!k%GKrF)&JhZ +%@XFmX)cALfGmC19raH9#K@TIZkkUerSl-YPNh,XaX[HqC,TGE8*DF+m,Kb1C$1C +,ChIeqVD'X1%"Yil0CU1*REhZr(Q'EiDT9*rckSjSV"hXR8eMCRHh,lmUqC2YNI@ +44*rVQQL'Dh6&YmG!qdImjCI6%8q1FSf%HjD@VG9$Z2%a0S@+p3E@hQfKG++[MV, +IAlGAIE1h+B9pBI(YScFIYF%20[Y+p@j2Ued(Lk4m0`Gkh2+l0J#NL8LX*i"-"Mp +DAA%r@Pj1+4Z%)Pe(S*JIF(E%%l1Vqpd'LUmRLJm8'&2,MeCAEHXdaR!k4T(QG)! +5eqNB)i5R)a4C6`FBTVkFIa&dZ5$pb-Zj$q"-m$*#Ki83Jk3K)IU)8EX@f9%('+E +)[2p)#3I)8[Z1-kD18U,0cKLM-"JQd*cr"+#(U$6RITK2mlj&T"5C93FSdU[q#MV +'Va+N5,!5SF5`%Q5%BL9%N@-P`M$*jJ-83CQ2dSr+[2m!p#cJM""YCia"LV&Lp&' +Yq"Dj9L)-NfdK`'JK"qK@R-Ij9X+8#,FV5!N1`j5E$h!-hN1NQrFrc,S&ja()&(P +A)K5*&m((@*F4LT4,pa,I-X))fG+rb,4d(kCCblX)3LY%2`)Yj`'%+A5-8'XZ`## +4U!"pT%V()U25ICK1EHrKXJd3+6h(@C3a5K5DMe!Xqc"j@YjMS"fL6F[j-'IDRN1 +i+,)Ph6ZT8V,`+[CUM'B-$cp@c*5R['qG"I2,Y)E1$kekqhLSLMABlb+"K6f0%lZ +I'`q1qH9$+qLaDhjfepaTqc-T[$LI06GhHMJ1[lr2Qic$rihc*q0`([H#b6M(jh% +M$ZGaAmGLUTL$FlEKaARF0dcQVEB9(jphM6LFRcdaCaYa1$plBXkffKeeFXifiR! +1q)hMG65i(M*J@A068f["U@apmbDe#4"Ye5el-pP)A1B+4L2F#+PbAAhM@QSRHA[ +NA5l[#RQV10`p30k@[5UIS1Y5p#-NZleH$$(dYZb+B6dZYLEKVP*$b1Q58T@m,IX +XH8r,Tq5FLC5D@ZUmQ@4(1QaRJ9UA5E6CmUk4GlAiikpS(b$Fa`'"+LP!i$h$f!! +)q-f8pb3J%-99H&X!J9h"LS!31rEc9CP`crh1"G32Vk#ZpZh2a6Lc56!ci,lm24[ +R-cjhi9HlS4H3!$Q`#j!!%H0clhrLi[M'5aFp#K1mZYCdU,VSI9M6SIbLG@&0KfR +iUX922dHrVKDqeTUR[LCcpE!9NhQEEVmFQi,143A#CdrccB`SimXa`L`p`4BHQjK +US-@VF9p1Jc8#i`VFVbNiA)Rl+S5XXLCLU"%CK3K(aQLB8Q"ahIGJBCEMB+I$FfF +b8@T41!1"i%XV6r8HI0&`U0QX)aY8eHJeAFiIlT!!AJH0E3E)`pqd[h04Ulk$hH& +Cf0%BrPKQE12h%hNdl%l[m1%q9eF1JJZdqGHB!I$4h0*Fh*VpG"jU*rVYT4Q4Ud1 +a@$+Cm'k)a)Dfh9Gjl-V'ci(IhUPrG`Q08hJe"qb-[l8qk,h!(f6JVG(8a-li8h8 +e6qb-cq0$Dff%CDIUU[*i)$V%,kIbQ&JBEC&j3ZN%!SfVaq2JEkfe,6p+1eMjB1- +%`ac2`aLJ8*T@0aaMcQ$E4$d-&8TRM$["9(T)qe"9V6b@,M8&j&%Ub1Nm4JRiqhQ +--rQ*22#Ci`,fY6)pMeGEQBj9!hR)Q2VK`ZSmX(@!)5#2m'KPaq[+JAY@(L0-G$U +2e+Jk(DqVRBF43,QV2$+4R4Ii`cbKZ+FQ2f*2J3PGPFPf'!,bi2%"*1IUbV'Hq5d +4"S*8Q#H8Zk&#Q4S"Q01X`&46Cf,TG20(6qI`(Za@jFrdG)Ec-*YdR8reBSE3FEB +A-j((b9l-4"iRHc((mMM4LaR2`lc3LaQXabZpQ1(#RZR&M1GaXKFcQ-E,[CL*2%l +eBJB5-4(dBNSap*iNT4KEr%d6HSJal+l&)%D-(cfG)5)kfp-T-ViTS+GcXG6bbme +pX'fH`j&p'eNlRXRQQF4pX*AIBM!5X*bHHGAY2VMP)r##TiZHLCkm+Z(TZ!DHd8, +-Zc&![a)TZbA2XQpBRP[r(Y2Fh`)2b62RH4F@[jS06kINQI2FXK,6lTrKE-*i9R8 +%C#SKUSBFUq(TH!LHC,5F*hilc'$4&mDcQAa-I@C3'ma#6&E0MYRf2Nccj`Mj'I& +X,0N6XaUeX5UGVmdMk&2m!Y#a,*l+G(VH$86jR`"feX@c1r19eQF1jlTHQNT(GMJ +mdcKLIj(PHGPQ42FU6rH$C'"l)kXV8!&AZijCa9f$&eZH9ci0K$bV24qm(3j,l$` +E@!cYZIGjTQGlhNPdk$`r1`Ffqhr)(2kVUp)alrN(f$KK8RY5jFh5-ErpUl#TZC- +mYh0CpXrTQ*Fraf,C-Am-MjADFqX"f04S5ZAj1m5D92V0,+2PHFG2!ha2kjM,bBT +Af6'"+)RTZBDY5AXBij`[N5Ce0Qpr@,1C2SrF$`M85ma(iF!pZA6-lpVSF,rpJl# +T'CJmUbJ%YQ[2Vrd(E,B'A-8+k(Ukrq"MX#fc21Ib-90l2R`*E-[YQ0r"BjkZ`1f +XY-rbI$eA*G"BFhrU0f'lh[CFL-H61ZE1$m"Q6`*qp$0!a`)GFarie,(#M[PAH(a +5aec)2HeZY$`ICTiD(Ck9hi,Y*X[cm3eJ&8(8M9q%!lB!!drr,RTCB#fEFf@V!M! +Hd&fc1mqj1Fq[)%qbc`h#ZChl*P5r2mqjGXc0B*fDEbTQeCcE&I1a(%YH83FE0a& +6TaPSQ,9"B`#PdK0-eENAD,GBFP8!$VGDRPr!eKF@5eD`3`PG)APq,XH5'cm2fff +@jr`2jeMbd9q%cGiFBrkRFmKpjlGKHkhYqBFjPRcq5C+MjEQ)@N9B%UZ"JZMXQ%r +P@1@&CYKHChNZi*)-JXr,eT*-,-rY3,[&NNZq62ME-DNQK&QIqeIBL"hY5Ei8P[a +hVK&KGdEE`FB@5bCB@eZ0cU%#%A5i@&ZlHrNUD%0K58mGP42VSr-NfU@Hcaf%cHi +8,0KNXk6Rj[I!YYlfT&S@PNcq%@aJ*['NpK*&XTRBV,Fm&h0e#Ff5lJ0N"qSGGAC +")3P,HVDbD[D2LGhcFbbjr8f`fGhUa6N0iDRiCGM)(cVQRpJXkGj2MSCk!Z1"jAi +'('@cT%cd"RpL*G*jE-9B,'PlEJ*kUPPG5jPf6J&ImN+H*HfBV@!l*Gb@#8YfaTa +,VD&Cd[e4lUV@C(Qq#k`L,1RC6+4BHmNj'S"pB8RheeQ&Zbc2Md1(@b`C)Zl)4ZV +8%*qLPTj#*m(4BPH!DN*BmJRU3RZ*ZcP8PX+5qqP&p1ZBa+0Q58mCe5*dVhMHR@2 +*4ZU`0TXPL@l4N[G#G6Zff*j[cE&N`cYJB`lUm"1dX+6lFHV2VAE-Rmr&[1eCf0j +Jm`"eSI$!Ma*4El3mNq!aB8RhVfq$`cdf1ULDK58Vf4BPXfK-8e@+P[`YmL,MDXr +p1CEmi!++&lXflmeTbEpNM[C(f`ASh`K,ZVp+E@2hQ1D4)S3P2G6YGXIS,L*FY+6 +cA54lbh-IXV'dj"HCLIhG*d-0V9R5rG@[`fErYPp#65-DiVdF3fhrk[STF+E&NRp +12B3q#rSUL,FI,1QX#6CL99Mh2l1[B[H,jK%G`ALQS![&NkVCmZaN[$RX995M&m0 +qK(4TrKm!!!d19'0X6'PLFQ&bD@9c,VNZZ5!Q)&0S6'PL!*!(Sa3!N!m"dFd!!L# +6!*!$&[q3"%e08(*$9dP&!3#[[Z!5X)6Ze`#3"3)rGJ#3"MCG!!#E#!#3"KXA"[L +,4EB,ImP#D-ZaKHdMLi36MPri`XP#Dq&&ZqAi*@'%,8S@HR[YZ)00C4kK!ql9@Gb +9VH'C*kG1q1V4SPf4ff`Ii3XRYlaNajBM[h[*FXCflm8**i`XaeV)[[KLC$Qfm+X +M#eQ1h,CM,@HXKI!MYf0pTRX3DcRI,SrF3PDXm!HmfRAYBpZ"hF+Z@eJ,!&R)E#' +hb5E$K@bb@cGCEG5NXipb-kmc,bFfciP0SRCpY-kADj1m,Xqm"RC1mVYd[jQ,ATq +Gk6A*5HIRjA9mmIh&3AlRZr$&FcPR,QpRGKjCI2+lb#Blb5DhMba(&V+3!0X#!!' +FGEVDpAD&2`E!SYGY9Zbl#'[KYr$&10Q3!*qh!GL#2q#"jGQJ9+pA2-r,)HZph-$ +&b'QHYfSBqpdqL[qPZpC2)Zrh["mDplc9EA[(9i$Ipb'U9!+8rKqm4lqNYm+lZ!G +!&1Ep-X@"qN*m&Z2mp1Q83[TUri"r8HlfjAG4%LqFpEe@UqhiF-[A"p239-AhPYr +d[*pVk4FZRRI4U`Ve@V&82rG@&@4[PIr4h2j-lhK-crZ`Ib+hFX8kEah2F+rRIEl +JrH"5VPC0MfmCD$6$hHA&Pr2#5el6re,TNb@mYbF2Dc6af+9D)#eiDb'&PN163RU +QX-!Uc3')6#a(5%ej,YmS4j`1&H4BeUZ-TSI4NKlamY`&ZC%9PhD,N!#T"VSi8-& +5Q%(k9lL5h2-Yj'XF(amP[*IY12h)[iM(0hD8IVaN+FT+5iRD`[DETdU9c@&a*Qb +1MqI2KkV51djr(qLrdcrYdMq9@dTVp@@ADm,rI*II(-A&j[qPp+qqY,IL%[*`)LN +iU9,HY4Cpl$0CAXKdGm,RGD2HP4[d(eYaY8`IZYEcAKeJ@)cFR061#l[%Tj!!P*l +Ap)rNVXKpX90bGcC+(aJYlfS@QhXhMMIVYi5P1#L8Sj+hj,LfA*[)"rPkY9'ZK-e +PejXhA)[MjR"6@$[YGe'j0[m-()9Cm&Xd8ilVTk!@efZR-&U,8DPBHIiCb(GYX9N +VefDMCGqPa6h&ji,@lleS-@i@6f%f,84a[4Um+Gal@ldjFmUhX93+SbJB,mCcTac +c#qA+c,24c0c`TZH5"(l0jDJB4@&eecHI&-kG'35hmQ-fVFYIGGAcbD&FZh+MkIS +#!B9CdDT8lhJXq%@2"HI1ai*EkmMehr)TCf0VILLUNPEE`mUbimT`hr2[ZUVBR!h +MB$+-ip-2ZaLdBB9LSj&icY``rhb9BmGLP@0AVR)P2hVfNTYqB[DM+XFZXXS9RUG +Bj6Tpr8aQhd+9+lmhe59f,98jpLe81ABX9$Pfj#TAmLX8MT*cC`CaP5Zm@&MP1Pb +lFS1UA#%V6*8Vq8@2CDTFb3e9VYfRR)eFjGLa81ABX96P#Y@M8193r`4lEC[$[B2 +-qCkGE-lh,G'jJU-Tm!8rVZC"r*eXVH4FS#5qXq'#[Sp2"Rf2GPEAr[+'L2QqC9l +R1lF61pqcRGRjRL9U9h"XTdX&lh)kPGKGm(!Gp+lSfje-KZ!	*NH!A(q-f+(+r +JTdPHk059R5@DjhZfmccIXdcdJY*3U)%kqDAA4HAS&-GXQ3M''R'j@Ylh$5STUmQ ++KEK"3l(GEdeKf@GCX63f'44#hU6mM1daKLA,""Nk8T5q8dHBA2N,8EEc3!Ubh8r +%b$lP%#rmcf*CF#lb3q2C39L-FiQA'pFLX65H)E-X1KEC@Y(ETfY&[i#EmB1Af'@ +lVk'Al+[jTI%T*UMa$$1dj0Me-%(b'UHHl1eS*(3iGcpRb)f,MR'k&GKad8r4ij* +6qDQ,"0Pi&KQbm@c[h"MIX(96G#`r&c0Di64lB2e!0&Ij9M9Nhd)aC-G5,@6IX"5 +bCk%5XL-A`T*I)6&,c[63*EHZG`k,B)HVAc3+VUS%XNZK!V)M&m"f[r+6k2,(2Kh +9Mle,aDr6YqXCZI59r+)%-i@[j)Dkeql6qF+&UXH1KD,(MX@DakjFmNTqRHp8,(M +Gh+qEpd@FEarIkqCkA6`[jRKPIYI&l4lKGBp`ZK+IkqCbA6bZQm1emlIph#hLE4& +Rkq*V-9FVml4fMYE&clUj@6F[FhCpHScVc1l)d(P)-GSR2c%Nj`rIE8*Qc)B"Ykc +CIJXTarE%p`kN'0'6hjmL+mhf0D5HSI`KXYV#KejMYM%SIFb'U9UDP24Z4+Nef`d +SrFb'Z2ZElIG4"TJ0XmLRQHdI815!XV$p#1Ade*E8f`ddfe#8-m`f&f@3!0P@@Pl +!GMA+B,1p&'@)fAiEj5bcLEQUa+9Qqb)+iS,YZbMRQ!eMHaMD3SSKRZ&Q%a2ALF[ +0eS)b`Q`E88DD$I0aSmaf"mTSXld,"EN$'pkqhQarMd*6E+Pr3aQAfY,+XI&Q3`` +6c)BKXiPQ@iXbb@b)@mb05GY[S%`aflY4TTVY8bKi3pLqML*(e)6YqeC28$T34fD +B$HmZ"YLNE6E+,,1KhX!1ffD81@Dl"DA"E'p#3Cl#pXFSmmb'R*Y[0Z6FJY3Qja0 +TJ#kV[*86fm+'0fSd'q,1Qfd,#NeLCZp%`53mE'p&@@5fpk0Jm!kf"e'@Q1dl+"H +B$EQ1A%Cj4"eCDME%ID(C%$FNE0HK,$2EVk%X0p[E8&DBl8p3a-bpY2dYbX9QqaF +8e#YGIY[-KKUdbQc)Yp9Q3raVc)Bm3ll$KYUfcQa[4N(YKHdM+1[0pK$+"V2K[5m +efhqLA*ED+P9rAf!fe+E,cBEmZF*Xm%&pJ`hMceHDl6A'0@"$RZ&pB2XXbP9QqbB ++DM*XU'hAT,BUmB4VcBDkKPb'$EQ+r))0X9e[0V`hYKr!K[IHCVEl8,DEl5XS0$P +ET6cEQGSNK3%IJ3dFJ%K`YHVAEV-KYfQZY9V[GD2CEN@jb@`BSAkKf6k)3U2Je3q +Jl$AEpe"HP0TUa,9S`,K'A1)PCN1p3jf%$EQ0Yi80EieiBEXGK3ClDpk(FT[C2SD +#QJ2E9e&HEVCr43'Qq2U)qi&h`)DkJYS)'qVb+mf'Z[-UXb&RIYeXd&pYYYp$!Bq +!l4-S[fNfF%6N2Q`r3!'r%epIFEAAQJfmi8kcSHlIC6E8apHC$IRrHV2KVGpJ0Z6 +C'mhf!46`#0LqJ!)H"a[b'h9!I,AL`-#%$E`"p4mfj$GU-Qb)qqeQ3ehj(E-K2j& +IX#&qm',B%2FlcICA+1"ZX(d,"AN"flqMJ&Z*VjrUfA[-"Pi(RJXEF[fpCN2YITr +C8,2q`'c)fcmd'plaEV0p#1@2c2DA++LaX(d$"Hm$fip48)[%eermr"kcSFk"jm) +'[SFk#4[i$lJBE1!!L"%f[-H(cBBD!"i"'plkSfC$R`#m"MC`#p3Lf&$c2QkfRk# +J9SK[J()#Q,#"'rqCf9#(Z[H3!(4ri!IJZq#ji,IJaq$&i,IJYAKIm'G`FI"0a)S +F"am"T`%A38d(*`H2"Um$e`A("GF#Td@Y30kLAS,,iSh3#d$G4Ld$j`Ir!1p!VU" +QSRkKCS0RJ&q!9i$IJ,q!9i"2J%H!2i!hJ#q!*i!IS1k!$i!(S2kMlU-'JJHKeU2 +'SlDM0U#rJPa#2`9p&13jHJ2SSi#rS@m#NS1kMli'kM"i(rJHqMV)Fh!mm"'3!%l +d9Y"635m&244`A[3iG0m(046e3bpe3(e&6`#m'(`8A!Rm&r`C["Rm&r89I"Ie"C` +FI"4m(0`42"*m%K`5h"&F(K`HR"KF'&`%h"HF&h`)("#F!I85("Hp!Y44p!E3%`# +r"8F"0`%R!4F"ad(G!KF""`(h!!F#a`(h!1F!ed#0"lF!T`#A!)G!c39R!&F!4`! +h!#F!9`)I!!p!r8FY4-e'V8D04Qe'c8G04fe'68BY"LG!c8FY4Je'l8A04De&M89 +Y48e&,8804He%c85Y4)dNlZ"rX[H@8KULQL(EYPkJK0alGV&(X+L$&JmNPFSYYV" +&IB@&,L+8*$+&c##cb%VND'39FK5b$MQ!Y39GLem5[Vf2l%iLrHi5E-ELeIQR'T2 +(GkZS#"$P)dYUp*U1"'elYD0Mpi"e&`jmj68CDJ23d9dCM#cUEUd'#CSUDZV[(YK +AYJj%m+hI-KjX(ET"&6")(9kPSpDUq#plH#P1A!4-UUTAeK'V@)-P&(U65l`3TX+ +lB&8kNk*Q,q4NbK-F$L9q8fAd1kMaGfLG5kP@q(9L`0)k&r@J&jiV',r-mR!2Mep +ZLU&4#8AbEh`f)0I-Tp-,XVB3aXhkE@&c2JU5)CkfbbH#+rheKqrbeqmHR"i,*ME +2H9F%Np,brCHp`I1h3EH'Ikc[XUhHE$"Yl9Vl643Vk9Z0'k,QKASm(l+Ar0&U2%Y +M)(9LZ65*PP(bGG9'"'ESf8M$aPXa2XVFFr,%JcM3`)&Ja'JRe6rlTQNebIHqGlq +hpkQNTHCGpAYprNHfU6AaS(K6)[F(lrIfINlM'ZVl2(*fk[NPN@ArPG-U4c@Tjjd +D"D!@YHJpL*VM2CakALp563eVJ3Qb9TGklK9PTIDei1)J`e@Tjai4AQTQ#dr3hFq +NRPp@ki&Dfk+[)CS)hTV8Fil)1M@k4EF$EB*2T*iE4DHTl5eb"Jd8b4!6ZGH+5&) +6A(4(40XLDHrj*G&fDSQ,IJ!D29HQRTp8Ji3Dj-*6Y!3'[5VeR#G+6Zeb`IRa(MG +AidL0(MeB)lph825pip5!GXp'03#Q911+'N+K*pUcZQi82'@MLI,Qlp4!S5Dpq0$ ++'TPkpPAEJ9VfP!hdDUp6)i`Dq)4aTfAKTl%Khi)pA25j!,DVZ@!q"m6f+k5a(ZC +bcAc(QcC*H!mB0rA(h#jIAApqE(LN2qJI(TSD3QhGNTqb'69AbLHB'IA(#Krk`i+ +!96"M)-FHD3DPq$hCm4-0+$94,)D5Ir&DrNT%[&PkH(66MY*mH8Cc`'XRf8Vibpj +Pf5GS6ENQYR058p)CX2'eDm@MD!J+FI!CU*XUApHIjqc+T6!RQa0-Y'9+Q+QqG2, +Fc)!IrkUlQh#km$dEch5jIDk3!0@Xf3Xd85#UC8ZY(*H,&ABr,'i@CkXKLmL1ILF +E1LVq9@'c@Ui9iqq%$ek&GDCkdiF3lHY-h306%mFqAQIkb*lXacI+DL6`(XbNkCN +bM!1KTD9Rdm!3+a+P-cJcX1)5i(qZU+',20RHK'aQ6r,$[4GNq8hG$1rJf*!!q*l +"I!EhfALI`hdU$XPMNJQM`3S0d9GUf$JEeX*QZG5cVHALTG26SJjX+rCZU`'b$XK +Tfd61YSUe,63Dp@Dmd5D9A@U&`@[4NK)M!`dE0aEUc9#VphB(2ce`cF$9[GE)JGH +L2j!!%am)HNjYFE%f8fc1-29"ZhrE4S0mUZfS9ZZeRQ3P!S09LKFq9JF%Hh59m34 +C"c,LKCYBdlNalYh@+T+mX)*I(6NL0X(Mb*&R)R%2(%&bG0H+qF5T*XAAe3XeR6k +I+f)&`31p30l49E0'!65dCkU1*cq9a*cG1Qk-FKjmMmiM(Tr%IhErQ-YXVBTMEL8 +)l@!`j`XFrkUl0h`ikhB`Z""f`6J3)K3aGRC4*(hUAUU$9@IBHBXGZ&MMK[fU@'L +(REPB@SEGZYL0Llfh@15'KA"i16"fE$h&cPMXHF9q@Hb@4Am4bpZ`Z"&lCV'b$U[ +K-(@*[J6f[Q,[,[E"JYTM[4Y@V@'r,PEFBEm,GU1#e@-r,IB&B(mY&VDKHi!p[pM +$Lrfmf1q,RElBdiY9EpMaLhff@)D((F$SdQ)P'kCrpFjI,)ZMhHGbcbpfjQ)R-IE +@BTFZdJ-lH,'V'$2a@15'0A@J#pK4Lrfk@0#(AEISG@!&R0jTLefhf'A,qhZa+eM +[lX8HB1`baRT"l#p'ka8lM,%M'IZ4p5jN%$[XS-(D#EeA@HpEaUjPV*VM2F[BrD[ +h0@#2!hBfS*Z#P3hB8iZpa&LBT2IjBXm`lbl'LN$H@iaGa)rXBmBZClf,'EYMX41 +BG`&MlbrYQIrTPreQ2eSkl8QfElJfZ)0Ei(-Km"$4,ACrl'Pj&[Hr-V(h#jMGiAJ +%V3(l#IBa`k$[`T!!qjJ&`ZKN3E1VTME'@HU`f)NQ1@eTTjMHi9lbdcYi5Rl`!Fm +Vq6QUeK2cYf`lTlr)'f6L%`9AMHbB'QL8AYU1,Z*3PpHdKe&qdfIL8"qD02)pGRR +edQZH(24@c*c`9QrDQ6[@HX$(lUfK*4-`Nlh[D9pN-XcEGh[IdUimq$lGTke0TQT +EYqFR&QTFMq05kY-@pjk9ccKd9H`p`j8%m+2GC(3pJ3'R@bN%$ZV@QM-iAKF(M(a +P"kG!a"N(AEGe,Kjdj6DFLBII"lcc8KFhZR@lAX!aHB&ZhKk(JmlM5mrJd*8M%JI +GbE8Z(R3`Ap["k9j**h$3%EhKI$a&UeYZ+LLS$A`64fPr)DU[lSAV1ce+phZ8qYr +P@cc+IAbq4H6RIAap0`Rhm4rTZHYq1hV[TAiljL2dl4jk"S2Zl(%eI)9a6&fKNA@ +D"#"EG+&(CQK![,VZK')U3mqak#Y*m'&k3er)K)N9I5%6*M"dc`iY9EU3!%PH$%) +A-XR,31K#*RR4KjihrCa9!RbiqSJZC%SmBC8"(kjIdKFbiEhdK8c`eaFbS5,3K8b +H-TdZC2+8`A3KNkI-T!ZC2%h2d)9-mNSRZT!!bG-d#Ph)j+QV4aFbHCT@S3ZC2%f +Ed)9-RUB`k'!&6qp0r4K2&j(3K8b51G+&60jI@k@#lFY@X@$$"6Pd)C1Rbi6S3LE +[+DYNX2f(965N-h+5'J4*j3aGb*681p+&6%RKdS9-5H8hAFL89-@L#jQ5QN#L#jQ +5QNkKjRC5NdCd)905Nced)905rA1kN!!T+@j+&c)P0F&$&c,*bk,S3UDN,MqK#jQ +51Q'",Q4+kQ`'ZT!!+DP*D@SV*C86G#&68KG3dB9-mK)TZT!!+IQd9@"GbUJhN9) +0SJZC8US4G#&65RP)&c+Pp+E8H8XT0VU3!#QPb5+Uh#P0j0#&6#P08G'&6#R9!lU +3!#QP+5LkN!!TT9%#ZT!!+D9V@@K,GNUAVY#&6#R91EU3!#QPA+F,Q9+I4U'HCNV +R5Y$j%@RP0adFNGCEdSN4DG8Q1LSLV9T!Cd5NP9Gd1%4DEd5R3U3e08E(3D49fqJ +FL,6HNJk!5'Z#Ldjq5'Z#L)j[51X#'$Uh)De,AqM!KV41K+!,QG,+AEU3!#QYUir +S3UDd,[+K2QMkXbM8Y8Tr"B8ZC**A9G'&6"Ra#TTFbSJ2d*4B4[@8,Q6+U%j4PcD +Mh+8,Q6,+0lU3!#QMGk3,Q6,#T3ZC-TT!S`ZC-TS+S`ZC-Z)VG#&64TI08#FrS`Y +Pk%+QM%k)S%CSjZp4k%+Qc--S02U58CfQ#jNbfSa*&c*P9C[T3UDXkMX0e'G9lqK +#TUcbQ5jNbLTrU,Z@9Cad)90@18SA-Q8eU8)A-Q9e'3eGb*39Vk+Gd&R9'1SGC4p +&S3ZCXVTdLLjNbUQZd)9-1A%5ZT!!59jj44Fbj94Ek8+QR1SAAFL88pfK#jPbbP@ +kN!!TTabJ#jPbHJZkN!!T*jj%&c,PG!81AFL8dmNGG#&66YFXdB9-1H8@AFL8dd9 +0G#&6lMp3k%+Q#[%rZT!!U8+mKbjNUP#HG9mRi%cie"lm4-k@-f0*F`D*@fh%K6Q +*(d"@)UZ3!0A)'Q3IC&pN,E)IXMpb!2)dC"hbG14!j"R)3FJcNB143j!!Cb(24Z+ +)iR13!-13!-14jb*()%FL4b&()mFJkj&MNH13!114%j!!%j'6N!#6N913!&14dj! +!dj(R)@FJCb*R)@FMjb!EN!"cNI13!214#j!!#j(R)aZ4H@36XKQj*$hbeph4jAB +EI1Bm!A%,k&9DH,@P8JjR!YZ'L40qKT%@#BlP`qTFDI1h*8j)$YRE'!FrJGp1"Nb +#N`hN8)XB@42iMC9+[643-Q[6A,'@'4I9pQ5fjQd6Q@ea[MV6-BT3I@0,,3kE#if +ikl#PBebdf$8ZVmIPhAXcXcM$Ed[R'9D'8AfK@HS%ePB[c@I'9BbIq-E9YI*LalL +RA-Q-&p@,HUCGi@bif%Lp'U1p0BXbAicD!HHC@0aP6p%dAq`N4"-@#PXmHH2Qc03 +bQTQ@[GP--MRTA@"FY0KSTYCQjL'9aFqS4AZBM566c@&TS3d3T"EbDRQarG",3N1 +lS"M0Q8AFJlUMr@`YYCP`F@cA,@fV5%0kmTDaYKiN4mYB&LCf0QM,lML,J(*(*Y) +@U`KiMGd@&@H[$pTDE%EfAUhcXfEB&8CKFdrEE9FlY9VV8APa-VB-%3Ca"D)I#"+ +F2)0(@e18bcDUfp@UET0QkL9[Z%a94"Bde4Yk$5500V8ed[)(fid9(QH2%XH[MQ4 +V$U2jZ0iSc#SiheRGmLJmGXI0@UNBKD@U(YkXULMABiN[Mh8Ye9l+U1AeQK+",f0 +08#E%IPYehD[d'&ELqN@"+a*(eN"j'k2HF*KJapQfHPcAZ*Kbf3)!e8LJ6k,@kIU +R))0k5(Cj+D1q#PX(*01TFG[NPL!r)&Fm64XeBeeQP8APkQ)TP(%Cf53KS0&%Q%4 +CYYfX-$BX"BUREDNQMbeF%Qp8RGe4)h@fLTZBiEG`-NmFj*K)6h9-l-L2AArp8Jc +Lb%Ffbh$e)MJ6,Ul6%*0V"E-1Q)`8-+`Q+*J4P'DPL@5rT*m4X)Bj%[JhMiiQ9XS +'lG)@cq3ck&,ZE+VASMS8BRHmX#ZT6hZ$E`4m5H*3M&N3)P%JLNL"8iM'ek(UYq8 +VT6L&J"8%)Pe3ZSb,INV1HK90q`Y6N!$d1#XQNh#SM#MIB@Z[+GqmBHE+EP6+NA1 +%RJc2UGq,d0@6FVBZ,MFMRI'T"Y%EdQ9GA8pHM'MBC&*jJaT[5RcKa4BAPpjXcra +bP69jm['PK*5PUTa)!B3X2*hTjfFZZ#ca9%&4VNl5JjUR6[&2M'CK4R-4XJ@C4)U +cb14P@KNNMX$-)5Z3!*A)+Q3eXJEC"pNA@B[XKqb2()!m$9Q(2"dj%(N'FK$b614 +Jj"$N@FLcN814jb#()BFMcd@13)j%MN+14Sj"eL2()XFKab-R)#FL*b%R)kFJTb+ +R)DFMcd213-j%cN,14Xj"0L$R)ZFKjb-A)"FLcdFf)S1l0a`!Ppr8,29JA(9Eap3 +%T0c6``k*Q`rIFC+6cDlA6++ll9XU&PDS#YG5&F5GPU"1$VG[Z@fS#+V#GUV#Rd0 +Sk9*54p[hSZ4q#Di+,kBUI+R9HYUQ3PeXhrrZai[hUVJIEV05@plF!9G'iCda+9c +DUBE0%jL),ZpD`c4ab8ppHJ-*Q"P#,QhNG$YX)-&Z"SbjPMD3!'$A!QEDH31*hMk +#[3VP$55Pl52BDi!p"pKTJ*PiC*8$BJZ*mq$VZU2"rE+Ed3&bqGN[6JG!R8qPZiC +%fDrT&EG(A03$9pbP@I+#'#IFT9RCRR#ACU&G*UlD+0B"Ilf#1kd5b$SJlR6mkB9 +4BQ0-JrII#1kZ1L$kKC)Ar[cLX0D&A48f-,A85T@&Q4jKT%(@JFF[6m2&B5kb)U$ +V3%92Z)[$8!HUHX*G*)JkJ)P"c*UT+51KLjRHVl9DRp!@fm,R$i5lhMlhbpjlE-h +'YQaXS+EYf()H#l86mcMk2J"XbmE@CQc(TUhBMSE2JC@MiM(f)Qp'6N9Z3fj(lN$ +Z40k!h)AFMGb$["(CM&b%A)aFJV`!fB*FLV`3H4&b'A)jFJ@b&ANaFL@b$EN+Z4U +j"VN@Z3jj#A)pFJ2b8Z4Pb"FJ,dGHJGb)["+j#ENCH4AbDZ3eb'Z4@j!!eb'[4fj +&6N014jk(R)'FLCb&R)fFJfa!cNA13mj(,N!Z4*k2E%6QN8PN#MNC139j%h)d%V@ +Nb&QrHefVGIcdA8M11-lUk[KF&,F'*Ee,AYPA0cLp%r$%Ka-"'KX0ZfcJ4"c,#U0 +f'82m&9IkGRh,aNIYBSD6FDc0fd81*pjejH6%Q3L2ab&[mEPbcLY&d@%1X,MG@lR +4,PB3AdAMbe),1ije3IM)%9hcN!$`5fp*,X+3!((dV'lY[3dSL'1mAAMaUcJHIC! +!aq0ip2bP*q)i%-La1,!FHr+q`64IlcNqerVUB%lf1Sj+U0Pl+&Nf2C5ji)T055i +,hiH3!$Yckpi@q&miPLbXNA%FZ!MTd(ZJJ56MH$3pRVK4DD0G(['V1*kpG8PprSe ++)JjELCkiGHR!Kh6Yr@3FZlfjFh@DpS[,3UeG5h8DbmGlBfeI80ljL6hV$S#pAq! +IZk*Q$i#&ciMMm%80a6hVA4q@PVX"iX!RPp(rr`qRNKbm5p(p`EM&bhCM2-QiicL +F!Bala4Zh[R,-E(%pIpIRq'$ZdDXqbG`2aR'%Z4q-i`Kc2a6(cjKlEabpc"dI-AI +[mBI@p`"Jl[[HifRQ(MrXFHEH'mF4jVi[M+HBHfmF2FaGa('3!,`VjRl9l2rR[I1 +$FBYV(,SaRQ6F84ci(!'-HffjG[A2")XEVMh$Z#[rPc'*1!j8KU1-k@!F4jM1S6K +qaR4kicKDbII&m93PlifMTqL*1%6H(f,Q[GGCQil%rJ`4FHcBC4FGGArLL*5$F93 +@rZYXJ)T#)@rh2*hi(UYi6@NQ(STMkmYF8F5"LilPk5PL1,hdZ(j'E`lEPjDHH0H +6R-(RdMfFr&JF[C@c*mrmr0Mc8TSppP8R"bcZVc&pNqlMcLpYSkX8GD(+Ap#&ZLE +)ZSp1J$LD+P(N8@*%!BRL%,8HljSiebHAA8-R`([)ETm6)!lGlE0a(1[fA4YA+pl +SJ6519ac9Y@A*(GJ(hVSqYqUY4lQJBXBMDdI[b`A(MJ-TjS$lI3Erqh1[Ir"$Z9A +[5&E3bMLZ8aE&lh%ickM,XU9a0Pr4BT'&D(mFZ-!bS8-e6qDpl,)i!I*9GPQF!(( +J8q8XmGehPA')p%Km,cp`qmm"bZ!9#Z2I2Q1MrF0SGbre5+cU26(jd$6fVV1p+IA +T(,1Ij$"1J$L@hY,F2B"mlPh"5Yd9-i[f+h1KdKG`S42j%A#K%rRU$TJ9NecSC2e +!"9jIEV`bJA2iAGd6R@Yma+QHIeFC4mpYY)IM1(Kkq*!!hQmcA[BlaR6F5M0H*cr +(dScAcXC,l,6hA9d%8pjL9ZcNj`#C&GYA3*kD&BXUY5YJ9N`aGhP-H6G)V[F%FhG +8-&-R319d+e5U`pr"`klF(cdGKmZQ(2Xj#(SaEU"015Frap+QR'1pQ1j!R!3EG`j +mY((R`1IHfVM61rkIk1Y9-IprK-'j0rB)('9Hqq*iQRR&$h+FHIA'iCcBMh#-HA8 +(iKEBXk$L%!HKl[m3K0KEVdM"IK`h!#13!(%i*IBqq(FMlKmCHr5V@EUIe&9h0b+ +f['mm2phSJ[N`DlYdk)BKbGVZK4ebHVhYmSRJ5RrpiE[mpEX(TmH#LFechKA"T,4 +mrf9[i%MhMl0#rBYpPfheCS0TZj,mk1G#k2'9hr5C104RHR-l5UpjFY"E-A2#@le +TCqjBk`%IZlH'QH2JlV'pll&*c&6JlEZpEqP@6[JqcAPUNqY'@VIRl3jEKa,R58+ +LF9FGIZI,9pHI(aXHk3rkKiHQKP"EYq5Rl2Kp9mU(#P!c-6CQ9pfVVf,6a(5lcq+ +YIZj#F1'iH-[S#2"H&bajBIYbF((&![NR68qCRMBpBhV@p*cT&DCAQPjPHVAT0DE +h-Ef[kE@Qpc1p[qRb(A$PV(QRe#9,Pif,LaJ)kc66PjKHCrVTTJmdr3c6"jPqTZQ +$64pLqPQQRfhk8022-AfBkF002pId%DD20(f8kD00(f0k[HPM6GI[10ld#DC20(f +5kC00Rf,k901RQ6lGp20-Rf(k600RQ6lEp$QQ0jJqYjYVk9HrF$PkN!$EmdbIErS +#daHDIVlTMDER6@mbr8,6QlYa9k`jFrBfP53FY%"kUJYEUA2KrE1iUF`*&MbkLe& +e)IlbA'k+3IM,FLEdE"HRHZAb1GhdhJ*%jV-)A1DTX'Xr@DZ%,ZZ1m(qKk6,[K5l +c9HJhQBkV,4lrF*p$-ZNMCHab26IFaA+TRR%Abk9l`PdXph$lBMNRh'9DQCja&mX +pIUNDA5cR,,Y-Um,9!Ap8`*m8F)a9!eN(+R[#A5chF)qjLqAFChFS68RhNbrYV(@ +SZ$ZaGHfhIp0l%jXhHHb!G!I6GUf(lYfrG0@)*hYiKa)'21@ZHHrSRrEKIkprp#- +lrAIrX1jpFdcGSI6i05[b$U@S6RIGS95Xdilb1j4F#XcG$E5Mh$*hI-6F[9pB[E- +(!(0h5pfKG$,[6fNClP$D@$Y#9bRU3T@rS![eR30Cpp%*%)Hj3bQ+Sq[HRQ*A6(B +0R3$[)EYp6S!ir$Z8pXGaV0XREkqjMqe`qJiPldlr)HcL$LA[`%FEhNErd0l0pqc +h$rkXLSQi3kQfK[r,r%1e6rS(RJ#2Ekq*hq0`RTNlP+)iZZj3+XCKlP"krMeNPm8 +*N!#[XX[L")J$(pe-mrblbMM8(8V2ah'+D(1(NRYl%kHm,elQk+Qh`bC1bGbGE*X +i$q6p+4A%*XjcrEXEIr[-*XklCljIe0R%L9[,8mQYj9QlYIbj*hD$IELafqXCGh0 +lTUImcHfBijFhYaIUJ,UUHN[[X)[DMp5"C-qiQpZchlkjh89fK[q6YpF,pZJN+`+ +k$P6dK,Zj(AA!)AGcZj!!Q#0B,88kbADb3IPSUJZF&1UD-cKH&fFdkLdC6PD`rRG +Efb"`--Qfi3b1(jc!QBakUm1CJ[SbKc-90C[Kj%kpYm'CKTTl)5m-cR68#SH$cGk +9$QF'DTA$`F4BYF1CK9VMF'DMpR%iFe$l1Kc3L9U(-aHeRm1CKpVIiFa((H"`&U# +HjR!@SYBj(+c41phK0+)1G$MBd(''`m(QM%%1Ca(UQ3i(%qb$(Fi5e#%1"eZTch) +i,DKR1jbPU%-G$[B"RH0`X*PPQ-0CKMVFi5a(2GIKV%!GiA!`PMV5i9b-1XVKV%3 +GlA$D8-FiR&@Sp3jR0HTBKl-'GCc$@BXkhZ'X3jhJF#j"RHK`eU01FMJE8#FlR%Y +4TcLFbe#R1T`AS%jc1*HM6RFi@!"aRX2"iXXC$JHce6-G$QDjCcNFh$dcfq&JEGF +FKi-GZJd1"j[EjMSFc2l2FcKE81Fl(-b8,h!if%qmd1&JSp6j$JHl6aSGcRE8[-2 +CJGVNF,$0SGRKh)#kb1&JbmGLKi1G"%XF$PB5A1"`X(1KaH(FK2SDemGk)HTV-ac +GUHU)jfE80lTqeel80jh(199(q(Uk`XbUE&HBF@aAQ(&X9jKaE&HBF@aAQ(&X9jK +aE&HBF@aAQ(&X9jKaE&HBF@aAQ(&X9jKaE&HBF@aAQ(&X9jKaE&HBF@aAQ(&X9jK +aE&HBF@aAQ(&X9jKaE&HBF@aAQ(&X9jKaE&HBF@aAQ(&X9jKaE&HBF@aAQ(&X9jK +aE&HBF@aAQ(&X9jKaE&HBF@aAQ(&X9jKaE&HBF@aAQ(&X9jKaE&HBF@aAQ(&X9jK +aE&HBF@aAQ(&X9jKaE&HBFG`!A@'1ahD&'FGfK4R(GS8CahD&'FGfK4R(GS8CahD +&'HF83k%V,&J-L%``"Ej[kPei!5+D!MFi)'64&,M"%C3URJ)h1"R8D!VFi'!rD$3 +&ER#`365D!MFi&DM4&,M"U850TX!06K9U0!9ZF+T4SbP`Je1$'Nf"'j`qU0%8Z-( +TLaT0J4ZF@Y4S#YcJB!Pa0!9ZF2UM4P2J"QF!DM3&ER"13ifQ`!e1(@Sd"@j`6NH +0TX!0cN$8D!VFi*b"'Nf"'aaaJ93m"@j`cN50TX!0cQ$8D!VFi!a"MDE!$FjCU0% +8Z-%j'c@D!MFi3e'M+A#$F`jU0!9ZF($66$3&ER#'SdC6i!ERA04S#YcJM%#0TX! +0cNM8D!VFi)a#MDE!$3kfM846i!CR$'Sd"@j`kP'M+A#$-aBeQJ)h1103SbP`Ji0 +Y+0%8Z-'CJ"T0J4ZFLDM4&,M"QB3D6B%E('aKLDE!$3kfX%46i!B(@eLL+A#$Jbd +Xd45i`F%@PQJ+h1"J#dXd"@j`X)8PQJ)h10M#%Nf"'aaXBBQQ`!d1YV"%8q!'"eY +BSLP`Ji-Y,0%8Z-("&TCS#YcJB!Y,e1mh1*LhLkE!$3lQlD)TF)1$HEYS#YcJB0i +ZQJ)h1*LhLkE!$3lQlD)TF)1$,II4&,M"`9aH0!9ZF$"(&r6Rpme9##r-X`ApqAd +iTpi)Rq!cMrARK6mJPQ8ib4Gad*m2q[Ja$[Vc34mraJRlmc%1q[0"[cr'3Amqk12 +(11M2"hhm'#IXcmFik-m(rIiB"rhjS)mIik!r(r6aBacdji-qISb$rRc3aipad*m +2q[Ja$[Vc34mraN&r2ZMMacMSc`Gpr"JRl-qRAX3*qr-a6YLIMh(#rRb-%rERBjb +`2arMK2hj'#IXcmFiBAmqaJRlmc&1f*q2FF,qI)`6pZGMR,!r(q1%rINB*qc2acK +KIcl'#I[c-8lBRipa`[jmM"2fjf1FX$mIii6pq4J(rIQJlalMS$mIp2&MR,!r(q1 +%rINB*qc2acKKIcl'3Amqk,[(11M2"hhm'#IXcmFiBAmqaJRlmc%1q[0"2ch'3Am +qk12(11M2"hhm'#IXcmFiBAmqaJRlmc&1f*q2FF,qI2T&(26RJcjdM)2qI0$(Mh( +3R`rkq$%1q[0"(cr'3Amqk12(1'&r2XC"IclSpmFik-m(II`B"hYc$[ERK6rfejb +#*26Rpkqm1M)#bcKf"*Cal!JXipJ4@-Da)l#-BdGJ'FH1`$+1(B&P($X#bcKf"*C +al!JXipJ4@-Da)l#-BdGJ'FH1`$+1(B&P($X#bcKf"*Cal!JXipJ4@-Da)l#-BdG +J'FH1`$+1(B&P($X#bcKf"*Cal!JXipJ4@-Da)l#-BdGJ'FH1`$+1'f!%PZ1a)l# +-BdGJ'FH1`$+1(B&P($X#bcKf"*Cal!JXipJ4@0pikT)`!ZZ5qC,XZK@d6[l1Pkq +Z2cmf20)Ip!m263fKYQl*6kQVje$jX&"XqGLfSF,)`-J1MH"dIQKrb$&Z++5[pJr +i&q9ZAiiNqrfKcAhccS"0crlDN!#H6Fq-BcFp-iiEB0-caf-h2611hI6-1(E6-q2 +B6Fq-BcFp-ilGp-`iGY-cipK0ciaM0cdcMYhdc$Kfdc2Mf%h2M'-h2611hI6-1(E +6-q1i!6BpFcafdc2Mf%h2M'-h2611hI6-1(E6-q2B6Fq-BcFp-ilGp-`iGY-cipK +0ciaM0cdcMYhdc$Kfdc2Mf%h2M'-h2611hI6-1(E6-q2B6Fq-BcFp-ilGp-`iGY- +cipK0ciaM0cdcMYhdc$Kfdc2MZ!%f2A-mGY-cipK0ciaM0cdcMYhdc$Kfdc2Mf%h +2M1-'f26-mGK0cicM"YMdc2(B6Fq-BcFp-ilGp-`iGY-cipK0ciaM0cdcMYhdc$L +RHJQERT2H*AhR"i!(pqkDrpd84BFprfYhH#XhkPj'p98d[Lbe22rVIIM)[bEkTVF +NZf8F621rNrF0T[Pkcr'jePF(Fl,A2F`XaYj$b5$S80S1&CZ5A"Dq$b&hjYDp,I# +rF#aCK1GBQ[pe#FcYARZZ6Y2FVLc8fV98TpeEFl[%Z1-eDZi2aZd!fU,Q@'$ZEU! +YDTDjib2QlMhqd2SH!-cGrE0&VBHjLcJ1NRGrqpR*[(H!E$rEYdAiUHeRqlF41`) +BGqqUM-TH&CXbR"-E,Y`6Qb#F!"XFC)9a5Qa-U#bmZ)G0I4@&3PlAG6X![XFUAP1 +DLBILf2Sb9a4aB)[aX(bpfLKA6MqZRp'E`qrZ!2IIib4Rm,Pd$bFr&NG[jHc*-cm +rpVbbE[hJ9heqAAhIT2Zimd[Ek#T&ADMb&h5KVJQblU-6)!kjC9kY`BlLd0ZVECp +FGJfG!1mKZhe1J$KdYmr'FDcE*lI$Ah3JMH-94jN3bH9fB"pikrVFUVFHCFI$RCSfile$I +@MYkA#iiG"e*XIqMh'Ic[clhq`3rP9Ve$dq4Q1hcm(SIcM,SX@aTRma8Y&PQ)pXF +KYm0MEIR*[*GG&LG![XSZLa-J$RbdeIhjGj9aL24)I#mr6JeY,Q9)Rp`q4@[jhh3 +i`,BSlM+8p-Nhdc-EJQHP2%eI-(hbaMYBp#im8r,FIMSSl25Zah2,%ZBkL0l0Ik- +$$e5FU41CTl$XB$)mYcq0dhLqb[0Z1X*-16a&R0U6r0q08Y9SXVXKq5GrJ5eBMV` +H16,e[&HA-G!#CI'pY)-jr#PXZ1Y"I5,@a+qPRVGTC&&ZbX(RkFD!TDRRK0r!KYX +Gj*Hj"f9+kVRL8pK`VB2br(B(-h85'ej"4I-6P0QTjkfkeJ!A1FJ[U4cYNhUqjdC +XZ-&"IZ1**Qh4l&80`Y80mXXpKP+AH[Ej3fbiXd&&JlSi12@m6l2%Z+a"IMrqFmj +1@*Gk[[*h&E2r0TMM6UHHVIq1$GFcU'JZ3aQ8HVEm(6EFbk!m8HXTSpi[&0!NjAQ +V)P#H(p5qHYc%S$,h!bJ2@ZCLFcqZB&"jJdT0f6&EliQl&e3dVp%E+Xp2UHiKGq3 +h"FlL2Cak[PYlYA(EJ[bbBM18(Hp8R,KQ38AcPXkV9DXhM2X9&#BfNAmQpC6A)1" +L"9A@40jlDe,2pp9a`)d++JY446k4HRj$HBD+)ErTZMYK8Z,TIH@Gf'"ARQ+cGp, +HFm#ri)"Bj6IVdhKZ6MfrrcXii0B%QCijhRck,aNQ,NX38P4MNDQc9*F'9H0)GCV +UB2VNp%(2Icm9VrCJ08ikqllR1ZB&EJ,6!h1VhP*lM[M82r'HU+Cp`83&,k`F6rj +q*fqqrK!fh+fJ2*'Y9(2(XJG#AUUJ22qQJhRlRGK`Qi,br%DRjMji+cCFSb#rqFS +LURLE$Q,$r3Rb5reaTqC@UX,JiJ6PL9T)Q!rSrJ$FQ+!m`9'SUYbZ14jFPD$H'rF +@8-hpV%CVrF-Rjpr3UERI8[5i(%&K)PqSUQclE@bi&8&jBQXreGbl&61Z3j!!Ab0 +[f(Gck[PQe8kICkFqf+Qj@m8Im#)UQVC1cEeHH+LKb[2PRBaU8)A!P3I+%e@5DZi +hYf,$A3H+$B)l8Fepp5mUKrak)ejDCmI[L[NJGq3hPPUJDqlR9*R"l16A5VC6cI@ +H[NhP6([1Q1jNafIr%KXZ-T!!hcK99Pdl(K#8pP`TlN8epkh`5RPeJIcDU&1kj[l +MK"j,Hki5KkDDHkGfLq#b![R0HeG@Fj0hX440hP)J[l8hG@VZ2hd4"ea2),p,k+$ +SQVY*Y46h%XLDfr#q6Xh0I8$KbCVl-EUSU("A@-dPKLPBmal2[iHkl0GFhh-YZpP +ZK`(l09GlMRL8&k[AQqZD'r"9I+JmQV-L8h"lJFV2(h9UlYe#`E8<&mSB-TZjc +)2IR09,ZDDZiV9AZ3!2'U$TLDqfQGbBXE#Y3$I,j6)3DU&[V62EPrl062&k["iAG +M2(A*U%jr8CMq")kRHNjapPI$a6qjFc1Y(eechrPC(2aZYrHj6PA*+4+mMhSEF%H +UZIrmG@cqPLUF69#hdqSdpZ,KPJ)9$ITP9(0R+pY4+99')I0dc88PmkIqkP@A+D0 +Z8FAd*klUpHj8FpqZGeR@pQc3@e$04F&,E-[L"%1NM,T$V(Y&&JdB%Y@S+e4[GGX +S-3ere$ahZjMfa9NdU)b8(9[d,V#VD&lAHE@r8%F$R%*qFmAYUHC18Sd@I5$eA8i +9d69hM5*"(LK--$QUZIe8Ej%(kJ@`'B4UlJ1U!@[ERYI6kp%eplir`f&Gfr-11LD +kjNj9[`05IKqQ(ZUDqcP9-Y(2%C99G*!!CRfXbh1TGb-UkfFmVda2bMYZ0GIhA)1 +erZH9#&4cYHH)IbD[aSKVk*VVBlBYdJ`5&DlLTQSmHkSI*AEEh!h[5,kZfSM!$$` +[)4Yq#1Y0h6Fq'XAdI&#M0f5F#2d+kSJ&Q1[K%0YJjpi"AJ$XbQIRPhb5%LfbeII +d183EQ218492--qKI)8484XUepiU9#Vj$,i!k32RCmMJfe!p9#j%[9!Y[&!0!YLM +-qe'Z6$frXNH&`Frf0f4aTLj9"dQ[`NKFUGUZiNaGr-[Br(89+I8(+-kr&Zr`Hk# +ALVe5R'rq"faqPh@QDTq+-h[Ked33r&U)ESQ+-hY-HH-2cDEZcZ,-hLJQkqqXfD$ +DSH,-rYKGf-4XY+J3)QIZSL1@f'a9KITqkC06'ccrRI#YIZfUiRZZT3[brRH61rG +D&QV2%Bp41qTPe9A&aea(R2I3SI#ZX8VNpaFP0dEr5E'Br2eLTEUMlpAJGF4LmVm +LVZ*h9ImIh5bURrQ@UVIZ"RYIjh@TIZE[9EI1hb@8PN9P4rk$RmIQphA(+EF8mfR +kUAZaqE0)BcqHFBMmNlq+cHriMU964F`Rrq[L%hKA&Fe,-ZD6Ie39`TqC'#rZU*K +2df[&9G$h8CM)9TAYHE99%PNhq[A8D@)qqI[9$I2lqH2&CJRcfkU5IJ0r[(L8UK$ +j2k+H*V+CK1q"55bcDD9k-DKY+M[HPQ9(dhAU6U#c)EmVP&qUGZ5I8!GAj$P9)R" +(pClj4p3hfjZp`)FcETcrZ2LG2bJfr[k-Vc80r"BfriEFmH,6P'XIK0%NP%IU"A! +lL1*VqCDLZDAYH5AXL2KDdiq,UD2(,EpVC99mV@P!A"6p-P9Cd@P6fG'd9P8&3aI +bq`&FL[KDrV"Bc'eCR1K8Ufj$rK2+&23cj6GI&9Lp@Y12L'QM2b@r694*kMBdr55 +pMi6H9(jh+EY9&ZB2kYA[D(YZ9#GGaGPm5081R%*9#2!"&@Icfm9[hYAfr*!!Q,q ++XrQ[e*G"cC'em#[UUe#fIeAE%$!eS0J5-Qp509TLIr65i2VD-kc6Q#bSJXQKRK( +Qr`%!N!30#e4ME&0SC@aXFbkjFbkj,VNJ*L"6D%aTBJ#3"lp+!*!2!HR'!!*2j!# +3!aErN!4069"b3eG*43%!VllJ3l#%l`d!N!8#51`!N!BZi3!!Dri!N!C92!B!a6) +h)e[bY+hEMK`R@r&E`JQRA@jTYYN4IQl+*EHCVAeH8GPkQpTah8mfi4422CR+$Tq +rm8rjq4djlLE(1X,)*M`**EH2E(,mb)6Y)rX)2m)fZdeSqmMam`JRa`NMV)[m*YY +1k`NlFTa`)r`*[`JRqb4d(pPN$qb08+$$(c!eZmlbY[6i18Jj*CcmIX,*FE,*FI, +lb*lJ1F&IepQSXdPfAV'ADefXcR0l0SKYZZf-RGcVBJZlcGakRGFeB!YVd,1Yfqp +BREQFpMD`JEf$cP"JAZIeIZGhC*m0NLa(GRiDBC[FcSrX1&Nmblad1A+,PjfpQF" +B!!&hkhQGAAEjf@NmZYRPKQbch-m)Jl`MqpN#`"D!J2(!Pqph&9cA9NUPB'HVe,* +[`(jHU3rUm'r3m101AGAl+GL[+hAa68V0(r&A$MQjNeAUlV[TH0l`a"rhN!"G0XS +`5AVMi3jZ$H%3bpr@@qZ01VFr2(h(Sk*5kirqRk)0D(06Ye4[bBfNQdrEH,)DMP6 +%1!XPcGI*h2f6U1bFR`cc4K-h2IlPd#M($r16N!#SLr,G0eVTZVGZfA)bD0RQ")! +XE-9m!VhA`!BcR8p5fC5!3,jlj)b&26cUYdR!R5Q`8pP(-rl4ID'DHf('GBb#HkE +#MF$1CZeJDP[P[p)#SET0HdBl2'ZK@S)bh+c8,c+*&(fDB,B@*impp*8,E1l+YG9 +j[VR"f[ak2[b&k)plZRri()mc2jGV4d,2lcl4Ah[bFFi+bP#i0%Yk)h1X3cDIP02 +i(E$R8ZflfY(8(k[B%pIaJ('b%R-UbmfLdb4BhTCCIf0R`@ieMD,Tjh,Tmjf3!2Y +Edr5Ie6F'SH(r#d#NA%Z03[0Q+f`eR+*YqZmN-%KJkBP0dDkKI!lp,f4UA0iUHlD +TS`rD&k*P$ceqbi9@8)VaY@8Ib[QGZ+bV$L3")1h`#DY)I+AfE%UE&CAQPQZ8qT! +!6[kVb!3+YF+1)C88#`FA,KM-K@f$(3XlRSa[J*dA@,Mjma338RkHV(GP6r'pmlh +#0q-fP#lXc2RZ4V-3kKNV++KMS5XYTb1YTpfbCa'TLX(2AR%03XmbejM1bFJPPV2 +T,eGUZUA)EL-S'2C8UKUpd[!GbqN,MNF[,&UK1h(P)YHCb('aFC-aPElf`-,0S@p +-j0I9(i4Z@ErHh(+ckaFRSZFA#QB3k$NM,%f%,ZLhl1+V14fli[V*jL(b"#X`JX! +Xpll5#S6hpKD"IFAY@T*H[(LUYD,JMCh9eC*"@QQ[G"-5UU8MFP2T#1mY(B(Gc5f +RS[UkY6[G'*64FZHDp[(3NmfY8j9HBIKpCUMRc6!m@HT&Z,%pBhKH&,Zq0!@3!%P +N#4q6f"JHbi(D"Z8i@qpLrL9XV!Dl"Nk#&4Q6'!1-585#Llf99`K,BP086))68%` +L%da-)Q0),!FQX&+1VVC6M)I&`KAKX#HfVjN8$)X08X$#FZ"Jc3T)@)i$##Y"[Gd +C`f!5QD"J%TQ#B"%D%Jb8L18Pdck1IbkUK(dZ,XDp*%J,RX6BZZCc,@&G'ZKDd`8 +UaMQr`6IR6E#YA&'&)4HABTS,62$-459BjU*L(%Z#%Ma))Y2@L,%VAj`LETAL+Sf +KH*8[HJ'VNU#qZK3`+SN"RiS"jHk+FFP&*CMNSP)mbVGaJNA(VYJd4FL+S598+JE +(d&5*e'*A!Qf"5d8S)99[Y1[+BV4L9M()!&Fa*%'[[Kj3p#N'TcK@M%l!V"LD)&S +a0)De5Q5#)CA`hYk+8Dj8[b,89B-hGTD#AUPA#XKALGa8ZJ)'9J)"`TkS[Qk0)E% +BQZ"L-63&aa*H*!KTmF"%,E'##FhNd4ekeJZYXVAeK$"L8'CCIqK"K1b*2$9c2%V +dkf9j2@1L1&AbQ@4D%4Sdd8UJj*N%9G*dj#"*XFMe0,eLN!#NjJ)UD4AE+XQY"ii +e`Cj)bE)B98NeJUG05&fq)!(VFR5+%18,LL"4$Nr`[K`G3hj[F!+G[6FSH[E'@UL +X0%84rMI'1`C3LBp)3$NZDHebG0cGIF%$"E2YA`lm+3+N2,,rJX&bar5Q0hK,QaC +)6QmX2+F[X,mG%VC6MNi)6cQk41$,m6'(l`hZ,f9*EPU`jMA*aiDQSSq0kp%XE'K +C3E1"U94NidTL84b@bM*aE&kBLD1Uj5f,4XA)@$CbN8iiXK%Thl*a*FD9K288Sm$ +&E%J[bE2"2@*J+E4D[T,1&BIeYe44kiUM91e+3LU&648['jG!R@IL2!9f,Ha6BFq +"I4,XNf'C`AiDl,Q`Cm1ZJEd3pJcB&m0H#RXQl%YJA`Ul'[D2B4N01arfkE#8HMd +X)deR`Ci1qdaBQG)12J[lH0JR`$i4pP@`Pm0H!AXPl-YKA`Pl&HbVB9m$qc,B9m" +H$IYDf0I"[Kk@1EFh`Vi*pXf`Ei&p+qcEB0m1q`lBGm*H"RX0l,YJVi@p$TB*dME +BFE$MB9YKam"1J*d)bcKj#f`$l,4SZLe3@f$r",B*PX'Vjm$@`qCJpi1G$,Xrl!' +`8f#R`YU*f!0K(b#6@3`KbTbHQH4LC28`f-0Kjm$+M&C`&Hb$BBq!24*f,LbcD!I +$2Kcf%0L,BCm,qd$Bjm%bEGF"1`[f+0LMBBq"PHQei&VBHE#cBDI$2Jbf%eE'N!! +$cYFI"$X6pRf`liHp%IBQf2I![KG@CXD#[E!IK(dhl!f`Ym$H#RXEl)GJ2``VmhV +"IYL2`Yi1qc(BMm0q![D6X-c`A3ql'2D2B'A',mM!RJ6lD0M(`#k$lBBp%CCacY0 +J6i#p"2B&X!YKA`Ml)YMjX!YJ(`Yl-Zabf"@`Tm#H#VX)pMMBPE#VB"m"qdKBT[c +-5*hAi9![qSmk8A2kJ&kRYm%!X*IDJJ(8P4T63fT*IF&1F"GFTXjJ'Ed2jY$Rp!D +B$SD"(q!%@!4fdH[d'[e(,i*lB!Zp!6D"(r30r@Ha!lbJ$fh[d%[J#(J%eS"2B"U +B!hk"Vf!HH!LHJN2J'lh0S8r"26!4I!8l`6c`&i`#%m!!-!eX"UIJ%H!XZ!Q1JJe +J&aJ1jS)0i!Mm!Pi#Gi&R`%(!'l!DlJ)f`hr!II!GV)HAJ12J2$`)22BB('QbDE! +0X2["6SHG$0X-f`Bl&[BJf*Q`(E$d06dk1aVkRSAbKl!Cf#cX40Kkf"aX!f`EE#Y +X#bbG3IFFHJ'EKMU6"bIP)CE(d!rd!$J"AS!IC1"h114[XBYkf2kh0H034qT*lDL +KAq&3NpQc@Bj$B0YK'f!lB'I"(Laadf#R`cE$cSKUQ"`I`!)'(mcjhpPm,m"a@eI +-%A4USqM9C90f@PqheD*'NTc6fh,kfXBEb0IZhfT4(2bqIcG#[$1QZXdN[cpJjrk +*3JllNm@"I-fc#0Jc+ZMYQcNdFD5)LV5k*`@hTB6QUfYUAp0Gf'39U9bZc!-+2#& +l80`+KHKB0QXC,GKLV@@%pLk3!0EL&mS"Gh*3-5`c3l'!BA*3,5a@SEVBI&"2BC` +F9&)B+JGebLTr+*X`4!kUT@A6+*'@MD1Q@I4(1820iA`1&Jl2q4YB&![1Yf"45MM +r$S[#3RQTSeQ%*6i8&G35I#K0q2'KXU$+i%1YSIli8*&34[#K6U%Ni80&3XR"KiU +,BS-203lP"Kq+,+S02K4$mX@(!JMQik1@U$AirJirU&$ilXB2+JqqIm-2DJIY55e +4TI"4#j3aI+K6U&Ri8(a3Zr#K+1NbN3b9(e3SI#MCe"`ILLfU%cl34&@H$09Xe#P +mp#J+($l86K3iI035K3lIAq%(93hIRILK*[Mq#6qSBIKqK"rk#hJ")m!6I23KUKS +qkSZkKXmUD[K3Ue$Fm+&JSE,K3kP(QF1(!XbGq-!Be$Ym+0f+T&R@6*A1l'2iJ@A +LZ`XrU)[irK8r5JlVL'ZSI2MS@rS,(r8#(r#KX+(-i81C4+R$"di3L`q9('84(qS +ppF5(HSrUL!rm3rA%Kj+-)SR[-rK"%FAh&F&HI1!%+LLq(q)(M!+L`@[UJ!qX"!I +`J8dSTIM!!T4@I239+LSqkSLULJpe%884(`SJIRbSkkL$q*K-3&h%4bhT6AcJ")S +X2[!Ep4(I"Z%,q-"XF-P5#C4EI1!iUL3qkS4DLBmq4`A'4jhS,hc8J[VJ!`p3-[( +"PTMSm08F0MqYYK`VY!`E8dbl8r!!U%ZV3p-[@ii4[TZq2d&0fVBYdDP"i6mc(Ge +HD(9UCARZ'!CZEV!E`Z#pk5f&623@@%&kbhdl(`[VQr)k@D&,9BY6m5b@+(kVc88 +CPmVM%eJGp4Y&,pR5jKAB%MCaE%[BqY,9'%"2&S9*h*)NLLeKIIh0,1A`fGPrNGT +6h$5CQ8haCbVUJBU6)ZM`-9[#VY-He[kF'KVI#PRT63r3m86-+1aNNS-NX*rV&0Q +C4F8*BkLS3mE$KA,lmX!mJdGQf%m#@QF,rYQT2bI@)[SB)FF'f2Nr%j!!V,+!4l4 +(SlT"aX9'P[0X!h2!CrL'[d9PeSYVV4Q0&p[!GUaYE1m`!lIIahKi%%El(1GlAK[ +%K6N&qPHBJAJN-Zr%Xkah)jcVV0ibdTe6G#ZqS-f"1[!NSf!Lc5$IPZhUVJ-aR%2 +[8JbJQrq9kBjq"f2BCdGKmpIPfr4dj4BTdH(d58+DZLh5R'0V1)*F)4T*AQX@pEJ +AmfYhiN)(Sd@1'hH19bMaQRPepA9"b4j*9MTJR98`3XYeN!#[H105,9d[ATcG[[P ++!@bZl9IUf+&KjVH$-3iS@((SXHdDTP6F)eIQ+d8Ab$@Q#maXZI&%r53jZ-CCC#a +FELeC89b8VS4TQiQl[4*Y!MY2V+b8U2DfXZIkS8lf[X#`EGGe8-ae(R%M'UdR8T` +kUBjAaX'$H&3mk%jR@eV'FT!!3I2BRH4X#A"R"DlDdAh&1db$&1S#Z#l0V4phfmU +S8bZ$Yj!![3PHhJjNFCf`HU36SMK3jS6fpXKEl+am[2@@N!"-)P*SN!!3pCI`VLp +p[5[1Z(!pC8Nh2pJq-@d5"4pk["*4P%XSKU[m-J4T1Eh(`"l0''&Q6hV9lSe5S%V +QIVL"K%ZqFrBceT%0FHGM)c#!Y"6ZLjV%A8&pf2m3A`(UFD[&FUVPk!%iQJ4Da14 +'8eB$Hk@#N@%DR$D%)`8'U5*-6['j5+ZLG-3K5c2@Q8("Yl``ESK#3$lRq"DED#( +'e11FLC[L+K8M6AH93dUm"QDhB!ei@hkc3i+p#pE%V1"-e`PFfk`,`[lHJ*c[(0a +r8R[c6DB64Rk6d,*m)h`qTj2"4%QN@hYJj+a*YHPX8h1YAY[8f0Q)hqkfG+H-9[P +5cQpRGB[lkDd+(X9UN!"FBHGGhBS+TdReU9CbjI34A5Y4VFK(%Q&@D&-q%PSrF[d +@&H`h-aXp%`pj29$+ab4IL!bClYY4qA+Pa$NMUU82B4V@Kj!!B65C*E-118`DQS( +4`8NYkCSaicV2'8[Ih*(1R)F(B[V"9c20hDq4jqH)I"5[Er"09#6+jKF65S+b9Tl +lriMZ*Sck%Cd"CHDLImpbE)$N`[bdQ80RN!$8$*!!mVS!$K1NCR,8AXhSU*'3!!L +!FHlNVNaRV$PeKfrQf)l"YQA-d(G[0[e0JFj'DmpIhU%[dZS(lp2U0kcUbZSGV59 +eKClRhi,D"5Z9YJkEX!jkY'E"f[J9"ST+"XTQr&5QhM+(8LYh2kPfl0JaBX2RlZe +44imH(EG[`240l,pKeki6GpaRUcAY[5[0hIpJEhr``B5%21ha4p3GI1L2p4,J`pE +FJeNemIl'`[l%R@I-q$K@crfKV%(`Z4hr%eYYEqAq4c`4@rf*["e&rJeE,ILG6If +eZ1(iAVCDblRUMmp3(@UekXqP&TIQY512AIAER9prr8I[UqEGImqZ[YR4Xd0jf$e +-jXe1BY(Zd0ckYark'6BIqN,H+*3lQ!#3!,ArB9*Hhcc&PXl)AGUr%A,[EA32'0) +iF`pidc4c$hM%0Y$U2@(e(M#VCI3HZmDXI!qieMTj6j`hZ-Ifd9Sqi1,ibAXZUGj +M0LVrY)j-)cem+Zq#JleSamhF`jkd4mlF`eD"*D2hp)dAQ,Imj0q190R$X[8H2p$ +b$&rcmFY06(X!*MjDaeE491rh`8emL!+D[",0E&6CHH,pF(k!ZHKB!3d(9-FXTA( +rUTemPQ1QX12M"9&!T42$$jcFEZ12k-3q,6VUa1,U1l-96rE6jV@jC,QG8kr0#`e +PTqe2lf(fGd3le,RH*mcF`mlN+C1DCC`hfZ[8mjUUh)0@1[qm"LT4l-apa-`pD+8 +$@[1ZHb3H6AUNMK*[jM"UfVA%Sb'2e&[LkBZ4HNXmZl0(kmJqj`(0[(!2QdF(Y2$ +#2H$&D4`!-2Y$L[R`#Jjl$kaM"(mNLThE!l-*KA[Bf$%`ke#iK`dI!c--KA[Bicf +LQGplMiIqH-hHcq&&f-B8R@4,FS&i9ULdjf`6XTIQh,mGVR#'-AMRaL+6'Jpb5&I +hdlk!Fr9j!$b8SA+I$q!&X(k5JmKURml$%,"pAJmRILD3!%m#,Rd)0,(kZ!0YM6c +)H*ifNr#i,11*0T0`G11$fdcbZqdMXGqHdFdNkIfe[(IA*kj(rlhhjVeh1m[Q[[% +(p0rZHr`(GUJ(Ulfk%81#iD5j#GZ@kT[iGNrBZXfAr41k2ZLGDJFYNeF"&ADU$5N +AIJ$fA2*[9Lj'eBEdIcGlV[VlmpjElpr8`hq3!2IZI@C$2GPrAfdA@jT2pC6c3"5 +ZE5Z,Yj&&eXZ`VEckjKdIh9D6`Y0+Gjiaidp6mG4T,Z`$mAqm3!ZQe0U4cFSEbl5 +`*hCdM6cV-T`lqU)HQDPCe0EHc1F[k[mXqHq3!2q$UcGRDNkm`%H#Z%L5VZFr1aN ++&fB['RqKM`bYD9E0FYPNr[-35[e[P@6-2I,I*[rM*AkFh$pGlVIj6"1rZ8rZDC! +!rlCU-HUL*c[NRlQLTC2Efq@rSATE`r[(RYNM(D3Cf6+E#XLG-k4#TR,LRmPr9SS +8FQQHce`+Vqq*hXkM"C!!bkE)rp468kck1$Z[cr&'c%a[d6!m!Lm&NcJb(GQ*+[G +`aFLZ8lRRClY6!8Ijj8@Nd6hQHB$ZZ*fMFJmML[Np+jPApmS!ir,,kdUMHl+[lT@ +"2XM[[-PlA(,3#rRYR,Q(r62(cpc$ATj(cGc$Qe"'pX$)2HcP'GQcSUp%AC[![ir +&4h!J2(!jmhrcpPpLPr4U[NSj1EkDp3hHPE2e[B$Y543[jK(dbX,@`EE#MS'YKmh +"0X!f`EE"MS8G$kZ[1Hdj%8kDe`XB*5*XD1Jc(G1h#Jh+Y&f[M"GSGAVaZmAA0q! +G@[+XS2`0AV*MC+"kiX$6M+5b$Yq'!mKhZ85ikD0)bFl)HrZAphYmkeqR[,l3TiB +1)@82QBSSiB!ESr+R)B("!AP29fGe!m$b%#rEA1-A4`E61U2A2(DZYT`'pHB0rLi +F-"06ScKJrXiVPpd%VfD[i8$$Ah&!f+1[M#ND(*!!9apd0[#*VQ[#j$EGd1#![0Z +hXk%KirTQLiAANEB[V9YHYbaT$4&#$cM!FPLQVmfD9"YT(!mFh$idK2(M'a2@3+& +i*Q,8b+i+MP$K#+##[16r)Dr2UrR3cRiBfbfL`SQ$(ri1hXCr,0qilfmllE,[)im ++Fm%9IUa8H@59XjrY,1"kT!39ZM$[RaNHrY@K"$G-m&#mLIE,!b-HXpNSI9J#-Eb +d*Sfc$bHS2P`KQYM2bF5qG`K!2"`RHV"5[,R"e`B$-!PK2jkic3heLA!6qhFQaNh +XJ`-H[iRpl[jHl!GZEh-+GRma)B`dr(TLhdF'!KB(r(!6qcFNaNhXhaP0l'I8qkG +IDMSehSMkKm0$9RKpJ"mJMk@CpYARla&Ufpj@TJC+CI[Z@*TVEdN)b'0PZLNCS+i +RjcXQ-pbCaiVQlXkkhT)U"%'pd[k`A9fdGR5MIq1Zb,I!X'dAVd!ibE3VVlX*[hT +2j![cm45EbH2-FrR+iaUkPU3A,hkp1f`H$ekF&*!!ajD#r$b2,BrDqApjE%jNHaj +RErE8S3HfaIekdp1PiGq[dZLIp!6QrLlDV6V-VHSb6(XdVLP'2ETKA`hLEpB1lHr +4lVTiH5@2TrbhAhP1J-PM5hZ3!%Hh%43-qh4KScc@m*N6AS!mcMaEf9TGH8k-c@- +)LEER-Icd%pUeGN`HkeAT$%j,'HB(C48"Y3e0FAT91M[0BlKMd4Y-jLbhd$pGY1% +$rhK'i,r"'VEQXEjNpjkrTl[jI&8PM`[k%`,bi-a"NrbZVVc(Bq39-Ii2aLf9lE[ +M2XBpQ)FhJ(%[@lefqQNM5h2VTq[Kq@$Z!e@pMlQ2j,'0ZBrNXBfjhj2(9ZCHbk2 +'h$R+h-2EjrFN!$$hMIAi&A-I,1a1jPl,BaYchjM',jPl,BpKjLjjM*!!piLjRph +hrllhIM"ZHFe5hahh-Hk"2$LH!-DpdR+@I9cI[1+D5FEGp$CMNM`f)m1pM'NNMfe +-jjimYM+G@Klh)[R'2(k*j,8mKN&2mT!![Vq(QFqaVE0S99!N0R@)j(&HlmBcH5c +Vh6LG4e2QM3Hc0fBbkGDr`pr!q4RLGF@GH%mHkmja4FQ$&j`bChSq4Sl5iVU12X[ +FN!!380HpR-&ak@&1[Mf2'R)1pjRVM`[1YYR26N[d,,e0'$-f8KrA1!p"94T3SDS +RVd,G09"4(ld!HA6C3D!!-3)J!hN)eP2Ai)a1EP4$,d!pM0VR"FM$URe$H@aAqji +5PQfe&fq9)ip,X4%Y[%-l![rNcpGV!`FpY8[Emm5q[GZdRGF46!l!Ne(14IccY6f +20fN$cdFEe%`H6f8A$GCMY-p8C9RVRHjA*"B$4*[bi&f&!CqIZ,I[MFVL"HKASl* +i!I,J4(!@r+HZ*JpTMq"rrI%BecRhd,-`NmQpmp5jmZ(K@$AU%Fa,RTKmk-VHH9U +ELSlYXD&M1)`A))r&'rd0GE#rUbZXe&maXcKdUP`S2ANZp)Iqb(1K2r5V2f"@c(# +K[IJ"!TpZHG-61+0ep8mSeacP9,q[UmPMq-@MShQ-2"6a`15h'DqKXjhTq*9Q[2B +Hcp+-erRH@ACDUkZ2B-TECXAf(Jr)V0K'!2RPV0J!8[X#CX8+cfVZZk3KHB+jHbU +BU4F!1Id+5$9kDSYb2##DMXGP8ml3m4"S-AkJ66PlMfGT8mjf,DB[%5r"aTh04cI +ZE$lqVBdlYI(rS#DTB[jr'i2cEq`4Z*GjEFcM9maVX#!lQ9FY$qr%IS6Yc+X[%Er +!RS8S$r2NjB&$%V+00#)&Qqla!c!#NiGABZr$2[A$5cr@[6G!H*[R2Y(#5cm16)a +lk3GmS&RQLaVaahc4Q2Hd$r0&(VUh[bFi)&lI'L8`1*!!6BbE-r[jI*([E-kX-6& +ZcXc-&hRIjXbLq5*Ih*aCLjSerEUYi6-ZhC,"jGIQVE*RQjf&%iY`IA#6Dfa*@h2 +qP3iqQ-1E-4BhhY")feC9Dm`V6*CIhU%[dZS(lp2U0kcUbZSGV59eKClRhi,D"5Z +9YQirRUVpqCS&De@IhT@0%Y+hGQIdeN(aE%LYh2fN'KiHMYcb(cjhEmqBhpR8AiX +EMUr@G#6RUMmq3h@SeDSrPeTFQYH12(E9EhGqrI8I[DqDGrmpZrTQ4mm1jL&l8r` +[,+(RC,!L8bD$cHGp1C0UdpQQjPUpYUQaXa'rh@hTcYT%PIVVQrZR#N`+)L53!"" +UpeIZi3heVC9lFRc5I1'Lr$h6m([#j$dZ1EPR1RiAcY`c!lq,+[FdM0pc5I8HAS% +bBDU1KAYQiRI*k$ep"%(ZkF"[if3qm6fcm0X`Fmr"q'fDZHF3r1D5!qlCTe)iXT0 +V3Q@SSAeK21C`k0XTb-9,iMH'Y1cV2T!!"ih'Fma4Y3*ZEZZAB$GNAq"HYLM& +(ITZLal'dCr[2,'NIcYT8%8jDep5qTVZ`b5VQI(IMq[aV,d(`TVe8q16SGE[Q0,C +qKEr9pjGNPQb0I1(45k**"!r6qdXHXRA9f,XhT4mI0I95BBRG`(l0,,P9eeimM#% +a2pK,K5XiAAfTF)V6I[1A#RX4Q,XIk"9V3mbGBeqXqmdMp3N!c0d[p9,K[Afr6m[ +`8Z([I[XTX08"&DTkmLV8c-'+qZJ&b+2b8Z&+(Y8AfDDUQ&%0[3$e-'UI&b!2UrB +0jE&Gl61[Fedb-2T5iFbF9$ImmP,Kc,c2(9)r6eflp+Af!bRpm01i8PiU21jRL2q +ce,8,YkIQID'qNSGjRHYJ28ElV2*5i8SHeCF+ThP8ALUmTaj'CI%#p+Y4@E`!HA# ++,aAH8eH64r*5i6ejl#2D[&6B-lH*-rc0Vb0Iq2@lG[,I3l@*dr4[Y)Pc6E"*Vdc +8HpFfFDBilB2Ia2PljZj$fX3CVG[h"c"hRpBQcT'qhkGPf-4jmj8rJUVdLif$H4A +UM6XVkU-A))rl0h'QUTK4$Ed!p6"URaFJ$k[f$H@aAHflCa2RXEHIfX4jl+dR0h% +1eQ1dcqlIa*RQFImQcM32Sl*i!IV9U#aHJ$`iZcGaTR8eHIak%fHDalk9cLE1aD2 +!eVJVm[91q,rfRXJAjJm2m0r$T#pZH'$Ef&kRN!$qRKI@Q4lGX+m'm6GVKrEhD(G +G[,b5KcGU%qI2phR&QcJV10flLE1%dhlc6CaH"1EZ"jV5hXEF`p[Rpb3!-(I[d5E +1AdaAlqhlI9U'6CaVR)HJ+JfS80@69k(Z'ULSMek!2#UE1#Yjp'iF,+PL4MAd!Y6 +$U(eHJ$cF*Xj0H@aAqmcfZ3rN4MGaKRGS4q#A6CcK`%&2ADAYU9faGjZfmcU#L@c +LE(-3rhaYcf@2D!22iljiqpaJ28ElV,+*Xj*(lbE18Kk96CalkQ&8&Lp![aU9a3Z +3!!HRZ)Pc6ee0(XNQcMejl#2DE1,-`@E9((VNN@DY5Q[iGiRpB)Gh[)A!JFiqdc& +pUj!!f!B)B3SrGF5"TaR*E4KJF!!q8+p5$I%Bb[*qMibbXp8dLUBI*+N"3fKMMMm +mJ3-L9Mc$m#dc33d*3Zjcb"!(cJjppfE6ha6SqG"`LSCIK-`0'Pc8N!$*Yq[TKY@ +@dk$H[-(IK31L&qEH`J(cGekjl$S0ZIjHfbVSE8l"lLmQK*%'J`-0Im8"BBpH-K# +`10")[I"-,&YF%bDh5BEKTF#"*Z*!CmEec4DXh9b[,keEAVFXD3d4$"pSIZrBmZE +fVIfkMi2cjRCr(!kB[8$a#GAdq*S(jkbq-3L0XeML36KKp)UA0HGR5hd`KdRRaBd +h00+f9EI@Qpd"bbr[d"GTpB2hDI8E9R9PpBl@NVT#cr0[3Hf#P8TEYeqTMcjDXf# +YkY1lXY*jjQ6deN(aE%LYh2fN'KiHMYcb(cjhEmqBhpR8AiXEMUr@G#6RUMmq3h@ +SeDSrPeTFQYH12(E9EhGqrI8I[DqDGrmpZrTQ4mm1jT%l#%UceV&#bl#YVHGfGf` +m(J"'Y$SdrE,P'1'lkCZh5IJ3Z)5(i`MlRjLhbTjYkTd&@emBP+lf[-+LX5Z)LqE +-@[EKF8MI3BJXGPKe)!'K0Z%RFG4hTICX5TX9eH8@+"RlkqKQH@@`IYkA-kNfR@e +UVY9VQaSl'r(EhCEZV%eLD31,!`I#@UbI"jX9hmQ`9ZeLadkpq&i-blZR10I!STY +`2JAE+,i[`cD*lapKQb9PmQN4hd(iD48IZ$9'I+IMCkcid%AEa(FjIKJY`(FMIXD +,lbra-d&mhm$242(p$eXpmQ9Birh%abkYbH*EM*rpaEF@2l*hbrKHJ*mTiRXpIUD ++li25,rMZ`-m$a!I+6iYp@IE'G2(0a-m-m4f((j8+XphiSIliRSiIlX(h-[adL1q +Gq*NP[NrLjf$ar5eq$K(I[q"(SDZ126aEI04&@8NGHrT"iMX&2kTBelRi18amj(f +iq-KlM[KJ,3m4heIa3qrJqajqMSappHcEZH)Mjk2%p`MmQ2I5Lqp8r"`M2NLYUU[ +eGq,RSH*lKq!2[SrLjf(Lqa*qp2%6p82iHAMXba&(k#Pmp!2jiL2QNH*EJ4rP4$R +f0bYfm&f-RrRLZ`Sr1S@EqaCq9"r22B)IRBE0X3B,aAFAIR5k,2F5INk-I3h%*Yh +mf-!qH,6ib"qF`8IrkAam!l9SR8CZf)kITH)$EaiM[[IM*e,*JMRI`ip+iJe(mI0 +BmIfcB$A`#`BX&amB32hadEINMSqkdiriq&mT2LDD`59mi0pTiRX6IX"NI1$Iim3 +(rMeHI0r&ca0LAa-arSRL!a0ALimqee9!6Db&,ZKTiUb"k[Y01r#M-`00c&[hT6A +GMam9-TTqJKrGVp9%(0,0$FhN%MVIdda-T0ra8@r9rCZC[qi,Dpk#(jhLE[ibIR5 +QX*NUj,RLSmEJ*$lkAVAKjTIa!fl,D5%Id)Qe&Z+GcNkdX1G9`'PK(AA'Sq8fr$a +,I'r%ciAL%l8Jf#`qm)Eki2X(r1MSC#Xjfjq+MlbI)ck`'%c$"bim9hc8QRcaJ6' +-LZ"l!hiZ%4pjdqri[S#I&iV[QrMK6R`r`!qm6miBmV&,a3H[H)Ri`%Vk&api!DI +!4qeH,MjkqM,aL4)F%2rab8VQi%[Lqdrm[#VfM58hJrIKSlId"GeML9YALSrH!m2 +aF4HpM1mQr1M1[l'2iHGUmAdE2kq0I@hNY+m6(a`-l-8(6k"(m)'$p#NqqSdDiRX +,IYiX[NrJ"cqqVq(RVH,l-AlH&[['N8rUAX"aj%[`(AcJ1j`,(aJ%TmC(Rla,I2# +5DmAh'IaF*lkramqlBppiBX0la!IfAbmqqZB'mC%VZH!$+pQ#M3pFJ'[JSarJerK +Z`3ppK1qcq&'&IMah9(j!I2q&(hL"R!RNfpb*$djfUrMJBlH*$q`(cr&4l`q,MaU +!4IMS*I!"(c(`3AaJrqhLJh0m,2C0*1IqZ2LS2If)$qi+GmB(Ce-9EL,j#AU&TqR +8$AA`N62#[5DTFK"@ThhVKMl9U9+,`)UEYb'bX'1MEZJcd$UQ)5,,519RK31LcND +NXVlScY1!+I1!Fif5Te&M6H5TB-32!JVN'%P@,[)dF*lf&bQ*4ARDb1MNb"+D24p +lN!!c"kR81$8e*$E1LL0[*kijr6`NJp!l$bIk@B8Yd-McVSSMAd&-Gp0P'6,FaA( +N3kPN@I8YZ)#DcE`imR(NA&D,LeMkQ$LbMFcI+R14eR"%(2PbmM'Vd`9cb'`dcjZ +)P&De#pT`Cje%[SMBVaUHA)&+-#Q1R%,%9d92$PamDKcjXFIK8he2L)!S0j0kiXK +A-hY9qq5!lh9aj11*bkVp5AI!A!k))dmKNUX5+!IFeilkm-2`U5iSI8-AMSdM2d) +QU#UKT!J(rh3Fq5Pb6G8-TB%+(A8#kkN+SP3!IDBeM[`XApZ[HU)dZY3cI#+1[*' +-5Y9&U3$F3l[M[Fa6Y8Dj$f9$UcDCl%b94kN!YIQ,1,+&[&ae5"(hK(Q(Pm54(b4 +DU#STR3[hqfJFq@eU%DT4#P6!bZI%N5(CVLU@)Rm*&QDNRY1T'DPq+H+6-,9--Bl +m$k+3!+UCJISqY+LM[KK(6U-f)0UQS,%`rEQXl!(P-#"1+`l@$A@MBUH#McHmVaa +'@h*Fj'R)XC(-q1KbD&09XC%Gck"r-cpQjmUG)(JbSe"AaGa[%4088D@ImjKl$2A +,r#JQ#SAHH6Q9$&9E"AcKm)UjAk6HSGUV("LP)YkjC1kUa%SAJZ@+%*1S[kSZ+l8 +"V44clk"US5UYG'%"FbmR,eI09L),Q,Z"D1''$lV4paCclbBI9MeAlU6E&9@H6FE +YCNiDH)GLlQdrJ%qehN#p$-A@BZiel%3hUaa5Ke$-[C!!U+3kX23RADLBqbbU1DS ++UmDR(E@3!2UADX452Y4CaGc[8)e3a9L+40D+Z9G6ae,p@1j%+G@1ZTjXAp9NLD6 +ZLP&I)ZDUYL`#XU#+aGb!@+K+Xpb*MU6Gm89LQZV1%NQP,AD!'kT#5bRJ'SUjee% +cG41r#c'qC6(h@e3r9+'@Y1"!LVP[TYkXHVANLF+LQ(XVle2e@Q44kAD,ZGqQrZ) +HmV3DM-pLlVQr4B!Ufi&@KKjR-AFLX8FdlEUKpDMjA(JFjLV$&$BV1Z2CB#5j9B+ +j,[*dm-#CB,0"[@#ZMHai'KbUJleL-6HCp`V*4V9[$U2bUHUk($3iaGcEb!G8DjI +XBD*kjq1)mhBD+@JKBLRQ[TVBVMUmS-S24l(`Fe6"9*8A3%$c8B3iL(UQfj496p9 +G-IG5pTfED-p`2N!apaV@e'fcbR)L3r2m#T959I0&SKAYbQ,Z!mJ(h1DB,"8b4CA +h-8G9qJ9cZ8-aG`)Vl9jmF$Akdf,ZhH6J1J8J"ie9-IFLGSV1"%LP[cq+Z9hdZ3e +k1I*DlDKR%BRFpV)QpSPLlN[*HA9k3'4qf,9LlVZT-HSXJ@!((%)lkJjU"6TC)*( +JQQ,8&@3NG[3qF+"`@X`pKbLV8`Gb*pUHGXG&e2"d"N'k!j9GUhB"F83R%U3fp*G +LlKH)[cUI)!+kU2X@Fap'(UA6#Y+Se&-aG`he5TeG%)5%b5RQ(NJ&@#FC4%BAIQ) +ap`kb#jeV%2&FXV'Bqh'bHCeb#,42!&8XjKj($9-k40LX-*!!ZCa1X*LVfSf`@G' +&cN)0``("h(bNB%#'#UN`B)1j6Zma6!%HC$%hL64X6r[QViNE1QNK&8#(9m`G5dD +MFaH#KI5ahRN9'CP1B3KBdDf+Z4G3fG'C$+NbM-SL(MaA*c3%LHKl4BLh8hqfUjZ +$TH5#LVPAF+,,V9F1141KQ$Z$f1&f04a&Y9Ac[*JFh'f$Z"MpBM(hJCb,F([aMb( +5+kTmJ`MTaPH2T(k[Q$Z2R-2Z@!rRJB90,X@4AbFlGlXmc9b@BZlhU35jr5pcUD) +VjVk"16[Gm%MLV(E8Fri%RpXC-CIpTCKlqqrJFj[Rcb"q+ZDqLE--6VNmQP0%9Ue +$9h+EUqB5qa5MEL(,GTVQ1Z4T-II2UA$S")Td&(a0Zq09R'e`Z`VQ8Q(3UKh)6YI +T&+N0(&Sap`M@aHQYjf-+a@,Z&839YrAM%ZVcLVR2T%VP"T'2MTLreSEZF*Y,AS" +D@-cp(KQRcVM)%)RJY-AFk84MRAJ*Y+p"(l5BZiV+SmbrL!BXfGj+hR#5B+kUSS+ +XSSpFKXKF56$A4Di!FR93q6K--0G'GK`&VRH3!$0Bc(9hVJ*ZA!pqdqbA`lja$IK +8)2&98(Bb(bYl!Hl-4ki%ehS*TM,#Kh,Y33Kf$K,TT)l36G"(9HMmRFY4e3iLh+f +S!$f@9m3%(@C4khZl4$VH[K,h2BL+a'b*G1aL*AMl#Q,XZb3bf8Kb#(%YkX+'$*8 +'Yh6Y%#SQ8HFfIZ3T8L%E@8Gp-%,*KKHTNHK8NHJhi0SCFH6GC"SkBb3XKQP!cI0 +jj0BkF56iLCi@jGR`3r*[R6m5VN)24(NfANTY6DH4T'rS!FhcR94eG$C*q!#+NHC +j$R&0*j@%@+1%D*jIilbTcLh4CrPkISJX3DHB"&$JcjVR(Db6cM3*#U%M4(NfcQD +11Z%NiJmiShdcLlfQmdk#3LLZQZGrF`T9TjqNRR5Gj[N$mJkCK4)F%0ai"R99LfZ +UkBY+Fi$5PJ)rRAlM)PHKCpDc8be#f-L1Sp!FCP%lYlMQlP`1,*p&hL$BBE$3c3k +BHU)q4M`UDj!!CHYXPJ`Mb&58DKIC1eJeRG3+0+TSLZ$CUmQBG@iVd&i!$l))rNV +QUP0F8NY33ERAPmJVGDC,+N"r4Y`V@dr-eJN[b36H%E'Bl(ELTmjlbCfI(q9HAk@ +qU00I3LVK34(hb[B5jh8@61k%L8AF+pY$K0(*-%'9JUlrBfT'lQdB,`1b+[I+pT( +MkY5Bb%p`S1M1V%HQVM0N%JQ1+5HkN3a6*mS#l5VJKI,Fl!l1DHTmQ@JNU&RD(Cq +QKUc6CN)$d6XLl-KHH!3qR6d6Q3Z'U28mL8LNNfK5!HDe)RDH[BbcEMUA*RI#Sj3 +aANY8e5NeZC-j+Q@-Rk6@TM0V!ReS&a&Mc*Bj3k)6E)&@"bh*-XBl1DIQhVRa'R) +0CB`r)YlSG*Z!(6J3G8HfMTaACpd#E3Q`64PMGJGhCqMNQr3RHNfNGf4A%Tpd$Nj +i,XLU9EZC'VK1a3RSdd@4hT(p009+RC%6'%-Y8EAK)q6Y1M%Rc33UG*H$9fB1&P# +TXT'*-YV&HYV)K(YeNA[Cb!4cMb&A'3GH#&p20*q6f,QAbCdDqAm!N!-K)3jdBfa +0B@03FQpUC@0dF`#3&Y%"J!*L!hF!N!-"!!)JN`#3"aB!N!1'!!!#!2q3"!-!V6C +3$,#%lk8!N!8-8kd!N!8#6elrerrl!*!'F!rhL`!!: diff --git a/mac/tclMacResource.c b/mac/tclMacResource.c new file mode 100644 index 0000000..db06571 --- /dev/null +++ b/mac/tclMacResource.c @@ -0,0 +1,2165 @@ +/* + * 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. + * + * SCCS: @(#) tclMacResource.c 1.35 97/11/24 15:03:58 + */ + +#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; + Tcl_DString buffer; + char *nativeName; + 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 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 char *writeSwitches[] = { + "-id", "-name", "-file", "-force", (char *) NULL + }; + + enum { + RESOURCE_WRITE_ID, RESOURCE_WRITE_NAME, + RESOURCE_WRITE_FILE, RESOURCE_FORCE + }; + + static 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; + + if (strcmp(Tcl_GetStringFromObj(objv[2], NULL), "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_SetStringObj(resultPtr,*pathHandle,pathLength); + HUnlock(pathHandle); + DisposeHandle(pathHandle); + } + 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); + } + 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: + if (!((objc == 3) || (objc == 4))) { + Tcl_WrongNumArgs(interp, 2, objv, "fileName ?permissions?"); + return TCL_ERROR; + } + stringPtr = Tcl_GetStringFromObj(objv[2], &length); + nativeName = Tcl_TranslateFileName(interp, stringPtr, &buffer); + if (nativeName == NULL) { + return TCL_ERROR; + } + err = FSpLocationFromPath(strlen(nativeName), nativeName, + &fileSpec) ; + 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: + 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_SetStringObj(resultPtr, *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 = Tcl_GetStringFromObj(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) { + 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 ) { + 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: + panic("Tcl_GetIndexFromObject 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; + int length; + + if (objc < 2 || objc > 4) { + errStr = errNum; + goto sourceFmtErr; + } + + if (objc == 2) { + string = TclGetStringFromObj(objv[1], &length); + return Tcl_EvalFile(interp, string); + } + + /* + * The following code supports a few older forms of this command + * for backward compatability. + */ + string = TclGetStringFromObj(objv[1], &length); + if (!strcmp(string, "-rsrc") || !strcmp(string, "-rsrcname")) { + rsrcName = TclGetStringFromObj(objv[2], &length); + } else if (!strcmp(string, "-rsrcid")) { + if (Tcl_GetLongFromObj(interp, objv[2], &rsrcID) != TCL_OK) { + return TCL_ERROR; + } + } else { + errStr = errBad; + goto sourceFmtErr; + } + + if (objc == 4) { + fileName = TclGetStringFromObj(objv[3], &length); + } + return Tcl_MacEvalResource(interp, rsrcName, rsrcID, fileName); + + sourceFmtErr: + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), errStr, "should be \"", + Tcl_GetStringFromObj(objv[0], (int *) NULL), + " fileName\" or \"", + Tcl_GetStringFromObj(objv[0], (int *) NULL), + " -rsrc name ?fileName?\" or \"", + Tcl_GetStringFromObj(objv[0], (int *) NULL), + " -rsrcid id ?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_GetStringFromObj(objv[0], (int *) NULL), + " -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. */ + char *resourceName, /* Name of TEXT resource to source, + NULL if number should be used. */ + int resourceNumber, /* Resource id of source. */ + char *fileName) /* Name of file to process. + NULL if application resource. */ +{ + Handle sourceText; + Str255 rezName; + char msg[200]; + int result; + short saveRef, fileRef = -1; + char idStr[64]; + FSSpec fileSpec; + Tcl_DString buffer; + char *nativeName; + + saveRef = CurResFile(); + + if (fileName != NULL) { + OSErr err; + + nativeName = Tcl_TranslateFileName(interp, fileName, &buffer); + if (nativeName == NULL) { + return TCL_ERROR; + } + err = FSpLocationFromPath(strlen(nativeName), nativeName, &fileSpec); + 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); + } 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); + sourceText = GetNamedResource('TEXT', rezName); + } 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) { + sprintf(msg, "\n (rsrc \"%.150s\" line %d)", resourceName, + interp->errorLine); + Tcl_AddErrorInfo(interp, 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: + if (fileRef != -1) { + CloseResFile(fileRef); + } + + UseResFile(saveRef); + + 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; + + size = GetResourceSizeOnDisk(resource); + + resultStr = ckalloc(size + 1); + + for (i=0; i<size; i++) { + if ((*resource)[i] == '\r') { + resultStr[i] = '\n'; + } else { + resultStr[i] = (*resource)[i]; + } + } + + resultStr[size] = '\0'; + + 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. */ + char *resourceName, /* Name of resource to find, + * NULL if number should be used. */ + int resourceNumber, /* Resource id of source. */ + 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 { + c2pstr(resourceName); + if (limitSearch) { + resource = Get1NamedResource(resourceType, + (StringPtr) resourceName); + } else { + resource = GetNamedResource(resourceType, + (StringPtr) resourceName); + } + p2cstr((StringPtr) resourceName); + } + + if (*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); + } + + Tcl_InvalidateStringRep(objPtr); + if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) { + oldTypePtr->freeIntRepProc(objPtr); + } + + objPtr->internalRep.longValue = newOSType; + objPtr->typePtr = &osType; +} + +/* + *---------------------------------------------------------------------- + * + * 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; + long newOSType; + + /* + * Get the string representation. Make it up-to-date if necessary. + */ + + string = TclGetStringFromObj(objPtr, &length); + + if (length != 4) { + if (interp != NULL) { + Tcl_ResetResult(interp); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "expected Macintosh OS type but got \"", string, "\"", + (char *) NULL); + } + return TCL_ERROR; + } + newOSType = *((long *) string); + + /* + * 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. */ +{ + objPtr->bytes = ckalloc(5); + sprintf(objPtr->bytes, "%-4.4s", &(objPtr->internalRep.longValue)); + objPtr->length = 4; +} + +/* + *---------------------------------------------------------------------- + * + * 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 bahavior 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 is 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. + */ + + if (flags & TCL_RESOURCE_CHECK_IF_OPEN) { + Tcl_HashSearch search; + short oldFileRef; + 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); + + + resourceHashPtr = Tcl_FirstHashEntry(&resourceTable, &search); + while (resourceHashPtr != NULL) { + + oldFileRef = (short) Tcl_GetHashKey(&resourceTable, + resourceHashPtr); + + + 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 ((oldFileRef == fileRef) || + ((err == noErr) + && (newFileRec.ioFCBVRefNum == oldFileRec.ioFCBVRefNum) + && (newFileRec.ioFCBFlNm == oldFileRec.ioFCBFlNm))) { + + resourceId = (char *) Tcl_GetHashValue(resourceHashPtr); + Tcl_SetStringObj(tokenPtr, resourceId, -1); + return TCL_OK; + } + + resourceHashPtr = Tcl_NextHashEntry(&search); + } + + + } + + 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 = (char *) 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) { + 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) { + 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) { + 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,(char *) 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..a25d476 --- /dev/null +++ b/mac/tclMacResource.r @@ -0,0 +1,92 @@ +/* + * 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. + * + * SCCS: @(#) tclMacResource.r 1.19 97/09/23 12:51:41 + */ + +#include <Types.r> +#include <SysTypes.r> + +/* + * The folowing include and defines help construct + * the version string for Tcl. + */ + +#define RESOURCE_INCLUDED +#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 +#else +# define MINOR_VERSION TCL_MINOR_VERSION * 16 +#endif + +resource 'vers' (1) { + TCL_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, 0x00, verUS, + TCL_PATCH_LEVEL, + TCL_PATCH_LEVEL ", by Ray Johnson © Sun Microsystems" +}; + +resource 'vers' (2) { + TCL_MAJOR_VERSION, MINOR_VERSION, + RELEASE_LEVEL, 0x00, verUS, + TCL_PATCH_LEVEL, + "Simple Tcl Shell " TCL_PATCH_LEVEL " © 1996" +}; + + +/* + * 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' (0, "Init", purgeable, preload) "::library:init.tcl"; +read 'TEXT' (1, "History", purgeable,preload) "::library:history.tcl"; +read 'TEXT' (2, "Word", purgeable,preload) "::library:word.tcl"; + +/* + * 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" + }; +}; + diff --git a/mac/tclMacShLib.exp b/mac/tclMacShLib.exp new file mode 100644 index 0000000..8936114 --- /dev/null +++ b/mac/tclMacShLib.exp @@ -0,0 +1,1069 @@ +AddrToName +AddrToStr +BuildAFPVolMountInfo +BumpDate +ChangeCreatorType +ChangeFDFlags +CheckObjectLock +CheckVolLock +ClearHasBeenInited +ClearHasCustomIcon +ClearIsInvisible +ClearIsStationery +ClearNameLocked +CloseResolver +ConfigureMemory +CopyDirectoryAccess +CopyFileMgrAttributes +CopyFork +CreateFileIDRef +DTCopyComment +DTGetIcon +DTOpen +DTSetComment +DeleteDirectory +DeleteDirectoryContents +DeleteFileIDRef +DetermineVRefNum +DirectoryCopy +EnumCache +##EnvStr +ExchangeFiles +FSMakeFSSpecCompat +FSReadNoCache +FSWriteNoCache +FSWriteVerify +FSpBumpDate +FSpCatMoveCompat +FSpChangeCreatorType +FSpChangeFDFlags +FSpCheckObjectLock +FSpClearHasBeenInited +FSpClearHasCustomIcon +FSpClearIsInvisible +FSpClearIsStationery +FSpClearNameLocked +FSpCopyDirectoryAccess +FSpCopyFile +FSpCopyFileMgrAttributes +FSpCreateCompat +FSpCreateFileIDRef +FSpCreateMinimum +FSpCreateResFileCompat +FSpDTCopyComment +FSpDTSetComment +FSpDeleteCompat +FSpDirCreateCompat +FSpDirectoryCopy +FSpExchangeFilesCompat +FSpFileCopy +FSpFilteredDirectoryCopy +FSpFindFolder +FSpGetDInfo +FSpGetDefaultDir +FSpGetDirAccess +FSpGetDirectoryID +FSpGetFInfoCompat +FSpGetFLockCompat +FSpGetFileLocation +FSpGetFileSize +FSpGetForeignPrivs +FSpGetFullPath +FSpGetIOACUser +FSpLocationFromFullPath +FSpLocationFromPath +FSpMoveRename +FSpMoveRenameCompat +FSpOpenAware +FSpOpenDFCompat +FSpOpenRFAware +FSpOpenRFCompat +FSpOpenResFileCompat +FSpPathFromLocation +FSpRenameCompat +FSpResolveFileIDRef +FSpRstFLockCompat +FSpSetDInfo +FSpSetDefaultDir +FSpSetDirAccess +FSpSetFInfoCompat +FSpSetFLockCompat +FSpSetForeignPrivs +FSpSetHasCustomIcon +FSpSetIsInvisible +FSpSetIsStationery +FSpSetNameLocked +FSpShare +FSpUnshare +FileCopy +FilteredDirectoryCopy +FindDrive +FlushFile +FreeAllMemory +GetCPanelFolder +GetCatInfoNoName +GetDInfo +GetDirItems +GetDirName +GetDirectoryID +GetDiskBlocks +GetDriverName +GetFileLocation +GetFileSize +GetFilenameFromPathname +GetForeignPrivs +GetFullPath +GetGlobalMouse +GetIOACUser +GetObjectLocation +GetParentID +GetSystemFolder +GetTempBuffer +GetTrapType +GetUGEntries +GetUGEntry +GetVolMountInfo +GetVolMountInfoSize +GetVolumeInfoNoName +HCopyFile +HCreateMinimum +HGetDirAccess +HGetLogInInfo +HGetVInfo +HGetVolParms +HInfo +HMapID +HMapName +HMoveRename +HMoveRenameCompat +HOpenAware +HOpenRFAware +hypotd +HSetDirAccess +InstallConsole +LocationFromFullPath +LockRange +MXInfo +NumToolboxTraps +OnLine +OpenOurRF +OpenResolver +PBXGetVolInfoSync +ReadCharsFromConsole +RemoveConsole +ResolveFileIDRef +RestoreDefault +RetrieveAFPVolMountInfo +SIOUXBigRect +SIOUXCantSaveAlert +SIOUXDoAboutBox +SIOUXDoContentClick +SIOUXDoEditClear +SIOUXDoEditCopy +SIOUXDoEditCut +SIOUXDoEditPaste +SIOUXDoEditSelectAll +SIOUXDoMenuChoice +SIOUXDoPageSetup +SIOUXDoPrintText +SIOUXDoSaveText +SIOUXDragRect +SIOUXDrawGrowBox +SIOUXHandleOneEvent +SIOUXIsAppWindow +SIOUXMyGrowWindow +SIOUXQuitting +SIOUXSetTitle +SIOUXSettings +SIOUXSetupMenus +SIOUXSetupTextWindow +SIOUXState +SIOUXTextWindow +SIOUXUpdateMenuItems +SIOUXUpdateScrollbar +SIOUXUpdateStatusLine +SIOUXUpdateWindow +SIOUXUseWaitNextEvent +SIOUXYesNoCancelAlert +SIOUXisinrange +SIOUXselstart +SearchFolderForDNRP +SetDInfo +SetDefault +SetForeignPrivs +SetHasCustomIcon +SetIsInvisible +SetIsStationery +SetNameLocked +Share +StrToAddr +TclAllocateFreeObjects +TclChdir +TclCleanupByteCode +TclCleanupCommand +TclCompileBreakCmd +TclCompileCatchCmd +TclCompileContinueCmd +TclCompileDollarVar +TclCompileExpr +TclCompileExprCmd +TclCompileForCmd +TclCompileForeachCmd +TclCompileIfCmd +TclCompileIncrCmd +TclCompileQuotes +TclCompileSetCmd +TclCompileString +TclCompileWhileCmd +TclCopyAndCollapse +TclCopyChannel +TclCreateAuxData +TclCreateExecEnv +TclDate_TclDates +TclDate_TclDatev +TclDateact +TclDatechar +TclDatechk +TclDatedebug +TclDatedef +TclDateerrflag +TclDateexca +TclDatelval +TclDatenerrs +TclDatepact +TclDateparse +TclDatepgo +TclDateps +TclDatepv +TclDater1 +TclDater2 +TclDates +TclDatestate +TclDatetmp +TclDatev +TclDateval +TclDeleteCompiledLocalVars +TclDeleteExecEnv +TclDeleteVars +TclDoGlob +TclEmitForwardJump +TclExecuteByteCode +TclExpandCodeArray +TclExpandJumpFixupArray +TclExpandParseValue +TclExprFloatError +TclFileAttrsCmd +TclFileCopyCmd +TclFileDeleteCmd +TclFileMakeDirsCmd +TclFileRenameCmd +TclFindElement +TclFindProc +TclFixupForwardJump +TclFormatInt +TclFreeCompileEnv +TclFreeJumpFixupArray +TclFreeObj +TclFreePackageInfo +TclGetCwd +TclGetDate +TclGetDefaultStdChannel +TclGetElementOfIndexedArray +TclGetEnv +TclGetExceptionRangeForPc +TclGetExtension +TclGetFrame +TclGetIndexedScalar +TclGetIntForIndex +TclGetLoadedPackages +TclGetLong +TclGetNamespaceForQualName +TclGetOpenMode +TclGetOriginalCommand +TclGetRegError +TclGetSrcInfoForPc +TclGetUserHome +TclGlobalInvoke +TclGuessPackageName +TclHasSockets +TclHideUnsafeCommands +TclInExit +TclIncrElementOfIndexedArray +TclIncrIndexedScalar +TclIncrVar2 +TclInitByteCodeObj +TclInitCompileEnv +TclInitJumpFixupArray +TclInitNamespaces +TclInterpInit +TclInvoke +TclInvokeObjectCommand +TclInvokeStringCommand +TclIsProc +TclLoadFile +TclLooksLikeInt +TclLookupVar +TclMacAccess +TclMacCreateEnv +TclMacExitHandler +TclMacFOpenHack +TclMacInitExitToShell +TclMacInstallExitToShellPatch +TclMacOSErrorToPosixError +TclMacReadlink +TclMacRemoveTimer +TclMacStartTimer +TclMacStat +TclMacTimerExpired +TclMatchFiles +TclNeedSpace +TclObjIndexForString +TclObjInterpProc +TclObjInvoke +TclObjInvokeGlobal +TclParseBraces +TclParseNestedCmd +TclParseQuotes +TclPlatformExit +TclPlatformInit +TclPreventAliasLoop +TclPrintByteCodeObj +TclPrintInstruction +TclPrintSource +TclRegComp +TclRegError +TclRegExec +TclRenameCommand +TclResetShadowedCmdRefs +TclServiceIdle +TclSetElementOfIndexedArray +TclSetEnv +TclSetIndexedScalar +TclSetupEnv +TclSockGetPort +TclTeardownNamespace +TclTestChannelCmd +TclTestChannelEventCmd +TclUnsetEnv +TclUpdateReturnInfo +TclWordEnd +Tcl_AddErrorInfo +Tcl_AddObjErrorInfo +Tcl_AfterCmd +Tcl_Alloc +Tcl_AllowExceptions +Tcl_AppendAllObjTypes +Tcl_AppendElement +Tcl_AppendObjCmd +Tcl_AppendResult +Tcl_AppendStringsToObj +Tcl_AppendToObj +Tcl_ArrayObjCmd +Tcl_AsyncCreate +Tcl_AsyncDelete +Tcl_AsyncInvoke +Tcl_AsyncMark +Tcl_AsyncReady +Tcl_BackgroundError +Tcl_Backslash +Tcl_BeepObjCmd +Tcl_BinaryObjCmd +Tcl_BreakCmd +Tcl_CallWhenDeleted +Tcl_CancelIdleCall +Tcl_CaseObjCmd +Tcl_CatchObjCmd +Tcl_ClockObjCmd +Tcl_Close +Tcl_CommandComplete +Tcl_Concat +Tcl_ConcatObj +Tcl_ConcatObjCmd +Tcl_ContinueCmd +Tcl_ConvertCountedElement +Tcl_ConvertElement +Tcl_ConvertToType +Tcl_CreateAlias +Tcl_CreateAliasObj +Tcl_CreateChannel +Tcl_CreateChannelHandler +Tcl_CreateCloseHandler +Tcl_CreateCommand +Tcl_CreateEventSource +Tcl_CreateExitHandler +Tcl_CreateInterp +Tcl_CreateMathFunc +Tcl_CreateNamespace +Tcl_CreateObjCommand +Tcl_CreateSlave +Tcl_CreateTimerHandler +Tcl_CreateTrace +Tcl_DStringAppend +Tcl_DStringAppendElement +Tcl_DStringEndSublist +Tcl_DStringFree +Tcl_DStringGetResult +Tcl_DStringInit +Tcl_DStringResult +Tcl_DStringSetLength +Tcl_DStringStartSublist +Tcl_DbCkalloc +Tcl_DbCkfree +Tcl_DbCkrealloc +Tcl_DbDecrRefCount +Tcl_DbIsShared +Tcl_DbIncrRefCount +Tcl_DbNewBooleanObj +Tcl_DbNewDoubleObj +Tcl_DbNewListObj +Tcl_DbNewLongObj +Tcl_DbNewObj +Tcl_DbNewStringObj +Tcl_DeleteAssocData +Tcl_DeleteChannelHandler +Tcl_DeleteCloseHandler +Tcl_DeleteCommand +Tcl_DeleteCommandFromToken +Tcl_DeleteEventSource +Tcl_DeleteEvents +Tcl_DeleteExitHandler +Tcl_DeleteHashEntry +Tcl_DeleteHashTable +Tcl_DeleteInterp +Tcl_DeleteNamespace +Tcl_DeleteTimerHandler +Tcl_DeleteTrace +Tcl_DoOneEvent +Tcl_DoWhenIdle +Tcl_DontCallWhenDeleted +Tcl_DumpActiveMemory +Tcl_DuplicateObj +Tcl_EchoCmd +Tcl_Eof +Tcl_ErrnoId +Tcl_ErrnoMsg +Tcl_ErrorObjCmd +Tcl_Eval +Tcl_EvalFile +Tcl_EvalObj +Tcl_EvalObjCmd +Tcl_EventuallyFree +Tcl_ExecCmd +Tcl_Exit +Tcl_ExitObjCmd +Tcl_ExposeCommand +Tcl_ExprBoolean +Tcl_ExprBooleanObj +Tcl_ExprDouble +Tcl_ExprDoubleObj +Tcl_ExprLong +Tcl_ExprLongObj +Tcl_ExprObjCmd +Tcl_ExprString +Tcl_FconfigureCmd +Tcl_FcopyObjCmd +Tcl_FileEventCmd +Tcl_FileObjCmd +Tcl_Finalize +Tcl_FindCommand +Tcl_FindExecutable +Tcl_FindNamespace +Tcl_FindNamespaceVar +Tcl_FirstHashEntry +Tcl_Flush +Tcl_FlushObjCmd +Tcl_ForCmd +Tcl_ForeachObjCmd +Tcl_ForgetImport +Tcl_FormatCmd +Tcl_Free +Tcl_FreeResult +Tcl_GetAlias +Tcl_GetAliasObj +Tcl_GetAssocData +Tcl_GetBoolean +Tcl_GetBooleanFromObj +Tcl_GetChannel +Tcl_GetChannelBufferSize +Tcl_GetChannelHandle +Tcl_GetChannelInstanceData +Tcl_GetChannelMode +Tcl_GetChannelName +Tcl_GetChannelOption +Tcl_GetChannelType +Tcl_GetCommandFromObj +Tcl_GetCommandFullName +Tcl_GetCommandInfo +Tcl_GetCommandName +Tcl_GetCurrentNamespace +Tcl_GetDouble +Tcl_GetDoubleFromObj +Tcl_GetErrno +Tcl_GetGlobalNamespace +Tcl_GetHostName +Tcl_GetIndexFromObj +Tcl_GetInt +Tcl_GetIntFromObj +Tcl_GetInterpPath +Tcl_GetLongFromObj +Tcl_GetMaster +Tcl_GetOSTypeFromObj +Tcl_GetObjResult +Tcl_GetObjType +Tcl_GetPathType +Tcl_GetServiceMode +Tcl_GetSlave +Tcl_GetStdChannel +Tcl_GetStringFromObj +Tcl_GetStringResult +Tcl_GetVar +Tcl_GetVar2 +Tcl_GetVariableFullName +Tcl_Gets +Tcl_GetsObj +Tcl_GetsObjCmd +Tcl_GlobCmd +Tcl_GlobalEval +Tcl_GlobalEvalObj +Tcl_GlobalObjCmd +Tcl_HashStats +Tcl_HideCommand +Tcl_HistoryCmd +Tcl_IfCmd +Tcl_Import +Tcl_IncrCmd +Tcl_InfoObjCmd +Tcl_Init +Tcl_InitHashTable +Tcl_InitMemory +Tcl_InputBlocked +Tcl_InputBuffered +Tcl_InterpDeleted +Tcl_InterpObjCmd +Tcl_IsSafe +Tcl_JoinObjCmd +Tcl_JoinPath +Tcl_LappendObjCmd +Tcl_LindexObjCmd +Tcl_LinkVar +Tcl_LinsertObjCmd +Tcl_ListObjAppendElement +Tcl_ListObjAppendList +Tcl_ListObjCmd +Tcl_ListObjGetElements +Tcl_ListObjIndex +Tcl_ListObjLength +Tcl_ListObjReplace +Tcl_LlengthObjCmd +Tcl_LoadCmd +Tcl_LrangeObjCmd +Tcl_LreplaceObjCmd +Tcl_LsCmd +Tcl_LsearchObjCmd +Tcl_LsortObjCmd +Tcl_MacConvertTextResource +Tcl_MacEvalResource +Tcl_MacFindResource +Tcl_MacSetEventProc +Tcl_MacSourceObjCmd +Tcl_Main +Tcl_MakeSafe +Tcl_MakeTcpClientChannel +Tcl_Merge +Tcl_NamespaceObjCmd +Tcl_NewBooleanObj +Tcl_NewDoubleObj +Tcl_NewIntObj +Tcl_NewListObj +Tcl_NewLongObj +Tcl_NewOSTypeObj +Tcl_NewObj +Tcl_NewStringObj +Tcl_NextHashEntry +Tcl_NotifyChannel +Tcl_ObjGetVar2 +Tcl_ObjSetVar2 +Tcl_OpenCmd +Tcl_OpenFileChannel +Tcl_OpenTcpClient +Tcl_OpenTcpServer +Tcl_PackageCmd +Tcl_ParseVar +Tcl_PidObjCmd +Tcl_PkgProvide +Tcl_PkgRequire +Tcl_PopCallFrame +Tcl_PosixError +Tcl_Preserve +Tcl_PrintDouble +Tcl_ProcObjCmd +Tcl_PushCallFrame +Tcl_PutEnv +Tcl_PutsObjCmd +Tcl_PwdCmd +Tcl_QueueEvent +Tcl_Read +Tcl_ReadObjCmd +Tcl_Realloc +Tcl_RecordAndEval +Tcl_RegExpCompile +Tcl_RegExpExec +Tcl_RegExpMatch +Tcl_RegExpRange +Tcl_RegexpCmd +Tcl_RegisterChannel +Tcl_RegisterObjType +Tcl_RegsubCmd +Tcl_Release +Tcl_RenameObjCmd +Tcl_ResetResult +Tcl_ResourceObjCmd +Tcl_ReturnObjCmd +Tcl_ScanCmd +Tcl_ScanCountedElement +Tcl_ScanElement +Tcl_Seek +Tcl_SeekCmd +Tcl_ServiceAll +Tcl_ServiceEvent +Tcl_SetAssocData +Tcl_SetBooleanObj +Tcl_SetChannelBufferSize +Tcl_SetChannelOption +Tcl_SetCmd +Tcl_SetCommandInfo +Tcl_SetDoubleObj +Tcl_SetErrno +Tcl_SetErrorCode +Tcl_SetIntObj +Tcl_SetListObj +Tcl_SetLongObj +Tcl_SetMaxBlockTime +Tcl_SetOSTypeObj +Tcl_SetObjErrorCode +Tcl_SetObjLength +Tcl_SetObjResult +Tcl_SetPanicProc +Tcl_SetRecursionLimit +Tcl_SetResult +Tcl_SetServiceMode +Tcl_SetStdChannel +Tcl_SetStringObj +Tcl_SetTimer +Tcl_SetVar +Tcl_SetVar2 +Tcl_SignalId +Tcl_SignalMsg +Tcl_Sleep +Tcl_SocketCmd +Tcl_SourceObjCmd +Tcl_SourceRCFile +Tcl_SplitList +Tcl_SplitPath +Tcl_StaticPackage +Tcl_StringMatch +Tcl_StringObjCmd +Tcl_SubstCmd +Tcl_SwitchObjCmd +Tcl_Tell +Tcl_TellCmd +Tcl_TimeObjCmd +Tcl_TraceCmd +Tcl_TraceVar +Tcl_TraceVar2 +Tcl_TranslateFileName +Tcl_Ungets +Tcl_UnlinkVar +Tcl_UnregisterChannel +Tcl_UnsetObjCmd +Tcl_UnsetVar +Tcl_UnsetVar2 +Tcl_UntraceVar +Tcl_UntraceVar2 +Tcl_UpVar +Tcl_UpVar2 +Tcl_UpdateCmd +Tcl_UpdateLinkedVar +Tcl_UplevelObjCmd +Tcl_UpvarObjCmd +Tcl_ValidateAllMemory +Tcl_VarEval +Tcl_VarTraceInfo +Tcl_VarTraceInfo2 +Tcl_VariableObjCmd +Tcl_VwaitCmd +Tcl_WaitForEvent +Tcl_WaitPid +Tcl_WhileCmd +Tcl_Write +Tcl_WrongNumArgs +TclpAlloc +TclpCopyDirectory +TclpCopyFile +TclpCreateDirectory +TclpDeleteFile +TclpFree +TclpGetClicks +TclpGetDate +TclpGetSeconds +TclpGetTime +TclpGetTimeZone +TclpListVolumes +TclpRealloc +TclpRemoveDirectory +TclpRenameFile +TrapExists +TruncPString +UnlockRange +UnmountAndEject +Unshare +VolumeMount +WriteCharsToConsole +XGetVInfo +_Ctype +_Stderr +_Stoul +abort +abs +acosf +appMemory +asctime +asinf +atan +atan2 +atan2_d_dd +atan2_d_pdpd +atan2_r_prpr +atan2_r_rr +atan2f +atan_d_d +atan_d_pd +atan_r_pr +atan_r_r +atanf +atexit +atof +atoi +atol +bsearch +builtinFuncTable +calloc +ccommand +ceilf +chdir +clearerr +clock +close +closeUPP +completeUPP +cos +cos_d_d +cos_d_pd +cos_r_pr +cos_r_r +cosf +coshf +creat +ctime +cuserid +difftime +div +environ +errno +exec +exit +exp +exp_d_d +exp_d_pd +exp_r_pr +exp_r_r +expf +fabsf +fclose +fcntl +fdopen +feof +ferror +fflush +fgetc +fgetpos +fgets +fileno +floorf +fmodf +fopen +fprintf +fputc +fputs +fread +free +freopen +frexpf +fscanf +fseek +fsetpos +fstat +ftell +fwrite +getStdChannelsProc +getc +getchar +getcwd +getenv +getlogin +gets +gmtime +instructionTable +isalnum +isalpha +isatty +iscntrl +isdigit +isgraph +islower +isprint +ispunct +isspace +isupper +isxdigit +labs +ldexpf +ldiv +localeconv +localtime +log +log10 +log10_d_d +log10_d_pd +log10f +log_d_d +log_d_pd +logf +longjmp +lseek +malloc +mblen +mbstowcs +mbtowc +memchr +memcmp +memcpy +memmove +memset +mkdir +mktime +open +panic +panicProc +perror +pow +power_d_dd +powf +printf +putc +putchar +puts +qsort +raise +rand +read +realloc +remove +rename +resultUPP +rewind +rmdir +scanf +setbuf +setlocale +setvbuf +signal +sin +sin_d_d +sin_d_pd +sin_r_pr +sin_r_r +sinf +sinhf +sleep +sprintf +sqrt +sqrt_d_d +sqrt_d_pd +sqrt_r_pr +sqrt_r_r +sqrtf +srand +sscanf +stat +strcasecmp +strcat +strchr +strcmp +strcoll +strcpy +strcspn +strerror +strftime +strlen +strncasecmp +strncat +strncmp +strncpy +strpbrk +strrchr +strspn +strstr +strtod +strtok +strtol +strtoul +strxfrm +system +systemMemory +tanf +tanhf +tclBooleanType +tclByteCodeType +tclCmdNameType +tclDoubleType +tclDummyLinkVarPtr +tclExecutableName +tclFreeObjList +tclIndexType +tclIntType +tclListType +tclMemDumpFileName +tclNsNameType +tclPlatform +tclStringType +tclTraceCompile +tclTraceExec +tclTypeTable +tcl_MathInProgress +tclpFileAttrProcs +tclpFileAttrStrings +tell +time +tmpfile +tmpnam +tolower +toupper +ttyname +uname +ungetc +unlink +utime +utimes +vfprintf +vprintf +vsprintf +wcstombs +wctob +wctomb +write +#DTGetAPPL +#DTGetComment +#FSpDTGetAPPL +#FSpDTGetComment +#TclMacInitializeFragment +#TclMacTerminateFragment +#_Aldata +#_Assert +#_Atcount +#_Atfuns +#_Clocale +#_Closreg +#_Costate +#_Daysto +#_Dbl +#_Defloc +#_Environ +#_Environ1 +#_Fgpos +#_Files +#_Flt +#_Fopen +#_Foprep +#_Fread +#_Freeloc +#_Frprep +#_Fspos +#_Fwprep +#_Fwrite +#_Genld +#_Gentime +#_Getdst +#_Getfld +#_Getfloat +#_Getint +#_Getloc +#_Getmem +#_Getstr +#_Gettime +#_Getzone +#_Isdst +#_Ldbl +#_Ldtob +#_Litob +#_Locale +#_Locsum +#_Loctab +#_Locterm +#_Locvar +#_MWERKS_Atcount +#_MWERKS_Atfuns +#_Makeloc +#_Makestab +#_Makewct +#_Mbcurmax +#_Mbstate +#_Mbtowc +#_Nnl +#_PJP_C_Copyright +#_Printf +#_Putfld +#_Putstr +#_Puttxt +#_Randseed +#_Readloc +#_Scanf +#_Setloc +#_Skip +#_Stdin +#_Stdout +#_Stod +#_Stof +#_Stoflt +#_Stold +#_Strerror +#_Strftime +#_Strxfrm +#_Times +#_Tolower +#_Toupper +#_Ttotm +#_WCostate +#_Wcstate +#_Wctob +#_Wctomb +#_Wctrans +#_Wctype +#__CheckForSystem7 +#__RemoveConsoleHandler__ +#__aborting +#__ctopstring +#__cvt_fp2unsigned +#__getcreator +#__gettype +#__initialize +#__myraise +#__ptmf_null +#__ptr_glue +#__system7present +#__terminate +#__ttyname +#_atexit +#_exit +#_fcreator +#_ftype diff --git a/mac/tclMacSock.c b/mac/tclMacSock.c new file mode 100644 index 0000000..fe276f1 --- /dev/null +++ b/mac/tclMacSock.c @@ -0,0 +1,2615 @@ +/* + * 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. + * + * SCCS: @(#) tclMacSock.c 1.59 97/10/09 18:24:42 + */ + +#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 Tcl_WatchFile. */ + 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. */ + 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, char *host, 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_((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, 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, + 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)); + +/* + * This structure describes the channel type structure for TCP socket + * based IO: + */ + +static Tcl_ChannelType tcpChannelType = { + "tcp", /* Type name. */ + 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; + +/* + * 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}, +}; + +/* + * Every open socket has an entry on the following list. + */ + +static TcpState *socketList = NULL; + +/* + * 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; + + initialized = 1; + Tcl_CreateExitHandler(SocketExitHandler, (ClientData) NULL); + + 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); + + /* + * 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); + + Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL); + + initialized = 1; +} + +/* + *---------------------------------------------------------------------- + * + * 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); */ + } + initialized = 0; +} + +/* + *---------------------------------------------------------------------- + * + * TclHasSockets -- + * + * 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 +TclHasSockets( + Tcl_Interp *interp) /* Interp for error messages. */ +{ + if (!initialized) { + 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 }; + + if (!(flags & TCL_FILE_EVENTS)) { + return; + } + + /* + * Check to see if there is a ready socket. If so, poll. + */ + + for (statePtr = 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; + + 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 = 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; + err = PBControlSync((ParmBlkPtr) &closePB); + if (err != noErr) { + Debugger(); + 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) { + panic("error releasing server socket"); + } + + /* + * Free the buffer space used by the socket and the + * actual socket state data structure. + */ + + 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) { + panic("error closing async connect socket"); + } + statePtr->flags |= TCP_RELEASE; + + InitMacTCPParamBlock(&statePtr->pb, TCPRelease); + statePtr->pb.tcpStream = statePtr->tcpStream; + err = PBControlSync((ParmBlkPtr) &statePtr->pb); + if (err != noErr) { + panic("error releasing async connect socket"); + } + + /* + * Free the buffer space used by the socket and the + * actual socket state data structure. + */ + + ckfree((char *) statePtr->pb.csParam.create.rcvBuff); + FreeSocketInfo(statePtr); + return 0; + } + + /* + * 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_GetChannelFile 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. */ + 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; + } + statePtr->dataSegment[0].length = amount; + statePtr->dataSegment[0].ptr = buf; + 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.*/ + 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, doAll = false; + ip_addr tcpAddress; + char buffer[128]; + OSErr err; + Tcl_DString dString; + TCPiopb statusPB; + int errorCode; + + /* + * 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 == (char *) NULL || optionName[0] == '\0') { + doAll = true; + } else { + if (!strcmp(optionName, "-peername")) { + doPeerName = true; + } else if (!strcmp(optionName, "-sockname")) { + doSockName = true; + } else { + return Tcl_BadChannelOption(interp, optionName, + "peername sockname"); + } + } + + /* + * 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; + + 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->nextPtr = socketList; + 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. */ +{ + if (statePtr == socketList) { + socketList = statePtr->nextPtr; + } else { + TcpState *p; + for (p = socketList; p != NULL; p = p->nextPtr) { + if (p->nextPtr == statePtr) { + p->nextPtr = statePtr->nextPtr; + break; + } + } + } + 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 (TclHasSockets(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. */ + char *host, /* Name of host on which to open port. */ + 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; + 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->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.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. */ + char *host, /* Host on which to open port. */ + 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 (TclHasSockets(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. */ + 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 (TclHasSockets(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; + + if (!(flags & TCL_FILE_EVENTS)) { + return 0; + } + + /* + * Find the specified socket on the socket list. + */ + + for (statePtr = 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) { + statePtr->checkMask |= (TCL_READABLE | TCL_WRITABLE); + return 1; + } + statePtr->checkMask = 0; + 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; + } + + /* + * 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. + * + *---------------------------------------------------------------------- + */ + +char * +Tcl_GetHostName() +{ + static int hostnameInited = 0; + static char hostname[255]; + ip_addr ourAddress; + Tcl_DString dString; + OSErr err; + + if (hostnameInited) { + return hostname; + } + + if (TclHasSockets(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; + + while (socketList != NULL) { + statePtr = socketList; + 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( + char *name, /* Host in string form. */ + ip_addr *address) /* Returned IP address. */ +{ + OSErr err; + int i; + EventRecord dummy; + DNRState dnrState; + + if (TclHasSockets(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(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(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 + * interp->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; + + for (statePtr = socketList; statePtr != NULL; + statePtr = statePtr->nextPtr) { + if (statePtr->flags & TCP_RELEASE) { + SocketFreeProc(statePtr); + return; + } + } +} diff --git a/mac/tclMacTest.c b/mac/tclMacTest.c new file mode 100644 index 0000000..2452ca1 --- /dev/null +++ b/mac/tclMacTest.c @@ -0,0 +1,242 @@ +/* + * 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. + * + * SCCS: @(#) tclMacTest.c 1.9 97/09/09 16:36:37 + */ + +#define TCL_TEST + +#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, char **argv)); +static int WriteTextResource _ANSI_ARGS_((ClientData dummy, + Tcl_Interp *interp, int argc, 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. */ + 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. */ + char **argv) /* Argument strings. */ +{ + char *errNum = "wrong # args: "; + char *errBad = "bad argument: "; + char *errStr; + char *fileName = NULL, *rsrcName = NULL; + 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) + 1); + 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; +} + +int +TclMacChmod( + char *path, + int mode) +{ + HParamBlockRec hpb; + OSErr err; + + c2pstr(path); + hpb.fileParam.ioNamePtr = (unsigned char *) path; + hpb.fileParam.ioVRefNum = 0; + hpb.fileParam.ioDirID = 0; + + if (mode & 0200) { + err = PBHRstFLockSync(&hpb); + } else { + err = PBHSetFLockSync(&hpb); + } + p2cstr((unsigned char *) path); + + if (err != noErr) { + errno = TclMacOSErrorToPosixError(err); + return -1; + } + + return 0; +} + diff --git a/mac/tclMacTime.c b/mac/tclMacTime.c new file mode 100644 index 0000000..e5b6a1f --- /dev/null +++ b/mac/tclMacTime.c @@ -0,0 +1,312 @@ +/* + * 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. + * + * SCCS: @(#) tclMacTime.c 1.19 97/06/27 13:07:10 + */ + +#include "tclInt.h" +#include "tclPort.h" +#include <OSUtils.h> +#include <Timer.h> +#include <time.h> + +/* + * Static variables used by the TclpGetTime function. + */ + +static int initalized = false; +static unsigned long baseSeconds; +static UnsignedWide microOffset; + +/* + * Prototypes for procedures that are private to this file: + */ + +static void SubtractUnsignedWide _ANSI_ARGS_((UnsignedWide *x, + UnsignedWide *y, UnsignedWide *result)); + +/* + *----------------------------------------------------------------------------- + * + * 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; + MachineLocation loc; + long int offset; + + ReadLocation(&loc); + offset = loc.u.gmtDelta & 0x00ffffff; + if (offset & 0x00800000) { + offset = offset | 0xff000000; + } + + if (ReadDateTime(&seconds) == noErr) { + return (seconds - offset); + } else { + panic("Can't get time."); + return 0; + } +} + +/* + *----------------------------------------------------------------------------- + * + * 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. */ +{ + MachineLocation loc; + long int offset; + + ReadLocation(&loc); + offset = loc.u.gmtDelta & 0x00ffffff; + if (offset & 0x00700000) { + offset |= 0xff000000; + } + + /* + * Convert the Mac offset from seconds to minutes and + * add an hour if we have daylight savings time. + */ + offset = -offset; + offset /= 60; + if (loc.u.dlsDelta < 0) { + offset += 60; + } + + return offset; +} + +/* + *---------------------------------------------------------------------- + * + * TclpGetTime -- + * + * 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 +TclpGetTime( + Tcl_Time *timePtr) /* Location to store time information. */ +{ + UnsignedWide micro; +#ifndef NO_LONG_LONG + long long *microPtr; +#endif + + if (initalized == false) { + MachineLocation loc; + long int offset; + + ReadLocation(&loc); + offset = loc.u.gmtDelta & 0x00ffffff; + if (offset & 0x00800000) { + offset = offset | 0xff000000; + } + if (ReadDateTime(&baseSeconds) != noErr) { + /* + * This should never happen! + */ + return; + } + /* + * Remove the local offset that ReadDateTime() adds. + */ + baseSeconds -= 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( + const time_t *tp, /* Time struct to fill. */ + int useGMT) /* True if date should reflect GNT time. */ +{ + DateTimeRec dtr; + MachineLocation loc; + long int offset; + static struct tm statictime; + static const short monthday[12] = + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + + ReadLocation(&loc); + + if (useGMT) { + SecondsToDate(*tp, &dtr); + } else { + offset = loc.u.gmtDelta & 0x00ffffff; + if (offset & 0x00700000) { + offset |= 0xff000000; + } + + SecondsToDate(*tp + 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; + } + statictime.tm_isdst = loc.u.dlsDelta; + return(&statictime); +} + +#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..e820fc0 --- /dev/null +++ b/mac/tclMacUnix.c @@ -0,0 +1,464 @@ +/* + * 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-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. + * + * SCCS: @(#) tclMacUnix.c 1.56 96/12/12 19:38:08 + */ + +#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 + +/* + * Static functions in this file. + */ + +static int GlobArgs _ANSI_ARGS_((Tcl_Interp *interp, + int *argc, char ***argv)); + +/* + *---------------------------------------------------------------------- + * + * GlobArgs -- + * + * The following function was taken from Peter Keleher's Alpha + * Editor. *argc should only count the end arguments that should + * be globed. argv should be incremented to point to the first + * arg to be globed. + * + * Results: + * Returns 'true' if it worked & memory was allocated, else 'false'. + * + * Side effects: + * argv will be alloced, the call will need to release the memory + * + *---------------------------------------------------------------------- + */ + +static int +GlobArgs( + Tcl_Interp *interp, /* Tcl interpreter. */ + int *argc, /* Number of arguments. */ + char ***argv) /* Argument strings. */ +{ + int res, len; + char *list; + + /* + * Places the globbed args all into 'interp->result' as a list. + */ + res = Tcl_GlobCmd(NULL, interp, *argc + 1, *argv - 1); + if (res != TCL_OK) { + return false; + } + len = strlen(interp->result); + list = (char *) ckalloc(len + 1); + strcpy(list, interp->result); + Tcl_ResetResult(interp); + + res = Tcl_SplitList(interp, list, argc, argv); + ckfree((char *) list); + if (res != TCL_OK) { + return false; + } + return true; +} + +/* + *---------------------------------------------------------------------- + * + * 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. */ + 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_Write(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_Write(chan, " ", -1); + } + } + Tcl_Write(chan, "\n", -1); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_LsCmd -- + * + * 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_LsCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int argc, /* Number of arguments. */ + char **argv) /* Argument strings. */ +{ +#define STRING_LENGTH 80 +#define CR '\n' + int i, j; + int fieldLength, len = 0, maxLen = 0, perLine; + char **origArgv = argv; + 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; + + /* + * 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 < argc; i++) { + if (argv[i][0] != '-') { + break; + } + + if (!strcmp(argv[i], "-")) { + i++; + break; + } + + for (j = 1 ; argv[i][j] ; ++j) { + switch(argv[i][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; + } + } + } + + argv += i; + argc -= i; + + /* + * No file specifications means we search for all files. + * Glob will be doing most of the work. + */ + if (!argc) { + argc = 1; + argv = origArgv; + strcpy(argv[0], "*"); + } + + if (!GlobArgs(interp, &argc, &argv)) { + Tcl_ResetResult(interp); + return TCL_ERROR; + } + + /* + * 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; + + /* + * 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 < argc; i++) { + strcpy(theFile, argv[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); + + } + + if ((interp->result != NULL) && (*(interp->result) != '\0')) { + int slen = strlen(interp->result); + if (interp->result[slen - 1] == '\n') { + interp->result[slen - 1] = '\0'; + } + } + } 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 < argc; i++) { + len = strlen(argv[i]); + if (len > maxLen) { + maxLen = len; + } + } + fieldLength = maxLen + 3; + perLine = STRING_LENGTH / fieldLength; + } + + argCount = 0; + linePos = 0; + memset(theLine, ' ', STRING_LENGTH); + while (argCount < argc) { + strcpy(theFile, argv[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); + } + } + } + + ckfree((char *) argv); + + return TCL_OK; +} diff --git a/mac/tclMacUtil.c b/mac/tclMacUtil.c new file mode 100644 index 0000000..254cfb8 --- /dev/null +++ b/mac/tclMacUtil.c @@ -0,0 +1,441 @@ +/* + * 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. + * + * SCCS: @(#) tclMacUtil.c 1.53 97/07/30 16:46:16 + */ + +#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) || defined(__MWERKS__) +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; +} + +/* + *---------------------------------------------------------------------- + * + * 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. */ + char *path, /* The path to convert. */ + FSSpecPtr fileSpecPtr) /* On return the spec for the path. */ +{ + Str255 fileName; + OSErr err; + short vRefNum; + long dirID; + int pos, cur; + Boolean isDirectory; + Boolean wasAlias; + + /* + * 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; + err = ResolveAliasFile(fileSpecPtr, true, &isDirectory, &wasAlias); + if (err != noErr) return err; + FSpGetDirectoryID(fileSpecPtr, &dirID, &isDirectory); + vRefNum = fileSpecPtr->vRefNum; + cur = pos; + if (path[cur] == ':') { + cur++; + } + } + + 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. + */ + if (err == fnfErr) { + BlockMoveData(spec, &tempSpec, sizeof(FSSpec)); + } else if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 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; +} + +/* + *---------------------------------------------------------------------- + * + * GetGlobalMouse -- + * + * This procedure obtains the current mouse position in global + * coordinates. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +GetGlobalMouse( + Point *mouse) /* Mouse position. */ +{ + EventRecord event; + + OSEventAvail(0, &event); + *mouse = event.where; +} |