summaryrefslogtreecommitdiff
path: root/tcl/mac
diff options
context:
space:
mode:
Diffstat (limited to 'tcl/mac')
-rw-r--r--tcl/mac/AppleScript.html298
-rw-r--r--tcl/mac/Background.doc92
-rw-r--r--tcl/mac/MW_TclAppleScriptHeader.pch47
-rw-r--r--tcl/mac/MW_TclHeader.pch47
-rw-r--r--tcl/mac/README195
-rw-r--r--tcl/mac/bugs.doc32
-rw-r--r--tcl/mac/libmoto.doc39
-rw-r--r--tcl/mac/license.terms39
-rw-r--r--tcl/mac/morefiles.doc74
-rw-r--r--tcl/mac/porting.notes23
-rw-r--r--tcl/mac/tclMac.h101
-rw-r--r--tcl/mac/tclMacAETE.r58
-rw-r--r--tcl/mac/tclMacAlloc.c348
-rw-r--r--tcl/mac/tclMacAppInit.c212
-rw-r--r--tcl/mac/tclMacApplication.r75
-rw-r--r--tcl/mac/tclMacBOAAppInit.c257
-rw-r--r--tcl/mac/tclMacBOAMain.c360
-rw-r--r--tcl/mac/tclMacChan.c1356
-rw-r--r--tcl/mac/tclMacCommonPch.h88
-rw-r--r--tcl/mac/tclMacDNR.c23
-rw-r--r--tcl/mac/tclMacEnv.c536
-rw-r--r--tcl/mac/tclMacExit.c333
-rw-r--r--tcl/mac/tclMacFCmd.c1408
-rw-r--r--tcl/mac/tclMacFile.c840
-rw-r--r--tcl/mac/tclMacInit.c284
-rw-r--r--tcl/mac/tclMacInt.h79
-rw-r--r--tcl/mac/tclMacInterupt.c289
-rw-r--r--tcl/mac/tclMacLibrary.c241
-rw-r--r--tcl/mac/tclMacLibrary.r223
-rw-r--r--tcl/mac/tclMacLoad.c245
-rw-r--r--tcl/mac/tclMacMSLPrefix.h24
-rw-r--r--tcl/mac/tclMacMath.h145
-rw-r--r--tcl/mac/tclMacNotify.c416
-rw-r--r--tcl/mac/tclMacOSA.c2937
-rw-r--r--tcl/mac/tclMacOSA.exp1
-rw-r--r--tcl/mac/tclMacOSA.r76
-rw-r--r--tcl/mac/tclMacPanic.c235
-rw-r--r--tcl/mac/tclMacPort.h268
-rw-r--r--tcl/mac/tclMacProjects.sea.hqx1704
-rw-r--r--tcl/mac/tclMacResource.c2205
-rw-r--r--tcl/mac/tclMacResource.r92
-rw-r--r--tcl/mac/tclMacShLib.exp1069
-rw-r--r--tcl/mac/tclMacSock.c2615
-rw-r--r--tcl/mac/tclMacTest.c242
-rw-r--r--tcl/mac/tclMacTime.c312
-rw-r--r--tcl/mac/tclMacUnix.c464
-rw-r--r--tcl/mac/tclMacUtil.c441
47 files changed, 21488 insertions, 0 deletions
diff --git a/tcl/mac/AppleScript.html b/tcl/mac/AppleScript.html
new file mode 100644
index 00000000000..4a73fbb384a
--- /dev/null
+++ b/tcl/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/tcl/mac/Background.doc b/tcl/mac/Background.doc
new file mode 100644
index 00000000000..b28e79e16d4
--- /dev/null
+++ b/tcl/mac/Background.doc
@@ -0,0 +1,92 @@
+Notes about the Background Only application template
+====================================================
+
+RCS: @(#) $Id$
+
+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/tcl/mac/MW_TclAppleScriptHeader.pch b/tcl/mac/MW_TclAppleScriptHeader.pch
new file mode 100644
index 00000000000..afd6cdba1ea
--- /dev/null
+++ b/tcl/mac/MW_TclAppleScriptHeader.pch
@@ -0,0 +1,47 @@
+/*
+ * MW_TclAppleScriptHeader.pch --
+ *
+ * This file is the source for a pre-compilied header that gets used
+ * for TclAppleScript. This make compilies go a bit
+ * faster. This file is only intended to be used in the MetroWerks
+ * CodeWarrior environment. It essentially acts as a place to set
+ * compiler flags. See MetroWerks documention for more details.
+ *
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+/*
+ * 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/tcl/mac/MW_TclHeader.pch b/tcl/mac/MW_TclHeader.pch
new file mode 100644
index 00000000000..bca9fe42366
--- /dev/null
+++ b/tcl/mac/MW_TclHeader.pch
@@ -0,0 +1,47 @@
+/*
+ * MW_TclHeader.pch --
+ *
+ * This file is the source for a pre-compilied header that gets used
+ * for all files in the Tcl projects. This make compilies go a bit
+ * faster. This file is only intended to be used in the MetroWerks
+ * CodeWarrior environment. It essentially acts as a place to set
+ * compiler flags. See MetroWerks documention for more details.
+ *
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+/*
+ * To use the compilied header you need to set the "Prefix file" in
+ * the "C/C++ Language" preference panel to point to the created
+ * compilied header. The name of the header depends on the
+ * architecture we are compiling for (see the code below). For
+ * example, for a 68k app the prefix file should be: MW_TclHeader68K.
+ */
+#if __POWERPC__
+#pragma precompile_target "MW_TclHeaderPPC"
+#elif __CFM68K__
+#pragma precompile_target "MW_TclHeaderCFM68K"
+#else
+#pragma precompile_target "MW_TclHeader68K"
+#endif
+
+#include "tclMacCommonPch.h"
+
+/*
+ * Place any includes below that will are needed by the majority of the
+ * and is OK to be in any file in the system. 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 reset
+
diff --git a/tcl/mac/README b/tcl/mac/README
new file mode 100644
index 00000000000..b8ca40e17c1
--- /dev/null
+++ b/tcl/mac/README
@@ -0,0 +1,195 @@
+Tcl 8.0.3 for Macintosh
+
+by Ray Johnson
+Scriptics Corporation
+rjohnson@scriptics.com
+with major help from
+Jim Ingham
+Cygnus Solutions
+jingham@cygnus.com
+
+RCS: @(#) $Id$
+
+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.3.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.3.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.3.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://www.scriptics.com/man/tcl8.0/contents.html
+
+Other documentation and sample Tcl scripts can be found at
+the Tcl archive site:
+
+ ftp://ftp.neosoft.com/tcl/
+
+and the Tcl resource center:
+
+ http://www.scriptics.com/resource/
+
+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 2 or 3
+ 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 3, 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@cygnus.com
+
diff --git a/tcl/mac/bugs.doc b/tcl/mac/bugs.doc
new file mode 100644
index 00000000000..114e0d9ab47
--- /dev/null
+++ b/tcl/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
+
+RCS: @(#) $Id$
+
+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/tcl/mac/libmoto.doc b/tcl/mac/libmoto.doc
new file mode 100644
index 00000000000..ba84ba29ec5
--- /dev/null
+++ b/tcl/mac/libmoto.doc
@@ -0,0 +1,39 @@
+Notes about the use of libmoto
+------------------------------
+
+RCS: @(#) $Id$
+
+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/tcl/mac/license.terms b/tcl/mac/license.terms
new file mode 100644
index 00000000000..9df3e600352
--- /dev/null
+++ b/tcl/mac/license.terms
@@ -0,0 +1,39 @@
+This software is copyrighted by the Regents of the University of
+California, Sun Microsystems, Inc., Scriptics Corporation,
+and other parties. The following terms apply to all files associated
+with the software unless explicitly disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+GOVERNMENT USE: If you are acquiring this software on behalf of the
+U.S. government, the Government shall have only "Restricted Rights"
+in the software and related documentation as defined in the Federal
+Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+are acquiring the software on behalf of the Department of Defense, the
+software shall be classified as "Commercial Computer Software" and the
+Government shall have only "Restricted Rights" as defined in Clause
+252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
+authors grant the U.S. Government and others acting in its behalf
+permission to use and distribute the software in accordance with the
+terms specified in this license.
diff --git a/tcl/mac/morefiles.doc b/tcl/mac/morefiles.doc
new file mode 100644
index 00000000000..88329c503a2
--- /dev/null
+++ b/tcl/mac/morefiles.doc
@@ -0,0 +1,74 @@
+Notes about MoreFiles, dnr.c & other non-Tcl source files
+---------------------------------------------------------
+
+RCS: @(#) $Id$
+
+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/tcl/mac/porting.notes b/tcl/mac/porting.notes
new file mode 100644
index 00000000000..c15beb14245
--- /dev/null
+++ b/tcl/mac/porting.notes
@@ -0,0 +1,23 @@
+Porting Notes
+-------------
+
+RCS: @(#) $Id$
+
+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/tcl/mac/tclMac.h b/tcl/mac/tclMac.h
new file mode 100644
index 00000000000..3f121c5ca61
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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, CONST 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/tcl/mac/tclMacAETE.r b/tcl/mac/tclMacAETE.r
new file mode 100644
index 00000000000..2090ff6adb8
--- /dev/null
+++ b/tcl/mac/tclMacAETE.r
@@ -0,0 +1,58 @@
+/*
+ * tclMacAETE.r --
+ *
+ * This file creates the Apple Event Terminology resources
+ * for use Tcl and Tk. It is not used in the Simple Tcl shell
+ * since SIOUX does not support AppleEvents. An example of its
+ * use in Tcl is the TclBGOnly project. And it is used in all the
+ * Tk Shells.
+ *
+ * Copyright (c) 1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacAlloc.c b/tcl/mac/tclMacAlloc.c
new file mode 100644
index 00000000000..71b6fb4effd
--- /dev/null
+++ b/tcl/mac/tclMacAlloc.c
@@ -0,0 +1,348 @@
+/*
+ * tclMacAlloc.c --
+ *
+ * This is a very fast storage allocator. It allocates blocks of a
+ * small number of different sizes, and keeps free lists of each size.
+ * Blocks that don't exactly fit are passed up to the next larger size.
+ * Blocks over a certain size are directly allocated by calling NewPtr.
+ *
+ * Copyright (c) 1983 Regents of the University of California.
+ * Copyright (c) 1996-1997 Sun Microsystems, Inc.
+ *
+ * Portions contributed by Chris Kingsley, Jack Jansen and Ray Johnson
+ *.
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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);
+ *hand = NULL;
+ 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;
+ if (*(memRecord->memoryHandle) != NULL) {
+ 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;
+ if (*(memRecord->memoryHandle) != NULL) {
+ DisposeHandle(memRecord->memoryHandle);
+ }
+ DisposePtr((void *) memRecord);
+ }
+ while (appMemory != NULL) {
+ memRecord = appMemory;
+ appMemory = memRecord->next;
+ if (*(memRecord->memoryHandle) != NULL) {
+ 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/tcl/mac/tclMacAppInit.c b/tcl/mac/tclMacAppInit.c
new file mode 100644
index 00000000000..05cdc416da6
--- /dev/null
+++ b/tcl/mac/tclMacAppInit.c
@@ -0,0 +1,212 @@
+/*
+ * tclMacAppInit.c --
+ *
+ * Provides a version of the Tcl_AppInit procedure for the example shell.
+ *
+ * Copyright (c) 1993-1994 Lockheed Missle & Space Company, AI Center
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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 Procbodytest_Init _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN int Procbodytest_SafeInit _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN int TclObjTest_Init _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN int Tcltest_Init _ANSI_ARGS_((Tcl_Interp *interp));
+#endif /* TCL_TEST */
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static int MacintoshInit _ANSI_ARGS_((void));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * main --
+ *
+ * Main program for tclsh. This file can be used as a prototype
+ * for other applications using the Tcl library.
+ *
+ * Results:
+ * None. This procedure never returns (it exits the process when
+ * it's done.
+ *
+ * Side effects:
+ * This procedure initializes the Macintosh world and then
+ * calls Tcl_Main. Tcl_Main will never return except to exit.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+main(
+ int argc, /* Number of arguments. */
+ char **argv) /* Array of argument strings. */
+{
+ char *newArgv[2];
+
+ if (MacintoshInit() != TCL_OK) {
+ Tcl_Exit(1);
+ }
+
+ argc = 1;
+ newArgv[0] = "tclsh";
+ newArgv[1] = NULL;
+ Tcl_Main(argc, newArgv, Tcl_AppInit);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AppInit --
+ *
+ * This procedure performs application-specific initialization.
+ * Most applications, especially those that incorporate additional
+ * packages, will have their own version of this procedure.
+ *
+ * Results:
+ * Returns a standard Tcl completion code, and leaves an error
+ * message in 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;
+ }
+ if (Procbodytest_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "procbodytest", Procbodytest_Init,
+ Procbodytest_SafeInit);
+#endif /* TCL_TEST */
+
+ /*
+ * Call the init procedures for included packages. Each call should
+ * look like this:
+ *
+ * if (Mod_Init(interp) == TCL_ERROR) {
+ * return TCL_ERROR;
+ * }
+ *
+ * where "Mod" is the name of the module.
+ */
+
+ /*
+ * Call Tcl_CreateCommand for application-specific commands, if
+ * they weren't already created by the init procedures called above.
+ * Each call would loo like this:
+ *
+ * Tcl_CreateCommand(interp, "tclName", CFuncCmd, NULL, NULL);
+ */
+
+ /*
+ * Specify a user-specific startup script to invoke if the application
+ * is run interactively. On the Mac we can specifiy either a TEXT resource
+ * which contains the script or the more UNIX like file location
+ * may also used. (I highly recommend using the resource method.)
+ */
+
+ Tcl_SetVar(interp, "tcl_rcRsrcName", "tclshrc", TCL_GLOBAL_ONLY);
+ /* Tcl_SetVar(interp, "tcl_rcFileName", "~/.tclshrc", TCL_GLOBAL_ONLY); */
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MacintoshInit --
+ *
+ * This procedure calls initalization routines to set up a simple
+ * console on a Macintosh. This is necessary as the Mac doesn't
+ * have a stdout & stderr by default.
+ *
+ * Results:
+ * Returns TCL_OK if everything went fine. If it didn't the
+ * application should probably fail.
+ *
+ * Side effects:
+ * Inits the appropiate console package.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+MacintoshInit()
+{
+#if GENERATING68K && !GENERATINGCFM
+ SetApplLimit(GetApplLimit() - (TCL_MAC_68K_STACK_GROWTH));
+#endif
+ MaxApplZone();
+
+#if defined(THINK_C)
+
+ /* Set options for Think C console package */
+ /* The console package calls the Mac init calls */
+ console_options.pause_atexit = 0;
+ console_options.title = "\pTcl Interpreter";
+
+#elif defined(__MWERKS__)
+
+ /* Set options for CodeWarrior SIOUX package */
+ SIOUXSettings.autocloseonquit = true;
+ SIOUXSettings.showstatusline = true;
+ SIOUXSettings.asktosaveonclose = false;
+ 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/tcl/mac/tclMacApplication.r b/tcl/mac/tclMacApplication.r
new file mode 100644
index 00000000000..10e2aa39053
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacBOAAppInit.c b/tcl/mac/tclMacBOAAppInit.c
new file mode 100644
index 00000000000..9ec795b45db
--- /dev/null
+++ b/tcl/mac/tclMacBOAAppInit.c
@@ -0,0 +1,257 @@
+/*
+ * tclMacBOAAppInit.c --
+ *
+ * Provides a version of the Tcl_AppInit procedure for a
+ * Macintosh Background Only Application.
+ *
+ * Copyright (c) 1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacBOAMain.c b/tcl/mac/tclMacBOAMain.c
new file mode 100644
index 00000000000..c08a39dba9e
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacChan.c b/tcl/mac/tclMacChan.c
new file mode 100644
index 00000000000..ffb91c73d94
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpOpenFileChannel --
+ *
+ * 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
+TclpOpenFileChannel(
+ 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/tcl/mac/tclMacCommonPch.h b/tcl/mac/tclMacCommonPch.h
new file mode 100644
index 00000000000..5f599ddabb0
--- /dev/null
+++ b/tcl/mac/tclMacCommonPch.h
@@ -0,0 +1,88 @@
+/*
+ * tclMacCommonPch.h --
+ *
+ * Macintosh Tcl must be compiled with certain compiler options to
+ * ensure that it will work correctly. The following pragmas are
+ * used to ensure that those options are set correctly. An error
+ * will occur at compile time if they are not set correctly.
+ *
+ * Copyright (c) 1998 by Scriptics Corporation.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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
diff --git a/tcl/mac/tclMacDNR.c b/tcl/mac/tclMacDNR.c
new file mode 100644
index 00000000000..1425bd224c4
--- /dev/null
+++ b/tcl/mac/tclMacDNR.c
@@ -0,0 +1,23 @@
+/*
+ * tclMacDNR.c
+ *
+ * This file actually just includes the file "dnr.c" provided by
+ * Apple Computer and redistributed by MetroWerks (and other compiler
+ * vendors.) Unfortunantly, despite various bug reports, dnr.c uses
+ * C++ style comments and will not compile under the "ANSI Strict"
+ * mode that the rest of Tcl compiles under. Furthermore, the Apple
+ * license prohibits me from redistributing a corrected version of
+ * dnr.c. This file uses a pragma to turn off the Strict ANSI option
+ * and then includes the dnr.c file.
+ *
+ * Copyright (c) 1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#pragma ANSI_strict off
+#include <dnr.c>
+#pragma ANSI_strict reset
diff --git a/tcl/mac/tclMacEnv.c b/tcl/mac/tclMacEnv.c
new file mode 100644
index 00000000000..30c159a33e8
--- /dev/null
+++ b/tcl/mac/tclMacEnv.c
@@ -0,0 +1,536 @@
+/*
+ * tclMacEnv.c --
+ *
+ * Implements the "environment" on a Macintosh.
+ *
+ * Copyright (c) 1995-1996 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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(&currentDir);
+ FSpPathFromLocation(&currentDir, &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/tcl/mac/tclMacExit.c b/tcl/mac/tclMacExit.c
new file mode 100644
index 00000000000..ef1b9d38435
--- /dev/null
+++ b/tcl/mac/tclMacExit.c
@@ -0,0 +1,333 @@
+/*
+ * tclMacExit.c --
+ *
+ * This file contains routines that deal with cleaning up various state
+ * when Tcl/Tk applications quit. Unfortunantly, not all state is cleaned
+ * up by the process when an application quites or crashes. Also you
+ * need to do different things depending on wether you are running as
+ * 68k code, PowerPC, or a code resource. The Exit handler code was
+ * adapted from code posted on alt.sources.mac by Dave Nebinger.
+ *
+ * Copyright (c) 1995 Dave Nebinger.
+ * Copyright (c) 1995-1996 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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();
+
+/*
+ * If we are using the Metrowerks Standard Library, then we will call its exit so that it
+ * will get a chance to clean up temp files, and so forth. It always calls the standard
+ * ExitToShell, so the Tcl handlers will also get called.
+ *
+ * If you have another exit, make sure that it does not patch ExitToShell, and does
+ * call it. If so, it will probably work as well.
+ *
+ */
+
+#ifdef __MSL__
+ exit(status);
+#else
+ ExitToShell();
+#endif
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclMacExitHandler --
+ *
+ * This procedure is invoked after Tcl at the last possible moment
+ * to clean up any state Tcl has left around that may cause other
+ * applications to crash. For example, this function can be used
+ * as the termination routine for CFM applications.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Various cleanup occurs.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclMacExitHandler()
+{
+ ExitToShellUPPListPtr curProc;
+
+ /*
+ * Loop through all installed Exit handlers
+ * and call them. Always make sure we are in
+ * a clean state in case we are recursivly called.
+ */
+ if ((gExitToShellData) != NULL && (gExitToShellData->userProcs != NULL)){
+
+ /*
+ * Call the installed exit to shell routines.
+ */
+ curProc = gExitToShellData->userProcs;
+ do {
+ gExitToShellData->userProcs = curProc->nextProc;
+ CallExitToShellProc(curProc->userProc);
+ DisposeExitToShellProc(curProc->userProc);
+ DisposePtr((Ptr) curProc);
+ curProc = gExitToShellData->userProcs;
+ } while (curProc != (ExitToShellUPPListPtr) NULL);
+ }
+
+ return;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclMacInstallExitToShellPatch --
+ *
+ * This procedure installs a way to clean up state at the latest
+ * possible moment before we exit. These are things that must
+ * be cleaned up or the system will crash. The exact way in which
+ * this is implemented depends on the architecture in which we are
+ * running. For 68k applications we patch the ExitToShell call.
+ * For PowerPC applications we just create a list of procs to call.
+ * The function ExitHandler should be installed in the Code
+ * Fragments terminiation routine.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Installs the new routine.
+ *
+ *----------------------------------------------------------------------
+ */
+
+OSErr
+TclMacInstallExitToShellPatch(
+ ExitToShellProcPtr newProc) /* Function pointer. */
+{
+ ExitToShellUPP exitHandler;
+ ExitToShellUPPListPtr listPtr;
+
+ if (gExitToShellData == (ExitToShellDataPtr) NULL){
+ TclMacInitExitToShell(true);
+ }
+
+ /*
+ * Add the passed in function pointer to the list of functions
+ * to be called when ExitToShell is called.
+ */
+ exitHandler = NewExitToShellProc(newProc);
+ listPtr = (ExitToShellUPPListPtr) NewPtrClear(sizeof(ExitToShellUPPList));
+ listPtr->userProc = exitHandler;
+ listPtr->nextProc = gExitToShellData->userProcs;
+ gExitToShellData->userProcs = listPtr;
+
+ return noErr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ExitToShellPatchRoutine --
+ *
+ * This procedure is invoked when someone calls ExitToShell for
+ * this application. This function performs some last miniute
+ * clean up and then calls the real ExitToShell routine.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Various cleanup occurs.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static pascal void
+ExitToShellPatchRoutine()
+{
+ ExitToShellUPP oldETS;
+ long oldA5;
+
+ /*
+ * Set up our A5 world. This allows us to have
+ * access to our global variables in the 68k world.
+ */
+ oldA5 = SetCurrentA5();
+ SetA5(gExitToShellData->a5);
+
+ /*
+ * Call the function that invokes all
+ * of the handlers.
+ */
+ TclMacExitHandler();
+
+ /*
+ * Call the origional ExitToShell routine.
+ */
+ oldETS = gExitToShellData->oldProc;
+ DisposePtr((Ptr) gExitToShellData);
+ SetA5(oldA5);
+ CallExitToShellProc(oldETS);
+ return;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclMacInitExitToShell --
+ *
+ * This procedure initializes the ExitToShell clean up machanism.
+ * Generally, this is handled automatically when users make a call
+ * to InstallExitToShellPatch. However, it can be called
+ * explicitly at startup time to turn off the patching mechanism.
+ * This can be used by code resources which could be removed from
+ * the application before ExitToShell is called.
+ *
+ * Note, if we are running from CFM code we never install the
+ * patch. Instead, the function ExitHandler should be installed
+ * as the terminiation routine for the code fragment.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Creates global state.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclMacInitExitToShell(
+ int usePatch) /* True if on 68k. */
+{
+ if (gExitToShellData == (ExitToShellDataPtr) NULL){
+#if GENERATINGCFM
+ gExitToShellData = (ExitToShellDataPtr)
+ NewPtr(sizeof(ExitToShellDataRec));
+ gExitToShellData->a5 = SetCurrentA5();
+ gExitToShellData->userProcs = (ExitToShellUPPList*) NULL;
+#else
+ ExitToShellUPP oldExitToShell, newExitToShellPatch;
+ short exitToShellTrap;
+
+ /*
+ * Initialize patch mechanism.
+ */
+
+ gExitToShellData = (ExitToShellDataPtr) NewPtr(sizeof(ExitToShellDataRec));
+ gExitToShellData->a5 = SetCurrentA5();
+ gExitToShellData->userProcs = (ExitToShellUPPList*) NULL;
+
+ /*
+ * Save state needed to call origional ExitToShell routine. Install
+ * the new ExitToShell code in it's place.
+ */
+ if (usePatch) {
+ exitToShellTrap = _ExitToShell & 0x3ff;
+ newExitToShellPatch = NewExitToShellProc(ExitToShellPatchRoutine);
+ oldExitToShell = (ExitToShellUPP)
+ NGetTrapAddress(exitToShellTrap, ToolTrap);
+ NSetTrapAddress((UniversalProcPtr) newExitToShellPatch,
+ exitToShellTrap, ToolTrap);
+ gExitToShellData->oldProc = oldExitToShell;
+ }
+#endif
+ }
+}
diff --git a/tcl/mac/tclMacFCmd.c b/tcl/mac/tclMacFCmd.c
new file mode 100644
index 00000000000..21f725dc451
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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(&paramBlock, 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(&paramBlock, 0);
+ } else {
+ err = PBHRstFLock(&paramBlock, 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/tcl/mac/tclMacFile.c b/tcl/mac/tclMacFile.c
new file mode 100644
index 00000000000..60a602eb5c9
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+/*
+ * 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 TclpStat 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpStat --
+ *
+ * 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
+TclpStat(
+ CONST 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpAccess --
+ *
+ * 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
+TclpAccess(
+ 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/tcl/mac/tclMacInit.c b/tcl/mac/tclMacInit.c
new file mode 100644
index 00000000000..218ca12a156
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacInt.h b/tcl/mac/tclMacInt.h
new file mode 100644
index 00000000000..303b1d93060
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacInterupt.c b/tcl/mac/tclMacInterupt.c
new file mode 100644
index 00000000000..d0ea2865cf5
--- /dev/null
+++ b/tcl/mac/tclMacInterupt.c
@@ -0,0 +1,289 @@
+/*
+ * tclMacInterupt.c --
+ *
+ * This file contains routines that deal with the Macintosh's low level
+ * time manager. This code provides a better resolution timer than what
+ * can be provided by WaitNextEvent.
+ *
+ * Copyright (c) 1996 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacLibrary.c b/tcl/mac/tclMacLibrary.c
new file mode 100644
index 00000000000..e3668f5efe6
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+/*
+ * 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/tcl/mac/tclMacLibrary.r b/tcl/mac/tclMacLibrary.r
new file mode 100644
index 00000000000..8da1a8119e1
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacLoad.c b/tcl/mac/tclMacLoad.c
new file mode 100644
index 00000000000..551096b0137
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacMSLPrefix.h b/tcl/mac/tclMacMSLPrefix.h
new file mode 100644
index 00000000000..56af9b22c3a
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacMath.h b/tcl/mac/tclMacMath.h
new file mode 100644
index 00000000000..cedefb7f078
--- /dev/null
+++ b/tcl/mac/tclMacMath.h
@@ -0,0 +1,145 @@
+/*
+ * tclMacMath.h --
+ *
+ * This file is necessary because of Metrowerks CodeWarrior Pro 1
+ * on the Macintosh. With 8-byte doubles turned on, the definitions of
+ * sin, cos, acos, etc., are screwed up. They are fine as long as
+ * they are used as function calls, but if the function pointers
+ * are passed around and used, they will crash hard on the 68K.
+ *
+ * Copyright (c) 1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacNotify.c b/tcl/mac/tclMacNotify.c
new file mode 100644
index 00000000000..db221d72d11
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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(&notifier, 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(&currentMouse);
+ 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(&currentMouse);
+ 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(&notifier.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(&currentMouse);
+ 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/tcl/mac/tclMacOSA.c b/tcl/mac/tclMacOSA.c
new file mode 100644
index 00000000000..169d578bcd3
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacOSA.exp b/tcl/mac/tclMacOSA.exp
new file mode 100644
index 00000000000..4cde51227f0
--- /dev/null
+++ b/tcl/mac/tclMacOSA.exp
@@ -0,0 +1 @@
+Tclapplescript_Init
diff --git a/tcl/mac/tclMacOSA.r b/tcl/mac/tclMacOSA.r
new file mode 100644
index 00000000000..82938bf07ab
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacPanic.c b/tcl/mac/tclMacPanic.c
new file mode 100644
index 00000000000..f059f4aee6b
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+
+#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/tcl/mac/tclMacPort.h b/tcl/mac/tclMacPort.h
new file mode 100644
index 00000000000..bad2646141b
--- /dev/null
+++ b/tcl/mac/tclMacPort.h
@@ -0,0 +1,268 @@
+/*
+ * tclMacPort.h --
+ *
+ * This header file handles porting issues that occur because of
+ * differences between the Mac and Unix. It should be the only
+ * file that contains #ifdefs to handle different flavors of OS.
+ *
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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 TclpAccess.
+ */
+
+# define F_OK 0 /* test for existence of file */
+# define X_OK 0x01 /* test for execute or search permission */
+# define W_OK 0x02 /* test for write permission */
+# define R_OK 0x04 /* test for read permission */
+
+#endif
+
+/*
+ * 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 defines replace the Macintosh version of the POSIX
+ * functions "stat" and "access". The various compilier vendors
+ * don't implement this function well nor consistantly.
+ */
+#define lstat(path, bufPtr) TclStat(path, bufPtr)
+
+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"
+
+/*
+ * TclpFinalize is a noop on the Mac.
+ */
+
+#define TclpFinalize()
+
+/*
+ * 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)) ;
+EXTERN int TclpStat _ANSI_ARGS_ ((CONST char *path, struct stat *buf));
+EXTERN int TclpAccess _ANSI_ARGS_ ((CONST char *path, int mode));
+
+#endif /* _MACPORT */
diff --git a/tcl/mac/tclMacProjects.sea.hqx b/tcl/mac/tclMacProjects.sea.hqx
new file mode 100644
index 00000000000..fc875e758ca
--- /dev/null
+++ b/tcl/mac/tclMacProjects.sea.hqx
@@ -0,0 +1,1704 @@
+(This file must be converted with BinHex 4.0)
+:#d&bBfKTGQ8ZFf9K!%&38%aKGA0d)!#3!q8B!!"Cp2Kc8dP8)3!&!!$P'(*-BA8
+#G`#3!aChG`d0$%eKBe4ME#"08d`ZZ5q,G!FhpA)!N!J(0rGdP-!!N"3S[!#3"2q
+3"%e08(*$9dP&!3#[Pi&PXQjr5J!!!4i!!8q,!*!$D3!!*mhGH6DA!*!'kiF4!!L
+bdAJqT3$Ldi`fC,5RjQK+Ai"mIT)55NR(rQ!RhrRB-b[h[pkGBp2c!SVId'RY!kK
+haCaq&5hRD[I2dCBfR+[G08GE9QZZGR51GREPZCSq4k[%Fl9EXc9lIc+GAH0VibK
+8hm)N!!!'!-@fALIKK'IN@0H46AMN9V)pXQQE-(*dNf14,@(N0YNqj@YA4i[+P&!
+VE8XiS4a2m8RI`RHfm8mlVhfQC(YN5lEI5lBIfIENYT&Y!`R[*2YBPdFff8GZ(qX
+kb5Dhb6E*mFJQYmmRqmQ@r$l#*EI2A-JqFJXrXLR3i39D"fa32Z%6XkF%J0`Q)dj
+3`$YbQj,$A`-fm"VDh%BjXN'1l$I,mlMP*H9RIV%Qk@BCjE%VIER@8AleZTaRpRk
+pGTfK3,*`XJICQ3-lqr3@,ff3!&j[HFN@lH+,rR*HZRMN&V)cXT!!@`6!!JJBh`1
+XEjfGYCld2Cjq(q'I4hiK#ePBjbd!2)!m4hk($q)i-XDX-XCEElc5cidhq)3aDcm
+2RbZpJSHRGaVL@VMG6j*kR"3V56`A"PR(ej*bBXE6ZGQXpXc58Y[a4dYddUE*hAl
+jcCZ-fA%62qAMQK0@Pq1Q(m5[C"JakmeDljl#hXilHh3Q[&h@)[$H2TPQI[*Q9dD
+-1Fl[MqH2T4$Mia3N2m$@`LZ,AjY"C6qi)24VBC,fcckIJ)[)LDMNl+aDGlGC+qq
+U05drf1R2K#rQAbhrZR5amCcri1TLQS@08Z5RDCJqipr[[2PQ,AST2Bf%R'(iI[6
+DSAid+6ErRIAA2NQN50KFfHJ#Bi[*4TXC@hHm5,X1fIb'fHGe5m-MBj2b'rCk2Hd
+&KeS*Q*RDq5LJ-8$U('H,*T[aqcE#q!PM6V2fJifl'RFpe-$c)*lDT[c@iQl+Hm-
+aCq2CKPYP((q%1#AfV@C0RY1jYPQFLh#[B9c-kPl()J$EY%hfT3!rKA8Te!2f&"S
+lCJda@GZaDT4-KNU"GE5JS%NIC9L#qq5m$#l![r[SQ$VG(2LRB[J958BVjkAl`UZ
+(HLbe)*-#`kT([T(RE,k`9l`qN!!5*j)C*02`,)K!%jUU)j8NR+i[2%-8fUYEl#G
+P'9J1NjQ`12SUF5QHG6MV+j@4['Ua`C(c*i+Gp4T*lVCUTG6Yf'JRDqdRS*ZUa1,
+#'HDf9Aej`HBEEd*8)P(2%EfBdlY8`pRBUr$T!N%1,-8(`jp[pkNk[i,E)e'1Z4A
+h30Nq`9i9fd2N5T)q13q5E6)jNYL@ic&%YLBmKXTfDCG!Z!Yi$*IYcDSKYMr&Bk4
+X(m'M4VB[iM&+YZrJ-GVD)UcT'0Q!mV'bJAVMC$Z,8#qfRAK-N!$Y@M`dr#[E,AK
+-NZfGH&"2E04JLQarKdHYE0r$BkUJPekD*KXqp#!fqQ#'E#r'BkCX&q-a5lDVm0#
+GV@`hib&S'[dk(R0NS`CcCD-'mf4l')rj25"E)"XeVC-0$&JS@b-HLf6EJFGLfAj
+1@)%0JBEH`IDAH#b9l@ra@#EEJhK3@b#&a&I)4YiVC52@+YR1`D0H0PM9DYPq#3q
+B%VEIaJ1fKHepH-#fX(fH3#UfIm,M&'[V4qaC,a[pZ8%fmJ&$XCf,"cA("Q8r9ED
+Em0JSfarJFCTX(m+$2XAf*@%QYZrLFBDep5FQ[N3fH[j-fDJ[@)GY#ajJ$MEb2PZ
+fAm9MNfarMJGpJBdDEjB0E#-q8!`ZEj80A+#Hf+J[,"2EGY)'XEdD$m9SYHehm6K
+I0[VT!YQqJ-H&XP(ALkaY)2(pCE*49c!+'re$MYM!ljI,GMdHB!JfHZJ5fHl!!ic
+&pJpi%"2E[iYh3#AJ!*I*"QDp3MCk6%KE94d2EQb[`q0bfFMePE+"LiL-f"3MGb6
+F9$f*aejVUbBQL6T@6GaTNBhHZ&+fUr(B,a[i*r*10IY'Z&meq`DXaUE8!iFae4P
+%$S-+J!fmKYGJ!e2J&pMSJ9I*pKSm`&YXB"lm#p[(m2J&fEk""lfXMUC8emN'2a'
+pBc!a%Ic%4Mq6&cEUp9VCi&Ad+EEhi%%IB32,Ej!!$3ai[E80)3Dm36Eib4YP!fp
+ZP)fq"6qaN31F"4Ymm8fbI3D2AjEYfhM!+p8C5Z`%!l(4pp3('hJ+rm*'Rlp&0R*
+lUfarKXH[bABA(VmZfcrMm6CV'dDmT(qa`DQdMU"XF)2INJfXSHq`8G2INHhGH)#
+Cf$k(aqr*"XrlI@XE6Ni,rQ#Merp30MM$(mN'I[faE265RmMf$McH,YZRmB$IBB0
+2`hR9'8%HHDYXF%9k#KXmKrMB`&ri+6E`iPfb8@YUL!hFSPq`hBX([Bm0[JM[8'F
+NHr%[C+0AhLXE(!RHK`eF)4GXB0clCD0A2b!EHAj3YNrLFCYXF%6iP$Sej1JIPJd
+qI,YXB"JBMJf1m9(C`%h`%"Yim9Hb8IH2brBT21!Df1#&GeVE+'SLIbdEf)pfJ3f
+q5Rf``E[q4MEi!,PJ!fr!&QcN5TpL``G-`rBY2$iV'kS8@+(1D(*3F"8E["G1K`h
+qGVGXF)alC+1ZF!TXB!(k"MEU#2I%"UHKIl(p*ajIYVBaj%rS!0MJm&q4$@lj9GR
+J2(m['cMh0GR!NB1bd4Y`0'ciN!![YKrJFCqeMD8Z!ar"KUE`6GRJ[ImS'jKk[fa
+`%[S4'pK+[f#Mjaq3!)fDJJrBISJ('+M111Sii$NfG)TrNBdHJNGJ!iIJb0M!qRq
+9$Ib#bf#M0m!kE260[eREH'T!i$NfG!eiR+hMH0*b4LF-6e(AjA5Vd)jhKU3MUmZ
+"b%*J!@`)+f!+dB8%393KU""Pb!-%&@)+)3@F)'836`JRB!M"K&K#+#'5%%L))i3
+DdJKCK#4##!!c3!c`!V3JHK!m#"H%$U))5B(33H3!3)JN`!V)!kS313JFa!h#"P'
+$S%(-)'5!-J3-iJAKJQK"X##HN!!Vb!L!$+'#,%#@%%dJ-SJNL#m3$83(a"&%%F3
+Ca"H!'M%%-SIiJHL"f!&T4Ga!e%$-J$!JAL"D)&BJiL"8)#iJ6%#L)3)3!)!I`)H
+-3F)JAj!!,XJFC!h5"GQ#C%(f)#+3!$G)#'3,NJ@jJS"!TL!+N!!R5"0N#C)%%B)
+83BBJ3C!!3`J3a!F#!qQ"l#$))6BM-N-X4*!!eQ)d!M2N%m%'d4Sd4F!&X&&d%$m
+3%b"8#,L3!((%&i4ea2*JDJ9K!k%GF4jK'V+*%)hBM&L(i!ba4M4!-%HmJG3M8N!
+@%DNJFJMV%"r%@3Ja`Le%#m%Ai4ELLT!!#HSM##1B)DJLc#,)3YJ4B"%N%&83KK!
+)%3d39"'p%%-JTBLR#(U)1j!!484V4%l%63JGBLBL+@3F-4-4%r%5%48LLd#(D)N
+)J8L*1)NSL9#!#),iL1L)f)M!KVL)1)AB#L&&L%&!&1(`IiiD*UNYp9F`PE&qDh@
+i@#U1e+F52eNdfjVeK@*e[Y@+-4QhB`U[lH&#2HX2M"p&FF$hhV3q19f2`T6'rI9
+XPQme2,DI85j*3cm*k(,*r24dQ%c@BpKf"R'Mi6GV01hbfdllQQNFK66ZmCRC6L5
+c1r#Mb55FUF2CGcZ`f!Va[V5HB)M5LpfF*%fqUB2Ri9ZmQ0VD%V0%Pd@aRlAGX'e
+[q0RXj%`d(l)"P*HUkik)'HkC5V1NhTbKUEPKApZRjjX"mpK66`1EDJ[L,jP1kG5
+#DG$YlFj)'Eed@j!!c)&T'+j-d8MU[Grf3'[J0qR@@TpTqK%0IYUH`8Y6f3l%84i
+2be9jS'[6X,N,KJ2e"LZQRe,ERFdXNNd00[SP[kTC6l0DehK0fj3+I9@@0[dmRrS
+1-03"%4cFYF-0BN%YX@)$LlZ[@E`JM&UQ[,eBHPpT`iEF8aNhq5MV&EPeFlM(i,d
+qECL*dZMQc3Gcf$4db&aIAL`HcVPT4pI"kB`qLEdHeUe"p(6)V*06(4lG0S&bk[G
+T86cP4qa@E@i+Qr-GibCfD(250QqV0f[aERUR8PUqkF,q9M*h+(GmVMLFZA)arB+
+jUE,pN!$ClaJa5XGJ6A8&j[T@+rKD0lHJb,S69"`6JQUdKYPFSpA2iZJ+RPeP+)A
+YHhH'L4b8Xqr3-*pQFD0iBELi1djUU4N)JM"08CB$XkNCpc%MGU"B$E--L04U0J0
+4DmYqUj9E0mcASeTaD#&,r03-KP2c-c0K8V3a(9[NKVM43Nb9TMDV@HLp0T'Y&D5
+i0ic-m&KaY!8BfVd(85J8-p1RKFhFePK255Vf6$'&9"8aU@lahM*Al@'0C+A!*jN
+leIDc`TE1+Y8+Q2aX'MDAfB-HEk#2324cXhS1)@eGrY)@S(dd2QZ,aZHf!"fKQ)Z
+fH,5Pc9+*YJiCD'[$IeX(pYX%jiZ,)hjcCKlMZXR'@j)AX"B9E%lm$,c)1E2E6%Z
+,!l8D@*1DPEiLEbC&Qf`PFT@Pk2Lk+"U0,lULqrL$PZJYrU!P@SSrD)PqiJpDSS[
+iJjES'rkJ*6U(2fL*PZ%2@U+VbD#PeXpNd&*VC$*SUI9''E68qU!-@QV06`BYYCi
+RJjCD#j0"5kf&bD#PeVCNd&*VE$*SkI`$YqKD,R1@38ZA2M*Sk9+$NN&,PjU6$&U
+kl!-CY(5T(mQJTFXqN!""5jFDN!!-@VV8EQ63dQAYCG$5TBiSJjBZG8-CY(5T$mU
+JT8Yp9JBYAHUZ-QMT8Nq938ZAZUN-@VV83'A3dUA1+B1@,[Y$"LeGkS3bD1P5rj0
+"5jFkT!aDZZ`2'E5-%"G%,Bq`*fA3-X)qN8(,#21A3FX)p6FCY)a34j0"b`La33B
+Y)p5lC0!b3Ue,"LdMe,9Nd$*#E*!!3FX)G@JCY)a3FjC"b`LeBKQdM,$f-QJCS9i
+UJjB4ePJ',528MQA3-X*K%"QdM",[C0!b5Kb438Xpp#U$PP(@83BYpG#V$&T'UHr
+*S'@8[5k$PRS!9JBYSp5"CG!b5VeG"LfMe-YPd$*+69X',D28TfA38Jr'bU"PP,U
+[$&T'1AJSJjBaiV)-@XD)F6*S'51Qb+#P(TL93FXBHd!',@2-@3BYBp3LCG!b4Je
+F"Lhe)+d-@XDSBFZJCBbc!M*S'D2q,i1@-HVi-QJCSai[JjBaeP8',@2X84QdM&'
+EPN(,'(9j'E5-XFBbD"PRM@A3-YiEY06$Z6*S'5GZbU"PR"JRJjCaiSMSTA(fX`a
+DaYPM-QJCCpePd$,1@XLJCCbaC0!bc[N!'E5-8r1A3FXiHd)',H2%E1(JFFj,b+"
+PR$-0-QJCjeb#$&V'LFdbD'PC106)c$F%QM1i3+q!Vf!q234ZJC9JMGkb33(9l@[
+jB!cm$pi(e[MD0pb2ISAh`IIJA(!*Ab1(hp(6F"6`bZMkp$BF$PccYA1`&qi"A`-
+2`8H`'Gb'!i#Ti!#mcX`b#%jVM)DMJ-rJ1Y`#6JG'`crm'BHm4Jp[!F2-[!+m"Lb
+$,i!Ti'jqVJ"--M-0rM`$H!fRJE["fH"Nm#li'$`-rZ9VrZ!4Q!9@J9&JNjPI%5l
+9Hla0&`lh&E'P)BBlMTZ"+E@B`cNHpd$F9ELVF3r#24Mh%0a$F3r$24ch50`eZ*P
+1()el$1kaZ-IK(Spl!Zk*Z#IKCYI2&0beZ+ILRSCl1ZiCZ'ILRSAl*0bcFFr"24I
+h20ccF5r!ABGl)Hj&Z"Hh9i#SHbRZCELAiek"HbAZ9EMVFDr'[3EhfRc0LDmdr'5
+KKM&B$pCQH%FKkG!YrLjIqhMUpY6&0j,RJZ&+FF[!4F58Ril,T5[kb()89C9dESF
+#k65H+Te1hRd,mZYBcC3l[ZJM0HmkfBEb"D4(XNY'iH@,@FH1ALeP!GJ[pIbK6D"
+')K'SmlrpB"ZBbI31K-0E$f-jb%i@J-@dPdhf$59cL@U+&bUNRR%A"rVEcm@"([G
+hFD$2!e`F+2C!&`IdUR*aS1R9,Jj8IT!!L`2&(qcL)!%-FA%JDd0G(+5%B5i1k$I
+Fa8'+'1(L`%K'ZMK)'68Z$P,(D"F(+@5-Li0F-YE&38)Cjq)JTBahFC!!@bDi1%J
+b%edFC*Y*,Jlbc@3A"fPRLSZ$r&2ViL!$6A9aN!#+TVNib%R6A4bNTKNZ$P,86"F
+(@@U@Li0dGC+,Jl`efm9"!T[MiL#6cA9aN!$3jVNi@QElF4`Nb38Z$Y*HRBZ$Y,I
+3a8(k@q6L)!qZHb&1-U!R)ClLiL$eVRGaN!"I0lJi5-)0,Jjbl#iA"lPfYiZ$*(Z
+jLi1FqdSA"eRe#KF(qAL2Li1F[YI&3CVHjq,S(9!rMS1FIk@,Jibqhm9"jQjp28i
+LRR)h#ckYLR(3ppKiQP3DeB*Lp6TcAh-1Ba460'jPD6XE!3S",TbeVlN6@Q"0Hf6
+8cRE3qY'eH3rBelambp,5SpTePca26YrBcNlhFQjQ-[ba5(mGNKQ$p-FIcGLM2r+
+)2Kq--CSe61@e5Ik)S6pDL*k*9SN1DFB,dIh3p0$Vd1+#G8TQMC+r2ZRiBH$`b#b
+5r33dl!1$F)R8'$N8flRHGGi*"@eSeFIMZFG2"&MR,hD`",6#-!aQeQT[M9iF8kI
+PR2l4irYk*i9kb,ZpYMD"6+`a++@S5kkKRNiK-IqUIQlJ[(rpUm1c*`9ZHAJ$"8b
+@XQMcZI2`UG'U!hFVUDmrX1V4"pYVLjdKc,TL9fI034pJAGeXd9"lmG8N)P1'hTV
+[rVJ(i"k)Z`Th0Hj"Z!IM(S*l+1jKZ)IMVX%p#[GSh'0`Mm8p$[Gih"0`6m3p#IG
+Nh&0`eq+HLRXDlZQiCq#HLAX@lT0`cmBp"rGFh20`cmHp!(FGlS@i&q&HM(X*lU@
+iPq&HMRX&lT@i9q'ZaldDpaVFDh'r+2mRK)(BS0'aQZURMCeZEJX#8!KFf!6J-[2
+r"6ShfIYLp6QfKi&UD@0r[C&pjX6a@b+GbrQ5ack!kdpdVFRD'1d8h0j#)FXNfVR
+q[!6,%GNNQ2!i&F[RpKedf"JhXjkjDEK5l5cJblTjUrGNDer@epr&Ph,EAlUieEU
+Nq+a,4hlLQGL6Ph,G-9-2LL@r9Fch9#4A"HC$q*X61eb,&3S+QPjcQ(4M98BqL`E
+X0FaQS0C5EZCfa&bPVYPdeV29abYA1CcDXa82Ij0MdM(B$CRd6'q*6''$f3LCG-X
+YAFbijXTN&M'QUT(C+"NiGV5j,(&-9GTEYYQ4JhKf*'&f8QDm#L8efcE66QbXc(U
+dpk,CBTPd5qqc6,TYLHH6)+ckdb&5DCiqdN,Y93[fHDBJ1lhCddF%lA8QGXHQdBm
+0SYiDR+"-'Xl8ET`@V-I*HVfdI'LTDcZf'1F-["M2A!@0FkD)DGF--KR2G+F%MTN
+@$lcp,Jrm%QfG,RJ@[YTpNq#6pK83-ciC0Q%mdafBFq`X6'GlGR5jFFm"DBGc9cR
+6F"Si&Y[0J'VJTh%ejj3[G3CGM@F'B)eR`,Z-EjTk"BljFKN#PPeVR%2%`$m0LS&
+cJ)Z"I`BD!qmd1JE1"L$c[ZQQc3H3!%E)ZalUK3aBGVNR!#ERlN&Qi*C'cF$C!'H
+REhHTI2J-r0S40)J3J'M"re#C$C6QIAXD-`@SH9F`YG1[d!9TC!fFdq!D1'Ia0A!
+h%*[h,G3`VA4N&FPflKN%b2$2`$[(3B-!@4iDZ'HiD1#GjU0jj`a*bNG)l[TZpbd
+f4CDIG[NR59h1AhKUi*IKUS&hQUpf1KmU@)+h"SiGh$8)NH1[K3$&FUGjE0kjYdd
+c+pREI6@RlA6XESF-Y`fm-r`fm!iiEZ#IjVPjjqj5&Q"BYZQ(31b(k)CLhlm%aRk
+)3h$X"qJ'C0qr!-R'[E[E6Bcf*M(HBCXFJZC-K$aD*5,Ni0Rhl!CShlm!d@Rh8[%
+kB0Thl3&U2dJ*UV-K`[)Ai0UiPeZi#l+0GaDddkl&"ZN'EYqr'lTprb*iqa%+m'h
+FLi8Y3RJII)I3h3IE2C!!(F*e#DV,-&f%k")mpd*c,bah3A))ab8S$Q'i!-%9q1f
+$hMlB,8&Z'@k,8&Z!f4,%K[!D3QX[V*BJY3LRh9$D*dQ&JP5I(08M4S959%Q)+XY
+344'U*%(e#P#pmP1Aq"4+6bAK+C5G#U*64A,U%jckj+D5f&5@QST#8d&Q+SP-SF3
+8#NbpmP**A#T+5b9K#GIJ6d3#2%cmHNK(5UFZ"+(pJBiZ`P$+6G2@I'iqmSHjNAr
+q[cqb)GTr!b3E)29A)&R2cTm%b9DTV`+%k+N!!ESUJ+IPkcG&@''KRYe,Y0ADLd5
+FF)QfL4-Zd6Ca`LAD*Nki40[%#CGSQcMK%Qd6*ebLEH+%5l40R(#*YSN6,Y%fFF)
+PfLC1Z%6Ea!QAD*Xii4*Y%bGFSQhLK%Zd6Ca`1ED*%bl(0R(#jGJQ6VJFfm3*Pf1
+E11&bE"-RA)jYiS6,X8fFF$QfL4-ZacCa`ZAB*Nki(0[%#CGMQcMKFQ`6*eb1EH+
+%bl&0R(!jYSN6,XFfFF,Pf#C1Z"cEa!QABjXiiA*X%bGFMQhLK-Za6Ca`1ED*%bl
+(0R%1,mG@F3i[aeCa$Lr(9R%1,mG@F3i[aeCa@-CA@BkYiV#`Vl)F@m9KU9pP1ED
++`q+rbR*X&BIPJ*APf#S1#`3Vbl&9(*B-9TCMUcJX)U`XaeCa@&CB@BkYiV$dZV)
+F@m9KkA9P1ECHKfA,A)jp*%i%2a6LrmkZpHA8p*9'"iIkLRf$!q-$H'iD,ShV*jc
+X2FFG`N,1D#kplRMZhf&X-mIrNaVZSH8``fDZjQ4R@Z53!*,D2+PqrUTa[lRb`D+
+jl)%ce[%h'bF@GTQhcpBLrNl4RGAaXG24Djm[$fhGe[l6&krr)MFmh[l"UhX+0ca
+jD'PKj2SPrAb8lH"deG4HF[6I*VZQS%%bqII#H[d9Z*l*fY)+JrTd2G![F&Pc$Mq
+aj0SGh'U1T"pTUffRJ@`N"kSM%NHFrrKGhf-C@c,mR111T0Eq&8V[9aKYCiL4`J2
+q38RK35F(Bh,CeX2"V"eiS"I#&r&!r56TeALV@9EI`hU'"dSCbr-$MbRZ$PF`krp
+Z2&$m325$0"iSUr@-%"6`3%mZG1cmY`%G5m61Ic[)(q&CEIjEB-5cIpYVMLAL[`9
+X,,mKmN)#B4*--BRFcdpE9drf4m9Y,-F+0rfT6m5Fa"pCb[`DHrD-YlqTG2c`Mm$
+V@bhpA[,a2*V+)rSpkH,*rUYpjfQUM1LhT8rNXDfNhk)qAYI0eE%A-R`UMke$%q2
+p8l-Q501r'ZqTQMRjTrTYD(ALTqDfp-rVbh'['XdeVXL"Ac)0,`rjqHMZNrl[rF,
+amZMp3kSRmZJYb"0jp!UT2mQM*j((mfKHX(RiXVCllYlpEH%pkPf'(d0qrCDI'Hq
+admlGqi@ehYl#Jl"rYah2[DU64ql[MCqU"cX)G4jp1R2bKrf,KIAb82Z8,3&jl$a
+@fA*Gq3XY2im3L4l2BlpK(fkjqSGj2jkrk9j[r+fGC8(Z$Q`"GVbYhYBl)UqmRcr
+Tl-$r[a(R6M2ZhIGZrXkjPdHVQAd&Tle0`di1e)RXXMKpAQPdQFF3Bq0V61E510!
+[HIrIS5$ldq3eeR!NMpECD2PAZG6HjJSrDmhQ9`M)Je1(*RQZVJ`+#1-ZbUrf$mC
+p5&0lQR%Aml!'-1kccpZbr#0L6CAYbr@`I$$hNJ,k*(1[j('8Z9Ib1-VF(mRM#(-
+[jk'Cq`qZaDd`1hS,GQGkc"h5*mcpR%rdQ2X@f$9IpcShNFGMc,e3MfHCHl'`6c(
+hFKj(QAXKM@HBHcQ2,(0IGmUrQIZkG8mcpqBCFa5R(55LIj+JNlNIc((DqX'iPfr
+LlpGhR+FCpk%m1*B!aUeq[1$NMFAHAa4Ncq!h'00jQlJbYSKiMc'Q5Kj(QFiMH4a
+K1Z8m@N`F4$GIC`ThVcHR6(d(EYZphE#2HZI0X&hIC35UdC6$aP%JAY[02mlp,rr
+@0lYj+'CK#FMM+,-Se28CCP(1S`q%94lk"b$#'&iHcDRCX1%PCN%5cDR!8pQ"Xd!
+"5#"R+3p$i6"RmMKiDAZSYIc,LLR&aQ[BEL6I0cAh5KjR6mdYje'G8kV5e1+3!&`
+lfrcHNlbVYVjFqBjM+Tr3cU`!H64%D@Vk!rjHhD%m9#2UBGj3jG@DPa@J(PUVXJ,
+Ni@Y9B4k2De@lXNCNAQB%DVArkpjcJ4K!cBjE$M1#flNShF[MFRC4X4k2pTPS",Y
+E4rX9MD$)lEdmQ%4h1,"rSZqe4Q!&k&HY%9J"mY")VZ(-q@pGG4lX-cM4(`2,,`k
+Zb4P4'YP@"4PURRkcm[`HUeE")9@P22,UG3l*8F'Y!(PSp9UeBZ[42!,e1Xm94I[
+Sc51VVKAc8%cD%T!!Kk+ZPS!mVNL2e82pMN[QcaFcH55i[G*LE!(e8&U-(5!2"@G
+K(Q8m5qDK0)*MH548jedQr3qq*ZV4BLM'K2f49Td2p3FDNb8J$k8a(H[AV-CN!mM
+M3'VqefI9[4r1l!aB*8E`X(IJkYqE`Td6jT5(VZaiMrliRBFU4ME+cX-pF%dq6a2
+j`$[)fj1-`"+3!)GL"*D!2"55f`,bD&NKS+kl6+[eCKl*Z3q&9-Ibb#,9mhP`k[a
+AmH28Ja1*NNXbMdXHk15aEb@!2+Tl[kA8LAMpFp@`kIlE9hePmYaFKCViI[jQKRF
+Q2[c5PM)96aFU0M54G94$+d!H@U@c!Z6KUh4K(RQ9,TR(kGb5j+YL2I9!&5[@`mY
+$Ue"@J,VU5C86HI!H1MEVYp+iY[JL(aTVNH(ZXeF+3!BE3"i*&DU84hjV3G#*#4A
+Urr0!PlB#e09$2"Y!(MlL+4,c11*4fNl%fiQ*Pe)pmY2LAANS4Q8*b%0aB%Y!(SS
+$f`,bD&NKS+l-8*j!mJ5A2PD20*FqPNH@5pX!mYLIUf)pc%ZY#G"!hG0RVDrQ)GX
+'H[,3f`E8U&1B4hlE3*!!KjjVS2&2i,eQP&D!2$5Mp"Uff"mG@`+#ZZVC4bY!(Kb
+YcKkV+a0FCmA0jc82rlMPFQ8&J!e`HMkL[&9Z`XT[H`h#NeHKJQ0MfQY`r&L@pKU
+dY)lY%LMA0D8ep*bmbY[C(qa(1(%X)2X4#J$bc(k%dSj+1m"qK2*iK-a(@!%kb"l
+!h#d9c03+J*af"D4kp*5A3eT!0"f,bhE)m&J)Y"Kld(E)BmHbY"hbF5fQ+a%EJ4E
+6'd2Q2RU1I@[,C(RPPA2b'`bZY0RR53CAbH-SJk[NFC6"2C,(%3CAcZ-ajPA)ieR
+Q95c)8mbVR-G4jP9)iaRQ9FRM-HE9PBLPB*GAk35l[,U2(B!4k$bX%VZmbUZmR,H
+Y2+%Y0SaHHh46B3)C+MrQSAH#@3(b1'-ZQHl(r9aGB4D@%ie5laBlhKr*h@,(qp8
+HS1RSh@)Rm)1*c3[UV@90jp'kfLFd(BlX+(ZqVMU2[RqZ1Cb(prGpr"0622qE-[k
+rSVlKVh6frfbp*r@hGI*hECDm[qqcqIchMV8$$a429hp"C-(lqclEfpr@*IlTU65
+TDcdpUApkkTQYXjLF&ek+rr38!3&6rebK2q[,UHNVM3i1p4Al"JI'"r$F0&`DleY
+CT3'X-"KJL6Qf1"5`ija4RXA+a,paH*F"8'@ddH$T#rN%TURq`86GQr'hA*`Yj[M
+m[CArcUA1ZG"c15meaqA[E8,B!hTITII9HVITICij-AqIRrrGSC,IaHp#[5p5[Lq
+6r@,PZehj[ecjlP$qPqLpdkc1hjFUEV2LAQC1b0q[@,%KA`i4L%UQH*%4dBP)PN5
+X`Pf0QqiBM(X)lU'i'EdBMRX%lT'iDpLKkKk0H`cZXEM(U9FRi*k)Ha,ZbELRi'C
+'BLVZDELRijk"HbEZ@EL4R'IMRS0l,QjQcpJM[`"h(Hk&Z0QDZaMh%Y`RifEreM,
+Fc$1Y`,d5pbVFr%V9kK8#m)GZLBk0MSiIG)UH2lDY2ErPRM5`Y6TFj$S'rEH(9'I
+Mm-J3!VYk4r51kKh61kjh2lhlkce!li&k9qPGVIFJ[3IV286[SAS2dhZihLIV29,
+['VfAkMe+lp&kMp&lV0lMp"k[p`5p*qSp5Hr*HNr4ZeE[UAT2dhZkhM2dRURh,,e
+2dRZChX[eAU(h5VeAk9f[pfUpeqLp9ZmApGSeFL,qZHqDpSrA!Ad5C,EHFr5HUrF
+m[HIV[D#A626iCVLl1MbkEB)i+J(Up&kSpb+p&qZp*2q$60@9r6plcGkPTDIqIaP
+rl$,(aU-i@QI1QAmM,Gf6CNHQr[TI3VUVr,+ML6AhN5BV[lf(r@*Ia'-fr(BIpKZ
+,H!18Ar42AEpDq296IXC[&[`LbXriZH6#JeS*rLTJf$hlZThXN[-ZXRiANA0l#S$
+MFG[m'GC[d51`H8U#ipf1a`$VYraq&Nlm2S$(9ZZhE!Jf6eP`#Z6qbkhIYGq&c9-
+@R!,(%qZXhhANqjl#i"6B&j,IlG3$2+A#+HcUqPhf-'bH%Z%82XBDH(lVYm*'[[L
+pTZ[h[Xr"K[k!h`eih'Mp6[deHm,cLd+,F+qaIUYr"`Gd#XU&hM$HqYh)(PGaG(q
+IcQEfr&b1*+*EU$2b*mJMS[jJf4Cp4c[Fa+EGNkA%Ja5+,[Jf5M#JNHAcbbQrfHK
+MjrmD@43IpS[GbaUSH1"%bQr9pq#lY*(0(-'$YAp'cHFh@LRLTIcFFk#A42C@4Y)
+-f9"MrBmG+SF[GA()j(%+kZXL[aC1A+EbM[qa9eI"$XiZi'+LVNNrpi0rkH@4LKG
+lXPIAP0p'kM6I8aqNr2T4Rr(kX[&GRi40rc@-Bcjb+f&1qcAXfJB(CHHmq3l%p2#
+[i4aU3qq`IQrl%DDeGeQrhq"HeRGD[rASEhH0pIX[%[Yh+Hpc#-UHhrd2`DEk9rI
+"hF3Hcqrlh'%26K)2h"aLrDiP[Vl(qTd'rFTGEHZhj$0`i*p)L%IpepKi0a22`%c
+mhYl0irf6X)(KkV`BHTIl@qYh0c&'e9R(ibherSmI`+Chk5XGiZ)1lMCF6La8Z-&
+T3diaLrF00r+rdm"cmU"Q(SifRNiXqk$m90mi8pD[rMH`h@EpjRmIMj8fM`p49m0
+1hJm68cfrYh`CYJm,4p([2"aYf(m!0QT&2j'6p0-[FREp)pC[hKr`q)601dTqm&(
+VGcVqm-K9AcCp'JiIXhi[BIeZYAl[TTC(hF"TaFq%pc4mD3YX(lGq"k#I403hhq!
+'K$Z%dp4TK[AlqMpKZe0qF"fTdkA8EF"RH#'Fi"EVpa$c9Vb!dm!qPMl6Iqh`5G8
+EIL*i0Tjk*rL[cN&J9G@PeQrG&A$iP29l!2m9*AM@H#CVICIeqmleA6alp4JF2Uf
+m`9h"XlZSNhj'HD1AHRh61%4-qDcmeREUfPLN$[SjqCh@aD(EN!#6m`[VerMh$Ti
+er)UB"6G8C`llh-1caJTj&6K*(Q#cj2&$lK5iarUp!AU[i&RMKmLT[U"ic)e,Ael
+2H&m8(UKi2TkpPcJ0Ie(R6F"4(mmZqbSFlK9ZS60lH0C`"@[`CG9*e8r`V#&NIm!
+0b3rq)(M@6!lb&H%@HV$JfDhXkDqU6X6`m+aa)hXH(--2AL(pY**mpf[@lie2G2#
+X)HC#"$J&&&AKV1"Cid@2`3(1Umj0l"r"XeZ)@h"cm&ApRjD2CpIZKZdqblIU`+Y
+m2,Z$I8`2JP[AGI(X"R+8EmU2AT%kE3'(FiLhkQ`JMJLHlHFZJ2Z&+aGfmHbf'f'
+$$kUm&i224baIGcj+A2Q@p6XCZRpdSr9l,E@("p3Im"!2rjcEVSEY34Y[aBHlH@a
+McceNr9C"'IIcH"8A+JKA(IQTEKkhN!$M+IkVH3Np1UL"R4**$NNHDpLApcEm)rY
+$h,-'m2$kUASGGbKm@hJ!rRNk4p9kiK#p#Fm!(ppUrAC3d)A,U"6@SjrFNkhIIC`
+lJ&XTP`hJCj*IeFAA`30q4*m4@r*lN!!jJ,(Uh-bHp[+VANh1iqd3Gd[S0mQ[fN2
+0R-ITpM-!!!d39'0XBA"`E'9cBh*TF(3ZZ6IeFJ#3#!Fhph4)A`#3%4B!!%*P!*!
+%rj!%68e3FN0A588"!+rBraHbERkj!*!&!3fp!*!''6N!!%21!*!''hF1q&l+kA@
+CJmc,V[2AlIADK2)hYqGQ$6`jc5YD9%k2kcABGXEfbYE`c&EZ1QL[(cq[K9YXiBY
+N)6YqhX*RjAHmLbrR`-@*REFmAcMjaF[cbmk"NFA)BVSS@FjMC,[`4Drc&Ni@RTd
+6J3lJa1H9$6JGj&RQ*CmXI$R#"d8'ZqF,f`+`"CJ!122kBZPd`V+XN!"P"@CE!FZ
+a!S%YPMAT12KF%JJ'S'UT6VFi+k+C6$bG#6GQdZFjX9cHei`LcE&%Y+dYi@4MQAK
+EERjeHcDA6SE2G$CFN!$1Y'5lh+YL-5HE$6G'FfZlICZMQ9BR&fjbFVPiUVAE[6D
+HF-)4H23kRpiH6l5%DpER-Y&Zha2RR4PQPbjf8VfZLq,CD$EV*&FRR%b[IhdmYDl
+IdfhhVKBiY[USSj!!FE)0&HX1V,f"'+RH1PEA4T!!@CGAhG*`3eXZRSaIf*22NZM
+jdA"$HkkY[EX3bR@J5-ThHD6,,4+0065&)`ik%8fDFK*G!4UMf9Jd-9KGFGpHhXE
+'kZfY50IK9U4rIb[5Xl'QYXqY[rK`VFSQq`Zme,P`Hh@EeYE(9qp%1"fM"(-k`!M
+@k4M$J+FMP&"2"aL#2Xqre(4HN!#qc[2FqrY-qQ8B$JXK"N"$3[3#ShBYSD-1-!5
+4[[p`#I["8[Z1)UD1-J+E(6&fY-%3J(VqB`dp#+@HH`qHqVkP6LNKU`j3JPI9iZY
+fiUX%+3'X4"K"@!Nb$,%5SS5a%Q%)C2d!TDEdSr4eTHrIhhTZi``$E8H-!BKaBr4
+#VIL@X&BL$)&Y)F#13[E$V6L2iUf%'3(FBT!!NABBJP`r`+lf(J4Ghlm(G3[1`be
+6`Pf*8!*H"0q*ZSa3JPbkMq!Y)`b$,Ie,5%[h)CKe[8Y0k)ESkd$AZEr$9(F-3kX
+AB!")9)"H8+9M#9(T2J5RHHqKX[8$+6e(8C3a4L$8Me!Uqa"iZYilQhB30PhR(Xc
+-H`lf43NYkGi"PC*&@+&AI6cEchXA1D[E@eZG6&M$B&q-fZE'%M&H2qp%B&r-!4D
+N8i6C353IbG9PYJ2*L[YiYZb%N939U!rN5Gra*(AYM`rA4e1Yl9'!dBk*f"b&*@'
+!LC1*-UQq+#H-M&$QKUYD@Z+-$ZJLp+)fr8fbUb`5D,3Z%QD`-1)rAKQfeDkb--T
+S64KMX#"dlUY'Reb-Gj0P6IQ*C8eel6ABQD$8@Z9+Ai`h+2'mj3R6JJ54qHY,rYM
+eX-)X$YibU-[`[JZQ@CC93ArD6j9kRLE9A-#SlpP'8lHf9dq+U[3"D(Le$@mjE1T
+4jNR5(j2!dFj`SLe1*PZjYUpH2AeN&re95[RDUV6lfX,D'qmqH#IMRB*h+Yj*H#[
+`PZ1GL0I@qDM#k65#36U%3Y6JE3@Q&e'N-VXfXIUPAC*k&90PY+KqmFVBZRJ,'GL
++TXCUeA("2)U-5U(,9#p1)4fZ6T!!E@BV,ibhG38mTkia[+4UHC9d9L&2!J*,`R)
+VD$!h3*K43M`dJ)TQQH$Lca[`(NS$'i9b'0ih&KcHK2I)JX2KH'F6KmSdC"&M*q*
+9X#Y)k[BN8CTj-MCc0K68&NQqA%q%R#MPdrAd)mcGb6cHJXUUH!1b0pk`KMp3V!i
+%,0%2)48HYG#`T3L-,P5HEJ5XJb)V9J&rU`MI63Uq0IQ3!-(9)1Dp#+`GTE0G#2(
+`a#YU0eXTi[PNMdUKljBeeP@fCC`emI8$r5bDQj!!UarLPq4N`4C9-EIh)4IiMAk
+eB"(PXfiV3ElTBp@p(Li44K51d8BBB4r6YJ$+*,'49HiMYRHVbQVECLK6a(B$&%@
+YBIXLP'PLqbFSqiU0,6pGfi)(3pP2E'q&-N0XEi-b8f`NmrZ,l9)SB,R+pM%S"iM
+Y5e!1&0[G8&iRlFRq%6`,%4S2%PXY&1C1'cNVqiZfhb*qLHf2"&*TBpmSD)AYai+
+XE$hfJd*Bf,Ka5B#PV3R+Qm@@%TbPlA)AEf'l%FS4B[X@J9PXpd-j5Y[+f$0(Lqe
+%+-H)MAeI+EB,S4`VYSp!B4eSq`+8im9f&j3jBRX8bJQ#LHb6Z@)l4H'lYN@K-&r
+DIJ2+5@*MImm6fpHJX'GTBdr-elCbiJ)aLMEQaVaSDiCbUYMDSC`QYJp#)Fl3pZG
+3&SVYZe!)aE3p"Z9dEDYJ2eH,M6NX%Y[ESG5)l4)Sl((D2J'&8ahD[JlP$,'aCRA
+D0S&iY%4Xl!h&)f"VK&)[0Q*H4'c%el2%pLG3'X6f(5L-5GY2SCbYE41*@8[&a[i
+K2Y#f!JVlJMEf"$'@YJp!@5ifjXFiY(dEbNUar4X8iL6KQVK*6+#0Z-"HS)heHSI
+BeN%j9fc%dAH+lD039SRYUe!`59+f(d%K$T!!!T!!!mL%C"*a+LBfjYFLY[1J1'*
+l,j3eB[X$++eL)dl,M'J518&FE!p"BAa5$1)qDd8EmB2EcE5aGNQaXEq*Ul4G$b8
+Y0Q+5d,Tpf0rX@9)9mTb-f)L2-L#Fc&k50HM*R!Db"fQl$XVjB[X+&()VfKk!)Y4
+Y#ZZd3@c%5Z)`EHbIAa2EEd+j5'bIK,*4E(m*KIN1#5T@&Ue-Bj[L[f8"T+Ua%IJ
+9f,35FmrAK2RNMFX#9`3Q"+A1VJ5-!&PfbXJXp*9Tf35p5S*&P9re92rNljGXrm@
+*BFFF&Y6GaPZ'Gfqm%r(ZKEFFE`AHbALRiTf#PfbFk-XT!DF$R!T`'X"%1%8J5R)
++)1aI64Q)r'5jC(9Nq@6hc)2-P+b*l*hXJfK)PNkQ3eE1@K*"LC`X2j'5E*%XNHc
+3CCYN3f5&C(*%&aIab6L*pQ5$l&fb2l)qXMfb2,)lXMUb1E)iXMHb0V)eXK)L-KN
+#8CNS443QqT*jNA'4DC&KNEQ4QC&KN3f3!&'4fC&4N!#p%8r)V-LSb+6)3XLFb*L
+)p'4)C%CN4'4#C%"N2Q3mC)*N)'3HC"c%($)-6KXiCA!(GQ6KR%DimcT1%6M0)$[
+La),-L0-%GeV(U3&C'+F%R!j`+N$@abN!f3eC2pNqQ5ba8pK8e`d%UcDHbA)6VMA
+TT(,@I%dhX(S5VNZfT61j-1GTNFB9"I2Td83LR8k&ch!5E9CN49M[NkP!+l!iSpD
+XD-9UNS@0Td,Sq4ld2VAZdVGTdV2YBH9L#H`E)'!QQYP3'40l3e09KbeMqI0"CPH
+AbMQC0G'B3f-%Ui&+EkS2,fe2BDR03@U9LqVVPBZUKrK9q['iHPFER6dh[QTHba(
+D%G%L+b38('3'fC@%GP-4rHjL0I-G*%ET)&VelTHeXVUK0Yp'bXrV+TUjLU#D)3l
+kFk[9eKElQ$5(04qqal#`#551ELSk`N%j-L,XZJRT1Drb1!C9q`dS@lHc9&2jD`p
+Y%hI2@1cF2Vp#,Y*PfU@cdECRRXm,P8bQFqPH&2%#MKHN0eDi+GfHL6P@diCXcNP
+LYdlDM@&dC`b-T#[EBQY4e[`'SpMmr8lYi1m1GUfh&TGC1eGALiZU(@ZTA3Yah[T
+EcdCPaaTKBAZeHeI9VeaK$l9ckl4cae4!X@0rY,"ZfEeFkEH$ZcKC@*2X@)VXh6(
+Vf5McpmGHf"BcQBUl9F*jMMYciTc"h5VK$-,G+Z%-`GdUi3c'h5VKh-6G+Z&Fa0d
+UiDc#hAELr%@f5Y6XaCdV[3q[E*9B[iGAYNUXcq19V4,V0UN2K90[f5UarK'[E*9
+Bpq'9V4+,@`fb94*JA@5V*-!FC+XN`&Q,E*8%1&q5VC)!jc5b94,Jh%Df5J+F-XY
+@5H"++,*9%[J`&*P*"MiY[82Ec9"NUb4`1a6C+JQ`IV*9%[J"&0NU#@!2eGdU#A$
+$3EC+JUbIE*8%@8IC+JNbVQb9"$RMNDf5)'GRXP85j-a+YNU#R&A*9NQ3!(dT@b9
+"eP@f5S+FG-Y@5I"@+,*9%Z3NAEC+JY`HNDf5)1XR@bA"ak()9NQ)[5GE*5(@3VC
++3Ub0E*@%L"Qb94,LV%ff5N+FLmP@5BJ6C0NU#E(RC+XNa#d#f5S*h34&YNT#h$+
+5VC,3R9"NUb6%,5#C9SHib5"E*5(LK'b9f-3if5UaL3Zb9@+c$f@Va'B2b&D*c4a
+NUm6QV&'f5Qc1$Q@Va1EX6lC+E-lkC+[%j[a4YNTXEKR*9SPp,46C+V%r$N@f5Qa
+LS@b9f1a,f5Uali!L@bAfhd#4V4,l(LLb9@,r$)TXPA!Xl'k9'#+%Y@20h$PXedb
+AH%L-)bmKGf![%[H)DF3lBL%aKeK'E#-QN!!2N!#MX2F,-f&L"c'!q%$1`riPAf"
+2XLq)mqa[BJqaL,K!l#'@%3q)NF3)mJ4hcNfH3!j%AN#F*LFJTT(c%2X,-fMf-RZ
+BA)(p5`j!A#GA)Zkife(%5hH'5b'1NKm4FmQ0L,IN6Z36K6NmF9@ial1LeS+4Mj`
+&a!k"5rQ8j,Fhe*idqQ`m$[YZQKGRC0qBlYXJBhADNd')ifik8kqi+AAaVGZfA5H
+EIX8J4MK4qfmGkrfm(J*q$YbiFYZfacP,-VF*Vp"iqjSD&iiQ,Q#9N!"EjZBfiEj
+Y&emi#VK`eECYGh''E@U6iVdVL"(Q,T-"3&DYbZDL'F2$&IX!G[(cjp(qVikQKf*
+b)i%E"ZlQ9Zm0+rGL!+GRl[D@+8"qH69"hbj3YF*U"bp&"'4Gb1!Q"!01J0@k&a$
+"+)F(i'TBbr+hmG@'ZA&9HLq['&E%#$FM8"+dbXj+Tq4HhB!%)T&'!`"$j#4#hEH
+ANbCp-NhIDGXY2%I&'c4QJ$bU'cERVdCY&p`VBQ&ha+K1C-I1UcbGaj,c-QXUmEj
+D9kj-4eC8bfErl[jBfV48VJAXcU1jCQ9cjHVqXpQZG0cF'K#9apTFmYAQChqS2-i
+EkFDRqeAPX4-lH50X9(LplcAm)!+I%fq6b`flkpU5MXQ0b0epRmPQj)EQlM`SPH$
+d`bMlG&e9(NpG6h`jMqi,9ehb1[-%diP%kZ8'b*!!G0l%kkpmBrdB`Mb6Kc'!S65
+F[Q3AFKD2qr@*SB,Tl-61MX1%1r)i5QlL[*E(5%&Hb'-(!2pR(U0)rQ`Hc[U4JSb
+,CUDlqcA26(G@ShL$XP520Tc)-`6NJ30rreYA(P*amaK'SKIbD0["6NIVQXr$#-$
+F94jC4birriqB*aMhdhq&k,amfLICA,XK)!p+*CVNeEVb1&&K-kC2bXd6c0e3`8b
+0!-KT9N#UTkAl5'QAQ$pQ1LMY319IR1N-j@%fQ6Sr2iXCl)kACM&MH6`hLaR,ilP
+Cc+imRTh&M1CKATM&$06M&l1BSF+q1)XCcH1j@Fa!'Ur-BXEbH(i@djq)L@!@-a*
+$lSh[%"iM(mfeqf$j8f,mQ1N-!Y&,-jd5iTX#CMVjUlcQ'F%pLp(I944q6k&I,K+
+Yr!X$3Qd#rMmVM$&Iq$'$(&RTq%51ZN9JIT0)8eh0028aL44qG9#c[Lf4cVamND)
+18b''aA4SC*ICB#C"-(hpe5d$$&(Flr$UKN8eKiF2Ae69A!@eXDkk@@lNQe*NJQ8
+I-23$MQFNrl8aBi$XqM51&U-!H0PGphX$Z,`qVGJAaeI+K`E-+B,bBiqR,S@[Ed3
+6q)0(E5DU[rhb[2aUG+,cEhBbbAJUQ[Y&qUU0c6AM6(f3!#p-f&2Ir(RPUhGl%JM
+c+28"3hbc)[mPL'!('Fr6I-3KjCJe(SHXli$a1$aH'Xl(i9IrqZ[$!kE(M1I$JkL
+9!eqJq(8r$VRAp2&mq*@,+D0IT8!FI[&Ll%X9L-0[B*`d(SFEHe-0bPqd)&["F6P
+Sr#r-"!2G8@,L`Ia@rNdU(M1f!B1,"e"$"VSMeF3$IA'PUEf0Dq,cjA1e*M@8m2&
+!mi1)NmZN,h!bkl,KTP`de4,0Y-Jhj$E-jfIcm%pUDZPN-TfDhpLq1K'2KHY5X84
+lLf-'J`dI$qcr`!1`##-C'"6aS%c2NqC(X(2$l`"Q`mGAcUdm`E3f8I$aS-+JFGV
+%,,HhES`jFf1+qI1Yqp(V!`GqeHPGcHU"M-,IV%d)[ld0B4DME[S8XUC4[Rr068$
+q,8rrHpCSpJFJ-EakBi&eG,qLr&qL8RreaN*&lkIAc#p(-e4hEjFp#)3,A[P[#C@
+q6@%mPEl2AjY9C!mVjPY#Z$44["&I[+fH2lN!,ei'mQkV$mAK[aEf'ir$S`dcaZ2
+`L[2-m6M2h*a((0kF2f,dPMbmH(2qZ2'mH50ql*Bmi["'r0JYHF3KI4Ul*Drq&M&
+q5ajaH1Yq6[j@pN!Gm`kQea[a%rIi(fl%llA([A!MIS*hFE#d2@XmPH,&`4dVEkB
+5fF1+Z6M)(ILCh5ZIa@paQ`Gq[[h[8[`9'prq-i)p8[K$@djprJNJZ-KC%dmjf9&
+kBa`4!m`IfS``rkJca)JT$JA-1$*$cYIZf5F`TqfbCk,!UE"IL[GNqDMP+HU+['@
+G+Kq42%ff4KDSZr)mpU)cU4,pG0'V*FiLp@F'bkS4[eV*Bl(kdi*PRB'2Q&,Uj11
+L5d3r8q)Y&Ee*p'E4PiPH,h@+L2dXbDp"kYBSG6TEe80rSp5'UI#CdZh#e6Z4dUS
+-Fc*2G$N,%PVDd0"FF!SYAVT-E5R40V9T3cER*19@DpcKYPT&E9ep$GV*&Ve-p,e
+%,aGGa8&(+$Ydecl46c"i)"DG*,X0B4b'$6HY6@"eYcVG`RehP4T#lLdT646GY8m
+@IB+I8Q!IT0639"A1TYXcXA`@U(9)G,GJdd5I+Rlf9GFZA"FJXMcjB2P9Pfd5YaR
+8bkqkeK,lrU,2XZkqqqk1efe`hHXGd#'IF$A0L(QQ)i`mq'&3i@R'QElhHp9NLj0
+@B$EE1CR0G9iVf2r[q&%$q(AHG3NIcCk(AkVS0`ej@,2K&brk(BTi&C*Ik&l2ldM
+ifCYdIJ@rbNqM*[4$IJ@ria#[c%VQe*dC[iZRFDLc#AkF1aAm[X9L*A2CVRJ23TQ
++H+b6llFIIi!c+CP,T)Yqqe+4ZKEUm4Ef`G(*YQbRhdNVd6Ieb9aVCehhCAmFf*C
+aePKP%rL$(Xj')3XI44mFTrcXMEcLm(VY9h-NmTLNidhNVcS1dRjRRSL2rYbMr6D
+H!SH$TAi(S'cPfQr$!h!i42,Q2CeC1VrE@%bj,EM[19!QkRMRrLYXEa#rmk"-e[%
+HrN-fX2Bl#(J3f%[(1r4q1"`QmGC$18AlRI-jf0iSIRrXjE(ahE#p5IZGqP(NFCH
+1G`b[+,aCiRfCrDIpCR-N*m[`dlp*80"jE,QF[DMpVN6HGTh%Z`B14dLmak(B%Zp
+k!Shim5[3UlAIeLGKNdXK#jRh21ehaqGK3eqVIU+bMrDl(,eP(50eBMhfdhQI`VT
+@5RlX,Ge2pKFq"GZafQr"a9"Zer&D2d)JdhkVdHF"kF[h%T11ehkaZ9"Zd2&QmIG
+#Fb5rUr*p8$E[2YK1d(jaB%03qQE10q$!q*!!((SPMjFq5C["Sb-ZA[Tq-jH`'33
+[#hkAqRMTqqhl)i@,'Lm,m6lKiGaK934+lEF"pCLm5GIlcqJ!(+0Fqb82jaC%#+$
+DlhG45aIRbRQMLKm6Chpmcm1j-lm+"hj@R(NM$aIRV[NGf14Bq(6q%PrkmZU(B6Y
+CiRhA`kF(ELH'DEm%IV6NiP`VHq48LII2HCbc(eS+ffR#NhKr3qG4GNJ0'ell*G'
+l,XiGr'hfPX6Mp4eGEr[qRm2'(U%Im8j`lJPZ(-JG,1G22Ca,I4%1FRPN1RQ#aVQ
+bi&r"*RHU$L(HDC`VUb+RB8fBhiFpR,[r*l$*2CMpPq9aVQcZ"f&E,(iAH6LArQ[
+BcK$m)(m3hV#F[9HRr9UfjR(1rKalRRJ#53'V"1I+cQ'Gj"VHqrM6*m'jeHq%6Hk
+3!"h#AK5F+rppf)J*M(G,(ZIXcF6@Xl6I66Il11GrffNKqmI&1Gr[B1"(RKIkIQf
+dZ6MRqbQkG,6JA#(Hp$c1fCrP&q-EY0mYU)IJA0PbrU*+PJkq!V`@R,0rb0c2eRj
+I"eki1"Fp(`l%&XLPYq9ac[i1b8L6j%dH)MMhCI)cfFcBr`S2jcE6Khh0IRV)`kF
+31GYblAFCHYV&ZAUHdPQKr6D60`ZIHpIlB31I96K%A*FmPP`*fpZdhjBEmMKRhdc
+q4ej*2flM#+krp4lBhLlp4hiQrAd4qqBG8PIJNH#FrI&9F$KAH!B[K`[19A$Z4+a
+JIV-m2[FCBKYMXYkE2CblILCXFQajjSFm2[F2c%h1)@mK(p)iChqIh%2Zk@cjE3r
+RbXL2jDE2jCGiI#j`,B&2qef"qVPmlK[-3BiaELA[e6KRIrq(X-NPp+hNDS,h(q)
+'NpcEh2UBKh0r5kiLCh%Hq3cb@pKBRmeCpL1F6mLFC3Ea[M'ClH*R-c"Ab2[jZ$@
+GJ$B9m`cb%(r1F3qfRB)9+QrV8k`Cj`#3!(Z"dk%jfZmka,$NZ[b9l%10IpB0ej%
+iDErl`'IF2+iQRXPPUKq$*lKjh)*CJLAcS`H*!C,(9[*TiLcpd&YZ(US[j@VC3mM
+9c@-pq40M3Kkqdm[MlmR9C*kej31UhVL*jp9DaIXA+0p,4V[Z&[lX#E5&QXHmCp[
+2,i1$A0rkk4h%@TeIJM-)qDEm)j`0A+2pPY`)Qpc(HK3i(CLUrGj2IXpHJ$`'2UR
+cXka,L*FAD,r(f5H5hb6dZX@j#[h)Db@rGH!@eJEYp`6`hFe[lar!3Bk*23RFFI0
+Ep$8iX,mJfiK9NYqGR(q3!0r4MhdJqGe)6Vj4Z3@SZrPP1%rL(-2kI`!!$3j8Bfa
+-D@*bBA*TCA-ZZA3(0r9b!*!)"cIhG)rF!*!3+,`!!([+!*!%rj!%68e3FN0A588
+"!+qqEj+bElZd!*!&!NT8!*!'128!!#Hf!*!'dPd'q)ZeF"X)Me#qb,Djk-)A[Li
+mb@CGAT5lr9`BBG[)`Qj6lYP(,bAdZXeZHE)F&mr@NhQb@bI,kP%HlBSXXReNRf3
+jRr#&lL2lA)jIPr(,4C2`KC0pV#2l[&`'I0&EMLaNGi6AXHAj`PU14rLLNH9iZQK
+TC%pFq'*2XF)VeHUDq6Ze@!5!E,+3!0pNNadC,@5`N!$EV3YClA23p6EXl,aSPf1
+EfGY)ZclDfmQG0HJm-hZhjb#rJI[LqCf$aFY,FcPGH'CqRJdb2kqccXp*[,0hmHb
+XmcX[MbbHbdQ@LAPN)C[F3QB,"m!##,M9Ep$PGq,Ca)VYmp`@Lr",XMqbG6-#`"E
+m!HI8UpaXeS)JQ"F%i4P"iB*D82LTp`6"Mhi5Qbq'KC!!Tr[90L[aKUMGVMEEa9+
+lZ68ZTaeE5mSAcfXKmVI)l`A"Lpm5"#I0kZp5$DP[Hq!Fh@h[Ck1+CBlLhr@hliZ
+5Jp)H!KPAq5L*qTZ6caAkE*Fkpb([jH&dH'cKXRejF58C6S6"c-bXi6-cS6j8rGf
+6lpeA"F&2cqKA*Nj`l0($c8C8EMkAUe4`4R"FH&GKCqIGR%`Rq(LiYl"JhMR"1D6
+Ki5"iH$MirlPj*$KZI@PeIkXGEkP129mA6TE0l+F+Rj4&JMGPLI9I+YPqI*k'A#Z
+TANPH@QK,e35-0R#L#kX3KkfMTlaDEe)M9SF1NUrkU&@RAf-k%kj9C""jVZ@X)I2
+K*['X`P'&&I01k6DKK0@m*0*)rF#R9rkGc,KmH--PkmUe9A&8LGZPdX#ccE[`*9e
+j[0S4cVSN5D2XXEQA`r3APRr@aDr+2ql,[q5(j@-0qGaT,YF,prYc++TGAcJmq81
+5,N$6[Xb3!'5Q*('cS$[ij*,M"1h`MX,TKDrN,AG6UrbKSHVQGY6H[Y5f'4A$eD3
+Fc"QZV$C'"iS$cAUV@S[Eqd`[Ah)"KZI'Jh(M5EZ&eFDfTpfCT8PJ0kG56C[lAFe
+Y0[ElD)b5FP3lN!!'X99lQ3h9aRLbchCqG'RdE0!Qhh1QdRDdhfIYC*)fkm@,iqe
+[D,BVqfeVbZ8i5BUP+*hBEjLCV0BUcd46YH6LCiX%ZlTU%L9*A0rm4%SahP&"Q,8
+NXrDFJE211P!FVQPVEG5Z(#DJI&8dUY5ENi9GHl)`hT%Xc"TAV(c+TU8D'`H@*hA
++DN0FffHi*0ja)+r,S[CiR"E(iM4p-V&cm9Ba(,9DQ@A9NQd(ZPaJ@1KbJ@R3jBT
+fN[DLQ8PaB#GG,M$4A5kARN+Aff'EVH6!0YIP@[)YI5N`,ADj`$EAj3,$A*F,$)-
+Z9l6,0BkLmBi+#VTF,Q2j,YGXfPSEdZ9b9H&hZD*GHl,m,PFdSmXefE48Bp$P!X0
+FP`X-LedZecebABlqThBK5bF`EalQXTBlTVQXEA'FbaRk$6jR&h4c,riGdeV41$H
+5C)hp@6"VNad'XaC08ee6j[e",'[E-YGPMCX'ZkaPdf5AY5b1GMR$TR%TCpe56XA
+TcNYFmhKAX'dV*Rr!m`UN-1(P$-1F&@DmR*dCm[*'VG9C(21bPNecAYDbCG$c@N1
+Z"jVLeeB,UmRq'E0qY$M55U[ekSl(4dRG64C2TLefr%ef+iEhf64%jC'aiR"-6PV
+5f"4M[QAj3HB0*FUX8A1B3HI24GNd"dU3!%ef+XE!TLA%"Ir4,(2'KIR3Yf`H@(c
+MiPcZQaB'5pmb2eN@$![6@X%k1ki9l,cC,%KiFETXX[A(bm$@c*Hq6D&!IFYmK4B
+0@a2M&Dp[Y,pkQaF*cFCYkFc2aJA$X0abdh("cKf2LdBYU5i-b,jPB8,f,CXf0lj
+YIR96-'a*9c$4+U-TrB[lNiRD8pd`X-de`m#`f!X$fh`V$#acR6!`$"TKd5jAQ%9
+M5A64V$A2q5EBE*TY'MP6Y`8'*VN1'"J'$E$*VL8PT[d&0XhG,l!Z0VmGYUeT$&T
+IdDkp`2c'9c5MlcACl-K`VZX&KVQQ&aJ@HPjJ'V5mSYf12"8DAY[XecEhYFpm1qH
+pYPQ[GFi,Clb@qDjeYYXeeqfDkBVcA0XXecV(YFe`6I2ElYQYI@jVRpPDjl9`9QZ
+CdjTQY0EjV'df#rFfCVe3&fqH("q2fdAA(A+fFpH9RPlediVQ6*9MLQeM3dfQZ2B
+SKK6BHL%9Gr&0)H8@,5eTm[TA,Nqj)A&(S[`aXMP6[R'3!+V#k0QD+j2-FF@KU$%
+q'G%e@P%2il-q8%%M@0Z1&%61HX)"$-M%i[)++eIP6A8AqTb+29G#,9%(pXe4"pC
+qe)&CDp5j'YZ9l+b$YQaRc3[TcYVYc,GIKfd*pafdC0`hckAFYfZ0h!,j2BRm#[+
+Vb+H3!+13!+14P8L&%ibm!MN514Bj$MNH14`*-m,*b'R)3FLr3Uj"RSXFLPb,2!m
+j'$N%13%j%6N*@B@FM*b#""(C(cN9@Bdm#9Q"I!`j!$N3q3hN0j&rKral*%`0kj%
+EN!#J@5p!ASMFK,`BZ4Pj2[,cb0FK[i#Ne3,@[34j+I)b*($VEFMA)m(Tr`(b#Z3
+EN9FL!EqZ3ek09"`(N8mKhibm"[N%mUq4Ad*q'3QLZacC&GN0f3RC'GNGf32C%eQ
+'r#35!'drC!,j+H3Cb$14DH4VN!#[4B,e6#&l)AXMqb!K@6JHf4GTX1JR)%p%'T!
+!p+H4-$!!P[iDmQq3!(q,r$Tb2R)"FL'b!AN+XKkj#,NBfBJ%V[eC*&MUdj!!GFM
+2)Fp#RSfFJeb02!GCJm`J6d8Z35j&,N-Z4kj!cN914kj%VN,13-j%2SkFKB3eiA6
+N@j&rK(`EmZh)kj!!Ed(q-I)Gb"Z3!0FLra$j6Z50b$p"+SD1b(Z40b(r$(Nc%Jc
+Y,FMh)'p&[KGj'r*kj1h)1j!!ld2HLE`2qAlNrFJ2),FMld8qJ(`3q4$b,Z3pb!m
+L&@BfFM2b3mJ2)ap&IJ6jjmLr3(i8q6(Naj&rLI`%mZl$h9G9r!&ZLIhI,,kD8SQ
+DGRCZI"+ZrHYiXXTQ$JYDM48JbYcR!IY0K0D"!1dXRrf"Rd#A-41-RHBV#HBGcf*
+S0$[9bPT9Xe664ar1p@+,3YAk84Yf!6kiNI)d(hbDhN1)E4B`BATX"9P'!NHZSmJ
+BXJ5C3+D43j!!TFM"b*l)l[YTB,a3)L(@E"8)9)q$lj6E%XZ1Q3IeB2dLYiSE!9'
+fNSU!"0MT-rJ1Fd'm6*5MG`AP!K,8eV68hp+rSkTY4qilIA@TZ'Ejkc@9ac-f%&1
+p,N@,i9A*4R9`bMUF@,pL24)+3eQK!dile)-#6[bk1@@RA@++Ac3dZ,'mV9S49Q(
+P'2`[B8VGr-I*C0$6GReXFI`+f2QCM[(6`YK4+Rk*Cq[QF*8A8X0kUC0f2lTCjJ,
+8kk69ci4MQrP-F5(,Kq1dhAa$h0k@&$-)cp,64SYRKSYhh4!ZhV*Xr8KaG09%F(T
+a6+r-SP-[$-+e,(m'h0ehkTTJ[,KH#lAphfM0*&hUD8Y,aPb,Q-(U+IV5HMV1UM1
+fGmjb&N[)k0Re9S,2['A0ec3R03k"HSlZeHU29DVq-Srb'2C-6Z1Iah,Ee6Z$lBp
+SUSU%CehA&pkq9J[BcfYULK3qI'f`r3YIlJ6*bR1+Xha5UlYXPZQXN3A1mJBY09N
+QkZmUHSjcRl1m8#XqeS@Z6dDeRXjbQaCS,"(e0eZVZ9*RZ9AV0eD,VLA,Ymmjbkp
+S8FV#8Ap[BVhTR1JXTfTeb"T5Ifr@i[,McR+ePRXX*pfDB4@Xjm0)i@50ND`UpAF
+05pDSjI0*,4&CB1V[1Ufcch'@Rp4DPc@TDkN@P-FGl5bRDd&)KHM[0ZAMmRUD'-Q
+Jppe1bhFHS!Fd@GCS(6QfRYDdiXjEXX`eI50RUGIT8MIId'UAeE,lXEJIj#blD!R
++`YRpU!E*fSeDql1#GMpmh'"9b'+CHSY5fB`UlTKRHIPk,KSBZ@M&SZ+LLjD[@mk
+cFIA!1QdT,#UI0BB*,'hE2)9`flA0krMX!pZqClIi&S*[0i1CTD*,-+!T&&-DY!Y
+J(DeAZ)prE4hp`&Il`0Fii"*!mLJ)L3eKFH95ZfP2TP31li*8+qj!NJ4)%9H#`&N
+P**IYi!XfbQBck4Sb40SQf$PPSfF0B(aYaJdaP)-c!hF@B)pB5l#!!+I%+JEm'Z-
+f1$I`DH$1k+kX)9KRJ'd#e`5Q#D`5Z3,ra+U#IJ#Q#``8q#c`91#j'2["Fl%bB$8
+"$JVX(0JZPM)X2eMMJ+8#8d@(!'m&9JiF&ZXU&KNX&F$%XFaK23l1La)!'dCGJ!P
+J08'r!U-(GJj-(GJmF(KJkeKUJ-d$5mED"r`HNa2,"mC+Jp0M,3+1N!!2E"ki0E"
+Ei,*BN!#!Vf*""1i3l"Bi-R"Ze$'B16#*,*0B9,(1B&N$2K$F'2JhX'+XUX#J8EN
+X3`c@$!`D',-!G`Ifcf$Z`2#"A@64"PD4Y4Pi4A#4B#)0pT'G!)XCF*-'6fP`PQ!
+X@ES%q%V`H@!$'3V!")+LB99'Kf2*#kk3!*(&B1h!"`EB3jCP!Hi3$1%Zr#Ai6B1
+p"1m'MJ,-RX(UdEI"j)(RI+@EfS'JA2D3!'r"B3C8jT,SBIXi([EB,mcSDm12QHJ
+L$5T+[Ri1)&%!V0J%jMpE&1`L`e"ELkb$*rQChJZb*DGfKMaB0L6I+cjImI[EH&r
+eqe)FrU$#acE#S,qBkJd)K-f"391cqG#!528aNl"4i'1$SpH0kQ0lC$#k)0EC0[$
+"Y'$fDB"rf$S32Qqp,&8kYKTX)Y!a$3RZdY''K@d%1MBqJU9c&,IHTbSG@aXf&1K
+J4'"6J3jf"cB@k(l#Jmd&U5ChE$R3XA&KXi11lB8q28(Tf#*4&qMB','Y3FIQLBd
+01MCEE'l3XC9MJi-1"JIL3JFN5U"68G8G@ae+M(MBmU"M5m1Q"adE26Bqk0KQXIP
+"ar6)"JJG'b8f3HMBZJP+*+EFbm3D%m-&'b*drm@$64(P6)fa-8*($'b1d,'CBB1
+%MXdG@b4da-e@#4hE-$C,k0LBX@&#"d#,(+,MDJHf3qKqD2f%eN%IB9Z%MVbc08,
+(eSMY%6Vk$ATdE1$B*+&M8mG'#4fE41S8(4Y3YN[SU$Qf61LS1EC0Y#RkQQb,%kT
+E2@NV(6PLXiQ1Z0PXSQ26+-cr#@dkfA#LBl2(4K)Gfe@fP1KJ"@&$LHjT(Q`"d9(
+Ve$,YN6l#&K%GF3X)1+QiNHMBAV+04-H'Nqd#1VDZ-S8Q"@S6)&a5,"`#I8Xqa%2
+!EVVp#V`YT4iNJ,D8kNdJE#R&,k#eP1T-B'STp6B"TU@dr48S@NUX&!)q5iR94@"
+J+H9EJ&qTEr)3U&GDrCGY-cTk%aYKG032ffCdf0$Id,%*&Ta,@YY"CJede"Rj33I
+,Kf`0drIcN!!YEPUpMHdf)`Cc!KYSG23eDKNGY8TpS5-fYX2Sb$FEBA6NQqdi1VE
+VE*V4`@3M0`k8UXjNpk*('1B4G-`!E)(4dEq%L+P-Y5eERc,PLbdd1ME@E+24XB9
+R)ik1MDN!bm[%T-0@(pfrmf"VVlj1QV9N*p4*X`6EIh6d1rSN1QUEh+)Mem5,MQf
+cX'eddVDIV6XkQ%ES1HJ!!i)132HI22#T[XkDrCJld0&Ak)hSk-ZJ$0$4GpKdSk0
+Qf(LMime'("hEGZB)G$#EX29(aia)lD2l%3rQ1r9edD`'@J!GFi-'PLSGI4md!6V
+k)fJ(G05r4ZmV(EPQiik11Q0,M`k%!h-%1KKhQ1238Grd!I@9D`E'*cVQ"[Sr1ZU
+ERSb1Z%&1S+1[J*C!4he5AqL)RlNB(A'$QN!(q`qc'cTepBK')D!$A-YXTEkZkQH
+J%Y!ae`Rl5eI91LJ&G24ZN!!+k1KCS"238EFJ3Y#44j!!)HM!*B%333H6%6d@hAG
+iN!!IG!VkUlIhkZZQq4c8#6Vk((-Z1ZBpqL3kjKpQ-A6-!#lf+A+#mJ'V$6Tk!(-
+%1R)0b`dkpJ6-0HLB,HK&k1KjX0qJqamHp!VeG9G0i"-GXc'X11MS3mbrk*L4Q%R
+4dGm"Qk+Ma`NqUEYU$*BTG0M$%)81GLJBLp$p0`rBLY6A3c-0-`JkCQRQ2R6dH1S
+)(I-'GBf1hJ1V%cVb$G3C(A(5Fp"4pmbYk+KlHVlkHQS(!BX@1[S0-`JkqMSeJSj
+C"9Jf1RSPc%cSU&9Q3h6%3$p(ada-EYAA5d-Lq`&dl!MS[HLBAGQCS'01BjDh&Aa
+fN!#c18TBB2RFTbdZP2R#3lD@Jbr!b(Pm(AlpU@@$TJhe#6bYUjp2@fTV+3#&TT1
+PUZhYXpH`UZdk)+KeDl(i-"Ja0hXI4Z,rKRmXQ'iDfh%jip4H&eH+MSKXLmEr#YB
+6NBahH*h6066EX8M@YjbGNA#[6SX[afj6-%GK9YNU6`4TZBErQPUY@Hi[QlCf)QT
+dP(-DPhCdG@Y(1lUj!r9+VP5KCTAeM64Z6lE5VX'kA$PRUUYFe%bV@lChe)VDHPf
+HKL9adTaXPr2!PME,fcV+CG9kEVQm8Ch+P9ZVYBjbB605QME(ir&8beR9*0XE&Q8
+Q5QB$cP3E9*LVUpd@j394LdE#9LQ[@G94e3pe9!drDbTGR*)AP(1Q@QfRVBY5XmY
+@e*a,iiE9`TbTZ$`jkm!V,HTU865Ek(QaH6XP5LC-SflCf$LEY[T'*CiDfEae9U[
++8&*H2c,lpSUMIU36*QXMSpQ5GL+3!0V4KE611J,Cf'*4"G@EGGSBY42,9q1fF90
+XMT1iIHQXfHECdQTX*Y@TXG3U4#N81ABf%!TF,,fNVBKNXTlZGTkk@f@P@3iZUY)
+9UB,DCN[CS'L-DQR,Y6pdPp4)cPB96VBlLUiZ6VDPcGE`Z),,'VYh##L,,@Ql8Bk
+5Z&aAiRfkmTCqV2hV3d1+[9FUDP'cS8))b-FMY)Q0lG4G*U)Y+XTL%Kp5T("d$p5
+mrXVH$9YabDa[TNfkZCLX`i(d5,aAdZY-rh0GH[e3p*VP2dXc+3(TFUTC1lDk10!
+[8e+cP*kaUU296H@mU"a,f8!eD4HmPN`fZ)9JEHDPBB0T89E2"8TUkqYCXT9*j(A
+UXaXEP-j&QNdUh1N&@#UPA$D!9Kd6%(&R"N3f$SbXA$NAJcT3)&$VF!hKMKmZK)d
+0T3hle#CJ88V!D2fJ8"08GM[F-$E8V3qdq4Val1Z'KM+Y9)-a@CT@"MUZLl@c"[E
+hMB`3@p,*c9PrfPCm21$6-S0#c'SJ8JfLi-NcbRX,k1G0EYhV&Y`-"b@%,bm3E8,
+VmNe-+S1UGk0TbV!%+FPC2*D&)fh%YDdiircU*8XUChDMFJf$'T'8N8k6,r(ZTM5
+SeVR9GU)6*-k[-fa&PMViIQ[+#a&9M'@Geq[aIS[2C@aZ0*HcVG[fG9Qr6Mif9j!
+!ZP@e&*,R3MHH(H@AV9aQ@CP6eBKbAPBHUQ%8d3#ZJAm@4I'+Lrca&S@E,I+R@Z3
+ZY#JHXe!iU+*`4i9hh8,ZF)VJ6)QQkb6m,*TE*))6*(+A4a3233RZ1fQk6m@rm53
+EMI3X999X(Ll+aY@@Q`qDccc`,cZ`%4jcX(9d[((-kUb,''3rZd%3qBE2(d5Ri39
+L"`a#2rAIr,00h`%'h9`m+KeJB$dqCK5'E8$c-5X23c+#CQX-5@"l#EKePq0&Xe*
+`SS%"hXIHXj6HY-IqEI5Yd0j549!FI2P0AGZhE-A'G4RFB&mFh[S[plANp*8ih!m
+iVml(jU"C2ZQKCF'mbYjJiH#Q`TkC6iISR4-KP[$)FlGG1F[c84(XZ+`[Q0IA@E"
+Jqc,FXMcEDM9Z',#09T0rfa'@bma2S1(02!dEJDPi805'+3H@%c1l`ZKKQ(,-(4j
+mS0)0lJViS@(+J@("6&N-`F+8SpN%K#P(SrH&+8HMh`e##,5j-19SG,GKRIm"8TK
+b)Q,"-8`jj-X`j@"[Q(*JGa#Q(%G--F+8i`L*,N`jMTJmK#R(%91*-19SYKjKbR(
+%h#&-1BlQ-'(+FF45)8`jMYJLK#R(%@Z$-18ibVGd8%FX"X+8iiJe3*Kb(#(cK5R
+(%G*GQ()FSGQ&+FF43iN`j6L2m"#Q(1Fa(M)p4P@6`T369Fd)8dj8H45QR+Mm#P0
+1928Y6$P4)E5%+5FUYKGKbSQ+D8@BFU*#dJY66P3X*-+8%pA'3jKbSQ,L%+DFU0J
+aK#P(-``*8dj8M!A#P"-9Di-`j86&RL"-19'a'mJJ%&90#&019+KqBFV4M%6#P"2
+p1JpKbY'Y6$$M-I8JBFU*U8F)8dj-G5K-16(P9'DPQ')6TTbB@!5%+5FQ4K"KbSQ
+*m8@BFQ,U"m+8%a-VJc$Pa,6m%DDFQ*JeK#NR*YB*BFU*UFm*8dj-Y5j-1E&ri#&
+6D%`X8m+8%eGp#e01A,N8TTbiHT-`jF69#i3T*kkk%UDFZ()N6$Pa-H-)8djF[8f
+BFZ,+T6$Pa-@d)N`jFE'C#&01A1`K`T36&aZ%-1A%aC3K6$PaeDi`jF6&"#&-1A%
+aFmMN'[p((X)Q%"FlK6$PD(BPBFSTd9`K6$NPQJH%+DG%r95BFNV8Tf5Z,P(Y#P0
+1LHT0Q(*+P%GKbLQ4Af(++4',M6$PP)KP4*Kb5M5[#&01L4K2C2G5)TB3BFST%G1
+'M+iPBUb3!#ehbAdmK-@K4(eDQ(*+a+)N6$N*p@CKbNQS[`X%-U&q*a$ZK1TCQ()
+5UKpKbNNS6Q(+5DK'K5NRSGfL-18Na+iL6$N*c9A#P*03Ma'QR-6RH!K66N+-4m+
+8Ne4I%DDFT'B5BFV4E%r#P*08Ea@QR+6kPc$P*09hK#NRU9S9TTbNDN#BFT,+K6$
+P*$8R#9018Q`I`T56&11*-18Na6!L6$P*eCB`j56&V#0-1FPrib&-15R0Im+8Np,
+F)d`j+G@C-1@N0-m)8dj+mi!`jD686i8T*kAH*%`j+I8#BFT*UDk%+5HPh!P66NU
+qK#NR*BBDBFT*LIP&Q(*5ADDFP(U'-1@N2XY$Q(*5$r-3TTc8[r)3TTbdHVm`jD6
+9#i8T*khH,d`jDI9qBFT*Dqi5TTbdFLa-1@RP@*KbdZVG`T569Ym8TTbdqTd`jD6
+9Ai3T*khD%kDFY2)P6$PTq4@Q(+[&"dYUlR0Q"h1VcfH*Z+E',RGkJq`PSk!%"AU
+r,UU"Y*[EMK`%mUFI2Fd-mErkG1KZr-[LGKeJrGE[Kqpp!IQ44d&rFdjkj%2jV+Y
+I3%0JDIRXFD4(0Y46b3TV@li%,!@+Vm,Z8T`fK0YEc"PAhHGBXi3PSLNA&SEXH*r
+U$kc'PKFkR'(jI'LXRHZ(9V,LS"qRk`I8ij,F6ki$"Aj!4+lbmB#+210J2%&q3+Z
+GkH-'qEMjN!!I[bj!3QleIN$([2'J(d%-D6mJ%&IkH&Jj[b2hdhBVQ2)$H[*LPBd
+kjZ#C3r&BF*J5h#[*)USh,[AJBSm5*%HcUF2K)XHS'5YCLLa$GN*f3CBMZb+l)EX
+MHb"l)RXKHb2l))p$(SrXLc`"H5+b(j*VqNp#9L!()!FL6dB13Jj'$N%143j$$NH
+13)j%MN+14PBLab$()XFKab-R)#FL*b'VN!#6N913!&14eFKTb1R)'FLCb&R)fFJ
+DC!CCLka$cN-Z3#j%0Y"kD$pcN!$eb2R)"FJS8Y%*kr1m5T!!A&H3!%5QN!"TC#Q
+b$0N*f4PCMZb+l)EXMZb"l)RXKHb0l)-m$RNmXLrb"15*b(l)rXL6N!!9b!()JFL
+6NB13!)143j!!3j($N!$$N513!#14Sj!!Sj'9b$()XFKab2()#FL*b%R)+Z4Nj"6
+N9'3eFKTb1R)'FLCb&R)fXJETRIhKUkac'"Sl,+GfPQM1c[,Ciq!PlXk8[56[e#h
+0hrhBc-ae3UER2N-6BhRl$Kr&fIETrC0&i-Sd6$9m`,G3kXJPYfap&f`59pKY'TD
+fMe9`X5pF3&qSRCQjbaJi,'hI)X2r"(eK`beETf#Ek[m$EN$,f2GkKJA$HQIk`K[
+F[2#%-I"BfEjdF03*6p'TrqpR(lLYj[NE0kd#3"pc"Bm0K(A&h)ZqLeXD*+Hj0a%
+q3(22H[(1pH+GL5fhUVIFb`JPPER[rCIh-TUlj)0l'AIGYQMZ@S46XAM6)TXeFlm
+k)&Zi%)8$dEE`c9dpY%+!3RN2Y[F3H`qepc"l$lIh#(Z2Y2FSHiqfGk@papKlV,h
+(fAZm[5IBHk+p*pQlbYk6l6h&hP2YA@h[DICH1$LkI[ENhrK,$YdPSRU+q*YZlaR
+fRQR[@IDHEHmDHfIXA@[["IDZkmDG1[(J84@UeH&1p6RcMRAGTR888[ES#Y8Dm3%
+)f2G41KplIBb&a+$X5qbGk2ST@l$[@![*Yh+LkeS&VZY9kBeGGh[h02[,l+hVAle
+ehDVhTIBHdaPRh+YDFeKXZEhDK[DeF[cCAmJLAKh+pAH8"X3+3)9QXk%"X@IFlQ@
+A`jG,Gp"G`3UeDE-)I*CB@NVrY#!lbfH2i`e`cdf$ALPh-YV1ceaiSElGm(%V38a
+UmE!0H6Pb("+-hhVN"Z4'j#ENaFM0b#h)VFK,N!"e5&!(Fj(cN!#R)1Z4mj%,N!!
+,N3h)4FM&b%ENUFJPb+A)CFMPb"A)PFK9b013!+FMcd#HLA`0mVA)Xj!!Cb0A)mp
+"VN'HLeb,2!pj2[)#j$VNkj!!&b+jjRNmFJ*b)R)5XJSj'6N&149CMCb'R)kFLCb
+&R)fX3@D3!&&N$$N'14Cj+A))FL5r6HjqEECVllEGr#ef#FTl$#GlUcAHpaUHqME
+V'JrEqa&lImMH(lEhlABKbKf'chqIfGeTllXXhZfQ[p[L[FIL[pIL[FrLIlqpllF
+,9MjJIKm`[`rDe5S2@EbZYBhLeL#b'mebaTNFd(-k5jH1Zl2hV'e$0fe"FfHb4pI
+pB(h8ZkdA&2T"R&R"@HS@mNXR@cVQVYTZCV5T03M6$a6BK$Xj9$r`lSKDQND05Y5
+Zc%*mUl2cqI4,GBhUdZ6Q'ZG8eMI+YFP+h$1XCjKqS2B(*IrC$p4PQ$DcHF(["mR
+[c3H+['"pXcGC@r$l3HUlrD$j2%M,@&I`qd(Dl4HTpY9TllB9Spm25PdrU1j`fP5
+mU[rmr[0kVA8'[aq8'6i&l!hi&*NAMUNAm#QfZAf#I3k2)22L)IbDMD`Cj2Z"BVk
+cc1%48YRq1HEiil,m#0Df[K"mXQp-pTc$)mMmD*($)b4keI&Pj2T"PX(HRYB4r(l
+!C6Yf1Ei-Q4GrL8qaN9(Ur!D[C%IM9dVe`1I,f2H"BeVG[k0UemIEDll69jH+DjD
+rIVPNHAIkRKpG'KA(6KXYRKNZhR9$Z(M,X[8MaG&9%m(Ta6'p-SY1[6!)eh+Iqi#
+lqdjG%i`AerrJZLYed8aMP*5MQZjNh1PMfBU0krTECEY3[cf1fR-'cMVVU6"DF[T
++(1l(cEdk(jZ$C[QNKjB&mbTlJi@$Q`TlCMiGSRG1C-[TA9Qbl8SlMce@$(CFeKI
+-kqXF"BRYbmFpP'HEMmB0!k16ME4DImUrY3LYjal%SdiFhAqRV,+#*c[TrG!P8Yi
+2I0TTl`I1S&,["rlZ-Zq(fDQ6p`0LY,2h!cpi&qm(VZpbliICVk[h!`pk0qq(QdK
+kH$rF6G,6qq&fJPlH$rHAp2CqZ0'NMrI$(5I(H6rFHR+mpm-p+(fp(fj'1F(liDk
+8%ldIENrTjre`RdTrl`H@rC1m(qjFUI"qZ)9PJ2I$[5`$[4pZDMRCqq(ZPN(H$j2
+RB1q(qef'H$rFq$,8qq%1Q'(H$lI#$2GqZ#GQK2I$c6%M[4rZNKRPrA#lc'M[KrY
+Q+VdIEU!Cire`*meBliGEDXCj2paE-plli5DE#Gi2GpY-p(kil@D5pm2p0eAH$cI
+L62CqZ#0RL[I$V6P6[4rZdDRfIVKCCjVh`edldldIEYqCiIe`(mp-liI&d#c[KcY
+lCRXrh1*6Fq4HE'A&[6iC(`mhrG4k2pcp8qIpF"[3(1q(qi(QHMrF'$62qf&[H)V
+h`ke#pGl22*l[1Z6(9h"3q%hHMcT42[*4lkH"CrFMIVb%C6(JHlpM'!3rpVKEDQ`
+iAbmEjHeap`FGDd&-6KD+TXNPG1Uj#pQG46)T2dT%KMYq4,($$mKZ$`%HqJ(Cl5(
+!3cpjC(IS"f5hKa32rB$XpK$JS4q3!0dH!McdNdGfKhj!GRY)mG!2b'i2!4lk!GR
+Y)F"$2ebmk5(!3cmJZcd%H1J(C,H(!!rpJ1cf%1#K(j!!h4i#22564hE([Z8RMq`
+1rH54hD'I2,)lp*0(GSGqmXMZd%mHf4hkb51l3cpjC(IS*irX$[hNNGfKRcbb1r5
+64hD(I[,)lY"2(YNGqXNMZd-rH@4hk#H2l!lpj*(GSCmmXM[dNdGfKhlbb1l36al
+C(IS"fHdKYN-r),Xp"(MS*irX$[hNNGfKRcbb1r564hD(IN"fHiMYd!r)EJm"([V
+*)lY$2hPNGqJRMq`1rB$XpT!!f+%IN!$G(J)mp!1bfd1!Khlbb1l36alC(IV*)lY
+$2hPNGqJRMqb1ImX2b'i2`4ck!GRY)F"$2b#l234ik!GNYiF!$rf!l2B3i+'I2,)
+lp!1bfd1+Khj!GRX)m0!2PqVZ3RCR2q@RRZFZC(IJCcl2AFMZ`!p)m9h)lUE['"!
+"f@d,L-+lDb24HqkZ$GB*e[Zl0UaeGk!Hdf$FY@&!Q5jM5JkMUC3f'ZBKqda&@(R
+kKi@L+1`e2Ff'q`RIfT!!`G(e*6-TRFqUHAdcED+,VP`pY!+@XK*l*lTqbKC`!dd
+@bi4MF6*BEl@E''PkBpIGhMh0rM*l$l(h@(YIDQpBCQE2Imr@,`CAA68c+j2[[QT
+k6Tq6jffj1d[0GqqrreNC$Bl2lkT4ZG[`p&deICHA5VTmi-Kh0V+QeDVe"SLMBAK
+)[#0l2e@'&D[V3TH&jr%dP)C@pJL)Bq@!H%XX!(PG-MCk--,$FHJVHmkC#-T*XMJ
+)(hYVX'"0fR'3!,Sadf@L@UhCE(!dqd@G#iqF+kr1G-lBRQQpG4aVRSqhiqSI,ik
+4iR#b!X5a,b'rL'2IB8X[a,%hN!"$FDbGDJ@lEp[Tk[@$$dc-2,SXP,lRIA!ZE,X
+T')eh"$r"E*)DV'3eqY3RqV"r10apqkE`LKH*YmQ0BqqY4mrQ!i)q(FHqmRMKqU6
+9QS5X!((mpSSPpmYHRk6Lf01*AVKLUD-qU0LpRijM5c$aA*p@DDK*kN(@U)eTX8q
+['KKjBBl"apc[6$,R0XZ64q,Sq*Jr0LIYld`02ipMbd4Yma%rM5Z1C&A&XA@b4d!
+FI*88bDYjC9FJ%hI9NSYlPNcF+V0Y2PkFZ--iV!%6pq,AVE%VXm*2RBae)"q@MmQ
+p2DX[6Zi(iRKZFMm3ah16ql0ar(abliLMBh,RNmRGHG[#66d!*[HGqAKjFJm6HhK
+bliMMZFPpCaJ[6HiGF9J%*[HeirpEppD2LEYUbEB$2PkFZ0[Mi,-%60`VUihccLe
+1,ERJi-5Gr[l%T1,BfaQHRjJ1a2(FT20X($qIG$VLH,k6licMT8lH%FIqTUIL8(A
+rl'4H@DXHlPEq4Q*hKDJi0QlHqP`FLcG[I5'1p2!K')CmUH(KJ9AIDhrYhmmkAUf
+Va'IMZ1Mj@9(&F9'8*',Xf!4)ZjMFE%@I'frT%C!!ek-c3hD@hMq6(iUMSh2ZVl0
+XI@`pA'BrqmUbda4hpjJZfICaGH-1YNVY@kL@cpY#26hGf6jD!H+SV59*3)Y4$D3
+p$YAVb@[NZ6fjhKTD!I+KYhe@J$M-YQp2()HfI4HNp9T`FmR&FGeZV+m,ldAIqaF
+AKp0hYB,6`KYERlTjChM0Dp4-e"d"j3hXV`T[V0`66Mq)2iRMGDUL-"m(kdbf,1Y
+DhkjA9Lbk%Hf1JpXU)cT"mfMGkbf,&D"HpCE&#K!(Rp[1&"rmlr+Uie$P%IPKI5a
+U0Z,$)i-c2&ckVh-VQcki3MY'MmJ*[5H3!!qe)qrmpQl+r8b0lIRd$'-&L'2qe[D
+@IZ5VH@8UY9GJ&[Gm,E03mI0QSGrAKcF,rEjH$fkF$kbEeDGRSD2pJ`jmHVAe!J,
+RB&lY%jYV2TQTMZ49al(rkYQ$FA4![#)Rphl$H1hj$NdkGL@-ep(2XS6afY3k2*e
+fj29jV0M"b6f,&6[k@8#`BMXEb%YBXIC1E3[!LVQ6Hd2F(Mqi&%hfRTMF,4@6U4@
+JFpS91YA"Vi-Sa`+bdl'i-1AXq5`%ZaJl%&21dFqba*4cD"I6&SL0B"HcciF`lZc
+pl&Z-1ah`rdKIV`Vmrh-6R(f$4q$jb@YR(#p2AQ&#$NpH(A&B*rJ4$NeHEB(B"AJ
+@h$Jd+fEl4a#+[pdG#RElX30-"$S1U`6[J`@0LI0KKp#ZcYJEpCGUI8h29S6r4ql
+iF[CHmh-CGXh#Y&mFXf2C(+p692UG6*c+pLRNTX)jEbb'ApbMbb3Y8dbF,q`&BH*
+Xlp1Y6*b&2QdTCq+d+8cZGL#8pTl*RFm`-[l6[BYl!%cZGLNQcU0eIdc,`-5jkDY
+Vf5UeEk&D2Qm,GAkaXhfd!X6K-h'fap(+1&MBLZQYS48J(hVEC`@))m[%Z6Z13pX
+qc6khF$TRiSc1,fa%VjJiSbImr'lZKTldQ61'2PNSlRN!PiU*Xq[RXIp-iG9rmE(
+##@r@KF!qqebBMi0ejM0aYXI4bX4CL-0RiMb5$lePX3,8Upkb@!(Li"2@Z#0jeA'
+i6*a(iMK'Y'(LG,mS8ThQ'IN4*!`a#@35Q8DQN!#Pb$*N*b36D'GN&f3jNVEB$GN
+Gf32C%pN,f4[C"kN`hj%I3rC&RS!m%GN2f4pj%K+8e!$N316*b%()`FJKb+()iFK
+Kb"&)ZY-Sj'JNpi&@)X'3!)p$MNGbCpSNj!3N'Cq-R)+FLZ4)jZR)'FLCb&R)'L6
+`4,D9dj!!A%0+2f&E11GrD[ehRkNpkSlk#fU[THiDHKV8(R9(r49VMlUMrS,DSqC
+-rEA8([99V$pUMaUNrS+kSmI-3aV'%HU5rN5ISMr4TqU4mj(J-L`!fqrrqq`dYrG
+f'KdC@6GRP1VH$HXX,,9M(65qVPD0+kbh9eILYZBc4hHr+[G1eePd2Z#(LR9PS4,
+drE$+AKmPUpiaHmIYVBq89Hq%[C2f6YNlE@ppMkakPpQlNldEl0h&hZAflQV[E[E
+@H9#0+0V08qci`EJ4YcR&hFf9[Re@qHTKlhRfeVI2URF[HrHfGapl(fI[iqhGepi
+Rf2Y%HrHcGhplRf6[#RX2X2G!HjpXld(feMIDUVHq[9DpKpTlQ,f(fhZ%[8dq4pP
+lY,dVl6h'h[U'@r8HCqrapTjJliRfRQ6[+RY2Y[F8HdqeGl@pTpPlBEIQiLrTA%%
+q+L2i@Cd@AdlpXCda06lGhM2X2G2HXq`pfpiepXlBZpEH#qaGeidlGH,BK%Ed0Fe
++6-"UZE"aXPh19Jbk0DRja,aMAEITifN0jSaldql86'1pZ,hArj,[ISG@-mpp+JU
+JkT-+EN*5Yf5hM8V+$pZ3!#V[Kqh,C1m(0T)ThJp!ZkRH$k`AeGi2@-YThJrEZZR
+HccUH-l`IYN!c[4mB4@Cj2d``XldId!SehXm'RKR[KlCAkrh3iHZmRiYjc[&qf-[
+2pAlSY21m(jD)ThJrE%RV[Cp,HEkpibHK48MfbcUPUk[R1cTqNPU@Y-Cc1FphjAl
+DifC-ZHQ)Rf`LP$PAbHpeFQb)i-I1Fr'q%al&0+QH"lb"5T!!Vfhqp'[23T&RZmc
+('P-pParD6cLhG[b`N!"BFG#2dr8$UZ5+JhZ-UlYaJc3j`qmlf&*HkIf!8VR+q`(
+*N["l%G!Z5Eqh!4'6mRj!cD5p(j!!0DAH$qLE-Zm(K%iRl`F86fI["k42&qm(0&#
+jp`0LU+[h!kUSQrF$kUL(p`-bUDIh!hUTPrF$`UQhp`-+USrh!dVU11m(*0AahJp
+SUllH$iLX%l`I8&XRHMmJZ2Tj2k#mqRXr)-&1mRj!Le9i2b$V"RJrLhJ1p(i@mcc
+Cq`(Q-FMl1CARB1pR#FmKhXp5RN1p(l"G`lbIj6b(HcmVH)l`IPEb(1RpV1)jb[X
+jMHGSlqGdRTAHcaNmahJrCr)FkrfmKZFilqHe2-Gl2fIaR1$p3-N`dIZaT10lP"m
+)EVVr(b3""f5eq*L`b*fD[FTkdL#5l(9I,eZaf'P3I(D@calA"e`qV(GR1-9)F(A
+'V"8*hTHaE%@#paY3pEdCUe3Nq)"eMGXcaUe)F%h'1"8*VM@l1qepPm@chI4h@cc
+h@(ch@Mch@Ac[YrIp'F0A*2KYmrZ!qAd`BrZ+""qaH*rr9"5PDdI@,KpHdEpLid(
+5-T!!DQjBkL([jH&dH'cKXUkABcb[U``YmeFCfXqB1-FkK2Em[,'PEXadV3MrQkl
+1G-lBRQQp,8claDGZfcR(kq4)RfIL92AV-R%kZcr4Krh$iHlE0i9A[%LAr9LQQ$K
+r`HHPQ6MEqh3V%fHK6e[+Q6KY#T1l(3LP[@Gbjj2*hARE`Ndp!#ChZa36jp'k2kC
+PB1*FhEL$V9,l&UVPmlC36dphYSp@J$Km*Xlf1&SC"`YE-EdeY!,N3frlV!"aC*N
+iGmGaD0ZRfHFH+H9-R-lPiEhS&41R-heA+eJDhV4peFdl`fYHSfDLQ$M,'pKI&Gl
+8Z#HFIK"r!IYFQ)q$GHBcFEE(dFV%@BM$Cq)mNJqpCE%#e+[HXPJ"iZ!6eVJMHG9
+aZ%bF4q)i4V4KiS`Fm`h"XYFRkH265HBDlZkm#V[#4V!#Ic(CF4@hrcRRmKL'(9G
+rqpq+Yd",)r(&pZCfkri8eJRX9(b"h45ZCBr*6X8Af*9Fak1deBDT)AVVlbYJLC[
+Ve3FjZiGeDDLFMHhm8Zj[`#2Sj1aXjjGjh1MXVKBd8Di4MEk8ahaR0qVA&E"VPlL
+CaeKRYrM[C#"fhmrpaEk%cVeb0&,iGbA4YEYbS3V-YBZT!MXlZqfkc0fpKM4bpQG
+*JF5a6IfPe1VbD4ipR9hRMk!VXcMSGhfGh52##,YAR%BqrNk1f,l$f9hr3FAUfLA
+Tk-jmCpIicaKdX6Li[[diCeIr*qM+cBlH,AAcJ(``pQ"hTAblGJqp%PdhCcH)klk
+GSkdZKf,3hHVe34l6RGd8jDq(aI(f2)j2UAp4)qSEr%ELq,DcZdYRR[Db1+EQGA#
+(iZ2@9HbiAPrb9+EYEKqcqcFHRh0fN4pAMPflSI39ar*pVbie2plC$9-IZmrCI8H
+e4"qJrZKRPCQGm`ek@14NCcIZ6'UmNr2ArCm`)%Ee,D*I*XcZKlq*36rA$Z,8mi5
+hQlrq@##*Mm9(46e0e&rp1fjrr%H8iKhe00[%qhEAIJMeLI@dTYcjGL@k'VmVrZL
+l[PhXYr,kq0Dpk%ib1fT4qZ9`m5m)5M(fPlQrDbj(0m$X[T2hbmrV)[@"cQkQDNA
+keKTb(hQCXi[IN[I,Y(V'),1MRiQraekPJKFlkNrkj6A#f!ba[)l+qqAM!Ve5qm5
+YkYMdbqmTjQ(QMlU3!$karMI3U6V9GFBi+IhbIX8k`YPpNILkl(4f0k[hbH3liG9
+j[la)[Cim%0qEmhjjSAc4!iR[PVaZUPAhP@C(r8QIqqiDG'1FhF3,mhljePp4VEK
+f)c5h5,qm6A-*08)Fe*2%mB6Q@2U5qMBc2dQrG,kZbpNR1,[KUNqTJmIr3!&,(*r
+TpS2(d+Nmmc@SrU9IhX*X&l&*F4P-BUCIIRX8JmR1VNTa5,qm3GGe6E(mcHcdbqJ
+08*&&e,I9Yj8k-2hb(lk#3E@cZqbpHEpFShPVQK[2TY%A6Ep-d[-Le![I$-h-9eQ
+rp%k$GrlI"lVpdV1,A(jRYerkGX19Ap-[I6Y(IF6-Lmb)-bb[2mRljIhb3CkTTbr
+Pr[3HFCDcQkaeX26,kkp(4be6*Rkrr23JZKV,KkTMdrpkUjm*FLEeehRrHi0@"l,
+RL'Vl*2hebr)Rb*DSHV$%edeV$$Q9Gbhpcr6,1c3Eb3BjqS@m6b39!cNKhp54p-Y
+rr"Gd`[Vd6IT"ceh@Aj8ZXYVCV@(q0[ebLZUBANFFc"#QAbj(*rLjiBT(kZB+HRR
+%N!",`q92qYbYbJAcRIUZZ$E[Pl5Yb&UEQqPVdLq[&6IaBS[l+hNFCkYAbYVPM4r
+1jmX0QQY20Ar-0e)(kj3,HKhji#ejqUaf"%ZGhD@lmRjCU4`XFhCA[6R[Pb[NQjb
+6aRr1qf9Ap8TbcVM"[#VpmM(90R1FqUjRjf(kj51rJm%UCrF@,K`crA+FpJG)&I)
+'kYMdbbI8NpL(%$il$c0IqLIh[iPql$aJrG+h'ri,QU@NArTfSqLMfAlTfphiHr5
+SFq[Tq12l'kKGSQIA@`RqI$[RHZEDH&!D5P,f*I6@-jcK*&eR*RZM))jEbDXc6Gc
+-20jFkhb3!0k@YI2lpcP[9em8Zb#[l!ZNEZl46(LQc6%hj[9ArrISk!Q%4&h%RGd
+PkYR8"I9+McR(fAf9R8IN%UX2qV%EAq`8l9f%&Q+%qV%EAqbiGk!6iSDijQq*lfZ
+D$f3A@+9pJF4hmeqTX8TmAq[%PjMr'$UjHQf&pJjZI)NpUJq"MFC[l-5Aq"R0N!$
+#fA,e9Chi%Mpj!cT""Ur9r(Hfp3R[-QIReLXSHp-RI,[Yk*aG9QHHAH5eQZp0Rr$
+Y(QC'G-kh[Z,E6G+FimiCQ6Xe%lSlE1H8YhEQM-b[DTk3!(hLrFcKd[mb-qUjlZl
+6fFem)rd[Fj[f@-+EF`eab&b5ZIjK$'52ZC`q*(0*l5Y[3bHBQj'DPpaqRhRSAHK
+N[eQ9lldb[kEG'AP8hhZS'jP,-Tp6h3XUi1T[GZD5fT-e6l!cB5iN[a)(kiQ)l9[
+[T"r)A*+j8ce!GY8c0#Y,[Vq[2LHVk9'DNpbkchcd(R5bDrm2jNkCmfSAD*p"Me*
+IbIXkrD$fYCVjf48`"c$2ZrdJmk"fPTGEAf&qFqI4c'HdCpeQF6rDQ8FcIkCC5f"
+2LpJlb[a8frpqG(*ap5MfHDDH(U)@)p,6epR[Z206CNCaA1(XYM0cbra8qc,0a1a
+[eAHhqUJl2pAfDhDN(TNrf-ZiG9"lK[S%HalehB-2QCmbZc4RU,P-ckRXBGdj2[2
+RUJPfHZUEUGlTjURfaCTVfI23CPArNcQqpK9rV)BLF91hETePVY'-IUh0(qb'hIM
+UEP,2)Qrd&HC20lkk0fRq82-Xhk1DVGhik[j32CJH3MpM2b&er(AeD2EHc$(89Q8
+pH@)1@j29JGMjrA@NjX"5jLVkNZp['[0Rp*JX[XKbl@)rjH+H60h%cR9f%lA,B(G
+%(16HlG14,kPhX2p4hiHBUd`Ffp8$Q"29p`ccLBPMYRCEHQKApF%X+h&FS9apeX8
+G[6+2idRYHqM2bLl1AXI%FIhY@$aZGFNF*A&d9[mM2l4kVN#81,CU"rB&CjIqQcb
+1(kPf[qMLZ&BcQBlMbTR(Y)YKGU!pXT2V9!Gpkp@-VPIfYPq[4dmJT1pJhR,F2UC
+ESYdp[BZkB6Hfc0PpG!mkCR2Q(ZE22h9f3l3rrV,,dphd9kI2fAe#ZAM5fBeRab$
+aG6rU85cBEc"AX8Z3!2Jdf[UVcPpRj8$LqaI0Z*TM,a+H4efDq'lA(-"Fb@M'2#h
+jZd'pK$d`m9(6%Ymbc8,-T-V9*1de*Ei'cCI8S,+V)KmQ[KqSCp%EPFQ(U6Z6[j6
+Q''T"Q6a+hC[iIUbFXU2'MVf8a2F0p6"9[pKpK(R!a$G-ZrY[bHcr!3#3"!d,9'0
+X8fKPE'ac,VN(,iYd"cIeFJ#3#!Fhph4RU`#3%%*P!!#br`#3"2q3"%e08(*$9dP
+&!3#[[Qr$XQjqU3#3"3+2k!#3"ME&!!$,f`#3"N,c"J$&pZPQ#lY0MNBf)j[X6V)
+pr-)@[M$V1PFQcb0XBB4YYT!!BlBbmp[VXI8@DXHp&R)l,Tjk-TAGqXEa(m[I,XC
+DMYdQE$PG'+&NaaG1ET%X6pKf)F[aLhAZf&9Nkj&0EXF)f`hi3KEM#peN18,j3ME
+K&f(,bA)a[RJ5RSX$Zl`Sd1'9DNh+kqMaK3dfff3P#eR)-l,*M*2C3REijI"pjcI
+SE##hYkGC0V"&4eiMYjf[CHr#VR0VNlaFH+a"TjhjRBdk,r-beLfmJGIR`1[bmfh
+U,ClAjGQ)E"Fl,hkG9lbhJEhAahY[1A,,HpPj'3I'!JLimTX`E9E8LZjM84jE@(B
+,)fcKbr%"h3#-!!5-"ajj$3UZ@eC+c92+@+i5pcbT%VYH91VY,m2Q&dE#`+rrfYb
+L[GRb2-Iec*cREVF,3F2@NI*USPV`cU"Dj(IM"j4kKqLpHeK$mXi066fe@b8Qh)[
+a9Ep$aB9M-hri)ET8k$J-Xr[K)i%INIlhN!$A@L[TeLm9lVjKU[9(r`mhKSd&L3m
+f[D6(`mf&lHFZ5%SqTD%9[Zm2dmklI``Z-II8pIUA4k(X-mC262`UbPAIB+CVrX[
+,!bq*NUf2#8!18kd1JGV,S*!!M$""eB6PSbANc8YS@kX[qeYEI3Qei,U-@l8+lU8
+-Cp9bp42'UF6HaRqj"",U9iar0%ER,9FVN!#'kj6ked`X4Ch'Q+h*b@(2b!R$qS&
+FEl,QfGZFAImC$hm*Y8J`S#QcqDlq3VR(YSUfPmZPjRa'VeDTYVa6UC9Y%kjEPm2
+YGY-ZcR(m8QFZPeCUiA'!`%pd'%HGM!QSLK89X`pHS'f#h%&`CL1TQiM6Z"f`CjR
+a+H2Ca(HEf"09qLD6C#9(V(,F,+K&*`"L18"HRR4S43MeN!!+l!)+UXRP82p8FA)
++fhYI$eA[iVe2iV[2f3aTLmSP)-bA3EiGD4Cff$LZpp56Sh[Rrpcf'TK2!LaU0$b
+0K15r'GE0Pf[45*F!)IA8@b%RkaVcEN4N,c3(XKIihT*Di3mePC%LQ5CGT0D-iaI
+8P1Q46V8[CDEF5XdTfpkdmEQV9X,d4,[EVTkhR1e8GlcUdMDY#NQBlaHXmPbSDLe
+pVd91GFLIYYkrk!6ZM-ZCER8QaPR@6QXZI+f"rAF&RM86Ae[G$pb+qAjlpcfZ9jb
+ahUp3X(hIc&P"DFCdHYdT&eq,DIG9ljmY(LaR1,lPqhCPm*95`,a5@aKf*,GY4HV
+GljiV,GqiXl,D0Q339V&@jK)5fP1(C@rU-+qN$X1jA4XZ@(98kpa8Tep"b5fbbp1
+QKpTljM*pQ180fB'CYi2JI+TR`Q0VaUV93YZPT6Q!M#d,q"ME4["B-Y3b+0RCI1I
+M,f"MZh&3`,'a)Q0XBi!aYSKaXC*jKE$BYSL+XA%-LV&PM)QaC35**F-B9NV@lH8
+8i@%qFANiA'lE88`+K[N##E'`C0L6Xa!*5hB!BGQS8Td4$-D@-3V'PN83c%0$M)&
+L-DpNPkIa,l!UB&pJ&q&HE+3*MfeXAR1a&V#ZD"L8CQ#S'"IS$Ei&fKME5KP9'!V
+XLTJ@'-Ci&PM&@"CB46J@'m9i%&X@5b2#VPabmVK9X#XAKZ*9,ZNK9X9'(AN*-5U
+f!Crb"UAULR!TX)Sa+E!UiP'ZM'-XfRh9MMP#PMFYS&,H1)+QXU8QZfaS%ea)3J'
+T+YC"9HDY&E2b4JDimLBaHRA8J+*2hVL)BhRV'-cbTM'Lj8dM@#YEaKK50Ur89S4
+bKIcPSDlGZ,1b&23+Y4)LApQb0h8K"TB0!F,P9Kh9'N&LhM6'aEaT%4`,H"%MT-8
+$BcAEm@Gk*JIeQGPDi&5F2HFD)`CP$UN(063KPeXHNCQfQQ-9XRNcBb-jlH3cMV6
+FD0"!biB5Cfa8$M-J"h')HDkRiH@0*,6!S"a@[UcLf*E$X3Dih&+Lc&Z93rAKU4H
+T5`jLX#jC&a'Lj#!2%LAc'1p,eK(N9iaMk+ai825Xf&US,"G&([ill3-'8,Eh58$
+*,LlYNR98h4h'h3Qcj9mbI"!"LMbbbd&2ZL0k8c&qS%a$NP1aKHGd'(D93maf5YB
+ai5PC&`Kmb6lLm"AMVP3@fNh6Zep[q9M6BY2(fLh[@9M68JI0'KCE4GDZd#b+c)T
+YQFJfejL*V0V6@fSDj5fMYP&J'651V%@4EeQl!Z1+cCBR)q4LeU4#mUcamQCJ`E3
+pIB8q9f6@99,jAPGNTGfZf+5Ff',(bpVe6L2-X!IV3d1fCfSd+9M2l-qp1Yi!L1f
+rUf#M'"GA#8dPpPf-+cE1K9@D2#K&&4$QBN5"B5kDZ+GELL42LS[Kj+ec-CAlF+A
+!5KKIM+e-Ki2`+[bJ&''qqRBadeCeU'i"UcUR5+D%b0-+k$R*XaK)`AlUh+60VQC
+R%8e"m5LS"YC+$[*&fKjml+!FHf`HKKlE95)2#VSpkX#i((&J'NBEf&3LcCGjHlK
+j$q@BmrCKi(R$5[3P&'K23-P*13FP&f%D5VD96$J@APeVk,$r-A(XccSRpQij0qZ
+j2Tj!f6lF+(%k"kV!I6#0QTri$hFb5-KE3'BJ0i*XJ4`(@3rC"$N5FL,NPT!!Nm*
+4cRM39`C!%j!!5FJ8j&M)@XJdC!Db#E)4XJ&b&()`Y-bLkaehPapBiFmCEq#&30i
+F+r`jS@ZA%r4Be5,Tm-02TVh(6NcH,bhG![)8$pq,hHciKmPl5G-fN!#YN!!Cb%Q
+3!&Y"ELef@d#f30C$6Y"6[(%J$S48Z9$H%b$P%-#$H'Bm$6LQkG9@CYYTDA+28jY
+aH(4[cYcBH8FR9'k'V4DjUIHFc4TH*-M@0q8Hm4kK8*Y#MSGXKC`)kqp#hJRj2FM
+[3ji-Z3$b&-K6)9m!q8*)GL1G"VN)mL6),d21Kc`(mRM)[i0m1H3bb"-J,i9N3[m
+ib1G$XVU!fMXG%Zi"i3BS6Q3*LM`,mQc)Bb#I"hNcj,'3!(m#q32)Zb$[K[`Kj+X
+KVi#m%[)eN!#A3li+mV@3!+q$P'e%hUp$hJ,j5XL[3,iHmJf3!'q%I"-N62*Ub$G
+$[JAbcb(I#[Nf50Nbi(dFmKf3!+q!I#INZb#r#RN0j,XKEi@m$I*VN!"r,pM"hSp
+4N!#M)9P4`$U!-C!!Bb(C4Y!!qAR)6&LcE'c`e#R)mb"I"#PG!BqFqha)0Q`)HTR
+p)H-J0iCNaG+QN!$M#8pDhCY"ELl)!Q*mJCJL%Ri$F0d1qAA)Ed"q%h*lb"dJGi5
+8I36HcC!!ci$F'A)Ab#Q3!$#lQb#hKR`Qj$D3!(m0H3(N5b#hKE`3NRl!*-LY)+G
+#lJUj'q6ZN!"l31i*#5ke31i&13eb!Q3Vj"FK`CmY)6m)q5()$d0H#rPHb!p!bUi
+%laMN4b$I!rPqb)p#AJpj!q6()$m1+Bc61`(j5FJE)6m&q@R)cd"q&K)@qMl)1b#
+r"IPYb1p!cS,mBdKKV9i'mN$)Jb!2KM`%FLlN!C!!I`-*Q$mAFJENhd*H"(Naj2k
+3!#q$[!35$M%GmMQ3!)G#cS-m$2*`b#-JCd,Z$ANNj&'3!-q#I$ENPb$KX'E0K(2
+KfGaCl+DqE@f6Br*)cC)rDS)k!5I!"2!$,N$1`3rb6Hl*,6NQlq!hq!p()2p`#(!
+5c!$(`!GiJH8%e"4e"1q!Fm!Ai!j`"I!6RJ"'J8&J)*J*6i'[J%p`&c#&'UDZUA&
+`'#bMeX")X)SkT+BY6S&0e,DY3fS6c!,r`$8`%9`&hm"-m"lX"CI"Gc!26)A$`)R
+!AS[VB#ki$Tl``!3`'`i!YS1rB$jF!D`%Km!GX"8H!HF#-q&*CTq3!1)pH!5'`MR
+J"Q!4q!9rJRI"cq"3F#a`$Ni"0i0$`22J+A!4Z!Qm#li"*i'l`IRJ#Dk$4l4ZQZH
+-fDr8er+eM4lEhY@@,cp"blA5Q''dU[6+a4DhI2hfL5ZKQe6Sq`$+p@3HTQ%M2lD
+PRI2mC+5Vqdc`!MB`#9ma'jM3,pBP*ac1Mq2db9E@S'lYcXI(3#&HZfJHkGZ10%T
+Nmc4Sj*GkPE)@"Z)@@"d&iPYHak1DU(V+hmV-Upe#,@DR`*B`9madLM`D$T0CQ5+
+hZ3*1b6FEl+`QqEIT&'UXM00X#AZhm6RMqiNA*VG#PQ[6!I3F%6-+DjKNrdj"H42
+KSFqq"PbLaliE9T6U!'EdZ!`HQHBQh,T@--jf13f&iG(fSc89'0KqCf!3aJahY3b
+8(TDr1pM[YeRmSlGQAG0$J`[ak#[kl02[rpPZ%[dhqS)mHTZf9mkm!*!!cQ-'`*!
+!@(Rdp1N4mZJ[@cj0,jPH*Bpj"rTX2(V)p-ji2iDNEmIl0dMUK2554rTUk1MadEG
+$4bm926VkI[32dG&A*2rSk*25Sd0(rj8q+6TkS[3FdG%(TBH)MTiT[84dc$I33d4
+($jeidG%6TiH)MPc50d6(c!Ip8R6hiSHH)lTIi)FH,H9*,ZQISL-Ap(l4d8HP$iZ
+1hU-L6**SS00K5IBJkCHLSip*cY(4lk3[LJlQTVh'*(X+p&(48D2dQ0(4rkI(M)j
+FdS0'ap`*r@"dc,'3!"0dpq'([LfkAq1(qJ*H`!M`""ee52mA(IQP$ic1pRl4d5Z
+L0ib1ALVpB(6dR1NKSk2[M%pdB!apCR6-M5KV66&RfYp2FBl%ah9[(QGDk*qMqeI
+mk0"3$A'0IM3kkTEk3NHq`!Gdp(VT%k-$KHNES`-RX%9(rjNH1$TQBV6h9F0C'2V
+Mk-!rq[VSQ+fJGik113GkrZLB(G(qG3ea!KU!lTHN!lk1)8M$"Y#"PBB9L!jXJK5
+J!`ZB*8"(AF%0d*&(`a&%4jrE%!M4dBG'Mikj"2V8k*MhSFq0MPa5QqM!#@B1d)(
+Ip-(4-Dm$Ad!(CS0,PNS`Qi!1(+FrMSimd6G(4jdcji#12&&Ik-J&q8%((Y"64dG
+RJMN%G1!Ch!3GH)BIG-`6-Gq!MPN@jL,3-@F$6U*MYNLE1@RLK(,%02E5QlN)G,r
+"MmjbCXKKG*!!180ZS#3h3mb&,k!$"jP43!IZ`*A38I2D--b`CTKG3%IZ`""da+P
+GP`cR4-"`G-a)JB(Sb$'fk*MAS@E4-DZM(CS-jfQB,8&(MZ%[k*K"Bai&(68+Gd2
+h(rKK2N9HP[b0149di$IeKBiFqaYKh6F[#4`2eZ@lAPjcHbUl[U[GE&rIfGq*hlQ
+pUAiKY+k84l2AhrdL5f6@Ka"dHQN(eDa2GfmTl(#+h0&bC"iMX!pMc*UH@$be80M
+$VEN9&c$RA0F+KHdMGA5Ya'A,H&qVf,9LjKq'f"Z2Q0D1Zqr[JMdqXp(6Ua0E54r
+pQ`3LT+Hc1[1P6)Pb3TK,lE!PP'19XApS(8EmBB1"GeH!K38,UPE`4[JZQcNc9mb
+NKi[LqJEA4%Bd+mi$TVKf'Sff00h`TeL10C!!@1LDQqN$fSHQE9JV48MId23,V@X
+DKUC4L!&XG!ehK9(0[q$$0I0Xa5#E-REJZIIBhJlIC+'ejTIfQ6FC(8FI-6UfV4h
+)QRdp*A@MQHIIp2BEeLKM%cB#62amf`dEp48'LQ%'1#Em[6#)j%KLc@22U!-($Sc
+,a*Q(YkTRRhef8Pk&HF0)rrl$KmrjH%5fjl6LUaLlqd(Hq*R2a#6%DCml)ZrJ3jH
+YN`!IIKHlBkkADiIGad`-ZqXUdI`VBd8IHVLIf'TV$rIJS2(Ck[eb1iVm'lBDkJ0
+Ce&IYqZdVE,8DFlXqHRejU1DU+jDUA6'ZeA'XcGqkq,Vb2qL[2HiZ2f[VCRA09Z1
+`A)2HRHhf-APT*hpTipR*AkEPE010#6!lqFYNTChmCD1@lHqcjFT1r[i88[ZIM'l
+BbGm%*epemMI"6V01rLBiCDK6'@Dk9LGr%p`dTT1r#@kSdXRI"+IBG2)h`HeNG[,
+h[r#MNlp*jPJRIj1FAYA*hb3R-RAb0mP*3Chm6A)56LGrcI5V6[iQZ3&,*hq6h'U
+QNlp*l&QdNlmTeSC1rUBirD'6[bP1,qVNEiVp6Thm6A'#8#Gr8j`-eFRI&,HHkH4
+[LYZcG2)ha8ea1[QEiJ5LNZXDeV"1rYB`,cVj@m1DeXRI'MCIGI+hKK101[PV*P*
+emVH'FH[NE`dhGZRNE`fhGZRNEmhAmD16[l@X@jhmV@A-1[PEbdPFRIbYjD5T6[l
+@XQZYNlqeh!URNlqeh25QNlqeh##QNlqeh*bQNlqe,q"(*hr6a"'Gr%fc(R6bedb
+*kZ4[QTeRR@*)XljemMI0+8QGr%ec@je1rUBjUDf6[fPZAY2*hc4cS*1rCJ*8*hr
+6[%T-*hmca#DGr-f`$R6b0m2iGI)h`rV6,S#Ck06*h``R,(AbedaZkZ4[KY[BG2)
+h`iP8RIc0X#qRNlqCV`P@!lpJJ%lqCSN"1[QECGhUj'q@HGI*hbcrGI)hbbPBRIc
+0%[pdmMI,cA`kqCXPrZRNEjEiTj1rfHIaSj1rGF4iRIbY)bEUj'mGke`RIqZB#jh
+mVH1NV%lqeR&b9#GrkaLh6[l@F@T94ilUZ192*hrVL%-kq9Y2,U'6[rA%4*hmV@H
+qGI+hR[(Vj'mp*fYemVHHf`TemVHH,&mRIqZCBjhmV@p1rYCr$cmkqGY!2U#6[`h
+%1jhmE@$0ki4'!r1SNlm0R-69bGm'EMl8b9rfYGV*h`ELM8lq0Tc&Mdlq0T+ck44
+1)q2@bGp'BV&1rMB5&h6bYj'jeXRI4Q+-pYXEZEG(*hmE'EG1rMCb3k01rMCb`k0
+1rMCq!cmkq6Z#I%`RIdH3!&ISj1m)BU91rSiJAX!Td*%l0N1LSkDC8NE(TN$`(af
+E!*NN4mH8-CXPjBdN0i2hSD1fQ1T("fkaF4)GY3H'Sm-AYBb1$BCXS%6(T$qE*p(
+GJamQeH8eNG1bU3%G(!cX43G2S%E3JB28+6VUM4bLBe-P'b[4XH%326Sf'V,"%Ke
+6i@bbP$H+I*+0PZMJ5r!GG1!lR!XG'!5R4NHG-*f1$Pl#GJ"dE!0Jmb8klRaMdPi
+HTcDCMBhS`(if,D+METJ!4dHXa))1V'6M)cT`!Dk"MRU!Ak0M#`GeK)lYL0S"'me
+0S'b%42FVr-!,j)dKhmBR1MJC@`c3`FHBVNF(pS2Rk-JhQb24N31`#"fe"$kJ``B
+qL!lXCjX!1MJ(facNM5ARCY-N1R*22D+$Zm+GdF(CG!rb@2)6qKAS`'+fIk)$,pM
+FLBlDJ!1L!crJAqMBD-(@6(R0l*A3Id!(2kFfdGPYNZM!BcCpS)0,X#N$(AM(GP9
+de$CF!adaJ-2Sk*d3Zlb0b+[BRS!1h'AV+6Si*02lk1!ce!Jkm"SHJ3jFB2XS1[*
+#I`BGI3Xf5XJc,4%i2MU`N!$Y+ZMJVq!c1R!GI%-(4k)AJ3iFeeQ4FF310JLJ)ip
+X0N%(lS)EmMCQ2iKH$6TU&'k&$Ki,ld"(I1#EJqM&"aH`-E`e2%[!blLia@CafF)
+&%-GlPVGEC6D,DS%GALpJ$9ZaZEbecrEGZSI$-,`JA'QqAkh@@h@#C)(k`faI0'+
+C8k)jC(!lP!ZG`3V#hE(S0R4HEaA6a5q`#ME#p2+pfB%Y55a[hT(D16Jp`r`IQHU
+V9h'!a8QKfAkEmVeQUZ&&8V3$G4+3!)CZNl6Me!%ZAUi3(L1a`#kDHMiMGje"P'J
+[5R)dc,&MV9#LQcf6(8Qr9"i29LTJS9-)lb#3!(K&'k9UcQE4STcVfDmN`-EDZXc
+%`3EQXJ*[BYZlc)pX0hA+J8N9IX4P,P08JEJa9@#ZbM!D[jiNKU"`CPV,EhG@V#V
+HP'UBDCQ*ZV9KE3bRR6[i6UaDHbXee`Y-k,ee9VRXZP8NFf'0Z1%IeB'P+29'$E4
+b#)DRMm6`YU5b'cC-a#!RCN6U1'DlkRjD!kjD8Aej(kC!`Vb)hX[N0NqUE@C8UCP
+"'dC[M1HPJ5a"*4`hAJQq(5Jc)jd1YIR+bYPEE3(*a+))$@+LqJ,HGB5[[U+)3rH
+NTAKTJkd68bDqmAE6QI#YJS!LZ-UG35)PThi-l&'-2QBZ$kqpH[d3b*,a$cF3FiP
+haa1d$FL'U(1f2KK!@N*rIT%%,XJ2Pjp%,N!p[&SX*eX"23"(Bd1,Q(JdD6@`9dJ
+B%4D0L`84N!!#Je3q*KIa18qVr("%)HHb,,6pJZI8!Pd3S8%ZjXL,$65d-INiGFC
+6P+@mTDQZNNQ"em$XTRH$Yq9Z1SQaGhUhCJ8Ri)+,a@8lk3Ie3CqFle6i2c$GYG1
+Z"U(H"(4)[K-kSk#ilF8Ti3NXUSX1jbQF*j1r5DC`L%cqqTMij*M5C55&%h[bam$
+NcL5*,hq*,QXTRG-5CY3HcK*Gba+Ib",IaG*qN!"4G'T8kELSm*bSiK&"hSAl2FV
+RHS6AH951m9$1,V'GX-iYlYEFhIhpp2l)M"a6@eKM,9VYc[Z(5XK3d8Srp+EV@Rk
+BYkK[qD'Iha$jX51**6r-(ME1q['DITK&(0Q+KaQLdE0q,QVk-4G&2*K(jKLI14G
+hU'#QCZq@(fBHRphb``cNl-K2ac)A[Fdb2CI(8-(Xj'9ArH3+`!bFPUG30eJ4r*M
+RLVRdSDm#)*[f'KRQc(+q(&#E5A6dd@iQiHR'Kf!cbC2E4b+pIB1E5BVqUh'[bdq
+8Mbkr$m8p[*eP4Gfi!qT[R4rhJHbV`ICDlF33Ebc@l0)'*m6pA3k!Plff&G1YmU4
+h-Q*U,V2dbKH"Xi[&`A!4H2'jRcSA%fq6M6$K6V@qcS8EJ$dAp#Xk&i2GKZ,r1[E
+FVZq+HcMIrjF2pd(FkrDCpG9NPlrU,VCL21f[&)GEEJ1SZ6a(pRrPjUALYPD`09h
+mX&IZ`*BIGMd0c(2TlZaM@hl-*50GFh1K(hE8lGIb`rcDISejLmkj0,&L4pUc@Rk
+BAfZ)$r$MMVRL5SBJ4jhEPl(Q4HFPZJ'D0bZ2$$#Y)VI!!`JDql*C@BhR[h4hhm$
+i9@L*"G&+(*PXRGQElS,2C[P2bRp+rQZDRT0YjeESj"c@bRm8DQV4j)SGQ4G9*aR
+jVq-r0dcUIk2B@6p0mMpDl%Ia(hcRR`XUq4Iifd,daTri-I(*Ie-c'69K$bqhTb+
+AZK(L2G2dPPNiX5G(dUS4Q65,VaD*I),%B$-h9M,8dSbPIJPM#GIRq-Y[0!(3+Id
+IM`@A6V1$)DA$EMI+r'mU!Rl5FGU#8q"5-$EGTX,q-IH'X[-l)8Y+(@q22S3"%2"
+JfK!hV5`Sa,B"!m48jiqj)d6`i$3V[Jd,#RK3bd[5p1EVHI8DYmj0Ncf!,M@!X(J
+JI*%E%)Yi%23&h@Q)%1)"ppF)(Z5QdZB&f,NkhbZ1GebRKAXQTkecUR&Qk'$aJ*Z
+Q9q'"q9YFUEM9eERk)0VTVEh93VPHM!NM%5%HC+lKJE"+*aNBj2%JUrZ,,*@H(m5
+hY4K$2+M6H$!YihSfPq%Z0@p0hTkm,@i0'8)mU1HN8QRKXR2ejHpHF,)mCpb-)T2
+FmLXh%KYeprjLmF1G(J2lPF82HiklpN3%`BNIpN3mUq@(IF2Gqjp$2a`YdVhA1I6
+$(6VGqjT$2qc2f(3f(VfAa[KKImE!2JkaBRp'erlXb!rhPR6YiilmF"Y5ejlYb!p
+h)hAYG5ljbHh2H'#8hL'r0pN*fdZ-VmPaXcaRA#r4%E,Aei&`8k[Bd)a,d9BNCjb
+0J15Ze64NKJN3f33j%T*6[TVPkJSAa8fd5E@`l5Vp(ar@`["M05a,`G`!FFc*T1A
+'ZjjA1'QTmZENdR,lhA!F4kENYVbRmhTS[Qmf`M9a(0DeT6mj@&)&hqp3aRIfU88
+E)b6-(JjemBe2&K$Hm,&3PmL2KVHjQ6Mk6m5*ci2UI(iFcj!!@`Ar%-F$#ANiMJI
+@42mqMK@"$-9adUkD'RPLVklADdqAaVkpeU#q'HHD0Hjq,$bfUJ@%*0XGhY'A'$R
+H"[[VM*%6@if$E`Y[-c4ak09mMqI$R"K2(!q84q%5V0km,X'G4Sk!114-VbIbbJ*
+@'dFI%Jh&dGmPT9bVcm5a9*8ZiE6Hef@!fTS@FCVMZkUaPJld@[lNIM1pDr$r(rc
+M$0q,!6JXL6M-f9,pVh"R@19a`*3E)!kH1A,YLFHD6'AFiFe2KHIqB0cPkfMe2F+
+iHq*`"M$Z3pCYP#XqHelKkV6+Fh``pqkX2X,F"q*BaG`(iPM&h&I'm6[QASfMbYa
+jbY`6qjGXM3&JlThjH)bjpb4f$A1[aV'+ZAH'm5KcVmE4cp`PMJ(blM2hNiEqYqk
+G(ibEfq3Ula('h4d(ca(!Z)pdUVHpepbeDZ8Xikll%@151&BJ`d1-D5#198aRC4b
+rBcV91"j#mXii(NAbDKcpS#Ga50f[C1EQ9X$H&hBNHLY%iPJm+,I*GMbjZ'iJMVV
+-$kB`XjP-5Ll6IIVp&[(N0$iUF@8F#kp`4BQ$+qjcpr&e9[5*YPb$1jcABFi3F1P
+q6Mi84a8jqqXXU)mc,jICEep$H1K*,mD-$,Z2mkZI49HTZ`[9rR*GU,Z('pe(*d!
+FE@AI9i!B!C!!lMJ%kmQVGkP2EVU'6S"mQ'kI%b!1fqhVLf1SfhGb8#QVBlKEK$L
+@iEEBa)H0Tk%IGhH(-AbUTJiE4ljir0KHip$l##Cb'h96&IB[0BjmBEdar&aiLkb
+*ia4@88mq"ZY-Zb`,DKIVP4D,!D,H1,K8aZ0"0m0eElSX6S"k09d@*d!F2"r1j!E
+V*r*UiT!!m["qA"m(Ze@jalNVJA-bZ4L!V3"XdUp5$fpar-6N3e[frSZp+IrC'ZY
+lKX-i!H+BYGhEPS4m-Uq`8RI&c',IDqG#aCIM3NrA4ii,29f[lS"C-F1&K[%$"$l
+DUIe`!QF`Vqk*cM921G96H69ap0m304K(GF6,HfImfiaAhaYL1QkP'DrKjeLDm9T
+5Zma1UhPe%8ajbkcBm(1!c)Te!XLMXf,G51d+Q"@,,PHZ[%cm"(0h9$"6*`"bZK@
+3!'V`$Hc*FhrdG"`ZQh,kRS1J&q-'fT3cr"a,Qh+'HM%GJ6J*0ZkXH,TaCm9cEfh
+FUBlrHfea&I2rUaLFHf12`%2-Uc11ajKA6d,@-+pU(-k*r3K$c+XM%,I!RJ8r$R1
+*A[FM#,PZa#F&[AlF!)c!a1'8f2Z`!BjbJeaZALREQ&GbbFfRe-E1cDHNiqAQ'EQ
+"I81)B9l*FBB(DqH9A'4JN!$(!kIqcF6qCPmhkjf6FYlN[**GMQZjFC,RMVYcNYP
+90mfVKCLIG#rG64RF4ZNC@!q*j+b3!+01aL$N*X()9CI%#NI"F[-jAejcHbUl[U[
+GE&rIfGq*hlQpUIlfH*8##2Sk[EH@1%qIR-rQL,f&A4j(RR-G,IrCakXIqS45Gpi
+eib3GaiB,j5Hp,KG-pmJ-5SeTF-I@jDN0'I44Y[Y4#1*iYQi*0Kb2"fJ%KSdq6`d
+B`mD#a!HR2+5q`MqUeaP3`6)KP(q*m#Tmempc1e`"(C5a!q%Fk0c4e!ZXXV2RbL&
+YICAbC,NF&k$VZD"U"@q%EmM3@-d#@N-DX,4d5ke@Q+3"f&Q89`4f-66q+km4%Xj
+I"pN!#CK6NM!mU$ilbRMd"*+3!1k&k3rLRhreTM!Ac12#Y9QGGh450M8Rqmh0H21
+@pTNh'4e((c%kYUdGb*Tp259eSjRRhr6f'pBSBp-*TAlkmfdhE&4$jN"@!iFI9p,
+X15UD@a0V(RZ')k&',Iq*-`p[RG!(XUL[f[AEYqGd)1CfII6kmP$099FX9EYLA+[
+M@*ZrGI&ejAr3Ah[FAAl@eXhUQZf*`jPfVIQKiBAIjQ9l(Z"[j3V-Rlm8kK)2(6c
+%I`I6$8Sll9NlFIY[N[VS@R06[rkej[2p(5EXrAUpEc4X0lV!VM8[ih6P@[-#6V[
+0Vc9h)M"h0p!PMhh-hID0h3(-h5eeVIP`h@r3-PaVIYfbTp"9kZj#YEpF&qVU3ih
+ZSa-JM[+ejZ8i+PGT&lTLTQ[S"-L(kIBj!H))VMA[M@1SffFZP*ip(&eVRTbIf!+
+pA'ZHA2b"%I9mBZAqPp-R%qESDEL8Dme(RBApja)VlpfA@(aI4b-1Fk&d6ci'kka
+mVANjMXUejS8ibYHDVmq(kE)i!HV9G&QF!((`mYHDVmqVL51qeRap("Z)0YHD1qB
+fF3ClJZjmHlKTf%'eLE-&+pTeVj2BhXTk6DkieiaIfm4Ca'NAr#E1TjLl#fN6TcI
+1h"0IIVSM"S#jZl3fF3l8r3BY`bE1Rhhb5f#Vrlea-0H&f[*SSr[S")MMm8fFaDk
+Bk4Sk!I*KZRe1J$KXYkm[MU&ZhmT0R([FHQ%6jal,cfrLl-R(B*dp[SQc'-IMQcL
+,FCJZLa1JANfAa3N3"frG*XjLANdFrl1*XaM("U,0*NkZH8f&IfQXKKN4(hF,YKa
+0HMrR0JdH6"ZbUlER&',EJ)($lX!$qDh4H(#D&Gq'"38mU-8Qjm4U2D!bVeiMejc
+@BeY&fr2Me!$#iS(`aF4j2*!!GXBCPZIB-@U)%1*"mM`HL0De4K&#2%KT2-KGFMd
+[X+T&bbYL3!+@F0lU6$jYTPD[FkTaCZ"Jm8$kcCRraJ2cYlK5FDZVFrA"XP-`HkZ
+&FVdB%dBU3cc)AX-$iC&1-M$)id'GlMHIJ2@Gmi2iYTCcL!Ie'JqQC9c2hS#&VN[
+0@j1h*fq,@d1'%!mDS+UPLGLBcHEQ-DALUIE(Gacq9D@'BJ*fF$LlhYL%aSF)&hC
+qI2ZZIU9+[a[VKJXEcAX&&`l3Z$#6BplZYYF+%LR3%12#&1,#r@0RAYqcl%4ldd%
+@-Y1id!CF1(aXl-@IMA&$"Aph!VM!lS6'rkY3l%j`b-h+Tf2RCZ9Aldj`Smh+Cf2
+RCZ9eGi*$EPDq2YbZiUCj5DcQ&D`dRr2P0EHRXZZlfXhfpChpRILGfj[UEipA+3!
+R$!BiBTkJ3-)`hJ6#*,m$hX+G!DXTeZ#'EQ&Za*p*k#`)2ZA6NIKd``qKelAm-1'
+HEIPK3m+@$6r*D$"HhN902fRmMSRma2%NQRkBP*h8m*1DM#H+QkdZHmlk#B)c%lH
+H@Yhbdi6II9TqU1*p@hl'i(Gf9$mG&5pqaZ)h29GRSCpQr#kJZCN-kiHE`%m9`0c
+lIS5G$9`2jMapE4Xb!,pVmNkP9VEl#c%!Ya!j1*icES1*-ki(lXCj'dQleKdh0b,
+AQ)Nm6EB),JThK(R+PHea5f3mj(6C2RQ'S-BPm[pbq9mQrjI+rp*`eX&6J@bj28[
+XcTErFb6HFd9rRX6l)SRra4,[q4,r"I,r%[&hB6M$`HNp[[iLmAmaTNKj,`[MGKA
+-a)b3!(mL`K'C*&Sb4PE)"&NN%r@3!%b(8!dMBcC3JDcC8L$6C*K-GfACVhHh`U1
+k(4KPY!qHZAE1E(bHNHlH8YMK&(1HZhec2VcQkQJNFI,&!'T3EAhfak!5Zqee#q6
+ifCQHB0C*r*JD$[bNAjYe%MpEi(I'V*mJ12(6JYrp@hiQi(GQ`dpQdXp&66rFTTQ
+Ef5VN-I5c*Ai(CTh%cb6L9hA@5IaX4Dald!pE0EYRf8)rfq"hB-E+qYN!4*LamRN
+K2"mH#GH(L0K0j&(h`qr(Z"GH8LeXZhTZj2MiVQXM6fMFDcdV1R-YHcM8Y9mEQAM
+`Bk%ZN4mGjVq$kGV)@jp!2jCk[ICdDHcED`hUQlq)[Y*jMi@1@X!hXYhKlGf*NH0
+YX,r1'$Qae6MiYYXEFEL6VShXRj@91!B1KG3V)8qqK00k*D3"DQYDa'RheT@3!-U
+iHmDZh4q-f`&dXl9MJEQlJ@kfAXAF%rZAE)d"B1lZRjZYh3+h9JrA[32NeZT6Ar2
+ak+h9RA(`(!'-ZhUBDeeFa9fZcSPl@Yd6GkFk!Hj&03MMP,M2Y#lcqYAArXYQ-UQ
+HDr$ArAk,H'fk%PI'XI!+9j3i&PUqcchP5l#DYTMFS+*2Y,I&"14eQ$-%A,UIN`r
+&88A1rMS,kZ2-(ab9r1"Vf'MYY#jKc-L`qcLrqPPdPEUl81d[ei@kHlM4IA3#a0&
+@pRd&L"%!kBj$X*kmHTIkj+CVk!6)KqRf13(LX0fq[ML'ZRdR"j@b1SDpYX5aE!6
+@bibRS4phGiFaI+UQEMD1Y+mkYYFip$k#b5CBNVJ)pLmeMPcrCmE`Fr#RFCc#+ZV
+*af#GDCGP3HeL[G*P-8$8'mI4[6P6*IGFMQ-`(kE,iJ5S9p0PF3,%`I2Kc2ZI[*S
+iT$bm(pI(`@jepJ4pkh"1*KF$X"Z1#fQUe-0E($maqG#@[IpLEmTrYXEkRZ%`6S!
+iCQhhYL8KRm`VV04aFL'pi8*2edH1#ceGVqk!MV2K3X2i!3)IlG4q1)%cQ&Ih41H
+DTjcUUEbD11"8`r8aX&PcXrLh'Dqq0m4dh%ScAX22X66M0A54I%FJMS*CXH(R!*N
+9k`53!%GRaEU4fK8`+qBcpcQf0c6E&-h%6c"h4`8cG3)JTeX"U3CIG9'1!k5Rih$
+CP02h(!5p'$I3TTcKjeMDP$28LqN)a%A3LhR!KflF@I(F@aYhUZ2rAPYFaIcr+JE
+RhYJMm"$ckScM-HE9Nj!!0FbV'SGcBMr#%22U#-3YX'I"Mq1Nbh9'%(+ePNm+H[f
+i!4L"LF-TXII"RED*mp"`Hk0jfCl``Z$+K2r2A`TeLBF1(Z+rJkQrZ01HY40lRC,
+8fle1YY'NQcMRqcY-f2[eHYpSH+q"@fS6jqrlJZ%QcM*19cCa&R$DEEk*diR!h0e
+!8pTpc0eH1Z%1B1l1SdfF$qr3PZRUiEVIS'ABa(RGXUI39HVZ3V@rA"IUkN10lU-
+6))lb*Xjb(*@0Ji@ZQ1ND1J(bBETp6S!iJNfF[A%-GI[-pVRC`p%QcZ6ma"ESC40
+RF[%(4Y6cLCAlAdkI6*LMTq&50R'11J[lcb9@hVX[XILqMNBFC[YF6ci'kkbmLE-
+F4f86Cb'1mLE1pIN`A4BR3,fD,SX6)!jHIK2RqVbD11*0R1[Mf%#dfF5j)Cp`#rH
+'X`CcmTrm)b)!&D@,"NrG-aAKj-%2"d94Z!%a,@'m+4(q$RKM6&LIH1b,b9f"#dk
+CZJpVG81"!5FEql,CrJQMG(II`2KQL-5#rN,Cc*IX-YH`e@lS6AI"Cl2m*q8r*Im
+EbAp0-i*N@mB0A-mYFprjmClPlDD41V)"45'R&Q'0KZpU3EJQBk3ibFKr(IrPDT!
+!"[P[&$[VTdRq4i[p+2kRP$,j,i"Pr03Up5l4'hrLam3Rrdh0C05mPIQAC4b,kei
+Kc)C0h3MaRQPkbbbNYebDda+45E2iDT()*dJ-0R0M*8-Yc9MUPc!@pYmY+&L"Jle
+VE@k4LaK0!X6CT[)r2R5hlr`(YRpeV2Nb(rh)hJPplRhMdI$pkY'A%+@8p5DUiJI
+6Qi!1ack3!%ED&EE[6Ii'DJ+l4G0!-SA'BVGJbQlqH"`5AqSVNGeHf!q6fD[MLqb
+f4ha*fNPmN9eQ+8YLLBHV+*+RILC#mZ44r'bPl@lNVLSP*-Nl)hml$""9e1jfr,a
+@flhbMeMJ#MVXNCqPlIEi$d+GEeIl([cXUHf1iFDqY16lD2b-d(C04-q-YXYbrmR
+1fZjbVZ(+LYh98AcArLPdGGTZ&[c9L0dPIm@FDI8IKCpQEEIT%ZJD*'kfTilAGTr
+k!Y(EYb1Ac8HehHXBm`LT'mPIBVkfHpi[%8be$VKrIa0YGrJ+k*SN[Vq+kZEMRi0
+1Z9k+G6C5fhhLYk!EVHff42dQVY0fRrp0')b4ZZ(qJVfeh3cQEkc%mCNSMLrakQe
+Xj@hp!Z,iYVElm+-`f%Mmh4c9`3FBhcM*(pXP08rM2J(GaYTZUc[`Fj1fDlL21IE
+YGX)DRB6Nqb2%J%dP6papm&jYGmrGV#fYearKCdGYPrKRk$E6G[X#Nj+,Y&e,(``
+fehCrJEM6B[I[a*3YY0d0L'rUEfLl,Ai0"Ld5(e[6@LZ"6h`0@HPT`-[-@bT"Z28
+bY,ZH1GZc%T6G+EXd,b!C"ArJEQKAfm6,(l2@*dKpIc(#bpeZB3l8EPRNliS[3cG
+4q-rj%9lHpK3,A[hp,-+c4Gq($Nb'!S#rLTI0cc"@c3HiShKjbd%@[1B[a-XV(S&
+Z'l%,mI*@BXDffZijU'H,PrGb,G0Nm8Hp+NkFqehSYY0fGF3MaFXErKUk2p*f2m@
+raFYhXYCdbp61pdGiH3jh+i(ra-'riXIC"k#MGX!KI'MGl2mpBSEL&VGP+-lpj'%
+#N!"[Ya[`hH,PkmmbB)d2EU&iqElIJfiA`BQT84bh%brK@I*frfL%Paka!6r%38e
+S(Ga'E+,fi3(JZ-8$X'!hEEFrm9(amYej"UCf8VX@,hrmac$B3h!DI&5mI-[2XpC
+pZdRNMiUAep2AAYVZi$q*m2+H,!bQDEX2246KjD*[`Z#C'PCffK6Kj9KLbGk5ekm
+em6*X1Vd(H*PH+hJCfNhk+QY,m6+dUqpXiQ9SPb3Ae2VBrTqJ)lFNK![0&5p[)(i
+r@qS*(UMqM[Ppk2E4GJF3Ha3[AdFmjY3%m[I,#-qqM0alc!2Pr-N),bGq#$VG8TY
+q1F,,CBHKdjeL+I!NLjI[C!jeLf`0HBA'pmeICJ(lGQFp(Z(PjX4[hGCB`reXLK-
+IC'`(50h!Ka3[ac#[HQ(0Eh$+NFA,HmPh$p*fCqk-m2)meX3XLB1m+RkdSFrJbDl
+U1YlLSA9c0R&&Y`42BRdVcPe'VMP(fme%2m$LjAXq6N"4RJ'1+&lH3Li1YP#UB*l
+'F59jJlC6cJ-f@l`m&4M[rB,%)IQcGA$H%p$"kFJ(q+ej1T2B!$E+Hr&SK*GI)Cr
+@cYRji#X@,rFL[k(R)1m#e*r&brQmK!Pq5YbA4hLj'E$FfkcY,[jdK*HhN!!(d!H
+3!0$'`*r&bdq6-p2I%,[Mrb(#bldr"3YUJIMJm4B[YII"Zq38-8ha-V5E62iNZ'M
+`-V5EmYY0[!cY%Z4G@KpI*aBFSqfQlS[`FL5aj(Q#Cq4&rEf@(1PBEAFJDe(amNc
+f4DJTD!Xmaq)@r2)ib3IeV(MjpSp!TbG26#%R8lbmmPqJdk-NNYb-U(JjJALJHqj
+f24R&G`(jVZl4H`RU`Z,PYY`CU(HNl%jX9TbiLaLR+p&hB5p%mA*2FJ2r(T(%JiK
+ch%PYp`2bB0ek[`ciB[(bApK$d6f*Zab*m2)UaUTpY'fIM1VQr(q(6[IVlI,R%Fl
+Gq#hSp$D65i%,&LrI$-cfT(qhbpp&H(N-Hc'keh8+qciDadI*DE@[GcNiSXA,,l"
+R!#lJljUS$Pj0,0I0Nj1"3cC2Ql''kDA)qqJR)lcFQER`qib*Gc`BiH@9a!Pr(f,
+L*qP2mI)XpR4drH%8FJM&bbq`$[apMSQI!TjD[2aRFMl`A1`f*bp8ZaELk$RDlTq
+3!#q,PdHa"hHZMQp2pKZH,hLT28(mI3fkG%R`-QIRhFJq`[D#Pk(G92C&,&k'FA`
+)IC2kHb["d&5r-l%8pCCm6kAQ`epSPpcj)ZRTh,3IJ!H$+l56i8p`-Zfc4R(mL*c
+eHX40(HAk3)QELAG[&lZ3!1rZbVl1C,(,iAVL1H,0Zm3ZbLXmd+qR6)EF5eF-Em[
+HJeqAfAGpL36*YkYP[ka'qlZ2r3ELKGi+Rr-'YGfpj!4`9H+$4fPm&j,,JS(82Id
+Q2ll-hj!!keiSI!*FpZ2,[T1p*rSHd&*mDhc[B%q%(L$a`4-d[P1*6aGVZeRX,fK
+mhb'hJq06#0+hX2Rl'$%GAL2[&Gq*iVZ&ZAQja!Fqq2&PhmcBk+H"+pKTI@c&HVT
+8ffAB4p(ir[-[SB1r8-,JSmEhFr+$2pATZ)Gp)SY2fTXfGY+I#2SISGdrXPGNkcj
+RjkeN6"DI3VZYLI'#"`E23VZTl-Rjr#CPNG2#@b@qM`1(Y"q3!2S`Bi+,LGdGb)2
+LEZS@FP6iR5$2424&,1kqLM(5ba9r@j%h+aqkRG`1lNcF`P19$k8kL,9AL"fFaZF
+CUAh%2rScf0&h84jc"hYYVp(aEBfHJI+Ke##j0eaEhPcb2jm2TEDb"`D2!aITR@X
+F[f'[aEpP+,%-26cP3kNKFNYi-IlS4fZqPj!!%`Z[iE84Mj5[I*JFlidkMQF!!j4
+ITJjm$4C[d[kbl2[iH*!!r![f5Um5QJeqqAL3!,VZDHMJ+I*QN!#Rq6`ie80FHE2
+%$ArbHA$UqVq&lLf5ThG&[1dDaJ3R`LlX%hq@24[i"A($LAhHPUT`YZ&YfZjQp&m
+XEl[c$iJa[Yefl(%UErXemB0H$ALFUi08NKK2ld[mh3*-9Gk@1M!'#f)"RqKhq2f
+$e"TL$AQM[Z%EQUIVf0Z&KmUlJEALp`p5Gl*R4km'f+([S$cq%q6(j%IHAUceZ49
+rTPrHaYkBY3YjdJcb#@XAmU%Cj%R@,X*Aq-3Sm$0iFGJ[1BKeq3Va&r+YV`,[Nfm
+*mq5GB$m-h"1lli'ATYkVl3jR(6#,3AcdjAdmp[DcEQl5rZj#Mp$'m5PL#Id"-6N
+F[-A'F41j2MJ2G)#A'XHh'$FF42`GaIk6aV'-'%'HaH4Sp%jX(*Ha[mZX#R8$TQS
+Fah%@jF[Dhl(NVaV("iMTp0l&j$Md0@dFLmJ4QHqJRUKEMH-ZcP[FS[fG!1bhFAb
+5I46U9%a13Rr)a['AV%Z`MT+N"kpa[)#B"3q5GaCaH93&&bcNDT+kq5(lFpqY@00
+A4L4,i*F*h6FDrB[Xh6"V)bDh%BF1dhBhXdp*he,XrS(qrdcEIB+FQhk,a,%!r#(
+4TZfH5rb',i[G+F!e'pq,L%0Ieh(mL,@LmHh$rJ5F92bG5VmDhlICXb!@X9X)EQ(
+M1i+e4"p'l%k$$a[I%HaYI8[E,5+IdrMZCKm&2S8GGD2aEF)krSkf@`aqCH-E5kb
+P"X9Z#A$IaMH&f(5RYMZGpDEahI&ek1J*L0dCR!2aia[e6FkFI&rXT%pTicZBR*!
+![U[B,3AZf2MH5Nbm5pZGbEkiaVFAqhRd2F6Z,2D40Ap6f31'[f!Rm@KmSrk0R*!
+!2UIhr`#3!`d23Np"Ae4ME&0SC@aXFbkj"cIeFJ#3#!Fhph4`f3#3%([+!*!)rj!
+%68e3FN0A588"!+qqEm1bEl`a!*!&!H(X!*!'-DN!!"d(!*!'R,J'q)lh$M+bf4&
+qRH6jN5fHl$0TjG1ZGc2#0NZbMmPTpVDTQpkQAl3Lq`KhcI#F(TlGqXEa9rc-VY-
+k2lE0M*(EAZq4rB346MECjlQpcMbbcipY`RZf6h+EE-+IX+iME,mGfi4hAK*1pX$
+HMQ`l"e%XB,a!9'Q6[Y00MR[ENe#bb5Dr#5HE62D4iEk9Y`,36lXqVeJfd"cPjD"
+Ll[Gl&lE2KFFqqic*[HeCAYlLZE$YGel(ZLlZlII+LcIaHUphdBB0[(b[3EjAIJl
+X1MX[1bFjb'%6Vr0kXj'$TJ"B!'IP),04GVh(ciAGCXYa`VmMQh#bbH,&h3#-i$X
+!S$V2ldY492BmEar2bdhcFRI1p(*h[GhcAVk@Q1Ybq4a(qkf0KS+"BKb(8Hcha0(
+bS&4[a(T5[VDX&Y6&r,r#mikHkAR(#-qpcfT)IeGklU+VdKYE8l"`GI2EFmHFBQX
+dZV[dlTFQ5`jb2Lfh0VGrrZ`GfE5jDFpjBf2MBImePV-2U0FGq9jkZHHpDFaq1U4
+P@bhXbPVEhfrCmE93AA1E"03aZI!*e0(YeD1mr9p5L+V&8V3l4ahH01q!h-EmkXB
+j5k,bcXhp1,GjRfRHG0*`JqIp[0#FSY,)P$+S`"N*,rSG+ZL+h!fj,IP(YPC3N!#
+E)Xm9XZ&pb&V1QmVrU+9(rkpS3X"S&9i*(kqrTDS1[U2jYlSRjr@12dprMq*rMV$
+,V9i%HaArH@'rP@EFITrK[e2BAr%[$+6X2I1%$A#B,qaB$JF)1j2$!Q'2jE"3f08
+F$K6f*Y80f1pb@#6X5a`1%[DI("D,"-Ra`F,ZcZ%3B5Ga1&6B"4`1%rC8$NZ%[B4
+$Pl"I8lf!IC4$Yl#rj0$MX&DVM9jKbcMd#EX[KhjKCh!Jrf!r`i&l`*l&B9$B+cN
+-#IY0$XZ&r5'(B@&rcf&%0%`0M`SM,f2#U'Pi"H`d$SF,JlU3!,YJ[(ZP-0ip,Zb
+p($4['1ac(+JGX(rJF!q(cE'k28)BEcj5f$%FY+)ff)-ih&-BR(B[B5MYH`[l&I%
+2f,Xir)5`6h&B*Ha[1Gc(BHh')p38'2A!Hm')q8PKQcM!,@$8Yq&-M6f*`fTKeh"
+B)qaA1D`9pRi1a`NM"qZ%ICR$mF,qQF-*$XXE0jdSM$Ui[c$H$mq!8Am2%2C)$ZZ
+&2Cf$m(,Hq1CNB@rNX%(Blh$B+1b21$a3f0q)Uk&I11"8BA!!q3HMERNl'(QR(X%
+iRbi-h`KH!S2r(LcX&cM!b@$`heR#i$rY,4RXDa`HiV"1irL(#S-6caC'RIqd-(+
+a@GK@$Jm6pN`1j`MMhHF+qh81j`Rl!`i2&`B22F*KFde,R#m-6U6H`FJhY3E'qp%
+bB*Gb)1GJcqG`S6#YNA*9BH4B*0jFUr[(#2Yh$[#frZDC(VKB'(a(2X'SHG&[mbb
+2D"Q`bcJm6YM,1$aHf$XiA#)-[L%rB&rPm!5(c6I0*ZTZ[VhlLF,JBMJ0$&li1@(
+NQ[H#`6'L31HMCA2R#H2Ge$[Blh0iQV!rim#GB0rNJ1l6h`'Qaki3KUjiKM#iN[S
+&Jbr3&'$NlNTKe249`Yl*!Ii(q`+(j`Ml&`l2GGJ#dfES2M"U#am6$0jkRM"U$`i
+(ibjU'Hak$Lm3pM%1,a6f&aaHj,#&TQPI,!`0"[H#S41S%6"iN!!k"D2Hb#(B,h(
+i4@'r`3%Fl)XFIPRBIh"iZF-10$fTA8D$SCI31f$`1jS,$!j#8i04*km@KLkj9YM
+[FEK1f*pcH)h$lQ,Fm&TKF2rVK&%hVaI'@hN,'&cj"Q(`!PS$M(T!Ai1pK30e"2C
+"$R!Mf*pbH,1`Iq@!,Y$I)Y2Eh!Q'*RZV-26Bfi6"rI!j'2PqZc"b!"H"88[`!aJ
+ak%%`Z2p'B@L1Gc[X)02Fla&'lUP(-,3VfKN-cBDH"81Ii&H!`F@r,3b1H*m`mSX
+'"2Xi"r3A'(VV!`jEE"i(rJ-B@ThD")-,U4F`h[SKBA$kKi94Cam44QfJ0F#)KiI
+"i#Ui5hm(QlBM$f$i#jm3KUlqT$"iN4S"3fqJ)m$J`8m,SjlaCm"iffH&iG1JmI9
+Rl*(2#d0R`-pJD$Mi$3bGL4F"KZC"Ni,"dAmX$,li%f(N&0i!qfX1H$Ak1p6m)l3
+9'2i&ZJ--(3krJD%K[b)-EB-Q!S-RU"%`Z!BI"ScD)*pJe#YFSEr$c)ILl@$`rGH
+&8DrF!iC@`fF!3rrmR6#i'lm&$1j!Hi*4!rmS$*hd$BFY-6m!(!a[j9[#d)AI&JE
+rrC-`p!pq!KKeJ`i#)jG`,"K[rMH(GGRld+&JF$aD!``[!1m,$)dU[QBAZXKCIPh
+`MA-fZq!!*Iq[Le`VqAp,mH#-A`HQr5!PrfmT[SD5rlF8RDcNrbe&MbRjIdZT45A
+rEbPmT16r,5AA5[jI0hkFN[rAM6qKj2peSe'8r,pZY,H5rpH0YP2brlVK-#ArVaX
+19I,rZUPR*Iq[KcFVqAmpq!&+rPm2[+,Nrr93kdVqA`mk@FRrkd(M+IPr2@J2*Iq
+["dj8m[pki!)PrkqAh#RjIlei2dVqAbqD3mRrkd8(+2Pr[@JGihq#D9j4m[pkd5G
++rPm[[+,Nrr95fdVqAamj8[,rqUaZa2rV`d[526m@dpk(N[rAKeCAm[rkd)T+rPm
+IHNE*rqZ$6jAm[hlUA-RrkbFA5[jI2jTBbIrVakp4m[rki@-PrkrIkNEm[hjdQT,
+reipQ82,rqZ&(*Ip[J"T@m[m'M'2%raZ`A)Vr0i$[Sq6r$H![+2Pr!qKE*Ip[!(f
+Pj2m0S!H8r,pPF*!!mCE"G2dSqAr,L&IbrjEKCbRjIm[3CdVqhc,dQC,rYmab+Il
+I-UY,mImS95ArEj!!'PEbr`D0Bm6r'm628[,r"Leri[m0@Lf+rcH)KP6br`E4+NV
+qhj!!mBViId28YT,r0f3F,2lIN!$9Z[Kr3rJX5[lI%"k"N[mhC"`TrYq3!2'@q(r
+,i8FPrfmjYDVNrbh(me$brjEM54P2(NcV5#ArElPa[2Krbp'@5[lIFV50N[mh$#m
+VqAr$m)k5rcG-c5MjIm1FPIbrBE`L*Ip['%p(bImE0ViArfmBlDhNr`fM-jAm[f(
+dNT,r0f*j&Ip[a,KIr,m4ic$ardE)YC,r0i*18[,r4[#jP2br%E`B*Ip[""p"bIm
+E-6i6rfr%q%cm[e(dLC,r0fV[&[p[&-ib[3aJZMk9r,p4hUrNrihDZmAr'mAV8I,
+r4ZhGi[q0SSH9r,maY*D5rcH'(P$brmC-2iRr0dDZPIbr-I`q*Ip[$,p*bImEXhS
+5rfm-rDcNrkf`(N,areBBpiMrY`)18[,r9T!!4bArE`AHLC,rY`,r4FRr@f%e+[l
+I#[5UN[ph1$V0G0Vj@6jm4Uhl1Z%@)i("Rk*1QUHU'qGTeSP$ej'fF)f'$dqkAmP
+q54Y6EPpkXmbI`HHVPfKKBH$8rP*j89!F#Z+H(JQFk-[kc0V5&LMA*p@D9qYAe$F
+90)2CjLAA3l6V@mbYSPA,Ac2[`C-[ZrG4idTm"A`'2!@m#V+$6i%riIU5m$2)$Gi
+%rJ(H!Ti#IS2i$pk#Ve!K1Km,&aI2+,DALdN5*+fV3LQ*`RGQCiqrZ1eNTdV2h&-
+Ij,Tp6raCC(mbAK0!X9BTKe'V're[l!%G2m,r-2r,q4rJIaRrJrchm*rR(cqbPrp
+$q*r,rd,q&r!rTMBfbSd5T$aG@FV66%P5VT3Tj8Mj8XU8SpK!hS22N!#$*60PU9R
+"raMr9!%15*lr3Ik(q&r1riK9K[l[iEqArlRmBqaSAYaHL)jFSka("%@98#&NRbS
+Jqe3MP8"PL"(Q@IJJbC@@)2fr&+PK"B9`[rX[#S4)'-MR8QF#jblTk@bYaF&TS54
+ZT8JiUBCU@E49YDaG8*Je@mj*mFlS+qL8L)9GcqVY,)aV6NF!h5+ipEJXZ!3SGSS
+I&i@1%aa[ZP(S3X'Y`FR"@F#"31QMr((,F1e`dh!b825i$*3$lK21'Xi0VJiZ$+i
+-MJe1#Ui*$JQZ%fSHJX!Ca*@%I&$[+(881Qi&cJ%CT"-'T`3h!&@2iNHj3c5SGP`
+lh%1RSR!3F"4`)h'JF+G`VL!Ph%GF0BJ-K`rR$5'*HiGVKd1(@iEEKXZ''iH$KeZ
+(#iJE#3(L!L)2B!HF90aA(&XF39a(FEK-jaTZ!Li#lJ'Z!@i"ML911FiVMLY1,T+
+&ML&F(K`Ah'!F@0`ih$PX1JJ9p`-(&MF,0pJT4T`Zh&L)$`FBT`rh$RF,p`UR#RF
+,G`P(#JF',R*GX6K@Z-8i3M!+EK!Z%!ie*)m,M@X'Td,i1&ELYNjp[-`(d2rY!IP
+d#A3@JRJim,[hY%02IFIbVHlTk8UP30[mVS9,5b[#)4YlFASIBVKT0`6e&qYPJZE
+hGRI,A*9U2D&hb6KhUIh4,Iep)d(C9%"q3@GA"kAIS[2"1VIUh+EcS6SICQINC[L
+LPLQ&U"l&8ERSGi@$F6%fLRF2QU0cqm5&V3F@"R69"B&a8BFZFGRSe(Q"cRI4Uh[
+YM*3A[%Ii`SR(YEh-mRY+N!"%Sh%TICh,j3'keEfbDq,@r%[YeN,k60ia%H612Yd
+jAqH$,VpmM-,-rTZNZ0X@SaDT3fU11U3'U6[H6Jl*'f9&59%a9!Z93T8i9dPjBE%
+FVT)RIHYhpUP*[CJH,[i2SCeG$f)[V"EVrhkqCE0Er,ViTP[(S$4G!L[9c5b&U&J
+p!Q-2I!*M*E`(qX-dSZ`H&h0GlZZjar-2EK[PJ+K'`(2ZZ$lkmR&MB`qV,5!NaMX
+5ja[lY-8cbkil[i$*G#[*'8q`j4h[#F#-aIAR@PBm!TmED9*iSHH'0(Cr2e+chS"
+2SI*&PRSpT(QKC@55PUaU&U2eabElpeZ$*M-6ZfSf5h3@BlNDeXAE0Tf+VKF%`pZ
+iD#i!ar["A0RlBMN&f1#Q+G(FF,Z"6mmbC[BYVim#c1Xef*HQ`aYMb1Sb1+J[V06
++J@q'hQJbFQbY9P+hXIf)Xc+4U5DYeT@[ECXc0(h"Dr#T#8(C*&!1KrT0+FDVq5B
+b`ZF'H[PR2Nf#KiS%eh5hrB8)GFckl54)F@pU`N##@P)b'kcFS9AkC,b#TC(Qq`S
+G9IElJ3Lemd#B!m,m$qCq-%Z$H5(-r@$H"h-qQ$I#2"(QI+!NQ1["2!rQH$#rJlN
+Gc1YJ6JIc1CL4`6`1jR!`Id22*@'H!A--Q&r!h!,Q)6$[J,N&c#YJ6J(c%TL(`*`
+#jK-`Pi"j"-`KB2i!F`HB0m#F!HB,-&H!H3,BYm`2-(-9k"#LkiY1*,U$k,kLfiF
+q%$V(k2M#'D#ML#ibZVVSfU)VLbiLZV,SHU,VL+i[h'XN,Te&VUX)Bi)Z*,UBk#b
+M`icZ)VU9k&+LBiaj)F`*B6i)Fd'B0m)m%HD#k-B@8i,-)p(c4N`a``rdHfZKM'C
+Q[JGc2CMR`4`2jRF`Yi0j(FcT-2-jQ1@KZmlS#kFIR$j`jKm`pi"j"m`jB(i#ma'
+BFm"m!qBD-&q"IRCQ+0$,cR`$jKS`ci!j"X`[B'i"m`UB8m"m!ZB5-)q!136-(f$
+Z!2-B62mlR9p4&alGBR5)dBe("aaGI(54d6d@GI24FdhE$l1GQ1R%,#IQb6#lLCP
+0c(YK4K1cQHJqBbi6mjLB`m6m*GG($'R5kH@D9qNqSk10VLmklHJBS9-0+8AI#je
+rG0YKG*)re`j+4aVGE(6%dB9'4aNGINl-d5e(ee[D6JV0-Sq)186-(f,Z%21-Q&I
+%h#(Q#6&RL*Nec$0LcK#cH*Mp`c`KjXX`2iLj3F`,BNi3mi'B#m3m)1B!-Ip(cdA
+#M'9Z!2-#Q"2!I"AQUM"2K6NUc'GKrJTc9*LI`Y`8jVF`ii!C,F`hB(i+Fe1BPm+
+F&1DM-"H&H5M-3@(q#A02Q(I#R"2QQc$AK(N[CLB#(B"dcp%Y5$FPhB4`,af8G&R
+5eBJ)drHElN2p"Y2C5"HNIS[TS-48eZmdAC4d-p+K5'FLABLZSe'rfA34dX9)Gk2
+1Nl'bG*k-UF6X,'CQ-5Z,Q8V-hQ+f&V0lQ"h&6#aQFc&lLjPBc#GLcK1cVjKja9`
+VCP`afiUC9XbbBSB9XkZB@B93C-B*-d-dIc&$JGN*c-TJ4JDc-CL*`D`0CQN`%i0
+C'-c!B"B(mb#BYm%X#'CK-!1$f4I-['$@"6-ZQ'h"6!YQ@6$$JYN9c+aJ9J8c+TM
+GBHC(d!9+ab-GS65Gd&P*Kb4GSR5mdZ0&TbAq)3iDRD"dQp+C5MFPhDGdU',,dHh
+U[$KR`H'md8e,KbZGTFlfSiZ8VP2A9HSCq,4B[pCTH#h9M@GP@rC+VACa+ehBa0C
+IV!j0Y-&Y+j8,a9*EVGCC$HZY*F-h"BN3(9N!36B-,JIF%Jj@H1li806!FTe9615
+(&dX"cmceGAB[@GU+-cYZk%5a2T+H6fr[(Dh@`dU3!)DYINYITprHZ%@RD+9KqN(
+bG*HNmGK,4LD5[V%89@TK13b'I'F"QrcD-SLkp3T*M[cLm9TTa+ijZR9'Dc*5(Rq
+XVS!YBDPB$k-Ulp9SP+U*!D'!'q2Jb35iYrBIkFqD[F+h5H@jVAkMlJ-ImiJXeLl
+hk#X,QD)+p$@Q#R4@0[5PL+dRrBDJF)i[6TXC6TmpG&4l)dc+6-2pM@J6Z'TV)R5
+*MAP%pAG@DP&Fpm&c*aA,j5LUNX`Y0H10YG2EMcikMG6JaQ*5+TC6p*4JPFGjG9,
+aPVCh,eL`p3dE1lE"mCZGal5U39Ip9&pbKbQ3!$!['Xm9HJDf`5ic!NTQ3-2AQq"
+6Zf#@S",1(Um%'`I,(0I9PD**C4AL(9TN-Kf48S-1%Ec)GlARbeh4Lm2V5BZG'K!
+5KG5*+4-E2,BM%cBUH&"%9cC8#%rZ-E4(-9V1,$q[8Vhf#@6*h)mfd1(k[H-h@f`
+J0M4FL,9NJ'J*ll0&%Pa"IPCCbX-VB$eZG9a1YJ*j!)r'JBiaZG'NeG"H-@'m-!e
+1#b)3"BDT,#HRr*c)+[XF$@a'3*bAP1+`9RF&%3B8hKcGiKiDaTKm2'VR69'@NNK
+6AD@3!++Z3GQY@BKZdaHY@HKU2Z,H03ZG+MJRULC41@K0kU1$L@Qq4h(rL9dGC`6
+9HSTcr83aV)+jqpFXh%i5J9"EXp#S5h+`VVm$8CAH@G4%pNDja)!&2SSj-ATDN!!
+3"mG+PSJ0E8q&b+@&E'eSQhKmjSkA!Y2FaIGEY*'pj+QQ!$HdZ3UN3$Hd5G#MrU"
+2c6FAVD""b`DJ+L4Jl@K5MbVqQi1ccScLSF4V+m&3@j&&fdDJTXedrflcqi*k2D`
+1*pk#N!$(6X!6"Pdc'TD(r)k9pELBH21$`G(Ki5$fhCdj*l,@TKUACaTBlpe`NA[
+)TKkHH&&3pMTlrHiD-N1hV9eXZY#Sf41#DSSG&bE@lR6KS$dKc#+3!&CVpL`l2&K
+0+Dr5)V2`GV,YAJ8@[bSX"5$h'P%mpr#lLYAKd5)pd%%4(1%%Yhl0HA%UpA-RC)N
+pbQmE'JSY5SY+%U[Td$rJhRJ"[dl-kjV-f2Y&66k,Q`+dpYc8%'fkk3TMQ(`!R`p
+K`-UYmbDDDGdH@3cf#Zc@N6e@aVKkI6j24-0iar-c#hdURbqQdAP+d%`A"j6LfSh
+-4c-Gr@#&*R3G-(qm`Ae2%hS3d%+[M4d2i)'Td-8YlGhc1eVmP[PYr@dF0hDfpfX
+BYLrPmm8-G-Qd[brLme1-&bJ(qAmqhm6SQhejr'%bY4-k88`2jh5X$1Z,k)fNXq#
+@,2S)4KAFmY'R[IR2)f9m-jmEX-2r`N*3Mk-cJhK&iYYFR[kq9rIk4q9QA(G9EXC
+TmjCdqlf,4V`Mr$ilV@NjI#i$!(6(mm"Y8`jIl!hl5paQ!e,V,BGr4mU`C90qlM8
+rp0DX@62qVqllp$*[bjBYfrjIbY$1#(rpq[92h((EIb@ReE[5YrXIrQrFX+&*`M[
+Gjir)1rc3L285m-2ZYmGpjD[85p9I#*25pTEc930KYGGD[D4$+`QqC2DX4Vp@&LR
+0@PQJ2,FB+IfppDG+G0#qP85[Sj2Vd6Z[2$kUlRbM%3$ejdX05*GF%TcfhLA4F5G
+H%KThhL@K8E0E&KQh[QAKeGU+fZ++q8Ykr#V"pFU5pVTLVB6GIeRN91V#cX!XN!!
+q`A*8V9UM(X)N01iS6%,6,XNLAm3pNp%-JS3JimJL2mDa%6f@!U8-5R%ZhmRlLpa
+B#3i+1!i@CSaM$$('%6%[9M-[&"E(TU`B"mHN'%I'R"K(4T4B#SaTT44G+DH)$j2
+%*A4BMUd9Nj!!B9)J)4H@!TXj#jQ`&!F4CN(9kSaS-)k-@6#16%N`SBDB!ph-J)6
+rJUJLp`9a%Hr&3C,`1-EPYI$@)YHPJ8&T"S(#F3&Zq#e!BfiVC94S+)K,15d)60U
+YA96-C8&8a'0a8-`(F@4D'K&h&C+6m&Ba,LZ-`M3)5AV)9A&3,5mK4m8am&-58+U
+ZL*H#U*L6JUL8M`TP((042%dP)D3NY-K+5A"%69PNd&kH"VS%&j03C+TUG&#95E4
+`9K*NL#X*LGQV9J2#2NP`bQ0*G%aQ5@M-D%PSdTkI4KDEpG2`DQe&,&I-Ad*ePH"
+kC3RT&@XPC,iXFLTe)3GQJ4"K1DT@V4%P*U(&'3K4D%U14Ek)'G,aJBPD(bBl2C0
+iJ%2C&p1YeBqK6kXFqD$#MLJlR-*fC*I&Crc5c'L3!)GQJIUGF9$fc%!Fa%p-Y*i
+m,`R56`X#XQFPC4@rV8c(mX"bT(jP%T8p0Chh8f,UdJ8a@CHL8iBSAC!!N!"%+6c
+Qqe*d42R9i*JkUcHim5@9f'5)4j2qkr'"!XMLV3JSaF@PABU1UVX@2*N`9rkP`*X
+C)0@4M3ZDkBlN669i4jQ')UFDLmkT"6E+)9BlTHKBm*5LL`+q&"pTq'T`)j9&ZdQ
+')e8X(aHDQMiZVZaCZ0#5JqB#8k[)a4A0SLJXY@@Lf))a%d99dPXbMC,)b$B+)J2
+Mb%@NHX[&&49A(&C14UM&A%K9j,RJXKPB$+fNVqKc4@'0NNUmVLK+h+ii*%YXkRL
+jZ*S2DfCD0Iec'ejcd'hXT)PV`qZY$$DijVrEf+S$A`LYqGQ&q064,N5@l'XTJES
+6(d9RAVa%&pei'eAc(QaXeAd)3qY*+RN60Qc+",%A6(CYa1'0p&BE#`UKdk9BD5i
+S4-EY"@&B,IQe&J-E@cHZDZb@aYFpb"UrTI%0ElM-F'P`cF@XFecPJT)GQ,&FU4B
+DEQD9jdVKUD0CCVSdZ'C8PEQZ'Kcl@!ff5fpSfZ%e[L[&9chD1Z09,UJACZE8CU4
+AMUfiY@ADbfB9lR$dbZ4AMYrZkP8SX(a"dpR,L,!HhV505R4BMkik4&95R,bKCR[
+&e&L1E$Tp'8%f`UH59rAl'NaC[Q5ljeIMbmS9$Gq[cTVe1jSPA(Ar-[UXKGFG`$+
+*&LGc6MNM0VjZMGMJTMGLia[QL)fZZb-fZ'D2&',VSVa`38Q9&d)cr5fed,")S[#
+bL5$KU8PL`qSZL3fZf54KE#09Q9&LikDG%RY$dbU*ibI6A$0,#V(E#l0UPa4#%lm
+NM+YA3GdaXF%0bk6'Hk8,'UC*MIP+&dcD*QAZ+dAAMC-kqe9[b"4kaRpC88bD*e8
+'c1*,pNQC!d[4G31Pc)+ek-4#DI"JkBST%kA'K0N&04ZPcSA9'aTP@RC5-MUX"&H
+pP$)KPNFRlh46bV4BZ@#(Re)KamS08ij+4T'0q#P"AL,+4RK0GeITXRP&h9L)5E-
+51Z@XC03j'6qG`TUjdZ$3bLdll*8DNeE[Q$4BkRcDZ'5US'XH5dDXpIL'be+Qeh$
+JH,AVbF@@fTjFB+A[bF9QM8mZXY6jj!,6eUFSVY5S%`@(R6T4@05A%q3jDhp+3Z-
+'SL#dd!$P3NSG8#i`EB'+ibSTLCUJA%bY#mT&9pUJLV(90+D08&(FC)%9@k'L-0F
+,&FH8-ecUKR+"fefrG$*p'PqB4Cp'dYQhEQ8TF'2FTqJ`I@*9"`E2V%4ARaUP1(e
+Lc*("dk+SkT25&&F'h+GhC!2[da[#!IKTD(8JIU8#+TQSh*0PSh*,Q*&+H$8V8I&
+8XK(&CeQ)SX2A4f%HP18!@*!!9"qlmSe0Kq*aKLURlEIe2@ETdF)pE[9qZDK`$l[
+QV0je6r!iI3rlk"`cH3qljXbE[)GpG"C-hX0q1i[qQZr`([DT@E,V2E+2VVQ(!H[
+VXhYU%aje&,[BG%bqQheYmT2hX"01jq3pl*DcEGFp8AlB2@IVj$f-,6jrmKif%pJ
+bH3mlm6cLZAX+K+5A3Y,I$+pjPHp"'$d@(RFci0ddkFGG(feJpkd$Y(1cTqel!U6
+JGPYf[(!TLerUAE-fD5Gj6p[h40YMYFJ,2`X[M)q0VG%qqckf6l6#V"HTldQmb2D
+R[8Kp$elIFE[Z#4kRlm(V@cGj$hZ9(Gqi*rqd*kM[`41Fp4KPiFGCle$IJpFhkah
+UHpJ4EGBle2IJ#FjkKrSH[,jC$drI`rjTXjkJ[JG2F0CMe2IJ#FjkPV)!Z0rE1r3
+-h-1Zc2SiUAqF,Y6hQ!@%Eli(Mf*5*iAhi(h1REa(rc9l@F*ld-'c[6Ek(RBcR1a
+j#HpKhm1ll,VR+42hQ*d3EmiM26lhHDlAaZ`JVAILZrNHGSkCeF(k(RU"'[SaHJp
+l+6EdD(32ZbXfG'Cd$lXY0R4`G!pl,ckL`92eHr`V[1aY[XIT$E5D%e6S(5I#m1+
+F"mmZQDjrKrdSh3lHl(AUqMIB%p@Y@-XqU'K22RDPG([IX0FSHi6bXE-SZT12286
+4PRcXPqQfF'@A62T!qGMrdfh+`KkID%FqGQYP6e%qGN0&+j*HmXMHSQ$dIU)0`I"
+S`F(`"p'#B1c-5Il"f0f5R8A"f1d5I3I'$UaS1$$f9@@(86"f`m4,"Q-I8raL-(E
+Sj,eJl-3TbhBTbbApRQ$X#)[2#mBHX(LjB1bdLPp,HC*,2&N`FN%[*KMlIXUD5#f
+fQbBq+aKE1-RQCbffRbYq+4JlZj*c-2BeCIp3-,C9N5hq@k`ILhe%`DK4pK)&Brp
+2pK)&)jIX*`V'6UEX+3V'9LAN")apCYP&&)cpADN[k!@1J%r!U%0f&!8M[q`U#XD
+ZR1`U#XDZRZ`X#XC1X1a"#XC1XZcA#FD58Y`*"XH`CbFBHkl+$NHYPM2CZl29pN@
+eDcARpV&pEGRM%i`pDf9[k$EM0AEr"+0ZU5m`mJ8rJ,%h+,Z#JV(p&,Z%JX%6a)+
+a)bXlKi+aZb[j"'0h9hB3"B2rf%88M0e+f888M"eBl@BaZ4IC$UZbIhkEm36V9i(
+Th@AY,Y#DSZ&VmJ!'9mTDrh1-QeM'#J`ZB$p4-1U+RDc!b+2Cc8TMl'0V&VI5'$[
+IJS1a2bRlfB+aPbalfS+45fS6$*jJEeX`q*[pEm(Bca@p!!CR`dY15V!l,KJmcZk
+iB15*hA$"U(2C3V[GmN4pJC%,mJ-'(l#c,KKl1V'l,KKmKMB"BaGDlJ&Mee9ff38
+M6qbd#dDHi%N`G[U9VH[E69[)2MejdcEXX3Z'CT!!hB$bPM-C!*!!0ck5lB2b9Y[
+S"6$UK$eh`6LMPF$Bk9E@$-lElVEX"`X'(m!KB2#"l##90kk9jHMcYNF`(!M'[Vb
+bX&H(D5eU&JcG)P[TG*K1N!"qP!lMA23,',c'IUYJm!MD$BbDC,GI-(,0MVpJ[%(
+k0cYXVecfr39$0m"eB1bCbaklB1`-bceJE*K(cX()+h`#aYkfl-%,aMl&l--,4Ym
+Ah!j'MYQ29hqGPQ2fi`9$6l!R,aLDLRej`G"#l-X,4Ph#9f"`+hS0$)iMEck%6bZ
+,dKU2hY8[AE(6`r,T,J%a0Aa[RpGQ+I9S-rYJ&pE`HhbBM$bjP,SIB(20R9qdP,V
+eKLq6DrjNeI06j)Sr9Flkdq5H2efZq`YeIT(1,pEj*6TI,QIm#MR`ce$F-h9qPYj
+lTI#Vp0jRkrh2dAZIUrGIVI22blPrRZkp4[FqAflp#p,hqLQfd2-FE05QYT!!pVP
+0S,CQkepNqiD$f+hCA!q,2TZYf8)mq%raQELGmC@FcVkjJNIIG"jQFY9ibdaFqUj
+EhR&lrVlr[NEqGpeAHAIMRY[VjTDDEEl$,lICTVrJfjp!8+YiKUeTAqjFI,"$QXI
+e*Y"$EKTQiB09`cBBqB*5dcCL326+6!'Qm@NqZ,$B[)d,LR``ar("U[U+fDdcQVI
+*"-F(fMmiD"XI'*RJHH1$aG[ii**b10L866H'I(!`h6&UcS4VI@UGrCShad1-3RK
+BA)c2QP2Skr,Edj-HNl#U,aU05d(6-Fi)rB01a`GQ)-DTSl9D&0IR,!U+3d'F0&-
+6#Si2Y0qXRZ)$U``Z#*US-8,)"bhrd)YV`QSc-hB)rB2fIrS(HS@F#k2UR*l4`A*
+BmMZVTI,S8*-`%4(b3IjIIT+2M!`52ZK`I[-jE)qdZGkmV88Tj)01p4p-4(&JZfG
+YpBpVRGPk3V-eCJMjB#jkFGk8j`B`GVR*8RGqM$YGADXe!4K[acXQ#PfD8Y[mY1$
+YlkbBBdTQDhG-p(4THZfqGjcHVZQiGqEeP,lHA5qmp4fE1TEfY`k1H+8NQH(PrRL
+qGf#[TXIUVf041JPd6E&FMU+U[bJS0kCQU0mpP',UbN[@fGQm3fE30Vk*!GrYK$C
+C(HiGKf[DmSrIX5-Krr11(F0#r[FGfaqbpahRVDajdeI0Fr9kr6dMB`r2baZqq'j
+QJepkMFFDD,Cq6dKVHA1b`LIHeZ[A0kHcdF`lC1l[$r*K4LAcMTfc923lC)ZfCQ,
+Y1r3#Fjk!Gjbc*l-cH@AFVh[(0"2YIFI11E@ZA+FrmikYhXKZRVCV`H95SRDK+8q
+IdGkp9mG`ar(2+TPcSj)Q-rrIKrki1)QI93flhl&eT+aTf-e2VeFhUmqfeNHE",b
+$Ej`Lq9&H'9XZLM[FbUriq6m8YmjXlBlE&(Ic(Gi!aEhKT-AD3k$j6I3-l-f(jd1
+j6fEe0Z8qqiipbRhf(AZ8qdh[f+hFCpiaSpcGR#arJ(+[jq0lbVfCf&Z9qm`lpLM
+hqM1qUpaRhV&6ZHYhc)ThUpc2'rjIhAXr&,IH6,9faff+Hr)GI*i!aDdAT$rbH(p
+LdrRLerQmBY,[f-i-he&-XqrBSh4ZHXGZT62cMZm`HIdGhfAbQAIX*$hp$V-Br`a
+$M$1)BcSqG#5Q+N5rip'$fUkLpQdBA,lh(Cf&CrGJk#J8fV9EajhIIc1HhS@85Vc
+T(9[qTKAe1lD`1QPKNG&k4CmED*q0IARGTaN#,Ee6Nqppa`aclUbcS$kf032BUA0
+H1ZjcLQ-@T1lM$8IHJDXdk8*9[S),pE*e$II4#r#1YH8NmD!B65#6lp"F6ejcZhe
+biaTk!I*Kh$i[`$ZFfcIpMVeZhrRe5YRV@Z[HF3@EQl6XQem+IXMT-r*6hl(*qeP
+qeJ9rk2TUhYpXQjcS68d1[*ripqCRRA0qIZTjk6BcjKf2Y#TUjQ0ARBR,FN(YQAV
+&BM&%0281jYAQE+c[[VSh,SXAS&k0bq)&H!HIT61p4Fi2mfVHSFXMpprk1$QUDU1
+B4J)R#Me0!2BbBAlQM26)6@dqdIQ`Y[[LCl`TqlNDQrk-K[%#[11NjI&TVIcr++q
+S8Rp&cq,d9p&#k9I33RI@4d%,h9Q[rS"H-D1&p[%($(aQ@0[EJE-VVri*jjT200A
+rjp@mBqFNq9h[Q'RabKhDr+h(DrVEUh6m5MeHqcl28SrABfTle1P-ARd%AGkk9fc
+Ij`(T&DX6b(Gla5DCfKI3+aDZ&9ql*0pm3VPl+T5T&i!jr3T-YHZEh96-rq(TH&`
+Qj8ar(J)[aJmd+@IIjeQDP,2ALkNpa%X`F@Il*a0hYRrqVBNl-qhrZ5R0+[VrpbJ
+irmBFJHmSVrSl[UHmQJQj9AR0[--l-4pKVr+U2F3[-'I"[Z1m2AA')r4-8LX+TZl
+a!bJ#m`k[a0b(F21JMR3cL2hpKmf$&MfjHC!!MqfEh%6*EKlNHpY%b6HhHC!!E!k
+bAm@aHC!!hYPiGNDQpr4EZk!!q9hVGV[ADphVlHjp+`AJMCYaX*pUYSA@h"FU%&Y
+SHGliS,+&PSIYblE3fJp!f$V+RlDP@ZF,'BSYY+EQ+AP22leePYipkp54S0adCIX
+XphRAYp!k+4[UdV%qaDVE,EcSLK46IC[AfYR$0&[[icHZhVV6M$)mfN+V0jFEhfP
+'EETT#[%hj$EG[#ahb8%c'qr`I'fKPI&dG3ZY)NrlcEI3mL)SGcr3KJ,6bTe2P,Z
+ki1"P63#8ZepU#kepGEpIbl#&eZEU,EK+Nbj8j5Zi8&pHfh!I[3$[b,E3bYj4hED
+Tk)SCep!,N!!2irCj!Gi4E+%epBkpETrC[1M02GN@@ZV#h(I"p4CDDZh'QVFqGqh
+G0heqG@lGL8BQHJZYK9ALRjblpXljZE8rjEjSmk*Q2RE9@ED&9[D1kKCDaAGN@fM
+GN3rMXRJ"kY@i,&k!Gr!P@fMGN9IcMRJ,V6[HXCpSXi9@#fR48eR0cr[b,@jTljl
+IdH+hc'rVEq1iXE1p[k@j5J&iB6M!%r2jCeM!AmZ,2V[[B1B2m[RRP*-qmaH4JJC
+p0"cKje@DrJc5p2pT)CqNP[EaZK5j8mlh['1m5P,Id3[HIJ3V@ckCZ1,mPD1Bjpp
+ZF4GYMe2(FaJPlS,YFIFjKL8!j(fY$f4a,er+!#&jAa6AYjJ@8(PI&$IR-!jcDc&
+cX9Xf[XBHE10DhXKKb-AGD0-+@T52EfIhV9`#eUVlk(5paX8pqe0JHK8S%rG0$LH
+jZ([rb4iXlkC2pHiZlNSEV-#@a-3aH96ZZriVB*fU'jUb&lZi*3H$X8mack3M@[,
+a&GYjS%pjHQD@[hXH#bBcqY6EX[ZZ[KGX`-@ehTEPla0hJ#h6IESZA6iZHKb-'Z(
+G0#p,rUkq#QaBF6GNphhm&M"T,'el*-[I*BD0ZELZ6eTZ,U`RaJG5BhbA`arYmbV
+eG%j2'0HpN!#$MYX5EBqEBqmqN!$li)N`VX9U3[*pejq!(D1mXZ@,e-IEhJ0'[I1
+1"GPpChd#l&JAG`rV&jIkH,jaK&PG@0rhJ5cI(lS5E,AULIG)IEc+FL*cH9U0hq5
+qcli6E*hLhT(9aaZXdjXmNkI4,"qIlJ3lbm80fp4FbGq#G@!rT6U$Eq@qDeV"(U,
+m[5I,hcEMPBIU(F4*2VUY[Xp@hA#3!2ce@4e,*rF)fffjqjjNr#'GiL-rb2,h*EJ
+fTq&H9hf$1PjIU3r[i114Ar('ZeCU#E84a3hb(V@kTbZTHrQ$V)BZGA&,M#G%"d6
+hGIf#`bZSHcVI`lTIbVYcbm3V`VPmem#M!Dq%FGh[RAK(&(IN"+q%F6f[jh#dH#L
+-'lQa8CHYFbm#)fr8'H0-T5j[-1kleX8pd1k`GGPkLR%6fN&rpcFp)(Af6Y-FVh&
+aiarJX,'5l15jZjNHF(&4RY!ZMPrEXlLAd+5Ihdpe%-Eef2[HUMS)ihU0ee`GK((
+,dA1jZDU$-'l#*PqlI)4a5daRG+9jlCMk8@SGAD1r'p%ahEpfFIG#NkLT,Zkklh(
+pr$3ZIr9Ma#ebFCp$jaejNEZ[l4A%(H6L"Vi&PRIhIH-8-,35lriUKm2FIDGpN6K
+pTq&,H0$@CFGKD$Ee5Yf(9P[JiZkhJEK$AGb+3m(DhIXqmeN`2HRGm0"mU`0lhq&
+h%EG%FHb5B0q4Ir`KiVTFh1'@ibqiqp4R`25lM*j!4dQqRiIf9DC2NA@Fapep$rX
+FF6dZlJCQG,3jRXK[SFl8Ur31G(1ELhYh,h'L#jIGD9aZhh%kR+'N#eI#alRC,Zl
+i!iJ6A6Ki-SF$A&cR'F5*,Zbb1-[hqIY[*djdBCG0DT!!HMUC@PI'$r!%ifrHlq,
+QSF28DehF%Ge@3rBG(riDQ1M))ffHifYGh&[3B8SkXXX'9pJkb$q'9MEk5"rIm5"
+dD1ZQSr0X!Tbq4!F)hh6!LmVT-'8jF(ac00T*14fQTP'lMQmq$cFU0iG)YIiSijY
+HhU#Ncqk'(R0mmd@cU@9hL0kh0[JQr`!k6jPf)aAk(Diq9U,9P'NPH*Im#YqXXcc
+*&-B6m$Q%EcS1J918jYreEXcijYk@l`R&`EIbMP8I)qjNk3Ni`2*0rL2'SlVZc(f
+2crMQdqKATH%GieXb[PQ%PP1@6rfp(Ai4[ZNidAK)[m[`$IN3[[N'QP2e53paVq@
+Er+eS(GA[iSkb1KDq1GIi!bm&R85mm-f+$a%(cj!!*rMBmNh(kXZ)1edklpPC2Dd
+R"`TG`(IA4aYmNlmHEDI3!R`2X(S5(I"!0+[a3j!!ZZKEbcFGCE5('YICm*I8`4(
+i(%SkHq+HM'm@QMi3R6eSHY(b6Ilf,a&RGEDb)AM#0rRISl'8dpRUA2)PI00a#$@
+Xh,"YG3(qK2"0rTYI)%$'BDmhlK'qqG`[l@%fEXLdZr$0ZfmJ6R4jRqNPUBrchbA
+I!cj'r`K2('Bk5I6k"[5dijZ6H*25*2aaibIKQc'i8QRHk[!(XhFFJrC9QY"c#RV
+&mFhVb+Q5IcImmBa[[[CYiQ5bbp)2Claq,Ck0dPB!Tl&'Qp-hhrSYFH)EYTh6i*Z
+13dfhb85a%dhhL,ijbh)TIQ+[k@c,0rR,,+rS9I3pHX2b6Ai62Tc5c+2KUc0pmdV
+HUNkA6X)lNAUkZp@Cq*ZRQNBA[[NNRU$5**ihh*IacIRN9'R`cK[KB+G[[[PpiQ5
+5d)QACRA`((`1KHELHbYFi[KQ,MP9aY[kq#kmY*EpdVMF!A#Y@ZRL2[BkI*)MA0`
+TF+L5Mc*NRShPTe`!KbNhe%hG!NqiGkc%%e*S#Z,HLejelhMS&343Gk3)IT&hh0I
+d%ek@2Vi2IH,Hm5UV!mdja(d!2mqpil2fMXY86ar-hR'9m3Hq)[a+r2`+Xb%,hJ(
+IQma2q95PZ'0qTlS9hD#FAj@l$Up#R5HGXC!!`bBApfSdZRUhLpZ)&XpGkZ+10Cf
+("kL2lm&6890Fh0IH!+Ec4Yb(M-2NI9Y0cehZhV'*ZV6[ZfcX%E`$GD',ZplU80j
+hlFh%i5rTiiIa4GclPTM1`kI9airJiERhhGGbq5cTHh`&bGp4q,,+H%`I2iL(iGk
+h$8fThZrL2QUEDFMl6[S0'2SSphm!G`)!!!%!N!0AHJ!!9RS!!!*kEf`l$3N*CR-
+ZF'&b583J25!UC'Pb1`d*#8jKE@9$Eh!50#i`)&0PE'BY4AKdFQ&MG'pbFJ)!N!0
+"8&"-2j!%!!""8&"-2j!%)!$rN!3!N"+Xp[d9!*!'@I3*#3d*#5TfEf`J25"QFbj
+f8Q9Q6R9Y1`d*#5TNDA)J25"QFbj`BA**4$X0#3P1B@eP3fp`H5KQFbjZB@eP,'j
+KE@8T1`d*#3d*#5ThBA0'EfaNCA*"E'PKFb!p)'Pc4QpXC'9b1`d*#3d*#A*PG(9
+bEL"bCA0eE(3l$3Pp$3N0#5Th!!!#L%&%3e)$!!-S$9d,iCE!0Qd2b6Z[IH9Ehp5
+Ue9CEkEAkC*9FPqJ9+UeD(CpNDpA*@5)i3-Q)R'5k55HY1-"hcYF,r!1f#E,M5[U
+MC@C+a!jlH+aC@B3V[%icXLX@fR,DNr0`SMITBMNC8cDC6Z*'D,'pk"KZJpfp@@Y
+N0fce%Me#6X-#1je1j30JAcZ3!!lQ+MM%ppFL13@,l3`kNiq#Xra)29V1aZ2p8$e
+-6X$$l9JkMKI#GRp!ljGYH)IYT+em*paYXfN1h`AcrADp66EM8cD0CR)dR2%&qSc
+-`1RqK$iT8r%HQd[cZ"kHY4l823UZ0RLjRL2VFBIRp%(CJ[IC4YV%pd*IreFIPcl
+iY(@PE[`Sl'hl8'rq(IEcAE4D$X)KIUZ@bRMmhLE5"2iFIVC40*TrJM&HU6r)*&a
+MlDNGr`LGr#[p@MVJ5&qYUk3cVV@a0)lA34GE5X[i1hM(rpDrj$emhrr6A[)FrQm
+Id,[m$hcUYIUPI),If"+DaGp#2rZB2Z-'k1NVp3[TL1IC#r3+A`5[qX9kSEb-Jra
+'[88'i`hf%Ah)em-)[dU[PQ&iV8fKSA`Gh'3$D6KI!rhpCLf6!ILDAk+AbZYiZEe
+*Er%9F,kp5-rcZI#5Ak#Ab4Y4q[3RrF'2`GYqT6iN[q#[rV!q)VmK'T-`3"4Cj"V
+@eLC8PNBMkcLAbb96p2B`aCDaP&9&U@-[5hXk6QI5UA49HN(DD[*0fCDk4QmZA"b
+hC9*QKDeY8Bq9e54!3"2#XdXQSE`ZSEJ`S5P3&#J)Y)Ab`R#S$SIkd&B5D'e1lV3
+f*HZ5PSJk56D-!bP*#TP!2[bb3,EL8QK,"6+"jSS2"5`IY[4X3MD3!)N6iRa#69)
+'!*!$,d&%3e)$!!"@$8X$RHJ'ApX&YA!,!-!YrkSA!!$3@iYDH!'Kj[m$@)8Habq
+AC[SF!*!'*lp"4%05!`!rmJp9$AC'!4!aiqGMIjYcMV()-TE9'U0IM"@MlZFhK%X
+0M$Q(cV&kM"'6f&`D[K#b%A#b!`R2br85Na)aK#$0iD9FNLE'pSA*#4a,Z66Aaq2
+4A%Sje-FeP%XTKkD%BiLe3#RJ!((Ipr[[lr[QCJkplE[hrI`('k5R+%%3*%%52*%
+"I!+Dpk1@k$81XCK2KMmBkh)ImdEZFT0$AjL%ESQ#Q[A"f+5lUd1X8%03`%TJXZ0
+G8#P3K%U%iPa9heDKjeeZFCT8f%RNL[a%6Xl(Kq@H#NEkblD+H`kJd#!9&R6m19E
+#N!"@#`8L1LLBQkT*bA,EbTI8INfUkU#1afYrX(Z[U%d8m(MZKZ%5'MEBDhm3C0i
+JK)p5BU8akQ"S@![63Pml6kiTHVpAihr2j32Lf+fkG@ZlZ2qbbrfRcfYiFSC,bG5
+Y8d[GbakZ`C*!PqQbISC,9cUh[kq,Qp3XJ@&0&dqqhqhq(hIamEjq6RGIeV6GIBq
+pj1,qPhCcdd[G20RajrS$`9R3X$UYkqliY"IALYEQ6KjA1h*[iHHed5N0*R[[V!i
+NhcQ&U81EjPqfE9TdZ9HB2YR2ihfK63fRECZq21eb,hd!JqT8iV&%8@1[1[8Q"(U
+mB&C#-Z4L!dNfeDUT$R9UF**EDh0$am8D%VjcI3Bl(HC)AEh(&bT[R-U`MaT@Jph
+SYKeETNk06r)iPN6GqD*Sf))1(1bLli`H9iDUf$lE2P[YrafP#h@UQh(Y)eAQU!L
+"dYE4-rA'5f-6,@jF-Qe%FM+L3VJ20NjCD2[LY!M#e4HMHX0pp*'1e"CVaQ'`l%a
+NBCU5(l-J1fM"hCpL`8$3JXIql3+aM&$4Hci))BmbK)3b8IXe[G)kX8DXmGE#9&M
++5leI#B-maQ2H"K3Xh1,p5L*fe2eSD8VFMHJJ,'U-N9kHY$IM&A@14GXKc1b`Hlk
+a#dE#'"h5B*,%,2(+4)Bb"DC5Ha+[)UFl$+3fh58MPq2TKVX`,*Ffh(9Z5fmH6'*
+kV36NJGU[XHe(eBR)*"IjkhBV*CH*BS&Sc6Z#@4$*j$*aR`5JE-J6C6Ipj21m0$X
+JhJ(&3pi1JdT0KrXQ%UFk2KPJ`VB,"cZ,'"TZmZ(lPR9cGr$a4m0KCXk*TNYKFSp
+YHhhhRQ0,4l28MU&5Yd'*'I5aqTrc4RhL6FjhV[TCfdHr%IGq0&dXBm`2Mikc4%,
++@0*N,QQb[)%-qeJ+-%*D`qVDc`fVFC523&LG+9bj#Y#X0$9Ui43UG*CT1*UeZ$-
+F$A'rYYPZ6BHdUY$*Y4Tq)8-aGJ&aY'VER[)M13EffUT"Na`H$cDX3T@*GS,X08Z
+"CB0,'qRUekSEZVA*YMhDbLkheN3,(9Yc&$4Q-14,NpdlLXR5TB0q3RQIrN+'IDR
+`b5[G0QfPm0'CPb*#")P`%e"+4ZV'kRakDTVYa6dZ0+%Prkm&#-&!I"N-DG'NMce
+bMIHiTM)8,Si3q[-$J5YPmE@i8MYGLDH6Jc$mLKi'SNEij!B')[YiVD9UbRj-DG0
+1[I'i'*K'!T*0Y$1`(BE&`Z(N'&8F"N6`Z(-S%G,''V#KcX`0"D*)KeKq*2GM*E+
+K9rK`Q(+8`"Df2d,Ej0`Af*!!0%-d""9Lh'Vi%L%'#LqX9cXFhmY4*SpAS4&M)`E
++)JL)i2AD"&H4I)(XkdYI1mk(IPL!6"35#,a@i(MZe8,H"d,$!8*94Z-S*BVF#+$
+U[H0)p#,f2X1rJ$J5[Le6M#a%jGIcYS$XYr,iXcbdKC(G3Q$fUGmKST5B)N(k2K)
+L(V39,*!!!15+8"J+5V)4'G%bL'$'UYdM6b%m8d5NaGX18lL%KTpj[R"peE$L++d
+DlUXDpMRmUH(c!4,V(899``%5BijNeA#!4"5Ek8#QP,C5hSp-d"F@e2PQ3jiX'qD
+,qILjGK$!Liqdl4Ah)l"e`f'%dRF5Ri@14)kbKYZ6L9))T%24-2H,C441`DF(`TL
+d-03V[S2N*jXYf4TBBMN+BHf'I'!b,*l%0$(mH0L56,$k(DU3!0%X,G3*FKi3'3Y
+eLhfXH`X4!ZReSrjd*&f+T!hCc,b%e"8,AcdFFXZ($0qUr8TU),,5NA%NbL0eeYP
+pif-VZ+Af+erhKBa,h(,6B%rV$#2G(GkR$akADkj4Q'm00Aid1*MPE@CS`#dU)B%
+MfRKL9"rjGpSdTkGcXpSrh,rlMcbZ3rM#[Icq1h-I8)cYQeAAX)Z'ep,`RqlrHXB
+$1-bMIrd04*+P40V9dqmDI19Qe$XqG&b0fa&98hY#%r*H3eMH!pBbA*JI[-RpFS8
+b1T3Rm[kM!mQ%Y5&2VR!f2iY`XlcRi"PZN[FVaKk`$i"Gl8q%j6)NSrj,XhdJBqq
+NL3M-`E#mAjV)3arTX,bhkVLF3)"Q&8-0mq8$GM@5ar@bA&L[jXQ(3&"f,lXrZr(
+NX(ji[V`A8mRi16V`ZHT#je%f'4HhTG#pThGPRPacLF[,-F@Ke"3e6fSZePkQT*Q
+)jMk&N!#XP$c*9`MCd*!!NY(FBc"5`c`)0GZbPa)STi*0K@%EQaHR`18J"F$U,1q
+Yj3MD%YV'R(d4&`*2fha'CjJYeSN)I&NDf2,@l5jAeR+@H"9KLbGad1mj6#'p21q
+J5clY"[$8i&X!3R0,fekj"JKd!S9(@(+I*hpLKb#T0"ZfJ$8A@EI)qi2-FF0mq33
+Y*mEFMk('RY#0'lB!kHb'e835a#CUrkSC*p5Vr@S`UrBd8kSQ*MV!R-X#[jbIa-F
+rq)kM35QQJ1l3fGXFE'MQRilk2V6I&$iL03Xrc,ZJ@LN`j25icj!!bJ0NAS6NcUS
+ZiRiS'4Ui"85MIXkG-2A'r(-JVXF`e(Kp50pIaZ11q*`5CGjfZVBkYU,V0#P2&!l
+f&6Rp!2AbB,,f0!AS@`m%+3FHk9KM`V(bq$K,[19Y8R*mQTRf&l&-h)"AXcJ23h%
+44bUHTLc,$!N3mk"dH+9&FD"l+eZLJZdL%TK1PX#%m4QR")BP+pP1[d08aX!XN!#
+IcdLUM'3m-9pX##,C(#VijlEGCm@5N!"02#V'S#l24!EH54Rpr$2f2+",#EE3Md)
+P3UlImhS-pb&6MR6hT(@f5lHIS!j#*pT%m9"#YpRSp@#k'`"YP%hHb5l9I4$0QTK
+!Pj'3!)4e&50"baHc$IHaaF@%Q!k6pbNb,80KjjCZTXUr4U!m9rM-HIU%`m8%a')
+N&46J&&Y5)!QUD!Kr`R!bj[@`j8m4HI`$&F@j#9e@dZY*63k`XL&FhA#3!),AR"2
+qFB$`q6m4N!#C3B$-C@3`P0`D!%)!d(RqL8)"%!+`DYKfr[QdV["5(SNX-6VXK#k
+q)m8!FQMk)-lNPeE394+kTlmPG@)5NcmeVI!aPF%%RjBQRj9"M)H%$fRc#p0C%)$
+c,BQfLL-m0j6l[0Y@2HE0mmVN$##+kRMZYPTVED+f"HVPIr&1j+l*A@1`-j%'dir
+8*9YIc"0r8Gj)+1%mVkSBVKLTbh)JJdD"L"EaGdi@,RGBAY5A[dX(k-blG8jLmff
+AEGC)AH[bK'iVhk![hi@I-P+(b[FDE18l0a6`52QqNESM2Q6hJS!5YF'QM4fZJ!U
+Q(LkMB6M$',[e'r6DT-'QM6#Q,'a@5$X'pBf!G!cU*TYVIiGX3pY4+!hjDhm(Yje
+EdQHdpRFrq&Yh&TCj62U5%V[TQL9!''Lm#kTIc'(XdQZ0KXAP0EiXTj@TD3R@$&Z
+"2&Tlj-dDEM%C(5Di5m#4JlF,kKUIj*b5#--@8ZSTF8MVB&Q!FKTe)V'49$Va"T*
+mZ&)B`Z,AKY$KbLk%BEE'S'lD$PH3!-*jZ%bViVU4&p(%D6I$D(!%kZBK8MVAjIV
+d!aP1AUT9EFHmEpUd8AkGLQd9@jB3f$L@9SH(3'G+TQN38kmVp1QRD0%l)h9KE'f
+XPCC%ER,,PKCR+j3i*hjZN[VVYXN3K*i+Al-J'B9"q4TR`rPEBaLZMj!!NPmjNI'
+m!9F*D5ZK*$r8Z!cCl,F1IfiBcE%IcN@f6@iFma`NKG8qr85'diUK%@QSJSEXrQp
+L-5ArCkGke%'Gb8)L0E'3!"Jp2%mJ$M-E('YH(Mr4YJ#,#Z11F4L9jlbS"cKj0)f
+f%Nh"`SQ-)%+F#1hHFeJ@&"pe+LHFJ-&"-(3Np@)2`NF($KE`E@5@`Bq4b#!,fJl
+)K2DeCEl3!GNCTd-rA!Qe,FpY1h`%2hX(3j[3J+[c-29#A!I*r(!%Qh6N-,Nq[8r
+$)h8[9!0ajM#4!8Z#d60L-h4V%fKZ-qdH*X4K'hqp8jJ'`KJNi0Ym68JElS5iN!$
+2frhNR)1IDA$QmU@,e*!!20#i@*jUI1`X'8`AFpqd%J`U',``Y`4ESXS@Cc-fe8"
+Eh6`'iHFc#'0*fHHX'f4UAR-FJr0pa8Kd6bqmH'ZYkMrj"`5YLN$-N!$Q`P%QFe,
+('K!fr9Mmkli!S*DhI,h1YqLkkG$K)i1K`h$kDVh'",@i8fhDmFZEK+ba3e)hYic
+4J3P#P#1CSa#`X6N#@PT-,(X$aCXI6d2TH0S4AVcaHZh`50hQkHN3LNqKDcU&BA!
+NR$IkN!"KS#)XmIADNiD3!&P@M-,'%1UU,a2!CRNL!iX+KR&3Xh"3XdT#EH)RdQ&
+jQS*U,#-3P625SH(i)f90@9km,&eMY'R(NB[XNM'Kfak9%'HE`p')F!pY%'XqJTE
+%YT'5#H6l"0HX(#!aG46Al-6R!XU5'flbkpSSblJir+j1GY8)3jUl)h9l)aFb)+!
+%LE5P6*!!JQ1$XG8BcB)"*KTTKhKAk&cMbcUe$80qChXNbE2XSc$**,P&BMMMp2M
+DZH98$-0qER(5F"B+ULpNVT%QK!PhMqj#"LAEKbYIe*YV*$2,Rf2,#!"I[ZR1UH'
+%MTrm[Q[BMF+(UedrAf+2NBNk-ClHQRYb%!DpN9I95p6JFFZ`@dQ@VYX-MdKma[E
+TcqS0!amY%@ZfVB!4-0mdrj8#8BBE&4PiYYS02TMPmP3-LYc%TNUm3SkPq0JmI'B
+)(bCZ8PS0cl!P*M$(l&%-qc'F4+%84T0RZ$r5adf@G&imeQG*pqMAkAN@)pBFlH2
+qcF2fj$SL'ShN$iUQ1eH&ckfa2Y-Bh%6rfcBGpcHSprd($2&YVl6aZ+r2p2Y6pGB
+qbdEFB8F%aM"R()$2AAF64rId+rQQ''rfjCGUEGfBZKR,G3El4eLZI[TQIRb!ecZ
+L1BTck-fEI$hhj'3j-eqibGrLL4F+qRY@IBHh1LcS6VjbNhYB+15+ZQViq)9205[
++,Skm5eZY-QI$eDid1ARah-AIGrhXchB)+ccb`@*bpKc19NCKBPq6d*9p0)cKHDI
+Y5GpTZeA4lUlQkUmDBI`RYK)$JZNm0[+NHK`CT"(X2FVHfdqF1irf5E%ppra0&'k
+GrkrREk13!1diG`N44Ik*dr'ULX*lrh4,T8,&UDZh8,"N2[Gbi5N8$$BE-9KZRA[
+14SAX3T5S`+8r)-iM`pKLKcrb$(m9,X$@b$0)mD*1IipH5FEk)Mqr8)+-S+!4Ka-
+LL[HL!@"EKJmI0Y&[5G[lT%eEVhjk&1EjBeX5h`Ul)rVaHVXRSKqXYmI`kS2j86r
+H"mGD##HB0Zi!1MK+#@e"LJXERGHDd0&4$V'Mr*I5e5kKX9L3!1e-BJPANM3!Gd3
+ZcaD3!)&lKYh@HMZACmhM6Zh[rd10SR-H3qCNPVmSZK@K))BHRLES%5AdL%hb,,"
+I&&$4jU'V4Ai@`Bcf#&kP0[pC[3#JV!3$jXd!B(f@k)*XHjbF&VR(Qk'1JfhY6`i
+")FY&KLq43h'XhUf2rG#(%)bp'DqNhImMR4,,e5N4iI0Y(3*%ZMM!'3GDU-I*r@@
+N%FV*E+!Y%!D1SA!YiR&#Q+N-eV9-rI3&U0EFbKJmZ4q$SARN5D"V"S%63qM*kFr
+j@$'pr6%5am#3!"&$aU#KT#-V4e(mMUbhP5D-F6$'Q`a&-"5K)8T++*3%%A%[%2M
+6N8BFaMJ3)fQkidcQ2KJ'%cPjKe2-%l82i5$m`pP)LU,K$b9Re%qCZ[!8#@beZFJ
+@%F2VkVKp''lT%iPk"$5MGXmmr98%CFqffC1aqRHA+GTBV%HI'djP'aa$5cS,!NC
+-I2c"$&,((C`F5N8MM1iJ#!&X(h2(K)#"d+BaM2!j,4D1pkklYhrUhNml5%N8@d)
+N&MjqN!$F4ID&5(#T#5BT0a`mcUh5iMJY2KYBA*c1`N+2q!i@@X@&M04TU*1jca+
+j$eMJ-SH4+jE)[F-!M!,!p`"B23"l4#)H)q+I6[52p,L#L"XCm4JM(K')`qM8P1K
+E!P'SJ'qVIa(q3G`64,ajT2%"ZV4VeBMV8q(D%[Pp),HI!GVN)-f2-k)a!,U,$T3
+18MV3#SRiNL$LS(eIGaAa#R)#V!qc`%3F"XCQ'&@XNAbdI@rCeP-@qm!&dE@#13*
+R#Sl!05X3EUeSq0"ZI$"MN!!C!mIphN`F[4%+F#-6E(F&ZJ4AGlP%-qk-B$K-Rc@
+c0H034U*`r[eI8%`qH6#$`XYJ28)"$&`*$-iVM2f)&0Bi))5[j+2S8S3&0!@Bbc#
+8q@#''XPpN!$#e)0mh&qlp1"a!U,f(c6M$K-F%Jii2G)LFQB2XdRQpBdVXF!``1V
+T!eK1NmErjYrL%-R&AGc)A"CR"Y$aT,$Yr@GGUcVkHcXHl1I-p0qRdd9QU%Rh-JT
+Z9Jp2ZNR*RMMra-ZN[0EQKXG(1kj`YAGS)YqL&2VmpQM-kLkTd#[q@+Dlj2D1jGc
+R[fpTYppA#TAL$GfS#V(M-[Im5@"4`T&5[Q2lE$-#E&((TXC5'S!,RU[`6PG(GGq
+UACBf&(453EpU&ip"K0aFh8fL9+PBlF+`6qSHN3Sp%Y[6eIF5ffDaHJ$$&k4Z45V
+FNYLDUfm5fcUaqJb'mk6ZFe,KGY@89k3T#c"F)R9IP!VCM1@'a(+(QC!!"&0p&Sb
+Vj1`GSDDjjh8Nekr$058kV,I#FELRPlS`''[S,Cb"KMiD&*UaEE`MK)XqcJi%(5L
+,D')8K(0DQG)-pCRI%04RT4iG1j`80SY')Clb)JTP34aI,T!!K8%BM0lcETYjP[I
+lJb%%Q'SDEHBjU*!!mHqDN!"jfRXfmdbB'M#apmE+ii@Gp'(Hllhah['JiH6Cd-B
+lbPjdPG%J%X6-L3c@I4(6`-(!dS2"p`'@MKe"039Zl5`X[ZL(%($,&N%FUdjk[mh
+#P$RiD2CqQc%P'C-a&EUV!RBk!lD&!6ZEJ2ACc!XZ654XjRRTU#$@E'UKJ[FL#qa
+3#"-+qkCUB4#-#kSBIp5%dF3BC`Q$!$P*Nd2GjX,N[XAQKCF+ZQcQcmUAi,@m[!A
+(Y!6(Y$5b'+%2MZj%SKI%mbeBBUk$S28@%pd-l9`DA#J-3Ya"SCdVQE4!BJBEXD3
+k%k(bkBdZ'%YQNqX!1F`-3!cJbl"a$"bfb4r$K,)%2p-hf-b9j5eil4-@H,m2BLY
+C85XJ#aaJSZrLmR5T(Kb@-)b!B"4J93@5Q""`6qf+VZ2QP8$L([0RH'de,m9,-5r
+I8'!aQCIJ)f5HMYHRjKDm$TTRSi1E'r'K0mpL(62a'M(2B4h69*YjTF28H(*5MbS
+6bC1&h,4"Epkh$8,)*e-EfXb9kb'XRi"c`!Qpql2(),*[-*42aq"m$#ikdFB'NdN
+$0k*M550G&SFb#eIEd&EH`P"PQ)4AZLc3BbrE5*i+lLrUlcUqmEUL"V%FDFT5Zij
+BK%-e!*N01"cm,$!X0YIKCq&JU(bRpbjHqlcAT9"ChE2Cl2L0`L&54RdAl05TdQB
+'MTB0JXa4bQiNk[PZ`[4#KM'$3fa!"rYBik0LR"LkEQkCCMk+c6Y+VM4NmV'6dGF
+BaP#A69i$aU4$l0+EMkTC,)-4!&8Lj1ip-ZpDTD8%bJ*cN!#$c%%%L(BXB"5b$*!
+!q-h)5X&N3[&V[$dNDdJ-&qF,dbDbP'faN!"X1G8+HrS,GZXCB`mr&32E'M$0IPb
+[c!*,6"+I66fpe+@X&&#8"&K5b'-S+#UZS[Uih3U6Cj5-Mb4FQV8NB!SX")+P[Ur
+!iKX&FLJDZ*-NJj`&KU0G5,b-U#,6aD1Q%R)D1"G'&B%i-piXJFT[TF9`1NMDNk0
+ReFh1P55`eJZ11%H`aDqLZY&Lmi&E-F1@3Gh$6e)6AIY2[R&lM$SGHBdIiN!2T$)
+m,F)90F5m854Q)0Pb(0JB(9K#RdCie+YJHC`kiIUE2j!!N8UNf-ATD(8q#[V5jQk
+$)H'Y"$N,5)P1H5*PEUN+lMmf8[H,*B)CJE+T41i,#TKUT0!e"50(kM)253I13Kq
+EbJD3!06+f04IPl&0XkB-416NCRE#%4JQKZT-r#cVkS#"#N(R,TF8d)i5-SJG$"Q
+D,Jc$0(88`D2N)+T@aGr[+TKilYc,fPKA`GP98bm!S9)NR[E"2%8U+Z6&*C,4dU6
+Gr'qQLY08mVke)5e$#CQ-`$1!c"GN"ej6mXZ'qk`KqDEli8'Ul"#5E9)K,48+T3)
+IY+6!-Ph!346*&-Vbf'%fP8IPPjR4**k18"JrqID&$!6`*C-+NY*T$6EjPPeP6*Q
+8c$9eVCKl"FGj,i5CZQSVVM@!+af5c%flYRme`65lp5db"FP*ZP`J'`VC$aR2+Q@
+mERCaLh4aK9em3(jCJa!X,[C-eG925B@`9$JM&5b"UeZZXUYEF29kG[91q@8!Ib(
+iqNAHl9cIqH1[rim&)h8("ZMkQi`-KHY6drhL-Ph32-T-CCc2&G"YlYbe2(a$B)R
+H0YP%@IE"K'4QaV*Y+-VfGPqrIC+51&PM9`'Q4TZ*E$6,RQQbbFJR5G[2GQTEf5Y
+653U$'fbb6eX2CJ13!-dQZi"U0VP$fiIAK&BA3$Yf!$kl"J6Rf$YTBZI3`9j82(#
+*+j!!*CG4eSJi`iZC`Pf(Mp1Np#V0U8[Y(#0!!@$%&j+(cM!hM*`fU-JlUi*R-J)
+B5El1(Yd!JD@m8['MLC@U1U(D+"b!+$YHXd5-I2ajIrd+2SkTGY,%QrI#I(kekR0
+R*-aI&(I#F*k%N4a&9VeKjl-VH*Jre)-LUf##*!9,CX"i5L'fIa5BGQ[j3k`i)bF
+Fk5epp"*9JQbRJAHT+J008H8fNM`UQ5+,dk4BlF686R+-LT8%9dBCTdT59BN*a,S
+l!SUEMK#`jHrKjdJ)4bkHa'Y#2#&Xl%&XLhJDVfla-MTFiN*m1-9jH0d5&`J)-D2
+&`hfP-2"(fCDl8S'ICr9LKUr8RSNX0HT-iY9TE`D486LN!h(&q3VX3B3Z6Mr!b@!
+Ej#H"9ET1`#!r`V*HGb"4)9-bQFd&jjrUBX'JE"V&G*e-Q5XL8NNZ12"dSH+*YYZ
+(+c-8C4R$%Fp4N!#m1H(ZU6@%4&N6NK-!5b0H&`c*-(XhrVL'&35[DmH%jMA3X&C
+cbDbG#d*Va1[5iD!CKQQLl[!dAbr#0dQ3!(p"YV#'&Dl-V'P9cm#XKXD9@iaRHmQ
+F,B![qi4XCh%(VP38EJpTTqJB!-4*C)immB4@Y`&1qpSa[%jVkhfi$$Zq*,Ij13C
++l4'`,8+Q3M8FV4((N!!")b%GHj38#FU!k&U)$)S$#Kb1$aR)ILXG%V9)jPBLJ*)
+0XEhi2lI!H,e0YBRDiBjI03*Gka`ap5pNd%hmK@@qX,J#BLLbh82A#)`H,),!"'F
+*XEJ"cLqfT3,*U0D`@$MiJ3a-9@drFl"Eb-*JU"BbEK3C4qNjfrX'!hNpMQ!XB,3
+[KcTbE&rJf'UH9*28P)9,!S-(&J!B)@RMFR[!P#iH*4@L+Pb(N!$q50e[@aRk0GZ
+Mj'3#B`#3!*-E!k)c*3qNVUcD`kpSG+89bM"6*&G6dM*cfUJqr,RhI2J'Z4M@rKk
+Z"(rSkCh+U(+FcI+1qcjdcJjbcMa%$RF`9V$Q3c2*Vb1TQ8Z"369dl-XZrE%[m,0
+LJfYMYX'fXG9JUmj"H1N+IMLDT8r`8lmY9$l$e#EZX-GJPDhfYr'R%GVHMm4kEfG
+)GU#`-[)BjrFFX%8RG9436*0&%,)3L$!0851@KU0$1R5X!8h,LXXA"@CFe@U,MNS
+,4V&J)j))bb%JiKNimY'#"4LiFIN2)hADph`ADF'P#ck!P9FYC%RY)CBCh$39$"a
+qDBT'harSF#mP`(b&-D2Ke5jKc+dd$CK,*I)r)PCKdd50!$)H2##!FJDSdCN18G-
+A[3+8(+2%X%Qc9ScYZ*d+B@h!SCC2DqS8@-U4)$RbS%cMN!"+)-$KXV+M-+R5X6&
+Mm$d(`P``3%&B*b4mKT!!N!$#V&fpD*`QY$%KmiG$f[VfV'q$(&#[UTV)50fK'Yr
+G2%r%*[j&LbUC`X'8cjh+`*60T'JI@iiNrdF$'D5XmQ@TTJFErdA$iQ-Vk%#1)6X
+Fqa+&,mM4M5&QK+QA%D$,&cf,Mhe"TS)Q6S"@##20$A5e1"9rMb*3'+qAb%@[(Lk
+#%8-KEiFEB$-pm!$'A,Kdr3J"Y%-MkABq",!5!![9D!29q1N3I$Gj,%p,46K1)jZ
+8'`11iD`jY$*MBBf[%)c9QRBHSkQ%#GK$&)CH+N#9arDAb%@`(P1@![RQ5YN16GK
+-V`D"VN-V@D0998NH6IblpMK0kmL%fCK9pFLTFLH%JY@-CS(%Qk6JeTS+[C)VbSp
+RBikBriD4A#E`F)US[CiF'[*H,5aN,Z-!6h!#dB`(h'5C'8GJ(`-lBb,h#c)(1U8
+VL9pUq%"'X'XY1mL66PBC(qBm90Hh5`fP4(Bq((Zdq+Pfk+K#*BS,m92'R$fd31S
+qE`DF+`U%!DT-LF'eE("ZC$kjlSLI1@G&jM1hRqA1*A!5LpKeM(d[@,r,@0Z&!BQ
+%L)k$V)-D"Y(1(R4J1!RA"J6aP'E*r+Tp!BBBlKFH+'*1+8NcbE(f'JH)j%Uq#`e
+@(6QPS%$KFP59P"F*lL4b$VNDNT-+'BFB'K[46)i+3r,q9Zj,3RL*UNphjQreGh,
+D!-Ij2*8pB!3L)j&2EB,6PA)h`HY9"lPel+Tb8jSVZ#Q"6C9!ZTFf`@NP9c6DPT3
+,%0XScYmK9b!NPbHfj`L8qceQbUfXQYV*MQjZP6Y5TH$ZJki1iH,Q#MDG4Q#Nbc-
+(-B3pK5BLjH4$KPlKD-`,fC+)F'"Q,hdbB$Tp#01S4ArrQS'B&0b0T%@251aAT'R
+2#CFeRi%lmPkqNicNJXU(V6R0R+,)l63ARcNS[)e'ZjL#-*,,f0X`3cB2!pA34,m
+IP"NX%(6L3VBK!5MPPN0,i3J6F("1`T8QU54lHTAC$Tj#GMP1e345&9LZm3)%!aZ
+q4*8!M`HSm0dTMXI)E0%M@0[&a)S'Z!R'hB@FUKG4*LL%+f%Fj[Tc)CJ$a*-(Tm6
+E!,d!S#Z8C6AFe`[hNRcQ%RI0EN`4h3+95#*k!@5QiHGa,0iABX&@XB+4U`'C&JU
+l%KRRHVJp'C(`Q@Kk(16%50hqDXdiN33j9#Ak,Ki9Jkj62!9Jmd3'Ueba,0KP1RA
+9dEP0rNqNR*mGr)0K04T)"R+IHS0,0!i*lV9*5P+J20p4ZJ8AkmT)H"aC`)9(R-$
+pSjeIFFfiUaE"fA4)J`F$)I4!4TTkaN*'3d2ML@'pJe8PJ!0-9X#P'C@,9J[*dVr
+@898DYS6F38'f+RN#XX,4&5#NT3S3+A-)!l12h-+TfJ-fl*h*l`ceYUjc&b[eQ8E
+hBjGG[[km0EjqCj`HL0+!#N#h*JGZV,aaGCqLCPS&"PXV'+b*6S(K,+Y3iG2R*Ep
+jr4[6RjMa[eZHMlBE*cmC+PDmhjMZp!pmVUaTAcH+!69qm[0+226KQ`prFVd5MmH
+i&%[(@B@5-lePc1'c1Cd&4e#UG$!3MNpNU&NR2rpQb98Z2JQ6qmlYEC*UR2c9k,+
+Uelq2PJ5lU,0!'Djj9Rq1UhpAr`l9YTSk"r&3'ULQb(JNU%A[3&L@QVmX5Zch96A
+E3*)$DjXfd%!e`C5`6jG9iA)QVd5RSi,H&@lUj9R*AJ6ik[b'39f-*TK*9@8d+pK
+NU&3ZjKPX)J`3BUA2aC4kPm2kpq@VpP520YkBe+H0JcT,Ql!-XYfAh%m$8-jRZQr
+UbHeX#!ea""e48YQ)B04+"K0P#X,[8'"a8I8`&ZhqSjkj'q#&cj``((r8)+BjQKA
+"a)Z@%d!T`N9IN!$aJ-H$8Bl#5E5!+Y$4Bk"'m3%EFL,iU!l(QKk9HjNe*1lj9fm
+*VJc"V()#eirBH,fiblCNE8MFEqVI)G+M&S#SR`rT8D89#"ATjj%mDap-@'*P@5i
+Yq`V,P!%D1"ICaiEm0LJRZpCDKi[FIhrdJ9Q,aCfME3T9NZ(LSh$#4q8e0'fS'VH
+@UdAZCBpQ0a+,RNMX+b%#C`Q3!,0S)&[keN@k88Pq9%hf&)1Y!T@8,dD+bDR5&LH
+P,RQS!)NiA4DZ"bj*`(i,9CjVX&"c5j2``af0hkYI3iqb@"&m'&Vr6hmB4-cCZSD
+1C(HTb+iDVlTU(0I[")[H+B"d&5,NY$@kHm6P0R%rBpj4aEa$h*AFcGZDEN[jPIr
+[E9NX6[XrfjLKi)hTC"[6fA4MKYki-6eXBkaA1#TkAh@)MGdDIXAbDe[65aekqkH
+rP2V2MApVr&YL8Vkf$%GfR8fiP`(m)GKkqK2p+l),Qbql%[f6c*hfG,D5a%IbG(C
+LGbFEZTaGGr'5[kii$bjh24F&4%QK$!*p*i04a[mCS8cN)KXbjI1cZjh6&@k1NaJ
+V9Z#kILSJG,*lEVCa,VUV&9kcLbh&idCD$b"4HP)`S*0a3Lk!)D1!URJqQMfT9cb
+M(ErG)qidR-*MTC!!m+jC)BSp[4-h)VeCIPYPbN3ZK(l*+-3U58H%"f&XfN80pa@
+Hcrrk'b5dlXH,eBjhIq1*ZIm`p*hre&+hffjF@aVT&d@R(pPk&"ArL`SX$mhhp9r
++(-5@$4E4BlRiF,K%DNa1N!$`dY-pm2NA5)5IQ"IX8ZrJ3f"Ufb1rBc992'*UicX
+D%kMY*(hm8Ip6I"VBCmP2qM3CK+)D9#9T"f[D*R&aQ0STN!!DbCXXX2`k$a8G2)P
+q((mf*6H6qVljh2V$AQkDiBVdalJ0CLPHTr1EA$[%6VmMXR0arU&Tl+%DpfH[1KB
+e#89+TXK)D)q4NC!!YUE(*KHX[kXi09EPDV5)FhYBf#`NP6[a'!Lrm,RU506[Fk&
+jKfY53PV@9k,K5RX6GP-9ZfPll&89cYmA'#RlU2S%,S,M2a*Fe4M9ijLD0QQ6me(
+T$Jri#mim[cbcrFccm1+UM"0$aQQKM%,KQ4dL5`VQMZVleYhQ(UTbM+lp,![p&3Z
+fq@'Q')qdmD+VR"rGA@eC2kYYeLpMI14GHMJ!&YmBeIHi2(Mi#+m-$*d9KQB&KU#
+QNI#q6%S`QP-2#VVrJ4m@#`mZN!!BLjXQ'9Kd)cpV#6,h9$F$MAAG4ee[)"*8BEq
++Q$+52dL,NpfScS2PZpMb(rbdbf1"6iHPdHmT)8@UTlHZpbUA[lJQ2AVXrZbAlNC
+0E2J)QI5&6&4c,kUk(K!H##"Abh2N#M%LkX8#SEUl`Klq"A6B)hp*kX&J9J`"9RI
+*)%mE"GA&('1CX"G)rVdRCU8UUV-+VAh#eU0bPT'bprm%SP2P#50c54MiI#9Pf[e
+TUZ`LLEFETb2a+4%$&D@4h5Y23%'-GYR%LZTjAEh9#j%JUG8V0pJfCPF[-LbZIUp
+Y6r8R9B)rJYj8A@"T!`(PJf',c%(915"6*i3RBI$+&J3+CY$aerlhVZ0i"*!!6Gb
+,kSeiT@2HMdCHp8k-[!S!!!*#!*!$#J#3!h)!N!4#H!T+RFj1ZJ!d6VS!*%*R5(N
+!!2rr5'm!"%KA5(J!!5)krpC1Y4!!)'d!E%k3!+Rd)MVrbQF%6V83!%je@Bm[2&T
+&8Np#CkQJ*&GCMbmm4%&838*RUD!J9b"3)RJ*##45B!ibf'B+-KTJ!N)C8FRrr,[
+*CZkTSkQM@Bm[2%4548a#CkQJ)&HJ*5"3iN!N$@!'-KM9Y4!!8FMrq+QM6R8JAc)
+B0"L`@&I*rrT+3QIq6[!Jr#"I-KJd',#B9mRrqNT#Crj1m#$k)&mb'$3BX%*Z#T!
+!3@d'd%""m!!#-""RrNl`!!!J,`!%,d%!"#)[!!J[A`!%51Fm!#3!*J&)3X6$+!!
+U!8K&b-A84%K#N!2!`G##60m!2#)I6R8J,`!%,d%!"#)[!!J[A`!%51Fa!%kk!*a
+-h`#-)Kp1G5![!!3[33!%)Lm!##pI!!4)jc%!6VS!I#!"60m!M#)I6R8J,`!%,d%
+!"#)[!!J[A`!%51Fa!%kk!#a-h`#-)Kp1G5![!!3[33!%)Lm!##pI!!4)jc%!6VS
+!$#!"60m!M#)I6R9+J'SF5S&U$%5!4)&1ZJ!J4)&1G85!6VS!&N5!4)&1G8U"DJT
+%J8kk!!C%J%je,M`!!2rrXS"M"L)!F!"1GE#(BJb!`8K!-J"#3%K!6R@bKf)D,J"
+#3%K!J-&)3%K(2J")4il"-!G)4c)(6R8N!#B"iSMLLE+(B[L!`F#(-J2#`#i$5%I
+1`%K(dSGP#*+#BJ4%J8je8d"Jj%je!*!$A!#3!i!!!!aB!*!$B!#3!b!!!$mm!!1
+Tm%&%3e)$!!"B$9-$R1S'YiZ&ElRP&ZjU#kJ&,-#Y&VE`,r!YGe&Vp9i!!4lq2r+
+65hNQLaM%"%SP-R')#kJ$EAISXa!"!*!$#PM!!!`!N!--!*!&I!!"!*!&D3"M!(d
+!R`3#6dX!N!Fp!'!!miKF9'KPFQ8JDA-JEQpd)'9ZEh9RD#"bEfpY)'pZ)0*H-0-
+JG'mJBfpZG'PZG@8J9@j6G(9QCQPZCbiJ)%&Z)'&NC'PdD@pZB@`JAM%JBRPdCA-
+JBA*P)'jPC@4PC#i!N!05!!%!N!9Y!'B!J3#L"!*25`#3"33!5!"R!31)-P0[FR*
+j,#"LGA3JB5"NDA0V)(*PE'&dC@3JCA*bEh)J+&i`+5"SBA-JEf0MGA*bC@3Z!*!
+$6!!#!*!&-3"R!%8!V33%8A9TG!#3"3S!8!!F!4#)'P9Z8h4eCQCTEQFJGf&c)(0
+eBf0PFh0QG@`K!*!&#!!1!#J!,U!#!!%!N!0q!!%!N!96!(-!C`#["!*25`#3"33
+!53"&!5k)A8&Z)'PdC@dJGf&c)'0[EA"bCA0cC@3JGfPdD#"K)'ePG'K[C#"dD'&
+d)(4SDA-JGQ9bFfP[EL"[CL"dD'8JFf9XCLePH(4bB@0dEh)JC'pPFb"ZEh3JD'&
+ZC'aP,J#3"&S!!3#3"9d!F!"a!+`%!Np,!*!(5J"9!41)1P0[FR*j,L!J5@jcG'&
+XE'&dD@pZ)'0KEL"[EQaj)'*P)("PFQC[FQePC#"[EL")4P-JGQpXG@ePFbi!N!0
+Z!!%!N!9S!(S!I!#f"!*25`#3"dJ!AJ%PL%j6EfeP)'PdC@ec)(GPFQ8JFfYTF("
+PC#"LC@0KGA0P)(4SCANJBA*P)'j[G#"cGA"`Eh*dC@3JBRNJG'KTFb"cC@aQ,@9
+iG(*KBh4[FLi!N!0D!!%!N!9G!(!!F3#X"!*25`#3"dS!93%6L$T8D'8JCQPXC5$
+5AM$6)'eKH5"LC5"NB@eKCf9N,L!J8'aPBA0P)(9cC5"TG#"hDA4S)'0KGA4TEfi
+Z!*!$+!!"!*!&c!#1!1!!dJ3)3fpZG'PZG@8!N!8%!!3!``&L`!)$k!#3!``!+!!
+S!,B"(!3"998!N!--!#!!#!#L!4`!JP99!*!$$!"L!*)!m!'B!)9993#3!``!+!!
+S!(8"2!#(998!N!--!%B!TJ#k!GB!KP99!*!$$!!J!!J!SJ%F!)"993#3!``!+!!
+S!+i"6J#e998!N!-2!!)%)'pQ)!FJDA4PEA-Z!*!$-33!J!#3!`-d,M!Q0#i`,#!
+!U5!a16N`,6Nf,#""E'&NC'PZ)&0jFh4PEA-X)%PZBbi!N!-D"!#!!*!$!c3Z-!p
+6G(9QCNPd)&0&35!d,M!!N!--#e9Z8h4eCQBJBA-k!*!$#!FJCQpXC'9b!!!%-d&
+%3e)$!!Ch$9803b)5%HCHEK"N,4P%D[*%*!X3@DZQ*LHh2BZ-i(ERb%Qh-e2bQAp
+h[qrELM`EhmbhY8#5eFlXbH4*f,ilNa'5j9ENLDc)laq42j1IbEcCeRB454B6XMF
+Yb5)S31)(rdjCKT&84$LS#cYiiLGf)5c5J1e3aD%@GK*H(mq1D3bR6lpC+R)6)mR
+Y[@4[%r6@hmf'R%[)+8FIZEr5&V,ejRAjaL3*5bPTf0kN&a@6NPGC,a$mi-RK`KF
+HI%V$*QM4BN6+hFfqTX2b,*5j55k)jb(*(2h&i)XDJqim&Fee*9$cG*JIMZ6*i#S
+GjViNe#Xq)@+3!*h[`af,NK")heX@$ED[(5XhPA-`LA1fRcbNbSE0RBqrZ,m'l)Y
+Vm[NQUD!A@)"Ck2@aqNDT+'b%UlJKdF4%D4j'8D8QKLJRXm5(8JQR40@4X8N6+L,
+JmB-'82KL!,85ECR3J#8d%@TbLdfC`eLTT"qR$+aeU)[Di0J#T6DMFe4B`aLflrd
+NJ-)Z!m3jBTLiESAmcl%RFHbLl9fAJNh,JDRU`pD$+ZFdM(GdeX@!G"Z$#pDfYBX
+aSNc2)HBbh"2bA1lEQ20L2d(0f,[Q&#I)A'ZQ2(KBb@@qIDUNT-rSkShVZbh3b(2
+j4EE#Hi"%aD`5%YXK48JPi!JpjfESS#*')G-lpU&Zma9#N6ZP%AF+hP-PhjBJ16h
+DlDNc*qDmR%1jlK@TB-cEfPh+FK[jplG1#3aZ,GrmP`rLNbG@J3Mpl)N"@`NmD#E
+1Ye,'NV,SJ&0-'N"4lGDH(L15jV)kGQArf*KLjXEBBNT+ilSFVqIQ2L6S6,2!-qd
+r5A,SM5rZ8%e+qrrFIEa)TAK+(ePf3"KU9mH96KeiJM&J8-CCH2(UplHlTpQ8&pI
+V!mfS&!eYS22Y"H59K,,GX(5'$iDUN!!D1!1%4[Dm+R'd+EK+q$Ll6eBP[SHY1Jm
+&+*f0f9@c1LPl$U`!LB0EA#,N"l-#h9`&eEjM4RfXIVf`GZf$Y!rlJeGII3NEUUX
+[JA!5-Xp,Z*!!K9k#PFalib#6LH[)jR!53fYB8Y@+#cA4e!fZkRA5eL(bC5reTB0
+Nq+`1N!#ULm1*SfpKL@KA(V8&MK(J'6fDpK)ZJ-$X$'lTUd'pQXb1L-Hl60lqhlJ
+2YkIbbBfVNkBAI5lbXT26-1CCQI@i%dADZX@J'V("P$45$f-VG@mlh@!+D@pipZK
+GMr,CUC9ZcXXMqF'dI'XXGU%XA8IkajQ+#fPh$2Aj@FVRjqVrINKT#[0r9hl1a'B
+ar[EhjGLF9@FZ6fPZ&[UQ90aX9RVbi))fT0[hNY60m9Pip++5qIc2PqqBK`c8pE9
+GaLXRlhFHm(""&mY9YB2CTqfiUkZ$G385RX(1bd[ERX3qk'4JZaVPpDGj!R%'T["
+3[!PH+Gc$PhJ`"hHqIVA3@F0Eeq31!!!",d&%3e)$!!&Z$98,Sa,3(`,[MX0G"i1
+VS')4Q(4+"CeP@S'Clmb(SM82Ip(I(*K(D4A2I'$q&[0K9K8c&"2&r$@V5EkTU*K
+9439&phSP6c(*!"1aC5A+J0LHE33'eM"R5Q-Af($455FbMP!J*%5L5"VfFJe#ABT
+bVcRA4B@a96dp+RHhD`rFF&F@))QYk$RCaCMM3iY@r'%I9"NNRi3H&+6Nb+Ekq,R
+[CKIQhmVFF$J`VdQ5m'YemhE`p&D5E6j5(VmrPl$l$k3rXTPHl)!0@&jc$0ZABIP
+&mp&"JI!#DaENdTMml81I`#icTcGeq@6Upd1K&[m+1ImYiqD1UMI2ikh!"rA+h@G
+FD3IbmLC@e9$jp9!k6JUl9a2!KP1+'B$U)Bc230$[U!%J"*%91SCK"!!!#4G"4%0
+5!`!1K3jG#k55),ibhjhrq-jHjTcIRm3X@FlH1F3CSh%X80,b[R0QLXqEJ*aX40@
+UcFUD#%5Rl(e8JllrGqBa*dc)b``NKG+`*T5N3Y"1UPMIThV[djD9N!!b0-6@8G9
+SS`-*3XV@EJZ,ppfG(@H&VG9l$HjZ05)#-d3M(d0&9JkT3rK[IJhrMG+c8Cl(B+4
+U`aFMihF0N!#jkkJ(bAf)aK,IG)P[ca,IG-NC,*)9Q2S@4ApLIjK&+S'Id[Z4`Ve
+!C&F!hk33*VJjb0NB50)-%!X)p`RCp6"I8pTh4UI[+E8`dFID4ZkGr@lA$AQDlbl
+dTY[ZH@,hr!+XiD9#H+cpQa8c%jVX(JTT(9@GRD&+8bJflXSeSl4`5cY$Y5BUZhI
+&Y(p9rDdY9MhmA'a'S26RfD0D,'"MJPS)PR+'Y$*IGS48-42MQe(`[2"&@fPYS(Z
+$2,ZVS#hMPle6m-bhq8GG`NZPN!#-)R+V%99PNPlh36fhm*9bM*6G5(DLl6*[`RR
+N%1kYe0apJX"08h9pBpI"UeK@ZP%fDY[@f0f"j$FB'16,"hmb'lqqr(ZIF,[qqr(
+8P4F2LPmU8Y6lJ@,C*HD'0C[m5%SlDaYhr0f9c5@RlpTrl%Ba0h%pIrF&([T)hhC
+PQkhZV!V"p*ICGPYr00)ATYFTIIZZ+Zh!F'lqJ+RpekRmf5JSrZ#`Jfc9YhilkVT
+Hh9L([6AGJPqp[j5#5'i#SGl@1!l!f0$([Z4B9)JpbTMr8K%pV8&b'lM9+a*8%hV
+&U'bb498Z@DQS*MRZ&3U9S9IX$jNN*bhPP@UI8TA)Arc+eR608jlGh#RG(Y%mCIV
+ZHjY0eR5&1qC1dXc90emD5,Ej@(eFD1XV&rqi5CRVdF[i&43@eN[fU#"2lpYa(Vl
+C*ZUUNZp0XGNQB,2*LUI#aR9CrBK0&Sp+b8*6C1J9E63Tqf"5i25flDL4ELQ!0#R
+1JdB29[1P&-F&`EQV34i-5ZXME9iP2CJ#Sq!JM5UKU,HpAUd@9Ujj,Ce3erhrA2Y
+0)fVEk2m'%q('2UUC%2HLD&+GqD$mXUXk4$HdCk9'VaEk0mH(e6-fT4pmjRa9e3I
+09fm)JH',*UjGkCYBVDUp[eRI+)5dJF4`UL@ija2YP0UeSHUUm2!Y1A!N#i,p%r+
+mraE)KC!!h)mYI"M-*-j`h-92m%VhR)6A)GbRK)!S@)q!m3bE39ZF$L3#NlajP54
+K$2XaQX4Q&+bEm4`j1AMiN!!D$8bLH48j)NdQ4ZlPFU1$$j(RK3$Nj8cC#bja3Z9
+@VceajE1aV$3CP1@85*e1e)0Q)jGcLA5!ibD49*QcdSUIb6+I1lrc$,UNCTa,i@!
+(l&NX8Z!UXl)Fc@N10p%$TmlaarF12*%VqVQP+IbmGAC[Z(*i4%i0ElMdXHT++-Z
+e+['9N53H0#4Y9,3%Ib#8d),3L422HK!GJGBFY,LYf`X9ljIeI"9HZ54kVCUF308
+$Ta+%AGi0FXiSpX5"dhYD$!,RQ3U0(P3fjfdBeq`NrZ'e,erl6RVGI#'mVR"`phb
+T-2,3[')Sd$cb3&!%V`adK&KR!aa2Y%%,Z,h0rNH#iVJi,$5IJe1*KV4GN8AeTLJ
+dhii*%jh+4+0lL-6G`Gb!1(p'+l"R#Xqlj[FAI)G0QQ+9i'Uh+QCdXfkhYb)Q$Qa
+("&X5L'&#mPXXm@0)Ucd%5eJUcINKX48pJ6d%PbS*K&[ci9Z%V*(I3hJPL`MrdJS
+I3`D53``T(FqmS[EkqkCA(rKLZIm!-KZ&[1GiTPFGl*ZpSH[blmr0kANl3fU1pr3
+DII1R0R@Yr)*!qJH3!1C*GFpaYEH[Hmh&ZN*A@%M6!%pD0diEiCSl$VHQjr`k#`[
+26@IZ9DI$daI6KAA2pN(q9D3Y+FMIp2MMdq%6Qblq,Td1hibPQPH&k,ZJH0C&,1f
+SJfdU0CD1i33[B404!YdjT'r8eK+UMqe@Db3GV(dcl%e2@"m1dpJq%Rh(6&#9H,q
+%2[!CM`9RLm[r%li9iPZYi6IUKXJLDiC`*LQ1j@0C1-R3A,AUd"L@,!$X'#)f9Q`
+GU4qjDTIlE$mqA(M5%LlfVkUm(4R$6f-Y*2qH0D6p36il*,[YSYC3Ef@,rIqX9*l
+!Ebf0'B$Bc0CR("AbR`1@-IY,[Y#8e9pF9R&@mL*83(aXZ8)Z"La%VB`"J[[p584
+Bj!eI5+Z3!-m'FQle8ke"Xl)9E+f!P-EiV3U5Rr1e(UmFZASXGdNpP#jSPS%Mep0
+4%N&XLQ8J!kB+PhfShJLZT1Hk&EZ'!AIMZcc23,,Q+,p414k9S,B6,'a82bfhAaY
+ETNrA%56ja02ZG*T5j4lME!"-qUA2Y$MMJiY4T!6X@Z)k)D`B8d'!AjJ-cVeXTE3
+(16-(E(Pb4Q#Tj'@9#%5hl5$Glld`DH2L9KVmeH*EFa9hpIDl1`$QYF(BlY&bTX2
+dqBA$reLmd2TDRl(Y0,(GBIAPhAX"rrC*9qj6Dcd$H#M1@jc&$Y'NGA48bXlAXh*
+S@kH!Zkfbli`-Z2M"q(b&pHX4I'G4%de6YE9A(RA@qh)a&f[!UpMFMmNq0q"eB[p
+iVX)kEUcG1DPY3V0$`62A6#EC@@pc!%i&h!kia8UMBYEe3)A945F$!Zi8m+KTci2
+1HV%pT#rHl4bkG*dEl!9mF@EmL(@Qr)X@N!!HQJGM-lYP`%2C`r@*QVC6V('0%["
+TN!#F,[k0%(eX#hU3!0aHNqRj@JSe6JF*I04KqTHADUE2Tc,B-J[*j9f1hMkq&-B
+!U*!!KfX`AB-8B"Ah@IS[4f&li'IQ$1CAe9if%aiqGN!&Q%S4&CiQhDIf@FS1cNU
+jaA2YaBpQiQbY3i'CP4mCLmX+L8rILUK3`8dZHmjaV0YkRM[jP2ccRlVip*4Me-q
+APMjP-@A,$9P,I85&#Ulqe@iaGfcqU-aPXh*qlF1-@ZZSlH5C1mA0TJqUQTkf2j&
+8)5`qd`4i3-FVSVN)cQ3YT[jL52V`9&+&J$mj!EMGASjQX`p@2&,*6%A`c5D4[Tl
+00DT3H*AkL5leI''P)l9im[YlhCrhTp2DG*J[E4#Y4L*[6e`je+K#q,'4%64@!PY
+K&Z!k%mV9AKd1kGd&SBA5Q+kqLIFl@5H@2&IJeq44!*!$'!!d!!!"(!&S!!%"!!%
+!N!8$k!#3!j3!N!-)!#!J!3!#!*!&('&eFh3!N!-"4P*&4J#3"B"*3diM!*!&J!#
+3!`G"8&"-!*!&!3!!!3#3!`+!!!!%3!!!#5!!!"13!!!!*mJ!!%%%!!#"!J!"!!%
+!!JI!J!32i%!)''!J%"[m%#3DP!K-'[3NRc)%-N`ek2NN05Jb%$Ii*!J`i!J%(q!
+3!J$!)!%$m%!!J!#!!%#"!!!JJJ!!%q3!!!R)!!!%N!!!!!)J!!!"3!#3!i!!!!%
+!N!-$J!!!"m!!!!rJ!!!Im!!!2rJ!!(rm!!$rrJ!"rrm!!rrrJ!Irrm!2rrrJ(rr
+rm$rrrrKrrrrmrj!$rRrrN!-rrrrq(rrrr!rrrrJ(rrr`!rrri!(rrm!!rrq!!(r
+r!!!rrJ!!(r`!!!ri!!!(m!!!!q!!!!(!!*!$J!#3"#!IU5!a16N`,6Nf)%&XB@4
+ND@iJ8hPcG'9YFb`J5@jM,J!!&8J!N!-"GJ"1F8U$CKT"l3!J-,`!#$&m2c`!!M&
+m!!%!"$&mUI!!"Lm$,c`!!"5Q3IVrd0$m!+)[#%+RB3!#*Ylm!""R%NU$C`4`!8j
+e6Ud!)Q%!!pDTp%ja5S0Q!URdF!"1G@"b38a"4%4$69!!!`#30&"b3@e)jf$`G$+
+I`Lp)!#!J6b*8-@N!&!!B)8!!*$&m!!%!,0+4)8%!,U!#hm*-h`m'6R9+1!THC``
+J+J!)C``J3#!3C`B[1[q%6R9)jam'3IVrRR!-)LS!"-+i!aTKT'B!!4*)H[q16VS
+%i&K2X(Vr@QB!!1bK'Li)##S!"J!%C`BJH!+QS"XX+J!%+LS!#"JU!!5Ae*A8)$V
+r9U%HCJ!!c#a))$Vr5L)'`VJ$'PK"B3$r8L!krd,!Z!-D3IVr2L#!5S9Q"+%LB!3
+J4D!RCJ!!Q#T),cVr!Lmkr[S[1[lb,cVr!Lmkr[T)H[m5,a!J1[m!8B""q[lf))!
+J$P#!3IVqk##!5(S!HQ%!#L6Hr!!J5N"R!URr)%kJ(b"(S"Yb!")%j`RM'H34!!%
+!)!)"!1!J6D"T!J!!(i!")%fJDYA8ep4"q[kB5T!!C`K`!D'BF!1KQ#"0*8J!#(!
+!60pJq%je60pJq'!!rZ!J6U!IeG6Ae#"(S"X`1!)J-F!+B*()B1"19J!!51F!1#K
+Z!!a(q[jD4IVq@L!8X**Y"#!5+)"+J'm5)&-LEJ!)SLiJ&0'6NC*`!'!%-$crf8c
+I(!"1ANje6PEreNMR%aJX,J!35IVpmN)(S4SY52rQ5Li!#fF')(J#TU!E,c`!!+$
+m6VS#RLe!rqTB6fF!!Ai[,[rU6VS"q%S!@%pR"R!"B!!"FNKZrrK)E[rd5'lrlNk
+k!j)J,[rdS4iY52r`6qm!$'F!!8JJ,[riS4iY52rmC`!"1LmZrrJ[#%kk!qC+VJ!
+88%pQ!!#8@Bm[2%024%9`!$m!U"mQAb!,ChiJ%h)Bd)%[!%kk!Y`-3!!$@%pQDL!
+0FLM3J5e!rpSJ%h3Bd))Y32rH,`"1ZJ,`5-!Y32rL)%ZJ+5!Zrpj3J#P!!#KCMbm
+,6VS3d#!IFL#3!)%T3!!X,blrr#mZrr3[,[r`,`B[,J!-5'lriLmZrpT)H[kX6VS
+)9Lm,UD02l`!N+@lrkJ!-+@lrm!!3+@lrp!!8+@lrr!!B,c`!!+'B6VS"L#e!rpB
+[2!!!U*p1ZJ&k)LlreV#"9X0%!dL$5--T3`!F+8B!)#PZ!!`!*%Kkr*!!2cbJr#m
+m!!#Jr%kk!54BMam!6VS3@#mm!!#KQ%kk!6T+J%r[!!aR"%kk%&B`1!&Di%!-3!!
+'CJK"qJ!D)FJ$2(i")'lrjU!E%!G-lKM)rm*1ANje6PB!!%MR!4K#"bmm!!#Jr%k
+k!2)S3#!-@%pR5#m-6VS!8%S!@%pR2#C-,bX!$$mmS2`[2!!!S2a1ZJ#L@)mI!%k
+k$pBJD`!3S"mJD`!BS"m`1!&Di%!-3!!'CJC`!#(!!caq!4!(61iBJ2rd6Pj1G8j
+@!!")ja!)+'i!#(B!$+a"6%&%!!*Q&!bX4%008!!'CJT`!l"X!!TQ!RB"%!0-lK!
+)rrK1ANje6PB!!&Q22cbSER!"(`"1ZJp-@Bmr2+TZF!%I!%kk$ciJ(l#ICJB`2!)
+!B!3`2!3!6Pj1G8j@!!![!c!m#!$!EJ!+FJ!b!%U"8X0%!fF%F!&J!R!!*Llrr%j
+H6R919J!!51FI!$iZ!!T)abm(6VVraKS!F!!3"3a!!!&B6fB3!NF(rdkkrhb`4fi
+%F!"J+PQ22cbSRh!"(`"1ZJl#+"pCMcm((`91ZJkf,"qiKPI$4!0R"(!!B!)J"Nc
+Z!2Mrl%jH6R919J!!51F4#$iZ!!iJEJ!)+&"f!(!!-"3-J!!!384Q,R!!-#`!!Jb
+!!!"$8QBJ$%IrrfFB)#`!"%*!5%$J5#)m!*!$rm+!5-HqJ@B#GJ%3!dcZ%)Mrp%j
+H6R919J!!F2m[!%KZ!!K1Z[qB5J"36fFD)'i!##!S!!4#3%K!i%JL2!#3!rr#J$!
+"B!*`rdjH6R919J!!F2m[!%KZ!!K1Z[pL5J"36fF3)'i!##!m!2q3!m#S!!4J!R$
+r6Pj1G8j@!!")j`!B*Qi!%#KZ!!`JEJ!)-,`$!A!!+)!'P!!!!53'P!!!!NJ'P!#
+3!b!'P!#3!i!'P!#3!i!'P!!!"*!!"T3!!!%N"T3!!!53!!D8!*!$I!D8!!#!!#D
+!"T-!N!-N"T-!N!-J"T-!N!0)"T-!N!-qF!"-lKJ!rrK1ANje6PErp%MR%aJQEJ!
+),8[rp!DZ!*!$*2rd+'lrp!DZ!*!$)2rd,@lrp2ri"Ui!N!0)rr3YE[rdrr`'VJ#
+3!clrp#!Zrr53!+i!#,#Z!!aM"R"PB!!!X%*(3NCJ4R!!-!F-3!!%9F0%!fF%F!"
+J$(!!-!GCJ()%6VS0)R)!-JFAJ"J!F!!`"b"ZrrM3J$''#!"`!$!(%$-)!()"iDR
+F36!(8NG`!$!($%!!*'@`3NGm!@"'F!!`"`a!!!&9`d3$C`4`!'!-F!!`"e1!FJ*
+1ZJc-FJ!b"aQ!'!"`!$!()'lrr0#!-BB)!(!!-!F30!J!FJ(KUGa"-!G54h!!-!F
+-3!!ICE"`!%cZ'-Mri%jH6R919[rm51F2'#CZ!!`SEJ!83NCJ$R!!-!E3J%*d#!!
+`"P*'F!!`"R)!-Li!%Y+"XS"ZiN*'H!*J!!#D3N9#4h!!-!BJEJ!)jB!YF!J!rra
+JE(!"`+lrr0j!F!!`"A)!-JCd!"3c'!"63NM#Y)"[+(!!-!I3J()!-M3)!%U"CJa
+`!$!(d)!jK!J!9%4`!$!(d)!q0!J!B"K`!$!'FJ!b,J!5dS(5J(!!-!I3J$Q"#!!
+`"9*&)#lrr1+),8$rr(!!-!9b!$)'G!!8-aJ!Y%"LJM!'8NDmEJ!5C3$rBNcZ'2$
+rj%jH6R919J!!51F2'$iZ!!iQEJ!3+'i!#$JZ!"Bk"qC0F!!`"h`(c%"q!(!!-!8
+30!J!l#Kb!")!F!(!!A)!%J$HJ5!(d)"b!$)c#!!Z!9*'F!!`"R))XS"Q"N*'-!9
+54A!!-!63J,#(BX"`!$!%d)!L"j+!%!&-lKM`rqK1ANje6PB!!%MR$`Ji,J!52Li
+!$LKZ!!Jm"qC1F!!`"hS(bN"`!$!'IJ!H0!J!F!!`"A)!-J65J1D*CbT6J@F@8i&
+Q)R!!-!C8J()!%M3)!%K"3N'1JA!!-!C5J()!%M3)!1'*MS&`!$!&i+p`)*!!"(,
+ri+R#Kc!"61i3m2rX6Pj1G8j@rqT)j`mB*Qi!##KZ!"!'VJ!!!53!&!DZ!!!#5!!
+8,@i!&2rd"Ui!N!-J!"3YEJ!8rrJ'VJ#3!i!!&#eZ!"6rr"!6jJKb!")!F!I!!A)
+!%J"536e"rqS3%q))FJ!5!(!$`!&b!")!1!&84(!"kDJp32rbF!!3%h*!`J"`!"!
+"28$rm(!"kDK6J$e!rqj`!"!6FJ(#!'F+F!!`,[rZ8i"J!R$r28$rl(S)5Qlrm'G
+@,bi!&#mZrr4`!$!Zrr)[!#!,8S![!%kkrcKb!$)!ji(D35mZ!"3[,[riF!!`,[r
+b,`![,[rd6VS)I#mZrra`!$!Zrr)[!#mZrr3[,[ri6VVp)Nr[!$"#4f!!!2C+E[r
+`Cc*`!$!Zrr)[!#mZrra`!$!&,`![#dkkrGjm!"`!F!!`"L"Zrr4b!")`#!$D38r
+[!""J'R!!-!3[!(!!-!8[!#m,6VVq,M`!fN42l`!-['lrl'B3-!G54h)!-J"#0"J
+!B!!!MVaZrqjQG%TZrr"R-R!!-#lrmLm!,blrr(!!-!8[!#m,6VVpF(`!(!"`!$!
+')'lrp()!%M!)!0T"6qm!%'!DF!!`"#m!F!!`"5m!,`Y1Z[h!2!$D4%r[!!a@4Q!
+8F!!`"e1!FJ!b"aQd#!!B!$!(8NF`"P0'5N"Qj'!5%!E3,[rV-JG54h3!0!%CJ#J
+![Qi!$Q8!r`C`!$!&AS$QL%cZ'2$rdNjH6R919[q'51F2'#eZ!#6rj!DZ!*!$*!!
+N,@i!*2rd"Ui!N!-J!#3YEJ!NrqJ'VJ#3!dJ!*#eZ!#6rq#CZ!"c@r!%Ne[`#50E
+m!#$@r!#!e[`!J#e,rpM@r!53!#e,rpc@r!%N,8[ri0Em"*!!,8[rm0Em!(`Y5rr
+)er`!!)!!)!Z3!+i!(,#Z!#"M"R"PB!!&8RS!3NFJ,[r)d,`!!)!!,8$rc#KZrmJ
+YI!!!J!$rr%KZrr`[,[r))'i!#%k3!%UZrra36fB'F'GJ!!8B)!a5J,#ZrmaMC#e
+-rlSYE[r-rliJ$&+!N!#ZrliY32qf)#lrZT!!V[r),8$rXL!Zrlk3!+lrZLe!rkj
+R$#"-)Qlrb#!ZrkkL,LKZrklCl[r)5'lrXLmZrmJJEJ!)6T!!)#lrXV#ZrlC36f3
+'F'GJ!!5U(9crah!!%#lradM!d)"63$e!rqa`!$!Zrqc3J$e!rqiJEJ!3)"$3VJ!
+-,8$re#mZ!"`[,[rF5(J"*#m-6VVmG()!-J"+JGR",bi!(#mZrpK)H!%N,blrh%k
+k"E`[,[rJ5(J"*#mZrp`[,[rB6VVkCLmZ!"`[,[rFF!!`,[rX,`![$%kkr#jb!$)
+!5S(C`5mZ!"`[,[rBF!!`,[rX,`![,[rF6VS&FLmZrr"`!$!Zrq`[!#mZrp`[,[r
+B6VVk'(S!3NFYEJ!-rp"2l`"JB!!$X%*'B!!!Q(!!-!G+J'Cd)!a5J,#ZrmaMC#e
+-rkBYE[r-rkSJ$&+!N!#ZrkSY32qL)#lrTT!!V[r),8$rRL!ZrkU3!+lrTLe!rjT
+R$#"-)Qlrb#!ZrjUL,LKZrjVCl[r)5'lrRLmZrmJJEJ!)6T!!)#lrRV#Zrk*36f3
+'F'GJ!!0HHJ!D((i)F!(!KGa!F!!`"L"Zrq$3J$``#!$LM6!(8dG`!$!'$%!#5'8
+!rf!%4J*)F!!`"Ja!!3"N%#!Zrp"5V[r3)%!3KQ!!!ZS%4J%!F!!`"L"ZrqM3J$J
+`#!"`!$!')'lrj()!%M!)!$e"rm*`!$!Zrm*+J'-!!+*JH#!-8S#`V[r-Bf3Y62q
+Q,@lrc2qU)!a5J*!!V[qU,8$rSL!ZrkD3!+lrb#e!rjiJ,[qUN!#ZrkBY32qDC``
+J6#*ZrmJJ,[qDSLiSE[qDfHlrb%KZrji[,[r))'i!#%k3!#!Zrjk`V[qL8%pN"R"
+RB!!#G(!!%"c[U)U!8%G`!$!($%!!''-!rhj`)*!!,[r$F[rJUF+&f%&`!$!Zrm,
+JVCjZrm*#4Q!!!*K`!$!(5S"QG#!-8S#`V[r-Bf3Y62q5,@lrc2q@)!a5J*!!V[q
+@,8$rML!Zrj+3!+lrb#e!riSJ,[q@N!#Zrj)Y32q'C``J6#*ZrmJJ,[q'SLiSE[q
+'fHlrb%KZriS[,[r))'i!#%k3!#!ZriU`V[q18%pN"R"RB!!"b(S!'Kaq#(!"`)A
+F3(!!-!BJE[r`d)!m-!J!iSd`"e0(['lrlQ8!rf5FE[rZF!!`"L"ZrrM3J$e`#!$
+ra(!!-!BJE[rdFJ!5-!J!28(r`R!!-#lr`NU!B`!!T'"i)!a5J,#ZrmaMC#e-rkB
+YE[r-rkSJ$&+!N!#ZrkSY32qL)#lrTT!!V[r),8$rRL!ZrkU3!+lrTLe!rjTR$#"
+-)Qlrb#!ZrjUL,LKZrjVCl[r)5'lrRLmZrmJJEJ!)6T!!)#lrRV#Zrk*36f3'F'G
+J!!$qF!!3(1qSLS"34h!!-!F-3!!BB`$rIR!JN!!Zrm0brq#T`SA6E[r%F!!`,[r
+#i+fHE[r#F!!`,[r%5S!QE[r3Pm#hlJ!-C6BJE[r38Ulrd"#E)'lrd&+Zrp!3Qb!
+Zrp"5V[r3)%!3Qf!+)'lrd&+Zrp!3Qc!%8d4+3'EZB&C@4#!Z!"M3VJ!8FJ!b,[r
+%*#lrd*5Z!!b5JLC!Pm&J$L"Zrp"5V[r3%*X`"&0%5N4R$#!Z!"M3VJ!8X)YLiLC
+Z!!aJ#L"Zrp"5V[r3%*X`"&0%5N"QlL!Zrp#`V[r8C3$m5#!Zrp#`V[r8C`4`Cf!
+3)#lrd*!!VJ!-)'i!%##!F!"-lKM`rfj1ANje6PB!!%MR$aJQEJ!81#i!#LKZ!""
+J!!%`2!3q,J!18NDmEJ!1C"*`!$!'FJ!b""!d#!#`0"J!CHC64lK(C"*`!$!(FJ!
+b""!d#!#`0"J!BZLq4Q0)F!!`"RS!'M3)!(!!-!Gb!$)''E3)!"J!F!!`"aQ&#!"
+`!$!'d)!k-`J!F!!`"p#!FJ!b"Y+"0l-)!"J!F!!`"p#!0i8)!'##Z%GQ"P*%B!!
+!SR!!-!4k!"Sd#!"`!$!(FJ!b""Qd#!!B!(!!-!FCK3J!F!!`"0#!1M-)!(!!-!I
+3J()!-J65J6Hc#!!B!(!!-!I3J$H&#!"`!$!(FJ!b"*!!JA)!-Li!$R3!0!G5JT+
+#XS"M(Lm,,`a`!$!(,`"`!$!%,`"1Z[lQ1!G54%r[!""J)#m,,`a`!$!Z!!i[!(!
+!-!G5J#m!6VVqa$e(!!j2l`!3F!!`,J!1FJ!b"*!!JA)"XS"Y!2l!61iBm2rS6Pj
+1G8j@rra)j`mB+'i!&!DZ!!!"*!!8*Qi!&%*'B#"`!$!')'i!#()!-JBCX!J!'!"
+`!$!'d)!hKJJ!-!C54VaZ!!jPfLm,,`a`!$!Z!!i[!(!!,`"1Z[j)3NC2l`!3B!3
+`"P*'['i!$Q31F!!`"R)!%M3)!%U"CqK`!#e!rraJGR!!-!C+J'-NF!!`"R)!%M3
+)!(!!-!C6J(3!&$3)!**#5-%J,[rmikJY32rmF!!`"RJ!'$3)!#SZrraq!'!1)!I
+ML()"`S@#J#i"iSd`"&0%5N"QkR!!-!E3J()!-M-)!#"Z!"$PJ5'('!!`"P*')#l
+rr&+ZrrbmEJ!1CB4-lKM`rq41ANje)PmJAk!P,S"U!N+A6Y%LAa)I-"p+!@F%TdC
+J!U0',SK1d5*I%Km`(b"I5J&R"+C(B!+L4dl4)Pp`!D'B6Y%L,`!%)#m!#%(k!!S
+bI!!#6[#5rQ!'6%%)!8je6VS!*#!"6R8L,`!%)#m!#%(k!!SbI!!#6[#5rQ!)6%%
+)!F0!6R9+J'X85S&V"Nkk!%C1G85"6VS!2N5"6R9%J%U"D`T1ZJ!`4)"%J8je4)&
+1ZJ!N4)"1G5)[!!3J,`!)3IS!#M*m!!*1m*,qB!K-33!"`d"1G6m"5%&+3@BF)J"
+#38K"C`U#edK"5%!`!8K!J0mb!%*!5%"1G8K"2S)[!c3!*J&b!8*!5%"Q$%K!-!*
+b!'!@dN&P%Y4#dB#`JfAdN!#$dN%)`3!!C1iQ(c3I6R8!!!%!N!0AHJ!!9RS!!!*
+k!3,f[$J`!*!$(!*U!"&%394"!*!$NPT&8Nm!N!1H4&*&6!#3!kT$6d4&!!-!YP0
+*@N8!N!2Q4%P86!!(!2*"6&*8!!B"8P088L-!!!'QGQ9bF`!"!E*69&)J!!%"bP"
+cCA3!!!(L8%P$9!!"!Hj%6%p(!!!#"NCPBA3!!!)53Nj%6!!!!Kj'8N9'!!!#+NP
+$6L-!!!)fBA9cG!!!!N)!!2rr+!#3#Irr#!!#M!#3"[rr+!!#[`#3"3,rrcJ!!X-
+"![DF!!(rra`!+SB"![D!!!$rrbJ!,-`"![A-!!2rr`!!35i"![A3rj!%!!!Y,!#
+3"!3"rrmJ!#dk!*!&KIrr*!!YZJ%#pR3!Krrr!!!Z%!#3"BErrb3!,Q!"![C-!)$
+rr`!!,Z)!N!@errmJ!#p!!*!&J[rr!!![XJ#3"!2SrrmJ!$!3!*!%"!(rrb!!-$`
+!N!@#rrmJ!$"-!*!&KIrr*!!`A!%#pP`!Krrr)!!`E!#3"BErrb3!-(`"![CB!)$
+rrb!!-)`!N!@"rrmJ!$#F!*!&J2rr!!!`V!#3"3(rrb!!-,m!N!8#rrmJ!$$d!*!
+%!J#3!b!!-4)!N!3#!3!()!!a)J#3"B$rr`!!-5i!N!9rrrm!!$9P!*!%!qMrr`!
+!0TJ!N!3$k2rr)!!rX`#3"[rr)!!rc`#3"B$rr`!!2pX!N!@!rrm!!$rl!*!&J2r
+r)!"!"J#3"[rr!!""#J#3"!C`FQpYF(3)a#"cG@CQDAM(93:
diff --git a/tcl/mac/tclMacResource.c b/tcl/mac/tclMacResource.c
new file mode 100644
index 00000000000..1695ca6ca1d
--- /dev/null
+++ b/tcl/mac/tclMacResource.c
@@ -0,0 +1,2205 @@
+/*
+ * tclMacResource.c --
+ *
+ * This file contains several commands that manipulate or use
+ * Macintosh resources. Included are extensions to the "source"
+ * command, the mac specific "beep" and "resource" commands, and
+ * administration for open resource file references.
+ *
+ * Copyright (c) 1996-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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, iOpenedResFile = false;
+ 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);
+ iOpenedResFile = true;
+ } else {
+ /*
+ * The default behavior will search through all open resource files.
+ * This may not be the behavior you desire. If you want the behavior
+ * of this call to *only* search the application resource fork, you
+ * must call UseResFile at this point to set it to the application
+ * file. This means you must have already obtained the application's
+ * fileRef when the application started up.
+ */
+ }
+
+ /*
+ * Load the resource by name or ID
+ */
+ if (resourceName != NULL) {
+ 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:
+
+ /*
+ * TRICKY POINT: The code that you are sourcing here could load a
+ * shared library. This will go AHEAD of the resource we stored away
+ * in saveRef on the resource path.
+ * If you restore the saveRef in this case, you will never be able
+ * to get to the resources in the shared library, since you are now
+ * pointing too far down on the resource list.
+ * So, we only reset the current resource file if WE opened a resource
+ * explicitly, and then only if the CurResFile is still the
+ * one we opened...
+ */
+
+ if (iOpenedResFile && (CurResFile() == fileRef)) {
+ UseResFile(saveRef);
+ }
+
+ if (fileRef != -1) {
+ CloseResFile(fileRef);
+ }
+
+ return result;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Tcl_MacConvertTextResource --
+ *
+ * Converts a TEXT resource into a Tcl suitable string.
+ *
+ * Side Effects:
+ * Mallocs the returned memory, converts '\r' to '\n', and appends a NULL.
+ *
+ * Results:
+ * A new malloced string.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+char *
+Tcl_MacConvertTextResource(
+ Handle resource) /* Handle to TEXT resource. */
+{
+ int i, size;
+ char *resultStr;
+
+ 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 behavior is controlled by the value of tokenPtr, and of the
+ * flags variable. For tokenPtr, the possibilities are:
+ * - NULL: The new token is auto-generated, but not returned.
+ * - The string value of tokenPtr is the empty string: Then
+ * the new token is auto-generated, and returned in tokenPtr
+ * - tokenPtr has a value: The string value will be used for the token,
+ * unless it is already in use, in which case a new token will
+ * be generated, and returned in tokenPtr.
+ *
+ * For the flags variable: it can be one of:
+ * - TCL_RESOURCE__INSERT_TAIL: The element is inserted at the
+ * end of the list of open resources. Used only in Resource_Init.
+ * - TCL_RESOURCE_DONT_CLOSE: The resource close command will not close
+ * this resource.
+ * - TCL_RESOURCE_CHECK_IF_OPEN: This will check to see if this file's
+ * resource fork is already opened by this Tcl shell, and return
+ * an error without registering the resource fork.
+ *
+ * Results:
+ * Standard Tcl Result
+ *
+ * Side effects:
+ * An entry may be added to the resource name table.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclMacRegisterResourceFork(
+ short fileRef, /* File ref for an open resource fork. */
+ Tcl_Obj *tokenPtr, /* A Tcl Object to which to write the *
+ * new token */
+ int flags) /* 1 means insert at the head of the resource
+ * fork list, 0 means at the tail */
+
+{
+ Tcl_HashEntry *resourceHashPtr;
+ Tcl_HashEntry *nameHashPtr;
+ OpenResourceFork *resourceRef;
+ int new;
+ char *resourceId = NULL;
+
+ if (!initialized) {
+ ResourceInit();
+ }
+
+ /*
+ * If we were asked to, check that this file has not been opened
+ * already with a different permission. It it has, then return an error.
+ */
+
+ new = 1;
+
+ if (flags & TCL_RESOURCE_CHECK_IF_OPEN) {
+ Tcl_HashSearch search;
+ short oldFileRef, filePermissionFlag;
+ FCBPBRec newFileRec, oldFileRec;
+ OSErr err;
+
+ oldFileRec.ioCompletion = NULL;
+ oldFileRec.ioFCBIndx = 0;
+ oldFileRec.ioNamePtr = NULL;
+
+ newFileRec.ioCompletion = NULL;
+ newFileRec.ioFCBIndx = 0;
+ newFileRec.ioNamePtr = NULL;
+ newFileRec.ioVRefNum = 0;
+ newFileRec.ioRefNum = fileRef;
+ err = PBGetFCBInfo(&newFileRec, false);
+ filePermissionFlag = ( newFileRec.ioFCBFlags >> 12 ) & 0x1;
+
+
+ resourceHashPtr = Tcl_FirstHashEntry(&resourceTable, &search);
+ while (resourceHashPtr != NULL) {
+ oldFileRef = (short) Tcl_GetHashKey(&resourceTable,
+ resourceHashPtr);
+ if (oldFileRef == fileRef) {
+ new = 0;
+ break;
+ }
+ oldFileRec.ioVRefNum = 0;
+ oldFileRec.ioRefNum = oldFileRef;
+ err = PBGetFCBInfo(&oldFileRec, false);
+
+ /*
+ * err might not be noErr either because the file has closed
+ * out from under us somehow, which is bad but we're not going
+ * to fix it here, OR because it is the ROM MAP, which has a
+ * fileRef, but can't be gotten to by PBGetFCBInfo.
+ */
+
+ if ((err == noErr)
+ && (newFileRec.ioFCBVRefNum == oldFileRec.ioFCBVRefNum)
+ && (newFileRec.ioFCBFlNm == oldFileRec.ioFCBFlNm)) {
+ /* In MacOS 8.1 it seems like we get different file refs even though
+ * we pass the same file & permissions. This is not what Inside Mac
+ * says should happen, but it does, so if it does, then close the new res
+ * file and return the original one...
+ */
+
+ if (filePermissionFlag == ((oldFileRec.ioFCBFlags >> 12) & 0x1)) {
+ CloseResFile(fileRef);
+ new = 0;
+ break;
+ } else {
+ if (tokenPtr != NULL) {
+ Tcl_SetStringObj(tokenPtr,
+ "Resource already open with different permissions.", -1);
+ }
+ return TCL_ERROR;
+ }
+ }
+ resourceHashPtr = Tcl_NextHashEntry(&search);
+ }
+ }
+
+
+ /*
+ * If the file has already been opened with these same permissions, then it
+ * will be in our list and we will have set new to 0 above.
+ * So we will just return the token (if tokenPtr is non-null)
+ */
+
+ if (new) {
+ resourceHashPtr = Tcl_CreateHashEntry(&resourceTable,
+ (char *) fileRef, &new);
+ }
+
+ if (!new) {
+ if (tokenPtr != NULL) {
+ resourceId = (char *) Tcl_GetHashValue(resourceHashPtr);
+ Tcl_SetStringObj(tokenPtr, resourceId, -1);
+ }
+ return TCL_OK;
+ }
+
+ /*
+ * If we were passed in a result pointer which is not an empty
+ * string, attempt to use that as the key. If the key already
+ * exists, silently fall back on resource%d...
+ */
+
+ if (tokenPtr != NULL) {
+ char *tokenVal;
+ int length;
+ tokenVal = (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/tcl/mac/tclMacResource.r b/tcl/mac/tclMacResource.r
new file mode 100644
index 00000000000..4bde129b4e9
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacShLib.exp b/tcl/mac/tclMacShLib.exp
new file mode 100644
index 00000000000..89361149844
--- /dev/null
+++ b/tcl/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/tcl/mac/tclMacSock.c b/tcl/mac/tclMacSock.c
new file mode 100644
index 00000000000..690bee29699
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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/tcl/mac/tclMacTest.c b/tcl/mac/tclMacTest.c
new file mode 100644
index 00000000000..06a66ba62c0
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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));
+ 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/tcl/mac/tclMacTime.c b/tcl/mac/tclMacTime.c
new file mode 100644
index 00000000000..f27a0425568
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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(&micros);
+ 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(&microOffset);
+ initalized = true;
+ }
+
+ Microseconds(&micro);
+
+#ifndef NO_LONG_LONG
+ microPtr = (long long *) &micro;
+ *microPtr -= *((long long *) &microOffset);
+ timePtr->sec = baseSeconds + (*microPtr / 1000000);
+ timePtr->usec = *microPtr % 1000000;
+#else
+ SubtractUnsignedWide(&micro, &microOffset, &micro);
+
+ /*
+ * 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/tcl/mac/tclMacUnix.c b/tcl/mac/tclMacUnix.c
new file mode 100644
index 00000000000..9f5f42aa94c
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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 *)&paramBlock;
+ DirInfo *dpb = (DirInfo *)&paramBlock;
+ 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(&paramBlock);
+ 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(&paramBlock);
+ 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/tcl/mac/tclMacUtil.c b/tcl/mac/tclMacUtil.c
new file mode 100644
index 00000000000..7d7867abe40
--- /dev/null
+++ b/tcl/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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#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. */
+ CONST 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;
+}