pax_global_header00006660000000000000000000000064124236565620014525gustar00rootroot0000000000000052 comment=04d83e1651aa2c2062e4423e9e08a3a01de9e7cc .gitignore000066400000000000000000000004661242365656200130670ustar00rootroot00000000000000.gdb_history *.[oa] *.gcda *.gcno *.gcov *.dylib *~ .DS_Store pkgIndex.tcl Makefile autom4te.cache config.* callgrind.out.* __test.log doc/junk.junk *.dSYM *.plist *-xotcl.html *-tcl.html xo-*.html xotclsh CVS nsfConfig.sh nxsh nxwish library/xotcl/xowish library/xotcl/doc/junk.junk doc/example-scripts/*.txt COPYRIGHT000066400000000000000000000050371242365656200123710ustar00rootroot00000000000000/* * Next Scripting Framework * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * This software is based upon MIT Object Tcl by David Wetherall and * Christopher J. Lindblad, that contains the following copyright * message: * * "Copyright 1993 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty." */ ChangeLog-2.0b3-2.0b5000066400000000000000000001327251242365656200141230ustar00rootroot000000000000002013-06-07 Gustaf Neumann - fix typo - remove doc target from "install-doc" since this takes a while in a "make install" - document tricky trace script setting in regression test - use tcl8.5 commands instead of 8.4 idioms - don't build doc always, since it takes a long time - documentation update and regeneration of html docs - add all tcl 8.5 cmds, nx methods + info methods - add missing tcl-keywords to nx-mode.el - adjust style files for document generation - added rules for generating pdf to ease release management - code cleanup - updated HTML renderings of examples 2013-06-06 Gustaf Neumann nsf.c: - fix a bug in SlotContainerCmdResolver() when NSF_MEM_COUNT is activated - fix a small memory leak for PER_OBJECT_PARAMETER_CACHING - all cases detectable with --enable-memcount=yes are fixed - fix git command in release instructions - fix label for debugging reference counts - updated TODO - reduce variable scopes nx::test: - change test cases to newer interface - don't use "namespace import nx::*" in test cases when not required nx::test: - use the standard configure interface for configuring instead of own version - changed from nx::Test to nx::test (user never has to know that nx::Test is a class). 2013-06-05 Gustaf Neumann - Eliminate all use of Tcl_GetStringFromObj() function. - fix typos, example code - further release work 2013-06-05 ssoberni Fixed some typos and suggesting some rewording 2013-06-05 Gustaf Neumann - initial checkin of nx-mode.el - preparing 2.0b5 release 2013-06-04 Stefan Sobernig Left a to-do in the nxdoc backend Completed update of nxdoc for NX 2013-06-04 Gustaf Neumann serializer.tcl - added flag -objmap to Serialzer.deepSerialize to make serializer usable for copying (-map is to coarse) - extended regression test 2013-06-04 Stefan Sobernig Adjusted nxdoc for the recent changes to NSF/NX 2013-06-03 Gustaf Neumann nsf.c - removed obsolete function AssertionAppendPrePost() - removed obsolete function NsfNSCopyCmdsCmd() and ::nsf::nscopycmd (handled now more general on scripting level in the "copy" method) nx.tcl: - "copy" method: fixed copying of class-level per-object methods - extended regression tests nsf.c - fixed a potential crash for objects having a wrapper-less alias defined 2013-06-03 ssoberni nsf.c - Make sure that the original error message is preserved when calling destroy after a failed CMD or INITCMD 2013-06-03 Gustaf Neumann nsf.c - fixed a bug in "info methods returns" in cases, where no returns info was available. - we can "/obj/ copy" now objects/classes containing * aliases * setter * ensemble methods * method protections Instead of handling cmd copying in NsfNSCopyCmdsCmd, it is replaced now by introspection. - extended regression test 2013-06-02 Gustaf Neumann nx::test: - deactivate calling overhead calculation, since this is not reliable (sometimes larger than the call). - improved handling of required configure parameters when classes are changed dynamically. When configure parameter are defined required, checking for the absence of required parameter was only performed at creation time. When objects were re-classed or their classes extended with required parameters, later calls to configure did not trigger exceptions. Now we check for the existence of the instance variable which addresses most of these points, but in future we might wish a more general solution (see comment for futures releases) - when creation with an required configure parameter failed, delete the half-baked object to avoid confusing states. - nx::test: show msg at start of test file 2013-06-01 Gustaf Neumann - changed multiplicity of mixin, object-mixin, filter, object-filter from 1..n to 0..n; rationale: when one has a list of eg. mixins, which should be passed, one has to test for the length before passing it, otherwise the empty list would generate an error. Allowing 0..n makes the usage simpler and the program shorter. nx.tcl: - renamed variable option "-config" to "-configurable" to make its intention clearer 2013-05-31 Gustaf Neumann - traits: added ability to turn on verbosity for traits by using nx::configure trait-verbosity on|off nx.tcl: - replaced functionality of "/obj/ configure" by "/obj/ info configure" to re-enable semantics of the plain configure method, even when called without arguments. "/obj/ info configure" is actually a convenience method to allow to write o1 info configure instead of o1 info lookup configure syntax package nx::class-method: - convenience package similar to nx::plain-object-method - allow for usage "C class method ..." in addition to "C object method". - made warnings configurable via nx::configure class-method-warning on|off - completed coverage and test cases package nx::plain-object-method: - made warnings configurable via nx::configure plain-object-method-warning on|off - completed coverage and test cases 2013-05-30 Gustaf Neumann nsf.c - fixed potential infinite loop in pattern matching for precedence lists - cget: make error message closer to tcl conventions - extended regression test 2013-05-29 Gustaf Neumann - adapt to object methods - prevent default value checking, when it is determined by a command - completed coverage if plain-object-method.tcl - provide warnings via plain-object-method.tcl via "nsf::log warn ..." 2013-05-28 Gustaf Neumann - renamed initblock parameter from __initcmd to __initblock - renamed nsf::configure parameter from "keepinitcmds" to "keepcmds" - saving "keepcmds" in an associative array named "__cmd(/parameternName)" to allow saving of multiple parmeters with less name clash danger (when application stays away from variables stating with double underscore) nx.tcl: - removed "info is .." since it might raise more questions than it solves - changed enumeration values for "-source" in "info lookup methods" "info lookup slots" "info methods" "info slots" from "all|application|baseclasses" to "all|application|system" for consistency with "nsf::my" and "nsf::dispatch" which uses "-system" as well 2013-05-26 Gustaf Neumann Traits: - changed from traits-as-objects to traits-as-classes. This allows for higher orthogonality of traits and class definitions and allows in principle traits for object-specific methods (not fully implemented/tested) - fixed property/variable inheritance in traits. - remove spurious debug line Cleanup of nsfAPI.decls - remove unneeded enumeration types - use "typeName" to shorten reported names of parameters - use camel case for reported names - changed ::nsf::parametersyntax(..) to ::nsf::parameter::syntax(..) - xotcl2: adjusted manual parameter syntax entries to new conventions 2013-05-24 Gustaf Neumann - added implementation for slots with traces+types for classes - exception for incorrect defaults are thrown during slot creation - extended nsf::is, added parameter * ?-configure? -- accept configure parameter options * ?-name /name/? -- provide a parameter name for error message - simplified nx.tcl by using new nsf::is - extended regression test - added partly implementation for slots with traces+types for classes - fixed cases, where valuechangedcmd (and the other traces) did not work with "configure" method. When slot traces are used, it cleans other traces for the same operations. - extended regression test 2013-05-23 Gustaf Neumann nsf.c: - fix crash when "nsf::my" is called with a single argument outside the object context. - fix assert - removed some TODOs from tests/parameters.test - parameter dispositions: We differentiate now between "initcmd" and "cmd": an "initcmd" is only evaluated once, but one can provide configure values for this parameter later. a "cmd" is executed on every evaluation, it is only possible to pass cmds to it. The trailing argument of the configure parameters (used e.g. for scripted object/class definitions) is now of type "cmd". Implementation not yet complete (object cases are not correct). - streamline documentation C-code Generator: - added "-typeName" for enumeration types that allows for disambiguation of enumerations with different argument names. Before that, the argument name determined the c-type of the enumeration. Therefore it was not possible to use argName "-type" for two different functions with a different list of enumerators. - renamed option "-methodtype" to "-type" in /obj/ info methods ... ?-type all|scripted|builtin|alias|forwarder|object|setter|nsfproc? ... /obj/ info object methods ... ?-type all|scripted|builtin|alias|forwarder|object|setter|nsfproc? ... /obj/ info lookup methods ... ?-type all|scripted|builtin|alias|forwarder|object|setter|nsfproc? ... - minor code cleanup 2013-05-22 Gustaf Neumann - streamlined AddSlotObjects() - cleanup of TODOs - nsf.c: handle full-qualified name for private slots (AddSlotObjects()) - extended regression test 2013-05-21 Gustaf Neumann - updated next-tutorial: * bring definitions of properties and variables up to date * fix "object methods" - XOTcl2: ensure public accessor when "-parameter ..." is used - extended parameter extractor: new functionality ::nsf::parameter get default /parameter/ ?/varname/? ::nsf::parameter get type /parameter/ /obj/ info parameter get default /parameter/ ?/varname/? /obj/ info parameter get type /parameter/ 2013-05-20 Gustaf Neumann - hopefully the last changes for ?object? method|variable|property: defined * "/obj/ delete object method" * "/obj/ delete object property" * "/obj/ delete object variable" - "info method syntax" returns now "/cls/ ...." - "info object method syntax" returns now "/obj/ ...." - updated next-migration guide to reflect changes from the configure reform - make name fully qualified for symmetry reasons - minor cleanup - nx-pp.tcl: fixed changed accessor handling, added cget to highlight words 2013-05-19 Gustaf Neumann - added new regression test info-variables.test 2013-05-19 Gustaf Neumann Method and configure parameter reform, Part 3: - added /cls/ info lookup variables -> list of handles /obj/ info lookup object variables -> list of handles /obj/ info variable definition|name|parameter /handle/ - nx.tcl: added forward compatible scripted implementation of "lmap" - nsf.c: handle names for private slots in pattern provided to AddSlotObjects(), used e.g. in "info lookup slots /pattern/" 2013-05-18 Gustaf Neumann - dropped "/obj/ info slot definition /obj/" in favor of "/slotobj/ definition" 2013-05-17 Gustaf Neumann - nx.tcl: handle "incremental" in slot reconfigure - nx.tcl: change defaultAccessor to "none" 2013-05-17 Gustaf Neumann - Method and configure parameter reform, Part 2: In order to streamline the interface further, we tried to follow the idea to use "... info /plural word/" to obtain a set of handles, and then a separate call to obtain the details. Therefore, we replaced /cls/ info slot objects /cls/ info slot definitions /cls/ info slot names /obj/ info object slot objects /obj/ info object slot definitions /obj/ info object slot names /obj/ info lookup slots by /cls/ info slots ?-type /type/? ?-closure? ?-source all|application|baseclasses? ?/pattern/? /obj/ info object slots ?-type /type/? ?/pattern/? /obj/ info slot definition /obj/ /obj/ info lookup slots ?-type /type/? ?-source all|application|baseclasses? ?/pattern/? 2013-05-16 Gustaf Neumann - Method and configure parameter reform /cls/ info configure parameters ?pattern? -> list of params /cls/ info configure syntax -> syntax output /obj/ info method parameters /methodName/ ?/pattern/? -> list of params /obj/ info method syntax -> syntax output /obj/ info lookup configure parameter ?/pattern/? -> list of params /obj/ info lookup configure syntax -> syntax output /cls/ info parameter list|name|syntax /param/ -> value 2013-05-14 Gustaf Neumann - avoid crash in case NsfParameterGetCmd() is passed a plain value 2013-05-13 Gustaf Neumann - minor documentation updates 2013-05-10 Gustaf Neumann - initialize stub-tables only once - syntax formatter: use /value/ for denoting placeholder in "... info method syntax ..." 2013-05-07 Gustaf Neumann - parametersyntax: make placeholders explicit - moved "/obj/ info slot definition|..." to "/obj/ info object slot definition|..." for consistency - provided "parametersyntax()" for "object mixin" and "object filter" 2013-05-05 Stefan Sobernig - win/makefile.vc: have the install target copy the public headers as well 2013-05-04 Stefan Sobernig - MinGW toolchains do not provide support _vscprintf(), so we limit the use of it to MSVC; tested NsfDStringPrintf under MinGW - Provide tcl library path to summary.tcl and remove intermediate file from repo - Fixed the Win32 logic for computing the Tcl_DString in NsfDStringPrintf(), tested for MSVC under x86 and amd64 2013-05-04 Gustaf Neumann - updating next-tutorial to object method syntax 2013-05-03 Gustaf Neumann - updated migration guide - update to new naming 2013-05-03 ssoberni - Fix the 86-specific tests to reflect the object-modifier reform 2013-05-03 Gustaf Neumann - try to print arguments in one sweep in NsfDStringPrintf() - remove "extern" declarations 2013-05-02 ssoberni - Revised NsfDStringPrintf() for portability to non-C99-compliant C runtimes (MSVC); remains to be tested 2013-05-02 Gustaf Neumann - first and incomplete update of migration guide to new syntax 2013-05-01 Stefan Sobernig - Re-enabling deletion of intermediate file in makefile - Remove legacy build artifacts - Provide native Win build support for MSVC++ and MSVC, using the Tcl 8.6 name templates. Tested using Visual Studio 12 (VC and name in version 11). See makefile.vc for build instructions for Win32 and Win64. 2013-04-29 Gustaf Neumann - bumped version number to 2.0b5 - tested with NaviServer and OpenACS (new version of nx needs as well a newest NaviServer, since ns_cache implementation needs to be objectified; newest NaviServer version works as well with older nx) - fix xotcl instance method serialization (still not covered in regression test) - renamed "package require nx::class" to "package require nx::class-method" in analogy to "nx::plain-object-method" - added "/obj/ object variable" and "/obj/ object property" 2013-04-28 Gustaf Neumann MongoDB - added "nx::mongo::db drop collection /name/" - returning status from "nx::mongo::db remove" as success (0 or 1) - adjust to object interface - reduce verbosity - add error messages for slot lookup failures Updated MongoDB interface - upgraded to c-driver 0.7.1 - tested with MongoDB 2.4.4-pre - new commands: * mongo::run * mongo::cursor::find * mongo::cursor::next * mongo::cursor::close - adapted interface for c-driver 0.7.1 (e.g. new optional name for mongo::index - nsfPointer.c: add parameter to Nsf_PointerDelete() for allowing optional freeing - reduced default verbosity - moved NsfConfigEnabled from nsf.h to nsfInt.h (no need to be part of the public interface) - moved NSF_ARGPARSE_* macros to nsf.h (since arg parser is public and uses these flags) 2013-04-23 Gustaf Neumann Object-method Reform: - changed interface to object specific commands by requiring an ensemble named "object". The rational behind is essentially to use always the same info command to retrieve object specific methods, no matter whether these are defined on a plain object or an a class object (does not break the "contract" what e.g. "info method" returns). Now we define methods via: /cls/ method foo {args} {...body...} /cls/ object method foo {args} {...body...} /obj/ object method foo {args} {...body...} Similarly, aliases, forwards and mixins are defined, e.g. /cls/ mixin add ... /cls/ object mixin add ... /obj/ object mixin add ... /obj/ require object method ... The same change propagated as well to the "info" method. Now we have: /cls/ info methods ... /cls/ info object methods ... /obj/ info object methods ... Similar, the object parameterization uses /cls/ create obj -object-mixin M /cls/ create obj -object-filter f /metacls/ create cls -mixin M1 -object-mixin M2 /metacls/ create cls -filter f1 -object-filter f2 - as a consequence, a) "/cls/ class method ...", "/cls/ class alias ...", "/cls/ class forward ...", "/cls/ class filter ...", "/cls/ class mixin ...", "/cls/ class info ..." "/obj/ class method require method ..." "/obj/ class method require public method ..." "/obj/ class method require protected method ..." "/obj/ class method require private method ..." were dropped b) "/obj/ method ....", "/obj/ alias ....", "/obj/ forward ...." "/obj/ filter ...." "/obj/ mixin ...." "/obj/ info method*" "/cls/ create obj -mixin M" "/cls/ create obj -filter f" "/obj/ method require method ..." "/obj/ method require public method ..." "/obj/ method require protected method ..." "/obj/ method require private method ..." were dropped - added package nx::class to allow optionally the "class" notation "/cls/ class method ..." (and friends, see (a)), and "/cls/ class info ... - added package nx::plain-object-method to allow optionally plain method b) "/obj/ method ...." (and friends, see (b)) - add support to slots to use ensemble methods as setters 2013-04-17 Gustaf Neumann - allow explicit unsetting of -per-object flag in 0-argument -flag=value notation 2013-04-05 Gustaf Neumann - reduce variable scope 2013-03-25 ssoberni - Improved wording in comment on NsfConfigEnabled() - Irgh. Had forgotten about two-level stringification to provide configuration macro expansions as strings in NsfReportVars() 2013-03-25 Stefan Sobernig - Adding a helper macro NsfConfigEnabled() to test for active/inactive build-time configurations; rewrote NsfReportVars() accordingly. This makes NSF compilable under MSVC (VC11). Thanks are due to Stephan Adelsberger for reporting the issue. 2013-02-19 Gustaf Neumann - ByteCompiled(): fix indentation of preprocessor #if statements - ByteCompiled(): ensure result setting in case HAVE_TCL_COMPILE_H is not defined - improve wording in rosetta example 2013-01-22 Gustaf Neumann - more code cleanup - reduce variable scopes - remove EXTERN declarations from function definitions 2013-01-08 Gustaf Neumann - Update to latest TEA. - Follow new naming convention for auto-tools (using configure.ac) - silence compiler warnings 2012-12-12 Gustaf Neumann - replace Tcl_GlobalEval by Tcl_Eval, since Tcl_GlobalEval will be removed in futures versions of Tcl 2012-11-26 Stefan Sobernig - Checked built and test suite against Tcl 8.6rc2 (core-8-6-0-rc) under MinGW32; removed temporary fix for bug #3401422 2012-11-19 Gustaf Neumann - quote filename in content disposition 2012-11-16 Gustaf Neumann - replace nx::configure option defaultPropertyCallProtection by defaultAccessor - protect trf against leading dashes 2012-11-15 Gustaf Neumann - deactivate tests in library file 2012-11-14 Gustaf Neumann - add additional "convertto" when generating zip files from strings 2012-10-30 Stefan Sobernig - The asciidoc-related configuration artifacts, especially for Tcl/NSF-specific code highlighting, are now contained in the source distribution and integrated with the respective make targets. Besides a running asciidoc installation, there is no further configuration need. Tested with asciidoc 8.6.8 2012-10-28 Stefan Sobernig - Extending test case to cover slotassign issue - Adding a test and a temporary fix for the ::nsf::is issue in setCheckedInstVar 2012-10-13 Gustaf Neumann - Make sure to NS_EXPORT Ns_ModuleVersion for people using still the old-style AolServer module. 2012-10-04 Gustaf Neumann - make assumptions in code explicit 2012-10-03 Gustaf Neumann nsf.c: - handling of method names in error messages from nsfAPI.h. Make sure that error message is generated with the actual method name. 2012-10-02 Gustaf Neumann nx.tcl: - property has now a boolean non-positional argument "-config" /obj|cls/ property ?-accessor value? ?-config boolean? ?-incremental? ?-class value? spec ?initblock? in symmetry with "-accessor" (parameter option "noconfig" is still needed to flag nsf for variables that should be initialized, which are not configurable - "/obj|cls/ info slot definitions" returns a full command (containing flags and property|variable) - extended regression test 2012-09-30 Gustaf Neumann - remove debug output nsf.c: - factored out ParameterMethodForwardDispatch() to call a parameter method defined as a forwarder the same way from "configure" and "cget" - extended regression test - improve comment 2012-09-29 Gustaf Neumann - extended regression test - changed name "/obj|cls/ slot info definition" to "/obj|cls/ slot info definition" since result is a set - dropped parameter method "properties" - dropped "/obj/ info properties" (since "properties" or "variables" are returned") nx.tcl: - change parameter name in "/cls/ info parameter ... ?pattern?" from "name" to "pattern" 2012-09-28 Gustaf Neumann - added functionality for "cget" to call parameter-methods (e.g. "... cget -class"). The method cget calls either "/slot/ get ..." (when slot=... is provided in the parameter spec) or it assumes that the method without argument returns the value - added "::nsf::object::property /obj/ volatile" to query whether a object is volatile or not - "/obj/ cget -volatile" returns now the volatile state of the object - factored out ParameterMethodDispatch() from OConfigureMethod() - extended regression test 2012-09-27 Gustaf Neumann - invalidation of per-object parameter cache * on mixin changes and * on deletion/adding of per-object slots - deactivate PER_OBJECT_PARAMETER_CACHING per default (flipping this parameter makes regression test more than 20 faster). - extended regression test - NsfObjInfoObjectparameterMethod(): return not only the first matching parameter, but the list of all matching ones. The last optional argument was renamed from "name" to "pattern" accordingly - rename invalidateobjectparameter -> parameter:invalidate::classcache - rename invalidateobjobjectparameter -> parameter:invalidate::objectcache - bring cmds into alphabetical order 2012-09-26 Gustaf Neumann - first draft of per-object parameter caching (for per-object-mixins and per-object properties). nsf.c: - fix potential bad interaction between per-object mixins and per-class caching of object-parameters 2012-09-25 Gustaf Neumann - don't blindly register all object/class methods for XOTcl Property Reform Part 2: better handling of per-object properties nsf.c: - changed "/class/ __objectconfigure" to "/obj/ __objectconfigure" to be able to handle per-object properties on classes properly. - renamed "info method parametersyntax" -> "info method syntax" - renamed "/obj|cls/ info method parametersyntax" into "/obj|cls/ info method syntax" - replaced "::nsf::methods::class::info::objectparameter" by "::nsf::methods::object::info::objectparameter" - new command "::nsf::parameter::specs ?-configure? ?-noposargs? slotobjs": convert provided slotobjs into a list of parameter specs - new command "::nsf::parameter::get list|name|syntax parameterspec": convert parameter spec into syntax form, or retrieve pieces of information from it (can be extended in the future) - added more or less generic list handling functions TclObjListFreeList(), TclObjListNewElement() and TclObjListAdd() used by "::nsf::parameter::specs" - replaced "::nsf::method::property /obj/ -per-object /name/ slotcontainer ?value?" by "::nsf::object::property /obj/ slotcontainer ?value?" - added "::nsf::object::property /obj/ hasperobjectslots ?value?" nx.tcl: - new info methods * "/obj/ info lookup parameter definitions" * "/obj/ info lookup parameter names" * "/obj/ info lookup parameter list" * "/obj/ info lookup parameter syntax" - changed "/cls/ info parameter definition ?name?" into "/cls/ info parameter definitions ?name?" since ir returns a list. Still, "list" or "syntax" won't be plural 2012-09-22 Gustaf Neumann - renamed timing variable from "time" to "ms" - added timing statistics 2012-09-21 Gustaf Neumann - generalize handling for per-object-properties - added additional file for regression test 2012-09-19 Gustaf Neumann Property reform part 1: - disallow protection modifiers for "properties" and add new flag "-accessor" to "property" and "variable" This changes definitions like Class create C { :property {a a1} :public property {b b1} :protected property {c c1} :private property {d d1} } to Class create C { :property {a a1} :property -accessor public {b b1} :property -accessor protected {c c1} :property -accessor private {d d1} } since "properties" are always public accessible over the "configure" and "cget" interface, but the accessors methods might not be public. The value of the accessor might be also "none" (specifying explicitly that no accessor is wanted) or "", which means: use the default. Same holds for "variable" - disallow parameter option "incremental" and change it to a flag of the property or variable. The motivation for this is due to the fact, that "incremental" is a property of the accessor, and not of the value. old: Class create C { :property foo:int,incremental :variable bar:int,incremental } new: Class create C { :property -incremental foo:int :variable -incremental bar:int } - disallow "public class property" and friends since these are not needed - removed parameter property "noaccessor" - removed "nx::configure defaultPropertyCallProtection" and method hook "__default_property_call_protection" - introduced "nx::configure defaultAccessor" and method hook "__default_accessor" - for the time being, "defaultAccessor" is "public" for NX and XOTcl, will be changed to "none" in NX - extended regression test (new file properties.test) 2012-09-15 Gustaf Neumann nsf.c: - fixed parmeter syntax for required nonpos args - deactivate deletion of methods via nsf::object::delete during shutdown to avoid missing slot forwarders called from destructors nx.tcl: - remove debugging output - renamed testlog file, remove it on "make clean" nsf.c: - made argument of cget required nx.tcl: - added Tk-style methods "configure" and "cget" - added additional regression test set for cget and configure nsf.c: - implemented cget as a configure-like method, dropped ensemble method variant nx.tcl: - simplified "/obj|cls/ delete method" due to resolving capabilities in nsf::delete::method - fixed regression test to run all test again correctly xotcl2.tcl: - made destructor of Connection more robust such it does not depend on accessor methods. 2012-09-14 Gustaf Neumann nsf.c: - extended nsf::method::delete to handle ensemble names nx.tcl: - added tk/incr-tcl style cget methods on class/object levels. - improve copy handling with other child-types of the slot container working - make sure to ignore non-slot-type objects in slot introspection - worked on regression test until "methods.test". others are missing, but maybe reconsideration 2012-09-13 Gustaf Neumann - updated 34 copyright notices nx.tcl: - rename the following internally called methods (not for XOTcl). alloc -> __alloc dealloc -> __dealloc objectparameter -> __objectparameter recreate -> __recreate - from these methods, only __objectparameter is defined per default, the others are defined on demand changes to allow efficient tk/incr tcl compatible configure/cget - refactored code to allow to parameterize handling of required flag for parameters - don't flag an error when configure is called on an initialized object (logic: if the object is initialized, configure must have been called before, and the required action must have been already taken). - rename nx::Object.configure to nx::Object.__configure to free method name "configure" for an e.g. tk-like configure 2012-09-10 Gustaf Neumann - finalize transition from old code 2012-08-28 Gustaf Neumann - fix frame scoping for myvar/myproc 2012-08-10 Gustaf Neumann - allow to change title of a scripted document - make doc beautifier more robust for invalid lists - use explicit return in tutorial example scripts 2012-08-01 Gustaf Neumann - improve documentation - use "nsf::relation $cl superclass" instead of "$cl configure -superclass" - minor documentation updates nx::test: - added summary at the end of "make test" via log file - updated .gitignore 2012-07-31 Gustaf Neumann nx.tcl: - set multiplicity for mixins and filters by default from 1..n to 0..n to avoid error messages, when e.g. empty mixin lists are configured. Setting empty mixin-lists is strictly speaking not necessary, but this eases the code in cases the lists are sometimes empty. - handle cyclical superclass/class dependencies during object system finalize - extend regression test 2012-07-18 Gustaf Neumann nsf.c: - ignore in internal calls to "dealloc" protection settings - handle cyclical class dependencies during object system finalize - extend regression test 2012-07-18 Stefan Sobernig - library/nx/nx.tcl: Following the code review, implemented a first scheme for handling traces in setCheckedInstVar. This gives us consistent behavior for defaults and traces (valuechangedcmd) across classes and objects. - tests/parameters.test: Adjusted the relevant tests - Pending: setCheckedInstVar currently uses ::nsf::is to validate the value (e.g., default value) to be set. However, it might get passed parameter options which do not fit the NSF_DISALLOWED_ARG_VALUECHECKED filter. 2012-07-17 Gustaf Neumann - define method "proc" of ::xotcl::Attribute in terms of ::xotcl::Object proc - make sure to have gcc branch hints in generated code - honor default type in "info slot names" 2012-07-17 ssoberni - Addressed a number of issues in the NX slot infrastructure (which revealed themselves when running Mark's BusinessActivities implementation in XOTcl2 mode): - generic/nsf.c: Initcmd blocks (as set for registering per-class slot traces) were subjected to argument checks causing unexpected behavior (e.g., attempts of unboxing initcmds as Tcl lists or of checking initcmds against value types). This was fixed by exempting NSF_ARG_INITCMD params from argument checking in ArgumentCheck() explicitly. - library/nx/nx.tcl: For per-class parameters, the use of valuechangedcmd effectively overruled the default value specified (simply because the generated initcmd holding the trace statements took the place of the default value). While for defaultcmd and for valuecmd the parallel use of default is forbidden, the valuechangedcmd semantics allow for specifying a default, in principle. This was fixed by providing a default-setting statement in the initcmd. - tests/parameters.test: Added tests to cover the above (and beyond). - library/xotcl/library/xotcl2.tcl: Extended the backwards compatibility of the hybrid XOTcl2/NX slots infrastructure to the XOTcl 1 interface: This includes support for inline -proc statements and instvar. This compatibility level corresponds to the slot interface as documented (by examples) in the XOTcl language ref and the manual. - library/xotcl/tests/slottest.xotcl: Added tests for the above compatibility enhancements. 2012-07-16 Gustaf Neumann - allow instance property inheritance for traits 2012-07-11 Gustaf Neumann - Don't advertise to use "configure" method for calling class/superclass in documentation sice we have better ways. 2012-06-27 ssoberni - Replaced erroneous check using the object-specific rather than the class-specific options in the query mode of ::nsf::relation with class-filter. This prevented [::nsf::relation /cls/ class-filter] from returning any filter info. As a consequence, the incremental interface of the filter slot failed (e.g., [/cls/ filter add aFilterProc] effectively reset the class-filter cmd list instead of adding to it). Thx are due to Markus Moldaschl for reporting the issue. - Aligning class-filter and object-filter behavior on errors during filter additions to the state-preserving behavior of object-mixin and class-mixin. - Adding basic tests on object-filter/class-filter (::nsf::relation, filter slot interface) and specific ones to cover the above issues. 2012-06-25 Gustaf Neumann - added a SlotContainerCmdResolver() to avoid interaction of slot names with names of callable tcl commands. Without the SlotContainerCmdResolver() the call to "list" in a property named "list" leads to a call to the container object ::Test2::slot::list instead to the intended ::list. The implementation is not perfect, since it ignores the namespace path inside the slot container. - added regression test. - adding gcc helper files to ignore list - cosmetical documentation changes 2012-06-01 Gustaf Neumann nsfShadow.c - bump MethodEpoch when a tcl ::rename command happens on a nsf method (which might be cached in a Tcl_Obj) This fixes a crash reported by Arthur Schreiber nsf.c: - make NsfInstanceMethodEpochIncr() and NsfObjectMethodEpochIncr() accessible from all files using nsfInt.h - remove experimental code (backslash escaping for "," in parameter option parse 2012-05-22 Gustaf Neumann - remove dependency of generated stub file from nsfInt.h - fix compilation when dtrace is activated (missing parenthesis, many thanks to Victor Guerra for noticing) - document private properties in tutorial and migration guide - improve wording in documenting - extend regression test 2012-05-22 Stefan Sobernig - configure, configure.in: Under win, the CLEANFILES missed a pattern for object files - nsfInt.h: Based on the BUILD_nsf convention, the internal header introduced a TCL_STORAGE_CLASS macro placing the dllimport attribute (under Win/MinGW builts). As a consequence, shared and stub builds ended up with function declarations annotated with dllimport (which for these cases is either unnecessary or even breaks builds because the dllexport counterpart is missing, obviously). As nsf.h includes a sane DLLIMPORT/DLLEXPORT handling (copied over from tcl.h), I simply removed the special macro handling from nsfInt.h. - NsfProcStub(): Use the EXTERN macro rather than extern directly 2012-05-21 Gustaf Neumann - added documentation for "/obj/ info name" to migration guide and .nxd file - adding more comments to examples in migration guide - added design study ruby-mixins.tcl to example-docs and regression test - minor polishing 2012-05-17 Gustaf Neumann - change "#!/bin/env" into "#!/usr/bin/env" 2012-05-14 Gustaf Neumann - increase backward compatibility for ::xotcl::Attribute by defining the public methods "set", "exists" and "istype" 2012-05-14 ssoberni - apps/utils/nxdoc: fix shebang path to env ... - generic/nsf.c, ObjectDispatch(): Re-arranged the handling of fully-qualified method dispatches with the selectors resolving to existing objects and, in particular, nested (namespace-qualified) objects. Prior to that, dispatches such as "::C ::parent::child" were not covered appropriately: ::parent was reported as valid regObj and so the dispatcher continued to dispatch to the cmd "::parent::child" in the self-context ::parent::child ... with unwanted side-effects (e.g., XOTcl's unknown handling was bypassed). - tests/methods.test: Added basic tests on the (current) handling for such ::* dispatches and a specific test set on the intended XOTcl behavior. 2012-05-13 Gustaf Neumann - start error messages with a lower case word for consistency and to follow closer to Tcl's conventions - added "private property foo" - extended regression test - allow parens in property names (array syntax) - added "/obj/ info name" (as alternative to "namespace tail [self]") - use newer style of tcl-obj interface instead of sprintf 2012-05-11 Gustaf Neumann - implement escaping for comma in value of parameter options: escaping in values can be achieved via duplicating the comma. - extend regression test 2012-05-04 ssoberni - library/xotcl/library/xotcl2.tcl: Provided a virtual slot "multivalued" to establish backward compatibility with XOTcl1; also, allow old-style class creation based on unknown for ::xotcl::Attribute - library/xotcl/tests/slottest.xotcl: Added some tests on backward compatibility for XOTcl slots 2012-04-20 Gustaf Neumann - explorative implementation of object method dispatches with KEEP_CALLER_SELF and no NSF_PER_OBJECT_DISPATCH - extend regression test 2012-04-16 Gustaf Neumann nsf.c: - refactor ObjectCmdMethodDispatch() for clarity - prepare work on object method dispatches with KEEP_CALLER_SELF and no NSF_PER_OBJECT_DISPATCH - remove debug line 2012-04-11 Gustaf Neumann - fix off-by-one error - add regression test for keepcallerself with and without perobjectdispatch - fix typo 2012-04-10 Gustaf Neumann - serializer: * pertain perobjectdispatch and keepcallerself in serializer * extend regression test nsf.c: - generalize stack monitor by handling growing and shrinking stacks - refactor into function CheckCStack() - serializer: * make [::Serializer deepSerialize -ignoreVarsRE "" $o] behave like [::Serializer deepSerialize $o], since learn@wu depends on that, and a value for ignoreVarsRE of empty looks more like an omitted value than a regular expression, that should match everything. * extended regression test - extended regression test 2012-04-05 Gustaf Neumann - added compile macro NSF_STACKCHECK to provide stack monitoring/debugging (especially useful for multi threaded programs, where stack is more limited) - make ::nsf::log more robust for aolserver/NaviServer, since ::ns_log is not always around when an output is needed - don't hide call to make.tcl 2012-03-09 Gustaf Neumann - protect serial generation for parameters via mutex - fix compilation when compiled without threads (many thanks for r.zaumseil for noting this). 2012-03-05 Gustaf Neumann - tcl86.test: better implementation of method "each", cleanup and extension of enumerator tests 2012-02-29 Gustaf Neumann - extended regression tests for yield - implemented "next" for ruby-like enumerators (each needs still more work) 2012-02-27 Gustaf Neumann - pass property name to slot "initialize" method to conform with the interface to "assign", "get" ... (all these receive the property name as well) - allow slot "initialize" method to be protected (handled similarly to "init") 2012-02-24 Gustaf Neumann nsf.c: - added object parameter option "slotinitialize" - renamed object parameter option "invokesetter" -> "slotassign" - call slot.assign instead of setter of object - removed restriction on nosetter/invokesetter: nosetter can be used in connection with slotassign - added regression test for slot.initialize 2012-02-22 Gustaf Neumann - Fixed a bad interaction between Tcl's apply (pushing lambda frames) and the variable resolvers. The variable resolver was not able to resolve variables, while the command resolver was still working correctly. - Extended regression test - Improve wording in instructions - Improve comments - Documented dependencies between configure flags and feature activation cpp macros 2012-02-17 Gustaf Neumann - improve error message to indicate, that nsf refuses to overwrite (redefine) some cmds - don't allow method to overwrite child object - extended regression test - documented new feature as incompatibility with XOTcl 1 2012-02-16 Gustaf Neumann - don't allow object creation to overwrite non-object cmds (e.g. procs) - improve debug line 2012-02-16 ssoberni - library/lib/nxdoc-xowiki.tcl: Finished and polished the xowiki backend for nxdoc. Some tests regarding relative paths and the tree-view generation are pending ... - library/lib/nxdoc-xowiki.tcl: Use the built-in base64 facility if running under Tcl 8.6 - Due to re-introducing nested-object-as-method semantics lately, nxdoc started stumbling over name conflicts between nested entity hierarchies (i.e., the entity object representing "/cls/ class info class" ...) and equally-named introspection calls on such entities (e.g., a "info class" call). I made these calls more robust my using -system dispatches; which is fine as long as we do not have sth. like "/obj/ info eval". Generally speaking, the risk of such naming conflicts discredits the use of object nesting in nxdoc; should eventually be revised. Or, as work-around: Using a filter on Entity instances to protect/redirect critical calls. - tests/serialize.test: Added some basic regression tests, in particular to challenge the filter options provided by Serializer->deepSerialize() and Serializer->all(). - Included a test for commit 595e6a2. - Along the line, two minor issues where fixed: 1) Set Serializer->ignore() to public, otherwise the various external message sends to this method would fail; 2) Object-serialize() did not distinguish between serializing a per-object or per-class container properly; as a consequence, per-object containers were not restored at all. Add a test covering this. - library/serialize/serializer.tcl: Due to the split between serializer and object system serializer, the ignore settings (passed as object variables, i.e., ignoreVarRE) got lost. This is fixed now ... 2012-02-16 Gustaf Neumann - treating incompatible forwarding to slot vs. slot option noaccessor - extended regression test - use Tcl's EXTERN macro instead of "extern" 2012-02-04 Gustaf Neumann update release procedure 2012-02-03 Gustaf Neumann - mv next-tutorial (source, images) into own subdirectory doc/next-tutorial/ - Bump version number of head to 2.0b4 - improve wording in documentation ChangeLog-2.0b5-2.0000066400000000000000000001633421242365656200136750ustar00rootroot000000000000002014-10-28 Gustaf Neumann - Don't complain at final ObjectDeletion about object refcounts unless we are in development mode - adding .3 man pages to ease installation - adding .1 man pages to ease installation 2014-10-28 ssoberni - Effectively remove getstubs from makefile.vc - Apply stubs* resolution without getstubs to makefile.vc 2014-10-27 Gustaf Neumann - use newest versions, re-generated stub files - adjust genstubs target to subdir logic - handle nsfUtil.c from version-specific subdirectory - binor polishing 2014-10-27 ssoberni - NsfDStringPrintf(): Fix vargs processing under VC 12 2014-10-27 Gustaf Neumann - get rid of getstubs - provide actual pkgIndex files - keep an internal list of Tcl_Objs of type mixinrefs for classes used as mixins. This list is used for avoiding stale entries in cases the mixin class is destroyed. - remove redundant and different prototype 2014-10-27 ssoberni - Bump version numbers in nx.tcl and xotcl2.tcl; adjust license statements - Rearrange 'make getstubs' slightly, so that we do not require the stub files present in generic/* for the build machinery to work properly - Done revising license statements in generic/* 2014-10-27 Gustaf Neumann - fix a heisenbug probably showing sometimes up due to more aggressive Tcl_Obj sharing in newer versions of tcl 8.6 2014-10-27 ssoberni - Correct and unify licensing statements in header files - Fix copyright statements in stubs* files - Fix copyright dates 2014-10-26 ssoberni - Silence instructions, do not install xowish.in/ xotclsh.in - Bump version number in win build files - Trigger generation of pgk index scripts - Add EXTERN to calm VC12, add missing C files from makefile.vc 2014-10-25 ssoberni - Use correct slashing - Render file paths in a platform-independent manner - Make sure genstub.tcl is called in makefile.vc - Update nmakehlp (required for VC 12+) 2014-10-24 Gustaf Neumann - set the right version number to 2.0.0 - Make dtplite configurable in Makefile, e.g. make "DTPLITE=/usr/local/ns/bin/tclsh8.5 /usr/local/ns/bin/dtplite" man - regenerate documentation - bump version number to 2.0 (also in .man files) - add Changelog for 2.0 release - updated for mongo-c-driver 1.0.2 and MongoDB 2.6.5 - minor changes were necessary: MongogDB does not allow to delete a capped collection, one has to use drop - white-space changes - move variable declaration to the front 2014-10-23 ssoberni - Fixing listings in shell-wrapper manpages - Fix a typo reported by lintian - Fix two more wordings lintian complained about - Revise manpages; add copyright notes 2014-10-22 Gustaf Neumann - reduce number of unused symbols - fix a possible double refcount increment for argument converter (e.g. for normalizing input values). This could lead to small memory leaks. 2014-10-22 ssoberni - Continue man corrections - Work on corrections for Object.man & friends, based on Thorsten's proof-reading - Remove an nxdoc artifact - Remove unneeded make variables - Revive nsfConfig.sh: The paths expanded during a configure run were broken, partly due to substituting obsolete variables 2014-10-21 ssoberni - Use revised interpreter directive throughout 2014-10-21 Gustaf Neumann - fix final refcounting bug: it was possible that classes contained in Tcl_Objs of type mixinreg were not freed, although their reference-counter became 0; now all test cases free all tcl_objs and referenced data structures allocated from nsf 2014-10-21 ssoberni - First solution to provide a multi-step resolution of a Tcl interpreter in our shell wrappers. Uses some bash scripting to walk a list of candidates: 1) interp in build directory, 2) interp in install directory, 3) tcl interp in PATH. Where appropriate (cases 2 & 3), restricts search to a specific tinterp version. By relying on a generic shebang line (/bin/sh) we do not run into package-dependency restrictions on Debian. So a win-win? 2014-10-21 Gustaf Neumann - fix (probably quite old) memory leak when parameter passing fails to reclaim temporary Tcl_Objs - fix memory leak with virtual parameters (resolved via args) with ref-counting 2014-10-21 ssoberni - Adding man pages for shell wrappers - Adjust file extension for nroff files to reflect manpage section 2014-10-20 ssoberni - Fix the remaining shebangs - Fix shebang lines - Remove any nxdoc artifacts from repo, to ease Debian packaging (incl. binary js is not accepted by lintian 2014-10-20 Gustaf Neumann - fix execution with --enable-memcount=yes: second argument of NsfMemCountAlloc() and NsfMemCountFree() may be empty - don't put macro arguments between parens when these are used for concatenation - remove // comments 2014-10-19 Gustaf Neumann build system: - don't call genstubs from configure, since Debian does not seem to have genstubs.tcl installed. Now, we pre-generate the stub files for tcl8.5 and tcl8.6 and copy the "right" version depending on the configured version. - avoid old-style prototypes 2014-10-15 ssoberni - Done with basic princexml integration, improve Makefile integration - Provide first stylesheet based on HTML markup by doctools HTML backend - Integrating manpages into build setup 2014-10-14 ssoberni - Improve wording - Add a draft section on self-references - Improve wording in overview diagram of Class doc item - Working on overview diagram for Class doc item - Add to the overview diagram - Adding overview diagram to Object doc item - Cleanup namespace export statement - Reflect plural reform in all doc items 2014-10-10 ssoberni - Doc item on nx::configure - Doc item on nx::current - Doc item for nx::next 2014-10-10 Gustaf Neumann - minor cleanup of "//"-comments 2014-10-07 ssoberni - Add some documentation on 'info info' - Make 'info info' behaving as expected again 2014-10-06 ssoberni - Minuscule refactoring, have DispatchUnknownMethod() reset the interp unknown-state directly, rather than its clients. 2014-10-06 Gustaf Neumann nsf.c: - make types for bit operations unsigned (mostly flags) 2014-10-05 Gustaf Neumann nx.tcl: - simplify the info ensembles on nx::Object or nx::Class significantly, by making use if ensemble-next. - delete "info unknown", since this is not called. - extend regression test nsf.c: - implement a new approach to error reporting in ensembles: instead of trying to find the "right" place to report the best "error", compute the longest valid ensemble prefix from all the stack frames. 2014-09-29 Gustaf Neumann - make compilation clean again - improve safety of macro arguments 2014-09-29 ssoberni - Adding a test showing the yet to solve unknown-handling issue in info ensembles 2014-09-28 ssoberni - Work-in-progress on sanitizing unknown handling in ensembles 2014-09-28 Gustaf Neumann - remove useless distinction from the past - revise sanity check for tcl _objs in 8.6 once one: length field might be garbage - add alternative test for sanity checking of tcl_objs: when bytes not NULL, length must be 0 or higher 2014-09-27 Gustaf Neumann nsf.c: - remove redundant definition - reduce variable scope - make sure to follow nonnull assumptions - improve cppcheck flags nsf.h - In Tcl 8.6.1 it might be that a Tcl_Obj has length > 0 but bytes == NULL. We have to relax out tcl_obj-sanity test for this case to avoid false-positives from assertions() 2014-09-22 Gustaf Neumann - documentation update to reflect recent changes 2014-09-17 Gustaf Neumann documentation: - add current args to migration guide - fix cut&paste error: replace "current currentclass" by "current calledclass" nsf.c: - remove redundant null check for object and add assertion - remove duplicate entry for returns_nonnull - serializer: keep parameter properties in xotcl parameter lists 2014-08-08 ssoberni - Revise Object documentation, align with output from quant.tcl helper - Reflect recent parameter syntax change in corresponding test - Revise Class documentation, align with output from quant.tcl helper - Provide corrections for some syntax strings 2014-07-09 Gustaf Neumann - no need to "set nodashalnum for int types" - extended regression test 2014-06-23 Gustaf Neumann nsf.c: - provide error messages for ambiguous abbreviations - extend regression test (now 5460 tests) 2014-06-23 ssoberni Revised life-cycle section further 2014-06-23 Gustaf Neumann nsf.c: - dropped unused object::info::is - renamed ::nsf::methods::class::info::filtermethods -> ::nsf::methods::class::info::filters ::nsf::methods::object::info::filtermethods -> ::nsf::methods::object::info::filters ::nsf::methods::class::info::mixinclasses -> ::nsf::methods::class::info::mixins ::nsf::methods::object::info::mixinclasses -> ::nsf::methods::object::info::mixins 2014-06-23 ssoberni - Change overview illustration on object life cycle in Class.man - Adjust for changes in plural reform 2014-06-22 Gustaf Neumann nx.tcl: pluralism reform part 3 - introduced simple plural form "mixins" and "filters" for introspection - moved differentiated interface into slot methods. the slot methods "get" stay symmetrically to "set", but additional methods "classes" or "methods" are used like "guard" to obtain partial results of the mixin and filter specs - changed info methods /cls/ info mixin classes -> /cls/ info mixins /cls/ info filter methods -> /cls/ info filters /obj/ info object mixin classes -> /obj/ info object mixins /obj/ info object filter methods -> /obj/ info object filters - dropped /cls/ info mixin guard /cls/ info filter guard /obj/ info object mixin guard /obj/ info object filter guard - added /cls/ mixin classes /cls/ filter methods /obj/ object filter methods /obj/ object mixin classes 2014-06-21 Gustaf Neumann nx.tcl: - make all __* system methods in nx redefine-protected - let "nsf::configure objectsystem" return handles and protections as well 2014-06-20 Gustaf Neumann nx.tcl: pluralism reform part 2 - changed methods /cls/ info subclass -> /cls/ info subclasses /cls/ info superclass -> /cls/ info superclasses /cls/ mixin ... -> /cls/ mixins /cls/ filter ... -> /cls/ filters /cls/ object mixin ... -> /cls/ object mixins /cls/ object filter ... -> /cls/ object filters - changed configure parameters /cls/ configure -mixin -> /cls/ configure -mixins /cls/ configure -filter -> /cls/ configure -filters /obj/ configure -object-mixin -> /obj/ configure -object-mixins /obj/ configure -object-filter -> /obj/ configure -object-filters - added handling for calling relation slot with unknown sub method 2014-06-20 ssoberni - Revising lifecycle section; adding TODOs - Restructuring __* doc fragments to an life-cycle section 2014-06-19 Gustaf Neumann nsf.c: - allow abbreviated nonpos args - change name of relation slot "superclass" to "superclasses". (part of a planned change to use plural for setvalued parameters, "info superclasses" and similar changes for mixins/filters will probably follow) 2014-06-18 Gustaf Neumann nsf.c: - define means to protect "undefined" internally-directly called methods __alloc and __dealloc in nx. This is achieved mostly via a an additional value in a method declaration in ::nsf::objectsystem::create. Example: -class.dealloc {__dealloc ::nsf::methods::class::dealloc 1} - extend regression test 2014-06-18 ssoberni - Adding missing doc fragments on __* methods for Object and Class 2014-06-17 Gustaf Neumann nsf.c: - relax the meaning of noleadingdash to allow negative numbers - rename noleadingdash to e.g. nodashalnum 2014-06-16 Gustaf Neumann - extend regression test - update parse context - remove debugging output - improve argument name 2014-06-15 Gustaf Neumann - extended regression test - cleanup, reduce verbosity - minor cleanup - allow to configure verbosity in test sets genttclAPI.tcl: - added option "-flags", which can be used for every parameter. example: .... -flags NSF_ARG_NOLEADINGDASH .... - experimental: use NSF_ARG_NOLEADINGDASH for pattern "info subclass" to improve error messages. nsf.c: - checked, that all CallDirectly() cases, where method is dispatched (no direct call) are covered by the regression tests - avoid double memcpy() in dispatch recreate by using ObjectDispatch() rather than CallMethod() - removed memcpy() in call-directy for "create" - some more cleanup 2014-06-14 Gustaf Neumann nsf.c: parameter passing reform - don't pass full argument list for filtering methods calle further methods from C (such as new/create/... ) to allow processing of e.g. "--" in "new" to separate arguments to "new" cleanly from arguments passed to "create". Now we can use e.g. "C new -- -childof 123" in case class C has a property "childof". - extend c-generator to accept option "-objv0" to pass the original "objv[0]" to the called command. Since we had previously "allargs", we have to pass the objv[0] now differently - more thorough checking ISOBJ(someObj) macro for asserts (use "assert(ISOBJ(someObj))" instead of just "assert(someObj)") - extend regression test 2014-06-13 Gustaf Neumann - fix parse context initialization 2014-06-12 Gustaf Neumann - collecting arguments 2014-06-11 Gustaf Neumann - fix shutdown comparison - don't invalidate class-level param caches during shutdown - add assertions for class colorings 2014-06-11 ssoberni - Clarify usage of 'current methodpath' in defaultmethod of ensembles - Editing TODO - Add doc fragment on new - Adding missing info sub-method docs for Class, document -dependent flag, and method create 2014-06-11 Gustaf Neumann - simplify test - use 3-argument version of NsfMethodNamePath() 2014-06-11 ssoberni - Add basic test for unexpected-#-args error in ensembles - Adding to the state of the method-path introspection reform; introducing CallStackGetFrame() which can also be used to implement CallStackGetTopFrame 2014-06-10 Gustaf Neumann nsf.c: - fix name paths in error messages triggered from ArgumentParse() - move NsfMethodNamePath() out of NsfUnexpectedArgumentError() and NsfUnexpectedNonposArgumentError() - no need to call NsfMethodNamePath in NsfArgumentError() - move NsfMethodNamePath() out of NsfUnexpectedArgumentError() and NsfUnexpectedNonposArgumentError() - move NsfMethodNamePath() out of NsfObjWrongArgs() - added option "-dependent" to "info subclass" - extended regression test 2014-06-10 ssoberni - Working on info sub-methods for Class 2014-06-09 Gustaf Neumann - white-space change nsf.c - base MixinInvalidateObjOrders() on DependentSubClasses() and avoid the need of using a separate hash table for class-mixin handling. The new implementation is several times faster and improves the speed of the functions CleanupDestroyClass(), SuperclassAdd() and NsfRelationClassMixinsSet(). Adding a class-mixin to ::xotcl::Object in OpenACS is more than 4x faster. - remove obsolete function MixinResetOrderForInstances() - rename ResetOrderOfClassesUsedAsMixins() to ResetOrderOfObjectsUsingThisClassAsObjectMixin() - used consistently DependentSubClasses() instead of TransitiveSubClasses() for invalidations. - extend regression test 2014-06-08 Gustaf Neumann - fix a few more cases, where required accessor method "get" was missing - omit warnings, when value of "expected" looks like a non-positional parameter 2014-06-07 Gustaf Neumann nsf.c: - improve performance of MixinInvalidateObjOrders() by about 30% by recompiling the list of the dependent classes over and over again, since MixinInvalidateObjOrders() iterates over the full list already. - Document NsfRelationClassMixinsSet() and add nonnull declarations and the usual assertions() 2014-06-06 Gustaf Neumann - added experimental code feature CYCLIC_MIXIN_ERROR 2014-06-05 Gustaf Neumann nsf.c: - new function DependentSubClasses() to determine all classes that inherit from a given class. The result extend TransitiveSubClasses() by including class mixin relationships. - simplify NsfParameterCacheClassInvalidateCmd() by using the new function DependentSubClasses(). With the new implementation, NsfParameterCacheClassInvalidateCmd() is as efficient as before without to MostGeneralSuperclass optimization (but being complete now) when working on the root classes (an more efficient on subclasses). 2014-06-04 Gustaf Neumann - fixed error message for forward ... onerror and method paths. The command "C object mixin" returns now "::C object mixin add|clear|delete|get|guard|set" and not "::C mixin add|clear|delete|get|guard|set" as before. 2014-06-03 Gustaf Neumann - add ".add" file for mongodb interface - use cget interface - rename nsf::parameter:invalidate::classcache -> nsf::parameter::cache::classinvalidate nsf::parameter:invalidate::objectcache -> nsf::parameter::cache::objectinvalidate reasons: (a) remove single colon inside the name, (b) put verb to the end - use functions IsRootClass(), IsRootMetaClass() and IsBaseClass() to access object/class properties - add gcc attribute "pure" nsf.c: - cleanup of NsfParameterInvalidateClassCacheCmd(): performance improvements. After the fixing of indirect mixin validation, performance of invalidation went up by a factor of 5. At least, in some important cases (invalidation of rootclasses like nx::Object / xotcl::Object), we are again on the same level as before (actually slightly faster). 2014-06-02 Gustaf Neumann - reduce variable scopes - fix error message - add target for cppcheck with same arguments as for compilation 2014-06-02 ssoberni - Removing traces of nxdoc 2014-06-01 ssoberni - Fixing an example script for changes in getter/setter reform 2014-06-01 Gustaf Neumann - updated migration guide and tutorial to reflect recent changes - minor cleanup - improve spelling - update of TODO and regression test - adapt mixinof.test to the additional information - transform mixinof.test to newer style regression test with automated object deletion - add test file nsf-cmd.test - ignore generated .txt files nx.tcl: - removed /cls/ info configure parameters /cls/ info configure /cls/ info syntax Use e.g. "/cls/ info lookup parameters create" instead 2014-06-01 Gustaf Neumann - nsf.c: - Let "/cls/ info mixinof -closure" return as well implicit mixin classes to make it easier to determine class dependencies. Example: nx::Class create M0 nx::Class create M1 -superclass M0 nx::Class create M2 -superclass M1 nx::Class create C nx::Class create D -superclass C C mixin add M2 # M2 is mixed into C, and implicitly into D # # Since M2 is a subclass of M1, classes C and D depend as well # on M1 and M0, as seen in the heritage: ? {C info heritage} ":M2 ::M1 ::M0 ::nx::Object" ? {D info heritage} ":M2 ::M1 ::M0 ::C ::nx::Object" # previously, only "M2 info mixinof -closure" showed the # mixin relations, even with "-closure", while M1 and M0 did not. ? {M2 info mixinof -closure} "::C ::D" # now these show the same relations (in this example). ? {M1 info mixinof -closure} "::C ::D" ? {M0 info mixinof -closure} "::C ::D" 2014-06-01 Gustaf Neumann - handle ensembles in info nx::help - harden code mongodb: - upgrade to the newly released version 0.96 of the c-driver - replace deprecated function mongoc_collection_delete by mongoc_collection_remove - tested with MongoDB v2.6.1 2014-05-31 Gustaf Neumann - provide a minimal nx::help - provide contextObj when required argument is missing nsf.c: - added options to filter output from ::nsf::cmd::info parameter options (args, syntax, parameter) - deleted: - "/obj/ info lookup configure parameters ?pattern?" - "/obj/ info lookup configure syntax" - added: - "/obj/ info lookup parameters /methodName/ ?pattern?" - "/obj/ info lookup syntax /methodName/ ?pattern?" This covers as well - "/obj/ info lookup parameters configure|create|new|... ?pattern?" - extend regression test - remove dead code - white-space change - improve error message nsf.c, gentclAPI.tcl: - new argument types "virtualobjectargs" and "virtualclassargs" for context-specific argument resolutions: when a context object is provided, arguments of type "virtualobjectargs" are determined based on the slots applicable for the object (like "... lookup ..."), arguments of type "virtualclassargs" are resolved against a class. These types are used as follows: /obj/ configure /virtualobjectargs/ /cls/ create /name/ /virtualclassargs/ /cls/ recreate /name/ /virtualclassargs/ /cls/ new ?-childof /obj/? /virtualclassargs/ This new feature allows us to provide better error messages and to make much of the "... info ... configure parameter ..." infrastructure much less important. - For "virtualclassargs" we need the functionality to obtain from the C-Code based on a class the configure parameters applicable to objects of this class. - add argument "-context ..." to "cmd::info" to pass the context object (so far the only place where the context-object is used) - object system configuration parameters changes: new: -class.configureparameter new: -object.configureparameter removed: -class.objectparameter 2014-05-30 Gustaf Neumann nsf.c - renamed parameter::get -> parameter::info - renamed method::get -> cmd::info gentclAPI.tcl: - handle duplicated domains by folding these to a single definition nsf.c: - added command nsf::method::get. Rationale: provide a central place to obtain information about all kind of method handles, which might be - scripted and c-based methods - nsf::procs - plain tcl procs - cmds (with and without parameter definitions) - make results of ListMethod() robust against missing information (e.g. plain tcl cmds, missing object registrations, etc.) - factor out common code for ListMethod call sites for per-object methods, instance methods and procs/cmds to ListMethodResolve() - return errors from failing converter registrations - extend regression test (new test set nsf-method.test) 2014-05-30 ssoberni - Done with first version of Class.man 2014-05-29 Gustaf Neumann - also drop class level "info configure" nx.tcl - drop short form "/obj/ info configure" for now - make output of "/obj/ info lookup configure syntax" equivalent to "/obj/ info configure" - updated TODOs - updated documentation - simplify slot parameter settings for object-mixins and object-filter lightly by omitting redundant method name - minor cleanup 2014-05-29 ssoberni - Some polishing of Object.man - Done with 'info method *' ensemble - Working on 'info object *' ensemble 2014-05-28 Gustaf Neumann - make error-checks as unlikely to succeed - we must replace ::get command with ::set commands for reestablishing relationships - return class of desired object in nsf::parameter::get when provided nsf.c: - extend nsf::parameter::get to obtained more detailed information for objects/classes/metaclasses/baseclasses and specified types - extend regression test 2014-05-28 ssoberni - Add doc fragment about 'info variable *' 2014-05-28 Gustaf Neumann nx.tcl - switch from "/obj/ info parameter" -> "nsf::parameter::get" to reduce potential semantic confusion of info options. "info parameter" was not object/class specific at all, but is just a syntax extractor nsf.c - force again literal "-guard" in a "mixinreg" type to avoid potential confusions - Base nsfRelationSet for mixins on cmds rather than TclObjs to avoid confusions and duplication of guard detection logic - Add interp to NsfMixinregGet() and NsfFilterregGet() to be able to return error messages - return more error message from mixinreg converter - provide at least minimal support for "funny class names" (i.e. containing spaces) - FinalObjectDeletion: don't enforce namespace = 1 for cases with weird namespace deletion order - extended regression test 2014-05-28 ssoberni - Amend doc of filter|mixin clear, re-instate -guard in filter|mixin set - Adding doc for the two new lookup sub-methods 2014-05-27 Gustaf Neumann - added methods "info lookup filters ?-guards? ?/pattern/?" and "info lookup methods *-guards? ?/pattern/?" - extended regression test - have "filter|mixin clear" return the list of former|removed filters|mixins. - disallow arguments for "... info configure" 2014-05-27 ssoberni - Complete info lookup doc - Adjust filter|mixin method for recent changes; working on info ensemble doc 2014-05-27 Gustaf Neumann - improve handling of space in object names nsfObj.c: - allow to omit "-guard" within arguments to flag definition of a filter/mixin guard - extended regression test - name parameter option "slotset" instead of "slotassign" - use "mixin|filter clear" instead of "mixin|filter unset" 2014-05-26 Gustaf Neumann nsf.c: - fixed unary argument passing notation for "-nocomplain" of nsf::var and for 42 other options of other commands nx.tcl: - added flag -nocomplain to "/obj/ /prop/ unset ?-nocomplain?" - renamed nsf::relation to nsf::relation::set and added nsf::relation::get in accordance with nsf::var::get nsf.c: - added nsf::var::get and "::nx::var get" to provide selector based interface for variable reading (used in slot-method get of nx::VariableSlot) - cleanup of nx.tcl and TODO 2014-05-26 ssoberni - Updated doc fragments to reflect recent changes 2014-05-26 Gustaf Neumann nsf.c: - finish implementation of NsfForwardPrintError() - use NsfForwardPrintError() in ForwardArg() for all error messages traits: - define simple setter methods "requiredMethods" and "requiredVariables" to avoid to "set" these explicitly - fix compilation for OpenSolaris (e.g. OmniOS) 2014-05-25 Gustaf Neumann - fix compilation for OpenSolaris (e.g. OmniOS) 2014-05-23 Gustaf Neumann - cleaned up relation slot mixin variants nx.tcl: - reworked error message generation of slot-forwarder (list all available slot methods with prefix value=) xotcl2: - use xotcl::RelationSlot instead of nx::Relationslot in xotcl2 (we can more value=assign here). - fix load paths for sub-libs (e.g. mongodb) in regression test nx.tcl: - add slot method value=unset to nx::RelationSlot and nx::VariableSlot - extended regression test - added likely/unlikely to result == TCL_OK etc. xotcl2: - use value=set instead of value=assign - simplify "-parameter" implementation - add setters for "name", "domain", and "default" to xotcl::Attribute for backward compatibility mongodb: - by directing calls to the setter, it is now more complex to determine the true calling object for an converter, since the set operation might be routed though the slot object. It would be nice to have framework support for this. - fix 2 error messages - provide shorter tracebacks by using "return -code error ..." rather than "error ..." nsf.c: - fix one more subtle case of an error being swallowed: for xotcl, the constructor returns the list of residual arguments. In case there was an error, it was possible that the returned residual arguments overwrote the error message in the interp result 2014-05-23 ssoberni - Adjust variable doc for recent changes - Extending mixin/filter doc to cover guards - Adding filter doc fragment - Adjust mixin doc for recent changes - Adjusting property doc to reflect recent changes 2014-05-22 Gustaf Neumann - don't delete system slot ::xotcl::Attribute on cleanup nx.tcl, xotcl2.tcl: - use value=* as names for internally called and forwarder-called accessor methods - disallow "assign" for nx::variableSlots nsf.c: - rename default slot methods add/get to value=add/value=get - provide an error message, when referring to a non-existing slot object 2014-05-21 Gustaf Neumann nx.tcl: - use set/get/add as slot methods for get/configure/incremental operations - demangle slots for nx/xotcl2 further xotcl2: - use assign/get/add as slot methods for get/configure/incremental operations - use object system configuration for -slot.get and -slot.set - allow configuration of internally called "slot.get" and "slot.assign" methods via objectsystem::create 2014-05-20 Gustaf Neumann - enforce usage of "get" for all slots in nx - TODO: check, what in detail "parameter" in xtocl2 inherit from nx::variableslot (e.g. needsForwarder?) - enforce using "set" for filter/object-filter in slot operations (same as for mixins) nx.tcl: - remove setter methods from BootstrapVariableSlots - reducing interface of BootstrapVariableSlots by setting methods protected 2014-05-18 Gustaf Neumann - put test cases for all kind of mix setter / getter together in one test case - reduce verbosity nx.tcl: - add "set" as a method name for relation slots - implemented relation slot "mixin" and "object-mixin" via "slotassign" to disallow "/obj/ mixin /value/" and "/obj/ object mixin /value/" to use instead "/obj/ mixin set /value/" and "/obj/ object mixin set /value/" while keeping "configure" and "cget" working. This has the advantage that "/obj/ mixin set" does not try to replace the mixin chain by "set" - adapted regression test - TODO: check, if we need the explicit "slotassign"? isn't the presence of the slotObj sufficient? maybe "-forwardToSlot" in relationSlots? - TODO: demangle "slotassign" in "ObjectParameterSlot protected method getParameterOptions" and check interactions - TODO: to the same as -mixin and -object-mixin to -filter and -object-filter - TODO: clean up relation slot mixin variants - TODO: do we really like the fact that we have to write now "B mixin set M2" instead of "B mixin M2"? - TODO: should we disallow "B mixin" and enforce instead of "B mixin get" ? - TODO: we could as well allow "B mixin clear" instead of "B mixin set {}" - TODO: allow "set" for variable slots as well. Do we need "assign"? nsf.c: - allow parameter option "method=" for slotassign as well. rationale: this allows to use the parameter "forwardername" to specify a different forwarder target method (e.g. in case of object-mixins). The method is used both in "configure" and "cget". - allow method name to consist of max tow words in relation slots (e.g. "-methodname {mixin set}"} 2014-05-17 Gustaf Neumann - extend bagel example slightly - finalize dropping of setter methods for nx - show info line for every test case 2014-05-16 Gustaf Neumann - add flag "-onerror" to nsf::forward::method to handle errors during dispatch of a forwarder (not all error messages of forwarder are already transformed) - added log-level Info which prints always, e.g. for "-verbose" flag of forwarder - drop setter-rule from properties (use always forwarder) - drop "/obj/ /prop/" and "/obj/ /prop/ /value/" in favor of "/obj/ /prop/ get" and "/obj/ /prop/ assign /value/" to achieve better orthogonality with e.g. incremental properties 2014-05-14 Gustaf Neumann - replace empty-named arrays by dicts 2014-05-13 Gustaf Neumann - fix change of semantics when default multiplicity (1..1) is combined with lower bound preserving incremental (results in 1..n instead of 0..n previously) 2014-05-12 Gustaf Neumann - make sure that nsfDtrace.h has a newer timestamp than nsfDtrace.d - preserve lower bound of multiplicity when incremental is added - extend regression test - added more test cases for multiplicity and incremental nx.tcl: - Define method "value" as a slot forwarder to allow for calling value-less slot methods like e.g. "get" despite of the arity-based forward dispatcher. - extend regression test 2014-05-12 ssoberni - Adding doc fragment on mixin method 2014-05-12 Gustaf Neumann - remove over-eager nonnull declaration - remove done entries from TODO 2014-05-11 Gustaf Neumann - add fixes for tcl 8.6 - finish non-null handling for tcl 8.5 - continuing with nonnull assertions, various small cleanups 2014-05-10 Gustaf Neumann - complete nonnull assertion cleanup to 80% of nsf.c - complete nonnull assertion cleanup to 33% of nsf.c - improve source code documentation - simplify SuperclassAdd() - improve code documentation - complete nonnull assertion cleanup to 50% of nsf.c - simplify FilterInvalidateObjOrders() and FilterRemoveDependentFilterCmds() - complete nonnull assertion cleanup to 33% of nsf.c 2014-05-09 ssoberni - Add argument to last discussion item - Add another incremental TODO - Adding discussion items 2014-05-09 Gustaf Neumann - finishing checking of first 25% of of nonnull assertions in nsf.c - added still more nonnull assertions 2014-05-09 ssoberni - Drafted a variable doc fragment, and revised the property's one to make the (subtle) difference clear 2014-05-09 Gustaf Neumann - added shortcut for MixinSearchProc when mixinOrder is NULL - work on nonnull 2014-05-08 Gustaf Neumann - let the c-code generator produce as well nonnull assertions - commenting item - made nsf::is using the "-strict" option when tcl's "string is" is called. 2014-05-08 ssoberni - Adding incremental doc to property doc fragment 2014-05-08 Gustaf Neumann - complete nonnull+assert adding in .c-files other than nsf.c 2014-05-08 ssoberni - Adding todo on multiplicities 2014-05-08 Gustaf Neumann - add asserts to nsfError.c - fixed all over-eager nonnull cases for optimizing with gcc and clang (works up to -O3 except gcc 4.9.0) 2014-05-07 Gustaf Neumann - added nonnull for args of type Tcl_Command and ClientData - add asserts for nonnull 2014-05-07 ssoberni - Starting documenting info ensemble 2014-05-06 Gustaf Neumann - provide nonnull statements for all functions in nsf.c - some more code cleanup - complete asserts due to nonnull 2014-05-05 Gustaf Neumann - update more copyright notices - update copyright notices - update copyright notice nsf.c: - simplify few inner code pieces based on assertions - add several more assertions based on nonnull specifications. - use nx rather than xotcl2 terminology in nsf::method::forward 2014-05-04 Gustaf Neumann - renamed "-methodprefix" to "-prefix" in nx, since the prefix can be applied as well applied to a cmd. 2014-05-04 ssoberni - Adding doc fragment about require method 2014-05-03 ssoberni - Adding a todo - nsf.c, NsfForwardMethod(): Make sure that a 2nd argument is available for prepending the methodprefix. Earlier, NSF crashed in case of a missing 2nd arg. - Added some basic tests on -methodprefix. 2014-05-03 Gustaf Neumann - use nonnull variable attributes for prototypes (nsf.h, nsfInt.h, nsf.c) nsf.c: - de-spaghetti precedence computations for multiple inheritance and improve documentation - get rid of // comments - stubsPtr can't be NULL there 2014-05-02 Gustaf Neumann - remove false alarm - deactivated "-onerror", since its semantics are expressible via try/catch, and there are no regression tests for xotcl and nx, and we could not find any script that uses this 2014-05-01 ssoberni - Adjusting forward doc, adding TODO - forward.test: Adding some tests 2014-05-01 Gustaf Neumann - use in forwarders "-frame object" instead of "-objframe" in nx for consistency with other calls (e.g. dispatch). Other values for "-frame" are not allowed. (btw, XOTcl has "-objscope") - move "checkalways for forwarders and aliases" to RFEs and comment it forwarders: - use for output of forward ... -verbose NsfLog(...NSF_LOG_NOTICE...) instead of fprintf() to make it redirect-able - RFE "provide %method" as keyword like %proc" was already implemented. Dropping %proc would break XOTcl2 compatibility. - adding a test case 2014-04-30 ssoberni - Continue working on forward doc, adding some tests - Adding some todos - Working on Object.man, in particular forward - library/nx/nx.tcl: Pulling out method contracts (pre- and postconditions) from NX for the time being. Corresponding tests have been commented out or, if applicable, turned into XOTcl2-specific tests. 2014-04-30 Gustaf Neumann - finalize autoconf quoting 2014-04-29 Gustaf Neumann - fix configure.ac quoting - stick closer to TEA conventions (keep tclconfig-sh in tclconfig directory) - remove obsolete version of install-sh, copy manifested version to mongodb library - fix more configure quoting 2014-04-18 ssoberni - Adding doc fragment on 'method' 2014-04-14 ssoberni - doc/Object.man: Adding documentation on alias (doc/alias.man.inc) and delete (doc/delete.man.inc) 2014-04-14 Gustaf Neumann small introspection reform: - Introspection for commands and arguments did not work for cmds defined in sub-packages (such as mongodb). We keep now this information in hash tables and maintain a slim interface for this. - fix generation of pkgIndex.tcl for mongodb 2014-04-08 ssoberni - Some more refactoring - Refactor the include file slightly, so that Object/Class-specific parameters etc. of a shared method documentation can be provided in the respective Object.man/Class.man files - Continue working in man pages, introducing a first shared section using [include] and [vset] 2014-04-06 Gustaf Neumann - tested with MongoDB v2.4.10 and J mongodb-c-driver 0.94.1 - moving .m4 files to subdirectory as recommended - update generated configure files - replace obsolete autoconf macros - replace obsolete autoconf macros - update to must recent build files (tcl.m4 and install-sh) from tcl source repository 2014-04-05 Gustaf Neumann build-process: - replace make.tcl by the much simpler mkIndex.tcl: * Does not use pkg_mkIndex * Does not load binary files (problem for cross compiling) * Requires package provide with constant in one line. 2014-04-03 Gustaf Neumann - reduce variable scope - remove redundant NULL tests - improve safety mof macro ObjStr() 2014-03-31 Gustaf Neumann - improve code documentation - add some more tests to the regression test suite 2014-03-29 ssoberni - generic/nsf.c: Avoiding excessive allocation/deallocation of temporary hash tables when invalidating per-class objparam caches in NsfParameterInvalidateClassCacheCmd(). 2014-03-29 Gustaf Neumann - extend regression test 2014-03-29 ssoberni - generic/nsf.c: Use GetAllClassMixinsOf() instead of TransitiveSubClasses() when invalidating per-class caches. Added two tests. 2014-03-28 Gustaf Neumann nsf.c: - remove obsolete code - invalidate parameter caches of subclasses on NsfParameterInvalidateClassCacheCmd unless during shutdown. Otherwise some classes might not become aware of properties added later to superclasses. - extended regression test 2014-03-25 ssoberni - Adding to Object.man: cget, configure, move, copy, ... 2014-03-23 Gustaf Neumann nx-mongo: - added command "::mongo::status /mongoConn/" - extended regression test 2014-03-22 Gustaf Neumann nsf.c: - fix case, where NsfDStringPrintf() failed (when print llength including \0 was 1 byte longer than print buffer) - make sure, that the list kept for the cached parameter is just built from unshared objects; otherwise Tcl append will abort nx.tcl: - new package "nx::volatile" - don't define configure parameter "-volatile" per default; use "package req nx::volatile" instead - don't define per method "volatile" per default; use "::nsf::method::require ::nx::Object volatile" instead - get rid of -volatile in nx.tcl and serializer - update/extend regression test nsf.c: - fix case, where NsfDStringPrintf() failed (when print llength including \0 was 1 byte longer than print buffer) - added mongo::cursor::aggregate 2014-03-21 Gustaf Neumann - added mongo::collection::stats - extended regression test 2014-03-19 Gustaf Neumann nx-mongo: - fixed surprising compiler message "alignment of array elements is greater than element size" when using e.g. "bson_iter_t i[1]" - some c-code cleanup - tested with mongodb-c-driver 0.92.3 2014-03-17 ssoberni - Started working on an authoritative man page for nx::Object, still cleaning up and still tweaking the doctools markup 2014-03-15 Gustaf Neumann - mongodb's c-driver changed version number from 0.93.0 to 0.92.1; followed the change in README 2014-03-14 Gustaf Neumann - tested with mongodb-c-driver 0.93.0 2014-03-12 Gustaf Neumann - since mongoc_gridfs_get_files is supported since today by the mongo-c-driver, we do not need it private implementation any more. all dependencies on private header files are removed by now. 2014-03-09 Gustaf Neumann - adjust to newest version of mongo-c-driver - remove one dependency for private header file 2014-03-07 Gustaf Neumann - move close of the pseudo comment for syntax highlighter out of quoted block 2014-03-04 Gustaf Neumann xotcl2: - prevent strange error messages, when "abstract" is called with quotes in the argument list. 2014-03-02 Gustaf Neumann - represent BSON_TYPE_REGEX as pairs (regexp + options) in the Tcl representation to preserve regular expression options - update to newest version of mongo-c-driver and libbson from Github nx-mongo: - optional support for mongodb connection pools (compile time macro USE_CLIENT_POOL controls this, per default, this is on) - allow to pass "-metadata" to gridfile::create to ease metadata attachment to grid-files - some convenience improvements in nx-mongo.tcl (ability to ignore attributes in "bson encode", ability to unset attributes in gridfs, ...) - bump version numbers of nsfmongo to 0.3 and nx-monogo to 0.5 2014-02-26 Gustaf Neumann nsf.c: - let [current methodpath] return full path (similar to -path option in "info methods" - handle collateral damage in regression test due to changed result of "current methodpath" - add traits.test to the regression tests nx::traits: - handle ensemble methods correctly (use full method path for resolution) - add new regression tests for traits nsf.c: - fix small memory leak in multiple inheritance code. - all regression tests rerun cleanly with --enable-memcount=yes 2014-02-25 Gustaf Neumann - raise an error, when an ensemble methods redefined a plain method - add incr/decr refcount for callInfoObj in unknown handling - deactivated suspicious assert() in NsfMethodNamePath() nsf.c: - change name of enumeratorConverterEntry to Nsf_EnumeratorConverterEntry, move it with NSF_ARG_* flags to tcl.h to make it available in derived modules using the converter - Added editor hints for a more uniform appearance - change configure flags from --with-mongodb to --with-mongoc=... and --with-bson nx-monogo: - Updated the mongo-c-driver and libbson to the actual tip version from Github (this is a significant change, since 10gen essentially changed the officially supported c-driver of MongoDB) - mongo-c-driver was more or less new-implementation, since structure and names changed in the mongo-c-driver substantially, several functions were added, several were dropped. The new interface supports now e.g. mongo URIs, and should be faster (by using collection objects instead of connection handles) - Although the low-level nsf interface changed significantly, the high level (nx level) interface remained unaffected. - Configure has now --with-mongoc=... and --with-bson instead of --with-mongodb - New commands: mongo::collection::open /mongoConn/ /dbName/ /collectionName/ mongo::collection::close /collection/ mongo::gridfs::store_string /content/ /name/ /contentType/ - Make use of the new collection handle mongo::count /mongoConn/ /ns/ ... -> mongo::collection::count /collection/ ... mongo::index /mongoConn/ /ns/ ... -> mongo::collection::index /collection/ ... mongo::insert /mongoConn/ /ns/ ... -> mongo::collection::insert /collection/ ... mongo::query /mongoConn/ /ns/ ... -> mongo::collection::query /collection/ ... mongo::remove /mongoConn/ /ns/ ... -> mongo::collection::delete /collection/ ... mongo::update /mongoConn/ /ns/ ... -> mongo::collection::update /collection/ ... mongo::cursor::find /mongoConn/ /ns/ ... -> mongo::cursor::find /collection/ ... - nsf::mongo::connect receives now a mongoc_uri https://github.com/mongodb/mongo-c-driver/blob/master/doc/mongoc_uri.txt - The gridfs interface allows now to store multiple revisions of a file - The gridfs interface allows now upload files from a string - The gridfs interface allows to refer to files by other attributes than just the filename (e.g. the mongo id). - Modified/new grid-file functions mongo::gridfile::create ?-source file|string? /gridfs/ /value/ /name/ /contentType/ mongo::gridfile::delete /gridfs/ /query/ mongo::gridfile::open /gridfs/ /query/ - Updated README - Updated regression test - Added editor hints for a more uniform appearance 2014-02-20 ssoberni - nx.tcl: Throw error exceptions using "return -code error", to exclude the unevaluated error cmd statement from the trace message - At two or three locations, we used to compute the method path using different helpers (CallStackMethodPath, NsfMethodNamePath) etc. I tried to unify this by revising NsfMethodNamePath to accomodate the different uses. - Besides, for required-argument checks, I included the method path (for ensemble invocations) into the error messages. 2014-02-17 Gustaf Neumann - improve performance of mongo->tcl conversion by using predefined global strings nx-mongo: - updated documentation (switch back to mongo-c-driver, but comment usage of tagged version v0.8.1) - added support for rep types (allow for mappings between certain instance variables such as arrays or dicts to customizable representations in MongoDB) - added nx-serialize to test cases (simple state persistance for nx objects) - added nx-rep to test cases (rep types for "array" and "dict") - provide datarootdir to get rid of warning during configure 2014-02-14 Gustaf Neumann - fix bug in interaction between up-level method from tcl procs -nsf.c: - fix bug in interaction between uplevel method and interceptor transparency - extend regression test 2014-02-07 Gustaf Neumann - in case of multiple inheritance, make sure that all supporting classes haver already a precedence order defined 2014-02-04 Gustaf Neumann - implement simple persistent handle management based on per-thread objects - reduce verbosity for FreeUnsetTraceVariable - return TCL_OK, even when FreeUnsetTraceVariable() fails (warning stays) - improve worrying in comments - fix stub provisioning for Tcl 8.6.* - change macro name from XOTCL to NSF 2014-01-26 Gustaf Neumann nx: - allow copy of objects with required arguments - use ::nsf::object::alloc in "copy" method - don't depend on method "trace", use directdispatch instead - remove method "-noinit" (nsf::object::alloc makes it obsolete) - extend regression test - restore traces after object-serialize nsf.c: - when ::nsf::object::alloc is passed an empty name (2nd argument), behave like "new" method 2014-01-25 Gustaf Neumann - don't rely on the existence of a "trace" method nsf.c: - new command ::nsf::object::alloc /class/ /obj/ ?/script/? alloc an object and execute script in the context. Can be used to regenerate an object in a old state. serializer: - fixed loading of objects with required data in the blueprint (many thanks for david hopfmueller for reporting this) - make use of nsf::object::alloc (1 command instead of 1 create + 2 evals) - these changes improved loading time of blueprint by about 25% for OpenACS+xowiki 2014-01-21 Gustaf Neumann - make test for retrieving parsed parameters more safe 2014-01-08 Gustaf Neumann - update hint at end of build for NaviServer - strip trailing spaces - silence cppcheck 2014-01-07 Gustaf Neumann mongodb: - add flag "-puts" to method "show" of nx::mongo::Class to turnoff output to stdout - handle empty find operations for "find first" - added method pretty_variables to output available variables of a class in a similar style as in the definition - added low-level method "close" to nx::mongo 2014-01-01 Gustaf Neumann - Tcl's "package present" raises an error if the package is not present 2013-12-26 Gustaf Neumann - add more assertions - ensure computation of requires orders for recursive merges - make test more robust - strip trailing spaces - get rid of potentially uninitialized variables - make compilation more clean - add assertion for validity checking of precedence lists - improve error message - remove trailing space 2013-12-23 ssoberni - Fixing a mini-typo in nsf.c: unlinkely -> unlikely 2013-12-23 Gustaf Neumann - made linearization monotonic (for multiple inheritance) via single-inheritance linearization merging while preserving overall linearization rules - added flag NSF_LINEARIZER_TRACE - extended regression test - upgrade to mongo-c-driver to 0.8.1 - added new flag "-ttl" to mongo::index - there seems to be now a different mongo-c-driver to be the preferred one, the old one is renamed to mongo-c-driver-legacy - link against nsf-stublib - bump version number to 0.2 - don't try to load nx when building pkgindex for a binary package (.so or dylib) generic/nsfPointer.c: - add reference counter to avoid double-inits and double-frees in case the table of converters is used from multiple interpreters 2013-12-22 Gustaf Neumann - change base stub table from XOTcl to NSF. - improve wording of error messages. - add parameter parser and converter to stub tables - make converter usable from c-based packages compiled with subs activated 2013-11-12 Gustaf Neumann xotcl2: - fixed "... info defaults ..." and "... info instdefaults ..." emulation in XOTcl 2 - fixed error message - extended regression test - bumped revision of nsf/xotcl/nx to 2.0b6 2013-10-22 Gustaf Neumann - nsf: added switch "-checkalways" to nsf::method::create - nx: added switch "checkalways" to "method" and "object method" - extended regression test 2013-10-19 Gustaf Neumann - minor cleanup: * reduce variable scope * remove uncalled static function - added flag -checkalways to nsf::proc and nsf::asm::proc (for the latter just a placeholder for now). If the flag is used, it will cause argument testing independently from the "configure checkarguments" setting. To force argument checking always is useful e.g. for checking external values (e.g. in a web server) 2013-09-15 Gustaf Neumann - fix object method serializeExportedMethod: targetName might have been uninitialized 2013-08-27 ssoberni - Explored assertion support from an NX perspective, by reviewing the current implementation against Eiffel's RAC; along the way, I made ::nsf::current more robust when being used in assertions 2013-08-08 Gustaf Neumann - don't call postcondition, when the command/invariant have returned already an error nsf.c - fixed a bug where turning on assertions could swallow result-codes - extended regression test - fix potential crash when preconditions are empty - fix typo 2013-08-03 Gustaf Neumann - added sample script doc/example-scripts/tk-geo.tcl - minor cleanup of configure script - bump version number of mongo support to 0.2 2013-08-01 Gustaf Neumann mongodb: - integrated configuration of mongodb into toplevel configfile option: --with-mongodb=MONGO_INCLUDE_DIR,MONGO_LIB_DIR - added regression test files for mongodb support (low level (tcl-only) and high level (nx based oo support)) - integrated mongodb-testfiles with "make test" - reduced verbosity of nx-mongo.tcl (added verbosity variable) 2013-07-31 Gustaf Neumann - added example scripts rosetta-sudoku.{tcl,html} and tk-ludo.{tcl,html} mongodb: - don't call NsfLog() in Nsfmongo_Exit, since interp-data might be already cleaned up - improve robustness - added flag for verbosity 2013-07-30 Gustaf Neumann mongodb: - updated to most recent version of c-driver (0.7.1) - adapted to nx 2.0b5 (use everywhere cget interface) - tested with mongodb 2.4.5 - updated locomotive example to use nx::callback 2013-07-16 Gustaf Neumann - updated TODO 2013-07-15 Gustaf Neumann - prepare for providing nx as a tcl module (.tm file). this is just a preparation, since for testing, one cannot set up a path that prefers a local copy over a global installed one (the global tcl-site is preferred over the one specified in e.g. TCL8_5_TM_PATH) 2013-07-08 Gustaf Neumann - explore the usage of dict instead of anonymous array nsf.c: - dont't use the default of a invocation parameter in "configure" when the object is already initialized. The default is in general only used when the parameter is not specified. We do not want e.g. superclass to be reset to ::nx::Object, when configure is called on a class without arguments. - extended regression test 2013-07-02 Gustaf Neumann - remove debug statement 2013-06-24 Gustaf Neumann - keep this single pkgIndex.tcl - keep packages versions from 2.0 and 1.0 disjoint Makefile.in000066400000000000000000001111531242365656200131400ustar00rootroot00000000000000# Makefile.in -- # # This file is a Makefile for Sample TEA Extension. If it has the name # "Makefile.in" then it is a template for a Makefile; to generate the # actual Makefile, run "./configure", which is a configuration script # generated by the "autoconf" program (constructs like "@foo@" will get # replaced in the actual Makefile. # # Copyright (c) 1999 Scriptics Corporation. # Copyright (c) 2002-2003 ActiveState Corporation. # Copyright (c) 2001-2007 Uwe Zdun # Copyright (c) 2001-2014 Gustaf Neumann # # See the file "tcl-license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # #======================================================================== # Add additional lines to handle any additional AC_SUBST cases that # have been added in a customized configure script. #======================================================================== NX_VERSION = @PACKAGE_VERSION@ src_lib_dir = ${srcdir}/library src_doc_dir = ${srcdir}/doc src_test_dir = ${srcdir}/tests src_app_dir = ${srcdir}/apps src_generic_dir = ${srcdir}/generic src_man_dir = ${srcdir}/man TCL_LIB_SPEC = @TCL_LIB_SPEC@ subdirs = @subdirs@ aol_prefix = @aol_prefix@ stubdir = stubs${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION} # Requires native paths PLATFORM_DIR = `@CYGPATH@ $(srcdir)/@TEA_PLATFORM@` target_doc_dir = ./doc src_lib_dir_native = `@CYGPATH@ ${src_lib_dir}` src_doc_dir_native = `@CYGPATH@ ${src_doc_dir}` src_test_dir_native = `@CYGPATH@ ${src_test_dir}` src_app_dir_native = `@CYGPATH@ ${src_app_dir}` src_generic_dir_native = `@CYGPATH@ ${src_generic_dir}` libdirs = lib nx serialize libsrc = COPYRIGHT pkgIndex.tcl appdirs = appsrc = COPYRIGHT # XOTcl subpackage xotcl_srcdir = ${srcdir}/library/xotcl xotcl_src_doc_dir = ${xotcl_srcdir}/doc xotcl_src_app_dir = ${xotcl_srcdir}/apps xotcl_src_lib_dir = ${xotcl_srcdir}/library xotcl_src_test_dir = ${xotcl_srcdir}/tests xotcl_target_doc_dir = ${xotcl_srcdir}/doc xotcl_libdirs = comm lib serialize @libdirs_actiweb@ xotcl_libsrc = COPYRIGHT xotcl2.tcl pkgIndex.tcl xotcl_appdirs = comm scripts utils @apps_actiweb@ xotcl_appsrc = COPYRIGHT # Documentation source for xotcl-style documentation system XODOC_SOURCE = \ $(src_lib_dir)/serialize/serializer.tcl \ $(xotcl_src_doc_dir)/langRef.xotcl \ $(xotcl_src_lib_dir)/lib/*.xotcl \ $(xotcl_src_test_dir)/*.xotcl \ $(xotcl_src_app_dir)/comm/[flsw]*.xotcl \ $(xotcl_src_app_dir)/utils/xo-*[a-z0-9] #export TCLLIBPATH=. ${srcdir} mkinstalldirs= mkdir -p #======================================================================== # Nothing of the variables below this line should need to be changed. # Please check the TARGETS section below to make sure the make targets # are correct. #======================================================================== DTRACE_OBJ = @DTRACE_OBJ@ DTRACE_HDR = $(src_generic_dir)/nsfDTrace.h DTRACE_SRC = $(src_generic_dir)/nsfDTrace.d #======================================================================== # The names of the source files is defined in the configure script. # The object files are used for linking into the final library. # This will be used when a dist target is added to the Makefile. # It is not important to specify the directory, as long as it is the # $(srcdir) or in the generic, win or unix subdirectory. #======================================================================== PKG_SOURCES = @PKG_SOURCES@ PKG_OBJECTS = @PKG_OBJECTS@ ${DTRACE_OBJ} PKG_STUB_SOURCES = @PKG_STUB_SOURCES@ PKG_STUB_OBJECTS = @PKG_STUB_OBJECTS@ #======================================================================== # PKG_TCL_SOURCES identifies Tcl runtime files that are associated with # this package that need to be installed, if any. #======================================================================== PKG_TCL_SOURCES = @PKG_TCL_SOURCES@ #======================================================================== # This is a list of public header files to be installed, if any. #======================================================================== PKG_HEADERS = @PKG_HEADERS@ #======================================================================== # "PKG_LIB_FILE" refers to the library (dynamic or static as per # configuration options) composed of the named objects. #======================================================================== PKG_LIB_FILE = @PKG_LIB_FILE@ PKG_STUB_LIB_FILE = @PKG_STUB_LIB_FILE@ lib_BINARIES = $(PKG_LIB_FILE) $(PKG_STUB_LIB_FILE) BINARIES = $(lib_BINARIES) SHELL = @SHELL@ srcdir = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ libdir = @libdir@ datadir = @datadir@ datarootdir = @datarootdir@ mandir = @mandir@ includedir = @includedir@ DESTDIR = PKG_DIR = $(PACKAGE_NAME)$(PACKAGE_VERSION) pkglibdir = $(libdir)/$(PKG_DIR) top_builddir = . INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ # INSTALL_OPTIONS = # INSTALL = $(SHELL) $(srcdir)/config/install-sh -c ${INSTALL_OPTIONS} # INSTALL_DATA_DIR = ${INSTALL} -d -m 755 # INSTALL_PROGRAM = ${INSTALL} -m 755 # INSTALL_DATA = ${INSTALL} -m 644 # INSTALL_SCRIPT = ${INSTALL_PROGRAM} # INSTALL_LIBRARY = ${INSTALL_DATA} PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ CC = @CC@ CFLAGS_DEFAULT = @CFLAGS_DEFAULT@ # useful for debugging: --param=ssp-buffer-size=4 -fstack-protector-all CFLAGS_WARNING = @CFLAGS_WARNING@ CLEANFILES = @CLEANFILES@ EXEEXT = @EXEEXT@ LDFLAGS_DEFAULT = @LDFLAGS_DEFAULT@ MAKE_LIB = @MAKE_LIB@ MAKE_SHARED_LIB = @MAKE_SHARED_LIB@ MAKE_STATIC_LIB = @MAKE_STATIC_LIB@ MAKE_STUB_LIB = @MAKE_STUB_LIB@ OBJEXT = @OBJEXT@ RANLIB = @RANLIB@ RANLIB_STUB = @RANLIB_STUB@ SHLIB_CFLAGS = @SHLIB_CFLAGS@ SHLIB_LD = @SHLIB_LD@ SHLIB_LD_LIBS = @SHLIB_LD_LIBS@ STLIB_LD = @STLIB_LD@ TCL_DEFS = @TCL_DEFS@ TCL_BIN_DIR = @TCL_BIN_DIR@ TCL_SRC_DIR = @TCL_SRC_DIR@ # DTRACE = dtrace ifeq ($(DTPLITE),) DTPLITE=dtplite else # Do nothing, use the environment variable as is. endif # # Not used, but retained for reference of what libs Tcl required TCL_LIBS = @TCL_LIBS@ xotcl_pkglibdir = $(pkglibdir)/xotcl installed_shells = $(DESTDIR)$(bindir)/nxsh $(DESTDIR)$(bindir)/nxwish $(DESTDIR)$(bindir)/xotclsh $(DESTDIR)$(bindir)/xowish #======================================================================== # TCLLIBPATH seeds the auto_path in Tcl's init.tcl so we can test our # package without installing. The other environment variables allow us # to test against an uninstalled Tcl. Add special env vars that you # require for testing here (like TCLX_LIBRARY). #======================================================================== EXTRA_PATH = $(top_builddir):$(TCL_BIN_DIR) TCLSH_ENV = TCL_LIBRARY=`@CYGPATH@ $(TCL_SRC_DIR)/library` \ @LD_LIBRARY_PATH_VAR@="$(EXTRA_PATH):$(@LD_LIBRARY_PATH_VAR@)" \ PATH="$(EXTRA_PATH):$(PATH)" \ TCLLIBPATH="$(top_builddir) ${srcdir} $(TCLLIBPATH)" TCLSH_PROG = @TCLSH_PROG@ TCLSH = $(TCLSH_ENV) $(TCLSH_PROG) #TCLSH = $(TCLSH_ENV) valgrind --dsymutil=yes $(TCLSH_PROG) SHARED_BUILD = @SHARED_BUILD@ INCLUDES = @PKG_INCLUDES@ @TCL_INCLUDES@ @NSF_BUILD_INCLUDE_SPEC@ EXTRA_CFLAGS = @PKG_CFLAGS@ # TCL_DEFS is not strictly need here, but if you remove it, then you # must make sure that configure.in checks for the necessary components # that your library may use. TCL_DEFS can actually be a problem if # you do not compile with a similar machine setup as the Tcl core was # compiled with. #DEFS = $(TCL_DEFS) @DEFS@ $(EXTRA_CFLAGS) DEFS = @DEFS@ $(EXTRA_CFLAGS) CONFIG_CLEAN_FILES = @CONFIG_CLEAN_FILES@ CPPFLAGS = @CPPFLAGS@ LIBS = @PKG_LIBS@ @LIBS@ AR = @AR@ CFLAGS = @CFLAGS@ COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) #======================================================================== # Start of user-definable TARGETS section #======================================================================== EXAMPLE_SCRIPTS = \ $(src_doc_dir)/example-scripts/bagel.html \ $(src_doc_dir)/example-scripts/container.html \ $(src_doc_dir)/example-scripts/per-object-mixins.html \ $(src_doc_dir)/example-scripts/rosetta-abstract-type.html \ $(src_doc_dir)/example-scripts/rosetta-classes.html \ $(src_doc_dir)/example-scripts/rosetta-constraint-genericity.html \ $(src_doc_dir)/example-scripts/rosetta-delegates.html \ $(src_doc_dir)/example-scripts/rosetta-distinct-objects.html \ $(src_doc_dir)/example-scripts/rosetta-polymorphic-copy.html \ $(src_doc_dir)/example-scripts/rosetta-polymorphism.html \ $(src_doc_dir)/example-scripts/rosetta-serialization.html \ $(src_doc_dir)/example-scripts/rosetta-singleton.html \ $(src_doc_dir)/example-scripts/rosetta-sudoku.html \ $(src_doc_dir)/example-scripts/rosetta-unknown-method.html \ $(src_doc_dir)/example-scripts/ruby-mixins.html \ $(src_doc_dir)/example-scripts/tk-horse-race.html \ $(src_doc_dir)/example-scripts/tk-locomotive.html \ $(src_doc_dir)/example-scripts/tk-ludo.html \ $(src_doc_dir)/example-scripts/tk-geo.html \ $(src_doc_dir)/example-scripts/tk-mini.html \ $(src_doc_dir)/example-scripts/tk-spread.html \ $(src_doc_dir)/example-scripts/traits-composite.html \ $(src_doc_dir)/example-scripts/traits-simple.html \ %.html : %.tcl $(TCLSH) $(src_app_dir_native)/utils/source-doc-beautifier.tcl $< PATH=$(src_app_dir_native)/utils:$(PATH) asciidoc -f $(src_app_dir_native)/utils/asciidoc.conf $*.txt #======================================================================== # TEA TARGETS. Please note that the "libraries:" target refers to platform # independent files, and the "binaries:" target inclues executable programs and # platform-dependent libraries. Modify these targets so that they install # the various pieces of your package. The make and install rules # for the BINARIES that you specified above have already been done. #======================================================================== all: binaries libraries end #======================================================================== # The binaries target builds executable programs, Windows .dll's, unix # shared/static libraries, and any other platform-dependent files. # The list of targets to build for "binaries:" is specified at the top # of the Makefile, in the "BINARIES" variable. #======================================================================== binaries: $(BINARIES) pkgIndex.tcl @if test ! "x$(subdirs)" = "x" ; then dirs="$(subdirs)" ; \ for dir in $$dirs ; do \ if (cd $$dir; $(MAKE) $@) ; then true ; else exit 1 ; fi ; \ done; fi; libraries: libraries-pkgindex @if test ! "x$(subdirs)" = "x" ; then dirs="$(subdirs)" ; \ for dir in $$dirs ; do \ if (cd $$dir; $(MAKE) $@) ; then true ; else exit 1 ; fi ; \ done; fi; cppcheck: cppcheck --enable=all generic/*.c $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) \ -I/usr/include -D__x86_64__ libraries-pkgindex: pkgIndex.tcl $(TCLSH) $(src_lib_dir_native)/lib/mkIndex.tcl -dir $(src_lib_dir_native) full-doc: doc pdfdoc example-doc # use language reference as sample file to trigger generation of documentation files #doc: $(xotcl_target_doc_dir)/langRef-xotcl.html # for now, just the two doc files doc: (cd $(src_doc_dir); asciidoc next-migration.txt) (cd $(src_doc_dir)/next-tutorial; asciidoc next-tutorial.txt) pdfdoc: (cd $(src_doc_dir); prince --javascript --script=prince.js --style=nx-small.css next-migration.html next-migration.pdf) (cd $(src_doc_dir)/next-tutorial; prince --javascript --script=../prince.js --style=../nx.css next-tutorial.html next-tutorial.pdf) example-doc: $(EXAMPLE_SCRIPTS) $(xotcl_target_doc_dir)/langRef-xotcl.html: pkgIndex.tcl $(xotcl_src_doc_dir)/langRef.xotcl $(XODOC_SOURCE) @docs=""; \ for i in $(XODOC_SOURCE); do docs="$$docs `@CYGPATH@ $$i`"; done; \ $(TCLSH) $(xotcl_src_lib_dir)/lib/makeDoc.xotcl \ $(xotcl_target_doc_dir) $$docs pdf: -(cd $(src_doc_dir); htmldoc --webpage --format pdf14 --title \ -f tutorial.pdf tutorial.html ) -(cd $(src_doc_dir); htmldoc --webpage --format pdf14 \ -f langRef-xotcl.pdf langRef-xotcl.html ) NX_MAN3 = \ $(src_doc_dir)/Object.man \ $(src_doc_dir)/Class.man \ $(src_doc_dir)/configure.man \ $(src_doc_dir)/current.man \ $(src_doc_dir)/next.man NX_MAN1 = \ $(src_doc_dir)/nxsh.man \ $(src_doc_dir)/xotclsh.man \ $(src_doc_dir)/nxwish.man \ $(src_doc_dir)/xowish.man man: man-html man-nroff man-pdf man-html: $(NX_MAN3:%.man=%.html) $(src_doc_dir)/%.html: $(src_doc_dir)/%.man @for m in $(?F) ; do \ echo " Generating html manpage from $$m" ; \ (cd $(src_doc_dir); $(DTPLITE) -style man.css -o . html $$m) ; \ done; man-nroff : man1 man3 man1 : $(NX_MAN1:%.man=%.1) $(src_doc_dir)/%.1 : $(src_doc_dir)/%.man @for m in $(?F) ; do \ echo " Generating nroff manpage (Section 1) from $$m" ; \ (cd $(src_doc_dir); $(DTPLITE) -ext 1 -o . nroff $$m) ; \ done; man3: $(NX_MAN3:%.man=%.3) $(src_doc_dir)/%.3 : $(src_doc_dir)/%.man @for m in $(?F) ; do \ echo " Generating nroff manpage (Section 3) from $$m" ; \ (cd $(src_doc_dir); $(DTPLITE) -ext 3 -o . nroff $$m) ; \ done; man-pdf: $(NX_MAN3:%.man=%.pdf) $(src_doc_dir)/%.pdf : $(src_doc_dir)/%.html @for m in $(?F) ; do \ echo " Generating pdf manpage for $$m" ; \ (cd $(src_doc_dir); prince -s man-princexml.css $$m) ; \ done; install: install-binaries install-shells install-libraries install-doc install-xotcl-shells install-xotcl-libraries @if test ! "x$(subdirs)" = "x" ; then dirs="$(subdirs)" ; \ for dir in $$dirs ; do \ if (cd $$dir; $(MAKE) $@) ; then true ; else exit 1 ; fi ; \ done; fi; install-binaries: binaries install-lib-binaries install-bin-binaries install-pkgIndex install-aol: install-binaries install-libraries install-xotcl-libraries @if test -d $(DESTDIR)/$(aol_prefix)/modules/tcl/; then \ $(INSTALL) $(src_generic_dir)/aol-xotcl.tcl \ $(DESTDIR)/$(aol_prefix)/modules/tcl/xotcl.tcl ; \ else \ $(INSTALL) $(src_generic_dir)/aol-xotcl.tcl \ $(DESTDIR)/$(aol_prefix)/tcl/xotcl.tcl ; \ fi; #======================================================================== # This rule installs platform-independent files, such as header files. #======================================================================== install-libraries: libraries $(DESTDIR)$(includedir) $(DESTDIR)$(pkglibdir) @echo "Installing header files in $(DESTDIR)$(includedir)" @for i in $(PKG_HEADERS) ; do \ echo " Installing $$i" ; \ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir) ; \ done; @echo "Installing Libraries to $(DESTDIR)$(pkglibdir)/" @for i in $(libdirs) ; do \ echo " Installing $$i/" ; \ rm -rf $(DESTDIR)$(pkglibdir)/$$i ; \ mkdir -p $(DESTDIR)$(pkglibdir)/$$i; \ chmod 755 $(DESTDIR)$(pkglibdir)/$$i; \ for j in $(src_lib_dir)/$$i/*.*tcl ; do \ $(INSTALL_DATA) $$j $(DESTDIR)$(pkglibdir)/$$i/; \ done; \ done; @for i in $(libsrc) ; do \ echo " Installing $$i" ; \ rm -rf $(DESTDIR)$(pkglibdir)/$$i ; \ $(INSTALL_DATA) $(src_lib_dir)/$$i $(DESTDIR)$(pkglibdir)/$$i ; \ done; @mkdir -p $(DESTDIR)$(libdir)/tcl8/site-tcl @for i in $(srcdir)/tcl8/site-tcl/*.tm ; do \ if test -f $$i; then \ echo " Installing module $$i in $(DESTDIR)$(libdir)/tcl8/site-tcl" ; \ $(INSTALL_DATA) $$i $(DESTDIR)$(libdir)/tcl8/site-tcl/; \ fi; \ done; @echo " Installing pkgIndex.tcl for nsf in $(DESTDIR)$(pkglibdir)/pkgIndex.tcl" @echo "" >> $(DESTDIR)$(pkglibdir)/pkgIndex.tcl @cat unix/pkgIndex.unix >> $(DESTDIR)$(pkglibdir)/pkgIndex.tcl @$(INSTALL_DATA) nsfConfig.sh $(DESTDIR)$(libdir)/ install-xotcl-libraries: install-libraries $(DESTDIR)$(pkglibdir) @echo "Installing XOTcl Libraries to $(DESTDIR)$(xotcl_pkglibdir)/" @rm -rf $(DESTDIR)$(xotcl_pkglibdir) @mkdir -p $(DESTDIR)$(xotcl_pkglibdir) @chmod 755 $(DESTDIR)$(xotcl_pkglibdir) @for i in $(xotcl_libdirs) ; do \ echo " Installing library $$i/" ; \ rm -rf $(DESTDIR)$(xotcl_pkglibdir)/$$i ; \ mkdir -p $(DESTDIR)$(xotcl_pkglibdir)/$$i; \ chmod 755 $(DESTDIR)$(xotcl_pkglibdir)/$$i; \ for j in $(xotcl_src_lib_dir)/$$i/*.*tcl ; do \ $(INSTALL_DATA) $$j $(DESTDIR)$(xotcl_pkglibdir)/$$i/; \ done; \ done; @for i in $(xotcl_libsrc) ; do \ echo " Installing $$i" ; \ rm -rf $(DESTDIR)$(xotcl_pkglibdir)/$$i ; \ $(INSTALL_DATA) $(xotcl_src_lib_dir)/$$i $(DESTDIR)$(xotcl_pkglibdir)/$$i ; \ done; install-xotcl-shells: @if test -f $(xotcl_srcdir)/xotclsh; then \ $(INSTALL_PROGRAM) $(xotcl_srcdir)/xotclsh $(DESTDIR)$(bindir); \ fi @if test -f $(xotcl_srcdir)/xowish; then \ $(INSTALL_PROGRAM) $(xotcl_srcdir)/xowish $(DESTDIR)$(bindir); \ fi install-xotcl-apps: install-xotcl-libraries @echo "Installing Applications to $(DESTDIR)$(xotcl_pkglibdir)/apps/" @for i in $(xotcl_appdirs) ; do \ echo " Installing apps $$i/" ; \ rm -rf $(DESTDIR)$(xotcl_pkglibdir)/apps/$$i ; \ mkdir -p $(DESTDIR)$(xotcl_pkglibdir)/apps/$$i; \ chmod 755 $(DESTDIR)$(xotcl_pkglibdir)/apps/$$i; \ for j in $(src_app_dir)/$$i/* ; do \ if test -d $$j; then \ mkdir -p $(DESTDIR)$(xotcl_pkglibdir)/$$j; \ chmod 755 $(DESTDIR)$(xotcl_pkglibdir)/$$j; \ for k in $$j/* ; do \ $(INSTALL) $$k $(DESTDIR)$(xotcl_pkglibdir)/$$j ; \ done; \ else \ $(INSTALL) $$j $(DESTDIR)$(xotcl_pkglibdir)/apps/$$i/; \ fi; \ done; \ done; @for i in $(xotcl_appsrc) ; do \ echo " Installing $$i" ; \ rm -rf $(DESTDIR)$(xotcl_pkglibdir)/apps/$$i ; \ $(INSTALL_DATA) $(src_app_dir)/$$i $(DESTDIR)$(xotcl_pkglibdir)/apps ; \ done; @rm -rf $(DESTDIR)$(xotcl_pkglibdir)/store/XOTclGdbm @rm -rf $(DESTDIR)$(xotcl_pkglibdir)/store/XOTclSdbm @rm -rf $(DESTDIR)$(xotcl_pkglibdir)/xml/TclExpat-1.1 #======================================================================== # Install documentation. Unix manpages should go in the $(DESTDIR)$(mandir) # directory. #======================================================================== # install-doc: $(DESTDIR)$(mandir)/man1 $(DESTDIR)$(mandir)/man3 $(DESTDIR)$(mandir)/mann # @(cd $(src_man_dir)/ ; \ # for i in *.1; do \ # echo "Installing $$i"; \ # rm -f $(DESTDIR)$(mandir)/man1/$$i; \ # sed -e '/man\.macros/r man.macros' -e '/man\.macros/d' \ # $$i > $(DESTDIR)$(mandir)/man1/$$i; \ # chmod 444 $(DESTDIR)$(mandir)/man1/$$i; \ # done) install-doc: $(DESTDIR)$(mandir)/man1 $(DESTDIR)$(mandir)/man3 @echo "Installing documentation in $(DESTDIR)$(mandir)" @list='$(srcdir)/doc/*.1'; for i in $$list; do \ echo "Installing $$i"; \ $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/man1 ; \ done @list='$(srcdir)/doc/*.3'; for i in $$list; do \ echo "Installing $$i"; \ $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/man3 ; \ done shell: binaries libraries @$(TCLSH) $(SCRIPT) gdb: $(TCLSH_ENV) gdb $(TCLSH_PROG) $(SCRIPT) test: binaries libraries test-core test-xotcl test-http @test_actiweb@ @if test ! "x$(subdirs)" = "x" ; then dirs="$(subdirs)" ; \ for dir in $$dirs ; do \ if (cd $$dir; $(MAKE) $@) ; then true ; else exit 1 ; fi ; \ done; fi $(TCLSH) $(src_test_dir_native)/summary.tcl -title NX+XOTcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) test-nohttp: binaries libraries test-core test-xotcl TESTLOG = ./__test.log TESTFLAGS = -testlog $(TESTLOG) test-summary: $(TCLSH) $(src_test_dir_native)/summary.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) test-core: $(TCLSH_PROG) rm -f $(TESTLOG) $(TCLSH) $(src_test_dir_native)/object-system.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/destroy.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/methods.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/method-parameter.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/nsf-cmd.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/accessor.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/cget.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/properties.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/var-access.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/varresolution.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/info-method.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/submethods.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/info-variable.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/disposition.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/volatile.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/parameters.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/returns.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/method-require.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/interceptor-slot.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/alias.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/protected.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/forward.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/mixinof.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/tcl86.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/contains.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/tcloo.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/interp.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/serialize.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/plain-object-method.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/class-method.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/linearization.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/traits.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/bagel.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/container.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-abstract-type.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-classes.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-constraint-genericity.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-delegates.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-distinct-objects.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-polymorphic-copy.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-polymorphism.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-serialization.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-singleton.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-unknown-method.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/ruby-mixins.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/traits-composite.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/traits-simple.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) test-xotcl: $(TCLSH_PROG) $(TCLSH) $(xotcl_src_test_dir)/testo.xotcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(xotcl_src_test_dir)/speedtest.xotcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(xotcl_src_test_dir)/testx.xotcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(xotcl_src_test_dir)/slottest.xotcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) test-http: $(TCLSH_PROG) $(TCLSH) $(xotcl_src_test_dir)/xocomm.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) test-actiweb: $(TCLSH_PROG) $(TCLSH) $(xotcl_src_test_dir)/actiweb.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(xotcl_src_test_dir)/persistence.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(xotcl_src_test_dir)/UNIVERSAL.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(xotcl_src_test_dir)/xoRDF.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) @rm -rf receiver depend: #======================================================================== # $(PKG_LIB_FILE) should be listed as part of the BINARIES variable # mentioned above. That will ensure that this target is built when you # run "make binaries". # # The $(PKG_OBJECTS) objects are created and linked into the final # library. In most cases these object files will correspond to the # source files above. #======================================================================== $(PKG_LIB_FILE): $(PKG_OBJECTS) -rm -f $(PKG_LIB_FILE) ${MAKE_LIB} $(RANLIB) $(PKG_LIB_FILE) $(PKG_STUB_LIB_FILE): $(PKG_STUB_OBJECTS) -rm -f $(PKG_STUB_LIB_FILE) ${MAKE_STUB_LIB} $(RANLIB_STUB) $(PKG_STUB_LIB_FILE) #======================================================================== # We need to enumerate the list of .c to .o lines here. # # In the following lines, $(srcdir) refers to the toplevel directory # containing your extension. If your sources are in a subdirectory, # you will have to modify the paths to reflect this: # # sample.$(OBJEXT): $(srcdir)/generic/sample.c # $(COMPILE) -c `@CYGPATH@ $(srcdir)/generic/sample.c` -o $@ # # Setting the VPATH variable to a list of paths will cause the makefile # to look into these paths when resolving .c to .obj dependencies. # As necessary, add $(srcdir):$(srcdir)/compat:.... #======================================================================== VPATH = $(srcdir):$(srcdir)/generic:$(srcdir)/unix:$(srcdir)/win .c.@OBJEXT@: $(COMPILE) -c `@CYGPATH@ $<` -o $@ #======================================================================== # next shells #======================================================================== pkgIndex.tcl: $(PKG_LIB_FILE) @echo package ifneeded nsf $(PACKAGE_VERSION) \"load [list [file join \$$dir . $(PKG_LIB_FILE)] nsf]\; package provide nsf $(PACKAGE_VERSION)\" > pkgIndex.tcl install-pkgIndex: # @echo package ifneeded nsf $(PACKAGE_VERSION) [list load [file join \$$dir .. "$(PKG_LIB_FILE)"] nsf] > "$(pkglibdir)/pkgIndex.tcl" #nxsh: tclAppInit.o $(PKG_OBJECTS) $(CONDITIONAL_STUB_OBJECTS) # $(CC) -rdynamic -o $@ tclAppInit.o \ # $(CFLAGS) $(TCL_LIB_SPEC) \ # $(DMALLOC_LIB) $(CONDITIONAL_STUB_OBJECTS) #xowish: tkAppInit.o $(PKG_OBJECTS) $(CONDITIONAL_STUB_OBJECTS) # $(CC) -rdynamic -o $@ tkAppInit.o \ # $(PKG_OBJECTS) \ # $(CFLAGS) $(TCL_LIB_SPEC) $(TK_LIB_SPEC) \ # $(DMALLOC_LIB) $(CONDITIONAL_STUB_OBJECTS) install-shells: @if test -f nxsh; then \ $(INSTALL_PROGRAM) nxsh $(DESTDIR)$(bindir); \ fi @if test -f nxwish; then \ $(INSTALL_PROGRAM) nxwish $(DESTDIR)$(bindir); \ fi #======================================================================== # We need to enumerate the list of .c to .o lines here. # Unfortunately, there does not seem to be any other way to do this # in a Makefile-independent way. We can't use VPATH because it picks up # object files that may be located in the source directory. # # In the following lines, $(srcdir) refers to the toplevel directory # containing your extension. If your sources are in a subdirectory, # you will have to modify the paths to reflect this: # # exampleA.$(OBJEXT): $(srcdir)/src/win/exampleA.c # $(COMPILE) -c `@CYGPATH@ $(srcdir)/src/win/exampleA.c` -o $@ #======================================================================== $(src_generic_dir)/predefined.h: $(src_generic_dir)/mk_predefined.tcl $(src_generic_dir)/nsf.tcl (cd $(src_generic_dir); $(TCLSH) mk_predefined.tcl nsf.tcl > predefined.h) $(src_generic_dir)/nsfAPI.h: $(src_generic_dir)/gentclAPI.tcl $(src_generic_dir)/nsfAPI.decls $(TCLSH) $(src_generic_dir)/gentclAPI.tcl $(src_generic_dir)/nsfAPI.decls > $(src_generic_dir)/nsfAPI.h aolstub.$(OBJEXT): $(src_generic_dir)/aolstub.c $(PKG_HEADERS) nsf.$(OBJEXT): $(src_generic_dir)/nsf.c $(src_generic_dir)/predefined.h $(src_generic_dir)/nsfAccessInt.h $(src_generic_dir)/nsfAPI.h $(PKG_HEADERS) $(src_generic_dir)/nsfStack.c $(src_generic_dir)/asm/nsfAssemble.c $(src_generic_dir)/asm/nsfAsmExecuteCallThreading.c $(src_generic_dir)/asm/nsfAsmExecuteLabelThreading.c $(src_generic_dir)/asm/nsfAsmAssemble.c $(DTRACE_HDR) nsfDebug.$(OBJEXT): $(src_generic_dir)/nsfDebug.c $(PKG_HEADERS) nsfError.$(OBJEXT): $(src_generic_dir)/nsfError.c $(PKG_HEADERS) nsfMetaData.$(OBJEXT): $(src_generic_dir)/nsfMetaData.c $(PKG_HEADERS) nsfObj.$(OBJEXT): $(src_generic_dir)/nsfObj.c $(PKG_HEADERS) nsfObjectData.$(OBJEXT): $(src_generic_dir)/nsfObjectData.c $(PKG_HEADERS) nsfPointer.$(OBJEXT): $(src_generic_dir)/nsfPointer.c $(PKG_HEADERS) nsfEnumerationType.$(OBJEXT): $(src_generic_dir)/nsfEnumerationType.c $(PKG_HEADERS) nsfProfile.$(OBJEXT): $(src_generic_dir)/nsfProfile.c $(PKG_HEADERS) nsfShadow.$(OBJEXT): $(src_generic_dir)/nsfShadow.c $(PKG_HEADERS) $(stubdir)/nsfStubInit.$(OBJEXT): $(PKG_HEADERS) nsfStubLib.$(OBJEXT): $(src_generic_dir)/nsfStubLib.c $(PKG_HEADERS) nsfUtil.$(OBJEXT): $(src_generic_dir)/nsfUtil.c $(PKG_HEADERS) # # Target to regenerate header files and stub files from the *.decls tables. # TCL_SRC_DIR_85=/usr/local/src/tcl8.5.17 TCL_SRC_DIR_86=/usr/local/src/tcl8.6.2 genstubs: mkdir -p $(src_generic_dir)/stubs8.5 tclsh8.5 $(TCL_SRC_DIR_85)/tools/genStubs.tcl $(src_generic_dir)/stubs8.5 \ $(src_generic_dir)/nsf.decls $(src_generic_dir)/nsfInt.decls mkdir -p $(src_generic_dir)/stubs8.6 tclsh8.6 $(TCL_SRC_DIR_86)/tools/genStubs.tcl $(src_generic_dir)/stubs8.6 \ $(src_generic_dir)/nsf.decls $(src_generic_dir)/nsfInt.decls getstubs: $(TCLSH) $(src_app_dir_native)/utils/getstubs.tcl $(src_generic_dir) # # Target to check that all exported functions have an entry in the stubs # tables. # checkstubs: -@for i in `nm -p $(PKG_LIB_FILE) | awk '$$2 ~ /T/ { print $$3 }' \ | sort -n`; do \ match=0; \ for j in $(TCL_DECLS); do \ if [ `grep -c $$i $$j` -gt 0 ]; then \ match=1; \ fi; \ done; \ if [ $$match -eq 0 ]; then echo $$i; fi \ done # DTrace support $(PKG_OBJECTS): $(DTRACE_HDR) $(DTRACE_HDR): $(DTRACE_SRC) $(DTRACE) -h $(DTRACE_SWITCHES) -o $@ -s $(DTRACE_SRC) $(DTRACE_OBJ): $(DTRACE_SRC) $(TCL_OBJS) $(DTRACE) -G $(DTRACE_SWITCHES) -o $@ -s $(DTRACE_SRC) $(TCL_OBJS) #======================================================================== # End of user-definable section #======================================================================== #======================================================================== # Don't modify the file to clean here. Instead, set the "CLEANFILES" # variable in configure.in #======================================================================== cleandoc: -rm -rf $(xotcl_target_doc_dir)/*-xotcl.html clean: cleandoc -rm -rf $(BINARIES) $(CLEANFILES) ./receiver $(TESTLOG) find ${srcdir} -type f -name \*~ -exec rm \{} \; @if test ! "x$(subdirs)" = "x" ; then dirs="$(subdirs)" ; \ for dir in $$dirs ; do \ if (cd $$dir; $(MAKE) $@) ; then true ; else exit 1 ; fi ; \ done; fi distclean: clean -rm -rf $(CONFIG_CLEAN_FILES) -rm -f config.cache config.log config.status @if test ! "x$(subdirs)" = "x" ; then dirs="$(subdirs)" ; \ for dir in $$dirs ; do \ if (cd $$dir; $(MAKE) $@) ; then true ; else exit 1 ; fi ; \ done; fi #======================================================================== # Install binary object libraries. On Windows this includes both .dll and # .lib files. Because the .lib files are not explicitly listed anywhere, # we need to deduce their existence from the .dll file of the same name. # Library files go into the lib directory. # # A manual generation of the pkgIndex files via tclsh should look like # pkg_mkIndex -direct -verbose library/lib *tcl # # In addition, this will generate the pkgIndex.tcl # file in the install location (assuming it can find a usable tclsh shell) # # You should not have to modify this target. #======================================================================== install-lib-binaries: @mkdir -p $(DESTDIR)$(pkglibdir) @list='$(lib_BINARIES)'; for p in $$list; do \ if test -f $$p; then \ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p"; \ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p; \ stub=`echo $$p|sed -e "s/.*\(stub\).*/\1/"`; \ if test "x$$stub" = "xstub"; then \ echo " $(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p"; \ $(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p; \ else \ echo " $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p"; \ $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p; \ ln -s $(DESTDIR)$(pkglibdir)/$$p $(DESTDIR)$(libdir)/$$p; \ fi; \ ext=`echo $$p|sed -e "s/.*\.//"`; \ if test "x$$ext" = "xdll"; then \ lib=`basename $$p|sed -e 's/.[^.]*$$//'`.lib; \ if test -f $$lib; then \ echo " $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib"; \ $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib; \ fi; \ fi; \ fi; \ done @list='$(PKG_TCL_SOURCES)'; for p in $$list; do \ if test -f $(srcdir)/$$p; then \ destp=`basename $$p`; \ echo " Install $$destp $(DESTDIR)$(pkglibdir)/$$destp"; \ $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkglibdir)/$$destp; \ fi; \ done #======================================================================== # Install binary executables (e.g. .exe files and dependent .dll files) # This is for files that must go in the bin directory (located next to # wish and tclsh), like dependent .dll files on Windows. # # You should not have to modify this target, except to define bin_BINARIES # above if necessary. #======================================================================== install-bin-binaries: @mkdir -p $(DESTDIR)$(bindir) @list='$(bin_BINARIES)'; for p in $$list; do \ if test -f $$p; then \ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p"; \ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p; \ fi; \ done .SUFFIXES: .c .$(OBJEXT) #Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status # cd $(top_builddir) \ # && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status uninstall-binaries: list='$(lib_BINARIES)'; for p in $$list; do \ rm -f $(DESTDIR)$(pkglibdir)/$$p; \ done list='$(PKG_TCL_SOURCES)'; for p in $$list; do \ p=`basename $$p`; \ rm -f $(DESTDIR)$(pkglibdir)/$$p; \ done list='$(bin_BINARIES)'; for p in $$list; do \ rm -f $(DESTDIR)$(bindir)/$$p; \ done $(DESTDIR)$(includedir): $(mkinstalldirs) $@ $(DESTDIR)$(bindir): $(mkinstalldirs) $@ $(DESTDIR)$(libdir): $(mkinstalldirs) $@ $(DESTDIR)$(pkglibdir): $(mkinstalldirs) $@ $(DESTDIR)$(pkglibdir)/apps: $(DESTDIR)$(pkglibdir) $(mkinstalldirs) $@ $(DESTDIR)$(mandir)/man1: $(mkinstalldirs) $@ $(DESTDIR)$(mandir)/man3: $(mkinstalldirs) $@ $(DESTDIR)$(mandir)/mann: $(mkinstalldirs) $@ end: @echo "" @echo "***************************************************************" @echo " Make completed. In order to test the next-scripting framework," @echo " invoke:" @echo " make test" @echo "" @echo " To install, use:" @echo " make install" @echo "" @echo " To install for AOLserver 4.x, use:" @echo " make install-aol" @echo "" @echo " In order to invoke NX interactively (before install), use:" @echo " export TCLLIBPATH=\"$(TCLLIBPATH)\" TCL8_5_TM_PATH=\"$(TCLLIBPATH)\"/tcl8/site-tcl" @echo " and" @echo " @TCLSH_PROG@" @echo " package require nx" @echo "***************************************************************" RPMSOURCES=/usr/src/redhat/SOURCES RPMSPECS=/usr/src/redhat/SPECS rpm: @if test ! -d $(RPMSOURCES); then mkdir -p $(RPMSOURCES); fi @if test ! -d $(RPMSPECS); then mkdir -p $(RPMSPECS); fi cp unix/xotcl.spec $(RPMSPECS)/xotcl-$(PACKAGE_VERSION).spec make tar cp ../xotcl-$(PACKAGE_VERSION).tar.gz $(RPMSOURCES) rpmbuild -ba $(RPMSPECS)/xotcl-$(PACKAGE_VERSION).spec bin-tar: (cd ..; tar zcvf xotcl-$(PACKAGE_VERSION)-bin-linux-i686-glibc.tar.gz \ `find \ $(prefix)/bin/nx*sh \ $(prefix)/bin/xotcl*sh \ $(prefix)/lib/nsf* \ $(prefix)/lib/libnsf* \ $(prefix)/include/nsf*.h \ $(DESTDIR)$(pkglibdir) $(prefix)/man/man1/nsf* \ -type f -o -type l | fgrep -v CVS | fgrep -v SCCS | fgrep -v .junk| fgrep -v .db | fgrep -v "~" | fgrep -v "#" | fgrep -v /receiver/` \ ) tar: libraries-pkgindex sh ./tclconfig/mktar.sh .PHONY: all binaries clean depend distclean doc install libraries \ test test-core test-actiweb # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: README000066400000000000000000000005601242365656200117520ustar00rootroot00000000000000************************************ Next Scripting Framework README File ************************************ Compilation of Source Distribution: On Unix-like environments (including Mac OS X), do ./configure make sudo make install For more details, and build instructions, binary releases, community support etc. consult: http://next-scripting.org README.aol000066400000000000000000000004721242365656200125260ustar00rootroot00000000000000 Starting with Aolserver 4.*/naviserver nsf can be loaded via package require. However, it is recommended to install the binaries under the aolserver directory. The recommended configuration is: ./configure --enable-threads --prefix=/usr/local/aolserver Then, you can do a make make install-aol README.release000066400000000000000000000050561242365656200133760ustar00rootroot00000000000000Steps for a beta release: - docs: * update all docs with asciidoc in doc/ - code: * tcl 8.5: * configure with --enable-development make test * configure with --enable-memcount=yes make test 2>&1|cat |fgrep Overall * configure with --enable-development and activate valgrind in Makefile make test 2>&1|cat |fgrep "definitely lost" (8.5.11 ok, when every test returns "40 bytes in 2 blocks") (8.5.14 ok, when on test reurns "64 bytes in 1 blocks") * get rid of non-ansi-c make "CFLAGS_DEFAULT=-ansi -pedantic" (warnings are ok, errors not) * complile with clang make "CC=clang" make "CC=clang" test * run static analysis: cppcheck --enable=all generic/*.c * configure without --enable-development make install make install-aol test with naviserver/aolserver (xowiki/xowf) * tcl 8.6: * configure with --enable-development make test * configure with --enable-memcount=yes make test 2>&1|cat |fgrep Overall * configure with --enable-development and activate valgrind in Makefile make test 2>&1|cat |fgrep "definitely lost" (8.6b2 ok, when every test returns "40 bytes in 2 blocks") (8.6.0 ok, when every test returns "0 bytes in 0 blocks") - build windows binaries - test tk apps under windows - Announcement * Summarize changes since the last release in doc/Announce-VERSION As source use e.g. git log --date-order --date=short|less ~/scripts/git-changelog2 -- 2.0b5..HEAD --no-merges > ChangeLog-2.0b3-2.0 * Run spell checker over announcement + ChangeLog * git add announcement - build tar * make tar * check content of tar file tar ztvf ../nsf2.0.tar.gz |sort -rn -k 5,6|less - tag version in git and commit git tag -a 2.0b5 -m 2.0b5 git push --tags git push : 2.0.0 # git push --follow-tags # git push commit - update archive at sourceforge * create folder with version name * upload tar file to new folder * upload announce to new folder and rename it to README - update web site * stefan, please add recommended steps here - prepare next release: rename folder containing nsf to e.g. nsf2.0b4 update version number in * configure.in * library/nx/nx.tcl * library/nx/pkgIndex.tcl * library/xotcl/library/xotcl2.tcl autoconf ./configure .... TODO000066400000000000000000007036611242365656200115760ustar00rootroot00000000000000<- handle change in tcl 8.5.8: http://tcl.cvs.sourceforge.net/viewvc/tcl/tcl/generic/tclObj.c?sortby=date&r1=1.139.2.1&r2=1.139.2.2&pathrev=core-8-5-branch in xotcl: * when e.g. the parent namespace is deleted with a "namespace delete", the change above causes, the no xotcl methods could be resolved (and called) anymore. * therefore, e.g. a "C dealloc c1" did not work, since dealloc passes c1 via tcl_obj, and the tcl_obj is as well converted to an xotcl object via Tcl_GetCommandFromObj(), which fails as well. - to bypass this problem, xotcl has now a C-function DoDealloc(), which is called, when this situation is detected. - some more cases, where xotcl could reference already freed memory were fixed (thanks to enable-symbols=mem) - as collateral damage, parts of the regression test don't work currently - added refcounting in ObjectDispatch() to make sure, obj survives until the end of the function - added existence test for slot extractor (needed for 8.5.8) - added refcounting CallStackDoDestroy() to ensure existance of object until end of function - make sure to call PrimitiveDestroy() before DeleteCommandFromToken(), otherwise e.g. unset traces on this object cannot be executed - regression test works again - get rid of rst->deallocCalled (not needed due to reference counting on xotcl objects) - reduce verbosity - reactivated XOTclErrInProc() - renamed "ClassName info instmixinof ?-closure? ?pattern?" into "ClassName info class-mixin-of ?-closure? ?pattern?" - renamed "ClassName info mixinof ?-closure? ?pattern?" into "ClassName info object-mixin-of ?-closure? ?pattern?" - added emulation "ClassName info instmixinof|mixinof" for xotcl1 - update to TEA 3.7 (from TEA 3.5) - use of INT2PTR to avoid warnings about different sizes on 64bit architectures - defined HAVE_INTPTR_T as a macro to make definition of INT2PTR working - use INT2PTR and PTR2INT macros in generated stubs - handle HAVE_UINTPTR_T like HAVE_INTPTR_T - use ::xotcl::setinstvar instead of ::xotcl::setinstvar in serializer - change and rename cmd instvar ::xotcl::instvar -object someObject newVar into ::xotcl::importvar someObject newVar Rationale of change: only needed in xotcl2 for importing variables from different objects - changed assertions: old (xotcl1) interface: 3 methods + 3 info methods * check Options * info check * invar Conditions * info invar * instinvar Conditions * info instinvar new (xotcl2) interface: 1 cmd (similar to ::xotcl::relation) ::xotcl::assertion check|object-invar|class-invar ?arg? - added emulation for xotcl1 - deleted namespecific C macros: isInfoString, isInstinvarString, isInvarString, isInstprocString, isProcString - made some more xotcl2 methods protected (no big need to call these from different objects): unknown, uplevel, upvar - added ::xotcl::exists as cmd - added ::xotcl::method as cmd instead of methods object-method and class-method - added ::xotcl::forward as cmd instead of method now, all method-defining-methods (alias, method, forward, setter) are defined as cmds (this should make life of serializer easier) - moved "-per-object" consequently immediately after obj in the following commands : ::xotcl::alias, ::xotcl::methodproperty, ::xotcl::setter to achiev conformance with ::xotcl::forward and ::xotcl::method -per-object nach methodName: vor methodName: alias forward method methodproperty setter - added experimental flag for alias "-noleaf" to force a stack frame to be written (e.g. necessary for "next"). makes only sense for aliases to c-implemented cmds - fix inconsistent behaviour of dotVarResolver "o eval {set .x 1}" was setting var ".x" instead of "x" The problem was due to a interaction between the namespace varResolver() and the DotVarResolver() - fix for DotCmdResolver during compilation of a proc by Tcl. - extended regression text - found 2 potential bugs (not fixed yet) - fix eval method with requirenamespace - removed dependency on proc/instproc in copy/move. code is now independet of class system - changed results of "filtersearch" and "self next" to new naming Caveat: for xotcl1, there is no mapping for the names to the old style in "self next" and "self filterreg" - backwards compatible mapping of filterseach in xotcl1 - removed XOTclInstVar from the C-level API. todo: add all xotcl*cmds to C api, including importvar - removed all unreferenced entries from XOTE_* - regrouped XOTE_* for easier comprehension - used XOTE_* on more occations - used XOTclCallCommand() for calling Tcl "format" and "interp" - added option "arg=.." to parameter options; this argument can be passed to converter; used currently for "type=relation" to flag, that the relation type is different from the parameter name - extended "objectparameter" to handle such cases - removed relationtypes "mixin", "filter", "instfilter" and "instmixin" since not needed due to converterArg - xotcl.c: removed all names starting with "inst" (exception: instvar) - added option "-application" to "info callable" to omit methods from base classes - extended regression test - changed naming of methodtype "system" to "builtin" - added "info methods" to migration guide - added "info method" to migration guide - modernize test a little: all local definitions of proc "?" are gone. - added interface to test: "Test parameter count SOMEVALUE" to specify conveniently e.g. the number of tests the be executed - add XOTCL_CM_NO_UNKNOWN to dispatch of defaultmethod - added option "objectsystems" to ::xotcl::configure to obtain the currently defined object systems - added option "baseclass" to ::xotcl::is to check, whether a class is a baseclass of an object system (root class or root meta-class of object system) - changed result of "... info methods -methodtype scripted" to return only truely scripted methods (no aliases) - some more cleanup in regression tests - first version of serializer for xotcl1 + xotcl2 - serializer: move checking of the requested objects to be exported to the invocation of "Serializer all" - replace "namespace import ::xotcl::*" by "xotcl::use xotcl1" - make allocation sizes for dynamically allocated parameterContexts consistent between alloc and realloc - added sanity check in getAllClassMixinsOf() It seems as it would be possible with __defaultSupeclass to define a class which has itself als a subclass. Just a quick fix, we have investigate more on this. - improved naming of resolvers: use InterpDot*Resolver and NsDot*Resolver for interp wide and namespace specific resolvers - added possibility to escape the call of commands starting with a dot from method bodies by prefixing with a second dot. - make sure to have an xotcl frame pushed, when byte-code-compiler is invoked. This is necessary for providing the context for dotCmd resolvers - use uplevel in slot add/delete to evalute in calling namespace (for not fully qualified object names) - determine namespace in test method "?" to allow its execution in an "namespace eval" - added regression tests - NsDotVarResolver: don't create variables on CMETHOD frames when their names do not start with a "." - general overhaul of XOTcl_PushFrame()XOTcl_PopFrame(): new functions: * XOTcl_PushFrameCsc()/XOTcl_PopFrameCsc(): for CMETHOD fames * XOTcl_PushFrameObj()/XOTcl_PopFrameObj(): for OBJECT frames (objscope) - preallocate obj->vartable in XOTcl_PushFrameObj() to avoid situations where to non-existing vartable is created on demand at different places (e.g. on stack and in var resolver) - caller of convertToRelationtype(): make sure that last argument keeping the result is large enough to held pointer (in case of sizeof(void) != sizeof(int) - Serializer: include ObjectSystemSerializer in "Serializer all" - Serializer: use class-mixin in dependency order - Serializer: add appropriate "::xotcl::use xotcl1|xotcl2" - Serializer: fix syntax in exportMethods - Serializer: provide limited support for exporting aliases for xotcl1 objects - add calltype to tcl85showStack - keep orignial objc/objc in call stack context such that next/self args works with canonical args (allow e.g. different order of nonpos args in a next) - make naming in xotcl.c more consistent - ensure to return empty, when "info callable -which" fails - extend regression test - exithandler is different in xotcl2 -> comment, check openacs - ensure relation filter/instfilter etc. mit guards - extended migration guide - defined eval in predefined via "-nonleaf", used at various places - added "info mixinof ?-scope all|object|class? ?pattern?", dropped "object-mixin-of" and "class-mixin-on" from registerd method - extended regression test - xotcl1: make "-volatile" invoked via unknown behave correctly - provide minimal xotcl2 compatibility - added non positional parameter "type" to "get_parameter" - removed "required" from parameters, which is in XOTcl 1 just a comment - minor cleanup - experimental change of resolver name prefix char from dot to single colon - making methodDefinitions NULL terminated - passing optional arg to user-defined argument converter - refuse to redefine type converters for a single parameter - adding regression test for parameters - added instance variable arg for interfacing with parameter interface. "arg" acts like a clientdata for type converter - added multiple parameter options handling to method "parameter" to obtain similar functionality from object parameters as from method parameters - added conveniance method "??" to test to indicated test that should fail with an error - added severy type converters to achieve same object type checking as in ::xotcl::is (these are currently in the regression test, should move finally into predefined.xotcl) - extended regression test - new function ::xotcl::parameterFromSlot (used in argument checker as well) - use ::xotcl::forward in Slot constructor (instead of dispatch) - checking methods on slots for single- and multivalues slots (can be optimized) - extended regression test - don't run multiple queries in "??" - fixed last changes to regression test as usual - added "multivalued" to parameter options - made error message produced by XOTclObjErrType look like Tcl's error messages - extended regression test - test utility: changed "?" to return error msg in case of error - checking multivalued types in "-parameters" by using argument checker - some cleanup - extend regression test valuecheck.001: 5.27 mms, ::xotcl::valuecheck object o1 valuecheck.002: ::xotcl::valuecheck object 1 ok valuecheck.003: 3.06 mms, ::xotcl::is o1 object - new cmd "::xotcl::valuecheck " where "valueConstraints" is whatever is allowed in e.g. parameters (including user defined types) - new Tcl_ObjType "xotclParam" - parameterFromSlot returns now pair object-param & method-param - define dissallowed parameter options for object parameter, method parameter and valuecheck command - make canonical table of parameter options (currently in tests/parameter.xotcl) - extend regression test - systematic checking, what valueconstraints should be allowed/not allowed in valuecheck - pass arg from objectparameter as first argument to objparms (similiar to client data) - support for parameter spec of form "type=XXX" to denote that the parameter must be an object of the specified type (class; directly or indirectly) - new built-in converter: convertToObjectOfType() - keep parameterObj (source for conversion to XOTclParam) for making introspection less work. However, this is only used for XOTclParams generated via ParamParse(). - extending regression test - name XOTclObjects always "object" instead of "obj" to avoid potential confusion with Tcl_Objs - remove unneeded push and pop operations in ListChildren() and ObjectHasChildren() - allow syntax "object,type=::SomeClass" and "class,type=::SomeMetaClass" (currently identical, we should as well check for class/meta-class in case of "class,type=SomeClass") - define everything concerning basic slot setup in a single "namespace eval ::xotcl {...}" - undefine ::xotcl::createBootstrapAttributeSlots after this block - fix default DefaultSuperClass() with isMeta==1 in cases, where we are already at the root meta class - use "-parameter" in xotcl1 instead of createBootstrapAttributeSlots - cleanup of legacy stuff in slot management. * merged InfoSlot and InterceptorSlot into RelationSlot * get rid of legacy "mixin set ...." command - renamed "parameterSlot" into "methodParameterSlot" to avoid potential confusions - refactor Slot class hierarchie - new methods in ObjectParameterSlot "toParameterSyntax" and "createFromParameterSyntax" - some more cleanup - removed legacy syntax for "-parmeters" - moved slot optimizer from ::xotcl::ObjectParameterSlot::Optimizer to ::xotcl::Attribute::Optimizer - support for all string constraints provided by "string is ... $value" in object and method parameters ("alum", "alpha", ..., "xdigit"). Technically, these types are tclobjs (using converter convertToTclobj) having pParm->converterArg set to the constraints. - extended regression test - get rid of convertToObjectOfType(), make convertToClass() and converterToObject() more general, accepting type constraints - predefined.xotcl: move toParameterSyntax and objectparameter to a position, where basic slot definitions are provided - fixed default value setting in bootstrap code - provide more precise error message in converter, when object type are used - extend regression test - added error message when substdefault is specified without a default value - extend regression test - added parameter option slot= to pass slotobj to a certain parameter - use slot option to keep relation between parameter and slot - object parameter dispatch type checker on slot objects - allow transformation via user-defined converter (can be use to standardize parameter values) experimental implementation, refcounting has to be looked in detail, maybe we need a different interface for the converters - provide checker-methods for -> objectParameter -> methodParameter - slotobject specific checker-methods - treat currently unknown converters in valuecheckcmd as error - fix the regression test from the last changes - added argument for converter to return the converted tcl_obj, which should be passed on (currently only used for viaCmd); this way, we could remove the kludge of querying the converter after the conversion for special handling. - handle multivalued + values converted viaCmd converter the new output list is only built when needed via ArgumentCheckHelper() - fix counter initialization in ::xotcl::importvar - register alternate "new" method in "contains" for xotcl2 and (if available) for xotcl1 objects - provide error message for cases, where parameter options are not allowed (or ignored) - move methodParameter checkers for "mixin", "baseclass" and "metaclass" to predefined. - deactivated checkMethods in gentclAPI.decls and in xotcl.c - renamed "::xotcl::is ... mixin ..." to "::xotcl::is ... hasmixin ..." (effects parametertypes as well) - made error messages for failed conversions more consistent (note that tcl does not provide the parameter name in the error message, but user-defined converters do) - fixed valuecheck in connection with modifying converters - extended regression test - fixed compilation for tcl 8.6b1 - Allowed parameter specification for setters. One can define now a setter with constraints like e.g. ::xotcl::setter o a:integer to define a setter named "a" for object "o" which has to be integer. - Extended regression test - Followed naming conventions for several structures - setterCmd(): Do not allow methodNames start with "-" - setterCmd(): Do not allow defaults for setters - extend regression test - removed duplciate error message in "hasmixin" converter - fixed refcounting in converting user-types in case of errors - extended regression test - added a "-nocomplain" option to ::xotcl::valuecheck - changed semantic of ::xotcl::valuecheck: per default, valuecheck raises an error or returns true. If "-nocomplain" is used, valuecheck returns 0 or 1 like implemented befor this change - extended regression test - added parameter "incremental" to ::xotcl::Attribute: when set, one can use "object paramname add|delete $value" etc. - use setters with parameter constraints in slot optimizer - as a consequence, setting attributes via slot names is about twice as fast as before, when parameter constraints are used. - extended regression test - fixed returned method name when setter was used on objects - reduce verbosity - implemented "info method definition|parameter|args $name" for settercmds (with parameter constraints) - extended regression test - fixed result resetting for user defined converters - ::xotcl::valuecheck: moved "-nocomplain" to first position (similar to e.g. unset) - experimental: allow to shadow built-in types provided that a) slot= is specified explicitely, and b) the specified slot is not the default slot. This should guarantee that we do not interfere with the predefined converters for the c-level interface. - incremented ref count on results of all-level converter - extended regression test - new methods for MetaSlot to factor out common code: + slotName (to ease name-construction, care about slot container) + createFromParameterSyntax: essentially move from ::xotcl::Attribute to the meta class - test environment: make sure to avoid confusions between the "namespace" method and command - added a version of the "attribute" method to predefined - removed the following classes and methods ::xotcl::Attribute->check_single_value ::xotcl::Attribute->check_multiple_values ::xotcl::Attribute->mk_type_checker class ::xotcl::Attribute::Nocheck - centralize fetching of tcl-obj-types in xotcl_init - support for method modifier "object", "protected" and "public" for method "attribute". One can use now e.g. Class create C { :attribute a :public attribute b :protected attribute c :object attribute A :public object attribute B :protected object attribute C } "protected" and "public" refers to the registered accessor functions - experimental checking function ::xotcl::is2 implemented, which generalizes between ::xotcl::is and ::xotcl::valuecheck (it is a valuecheck -nocomplain with an ::xotcl::is syntax and switched arguments) - Unified on c-level "info class-mixin-of" and "info object-mixin-of" to "info mixinof ?-scope all|object|class? ?-closure? ?pattern? The former "info class-mixin-of" is now "info mixinof -scope class" - adapted xotcl1 layer for this change - extended experimental ::xotcl::is2 to handle flags -type and -hasmixin ::xotcl::is2 object ?-type ? ?-hasmixin ? - renamed old "xotcl::is" -> "xotcl::objectproperty" - renamed old "xotcl::is2" -> "xotcl::is" - we have now is tests for objects in ::xotcl::objectproperty ::xotcl::objectproperty $obj object ::xotcl::objectproperty $obj class ::xotcl::objectproperty $obj baseclass ::xotcl::objectproperty $obj metaclass ::xotcl::objectproperty $obj type XXXX ::xotcl::objectproperty $obj hasmixin XXXX - "::xotcl::is" is the higher level command, supporting string contstraints "e.g. upper", user defined type checkers and as well object properties (every parameter type supported for object and method paameter). Examples: ::xotcl::is $obj object ?-type $type? ?-hasmixin $mixin? ::xotcl::is $cl class ?-type $type? ?-hasmixin $mixin? ::xotcl::is obj metaclass ::xotcl::is $num integer ::xotcl::is $string upper - implemented 2nd level reference counting for paramObjType - defined "info is" as alias of "::xotcl::objectproperty" - renamed ::xotcl::valuecheck -> ::xotcl::parametercheck - replaced in predefined occurances of ::xotcl::is by ::xotcl::objectproperty - fixed namespace handling on stack for objects with namespaces (before, it was possible that a variable was created in an object's namespace without -objscope) - as a consequence, ListChildren() had to be adjused, since it depended on the previous namespace handling on the stack - fixed object sesolving in NsDotVarResolver() (before, it was possible that NsDotVarResolver could create variables in the wrong namespace) - simplified NsDotVarResolver() - more cleanup in name resolver * USE_DOT is gone * XOTclDotDotCmd() removed * improved performance of InterpCompiledDotVarResolver() * made LookupVarFromTable() obsolete and removed it * renamed DotVarResolver() and friends to ColonVarResolver() etc. - extended regression test - call XOTclObject always "object" instead of "obj" - initcmd: use for initcmds CMETHOD frames instead of OBJECT stack frames - initcmd: skip parent-stack frame's objscope for initcmd - changed hash-based lookup of children into a cmd-based lookup - extended regression test - cleanup in stack handlers (naming, arguments) - XOTclCallStackFindLastInvocation(): return last scripted invocation - use xotcl1 in webserver test to make rull regression test working - make xotcl::use silent - added option "-nonleaf" for method alias - added introspection (for "info method definition") for "alias ... -nonleaf ..." - removed obsolete ::xotcl::configure option "cacheinterface" - removed XOTclCreateClass() and XOTclDeleteClass(); both are identical with XOTclCreateObject() and XOTclDeleteObject() - renaming of instance variable specific primitiva for more constistency with ::xotcl::importvar: ::xotcl::exists -> ::xotcl::existsvar ::xotcl::setinstvar -> ::xotcl::setvar - requireNameSpace: * fix potental crash in requireNameSpace in objscoped methods by swapping all vartable references in objscopes on stack to the vartable in the namespace, when it is created - extending regression test - working towards KEEP_VARS_IN_CMETHOD_FRAME - enable compilation with assertion turned on (via NDEBUG) - fix potentially uninitialized flags for ArgumentCheck() - some further cleanup, tested with 32 bit under Mac OS X 10.6 - removed obsolete generic/xotclAppInit.c - changed loading method in xotclsh from Xotcl_Init() to Tcl_PkgRequire() - updating tcl.m4 to the actual version (TEA 3.7) - minor cleanup (varresolution test and comment) - defined "xotcl::current" as synonym for "::xotcl::self" - new options for ::xotcl::current current object == current current method == current proc current callingmethod == current callingproc - "self proc" and friends are for backward compatibility for xotcl1, "current method" and friends are for xotcl2 - namespace exported "::xotcl::current" - use "::xotcl::current" instead of "xotcl::self" in predefined - use "CONST char *" in generated interface and below - further cleanup using "CONST char *" - remove dependency from xotcl1 in handling of forwarders in method "copy" - further cleanup using "CONST char *", improving on naming conventions - added an experimental "info frame" handler, which appends "object" and "class" pairs - fixed wrong name for per-object filter in RelationSlot - fixed condition in filter-incovation to top-level frames - added frametype to information returned by "info frame" - change frametype in "info frame" form bitpattern to symbolic names - use a more recent version of unix/tclAppInit.c - fix syntax in predefined - let serializer call "init" of attributes, even if it is protected - fixing "-parameter" with empty content - more variable naming cleanup - fix line breaking in serializer for "exportedObjects" - call c-implemented methods directly, when this is safe (implemented for XOTE_ALLOC, XOTE_CLEANUP, XOTE_CONFIGURE, XOTE_CREATE, XOTE_DEALLOC); this improves create/destroy speed by up to 15% - allocate namespaces for objects less eager - make nameing more consistent (change newObj into newObject when variable is an xotcl object) - get rid of misleading RCS: lines - passing --prefix to subdirs - regenerated configure files - added stefan's expat library linking extension - define RecreateObject() for internal call via direct invocation - doCleanup is just called by recreate; merge it into XOTclCRecreateMethod. Is method cleanup necessary? is recreate not sufficient? Delete "cleanup" from internally called methods in next, keep it for compatibility in XOTcl - make XOTcl_FrameDecls transparent (use variable declarations in place instead of the macro) - fix variable shadowing bug in error handling (could cause crashes when initcmd lead to errors) - call all truely essential methods directly if possible (Object.destroy, Class.alloc, Class.dealloc, Class.create) The basics of XOTcl can work now also in cases, when these are not defined as methods. - handling OBJECT-frames in CallStackPopAll() (for handling exit with active stack frames) - deactivate filters in finialize - new method InvokeMethodObj() to replace finally CanInvokeDirectly() - remove some more obsolete RCS Ids - call Tcl_Objs "Obj", not "Object" - replace all CanInvokeDirectly() by InvokeMethodObj() - block call-stack deletes during XOTCL_EXITHANDLER_ON_SOFT_DESTROY; however, this seems to create a small leak during exit (revant for threads); so currently, debug still turned on - fix last issue, with freeing objects, when exit happens from higher stack frames - first part of allowing arbitrary named internally called methods. - move refcount fixing logic for exit from higher stackframes to new function: finalObjectDeletion() - created new functions: ObjectSystemFree(), ObjectSystemAdd(), ObjectSystemsCheckSystemMethod(), ObjectSystemsCleanup(), GetObjectSystem(), CallDirectly() for better separation of systems. We keep track which essential system methods are defined and which which methods are potentially overloaded. - replaced hard-coded method calls for XOTE_DEFAULTMETHOD, XOTE_INIT, XOTE_OBJECTPARAMETER with MethodObj() - renamed MethodObj() to XOTclMethodObj() (it is an exported symbol) - eliminated XOTE_MOVE - eliminated XOTE_RESIDUALARGS - eliminated XOTE_UNKNOWN - eliminated XOTE___UNKNOWN - renamed __unknown to requireobject - provide prefix for internally called methods to distinguish between methods called on objects or classes - handling of minimal object systems. For example, the following three command create an object system around ::object and ::class ... ::xotcl::createobjectsystem ::object ::class ::xotcl::alias ::class + ::xotcl::cmd::Class::create ::xotcl::alias ::object - ::xotcl::cmd::Object::destroy ... where no internal message dispatch are used (e.g. no constructor "init", and where just two methods ("+" and "-") are used to create and destroy objects - extended regression test - get rid of reminder of tcl 8.4 compatiblity and remove range of ifdefs, such as PRE85, FORWARD_COMPATIBLE, TCL85STACK, CANONICAL_ARGS, USE_COMPILED_VAR_RESOLVER - rename CallStackPush() to CscInit() - rename CallStackPop() to CscFinish() - remove "buffer" from compiled var structures - remove xotcl1 dependency from aol-tcl - removed conditional var table creation by assertion - make clean compile with assertions turned on - xotcl 1.6.6: make sure to load always xotcl 1 versions when needed - xotcl 1.6.6: make compilation clean when compiled with assertions on - xotcl 1.6.6: more cases for the regression test, where we want to load xotcl1 not 2 - xotcl 1.6.6: one more cases for the packaging, where we want to load xotcl1 not 2 - replace hash-lookup in namespace in ObjectHasChildren() by pointer lookup to reduce namespace dependency. - fix memcopy size - add check for optional match arguments in tcl stub generator - fix potential memory leaks all "definitely losts" from valgrind (except 24 bytes from Tcl_PkgRequire) gone - fix a potential ordering problem with cyclic dependencies created by namespace import commands - Handle cases, where objects/classes are created with the name of preexiting namespaces. Cases, where pre-exisitng namespaces contained classes/objects lead to problems, since xotcl did not see the object/classes of the pre-exiting namespace as children of the new object/class. - Allow to speficy last arg of objectparameter to replace scripted init block. The redefinition of objectparameter allows us to specify whether no/some/classical/altered/additional arguments should be allowed during object creation - Naming namespaces: ::next ::next-core file exension: options: .tcl .xotcl .next .nxt file names: composite words with - instead of capitalization package names next::pkgname next::doc-tools # use - instead of _ or capitalization Classes use first name capitalized to distinguish from objects Objects typically, first charaction small - next::core as namespace name not perfect for adressing variables: set ::xotcl::version ${::next-core::version} set ::xotcl::patchlevel ${::next-core::patchlevel} do we need: checkMethod "::next::core::cmd::ParameterType" - namespace changes: mostly due to marketing reasons, the naming of the top-level namespace changed from "xotcl2" to "next". reasons: xotcl is hard to pronounce for beginners, sounds like "exotic" (but who wants to program in an exotic language) has a certain stigma of strange namings (e.g. "instproc"), is seen as a precursor of tcloo, the top-level namespace ::xotcl2:: is not very nice either, the separation of framework and language is not clear. We have now: ::next (the new object system, former ::xotcl2) ::next::core (framework, primitives) ::xotcl (former xotcl1) - "::xotcl::use" no longer needed, use Tcl standard mechanisms instead (e.g. "package req next"; "package req XOTcl", "namespace import ::next*") - [self next] returns instead of "proc" and "instproc" => "method" instead of "parametercmd" and "instparametercmd" => "setter" instead of "instforward" => "forward" instead of "instcmd" => "cmd" prefixed with the modifier "object" when needed - [self filterreg] returns instead of "instforward" => "forward" prefixed with the modifier "object" when needed - We have now: ::nx (the new object system, former ::xotcl2) ::nx::core (framework, primitives) ::xotcl (former xotcl1) - naming next scripting framework ::nx::core::existsvar <==> nx::var exists ::nx::core::importvar <==> nx::var import ::nx::core::setvar <==> nx::var set - copied infoObjectMethod and infoClassMethod decls as comments to xotcl.c, aligned order of method definitions - removed "[o exists varname]" from next scripting language - reanimated "info vars" to show locals in eval method - provide error messages for [objectproperty ... type ...] - replace 0 by NULL in calls to GetClassFromObj() - extended regression test - use size_t where appropriate - added notnull annotations - Implemented "Class info parameter" in Tcl, aliases for xotcl. Now both definition of parameters and setting of __parameter are in Tcl. - get rid of ":::xotcl::use" - renamed tests based on next from .xotcl to .tcl - extended regression tests - use namespace ::nx::test instead of ::xotcl::test - use namespace ::nx::serializer instead of ::xotcl::serializer - rename xotcl1.xotcl to xotcl.tcl - some cleanup (version variables, etc.) in xotcl.tcl - renamed tests/method-modifiers.xotcl to tests/method-modifiers.tcl - changed "require xotcl::test" to "... next::test" - changed "require next" to "... nx" - changed "require next::test" to "... nx::test" - changed "require next::doc" to "... nx::doc" - added missing ./tests/var-access.tcl to git - added section about registering filters and mixin to migration guide - moved and transformed to next tests/mixinoftest.xotcl -> tests/mixinoftest.tcl - moved and transformed to next tests/object-system.xotcl -> tests/object-system.tcl - changed pkgIndex reference for .so file from next ot nx - changed stubs from xotcl to nx - first part of openacs updates - changed "Serializer.xotcl" to "serializer.tcl" (package name from xotcl::serializer to nx::serializer) - added stub for xotcl::serializer for backward compatibility - changed serializer to new namespaces - renamed xotcl.tcl to xotcl2.tcl - added proc finalize to xotcl2.tcl - renamed mk_predefined.xotcl -> mk_predefined.tcl - renamed predefined.xotcl -> predefined.tcl - additional subcommand "info method parametersyntax " returns parameters in a syntax similar to the tcl man pages - added ability to pass syntax for forwarded methods via set ::nx::core::signature(::nx::Object-method-forward) (experimental) - fixed documentation system to work with actual version - added undocumented methods for quality control in documentation - added checks for documented, but unavailable methods in documentation - added comparison of documented parameters vs. actual parameters in documentation - added @properties and has_property to the documentation classes. Current primary purpose: define, which methods are internally-called - added internally-called to the method object template - added redefine-protected to the object template - added methodtype to object template - some documentation updates - some indentation/spacing improvements on xotcl.c - let ".... info method .... METHOD" return values, when METHOD contains namespace prefix. This can be used to obtain the parmeter definitions from nx::core - get forward definition from the original command - created own directory structure xotcl under library containing doc, tests, apps, lib etc. and moved obvious content here. - adjusted regression test and old documentation system to work with new structure old structure xotcl apps actiweb comm persistence scripts utils xml config doc library lib comm patterns rdf registry serialize store xml man tests unix win new structure nx config doc library lib serialize xotcl apps actiweb comm persistence scripts utils xml doc library comm lib patterns rdf registry store xml tests man tests unix win - moved some more xotcl specfic tests to library/xotcl - transformed forwardtest from xotcl to next - moved slottest to library/xotcl - added new Makefile target test-xotcl - finished test migration for now - deactivated __next for now - iterated through doc.tcl-TODOs - changed CheckVarName to allow array names like e.g. a(::b) - extended regression test - fixed serializer to handle subobjects of explicitely exported objects - xotcl.c: * new function GetObjectFromNsName() to obtail object or class from a fully qualified namespace name used in method handles (such as e.g. ::nx::core::classes::X) * new function MethodHandleObj() to return a tcl_obj containing the methodhandle * removed obsolete method getFullProcQualifier() * info methods obtain now object and/or class from fully qualified method names (method handles) if possible * return message handles in "current next", "current filterreg" and "... info filter ... -order", which can be used in "info method .... " for obtaining more details. * change all occurrances of "self" in next regression tests to current. - xotcl2.tcl * implemented "self" as a proc to provide extensibility and full backward compatibilty; this opens opportunity to replace now e.g. "self proc" by "current method", etc. * provide full compatibility for "self next", "self filterreg" and "... info filter ... -order", returning old-style multiword method handles (such as e.g. "::C instproc foo") - changed "next" to current in documentation framework and templates - updated migration guide, added section for callstack introspection - updated serializer for new names - Introduced $xotcl_target_doc_dir for generated xotcl documentation. Generate xotcl documentation in this directory. - moved more (hopefully all) xotcl doc components into library/xotcl/doc - added interp alias "nx::self" to "nx::core::current method" - changed "current proc" into "current method" in scripts and tests - file extension for next scripting .tcl DONE - changed ::nx::core to ::nsf - made the "next scripting laguage" a own, loadable tcl package (currently named nx, name is subject of change) - predefined.tcl is now pretty minimal. - updated to TEA 3.8 - moved all exports of nsf to predefined.tcl - made imports in xotcl2 and nx explicit - adjusted path in documentation system for nx/nx.tcl - Implemented "interp alias" support for classes. In some cases. interp-aliased classes worked already without additional code, but e.g. in "... -superclass C ..." it failed. Without this feature, one could not reuse a class with a different namespace, unless it was explicitely "namespace exported" in the source. The problem was the implementation of "::nx::Attribute", which should not be exported in nx (most people do a "namespace import ::nx::*") because there is no need to do so, but ::xotcl::Attribute should reuse it - without subclassing). .... However, we still seem to have a problem, when the interp-aliased Class is exported and imported to a different namespace. - TODO: info methods shows finally "slots" and "slot". Wanted? Actually no. - removed definition of slots from nx, changed regression tests examples from slots to ::attribute - replaced several occurrences of "eval" in nx.tcl and xotcl2.tcl - implemented parameter option "allowempty" - extended regression test - commented out XOTCL_CMD_NOT_FOUND, since it seems to be obsolete by now - extended regression test to avoid CallDirectly on dealloc (the last place, where XOTCL_CMD_NOT_FOUND was used) - implemented return value checker (for scripted and c-implemented methods) - additional methodproperty returns (for registering a return value checker) - support for incrementally adding stuff to paramDefs (such as slotobj or return value) - new c-function ParamDefsNew() - added regression test for return value checker - upgraded to TEA 3.9 - nsf: provided scripted support for "require/provide methods" - nx: new method ":require namespace" ":require method" "require object method" - added regression test method-require - removed requireNamespace from nx.tcl (still exists in xotcl) - replaced "requireNamespace" by "require namespace" in nx regression tests - updated migration guide - removed method "autoname" from nx.tcl - added "method require autoname" - added "method require exists" - removed method "filtersearch" from nx.tcl - added "obj info method filter methodName" to nx - updated xotcl2 to use new filtersearch imprementation - updated migration guide - renamed "info method name ..." into "info method handle ...", since it returned already the handle - renamed ListMethodName() to ListMethodHandle() - changed output of "info callable -which ..." from definition to method handle - renamed "info callable -which ..." into "info callable method ..." - updated regression test to reflect the changes - changed "info method filter ...." into "info callable filter ..." - fixed "o info callable method" in some cases with mixins - extended regression test - updated migration guide - made Class.info, Class.mixin, Class.filter robust against per-object mixins from meta-classes - extended regression test - checked saftey of Class.method, Class.alias, Class.setter, Class.forward - made Class.filterguard, Class.mixinguard, Class.attribute robust against per-object mixins from meta-classes - fixed mixin/filter delete methods with guarded operations - extended regression test - all methods defined on both, Object and Class are now safe in respect to per-object mixins with meta-classes - make slot optimizer more robust - removed methods object-mixin and object-filter from the interface. (Caused some duplication of logic in the method "object") _ added option noforwarder to RelationSlots - some minor cleanup - removed XOTCL_METHODTYPE_OBJECT from XOTCL_METHODTYPE_BUILTIN - default methodtype returns now everything, which is a true method (except objects) - methodtype -all includes objects - the object "slot" does not appear now in the method listing per default for classes having slots - changed __invalidateobjectparameter from a method of class to framework primitiv ::nsf::invalidateobjectparameter - experimental method-property "class-only": this allows to make object save against per-object mixins of meta-classes. the flag is only used in the mixin-resolver - used for the time being in nx only for Class.info, but would apply as well for methods defined on both Object and Class. - use now class-only for all methods methods of meta-classes. Methods of meta-classes are intended to be applied on classes, one should not change this via per-object mixins. - respect class-only in "info callable methods|method" - extended regression test - provided relation name "object-filter" to slot filter. - replaced "obj|cls filterguard name cond" by "obj|cls filter guard name cond" - replaced "obj|cls info filterguard name" by "obj|cls info filter -guard name" - replaced "cls object info filterguard name" by "cls object info filter -guard name" - removed XOTclObjInfoFilterguardMethod() - removed XOTclClassInfoFilterguardMethod() - extended regression test - updated migration guide - replaced "obj|cls mixinguard name cond" by "obj|cls mixin guard name cond" - replaced "obj|cls info mixinguard name" by "obj|cls info mixin -guard name" - replaced "cls object info mixinguard name" by "cls object info mixin -guard name" - removed XOTclObjInfoMixinguardMethod() - removed XOTclClassInfoMixinguardMethod() - extended regression test - updated migration guide - deactivated "abstract" - implemented experimental delegating version of "object as method" that keeps the original self. - changed requireNamespace to "require namespace" in lib/make.tcl - use prefix sub= for methods invoked on "object as method" - change further instances of "my connection" to "[self]::connection" in xo*comm* - implemented "object-methods-only" as alternative for prefix for invoke "object as a method" - added option "-returns" to Object.method - added option "-returns" to Class.method - added subcmd to method/object method in nx - delete class methods in freeAllXOTclObjectsAndClasses() explicitly to handle potential double-deletes - extended regression test for subcmds - started new interface bundles, objectInfoMethod and classInfoMethod for using new infrastructure - added object info methods filterguard, filtermethods, vars to objectInfoMethod - added class info methods filterguard, filtermethods to classInfoMethod - built a temporary solution for dispatcher "filter", since forward mangles args - nx: we have now "obj info filter guard name" instead of "obj info filter -guard name" - nx: we have now "obj info filter methods ...." instead of "obj info filter ...." - added object info methods mixinguard, mixinclasses to objectInfoMethod - added class info methods mixinguard, mixinclasses to classInfoMethod - built a temporary solution for dispatcher "mixin", since forward mangles args - nx: we have now "obj info mixin guard name" instead of "obj info filter -guard name" - nx: we have now "obj info mixin classes ...." instead of "obj info filter ...." - updated migration guide - changed info to new interface (partly done for nx, migration for xotcl to be done) - fixed "info methods" and added "-methodtype all" for setting class-only - regression test works now until first XOTcl reference - Changed handling of "child objects": now, they are shown by default. - At the same time, the subobject "slot" was made protected to avoid its listing per default in "info methods" - unified slot parent-object creation handling - changed XOTcl info to new interface - reanimated 5 tests in xotcl/tests/testx.xotcl - reanimated 5 tests in tests/destroytest.tcl - changed resolve_method_path to __resolve_method_path and made it protected - fix requiredness of last argument in parametercheck - return "object" for "info method type ...." when method is an object. - return valid creation command in "info method definition ...." when method is an object. - extend regression test - eliminated "info classparent" and "info classchildren" - added tests to xotcl/tests/testx.xotcl to assure equivalence - backported fix for xotcl 1.6.6 reported by kristoffer lawson, which helps just partly here - extended regression test - added class ::nx::EnsembleObject - factored out DispatchUnknownMethod() - added flag XOTCL_CM_NO_OBJECT_METHOD to ObjectDispatch() and friends - added tests/subcmd.tcl - added methods "defaultmethod" and "unknown" to ::nx::EnsembleObject (together with a set of helper methods to provide user-friendly information) - scripted "info slotobjects" to return only objects of class ::nx::Slot - fixed test with UnknownClass in xotcl/tests/testx.xotcl - fixed silent (scripted) unknown handler. - reavtivated corresponding regression test - extended regression tests (call unknown method with filter with and without unknown handlers) - make sure to test next to non-existing shadowed method in connections with filters and unknown handlers - documented incompatiblity of object-invocation via method interface (due to ensemble objects) in migration guide - implemented XOTclObjInfoHasMixinMethod() and XOTclObjInfoHasTypeMethod() - renamed "$obj info hasnamespace" to "$obj info has namespace" - added "$obj info has mixin $class" - added "$obj info has type $class" - extended regression test for parametercheck/objectproperty/is - updated interface definitions for info methods, sort these alphabetically - removed "objectproperty .... hasmixin" - removed "nsf::is ... -hasmixin ...." - removed type-converter "type=hasmixin" - adoped emulation layer in xotcl2 accordingly - added two tests for "info has mixin" to regression tests - removed "objectproperty .... type" - renamed isSubType() to IsSubType() - adoped emulation layer in xotcl2 accordingly - added two tests for "info has type" to regression tests - removed "nsf::is ... -type ...." - adoped emulation layer in xotcl2 accordingly - extended regression test - introduced ::nsf::isobject - replaced in all scripts "::nsf::objectproperty ... object" by isobject - removed "infoObjectMethod" and "infoClassMethod" - replaced ::nsf::cmd::ClassInfo2 by ::nsf::cmd::ClassInfo - replaced ::nsf::cmd::ObjectInfo2 by ::nsf::cmd::ObjectInfo - changed argument order on objectproperty to make it conformant with Tcl conventions - updated migration guide - changed argument order on nsf::is to make it conformant with Tcl conventions - removed objectproperty, replaced it by ::nsf::is - move functionality of objectproperty to make "obj info is ..." more efficient - report "invalid parameter" in nsf::is and parametercheck, even when no-complain is used. - fixed reference counting problem with user-defined converters - added flag -complain to ::nsf::is - removed ::nsf::parametercheck - new parameter option "convert" to signal that an application specific parameter checker should convert the value (takes the result of the methods as conversion result) - added parameters for slots "allowemtpy" and "convert" - extended regression test - added handling of parameter options "allowemtpy" and "convert" in createFromParameterSyntax - renamed slot attribute "noforwarder" to "nosetter" - method parameter can now use option "nosetter" to allow object parameterization witout providing a setter method (example: "Class create C -parameter {x:integer,nosetter}") - extended regression test to include "nosetter" - new flag for configure: "nx::configure checkresult on|off" - turn off result-checking for non-converters, when checking is off (per default, it is on) - extended regressi - new flag for configure: "nx::configure checkarguments on|off" - turn off argument-checking for non-converters, when checking is off (per default, it is on) - extended regression test for optional argument checking - reflected changes in /is/objectproperty/info has/info is/ in migration guide - changed stub naming from "[Nn][Xx]*" to nsf (for next scripting framework) - checked "my" vs. "nsf::dispatch" in nx.tcl and xotcl2.tcl - "child objects" are showed per default in "info methods" - big renaming orgy (BRO): - changed filenames starting to "xotcl" into filename starting with "nsf" - adoped Makefile insfrastructure accordingly - removed compile flag XOTCL_METADATA and generic/xotclMetaData.c - changed compile flag PROFILE into NSF_PROFILE - BRO continues: - changed all XOTCL_ into NSF_ - changed all XOTCLINLINE into NSF_INLINE - changed all XOTCLOBJ_TRACE into NSFOBJ_TRACE - changed all XOTcl_ into Nsf_ - changed all XOTcl([A-Z]) into Nsf\1 - changed all xotcl into nsf - changed changeClass into ChangeClass - changed XOTclpGetObject into GetObjectFromString - changed XOTclpGetClass into GetClassFromString - changed callDestroyMethod into DispatchDestroyMethod - BRO continues: - overworked function prototype definitions in nsf.c - renamed some static definitions on the way to follow Tcl conventions (start with a capital character) - added flag "-type" to NsfObjInfoChildrenMethod - added flag "-type" to NsfObjInfoSlotObjectsMethod - removed dummy argument "pattern" from NsfObjInfoSlotObjectsMethod - removed NsfClassInfoSlotsMethod (implemented via "children ... -type ...") - moved "info slots" from nx::Class to nx::Object (to report e.g. per-object attributes) - extended regression test - [::nx::Object info method parameter info] returns now empty instead of error. - extended regression test - splitted "info callable" into an ensemble (submethods have quite different signatures) - added "info callable slots" with functionality of "info slotobjects" - removed "info slotobjects" - handle aliases to (sub)ensemble objects during final cleanup to improve sharing of logic. - share definition of "info callable" and "info has" ensemble between object info and class info - new function AliasDeleteObjectReference() to delete aliases to objects - removed some obsolete functions - changed "info available" into "info lookup" (and accordingly c definitions, migration guide) - pass tclobj instead of string to NsfObjInfoMethodMethod and NsfObjInfoMethodsMethod - first part of ensemble name resolver. We can resolve now e.g. [nx::Object info method parameter "info lookup methods"] - second part of ensemble name resolver, distinguish between registration object and definition object - new functions: GetRegObject() - extended regression test - fixed handles with subcommands used on objects without namespaces - new functions: GetRegObject() - fixed handles with subcommands for class methods when called on classes or objects - extended regression test - changed "cls object method ..." and friends into "cls class-object method ..." - added "info method subcommands ..." to return list of subcommands (of the ensemble) - extended regression test - use always NULL instead of 0 when assigning to a pointer variable - improve comments in nsf.c - follow closer naming of Tcl style guide - removed overhead on ::nsf::dispatch when called with absolute paths - absolute paths have to start with ":". - checked equivalence of the following two commands in respect to fully-qualified names ::nsf::dispatch $obj ::nsf::cmd::ObjectInfo2::hastype $class ::nsf::parametercheck object,type=$class $obj extended regression test (parameters.tcl) - renamed "parameter" into "attributes" in nx - renamed "info parameter" into "info attributes" in nx - updated migration guide - fixed several common typos - documented behavior of upvar/uplevel with aliases on scripted procs through regression test - implemented next within ensemble methods - added regression tests for next within ensembles - added regression tests for upvar with ensembles - refactored next and argument passing (new methods NextGetArguments(), NextSearchAndInvoke() and CallStackFindEnsembleCsc()) - doing an internal next in cases where a method ensemble does not contain the called submethod - added regression tests for partial ensembles - renamed "... info ... subcommands ..." into "... submethods ..." - rennamed tests/subcmd.tcl info tests/submethods.tcl - moved relevant tests from tests/parameters into tests/submethods.tcl - renamed functions in nsfStack.c to follow Tcl naming guidelines - call internal NextSearchAndInvoke (without NextGetArguments) from the implicit next in ensemble methods - made NsfNextMethod() obsolete to ease different noarg handling policies - new nsf::next cmd. receives 0 or 1 argument, which might be a list of arguments. This avoids ambiguity of ::xotcl::next with "--noArgs". - renamed namespace_copycmds and namespace_copyvars to nscopycmds and nscopyvars (we don't use "_" in nsf::*-names as delimiters elsewhere) - renamed __qualify to qualify (it is a non-exported cmd) - handle next in ensemble with arguments - extended regression test - removed obsolete code - made methods (for now just scripted methods) by default protected. - provide methode __default_method_protection to obtain the default method protection when neither protected or public is used. - per default methods are now protected - provide ::nx::configure defaultMethodProtection true|false as conveniant interface - update regression test and serializer to handle default protection - decide on paths for documentation of next and xotcl 2, with version numbers; what should be included in distro, what on web-site - decide on syntax subcomponent. Candiates are * Object.method * Object->method * Object#method - handling namespaces in documentation # @object ::nx::Slot vs. # @object Slot (best allow both variants, write fully qualified name via introspection) - why only @object? there seems to be no @class. what to do with metaclasses? - systematic way of specifying results of methods - systematic way of reporting results in documentation - handle line-breaking in long @definitions (e.g. @param; e.g. via indented next line) - danger, tcl-commands in comments (see :method get_unqualified_name) expecially for code commented out.... - kann man "[:? {[$attr eval {info exists :default}]}" durch "[:?var :@param ..." ausdrücken? oder vielleicht besser die variablen mit leerstring initialisieren + infrastrukt anpassen? - listing von methoden im left-bar, ähnlich http://developer.yahoo.com/yui/docs/YAHOO.util.Connect.html - "Objects" im left-bar irreführend, sollten eher "Classes" sein. Allerdings sollten auch objekte dukumentierbar sein - doc-tools: was machen die argumente von :? (bspw. ops?); ich nehme an, das ist work in progress. sinnvoll wäre: [:?var obj varname body], da viele (die meisten) operationen auf anderen objeken ausgeführt werden - die Dokumentation der Objekt- und Klassenmethoden muss aus gentclapi weg und in predefined.tcl und xotcl2.tcl hineinwandern. Es werden nicht alle möglichen methoden in next und/oder xotcl2 registiert, ein paar namen sind anders, etc. - fixed a crash for tcl 8.6b1 in return value checking. now it complains about missing cmdPtr; it is not clear, why this is missing at the first place in 8.6b1 while working in 8.5 - improved library/lib/make.tcl once more - fixed handling of TclNRRunCallbacks, such that coroutines can be easily used (more testing required) - added temporary routine ::nsf::yieldcheck for coro debugging - renamed Tcl85showStack() to TclShowStack() - Big internal changes for handling nre-enabled procs in more situations. Handles now all nx regression tests, but fails in testx.xotcl (just nre-enabled) - passing part of dispatch flags in cscPtr->callType - runs now full regression test NRE enabled, but still much debugging and options for less conservative behaviour - some cleanup concerning TCL_STACK_ALLOC_TRACE - make ::nsf::next and ::xotcl::next NRE-enabled - make coloncmd are NRE-enabled - make every internal method invokation (NsfCallMethodWithArgs() and CallMethod()) NRE-configurable - use "Nsf" prefix for global vars to avoid potential conflicts - minimal support for sane-NRE patch - failed so far to make my NRE-enabled - new compile-flag for tracing: NRE_CALLBACK_TRACE - extended regression test (added test for 8.6 apply) - renamed tests/coroutines.tcl to tests/tcl86.tcl - some refactoring for making code structure more sane for NRE (but not done yet) - save snapshot; refactoring in order to ease NRE development with unified method and dipatch exit. - named debugging cmds __db_* - new cmd __db_run_assertions to perform checking of the internal state - simplification and unification of unknown handling and method finalization - some cleanup - make "createobjectsystem" more robust (allow to provided not fully qualified names). - added tcl-cool as an additional sample-object-system for nsf - changed internal method name requireNamespace to "require_namespace" - changed debug command __db_yield (unporable between various 8.6b* version) into __db_show_stack - some more cleanup - provide flag for DispatchDefaultMethod() to control immediate execution. - nre-enable DispatchDefaultMethod() for simple cases - removed TCL85STACK_TRACE - renamed cscPtr->callType to cscPtr->flags, since this is now more appropriate - some more minor cleanup - changed "info method lookup -application" into "info method lookup -source application" - introduced "info method lookup -source application|baseclasses|all" - updated migration guide - extended regression test - provide debugging output when varFramePtr in GetSelfObj() is NULL - filter misleading "proc" entry for "info frame" for nsf-stack entries - add "method" for "info frame" for nsf-stack entries - defnied SKIP_LEVELS to omit optionally skipping of tcl-stack entries in GetSelfObj() - added scripted replacement for binary nxsh - new define SKIP_LAMBDA to make apply tests working without SKIP_LEVELS - renamed ::nsf::cmd::ObjectInfo into ::nsf::methods::object::info - renamed ::nsf::cmd::ClassInfo into ::nsf::methods::class::info - renamed ::nsf::cmd::Object into ::nsf::methods::object - renamed ::nsf::cmd::Class into ::nsf::methods::class - removed capitalization from exit handler interface - reduced interface of exithandler to ::nsf::exithandler set|get|unset ?arg? - renamed/removed remaining ::nsf::commands with capitalized names: parametersFromSlots ==> parametersFromSlots unsetUnknownArgs ==> __unset_unknown_args infoError removed - renamed predefined.tcl into nsf.tcl - remaining cmds in nsf (except __*) containing "_": ::nsf::provide_method, ::nsf::require_method - removed DISPATCH_TRACE - moved return-value checking into ObjectDispatchFinalize() - perform invariants checking after cmd execution, not additionally before - commented dispatch machinery - added nxwish.in (scripted replacement for former xowish) - added xotclsh.in (scripted replacement for former xotclsh) - added xowish.in (scripted replacement for former xowish) - added error handling to all scripted shells - removed old xotclsh.in and xowish.in (from apps/utils) - altered xotcl.m4 to nsf.m4 in ., library/xotcl/library/store/XOTclGdbm/, library/xotcl/library/store/XOTclSdbm/, library/xotcl/library/xml/TclExpat-1.1/ - removed traces of xotcl.m4 - removed build flags with-nxsh, with-xowish (since these are scripted now) - removed flag --with-tk (not needed anymore) - removed NXSH and XOWISH from Makefile.in - used Tcl_ObjCmdProc in prototypes - allow CMETHOD to be target of calling-object/calling-level - added NSF_CSC_CALL_IS_TRANSPARENT to handle proc aliases transparently - access self in NsfProcAliasMethod() from tcl->object; - added public|protected to output of "info method definition" (needed to make serializer more sane, neccessary on the longer range) - reduce size of output of serializer - make nx::Object.serialize public - XOTcl 2: allow info slots for objects as well - serializer: * added support for ordering on aliases referencing other objects/classes * provide shared version of the method warn via alias and removed direct output to stderr - slots: * change name "initcmd" of "experimental slot features" to "defaultcmd" to avoid naming conflict the the initcmd executed at the initilization of a slot object (effects XOTcl as well) * make defaultcmd/valuecmd/valuechangedcmd working for nx (absence of trace method) * provide error message, when default/defaultcmd/valuecmd are used non-exclusively * make sure to init per-object slots after copy operations * make nx::Attribute.destroy more defensive * extend test cases - nsf: added flag NSF_DESTROY_CALLED_SUCCESS in addition to NSF_DESTROY_CALLED to distinguish between attempted and successful destroy method invocations (important for cleanup) - fix potential crash in ::nx::Object info method definition ::nsf::methods::object::instvar - "info method submethods": return all submethods, independent from protection - serializer: experimental code to serialize submethods - new option "-expand" for "obj|class info methods" to return compound names (i.e. it lists the full ensemble names) Example: ::nx::Object info methods -expand "*filter*" returns filter {info filter guard} {info filter methods} {info lookup filter} - allow ensemble names in nsf::methodproperty - fix compound name lookups when aliases link to shared ensemble objects - make objectName() NULL-safe - fix option "-source application" when applied directly on base-classes - extend regression test - nsf.c: use name "varTablePtr" instead of "varTable" when referring to the table - new option "slotcontainer" for "methodproperty" to flag slotcontainer to make them easier to process in the serializer . don't report slot container in "info methods -expand" - new function "::nx::isSlotContainer" to centralize checking for slotcontainers (used by serilaizer) - support export of method ensembles in serializer - "info lookup methods": order of non-pos parameters alphabetically - added option "-expand" to "info lookup methods ". It collects ensemble methods along the precedence path - added support for ensemble methods in "info lookup method" - extended regression test - provide full set of ensemble methods from EnsembleObject.unknown (i.e. from all classes along the precedence order. It was necessary to pass calling object as first argument) - "info method parametersyntax" returns now for all nonpos args the type instead of "arg" when possible (eg ..... ?-x object? ....) - extended regression test - factored out ParamGetType() to obtain from a paramPtr a type string - parametersyntax: * changed "... -x arg ..." into ".... -x value ..." * changed multiple values notation from "list" to "..." - nsf::current: new option "methodpath", returns the full name of an ensemble method starting from the ensemble root. - documented functions in nfsStack.c - removed obsolete CallStackGetFrame(), replaced by CallStackGetTopFrame() - push stack frame before calling the defaultcmd of an ensemble object to make implementation more simple. - simplified EnsembleObject.defaultcmd and EnsembleObject.unknown significantly, scripted support methods are removed. - extended regression test for "current methodpath" - allow %method in forwarder. - defined unknown methods as call-protected - make __default_method_protection protected - added syntax "?arg ...?" in parametsyntax output for "args" - removed "info forward" from nx::Object and nx::Class (can be replaced by "info methods" and "info method") - Methodpaths can be used now in the definition of "method", "alias" and "forward." We do not support it for "setter" and "attribute", since these require a parameter spec, which does not have clear semantics for a method path. - scripted definition of nx::Object.forward and nx::Class.forward - cleanup of __resolve_method_path - change TclShowStack into NsfShowStack() to avoid possible interference with the Tcl* namespace - made the following function static to avoid pollution of the global link namespace: CompiledColonVarFree(), GetRegObject(), ParamGetType() - changed option -expand in "info methods" and "info lookup methods" into "-path" to associate with the method path - changed method property name from "protected" to "call-protected" - changed nx::defaultMethodProtection to nx::defaultMethodCallProtection - nx::defaultMethodCallProtection is used for scripted methods, forwarders and aliases - added nx::defaultAttributeCallProtection, used for setter and attributes - call scripted converters without checking protection - removed defaultMethodCallProtection from tests/parameters.tcl - Implemented built-in-converter for "baseclass" and "metaclass". Change in performance for this call. >8 times faster before: parameters/parametercheck.007: 19.49 mms, ::nsf::is baseclass C after: parameters/parametercheck.007: 2.32 mms, ::nsf::is baseclass C - remove scripted definition of "baseclass" and "metaclass" - keep track of defaultMethodCallProtection and defaultAttributeCallProtection in serializer - cleanup aol-xotcl.tcl and document usage in aolserver and naviserver - iteration over TODO file - removed obsolete entries from generic/nsf.decls und generic/nsfInt.decls - removed NSF_CMD_NOT_FOUND - fixed aliasing warning for gcc 4.4.4 - removed CheckAllInstances() - added functionality to show enumerated values in "info parametersyntax" - extended regression test - added expermimental code to avoid "variable :x" crash based on shadowCommands, but this does not work, when varible is bytecompiled. deactivated for now. - added support for aolserver (essentially Makefile + aol-xotcl.tcl) - removed unneded content from serializer output - the two flags "-objscope" and "-nonleaf" are for adding frames, and they are mutual exclusive. Make them a single flag? check if both options are in every case sensible. possible realizations: -scope object|method -varscope instance|proc -varscope instance|resolver|none -frame object|method|default * instance|object: within this method, all non-prefixed var names refer to instance variables; the method can use most probably not "next" (actually, only needed for XOTcl) * method|proc|resolver: within this method, we can use colon-prefixed variables; the method can use "next" "object" könnte mit dem -per-object (dem registierungpunkt) leicht verwechselt werden. es ginge auch -varscope instance|method allerdings, meint method eigentlich "scripted method". "none" would be dangerous for "-frame", since it could imply to avoid frame stacking at all. effected are: alias, forward, dispatch für "alias" betrifft das in gleicher form auch die cmds, bei "dispatch" und "forward" gibt es dzt. kein "-nonleaf" - replaced "-objscope" and "-nonleaf" by "-frame object|method|default" for nsf::alias and nsf::default - added functionality for "-frame method" to nsf::dispatch - made the order of argument in ::nsf::alias and method "alias" the same (always first the method, then "-frame ...") - extened regression test - renamed some arguments of tcl interface to increase consistency - make requiredness explicit for nsf::cmds - introduce ::nsf::parametersyntax to provide syntax for potentially missing definitions - provided ::nsf::parametersyntax for 3 ::nsf commands and 7 nx methods (from relationslots) - fix requiredness of several info methods - added "nsf::configure debug ?level?" - use "nsf::configure debug" for value 1: compain about protected value >1: provide load messages for nx and xotcl - unset unneeded variables in ::nx namespace - copied decls for objectMethod and classMethod as comments to nsf.c, fixed order - documented a few functions - enabled nsf::__db_run_assertions in nx::test (lead before to false posigives in destroy-test) - eliminated deleted objects and objects from deleted namespaces in GetAllInstances() - added handling of unstacked csc entries (removed all DEBUG warnings). - made handling of unstacked entries optional by defining macro CHECK_ACTIVATION_COUNTS) - added macro NSF_DEVELOPMENT for toplevel handling if NDEBUG and CHECK_ACTIVATION_COUNTS - cleanup of method-modifiers.tcl - updated next migration guide - follow current Tcl vonvention for patchlevel var: - changed name of ::nsf::patchlevel to ::nsf::patchLevel - changed content ::nsf::patchLevel from eg .0 to full number including release details - fixed bug in ::variable with colon-prefixed name (shadowCommands does not help, see above) - removed traces of Nsf_VariableObjCmd() - extended regression test - provided parametersyntax definitions for XOTcl 2.0 similar to nx for all methods without a spec (e.g. fur builtin Tcl cmds, forwarders) - make sure not to return CompiledLocal vars from InterpColonVarResolver() when TCL_NAMESPACE_ONLY is requested. - delegate always from InterpColonVarResolver() to other resolvers, when TCL_NAMESPACE_ONLY is requested. - implemented exported command ::nsf::self as fast conveniance replacement for "::nsf::current object". - removed bug-alert from nx.tcl (wrong false-positives for compiled locals in slots) - added a few small optimization. nsf appears to run on the shootout benchmark the same speed like a year ago (which much less functionality) - added a few more small optimization. - code-generator: don't call argument parser, when no arguments are specified - fixed bug when calling aliased proc not via method interface - fixed bug when calling destroy in initcmd - allowed public|protected for method deletion such as "Object public method foo {} {}" - removed defaultMethodCallProtection in alias test - extended regression tests for aliases to procs - renamed nx regression tests .test to follow tcl conventions - added regression tests for destroy-during-init - removed debugging from NsfCleanupObject when compiled without DEVELOPMENT - removed debugging from CscFinish when compiled without DEVELOPMENT - changed CallStackGetActiveProcFrame() to return also CMETHD frames This allows to execute :volatile in a initcmd and to delete the object at its end. As a consequence, code like [CopyHandler new -volatile] copy [::nsf::self] $newName has to be changed to CopyHandler new { :copy [:uplevel ::nsf::self] [uplevel set newName] :destroy } - renamed CallStackUseActiveFrames() to CallStackUseActiveFrame() and ctx->framesSaved to ctx->frameSaved to reflect implementation - new function MethodNameString() to obtain name without colon prefix from tcl_obj - fix bad interaction between filters and cmd name resolvers - output object frame to ease interpretation of [info frame] - fixed scoping issue, when "-volatile" was used for object creation - added regression test for interaction between filters and function resolver (and volatile) - reactivated new volatile test in destroy.test - undone temporary fixes for volatile in serializer and nx.tcl - impoved NsColonVarResolver, made some assumptions explicit - gentclApi.tcl: added optimizer rule for single argument of type tclobj - improved speed of CompiledLocalsLookup slightly - added an experimental code for setting parent namespace path as default for child-objects. At the time when an object namespace is created, the namespace path of the parent object is copied to the child as default value. - added new contains definition based on "apply" instead of "namespace eval". Main intention is to replace SKIP_LEVELS by SKIP_LAMBDA - added functionality to use ":attribute contains:method,nosetter" - added regression test for contains and attributes of type method - activated SKIP_LAMBDA in nsfCallstack. As a consequence, we disallow resolving self/my ... from tcl-proc frames (use uplevel if necessary, avoid "namespace eval") - improving error messages from argument parser - test "namespace current" and "self" in "contains" and "slots" regression test - added "nosetter" automatically, when attribute ":method" is used - fix a bug, where "o configure" (without arguments) resetted initialied values to the defaults. - show "unwind unstacked entry" message appear only when debug level>0 - removed fixed TODO entries - New function NsfNamespaceInit() to initialize pre-existing namespaces in PrimitiveOInit() and in RequireObjNamespace() - provide error message, when provided setter name starts with a colon - Make sure that DispatchDestroyMethod() calls as well protected destructors - New function NSCheckNamespace() as replacement for NSCheckForParent() - pass parentNsPtr around instead of recomputing it in NSCheckForParent() - removed unneeded argument from NSGetFreshNamespace() - switched to dstring operations in NameInNamespaceObj() (seems slightly faster) - factored out NSRequireParentObject() - by the measures above, we obtained some speed improvements - moved some more debug output to be controlled by the debuglevel - extended regression test with testcases for creation of parent objects via method "__unknown" - disallow object parameter of type "switch" to avoid possible confusion between (a) providing a value for the o.p., (b) calling it as a setter, and (c) calling it as a getter. For providing a value, no arg is used for switch. For calling a setter, it is necessary to provide a value even for a switch. - disallow type "switch" in setter definiton (use boolean instead) - disallow type "switch" for positional arguments (use boolean instead) - extended regression test - configure.in: removed --with-tclinclude option, since it appears to be included in tcl.m4 (since a while). Many thanks to Victor Guerra for noticing it. - perform relation handling in objectparameters outside of object-frame - For preexisting namespaces, we do not set the deleteProc. Is this desired? Should nsPtr->deleteProc be moved to NsfNamespaceInit()? .... It is ok on the current labor distribution between object and namespace: if an object is deleted, it takes care about the deletion of subobjects, not the namespace. However, it might be an option in the future to overthink this strategy and to bush (sub)object deletion into the namespace deletion. - work on replacing SKIP_LEVELS by SKIP_LAMBDA for openacs (works with regression test, has problems with OrderedComposite::ChildManager.init) Note concerning treating CMETHOD_FRAME like METHOD_FRAMES: we did this change for NsfCallStackFindLastInvocation(), but nsfStack.c has still several occurences, where they are treated differently. - changed relation handling by evaluating the relationcmd in the parent context to keep evaluation order. - extend introspection "nsf::configure objectsystem": the command returns now all system methods in the syntax of nsf::createobjectsystem - "nsf::createobjectsystem" creates now a warning when an existing objectsystem is redefined and ignores the new definition attempt. This was done with the purpose to allow "package forget nx; package require nx" - Allow overwriting of redefine protected method during bootstrap to ease "package forget nx; package require nx" - forward had just "-objscope", no general "-frame method|object". Since forwarder have client data, they always push a method frame. So, the situation is different to nsd::alias and ::nsf::dispatch. Therefore, the flag "-objscope" was renamed to "-objectframe" to provide better consistency with "-frame object" - fixed bug, where error handling of invalid options in ForwardProcessOptions() could lead to a crash - return forwardoption "-earlybinding" via instrospection - extended regression test - provide a more explicit way to handle resourcing after a "package forget" in the info methods (similar to Stefan's suggestion). - xotcl2.tcl: added empty namespace eval to make package indexer happy - nx.tcl: removed debugging output - nx.tcl: added syntactic sugar to "method" and "alias" for return value checking. One can write now: Class create C { :public method foo {a:int b:int} -> int { return [expr {$a + $b}] } } - extended regression test - changed returns syntax from '->' to '-returns' - xotcl2: fixed and completed results of "info instforward" and "info forward" - serializer: fixed handling of nsf::configure options - nx: added "-returns" to forwarder - added regression test for forwarder and returns - rebuild pkgIndex.tcl more eagerly - added error handler to pkg_mkIndex when called via "make libraries-pkgindex" * just show warning and errors when rebuilding pkgIndex files * stop make in case of errors in pkg_mkIndex - don't leave error message when __default_superclass (or __default_metaclass) is not set - Fixed switching between INACTIVE_MIXIN frames to ACTIVE_MIXIN frames - Extended regression test - make handling of redefinitions in system methods more robust - follow Tcl naming convention (uppercase functions) - Don't allow to call objects as methods (for the time being) via absolute names. Otherwise, in line {2} below, ::State is interpreted as an ensemble object, and the method "unknown" won't be called (in the XOTcl tradition). {1} Class ::State {2} Class ::State -parameter x - Converted migration guide to asciidoc - Overhaul of several sections in asciidoc - Developed styles for nx for migration guide (.css and source-highlight) - fixed bug in xotcl 2.0 "info forward" - extended regression test - NSDeleteChildren: delete objects before classes - NSDeleteChildren: delete here aliases as well - fix potential crash when "next" is called from a non-proc frame. - nx.tcl: cleanup of forward implementation - xotcl2.tcl: cleanup of forward implementation - xotcl2.tcl: provide debug version of default init method - nsf.c: acquire parameter structure for returns more lazily (otherwise, a serializer handling returns would acquire the structure for every argument) - extend regression test - xotcl2.tcl: fix the default init handler - nsf.c: provide warnings when unchecked parameter values might conflict with nonpos args - provide a generic logging interface * predefined for plain Tcl and aolserver/naviserver * C-level: NsfLog() * Tcl-level: ::nsf::log - quote name of referenced parameter in error message to ease reading - new parameter checker "parameter", performing an approximate checking for valid parameter specs - set NSF_ARG_CHECK_NONPOS only when there are multiple arguments - remove space checking in values for NSF_ARG_CHECK_NONPOS in favor of parameter checker - make "... info children ?pattern?" compliant with XOTcl 1; if pattern contains no wildcard and is no absolute path, nsf completes it. (eg. Object create o; Object create o:x; o info children x" will return ::o::x) - extended regression test - introduced a few forms of multiplicity * 0..1 allow empty value * 0..* list, can be empty (equivalent to 0..n) * 1..* list, cannot be empty (equivalent to 1..n) - deprecate multivalued in parameter specs in favor of multiplicity - deprecate allowempty in parameter specs in favor of multiplicity - adjust regression test - fixed bug with required last object parameter without provided value - extended regression test - new printf-style error message generator: NsfPrintError() - simplified error handling: removed NsfVarErrMsg() and NsfErrMsg() and replaced it by NsfPrintError() - testx.xotcl: fix messages when test fails - further cleanup of error procs: eliminated NsfObjErrArgCnt() - improve error message, when too many arguments are passed - extended und overworked migration guide (added e.g. multiplicity) - extended regression test - added returns handling for nx in serializer - extended regression test - provide warning if non-positional argument is passed more than once - made error messages more consistent - improved error messages for "returns" and "nsf::is" (omit parameter name) - streamlined error messages - removed NsfErrBadVal() and replaced it with a generalized version of NsfObjErrType() - "/obj/ info method parametersyntax /method/": return results of ::nsf::parametersyntax in case, the parametersyntax cannot obtained from a parameter definition (e.g. the method is a forwarder to a tcl cmd). doctools - interfaces in documentation for slots (see for more details ::nx::Class#superclass in nx.tcl) - handle object methods as well in quality checks - why does one have to specify @superclass rather than determining the superclass via introspection? - use tcl parametersyntax for short description of commands/methods - deal with interally-called methods (can be overloaded by the application) * user-called and internally called (e.g. from "create" or "new") XO_c_create_idx, XO_o_destroy_idx, XO_o_move_idx, * not documented yet: XO_c_requireobject_idx, XO_o_defaultmethod_idx, XO_o_init_idx, XO_o_objectparameter_idx, XO_o_unknown_idx * only XOTCL2: XO_o_cleanup_idx, XO_o_residualargs_idx, text - use term "callprotection" in documentation for public|protected (to be consistent with "... info methods ...") - reduce indenting for code examples in documentation (high indentation makes readability worse). i use usually just 2, 4 are ok as well; we should decide. - removed method "setter" from nx - removed method "setter" from migration guide - nx method "attribute": changed name of nonposarg from "slotclass" to "class" - fix bug for "C class-object attribute foo" (incorrect forwarder) - extended regression test - doctools: changed "-slotclass" to "-class" - nx::test: made differences in regression test easier to read - serializer: updated serializer to avoid calls to "setter" - extended regression test - fixed in bug in ensemble-next (removed colon-prefix from methodname in next) - extended regression test - Experimental Object-System specific resolver in method bodies (allows resolving to the "right" next, self, etc. without namespace imports/paths) - deactivated automatic namespace path copying for child-objects - extended regression test - added deletion functionality to nsf::mixin - moved handling of methodNames of c-cmds to ResolveMethodName() - extended regression test - nsf.c: renamed MethodNameString() to MethodName() (for consistency with ObjectName() and ClassName()) - raise error, when "obj :method ..." is invoked (colon misleading und not necessary) - remove colon from method name in error message "unable to dispatch method ...." - extended regression test - nsf.c: code cleanup and documentation improvements - made assertion code optional - added and renamed additions compile flags NSF_WITH_INHERIT_NAMESPACES NSF_WITH_OS_RESOLVER NSF_WITH_ASSERTIONS - added flag NSF_WITH_VALUE_WARNINGS - defined nsf::deprecated as tcl proc, using ::nsf::log - some minor refactoring - "info parameter": return :switch as parameteroption for C-defined cmds, when a nonpos-arg gets no arguments - updated regression test - added experimental ::nsf::proc for realization of procs with next scripting argument passing. These nsf::procs improve the orthogonality of the code (using e.g. nonpos args and value checker for procs) and allows the same introspection interface (info method parameter|parametersyntax, ...) - removed unneeded functions: NsfComputePrecedence(), NsfComputeDependents(), Nsf_SetVar2Ex(), NsfOSetInstVar(), Nsf_ObjGetVar2(), NsfOGetInstVar(), qNsfCreateObject() - removed unneeded external declarations: NsfClassListAdd() NsfClassListFree() - make extern declarations explicit - grouped most extern definitions together - improved documentation - moved variable declarations to inner scopes - removed warning from cppcheck() - added block for none-one-liner if statements - added methodtype "nsfproc" to "... info methods ...", to be used with namespace qualified names - return "nsfproc" as methodtype for nsfprocs - refactored InvokeShadowedProc() - some minor code cleanup and improved documentation - added flag "-ad" to ::nsf::proc for obtaining the semantics of ad_proc in OpenACS (boolean with no arguments, append "_p" to variable names) - added "... info method definition ..." for nsfprocs - new function DStringAppendQualName() to append qualified name to a DString - removed obsolete function NSCmdFullName() - serializer.tcl: export nsf::procs via "Serializer all" - nsf::proc: alloc shadowed methods in ::nsf::procs - new helper function ::proc strip_proc_name to strip nsf::procs prefix - improve error messages - reduce verbosity - removed the following obsolete macros: ALLOC_NAME_NS, ALLOC_TOP_NS, ALLOC_DSTRING - some refactoring of the argument parser - argument parser handles now as well OpenACS like single-word parameter values (such as ... -flag=1 ....) - improve error messages and warnings for nsfprocs - extended regression test - no need to define "-class" as objectparameter - no need to define "Object.cleanup" as a method - let "obj class NEWCLASS" return NEWCLASS - doc-tools: added "-where" to !get - doc-tools: title to internal links, provided css class, added nicer label - updated reference doc - removed leftover -public flag in nsf::method - general cleanup: removed unused arguments - defined UNUSED macro to get more checking on unused arguments - nx::pp: added flag "-linenumbers on|off" to render method - added first version of next-tutorial.[txt|html] - xotcl2.tcl: defined ::xotcl::MetaSlot - make sure, that classes of the intrinsic class hierarchy are of the same object system - add regression test - removed c-implementation of method vwait, it was replaced by "vwait :varName". We had to allow flag TCL_GLOBAL_ONLY in InterpColonVarResolver(), since Tcl vwait implementation calls it with that flag. - added a scripted implementation for vwait in xotcl2 - added regression test for vwait - removed TCL_VWAIT from the code, since we have it in git - nx.tcl: defined method unknown as protected - nx.tcl: never pass "substdefault" to a setter - nx.tcl: define a minimal valuechecker any to suppress warnings for potential conflicts with non-positional parameters, when the values start with a dash - doc-tools.tcl: make -name parameter of method new always required and "any" - doc-tools.tcl: make object parameter of Entity of type "any" - nsf.c: added wideinteger to list of valuecheckers - nsf.c: provide context for warning messages - extended regression test - next-tutorial: documentation updates - add explicit reference counting for oacs-style flag value passing - parameter specs: use "arg=" in object parameter type "method" as name of a called method to allow to call unregistered methods - eliminate protected method "noinit" for nx and allow it just as an object parameter - added first implementation of object parameter type "forward" - renamed object parameter type "method" to "alias" - removed parameter option "slotobj=" in toParameterSyntax - renamed to [from|to]parameterSyntax to [from|to]parameterSpec - serializer.tcl: reactivated methodSerialize (used in api-browser of OpenACS) - nx.tcl: * new method requireClass to Combine two classes and return the more specialized one * make slot objects for parameter aliases and parameter forwarder instances of ObjectParameterSlot * get rid of attributes "isforward" and "isalias" and replace it by "dispositon" * complete list of predefined value checkers * we have now three approaches for providing parameter -object-filter and -object-method Approach 1: create RelationSlot with nosetter Approach 2: use parameter forwarder Approach 3: use parameter alias we switched from approach 1 to approach 3 - extended regression test - fixed potential crash with missing parent namespace - added shadowing for ::tcl::info::body to allow "info body" to be called on ::nsf::procs - commented nsfShadow.c - added regression test - removed NSF_INFO - fixed potential access to freed memory (actually when checking if namespace was removed by Tcl); found this problem when compiling Tcl with SYSTEM_MALLOC (own modified version of tclThreadAlloc.c) - fixed memory leak (namespace names and structures) - nx.tcl: * full rewrite of slot machinerie, much simpler structure * relation handling via parameter aliases instead of pseudo converter * mixinclass SlotOptimizer removed * new class BootStrapAttributeSlot - ConvertToRelation() and handling of parametertype "relation" - Make CompiledColonVarFetch() more robust in case of half initialized objects (create vartable on the fly if needed) - allow empty parameter options in parameter parser - removed nsf::parametersfromslots (became simple, part of objectparameter now) - removed hardcoded objectparameter (attributes, volatile and noinit) - updated regression test - updated class diagram - nx.tcl: needsForwarder is true, when method "get" is specified on a slot - nx.tcl: Don't generate per-slot add/assign handlers, when not needed - nsf.c: fixed a nasty bug within namespace deletion, when a deletion of one tcl cmd caused implicit deletions of other cmds in the same namespace. The classical idiom for looking over hash tables with Tcl_GetHashValue() and Tcl_NextHashEntry(hSrch) can lead to crashes (and has different behavior depending on the number of buckets). - added regression test - nx.tcl: added default/initcmd for ObjectParameterSlot - added ::nx::Object as default for "superclass" slot to make default superclass explicit - unified interface for getParameterOptions - extended regression test - update class diagram of slots - new function Nsf_NextHashEntry() simular Tcl_NextHashEntry(), but handles triggered hash entry deletions - fixed reference count in AliasDeleteObjectReference() - nsf.c: changed handling of cmdPtrs in callstack content. * we use now explicit reference counting using NsfCommandPreserve/NsfCommandRelease * as a consequence, we do not need the following functions Nsf_DeleteCommandFromToken() and CallStackClearCmdReferences() any more. * the flag NSF_CSC_OBJECT_ACTIVATED is not needed anymore and was removed - removed a small memory leak when a destroy method of an object o deletes its grandparents namespace, containing also this class of o (and its methods). Significantly eased by the change above. - use NsfCommandPreserve/NsfCommandRelease for tcd->aliasCmd as well. In case of epoched cmdPointers, refetch the cmd and it client data. - added regression tests - added flag to AliasGet() to leave optional error message if alias data is removed - some cleanup in NsfProcAliasMethod(): handle not existing alias data, more careful refcounting - added experimental flag WITH_IMPORT_REFS to deactivate automated alias deletion (seems, that this solves all issues we had before) - added flushing of bytecode on alias registration - added regression test - update slot uml diagram - fixed incorrect (unwanted) call to unknown that caused creation of objects names __unknown when classes could not be resolved - nsf::relation: fixed error message when receiving and invalid class for relation type "class" - updated documentation - reanimated NSF_PROFILE (when activated, needs more stack and slows execution slightly down) - fixed a problem with object-level alias - nsf.c: provide low-level commands for managing profile data - nsfStack.c: provide hook to obtain callers information in profiling code - nx.tcl: provide caching for computed values of object slots to make method objectparameter nearly twice as fast; direct changes on slots require a reconfigure call. - nsf.c: removed SUBST from shadow commands (does not appear to be necessary any more) - nsf.c: fixing a memory leak (some substituted values were not freed correctly) - nsf.c: fix potential crash for epoched cmds - some minor updates for profiling support - The fix of yesterdays subst reference counting bug triggered an other problem: If the last arg was "args", the flags array for checking the need for decr of refcounts was not extended. There are multiple possible fixes, i have just extended the flags array for now as well. - When profiling is activated, perform now a more sane shutdown order, all hash tables are now correctly freed. - Improve behavior, when object system creation fails - Drop function NsfProfilePrint() - Altered Nsf_NextHashEntry() to re-init hSrchPtr when the number of expected entries differs from the number of real entries. This fixes a bug that Michael Aram detected, that happens when multiple hash buckets exist, but on deletion of an hash entries triggers some other deleted from the same hash table. - extended regression test. - made default setting more compatible with XOTcl 1 * set variables to defaults only when not set already * flag init_called already before init is called, not afterwards (a call to "configure" within init does not clear the already set instance variables) - extend regression test - configure: added flag --with-profile to enable profiling support - cleanup and documentation of nsf-specific interp state - nsf::configure: added an option "profile on|off" (per default off) - profiling: return object data with method information - the following is obsolete since valgrind 3.6 =========== reminder for valgrind testing svn co svn://svn.valgrind.org/valgrind/tags/VALGRIND_3_5_0 valgrind curl http://bugsfiles.kde.org/attachment.cgi?id=36999 > 10.6.patch mv 10.6.patch ./valgrind cd valgrind patch -p0 < 10.6.patch ./autogen.sh ./configure ./configure --build=amd64-darwin make sudo make install ==================== - doc: * This package contains 12 classes.... 3 objects .... Why are all these marked with "mismatch"? * (optional) protected method elimination in leftbar * heading "Glossary" missing. It ist not clear, what the list of items is, when one sees index.html * make quality checks (missing documentation, ...) optional? how to deal with non-resolvable quality checks? * provide a renderer for XOTcl @-notation to produce object structure for the new doctool (makes the old documentation usable, eg. for XOTcl2) - first steps towards DTrace support - DTrace: * track objects in method invocations * trace result codes in method-return * add some DTrace sample scripts * add DTrace header file dependency * add --enable-dtrace flag for configure * --enable-dtrace sets DTRACE_OBJ on mac os x empty (since not needed for mac os x DTrace) * added "nsf::configure dtrace on|off" for skipping package initialization (to be handled in D script) * make compilation clean * extended README file * handle self->tracing in D scripts (and in dtrace/sample.tcl, tests/object-system.tcl) * add probes for object creation and freeing * add sample d-script for object bookkeeping * renamed object-create into object-alloc (counterpart of object-free, no confusion with create) * fire probes as well for classes created by the object system - configure * make "configure --help" more pretty * simplify configure.in - added first version of "nsf::methoddelete" - extended regression test - updated TODO - fixed potential crash with -param:switch - added "... info method exists ...." - updated migration guide - changed names of method handling commands in nsf: ::nsf::methodproperty => ::nsf::method::property ::nsf::method => ::nsf::method::create ::nsf::methoddelete => ::nsf::method::delete ::nsf::alias => ::nsf::method::alias ::nsf::forward => ::nsf::method::forward ::nsf::setter => ::nsf::method::setter ::nsf::provide_method => ::nsf::method::provide ::nsf::require_method => ::nsf::method::require (updated regression test, docs, ...) - Fixed cases of -flag=$value for type switch outside the context of "nsf::procs -ad" - extended regression test - removed hardcoded name "init" from CallConfigureMethod() - improved documentation - removed isInitString() - changed names of var handling commands in nsf: ::nsf::existsvar => ::nsf::var::exists ::nsf::importvar => ::nsf::var::import ::nsf::setvar => ::nsf::var::set - improved misleading error message for method modifiers "public" and "protected", as well as for "class-object" - extended regression test - serializer: catch for object-level alias apparently not needed anymore (search for ns_cache_flush) - silence compilation when compiled without DTrace - nx: * removed methods ::nx::Class.alloc and ::nx::Class.dealloc from predefined method-set * added definitions such that these methods can be loaded via ::nsf::method::require ::nx::Class alloc ::nsf::method::require ::nx::Class dealloc * make explicit that "method ... require" returns a method-handle * removed misleading reference in error message, when a class-spefic method was called on an object; solution is somewhat dangerous for potentially unknown client data * added regression tests * removed methods ::nx::Class.recreate and ::nx::Class.recreate from predefined method-set ::nx::Object.configure from predefined method-set * added definitions such that these methods can be loaded via ::nsf::method::require ::nx::Class recreate ::nsf::method::require ::nx::Object configure nsf: * added CallDirectly() wrapper for calls to "init" * reactivated "configure", since we plan to use it more prominently * added a configure flag for "class" * removed method "class" (should be used via "/obj/ configure -class ...") * removed method residualargs from nx * added C-implemented method "init" for orthogonality * allow specification of system method handles in nsf::createobjectsystem * automatically register alias, when system-method handle was provided and a same-named method is defined * provided a fast path implementation for "defaultmethod" * provided default system methods for "init", "defaultmethod" and "unknown" * provided handles for system methods "alloc", "dealloc", "recreate", and "defaultmethod" * strip in dispatch invocations of "unknown" potential leading colons * removed c-level implementation of init again, since scripted one can be used now as well in registration of createobjectsystem * reduced verbosity * added definitions such that these methods can be loaded via ::nsf::method::require ::nx::Object unknown * added methods ::nsf::methods::object::class and ::nsf::methods::class::superclass in order to make faster and nicer looking objectparameters (compared with forwarders) * nx: changed parameter -class to use ::nsf::methods::object::class * ns: fixed chicken egg problem of method objectparameter needing objectparameter by creating/destroying slot Class::slot::dummy; otherwise default values for slots would not be available * reduced verbosity of parameter forwarder * Hopefully the last big naming change: Instead of writing "C class-object method foo {} {...}" one can write now "C class method foo {} {...}" to define a class method named "foo" for class "C". This naming change became possible by defining XOTcl's "class" (and "superclass") as object parameter only. To change a class of an object o, one has to use "o configure -class NEWCLASS". The term "object-class" looks alien to language beginners, the term "class" is much more straightforward. Changing classes or superclasses is seldomly used by typical application programs. For already existing nx scripts, changing "object-class" into class should be straightforward. * various documentation updates (migration guide, nx tutorial) * fixed bad interaction between filter and parameter alias * some documentation/todo updates * fix compilation for tcl-head in fossil * deactivate coro regression test, since it is apparently broken for tcl-head in fossil (stack frame seems to be lost after a yield) * make sure to create the cmds for objects with Tcl_NRCreateCommand() to choose trampoline-path in the trunk version of Tcl * The newest trunk version of Tcl in fossil has TclStackFree() and TclStackAlloc() removed We had to substitute this functions. Unfortunately, the lifetime of the strack structures has changed, so we had shuffle some internals around. - nsf.c: remove unnecessary test when compiled without NRE - nsf.c: make sure, validCscPtr is always initialized - tested all regression tests with valgrind against tcl-trunk - gentclAPI.tcl: * renamed "nsfCmd" to simply "cmd", since the code can generate arbitrary tcl commands * allow type "int" in the .decl files - nsf.c * move several functions from "static" to "external" to make the code generator usable for submodules as well - added flag ?-type ...? to "info lookup slots" - made all useful converters external symbols - added flag ?-type ...? to "info slots" - delete accessor when slot is destroyed - added pattern to "info slots" - added to "info slot /attName/" - Fixed dispatch of defaultmethod for ensemble methods - Added compile flag DISPATCH_ALWAYS_DEFINED_METHODS (deactivated). So far, nx just uses dispatch on overloads or filters, but not on defines (possible to call e.g. "destroy" from a script, but internally the direct dispatch is used, as long there is no overload). The compileflag would force to use the slower dispatch always. - Extended regression test - Improve locality - Let "info slot" return the slot object - nx::mongo: Initial commit of the experimental mongoDB interface for nx - nx.tcl: fix handling of arg in converter - nx::mongo: * first step towards handling embedded objects * one more example script: example-nx-bi.tcl - nsf:c: fix dispatch of setter without current method - extended regression tests - nsf.c: added nsf::var::unset (provided so far just var::set) - nx::mongo: * added mongo::count * obtain _id from mongo::insert * added mongo::Object.delete method for embedded and non-embedded objects * handling of mongo-embedded objects when destroying objects * simple bson pretty print function * extended examples * handle fetch of embedded objects * added method count for mongo mapped classes * improve documentation * added handling of bson types for timestamps and dates * provide setup based on mongo_db, mongo_collection and mongo_ns * implemented type "reference" similar to "embedded" * all referenced objects are for the time being auto-dereferenced * new method "show" for mongo mapped classes * added two new example files example-nx-reference-many.tcl and example-nx-reference-one.tcl * replaced "arg" by "type" in spec for mongo attributes to make spec less strange - nsf.c: made potentially unknown clientData more safe (error message, when something is passed via clientData to a method expecting an object/class as clientData). - renamed NsfNoDispatchObjectError() to NsfDispatchClientDataError(), extended interface - Makefile.in: fixed name methods.test - nsf: renamed nsf::isobject to nsf::object::exists - nsf: renamed nsf::qualify to nsf::object::qualify - nx.tcl: added support for positional object parameter and removed special handling of the last argument for the init block; added attributes "position" and "positional" to ObjectParameterSlots, removed last argument of method "objectparameter" - nx.tcl: simplified createBootstrapAttributeSlots (second round of default value setting seems not necessary) - nx.tcl: some cleanup - test.tcl: * don't export Test per-default * define Test as nx::Test * make Test parameter count 1 the default, change to higher numbers where needed - nsfmongo.c: * upgrade to newest c-driver (version 0.3) from git. * support connection to replica sets * support attribute selection lists for ::mongo::query (positive and negative selection) - nx-mango.tcl: * support for unique indices * support for query operators "in" and "all" - extended migration guide (introduction, feature lists, etc.) - serializer: * prefix warnings to ease tracking of warnings * some cleanup for handling aliased methods - nsf.c: * moved implementation of ::nsf::method::delete to C * produce same error messages when methods are delete via nsf::method::delete and nsf::method::create {} {} * Prohibit deletion of methods during shutdown. Otherwise when destructors delete methods, some other destructors depending on these methods will fail. Cleanup deletes all methods anyway. * Provided alternative (faster) way of dispatching nsf::procs (can be tured off with NSF_INVOKE_SHADOWED_TRADITIONAL) * renamed NsfMethodCmd() into NsfMethodCreateCmd() for consistency * nsf works with OpenACS again (requires new nstrace.tcl, aolserver-openacs.tcl, and 01-debug-procs.tcl). - nsf.c: * factor out NsfClassListAddPerClassMixins() * factor out NsfClassListFind() * let result of "cls info heritage" return per-class mixins as well, otherwise it would be useless, since "cls info superclass -closure" would return the same * replaced loops with NsfClassListFind() - nsf.c: * handle direct dispatches for aliased methods * new generalized error message: NsfNoCurrentObjectError() - nx.tcl: replace loops ::nsf::methods::[object|class]::* by explicit command registrations - nsf.c: * added NsfClassListNoDup() to allow just single inserts * added NsfClassListPrint() for debugging * info heritage returns no duplicates * added prototype for NsfNoCurrentObjectError() * report "no current object" when no object is passed to a method. * code cleanup - extended regression test - nsf.c: * ensure that explicit per-object-mixins are kept at the front in "info heritage" order and in "info precedence" when classes are added as POMs and PCMs * extended regression test - nsf.c: * renamed old flag "-order" of "info mixin classes" to "-heritage" since it computes same heritage as in "info heritage" (but potentially for a list of classes) * added compatibility layer for xotcl2 * added lost option "-heritage" to "/cls/ info mixin classes" (was only there for "/obj/ info mixin classes") * extended regression test - nsf.c * first version of c-bases "info slots" for classes * switch "-closure" just for class info method * added switch "-source" to "info slots -closure" and "info lookup slots" (similar to "info lookup methods") * extended regression test * base objectparameter on "info slots" - nsf.c * added "pattern" to "info lookup slots" * added "pattern" to "info slots" * extended regression test - nx.tcl, xotcl2.tcl: removed unsafe {*}$pattern - added: "info slot handle /name/" "info slot parameter /name/" - nsf.c: Since the method "objectparameter" is just based on the class (and object parameters are invalidates as well over the class), we moved the method from obj to class to avoid potential confusions - nsf:c * added C-implemented class level method "info objectparameter list|name|parameter|parametersyntax" * added enum to handle different print styles for parameters * renamed ParamDefsList() to ParamDefsNames(), added true ParamDefsList() - nx.tcl: * removed "info slot handle" and "info slot parameter" * added "info parameter spec", "info parameter list", "info parameter name", and "info parameter syntax" * extended regression test - nsf.c: * Added argument "-reg-object" to ::nsf::method::create to distinguish between a registration and a definition object for ensemble methods, similar as on other places. If no reg-object is provided, it is the same as the definition object. One should take care that the registration objects are deleted after the definition objects, which is the case for the usages of the reg-objects in nx/xotcl. * The namespaces within plain scripted methods and scripted ensemble objects are now the same. * Extended regression test * Code cleanup and documentation - nx.tcl: * added method "delete" to delete methods and attributes * extended regression test - nsf.c: removed all but one occurrence of Tcl_AppendElement() - nsf.c: removed all occurrences of Tcl_AppendElement() - nsf.c: passed around resultObj explicitly - nsf.c: fix and document GetMatchObject() - extend regression test - nx.tcl: * splitted method "delete" into a "delete method" and "delete attribute" * remove flag "-per-object" in method "delete" * delete per-object methods of classes with "/cls/ class delete method name" and "/cls/ class delete attribute name" * extended regression test - added test cases for "info slots" - nsf.c: * handling of same named per-object and provided slots for Class objects * per-object slots are saved now under ::per-object-slot::* * returning correct results when per-object slots are used * removed obsolete functions: NsfObjectListFree(), NsfObjectListAdd() * removed obsolete type NsfObjects * transformed ComputeSlotObjects() into a more generic AddSlotObjects() that can handle per-object slots as well - nx.tcl: * generalized slot object handling. * extended regression test - xotcl2:tcl * made "info heritage" in xotcl2 compatible with xotcl1 * fixed "info slots" in xotcl2 * extended regression test - nsf.c: require NSF_IS_SLOT_CONTAINER for slot-container - nx.tcl: ne proc ::nx::setSlotContainerProperties to handle slot container properties in a uniform way - reduce verbosity - nx.tcl: improve code documentation -nsf.c: added c-implementation of "/object/ info slots" to share implementation details and reduce scattering - migration guide * included change in "info heritage" * included "info slots" * included "info parameter" * included "delete method" * included "delete attribute" - nsf.c: NsfRelationCmd() returns per default list of actual values, therefore mixin add|... return now as well the actual values - nx.tcl: added "info parameter slot" to return slotobject(s) - added "info parameter slot" to migration guide - extended regression test - nsf.c: changes to use trunk-branch with and without TclStackAlloc() - migration-guide: add third level to toc - fix regression test for 8.6 to use nx prefix - nsf.c: added cmd "::nsd::method::registered /handle/" that returns the object on which the method identified by the handle was registered, or empty. - extended regression test - bring defined commands in the source code in alphabetical order - generate interface for NsfUnsetUnknownArgsCmd() - delete some obsolete code - added "link" from 2.4 (parameters) to "info parameters" in migration guide - remove alias warnings from gcc under ubuntu (4.2.4) - nsf.c: fixed possible crash in tcl8.6 with nsfprocs, still one inconsistency - nsf.c: fixed bad interaction between mixins and ensembles in tcl8.6 - nsf.c: document two more functions - nsf.c: removed unneeded casts to (ClientData) - nsf.c: generalized disposition handling (forward, alias, initcmd) for object parameter * disposition is now an option for object parameters rather than than an own type. Therefore, one can check the arguments passed to the disposition cases * changed specification of name of method from arg= to method= * this way "type" info in "info parameter syntax" is handled automatically - nsf.c: * added a new converter for converting mixins with guards (named mixinspec) * used mixinspec in nx.tcl and xotcl2.tcl * extended nx regression test. * added profiling support for nsf::proc when NSF_INVOKE_SHADOWED_TRADITIONAL is turned off. - removal of unneeded flags "-incontext" and "-nomixins" from * /obj/ info methods * /cls/ info methods These flags are correct for "info lookup", but unneeded for "info methods" - cleanup of ListDefinedMethods() - nsf.c: use NsfObjectRefCountIncr() instead of object->refCount++ - nsf.c: fix small memory leak for nsf::is in error cases - renamed converter from "mixinspec" to "mixinreg" - Use mixinregObjType as well in NsfRelationCmd(), so this is the only place, where mixin and guards are processed. - Since the type converter converts Tcl-Objs, we have less context information (e.g. we have no base class, on which we can decide to call e.g. __unknown on on of the objects systems). - because of the point above, i removed ::xotcl::Class->__unknown and ::nx::Class->__unknown in favor of a global proc ::nsf::unknown, for which unknown handlers can be registered - GetClassFromObj() receives as last argument "withUnknown" instead of baseClass to indicate, when unknown should be tried. - new function NsfCallUnknownHandler() - moved mixin reg type converter to a new file (nsfObj.c) - added NsfFilterregObjType, to provide a converter for filter registration similar to mixin registrations - replaced dummy dupIntRepProc and updateStringProc in nsfObj.c by NULL - fixed memory leak in "... info mixin classes -heritage" - added tests for integer, wideinteger and bignums - added value checker type int32 (similar to "string is integer") and changed value checker "integer" to accept all integers - library/mongodb: * use type int32 * updated to new nx/nsf interfaces * updated for mongo-c-driver post 0.3 (supporting result MONGO_OK for mongo_cursor_next) * factored out "mongo cond" from "mongo query" - fixing part of the memleak introduced for bignum handling above (for some unknown reasons, we see crashes from mp_clear) - extend regression test - improve bignum conversion handling further - found memleak in tcl - provided nicer registration and inspection interface for unknown handlers - added documentation for unknown handlers in tutorial - cleanup of __unknown - added handling for provided arguments to positional object parameters with disposition alias and forward - provided better error messages for unknown parameter options - provided error messages for multiple disposition parameters - reduce redundancy by introducing macro NSF_ARG_METHOD_INVOCATION - gentclAPI.tcl: * renamed "nrArgs" to "nrParams" * switched default for nrargs from 0 to 1 - gentclAPI.decls: * added "-nrargs 0" where needed -nsf.c: * switched parameter logic from default for nrargs from 0 to 1 * simplified logic to detect additional arguments in argument parser * improved error message for missing required argument - regression tests: * added disposition.test * extended regression test - xotcl2: use filterreg instead of plain arg for registration of filters - nsf.c: * improved source code documentation * added parameter option "args" in order to get eventually rid of hard-wired call to residualargs. * improved a few error messages * fixed object parameters consisting only of plain parameters (previously, no parameters were returned in this case, e.g. for method parameters; but object parameter code depends on it) - extended and updated regression tests - nsf.c: * added refcounting to parameter definitions (needed, when aliased object parameter redefined the actual objectparameters) * removed hardcoded call to remaining args * switched implentation of xotcl2 to use object parameter with parameter option "args" * removed residualargs from object system definition * extended regression test - nsf.c: * Don't output non-consuming procs (which are always called) via parametersyntax (shows, what a user can input) * additional command ::nsf::object::initialized to check whether an object is already initialized * new function DispatchInitMethod() similar to DispatchDefaultMethod() * let residualargs call init directly instead of doing it the inidrect way * provided ability to call init with object parameters at arbitrary times * switch from Tcl_ObjCmdProc style interface (ClientData first) to a C stype interface for DispatchDefaultMethod(), DispatchUnknownMethod() * bring cmd definitions for nsf::object in right order - extended regression test - genAPI.decls and nsf.c: bring cmds in same order - nsf.c: align naming conventions - renamed gentclAPI.decls to nsfAPI.decls - renamed tclAPI.h to nsfAPI.h - added nsf.m4 to git for the time being - mongdb: * added preliminary gridfs interface * refactored some code * added new types for "gridfs" and "gridfile" * added new example file example-nsf-gridfs.tcl - nsf.c: no good reason to disallow user defined types for for alias, forward or initcmd - library/nx/nx-zip.tcl: added a zip file generator as package - nsf.c: * new file nsfPointer.c * generic new value checker ConvertToPointer to handle c-level conversions (which can be registed from nsf extensions) * extern defined interface for the pointer converter: Nsf_PointerTypeLookup(), Nsf_PointerTypeRegister(), Nsf_PointerAdd(), Nsf_PointerDelete(), Nsf_PointerInit(), Nsf_PointerExit(). - library mongodb * changed mongoAPI to pointer converter interface - C-code generator: * additional parameter swith "-withObj" to allow passing of internal representation and the according TclObj * * c-implemented methods: report types in "info parameter" for more builtin types. * use "-withObj" in mongodb interface * adapted regression test - mongodb interface: * mongo::gridfile::seek: added a seek command for gridfiles * added example for the low-level interface to shwo how to access gridfs via the plain mongodb interface, how to add some additional metadata (e.g. dublin core meta data) and how to retrieve this via the gridfile interface - nsf.c: * report only fully initialized slot objects via "info slots" to avoid chicken-egg problem during method "objectparameter" * added flag -array to ::nsf::var::exists to check, whether the variable is an array (to avoid "o eval {array exists ...}" in the serializer. * provided flags to VarExists instead of multiple args * don't add new pointer entries in Nsf_PointerTypeLookup() - preliminary fix for volatile called through residual args - new regression test file volatile.test - fix the comparison with "unknown" in residual args - provide backward compatibility for unknown method (when method contains spaces). - some minor cleanup - extended regression test - fix typos in string "unknown" (unknwon, unkown) - reduce verbosity - reduce scope of variables - renamed ObjectParameterSlot attribute from nosetter => accessor (positive formulation) - nsf.c: make sure to always initialize variables - first draft of separation of attribute -> variable + accessor - library/mongodb: * updated to current interface in git HEAD * added flag timeout to mongo::connect * added new index options "-sparse" and "-background" - regularized more nsf::* names: renamed "nsf::createobjectsystem" => "nsf::objectsystem::create" renamed "nsf::unknown" => "nsf::object::unknown" renamed "nsf::dispatch" => "nsf::object::dispatch" - generalized "nsf::object::initialized" to nsf::object::property objectName initialized|class|rootmetaclass|rootclass|slotcontainer - nx: factor out method createFromParameterSpec - method variable: * check default value * added shortcut, when no slot object is needed * extended regression test - nx::Attribute: changed method 'checkInstVar' to 'setCheckedInstVar' - set only fresh variables via per-object method "variable" and "attribute" - added flag -concomplain to per-object method "variable" and "attribute" - extended regression test - added support for "class variable" - added tests for "variable" + multiplicity and "class variable" - provide error message, when method variable is a noop (e.g. no value provided and no accessor is wanted) - added tests for object specific "variable" and "attribute + application defined value checker - library/mongodb: * updated to current interface in git HEAD - nx.tcl: added switch "incremental" to "variable" and "attribute" - added regression test - nsf.c: improve performance (branch prediction) by using likely/unlikely macros for gcc - nx.tcl: * added support for "variable" on the class-level * added flag "noconfig" to object parameter options * parameters with "noconfig" are omitted in "info parameter syntax" and "info parameter list" * used switches for all configurable boolean options for "variable" and "attribute" * regularized the interface of "variable" and "attribute" * extended regression test - fixed a possible crash in the ExitHandler: Object create o {exit -1} - nsf.c: * added flag "-array" to nsf::var::set such we have now "::nsf::var::set ?-array? object varName ?value?" With "-array", nsf::var::set behaves like "array get" or "array set" (on instance variables) * use "::nsf::var::set -array" in serializer symmetrically to scalar case * extended regression test - nsf.c: * fixing compilation with NSF_MEM_COUNT * New function DeleteProcsAndVars() to trigger deletion of ParamDefs (fixes a small memory leak); * improved comments * improved INCR_REF_COUNT/DECR_REF_COUNT for easier tracking of potential refcount errors * added macros DECR_REF_COUNT2() and INCR_REF_COUNT2() for easing the association of refcounts to locations in the code by providing names for refcounts. * fixed a refcount bug for valueObjs in non-NRE-enabled versions in the argument vector of scripted methods (found via INCR_REF_COUNT2/DECR_REF_COUNT2) - nsf.c: * refined refcounting debugging * fixed various refcounting bugs, especially in error cases. * added explicit INCR_REF_COUNTs on Tcl_Objs with 0-refCount to ease debugging * added explicit names for refcounting for "paramDefsObj" * added explicit names for refcounting for "freeList" (for forwaders) * provide debug-refcounts for "NSNamespace" * provide debug-refcounts for "nextArgumentVector" nsf.c: * change DeleteProcsAndVars, such it deletes procs and vars explicitely in all namespaces * added more sanity checks for parameterContexts, testing in ParseContextRelease() in DEBUG mode * provide debug-refcounts for "pcPtr.objv" * provide debug-refcounts for "pcPtr.clientData" nsf.c: * provide debug-refcounts for "class.activationCount" * provide debug-refcounts for "object.activationCount" * deactivated CHECK_ACTIVATION_COUNTS oer default * tested refcounts with Tcl 8.6b2, found bug in Tcl and submitted patch to sourceforge http://sourceforge.net/tracker/?func=detail&aid=3383616&group_id=10894&atid=110894 - nsf.c: * fixed a bug in "info parameter list|... name" when the named parameter is not found (returns now empty, before, it was returing the full list). * added flag "-nocomplain" to nsf::var::unset - nx.tcl * added "delete variable" analogous to "delete attribute" * unset instance variable for object-level "delete attribute" * extended regression test - library/mongodb: * updated to current interface in git HEAD (resp. "git checkout v0.4") - nx.tcl: * fixed copy for object created with new * copy returns now the fully qualified name of the copied object * extended regression test - library/mongodb:updated to current interface in git HEAD - nx.tcl: implemented copy without a provided name. If argument of copy is omitted, the copied object is created with a fresh name (i.e. created with the method "new"). Example set x [o copy] - extended regression test - nx.tcl: * added protected and public for "require method" The following forms are now valid "... require public method" "... require protected method" "... require method" "... require public class method" "... require protected class method" "... require class method" * extended regression test - library/mongodb: * replaced NsfMongoGetHostPort() with the newly available function mongo_parse_host() * updated error codes according to git head * factored out mapping of error code to string - nsf.c: added cmd __db_compile_epoch for compile-epoch introspection - Mystery solved, why in the script below the interp>compileEpoch is incremented, when D is redefined, but in other cases not. In the script below the method D.init is compiled by tcl, since it has a trivial body. Therefore, a redefinition of D will remove this compiled body and all its potential usages. Therefore the interp->epoch is incremented. If the body is e.g. "return", the epoch is not incremented (observed with Tcl 8.5.10) ================================================= # -*- Tcl -*- package require XOTcl; namespace import -force ::xotcl::* package require nx::test; namespace import nx::Test Class C; C c Class D -superclass C D instproc init args {} Test new \ -count 100 \ -pre { puts stderr ===create-D;Class create D; puts stderr ===D-created; Class E; Class E1; Class X -instmixin {D E E1}} \ -cmd {X info instmixin ::E*} \ -expected {::E ::E1} \ -post {foreach o {D E E1 X} {$o destroy}} Test new \ -count 100 \ -pre {Class D; Class E; Class X -instmixin {D E}} \ -cmd {X info instmixin ::E*} \ -expected {::E} \ -post {foreach o {D E X} {$o destroy}} Test run; exit ================================================= - nsf.c: * enabled MEM_COUNT debugging for multi-threaded apps. We collect the MEM_COUNT statistics now per interp. Therefore, we have to pass around "interp" in case where alloc() and free() or refCount functions are used (textually, a big change) * verified, that nsf runs clean in aolserver/naviserver (all INCR_REF_COUNTS all balanced) * added paramPtr->name to MEM_COUNT tracing * renamed NEW_STRING to STRING_NEW * added STRING_FREE, calling MEM_COUNT macros * checked all ckfree in nsf.c, everything allocated is covered by the MEM_COUNT macros - nsf.c: fixed autoname problem with code from tcl trunk - fixed book-keeping for TclNamespace refcounting such that now alias.test, destroy.test and test.xotcl run now clean, 2 test are still open with tcl 8.5.10 (contains.test and xotcomm.test) - documented functions in nsfTrace.c - updated next-tutorial to the current naming conventions - added tests for using submethod handles - changed Stack example in tutorial from constructor to :variable - allow just valid specs for :attribute and :variable methods - improved error message for invalid parameter specs (with leading colons) - extended regression test - library/mongodb:updated to current interface in git HEAD -nsf.c: * move to greedy assert to an inner scope ("info method ...") * allow testwise "switch" as object parameter (when it is used, accessors are deactivated for this attribute) * extended regression test - nx.tcl: extended object-parameter "switch" implementation: now, accessors of type boolean are created, when type "switch" is used. - nsf.c: implemented "... info method origin ..." which returns the implementation handle (in contrast to the registration handle) of a method. - nx.tcl * renamed "attribute" to "property" * renamed "defaultAttributeCallProtection" to "defaultPropertyCallProtection" * renamed "nx::Attribute" to ""nx::VariableSlot" * renamed "BootStrapAttributeSlot" to "BootStrapVariableSlot" * renamed "createBootstrapAttributeSlots" to "createBootstrapVariableSlots" * removed method attributes * implemented old "attributes" definition in xotcl2 as method "parameter" - nx.tcl * renamed "info parameter name" to "info parameter names" (since it returns a list of names) * renamed "info parameter name" to "info parameter names" (since it returns a list of names) * renamed "info slots" to "info slot objects" * additional method "info slot definition" * additional method "info slot name" * additional method "info properties" (alias to "info slot definition") * removed "info parameter slot" * use term "noaccessor" and "noconfig" to deactivate accessors or object-parameters in property definitions * don't show slots with noconfig in "info parameter names" * don't show slots with noconfig in "info parameter definition" * renamed slot property "configparam" to "config" * renamed "::nsf::methods::class::info::slots" to "::nsf::methods::class::info::slotobjects" * additional public method ObjectParameterSlot.getPropertyDefinition * updated and extended regression test - nx.tcl: * added "/obj/ info slot definition" * added "/obj/ info slot name" * added "/obj/ info properties" (alias to "/obj/ info slot definition") * extended regression test - nx.tcl: * added parameter option incremental for "property" and "variable" * removed the nonpos argument "-incremental" from "property" and "variable" * adapted regression test for these cases - new folder example-scripts * Added 8 of the rosetta examples and one classical OTcl example * all examples are tested via regression test * all examples are pretty-printed via asciidoc * added example rosetta-abstract-type.tcl * added example rosetta-unknown-method.tcl * added ./apps/utils/source-doc-beautifier.tcl * fixed the file-handle output/formatting in rosetta-serialization.tcl; using proc "!" - nsf.c: * fixed next path computation in cases where command handles are used to refer to methods in the current mixin order. * extended regression test - nx.tcl: * made "/cls/ class ..." using ensemble methods and therefore extensible. * This introduces some definition order dependencies in nx.tcl and some redundancy ("class filter" and "class mixin"), but maybe this can be eliminated. - nsf.c: * fixed "nsf::my -local ..." (never worked in nsf) * added regression test - documenting current behavior * test method-handle + next + dispatch (unwanted) * test "my -local" vs my + method handle * test "my -local" vs dispatch + method handle - nsf.c: * added preliminary/minimal "private" support * private can be called via "my -local", direct dispatches are forbidden, ignored in mixins and next; * extended regression test * fixed name path in unknown called from ensemble methods (erroneous colon could show up) * added -system flag to: - ordinary dispatch (e.g. "o1 -system info vars") - nsf::object::dispatch with plain methodName - nsf::my (mutual exclusive with -local) - nsf.c: * change mem-count statistics from per-interp to per-thread * generalized GetObjectFromCmdTable() to ReverseLookupCmdFromCmdTable() * changed GetObjectScreenedByCmdName() to GetHiddenObjectFromCmd() * modularized interp.test to locate potential problems faster * partly simplified interp.test syntactically * deacivated a few tests in interp.test for the time being (runs commands after finalize) * re-established assertion checking for deleted cmds in cmd lists * added flag "-keepvars" to nsf::finalize for handling cases in interp.test * reactivated tests and simplified interp.test - disposition.test: * remove/check exit (see comments in the file) * handle exit from eval/inticmd with proper refcounts - nsf.c: * integrated "-local" and fully qualified handling with ObjectDispatch to ensure proper behavior of mixins/next etc. * added "/obj/ -local ..." similar to "/obj/ -system ..." * added "nsf::object::dispatch /obj/ -local ..." similar to "/obj/ -local ..." * extended regression test (next from -local, fully qualified names, private methods, "...dispatch -local") * provide error message for "/obj/ -system" - nx.tcl: * make calls from "protected", "public", and "private" system calls, such that "obj -system protected method foo {} ..." works, even when obj has a method named "method". * extended regression test - nsf.c: * added "/obj/ -intrinsic ..." similar to "/obj/ -system ..." * added "nsf::my /obj/ -intrinsic ..." similar to "/obj/ -intrinsic ..." * added "nsf::object::dispatch /obj/ -intrinsic ..." similar to "/obj/ -intrinsic ..." * extended regression test - nsf.c: * simplified permission handling * made private/protected mutual exclusive * extended regression test for private methods * per-thread MEM_COUNT tested with aolserver/naviserver * removed INTERP macros for MEM_COUNT (since we use now per-thread tables instead of per-interp tables as in the first implementation) * re-enabled transparency of private method in mixins * added transparency for per-object private methods * extended regression test - nsf.c: * allow protected and private methods to be used as filters * added regression tests * some cleanup in regression tests * added support for calling private methods via -local and filters * extended regression test for private + filters * removed "-local", "-system" and "-intrinsic" from plain dispatch (like e.g. "o1 -system method") * removed flag "-local" from nsf::object::dispatch * made nsf::my and nsf::object::dispatch available in the nx namespace - nsf.c: * factored out CmdIsNsfObject() for NRE handling with slave interpreters. * added flag ZSE_NRE_PROC for using nreProc instead of objProc - nsf.c * implemented NsfObjDispatchNRE and NsfObjDispatch * this fixed all issues of tcl8.6 and interp.test (xocomm still hangs in 8.6) - nsf.c: * "private" implies now "protected". This means, setting "private" sets as well "protected", unsetting "protected" unsets "private" * make sure the "... method definition" of private methods is returned as "private" * extended regression test - nsf.c: * removed warning about resetting cmd to 0 for private method invocations. - fixed interp.test for tcl 8.6 - fixed xocomm.test for tcl 8.6 - fixed mem_count on xocomm.test (was 26 / 26) - nsf.c: small performance improvements - nsf.c: experimental implementation of ::nsf::method::dispatch - renamed "nsf::method::dispatch" to "nsf::directdispatch" - renamed "nsf::object::dispatch" to "nsf::dispatch" - nsf.c: * added permissable value "private" to flag "-callprotection" for "info lookup method" and "inbfo methods". * extended regression test - doc: * fixed naming of "attribute" in migration guide * added "private" to migration guide * some textual improvements in migration guide * fixed spacing in documentation * fixed documentation of "info slot objects", "info slot names", "info slot definition" - nx: * added namespace "nx::internal" * delete procs via "rename to emtpy" instead of "defining procs with empty argumentes and body" * provided "-properties" as a replacement for -attributes, but without magic variable * extended regression test * changed "info slot name" to "info slot names" (like "info parameter names") - library/lib/pp.tcl: improved handling of placeholders - doc: * added section about ":variable" to the tutorial * fixed a few outdated places in tutorial - nsf.c: * reduce eagerness of methodName computation in ResolveMethodName() and GetRegObject() * reduce eagerness of DString computation in ResolveMethodName() * use conditional assignments in ResolveMethodName() * make use of Tcl_Obj type in ResolveMethodName() to reduce number of string operations - gentclAPI.tcl: * added option handling for every cmd/method/... * added option "-nxdoc" for outputting an index to ease maintenance in nxdoc - nsf.nxd: * adapted to new namings * tend to use fully qualified names (make maintenance easier) * bring cmds to an alphabetical order (make maintenance easier) * add optical separators between doc items to ease reading - fixed mem_count on contains.test - nsf.c: * minor cleanup * added regression tests * fixed recration of object alias with a alias to a different cmd - xotcl2.tcl: * added a backward compatible ::xotcl::alias method - Switched to the tcl numbering scheme. Version is now 2.0a1 Warning: From the Tcl point of view, this is a regression in numbering relative to the previous 2.0.0. Make sure to remove old releases from you installation path like e.g. rm -rf /usr/local/lib/libnsf2.0.0* /usr/local/lib/nsf2.0.0 - use same version numbers in nsf, nx and xotcl2 - library/lib/nx-zip.tcl: refactored implementqtion, improved utf-8 file-name handling (which is a mess in pkzip) - configure options: * improved and extended configure options to reduce necessity to switch features on and off in the nsf.h. Optional Features: --enable-profile build nsf with profile support (default disabled) --enable-memcount=yes|trace build nsf with memcount debug support (default disabled) --enable-development build nsf with development support (assertion checking, etc.; default disabled) Optional Packages: --with-dtrace build nsf with DTrace (default: without) - nsf.c: * report configuration options via "parray nsf::config" sample output % parray nsf::config nsf::config(development) = 1 nsf::config(dtrace) = 0 nsf::config(memcount) = 0 nsf::config(memtrace) = 0 nsf::config(profile) = 0 - build-environment: * make configure.in Makefile.in more in line with the TEA sample app (removing leftovers from prior versions of TEA) * remove GNU-Makefile-isms from Makefile.in - nsf.c: * provide an intermediary fix for the final memcount problem of elements in on the iPtr->errorStack * improve iPtr cleanup for memcount debugging - added configure option: enable-assertions (default on) - nsf.c: * allowed to call ":" like "my" or "self" Object create o o public method bar2 {} {return bar2-[: foo]} o public method bar5 {} {return [self]::bar5} o public method bar6 {} {return [:]::bar6} * extended regression test - nx.tcl: moved "properties" from nx::Class to nx::Object - nsf.c: make ":" a full equivalent vor nsf::my (i.e. support -local, -system and -intrinsic) - extend regression test nsf.c: - reform of argument parse. new parser uses NsfFlagObjType to reuse earlier parse results. Improved speed for for methods with primitive bodies: 5%-25%. - added regression tests for argument parsing - nsf.c - added experimental parameter option noleadingdash - additional regression test file method-parameter.test - provide selective error messages for unknown nonpos args nsf.c: - reform of method lookup. new code uses NsfInstanceMethodObjType and NsfObjectMethodObjType to reuse earlier lookup results. Improved speed for for methods with primitive bodies (over version before argument parse reform: 10%-43%. - additional compile-time option: METHOD_OBJECT_TRACE - experimentation version of unknown handler for non-pos args - extending regression test nsf.c: - moved methodEpochCounters from global vars to the interp state to improve reuse in multi threaded apps - separated objectMethodEpoch and instanceMethodEpoch - bump version number to 2.0a2 nsf.c: - new cmd for debugging: nsf::__db_show_obj - added MethodDupInternalRep() and FlagDupInternalRep() since they appear to be required in Tcl 8.6b2. tests: - added "package prefer latest" to avoid confusions of alpha and beta releases with install versions nsf.c: - added MixinregDupInternalRep() and FilterregDupInternalRep - perform more eager invalidation on objectMethodEpochs - cleanup on nsfObj.c nsf.c: - don't convert obj types tclCmdName and parsedVarNameType to instanceMethodObjType nx: added traits package noleadingdash handling: - doc: added "noleadingdash" to UML class diagram - nsf.c: added error message, when "noleadingdash" is used on non-positional parameters - nsf.c: use same logic for "noleadingdash" to "value in argument" - nsf.c: deactivated rudimentary unknown handler non nonpos args for the time being - nx.tcl: added handling of parameter option "noleadingdash" in objectParameterSlots - doc: * integrated ::nx::doc::make with Makefile.in (provide shell calls and, targets and dependencies) * provided a different flag for the generation of the documentation (-develop, .... or -final) to show/hide it. * separated entries for methods and hooks (can't be called if not defined)? hooks: * recreate should only be called internally, similarly "init" etc. * __unknown unknown is a hook for Object but a method for Class - fixed strange ref-counting bug in 8.6b2 bug-is-86.tcl where 2 ref-counted items are not freed (value:class, issued from nx.tcl around line 120). Compile with DEBUG86B2 for more info ================================================= # -*- Tcl -*- package req nx package require nx::test nx::Test case ensemble-next-with-colon-prefix { nx::Object create obj { :public method foo {} { return [:info class] } #:public method bar {} { return [:info] } :method info {} {;} } ? {obj foo} {wrong # args: should be ":info"} } ================================================= - nsf.c: cleanup on DEBUG86B2 - nx.tcl: * do not namespace import my into ::nx * replace usages of "my" by colon invocations - doc: * extended method resolution section * documented invocation flags for colon - nsf.c: * add flags "-closure" and "-source" to "/cls/ info methods" (the cases for "/obj/ info methods" are already covered by "/obj/ info lookup methods") * extend regression test - nx-traits: * use "info methods -closure" instead of instantiating a class at trait-insertion time * added trait as package nx::callback - example scripts: added tk-mini and tk-horse-race - make "/object/ require" an ensemble method - traits: renamed "useTrait" into "require trait" - added per-object traits (and per-class-object traits) - added tk-spread and tk-locomotive to example scripts - altered default handling to honor side effects of aliased object parameters. This slows down evaluation a little. Side-effects from aliased parameters are discouraged, since the order of the evaluation should not matter of an declarative evaluation of the argument vector. - extended regression test - library/mongo: * updated interface to current nx * updated to mongo-c-driver 0.4 (current version) * The mongo c-driver does not allow to add DBRefs, since it refuses to accept field names with leading '$'. So we skip these tests for the time being. -nsf.c: - remove quadratic behavior when adding many classes (10 thousands) - deletion is still for huge number of classes quite slow. - nx.tcl, xotcl.tcl: * remove proc "register_system_slots" since 'rename register_system_slots ""' fails on aolserver * bump version number to 2.0b1 - nsf.c: extended "new": * nonpos-arg "-childof" accepts now a namespace (required an object before). Therefore, one can use now "... new -childof [namespace current]", even when the current namespace is not an object - nx.tcl: simplified ::nx::ScopedNew to ::nx::NsScopedNew: before it was necessary to create a new volatile class for every invocation of contains. - extended regression test - nx.tcl: don't use mixins in method "contains", but remap the new implementation. If there are ten thousands of classes defined, adding mixins to the root meta-class degrades linearly on the number of classes defined (the mixin paths for all classes must be invalidated). This might be a problem for frequent "contains" invocations. - bump version numbers for nx, xotcl2 and nsf to 2.0b2 - rename "info method handle /methodName/" into "info method registrationhandle /methodName/" - rename "info method origin /methodName/" into "info method definitionhandle /methodName/" - added "info method handle" as short form of "info method definitionhandle" - added "info method origin" to return the target of an alias (or empty) - update migration guide and tutorial - cleanup "//" in sources nsf.c: - adding method epoch incr to NsfAddObjectMethod() and NsfAddClassMethod() - added function CmdListAddSorted() to improve mixinof management serializer.tcl: - Use directdispatch to query existing traces without the need of an extra method. By this change, the serializer works in constant time independent on the number of existing objects. nsf.c: - reduce number of RUNTIME_STATE(interp) in favor of a variable. - make time of the definition of a method independent on the number of defined instances (unless, when filters are defined) nsf.c: - fixed bug with recursive aliases - extended regression test - updated and shorted README.aol and TODO - removed the following files from the repository. deleted: COMPILE deleted: COMPILE.win deleted: ChangeLog deleted: unix/xotcl.spec.in deleted: win/Makefile.vc These files should be probably added again at some later time, but need some rework. Old versions are still available from the 2.0.0-develop branch - Replaced hash-table for GetAllInstances() with a linear list. As a result, mass-destroy on exit is now much faster. Valgrind reports that the full circle of creating 100.000 objects and destroying it on exit became about 15% faster. - added additional argument adEnd to CmdListAdd() - renamed CmdListRemoveList() to CmdListFree() - improved code documentation - removed unneeded AddInstance() and RemoveInstance() - deactivated uncalled ReverseLookupCmdFromCmdTable() and GetHiddenObjectFromCmd() since apparently not needed any more nsf.c: - removed conditionals in AddInstance() and RemoveInstance() - dropped result from RemoveInstance() nsf.c: - added method NsfMethodNamePath() to compute the ensemble path in error messages - reduce verbosity - extended regression test - nsf.c: fix call of DispatchDefaultMethod() in cases it triggers an error for 8.6b - nx.tcl: give a slightly better error message in case the root-object of an ensemble is called directly nsf.c: - handle duplicates in the cmd-list during cleanup - Avoid duplicate entries in instance lists for diamond inheritance by maintaining the set of already processed entries in NsfClassInfoInstancesMethod1() and GetAllInstances(). - extended regression test - removed "namespace import" in object-system test nsf.c: - Reform of subclass/superclass handling. * unifying transitive subclass handling * localizing and documenting caching of subclass/superclass lists * eliminating repetitious computation of subclass lists * re-factored code, renamed some functions to better reflect their purpose * improved documentation - fixed a potential crash for class deletion triggering implicit deletions further deletions referencing the parent class - extended regression test nsf.c, nsf.h, nsfStack.c, nx.tcl, tcl-cool.tcl, xotcl2.tcl - fix spelling in comments - strip unneeded space - fixed potential crash when generating error message about argument usage when called without a callstack - added regression test nsf.c: - change argument of IsMetaClass() to type *NsfObject - provide basic protection against most likely unintended deletion/overwriting of base classes. - extend regression test - fix typos - extend regression test to improve coverage - improve branch prediction - simplify macro handling with __WIN32__ - regroup some macro definitions - "info method": missing an option to return the "-returns specification". Also: "-returns" spec is not included in "info method definition". - simplified usage of ObjectName() and ClassName() macros (no caller parenthesis needed) - added experimental object property keepcallerself (currently only evaluated by aliased objects) - removed TODOs from keepcallerself in destroy.test; calls were truly recursive, behavior was correct. - Added experimental object property "allowmethoddispatch" for child-objects to be called from parent objects via method interface. Background: both, per-object methods and childobjects are implemented via cmds in the same tcl namespace. Without special care, both are callable via the standard dispatch. Apparently, this is not always wanted. - handled allowmethoddispatch and keepcallerself in copy/move - set allowmethoddispatch per-default in XOTcl - removed visibility of objects with "allowmethoddispatch" false in "info methods" and "info search methods" - extended regression test - improve handling of multiple error messages in a single command - alias reform: instead of calling first an alias with a stack frame followed by the dispatched of the aliased cmd, resolve aliases internally in dispatch. This has the advantage that we do not have to ignore the "transparent" stack frame in some situations, and we have much less problems with the names of the aliased cmds (e.g. objects) in the introspection methods. Additionally, several problem cases disappeared from the regression test cases. In addition, the new approach is faster. - eliminating obsolete flag NSF_CSC_CALL_IS_TRANSPARENT - use alias-stubs for aliases pointing to objects. This allows us to distinguish between cases, where an object is dispatch-able due to the alias or due to allowmethoddispatch (when the object happens to be a subobject and has therefore its cmd in the same namespace). The semantics are now: - aliases to objects are always dispatch-able, no matter, how allowmethoddispatch is set. - direct sub-objects of objects are currently on dispatch-able when allowmethoddispatch is set. Note, that this could be seen as a method-property of the method-name, which could be made "private" as well to avoid such direct dispatches. - nsf.c: start all error messages with a lower case word for consistency and to follow closer to Tcl's conventions - deactivate for the time being allowmethoddispatch (make it behave always like true) - added instead new flag "perobjectdispatch" to make behavior of ensemble objects configurable. - The behavior for keepcallerself is currently only activated for the method-interface for object dispatch, since otherwise the following would be dangerous, since "o2 foo" would destroy o2 nx::Object create o1 nsf::object::property o1 keepcallerself true nx::Object create o2 { ::public method foo {} {o1 destroy} } o2 foo - fixed potential crash from method caching, when permissions on cmds are changed and become e.g. unresolvable - removed flag allowmethoddispatch, since behavior can be archived via private flag. - extended regression test - extend regression test for interactions between "keepcallerself" and "perobjectdispatch" - some minor cleanup - fixed NRE memory management (for Tcl 8.6) by refactoring alias handling - removed documentation about incompatibility to XOTcl1 in respect of the method interface for object invocations - doc fixed line-number handling locally - changed object->flags from "unsigned short" to "unsigned int" - reintroduced NSF_TCL_DELETE to address bug flagged from memdebug - extended regression test - Cleanup for compilation under MSC (avoid unsupported forward declaration of array) - further documentation of functions, better grouping of functions - use fixed array size for method_definitions for MSC - Don't export symbols in the general case just because of MSC/C89 compliance - Forward setting of object parameters to the slot object, when assign method is user-defined on the slot object - Cleanup and extend regression test - additional object parameter option "invokesetter" managed by nx.tcl This was necessary, since the previously implemented strategy called the setter whenever slot= was provided. This has the problem, that values could be converted twice (once by "configure", once by the setter method", which lead for the converter to a double refcounting on the value. - use Tcl's EXTERN macro instead of "extern" - treating incompatible forwarding to slot vs. slot option noaccessor - extended regression test - don't allow object creation to overwrite non-object cmds (e.g. procs) - don't allow method to overwrite child object - extended regression test - documented cmd overwrite protection feature as incompatibility with XOTcl 1 - documented dependencies between configure flags and feature activation cpp macros nsf.c: - Fixed a bad interaction between Tcl's apply (pushing lambda frames) and the variable resolvers. The variable resolver was not able to resolve variables, while the command resolver was still working correctly. - Extended regression test nsf.c: - added object parameter option "slotinitialize" - renamed object parameter option "invokesetter" -> "slotassign" - call slot.assign instead of setter of object - removed restriction on nosetter/invokesetter: nosetter can be used in connection with slotassign - added regression test for slot.initialize nsf.c: - pass property name to slot "initialize" method to conform with the interface to "assign", "get" ... (all these receive the property name as well) - allow slot "initialize" method to be protected (handled similarly to "init") - extended regression tests for yield - implemented "next" for ruby-like enumerators (each needs still more work) - tcl86.test: better implementation of method "each", cleanup and extension of enumerator tests - fix compilation when compiled without threads (many thanks for r.zaumseil for noting this). - protect serial generation for parameters via mutex - added compile macro NSF_STACKCHECK to provide stack monitoring/debugging (especially useful for multi threaded programs, where stack is more limited) - make ::nsf::log more robust for aolserver/naviserver, since ::ns_log is not always around when an output is needed - serializer: * make [::Serializer deepSerialize -ignoreVarsRE "" $o] behave like [::Serializer deepSerialize $o], since learn@wu depends on that, and a value for ignoreVarsRE of empty looks more like an omitted value than a regular expression, that should match everything. * extended regression test nsf.c: - generalize stack monitor by handling growing and shrinking stacks - refactor into funciton CheckCStack() - serializer: * pertain perobjectdispatch and keepcallerself in serializer * extend regression test nsf.c: - refactor ObjectCmdMethodDispatch() for clarity - prepare work on object method dispatches with KEEP_CALLER_SELF and no NSF_PER_OBJECT_DISPATCH - explorative implementation of object method dispatches with KEEP_CALLER_SELF and no NSF_PER_OBJECT_DISPATCH - extend regression test nsf.c: - implement escaping for comma in value of parameter options: escaping in values can be achived via duplicating the comma. In the following example is the value for arg "1,3" D public method foo {a:commaRange,arg=1,,3,optional} {..} Escaping via \ whould actually require 4 backslashes due to Tcl's escaping rules (two, to get a single backslash, another two due to list-splitting to obtain default from arg). - extend regression test nsf.c: - allow parens in property names (array syntax) - added "/obj/ info name" (as alternative to "namspace tail [self]") nx.tcl: - added "private property foo" - extended regression test - start error messages with a lower case word for consistency and to follow closer to Tcl's conventions Documentation: - added design study ruby-mixins.tcl to example-docs and regression test - added documentation for "/obj/ info name" to migration guide and .nxd file - adding more comments to examples in migration guide - document private properties in tutorial and migration guide - improve wording in documenting - extend regression test nsfShadow.c - bump MethodEpoch when a tcl ::rename command happens on a nsf method (which might be cached in a Tcl_Obj) This fixes a crash reported by Arthur Schreiber nsf.c: - make NsfInstanceMethodEpochIncr() and NsfObjectMethodEpochIncr() accessible from all files using nsfInt.h - remove experimental code (backslash escaping for "," in parameter option parse nsf.c: - added a SlotContainerCmdResolver() to avoid interaction of slot names with names of callable tcl commands. Without the SlotContainerCmdResolver() the call to "list" in a property named "list" leads to a call to the container object ::Test2::slot::list instead to the intended ::list. The implementation is not perfect, since it ignores the namespace path inside the slot container. - added regression test. nsf.c: - ignore in internall calls to "dealloc" protection settings - handle cyclical class dependencies during object system finalize - extend regression test nsf.c: - handle cyclical superclassclass dependencies during object system finalize - extend regression test nx.tcl: - set multiplicty for mixins and filters by default from 1..n to 0..n to avoid error messages, when e.g. empty mixin lists are configured. Setting emtpy mixin-lists is strictly speaking not necessary, but this eases the code in cases the lists are sometimes empty. nx::test: - added summary at the end of "make test" via log file - updated .gitignore nsf.c - rename nx::Object.configure to nx::Object.__configure to free method name "configure" for an e.g. tk-like configure - refactored code to allow to parameterize handling of required flag for parameters - don't flag an error when configure is called on an initialized object (logic: if the object is initialized, configure must have been called before, and the required action must have been already taken). nx.tcl: - rename the following internally called methods (not for XOTcl). alloc -> __alloc dealloc -> __dealloc objectparameter -> __objectparameter recrate -> __recreate - from these methods, only __objectparameter is defined per default, the others are defined on demand - updated 34 copyright notices nsf.c: - extended nsf::method::delete to handle ensemble names nx.tcl: - added tk/incr-tcl style cget methods on class/object levels as ensemble methods. - improve copy handling with other child-types of the slot container working - make sure to ignore non-slot-type objects in slot introspection - worked on regression test until "methods.test". others are missing, but maybe reconsideration nsf.c: - implememted cget as a configure-like method, dropped ensemble method variant nx.tcl: - simplified "/obj|cls/ delete method" due to resolving capabilities in nsf::delete::method xotcl2.tcl: - made destructor of Connection more robust such it does not depend on accessor methods. - fixed regression test to run all test again correctly nsf.c: - made argument of cget required nx.tcl: - added Tk-style methods "configure" and "cget" - added additional regression test set for cget and configure - renamed testlog file, remove it on "make clean" nx.tcl: - remove debugging output nsf.c: - fixed parmeter syntax for required nonpos args - deactivate deletion of methods via nsf::object::delete during shutdown to avoid missing slot forwarders called from destructors nx::Class create C { :property {b b1} :public property {c c1} :protected property -accessor {d d1} :variable foo } Property reform part 1: - disallow protection modifiers for "properties" and add new flag "-accessor" to "property" and "variable" This changes definitions like Class create C { :property {a a1} :public property {b b1} :protected property {c c1} :private property {d d1} } to Class create C { :property {a a1} :property -accessor public {b b1} :property -accessor protected {c c1} :property -accessor private {d d1} } since "properties" are always public accessible over the "configure" and "cget" interface, but the accessors methods might not be public. The value of the accessor might be also "none" (specifying explicitely that no accessor is wanted) or "", which means: use the default. Same holds for "variable" - disallow parameter option "incremental" and change it to a flag of the property or variable. The motivation for this is due to the fact, that "incremental" is a property of the accessor, and not of the value. old: Class create C { :property foo:int,incremental :variable bar:int,incremental } new: Class create C { :property -incremental foo:int :variable -incremental bar:int } - disallow "public class property" and friends since these are not needed - removed parameter property "noaccessor" - removed "nx::configure defaultPropertyCallProtection" and method hook "__default_property_call_protection" - introduced "nx::configure defaultAccessor" and method hook "__default_accessor" - for the time being, "defaultAccessor" is "public" for NX and XOTcl, will be changed to "none" in NX - extended regression test (new file properties.test) Property Reform Part 2: better handling of per-object properties nsf.c: - changed "/class/ __objectconfigure" to "/obj/ __objectconfigure" to be able to handle per-object properties on classes properly. - renamed "info method parametersyntax" -> "info method syntax" - renamed "/obj|cls/ info method parametersyntax" into "/obj|cls/ info method syntax" - replaced "::nsf::methods::class::info::objectparameter" by "::nsf::methods::object::info::objectparameter" - new command "::nsf::parameter::specs ?-configure? ?-noposargs? slotobjs": convert provided slotobjs into a list of parameter specs - new command "::nsf::parameter::get list|name|syntax parameterspec": convert parameter spec into syntax form, or retrieve pieces of information from it (can be extended in the future) - added more or less generic list handling functions TclObjListFreeList(), TclObjListNewElement() and TclObjListAdd() used by "::nsf::parameter::specs" - replaced "::nsf::method::property /obj/ -per-object /name/ slotcontainer ?value?" by "::nsf::object::property /obj/ slotcontainer ?value?" - added "::nsf::object::property /obj/ hasperobjectslots ?value?" nx.tcl: - new info methods * "/obj/ info lookup parameter definitions" * "/obj/ info lookup parameter names" * "/obj/ info lookup parameter list" * "/obj/ info lookup parameter syntax" - changed "/cls/ info parameter definition ?name?" into "/cls/ info parameter definitions ?name?" since ir returns a list. Still, "list" or "syntax" won't be plural XOTcl 2 - don't blindly register all object/class methods for XOTcl nsf.c: - fix potential bad interaction between per-object mixins and per-class caching of object-parameters - first draft of per-object parameter caching (for per-object-mixins and per-object properties). nsf.c: - rename invalidateobjectparameter -> parameter:invalidate::classcache - rename invalidateobjobjectparameter -> parameter:invalidate::objectcache - bring cmds into alphabetical order - NsfObjInfoObjectparameterMethod(): return not only the first matching parameter, but the list of all matching ones. The last optional argument was renamed from "name" to "pattern" accordingly - invalidation of per-object parameter cache * on mixin changes and * on deletion/adding of per-object slots - activate PER_OBJECT_PARAMETER_CACHING per default (flipping this parameter makes regression test more than 20 faster). - extended regression test nsf.c - added functionality for "cget" to call parameter-methods (e.g. "... cget -class"). The method cget calls either "/slot/ get ..." (when slot=... is provided in the parameter spec) or it assumes that the method without argument returns the value - added "::nsf::object::property /obj/ volatile" to query whether a object is volatile or not - "/obj/ cget -volatile" returns now the volatile state of the object - factored out ParameterMethodDispatch() from OConfigureMethod() - extended regression test nx.tcl: - change parameter name in "/cls/ info parameter ... ?pattern?" from "name" to "pattern" - changed name "/obj|cls/ slot info definition" to "/obj|cls/ slot info definition" since result is a set - dropped parameter method "properties" - dropped "/obj/ info properties" (since "properties" or "variables" are returned") - extended regression test nsf.c: - factored out ParameterMethodForwardDispatch() to call a parameter method defined as a forwarder the same way from "configure" and "cget" - extended regression test nx.tcl: - property has now a boolean non-positional argument "-config" /obj|cls/ property ?-accessor value? ?-config boolean? ?-incremental? ?-class value? spec ?initblock? in symmetry with "-accessor" (parameter option "noconfig" is still needed to flag nsf for variables that should be initialized, which are not configurable - "/obj|cls/ info slot definitions" returns a full command (containing flags and property|variable) - extended regression test nsf.c: - handling of method names in error messages from nsfAPI.h. Make sure that error message is generated with the actual method name. Object-method Reform: - changed interface to object specific commands by requiring an ensemble named "object". The rational behind is essentially to use always the same info command to retrieve object specific methods, no matter whether these are defined on a plain object or an a class object (does not break the "contract" what e.g. "info method" returns). Now we define methods via: /cls/ method foo {args} {...body...} /cls/ object method foo {args} {...body...} /obj/ object method foo {args} {...body...} Similarly, aliases, forwards and mixins are defined, e.g. /cls/ mixin add ... /cls/ object mixin add ... /obj/ object mixin add ... /obj/ require object method ... /obj/ object property ... /obj/ object variable ... The same change propagated as well to the "info" method. Now we have: /cls/ info methods ... /cls/ info object methods ... /obj/ info object methods ... Similar, the object parametererization uses /cls/ create obj -object-mixin M /cls/ create obj -object-filter f /metacls/ create cls -mixin M1 -object-mixin M2 /metacls/ create cls -filter f1 -object-filter f2 - as a consequence, a) "/cls/ class method ..." "/cls/ class alias ..." "/cls/ class forward ..." "/cls/ class filter ..." "/cls/ class filterguard ..." "/cls/ class mixin ..." "/cls/ class mixinguard ..." "/cls/ class info ..." "/obj/ class method require method ..." "/obj/ class method require public method ..." "/obj/ class method require protected method ..." "/obj/ class method require private method ..." "/cls/ class property ..." "/cls/ class variable ..." "/cls/ class delete property ..." "/cls/ class delete variable ..." "/cls/ class delete method ..." "/cls/ require class method ..." "/cls/ require public class method ..." "/cls/ require protected class method ..." "/cls/ require private class method ..." were dropped b) "/obj/ method ...." "/obj/ alias ...." "/obj/ forward ...." "/obj/ filter ...." "/obj/ mixin ...." "/obj/ info method*" "/cls/ create obj -mixin M" "/cls/ create obj -filter f" "/obj/ require method ..." "/obj/ require public method ..." "/obj/ require protected method ..." "/obj/ require private method ..." were dropped - added package nx::class-method to allow optionally the "class" notation "/cls/ class method ..." (and friends, see (a)), and "/cls/ class info ... - added package nx::plain-object-method to allow optionally plain method b) "/obj/ method ...." (and friends, see (b)) - added support to slots to use ensemble methods as setters - added "/obj/ object variable" and "/obj/ object property" - bumped version number to 2.0b5 - tested with NaviServer and OpenACS (new version of nx needs as well a newest NaviServer, since ns_cache implementation needs to be objectified; newest NaviServer version works as well with older nx) - moved "/obj/ info slot definition|..." to "/obj/ info object slot definition|..." for consistency - provided "parametersyntax()" for "object mixin" and "object filter" Method and configure parameter reform: - unify handling / naming / parameterization of method parameters and configure parameters - New Interface: /cls/ info configure parameters ?pattern? -> list of params /cls/ info configure syntax -> syntax output /obj/ info method parameters /methodName/ ?/pattern/? -> list of params /obj/ info method syntax -> syntax output /obj/ info lookup configure parameters ?/pattern/? -> list of params /obj/ info lookup configure syntax -> syntax output /cls/ info parameter list|name|syntax /param/ -> value "... method syntax" and "... configure syntax" return the full method/configure call, and not only the parameters as in previous versions. Therefore, providing a pattern could lead to unexpected results, therefore the argument was dropped. - Replacements relative to 2.0b4: {/cls/ info parameter definitions} -> {/cls/ info configure parameters} {/cls/ info parameter definitions x} -> {/cls/ info configure parameters x} {/cls/ info parameter syntax ?pattern?} -> {/cls/ info configure syntax} {/obj/ info lookup parameter definitions ?pattern?} -> {/obj/ info lookup configure parameters ?pattern?} {/obj/ info lookup parameter syntax ?pattern?} -> {/obj/ info lookup configure syntax} - Dropped calls: /cls/ info parameter list ?/pattern/? /cls/ info parameter names ?/pattern/? syntax of a single parameter via this interface /cls/ info configure syntax ?/pattern/? /obj/ info lookup parameter names ?/pattern/? /obj/ info lookup parameter list ?/pattern/? Method and configure parameter reform, Part 2: In order to streamline the interface further, we tried to follow the idea to use "... info /plural word/" to obtain a set of handles, and then a separate call to obtain the details. Therefore, we replaced /cls/ info slot objects /cls/ info slot definitions /cls/ info slot names /obj/ info object slot objects /obj/ info object slot definitions /obj/ info object slot names /obj/ info lookup slots by /cls/ info slots ?-type /type/? ?-closure? ?-source all|application|baseclasses? ?/pattern/? /obj/ info object slots ?-type /type/? ?/pattern/? /obj/ info lookup slots ?-type /type/? ?-source all|application|baseclasses? ?/pattern/? - nx.tcl: handle "incremental" in slot reconfigure - nx.tcl: change defaultAccessor to "none" - dropped "/obj/ info slot definition /slotobj/" in favor of "/slotobj/ definition" Method and configure parameter reform, Part 3: - added /obj/ info lookup variables -> list of handles /obj/ info lookup object variables -> list of handles /obj/ info variable definition|name|parameter /handle/ - nx.tcl: added forward compatible scripted implementation of "lmap" - nsf.c: handle names for private slots in pattern provided to AddSlotObjects(), used e.g. in "info lookup slots /pattern/" - added new regression test info-variables.test - nx-pp.tcl: fixed changed accessor handling, added cget to highlight words - updated next-migration guide to reflect changes from the configure reform - "info method syntax" returns now "/cls/ ...." - "info object method syntax" returns now "/obj/ ...." - hopefully the last changes for ?object? method|variable|property: defined * "/obj/ delete object method" * "/obj/ delete object property" * "/obj/ delete object variable" - extended parameter extractor: new functionality ::nsf::parameter get default /parameter/ ?/varname/? ::nsf::parameter get type /parameter/ /obj/ info parameter get default /parameter/ ?/varname/? /obj/ info parameter get type /parameter/ - nsf.c: handle full-qualified name for private slots (AddSlotObjects()) - extended regression test - C-code Generator: added "-typeName" for enumeration types that allows for disambiguation of enumerations with different argument names. Before that, the argument name determined the c-type of the enumeration. Therefore it was not possible to use argName "-type" for two different functions with a different list of enumerators. - changed "-methodtype" to simply "-type" in /obj/ info methods ... ?-type all|scripted|builtin|alias|forwarder|object|setter|nsfproc? ... /obj/ info object methods ... ?-type all|scripted|builtin|alias|forwarder|object|setter|nsfproc? ... /obj/ info lookup methods ... ?-type all|scripted|builtin|alias|forwarder|object|setter|nsfproc? ... - removed some TODOs from tests/parameters.test - parameter dispositions: We differentiate now between "initcmd" and "cmd": an "initcmd" is only evaluated once, but one can provide configure values for this parameter later. a "cmd" is executed on every evaluation, it is only possible to pass cmds to it. The trailing argument of the configure parameters (used e.g. for scripted object/class definitions) is now of type "cmd". Implementation not yet complete (object cases are not correct). - nsf.c: fix crash when "nsf::my" is called with a single argument outside the object context. - fixed cases, where valuechangedcmd (and the other traces) did not work with "configure" method. When slot traces are used, it cleans other traces for the same operations. - extended regression test - added implementation for slots with traces+types for classes - exception for incorrect defaults are thrown during slot creation - extended nsf::is, added parameter * ?-configure? -- accept configure parameter options * ?-name /name/? -- provide a parameter name for error message - simplified nx.tcl by using new nsf::is - extended regression test - changed ::nsf::parametersyntax(..) to ::nsf::parameter::syntax(..) - xotcl2: adjusted manual parameter syntax entries to new conventions Cleanup of nsfAPI.decls - remove unneeded enumeration types - use "typeName" to shorten reported names of parameters - use camel case for reported names Traits: - changed from traits-as-objects to traits-as-classes. This allows for higher orthogonality of traits and class definitions and allows in principle traits for object-specific methods (not fully implemented/tested) - fixed property/variable inheritance in traits. nsf.c: - changed enumeration values for "-source" in "info lookup methods" "info lookup slots" "info methods" "info slots" of "all|application|baseclasses" to "all|application|system" for consistency with "nsf::my" and "nsf::dispatch" which uses "-system" as well nx.tcl: - removed "info is .." since it might raise more questions than it solves - renamed initblock parameter from __initcmd to __initblock - renamed nsf::configure parameter from "keepinitcmds" to "keepcmds" - saving "keepcmds" in an associative array named "__cmd(/parameternName)" to allow saving of multiple parmeters with less name clash danger (when application stays away from variables stating with double underscore) - completed coverage if plain-object-method.tcl - provided warnings via plain-object-method.tcl via "nsf::log warn ..." nsf.c - fixed potential infinite loop in pattern matching for precedence lists - cget: make error message closer to tcl conventions - extended regression test package nx::plain-object-method: - made warnings configurable via nx::configure plain-object-method-warning on|off - completed coverage and test cases package nx::class-method: - convenience package similar to nx::plain-object-method - allow for usage "C class method ..." in addition to "C object method". - made warnings configurable via nx::configure class-method-warning on|off - completed coverage and test cases nx.tcl: - replaced functionality of "/obj/ configure" by "/obj/ info configure" to re-enable semantics of the plain configure method, even when called without arguments. "/obj/ info configure" is actually a convenience method to allow to write o1 info configure instead of o1 info lookup configure syntax - traits: added ability to turn on verbosity for traits by using nx::configure trait-verbosity on|off nx.tcl: - renamed variable option "-config" to "-configurable" to make its intention clearer - changed multiplicity of mixin, object-mixin, filter, object-filter from 1..n to 0..n; rationale: when one has a list of eg. mixins, which should be passed, one has to test for the length before passing it, otherwise the empty list would generate an error. Allowing 0..n makes the usage simpler and the program shorter. Removed oboslete item. At least in this concrete form, the warning does not show up. - NSF_WITH_VALUE_WARNINGS: Right now, value warnings are also fired for arg vectors with a delimiting "--"; right now, this gives a warning: Object create o { :public object method foo {-np1 -np2 p1} { return $p1 } } ? {o foo -np1 1 -np2 2 -- -X} "-X" nsf.c: - when creation with an required configure parameter failed, delete the half-baked object to avoid confusing states. - improved handling of required configure parameters when classes are changed dynamically. When configure parameter are defined required, checking for the absence of required parameter was only performed at creation time. When objects were re-classed or their classes extended with required parameters, later calls to configure did not trigger exceptions. Now we check for the existence of the instance variable which addresses most of these points, but in future we might wish a more general solution (see comment for futures releases) nx::test: - deactivate calling overhead calculation, since this is not reliable (sometimes larger than the call). - Old TODO maybe obsolete: handling of recreate (see regression test for class-level properties) Could not find refernces about this in the parameters or properties tests. nsf.m4: - nsf.m4 currently unused (SC_PATH_NSFCONFIG and SC_LOAD_NSFCONFIG) We can delete it from the current version. nsf.c - fixed a bug in "info methods returns" in cases, where no returns info was available. - we can "/obj/ copy" now objects/classes containing * aliases * setter * ensemble methods * method protections Instead of handling cmd copying in NsfNSCopyCmdsCmd, it is replaced now by introspection. - extended regression test nsf.c - fixed a potental crash on destroy for objects having a wrapperless alias defined - removed obsolete function AssertionAppendPrePost() - removed obsolete function NsfNSCopyCmdsCmd() and ::nsf::nscopycmd (handled now more general on scripting level in the "copy" method) nx.tcl: - "copy" method: fixed copying of class-level per-object methods - extended regression tests serializer.tcl - added flag -objmap to Serialzer.deepSerialize to make serialzer usable for copying (-map is to coarse) - extended regression test nsf.c: - Eliminate all use of Tcl_GetStringFromObj() function. nx::test: - use the standard configure interface for configuring instead of own version - changed from nx::Test to nx::test (user never has to know that nx::Test is a class). - change test cases to newer interface - don't use "namespace import nx::*" in test cases when not required nsf.c: - reduce variable scopes - fix a bug in SlotContainerCmdResolver() when NSF_MEM_COUNT is activated - fix a small memory leak for PER_OBJECT_PARAMETER_CACHING - all cases detectable with --enable-memcount=yes are fixed - recheck Announce - concatenate Changelog - update next-scriping doc + examples, pdf-files? - build tar etc. as in README.release nsf.c: - dont't use the default of a invocation parameter in "configure" when the object is already initialized. The default is in general only used when the parameter is not specified. We do not want e.g. superclass to be reset to ::nx::Object, when configure is called on a class without arguments. - extended regression test - prepare for providing nx as a tcl module (.tm file). this is just a preparation, since for testing, one cannot set up a path that prefers a local copy over a global installed one (the global tcl-site is preferred over the one specified in e.g. TCL8_5_TM_PATH) Moving to tcl modules works in essence via git mv library/nx/nx.tcl tcl8/site-tcl/nx-2.0b5.tm This is really usable, when http://core.tcl.tk/tcl/tktview?name=86ceb4e2b6 is through monogodb: - updated to most recent version of c-driver (0.7.1) - adapted to nx 2.0b5 (use everywhere cget interface) - tested with mongodb 2.4.5 - added example scripts rosetta-sudoku.{tcl,html} and tk-ludo.{tcl,html} - added sample script doc/example-scripts/tk-geo.tcl mongodb: - integrated configuration of mongodb into toplevel configfile option: --with-mongodb=MONGO_INCLUDE_DIR,MONGO_LIB_DIR - added regression test files for mongodb support (lowlevel (tcl-only) and highlevel (nx based oo support)) - integrated mongodb-testfiles with "make test" - reduced verbosity of nx-mongo.tcl (added verbosty variable) nsf.c - don't call postcondition, when the command/invariant have returned already an error - fixed a bug where turning on assertions could swallow result-codes - fix potential crash when preconditions are empty - extended regression test serializer - fix object mehtod serializeExportedMethod: targetName might have been uninitialized nsf.c - reduce variable scope - remove uncalled static function nsf.c - fixed a bug where turning on assertions could swallow result-codes - extended regression test nsf.c: - added flag -checkalways to nsf::proc and nsf::asm::proc (for the latter just a placeholder for now). If the flag is used, it will cause argument testing independently from the "configure checkarguments" setting. To force argument checking always is useful e.g. for checking external values (e.g. in a web server) - nsf: added switch "-checkalways" to nsf::method::create - nx: added switch "checkalways" to "method" and "object method" - extended regression test xotcl2: - fixed "... info defaults ..." and "... info instdefaults ..." emulation in XOTcl 2 - fixed error message - extended regression test - bumped revision of nsf/xotcl/nx to 2.0b6 gentclAPI.tcl, generic/nsf.decls - make converter usable from c-based packages compiled with subs activated - add parameter parser and converter to stub tables generic/nsfStubLib.c: - change base stub table from XOTcl to NSF. - improve wording of error messages. generic/nsfPointer.c: - add reference counter to avoid double-inits and double-frees in case the table of converters is used from multiple interpreters generic/nsf.c: - made linearization monotonic (for multiple inheritance) via single-inheritance linearization merging while preserving overall linearization rules - added flag NSF_LINEARIZER_TRACE - extended regression test library/lib/make.tcl: - don't try to load nx when building pkgindex for a binary package (.so or dylib) mongodb - upgrade to mongo-c-driver to 0.8.1 - added new flag "-ttl" to mongo::index - there seems to be now a differen mongo-c-driver to be the preferred one, the old one is renamed to mongo-c-driver-legacy - link against nsf-stublib - bump version number to 0.2 generic/nsf.c: - add more assertions - ensure computation of requires orders for recursive merges mongodb: - add flag "-puts" to method "show" of nx::mongo::Class to turnoff output to stdout - handle empty find operations for "find first" - added method pretty_variables to output available variables of a class in a similar style as in the definition - added low-level method "close" to nx::mongo nsf.c: - new command ::nsf::object::alloc /class/ /obj/ ?/script/? alloc an object and execute script in the context. Can be used to regenerate an object in a old state. serializer: - fixed loading of objects with required data in the blueprint (many thanks for david hopfmueller for reporting this) - make use of nsf::object::alloc (1 command instead of 1 create + 2 evals) - these changes imprived laading time of blueprint by about 25% for OpenACS+xowiki - don't rely on the existence of a "trace" method nsf.c: - when ::nsf::object::alloc is passed an empty name (2nd argument), behave like "new" method nx: - allow copy of objects with required arguments - use ::nsf::object::alloc in "copy" method - don't depend on method "trace", use directdispatch instead - remove method "-noinit" (nsf::object::alloc makes it obsolete) - extend regression test serializer: - restore traces after object-serialize nsf.c: - fix stub provisioning for Tcl 8.6 - reduce verbosity for FreeUnsetTraceVariable - return TCL_OK, even when FreeUnsetTraceVariable() fails (warning stays) nx-mongo: - implement simple persistent handle management based on per-thread objects -nsf.c: - fix bug in interaction between uplevel method and interceptor transparency - fix bug in interaction between uplevel method from tcl procs - extend regression test build-system: - provide datarootdir to get rid of warning during configure nx-mongo: - updated documentation (switch back to mongo-c-driver, but comment usage of tagged version v0.8.1) - added support for rep types (allow for mappings between certain instance variables such as arrays or dicts to customizable representations in MongoDB) - added nx-serialize to test cases (simple state persistance for nx objects) - added nx-rep to test cases (rep types for "array" and "dict") - improve performance of mongo->tcl conversion by using predefined global strings nx-monogo: - Updated the mongo-c-driver and libbson to the actual tip version from github (this is a significant change, since 10gen essentially changed the officially supported c-driver of MongoDB) - mongo-c-driver was more or less new-implementation, since strucure and names changed in the mongo-c-driver substantially, serveral functions were added, several were dropped. The new interface supports now e.g. mongo URIs, and should be faster (by using collection objects instead of connection handles) - Although the low-level nsf interface changed significantly, the high level (nx level) interface remained unaffected. - Configure has now --with-mongoc=... and --with-bson instead of --with-mongodb - New commands: mongo::collection::open /mongoConn/ /dbName/ /collectionName/ mongo::collection::close /collection/ mongo::gridfs::store_string /content/ /name/ /contentType/ - Make use of the new collection handle mongo::count /mongoConn/ /ns/ ... -> mongo::collection::count /collection/ ... mongo::index /mongoConn/ /ns/ ... -> mongo::collection::index /collection/ ... mongo::insert /mongoConn/ /ns/ ... -> mongo::collection::insert /collection/ ... mongo::query /mongoConn/ /ns/ ... -> mongo::collection::query /collection/ ... mongo::remove /mongoConn/ /ns/ ... -> mongo::collection::delete /collection/ ... mongo::update /mongoConn/ /ns/ ... -> mongo::collection::update /collection/ ... mongo::cursor::find /mongoConn/ /ns/ ... -> mongo::cursor::find /collection/ ... - nsf::mongo::connect receives now a mongoc_uri https://github.com/mongodb/mongo-c-driver/blob/master/doc/mongoc_uri.txt - The gridfs interface allows now to store multiple revisions of a file - The gridfs interface allows now upload files from a string - The gridfs interface allows to refer to files by other attributes than just the filename (e.g. the mongo id). - Modified/new gridfile functions mongo::gridfile::create ?-source file|string? /gridfs/ /value/ /name/ /contentType/ mongo::gridfile::delete /gridfs/ /query/ mongo::gridfile::open /gridfs/ /query/ - Updated README - Updated regression test - Added editor hints for a more uniform appearance nsf.c: - change name of enumeratorConverterEntry to Nsf_EnumeratorConverterEntry, move it with NSF_ARG_* flags to tcl.h to make it available in derived modules using the converter - Added editor hints for a more uniform appearance nx.tcl: - raise an error, when an ensemble methods redefined a plain method nsf.c: - fix small memory leak in multiple inheritance code. - all regression tests reun cleanly with --enable-memcount=yes - let [current methodpath] return full path (similar to -path option in "info methods" - handle collateral damage in regression test due to changed result of "current methodpath" nx::traits: - handle ensemble methods correctly (use full method path for resolution) - add new regression tests for traits nx-mongo: - optional support for mongodb connection pools (compile time macro USE_CLIENT_POOL controls this, per default, this is on) - allow to pass "-metadata" to gridfile::create to ease metadata attachment to gridfiles - some conveniance improvements in nx-mongo.tcl (ability to ignore attributes in "bson encode", ability to unset attributes in gridfs, ...) - bump version numbers of nsfmongo to 0.3 and nx-monogo to 0.5 - represent BSON_TYPE_REGEX as pairs (regex + options) in the Tcl representation to preserve regular expression options - update to newest version of mongo-c-driver and libbson from github - tested with mongodb-c-driver 0.93.0 nx-mongo: - fixed suprising compiler message "alignment of array elements is greater than element size" when using e.g. "bson_iter_t i[1]" - some c-code cleanup - tested with mongodb-c-driver 0.92.3 - added mongo::collection::stats - added mongo::cursor::aggregate - extended regression test nsf.c: - fix case, where NsfDStringPrintf() failed (when print llength including \0 was 1 byte longer than print buffer) - make sure, that the list kept for the cached parameter is just built from unshared objects; otherwise Tcl append will abort nx.tcl: - new package "nx::volatile" - don't define configure parameter "-volatile" per default; use "package req nx::volatile" instead - don't define per method "volatile" per default; use "::nsf::method::require ::nx::Object volatile" instead - get rid of -volatile in nx.tcl and serializer - updated/extened regression test nx-mongo: - added command "::mongo::status /mongoConn/" - extended regression test nsf.c: - invalidate paramter caches of subclasses on NsfParameterInvalidateClassCacheCmd unless during shutdown. Otherwise some classes might not become aware of properties added later to superclasses. - extend regression test nsf.c: - remove redundant NULL tests - improve safety mof macro ObjStr() build-process: - replace make.tcl by the much simpler mkIndex.tcl: * Does not use pkg_mkIndex * Does not load binary files (problem for cross compiling) * Requires package provide with constant in one line. small introspection reform: - Introspection for commands and arguments did not work for cmds defined in subpackages (such as mongodb). We keep now this information in hashtables and maintain a slim interface for this. - fix generation of pkgIndex.tcl for mongodb Configuration: - stick closer to TEA conventions (keep tclconfig-sh in tclconfig directory) - remove obsolete version of install-sh, copy manifested version to mongodb library - fix configure.ac quoting forwarders: - RFE "provide %method" as keyword like %proc" was already implemented. Dropping %proc would break XOTcl2 compatibility. - adding a test case - use for output of forward ... -verbose NsfLog(...NSF_LOG_NOTICE...) instead of fprintf() to make it redirect-able - use in forwarders "-frame object" instead of "-objframe" in nx for consistency with other calls (e.g. dispatch). Other values for "-frame" are not allowed. (btw, XOTcl has "-objscope") - deactivated "-onerror", since its semantics are expressible via try/catch, and there are no regression tests for xotcl and nx, and we could not find any script that uses this - renamed "-methodprefix" to "-prefix" in nx, since the prefix can be applied as well applied to a cmd. - use nx rather than xotcl2 terminology in nsf::method::forward nsf.c: - de-spaghetti precedence computations for multiple inheritance and improve documentation - get rid of // comments - use nonnull variable attributes for prototpyes (nsf.h, nsfInt.h, nsf.c) - add returns_nonnull assertion - simplify few inner code pieces based on assertions - add serveral more assertions based on nonnull sepcifications. - made nsf::is using the "-strict" option when tcl's "string is" is called. - let the c-code generator produce as well nonnull assertions - simplify FilterInvalidateObjOrders() and FilterRemoveDependentFilterCmds() - simplify SuperclassAdd() - improve code documentation nx.tcl: - Define method "value" as a slot forwarder to allow for calling value-less slot methods like e.g. "get" dispite of the arity-based forward dispatcher. - extend regression test - added more test cases for multiplicity and incremental - preserve lower bound of multiplicity when incemental is added - added log-level Info which prints always, e.g. for "-verbose" flag of forwarder nsf.c: - add flag "-onerror" to nsf::forward::method to handle errors during dispatch of a forwarder (not all error messags of forwarder are already transformed) - added log-level Info which prints always, e.g. for "-verbose" flag of forwarder - drop setter-rule from properties (use always forwarder) - drop "/obj/ /prop/" and "/obj/ /prop/ /value/" in favor of "/obj/ /prop/ get" and "/obj/ /prop/ set /value/" to achieve better orthogonality with e.g. incremental properties - allow parameter option "method=" for slotassign as well. rationale: this allows to use the parameter "forwardername" to specify a different forwarder target method (e.g. in case of object-mixins). The method is used both in "configure" and "cget". - allow methodname to consist of max two words in relation slots (e.g. "-methodname {mixin set}"} - allow configuration of internally called "slot.get" and "slot.assign" methods via objectsystem::create - rename default slot methods add/get to value=add/value=get - provide an error message, when refering to a non-existing slot object - added likely/unlikely to result == TCL_OK etc. - fix one more subtle case of an error being swallowed: for xotcl, the constructor returns the list of residual arguments. In case there was an error, it was possible that the returned residual arguments overwrote the error message in the interp result - finish implementation of NsfForwardPrintError() - use NsfForwardPrintError() in ForwardArg() for all error messages nx.tcl: - replace empty-named arrays by dicts - remove setter methods from BootstrapVariableSlots - reducing interface of BootstrapVariableSlots by setting methods protected - use value=* as names for interally called and forwarder-called accessor methods - enforce using "set" for filter/object-filter in slot operations (same as for mixins) - add "set" as a method name for relation slots - implemented relation slot "mixin" and "object-mixin" via "slotassign" to disallow "/obj/ mixin /value/" and "/obj/ object mixin /value/" to use instead "/obj/ mixin set /value/" and "/obj/ object mixin set /value/" while keeping "configure" and "cget" working. This has the advantage that "/obj/ mixin set" does not try to replace the mixin chain by "set" - disallow "assign" for nx::variableSlots - use set/get/add as slot methods for get/configure/incremental operations - demangle slots for nx/xotcl2 further - enforce usage of "get" for all slots in nx - put test cases for all kind of mix setter / getter together in one test case xotcl2.tcl: - use object system configuration for -slot.get and -slot.set - use value=set instead of value=assign - simplify "-parameter" implementation - add setters for "name", "domain", and "default" to xotcl::Attribute for backward compatibility mongodb - by directing calls to the setter, it is now more complex to determine the true calling object for an converter, since the set operation might be routed though the slot object. It would be nice to have framework support for this. - fix 2 error messages - provide shorter tracebacks by using "return -code error ..." rather than "error ..." nx::test: - don't delete system slot ::xotcl::Attribute on cleanup nx.tcl: - add slot method value=unset to nx::RelationSlot and nx::VariableSlot - extended regression test - reworked error message generation of slot-forwarder (list all available slot methods with prefix value=) - cleaned up relation slot mixin variants xotcl2: - use xotcl::RelationSlot instead of nx::Relationslot in xotcl2 (we can more value=assign here). Makefile: - fix load paths for sublibs (e.g. mongodb) in regression test nsf.c: - added nsf::var::get and "::nx::var get" to provide selector based interface for variable reading (used in slotmethod get of nx::VariableSlot) - renamed nsf::relation to nsf::relation::set and added nsf::relation::get in accordance with nsf::var::get - fixed unary argument passing notation for "-nocomplain" of nsf::var and for 42 other options of other commands nx.tcl: - added flag -nocomplain to "/obj/ /prop/ unset ?-nocomplain?" - use "mixin|filter clear" instead of "mixin|filter unset" - name parameter option "slotset" instead of "slotassign" - have "filter|mixin clear" return the list of former|removed filters|mixins. nsfObj.c: - allow to omit "-guard" within arguments to flag definition of a filter/mixin guard - extended regression test nsf.c: - improve handling of space in object names - added methods "info lookup filters ?-guards? ?/pattern/?" and "info lookup methods ?-guards? ?/pattern/?" nsf.c - force again literal "-guard" in a "mixinreg" type to avoid potential confusions - Base nsfRelationSet for mixins on cmds rather than TclObjs to avoid confusions and duplication of guard detection logic - Add interp to NsfMixinregGet() and NsfFilterregGet() to be able to return error messages - return more error message from mixinreg converter - provide at least minimal support for "funny class names" (i.e. containing spaces) - FinalObjectDeletion: don't enforce namespace = 1 for cases with wierd namespace deletion order - extended regression test nx.tcl - switch from "/obj/ info parameter" -> "nsf::parameter::get" to reduce potential semantic confusion of info options. "info parameter" was not object/class specific at all, but is just a syntax extractor nsf.c: - extend nsf::parameter::get to obtained more detailed information for obejcts/classes/metaclasses/baseclasses and specified types - extend regression test - Updated tutorial and migration guide nx.tcl - drop short form "/obj/ info configure" for now - make output of "/obj/ info lookup configure syntax" equivalent to "/obj/ info configure" gentclAPI.tcl: - handle duplicated domains by folding these to a single definition nsf.c: - added command nsf::method::get. Rationale: provide a central place to obtain information about all kind of method handles, which might be - scripted and c-based methods - nsf::procs - plain tcl procs - cmds (with and without paramter definitions) - make results of ListMethod() robust against missing information (e.g. plain tcl cmds, missing object registrations, etc.) - factor out common code for ListMethod call sites for per-object methods, instance methods and procs/cmds to ListMethodResolve() - return errors from failing converter registrations - extend regression test (new test set nsf-method.test) nsf.c - renamed parameter::get -> parameter::info - renamed method::get -> cmd::info nsf.c, gentclAPI.tcl: - new argument types "virtualobjectargs" and "virtualclassargs" for context-specific argument resolutions: when a context object is provided, arguments of type "virtualobjectargs" are determined based on the slots applicable for the object (like "... lookup ..."), arguments of type "virtualclassargs" are resolved against a class. These types are used as follows: /obj/ configure /virtualobjectargs/ /cls/ create /name/ /virtualclassargs/ /cls/ recreate /name/ /virtualclassargs/ /cls/ new ?-childof /obj/? /virtualclassargs/ This new feature allows us to provide better error messages and to make much of the "... info ... configure parameter ..." infrastructure much less important. - For "virtualclassargs" we need the functionality to obtain from the C-Code based on a class the configure parameters applicable to objects of this class. - add argument "-context ..." to "cmd::info" to pass the context object (so far the only place where the context-object is used) - object system configuration parameters changes: new: -class.configureparameter new: -object.configureparameter removed: -class.objectparameter nsf.c: - added options to filter output from ::nsf::cmd::info parameter options (args, syntax, parameter) - deleted: - "/obj/ info lookup configure parameters ?pattern?" - "/obj/ info lookup configure syntax" - added: - "/obj/ info lookup parameters /methodName/ ?pattern?" - "/obj/ info lookup syntax /methodName/ ?pattern?" This covers as well - "/obj/ info lookup parameters configure|create|new|... ?pattern?" - extend regression test mongo: - upgrade to the newly released version 0.96 of the c-driver - replace deprecated function mongoc_collection_delete by mongoc_collection_remove - tested with MongoDB v2.6.1 nx.tcl: - removed /cls/ info configure parameters /cls/ info configure /cls/ info syntax Use e.g. "/cls/ info lookup parameters create" instead moved block here for keeping arguments ===== - configure parameters: we have to cleanup Given: nx::Class create Person { :property name :create p1 } Person info configure says, how object "Person" can be configured Person info configure syntax says, how instances of "Person" can be configured p1 info configure says, how object p1 can be configured p1 info lookup configure syntax says, how object p1 can be configured (long form of above) p1 configure -help gives a reasonable error message, "p1 configure" does not work, since no args are needed Person new -help gives a reasonable error message, except, that "configure" is not perfect Person create just complains about missing name, does not know about configure arguments Person create -help creates an object named "-help" Person create foo -help gives a reasonable error message, except, that "configure" is not perfect possible path: 1) "Person info configure" is dangerous, too close to "Person info configure syntax" but completely different. maybe: use "Person info configure" as short form of "Person info configure syntax", or drop it, since the lookup variant is at least not surprising. 2) It would be nice if we would be not to need the "info" at all but improve the errors in 5-9, maybe special switch "-?" or "--" 3) another possible path: p1 info lookup syntax configure (short form of nsf::method::get syntax [p1 info lookup method configure]) where "configure" is a method name, would work the same way as "p1 info lookup syntax create|foo|..." (under the assumption, we get the "right" syntax for configure). Similar: p1 info lookup parameter configure (short form of nsf::method::get parameter [p1 info lookup method configure]) this way, we would not need "info configure" and friends at all. Tk uses "/obj/ configure" for obtaining possible values Furthermore: The command p1 info method definition [p1 info lookup method configure] gives an error, since "info method" is not defined for p1, we could have used p1 info object method definition [p1 info lookup method configure] which is somewhat strange, since configure is not an object method. Probably: nsf-level command for handles. ==== nsf.c: - Let "/cls/ info mixinof -closure" return as well implicit mixin classes to make it easier to determine class dependencies. Example: nx::Class create M0 nx::Class create M1 -superclass M0 nx::Class create M2 -superclass M1 nx::Class create C nx::Class create D -superclass C C mixin add M2 # M2 is mixed into C, and implicitly into D # # Since M2 is a subclass of M1, classes C and D depend as well # on M1 and M0, as seen in the heritage: ? {C info heritage} ":M2 ::M1 ::M0 ::nx::Object" ? {D info heritage} ":M2 ::M1 ::M0 ::C ::nx::Object" # previously, only "M2 info mixinof -closure" showed the # mixin relations, even with "-closure", while M1 and M0 did not. ? {M2 info mixinof -closure} "::C ::D" # now these show the same relations (in this example). ? {M1 info mixinof -closure} "::C ::D" ? {M0 info mixinof -closure} "::C ::D" - adapt mixinof.test to the additional information - transform mixinof.test to newer style regression test with automated object deletion - updated migration guide and tutorial to reflect recent changes nsf.c: - cleanup of NsfParameterInvalidateClassCacheCmd(): performance improvements. After the fixing of indirect mixin validation, performance of invalidation went up by a factor of 5. At least, in some important cases (invalidation of root-classes like nx::Object / xotcl::Object), we are again on the same level as before (actually slightly faster). - use functions IsRootClass(), IsRootMetaClass() and IsBaseClass() to access object/class properties - add gcc attribute "pure" - rename nsf::parameter:invalidate::classcache -> nsf::parameter::cache::classinvalidate nsf::parameter:invalidate::objectcache -> nsf::parameter::cache::objectinvalidate reasons: (a) remove single colon inside the name, (b) put verb to the end - fixed error message for forward ... onerror and method paths. The command "C object mixin" returns now "::C object mixin add|clear|delete|get|guard|set" and not "::C mixin add|clear|delete|get|guard|set" as before. nsf.c: - new function DependentSubClasses() to determine all classes that inherit from a given class. The result extend TransitiveSubClasses() by including class mixin relationships. - simplify NsfParameterCacheClassInvalidateCmd() by using the new function DependentSubClasses(). With the new implementation, NsfParameterCacheClassInvalidateCmd() is as efficient as before without to MostGeneralSuperclass optimization (but being complete now) when working on the root classes (an more efficient on subclasses). - added experimental code feature CYCLIC_MIXIN_ERROR nsf.c: - improve performance of MixinInvalidateObjOrders() by about 30% by recompiling the list of the dependent classes over and over again, since MixinInvalidateObjOrders() iterates over the full list already. - Document NsfRelationClassMixinsSet() and add nonnull declarations and the usual assertions() nsf.c - base MixinInvalidateObjOrders() on DependentSubClasses() and avoid the need of using a separate hash table for class-mixin handling. The new implementation is several times faster and improves the speed of the functions CleanupDestroyClass(), SuperclassAdd() and NsfRelationClassMixinsSet(). Adding a class-mixin to ::xotcl::Object in OpenACS is more than 4x faster. - remove obsolete function MixinResetOrderForInstances() - rename ResetOrderOfClassesUsedAsMixins() to ResetOrderOfObjectsUsingThisClassAsObjectMixin() - used consistenlty DependentSubClasses() instead of TransitiveSubClasses() for invalidations. - extended regression test nsf.c: - added option "-dependent" to "info subclass" - extended regression test nsf.c: - move NsfMethodNamePath() out of NsfObjWrongArgs() - move NsfMethodNamePath() out of NsfUnexpectedArgumentError() and NsfUnexpectedNonposArgumentError() - fix name paths in error messages triggered from ArgumentParse() - use 3-argument version of NsfMethodNamePath() - don't invalidate class-level param caches during shutdown nsf.c: parameter passing reform - don't pass full argument list for filtering methods calle further methods from C (such as new/create/... ) to allow processing of e.g. "--" in "new" to separate arguments to "new" cleanly from arguments passed to "create". Now we can use e.g. "C new -- -childof 123" in case class C has a property "childof". - extend c-generator to accept option "-objv0" to pass the original "objv[0]" to the called command. Since we had previously "allargs", we have to pass the objv[0] now differently - more thorough checking ISOBJ(someObj) macro for asserts (use "assert(ISOBJ(someObj))" instead of just "assert(someObj)") - extend regression test nsf.c: - checked, that all CallDirectly() cases, where method is dispatched (no direct call) are covered by the regression tests - avoid double memcpy() in dispatch recreate by using ObjectDispatch() rather than CallMethod() - removed memcpy() in call-directy for "create" - some more cleanup gentclAPI.tcl: - added option "-flags", which can be used for every parameter. example: .... -flags NSF_ARG_NOLEADINGDASH .... - experimental: use NSF_ARG_NOLEADINGDASH for pattern "info subclass" to improve error messages. - extended regression test nsf.c: - relax the meaning of noleadingdash to allow negative numbers - rename noleadingdash to e.g. nodashalnum nsf.c: - define means to protect "undefined" internally-directly called methods __alloc and __dealloc in nx. This is achieved mostly via a an additional value in a method declaration in ::nsf::objectsystem::create. Example: -class.dealloc {__dealloc ::nsf::methods::class::dealloc 1} - extend regression test nsf.c: - allow abbreviated nonpos args - change name of relation slot "superclass" to "superclasses". (part of a planned change to use plural for set-valued parameters, "info superclasses" and similar changes for mixins/filters will probably follow) nx.tcl: pluralism reform part 2 - changed methods /cls/ info subclass -> /cls/ info subclasses /cls/ info superclass -> /cls/ info superclasses /cls/ mixin ... -> /cls/ mixins /cls/ filter ... -> /cls/ filters /cls/ object mixin ... -> /cls/ object mixins /cls/ object filter ... -> /cls/ object filters - changed configure parameters /cls/ configure -mixin -> /cls/ configure -mixins /cls/ configure -filter -> /cls/ configure -filters /obj/ configure -object-mixin -> /obj/ configure -object-mixins /obj/ configure -object-filter -> /obj/ configure -object-filters - added handling for calling relation slot with unknown sub method nx.tcl: - make all __* system methods in nx redefine-protected - let "nsf::configure objectsystem" return handles and protections as well nx.tcl: pluralism reform part 3 - introduced simple plural form "mixins" and "filters" for introspection - moved differentiated interface into slot methods. the slot methods "get" stay symmetrically to "set", but additional methods "classes" or "methods" are used like "guard" to obtain partial results of the mixin and filter specs - changed info methods /cls/ info mixin classes -> /cls/ info mixins /cls/ info filter methods -> /cls/ info filters /obj/ info object mixin classes -> /obj/ info object mixins /obj/ info object filter methods -> /obj/ info object filters - dropped /cls/ info mixin guard /cls/ info filter guard /obj/ info object mixin guard /obj/ info object filter guard - added /cls/ mixins classes /cls/ filters methods /obj/ object filters methods /obj/ object mixins classes - asymmetry between "/obj/ info lookup mixins" vs. "/obj/ info ?object? mixin classes" resolved. nsf.c: - dropped unused object::info::is - renamed ::nsf::methods::class::info::filtermethods -> ::nsf::methods::class::info::filters ::nsf::methods::object::info::filtermethods -> ::nsf::methods::object::info::filters ::nsf::methods::class::info::mixinclasses -> ::nsf::methods::class::info::mixins ::nsf::methods::object::info::mixinclasses -> ::nsf::methods::object::info::mixins nsf.c: - provide error messages for ambiguous abbreviations - extend regression test (now 5460 tests) nsf.c: - see no need, why we should "set nodashalnum for int types" - extended regression test nsf.c: - remove redundant null check for object and add assertion documentation: - add current args to migration guide - fix cut&paste error: replace "current currentclass" by "current calledclass" Migration guide and tutorial: - updated "/cls/ info superclasses" and "/cls/ info subclasses" - updated "/cls/ info mixins" and "/obj/ info object mixins" - updated "/cls/ info filters" and "/obj/ info object filters" - dropped "/cls/ info mixin guard" and "/obj/ info object mixin guard" dropped "/cls/ info filter guard" and "/obj/ info object filter guard" (use "-guard option insteads)y - updated "/cls/ mixins ...", "/obj/ object mixins ...", "/cls/ filteres ...", "/obj/ object filters ..." nsf.h - In Tcl 8.6.1 it might be that a Tcl_Obj has length > 0 but bytes == NULL. We have to relax out tcl_obj-sanity test for this case from assertions. nsf.c: - remove redundant definition - reduce variable scope - make sure to follow nonnull assumptions nsf.c: - implement a new approach to error reporting in ensembles: instead of trying to find the "right" place to report the best "error", compute the longest valid ensemble prefix from all the stack frames. nx.tcl: - simplify the info ensembles on nx::Object or nx::Class significantly, by making use if ensemble-next. - delete "info unknown", since this is not called. nsf.c: - make types for bit operations unsigned (mostly flags) build system: - don't call genstubs from configure, since Debian does not seem to have genstubs.tcl installed. Now, we pre-generate the stub files for tcl8.5 and tcl8.6 and copy the "right" version depending on the configured version. - Make dtplite configurable in Makefile, e.g. make "DTPLITE=/usr/local/ns/bin/tclsh8.5 /usr/local/ns/bin/dtplite" man - regenerate documentation - bump verison number to 2.0.0 (also in .man files) ======================================================================== TODO: - should we change "/obj/ info lookup syntax /methodName/" to return obj and method as well? (similar to "info method syntax /methodName/") - we could drop methods::object::info::objectparameter - check deactivated tests in tests/serialize.test C(One), C(IgnoreAll), C(None2) and xlloc fix Stefan: doc items - make rough comparison table with NX, XOTcl, tclOO, itcl, Ruby, Python comparison NX vs. TclOO is most probably the most important Most general superclass Metaclass Per-object methods Per-Object mixins -- none, class, or class hierarchy Per-Class mixins -- none, class, or class hierarchy transitive mixins classes Per-object filters Per-class filters aliases Traits Composite Traits Method protection public/protected/private positional arguments none, leading args, arbitrary argument value checkers Create objects classes with no callable methods - doc/tutorial2.html - warnings for "numeric" names for args and nonpos-args? - special handling of values looking like nonpos-flags, but wich are not ones (-1, "- a b c", ....) in detection when to throw unknown. - interface of "variable" and "attribute": * add switch -array for "variable"? (Just setting is trivial, handling setters and incremental setter is more work) - extend mongo::gridfs::store_file with a switch -metadata to pass metadata together at gridfs file creation time - do we have to adjust the documentation in xotcl2 for object initialization? - maybe optional arg (true) to ::nsf::object::initialized to generalize -noinit - maybe: add a disposition=pipe - maybe: add "-asHTML" as style option to parametersyntax - MixinComputeOrderFullList() could receive a flag to store source classes in checkList - if the check on eg. info-heritage-circular in test/info.method.tcl reports a warning on exit, if we get an exception. - what to do with metadata analyzer? Still needed? Already replaced by new doctool? Does it have to run on make? Can doctool run on Make? - Higher binary compatibility for future versions: * It is not nice to have the full Nsf_Param structure in nsf.h (required for Nsf_methodDefinition in the c code generator) * It is not nice to have the full ParseContext structure in nsfmongo (required for the allocation of the parse context in the stubs) Adding fields to these structures would kill alder binaries - when alloc|dealloc are loaded via require, we have no redefined-protection on those. Since the script does not know, on which class|object these are defined one cannot make assumption on these. Problem? - The structure of the script body requires currently that "class|object ?-per-object?" is inserted at the 1nd pos. This is not sufficient, if we would like to test in this script, whether the first arg is e.g. a class. - document value added replacements of Tcl functions - configure parameter type forward: - regression test - get rid of eager tcd creation (optimization, can go to future releases) - slotmachinerie 2 - should we deactivate add/delete for non-multivalued cases? - allow noarg+default/initcmd ? - default/initcmd/subsdefault: can we simplify these? or add messages for conflicting usages. - Makefile/::nsf::config: Integrate git meta-data (commit hash, branch/tag labels) - doc: * no-accessor properties/slots are still reported as methods: class * method parameters need indentation ... * method ensembles are reported as "Implementation details: alias", is this ok? * doc validator reports wierd info submethods: info definition, info names, info objects -> mean "info slot *" ... smells like generator garbage ... * inconsistency: "info slot *" are built on "slotsobjects" which does not take -source and -closure parameters ... still, they are in the NX method interfaces ... review and document accordingly. * sub-method cross-references per @use does not work (parameters are not reproduced, probably no [:origin] resolution is performed: See the case for "info properties" -> "info slot definition" * "info method": elaborate on the options, right now the doc is minimal ... * "info method" -> why does the parametersytnax does not report all enumeration literals, rather than ?infomethodsubcmd? ??? * fix sub methods validation reporting -> mismatch? * Ensemble methods: obj info & friends ... there is no parametersyntax reported; add something literal in the template: ? * Object|Class mixin|filter guard -> how to document (and not reported by nxdoc as missing either, as their as slot-specific)? And link to Class class filterguard|mixinguard ... Why not defining the latter as ensembles "Class class filter|mixin guard"? * Ensemble methods, i.e. intermediate ones, should not be filter by inheritance in the nxdoc output. For example, info() in Class shares many details of info in Object, but it does not visualize this quasi-inheritance * @package.@require(): really needed? * @package.@version: fix validation mode ... expected/actual version numbers are not compared ... * NextScriptingLanguage/index.html: glossary entries in nsf.nxd should be sorted (in the source) ...... Maybe, a single glossary.nxd file? SS: Right now, the name of the nxd file derives from the the script name. I mark this as TODO for the future. * doc/langRef2.xotcl vs library/xotcl/doc/langRef.xotcl * @author: how to visualise the authorship in the generated markup (yuidoc)? - do we need contains in nx? - nsf::proc * check, if there are parameter types that should not be applicable for nsf::proc * toplevel (object less) introspection - documentation * migration guide 3.8. Dispatch, Aliases, etc.: to be done or omitted - check performance implications of value checker - library + XOTcl apps - work on binary packages of xotcl (store + xml) - nicht gewartete/nicht getestete library aus distro entfernen? - migration von einzelnen paketen nach next? von welchen? Ein paar Punkte im folgenden könnten obsolet sein: TODO "Kleinigkeiten" - should we continue to work on the problem of the interp-aliased class, exported from one ns, imported into another one? - constants in type converter (in and out) #if 1 /* TODO: the following check operations is xotcl1 legacy and is not generic. it should be replaced by another methodproperty. Most of the is*String() definition are then obsolete and should be deleted from xotclInt.h as well. */ if (isCheckString(methodName)) { return TCL_OK; } #endif For future releases (after first alpha/beta release) - cmd introspection: nsf defines a commands (e.g. nsf::proc) and unregistered methods. The interfaces of these commands can be queried via methods % nx::Object info method parameters ::nsf::proc -ad:switch procName arguments body % nx::Object info method syntax ::nsf::proc /cls/ proc ?-ad? /procName/ /arguments/ /body/ % nx::Object info method definition ::nsf::proc Warning: Could not obtain alias definition for proc... which return only patially correct results (the first is correct, the second one is misleading, the error message can be improved for the third case. maybe ::nsf::info command parameters|syntax /cmd/ might make sense, but there might be more candidates for nsf::info around. OTOH, we have already nsf::object::exists, nsf::is, etc. * generalizing parameter-specific per-object info Now we have __initcmd(varname) for init-cmds of varitable traces __cmd(varname) for the eval-ed cmds (mostly for documentation purposes to postprocess the init block We could need __required_satistfied(varname) to handle required parameter aliases As generalization, we could use a dict __parameterstate(varname) or even __parameterstate with a dict to collect the various aspects. for performance reasons, we do not want to set this variable too frequent, serialization and blueprint aspects have to be considered. This would as well address cases, where the parameter has a different name than the associated varianle (e.g. private properties) * Currently, in NX, specifying mandatory parameters may break object construction as init won't receive any arguments (no residual args). We should provide a warning when a user defines arguments for init (or provide some other syntactic sugar) * extend traits (object-specific traits, test cases, etc.) * RFE: forwarders/aliases: -checkalways is missing. Issues: 1) limit to -returns only? checkalways affects only input-parameters, not return. (purpose: make it possible to rely on parameter checking from external sources, no matter whether checking is turned on/off). furthermore, it effects only checking for procs. For C-implemented commands, there is no way to avoid e.g. checking if something is an integer, since the binary representation of the integer is passed. so i don't understand "limit to -returns". 2) cover value checkers of method parameters also, effectively overruling -checkalways settings of the aliased/forwarded nsf::proc or method? i guess that this is based on the assumption, there would be value-checkers for forwarders or aliases. Then one would have to handle the conflicts. However, forwarders and aliases pass arguments around without having any knowledge about parameter definitions. They don't check anything by design. * RFE Revisit nsf::*::assertion interface? Why does nsf::method::assertion allow for setting invariants. One would rather expect a ::nsf::object|class::assertion or the like? The reason for the current naming is simply that assertions are only implemented for scripted methods. * pre/post conditions are just checked for scripted methods, since only these have stack frames, which are necessary to access self or the resolver variables. * invariants are only checkable during scripted methods, there is no way to intercept c-based functions. Checking these before/after c-implemented functions should be possible though. * REF: feature request 'remove "-guard" from mixin and filter specs, just have two-element lists' would require to have different sets of converters for slots depending on object system * add maybe "::nsf::object::property /obj/ volatile 0|1" to alter volatile state. * Reduce / remove hard-code names. Right now, "method"/"alias"/ "forward" is returend by "info definition" introspection. This is not serious, since e.g. XOTcl handles this on the script level by mapping these names to the XOTcl counterparts. We could as well make this configurable via the object-system parameters. * Consider alternate method name/place for subcmds (and/or slots) on classes - currently, there are potential conflicts between slots and ensemble objects - provide a different place in the namesspaces could simplify this * Cleanup the set of active filters when filters are removed (only relevant for the speed of scripts with filters and a high number of instances) * Ensembles - It could be possible to reduce stack frames in ensembles. Just a top ensemble frame could be sufficient. - The full names for ensemble methods could be stored in the cmd tables to make lookup faster. * Serializer: handing of xo::at_cleanup in serializer (either generalization or move to OpenACS/aolserver init/naviserver init) * Parameter/argument handling - Add an unknown handler for unknown non-pos args to Argument parser. The problem is to find a way to aviod method deletions or redefinitions or to recover from these gracefully (when e.g. the unknown handler deletes the parameters currenlty being worked on). - Canonical parameter representations: "p:integer,multivalued" => "-name p -type integer -multivalued" "x:type,arg=::D d1" => "-name x -type type -arg ::D -default d1" - Argument passing of C functions is currently based on an interpreter of the argument lists. It should be possible to turn the interpreter into a compiler to speed up argument passing. But maybe, this would bloat the code-size. - Use parameter syntax in genTclAPI - better handling of "c1 cget -noinit" ? * experimental features: - document/extend/generalize/remove the experimental object properties perobjectdispatch and keepcallerself - behavior on keepcallerself on ordinary dispatches with implicit/explicit receiver (currently the flag is ignored, the code just commented out) * C-Code * Several of the tracing options in nsf.h could be replaced by DTrace * Rework implict namespace completion (NameInNamespaceObj(), maybe based on BeginOfCallChain()). * Rework C-interface (maybe for post-alpha, but we have be first clear on scope and intention. A possibility would be to provide all entries from gentclAPI, but we would loose most likely static property and the newly achieved ease of changing is effectively frozen by the stubs interface, which has to be stable. * NsfClassListAdd() and friends could be made generic * Coroutines - extend coro regression test - coro-enable nsf::proc * renable/translate deletex XOTcl demo scripts, such as e.g. those from library/xotcl/apps/scripts observation: - [current isnextcall] does not work for ensemble next: ... but should probably not, since this is an implicit next call just for resolving an ensemble call. Object create o { :public object method foo {} {return [current isnextcall]} :public object method "x y" {} {return [current isnextcall]} } Class create M { :public method foo {} {next} :public method "x y" {} {next} } ? {o foo} 0 ? {o x y} 0 o object mixins add M ? {o foo} 1 ? {o x y} 1; # gives 0 ...aclocal.m4000066400000000000000000000012621242365656200127320ustar00rootroot00000000000000# generated automatically by aclocal 1.14.1 -*- Autoconf -*- # Copyright (C) 1996-2013 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_include([m4/tcl.m4]) apps/000077500000000000000000000000001242365656200120345ustar00rootroot00000000000000apps/utils/000077500000000000000000000000001242365656200131745ustar00rootroot00000000000000apps/utils/asciidoc.conf000066400000000000000000000002741242365656200156240ustar00rootroot00000000000000[blockdef-listing] source-style=template="source-highlight-block",presubs=(),postsubs=("callouts",),posattrs=("style","language","src_numbered","src_tab"),filter="source-highlight-with-pp"apps/utils/nxdoc000077500000000000000000000063631242365656200142450ustar00rootroot00000000000000#!/usr/bin/env tclsh package req nx::doc 1.0 namespace eval ::nx::doc { namespace import -force ::nx::* Class create CLI { # # rendering # :property {outdir "."} :property {format "html"} :property {theme "yuidoc"} :property {layout "many-to-many"} # # doc project # :property doctitle :property {docversion 0.1} :property docurl :property docbaseurl :property {validation:switch false} # # frontend # :property {frontend dc} :property includes :property excludes :property indexfiles:alias :property -accessor protected sources:1..* { set :incremental 1 set :config false } # # auxiliary # :private method barf {msg} { return -code error "NXDoc: $msg" } :protected method ... args { foreach i $args { set idx [string first : $i] if {$idx == -1} continue; dict lappend :sources [string range $i 0 [expr {$idx - 1}]] [string range $i [expr {$idx + 1}] end] } } :protected method indexfiles {paths} { # # TODO: Should we provide for an auto-lookup of nxdocIndex files, # similar to tclIndex or pkgIndex.tcl? # # # Some sanity checks first ... # set sources [list] foreach p $paths { set p [file normalize $p] if {[file exists $p] && [file readable $p]} { lappend sources [list source $p] } else { : -local barf "Cannot find or read the index file '$p'" } } if {$sources eq ""} return; set i [interp create] $i eval { proc ::unknown args { puts stderr "UNKNOWN: $args"} namespace eval ::nxdoc::internal { proc getIndexedCmds {{choice 0}} { if {[array exists ::nxdoc::include]} { array set out [list 0 "" 1 ""] foreach {v cmd} [lreverse [array get ::nxdoc::include]] { lappend out($v) $cmd } return [lindex [array get out $choice] 1] } } } } if {[catch {$i eval [join $sources ";"]} err]} { : -local barf "Sourcing index files failed with error message '$err'" } lappend :includes {*}[$i eval [list ::nxdoc::internal::getIndexedCmds 1]] interp delete $i } :protected method __objectparameter {} { set slots [[current class] info slots] foreach slot $slots { lappend defs([$slot position]) [$slot getParameterSpec] } set parameterdefinitions [list] foreach p [lsort [array names defs]] { lappend parameterdefinitions {*}$defs($p) } return [concat $parameterdefinitions ...:alias,args] } :protected method init {} { set prj [@project newFromSources \ -frontend ${:frontend} \ {*}[expr {[info exists :includes]?[list -include ${:includes}]:""}] \ {*}[expr {[info exists :excludes]?[list -exclude ${:excludes}]:""}] \ ${:sources} \ -name ${:doctitle} \ -url ${:docurl} \ -version ${:docversion} \ {*}[expr {${:validation}?"-mixin ::nx::doc::@project::Validator":""}]] $prj write -format ${:format} -theme ${:theme} -layout ${:layout} -outdir ${:outdir} } } namespace export CLI } namespace import -force ::nx::doc::CLI # # nxdoc -outdir /tmp/ -format html -theme yuidoc package:nsf # CLI new {*}$argv apps/utils/source-doc-beautifier.tcl000066400000000000000000000043531242365656200200650ustar00rootroot00000000000000#!/usr/bin/env tclsh # # Script to beautify scripted documentations. # # The script receives a nx script as an argument and # outputs to the same directory a asciidoc scipt. # # Gustaf Neumann, Dez 2010 package req nx nx::Object create output { set :state "" set :text "" :public object method line {kind string} { if {${:state} ne $kind} { if {${:state} ne ""} {:flush} set :state $kind set :text "" } append :text $string \n } :public object method flush {} { set trimmed [string trim ${:text} \n] if {$trimmed ne ""} { :${:state} $trimmed } } :public object method postprocess {block} { set result "" set cmd "" foreach l [split $block \n] { append cmd $l \n if {[info complete $cmd]} then { set w0 "" regexp {^\s*(\S+)\s*} $cmd _ w0 #set w0 [lindex $cmd 0] if { ($w0 eq "?" && [llength $cmd] == 3) || ($w0 eq "!" && [llength $cmd] == 2) } { append result "% [lindex $cmd 1]\n" set cmdresult [lindex $cmd 2] if {$cmdresult ne "" && ![string match ::nsf::__* $cmdresult]} {append result $cmdresult \n} } else { append result $cmd } set cmd "" } } return [string trimright $result \n] } :public object method prog {block} { puts $::out {[source,tcl]} puts $::out -------------------------------------------------- puts $::out [:postprocess $block] puts $::out --------------------------------------------------\n } :public object method doc {block} { #puts $::out "=====doc\n$block\n=====" puts $::out $block\n } } #puts stderr "find -L $opt(-path) -type f -name '$opt(-name)'" foreach file $argv { set F [open $file]; set content [read $F]; close $F set outfn [file rootname $file].txt set out [open $outfn w] set title "Listing of $file" regexp {^# = (.+?)\n(.*)$} $content _ title content foreach ignorePattern { "package req nx::test" "package require nx::test" "nx::Test parameter count 1" "proc ! args.*?" } { regsub "$ignorePattern\s?\n" $content "" content } puts $::out "= $title\n" foreach line [split $content \n] { if {[regexp {^# ?(.*)$} $line _ comment]} { output line doc $comment } else { output line prog $line } } output flush }apps/utils/source-highlight-with-pp000077500000000000000000000001471242365656200177570ustar00rootroot00000000000000#!/usr/bin/env tclsh package req nx::pp lappend argv [read stdin] puts stdout [nx::pp render {*}$argv]autoclean.sh000066400000000000000000000001451242365656200134000ustar00rootroot00000000000000#!/bin/sh make distclean for configscript in `find . -name configure` do rm -f $configscript doneautogen.sh000066400000000000000000000001341242365656200130650ustar00rootroot00000000000000#!/bin/sh for pdir in `find . -name configure.in` do (cd `dirname $pdir`; autoconf) done config/000077500000000000000000000000001242365656200123365ustar00rootroot00000000000000config/install-sh000077500000000000000000000330541242365656200143470ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2011-04-20.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -S $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -S) stripcmd="$stripprog $2" shift;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: configure000077500000000000000000010626101242365656200130060ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for nsf 2.0.0. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: xotcl@alice.wu-wien.ac.at about your system, including $0: any error possibly output before this message. Then $0: install a modern shell, or manually run the script $0: under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='nsf' PACKAGE_TARNAME='nsf' PACKAGE_VERSION='2.0.0' PACKAGE_STRING='nsf 2.0.0' PACKAGE_BUGREPORT='xotcl@alice.wu-wien.ac.at' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS CONFIG_CLEAN_FILES TEA_PLATFORM apps_actiweb libdirs_actiweb test_actiweb NSF_INCLUDE_DIR NSF_BUILD_INCLUDE_SPEC NSF_BUILD_INCLUDE_DIR XOWISH NXSH NSF_STUB_LIB_PATH NSF_BUILD_STUB_LIB_PATH NSF_STUB_LIB_SPEC NSF_BUILD_STUB_LIB_SPEC NSF_LIB_SPEC NSF_BUILD_LIB_SPEC UNSHARED_LIB_SUFFIX SHARED_LIB_SUFFIX NSF_PKG_LIBDIR NSF_SRC_DIR TCL_EXEC_PREFIX NSF_COMPATIBLE_TCLSH TCLSH_PROG VC_MANIFEST_EMBED_EXE VC_MANIFEST_EMBED_DLL RANLIB_STUB MAKE_STUB_LIB MAKE_STATIC_LIB MAKE_SHARED_LIB MAKE_LIB TCL_DBGX LDFLAGS_DEFAULT CFLAGS_DEFAULT LD_LIBRARY_PATH_VAR SHLIB_CFLAGS SHLIB_LD_LIBS SHLIB_LD STLIB_LD CFLAGS_WARNING CFLAGS_OPTIMIZE CFLAGS_DEBUG RC CELIB_DIR AR SHARED_BUILD TCL_THREADS TCL_TOP_DIR_NATIVE TCL_INCLUDES PKG_OBJECTS PKG_SOURCES DTRACE_OBJ aol_prefix MATH_LIBS EGREP GREP RANLIB SET_MAKE INSTALL_SCRIPT INSTALL_PROGRAM INSTALL_DATA INSTALL CPP TCL_SHLIB_LD_LIBS TCL_LD_FLAGS TCL_EXTRA_CFLAGS TCL_DEFS TCL_LIBS CLEANFILES OBJEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC TCL_STUB_LIB_SPEC TCL_STUB_LIB_FLAG TCL_STUB_LIB_FILE TCL_LIB_SPEC TCL_LIB_FLAG TCL_LIB_FILE TCL_SRC_DIR TCL_BIN_DIR TCL_PATCH_LEVEL TCL_VERSION NSF_PATCH_LEVEL NSF_MINOR_VERSION NSF_MAJOR_VERSION NSF_VERSION subdirs PKG_CFLAGS PKG_LIBS PKG_INCLUDES PKG_HEADERS PKG_TCL_SOURCES PKG_STUB_OBJECTS PKG_STUB_SOURCES PKG_STUB_LIB_FILE PKG_LIB_FILE EXEEXT CYGPATH target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_aolserver3 with_dtrace with_mongoc with_bson enable_profile enable_memcount enable_development enable_assertions enable_assemble with_tcl with_tclinclude enable_threads enable_shared enable_64bit enable_64bit_vis enable_rpath enable_wince with_celib enable_symbols ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures nsf 2.0.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/nsf] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of nsf 2.0.0:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-profile build nsf with profile support (default: disabled) --enable-memcount=yes|trace build nsf with memcount debug support (default: disabled) --enable-development build nsf with development support (intensive runtime checking, etc.; default: disabled) --enable-assertions build nsf with assertion support (default: enabled) --enable-assemble=yes|label|call build nsf with assemble support (default: disabled) --enable-threads build with threads --enable-shared build and link with shared libraries (default: on) --enable-64bit enable 64bit support (default: off) --enable-64bit-vis enable 64bit Sparc VIS support (default: off) --disable-rpath disable rpath support (default: on) --enable-wince enable Win/CE support (where applicable) --enable-symbols build with debugging symbols (default: off) Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-aolserver3=AOL_SERVER_DIR build an AOLserver 3 module; point to directory containing aolsever/include (default: off) --with-dtrace build nsf with dtrace (default: without) --with-mongoc=MONGOC_INCLUDE_DIR,MONGOC_LIB_DIR build nsf with mongodb c-driver support (default: without) --with-bson=BSON_INCLUDE_DIR,BSON_LIB_DIR build nsf with mongodb bson support (default: without) --with-tcl directory containing tcl configuration (tclConfig.sh) --with-tclinclude directory containing the public Tcl header files --with-celib=DIR use Windows/CE support library from DIR Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF nsf configure 2.0.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ---------------------------------------- ## ## Report this to xotcl@alice.wu-wien.ac.at ## ## ---------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by nsf $as_me 2.0.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in tclconfig "$srcdir"/tclconfig; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in tclconfig \"$srcdir\"/tclconfig" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. # This will define a ${TEA_PLATFORM} variable == "unix" or "windows". #-------------------------------------------------------------------- # TEA extensions pass this us the version of TEA they think they # are compatible with. TEA_VERSION="3.9" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for correct TEA configuration" >&5 $as_echo_n "checking for correct TEA configuration... " >&6; } if test x"${PACKAGE_NAME}" = x ; then as_fn_error $? " The PACKAGE_NAME variable must be defined by your TEA configure.in" "$LINENO" 5 fi if test x"3.9" = x ; then as_fn_error $? " TEA version not specified." "$LINENO" 5 elif test "3.9" != "${TEA_VERSION}" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: warning: requested TEA version \"3.9\", have \"${TEA_VERSION}\"" >&5 $as_echo "warning: requested TEA version \"3.9\", have \"${TEA_VERSION}\"" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok (TEA ${TEA_VERSION})" >&5 $as_echo "ok (TEA ${TEA_VERSION})" >&6; } fi # If the user did not set CFLAGS, set it now to keep macros # like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2". if test "${CFLAGS+set}" != "set" ; then CFLAGS="" fi case "`uname -s`" in *win32*|*WIN32*|*MINGW32_*) # Extract the first word of "cygpath", so it can be a program name with args. set dummy cygpath; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CYGPATH+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CYGPATH"; then ac_cv_prog_CYGPATH="$CYGPATH" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CYGPATH="cygpath -w" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_CYGPATH" && ac_cv_prog_CYGPATH="echo" fi fi CYGPATH=$ac_cv_prog_CYGPATH if test -n "$CYGPATH"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CYGPATH" >&5 $as_echo "$CYGPATH" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi EXEEXT=".exe" TEA_PLATFORM="windows" ;; *CYGWIN_*) CYGPATH=echo EXEEXT=".exe" # TEA_PLATFORM is determined later in LOAD_TCLCONFIG ;; *) CYGPATH=echo # Maybe we are cross-compiling.... case ${host_alias} in *mingw32*) EXEEXT=".exe" TEA_PLATFORM="windows" ;; *) EXEEXT="" TEA_PLATFORM="unix" ;; esac ;; esac # Check if exec_prefix is set. If not use fall back to prefix. # Note when adjusted, so that TEA_PREFIX can correct for this. # This is needed for recursive configures, since autoconf propagates # $prefix, but not $exec_prefix (doh!). if test x$exec_prefix = xNONE ; then exec_prefix_default=yes exec_prefix=$prefix fi { $as_echo "$as_me:${as_lineno-$LINENO}: configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}" >&5 $as_echo "$as_me: configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}" >&6;} # This package name must be replaced statically for AC_SUBST to work # Substitute STUB_LIB_FILE in case package creates a stub library too. # We AC_SUBST these here to ensure they are subst'ed, # in case the user doesn't call TEA_ADD_... #-------------------------------------------------------------------- # specify some extra flags #-------------------------------------------------------------------- # According to http://www.gnu.org/software/autoconf/manual/autoconf.html # - "with" is for external software (optional packages) # - "enable" is for optional features # # Check whether --with-aolserver3 was given. if test "${with_aolserver3+set}" = set; then : withval=$with_aolserver3; with_aol3=$withval else with_aol3=no fi # Check whether --with-dtrace was given. if test "${with_dtrace+set}" = set; then : withval=$with_dtrace; with_dtrace=$withval else with_dtrace=no fi # Check whether --with-mongoc was given. if test "${with_mongoc+set}" = set; then : withval=$with_mongoc; with_mongoc=$withval else with_mongoc=no fi # Check whether --with-bson was given. if test "${with_bson+set}" = set; then : withval=$with_bson; with_bson=$withval else with_bson=no fi # Check whether --enable-profile was given. if test "${enable_profile+set}" = set; then : enableval=$enable_profile; enable_profile=$enableval else enable_profile=no fi # Check whether --enable-memcount was given. if test "${enable_memcount+set}" = set; then : enableval=$enable_memcount; enable_memcount=$enableval else enable_memcount=no fi # Check whether --enable-development was given. if test "${enable_development+set}" = set; then : enableval=$enable_development; enable_development=$enableval else enable_development=no fi # Check whether --enable-assertions was given. if test "${enable_assertions+set}" = set; then : enableval=$enable_assertions; enable_assertions=$enableval else enable_assertions=yes fi # Check whether --enable-assemble was given. if test "${enable_assemble+set}" = set; then : enableval=$enable_assemble; enable_assemble=$enableval else enable_assemble=no fi subdirs="" if ! test "$with_mongoc" = no; then test_mongodb=test-mongdb subdirs="$subdirs library/mongodb" fi test_actiweb="" libdirs_actiweb="" apps_actiweb="" #-------------------------------------------------------------------- # __CHANGE__ # Set your package name and version numbers here. The NODOT_VERSION is # required for constructing the library name on systems that don't like # dots in library names (Windows). The VERSION variable is used on the # other systems. #-------------------------------------------------------------------- NSF_MAJOR_VERSION=2 NSF_MINOR_VERSION=0 NSF_PATCH_LEVEL=$PACKAGE_VERSION NSF_VERSION=${NSF_MAJOR_VERSION}.${NSF_MINOR_VERSION} NODOT_VERSION=${NSF_MAJOR_VERSION}${NSF_MINOR_VERSION} echo "Configuring next Version $PACKAGE_VERSION" #-------------------------------------------------------------------- # Load the tclConfig.sh file #-------------------------------------------------------------------- # # Ok, lets find the tcl configuration # First, look for one uninstalled. # the alternative search directory is invoked by --with-tcl # if test x"${no_tcl}" = x ; then # we reset no_tcl in case something fails here no_tcl=true # Check whether --with-tcl was given. if test "${with_tcl+set}" = set; then : withval=$with_tcl; with_tclconfig="${withval}" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl configuration" >&5 $as_echo_n "checking for Tcl configuration... " >&6; } if ${ac_cv_c_tclconfig+:} false; then : $as_echo_n "(cached) " >&6 else # First check to see if --with-tcl was specified. if test x"${with_tclconfig}" != x ; then case "${with_tclconfig}" in */tclConfig.sh ) if test -f "${with_tclconfig}"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself" >&5 $as_echo "$as_me: WARNING: --with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself" >&2;} with_tclconfig="`echo "${with_tclconfig}" | sed 's!/tclConfig\.sh$!!'`" fi ;; esac if test -f "${with_tclconfig}/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd "${with_tclconfig}"; pwd)`" else as_fn_error $? "${with_tclconfig} directory doesn't contain tclConfig.sh" "$LINENO" 5 fi fi # then check for a private Tcl installation if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ ../tcl \ `ls -dr ../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ../tcl[8-9].[0-9]* 2>/dev/null` \ ../../tcl \ `ls -dr ../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ../../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ../../tcl[8-9].[0-9]* 2>/dev/null` \ ../../../tcl \ `ls -dr ../../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ../../../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ../../../tcl[8-9].[0-9]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/unix; pwd)`" break fi done fi # on Darwin, check in Framework installation locations if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d ~/Library/Frameworks 2>/dev/null` \ `ls -d /Library/Frameworks 2>/dev/null` \ `ls -d /Network/Library/Frameworks 2>/dev/null` \ `ls -d /System/Library/Frameworks 2>/dev/null` \ ; do if test -f "$i/Tcl.framework/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/Tcl.framework; pwd)`" break fi done fi # TEA specific: on Windows, check in common installation locations if test "${TEA_PLATFORM}" = "windows" \ -a x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d C:/Tcl/lib 2>/dev/null` \ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \ ; do if test -f "$i/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i; pwd)`" break fi done fi # check in a few common install locations if test x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d ${libdir} 2>/dev/null` \ `ls -d ${exec_prefix}/lib 2>/dev/null` \ `ls -d ${prefix}/lib 2>/dev/null` \ `ls -d /usr/local/lib 2>/dev/null` \ `ls -d /usr/contrib/lib 2>/dev/null` \ `ls -d /usr/lib 2>/dev/null` \ `ls -d /usr/lib64 2>/dev/null` \ `ls -d /usr/lib/tcl8.6 2>/dev/null` \ `ls -d /usr/lib/tcl8.5 2>/dev/null` \ ; do if test -f "$i/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i; pwd)`" break fi done fi # check in a few other private locations if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ ${srcdir}/../tcl \ `ls -dr ${srcdir}/../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ${srcdir}/../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ${srcdir}/../tcl[8-9].[0-9]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/unix; pwd)`" break fi done fi fi if test x"${ac_cv_c_tclconfig}" = x ; then TCL_BIN_DIR="# no Tcl configs found" as_fn_error $? "Can't find Tcl configuration definitions. Use --with-tcl to specify a directory containing tclConfig.sh" "$LINENO" 5 else no_tcl= TCL_BIN_DIR="${ac_cv_c_tclconfig}" { $as_echo "$as_me:${as_lineno-$LINENO}: result: found ${TCL_BIN_DIR}/tclConfig.sh" >&5 $as_echo "found ${TCL_BIN_DIR}/tclConfig.sh" >&6; } fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for existence of ${TCL_BIN_DIR}/tclConfig.sh" >&5 $as_echo_n "checking for existence of ${TCL_BIN_DIR}/tclConfig.sh... " >&6; } if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: loading" >&5 $as_echo "loading" >&6; } . "${TCL_BIN_DIR}/tclConfig.sh" else { $as_echo "$as_me:${as_lineno-$LINENO}: result: could not find ${TCL_BIN_DIR}/tclConfig.sh" >&5 $as_echo "could not find ${TCL_BIN_DIR}/tclConfig.sh" >&6; } fi # eval is required to do the TCL_DBGX substitution eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\"" eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\"" # If the TCL_BIN_DIR is the build directory (not the install directory), # then set the common variable name to the value of the build variables. # For example, the variable TCL_LIB_SPEC will be set to the value # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC # instead of TCL_BUILD_LIB_SPEC since it will work with both an # installed and uninstalled version of Tcl. if test -f "${TCL_BIN_DIR}/Makefile" ; then TCL_LIB_SPEC="${TCL_BUILD_LIB_SPEC}" TCL_STUB_LIB_SPEC="${TCL_BUILD_STUB_LIB_SPEC}" TCL_STUB_LIB_PATH="${TCL_BUILD_STUB_LIB_PATH}" elif test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use the libraries # from the framework at the given location so that linking works # against Tcl.framework installed in an arbitrary location. case ${TCL_DEFS} in *TCL_FRAMEWORK*) if test -f "${TCL_BIN_DIR}/${TCL_LIB_FILE}"; then for i in "`cd "${TCL_BIN_DIR}"; pwd`" \ "`cd "${TCL_BIN_DIR}"/../..; pwd`"; do if test "`basename "$i"`" = "${TCL_LIB_FILE}.framework"; then TCL_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TCL_LIB_FILE}" break fi done fi if test -f "${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"; then TCL_STUB_LIB_SPEC="-L`echo "${TCL_BIN_DIR}" | sed -e 's/ /\\\\ /g'` ${TCL_STUB_LIB_FLAG}" TCL_STUB_LIB_PATH="${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}" fi ;; esac fi # eval is required to do the TCL_DBGX substitution eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\"" eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\"" eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\"" eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\"" { $as_echo "$as_me:${as_lineno-$LINENO}: checking platform" >&5 $as_echo_n "checking platform... " >&6; } hold_cc=$CC; CC="$TCL_CC" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifdef _WIN32 #error win32 #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : TEA_PLATFORM="unix" else TEA_PLATFORM="windows" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CC=$hold_cc { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEA_PLATFORM" >&5 $as_echo "$TEA_PLATFORM" >&6; } # The BUILD_$pkg is to define the correct extern storage class # handling when making this package cat >>confdefs.h <<_ACEOF #define BUILD_${PACKAGE_NAME} /**/ _ACEOF # Do this here as we have fully defined TEA_PLATFORM now if test "${TEA_PLATFORM}" = "windows" ; then EXEEXT=".exe" CLEANFILES="$CLEANFILES *.lib *.dll *.pdb *.exp" fi # TEA specific: for ac_func in strnstr do : ac_fn_c_check_func "$LINENO" "strnstr" "ac_cv_func_strnstr" if test "x$ac_cv_func_strnstr" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRNSTR 1 _ACEOF fi done #-------------------------------------------------------------------- # check for TK #-------------------------------------------------------------------- #-------------------------------------------------------------------- # Handle the --prefix=... option by defaulting to what Tcl gave. # Must be called after TEA_LOAD_TCLCONFIG and before TEA_SETUP_COMPILER. #----------------------------------------------------------------------- if test "${prefix}" = "NONE"; then prefix_default=yes if test x"${TCL_PREFIX}" != x; then { $as_echo "$as_me:${as_lineno-$LINENO}: --prefix defaulting to TCL_PREFIX ${TCL_PREFIX}" >&5 $as_echo "$as_me: --prefix defaulting to TCL_PREFIX ${TCL_PREFIX}" >&6;} prefix=${TCL_PREFIX} else { $as_echo "$as_me:${as_lineno-$LINENO}: --prefix defaulting to /usr/local" >&5 $as_echo "$as_me: --prefix defaulting to /usr/local" >&6;} prefix=/usr/local fi fi if test "${exec_prefix}" = "NONE" -a x"${prefix_default}" = x"yes" \ -o x"${exec_prefix_default}" = x"yes" ; then if test x"${TCL_EXEC_PREFIX}" != x; then { $as_echo "$as_me:${as_lineno-$LINENO}: --exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}" >&5 $as_echo "$as_me: --exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}" >&6;} exec_prefix=${TCL_EXEC_PREFIX} else { $as_echo "$as_me:${as_lineno-$LINENO}: --exec-prefix defaulting to ${prefix}" >&5 $as_echo "$as_me: --exec-prefix defaulting to ${prefix}" >&6;} exec_prefix=$prefix fi fi #----------------------------------------------------------------------- # Standard compiler checks. # This sets up CC by using the CC env var, or looks for gcc otherwise. # This also calls AC_PROG_CC, AC_PROG_INSTALL and a few others to create # the basic setup necessary to compile executables. #----------------------------------------------------------------------- # Don't put any macros that use the compiler (e.g. AC_TRY_COMPILE) # in this macro, they need to go into TEA_SETUP_COMPILER instead. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu INSTALL="\$(SHELL) \$(srcdir)/tclconfig/install-sh -c" INSTALL_DATA="\${INSTALL} -m 644" INSTALL_PROGRAM="\${INSTALL}" INSTALL_SCRIPT="\${INSTALL}" #-------------------------------------------------------------------- # Checks to see if the make program sets the $MAKE variable. #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi #-------------------------------------------------------------------- # Find ranlib #-------------------------------------------------------------------- if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi #-------------------------------------------------------------------- # Determines the correct binary file extension (.o, .obj, .exe etc.) #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # Any macros that use the compiler (e.g. AC_TRY_COMPILE) have to go here. #------------------------------------------------------------------------ # If we're using GCC, see if the compiler understands -pipe. If so, use it. # It makes compiling go faster. (This is only a performance feature.) #------------------------------------------------------------------------ if test -z "$no_pipe" -a -n "$GCC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the compiler understands -pipe" >&5 $as_echo_n "checking if the compiler understands -pipe... " >&6; } if ${tcl_cv_cc_pipe+:} false; then : $as_echo_n "(cached) " >&6 else hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -pipe" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_cc_pipe=yes else tcl_cv_cc_pipe=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$hold_cflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_pipe" >&5 $as_echo "$tcl_cv_cc_pipe" >&6; } if test $tcl_cv_cc_pipe = yes; then CFLAGS="$CFLAGS -pipe" fi fi #-------------------------------------------------------------------- # Common compiler flag setup #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac if test "${TEA_PLATFORM}" = "unix" ; then #-------------------------------------------------------------------- # On a few very rare systems, all of the libm.a stuff is # already in libc.a. Set compiler flags accordingly. # Also, Linux requires the "ieee" library for math to work # right (and it must appear before "-lm"). #-------------------------------------------------------------------- ac_fn_c_check_func "$LINENO" "sin" "ac_cv_func_sin" if test "x$ac_cv_func_sin" = xyes; then : MATH_LIBS="" else MATH_LIBS="-lm" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lieee" >&5 $as_echo_n "checking for main in -lieee... " >&6; } if ${ac_cv_lib_ieee_main+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lieee $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ieee_main=yes else ac_cv_lib_ieee_main=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ieee_main" >&5 $as_echo "$ac_cv_lib_ieee_main" >&6; } if test "x$ac_cv_lib_ieee_main" = xyes; then : MATH_LIBS="-lieee $MATH_LIBS" fi #-------------------------------------------------------------------- # Interactive UNIX requires -linet instead of -lsocket, plus it # needs net/errno.h to define the socket-related error codes. #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -linet" >&5 $as_echo_n "checking for main in -linet... " >&6; } if ${ac_cv_lib_inet_main+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-linet $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_inet_main=yes else ac_cv_lib_inet_main=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_inet_main" >&5 $as_echo "$ac_cv_lib_inet_main" >&6; } if test "x$ac_cv_lib_inet_main" = xyes; then : LIBS="$LIBS -linet" fi ac_fn_c_check_header_mongrel "$LINENO" "net/errno.h" "ac_cv_header_net_errno_h" "$ac_includes_default" if test "x$ac_cv_header_net_errno_h" = xyes; then : $as_echo "#define HAVE_NET_ERRNO_H 1" >>confdefs.h fi #-------------------------------------------------------------------- # Check for the existence of the -lsocket and -lnsl libraries. # The order here is important, so that they end up in the right # order in the command line generated by make. Here are some # special considerations: # 1. Use "connect" and "accept" to check for -lsocket, and # "gethostbyname" to check for -lnsl. # 2. Use each function name only once: can't redo a check because # autoconf caches the results of the last check and won't redo it. # 3. Use -lnsl and -lsocket only if they supply procedures that # aren't already present in the normal libraries. This is because # IRIX 5.2 has libraries, but they aren't needed and they're # bogus: they goof up name resolution if used. # 4. On some SVR4 systems, can't use -lsocket without -lnsl too. # To get around this problem, check for both libraries together # if -lsocket doesn't work by itself. #-------------------------------------------------------------------- tcl_checkBoth=0 ac_fn_c_check_func "$LINENO" "connect" "ac_cv_func_connect" if test "x$ac_cv_func_connect" = xyes; then : tcl_checkSocket=0 else tcl_checkSocket=1 fi if test "$tcl_checkSocket" = 1; then ac_fn_c_check_func "$LINENO" "setsockopt" "ac_cv_func_setsockopt" if test "x$ac_cv_func_setsockopt" = xyes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setsockopt in -lsocket" >&5 $as_echo_n "checking for setsockopt in -lsocket... " >&6; } if ${ac_cv_lib_socket_setsockopt+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char setsockopt (); int main () { return setsockopt (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_socket_setsockopt=yes else ac_cv_lib_socket_setsockopt=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_setsockopt" >&5 $as_echo "$ac_cv_lib_socket_setsockopt" >&6; } if test "x$ac_cv_lib_socket_setsockopt" = xyes; then : LIBS="$LIBS -lsocket" else tcl_checkBoth=1 fi fi fi if test "$tcl_checkBoth" = 1; then tk_oldLibs=$LIBS LIBS="$LIBS -lsocket -lnsl" ac_fn_c_check_func "$LINENO" "accept" "ac_cv_func_accept" if test "x$ac_cv_func_accept" = xyes; then : tcl_checkNsl=0 else LIBS=$tk_oldLibs fi fi ac_fn_c_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname" if test "x$ac_cv_func_gethostbyname" = xyes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5 $as_echo_n "checking for gethostbyname in -lnsl... " >&6; } if ${ac_cv_lib_nsl_gethostbyname+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lnsl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char gethostbyname (); int main () { return gethostbyname (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_nsl_gethostbyname=yes else ac_cv_lib_nsl_gethostbyname=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_gethostbyname" >&5 $as_echo "$ac_cv_lib_nsl_gethostbyname" >&6; } if test "x$ac_cv_lib_nsl_gethostbyname" = xyes; then : LIBS="$LIBS -lnsl" fi fi # TEA specific: Don't perform the eval of the libraries here because # DL_LIBS won't be set until we call TEA_CONFIG_CFLAGS TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}' { $as_echo "$as_me:${as_lineno-$LINENO}: checking dirent.h" >&5 $as_echo_n "checking dirent.h... " >&6; } if ${tcl_cv_dirent_h+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #ifndef _POSIX_SOURCE # ifdef __Lynx__ /* * Generate compilation error to make the test fail: Lynx headers * are only valid if really in the POSIX environment. */ missing_procedure(); # endif #endif DIR *d; struct dirent *entryPtr; char *p; d = opendir("foobar"); entryPtr = readdir(d); p = entryPtr->d_name; closedir(d); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_dirent_h=yes else tcl_cv_dirent_h=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_dirent_h" >&5 $as_echo "$tcl_cv_dirent_h" >&6; } if test $tcl_cv_dirent_h = no; then $as_echo "#define NO_DIRENT_H 1" >>confdefs.h fi # TEA specific: ac_fn_c_check_header_mongrel "$LINENO" "errno.h" "ac_cv_header_errno_h" "$ac_includes_default" if test "x$ac_cv_header_errno_h" = xyes; then : else $as_echo "#define NO_ERRNO_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "float.h" "ac_cv_header_float_h" "$ac_includes_default" if test "x$ac_cv_header_float_h" = xyes; then : else $as_echo "#define NO_FLOAT_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "values.h" "ac_cv_header_values_h" "$ac_includes_default" if test "x$ac_cv_header_values_h" = xyes; then : else $as_echo "#define NO_VALUES_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default" if test "x$ac_cv_header_limits_h" = xyes; then : $as_echo "#define HAVE_LIMITS_H 1" >>confdefs.h else $as_echo "#define NO_LIMITS_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" if test "x$ac_cv_header_stdlib_h" = xyes; then : tcl_ok=1 else tcl_ok=0 fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "strtol" >/dev/null 2>&1; then : else tcl_ok=0 fi rm -f conftest* cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "strtoul" >/dev/null 2>&1; then : else tcl_ok=0 fi rm -f conftest* cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "strtod" >/dev/null 2>&1; then : else tcl_ok=0 fi rm -f conftest* if test $tcl_ok = 0; then $as_echo "#define NO_STDLIB_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default" if test "x$ac_cv_header_string_h" = xyes; then : tcl_ok=1 else tcl_ok=0 fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "strstr" >/dev/null 2>&1; then : else tcl_ok=0 fi rm -f conftest* cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "strerror" >/dev/null 2>&1; then : else tcl_ok=0 fi rm -f conftest* # See also memmove check below for a place where NO_STRING_H can be # set and why. if test $tcl_ok = 0; then $as_echo "#define NO_STRING_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "sys/wait.h" "ac_cv_header_sys_wait_h" "$ac_includes_default" if test "x$ac_cv_header_sys_wait_h" = xyes; then : else $as_echo "#define NO_SYS_WAIT_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default" if test "x$ac_cv_header_dlfcn_h" = xyes; then : else $as_echo "#define NO_DLFCN_H 1" >>confdefs.h fi # OS/390 lacks sys/param.h (and doesn't need it, by chance). for ac_header in sys/param.h do : ac_fn_c_check_header_mongrel "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default" if test "x$ac_cv_header_sys_param_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SYS_PARAM_H 1 _ACEOF fi done # Let the user call this, because if it triggers, they will # need a compat/strtod.c that is correct. Users can also # use Tcl_GetDouble(FromObj) instead. #TEA_BUGGY_STRTOD fi #-------------------------------------------------------------------- # check for extra flags # # FORCE_NO_STUBS is deactivated for now if ! test "$with_aol3" = "no"; then echo "Pre-configuring AOL-Server (nsf)" AOL_DEFINES="-DAOL_SERVER -DUSE_TCL8X -I$with_aol3/include -I$TCL_SRC_DIR/generic" FORCE_NO_STUBS=1 else AOL_DEFINES="" FORCE_NO_STUBS=0 fi # set the default aol_prefix aol_prefix="/usr/local/aolserver" # if we have under ${prefix} either modules/tcl/init.tcl (aolserver # style) or modules/tcl/init.tcl (naviserver style), then use # ${prefix} as aol_prefix (path for the installation of # aolserver/naviserver specific tcl files). if test -f "${prefix}/modules/tcl/init.tcl" ; then aol_prefix="${prefix}" else if test -f "${prefix}/tcl/init.tcl" ; then aol_prefix="${prefix}" fi fi #-------------------------------------------------------------------- # check for tclCompile.h (needed, when compiled without full source) if test -f "$TCL_SRC_DIR/generic/tclCompile.h"; then $as_echo "#define HAVE_TCL_COMPILE_H 1" >>confdefs.h fi if test "$enable_assertions" = yes; then $as_echo "#define NSF_WITH_ASSERTIONS 1" >>confdefs.h fi if test "$enable_profile" = yes; then $as_echo "#define NSF_PROFILE 1" >>confdefs.h fi if test "$enable_development" = yes; then $as_echo "#define NSF_DEVELOPMENT 1" >>confdefs.h fi if test "$enable_memcount" = yes; then $as_echo "#define NSF_MEM_COUNT 1" >>confdefs.h fi if test "$enable_memcount" = trace; then $as_echo "#define NSF_MEM_TRACE 1" >>confdefs.h fi if test "$enable_assemble" = yes; then $as_echo "#define NSF_ASSEMBLE 1" >>confdefs.h fi if test "$enable_assemble" = call; then $as_echo "#define NSF_ASSEMBLE_CT 1" >>confdefs.h fi if test "$enable_assemble" = call; then $as_echo "#define NSF_ASSEMBLE_LT 1" >>confdefs.h fi DTRACE_OBJ= if test "$with_dtrace" = yes; then $as_echo "#define NSF_DTRACE 1" >>confdefs.h # Under Mac OS X, we need no nsfDTrace.o if test "`uname -s`" != "Darwin" ; then DTRACE_OBJ=nsfDTrace.o fi fi #----------------------------------------------------------------------- # __CHANGE__ # Specify the C source files to compile in TEA_ADD_SOURCES, # public headers that need to be installed in TEA_ADD_HEADERS, # stub library C source files to compile in TEA_ADD_STUB_SOURCES, # and runtime Tcl library files in TEA_ADD_TCL_SOURCES. # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS # and PKG_TCL_SOURCES. #----------------------------------------------------------------------- stubdir=stubs${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION} vars="nsf.c nsfError.c nsfObjectData.c nsfProfile.c \ nsfDebug.c nsfUtil.c nsfObj.c nsfPointer.c nsfEnumerationType.c \ nsfCmdDefinitions.c nsfShadow.c nsfCompile.c aolstub.c \${srcdir}/generic/${stubdir}/nsfStubInit.${OBJEXT}" for i in $vars; do case $i in \$*) # allow $-var names PKG_SOURCES="$PKG_SOURCES $i" PKG_OBJECTS="$PKG_OBJECTS $i" ;; *) # check for existence - allows for generic/win/unix VPATH # To add more dirs here (like 'src'), you have to update VPATH # in Makefile.in as well if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \ -a ! -f "${srcdir}/macosx/$i" \ ; then as_fn_error $? "could not find source file '$i'" "$LINENO" 5 fi PKG_SOURCES="$PKG_SOURCES $i" # this assumes it is in a VPATH dir i=`basename $i` # handle user calling this before or after TEA_SETUP_COMPILER if test x"${OBJEXT}" != x ; then j="`echo $i | sed -e 's/\.[^.]*$//'`.${OBJEXT}" else j="`echo $i | sed -e 's/\.[^.]*$//'`.\${OBJEXT}" fi PKG_OBJECTS="$PKG_OBJECTS $j" ;; esac done vars="generic/nsf.h generic/nsfInt.h generic/${stubdir}/nsfDecls.h generic/${stubdir}/nsfIntDecls.h" for i in $vars; do # check for existence, be strict because it is installed if test ! -f "${srcdir}/$i" ; then as_fn_error $? "could not find header file '${srcdir}/$i'" "$LINENO" 5 fi PKG_HEADERS="$PKG_HEADERS $i" done vars="" for i in $vars; do PKG_INCLUDES="$PKG_INCLUDES $i" done vars="" for i in $vars; do if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then # Convert foo.lib to -lfoo for GCC. No-op if not *.lib i=`echo "$i" | sed -e 's/^\([^-].*\)\.lib$/-l\1/i'` fi PKG_LIBS="$PKG_LIBS $i" done PKG_CFLAGS="$PKG_CFLAGS -DNSF_VERSION=\\\"$NSF_VERSION\\\" -DNSF_PATCHLEVEL=\\\"$NSF_PATCH_LEVEL\\\" \ $AOL_DEFINES " vars="nsfStubLib.c" for i in $vars; do # check for existence - allows for generic/win/unix VPATH if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \ -a ! -f "${srcdir}/macosx/$i" \ ; then as_fn_error $? "could not find stub source file '$i'" "$LINENO" 5 fi PKG_STUB_SOURCES="$PKG_STUB_SOURCES $i" # this assumes it is in a VPATH dir i=`basename $i` # handle user calling this before or after TEA_SETUP_COMPILER if test x"${OBJEXT}" != x ; then j="`echo $i | sed -e 's/\.[^.]*$//'`.${OBJEXT}" else j="`echo $i | sed -e 's/\.[^.]*$//'`.\${OBJEXT}" fi PKG_STUB_OBJECTS="$PKG_STUB_OBJECTS $j" done vars="" for i in $vars; do # check for existence, be strict because it is installed if test ! -f "${srcdir}/$i" ; then as_fn_error $? "could not find tcl source file '${srcdir}/$i'" "$LINENO" 5 fi PKG_TCL_SOURCES="$PKG_TCL_SOURCES $i" done #-------------------------------------------------------------------- # __CHANGE__ # # You can add more files to clean if your extension creates any extra # files by extending CLEANFILES. # Add pkgIndex.tcl if it is generated in the Makefile instead of ./configure # and change Makefile.in to move it from CONFIG_CLEAN_FILES to BINARIES var. # # A few miscellaneous platform-specific items: # TEA_ADD_* any platform specific compiler/build info here. #-------------------------------------------------------------------- if test "${TEA_PLATFORM}" = "windows" ; then if test "$GCC" != "yes" ; then $as_echo "#define VISUAL_CC 1" >>confdefs.h fi CLEANFILES="*.lib *.dll *.exp *.ilk *.pdb vc50.pch vc60.pch " #TEA_ADD_SOURCES([win/winFile.c]) #TEA_ADD_INCLUDES([-I\"$(${CYGPATH} ${srcdir}/win)\"]) else CLEANFILES="*.a *.so *~ core gmon.out" #TEA_ADD_SOURCES([unix/unixFile.c]) #TEA_ADD_LIBS([-lsuperfly]) fi CLEANFILES="$CLEANFILES *.${OBJEXT} pkgIndex.tcl" #-------------------------------------------------------------------- # __CHANGE__ # Choose which headers you need. Extension authors should try very # hard to only rely on the Tcl public header files. Internal headers # contain private data structures and are subject to change without # notice. # This must be done AFTER calling TEA_PATH_TCLCONFIG/TEA_LOAD_TCLCONFIG # so that we can extract TCL_SRC_DIR from the config file (in the case # of private headers #-------------------------------------------------------------------- #TEA_PUBLIC_TCL_HEADERS { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl public headers" >&5 $as_echo_n "checking for Tcl public headers... " >&6; } # Check whether --with-tclinclude was given. if test "${with_tclinclude+set}" = set; then : withval=$with_tclinclude; with_tclinclude=${withval} fi if ${ac_cv_c_tclh+:} false; then : $as_echo_n "(cached) " >&6 else # Use the value from --with-tclinclude, if it was given if test x"${with_tclinclude}" != x ; then if test -f "${with_tclinclude}/tcl.h" ; then ac_cv_c_tclh=${with_tclinclude} else as_fn_error $? "${with_tclinclude} directory does not contain tcl.h" "$LINENO" 5 fi else list="" if test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use # the framework's Headers directory case ${TCL_DEFS} in *TCL_FRAMEWORK*) list="`ls -d ${TCL_BIN_DIR}/Headers 2>/dev/null`" ;; esac fi # Look in the source dir only if Tcl is not installed, # and in that situation, look there before installed locations. if test -f "${TCL_BIN_DIR}/Makefile" ; then list="$list `ls -d ${TCL_SRC_DIR}/generic 2>/dev/null`" fi # Check order: pkg --prefix location, Tcl's --prefix location, # relative to directory of tclConfig.sh. eval "temp_includedir=${includedir}" list="$list \ `ls -d ${temp_includedir} 2>/dev/null` \ `ls -d ${TCL_PREFIX}/include 2>/dev/null` \ `ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`" if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then list="$list /usr/local/include /usr/include" if test x"${TCL_INCLUDE_SPEC}" != x ; then d=`echo "${TCL_INCLUDE_SPEC}" | sed -e 's/^-I//'` list="$list `ls -d ${d} 2>/dev/null`" fi fi for i in $list ; do if test -f "$i/tcl.h" ; then ac_cv_c_tclh=$i break fi done fi fi # Print a message based on how we determined the include path if test x"${ac_cv_c_tclh}" = x ; then as_fn_error $? "tcl.h not found. Please specify its location with --with-tclinclude" "$LINENO" 5 else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${ac_cv_c_tclh}" >&5 $as_echo "${ac_cv_c_tclh}" >&6; } fi # Convert to a native path and substitute into the output files. INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tclh}` TCL_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\" # Allow for --with-tclinclude to take effect and define ${ac_cv_c_tclh} { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl private include files" >&5 $as_echo_n "checking for Tcl private include files... " >&6; } TCL_SRC_DIR_NATIVE=`${CYGPATH} ${TCL_SRC_DIR}` TCL_TOP_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}\" # Check to see if tclPort.h isn't already with the public headers # Don't look for tclInt.h because that resides with tcl.h in the core # sources, but the Port headers are in a different directory if test "${TEA_PLATFORM}" = "windows" -a \ -f "${ac_cv_c_tclh}/tclWinPort.h"; then result="private headers found with public headers" elif test "${TEA_PLATFORM}" = "unix" -a \ -f "${ac_cv_c_tclh}/tclUnixPort.h"; then result="private headers found with public headers" else TCL_GENERIC_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/generic\" if test "${TEA_PLATFORM}" = "windows"; then TCL_PLATFORM_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/win\" else TCL_PLATFORM_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/unix\" fi # Overwrite the previous TCL_INCLUDES as this should capture both # public and private headers in the same set. # We want to ensure these are substituted so as not to require # any *_NATIVE vars be defined in the Makefile TCL_INCLUDES="-I${TCL_GENERIC_DIR_NATIVE} -I${TCL_PLATFORM_DIR_NATIVE}" if test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use # the framework's Headers and PrivateHeaders directories case ${TCL_DEFS} in *TCL_FRAMEWORK*) if test -d "${TCL_BIN_DIR}/Headers" -a \ -d "${TCL_BIN_DIR}/PrivateHeaders"; then TCL_INCLUDES="-I\"${TCL_BIN_DIR}/Headers\" -I\"${TCL_BIN_DIR}/PrivateHeaders\" ${TCL_INCLUDES}" else TCL_INCLUDES="${TCL_INCLUDES} ${TCL_INCLUDE_SPEC} `echo "${TCL_INCLUDE_SPEC}" | sed -e 's/Headers/PrivateHeaders/'`" fi ;; esac result="Using ${TCL_INCLUDES}" else if test ! -f "${TCL_SRC_DIR}/generic/tclInt.h" ; then as_fn_error $? "Cannot find private header tclInt.h in ${TCL_SRC_DIR}" "$LINENO" 5 fi result="Using srcdir found in tclConfig.sh: ${TCL_SRC_DIR}" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${result}" >&5 $as_echo "${result}" >&6; } #TEA_PUBLIC_TK_HEADERS #TEA_PRIVATE_TK_HEADERS #TEA_PATH_X #-------------------------------------------------------------------- # Check whether --enable-threads or --disable-threads was given. #-------------------------------------------------------------------- # Check whether --enable-threads was given. if test "${enable_threads+set}" = set; then : enableval=$enable_threads; tcl_ok=$enableval else tcl_ok=yes fi if test "${enable_threads+set}" = set; then enableval="$enable_threads" tcl_ok=$enableval else tcl_ok=yes fi if test "$tcl_ok" = "yes" -o "${TCL_THREADS}" = 1; then TCL_THREADS=1 if test "${TEA_PLATFORM}" != "windows" ; then # We are always OK on Windows, so check what this platform wants: # USE_THREAD_ALLOC tells us to try the special thread-based # allocator that significantly reduces lock contention $as_echo "#define USE_THREAD_ALLOC 1" >>confdefs.h $as_echo "#define _REENTRANT 1" >>confdefs.h if test "`uname -s`" = "SunOS" ; then $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h fi $as_echo "#define _THREAD_SAFE 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lpthread" >&5 $as_echo_n "checking for pthread_mutex_init in -lpthread... " >&6; } if ${ac_cv_lib_pthread_pthread_mutex_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_mutex_init (); int main () { return pthread_mutex_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pthread_pthread_mutex_init=yes else ac_cv_lib_pthread_pthread_mutex_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_mutex_init" >&5 $as_echo "$ac_cv_lib_pthread_pthread_mutex_init" >&6; } if test "x$ac_cv_lib_pthread_pthread_mutex_init" = xyes; then : tcl_ok=yes else tcl_ok=no fi if test "$tcl_ok" = "no"; then # Check a little harder for __pthread_mutex_init in the same # library, as some systems hide it there until pthread.h is # defined. We could alternatively do an AC_TRY_COMPILE with # pthread.h, but that will work with libpthread really doesn't # exist, like AIX 4.2. [Bug: 4359] { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __pthread_mutex_init in -lpthread" >&5 $as_echo_n "checking for __pthread_mutex_init in -lpthread... " >&6; } if ${ac_cv_lib_pthread___pthread_mutex_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char __pthread_mutex_init (); int main () { return __pthread_mutex_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pthread___pthread_mutex_init=yes else ac_cv_lib_pthread___pthread_mutex_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread___pthread_mutex_init" >&5 $as_echo "$ac_cv_lib_pthread___pthread_mutex_init" >&6; } if test "x$ac_cv_lib_pthread___pthread_mutex_init" = xyes; then : tcl_ok=yes else tcl_ok=no fi fi if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -lpthread" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lpthreads" >&5 $as_echo_n "checking for pthread_mutex_init in -lpthreads... " >&6; } if ${ac_cv_lib_pthreads_pthread_mutex_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthreads $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_mutex_init (); int main () { return pthread_mutex_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pthreads_pthread_mutex_init=yes else ac_cv_lib_pthreads_pthread_mutex_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthreads_pthread_mutex_init" >&5 $as_echo "$ac_cv_lib_pthreads_pthread_mutex_init" >&6; } if test "x$ac_cv_lib_pthreads_pthread_mutex_init" = xyes; then : tcl_ok=yes else tcl_ok=no fi if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -lpthreads" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lc" >&5 $as_echo_n "checking for pthread_mutex_init in -lc... " >&6; } if ${ac_cv_lib_c_pthread_mutex_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lc $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_mutex_init (); int main () { return pthread_mutex_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_c_pthread_mutex_init=yes else ac_cv_lib_c_pthread_mutex_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_pthread_mutex_init" >&5 $as_echo "$ac_cv_lib_c_pthread_mutex_init" >&6; } if test "x$ac_cv_lib_c_pthread_mutex_init" = xyes; then : tcl_ok=yes else tcl_ok=no fi if test "$tcl_ok" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lc_r" >&5 $as_echo_n "checking for pthread_mutex_init in -lc_r... " >&6; } if ${ac_cv_lib_c_r_pthread_mutex_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lc_r $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_mutex_init (); int main () { return pthread_mutex_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_c_r_pthread_mutex_init=yes else ac_cv_lib_c_r_pthread_mutex_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_r_pthread_mutex_init" >&5 $as_echo "$ac_cv_lib_c_r_pthread_mutex_init" >&6; } if test "x$ac_cv_lib_c_r_pthread_mutex_init" = xyes; then : tcl_ok=yes else tcl_ok=no fi if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -pthread" else TCL_THREADS=0 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Do not know how to find pthread lib on your system - thread support disabled" >&5 $as_echo "$as_me: WARNING: Do not know how to find pthread lib on your system - thread support disabled" >&2;} fi fi fi fi fi else TCL_THREADS=0 fi # Do checking message here to not mess up interleaved configure output { $as_echo "$as_me:${as_lineno-$LINENO}: checking for building with threads" >&5 $as_echo_n "checking for building with threads... " >&6; } if test "${TCL_THREADS}" = 1; then $as_echo "#define TCL_THREADS 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (default)" >&5 $as_echo "yes (default)" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # TCL_THREADS sanity checking. See if our request for building with # threads is the same as the way Tcl was built. If not, warn the user. case ${TCL_DEFS} in *THREADS=1*) if test "${TCL_THREADS}" = "0"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Building ${PACKAGE_NAME} without threads enabled, but building against Tcl that IS thread-enabled. It is recommended to use --enable-threads." >&5 $as_echo "$as_me: WARNING: Building ${PACKAGE_NAME} without threads enabled, but building against Tcl that IS thread-enabled. It is recommended to use --enable-threads." >&2;} fi ;; *) if test "${TCL_THREADS}" = "1"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --enable-threads requested, but building against a Tcl that is NOT thread-enabled. This is an OK configuration that will also run in a thread-enabled core." >&5 $as_echo "$as_me: WARNING: --enable-threads requested, but building against a Tcl that is NOT thread-enabled. This is an OK configuration that will also run in a thread-enabled core." >&2;} fi ;; esac #-------------------------------------------------------------------- # The statement below defines a collection of symbols related to # building as a shared library instead of a static library. #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to build libraries" >&5 $as_echo_n "checking how to build libraries... " >&6; } # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; tcl_ok=$enableval else tcl_ok=yes fi if test "${enable_shared+set}" = set; then enableval="$enable_shared" tcl_ok=$enableval else tcl_ok=yes fi if test "$tcl_ok" = "yes" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: shared" >&5 $as_echo "shared" >&6; } SHARED_BUILD=1 else { $as_echo "$as_me:${as_lineno-$LINENO}: result: static" >&5 $as_echo "static" >&6; } SHARED_BUILD=0 $as_echo "#define STATIC_BUILD 1" >>confdefs.h fi #-------------------------------------------------------------------- # This macro figures out what flags to use with the compiler/linker # when building shared/static debug/optimized objects. This information # can be taken from the tclConfig.sh file, but this figures it all out. #-------------------------------------------------------------------- if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi # Step 0.a: Enable 64 bit support? { $as_echo "$as_me:${as_lineno-$LINENO}: checking if 64bit support is requested" >&5 $as_echo_n "checking if 64bit support is requested... " >&6; } # Check whether --enable-64bit was given. if test "${enable_64bit+set}" = set; then : enableval=$enable_64bit; do64bit=$enableval else do64bit=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $do64bit" >&5 $as_echo "$do64bit" >&6; } # Step 0.b: Enable Solaris 64 bit VIS support? { $as_echo "$as_me:${as_lineno-$LINENO}: checking if 64bit Sparc VIS support is requested" >&5 $as_echo_n "checking if 64bit Sparc VIS support is requested... " >&6; } # Check whether --enable-64bit-vis was given. if test "${enable_64bit_vis+set}" = set; then : enableval=$enable_64bit_vis; do64bitVIS=$enableval else do64bitVIS=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $do64bitVIS" >&5 $as_echo "$do64bitVIS" >&6; } # Force 64bit on with VIS if test "$do64bitVIS" = "yes"; then : do64bit=yes fi # Step 0.c: Check if visibility support is available. Do this here so # that platform specific alternatives can be used below if this fails. { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler supports visibility \"hidden\"" >&5 $as_echo_n "checking if compiler supports visibility \"hidden\"... " >&6; } if ${tcl_cv_cc_visibility_hidden+:} false; then : $as_echo_n "(cached) " >&6 else hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern __attribute__((__visibility__("hidden"))) void f(void); void f(void) {} int main () { f(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_cc_visibility_hidden=yes else tcl_cv_cc_visibility_hidden=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CFLAGS=$hold_cflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_visibility_hidden" >&5 $as_echo "$tcl_cv_cc_visibility_hidden" >&6; } if test $tcl_cv_cc_visibility_hidden = yes; then : $as_echo "#define MODULE_SCOPE extern __attribute__((__visibility__(\"hidden\")))" >>confdefs.h $as_echo "#define HAVE_HIDDEN 1" >>confdefs.h fi # Step 0.d: Disable -rpath support? { $as_echo "$as_me:${as_lineno-$LINENO}: checking if rpath support is requested" >&5 $as_echo_n "checking if rpath support is requested... " >&6; } # Check whether --enable-rpath was given. if test "${enable_rpath+set}" = set; then : enableval=$enable_rpath; doRpath=$enableval else doRpath=yes fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $doRpath" >&5 $as_echo "$doRpath" >&6; } # TEA specific: Cross-compiling options for Windows/CE builds? if test "${TEA_PLATFORM}" = windows; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking if Windows/CE build is requested" >&5 $as_echo_n "checking if Windows/CE build is requested... " >&6; } # Check whether --enable-wince was given. if test "${enable_wince+set}" = set; then : enableval=$enable_wince; doWince=$enableval else doWince=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $doWince" >&5 $as_echo "$doWince" >&6; } fi # Set the variable "system" to hold the name and version number # for the system. { $as_echo "$as_me:${as_lineno-$LINENO}: checking system version" >&5 $as_echo_n "checking system version... " >&6; } if ${tcl_cv_sys_version+:} false; then : $as_echo_n "(cached) " >&6 else # TEA specific: if test "${TEA_PLATFORM}" = "windows" ; then tcl_cv_sys_version=windows else tcl_cv_sys_version=`uname -s`-`uname -r` if test "$?" -ne 0 ; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: can't find uname command" >&5 $as_echo "$as_me: WARNING: can't find uname command" >&2;} tcl_cv_sys_version=unknown else if test "`uname -s`" = "AIX" ; then tcl_cv_sys_version=AIX-`uname -v`.`uname -r` fi fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_sys_version" >&5 $as_echo "$tcl_cv_sys_version" >&6; } system=$tcl_cv_sys_version # Require ranlib early so we can override it in special cases below. # Set configuration options based on system name and version. # This is similar to Tcl's unix/tcl.m4 except that we've added a # "windows" case and removed some core-only vars. do64bit_ok=no # default to '{$LIBS}' and set to "" on per-platform necessary basis SHLIB_LD_LIBS='${LIBS}' # When ld needs options to work in 64-bit mode, put them in # LDFLAGS_ARCH so they eventually end up in LDFLAGS even if [load] # is disabled by the user. [Bug 1016796] LDFLAGS_ARCH="" UNSHARED_LIB_SUFFIX="" # TEA specific: use PACKAGE_VERSION instead of VERSION TCL_TRIM_DOTS='`echo ${PACKAGE_VERSION} | tr -d .`' ECHO_VERSION='`echo ${PACKAGE_VERSION}`' TCL_LIB_VERSIONS_OK=ok CFLAGS_DEBUG=-g if test "$GCC" = yes; then : CFLAGS_OPTIMIZE=-O2 CFLAGS_WARNING="-Wall" else CFLAGS_OPTIMIZE=-O CFLAGS_WARNING="" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. set dummy ${ac_tool_prefix}ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="${ac_tool_prefix}ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_AR"; then ac_ct_AR=$AR # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_AR" = x; then AR="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi else AR="$ac_cv_prog_AR" fi STLIB_LD='${AR} cr' LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH" if test "x$SHLIB_VERSION" = x; then : SHLIB_VERSION="1.0" fi case $system in # TEA specific: windows) # This is a 2-stage check to make sure we have the 64-bit SDK # We have to know where the SDK is installed. # This magic is based on MS Platform SDK for Win2003 SP1 - hobbs # MACHINE is IX86 for LINK, but this is used by the manifest, # which requires x86|amd64|ia64. MACHINE="X86" if test "$do64bit" != "no" ; then if test "x${MSSDK}x" = "xx" ; then MSSDK="C:/Progra~1/Microsoft Platform SDK" fi MSSDK=`echo "$MSSDK" | sed -e 's!\\\!/!g'` PATH64="" case "$do64bit" in amd64|x64|yes) MACHINE="AMD64" ; # default to AMD64 64-bit build PATH64="${MSSDK}/Bin/Win64/x86/AMD64" ;; ia64) MACHINE="IA64" PATH64="${MSSDK}/Bin/Win64" ;; esac if test "$GCC" != "yes" -a ! -d "${PATH64}" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not find 64-bit $MACHINE SDK to enable 64bit mode" >&5 $as_echo "$as_me: WARNING: Could not find 64-bit $MACHINE SDK to enable 64bit mode" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Ensure latest Platform SDK is installed" >&5 $as_echo "$as_me: WARNING: Ensure latest Platform SDK is installed" >&2;} do64bit="no" else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using 64-bit $MACHINE mode" >&5 $as_echo " Using 64-bit $MACHINE mode" >&6; } do64bit_ok="yes" fi fi if test "$doWince" != "no" ; then if test "$do64bit" != "no" ; then as_fn_error $? "Windows/CE and 64-bit builds incompatible" "$LINENO" 5 fi if test "$GCC" = "yes" ; then as_fn_error $? "Windows/CE and GCC builds incompatible" "$LINENO" 5 fi # First, look for one uninstalled. # the alternative search directory is invoked by --with-celib if test x"${no_celib}" = x ; then # we reset no_celib in case something fails here no_celib=true # Check whether --with-celib was given. if test "${with_celib+set}" = set; then : withval=$with_celib; with_celibconfig=${withval} fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Windows/CE celib directory" >&5 $as_echo_n "checking for Windows/CE celib directory... " >&6; } if ${ac_cv_c_celibconfig+:} false; then : $as_echo_n "(cached) " >&6 else # First check to see if --with-celibconfig was specified. if test x"${with_celibconfig}" != x ; then if test -d "${with_celibconfig}/inc" ; then ac_cv_c_celibconfig=`(cd ${with_celibconfig}; pwd)` else as_fn_error $? "${with_celibconfig} directory doesn't contain inc directory" "$LINENO" 5 fi fi # then check for a celib library if test x"${ac_cv_c_celibconfig}" = x ; then for i in \ ../celib-palm-3.0 \ ../celib \ ../../celib-palm-3.0 \ ../../celib \ `ls -dr ../celib-*3.[0-9]* 2>/dev/null` \ ${srcdir}/../celib-palm-3.0 \ ${srcdir}/../celib \ `ls -dr ${srcdir}/../celib-*3.[0-9]* 2>/dev/null` \ ; do if test -d "$i/inc" ; then ac_cv_c_celibconfig=`(cd $i; pwd)` break fi done fi fi if test x"${ac_cv_c_celibconfig}" = x ; then as_fn_error $? "Cannot find celib support library directory" "$LINENO" 5 else no_celib= CELIB_DIR=${ac_cv_c_celibconfig} CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'` { $as_echo "$as_me:${as_lineno-$LINENO}: result: found $CELIB_DIR" >&5 $as_echo "found $CELIB_DIR" >&6; } fi fi # Set defaults for common evc4/PPC2003 setup # Currently Tcl requires 300+, possibly 420+ for sockets CEVERSION=420; # could be 211 300 301 400 420 ... TARGETCPU=ARMV4; # could be ARMV4 ARM MIPS SH3 X86 ... ARCH=ARM; # could be ARM MIPS X86EM ... PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002" if test "$doWince" != "yes"; then # If !yes then the user specified something # Reset ARCH to allow user to skip specifying it ARCH= eval `echo $doWince | awk -F, '{ \ if (length($1)) { printf "CEVERSION=\"%s\"\n", $1; \ if ($1 < 400) { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \ if (length($2)) { printf "TARGETCPU=\"%s\"\n", toupper($2) }; \ if (length($3)) { printf "ARCH=\"%s\"\n", toupper($3) }; \ if (length($4)) { printf "PLATFORM=\"%s\"\n", $4 }; \ }'` if test "x${ARCH}" = "x" ; then ARCH=$TARGETCPU; fi fi OSVERSION=WCE$CEVERSION; if test "x${WCEROOT}" = "x" ; then WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0" if test ! -d "${WCEROOT}" ; then WCEROOT="C:/Program Files/Microsoft eMbedded Tools" fi fi if test "x${SDKROOT}" = "x" ; then SDKROOT="C:/Program Files/Windows CE Tools" if test ! -d "${SDKROOT}" ; then SDKROOT="C:/Windows CE Tools" fi fi WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'` SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'` if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" \ -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then as_fn_error $? "could not find PocketPC SDK or target compiler to enable WinCE mode $CEVERSION,$TARGETCPU,$ARCH,$PLATFORM" "$LINENO" 5 doWince="no" else # We could PATH_NOSPACE these, but that's not important, # as long as we quote them when used. CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include" if test -d "${CEINCLUDE}/${TARGETCPU}" ; then CEINCLUDE="${CEINCLUDE}/${TARGETCPU}" fi CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" fi fi if test "$GCC" != "yes" ; then if test "${SHARED_BUILD}" = "0" ; then runtime=-MT else runtime=-MD fi if test "$do64bit" != "no" ; then # All this magic is necessary for the Win64 SDK RC1 - hobbs CC="\"${PATH64}/cl.exe\"" CFLAGS="${CFLAGS} -I\"${MSSDK}/Include\" -I\"${MSSDK}/Include/crt\" -I\"${MSSDK}/Include/crt/sys\"" RC="\"${MSSDK}/bin/rc.exe\"" lflags="-nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\"" LINKBIN="\"${PATH64}/link.exe\"" CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d" CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}" # Avoid 'unresolved external symbol __security_cookie' # errors, c.f. http://support.microsoft.com/?id=894573 vars="bufferoverflowU.lib" for i in $vars; do if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then # Convert foo.lib to -lfoo for GCC. No-op if not *.lib i=`echo "$i" | sed -e 's/^\([^-].*\)\.lib$/-l\1/i'` fi PKG_LIBS="$PKG_LIBS $i" done elif test "$doWince" != "no" ; then CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin" if test "${TARGETCPU}" = "X86"; then CC="\"${CEBINROOT}/cl.exe\"" else CC="\"${CEBINROOT}/cl${ARCH}.exe\"" fi CFLAGS="$CFLAGS -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\"" RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\"" arch=`echo ${ARCH} | awk '{print tolower($0)}'` defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _WINDOWS" if test "${SHARED_BUILD}" = "1" ; then # Static CE builds require static celib as well defs="${defs} _DLL" fi for i in $defs ; do cat >>confdefs.h <<_ACEOF #define $i 1 _ACEOF done cat >>confdefs.h <<_ACEOF #define _WIN32_WCE $CEVERSION _ACEOF cat >>confdefs.h <<_ACEOF #define UNDER_CE $CEVERSION _ACEOF CFLAGS_DEBUG="-nologo -Zi -Od" CFLAGS_OPTIMIZE="-nologo -Ox" lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'` lflags="-MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo" LINKBIN="\"${CEBINROOT}/link.exe\"" else RC="rc" lflags="-nologo" LINKBIN="link" CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d" CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}" fi fi if test "$GCC" = "yes"; then # mingw gcc mode if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args. set dummy ${ac_tool_prefix}windres; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RC"; then ac_cv_prog_RC="$RC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RC="${ac_tool_prefix}windres" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RC=$ac_cv_prog_RC if test -n "$RC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RC" >&5 $as_echo "$RC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RC"; then ac_ct_RC=$RC # Extract the first word of "windres", so it can be a program name with args. set dummy windres; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RC"; then ac_cv_prog_ac_ct_RC="$ac_ct_RC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RC="windres" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RC=$ac_cv_prog_ac_ct_RC if test -n "$ac_ct_RC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RC" >&5 $as_echo "$ac_ct_RC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RC" = x; then RC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RC=$ac_ct_RC fi else RC="$ac_cv_prog_RC" fi CFLAGS_DEBUG="-g" CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer" SHLIB_LD='${CC} -shared' UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}" LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cross-compile version of gcc" >&5 $as_echo_n "checking for cross-compile version of gcc... " >&6; } if ${ac_cv_cross+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef _WIN32 #error cross-compiler #endif int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_cross=yes else ac_cv_cross=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cross" >&5 $as_echo "$ac_cv_cross" >&6; } if test "$ac_cv_cross" = "yes"; then case "$do64bit" in amd64|x64|yes) CC="x86_64-w64-mingw32-gcc" LD="x86_64-w64-mingw32-ld" AR="x86_64-w64-mingw32-ar" RANLIB="x86_64-w64-mingw32-ranlib" RC="x86_64-w64-mingw32-windres" ;; *) CC="i686-w64-mingw32-gcc" LD="i686-w64-mingw32-ld" AR="i686-w64-mingw32-ar" RANLIB="i686-w64-mingw32-ranlib" RC="i686-w64-mingw32-windres" ;; esac fi else SHLIB_LD="${LINKBIN} -dll ${lflags}" # link -lib only works when -lib is the first arg STLIB_LD="${LINKBIN} -lib ${lflags}" UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.lib' PATHTYPE=-w # For information on what debugtype is most useful, see: # http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp # and also # http://msdn2.microsoft.com/en-us/library/y0zzbyt4%28VS.80%29.aspx # This essentially turns it all on. LDFLAGS_DEBUG="-debug -debugtype:cv" LDFLAGS_OPTIMIZE="-release" if test "$doWince" != "no" ; then LDFLAGS_CONSOLE="-link ${lflags}" LDFLAGS_WINDOW=${LDFLAGS_CONSOLE} else LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}" LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}" fi fi SHLIB_SUFFIX=".dll" SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.dll' TCL_LIB_VERSIONS_OK=nodots ;; AIX-*) if test "${TCL_THREADS}" = "1" -a "$GCC" != "yes"; then : # AIX requires the _r compiler when gcc isn't being used case "${CC}" in *_r|*_r\ *) # ok ... ;; *) # Make sure only first arg gets _r CC=`echo "$CC" | sed -e 's/^\([^ ]*\)/\1_r/'` ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using $CC for compiling with threads" >&5 $as_echo "Using $CC for compiling with threads" >&6; } fi LIBS="$LIBS -lc" SHLIB_CFLAGS="" SHLIB_SUFFIX=".so" LD_LIBRARY_PATH_VAR="LIBPATH" # Check to enable 64-bit flags for compiler/linker if test "$do64bit" = yes; then : if test "$GCC" = yes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC on $system" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported with GCC on $system" >&2;} else do64bit_ok=yes CFLAGS="$CFLAGS -q64" LDFLAGS_ARCH="-q64" RANLIB="${RANLIB} -X64" AR="${AR} -X64" SHLIB_LD_FLAGS="-b64" fi fi if test "`uname -m`" = ia64; then : # AIX-5 uses ELF style dynamic libraries on IA-64, but not PPC SHLIB_LD="/usr/ccs/bin/ld -G -z text" if test "$GCC" = yes; then : CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' else CC_SEARCH_FLAGS='-R${LIB_RUNTIME_DIR}' fi LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' else if test "$GCC" = yes; then : SHLIB_LD='${CC} -shared -Wl,-bexpall' else SHLIB_LD="/bin/ld -bhalt:4 -bM:SRE -bexpall -H512 -T512 -bnoentry" LDFLAGS="$LDFLAGS -brtl" fi SHLIB_LD="${SHLIB_LD} ${SHLIB_LD_FLAGS}" CC_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} fi ;; BeOS*) SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -nostart' SHLIB_SUFFIX=".so" #----------------------------------------------------------- # Check for inet_ntoa in -lbind, for BeOS (which also needs # -lsocket, even if the network functions are in -lnet which # is always linked to, for compatibility. #----------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_ntoa in -lbind" >&5 $as_echo_n "checking for inet_ntoa in -lbind... " >&6; } if ${ac_cv_lib_bind_inet_ntoa+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbind $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inet_ntoa (); int main () { return inet_ntoa (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_bind_inet_ntoa=yes else ac_cv_lib_bind_inet_ntoa=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bind_inet_ntoa" >&5 $as_echo "$ac_cv_lib_bind_inet_ntoa" >&6; } if test "x$ac_cv_lib_bind_inet_ntoa" = xyes; then : LIBS="$LIBS -lbind -lsocket" fi ;; BSD/OS-4.*) SHLIB_CFLAGS="-export-dynamic -fPIC" SHLIB_LD='${CC} -shared' SHLIB_SUFFIX=".so" LDFLAGS="$LDFLAGS -export-dynamic" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; CYGWIN_*) SHLIB_CFLAGS="" SHLIB_LD='${CC} -shared' SHLIB_SUFFIX=".dll" EXEEXT=".exe" do64bit_ok=yes CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; Haiku*) LDFLAGS="$LDFLAGS -Wl,--export-dynamic" SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS}' { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_ntoa in -lnetwork" >&5 $as_echo_n "checking for inet_ntoa in -lnetwork... " >&6; } if ${ac_cv_lib_network_inet_ntoa+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lnetwork $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inet_ntoa (); int main () { return inet_ntoa (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_network_inet_ntoa=yes else ac_cv_lib_network_inet_ntoa=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_network_inet_ntoa" >&5 $as_echo "$ac_cv_lib_network_inet_ntoa" >&6; } if test "x$ac_cv_lib_network_inet_ntoa" = xyes; then : LIBS="$LIBS -lnetwork" fi ;; HP-UX-*.11.*) # Use updated header definitions where possible $as_echo "#define _XOPEN_SOURCE_EXTENDED 1" >>confdefs.h # TEA specific: Needed by Tcl, but not most extensions #AC_DEFINE(_XOPEN_SOURCE, 1, [Do we want to use the XOPEN network library?]) #LIBS="$LIBS -lxnet" # Use the XOPEN network library if test "`uname -m`" = ia64; then : SHLIB_SUFFIX=".so" # Use newer C++ library for C++ extensions #if test "$GCC" != "yes" ; then # CPPFLAGS="-AA" #fi else SHLIB_SUFFIX=".sl" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if ${ac_cv_lib_dld_shl_load+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes; then : tcl_ok=yes else tcl_ok=no fi if test "$tcl_ok" = yes; then : LDFLAGS="$LDFLAGS -Wl,-E" CC_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.' LD_SEARCH_FLAGS='+s +b ${LIB_RUNTIME_DIR}:.' LD_LIBRARY_PATH_VAR="SHLIB_PATH" fi if test "$GCC" = yes; then : SHLIB_LD='${CC} -shared' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} else CFLAGS="$CFLAGS -z" # Users may want PA-RISC 1.1/2.0 portable code - needs HP cc #CFLAGS="$CFLAGS +DAportable" SHLIB_CFLAGS="+z" SHLIB_LD="ld -b" fi # Check to enable 64-bit flags for compiler/linker if test "$do64bit" = "yes"; then : if test "$GCC" = yes; then : case `${CC} -dumpmachine` in hppa64*) # 64-bit gcc in use. Fix flags for GNU ld. do64bit_ok=yes SHLIB_LD='${CC} -shared' if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC on $system" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported with GCC on $system" >&2;} ;; esac else do64bit_ok=yes CFLAGS="$CFLAGS +DD64" LDFLAGS_ARCH="+DD64" fi fi ;; IRIX-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -n32 -shared -rdata_shared" SHLIB_SUFFIX=".so" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}' fi if test "$GCC" = yes; then : CFLAGS="$CFLAGS -mabi=n32" LDFLAGS="$LDFLAGS -mabi=n32" else case $system in IRIX-6.3) # Use to build 6.2 compatible binaries on 6.3. CFLAGS="$CFLAGS -n32 -D_OLD_TERMIOS" ;; *) CFLAGS="$CFLAGS -n32" ;; esac LDFLAGS="$LDFLAGS -n32" fi ;; IRIX64-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -n32 -shared -rdata_shared" SHLIB_SUFFIX=".so" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}' fi # Check to enable 64-bit flags for compiler/linker if test "$do64bit" = yes; then : if test "$GCC" = yes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported by gcc" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported by gcc" >&2;} else do64bit_ok=yes SHLIB_LD="ld -64 -shared -rdata_shared" CFLAGS="$CFLAGS -64" LDFLAGS_ARCH="-64" fi fi ;; Linux*|GNU*|NetBSD-Debian) SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" # TEA specific: CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer" # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS_DEFAULT}' LDFLAGS="$LDFLAGS -Wl,--export-dynamic" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} if test "`uname -m`" = "alpha"; then : CFLAGS="$CFLAGS -mieee" fi if test $do64bit = yes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler accepts -m64 flag" >&5 $as_echo_n "checking if compiler accepts -m64 flag... " >&6; } if ${tcl_cv_cc_m64+:} false; then : $as_echo_n "(cached) " >&6 else hold_cflags=$CFLAGS CFLAGS="$CFLAGS -m64" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_cc_m64=yes else tcl_cv_cc_m64=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CFLAGS=$hold_cflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_m64" >&5 $as_echo "$tcl_cv_cc_m64" >&6; } if test $tcl_cv_cc_m64 = yes; then : CFLAGS="$CFLAGS -m64" do64bit_ok=yes fi fi # The combo of gcc + glibc has a bug related to inlining of # functions like strtod(). The -fno-builtin flag should address # this problem but it does not work. The -fno-inline flag is kind # of overkill but it works. Disable inlining only when one of the # files in compat/*.c is being linked in. if test x"${USE_COMPAT}" != x; then : CFLAGS="$CFLAGS -fno-inline" fi ;; Lynx*) SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" CFLAGS_OPTIMIZE=-02 SHLIB_LD='${CC} -shared' LD_FLAGS="-Wl,--export-dynamic" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi ;; OpenBSD-*) arch=`arch -s` case "$arch" in vax) SHLIB_SUFFIX="" SHARED_LIB_SUFFIX="" LDFLAGS="" ;; *) SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}' SHLIB_SUFFIX=".so" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.${SHLIB_VERSION}' LDFLAGS="-Wl,-export-dynamic" ;; esac case "$arch" in vax) CFLAGS_OPTIMIZE="-O1" ;; *) CFLAGS_OPTIMIZE="-O2" ;; esac if test "${TCL_THREADS}" = "1"; then : # On OpenBSD: Compile with -pthread # Don't link with -lpthread LIBS=`echo $LIBS | sed s/-lpthread//` CFLAGS="$CFLAGS -pthread" fi # OpenBSD doesn't do version numbers with dots. UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' TCL_LIB_VERSIONS_OK=nodots ;; NetBSD-*) # NetBSD has ELF and can use 'cc -shared' to build shared libs SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}' SHLIB_SUFFIX=".so" LDFLAGS="$LDFLAGS -export-dynamic" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} if test "${TCL_THREADS}" = "1"; then : # The -pthread needs to go in the CFLAGS, not LIBS LIBS=`echo $LIBS | sed s/-pthread//` CFLAGS="$CFLAGS -pthread" LDFLAGS="$LDFLAGS -pthread" fi ;; FreeBSD-*) # This configuration from FreeBSD Ports. SHLIB_CFLAGS="-fPIC" SHLIB_LD="${CC} -shared" TCL_SHLIB_LD_EXTRAS="-Wl,-soname=\$@" TK_SHLIB_LD_EXTRAS="-Wl,-soname,\$@" SHLIB_SUFFIX=".so" LDFLAGS="" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi if test "${TCL_THREADS}" = "1"; then : # The -pthread needs to go in the LDFLAGS, not LIBS LIBS=`echo $LIBS | sed s/-pthread//` CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LDFLAGS="$LDFLAGS $PTHREAD_LIBS" fi case $system in FreeBSD-3.*) # Version numbers are dot-stripped by system policy. TCL_TRIM_DOTS=`echo ${VERSION} | tr -d .` UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so' TCL_LIB_VERSIONS_OK=nodots ;; esac ;; Darwin-*) CFLAGS_OPTIMIZE="-Os" SHLIB_CFLAGS="-fno-common" # To avoid discrepancies between what headers configure sees during # preprocessing tests and compiling tests, move any -isysroot and # -mmacosx-version-min flags from CFLAGS to CPPFLAGS: CPPFLAGS="${CPPFLAGS} `echo " ${CFLAGS}" | \ awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \ if ($i~/^(isysroot|mmacosx-version-min)/) print "-"$i}'`" CFLAGS="`echo " ${CFLAGS}" | \ awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \ if (!($i~/^(isysroot|mmacosx-version-min)/)) print "-"$i}'`" if test $do64bit = yes; then : case `arch` in ppc) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler accepts -arch ppc64 flag" >&5 $as_echo_n "checking if compiler accepts -arch ppc64 flag... " >&6; } if ${tcl_cv_cc_arch_ppc64+:} false; then : $as_echo_n "(cached) " >&6 else hold_cflags=$CFLAGS CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_cc_arch_ppc64=yes else tcl_cv_cc_arch_ppc64=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CFLAGS=$hold_cflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_arch_ppc64" >&5 $as_echo "$tcl_cv_cc_arch_ppc64" >&6; } if test $tcl_cv_cc_arch_ppc64 = yes; then : CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5" do64bit_ok=yes fi;; i386) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler accepts -arch x86_64 flag" >&5 $as_echo_n "checking if compiler accepts -arch x86_64 flag... " >&6; } if ${tcl_cv_cc_arch_x86_64+:} false; then : $as_echo_n "(cached) " >&6 else hold_cflags=$CFLAGS CFLAGS="$CFLAGS -arch x86_64" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_cc_arch_x86_64=yes else tcl_cv_cc_arch_x86_64=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CFLAGS=$hold_cflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_arch_x86_64" >&5 $as_echo "$tcl_cv_cc_arch_x86_64" >&6; } if test $tcl_cv_cc_arch_x86_64 = yes; then : CFLAGS="$CFLAGS -arch x86_64" do64bit_ok=yes fi;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Don't know how enable 64-bit on architecture \`arch\`" >&5 $as_echo "$as_me: WARNING: Don't know how enable 64-bit on architecture \`arch\`" >&2;};; esac else # Check for combined 32-bit and 64-bit fat build if echo "$CFLAGS " |grep -E -q -- '-arch (ppc64|x86_64) ' \ && echo "$CFLAGS " |grep -E -q -- '-arch (ppc|i386) '; then : fat_32_64=yes fi fi # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -dynamiclib ${CFLAGS} ${LDFLAGS_DEFAULT}' { $as_echo "$as_me:${as_lineno-$LINENO}: checking if ld accepts -single_module flag" >&5 $as_echo_n "checking if ld accepts -single_module flag... " >&6; } if ${tcl_cv_ld_single_module+:} false; then : $as_echo_n "(cached) " >&6 else hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -dynamiclib -Wl,-single_module" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { int i; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_ld_single_module=yes else tcl_cv_ld_single_module=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$hold_ldflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_ld_single_module" >&5 $as_echo "$tcl_cv_ld_single_module" >&6; } if test $tcl_cv_ld_single_module = yes; then : SHLIB_LD="${SHLIB_LD} -Wl,-single_module" fi # TEA specific: link shlib with current and compatibility version flags vers=`echo ${PACKAGE_VERSION} | sed -e 's/^\([0-9]\{1,5\}\)\(\(\.[0-9]\{1,3\}\)\{0,2\}\).*$/\1\2/p' -e d` SHLIB_LD="${SHLIB_LD} -current_version ${vers:-0} -compatibility_version ${vers:-0}" SHLIB_SUFFIX=".dylib" # Don't use -prebind when building for Mac OS X 10.4 or later only: if test "`echo "${MACOSX_DEPLOYMENT_TARGET}" | awk -F '10\\.' '{print int($2)}'`" -lt 4 -a \ "`echo "${CPPFLAGS}" | awk -F '-mmacosx-version-min=10\\.' '{print int($2)}'`" -lt 4; then : LDFLAGS="$LDFLAGS -prebind" fi LDFLAGS="$LDFLAGS -headerpad_max_install_names" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if ld accepts -search_paths_first flag" >&5 $as_echo_n "checking if ld accepts -search_paths_first flag... " >&6; } if ${tcl_cv_ld_search_paths_first+:} false; then : $as_echo_n "(cached) " >&6 else hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-search_paths_first" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { int i; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_ld_search_paths_first=yes else tcl_cv_ld_search_paths_first=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$hold_ldflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_ld_search_paths_first" >&5 $as_echo "$tcl_cv_ld_search_paths_first" >&6; } if test $tcl_cv_ld_search_paths_first = yes; then : LDFLAGS="$LDFLAGS -Wl,-search_paths_first" fi if test "$tcl_cv_cc_visibility_hidden" != yes; then : $as_echo "#define MODULE_SCOPE __private_extern__" >>confdefs.h tcl_cv_cc_visibility_hidden=yes fi CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" LD_LIBRARY_PATH_VAR="DYLD_LIBRARY_PATH" # TEA specific: for combined 32 & 64 bit fat builds of Tk # extensions, verify that 64-bit build is possible. if test "$fat_32_64" = yes && test -n "${TK_BIN_DIR}"; then : if test "${TEA_WINDOWINGSYSTEM}" = x11; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit X11" >&5 $as_echo_n "checking for 64-bit X11... " >&6; } if ${tcl_cv_lib_x11_64+:} false; then : $as_echo_n "(cached) " >&6 else for v in CFLAGS CPPFLAGS LDFLAGS; do eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"' done CPPFLAGS="$CPPFLAGS -I/usr/X11R6/include" LDFLAGS="$LDFLAGS -L/usr/X11R6/lib -lX11" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { XrmInitialize(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_lib_x11_64=yes else tcl_cv_lib_x11_64=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="$hold_'$v'"' done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_lib_x11_64" >&5 $as_echo "$tcl_cv_lib_x11_64" >&6; } fi if test "${TEA_WINDOWINGSYSTEM}" = aqua; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit Tk" >&5 $as_echo_n "checking for 64-bit Tk... " >&6; } if ${tcl_cv_lib_tk_64+:} false; then : $as_echo_n "(cached) " >&6 else for v in CFLAGS CPPFLAGS LDFLAGS; do eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"' done CPPFLAGS="$CPPFLAGS -DUSE_TCL_STUBS=1 -DUSE_TK_STUBS=1 ${TCL_INCLUDES} ${TK_INCLUDES}" LDFLAGS="$LDFLAGS ${TCL_STUB_LIB_SPEC} ${TK_STUB_LIB_SPEC}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { Tk_InitStubs(NULL, "", 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_lib_tk_64=yes else tcl_cv_lib_tk_64=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="$hold_'$v'"' done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_lib_tk_64" >&5 $as_echo "$tcl_cv_lib_tk_64" >&6; } fi # remove 64-bit arch flags from CFLAGS et al. if configuration # does not support 64-bit. if test "$tcl_cv_lib_tk_64" = no -o "$tcl_cv_lib_x11_64" = no; then : { $as_echo "$as_me:${as_lineno-$LINENO}: Removing 64-bit architectures from compiler & linker flags" >&5 $as_echo "$as_me: Removing 64-bit architectures from compiler & linker flags" >&6;} for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="`echo "$'$v' "|sed -e "s/-arch ppc64 / /g" -e "s/-arch x86_64 / /g"`"' done fi fi ;; OS/390-*) CFLAGS_OPTIMIZE="" # Optimizer is buggy $as_echo "#define _OE_SOCKETS 1" >>confdefs.h ;; OSF1-V*) # Digital OSF/1 SHLIB_CFLAGS="" if test "$SHARED_BUILD" = 1; then : SHLIB_LD='ld -shared -expect_unresolved "*"' else SHLIB_LD='ld -non_shared -expect_unresolved "*"' fi SHLIB_SUFFIX=".so" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}' fi if test "$GCC" = yes; then : CFLAGS="$CFLAGS -mieee" else CFLAGS="$CFLAGS -DHAVE_TZSET -std1 -ieee" fi # see pthread_intro(3) for pthread support on osf1, k.furukawa if test "${TCL_THREADS}" = 1; then : CFLAGS="$CFLAGS -DHAVE_PTHREAD_ATTR_SETSTACKSIZE" CFLAGS="$CFLAGS -DTCL_THREAD_STACK_MIN=PTHREAD_STACK_MIN*64" LIBS=`echo $LIBS | sed s/-lpthreads//` if test "$GCC" = yes; then : LIBS="$LIBS -lpthread -lmach -lexc" else CFLAGS="$CFLAGS -pthread" LDFLAGS="$LDFLAGS -pthread" fi fi ;; QNX-6*) # QNX RTP # This may work for all QNX, but it was only reported for v6. SHLIB_CFLAGS="-fPIC" SHLIB_LD="ld -Bshareable -x" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; SCO_SV-3.2*) if test "$GCC" = yes; then : SHLIB_CFLAGS="-fPIC -melf" LDFLAGS="$LDFLAGS -melf -Wl,-Bexport" else SHLIB_CFLAGS="-Kpic -belf" LDFLAGS="$LDFLAGS -belf -Wl,-Bexport" fi SHLIB_LD="ld -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; SunOS-5.[0-6]) # Careful to not let 5.10+ fall into this case # Note: If _REENTRANT isn't defined, then Solaris # won't define thread-safe library routines. $as_echo "#define _REENTRANT 1" >>confdefs.h $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h SHLIB_CFLAGS="-KPIC" SHLIB_SUFFIX=".so" if test "$GCC" = yes; then : SHLIB_LD='${CC} -shared' CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} else SHLIB_LD="/usr/ccs/bin/ld -G -z text" CC_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} fi ;; SunOS-5*) # Note: If _REENTRANT isn't defined, then Solaris # won't define thread-safe library routines. $as_echo "#define _REENTRANT 1" >>confdefs.h $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h SHLIB_CFLAGS="-KPIC" # Check to enable 64-bit flags for compiler/linker if test "$do64bit" = yes; then : arch=`isainfo` if test "$arch" = "sparcv9 sparc"; then : if test "$GCC" = yes; then : if test "`${CC} -dumpversion | awk -F. '{print $1}'`" -lt 3; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC < 3.2 on $system" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported with GCC < 3.2 on $system" >&2;} else do64bit_ok=yes CFLAGS="$CFLAGS -m64 -mcpu=v9" LDFLAGS="$LDFLAGS -m64 -mcpu=v9" SHLIB_CFLAGS="-fPIC" fi else do64bit_ok=yes if test "$do64bitVIS" = yes; then : CFLAGS="$CFLAGS -xarch=v9a" LDFLAGS_ARCH="-xarch=v9a" else CFLAGS="$CFLAGS -xarch=v9" LDFLAGS_ARCH="-xarch=v9" fi # Solaris 64 uses this as well #LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH_64" fi else if test "$arch" = "amd64 i386"; then : if test "$GCC" = yes; then : case $system in SunOS-5.1[1-9]*|SunOS-5.[2-9][0-9]*) do64bit_ok=yes CFLAGS="$CFLAGS -m64" LDFLAGS="$LDFLAGS -m64";; *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC on $system" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported with GCC on $system" >&2;};; esac else do64bit_ok=yes case $system in SunOS-5.1[1-9]*|SunOS-5.[2-9][0-9]*) CFLAGS="$CFLAGS -m64" LDFLAGS="$LDFLAGS -m64";; *) CFLAGS="$CFLAGS -xarch=amd64" LDFLAGS="$LDFLAGS -xarch=amd64";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported for $arch" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported for $arch" >&2;} fi fi fi SHLIB_SUFFIX=".so" if test "$GCC" = yes; then : SHLIB_LD='${CC} -shared' CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} if test "$do64bit_ok" = yes; then : if test "$arch" = "sparcv9 sparc"; then : # We need to specify -static-libgcc or we need to # add the path to the sparv9 libgcc. # JH: static-libgcc is necessary for core Tcl, but may # not be necessary for extensions. SHLIB_LD="$SHLIB_LD -m64 -mcpu=v9 -static-libgcc" # for finding sparcv9 libgcc, get the regular libgcc # path, remove so name and append 'sparcv9' #v9gcclibdir="`gcc -print-file-name=libgcc_s.so` | ..." #CC_SEARCH_FLAGS="${CC_SEARCH_FLAGS},-R,$v9gcclibdir" else if test "$arch" = "amd64 i386"; then : # JH: static-libgcc is necessary for core Tcl, but may # not be necessary for extensions. SHLIB_LD="$SHLIB_LD -m64 -static-libgcc" fi fi fi else case $system in SunOS-5.[1-9][0-9]*) # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -G -z text ${LDFLAGS_DEFAULT}';; *) SHLIB_LD='/usr/ccs/bin/ld -G -z text';; esac CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' fi ;; UNIX_SV* | UnixWare-5*) SHLIB_CFLAGS="-KPIC" SHLIB_LD='${CC} -G' SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" # Some UNIX_SV* systems (unixware 1.1.2 for example) have linkers # that don't grok the -Bexport option. Test that it does. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld accepts -Bexport flag" >&5 $as_echo_n "checking for ld accepts -Bexport flag... " >&6; } if ${tcl_cv_ld_Bexport+:} false; then : $as_echo_n "(cached) " >&6 else hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-Bexport" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { int i; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_ld_Bexport=yes else tcl_cv_ld_Bexport=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$hold_ldflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_ld_Bexport" >&5 $as_echo "$tcl_cv_ld_Bexport" >&6; } if test $tcl_cv_ld_Bexport = yes; then : LDFLAGS="$LDFLAGS -Wl,-Bexport" fi CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; esac if test "$do64bit" = yes -a "$do64bit_ok" = no; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit support being disabled -- don't know magic for this platform" >&5 $as_echo "$as_me: WARNING: 64bit support being disabled -- don't know magic for this platform" >&2;} fi # Add in the arch flags late to ensure it wasn't removed. # Not necessary in TEA, but this is aligned with core LDFLAGS="$LDFLAGS $LDFLAGS_ARCH" # If we're running gcc, then change the C flags for compiling shared # libraries to the right flags for gcc, instead of those for the # standard manufacturer compiler. if test "$GCC" = yes; then : case $system in AIX-*) ;; BSD/OS*) ;; CYGWIN_*|MINGW32_*) ;; IRIX*) ;; NetBSD-*|FreeBSD-*|OpenBSD-*) ;; Darwin-*) ;; SCO_SV-3.2*) ;; windows) ;; *) SHLIB_CFLAGS="-fPIC" ;; esac fi if test "$tcl_cv_cc_visibility_hidden" != yes; then : $as_echo "#define MODULE_SCOPE extern" >>confdefs.h fi if test "$SHARED_LIB_SUFFIX" = ""; then : # TEA specific: use PACKAGE_VERSION instead of VERSION SHARED_LIB_SUFFIX='${PACKAGE_VERSION}${SHLIB_SUFFIX}' fi if test "$UNSHARED_LIB_SUFFIX" = ""; then : # TEA specific: use PACKAGE_VERSION instead of VERSION UNSHARED_LIB_SUFFIX='${PACKAGE_VERSION}.a' fi if test "${GCC}" = "yes" -a ${SHLIB_SUFFIX} = ".dll"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SEH support in compiler" >&5 $as_echo_n "checking for SEH support in compiler... " >&6; } if ${tcl_cv_seh+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : tcl_cv_seh=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN int main(int argc, char** argv) { int a, b = 0; __try { a = 666 / b; } __except (EXCEPTION_EXECUTE_HANDLER) { return 0; } return 1; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : tcl_cv_seh=yes else tcl_cv_seh=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_seh" >&5 $as_echo "$tcl_cv_seh" >&6; } if test "$tcl_cv_seh" = "no" ; then $as_echo "#define HAVE_NO_SEH 1" >>confdefs.h fi # # Check to see if the excpt.h include file provided contains the # definition for EXCEPTION_DISPOSITION; if not, which is the case # with Cygwin's version as of 2002-04-10, define it to be int, # sufficient for getting the current code to work. # { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EXCEPTION_DISPOSITION support in include files" >&5 $as_echo_n "checking for EXCEPTION_DISPOSITION support in include files... " >&6; } if ${tcl_cv_eh_disposition+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # define WIN32_LEAN_AND_MEAN # include # undef WIN32_LEAN_AND_MEAN int main () { EXCEPTION_DISPOSITION x; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_eh_disposition=yes else tcl_cv_eh_disposition=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_eh_disposition" >&5 $as_echo "$tcl_cv_eh_disposition" >&6; } if test "$tcl_cv_eh_disposition" = "no" ; then $as_echo "#define EXCEPTION_DISPOSITION int" >>confdefs.h fi # Check to see if winnt.h defines CHAR, SHORT, and LONG # even if VOID has already been #defined. The win32api # used by mingw and cygwin is known to do this. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for winnt.h that ignores VOID define" >&5 $as_echo_n "checking for winnt.h that ignores VOID define... " >&6; } if ${tcl_cv_winnt_ignore_void+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define VOID void #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN int main () { CHAR c; SHORT s; LONG l; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_winnt_ignore_void=yes else tcl_cv_winnt_ignore_void=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_winnt_ignore_void" >&5 $as_echo "$tcl_cv_winnt_ignore_void" >&6; } if test "$tcl_cv_winnt_ignore_void" = "yes" ; then $as_echo "#define HAVE_WINNT_IGNORE_VOID 1" >>confdefs.h fi fi # See if the compiler supports casting to a union type. # This is used to stop gcc from printing a compiler # warning when initializing a union member. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cast to union support" >&5 $as_echo_n "checking for cast to union support... " >&6; } if ${tcl_cv_cast_to_union+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { union foo { int i; double d; }; union foo f = (union foo) (int) 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_cast_to_union=yes else tcl_cv_cast_to_union=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cast_to_union" >&5 $as_echo "$tcl_cv_cast_to_union" >&6; } if test "$tcl_cv_cast_to_union" = "yes"; then $as_echo "#define HAVE_CAST_TO_UNION 1" >>confdefs.h fi # These must be called after we do the basic CFLAGS checks and # verify any possible 64-bit or similar switches are necessary { $as_echo "$as_me:${as_lineno-$LINENO}: checking for required early compiler flags" >&5 $as_echo_n "checking for required early compiler flags... " >&6; } tcl_flags="" if ${tcl_cv_flag__isoc99_source+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { char *p = (char *)strtoll; char *q = (char *)strtoull; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__isoc99_source=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _ISOC99_SOURCE 1 #include int main () { char *p = (char *)strtoll; char *q = (char *)strtoull; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__isoc99_source=yes else tcl_cv_flag__isoc99_source=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test "x${tcl_cv_flag__isoc99_source}" = "xyes" ; then $as_echo "#define _ISOC99_SOURCE 1" >>confdefs.h tcl_flags="$tcl_flags _ISOC99_SOURCE" fi if ${tcl_cv_flag__largefile64_source+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { struct stat64 buf; int i = stat64("/", &buf); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__largefile64_source=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGEFILE64_SOURCE 1 #include int main () { struct stat64 buf; int i = stat64("/", &buf); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__largefile64_source=yes else tcl_cv_flag__largefile64_source=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test "x${tcl_cv_flag__largefile64_source}" = "xyes" ; then $as_echo "#define _LARGEFILE64_SOURCE 1" >>confdefs.h tcl_flags="$tcl_flags _LARGEFILE64_SOURCE" fi if ${tcl_cv_flag__largefile_source64+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { char *p = (char *)open64; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__largefile_source64=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGEFILE_SOURCE64 1 #include int main () { char *p = (char *)open64; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__largefile_source64=yes else tcl_cv_flag__largefile_source64=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test "x${tcl_cv_flag__largefile_source64}" = "xyes" ; then $as_echo "#define _LARGEFILE_SOURCE64 1" >>confdefs.h tcl_flags="$tcl_flags _LARGEFILE_SOURCE64" fi if test "x${tcl_flags}" = "x" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${tcl_flags}" >&5 $as_echo "${tcl_flags}" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit integer type" >&5 $as_echo_n "checking for 64-bit integer type... " >&6; } if ${tcl_cv_type_64bit+:} false; then : $as_echo_n "(cached) " >&6 else tcl_cv_type_64bit=none # See if the compiler knows natively about __int64 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { __int64 value = (__int64) 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_type_64bit=__int64 else tcl_type_64bit="long long" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # See if we should use long anyway Note that we substitute in the # type that is our current guess for a 64-bit type inside this check # program, so it should be modified only carefully... cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { switch (0) { case 1: case (sizeof(${tcl_type_64bit})==sizeof(long)): ; } ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_type_64bit=${tcl_type_64bit} fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test "${tcl_cv_type_64bit}" = none ; then $as_echo "#define TCL_WIDE_INT_IS_LONG 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: using long" >&5 $as_echo "using long" >&6; } elif test "${tcl_cv_type_64bit}" = "__int64" \ -a "${TEA_PLATFORM}" = "windows" ; then # TEA specific: We actually want to use the default tcl.h checks in # this case to handle both TCL_WIDE_INT_TYPE and TCL_LL_MODIFIER* { $as_echo "$as_me:${as_lineno-$LINENO}: result: using Tcl header defaults" >&5 $as_echo "using Tcl header defaults" >&6; } else cat >>confdefs.h <<_ACEOF #define TCL_WIDE_INT_TYPE ${tcl_cv_type_64bit} _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${tcl_cv_type_64bit}" >&5 $as_echo "${tcl_cv_type_64bit}" >&6; } # Now check for auxiliary declarations { $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct dirent64" >&5 $as_echo_n "checking for struct dirent64... " >&6; } if ${tcl_cv_struct_dirent64+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { struct dirent64 p; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_struct_dirent64=yes else tcl_cv_struct_dirent64=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_struct_dirent64" >&5 $as_echo "$tcl_cv_struct_dirent64" >&6; } if test "x${tcl_cv_struct_dirent64}" = "xyes" ; then $as_echo "#define HAVE_STRUCT_DIRENT64 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct stat64" >&5 $as_echo_n "checking for struct stat64... " >&6; } if ${tcl_cv_struct_stat64+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { struct stat64 p; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_struct_stat64=yes else tcl_cv_struct_stat64=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_struct_stat64" >&5 $as_echo "$tcl_cv_struct_stat64" >&6; } if test "x${tcl_cv_struct_stat64}" = "xyes" ; then $as_echo "#define HAVE_STRUCT_STAT64 1" >>confdefs.h fi for ac_func in open64 lseek64 do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for off64_t" >&5 $as_echo_n "checking for off64_t... " >&6; } if ${tcl_cv_type_off64_t+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { off64_t offset; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_type_off64_t=yes else tcl_cv_type_off64_t=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test "x${tcl_cv_type_off64_t}" = "xyes" && \ test "x${ac_cv_func_lseek64}" = "xyes" && \ test "x${ac_cv_func_open64}" = "xyes" ; then $as_echo "#define HAVE_TYPE_OFF64_T 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi # Without the following two eval statements, NSF_SHARED_LIB_SUFFIX # in nsfConfig.sh has $PACKAGE_VERSION unresolved. When another # app links against nsf, the PACKAGE_VERSIONs are confused. # # Without the first eval, we get # NSF_SHARED_LIB_SUFFIX=${PACKAGE_VERSION}\$\{DBGX\}${SHLIB_SUFFIX} # NSF_UNSHARED_LIB_SUFFIX=${PACKAGE_VERSION}\$\{DBGX\}.a # # after the first eval, we get # NSF_SHARED_LIB_SUFFIX=1.2.1${DBGX}.so # NSF_UNSHARED_LIB_SUFFIX=1.2.1${DBGX}.a # after the second eval, all variables are resolved. eval "SHARED_LIB_SUFFIX=${SHARED_LIB_SUFFIX}" eval "UNSHARED_LIB_SUFFIX=${UNSHARED_LIB_SUFFIX}" #eval "SHARED_LIB_SUFFIX=${SHARED_LIB_SUFFIX}" #eval "UNSHARED_LIB_SUFFIX=${UNSHARED_LIB_SUFFIX}" #-------------------------------------------------------------------- # Set the default compiler switches based on the --enable-symbols # option. #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for build with symbols" >&5 $as_echo_n "checking for build with symbols... " >&6; } # Check whether --enable-symbols was given. if test "${enable_symbols+set}" = set; then : enableval=$enable_symbols; tcl_ok=$enableval else tcl_ok=no fi DBGX="" if test "$tcl_ok" = "no"; then CFLAGS_DEFAULT="${CFLAGS_OPTIMIZE} -DNDEBUG" LDFLAGS_DEFAULT="${LDFLAGS_OPTIMIZE}" { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } else CFLAGS_DEFAULT="${CFLAGS_DEBUG}" LDFLAGS_DEFAULT="${LDFLAGS_DEBUG}" if test "$tcl_ok" = "yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (standard debugging)" >&5 $as_echo "yes (standard debugging)" >&6; } fi fi # TEA specific: if test "${TEA_PLATFORM}" != "windows" ; then LDFLAGS_DEFAULT="${LDFLAGS}" fi if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then $as_echo "#define TCL_MEM_DEBUG 1" >>confdefs.h fi if test "$tcl_ok" != "yes" -a "$tcl_ok" != "no"; then if test "$tcl_ok" = "all"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled symbols mem debugging" >&5 $as_echo "enabled symbols mem debugging" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled $tcl_ok debugging" >&5 $as_echo "enabled $tcl_ok debugging" >&6; } fi fi #-------------------------------------------------------------------- # Everyone should be linking against the Tcl stub library. If you # can't for some reason, remove this definition. If you aren't using # stubs, you also need to modify the SHLIB_LD_LIBS setting below to # link against the non-stubbed Tcl library. #-------------------------------------------------------------------- $as_echo "#define USE_TCL_STUBS 1" >>confdefs.h #-------------------------------------------------------------------- # This macro generates a line to use when building a library. It # depends on values set by the TEA_ENABLE_SHARED, TEA_ENABLE_SYMBOLS, # and TEA_LOAD_TCLCONFIG macros above. #-------------------------------------------------------------------- if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes"; then MAKE_STATIC_LIB="\${STLIB_LD} -out:\$@ \$(PKG_OBJECTS)" MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LD_LIBS} \${LDFLAGS_DEFAULT} -out:\$@ \$(PKG_OBJECTS)" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if defined(_MSC_VER) && _MSC_VER >= 1400 print("manifest needed") #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "manifest needed" >/dev/null 2>&1; then : # Could do a CHECK_PROG for mt, but should always be with MSVC8+ VC_MANIFEST_EMBED_DLL="if test -f \$@.manifest ; then mt.exe -nologo -manifest \$@.manifest -outputresource:\$@\;2 ; fi" VC_MANIFEST_EMBED_EXE="if test -f \$@.manifest ; then mt.exe -nologo -manifest \$@.manifest -outputresource:\$@\;1 ; fi" MAKE_SHARED_LIB="${MAKE_SHARED_LIB} ; ${VC_MANIFEST_EMBED_DLL}" CLEANFILES="$CLEANFILES *.manifest" fi rm -f conftest* MAKE_STUB_LIB="\${STLIB_LD} -nodefaultlib -out:\$@ \$(PKG_STUB_OBJECTS)" else MAKE_STATIC_LIB="\${STLIB_LD} \$@ \$(PKG_OBJECTS)" MAKE_SHARED_LIB="\${SHLIB_LD} -o \$@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}" MAKE_STUB_LIB="\${STLIB_LD} \$@ \$(PKG_STUB_OBJECTS)" fi if test "${SHARED_BUILD}" = "1" ; then MAKE_LIB="${MAKE_SHARED_LIB} " else MAKE_LIB="${MAKE_STATIC_LIB} " fi #-------------------------------------------------------------------- # Shared libraries and static libraries have different names. # Use the double eval to make sure any variables in the suffix is # substituted. (@@@ Might not be necessary anymore) #-------------------------------------------------------------------- if test "${TEA_PLATFORM}" = "windows" ; then if test "${SHARED_BUILD}" = "1" ; then # We force the unresolved linking of symbols that are really in # the private libraries of Tcl and Tk. if test x"${TK_BIN_DIR}" != x ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TK_BIN_DIR}/${TK_STUB_LIB_FILE}`\"" fi SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}`\"" if test "$GCC" = "yes"; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -static-libgcc" fi eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}" else eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}" if test "$GCC" = "yes"; then PKG_LIB_FILE=lib${PKG_LIB_FILE} fi fi # Some packages build their own stubs libraries eval eval "PKG_STUB_LIB_FILE=${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}" if test "$GCC" = "yes"; then PKG_STUB_LIB_FILE=lib${PKG_STUB_LIB_FILE} fi # These aren't needed on Windows (either MSVC or gcc) RANLIB=: RANLIB_STUB=: else RANLIB_STUB="${RANLIB}" if test "${SHARED_BUILD}" = "1" ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TCL_STUB_LIB_SPEC}" if test x"${TK_BIN_DIR}" != x ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TK_STUB_LIB_SPEC}" fi eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${SHARED_LIB_SUFFIX}" RANLIB=: else eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}" fi # Some packages build their own stubs libraries eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}" fi # These are escaped so that only CFLAGS is picked up at configure time. # The other values will be substituted at make time. CFLAGS="${CFLAGS} \${CFLAGS_DEFAULT} \${CFLAGS_WARNING}" if test "${SHARED_BUILD}" = "1" ; then CFLAGS="${CFLAGS} \${SHLIB_CFLAGS}" fi #-------------------------------------------------------------------- # Find tclsh so that we can run pkg_mkIndex to generate the pkgIndex.tcl # file during the install process. Don't run the TCLSH_PROG through # ${CYGPATH} because it's being used directly by make. # Require that we use a tclsh shell version 8.2 or later since earlier # versions have bugs in the pkg_mkIndex routine. #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tclsh" >&5 $as_echo_n "checking for tclsh... " >&6; } if test -f "${TCL_BIN_DIR}/Makefile" ; then # tclConfig.sh is in Tcl build directory if test "${TEA_PLATFORM}" = "windows"; then TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}" else TCLSH_PROG="${TCL_BIN_DIR}/tclsh" fi else # tclConfig.sh is in install location if test "${TEA_PLATFORM}" = "windows"; then TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}" else TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_DBGX}" fi list="`ls -d ${TCL_BIN_DIR}/../bin 2>/dev/null` \ `ls -d ${TCL_BIN_DIR}/.. 2>/dev/null` \ `ls -d ${TCL_PREFIX}/bin 2>/dev/null`" for i in $list ; do if test -f "$i/${TCLSH_PROG}" ; then REAL_TCL_BIN_DIR="`cd "$i"; pwd`/" break fi done TCLSH_PROG="${REAL_TCL_BIN_DIR}${TCLSH_PROG}" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${TCLSH_PROG}" >&5 $as_echo "${TCLSH_PROG}" >&6; } # make this available, for such as nsfConfig.sh NSF_COMPATIBLE_TCLSH=${TCLSH_PROG} # # Next Scripting specific configs # #NSF_SRC_DIR=$srcdir NSF_SRC_DIR="`pwd`" eval "NSF_PKG_LIBDIR=\"${libdir}/${PACKAGE_NAME}${PACKAGE_VERSION}\"" if test "${TEA_PLATFORM}" != "windows" ; then NSF_BUILD_LIB_SPEC="-L${NSF_SRC_DIR} -lnsf${PACKAGE_VERSION}" NSF_LIB_SPEC="-L${NSF_PKG_LIBDIR} -lnsf${PACKAGE_VERSION}" # If someone wants to build without stubs, as it was the case for # earlier Tcl versions, we keep the following block as a reference. if test "${TCL_MAJOR_VERSION}" = "8" -a "${TCL_MINOR_VERSION}" = "0"; then NSF_BUILD_STUB_LIB_PATH="" NSF_STUB_LIB_PATH="" NSF_BUILD_STUB_LIB_SPEC="" NSF_STUB_LIB_SPEC="" else NSF_BUILD_STUB_LIB_PATH="${NSF_SRC_DIR}/${PKG_STUB_LIB_FILE}" NSF_STUB_LIB_PATH="${NSF_PKG_LIBDIR}/${PKG_STUB_LIB_FILE}" NSF_BUILD_STUB_LIB_SPEC="-L${NSF_SRC_DIR} -lnsfstub${PACKAGE_VERSION}" NSF_STUB_LIB_SPEC="-L${NSF_PKG_LIBDIR} -lnsfstub${PACKAGE_VERSION}" $as_echo "#define COMPILE_NSF_STUBS 1" >>confdefs.h fi else NSF_BUILD_LIB_SPEC="${NSF_SRC_DIR}/${PKG_LIB_FILE}" NSF_LIB_SPEC="${NSF_PKG_LIBDIR}/${PKG_LIB_FILE}" NSF_BUILD_STUB_LIB_PATH="${NSF_SRC_DIR}/${PKG_STUB_LIB_FILE}" NSF_STUB_LIB_PATH="${NSF_PKG_LIBDIR}/${PKG_STUB_LIB_FILE}" NSF_BUILD_STUB_LIB_SPEC="${NSF_SRC_DIR}/${PKG_STUB_LIB_FILE}" NSF_STUB_LIB_SPEC="${NSF_PKG_LIBDIR}/${PKG_STUB_LIB_FILE}" fi # include dirs for nsf during build process (i.e., does not assume installed) NSF_BUILD_INCLUDE_DIR="${NSF_SRC_DIR}/generic" NSF_BUILD_INCLUDE_SPEC="-I${NSF_BUILD_INCLUDE_DIR}" eval "NSF_INCLUDE_DIR=\"$includedir\"" #-------------------------------------------------------------------- # the value of this variable is set to the files which are to be # removed when the user invokes 'make distclean' (i.e., those # files generated by ./configure) and is used in the make distclean # target, defined in Makefile.in #-------------------------------------------------------------------- CONFIG_CLEAN_FILES="Makefile nsfConfig.sh library/xotcl/xotclsh library/xotcl/xowish unix/pkgIndex.unix nxsh nxwish autom4te.cache/" ac_config_files="$ac_config_files Makefile nsfConfig.sh library/xotcl/xotclsh library/xotcl/xowish unix/pkgIndex.unix nxsh nxwish" #-------------------------------------------------------------------- # Finally, substitute all of the various values into the Makefile, # and generate the other output files. (this is done by invoking # config.status) # # NOTE the lack of parameters! AC_OUTPUT with params is deprecated; # use macros such as AC_CONFIG_FILES, AC_HEADER_FILES, etc to add # to the files output by ./configure. #-------------------------------------------------------------------- cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs CFLAGS="${CFLAGS} ${CPPFLAGS}"; CPPFLAGS="" : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by nsf $as_me 2.0.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ nsf config.status 2.0.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "nsfConfig.sh") CONFIG_FILES="$CONFIG_FILES nsfConfig.sh" ;; "library/xotcl/xotclsh") CONFIG_FILES="$CONFIG_FILES library/xotcl/xotclsh" ;; "library/xotcl/xowish") CONFIG_FILES="$CONFIG_FILES library/xotcl/xowish" ;; "unix/pkgIndex.unix") CONFIG_FILES="$CONFIG_FILES unix/pkgIndex.unix" ;; "nxsh") CONFIG_FILES="$CONFIG_FILES nxsh" ;; "nxwish") CONFIG_FILES="$CONFIG_FILES nxwish" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi chmod +x nxsh nxwish library/xotcl/xotclsh library/xotcl/xowish here=${PWD} for subdir in ${subdirs} do echo "==================== configure $subdir" if test x"${srcdir}" = x. ; then confdir=. else mkdir -p $subdir confdir=${srcdir}/$subdir fi (cd $subdir; echo $SHELL ${confdir}/configure ${ac_configure_args} --prefix=${prefix} --with-nsf=${here}; eval $SHELL ${confdir}/configure ${ac_configure_args} --prefix=${prefix} --with-nsf=${here}) done configure.ac000066400000000000000000000421301242365656200133570ustar00rootroot00000000000000 #-------------------------------------------------------------------- # Sample configure.in for Tcl Extensions. The only places you should # need to modify this file are marked by the string __CHANGE__ #-------------------------------------------------------------------- #-------------------------------------------------------------------- # __CHANGE__ # This very first macro is used to verify that the configure script can # find the sources. The argument to AC_INIT should be a unique filename # for this package, and can be a relative path, such as: # #-------------------------------------------------------------------- AC_PREREQ([2.69]) define(NsfVersion, 2.0.0) AC_INIT([nsf],[NsfVersion], [xotcl@alice.wu-wien.ac.at]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([tclconfig]) #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. # This will define a ${TEA_PLATFORM} variable == "unix" or "windows". #-------------------------------------------------------------------- TEA_INIT([3.9]) #-------------------------------------------------------------------- # specify some extra flags #-------------------------------------------------------------------- # According to http://www.gnu.org/software/autoconf/manual/autoconf.html # - "with" is for external software (optional packages) # - "enable" is for optional features # AC_ARG_WITH([aolserver3], AS_HELP_STRING([--with-aolserver3=AOL_SERVER_DIR], [build an AOLserver 3 module; point to directory containing aolsever/include (default: off)]), [with_aol3=$withval], [with_aol3=no]) AC_ARG_WITH([dtrace], AS_HELP_STRING([--with-dtrace], [build nsf with dtrace (default: without)]), [with_dtrace=$withval], [with_dtrace=no]) AC_ARG_WITH([mongoc], AS_HELP_STRING([--with-mongoc=MONGOC_INCLUDE_DIR[,MONGOC_LIB_DIR]], [build nsf with mongodb c-driver support (default: without)]), [with_mongoc=$withval], [with_mongoc=no]) AC_ARG_WITH([bson], AS_HELP_STRING([--with-bson=BSON_INCLUDE_DIR[,BSON_LIB_DIR]], [build nsf with mongodb bson support (default: without)]), [with_bson=$withval], [with_bson=no]) AC_ARG_ENABLE([profile], AS_HELP_STRING([--enable-profile], [build nsf with profile support (default: disabled)]), [enable_profile=$enableval], [enable_profile=no]) AC_ARG_ENABLE([memcount], AS_HELP_STRING([--enable-memcount=yes|trace], [build nsf with memcount debug support (default: disabled)]), [enable_memcount=$enableval], [enable_memcount=no]) AC_ARG_ENABLE([development], AS_HELP_STRING([--enable-development], [build nsf with development support (intensive runtime checking, etc.; default: disabled)]), [enable_development=$enableval], [enable_development=no]) AC_ARG_ENABLE([assertions], AS_HELP_STRING([--enable-assertions], [build nsf with assertion support (default: enabled)]), [enable_assertions=$enableval], [enable_assertions=yes]) AC_ARG_ENABLE([assemble], AS_HELP_STRING([--enable-assemble=yes|label|call], [build nsf with assemble support (default: disabled)]), [enable_assemble=$enableval], [enable_assemble=no]) subdirs="" if ! test "$with_mongoc" = no; then test_mongodb=test-mongdb subdirs="$subdirs library/mongodb" fi test_actiweb="" libdirs_actiweb="" apps_actiweb="" AC_SUBST([subdirs]) #-------------------------------------------------------------------- # __CHANGE__ # Set your package name and version numbers here. The NODOT_VERSION is # required for constructing the library name on systems that don't like # dots in library names (Windows). The VERSION variable is used on the # other systems. #-------------------------------------------------------------------- NSF_MAJOR_VERSION=2 NSF_MINOR_VERSION=0 NSF_PATCH_LEVEL=$PACKAGE_VERSION NSF_VERSION=${NSF_MAJOR_VERSION}.${NSF_MINOR_VERSION} NODOT_VERSION=${NSF_MAJOR_VERSION}${NSF_MINOR_VERSION} AC_SUBST([NSF_VERSION]) AC_SUBST([NSF_MAJOR_VERSION]) AC_SUBST([NSF_MINOR_VERSION]) AC_SUBST([NSF_PATCH_LEVEL]) echo "Configuring next Version $PACKAGE_VERSION" #-------------------------------------------------------------------- # Load the tclConfig.sh file #-------------------------------------------------------------------- TEA_PATH_TCLCONFIG TEA_LOAD_TCLCONFIG AC_CHECK_FUNCS([strnstr]) #-------------------------------------------------------------------- # check for TK #-------------------------------------------------------------------- #-------------------------------------------------------------------- # Handle the --prefix=... option by defaulting to what Tcl gave. # Must be called after TEA_LOAD_TCLCONFIG and before TEA_SETUP_COMPILER. #----------------------------------------------------------------------- TEA_PREFIX #----------------------------------------------------------------------- # Standard compiler checks. # This sets up CC by using the CC env var, or looks for gcc otherwise. # This also calls AC_PROG_CC, AC_PROG_INSTALL and a few others to create # the basic setup necessary to compile executables. #----------------------------------------------------------------------- TEA_SETUP_COMPILER #-------------------------------------------------------------------- # check for extra flags # # FORCE_NO_STUBS is deactivated for now if ! test "$with_aol3" = "no"; then echo "Pre-configuring AOL-Server (nsf)" AOL_DEFINES="-DAOL_SERVER -DUSE_TCL8X -I$with_aol3/include -I$TCL_SRC_DIR/generic" FORCE_NO_STUBS=1 else AOL_DEFINES="" FORCE_NO_STUBS=0 fi # set the default aol_prefix aol_prefix="/usr/local/aolserver" # if we have under ${prefix} either modules/tcl/init.tcl (aolserver # style) or modules/tcl/init.tcl (naviserver style), then use # ${prefix} as aol_prefix (path for the installation of # aolserver/naviserver specific tcl files). if test -f "${prefix}/modules/tcl/init.tcl" ; then aol_prefix="${prefix}" else if test -f "${prefix}/tcl/init.tcl" ; then aol_prefix="${prefix}" fi fi AC_SUBST([aol_prefix]) #-------------------------------------------------------------------- # check for tclCompile.h (needed, when compiled without full source) if test -f "$TCL_SRC_DIR/generic/tclCompile.h"; then AC_DEFINE([HAVE_TCL_COMPILE_H], [1], [Do we have tclCompile.h installed?]) fi if test "$enable_assertions" = yes; then AC_DEFINE([NSF_WITH_ASSERTIONS], [1], [Are we building with assertions support?]) fi if test "$enable_profile" = yes; then AC_DEFINE([NSF_PROFILE], [1], [Are we building with profile support?]) fi if test "$enable_development" = yes; then AC_DEFINE([NSF_DEVELOPMENT], [1], [Are we building with development support?]) fi if test "$enable_memcount" = yes; then AC_DEFINE([NSF_MEM_COUNT], [1], [Are we building with memcount support?]) fi if test "$enable_memcount" = trace; then AC_DEFINE([NSF_MEM_TRACE], [1], [Are we building with memcount tracing support?]) fi if test "$enable_assemble" = yes; then AC_DEFINE([NSF_ASSEMBLE], [1], [Are we building with assembly support?]) fi if test "$enable_assemble" = call; then AC_DEFINE([NSF_ASSEMBLE_CT], [1], [Are we building with assembly call threading support?]) fi if test "$enable_assemble" = call; then AC_DEFINE([NSF_ASSEMBLE_LT], [1], [Are we building with assembly label threading support?]) fi DTRACE_OBJ= if test "$with_dtrace" = yes; then AC_DEFINE([NSF_DTRACE], [1], [Are we building with DTrace support?]) # Under Mac OS X, we need no nsfDTrace.o if test "`uname -s`" != "Darwin" ; then DTRACE_OBJ=nsfDTrace.o fi fi AC_SUBST([DTRACE_OBJ]) #----------------------------------------------------------------------- # __CHANGE__ # Specify the C source files to compile in TEA_ADD_SOURCES, # public headers that need to be installed in TEA_ADD_HEADERS, # stub library C source files to compile in TEA_ADD_STUB_SOURCES, # and runtime Tcl library files in TEA_ADD_TCL_SOURCES. # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS # and PKG_TCL_SOURCES. #----------------------------------------------------------------------- stubdir=stubs${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION} TEA_ADD_SOURCES([nsf.c nsfError.c nsfObjectData.c nsfProfile.c \ nsfDebug.c nsfUtil.c nsfObj.c nsfPointer.c nsfEnumerationType.c \ nsfCmdDefinitions.c nsfShadow.c nsfCompile.c aolstub.c \${srcdir}/generic/${stubdir}/nsfStubInit.${OBJEXT}]) TEA_ADD_HEADERS([generic/nsf.h generic/nsfInt.h generic/${stubdir}/nsfDecls.h generic/${stubdir}/nsfIntDecls.h]) TEA_ADD_INCLUDES([]) TEA_ADD_LIBS([]) TEA_ADD_CFLAGS([-DNSF_VERSION=\\\"$NSF_VERSION\\\" -DNSF_PATCHLEVEL=\\\"$NSF_PATCH_LEVEL\\\" \ $AOL_DEFINES ]) TEA_ADD_STUB_SOURCES([nsfStubLib.c]) TEA_ADD_TCL_SOURCES([]) #-------------------------------------------------------------------- # __CHANGE__ # # You can add more files to clean if your extension creates any extra # files by extending CLEANFILES. # Add pkgIndex.tcl if it is generated in the Makefile instead of ./configure # and change Makefile.in to move it from CONFIG_CLEAN_FILES to BINARIES var. # # A few miscellaneous platform-specific items: # TEA_ADD_* any platform specific compiler/build info here. #-------------------------------------------------------------------- if test "${TEA_PLATFORM}" = "windows" ; then if test "$GCC" != "yes" ; then AC_DEFINE([VISUAL_CC]) fi CLEANFILES="*.lib *.dll *.exp *.ilk *.pdb vc50.pch vc60.pch " #TEA_ADD_SOURCES([win/winFile.c]) #TEA_ADD_INCLUDES([-I\"$(${CYGPATH} ${srcdir}/win)\"]) else CLEANFILES="*.a *.so *~ core gmon.out" #TEA_ADD_SOURCES([unix/unixFile.c]) #TEA_ADD_LIBS([-lsuperfly]) fi CLEANFILES="$CLEANFILES *.${OBJEXT} pkgIndex.tcl" #-------------------------------------------------------------------- # __CHANGE__ # Choose which headers you need. Extension authors should try very # hard to only rely on the Tcl public header files. Internal headers # contain private data structures and are subject to change without # notice. # This must be done AFTER calling TEA_PATH_TCLCONFIG/TEA_LOAD_TCLCONFIG # so that we can extract TCL_SRC_DIR from the config file (in the case # of private headers #-------------------------------------------------------------------- #TEA_PUBLIC_TCL_HEADERS TEA_PRIVATE_TCL_HEADERS #TEA_PUBLIC_TK_HEADERS #TEA_PRIVATE_TK_HEADERS #TEA_PATH_X #-------------------------------------------------------------------- # Check whether --enable-threads or --disable-threads was given. #-------------------------------------------------------------------- TEA_ENABLE_THREADS #-------------------------------------------------------------------- # The statement below defines a collection of symbols related to # building as a shared library instead of a static library. #-------------------------------------------------------------------- TEA_ENABLE_SHARED #-------------------------------------------------------------------- # This macro figures out what flags to use with the compiler/linker # when building shared/static debug/optimized objects. This information # can be taken from the tclConfig.sh file, but this figures it all out. #-------------------------------------------------------------------- TEA_CONFIG_CFLAGS # Without the following two eval statements, NSF_SHARED_LIB_SUFFIX # in nsfConfig.sh has $PACKAGE_VERSION unresolved. When another # app links against nsf, the PACKAGE_VERSIONs are confused. # # Without the first eval, we get # NSF_SHARED_LIB_SUFFIX=${PACKAGE_VERSION}\$\{DBGX\}${SHLIB_SUFFIX} # NSF_UNSHARED_LIB_SUFFIX=${PACKAGE_VERSION}\$\{DBGX\}.a # # after the first eval, we get # NSF_SHARED_LIB_SUFFIX=1.2.1${DBGX}.so # NSF_UNSHARED_LIB_SUFFIX=1.2.1${DBGX}.a # after the second eval, all variables are resolved. eval "SHARED_LIB_SUFFIX=${SHARED_LIB_SUFFIX}" eval "UNSHARED_LIB_SUFFIX=${UNSHARED_LIB_SUFFIX}" #eval "SHARED_LIB_SUFFIX=${SHARED_LIB_SUFFIX}" #eval "UNSHARED_LIB_SUFFIX=${UNSHARED_LIB_SUFFIX}" #-------------------------------------------------------------------- # Set the default compiler switches based on the --enable-symbols # option. #-------------------------------------------------------------------- TEA_ENABLE_SYMBOLS #-------------------------------------------------------------------- # Everyone should be linking against the Tcl stub library. If you # can't for some reason, remove this definition. If you aren't using # stubs, you also need to modify the SHLIB_LD_LIBS setting below to # link against the non-stubbed Tcl library. #-------------------------------------------------------------------- AC_DEFINE([USE_TCL_STUBS], [1], [Use Tcl stubs]) #-------------------------------------------------------------------- # This macro generates a line to use when building a library. It # depends on values set by the TEA_ENABLE_SHARED, TEA_ENABLE_SYMBOLS, # and TEA_LOAD_TCLCONFIG macros above. #-------------------------------------------------------------------- TEA_MAKE_LIB #-------------------------------------------------------------------- # Find tclsh so that we can run pkg_mkIndex to generate the pkgIndex.tcl # file during the install process. Don't run the TCLSH_PROG through # ${CYGPATH} because it's being used directly by make. # Require that we use a tclsh shell version 8.2 or later since earlier # versions have bugs in the pkg_mkIndex routine. #-------------------------------------------------------------------- TEA_PROG_TCLSH # make this available, for such as nsfConfig.sh NSF_COMPATIBLE_TCLSH=${TCLSH_PROG} AC_SUBST([NSF_COMPATIBLE_TCLSH]) AC_SUBST([TCL_EXEC_PREFIX]) # # Next Scripting specific configs # #NSF_SRC_DIR=$srcdir NSF_SRC_DIR="`pwd`" AC_SUBST([NSF_SRC_DIR]) eval "NSF_PKG_LIBDIR=\"${libdir}/${PACKAGE_NAME}${PACKAGE_VERSION}\"" AC_SUBST([NSF_PKG_LIBDIR]) if test "${TEA_PLATFORM}" != "windows" ; then NSF_BUILD_LIB_SPEC="-L${NSF_SRC_DIR} -lnsf${PACKAGE_VERSION}" NSF_LIB_SPEC="-L${NSF_PKG_LIBDIR} -lnsf${PACKAGE_VERSION}" # If someone wants to build without stubs, as it was the case for # earlier Tcl versions, we keep the following block as a reference. if test "${TCL_MAJOR_VERSION}" = "8" -a "${TCL_MINOR_VERSION}" = "0"; then NSF_BUILD_STUB_LIB_PATH="" NSF_STUB_LIB_PATH="" NSF_BUILD_STUB_LIB_SPEC="" NSF_STUB_LIB_SPEC="" else NSF_BUILD_STUB_LIB_PATH="${NSF_SRC_DIR}/${PKG_STUB_LIB_FILE}" NSF_STUB_LIB_PATH="${NSF_PKG_LIBDIR}/${PKG_STUB_LIB_FILE}" NSF_BUILD_STUB_LIB_SPEC="-L${NSF_SRC_DIR} -lnsfstub${PACKAGE_VERSION}" NSF_STUB_LIB_SPEC="-L${NSF_PKG_LIBDIR} -lnsfstub${PACKAGE_VERSION}" AC_DEFINE([COMPILE_NSF_STUBS]) fi else NSF_BUILD_LIB_SPEC="${NSF_SRC_DIR}/${PKG_LIB_FILE}" NSF_LIB_SPEC="${NSF_PKG_LIBDIR}/${PKG_LIB_FILE}" NSF_BUILD_STUB_LIB_PATH="${NSF_SRC_DIR}/${PKG_STUB_LIB_FILE}" NSF_STUB_LIB_PATH="${NSF_PKG_LIBDIR}/${PKG_STUB_LIB_FILE}" NSF_BUILD_STUB_LIB_SPEC="${NSF_SRC_DIR}/${PKG_STUB_LIB_FILE}" NSF_STUB_LIB_SPEC="${NSF_PKG_LIBDIR}/${PKG_STUB_LIB_FILE}" fi AC_SUBST([SHARED_LIB_SUFFIX]) AC_SUBST([UNSHARED_LIB_SUFFIX]) AC_SUBST([NSF_BUILD_LIB_SPEC]) AC_SUBST([NSF_LIB_SPEC]) AC_SUBST([NSF_BUILD_STUB_LIB_SPEC]) AC_SUBST([NSF_STUB_LIB_SPEC]) AC_SUBST([NSF_BUILD_STUB_LIB_PATH]) AC_SUBST([NSF_STUB_LIB_PATH]) AC_SUBST([NXSH]) AC_SUBST([XOWISH]) # include dirs for nsf during build process (i.e., does not assume installed) NSF_BUILD_INCLUDE_DIR="${NSF_SRC_DIR}/generic" NSF_BUILD_INCLUDE_SPEC="-I${NSF_BUILD_INCLUDE_DIR}" AC_SUBST([NSF_BUILD_INCLUDE_DIR]) AC_SUBST([NSF_BUILD_INCLUDE_SPEC]) eval "NSF_INCLUDE_DIR=\"$includedir\"" AC_SUBST([NSF_INCLUDE_DIR]) AC_SUBST([test_actiweb]) AC_SUBST([libdirs_actiweb]) AC_SUBST([apps_actiweb]) AC_SUBST([TEA_PLATFORM]) dnl macro expanding to the names of files ./configure is to generate. dnl reasoning: this is a factoring; I use this value elsewhere. dnl dnl Change the value of -this- macro if you want to add or remove dnl such files. AC_DEFUN([CONFIG_OUTPUT_FILES], [[Makefile nsfConfig.sh library/xotcl/xotclsh library/xotcl/xowish unix/pkgIndex.unix nxsh nxwish]]) #-------------------------------------------------------------------- # the value of this variable is set to the files which are to be # removed when the user invokes 'make distclean' (i.e., those # files generated by ./configure) and is used in the make distclean # target, defined in Makefile.in #-------------------------------------------------------------------- CONFIG_CLEAN_FILES="CONFIG_OUTPUT_FILES autom4te.cache/" AC_SUBST([CONFIG_CLEAN_FILES]) AC_CONFIG_FILES(CONFIG_OUTPUT_FILES) #-------------------------------------------------------------------- # Finally, substitute all of the various values into the Makefile, # and generate the other output files. (this is done by invoking # config.status) # # NOTE the lack of parameters! AC_OUTPUT with params is deprecated; # use macros such as AC_CONFIG_FILES, AC_HEADER_FILES, etc to add # to the files output by ./configure. #-------------------------------------------------------------------- AC_OUTPUT chmod +x nxsh nxwish library/xotcl/xotclsh library/xotcl/xowish here=${PWD} for subdir in ${subdirs} do echo "==================== configure $subdir" if test x"${srcdir}" = x. ; then confdir=. else mkdir -p $subdir confdir=${srcdir}/$subdir fi (cd $subdir; echo $SHELL ${confdir}/configure ${ac_configure_args} --prefix=${prefix} --with-nsf=${here}; eval $SHELL ${confdir}/configure ${ac_configure_args} --prefix=${prefix} --with-nsf=${here}) done doc/000077500000000000000000000000001242365656200116365ustar00rootroot00000000000000doc/Announce2.0000066400000000000000000000311731242365656200135540ustar00rootroot00000000000000Dear Community, Since releasing the Next Scripting Framework (NSF) 2.0b5, we have received more feedback from early adopters. Many thanks for the helpful and the constructive comments! Since the release of 2.0b5, there have been more than 450 commits to our code repository. The implementation is very stable and has been used for more than two years in production of our large-scale, multi-threaded web environment inside NaviServer. Most of the changes happened in NX and, therefore, on the NSF scripting level, without the need to modify the NSF C layer. The implementation of XOTcl 2 has changed very little. The Next Scripting Framework was tested with Tcl 8.5.17 and Tcl 8.6.2 on Linux, Mac OS X, and in Windows enviroments (MinGW, VC12). Below are the most notable differences in NSF/NX 2.0 final relative to 2.0b5: a) Pluralism reform: Use plural names for structural features of objects and classes, whenever potentially multiple elements are provided or returned. This rule applies now consistently throughout NX. Here are examples from the introspection interface: /cls/ info superclasses /cls/ info subclasses /cls/ info mixins /obj/ info object mixins /cls/ info filters /obj/ info object filters Similarly, there the plural is used for configure options, e.g.: nx:create create Foo -superclasses {C D} Note that abbreviations are allowed as well: nx:create create Foo -superclass {C D} b) Dispatch by arity is gone in NX. XOTcl and earlier versions of NX supported a dispatch-by-arity mechanism for relation slots: o mixin; # arity 1: get value o mixin M1; # arity 2: set value o mixin add M1; # arity 3: use arg 2 for slot methods The problem with this approach is that it is not straight forward to provide meaningful error messages. In addition, one might be irritated about the result of e.g. "o mixin add" (leaving out a class to be added). In the final release, we removed the entailed complexity by relying on a fixed arity of 3 (last form above) for the default slot methods: add, clear, delete, get, guard, set c) Support for querying computed parameters of methods. Earlier versions of NX had several methods to query the parameters for configuring objects of different classes. The configure parameters are determined by the inheritance order in the class hierarchy and are relevant during object creation (e.g. methods "create" or "new"). Now, these configure parameter can be queried using the standard parameter introspection interface for e.g. "new", "create", or "configure", such as in: nx::Object info lookup parameters create The above command call returns the parameters which can be provided to an "nx::Object create ..." invocation. Furthermore, these computed parameters are returned in error messages. d) Support abbreviations of non-positional parameter names (for plain methods, nsf::proc, or "... configure..."). To avoid surprises, especially for computed argument lists, the minimal number of optional trailing characters is set to 4. e) Updated MongoDB interface: The interface was extended in various ways and is now based on mongo-c-driver 1.0.2 and was tested with MongoDB 2.6.5. The driver can be used nicely with e.g. Naviserver by using the c-driver supported connection pool. f) API changes: nx::Object info lookup parameters create /cls/ mixins ... /obj/ object mixins ... /cls/ filters ... /obj/ object filters ... Simplified info methods for interceptors: /cls/ info mixin classes -> /cls/ info mixins /cls/ info filter methods -> /cls/ info filters /obj/ info object mixin classes -> /obj/ info object mixins Dropped methods: /cls/ info mixin guard /obj/ info object mixin guard /cls/ info filter guard /obj/ info object filter guard Instead, use the "-guards" option of "... info ?object? mixins|filters ...". Added methods: /cls/ mixins classes /cls/ filters methods /obj/ object filters methods /obj/ object mixins classes g) Added API documentation: Using tcllib's doctools markup and dtplite, we now provide manpages for: nx::Object nx::Class nx::current nx::next nx::configure a) NX 2.0b3 used the following conventions to define methods for instances, object-specific methods and class-object specific methods: /cls/ info lookup parameters create /cls/ info lookup parameters create /cls/ method foo {args} {...} /obj/ method bar {args} {...} /cls/ class method baz {args} {...} Introspection was possible via (in the same order): /cls/ info methods /obj/ info methods /cls/ class info methods The problem with this convention is that e.g. "info methods" operates on different method records, depending on whether it is called on a class or on an object. This breaks a basic inheritance contract with the programmer: As nx::Class is a specialization of the most general class nx::Object, the same introspection operation (e.g., "info methods") should return e.g. object-specific methods for both class objects and ordinary, non-class objects. Therefore, we adopted the following more orthogonal conventions to define methods for instances and for object-specific methods /cls/ method foo {args} {...} /obj/ object method bar {args} {...} Introspection: /cls/ info methods /obj/ info object methods Note that we can now use the same mechanism to define or query object-specific methods on objects and classes. The same applies for aliases, forwards, mixins, and filters. The new convention imposes a little typing burden for the code writer, but reduces the potential ambiguity for the code reader, who is trying to understand what exactly "$x method FOO {args} {...}" means. For convenience, we provide two packages "nx::plain-object-method" and "nx::class-method" to switch to the old conventions. A verbose tracing mode can report usages to ease migration. b) Parametrization: NX 2.0b3 followed the XOTcl conventions of registering by default same-named getter/setter methods for configuration parameters used in object creation. These getter/setter methods bloat the method interface and risk shadowing inherited methods, leading to unexpected behaviors for beginners. NX 2.0b5 adopts a Tk/itcl/... idiom by offering a cget/configure interface to objects as generic getters/setters. To obtain parameter-specific getters/setters (i.e., the old behavior), the flag "-accessor public|protected|private" can be set when defining properties and variables. c) Further clean-ups of the introspection interface ("info"). In order to streamline the interface further, we followed the idea to use "... info /plural word/" to obtain a set of handles, and then to make a separate call to retrieve the details. Therefore, we now provide ... /cls/ info methods /obj/ info object methods /cls/ info variables /obj/ info object variables /cls/ info slots /obj/ info object slots /cls/ info method parameters /methodName/ /obj/ info object method parameters /methodName/ /cls/ info configure parameters /obj/ info lookup configure parameters ... to return a list of handles. The result list can be filtered in each case by specifying a match pattern. Each result handle can then be used in a separate call to obtain details: /obj/ info method definition /methodHandle/ /obj/ info variable definition /varHandle/ /obj/ info parameter name /paramHandle/ These are just a few examples. In NX 2.0b3, we had e.g. "... info parameter definitions ..." leaving a beginner in the dark about the parameters actually meant. Also, the introspection interface made mixed use of plural and singular wordings for different purposes (e.g., retrieving collections and/or detailed information on one item). Below is a more detailed summary of the changes. The Next Scripting Framework 2.0b5 (containing NX and XOTcl 2.0b5) can be obtained from http://next-scripting.org/ Best regards - Gustaf Neumann - Stefan Sobernig =============================================== Announcing NSF 2.0b5 ************************* Major changes relative to NSF 2.0b3 are (in addition of the items (a), (b), and (c) above) are: * Additional Features: - Serializer: * Added flag -objmap to Serializer method deepSerialize to make serializer to provide mapping only for object names. This makes the serializer usable for object/class copying (-map is too coarse) * Made "ignore" method public * Due to the split between serializer and object system serializer, the "ignore" settings were lost - Allow explicit unsetting of -per-object flag in 0-argument "-flag=value" notation (all built-in commands accepting this flag) - Better compatibility with XOTcl 1.*: - Added "/obj/ info name" (as alternative to "namespace tail [self]") - Test-suite: added summary with statistics - Traits: added property/variable inheritance - MongoDB - Added "nx::mongo::db drop collection /name/" - Returning status from "nx::mongo::db remove" as success (0 or 1) - Adjust to object interface - Reduce verbosity - Add error messages for slot lookup failures Updated MongoDB interface - Upgraded to c-driver 0.7.1 - Zested with MongoDB 2.4.4-pre - New commands: * mongo::run * mongo::cursor::find * mongo::cursor::next * mongo::cursor::close - Adapted interface for c-driver 0.7.1 (e.g. new optional name for mongo::index) * Improved Code Quality: - Fixed functional bugs: * Copy did not copy aliases and ensemble methods * Automatic object destroy for half-baked objects (when e.g. configure raises an exception) * Handling of required configure parameters on later calls to "configure" * Fixed potential infinite loop in pattern matching for precedence lists * Handling of full-qualified name patterns for private slots * Invalidation of per-object parameter cache - on mixin changes and - on deletion/adding of per-object slots * Handle cyclical superclass/class dependencies during object system finalize * Fixed a bad interaction between Tcl's apply (pushing lambda frames) and the variable resolvers. The variable resolver was not able to resolve variables, while the command resolver was still working correctly. * Don't allow "method" to overwrite child objects - Fixed potential crashes: * Avoid crash on object destroy, when the same wrapper-less aliases was registered on more than one object/class * Fix crash on "nsf::my", when this is called with a single argument outside an object context (many thanks to Markus Moser for reporting) * Avoid crash in case NsfParameterGetCmd() is passed a plain value (without a parameter spec) * Fix potential crash in method caching when commands are renamed by Tcl (many thanks to Arthur Schreiber for reporting) - More code cleanup and refactoring - Released version runs all regression tests without memory leaks with Tcl 8.5.14 and Tcl 8.6.0 - Build system: * Improved compatibility for windows compilations. NX can now be compiled under windows with the native window tool chain (VC11) and as well with MinGW (Many thanks to Stephan Adelsberger) * Update to latest TEA * Follow new naming convention for auto-tools (using configure.ac) * Fix compilation when DTrace is activated (missing parenthesis, many thanks to Victor Guerra for noticing) * Added compile macro NSF_STACKCHECK to provide stack monitoring/debugging (especially useful for multi- threaded programs, where stack is more limited) - Fix compilation when compiled without threads (many thanks for r.Zaumseil for noting this) * Improved documentation - Fixed typos, improve wordings - Updated tutorial and migration guide - Use slashes in generated syntax to distinguish between constants and placeholders. Example: /obj/ info method definition /methodName/ * Extended regression tests MORE INFO General and more detailed information about the Next Scripting Framework and its components can be found at http://next-scripting.org doc/Announce2.0b3000066400000000000000000000046071242365656200140030ustar00rootroot00000000000000Dear Community, We are pleased to announce the availability of the Next Scripting Framework 2.0b3 which can be obtained from http://next-scripting.org Best regards - Gustaf Neumann - Stefan Sobernig =============================================== Announcing NSF 2.0b3 ************************* Major changes relative to NSF 2.0b2 are: * Additional Features: - object parameters: call method "assign" on slot objects, when it is provided - include "-returns" in introspection for aliases and scripted methods. New subcommand "info method returns" for nx. - better compatibility with XOTcl 1.*: * invocation of objects via method interface follows per default the XOTcl 1.* conventions. In order to obtain the semantics of ensemble objects, the object properties "keepcallerself" and "perobjectdispatch" have to be used. * better handling of XOTcl 1.* configure commands (starting with a "-") when list property is lost (Many thanks to Zoran Vasjljevic for the good example) * use different version numbers in serializer to avoid mixups between XOTcl 1 and XOTcl 2 when both packages are installed * Improved Code Quality: - improved performance * rewrite of alias handling (alias reform) * improved branch prediction * significant speedup for handling large numbers of subclasses (subclass reform), avoid repetitious computations * significant speedup on mass-destroy (e.g. on thread exit) - provide better protection for deletion/overwriting of base classes - fixed potential duplicates on diamond inheritance - fixed unknown handling for "-local" and "-system" dispatches - improved compatibility for windows compilations (c89, __WIN32__) - fixed potential crashes * during method caching * error messages * implicit deletions - follow Tcl convention on error messages (start always with lower case) - better handling of multiple errors in a single command. - return GMT dates in Httpd as RFCs require * Improved documentation - fixed typos, improve wordings * Extended regression tests MORE INFO General and more detailed information about the Next Scripting Framework and its components can be found at http://next-scripting.org doc/Announce2.0b5000066400000000000000000000224731242365656200140060ustar00rootroot00000000000000Dear Community, Since releasing the Next Scripting Framework (NSF) 2.0b3, we have received feedback from early adopters. Many thanks for the helpful and the constructive comments! This feedback triggered an internal discussion and led to revising some earlier design decisions and naming conventions, especially in NX. The new naming conventions improve the orthogonality and the clarity of NX programs. Since the release of 2.0b3, there have been more than 250 commits to our code repository. The implementation is very stable and has been used for more than two years in production of our large-scale, multi-threaded web environment inside NaviServer. Most of the changes happened in NX and, therefore, on the NSF scripting level, without the need to modify the NSF C layer. The implementation of XOTcl 2 has changed very little. The Next Scripting Framework was tested with Tcl 8.5.14 and Tcl 8.6.0 on Linux, Mac OS X, and in windows enviroments (MinGW, VC11). This beta-release is supposed to be the last release before the final 2.0 is out, which should be soon. Below are the most notable differences in 2.0b5 as compared to 2.0b3: a) NX 2.0b3 used the following conventions to define methods for instances, object-specific methods and class-object specific methods: /cls/ method foo {args} {...} /obj/ method bar {args} {...} /cls/ class method baz {args} {...} Introspection was possible via (in the same order): /cls/ info methods /obj/ info methods /cls/ class info methods The problem with this convention is that e.g. "info methods" operates on different method records, depending on whether it is called on a class or on an object. This breaks a basic inheritance contract with the programmer: As nx::Class is a specialization of the most general class nx::Object, the same introspection operation (e.g., "info methods") should return e.g. object-specific methods for both class objects and ordinary, non-class objects. Therefore, we adopted the following more orthogonal conventions to define methods for instances and for object-specific methods /cls/ method foo {args} {...} /obj/ object method bar {args} {...} Introspection: /cls/ info methods /obj/ info object methods Note that we can now use the same mechanism to define or query object-specific methods on objects and classes. The same applies for aliases, forwards, mixins, and filters. The new convention imposes a little typing burden for the code writer, but reduces the potential ambiguity for the code reader, who is trying to understand what exactly "$x method FOO {args} {...}" means. For convenience, we provide two packages "nx::plain-object-method" and "nx::class-method" to switch to the old conventions. A verbose tracing mode can report usages to ease migration. b) Parametrization: NX 2.0b3 followed the XOTcl conventions of registering by default same-named getter/setter methods for configuration parameters used in object creation. These getter/setter methods bloat the method interface and risk shadowing inherited methods, leading to unexpected behaviors for beginners. NX 2.0b5 adopts a Tk/itcl/... idiom by offering a cget/configure interface to objects as generic getters/setters. To obtain parameter-specific getters/setters (i.e., the old behavior), the flag "-accessor public|protected|private" can be set when defining properties and variables. c) Further clean-ups of the introspection interface ("info"). In order to streamline the interface further, we followed the idea to use "... info /plural word/" to obtain a set of handles, and then to make a separate call to retrieve the details. Therefore, we now provide ... /cls/ info methods /obj/ info object methods /cls/ info variables /obj/ info object variables /cls/ info slots /obj/ info object slots /cls/ info method parameters /methodName/ /obj/ info object method parameters /methodName/ /cls/ info configure parameters /obj/ info lookup configure parameters ... to return a list of handles. The result list can be filtered in each case by specifying a match pattern. Each result handle can then be used in a separate call to obtain details: /obj/ info method definition /methodHandle/ /obj/ info variable definition /varHandle/ /obj/ info parameter name /paramHandle/ These are just a few examples. In NX 2.0b3, we had e.g. "... info parameter definitions ..." leaving a beginner in the dark about the parameters actually meant. Also, the introspection interface made mixed use of plural and singular wordings for different purposes (e.g., retrieving collections and/or detailed information on one item). Below is a more detailed summary of the changes. The Next Scripting Framework 2.0b5 (containing NX and XOTcl 2.0b5) can be obtained from http://next-scripting.org/ Best regards - Gustaf Neumann - Stefan Sobernig =============================================== Announcing NSF 2.0b5 ************************* Major changes relative to NSF 2.0b3 are (in addition of the items (a), (b), and (c) above) are: * Additional Features: - Serializer: * Added flag -objmap to Serializer method deepSerialize to make serializer to provide mapping only for object names. This makes the serializer usable for object/class copying (-map is too coarse) * Made "ignore" method public * Due to the split between serializer and object system serializer, the "ignore" settings were lost - Allow explicit unsetting of -per-object flag in 0-argument "-flag=value" notation (all built-in commands accepting this flag) - Better compatibility with XOTcl 1.*: - Added "/obj/ info name" (as alternative to "namespace tail [self]") - Test-suite: added summary with statistics - Traits: added property/variable inheritance - MongoDB - Added "nx::mongo::db drop collection /name/" - Returning status from "nx::mongo::db remove" as success (0 or 1) - Adjust to object interface - Reduce verbosity - Add error messages for slot lookup failures Updated MongoDB interface - Upgraded to c-driver 0.7.1 - Zested with MongoDB 2.4.4-pre - New commands: * mongo::run * mongo::cursor::find * mongo::cursor::next * mongo::cursor::close - Adapted interface for c-driver 0.7.1 (e.g. new optional name for mongo::index) * Improved Code Quality: - Fixed functional bugs: * Copy did not copy aliases and ensemble methods * Automatic object destroy for half-baked objects (when e.g. configure raises an exception) * Handling of required configure parameters on later calls to "configure" * Fixed potential infinite loop in pattern matching for precedence lists * Handling of full-qualified name patterns for private slots * Invalidation of per-object parameter cache - on mixin changes and - on deletion/adding of per-object slots * Handle cyclical superclass/class dependencies during object system finalize * Fixed a bad interaction between Tcl's apply (pushing lambda frames) and the variable resolvers. The variable resolver was not able to resolve variables, while the command resolver was still working correctly. * Don't allow "method" to overwrite child objects - Fixed potential crashes: * Avoid crash on object destroy, when the same wrapper-less aliases was registered on more than one object/class * Fix crash on "nsf::my", when this is called with a single argument outside an object context (many thanks to Markus Moser for reporting) * Avoid crash in case NsfParameterGetCmd() is passed a plain value (without a parameter spec) * Fix potential crash in method caching when commands are renamed by Tcl (many thanks to Arthur Schreiber for reporting) - More code cleanup and refactoring - Released version runs all regression tests without memory leaks with Tcl 8.5.14 and Tcl 8.6.0 - Build system: * Improved compatibility for windows compilations. NX can now be compiled under windows with the native window tool chain (VC11) and as well with MinGW (Many thanks to Stephan Adelsberger) * Update to latest TEA * Follow new naming convention for auto-tools (using configure.ac) * Fix compilation when DTrace is activated (missing parenthesis, many thanks to Victor Guerra for noticing) * Added compile macro NSF_STACKCHECK to provide stack monitoring/debugging (especially useful for multi- threaded programs, where stack is more limited) - Fix compilation when compiled without threads (many thanks for r.Zaumseil for noting this) * Improved documentation - Fixed typos, improve wordings - Updated tutorial and migration guide - Use slashes in generated syntax to distinguish between constants and placeholders. Example: /obj/ info method definition /methodName/ * Extended regression tests MORE INFO General and more detailed information about the Next Scripting Framework and its components can be found at http://next-scripting.org doc/Class.3000066400000000000000000001503061242365656200127740ustar00rootroot00000000000000'\" '\" Generated from file 'Class.man' by tcllib/doctools with format 'nroff' '\" Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). '\" '\" The definitions below are for supplemental macros used in Tcl/Tk '\" manual entries. '\" '\" .AP type name in/out ?indent? '\" Start paragraph describing an argument to a library procedure. '\" type is type of argument (int, etc.), in/out is either "in", "out", '\" or "in/out" to describe whether procedure reads or modifies arg, '\" and indent is equivalent to second arg of .IP (shouldn't ever be '\" needed; use .AS below instead) '\" '\" .AS ?type? ?name? '\" Give maximum sizes of arguments for setting tab stops. Type and '\" name are examples of largest possible arguments that will be passed '\" to .AP later. If args are omitted, default tab stops are used. '\" '\" .BS '\" Start box enclosure. From here until next .BE, everything will be '\" enclosed in one large box. '\" '\" .BE '\" End of box enclosure. '\" '\" .CS '\" Begin code excerpt. '\" '\" .CE '\" End code excerpt. '\" '\" .VS ?version? ?br? '\" Begin vertical sidebar, for use in marking newly-changed parts '\" of man pages. The first argument is ignored and used for recording '\" the version when the .VS was added, so that the sidebars can be '\" found and removed when they reach a certain age. If another argument '\" is present, then a line break is forced before starting the sidebar. '\" '\" .VE '\" End of vertical sidebar. '\" '\" .DS '\" Begin an indented unfilled display. '\" '\" .DE '\" End of indented unfilled display. '\" '\" .SO '\" Start of list of standard options for a Tk widget. The '\" options follow on successive lines, in four columns separated '\" by tabs. '\" '\" .SE '\" End of list of standard options for a Tk widget. '\" '\" .OP cmdName dbName dbClass '\" Start of description of a specific option. cmdName gives the '\" option's name as specified in the class command, dbName gives '\" the option's name in the option database, and dbClass gives '\" the option's class in the option database. '\" '\" .UL arg1 arg2 '\" Print arg1 underlined, then print arg2 normally. '\" '\" RCS: @(#) $Id: man.macros,v 1.1 2009/01/30 04:56:47 andreas_kupries Exp $ '\" '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. .if t .wh -1.3i ^B .nr ^l \n(.l .ad b '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ta \\n()Au \\n()Bu .ie !"\\$3"" \{\ \&\\$1 \\fI\\$2\\fP (\\$3) .\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n .\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. .AS Tcl_Interp Tcl_CreateInterp in/out '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ .\" Draw four-sided box normally, but don't draw top of .\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .if !"\\$2"" .br .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ .\" Draw three-sided box if this is the box's first page, .\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .nf .sp .. '\" # DE - end display .de DE .fi .RE .sp .. '\" # SO - start of list of standard options .de SO .SH "STANDARD OPTIONS" .LP .nf .ta 4c 8c 12c .ft B .. '\" # SE - end of list of standard options .de SE .fi .ft R .LP See the \\fBoptions\\fR manual entry for details on the standard options. .. '\" # OP - start of full description for a single option .de OP .LP .nf .ta 4c Command-Line Name: \\fB\\$1\\fR Database Name: \\fB\\$2\\fR Database Class: \\fB\\$3\\fR .fi .. '\" # CS - begin code excerpt .de CS .RS .nf .ta .25i .5i .75i 1i .. '\" # CE - end code excerpt .de CE .fi .RE .. .de UL \\$1\l'|0\(ul'\\$2 .. .TH "nx::Class" 3 2.0 Class "" .BS .SH NAME nx::Class \- API reference of the base-metaclass of the NX objectsystem .SH SYNOPSIS \fBnx::Class\fR \fBcreate\fR \fIcls\fR ?\fB-superclasses\fR \fIsuperClassNames\fR? ?\fB-mixins\fR \fImixinSpec\fR? ?\fB-filters\fR \fIfilterSpec\fR? ?\fIoption\fR \fIvalue\fR ...? ?\fIinitBlock\fR? .sp \fBnx::Class\fR \fBnew\fR ?\fB-superclasses\fR \fIsuperClassNames\fR? ?\fB-mixins\fR \fImixinSpec\fR? ?\fB-filters\fR \fIfilterSpec\fR? ?\fIinitBlock\fR? .sp \fIcls\fR ?\fBpublic\fR | \fBprivate\fR | \fBprotected\fR? \fB alias\fR \fImethodName\fR ?\fB-returns\fR \fIvalueChecker\fR? ?\fB-frame\fR \fBobject\fR | \fBmethod\fR? \fIcmdName\fR .sp \fIcls\fR \fBcreate\fR \fIinstanceName\fR ?\fIoption\fR \fIvalue\fR \fIoption\fR \fIvalue\fR ...? .sp \fIcls\fR \fBdelete\fR \fIfeature\fR \fIarg\fR .sp \fIcls\fR \fB\fR \fBfilters\fR \fIsubmethod\fR ?\fIarg\fR ...? .sp \fIcls\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fB forward\fR \fImethodName\fR ?\fB-prefix\fR \fIprefixName\fR? ?\fB-frame\fR \fBobject\fR? ?\fB-returns\fR \fIvalueChecker\fR? ?\fB-verbose\fR? ?\fItarget\fR? ?\fIarg\fR ...? .sp \fIcls\fR \fBinfo heritage\fR ?\fIpattern\fR? .sp \fIcls\fR \fBinfo instances\fR ?\fB-closure\fR? ?\fIpattern\fR? .sp \fIcls\fR \fBinfo mixinof\fR ?\fB-closure\fR? ?\fB-scope\fR \fIoption\fR? ?\fIpattern\fR? .sp \fIcls\fR \fBinfo subclasses\fR ?\fB-closure\fR? ?\fB-dependent\fR? ?\fIpattern\fR? .sp \fIcls\fR \fBinfo superclasses\fR ?\fB-closure\fR? ?\fIpattern\fR? .sp \fIcls\fR \fBinfo info\fR ?\fB-asList\fR? .sp \fIcls\fR \fBinfo filters\fR ?\fB-guards\fR? ?\fIpattern\fR? .sp \fIcls\fR \fBinfo method\fR \fIoption\fR \fImethodName\fR .sp \fIcls\fR \fBinfo methods\fR ?\fB-callprotection\fR \fIlevel\fR? ?\fB-type\fR \fImethodType\fR? ?\fB-path\fR? ?\fInamePattern\fR? .sp \fIcls\fR \fBinfo mixins\fR ?\fB-guards\fR? ?\fIpattern\fR? .sp \fIcls\fR \fBinfo slots\fR ?\fB-type\fR \fIclassName\fR? ?\fIpattern\fR? .sp \fIcls\fR \fBinfo variables\fR ?\fIpattern\fR? .sp \fIcls\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fB method\fR \fIname\fR \fIparameters\fR ?\fB-checkalways\fR? ?\fB-returns\fR \fIvalueChecker\fR? \fIbody\fR .sp \fIcls\fR \fB mixins\fR \fIsubmethod\fR ?\fIarg\fR ...? .sp \fIcls\fR \fBnew\fR ?\fB-childof\fR \fIparentName\fR? ?\fIoption\fR \fIvalue\fR \fIoption\fR \fIvalue\fR ...? .sp \fIcls\fR \fBproperty\fR ?\fB-accessor\fR \fBpublic\fR | \fBprotected\fR | \fBprivate\fR? ?\fB-configurable\fR \fItrueFalse\fR? ?\fB-incremental\fR? ?\fB-class\fR \fIclassName\fR? \fIspec\fR ?\fIinitBlock\fR? .sp \fIcls\fR \fBrequire\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fB method\fR \fImethodName\fR .sp \fIcls\fR \fBvariable\fR ?\fB-accessor\fR \fBpublic\fR | \fBprotected\fR | \fBprivate\fR? ?\fB-incremental\fR? ?\fB-class\fR \fIclassName\fR? ?\fB-configurable\fR \fItrueFalse\fR? ?\fB-initblock\fR \fIscript\fR? \fIspec\fR ?\fIdefaultValue\fR? .sp .BE .SH DESCRIPTION .PP \fBnx::Class\fR is the base metaclass of the NX object system. All classes (e.g. \fIcls\fR) are (direct or indirect) instances of \fBnx::Class\fR. Therefore, the methods provided by \fBnx::Class\fR are available to all classes. A class \fIcls\fR which does not have \fBnx::Class\fR as its direct or indirect superclass is referred to as an \fIapplication class\fR. By default, when instantiating a new class from \fBnx::Class\fR, it becomes an application class with \fBnx::Object\fR being set as its superclass. A class \fIcls\fR which is explicitly declared as a (direct or indirect) subclass of \fBnx::Class\fR is referred to as a \fImetaclass\fR, that is, its instances will become classes as well. In other words, a metaclass instantiates and subclasses \fBnx::Class\fR at the same time. .CS +---------+ | ::nx::* | +---------+--------------------------------------Y | | | instance of | | .-------. | | +--------'+ instance of +----------+ | | | |<....................| | | | | Class | | Object | | | | |....................>| | | | +---------+ subclass of +-----+----+ | | ^ ^ ^ | \\...|...|................................|......./ | | | | |subclass.....(xor)......subclass| | |of +-----------+ of| | |.........| |..........| | (metaclass) | /cls/ | (application class) |.............| | instance of +-----------+ .CE Classes can be created in the following ways: .TP \fBnx::Class\fR \fBcreate\fR \fIcls\fR ?\fB-superclasses\fR \fIsuperClassNames\fR? ?\fB-mixins\fR \fImixinSpec\fR? ?\fB-filters\fR \fIfilterSpec\fR? ?\fIoption\fR \fIvalue\fR ...? ?\fIinitBlock\fR? To create a class having the explicit name \fIcls\fR, use \fBcreate\fR. .TP \fBnx::Class\fR \fBnew\fR ?\fB-superclasses\fR \fIsuperClassNames\fR? ?\fB-mixins\fR \fImixinSpec\fR? ?\fB-filters\fR \fIfilterSpec\fR? ?\fIinitBlock\fR? To create a class having an automatically assigned, implicit name, use \fBnew\fR. .PP The configuration options for direct and indirect instances of \fBnx::Class\fR, which can be passed when calling \fBcreate\fR and \fBnew\fR, are documented in the subsequent section. .SH "CONFIGURATION OPTIONS FOR INSTANCES OF NX::CLASS" .PP Configuration options can be used for configuring objects during their creation by passing the options as non-positional arguments into calls of \fBnew\fR and \fBcreate\fR (see \fBnx::Class\fR). An existing object can be queried for its current configuration using \fBcget\fR and it can be re-configured using \fBconfigure\fR. .TP \fB-superclasses\fR ?\fIsuperClassNames\fR? If \fIsuperClassNames\fR is not specified, returns the superclasses of the class. If provided, the class becomes the subclass of \fIsuperClassNames\fR. .TP \fB-filters\fR ?\fIfilterSpecs\fR? Retrieves the list of filter methods currently active on instances of the class, if \fIfilterSpecs\fR is not set. Otherwise, activates a list of filter methods for the instances of the class. Filters are returned or set in terms of a list of filter specifications. .TP \fB-mixins\fR ?\fImixinSpecs\fR? Returns the list of mixin classes currently active on instances of the class, if \fImixinSpecs\fR is not specified. Otherwise, the class is extended by the list of mixin classes provided by \fImixinSpecs\fR. mixin classes are returned or set in terms of a list of mixin specifications. .PP The configuration options provided by \fBnx::Object\fR are equally available because an application class \fIcls\fR is an indirect instance of \fBnx::Object\fR. .SH "METHODS FOR INSTANCES OF NX::CLASS" .TP \fBalias\fR .RS .TP \fIcls\fR ?\fBpublic\fR | \fBprivate\fR | \fBprotected\fR? \fB alias\fR \fImethodName\fR ?\fB-returns\fR \fIvalueChecker\fR? ?\fB-frame\fR \fBobject\fR | \fBmethod\fR? \fIcmdName\fR Define an alias method for the given class. The resulting method registers a pre-existing Tcl command \fIcmdName\fR under the (alias) name \fImethodName\fR with the class. If \fIcmdName\fR refers to another \fBmethod\fR, the corresponding argument should be a valid method handle. If a Tcl command (e.g., a \fBproc\fR), the argument should be a fully qualified Tcl command name. If aliasing a subcommand (e.g., \fBarray exists\fR) of a Tcl namespace ensemble (e.g., \fBarray\fR), \fIcmdName\fR must hold the fully qualified subcommand name (and not the ensemble name of the subcommand). .sp As for a regular \fBclass method\fR, \fB-returns\fR allows for setting a value checker on the values returned by the aliased command \fIcmdName\fR. .sp When creating an alias method for a \fIC-implemented\fR Tcl command (i.e., command defined using the Tcl/NX C-API), \fB-frame\fR sets the scope for variable references used in the aliased command. If the provided value is \fBobject\fR, then variable references will be resolved in the context of the called object, i.e., the object upon which the alias method is invoked, as if they were object variables. There is no need for using the colon-prefix notation for identifying object variables. If the value is \fBmethod\fR, then the aliased command will be executed as a regular method call. The command is aware of its called-object context; i.e., it can resolve \fB::nx::self\fR. In addition, the alias method has access to the method-call context (e.g., \fBnx::next\fR). If \fB-frame\fR is omitted, and by default, the variable references will resolve in the context of the caller of the alias method. .RE .TP \fB__class_configureparameter\fR .RS .TP \fIcls\fR \fB__class_configureparameter\fR Computes and returns the configuration options available for \fIcls\fR instances, to be consumed as method-parameter specification by \fBconfigure\fR. .RE .TP \fBcreate\fR .RS .TP \fIcls\fR \fBcreate\fR \fIinstanceName\fR ?\fIoption\fR \fIvalue\fR \fIoption\fR \fIvalue\fR ...? This factory method creates an instance \fIinstanceName\fR of \fIcls\fR and returns \fIinstanceName\fR. .CS % nx::Class create AClass { :method init args { next }; # initialization method for instances of 'AClass' }; # defines a class 'AClass' being an instance of 'nx::Class' ::AClass % ::AClass create anInstance; # defines an object 'anInstance' being an instance of 'AClass' ::anInstance % ::anInstance info class ::AClass % ::AClass info class ::nx::Class .CE .IP \fBcreate\fR accepts the configuration options \fIoption\fR available for this instance, such as those defined by properties of \fIcls\fR (see \fBproperty\fR). .sp Note that \fBcreate\fR is called internally when defining an instance of \fIcls\fR using \fBnew\fR. .sp By calling \fBcreate\fR on \fBnx::Class\fR itself, the created instance will become a new application class \fIinstanceName\fR on which \fBcreate\fR can also be applied (i.e., it can be instantiated). If the so-created class has \fB::nx::Class\fR its direct or indirect superclass, \fIinstanceName\fR is referred to as a metaclass; that is, a class whose instances are again classes. .RE .TP \fBdelete\fR .RS .TP \fIcls\fR \fBdelete\fR \fIfeature\fR \fIarg\fR This method serves as the equivalent to Tcl's \fBrename\fR for removing structural (properties, variables) and behavioral features (methods) of the class: .TP \fIcls\fR \fBdelete property\fR \fIpropertyName\fR .TP \fIcls\fR \fBdelete variable\fR \fIvariableName\fR .TP \fIcls\fR \fBdelete method\fR \fImethodName\fR Removes a property \fIpropertyName\fR, variable \fIvariableName\fR, and method \fImethodName\fR, respectively, previously defined for the scope of the class. .sp \fBdelete method\fR can be equally used for removing regular methods (see \fB method\fR), an alias method (see \fB alias\fR), and a forwarder method (see \fB forward\fR). .RE .TP \fBfilters\fR .RS .TP \fIcls\fR \fB\fR \fBfilters\fR \fIsubmethod\fR ?\fIarg\fR ...? Accesses and modifies the list of methods which are registered as filters with \fIcls\fR using a specific setter or getter \fIsubmethod\fR: .RS .TP \fIcls\fR \fB\fR \fBfilters add\fR \fIspec\fR ?\fIindex\fR? Inserts a single filter into the current list of filters of \fIcls\fR. Using \fIindex\fR, a position in the existing list of filters for inserting the new filter can be set. If omitted, \fIindex\fR defaults to the list head (0). .TP \fIcls\fR \fB\fR \fBfilters clear\fR Removes all filters from \fIcls\fR and returns the list of removed filters. Clearing is equivalent to passing an empty list for \fIfilterSpecList\fR to \fBclass\fR \fBfilter set\fR. .TP \fIcls\fR \fB\fR \fBfilters delete\fR ?\fB-nocomplain\fR? \fIspecPattern\fR Removes a single filter from the current list of filters of \fIcls\fR whose spec matches \fIspecPattern\fR. \fIspecPattern\fR can contain special matching chars (see \fBstring match\fR). \fBclass\fR \fBfilters delete\fR will throw an error if there is no matching filter, unless \fB-nocomplain\fR is set. .TP \fIcls\fR \fB\fR \fBfilters get\fR Returns the list of current filter specifications registered for \fIcls\fR. .TP \fIcls\fR \fB\fR \fBfilters guard\fR \fImethodName\fR ?\fIexpr\fR? If \fIexpr\fR is specified, registers a guard expression \fIexpr\fR with a filter \fImethodName\fR. This requires that the filter \fImethodName\fR has been previously set using \fB\fR \fBfilters set\fR or added using \fB\fR \fBfilters add\fR. \fIexpr\fR must be a valid Tcl expression (see \fBexpr\fR). An empty string for \fIexpr\fR will clear the currently registered guard expression for filter \fImethodName\fR. .sp If \fIexpr\fR is omitted, returns the guard expression set on the filter \fImethodName\fR defined for \fIcls\fR. If none is available, an empty string will be returned. .TP \fIcls\fR \fB\fR \fBfilters methods\fR ?\fIpattern\fR? If \fIpattern\fR is omitted, returns all filter names which are defined by \fIcls\fR. By specifying \fIpattern\fR, the returned filters can be limited to those whose names match \fIpatterns\fR (see \fBstring match\fR). .TP \fIcls\fR \fB\fR \fBfilters set\fR \fIfilterSpecList\fR \fIfilterSpecList\fR takes a list of filter specs, with each spec being itself either a one-element or a two-element list: \fImethodName\fR ?-guard \fIguardExpr\fR?. \fImethodName\fR identifies an existing method of \fIcls\fR which becomes registered as a filter. If having three elements, the third element \fIguardExpr\fR will be stored as a guard expression of the filter. This guard expression must be a valid Tcl expression (see \fBexpr\fR). \fIexpr\fR is evaluated when \fIcls\fR receives a message to determine whether the filter should intercept the message. Guard expressions allow for realizing context-dependent or conditional filter composition. .RE .IP Every \fImethodName\fR in a \fIspec\fR must resolve to an existing method in the scope of the class. To access and to manipulate the list of filters of \fIcls\fR, \fBcget\fR|\fBconfigure\fR \fB-filters\fR can also be used. .RE .TP \fBforward\fR .RS .TP \fIcls\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fB forward\fR \fImethodName\fR ?\fB-prefix\fR \fIprefixName\fR? ?\fB-frame\fR \fBobject\fR? ?\fB-returns\fR \fIvalueChecker\fR? ?\fB-verbose\fR? ?\fItarget\fR? ?\fIarg\fR ...? Define a forward method for the given class. The definition of a forward method registers a predefined, but changeable list of forwarder arguments under the (forwarder) name \fImethodName\fR. Upon calling the forward method, the forwarder arguments are evaluated as a Tcl command call. That is, if present, \fItarget\fR is interpreted as a Tcl command (e.g., a Tcl \fBproc\fR or an object) and the remainder of the forwarder arguments \fIarg\fR as arguments passed into this command. The actual method arguments to the invocation of the forward method itself are appended to the list of forwarder arguments. If \fItarget\fR is omitted, the value of \fImethodName\fR is implicitly set and used as \fItarget\fR. This way, when providing a fully-qualified Tcl command name as \fImethodName\fR without \fItarget\fR, the unqualified \fImethodName\fR (\fBnamespace tail\fR) is used as the forwarder name; while the fully-qualified one serves as the \fItarget\fR. .sp As for a regular \fB method\fR, \fB-returns\fR allows for setting a value checker on the values returned by the resulting Tcl command call. When passing \fBobject\fR to \fB-frame\fR, the resulting Tcl command is evaluated in the context of the object receiving the forward method call. This way, variable names used in the resulting execution of a command become resolved as object variables. .sp The list of forwarder arguments \fIarg\fR can contain as its elements a mix of literal values and placeholders. Placeholders are prefixed with a percent symbol (%) and substituted for concrete values upon calling the forward method. These placeholders allow for constructing and for manipulating the arguments to be passed into the resulting command call on the fly: .RS .IP \(bu \fB%method\fR becomes substituted for the name of the forward method, i.e. \fImethodName\fR. .IP \(bu \fB%self\fR becomes substituted for the name of the object receiving the call of the forward method. .IP \(bu \fB%1\fR becomes substituted for the first method argument passed to the call of forward method. This requires, in turn, that \fIat least\fR one argument is passed along with the method call. .sp Alternatively, \fB%1\fR accepts an optional argument \fIdefaults\fR: {\fB%1\fR \fIdefaults\fR}. \fIdefaults\fR must be a valid Tcl list of two elements. For the first element, \fB%1\fR is substituted when there is no first method argument which can be consumed by \fB%1\fR. The second element is inserted upon availability of a first method argument with the consumed argument being appended right after the second list element. This placeholder is typically used to define a pair of getter/setter methods. .IP \(bu {\fB%@\fR\fIindex\fR \fIvalue\fR} becomes substituted for the specified \fIvalue\fR at position \fIindex\fR in the forwarder-arguments list, with \fIindex\fR being either a positive integer, a negative integer, or the literal value \fBend\fR (such as in Tcl's \fBlindex\fR). Positive integers specify a list position relative to the list head, negative integers give a position relative to the list tail. Indexes for positioning placeholders in the definition of a forward method are evaluated from left to right and should be used in ascending order. .sp Note that \fIvalue\fR can be a literal or any of the placeholders (e.g., \fB%method\fR, \fB%self\fR). Position prefixes are exempted, they are evaluated as \fB%\fR\fIcmdName\fR-placeholders in this context. .IP \(bu {\fB%argclindex\fR \fIlist\fR} becomes substituted for the \fIn\fRth element of the provided \fIlist\fR , with \fIn\fR corresponding to the number of method arguments passed to the forward method call. .IP \(bu \fB%%\fR is substituted for a single, literal percent symbol (%). .IP \(bu \fB%\fR\fIcmdName\fR is substituted for the value returned from executing the Tcl command \fIcmdName\fR. To pass arguments to \fIcmdName\fR, the placeholder should be wrapped into a Tcl \fBlist\fR: {\fB%\fR\fIcmdName\fR ?\fIarg\fR ...?}. .sp Consider using fully-qualified Tcl command names for \fIcmdName\fR to avoid possible name conflicts with the predefined placeholders, e.g., \fB%self\fR vs. %\fB::nx::self\fR. .RE .sp To disambiguate the names of subcommands or methods, which potentially become called by a forward method, a prefix \fIprefixName\fR can be set using \fB-prefix\fR. This prefix is prepended automatically to the argument following \fItarget\fR (i.e., a second argument), if present. If missing, \fB-prefix\fR has no effect on the forward method call. .sp To inspect and to debug the conversions performed by the above placeholders, setting the switch \fB-verbose\fR will have the command list to be executed (i.e., after substitution) printed using \fB::nsf::log\fR (debugging level: \fBnotice\fR) upon calling the forward method. .RE .TP \fBinfo\fR A collection of introspection submethods on the structural features (e.g. configuration options, superclasses) and the behavioral features (e.g. methods, filters) provided by \fIcls\fR to its instances. .RS .TP \fIcls\fR \fBinfo heritage\fR ?\fIpattern\fR? If \fIpattern\fR is omitted, returns the list of object names of all the direct and indirect superclasses and \fIper-class\fR mixin classes of \fIcls\fR, in their order of precedence, which are active for instances of \fIcls\fR. If \fIpattern\fR is specified, only superclasses and mixin classes whose names match \fIpattern\fR will be listed (see \fBstring match\fR). .TP \fIcls\fR \fBinfo instances\fR ?\fB-closure\fR? ?\fIpattern\fR? If \fIpattern\fR is not specified, returns a list of the object names of all the direct instances of \fIcls\fR. If the switch \fB-closure\fR is set, indirect instances are also returned. A direct instance is created by using \fBcreate\fR or \fBnew\fR on \fIcls\fR, an indirect instance was created from a direct or indirect subclass of \fIcls\fR. If \fIpattern\fR is specified, only instances whose names match \fIpattern\fR will be listed (see \fBstring match\fR). .TP \fIcls\fR \fBinfo mixinof\fR ?\fB-closure\fR? ?\fB-scope\fR \fIoption\fR? ?\fIpattern\fR? If \fIpattern\fR is not specified, returns a list of the object names of all the objects for which \fIcls\fR is active as a direct mixin class. If the switch \fB-closure\fR is set, objects which have \fIcls\fR as an indirect mixin class are also returned. If \fIpattern\fR is specified, only objects whose names match \fIpattern\fR will be listed (see \fBstring match\fR). Valid values of \fIoption\fR are \fBall\fR, \fBobject\fR, and \fBclass\fR. Passing \fBobject\fR will have only objects returned which have \fIcls\fR as \fIper-object\fR mixin class. Passing \fBclass\fR will have only classes returned which have \fIcls\fR as \fIper-class\fR mixin class. \fBall\fR (the default) will have contained both in the returned list. .TP \fIcls\fR \fBinfo subclasses\fR ?\fB-closure\fR? ?\fB-dependent\fR? ?\fIpattern\fR? If \fIpattern\fR is not specified, returns a list of the object names of the direct subclasses of \fIcls\fR. If the switch \fB-closure\fR is set, indirect subclasses are also returned. If the switch \fB-dependent\fR is on, indirect subclasses introduced by mixin class relations of subclasses of \fIcls\fR are also reported. \fB-closure\fR and \fB-dependent\fR are mutually exclusive. If \fIpattern\fR is specified, only subclasses whose names match \fIpattern\fR will be listed (see \fBstring match\fR). .TP \fIcls\fR \fBinfo superclasses\fR ?\fB-closure\fR? ?\fIpattern\fR? If \fIpattern\fR is not specified, returns a list of the object names of all direct superclasses of \fIcls\fR. If the switch \fB-closure\fR is set, indirect superclasses will also be returned. If \fIpattern\fR is specified, only superclasses whose names match \fIpattern\fR will be listed (see \fBstring match\fR). .TP \fIcls\fR \fBinfo info\fR ?\fB-asList\fR? Returns the available submethods of the \fBinfo\fR method ensemble for \fIcls\fR, either as a pretty-printed string or as a Tcl list (if the switch \fB-asList\fR is set) for further processing. .TP \fIcls\fR \fBinfo filters\fR ?\fB-guards\fR? ?\fIpattern\fR? If \fIpattern\fR is omitted, returns all filter names which are defined by \fIcls\fR. By turning on the switch \fB-guards\fR, the corresponding guard expressions, if any, are also reported along with each filter as a three-element list: \fIfilterName\fR -guard \fIguardExpr\fR. By specifying \fIpattern\fR, the returned filters can be limited to those whose names match \fIpatterns\fR (see \fBstring match\fR). .TP \fIcls\fR \fBinfo method\fR \fIoption\fR \fImethodName\fR This introspection submethod provides access to the details of \fImethodName\fR provided by \fIcls\fR. Permitted values for \fIoption\fR are: .RS .IP \(bu \fBargs\fR returns a list containing the parameter names of \fImethodName\fR, in order of the method-parameter specification. .IP \(bu \fBbody\fR returns the body script of \fImethodName\fR. .IP \(bu \fBdefinition\fR returns a canonical command list which allows for (re-)define \fImethodName\fR. .IP \(bu \fBdefinitionhandle\fR returns the method handle for a submethod in a method ensemble from the perspective of \fIcls\fR as method provider. \fImethodName\fR must contain a complete method path. .IP \(bu \fBexists\fR returns 1 if there is a \fImethodName\fR provided by \fIcls\fR, returns 0 otherwise. .IP \(bu \fBhandle\fR returns the method handle for \fImethodName\fR. .IP \(bu \fBorigin\fR returns the aliased command if \fImethodName\fR is an alias method, or an empty string otherwise. .IP \(bu \fBparameters\fR returns the parameter specification of \fImethodName\fR as a list of parameter names and type specifications. .IP \(bu \fBregistrationhandle\fR returns the method handle for a submethod in a method ensemble from the perspective of the method caller. \fImethodName\fR must contain a complete method path. .IP \(bu \fBreturns\fR gives the type specification defined for the return value of \fImethodName\fR. .IP \(bu \fBsubmethods\fR returns the names of all submethods of \fImethodName\fR, if \fImethodName\fR is a method ensemble. Otherwise, an empty string is returned. .IP \(bu \fBsyntax\fR returns the method parameters of \fImethodName\fR as a concrete-syntax description to be used in human-understandable messages (e.g., errors or warnings, documentation strings). .IP \(bu \fBtype\fR returns whether \fImethodName\fR is a \fIscripted\fR method, an \fIalias\fR method, a \fIforwarder\fR method, or a \fIsetter\fR method. .RE .TP \fIcls\fR \fBinfo methods\fR ?\fB-callprotection\fR \fIlevel\fR? ?\fB-type\fR \fImethodType\fR? ?\fB-path\fR? ?\fInamePattern\fR? Returns the names of all methods defined by \fIcls\fR. Methods covered include those defined using \fB alias\fR and \fB forward\fR. The returned methods can be limited to those whose names match \fInamePattern\fR (see \fBstring match\fR). .sp By setting \fB-callprotection\fR, only methods of a certain call protection \fIlevel\fR (\fBpublic\fR, \fBprotected\fR, or \fBprivate\fR) will be returned. Methods of a specific type can be requested using \fB-type\fR. The recognized values for \fImethodType\fR are: .RS .IP \(bu \fBscripted\fR denotes methods defined using \fBclass\fR \fBmethod\fR; .IP \(bu \fBalias\fR denotes alias methods defined using \fBclass\fR \fBalias\fR; .IP \(bu \fBforwarder\fR denotes forwarder methods defined using \fBclass\fR \fBforward\fR; .IP \(bu \fBsetter\fR denotes methods defined using \fB::nsf::setter\fR; .IP \(bu \fBall\fR returns methods of any type, without restrictions (also the default value); .RE .TP \fIcls\fR \fBinfo mixins\fR ?\fB-guards\fR? ?\fIpattern\fR? If \fIpattern\fR is omitted, returns the object names of the mixin classes which extend \fIcls\fR directly. By turning on the switch \fB-guards\fR, the corresponding guard expressions, if any, are also reported along with each mixin as a three-element list: \fIclassName\fR -guard \fIguardExpr\fR. The returned mixin classes can be limited to those whose names match \fIpatterns\fR (see \fBstring match\fR). .TP \fIcls\fR \fBinfo slots\fR ?\fB-type\fR \fIclassName\fR? ?\fIpattern\fR? If \fIpattern\fR is not specified, returns the object names of all slot objects defined by \fIcls\fR. The returned slot objects can be limited according to any or a combination of the following criteria: First, slot objects can be filtered based on their command names matching \fIpattern\fR (see \fBstring match\fR). Second, \fB-type\fR allows one to select slot objects which are instantiated from a subclass \fIclassName\fR of \fBnx::Slot\fR (default: \fBnx::Slot\fR). .TP \fIcls\fR \fBinfo variables\fR ?\fIpattern\fR? If \fIpattern\fR is omitted, returns the object names of all slot objects provided by \fIcls\fR which are responsible for managing properties and variables of \fIcls\fR. Otherwise, only slot objects whose names match \fIpattern\fR are returned. .sp This is equivalent to calling: \fIcls\fR \fBinfo slots\fR \fB-type\fR \fB::nx::VariableSlot\fR \fIpattern\fR. .sp To extract details of each slot object, use the \fBinfo\fR submethods available for each slot object. .RE .TP \fBmethod\fR .RS .TP \fIcls\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fB method\fR \fIname\fR \fIparameters\fR ?\fB-checkalways\fR? ?\fB-returns\fR \fIvalueChecker\fR? \fIbody\fR Defines a scripted method \fImethodName\fR for the scope of the class. The method becomes part of the class's signature interface. Besides a \fImethodName\fR, the method definition specifies the method \fIparameters\fR and a method \fIbody\fR. .sp \fIparameters\fR accepts a Tcl \fBlist\fR containing an arbitrary number of non-positional and positional parameter definitions. Each parameter definition comprises a parameter name, a parameter-specific value checker, and parameter options. .sp The \fIbody\fR contains the method implementation as a script block. In this body script, the colon-prefix notation is available to denote an object variable and a self call. In addition, the context of the object receiving the method call (i.e., the message) can be accessed (e.g., using \fBnx::self\fR) and the call stack can be introspected (e.g., using \fBnx::current\fR). .sp Optionally, \fB-returns\fR allows for setting a value checker on values returned by the method implementation. By setting the switch \fB-checkalways\fR, value checking on arguments and return value is guaranteed to be performed, even if value checking is temporarily disabled; see \fBnx::configure\fR). .sp A method closely resembles a Tcl \fBproc\fR, but it differs in some important aspects: First, a method can define non-positional parameters and value checkers on arguments. Second, the script implementing the method body can contain object-specific notation and commands (see above). Third, method calls \fIcannot\fR be intercepted using Tcl \fBtrace\fR. Note that an existing Tcl \fBproc\fR can be registered as an alias method with the class (see \fB alias\fR). .RE .TP \fBmixins\fR .RS .TP \fIcls\fR \fB mixins\fR \fIsubmethod\fR ?\fIarg\fR ...? Accesses and modifies the list of mixin classes of \fIcls\fR using a specific setter or getter \fIsubmethod\fR: .RS .TP \fIcls\fR \fB\fR \fBmixins add\fR \fIspec\fR ?\fIindex\fR? Inserts a single mixin class into the current list of mixin classes of \fIcls\fR. Using \fIindex\fR, a position in the existing list of mixin classes for inserting the new mixin class can be set. If omitted, \fIindex\fR defaults to the list head (0). .TP \fIcls\fR \fB\fR \fBmixins classes\fR ?\fIpattern\fR? If \fIpattern\fR is omitted, returns the object names of the mixin classes which extend \fIcls\fR directly. By specifying \fIpattern\fR, the returned mixin classes can be limited to those whose names match \fIpattern\fR (see \fBstring match\fR). .TP \fIcls\fR \fB\fR \fBmixins clear\fR Removes all mixin classes from \fIcls\fR and returns the list of removed mixin classes. Clearing is equivalent to passing an empty list for \fImixinSpecList\fR to \fB\fR \fBmixins set\fR. .TP \fIcls\fR \fB\fR \fBmixins delete\fR ?\fB-nocomplain\fR? \fIspecPattern\fR Removes a mixin class from a current list of mixin classes of \fIcls\fR whose spec matches \fIspecPattern\fR. \fIspecPattern\fR can contain special matching chars (see \fBstring match\fR). \fBclass\fR \fBmixins delete\fR will throw an error if there is no matching mixin class, unless \fB-nocomplain\fR is set. .TP \fIcls\fR \fB\fR \fBmixins get\fR Returns the list of current mixin specifications. .TP \fIcls\fR \fB\fR \fBmixins guard\fR \fIclassName\fR ?\fIexpr\fR? If \fIexpr\fR is specified, a guard expression \fIexpr\fR is registered with the mixin class \fIclassName\fR. This requires that the corresponding mixin class \fIclassName\fR has been previously set using \fBclass\fR \fBmixins set\fR or added using \fB\fR \fBmixins add\fR. \fIexpr\fR must be a valid Tcl expression (see \fBexpr\fR). An empty string for \fIexpr\fR will clear the currently registered guard expression for the mixin class \fIclassName\fR. .sp If \fIexpr\fR is not specified, returns the active guard expression. If none is available, an empty string will be returned. .TP \fIcls\fR \fB\fR \fBmixins set\fR \fImixinSpecList\fR \fImixinSpecList\fR represents a list of mixin class specs, with each spec being itself either a one-element or a three-element list: \fIclassName\fR ?-guard \fIguardExpr\fR?. If having one element, the element will be considered the \fIclassName\fR of the mixin class. If having three elements, the third element \fIguardExpr\fR will be stored as a guard expression of the mixin class. This guard expression will be evaluated using \fBexpr\fR when \fIcls\fR receives a message to determine if the mixin is to be considered during method dispatch or not. Guard expressions allow for realizing context-dependent or conditional mixin composition. .RE .IP At the time of setting the mixin relation, that is, calling \fB\fR \fBmixins\fR, every \fIclassName\fR as part of a spec must be an existing instance of \fBnx::Class\fR. To access and to manipulate the list of mixin classes of \fIcls\fR, \fBcget\fR|\fBconfigure\fR \fB-mixins\fR can also be used. .RE .TP \fBnew\fR .RS .TP \fIcls\fR \fBnew\fR ?\fB-childof\fR \fIparentName\fR? ?\fIoption\fR \fIvalue\fR \fIoption\fR \fIvalue\fR ...? A factory method to create autonamed instances of \fIcls\fR. It returns the name of the newly created instance. For example: .CS % nx::Class create AClass; # defines a class 'AClass' being an instance of 'nx::Class' ::AClass % set inst [::AClass new]; # defines an autonamed object being an instance of 'AClass' ::nsf::__#0 % $inst info class ::AClass .CE .IP The factory method will provide computed object names of the form, e.g. \fB::nsf::__#0\fR. The uniqueness of generated object names is guaranteed for the scope of the current Tcl interpreter only. .sp It is a frontend to \fBcreate\fR which will be called by \fBnew\fR once the name of the instance has been computed, passing along the arguments \fIoption\fR to \fBnew\fR as the configuration options (see \fBcreate\fR). .sp If \fB-childof\fR is provided, the new object will be created as a nested object of \fIparentName\fR. \fIparentName\fR can be the name of either an existing NX object or an existing Tcl namespace. If non-existing, a Tcl namespace \fIparentName\fR will be created on the fly. .RE .TP \fBproperty\fR .RS .TP \fIcls\fR \fBproperty\fR ?\fB-accessor\fR \fBpublic\fR | \fBprotected\fR | \fBprivate\fR? ?\fB-configurable\fR \fItrueFalse\fR? ?\fB-incremental\fR? ?\fB-class\fR \fIclassName\fR? \fIspec\fR ?\fIinitBlock\fR? Defines a property for the scope of the class. The \fIspec\fR provides the property specification as a \fBlist\fR holding at least one element or, maximum, two elements: \fIpropertyName\fR?\fB:\fR\fItypeSpec\fR? ?\fIdefaultValue\fR?. The \fIpropertyName\fR is also used as to form the names of the getter/setter methods, if requested (see \fB-accessor\fR). It is, optionally, equipped with a \fItypeSpec\fR following a colon delimiter which specifies a value checker for the values which become assigned to the property. The second, optional element sets a \fIdefaultValue\fR for this property. .sp If \fB-accessor\fR is set, a property will provide for a pair of getter and setter methods: .RS .TP \fIobj\fR \fIpropertyName\fR \fBset\fR \fIvalue\fR Sets the property \fIpropertyName\fR to \fIvalue\fR. .TP \fIobj\fR \fIpropertyName\fR \fBget\fR Returns the current value of property \fIpropertyName\fR. .TP \fIobj\fR \fIpropertyName\fR \fBunset\fR Removes the value store of \fIpropertyName\fR (e.g., an object variable), if existing. .RE .IP The option value passed along \fB-accessor\fR sets the level of call protection for the generated getter and setter methods: \fBpublic\fR, \fBprotected\fR, or \fBprivate\fR. By default, no getter and setter methods are created. .sp Turning on the switch \fB-incremental\fR provides a refined setter interface to the value managed by the property. First, setting \fB-incremental\fR implies requesting \fB-accessor\fR (set to \fBpublic\fR by default, if not specified explicitly). Second, the managed value will be considered a valid Tcl list. A multiplicity of \fB1..*\fR is set by default, if not specified explicitly as part of \fIspec\fR. Third, to manage this list value element-wise (\fIincrementally\fR), two additional setter methods become available: .RS .TP \fIobj\fR \fIpropertyName\fR \fBadd\fR \fIelement\fR ?\fIindex\fR? Adding \fIelement\fR to the managed list value, at the list position given by \fIindex\fR (by default: 0). .TP \fIobj\fR \fIpropertyName\fR \fBdelete\fR \fIelementPattern\fR Removing one or multiple elements from the managed list value which match \fIelementPattern\fR. \fIelementPattern\fR can contain matching characters (see \fBstring match\fR). .RE .sp By setting \fB-configurable\fR to \fBtrue\fR (the default), the property can be accessed and modified through \fBcget\fR and \fBconfigure\fR, respectively. If \fBfalse\fR, no configuration option will become available via \fBcget\fR and \fBconfigure\fR. .sp If neither \fB-accessor\fR nor \fB-configurable\fR are requested, the value managed by the property will have to be accessed and modified directly. If the property manages an object variable, its value will be readable and writable using \fBset\fR and \fBeval\fR. .sp A property becomes implemented by a slot object under any of the following conditions: .RS .IP \(bu \fB-configurable\fR equals \fBtrue\fR (by default). .IP \(bu \fB-accessor\fR is one of \fBpublic\fR, \fBprotected\fR, or \fBprivate\fR. .IP \(bu \fB-incremental\fR is turned on. .IP \(bu \fIinitBlock\fR is a non-empty string. .RE .IP Assuming default settings, every property is realized by a slot object. .sp Provided a slot object managing the property is to be created, a custom class \fIclassName\fR from which this slot object is to be instantiated can be set using \fB-class\fR. The default value is \fB::nx::VariableSlot\fR. .sp The last argument \fIinitBlock\fR accepts an optional Tcl script which is passed into the initialization procedure (see \fBconfigure\fR) of the property's slot object. See also \fB\fIinitBlock\fR for \fBcreate\fR and \fBnew\fR\fR. .RE .TP \fBrequire\fR .RS .TP \fIcls\fR \fBrequire\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fB method\fR \fImethodName\fR Attempts to register a method definition made available using \fB::nsf::method::provide\fR under the name \fImethodName\fR with \fIcls\fR . The registered method is subjected to default call protection (\fBprotected\fR), if not set explicitly. .RE .TP \fBvariable\fR .RS .TP \fIcls\fR \fBvariable\fR ?\fB-accessor\fR \fBpublic\fR | \fBprotected\fR | \fBprivate\fR? ?\fB-incremental\fR? ?\fB-class\fR \fIclassName\fR? ?\fB-configurable\fR \fItrueFalse\fR? ?\fB-initblock\fR \fIscript\fR? \fIspec\fR ?\fIdefaultValue\fR? Defines a variable for the scope of the class. The \fIspec\fR provides the variable specification: \fIvariableName\fR?\fB:\fR\fItypeSpec\fR?. The \fIvariableName\fR will be used to name the underlying Tcl variable and the getter/setter methods, if requested (see \fB-accessor\fR). \fIspec\fR is optionally equipped with a \fItypeSpec\fR following a colon delimiter which specifies a value checker for the values managed by the variable. Optionally, a \fIdefaultValue\fR can be defined. .sp If \fB-accessor\fR is set explicitly, a variable will provide for a pair of getter and setter methods: .RS .TP \fIobj\fR \fIvariableName\fR \fBset\fR \fIvarValue\fR Sets \fIvariableName\fR to \fIvarValue\fR. .TP \fIobj\fR \fIvariableName\fR \fBget\fR Returns the current value of \fIvariableName\fR. .TP \fIobj\fR \fIvariableName\fR \fBunset\fR Removes \fIvariableName\fR, if existing, underlying the property. .RE .IP The option value passed along \fB-accessor\fR sets the level of call protection for the getter and setter methods: \fBpublic\fR, \fBprotected\fR, or \fBprivate\fR. By default, no getter and setter methods are created. .sp Turning on the switch \fB-incremental\fR provides a refined setter interface to the value managed by the variable. First, setting \fB-incremental\fR implies requesting \fB-accessor\fR (\fBpublic\fR by default, if not specified explicitly). Second, the managed value will be considered a valid Tcl list. A multiplicity of \fB1..*\fR is set by default, if not specified explicitly as part of \fIspec\fR (see above). Third, to manage this list value element-wise (\fIincrementally\fR), two additional setter operations become available: .RS .TP \fIobj\fR \fIvariableName\fR \fBadd\fR \fIelement\fR ?\fIindex\fR? Adding \fIelement\fR to the managed list value, at the list position given by \fIindex\fR (by default: 0). .TP \fIobj\fR \fIvariableName\fR \fBdelete\fR \fIelementPattern\fR Removing one or multiple elements from the managed list value which match \fIelementPattern\fR. \fIelementPattern\fR can contain matching characters (see \fBstring match\fR). .RE .sp By setting \fB-configurable\fR to \fBtrue\fR, the variable can be accessed and modified via \fBcget\fR and \fBconfigure\fR, respectively. If \fBfalse\fR (the default), the interface based on \fBcget\fR and \fBconfigure\fR will not become available. In this case, and provided that \fB-accessor\fR is set, the variable can be accessed and modified via the getter/setter methods. Alternatively, the underlying Tcl variable, which is represented by the variable, can always be accessed and modified directly, e.g., using \fBeval\fR. By default, \fB-configurable\fR is \fBfalse\fR. .sp A variable becomes implemented by a slot object under any of the following conditions: .RS .IP \(bu \fB-configurable\fR equals \fBtrue\fR. .IP \(bu \fB-accessor\fR is one of \fBpublic\fR, \fBprotected\fR, or \fBprivate\fR. .IP \(bu \fB-incremental\fR is turned on. .IP \(bu \fB-initblock\fR is a non-empty string. .RE .IP Provided a slot object managing the variable is to be created, a custom class \fIclassName\fR from which this slot object is to be instantiated can be set using \fB-class\fR. The default value is \fB::nx::VariableSlot\fR. .sp Using \fB-initblock\fR, an optional Tcl \fIscript\fR can be defined which becomes passed into the initialization procedure (see \fBconfigure\fR) of the variable's slot object. See also \fB\fIinitBlock\fR for \fBcreate\fR and \fBnew\fR\fR. .RE .PP .SH "OBJECT LIFE CYCLE" \fBnx::Class\fR provides means to control important stages through which an NX object passes between and including its creation and its destruction: allocation, recreation, deallocation. .CS /cls/->create(/instance/) \.---------------. exists? [false] .----------------. .-------------------. ---->|Class::create()|----><>---------------->|Class::__alloc()|-----------><>---->|Object::configure()| `---------------' | (1) `----------------' ^ (3) `---------+---------' [true] | | | (4) | .-------------------. | .------------------. `->|Class::__recreate()|-------------------------' |/instance/->init()| (2) `-------------------' `------------------' /instance/->destroy() \.-----------------. .------------------. ---->|Object::destroy()|---->|Class::__dealloc()| `-----------------' (5) `------------------' .CE Object creation is controlled by the factory method \fBcreate\fR, provided by \fBnx::Class\fR to its instance \fIcls\fR. \fBcreate\fR produces a new object \fIinstance\fR as an instance of \fIcls\fR in a number of steps. .IP [1] If \fIinstance\fR does not represent an existing object, an internal call to \fB__alloc\fR, provided by \fBnx::Class\fR, runs the \fIallocation\fR procedure for a fresh \fIinstance\fR of \fIcls\fR. .IP [2] If \fIinstance\fR corresponds to an existing object, the \fIrecreation\fR procedure is triggered by calling \fB__recreate\fR defined by \fBnx::Class\fR. .IP [3] The newly allocated or recreated object \fIinstance\fR is then configured by dispatching \fBconfigure\fR, provided by \fBnx::Object\fR, which consumes the configuration options passed into \fBcreate\fR. This will establish the instance's initial state, e.g. by setting object variables and object relations according to the configuration options and corresponding default values. .IP [4] Finally, the initialization method \fBinit\fR is dispatched, if available for \fIinstance\fR. \fBinit\fR can be defined by \fIcls\fR on behalf of its instance \fIinstance\fR, e.g. to lay out a class-specific initialisation behaviour. .CS % nx::Class create Foo {:property x} % Foo method init {} {set :y [expr {${:x} + 1}]} % Foo public method bar {} {return ${:y}} % Foo create f1 -x 101 % f1 cget -x 101 % f1 bar 102 .CE .IP Alternatively, the object \fIinstance\fR may define an per-object \fBinit\fR on its own. A per-object \fBinit\fR can be chained to a class-level \fBinit\fR using \fBnx::next\fR, just like a regular method. .sp Note that the definition of an \fBinit\fR method must contain an empty parameter specification, since \fBinit\fR is always called with an empty argument list. .PP Object destruction, such as triggered by an application-level \fBdestroy\fR call (5), is finalized by \fB__dealloc\fR offerd by \fBnx::Class\fR. .PP In the following, the three built-in procedures --- allocation, recreation, and deallocation --- are explained: .IP \(bu \fIAllocation\fR: \fB__alloc\fR creates a blank object \fIinstance\fR as an instance of \fIcls\fR and returns the fully-qualified \fIinstance\fR. \fB__alloc\fR is primarily used internally by \fBcreate\fR to allocate a Tcl memory storage for \fIinstance\fR and to register \fIinstance\fR with the Tcl interpreter as a new command. .IP \(bu \fIRecreation\fR: Recreation is the NX scheme for resolving naming conflicts between objects: An object is requested to be created using \fBcreate\fR or \fBnew\fR while an object of an identical object name, e.g. \fIinstance\fR, already exists: .CS % Object create Bar ::Bar % Object create Bar; # calls Object->__recreate(::Bar, ...) ::Bar .CE .IP In such a situation, the built-in \fB__recreate\fR first unsets the object state (i.e., Tcl variables held by the object) and removes relations of the object under recreation with other objects. Then, second, standard object initialization is performed by calling \fBconfigure\fR and \fBinit\fR, if any. .sp Alternatively, recreation will be performed as a sequence of \fBdestroy\fR and \fBcreate\fR calls in the following recreation scenarios: .RS .IP \(bu An existing class is requested to be recreated as an object. .IP \(bu An existing object is requested to be recreated as a class. .CS % Object create Bar ::Bar % Class create Bar; # calls Bar->destroy() & Class::create(::Bar, ...) .CE .IP \(bu An object of an object system other than NX (e.g. XOTcl2) is asked to be recreated. .RE .IP \(bu \fIDeallocation\fR: \fB__dealloc\fR marks an instance \fIinstance\fR of \fIcls\fR for deletion by returning its Tcl memory representation to the Tcl memory pool and by unregistering the corresponding Tcl command with the Tcl interpreter. .sp Beware that \fB__dealloc\fR does not necessarily cause the object to be deleted immediately. Depending on the lifecycle of the object's environment (e.g. the Tcl interp interpreter, the containing namespace) and on call references down the callstack, the actual memory freeing/returning operation may occur at a later point. .PP The three methods \fB__alloc\fR, \fB__recreate\fR, and \fB__dealloc\fR are internally provided and internally called. By default, they are not part of the method interface of \fIcls\fR and cannot be called directly by clients of \fIcls\fR. In addition, \fB__alloc\fR, \fB__recreate\fR, and \fB__dealloc\fR are protected from redefinition by a script. .PP To extend or to replace the built-in allocation, recreation, and deallocation procedure, the methods \fB__alloc\fR, \fB__recreate\fR, and \fB__dealloc\fR can be refined by providing a custom method implementation: .IP \(bu as a per-object method of \fIcls\fR; .IP \(bu as a method of a per-object mixin class extending \fIcls\fR; .IP \(bu as a method of a per-class mixin class extending \fBnx::Class\fR; .IP \(bu as a method of a subclass specializing \fBnx::Class\fR, from which \fIcls\fR is to be instantiated. .PP This custom implementation can redirect to the built-in \fB__alloc\fR, \fB__recreate\fR, and \fB__dealloc\fR, respectively, by using \fBnx::next\fR. By providing such a custom implementation, \fB__alloc\fR, \fB__recreate\fR, and \fB__dealloc\fR, respectively, become available as callable methods of \fIcls\fR: .TP \fIcls\fR \fB__alloc\fR \fIinstance\fR .TP \fIcls\fR \fB__recreate\fR \fIinstance\fR ?\fIarg\fR ...? .TP \fIcls\fR \fB__dealloc\fR \fIinstance\fR .PP .SH COPYRIGHT .nf Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). .fidoc/Class.man000066400000000000000000000466431242365656200134150ustar00rootroot00000000000000[comment {-*- tcl -*- nx::Class manpage}] [manpage_begin nx::Class 3 2.0.0] [comment {For the time being, we do not render keywords & terms; and the corresponding reverse index}] [proc keywords args {}] [proc term v {return $v}] [keywords "base metaclass"] [keywords NX] [keywords "mixin class"] [keywords "re-classification"] [keywords "submethod"] [keywords "method ensemble"] [keywords "linearisation"] [keywords "filter specification"] [keywords "metaclass"] [vset SCOPE "class"] [vset CMD "cls"] [vset MODIFIER ""] [copyright {2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT).}] [titledesc {API reference of the base-metaclass of the NX objectsystem}] [description] [para] [cmd nx::Class] is the [term "base metaclass"] of the [term NX] object system. All classes (e.g. [emph cls]) are (direct or indirect) instances of [cmd nx::Class]. Therefore, the methods provided by [cmd nx::Class] are available to all classes. A class [emph cls] which does not have [cmd nx::Class] as its direct or indirect superclass is referred to as an [emph "application class"]. By default, when instantiating a new class from [cmd nx::Class], it becomes an application class with [cmd nx::Object] being set as its superclass. A class [emph cls] which is explicitly declared as a (direct or indirect) subclass of [cmd nx::Class] is referred to as a [emph metaclass], that is, its instances will become classes as well. In other words, a metaclass instantiates and subclasses [cmd nx::Class] at the same time. [example { +---------+ | ::nx::* | +---------+--------------------------------------Y | | | instance of | | .-------. | | +--------'+ instance of +----------+ | | | |<....................| | | | | Class | | Object | | | | |....................>| | | | +---------+ subclass of +-----+----+ | | ^ ^ ^ | \...|...|................................|......./ | | | | |subclass.....(xor)......subclass| | |of +-----------+ of| | |.........| |..........| | (metaclass) | /cls/ | (application class) |.............| | instance of +-----------+ }] Classes can be created in the following ways: [list_begin definitions] [call [cmd nx::Class] [method create] [arg cls] [opt "[option -superclasses] [arg superClassNames]"] [opt "[option -mixins] [arg mixinSpec]"] [opt "[option -filters] [arg filterSpec]"] [opt "[arg option] [arg value] ..."] [opt [arg initBlock]]] To create a class having the explicit name [arg cls], use [method create]. [call [cmd nx::Class] [method new] [opt "[option -superclasses] [arg superClassNames]"] [opt "[option -mixins] [arg mixinSpec]"] [opt "[option -filters] [arg filterSpec]"] [opt [arg initBlock]]] To create a class having an automatically assigned, implicit name, use [method new]. [list_end] The configuration options for direct and indirect instances of [cmd nx::Class], which can be passed when calling [method create] and [method new], are documented in the subsequent section. [section {Configuration Options for Instances of nx::Class}] [para] Configuration options can be used for configuring objects during their creation by passing the options as non-positional arguments into calls of [method new] and [method create] (see [cmd nx::Class]). An existing object can be queried for its current configuration using [method cget] and it can be re-configured using [method configure]. [list_begin options] [opt_def -superclasses [opt [arg superClassNames]]] If [arg superClassNames] is not specified, returns the superclasses of the class. If provided, the class becomes the subclass of [arg superClassNames]. [opt_def -filters [opt [arg filterSpecs]]] Retrieves the list of filter methods currently active on instances of the class, if [arg filterSpecs] is not set. Otherwise, activates a list of filter methods for the instances of the class. Filters are returned or set in terms of a list of [term "filter specification"]s. [opt_def -mixins [opt [arg mixinSpecs]]] Returns the list of [term "mixin class"]es currently active on instances of the class, if [arg mixinSpecs] is not specified. Otherwise, the class is extended by the list of [term "mixin class"]es provided by [arg mixinSpecs]. [term "mixin class"]es are returned or set in terms of a list of [term "mixin specification"]s. [list_end] The configuration options provided by [cmd nx::Object] are equally available because an application class [arg cls] is an indirect instance of [cmd nx::Object]. [section {Methods for Instances of nx::Class}] [list_begin commands] [cmd_def alias] [list_begin definitions] [include alias.man.inc] [list_end] [cmd_def __class_configureparameter] [list_begin definitions] [def "[arg cls] [method "__class_configureparameter"]"] Computes and returns the configuration options available for [arg cls] instances, to be consumed as method-parameter specification by [method configure]. [list_end] [cmd_def create] [list_begin definitions] [call [arg cls] [method create] [arg instanceName] [opt "[arg option] [arg value] [arg option] [arg value] ..."]] This factory method creates an instance [arg instanceName] of [arg cls] and returns [arg instanceName]. [example { % nx::Class create AClass { :method init args { next }; # initialization method for instances of 'AClass' }; # defines a class 'AClass' being an instance of 'nx::Class' ::AClass % ::AClass create anInstance; # defines an object 'anInstance' being an instance of 'AClass' ::anInstance % ::anInstance info class ::AClass % ::AClass info class ::nx::Class }] [method create] accepts the configuration options [arg option] available for this instance, such as those defined by properties of [arg cls] (see [method "property"]). [para] Note that [method create] is called internally when defining an instance of [arg cls] using [method new]. [para] By calling [method create] on [cmd nx::Class] itself, the created instance will become a new application class [arg instanceName] on which [method create] can also be applied (i.e., it can be instantiated). If the so-created class has [cmd ::nx::Class] its direct or indirect superclass, [arg instanceName] is referred to as a [term "metaclass"]; that is, a class whose instances are again classes. [list_end] [cmd_def delete] [list_begin definitions] [include delete.man.inc] [list_end] [cmd_def filters] [list_begin definitions] [include filter.man.inc] [list_end] [cmd_def forward] [list_begin definitions] [include forward.man.inc] [list_end] [cmd_def info] A collection of introspection submethods on the structural features (e.g. configuration options, superclasses) and the behavioral features (e.g. methods, [term "filter"]s) provided by [arg cls] to its instances. [list_begin definitions] [call [arg cls] [method "info heritage"] [opt [arg pattern]]] If [arg pattern] is omitted, returns the list of object names of all the direct and indirect superclasses and [emph per-class] [term "mixin class"]es of [arg cls], in their order of precedence, which are active for instances of [arg cls]. If [arg pattern] is specified, only superclasses and [term "mixin class"]es whose names match [arg pattern] will be listed (see [cmd "string match"]). [call [arg cls] [method "info instances"] [opt [option -closure]] [opt [arg pattern]]] If [arg pattern] is not specified, returns a list of the object names of all the direct instances of [arg cls]. If the [term "switch"] [option -closure] is set, indirect instances are also returned. A direct instance is created by using [method create] or [method new] on [arg cls], an indirect instance was created from a direct or indirect subclass of [arg cls]. If [arg pattern] is specified, only instances whose names match [arg pattern] will be listed (see [cmd "string match"]). [call [arg cls] [method "info mixinof"] [opt [option -closure]] [opt "[option -scope] [arg option]"] [opt [arg pattern]]] If [arg pattern] is not specified, returns a list of the object names of all the objects for which [arg cls] is active as a direct [term "mixin class"]. If the [term "switch"] [option -closure] is set, objects which have [arg cls] as an indirect [term "mixin class"] are also returned. If [arg pattern] is specified, only objects whose names match [arg pattern] will be listed (see [cmd "string match"]). Valid values of [arg option] are [const all], [const object], and [const class]. Passing [const object] will have only objects returned which have [arg cls] as [emph per-object] [term "mixin class"]. Passing [const class] will have only classes returned which have [arg cls] as [emph per-class] [term "mixin class"]. [const all] (the default) will have contained both in the returned list. [call [arg cls] [method "info subclasses"] [opt [option -closure]] [opt [option -dependent]] [opt [arg pattern]]] If [arg pattern] is not specified, returns a list of the object names of the direct subclasses of [arg cls]. If the [term "switch"] [option -closure] is set, indirect subclasses are also returned. If the [term "switch"] [option -dependent] is on, indirect subclasses introduced by [term "mixin class"] relations of subclasses of [arg cls] are also reported. [option -closure] and [option -dependent] are mutually exclusive. If [arg pattern] is specified, only subclasses whose names match [arg pattern] will be listed (see [cmd "string match"]). [call [arg cls] [method "info superclasses"] [opt [option -closure]] [opt [arg pattern]]] If [arg pattern] is not specified, returns a list of the object names of all direct superclasses of [arg cls]. If the [term "switch"] [option -closure] is set, indirect superclasses will also be returned. If [arg pattern] is specified, only superclasses whose names match [arg pattern] will be listed (see [cmd "string match"]). [include info.man.inc] [list_end] [cmd_def method] [list_begin definitions] [include method.man.inc] [list_end] [cmd_def mixins] [list_begin definitions] [include mixin.man.inc] [list_end] [cmd_def new] [list_begin definitions] [call [arg cls] [method new] [opt "[option "-childof"] [arg parentName]"] [opt "[arg option] [arg value] [arg option] [arg value] ..."]] A factory method to create autonamed instances of [arg cls]. It returns the name of the newly created instance. For example: [example { % nx::Class create AClass; # defines a class 'AClass' being an instance of 'nx::Class' ::AClass % set inst [::AClass new]; # defines an autonamed object being an instance of 'AClass' ::nsf::__#0 % $inst info class ::AClass }] The factory method will provide computed object names of the form, e.g. [const ::nsf::__#0]. The uniqueness of generated object names is guaranteed for the scope of the current Tcl interpreter only. [para] It is a frontend to [method create] which will be called by [method new] once the name of the instance has been computed, passing along the arguments [arg option] to [method new] as the configuration options (see [method create]). [para] If [option -childof] is provided, the new object will be created as a nested object of [arg parentName]. [arg parentName] can be the name of either an existing NX object or an existing Tcl namespace. If non-existing, a Tcl namespace [arg parentName] will be created on the fly. [list_end] [cmd_def property] [list_begin definitions] [call [arg cls] [method property] [opt "[option -accessor] [const public] | [const protected] | [const private]"] [opt "[option -configurable] [arg trueFalse]"] [opt [option -incremental]] [opt "[option -class] [arg className]"] [arg spec] [opt [arg initBlock]]] [include property.man.inc] [list_end] [cmd_def require] [list_begin definitions] [include require.man.inc] [list_end] [cmd_def variable] [list_begin definitions] [comment {::nx::Object variable ?-accessor /value/? ?-incremental? ?-class /value/? ?-configurable /boolean/? ?-initblock /value/? ?-nocomplain? /spec/ ?/defaultValue/?}] [call [arg cls] [method variable] [opt "[option -accessor] [const public] | [const protected] | [const private]"] [opt [option -incremental]] [opt "[option -class] [arg className]"] [opt "[option -configurable] [arg trueFalse]"] [opt "[option -initblock] [arg script]"] [arg spec] [opt [arg defaultValue]]] [include variable.man.inc] [list_end] [list_end] [section {Object Life Cycle}] [cmd nx::Class] provides means to control important stages through which an NX object passes between and including its creation and its destruction: allocation, recreation, deallocation. [example { /cls/->create(/instance/) .---------------. exists? [false] .----------------. .-------------------. ---->|Class::create()|----><>---------------->|Class::__alloc()|-----------><>---->|Object::configure()| `---------------' | (1) `----------------' ^ (3) `---------+---------' [true] | | | (4) | .-------------------. | .------------------. `->|Class::__recreate()|-------------------------' |/instance/->init()| (2) `-------------------' `------------------' /instance/->destroy() .-----------------. .------------------. ---->|Object::destroy()|---->|Class::__dealloc()| `-----------------' (5) `------------------' }] Object creation is controlled by the factory method [method create], provided by [cmd nx::Class] to its instance [arg cls]. [method create] produces a new object [arg instance] as an instance of [arg cls] in a number of steps. [list_begin enumerated] [enum] If [arg instance] does not represent an existing object, an internal call to [method "__alloc"], provided by [cmd nx::Class], runs the [emph {allocation}] procedure for a fresh [arg instance] of [arg cls]. [enum] If [arg instance] corresponds to an existing object, the [emph {recreation}] procedure is triggered by calling [method "__recreate"] defined by [cmd nx::Class]. [enum] The newly allocated or recreated object [arg instance] is then configured by dispatching [method configure], provided by [cmd nx::Object], which consumes the configuration options passed into [method create]. This will establish the instance's initial state, e.g. by setting object variables and object relations according to the configuration options and corresponding default values. [enum] Finally, the initialization method [method init] is dispatched, if available for [arg instance]. [method "init"] can be defined by [arg cls] on behalf of its instance [arg instance], e.g. to lay out a class-specific initialisation behaviour. [example_begin] % nx::Class create Foo {:property x} % Foo method init {} {set :y [lb]expr {${:x} + 1}[rb]} % Foo public method bar {} {return ${:y}} % Foo create f1 -x 101 % f1 cget -x 101 % f1 bar 102 [example_end] Alternatively, the object [arg instance] may define an per-object [method init] on its own. A per-object [method init] can be chained to a class-level [method init] using [cmd nx::next], just like a regular method. [para] Note that the definition of an [method init] method must contain an empty parameter specification, since [method init] is always called with an empty argument list. [list_end] Object destruction, such as triggered by an application-level [method destroy] call (5), is finalized by [method {__dealloc}] offerd by [cmd nx::Class]. [para] In the following, the three built-in procedures --- allocation, recreation, and deallocation --- are explained: [list_begin itemized] [item] [emph {Allocation}]: [method "__alloc"] creates a blank object [arg instance] as an instance of [arg cls] and returns the fully-qualified [arg instance]. [method __alloc] is primarily used internally by [method create] to allocate a Tcl memory storage for [arg instance] and to register [arg instance] with the Tcl interpreter as a new command. [item] [emph {Recreation}]: Recreation is the NX scheme for resolving naming conflicts between objects: An object is requested to be created using [method create] or [method new] while an object of an identical object name, e.g. [arg instance], already exists: [example { % Object create Bar ::Bar % Object create Bar; # calls Object->__recreate(::Bar, ...) ::Bar }] In such a situation, the built-in [method "__recreate"] first unsets the object state (i.e., Tcl variables held by the object) and removes relations of the object under recreation with other objects. Then, second, standard object initialization is performed by calling [method configure] and [method init], if any. [para] Alternatively, recreation will be performed as a sequence of [method destroy] and [method create] calls in the following recreation scenarios: [list_begin itemized] [item] An existing class is requested to be recreated as an object. [item] An existing object is requested to be recreated as a class. [example { % Object create Bar ::Bar % Class create Bar; # calls Bar->destroy() & Class::create(::Bar, ...) }] [item] An object of an object system other than NX (e.g. XOTcl2) is asked to be recreated. [list_end] [item] [emph {Deallocation}]: [method __dealloc] marks an instance [arg instance] of [arg cls] for deletion by returning its Tcl memory representation to the Tcl memory pool and by unregistering the corresponding Tcl command with the Tcl interpreter. [para] Beware that [method __dealloc] does not necessarily cause the object to be deleted immediately. Depending on the lifecycle of the object's environment (e.g. the Tcl interp interpreter, the containing namespace) and on call references down the callstack, the actual memory freeing/returning operation may occur at a later point. [list_end] The three methods [method __alloc], [method __recreate], and [method __dealloc] are internally provided and internally called. By default, they are not part of the method interface of [arg cls] and cannot be called directly by clients of [arg cls]. In addition, [method __alloc], [method __recreate], and [method __dealloc] are protected from redefinition by a script. [para] To extend or to replace the built-in allocation, recreation, and deallocation procedure, the methods [method __alloc], [method __recreate], and [method __dealloc] can be refined by providing a custom method implementation: [list_begin itemized] [item] as a per-object method of [arg cls]; [item] as a method of a per-object [term "mixin class"] extending [arg cls]; [item] as a method of a per-class [term "mixin class"] extending [cmd nx::Class]; [item] as a method of a subclass specializing [cmd nx::Class], from which [arg cls] is to be instantiated. [list_end] This custom implementation can redirect to the built-in [method __alloc], [method __recreate], and [method __dealloc], respectively, by using [cmd nx::next]. By providing such a custom implementation, [method __alloc], [method __recreate], and [method __dealloc], respectively, become available as callable methods of [arg cls]: [list_begin definitions] [def "[arg cls] [method "__alloc"] [arg instance]"] [def "[arg cls] [method "__recreate"] [arg instance] [opt "[arg arg] ..."]"] [def "[arg cls] [method "__dealloc"] [arg instance]"] [list_end] [manpage_end] doc/Object.3000066400000000000000000001666501242365656200131460ustar00rootroot00000000000000'\" '\" Generated from file 'Object.man' by tcllib/doctools with format 'nroff' '\" Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). '\" '\" The definitions below are for supplemental macros used in Tcl/Tk '\" manual entries. '\" '\" .AP type name in/out ?indent? '\" Start paragraph describing an argument to a library procedure. '\" type is type of argument (int, etc.), in/out is either "in", "out", '\" or "in/out" to describe whether procedure reads or modifies arg, '\" and indent is equivalent to second arg of .IP (shouldn't ever be '\" needed; use .AS below instead) '\" '\" .AS ?type? ?name? '\" Give maximum sizes of arguments for setting tab stops. Type and '\" name are examples of largest possible arguments that will be passed '\" to .AP later. If args are omitted, default tab stops are used. '\" '\" .BS '\" Start box enclosure. From here until next .BE, everything will be '\" enclosed in one large box. '\" '\" .BE '\" End of box enclosure. '\" '\" .CS '\" Begin code excerpt. '\" '\" .CE '\" End code excerpt. '\" '\" .VS ?version? ?br? '\" Begin vertical sidebar, for use in marking newly-changed parts '\" of man pages. The first argument is ignored and used for recording '\" the version when the .VS was added, so that the sidebars can be '\" found and removed when they reach a certain age. If another argument '\" is present, then a line break is forced before starting the sidebar. '\" '\" .VE '\" End of vertical sidebar. '\" '\" .DS '\" Begin an indented unfilled display. '\" '\" .DE '\" End of indented unfilled display. '\" '\" .SO '\" Start of list of standard options for a Tk widget. The '\" options follow on successive lines, in four columns separated '\" by tabs. '\" '\" .SE '\" End of list of standard options for a Tk widget. '\" '\" .OP cmdName dbName dbClass '\" Start of description of a specific option. cmdName gives the '\" option's name as specified in the class command, dbName gives '\" the option's name in the option database, and dbClass gives '\" the option's class in the option database. '\" '\" .UL arg1 arg2 '\" Print arg1 underlined, then print arg2 normally. '\" '\" RCS: @(#) $Id: man.macros,v 1.1 2009/01/30 04:56:47 andreas_kupries Exp $ '\" '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. .if t .wh -1.3i ^B .nr ^l \n(.l .ad b '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ta \\n()Au \\n()Bu .ie !"\\$3"" \{\ \&\\$1 \\fI\\$2\\fP (\\$3) .\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n .\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. .AS Tcl_Interp Tcl_CreateInterp in/out '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ .\" Draw four-sided box normally, but don't draw top of .\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .if !"\\$2"" .br .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ .\" Draw three-sided box if this is the box's first page, .\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .nf .sp .. '\" # DE - end display .de DE .fi .RE .sp .. '\" # SO - start of list of standard options .de SO .SH "STANDARD OPTIONS" .LP .nf .ta 4c 8c 12c .ft B .. '\" # SE - end of list of standard options .de SE .fi .ft R .LP See the \\fBoptions\\fR manual entry for details on the standard options. .. '\" # OP - start of full description for a single option .de OP .LP .nf .ta 4c Command-Line Name: \\fB\\$1\\fR Database Name: \\fB\\$2\\fR Database Class: \\fB\\$3\\fR .fi .. '\" # CS - begin code excerpt .de CS .RS .nf .ta .25i .5i .75i 1i .. '\" # CE - end code excerpt .de CE .fi .RE .. .de UL \\$1\l'|0\(ul'\\$2 .. .TH "nx::Object" 3 2.0 Object "" .BS .SH NAME nx::Object \- API reference of the base class in the NX object system .SH SYNOPSIS \fBnx::Object\fR \fBcreate\fR \fIobj\fR ?\fB-object-mixins\fR \fImixinSpec\fR? ?\fB-class\fR \fInewClassName\fR? ?\fB-object-filters\fR \fIfilterSpec\fR? ?\fIinitBlock\fR? .sp \fBnx::Object\fR \fBnew\fR ?\fB-object-mixins\fR \fImixinSpec\fR? ?\fB-class\fR \fInewClassName\fR? ?\fB-object-filters\fR \fIfilterSpec\fR? ?\fIinitBlock\fR? .sp \fIobj\fR ?\fBpublic\fR | \fBprivate\fR | \fBprotected\fR? \fBobject alias\fR \fImethodName\fR ?\fB-returns\fR \fIvalueChecker\fR? ?\fB-frame\fR \fBobject\fR | \fBmethod\fR? \fIcmdName\fR .sp \fIobj\fR \fBcget\fR \fIconfigurationOption\fR .sp \fIobj\fR \fBconfigure\fR ?\fIconfigurationOption\fR \fIvalue\fR ...? .sp \fIobj\fR \fBcontains\fR ?-withnew \fItrueFalse\fR? ?-object \fIobjectName\fR? ?-class \fIclassName\fR? \fIcmds\fR .sp \fIobj\fR \fBcopy\fR \fInewObjectName\fR .sp \fIobj\fR \fBdelete object\fR \fIfeature\fR \fIarg\fR .sp \fIobj\fR \fBdestroy\fR .sp \fIobj\fR \fBeval\fR \fIarg\fR ?\fIarg\fR ...? .sp \fIobj\fR \fBobject\fR \fBfilters\fR \fIsubmethod\fR ?\fIarg\fR ...? .sp \fIobj\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fBobject forward\fR \fImethodName\fR ?\fB-prefix\fR \fIprefixName\fR? ?\fB-frame\fR \fBobject\fR? ?\fB-returns\fR \fIvalueChecker\fR? ?\fB-verbose\fR? ?\fItarget\fR? ?\fIarg\fR ...? .sp \fIobj\fR \fBinfo children\fR ?\fB-type\fR \fIclassName\fR? ?\fIpattern\fR? .sp \fIobj\fR \fBinfo class\fR .sp \fIobj\fR \fBinfo has\fR ?\fBmixin\fR | \fBnamespace\fR | \fBtype\fR? ?\fIarg\fR ...? .sp \fIobj\fR \fBinfo lookup\fR \fIsubmethod\fR ?\fIarg\fR ...? .sp \fIobj\fR \fBinfo name\fR .sp \fIobj\fR \fBinfo info\fR ?\fB-asList\fR? .sp \fIobj\fR \fBinfo object filters\fR ?\fB-guards\fR? ?\fIpattern\fR? .sp \fIobj\fR \fBinfo object method\fR \fIoption\fR \fImethodName\fR .sp \fIobj\fR \fBinfo object methods\fR ?\fB-callprotection\fR \fIlevel\fR? ?\fB-type\fR \fImethodType\fR? ?\fB-path\fR? ?\fInamePattern\fR? .sp \fIobj\fR \fBinfo object mixins\fR ?\fB-guards\fR? ?\fIpattern\fR? .sp \fIobj\fR \fBinfo object slots\fR ?\fB-type\fR \fIclassName\fR? ?\fIpattern\fR? .sp \fIobj\fR \fBinfo object variables\fR ?\fIpattern\fR? .sp \fIobj\fR \fBinfo parent\fR .sp \fIobj\fR \fBinfo precedence\fR ?\fB-intrinsic\fR? ?\fIpattern\fR? .sp \fIobj\fR \fBinfo variable\fR \fIoption\fR \fIhandle\fR .sp \fIobj\fR \fBinfo vars\fR ?\fIpattern\fR? .sp \fIobj\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fBobject method\fR \fIname\fR \fIparameters\fR ?\fB-checkalways\fR? ?\fB-returns\fR \fIvalueChecker\fR? \fIbody\fR .sp \fIobj\fR \fBmove\fR \fInewObjectName\fR .sp \fIobj\fR \fBobject mixins\fR \fIsubmethod\fR ?\fIarg\fR ...? .sp \fIobj\fR \fBobject property\fR ?\fB-accessor\fR \fBpublic\fR | \fBprotected\fR | \fBprivate\fR? ?\fB-configurable\fR \fItrueFalse\fR? ?\fB-incremental\fR? ?\fB-class\fR \fIclassName\fR? ?\fB-nocomplain\fR? \fIspec\fR ?\fIinitBlock\fR? .sp \fIobj\fR \fBrequire namespace\fR .sp \fIobj\fR \fBrequire\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fBobject method\fR \fImethodName\fR .sp \fIobj\fR \fBunknown\fR \fIunknownMethodName\fR ?\fIarg\fR ...? .sp \fIobj\fR \fBobject variable\fR ?\fB-accessor\fR \fBpublic\fR | \fBprotected\fR | \fBprivate\fR? ?\fB-incremental\fR? ?\fB-class\fR \fIclassName\fR? ?\fB-configurable\fR \fItrueFalse\fR? ?\fB-initblock\fR \fIscript\fR? ?\fB-nocomplain\fR? \fIspec\fR ?\fIdefaultValue\fR? .sp .BE .SH DESCRIPTION .PP \fBnx::Object\fR is the base class of the NX object system. All objects defined in NX are (direct or indirect) instances of this base class. The methods provided by the \fBnx::Object\fR base class are available to all objects and to all classes defined in NX. .CS +---------+ | ::nx::* | +---------+--------------------------------------Y | | | +---------+ instance of +----------+ | | | |<....................| | | | | Class | | Object | | | | |....................>| | | | +----+----+ subclass of +-----+----+ | | ^ ^ ^ | instance.|...........................|....|......./ of | | | +-----+-----+ subclass of | | instance | |.....................| | of | /cls/ | (by default) | | | | +-----------+ | ^ | instance |.............(xor)..............| of | +-----------+ | |.........| |..........| | /obj/ | | | +-----------+ .CE NX allows for creating and for using objects (e.g. \fIobj\fR) which are instantiated from the base class \fBnx::Object\fR directly. Typical use cases are singletons and anonymous, inline objects. In such use cases, NX does not require creating an intermediate application class (e.g. \fIcls\fR), which specializes the base class \fBnx::Object\fR by default, beforehand. .PP Objects (e.g. \fIobj\fR) which are creating by instantiating a previously defined application class (e.g. \fIcls\fR) are indirect instances of \fBnx::Object\fR. .PP Direct instances of \fBnx::Object\fR can be created as follows: .TP \fBnx::Object\fR \fBcreate\fR \fIobj\fR ?\fB-object-mixins\fR \fImixinSpec\fR? ?\fB-class\fR \fInewClassName\fR? ?\fB-object-filters\fR \fIfilterSpec\fR? ?\fIinitBlock\fR? .sp To create a direct instance of \fBnx::Object\fR having an explicit name \fIobj\fR, use \fBcreate\fR on \fBnx::Object\fR. Note that \fBcreate\fR is defined by \fBnx::Class\fR and is available to \fBnx::Object\fR being an instance of \fBnx::Class\fR. This way, singleton objects can be created, for example. .TP \fBnx::Object\fR \fBnew\fR ?\fB-object-mixins\fR \fImixinSpec\fR? ?\fB-class\fR \fInewClassName\fR? ?\fB-object-filters\fR \fIfilterSpec\fR? ?\fIinitBlock\fR? To create a direct instance of \fBnx::Object\fR having an automatically assigned, implict object name, use \fBnew\fR on \fBnx::Object\fR. Note that \fBnew\fR is defined by \fBnx::Class\fR and is available to \fBnx::Object\fR being an instance of \fBnx::Class\fR. Using \fBnew\fR allows for creating anonymous, inline objects, for example. .PP The configuration options for direct and indirect instances of \fBnx::Object\fR, which can be passed when calling \fBcreate\fR and \fBnew\fR, are documented in the subsequent section. .SH "CONFIGURATION OPTIONS FOR INSTANCES OF NX::OBJECT" .PP Configuration options can be used for configuring objects during their creation by passing the options as non-positional arguments into calls of \fBnew\fR and \fBcreate\fR (see \fBnx::Class\fR). An existing object can be queried for its current configuration using \fBcget\fR and it can be re-configured using \fBconfigure\fR. Legal configuration options are: .TP \fB-class\fR ?\fIclassName\fR? Retrieves the current class of the object or sets the object's class to \fIclassName\fR, if provided. .TP \fB-object-filters\fR ?\fIfilterMethods\fR? Retrieves the list of currently active per-object filter methods or sets a list of per-object filter methods, if \fIfilterMethods\fR is provided. .TP \fB-object-mixins\fR ?\fImixinSpecs\fR? If \fImixinSpecs\fR is not specified, retrieves the list of currently active per-object mixin specifications. If \fImixinSpecs\fR is specified, sets a list of per-object mixin specifications to become active. mixin classes are returned or set in terms of a list of mixin specifications. .PP .SH "METHODS FOR INSTANCES OF NX::OBJECT" .TP \fBalias\fR .RS .TP \fIobj\fR ?\fBpublic\fR | \fBprivate\fR | \fBprotected\fR? \fBobject alias\fR \fImethodName\fR ?\fB-returns\fR \fIvalueChecker\fR? ?\fB-frame\fR \fBobject\fR | \fBmethod\fR? \fIcmdName\fR Define an alias method for the given object. The resulting method registers a pre-existing Tcl command \fIcmdName\fR under the (alias) name \fImethodName\fR with the object. If \fIcmdName\fR refers to another \fBmethod\fR, the corresponding argument should be a valid method handle. If a Tcl command (e.g., a \fBproc\fR), the argument should be a fully qualified Tcl command name. If aliasing a subcommand (e.g., \fBarray exists\fR) of a Tcl namespace ensemble (e.g., \fBarray\fR), \fIcmdName\fR must hold the fully qualified subcommand name (and not the ensemble name of the subcommand). .sp As for a regular \fBobject method\fR, \fB-returns\fR allows for setting a value checker on the values returned by the aliased command \fIcmdName\fR. .sp When creating an alias method for a \fIC-implemented\fR Tcl command (i.e., command defined using the Tcl/NX C-API), \fB-frame\fR sets the scope for variable references used in the aliased command. If the provided value is \fBobject\fR, then variable references will be resolved in the context of the called object, i.e., the object upon which the alias method is invoked, as if they were object variables. There is no need for using the colon-prefix notation for identifying object variables. If the value is \fBmethod\fR, then the aliased command will be executed as a regular method call. The command is aware of its called-object context; i.e., it can resolve \fB::nx::self\fR. In addition, the alias method has access to the method-call context (e.g., \fBnx::next\fR). If \fB-frame\fR is omitted, and by default, the variable references will resolve in the context of the caller of the alias method. .RE .TP \fBcget\fR .RS .TP \fIobj\fR \fBcget\fR \fIconfigurationOption\fR The method is used to obtain the current value of \fIconfigurationOption\fR for \fIobj\fR. The configuration options available for querying through \fBcget\fR are determined by the configurable properties defined by the class hierarchy of \fIobj\fR. The queriable configuration options for \fIobj\fR can be obtained by calling \fBinfo configure\fR. The \fIconfigurationOption\fR can be set and modified using \fBconfigure\fR. .CS % nx::Object create obj ::obj % ::obj info configure ?-object-mixins /mixinreg .../? ?-class /class/? ?-object-filters /filterreg .../? ?/__initblock/? % ::obj cget -class ::nx::Object .CE .RE .TP \fBconfigure\fR .RS .TP \fIobj\fR \fBconfigure\fR ?\fIconfigurationOption\fR \fIvalue\fR ...? This method sets configuration options on an object. The configuration options available for setting on \fIobj\fR are determined by the configurable properties defined by the class hierarchy of \fIobj\fR. The settable configuration options for \fIobj\fR can be obtained by calling \fBinfo configure\fR. Furthermore, \fBconfigure\fR is also called during object construction. Under object construction, it receives the arguments passed into calls of \fBcreate\fR and \fBnew\fR. Options set using \fBconfigure\fR can be retrieved using \fBcget\fR. .CS % nx::Class create Foo {:property x} ::Foo % Foo create f1 -x 101 ::f1 % f1 cget -x 101 % f1 configure -x 200 % f1 cget -x 200 .CE .RE .TP \fBcontains\fR .RS .TP \fIobj\fR \fBcontains\fR ?-withnew \fItrueFalse\fR? ?-object \fIobjectName\fR? ?-class \fIclassName\fR? \fIcmds\fR This method acts as a builder for nested object structures. Object and class construction statements passed to this method as its last argument \fIcmds\fR are evaluated in a way so that the receiver object \fIobj\fR becomes the parent of the newly constructed objects and classes. This is realized by setting explicitly the namespace for constructing relatively named objects. Fully qualified object names in \fIcmds\fR evade the nesting. .sp \fB-withnew\fR requests the automatic rescoping of objects created using \fBnew\fR so that they become nested into the receiver object \fIobj\fR, rather than being created in the default namespace for autonamed objects (i.e., ::nsf). If turned off, autonamed objects do not become children of \fIobj\fR. .sp The parent object \fIobjectName\fR to be used instead of \fIobj\fR can be specified using \fB-object\fR. If this explicitly set parent object does not exist prior to calling \fBcontains\fR, it will be created on the fly as a direct instance of \fBnx::Object\fR. Alternatively, using \fB-class\fR, a class \fIclassName\fR other than \fBnx::Object\fR for the on-the-fly creation of \fIobjectName\fR can be provided. .CS % nx::Class create Window { :contains { # # Become children of Window, implicitly # nx::Class create Header; # Window::Header nx::Object create Panel; # Window::Panel } # # Explicitly declared a child of Window using [self] # nx::Class create [self]::Slider; # Window::Slider # # Fully-qualified objects do not become nested # nx::Class create ::Door; # ::Door } ::Window % ::Window info children ::Window::Panel ::Window::Header ::Window::Slider .CE .RE .TP \fBcopy\fR .RS .TP \fIobj\fR \fBcopy\fR \fInewObjectName\fR Creates a full and deep copy of a source object \fIobj\fR. The object's copy \fInewObjectName\fR features all structural and behavioral properties of the source object, including object variables, per-object methods, nested objects, slot objects, namespaces, filters, mixins, and traces. .RE .TP \fBdelete\fR .RS .TP \fIobj\fR \fBdelete object\fR \fIfeature\fR \fIarg\fR This method serves as the equivalent to Tcl's \fBrename\fR for removing structural (properties, variables) and behavioral features (methods) of the object: .TP \fIobj\fR \fBdelete object property\fR \fIpropertyName\fR .TP \fIobj\fR \fBdelete object variable\fR \fIvariableName\fR .TP \fIobj\fR \fBdelete object method\fR \fImethodName\fR Removes a property \fIpropertyName\fR, variable \fIvariableName\fR, and method \fImethodName\fR, respectively, previously defined for the scope of the object. .sp \fBdelete object method\fR can be equally used for removing regular methods (see \fBobject method\fR), an alias method (see \fBobject alias\fR), and a forwarder method (see \fBobject forward\fR). .RE .TP \fBdestroy\fR .RS .TP \fIobj\fR \fBdestroy\fR This method allows for explicitly destructing an object \fIobj\fR, potentially prior to \fIobj\fR being destroyed by the object system (e.g. during the shutdown of the object system upon calling \fBexit\fR): .CS [nx::Object new] destroy .CE .IP By providing a custom implementation of \fBdestroy\fR, the destruction procedure of \fIobj\fR can be customized. Typically, once the application-specific destruction logic has completed, a custom \fBdestroy\fR will trigger the actual, physical object destruction via \fBnext\fR. .CS % [nx::Object create obj { :public method destroy {} { puts "destroying [self]" next; # physical destruction } }] destroy destroying ::obj .CE .IP A customized object-desctruction scheme can be made shared between the instances of a class, by defining the custom \fBdestroy\fR for an application class: .CS % nx::Class create Foo { :method destroy {} { puts "destroying [self]" next; # physical destruction } } ::Foo % Foo create f1 ::f1 % f1 destroy destroying ::f1 .CE .IP Physical destruction is performed by clearing the in-memory object storage of \fIobj\fR. This is achieved by passing \fIobj\fR into a call to \fBdealloc\fR provided by \fBnx::Class\fR. A near, scripted equivalent to the C-implemented \fBdestroy\fR provided by \fBnx::Object\fR would look as follows: .CS % Object method destroy {} { [:info class] dealloc [self] } .CE .IP Note, however, that \fBdestroy\fR is protected against application-level redefinition. Trying to evaluate the above script snippet yields: .CS refuse to overwrite protected method 'destroy'; derive e.g. a sub-class! .CE .IP A custom \fBdestroy\fR must be provided as a refinement in a subclass of \fBnx::Object\fR or in a mixin class. .RE .TP \fBeval\fR .RS .TP \fIobj\fR \fBeval\fR \fIarg\fR ?\fIarg\fR ...? Evaluates a special Tcl script for the scope of \fIobj\fR in the style of Tcl's \fBeval\fR. There are, however, notable differences to the standard \fBeval\fR: In this script, the colon-prefix notation is available to dispatch to methods and to access variables of \fIobj\fR. Script-local variables, which are thrown away once the evaluation of the script has completed, can be defined to store intermediate results. .CS % nx::Object create obj { :object property {bar 1} :public object method foo {x} { return $x } } ::obj % ::obj eval { set y [:foo ${:bar}] } 1 .CE .RE .TP \fBfilters\fR .RS .TP \fIobj\fR \fBobject\fR \fBfilters\fR \fIsubmethod\fR ?\fIarg\fR ...? Accesses and modifies the list of methods which are registered as filters with \fIobj\fR using a specific setter or getter \fIsubmethod\fR: .RS .TP \fIobj\fR \fBobject\fR \fBfilters add\fR \fIspec\fR ?\fIindex\fR? Inserts a single filter into the current list of filters of \fIobj\fR. Using \fIindex\fR, a position in the existing list of filters for inserting the new filter can be set. If omitted, \fIindex\fR defaults to the list head (0). .TP \fIobj\fR \fBobject\fR \fBfilters clear\fR Removes all filters from \fIobj\fR and returns the list of removed filters. Clearing is equivalent to passing an empty list for \fIfilterSpecList\fR to \fBobject\fR \fBfilter set\fR. .TP \fIobj\fR \fBobject\fR \fBfilters delete\fR ?\fB-nocomplain\fR? \fIspecPattern\fR Removes a single filter from the current list of filters of \fIobj\fR whose spec matches \fIspecPattern\fR. \fIspecPattern\fR can contain special matching chars (see \fBstring match\fR). \fBobject\fR \fBfilters delete\fR will throw an error if there is no matching filter, unless \fB-nocomplain\fR is set. .TP \fIobj\fR \fBobject\fR \fBfilters get\fR Returns the list of current filter specifications registered for \fIobj\fR. .TP \fIobj\fR \fBobject\fR \fBfilters guard\fR \fImethodName\fR ?\fIexpr\fR? If \fIexpr\fR is specified, registers a guard expression \fIexpr\fR with a filter \fImethodName\fR. This requires that the filter \fImethodName\fR has been previously set using \fBobject\fR \fBfilters set\fR or added using \fBobject\fR \fBfilters add\fR. \fIexpr\fR must be a valid Tcl expression (see \fBexpr\fR). An empty string for \fIexpr\fR will clear the currently registered guard expression for filter \fImethodName\fR. .sp If \fIexpr\fR is omitted, returns the guard expression set on the filter \fImethodName\fR defined for \fIobj\fR. If none is available, an empty string will be returned. .TP \fIobj\fR \fBobject\fR \fBfilters methods\fR ?\fIpattern\fR? If \fIpattern\fR is omitted, returns all filter names which are defined by \fIobj\fR. By specifying \fIpattern\fR, the returned filters can be limited to those whose names match \fIpatterns\fR (see \fBstring match\fR). .TP \fIobj\fR \fBobject\fR \fBfilters set\fR \fIfilterSpecList\fR \fIfilterSpecList\fR takes a list of filter specs, with each spec being itself either a one-element or a two-element list: \fImethodName\fR ?-guard \fIguardExpr\fR?. \fImethodName\fR identifies an existing method of \fIobj\fR which becomes registered as a filter. If having three elements, the third element \fIguardExpr\fR will be stored as a guard expression of the filter. This guard expression must be a valid Tcl expression (see \fBexpr\fR). \fIexpr\fR is evaluated when \fIobj\fR receives a message to determine whether the filter should intercept the message. Guard expressions allow for realizing context-dependent or conditional filter composition. .RE .IP Every \fImethodName\fR in a \fIspec\fR must resolve to an existing method in the scope of the object. To access and to manipulate the list of filters of \fIobj\fR, \fBcget\fR|\fBconfigure\fR \fB-object-filters\fR can also be used. .RE .TP \fBforward\fR .RS .TP \fIobj\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fBobject forward\fR \fImethodName\fR ?\fB-prefix\fR \fIprefixName\fR? ?\fB-frame\fR \fBobject\fR? ?\fB-returns\fR \fIvalueChecker\fR? ?\fB-verbose\fR? ?\fItarget\fR? ?\fIarg\fR ...? Define a forward method for the given object. The definition of a forward method registers a predefined, but changeable list of forwarder arguments under the (forwarder) name \fImethodName\fR. Upon calling the forward method, the forwarder arguments are evaluated as a Tcl command call. That is, if present, \fItarget\fR is interpreted as a Tcl command (e.g., a Tcl \fBproc\fR or an object) and the remainder of the forwarder arguments \fIarg\fR as arguments passed into this command. The actual method arguments to the invocation of the forward method itself are appended to the list of forwarder arguments. If \fItarget\fR is omitted, the value of \fImethodName\fR is implicitly set and used as \fItarget\fR. This way, when providing a fully-qualified Tcl command name as \fImethodName\fR without \fItarget\fR, the unqualified \fImethodName\fR (\fBnamespace tail\fR) is used as the forwarder name; while the fully-qualified one serves as the \fItarget\fR. .sp As for a regular \fBobject method\fR, \fB-returns\fR allows for setting a value checker on the values returned by the resulting Tcl command call. When passing \fBobject\fR to \fB-frame\fR, the resulting Tcl command is evaluated in the context of the object receiving the forward method call. This way, variable names used in the resulting execution of a command become resolved as object variables. .sp The list of forwarder arguments \fIarg\fR can contain as its elements a mix of literal values and placeholders. Placeholders are prefixed with a percent symbol (%) and substituted for concrete values upon calling the forward method. These placeholders allow for constructing and for manipulating the arguments to be passed into the resulting command call on the fly: .RS .IP \(bu \fB%method\fR becomes substituted for the name of the forward method, i.e. \fImethodName\fR. .IP \(bu \fB%self\fR becomes substituted for the name of the object receiving the call of the forward method. .IP \(bu \fB%1\fR becomes substituted for the first method argument passed to the call of forward method. This requires, in turn, that \fIat least\fR one argument is passed along with the method call. .sp Alternatively, \fB%1\fR accepts an optional argument \fIdefaults\fR: {\fB%1\fR \fIdefaults\fR}. \fIdefaults\fR must be a valid Tcl list of two elements. For the first element, \fB%1\fR is substituted when there is no first method argument which can be consumed by \fB%1\fR. The second element is inserted upon availability of a first method argument with the consumed argument being appended right after the second list element. This placeholder is typically used to define a pair of getter/setter methods. .IP \(bu {\fB%@\fR\fIindex\fR \fIvalue\fR} becomes substituted for the specified \fIvalue\fR at position \fIindex\fR in the forwarder-arguments list, with \fIindex\fR being either a positive integer, a negative integer, or the literal value \fBend\fR (such as in Tcl's \fBlindex\fR). Positive integers specify a list position relative to the list head, negative integers give a position relative to the list tail. Indexes for positioning placeholders in the definition of a forward method are evaluated from left to right and should be used in ascending order. .sp Note that \fIvalue\fR can be a literal or any of the placeholders (e.g., \fB%method\fR, \fB%self\fR). Position prefixes are exempted, they are evaluated as \fB%\fR\fIcmdName\fR-placeholders in this context. .IP \(bu {\fB%argclindex\fR \fIlist\fR} becomes substituted for the \fIn\fRth element of the provided \fIlist\fR , with \fIn\fR corresponding to the number of method arguments passed to the forward method call. .IP \(bu \fB%%\fR is substituted for a single, literal percent symbol (%). .IP \(bu \fB%\fR\fIcmdName\fR is substituted for the value returned from executing the Tcl command \fIcmdName\fR. To pass arguments to \fIcmdName\fR, the placeholder should be wrapped into a Tcl \fBlist\fR: {\fB%\fR\fIcmdName\fR ?\fIarg\fR ...?}. .sp Consider using fully-qualified Tcl command names for \fIcmdName\fR to avoid possible name conflicts with the predefined placeholders, e.g., \fB%self\fR vs. %\fB::nx::self\fR. .RE .sp To disambiguate the names of subcommands or methods, which potentially become called by a forward method, a prefix \fIprefixName\fR can be set using \fB-prefix\fR. This prefix is prepended automatically to the argument following \fItarget\fR (i.e., a second argument), if present. If missing, \fB-prefix\fR has no effect on the forward method call. .sp To inspect and to debug the conversions performed by the above placeholders, setting the switch \fB-verbose\fR will have the command list to be executed (i.e., after substitution) printed using \fB::nsf::log\fR (debugging level: \fBnotice\fR) upon calling the forward method. .RE .TP \fBinfo\fR .RS .TP \fIobj\fR \fBinfo children\fR ?\fB-type\fR \fIclassName\fR? ?\fIpattern\fR? Retrieves the list of nested (or aggregated) objects of \fIobj\fR. The resulting list contains the fully qualified names of the nested objects. If \fB-type\fR is set, only nested objects which are direct or indirect instances of class \fIclassName\fR are returned. Using \fIpattern\fR, only nested objects whose names match \fIpattern\fR are returned. The \fIpattern\fR string can contain special matching characters (see \fBstring match\fR). This method allows for introspecting on \fBcontains\fR. .TP \fIobj\fR \fBinfo class\fR Returns the fully qualified name of the current \fBnx::Class\fR of \fIobj\fR. In case of re-classification (see \fBconfigure\fR), the returned class will be different from the \fBnx::Class\fR from which \fIobj\fR was originally instantiated using \fBcreate\fR or \fBnew\fR. .TP \fIobj\fR \fBinfo has\fR ?\fBmixin\fR | \fBnamespace\fR | \fBtype\fR? ?\fIarg\fR ...? .RS .TP \fIobj\fR \fBinfo method has mixin\fR \fIclassName\fR Verifies whether \fIobj\fR has a given \fBnx::Class\fR \fIclassName\fR registered as a mixin class (returns: \fBtrue\fR) or not (returns: \fBfalse\fR). .TP \fIobj\fR \fBinfo has namespace\fR Checks whether the object has a companion Tcl namespace (returns: \fBtrue\fR) or not (returns: \fBfalse\fR). The namespace could have been created using, for example, \fBobject require namespace\fR. .TP \fIobj\fR \fBinfo has type\fR \fIclassName\fR Tests whether the \fBnx::Class\fR \fIclassName\fR is a type of the object (returns: \fBtrue\fR) or not (returns: \fBfalse\fR). That is, the method checks whether the object is a direct instance of \fIclassName\fR or an indirect instance of one of the superclasses of \fIclassName\fR. .RE .TP \fIobj\fR \fBinfo lookup\fR \fIsubmethod\fR ?\fIarg\fR ...? A collection of submethods to retrieve structural features (e.g. configuration options, slot objects) and behavioral features (e.g. methods, filters) available for \fIobj\fR from the perspective of a client to \fIobj\fR. Features provided by \fIobj\fR itself and by the classes in its current linearisation list are considered. .RS .TP \fIobj\fR \fBinfo lookup configure parameters\fR ?\fInamePattern\fR? Returns all configuration options available for \fIobj\fR as a list of method-parameter definitions. They can be used, for example, to define a custom method refinement for \fBconfigure\fR. The returned configuration options can be limited to those whose names match \fIpattern\fR (see \fBstring match\fR). .TP \fIobj\fR \fBinfo lookup configure syntax\fR Returns all configuration options available for \fIobj\fR as a concrete-syntax description to be used in human-understandable messages (e.g. errors or warnings, documentation strings). .TP \fIobj\fR \fBinfo lookup filter\fR \fIname\fR Returns the method handle for the filter method \fIname\fR, if currently registered. If there is no filter \fIname\fR registered, an empty string is returned. .TP \fIobj\fR \fBinfo lookup filters\fR ?\fB-guards\fR? ?\fInamePattern\fR? Returns the method handles of all filters which are active on \fIobj\fR. By turning on the switch \fB-guards\fR, the corresponding guard expressions, if any, are also reported for each filter as a three-element list: \fImethodHandle\fR -guard \fIguardExpr\fR. The returned filters can be limited to those whose names match \fInamePattern\fR (see \fBstring match\fR). .TP \fIobj\fR \fBinfo lookup method\fR \fIname\fR Returns the method handle for a method \fIname\fR if a so-named method can be invoked on \fIobj\fR. If there is no method \fIname\fR, an empty string is returned. .TP \fIobj\fR \fBinfo lookup methods\fR ?\fInamePattern\fR? Returns the names of all methods (including aliases and forwarders) which can be invoked on \fIobj\fR. The returned methods can be limited to those whose names match \fInamePattern\fR (see \fBstring match\fR). .TP \fIobj\fR \fBinfo lookup mixins\fR ?\fB-guards\fR? ?\fInamePattern\fR? Returns the object names of all mixin classes which are currently active on \fIobj\fR. By turning on the switch \fB-guards\fR, the corresponding guard expressions, if any, are also reported as a three-element list for each mixin class: \fIclassName\fR -guard \fIguardExpr\fR. The returned mixin classes can be limited to those whose names match \fInamePattern\fR (see \fBstring match\fR). .TP \fIobj\fR \fBinfo lookup slots\fR ?\fB-type\fR \fIclassName\fR? ?\fB-source\fR all | application | system? ?\fInamePattern\fR? Returns the command names of all slot objects responsible for managing properties, variables, and relations of \fIobj\fR. The returned slot objects can be limited according to any or a combination of the following criteria: First, slot objects can be filtered based on their command names matching \fInamePattern\fR (see \fBstring match\fR). Second, \fB-type\fR allows one to select slot objects which are instantiated from a subclass \fIclassName\fR of \fBnx::Slot\fR (default: \fBnx::Slot\fR) . Third, \fB-source\fR restricts slot objects returned according to their provenance in either the NX \fIsystem\fR classes or the \fIapplication\fR classes present in the linearisation list of \fIobj\fR (default: \fIall\fR). .sp To extract details of each slot object, use the \fBinfo\fR submethods available for each slot object. .TP \fIobj\fR \fBinfo lookup variables\fR Returns the command names of all slot objects responsible for managing properties and variables of \fIobj\fR, if provided by \fIobj\fR or the classes in the linearisation list of \fIobj\fR. .sp This is equivalent to calling: \fIobj\fR \fBinfo lookup slots\fR -type ::nx::VariableSlot -source all ?\fInamePattern\fR?. .sp To extract details of each slot object, use the \fBinfo\fR submethods available for each slot object. .RE .TP \fIobj\fR \fBinfo name\fR Returns the unqualified name of an object, i.e., the object name without any namespace qualifiers. .TP \fIobj\fR \fBinfo info\fR ?\fB-asList\fR? Returns the available submethods of the \fBinfo\fR method ensemble for \fIobj\fR, either as a pretty-printed string or as a Tcl list (if the switch \fB-asList\fR is set) for further processing. .TP \fIobj\fR \fBinfo object filters\fR ?\fB-guards\fR? ?\fIpattern\fR? If \fIpattern\fR is omitted, returns all filter names which are defined by \fIobj\fR. By turning on the switch \fB-guards\fR, the corresponding guard expressions, if any, are also reported along with each filter as a three-element list: \fIfilterName\fR -guard \fIguardExpr\fR. By specifying \fIpattern\fR, the returned filters can be limited to those whose names match \fIpatterns\fR (see \fBstring match\fR). .TP \fIobj\fR \fBinfo object method\fR \fIoption\fR \fImethodName\fR This introspection submethod provides access to the details of \fImethodName\fR provided by \fIobj\fR. Permitted values for \fIoption\fR are: .RS .IP \(bu \fBargs\fR returns a list containing the parameter names of \fImethodName\fR, in order of the method-parameter specification. .IP \(bu \fBbody\fR returns the body script of \fImethodName\fR. .IP \(bu \fBdefinition\fR returns a canonical command list which allows for (re-)define \fImethodName\fR. .IP \(bu \fBdefinitionhandle\fR returns the method handle for a submethod in a method ensemble from the perspective of \fIobj\fR as method provider. \fImethodName\fR must contain a complete method path. .IP \(bu \fBexists\fR returns 1 if there is a \fImethodName\fR provided by \fIobj\fR, returns 0 otherwise. .IP \(bu \fBhandle\fR returns the method handle for \fImethodName\fR. .IP \(bu \fBorigin\fR returns the aliased command if \fImethodName\fR is an alias method, or an empty string otherwise. .IP \(bu \fBparameters\fR returns the parameter specification of \fImethodName\fR as a list of parameter names and type specifications. .IP \(bu \fBregistrationhandle\fR returns the method handle for a submethod in a method ensemble from the perspective of the method caller. \fImethodName\fR must contain a complete method path. .IP \(bu \fBreturns\fR gives the type specification defined for the return value of \fImethodName\fR. .IP \(bu \fBsubmethods\fR returns the names of all submethods of \fImethodName\fR, if \fImethodName\fR is a method ensemble. Otherwise, an empty string is returned. .IP \(bu \fBsyntax\fR returns the method parameters of \fImethodName\fR as a concrete-syntax description to be used in human-understandable messages (e.g., errors or warnings, documentation strings). .IP \(bu \fBtype\fR returns whether \fImethodName\fR is a \fIscripted\fR method, an \fIalias\fR method, a \fIforwarder\fR method, or a \fIsetter\fR method. .RE .TP \fIobj\fR \fBinfo object methods\fR ?\fB-callprotection\fR \fIlevel\fR? ?\fB-type\fR \fImethodType\fR? ?\fB-path\fR? ?\fInamePattern\fR? Returns the names of all methods defined by \fIobj\fR. Methods covered include those defined using \fBobject alias\fR and \fBobject forward\fR. The returned methods can be limited to those whose names match \fInamePattern\fR (see \fBstring match\fR). .sp By setting \fB-callprotection\fR, only methods of a certain call protection \fIlevel\fR (\fBpublic\fR, \fBprotected\fR, or \fBprivate\fR) will be returned. Methods of a specific type can be requested using \fB-type\fR. The recognized values for \fImethodType\fR are: .RS .IP \(bu \fBscripted\fR denotes methods defined using \fBobject\fR \fBmethod\fR; .IP \(bu \fBalias\fR denotes alias methods defined using \fBobject\fR \fBalias\fR; .IP \(bu \fBforwarder\fR denotes forwarder methods defined using \fBobject\fR \fBforward\fR; .IP \(bu \fBsetter\fR denotes methods defined using \fB::nsf::setter\fR; .IP \(bu \fBall\fR returns methods of any type, without restrictions (also the default value); .RE .TP \fIobj\fR \fBinfo object mixins\fR ?\fB-guards\fR? ?\fIpattern\fR? If \fIpattern\fR is omitted, returns the object names of the mixin classes which extend \fIobj\fR directly. By turning on the switch \fB-guards\fR, the corresponding guard expressions, if any, are also reported along with each mixin as a three-element list: \fIclassName\fR -guard \fIguardExpr\fR. The returned mixin classes can be limited to those whose names match \fIpatterns\fR (see \fBstring match\fR). .TP \fIobj\fR \fBinfo object slots\fR ?\fB-type\fR \fIclassName\fR? ?\fIpattern\fR? If \fIpattern\fR is not specified, returns the object names of all slot objects defined by \fIobj\fR. The returned slot objects can be limited according to any or a combination of the following criteria: First, slot objects can be filtered based on their command names matching \fIpattern\fR (see \fBstring match\fR). Second, \fB-type\fR allows one to select slot objects which are instantiated from a subclass \fIclassName\fR of \fBnx::Slot\fR (default: \fBnx::Slot\fR). .TP \fIobj\fR \fBinfo object variables\fR ?\fIpattern\fR? If \fIpattern\fR is omitted, returns the object names of all slot objects provided by \fIobj\fR which are responsible for managing properties and variables of \fIobj\fR. Otherwise, only slot objects whose names match \fIpattern\fR are returned. .sp This is equivalent to calling: \fIobj\fR \fBinfo object slots\fR \fB-type\fR \fB::nx::VariableSlot\fR \fIpattern\fR. .sp To extract details of each slot object, use the \fBinfo\fR submethods available for each slot object. .TP \fIobj\fR \fBinfo parent\fR Returns the fully qualified name of the parent object of \fIobj\fR, if any. If there is no parent object, the name of the Tcl namespace containing \fIobj\fR (e.g. "::") will be reported. .TP \fIobj\fR \fBinfo precedence\fR ?\fB-intrinsic\fR? ?\fIpattern\fR? Lists the classes from which \fIobj\fR inherits structural (e.g. properties) and behavioral features (e.g. methods) and methods, in order of the linearisation scheme in NX. By setting the switch \fB-intrinsic\fR, only classes which participate in superclass/subclass relationships (i.e., intrinsic classes) are returned. If a \fIpattern\fR is provided only classes whose names match \fIpattern\fR are returned. The \fIpattern\fR string can contain special matching characters (see \fBstring match\fR). .TP \fIobj\fR \fBinfo variable\fR \fIoption\fR \fIhandle\fR Retrieves selected details about a variable represented by the given \fIhandle\fR. A \fIhandle\fR can be obtained by querying \fIobj\fR using \fBinfo object variables\fR and \fBinfo lookup variables\fR. Valid values for \fIoption\fR are: .RS .IP \(bu \fBname\fR returns the variable name. .IP \(bu \fBparameter\fR returns a canonical parameter specification eligible to (re-)define the given variable (e.g. using \fBobject variable\fR) in a new context. .IP \(bu \fBdefinition\fR returns a canonical representation of the definition command used to create the variable in its current configuration. .RE .TP \fIobj\fR \fBinfo vars\fR ?\fIpattern\fR? Yields a list of Tcl variable names created and defined for the scope of \fIobj\fR, i.e., object variables. The list can be limited to object variables whose names match \fIpattern\fR. The \fIpattern\fR string can contain special matching characters (see \fBstring match\fR). .RE .TP \fBmethod\fR .RS .TP \fIobj\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fBobject method\fR \fIname\fR \fIparameters\fR ?\fB-checkalways\fR? ?\fB-returns\fR \fIvalueChecker\fR? \fIbody\fR Defines a scripted method \fImethodName\fR for the scope of the object. The method becomes part of the object's signature interface. Besides a \fImethodName\fR, the method definition specifies the method \fIparameters\fR and a method \fIbody\fR. .sp \fIparameters\fR accepts a Tcl \fBlist\fR containing an arbitrary number of non-positional and positional parameter definitions. Each parameter definition comprises a parameter name, a parameter-specific value checker, and parameter options. .sp The \fIbody\fR contains the method implementation as a script block. In this body script, the colon-prefix notation is available to denote an object variable and a self call. In addition, the context of the object receiving the method call (i.e., the message) can be accessed (e.g., using \fBnx::self\fR) and the call stack can be introspected (e.g., using \fBnx::current\fR). .sp Optionally, \fB-returns\fR allows for setting a value checker on values returned by the method implementation. By setting the switch \fB-checkalways\fR, value checking on arguments and return value is guaranteed to be performed, even if value checking is temporarily disabled; see \fBnx::configure\fR). .sp A method closely resembles a Tcl \fBproc\fR, but it differs in some important aspects: First, a method can define non-positional parameters and value checkers on arguments. Second, the script implementing the method body can contain object-specific notation and commands (see above). Third, method calls \fIcannot\fR be intercepted using Tcl \fBtrace\fR. Note that an existing Tcl \fBproc\fR can be registered as an alias method with the object (see \fBobject alias\fR). .RE .TP \fBmove\fR .RS .TP \fIobj\fR \fBmove\fR \fInewObjectName\fR Effectively renames an object. First, the source object \fIobj\fR is cloned into a target object \fInewObjectName\fR using \fBcopy\fR. Second, the source object \fIobj\fR is destroyed by invoking \fBdestroy\fR. \fBmove\fR is also called internally when \fBrename\fR is performed for a Tcl command representing an object. .RE .TP \fBmixins\fR .RS .TP \fIobj\fR \fBobject mixins\fR \fIsubmethod\fR ?\fIarg\fR ...? Accesses and modifies the list of mixin classes of \fIobj\fR using a specific setter or getter \fIsubmethod\fR: .RS .TP \fIobj\fR \fBobject\fR \fBmixins add\fR \fIspec\fR ?\fIindex\fR? Inserts a single mixin class into the current list of mixin classes of \fIobj\fR. Using \fIindex\fR, a position in the existing list of mixin classes for inserting the new mixin class can be set. If omitted, \fIindex\fR defaults to the list head (0). .TP \fIobj\fR \fBobject\fR \fBmixins classes\fR ?\fIpattern\fR? If \fIpattern\fR is omitted, returns the object names of the mixin classes which extend \fIobj\fR directly. By specifying \fIpattern\fR, the returned mixin classes can be limited to those whose names match \fIpattern\fR (see \fBstring match\fR). .TP \fIobj\fR \fBobject\fR \fBmixins clear\fR Removes all mixin classes from \fIobj\fR and returns the list of removed mixin classes. Clearing is equivalent to passing an empty list for \fImixinSpecList\fR to \fBobject\fR \fBmixins set\fR. .TP \fIobj\fR \fBobject\fR \fBmixins delete\fR ?\fB-nocomplain\fR? \fIspecPattern\fR Removes a mixin class from a current list of mixin classes of \fIobj\fR whose spec matches \fIspecPattern\fR. \fIspecPattern\fR can contain special matching chars (see \fBstring match\fR). \fBobject\fR \fBmixins delete\fR will throw an error if there is no matching mixin class, unless \fB-nocomplain\fR is set. .TP \fIobj\fR \fBobject\fR \fBmixins get\fR Returns the list of current mixin specifications. .TP \fIobj\fR \fBobject\fR \fBmixins guard\fR \fIclassName\fR ?\fIexpr\fR? If \fIexpr\fR is specified, a guard expression \fIexpr\fR is registered with the mixin class \fIclassName\fR. This requires that the corresponding mixin class \fIclassName\fR has been previously set using \fBobject\fR \fBmixins set\fR or added using \fBobject\fR \fBmixins add\fR. \fIexpr\fR must be a valid Tcl expression (see \fBexpr\fR). An empty string for \fIexpr\fR will clear the currently registered guard expression for the mixin class \fIclassName\fR. .sp If \fIexpr\fR is not specified, returns the active guard expression. If none is available, an empty string will be returned. .TP \fIobj\fR \fBobject\fR \fBmixins set\fR \fImixinSpecList\fR \fImixinSpecList\fR represents a list of mixin class specs, with each spec being itself either a one-element or a three-element list: \fIclassName\fR ?-guard \fIguardExpr\fR?. If having one element, the element will be considered the \fIclassName\fR of the mixin class. If having three elements, the third element \fIguardExpr\fR will be stored as a guard expression of the mixin class. This guard expression will be evaluated using \fBexpr\fR when \fIobj\fR receives a message to determine if the mixin is to be considered during method dispatch or not. Guard expressions allow for realizing context-dependent or conditional mixin composition. .RE .IP At the time of setting the mixin relation, that is, calling \fBobject\fR \fBmixins\fR, every \fIclassName\fR as part of a spec must be an existing instance of \fBnx::Class\fR. To access and to manipulate the list of mixin classes of \fIobj\fR, \fBcget\fR|\fBconfigure\fR \fB-object-mixins\fR can also be used. .RE .TP \fB__object_configureparameter\fR .RS .TP \fIobj\fR \fB__object_configureparameter\fR Computes and returns the configuration options available for \fIobj\fR, to be consumed as method-parameter specification by \fBconfigure\fR. .RE .TP \fBproperty\fR .RS .TP \fIobj\fR \fBobject property\fR ?\fB-accessor\fR \fBpublic\fR | \fBprotected\fR | \fBprivate\fR? ?\fB-configurable\fR \fItrueFalse\fR? ?\fB-incremental\fR? ?\fB-class\fR \fIclassName\fR? ?\fB-nocomplain\fR? \fIspec\fR ?\fIinitBlock\fR? Defines a property for the scope of the object. The \fIspec\fR provides the property specification as a \fBlist\fR holding at least one element or, maximum, two elements: \fIpropertyName\fR?\fB:\fR\fItypeSpec\fR? ?\fIdefaultValue\fR?. The \fIpropertyName\fR is also used as to form the names of the getter/setter methods, if requested (see \fB-accessor\fR). It is, optionally, equipped with a \fItypeSpec\fR following a colon delimiter which specifies a value checker for the values which become assigned to the property. The second, optional element sets a \fIdefaultValue\fR for this property. .sp If \fB-accessor\fR is set, a property will provide for a pair of getter and setter methods: .RS .TP \fIobj\fR \fIpropertyName\fR \fBset\fR \fIvalue\fR Sets the property \fIpropertyName\fR to \fIvalue\fR. .TP \fIobj\fR \fIpropertyName\fR \fBget\fR Returns the current value of property \fIpropertyName\fR. .TP \fIobj\fR \fIpropertyName\fR \fBunset\fR Removes the value store of \fIpropertyName\fR (e.g., an object variable), if existing. .RE .IP The option value passed along \fB-accessor\fR sets the level of call protection for the generated getter and setter methods: \fBpublic\fR, \fBprotected\fR, or \fBprivate\fR. By default, no getter and setter methods are created. .sp Turning on the switch \fB-incremental\fR provides a refined setter interface to the value managed by the property. First, setting \fB-incremental\fR implies requesting \fB-accessor\fR (set to \fBpublic\fR by default, if not specified explicitly). Second, the managed value will be considered a valid Tcl list. A multiplicity of \fB1..*\fR is set by default, if not specified explicitly as part of \fIspec\fR. Third, to manage this list value element-wise (\fIincrementally\fR), two additional setter methods become available: .RS .TP \fIobj\fR \fIpropertyName\fR \fBadd\fR \fIelement\fR ?\fIindex\fR? Adding \fIelement\fR to the managed list value, at the list position given by \fIindex\fR (by default: 0). .TP \fIobj\fR \fIpropertyName\fR \fBdelete\fR \fIelementPattern\fR Removing one or multiple elements from the managed list value which match \fIelementPattern\fR. \fIelementPattern\fR can contain matching characters (see \fBstring match\fR). .RE .sp By setting \fB-configurable\fR to \fBtrue\fR (the default), the property can be accessed and modified through \fBcget\fR and \fBconfigure\fR, respectively. If \fBfalse\fR, no configuration option will become available via \fBcget\fR and \fBconfigure\fR. .sp If neither \fB-accessor\fR nor \fB-configurable\fR are requested, the value managed by the property will have to be accessed and modified directly. If the property manages an object variable, its value will be readable and writable using \fBset\fR and \fBeval\fR. .sp A property becomes implemented by a slot object under any of the following conditions: .RS .IP \(bu \fB-configurable\fR equals \fBtrue\fR (by default). .IP \(bu \fB-accessor\fR is one of \fBpublic\fR, \fBprotected\fR, or \fBprivate\fR. .IP \(bu \fB-incremental\fR is turned on. .IP \(bu \fIinitBlock\fR is a non-empty string. .RE .IP Assuming default settings, every property is realized by a slot object. .sp Provided a slot object managing the property is to be created, a custom class \fIclassName\fR from which this slot object is to be instantiated can be set using \fB-class\fR. The default value is \fB::nx::VariableSlot\fR. .sp The last argument \fIinitBlock\fR accepts an optional Tcl script which is passed into the initialization procedure (see \fBconfigure\fR) of the property's slot object. See also \fB\fIinitBlock\fR for \fBcreate\fR and \fBnew\fR\fR. .sp By default, the property will ascertain that no (potentially) pre-existing and equally named object variable will be overwritten when defining the property. In case of a conflict, an error exception is thrown: .CS % Object create obj { set :x 1 } ::obj % ::obj object property {x 2} object ::obj has already an instance variable named 'x' .CE .IP If the switch \fB-nocomplain\fR is on, this check is omitted (continuing the above example): .CS % ::obj object property -nocomplain {x 2} % ::obj eval {set :x} 2 .CE .RE .TP \fBrequire\fR .RS .TP \fIobj\fR \fBrequire namespace\fR Create a Tcl namespace named after the object \fIobj\fR. All object variables become available as namespace variables. .TP \fIobj\fR \fBrequire\fR ?\fBpublic\fR | \fBprotected\fR | \fBprivate\fR? \fBobject method\fR \fImethodName\fR Attempts to register a method definition made available using \fB::nsf::method::provide\fR under the name \fImethodName\fR with \fIobj\fR . The registered method is subjected to default call protection (\fBprotected\fR), if not set explicitly. .RE .TP \fBunknown\fR .RS .TP \fIobj\fR \fBunknown\fR \fIunknownMethodName\fR ?\fIarg\fR ...? This method is called implicitly whenever an unknown method is invoked. \fIunknownMethodName\fR indicates the unresolvable method name, followed by the remainder of the original argument vector as a number of \fIarg\fR of the indirected method invocation. .RE .TP \fBvariable\fR .RS .TP \fIobj\fR \fBobject variable\fR ?\fB-accessor\fR \fBpublic\fR | \fBprotected\fR | \fBprivate\fR? ?\fB-incremental\fR? ?\fB-class\fR \fIclassName\fR? ?\fB-configurable\fR \fItrueFalse\fR? ?\fB-initblock\fR \fIscript\fR? ?\fB-nocomplain\fR? \fIspec\fR ?\fIdefaultValue\fR? Defines a variable for the scope of the object. The \fIspec\fR provides the variable specification: \fIvariableName\fR?\fB:\fR\fItypeSpec\fR?. The \fIvariableName\fR will be used to name the underlying Tcl variable and the getter/setter methods, if requested (see \fB-accessor\fR). \fIspec\fR is optionally equipped with a \fItypeSpec\fR following a colon delimiter which specifies a value checker for the values managed by the variable. Optionally, a \fIdefaultValue\fR can be defined. .sp If \fB-accessor\fR is set explicitly, a variable will provide for a pair of getter and setter methods: .RS .TP \fIobj\fR \fIvariableName\fR \fBset\fR \fIvarValue\fR Sets \fIvariableName\fR to \fIvarValue\fR. .TP \fIobj\fR \fIvariableName\fR \fBget\fR Returns the current value of \fIvariableName\fR. .TP \fIobj\fR \fIvariableName\fR \fBunset\fR Removes \fIvariableName\fR, if existing, underlying the property. .RE .IP The option value passed along \fB-accessor\fR sets the level of call protection for the getter and setter methods: \fBpublic\fR, \fBprotected\fR, or \fBprivate\fR. By default, no getter and setter methods are created. .sp Turning on the switch \fB-incremental\fR provides a refined setter interface to the value managed by the variable. First, setting \fB-incremental\fR implies requesting \fB-accessor\fR (\fBpublic\fR by default, if not specified explicitly). Second, the managed value will be considered a valid Tcl list. A multiplicity of \fB1..*\fR is set by default, if not specified explicitly as part of \fIspec\fR (see above). Third, to manage this list value element-wise (\fIincrementally\fR), two additional setter operations become available: .RS .TP \fIobj\fR \fIvariableName\fR \fBadd\fR \fIelement\fR ?\fIindex\fR? Adding \fIelement\fR to the managed list value, at the list position given by \fIindex\fR (by default: 0). .TP \fIobj\fR \fIvariableName\fR \fBdelete\fR \fIelementPattern\fR Removing one or multiple elements from the managed list value which match \fIelementPattern\fR. \fIelementPattern\fR can contain matching characters (see \fBstring match\fR). .RE .sp By setting \fB-configurable\fR to \fBtrue\fR, the variable can be accessed and modified via \fBcget\fR and \fBconfigure\fR, respectively. If \fBfalse\fR (the default), the interface based on \fBcget\fR and \fBconfigure\fR will not become available. In this case, and provided that \fB-accessor\fR is set, the variable can be accessed and modified via the getter/setter methods. Alternatively, the underlying Tcl variable, which is represented by the variable, can always be accessed and modified directly, e.g., using \fBeval\fR. By default, \fB-configurable\fR is \fBfalse\fR. .sp A variable becomes implemented by a slot object under any of the following conditions: .RS .IP \(bu \fB-configurable\fR equals \fBtrue\fR. .IP \(bu \fB-accessor\fR is one of \fBpublic\fR, \fBprotected\fR, or \fBprivate\fR. .IP \(bu \fB-incremental\fR is turned on. .IP \(bu \fB-initblock\fR is a non-empty string. .RE .IP Provided a slot object managing the variable is to be created, a custom class \fIclassName\fR from which this slot object is to be instantiated can be set using \fB-class\fR. The default value is \fB::nx::VariableSlot\fR. .sp Using \fB-initblock\fR, an optional Tcl \fIscript\fR can be defined which becomes passed into the initialization procedure (see \fBconfigure\fR) of the variable's slot object. See also \fB\fIinitBlock\fR for \fBcreate\fR and \fBnew\fR\fR. .sp By default, the variable will ascertain that a pre-existing and equally named object variable will not be overwritten when defining the variable. In case of a conflict, an error exception is thrown: .CS % Object create obj { set :x 1 } ::obj % ::obj object variable x 2 object ::obj has already an instance variable named 'x' .CE .IP If the switch \fB-nocomplain\fR is on, this check is omitted (continuing the above example): .CS % ::obj object variable -nocomplain x 2 % ::obj eval {set :x} 2 .CE .RE .PP .SH "OBJECT SELF-REFERENCE" Objects are naturally recursive, with methods of an object \fB::obj\fR frequently invoking other methods in the same object \fB::obj\fR and accessing \fB::obj\fR's object variables. To represent these self-references effectively in method bodies, and dependening on the usage scenario, NX offers two alternative notations for self-references: one based on a special-purpose syntax token ("colon prefix"), the other based on the command \fBnx::current\fR. .PP Both, the colon-prefix notation and \fBnx::current\fR, may be used only in method bodies and scripts passed to \fBeval\fR. If they appear anywhere else, an error will be reported. There are three main use cases for self-references: .IP [1] As a \fIplaceholder\fR for the currently active object, \fBnx::current\fR can be used to retrieve the object name. .IP [2] Reading and writing \fIobject variables\fR directly (i.e. without getter/setter methods in place) require the use of variable names carrying the prefix \fB:\fR ("colon-prefix notation"). Internally, colon-prefixed variable names are processed using Tcl's variable resolvers. Alternatively, one can provide for getter/setter methods for object variables (see \fBproperty\fR and \fBvariable\fR). .IP [3] \fISelf-referential method calls\fR can be defined via prefixing (\fB:\fR) the method names or, alternatively, via \fBnx::current\fR. Internally, colon-prefixed method names are processed using Tcl's command resolvers. The colon-prefix notation is recommended, also because it has a (slight) performance advantage over \fBnx::current\fR which requires two rather than one command evaluation per method call. .PP See the following listing for some examples corresponding to use cases 1--3: .CS Object create ::obj { puts [current]; # 1) print name of currently active object ('::obj') set :x 1; :object variable y 2; # 2) object variables :public object method print {} { set z 3; # 2.a) method-local variable puts ${:x}-${:y}-$z; # 2.b) variable substitution using '$' and ':' puts [set :x]-[set :y]-[set z]; # 2.c) reading variables using 'set' set :x 1; incr :y; # 2.d) writing variables using 'set', 'incr', ... } :public object method show {} { :print; # 3.a) self-referential method call using ':' [current] print; # 3.b) self-referential method call using 'nx::current' [current object] print; # 3.c) self-referential method call using 'nx::current object' } :show } .CE .SH COPYRIGHT .nf Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). .fidoc/Object.man000066400000000000000000000702231242365656200135450ustar00rootroot00000000000000[comment {-*- tcl -*- nx::Object manpage}] [manpage_begin nx::Object 3 2.0.0] [comment {For the time being, we do not render keywords & terms; and the corresponding reverse index}] [proc keywords args {}] [proc term v {return $v}] [keywords baseclass] [keywords NX] [keywords "mixin class"] [keywords "re-classification"] [keywords "submethod"] [keywords "method ensemble"] [keywords "linearisation"] [vset SCOPE "object"] [vset CMD "obj"] [vset MODIFIER "object"] [copyright {2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT).}] [titledesc {API reference of the base class in the NX object system}] [description] [para] [cmd nx::Object] is the [term "base class"] of the [term NX] object system. All objects defined in [term NX] are (direct or indirect) instances of this [term "base class"]. The methods provided by the [cmd nx::Object] [term "base class"] are available to all objects and to all classes defined in NX. [example { +---------+ | ::nx::* | +---------+--------------------------------------Y | | | +---------+ instance of +----------+ | | | |<....................| | | | | Class | | Object | | | | |....................>| | | | +----+----+ subclass of +-----+----+ | | ^ ^ ^ | instance.|...........................|....|......./ of | | | +-----+-----+ subclass of | | instance | |.....................| | of | /cls/ | (by default) | | | | +-----------+ | ^ | instance |.............(xor)..............| of | +-----------+ | |.........| |..........| | /obj/ | | | +-----------+ }] [term NX] allows for creating and for using objects (e.g. [emph obj]) which are instantiated from the [term "base class"] [cmd nx::Object] directly. Typical use cases are singletons and anonymous, inline objects. In such use cases, [term NX] does not require creating an intermediate application class (e.g. [emph cls]), which specializes the [term "base class"] [cmd nx::Object] by default, beforehand. [para] Objects (e.g. [emph obj]) which are creating by instantiating a previously defined application class (e.g. [emph cls]) are indirect instances of [cmd nx::Object]. [para] Direct instances of [cmd nx::Object] can be created as follows: [list_begin definitions] [call [cmd nx::Object] [method create] [arg obj] [opt "[option -object-mixins] [arg mixinSpec]"] [opt "[option -class] [arg newClassName]"] [opt "[option -object-filters] [arg filterSpec]"] [opt [arg initBlock]]] [para] To create a direct instance of [cmd nx::Object] having an explicit name [arg obj], use [method create] on [cmd nx::Object]. Note that [method create] is defined by [cmd nx::Class] and is available to [cmd nx::Object] being an instance of [cmd nx::Class]. This way, singleton objects can be created, for example. [call [cmd nx::Object] [method new] [opt "[option -object-mixins] [arg mixinSpec]"] [opt "[option -class] [arg newClassName]"] [opt "[option -object-filters] [arg filterSpec]"] [opt [arg initBlock]]] To create a direct instance of [cmd nx::Object] having an automatically assigned, implict object name, use [method new] on [cmd nx::Object]. Note that [method new] is defined by [cmd nx::Class] and is available to [cmd nx::Object] being an instance of [cmd nx::Class]. Using [method new] allows for creating anonymous, inline objects, for example. [list_end] The configuration options for direct and indirect instances of [cmd nx::Object], which can be passed when calling [method create] and [method new], are documented in the subsequent section. [section {Configuration Options for Instances of nx::Object}] [para] Configuration options can be used for configuring objects during their creation by passing the options as non-positional arguments into calls of [method new] and [method create] (see [cmd nx::Class]). An existing object can be queried for its current configuration using [method cget] and it can be re-configured using [method configure]. Legal configuration options are: [list_begin options] [opt_def -class [opt [arg className]]] Retrieves the current class of the object or sets the object's class to [arg className], if provided. [opt_def -object-filters [opt [arg filterMethods]]] Retrieves the list of currently active per-object filter methods or sets a list of per-object filter methods, if [arg filterMethods] is provided. [opt_def -object-mixins [opt [arg mixinSpecs]]] If [arg mixinSpecs] is not specified, retrieves the list of currently active per-object mixin specifications. If [arg mixinSpecs] is specified, sets a list of per-object mixin specifications to become active. [term "mixin class"]es are returned or set in terms of a list of [term "mixin specification"]s. [list_end] [section {Methods for Instances of nx::Object}] [list_begin commands] [cmd_def alias] [list_begin definitions] [include alias.man.inc] [list_end] [cmd_def cget] [list_begin definitions] [call [arg obj] [method cget] [arg configurationOption]] The method is used to obtain the current value of [arg configurationOption] for [arg obj]. The configuration options available for querying through [method cget] are determined by the configurable properties defined by the class hierarchy of [arg obj]. The queriable configuration options for [arg obj] can be obtained by calling [method "info configure"]. The [arg configurationOption] can be set and modified using [method configure]. [example_begin] % nx::Object create obj ::obj % ::obj info configure ?-object-mixins /mixinreg .../? ?-class /class/? ?-object-filters /filterreg .../? ?/__initblock/? % ::obj cget -class ::nx::Object [example_end] [list_end] [cmd_def configure] [list_begin definitions] [call [arg obj] [method configure] [opt "[arg configurationOption] [arg value] ..."]] This method sets configuration options on an object. The configuration options available for setting on [arg obj] are determined by the configurable properties defined by the class hierarchy of [arg obj]. The settable configuration options for [arg obj] can be obtained by calling [method "info configure"]. Furthermore, [method configure] is also called during object construction. Under object construction, it receives the arguments passed into calls of [method create] and [method new]. Options set using [method configure] can be retrieved using [method cget]. [example_begin] % nx::Class create Foo {:property x} ::Foo % Foo create f1 -x 101 ::f1 % f1 cget -x 101 % f1 configure -x 200 % f1 cget -x 200 [example_end] [list_end] [cmd_def contains] [list_begin definitions] [call [arg obj] [method contains] [opt "-withnew [arg trueFalse]"] [opt "-object [arg objectName]"] [opt "-class [arg className]"] [arg cmds]] This method acts as a builder for nested object structures. Object and class construction statements passed to this method as its last argument [arg cmds] are evaluated in a way so that the receiver object [arg obj] becomes the parent of the newly constructed objects and classes. This is realized by setting explicitly the namespace for constructing relatively named objects. Fully qualified object names in [arg cmds] evade the nesting. [para] [option "-withnew"] requests the automatic rescoping of objects created using [method new] so that they become nested into the receiver object [arg obj], rather than being created in the default namespace for autonamed objects (i.e., ::nsf). If turned off, autonamed objects do not become children of [arg obj]. [para] The parent object [arg objectName] to be used instead of [arg obj] can be specified using [option "-object"]. If this explicitly set parent object does not exist prior to calling [method contains], it will be created on the fly as a direct instance of [cmd nx::Object]. Alternatively, using [option "-class"], a class [arg className] other than [cmd nx::Object] for the on-the-fly creation of [arg objectName] can be provided. [example { % nx::Class create Window { :contains { # # Become children of Window, implicitly # nx::Class create Header; # Window::Header nx::Object create Panel; # Window::Panel } # # Explicitly declared a child of Window using [self] # nx::Class create [self]::Slider; # Window::Slider # # Fully-qualified objects do not become nested # nx::Class create ::Door; # ::Door } ::Window % ::Window info children ::Window::Panel ::Window::Header ::Window::Slider }] [list_end] [cmd_def copy] [list_begin definitions] [call [arg obj] [method copy] [arg newObjectName]] Creates a full and deep copy of a source object [arg obj]. The object's copy [arg newObjectName] features all structural and behavioral properties of the source object, including object variables, per-object methods, nested objects, slot objects, namespaces, filters, mixins, and traces. [list_end] [comment { [cmd_def defaultmethod] [list_begin definitions] [call [arg obj] [cmd defaultmethod]] This method is called implicitly when the object command is invoked without an argument. By default, the [cmd defaultmethod] implementation returns the fully qualified object name. [example_begin] % Object create ::foo ::foo % ::foo defaultmethod ::foo % ::foo ::foo [example_end] [list_end] }] [cmd_def delete] [list_begin definitions] [include delete.man.inc] [list_end] [cmd_def destroy] [list_begin definitions] [call [arg obj] [method destroy]] This method allows for explicitly destructing an object [arg obj], potentially prior to [arg obj] being destroyed by the object system (e.g. during the shutdown of the object system upon calling [cmd exit]): [example {[nx::Object new] destroy}] By providing a custom implementation of [method destroy], the destruction procedure of [arg obj] can be customized. Typically, once the application-specific destruction logic has completed, a custom [method destroy] will trigger the actual, physical object destruction via [cmd next]. [example { % [nx::Object create obj { :public method destroy {} { puts "destroying [self]" next; # physical destruction } }] destroy destroying ::obj }] A customized object-desctruction scheme can be made shared between the instances of a class, by defining the custom [method destroy] for an application class: [example_begin] % nx::Class create Foo { :method destroy {} { puts "destroying [lb]self[rb]" next; # physical destruction } } ::Foo % Foo create f1 ::f1 % f1 destroy destroying ::f1 [example_end] Physical destruction is performed by clearing the in-memory object storage of [arg obj]. This is achieved by passing [arg obj] into a call to [method dealloc] provided by [cmd nx::Class]. A near, scripted equivalent to the C-implemented [method destroy] provided by [cmd nx::Object] would look as follows: [example { % Object method destroy {} { [:info class] dealloc [self] } }] Note, however, that [method destroy] is protected against application-level redefinition. Trying to evaluate the above script snippet yields: [example { refuse to overwrite protected method 'destroy'; derive e.g. a sub-class! }] A custom [method destroy] must be provided as a refinement in a subclass of [cmd nx::Object] or in a [term "mixin class"]. [list_end] [cmd_def eval] [list_begin definitions] [call [arg obj] [method eval] [arg arg] [opt "[arg arg] ..."]] Evaluates a special Tcl script for the scope of [arg obj] in the style of Tcl's [cmd eval]. There are, however, notable differences to the standard [cmd eval]: In this script, the colon-prefix notation is available to dispatch to methods and to access variables of [arg obj]. Script-local variables, which are thrown away once the evaluation of the script has completed, can be defined to store intermediate results. [example { % nx::Object create obj { :object property {bar 1} :public object method foo {x} { return $x } } ::obj % ::obj eval { set y [:foo ${:bar}] } 1 }] [list_end] [cmd_def filters] [list_begin definitions] [include filter.man.inc] [list_end] [cmd_def forward] [list_begin definitions] [include forward.man.inc] [list_end] [cmd_def info] [list_begin definitions] [call [arg obj] [method "info children"] [opt "[option -type] [arg className]"] [opt [arg pattern]]] Retrieves the list of nested (or aggregated) objects of [arg obj]. The resulting list contains the fully qualified names of the nested objects. If [option -type] is set, only nested objects which are direct or indirect instances of class [arg className] are returned. Using [arg pattern], only nested objects whose names match [arg pattern] are returned. The [arg pattern] string can contain special matching characters (see [cmd "string match"]). This method allows for introspecting on [method contains]. [call [arg obj] [method "info class"]] Returns the fully qualified name of the current [cmd nx::Class] of [arg obj]. In case of [term "re-classification"] (see [method configure]), the returned class will be different from the [cmd nx::Class] from which [arg obj] was originally instantiated using [method create] or [method new]. [call [arg obj] [method "info has"] [opt "[method mixin] | [method namespace] | [method type]"] [opt "[arg arg] ..."]] [list_begin definitions] [def "[arg obj] [method "info method has mixin"] [arg className]"] Verifies whether [arg obj] has a given [cmd nx::Class] [arg className] registered as a [term "mixin class"] (returns: [const true]) or not (returns: [const false]). [def "[arg obj] [method "info has namespace"]"] Checks whether the object has a companion Tcl namespace (returns: [const true]) or not (returns: [const false]). The namespace could have been created using, for example, [method "object require namespace"]. [comment {Note that the results do not necessarily correspond to those yielded by '''[namespace exists /obj/]'''.}] [def "[arg obj] [method "info has type"] [arg className]"] Tests whether the [cmd nx::Class] [arg className] is a type of the object (returns: [const true]) or not (returns: [const false]). That is, the method checks whether the object is a direct instance of [arg className] or an indirect instance of one of the superclasses of [arg className]. [list_end] [call [arg obj] [method "info lookup"] [arg submethod] [opt "[arg arg] ..."]] A collection of submethods to retrieve structural features (e.g. configuration options, [term "slot object"]s) and behavioral features (e.g. methods, [term "filter"]s) available for [arg obj] from the perspective of a client to [arg obj]. Features provided by [arg obj] itself and by the classes in its current linearisation list are considered. [list_begin definitions] [def "[arg obj] [method {info lookup configure parameters}] [opt [arg namePattern]]"] Returns all configuration options available for [arg obj] as a list of method-parameter definitions. They can be used, for example, to define a custom method refinement for [method configure]. The returned configuration options can be limited to those whose names match [arg pattern] (see [cmd "string match"]). [def "[arg obj] [method {info lookup configure syntax}]"] Returns all configuration options available for [arg obj] as a concrete-syntax description to be used in human-understandable messages (e.g. errors or warnings, documentation strings). [def "[arg obj] [method "info lookup filter"] [arg name]"] Returns the [term "method handle"] for the [term "filter"] method [arg name], if currently registered. If there is no filter [arg name] registered, an empty string is returned. [def "[arg obj] [method "info lookup filters"] [opt [option -guards]] [opt [arg namePattern]]"] Returns the [term "method handle"]s of all filters which are active on [arg obj]. By turning on the [term switch] [option -guards], the corresponding guard expressions, if any, are also reported for each filter as a three-element list: [arg methodHandle] -guard [arg guardExpr]. The returned filters can be limited to those whose names match [arg namePattern] (see [cmd "string match"]). [def "[arg obj] [method "info lookup method"] [arg name]"] Returns the [term "method handle"] for a method [arg name] if a so-named method can be invoked on [arg obj]. If there is no method [arg name], an empty string is returned. [def "[arg obj] [method "info lookup methods"] [opt [arg namePattern]]"] Returns the names of all methods (including aliases and forwarders) which can be invoked on [arg obj]. The returned methods can be limited to those whose names match [arg namePattern] (see [cmd "string match"]). [def "[arg obj] [method "info lookup mixins"] [opt [option -guards]] [opt [arg namePattern]]"] Returns the object names of all [term "mixin class"]es which are currently active on [arg obj]. By turning on the [term switch] [option -guards], the corresponding guard expressions, if any, are also reported as a three-element list for each [term "mixin class"]: [arg className] -guard [arg guardExpr]. The returned [term "mixin class"]es can be limited to those whose names match [arg namePattern] (see [cmd "string match"]). [def "[arg obj] [method "info lookup slots"] [opt "[option "-type"] [arg className]"] [opt "[option "-source"] all | application | system"] [opt [arg namePattern]]"] Returns the command names of all [term "slot object"]s responsible for managing properties, variables, and relations of [arg obj]. The returned [term "slot object"]s can be limited according to any or a combination of the following criteria: First, [term "slot object"]s can be filtered based on their command names matching [arg namePattern] (see [cmd "string match"]). Second, [option "-type"] allows one to select [term "slot object"]s which are instantiated from a subclass [arg className] of [cmd nx::Slot] (default: [cmd nx::Slot]) . Third, [option -source] restricts [term "slot object"]s returned according to their provenance in either the NX [emph system] classes or the [emph application] classes present in the linearisation list of [arg obj] (default: [emph all]). [para] To extract details of each [term "slot object"], use the [method info] submethods available for each [term "slot object"]. [def "[arg obj] [method "info lookup variables"]"] Returns the command names of all [term "slot object"]s responsible for managing properties and variables of [arg obj], if provided by [arg obj] or the classes in the linearisation list of [arg obj]. [para] This is equivalent to calling: [arg obj] [method "info lookup slots"] -type ::nx::VariableSlot -source all [opt [arg namePattern]]. [para] To extract details of each [term "slot object"], use the [method info] submethods available for each [term "slot object"]. [list_end] [call [arg obj] [method {info name}]] Returns the unqualified name of an object, i.e., the object name without any namespace qualifiers. [include info.man.inc] [call [arg obj] [method {info parent}]] Returns the fully qualified name of the parent object of [arg obj], if any. If there is no parent object, the name of the Tcl namespace containing [arg obj] (e.g. "::") will be reported. [call [arg obj] [method {info precedence}] [opt [option -intrinsic]] [opt [arg pattern]]] Lists the classes from which [arg obj] inherits structural (e.g. properties) and behavioral features (e.g. methods) and methods, in order of the [term linearisation] scheme in [term NX]. By setting the [term switch] [option -intrinsic], only classes which participate in superclass/subclass relationships (i.e., intrinsic classes) are returned. If a [arg pattern] is provided only classes whose names match [arg pattern] are returned. The [arg pattern] string can contain special matching characters (see [cmd "string match"]). [call [arg obj] [method {info variable}] [arg option] [arg handle]] Retrieves selected details about a variable represented by the given [arg handle]. A [arg handle] can be obtained by querying [arg obj] using [method "info [vset SCOPE] variables"] and [method "info lookup variables"]. Valid values for [arg option] are: [list_begin itemized] [item] [const name] returns the variable name. [item] [const parameter] returns a canonical parameter specification eligible to (re-)define the given variable (e.g. using [method "[vset SCOPE] variable"]) in a new context. [item] [const definition] returns a canonical representation of the definition command used to create the variable in its current configuration. [list_end] [call [arg obj] [method {info vars}] [opt [arg pattern]]] Yields a list of Tcl variable names created and defined for the scope of [arg obj], i.e., object variables. The list can be limited to object variables whose names match [arg pattern]. The [arg pattern] string can contain special matching characters (see [cmd "string match"]). [list_end] [comment { [cmd_def init] [list_begin definitions] [call [arg obj] [cmd init] [opt "[arg arg] ..."]] The method [method init] is called during the object construction process. It is invoked as the last step during object construction (i.e. after method [method configure]) to provide the fully initialized state of the object. Note that the definition of an [method init] method must contain an empty parameter specification, since [method init] is always called with an empty argument list. [example_begin] % nx::Class create Foo {:property x} % Foo method init {} {set :y [lb]expr {${:x} + 1}[rb]} % Foo public method bar {} {return ${:y}} % Foo create f1 -x 101 % f1 cget -x 101 % f1 bar 102 [example_end] [list_end] }] [cmd_def method] [list_begin definitions] [include method.man.inc] [list_end] [cmd_def move] [list_begin definitions] [call [arg obj] [method move] [arg newObjectName]] Effectively renames an object. First, the source object [arg obj] is cloned into a target object [arg newObjectName] using [method copy]. Second, the source object [arg obj] is destroyed by invoking [method destroy]. [method move] is also called internally when [cmd rename] is performed for a Tcl command representing an object. [list_end] [cmd_def mixins] [list_begin definitions] [include mixin.man.inc] [list_end] [cmd_def __object_configureparameter] [list_begin definitions] [def "[arg obj] [method "__object_configureparameter"]"] Computes and returns the configuration options available for [arg obj], to be consumed as method-parameter specification by [method configure]. [list_end] [cmd_def property] [list_begin definitions] [call [arg obj] [method "object property"] [opt "[option -accessor] [const public] | [const protected] | [const private]"] [opt "[option -configurable] [arg trueFalse]"] [opt [option -incremental]] [opt "[option -class] [arg className]"] [opt [option -nocomplain]] [arg spec] [opt [arg initBlock]]] [include property.man.inc] [para] By default, the [term property] will ascertain that no (potentially) pre-existing and equally named object variable will be overwritten when defining the property. In case of a conflict, an error exception is thrown: [example { % Object create obj { set :x 1 } ::obj % ::obj object property {x 2} object ::obj has already an instance variable named 'x' }] If the [term switch] [option -nocomplain] is on, this check is omitted (continuing the above example): [example { % ::obj object property -nocomplain {x 2} % ::obj eval {set :x} 2 }] [list_end] [cmd_def require] [list_begin definitions] [call [arg obj] [method "require namespace"]] Create a Tcl namespace named after the object [arg obj]. All object variables become available as namespace variables. [include require.man.inc] [list_end] [cmd_def unknown] [list_begin definitions] [call [arg obj] [method unknown] [arg unknownMethodName] [opt "[arg arg] ..."]] This method is called implicitly whenever an unknown method is invoked. [arg unknownMethodName] indicates the unresolvable method name, followed by the remainder of the original argument vector as a number of [arg arg] of the indirected method invocation. [list_end] [cmd_def variable] [list_begin definitions] [comment {::nx::Object variable ?-accessor /value/? ?-incremental? ?-class /value/? ?-configurable /boolean/? ?-initblock /value/? ?-nocomplain? /spec/ ?/defaultValue/?}] [call [arg obj] [method "object variable"] [opt "[option -accessor] [const public] | [const protected] | [const private]"] [opt [option -incremental]] [opt "[option -class] [arg className]"] [opt "[option -configurable] [arg trueFalse]"] [opt "[option -initblock] [arg script]"] [opt [option -nocomplain]] [arg spec] [opt [arg defaultValue]]] [include variable.man.inc] [para] By default, the [term variable] will ascertain that a pre-existing and equally named object variable will not be overwritten when defining the [term variable]. In case of a conflict, an error exception is thrown: [example { % Object create obj { set :x 1 } ::obj % ::obj object variable x 2 object ::obj has already an instance variable named 'x' }] If the [term switch] [option -nocomplain] is on, this check is omitted (continuing the above example): [example { % ::obj object variable -nocomplain x 2 % ::obj eval {set :x} 2 }] [list_end] [list_end] [comment { COMMANDS list }] [comment { [cmd nx::Object] provides a set of default implementations for internally called methods, which are called primarily during the creation or destruction of NX objects. Application developers can provide custom implementations of these methods by providing tailored implementations for these methods in application classes (i.e., subclasses of [cmd nx::Object]). An adequate method implementation must comply with the method signature interfaces described below. }] [section {Object Self-Reference}] Objects are naturally recursive, with methods of an object [const ::obj] frequently invoking other methods in the same object [const ::obj] and accessing [const ::obj]'s object variables. To represent these self-references effectively in method bodies, and dependening on the usage scenario, NX offers two alternative notations for self-references: one based on a special-purpose syntax token ("colon prefix"), the other based on the command [cmd nx::current]. [para] Both, the colon-prefix notation and [cmd nx::current], may be used only in method bodies and scripts passed to [method eval]. If they appear anywhere else, an error will be reported. There are three main use cases for self-references: [list_begin enumerated] [enum] As a [emph placeholder] for the currently active object, [cmd nx::current] can be used to retrieve the object name. [enum] Reading and writing [emph "object variables"] directly (i.e. without getter/setter methods in place) require the use of variable names carrying the prefix [const :] ("colon-prefix notation"). Internally, colon-prefixed variable names are processed using Tcl's variable resolvers. Alternatively, one can provide for getter/setter methods for object variables (see [method property] and [method variable]). [enum] [emph {Self-referential method calls}] can be defined via prefixing ([const :]) the method names or, alternatively, via [cmd nx::current]. Internally, colon-prefixed method names are processed using Tcl's command resolvers. The colon-prefix notation is recommended, also because it has a (slight) performance advantage over [cmd nx::current] which requires two rather than one command evaluation per method call. [list_end] See the following listing for some examples corresponding to use cases 1--3: [example { Object create ::obj { puts [current]; # 1) print name of currently active object ('::obj') set :x 1; :object variable y 2; # 2) object variables :public object method print {} { set z 3; # 2.a) method-local variable puts ${:x}-${:y}-$z; # 2.b) variable substitution using '$' and ':' puts [set :x]-[set :y]-[set z]; # 2.c) reading variables using 'set' set :x 1; incr :y; # 2.d) writing variables using 'set', 'incr', ... } :public object method show {} { :print; # 3.a) self-referential method call using ':' [current] print; # 3.b) self-referential method call using 'nx::current' [current object] print; # 3.c) self-referential method call using 'nx::current object' } :show }}] [manpage_end] doc/alias.man.inc000066400000000000000000000042251242365656200141770ustar00rootroot00000000000000[comment {-*- tcl -*- manpage fragment for alias method, shared by nx::Object and nx::Class}] [keywords "alias method"] [keywords "value checker"] [keywords "method handle"] [call [arg [vset CMD]] [opt "[method public] | [method private] | [method protected]"] [method "[vset MODIFIER] alias"] [arg methodName] [opt "[option -returns] [arg valueChecker]"] [opt "[option -frame] [const object] | [const method]"] [arg cmdName]] Define an [term "alias method"] for the given [vset SCOPE]. The resulting method registers a pre-existing Tcl command [arg cmdName] under the (alias) name [arg methodName] with the [vset SCOPE]. If [arg cmdName] refers to another [method method], the corresponding argument should be a valid [term "method handle"]. If a Tcl command (e.g., a [cmd proc]), the argument should be a fully qualified Tcl command name. If aliasing a subcommand (e.g., [cmd "array exists"]) of a Tcl namespace ensemble (e.g., [cmd array]), [arg cmdName] must hold the fully qualified subcommand name (and not the ensemble name of the subcommand). [para] As for a regular [method "[vset SCOPE] method"], [option "-returns"] allows for setting a [term "value checker"] on the values returned by the aliased command [arg cmdName]. [para] When creating an [term "alias method"] for a [emph C-implemented] Tcl command (i.e., command defined using the Tcl/NX C-API), [option -frame] sets the scope for variable references used in the aliased command. If the provided value is [const object], then variable references will be resolved in the context of the called object, i.e., the object upon which the [term "alias method"] is invoked, as if they were object variables. There is no need for using the colon-prefix notation for identifying object variables. If the value is [const method], then the aliased command will be executed as a regular method call. The command is aware of its called-object context; i.e., it can resolve [cmd ::nx::self]. In addition, the [term "alias method"] has access to the method-call context (e.g., [cmd nx::next]). If [option "-frame"] is omitted, and by default, the variable references will resolve in the context of the caller of the [term "alias method"]. doc/configure.3000066400000000000000000000150561242365656200137120ustar00rootroot00000000000000'\" '\" Generated from file 'configure.man' by tcllib/doctools with format 'nroff' '\" Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). '\" '\" The definitions below are for supplemental macros used in Tcl/Tk '\" manual entries. '\" '\" .AP type name in/out ?indent? '\" Start paragraph describing an argument to a library procedure. '\" type is type of argument (int, etc.), in/out is either "in", "out", '\" or "in/out" to describe whether procedure reads or modifies arg, '\" and indent is equivalent to second arg of .IP (shouldn't ever be '\" needed; use .AS below instead) '\" '\" .AS ?type? ?name? '\" Give maximum sizes of arguments for setting tab stops. Type and '\" name are examples of largest possible arguments that will be passed '\" to .AP later. If args are omitted, default tab stops are used. '\" '\" .BS '\" Start box enclosure. From here until next .BE, everything will be '\" enclosed in one large box. '\" '\" .BE '\" End of box enclosure. '\" '\" .CS '\" Begin code excerpt. '\" '\" .CE '\" End code excerpt. '\" '\" .VS ?version? ?br? '\" Begin vertical sidebar, for use in marking newly-changed parts '\" of man pages. The first argument is ignored and used for recording '\" the version when the .VS was added, so that the sidebars can be '\" found and removed when they reach a certain age. If another argument '\" is present, then a line break is forced before starting the sidebar. '\" '\" .VE '\" End of vertical sidebar. '\" '\" .DS '\" Begin an indented unfilled display. '\" '\" .DE '\" End of indented unfilled display. '\" '\" .SO '\" Start of list of standard options for a Tk widget. The '\" options follow on successive lines, in four columns separated '\" by tabs. '\" '\" .SE '\" End of list of standard options for a Tk widget. '\" '\" .OP cmdName dbName dbClass '\" Start of description of a specific option. cmdName gives the '\" option's name as specified in the class command, dbName gives '\" the option's name in the option database, and dbClass gives '\" the option's class in the option database. '\" '\" .UL arg1 arg2 '\" Print arg1 underlined, then print arg2 normally. '\" '\" RCS: @(#) $Id: man.macros,v 1.1 2009/01/30 04:56:47 andreas_kupries Exp $ '\" '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. .if t .wh -1.3i ^B .nr ^l \n(.l .ad b '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ta \\n()Au \\n()Bu .ie !"\\$3"" \{\ \&\\$1 \\fI\\$2\\fP (\\$3) .\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n .\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. .AS Tcl_Interp Tcl_CreateInterp in/out '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ .\" Draw four-sided box normally, but don't draw top of .\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .if !"\\$2"" .br .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ .\" Draw three-sided box if this is the box's first page, .\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .nf .sp .. '\" # DE - end display .de DE .fi .RE .sp .. '\" # SO - start of list of standard options .de SO .SH "STANDARD OPTIONS" .LP .nf .ta 4c 8c 12c .ft B .. '\" # SE - end of list of standard options .de SE .fi .ft R .LP See the \\fBoptions\\fR manual entry for details on the standard options. .. '\" # OP - start of full description for a single option .de OP .LP .nf .ta 4c Command-Line Name: \\fB\\$1\\fR Database Name: \\fB\\$2\\fR Database Class: \\fB\\$3\\fR .fi .. '\" # CS - begin code excerpt .de CS .RS .nf .ta .25i .5i .75i 1i .. '\" # CE - end code excerpt .de CE .fi .RE .. .de UL \\$1\l'|0\(ul'\\$2 .. .TH "nx::configure" 3 2.0 configure "" .BS .SH NAME nx::configure \- Get and set configuration options on the object system .SH SYNOPSIS \fBconfigure\fR \fIoption\fR ?\fIarg\fR? .sp .BE .SH DESCRIPTION .TP \fBconfigure\fR \fIoption\fR ?\fIarg\fR? This command sets and retrieves options for the NX object system. Legal configuration options are: .RS .IP \(bu \fBdefaultMethodCallProtection\fR returns the currently active call-protection level used as default for newly defined method implementations (if not specified explicitly by a method definition), if \fIarg\fR is not provided. If \fIarg\fR is set, this default call-protection level is re-set to any of the available ones: \fBpublic\fR, \fBprivate\fR, \fBprotected\fR. .IP \(bu \fBdefaultAccessor\fR returns the currently active call-protection level used as the default for newly defined properties (if not specified explicitly by a property definition), if \fIarg\fR is not provided. If \fIarg\fR is set, this default call-protection level is re-set to any of the following values: \fBpublic\fR, \fBprivate\fR, \fBprotected\fR, or \fBnone\fR. \fBnone\fR indicates that no accessors (getter/setter methods) will be generated for newly defined properties, if not requested explicitly. .RE .PP .SH COPYRIGHT .nf Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). .fidoc/configure.man000066400000000000000000000027631242365656200143240ustar00rootroot00000000000000[comment {-*- tcl -*- nx::configure manpage}] [manpage_begin nx::configure 3 2.0.0] [copyright { 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT).}] [titledesc {Get and set configuration options on the object system}] [description] [list_begin definitions] [call [cmd "configure"] [arg option] [opt [arg arg]]] This command sets and retrieves options for the NX object system. Legal configuration options are: [list_begin itemized] [item] [option "defaultMethodCallProtection"] returns the currently active call-protection level used as default for newly defined method implementations (if not specified explicitly by a method definition), if [arg arg] is not provided. If [arg arg] is set, this default call-protection level is re-set to any of the available ones: [const public], [const private], [const protected]. [item] [option "defaultAccessor"] returns the currently active call-protection level used as the default for newly defined properties (if not specified explicitly by a property definition), if [arg arg] is not provided. If [arg arg] is set, this default call-protection level is re-set to any of the following values: [const public], [const private], [const protected], or [const none]. [const none] indicates that no accessors (getter/setter methods) will be generated for newly defined properties, if not requested explicitly. [list_end] [list_end] [manpage_end] doc/current.3000066400000000000000000000215241242365656200134100ustar00rootroot00000000000000'\" '\" Generated from file 'current.man' by tcllib/doctools with format 'nroff' '\" Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). '\" '\" The definitions below are for supplemental macros used in Tcl/Tk '\" manual entries. '\" '\" .AP type name in/out ?indent? '\" Start paragraph describing an argument to a library procedure. '\" type is type of argument (int, etc.), in/out is either "in", "out", '\" or "in/out" to describe whether procedure reads or modifies arg, '\" and indent is equivalent to second arg of .IP (shouldn't ever be '\" needed; use .AS below instead) '\" '\" .AS ?type? ?name? '\" Give maximum sizes of arguments for setting tab stops. Type and '\" name are examples of largest possible arguments that will be passed '\" to .AP later. If args are omitted, default tab stops are used. '\" '\" .BS '\" Start box enclosure. From here until next .BE, everything will be '\" enclosed in one large box. '\" '\" .BE '\" End of box enclosure. '\" '\" .CS '\" Begin code excerpt. '\" '\" .CE '\" End code excerpt. '\" '\" .VS ?version? ?br? '\" Begin vertical sidebar, for use in marking newly-changed parts '\" of man pages. The first argument is ignored and used for recording '\" the version when the .VS was added, so that the sidebars can be '\" found and removed when they reach a certain age. If another argument '\" is present, then a line break is forced before starting the sidebar. '\" '\" .VE '\" End of vertical sidebar. '\" '\" .DS '\" Begin an indented unfilled display. '\" '\" .DE '\" End of indented unfilled display. '\" '\" .SO '\" Start of list of standard options for a Tk widget. The '\" options follow on successive lines, in four columns separated '\" by tabs. '\" '\" .SE '\" End of list of standard options for a Tk widget. '\" '\" .OP cmdName dbName dbClass '\" Start of description of a specific option. cmdName gives the '\" option's name as specified in the class command, dbName gives '\" the option's name in the option database, and dbClass gives '\" the option's class in the option database. '\" '\" .UL arg1 arg2 '\" Print arg1 underlined, then print arg2 normally. '\" '\" RCS: @(#) $Id: man.macros,v 1.1 2009/01/30 04:56:47 andreas_kupries Exp $ '\" '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. .if t .wh -1.3i ^B .nr ^l \n(.l .ad b '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ta \\n()Au \\n()Bu .ie !"\\$3"" \{\ \&\\$1 \\fI\\$2\\fP (\\$3) .\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n .\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. .AS Tcl_Interp Tcl_CreateInterp in/out '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ .\" Draw four-sided box normally, but don't draw top of .\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .if !"\\$2"" .br .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ .\" Draw three-sided box if this is the box's first page, .\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .nf .sp .. '\" # DE - end display .de DE .fi .RE .sp .. '\" # SO - start of list of standard options .de SO .SH "STANDARD OPTIONS" .LP .nf .ta 4c 8c 12c .ft B .. '\" # SE - end of list of standard options .de SE .fi .ft R .LP See the \\fBoptions\\fR manual entry for details on the standard options. .. '\" # OP - start of full description for a single option .de OP .LP .nf .ta 4c Command-Line Name: \\fB\\$1\\fR Database Name: \\fB\\$2\\fR Database Class: \\fB\\$3\\fR .fi .. '\" # CS - begin code excerpt .de CS .RS .nf .ta .25i .5i .75i 1i .. '\" # CE - end code excerpt .de CE .fi .RE .. .de UL \\$1\l'|0\(ul'\\$2 .. .TH "nx::current" 3 2.0 current "" .BS .SH NAME nx::current \- Return information about the method callstack .SH SYNOPSIS \fBcurrent\fR ?\fIoption\fR? .sp .BE .SH DESCRIPTION .TP \fBcurrent\fR ?\fIoption\fR? This introspection command provides information about various details, to be identified using \fIoption\fR, on the callstack. The command is invoked from a method body. If \fIoption\fR is not provided, \fBnx::current\fR will default to option \fBobject\fR (see below). \fBnx::current\fR operates on the Tcl callstack and is aware of NX-specific callstack and stackframe details. \fIoption\fR can be any of the following: .RS .IP \(bu \fBactivelevel\fR returns the actual callstack level which calls into the currently executing method directly. This \fBactivelevel\fR might correspond the \fBcallinglevel\fR, but this is not necessarily the case. The \fBactivelevel\fR also includes intermediate calls, such as \fBnx::next\fR invocations. The level is reported as an absolute level number (# followed by a digit) to be directly used as the first argument to \fBuplevel\fR or \fBupvar\fR. .IP \(bu \fBargs\fR returns the list of argument values passed into the currently executing method implementation. .IP \(bu \fBcalledclass\fR returns the name of the class that provides the method implementation to which the intercepted method call is to be redirected (only available from within filter methods). .IP \(bu \fBcalledmethod\fR returns the original method name requested by intercepted method call (only available from within filter methods). .IP \(bu \fBcallingclass\fR returns the name of the class which provides the method implementation calling into the currently executing method. See also \fBcallingobject\fR. .IP \(bu \fBcallinglevel\fR resolves the callstack level of the originating invocation of the currently executing method implementation. Callstack levels introduced by method interception (e.g., filters) and by method combination (\fBnx::next\fR) are ignored. The level is reported as an absolute level number (\fB#\fR followed by a digit) to be directly used as the first argument to \fBuplevel\fR or \fBupvar\fR. See also \fBactivelevel\fR. .IP \(bu \fBcallingobject\fR returns the name of the object which is calling into the currently executing method. See also \fBcallingclass\fR. .IP \(bu \fBclass\fR returns the name of the class providing the currently executing method implementation. The returned method-providing class may be different to the class of the current object. If called from within a method implementation provided by the current object itself, an empty string is returned. .IP \(bu \fBfilterreg\fR returns the object (class) on which the currently executing method was registered as a filter method (only available from within filter methods). .IP \(bu \fBisnextcall\fR will return 1, if the currently executing method implementation was invoked via \fBnx::next\fR; 0 otherwise. .IP \(bu \fBmethod\fR returns the name of the currently executing method. If an ensemble-method call, the name of the bottom-most ("leaf") method is returned. .IP \(bu \fBmethodpath\fR returns the combined name of the currently executing method (including all ensemble levels) in an ensemble-method call. Otherwise, for a regular method call, the result corresponds to the result of option \fBmethod\fR. .IP \(bu \fBnextmethod\fR returns the name of the next most specific method implementation to be called when invoking \fBnx::next\fR. .IP \(bu \fBobject\fR gives the name of the object on which the currently executing method implementation is evaluated. .RE .PP .SH COPYRIGHT .nf Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). .fidoc/current.man000066400000000000000000000101701242365656200140140ustar00rootroot00000000000000[comment {-*- tcl -*- nx::current manpage}] [manpage_begin nx::current 3 2.0.0] [copyright { 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT).}] [titledesc {Return information about the method callstack}] [description] [list_begin definitions] [call [cmd "current"] [opt [arg option]]] This introspection command provides information about various details, to be identified using [arg option], on the callstack. The command is invoked from a method body. If [arg option] is not provided, [cmd nx::current] will default to option [option "object"] (see below). [cmd nx::current] operates on the Tcl callstack and is aware of NX-specific callstack and stackframe details. [arg option] can be any of the following: [comment {activelevel activemixin args calledclass calledmethod calledproc callingclass callinglevel callingmethod callingobject callingproc class filterreg isnextcall method methodpath nextmethod object proc}] [list_begin itemized] [item] [option "activelevel"] returns the actual callstack level which calls into the currently executing method directly. This [option "activelevel"] might correspond the [option "callinglevel"], but this is not necessarily the case. The [option "activelevel"] also includes intermediate calls, such as [cmd nx::next] invocations. The level is reported as an absolute level number (# followed by a digit) to be directly used as the first argument to [cmd uplevel] or [cmd upvar]. [comment {[item] [option "activemixin"] ...}] [item] [option "args"] returns the list of argument values passed into the currently executing method implementation. [item] [option "calledclass"] returns the name of the class that provides the method implementation to which the intercepted method call is to be redirected (only available from within filter methods). [item] [option "calledmethod"] returns the original method name requested by intercepted method call (only available from within filter methods). [item] [option "callingclass"] returns the name of the class which provides the method implementation calling into the currently executing method. See also [option "callingobject"]. [item] [option "callinglevel"] resolves the callstack level of the originating invocation of the currently executing method implementation. Callstack levels introduced by method interception (e.g., filters) and by method combination ([cmd nx::next]) are ignored. The level is reported as an absolute level number ([const #] followed by a digit) to be directly used as the first argument to [cmd uplevel] or [cmd upvar]. See also [option "activelevel"]. [item] [option "callingobject"] returns the name of the object which is calling into the currently executing method. See also [option "callingclass"]. [item] [option "class"] returns the name of the class providing the currently executing method implementation. The returned method-providing class may be different to the class of the current object. If called from within a method implementation provided by the current object itself, an empty string is returned. [item] [option "filterreg"] returns the object (class) on which the currently executing method was registered as a filter method (only available from within filter methods). [item] [option "isnextcall"] will return 1, if the currently executing method implementation was invoked via [cmd nx::next]; 0 otherwise. [item] [option method] returns the name of the currently executing method. If an ensemble-method call, the name of the bottom-most ("leaf") method is returned. [item] [option "methodpath"] returns the combined name of the currently executing method (including all ensemble levels) in an ensemble-method call. Otherwise, for a regular method call, the result corresponds to the result of option [option "method"]. [item] [option "nextmethod"] returns the name of the next most specific method implementation to be called when invoking [cmd nx::next]. [item] [option "object"] gives the name of the object on which the currently executing method implementation is evaluated. [list_end] [list_end] [manpage_end] doc/delete.man.inc000066400000000000000000000021321242365656200143430ustar00rootroot00000000000000[comment {-*- tcl -*- manpage fragment for delete method, shared by nx::Object and nx::Class}] [keywords "alias method"] [keywords "forwarder method"] [call [arg [vset CMD]] [method [string trim "delete [vset MODIFIER]"]] [arg feature] [arg arg]] This method serves as the equivalent to Tcl's [cmd rename] for removing structural (properties, variables) and behavioral features (methods) of the [vset SCOPE]: [def "[arg [vset CMD]] [method "delete [vset MODIFIER] property"] [arg propertyName]"] [def "[arg [vset CMD]] [method "delete [vset MODIFIER] variable"] [arg variableName]"] [def "[arg [vset CMD]] [method "delete [vset MODIFIER] method"] [arg methodName]"] Removes a property [arg propertyName], variable [arg variableName], and method [arg methodName], respectively, previously defined for the scope of the [vset SCOPE]. [para] [method "delete [vset MODIFIER] method"] can be equally used for removing regular methods (see [method "[vset MODIFIER] method"]), an [term "alias method"] (see [method "[vset MODIFIER] alias"]), and a [term "forwarder method"] (see [method "[vset MODIFIER] forward"]). doc/example-scripts/000077500000000000000000000000001242365656200147565ustar00rootroot00000000000000doc/example-scripts/bagel.html000066400000000000000000000517271242365656200167320ustar00rootroot00000000000000 Listing of doc/example-scripts/bagel.tcl

This example is a straight translation of the OTcl Tutorial http://www.isi.edu/nsnam/otcl/doc/tutorial.html to NX. It serves as a very short intro to the basic elements of scripting with NX and provides a comparison study to OTcl.

Suppose we need to work with many bagels in our application. We might start by creating a Bagel class.

We can now create bagels and keep track of them using the info method.

Of course, bagels don’t do much yet. They should remember whether they’ve been toasted. We can create and access an instance variable by defining an property for the class. All instance variables are per default public in the sense of C++.

Since abagel was created before the definition of the property we have to set the default value for it using the setter method. Again, the info method helps us keep track of things.

But we really want them to begin in an untoasted state to start with.

Our bagels now remember whether they’ve been toasted. Let is recreate the first one.

Now we’re ready to add a method to bagels so that we can toast them. Methods have an argument list and body like regular Tcl procs. Here’s the toast method.

The defined methods can be queried with info. We see as well the setter method for the variable toasted.

Aside from setting the toasted variable, the body of the toast method demonstrates how to access instance variables by using a leading colon in the name.

We invoke the toast method on bagels in the same way we use the info and destroy methods that were provided by the system. That is, there is no distinction between user and system methods.

Now we can add spreads to the bagels and start tasting them. If we have bagels that aren’t topped, as well as bagels that are, we may want to make toppable bagels a separate class. Let explore inheritance with these two classes, starting by making a new class SpreadableBagel that inherits from Bagel. A SpreadableBagel has an property toppings which might have multiple values. Initially, toppings are empty.

Let’s add a taste method to bagels, splitting its functionality between the two classes and combining it with next.

Of course, along come sesame, onion, poppy, and a host of other bagels, requiring us to expand our scheme. We could keep track of flavor with an instance variable, but this may not be appropriate. Flavor is an innate property of the bagels, and one that can affect other behavior - you wouldn’t put jam on an onion bagel, would you? Instead of making a class hierarchy, let’s use multiple inheritance to make the flavor classes mixins that add a their taste independent trait to bagels or whatever other food they are mixed with.

Well, they don’t appear to do much, but the use of next allows them to be freely mixed.

For multiple inheritance, the system determines a linear inheritance ordering that respects all of the local superclass orderings. You can examine this ordering with an info option. next follows this ordering when it combines behavior.

We can also combine our mixins with other classes, classes that need have nothing to do with bagels, leading to a family of chips.


doc/example-scripts/bagel.tcl000066400000000000000000000133071242365656200165400ustar00rootroot00000000000000# # This example is a straight translation of the OTcl Tutorial # http://www.isi.edu/nsnam/otcl/doc/tutorial.html to NX. It serves as # a very short intro to the basic elements of scripting with NX and # provides a comparison study to OTcl. # package req nx package req nx::test nx::test configure -count 1 # Suppose we need to work with many bagels in our application. We # might start by creating a Bagel class. ? {nx::Class create Bagel} ::Bagel # We can now create bagels and keep track of them using the info # method. ? {Bagel create abagel} ::abagel ? {abagel info class} ::Bagel ? {Bagel info instances} ::abagel # Of course, bagels don't do much yet. They should remember whether # they've been toasted. We can create and access an instance variable # by defining an property for the class. All instance variables are # per default public in the sense of C++. ? {Bagel property {toasted 0}} "" # Since abagel was created before the definition of the property we # have to set the default value for it using the setter method. Again, # the info method helps us keep track of things. ? {abagel info vars} "" ? {abagel configure -toasted 0} "" ? {abagel info vars} toasted ? {abagel cget -toasted} 0 # But we really want them to begin in an untoasted state to start # with. ? {Bagel create bagel2} ::bagel2 ? {bagel2 info vars} toasted ? {bagel2 cget -toasted} 0 # # Our bagels now remember whether they've been toasted. Let is # recreate the first one. ? {lsort [Bagel info instances]} {::abagel ::bagel2} ? {::abagel destroy} "" ? {Bagel info instances} ::bagel2 ? {Bagel create abagel} ::abagel # Now we're ready to add a method to bagels so that we can toast # them. Methods have an argument list and body like regular # Tcl procs. Here's the toast method. ? {Bagel public method toast {} { if {[incr :toasted] > 1} then { error "something's burning!" } }} "::nsf::classes::Bagel::toast" # The defined methods can be queried with info. We see as well the # setter method for the variable toasted. ? {Bagel info methods} {toast} # Aside from setting the toasted variable, the body of the toast # method demonstrates how to access instance variables by using a # leading colon in the name. # We invoke the toast method on bagels in the same way we use the # info and destroy methods that were provided by the system. That # is, there is no distinction between user and system methods. ? {abagel toast} "" ? {abagel toast} "something's burning!" # Now we can add spreads to the bagels and start tasting them. If we # have bagels that aren't topped, as well as bagels that are, we may # want to make toppable bagels a separate class. Let explore # inheritance with these two classes, starting by making a new class # SpreadableBagel that inherits from Bagel. A SpreadableBagel has an # property toppings which might have multiple values. Initially, # toppings are empty. ? {nx::Class create SpreadableBagel -superclass Bagel { :property -incremental {toppings:0..n ""} }} ::SpreadableBagel ? {SpreadableBagel cget -superclass} ::Bagel ? {SpreadableBagel info superclasses} ::Bagel ? {SpreadableBagel info heritage} {::Bagel ::nx::Object} # Let's add a taste method to bagels, splitting its functionality # between the two classes and combining it with next. ? {Bagel public method taste {} { if {${:toasted} == 0} then { return raw! } elseif {${:toasted} == 1} then { return toasty } else { return burnt! } }} "::nsf::classes::Bagel::taste" ? {SpreadableBagel public method taste {} { set t [next] foreach i ${:toppings} { lappend t $i } return $t }} "::nsf::classes::SpreadableBagel::taste" ? {SpreadableBagel create abagel} ::abagel ? {abagel toast} "" ? {abagel toppings add jam} jam ? {abagel toppings add m&m} "m&m jam" ? {abagel taste} "toasty m&m jam" # Of course, along come sesame, onion, poppy, and a host of other # bagels, requiring us to expand our scheme. We could keep track of # flavor with an instance variable, but this may not be # appropriate. Flavor is an innate property of the bagels, and one # that can affect other behavior - you wouldn't put jam on an onion # bagel, would you? Instead of making a class hierarchy, let's use # multiple inheritance to make the flavor classes mixins that add a # their taste independent trait to bagels or whatever other food they # are mixed with. ? {nx::Class create Sesame { :public method taste {} {concat [next] "sesame"} }} ::Sesame ? {nx::Class create Onion { :public method taste {} {concat [next] "onion"} }} "::Onion" ? {nx::Class create Poppy { :public method taste {} {concat [next] "poppy"} }} "::Poppy" # Well, they don't appear to do much, but the use of next allows them # to be freely mixed. ? {nx::Class create SesameOnionBagel -superclass SpreadableBagel -mixin {Sesame Onion}} ::SesameOnionBagel ? {SesameOnionBagel create abagel -toppings butter} "::abagel" ? {abagel taste} "raw! butter onion sesame" # For multiple inheritance, the system determines a linear inheritance # ordering that respects all of the local superclass orderings. You # can examine this ordering with an info option. next follows this # ordering when it combines behavior. ? {SesameOnionBagel info heritage} {::Sesame ::Onion ::SpreadableBagel ::Bagel ::nx::Object} ? {abagel info precedence} {::Sesame ::Onion ::SesameOnionBagel ::SpreadableBagel ::Bagel ::nx::Object} # We can also combine our mixins with other classes, classes that need # have nothing to do with bagels, leading to a family of chips. ? {nx::Class create Chips { :public method taste {} {return "crunchy"} }} "::Chips" ? {nx::Class create OnionChips -superclass Chips -mixin Onion} ::OnionChips ? {OnionChips create abag} ::abag ? {abag taste} "crunchy onion" doc/example-scripts/container.html000066400000000000000000001266301242365656200176360ustar00rootroot00000000000000 Listing of doc/example-scripts/container.tcl

This example is a small design study to implement container classes with different features, namely a SimpleContainer, an OrderedContainer and a SortedContainer. First of all, we require NX:

package req nx
nx::test configure -count 1

Simple Container

The first container class presented here is called SimpleContainer, which manages its contained items. As all container classes presented here, the items are created as child objects embedded in the container. If the container is deleted, all items are deleted as well. The items, which will be put into the container, should be instances of a certain class. We define here for this purpose an arbitrary class C:

nx::Class create C

The class SimpleContainer keeps and manages items added to it. Every instance of this class might have different item classes. We might provide a prefix for naming the items, otherwise the default is member.

nx::Class create SimpleContainer {
  :property {memberClass ::MyItem}
  :property {prefix member}

  # Require the method "autoname" for generating nice names
  :require method autoname

  # The method new is responsible for creating a child of the current
  # container.
  :public method new {args} {
    set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
    return $item
  }
}

Create and instance of the class SimpleContainer

% SimpleContainer create container1 -memberClass ::C
::container1

and add a few items:

% container1 new
::container1::member1

% container1 new
::container1::member2

% container1 new
::container1::member3

The elements of the container can be obtained via info children:

% container1 info children
::container1::member1 ::container1::member2 ::container1::member3

Ordered Container

In the example with SimpleContainer, the order of the results of info children just happens to be in the order of the added items, but in general, this order is not guaranteed, but depends on the population of the hash tables. In the next step, we extend the example above by preserving the order of the elements.

The class OrderedContainer is similar to SimpleContainer, but keeps a list of items that were added to the container. The item list is managed in a property items which is defined as incremental to make use of the add and delete methods provided by the slots.

nx::Class create OrderedContainer -superclass SimpleContainer {
  :property -incremental {items:0..n {}}

  :public method new {args} {
    set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
    :items add $item end
    return $item
  }

  # Since we keep the list of items, we have to maintain it in case
  # items are deleted.
  :public method delete {item:object} {
    :items delete $item
    $item destroy
  }

}

Create an instance of OrderedContainer

% OrderedContainer create container2 -memberClass ::C
::container2

and add a few items:

% container2 new
::container2::member1

% container2 new
::container2::member2

% container2 new
::container2::member3

The elements of the container are obtained via the method items.

% container2 items
::container2::member1 ::container2::member2 ::container2::member3

When we delete an item in the container …

% container2 delete ::container2::member2

the item is as well removed from the items list.

% container2 items
::container2::member1 ::container2::member3

Sorted Container

In the next step, we define a SortedContainer, that keeps additionally a sorted list for iterating through the items without needing to sort the items when needed. The implementation maintains an additional sorted list. The implementation of the SortedContainer depends on "lsearch -bisect" which requires Tcl 8.6. Therefore, if we have no Tcl 8.6, just return here.

if {[info command yield] eq ""} return

For sorting, we require the item class to have a key, that can be freely specified. We use there the property name of Class D:

nx::Class create D {
  :property name:required
}

nx::Class create SortedContainer -superclass OrderedContainer {

  # In order to keep the index consisting of just the objects and to
  # ease sorting, we maintain two list, one list of values and one
  # list of objects. We assume for the time being, that the keys are
  # not changing.

  :variable values {}
  :variable index {}
  :property key

  :public method index {} { return ${:index}}

  :public method new {args} {
    set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
    if {[info exists :key]} {
      set value [$item cget -${:key}]
      set pos [lsearch -bisect ${:values} $value]
      set :values [linsert ${:values} [expr {$pos + 1}] $value]
      set :index  [linsert ${:index}  [expr {$pos + 1}] $item]
    }
    lappend :items $item
    return $item
  }

  # Since we keep the list of items, we have to maintain it in case
  # items are deleted.
  :public method delete {item:object} {
    set pos [lsearch ${:index} $item]
    if {$pos == -1} {error "item $item not found in container; items: ${:index}"}
    set :values [lreplace ${:values} $pos $pos]
    set :index  [lreplace ${:index}  $pos $pos]
    next
  }
}

Create a container for class D with key name:

SortedContainer create container3 -memberClass ::D -key name

Add a few items

% container3 new -name victor
::container3::member1

% container3 new -name stefan
::container3::member2

% container3 new -name gustaf
::container3::member3

The method items returns the items in the order of insertion (as before):

% container3 items
::container3::member1 ::container3::member2 ::container3::member3

The method index returns the items in sorting order (sorted by the name member):

% container3 index
::container3::member3 ::container3::member2 ::container3::member1

Now we delete an item:

% container3 delete ::container3::member2

The item is as well removed from the result lists

% container3 items
::container3::member1 ::container3::member3

% container3 index
::container3::member3 ::container3::member1

doc/example-scripts/container.tcl000066400000000000000000000141721242365656200174510ustar00rootroot00000000000000# # This example is a small design study to implement container classes # with different features, namely a +SimpleContainer+, an # +OrderedContainer+ and a +SortedContainer+. First of all, we require NX: # package req nx package req nx::test nx::test configure -count 1 # == Simple Container # # The first container class presented here is called # +SimpleContainer+, which manages its contained items. As all # container classes presented here, the items are created as child # objects embedded in the container. If the container is deleted, all # items are deleted as well. The items, which will be put into the # container, should be instances of a certain class. We define here # for this purpose an arbitrary class +C+: nx::Class create C # The class +SimpleContainer+ keeps and manages items added to it. # Every instance of this class might have different item classes. We # might provide a prefix for naming the items, otherwise the default # is +member+. # nx::Class create SimpleContainer { :property {memberClass ::MyItem} :property {prefix member} # Require the method "autoname" for generating nice names :require method autoname # The method new is responsible for creating a child of the current # container. :public method new {args} { set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args] return $item } } # Create and instance of the class +SimpleContainer+ ... ? {SimpleContainer create container1 -memberClass ::C} ::container1 # and add a few items: ? {container1 new} "::container1::member1" ? {container1 new} "::container1::member2" ? {container1 new} "::container1::member3" # The elements of the container can be obtained via +info children+: ? {container1 info children} "::container1::member1 ::container1::member2 ::container1::member3" # == Ordered Container # # In the example with +SimpleContainer+, the order of the results of # +info children+ just happens to be in the order of the added items, # but in general, this order is not guaranteed, but depends on the # population of the hash tables. In the next step, we extend the # example above by preserving the order of the elements. # The class +OrderedContainer+ is similar to +SimpleContainer+, but # keeps a list of items that were added to the container. The item # list is managed in a property +items+ which is defined as # +incremental+ to make use of the +add+ and +delete+ methods provided # by the slots. # nx::Class create OrderedContainer -superclass SimpleContainer { :property -incremental {items:0..n {}} :public method new {args} { set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args] :items add $item end return $item } # Since we keep the list of items, we have to maintain it in case # items are deleted. :public method delete {item:object} { :items delete $item $item destroy } } # Create an instance of +OrderedContainer+ ... ? {OrderedContainer create container2 -memberClass ::C} "::container2" # and add a few items: ? {container2 new} "::container2::member1" ? {container2 new} "::container2::member2" ? {container2 new} "::container2::member3" # The elements of the container are obtained via the method +items+. ? {container2 items get} "::container2::member1 ::container2::member2 ::container2::member3" # When we delete an item in the container ... ? {container2 delete ::container2::member2} "" # the item is as well removed from the +items+ list. ? {container2 items get} "::container2::member1 ::container2::member3" # == Sorted Container # # In the next step, we define a +SortedContainer+, that keeps # additionally a sorted list for iterating through the items without # needing to sort the items when needed. The implementation maintains # an additional sorted list. The implementation of the SortedContainer # depends on "lsearch -bisect" which requires Tcl 8.6. Therefore, if # we have no Tcl 8.6, just return here. if {[info command yield] eq ""} return # For sorting, we require the item class to have a key, that can be # freely specified. We use there the property +name+ of Class +D+: nx::Class create D { :property name:required } nx::Class create SortedContainer -superclass OrderedContainer { # In order to keep the index consisting of just the objects and to # ease sorting, we maintain two list, one list of values and one # list of objects. We assume for the time being, that the keys are # not changing. :variable values {} :variable index {} :property key :public method index {} { return ${:index}} :public method new {args} { set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args] if {[info exists :key]} { set value [$item cget -${:key}] set pos [lsearch -bisect ${:values} $value] set :values [linsert ${:values} [expr {$pos + 1}] $value] set :index [linsert ${:index} [expr {$pos + 1}] $item] } lappend :items $item return $item } # Since we keep the list of items, we have to maintain it in case # items are deleted. :public method delete {item:object} { set pos [lsearch ${:index} $item] if {$pos == -1} {error "item $item not found in container; items: ${:index}"} set :values [lreplace ${:values} $pos $pos] set :index [lreplace ${:index} $pos $pos] next } } # Create a container for class +D+ with key +name+: SortedContainer create container3 -memberClass ::D -key name # Add a few items ? {container3 new -name victor} "::container3::member1" ? {container3 new -name stefan} "::container3::member2" ? {container3 new -name gustaf} "::container3::member3" # The method +items+ returns the items in the order of insertion (as before): ? {container3 items get} "::container3::member1 ::container3::member2 ::container3::member3" # The method +index+ returns the items in sorting order (sorted by the +name+ member): ? {container3 index} "::container3::member3 ::container3::member2 ::container3::member1" # Now we delete an item: ? {container3 delete ::container3::member2} "" # The item is as well removed from the result lists ? {container3 items get} "::container3::member1 ::container3::member3" ? {container3 index} "::container3::member3 ::container3::member1" doc/example-scripts/per-object-mixins.html000066400000000000000000000713221242365656200212100ustar00rootroot00000000000000 Listing of doc/example-scripts/per-object-mixins.tcl

NX supports "open class definitions", object specific behavior and mixin classes (among other things) to achieve dynamic behavior extensions. The so-called per-object mixins are actually an implementation of the decorator pattern.

package req nx

Here is the original example: a method from a derived class extends the behavior of the baseclass; the primitive "next" calls other same-named methods. In the example below, the baseclass method "speak" is called at the beginning of the derived-class method. The primitive "next" can be placed at arbitrary places, or it can be omitted when the baseclass method should not be called.

nx::Class create BaseClass {
  :public method speak {} {
    puts "Hello from BC."
  }
}

nx::Class create DerivedClass -superclass BaseClass {
  :public method speak {} {
    next
    puts "Hello from DC."
  }
}

DerivedClass create o1
o1 speak

The output is:

   Hello from BC.
   Hello from DC.

There are many ways to extend the behavior NX classes at runtime. The easiest thing is to add methods dynamically to classes. E.g. we can extend the BaseClass with the method unknown, which is called whenever an unknown method is called.

BaseClass method unknown {m args} {
  puts "What? $m? I don't understand."
}
o1 sing

The output is:

    What? sing? I don't understand.

Often, you do not want to extend the class, but to modify the behavior of a single object. In NX, an object can have individual methods:

o1 public method sing {} {
  puts "Ok, here it goes: Lala Lala!"
}
o1 sing

The output is:

    Ok, here it goes: Lala Lala!

In many situations, it is desired to add/remove a set of methods dynamically to objects or classes. The mechanisms above allow this, but they are rather cumbersome and do support a systematic behavior engineering. One can add so-called "mixin classes" to objects and/or classes. For example, we can define a class M for a more verbose methods:

nx::Class create M {
  :public method sing {} {
    puts -nonewline "[self] sings: "
    next
  }
  :method unknown args {
    puts -nonewline "[self] is confused: "
    next
  }
}

The behavior of M can be mixed into the behavior of o1 through per object mixins …

o1 mixin M
  1. and we call the methods again:

o1 sing
o1 read

The output is:

   ::o1 sings: Ok, here it goes: Lala Lala!
   ::o1 is confused: What? read? I don't understand.

We can remove the new behavior easily by unregistering the mixin class ….

o1 mixin ""
  1. and we call the methods again:

o1 sing
o1 read

The output is:

   Ok, here it goes: Lala Lala!
   What? read? I don't understand.

Mixin classes can be used to extend the behavior of classes as well.

BaseClass mixin M

o1 sing
o1 read

DerivedClass create o2
o2 read

The output is:

   ::o1 sings: Ok, here it goes: Lala Lala!
   ::o1 is confused: What? read? I don't understand.
   ::o2 is confused: What? read? I don't understand.

doc/example-scripts/per-object-mixins.tcl000066400000000000000000000060311242365656200210210ustar00rootroot00000000000000# NX supports "open class definitions", object specific behavior # and mixin classes (among other things) to achieve dynamic behavior # extensions. The so-called per-object mixins are actually an # implementation of the decorator pattern. package req nx package req nx::test # Here is the original example: a method from a derived class # extends the behavior of the baseclass; the primitive "next" # calls other same-named methods. In the example below, the # baseclass method "speak" is called at the beginning of the # derived-class method. The primitive "next" can be placed at # arbitrary places, or it can be omitted when the baseclass method # should not be called. nx::Class create BaseClass { :public method speak {} { puts "Hello from BC." } } nx::Class create DerivedClass -superclass BaseClass { :public method speak {} { next puts "Hello from DC." } } DerivedClass create o1 o1 speak # The output is: # ---- # Hello from BC. # Hello from DC. # ---- # There are many ways to extend the behavior NX classes at # runtime. The easiest thing is to add methods dynamically to # classes. E.g. we can extend the BaseClass with the method unknown, # which is called whenever an unknown method is called. BaseClass method unknown {m args} { puts "What? $m? I don't understand." } o1 sing # The output is: # ---- # What? sing? I don't understand. # ---- # # Often, you do not want to extend the class, but to # modify the behavior of a single object. In NX, an object # can have individual methods: o1 public method sing {} { puts "Ok, here it goes: Lala Lala!" } o1 sing # The output is: # ---- # Ok, here it goes: Lala Lala! # ---- # # In many situations, it is desired to add/remove a set # of methods dynamically to objects or classes. The mechanisms # above allow this, but they are rather cumbersome and do # support a systematic behavior engineering. One can add # so-called "mixin classes" to objects and/or classes. For # example, we can define a class M for a more verbose methods: nx::Class create M { :public method sing {} { puts -nonewline "[self] sings: " next } :method unknown args { puts -nonewline "[self] is confused: " next } } # The behavior of M can be mixed into the behavior of o1 through # per object mixins ... o1 mixin M # ... and we call the methods again: o1 sing o1 read # The output is: # ---- # ::o1 sings: Ok, here it goes: Lala Lala! # ::o1 is confused: What? read? I don't understand. # ---- # # We can remove the new behavior easily by unregistering the # mixin class .... o1 mixin "" # ... and we call the methods again: o1 sing o1 read # The output is: # ---- # Ok, here it goes: Lala Lala! # What? read? I don't understand. # ---- # # Mixin classes can be used to extend the behavior of classes # as well. BaseClass mixin M o1 sing o1 read DerivedClass create o2 o2 read # The output is: # ---- # ::o1 sings: Ok, here it goes: Lala Lala! # ::o1 is confused: What? read? I don't understand. # ::o2 is confused: What? read? I don't understand. # ---- doc/example-scripts/rosetta-abstract-type.html000066400000000000000000000426751242365656200221230ustar00rootroot00000000000000 Listing of doc/example-scripts/rosetta-abstract-type.tcl

Rosetta Example: Abstract type

Define a class without instances and without implemented methods. For detailed description of this example see http://rosettacode.org/wiki/Abstract_type

Define a class AbstractQueue

Define a concrete queue (named ListQueue) based on the Abstract Queue

Demonstrating the behavior in a shell:

Trying to create an instance of the AbstraceQueue returns an error message:

Create an instance of the concrete queue:

Enqueue and dequeue items


doc/example-scripts/rosetta-abstract-type.tcl000066400000000000000000000023361242365656200217270ustar00rootroot00000000000000# # == Rosetta Example: Abstract type # # Define a class without instances and without implemented methods. # For detailed description of this example # see http://rosettacode.org/wiki/Abstract_type # package req nx package req nx::test # # Define a class AbstractQueue nx::Class create AbstractQueue { :public method enqueue {item} {error "not implemented"} :public method dequeue {} {error "not implemented"} :public object method create {args} { error "Cannot instantiate abstract class [self]" } } # # Define a concrete queue (named ListQueue) based # on the Abstract Queue nx::Class create ListQueue -superclass AbstractQueue { :variable list {} :public method enqueue {item} { lappend :list $item } :public method dequeue {} { set item [lindex ${:list} 0] set :list [lrange ${:list} 1 end] return $item } } # === Demonstrating the behavior in a shell: # # Trying to create an instance of the AbstraceQueue returns an error message: ? {AbstractQueue new} {Cannot instantiate abstract class ::AbstractQueue} # Create an instance of the concrete queue: ? {set q [ListQueue new]} "::nsf::__#1" # Enqueue and dequeue items ? {$q enqueue 100} 100 ? {$q enqueue 101} "100 101" ? {$q dequeue} 100doc/example-scripts/rosetta-classes.html000066400000000000000000000475321242365656200207730ustar00rootroot00000000000000 Listing of doc/example-scripts/rosetta-classes.tcl

Rosetta Example: Classes

package req nx

nx::Class create summation {
  :method init {} {set :v 0}
  :public method add {x} {incr :v $x}
  :public method value {} {return ${:v}}
  :public method destroy {} {puts "ended with value [:value]"; next}
}

Demonstrating the behavior in a shell:

% set sum [summation new]
% $sum value
0
% $sum add 1
1
% $sum add 2
3
% $sum add 3
6
% $sum add 4
10
% $sum value
10

During the destroy of the object, ended with value 10 is printed

% $sum destroy

doc/example-scripts/rosetta-classes.tcl000077500000000000000000000011501242365656200205760ustar00rootroot00000000000000 # == Rosetta Example: Classes # For details see http://rosettacode.org/wiki/Classes # package req nx package req nx::test nx::Class create summation { :method init {} {set :v 0} :public method add {x} {incr :v $x} :public method value {} {return ${:v}} :public method destroy {} {puts "ended with value [:value]"; next} } # === Demonstrating the behavior in a shell: ? {set sum [summation new]} "::nsf::__#0" ? {$sum value} 0 ? {$sum add 1} 1 ? {$sum add 2} 3 ? {$sum add 3} 6 ? {$sum add 4} 10 ? {$sum value} 10 # During the destroy of the object, +ended with value 10+ is printed ? {$sum destroy} "" doc/example-scripts/rosetta-constraint-genericity.html000066400000000000000000000434631242365656200236610ustar00rootroot00000000000000 Listing of doc/example-scripts/rosetta-constraint-genericity.tcl

Rosetta Example: Constrained genericity

Define the two classes Eatable and Fish. Eatable is a class for all eatable things, a Fish is a subclass ant therefore eatable.

A FoodBax may only contain eatable items. Therefore with we define items as a property of type Eatable" which has a multiplicity of +0..n (might contain 0 to n eatable items). Furthermore, we define items as incremental, such we can add / remove items with item add or item remove.

Demonstrating the behavior in a shell:

Create two fishes, Wanda and Nemo:

Create a Foodbox and add the two fishes:

Return the print string of the contents:


doc/example-scripts/rosetta-constraint-genericity.tcl000077500000000000000000000025371242365656200234770ustar00rootroot00000000000000# # == Rosetta Example: Constrained genericity # For details see http://rosettacode.org/wiki/Constrained_genericity # package req nx package req nx::test # # Define the two classes +Eatable+ and +Fish+. +Eatable+ is a class # for all eatable things, a +Fish+ is a subclass ant therefore # eatable. # nx::Class create Eatable nx::Class create Fish -superclass Eatable { :property name } # # A +FoodBax+ may only contain eatable items. Therefore with we define # +items+ as a property of type +Eatable" which has a multiplicity of # +0..n+ (might contain 0 to n eatable items). Furthermore, we define # items as +incremental+, such we can add / remove items with +item # add+ or +item remove+. # nx::Class create FoodBox { :property -incremental item:object,type=::Eatable :public method print {} { set string "Foodbox contains:\n" foreach i ${:item} {append string " [$i cget -name]\n"} return $string } } # === Demonstrating the behavior in a shell: # Create two fishes, Wanda and Nemo: ? {set f1 [Fish new -name "Wanda"]} "::nsf::__#0" ? {set f2 [Fish new -name "Nemo"]} "::nsf::__#1" # Create a Foodbox and add the two fishes: ? {set fb [FoodBox new]} "::nsf::__#2" ? {$fb item add $f1} "::nsf::__#0" ? {$fb item add $f2} "::nsf::__#1 ::nsf::__#0" # Return the print string of the contents: ? {$fb print} {Foodbox contains: Nemo Wanda } doc/example-scripts/rosetta-delegates.html000066400000000000000000000420351242365656200212640ustar00rootroot00000000000000 Listing of doc/example-scripts/rosetta-delegates.tcl

Rosetta Example: Delegates

Demonstrating the behavior in a shell:

Create a Delegator, which has no delegatee defined. Therefore delegator performs the action by itself, the default implementation.

Now, we set the delegatee; therefore, the delegatee will perform the action.


doc/example-scripts/rosetta-delegates.tcl000066400000000000000000000022641242365656200211020ustar00rootroot00000000000000# # == Rosetta Example: Delegates # For details see http://rosettacode.org/wiki/Delegates # package req nx package req nx::test nx::Class create Delegator { # The class Delegator has a property named "delegatee" which is an # object: :property delegatee:object # The method "operation" decides, whether it deletates the action to # another object, or it performs the action itself. :public method operation {} { if {[info exists :delegatee]} { ${:delegatee} operation } else { return "default implementatiton" } } } nx::Class create Delegatee { # The class "Delgatee" might receice invocations from the class # "Delegator" :public method operation {} { return "delegatee implementatiton" } } # === Demonstrating the behavior in a shell: # # Create a +Delegator+, which has no +delegatee+ defined. Therefore # delegator performs the action by itself, the default implementation. # ? {set a [Delegator new]} "::nsf::__#0" ? {$a operation} "default implementatiton" # # Now, we set the +delegatee+; therefore, the delegatee will perform # the action. # ? {$a configure -delegatee [Delegatee new]} "" ? {$a operation} "delegatee implementatiton"doc/example-scripts/rosetta-distinct-objects.html000066400000000000000000000460431242365656200226020ustar00rootroot00000000000000 Listing of doc/example-scripts/rosetta-distinct-objects.tcl

Rosetta Example: Multiple distinct objects

package req nx

Set the number of objects that should be created

% set n 100
100

Create a sequence as a list with n instances of the class nx::Object

% set sequence {}
% for {set i 0} {$i < $n} {incr i} {
    lappend sequence [nx::Object new]
}

doc/example-scripts/rosetta-distinct-objects.tcl000077500000000000000000000006341242365656200224170ustar00rootroot00000000000000# == Rosetta Example: Multiple distinct objects # For details see http://rosettacode.org/wiki/Multiple_distinct_objects # package req nx package req nx::test # Set the number of objects that should be created ? {set n 100} 100 # Create a sequence as a list with +n+ instances of the class +nx::Object+ ? {set sequence {}} "" ? {for {set i 0} {$i < $n} {incr i} { lappend sequence [nx::Object new] } } "" doc/example-scripts/rosetta-polymorphic-copy.html000066400000000000000000000454121242365656200226460ustar00rootroot00000000000000 Listing of doc/example-scripts/rosetta-polymorphic-copy.tcl

Rosetta Example: Polymorphic copy

package req nx

nx::Class create T {
  :public method name {} {return T}
}
nx::Class create S -superclass T {
  :public method name {} {return S}
}

Demonstrating the behavior in a shell:

o1 and o2 are instances of T and S. $o1 name returns the same value as its copy, same for $o2

% set o1 [T new]
% set o2 [S new]

% $o1 name
T
% $o2 name
S

% [$o1 copy] name
T
% [$o2 copy] name
S

doc/example-scripts/rosetta-polymorphic-copy.tcl000077500000000000000000000011061242365656200224570ustar00rootroot00000000000000# # == Rosetta Example: Polymorphic copy # For details see http://rosettacode.org/wiki/Polymorphic_copy # package req nx package req nx::test nx::Class create T { :public method name {} {return T} } nx::Class create S -superclass T { :public method name {} {return S} } # === Demonstrating the behavior in a shell: # # +o1+ and +o2+ are instances of +T+ and +S+. +$o1 name+ returns the same value as its copy, same for +$o2+ ? {set o1 [T new]} "::nsf::__#0" ? {set o2 [S new]} "::nsf::__#1" ? {$o1 name} "T" ? {$o2 name} "S" ? {[$o1 copy] name} "T" ? {[$o2 copy] name} "S"doc/example-scripts/rosetta-polymorphism.html000066400000000000000000000572071242365656200221000ustar00rootroot00000000000000 Listing of doc/example-scripts/rosetta-polymorphism.tcl

Rosetta Example: Polymorphism

package req nx

nx::Class create Point {

  :property x:double
  :property y:double

  :public method print {} {
    return "Point(${:x},${:y})"
  }
}

nx::Class create Circle -superclass Point {

  :property radius:double

  :public method print {} {
    return "Circle(${:x},${:y},${:radius})"
  }
}

Demonstrating the behavior in a shell:

Create a point and get the print string:

% set p [Point new -x 1.0 -y 2.0]
% $p print
Point(1.0,2.0)

Get the x coordinate of this point:

% $p cget -x
1.0

Create a circle:

% set c [Circle new -x 3.0 -y 4.0 -radius 5.0]

Copy the circle

% set d [$c copy]

Change the radius of the copied circle:

% $d configure -radius 1.5

Print the two circles:

% $c print
Circle(3.0,4.0,5.0)

% $d print
Circle(3.0,4.0,1.5)

doc/example-scripts/rosetta-polymorphism.tcl000077500000000000000000000016761242365656200217200ustar00rootroot00000000000000 # == Rosetta Example: Polymorphism # For details see http://rosettacode.org/wiki/Polymorphism # package req nx package req nx::test nx::Class create Point { :property x:double :property y:double :public method print {} { return "Point(${:x},${:y})" } } nx::Class create Circle -superclass Point { :property radius:double :public method print {} { return "Circle(${:x},${:y},${:radius})" } } # === Demonstrating the behavior in a shell: # Create a point and get the print string: ? {set p [Point new -x 1.0 -y 2.0]} "::nsf::__#0" ? {$p print} "Point(1.0,2.0)" # Get the x coordinate of this point: ? {$p cget -x} "1.0" # Create a circle: ? {set c [Circle new -x 3.0 -y 4.0 -radius 5.0]} "::nsf::__#1" # Copy the circle ? {set d [$c copy]} "::nsf::__#3" # Change the radius of the copied circle: ? {$d configure -radius 1.5} "" # Print the two circles: ? {$c print} "Circle(3.0,4.0,5.0)" ? {$d print} "Circle(3.0,4.0,1.5)" doc/example-scripts/rosetta-serialization.html000066400000000000000000000617631242365656200222150ustar00rootroot00000000000000 Listing of doc/example-scripts/rosetta-serialization.tcl

Rosetta Example: Object serialization

package req nx
package req nx::serializer

nx::Class create Being {
  :property {alive:boolean true}
}

nx::Class create Animal -superclass Being {
  :property name
  :public method print {} {
    puts "i am ${:name} alive ${:alive}"
  }
}

Demonstrating the behavior in a shell:

Create a few animals

% Animal new -name "Fido"
% Animal new -name "Lupo"
% Animal new -name "Kiki" -alive false

Print the created animals

% foreach i [Animal info instances] { $i print }

The loop prints:
i am Kiki alive false
i am Lupo alive true
i am Fido alive true

Serialize the animals to a file

% set f [open /tmp/dump w]
% foreach i [Animal info instances] { puts $f [$i serialize] }
% close $f

Destroy all animal instances:

% foreach i [Animal info instances] { $i destroy }
% puts ===========

Print the existing animals (will print nothing)

% foreach i [Animal info instances] { $i print }
% puts ===========

Load the animals again …

% source /tmp/dump

and print it. The print output is the same as above

% foreach i [Animal info instances] { $i print }

doc/example-scripts/rosetta-serialization.tcl000077500000000000000000000025601242365656200220240ustar00rootroot00000000000000 # == Rosetta Example: Object serialization # For details see http://rosettacode.org/wiki/Object_serialization # package req nx package req nx::test proc ! args {uplevel {*}$args} package req nx::serializer nx::Class create Being { :property {alive:boolean true} } nx::Class create Animal -superclass Being { :property name :public method print {} { puts "i am ${:name} alive ${:alive}" } } # === Demonstrating the behavior in a shell: # Create a few animals ? {Animal new -name "Fido"} "::nsf::__#0" ? {Animal new -name "Lupo"} "::nsf::__#1" ? {Animal new -name "Kiki" -alive false} "::nsf::__#2" # Print the created animals ? {foreach i [Animal info instances] { $i print }} "" # The loop prints: + # +i am Kiki alive false+ + # +i am Lupo alive true+ + # +i am Fido alive true+ # # Serialize the animals to a file ! {set fpath [::nsf::tmpdir]/dump} ! {set f [open $fpath w]} ? {foreach i [Animal info instances] { puts $f [$i serialize] }} "" ? {close $f} "" # Destroy all animal instances: ? {foreach i [Animal info instances] { $i destroy }} "" ? {puts ===========} "" # Print the existing animals (will print nothing) ? {foreach i [Animal info instances] { $i print }} "" ? {puts ===========} "" # Load the animals again ... ? {source $fpath} "" # and print it. The print output is the same as above ? {foreach i [Animal info instances] { $i print }} "" doc/example-scripts/rosetta-singleton.html000066400000000000000000000570731242365656200213410ustar00rootroot00000000000000 Listing of doc/example-scripts/rosetta-singleton.tcl

Rosetta Example: Singleton

A Singleton Class

package req nx

nx::Class create Singleton {
  #
  # We overload the system method "create". In the modified method we
  # save the created instance in the instance variable named
  # "instance"
  #
  :variable instance:object

  :public object method create {args} {
    return [expr {[info exists :instance] ? ${:instance} : [set :instance [next]]}]
  }
}

Demonstrating the behavior in a shell:

Calling Singleton new multiple times returns always the same object:

% expr {[Singleton new] eq [Singleton new]}
1

A Singleton Meta-class

Alternatively, we can follow a more generic approach and define a metaclass which allows to define several application classes as singletons. The metaclass has the most general metaclass nx::Class as superclass. In contrary to the example obove, the create method is not defined as a class method, but it will be inherited to its instances (to the application classes).

nx::Class create Singleton -superclass nx::Class {
  #
  # We overload the system method "create". In the modified method we
  # save the created instance in the instance variable named
  # "instance"
  #
  :variable instance:object

  :public method create {args} {
    return [expr {[info exists :instance] ? ${:instance} : [set :instance [next]]}]
  }
}

Create an application class named Counter as a singleton:

% Singleton create Counter
::Counter

Calling Counter new multiple times returns always the same object:

% expr {[Counter new] eq [Counter new]}
1

doc/example-scripts/rosetta-singleton.tcl000077500000000000000000000032021242365656200211430ustar00rootroot00000000000000# # == Rosetta Example: Singleton # For details see http://rosettacode.org/wiki/Singleton # # === A Singleton Class package req nx package req nx::test nx::Class create Singleton { # # We overload the system method "create". In the modified method we # save the created instance in the instance variable named # "instance" # :variable instance:object :public object method create {args} { return [expr {[info exists :instance] ? ${:instance} : [set :instance [next]]}] } } # === Demonstrating the behavior in a shell: # # Calling +Singleton new+ multiple times returns always the same object: ? {expr {[Singleton new] eq [Singleton new]}} 1 # # === A Singleton Meta-class # # Alternatively, we can follow a more generic approach and define a # metaclass which allows to define several application classes as # singletons. The metaclass has the most general metaclass +nx::Class+ # as superclass. In contrary to the example obove, the +create+ method # is not defined as a class method, but it will be inherited to its # instances (to the application classes). # nx::Class create Singleton -superclass nx::Class { # # We overload the system method "create". In the modified method we # save the created instance in the instance variable named # "instance" # :variable instance:object :public method create {args} { return [expr {[info exists :instance] ? ${:instance} : [set :instance [next]]}] } } # Create an application class named +Counter+ as a singleton: ? {Singleton create Counter} ::Counter # Calling +Counter new+ multiple times returns always the same object: ? {expr {[Counter new] eq [Counter new]}} 1doc/example-scripts/rosetta-sudoku.html000066400000000000000000001224721242365656200206450ustar00rootroot00000000000000 Listing of doc/example-scripts/rosetta-sudoku.tcl

Rosetta Example: Sudoku

Solve a partially filled-in 9x9 Sudoku grid and display the result in a human-readable format. For detailed description of this example, see http://rosettacode.org/wiki/Sudoku_Solver

This implementation is based on http://wiki.tcl.tk/19934

package require nx

The class Sudoku implements the basic interface to a sudoku 9x9 board to load/dump data and to set/access cells, rows, columns and regions.

nx::Class create Sudoku {

    :variable board

    # Setup an array from 0..9 to ease iterations over the cells of
    # lines and columns.
    for {set i 0} {$i < 9} {incr i} {lappend positions $i}
    :variable positions $positions

    :public method load {data} {
        #
        # Load a 9x9 partially solved sudoku. The unsolved cells are
        # represented by a@ symbols.
        #
        set error "data must be a 9-element list, each element also being a\
                list of 9 numbers from 1 to 9 or blank or an @ symbol."
        if {[llength $data] != 9} {
            error $error
        }
        foreach y ${:positions} {
            set row [lindex $data $y]
            if {[llength $row] != 9} {
                error $error
            }
            foreach x ${:positions} {
                set cell [lindex $row $x]
                if {![regexp {^[@1-9]?$} $cell]} {
                    error $cell-$error
                }
                if {$cell eq "@"} {set cell ""}
                :set $x $y $cell
            }
        }
    }

    :public method dump {-pretty-print:switch} {
        #
        # Output the current state of the sudoku either as list or in
        # a pretty-print style.
        #
        set rows [lmap y ${:positions} {:getRow 0 $y}]
        if {${pretty-print}} {
            set result +-----+-----+-----+\n
            foreach line $rows postline {0 0 1 0 0 1 0 0 1} {
                append result |[lrange $line 0 2]|[lrange $line 3 5]|[lrange $line 6 8]|\n
                if {$postline} {
                    append result +-----+-----+-----+\n
                }
            }
            return $result
        } else {
            return $rows
        }
    }

    :method log {msg} {
        #puts "log: $msg"
    }

    :method set {x y value:integer,0..1} {
        #
        # Set cell at position x,y to the given value or empty.
        #
        if {$value<1 || $value>9} {
            set :board($x,$y) {}
        } else {
            set :board($x,$y) $value
        }
    }
    :method get {x y} {
        #
        # Get value of cell at position x, y.
        #
        return [set :board($x,$y)]
    }

    :method getRow {x y} {
        #
        # Return a row at constant position y.
        #
        return [lmap x ${:positions} {:get $x $y}]
    }
    :method getCol {x y} {
        #
        # Return a column at constant position x.
        #
        return [lmap y ${:positions} {:get $x $y}]
    }

    :method getRegion {x y} {
        #
        # Return a 3x3 region
        #
        set xR [expr {($x/3)*3}]
        set yR [expr {($y/3)*3}]
        set regn {}
        for {set x $xR} {$x < $xR+3} {incr x} {
            for {set y $yR} {$y < $yR+3} {incr y} {
                lappend regn [:get $x $y]
            }
        }
        return $regn
    }
}

The class SudokuSolver inherits from Sudoku, and adds the ability to solve a given Sudoku game. The method solve applies all rules for each unsolved cell until it finds a safe solution.

nx::Class create SudokuSolver -superclass Sudoku {

    :public method validchoices {x y} {
        set v [:get $x $y]
        if {$v ne {}} {
            return $v
        }

        set row [:getRow $x $y]
        set col [:getCol $x $y]
        set regn [:getRegion $x $y]
        set eliminate [list {*}$row {*}$col {*}$regn]
        set eliminate [lsearch -all -inline -not $eliminate {}]
        set eliminate [lsort -unique $eliminate]

        set choices {}
        for {set c 1} {$c < 10} {incr c} {
            if {$c ni $eliminate} {
                lappend choices $c
            }
        }
        if {[llength $choices]==0} {
            error "No choices left for square $x,$y"
        }
        return $choices
    }

    :method completion {} {
        #
        # Return the number of already solved items.
        #
        return [expr {81-[llength [lsearch -all -inline [join [:dump]] {}]]}]
    }

    :public method solve {} {
        #
        # Try to solve the sudoku by applying the provided rules.
        #
        while {1} {
            set begin [:completion]
            foreach y ${:positions} {
                foreach x ${:positions} {
                    if {[:get $x $y] eq ""} {
                        foreach rule [Rule info instances] {
                            set c [$rule solve [self] $x $y]
                            if {$c} {
                                :set $x $y $c
                                :log "[$rule info class] solved [self] at $x,$y for $c"
                                break
                            }
                        }
                    }
                }
            }
            set end [:completion]
            if {$end == 81} {
                :log "Finished solving!"
                break
            } elseif {$begin == $end} {
                :log "A round finished without solving any squares, giving up."
                break
            }
        }
    }
}

The class rule provides "solve" as public interface for all rule objects. The rule objects apply their logic to the values passed in and return either 0 or a number to allocate to the requested square.

nx::Class create Rule {

    :public method solve {hSudoku:object,type=::SudokuSolver x y} {
        :Solve $hSudoku $x $y [$hSudoku validchoices $x $y]
    }

    # Get all the allocated numbers for each square in the the row, column, and
    # region containing $x,$y. If there is only one unallocated number among all
    # three groups, it must be allocated at $x,$y
    :create ruleOnlyChoice {
        :object method Solve {hSudoku x y choices} {
            if {[llength $choices] == 1} {
                return $choices
            } else {
                return 0
            }
        }
    }

    # Test each column to determine if $choice is an invalid choice for all other
    # columns in row $X. If it is, it must only go in square $x,$y.
    :create RuleColumnChoice {
        :object method Solve {hSudoku x y choices} {
            foreach choice $choices {
                set failed 0
                for {set x2 0} {$x2 < 9} {incr x2} {
                    if {$x2 != $x && $choice in [$hSudoku validchoices $x2 $y]} {
                        set failed 1
                        break
                    }
                }
                if {!$failed} {return $choice}
            }
            return 0
        }
    }

    # Test each row to determine if $choice is an invalid choice for all other
    # rows in column $y. If it is, it must only go in square $x,$y.
    :create RuleRowChoice {
        :object method Solve {hSudoku x y choices} {
            foreach choice $choices {
                set failed 0
                for {set y2 0} {$y2 < 9} {incr y2} {
                    if {$y2 != $y && $choice in [$hSudoku validchoices $x $y2]} {
                        set failed 1
                        break
                    }
                }
                if {!$failed} {return $choice}
            }
            return 0
        }
    }

    # Test each square in the region occupied by $x,$y to determine if $choice is
    # an invalid choice for all other squares in that region. If it is, it must
    # only go in square $x,$y.
    :create RuleRegionChoice {
        :object method Solve {hSudoku x y choices} {
            foreach choice $choices {
                set failed 0
                set regnX [expr {($x/3)*3}]
                set regnY [expr {($y/3)*3}]
                for {set y2 $regnY} {$y2 < $regnY+3} {incr y2} {
                    for {set x2 $regnX} {$x2 < $regnX+3} {incr x2} {
                        if {
                            ($x2!=$x || $y2!=$y)
                            && $choice in [$hSudoku validchoices $x2 $y2]
                        } then {
                            set failed 1
                            break
                        }
                    }
                }
                if {!$failed} {return $choice}
            }
            return 0
        }
    }
}

SudokuSolver create sudoku {

    :load {
        {3 9 4    @ @ 2    6 7 @}
        {@ @ @    3 @ @    4 @ @}
        {5 @ @    6 9 @    @ 2 @}

        {@ 4 5    @ @ @    9 @ @}
        {6 @ @    @ @ @    @ @ 7}
        {@ @ 7    @ @ @    5 8 @}

        {@ 1 @    @ 6 7    @ @ 8}
        {@ @ 9    @ @ 8    @ @ @}
        {@ 2 6    4 @ @    7 3 5}
    }
    :solve

    puts [:dump -pretty-print]
}

The dump method outputs the solved Sudoku:

+-----+-----+-----+
|3 9 4|8 5 2|6 7 1|
|2 6 8|3 7 1|4 5 9|
|5 7 1|6 9 4|8 2 3|
+-----+-----+-----+
|1 4 5|7 8 3|9 6 2|
|6 8 2|9 4 5|3 1 7|
|9 3 7|1 2 6|5 8 4|
+-----+-----+-----+
|4 1 3|5 6 7|2 9 8|
|7 5 9|2 3 8|1 4 6|
|8 2 6|4 1 9|7 3 5|
+-----+-----+-----+

doc/example-scripts/rosetta-sudoku.tcl000066400000000000000000000164121242365656200204570ustar00rootroot00000000000000# # == Rosetta Example: Sudoku # # Solve a partially filled-in 9x9 Sudoku grid and display the result # in a human-readable format. For detailed description of this # example, see http://rosettacode.org/wiki/Sudoku_Solver # # This implementation is based on http://wiki.tcl.tk/19934 package require nx # # The class +Sudoku+ implements the basic interface to a sudoku 9x9 # board to load/dump data and to set/access cells, rows, columns and # regions. nx::Class create Sudoku { :variable board # Setup an array from 0..9 to ease iterations over the cells of # lines and columns. for {set i 0} {$i < 9} {incr i} {lappend positions $i} :variable positions $positions :public method load {data} { # # Load a 9x9 partially solved sudoku. The unsolved cells are # represented by a@ symbols. # set error "data must be a 9-element list, each element also being a\ list of 9 numbers from 1 to 9 or blank or an @ symbol." if {[llength $data] != 9} { error $error } foreach y ${:positions} { set row [lindex $data $y] if {[llength $row] != 9} { error $error } foreach x ${:positions} { set cell [lindex $row $x] if {![regexp {^[@1-9]?$} $cell]} { error $cell-$error } if {$cell eq "@"} {set cell ""} :set $x $y $cell } } } :public method dump {-pretty-print:switch} { # # Output the current state of the sudoku either as list or in # a pretty-print style. # set rows [lmap y ${:positions} {:getRow 0 $y}] if {${pretty-print}} { set result +-----+-----+-----+\n foreach line $rows postline {0 0 1 0 0 1 0 0 1} { append result |[lrange $line 0 2]|[lrange $line 3 5]|[lrange $line 6 8]|\n if {$postline} { append result +-----+-----+-----+\n } } return $result } else { return $rows } } :method log {msg} { #puts "log: $msg" } :method set {x y value:integer,0..1} { # # Set cell at position x,y to the given value or empty. # if {$value<1 || $value>9} { set :board($x,$y) {} } else { set :board($x,$y) $value } } :method get {x y} { # # Get value of cell at position x, y. # return [set :board($x,$y)] } :method getRow {x y} { # # Return a row at constant position y. # return [lmap x ${:positions} {:get $x $y}] } :method getCol {x y} { # # Return a column at constant position x. # return [lmap y ${:positions} {:get $x $y}] } :method getRegion {x y} { # # Return a 3x3 region # set xR [expr {($x/3)*3}] set yR [expr {($y/3)*3}] set regn {} for {set x $xR} {$x < $xR+3} {incr x} { for {set y $yR} {$y < $yR+3} {incr y} { lappend regn [:get $x $y] } } return $regn } } # The class +SudokuSolver+ inherits from +Sudoku+, and adds the # ability to solve a given Sudoku game. The method 'solve' applies all # rules for each unsolved cell until it finds a safe solution. nx::Class create SudokuSolver -superclass Sudoku { :public method validchoices {x y} { set v [:get $x $y] if {$v ne {}} { return $v } set row [:getRow $x $y] set col [:getCol $x $y] set regn [:getRegion $x $y] set eliminate [list {*}$row {*}$col {*}$regn] set eliminate [lsearch -all -inline -not $eliminate {}] set eliminate [lsort -unique $eliminate] set choices {} for {set c 1} {$c < 10} {incr c} { if {$c ni $eliminate} { lappend choices $c } } if {[llength $choices]==0} { error "No choices left for square $x,$y" } return $choices } :method completion {} { # # Return the number of already solved items. # return [expr {81-[llength [lsearch -all -inline [join [:dump]] {}]]}] } :public method solve {} { # # Try to solve the sudoku by applying the provided rules. # while {1} { set begin [:completion] foreach y ${:positions} { foreach x ${:positions} { if {[:get $x $y] eq ""} { foreach rule [Rule info instances] { set c [$rule solve [self] $x $y] if {$c} { :set $x $y $c :log "[$rule info class] solved [self] at $x,$y for $c" break } } } } } set end [:completion] if {$end == 81} { :log "Finished solving!" break } elseif {$begin == $end} { :log "A round finished without solving any squares, giving up." break } } } } # The class rule provides "solve" as public interface for all rule # objects. The rule objects apply their logic to the values # passed in and return either '0' or a number to allocate to the # requested square. nx::Class create Rule { :public method solve {hSudoku:object,type=::SudokuSolver x y} { :Solve $hSudoku $x $y [$hSudoku validchoices $x $y] } # Get all the allocated numbers for each square in the the row, column, and # region containing $x,$y. If there is only one unallocated number among all # three groups, it must be allocated at $x,$y :create ruleOnlyChoice { :object method Solve {hSudoku x y choices} { if {[llength $choices] == 1} { return $choices } else { return 0 } } } # Test each column to determine if $choice is an invalid choice for all other # columns in row $X. If it is, it must only go in square $x,$y. :create RuleColumnChoice { :object method Solve {hSudoku x y choices} { foreach choice $choices { set failed 0 for {set x2 0} {$x2 < 9} {incr x2} { if {$x2 != $x && $choice in [$hSudoku validchoices $x2 $y]} { set failed 1 break } } if {!$failed} {return $choice} } return 0 } } # Test each row to determine if $choice is an invalid choice for all other # rows in column $y. If it is, it must only go in square $x,$y. :create RuleRowChoice { :object method Solve {hSudoku x y choices} { foreach choice $choices { set failed 0 for {set y2 0} {$y2 < 9} {incr y2} { if {$y2 != $y && $choice in [$hSudoku validchoices $x $y2]} { set failed 1 break } } if {!$failed} {return $choice} } return 0 } } # Test each square in the region occupied by $x,$y to determine if $choice is # an invalid choice for all other squares in that region. If it is, it must # only go in square $x,$y. :create RuleRegionChoice { :object method Solve {hSudoku x y choices} { foreach choice $choices { set failed 0 set regnX [expr {($x/3)*3}] set regnY [expr {($y/3)*3}] for {set y2 $regnY} {$y2 < $regnY+3} {incr y2} { for {set x2 $regnX} {$x2 < $regnX+3} {incr x2} { if { ($x2!=$x || $y2!=$y) && $choice in [$hSudoku validchoices $x2 $y2] } then { set failed 1 break } } } if {!$failed} {return $choice} } return 0 } } } SudokuSolver create sudoku { :load { {3 9 4 @ @ 2 6 7 @} {@ @ @ 3 @ @ 4 @ @} {5 @ @ 6 9 @ @ 2 @} {@ 4 5 @ @ @ 9 @ @} {6 @ @ @ @ @ @ @ 7} {@ @ 7 @ @ @ 5 8 @} {@ 1 @ @ 6 7 @ @ 8} {@ @ 9 @ @ 8 @ @ @} {@ 2 6 4 @ @ 7 3 5} } :solve puts [:dump -pretty-print] } # The dump method outputs the solved Sudoku: # # +-----+-----+-----+ # |3 9 4|8 5 2|6 7 1| # |2 6 8|3 7 1|4 5 9| # |5 7 1|6 9 4|8 2 3| # +-----+-----+-----+ # |1 4 5|7 8 3|9 6 2| # |6 8 2|9 4 5|3 1 7| # |9 3 7|1 2 6|5 8 4| # +-----+-----+-----+ # |4 1 3|5 6 7|2 9 8| # |7 5 9|2 3 8|1 4 6| # |8 2 6|4 1 9|7 3 5| # +-----+-----+-----+ doc/example-scripts/rosetta-unknown-method.html000066400000000000000000000512321242365656200223030ustar00rootroot00000000000000 Listing of doc/example-scripts/rosetta-unknown-method.tcl

Rosetta Example: Respond to an unknown method call

package req nx

Define a class Example modelled after the Python version of Rosetta:

nx::Class create Example {

  :public method foo {} {return "This is foo."}
  :public method bar {} {return "This is bar."}

  :method unknown {method args} {
    set result "Tried to handle unknown method '$method'."
    if {[llength $args] > 0} {
      append result " It had arguments '$args'."
    }
    return $result
  }
}

Demonstrating the behavior in a shell:

Create an instance of the class Example:

% set e [Example new]

% $e foo
This is foo.

% $e bar
This is bar.

% $e grill
Tried to handle unknown method 'grill'.

% $e ding dong
Tried to handle unknown method 'ding'. It had arguments 'dong'.

doc/example-scripts/rosetta-unknown-method.tcl000066400000000000000000000016201242365656200221150ustar00rootroot00000000000000# # == Rosetta Example: Respond to an unknown method call # For details see http://rosettacode.org/wiki/Respond_to_an_unknown_method_call # package req nx package req nx::test # # Define a class Example modelled after the # Python version of Rosetta: # nx::Class create Example { :public method foo {} {return "This is foo."} :public method bar {} {return "This is bar."} :method unknown {method args} { set result "Tried to handle unknown method '$method'." if {[llength $args] > 0} { append result " It had arguments '$args'." } return $result } } # === Demonstrating the behavior in a shell: # # Create an instance of the class Example: ? {set e [Example new]} "::nsf::__#0" ? {$e foo} "This is foo." ? {$e bar} "This is bar." ? {$e grill} "Tried to handle unknown method 'grill'." ? {$e ding dong} "Tried to handle unknown method 'ding'. It had arguments 'dong'." doc/example-scripts/ruby-mixins.html000066400000000000000000001232211242365656200201330ustar00rootroot00000000000000 Listing of doc/example-scripts/ruby-mixins.tcl

Design study to show the differences between decorator mixin classes and Ruby’s mixin modules

This example shows that the dynamic class structure of NX (and XOTcl) is able to support Ruby style mixins (called modules) and decorator style mixins (named after the design pattern Decorator) in the same script.

nx::test configure -count 1

One important difference between mixin classes in NX and Ruby’s mixins is the precedence order. While in NX, mixins are decorators (the mixins have higher precedence than the intrinsic classes, therefore a mixin class can overload the methods of the current class and its subclasses), the mixins of Ruby have a lower precedence (they extend the base behavior; although Ruby’s modules are not full classes, they are folded into the intrinsic class hierarchy). Therefore, a Ruby style mixin can be refined by the class, into which it is mixed in (or by a subclass). Decorator style mixins modify the behavior of a full intrinsic class tree, while Ruby style mixins are compositional units for a single class.

To show the differences, we define the method module, which behaves somewhat similar to Ruby’s module command. This method adds the provided class to the precedence order after the current class. The easiest way to achieve this is via multiple inheritance (i.e. via the superclass relationship).

package req nx

nx::Class eval {
  :protected method module {name:class} {
    nsf::relation [self] superclass [concat $name [:info superclass]]
  }
}

For illustration of the behavior of module we define a class Enumerable somewhat inspired by the Ruby module with the same name. We define here just the methods map, each, count, and count_if.

nx::Class create Enumerable {
  :property members:0..n

  # The method 'each' applies the provided block on every element of
  # 'members'
  :public method each {var block} {
    foreach member ${:members} {
      uplevel [list set $var $member]
      uplevel $block
    }
  }

  # The method 'map' applies the provided block on every element of
  # 'members' and returns a list, where every element is the mapped
  # result of the source.
  :public method map {var block} {
    set result [list]
    :each $var {
      uplevel [list set $var [set $var]]
      lappend result [uplevel $block]
    }
    return $result
  }

  # The method 'count' returns the number of elements.
  :public method count {} {
    return [llength ${:members}]
  }

  # The method 'count_if' returns the number of elements for which
  # the provided expression is true.
  :public method count_if {var expr} {
    set result 0
    :each $var {
      incr result [expr $expr]
    }
    return $result
  }
}

After having defined the class Enumerable, we define a class Group using Enumerable as a Ruby style mixin. This makes essentially Group a subclass of Enumerable, but with the only difference that Group might have other superclasses as well.

nx::Class create Group {
  #
  # Include the "module" Enumerable
  #
  :module Enumerable
}

Define now a group g1 with the three provided members.

% Group create g1 -members {mini trix trax}
::g1

Since the definition of Group includes the module Enumerable, this class is listed in the precedence order after the class Group:

% g1 info precedence
::Group ::Enumerable ::nx::Object

Certainly, we can call the methods of Enumerable as usual:

% g1 count
3

% g1 map x {list pre-$x-post}
pre-mini-post pre-trix-post pre-trax-post

% g1 count_if x {[string match tr*x $x] > 0}
2

To show the difference between a module and a decorator mixin we define a class named Mix with the single method count, which wraps the result of the underlaying count method between the alpha and omega.

nx::Class create Mix {
  :public method count {} {
    return [list alpha [next] omega]
  }
}

When the mixin class is added to g1, it is added to the front of the precedence list. A decorator is able to modify the behavior of all of the methods of the class, where it is mixed into.

% g1 object mixin Mix
::Mix

% g1 info precedence
::Mix ::Group ::Enumerable ::nx::Object

% g1 count
alpha 3 omega

For the time being, remove the mixin class again.

% g1 object mixin ""
% g1 info precedence
::Group ::Enumerable ::nx::Object

An important difference between NX/XOTcl style mixins (decorators) and Ruby style modules is that the decorator will have always a higher precedence than the intrinsic classes, while the module is folded into the precedence path.

Define a class ATeam that uses Enumerable in the style of a Ruby module. The class might refine some of the provided methods. We refined the method each, which is used as well by the other methods. In general, by defining each one can define very different kind of enumerators (for lists, databases, etc.).

Since Enumerable is a module, the definition of each in the class ATeam has a higher precedence than the definition in the class Enumerable. If Enumerable would be a decorator style mixin class, it would not e possible to refine the definition in the class ATeam, but maybe via another mixin class.

nx::Class create ATeam {
  #
  # Include the "module" Enumerable
  #
  :module Enumerable

  #
  # Overload "each"
  #
  :public method each {var block} {
    foreach member ${:members} {
      uplevel [list set $var $member-[string length $member]]
      uplevel $block
    }
  }

  #
  # Use "map", which uses the "each" method defined in this class.
  #
  :public method foo {} {
    return [:map x {string totitle $x}]
  }
}

Define now a team t1 with the three provided members.

% ATeam create t1 -members {arthur bill chuck}
::t1

As above, the precedence of ATeam is higher than the precedence of Enumerable. Therefore, the object t1 uses the method each specialized in class ATeam:

% t1 info precedence
::ATeam ::Enumerable ::nx::Object

% t1 foo
Arthur-6 Bill-4 Chuck-5

The class ATeam can be specialized further by a class SpecialForce:

nx::Class create SpecialForce -superclass ATeam {
  # ...
}

Define a special force s1 with the four provided members.

% SpecialForce create s1 -members {Donald Micky Daniel Gustav}
::s1

As above, the precedence of Enumerable is lower then the precedence of ATeam and Enumerable. Therefore ATeam can refine the behavior of Enumerable, the class SpecialForce can refine the behavior of ATeam.

% s1 info precedence
::SpecialForce ::ATeam ::Enumerable ::nx::Object

% s1 foo
Donald-6 Micky-5 Daniel-6 Gustav-6

Let us look again on decorator style mixin classes. If we add a per-class mixin to ATeam, the mixin class has highest precedence, and decorates the instances of ATeam as well the instances of its specializations (like e.g. SpecialForce).

% ATeam mixin Mix
::Mix

% s1 info precedence
::Mix ::SpecialForce ::ATeam ::Enumerable ::nx::Object

% s1 count
alpha 4 omega

This example showed that NX/XOTcl dynamic class structure is able to support Ruby-style mixins, and decorator style mixins in the same script.


doc/example-scripts/ruby-mixins.tcl000066400000000000000000000162631242365656200177600ustar00rootroot00000000000000# # == Design study to show the differences between decorator mixin classes and Ruby's mixin modules # # This example shows that the dynamic class structure of NX (and # XOTcl) is able to support _Ruby style mixins_ (called modules) and # _decorator style mixins_ (named after the design pattern Decorator) # in the same script. package req nx::test nx::test configure -count 1 # # One important difference between mixin classes in NX and Ruby's # mixins is the precedence order. While in NX, mixins are decorators # (the mixins have higher precedence than the intrinsic classes, # therefore a mixin class can overload the methods of the current # class and its subclasses), the mixins of Ruby have a lower # precedence (they extend the _base behavior_; although Ruby's modules # are not full classes, they are folded into the _intrinsic class # hierarchy_). Therefore, a Ruby style mixin can be refined by the # class, into which it is mixed in (or by a subclass). Decorator style # mixins modify the behavior of a full intrinsic class tree, while # Ruby style mixins are compositional units for a single class. # # To show the differences, we define the method +module+, which # behaves somewhat similar to Ruby's +module+ command. This method # adds the provided class to the precedence order after the current # class. The easiest way to achieve this is via multiple inheritance # (i.e. via the +superclass+ relationship). # package req nx nx::Class eval { :protected method module {name:class} { nsf::relation::set [self] superclass [concat $name [:info superclasses]] } } # # For illustration of the behavior of +module+ we define a class # +Enumerable+ somewhat inspired by the Ruby module with the same # name. We define here just the methods +map+, +each+, +count+, and # +count_if+. # nx::Class create Enumerable { :property members:0..n # The method 'each' applies the provided block on every element of # 'members' :public method each {var block} { foreach member ${:members} { uplevel [list set $var $member] uplevel $block } } # The method 'map' applies the provided block on every element of # 'members' and returns a list, where every element is the mapped # result of the source. :public method map {var block} { set result [list] :each $var { uplevel [list set $var [set $var]] lappend result [uplevel $block] } return $result } # The method 'count' returns the number of elements. :public method count {} { return [llength ${:members}] } # The method 'count_if' returns the number of elements for which # the provided expression is true. :public method count_if {var expr} { set result 0 :each $var { incr result [expr $expr] } return $result } } # After having defined the class +Enumerable+, we define a class # +Group+ using +Enumerable+ as a Ruby style mixin. This makes # essentially +Group+ a subclass of +Enumerable+, but with the only # difference that +Group+ might have other superclasses as well. nx::Class create Group { # # Include the "module" Enumerable # :module Enumerable } # Define now a group +g1+ with the three provided members. ? {Group create g1 -members {mini trix trax}} ::g1 # Since the definition of +Group+ includes the module +Enumerable+, # this class is listed in the precedence order after the class +Group+: ? {g1 info precedence} "::Group ::Enumerable ::nx::Object" # Certainly, we can call the methods of +Enumerable+ as usual: ? {g1 count} 3 ? {g1 map x {list pre-$x-post}} "pre-mini-post pre-trix-post pre-trax-post" ? {g1 count_if x {[string match tr*x $x] > 0}} 2 # # To show the difference between a +module+ and a +decorator mixin+ we # define a class named +Mix+ with the single method +count+, which # wraps the result of the underlaying +count+ method between the # +alpha+ and +omega+. nx::Class create Mix { :public method count {} { return [list alpha [next] omega] } } # When the mixin class is added to +g1+, it is added to the front of # the precedence list. A decorator is able to modify the behavior of # all of the methods of the class, where it is mixed into. ? {g1 object mixins set Mix} "::Mix" ? {g1 info precedence} "::Mix ::Group ::Enumerable ::nx::Object" ? {g1 count} {alpha 3 omega} # For the time being, remove the mixin class again. ? {g1 object mixins set ""} "" ? {g1 info precedence} "::Group ::Enumerable ::nx::Object" # # An important difference between NX/XOTcl style mixins (decorators) # and Ruby style modules is that the decorator will have always a # higher precedence than the intrinsic classes, while the +module+ is # folded into the precedence path. # # Define a class +ATeam+ that uses +Enumerable+ in the style of a Ruby # module. The class might refine some of the provided methods. We # refined the method +each+, which is used as well by the other # methods. In general, by defining +each+ one can define very # different kind of enumerators (for lists, databases, etc.). # # Since +Enumerable+ is a module, the definition of +each+ in the # class +ATeam+ has a higher precedence than the definition in the # class +Enumerable+. If +Enumerable+ would be a decorator style mixin # class, it would not e possible to refine the definition in the class # +ATeam+, but maybe via another mixin class. # nx::Class create ATeam { # # Include the "module" Enumerable # :module Enumerable # # Overload "each" # :public method each {var block} { foreach member ${:members} { uplevel [list set $var $member-[string length $member]] uplevel $block } } # # Use "map", which uses the "each" method defined in this class. # :public method foo {} { return [:map x {string totitle $x}] } } # # Define now a team +t1+ with the three provided members. # ? {ATeam create t1 -members {arthur bill chuck}} ::t1 # As above, the precedence of +ATeam+ is higher than the precedence of # +Enumerable+. Therefore, the object +t1+ uses the method +each+ specialized in # class +ATeam+: ? {t1 info precedence} "::ATeam ::Enumerable ::nx::Object" ? {t1 foo} "Arthur-6 Bill-4 Chuck-5" # # The class +ATeam+ can be specialized further by a class +SpecialForce+: # nx::Class create SpecialForce -superclass ATeam { # ... } # Define a special force +s1+ with the four provided members. ? {SpecialForce create s1 -members {Donald Micky Daniel Gustav}} ::s1 # As above, the precedence of +Enumerable+ is lower then the # precedence of +ATeam+ and +Enumerable+. Therefore +ATeam+ can refine # the behavior of +Enumerable+, the class +SpecialForce+ can refine # the behavior of +ATeam+. ? {s1 info precedence} "::SpecialForce ::ATeam ::Enumerable ::nx::Object" ? {s1 foo} "Donald-6 Micky-5 Daniel-6 Gustav-6" # Let us look again on decorator style mixin classes. If we add a # per-class mixin to +ATeam+, the mixin class has highest precedence, # and decorates the instances of +ATeam+ as well the instances of its # specializations (like e.g. +SpecialForce+). ? {ATeam mixins set Mix} "::Mix" ? {s1 info precedence} "::Mix ::SpecialForce ::ATeam ::Enumerable ::nx::Object" ? {s1 count} {alpha 4 omega} # This example showed that NX/XOTcl dynamic class structure is able to # support Ruby-style mixins, and decorator style mixins in the same script. doc/example-scripts/tk-geo.html000066400000000000000000001075511242365656200170430ustar00rootroot00000000000000 Listing of doc/example-scripts/tk-geo.tcl

Drawing geometric figures - the result of airplane travel.

The example script shows the use of canvas and geometric figues (regular, convex polygons) with different number of edges based on trigonometric functions.

-gustaf neumann (Aug 2, 2013)

tk-geo1.png
tk-geo2.png
package require Tk
package require nx

Class Canvas is a simple convenience wrapper for the tk canvas, which packs itself.

nx::Class create Canvas {
  :property {canvas .canvas}
  :property {bg beige}
  :property {height 500}
  :property {width 500}

  :method init {} {
    canvas ${:canvas} -bg ${:bg} -height ${:height} -width ${:width}
    pack ${:canvas}
  }
}

Class Area provides a center point (x, y) and a radius

nx::Class create Area {
  :property {canvas .canvas}
  :property {x 250}
  :property {y 250}
  :property {radius 200}

  :variable pi [expr {acos(-1)}]

  :method degree {d} {
    #
    # return a coordinate pair on a circle around the center point with
    # :radius at the provided degrees (0..360)
    #
    set x  [expr {$d*${:pi}/180.0 - ${:pi}/2.0}]
    set x0 [expr {cos($x)*${:radius}+${:x}}]
    set y0 [expr {sin($x)*${:radius}+${:y}}]
    list $x0 $y0
  }

  :method n-tangle {n} {
    #
    # Draw a regular n-tangle (e.g. when n==3, a triangle) inscribed to
    # a circle with radius :radius
    #
    for {set i 0} {$i < $n} {incr i} {
      set p($i) [:degree [expr {$i*360/$n}]]
    }
    lassign $p(0) x0 y0
    for {set i 1} {$i < $n} {incr i} {
      lassign $p($i) x1 y1
      ${:canvas} create line $x0 $y0 $x1 $y1
      lassign $p($i) x0 y0
    }
    lassign $p(0) x1 y1
    ${:canvas} create line $x0 $y0 $x1 $y1
  }
}

Class Inscribe draws multiple n-tangles with the came center point.

nx::Class create Inscribe -superclass Area {
  :property {count 4}
  :property {edges 3}
  :method init {} {
    for {set i 0} {$i < ${:count}} {incr i} {
      ${:canvas} create oval \
          [expr {${:x}-${:radius}}] [expr {${:y}-${:radius}}] \
          [expr {${:x}+${:radius}}] [expr {${:y}+${:radius}}]
      :n-tangle ${:edges}
      set :radius [expr {${:radius}/2.0}]
    }
  }
}

Class Hull creates an n-tangle with :density hull lines between neighboring edges

nx::Class create Hull -superclass Area {
  :property {edges 3}
  :property {density 10}

  :method n-tangle {n} {
    for {set i 0} {$i < $n} {incr i} {
      set p($i) [:degree [expr {$i*360/$n}]]
    }
    lassign $p(0) x0 y0
    for {set i 1} {$i < $n} {incr i} {
      lassign $p($i) x1 y1
      set line($i) [list $x0 $y0 $x1 $y1]
      ${:canvas} create line $x0 $y0 $x1 $y1
      lassign $p($i) x0 y0
    }
    lassign $p(0) x1 y1
    ${:canvas} create line $x0 $y0 $x1 $y1
    set line(0) [list $x0 $y0 $x1 $y1]
    set line($n) [list $x0 $y0 $x1 $y1]

    for {set i 0} {$i < $n} {incr i} {
      lassign $line($i) x0 y0 x1 y1
      lassign $line([expr {$i+1}]) x2 y2 x3 y3
      set dx1 [expr {($x0 - $x1)*1.0/${:density}}]
      set dy1 [expr {($y0 - $y1)*1.0/${:density}}]
      set dx2 [expr {($x2 - $x3)*1.0/${:density}}]
      set dy2 [expr {($y2 - $y3)*1.0/${:density}}]
      for {set j 1} {$j < ${:density}} {incr j} {
        ${:canvas} create line [expr {$x0-$dx1*$j}] [expr {$y0-$dy1*$j}] \
            [expr {$x2-$dx2*$j}] [expr {$y2-$dy2*$j}]
      }
    }
  }

  :method init {} {
    :n-tangle ${:edges}
  }
}

Draw either one larger figure with inner figures or a series of smaller figures next to each other.

set multiple 0

if {$multiple} {
  # Draw a series of figures next to each other
  set c [::Canvas new -width 650 -height 750 -bg white]
  ::Inscribe new -canvas [$c cget -canvas] -x 100 -y 100 -radius 80 -count 7
  ::Inscribe new -canvas [$c cget -canvas] -x 300 -y 100 -radius 80 -count 7 -edges 4
  ::Inscribe new -canvas [$c cget -canvas] -x 500 -y 100 -radius 80 -count 7 -edges 5
  ::Hull new -canvas [$c cget -canvas] -x 100 -y 300 -radius 80 -edges 3 -density 10
  ::Hull new -canvas [$c cget -canvas] -x 300 -y 300 -radius 80 -edges 4 -density 10
  ::Hull new -canvas [$c cget -canvas] -x 500 -y 300 -radius 80 -edges 5 -density 10
  ::Hull new -canvas [$c cget -canvas] -x 300 -y 600 -radius 200 -edges 3 -density 40
} else {
  # Draw a several series of figures with the same center
  set c [::Canvas new -width 650 -height 650 -bg white]
  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 300 -edges 5 -density 40
  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 150 -edges 4 -density 20
  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 75 -edges 3 -density 10
  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 30 -edges 5 -density 5
}

doc/example-scripts/tk-geo.tcl000066400000000000000000000111121242365656200166440ustar00rootroot00000000000000# Drawing geometric figures - the result of airplane travel. # # The example script shows the use of canvas and geometric figues # (regular, convex polygons) with different number of edges based on # trigonometric functions. # # -gustaf neumann (Aug 2, 2013) # # image::tk-geo1.png[width=400] # image::tk-geo2.png[width=400] # package require Tk package require nx # # Class Canvas is a simple convenience wrapper for the tk canvas, # which packs itself. # nx::Class create Canvas { :property {canvas .canvas} :property {bg beige} :property {height 500} :property {width 500} :method init {} { canvas ${:canvas} -bg ${:bg} -height ${:height} -width ${:width} pack ${:canvas} } } # # Class Area provides a center point (x, y) and a radius # nx::Class create Area { :property {canvas .canvas} :property {x 250} :property {y 250} :property {radius 200} :variable pi [expr {acos(-1)}] :method degree {d} { # # return a coordinate pair on a circle around the center point with # :radius at the provided degrees (0..360) # set x [expr {$d*${:pi}/180.0 - ${:pi}/2.0}] set x0 [expr {cos($x)*${:radius}+${:x}}] set y0 [expr {sin($x)*${:radius}+${:y}}] list $x0 $y0 } :method n-tangle {n} { # # Draw a regular n-tangle (e.g. when n==3, a triangle) inscribed to # a circle with radius :radius # for {set i 0} {$i < $n} {incr i} { set p($i) [:degree [expr {$i*360/$n}]] } lassign $p(0) x0 y0 for {set i 1} {$i < $n} {incr i} { lassign $p($i) x1 y1 ${:canvas} create line $x0 $y0 $x1 $y1 lassign $p($i) x0 y0 } lassign $p(0) x1 y1 ${:canvas} create line $x0 $y0 $x1 $y1 } } # # Class Inscribe draws multiple n-tangles with the came center point. # nx::Class create Inscribe -superclass Area { :property {count 4} :property {edges 3} :method init {} { for {set i 0} {$i < ${:count}} {incr i} { ${:canvas} create oval \ [expr {${:x}-${:radius}}] [expr {${:y}-${:radius}}] \ [expr {${:x}+${:radius}}] [expr {${:y}+${:radius}}] :n-tangle ${:edges} set :radius [expr {${:radius}/2.0}] } } } # # Class Hull creates an n-tangle with :density hull lines between # neighboring edges # nx::Class create Hull -superclass Area { :property {edges 3} :property {density 10} :method n-tangle {n} { for {set i 0} {$i < $n} {incr i} { set p($i) [:degree [expr {$i*360/$n}]] } lassign $p(0) x0 y0 for {set i 1} {$i < $n} {incr i} { lassign $p($i) x1 y1 set line($i) [list $x0 $y0 $x1 $y1] ${:canvas} create line $x0 $y0 $x1 $y1 lassign $p($i) x0 y0 } lassign $p(0) x1 y1 ${:canvas} create line $x0 $y0 $x1 $y1 set line(0) [list $x0 $y0 $x1 $y1] set line($n) [list $x0 $y0 $x1 $y1] for {set i 0} {$i < $n} {incr i} { lassign $line($i) x0 y0 x1 y1 lassign $line([expr {$i+1}]) x2 y2 x3 y3 set dx1 [expr {($x0 - $x1)*1.0/${:density}}] set dy1 [expr {($y0 - $y1)*1.0/${:density}}] set dx2 [expr {($x2 - $x3)*1.0/${:density}}] set dy2 [expr {($y2 - $y3)*1.0/${:density}}] for {set j 1} {$j < ${:density}} {incr j} { ${:canvas} create line [expr {$x0-$dx1*$j}] [expr {$y0-$dy1*$j}] \ [expr {$x2-$dx2*$j}] [expr {$y2-$dy2*$j}] } } } :method init {} { :n-tangle ${:edges} } } # Draw either one larger figure with inner figures # or a series of smaller figures next to each other. set multiple 0 if {$multiple} { # Draw a series of figures next to each other set c [::Canvas new -width 650 -height 750 -bg white] ::Inscribe new -canvas [$c cget -canvas] -x 100 -y 100 -radius 80 -count 7 ::Inscribe new -canvas [$c cget -canvas] -x 300 -y 100 -radius 80 -count 7 -edges 4 ::Inscribe new -canvas [$c cget -canvas] -x 500 -y 100 -radius 80 -count 7 -edges 5 ::Hull new -canvas [$c cget -canvas] -x 100 -y 300 -radius 80 -edges 3 -density 10 ::Hull new -canvas [$c cget -canvas] -x 300 -y 300 -radius 80 -edges 4 -density 10 ::Hull new -canvas [$c cget -canvas] -x 500 -y 300 -radius 80 -edges 5 -density 10 ::Hull new -canvas [$c cget -canvas] -x 300 -y 600 -radius 200 -edges 3 -density 40 } else { # Draw a several series of figures with the same center set c [::Canvas new -width 650 -height 650 -bg white] ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 300 -edges 5 -density 40 ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 150 -edges 4 -density 20 ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 75 -edges 3 -density 10 ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 30 -edges 5 -density 5 } doc/example-scripts/tk-geo1.png000066400000000000000000016420431242365656200167450ustar00rootroot00000000000000‰PNG  IHDR°œ-áÕ iCCPICC ProfileH ­WgXSÉžS’@HhP¤„Þ‘^¥÷¢ l„$$¡„"bwQÁµ‹VtUDŵ²¨ˆ]Y{ßXPPÖÅ‚ ”;‡¢îÝ»ÿîyž™óžwÞù¾o¾™3Ï Šl±8 U [”/‰ ñg%%§°(€P6lNžØ/::üëóþ&@ˆÆk6„­•ýï./ ›Ó¸yœlˆ€5rÄ’|H„=ã™ùb¯‚XM„x'ù#¸‘Ài#¸mX52ähl¶„½ò¬ÚQ¤Al'â EO€Ø›#`s!.†Ø:;;‡À{ 6OûÁÿÌf§}³Éfó¿á‘±ÀžÐq 0OœÅž5üñÿ¬²³¤0_ìiy™±ðÍ„y+ä°ƒb!Ö„x…€9ÊïçûÇŒòÍÂü°8ˆÕ æº@?Š»¥™ñ~ë@~03'‚ÐÃ<¡š¢´IQ«BlÌÉ €¹'|¡.E‚¸ÄQM$—1\Eh’$'fL/È+ˆ㋊“Æôìpb¾¡¾”-h8´‚—Bø5„ü>q~4'á«]”5it,èÓtI0¡!øÏ¼¼áñ± òq¡‡1cÊù’8BÇˆé¤ ƒÃ †±avIèï+Î^Ó°/'‘Æy0†8'Š'rHð¥\v ‘[˜ll <D °@$£5 ò"Èq@È‚EÂRk!=#u’“nd¤;cì9ªBÀ…xÄÖý! ŠÀŸÐ*äyõqoÜ„µ/,¸î>ÖÖÞÛÐ;†Gcåþ6£¶ýG£/€¿ŒéfJÆðhŸ´o=þS0x 3ÀSØÕÚõØ Žõÿ>br9J&[`K±ÃØyìvkÆ ;‰5bmØqÆ5æ… "+D†ó@Ì"H‡¿Dcþþ–%é7ŨEKEg{‰@&l~ó0µðV¤P‘=f@mÄ·ù 7…ÙuÆýq/˜g˜cœ‰kÜ fÜ÷sà Ùï³ø÷ÑØ€ôál %<ƒãÈÎçæÃµrij$B¾ ŸåwKž5+Lıµf9ØÙ;bï%4¼eï©óÒw.·÷RøÛ‹PÀ6àØ3ï¿sFoào÷Ê㩤`D‡/ %øWh=`ÌaF€ ð¾ „ƒ(’Át¸† F<ƒ ”U`=¨[Á°ì‡@h§À9pt€à.ðô÷`A BGˆ¢˜ Vˆâ†x#AH$ƒ$#©!R¤Y„”!kJd;RƒüŠCN!‘NäòéAÞ ŸQ ¥¡j¨.jŠŽGÝP?4C§¡|4-B£+Ð ´֣݇§ÐËè T†¾Dû1€)`L̳Áܰ, KÁÒ1 6+ÅʱjìÖ×â5L†õbŸp2ÎÀY¸ œÉP<çà¹ø\|9^‰ïÁëñ3ø5üÞ‡%ÑI:$+’)Œ”Dâ“f’JHå¤]¤£¤³ðî"½'“ÉL²Ù®ödry6y9y3¹ŽÜBî$?!÷S(-ŠÅ‹EaSò)%””}”“”«”.ÊG99}9¹`¹9‘ÜB¹r¹½r'ä®Ê=—W–7‘÷’çÊÏ’_)¿S¾IþŠ|—üU…jFõ¢ÆQ3¨ ¨ÔÔ³ÔûÔ· † î “„ ó**\Px¤ð‰¦J³¤ЦҤ´´Ý´ÚÚ[:nJ÷¥§Ðóé+è5ôÓô‡ôŠ E[Å0E®â<Å*ÅzūН”ä•L”ü”¦+)•+Vº¢Ô«,¯lª ÌVž«\¥|Lù–r¿ CÅ^%J%[e¹Ê^•‹*ݪUSÕ U®êbÕª§UŸ00†#€Áa,bìdœet©‘ÕÌÔÂÔ2ÔÊÔö«µ«õ©«ª;©'¨ªW©W—11¦)3Œ™Å\É<ļÉü¬¡«á§ÁÓX¦q@ãªÆÍqš¾š<ÍRÍ:ÍšŸµXZAZ™Z«µ´hãÚ–Ú“µgjoÑ>«Ý;Nmœç8θÒq‡ÆÝÕAu,ubtfëìÐiÓé×ÕÓ ÑënÔ=­Û«ÇÔóÕËÐ[§wB¯GŸ¡ï­/Ô_§RÿKåÇÊbU°Î°ú t B ¤Û Ú  Í ã Ö>0¢¹¥­3j5ê3Ö7žh\l\k|×DÞÄÍD`²Áä¼ÉS3ÓDÓ%¦ ¦ÝfšfafEfµf÷Íéæ>æ¹æÕæ×-Èn™›-:,QKgKe•å+ÔÊÅJhµÙªÓšdín-²®¶¾eC³ñ³)°©µydË´´]hÛ`ûj¼ñø”ñ«ÇŸÿÕÎÙ.Ën§Ý={Uûpû…öMöo,8U×éŽÁŽó_;Y9ñœ¶8Ývf8Ot^âÜêüÅÅÕEârÀ¥ÇÕØ5Õu“ë-75·h·ånÜIîþîóÜ›Ý?y¸xä{òøËÓÆ3Ós¯g÷³ ¼ ;'<ñ2ôb{m÷’y³¼S½·yË| |Ø>Õ>}|¹¾»|ŸûYøeøíó{åoç/ñ?êÿ!À#`N@K XؤTô0Ø0˜\Üâ2;¤%”º:ôV˜n'¬&¬/Ü5|Nø™ZDlDeÄãHËHIdÓDtbøÄµïO2™$šÔ¢Â¢ÖF=ˆ6‹Îþm2yrôäªÉÏbìcŠcÎÇ2bgÄî}ç·2î^¼y¼4¾5A)ajBM‡ÄÀÄ5‰²¤ñIs’.'k' “S() )»Rú§MY?¥kªóÔ’©7§™M+œvqºöô¬éÇg(Í`Ï8œJJMLÝ›:ÈŽbW³ûÓÂÒ6¥õq88/¹¾ÜuÜžo ïyºWúšôn¾-¿Gà#(ô „•Âס[3>dFeîÎÊJ̪˖ËNÍ>&ReŠÎäèåætŠ­Ä%bY®GîúÜ>I„dW’7-¯1_ rÛ¤æÒŸ¤ ¼ ª >ÎL˜y¸P¥PTØ6ËrÖ²YÏ‹‚‹~™ÏæÌn-6(^PühŽßœís‘¹is[çÍ[<¯k~Èü= ¨ 2ü¾Ðnáš…ï%.jZ¬»xþâ'?…üT[¢X")¹µÄsÉÖ¥øRáÒöeŽË6.ûZÊ-½TfWV^6¸œ³üÒÏö?Wü<´"}EûJ—•[V‘W‰VÝ\í³zÏ•5Ekž¬¸¶~k]éºwëg¬¿XîT¾uuƒtƒ¬"²¢q£ñÆU+•7ªü«ê6élZ¶éÃfîæ«[|·ت»µlëçmÂm··‡l¯¯6­.ßAÞQ°ãÙ΄çqû¥f—ö®²]_v‹vËöÄì9SãZS³WgïÊZ´VZÛ³o꾎ýûØØ^Ǭ+;J¾ø5õ×›‡"µv;|àˆÉ‘MGGKë‘úYõ} ‚Ycrcç±ðc­MžMG³ýmw³AsÕqõã+OPO,>1t²èd‹¸¥÷ÿÔ“Ö­÷N'¾~fò™ö³g/œ >wú¼ßù“¼.4_ô¸xì’Û¥†Ë.—ëۜێþîüûÑv—öú+®W;Ü;š:'tž¸êsõÔµÀk箇]¿|cÒΛñ7oßšzKv›{»ûNÖ×w îÜ›Ÿt¿ôòƒò‡:«ÿ°ø£Næ";þ(ðQÛãØÇ÷žpž¼|š÷t°kñ3ú³òçúÏkºº›{‚{:^LyÑõRür ·äO•?7½2uä/ß¿Úú’úº^K^½YþVëíîwNïZû£û¾Ï~?ð¡ô£ÖÇ=ŸÜ>ÿœøùùÀÌAÊ`Å‹/M_#¾Þʳ%ìá³k4=€7»á½(ž: *ŽÜ†ÈÈ}bd´ôá‘ûÑÏ`·/ñóˆl` ,&Óà›8æÇùÔÑñ[ ñä¥;: „&G“CCou 4ðE244°yhèËNxî¾@KîÈŒP“á9~›.¶/ŸO¼|þ#å`l…øº+ pHYs%%IR$ðŸiTXtXML:com.adobe.xmp 1200 1180 U:@IDATxìuÀÅõþ+TÒRŠ-n !”༠nÁJ€P44¸»[q‚»B°  )nA¼Hi ýå÷)Ï·‡ÉîÞ½{o¼y÷É—½»3gÎ|fö2ç=3ç|ìØ±ßó?000000úøAýºì›€ ˜€ ˜€ ˜€ ˜€ ˜€ ü—€ BÏ00000¨)„5xwÛLÀLÀLÀLÀLÀlz˜€ ˜€ ˜€ ˜€ ˜€ ˜@M Ø ¬éÀ»Û&`&`&`&`&`&`ƒÐsÀLÀLÀLÀLÀLÀjJÀaMÞÝ600000„ž&`&`&`&`&`&PS6k:ðî¶ ˜€ ˜€ ˜€ ˜€ ˜€ Ø ô000000š°AXÓw·MÀLÀLÀLÀLÀLÀ¡ç€ ˜€ ˜€ ˜€ ˜€ ˜€ Ô”€ š¼»m&`&`&`&`&`6=LÀLÀLÀLÀLÀL ¦lÖtàÝm00000°Aè9`&`&`&`&`&`5%`ƒ°¦ïn›€ ˜€ ˜€ ˜€ ˜€ ˜€ BÏ00000¨)„5xwÛLÀLÀLÀLÀLÀlz˜€ ˜€ ˜€ ˜€ ˜€ ˜@M Ø ¬éÀ»Û&`&`&`&`&`&`ƒÐsÀLÀLÀLÀLÀLÀjJÀaMÞÝ600000„ž&`&`&`&`&`&PS6k:ðî¶ ˜€ ˜€ ˜€ ˜€ ˜€ Ø ô000000š°AXÓw·MÀLÀLÀLÀLÀLÀ¡ç€ ˜€ ˜€ ˜€ ˜€ ˜€ Ô”€ š¼»m&`&`&`&`&`6=LÀLÀLÀLÀLÀL ¦lÖtàÝm00000°Aè9`&`&`&`&`&`5%`ƒ°¦ïn›€ ˜€ ˜€ ˜€ ˜€ ˜€ BÏ00000¨)„5xwÛLÀLÀLÀLÀLÀ&100¨ —^zéòË/§³›nºésÌQ“^»›&`&`&PBàûcÇŽ-yìG&`&`&Ð9\rÉ%}ûöýì³ÏèÎä“O~æ™göîÝ»stͽ000¶ Ø l+š€ ˜€ L>ýôÓí·ß^¾Áå–[¥ï¹ç>ñž}öÙSL1ÅÄÑ ki&`&`€€ÏN¨i&`&ÐaŒ9²K—.Xƒ“M6Ù…^8ì«\ð•›<¢@‡QÖŠ˜€ ˜€ ˜À·MÀÂo›¸Û30øÖ`øõéÓçË/¿\xá…¯¾úê¹æš+š~á…6ÜpÃQ£FM2É$çž{îV[m|a&`&`õ!`a}ÆÚ=503fÌÊ+¯¼õÖ[c n¶Ùf?üpj ‚¯Üä(FaªÔ»j&`&`_°‡ÐÁLÀL ³xôÑGW[mµ÷Þ{ïW¿ú±d¸.éám·ÝFtæº[·n%…ýÈLÀLÀ:{;Ù€º;&`&Pk_|ñE¿~ý]tQ ¼•VZé©§ž*·EŠQ˜*T¤:Bj Ñ70¨{ë4Úî« ˜€ tjé±ÀÝvÛí¸ãŽk©»ýû÷?ùä“ ¶$Ç…MÀLÀL`""`ƒp",«j&`&ÐÀàÁƒ óüã7¿ùÍUW]…¯¯aÑÆˆ8ºÑF½üòËÄ %MÏž=—õ00è ¼e´3Œ¢û`&`u&€·à‚ ®±ÆXƒ«¬²ÊO<Ñž5C*R!ˆB b”¢ÎSË}70:°AX‡QvMÀL Ó8ì°Ã[l±§Ÿ~šþà?À–ûñ<>½¥:B…Ä"œ&ÆG ëš€ ˜€ ˜@G&à-£yt¬› ˜€ ˜@Cÿþ÷¿±Üî¹çJL9å”\_wÝucÇŽÅ„»ôÒK3I&J÷§7ß|sÒQ|ÿûß_o½õî¸ãŽ?þ˜"Ë-·×ãijŽÛ”¿™€ ˜€ ˜@‡ `a‡+a&`&ÐGy„”²—Xb‰÷ßÿšk®¹ë®»f™eÌ9ÒГ’¾%¦ ©ŽD!±ç Ѷ*ÓåMÀLÀL ƒ°AØÁÈꙀ ˜€ d œxâ‰Ý»wÿä“OØØyÆg<ðÀ“L2 …zôèñä“OâÙã ¹æW]uÕ·Þz+[¹è;Å(L*R!ˆ¢ bN4Ds4JÓE|ÏLÀLÀ&VÞ2:±Žœõ60øüóÏW\qÅ#FÐ÷i¦™†‹9æ˜#Ïáúë¯ß~ûíÉ+8ÕTS]tÑEk®¹f¾LÜ4hЖ[nùá‡â<ûì³×]wÝx/½ô®Âwß}—;\à?œtÒIã©/LÀLÀL`â%`áÄ;vÖÜLÀêEà¾ûî“H·±ÊÞyçBk§uäš'ifÞZk­µÉ&›p‘‡ÅMQ€ S¥Ð¤" Ñœ¶b…¢ÊäúŽ ˜€ ˜€ LtlNtCf…MÀL Ž?üðe–Yæ³Ï>ûá8`Àvr–S˜nºén¾ùfÍO1ÅW^y% $† ’Vá+7yDŠQ˜*iü5Ò4  ÊqÄù2¾c&`&`o¸ÆËÚš€ ˜@íçsùå—üñÇéùÔSOMd—ÙgŸ½:²Ìo±Å÷ß?UöÜsσ>˜ >=öX.–Zj©‹/¾˜\ö\Wü÷Ê+¯üîw¿#Þ å»ví:lØ0bœV¬ëb&`&`&ÐÑØ ìh#b}LÀLÀ¾&@²‡µ×^ûŸÿü'·ˆûrË-·(Cà×%*\ý¿ÿ÷ÿ°9æ2UÌ<óÌÔ=z49$öÚk/î·'põÕW¿ýöÛõÓŸþtàÀ$½¨ ˆ‹˜€ ˜€ ˜@‡#`ƒ°Ã ‰20€À‹/¾ø‡?üáÞ{ïåZÛD·Új«ñ!óè£rPp̘1awèàÁƒ»uë6>IS±í¶Ûþç?ÿA‰ ÙM:çœsŽ@×500oŸ€Ï~ûÌÝ¢ ˜€ ˜@d–Ÿgžyd Rt·ÝvÛ`ƒ šÔ)}üì³ÏwTÖ ¹à+7K+5yˆJ(¦B$*DaÔnRÇMÀLÀL ƒ°AØÁÄꘀ ˜@í ì´ÓN½{÷fŸ'$fœqÆïÿû'œpÂ"‹,Ò4L!¹±cÇâ»ã°ÂÙf› Ë\ð•›<¢@aÅò›(ƒJ(†z3Ì0…QµÿøÇ?–WôS00èPlv¨á°2&`&Pk¤˜uÖYO?ýt(`h{î¹o¾ùæƒ>8ÿüó?ÿüóK/½ôHîøêŒÞxã•W^¹OŸ>œBÜh£þò—¿,ûÕ?.øÊMQ€bÕe¢j  *¡ê‘×þœsΑ²ØÓ…Â,Õ›pI00øÖø á·†Ú ™€ ˜€ ”¸óÎ;‰ÔòÅ_Pˆ„òZsÏ=·* ¦ÿþØZ_~ù%!FÏ;ï¼=z”ÉúêÙ 7ܰõÖ[ÿýï'm ž@ò fªÜtÓM$ÝüÏþó .¸ W¯^™ù¯C‡åd#F'™d’wÜñ¸ãŽ#8ŠavïÞý£>âë~ô#â߬´ÒJy ¾c&`&`Š€=„j8¬Œ ˜€ Ô”À€§NÖàb‹-†c0¬Aˆ`trÊ)#GŽ\xá…1ÆVXaìÃO>ù¤¬>ø€~d™ÇÄ*{úé§óÖ u¹É# PŒÂn¸!ɤ9¥i@ ”A¥°©…¸ ÉHÁ5¡;tª‘4ß700ŽB€³þg&`&`ßvWÎ7ß|ñ?E|n%š`h‘KPfØL3Ítß}÷å ãšÓ¡¾ŸýìgçŸ~¾@þÅ(ŒT¤z¾ ÑhP#_&îÐ…è{Jé`<ò… ˜€ ˜€ t4Þ2ÿ×ö… ˜€ ˜À·Màî»ïþýïÏŽPžl²É†N¤–¦Jüõ¯e/èC=DI’Ο|òÉl1åú³Ï>ãt߉'žÈ5G/ºè"‚Çp]åß«¯¾ºå–[*®i¿~ý=ôÐÉ'ŸœŠ˜s„%y=×ìegij¾6’üØcqÈðóÏ?§ÀO~òŒL\‹ û¾ ˜€ ˜€ |‡l~‡ðÝ´ ˜€ Ô—19?üðƒ:HºtéBðOÎòU$BõC9_FI/»ì2ìIŒC²b€í»ï¾l×$,MEi*ÆŸl;ì°#<ò_ÿú1‰³Ùf›‘£bÒI'e¿(ÚVÏbÏ6T’>ñÄ޶ûï¿õê-iîÂ&`&`&Ð6„m£sE006 (øg¤Ün»íÎ>ûì6daþm³Í6ø©‹ù‡E‡aI2À\° iªòÔSOm¾ùærÈÍe–Y†=¥í%'Ûa ŵxÇwhëiÛê¹¢ ˜€ ˜€ |³l~³<-ÍLÀL  ì7â¸h›(Ñ8È®Ñ&u?&* á[Þ~ûmŠüô§?½òÊ+×^{íÆÅ+=A¥7Þ˜¤”æTá!CX`J5‹ Ýzë­¨¤x9A$˜*fQAß300£Œ~Ðݤ ˜€ Ô“&i8Ý'k¤ó£GnÛÄxÖYguëÖ kpæ™g&¼'ò×Yg¬/Ù‡m@¦"Õ‚("–;4AC4׆@ªÐAºIg¹¦ãt²6ÛèZ&`&`&ð °Aø ´(00†påág#… J 3‰ã +”>xýõ× ÓÒ·o_ÎûqÌ03?üðI'Ä)D² ²9óºë®+Pð*T¤:B…@Ä"œ&hˆæh´ Z…[t“ÎÒe•›ZR¡ª‹˜€ ˜€ ˜À„%`ƒpÂòµt00\sÍ5œî{ùå—ƒ1Bwß}÷wÞy'îT¿@ÚB -4lØ0 ­›o¾™CƒŠJ8Ðgžy†P.tYýõW]uÕŠ&Å(LE‚A¢Ð±§ ¢9¥éêzFIºIgU7_zé%€´'-ÄúÂLÀLÀ¾ìñ?00˜@Hõž9Ô×µk×?þñÄåÿaSO=õÕW_]½éwß}·W¯^úŸßj«­Æ×ºE2Ŧ˜b ,ºÂ2q““2TŒûé ÑœÚEFí¦UâšJº¼óÎ;“ŽBrô Ea_˜€ ˜€ ˜À·Là{ßr{nÎLÀL >Hë7ýôÓ§öê”Õý…^ˆ-”ØHìŸlŠeРAÚb:å”S’¢¼êÝ»·’ÈÏ:묤§Ï»ï¾ûfŸ}väU‚\ö¤¤Ï(ÿšîY]|ñʼnÃ?.¤X;6Q5P (†zèá)¢kt0S@_ïºë.}”&| t……}ÓLÀLÀ&„¬Åš€ ˜@M :Tû*ÃÎÁ7jÔ¨r„ôäl¡ªpHÌõ”'¼ç!‡"OÚ"‹,Re[i£Vüë_ÿù?üê|åf£òMï£ *!õPU©‚ÚqÄ‘îЩr9Ï=÷Ül³Í†ø:–×òS00ø 81}ü_Ø&`&`ãEàóÏ?gå€R)XMØ]™“„i¸Æóvä‘GsÌ1Ÿ~ú)Q^öØc믿þÉ'ŸÄ‚Ûu×]>úhÜŒQ¸‹x gÏžøë¨û‹_ü­2nÌVerrï½÷ÖVO®»îº'œp‚”ßk¯½öÝwß̦ÐBù80±!9C˜>%Qá©§žª­é}_›€ ˜€ ˜À7NÀá7ŽÔMÀL Ž°Ü6ÜpÃgŸ}6íüzë­G Oí®Lï—\¿õÖ[[o½õwÜ¡23Í4¹b‡gIÅ’GXn˜mûì³Ï—_~©#…cÆŒá€ßQG…©9žvæƒ>¸Áà”«¬²Ê\ 4ô%*¥Po‡v8ÿüóÓ›óÎ;/áI±3Ó›¾600oœ€ó~ãH-ÐLÀjGàŠ+®èÖ­[Æìӧϵ×^Û’5¸ÿûßxƒ †~BÒÆV/FŽÉîÍþýû²e«­¶" ÿ¸à+7yDVeFyC½°¹òt! T¹À"%U=AhtRU€ RÀV‘à2&`&`&Ð>opû©E™€ ˜€ ÔÀO<1ß|óeþ'„óí’K.iÑ8ö³Ÿ!m†f¸ñÆwÚi'¹ï¦vÚ¶*Ûì°ÃӾ͹æš+ý…¯Ü¤- PŒÂ­ÊG%CJ¢* £6_éB>¬háHÈo/«Tw00hƒ€ƒÊ´ÍULÀLÀþK€Ì ùcr¿üå/ï¿ÿþV±‡s­µÖšâßk¬ñþûïKÂ3Ï<³Ì2Ëèþ +¬ðüóÏW”Œ½7÷ÜsS ÙYhïq“Gê…3cIC¨2ÒjÙe—EIFm”×}ºC§J„>züñÇüFBô‰† .,ï›&`&`&0žlŽ'@W70:à0ÞŽ;î˜-q½ûî»üñÇ-A¹ýöÛ§™f$ëå²Ë.Ë×%ÂÊÔSOMÿøÇÇwÜ?ÿùÏ|™¸óÙgŸí·ß~Ú~¹À 4 õIŠ!œ*T¤zˆÊ_Ð4  åQ Åòeè¡¢kù%w@ÇFÖtï(rôà`/©ëG&`&`&Єm@s0¨5W_}U›-ÿgªü߯…/\ ¦JÂ@ìŽó©þJ+­4zôèFdq¾´F%çœsÎ#F–$¹Ÿ9°mu·Ývã8_a±ÌMŠQXy䩎L}¥Qš–(nÌ|a:BwT’V±Á´`H4UO?Áü|s¾c&`&`m°AØ6:W40:2dHaœ $´<ú裑ËaÑEmd¶ \$pçàY%ªÐdW§¼y˜I›l²Éßþö·¨E|—~ýúÉ|"TLçî¨BEI@C8 Ñœ¡@ÅÍ¥tJgÉSOgCZþPà’|‚ûW¡î¤ŸÀgò|ÇLÀLÀÚ#`ƒ°=n®e&`µ#À^JB¤Æ‰®ÙßxÚi§¥8Î:ë¬8×·oß7ß|3}Ê5/8àmŒÄ"¢f¦@ÉW’4ì¿ÿþ“M6­ÿüç?¿øâ‹±Èã§ÙÌInÀ¶·VR‘êÚŠ@Ä"œ&hˆæh”¦Q D½Ì#º&KÎÒåüfWà€H$it©š.ÌŠÁ@”omM…øÚLÀLÀJØ ,ãG&`&`ÿG`Ô¨Q…ÛD‰¨yÛm·å1a®âï'?ù ¦Îä“OÎY»°…µà‚ rŸ-š8ÁZ²¯¢!vN.¿üò²£t¼kÒF|—(ÙÆB"óa§¹ö¶kÒAº©ý¨tœîK%€€8h(pÚxÇÿÕ¯~¥ž¦Ÿ Gˆj£®b&`&`"`ƒÐ3ÁLÀL  rHú©Ø ùôÓO—T~å•WHÔ.3föÙg'ÂÊÉ'Ÿ,QóÌ3OÓp/%’õè ƒŠ0§DûL7y6­[^Q”æ4ACåå›>¥³tt ˆäƒP%^~ùeåöÈ›AT{é=JÚò#00º°AX·wMÀL ï¾ûîꫯ.»%ó¹ÄK𴊬»ï¾[.Áð‡?üáÿøG•ºÊ¼óÎ;믿¾N?ýô2 gœqÆBwe#!î#QG,ÂÕÊl@£ªT¹O—éx@à,À©R÷£>’i± ÂUˆ* ¹Œ ˜€ ˜@Ýü ý?“¯MÀLÀL `«,´ÐB·ÜrK܉ Œ“aÆneŒ2qÑ£GöL†+‹|îl˜Œ­^ 4Ùµ×^;ÅS¸åí·ß&}_÷îÝßzë­ÕV[ ‰½­ÊTy*R!ˆB bN4DP¥éö$S‹.ÓqÙ®|X€SE ‡ˆ>}ú°LÉ”ç>ÃÄ`eîû« ˜€ ˜€ T"P7 Øý50hJàµ×^[sÍ5ý_ä°Ãk*! `P…‘ buþð‡?D2ù뉘B`Ò(Yå‚,¡F)zF-¢¿!P ‰íÉñ¼Š9'$ÂTQPP„ !œ†ä Csȇɉ’…t“ÎÒeªÓ} ¤L@TX«ð&J `~tP,RXÝ7MÀLÀL CÀ[F3@üÕLÀêNàÎ;ïT˜ÍÔä_‹œ8ʪO9å”ÂZcÆŒY{íµ%‡^y²‡@1Ò£"ÕÒ  P&}TrMé¦T¢ãt_…"D€*‘ytë­·N9å”Ìoeà¾Ly5006Kàø‘ ˜€ ÔŽÀ9çœ#3#ol°A´ºñÆ™·Þ½{Ë ZuÕUó.µë¯¿>¢ª¬·Þzå*IP–矾)S¼d±Énºé¦¨ò /Dî‡ßÿþ÷mù|ñʼn†ŠUÆ¿¥–Z*¶krÁWݧŢъ(ƒJ’€’¨éBÀ¤kyïh”Œ @ i  ãQzvàË<¦$ƒÒ«W/韂ÏWt`XÓê¾600<„y&¾c&`õ"pÁäÝMákÚb‹-ä#å`¤kŸnºé8L˜ óè£ê$Iرj¾üòËêxàE]TV ¦{SÏ>ûl…x¡­üfËê’)yÅW({ŠðÕ?¥‰ç&Z•)Œb¨‡Ú¨ŠÂ¨v&Ý1bD¦|ÉWpMŠ˜iaP\mÑ éŒÀü r‡ÁMåøÚLÀLÀ2lf€ø« ˜€ Ôˆn®8Ã&cLŸ *ƒq2`À€ Ž{ï½wŽ9æP1Ý)j Æ û3 ¾bvµ%l5ìƒ>hT¥ú}Ío¹å–á=クßH"{Ô˸éèÉï#­¢mjTƒTF5ãH$ð‚Œ(âÓÈxÖÙt(¹fˆÛp®fšðW00ÎJÀagY÷ËLÀš¸ãŽ;H‹—1ø*Ó‚Ô C‡m$‚€sÎ9§êvéÒå·¿ý-×XYÛm·]£íŽD¥÷¿øâ ŽÞ…sRn·–²G¤ÒÒëÑ£G¯³Î:igùÊÍ´L{רÎLä£<] #íI£Á(Û°à•Ú{#±=ô¼ :¸˜ö”kšánT×÷MÀLÀêLÀaGß}7¨)Ì•£Ž:*c3è«\LØMÿá¼"!áÏ~ö3UĹð Çè¨Q£^xaI[c5bï%α¼O¬zCè‰Á¦Ð5h‹Î¡67y$/\uiI ß £¶ôïÚµ+ÝIK¶z Ì0í¤vS=_ýuYæù¬!ÒŠAKµÕ.¸¼ ˜€ ˜ÀDAÀáD1LVÒLÀ¾1PéÖ­›,„̧¡-¿üòUvi¦)TçØž{îÙ4'D¾'„÷Ä«¦§¤gˆH›$Ü›gžy¤$TšÚ¨yÉ>ø l$„¬¶Újß… ¾J2.8Šåë–ßA™H.’¨ªò(OLwèTõÈ¥ÑÁ(7i ,LÝUââ“O>!==­S1öǪ›úÊзA'äûÂLÀL ó°AØùÆÔ=20bXAÛo¿½ì.Eª”©Àç~ô#]³Š‰@š‘T}ŒQÛxãe½à×âð[õãy?üðüóÏOëX,Ûl³M&óÊzè¡J’|ðÇ\ܽqï~øá‡{챇¬ ,´0ØÒRÜ”ñF1 S%}Úèš®¡†ˆ¡êeˆÑ:¢¦él$*sÉ “c˜ ,x•âàiÖLÝøJø™wÞY£©±Ö5Ÿtn2 Â6ŽŠ¾00¨'„õw÷ÚL vžyæ™ØÞ‚.d`À`4å‚Õ´É&›¨".²·ß~;ªàz"A…a½\tÑEåÇÿ8,G>=¹ÂØxyÿý÷‡¨Ì… N™X‡#†JyÈ–n¸AÇ#1ÛvÙe—’c<¢€¬;ªP1Ótú•FiZ’QF[Z ½¦;ÚMJéf‰Ô¸dcŒ©Èá~ÕÜô²cß©Æ%>™ L‰Ta_›€ ˜€ Ô“€ ÂzŽ»{m&P/¸Âdó`è4]:4Hbô*Þ'“(ÃBN>ùäBˆ¤^Xh¡…$ÖYg½å–[ ‹ >|®¹æ¢®°wܱ$o{T'«ÞÒK/-Él},4 Ÿ{î¹=z¨ÌrË-÷׿þ5ª—\PŒÂªEu„ä Ó\lµE*)þè]“ã”ÎÒå¼Xî€Pjt,,pCP%BÌ]wÝ5ÕTS!6z!2­™…ŽÓÂÖ}ÓLÀL ³°AØYGÖý20ÿ#p 'ÈØ ÖHf§¨îc4CJôþýû«ü’K.™ú¯ Acä,¸à‚*Ï!½‘#GF±O?ý4’×S¦‘ýå3çž{îL3Í$Él}|ã7Të‹3{Чò«_ýê´ÓNËTlú•*TD2B6*MÐZ¤ih**-@…3ŒŽÓýx –"J™FÆs”;ð¥ ÃÁ Ä£Â ²,f¬AÕÅ&ŒûLº¾i&`&P6k2Ðî¦ ˜@ üío[vÙeëJ_ùÔ–Â8f¶Øb‹5Š­Â)8wÁt$ z&}#²l°ÄØãm…VÀ«vçwÎ6Ûl´Ž¨~ýú•ï)m$CˆºÚ ‰o×Ù°aÃ";";9ßÿýFuËïS‘êB„@Ä"œ&¸Cs4ÚÔ+”O7©K—‘C÷ €¨!ª|lˆ>C Q J£Š e„iUáh5ÊFVùùÊ$aªD¾00¨„µnwÖL FØ ¨`$¬ø”E–Ÿ:¶ÇÅúë¯ tàÊ3Æ%–X"õæ.å裖- «T „ÙÜ{ï½Éj­s±È"‹<ñÄã9¯½öZD •p4,ÜGÚjC‘O/t¦!škUN¦<]¦ã!“ °§H¤?þ¸4dh 4ª Ã`XÜ>úˆ 7j7†^_cb0U¼}43^þj&`5!`ƒ°&ínš€ Ôˆþ¨Ã?< LX‘ð² 2 °q¿ýö‹ &Ë,³ &dzÏ>»è¢‹"‡½Žù…ÀÌÅ AƒägC6ÉAôÞ{ïeÊ´ú_;]C,’9øä“O¶*'_!q¤±4ACý¢yiq‡.Óñ0É –xÚê…(ê4 ÃÄ`1dáud(Ðt{êG¡¦3®ÂI'”>êÓ¦=Ÿm«Ê»¼ ˜€ ˜@Ç!`ƒ°ãŒ…510o€ùñÈŠþ¿þC¶Ä5Ú=ȹK/½4ßöÚk¯ðæÉ~ ÞÉ=÷Ü“/\ñÈÍ7ß\:à#É¡l¢\žqÆííÀ¤éG}TÆ*’ ³ï¾ûÊJIâ7>[F©.hDl„¨¡9­ØëL1ºIgå•î!\…Ài[[Zah&ìL†A,LÎqã7j›¨NZÆÄH' “§|™þú« ˜€ ˜ÀDDÀáD4XVÕLÀš8p Vü/–þ²ÙFX¾¯òé§Ÿže–YÂZÀzi;?Ám·Ý¦$ ¸¡È×''þ·Å_\ò§Ÿ~zòìµä|ÃÔÁN“ý3óÌ3cä ÉØ)Û†èšøô¾üòË&¼’ǦŠÂr"Q‘Ý&h…i”¦ m­DÒ8—tÒMõ—ŽË‡É}€È;"@S­ò"Ö¬¼òÊ1X ÃWR/¢NuflBIÐ a 1‘J„ø‘ ˜€ ˜@g"`ƒ°3¦ûb&P_X) %‚…„À…,¥ùæ›ï•W^)Átýõ×ËCˆlÍ5׌¨¤¢kÉq4f̘õÖ[O `R¾ôÒK™F‰×ò›ßüFŽRÑ IÎgœQÝaËkº%Rò1‡8)±œ²{衇2í~¥X¤zÞ¦¢!šChT­Ð)ãA:K—3­ƒ8Ò\@Ë(ùJXšq†‰Á’”ácK*¾õÖ[r®þÕ &Â[2}KZô#00ŽLÀaGëf&`Í p2À$²Üð8M3Í420òŸ«¬²JÉŸ‹n¸¡j­µÖZ2N¸¹óÎ;‡‘гgϼi—W—šŽ#²IòØc͈;W_}uDÅ+IøòË/Gülå1iP ¬Í^½z•ØÀ<¢€zM•ð7††é†oeP)}š^Ó‘°Ké ÝLŸf®A¤Ý¤@+W@Qc³Í6“Î ¤M§ §û ¥nfÚÒW¶°Ê\×ö]U‰O¦\—L*¦ÖøŸö,ÔÁ7MÀLÀ:„d ¬† ˜€ ´C€Åzº½3’Åâ>.pø”ìÌ$Âä 3Ì@a,“|¿wß}·oß¾2(³öÚk7²ÇÞ|óMŒF5ºêª«¾þúëM{Å^M6O†‹I“1 sBî…ÆaKgÅ „î$ªŠLYrîyä‘w"_¹©t|£p•hŸÊ¥¡¥¨„b™(,(V¢kUv® \â@0r{ê©§¶ÜrKc8†&S’á“yÉ€–¥#‘XRÓÏt"1Álf û« ˜€ t&6;Óhº/&`õ"€‡*Òüö·¿Mñ™Å}ÞÆ RŸ}ö)òTžà¢%Î4PÛm·]Ä,å”öIÈáâÊ+¯”>ìZüóŸÿœ>jzy®H:BXv6R‹m–óÎ;¯ÔÕ÷Î;ï4•@Ⱥ뮫êlõ ÿÚzÊ# ¨­´bù5j„_õ´!¨­Q ‚(Ýɘ å2y 4í×#0ÓòØ™—C!` Jö—2ˆ ¥zÍà2Ä©¨ôúâ‹/.Ã*/]˜c‰Áôˆq æ\h:Ñ:Lzà&¯ÊÆÚBe|ÓLÀL c°AØ1ÇÅZ™€ ˜@1òwéÒE wÖñ«¯¾º®e6èZŸX>Ѳ{`‹-¶«(šÊƒÇÓ]vÙ¥í%þ½÷ÞgÿhtÛm·}ñÅ‹õ®|—¬ á ûm¶Ùf+9×T6Öo¤¤ËüC[Úq3s°©¨´*¡˜h‡ª(¯ÄiÉV¯ÙŸ LIæÈ nUˆÊ3¸ ±ºÌ GU&SBà))âåD»ªÅWž†ÑÈôc¶§Œk™€ ˜€ t@6;à X%0(&@:0 ì©§žŽAÁ‰Oâšj|u‡ ™SS\ÀLÀL c°AØ1ÇÅZ™€ ˜À׈2묳² M¬Ñ3¹æšk¾®™\‘ O¹ ÃçÃÂn¸¡%§Ó_|qÒI'I™gž9¿“t„ß”Emƒ3ªé±7lòX¨#¨ÔÈøÏþiU•“o\Iú7Î%†Ê ?U°Ñ|œ_}á&$“ÂT)±phŽF¥'UP•ò2¹C´#—Ât-otej$iŠÓ:æ"‚à>CPØŒØøÊ3С’&“¡QvÄ[n¹E‰+ÔÙÂOͦeI¸šPÀ&`&`™€ ÂŽ<:ÖÍL îpŽ~úérËÄqµÂ:7§›nºGy$ŒTu‘zWû98âˆé§Ÿ^ræž{î‹.º¨ÊQ:œc±O’Äè%9î‰;ºæškÊðÀr Å_a`Lö(² R©±@ˆòÒÔ:Å]F˜MY#œdùˤ_‡Øå—_®“Wè™ÏÔ—ADŠÉˆ¥"Õ3Áfh‚†ä»£iS4#'ýJGèŽÌ*:H7 ³þíoŽ<·àèR9é5À#=QÅMʰ2¸ ±ÆšAgè™á8ebò!¡ˆþÑh²q_’ÉÉÍKÕöµ ˜€ ˜@'`ƒ°ƒÕ3¨)ŒãŽ;nÚi§ÕŠœmŠáÙ“ã+³R'ÔÇèÑ£ó°n¾ùf E O9å”^xaÀ»EÖ;+ãéL3ÍD®ÂFÇÞ8ŽxÔQG©].V1gÆ/©Ë/¿|á‰>ÜV .¸ Š¿¿Ö|ÀC=$eÐЗŒÆ´JzM4ËUVYEòq…ájc?äî»ï®aÛÜvÛmiùê×HîÞ½»$cÄ9¶VžwÞyÕå¤%©¨™ˆE`ä41ZÓòU®é L8ºLÇ iùEi8­Jfdh T †AT(ä3¸ q¡’)ÁÄ  ÿ˜*ù3¥›nº© ¤Ÿ1 Ñ!=˜Š2LÝT_›€ ˜€ t|6;þYC0zøÇ?þ»CçwÞ°OX‘‡s&]oµÕVù {8¸d„`L²K0_ eÊS‚FFêvbûì³Þ-49è ƒ´—’ ì<ð@Z«¥kö7†Á&åé á4åÙkIT¦0±a†}²ñÆ£v¦LK_©Ž™[¨ŠðL4𖤩0݌ء1v©²í³Qs ƒ‚4ˆaBm†Œ“?“û (ÃÚtè™:¾È„‘_4Ó"G+Cç¸H§"S”‰ªGLÝñäŸiÝ_MÀLÀ&4„š°å›€ ˜@ žyæ™_þò—Z[÷íÛ÷’K.‘eöI¬È¹àøÖ€2ÒÙCHÎYql}¬¸QBØŽù82§h+´B0•t·a¦ÅŠ_ ãÙ­[·Ð/Ó1Ç“ñFVÅGÝ"ì*™KNâEÝ *æ³ Ò –¯x¿ ¯ @Q1®iI C£#¦ –N9"œA¬¸­W™$ÚjË´aòä·¡^uÕU:·狘œLT¦«n2™Æ%jû‘ ˜€ ˜@‡"`ƒ°C ‡•1¨5¢>ÊñÂ:û‚ . ¦VجõóËqʰÍ/Ý„”èJ[‘Я_¿*¡bòÄɉûP gÏž­îiÌÈÄ?vôÑG« Dméß¿?éõÔ5ÜYÇ|û Ÿxâ‰å–[NBˆ°‚ÎÞd+fl•$Õ^K…©"AqthŽF3]kú•H0tð¿ø…$Óqº¯˜7ËxúHHÂùdà†ÞT«|¦ F¶%Sˆ‰”–aš‘#ÿW º ƒˆ4ÍteÒª Ó¸QÓT¬¯MÀLÀ:„a¬ƒ ˜@Ý °àŽ\s¬­YXG0ü-ù…xø‚p‹a`<÷Üs„µ$jˆöþÍ9çœmoï2dˆ|n¬é9*ÆÒÃ?ÜÆ8± qŽ9æ Sô‚iá¼ÿþû_|qîó‹÷ä“O. [š¶ûÞ{ïaœÈŠåF&À46)ápšÉ[HÒ9¼ýöÛiõü5(&T¤zzšá4!;“24y!ù;¡SSM5•úHg鲊VànÔÌ ÌÜa8"¿|:X ƒ˜)\ñ+ӆɃÂL$¦}gj1ÁÂûZø‡‰ði3i™ºê/Ÿ‘£²bë.f&`&ð°Aø`w£&`&ð5ìô à¾ûîÁ?§žzêX^Ç^& Èk¯½6ül< ÏØ–[n™0óuc¯pÓE64Ê+HPÍ?þñaà%»óÎ;Ëç GÚ6Úh#©½ÐB Ú¨÷ÜsOœ]ÄvÂI±qÄKd‚¾ÈÄÂ"°J#×"áLiZþ.ö¦bÑZ›Üä‘B¤P˜*iÔ´u¢9™Ç(€…aZT… /t$ÆŽÒÍTš®ñ¡ipåËÞaÂ;ÊÐ0@Š}ÊŦ\†²œB™q“ÉÃ’V1©øÊdcÊ1:«®ºªž¦ŸQ’©ËŽGLìöÌÝÐÇ&`&`š€  MØòMÀL !<0§žzª|SúÜ|óÍ#l£òÈÇÚZk­µVš”œ3oK-µT”aýM½6v!Þ}÷Ý3Î8#rp í¿ÿþk‡Tu{ï½wì{lšÑž~þùÚLÈÁ¹ƒ>8Õ9ƒÖ#]v™ò±L1Ÿì%ér•S‚„3#‘ˆ½øâ‹c-| ›bù\ðy=SÚ(“7q‰§‚òÚŠžtŠ®ååİGg Á4ÐÅÓÌÒüò ƒ’É"ÈÀ1|r3 å­gäë+“‡)”þ…"C5vÜqǘrqÓ• Ì4æ~Ll&yI¿ ÕðM00o€ Âo µ20q60ßÉI¥­•ZaǦÐXpsAª·T¹ìÖ^{m`§_äšÇÎ!ñ@E #µñTJÎÝáOCl´Ò(£ýÈ‘#ÃOEžºê‰ûî¸ãŽÈLˆMuÎ9ç(^åK/½ÝÑM7Ý”BhzýàƒF^ üW·~õ/|°<¢@S!iˆ‘B1Ôã)ñ]PX):BwÒZ%× Š„~ {ôÑG3…1_3ùåˆBŸ§*2ˆ‡Á­¸Ç• ƒØ0’hm¥;t“É–juÖYgi⥟é¤"MlÊ0Õ ód¦2}m&`&ð°Aø`w£&`u'0lØ0m}Ä„PÖ9XäT)<4˜1„(Ož¢K/½ ¸›°IÂÆê|ûí·/÷¤«ð¼q„l‹-¶ÐºŸƒvd hïc(på•W†Í‰’XYdQ§m\Œ5*¼pê5_¹Ù†¨¨‚g/lWd¢0jÇÓ6.€:T&Hãl¨žÙÊÛ´ †•ÁUîÌ I¦#N¢2a˜6ù}°L°È!ÉÄK=“„œ‰ý±j%>5™5±™äLu=bò·±¥¹iO]ÀLÀL m6ÛFçŠ&`&Ð2§žzj`eŒ[‰dâk®¹f, ÓS[q“  ¤q2 O¢´õ8gHmWâÓºP.Aä|’m~ÿûßQš|åŠ ©¬-wãhQ…V~ýë_ã8úßó6ÿKüÌîÝ» Bì9Dòe—]‡«‹~õÕWwÞygIƒÆ_ý ,<¢@ui*‰÷ ef™e–Œ’¨Ý^,ÖTÒYIæ¼Mó˧Õó×ä½P¶zQ`0N–& ÁT)1¹h&›üL¿4FÎû￟N€P›‹tJ3Õ™ðröò ð"äõô00ï„€ Âï»5¨#+®¸BÑ>Xï³Ï>n¸¡VÏx„"Ù<*±ª&ØI¸SØ«É6Åp¹”ï ¾8XÊG^AöÆù7œ6Õø…Àô‚“'žx¢ìùµÐœ>÷²U—£ÄbúRWÛg˜a´pϘ¬h„cƒá…ÓnÆT“ÂklÒ Êásë­·Ž¤\ð5QŒÂ…B27ñÑ¥ÞKC=Ê * £!ÊÓ…Ô†ÏH(ù 4êj’ JHÁ ä6,á´!:|tLm%¥ &Ó£¢;—)m¦"RM [ú§ ÄÆ4fbÇÄ`Â3íešÒG^‡TC_›€ ˜€ |Wl~WäÝ® ˜@`~DÒs–Ëé?h­¼òʺ#û$žn²É&ÁˆÍڟɾ¾=÷ܳ¢EÕñð`ÆDŒšÀ—õÐCE6.†:ï¼óJ[2Ît„h(tSÆz²ã±zF{lKËÉZ€Ž»4ÿús¾ùæSs8©HP²+ MaN±ÐÖXcB÷7y$û“ÂT ó;„ –4 Q•P,J¢0jkéÝ©~âPàÒ._‚˜ ì” x´ÕÆÃ-sN éS"Õ¿ŠL&ÓOz2!Ó=·»ï¾»$ë3&3Ó[§Ó§º¦›a¢WiÝeLÀLÀ&„‚ªeš€ ˜À×î½÷^…Ñ™íy¤mÈ/Ž3O?ýt‰À!¿¹êس=+î•W^‰c`N‹Å:Á0¯¾úêêv‹TÂPa‡¡º7TqÙ»woE(ÁÖbÑßTg6I†¡ÅѾFÞEPìjOi¸ù5ë±cùzÝu×)…Ű{Óã—iɸ¦@ìP¥"Õó2i(Úå52eBÊÇaE:Õt-p@$£h ËzM o°g¢}F».\†8·2ô:ê ¦£QÅ’ûè,HcrŠ /¼PÑe¦´n2ùejòjð‚”´åG&`&`š€  MØòMÀêK€…òI'$™ \+±{“ÄšRke}R&2`]DšÁ?üáJÃÐPùÖ´4'BÉUW]Eu’×íµ×^Ñ:Q(=öØ1cÆ4•Œ4ÎÎ)>*[ÙØh+#{Ùý[e´gâ*«¬¢ŽÜrÈ!Mu€*®¼ØøŠeB°Pyº0-‹ˆáÑR> ‡¡ŽY)ˆExȤQšû§DU:AMé`áþÞFùåób jmÍ>C þæK¦wP†UQd!Ìp3èÊ[È4P¸&FÆÏ™J(¹f*2!5pLѰá‡{DõTŸì§L¼ò1ê¥àá5©BµD?20h›€ ¶ѹ¢ ˜€ ”À"’-‡Ï„ò‡hK$Kä7ÞX±a¸Ž'Z:¬’¼äŽ#sº÷bäöÛo/k¬Á3’­G INye’˜cfœqÆáøBCL¸^x¡°±lk\b‰%¤$iåÂhTžû´˜ÉhOP-ý?úè£ýöÛOÆ.££Ž:ªd h¾ §Ù/€טmù*UîP‘êê#Ãz× ´ª!’7ŒnÒYºŒta|R Ÿ_¾PU€GâJ¢d;.ƒÈPjÊцôdB¬@IDAT˜Î˜î Müc’0U -¿É´Tü&*Ó•IË üæ7¿@}ÆôfÂ3íu3öójÈ©ÈË2ž'ZËUõS00Fl6"ãû&`&Ð>œ?²X.ã‘—F®BĬ}u¼ëØÜÈ5þ´ðÚÅÖ»öÎYau°éT>%œ3™ r™Ž4/V¨·üòË£êƒ"è!"e¼!-ˆ å_ITf´ÇÊ"ë Ó(gç*fNϷ‘¶}÷Ý7"‘âwBZ«,3b©Çù„ÓDÛ2éÒÄ–.Óñ02ëòüòÅôøŠ^Ãp0(i<†ŒcøP›4ʰ¦Aó™’ÆTa´á¦KÏÇÆ¤¥ké~њʴ¿P¨0¯/ˆ ðÊ ^Iß10˜ lNP¼n&P;l¥ëÓ§VäË.»,Nˆ©›¸kÂôŠ…2¸Ïr›#E¸îËaX%1ý#?Þlðá‡V©ûòË/£yø0Ù9`À¥°û1¦³K0MCWEl”QF{™BØ|0 ´zAV=|kÚ|±°@8/×ôä^£¶¨¾;j h‚†2IüI(¼O7Ó @`L#Jga•’› ¡Y„Xˆab°b-ƒÈP2 %BâÓƒI¢aÚ´”B.Áp2#bL`¦1Öi×®]%–Ïè>j§åyÄ Bt^Fó6¶FGw|a&`&Ð*„­sy0hH€]|:oFT}ü-¬‰Sw ë]¾ÓÂ7rŽó•Í{%;Þ$=e}Lð°Ùþ·ÕV[哆gTÁ‘u 'h¯ KpÒŽg 4ýбqÄGĶIyQ©[·nĽlZ½¤¶1™Ô»°x€—¬ÕP“Ø?tS±ÖÈg€µFô2(„Á‰5Rî˨Já°¢‚("á²9iŽFi:S±ü+]£ƒ1ÖÑqPŒçÉ‘#G2(âÃÄÀ1|míLÙlLºÙÔ#ÊTdBÆ®T&*Ó5Œ:¦±2^®¿þúÒÏ8Íȵ^„øË¯ / ¯ŒRnð•lˆ-î§&`&`­°AØ*1—70b—^zi,gŸ|òI Eæ7ŒÕ3 bm(ÕZ™xý&¨‰cxI\mài¹æškˆZKg¼+†=öX—.]$“`•­š.i¯°…¶ÜrËh”Ý}›o¾yÛ!ŸãŽ;N;é¡M¸Ã9º0Àðhá‚{ë­·R ¯1T8«öU=ž{î¹´$y#°ˆ"&MÜwß}iü5âl$©žI>A4$°4Mí%Z¡;t*<®hB—é8Ý—) °´í c8”Ø¥É`1d \¾ƒï`F2mÔM&Ó)_1ã¤Q&'S4NW2uÕ;&3S @L>ùË_Ƶ^½Üäe¡0/NüI…*¯€ï˜€ ˜€ |ãl~ãH-ÐL vÈiñ9p(aNð/âè” îò5ÅæFÖÁD1¡0»ìT`±ÅËX8¢‰7‰…u#‡!ffŒÜDì6lØøŒΟ…ZH wz±ú꫇)ËvJ¬šLx’ò¶n¾ùfº\k­µF–GÕȇ9M(”’X5x±"` IðFŒ‘ŠJ¯±²È5?Í4Ó¨‹/¾xáÞTnÆ‘6 S¥Ä<£9¥‚D&j”x_éÑ_(L3#PH7à€(U¾üøiL††ŠÉÆÀ5u#—ËGUm=e:1©ÂÂÌ»™…NN&0Ó˜Þ1¥™ØLïsÏ=WåS ^Íù8FK/(Ì?^"•çµâå*WØOMÀLÀÆ“€ Âñèê&`u'@š8E‚a½é¦›à‘‘U/=@¨/a'°ä½õÖ[ñÃ(<&Õ ^ž–B¬…Ã~ýú)N îší¶Û®Ä¤)”™Þdã®»îªe:VA„6eçä{ìö;*O<ñĦ‘`ÈZ{ÙÀIB‚´­ôg±säÄTÀO•Ù1H؉馛nJ«7ºþì³Ï<ð@E¯6±|yäæ‚L²:(@1 7’“Þ§i¹°¨›ßF‹Ú(/k‡îЩ·*@â” Ò$ïi‹q p°ÇžX†ƒA‰“ – 9†Alun´ÂSˆ‰¤á`jÑJœúãfÆ%˜VŒk&*“Y¡`z3É9è(Ú|êEЧnÆk‹£7ˆWIÕy¹xÅB²/LÀLÀ¾q6¿q¤h&P¬zÙò'ó)»\h%ÍEøm¸Ö¡)­qUæñÇg•acp$¬:89 õ‚@‚ûŸ}öÙÕ%äK^{íµ2ùp:adâ¨É”!Ê©§žÎCô…1HHiÀ>Xq@fš»<#3ýJBvΞ T±¦H€NÖøUW]•òþB¬ÕH˜l%åCœÞÄŠ“ÿ ÜäQ«{kQ5>ê¡d&¿<Éä—O{׈ްƒ h™Ô * dP‡Å0ù˜4 'w.2Ðh¨ ¦SdŒüF.ÁF™ÒóÎ;/uSˆa-Ç«¡!…2ñ·ƒ(ÃMýc2ð¢•ÿ­¤‘¾o&`&Д€ ¦ˆ\ÀLÀ ¼úê«qðŒe+GÅÎ9çœptüo)ûßÿ²öUŒ–Xéâ$¼ÿþûÙÇÈS»¤"È[_MŽ{‹ (±2š+9a8níq¾‘6=‚ßpX®©%ƒ—l…VKxíµ×Ž-‘®ƒƒNHÒqº¦ä{ã´Wú[—ý–:‡ý"]Áž{îÙ¥hŠ'øî lj·Í6Û(ðI”iéeP)Ò‡HUÔFù”%ÂA(é:ÆEÀ‚WÂö¦ÞQ†/N<2¬ nIÓùG™S‚1L¶–‚ôH2”蚦 žir½¼r¨FCºàU‚CœÀä&¯/]^aß10O6Ç «›€ Ô‘Ûóä<Á£Åî\°ä’K²lÕ×8tǰµÒÅ„ K¸ â:¶‘ VÿþýÕ)Ø”(‡a£†%#Äz˜áS:묳J g±µP™q[”]‚óÌ3º‰Ã§©a™_Ùîˆ3*åF798תo0⪊p—tVýEOÒô=óÌ3Q¬¥ ”A%SùDaÔn{¯&¸ÂKF`ÆFM ƒàÕ5d(åx¤³ q[ºÑ)A&˜ºÉ”câµêPEg&¹B˜2í™ü©ãQÜ‚¡†Fs›Š×ŠG|U_¨Û˜«£pI00r6Ëùø© ˜€ ŒC€“fx–´~eÁªói}ûöUÜK94»Å:ž±Øå‚GœdÓL”V½g¨Â‚X1Z؆‡‡*+¯¼r˜RåÃ{ï½W>FÜÄ«lÏ’¡ ‡rHl£kD’¼ð ǡVù v –L ìÆÄÑM$Ó‹.º(írSÁìËÝxãœp&»í¶Ýäß’ ,…| GÔk õP2"Ê¢<]¨b€Ê]аàmcž œnFRšá.l1ãdò0…ÒÀ¡Ô¢ËL6íæeúµa•Ñ…°Éy≡‰7Eô*ñZñrQ†Mpáš°âÏÂþú¦ ˜€ ˜@†€  50†û!§¹È™Ö½{w–§DÝCC±FtÍ}–ò±ækú=r$ûí·¶Tô€%õN;í$9‹.º¨2[›w’ >IF„ÈEŽó§$\g¡üô&þ",™ aˆÒA²ˆçÌ[œˆ+”ÃMâm*Ô* ¼Igó%Ñ¿\„´ÁwÄNHœ`ù’ŒvŽŒ rý1Rï¼óN¾˜îðˆÊ‘HëTÌ8U1¢9ù)†(Sˆå#v(*‰­*ÉÀ‘þ¸€dÐŒÀ©zdP¼Q/Êï£'-CÏ`„‡³Ð%ØH ¢˜~šöLÈ‘ÊÔez£u¸4Ä¢'/¯ž†ƒ—1ýÓ‰jñÚòò6k00H Ø LiøÚL îؘÇz:–¤,v‰ª7¢PòˆÅ=>¨XÙkß /µ¸àt‡ÜZ¥Ipž¢:A,ÙmHF»¥—^:ÖÄDeìׯßc=V.ùî»ïŽñd¨rJ‡áµ×^›q² o—]v‘ •Ž9昒¯%*ÝvÛmsÌ1‡ø`ɰChÀ€ É}L»>}ú¤†M#iìää›àƒ…\×ÝgŸ}"ÆÏ"‹,RžÑ^ùå)&õ¨Hõ 1š !4P[±‘†qŸîÐ)Y­§³t™ŽÓý0çÀœ¨Rý‚`8tˆ•&+v£*CÉ€V/:9H˜6Lžr5˜~LÂJ[LQ&*Ó•I+•˜ÆLær9ù§¼2ª®±à³K—.|ƻƯ^X×<âÅäõä% »”›¼Â73çuð0¨!„5twÙL ˜+]e<ÃÆÓúòœsÎyá…"kͰ4¸æŸŒ¥Œ§‚h­nòÄ•žÅÊÄÆÄ :ì°Ãb¹O»sÎ9çQG•ßÄH…iC¨’6ŒRLN¦arpźýý÷ß/¦Vz¤‘¹nÞyç8p`¦8Ögêäó¡_lÿ»å–[ Ó÷‘Ån¯½ö’ù–BÎÂe¤ÅWŒBžD\Б¢ ã_å+7ƒ*…©BÅ’¹ 9•Yˆe‚2™ˆ *òt!ö1Ò5:H73Ò@ÿΡï}DMüLu}ePštk%ÇðU±W3™*þ‡)”OÔÁdcÊ1ñ¤3Ÿ cZêlmHcꆋ»³¯¼8½V éåÒ‹M§¯!¯'/)¯*Oym5Cx‘ÛCñ… ˜€ Ô‡€ ÂúŒµ{j&PFà¼óΓ3‡sücq‰Ÿ‹\vZeþþ÷¿_mµÕbUZx󤬙Ü3¶íá8ÒFGÖ¾œ’Êùúɸ9̦€“j}ùå—Gs ä°Q–Åûí·_ÆþùZJ³+lH;Èê¿ä„a^$Ùü‚‘KŽ;î¸B3O1`Ø1«üYgŸOä=3f Ý‘‰ŽñCF;éÌ7š¹Ó(£}y~ùŒÌWšFÙ`Ì ÃRUFm”7º£}§ ñ `Q¼ª€«0b”O/pëå‡"„k㯒̄ ÎL$¦“Š©Åcš©S|2ý˜„åyá™Æ2ä˜ØLoä¤Ê7½æ%Šæ /`ÅËÈ#½˜¼¤¼ªòíÿ÷íýêýåuFó¦m¹€ ˜€ ˜€ BÏ0ºÀ¦KïÞ½I~ÍBs–Yf!1€£ZâsGQUd7Ê„K3fÕÉÖ]ñè N§p%‘޼0Ÿ[~x0$ˆòÏÙ6ü¤]ü'q ‘ƒpm‡Ö xcì˜m¶Ù®»î:L5¶PfB’¦' óêQ…Œ"ÃJ}Ï=÷,I¨Vg›ß±Ç+'-¢w;í´S Æ–ÆÅIë–\³m2Íh4 ¤ ,‡FùåKò5Â5‡¥Š’ü‹á  t¤â®Eà€HwèÊý{ùS‚ gö,†ŒNñATÎò^>eòÄYJ&U¸æèSމWbا™ÌLiéÃ$/q禵xqx}x‰dÿ«:ŸšN8½€¼Œ¼’< œVw¨Î‹¬º¼ÚyogÚ¢¯MÀLÀlz˜€ Ôšq§›n:ÖŽL4h‘EXz²-PÇÛXz’r€§lÔ1Bù=âP“ú”]³z&%÷­·ÞÚÈÂJùóŸÿ¬UoÛ¡ÿñtqv+ÌšfÅŒWŠôå­údГChÒ!ؤÿŽ™¶ù†y‡á‡~ÈB9m@·ÕV[U4q£!.ÐÿRšž‘~±°Ü%•J(¼f{çºë®« Ÿ䂯…{> «ÞD¥ÌþFÔFùVá#Pà’©@03m4ãDÿü)A†Œ“ÕÄP2 ¦_*9½Fs&SHB4™™`L3&[Z²âu¤ùä,=‰tÏ–³ðpG‰(¨ˆE*B„Ìãlº‹³a`ͺÅ[p`ì“O> Uð/ÅÉ:R®ÅÞÈ(Pñ‚0Ê{˜"i 8Ü&$…Ë2,”Œz‘?ƒ ™SaiÐ"K!0AJC\‚©>é5ÃÇ jr2¬ùs›iá¸&¼mTg:1©Ô5ä0Ù¢pKLïHÎÉ´O}¼¼¨Ç ¢¿¶Hg^^"^%ZáµÒÍôS/`d…á/)¯ªæ°^^^d^g^jmv¥¼ìíÅtm©³.l&`#„ã¨Yg0ñ%@ÜH™dÄü`Þ\: ƒ%&;ÍØäç»(¶í¶ÛÊ ‘®M¹f­É&·ŒB¬ìY^ë,“Êc{°ã+âÐC•¶·µ½ÈfC 'Ù$™ŽDÚtN¸3]Ö³næ`Çð2ê+7)9”,ß ä0$y]ØÆa6ã1«žw1êËÈ™?ÿùÏ¥~øaY¦—ÇðÀ]CìJÜ™E-9ÖZÍh'=ñr°õN;ú8xÆYµü™.Ál™ÃñÆ btaNÄÆ?ü'»îº«,:ÌöÊ*‡T³/œ[‹ôÁzáôZE«2•MlÏã?>Œ01.*F¼Üeáze£ çÄJŽ¥‘_>°a9€âÙgŸM[Ô57y$ûý©BÅ|1Ý¡9ÝÂ(ƒJ™È=¨6£C§Z7K[r †)ˆb@u#ÅÝg@VÙ® 4çé˜LúSL›ü4`‚1ÍtÊ‘‰ÇôkÏÕÆ„WjA½;|òRðjð‚”8¥é¯˜&jTÔ¯d$ºà/,¯-/o¼k\ ã¢Õ¼ø¼þˆù¾ ˜€ Ô“€ ÂzŽ»{mu$À9¥Ë/¿<œ~ø[b±~þùçk‰É²X‰×9`¦;Äâç¨X˜"º©O©Ã‡¯‚’d)ˆuªª³Ç劶¢¿‹†0"wßZk­ñ-é@»—]vñÃæÑáFÂoh}Ï}ŒœàÐHNþ>±e—]VÁ§D½ÔaX=$)‘WN>ùä0¶_|ñûî»/ß\Ü!/9žŸ0ØÈñ°Ô†Á¤ Ű^ˆõÒ4I:(§æ¨Žhá4a6iP# ä/è¬\:X’Í"ªçO ‚°áïE1°GùŠ .C¬ À 3ôéQ=î3=˜$L•rL6¦œ:Å$¬nö3½™äLõ0>%„×—¢i»ÒŠ-]ÕõÉ‹Éë 3xm)Ï+ áÕ–8ÀSµøà§ Õ–å|üÔLÀ&^6'Þ±³æ&`­Xc5Ò¥$)­Y|ðÁºO,Ma„5ÈÙÂt™VgyZqML:Ù“TÙa‡0?N9å÷a¤±2f«Û¹çž[â'áXZ„î$\«A2¨ŽY’î ¤/lá#c^kÇŽ%ç…P´zá%ËŸ0ÄéÔˆN*|Má`ä¸Z¹¯5Õ“H˜äÊ“M‹&œÄY‡UÓµkW)†SW;9ÓZå×Ú›ª¼A«Ð²Ü¡9¥ér9ñ”îÐ)éƒÅKgÅ Â÷Èûì1.\ŠüéUY’š¿)DDsÌ1U(¿Ûn»•XqÄZ$y7ËÙtý*ÎСC#T ‹ì’Ss%ú`ätÐA²ps‘Ò§Ó=÷ÜÃvÄð;Ñqm,4¥pmx≊ßHÉ 6Øà­·Þ*iQòCÖâDøˆ1Ý»wo;¬MÈÇ‹{þ(Z9rdSõ P‘êáÔ’LšÀ¿WÅb)”ÉMºIg% K´ÒÈ%ØH÷Á|ÍR†ƒA)ô:2ˆÚ*ó™f¸t†ž ç* 01 G¼D=b*†ÕÇe¢f\ÐLff;¯›ËKÁ«!—¥Š‹˜W¯ÐOÈ«Šp^Û˜ºˆO^öØ Ì@8oùq¨¾g;£¿¿š€ ˜@ç `ƒ°sŒ£{a&PL€°õá‰b«Î+•»âŠ+”sO+f|/éÞ6-"cGb¬)¹¨b ²ÂÆó £¢K—.<æ5æPÓÅ_ÌI­4²ôÇC•/ßô{çç¨|nè³É&›dÜJ˜ ."´Å:^ÁE8„ÏK.¹$r”î¯ýkÓvÓr’).µ²È‡»Ò¯?øà²;„5‹ nØrKae‘[‚®ÑÁ*2Å!ÒQ QDl¸×hŽFiºŠÀÂ2tY©4£–F.ÁB ºÉÄ™F††ŠÁÂæaøâ/ «‚ e80 ˜ ¦“„©RÒb£GLKý™@=ⓩˆ[õz¼ ¼&ÔE^œ¦j#›0óÂ"—šWÉzÍ¡Á‹¯¾0-µ÷•§0TÚ˜FÝô}0èÜlvîñuïL ¾XÅžÕžþ±ÄW$}Ö©± T±L¶Ùf›“N:)|)xÒb¥ø¿Úÿ÷_¬Á̶Ï<_V–:¬E‹9l/#'µ”z;U`žyæÁ ©î$DÕH°ä’KFÆ…¼ÎÜa;[U)&#vQ`ýõ×f`^aÝò› 1EBNlY„OÉ ÃF2q‘A5 fKÞpà ©1ƒ·§ÿþAoä 'œÀæÌFyDpZR‘ê©Ëá4!giª8HÓA—ž 3Î8#pÂkVizLE3¼ VôšAd(ÐÌ.ÍŒL¦Å$©ÒtnGu&!S‘ ™ÎOÍ™VO·J&¯ T™áõijž5Ú;Ju^^Éáš—šW›œë\Äë/c•„(I~.2fsô×&`&й Ø ìÜãëÞ™@M ÕZÞv—Éx8ûì³aÁ}%'¼çšXÂ1BÈÈŠƒv<Íü£Àꫯ~úé§ç³¥!œ¼sd9W6¤=õÔSmÐ'+{ ÁmBpEò àÌ o8EœŒ4Áw¦!„l·ÝvÂvG¶íe ”|}ýõ×Y‡ý†¬Œ™Â.—Èá«|6.FLÈóµð„!FJl6µ’É/Ïð•ì0d/âi§Î=\C;ï¼sfDøÊÍpR˜*%;{iNs&œ‹«’ÑžNÑ5ŽPj,°Oˆ€"— (B=hóµ k„Aah¼¤D1| bùФO™±í–iÃäIŸ¦×L9&^zþ–iÉädŠ2QååC&p‰T`暉œà-LäH—yy ã/b›~êŽ¼àš„ ŸâÊ0•øžŸ*ò¡m¨ühè~F150ÎMÀaç_÷ÎêH޶±±rÕ™¢¥–Z ? q2†³þóAôHÖ‹¬å dE¨8õ©Ý•.1)L8DvÒ}ÖÐlo»ûî»u´ LjZ``°h޽©Õ€loçœsŽ4Dz¥B¸&Yþ´ÿS:°úßwß}Ùq.2ì\"ê>š a«'ßXô³Ú–|VÏÒG_93vÁTÙ3‰¶ìB ËG{CI1ÉŸ0Ķ)Ì9Q’_¾)Þ›o¾3 lv3’“€‘AG XSQ*P1£=I]‚™À¡°„—£hU\Ê Gø†)Bh2|%,(ì&“„©"Û˜Éà Ã%™`L³ôoLB¦"23E™´úû ú0™ó2 [Oo"×GšðBÉ߈z¼hh†+]æeä• Ç¾¦h|êE楖>¼æ¼ìŒ5/¾›hȽãÇZüPhºÒ}~@R•|m&`ž€ ÂN?Äî  Ôˆ§ðWhQسgOrÍB™3W¬huà 7ÄפCqZCÏ5×\á«Q™Ì'KOí–$<Æ…^Èé2Y\*ÆÓpORòö< <òH`cm)òƒÇ– uÛn»m4Š,ÙèHTeßæñ eÉ,Ê ç†i#5˜HL'êϺɔcâ1ýJ”F#² Sš‰]ØVùM^"^¥h4ýC ]æÄV¬^Ìô©ª¤Ÿ855ú¼ø¼þ@S~øqÐ#~.øÑÐ}~Fše,ï‚Ÿš€ ˜ÀDDÀáD4XVÕL ŒÀC=¤ ,9—ÅÒ\GªX×*K8;ëŽ>úhÖ²‘ŸšÅKÀ^½z¥+ÈÌ5ËÍB·nÒjgr»±š'€>‹ÔBc©P{âãÄ_æ>¢Aƒ+¼‰§‹u2nCm|#TUM!“½y‡~¸²_@‰CVï½÷^<ÕÑ,Ï;ï¶\FªtÀâZ‰se¬ÂICWÝG„!A*…°rÁýb½^%¿|Fáô+[( §ÉÊÅŠ¯Üliwe*“ktN3Ú£p't$Í%˜©˜ÿ (p…!G ˜AÈ Ž‰ ÁpäCŒ2p Ÿ6‘2  káÆË¼ºÃ´aòdüäô‹iÆdkT+Ÿi¬]š¨Êô.LO’¯Å^^^"½Ë1«yÑÍÊK—¯ÅëØ£|zÁ–Ѱô¸ÏëÏ\å§@”øqà'‚ûü\ð£ÁO‡þöü¤ä›ó0è|lv¾1uL ^pŒà!a%­m8”]M'^xaå–`A©åkÍX,²T k0¼.ñ” ¶™Zƒ ~ûí·×YgÆ "\dD­à&a7ÝtÓK/½´|ƒå7Þ¨p&¬hYñ·º½5GFšH%Œ-âËMSvrŒê¿ø…j±Û°iâu:N¦¾ØèHE¼I$QPŽ ¾bãl)ñ‰•LPÎÑ…›K*A•ø%¬ÔKj•<5j›uB ø¬ðñò/ü±<¢ÅJ„”?´Ë‚þ ÃO?<åGƒûü€ð3ÂWðÃÂÏ‹‚ДèïG&`&0Q°A8QŸ•7«‚±Ôã‚5œŽW…ÇF¸÷ßX8.´Ìå+orý©¢œc©®Y “·­1ɸµt&Ò=î•Áí@º¹]wÝ5ü<È"ž³Uœ=Km$¤ÏVN1eâ6š¹‰46ÎÉa5ÌîGVÕcùŽ!}ACmó#|*m¯¿þú8F0ò0¤i]]£s¿~ýÒ\Ž`dCc«8% ;-â¯à• FNu„¡ltÂ0¯wðtaDhPÌL‚ÌöE¾r3œK¦JuÏ*FHzJUQ8â|ÒºÓžY@0¦sÈ»ï¾{«“ZÄha n=ÅÅ”Ðöã4a“‡)ÄDb:1©dh1͘léNå”\£³é1ŽLxý±F呯”ãÕHÿ¸@1¼£¼D¼J¡-¯˜4ä¥ãÕ+lŽWU‰™3G¼ì¼ò:È€\ñü,Ä.Yý\ðÓ‘y©ÎLa£¾i&`ƒ€ ÂÎ1Žî… Ô”ñµÚ#t~ü :C¥›ú$!øàÁƒYùÝzë­Z2â39æ˜cm‚22 2«I¾Ç"•†$7“dRZ¿ñÆù2ÜÁ•Á*–Íi©>,jYY’ ŠZî³Î&ãy,| EÞD·8÷H怼©ðì³Ïr>*¼+(ÌF8aŸ!ÆLT'8d{Ùá ø!¿ ÂÙÕŒ¬/ÌŠ~\I§žzjlÅ?Ñ>±tÂ0ü?é ÃB2˜1lzŒè#ì~Ä”* zÉ# Ä&I*R!…¹‰Ka¸¢£§qÄ¡|¸øè]«èøÐR¤:ÞaÆ«½ŒÔŠøŸaè™x½˜ Ú©ÉÌTaÂ0m2gjEj ª¾™*™¯Lo&¹þrA˜ü¼¼©ÊkF^™F>@^4^7©Ê X¸ÑÝòo1UÂæç•çÅ—?œ’ü ð³À?’Ÿèà ˆâW‚ŸšL¿üÕLÀ: „f(ݨ6¹pÀZÀ±§.ÇaBÌ7ß|±°Ë_°'í™û±µL÷q .¾‰ )Õ-ÙÀ«Çã4pàÀí·ß>|qÑ4ËtVç­ZƒDþXo½õ$g&D¹„Ð lã‚ .íÆ¢•X涪ͱ½0œ«XPäñc8p7Á„€"±þ†'!A£&°»ØoÆö„ ÝPœÖÃ\ £±ÐaHtL»° æŸ~ ŒŠF)Å(LQB¢˜²Í¸Q•P,-£kº@G莤ÑAºÙÈȈ¥©E‚˜ ,xþÅoé,Ÿô¡ :f`LÚbb0=˜$ù^¤w˜fávÞ|óÍ™„éÓ¦×(½°K……OT¢ã¼ ³0‘a +çÛåµM{G±M åõO7¦Æý¸`Ä$ä‡%¶ìòƒÃpäõ0˜Ø Ø œØGÐú›@ àÇßKÃ4`KüÕÿµ×^2d±(b#_¬ùÒ í"‹;Xƒ·ÜrK++æpP°Ç,ìÏL±’¯cÆŒa ZѶU]c-ôíÛ—è‘Mƒàh®¶øvößÿòƒaeî½÷ÞÈS¯¦1ƒI‡»Fûi3å ¿²1/öaK^|h'žxb×®])¶Ú¦BÙIHžp¿ “*ìÆÐ„e:¾8Œ¥ŒÃ—ŽŒªÅå–[®üÄZÌ_P‘ê¡9bžq ¢@¸óâÝ¡S‹ÎÒåt?-@ÀV. tù“@uüÄo´™9Z —!f 3õ`20%¢XÓ t@[9™„LŦžO¦4›é6?}ŒÉÏëÀKÑ´ÝL^½ØäÉ+™7eyy£ cü †5½àÇŸ~(ø¹W!?#Ñ.?/a«ó³“w¢FI_˜€ ˜ÀDJÀáD:pVÛêKàšk®Ñ O˾XúÉ•º–zl?“Ï ±ÈKW%×ø+R¸,ëɨ¦9ØFëéÓ*×H t¡6ªášÀÏ@Ä|ŽB±!-µTñl°Ž†ù5.­`Z„›ˆÕjÉȼJï¼ó/å¸c)OJn%|)<"/.Jæ«ëžØ:Hº#<2s(1_‘ä{ï½w쟄9>¢<ŽÇþRÜ5%ùåó2uGâÑD&’3n½FÊï#$URòi®‘K°\ §´”Bê. š&,8Ø#%¦&ƒÒ¨i†’eXÃaËp3è =@Và£dÐó™xñ7€0-óe˜ÀLc&sL02ÕAzÕUW1ùyä£ã¥àÕhú‡€|¼†¼ŒˆåÅäõÌHà7!cj}‚E/?üt¨?&Ü”LZlãG ßß10ŽCÀaÇ kb&Єë³H ¦àœì^S¢DF”Kÿèæ< §J¬‰Y³EMëT¼Lé‚U«@<Ç{,áFo@XAîÊ'ch¢ñر(¦¤È'èH>†'n¢C9„Å}êÀ!B [ñZpÜ‹À–¯Îˆ#š6¨N’q9âè, ÜRKOû$—Yf™àCÜ/ H‹‘a2âCt“C=´©k(ÐÅСC O’žCñNØ*™)Yñ+êaØh÷ Ó'á@ðæå=lŪÕ’,Bs4š’iI,/Ò `NKr€ÏD,æ§R Jê1| bÌm—ác S,c2¨ ÓƒIÂT©®“0¼ÍLN¦(Õ™®LÚˆD™ÒLl¶¤¦ÎaµÂ‹Àë ¼ mDyåeä•” ÈqÇkËËË+;T€O:«yÎ?2‰¹¯›üPðs!ÝøQ-~RB1~j¸©Ÿ.ø! s±:7—40ŽIÀaÇke&%ÀT‡yX¿’P5û¸´:¿üòËcyÇæ4™mÄxÔñ!™C”gHpí6¤|ù9"žr¼J…Y,¶”Pªã !º½$àK!Ûx¶Kã~gIK‡}t±? cYÐÒy?|Äño‹ïgžyfÜ¿þÆñ¶3Î8ƒøŠqà gËbÜVK,±jðsˆp -™ Ñ@š_ÉÑ)œ-x«8G¶¢J£ bœ`D…ñLzÆ”#m™†½ÿ?{÷Ös]R•ÿý?NÞ•˜È‰ j$$D¥ z Q6’Š 75ŠÒJˆ&lô@@$1#QLØÆ¸ë`kpƈž¿Àûÿu_rQÔܬuw7Mwßu¬§Ö\5«F1j<טcÔ˜Ï}îIôìhp·¸±LÈ)Aƒ›"ï!ÀS#`[ÅçhLK³@ËLÙ–ßøUßhtûÑu‚ ŽºÄ\MÂ"²ŽlJ¬GgL%ê•Q såY>÷ ÊQÈú¥•©Ràg>ó™”ù¢¶Ø óÚ&6KÞ5´äÝë6fúÁ†=ßÔD)èì-Žuc"pŒÑ0 ÒW†‚yq‘©ÉÆd|ÒŸ9ºÑÓ™]ú×ÅÅÅÅG–CøH¢aq`qàŒ §*…ñ¬Ä%<³ˆÓÕ‘‡ƒúK Ÿ¢/yÉKr±Þ  ¶Âô‰¥›O¿‚ÎÀß‹^ô¢ñX—Ÿ "Mü §ä´#º0Ì ¨zñ‹_<žr<ºe¼.Ê!umÙÈPÕC‚Ÿ@ÓöØÞx¯¶ª¡ p©q} ^.Ù¯)æÅ+/ Øy¦Iw¿Š}û·»AüÁây¿¼£eJqŠ Õ{á8Çõw÷w»ƒ¸È­r­‘R\½ãŽ;&8î5îÓ Ã+†SHÐà9%Ø÷‡*Ó™4jf9ˆAÒ‰+k9UŸÜb-ÙÂ-z£=aÔÑÚO® ÑŒÊLpÄGˆ×«õh5»Xr–ú¹…*¶ÌÌýâ½ÏÑ¥´T÷„àíOè´AÂU[ÆÆÙöÙ^± mF[ÒÆì±…mdÛÙ¦ÎÅ~š(?jýÊ,ä§Ât0 ̈ë•5#ÃÔ08.2>LP¤:<°*ÁÛ­+‹‹‹_A,‡ð+Èü5õâÀâÀex0Ÿ <-ÑKCÁz°Ì±h8……¼»ÌÅ—¾ô¥jN<ùÉOÖ†’‹ç^ùÊW¶$i}úY†XPõŽ D  í¼ºº=zÄsli|£Ú¸ŒÏ~ö³OúÓÓ_ÜÆ»àÆ_¯i«žß·p¤)z%wCF¶^4ˆÀlSXÿú¯ÿºUL ÂAºfƱϧ>õ)EèÇÃÆâ\qìí®»îº¦Âd|³Ö•ñú8¼1e13â•0×ãÿøLçS…Ø7¼á Ê{”¤ÿþïÿ&¯ýÚ¯Mü÷ºóÝ·ô–œ0¬WÏ€ûw†»!A·w¨mÃÔȉP$! yˆlOÄ[ÂXêÖ-ÓbÛ' Á–¾ »ÈkòB§[ò•b<…8>ãp^ñĵÞÐEuD«uTˆ"M=)["ØcŽZRN*Z·ß àÕã+mŸ-¯BŒMgëQþò?Za“ÚªÙ#Xa Ûȹ…tÒgüì£er‡üäb\k„aL\gX²^¦†Áav\d‚"æ(Æ„JªÂıõuq`q`qàÑÂå>Z$µè\¸Pà1 O˜%ïøä'?Ü'ƒËûÓD 4Oî!ÑÎ×zÖ(W¦OeýÃÜ¿üË¿Œ‡ çýðÿpØ =!—g?ûÙINëíêdx5¹v¿ÂˆÎž%'í«¾ê«Px ¸å q~÷wwÆçExixÉ­ñÕ‚zr<A–µøžïùžÜ뤓cT'Á«;6œS‹2#àíOüÄOˆe)d"Ø"cEù)“ªn²[E]þñýò8öK¿ôK£UŽo©E™2!ÆÇ^%¼ìe/ë»òxXzýñÅ8¥b}}. ü•£t<1 _³"vC‚#s¦62S ‘HE0²ûÄÁr,ª'Ó¦ús°¨ªeL Ü}½†cûèj Ñ1‘eSX¯þ$I¸³ c =&G(Õ¢`㤧„9ã:Ì©k}{jL™ÇÁ/¶É K£f6‘­”üU›Ë³ÑÆ LhÀ1[ÒÆ¬3fÃÚ¶á¿l;gR<‚Þýdš7«CLÇhL˜—l:¼E!§‘ bˆ Î(%ÌL1V׸:,,,<29°ÂG¦\U‹·ÒÒOà@ÛBs”«5NUÑ †ƒ†“è˜ÇÿÉÔrKazü~áðÚtPo<1“rüà©sY rÄS-ÄôµQ 1“›¾—B ÁŠ„ã`b5ýƒ†wõÀà©â8•iAŒ#OÎ8]ï5e|¸'³|ƒõ·/¢rqâKp¦çÖôÇ+|Ë:ÆPÕ¸œÊ™‚3³[ÇrH”s䔎þ¼Y$4¾ûÝï}ñ®‹í)`H=¢!FF-5; žŒ$„5ß2 D¼%XÈrŒÂ®VdEfæ öbrbà™‚‚8+‘G|M€mãç÷×k”‡ õPnfôIÙìDTè6¥¥ºqê(3•¾iåƒÛ>™Ô†Úî2[Ï< !Z Í^ÙÎø ´Í»4˜ˆL¾E=&EgedºAb|Ü’Ê45¢N—n¤ð׈cõYXXx8°‡ÉkŠÅÅ›q@d£‘:ÏæÅOTbHQ¬>‹×¥‰fÀdÉKlÍÃÀ> lñî‚óê`ÄãJŸÊ>úÑ&‚ÂW¾ÊÜØÄ1CÆ$:ÃJ`sÉ‹%¶I’[Fp*~÷w7Á1(fxýk ÉYi¥aù¯ýëï¹çž‹N”>ÏyÎs‹…e$C^¤™Ÿã„•ˆMzf„Ý˲“[x#w(l¦»"zcµk“ãË_þòÆ|¶l<¿bQÞï×(œýùºûÒ¿ó¡ú+b4z,%Ø,äø˜†uå–ö)‚1Û1ÿ"o­—(¬v#óùKùnƒªPjÓäÏûxtÿ£–Ûg»ã¸Hõ[…fê}QÝ…f[ÆÆ™’´1Á³ÑŽ’´·dØÂ6r°µmp}ú€)‹Jv¨v\ßÈÑJ§åǰ02L ƒKÄ1JLb4²»i`vKÿº²8°8°8ð0s`9„3Ã×t‹‹8 äR×"Ð-ŸEÛ¾‚ò)µÿã?þã¾ŽÑ '²ÔŠH¹‹`¾ú-c7wAó‚!jc8ŠóŒåû/Pù…ŸQ›B‹\M‰g2ÇšY+@s¯kÛ«€ª^2®›?'—®?pÈÙxë[ßÚãg³:Ž®¡aä˜ë¤b[jlå]„«n oú–p9~°¾PÉ($Ýyçên”.‹9PusÉ|ç!Èqó½ À•ë}lá3÷‰€È›Â6þÉô“º}A¤þ55úŽ‚Cž+HE0²ëvZŽEíÊýhLÃ: ¬d½˜ŒÕ7b©)”X«ÄÍ囦¦Ôƒ’Œž­[(ÿ[¨Ö}Úùþ’¨Üõ^.eÎi^÷RòxeÓì˜ckØ cüS›ÈVBCžàØb6Útïů¶s‚·6¸mn³Ûò÷­ä ñë|Ë«2b.˜däìScFcV¾äâï ƒ}ñ_Ü{Ô^\Îê°8°8°8ðåãÀr¿|¼]#/,ÜŒAõ [À™³CJáƒïõš »$°µzJ/*,ñïÿþïMîr]%úüÚ£hùú¾ÐÃdè™ ÚÛ=µu²Uƒ Êý÷@`Ôè¬b’;WòÄT°PšR}tz{A~úê¯þj Oæ2> Ý(Šº5SÜ d—L¨îEë[š&VÒÁ9t:Üå׸Á0±#RŸÿüçÇ)®iÿÓ?ýg£ú›¾é›¼A¡¥_ÌÈÅ+^¡jËy8Ëëãx#uØ xUC¶™ª¤-DÃ4Ì×s[V'ÄóͶ§ÕM©O¥áëî ÃÝå›Èt•8ƒ¤mùÄëÙLP 0MÍñΣ(üŒß¨çn\tZÝúº8°8°8ðçÀr¿â"X,Üv‚o|ãã`¨ê.þœÁå †À-{èÕa–ˆÊ×ý×¢ÅëëW W€¹\o Ô„HÈÂ\…T†¸^|òQ îíd×Läåj4ªz@d™u–æ0ÒÉ‹¼Ei¾ó;¿3·š 4Šr ñ “Œo-Ï8܃ïýÞï}Ë[Þrþ‡Ž/t#f•{AsË?©$ÉiQ“ÃË»Óß§Ä‘¯wñµz è¬ç5¯²(\‚·¿ýíb8õpøi^ ØDPê$ú7†ÇÛ·mJ8 eÀzž&2IoêXš6²dá– X11 »N<=¬Æð¸U˜yýí —ˆ º¼ªD¨Ä6¹eN¯P9Š×Ç1rŠN·§e¦Ò›zO~ -`#Ø×ï>Íò³£m@ÛpœëbÛD6»-oí¶«R…1wb0S”Eãf§ï¾É†Š{Ìd™BòÍ *è!ãvâë^¤|uXXXx8°‡ÉkŠÅÅCHÍʳv(J¨DÌ$@JÀ=Ê Å~ë·~kFQÜ¢Þ »à<njf(i8áÎÕ¥Ýó?²¿>ýéO’µ÷R[pàíõ:¼y×£“Èê€Ö«_ýj™x©!Ïbõ±vø²!¯þçnž›£MÞhw£ìVõ¥´…>÷D·~ê§~JÂÞÈ. z>ò‘tÞq%Êë7$=ð$æ6Þ¨-ÍÏ ®¾¡¡Bó±Ò¾ÛcºëʯŽÉQ›ñÄÎ#lW•nnt{O„Tƒ›â¦,§I-Óbæ*4°s®Ï„ÄvÌï3BÉëX¦é‘( ttȉ›Ð‰žPƒ¨„•ð_þå_¦N¾R?JXžSN*šþ楺VJ3~KÉ©:z¨}³¸õ¹‘;j ›®±>›Ñ–<¡sû“-ß<óš‚Qc»˜†…yadêJn}F)·X,ceFkw…cʨMî"¦›’º%~]YXXøòq`9„_>Þ®‘.p@ˆ ÉrÀ™¶Þo~ó›Á)5d¸/(“Ò&û«ùu 0zIZæøÓ?ýÓxV>§Ò#ÈÓÑÄ$¡AáW>¹cI°HazD%¸°Âû˜Š¡t‡Ê–ã¼"Nwß}·5HwôC,D6М‹*[8B6¾ýâÔœÇÒS2ˆ˜L{îuÁ‡ïÿþïo5ü1‘Œ8¯Â“‡N¯JïY/>dìxÞÅ©§ªöËÇ+¤®D”ßp]hîâ‹ §}N ‚q!BÄbqWÔMç1EÐP¬ÏÜwn)9¿bih™-îÒåcˆë˜s>Âö×T¯©KF@y£=‘ñõ“Td!b‚ž¢Ù”JD»ø3TåsŸûÜv®£+T‘Bf"ƒPTêš=˜ºH¥)6õê”ÊhUO[£þÕÑ\ãu[ÏÌû!lIóÊÍh^[vÀІc‡ ã[Ã’Ù™šFtq>yæŒÒ¸d&‹áJ‰¦ÌÌZèœìPÕ2SÅ º‚ 1È©žtÞ`_qæ:„ݲ4#Âk{òI$òÉ”úõ| &IÀëõˆ »>2fP p‡¹e N |šK ²±q0i$Õêd÷y¿¶Ê×dÙA¥pv ª{yçe9¥ÊÁsKÏj†cMP$3oê¶ ’x‘@½,ô8d%ä"hé½p¸Ú5ú ‘j3žó'ìšN ÊõŠ\,ìV¥okà–ÜqÇïyÏ{vßÍ袟tˆ_dÕnt»A’‘kXƒ'ÇØ¯è¼²$©…XŽEE îµXK¶p~&`EÂ"ŒÂ®Iο¡4¡ºÂŠìˆ’@»£¡("³}Œà–k4œRE I-so&µFóR`j|ûÅÛ!QDÎ’mr¥_—… Òf̤¸zäTÛÔ¶¶ n›7k·¤¦±û‰'QN«c^2)ƒS½r9ri23w‘áb¾\!—ìƉË,ŒÞnþH:ëúâÀâÀâÀÃÃå><|^³,,|‘γ%m VÍ¢?¨>6)@Ÿ°”œTI9Z²¶‚ ¨ò ÔnôÔ_þ^¯ïÑ+mȲ“®&R!¥ øŠòÛÇÔRé~ý×]4@^œˆXAð®`‘Òˆ%õš†zÍŽ“…xe†*X¬bas …ÆÒHˆF±Ñò'JDº$æ…(×ó¼¾åt»¯Î¼aÎb2©Â¡w½ë]ÿñÿ±½e{‹ë«(žùªW½jûŽ+¡Èâ “MÄCp:îï|gžŒ#»2õBð£S‚†?ˆ×é²6jÃ4¾¦ ˆyuÓÙ-n'M[™®~ãQÀ‘ˆ·„¸:F¶4 ´L‹FÆlieQì´í§»ò• ˆƒPºsù#8ÏDq÷®£‹”„ªD XA…¶‚Æ*GñôL€.3ú¤¢ÍÀ¤ºøJïΦ°52ŽÍrÓ,¶drbQn«Ú°¶­Ík ÛÈÓ«,ÌbËÓmÛŸ` ¥É·]K£1q¼ÓòqÙ©èÉ(Q˜T”e¬r dj­ g袊–‰½¾‹‹‹,‡ð‘ …EÃâÀ-â€×©ÅÙð*ç8uY¼ä«1ÄéúÕÏé½ã»©€ÁÙwyßGïÀYRIëZØi(ð(*µå2÷ ÐÊò™€µ‰Bü'd4æ¼mÇ™®8‘4‡e*§G_ëY;Çz.n&ð·K”øøÉi*Ž8ä&Ÿ3y‰˜ª^é‚–¯¤¿êûè Wñ"ijxtF Þ»H'¹·§áMwŽNÕŸT—ÒÙª]/k¼ñjo¹~¶{ž7‘ÑIHpd·-‘Ó“cè'èQÖ~ÒaûžÆÝÑŽ†ˆD*‚ë-XˆåXÔÅǘƒE-à‰u¸}a z0Û1¿NTäB@BO„Õ„H”W.ª+¥0Ô&‘1dP'.+Õ¢`Ô¬ “™”*RÈÑu§®9¬5Þ=ÜØ¹Æ† Ò"C6Î4ŠñÚža» [ý ‘mkÛà¶9“¦Í,´ÔmnÉgÌHLJÂÔ$âÇøèScÅ41Pú¸B: WÇqe¬Ýê×¼¿o™Á-1ëÊâÀâÀâÀWŠË!üJq~Í»8pë8ž¶F¥x‚(Ä{ßû^p39…ežÙC™ö‚zŸò”§œ\IÝTBüAÞÝžðØ+ÏÝ{L+.È̉£kxmLç‚`_®¶qÑPõõ¯ý‡?üá“\8•…rIågög¯,cê¼lÍ¢OÞivD3\ëÝn 㫊A5åºi­á âhج‡Ñ“328BÉ9.§j¨7Ëéì´ÕÓŸþôP‚ÿ’ôdë­âè:ÿMKÒ ¸»4jp<°ôsoúX!cúêâ5’Óà³ £™¥PÙˆ·„‹ËøØ…iU]ÌÄRŒÅÞT%’éÂÖ ”‘~ ®^îÆú¦åL_)Õ6•£xÔo×¹Ê8T—ç*³vÓÛ¯¶‰Í ´}l¢“Š»6 mh3Ú’B—Hœ±…mdS_¨d"z{y¨1‡Ù1fÞÊè„d„E ÈÂL0_Ý&ÌšÅ2q sG ¥2†7õØ·|[W,‡ð!aãdq`qàDúJ€B®4xtEugÒÆ„VüZ¯@õW ´{ï½W±Á$UBWЅ˽¨}ò“Ÿ¼@Ùð³XÍ/ÿò/ÇsðÏQ(oHsTIYŽ‘fáXÐÛÆa_Ä€€Ã½sâ2Ó¶IwÃT_l Ë8Öñ-JÛ•(Ö°;·aŠŠ ?…MÞñŽwŒÞ§Z å .eÉn|êSŸ j¿ÏMÀ/$;.+-[Š€az^âù‘ÅyÐÍw!A!©FÛ: x-šw%cêÐtîõëU ¢ÄýU¦~ÒA·IÝ3_@2ú£ä!ÙˆŸo¸Éw¬ÃÀ:çelf¡ð˜Oç‘(s áñÅ7ÚS CmºÇuQ³“WbLë£ÀÔ¸iÏÔûÊ`5ÆÚ8³¡2²-f£Ùn6ÝT{ ‘Di“Úª6lÎëZ²|1.=’ÍPlõ­&…%Iæ¶E1>ˆÉ–áIöÅ$1V —a±>¯ÁÏ<´*?Û`›0³Ú‹‹‹3–Cø03|M·8pë8 œr×]w%–×EüD%Á(s×K„ºÒ?ÈI`Ðù«¾º8öq‹Ûg¢Ót&M˜:ëE‡Èí ã2EÓ°ãWLÃ: ¬bg"bÂê›:jQ0#XÝäÔQ ŠA=¦ó´TÈÔ‰RQ­Ð@E©ÜõA-ÊL¥ãÙ¢ª_ùêÛ§)¾¶•Í5e«Ú€¶¡ÍhKŽ9™8iÛÚ¼!XÚjk„ŽL>j3Ûg+µ $’À)…Á4†È,ŒÓ4ª>×ù´ªÂè1}  ‹™ g˜ÇÝìë#:×õÅÅŇœË!|ÈYº\Xø"¼;™ŸpO`ÜÜŸýÚô6§}žÿüç«?žï –ò0>ÛœÇ<ÉE‡¾å[¾%ídp¥OÓ9±Ó‰ÎJ½à/ÈêC\„ÝêŠqªwÄM§æšBÞÞaàÝGu#U¿€­ãŠÀ…Þ¸}½ó %Y®HÝFA%Ë)~(2Ó8X(tR ŒvML,W`k³ßÈsÆL&Ÿ÷¼çÅ™4É:jè ¨¸h]Ž]9wä¶BÁOLk …Ò¡íæ§“W•ºÁfñFJ•6D˜÷tÕbMÔï­o}ëIôÏO:è6F b¨LmpSôÕ—&E23,d›J’ZÔ5C,Â(ìÌÄRŒ­¿áØ^ÂÊ™óáqŸb= =¶gEþ0ÂP›m’'k©µS? |>i¥ØfϺ(<µ?º×f±elLÛ>ß±Ñl7›?/ê´…Sèźlím5£’75 ¦ã~~üï­¨yñÖÁÚqÚTBfš-ïbĘ2­‘^†Ž¹ëtÌ þ$›é¨ßøk»­ÆâÀâÀâÀÃÃå><|^³,ÜF¨*‘*í²¿‚ 6Rß/¼ ‰› þÁ~Ðuˆ°±ðkæÕˆ· Ò^/\;\Y®P*š<´éü¡SLG.ÜV~z ¡$a ÞU`ðIOzÒäÍBÀ.ªöáøPŸL?±…:NRÝ.æòujG§â$ÅY,ù±}¬¿5Tä—>÷´§=­óÃ(lÿÆoüFÑ­+ËZ_èLT­Ñ>†—ƒÙ8µj(ªü àÔ]4t;Á"…='Š· Å®A·\sJñÓkÊ SðgÊU_UïpíbÆìH¹ÎnIÙ°È€õ-]1IM}ž®Ù†å€Zæäóc†`Kr3£[°§¢2*gbu:"£ã*vÛDLÐÄ]§7ÓQ ŠA=Ž^Û0ŽFÙ¨\nô<‚*ž¸ÙãÚ”\ÅÜ‹«¶@xhSà§ b›äùHúø´•\´­âLÚh¶Û¶§íœCÅ–i›7µ{¢múÊtL\òµFF ¯ÏbJªs¤OIF,û‚Yókdî½LÇ ºÎ$2ŒÉ¾Fäõex&š××ÅÅÅÉå>H®Ûv8ìÊC `ÊS`C”À×ç<ç9zCÌbI‰°ÁÙΉ<Q)ø‘·¨ƒÑúõ·Ïã3Q?½<`‡¦Í%‘Æ'Õ„¸Q¥þ1¿T!:uV!€£ …õr-&dÉ£hÙÁ(CÔþÙw<ŠpÀ2…F/†1Ç0Ü{á¾ ‹ 캹"š¡#´zô@D@p”¨ò¿}Â8©@.ªSß\dDZQó}ï{ßÅà8Ún[Æ£H×”¡çaèÍõñáid7ºÝ áL>Ma¢)çvºqûu7`há– £cFñÙŽ¢gñ½j@„rT½†( ”X w\ ÑÇÅr‘Jäö[â®P¼Fæé3Nj,MƒPøF;m„ÑŸGŒÍbËØ8¶MÔ“´6—-–%p­o”jS§Ü‹Ûmömðs¢0_LwôIv±T‘oŸÉjá"ËaЬ‚q3N ‹L_DC×­—ÉÌÅlNMv)\Z,‡ð¡åçmq`qà¾J'É×› QG ¡¼€ ªÎ©$ÁeëÁ ‰‚ò'Ò¸“+–÷Ú3žñ W=ª„2êjއʧú)›KRŸ:À›=¬²a_óš×$€UÃÄýébÃÛ’îe.ïàN`óè. Ü™´×½îuÀhãE!ÒWpü%/yÉoÿöo•i¦¡ C‘¢LQa¥©ÏÉWq^Y_e@*Þ©ÆBù¶oû¶ú¨##yqòÁîˆmNµɪ§r1go"IžÉ˜u&ÀÇ8ãì“€¦Û§¯Š…¼ÿýïW}¤)¦4ÁÛ¸ ãùIÁE)ÌÅкéì–æÓP4lÕÌt&5õª•XšZ¦Åvð40Ä[4Ï«øLk÷ó‰ )‘DC@Ää'"#8â#Ä>[1—>MÜ„NôºQƒ¸(~¥”ä<ì9‘aœÍ8ETÔë¦>ùŠ·h£äT}[Æ.ÀÄ69z‘ql4Û-|³oôvM<Ï2LgãO,1 sÁh$4MŸÝGÆLh7¦‰™ xË|õFfq‹¢2w1 3È’Ø×l0•yF@²Lè.3×ÅÅÅÅ/–Cøebìvqà–ràío{` ôVX£š„ô²—½ÌùŸäG ¹|èC‚]÷t¼HËW¸¹§¤¬*´‚&=€Zɕﶗ¹éVßëÆ—íæ8–Óbê‚„à HÐá‰ØDoÔ„Hl 橪Œƒ(ìÑÃkÊ”|”—qJ¡<4É{ ûýÑ¥ž Üoù ¹²G>o *rêá80¦”Å®'ë(+ÒS a8Ü`”¯ŠšˆnOzò«È’`W=zGçDKÉ;w ¸ÿøÇEê ¸I¶æÌÔÑ—ˆu¬’‚íÎJlÐ)túꢟF7Òn7ˆ¡ÒÍà¦0QÐä,].ÜFH{KR–ç÷}{X‘‚–üヨÀntL¬ÊÓô¸[²°íá0ÔM’#ê8Ö7|Ã7l1Y¯¨‚xÂwn‰ DÂSPZ½WC 9uœå.ÔT«e@±=øÉ]s8 fm,D6õyì å $¾9!Æ=à h3‚8†ê#ÊoœWI5å=6%"1½_þ„E¡AÖ\Nlš—ôƒ?øƒ˜Ï[¡m\ø^ÙŒ“iB‚ 9¦pèî‹\tM~æX’‡Ÿ Æ&'¡Î’¾æk¾þþïÿþ|!ӯж²%î5Âô碟 ǧ¾"〈D*‚ëÞ˜Èr,ÊÒvW½{Âð$`ˆÕŽí“»N4DL„Uï—‰²þÉÑBz×tÓ7Ú£ŸBâå¤c©1e¦Òi°5lŒ¸ß5O[lÃ>²=m񨁾µ…mdÛÙ¦•*Dš%ú¬aêÿëíScû¶Éq¥ŒáÆýsÉÊŽ`Ä’P³ÖÍ<2} 3˜qbÉq^|K ”Q½iFñ8Îj/,,\Éå^ɨÕmq`q`ŸÀ¢üA ƒL°v€§áï:‡¹«+_E™ö‰»ÿ*°ØÓkÎy¨/ ;Klœ5uÓ2G’Ô·ð€º‚ü™k’¸,Ù›ôRzÔªU 9wáJ¶ò‰Æ¯áX—rùÞ1 1-è· ¡*ÎCüÞmý ¹ŽÄ œkÝrèktöHÄA8SÀ¯#=жà­%;R•¸ÊQH°Q—.yÛÀ ƒ’:¥‘ˆ`ë·~ë·Î¹Û]rùÃ?üCï[ï ÄG)»è'ÆÈÕî8Û‹ˆAÂëËȈ· ¹¦$&¶œ ýŠ±Ø‹É£cCA„2¦MÁ58I (iæöv Û+Ô†òd!ÔÉ-˜D٨ţ~õ<Ó=•ºRZ\´g©ô5L@†mb³ÄPŠ%çÁЖÂñŠñ›l«Ú°c‘ØáÓ·Ímv[7p•°Št`Fr?íÚ¢ôõßËd ’ïà«Î‘Sæ+³fêܘO€e ãõeÕÆ’™MÆ“ eHSÙÈt¾2³[ÂÖ•ÅÅŇŠË!|¨8¹ÆY¸‹5ž¢qOÚÁ:Úu;c¿cR¸#˜–’ô 9&:(ØsM2ÊÔgŸ |ðg ‡”Ž’erL(Ýà°“ú„ šªÂ/’ÄÄ[êYå^Ÿà¸Xçú|Å·'!6Æšžªóôæ€#ýâ%ª5ÒìÆ=!YyX!Ïpë)q ,- Õ'˜{Íq¬ƒf¨T’,“ÿÀñ‹xDj®ã* û¶²öåÐÖ;‚Ôú§z78¶;>˜.cÓ°7åp«›j"Èž@Aí1„»;Ñ4ç÷ò !D*×á`›W™ûÓh]¿ê¦³[¶’Æ751>G@$R«í–`!–cQÓíG_1 »êÞ`#fŽÄ#RÞ©a1?§L†r‰²¬#b‚¾Þ©Èí«TŒšU²a&U¤Ô’rŽpª‹BjœnVA½Ç‡ 'dÛ2ÈÙJÆÙÒL@6 mh3ê<úÉ™Ñæ5»l;ÛÔ'é¯ ‚¥å.=â*ó2ž„$å26÷73%ÿ3_™¯xÑ š+Œf¦HÓC&1‰®WyÒgúdfÛÖ­Ÿ –Cø`¸·î]¸ÕP7"8)ðèMozÓŸýÙŸyR.Ïéòâ@< ÛOþäOHyXî±½D}ßóž÷LØKjÙˆ’e$Ê“ÜÃïýÞïå]f9r·÷Þ}÷ÝÔà2â’÷5B4ðΔwŽK| rmhB]Áó£hØUà¾U ù'ïxÇ;FW°=džk#B7Ûà!`ZŒ~ô6‚ %ˆ¡2d—É™qÂM‘Œq¢kÚD¦‰‚““˜¤­Ê`”¶×øÒÉh,s‘1> »‰ ‹ÝQѧñÈçDæ¤%L§Î¬KgѕѥW Ä w_+⢟t¨pÝèvƒŒ‘7Ä›Èt&ý"$!Œ`D0²ßÑ,ÊÒ,ðdùù £° Óšñ›A0{1«/2u Pbm+q[´®éjÓ§õÌC §rúÇ&§ò•SæºÜ{ÜãpiëÝíÞkû´æ°mesÙb6šífÓžU³=mÒî#Ë´…wGÞ^d˜ h sÁhlû¸ÂÈ4ùÙ¤LД%KLt&o˜Ðcʈ,1@Äw–мû«Âx2¡ )sʨê™ÍÅØ2¹»ä­‹‹‹‹’Ë!| \·/ÜFHm’LÌçAûˆöàÅfiÂ7„«”°}–?¢"J…½úP¬`H;ä:6æªÁO»¯òûô§?íìPî•ÿ&t¥¤ç5@ÁIéz#¬¯"ˆWx;'æàÐèo”HU%LçmrÎz5™³#Œ #‰aM®ƒzÝu}4IÝÈ B‰ø?pt¨B’ñ¹+˜/àÓ n€›•…©í×~íšô¼‘þ´ñG¦@Ùk äì L5L2DÒ!•ñsŸûÜ8ˆ˜˜|à±–#Lï• ŸùÌgÆnc›dq™üOQ5^ À­Z£èq&¥0º‘ו)»ºéì–j ¡ hXƒ7pgðxh"cˆiÛ–`!uQÜe™;¨ÃlÁœ1ب3b#fö͘ŒÕ£BŽÓ· —ˆûÀ‚è)@7Å ”UÈðÐ'¥¢Z¬ Oñ®:•¦ØC¨ý¹žÛ8¶»dZ¶®RéÑ@‰ h’—-9†õð§¾½l;Ÿs¦¿âFe™Ž]É25£/Š3ñ ÇLVŸgQ$­_Ç%´M #æ3’¹ÈlŽÎ6£šf]k†÷íÖ.v5¶Xá–'ëÊâÀâÀœrñœ|‡”­ˆ è=ž”7Ê¡²‚gðb€M¸®ßu×]¯xÅ+¶nLÑRpy\#L)MÔIÓ¨á0Ä+²½|êpô¬DLœaæÏa+­ðCÑçˆC¶+0œ€ó?žë7sRäD¶X¨§ýæoþæù\¥Y7“öX#ŸYôi¬Àa€;u>Æ8ª‰`k>Ɉø_ïg»ììmlO Š›YÔ¸ ñ4ºáTX‹†-ÄÍñž§P]/}Td¹‘·#oSœŠ×4½AÞ,¼ÞÚˆ§entûèË…rAí&½˜­ZFiX”¥• –lá– “æëƒ]˜6Æ<±cǰ$÷€cy^e$ m‚&î>(±(Ê@%&§‹Ú´‚Q•Ö ©“Ô`#%¤Š£Ä;Ѷ¡%ïæ5ï¨-f±Al›Å–ÙÝJífÒ>ÚØN䊹lÞ“qÛ¦=ÆÝ[z‘¹ˆg΀0#Ûc¥ N(É'%‰Yc bsÆ_ÛΊˆ1Œ^ŠÍø5ÆÐÌ##ÙëŒçhÙ˜V1nÄe=á’½‹‹‹žË!|ð<\#,Ü"xrŸX'Ù^¬°%<¨€„ÒíAB鿍ÏŸøDW ¢zAžšçÅhÞ¸Kåñ¼vg TÆ9úTB2–Köÿø©°ïDPNâ‚NÆUfð¥7’e^Á/+DµÛØç©÷½qÀ„5 f¸Ö{Ò<ò‡k7+ýðàïF úæ7¿y «ŽSLmìò*¼ºs ¤‰”+œº_ù"ÅR¦’dÙõM׸7cHðÊS‚")ï¸ãŽñ VØ‚ jQ%ýŽËÛ\"AªK©Ñ˜gFæ8ñ–)Øî+øÆÑÒÖMg·$ S©vœÅ¤¦=¥íPÛ+h™=&ÚÁ1[0g mowÅŒã Û ‰XÝgê}›ÿL=( U#É»dP9Š×Ç:’Z^ÉaªNáËÁv˜²RqÆÒl›ÈD6”mesÙbyÆD¶ÞhÝ’j ÛÈÉ´´µ‰ìÜìŒÓ‰#5§ŽykŒ SÃàxµcÅwÔˆù²À4¼Á’‰Ó¦`&®&1}I¦Ò€Ä2¤©Çƒoy<ÁØ2¹É¼Ð).Ù«±8°8°8ð 9°ÂÉÀuûâÀmád&‡-ˆWÀó0>>ž´7\ÿ–“? ] ëyæGÚ ñ5mÌ*=èî@~¾&Tð ød|Ÿp’ƒ7MÕ›ªŒ¶[ ˆ n“Æ]” ”Œ#ÇìÞ{ï=¿EtU ª€›â<ÿõ_ÿµ{Kü1 íå/¹·‡ΖZ „í’ùæÔÖ‘S ‹xtù'ï—ß’!Ø…Ú:±bâý– ñ®pa+á/Àw3¼&$¸½WD3ä(Ž•„¬ª¼B×Âi7£#~Íy?GàDØ‚¡Ý«×îLJ Ò¦Eã“•§¼äî¡Jý4å3»Ý †JpÉàÚ&*XG2³{.1 ·˱¨ºñY©…ç\Y¾b æ\ð±ã® "Q(±n\£ÌèÓ©e(£( U¹2¬jgñ[ù‰rRQŠZ‰ ŠM½“kMá+»ck Æ6±YNIb8*väö×¼æ5nƒ¥œ¹ Êñ ðI}l0¢íO€ÄU^úÒ—OÍÇûh{Ì/KMºP®&TªnGÔ2­ÿ©[Þ‡q"THQÍÎeØ“£nã8À_“4‡JPŒô$hjsM ÷RE YRTêJi©.¦Æ”yt}C µ§ü¶6&±ÓuïÉÈC¢q»m›ÎÖËPf´%/Æ'míf«Úò6~F¶vÇ&°º5œ)úi.¦†ÁavKèOI‚Ð`²®1 WúqfÏ.cô2;3¨L"óÈHv@Æ“ sȨ2­ lÜo&7·£!"f–ç]^­‹‹‹‹×s`9„×ójõ\¸€E<ŒÏcoõ$Z¢h*(û\ȃ‰Ÿò”§܈~€Pñ|¡ozU:8äÓ XßS|BÀ.h®ð NåxO‡M#^" ž_A7>ÀQp#ò†ž½"¬Þ©’ŸúÔ§®Q@­'ÕÂùÕ_ýռ£ÊT8¶Œ"~Bð#ÙÜ0°òóŸÿüÅI |lTÄbIäb`D>¡ºŽ¼‘íûåBéó&úˆOê¡ •™W„ þݵŒÊ“Aùèk™}ÄÖܪïû¾ïƒ‰¯Ïq5#EüêiO{ZÀ÷ÈXmý¤ÃÅ,Ä‘x 1£+;’jd ±‹²´7[0'G×Ü…iX‡‰yŽ“nÛÔ‰PvÃn†Êû*‰•p/rŒ’P•ºC(§HGŽñH µ¤œTÔŒ»Ûh”œªSø>•0Hèo™(^ô•¾ ØecÚžuqGÚÚ¶|Û<²ñmNÚ®}p‘é`@˜Æ„IA|ÖÅÔ08“ ªÈòkÍÃÅ|5«Ö¯²‚ûnFé33˜ç,1ŒŒ$SÙ(:`HIÄíT‚efµ™Ü …)Ne)˜h†ºë]ÅÅÅ›r`9„7åØê¿8p‹8 ¾…£z€œtçw‹`ú쮂rçÒág~ægä/å‚®+¢þÀÁàQ‘°¯E;XŠ¥ô”U¥r`CE¨÷s_Š`Þ² .ÿØýX\ˆÛJBëxßûÞ§ÃþèŠW 6·d´~ZÄ-N©1Uüóx^ &ë£ýh}Nµ w'ÚNííÞ=kä€PžSŽÛžãØZÄ£¦U—NØZ˜Ây*§‰TPÇt¸Ë­fñ©€"WðÊpSi€ªy/üœúê»@jb¥ Ê“Lp/B¥ì~ó*yD ^pâuê6¬Ë!®ÆE»vR(ªƒNòPfõ1ïÔ‚x„Äâ„ê,%-*`È'T's,cÖ'é¯IìˆóxÞcuøl[ pW„žîǹ5Žä@oµ–çð’<[L¯˜Ut.óM@ó¢o£n'!4{Þ/KðJòàlp0Ø=‚uŸøÄ'DB”gA‚þ·YtV–ãÏx†UÔ77åW~å(—r—3.:%H¸Í`4² :ii¼ïøßø Ç–úkVçS0DaLpÖY©úˆGpÐh°|Ø· έr´ÏE˜œ„Ä™3µ6…ôþ@ÜÞ=)ª4U‘!Ùó“nßãûÁÍþpÉ×Óëì7î–#2‘éLjê‰ä!©]YËqñFž!¦aV'1{1«6| Ñ1‘\´(¿ëMK’¢s* ª…jFÙ:r  –”“ŠRTêJi©nˆ×™J_¹|ÛÄfiˆŒ‰H –#u=¾ŸYݳa·"=¶¹UØòú7‘˜AhÐïd"?!©‰Ü&ÈEªR'¼ ‰á¦¬Ï’òkwÕ1ƒŒa¢£Ì##™-ÌlÖ<2§Œ*Ós Àð2¿Œ0SL¸Ì2…L°Ñòíó…¬_v9°Â]¶¬‹‹·šÊóŸÿüBœó†ÇáŠ=ŒG¼`²qþÞ_ýÕ_‰0ÄQT2^•ŽfOÄÔ“‘ %lDÕ] ÙVžè犒»¯ ÛJN˜B ÐM*—ÀÅ9¤=ïÿƒ?ø‘ oó̾®`gO"”¡‹(4ò¡}HâV§Ž‡õé>ž8¢ve Ë} ™ñq^9í*\QŸå“ð €Úú %Òé#qA ÷ÜsÏ®³4ŽŒâHP{daQÕ,ªh*Á"Z"2æÈSãHsË:‘EL‡ÇIÛÖG¡ýy“x}…×Ì2ùÞ˜ií@¹óf#èI“ó©"%oD„'Ù¡c>–Ê»ž¹‹~2c]”¸=y¤4¬Á}mä3$MRF6â ŸuY eZì•lÁ@lÄÌÑ_ÍPØŽùD`–Ý29Ø»{Âðš€!…¡6”‡ Q¤ñ$pf§rúQBk?ª„Dñ¼n3õ¦ä•ûI3mœ&xFn´Ål4Ûͦ³õl@۰ㇶ~Ú¼¶°l;ÛÔ¶öe%F YÌãpM$Ù NßÛÁ£ÔyÛˆùBƒÖÓ†1tÝhÌ cÈ$º‹yd$™Ê< ccFa¦ÔîB öVÁ\Á|"¥¹».S\ „bPJBU¶ayJå:+O(õ›\¬](3•¦ØY»@"†ìö/òÖlôôF-aÆ´ñÓÆ´=mR•llØØ[ØF¶Ç‘§6SÀ ĤX&CqÍ¢ ÂøÄås¯y™¦,püŒC ³ÖÊ:5wéÉ2‰ñiUa0GAŽm '¦˜Af–g&š¡Ö'ŠgƧõ®¯‹‹‹'Xá sÖO‹·ŽœQ%hãÑõX;àŒÄRÄ ”¾xÁ ^8ÌZeÄ.Gm¯q! Fª^ÆQG!˜f¼ºJ¥>? gã¼Ê ÂÎ]†Rw±ÕÛalˆA„‰z™E’Þ¿ýÛ¿ÒÖMEÉZª8ˆI¢ »FjÓýj¡!I€›pJDºÈ@Ù%á x½ˆïÀ‹O[„Á[bG¡!6øæÝ-›Èò”86Å÷ç!ÁrÀA/ŽÐx¼¯@aL΢ÄvýBž>-Và'(,Ê{ùøëp4K¶á•gÆJŒgC°HÑÈ£0ˆ1 à\s8ÐtÓÙ-[¿:«0QÞEqÓ>’ªm–™sh °C°s&Ç@lÄL7&Ig°Ã˜µƒô!”ñ(é4o¾NC¢§Ô ^MèɧŸ¨ å¡Bi S3ÊÖ8*Ë€¼k’Q)6õnV'µ§üñ‡±Ý¦°5lƒ›×–‰‡6R•¶Æ5µél=Ð6Ü ùÚ¶Æ ?ñʦÞíVF1 ŒC¦°vLîO' &ˆ!Ê,îÚ>ÃòSJÂ0kêÓT¾ŒÞ®s»]o®`ÓªmÆ–ÉÍP1¹| u1bW±‹1?YÂúiq`q`q`äÀrGn¬öâÀíål':"é(çX@ä‘#ŠIÔÿñp:. ¤"è%IÔ¢jž¾+Ÿ¨NƒÌ.cŽés™âè³È©š5g¨‹3C­PO˸þ*LŒ«˜Úrüài´J#uk¦nÓWéòÖB¤…ã˜((²!Å+®KhØå€5"‡•Ç¢Ó,ýŠ$Õ>Š•å+Š]Ìå*ȸÓ$å4;·ôx/^“·õ´Av6‘­dCÙVˆ¯—n_;¦8®q·$9£ÙÚ6ø”<ÝÅD0éoŠ+ËxûæÆÝÍ•/~Æ<ŠÁd6O&4l,œi¥Q?&—áÍ€t†ŠŽ Iþ*E±ôaÒ¯qÚÇAV{q`qàvr`9„·SîkÕ‹_Âx(O—!6Gƒ•'dj5ÙO˜bÌŒRÞ (_å ÅýxGÔP‹çýZøÂ8“? ƒ. ‡8M ;B‡A6>UÀDYaà‘$7yk¦†žmȤ ¾úÕ¯VF’|ªÝ²¥Ð›ǥ4ˆú ýé¨áÁ¼3T¡ \s¢©\oqŒGw½ë]²±ÎàGÉ`8 à¾uAÌ€ EÃø²ìZ¸•·Ìðæ´q¢ó¶Hd<%Hݲ¨ˆŒµˆ Öy0o‡…mÇtGD^_¡„k!N._=Iƒsš>×IMá­t¦ãzí:™BLɬª¸W^¥ø˜B&Gçèx_âN ®LáJ7BÌþ¦ÌLÝtvË‘j"Ó™t¼I‡¼Ý¯åX”¥YàÈɬ+úhÀŒÂ.L»è•E┄8Æš‚ÈLG|„Øh^YMÜ„Nô ¾™íI=(ÉÖŸ1ÐÒê¬özÌã–£~1}Ì ‹¹Þ[ˆ`LÑOÄϯ ,3Kús‘)+ ÕÍEüÄ€[cžw™…‘?4vÄ“u}q`qà–p`9„·DÐk™‹;€çò¦UÈþÏÿüO> ‘CwŠ"Ä-Y¿`9N èAÛçŠOÁ‡sxt‚ù¼ Ì×<€‡›D 4+ŠÓþiÀaŠ–n¯ ¤—ò6/ùË‚/Guº=_¡Un"Òê "èÐ+žjϯ¢(ŽBdwº±á€Y Á»Je4ˆ7v›ÚÎG½êU¯ªS‡Z)a¢´³ %’w¾ó܉g?ûÙ'Ǩ E^úè©¿»¶ñ±mHP,‚Ë1žëÔmÀšâ3¸T—É‘9ÓM€ÓŽ"/¡’{ï½7¬³¹Žzã!zb;†ŸÝD­ôˆÏ ’({X¦[ ˆGÑjîÕ߃™î¸ãŽ@‘êåÈÅ­@ƒÄ=:ˆ£Ç(:¯F…¯ûŽÁȶʯ€ P¨ðC;·a–óŠ ™KWÝT¸JüÍÊu¨=ø¸Ç=®à¸ÃŽ ô@c‚6ÂGÞµàp°%€`uñM‚?~€îÄTVWºÛài‹4pjŠ1“v÷eÁ¡¿POlGÒ圄)àT°Ø«|65Ñ8Ûà45|)ÚFÖºyqBRqQ6šƒR ÂÍfIpcä¡Y¾"Ù\çyOÃnIžiö|å½ªÖØ‚þ§|ÈW"À¤í #:å[ͽ|?/(—é7”|uÑO“sèvƒjWÖ¦F2ƒ¤èF&ò9‘mQ–fçÞàȬÃ@lì˜Ø‹ÉÓÈ~艆€ˆ)©ƒG|„H”p‘JлLËÔÛ€!EªRíŠ>ƒÛû”“ŠRTêÿÄO¨¥Ì×$vÚ6HF³elÛgdÈn›hlÆ<€ ›´/¡±[˜Œlgb²µmpSØì£¢fÆñ“¹`4ˆÌr˜ô×U3>S³KÉx‘ÉšbÚŸ‰cè*°°uà—ŽùŒgÇdTuÀä˜2&—ᵺÎÂ,3ΙͤOÖ¾2ã„yÏ©ZŸÙïõÕXXX(–CXV¬ÆâÀmáÔÒÀÄønIeÇžv«6X1p^ŸI¦« é Ô~ÐMnUØ‘4¯ü2ŽDŸ¾{y—ÚwúûsHUÃ=_ÇOhiLÚ RAíŒ,jç1ün‚¨ÅªÓàñ9ˆœ‡ôà#$}”Ø2P.fÒ ŒJ/‰Á8' ô£ƒÝ ¢jåT p»q¾GBµf‡ü9ÛEÒB4ÿÆ£Y'!fRFƒ  ² ÄÊLsQ@IDATÇêø3÷¼@t ­JŒ‚Ó¦~åpKTm9rBL›rfйÝKšÞ~×]wýë¿þ+"Ap11™~cìQOâóÖG½ägª;¢¢)#%DìÏÊ›Ç/:c:è–÷ NN¯a n ™Î¤“æ yú 5>â-v¯Ú#Ì2-Ö’8ŒQØ…iX‡­r4.JÛýJ±Õ¥ñЉ”Xë²7¡Oñ4ŠAÙzØ•ÚôAÆ45eó+g…9ìº P#Àh¸Ïb(6õ¦ä#mÛ¶mb³Äa³}l"[iÛ­WlC›Ñ–´1Çà›m›A&âû•øˆ8­7—eÀ\ÃÊBÈÃÃsòò«uɲãp&&`u¢"Ó)AÞ©NP#OR4þ‘£ ?ùÉO†Ôûz·éÈ÷mw®y…e [s"mê[$ÌÅ1£6½k’5—†/ê é¹¸;>ª céü¨RŒ©=I„v×r>yË8ß.6tv‹Ý>ù¦˜&Eb4±ëhË´Ø“¬EŒÂ.LKp1s ñó+zw %8Ùþ¨²S‚X —ˆ zA¾R êAI¨ …¡6”§ãð…®0;a/CĹe”Ž:ãg‰WÄô1€ù‰ID0ƦƒÉlN»rwqÀ©=ögÒvæÝ¤L=ƒout¸·øOa÷ ìÑ¢ÖõÅÅÇ*VÊh ãj,U&ð*éQZ£ÌRÇÆ¨P&2;` õúãÉ”“!ÙTIŒôhã'Eå€ÉK†¤‹D#yR4,9Ö5ݘ¯j«ÈC†?oh®f~•4›““pp)ÌOæåP¡ÐŸlºÚÙÎ"¨%¢•?ká'¤D¸†ör5§yu Ö¦Îj`¦^ñ(-'ƒ P¼ŽŠŒé8Ÿ¸ÿ¡ÂÒÀL ÙRH£8üœ KcO›—ŸFIhWfg(‰¿±¤Íx‹ÍB¹Þ–î:K"m•Ò.ÆòGá§ O‘º]ÊÈH5¿Ê`ÊoÉœê7½éMá§‹ìÀÉkÛŒâ·ï}ï{‰Æøö¦„U»¦¦†å³*‘FÅ`9fã²±?oœ=tТdr²/|á ‰~ìC{qão|#Û2^O›Å.Îäö§ó+6//[L-MÀˆ˜eök§–)FY2’ëk#X» 6éŒÃ² ìK‚xŠŠï´èû±çj/,Ü"Ÿ…¹`4bm,‡1aR¦)˜Ƈ "úrÌ-Ì”Õ1YÓ—‘ GmÜ÷¬e2¡: 7t™{QÕ„[¦¸ù®loèd´c®™…ûdÞ§Uø/À€þ;@sF&‚­=™îZ_ÃX)£aᮥÝv 1&‰Þ(á¿ñ“²®u'˜ ŸàBJØ{Øìa|®K*«Ýе¿†²éB%IBË »M꺬ªY¿5¶‘AÔSñ[EQš 2~Ò6,‹°æNL‰( *](Þ%Òõ¦!¨"r˜8¤dÐ]Í »Ü¢X©wŽ<‰_M#_…}ÄñÂK¦¼GûcŸ´á]ÅþøÿÙ¯{Ýëð_BÝQt䆠Ÿ»Q"Øèĉlç¯ÓAÀ#…ÎI\Á Ú2öBÁI,…ËEqzøþHR0±ÅÂÓÂ#Êÿ€°ÓJ#S-³ƒõ2§0£Ð–*4Êg}ïqÕdMd¥Î‡X¡FRi)¼.¡Q=Ì1å5#(ÁbSøÛÖbÑÙ-nt{U=#›ÂD¦3©©0Ò“6RK¶%Œ,Ð2-Ö’'j9X„QØ…iXwäT`8¶c>q ÑŒË'8â#Ä>ƒ@ ´ÙÇžÛ6…¡6”§ÄS*ªEÁ¨ÙI„­ý)-Õ¥ÀE™©4ÅÞ}bb#Ø›{m›Å–ÙRÕ+¶›Mgëå›Ñ–<Üà^[›ÈbpøØ6¾`cÇLƒ‰ÀIæK‘Í€ÄÃgRÈšyÁÀÉduÉ» ’ÂFY›êcuî F’©4TÌEL¨_ï¹ç†wœ‚Dúp–öWj1Ýñ÷Æ»Hp|’â¿¿úø¯!“Úþ˘8³¾.,ܬ”ÑÑf®öâÀc‡rö,ÑåÂÉ j§à¬’ñå)¸ lá"ˆ¥%}ì2ò",ñ'mr— 7ù=)/³½Ýqñ™úZ®Ãg 9ˆ68X‘/ú¿õòîÿô×A.6ŒÛÚîŽáOôu€JÍž¿úo|ô1 ÄL%òIZÛzÑí–áƒô3~…‹Ä0PnŸ£†,2WðApF¨TÐÕíÜñLñþ Ôè'e ú-Æ‘ùÒR%éÀûßÿ~‚ÎO\ky˜Ü¾  ÌñmØWÐ=†yX é’r»ÑA0å7e¾Mš*°Ii¹+þT[‰®ö^é£4ÿ‡¶È”j5ú+>ÈÿÄüû2A¿ù›ÇÁiæ}y¥Ÿø„ÔMÙD½ DZl jfRKë¯ÔɤM¥Ÿ“O:¼ ÅT±kÌM¥ö oA:Ÿ>¨û8¸6×…Ô, ¾Ç.û(ìºGXØ•Ü×éÆ~ŹâÒŒ «Úœ^IàB‘¢[$5ªSÚèùÐÑÒ°‘IpT'ŵ4s>Íz)¿ž­¦ k“ãvÓ ÓWÌ·YÜ©©Öc³08»A×ÜK÷$jœ¯´NNøäǦ›k1”rQ˜Ãé'#3ö; d &uʽGŸÔÌ߬Å}ÿ ÒOA`ãÐñOÙª£KæwÔª229æ‘fÚG¤ã#ÃË™çú–$=Yi:+ÔJKerÅÙÛAÃÒÿPÎmî’zj²62„™_¥†ýG€™ò©GçZÅÅÛÂ[âø®e.Ü€é0_Lìydí å»èùw¾‚Ið+Êa—üÊgè+tHv–_–#P+ÕÍ-yùU~û ¾%{­OÁs#\2Ž95 Ê8<u؃tf!<4}B{\šF3äVÁL|H¼9¨B=ÉàÖ89o6RÒ6¼ßóL -Ïø Lùj¯‡)êEý R=ñÿ9ŽŸAüe×H•àöÐ"õ!¹œlrÄ…ßö¶·9³7ž^˽Tpç¸wTéüJí%ª,âv:f øÓðÕE?ˆdÄP4l½”t Ò@†Û‘„0ä!©F6âÝk!ã€i[²…»Æ ’_1 »0-í@sÛ1_Ìœ :>aõl*ÍÜeooiƒbPJBU( µÙNMÁ¨eë]”*Žrzït[’”’SõöÙmØ,Øhã47Õl+›Ë³Ñ®_Tè´›lj[Û·Ímv[ÞÆ·ýôaâ‡>Û%Œyi•£1>»Ý\d²jâ˜2:Öž ݤ\îòS#¦2f“ ¥Eù•iÍé>£5âšn 2³l &Zç˜k¦›ϼLz§è¾`üý` Díý×P"©_.úOÄ%½¾‹‹y,‡ð1/âµÀÛ¤½ò+@›ê£Èª‚  UÏóÌ;=]̳jé‚8eå%MTõ«gí‚'j3XR5ž‚{Hæ6ÅHŠ©ÔÊô?3`JÎŒ×èŒ RíÐt€T"9›so™(÷ +î¼óN²“wÊ…HŽ(d6 ¡\ÐçvªB»ÔŠ¬Â„`®Tchô“Kƒ·eÈ4ut£ETˆFLIäDªÛÈÃÊ/• Hcæ)Fo•ïªOx+T×Ê$;@MöšD¸ºp°ëâG( UâTÈ6+êr^®Ãa*7 —=ôp9$¡ùsnªdì.!ŠÑŸ l ™‹ëxÕ.·w/R¯@à3ðv¦¸\GnÃŒsÈŠ†C®Ä'yÕrÄ7$Å |‘験½}lè ›Înq£Û‘g(Öà¦0‘é¦eŽƒ¤lÄ[‚…\L™€QØ…iSäð|FB!šû$ôÿ/a‰’@‰5ËçÛƒzÔÕ± ÊC…Üxߦ~”*öÉ2(*u¥´®S`jL™Gz¶¼š®Ø,¶ŒcûØD¶2l«ø0:Ûn6ÝUeÃÚ¶÷’)ñ‹Ý¶™Æ!”X8£q”8í^G‡†é˜£1¼6Έ1eÙþ´‹‰‹20zÓªGÃ8š w1§øþÌlÆÌLp˜•f£ÛLtl#£Ítç™cn„$ó£™©Ïøжÿ Θû‚†ûÏ‚ìÌè¿Ú¿²¢þ‹9JKW½Ú‹‹^¬¢2“q^_eð’@ÿ?´D ž<18u38<wŠçd"<ž×Mn\(…V€a_Ý(tl¥?œ¡ÚA†ÊXÍ ²„#`V°ÛE©’wŸd§[>ÅäD9â”@{âj ÝÒż*Íò”œÎa¸·ý¡[Õ#s˯=P <±µñ.Ä8‡‰A\‡œ W0ô¨¿>"j*.œá‰U¸h!îÅgxÈ µãD'm÷:êfFäb·“pwæFuhˆCü¤¹µÛQ¥è/ Žÿ“B,Ä!O¯ØÚO¢[eä+¶`²%}EüvØé @)ͱ´þ™"mIn¤o4á/Ÿ‚W%à ֕z0”Z£”Âþ š‘Ý4Å“g¸O×¥ŠD ªøäºøÕá:K€t}¶æJïò,%£Ææ'Pñ<“þá?MðâGKt|ë (‘t*30%m0Ae ô7tñø!½ñ¨ÁW´A,Á˜ÄЇ¾Ö_r—T@9„dÊa3EÆA†à<òi§ò§ãDÄ*&éÝ}Ýæœi¢É­ ÿ)ª?jc¢ñÞó¶Û)*â "æ™{¹%VÁýPU'þ¿m7¾º{MDl7Ô)¬Zô’a³ï\$ÑQ…Fã&µÛØp*’/jê"uU†w4yæíϰ?N0ZˆU †G½KŒ " g¹—ôEƒ……%ÿâ/þ¢£ÑX¾ã™+xB‘¢xäKjóˆ$ûU´™ Çöì@¼:ý=°ÌØ¢˜kÏðY·Nñ4¸ÖxH†Zƒ,,<9°ÂG PI‹×rðõ´uŸÇ³aèìX ­ˆ7¦ƒÂ Ð \Ëy€†%¤q® œvxh+_b“'ÉOótÉT®ëà°Î=èŸ>Ú¿wgƒƒ{qox†»ØË8VêX×Îsq_æá ò<Ç©Ç6îMï—÷˜œ7xäjrW L/qŽ¿!”¡?¸pÃNmXm*Ê—/§Æ„PÉdä†~ã˜0M:…ã×cq‚–Ý8~Ÿïxÿ˜ 1K 々ÿ&Â[¸9>ƒØÎ´_¹+räø´þ§­§„W ¬ ÿR7àýþΗ8<í¼vº‚`Gì¸bDügí&¥?Aó^ÀbÀš .Óái¨ñ+ ¬ÿæÉEâÆV¤ÂdÝËIEÇÛµ…SÄ»xGÔÉŽ³XÊ9õñ•º:V‡EÎÖrwˇnïÊkß:Ì€þXì‡?üaòê&ÕY^ùs¸.+šFêäŠpémžAè`@òâEÐ…<‰  ù£`9:sô•©‰FQ0j`nm5J£Míq÷u‹¸¬Ùp0BÆ^S’”n©÷›sq<öç=ïyv+½Ço¯ìVýãFR€‹o´§NÜB¹¦±([ÏzÆg"\ƒEårÌhcÅ-|æ3ŸI])g¬®Fþ’P!çUòH*1¹—ûìúãª%]V7syèp化ŒíÀºÏ°]Ö1OŒ—î¯.z¦£öÜÝ<˰}¨„˜L·Êf´»q`;‚UÛût4£«xÈ£{ÒPqi{‹°Ýö4$¾4]5Ýv°mLż2`8:lñÇxžHŒõ“ÙíbGÒÁçø‡ »8Äo©}0WpŒ7K÷¢Q!‘yj0Y Ε͈3~ 嚀¸Í2c]ñHë ’ð¦+1³1¹¶×pžín,e ¸»tÌW5TùlúÓ(ÑQ®øÏÝÿgFé jø3;™¯>uTìÇD½Ò¯˜Æúê!ýi5XácCŽk·ˆ|0P@"¥5Ž !ë5±ÀC}~À”ëBbb0Y»IÙé|…Ï„›T#TÂAÐ) 5 MÄç„PM¾ÃІ À(¸ Ä÷vî‚uRÄW1_·§³üÄ­ÏÌ»¶rc?§n€oWd N©ýUÃzƒó¸1~+è7»ÎK±:8~DîWý¡+h>Øhó¡jyño3 _‚W€±s¡!¼ÈÛ‘|eAФ`#vq³Â8™%I¬UŠtÈ©‹ã-†#Kë(EXÛ³ Ët„ ô¹¹ndy˜@¿‰¦»x&BOŽƒ’ÈpãŠÖâ®DÀŸ{@±s]Ù NX!:-ˆÇ“ôï1¾ü‰ÅiˆËMÀ÷ ~ŒBý÷wå¼`1§Q˜.%Õ“¿‡Ãn§ÜsQì.ì"”³6ޤMX‚Ü*Ø´f°)¤è¬ ’fR¾©ùñy¥šU†Jýq »1œ£žtP·P³Þ’™Æ¥árføo@s.Ö™n¹éW4@Æ|{^M£½¨‚ý÷Ãóí˜V‡EzžWvƺâéÀîPÜhÆ4Z<ŸiFC%–(*åሞø/l…ùbëM>ítûõ_#wzˆ!¼”x‰;íâ4‡Ö^ŽX)Ci -9–I²ôyÒ·>ûK^eÆÄ|»˜2à=·(?mᓾH>e(Ã)¡v¿‰°ù•ßËÛÁóIíóÕ'S@ŸcppL(ŒçÆ2LýÌd·VH UhÎû í”#"=xr2™eÐiIcå¢EÓ-îà"_ÎÃ2?©Â"»Õ©›¯ü.Ḹšã¯dÊ'óSˆÕÓ 91°éÆ£Ö‡¦Y‹Gu¤ÆÙcÜd0ÔÖnRûdê2nŒ0píeî%a¹ÝS€ñÉ ]à¡U³‚m=OýbK˜ÍÎsöÐAÔQÚ5cÉ-ÁSÂöYÅÅG5–Cø¨ß"þqÒR[E)sðTÕ §tdpy'ÇOZæÈÝøfÁ‚A<œ¼O"ÝÆ°Ž’3É#âû9sÂQâìÇÙè0ÏÇõ† ˜œ: †ƒ*‹U7ô•`PèA‡>æq½2rRŒò¿~>ýšA80Ÿ¶º5ðD.4iôSÿT%)©iÀ¬Lhðé}®L}òrõ¼úϘž#|Ùí좔¶æšYÈx·3|Ï•m¶vAož—ïvÎE¿*SÑeB“BpR.9½ ÷v 0·L0ð$ÎÐ>mÀÇ|Q ­ùo2H=×r•|+ a Î-ŒÈ­å·Ðí¯’£LEŠá‘?Oƒ{‹}Ú¶…s}MjFJ²{ µ«Wᔼãó¾§ŸçYgíµ×Ðûìþw÷êîe&A„ŒÀAî!W’¥((µœ„…ûa¨Ñ e€ )'~]õX«‡mW ûÒ ê'äq#‹ž¥ Ûwˇö+ZK%[~ã’«:ŒÔ»VWMÌôLÒTM˜îljÔ,Är¶ú”Öš‰zË œÇç!z”¨ÇêázÄ´Çí¡{ô~~ k¬ýTü`ülæ¿%ûBú¬Ÿ´[üýý ý,Çž²Ÿ®°Ÿ±suîiú©ûÁO{)¼^R#âõño¢WÌ‹æuK‡XŠ[¼ŒKø€Û­ÅCÉ-^s/»W~2|Å"«æ€`# [ª4(„¥g[ëÖ;»s;¶†¹åW„ÝazóW{œï4#õX‹ˆÙÖ» OÞMŒ:#‚ŠÿÄÒUbï˜|= ?ˬ…Ü&4ð0Ãb‰†Ó[ §_—6 '5À(j¤_Ž_B*û³)ÐØI)Ð áNúàzÚ+A>EBéä¤Ñ.³±ü+!ÇÅ’H¥D:Öh¸Nåç¦ á‰k–®x¦q9KŸ6Ü„¥Q…v9ÔkVº }Ú]r#9)r!¥‘”©Ò(5t§Ó> |"Ÿúde瘗«d/ùëÆUÖ’“Q;-Iº„Z•Õ`,¤±µ§1—Bû<Ç6)“ç¬"ª¬ÆHd[c‰üç®Mn ê“ ÍjnÜLMœ@-ª‡ èdùˆ_–“–>Ý%(®n“Ƽùž™mžNn¡ðHöcç´}›Kã#ÓžÜO=°÷²–bìgÆA‘ HÈ®=Øš›mb\È,PèTíTn1K2^,ÔÓü Ⱦv´Æ9W‡ äcôñ7V®B9«^KÉD.AH‡€ó6HàÈŽøáÁy(ä1MèQz «‡«åäw•¯œxý0ü<&Ê¡Ÿ’ŸS¸Gû±ùÉÙOóó«±h2~œ~¢~¨“!ÌÐOÚ»^ wùÙûñ{&uîeñÊÔ¯Â]^( ,­Ï;¾†^m/xiæf«¦ú§]`5[ÌÿYÂp°£z‹Y–4ÆâÊJ…õa€“n1É"iŠ¢I”4,ãÕ¦'Wã‘UŸ¶ì¢úEaìY#V_o7 31“¸=hÐÀƒF"£ðQŒÔÐÀ%=€ž"ÆXŒT­õ3«ö]h 46,Z!ܰ¦'¶êÿ0-°_ƒ‘€? Uú$" 4±U‹'ÞBÝ(_hDsDéW±Ä¥[¦ôÈa>Ëf,Œ§LòD2E1FbSbìÏÕ’´ 7ÿ›ÌœtÂI/ÍØ§?ô¡ëª2qMlOÙàÉ." ײÁ“;I®²tK²¡aòÕ[VZ-i,6 æ‚lµ7Mn ¢Ã‘GY’–9xjÜädú©~R`kçÉ6¶$Ñë×'^göxâ àãŽ<ac6|$œ¨žI~òëݬý„"EÏ]ü^5 ä?»²úIDÃÏ>‘…˜È%Â7¯ÉðÓs¸ý–¹Žçv·D`­Nª`/ÂïÄ‘ÇÍß ñÇ£j6¬”šáo®Í6`Ù»™Ï©4™-" ‚ â QmOMZ",òNž‘6†ð8<Æò˜<,̃óøÜ2öãvÚãöÐGoüIø©øÁøÙÔþRzðÓòó3=ýýy„†o¤¥Ÿ«­ÉL6Áü¼ýÈýÔc“Jc/‚ס6 j&^¯Ï˧¥WlÜ]ÜŒ C¯¹—½È‚øXÁhO© `HAKì“©cSš¢\YWí%ŽmªŒÝñÒÏò±ÁRÕÒ«Ì¥…Ÿa³~'èVŒ!cË¥7ºZ|‰ò#ô€¬4C`øØ~úÑ'5óÜ5Hœyb­ ”Ô bРܸô”æY3Ï”<5°•Êþl 4v. tR™…¼·+›;˜LÈ{2¸îàyôðM¦@S )ÐX#8è¶ðúšw«¦@S`£P`AJ÷2µžGS`%) ù#='®ÖWòù÷¢›M¦ÀÎJ°¼@˜ÀÅu =ï¦ÀJR •|ì½èJ îx‘‰E‰ÿ«¹Ë×Ñ»L†€Iž[’`3ù?ËCOjþ׿þõ–.H†O×Ðøe‰YJr9Îf’%øòÄIIæw1HWÃ#N yÒ¦|ŠRˆ“’Kf([ ï/KH%¿&Á'óY ¨¾sé“‹šÀ§µ|´ø­921KÓž×7¹9•äÁCÆJšgÚd©Òç-SÃÝN õJ]ÃÏŠo•žãå“ï¨c©ËåL‡Ò-ÚÈ•â¿ú4(¯0É6ç“å •ã";Qͬ©¥p¨…h,HŒ×–ˆîºåÂ'HÉ!i§”Ùày¦ ‹’Î^¢üÃ;ÌĘäÝ+O#W4© P»MîÚnŸ·E™ƒissš¤f(Á [µØ'?!™ô×ú1Œ„ÚÅÊ–lá–H È‚8H„PÈ…hH‡€õ*m·—ülüxÌÁÉÏÉÊOË ýÌüØüäüðüüƬTn4m?T?W?Z?]?`?c÷Ž!¦åoÕ£ë©çëÕð‚KÑ›×ÇKäUòBÕ@·^7/W/Sõ2š˜³^R/¬×¶¼X½Î(ìÕ®N& [¨± Lc´Úë9–†¶,h²Šj‰qa_埯ŸìÇ€%ÆáÓãÆ*1Ll³Øûä7Po4Zyh0g,£6·ràÇÆ‘KWɳ7É¥0|ló?@!Ä LXš¯à£–“ˆ4iéÓ zÛZ‚«è‰Œü™ÁYÝÒ…¦@S`£S`òÚ÷צ@S`‡P€L#(¢ŒP1Ñ xÇW\1ŸŒôåÅV¤·£8isã7–X¹ B‹ø'è8³Ø½’R ’z„l¼UÂ{ãVÏI2!e¢@—H ‘!ó¬åTâ‹Ñ¼ç,‡H6%AÅ<¦.Ó&öÕ¸E„µ2$ ¨bÕ¬Å(“µû*3„´Š•$ƒpIE“yK5¢â-÷F …(?&uÀ¡ªRà šøVšð%—\"ÙFéÞš™€d‰f"jK ÒÞðd'OË âô¹¢³Ê«‘¨ÃŸ¸ šÉØf ËDI¥ÊÍë åýt¡±Û¹V$ˆð0mZA¯~Û¨€¤‹¼ˆŒÔŽìˆïx‡‡²…uó~ ›¼ËÏØ9¿j?ïQp¯/‚×Áм^¯‰—Å+So®f^(¯•—kä ^=/ Ô¼ž^R¯jžÂÂC¯ùÂD2nÁ°ˆ2☀™,ÌG…á„…å—D ›0VVk1[ŒnácÄÃù1LlÓ(õL‹©ÖbSÀ„±bå°e,£Æ®ÕTô£2ÖÊ;i¯ÛÇü‘]ÙZ€hÏ)%¯˜3L@3Ò\¢Úùr–n±Ð°t‹B„¨ëóÆ]Óh l( tR™ õ8z2+J c<(%°Ä礇1k\ÑEÍÒ’ÔRõU`Ÿ®­¶´´íà0e ï!½½GY ƒýß‘{’“¦Dw‘8cQ†úië8ù†I;ÝúŒ¢˜¯z®z‰.*{„²Æl5O¦ý¤Sw£ÅÊ$1î¶-ÍŸ™É@NF^xš‰P¼=‰G’ò­Õ§}éHÕ5“‘'ÃA^6"l,Ø´E+ÛöLlãHøN–•…O¶ Bd)¨µü­Xð°HÕRV°ñKÀh{„ñ~¯½öòLiò¶_ƧV Ù¼ ËC·©‚N©ö;”¾ò /”ƒ¤Eºñ׸ËÇäaydœÇç!z”~<ñ5ØŒŸ„mòëòÕOÎbàGè§èégéÇé'º%c­gz^4¯›—Ϋçôz½’µi¯/¬×ÖËëö"{Ñ¡X“—Ý+¿Ö†!F]D§Ò6‚™”’9>tÌ J·˜Ö;¶IYŸØZLoúD(LoÞL &Y¹Á0O*bѤæ¯f\,V…3̣Ʈµñcàu{¢Î*˜ƒ´~z F7,¥nQ" }4€Ný*€Ñd-~‡Y,ó;LKÐà&-ûkS )°Ñ(ÐIeFØå¦À g$Iüx ‘f¤ò#`IïFp9äCØÎkB47tí©¸µ±Vmx£‘‡$jO ŠáÜŽbT)¶öÓN;„ÄBŒIúÇ­ËäN{ø­^^{<ŸŸk¯½Vº9†|Ç—¤øeìÖxÀؤkh·0ŠûJd‘éNHaþ†«6U°ãÁ,s2釣Ÿ±R䪙‚µ0™ÛQfÆær†,EÒâø”?©óä*ÔF.þÜ+Û¡Ìv!~Ýä§Õ^c».>mŒmyMhÈþø€e-FñXýUçü¸¤NEX”äVwýõ×£°?‚TÚÑØ×ÉÜä*]‰Ã‘^KOVa çìùš©Ú¾ ?i Ä·ÍŸ=–Qî¬ wa €Ÿ ÿFB¹?gÄó6ô§Œ¥xA¶p~64.¦^7¿p΄x…sR?l,ˆ®åFç•w;%DFÊ1ÒŒJé/nyauå ÚÂINn7g£Ø~ôémõç½HÁ§×Íô¸Væ.n–T_úR5N{;{HG;Ê¡ª¥6˜íBvg-¯}íkéêö6µ¼Ï}îCK¤ÓNfå+vG ¥p*{ý­]¡XkÚSêh•¡I2^áTIÉN± |ÏÒÉâÆ’‹†¥çF¢¶4q{Ú zÚ) €vž5ð¤Ž9æÌ–˜öÖB©ödóµ>åuo¾š€Ó,jsU¥ù# ]ѱ“6Eãâ±Ú~Ä#Qt¡)ÐØhh…p£=‘žÏ Qf³é²g[3›.`²2{•­ôñ!‡ã¿ +÷ž|÷B™PŠŠ(h‡æzpƒã&¤†y˜ÐO4Q& P8…þ;‹™+‘²íEÁ| :=Á×Ú>N8á„’-ÈvÇw™C{¢q“‚Aøó5t.Id—¢’•>&[½çÑÜ ÊxL¶Ð5¨ö|–’–Yé6ýz„­ó?r!–„G¢(–Òå­’æFÀ¢âj`HÇs :òP«¤[™¯z «]|ñŨJ&Î,é±Br²‹âéPÿÈÊ®n¡v—Î~f]t*«‰Ö¢õÍe»…·wåJQÀo’rèý¥¡y=éŠ4"¯aér[~ŸtL†~è7i“Šf/®^+W±2Û¬NqÃvÕ¦Û™–™×ÊF¥?¦ 5&Ì=“.——Ë@tT=`YP>ñ“­þÞ¥úÄÓtlÜfHÚÍ4< ë`Ûµèj¤ 7Ö7ö2P”ƒ­Xëlæ…-;°QÿŽ ÈWÓÀdØãŽ>úh{GUmÄp‘ÍíæÉX¿S-mT²N–IÑÓÁÊJý«ù0̤|µÓÈŽ ’ò•k.Ã%Žç‘©at`ˆÌ)‹Ï~ö³Í‡~[ýt¡)ÐØ8h…pã<‹žÉjQ^‚U @ ºœõSÈ ä‚Œ§)1â&Ì ¡‰n|ÃF§ 7Jç ¸¨LéÌÕ€œQ4¥¿|%vÐ<)$dŽˆ&‘3ÈLT5ÒÍS¼Hd ª Xâ>ûìCõµM®(øIñ1¼ HአŸ»l<Â8Ì ´1eÆ|6vØå¦@S`‡S Âþz«HPÍ3~ÛÃ!»SŠ Ž'~ÉK^"{ eª]ÀPØ.¤"0L Ê-$·ŸÔ¸tÆgÄ(©… ž @6"¢ñ}¢sÆS Ø—îAPØA÷šÌÌdc>‘Š&T @IDATõ¦†,B{ɾ¢ô7’= ÛkÙä´L̓YÚÄ´Läe» 3·î¦ô7k™ÌÖF:ó¢fhÔCv‚#IÙI0e†÷ È”…4þ.ù¡ …‚Q´´~Y¬öu»9X¯…û$Ÿ‘5GoÒÉä×úJ.´%bßú—8FÒáZ»¾)°)à—ÌÉÙ xùå—ó´a^– õÂ`ÄÔ¢+ÜÏíQ´r»_²÷";Wëéc‰¶Y·Ûu´ç&4ZÁY‚Xsl{¦ ΃¥`ì&´N•dD !Jæ0[ë“ѱìÛášîVÃ)Ûñúj”°YŒK¡ˆb êéE69yà«ià3 'ö‹óØÉÌÐxŽ?ŒEWë_þd’ ¿"¾ßÅðq!> œŒˆì6-¿X±9PGFg—ú˜dFq‰ß¾e¢ô¿ª£f©Äš„œzê©8eçªY1½!»û!;:×¥.4š­n„§ÐsX! PìÂÅQ“‘dF6*‰ÁÆ6¼¤ÅUÚn¶XÖVñ{œ»HTöÁÒÒ—F·u‰ûÉ “>‰$ òͤÞWq€¼†FxÁH|Ì1Çd±4b‡$ûÙy{° „t±Ùs­díf“¦€U32 £µØ›¸3‘l¸¤r|š»3¡§= VmyØ#ÓØdÐØžiLX‹ØEj^z¶š°2Í5Ü’Bˆ çjo¥P’»¸¹Þpà ‡³¤Ÿõ\Bê—É€dé×sc·i l Ðèúì8>YO¨ˆ[ñ—ë[ßßóºá‡^X/+š¿€ËWª=ýG|¦”/ì{ì!`˲ßÈKÓ(Ú`ûí·o [pnœôÌB‡i®ËG b³cú©–(À_ƒW®’?‚3*øÂgª™‚Ìc=–7~ôjûrBï8x(`Y¶Ôè¢ã)›ªiª Yw]Ý’®kÑcÏâˆN±daÄ÷Ò3 œ%ÎÂm –’‰ÎèÉÜIiÔÒbí^–«ê–̪ïm 4¶ Z!Ü*dìNšë¢U„kMvÀÖuúji;‡'vk.RAb¶Ûò›¢]VÈ.¶È袜HIšÉÔ‰t0²Ž1U’EXßUF£…ÚΪ„1“yÑEŽ•Dª Û¼JÙX‘)º:Û(ë“J‘>Í´‡6šº0›Š´ÇÄK»ÝŽ"‹ØBȇÓþ[¸?j!Ùq¶"1À±&å0d}†&ø6+žµ‡kCYÅ_W×4šÛŸ·@ZÚþ“ë›» @/•‰RI…´álƒgŸõǨLŠªUs^àA"Ñ€VF<|åà «%Ì–!†ÅUɾ¢, LõêA¸{ÉÕ›በ¥KLã<* "dh jÜK‚©Æ%‘$Ü2ùJ•»QÚ˜›”]åzT7’<ä—“r€tH_bk—4•ýÙX øÁûÙûñ{¼^‡[úí½­Ë_X ÁKíÕ‹ßë 8†€-`Õ-¦uðfÄFÔ‡›¹ŠÉ¸„á`;ëgÆÅÖ07,£Ë(nDZAÌKıÇ<} ÛÄ<µ44všyºŠÍb¶éû­‰¥‹.޼xa„.aò:±´´€@ P(Ð •øp ”h V”ýp“Ûu†€‘¯€) |,°•6>m`µº:€ Yp‘ãÕ.7šÛŸ}0ýö§y¸Zà6É– üX…cñÛ6'/¼çÆÔÇ6쩹êPÁ’:è jÃônÐ.Å6£}'_«%¤ç;ª±¡uî’?ò× RNj|’Òø½ÿýïϽlí£•:ÍÌ-¢8g†'ؤ7݉;)‡1,Ø­Õºj”%’ [{èÊN406WekàæÄ®Ï'MòÉÍ "Õ’¬ÆhMx²å8ðE”Iž‹©zëæPÀÌýe± 猆5‡yka#'úp÷§dW³žWšMðRx5¼ ^/ËÄö4¾S^´â]c}ÊÞë¼­Åôæm&5nÁ°Ì‹À(°‹jƒ`&X ÆR¦%sÀv0,#ÂŽ0%¬ ƒÂ¦0«b/Z.YKR“Ç*1LlóÄB«+3ä»Íb¶á±8’¬Zuo 5v­Œ86Ž™WL«Çð‹† A"ÐP,.ƒ‚ J¢+—ú­ôi®Êfžª€¨Q\mãÕ” í^sÎf)ˆ”óf]Óh l7 ´B¸ÝHÝ­øÆˆ- |r%‚»¤Ðûk‘ƒ³ÀÂ4óɪ:pš±X»T¦\pK©D§h@²á%5†©$ 5R‰F#-En`ж“fJ54c6Cò¥—^Q†ð1š™ÓLÆðñ–ºwyÁªK<Ò­ Ȭ#åû49€€E(”H¦1ŸlÁÍ»×`) ö‚É`5Åa° Œ;š¬˸¢äè CÃÖ07,£Ãî0=¬ÏЇ`Œ¥Í'°VUÏoÀ®Ó­«Ø8f>27•>¶¯Ï@€å$û4€P9&¬BXPPÔ[þ¸LÐSú^ < ”A «É807¶‚VmJ`8¦%¸šc³.7šÛ­n7R÷@«E¦å6ì”JÎâ `Ïi#!œn7b¶b6ÆÊ„¤àwP“&#ªmlDã*°°ìÊ|œU@êÝÊ. „Р™pÄt¨A´5†ðÑFž«ëùÔ!ï/‰ãä!‘$ν‘\4£”¢zÒ k1qdâD$:‘„Tê"›1©¥|¨,“Œ%Ú‡ %<²²KæFÔ ß”à¢%…Õ_ <{ù&…0$B ^R>6ÖÀUÒ ÑðƒüàDâP—›MÍ£€×ÊËåó¢yÝŠ¿ï¾×ÓKŠù\Ø`ÒØëYa¨·`˜Ö1¶Ÿ”å/•ئ„5aP£õ ûÂÄÊ·sÃâ0º‘Ø fˆ%¦+L«Ä0±MÌ ÅH±Só´®Ò'“Yþ5²·i]ÕChè+ Èí @X‹uŒòÀGI€¢ p™èi ÕèàinüJˆ{µsÀn¤(t5ƒ 2¼h–—ìØ¸ËM¦À¶¦@+„ÛšÂÝÿ*R€4 ?'´c]–J!$y\ é!_¹ÙÄþÈ$…iwUH^š±——`¡¥¨¾@šæXáÚ^ÓŒ› –•ÊêG¡'íÿ³$ ˆî`‰ÜÈÄÝÌ´¥×s ÖdJ‚…dã–ö,ýŒÐ£Ì¤t ¼¡Ò@oäA)Ò÷J+±å—¢¸|ÂìÙºB[HDªö¦ŠkõS«K{2®@ …B ,k7K?©e.MˆÐ_›M­K/WÏ è5ô2.|¯)6^a/òD9œ¼ìÅ"t‚EŒÛ†ŽÔÃF0 cÑU5^RÐÕã¾01¬ÌíØZX¥u…é‘ÃX"ÆX¡&¶96õ0Xl6ЍUÔT1äØ×ZÚ’ÙŽ—Dx&È[W³v3yŽ`‰ªUc§QDG^ òÊ¿yÝs —j€ »êrS )°(Ð áv r±B`϶˜t¶ïhXÍŽ[2r>7ýDÈsà.öà"–dqd…Q¦qV„f¢84“ 3‰XF¸Í¸k}¢¦VB¦¨‹º-Ë·¥CƆ ÚÏ?ÿüºQAcrOÎm¯9+È+ð¦7½©ìDzz²þÒrÕkä…ö|ä#Ë=ɬü=/,›¹¸ÂÖèÅÊÒ/¿S4ñ‹?XlÌ o+#¦TÑIr ç@ð$jÈœGmv\o—›MH/¦×ÓKêU-M©Þn…ù ®fý¼cÁ^0¬£®ž1"ìSš0Õj0Œè/5XhΘ!–ˆ1b˜$V™˜'ª~BUÌ˹´öØrÖR›Åº]Õ8Q*Ÿõ¬g–2Wͼf•¡—|Ê'Ð=H·À$Õ-  `­š¶f®¢ÞH7Àþ´…®Ú±¬öàh¦C0Ú>E™.4¶Z!ÜDî!V…—]vYòæ±O3ôŽËÎÁ•a@Ž0L¤Ñ.7*Ë NÔX(Ùô®U€÷ޱò˜“Q R ´dz¥¤ÅbÍ]J GQÇOSªÍÆ2³æ–[Ëý÷ßÿú믨Ìô+;¹ÀžLϽ¢n8zñª"ëŒýOæï“¼C4~üãÿ‡<Ä×´1¨¯D)jäfÌ.ï¦I‡$$ç7T{WI3K&P·»Q·VÇ}k²U;Ylm 46 ¼¶^^¯°y=Z.1ÚÔ|Å:Öº[ÆE1ŸRµÇš0(lªöå+V†¡ak¾.a>.a˜¤Æf±\Œ;­ý´"5ÆkuÕ¡AíC†ƒÕ½ÅÀÕkIéRÀê1ü$£®©ZhÈeÀp€ JЇ\O•@ gÚƒ¶¤l­{M ‚ÂèÀÀ±V§:hn©ã¥.7šÛŽ}añ¨.46Ÿœ|œÃ.â»ʆzÈ!‡À¼ñØt˜'³¶(á%›1 ‘…émÜ}࢈ê*œÓš•ÆhÒ*B7›¥G}´ìݺI¸*è1L{ìs=å"ìXŽ_!ÛÿE7H!Û39,_–׉§Ìzzî6M¦À-¦À¶Ó5»ç¦ÀŠP€ÆRÁrë‰ »í¶5Oj¶a7êD¨7ȱB†¯²¢ÄäMÄx<¶e„HHNq1bö.'U訰¿Æ’Õ€RT_ŒX™N6€ß Û|ŠxpqF• ‚ŸUíL²»syª ÊLõP éÒíÄ'ª"ëЄI9UCÑu3å–ŒR•›,اEð7¼á |–:püÙt¹)° SÀËî•÷â{ýz·I^‘Ø &ƒÕ UaG˜ÒÂNF6…¹aqŽ©i‹ b†µ‰Ib•&¶‰yb¡)vº°óTbŪ% \ÂÒ+§—¯>¶ùÇ™S\·öÁÇ8Užä€ž‰;+xr5Põë†ÿ ³À#8 ;7ÂV5^^€Œ`bœR—›M­Nÿg³_þBöÕ¦@S`!.ºè¢Ç>ö±6Ö\å±i‹©vðÊäéꡇšÛa¼×ØIÁk*-l3óÕñiƒÒ3H)ì:ÂÔ›nºÉ6 œô`¯ï)OyÊ—¾ô¥šÏƒô Ñâ+Ë9ñE?z`ÙeëuX03¿&·œúÅM褓Nªà´™Û3¬š%žE¼€CìÄöc<¾×½îõüç?¿â'í ƒ1ùË 7OzÒ“øPýð‡?äòĈN<²(…%[¦èÓ¸ã³>þñg»ÒÄ8ƒé=[r¼%ó¬Kú±@ÙLr÷Ýw?R]íBS )°‚°õ÷‰O|cq@ü'?ùÉMî†Dô~ x2–ž þi#‘ºµ¤úÌ>¦@ ¤Œ9Æð”SN)>I-žWÐ`ƒ¯6Icáçö ÁŠÝ9,1 ³É‡áÛ´ÀjéÈD¼Úaø¯~õ«ùwœ~úé#d U·ì½÷Þ#ÄÜñŽw<ñÄí+¦ÁÕW_mQtl“·›a ‡ÈÅ_ìÓ&mƒ.¯@*P˜JZ:X(õ—H3#ª«\`úЇ¦}6š[Ÿ[]Åì›+BxœÄ¡y-©| Î&Vä˜{}²ƒÌjLGª=1špêvâ“ÎY^Ó”Ž#º”$çž{nõæãÑ8-PP'|Y3ºÎayÊëá&£5—\Bn E±mË/—QLx¿ýö#Ô\å%U¦kRµ¿qæã’¨b¥Î%,í’¡S/«—t%æd\ݼ«Ôhè, †”Ó¦åz4]h 4Ö¢F]`XÇ:™ v䯸f…ea\Ø&Võ˜[ÅVe0Æ_1È{צ¥~°ÐÑ“ƒÅf1[w™öë*VŒ!cËÕÕÈ®«ra‚˜Æ”˜ÕØX ¤hb\b†« À(aše{P'` x¹ k2aeæª7ðCù‘€²Œ…²¥fD°;^írS )°µ(ÐIe¶%»ŸÕ¢<¨ Ò'…,œ@pžûî«MýAJæÒ±™¡ki œ/aý*’½3Ç<€|¶[™´APBƒB7i’m7gW ¡zÍ"i\˜Èüy&Þ>iƃ¨#ømÚýèG+­œ HPNв„k®¹æÝï~·ÄèI3%5QüjJ†6n9M‘ Ìü#HëFwÕ-kõ¬ž7™éU¯zïYQ‘#‘»Üh 4ÖO ÁL°”ѯ~-þƒAl ûÂİ2 ­|J1ºÒÁôã–劢6Ø)¦Šµb°Ø,f‹åb¼™VŒ![T\?Òg¹}¦M>±Y3Ù¤gD¦TZœ{ X. &ÚàQG5Ú7A`OÑK–ù/ævpÔ2±„ñ€Â Zø—@sl£ X]²(¬€†£ªûlT#a»Üh 4¶°L«ÁpŠŸ/çN¹Š¡akÔ*,®X1Ö‡ÖWŒ{Ä$±J Û “Ÿ÷Ùb¹/ö[mL sÖÚƒ†_òî#Åä±úDôÌ€5¥ùÐj4æ€Wçd‹šx2ƒ¬h} ,ÖL7´Y)È @‹.„š sl“T7ˆjÓØ¸ xlÓå¦@S`Ë)Ð á–Ó°{X! ˆg¨-2qò¹1 Â3¹Å‹ ñ©O}j@®ü$¡©¸jÃàZmÒÒ'<Ú!íD¡‘x‰õÈ Ò%¤½pDa'Â9ªsÇþÂc–ï´á_$³\òŒÍöåòdV›4-çvo²‚ÉËæGøð'P„øRŽLúM°_@Ni†ãÖ*¤ÄCžp |¨äµ+zv¡)Ðh l `;˜„aGkqªy=F'0VÂ\Å ‹µb’X%†ù+Æù ,#ÅN1ÕyWóý”WEXwØøØ«ÇðÃA 9 ¾š À‘#‹ z€Ku^-ç…´[ÀËä8+%³ ð¿zR`±lš—ڌۉàÕp à‚Ý ˆÁqõÓ…¦@S` )Ð á°o_! @k™ ‘èˆsÎ9'+}¡F0}¾^qÅP_ ÇžÀ[<|ÀXŸp$qbÿDVЈo€³§Ô,üöHŽ8â)ï4¸ç=ïY¶a=˜LÀÕ)uÂoúaT&j˜^íûq"šK™m÷d® Ü(+€£ ìÅÀˆø¯ÀH x[IšÛõVršõÈ5®åè\æq9 ¤ û³)Ðh l `JX…M•bSìkIadƒØc±\lóÄB³:LkÕyºÂr1^ìƊî磤·ùUJ©Ó€ 0†º8rÈåp-&hÀ i¶Ð=u2 V=»D˃? Xx/³š)€QÓ À«n¡[¾ßDÚƒc ¼~=‡¦À.@Vw‡ØKØæ`vþR€‹Ž³¤jȤbQ2q3¯¦ ߘ˜KÅŸ8d ˜AS À ø'À™¯pŽ‹‘s«¢yªd„.Ä…âÑ$«7èþ©O}ŠEÖ-Rº•—Sò'Ÿç•W^™Å…C¯UIidºÈ!¥'k.é„D’ÆÛ%ÉBC ³Ïj*GNÖê|a=WX¦qƒ˜}©»Ðh 46,0+, ã¾ÆÀ…\nae1L…Ú0Ìz±Y5¥ãaÂX1†Œ-cÎ.Í­{ ‡+ÁP Á`‘«à#G°R6ÄÀ è [öÉ6·Ø¯«ýRàÂYÔ¶qı¬7-Áblš€2áš €jiV °‚W_Am=z ˆÓ4··HQ¦ Mͦ@+„›Mº¾qU(qƒXlºB/Ê®)Ý™ÌãñÌë_>–ê#øMʲ8J¸*O=õT’„›£JmšÇQ}òà ŦåàûºWBuÀ91Ù:ŽbÏ=÷Ô@¸ãniã½cybáªñö·¿='õqÎqüT©©L¼d9Á3¥/|á N•( Œ}¦¼|h„µnBôÉγ*ïU¯³)°KS+Ãа5ÌmܼEìSÅZ1Ø ËÅxã`¢ [Žç$F]—ŸgF™°ô…Cƒ†Ä 1¶(`¸XÂX€‚ fEŽš ª£¨%dà ¨6W·¾å¹ÇÒ²`À«JP p+Ë( Ç!,€Ó»ô¯©×Øæh…p›“¸Ø©) vÔ3ñ’‰Ãf¦S¶Ï1® n,ð‚RIÃðg œÝÝ]X] ‰¤ŒKr6—¢g†™~åòN›~ðƒ…Ï€ª%S±°{ŽÚ«>–è`.%roS&+¢ ÆméÚk¯•н 9œuÕUW>¤5z¥GW&%DP˜Ôç+¼ç%‚°–úÒ“o 4š›¤F‡Ýaz¥êÌ㞉Íb¶X.Æ‹ýbÂXqzÀœ±hŒÚ0í C{>p vÌ' f`¸€£»Z7eÕŽÆ50€ðÒœ%ªf…€êÁ¬c ]ÊÞ ¸šc|Æó?+Á€å,L¯upÅ&M7h 4P Âþ4S@ž4ž?s4ª¸;zé0²Fuäú°Ó©ôqÚG›“¯{Ýë2cF¥’ñ«qõØcˆÂæ$&1°­ª¯¯×]wÞäò®¼m®æ©˜£ÎÂs ö˜G¿È"Ùœ\Ž»]t-4û“éÐaÊìÇéZ¢æiiˆ…óqÉ”ÌV@ȘT`1õ»¶)Ðh ì¢À±AÌp4É…ÍæSýòrL+Ʊ庻ƴ±îª™Âö£jµ d,œ–n¶µ3YÝ‚¤œd1ç0…ùƒ0@¦™¯  À9_A^l‘@Ä€T \æЄZ€Ô1*À¨»5“@vrtï¢?œ^VS`R ÂmHÜîz祀÷¹¨jÇw\\=a<²Dju2!£¬tá2’ï¾ûîeš@àä+|e<þÒ—¾ÄQg„vcÉi®qðXöðLxr{¾2Ó–wè’f ïWŠ <è ƒH ;ïï¡gÞh 4¶0FìCžsÎÔ¬“W3¬_«78’±@a´‚ À>€HTµµú©z‚' ådŽ: „2pë!€sn1à‹Ž ?ô¡ÅÄ+º .+>ŒS•Ö^c{5=° |“‹eÐ  +C)àßÛâIuŸM]›­îÚÏ·W·9­W›`Î}šwÁ-'I>ŠIŠÍÚzÌ1ÇTcÇõfÞtÒIêYC£¡±ãB;Çòº=m`ª@‹ã?¾ÊÀg”ɧ0Œ¸Ù¨—-¡#¬¼Ä ‘F¯LÀi  ÀWFâñjÍa2D}u#Õ”1·W›IAÿ–)@‡û×O¢ M¦@S`- `•çž{.¶92ç _|űe̹xû¤A}û¤é•{ˆAC)rÚk >L”dÛ¸$u¶«@gy`Á–‰3 Ú2 à–© ‚E‘¹Q£‰@j¶1²éøVÖj©¬Ó|WÈýØ ËM¦À ´B¸„8}iå(s\I„F¥º.ZœwÞyÙÐ ÄFÇc”u¬SÚp¡a° 2ùÜo¿ýØJ<ð@e˜'^ ø{¿÷{i£‡tR·T¡ôR¹Üüõ_ÿu.U=[¯¦+3¾Š0)ðNc<ùÉOÎ׵ƪA¯òâ¿ø=ïy‹ì^{íµžýIàôÒ—¾ô†n(ru¡)Ðh 4n°Ð—½ìeØéz¯6Xôi§†]cÚôÃ…~•ѸŠÏ€1†µkcŸ ”|ö³ŸÍ] ÐÔ6fHL…eU_£¤ðK„ûßÿ;eÁ…é8‚H@YwÐx¢"`»Jzì‚`@å”S¤—X<5ùdå•’ûóŸÿ|2t«„uÚDÙƒÁ9t~Ò¸¾B&J',ø¸ímo›­< ¸:WRÀž€T©‹¯¨²@ ´ÔjÄÌ FÍ‘‡z¨z {+¦9ß¼ÙÜvËÞ ŽrQXk¸ÁwB3 ]ùتYšM…h…p!Yºrµ(À=ÆÉÂ-QïДÅ4zÝÍ7ßZH¤&ªÞˆspðQm$*È ·.¿üò²M²hޱþA²ù'ŒgÓe…ýä'?™ÎXú³¶>ó™ÏüñüÃþ°œaÒ ÌÄ$j+ ®ÎA¸«ùÊ¡h¹{OÝ•‚¤pzÔ£ÖãŠVËx.Údµ~+½Ú¦@S )°#(€Ùb¹ïBýjÂ̱qÌ< ÌêRn\ëv`Q̈€’º1pt@OALêÁU«6 Y‚@€sÀn=j-èÌ– ÎA*`¯@vÄ\œø|ƒæ< Ço¸pƒo žœm ÜAüŽxb=fS`g¢@+„;ÓÓê¹n ÈžŒØL•"ÔsÌ ßKÐÂ÷҈̟Ž~н“7ÎYg-+°ÊZ ´–g„Ó^ÏÌ™Q2}&¬‚µUÿüg²7€c×4´8ûdaá;4QêýèG»ËyÁ ð(’á:.™‰0Œ‰7©nÑŸ™2Pöo‹gÑ}6šM¦Àr `¿˜0V@¹E<|l ÆØ¼ñ’2((µ˜­< zL íT"ÀV%‰gI= àÀœ»à »À_A!XT9Á¸±çÌÈfc0°«= €cWA3€ÓúÀoeP®çLÄ÷ùFhÒM%h…p qúÒ®O6˘:1ɲs.gyAES@8Û–ã_—ý÷ß_¼è Pê.Œ¦ì£÷¸Ç=ò•™óë_ÿz, R¥ »bU’4Ížóœç˜<“™-WUÞýîwO:¸3Ï<“ɳôÀt;ÿ,Km.éšÃ¼ñòþ~»·ìœ?ðžõ6§@+„ÛœÄ=À¤Sböú`'g6ÎàÖ`ÔØ»³½–àŠßøß3VT1îÐÔ)RÚ~å•WÆ êFS6ÊÓŸþô:0WÙÕàbF‡0UÖ5_ÅcÜùÎwNý’Oþ<°-;XʵѲ«>›KzXxI:½÷Þ[ƒz7à³ë)5šM¦ÀZÀº/¼ðBl¼rÃ,dõkUŽÀJ€àb|Ut–D%TŸ ,qï@ ´•B¨A€fþ`Q9J,¸,›¦20Mð d˦ |Õb«CÐ  G›šF ÀÄúŒú kQ¯ë›+KVWöѯèÂ¥ÄÜÀÈWÞ/»í¶Ûã÷¸Ê«“.½ôRî+A)a ÂúÅ*p˜É-¬›—]vY‘òMozS´²QÙsÄSBªÙÄïTÜÿá‡.÷wYÇé1…Š”Øc=ÆÊûÞ÷¾¦a8•²x›öxu3ÊôZò»ßýîdZ“9íãÿ¸•?bküÙÏ~ÆÊ°‘çZ\Ä7ÞX¡ GuÔûßÿþèŠð/FDhzâ‰'Ê&”Oê\ˆ7¦œ×‡rHmÖA»¸»”~è$z><…Ic?Êëë0ÃÉ¥u~5sìÂÆN‘Z¡ßA/µ)Ðh 4~E!8ÇyY ›æW×S°Æ„juK¬±Àä¥2pÀÙÐS (Á%о‚Ñ…¶Qúg ƒã¸ÿDAÖ pgPÐÁzNôàè£@€2Á€xà+QÀÀÊR •}ô»øÂE#H2``5t,’Ë*øñ•qñNwº“Á*ÖDÄD&¨tIn´Üä(rÐ×2…êÓXøÀn¾ùfðã.á|á­F5NR:÷Üs›>¡¤m±M¦&Ÿb*¤Ø|`>¡Ïù]c&MÕ,¦µ9¶qÔ.þø{yM¦@S )°êÜ€ȈAŽø¯NÊ ªûHËV†ØHãk®¹¦&Ü…¦@S )ÐXA ‚`G¡ƒØà#•¬D'œ7o H-ÜTœ@à@°" Ç~2‡ÔS:>š Lp ”A3€Óy-÷èÁ=ÐR1`â#J`HR"„ÎÇS1Æit¹)°KR Â]ò±®è¢:ô =(PÁšøóŸÿ|Bˆ>ð¹ÊW„ãJÙ#Sé³ì—ñ3Ÿù̘yÌ1¸ögV-™ÇcmáÖ£ýèy‡ÕžÑ1ÉH…Å—wJ]Ma´ÅÂרµ“6õ•»Ž°Æ:5ñï|gΩ×À4ÄÇ?êQªÆ YšôÜšôצ@S )ÐXA €ƒ‚† À> ËÀ pÉUptzŠÖíT²Ñ‚9‚ZµQ‚'œp‚XŒ;ÏxµÊ¦XGµìßj”As=; ¸K¡-@¯ö:ýå€J$¨{S 6ÔV$q"'NÚôצÀ.IVwÉÇºŠ‹ºøâ‹ãÌéS „ógΫ_>·­ƭnu«d" l¼ /¹ä’$§Iƒ½öÚKçÎM:î¸ãÀUt[¨3 ÍñÆŸÊ9>™&6Ôñö…e¨  ·DkXrÁ0³®¹Ío|ØÃ6YWm 4šM•¥P˜#ø(g°\@LšÐ€æw-© À•V-ˆÁÇJçVWǵ×Ü€/®« @Ožà¨µw¯½á %ëv‚¡ DC²ÎÌʼnIÿýµ)°ëQ Â]܊˜ô?üððw¡zGq„sÑsÖŸ6|K$(«CŠØÜ]Dñió‡<¤ˆøýï?§ªg\Œ-S†˜ùöÛs®QFñ)~]ºmHs»ÛÝ®*«À[¡ŒU¹¼T£;NêùÏ~5–D»˜Ôܹª~2º«µ“ ¦u=M ®€žÛA|ù~1`á<‰ „"A¢"ÿ s‡£« M]ƒ­îÏquWQNÿsõåO¨zÅ:ê]ø!!!ìp®8þ_üÅ_|ó›ß|îsŸ $d$ AO9å”ÅÉæž¯ BÐÞžä©§ž˜`Œžs^ÅrÒ/‰ÑªçI›ÉWÅë†Môþ÷¿¿«œNÍP!Sr pÔ¿|R2÷ÝwßI'Vý‘|du%½ò¦@S )ÐXDÐPjU‰gf  )ÐyÉK^†|IÙ¬Rˆ¬®&Àþj At–}Ó½€¼V.7°;¨¾ê dg‰H€Ö{æîÓ3€`ÕD‚A®Ô/ô$Ò`ž’`-»®)°S ÂøáõÔßñŽw„¿‹ À²©ï¹çžOúÓ¥ Ãî/ºè¢Øµ9þøã¯¾újmXCB ¹W†kxbFã:ôÐCÁáÜ¿%Ð~žô¤'Ńª=ç9ÏqQÒ—WêØ«·…[Õ`“…ÛÜæ6B ûÁ~P{yº¹Í(è-3Œ}÷þèTÊíæ,©±Û }ôÑG÷¯¥)Ðh 4šs ¨1Q AIò]\4@'@†rŠ`ŠNª&>2#­Už@äܰëFðú‰O|ÔF¾ 87”ÇÎÍ|q•=‹ñ9Êèg!Ä€ăbáA N$óM mˆsêuMS`× @+„»Æs\¹UHBÍ©#0°ß~û½ë]ïRæ Büô§?}Æ3žÀüôʹ¢e9ºˆ øÜç>wÞyçqƒ)Kaº­OæL§*97£”Qó^÷º”'Tc ò'C#ˆ½ûÝï½tl3ÞñD4«œÉ›MHp+°!©hùÈGf]I'ÆËZèHß1\QÀAL+÷é7šM¦À:( *J°À”äŒxà’Ü››@pF¦ ê `/¶PµKçóK P] `4øUóµO|âÁnjÄ@4h0]-ÇXî ÐWÀd Z Pˆ ”ð@„ÙZ")1#Ý<ƃ¦ÖAÝnÒØ9(Ð áÎñœz–#ìÈÙ:ÃY O?ýt—rDÎ[w‚PŽEipÔQGEú·û·`ȉÊä^P¥#„T™Mѱ¶ÚE¥×¢¹cI°PÒ³RÉJu¬—LÆ„o¼ñFFJ-ƒš[çÑäS §+ R¼â¯ÐÞôj†5؉¯ýë#õºÜh 4šM‘`bLÄ)L1Á\õ 'Š"0I€ <©¤øB0@Ζ#láT 4Á70UŸ½j `Äjf†u– °ž7o4 ï0ý“`@<@ ¢‚ ×-DˆEH¨ð•€¡a#÷"!d¤^—›»Z!Üâ -A~3ž*ðfD”;‹®æØcåú’«ŒšÒOçêùçŸçm欞ÂÍõpûÛß>Wƒl¢7ÜpàÃú8±VŽý‰3Î8#@8Ö+—r8©¿ê™å‚Z]&\{•&SeŠfg áH\z¾ð…/$^qŽŸUœuÖY+ôûè¥6šM¦ÀfQX€•G!T`Äšxº€ÀB <TUâΖ™Pmn €ÖX áÒ¶³9Y-Ç‚ž4˜Ö ;›™µ °ÜMlIÄBB&Llˆ» A‚8A¨0–r•ÈAðPã*Q¤ ;Wû³)°SS Âúñ­ÖäYc2"ÆCãÄrÛÛÞ§†νýîw¿ !2S9Ágã|ðÁâ €œ~„­OЂ£›¨nç‡çÖÖ\L‰ì‹w¹Ë]&ý¡¹+騦ÊÂ3¬k|ÏÍÕ=öØ#ÚàüãD8¨?äCø«$”|~ìcKøÇÄ28<ì°ÃÆž»Üh 4šMµ(2 ÌDy ¸p !`¨O@JŸë¾÷½o*Ù8 ˜K,b®.ùÚ@'sÙÔÀ®Û ˆ«+` ²wü<«¸ƒx@î>è×Û¤‰ bá ü ÛZ‹~ˆ¹@2ïjÖ…¦ÀNGVwºG¶¢~ï{ß›ãþ(`ïÿûG*ü⿈;(žº¤cá+a¡‚&~Î9çüèG?ªÛ€$t!mj‹ožNFPĈ1¢çÓx*ÜW ÄD·¬ Tãš›¬Ù5“¾üå/Ç)E¬|´Aõ‰×LÙûˆG<"}2jBå²ÚVÿ¹Ê²[=LFé¯M¦@S )ИPdL·¯õ)†!ç#¥GÓšw”ÚWüÀ>KÎÕÈÜ?p‰yÏ{Þ“{ŸùÌgj` Dz"1ôcç]n 4šM¦Àr ޹_LPܸôä+0IÉŤ@Uz^iÎ*¬#—@^(\g„!xh†:áçY»…L'k¨K8†òY‘‚%€þ¢@ÌÁ21bCu[SU f6ˆu/Q¤ÌÄD‚J]êBS`g¤@+„;ãS[¡9_wÝuñ3á©òÒ—¾Tä:¯•×¾öµ2}Íõ@¡Þûï¿ÿk^óÖÊQÁ“¢úu¯{žþ7ó7h'‚\ÌCYG¦cäL‹C¦°uàqà.4"æ.Ù¨ç@5v˜²ØS…R<ïyÏS“VÜlò8e9+<㦒JÁúÙ4[k¿êª«‚UVª³ƒ ¹Ëš…sŒIýÙh 4šMõS|N)¸€ £¤$À”/  `e”xZjÔ’å;õþˆ`8.YäÔæø‡_Wüÿõ¬Av&ÄAùÂÜÀ”D2D½ Õ %B$ˆYi]R x?!Dk'–Ä– B\Y?‘»eS`£Q ÂöDz>ÿo{ÛÛÂÜð°àÝwß=œwäÎÊÑ£„z»óûßÿ~í\â òÏÿüÏêŸð„'øªMmÁ¥“ô¯\GÕ§^Z—{ßûÞKPª4±´_ë3jç+_ùJ'‰¸`€Ì~ããÿøZ*ÈŒb™#†©ÑU`l×ÿ•ô ›M¦@S`Pˆ• õêÐyÐc@0”K€ G¯ .¿üòÍ yßÒh 4šM¢()gQè#x>ˆ z´CIà H`/ÈÔµ`˜ ^¾ì7‚B€ DJí¦Î(Í¢ÊùÈNdG]ÒX; c ¬«Úë?X ú Ä€º:)bqVO¨ Z(3L̲5–F !–Nˆ( B" 1¦(Ù…¦ÀNAVwŠÇ´*“\GB»í¶Û™gž‰íŠYÏú¹ìg—ŒüÔ@øÃ5›ÿaÍÌx/|á ÷Þ{ï\å:Â-D~°¸²¬å bÏÍ]&0Q;e¬aCe鬧=0þ*Ž­ÏQ¶O~ò“Ý"vB3è’#Y[™T“9M¶î Pv;Á^æŸ9KêíFöÎŒkã¥FïBS )Ðh 46›Qó b I>sÂD€É¥hh-–ó“€hËv"°‹N˜XzP¨°  š5[`ZÛ’5‡IaÊ pƒï bœ´è›31€0PÞ¤æï.xnr—¯ÄŒLŒà,—@B,I}’ëZL@{b a¦¤”ZTš–­nØG³r~ÿûß'ÅÓ…†ÿ÷ÿ·ãh}Ýo¿ýœüsä‘GFQüÃ?üCiÐĦ_rÉ%Ï}îs+ÊNKØ´\ÕvöØç~þóŸó& ظÄB)æ{¡Þ`™EDàæË÷9ÌðWZ¥£oÇôeL¤åz #?ûÙÏz¢’$1¾~ík_+mÐrN?ýt½ Wˆ¯,etTãÄ$Ÿ¼_² Ü.¼ðÂŒ_ðœ¯,—r¾­Üï¦Üh 4šÛ€¥6Ä& †’M0Å93P¶bÙd÷¸Ç=ÜÚ\é„€ü©…Ѭ}©h‚ÎZ H­Ãî O3“埴SP^Q÷ ~âéãv‘ :¾ZØ@x B$&Ú&aƒÈAð°BQÄ]ĉ…T|%´]0±&‹f¬D;µ¨.46&Z!ܘÏeåfuÞyçTø{Ô‰ Ò|á°{ÜãM‡Ãâ¹Ïþó™âæÎ$r|qÞ¨ÔÏ×\s ·û‹Þ5ºV¦ž™0ñâùZ§ ¿¦²>Á€P‡X:U@2>6!i o*wøã?þc ·XFVèâF9l´jB/Qû¥—Ò9ÿþïÿÞUm>ÿùϧÞç¸"`öo|cœ@—›M¦@S )°%+±EÎxx£Àx*è«À+6ÖÀ™6&æ²£øÀÔ!@ÌÜ@d)f sœ0`¯à‚ݘ†S3ÿ\‚ãà>û™ã]9G*B‚zbC¬·æ@œ T$Ïùx ñƒB!Dñ#¢T´!´dòĘ8¯Ò{‰7㊺Üؘh…pc>—š•ÄÍu®‘£`¿ûÝïÖâŸýìgã°áþ`f´ðaú¥bÓå›xÀFÝ5‰4H{n!Pj-\™ J ‘‚Š£Ž:JƒÄÓ©aGŒ¥³fÎ×%0 ¾þõ¯W=0sË‹^ô¢hƒ@Åñ¸®~ñ‹_TŸ²ƒ ®`‰Lç&Ã蘜7 ¹Ñ¥òuI3gìÖ(]h 4šM¦ÀV¡p Êä³ Å‹<)n/– ½€€3`¿J7A½Äš! Œû è#/4¯µYv/Ný8±±\“+•Õƒþ…~¡D…"D µF'ZDëKWÚŒ[`Ý¥¡¥VD˜!Òä.BQ§.u¡)°)Ð á|(+4¥k¯½6~&Øý+^ñЬœÇ¿| R–Š ³Ã=ëYÏ’¨ºv÷ÜsOWuâ^7êdÜ7Ë­¥ ¦ýÂOðЇ>ôâ‹/Òd~øáÕŒ™p|fÂ$êð"9Êê’P 3ÑO ,EÔ b-YlÃdy±žp ŽÜ5–tm±°*'¿&ÀA¥FéBS )Ðh 4¶"@LÁB,HJQ ªÒx0@Vž˜Cóy€O3  "X¬y‚Ëô@mX—♫Àbp ”Gm- 6ùI(ÓjO„•ÖHœ T˜C¤”.?!DɨºdÉDLnt¯NBxjE]h l4 ´B¸ÑžÈ Íç­o}küLîp‡;|úÓŸvÄ-PaQ›h;¬Žqºøÿøg%Îçe–;ÿüóßýîw+ãËûì³Oi‰j˜'Eœ|òÉrIûš¿Xò~ýí—®¤“°„…Šbðæê«¯Î³a­Lj¦Áø½<¨Ç&&¡,âjRõ Ǽ¡³d~)¥ þä'?I%h9ûì³µ¬-SéËT$¾"‡dÌg(gZ›G:w¹)Ðh 4¶"@Lòrz*ŒL@ T¬4aFgÑ—˜Ë|_¢9‚z`qœgùg‚Ññœw «g€›-G\^9 Ù¥…jáD[÷嘚yŽ"Qá”SN!6ŒR¡‚hAÀО°Aä¨[ˆ"b ᄈ/žtë“C˜!Òlˆ7„•–Lì×Ûå¦ÀÆ¡@+„çY¬ÐLs8;‰Õ2òÍ=û] çÍ©G¨Ã)¥85c›´×‚¹'~#@÷¥—^ -˜>ÁòWª+ù›+Z†EðØcuõnw»[7’$öÄîEÕGw­¨qÈ”(ö¨p“œŸÑõÇô39¢Pý[Þò£˜v&ct__ýêW»%3X8N‰Š>™a6šM¦@S`ëRД.’C¤À¨2b¶Ñ\ÍPÓØ˜«ù€¿è„êÁbÕ+$¯i˜–Èj<Â. .¯Ní*°6ú\3S3ÐoÐÚäTSÓ&<!}’°Aä xdÃÐ]º"–dþè£f$QuK¼!äD«TIø÷EÇåw¹)°)Ð á$þŠýö·¿=êß„qK0-mf\%Eœzê©);!÷ÛßþvÔ°pX¦Ê‰5.õR]k‰¬_þò—c¶,Žœ‚qêF.ÉæܸâÅs–à?þã?º÷iO{šn¿óï$ Ñ™ý„›»TÛ€R“eÂyÈC…?8“ƒ[{=ŒÚ`Ǫ̀^~ï{ßK$:^7ì²9²)´šcXtÈý%õ²›M¦@S`{Q ´;€•¿„Êž@À[q¿dàÌÔâP£}¥Q +£p¬M-(U©u _6Á.ð  ì^­1°VÜà;X¬²þìÎÝêV·ª¯ÄÁ -Ñ„"„®ˆ9ªîJÁèã~)áDK‚Š«Æ%º„,ÊDšÉÉ¡•q Bµê.46Z!ÜOaUæÀ«¾œ!ÃXùoP„8Q:¯8:–¹n¸QÂ:%­žìæå^ŸwºÓRELz_ùÊWœü“³ S_Ÿl„ÙÍK Ÿ~6Å1yZêñn>!•ŸÆ’ó’éè¯àF5ÌŒÁ&Õ˜ AœÐ¹Ñc–u‰ÈQùH«¥‚Iæà(B‹9` ¯ 6ÄBQgâ°JªPÚyš;Š­î(ʯܸ²W—EeîÄOŒZ…L€aÜ,‹X°xq•9ªhÜÍÃv1n>!üòjK”;ì°rÈÙô¼ÌtÇõ¾+ˆ¡øÃž?•†ñÞ÷¾7N,VŠIR˨£—]vÙ¹çž›èŸøD8ô¤'=ɸ/yÉKÒ¨+Xºà‚ j«Ž©±œfntôm&LwhG3¨J!õ#†©á¾"z¡zëBS )Ðh 4¶)€NDU>Se|X` x•ù¨6—ö ¯vüÌ ¦D–N:UŠès#HU¯@Ô*ƒÝ,çœCƒh—€õ|íNŸŸç%PزçÆåD Fµ!x?!D‘‰&ßÔœZAŒ!ÌD0°ºZ5±‡ðS™Z E}^Ôü©uÍ¡@+„;„ì+7¨¼ÕµÇðVëwÄÐ _øÂh}Ø./|®ì‹|6&ª C`…tCƶûÝï~áÎ C‹qO øøü¨‰lB‚´Rójn ýèGuÂ"xãþQA ˜¸K…MP×JRŸE‹sp­D>”6ÈÊáÒÿg>ó™ø´ä^§0=ùÉOÖaB2¢L&zP¥i‹kW¨?Ó–×{œj—›M¦@S )°­)z ‰Rp}r‡úšHÂ(u3Ðà\ Ø>ð—IÄÚ|”‰ÆƒQWAjÜAÝ j£Â•5†ôgãdÏ—ÏsubNʹɲ¤Ž–åÔ/ù,‘ƒB!KrB}Ý¥[ 1†0C¤‰©Z%Qg<¬¸\™ˆF}jÔü©uÍö§@+„ÛŸæ«5"‘0tì2þ™ k›Xp¼’sˆ<Î¥;oU€ñ3ÑÕ‹_üâ€Ê&N;Šn¼kRžøñ»]0a´A-ÇÓÆÇóò—¿ÜUÓó9¦5Ó†‘O¥ù§™¦s£3yÕ[ˆ¯à-^¦ɲӿK_úÒoÿöok– Á9ÄÂ*LÌžHŒ¸Ù¸7ÁŠÚןèùtÕŸM¦@S )ÐØžH®µÂ# ˜[À „2php‰ ä?*b&ì˽à2:a$`š6àÕ 6ð­ÅJ“ÅÀ4È®KcAîÐÌV' ”ÔOă´?­¢¤õ¹Ý $ÄQî7ZÁ†x“é™?±'#骂DˆIéjœv—›Û“­nOj¯ÜXxßï|g\?åbqÖYg) B`ùl£’`†KþxwŒÌZ`:OfÂãŽ;.[jiæS`wN@ªš*”Gèâˆ#&ÏU2Ú¬O:餺ê®,í’K.‰6HáLf6ÑšÝtÓM©ÿÓ?ýSmôÏ]$ 0O›à“3‹¢9»J±|Üã—iäÓ¸±¡Ö ]h 4šM¦Àö¤ <ªl÷©a€L¨%öÌ%lð?—@!@4ç„ÊØgÕ)H­EÚ¨sÆÁUŸ˜ÎL÷xW®‚xWÁ½ô3Q̈„"A™}s;Á ²y'2b.å“àAü „dW3•D‚ qel©L¤‘T†x3éÇÒ"‡ŠÌ€DLÒÞè§ÉÒúkS`ûP ÂíCçÕŸ- ™µœH‹ïc÷W{ꨣÜ"Vðšk®I¥à Gf ú”9m±ËM¦@S )ÐØþFL)¬Ä‚0@–ÐwÐà4®>ðÝ ‚Åd%”à2õ4GDLbnÁs­¯dð=F롌Ià^½S+Ò9a ž«Äƒ8©ÖrÒ8_£@Ö%WM€(B !–L”=È-f J¼!äuì ŽÖm]¡ÑHWÄ$ÂRF!>¢¶ÿíWœ­®ø`ë/‹Ì.W ‡Ìr²—GkÂ1±{IÃbäAaø¬ÏÑe4ü‘Añk_û«áœù¦A>'7Rã°*´u:O6JF»8«|ò“Ÿt#ËÜHaâ9j" êA4^ýŸÿùŸr7 ã%å8®dFAð†­CAûêW¿šhöÈ›õt4!º)ûee¦>þøãKËÍéÏ“ûkS )Ðh 4vÊ6ˆX`+hÈr„t/®N#|AÀlÍEà"ÕƒK ßÑÀh™—V#bÝŽ—€µ¡Ü@”W³÷©O}J¥íÍhžF!¨É¡† æN„Š,-Ÿ–L !–N²ç9^Íe›&ä$ϱ‡ð“qµ×1‰°DdJh !Š(5οËMmMV·5…W¨Üù˜cŽ 0ˆCËÃëý¥ Bœ‘á0V´|¬{Û8Ü?ýéO¯šj³¤€™â¹FIÞÕW_}òÉ''ãÙCúÐÊèzüã_Éñ² [7®Ó) e²&œüÚYc…¼×íuB‘Nbá{ýë_¯QÒŽ)ÈQ‡Ï¼à™¿ztˆ3-¨sè5ßP¬_cu¡)Ðh 4š;•ü,pælEõRgÑ|˜ ªæ “´S} Á¥zÐÖ¤NV—´m`p¬Ûj¥pg—R'½®zýÔØ‘‹nI0 \Í™„‡µÕ¬çpBD!¨d\·LÄ•r<5áQC.é(÷&çœ5¨&Jo-§ M­NV·:IW´Cv²ßÿýߟpOÉ»œ&^ïRÎcˆ›¥ÔÕ¥>Õ]8âO2ºæO9ªåZ$Ñsž#_Ãg+ÍéSžò”1Ë!$‰¾Ímn£gÆæHˆUO‡2€e!‰ôfPœ=ýï}ïˬ$R‹6¨>1Š9€‘CKŽ^E±bZ]¬˜Ù#½Ï}XëhZiŸ3:÷gS )Ðh l ÌO¡xÈjÎl]òy‚? ¨ ÆÏ3 .³(š|¤à/9Æk±`71~b \É9s^àßQD:XϽsû/§Óç<ç9AØä¼!0ÔIñ‰²Ï //Œâ 1†039³ÑíÄž@yÜ_#©·ÂÒ<ß)±ŠpUËïBS`ÛQ ÂmGÛê91ᕌa¼5Ä‘;\áòË/ P¨´šdÑ|æ3Ÿ9 ãv£#e…Œïµ×^é$¼>åØSö©«*WöpÜÐHôÀC<@ÌjÌþ’f£É'>ñ‰(¢’§qhä­s¼8-!Y@N†±½èE.ñòÇð[¿õ[ê}Ö4®¿þz5±Z`ˆU~6sNdãE]¤Y¢¢(&Ù¸pñãp]n 4šM¦ÀF @â䡘¿ÀV ,phs¦ ò¢(V®l°Ý šY-H¯ã2“¡gÐ\Þ¡±Jƒo ž<¨`¸»³¨Q€þØ•²¾Ž#*” 8ÍLã±}læüËå QdÄë`ˆ4,p¸õ—ÙP BjED£tEX"2œÔ¢BÜEÄšL»¿6¶:Z!Üê$]­Ùùr¢:¶…ŸvÚiqÈäI"stèYqd—*hx\9ëË"­ñÈþÒ`ä°jæ±Ý* ᢅnq×ÔÆ¸NIš<â^H Êüâ‹/Îd8”Ær™{ãÝ b¿dzĬãg2ª—જd⪚±Ê/TÊÓ`’z9Dzºg?ûÙi–óˆTZoì…øáÓ2Ùœ¬¥¿6šM¦@S`‡P<%"#Ðæ3Î ÄÁ\æøÒ ¦8‚ÈTÆ›4õÀ4•àuÔ ¯z@ ŽãÈ c‡f\OA9@׸ƒx@î~iE+ÑzQ>5νu)1#òL¦TŸ±¤Ö[ 6Ä›:§ªÄž4 œP ^B†Ð8±!D)N°Qq5&hE8™Ì­¿6¶Z!ÜZ”\Å~*W2nÅÄU$^,HànŽ$Ú{ï½'hÁh J=ãb¢¨ÃñSN&£ë…úÉíi#Üþûï_ãŽ6œ—7?Ø/¥\y¨£‹>á O(w‘Ä J}ã7&3)Ýpà dܤ¥ÑÕw¾óD*š•±FÕ4 °É×±¥"ÂË œ¹e9>“Ú4“aæ]ïz×|þ]Óh 4šM@ U@ä U¾nWÄ€¯,ž¥þÈ <ЬåÓ Æ² 6—€¯Þ18Ê‘À4°Ù.ï´Ô¬«1@¿Ö)Ss^Nhòe½­™¤@ØÐÕ(®øš¿ì1þúÛÿ2%b̨º‹¨“5~âŽTíÕ“KÑ9‰O„¨½¶"ÝHèªú.4¶.Z!ܺô\¡ÞXéâg}¶@‹çé”Õp=é•Ë¢¦PL©,1ÓŒ‚c fð jK' ì‚c-G£m|W À==îAÿxµÊ4Æ4#<!ª¾ „ ÄBHŸ£HŒ!Ìi"BTÏÙ %Õž!Ѩä…ˆ.n'J%åAü†2"¡kôNª¹u¡)°åh…pËi¸r=ðÍxØÃÇü–=.Ž B¥K¥ÉUf3>ô{î¹g¾†ëL3õr7Ëñ&f³\?y—‹@Ÿ<€—¾ô¥"œ÷‹_üâ¤A¾Vöj³ªÌc¹tÝu×Ô]"¹£T’˜ç=ïy.ÅÛó»ßýn`é.w¹K6<å•IñiѲbÿ@H,¦Ù6ü—ù-M”©r©[€_4í,’Ä—ÒúÇKU&*"kLU†Ïº”B4Æ<0_‰"›Ì:C¤!Ø|úÓŸ&äè|þW›¥Ä$ÂÒd§‘@E¬Š×(A+»:!€ÅEv2ÃþÚØ ´B¸%Ô[Å{¹väPW¾NBáy8Ô¸­§Œ³s?ûì³sªžšxóO"(âðÙѸ8i“¯œ(‚%Eô{ßûÞ. ®ž÷Ñqs¾(ØL¨Mc-Ëg’ÕC 'žx¢ž£•ë®æ('¥‚(&Úó ÉXï|ç; “ò”ºíTxº–L}(Éuø¨ð¢I²5s®¤¬ZŠ[˜Ì­¿6šM¦@S`R`­ü²è`  ÀÅäeÚÙï G©\æ^ FÕ€Ô¸hÙ2¿_¬%8."Tâ·7¯K)úH) ?Az“Æ"0˜³-ÄÌœ81¶$l”à1ÞH,I€Lf¾ð“xCÈ!êÄguÒ†h”‰–L’àTÎ¥i™«Éƒ@èŠ-1¬BQÆùt¹)°Ùh…p³I·r7rÇ?òÈ#Ã(ÃÊ7Ã^\Nw-ÇQĉC‚¡/›Sb]âQþ¢Õ;ã•á$¢Ñ{¤®Žìû½ï}ïœÜ1@æX?WDB£çÐù0ª‡üàQ&Ÿ“º”B%;SZ»$Cš[›B0DqbyùË_®žcŒ6^xaØ·š{Þóžj¤À޲g¥Éó†7¼A}ù±° :ŽV{ñƒMYŒD™¨}ÿ5šM¦@S`ÃR`!vgÌ×}•K3ˆ”Ö4Ó ŒS5ñ8² |Ap4L…¢F ¥ ^õUˆC©«b@ÕW!_ƒ¡³UH¨Èž¤6„ ÷<ª}ˆ(11gò ?­TÄž˜ÔÇ6¤ÄѨ$8ŸP’(Už·iLÜ"tYL aŒHF0«it¡)°%h…pK¨·B÷bÖñŽÀƒ0£ØêFަÇE.¸à‚Äg×FÙØ’Äa‡&+4(Ýil0–-ûo|cNháæ:÷nȲ˜ó-xŒÄÖ(tP?pÀäÞÒå\•–zrÕWv8ݺ*È{’Û3ëòDG¬0÷øÃœþù8uTßÄU¾îu¯ãkS¨³w¹|èl`÷ Y¦mFÈ:p©ü4®. c˜O¸kšM¦@S )°(¶m>¹z&`À¹øÔ¨@D°˜ë%¸š@)H¬jnX0 ‚sŠPW ²·özž§×2gQh­r¼W™ÀàRòðÍQÉD‹xú6tëoLW=Tˆ+n_ò‡Ä¨œEÇö:÷•øDˆJÏĪXáÇf0bXê fÑ¥k&]h lZ!Ü<º­Ö]çœsN<Æ$à¶·½m1©(WΚðĘñªYœCÞúÖ·¦Ãª •`sŸ}öùÙÏ~¶Örp¹6¸êØÖdþ¤˜•½0‡ü`¾ãíâ Â⣯~øáãUeØ'~Ìzîkú¨G=Ê  ,uI TZ¦¸ˆØ9úãûz-›–ÎØåÐ’)|ðÁ" ʚȲ˜3-Œ{ÖYgØ'së¯M¦@S )ÐØàý†€Z Ì»ø‚ ÐW°s =¸š €&^¤ÖH1 Ó ¯Ä@yB À]˸}rè»7a`jH`È”rˆIÀÈéðQG×JõI\!´èÁ_‰1ù:~Z¨IêÒDX"JåìâRbµ$t%ö2wE$#MÒ&+í¯M[JVo)ÅV¨ý/~ñ‹|ä#eÕ âØ€{ì± Äõr°žfáéi†ãKý<:<„?jV‰¤ÓrüB=ê“•ÊeNtìÏB«‹‡ûË`6žÒ3a™Ùôpå•Wæ|ØûÞ÷¾xÀÜ>á¡gœqFi¼ó¸sÖÁè{n,mP·ÎUù4Ã-IJjCÎÏ…:Ò A©ø„ðE ’iãÒG¡à|å˜K檫®š¯ºkšM¦@S )°‘)¼j>hËW`ƒ¬¯@*€Eà"s hF§£ñ&¬‰«µY5=-:¡ÛòÜg²2Æt°>Ò*’ƒ}Kb€Û‰¢ûªA%á¡jèx99ƒ˜ÁÍ5éô&’C5V ´è!„™Iš½__ùå‚P<­FÕ‘ÈDp*{±fĪÈ`­´$zÀˆaåešn5#°ÛÆùt¹)°~ ´B¸~Z­\˰Îð²ÿÃÞ½=Ý—Uõý'¿ÿÁ J«L¬Jr‘Dc"Z­hEƒ456GE9Šhä#‰á A@ºEäˆ' ‘ƒB¨Âp’2eB)ªÄ”¤ŒÄ«Üð{u¿éát­ýìïóín¤ù>k_ìg®¹Æœk®±Ÿ9ÆgŒ9æ˜ÿìŸý3;¹ÿôOÿT‚¯„ûÿäOþäM7ÝMß„”ýÜâVó†M¦¯”k™ûðå/y"YVîž Ìèg‘Aÿ O|â7r°0ýßû½ß‹ž§0!+׳¥³ÿ´f`$£i£Íï=ÞMÛâ;‚"‚ÖëÐkRN­øe‘ªÏÙ k˜í‘h£çhœ´ƒ¢ÐšxòèG?zóôãòàÀÁ»…vñ"ùÌÖ »¥Û£“ƒ†TXºÌ7÷k»è)»ÂdRTa»-Úl"Égj4ÕIR¦`ÕSµÓ?›°”lú§š§¾B>ÙT9µ>9KÝm Á€~ f!`Ð!ð°éðÏxF¯ä?6s ´DŒi› AõûoШ×ìà„!@ÔÆÞ´À­ðN‚aÀH˜!Uó3Æê ò(8Éà <É–£òSSò…è”öªóp92Á[&r½ãqFÀ‰øçÃãQ“—y*ï}ï{sÅíƒà‡Àþw¿ûÝ$rB?Ì¥»÷Ïü “YXHêd%þ“?ù=Oþå–[ZE|ä#I^ÿöoÿ¶»«Ðoß‚JŽ·\z¿ù›¿¹vÈíWÜX­Ae¢ÙG[Œdu^Œw©ŸÒØ$Íñ½01}‹cQY½¨˜õéGùàÀÁ»…ŒÀ`¢‰¦pØ„w WNl8@…­»B&†Ê«>%˜Bœ¼Ô%¥in¦@)S*5Wizv£|3©æM0õ­ªœBWð¡âg„9‹u €î‚@€¡€C_ᵯ}m·Ðƒ›»ë%肌)f °o\¶UÒ­ý@r.E<é.JTóÐê.Ð+<Œ•1Á°´¸ x[Çs”\’‡AxIF]!2 éþ÷¿ÿȬÙˆNi¾Š¨LXG0Ñ"@øÞ~çw~gbBHð5Ø}:œÂø@‚ÛÊžÃ[³©Äfû¤ÐŒ’سފ¶.²;1”¥‡–âYÎ{"'_?û³?«FhJ—9ÿ €'ÏÆw·¼àº²™S1^ÿú×ÏS:Ý^Ï*#èh>"¾c9}øÃ®[ßÔCnG2´ ýr®O9ÊÜuŒ5(}…ÉxØ„w«GNr <¢f™U¯ÓSNñQ‚¹€ƒTdPšã>¦LUR¬sZÃæLyêøöîïEA¯6!õ]·:µºÐV0 'Îꨠ¦gÄï}ï{[Û?&ø¨NÖ` 0S¸©èÔxÀžÆ|òdj´î‚RNª«* Ür·É3ðÌZÞ¬[îðv­?ÊQ¾ ƒð2\ºB4så¦[ŸÃIš^¨¿úWÿê*¿·J 'ž²Í¾û»¿›.ÇÕJ¹/£‘X™[í&O:ÓäZŒ~ÃÞ Õ~³xwÉÙ9¿þïxÇÉßæe/{™ö°‡=êQRðÄ5häñ¼Jé› è'<á ʤê«_ýj]e+ÚÃ0ÝJ†6[äÅ–¬§|» Åý#ÓD‡ÅÆèó%/yÉt2ñ3Fb=ÓwBßÖy”_ôE_仓MjÓéä(88p§90Ö`FàæòNw{4<8ppà$(²ö€¤ÚJA—Ê£þFvwÝ%AuVI™R©:g앀¥vçqÔñl·£¦ÓÂÝm#bVåž/›º×!Õ¯åégbˆ°Á]bî®…Î?D„€"ë­)—|Œ©°ÉÄu<ðá€~ÎЀUÀ²€Ðz ª9x¤j.Á¶r%r'ó¬Î8ÂÁ ƒpë{)^Ÿ«,CE´:¿T‘±Üá”âñr<ŽãÚ«±{;ñº‰w/Wdóít =Çeb±T"ì­o}ë°¾ {Üã¦f â"rŒ%aWÝ04 ¢ê=±­Øëhrà½ímoûÎïüNdœ—Î-ìV’wvŒ5Xp¬@ÿyŠpŽvêAâìêq¦7}Ö³ž5”Ü„‰r·^ô¢e£*§¢&j´†+¦‡£ppààÀ]áÀIóïdå]yÊÑöàÀÁ•ÔYz­ï̘ŸªBìIQN[ ´z*µJJ¶j—òÊvߥ W›°,z”T|I (}ª_W›¤É2l!æk¡Ý(ÁP YïV]ô¿¦!À‡ÜëO®p”€ÐœxÜ«]ôÝv@k’å`8Œ‡jTÛô\f0 ¨Û'ÝÙù¨98€‡AxüÜÆßýÝß-R‚GŠ‘wë±}ìjÔ¸Ó`ðWµ0¨ÆiBüm›¤2ëÎp{mŸ(Ê·¼å-”’¿â"ÖŸáiO{š†›#’½ÿýïWóÇüÇmy·‚—»qBóù>¿þ뿾·³c¾æòeç­´‘_³ú +ÅRê$îi›½½º ½×Œä(88p·pàŒáwæÖÝò裓ƒWœ)ëtevTŠO UH!R‹Ý]ÃA)PjT=•J±ÆÃrƒ«¤|'ަdt6T6ÅžGI¡ÿ)ú”oª ØXJ“‡\ù8(”_ý!y2Ÿ§?ý髺GÐÙ`ÌÚ ÈÉÔ{€ŸnCz 5º½¿OA_(åõ=èÊúõ.ÀH†ÕÔRC,áà¥Ðhà­ƒ9ÊNrà0O²åjU:)¨h~’Z$Ãzº CnŠÂÇÙ2ÇÐú§ÿôŸÊ F䭛߸±’ø«\ã|þóŸo@Òüï|§®lõŽÒYm½[9þà?X?õS?5•‚¥W¸¿øÅ$;ÁZpZQrPä½A®·”¤mKvö¬æ¢œ]F¥­ÇQ*eÃÿù?ÿgî½¶ ØÀýmßöm:)úEâ/ÝŠi™ð˜õ"]¥„¤É¦Ã¾ÿû¿_CŸviޝTÞ6†ñfäÇåÁƒ×Ëkš|×$¸Þ'ô (µåízïÓq1“¤€B¤;C‚¢¤.§!5ZеýTmŠÒ7\Ƹ6ÎQÐÔtЅ⦾uE•SèÔúôIÝÏ&@s«ÀІ=m;öpCà2$c» \”NÖ‘ƒ.:c6mA€Ç-£Ü‡\êÐ-0iê–a´<¨ìn] €U’•ÆÜ`V0æ.n€s™ß˜·Ïqyp`Ãà Ü0äj]C}èC“,kìâÄå»õÁ~#mNÉ#_žûÜç’këú!²Í¥‚ÕνÙt׃$r›ªÉÄç<ç9×ZÜÿê¯þjÍÅuté„¢l3^´\k22# ìö¿–ƒ\Û©ÿ‰M]É&FEŒkºæn{弿ukP'%¹_ca¥C×ôG$‚¥CŒŠ‡r:ôv÷»ßýTŒø~š/‘MÃåþtk>Ï|æ3§áQ88ppà®sà’ÆÞ%ÉîúxŽ\APm£æ*PùO)Dj‘r¤"Ý¢.WHP„Žzê•’¥j)\j·õ´ -®‡š¦¬Ç&¤Ä;¾böòÅyJ¶·¬Qó»è¤Á ?·*†„¸ä›.Ð `T"($ŒÙ4téíÀžú„À!]FQK Ó¤q«Ï\¹ÀÀ°Œ=¡°àªÝÑâ¶•Õ)´ööþ÷ý š+Ëà ¼²?ý§¤Ìš (É! 2#禲†Ö²˜ð÷ɪ¢0¹ÂFÜì ¶8¿îu¯+óÕ𷆎òÒíÏÊ6&‹eåj7¹æ#ŸúÔ§z¨úiRAf°¤³»ë™ECÆÑØÛ@~µ¹¥PìŠ@‹ra{‹}ìcêKê¨Ce‡ÿ蜴ýÁüAìâ§,²?coÍ:ëÎÕ¶ Tgž¶ë‡î9$5Ÿƒw®ËÌ».â»k„G?®¨¶¼´«Ê£©B5Ô"&P‘Ý¥4‡'å)M±R²TmÎbÊ7ƒ‡:FL5k[JR*›âvI‰·Ç„ZŸ+Pý­­=n&;8±O7ZÒ dz뉀 ¸¢P10fÓí\? 8ÛÌ-À |ªÏxrÑ·†+6kÇ&ØV”·ÃÛMÜ)Èø­;ʆ‡A8¬¸*>ªÿðþÃ’5üUÿðþCqDg¾(w牤¶pŽÜZɦêyþÞüæ7ŸdeB !8YXNRfÔ}âŸøÅ_üÅZÏz4_q› „ã§9::v¿)ÜQ¶“ çäá­…ø¾ò…_ø…YƒF˜Ìåö³[ÀøÀüʯüÊ?úGÿH™VËŒ$ßy.} Ë^j2b#³ÜÚ¦‚#{P—}ÿ»÷ïN²â¨<8ppàNpàNxw¢ÉØÑäàÀä·ê;eJpNߥ©È!ÍnJ¦U3Ÿ¨Ú|©”/\$*¥ÜŠ5c)nê[o©rÚ3èq `°!()]@¨˜Ä‘¥Ùi«|ÅW|…Á Å¥23û”5X«=PjIÔ°×oÀ¬m)A5‡Uo*5dñP*(kJ«õ–2gÕÞ—6Ì@M›öG¾Y…ŒRYÂaßñß1q§tÞ«NCI¢#&þ:³~“6z6ª÷Út¨)ÉSÔÉìÀãá|á _è–§A±-Aú'=éI*Ålt”EÕ>ðdºûu_÷uö9dÙr×­†¢»>¤ó¼ÈQ88ppà.ràN›vwºá]ðÑüàÀ ÏL”T^ßTaëW”#IQVOuR 1$Ç+õZŽP 7uLSÄ)}ª9u<¦õM¨Cj}ÍJZ·ÀÀÙhÀƒ•ÿ[Häíõ”N™9qž5tòa»o àô\ã\ûÜ—¡ÙK í ª«€«8³ÿÆ–˜0·¼—‚p€\63h§s6ÛAÁ‹zÔ_MáÕúÝùÒ2{È‘dœÐsG¦&¹£DØÛ]Ò”1b!þÊ_ù+s¹)‘ß9~{nêEW½¦SJ:,ÄçÀ‰¸ï³mÖsèÐlQ ï–ýÈG>Rf°ö-¬Ûü¦ó×¼æ5 ò>÷¹ÏXƒîþôOÿ´z&(ù®P ™Œºz#Ž%þjQA›Šé8 z+ Vó¼àEÀ*Ïç7~ã7fGáàÀÁ»Â»hÔÝÅæweäGÛƒ70¨¹Qy¨B ±2IQ*ÏzÔ(nP©*©W…,ŸÔ.ìnif¨æ4,e= ¤Ä©ò:§Ü§~ mP¬7ðH˜[l0  5@EÌ&ÀC 2ôSMNÀ\3·'8”¡«C0iÍL3}*WfëcãY¿Á³ ®áôεÆàyeUüz((®:ÊWœ‡AxUþlÝdŸ´"JÞýîw{sB0óI½sZåw~ä#9ž³$ލýiµÊ e‡áˆô(ÿçÉVo_üÅ_Œ²õ1úšìžÐJn„þ¦ ĦCM=×Y‚ÆüÊW¾2Ê2¦"ëR²²’2ù—9™¨ícó˜MŸÎ3l;¥0³0Û8ðDÖpòâèMÖÓŽHò\žH‡)e–D‡eø =™7’[¬ò|ï³ÛqypààÀ%9p·˜swK'—ðAvpàêp€²ÅWÁQÔbeŠ2Gjª“¥L©Ô:%KÕ¢Ì,¤‚ã[‘;)hÊze&UÞöÊ}dqƒ Ý “<Œá¹õZŠ€lªB¥c¬_Z§>Ë·ÞÝ—³ZHÀÒ~DŸ© h[ŦƱõ[Ú`9•Æ Îu ]üö²{Á¿ÈÔƒ…Àá~`GÍäÀa^‰]Lyi3Ɉ, —9ŠÚ™-è\Ãð€¤I!ÒàÑ~tÂtäËZõ+öuFß›Þô¦ +ßõ®wåÓ’ø‹¹¨9y·¡Ù\’V ½÷¾÷½os·K[¢uŽG[t ùûË¿üËÝ- µì[]ê¤Ô8_ûµ_‹>Cn=ÐU1Ñ,6s×°oqs@М‹HEÅ7ÃÈkXò—ÃïŸü“¢Þ+ã§Â$’Q¶÷}Ü.}t¾î\}”¸.܆ÜÝØÕu½ÂA|pàæe7ú4 H!–¦À¥Ô%ÕY²PÊ”J¥X—lf¢¨à íÜ¿ô)•½2BïATüÆ&Ü ð@TÔQOp¢ÞŒÌ<}«‹eg±š• ̬ ëÀ¦œÇ@ê•A&ÀiîV® ï¿ñ»½&íMo·~ƒmÀ[Á¢ê:üðÀ<—øøuø–áeè‡ âæ¡ÇåäÀaÞø?:™X_–ñÓ Á·F¨¤0·Yn¹UÐT&¿d^¹–_wwW~°dúÕ_ýUÍïw¿û­ 7å_ûµ_ËxC)sw.ögwßûÞWÁ6ÈâhòM+;ä§·ÿ[ˆ¿šÞôÃþðôFI¬²µ3mçnç[x ÛTJ ­ÆÇY@Ô•OÝJ{ý}ß÷}êId|æDÄ̘à[9OäíM?ýµ¦$þÂÁƒ×˻݄»Û;¼Þ7:èÜxxÉK^²j@eÎ墤:)ÐüÑTjÇ>Q²iÛ9‚"þPÐuÛiSO¡Ïã(úÕ&ÜÒ-bð LuCG%wdÅêæ3€ M¼½'#Q{ôÃö°ž Ò63ž}(B j»(¡… 0èàe@km†eLö õxá¬(bìÔ{Ó–^Õÿ@A|n‰Ò‹o¼á볎òáÀaÞÈ?4ñ7ñŠÌ¼²¹LÞ*b·è‹âé ² x˜ú îž\Î*†a2ÊØ®=ÒPjæNm'Àš˜kÃ÷[n¹%ŸVÙ–g9nC沘þvßxàr>ùÉO–Ù9/ÝCò¼†žá¡lHº’»óÛ”¨¼>NI z’•QEݲ SÙ832‰{Ý’¶íBäé$¾ËLÝ2\oºé¦=?P•áGùàÀãÀgÈxû u{çÞñhupàà•×¶½U¦S”)M ”mÙb¥^ÑgYQ»ŠxòPÐ奲'ò3vmTüï´\iš£@O1à„PÞà`äPü(gÁÉ%"] bÀ¼9I¦2C´ì†Ô‘ZZQ“ú®=€Ö¾lÏOÍ}9tHc5¾A¾â]•AA‚……5©W›yÿ¸£æÆæÀaÞ°¿¯<]ÉMáéí¯#=ËÙµJ‡¢9¥dgNþÎÝ)pŒm쮕kÚWZ0Á 9Ÿ!>Š«ÜÈ‘¹,é6u¸·Ü{ÞóžŒ(æÐÆKç >å)OÙôC4gàÁE€8êåEɯ„sdµz¯<7vvP‘œ|ëݶAQé¹pRö ä› M‚²”‡‘ÐUåþ"Ísæ©Ìµ¦ÚhޏU_déh&1úÏzÖ³ÔG£0Çì®c;ÊÜ Œ5˜cËü2sÿëý¯w¢«“MtõùŸÿùM[˜³IO•\†Ôߨ ©Hê²$*³ÓŠÍÚA3æß:QÊZÔt†¥¶„À¤ Ïþ¤â)úÔ:ÕŸMØIÚÈ[ B€û×<ŠÕEŠ/ÝÓt4"0ã[«E9PglÑi0€45 àS>e€ ¬r‰æä`ÖVÒˆòäg`à·Z`!p"нè@ªY;?ÊW‡Ax£ýÊÏ£$l e kÃÞ¦‰XPëViQù%×ZQ^™O|⛤œN¨õÝþ½ MùÊ6š$òûoÿÍih »c‚ÞñŽw¸,ãÿüŸÿS@ˆïëÿí¿ý·z +Ñþ92g ¢ìdnÖ…^8ìÕ¥!>)Ϊ™ïg<ã3ª£ppààÀàÀXƒI –›©m• ¨xbÍF ;Ñí4Ñ<Ù¨CÝÚÆNõ¸ÁBàD\ÉÈ2P ,Oúß×A僇Ax#üŽÄeÂËìÍ 2·óœ©yüã/çÕD7íוÈùí&F¢´]“^ï@IDAT³¶ç”DÌ×£‡œy{šjŠåè´@ë¤ÉÃþðMšMêYªÓ‚?øÁ*I®W¿úÕ¿ôK¿¤úY‹uYk«5èV †Î“ÍUæév IǬC2Q,kïþèÚ˜®rôJ l¼ËÄ»N¶.5‚xÿM0ïx¸<Æ,ä‰(Û/æB2ûœ|}ùž£Ô¤i«“M["¨[=ÀtCs\88pP…{ý¨†ê,lG™J­+*5Ø0‰^òä:_žjFIM£ÉŒ¡¾)ñr“:@‚rG@ÑϨÆ& œ \/Æ$Üš€-J±˜޵s=YÁíj~’,ôiRÀaždÑQypà’8£%©ÑTgá :lå߬§|]RÄ(eª™‚V¦¬剙Tá)ww×E<0`¶Ï¬ˆbF^‚ ¢HK=—# ‚…ä+.“îà ÐA˜¤™§ð‚¼ Ñeì æšµ\y»ÄÐu‘ÄëdÅ1ë¨+Œ¢æ±´o]„S(‚‹Äš»$¹Úîž [G{”?w9p„Ÿ»¿ÝmÈÌ!­¥UàÇš8òtÝ·-$àd„Fâ@«¯ì(¿ËzŒ{w‰EΤdJøIyµk'•‹>-²Ÿdù…_ø…=š7¼á ºÅ® l•"BPÇoüÆoD¿Ja5rŽ}Ã7|ƒ&dýdpÙ'¤4RÀ ¬Ö ìöÖ¼mH¾…Ô÷ vÄ1šÀÛuÜ_ì½Î³H¤f ¿ë]ïéiWW¥n÷Ÿy‘žr|88pyŒ5Hšå:¿øó?ÿóE[Yg8ï±2­ðk¢á™Qµt`vÆažaÔqëàÀy|ðƒÜkI5èìâ£X©Wý˜¡ÍzÊ— VC#¦š)èLÁT6õÝs)ôQî(©ûêû¢€„õ–r‰a„†‚ù…IsÊÔáX’4{ѧ@K2ŒAÒlºíRÕ0[ ¨8Ï“”*½®|n`«uZà ÂÉd3½vÁ±ýê­oü!Ǧ·Çתث†Šà<™Ý}žu>§9p„Ÿ«?ŸÈ¤!×Ô«^õª2[¶ØòàÌpø&q65…Ê'… ¾”Ùù¿ü—ÿ²òHæ®b,‰fù”ÉG=xÄJ³/g>¡ä“›üöd6g£!dÅô·‹@ÈlöC?qÊv|ÕW}z°}èCÂÜÀo#²ë_BˆÈPîØé˜{·°.¿£Vâï‰Î¤§¶ЗÿZ<=`ÉQµzä#‰žÀM¨±=@ ÿðVÍ|Îìh¨Ç÷Áƒq`¬A2!]D<õÀ¢$Ææ ©U†‰¹µÜJ¬!Öd½u²<ÈÌ`›ð$‹ŽÊƒ—áÀ8µGQ*P Ôh‰\R¯9¬)ÜȨ`ЏSþ:퉚Nѧ»Ëƒ`Ôz¸HÃ}\HP‡Ú‚ ë€ p¨Ð3{¯u6dmkÜï^T X]˜œ×û|3óÆêlç¦þd!KØ€®ÊäUiT‡Tó.s&ÖÔ+¬Nü“à„É—ï,nøFÆ:ðÈlì<¶º>ô(Îqà0?ç~²O‰krjM«&*/—”VŸ÷yŸ73YaV«ö¢‘%j9ÆÎøÂgŠø?ÿçÿ<<"‹(`ו)Ë>i4úš}áW~åWz¨Hô?øƒ?ØLÍÓžö4”ÀVçz»Èk'7Á'óØ—}Ù—!Ußf‡ÕnÜcD|Û¸Q¾ó )äÃL.É©ìTYM´ ˆlKQø€<À­øÿë¿þë¶ ’pÖÒQõhúdXú!6ï2; œçÀXƒ$@Ù‰­æ]2”¸(6Ìt>yl½ÊB×mü_ŠÏ>9`ŽKsØ„1ê¨?8pžÔb(%Ey‡Ú¼5ZŒhŠ•’¥j)\)_:vI5÷ÊzbP)ñy.å^ê~*§0V¨°± ’Ê… f”1ð?²¾ˆ£éGÁ»äš`ò4+Á¦,ùM±HÞLÚÜ]/ q €^ 4`l9Õô¶­ ×2°W?ci£_?@v¼2AÍÞ3AÐKŠßuGùÎà ¼‡ÿ@Ûá1¨²pHOŽ1"òÖ[o]³ ›ºÉ¾xmb›ð#1Å*¬‹oÛ'ݱ n"0mžÎ‡$`²]‘hz&þöÍ«yéK_Z¸<²}TƦÕCòd9ÀìÏ·Ò䣲¿Y€;JΪÉö3?ó3j¾õ[¿uè ÷NòIÎfô!°‘ZŸW¼âSIÌeôZ+˜0Z")\.Ó"(h¸0¹¯IŽ7‚òö.·_RéÌ#ŽÂÁƒ—çÀXƒ«r ñŽ_3tíßÙ_íˆþ6i“]†¿Íakû²uƒŽ35Õ¥4Ü~S©ð=ßó=ÕãöÔ×y‚ž ¬`³ûË^ö2ÄDeıL³_m…I'wT~qà0?7~,îð1ÉœŽ@„­Þ¶yL©ý”VÃù-‚"ÙšO蛿ù›¯ùæ­Âهݚ¤†ëJZÍç,ÔMo[ Bµj!ŽÝÐl.Ë j´œj›».ßùÎwº›b}:›¢¼ÿýïïn°D^gHñVzqÑ&Ó§„׳né0‰©Îsž£ §RAT˜Êl<§ß¶…É.‚B/(˜¢F;ûåfyVÚëµ·£|pààÀe80Ö –<±šw2ìó2½ñýwl½ië¤ ŸæoGÏ_¦‡= ±AWyØ„{5®ÉN†¸MõÞþšJu8a\¶Í{TpÇЧ”)èõ™vz¢Ð§¾S£TRýëáO…¡  B ‡lBp=h1(òKÖ»•˜Â4lökMæüÈÅÄÝ;¼r‚ƒ[kCe,D¤•!ïš¡VàŸÕЋ‡d—š‹>àeþ">AÐ –Ðôºâ56¯p\Þs8p„÷œßâ‘S­òs‰™ö«Ï†üâ0+FâädvËÔ•ê*†XÉòë¾îë.|Þ7²£ò«yÐjPÝAò)Gšêm#ƒœ½“|á@’¡+Û³Óp-žs¬¼Þ6–ÞP>ëYÏêKì%fq_2Måy 9±°Nœ*¿#ÖM(È[Þò–´HBvÎ ’fzôè”yné­»EöI¾lÿïÿý’q‰ h;Aâµæ¼ì›ò˜®ŽÂÁƒ—äÀXƒàHw+fô%›Ÿ$ãGŸäõÍh—']ò'›Ÿ¬H62ù° O²è¨<8pMde­ª3ešb¥d©Z5Ô.å«@ç%o"ƒ"óˆöq qkŽ¡ê,â”>Dx(*/­}€8¡Ðbz®~±é.X²¹Û%ãnðæ¤;JИ$`¡ìtÄÈæÃ¼óÖ÷ÏÒ0;6¨¶íi65àŸ'‚‚q8ÅóHÒ›ö ^ ºGû½Ô»(™7#<.?+8 ÂÏ Û¯ã¡?ýÓ?ÝAAóͽf#I'Èa]'¬~¾¹Á¤ää]&.õèÅ8Éà¬ü•_ù•×ÄÄ£’¿kpæÚ°Íu¦ÒÁ;…ð{µÁ¯Ž›mèE>”gŒÜO²ŸÄg”ÿÌ+ï÷"J8¦­ZI ÖÓê),Rï;V™‚- 8Ö6Ñ߉«¿ök¿–âɰ´z0#,^T'z6¶úoIÖk¾±\ Ù¬=*ûìÏ혞ÂÁƒ'9`¯K»ƒøþ‚¦í„zžlrùʲš› —ou†’ϨíÍú4ìó{³ÏôsÜ:8pe9Щ fPštÔh—”,U[R“”搜µZ;©o5©rjrÇÕvýQúhOžL>álQPØ0Ð?8,QØü.@Hè ,™Œ+ ¨Ð»„+€œu¯ÍJYpS±—‚BKæD­gfŸ|®À³<ñäþõqÊàJP  •}EphÎW¿~{eû*긔¶ŸLV7:.?·8p„÷Üß‹jæjsÒW8p„÷Ð_ŠêD„&$a—¿Üe­žœ¨* Ç׿þõÞêío{LÒmÍÌW_䙸É2«z„#þÎðèmo{2ñ""žñŒgÔ仾ë»ÖU¾—¼ä%êí–Þô#SVž9ÂŨøÃí-U›˜[,ýÃú.Óá-·Ü¢m!«5ˆ ójí@àùkéR q™¡øßùR ÖðÛ¾íÛ2JY³Ós;H½Â÷Ÿÿüç÷ŽD!#{ùË_>5|'%i¬ã×áäQ88pˆÌ'SKsŠý2mÏÓÀLëÑó6%X.slýùž‰¸òaŒð Ç~ÂóL;îØp€º¤4G¦FÇä jÑS»”o:VC5GCYO‡íp£Ð©uÄTRLE“Ä6l VóÄ£ð9Áà ¼gýLÖë^$à“Ÿüä¢É/šŠk}‘°•¹”*fõ3ŸùÌýVæ3EÈô•åù%_ò%b3Š×/¦â"6Ù‹lùŠNÌFí•Þ‹ É™W½Pød–3^'…Ì‹_übdëBâOþäOfh=ñ‰O”5Þ]2wÿF$~K 3œÍº>šŒÓÊnÌB¼-{š/ø‚/p«Döø ê¹@‘¶GÛQPô?²œ¨Uö¡HzŠ ëÕ¬ßi/ç­#9ʜ瀕7Êl²o0S û…_ø…"Æ9¶sïgwŽž7ë7 Ée®"H\oþRƒÉ×nxÙsè‡~h¤×9lÂýÏqÔ8ÃŽLÎTª0b²åTvj<•ÝV@J\ …N­SîA©Ô=!ÐÓàxPf÷œ(ÚÀØïÔs›µ8ÅãÀ e^­¥?À¦PàAü¬» ;“¦á,ÖÅ ¸5·ö…vÁ€mÀçAàÜÉ ç-ý­‡XÔ›7]`¤'ÇÞÖçù>{‘~;¼h÷ã Â8sÄÔ–›ÄÔºßæ|Ïdi›|Î=ÏÇ_þ-Ä—Ù3› ikj ÐÈ%/âÕ›ðüÏtÜ=8°r …©Œ@3(+NÁg6 ¶á_ Õ¬möO*›ú¦Ä‹ä¤Ö뙢oµM“U€¹zÕ ë0”ŠÝ5©76aA¡em@ ®4fÆ%0£Õ ¸žÁàG= T†¼Î`Ó±¤,³€Iònž²^–ä]êçÌñ¨üìrà0?küfY¯ÔÆ×œZNÝ ¼êU¯Êž$MZès†Ì5_©py»‡›êN]÷òMó6@“2S£ ¹Kû°åY–S«=„2#¯4›r¡üÞNªÿïÿýæî\Î:›7Ú‡ò÷Ðv …å% ½oòz‚Ku(¶¡³}e,mÃúУ|pààÀžc P&hsuxí[©¹æ– FØÇ€ìp­œ8óâ¾æá"ÜošMY»zŽ›pßãòàÀE FÃ'£^WUKSÄÔ±»©fj:@EqSßiùújûQúeÂD ¬O<´§» …§€ÀFüÈ#ÜNÖ~”˜€™»²R‚C YÉ Ö»kÄBn‰/ ¿y(0¶Òd‚mkeeÌÓ–~ :Ù“mj¼/JÀ2Ÿ¾78Wptö7¢<óˆë7}×NŽò=‡‡AøÙù-$ÂÊk’,;3‘ºeöB'ë¿§"Ñ(ˆsÈ£¶Æ@^ônƒQà°5ÚaC¿9½ÔÝ|ä#­FÚ1\š¬2&—÷eÓ¼K½#ïÔùcU{SŽÀ“d%£ÿÝßý]‚¸ÇvûtN8rî­òuȘ@ö˜­äyÂ-7õ í` ˜æÇÊê+ek­($”áÅ^ 'WÏ¥WhÃáÚóQ>8pp`ϱ‹)*Ð æ¸Ì>½,íÓ3×A\‘Nær—í}Í º žß4x¦CaZ½`6á^·¬h[`ÀÀ·´1A—”o¾ž¬ÄtÊšâÖI™išt›]|­ÈéP«É.£I{Þ‚ ÄŒ´@Üž£=É@(¸5çaL0f°Áºz¥‰¬(-/µ’â<ïÝ%›hö0’ Mç"îOr`/†3NNýE`%` ^®h³¤ƒµJAÓlTÄç?ýp o™`/zîQÿÙâÀaþEsž[ËÜ^]_3E×¹´˜l›ÃLcS €[íKö-šMöó‹^ŒS­øobh¿·¶*|kð±˜8öèIÖ÷”§<Ű×T4k“íM™¸Ö»SþÓ?ýÓŒ:¬8IÆ«—ÃÏNñRÔÊå›ÖI£š]‚EB–Éýg?ûÙ=ȘË/Ÿ)þg«tûåÈ1˜廄«¯xÅ+–jóëlÒ1äáý®…ƒq`¬Á0SÓÌ*¢é¢V›zÓv³¥ðj•ª›F›V—¼Ôõ)aMi‘K“!ÁÃö0­ÎŸv@L\Ä5³·g¤‰È×ù­·Þšhæ•ÿ¿ÿ÷ÿÎx÷¸Çyèꡟ[â¾’¹ xê7B?ËA³¹Û¥®ÜœPR/Bp¬AÒÜ­Éõ’L7TÅê’;{ †ò¦›n*ýzHÄæ–mÐ-X"p™C«T]ÜüýèGÛL˜ìÁ›º½ém_ÏxÆ3NŽÿ¨<8pp`80Ö`ñWl°ÂÉm{Óê¢Âl)”Z°mÏDÜfÛðEmÏ×ëdrÙä5}mgzƒ¥’‰ŽÃ&<ëãÖÁá•ÚÄé›Â-ióˆ:¦”Û(˜É”Ê.4€*/?œ¶kØÕ¯ LÆÜ¸R6¡ñ&—rfLGð#74@RÌääk™‘WÈqï)@NOÙt¢C2 ˜±Ü·¦9 ³P)CÏÔƒjhZɲ}ðCæCNž_(K*`9œÁ'Þlÿé ²Öÿ@ÙÕ\A/Vwâ×ô>»8 ¿8þs9l1³e Í¢¾A%áÚÜNÉy‡ TˆB+rh8­y§ÖÑw4ê _øÂµr-ËÎܳB9«1¹’M9ƒ**•–‡’GÙKCóˆG =eþt¡ú³^€ÓPðH”Á” D`©H"í99Ðð€z–€(3æµP@SðÔ!ýÖ»SôX"&,k@,àÖÔ(À‡ƒÍ,‚jh±ÒìË€²¸ê;´cO¦˜D X®w±´ˆ ·ÀÑöRÆ: 1$À5OŸ±ã§š Á`/{C9Ö±å»Èà ¼‹ ¼Ts!×­¤™ ŽØš‰qÛt¹ã3Ó†‹Ë$ŸU¸¶Ö˜Ã\)­¤ÁOkzÌA·N.RÙR\ü¤§pz%ROgNo DÓ·Ù{Ë-·¬w+?èAò¯~õ«×[ozÓ› º+¯•ð†• ²7*ŽïÿØÇ>vO¦f2ƒñi‰¯Xi²ÖÿøÇ;•(6ΤÞ}îsê| A ñDY¡•WËÓCûŸÔ³¤due`üp™¬%VÍÈD? ­µõ}dÐZ—£|p`ϱ¹ùËÍ› °UoO|ù>õ–îMCÒõdÀÂå{‹r6 J¤œ˜ºÈyùžC‡Ù Ã&¼<ëÊ+ËvŽžUÈSH§š©iʺ©J}OfÑbJ¯è¢™ €Å«  À€2_9 ¡¬+ÐÀPÞØN ÈÀ€“ÌOºo˜oOÜÿ”ÅKsò µ’ýÌÏüŒ€[keeëxùÔ°íiÖÀOW@ (ÎO¦lyÖà׿•½oJ½Qp Xí.ø ĶZ}o>uçͪ×}‰ûÇ518 ÂÏ8ŸM•¼P¦Çæ|‚fHs²2ða›ò:¦Îuøáþá6¹%›´ŸC\.¬5¨ [“*Ý*R¦}Ø×Œªšs´È_Gå̃ÖBι7¼á SÉ•• (ÛûEgìˆôÈ+–Û,÷ùÉä4r@·‚gKÏÆôÐ’Á”9œæˆž´I×Â)OR¾ÀZ‰ÈpŽ7Ì7æYl%P@ùma]¡¹ÑÏÒA?¥2KŽÃŠ£ppààÀp`¬A8 8«¬A»þÆy?Ä—/L0‚ˆ¸-qz™S /zl¤¹y­«6 —­“Îû‹ú9YŸƒOÏI¿Ã&<É¥£òàÀp€bÍfKÕ®Ê7uL5rIYSÙÁJ¼¨õj4œP¦Rˆ ¤ÍA…üæãÛ'´-ÊSµIN£€¤Æ Êæ°ŠžÒè¼!ýò,›øÀϼ]…2™K^¶¬éÆ<£Ebé‡ÕºiØ¥ì/³(·L’¸…Ä2䄱îoÿí¿½ÏŽÞ2Æš¿}ímOh  °º€²ÁW·|V [oKµý:$amíá(ÿsà0?³ çdÊ‘43aS òNnêëø„æ’Sh>ñ‰O\4â“>¤õXÒ‰Mïø·¿ýíu¥þø@ [3YmštÖßx³Ê…åÕf¡R ¹K¡ökC1]÷¾÷½ÕO`}vÝ~$ù8ñ ÿ\eÑPŸûÜçÎSõ¨GéÇ|Ûcð®w½«[!Ѷ £5DC"GPf-5­Z£›KrZ{Ñv>Fu\MŒ5h)/7|‚±`­;Í“ýÑó³¥ð$Ä¹æƒÆ}&¤jÝ4È=ÎûëÊ|³y¢EŒ„ ÉÓª¦ÐÙk»éä¸<8p¥8@½ÒÈëg¬RÊÍ)Ê[2ÞSåq)sKIW%àˆ<(pIk€‡a/PQCßÀÆÔO¡%w•½S¸mx}èCÑOºàgÖ^r„Ù•7;'ªby¸5ÏÝdˆ)ß;o›»ë%à§+ °J°°ÕK&å‚ÿX®Í×28Ä «ëÝÊúÜØóž¾ùi„“ahû>šÏƒð3ÄØÛÎëËXÚüëï/MûgNº—DfwˆŸùf}_HÙá‚):·‹zh¸Áši2Á¬7VÜÐO;*ç½Ï£Ÿ±-‰Âl0OœØKJ¡ÇÊOç"òfñGÕ×Ï[ßúÖ!Sk0鿞‹†äŸóˆïùžïYÛƒ¡ëæ"Y Ñ/W2/]ÃÚKeÖ¦mB­Äj%S™…‹‘e9ÃIâO«>zX}”X90Ö`¸íŽys/i`V¹´6¹L™lI¾mŽž_O)ìäÒËô†æüIƒÄÑùcë/ùÐmüP±Bà]²ùAvpà r 5=¢£Bê8Õ¬†²¦²)î4UÞ¾ßl¶z ô©~ p½ £Ö7†ßì÷6ö ,ñèF²· AwÇúxZ43ŒuÏ€„lͪD%ÜÀ*’gcÅüxó›ß¬àm ÚÚïmËõŸ žÒª”<ç †Lè)° ²nç¸qó6Â3ßÐéæ|Å}‡GÍgˆ‡Aøa,§È—û×ߨÿu2ð ]´è' |6ÆlVØNú=ïyžË1e+vÞ2Îq=)Öž:ÇÊŸìÇa5IƲ§Œ›í$q‡õR…Y’D¿ø‹¿¸R–û+×zɾÚÍÌݾÎSTíjò‚Åoç(Ôv/k²½µÆõ½~ÂÔ­ "õt›Ôs$`GÖ’>½¦pÛ£Û¥‰¦]éIs—dY[Ø‹¯P³~ŽCuÖ_ü(X90Ö`¾­uâH ºSZÛž)?ïyÏ««Ž#ÛP®碧Md ûlCÖ1¤m›[—¼ôÊŽêdüY@ía^’Ùä%»ŠŽÊ©cªyŒ|Ê¥EC¡Së”»2EŸ+œê'²v^óš× 3 EI¬ùá@‹"˜ÜÚ[GåSQŠ;Z@—é3s¼™°§X@hrŸˆ, ™(•_¸Ê ìˆÅ•f-ƒjFXìwrzÀÙ&úÔ;’l?б×/­ `¹>åd¹NÝ‚¬€ëI@wÈPn>+Hž7k•';<*ïváÝÌR)¡Ó¿ûf§Ù:9;a?ˆ¿øÅÍVóI™=ͦF‚þA É‘KcËõÏýÜÏmÈ\&Ý$ƒÚÜ"ª2#¹»H“¤êyƒ°}†¢³<Ú›½ìèäú"~$à„lVDµÕØ|DjÃHü¬>»6£åhÏê¦VŒL’›XÝ¡‚Ó°è|Ϊ¯ýÚ¯EÀeXÎR Mû¿áýØ©ÉÊEÙk¶•Ñå*¼üÜÓÿQ88pp`åÀXƒäX òML`¨Ì{&87ÍÚäše‹E…S?þã?~=ˆã$± ar2kBmÝJÚ Ödu-ì|lýI²“•^6›_ 7J0£€E‡Mx’iGåÁX‘Õ¨à”òä;hë õmB¥ÊË%“oºoЀ+o'ð&O€‘GäØß×VF@¥©M˜ É\oÖ§?m2Â6›´×¦sçWJ€*hÄâA[ïnÊ„`Û ^ð‚V,Á¹Õí=àçÅÀMs—àbyzH02§ù&ºußJM¾ûÀªŸ|=I¦èg½al>+`ös¯fùEõw#ƒðnc¦Iþ¯ÿõ¿OÑ0ÞßÍHN™±'S)üÉŸüÉøQžô¤'Y|3gì´¹æ@EG $>BZNÉ»h&g­:‡ErñuµÊ×Ñ ³ûîäf[øu‘Z{š ýÆ7¾1!îéûO~e%¦%uÀC—;$«oí\b«Yp˜åÇÜJ¬Øª´Áv ¬6%âÒïÿþï·Z$X¹È„‘øáB¿ú!U²Ø¯Ö¦ð;vB¯¿ËQ>80kƒ¼­&ÍSI&qA¥¤R©pÉ0!øæºŽž¿æ–ÂuÓà&ºa^d_˜cë f¹öôjöïÛBAB;Î`ÔažäÞQyp …¾fÊ|Û JMSÙ7õM‰SåÅ…Rî{S÷”~)¬ŒÚá BlÒ¶ˆç¹€ÇæVE’µ€+Ù„L6!H£‰Q­OQ‚aFeÁ–¶`C Vµ9P?àÖæîz ª¡)¸‰ušiÔmD '²%<ý #Vn¶èeŽÛéX/`dÕÖˆõCL·SðâÁª~µˆ}W©67re¿ P,œŽÂgއAx7ðÖÌwîp^"ÿÄÌ¿)çŸVÙÇeËñ¹µö”4Y»J•ÒÀ–_mÑ_s ÒÜñœ{q€MÚå}Ãr(¯Áâ¤@r#Íá<5i+ö;ßùÎ}ÕhUnOÃ#‚O’å #}²ŠŸð„'ìÉ`2#4åfÖ„`}ï{ß«¦ƒ"ðmp¢­9¬›ž…‚ؘZå›VR!:ÉR¸ÅÌB^·“xjda·÷¿ÿýõƒ2ýÑær5]öjfú?  VkpŽ1kLÀ5ìJB¿ ”…²·½ímÓüdÁìyðÓ_3IòôpfKáùMƒÓÃÉ‚¡`Hv’f*½Z+^vrZ™ì”çäL"å° ‡iGáàÀ†ÙuÍ”QÄ©f—™·Vy·Sè”{GêrIõGy;øÿ戯ž•mæ›Î%í r€«•U\ ¢ %Æd¶Åñdl<8äA QÆ¡&aó \…Á­‹¼üš€jz›ôx¢U{4h·¶*ÃêI$ÖsAÇB¨âóe„hx¥-\óȃ²íæ]HcÝ¢·ú‡o+BVžKzÖQ”옹éí¸¼{9p„w?[Äkò¬Yfµ°[ÖßåDîyÿàü•›ÃLýÓg–pzÍá JñEçÊKýð‡?¼9¢t¿·6…RÈD•‚aNZÿƒ?øƒ¡¼ß퇱^tàÄà!]ýÖoýÖ´ÚJÛÕÀÖ, +Yq›_öe_fØ7ß|3bïk !š§?ýéjžùÌgv‰!%ŒálÅo"oí/Ì5q?–m KÜœÄ)\VW$,¹“1,-M‘c".r¶ynÐm?ÙDª¸¥í‘pýòÁ8°Zƒ2³7MŸ5qBÄ R~s“ƒù¢0!Žê½8­V|sž›évzºÉ>[ ×Mƒ'Ù×ìÙ0 E3°‹üè^ÇK%v¼æºLöÞΛOgf?lÂkrþ ¸š póA7_RǬ—ô~ÊÚ-ê›W Ð©u*ÞÜ×–(ÀºÂ&›•›ü+`úD–nŠX]bRh)©+wM•(A—°q÷¢uãhF,ÕÃþ»½ˆhÎøÂ:â l›æø‡ØÓ ™bSÝ*V²}Á°ÁHó,¯ÑûCÀ×@@ Ö®ý½ú„¦ª‰ L½ýQ·}­°y…Ó`öÚÏQþLpà0ï*WMÔ;­>ùŸßFÿåVÀ7ûdr½ô¥/íñ|0cÎÉ7µB"1 õvfÝiÚ÷¸üãçßêYÏzJßÈŒ¡þÅ>m\A¥Ã:¹»w"¦š½äÎEOÌGåqë‚䆸£)Dÿçi[­A”…fÂñ©wˆ"Ϻ·Æy2]%2Œ*¤MÕ$цcun$#¸…j¸,.BÐT¦™~P?Ÿ<Î)XM“>ûˆÿÍ{—® Vkd˜*fÍE‰Xà»h‚wðÁ¸x†{|ÏI’öHOýå äÃl)¡êc<&õe6 žyŠ6½ áå _‰½HpÇ«yA¯¹Þ­ èŒâ0¤66ážQGÍÁ(+A*8ulËØYœaS‰ú¦ÄÑPèÔz)Rô”~lÌÑŒ0X 6ä—HØGt Ôƒ@À£…}P$ì‘y ¨L?cê¼Ls¤ÄÐL¡%;#!C¦rS H·ˆlCàTC¶­·€É6øyÅL­ðo¥Ü”ÁH½õ//rxi…cÈô£êÇsKêp;ØàT³ X'L´'J•ý A/œ¶7ƒ<.ï^á]âçË^ö²ÕŸÑ¿ò؇.ý+;ütJ‘Çãñ|$ëoý-ÄÎèÌìÙŒ©õ«ýVæÈxž²^x꣟}$ê¦Ã\DO}êSK£¢‰‘¬“9ú‹ BoØòž5ž¡Íƒë¬sŸ’ÊlîÎ%W=š„,kpãK³‘Ú]a|uE2tr—eÉZÕOÁëÆSˆ¼ƒ¤§Ù¥û¥ñù9F`Q¶üÒ/ý’)Ù¼Bõ£d…Ú]9ÐMù’»žÖaåƒ76VkP9÷¼iåsr‡ÌÊ æSÆ•y'áÞÌSè*0gRŸ‰„_»º¨l— ™Ûxf³ôEÄ—¬?yl½Á{…ˆ—Ú›¸kç ÀFå»°´Ã&\Yt”ÄjRj²Xñk:SÐYeƒ¾(ñì@jrO€hEéךZ×dŸè9ƒÞ€ÕQ •íëHÀà=«)²¾Á˜ÖÍj5 +Í”K*£i*׈å®ñ””ÃVåBÕ6¡zÐn6ù?]M€Ø¦“¹,¶sP%¹fC šêLš)øQú±@ÜâãZxÜ{ý ²~泇Öjöo=Ï: w‡Ax'y(…î˜ ó<ÖB54ú&@höú׿E9þª„ŽP„‹Ò“ rÏÌ­·Þš'`Hmn¾hÞÎÓóHåq×|݃74 _ó5_c›•ú¢#ÔÿÙ¤ÝGAyÌcâƒï5u}DåBX‘ñµµo¥é`@aô9õ”ä#AðÚ×¾V{—•Ë ã]„Ñ'[ŸøÄ'N'¢Ï3nѯ˧/F{›—J¾ÚÖÁqPI†1 ¯û!tâcOÂôÀ5Hd5Y|“ucàá¿2Ä“K¸KsØKÁ2næ3=œ¿Ešµ¿Å6Âí|Û3w ¬ 1†jÀÄþÁz5âã¢N V]ôHNÀÃ&¼ˆ]GýUæ@›,£”©éÉ\•úN•—N“ŠOªPúúYÖ-jÀƒÎ%Z'ÂW†Veû<”RE€%À z@ez®ÌÌÂ×ùM©×ñ 8í½ó –[à–žÇzœ}4=.ƒlÛ £Kl„Ö~g"¶¢#=¤,Û=„ç¾óä¼bK X[(/  îRº€O“œ­ú¢é3H쎊{Þ†t²ùQy9p„w†"Ô³%æT!qS )Ô¸¨wÙ¨P&PÄ:~ò“Ÿ¼ˆ¸)´‰twºN¦5·?8qÖ†½ÍÖÄ}·$]ã$ÂΜ¡7‡Î׃G|×w}—†Ür¯|å+«ì­7òKAñ™“0ê $ËjEZíÃýæ‰R!µèg›r‚‘‰ÎÕ1 Ÿx2[ù?ËÞ÷¾÷nË®óL;cΉ˜S?þ€Œ¿÷{¿÷…_ø….Iâ¹”ñ­X‘éí(¸âØXƒl¤Y‹ãqßûŒÎ°kR°˜qæµ'™ÄŠØÎ´=sk6 òëûèÚ8ï¹?ÓÛæ–á•ñ€—I“3'íM2*9îË›u؄â£pp ˜,¹‰Ís-›š¦¬ÇÝãVªœZ§Ü53rH Ámóí^÷†½Àƒ@BòmØâ/ý%0#oø| Š”V 8i1`6NW MfsÆ# y"ܘèŸóôb!0Œj°öR‚d#¯yx½P‹Áœ@àt~²@j[£G”ÿÆÀNàsmRv0u­\ËÀmÛy4oðJ°)ãùèŽÞÚ÷ú¿ðM«ãò®sà0¯‡fé#ñˆù×lNÎ¥‚K:l&óþm\FO–]ÓU“=³í" ¼¨âc]ß+?çf¿âæé¼VE2“ºfCÓeiTZ=#‚ËÊ àÍo~sI±¼VÓäWм§hsæî¦°Æ lNeR¨•2¿Éˆuñ3ÃïÙÏ~vwš@ûÑ~´ ÇÊÏÔ¶IõF^Ò?Y³A_WÅ­y®Ãˆf§ÁºKóddïæÕŽËƒW‡kЋy#bê¢ò3ü6é‚™Fw1<›øj Ž|nÓ Y¯àÒ4w Á™Á\ò–AfÅéÓà¯w̼ãئgç,?lÂKòÿ »:(@Ô4ñÕLYÏÄ)ñz§Õ›òÅyRýã¡nC]ð`|CÀƒn‰ø Z¸Ô°¡¡Ø-€$Ó%ˆr22¼æžÜ=4?)xDmœh½ËT‚aEcf‰¯àÖäk™ž×‚èÙŒXOÙGc­”`¤ñ¬‰OAÍìUàsMš¢\-굟)º™îˆ7 ÿ†f tkˆ×Ï ¹Añk"íéð(\†‡Ax.}šÆ¹Ÿ³0èÿ20Ñwÿ²ò\´Óo}Œy;ýœŸ“µjAoÎXÿûßÿùŸÿùžHºÉà·öÜžÀ—¼ä%kåZ&.+š“8ë­}9':aj>G·ÜêÚ)®a9m ¦Ö!ã,bëU  ›óñðäŽáò¾-Q¶öÐÂ]Òs"ïåx@ìéQÚfiwådLhõ1í&Ÿ5ø•gÑ‹ 6ûtuˆ¡õ‡8ÊWœ{kp¶¬Hw&ÁE|ã'j« ɹ®Õ¶M·Ö ²NÉŠÍIƒ. . ïßôvÑ%©žÉ€h^áÌšÀÉ~}­ûœÁuØ„'uT^qPÁétê~T3eMe‡@”›;(÷Ø5f$PM™9ë T˜z¹fÀ‰Èʆ¬Y§ÇŦU<$!Ç,ÔX~²íQy'8p„—bÄ“utû?ó§¬*¯ß›mö]ÃÏyÎsRù—Oäžxjœ[å)}èCÕ8ë&‹ÎáWÿëý¯¡©P®“\ˆ¡Ai-ý ÅÞ4ß\öʲNh¶yoÈÊ|%º©¢ªuK¢­ƒ(Ú—lJO“)O9]8Ø’³˜¡#ü‚‰WÍÛɉ¬Š~#ìuÆk¿oÆgåY A…ƒW–{kP`R3…ceŸxóšŒâo¯ËÊ‘Vë~¼3‡R\Ô-+te¡òä`T–õÙÛRHm–˜$̨ƣÑ7õE4$Ü(ÌeŸ²ª±-ý¬Ksž’eÍi )÷cXkJ~²BoYõäxÚ©ˆ-k[eìí0XLëèEÐT?s¼ê†~½wQ}ýFÀð ·•l-Õšì?³¦â–ë$z\û9Ê—áÀa^ƒKr ›0³¯ hÈc½þƒZUq™œìÔ¿l¾«fB;ž7ë“ ßò–·xœ¶ÆmÛñ&Œ»†ÏþóQ>ùÉOÞôc?OâŒ48®k^ó¶hgSñ ïÃ?ð¸¿ü—ÿ2ú?ú£?êä[–g™tö%oÆCµ‘oƒ'ñ½_v(¯ŸMbh½u«[’7¬«ü4L_‚>œ÷ÍßüÍE÷éÙo4þÒò2áÖ™¶¡ qM³£h€Nêöä¡›Næ’¬k:C?gNt+x”xœæ—);y°„ÁšåuòC]¦·h&ºciLóaµòåû9(ÜØ(=»i’‚NYËÚQU¦s ÕPñ±"ñ¢ °•–HÔƒ &lÞó5 ˜Q¦(4àdž±%MpkÒ+ `£ÈuZÌ~ö6Ø&# Uö° ¸Ò[—@×ô\0+"¨vMƒ0 ÊØÇ€Ã}¾.0Ò°AÊÍã\Ÿ“2(­C0uO¹©wõ úκˆ¶çm¹ÒmØí‡Ò|> xÎ>5´ ®ožx\^ƒðBv™ÆL‚ñC˜Ï÷¹Ï}æ± ] c¼(¾@IDAT*|sçÚ5Ó¨8O{j \lgà‹^ô¢•ìd¹ ‡¹—xeÎxbZK–°öC^ü½¿÷÷ ˜Ÿ¬ÃIË %çòJ¶/wÞƒ†rL„Sÿý¿ÿww P0%T¤qù¥_ú¥C¶/È©ÕÂ1odßO8f*²›(Öý¾dk·±˜›­UÄÍ>¢á[ÇËË^GÏÕCPÏKÍÖÁnhÉë¶ ºÈ:Í÷&Â$¡) #Øš{ÊSž¢+Ÿ9–yਦMAýv.y³æÝÂÁ«ÌfÉ}ÑÝ6mîu/“%”“ÄPã(äëeŽÐÍ\àŒ«Õ©´ö#Rks(ÅzwÊg6 Íɘœgéä$M•k 뙃%¼HF¬W;°zÑ#ô¿ÚPæD¬aûa^Ä·£þªq å)2'5Ê&‘Æ·[,Šsr¯mo =Ü5a‹Õ B€ÇS› õ„ üÓDQ\]Jc3¿Eio„«{’–€ÐÚC+`›Œ€U¶+ˆ•¼ˆéy @Z ˜„0ð6õûàg¨@`·©p (®F]ë¥4»ïG 8Ãuȶò‘j8ó&¿K»þV#V´U“ë.Ô ýÖâá›Þô¦ulþð‡užPÆ´Œg“ë­eIžÂö‘ßûÞ÷6åÜK|KÃçθ7ȩѭÏe6|®ã9ÊnH&“°0òŽNjÎmšÐîùz.ÿî R òPÚ^Œlú™ô-àÚ?¡¼æ¦ÁMo›ËËl)ôÐOÀÐ5S#x § nžxò3o<·}‰aGæ¯òüdGåÁ«À¶¥Í4¡²[*L‰«WSœEOÝSúT¿zå"ƒ&^©óÀ†ô~@¢M}ÃICÛàGf·@õàJë ÇD¼#ȸš”3Ý Á!ã©0I Ó<«’=Ð:³/ TÓƒð¶&¥ßtXöæõÔkp7à \£/Â5%9PÚC1 XÝcÞrË- OdÅÉ„:ë¾d=wp¢w!ç‰%Q£[Ša ¦§ !nK\"~ãÅ/º od€ØËúUEJÈݧ,"‚Ô˜:Ý ^ç…%$‰êM=§ã ã(¸²X­A¢‡p’i2.ö‰†8Ï+m“oàÂ~+ËɶNt˜3…9à «ß4x²Ï©\·npÕE϶' ^*$ä57>¬==¦µ:“¹½G ¸ÊÁçÖaîYwÔ\MÎcR4SRÜ”xAFêG¹S÷”~Á`0кb¶Ä¤¦·Í> Ã-ð£Î’áy°hÉÂc€™îfì:C<6!P”Mx&# pÕ^º¦“M!ƒl‹€Ü† Ë^Ö8×»ò‡èhl¯¨õuÍp¥Ÿ2PªU\dÍ®‚µÐŽ€aËÜ3¿æÛ=?cI ‡¾(¾†Qxz®?ôüš›VÇåáŸcw…SG³£æË¿—9Æ!tQð$•¯άÚ[œŸéK¾äKN÷Çñ£[YƒÿÜ<Ú§Ï—¾ô¥:A<«üwPmÿ 4BƵ\˜„1ó+o‰>õ©æ°¨‰Í-ƒO(x¨3gJ´p‘A8ëx^a“tkºm5Ï9 äS¡¥QÔQ‡¼DÞÈçÖ[o­«M8G[ÉÝr^ÐŒ¡Ýáy×l7WŸlÕ›“¾÷3)$’@¤aö°zÝŸÓÿQ88pu80Ö`x¨n¬Ìg†™) 3.áÉS‹`DâI.ýÄOüDmPA'ÛÚß2+uÛ- ™ªB÷ÃX'[]¦RsèÊé¶äïëÊä™­5'û÷j…Ê{Y¯|’£°«È ÄFêfV„ŠÉ§ó›ð$ʫƪ¹©jŽøØq—1“*O­«OÑSú¤Þþ@À­àÁ –:rЭv¯ KAŽŒ ÄC}fñ PA´ év`FžmÁ·6 TW´8W¸®:ÎC@¬N˜ ֵǕ&ƒlÞz#pn/~Û/sruá_þËé €œ}ë#öåö¨`j\ÇC·¡ïN&Ù’C¹~¦IÌcðú$÷.r’8Ö˜ »OÚG+`~ÿú›!—+ƒðϸá?²¥yÿ^ýKÝñv/û@æ ó?kðçKù]øß/_ÑfunmT<¤dÖJeaâBÆ=g«É_JÒkz—-¸kÀ"k. %÷Dd¼\ësÅ<”V")¾ôÄ'Š`XD®ý¬åvå@yÈLõ}b÷ÉY,}VÛ½l9T‰ã¤ôùîw¿;vIäPRœÍæivÒˆ­p°x-ý´¯‰³ˆÁ¢¶}(Ýõ±ÍÒ79âÓ£o«½ýãw\ßë(¸‚k0¯ ©HÝÒýÙ0& d[D&²T";UòyÏ{^ókŸÜü’ì½|õc$ûXŒKvµ'ÓU[ 9¶‰²`‡Ø*Ý_¦¦ECõâz,j·¡»X‡lÌ¡‰©~‚Ã&ܰñ¸¼šhQ.!à›úN+7_º•º§ú0 °&À » Âp„()PZL½BÉö€PDCÀ‰ú<>%Ó`€ `S¶¿N[»‡2S$0IoäÌJ°–ó_£½Bhë]ePÍÝŽCXêÖ7È ÎßÃÎz×´ Fîc8O>Ô˪V[Œ_÷{ƒØµéa.AåùAh@ºM@'cÙ¦•p>Â|DR$®Aú“ë1kó£<8 ÂO³âµ¯}mÂbbrúÇò}f~ø¨ÐtåâmYܤâ§Y öåRZmÖý8œƒ¼OûØÇjÕŠß+^ñŠ}'kM›ÙÆè3±æÅ»á´õÊD*}Îv_˜ÓÄ”±ð\Ô¹ÄÎÄR¶2;“à;¹í¸XpA…×’Âã jQT†t8ùùžúÔ§™ëWÑ ˜Î™ëöNb½˜™‹N6Óç{…‚ûÕ”6:baSÙ‡dÄŸyý£ppà r`¬AS)Ç6ÆTÍßl¦^›H dÏl"H9ÑsÁm­Ûßi–Z¸k<Æ ;Â>éN÷¬¡˜…"ÁtNðôo¢®·s/ë•õæõëjÅ®=Ôc®‘QÙ¨^9Þ6áõþýÇÒ)ÆÌò¡¾Ûn“BŸ3!R÷TÐ À À ©„xu%õZ¬ M¹¹3ü4À ˆÒ~–Yâ#3)›bPA=ç¢lÂ`È´§©¦ÈØ@—WXwâEª ØÖ% WOÐn0Ù{E=œ|–†“*fÝjx’(õPµ» kHˆµÓnµ–ˆó'û™J°¹Òà´Î­yÎÝ3…ÙÁ¤IŸ€4`ÞŸixÜá§x‰šáþ‡Zò¾ãßé¶Å"åO~ò“ï3…Pf†Í„“ÒóL“ÄÓLøÀwOçLZÑ•#VÔOª¥“}®Õ{÷ÌÚÄé©J”Œ1ž˜u!´XüÇýU¯zU’×V`SW?~ðƒ×Î×ò¾ye_h‹sø»÷ï®ç[|Ó7}“þA¨¢Hù TP²ƒe{\ñ¨ˆE‰¬Èö;N²æ²Ú´ñ‰O|bH‹ø«O=pé AYµ‹_víó(¸jkù‘ç‹' 2ÝoÖ˜b'-1!=ûRžà\EÄB˜ìNós6 Â^eá#ïy(Å%GÚè*LÙ;n¶^²Ÿ ™W’o„XŽ|¨Ñ q—VRF†á`òCø9\6áI¦•WаÛçÇm¾J¼€#5”{uOé« ±5©‡˜io.Jã%PÈ:=¬…ÌZ «,,Þ[ @5=€mS ιf³P–Àiut(7…YZÀ±‹â;jR¤€:=®…} @fsÐUôåEà9aœ¾$ Ö®rÒãÒùç_ù¢Á\©ú«n~èCjÎoþ¬ªÛõÛd¾(b{ýG!0éE \ò?¯DX0‡®ÄUw ü!¯îÚ¹rŒÌžMý\Šhï_?¬°ÚuC³"×p U~/»®­!Î[›X-Äâ•KæL8e!ãfõÞí=ƒ)€ÁÔú¿É ÕZkÂÔá9c!çx›õFés4·­g0 äèg-ž¡'Nî>Ùœê§~JC'Æ‚¥ÌÍ™Ý^}ÛFm".fäGáàÀUàÀj ‚ &Ha]kr9¨Iˆ]Ä5‚”¦/‘:·úeDëE}òpo6 ’ß]ì쉌ªC¡°@I=ϖ‹v™z/¾†¢¬1¢5/ùV'Ó txØ„q쨿R ¦ÇwcšPâ­˜)Sîsb¡$1ÉõÀÀ°¨O“«øÏêK?S·`F•€‡¶@H—ÄN'ûT€–éS¤lÐû€:ë­µ eü€LkýZ.œÒSYð9!°f%ÕÞ åÉKzByAëö¤œJBUÿ^tT©Æ'™ êãÀÐ…=®®A(6À¿l“0åÚ¿Žoÿây5Z_²l½gÜZãìšûÞ÷¾5ç(Zo/§ã¡ ¡Ò-'Q錊Õ÷3=jq à˜—K¦Ÿ)>–)I0Ãވ݄mzö”YškA¿cFg~UW lK´ˆ èÞšjuwQ¡Ð0­l0û"²êƒ£­ñêDi>€ì_ƒÏ÷yÕî^Qƒ×¤ˆíùG©` ÊÄúWXÿÃ.úÏ€r²+ŠO¸æL[û)+9•U¦`ÿñJ0eXá&Š»¦z;ýxqÚëXnÒY=›6…‚Ës} E8é€)¢•”ÑVÎ=àhY}E™ˆ'ôŸð„' oW[n†¸ü–Uu;ñ Cðñ¼dž î&ŽåtV¶Ã§3*žûÜçcÐH¯Ó¹´½àpµ·Ð­µÁ‰µ»“ëmw&»‹‰ïÑÄÇIÎÌ ÂÁ˜«5¨ÜʹíûkÊ%²ë’IVÌÖ|8ËŒìº9ç’œ?™Ø”×ç¾Õ;¢~ηõ¸äŒìócíG²©ñ²^ybD Ép¤•Š“/²inQ´{Ò©N¬l~¦M«ãòàÀáe‹?õM•SèÔºK*¾œ+ÊTÿ1Hp+À<¤÷Á •ò ÀFó4Ô‘Kºæ ÊaºlÞ´Hð\$ñÀ$èC2lzp™nÌ?5ÀXÝ–¿4ƒðdL¦×Y³$€ûþךVAI•r~‚—jî— J±sÑ «¸šNMT¸(kýúô)ƒÓž´³Ïši²_¿æAúOótùüo¢áæ‰W¼p Bnž‰œîçÒlþf zSß%'TR¼$‘•5™`N¶Z+;½°1|Çw|Ç$SYi*ƒ\ÈÖõ\&¹É9¥¬’GÙñ\³/qßU5“‹Ùè‹ü%íóá9{ìcëé„Âë^÷ºµÃŽ\ÃÇ»[˜>AœxPϵ­‡ÎVïM”Ed!½›si&`˺&&ÊôÄäx¾´~«ÝÊðæmÛá*¼ ¡v¦P­º»ž2´þ(¸á9°13 ž"ˆÂFMß—\…ƒ²â¤ò#÷ ÐÒüLÒ“žMƒ’é­û÷Ä롹ö÷4›šuuñ¢ƒ%<´ôËTÆum)´":©ð&F ¼®cë­L&Ĉ¬~ÃðÓl~¬Í{—®Êì’tÊì©Æ|1MÆÕe6…ð'ïL ¸'·€ mŒ"äAÀc³i%Þ*¡ÁÍIÝmÿ‹='˜ä.È” Dm~²ÂD®µ$ËZÒZä¼(-‡–¥ÏSÀ¿µ“}¹ý~Àd·ì©Izœ›•ºöàœñ ‚µÀm¿ˆï“' î ÆƒÐû90|zAî“ •è/ÚáØ’3€ L€K*…“c»Q+¯–Ahz¯‘Nó/"5‚ÿ¹ýoL÷“öûî:pkqþÕ ¤føI1±ï¶Ï*åù8è­m'ÓÛ>ð©DUZŽ_W5Ë}|&©¡¶)YÛÍÁ8Óy…vKßtÓM(1aïÚ9)¡ÚÉQçÐ [ÊÛô첺I®°!èìxÍ%øÚÜ*RÂ&o·$Ž#}”‰xÄÜfÊ;/³ݲazB×ì~îW+¤ôÐh|Èå“‚{3žãòàÀǽ‘{ȆðGsdÃyðÍ£%¾hÜ^ܽ êÊ4œ0­‹ºÚo¼ˆrêÁá¬ú'ÆÉ¥“ž©ˆÝB€ ñ5– ö§ÎC÷¯6Y¼òÆÏ!…]aFí›oj¬L6N.ó€N™´ö?Ù¦áqypà†ç€¹™A•€¢ÐÕ´ÐDÑÏ4&oJ¶Îd6´ÌX0Tv#€ažfr:{g†«aÞ¯.´'ÀüL“µÐÂ#àÔ6Ûí.ò¿fuTÛÖ>7åŽ}6B ð ¼)Nª¿êÁAöG€ÀçtÛ^èkf^l¡ç½Ðì4?SÈõï¥Ð*˜¸¼ÉºMCÆ“ƒ÷“ÿ¬Œ¾™ãØtx5/¯Ah›ì—~é—®ÿ Êþ‡Îg¤m j“{÷=ïyOÉÖ…›¯;a— ’fÂuîyª}=¸è±5½Ná¿ `@Söû¾ïû¼×EK[¶·òÖs7‹o›GõƯ¶¾æíc25‰³7¿ùÍœÙÚŠžú)$•0¿}†ùøÆ7¾1O›ælÎiU¡”°¹ÕmjûÍÿð#ÖÿSŽs-¯R¿o G‰ ‘ÍÆHâ†hÈ æ®>7>.\ìM‹Ò39ãb7A̾ð9Éœ3GÏGÏ|æ3›‰z>AJ˜\´iðäC§’;ÙY±VN¦BUÙâ2Äy §Ï —ÙR¸‰õ²^yÓËË[¿¶*–Ÿ ã d‚ý·¶:Ê®œ®nFô¡Ð©õ jHÝ'pL™YµpTÐ lÈBNQÀFû“ƒ È†Ÿ¹ÈuÀ€1ëÝ<àzËx;é‹o«!Y>eTÓÉ™:àÙHæ‹VëÇþIà þ‚'ÅÊ–UÉyº‚eÒ¯þê¯Öœ"(­£¯¹Þ¨m;}z.è»®a¬XËâ/< ¨žJ¯YbðŸz…Ì{p}­Ü”ý¾1_·óa\3㦟øòª„¯yÍkŠ´™ÿ3ÿÌß~õVÉ^ýêWÏ?·P‘ÉCîE/z‘nçtæi²)˜í¹‚È£DÒ¯ÿú¯ohö—Aÿ÷ä]óJÌôÞ½‘i4‘ñk?¼8­jr˜e޾÷½ï] Ö2|sè}ï{ßzkÊ­¹qÕ@µ–fáÆþØ™]xˆ,Ëp‚pÞk¾"ÎůÏã*³®gRiŽpÌýFŽgÀsò!.öã·û·Å‹·šªU>¤­W+Ç,Ï“XaA}t¾yîqypà*p`oT -å%µš DÐeBn.sô¼P®Ž²ÐóÉÒkn¼æïB®Éäã«“OÁeæ"‚“æâ™ÎÏo)_&FÔ zÍ3]¹5hrlý¾a)©q¬Œ )©ýÏ·o{Ô¸±9@}'¦|Së”û¬¥·¢˜CðHÐhZx8‘q8ó@Žé|ŸÓ\ÑCÐŒf†ÏÁ€ì @®°'²NGL !ks°! ¥óYÏœž+i½Ø¼mîÎ%ȧ`²US€ðäâ^Ùö>q®î"ÿ163LoÓÿE×sñR%`ðEÄÕ—„f“rä.è×ZÒOÿçWGu ð·N€x>LÂùÁ\‘»7¾AèßÝAyóÛWà'˜Mwçé¦qs’X)ÏŠÉ`qlG*HÿgN’±ðÍœkùK´•DÉßú­ßªÉeþïw¿û¡ÌC£‡ý\íEÊ‹µºUªÿÃ?üÃäÙÇKTlÃÉŒ¦è-»ÎX71oxµæAnµ›¨'Þs£³zŒ¿…»ëf»ŽTTeÅð²Â^ß<4÷žžEVÌ­l;ö×\˜üÂhtÔ:åÐr™Ò@¨Cx Z~T˜PâöÂè ™Ê àŠzÐ%ç80Òt«Ý(­ä?Mó`ìYMA©¤.p¥“kžò5áNÀÛÉÈI€|FþyÉÁBüió]°ÛälŸú "p4Ýì/A\Ïõ+½ ¯²€a<ßWFÝØ»xcH’ ËUÆðKÂ6Þ/2NÚÆ ¿Á Bñÿ·~n¾ùæKþœü4ú¿7ÖÇPl^¢*Óìä] šZo48W¢yÚÓž¦ÿ¹<Ù°ÊÚ"æÚdZ_[½þõ¯Gcâ­•\_íš³ & Á­l¤·¼å-+ÙÿÏÞݼ|·dwýïÁïŸp â 4ˆ“ø€H+ˆŽ ±CZ¡MÄHTHt`::ˆ:D1‰ŸÑƒF#IˆØ Ž„ŒÄÃßëœw²,kïï¾¾÷9÷9}Ýwöwp]µkWÕ®½vÕªÏz¨U¥œ“¯|Ò*wm…‹ ã,¬bƒ&+e:*cTkêù`ÑëÕe>ÀPöÉ-B£„·ó[)¦¢œìŠ\DÚßÜVCN9?äp[ÇÚ0W&ÂMMmoz_Þxï)0Ò`âÄÓìC—ï h]6e¨„¯Å'ŠØέ»ëA¦×4dŠ\=Hÿú_ÿëÁ8k?õt¸nv½;ac0·ø°gLk#[ZÇfK¡ëvî[ðâ#Ñ­…õ¹+xíÀ‚“÷iv>7tavfÊqËg]Û¿Ó7~#P µ>®eq·Ä¯ ð“S±à¨Ð†”LO€D[H*f ÝÀZÛX¸"t`b€ MëÀS#›f¼Ìì ëUsÄ´4Nn™nl‰$ÕØ·ê\áºþ¹Ä@òAĵµ/0¹f®iÀxœT_´Ë©ÅÀÝq™H©î1riebk+P¦¯–牦zµvò"Mè ® ãtvQ÷=¾õÞ „i s~óÉE"“¸´d>ùQ›ád°iJ£ªÓê¸@³ñ¨žá¦œjŠ­Ü‘8S=ƒ^g§Læ1AÏ”‰_u£ÿX`r²Rß‘opQUy8 ˆõt¤ÄÔ•À¼rsB¢?øÿ Z]0…ân¥lö61ÕÞ!ÕÇÏÞ\ÍSbÝoMleD±Ž‹åkRá~«–ÃzR¢¿kŸ;Zdz”}RN¤àQü4F¯ ”ÆûÄTØ¥âXº¿á/ü…µý;}SཧÀHƒ¦Ê#VÑÂÔH)kB]»VÚjR & öG8z~õ õPuc,ó[`Å#ñJ¼­ $:ó‡<ã#úèE-¬ƒŒˆù¨˜ü1V¤¬ìÑþúpE“öAo™ð‚†÷­÷’ñ™–øP´¥+fÂh»æ(?pbp¦Ø(RX‡h¸â0¦­†€ xäh™ÏäP»Èyí)³nNSFc ô€®õîšÎ¶Å·MüUˆ­äv4@ØÄYCŤ©4µ>eÒ¿ôK¿›MÇ:w·DgB¬&G0¸=@à‘ £rù@Ž xV\àDßëƒ/ôá/õ%¦GÅn’¼Ì ·L¸Qï¾|ï)Œg ´¸·Ðn Š• Åt 6L`yeŠNf¬,¢ø1àdˆÙôDÒt`} [˜’%RgkLjë#à´•q©Á¸"Ðu¼[Î*ìp!F nåmeÿÖF0±¢9`ò@£[í´Ú¬k•Ò½TútÝû[ëoË”“¥á\$Îò©ú®"ò‚Ó\óG¢×XK–Îo– 0·)àâ¢þ"ˆSà7Hâ½!˜B›ÌGÅpÍàÓ†7ù×ßX\Ý$üÈŸÜ®Z€ãÂõRW4atƒ*hÄ•õ¹æƒÞb+kfië}¡®Ð`1Z:•åXxrÀ»Fs{ç¤O#ŽÆ†H›Uä–éÜFÞ‘Z¯Â?ÿçÿ¼ÆýQ¦Î+ç±¶å2"û»ºÂ¦–£¹îå\oŧV߆$[ÝXe~Œéî}°ßR~ªALßùH´<@HÊP¢Ç¯+¯Ì“J¦!ø¸)ðîR`•¥ &BæÁMó¢Å’&;Pe6±SV3Ò‚®>MV†)_ sõ % žóâ³ð·TѤ#ÛWfEp™È¤Àµ+ìñ)§qDu[çc2øÿÇá'Ž­ßDA.¾‰ë½Hn89üšO~»qn™ðøïœ÷˜¦^Šà˜˜…>›yK¿ €•@…Ü©”_eU-¬r" Ic5a:¦bµò6rTñÄ•æØEñ 0Ï0ÕfŠœÂ‰‹ZPl2×Ä*ÊäŠËÚ gù´pTÍ+_dQwAÇxˆamÿ˜îäCÐôË_þ²Š~:;K&9¶\  x¼1ÌŽO{„-×êÒQ¦Õ©;c+³^¦ ¬™Öùu£wñ—@A¬X‹½ßé÷G ¤ÌhžÏ·´Lþ÷ÿþßßïÇ~ìÇ”ù½¿÷÷o•ÃàK_úRíXÑ;èü™³ª;|FÅ?ô‡þiŸÂɽÞÓÉ6ÛÞYú]=Ô¯ÍÄZþÏÿù?»ì âG=—Ÿïuȧ[ùÚZ+ŠhQ2EΙ›çÀªËÚyu"¤ëÒ#¿‚ªçeN tÔîÏÎ=½->Þô°ø„ ÊákîELTfò÷¶ÒI5úP¨è©>1Ù‘ŽÑ#­yámzq V&a´³ªú¦µ;qSཤÀ& ZÈÇGÏ¿Ѹ6µ‹ø”aò5SØyZæ_ ¥ß¥Â_œÖ¶¤Ïx®N¡ ÚªíZ[–vKÞ¢C)¶s¹ùˆR*?R!{‘,^íãl)DÒöÉèÞÓŧcëfB,7pù§þÔŸJÌæº’Âô– ‡bwâ½§€›_˽¥¿˜sÀH­OPA’ @ˆ%4*r(Ý’À’TÀ E1f£må¹öl·\êF»ìœŽäZî]<0l+ª)°…Ð÷… õ²¸ȧ̩š?EZsN¿BðÝ2áÅǺo½—èø3"Y%~5Gö1%3¹°!÷'þÄŸ‚Zlà‡[ ˆÌÛ_®t;3u'‘¥ì9½ &eœj*–€W=è½ÂNÀØZ&™l[3¥½`Ú"`¯Èð×®˜»˜¼@°€¨þ¥ é<‘.";Æ àVÉcǦÖ$<.\y_áB…7UÀr%½ÚcÐ=[…o}܉ÓÂiÂB Õ“~—§Ó¦Þ¡Ìw[ dCO`èƒÐÌ3ÔÏÄÜöÙÊ›3ã@ mè§sùN§ñú8é3m׿z÷Q:ƒ/jLé$4¦ãTìe!zk™SÊà˜Å#Ã]…cˆ1>^K–K&êÍq…Ç2r²vâ2Òé/ý%Uto=§¾P4«%“ÃzÊ!œ±»SÃX¿á¾ASºšÀv<ÿã§~ê§êÆ:ѱô!UžWîR_ýôOÿôøÿAlu×5\ÆŽ‹%‹ª˜¸2Ü[/€•’£z—÷MÝ¿›ï% !‰$ sÁåB×ÇŠ™RÅtm—&h `‹•sm­ñÐx ¿¥ª£Â/ܪ$b!Þ·ähêØ*¾‹—ïž@hÏn’F¦ƒÙ”ŒÚ'¿ASËüS®!kq½øÀF’ÇmVéy¿ó6+:¦P'¨l[{§ð1ñïþÝ¿ë]¨ÇEáXLN9êf L|?íI_‘<›§d‰„Æ ×€T[1—µÃÂÞYXÏvH=êù(ªÙåÛ¾Ì}<¯w×fq·„@¼¡:ãññ×2Ò½¦º?þã?>·hå})s2'O¼Æ-‹&òš´Íé4§q«fÌøÐ³$ŒÍЦäyи)ð^Rà‘@™ÍmÇÇì™ñ[[52‘åÅ£ç_iÞhÓà3ß‚U°Ÿnƒz«Ð3-ËÐß§Ó¦Ÿö¤8ÖºÎÿ.¶¾H·iœ\ =[µ¦ÔmG„©52K¼4æwâ¦ÀûD⎘³èƒI-<(&|€xH;œ-"n°P KV— Eƒ~&ÝF@PG¾9k†n ZE>å?¬úð©@Õ°23×] km$K&Òr†ÛÖ[ºˆÁ¿G¡eÚòW; e[%ÑÔ\[DO_s-Cñ7Ð Ð]o]¤ë$ PÉcV"0û´J»“V½äV °‡útÕ—øC­«c+¿]ÆK‰”k³Pj \'Œl…ßõËÿ¯¿ò—!xLPßÃT\ý§›`Ï;øf§Ò޹A!d1¾xýðˆÙ·ZaŒ ý„vœ‚Z#9‚Û|ÑàÜÂ#ZÚµ0g¡ÎÝ-Á®Øêä)Ònì`ï—GåüÀlÕ)º²ã›9”_hwÔÍÔY|-Oá ±5[†•ç·ù9qŸÐÕqoPÑÓóÔ瘑7E3mLù5Ž¡Òoû¶o[§M 6™qÉnÕC¶·Ù‰{›bñq©¿øÅ/ºl¯v›£:vB&=zý—>]×Gßé›ï:.€ôÊfÁè¶H•Ó„ås{q[_ry2§Åp«Ê©x#@Þ¦>–ê}{Ä›^â?£×kFÌSì1ùÕGô»¾ë»rÂa]&€½ÚŽc§ÅÞ¹"Ï13˜ÃóNJÓUâ*Ø—¦ò¥›òPŠiç:áõ[€˜ ÄzúH ¢]Wïî£cë»›Û›·ðe-¤²øÊ³'Ç`€í•zæ¹w™›ï,ñ1:3‚@¦ W`R‡) N„[Lõ¯þ•Ì¡wŸ  2×À{î‚.2ý€fh¥2MÀr¡Ü~Ê‡šƒ³£/øT€ªG¤ßÏfxêîˆ#iílªÁ¢o¤)“„ÃU'žÙšápàÍÐ ”ÎÛ&òV@¢|n ì´d™`3êá[0»¸‰–ªŒ‡ ûŸ ª|}Ö>àEQVù<üö¯]"† †DBÊz÷M¿vÐÐ_ãÇÐÜœÆ;Bý Ó \| þ9 J"Žð¤jA›™ãRÕ°›7KõíôĘÐqìDJÿå,œ*tÔe?U ‰ŽP“sŸY¿Ñö¾|Ÿ(`¡¶ê@S#H"Þ­¡î.±PÐ"æØ<†,‰2æï¤t‘ Ƥ‰k3^UŠ?òŒÌЀTÄ„±àÍ#Àªx/ UäÂGÆüP¥G¯Ç2O;%ŠT¿â@°PÏ5_áÄV´6œ)•@P@TÅMmñœì–vcätuA§} 2°fAè5³4°8à;漚cÚ-U¶œ6þ=…8p}P¶bÂÅÖN—„‘1&kMßNeõÓº¯3óU „ÔØcÊGîSw畬鬂k椩s:1S›à Gpaüg¦äiâŸü“Ò'·²Jøñ~2â&`б)~>iój¶®µeÝx¬(§y©”ø?äœy,YW­ônÑfe[ÃJVKf²%|v¬.§Àt½æ2¦|&>.df¸ßáƒê †NfÝÔBí½¦Þ®AœByŸ&1¥bÄC`TéXóä þ«JlÀ’?Êòtxñ>}£±Kµ–ßÂh‰Tg¸˜ïÄM÷‰«4hš¸ôvb88¡}€Æ¿_3HH*Êk¥ƒ#Î?,ø™GAÖÂ[Úr»mÜÄ¡ ®–ºílO§l~уôÔGtkg»ôÐÄfhà‘Öo«ÒåñÝŰ©BáÙRxZ÷:³Pañ±Ö’iëú|>îj´¶ö3n™p¥Þ~Ï(0~ÔfD0 <°[¬i˜–„Hò ¢ª4M! @ÒäQVWÆ0‚-,ZA”d¿i4Ò˜,¥Ÿæ®†£o~yì?r=Õꌿ ÜÚ¤ó_¥8°09P 8ŽZ#§ÖšÐÉÙe PºÞ=¦Ûjâv‹|ðƒO÷€ÍšuÔê±)9 ÷DÅÃÒººµ€ÝiÙÓ"@Ÿ§³­|‘‰[þzÉó"ƒª–ý´IlY ¼[éW*ú`m„‹Ê¶á®ÂÌ#wÚÉé± ÎlÉ]›͈ûí"sæÉ£×üSÁÔµ¯w·t¶©ìTë-ìòRØÇê™–èÿñ?¾>¦;Ahžæ-ŽÅЉÊW¦*½G…mÐÓmhjÝã7í$hQYuzϺxÊHØ®Sgü=оíÀÆÝbaÇégZùÜÊ=¿︘«œ4z5R~¤žÍ@TӇ̤òù²Ë4B²C áòÿïÿ­Aw±Á $2\e~ëoý­ó‰qÈÏ~ö³.£¡ Ë¢¡rË„§ä½3ß ˜¶|S``@ÀHHÒPx𲀄b¦Làsü)!…y=J@e¬^y™2A j$;…ަ…åK „Çãoü™Å ×V½KPM°- ´ ®S>gÍÏ8Ì—\C\q;ÏŒŸ”Nã§ °V±ÕÞè´° kÀðªâ¯ºS¸•ÏN{Ú Lð;3C€üI£NÇ‚ý5ë-rª'ÌÙÚëèç:Na#dà¥Î^†O™w"ñBëô˜ï—Nå‘!n#qóA8µ5ŸóÏhL¶‰G¢ × í?yœ½AÜ4Ûd¹õY“A´lfNŽUtš1޶}‰p³ãn­5é 5®e™ücBIeL¶¼Å¸ÅŽÏúÆ•á19%„Ñ“ïMM¹<‘9²ÂJúökmJì)·B9ŸûÜçŽÓ£øÎlýÔ0ùcÈîaç#Ö‡ÊH‰=¨Zµ–¯œƒºE7nÇ3ýE¹­ äŠù=ïz¾¾Ú¾)ðÊ)0Ò ÅÐ[Õ–˜OË¿)ñáløµ?|u¦ø´ ­t–Þ‹CM‘ÂÖ‘”AB´møcª\‹IS¬­öDûÄ^Ž,e+¿^n¤ü3ýžñ]ÙÒ: ÁA[•îkÉ'߉ ”h³Ñzmç™´ÏN‚Y5b½´Ú×%ÚI˜Lا÷ ZöÅ0T ¨sË„ÏPû.ó.R `M‡À@ÀH2¦bžóÀ@ ÌH Â7ÖOö ¢€+Ý*¨o± L¢¨ÓÎ#àgmA3“ô*ÈÔ¬ÜÊäE¥Ì#Ý\Ιú¼Va¢A>-Ui " è–¹YÀÈ­ëeG@+o)¹.Ù®ÈM¼ä¨Òy€ q§ñì/ò@•Ǽé5ž–;bìŸâ@¸!&øÊsK¢øÈ"²–”&¤dþÕ”æÉ½ë[;_ßËW'ZÒ²Ô£i–åÙnû"¥²YÙ£2%ÙÖ[üÌó£Ò¥­Ã¿ëwý®)ÿ(A{ÝB«W«ªãQyKI¡e¦€QÕÚl—ê*”VÀôØÊOŦSÂdz}§£nÖKQk­÷¦÷êÉ0Åò>]{èVœËsË4dÇ 5nýìÏþlÙZp«ß˜Ñ9`Œoú¯ßüàX–÷Ú¨aâ˜ÅFÏïÔ·Ëë€Ã÷´Àâïý:hd“ðÖ‡»¥:2Û²Uy3PxêN›wâ¦À{@‘Íý\lœ(„Œ·ã¥è†f"@Í‹°‘’C›ý°m-†DôÙñìî: ¸Ö^™ %È&þ©óD¯;¶zzG¿‰#úbÝGt&zêÞv(ŋﵵ‰PÅ0@:Üî>y9±zAMq •ã˜t.3V7ãÝ5Œa#Ó_éɇÞÅn ¼+è æÂ €6ñx!‰˜˜ÂÙÒ§îºÅº° JÂdÞžŒZ³·-ç5Pà‘ŸÀ¹QÌãÚ䢸´Ýí²h…X7v,ª©›‡—L˜ž} שŸ' ØÜï­¯åÔ³‚£×kDO<—Ê › Œs®BWþ«›Édú¿&ZÝtA0±õÖiÔWìßî úv„…Õ·ð‘=vkÄe¡[<ˆ ó©Çf?ÍœW$RHOÄÔtŒ1-1ÞébUk‘ëþóþÏ;éŽRäT…`hŸJfÐÒñäþŽõS2÷§™äÇ“£Á†…=¦ÐÌx®kÊ囄§äW€%Mæ$~â'~"ôFaŸãûµÏ4ûµÞúå0í¬ S€ëüdæÈ®³-ç‘@èìÄ6"káTœæ8‘K^°Æ†™gIô"90àƒI9A‘®‹W[4¤ËÆ;ê..E «)[´±°ØŸcLå8Nãƒ÷ÿÌglžþêW¿ZÚß0e—TkîôM÷€«4(ÝnÑ©Á¹­HÈtèèaf®ÿößþ›†MÞ¦‰` Ÿ$κi"ü‚µn >ŸVçLg=¿èœ¹5»^‚8ëã]^ãžµú£´.éX²tެÞåQ “\ˆVSo´¥PEŸi3 ²ZÌÂW°Š¾)ðšÚ>> þŠš1`¶!4}»7Þ d›j.¬T(Øž[ÈÕ©è €Q$˜¬gàÇèÙÁåA”|¦€g!d¬Á¼IØ ð?§”–Ÿpàñ>]K&Òx†­·¤WÐe;^Ã7\Ä)Ue6©•£ìöˆ¹,P…/0uîN'D= íÔËoÅÆ”V.Ã¥høÌ‘ñ X®ÃÐõéS¦3Æùp“/Of;%2äd^"P<Ó“šj±¥£,uÉÏ3àv}âëL¿й+¹6¡ ÏÖN}ûŒ$ƒc³ä^2#¯‰!Jo˪îtëj´·ä?þÇÿxÚæÔèBÜ þë]œîØÚ´ ÇÇÅh¶SÂû¡ú¡D;hM˜†Ú#7NåMã¼<½-ÂÇgÉ1F˜Ø-¡ûáþá)Ÿ3÷æŸ`ž¤Â)þDØT)Q”*íå+_ÙnÍåxxÚ³»žM_³îÓX·ó›¿ù›µÖDE„ßó{~K?z8·Ø–¾}&wñ>ü(§Ó‹ÿšB«ôQQ4}»7ÞE lPžRŽÁyš_æB>-ŸšÂM±dL|Ų¼aAãçQÌd„*ÖyzJŸ‹Mƒ§å™›(¥ ƒÞ(|ËÖ2tµÆýþ雸r¹u ¿¶F®/u/AZ‡Cu膫?³7rkù¶ú(>MÜÒ}qKžð¹×2J´¦X r‘UÆçn»«H®ûHA&4T7l¶´uï¾¼)ðNS`N•0Ö­Ô þ“Èä NH-À `#ÓŸL A°Ä%ˆ"]4A“Ñ–¹GažN(/àçA&ý&JüZ25Œ„‡oÆ«$R°mª€sÁàÑ f »Q0Š:F^È„I¼à("gÑb¬Î£K`>ZKq¿ÝšKÄy•„!q}rÆTœ£à{/ÛàOÃþ«äkÙGœê[‚˜þňI׳wi+|¼$¤à¢êÖrüé¡D›#°?¶ðr^…@h=äAå°Êo¶Û!(±êIbµ 4ßbk¯ëkù>…ñþàÛ7æØ“¢ô¾Ñ!˜NÔó ÕÑOúø¸oû¶oS~;Ô…¶^¦ß˜%‹&·:­ég¶»¦Ç©c@… ÍÒ »ÌÁPÞŠÆÝÖIΞÞÑ…&”Ò<½DÞ›ˆ¯ýÎ<Ü tów‹ ›^ c!hò2¬^Edíõ¡ÝØBEùy"öõ¥ífÆ­2çA*³$t=0Nû|gÞxµ8‚ø¸«Q}N3b‹Ú÷|Ï÷üú<ø%½d~Ç@ÚøÇˆ“ Ö¤–m­ w:wžÙ4ø$õÀ2Äj7}¬qÍòù²û ­_BÆÏwVŠ€o)ùüç?/Ÿj<<¤/{N/Rà.pSà €¹³î"1øƒ ÀP™€8!1èØö?ŠœÙþ Å»ƒ+™0ÀÌJPg`Ïš¿¦“ˆ‚Oói äR®£€±ÕÅTÓÛ-–!P—Læi§7½Ðµ™¨&cÓIô-'c§Ç=¦Xn'ç4Á»¡u'xy* +¶é,wK?/WÀ ¢{ÿ±É10ˆ ?y¢rʯ‰Nö&°L&œœçpV;í”ym‰¯³@èd˜|WÌ´v#PΖîÏý¹-ÿÑe^¦Zó9­—ŠMþ£Qb7pSW÷Ƨˆ=šIÍ캰¼MËISzB;²)r¦ÌšÈ7Á‹ÖixµÚ) MŒi­[z=f4£ÙµZ¢½šÅõR«w &k•Š«fù-p sf†žl”º§ÏÍÕG¦W®Õêúamë‹xëf£[¤è¶>‹yè(4]¹mh\o‹ú §bòͺäpuU¡›_1’2ûâ®Ý¸Ó7Þ- á;d“BÚZë]R$™òNyJ[`ô¢S9+?ÛÏé‡Ý"-ÌÜ< ßhÓà´ü(±ZÛÆ˜ù¬m«(Þujc”[Ã>š)®…Õè^¼E‡ßŠUóÑ–ÂS“ oç3môô³ ûî[Ÿãhœ0·ar 7­ÜX"µaSSÇAµµ_ÞxG)ÐÙMØ æ²ü ɨ- lSÉ`ø!- KB#gZª« sXI”<éÖ£Ýe²À'+6€°F:|U°ÜGñ. ¡»Yà´ë{¸ÁÚ¥-MO€«žEcxªÖ®ÝSë3à:o \–Ûãæòk_ûZQµ¼õ&ÓN™5„{)«Ø¸‰âöI {á|Öò¶Ÿµ˜4ÁaŒ Šíî£KBŠž1#Ô$]»«{¯üÔ믧@š$t¡?à‘»VŠÓ:»û¤™>åZšGíȆr‡z®‘½y«æúb”3¶A`¹îóÐÓDÁòÔlïø,ô y-C±wÍ”æ¢Âã¶îš¸ØVl½¶e^ú æ @fVË8ÎZ+¦!§½«.‡y–^,UP < IÌ÷=YtÝÁXÇVËO;ãÒYÈzn~Þ Ï,]"]ЭwÁ¸2 ¶û\öÇ ¤Æ}šï®4¹Ó7Þ9 œ÷fÉ ^É•o”™ŸL¥ÊXÄP$—™ßÔ)°h àäLSÉDc¡2O;ç׌{£Mƒ§ b·DSÚ'¦ên¶Š[OŠ…›(É“¯OwK#ØîyÒG}Óíܺ¼ˆ×ÉÍa}â“ißn¶"2RM‚§ëæ´¯‡Y†}\ŸXOV[,²“xwü0sbË–qR §Ck¿7ÞQ ðÓC€¡J¦I` ÓŒ¡¹TBà°¤ÓíÇÅÀ< iùxrdTEØä·¸™¯éãžš×®iŸµº>tÌÓeZ9 u;^]ŒA9k ·GlmÿNßxç(P̼慿À†N’H* 0#Ðð0: ,©)@eˆ-N³«{WwçüCí€CS¥„i¨øä²¨ïDÓ‰) n¹;Î\æf*oð HËw*j­Mqª~ÛC×Ëœ<H™ÀdáâÁË¢§NÉ¢÷OÇ&dM¨bAÙ¶ù·Sà:Q`O¡ñÏ‹ÂíÈeÐ!Æ ÌAÁ{ ß[<©›+ô’'\$rŸUŽ7Õ>.}ai æ(ãÕüˆ?ëêö¨ÙO?ÿë 2 ·O ]¨¨_4åå}1¤¬µ!Ý2Ù,¢}’šYÞ)uø"×Ï™Ÿù™Ÿ9­~1 •·U7Žo4[¹ý.4Óë#ŠhlIn†SDmGÉWXØt£¤Ÿºæyã Ú›Ì^ƒ#Ï­½ˆ¦fwâVÀ¥Ðy pëØQ¹Z _üâ•´ã%ð±õ‡`¶éç2Y·w4ÍÖ‡¢^^L”XÛݸmÒ /¿ô¥/队§¤‹5ËáuP·iÖÃ7ƒ{>¬ñ£9ráµwú¦À»BG`}ÂÉà‡Y„H´¼Mrò´`çüƒSmá£^$ÂþèÖTÍZƒ/v^·fu´‰±Uüâ`‰Å0ºØ\ƒté‘èuO^ô }±kûÖ¦G‡R¬ÅNÓx ç¥Õ$è¥ÙOË_gú¸>±|î . ´óǰ™¥yÝ›ôh˜]?ñ¾{Sà5S K`¼«¿A°¡HK€Ä81ÞØÕ Ë?(R„‚Àɪh6y‹@Òl\2]À¡#FÒO‰tí<®:ÊÜrô‚d=HÕÜÕí¹»%f§ßÚÂVhÔ9ù©ñŠÕrŸ8:Å&¸æºåÃ` 6%@eLÉ8k§7êMÝkÛ ßÒöབྷõ§úu"¯ ¢„4O:í\(õ2çø×ÍvòÊÑœ–(t]þÓ¿ûi „ÔÕù[£ˆ“@-®/¾sQ}O5ê2…J(o8êÈI²2“_l¹µí…KEdæ¯bÏÖÈ©º2Vͼn´“…=#u{^·vŽ—ÁÑ$…€ÓظjÙX¢˜GÔBÒ¯œÕ¤æVƽS#§»i’Ôò»8T4›Sõ6|{4êÙ…ÜüÇéú ´)Z>z„¶ÁOá¼/6g÷f¸Š›f(ø#NÄ-‚’mÄG‚’r~ú§:²çÒ-g ’Ò~kà¯hxÿ½)ð.Rà¦Ã÷†:-o‹™‰O„]>œøäHXœrMÇBíiyD™uÓ åðz‡á£FÊ_,¡™ !랊dj­qDÿâ_ü‹OjâŽí«¨úуôô¹/.a:æ¥bì9ÁŸ¸åœîDäyJ¡OÜZi`@] €Æ^ÝÆx㤱ÁSníÕÅ`[‹Ýé›ï€Æ* p¢[Füd6!Þ4ñ)p¨¬ïÆTwî)à@ PP´Ö’Χ4“ö’©0 õgÿìŸÕ&èµVÌÚóTKG¿˜4°W—ü½‡›™q žóèÐò£3fÁ×vÅ÷f—ãôä4á¤1Cvwk‘¤Tc>ÇòYk/voñÙ tµÓÚulꘓ" 9ÖòÔ³0ÏÓ­Uª‡] ÌvlmÍñA >}/ø¦ËîÚÔ'‘þôBëbRJ´xÒ1×;‹ö© -Èöþ¬À÷é.=Ê(mó=žÿ¾µÐe»„땯ሠ`ˆ³“µF¨Ž¸}k„Bhbõ¶SÿEgèZh§œ¾ñ¿ñÑ4«dÞ˜¶&èIÖ@ •)†Ä©õ/GsJÇsqÆ}¡’`¥¹ZËó7m“9–#;]טËM!µ¶OVo¡Rr¸‚tÕ`öÆÔB6vÏS$z_ ŠqìåC3œ2»÷Yà *–:Û‚cIîö;zá®ÏºÓ7Þ \t“¢©Ýñ3Ή‚-FÕ꤉V`^³ûbY=RãM#nf™ÈcÌg³­G›kôŒÁsN¾ëoz°Ä&žJh#zìüó9«)U75ù„yrãú¬ “ãemg8šIg— Có Úæ°>åÅ´Ž 0ê3¥Â—˜p³@løébȽøÜ»ÀMWH` TÐß ÀCR8‘N9õ*}4œCçòóœ×, ]œvµÈ&ÝD„RÒm{¦²øeÞÐX•_ eJêɦ1Wl Ë„.~Á$é‰!?% Çpj°5NµŸ€èÔ:Mxe…ý¼þ3ÂX6ƒuë£hâ7Ž·Æb‰¶¨qúÜ-3OZÝõcq[í’à ðñ„m‚FvQ¢Ç&Œ(OýŠsnM=ºìä'¢P¸—pôF«ð£f?~þ§!R-„Ë •H Èwq4çñ­hšU- 癬É6ëošƒê¦ä°öØÔšÃ£·iÖô^¿ñZlKÏi˜å›ZAVéÑ(tKÈZÝÖù­…íÒ¢›)Þô¢Ô¬ŸÁô¨pL'“ÚàêR{„Tx(($¥ZÊȉIQ¹¹ÌnÀè€àèõ™ÏŒZã÷ß›ïFÜ Œ%“òòþÃ8.Ë Ûà÷}ß÷ù­d1:—MÛÛVó")Þè¤Ákƒáj1û»÷ï¾øèc5ލõEÔ;RÊ“ü£¿=b³@F:d|>éÖ`—^6qŽì âl»7“ài 29vfòµ¹è4®Ý£ŠåûèílŒ\RãAhŠ ŒÛ[ó’2¨ ­º]mª¹eÂk:ßw_?BJ3ª0!À ¯Z(`ŽÌ€OJEÊNâàʼ2£H“Ÿ§©Ò¼¨-#^&®H#ð6«ˆ5š©Ít?O¼S‰Q1`O—ÚêÖ¾!ÝÞüË2x>BJg2ŒvàM¶„еN¸«vi+Ùe8;|ŽB³(־ȸ¹Mµ ŠŸ¶³eö ÷þ£–GçÔ½Ž¸QØ3Ÿ•Òƒòn{篽"þèQˆ@”Ƕ}ˆµü§–þdBSâÿá?ìÍýxÙfiµ‡éŸ9/B$YñÀäÆÝ×Ò m¯+R*ÏoŅwˤg­ô1É'T¿åçVÒ¦I1ŒS%§ó£z ‡†þ©{L× Ö¿ö×þZ­Ôt,,õŸp® (Ðéˆ?*Д¹:Ù)“Ý+1V8t~td_z,Ý8Fò]ŸNÇ“B:•Xígd`a§Ïœ ‹p™‘p:#ÈA’älüK_{59X3Ë~ÀWq4•î§©iùNÜx)`çáoN,ŽQ¦Óõ¯övðZop9³ÞbLü˜[%è‰WÍË‹ÔÀ—Iøðµ£ÎÚÔÑ`ÈÈÖ‘ ºñ䞺µÁÒÐR ³FØ?‹€²‰pÁ±³ÆOÛÈ2´z¢GopíØÃ‹Î`ÅèëCø‹°&ÁÓ¦|‚¶$¨{½dœV÷éó©‘–îü.ËDÄéa $5Ø:[ŽA8ùô)wæM×O0Æ:Ô<€@…AÞ]Ž ½Îì$™¤íˆšZæ•óxlš_ ~بՎ—Ø,©f^m†5h%¯®¾Wó,‰zÌËz¹Þ•ö¼HR®Ë$å×pbž>MuG'ÿ˜HÖkÛzî‹MNòèZùË_îh NÆ>•Ù¦µIöêùµöŸú¶Lùv?"&gM: Ù¨M‘$>LHY‹]¤ >êêFÔ#µQ&â¿Ñ¢|ñ”vëÅùi“.hBŸö/‹³èkæEšF±åE쥢`—»(o±4þøÀÌÆÂµ°áÅ6ݧ|âÖl1NÖ*kºIÅ=:1RgN6UñiuO9ÊŠîrƒ. ž~:ÓO‚(ÿÝßýÝëãÖ4eÉø@¿G†»Êo6:á¡zßUI“¿ÖÑ”O%¬}å[øOÑÆ¸ž ø¹ö°´PËZÀæt8]äמì ä,ÌWþ âAa£”åj)•Á:S•É,¼Uz¬©„UŒÁ]¿ ÎXU—‰…½²œ×ë¡÷ß›¯–«4(–q¶ÿýÿñ?v0ɦ@ùé\Ö|û46w†‹·Æ!O7 ^T9ÞÊ`Bª'úÿ/ÿå¿<åÇê“£ÙB݋̭§âœÌ­Øéå‹u=.§¯ ³»ï´µc¦—Åå,:«¸…®ÞPÇZr´öq¶±Ôçh±Ö1è„оŽ2§˜ŠX=iÏvñin™ðÑ×¹óß dXÛÐÂh–S»ÇlMŠø!IQš2@ èÒ»§s1åÁ›‚^<`’fýÐ4JG,LɇÇó*”ŸøêGWOwÀÀ¶ÞåØóèkUå(?D]× ¯1E‚£:ü范µü'”þDBË*Óyêd|!ÁÙim0\•,:ùìà±9'Gçø˜»%xT¶êhÄã}M?ðâW~åWÖF¢ÞýØé˜ô¿þ×ÿÚÝœ7ð‹Jæ,rrXªÍ™m›VŌņš.ž¥V H¯Fl–î÷â _»w§o ¼6 lÒ`ZF^|«Ž¶PR¼cެúͲ_Ÿÿ÷ÿÅÑóÇ·fËžïY׊ÕcÝ5H²$71½a ö`Ï„¡ó¦Tà­¾4ñ×qD_íÖ¾I?_z[cêÒú ¶fç²]‚mò%|zz.;'††8ƒ#§Ö3 %gNŸisݱº·ž˜Ûh Ø1X€>pVýÄ{[ƒ’o™ðE:ß^3²ü_þøaª“œ%SЧ¹v r¦@bƒ°8Q¤ãoã8šÆ?ÑÈ1Åà°GIé•&R`dêY¸âZ 4¸¥®þyÜ.ª¹¶¥¯WfÜ×ê€ßÚ2X¨–Ž%@fFÿÕµä¤C†1 ´Åsw¹TŒ©Ð{ƒ¾Gç¾"ÌSý˜@ääê˜ÕÅqkÝ¢Ž”5gZŽ=ÏÞC|X[8MCò†E=âÉi™ÓÌ`üf$SâÆÞΜO«¢™o_ ä½ÓÉ~†#€rª 0ú?6‘ýÅ×C—ÙÏÆeå‘þ`k'l4‘Óºû_ÿëmï„9¼ª–í4õQõç´·[Ë$@W^g^\€¿é›¾II§ ¬Àv™: îU¸MŠËص–— S›Ù31Ž­ÌzÙìûȦõpY HwÖʪFâÝŽÍépþ1¦ÍkN¶#4ÙÑÀ¨å¾Wãže|Û/k„x‡øpÌø…ç©Os£ž6çÓCûèÉx…Ûrn¬2+W ªV‘'Ãèó<(­3ªÜ¿›ï"6iÐ+¤sY—vn?F»Ÿ5•hÑbl£×ÑŸúˆŸ¸å<ùåoÕ/.­2«éº}È+x‘Í$ø(p¨vÚä‰P6 \tíuKá)·¯ºísûè™#©ÌJÏ_C(¥ƒ&©óÓ`²äI%4Ž’pðéà¼îê}÷¦Àk£@˜d‰ €!Ø9Ìð£-ÜI*3%2Ђë‚1æ²Z€Í¼oööÀÏ“K]µ€¨©5‰äRÐ S[õtMX°­ò€œ2ž5Ûá2~Óà$².z»dÚU|š2“HdDÁQ ÔSÔ6@N‰°":¬™–¤¸" Ï­:ð̾ž =‘[>òþ#tð¡ EêI»»1äéØu"E_¨2*³GµˆÑ)I›0E¤º(ùÖo½5ý»¾ë»¼ª±BpÚé>ÿñPε°o™Ë%ã›Ñv˜Š×®½Sü­'&CF?ú’¾™MÀ^0%KŒŸñ–?—t½³±µ]§ÏliÍEej‡²$åõÒéÐi6®ÊJ‚HÊŒôÊ¢$y5*áéÛ1Á¯éƒ/ñáo3ñMaÑêÝÏoËK;…ÍMM 4á3‘û¾°T–‡Ó™“7¬ÆGÚœ6%¨œëw©õ[tŠ(¥W›ÍÌâÿš<õMÀƒâMáÔÙK“!ÛŸÝL†«ŠúÓ#’Þqêµwú¦À»BSÀmÖë0´Ø¬m„ñ Ë9&i½YŽ}Æ·‰à/e¶·V×<-MmwÍ/[¬U5¸n„*Yí^¤áÅA []œÝ,D¥KÞ‚ZL<ª´'}D·—›ø‡ÇŠ=ƒE4޵^ÌÙ½çbŒ«÷ÏêãúÄ1Ò:fƒ!‘o[ùýÕšx É×±$%úâÆ^™¸køužr:Duòο)ð:)óYõe€Djšì yÎ9ÀÄE€Ä 2e̬Œ@KPŒfÜlæ•͵‰†Mþ$’ÙÔ¢&sà–[¶þbViäA²‘Ä6P­¤V}ð\v¾<à7 ®‰ ŒÚ÷3Ç×[[:„Âub¹Ý᢫E+ŽElu]‚¾ÙWqà±&>LÈÃc­É àeÂÑU¦¹‹*:ð~ªO·($!V–Oa!Y}EàSþ˜ ŒI0IâIÛã ,Ä–cÉÉ!òèó…ή’8v±jFâÕ´ð‰&Þš@˜c4ü=€×ýv’¦WEÄÓb>°™æ’6¢_§ÓYtÚHJMJÙ´2¾º‘ôhèˆu«?3jkÐ÷¨«£!›âçQáµnq~ ’)ÝzL”2×b“λÙ+ÓN¡6OþŽßñ;fŒæœ@—<µŽ‰4¸ÞèâĬm¼ÄÒM—#zÒº¥ƒ%è¹µ¯ÃÍ@‘FgŸc”OÓþô‰a^s2ׄ€:f ž¾æg[Ê]oGùÔ®qj}S8‰4³¾¨\xÂ4^ÿþßÿ{ ?;&ÞeŸ“kgîôM¯;Aí ˆccô£Q‹0ÚÙs2Ç‘L,*#–(³yõà„¶µPúŒKuL’ܘN;yb4:¬V¯'–8 ›¿Ð“>¢/vÐá15d‘py~^lSÝ[´è¶•âM‡jg=”âT×öbg|²üJ…oÐ[û¬QÒ_ŸÛG÷é×¥ªKð®XR¥v '9V ϵ>Öj)ô¥r–k#Ó£úboï7^ òçœi"a´çx) `äQr5=èa'*@ èÒ Ì0!®ïöÄæöõ®tªv ʬÜn[:žÃ2OfáIPÍÝõP ó´¾x`žÍÏ 4·–» q)sͱ{;@´Z i«°:g±ÚhO¤Ã½&H +Óž‚Ò§U&3)Ù%Gz$2Ux;ÊbšÂ ±Gô™à0®Ù6ú„” §ºDxY¹ëÚHr#ñgÍ|”6&ÃÒŸšð­ „ ¾ ^oˈL”¼¹ËÄœplYrœÃ(nS œÊúSwMdàJn‹ü?ý§ÿt½»¥YÞ »&¶|—¾t²¥iP˜çÅ, 7t!’âuê ÕsÛ›ÑÆ¼_ú¥_J‡Í™xµ‹jàŒŸÇ {5àè;åÿê_ý«^™©ežÂik0–gpëpp‡häÒlt?÷s?7 J˜–ú´ s¬·JûÜ£Ã^ïÚK£Š¹”­ãGôGÝÍ(ßGô\°CšÛFØô‰§ WqwJ×”õñßÚÕ;}SàS ÀÈî¼&?È!¡|#ÍQ©±¹/®š‘ké‚f—zØzÖújîøñZÁè;/øÕ)ÌeA)›æ–y»>N‹=ÊÔóVñºá/ÎÉæfá£UöQSkþ'd!Ô%[M‚uÛ+¬¤kO®Óë~KdDÌëòÛ]ëG~äGÒ Ù=|\ŸxUáo3Mc«•©¤áé¦(„c 6ß­åcŽº½®ÛƒîË›¯@Bà;ØÐ,8`´äÈoi»Š‘œŒÃÐâí-h*ŽºÍâÌŒ8äýª 8õtPj“sÀ-·t&z.H&<3ñ³†aÚ+yºl‰–ƒ †YÃÖ2“{4 FNþšÐ 2 ¬Ü@-tÈ ¸*¹,LkÝ5­ÃI Ø;x\ˆgxf§@¸Ö¬kq$},¥ëSÚ½:Ö®wK j¤Hp8–9Íɯ`Ò]K ¥…³ùÊ*ãs#Áç´µÓÌ„ü-$éiÉ·’ùÖBšH¯Údx²gÅ,Ùì×ÎZÉ)Ù’¶Åm2œÊl§OlG¯^9ùqô§%eú–MïmDroQ´é‚Š¥ê¦ÞáÃmí›3mDÖ ?CmE[a—Ñ„eßa,-ê6ô¯Î?UÉP¶z`OSt!Iƒy!±q L¢O–²‡·ÃƳŸÒŠLõõ‰yP4ù§ÍD¸Dß!×Ü5cS g¶É—øùŸÿy™ñâÙô<;p~ò'RöÊdÌÄRåD¢!Üâ¾®D\\t§o ¼~ \Àk ¤µ“?R¾:4”9hÄÁLL\4®eRøn9K‹ÖÑÁ0U§Äµró9eбîäpšÈ™ù/ÿ剹{H¹ã#J“MËÎ6R †`\w^7ØÝM¡r}”ÿL›ÊœîÔaÝîCx¯ã¥žlpŠ!ÒÅEs¼ª¦À1áÓø@>SN(}¸þǘ~Çô³á4uò½ ÝyUØ(âÒK‹Â¬hƒöø¸;ç¦Àk£¨`ðšàD;Í\32÷%ÑEŠ’ÀT^<ª÷cä4YÀ›õeÇ“:Š1€“ŠMÿ Qƒ£náðÓH–ÙH{ä ÚåÎØü¦ú–ȬtD‡SÚ'êC¦Ñµ:˜Zô)¨ |­3E¸Y‹­i¬#³ý@åa&k±5 r[¤Àï±^ð\Md°ä¡ÏZ˜×,†Õ¯ùÇ4¡â•/®á±Ì1'is3Š[Â¥zµÑ¢~^àôÄõÅ';öêãä¼50£¤òâîÚ:‰ú¡ÉqJJª‘£^ÙG-*Ìñ‡iaÜ©›ØVµë ²S¥Ñ¼g©‚B'ø‚ÕnJJd"?Uð¬ÅP£éêM_ôV±=‡´ =÷ü‘?rjÝÊ]çd Ðé­ÍFeza‚O÷  …Ó© ë÷~ï÷º›k¨þlL-ïªzÉÓ Mn&p:¤6aoÁff£0÷ôà‹ž~û·{$µGüj»HMá-€>¶Eê5b ¶ŽÁÕŸcÖnðoœÝ5TÚ«¨É~ÜaøŒÁð‘ï™2k?Qf3 ¢ð¶KPçÓÊ£›—òjk O¦0 IöT´ö!|%¶ßgòÉ|8ãD`˜ø¡{Ô*»á½´°Z‰‡`S~"ˆ×Ø×CwZ¸7^!Æ>3ŒÿÒ`F˜ðÀ‘®ä›û½  R•03Ìs¢U2Õv\(Ù¦»×Õ#ÀªòýÍ‘M³“#˜ƒj`Ûz·4€7^T~|à¢GƒŽ‰s¸ H¹µv*ÓŽôí–YÝÕŸ¼üÖƒ¯·Âs‰ƒ©â·j—æî–ÈÈ~¯ùÔaaKÏÚçVzRÀ~r.Ä„XŸ·&>\”ì1DŸ‰$Gi“ð’£qfxT+~Ž3¿Øx¬5aéÌ¡OÖú8ÅÞš@¨ùUfÌy¦OŽ€LûhX®(2?Ÿù ;쩤Í\‰VÄÈžÛ´Éœ øE±­F’7Æ›ƒM«¬Ž¸ÿüç?¯«×[`é\뉒WæØá5§SPz.•Ïq¨U8ÏRۗ׺Ψ©"Ïuù…fÉût-Vš¢¥@sô¶¹h 'ê¼y²œ¡LÑ G8Çòœ._É´b›·CŒ•‡[³bË9¨þ ¿ð u P¥=–÷où–oÑ¿ŽÊˆge¨l+𜩥Lš…çÝÁç­ïÄM¯#^„Ô©TÓME(AñÏ&¾¿´›Í…rLÉm¢_Æ·I¬«é”Ì¡”ÔÑœúp ~pVÉ^”lut4s_A§q ¯ðLQöø¤ÁðMżgÊŸšuéTTó^Ü™B(æ7ß“•ÒȈ˜>¢ÆùÁ‘ñcà}ŸÆ::…ú ÁJŸxõïxôÄœAòjé¹µo8fSË,¿U¿»/àiäNÜxmÄ®Œ.PSOºÝfXÒ[*Iæl9ÀLÞ ›Ëž¬" ”Ã?h´Òprˆ¥T7ËFžL½ÖòÒJöhOBeÀ¼ *øÆ#×¶ü~4.§MS.&SÕ“fâ¼À=ZÏ©¦µ-Ñi«öÅÛfà8ˆ7ÇN…g8£ý¯D‘³5>—){”9Ýaœå³X©< â˜IȪø­ý/ RЮ¥óXƒ¬Úi 6zºP18féOr¦øTµœU$†ëq«knSZæ)ÀïÄMWEgÀôÈÌÅy‹Ëí¦OÓa¼_6°~|YBKŠjUVkÿ±¤:o{c¬”³aÆCqZ˜©éo›ÊõfÅ­YœvõÅl©׊VVÑRHéuþºÃðÑnmmMŸÖ¥¿ßL‚­«€´6²¦½Ž—JïI{ýÑkãÁ‡~ñëx©¬ ±Pî70·l¡[<£ÖW–~foUîË›¯C£} „Ëq׬å@H¡2Á’éy^ Ëœ½ÄoÖSÔÍBàPøpŽZ®š gšøíý©"è5OœV-°m2·DV;ÅÀ¿S]BÎìÙ)¦`¹žîÛÅùú?K@»õáô2-g¤›É«ê^[b@ý>oq)PÍzªÄéÓËTeôœà%J\lºÎâB$¹hÓgmðp¬ÅÚˆ{Qkn¥'ÍÀ3™Ÿhâm „ù[ºžï±íïhÔ4£/ ß\TϦÏSåT)ËÛî»DÚÀ–#â“&cá1u†>•ªUÂ$|d^ÓÃSËõôܼM.å#Nk«µÕåfŠm žåfc»µ]_gÐ2& Z¤§d!aÒfM¦„y’(|$Ú¡”Hf¿òZ}ÒmÃeg§ù¯ÄGøÄ{ß•g±w7+~è‡~Hõ>·nÔ–«ƒZá,V+g"býøÿ¸K¿±ž*mÚ' V&xzjÍŸþ߉›¯Š`tÂDnMŽ"‡­p(Ä ÕÿQK½ˆ†86‚žòÉyk¾:ñ L` [5%,ù©2TÖ%ùíhs;ê÷Q#>¢ª¬ùGƒ!ÉÄr;t˜½‚k­gÒ›X8þcûä…IðQãǃ1‘4‡¨¡3²gžõ!=tÍ÷qÂ>·ÖÖ[ÇtÑeÚ㉅ƒ—Pwœ5ªÕahnk­LƒÖÐ5€, iûø”;ç¦À+¤€¹Ÿ-ÑŒ Z3äõ¼}Rr&ô=¸R&GP¦ GãU~dâ'."ii€ÌžØöÂ4û  e* zÕÎö·È`[KÃv×eL à›’y]­aiÒû˜û@fÅl&’ÊÔÝIwV¢-ZÄV¬K'šz/P¹Àój0X«¼¸W‹›hߌÏ9°_[x”nËXÆXâCÑz¬¡§žVŸÈøâŽ$BM>qáÞ!ã£n¬ùáêtkþ'—~›¡^fR?ºž¾íéw|Çw~T¼:εzFD^3¥é-`~ÿïÿý³n%@ú¨[áG—kÑþâÇG¬µ,±Æ:X¶ù+c o“«!%±¡Á£umMÚ:ý!~]u=ˆ;õT ©¡&ÁÚf3YÔ£5Ó8FjO¡jgó©¼w¶?DÉGöÕlzB7Eéœâ/¢*«5nîÕÔÊi–ÆÅ]»–§Wí<ìš]qœÔêÉäˆ\ MUÑn]íø™ó¾xéíï“ó¬;qSàuRhnšlcø°rÉ@6@Ï<ÎSò@;C’ëL" ËTQÚ¹°øñ`i·º¦>òIƒc¹Â Ø bõŸT¶:”n4÷"³A€Àð¢Ùj«¾]’@Ðgø•EHÅGμU|t‰ªL‚èSê,ÿì{éÕ½È÷šcÝõúˆð¨0Fzê:DÞ”h^jN)ôj[ ==“‘ЋG£q¥€Õ$Ÿ7¼ú»¿û» ÔQÒU¾¿ö->úÖwþk£Àuë–30¤'XHL8‰ÛärtI_“¬» Ü’*‹¦F+׿ÃÎå2óç´¸}zúj“\©—KÌð4Œb#à——(h ¯-€‹ª¯vwK™^ÔthA׊[:ãJ DÝ•Pfï 0GR9GÅ""{wkÖj{n—$¨‰+q ~sZEfƤðKbÅê‹WuÒ÷šBÚ,ŸhêVõžñ‚Q‘¥<‘êºñ·{÷- „Üc¼sí‹½ä »¶¯«ÊÊåºn[ÑVá‡Kná¡´c@oC¼áõŒMVÃ"·Ú÷õ'ÑtÕë(i 4°µ‚òùüæ’ß…ëpÇ¿ …ƒGBx?û³?ûè¹òÅÉõ²Ž.e’Ö²ôQa“æ‰wm'Öƒ¯áh0‡ŠWkiº™´ì©ÿWR¯%à!T%g#"í”f£0Ò©ƒŽ£É!»H0ã-¨½þ{ñÞ‚ƒŽ™ršÒçxœ/ùËõ¡3X=¯Y}¨äÔ”. ×ú"wú¦À×—àr>óF/§#¨‚<ÃsfrÜšõsòaª“ò­”|]L¨.Ù²½Ô‹›Uœ|óôx°„e¯@—«T`næÓ3n>‚è<÷˜ Ÿ{“÷â ˆ‰ðç7 IzºKð{¾ç{ÆêA· EÇ.]ä\{"Q¹+CCÌB¼¦0]÷j~„C) ›@íõ–¢ˌ„Ï?6>° *̤¸™¸v5²´†®lçA'ßð¾e‹qßz=â!ÕþꣵŸ]jI@¯ötÑ‚êÀ V©À€œ9µ<ô ©u5°2Éé€Á1ôW± ¦!èuJ®$·J‚pG#[FNÀOšø@júi-›ž\“)‘Kš·8ÁNot}Šøª ( ÐFÌè³¶9iÀXË~IJØ`Þ¬2™dŸÃøtU ý©‰ož^"÷Fì}Í'J$!{"Æ à#õ¶ùs­»¥ o)|ú ½±[/Û¶öhÏÚZò-¦ß²@Èsí^ÊYÊÞjÔvÜ'r?ùV¹~ã7~cåÍÞ¦(5ikçóFaŒÞò·K q rÌ×v÷ô²É¹zW“éSŠP-¬Ïô=Û ÓfGM˜tJsmYÆhЙîV-‰ÓÉÖI8«ËrN¤ìéq¥ŽñÙ¬þˆF•»æg?ûÙ‚äEM%Í ÏíéReÕB[1q —)ÛxQüt·£<óSe]Ìî—Ç9fCư®ç‹@ðÇøoŒñ96Î;'mìXé‰÷ß›¯™# ¦µ5eÖÞÞ’çxOóîø£“6³,`ùé™ „–¥xï3›מLZD„9X‚NzSÀ)ÓÀ¸M[×ê­éü ßð #+âkÑiüùÄ& ލÆ`x±Ãð´}&Qâΰí|ô¬ÓÖ^Ìôâùn¡ Jbû–'F€Tl‘ éˆk›ŽìuUõƒ­u¥ŸÜRX„††ŠŽ1„fŽcor ÑM=ÿo`ß2áö!îËWKàaFµÄ@ `#=5øQç3ã(¢*àŠÂæ #Ó˜i2ržÎsŒ¯)€”“vü?¡kÝübµI'е 'CÆÄ6ºàÍÓ¹-z^‚ms@IDAT|P9)  V­¢íx§ÍI¤”÷ôöƒ së˜_u”u «ÏŽ ŒÆñ¦à9ë8=Ï]`[IÀ{J^$:­Wy¬òÚž©‘6vŽ ZCõ¨76ˆ– #Çò§9å ;ÙZðÕ-Ìþ±1J•ÙŒz,ðIä¼ePó~¶¾žv×ÊJ«ÝÒ"0·¹Ñ¡+¤ùÓòÇLU /r?§ùz*?· Gd¥z96UŽõ5II±b]Ž»ã£*å7ý&ZŒÅ8|ûÍ[ésŸûœÆ'TÔ4ëÑ©¼ÎŒ-óYáGv¹êf‘WÌï‘ Á¾wÇ#4ã­¹1\Æ)à ü¶ßöÛ¦?º#5tEO“3ç©@hëàÏþP&Üœ—øÈ7qOœÑ·ž'zÐþປf¥X©âÛ–#“^™2^ýq+‰Ìåò¥ã)¿ú«¿ªª¯†srÚŽ¬Àöû©Ÿú©y¯;qSàuR`¤AС´l/Ç®ZfO—s&°ˆžÙÆü˜ty$Òp'u€#×^7Ç>ÈÁ¯l2I‰žEøDy(KQϭضiëX‹'g¶Î<#žé*®b‰‰oxî©ÁðhTåÑ.Ágž»uõÑ¥g½œoY|¾oþæoF®‹HÓ â§ó9|ï;·^LøôáQÅãŽåá1*úÐÛë”UT¢°hÜ&ê²0ñ4h0+¯@j¾[&Ê܉×LàátØãZ ‡[àGö7€ÄeLPñRm›`2€4\%¦]|Î<ÝÀ¡¡˜¤YSpJ˜ÉEs ZÃÍNR ‘‚mÀ§càÜŠE=™€_mzbs|0d>¥ë–àyºDR ~×{š2 Y§ªÜæ|î9¿ Éë³t;! ¨­»Ø;E¤kÝÒÑâWÛÂp|ôÔÊ‘m4ù% rV.§n=¼Žo‘l¿>½Ì\IØQ¥‰|k¢5å´|>wz÷“Ë|ûa^LÔ!ÇN›BÅkAJ‘sF÷ÙÖɬçÇZÇœìéÑh6߯_ñX˜ÑöHऊhiÔ-û~ ž‹-óˆÄg¶È)ªm{ö*œÝ™ÞzêJñ9Óî¯3°Hª§ºŠ©žÐåq[›S@b52Ü+Œ×¬B)m™ÔºS«hi/@yî§æG¬D]¿£wu"HšÃÛD=í)ö±¨…³>‹ÒË*mNꡱa$2‹³Y‰tcã*ÀN#æ¤6ÓØåµÿA‡>üùÜ3ºæíîÄMWEU”n¾0pmdPj9é¯3júÔ‡v œý-¿6> ~@ÕMå —›ì¹X‡>Ú¦ÁÕYïÅ*·~ž^t8»¥ -åQ_'s(å†ÁœVß2?‚Hv4Ò‚‹gã'12ÏfÜž»^~„>Lu¯ée±µ£§%iÐ"²JSSëQÂ'ð!|PÄä æ=*yÌÇ×-…†ÇxŽwR‰6@ŽÒÁhÌŸ¿ólqï*âüÃ{_]{êÇþÜ97^ à˜íL°¡“É6ã“–(DWÌn—` 0Ò$l9fhŤ®ÌÁõ­‹êi¾˜DYÅÅŠáÓ­Õn6-dÄk[ ×|ê¦@Þ¤ëÞ<€P5"*– q;i}ªKœ ûmÂêZF|UfÀ âFC w•Q§Íïû¾ïÛq9›EÚ¶5 xKn9¼÷æ1ÉþáŽÏ[(WǽµJŸû‚…1ì5…ëêF»¢ÖF¥Ôª<1§F—×16dˆŽž½ŠõÂÔ£6?¡ü·/æÍhñØzÌû%ì.ŽÝðz×~t¤ÙæÆZ`K'sªâÇ ýHÈžZù½lÞ,îò3Îu›¦vLšÅs;oÓ` Õu@ãy½æùV¦Ë¬”fÂÜ5L“jÀ¸í˜Ä,{TÅSxKÐËfŠôô#|œÂ$¤(rŒL a³›×3¡*9ˆ2ŽÏ~Ë~F{4-«¯ó&s)‡Š%xÔ€ƒ ´#[±åIÿ½‹üü+ò,êИÜh麂ÙQsåm;¯º¦w2­ôÀi¿qñŸ^݉›¯ŠD¶~·¦ÕÖI Å Asµp9IsiŠá'Mp¶±t%^~0–¾A ýÇõe{Ü£K¾: œo΄Еs©.˜­ùˆê-õùÑ¡”Ï$¿ÓXzõqÄ0ÕèbU¾0 >¢Æ›öÇK=r E ö@Ä©?È…hÏ=Þb7H£ê­ûŽ%×À00f)Y†É"œ!D§^苹Ճòó—iÝ´Í©…F82ÉŸ"žÕJaxo~ ܉›¯¤ ÁŸ( rÌlÍ÷2k^‹Î¯*÷ÈŽ¶Ž33zM€G#ÀO’ÕŸü“r}w¼±x›Á§S7±¦˜i†­u¥2s:u Èåë8–ºÄž­"X¨K " ˜]ñÖf*åñÀÛÖ‡.ÁWeV;‡|@·Eôµ`ME\HáGÖ9 :‘ÌÉoýŸvÖD®¼Ê2m#¬.À¬ÞÖDÖŽikP¸Toýž—Öˆ6ÊsÖ6 A}ebáh½%öíÚ/w«òV.ß¾@héôÅ(çÓ’@lÕ? Äm³Ãä™WŠ—ÓúÁ×í¦µA7u‹Éœ€Šô«Ê$«ô…åmš•èMu†@²¿ËX7/ȱ;K)NawïZRºõ•ßù–ߥ§´„ç+{¬>µ,À:F]yˆdn• ®V@\f–TruïŒm¤ z™0†ØÇÖ¦K0B³~^áhRÏ9>7ôÙ’;–KC^d¹.䨹y™w“±”}XûN†Íø>; ?|ì¬Ê»;vçÜx%8‚ã€Â¶ñU°Âràxù#þèý£ópFºv|ÉÙGA…k–a0P­ù RŒýðB>¤íÀ%%.|1VÚj0%·ZìæU>%‰ˆì‡&øªÐaüĄǡôcŠ‚= ß ò;Ê?(#Ó-¦WÏ'.ú†y/²Úr½¦—õÊÉÆëƒ(€bHwñEÖZ¥ŸùFI€À¢ž6#¡R*Œa3@w[»%²ox…qvÅ{¼ Gn¿úÕ¯'P¡Ç©v<ûyи)ðª(`¬nl3˜rÌ‘ô ¾–ᢳ˜–¶ê`yiÊrzÁœEƒ@…ZX_djÏ¡* Ôz«tsÍ]0lsdKÈ\Ïaçz‹Œu-GwŒvÐ)™çÂ|8=:ð©ü#ÐÛ+KÕë<ûcm&]ßWާÌñ¢ØV ‘bÕL}ík_‹Á‚ýÛ³rj#&œ>}Ë$td_±(w.ÈVàxÙ s¶[D!‘wÇ`‰H³’Ç›^ø¸nM½­Ë·/êY2U:Á[ƒûˆÈ(<`e}¸§`²vt¬ùÇ4+|JÊ>ɵóTï´M ÿäaMoãrZÜò…ŽuêN‚wGÖ0…o6Ï)S¢wŠùä×–_/Òa‰[aóDtoËw)TQÓÛœoÓíE¬'~eÚéw´òi­Ñ!Q·ä³­¯=òýï´úØY{üÖ*Ò9Ê3¬ÿâ/þâvËeözÚ; 3yµ_ø…_¨J_Y1ÃLÎË{[eÖ[ƒ!j¤ëMýå5~|îsSà•Pà·sx”»ºŠUækd®Aó1ÕÑÅô¦fžI–˜Á¿éY­ô©W1aó.÷ Sû!!rZVûáj}âõ WQçIÅs]—'ç%‚PP£×¡ ¹R£Ê„¼Ýé rÑ8ú89·.à wô“ˆÈW@± Ómb¡ëvŠü^ÄKy5/øÈa©f ¹r¬RÜøF¤>ÐjÅ Ðg=J€Ù CÂÀh6TŽzÃÑ54¬¼Ø¯°]zA—ßú­ß*Qt ·”ñcpð^F”[c£8ü§T½3o |})0ÛÏšÅMLðhØû €%  ’ß“K¦Îƒ41 §°gü&N”ù˜‹#(u¤@f )BÉS þ@&/¤4’ØÚxÖE=÷»Ø^^|{à³gyµÓB×§zCw`00¬ÿötWäôÀ®È}]²*6yÕãÚuo½•‰’˜0»H¿C(/b0\vËš¢°Q˜ý­°µƒXTk˜pæe‡\¥Ù­Ö'wù‰„<`¬Ó´“°{!ºx½"v~å+_¹xU#;=L$|/ÊÏ-š|¼Ù,'D[€•ÿ¸â2(+lQŸê§ ¦ó‘møûž–Y3›L…ÕB–U»–”®ðšÖÆÜ8K¾µœ ôóâÐÏ—•CÜö”$FÓ¬Ù;:Ý)vsé6'Á-oJUôÅ hdñ'Ðÿú_ÿ«Ï1GJ$?—Iqb"yq?S+£®GkGT‰ãoLÓó"wâ¦À+¡À#@Üþköô³bpÎvjùÈ€‡¿[y+±)Ó,ÀUF^"{̦A^L+nèâÚ]ȇ|ŸÆ´%øÅæŠé°Ä©èZàÅ´~24QÏLïÕD °UÎÔN&Á •FNw b㸜[=Eáf0Ô1Ý+ŒAMù뼈×9ÿQ·å3&žiáO7ôÐ ‰å6«àŠD#Ü1 ?òªÆÇ•Nzâv´=ŽÍ@aH»exϳM)p'n ¼ d¨™4 À#S¹sÜxþžü€ ¸"³y4rK±d‚:½ð”cNß´-d @µh›y&kÄ´âT T´Ó+0/™ê‘¼—éRIÐq{è\vxuž®yBz÷ ˆX1ÀU#ëYÓS½è›\ ç=wQxêF-«…)Mþi"5âÑ;ÔöÅÜÍ|ŽñÈmkå*ÿ´Í2‹*DÉì;L.Êjôù:2*á¨EY|ñdbÔE³ŸÐ­OD DôLÏ­Fð&Û_æïý½¿‡jt™Ç[rˆm3¾øÅ/j¿Íµöïž–ß2^[Òl¿Iëc¦Y¿·bsÙ·¹†N"Ôrý±k¶í°mɰÌ[¼çqÇD~b«ÕQxb‰Æq¹”§_4ˆãŒWÏE̺Lmš5IŽÝHi4V‹"ÙèÛ7”£–}Mk-·ZùÔµüÚÁ\ÝÒ4;[k\1Qé|úÑÀe$Éz£+²|Øê&ôÚ±;}Sà5Pà ÃëFï°…6¢XÂ[Èc3ÂOŒ¡…ûmÈbßÝ1ZqS$aÿâë¯òáÀyè©ýpmSÏŸñ]«œ¦7kÿ"3z•:ˆ©¨^ü‘áñÔ$ÈáöÂýÆ-ÞÔ`¨º¡3#9#—®êpnQ.?šUežñ Esâþ|jôtøOH bÿQ<%¾¡’ÂÎàYõ• xÊÑÒ¬³†€˜E3àhS¾á½>ñb"¬ÅîôM¯/Ú¿m÷kü›ã€Š€%ãÁ®PWy0¦Î6¡Pg^§8ŠÂ³ s PY}*v– ÆÁ°šø.M:°m O")Èã@¾ÉÜÏ@ÇÑ×oeŠ[³¡Pݘó3”\=ˆÝê®—xWfÒŒ…f\ Ó‚ÜÈr!¿áB:Æ)ggˆ„…  ÄÚNAƒ >+aD]?âÉѶT-ÌYÎÚÈ1MDJÔoŒáÇ…?¹œOD ¤_}6mÊ£•{}«ìr¾Ê±°¾ÀÖdñ!kF?KÑÚÈEºÁ”&ÀSVMü±VkÐÕV€ú$ôö;çïLV™è—[Éõ²}ŒF§/Ëázë˜ZÍËšl’5“½¶ZûÓúO«Û™çU«û£¹Íƒ¹…\lâ1,¬­­A«XÆs‡H–kÛ'KÅ”72Û¦…:j¶<¢³Ô´3µ&Z)¥Èd††½]h8}zkŒX‡ó•}t,Ûµ‘yq'n |ʸÁÀ·ÑNèªKmèµr'ËÙß®ie,„<ŽÙ¸Yî@òý\6‹É©´Xl:÷F¯iSrÇmD N\ø—Ò|c¤„UÆ¢4næ>8räáÏôaWQÊ‚rt(¥sd †b§l\ì“D”: hÅú÷¢.yíÛ©ÁP³«‰Ïã<Ô£Sz~Hû6Õ¬N¡ï²>î:Œˆ;E^DFjGöS pµ’oò¨áƒ>‰uôçô”BXgœÜFúýÉŸüIC1e6(“cIrÂEäâWÆUÂô ‹épM–ûîMO™wšãëßà(€%ùb˜¡:´Tr<žd6Mä¯1TšŠo¯/>)ÜÜ«Öù›{vÒ’*µ“ãb¦ÈS#Gt¢˜£xôh@ÑCÚYcúp*³FOJåà3#fÝÁ§Öi˜/ÖBnÿ 7àí‰Ë#K&è®úÜ2­¡­Y Ïuª´qÔËfì%¤U¶'âá=åh«ÜJºTx¼ÞP«›Æ±ð'”ó‰„oßÃåù~·³nÕLŒbš±­1.¯­1ét£ÚZ¬t£V—´öb•ìþ¾Í± m³”WB¼Ñ`Ž3À±Š¿„ ¦q$8-Yf;ÓÜ?þÇÿ8ˆ³ú(ÆuÛët¦ÍÚ#HÈ-|ähiœ’Ü»™ó{d¿cm´™ ^ §Z’Ò©OƒÅ’jû^Ûò¯Ø|ê´jqéN?¤cì…½zÖDˆ*ŠW,•]~YhþaßÿŸ?,*Ïwú|'n |:¸†¿9`QÆ|Ïn_L`› R‡è†w¦žôÓÔ´-6ÍX£ûÄ ¹=zeòFêg“‹Ês½òžËIéÑþÃ:` Óç1r>zÊ1ÿyñ sªN<ä‘OÑ*æ¢ä„ »IÙ­¾‚„_m–þ¾PË÷ß›¯‡/_³Û¶Žb#q<ûœõŸ<0¶AZ†Y±Œ37 Z#Ÿ= Ž¿Þ4xM7:X‚ÀŒ¹ùSÕó/5£±…kùðãˆL¸–…?À4¶¯D»p(½&Âv—õíßþí«%Ѓ0d‘ þæßü›G´´UïòÞ¹ éNm€½&²#þQ<}úèùC)Ùã–‰.ÓöÉÜÁXCQÙ í»ƒn©óþÒ—¾tìá‹SãXåι)ðiR ‘¯©·þmÌ$™ÔÜO1Ð¥’cI3£åuF÷<`Lœu·Ÿ”¥°2R[è­Ûb31Q@²öŸi šZ³5n£R¡A>ðo°ÜU×+бi Lš¡S@àT榡% oÔHŒâzgÝ´Y BmÉ›ÉgÊHX•¶ËÄTs‚Eœ4§kaÐ]áãYk™ÒÄB„íŒca9¹Úʵ€v²¾X¾W6['}‘µ…Ò€æ_©;–ü„r>0ªµm”ÓΓ]Oøæ‘¢¼ïíØßÉÊ?uÏÍÌÝÕkñôA¬êër5ê™ÓÂeRpjÙ§]çLæ2ù#ºTøóÁ93kÖ|Od¯_¶§—©c-ü Jèc±tH´8s‹º%ÉÍ_iùÐŒëé”ì0@Éÿg6~L „é§ýMïÛ-ƒ^Ëú×jÓ‘­&îf@_Í œàÓ ‚<±­Ï'ìUZäâ‰à4û— !gLêÓ~;z=º§H¬¿M=³½Î}ySàÓ§À3·I™’¸X2mlðól©óE¤Æ?–û¦›‡¦X CS›*gŠM7Þ|Dù^ØG>„F>|#1i]bÛ%ˆ½ âE¯Â¡Þ‚Ê·s4[S§—ª¨¨új€Í_=jC~o’ôÑû"²<’W "o¤§ËâñÞôûN ¬ݶÒDäcUÊÏ †ƒ•Ï«¿u6Voè É‚&«¯‹Â<å™ 2…ïÄMO™yo®0C:)B i‡m\:æY Jw™`L¸…¬UœOàÇ[BÊŠÌšu¯ ™â£Xík Ðr t¹µJ€Ù€4·‰í¬ùâ“@àFÉ\=FùG0Yá¼ÏŽþ_îv5梈­Êõß:<•æÓòÙ €íõn¦H¯¼Eö”ª+^]k­iÜIõ4YØWû¡Ö[:Ý>ßv 7îƒjð‚á+À~Ñ'qš".©žèôŒ­k*¾­Ä'"r>öV-Ö’'ûJ+Ùà°¡¶à°fÝE(X ‰IÈ ÿþÏÿ9}„O’=]gåÛÜi¥?-¼e&Ye÷ãATü%#¦ã\Ö§#µÿìŸý³†¦9l˜/އÀ¬­•¦™Öç~«·ÀZÒŒU@›eZ˜SQPêŒ+s¾[­×ŠÙ÷1&‡±+m2d¦¨pêæP^°)aZ=7$šµ°^ëÓ„qEÈk¬ñí:e[X¼tN‘úsŸû\OÄþj¡FeÚeÊev™Ãððe9 &ëCïôM¯;ž»¹;¶]ÞÎ ‡ 6·‚qXjƒŠ[I‰ï& 7¤"Î}„Mƒ«陃%ž‰#ÊxE>ÄgN÷šøØÎ8M¬{¯¿—â¸K3™]‚ `#tÕ«éÒbωÑâúâvJSx,]È«) jVãäqA"·@¾0ÛÃëW°$ùŽ}h-¬±O™ˆt§^ g–Œ*ÏaÕÓ¯Ÿ8w·C)&ÿ:&•gù^†JØÔeNe£,Ï-H_+‘*Y,4òý£NÚúä4ÙjÝ—7> ¬¾AŽà‡!müƒ%Í—ce½6H£“ˆ» NgâÆ×sûœû¾»{_ƒsj×^U»víZU¿õR«ðcr©yf%K­4– Fæn¦yé}äbkÕZ¶t&Jmð{ÆG½£°Õ 'èÌ`ó±æGv€¼YD·8*XWðÇJŽ9ÄM%d•'ætŽR– a9&tøvǪÊ!°4B¸QˆPcíxD¿å‡¥6ȽQ¾Òå«„ÚZHÖ>ÕÈ'/¾C;V7¼¢†é­†LÃÃÝò]r Ê+’–%mJ;3Oé·œ$@ø€ª&·i#uœWâÆßزçàì{^ª!z»‡~h%„ñEé×YŽÈšt(9ìFm…WG¦ô íE¬’Ždä6×Õ¤GG©t68† M Æ<¹}šóJO”]í–|«¼ˆÙŠÇB✹¬.…µ*ÉØâŽ2,«Á©ä7Õæq®¶r«@x4„>ê·;ÿî·Ðsq™Í%…ÍÚ#V»{ÍSŸ5Uö&–™ä@s ‘ß­Î,mïn‹[%âhû#mô keŸÙcÆ–Õší¹Òă'ûpäCkd@D óÃþ­Ç«ýp«y3 š¾H×»Mƒf3ÌêJ@_KwKumÂì.e¦Ê­UŠ(øÈ)ì8î0¼0^Û=Ñ·f%øô§?ý¤¨–ëülŠû(cwÝúm»49ÏQŸÞØN/×-…^³-.dækÛÎ2}~:BÓ«uwäpÞ2ì ~_#`LAÜÌY.ðÖi ïÌ»^µ2599Œÿ`*@âÑÀIwÇ™ËL.§! Ø€7.ÍuˆÍ-Ò Ðá%ΙsæER<%VWÉœàVñö°¡,¤5™ƒmÛ­.Á<Ïù\‚ùŽ®— n«OŸY4ý#xiªÌž6ÊúÓ§t†žMŒ™S²2‹'_“€äð¿Eí¸}îbgX^?ëRsÐîéüÅsçV6½v:q¾Å‚àpº’4Ô|ÜJ6µ• ¶´p7Bò÷ÙhN/-sêo¹'@Ò¼væk „ … ù'ÿäŸ<ù9"ë*™Ñû^”~˜p(qQÁqàH•ËG«:d‰Lü`Gûƒ±«èF§ô†>4"WÿRþÓ©™×¶åZMEtZÏdB#çä$0·¶Ds„͸ÅP57mJ…ºk$˜FÛx]'·oþë=E°¬ˆYó·çºŒëšõ(x&æ’cX}¾¯ù𝩥T<ÖI5‰è6“ ¡3ݘeôž¹I76õâu"úä€$Š ùT(è×¾êšWŽIj”XÇÖÞ9w¼åip€{õâ/>îÉ­ëÌá§×þF¾J¢™Úìøz^ ©ªÿú_ÿkqY¬¾4ÊéVuøê#*ŠÝTŠlùd1ok°õÒk’3Î[H¬ò¡ùáÚ$¸=åôR¬6ž…O—Գȥ´õ~«€Šâ'û=2jöxzõí¤ÇHü©Ÿú©é“ç-¥½¦OàC¤K6r| Ÿé´ÖLÚçN`ëÝGiCk¶~ã7~cod’/á‹[È‘¬9Yt Kpîªo×hô/þ=î\zÔ°;ÿî·Ð ÅXüŒÞùLŹzvf¸R{Šk˜lÀ¥ð¶5?dý EŠd–Æ]+þ¨ªÄÂæžè2u_<ÇK*<ûFæ)>’Ø»¤²Aœ?и)›9LPS‘YŒV²Iƒ¬hü€XPvòOÙKsR ð¼nAÌ TïÊi çIÔàúºéô¡kf‹àD+%,4cÅWâÚFèX3OÓ–†±*?C_%%ý–д ê§x¥Ì×õˆï—B\Ý[o¡iP§äxýb©l²›uxý¨«ÎÕw¬b¨=³Þw¬Eí·˜¥žÔž¢žLøjïî-6gî¾÷µÑæ&i0Ÿ„‚ ?znks¶AÚˆ™J†^TÍ€2åÐ…ÖV5ƒŒŸÃ¤²Ê}WñÓÝö„ÞÙ߬x»5LÚ4¿ÑiUss\ó}ŒÌHì׉.†Jû;=b|ò,ÏÀHëÓ¦†ÑB¿ñsèA÷ß»ÞaŒ4h`7GI|ö³Ÿ¥*6ÑSÁ T`Ý¢õ3KÃøÑ_žuÑ®¶• I†*jVôg^ÙLK!šHÀòE‘€H{4³4óÌS†fbÏØmÏ8ʇ½¯7…·(é®×yèi‚TÃAÈÔÔ48=éR¦[I¢žGh’†i^Ÿcª„—òj¼@_ìŸyÐiÂçÈçq>Ó“¤>z^µ†Áðd¯fÙ‰²2öh[ MΆhJñy÷G õüX#`L50ÈÄÃ/·ðô‹ß™ï¤ŒÌMÚæäü8Œ|M ù Z\v^0Óà!ínÛÉ€é€ÐhÏ3ýáâTÛ¡;ÕÎûZJºV7¡7nòK´ûfÕï‡âL€ùˆå4nËø:¨ºÛhæ²ÉfGÏ©ÿç(d³uÚ™ét쥅Z™˜=S|M€èiíc¢Xi¶4A@gšÄˆsëÔ˜ÔÝö‘Ýå¦ìš˜}¤>™ïòÌÛPÑ~ïþ¤´>ñcI¿–@¨qIêÞM§³5_4×Ý$}QT½ÍCúQYbxÈ#fcN»0û-kÕ£:ËǪõ-©C^ðܤ‘Ü[à 4-ç4§ìÑ:²=Âeà“P°Ýb‡Ì—MãMàÝ%b¨–¸q½hNUù›[Ê 8/ AfÅÞzàñÔöÖ¯(F2¥ï×d}½ßû½ß+dÆ`ÔΈLPYi.ÒI’¿ø‹¿ˆ gc¨¥Î{,Õ.ÞÓø×+±m~ ì£¾d¥,]Ò¿÷÷þ^gñáÿÓýl6è«P¡c rF¬y©I®OGIvò¸Ó]”êl#íu?`©­[²9¬A“h2 Z€µÚ‚xa…<9U»Uئ뽗Å?c8Õžpžý$¢ôÖ 6¥ KPl‹sêgìWUîB ˆy:Iû‚)6+G€Áj»ÿÞ=ðn{`“5¦rSÄÔÈŽj ·4⦪V5Q:hyݵÃåÖé4 ­•Âû%ý¥Ù… ,oX_«¥åß{§ë8š±S`ÓTÑ7oøš Æ@„»µ–ÇÑüÁŸæÔ~x!úÖr}Ø[Ô?| øí«pœD$\Ê\Ý QPq•Ô'×àjÔ`ÍÖø™ ¼”W{tôô¹ÄôÛß=1‡°sJ,³ˆ>Mž+ DÿJù¯(Šúj‘¾ˆStúdôl;5sF¢ÃÖï<¶Ãý´¾Ôèíý(Š1t:–ç!b*œÓªÊ¤’‰'c*šû âneÈ÷×:aœ±B›GðÿQÇ€aZ2ÿößþÛ•M)²qïZmÈë`û5MzÜ>0hh2â­Ñ“iMU|Á“ÖŽ=¼š›§6 ˧KÎç~Ý0É•×-?›g¦Èê55ð”à?†@ORn78ûÆR%‘ÍÀéSeó<âNÜ=ðvzà( znZºª­ £ºç$=k½2v–”c‡¸‰ÍGY3ƒüõg±n< !T’Ö6 ¥Î'Ãh²×Rk“ޏH÷læ4û W†¥U­iykü£Ë*ÒÐâ‘ ŸXx"3·x´üÚ¿ÔºK OãËr›É0™}&Æ:· K™^òQF5>™«¸îJX»å¸ÃÐkzÙ‹6Tü£öáúP+ ¢©|ÏXéìñY}\ŸxD¾uOԼך0x !Ép2¨|Ì03ظ3ûÐ7€ƒÂqk\kJ¶²½/pÖ± ™Ì‹F.oËUË,pÊJë›Þé»Þr¤ãŽGR£ÌøNŒê’ º0Bóƒ™<ÉÇ-¬4Í„ªmâ\¸•E{QîZ†¾ÄhÆ'ÎÂÄV`𖴹ƨóTˆ=‡¾X˜(P¼ZZ›T|n•Ìe6𵜖­S™ 6{€ÄTW€tKXæ0{žò(QݽÎf_ÝJå€öè  Ä<#J D /~ºus«Ö%ᥠ¶ˆ¦åâ_vÆD¼•*Ò¬IÒRh,mwßÚå+ „Þa<޼¤Ècë[Y'fŸ:ßåñ*DS@¡ CóZÓ°ïÔ¸ñ ŽÛáVbÑrõ8âG'UÐq¶Â dñS3—µ†ÓtB,b>t!l7Îv,‰6ÇŠýqƽGG)´!Øý.,øÿûÿïI>82u´Ýë+Ö© {ò~@ïcå¹^ä˜uNAÓ,V/­ò^ÕfôÈ™[Чy–ÏK˜§&S"6ðÐú-W®êé„r™ã†>¡«fûJ P ¤_ë¼Ów¼ýxas «al&­4mÛ0Œmº°ç„G:è)4Ž3ÃDүŷ¾øš SVµè­Ê¶XÔ-ÿ–Ø4PUxú—m¸H'JŠpl‰ÂbFº c—ð*Æ<2 ®*íß.ù°d4…®/b#B$\’^ô/]m€u×Z•´úIGñê³5Õë¼™ÁðÉþô!ù|Ègò±ôF âö Û¥` †„ÑxðîÌ3öÌÙ5 Zrx† ‹&GêFCºÎÉeÎ-nTz»ðÔâU°©NïqCÒ}ÄPC|'îx›=fdê1D~ Q·-L;îòÀoÜòãt½¶9V=Q1ØQÕJ/ÄXVݺ[ióÁ6iÎ艳L´­ñ4è%^ï  q{â\Î%Õ‚ “¿& ¦²Zç2µ™?7p^üx-¾¥õ[ޏž˜pµl—mˆ0å†lõH¿ÑtI0I¡Z¢]3ê3ž¨ ^4Ø8&à´Ö{b&ǹ%A8êc)•þw½û6Ó¯+Æ!¹_ZfæÅx’ä¯ åU )ZŽú)¾&ŒªxÃÚöÌF²$Ó­®Û1—í 5ÖÇÓÖ(NÙ§T¬¾Z„ ¸ ŒmÇC&aˆÁa½\3K‹ƒ×+ˆåboqMÕó(Zi›zÓ÷P7ègãbÖWSÛ´fM_ž·êÑŸ¶&y¢élõ™®*ù\’¦6‰¸]–ùÉO~ LSKº+¶ÕL îâ(ç­Þé»Þ~\€×öЬÛ$,–±<—•Ð}3ˆüË¿üËÆ6Ö6'xŠ[ÝåœÕ˜O¯i£™áúM©`="~9Ý'÷3ј ­š3?(OÔªp¸ì˜01:4‰¸h’ñŽ•èÂyL=jÜó‘L‚/¨Ïé¡7§Pªzò9ö(Ôiáºÿp•§ñóšÙM}pžIûèPêÑpѼíÖ› u£Îó‚vêj®Ûu~HeÚü(áƒ2{~×w}?.V>ŸÛG?Š|ðVpÊ€1lŽn,Û¹l³_ϵj[Í= †kܶN©ÍÀFÜòšg‡‚ UˆÆü›óoÔ6Ûœ.Øêؘ;çî×î`c岦Ör@”‚p†UÌŸ`Œ[ M­ %º»6Ò¬[ñÕô8% ºµÁBeóŠ €c«A/5úTÕn-í̧ ä[0é‰jz™Ì-jÖ$q:ß\’tÛÆ,´êµ´U*VX•¬9@µÇùéÀG†œ¡oJ,"K;¹4ó Á$’M¹.™Äïÿþï#Ç+7Ö[ÒùЙ7ÂNŠ]âÏ1•p¤ J§²ÉVÏë]¾®@hQ™-ßôMßÔkˆÿÓEÖ†Óˆ=ö!èˆçÅצ}™úî‹§ÅsJÙ"gZw;î$²OÁ¶çnn-sWh+PMXêE£vÊ ™‚†6=®ÕJ3:~›ë¬å6]‚ÐLmtfßÊv¹N^§2ë ]G-*Zç‹<Ö&,ª, ƒO£¯rÐ=Îõ†‰rUýÿñ?F¢[]&ìÒQ›_öíIm\0Òc†ÚX²nk©··²Þñο{àU{à¶R¨N]« Öïæ}bÆ-ö ²;%Ž7Në%#á€õ@m\½ï£W3kå`ºÀà–7Z”ê_}DÁtëŸõ‰2˜,Açê.ƒ’Õ1kR xú7qâç¡d"â†j^µLŽ…-ðT¹ ;}5Ë3Ÿ(¢‹¹qS(Ï«UÑ~í_:eK\{ªVå›C©h†ÆhÒiS™§C_Ç’'l+ŸŽÒ]:M×=#òù>‡âÓø@>“å“ùp>ßæAêC›´æhžÆ´¾ _¥Íµð8ý¶ ¾5° ob!bQïÓÉüv%i¹L:Žˆt‚ËÕ/æš¹ÖÆßé»^»•ð±I—@%ÝMÐ%ÒÔª);qJäƒCÕ IÓ~ð)i+Mf‰L ˜›úÉÜÏtŠŒø¯ü•¿2ÄZ•Ÿ‹€2ùù oî^CÀÓW=jã2žÐòâ¶×´;6“¨Är˜4øÈÚ¨ZÍž¶%YåÆI“äé°×4iùÐF¤2ì” Ô€7€ ÐsUíoaa¯|¸é±T¿S§Ô!EÆñÉT¡ó¾T^Ð^{Qº¥÷b ßzˆ©öQú$Å£Ó$oá×ü¸íç¡òŸ²wâî·ß/Öv’°Gi›¿éÍñ~¨ÂÂÐZÛÌÐ[¤iˆ)üÌ•O··6>y°ÄGt–ê­Â㥧]FºÃÈ–4½õ3â¢YÂr˜¸hIVÓa€‘êÔý[&a†â)TÚºÆÉ°¾=6rl€öm€Ó¥% ³ÕóEÿRÕz¨G÷+®aš§‘ÚïÕ¼ ×ô²^Ù‡f[1åÖž¹LäÓ±ºW'7gº«óUu1 ¦ChåSä)õL R¸êùC)Z§µ-܉yEô¦îÞ¹„öC3Jæ›àf¢É¡ñ¸¼ú"É“2Žú=ßó=Ù.bH—m/–è÷¢Ivšq'îøØ{ਚï%l g“ñbÛößE¬îƳiafE¬=Ðÿ‹ƒýT]¸— óàêøG)ûHÝhªÄݹW˜ 1é©$sì1Lêé#¦J¸l.]‰M¤#<>ÑÌÕôè.ŸqFåöi†±LXÅ™f½Ô¼7MÈ$«é«y?Iq¬||ô’Ÿ„K™Ç®~F>ÔÍИաTS5X³5Þ+x‘PÔ|Äc¢ÁP¾^)š8»Pl5×óv%žìÿ­”ëŒ|tŸþz­4l žëSg㩟éoÞ¨­R2­Èž«—Róä‰åVoÑÞZëkCPOQÿ+h˜úK<Ãh[‘ûòî×è V3æQ‚+  Á<Óc.—òMŒµ-Å=h”¢p•Ö€¨j«Öºä›%db¥üMÒ³¯;€¦PW«‚y ßÜZékÀE QýsW@¦|“ÕàÕXØ”šV°:m[+/­`ö!œâÔŒ¶–CÝÀì °€÷qbTÄTìé@ûZ\°o†õ~9¾H“¡‰e#>^š*Û)JÐ ’4¿@M¡™«š³¢éNkÕ–v,iu¬â·™xuP×· xy?ãéÇüÇ_|CjZľߑ®*Ö‚Q5{ö ·Ê!Ç‚““¼B™bÞ8; ð_þËÑ L2eK@ ZþJË|æèLdŒû*‰‚8&Žá®í.s'h|c× ¹*R™'¤Uç´ÆzæÊ¬s”ÓÇúå´‰_“0̺¹?âŸù™Ÿq‹æ›FLJ3vG:-BÀÚá™7ã×®[ÂŒÖÏ›GDRëpÏ+óóQ0[Ah\zÔrŽI>øc.ž©öôíîÌ»^¯ž©©] aç)'ÝfŒâq-ŒG¡¸rñì¹E0Ã>/ìѰ§–JgÄ,¹)kÖ~0õTn5z2ލ‡># šÐ,öV»1™£à•öuÔ ]GÚ!ópé1MÑSrsd•P½tükm&e‘â,ÿ š= ³\Ú÷•~“×8¦_”!0;aH/væ˜6I’ \X½¥½”Wó‚^S›½²Oä ‘è]4öO]§uã£ÕáÉo±½æGõ 5„²oTŠüük¼²¯Ðz*ÝBP »¯ÓPŸsL”°‰Íz8èv|ϰÛö¾÷åÝ{`½cóægð&Ü%Â`†Æ¥Üdå.ØJýÁ!9ÊHÀÒÓDɉ€«y‘E¬LmÈ€´âaÒGû­€Þe úr,29u8£BG_œ’±t(KdaZ£lsIêûjŒAéÑÊ>E^;ñê¡ÇôÎÏ8à*B«Á÷×o³Y9=3:ÅUµjä‘é´û"6.[íXoW°²ã´¯ÚèSEõ‰UgnÁT×ÚJÊÛqæ„ÎÄhUXå·U4ÿ|8Ø?ð>È.wjü“?ù“6® –pyÚoæ#ú¤øÁ/«jÑ&5CË…Ì<«ë´U2E ÿˆe2BQ5!Äå3‰=).ê[“•«€ÌFr³~›yé¼2ÄŠØæMx#ì‘dÔÙ'¸ø ß$òiªkö|8¥¼”Wó‚×fX¥»tÚ”Õ™ºô‘Ãÿ“ßeësÐóyÒgÆ•N›ž±ª¡^é·§Þ} ûiF¨{• ¦1ÙqGÿ$ÓMåwâî×èä¥óÁP)t`À˜.Ç+ª˜yÁÈlÐigÑ8A¦0¥x€ ¸šp&Τ•€Í«e’2ïjnmsk˜ç.È7™kÂÌã.Ѵ̪&'¡‰AãÔÔüÂT0µ¨6×& wvNjóTuš§5ÛëUÑêš7¥@t3‰™SÏOæšòƒ `B)A`%x”.rþJLôÈ©ÁDJ$Y >’\Vš5M Ò«ýÖ‰q¥y›é·!æSÛ;_(·×Πdcúä;0$E£‘]nnæuÝŒ>·Ž 1ß5¦uˆ”­¢ˆ{'`.µÍ#Wæ ×6_±€[á†kiP³;à¿¥#±ÿ8Ö [Ês =V‰R× ÿ)XÔŠc?hjð¨ßr{h…VçÚༀZ¶ùù¤›á=ÝIò³­2ú¦ipõ°™¤¦ª :Ì)‚Õ™:+†f«~p9Ž£Òºº=¾Ôs÷Àk÷ÀóÀ4ÿ"¢‚›‚ƒä`þ©…©ÝâÂWNÉ“Df¿T*Ö¹Ìõ§æÁÕ’ã<ßÓ×·r¿è3"‡iáE“ài“dš²l«öʉʽòêjž§jµ$›VO“(Í6\­ÌºÅ2ŸÛÅŸvÜÙ?¦<”v¸™K¿åß„K™/Zù|>†2¿ä–µzs”æiäºÿÐxt(õš^Ö+-`k}$ƒá3ßh­\ýH¤•÷Ê:yÕ\LµΚó›ñ.D¯ÓšüQ~uÚæøƒúp–¡0+2 úT¾&žg½µÔ¾{àcìÀcª±:r@e qí?JóØôôâÁ€=¡q!A€SÒ¦éÄ¢rS\GùX±Ð%?²jqœùGf l;}Yü…ä;Ũ ¢»s†Y e“•Ù À¹U”šEÝB™p¸†±Ñ»Ôà P\'œiÊÑT5o¦¼5xÇ8£%~ƒëª’êç]î…ø¢ˆ[õÛ6#Y˜’K‰$ëI† Öu×uÎÝöš)âw3eмjâm„^ AÎ;Ïþ×ߪH¬´ÝQ ž¢¢@5f*‘cö™í7J 1Ö?ìÿ¯©éZe‹¸µÍéÒÒ1üi°£ïüÎïTí#‹¼âÆÍ8tVŒÌ‹û› [,uj{Ì'ÛCõ@[ùQ®ýCG«’£_5µnv?wm}?x 7ãx`~¶¡á¦µùax†{(ÃÏžãÔÞB3gJeˆOÌÄÿŸþÓªlà8ø+GË3' ¢°¢Æxߺ{àcï¤ ×ûïÝw¼“àÄ…?v¿+¼{àÅw #,)¼ ’ϤÌü6G Þ¨³-Hd9à'ÃW'U€F=X™\¢¢b+°ªívÑ®]lm$xVôiE „mÆCøkqéZ>&}9`$0‰°”2¥O£ž€¦QVÇql{ÄzYô ‚Ä«@µÒH÷PÐzËמBä¹ ‚èj×7Êíà/B>âGG®EÄ ÂÅ ª#“Iƒ`B<)³±ž±·ÖvLˆ´Äˆt¼ûösÞ’@˜±È' H @IDATkS4Òh>óžL±u]…£‡?賯ú*¡9 e¸ÏVvQ?'",WmÏrAìVû|Hüx8-2‰ôÔûèÑØ­~&û¤ÁÌÇ!~lƸâˆEÑ^Ø# E¯wáÕ.»Qç eG]oÞ•¸]—Y5éu†~Md¬Àã®0wóìâý‹¿ø‹@%À×&ij*ýSüŒø¨ˆí+nQ0§LÊË<5%™¹/ßW4¦HEÒÊ ÈÉ^¾Çü£Ä4æNÜ=ðvzà–‡ïÄÝï¼n™ðíÌ{÷S¶?ÖÁ?à\ ÕäZ™¼‡Ò%“=0èŸÛ$8!˜Ü “Kð ˆÊâVWù^[Eâ]÷òÕ6 -Ãضµ¶ËÌwA>Œ³òƒײlƒ-ÁK SÃÖ°7+%€äCs”6WÊÒ™OŠÛ ŸnYByaeÑ? ¸Ç‘)€ó왳?ùøÄÉ™ÃrÄÉ?Md%\œÞ%Œ¬² Q¥¯¿ù‘ž–•IÊóÂ[d~DùÖòß’@ØY¯í÷Ì7ëý³ %A­Røiï䟤qJ ³1M7“ø!BÀ#ÊÉO’áÉC û>Þ˜CSâÔºÝ-ãµ]p<³ÛÏzmÚVŠZ(W]0X!¡òL ø‡ïiS¤VʼnAÜÔ€ aüM_<"rJIðnϦw< ÇÝŽ¥ j@‹‘ sUµ¹™_„·¨Î¬‰‘¦9 -s†éXÿâ_ü i?,ŠÏiqz´ojCN·u(á¯ýÁ¦×j¾ÿÞ=ðvz`“_|(”ñ,Âk«—{ÛŒd,ê±i0-£Ýó­ëôÁe:RœO#âÙ=ˆ…_œåsè·µIÆôn¡%v×pwäM™½Ýß SP “ÕÄä2ÄŠ(¨¸JT¥Â4ßÛ³ÖK Ó<ÔTEê—Ö|ž5Ä_a/ùé–5B©.’óÈ¡ÔGr‡á‹ßqº†ÞN_© ÌíbÚȺ|t(œÚh7hC·a©^\f±ÚsÙ’‰¶ú1ˆ>œŽÒu`c£t9d·Lxìœ;çµ{üÌ4œ€+Q[3ÍáÈ€­jO YJÞ«ž±ªL9”Qùa¯ÄR‰y¦=„Øñ3šy4ðv¼k^UÈ—¦”!Ëîè¹R6K2Î)²%Š:ã€ë¿f.á6+‚Ç@òÜÄõ>,P¼s˜€ó:väê©á˜èE{ó¡àH39Éí¾éä>MÞ‹‰*ã!|¤Ür²6é1¿ñ}ÝhÞòå[½ÕÌõ´)O¾¤%¹Î"v_˜•«á1Ùiå†NAi}9+n‡>?éé;PF˜Ý•…¶qP6Â@‡MbiJ0Ø¢xl+¾^ª!Çôä¢ÓöÑÍrn¦X+)Í–íq¸nn%­i9³Â˜sî–K`æ;ñÇ·». W ÐñÞlãŸmjs“¨,p¬±M¢ZÁLÿ÷ÿþß¶e"P}ÖÙ¡‘òýÖèíkЭcÛ>öi0Óºyýê¡Ùlf_=®í#o)R‰Õ(;¼[J@ùVJ·²¥ËÏõ€L讇ڻï$n™!O—^ mŽÖÈ™/.“×"„Yë¸KªøQ,.¤ÒZ€ÚkÞTKL¤IÒóL5·p‹2{¿¸—ÏÚÞÒ›Èg©&¿ÙØF–{äF±~©x ï&Øub\ÉŽi•{„yœ‡&.j†Æ¤+œ7:&, ]«oùí®|ÐDGÍ¡Žtš¨-Ñí:æg“ª!äm:²ëoz¬Ö ÉJ CèHSŽ!çõû²ë¡©ó|¥óµ¶÷ÕÎt²½ã1Â6 W¹HŸûÜçN›¡¿˜ñ– O»èÎ|Õ(¬zãpUT-Þö îr@\+‹úž<(Z÷³NàUÙ¶#z—Ü]Ô%Ç·ëðwM29㊵²a+œJÌÆõqò%Ë™·ÎõÖšnscÕìºÁà•RºäkóQ&Tƒ™SgnÆÌµ*€¼Ó/Ô`}Yo=JgK$ê|¢Á£ÖZ’ÔœWê£ åO² "67^P®·Æ°î]o½«ô °æclV!+õd¿-]§O™ {pÕ3X#£Or<>³oƒÀ˜+< µ‡–ÐМ>zÍ´ü£ô{Fzlô¬zMN᪸àN#L6}¬Êéõ‰ÈR{À Âu²X)¥ÇÔ°3dÿößþ[OŸ­Àɇº´­}¹2Å ½„üµ¿ö×”bˆówu+²Q™›zfßf¡}š;𠇾3CÕf3q™í¦Å©¦ŠH1«ëLÒáJ…$üÌ•ÛQ­ó”;q÷ÀkôÀ* J7/DÛ×G9Ûh±¤áÑ/X/'p\¼8¥FìHi5ZÃ*þ(¼‡€Í*¡Ó}ñ¬ùk±áh$3< J.ô¹9AõîÇ¿¡¥cþä¯Ì„ÏCϹë’ôúû®–^cH»¼lÚ€4,'RÎÇuWÎÄÛ€_Ó«6àÒ ¶ }mç±Þ½N0óŸ~ôh6Æ\ë¹Ów¼j!&“Æ¡¿£€Zä o`&}_ðÔIø…_ø…ÚVlŤµµéăRÎЊ}Ð`+}éFÛ@¸œÝ->YÆCKRúÊ‘ O.«•3§/ ûCŸÒd\µÐì±5`(óÓ?å 1õ­k¥U&P=%‚÷ˆÁõG4“Ÿa6g:â@ó’Ž»‰ê$VR/•¸è5½¬Wöâ^tÛlhýf²åBfépÝ®ó æüŒÁðâ[_$Ò4Ö¯ RïµÉ&moaΧ D?z=ƒ? ,S¬m'ŒEBU–Ë#V«£<²çZÛ¾{àõz SøŠA”àÊp¤AÞ˜A<~j8Ôœ?á=Ë7™wF‚²`Õ¼¸$VÛdN"èH«X@fOŸ[‰¬ƒ¥óAõô€h¾Ü~-X'=ÔZí¤‹ ²®ŸBGôžè½xFLYð8>Ø\~6Så=J$ «S·\o ̲ ™Z[wL&ë# ?I¬X3¥u¬ôu,pD˜G”å·]K¿G~×5¼ÆÝ‡°æ56ÞPŸøÄ'.ê7 ÃÎñLN’è‚~nµüàÆÉá™2’ötÛ_—úxˆç”µrûTZ’T¶…¾²5„)Ò†=[W“íZ¥Aô©Cp¼–-Gi¥]¶¹%Ð)ù´f{"®>”e"!—‡w ó:Mèwÿâ_ü‹S<6ÃZ8àÌzG®p•òÛ6avŽ)c‹ŸËw¨®XY«Ø¤meô/(ŽIj^*ô m›}øÌ¯ÊÝb|'îx½8…›ÃÓ‡òykuAc›x*ü:IYm‚ç™U Q*4é•Wƒ’ÁŸjY0­±Æœ*A-䣢¶{mDÐÓF^ˆ›Iß‘—¥&"»çÍÛ6¢`ÌaÒºâø×$@ýI€¡y5-˜(ÈB¬O¯-ò¾o™ùJ˜Ç}—(Ò^èQØ_½[‰‹:G™œõ˜~Ó{)ûŽ}»æ^>‡BP„œxÞN)Òæf0¼øîÇ·3„&ž¡u HÊ~­2DÃpµê%¬Z@-gé­¶|w±@±1Åê1„`œ9¶Þ¥>Ù|ºª¤fŸ2éñ>ÞhÛKC1¾ ®]ä›'“»À›<<Å ­1™1$0imUæÏ5¿-p*?Nò „`ðfêÆz«Wvmù¦6³zL‰Àa‡(\¸KŒ²ÒK­»¦¶ŽÇh;øšl|ºCÏ飞 OY 9L¨+ ÷Ø* T¯d§é¶ê  û)™ÌÂ×ÿ+Ñ Ø9„…µì/¶M®•7•ÀbÏ¿·Ó••`K[L‘ù‹¶[ïðò­ „‚"Ô–‡Ów擈ڃNÎV„þ”~ˤnAL=Ÿfp·Þs‰t6â|AÇ—r»«h~f©W­ñ½­I[—Ö©B)rÒ‘xMD։󣨞zrÃxã¨ýïþÝ¿óhŽ7CS‚z8Üid÷Ž–Þ¦ËúÄIhÁÍm'±RêØ`ª•[N :­µf‹·¼”2㬯]U6käZäïþÝ¿‹ÞÏ9]“o“L™À‚lé››JÜG£(ý=ÝK=uÞ‰»>Æx4DZØLŒ ÏzÆâ¼»ÍNÐm5ö ·ý# V54ùá‚è×ýZóôä#º‰¥–1èÁ† lN™GO¢G_ÿ5OšŽÌ{@|Ô.–Õm2áV„Ì–h@íSQ­¢þä¢ãÜa^IVTø¢³¢F¢÷«µµdþʤíf6¤­CÞ«ËÜ=ìÌÜ$]švy‹·þ^5~šÝI¸2¨ŽŸÏäiN¶40P|õWõéW›Ï'¡à_Ùð0H fuÑÜ ˆ°Ð£‰…H$P ÁEBY_ÙËŽ2Ô„ÖÒL¨9Ƽ þxŸqºGúâY¯zk‡5¯ú0•Ç—õY6³eÑöÁ6®Èe­Zé¥s&¦•ia–=]½*ž`äf¸7iwVWé8-£å£G—?J ÞÜÏÀP` ˜>KF†ÌÍ4ͮ֡ÄÛÖT”pLËvü9FÅ¡,a9GìG©sTuäŸãegÂf%¡Qc´–OÎKå™Òkò=eŠ+[\&_ÔÚáØLºå£ô¾'ëE*“& µ™¿‚Òýù¾0•߉»^©®Áe£q}4>MÿjˆÎ☂dÉÄðºp#*iE´„dT¡1m¹ X”FÆao]e{îê#j†¤˜´úB'¤S+Õ2ÎjAªµ§cØn!&fˆŸÎ¤ÓÄxZĬŘi±<®µk‡¼WiÚß4t–ùc袽©Oãà„ãÝ÷9‡wA—ÇÑé‡ö Ë™Á0Ôø„&i.VÏäÓ­NCHYÃÉ 2 ±üG¤j®(SgŠN#¬n X ϺZbÝœ3ÛŠ6[ñÕùþÑ?ê‹Tÿöu®Ùv#¾/ïøó÷@’™S½ä4’6@¨ƒwä+2¸4G’¸XZA#(…¸Yzxñ‘[ÀH6o‘Wê`H@®Çvh²X¶aŠH€…ÙäU.®·&ÝZæuä€)Ñ´vu¬(¦ã&p†!IJ«[~Ž0BEê°¨ö˜¦ `9ÊÕnº÷²f?~ 2Ãd89[ÂGéÑ:6Óî3¡CTB<ÑóíZ!A¦Eœh³JhLªèýDk‘wž~Ûa‘ˆtÄú¥)Èã#ld†éšÔ*Û¦Û¹»%Æþî\¯Q‹:‘Ûn»& ‡í” &f”†Èö¸íNŠ{…ZÚTÑ+¥1J‡Iò‡ ƒ€ÇC`”j¦HÇ ÎLü<žÇ 6#þD‹Z$Ýœ¢ñ«|;4‰‹“¢ Xõý•å=-HضÿŠaX>D›m$[庛¶}Ƹ¢n™éF÷j’_†âi¤—ÐÉm¥°‰Òß÷ÊÙzúðN|™õÀ‹°²9oÍisÔ‘ÝmT[“fK‰‚ët*ÞB8¶ðp8IO„f”JÒÖ¼oí´+#‘ÏÊׂêî†Eäl? [s‹|0Å~y†w¤(ÕÀ¥p}bùivK«p*àEÌÉé÷0ÑžíG»48ï†ñŒx_áÅ&ù4öìQ“¯B~Ÿ¯¿>±=Æ Â@ÍHH¶wÑÀkÀøâkÙÓ´U’P—¸hù08 QÕp¥ž‚ÀPƒMð3®ZX#Œ‚%6É5®øëÖa{ƒ›È `½Q“ŽÝò"ó‹Ü9wüyz`Bc4&éÔŠê˜ ¿I[¥¾j;ÒÿéÚ…âRJ[.ú½z׫5kovØ–¢uO£×IÔdéb÷oû"¿/gUé-–qø(i6š‘ÅÒõ:/†ÊE€LHâdÃn«p.Û‹«c*ÍØØC˜]UMÛ˜<~hZ] ¦r‰bñ ŠÚÔžâé/æ‡_ï¨ €À™,7Jû‹p¦²Ü znÅåŒg|›Ft…€b¬½&šùyôÚì;}÷ÀÇÞOBÉÆ¤§ó6Œåµƒù™GŒA¬CVü’=Џ%žþ#ñl†½„‘/ˆ©Wä`4õE7ÿMÚ`½§ÐXCùÇ1ѰðÀ%¹Í1£ u¸ïbBþØ{þõ*$Ãx5R¯AøA`éÀkÊ/•»>ŸèS6»Î÷¥««ÅdÖA›ôìQd£±Pâ4äŒh±…|P0ĸjž×Õªá:Õž&f(Zs)ãy|ù›^=£ ±ÄêVŸ[Ʋ];œŒü¨ø÷ÀGêmÌã»é3mk6¬Îæ8-¿Ì<0Á¤$‡q#¥™í«4V¦}ô©Æz4x&?ƒÁjZï‰Å®W°WÎö·ÆcÀ˜¶bm':xÍžš&v^xadªÑÃñ$BN_È„¹÷ÒŒÓy˜o+‡©È7Å!>Û¶!> L” VŒïFÖ%Á1!åô®LSñèôC^~Š…yWùï@ ¤ ×~A[ÛW÷âÓŽH­h'Ãé]™œOf?hü¶Åê=-h ÅfÙëxf_h‘á*m>=¸'çòê\~ÃȬ‹§OœLë4²VA ¾À^Í,™ÅõØQÐͯ`cE£?)Ú¶þÍp7mÈyUK0ÞÑ‚ŸŸ3D`´yÇ)XŠpr8+-QsP‰f“&œüùÏ>?Þb¥zÓ˜\ ah9©¯ìáw$šF}îÒþúq½[[~§ïøx{àyـĉãìÀGÈÄ’Èãbۑߢ¿øk=;ÆQìÆÈìW©ºÔ@xøñôÅÍf < ° ìžçÊ1/Ùjoqâõ=Q˜ˆ8…â_¢N¡§"3u‰e ÓwJlkMñ~RâžÒ|éfú¬>®O3-û‹¯jïbkÏ)åDŒ¥ `.H€1}J¼eZ#ÕìGyp<×e%NÄ?F³¥§I#ÞPÃo¾º¹àb©¶={žËÅùz€~êSŸBÖä2ñ£Ö†9³eÿdª°]uþåY#›b†¦DjÅGYµäÃ`Ná¼`wáE0I6ÀáÆ ø•Dû›‹+Ppš|äbŒ„ÛôÁ^m½@U}»B,öâ÷Èåumð¾{àÍzàøˆ£Å½c²ŸÙ,½Ò3NþÃÁV8 9&j¯H;OøõÑòÎoI˜b–S¥‰¢K8Æ5eÙ„àÀCœ>ËÌöxD£ …`=SÁÚTÒ#%¥¾¤B}qóIP`så}Dßþv ˜{Dó¥žïC{M}F‹ÁÉFÁt&Ûmuizçö¶E&³%ɰlP¨†ëhÓgà*r=µ^ò¢£Ì’˜k¼ØÍð |4Ú“oýÖoÎKbÌã‚û S©лýïC'ñHºh#¤1•êÈ{ä7†›š S(ÎÜ¡Á' *ó¾ë³I`+ËÕ0šAìãHk³4ØvìÒ¿Sg´Ô÷„:p1Trťॲ¸U^,ú^XÝî®—*œ•ÎD±ÞÚÒxY…ºœVª³0µóá€ó­†õÒ*™mÐ+üë­Gé¾Q‚@‘‘vÂvl¥RŠ%Ÿo·¶KŽìCÔœ©r#{‡—ï@ ô¶ß÷}ß÷ÁØü¢òãÅ÷·ê„ŠŽ2@†)ZL{Te %ÖÂòQýØ8§J,÷ècOÙöÝn'"ÚM×^|¶þQ½ä@E:e·„†µ[ÞfÃëF3—ÅeÑW`mn•h2¢Þžüâ²PÇ7/G‚ãvañy?üü95£'¼¥Y±ZÒ¨-O$\:.ïÍq¦¿œåL")À’¥YQL7½x<æ¡X¢Ÿoæ,oj-Ÿ¶I@ióvwâî·8ZÄ‚³ÏÊ´ž¾rµ»Ýà\å¨udNí‚p-¡«©[´€Ã85»ˆ2ÆÖ<¡µ'â ìá‰ÖûŒünYG›ú’Áh Ûq“„m½imVÊœ`*à/„'S>ÇH Ú/§ÐGƒ¡Ð¶µ¬û—ßÁ_:íšìËà®™Ù0 Ú†Ôðó—¶b¡áÄ‹¬Åt$äÀ‹!Ùr†å@=ƒ6Í7>ªÔ ã¡Ë ­A«ØQ—JÉÑv&';¸ˆ¹žáAŒƒa±-æÅÂü´3üâ2lþeðáîWx?{DYyÄ`Ë0(°1,%@€§JA 9@Q3UR Hú¢@)¥Àª^ÐÊöôšú¬/àÊ Ú©þ«‡¢ñ›ÓÔ¦'‹Ñæ  tD¶Nƒ1Ñ#¥ü¼ég*_àn\ìÅ/ð9ð¬³É´Î™ÖÔr¯¶»ò4bâJÞg®ø½ÅÐk˜§›å‚ôÄŠ>ŸeýèBhDL<9Ö3uN"˧"„ É|ïF ,*‰N±B<Ù9M9ms¥§0‰ä³N÷é&O5%S‡dÅÒ Ã}ò%XóQbÈ!iÐnû‘ÝÍpÿHY"Ó@@¬~ V¸$²ÂyóÜ-a:Ð0#²ü6jÓk·Û1 ^?ÎL‰.ôÂV­ËB•¯k¬§œT -0ÇÔ íD I¸Ëá­:몯Â7™ïÚÄB6â®Éhì¦ñ-tê=Žû­½sîxƒX¥ACýâ‡_Ìæé/"^A[&¾ñHİY¹§ž-Ì·ÕÅ­¶=W1‘,S‘rÆ­Ú"$êÆ…X)ìßDQåXŒóO¦Èpþ"^,ÃoÐ{ïm¯™w"ï’çiωOcº3=>_êKR_†Çº·Pz”’õ—ðvêði ¦ž0D ÔQ… ‡_'n«ŒAÞÚgOU[lmQ"6nEžÀPk÷b4ì6¢fc¶Æ˜/Š‹·L¸öäþx{`öé™= Kf|IpVJyù1,¿ü€¢¬‚`RMœ0f¥2(J±ã Z#VN”=Wͧa ÆY8e›ß€CÇétL¹?.iÙc¦Ç®«Ö a7Jp·š=?ÃA—¹ÅM‡È°Ál¥6™0 8?­gÍL¦UØ¿½þJ&g!bòµ³`¡æºÍJI$Q§énˆ¯3©n’íu©·v÷Ý„´ m`Õ•3æ®ß¹Ý ¶Ã Ù¯ýÚ¯µà·-V} j¦&â-amÚÄÜ´Žá¶". ø;¿ó;ÒjH“ÊξJƒnGóTe.(ò O.‘G•Ã…¢pä½¹»&âFžNT³Í ü͆ x «L¥)bù¾çº6Òã”’g÷M˜®P÷ú}Û·}ÛÐS,Ér9Xø>[f=]20z}º±tQîæ‡-FïËŽ@˜zžu'îøXz`¤AÐ3ðJÕJéhê ÀUùýCŸ‰|D»Žú5Jý~Û¤A8L énvëÇÖΜs øž…|mk†Á? ùƒç}ø3kÁ儽‘KÍ Ã&l”ÄS³ȾŒB·¾]/ (Ç×hÍ|&Ýîü ñ—Í©Cé ÕbÇGk •ñRüÓÑùá?™ Hž¢x&8å¾¥™ÓÚÔ)@ÕaýI…? [û¹'fl—¶Ç {ޏˆm1/ÆÈØy•0WÅñ±Ú;çî7ë,E†bƒßdn¤Í0xð€=a?@P”@&Í£sª¥Rê­G¤] þõˆ#êÜ*4ÃTUÀs Øk»#ø‡Y†&iPœ=–)Ki sî3€¬Ä<¥À]U •‘^›1Õ‘m60»Š€7øGæ‰SðQÂJRWwš…e´NéMˆ·ÍäDŒp¬/Kô˜‚Eè(ØÏd>Jd­Q¹a°zF<¢ûùïF ôžceömžym[i15¦í.Æk@Jt„MS›¸Îe‚U¤Ïlïï‹Íˆ—x¡P·„ü¸‡Ÿ¾Ù»§Zíf€fÂÎ#|ìþCVÂk Bè-ðL²ëþán4s™t÷«¿ú«Y ÖèÀhÚÂ÷=ßó=CßaÎBšÔ€>žè`¼BÉ@ÿº™1[òÕÏ6æÌ‹®†‘NÎèݹ˛bTë’RY"èÜ˺„h‹›*ÝÏœ8-¿w|\=°JƒÒÒSS¹'ZQÀigÌÍÔ€Ž•ÏLÅä‚‹m­Íõ¥MU™%p‹ØÝþâ}޶{P6ïå¾âîH’ž;‚h¥r åªô~®:[Ÿ|ì—ôå-+zò)öí4‹:¼áÉ"_~d†Ást(mt³´õy©É!’¢aJµ¥*²íoþ8MæN³AÆ2€ï¶ub,*A³Yux•Óô·|¸Äž›‚f¾Ëïþîï"È4aQ»eÂé™;ñ1ö@Â[#Ó_0&NéàMcìi)„Ú=Ø¥h>e Pe„è.ÐÕHžS(ÊÒ"6ÂÓ~gc{ _ÂØ†8ÔÈ61N:ÏÌdXº dέ-‘÷ÞÏÚáeCé™ËŒ& oÑBæÝ5i£|´ë Ø."†õÎJ ŠkX¾?^­Ô×±`¿RD€Ó]]º.¯Ãõh·*ôôŽà¶òfqñšDk ñäøÐcÎLÄŸãÝ÷!ç „FâÃø­ÂÃE§$ÜS±ÿøÿ¸o àjõÚ ¦ô”-Ÿº1½;5@²\G޾·Q/3<OÁ/æ‚£4¨Gpd½Ëw¼6fÏ}îsÈ:|ï'ò'²¶QkÑÅ9©WOx•íX¿`%K÷6Ä £,ïHZ–ŒûÅÀý•_ù•­s ­Ìz‹Ï[6UÛHÖü$Zà7ñrÌ€˜çä°®ÿó¢W-–K¾ýph|ðç™#%×–Üé»^ìMDŸbèTACöËtŒx†å<‚î0o™a;æ1”Ëm ;Ž‹AÜâ0³mß’Ùϲš8òŒ Á Dø"ÕÿÇ6(Í0è,ꛤ:íü I˜ýš0y0¾Ù+§Aã…¸I)oVÛ—z)ÃI‡ZklãÍ'TÃl»ï QÕ ¥¼0€¡ºu|~0^?üɇ‰E—ùbÆÿ 96}å@ÂîÆPÒXlõê.z›~ÃÇr0ééà§Ñw~HÅyË„ÓÏwâcìL N0&ùaÆpPìÉC'“ÖÆ$µ^Wë­Bª[ó3 ©ê™Z€=ô–¡¼Û@Áеe3Š˜HÛ{°\N\*ùѬ ¬¦'_M™KpWóð¾4œm•\ë€Í8Úôr ¥”»ùéáäÍ”w|¨ð~ºÅ³ê‚ÀÑû Á„øpZ̬©šG ÉßÇÄòˆxÍ'æhC¿£`²R¾Ãôÿ‡5o¿¶û×;>ð3O§&Dß&|²”·rÚöÍÖ| UÌéîX½Í‰•ò4ÝÖÇX”~eÄ};GÇƳ‚Œx^<´ˆSoœEÒ£üaÄÚF»^Î1fs]5{A¯ßhe\še,ÒFóH€i\?qùI%DìݹeŽh¯ ù¾O¾…z_“÷üš/=° gg‚¡ì,„ä>J«uï^Uý½öJßžu_Þ=ðb¥AErS9Æ^rÜ_Ú¥Ô{ãÙS Î|¤­ë-œ˜Ñ­¢„ÇÝV>ÜA¡;Òà:¼‡ÑÊÜv>' Ž5f-X"×r!ËzÖûáË’à_þË©gtËq&|ò}õyòÿýØ=Yä+Ì3Ì é,¨3ÍÞã06ba"ÐÂèJ†~K S¬êݘ1C¶ 4c´Y ª¤§PẫU1ìö9Ú Ÿ2þF|_Þ=ðf=`pnc;0° äx? øÙ˜c oÏ-’‚:7o…b1ÈÆ@²)Õ¹ò­)ÛV·â>{ƒ@ '¦»•:JG¦ÐÑZ¶®Í³Öhªx&5GîMÅ}oúv g-0K ›ÕB¯XÓ€w>nMÏlÆëŒMų|BÁZ9‘ÁÓÝ]3·4Ñ£G'Œý6ú.g¿ Áç”à}È|—!=º®÷£z|¦/ìøžþ`ÝP{Z¶šö0Ì]úË–í°f_ù~ÌÜSƒ°l5€fâBDŸ”•“7޵ˆ*H+³šþ3©ÙX<õ—È™îäøTNÔ¨s6z—1­U9ÛãFP³éo¸¹g _«úÄ'>¡m«?:ÅLm6“Õ"ŸLÞòOË»>¨wôi6r¡ê4ÌS§t8¦J*F@[“$¯ƒŠøËi}ʾ{àÏÙ@!9ãMÀ·µ~KHð·ÁiÐæô‚™4gc»íR0«E¦ƒäùÇÍx.AÅ››Ð& zÊ(V%œ1Õn“­¸KþEö™|Å:…®ßhMëülV곕þQ:õ¼‰îÂQÿQÙ/û|+š'þg.j38G´[ÅBÄZf( ~,’NN HÀîM+KkD穸…¹Â^Ø-ØýtuZ€21ì ¿D+uKm.±ÿTu'îxãW„ý Ì`‡¼Òd<ØÄ:"m{PÔØ–3Ûk(•õ;Åâ´ ƒ †­ÒH{š‚m Üja+îÊåò³<…¹TŠóˆIXËz9™[¢ªÀÔò×Ô¦GÇÈâè8É}j‰3WÒ%ÜÖÔ‘]‡rMè´Ìƒúá´Ù+±EA‡ûmŸ  ¸üü?+Ò:~ ĺV(MéÓ¨`²Ý=½‹ë#7N˾µÌ`Í»ú­ •u›æi{øaÜxÆFŒ—¬7¾Aþ¨Š´üǨÇU³ Ú§.Ó™³…ýÅ(·qH6´ÎØ0#l«óºw±z­M—iŒþò/ÿòä·ÃLΚ(†ª6Ÿò¡¡³qÖŒíé¨Fƒ&MI'ËW$Ñ\Ö*;“uN|žÅr•*ÿøÿ8o:•ÏsK䮣£‘þ,wíBñÂÇ+J¶–oÞ—w¼q\ÀÁW½&lÀcœÀnlb¸úY‰Ëp¿=f*ÛÙ'™¿ØyÒÜá¨o's°‚[­LDAkƸROYêÌÛ)ôúÓçÏ×”×wA‡¶w>šT¯‹åÜ…&…§Ÿq3V%HæÎÈ6˜S#ޏè–Áò o”´´ÖCu ‹åÀ¦ž*D¹ös™­òØ–âfîþ­¿õ·ÛÓUÎÅ$0EîÄÝoЩÃfŒÉBUÉxÆ ê QEŽ08‹"pbM{þñ?þÇèƒa†ú8%‚m9ßrS$•ÕðV¹Õ-ugþhSdæÀš÷È)4E°†‡´t¶Àm!OÓ®‚¾S¹`Ü"ØI Ù?Wx¹Oºsà<,Î'ÿ˜è€Y ÿx+ë‹”LAX0Û¸\{ûXªEê5Ÿ†ÆY  VdÜV‚÷$ýg¦Ô·ß¦ñ?ä¢sñtjÚ6Â&u¤ê» ïVǩۿžØ—xtLJâáxZíHƒmvÿ‘ù‘S²ÉäÄâq” Õ ÉiÆÜD1s‡= šEŒI=JeÕfCðœMöÄ–xúÖú§xË* –¨ S\¢³ég±L"¥2;ä дb]oc´é¬YëîÇf"O9öLû!†yhF}L¤æ‘Î¥¡[u‹ÚüŽþ?Sϸ{à#õÀ5äg¼ àYVЖ4QÝÒŽnâÃù§(b’™0àݱg ^›cÙ@ªÙ_ûÜFýÁ/”:–È×ÝyŠú-É·Sè‹ßÚ™éÓ™›NýŲAþ}‚5Ð[Ù¯ÌK,@fèJX²åã‚Dµ rŒ€ ÂÝP-îrÐLݵ*ŒëÊdn]Ý¾ÄÆ€úGËى불ë©`«ö¾¼{àÉWfpc‚4à ãÀö”vkªM¥rܧPUáèåIdKÀ <“Ÿ*lÞâÄ‘Ç Ô·J fÒ bŠiÏšÀ¿µçbºÞ•Ml7ç/EFÊÍ¢[¶â.•­Oælt§›ùׂõL€üZ&Ì$£æµø¤çä3bBH›à0w/)›µˆ'×¾$íAÐKDž‹:ßù­w,¦·ÐMV ÑxO»ƒþ#Ÿ†õŸ¸–§r£š3‹Ycfy‚I÷ÒŽ…ÉYb”¥õkжÓùI†­L››òЯtap[ÇX™øàÅ)XB~ÓM{ªAÉs@?øaûãfÖN)LºX©y„¯š‚z£réØÏ&`ºÔ]#Cv*T(v2k!5pο\&ÕK99Ð7iò¯kC¶a€Ž†¿\õÜïøsöÀ‹œQ×$0Š^v‰XÌzl#_Ña5Dç¯k$˜6ºkHgkb0I«Uü‚;òl/gq—T¿¸†4’#ÍŸóõ¿ŠÏ&æ"ô›½uª™ˆ¬ ÷'x²'™hî ÿÖ0^ÿ¶FÈI8ìÖØIÒ?ŽHï.ž*>ÄZϤ1#–Ę4)Zè3eÍVJ‰“*Sø™õ-^œVâ;}÷À“=°:ù›CêàM›‡’% ãÑÎgî“cQX” ãZÝ* ÆŠË’">ØÑÐ œÚÀ¹¢eŽ ¹V. & "^]LW²èªÜjv®w¥ASÅù ¬|ÌØ6IUNï5ƒÍ›÷ìöD—yÆä`¹§€è€ú‘LN[‘üÓ»2‰)ªž48%ãi@¿„”1Õn"ÚÌŒw!†l¥ÞÉå;ÿèþhzŠ_DZ ¸#ç¨IvÂêÝÈékÇë O8"·%¦è m¬¿Õ/Ì‚Rà4b:¬·ÖVÚÇâj·£ž‰>'À`Ñž&fï´ù™iaˆïÄÝÏôÐ2ƒsÀ ]S>Øü48Á¡êº 2­ê'ã_~àÊbѨⴃaÊ‚dØÄ ¥Ú´!`[uc¨íò2]O‡žwÉ”ç)GkA4 <@f ì>§¸D"å©—~qV­ƒ nºðÝh’–Ïký§éœro—£™à¸Ï]À^=:“¦Ê £Šº§%æ7í$Œø"¾f¦¢ÊþJ‰\ÖÑûyÖ[ï[ú „ºcL[È·Þa5Nü£˜z­/n#dCÏ€î¬S¶GÌe_t¬Ûåã0AÒ`™Y ާ”LU¼S>üú_Ó®·ÖtjÛ–Â…b=­'U¬”­¬³¤!˰;âC#Ò㎨·f£3_…µÎÒ"é)K)Ò1ë^äΫ°{ i¢i©N×EY’Âf5ôç}Ú.,G¯V6?û‰Ëb[ÁÜÒ…‚ð”Ü$üÌq•ºÿÞ=ðÆ=ð$ìËßÀ;þ5lf Ëv›š $Só°ÛÄþV‰Uð/ý¥¿$1‹M c>¾XdÝ¢ô½ŽVõÆðå]a*̤?Ÿ¦Èìf˜]½Ø ˆ›”ˆÒÍê>©Ï_¬ó+™ÀwqˆYl²õIj¥™2~ê§~JþVºöu Ca+Ë\Vwµù4SƒÆl™^3¥±X°»§ „X¿È““ÃZäNß=pѶÍ8 ¤¹oI½h†‘n0ƒCUÕÙô- «ú©ÍGÀUö¨üªl5Wç(îCn@ZJ“1~˜²r±F»ÜiØd°°÷Õ¶‚¾ÄƒAAï5H(UÛF¼ýEÖŒ âfHÈm#›ËhT¸:xÏÝI€ßh@ñrF&¤Ú‡L¢Í{àýšyš.˜j ¹àž’•ÙÂÂÉ1·d¨$°¬8â:PÍ„‹:߇[ï^ \¦Ö`-ö£gA¶vý<©L¬÷Ýg|ä>T |Q.ˆ»•^g5Xmrx‹ §†Ô$£¹œüIŒ?´p½5¶ˆ…©K)r9ͪ9UÓGÓ±*‚¼%ãYw­|–½µ£”’ß¾á&š£úMÚt»,‡VW…­™e4@Éä>ÜïýÞï)¢ oœZ8g}z—T8ékcÎ úÄW·î-MLÝâÒgcy¨[¦KïÄÝoÖ€r1/P£î(3_˜púÿïÿ½1o¨·+ØXÍ´nM"õÕ6ŒŸ^#¿keí?lûÒË5¡Zö¨yßì}¿2K§t)My[²)b›½¯ñÇÖ]ièLË¿}ü©¸ÖoÄ÷åó=`†®Ãþ˜æ{æ`Ü—gP¬„­0FKë)ŽK`OLš)v.vÅV¿…fõ/Æ_LCs'îx¾º†0c46±À(»Ÿ6¶¹©9Á/°8 ¨ÐW –¡n]aæqKÌü‚j#ªœ ¡µö4ˆFŽZADph\ßÔªä¹àeùg±…çàîAí×RkÚ»äzÐ=ÊK+1À\w™É±üzkM'€â“ ¢w¤¶Ù{• “„Ÿñ5Ëe=‘ø°bþyÊ$ú¬« £ ШÂËPNðïåÃMþû™x÷!7è¾þ2nê¦Ç uc«}tj°q}-‹G»ÌÿÆßø/ö~Ÿ ãE‰ëÂdúíA…¨Zí´ÎßþíßΪ™$vG‡{Ô9ïO>ë1/“ÝÆƒ¤P×áÈ„+ì€Ì…Å0`ªÏjðvršL.ë²`UϨçׇR #nÅûÓiwK¾{ ¨›·4@¨S&ð3:, 4Ü–rUŸzq€J>på²ø@W›œÁ°é£}øl›|  N <`o½U:g{1Ä»E€”Š[ § ØYm-[ùy®1o†rÀ-ˆ«¿Gí"îÀ¶À³œžJÖø­*P|ÍÔó7ô"D3T¬¦¦µÈš&&¨Ó$“ÖÉü°S1ÄÔâN™Ìm›TϘ‚†å˜å,UýûvùîB=2²Ißq™¿tßÇví¸äÓS"ôx x-[à3C=2g¯uZ®|3¾Ëýÿößþ[ƒ¥~“qˆ ÊÓ4ÎTiq2#ôhš›õ)[zìãëù„M—éD¶_ÿõ_oùOñÚ7¿žla¥ÔŸˆ¹Dw¬ÜAØ^Çws¯ú¦_I×OÒf}á _Pm¶‘¶‚üÏÿù?-çžn+Tá³pN;¡SDy¨ùËÏs͘¹€Ó«­ÎÖ«­òØì;çîëòR@¶Åm€B‘`,lY³3G¤Nbd2Î6¿–Ê×Ù4ÿ!»|ð'Š­ãúÉO~’ MvÈ$ZAׯÄqÛ¯?åõÝŽ“2™˜pLÝ+qÊ©Äí½Yomi^¼â#c|8Q}0SÑ@o¥îËÔEÎh©ò±šÿc ïiabÀ•M|ŽÏþóØŠ¡C>FÓŠËžŽ1c@¬:‡¹Åy¯åeŽÏ€x:ÌaÒ0u|¤÷º‰ïX{t™1 &e0r›OÂ]Á!Ð(;°2a ð ˆR'@¥ªtö.n;¤Æ ^=«3ê”ñP==½g ¥‚xvè%Š.MPW‚cÄ­O€ÞãÝÉÉÔ <'TãßSl\œ˜õ<Ãj×Û»À[ ,¬Åòïç¹ðx4Á!ÔJ”8á:Ç‚rZ±%ˆNéô»^œ˜sJÿ^e¾an¾õZ'¢ÀO§ò^}gy@|Üœª÷("/ zËÀ= ƒ‹Þo …GÒ -ÅQTÜæTO×Âñ¢®NšT,LùÌëî|¸ëÑæ“¶#PÛg?ûÙ‹†¹Õ~4uÜ2áé¼3ŸéÐ%tdˆ9€Sx¿`O(8…*Uõg'¢¤*e­G=·àU¾`¤íù“@IDAT…£ŽÕ@»”¦²cû3ñÎKzè8Æö¼·0×V09eƆSQs-’ 4¸;!WiìŒ@Sg:h ToFEwS°žÆ‰Ú30‚ñ½`¿=èx™%Ö‡èñ¡ð­ bÅFOôÐK"ÇnùsIx©…ëT¶(â÷*ñ^„>aݧ—ý0Ìê x쯌o« =b^‹4|¦û)•,d‹Îä9[tÏͧuy^m#[/ë„us™9!#“áb%NV$†¬™[Ú»$ù÷ùôÒ…@±•}‡—ï…@èý¿ã;¾£Žó÷Ÿý³öbÄ<ãUÈÑyÂWn›ñò{4°^¬“çXm`‹ßlb[ÙïþîïF9;î(`:\ÅVsVïO}êS[qn-aD.Ýù‚þƒð6ší’"¼¶ñH>õlîœ@›"ÌÐáìQ fÜ슬á £fü³>q¶)»%ÈÛzK:wpËê„-Õ(©dðp§o÷ó0¹4ÁÍÙ8ˆ[§ébsFЖ\·ünMüÖç÷å“=0Ò`Ø‘h÷¨ £ÂüÒ¬Ö-ÒVèmÓàÔ0Ñ, ÑUÌs9 |jÚñAP¤I©*k]@§ò;ñQ{€Ã’NN–¸P7‹ЩãÌ„N#.Ô¤Ž É”tAöQÛÿHÿOÿé?õÉF¥ÈK­4ˆl=mßÁœógàøÛ¢ÿgï|ŸlíÆÙRˆ…³r¤*U ³S aöÒ§‚™.Ümê¸eµcïôGêf*`þuò[ ü4Ì Š %~˜|R”¨rX. +å¶5) MAj—QˆD“€äÈ·¹Afë«Ð„k[ H©TçnDgƒ\®ÁzY=ЂVX=Wïî(AeÏ›§ 8Ýþ {z ]‘@ø|;›TØ^Ç#Íš“ä¹yÛ%&ì„çÓš‹Öâ§é>¢øpNiÞ·Ì÷E ÌS¹¾ ÁEgåEã²ðb¹ÿZZÄÆÝJåœMU€µ¶[ë¥Jrø4R¯¥A¥ràÌ'Ø‘,q²foœvßÊ27$x'¯9¦GXl£éh¶ìT±n§IÖÔ~-ÕI©1?{Åz«U3Áok›˜QAáUˆ-:ŽÉî'~â'|DžWõvÖ¿êáZñå¿Îù–où–>:+ª¦în¡®Ö¶Ýé»õÀHƒÀ\‘ë·í¯SpŽžOKBŸ7Ãov+Ûy¥FaÔˆí/=Tn?.“ ×»ÒV²õD„ª*š¥»×ZÆií8ö¡:;pâܶ§z¥§ÌNw~ô)E–ïdk©5ÝÄÕÁKêèD´ßé‹ ~ã/²YìFÌ%‹Â7…aÖ‰º´T9Õ“-¥whð v öÜmKa&ý;Hšy!¬xÇÖ¯­m#–©£¡uË„kçÜéç{€i1Ì õœ¥<ÀÏÀÍ-y]“@&OŸòu¨’!W·IÐKU`Ø#­a „Õ­­Í!%°‡Ö[`¡ÚV© t £“¹GvèôZª48ª¬ßÍëH&§¶5K×~†WâS P«®ïüÜw^tRãCªæmð~}ЖÖÛÙ Û-E+"F•4_@6ÊãeÍ®8G‚÷0ç}ñOjÝW?ÌÖw´ (!-N>­Ó”¸cÛˆ[?NŠ¢ÄlípkXœ.¹Ö)Ú„§3SÃäd•èˆ6 ¸¨‚/c›Î­Ü³ֿ¥“ñ‚›414£šÄ1 ih‹o»\£~úQô¬ÿú_ÿk ƒ~:G¦YwnI4}(2*™zLuQš°Ñýt)cˆÆ[°R۱ÑO‘YÚ[°åøýW×Üé»N{`•¥y…KàØTÓ†1¨T¨7wíÈ»#àR\ËšL,*Rcdˆm?_ü±œÚµ<®&ó¾SÜ]·üÍQ êm¹Å}[ÛîËç{  UÍÛÜõ¯õ}±Ú)º¹Ÿ¤Äu ÁţɊa©6 \ÇE¸¨ç¾¥2$qaŠŽÌŽAr I,l FœµþÅ›íD™™}î*‚UqöÏ;AõX¡òëÁéøã÷DSÁª| ¯ãÓm2¹?ÜݵÆýØøxà~ZƒÜŠ@#©Á 2õ  .@•n¥Î_iÌo*þÎZÛrIä¦ÍI5À^,«y¼c¥-UC,ÑÉÀd¾¦k˜™•¬ðAS0u½µ¥óŸüvrYUW™6oeAë¢FéR;M |#Û.¥AzËÄ*Qo”œáÓlù]Byˆ4´?õî@ÜÓ"2‰0u‹/E´Y'™GEÞ‡ü÷E ÔÍ× ôÍäØS¬Õy 5¾mþ¹0ëe+À„Çzä(ù¢X RQÐÊœRN¦+ñµð*T2§x‚nØ jÛ‰S­Rö§6ðÅøFã]Ô9õ¯ #¯¥+Žâ%µÞt{vÓˆˆn<ù%ŠÀ>ó¼r|ÂD.¤’>fÉ*~cÄà`ãÖºQÐSjŒüÕ5·°@¡a+ñ<}´¿óoþM™Í&>þÙiÅj°Úê ¿õ—©êNÜ=pÑGÇïßXb¾[K™%Ú#+ÓÅþŸÿó‚Œy(BZ3›„FòñÁ¸ü³¿aÙvCÑ€Ž4ØÒn©s–Q»zš%Ú?¬Ô¦Ä]Ûy§¯{€ª8/—žñÞ¿(U<óö¸NH4O,®‹ây…˜„$ßwìQEî[§=Ð~ø åâŒA0Ëÿ…1ñÈÊeÓù¿þ×ÿûüÊªÒ ƒ])Ú}˜,ÄŒÓ-@Ø?+¥ aÀCµµeè8¥œ¾Îy÷ÀiXAfļ ð?F`ËD¶)â¼Þª0¥FËÊ’ôŠ#¬F¶–6°-@®ðjKöÚBþ€ÔvÇõ¬žÞ!5x}Êú²9íM5摘Ü‚¸€î8²Àí¤gcuÉÑœÚu,Y>ƒÜæíG€yš—·HŸ–Èß\~†2ã1ar¶„ùa",êyí<Ý·µ–Zƒ!_{·®¥Þyú=Û»ß@ç–ó¢HÝÞ3ô‚|uÚ¡éZ¾îë¾îx×IüæoþfþÊ9pžzmeÑ×TÅ'"ÓFãr¢èª<Ã6âÿÂ_ø ª¢k9§)J!ïl÷Ù4Ž”6ÝÖ s {­R ímýÚ¯ýZĹ5ëilYí?äɃßìªrk<9iGðƒü5ÄSaˆeRM­6Ì`3…•[ýaîÂ%·çS‘¦E‰~.í¯m÷ß»N{àtk÷ÅoĶ!•€ç˜ªÈ-f¡É´$Ö-¿F£„1‡Ê1’W?w f ÉLLNî@X>ëG“€Æx"&jÂQÛ,ɧ/ug^ô@[R{Ó î¹(âVžð¬|¤¿Ì}OŠå¦Í¬Äù>m!þ®Ÿ{ß]{€¹5ŽÀMq™å^¢L,±UYØLFo˜_)¡=ËÍXïñ©¯É¢2ü;”ÜP-Ó>b¾süñÀPËb®¦“C†—ø´6<šXÖ×¹Ówœö@xF Ä@àG‘6¹EÒRì°:dÛ-%Ê®.ñƪœ`˜åiD£H°M‡¦}€ê/ÈEàçX˜“B£írÚŸô…xé²¾l§)‚¦E©VO wLì¹–ƒÃ“€±µ²P‘&•fMãßY:ÁïõÖiº½ ½ÊƒëŠ×=ÑÀ;>’f#&bõ%Ñc«a»$¼4§!ö;òÙèߟË÷H Ô)ùÿÔ‰ÛþÎ­Ë ú ZEÇ̉¸1䩊 –öŃF~×34§ KZši*Æë]@¡æøq_ kUžãÙ÷Ì-+Vk$ŸiO \þàþài“æ¿Ùz»’Ål9¦†¥@ØÐiªt¸nDŸ!Ñ¢TM ¤j­ª4.ô4ÍD£Uu×ÖŽ¾ZQmæÑ©)ÍnÑÐuéíuÍ^™‡kÖ¿w¼Ø ­%¡¸2TŒy„²f#2€‹Dß:= Ó(Å×ô¦V;f “Cã–иZš]Þ>-´RÜš©fŒ|û+’3Û³T4ðœ·Ñó/¾ÚMpÚ0‡?¥µ½ÐìnÅé§û^Ž›ïÄy—£´Þˆ—âŒûp¬k“Â/ýÒ/iîœgz Æ “Êb,3ûäÃxZ]Y/· ¸R¾/£Éa¿uŒ"˜ gJ¯¹æ000ä Ób(7uðÅôòÌ›Þ4_É=ŸyÃo9-”_•»éÐç0ÅÚcÅbAbM¾•K–Ñl<ÚÚ.›ç —ö$Ó_n_qà—³p9oŸâØ‚zx9O_਻ )€ÚÖ>5‹ßJÖö )×Li`¸W[æf½ÔÈ8üöÄõÖ1ž( Po·Žü›é…PèÌ~ºÑêŸ89™aõŒß‹ÒÄ”zï—@ØÃõã©A¯.ÓÝƒÞøw]›+’†` 98Ò åd íh?= s>X2•vÒpLæiÂfwdÙëù8Ú¾÷{¿Ípuõx©—õCÂjƒlÛq±Ñê·£À‰v3´ÕÚi|,xn8Ø8ÜŽçª5³¸•@æf?1‹ W9ó™šô›¿ù›*tÐM—8°HM.“xçkÊYcÕÔŒûïÝzà®1Š B4•[ü€EµáˆìäÉþRÖæˆØpZÛqõ²1kŒ((á,AÀ¨=~ÒX¾å™Ž¶U“/Åḭ́‡äÑKÝù×=ž[‡›:¨´®é×»fž™m$Šé·\¤HðH7ÇO¸™ù¢È}ë´ -Ó:26lò‰O|ïŒ 2k|ߨ>ÇžãZ‚²$¶‹æõèÿðþƒ[ÎU2”ÃÊP3ˆ !LœœiÒØÚ|=ÉlÄ÷åÝÓEOip6íxäÕ€P·:Lêpšªp×j9½Pü‰”²à­¡ŠÛÔvé°Œäjÿâ;€0£ˆq: `²&ù»‚ç!hodÆ“a ¸n˜œŽF €îœHÜšH˜çÖi"£¢Ú’fOidðh¬¶Cäƒú2­ò«Lx~Æäè¥Úü¯_³·žú×D&Gd~Ïë+×ÞUúý9ú׉ý]÷›Nq IY`u„‹0Ms÷Q¢9Á¨d O1vWiPñä–5æÒVçýÐ)˜•ìE5z+У@C)&sàœgu8)^#P:}…'ÀДv¦½<º¸ôŽÚ`QL)»nÿe¸wK íõçQ3ª—ô:”7j(,/tËͽÙdž2sÖ&¬æ"‹Ø+ðÆ I§s›|¿1¿téï16Ï<èNÜ=°öÀ‹@­lvnÉa´ÖB–Í3ä"„.LEÒ8ǶvPŒì7”îi©¢ ½¦f´¦¦ÓéP»–le¡HcTü"ÆÕúŽwzë¼@ü|"þú_ÿëÁ‹—³±yÝ®ób©RÞ™Ã%GááÉzn²–Œ0¢6Á,­eE—i`’[^pV™•Æ68LÒ%ÝýüÏÿ|áa™&,j-©CV¦A!õM 9 ÈŽ_çÅ©æXäι{˜ÙÆÛHÿ„Ç,4R›ÞÑNk×%tmÚö9¿ws‘"&ErC_3c>i¼[­›• U¨1àƒk@ÊÍ€¥Žî ±dû/ñ -…ƒ´«''ÕGÛã4^ÄÊ[‘G3´¿AñGdÅ/LÒÅe•u«iá”ë§`‰À<ðÐ B ™@+%±eýè×NׂïCúýõHä:ôxf^‹î:ÎqZÃg¶l~ö³ŸU*…_ä¼DŽÒ :“UY®óu¡€K2»_Û>^˜˜­FÚ¶Î?ýÓ?-“üçÿüŸg”X,e®;ÜÂá¹%Э¦8?=ÙÔ l¿VÇ©ÖB+?U+ž_…ð°—U¹­•@‘rÐ ~55d‘¹2üj´4mÍžéܯÛÇŒ·ÛÁUÃü¥ÇšjïÄÝ=ð D‹e]&–¶l–Uàr}J«oËíJIöûþïÿþ± º¥`›7„uÉ(‘ͼø½…ÛMóR¤ †¬f ÄXS¬Ï½ÓÏôm6[óp>ºãéðLñhÒ£û c‰z¾¬éz3\VÁpQ’ô”ò~¬ž2׿™ìqª®a¶®Z|EL-‚°æ@êJõ÷™ g¥¿ÓwèD¾“O" äîÂRµG¹šãÀª2­éÒÖ` $ËãHK’h”æ«X¦± ìM 0ᆡ~—sk)7ËL÷ æ*R"k—¾uvŠ€¬Iw@ìd&7º“³%šN5`Þn­—Ùý,£Þ1‹«…`%˜t{µ’º'SbdB"A@N>;„•ì4]ØKB‡»§ªÁ›gŸ[sð©»PÇiUïmæ{'ðGWúY!VØ”;µ‘=‚G Œvè^w1¬,c—O¤;9Ý=bÕñèmÿk•Ç™€÷äÄ$ì{m—j£V…9ªRòŸA€«»+j6ÌAý°N nAŸí·šãóÕ´ìè©aĶ:}JŽ$.Û<½ßV6ˆé oì~©µ#—ê®AÏÓ`MíÕ2¿˜•ôƒ–»ãØÓ›…ÈØû§ÿâÝ­y÷åÝ[<ÎD -ªíŒ. °/÷0ióÀl1· ˜fÙ$2Ë›ü‘åƒÖ¤B†ZžsQ“™–JA‘²±ªÁ/_V³ÆfKßÞî¾|Ô-Ãæ–4ƒ\€Q>ÊïXd+‹Ÿï2ÂÆ#úcþ·û·+ȈqR¢ÓhdwÎuà¸qŸëfÁ2…˜ÇGEªX7Än u:ßBƒ­†%åôÛäCãdð.–¯~ .¿€¼‚×_,ýÁSÇñì«g¦ë·¾ï~¥õ@‡:Ìв¾€=.A ]Ñ]Ð@2øÛ4ë®).m#šÑ­Zc×J ËHË$°Ù€ºyúÖùŠçJ n·º¬,` ^¦[Y=W±ª×Ñøij¥4X>ÎeËIdÅ>§O‘ ×HýwÀ)eÎt€·»@xrĵHq঻Ö[`†S 7]¢AÊÜ•ì˜ÎY}\ð<·¬3,­,½Î££ Ž•¿'9ï@8#£MÜ2›wüá’0V÷Éo@_›é"N÷Ÿ("zÒ©4ˆRÑl¦^!úüc&ÎE{sõœïJån&›çÆ¥CV¢'òضǽÅ)=ÄF–v¼nýØý˜^R6UG¾¬ÇÃvQ2Êן›ÈWý-ŠG/êüm’Ÿõ˜žA_ûg*›­/?9÷Fy\Å|klB±çûŠ`âï×6ÿãüU{ÿ½{àQ\Ã2ó5ÞÙœBMåÀ%–Ñ”˜î“Ÿüäl훡XB¨‰ø×%Ü™U°[šGŒ[×íùð ù­ßú-4óÉ¥g…­ë$öè5ïü­|÷$óRëý3›ÖJéÅLæ~¾‘ËQ ¬”i›ÔTù+þûÿï/èï[z w‰ÅáòÃ82õm=,k ÅÞ@àWþ©X–­ñ)×PÌ®Ư :T¼úåÂ:¢fõ›FL&•½ž|ÖvÞé»ôHÓXš¿Éo.!pH"hdœwä`Àip&X…&ˆ•]Qµm‰oû4`V8Ð gEÐN `Þñ‹Œ¸xº¡ ˜T0®2ƒ©`gõ¢î¥Çjq[˜ ÅŠ´²G²ÉÉöÐR 6?²×eó˜ áL\l2Ÿª$Úzæ‰AÖõVià¿ëžÑÏfœ$nü?öî¥Õºg½ëþöeØô IÇFÚñ€`ÃNz&D7‚&F#ˆ‡D‰Ú E7$¦b‰‚`:Bð@66TÏ÷ZkÎÆZ5jTÕ¨Q£êªßu¬Ý¦}Kâí°'åDzÊé÷DtíÕ—óÅ1„%¶§uHrß<¸µåØjÜçÚî¢YÆ]¸ÁZhoØ‚óµk›SŽk,çú\ž´)ç¹³l–ôæ !tàÚ¦œâ·àíšV¸lXÍNj¾úÚ R~ÒÛ}¼iŠðÿã<êA}¢©aá'™Ð­Â°ì£h¸ò-Î6¯XŒæ ®ò‘µO<¤…d‡®ýyÊÑ×åkޏd¢®1n¦¥qOª¢‹+cÉç?à®9I"˜Jp” “˜êyºV°ÎT@ (‰4“ÑÝɾý¦ÌVAÞU´f½Å®òJÎäìz؆$»÷æO¶@ž‡0"¼¶™n}²ú.#~¦˜a„ Ÿ#`Þ-¼ÒF€‚"8åkv!ÔÛÈd}“¥YþQ3bü²F‘2™·la¶-­;=޶ßÂ÷½Hˆ¬D™>’´—S(ü:2©ˆ° /ˆ RsK‚vO^é×ì8v°§i”eü HŠEXÚ2€¨bV…3A,@Ë„ þ%^œà(àÙjU`w´k&_õE]rDw-é¢'–僚–Œ®&ë þQKµº E´™ƒvæÙmI™RP9•©=t,nv4_WÙå‡ê ÝÀ`ù™ò=BÑU„Ú¾5¸Qý~ÜNÇâ^ÝÓ˜—çÐh7A‚¢cö-«¼ÛüÓ_"Cxð ì=Œ/Q.õîuÍwHu½µsph‰XÌæOZÕ! )¦5"üC~·‡þ×rµ°5HŸ¾Ÿ%¦S~}¼äA¢ƒÚ‡v™#-³4RÉÁQÆeÜïßý»—à!Õ¨]mŠeZ°uúÝ*p²ÏQâw‹ÌÈD¯ÂŒ¢Œ^‹ažq³½Z¯ù“?ù“ñ±)*“*YYgµ†ifRÚ8º­Šš€Q­†Có”Wâ5×8 èÆa¯µKXæ ‘i¸¸2.cóiÓÝ*Aë—is2€ØlìïÁ Ö¥n¥Ÿ÷8Á'§«Ì]º;nHE”ë.{•)ÿJ|æ8Þ7²ñ‹J—÷—Oÿ™u+–«3U0Ç›r$Ò oçœÏi³à~¬$èš’Aس>§â«Ì1¡F‹"û‰àìD—‘ƒa;ju°°[[wW™ØÂÜCègL£òŒ¯¢ àxÛ_›©»?ñ?Q;HG81 9T©Apx:%¶F:^Âãë¼.‹è¦ØS€k ’ ¾fºXœ]eíV)bZ@è• s ñãX(Óî˜'ßÏl?`@X¾¿Wë÷<’ÆH›§’Ö‹vQé+ _;ï¡çßÝ“5*{µvL¯y_fÛ¬Aîçe¢Oœ¶ÄIû Žãq]b4è¹8á¶Ìdvæíf;çÆ$ÓX ö{‹›Â—ÈXßÒ_ÿO±K’1`'lÉP‚ïzœ1¸Ë6ؼµ•·ë­r|—ïÔ>3R&¡l‚¯nÜM³·žMÖ„c“Vkü%ö#Žô¸¨ ]]¦pâ‘î’ÍlɴÚ¼£§Oy‰TØdK9¶j¿÷÷þ^UšÍ#mšêy˸»=9³˜²ÔsãA¬át)°¯žCAÿQ=_‡^±Mz¢Ëpô÷á~ýýoÛÝM^‰×4à Ú…Å ÿ&áýýÿâ_$öã´Pè³WÆ,ãzÑ êñk)Í<œ6oYÁzRa³Wá½ö-“±n˜³›&Vw@çëË~þ¤Ý%B¶ëCˆ„®æó«›¢ƒø»4ÜRàó[£™Ì tcãßR Ÿÿ¬÷]2ëP4c9‰ ®6ñØ L}Œƒ’~¾Â+†- ŒVÌ_:‡ÑÒÇéÉ´$Ýl^Ýb$ß'$ÅÇE^6ÍQ=11rôâ ¯óºÜ#J`¦¢øé28d0ë"Dò§ÌDA)€ ¬’ bZqŒ WÓ›Ù}r`Ûd–í´Ð]oßÍÚ8Lý˜‹ÖÊ–“pæ©hß,äòõ(›±˜§?ßõr'î,D>†ÙÓ÷êNØ rOg&Q@½IQQkSæš@üu,¦ƒp«“¬–¯àëøpúvmGö$Ü«ÁùmIñm­/0óKd Ó~hpqƒ h¶ˆ×€BÝõ™3˜¤³Nöµj·ŸÄv’ÎÁ Ï9Üš'ýÈüˆŽÍj)„'_'²“v|4™ô!—<2ž®Ÿ¿ð ¿àq€l —$ @™—/—Ÿû¹ŸkÍêÌÝ| m·›-mR†$>†íоr˜ñtä BX«p[¬ÕÛ€däóÍo~SI?8nIh0ª$RTbú'5·ÓóWâ£Àpƒ£è3s’Öë[ßj­áýŽ£çRb …çþá?ü‡Í[yùvk6—V""ðh§©?ý§ÿô.“Œ6–²ŒÜQá™á%n#Ýí¼ÒÇ䇉tµ’{”¼½d è¿­ÕWSàj1uÛZ™?ó3?ã˶MôY‡“yRëuë»jµU3¢“Â$üuëºg >:Ï“IØ"T‹ÆáÃQÂÌ’µ«MÛ«èùõ“T`„y‘‰Ô 8‡U¢ôâ /ûºœlšZ³ ø±…²ìc¨ 8© D©P…@,™éÆA¯V‡÷r™<¶˜¬ºÀ¼BžŽžÓò9ÂDô8ëbŸ-ÑÚÌòk^ìª P:·® €Öfªˆ{½[`l{Á ‚j•ÚÁéÑyD¨ ȵd$­ ßèE®D㨘¶S€5ÐCO|ÄvôÀ8pít9Æ·šòC|n‹}á™_(CÈœ£aíïóX´)Á€¼ëX;¯³‰E®`ÚÙi4øÜš¹Fâë²'Aýg¦ˆCûƒð–ÓE–3wåg°-»F2Hwþ5=Ê6°+ëÐx;b’*’ gW€úÈI±ž‚KçÞÄ›ýâ/þbÒ¦# K˜4x؈sÀ™ ×|Mu…ç²"Ð1ïRß2ˆw‰<ÉQ+ŽÄ®¹ÊlW&IªÛ¯¿¯8F`¸AD66‰¦U³uD¶±@¿2YÅ8ó°Õ!4ÿ–°¥e cúùµúJÛHšŸÅŸ8zÒe%í¯»ÌrÔíÖè4âddŽÄVzÛæ+óшÆ>6{)`ÉtK§•~ï÷~¯O@Þ| d&WìQ®ù@^N;ŒL!<â û×Z¯œëLh™v+Ã8"ÈÑþ›óoŽŠŠùõ ?ˆ)Éa¾bÈÅp›rÆÓ³-†z+Ù_gŒt1‰±D^6V¶ö‘ Äj)óâ gä_‰c¢`#€ü´ÑŠ$0©pR]ŽK€Ê ÌH-[÷â!À&Œ_Kwg6^ áF0Øs×fþUņ('g¨ØTËpöµž{•”éÛÈPÜx¼¸ËÌǼKÖà×™¶ÉûZZÅ1ͳuõÿ ¢ghÿœ#…â1Xƒlý0 X†ÝÒ˜ >Ô§G±±hPÒ sx—_(C˜òª‘õ×úy2š&·24¶ÛæÓ&ɰßûäZ°¢¬I¿.Ÿ´9{¼Å ‘<*YÈM‹Süb­“lÕv•VÚÌÌ]zíVé‘5ó<ÚÍl1Lb/à¸Sª6³Je#§0¡K–u\x]piŸCjH[ZC˜Ó¦»6ã¢úg—Ðvvð]õ'qTÕóoŒˆ¤Zi™º~ul^ä•x@#°¹AiÆ¢f ÓÁ=>l?ÚìÝüÛá3ØF›Ñhd ßÛã#Lj§@”ûnéd—¹]AÉÜ´|»?÷çþÜˆŠ†Pd\À ˜AZݨ}v¶EOA:ÒÌ 5/ÊSr}õ>-â‹'¼~…WŽèГj¬H‚@ÍvùyâKM¼ì“A)s¬ª Õ`Ì”Üá^Ü Èu„ä*Ÿ3 g ‹³ÝÍYiaàbÜ&:Fz„G×Èñ׊¨«@)hzÜË ‘Z@ p÷*»y¢/™„AV`{Z¾M€ë¹/yГæ(BÆL¾=³à¥ÔÝlgfºXŒ)yMŒªf–G¦ì׺_TÎʦÚöm†d±ÝAüËù/+üC?ôCåon0À”Ïùz6ÃP¾(v}Ú1™G¢}"È#©À­NœpÜ)<«+»¦GUˆsR>DG†4ÉΓV=Žnâ+ÖÚáèâkDíª§j(Sä7°8˜{x!üݾ8¬razâxU°ØGI¿Z-—éLBð.Ì£AioŽ×y]~ä¸rƒFƒr&ΪØBM¶ô˜™¸Ä¼òË0ÌL‹ô«5±+ÀóÿFšíÄXk¼ b‰ U×ßù4K† "± ¶Ó]è3ò•ÌH¼ÂSñ•øädt`èg‡H„¨0`GlçkS$»mðÏãOÅèž*‡¥É@‡Û“yô½ºä„ú‰ fR½>ñ±ÏIœ2˜ö‘n­^Ý ¦{`•ôS€©vòû9·mžXôyp3 ežS–…<Э¦üM  d#0ù"0ýÿñ?.Á‰8ÈÑy“íû–XMO^‰>àMŒÖˆ·‚@cB "`©Y7 ’¸a"è„«ÖŒmÒ°-™æÐ½vÀšujS  `®Àá4Òéež˜?Ôa}:Åbqëhš¾qîN¢s5€[kÐõ\ w/d%Ÿ{T¥Mi—ÏGi¿&r×liƒñøß娇W¹òWž[¡ÛXŒ]ýH0ª²‡ô(ü%_þ¬ù¢ziKNhçKô#·{›e$]\P;„À_j™× ‘_Á#±‡­"ûIûzqí™,Ïp9’²v|çäï„n˜(φ‚å5óVÊHuÔn»ºôþèz [‚Îm³<ʸ,xw2Qî »@›®—ÊcÐÞ6wCB?ÿó?/'qÔÅ`¦PWT+S%Ê A;“Ù93ºgküßÿûËç”ùㇶXÈ8IO#6·œfš}%>ò<X½ˆZ–pÇ,*<òW¢òí"Äùöªö­™oXÇŽ˜·OgêIå8ÖÚ­µkÌèøÿ¡bG%æëÔ p³ZºU¬‡ŠÅæÂô9àN³¯é²/›Æ†-Pü<‚«OF)I9{õ¹­¥X›Î#"¬VaɈ±òjc\dŽ‘âG]ëêßü›ó¶ýW棰 š´kì!Ÿ¢TËGzZh-wiçri÷9egÏ ÉYÚàXì#y´Á…DkÊ_Ÿã‡\(“THæ( %h†‹]!Ÿ‡HçbOð‚'$kºýJ|Ø#‚™oàP3@jdr´±ŠlPeá¢ðF}¡S`läòªw`Øfn·&ãh ¨d¢ P°v¯ý]òŒ F>Ú³B}ˆp¶iî&¶庑nÐw €Ätzèü£кA¶§âm"&ÖKðªó ýmÉ”®×s¹aõWLVBÝb–>q•ĘŒÑAýd⻡þm¾ÌÌ߀5_Zÿ ãf|G›„?ê$a|ëÊbˆ¤1¸e •Œ¥ÙJášÏ³ÖNß9c#ª¹}´…×s Tf§¹-¹ãê*PðCK}L%™xY›ku ÆvåÀ“øÏGQv ¬VGÜÆò¾òݬò÷ÿ÷»eÀ ÝIC¥ùfè†ÇI[óÓ¥Îò’),ÕdŽ5ùlØÃ$³¨©Xˆœn¹Ä8¯+‘RÄsoKV·è!V–¾6û&r~ƒ¤~iÝ%ý KµInâÛ'ŒÓhÞØn¹Áª7G“0rÁeäЋ»z+Ogð@ßvÌÜ£X—¹d*®‘—²„þñ?þÇS8<=Ãä”àÓŽ•ƒSë|ˆÅQx l‘bEÀ×UÏ=î&ö¶qæaÌZºm5†l¾§lof%çÑ©:-†d<Ü~F!“–²5ïÑ„^y{JûµCK|¦žøJ¼ãxªl Ü!&ž‡™#Æ#êA6—,á³›~¤’‰ú¾šj_ÿð›¬s1²¬ÄĨ3¤V¥âc`#qH¾‹-qYk•OÁˆ+Ø—€l΄•%I|=È«Uìõ÷“#SŸx!i‡TÏ ÿ–Î0þ×ýÜÉ~˜hà“: ¨â«©¾Efʤ7öÙòoÿí¿Už° 2hºdéz4ûº|>4!-ÿÂ_ø x”Žù5H`c¼}îÚ©ð´éc%Úv:™ ŒËŸ×¶{„âHÊéVmú‹,`&ÑŠ‰vÅ"2HMópJJ€žÓ ‰'äk{¥?Ú9M›=.Á! ¨ü®LÜZ»(e”Àª8­X›(5(R±ý”Õ4ºáÂZJ¦Ê>DœŠ™ç=úz,»»Ý:jÍWKèŸæ ƒy0õv%z¸]œl¯m»`°Ö:¤<ž–¯ ï’g .ÜE¾¸““!è. ÆgTØÛÁwùÄL£Ûß·Jc0C¹¸–™œìJn<íÏMW¦â˜ørBƒ%Š@“2y¿tsèvûÆÊ<çÕmyAÆsr#3š¬öÙ™x.0wë¬B›®?öìèdR‹Ó™0©,Ë"O1Ÿ,ïY=%cË1ž­òÂ?Úwɵ·éëФë]Æ æ¸ùM™ôŠöE/k_œÀ6…aÛ_s° +­U7ªY½'èÊÆ=Ðlx¢¬þ¶~»éÉ+ñÁGàœ¢rÎ4#bÉsÖˆsiÛn-f—if‡&·ñ3Ä,’³ìag ¨–y>™€ ÚŶ"i?w'–L1ä´‹Ð/‰US1#y›6_‰'#€¶ËÆžÅûaÒ6mÊ%rÇClçk67?_ðÊ+>yh·T‰ÄM´dùÅÄB·ÕÖ4•¥V–Ìè^PûÌè)ùJ<Œh¬®PUÑ›r1ÁÏkß,ºL‹k·æL6tÀbŸ¸=hˆ«…ŸrÕû|¼ó§šeëqŒ²_•‰ž 5ÆRÉhŽ|Ÿ›.q넱ÝÛWúŽ@tl6&“2Ùž$ìˆ6uªÅ%ZÃØdŠ¥ÌÌÍÊ1óÍRT+‘ÇU×TÓ ?³?J‡ú¢±·1c •Jâ(”°ºM4‘e/è} úz¨õ ÇÙ>±þÓ%+Zy0;0l?ÝO™>{Š—EÆû˜o—?œ- Ø‘íÀT?Ãzô¡=Ú%±$ øyn lË.ð¶Ò_4CH4Ò(y؆t{ í¯¶ »¤4ƒ_Žé³yÜ­ ‹”D S¾-#bœ|˲@íNŠ“iJÂ’)s$ŠxFÏ sT »Ä£cûPÑ@#–W§H¼eùf?cB½›-Ý;jÿXœî’ÈÊOþÃâÿʯüŠ|aÜ”ÉåÊnj¬C‡ïõ틤YH@ZGö9D¿X‹êÛÈksÿ©__á•ó¾Gà ¤¬P*A¡oÎ4—ÚAqYšops2Áâ¬&V}¦kj–$—Hf¬vžƒ1™Óx¨GèIÅÜõc¬˜¶ßneN3Ýq^9ÏýÐWz@¦¼\¼b³_ºn´öû¬'ÓJÙôRée»ýçi³kÒ”’E5ð5éˆn+Äc–úiFp‚Ù_nk½2HìbeÁ n%·MØš/zËÊ¢³ô¾^‚'zi¶@Ùªd7šw˜‘™ ºb,ÄZÔµï¯Ï:‘N]"8ÁÍÜõ‘…J*È”™ÐG¿’²ãe_—pš33µJEx`—–"}à“©JÉ1PÙ…)?œIv¤HM[aæšEÜÛfl£EíJ`Þä—ëpxÜr™{¡&öõnìâx<¨-u ƒ²ª[“3‰D´Àp¦7‡Ò~Š•èõÁl`;KWð{þÔ=ÒçÓ1ïh…æ÷˜eøT¿&²,í22…ÛÜ•I\…D|¦ÀÛJœ$õKë}憻ŸErfй'om´‹?‘X¯Ta--’ îG­¼È0Žü ¸é":©6'º[p9.êÀa…boÎÝÀ«»í³pO ÉúOÿiJ¦èóèÉ)‘ãS“Ø=îòˆÆUÞ#~FÃK*¿}” Š.šgû“?ù“Âv+LpëÞ¢õ)§a]DA¾ùÍoÊt”g±¥­çÌ®ÚDǬôÖôüèÿëòÝÀ¡• …ƒòFÇaÄt×Mi"@SˆÛC*3­ŸÂ-φ.é,›ûAÒœHf/í„’ÝJwtÄk•¬ý膵¶ÕPY¶Œ®CI~=z zöã^écÆ-Ó^>a?0~›4MêÜÄ…¬z|Yö{‰„·¿ÍþüD$]S”Bè•ß@±ÛFâ[ ÃÀZ8öíz’ܾãÿß™¤þƹÅÅ†Íæ2òùÌÆ,´ä8-=%.)Є¡Ý… ÷òT;·Ì Ú±B§)Ü&c„ÙžêOkÝ…éuž82 oÅüUG¾‚6í¿v’ª›$Í®¦P”FA>°2IÐhQ•áJÈåÜ :¦9ÆþÖßú[òÁ3ó<n‚‘ªˆfŽAã5L(¨®Ÿô4¥µÖÌ/¯–%ÎFã`jr´ n_à°ö¨ëÒ[Ï3H _ËLNBœHº=½Gï0•LÙ´OÅ€½ÝÜk¢r:  p»^6ì±X ŒÆQÆë÷¿ůþÜ2ÏG­/ùò$©_Z_í謃$øàîNúHI&(ÙmÞ ¶ »ð¤[wL—õÐÁ S D¥ÌÀv~‚LKzÔ‰ÿù?ÿgϵVw±k:°¶&õ¹ˆ›Ê”Tø£&qÁ?wá„â;fŒ¥˜•yQïØåÚ9‹I?¯vÞÄé°È;Ay—V#ˆN¶ã­ù,©Z0êZ„1V“ñŒŒjòWíhd _[Häî‰Tw‡_é6žp_bÆ ªí¦ €eÅ™]QimcH|ú7‹Î¬·X”ïg§<$ˆMHNh‰`m*O„YA[§…œ¶5ZDóQzDrG¬éä'¬iM;¡d£ÏÙ~¦žÈ—Û¹Um·}è«x{F‰­`ß‚»Kòæ'Î!S듉-‹¼*'êA‡ø@®>9Æ õ·LìQ÷u9#ðŪi;.GF¹ê% ês+6'AìÒÝêZ†sk~˜)èXÝñw˜2jˆ¼Å>B%ƒhQ#Ge žPÈ—êHYoD^€ÄMã¯ÄÇP§ùMkREá+—À’™/aBR¾ÁH&32˜]b@« ®ÀHºÃ"fѶC^çêjs»×6þIC’ idïq „3ƒ”æöVKŸÒo†„¨L9‹)HëËÔ²iÙgC¥6<$ô¢GÌ´Æ3>ózë·Éüh¨ù,鱨ÂðèÑàLàÃð;sîXÓ¾ég,e‚ÞG{åÏä.âs3×)8žÑö žo´ø„QéØ/žI<Ïzž©ü³ü¨Š ¢}Joq&mˆüUyå_G £PcXpoj„¬ÝäL€øŒÍäø][+Øžeº-cù·Qì q¸-vUzè¾LÕB”2:0 ŲÚò,-Ox;Â-3[¶¯'ïoü “‚LÍÛ&X\)ÐR ôj’çg2»k§s»ÆU.ÆõÕϨ€X`aPœÏ@¶Q‚”A>eÆsøôÄÛŲº¾±" +N³G"¶Vù´ ÇÝ.jìÓ‚ß­_Tºb(p ý¶ ßù¥ùÜþ¶¼O™­Ðæð„˜ŽÊ#òÉ•to”=·M½¡Ì{’úE½ÀØO÷~)Í|˜ìÍËáÄâm¶Øþö]”73jÎý¶L™íë¹qgOZyeêr3ÈÅöÚ+ÐÙEž¸Ïuë9GªÒî¢} &º&},?m'OÎAê ΋:x4xmñçs\Tâi3‰”ç:^y2KÄf3áÝrkd•í¨š(œÁ@è9 ª–ëRQø\î¸|<ôuù¡FÀ¶qpt©Ù\5´Éö¸UpTÈiÐJ´ç’4uÜ!æœÁÌÁ Åã'Ýù:Ø·š€p)ÑCi.å—ù—»e×÷ÒTíâÛ¯ãhíu9#`‹-LP™ h ã˜>…köÿÿŸd¿=^›æÌÕžêxºË–T TÅãòj‘q­ûÊi¬ú>¢åSNBR™à,º”ð2[æ ˆHü¨Lß+‚p 0µM¶v«oû/~2—Bh5·yÄ Éò"Ô)Ð!wÓò+ñG ùàL‰A£`R)Í62¢X´Â™ æÖD3.n_PMác`A»”â}îf‡f8 8uHUtIaÀ2%ähÕ:ù‰àcššÈÚÖ Ä¦²Kº)3‰9°Á£ÇUjî–(Œ¢?òc&ò¢LåÈ ®ÅöeæµÞ e–aØéŒ†‹V£¡.¦#ž0uhÛßâhìFÞ\ú!IýrÞÁm¸SaKÃXnÜ ñÆþ´ÅGù¤<ÞŠ!Tqã8šQÆ?iüIl?Áϲej I¼}Ö+óüoíέ; '9#Ð<%G‘øhYá»úÏb¾I¦â0ß&XfÚÚß)C&f)2խľ²–HË-äîe;z;Â$3¶=¤ƒF`ÒHÀ§õP pe×·\¢N¹Ž{Ô> l;³¨Ë*jaÂ*“2 Dš_®ù¡ •&k —œr²q}d ¸f;±Ìvn::" Û%A壀K ZOìG·¬tà¼øàúµØÎɾy‰Ü·Žt\ÛU£‘‘ÖÃ;&êÕ·Ù§’ í¼­Ë7ÀÐ>ƒ¡Ÿ_ÛÆÁ *)à»2ÃÖß~ s½=FÉO²ŽE%êhŠ“µ ò×fs>)ÆN:ÁYÎçÁëHZãE®O)§¨wh«ªtÖÛå7YQë6]?ewŽõzÙüMf·±¶: ¡Ç“…*¶RÂøÀS³Geimlã ó3¨4_P‰™f_‰6c)j&˜{(ìp°É K1™ó£Gа'Û­-Úí”¶f3¿Q«ãž®evN'¬˜Ÿñƒ@IDAT£iŸ»Ã£¶¦&_òÓ¾%gˆ9Imõÿ‰Îj?è]¦‘h¬“$½àg‚ò}³M²¨ø~bƒô¼5+b•9–£Ýg„ñOªg.e|Ï÷|bÉ5uëÖò¤|kÖç¥Æ¡• 4ÏÎkéµüã»n‡«ˆŽ&ƒÐö·dι1?*,ß®ÚÝÓÛò¤­tv}™´!VÍ@äkdIˆÛ˜6Ðó¼xÂ'ƒüîoÅY5…æo„"°Ô—»ZV± N;¶…MD¡Á+CØÜ æ™–3ÂíS`¡k* Xt/MÃØ…)PØB»ØÙÖ ˆNSG|M©n½·öe0Þ2Ìî T¾Æ×È1ò6ô 5›òÓÆ‘6òy´RŽÍ3JX€´&»?;ôùÀáÃî¸q}DÛÄ®þFÓoƒ!ÜÎ$³„œIrõ¡°NõHæGÔC_HÏç¬£Š”{bêDÎ$D’±@ZÞó€ D…Vi}C 4õ(.­ðI¸·1÷n°tÌ'œ”ÌrZ«Vo:tO^8^‡ú¤Ç˜Ól扑¶tÜs ˆŠ|’ÑGæ´0b0ä#³„[†ðÖ}žþJ¼ãn0+—#¨³Ïì¢)Y’ðŸ96‹Q¢ßm܈cè’Ü·ÑÎáæG™¹ÄæÆ–f2KLL>nƒyçÇf÷È̦mÛëGá×åu’#˜AóL3Œósův’s›*•5ÚQÑ« ÷úÐ#Çîˆ yмf5®'Û¶â¨Ø%¨3à/…¡Ì‚ânÿoùà™´­kñBŠÜ¥dîc?*([’·#–ôS1[¼Í÷¶Le" HÄm™#È'°€°#\æqÄ@²" ˆØX°gŹ{ñ„Ǩ~¨Ëø®æÏüÉQôGþO%”c4P@è‰ÛÂ2 ­™¼Å÷ l-€y ¨8î¸)úF«(Ƭ"¹±›{ÔTʰ3xˤͧ,ú ÇE–'`°7òD´Ñ ’½#ú¬ÖàzìñÔ„õ˜»~á+O±IÄæþµŒàÏ­Èž+q¥Û˜ŽŽCìÛ5æÒ˜”ÝÂMŸ°æË| ›k£?¸Pâ*K¨ó ~â'~âú.ÖRԜȄƒl¤üùBÐÏP¦omûµñd{†¥©ßžž5ª¶<2Ôö¦–îµýÉîI™œØ«\qgHàýrÇßîR ™T,ØPÑ·™Wų±З|j¸/Î×î[ä÷Ï8§K'ÎͶÒxÆîî•Fª*çÿ¥e× È+ñ¶F`¸AÀÈn¤šã÷þÏÿÉ ÁÖÈà*É_slˆo—ýe—²ס 5D.ìLIoíº§6 §küµ(“ú½çN•3Ïá×Èw»ç¾ÇX]/3`¬Œ]w‹–^ ÇkùÉaË„bC5™”÷ Dk˜ÌO&þëý¯ípü2ÐBrg–>j­ÝÇ ÉÇ^É–•®øQ•Wþ1c_0È: ÇFk¸ôrü¥Ü;ªwÉ{ÐÝb³=°&ƒ@,Rû¤zA˶iGùá ûÖ%0)`uÜ´DL4Ó‰C¾;DOÉOxûÕ>B&ØÓTñ78d:•cæÌ] ªÑ(‚KòA½ºs¦þ$O;5hÙ±t«`VI}~üÇ\ ÀŠ%D-.™Ó¼Ô˜?id2%ÚÏ ·nT¹Ÿ ì’¯ôíd8~b\í±¼­E—ØYÌc›0Å:Öܶý(‚ô”,!ôKXG<¡v‡£@Rv{n[X¤Y=7«k!§ìÁ!¹;Ú]ÎÄËÃðh 0aó<*æ®_‹ñÖ(=Œò*š·V»)ð‘…¹Å-:œŽI¤T´¨aƒIŒ Ò1Ô€]C3 Aû3æÏÈlJ\ ànÿ•~÷#ö|5w¿þ‡fæPSƒLñ@Ô Å¬ˆ ß”À4ŒM‰¢ŒÊÞv>h'Ìö’OÕxÖd[jf#kV ÉÝŽ4À9, zÜÝ—q­ÚÜ÷]é\Ÿ@âÉ•[Ñã&‰ûÌHÞ\™Ñ4;‰„¶™#!òU0W+ƒÂ:NàÆi¡Äðºª÷;@òQþ ]¾†ÐIPßü_ÿÏnøv í»¦,à¸]<‰2¡¶6ŠÆ©n>[ q4XH¨d×0GGᢠ$ _d$›nŒ˜§Â×5P~‡RØ(óz‡²îx;æÖä­ähçКÿþßÿû®ûþÑ?Ò8^$)JTÄÑìÔGT£ÊöQ>æz£1ñ¦2LÕ¬¢çY™Ìeê<±_ÿ`¿ùŠ6å_‰2·`¨ã.ÑeÇôB€T¶U¦L’u À®iŸsB¤¿¸aæØíLcº@2¸Mí Þ–)ª[ð±sätcJÚ?" ÐbnI´ÓÔ±P %âÙ*ú]啞2 (0vAh$â–7›Š(eª$Y˜ü‘wsf¸ˆ£À\R3ܘÐês·„G4'=ô¸u\fûDÍ5ùYX‘_LÎ+ñdýš~4à‘—nß±œ|_ßÿF‹Ñ¼Γ>èœ_–pW7ãL`Òú`Fû·ÚŒémàYÔ#ýÑË? sdJŽI5È:fˆ»%ƒóˆWâÝ@ì\sxÿm#K–DÍ8WûùbË[SFb$ÐïD”ŸaZñ@>M@`Ñ\ÀÂÝÐ8T쀯Ë Ï$¿.¨ÝNjI½²W§î[íˆ'jx.bq¹®Qý÷ƒ¤ ¾‰x¢MÉdoŒ50±ÊHª×šÅ8@Ýx$ÂË×}¸OT{tø‹½|3 aæÎûH3ò¹Ùô3ÑI2²úµÒ¶b—ý†F1–1i–nBAZòÛgMfš}; !qSs¶)shÉË’Þ†a X L§ti#Ñ©;‰±[»z+“ý§F®ÔloÈE’ŒÆ»ª’ *ƒœyDâðLƉ&?i·ÆÇÀ=g•)c´CÆY„&jê¤oç6M•Wâ#ŒÀ#Då’Ø²U3âRSe6Π›œŒUbeâ°Z™pØUo#'½ ýȆBlVÙuÀ5XÜ0±%q=}ŠëÂꞀskÔƒM{ýt8áè#5Õ?x¾8oĹÀó?œ÷ïþ]êb§}<*3€º>qeÿ«Ð¦VG³+â¡}ÜÚ—CÇüÕFcnèÆm½]÷•6¸»‘¿ü”ÌVVL{'UÀb´$¥è2ª´¥2ý•ÎÙþPS´Õ"9‘Å“3Vz„h+ÿèý£Ó[‰ŒíæC²Òm—;@Å#bøša’íiÅË62ÃNs~«à2Z™ødcGºíœÓÛ¶ÑDûÚXxÆe©ÈÀÂcÌGv«Wg]ÏwÈ™£—E·Êhà-ëñ8{é‘ÿTf«À Ýê_¤ëSÊ  ò†½•ì*1r¨ñdbb¿íA3Ú)N0Sl'lèûÕžÀ»ä[L¿†ðOü‰?qý ‡•ã|€‡)šú–ŸX±¨ð”)ѬÝVåRšÄˆx:âb6ø£‘.HkX•„‚cþ´ 3wÖ,È;>*Ä“ÔLæì^âGÐdN_1·Á*âå –+DNÎThcôeÂ3—Ò;ìºZе ‰Ü­Á ›ÂE_t^g¤­Òœ0qîÔ^Þîâï;¾ƒ3[ö—}¥ŸÀsÔALMŒã/¶*Œ…šg®ŒÁÚí¡í#}ŸnH†©Þ¶LÎJü–=È@k±šmÀ9*3)iêvÀi=œ¥lYþho¦â˜b¯ÄídÛ ë7ìÊŒÕå•,O s4]âdÞ&ò©F…nϹ"äŽ ö¤ºmJfªK^1cfr[2€²ÚÄêWG†Ûê¯Ì„)ÖQ~¼ùzäØcðí’³;.ÌòÜv_T¾ Œ¨×nU¨¹mœœ7û±¡ÈñïQ€XÍNõ¸DZ¾­¿^Í_6çÉCËAöIÅÏIâk¼ãØgk·³D1)okƒ9³Å)ÜÀ­^7¥›T Ù ½ÍÛšíô%»9ðkwkÏÚ¶]¹“µm®r>G üvºGº;ðU÷@YKUÅü†Ý´ñr`Ì0|«ÒÖ”1ydµQ—ÀrÏÑ]‚ëªhöŠí&ŠüÕš¿–að›à8ç¯G1ÉR*6øó{2 ¾éÄ›aÿÉ?ù'3ú“ u±?Cá¤X”62³Iì2¥›yGr‘¦û(‚;¿áªñ;lAê‘äÕˆ¥ÂíEþT&àȦ¨•£LL×­P„€9v1ÕǦ5ÞZÕý?úæ’\Ó­ö­mF•ÇNK…̵åÁ¯•6gÑüÌÏüÌT/Ï8@O4*Õ㱕¤E¬é~cŠàò¥9¹~ ÷šóú€S ,›!­»ÒøÀü ]’îçGÁ¦t6•†«p”÷è‘›&Жfç·3]ãÊ0ÅQÞÓYVþwþÎß鹘—-·ñ «{õàÈAZ_ ¡nºw>xš€6Ѹ;fä3°$Bz28™ê™·ôÿ¨Ø!± ΡK$žË¸}¤cGÝãÒãš“:pÜÚ—ñ&p™^Í™] Ån…»î+mÒrXhà í©•j"hÛNänc•²E:C—þd;AˆPª<;ÞÅE”I;‘‹åV¼ÌÝ[Rã®YQ¬µÂáDú‹{DXvr‡hÔæÂ8}%Þßl-SâΙ! Ói¥ª^??Å2¼*>J{åP™Î ÖBž€\ÕÓŒA¾ÂùDT}«»g¨g#¾rVítí‰àè­ÕCÀ”­Aà6ªîŒƒèiDç¡“P&­¾÷…ÄÜ=i;-íœËôÿPo&NºÕÓ` ŠgƒYÀ2dm> Õý8+Ó1kØ“]ìí¦ß CHh£2ߠİ"Ç7¨p‰·Eõ.Ù>Au>™\Ø“¯ˆ 1™­Ÿ?ô‡þÐäÜ&z¨h¶ým™yÅen.ncå »¯¡nȤS†xå|m«ì§ É™rÌgÛØI7h[¡§1¹|ÂQéÜëIa‘’Vu¡5,ÑäIÑ/Òt¿¬ÃÕã;;ZÚ..pïd¶1ü›­±Öîá+ýþFàèŠLò1¬jÎ4?ýµD—-Fÿ+™¦÷¶÷h¬Û³ú›¡³Ã53MæÉ,‘ðõˆ+cþ'ÅØ†™Óü³öÏ&–Ì0¢u²S‚}cM]·s†<ŒdŽÎ¼.cœO[8 O¨%˜òˆ°´>"ªòD…xŒm :d'¯iw‘Ó0PÂÓì(ÿäÒC#hY@Ü–DÏ{)ÖG^jЕ̭ ½­ûÊ4ìB@éxSÁÿ·gâëVceaÑehƒÝ=öë6V~VñcQæð'fl‹šÅ—U\m$B2¶!Œò~ØE„¢ÝÙ%¢ç²n<"×G¼rÞÍ?IöM†EQÉ|–߆81±@,™~¶¶¯Á×W‘c€Y\V¹IC€·°h;`zïT‹3†©5²Ý纷ٚ2i$™4 ”^Í%ÚÇÀU³HbLWá'Zȇ÷ôMºèÀ /²ó¯iËÜ[„¨»[87ÈaÇüoCø¯-ÈÁ d•Ëð衽²ÇíŸÂï;†Ð7˂їØ<º¯~ûIdûfÃÜPYëªõk¿ökISx÷îoœDÇŒ¹}V™í¯ýµžø<®`ò Ö *³ï´üWÿê_ÕN‡CL¦D>¸„Ùè…r9¬œ3uÄÊVnì³ìqSÔwF#f8½JrÖÂŒHÇ#[$%™äµÈI§ÞìºGõ6jˆŽžïN¾ÒïináN¬`~ƒÍ‡ÌöJS>¶M¢ÎlcR뙟¿ú«¿z›_‚y§À¹{’9Ê·×Ú§"P{ÖhéÕJoÉ 1ô¹ Hê§2^+èÒZî_éV„ 5ªrôáuÙàÛQ†>4*]"FÚèÝ:H«dïôÈZéÑðþÉ?ù'5‹ÔôÄÀñ‡æ?ªò(?»}Ý8 Ò.Ÿ%H6^-LJCáwÉWú:Ù|ú^~¶˜¾agiÖÀvk*¶=ÞV\ñ´'òYÅâŽbÊ"4QÔHy¦)‰Û36MõÇÝcW‘&VÀí+#íè7ê§¿6tä"‹¤2ÀØÂ["y4þº|g#ÍÜ(L™'9¨Q²X2ƒ[&p˜ Ë`!œaU#lF@]Tè0¼l>kóª ÏhŒì¼¾Ãp´žƒ Ì;4=¾NìëAèÝУi{ôÕÆm·“Ö]1Ðzçé¹mqºË˜¼# (?:œÈÑB—Ø„¨´'bnËÌæ®L‚K‰Ãðä¶â[É|K ¡1 ûû÷‹¿ø‹×áN¦hÌ)œ×2å­5®6ŒTpfüɵoõãÚÑÏòÓmãgÉ+£Üo•n¯†jýüÏÿ¼F¶Œü‹lME‚R†dhTxRšˆ=YÈ1M±pWEðH¦µúýßÿýã+œé«* ;Ë.T¦e`¬’ß$ž)QÈ=̦h'È”Å69dŽm~N\çÉz(BŸ[ìþÑýQùm~ÈB¶7Ýýª—_k'’éxïUÚ0RÐHLk¯Äu²NŒÅ¹Ž*媱E‹na·ï›é·vׯ¯9Ê'SóuÂëćùZë6GSÑØ'ÝÈĻཚ”Δ 4¹õ_¸}Ö‡Ílõ·ØžÙ¡Œ[„ñœÁ±<Û»ƒ˜ xTa{{´Íˆ/ŸêHÁ´°HG81™iÏù<>GÒŠïû¾ïC¾2z–±—}ó—~é—¶„õ@FHröÙÜy¥ßådÙØÄðw$À)JØVk n^&’Z&XŒx¤e?ÿŠ>Ôņy{$sóÓˆî[9Ä ˜ŒG/§@–bQNp4¡ÿa ß.|=¶ÜõDÐ7ëÖ+/:O‘ÈlçkXýÛnA¾2¹ó]Q€Ÿ½ÛXåi êÅŽKÌB~»O}ð¬ã½Lwø ’ú&^& OŸd$¯Ò e^¡ý /w+œ’ãJ Ù‘H§d{Òí‚‚ÎÙã=ΞÑú9TpÓˆD²Ï¶ŠÛ@äMqÖ)S‹#îk9´cyœu^º‘hUãëÆØ 2Bݹ‘H†6yÚLçç8Š¿ô—þ’[ÆÇ2«L¡,¹´+‘ƒŒœ¬Œ )*¥¯¿G|õ¼ò+ñÖGààV°]Ä*lÄé. n¶;æQÆô+AéÞ®#“ æNkm î@2GùøfìD›(tá.™8£õR,™¹Û|öFq¶ðœ[)’Ñ ¯ÞÓÂ+G2Œ¨VAebž ìÜð27bu T~ ÏUsG•}I¾PÀ`yuë%²Ë?ICð}åÃÿ\Ý.ör/’|-{Efvë%,x2¶ÝJ?ÐÆ”ЗåÖ¬;ûÙÑeâü)íwÒ™“¥U@ ®ʹ˜‰¥GjU)ŸYML¬³à®zBÛ+r!ÜhzdЋ $ Eú–Oø|„ßÇÝ`^$âø 8úR¼k·rðI%nµK`Å}Éšf@Ú>^Â(r•ÑÈ`Ž€hÜa–ã©p!7ê pä´$GÞ7Ÿ·©E¦·0/ïðˆN÷R`ÚÊÄØ·±;Èíöù±â6>)$?y–xD}LÄt¦DÌqŒ1 J>²A=꾕ËßDRßD§c½¶@ÅWÙŠà@ƒ­“O*£òŠ@c÷1©à:ÐlC‘ô–yô%•y‚IÁ–ý!³©YŠxsN™däþv¼Ì!´H‘bj0NŒõy-¤¯ÿñu°)è£AÍ6§¯6´ñ“FY™·³åk¶Ã1؆A ½ø^ÆWY^CpKI G¢>Ü—3?Ŧ>Oy%Þßlnt†âÌòŦîìUü³ÝWÀ,›LÏ KŒÿL4A"ƒçG£:qe°=Ô.{-œA&–Ì”i&{) ïe2“¤¶Odcƒ‰Õ—¤FìH iéslö)ÍH‘|ޱ s ÆÛ ósœ÷Ž—H%¦¢Ï‡âÝ╣ʓË[WFNãÍ`8GV“•NLæeáªÀ„éý¤ñ×­‰XÖjòÕÆõ·-FαLt™–áDY܃ @ç× úQf“FF¬bëš“¼ØÎn…Å׊m|ꪅ;8ô×s‘ìkó1Lå"hã‹'<†ôý]šºQ9ß}ÿš9Á'ó<è\S- Ky,G 0“£dž›qJ”ï.rwŒ!r* 4-=tKÇ@G·RÈ«U$ ýDÊÜ2iwk™ãéó@V jö0R­ Лª@ îvv„ö ¤iy-‚ÙWgכּÉ‹ùVYðÞîG\Ó©m1a‰-ìÃbèÏü*€¹6ò¦sÞCøƒ?øƒ}•¶Õsj|l›¹ÈÇ´)5·}'³¶F¬Çš=*ÆÄsévªötßH¼Æ­„ÜOÇœ}îÐàífcÛ²ŠN³gZÇ€Æî:Ó9‡6@›ÖÓ«®˜‡rBÀW·øB& Ûš”–Æ+çk‘ûoai"R$¦úœPŠ•B&s‚7ð&l÷>^Ž)Â~ÙWúÀpƒÞxà JXqƒÍ•ù{ïï5ý˜ˆ$†ԬĄO‚‹ìq+Z¯¯dv1ép¼õ¯ÿõ¿®¼‡+«ò-"ˆð*!ªó½WêÁæòÓJXhÇs_—3oÑÌè"dˆq 4‹„$rI¬«J›w²iç·š7ÂÈ šù„àNã5¨cº§“ºZÿu^4Ëb†y©?õ§þT+I|³å»¿û»ñÀŸó”Y¦À0F¯P.ؤ†˜véwAKµHm ï1„}­kÕ·ôs˜ô˜IÊœ®fî>I„=<ÂLPŒÃUöo=W>4l2¤„tÆ¡“IÊÜš ýɃ^·Þîdvn2ôËÆ$È|¥Š†\å¿SàÜvÉJ&õ Ú„k62à\v§W†0Æ%ì¼%qسrF•bæ1jœz |îa·ÆwEß:ßÛÜev: ¤vŠ˜ºoMº>XrÖ(6°}X·6hÀùT<(|èÈ¿ePwùŽ$°x±Qò¡0ÝjýŽz3²«¿ƒôÛccÌL¦&zk õ1˜,÷Á@L—xzÕÇ{>:¼X;ùš?ú¨VKß=ù²ÿÁðLÔò cîÛv-lžyܽGVmtäÍÞÈT¾†XˆÓùØ3ž óĬ &‚“Ñð8aR6è‡;ªKL€=HĪënщ·]6ÈÞT™(×·¿ýmŸ@>”Ùž*ó¦œìÕšµÀ~Ê+ýžF`¸Á-ù†u0Næð–žW+j:ûŠãÍs5¾‘èñs@3÷‰¨óÊ>9˜P£GXËI1aµk;P˜R±ëÝ™ÉÞ 6ÕÃŒå'‰”ø$_zmöƒäPçÆê§ÀIúkß4tOË,A,•N”oph÷™ã–k3åŒïý¿v#ì¡:»¢{étX·u^9* —’|!+ûQøéÖ¦ñ3_çÝˤÍ|Ø‹kÞZ¾ŸÝ'¥Ääg`ÖvÆ6aòD`Ú<|R¦*© ”G;.Ñ„HžDa·Ü+êyõ?1‡XŽÈ㶤P üd÷uà•ÿå@suæC0 jòkƒRX\XJš0Þ+ ëøMð¬*Êl†PáL0Üݲ~ù‰,LZlv«M9¯ —~ॖAÍÐòÕDÓìõ]Y3®Ù'|ÖÈü}{ko OþNäÈ”`§W@i§X¼€=zXÙ¹µ!ROÔù¤‘eÌ‚.eolöFyBúõÙßa=6/p´öF/ßCh cB|˜àBß v„ð¤9sÏÇH®ðÈ!U1À_­¿ßöÛTl·žº×Ä¡ž¶ñ«e«ØÚöœ}ÁŽc£Òš8!¡ïýÞïUq¶ëƒ²xÖ¢…J¢|-“¦1`‘sÅ”±8µOä#‡’=>Ö*u™ÅÔ\S• ’*GLÿ9!zµÌFɉ¤Z(m"p™­ÕÎl¬þÛÖ¶t¿#ªêtû•xë#0Ü`r;Ÿ;V>&œK¾­Eè°¦·4eî+cñâÀÛñQ%vU2Ÿï µ¼SyN·mÎŽe¸P±_êAŒÍw2¾ú1ÙAÏ®-|ägø¥1Ü(¨L8#ª-6vòÍhNÌŸG:ŸOŽg˜À1øÃÔ¢%Öú™‰0ôÉv®P³™ØºªÃSÒJèà)ù.N¼¾dv€Åç˜ Nƒ-‘—¦Ñk›“ÀÉ7Òý¦Zt™XtË0¶ü:h>·º!¼?ðþÀµÀÎá+E3Ù³žx&ï*Ò™!Äp,¬Bo•¨ÙÈ 0ª–>£*CÒ—žðÛwsYŒ†æ€¿¬KšÙsÉél½daA,p«×Ÿœkì:;wF‰Š¬G€y“)a*Ê:ÆÂŠÙdÖ0r—:¶7‰ž2>˜ÌÙ©±sëHÄpƒAâ+ùžó§§.˜ÝÚ™`QÙ^ݸ¦J‰dŽÆÖï‰Ë_1c Ž©Ž•0Dhx6± c\†´(ÁSòÝ$Þ$C˜ý†OB!ào’’¾Ö><ÐG*Vïía”î:“ª]ÿw­=ÿ®ÛÕŽnzƒ0¦nÒGˆ|â‡<ãYEg½°gjíD=%‰ z»±ýôOÿt¯lîe£üqm*ÒÙ;ç·ösSÞšÀíy|È–ò'öšãûË”«‡’²×`ë6F—K&ÉPeØH=é0Ë„ùvÀÆyý}#0Ü üTÐgû™©ës7 ›þò8·»˜WÒ¦÷ï ¦ÇLBQט"·£Ö7ÓéÑ1Þۙ鬯[†“̵Ûö³ý“k¤I=(gÑÎ\ÍÝkгýôœ¶©£™yc«™‘íïþݿ۸EFŽ¸Ð¦Ç˜è`#¹kþV°ýÂsÉ ý^¸Ê”KÐþ[mYg2%ÒyÜ3¹¦Æ8"k./;0óÕrãä¼Ç¤Å5JI $(L*#ÝÏ6wÈ^gÛzôMãÆçè£|Rï×™i‡Ô=|tÙ&È5:M5ÀÝ$éÓç ¾úŸh’‰¿E6Gøò'|4Èo=ßθÝm:©žT™)ÙÒ FÁ­Þkþ€dåim©EœÁ;/XØ›ü4‡#Ív¬8É#vaµ€Ì$ž |N;“0i›Ø×küü)™F .â`|ñå HO•À¶5â5Ó^> ô¸+Æ&ò ÂE»ËHw$Ìœ ÖÝbP5Ô1µ&g{M½ÝË7ÉÆPù$öøÖ@ì꜖tĽ~!|& ­BJƒÿößþÛµää Ú#Ú £lÎjŠ¥"Û—¹UäO ’ØØ†è9S·©µâ ôFBvµÂå+v/¡HZ>󼎸€S§‘Ðóv¦o¯ÅÑMO~×ïú]Ú12““ÏŒËñ. DÔFgØùr,tröîŠõû¤DvžõJ¼¡ØÜ 4!·Ïm¶ Ì0bþ&ˆÜÉ~$m$pÛ"ÔŽ2º>Ä÷s ¿n)ìA)º-ççãÆ‚«ž<’Õ*’ìW¬zêA"U`.<7oúBù>Aì“+S ýÕGLò £ï)¡XJ¼„†I<øºiê6Ñ‘tÚ¿’UžU¦ª¨ÖçÌ·ýÝÈ>BÇ ç0 À)–ZÅL/;·Ì+óÖ[?ßt¦üLdcÜø,ù‚ì܇Ö`¡‰&øv·Fji!ßÚŸÿó^u›`Š[ùˆ'nË[Î=Nžʯ:£tÈÄ—åße¯­%t˜f8ÒѨåý—éV!¡U ™Oxû]ÞA&8´?}` p *¹U@ èêÅ“?‚d3X°mr$â'kp(óߣGÛ"Ö žgÇU ÛqjŸû“ÖÈкGA³€^ëÈ»XÞHÐ`M§àYf ÒÉ,kÍá­‘ç±ÁPWM ?žì¿­{XÉyèáêiikÐrôí\¾I†Ð¸gyï«4Ñ¿þ@ß4¯Ÿ¤Ï|HùÂ%í ŽÏÆ¬et€×&Ç Î÷  Õßüæ7çÖNä2K!>™…°'êAùÅbù×ÿú_O™`‡ÝñBÙù“Ö“ ˆ6's'ZÒ…?Ý/U8‡d$.d…|Ï÷|Äèý4Õù62y·×2㙬O ~9¹¶BÆ/1aÂÕGÒ)í——ðîê+ýÖGàཎù±î£÷×6@ö 2©u´Z¤¶£¡¶ØâvˆXð7!sØËRšúñ¶p™âÃæäµb”|…„Pˆñ,,ÐqÎ[³Tñ9¨jGeô?ð¤iÓ²„eÖiüàvF›Ù´35,Bþ{›öhhVë’Žé^__‡w_±ñ,=ȵ€Iî ‰¹«¼Ò3M_sÕ²]µÁ/^k[Äæµ+ÝÆLÓ†•ô–ŽbŒQçé“H¡Q4;瓎CAmVµ@ÝÜ&`ÚÂ` mï2 ÁehBú+çÅÎçxO p¨¯ßßœ’¤‹1úÀ,›Ò@W#6!çˆb©¼Í(¥KðÒÌvVÜã­Ï.Ð@N ;‚iø´ÅïüIã4;™;ôj¡è8òQÈÚ’§XÖ¤Â.ÞZ„äzRk½T¡ÂAýìo“ÆŠa´y«:J~í®ßœ”;xúü>o•!,B`iÿ…MÓ¼ç7ùtß­ÊŠÙR¸Ñ›O±ÛÄ 6N{Ú…SKŽ0˜À2ÃÔ­ÊͪM –w§¸x/8còw"EŸhÄ!P¯Xr)íÁÙ|‚õ¿ºô^C,¬œXV-Xüµ™ý­œAÒÿþßÿ{—~¶ÌžÂ ¾œ4¢Ò‡á¡„Ü/õJ¿Å¸rƒæ•©Òœo2økƒ Ù€wÜ!®¶šMi’?æ"…–±‚öÔ=(N ç&9hý®˜x¢ÈÀ”Qû–4jЊ(îQ ›¾`ge)¯{ñ½cŽâMmQCUŽö?ò¥1Év`oü™4³"MÝöñ ¬Íc«U/“]Óa– /³¢ Ô'£XaP c›óæhç¸ôÐLªtc¿‹Nz™‡Œ9€5Hbüšµvâ?Gïttã#\W&Rûùh‚¡ö“n+§ž)DÓÉr¾.Æ,EÛ[©ˆã÷¹<µ›Ó0xVxÙy"ŒP,¥÷¶]7©òlöYSÈ`¾a½ŽŽm²Ù™‡W;oúJ¼õH}××÷wÀÒÀ§¢ ™TÍRe€®Þ:¹†œ±rŒE”¼¥çP2¡?€—nÍŠ¸&²ÏÎ¥=%~-Øæ®c«@Uûõµ€œ‰åAéî’qhðdfãmæùk+è‘Þ^ÿs1Ð ï5íÜ&2…•&j?º±=­0ZMÉ4eõÉ¿þ0 Sæ=%Þ*CXÞ¾Ss4ßëüHä?æÈHpâ||Î×5 QžÈ¬±¬ÈàÎ'¸j ²À$|‚?ûgÿìž7×U1w3eNùÏÿù?ŸüI°5"1¡’‘ŒsÂè`Scr¼ýÓúO Z–™3E òòšP¥EÍNûš,„˜s”±cxp×àà ‡Ã ë·²}ïKéö•ØÝ~¥ßÖ\ÁŠôé‹÷—@.) ÄmTÏðþ’rb{ -æÁt½2ì49÷®SèŽG @BkœO“ùpg/– À×–™s< ®éèõVf{-ßEeæ}_gŽÏpíDâ$Ûð&°¤Šé¢^M¸f;wN×WC {W™0¬ wƒóD,jã£< æ<…%ÌäX ”ój+UIò¸¶¸Æ Jt}¨› öÄMr]ôQBè¦ÜîÒ+mfiç5€I-[n ć“Ë̆íÖFÇ(¢Œå?ÃK9€ç4÷&¤áÜ’(ÜQæåHM˜ñÀ½ K·Ã"_GRªŒ•lµ€õ®…$ÎÞJ)ôÛûoïí¤3º’Ù]ý•~»#E¦úâ–Lû DGNbUf `X9&xfšœÍ%®a)@¨Yg{ªdPSõc‡ÍŽ]dð:ª‘k°3¥ý-+¨|mÕíÜêBÀcµ@e€l–¡³Á9ªw ~'üEŽ…¶Ë£ØéEÁûÉû3¯Ã ¼\ ¢Û1Q~}óÀLƒï)ñVBß`hh_«ïduÁL!NEÀ4ÿª| <2ãôÄLA<ˆÊóIðÆ^•4‚ ÄZ½ù´–°ÄbHyU!z»f3dž¿Nb )ÁóUá™Yþ0sj©d]ÅlDáHÕÙY;ºÇ?Þ‹§1†u8=­1”ÞxÔ¶´ý]ú:ÂÌÌ›¾o}˜‚æféá[o9÷QÓfÛ Îë3 «ÌœùãV@…®.À¯íóˆûG¦îéÖÅ4> L`>_²=“³)Àÿ!®u >é´&*fe¶zP¾ÌÂÉ`uò­•é'Å´ùJ4¶ùÿŒm|¬8­ÎŒXŽ[€õ-½h”s“£0¤Ú‡Ÿ°®ó›?™ ŒÏ:ˆyëñªè‰Ï=Ô£‡ýØÍêj‚ŒíDš £Á&ùV8«k_ˆo ¥íÖ^i#0ŒtÓú*@Ú×Kí«5fz0AóSì#&ËYÔãB¯|V¦šÝÛ@*¸Êr¦(F·*N¨}œ¯v;Ò…“¹ 8!¢RuÙÆµÆlô^þÎî ¸#­±=ª¿.ßî{o¾û|ú@@•øÄŠuÝT±:ƒdà™h!Û@¸hTÂýl&óBŠÁ? гŽP¢I¥@^ù±Üïm¯g@ôÛªg侦Ùfq}£dyÌFëÆñŒZµ®ðí‰kI0°?ªÿyçÂ$†×h`quøˆmãí6»ž©‚‡b=ŽßÍåf'8¯/4ÔSú·•ê$óQs}1ß²œ¦WXÓƒìëfÏ1L;?öc?¦' ;½ÿ.ÀæD#PËf /ƒMBåV»=†«^Ô#ÀÅÜ-œ.íÊnY:ÕŸ\öòiA8²I° U7±M`%¡5˜RðËɳ2yÊ4nü|ÒC®ÔH.ž;öQ{9ÉßÊ–£Ï¯Ë·5 ˆÆ)Š›BU›Ã>·ŸIî¯y>K¯Éõ¼ãäc!LkíêƒòG³Vâö”¨Yú%ùd«[m袟]ÀÈ2îl†abÉL“­ÎI=8&£…“ÑIÛIu¿~ão™iá•h"/ëx†¥#(¸Áä>:s€˜Ù'¶ïS¦Ö$ ™`naÈ ÕTt´ñ”üÌ P¬ZlÀžB!ñ¸+„Úë°nëüwØ)L™IÙp†»¼´xHfN(í¸õº,®LFžÑ#iéµÖŸœ™™O·ëo‚NËsFr"ÊLŽì››_''Í­¡'[*ì 2>ñÕбB²|näkÙ‰!wW”¢†¤Þû»G;ŠB"°ñûû"2{Tßtz[—øî&Os;Ùº4XÕ ¦ÅÝ]æ0…Rƒ¸€´T`[2¦N–ê4‚×Hrð¯¹4Q-f Ç­ñ*¢Êìô¬dgGT$ež Äz.²¶ÍpÀ]»³µs5º’ãƒÍ ôtéš@T5b±X‰ž’qõµX»üDRݰiM0»žñѾŸÇ•ðë±›zOé7Ìþý¿ÿ÷ç í¯uu ÊT,u¹é8:÷ãCvžØuÃVÌ9BÁVk¬@y[·p´ãÒVQßì^‡,d ‡fAZ*Eõ ³&Ë{ÊK0n±üòÄK¬N\½ËЄŒ0õqâÑ1añŒ5ÎDÎcžJ ŠcKÀFaÄ¥T Î7q¼aŸ`P2K»2›Ïôhäwÿ_é/67h!Œ(ñ^†¾øV‚ÑWq£×ä$È\të[wvÅ! ™@2ÒµŠ}ðôC0ÙB VGE™›þˆ!3,/–Ì4›£¹i/gÔƒc5Z8™¢\´r=÷*ŒœÖ>lBð Û6 sXKÒí„P¹\1;f9/e0}µOdPåéR|ÇäÔDOL‰ž4ûk¿öky°˜Hpd?¦Yô\X³3ÙÆ­ 9™aÜ©*©šõùIÇ>æ­„Œ¡*òÒÒóÅ:«™ jÂÍÙ³Ùï Ö2v4ïdš¾‡”ê¯’;mgµ]#c#>ž~ 0p2»ýI{´ýQuf2Kp/6@€5#@i?d±}ñ„Ç ½õËñ»é+™P‰&‰Ì™~@r«ÃWFˆŸK<¶e· Èsy'x TÇŒi¤ ÍÈ1€ ý=WO€É}Ô”ŸØÕC?»ä.­KV Ê*osµG ÐwW™4¨ 0«âw•¶L1‰ôü xÚ à|ŽúÜÅ’òö;sÒF>»ÜXlÂÜ*‘ÊXéOœç×]»Y¹GÅ·{ù†Bƒ¾AgŸª¿‡À .¨[¿ð ¿ðèk‘B-¦øö-TXõ€Kç³Ç†ñÔŽüYäW›Ï©•Þr ÌRýÁ"Y­XŠ:ì¹S~¤2ñ/þÅ2;éðY³NÅ\êG•7 î6¹¹HM~¾ ÚҲ݊!$ j—⼑]Ä%é¾Ö2žÑaÍ=9~{'+Ç]OŸ'¾ot†41²¾/°¥Íz,a51_Iå‚:Þ—TfN}Í*æ(À¥GËVÄÜ@2£è;ª¸Lµž…Lw1$z‚¬o{ÅhBÂ#Š…¶„Ãùo ´3êAœ¡L¿@j‡õ"2÷šºöícæ3ý*œÊÓ åz¾ÄÂu 0î|÷OŽªžd¡Í›žçóë^ÇÄ‘Ƨ¥Ñø“ãhÁ£SG{‘ne²QÜï;àl*JK}2_ #€J±1:.8CßhZË!ÝÑÑ%Í´ÀÓò®8XS ޱy~-mÑvFŒ>9Á‰«ÏÈ%ßÏúÙë$W D°™E¡,Jæ«)þ•n·cé³M£ýË2!SœH’ZE©ùêIßùIFì•ÿVF`¸A³"E´i Yƒ%èÊ:Xb³RóšæO¢n³b»çn s5¬ß]f™i \·±©ËČӽ¸ gHsµibS×ätaÍŽaö9MÙ¶zЃ¾3£¿×í)v2“Ó«›hÇÅVR6B1Ø eÅ”~¬Ø ”æÐ!Y#`€Z”…Ϭ{-F¿ùÕC”ÿÀ ×òG<äÔ-ÚVrñÍÕŒ¦z*&}ÇÆHó¦¿ÕD¨²Kæ®=×šÔ oÕ“6+bÑC%M¡˶ÀübÛ¸ÎU QþˆØ~îç~nÆ3C˜Á¨flK`´#Àªº(ç¬>…-FS7ú1rÓìNtÖ·F¬Ù Pwé„k ¸´žZPßd?Krþ¨{\æx¥‡; aù:³·‘rŽ6ßîåÛf©‰ûT£hž/7æIfØìÄcÜøèƒ 'Œ„Õ’ Ðä˜-Ü4%\ñÄ-Ù V%@0ž-»@éÌJ-? »õéé»XAö ÅK^bjÂìŠq*ð¾³½‘ÇdíùÓ?ýÓA«[Wk\ðkªÏßwœ¦:ÓÓzV> ~ÅH˜2|oƒø­É] ö­œ7æsHl’´_ó•~+#0Üà o¾ãDø…õÛßÚ1ls‘¬£GIèVæs¶“k‡= -“|”°ã$³Ë—΄µÝ‚#™ŽÙ]¦?S¾x ·mûAÅÒ6$¶ä=˜áë¼if«†¥®’Óø+aŒyð}‡ jdp\-ºÇÎ Ñî[lò9#É>byAøËFô*F™ºMæ‚ÄlÓ) ‘oRv/ÃÐö²vád”™Ó8ó6ÜÕ?x:±iîAÙ K†IgDÌo‚%çþ¾ïû¾ßÒ¸(™%#Ë$ÄÈW_M›ÐR<üg6¨°*±©q’!ês›ä×Wød›^D]/…>çÿã2™:˜5†Óξ' ð)üq­Óˆ†d:àl‚]/ª2ו˜€²Å®Á]ñHÑ(fòóÂÈ¢„È P¦±Ž©êÑìõR‡³ÁÃÄÚŽÛ:q¡GI‹Hû^Êß°M&î2ýekç¨õº|C#{_³¿àSì§Q3+tÈÊlÉ»gæg†lªOÈe”ð+<÷uØ•ÃÂ|ëAá1PQ<ÒüÌÚ¿€4ˆ˜ég’5K7LÍèúð¡E9[tÁ] Ð÷xè\Ì Ïñ„×À6JÆý¢öSKÂUEÝòC?K¦döç1ö£ÌÆo\güC ìÆÿÅF°ûù…§ß6Chp €ëS}ÅWð_iÆn'i‡éHVm–ÿÜIÃ\Ï4…È9¯v¶ëQŸ3•Èu-1ÿPÞ²±iÅ[^ut{BäÏ WWpœëbÉìÂíÊŒzÐݯ^õß(œŒK °œ«Úg7õÑÒæLê¾1Š›@g|Wr.£‡€¤ù‘ó9[¦)P8ƒ@±éƺ¯e_ˆøˆz â2ÕwB±ÎPQõ6òàdŽâòAH»ü'Ó^¤ôj>áMM¤ [Ö¤»íaï剺‡%ÿ@Va}PÚæ9Þf“)äëyK¶×bƸ;ÛÙQ}.äG~D±äŸÔ3`&u@áiðQ‚Uj:“À^ò&áÀ›°)å >Ñãç­·9ª¿.¿ð†å0ñüÀ§Üª¬èQȬ@®¬£ƒaƒBg;Û!ô³‡ÔàaV¼4ùãD4Jà¢U¦3±=À¤r<ÈTà<ÓæžLÍoèºZ}ë-ìË;ðlÕ{®×¿n¡P |W”Ôß ;ß0n|”w‰P kÌÄµêÆ¡vÜê7¤Ã¾pmä=å¼y†P`¢ï|µ¯Dh“–ÈJ‡D6V$ rPž|–ÃëN²º cæi;ïiФ4'¢H¯mbm³æã¹mÿšºÝ¢È§ÝJ d¿Ùë|Ú °B¡¢½³¡Ê»·ò©"–_þå_žF$²ÇCvùDP(”ÌÊ%Y&‚•ùJ‚+k†Ñûðó÷«ið•øòGæž(B$ÞcF2hÞW†WÈÈŸ¼KÚ„ÌŸ~õWõIá}kB<ñDßå¥1«ƒŸÈ‰ôr(ÒùCzº[Kɣ̨µÖ¬.œŒÂyíË,øÛ®þ‘Óÿ¤Ê(vèŠT¸Ðs2]CGL€ø –ã ²Ë?Js¤I)!IÅ| 4&Ã|>eØ«ûLš3ªÌ—­A]г-¨Øñ”ç—¹Ðx5Ó8{™_%ûÁa‚\dëhž·üqîfJP¦¨§ëËÔ´¹.ív·[W̬`Ôú¤€:L½€ã:¾Ñí°{F˾òm'™¤£Ø¼…Õ¥~ù¡l Á„õB¢_zÂ'ãü…ßÛà>®¿vØÒ`U I@ èŠW †ÿžÕTÛæeÁ9ÄGæ{Á?™-–mA­¼|ÐqZ)倗‰´Î¹5 ó0Ho¸Î­I ¿ ôNæ‘•mèdÞXÙÕ õ.ÙS®zerî@?óœæwÅk:ܛԚùFÃâ­ûmæðёזßhΛg¿±xñOù »û¨§[„Ï?UFÌÍïÛhøªSƒx„içéµ&‘¶D$ÜÙ¥Êyd*‰Ä§FÃòÝv‰ÆßS‚¹_‹¥o)²(„Q µŠŒx4kc•íÍá¥ÓJÐÂ<‚€< 'NàâB@IDATs Ÿè^¼rx+-¿±*ÆM4íè|‰ 35M½ob€ŒñZ–ŒQÊp\>.™å'ñ+9bÁ!HmžàžcL ±!<åv›9ÊÏåˆQžt,ß-ÍTŸD§ô£#hubí¶©õJ€I'­W'@Àj¤iDx™o¨åù0j'd€ç|d§‡Käæêû¥¾†¢írp—ˆyB@9 _KÝ ¥Î‰kïçÝ›»a+õ£§à7’°ÀXŠýÿñ?XF%õw·ßD(v>r"Òщ>·¥×(]°>7mˆOº©Øqjžr}ÄѬËD· #VC®ÅåŒ6ð0ˆâZXË}´×§Í€•‡}ù^í­älÏØ>.š¬®d´€.“$ã™[J5€m›£uŠx{(b)AÁÀó8b¤9Ü× e-2Aâ¼ý«ü÷³&ݦ ô‚¾“¹™Î‚ÍeÒñ&gÖ…ºínm‘Tl¬"òOBxôˆt›£Á,„iµ?¿a+ŒÕtcwû=¥ßxrmï7¶HÊ`—,ŽÚãÙçørüúXª<¡·Ç!g¦þ;~Çï8äÇGI—t×áž¡H,©üzz™ Û©%«îïf(¦ÓÂûK¼†p H|¿y}Ñæ®!ïæ»¢Åðw˜½¹»ñ<ìŽd:ÊÖãL¬Ãß “LnìÏþìϪ) vË“·“`Ì­IÌa)[PÔÝ”‡‘†BÝ´ì Nµ¥¤éõgŽ@%ÊâTæfÃ+Þ´ÀSeˆpòôPJÚ½¿Ås&|^ç•ørF`¸Á@FH+M²¯ì‹ï˜·OzÎp.´”Ãú¶ß{RËy²&ŠÌŸžË™ ésüdìLŒ©êáAë÷³:ì.å'χ}kÒúY;³AþÏÿù?‚ʯ+—VAQ¬¦îGND[þð±/‹ØM¾ )³œT—Οop‹:e*¯‘Ošÿõ!Hij§ò8_ŠÝo"¸¥À'?™ Y8“-güd-¼ÚÖ¨“¡ÄBôî–§8ô&KR,Á$>_tò9Ýx»eì³ÆÊñ’Øá°-Éý^íPŠÑ–dx6wspjÉ?WÀþ¯ÿõ¿,j»sB‡«Gâ´)(!M&2…X©øè ì]kÒãoü ½péÏ[\È™Â%„@D{Šþb˜r}Ty]~á# ÍrQÃË!™›)c‚yÌîæÒ„ŸW†xc±‚Ó(¹õ~YÇ€‚hNlgá[bQæi³Dgýiä8â{ËO™ ö­Ò™azàk¨âñ º7µ­¨)¿¯Õ''¦QO€ù'⌀2³¹W=ß+ùýÆ{ÐåáJ0{O‰÷ÀH¤ªúÎGüMÿ·nê[ßú–{Ç ØŸ3f)è°Y¦]¦t&Ñ6 1»W>“¦Â㮪áªÃúc®[áa„@䑨…_ú¥_šü#ÑÙ öÅ«¥SØÂú ±&À˜ÃYQ9´äÜEl_û…çŽRØ{æ¡éÓÆÈa¦¥oácÐy W¸Á”Ù_eùÊO³¯Ä—6Wxîñ5ûñ $<Þ…gT| [ŽX¬ å(<—ì»2Õ(L¡eþÊ_ù+SàQ"€eÑñèØ]ª D달ÍíÚ,^LBï;[i@U-Ý%”mäæà£ÝE+C½JØÜ¸õ78ò9ÃEœ¤yœëO**ï¡ ziQ"Jùy´ËT+ (¦ð“¦ÜBŠka{k?¯Ò]êÇý¾ÒȾ¿l–¶UÒ OØîÖƒýs÷ÎÊ$mLœ+{ãy|ˆ$V…¦àÜ»Ç!‹SK>÷È•C•"> Y´Ë9«»©F”ô¤p2˜A¸žX¡ïºK»|Úà_ù•_ û"b»Ø5mÎ4Us –¸íkÅWÎ;ã#šÔ É£Ø'®Ú:#>YȧÞ@lçÛ@¸ò‹•ÀöÊLŸÑ)(ˆ(Æn]µÜàe†çqGb¶BSúªqÍRÓ;_Ê–äêåÑz³íd4ϽNbwƒñO:Ünܾõ´­¬ù‹ÅøLÀs ËÛº| ¡ûŸù~m·.yøÌ'ÉeÖÔO?>ù%ÒÚ™÷‘x3ï(°/ &5nki>:á°ídO}Î¥(…1_}Uã¯R—?bMÑ…@3‘Òî[éœ!÷t;ƒÌZ]…€"ùàòÉQWsqI\0Õ WnT³­— ïèVœ­1‡œùÛàÚÏWΗ6W`L\Ÿõ99Þ¯Q¥j&’§)2ßÚÞv±#móHP7NáY5[9 ïË9‰Ÿ#ªuÕñ»€4ÑisžQkCeØ|e\¶Ç-ÐDÉñè=U™=ûÛßþöµ‘˜“É WdsŸlBKˆ›…ŒaB„%„§Ÿkü|‚΀ovH˜ýˆ¦šë)XPA›U'oÎ ØC%\bSÝŠGUø“ŠJŠ  üÆ~îN3÷¢óéžç2ýrÆ4q#ºw‡>F7£×|ä*³ñîÓȈqk3•hUJøY’›&|3œ¡‘ÞJÂL»-ù¢Ë £E9p#Ŧàà-çÑ}²±ÇkûC¾®BÞã).C®m»Ý厘ðBÿkùCÅÝ5þò1»’î]å•þ’G £ùšÍ¥ø(kæS1“»nõ^­‹`›uÊÍÑ:€gÎ'óÿ´?¼PÂ@±€ö·®wé-Ñ®GÛMæ–øz uÑyùE ¶GlŽ ê’w<*vYô `;iμòmá˜F0>&öVéÑ©I0ÓHátcØé~XŒ)öŽï„!|ÂÙû´Ûa ìj„É›6áœåDoË·[oÛâë$ó9m¢Î×rZi{FaÏÒ-öi+ó;›Ø,4_Óæ‰×wÛx&4ÍݽóU8H!’Þe‚ 'È¡LWŒàÓƒhB ¿ËËEfœ¤·+ £®Ö`Ñ~•߇ºäé^•4a>ùó{dBP³¯¿_Ô\!М°ÍÂ’#~NŸ‰3ÐbÃ9ÒµO* Ùx¤2gÊL¥#ÒÚ£G{Dšl®rÄ£‘ö-ˆ­Ý(¤~ð!£Lz‚2xhDfÌräh¹“ë aÿ£Ž}œ|.¦™K`aå0İ4qõذ&C üuÛ8îId×Ô1nF;j‰0~ÎéÀ¨n[>lTðR|Ÿ—D\Êô “©Ê&×GºÅ¢ºÝÞ…ñÃÂý%þèYÈlËÇËV2ø5q ”’-'Eënöæcã‡5¤-Æ1²¢}ƒl“ºf_`ÐÚË,viÅ’7!×ñ´“bÿ´3 3¡}íª@.ð#Ò4DÉ  b“y}Š£—f²bzoq{Ðñ4•2‡¸m[#[S¸…¨®¿ˆù”%¾ð¡æ¶¿A©`ˆ5!…œÿî]À°JŽWsÌToÄ×åº àeð’®;Ç(P°1é@2s¾ÀWW¯EáY=°óv$;Zdê±»R©…ô o¼+0v`Z¨‡2õjþJô3€Sò'Þ C81—|¼*ßùŽ_ý7‰[0>dª°ÇÅ­„(Âß0ŠüĽ›€¡ysË­ÍŒ @7ƒ™hA¶»ÑÖ¤W+„Z(@Aÿ¤i°„%M{îQz{ÒU —™2ΫÙàÆ±`2¿ë»¾«˜Nõ-ÞÏVšJÄCÈj×Q²ý5¼À4KnÝÈÕŽK"e(Üšó”Ôjp$ô£ó¯Ë/sn[•µ‰è'.bÝî»§a~ô"4liù°ÐOáÍnMf ÔÜÌ¡5š@ØSK†ï‚oCË$jQ`ǹI?TMK&‡ŸZNÐ΢f4êAý”)¶‡×ç§nÓ¾Uüõx|E9ÛD]’ôOõ›ˆ”!¶éÜŒCn„™VÚP}Y;:÷’A؆îð4ëÈ­1f‘ÔdeÂY=qýòD²g{|fEž•M”Ì éÌpÔ&+â) d²¡J°C#š*óö›&ËÓ=ó3 9d<{? ²­be„ñHÝW ™~¸‹l™u \b ÜÚ*Áx`jÌÛ|¨ÌÖéqD˜ÅhIZ˜AϤ ¨V#ù*.b&sÛ´!·äkt™âg±ÁÍm_ñ‘˜aŸp2W#:äKysA›ò;ud\0€xß•ÎFŽ,©ÁqËe¤¸[ö÷rÌŸ~n¥}é ¯£÷ÅæôÉ|A3ç;_ò`b(ÐJ ÒwI‹N‚g^*sM° ÍIF€š• à)ìiÖÜÿ$ö ±¦PÊ>3 jfn |&g™Éò’mfä/îÆ.îå{Hl+PßæÝæ‘®Cq…Sh×ìÆÒgjä p¶Éé\º¯«îü6+áÅçï8ñNÂ9ÉÇ·Dµý棖à%ÒW´T"Ĺ 0ê¹’£l=†‰«¢Euûí­Ì|”¹u@šZ¬æ”0ˆjŠŒgwß!_[‘—âEó¸ÕøW"‡x#¤‹R¯IV¢ó\‰‰[uX@é¼.Aê™iµmi2áZK]‚ˆQ ZIÎ8§BRåni\´±U ¢‚âr~£tÝ}{¥¿´8¸At¡jßÞÚJ60¨[WÃ’y©HšÆÿå¿ü—ÉœÄ#%!c§æäzM•a¸-Õ+Ÿ­]ºî#(¶nëÿœ®9±d6šeõŽ+sð«±"³žßosÛ£³ s™­õÑÛu‰D!`ïžt‰×ŸËnæ‘ÞºG ÀAq¯Çž5æ"ﱃŽîa.aÛD“åŽ]˜çßÅ£ŠK™*ÅV%“§ž¨ÁGžfêf¢¤5Ry”4×έA3°’A ÿê_ý+—ˆdv;ù$8¤Ýfw±?³ Ún±inìÜÉW›ókâdµëé4 -uSp8½)ˆaDŠ“”%¦ ä »7-{•ÉJÙsg­Nƒ%Æåï°Ø´< HÎ#±ÉÉ,"0Ý:±É$”Õ /´b™3î)Ù¦ëáìÓ Ïo[Ý{]~9#°¹AâºBõ):®"êt#¾û-V'¡ïöÞñ`º»’¹ÖÍ Èsèxer¾ç©xä[Å-(BS]mW8ì»àBo:bC=¨}âØ ø›õ£„_«£ô¿¹öùƒäÄûÁÛ™9s bo m€ã`ÇЊ+ò>F¬£Vó0]öi\úOýÔOw¢*¨Ü¤àŒ9«„u?(ü~Š[ÃÛW^õÀŠKÍ$Ô4cmE˜ü¢*è¼. ·°ÛŸ´—­°¡HT—§ßžô#´®VŸ»[sZ~÷‰–[;Ô(R¼uÆŸ6&¥Ó·2£e|{PrK‚ÔͰY:fKõ]Lãëx"&}©\ú=.ÝËá¸+> 0CSžmit‚Œ;S]WÛpáý¬Ò<¢xM3+%ò‹'¼ŽÕ˜“©gßnþ"• ’|÷uXÒf~(HKn¥x5ó³å[2C—2@ þmˆ*c³ºmj¥­u°F€OÔx“î,´w Æ@£ÙÖ·^Ð=ÆN=Sø¥‡[]°*ƒHÔDVeüVÝåf$¤ÞΙ._ý:¶ŠŠ¥=YCËX¡žÈªs W­zVJ“öPrÛ*” 3¥qΡö)¶&¾ô¥/ÕáÖ»ÒyztÌצµÙë÷wWõ‰‚ó;¿ó;.©0çX`ËØÆ³6›(qô·QåÿV- »NC³È3žÄ•Øè9q±)&2{j'é ˜TJM”3ê[i:ÔµWô»6«4ˆà ´ëÅù³Á\ñßÈùÄBX~¾Lпº[¤ÓQ¯¢WpÛHæX1¥¾ý©[„Àüpùca9y΀Siˆ2ñm%[,pùÑ<¨ä~ãÈújzÞÿ"p ¿sÀrkù…\¦·.<Ì6ä„™øÞ,iö©l]2 °Ðõ.²üêW¿jþÛàç-HDx‚Ëå€DTà½éTɘM iu ]þ$Òk¨¨€b ']¨®SÊ4Š‘€æã ˜¿å³ºr¬}.m°ª·CÅ'MˆÌõÝ2]ÇZÆð@½c™—3dÓ)Ó•‰`BËIˆî` k¿Wf™¯ù¥30šÛír‡;™Qù48qN³›»Á±ý 0ƒÅu—ö$óà)/:¶Ø¤=Áôì¼Iw×ZÁ!øî·uô Óû®ådvîÅ ”®â±Á-w0¤%)fùgÕ†Kr£[õyYÛØE ·Å’^cì,§Sè´Fˆ‚êÎe™ lÊÖÀí%•Vâ¥êŸßºWP7°A>øÝB(hê”LÿkËžœ5‘·Øo9þXMgVõ¡¿Ä‡µòÖàÚΧïG ô’:”ßåò›^ð»¯øýÿãÝÑÇ:å0…_z»Þ¤ùžµ$¬S2äŸþéŸV+«½S—‘kF›>fªÖ,–}Œ S4Ñ>OQ²Y-$0½à Ó` PnåodáøUéëÕ…£þõ‹e XB¥¦’ÉT°òSYåXË';»«ü•£QçL¯‰wmF´m Øòýa¾GŒ­ÿ™Ê'ºŒc‡¡ðõÙVe½#¡ýí¡ÿ£æb-_z -cÃKÀØŒêk-{’–ýÔ¥Ó>)Ý™Vu!6¨«‡}—ÿþ}׿ºôK>â—àŠægóulç;ÔÉÃtá–£#ƒ`fž·Ù,yoäð­€Kí¶(#˜U¶JBŸsd…RYôš)Lð×»“éÖQ%Âv—nˆc¦]â1X˜å­?yUY³Ëlº­¿'W3-3!¦ë´VáO7íäiÉ;Îlz ³}g¶Ð2qƒMœKu¥Ö¯ÿú¯ŸNÎD—±½*fG£,8-Yæ"B Çp2ÇŠØþcd@në¡åc•-‰l"Eô¹8^-Y;hýÃê±ýcùGÎ;29õÊJW{ n½‚aŽžgsª­ÒKžühÀ›Î÷*@ÁÉ”=hLǺnÇéÂVÊÉBŽZ5ê¶$’ú–Fûï¥Ï¨Æ™(FÏ»öjÒqËñÊþiënŒæcþW"‚Æ¥ S‡k9uÛºFkP’¿7;ý|Ö‰»Vjo—Á7ãÀú¦9{[ÔiAh/Öur|‘ˆô"C—[èÑ8uÃc «1zŽlÅÆOà t—c€Z9O[ ër¥oN-Ä–h 4üÕ…FÅì!ò×SAa™ëÇûÙÃhø'¨ÛÂÎg=eªEÔèà‘TPÅí[4[·—ã Œ4d Hí­ñR¾¾.ê¶§ýÉvåœaú—Íiäú£ºˆj‹s¥â„–)¤‡}âÒʪ‘ŒThxT§[ãi òÏ%ä”éÈ1ìä4K~[wWNKN­{MÄ”¨;` ¦ÈË Aç¦RTÉd²8­%†sª·ôP§eêqûè‡á˜û´¬º§Ô×3Oð'árCEžE&„Â(•¬3UôP>v )@¥Eq…rÉ6þž6%ØNsŠ[–ãeœÂdàÓ/!³uêô¥%‰á6QT>¥ÿüÏÿ¼ÍFëô’8-ÄÇ‹£áÚ™K[dÚ;àd^Ot–‡°=7cÎuß±µo}ë[Q²\ÿ±äšµGºæjþŠì×òôÇ>«K(•¸Ò±qb zÃp?·èD"/zRVåø[7µqæÚÌ€I˜@ca)ÀÈÜ7P‹,¢mrÒRª“ÄVw¡)<:ÁÓ»ŠñøxÕÇ÷橸&:…¨¬™Ë£ç|å\®e¶4`“Ñ’“]û×t@‚à0SÛ_2äl-ßÇå] „^Ég>ó™yµ¾¦² ù³Y-CHü:ð…r´F¬è Í-ªE2¯¿ÓtùMf Þüí4!ûÙV Ë‰šp„& ÀáždK›Êºàk¡ 7}=Foõ³Åœ»©ï³Ís “_߸¬Ýl±+hÎI]k€Ž, iðÅ/ÝU!€¥;Bù‰O|Bº_‰þ6OôéÆ#ññÎÀHƒ¡“ï¾®÷ÿ_ÿvÖÖíÜ)EÞKWâ¨Ìøo%O/íÁnæ£Ó§™©ô¨H"ˆ|ÔJ®µm0ÑYó×4š7ð@™på †ÓÐ42UfºZ˜.Gm9e^H";¨%…ÑqÔ `´ÃŸ\+Mï°eLõXÅûM© :p°*,2o áÍ'þ°>Á?Þ*@ƒàôS¦_gHüÍ%›­9R¡Ú ZŒ?ü4Â&Ó×±+êÀ’A?ñêÐûñ[^ ¤}$Àñ—ù—ùšÂvèYoOÝn«˜ÁÇÐ<å8'/!' žYÊ‚!1à/:L5PàûuB*o»‹ñzW:Gô^î%H:Uð„X ÞÅn3ù×§ÊY”·óõÂÇ»ˆ3 ‰QoŽyÇÂkRoUìéØ~.²kÉGú]˜É š¿UúÖ!:·@¯Œ‡1´ánÚ(wWVÜÖ&À›}­Å\c¥ô!_Ô5F…ŒcX™Ž( Úð ©Ýð”5µªû»¤óMÑH˜QìñEô‰ùùÂmôÃÔ2¦jtAßKªÉª(–zH-‚€!4ï÷ïÕŸ½©ñvI 8væŽsîM ¤<ûŸûÞ{\Ñ’ˆ&G¢Øèbf´ëa¾ ”D÷œ·=û(Œu¢÷§~ê§6*±J{8’ú4ò„Õ2ªK&õÚÉŒs9½o9éNQ´šû Zµ:Ôu ¢µçÐT}N)sYE¸â›´$U.×Òƒóˆ.íÁæ#h7-@Ž|–Ö6'ËwaFLÔéeùõ*o9û·ÛgëHuþZOz™®uÑL []ÒÂlWk™Ó´Šsæj3×Ë“ ½àévU•ÁL«&Õ->iU'ÒLã嬿Ç2Má;NxeE¼¸âÕY<ª$+R®Ø‡òzúéŸþém~|°8%Ú¶Œii£µÏqš%‚†ß[&<ÀOÅßš7eïÿñÿñ± ñ,b ño…àT ÆÎò¬Á xDÇf<´áè†ÎlÝ#-dÁÆýæÈÍZÆõ§2?ÿó?oBf´¼ràµ"kÁžÚ'×’wœžu:ï4?—á ^â¶Ì‹R8ÅÓäÛ¦²L%jþ³6lÖËœåÚg­º‘é 03G®;¯Ïštê€dBœC+SàJ‚%$•±y©1?9Ò+m>n}x30ú2ï+@…ç j@+.z¥h Œunä®bÕ½ÄËŠ»VQ¬á $Ð(gŽ”ÁÈSË98ª“AS0ušZ‰R Ð I®w¥‹$lj»‰¸k1[ xòš)mNR¥èÅšÚ›måû Ð’*Ro©^š:í¨p?i:+(€°…õçhº=ñÒ儈D3—Êœæóµ‹q4͘I¾½¾hkÇjšÀࣀ~ĘÙf鹜˜Irˆ¥{Eg±©`ÃÓfc ©â-3<-ƒv`°çV‘ðØœ'ÁÂÊ·«Î.™'H™ Û©¿ºJå§«›ùnúPÐÄvXp{œ[%:$[›Åôž·2寲îÐá¦Âð1ä™)°&~]þ‰3rú³Ê¥É çµ…ûNß¡@˜É®,ÔìêDZfÑo“aðôë/8=ŠŠ4§%­±”…«fפ‰ksýW+Ã= ²µÚ¹£³¿§ayUÉÀ^u„5˜çâ¡VÅô„ñ]Ï­%Å –Æ".S~J,ìò;›«L¢iß6”OI“:vºm§lyU)àY.‹XSƒ.é®*ã×W¹{ü½;30Ò Qp,{ÞÈ+$ÌëöEµuVN5ˆ—Ú_=û£‰$ êÃæ«y©¢ü>y¤Šê×xY Áú¸®;åÚ¾¤/üä'?¹æ·éZÅ!¿ùì’Gû#*”¸tnmêþÒ&‰q°f°ìòšLÔÎ –¡Œž{ “‹j¢z±”Á|~ø‡¸ ÷v©e—d¤˜ð˜„2Mé ùPËy®ÎÝŸ¼b8`Ü h<³¤üÊÔŠéØ&E°8%6¨µE8HµO)JËŠ˜£i…H³šž)’è¹§àl-v¯é¾¿¾±Kd¸s˲]‡Ÿýxÿ#¯}Œ¹–‘N—ª:¦±ÝZ/1FHxÝk½ê¢ÙIµ¯<zò %ëCÓ/èa™‰¾˜ölÖká+é5ê^¨÷!^™®ëà4>¾0 –þ÷ IÇ]•æmcaK%Ú%P{*æZ©Áb情ñÛÊŒÇ&™ˆù] Ý ”Æ/i²2r‚¸èÝ&¶nwX `nMÐS,;'&œ@;ù“Λ1c™ÌÓD[yÒu›©– ,ô¡š2ÉÓ6ï)óÂ"öFq¿ôü&Z™æÿíßþíÒåk‘Û}U.‹°FþáÃ(•ÄU­ÛzäÀvXÛŽ¯¤ÈVŽ€-2¡@ÌÇ»åô-—ü¹)à§X§u-$9©…tCƒ%-°üXtþ‡~è‡LÑ臒QåK Ž)=e1¯Üí«qÎåªtyòÓÏGâ#˜‘‘SèyTÅ^ñõ¸,§Ýë ™D)œÅ¬Ó’[¦`ýÑj‡ ¨ ƒé· õ3(“ÆñJБ>æ«cvšâª]ZÅI¤¨wû2XQ1ë ³E)éo¼M(q·¾„ËT­9œŽãð·¦+¥o.ý4Ü9)Œ÷#^‘¥ŽLŽAm ÒaÇ«ƒsJË<߯rèŸ'²õ¥ª¨7þªR å5¥Á‰¶çAyÖTݨKº7Šö:Ïj٠ו²êL”}Ÿ6‘xœ”‰I/Û Ì¥]ìjƒÛæÖKHL\™^BòŽ‚’d–¹E9î#“*Ƽ©£‘Ð>˜µ$‚¹b|+œã¸:gŸø–Uù'Õu– 'çæ¤L¬*÷ò¸8ù4¥KåèêÔT:/T j4?0| ªB '?§©ëXîÚÎ6±I’[„À­Ì½^ÎêkþóPè\ýX ,Û•'$·'4¶5mb·jЫÌ!ë8åcšõ*•„œ ××ý#M¨º11lð¸Ýo/N7âN›}Æ~÷¬½{póV÷xÙ3@ÞPÄÄvÂã\}¼9Ãy¢ó~-½Jg€aq¶q^K¤0¦WÿÓŠ‚v&Èø)Óü:v a¬&Ç£>‡ÿõ-§Þš| o·:à2Í—îÍ)ƒ ˜êÔ2®°¨Õ„¨ƒÙSæ˜è4»qíÇ»å€ú diý‘œþ0a‘¡KRÃ¥vî8ÿ>B߉öR @ë?Î]ÿóæß{åÐK”\pyú‚ÛàÈD5ú´˜ÌÂÝZoÒ}ÑìÞØtBãD@ê ‰g6þ<6× OZ£+*¤2VNw)Åìv ͧœ]®m'2t²¼æHºòÎéÚ $Ò'µ[däÉäØiÝAÉ9‹Ó‘k?MyCþìg?ÛÓcU¥¯‹Ðké{FL( #*`”:E=Õ`Ï-É+Ûú“ vŸ˜w[kƒÈ/7N[B¶åîª~‹‘ðk_ûêÒ[}ÖTU¸Š­(… pt–Äõ„­ðÈ¢©W… ¨@ádlu”¢´ïê&ú®,j{Ê]F‰º4.;ëpƒbV‘vîí£Ÿx2!|2%¼ÁJ “6#*äjÚÇkòåàu¤,œ-ÛŽL9Fï^Ðú‹ì"âåúÓ ¡‘µ‡âüRE«}DSäqêÑ­ Ú׎ëlDIª)™ÄÕ2|v*°WgÂ&*}Í•o4ƒnIcº¼ôî2¿SÁÍ|tÉÆ9RfIör{e¼¸.3˜ Es¾NN ÈÆ.²¡­òy¥b;m£µ€zcî^[.Ý÷ŸÆ#U`€†`9¬M+v¨Q…§×‚ eÍàƒ½ g-6é@‘ù!Tó³H£i2áLÔ»ˆ<"òa¤N¾é[r]0,-?`VŸÓ}Û2èe^íHçE ¥¸Dȯ€CAD¬x>^W7b_Û]—@)&–…–Vow kÜVÜMQ;Ç7Šä¿ {6…¸:8­bC>ÑU³Nbg'-p·Ö_PßXúh0 qÍ/žLXp÷^Öº/$}Ÿ!g ïõÒ( {÷v壢Žu•#2'd~ìÇ~L•ܯOÉÂ.Wu– ZÇ­:€³k>ÒsýÍ'5eöAÒM˜F“J#Ìt£IÝÄK<.ÚÖ@Á9ê { w˜ÇÆhΚ 1Åv.s „ xHÅ&Ä(Ô&Kå}ð˜×ét=2?Êi°·N¥V„ŒÇÓOÐz²oœ¯.swUò`±{]úÜb¢)¨mÛ†[Á}tž;DZ#¸~ýë_ïn*ö™­03`¨‘qon¥aƒš‰1zz6« ½[8™Üé—~I•¸AôŸ¹à¥íi‹šÇïÇ8 Õè;Vª¾ït¾âM;Ù%Š“Žò¢ì{$o“PD™ÕššñÁYÄu–RŒr:YP~ äèM)ø7ãŸ6Ù#[Ó ˜‘Æ-gŠž«d²ë•3k,™µ·“æ§ÚæË¸1&—¹»& äŠöw·0ÿÁÄ@‹ù|È„ëŒ}¼éBF䫼k½ ѹ…œÒf6G·²÷ÛræÏ˜Ñ ¯H ðŒÚ™ü bˆnûF„[ïJwÜÉÆ ¦êȺhën'è}~ÿOŸTMåOºg\(³îØWx]0lÍàzµø­A Ô÷hë1¡é=þ6áÊl-¼„Ëû½¹avÞ7™ïVÍëïCI\úú²9ëB€íK~¸3CóZlMSþi9­Æé‰ª‘–€Iw5>WN_wá Jæ»A«SK¢³¿ëd¼^×oY“Ê'ÄÎÁÙNõ@Æyuc©WqOIÇ©–'.q¨ vœb¦>ô{ådæÚùGúC‘Sà±”ð7¹k\1Wo«cßXd³Ô©d®—¼§žîo¶¢µýëFB½j#\MÁÁXüœ®©ÁÌP«Š|‹È£ñúÕ µJ¡ŒEH7sß„“t[Ý\ ˆÐpçjJÙyº*סÝSڮܨ×ÅþHô3éQ8¹'b;ËÏþìÏÎl+ÐFOK•vÎîZh™‰(3Me²`ÓãÆYfÛœœAœyc#SK“±ƒkÛYóK瀠3_u7uíQo… ¾b‡ÌÌ££(Ç'–Ch,:‘V—X7ÖšA~Ååªëj±Ì'Œä÷!^šç8¿ÓnCçA,p«àŸrlâÁWiðLÑsÒ ×7‡ríi_­vÖÎÑßÒŽ/ß=òD+\g#‹^ÙaóY]¿äyjçȧ ¦Ñ£ÜÚr¡àÕc]_ L:Y4fXãü† ÒÚ$¯f#m\ýZ]ZÈÓø‹JÜ­@¸ÔòŽC{øïÎNU'çœ÷îí }¬éÄaÅÓW‹uþ eä¬ãø%Çζ®,<*·b›mÝRiuõõ˜üarZÛº‘,š÷&Ù*Ù‚Ô±5"™ÀwÍÇt£X¦Îc´·YæG1ŠLsÒ²tIÛß‘¶Weó) Mmúµ´®{ÂlCx\~3@LýÖ{™¯`ÆÙ›`-§4V÷œ"H«}éqXp[Ѹ’θ?ȱª@2“¿&VñlÍ—þ¾`¢ èçz+@³†–Ycɬ%¥éÑ52:òÕ<ènñ*¤S²Ú$j¡#FÔ±}¸¥ùôÛ·ËVÅåïø÷‹_ü¢‡Añ,â8Øcã<™K^úlØ…‡›FâÆ`Áê]ŸlfxK —A`€ï$Êãi¸åë~å8 9˜<$|Ná7ê g-}ô0‰­«]6ÂÏs£žo˜„”ë]š¢ú`Ҳؼ½2á±ß¯5e™‹ÝqNà›^ÃŒ3Øæâo¦4åo«ì–ö:!ùLJåˆXEz)s¢^õNM¯Pam“xÙ1Š•)€_峇¬U¤'ÀÌ16LÎ~º±áÚ­—†œvà[N"u´òMGõÖæP¯-ã¥QÔqJ?öœgc,yž[õi]½/|`¾ÂžiíÎXD¢ Î)9ñ?½¼Zd®ÑÃ`S˜‡ü؃—§RY{%–;…Wì®9<= ¥qìÀð%éK±ªë[®:—ÞK®³ñ|Ûzr2oN¦JPÁb7?ÀãõÛž’€0™Ä‡©ø¢w+ 8oWAOTÀÉGÄEÎDÁ£]\ˆÂ#R°83^±z¥–°†O•(µNÒ8r<úCo§o³×MØÃKXáuÒ2jGН!IÑ|+*gƒ©Ëî¡°¿¯|å+“YG÷¨m8!Øä”±Ùän+I¬ ª’Ï|ãèU«ï³ªiö‘øXfÀ¦>®¼È²/t{­›ŸçÚ7¾Jzû^_oI;yâ¡8ŸÅ²•q™¡bEƒL´hºÚ÷Žu/ õG¯ôm|ž§®èHeÖ%Í~uj9ñ¥r…Yª¾ÉŸùÆpÖÔ„“©¤µ ¢á§‰ŒÈý&'ÐÈNî;%=q× 28À6dÒ239«…¶ülsOŦ ¦qÌóöã½ò•äA;Z#ÁkÅ?i»Åó@Êûóâ¨Ì5þa¨¢„–ù ·ôD\.ïë.ûÕaÝÖù+Ó  Í,±,µ$W—¤©bÅ9k2‹³'óøI¸tûBŒG ·½‘;»œ`׿ÇЪ½—Ü2 `ù|ZÂÞZ§ª¶“48ê*j!JÞŽ„äämÆÇ4Õö7ád޳êpI˜_m/}‰òâX¾œÓ3Š…ê$vz©âšO›œçÂ&Ž*C£a=ê˜2k•5m)Í:òPTú ×ùùXÒé}¼Žþ‚XàV ¾ËH‹9ƒm6,/ݦ™ÚhåÒ _ æü2C‹5‹“S¤T¼l}½{²8Þ_ùVS6I|¯CZÇ3Y¸îÕÀx{è\‚ÓQææO7$²¦âÃùíS­V¼·’ÖoöÜ#ãJî—hÄ;™Q|}ÐKHß­@˜î­IE©ë‘t ê„4‹1úÔ!ˆØ÷[Öן›ÊDsZoIÓµä JúS8¡N—D¼˜Ì5Q ;\±ø/ûjâ|s©Zô&jûœ&hMPcŸ3åg=wkEí9…昗ä™"Ÿº@jB¥ë|ʘ| =¯@'ƒg9½ÌMë«üxÓ¶óùè<>^4 Ž"›¦üØÉ‚€Þæxae §ŽÙ íÖÖˆ= Ùo¢Ë`Óùp²l!p·º.7!MŽÇÕ b;–×øZF(Dho[UƒSk°£Z›y°2™Á rm‡˜ †§¨f¡ÿ ¹æRš@˜»óˆûNä+.ü¦aú*#”iS%ÓŒ¥2Wë¡&ebxï pŒºsæê?þã?²*ßx;z¦•*úEÉ8'. ^˜ÀxùÿˆöqúÆ?ð×X`Î4îÁ‹ú~ê¶ÎB@M¾¡ Î°öÌÂL»QÈPe¶i   ±!BÕ•1pÓÞ'1rér ãþùlp¦º¾¥¾‘‰êL—¿f¯…lCÈ´Â8Vhè9ápJ"§ rÑeNÃÉLáI´‡zÅ1º1®"â.¡d¦cLRÏ˼Kf«>—x{Š˜b®–otMΩWÈÔ•P}ÕnÂC&\çç£Oo±ñA,Ì‘º`±€V" %§çr(m _üjó&ËXçи6h^æpnœ¶˜4}ÙOE<ªmŸlõA¯ NºÀoF„¥Oæ–ÈL¢ >¿š4×b…=o·øônyØ¥8õ±åì¨Úô—8h°²ñ+~Lë£ï/}·á|Á6³†w{å1ÍWÄðþÿ“Œì|u’ëæ+.ó¾Ã©§s¸çû¿ÿû5Up§íkTÓ‚DÇùlá)Œ×[¥ç{†i‹°Ô-Û•GÐ:Ïž!?·Ø–hGo·Ï‚ANïòÿýÀšê°üõÈ8JüÂ/ü‚Wãhå 9Ð s‹¼ŠçÄX˜ÎârʘÀ*‚&©ŸÛª5Û‹Pfþð&Ç1»éÇ?á…žT1Lß>ØBÅr¡¥!Œéj Ch~”†À&R’¦J(p®‰]»®ŽuIS­qÍÎXòìÛ“y÷‰¶iSÑHƒÈ.E#,'up Ù‹8NH V¢{B#gòc™5ºL;Ý“ŽdÐv{+Œ®Á"7® äø9Ô"Ùv°J—C!éiùK™…‘´AÉÏÈ=v?z$>¼0ósÚóG~äGRrcsW>"´ufóϤQ †jdó¼Ú*®—sáz ™µÊ¤WQÍ>„_£ÀS/Öª²8{ZÿÕSqÚœD?®t.Û.ÒA‚ž¸yÔ¤ÅGíƒ>³í¬+kžr— ädø«{¸AL†‰W@!ÛQOw“‹R7Ä”y"Î…YµìèWZíNÐÉ9ýc]ÖõÝá0¸´ÌW×>7ƒêT›[kÈ Ó¥³.…”N¯¿RNGÝ0j+ß$+f —RI,:/ÖÍãqmçþÒ£”ihû·kfü95TŽÅ‹-d™õé ¤t p&?-Ó¹#Œ{Ñà-T‡Ó†àñ–°ò1‚âñYk€™[bÉ[(' P”Åü€lÊ·ÇKÔùña6u¶’[†|©3ü·™3ÿ>M¿2Žù±-‰@Ö~'0Ï$²ñ&ztgæå¯Ê;äü[=ÒóŠ3¹O#=®3®s@i6Gß\Ѿ¾­#ÅÛ.sú8žcª0¬n—\4ó­l%m¾ëƒ¤=«UvzÔ6ó&`_uP¿}¿‡¶öc&¡¦Qˆ\1éo¸³Ë»½§9-ÝÞ0ö«•&P†eIIówºdˆˆÄƒ>C}eXûNåf•f@Ÿ»“ >é¸Q«îÒæ]k¶1Ç3è-ïo~ó›5Ûq¦QçäÕf'­O~ž©©±§3™øs~ jtk´û}-Z&{}+Ç/ŸïŽUHóV/ê©4€Â,è£ÛrðšyÖ#ñQ΀ÃlãeÇŒ?ÇÏšg@IDAT ’[Åþ[ú“Í­.y ¢ŸÛEJÀ[SéÖ éÉŒ‘2¯UódõpÚóÐëR+œ¤XÄw×þ´ã*0ádæîÄaô™[WAh¦Ø½&úŠºY«&Ø`Óa1A`#3;p²ÍÍîˆyÞQþH´}‘pI § NÂë/~âuø£Ú½à’*m{ÜG‰K½]ñC_¿4ÀÖ‚b®@²tJw—ëÂú?±ÑÝôfï:7è$}ÍËq]ÎΤ5Q@a@DžW?B¸E=™kbTÆÚ9Æt©$f2ØàR8™µÍÒiNÃ[¨ÒcáÉI!‘xè)ÀÂW¹TØÚ Xi®>Þ²Y¬s¼œ3ÒÇÙøxsb€ýâ˜@LÅ`Ev¦ÎÏ$ú›{áI™ Ý $׃vÀ¬æé\+LfcÞ’ÓðYpTû›cš[à«n×óÁ“Ó‡`°ºAbý¶³T&° ZØ`¶ò[k©Él=[ã]óaPÑššýÝ¥¿,U]Ýgâ{¶yß™÷,ZqóN–gj§êC #ÏHSc/¦ð¸ô¾;^5¡Éò;I’~Ñãìý¨ö(RÆšcOnÓGÀÔCù¼½O§¯â»‘¨sŒ<¤Q0…ÐꆗnÏybu;ó {–™^)ö-v™åW$U*“yP0…ŽÄ9™2)³ëw99Àx(î0aE™ûÍCÅú==³1Oy$>¤àÞx×¼3ÈxM§|öz@+dl½A Í Œqé+Wú‘°Å«'C—ªYùyè¥2åóéªY…7ó ÌïÑ0o±­b?ØÉ6•I?Òéù­ð]rÞ_þò—·¡aé ìffßQlKµì.öK&OË€/ÉnŽÁv Ö2 §ÖüæJ·=ú]»ŒhCá<Mˆ©Ðùþì œ²²Ø7p VùÝûïåd¸ jö©¦×T2›’@[ çå¸9ÍÁË™„5׳ë9|šÞñ©™Â“è¤Ãª-[“>¶F»—™Ó=} Fp©|ùÙÕz’]i·aX7®”¼tË–1û»ÎØV¾£—æêCÍgoÁ Õ±#i/+È$˜éFÄìÖxÑ£„L^ Ýô3‡8ð/ekñÀCÌ _òêô¤*À¤ÀT8=TᛪKÐt Lb"„Mذ¹U¢8ˆ qÄvtí¤±M½JÄmÃÕ=ó0MõÁFà|r¶HŸ’ÈŸcêú¼Š¥Àƒ‘¨·¦^Âå= „Þ_q½~vÍXKƒwën]Z@ð+¯<ŸOþœ•±ØbúB,ŒQ.Ç!Dk;¹AC9Ž -fY‹IóœÒY –ò¸¢2lšŒÖmоÕ·Of‡Ø’uƒš j¬Ÿè ¾–ç¡ãnÞ§ÀÒ*5€ËS“<úžNÓÂ#ñÑÌ€ ;MÚvL.å4WÏ£nâÆþØ jÄÒX‰äÆêH´Cêú³‚º±:#aXYRp\¯8ÇÓW˜KUZ;ö•Í<¨üøuEš,„Ⱦ=Ò¸òÎzƒÑ]êØ;›ß™c?Θ>ˆË„ÄEOÉäVØBÒ„àø‘ÏÝ¿D¦‚² ¹ïæD™œTΩQ¬G¾É‡3.‰¯I ´4RùtöÇq9ÖµÖ-@±Xa3)n®(4µžoÎøª sÈÿ“õu5i—¼Ñ”,r#Ð9ŧú$:aá-m³Sæ˜H»ªÖ¥@tÇ*r0“^7æ9 â´äõÌìöš²§¿™,gã®Äüfí\ïçãîõÈiÜü#Q¿€Öx˜gË€˜iHSÆß¢Í̃2 €áI€Ð-|CE@Qh” ÁȼÞË©.‘ç–‘hª½pæZFºàŸšk·[]v6 $ŒSÎÙ× ÒªÕ]z°-ðÖ·µ‘+k\1À^­õÏV•†W&¡ TÜþ•u§Æ_àï „h(\›a0}" ÁÍà6T‚¨ÿ×µ´Qƒ[T#Ú)®F”b{u²o©¬1°ÑÐOÊu O´sƒ¼[ûÐCA¢ØÌâ•Ì B úÇüÇÊ£ ª@çúx¨¡D44ÆC&±ûŽîhªº¹Òivµç„3dòUP¬îõM› š1}H0÷룡G<~?¤iMæ•áÔ§ðýÆ>p®ˆ?Þ(’mÍöm¢¸¼.]QÒo»qÔúzrKƨ©úNœL­EºPûdNb0Ðñ[óÙvz©÷¬4 ¶Õ©~— ò[R _ÙÓb€)ƒú=úºç gÆ0OD•Úe&±þQЧÌò‚RQ ¹§Ýx23êv2!jé;xs˜g©ô‡åŸ Ó‰¦wÝk¶ñûRü†­ðs¿g9‹±±ïØh÷û^¶‰µ¨/wüuÙ¨/•Éz Üì’­c«‹!Æ?}L|+pz‰IV+¶ÙwOK^ÏB#7¿–Ýà ׫ÏÝ>˜eú}È„33Yb>¸ÂO¼Ê^nÇöúv4`¦K@Z/kuç›3€W·Cà˜ã(X˜û4RQhÌpFæŽX®CÖ‡*jªsI§ÆóЦ.éÞz칦àܼó`••W½C1{W³˜/IÚPÜ’˯ãv3¶í5bæÆÉC RÌY­o£ˆYg陦ï\ ôVVÁ…7…/üŒv¹´ïÎG«O_g—lB@íY­m%䕃e-pŸÃÏÐYpç› ¾—kÖé1\»ò§ZGmq!1¬Ÿ4ësR1·ošÎ$C @ߊ1úËñM’˜±wLÑHåøŠ‘§û«z¾ 6K »mlÂ×r’™g=o6# Ú°×ìÜb+»ôDv‰˜u¸ö§Íµ©5ÌD—Y \OSþe‹=)´ç1‡R£ÚV’ヂìÈø¸‹Ø´z2áWËüÂÍIw‚dˆU•¤G¿¢LhAt—þÀë\Ò¥S\I¼™>R›¿×‘Iníܘ]VXuNŸ|„à1?R¬ÈNœj(N›ž¡Ý«¦@T™An÷;m•ynÔ×E‚Ìæ7ðHú´oƒ×³ lÚŸ¬š­UP;…TAÛ!dó‘·ð|ú¬ûÈüö·¿3ëYåuhÅÏ mMƒ@‡½ÞÐÂæm®üF¶Ëùw´ÐV—ƒ„r¿|R°>î]H§qÐyCv]<=ÏÆû1áœá l8 äJÖš ¿4Ì|˜qþ'ê—Zx.ùngÖél峃·N[ΧֶBg)Ÿ_€øÃÇá·Kz}]fS«0b´o ŒÌpQ¨¡ÓÆ×º9òácé³rÒÆH/E»Yë®éÔßø[»­bÚ‘½â+Ô`êõ,VN°Øé8Á>ùÊ2ž@ÿô8SGMº»xc KØ2úXëäö2Dk»yÈ„ëŒ}¨é¤©¨Ý/¸tu †Å']‚g@Ú¼£$y‹íÀ<9›kX ‡€âŒ¥ð¡ÉS§ƒÀ°Gwšq*N¢½  Ø:WS¬.0<å%:ëd,Àsá6Àéµ@iÀ;g· ø)®$0ß ‘™™¦®_‚À8¾ÉñÜg·Ý'ç-sî_ 4A}Ì$" ·°À"]Î Âï•¿"åŸUz ;Ñ›(©¥ÕWbއšùlQóæZ¢H\û@05ýãt3Ì6-”èd°!¬ÁÖÜú½ßû½ë|Ω˕œú„Æ <ÑÖ¨·¥Z˵UŽþt#™]¥ó“j)Yx·Ú÷»ºËÖÎã÷Ø‘©»Šåí ézˤF~W[Çxó§qà± \ÎÝH4Y3çî$<(¯?;ÄÊú‘tRâÑódê–Ð~%ÓSȼ.d1©ft MhSFÖøŠÔ³0PŽÎäNÆ©9¹ö¤''÷?ä=|Cº=fÔ6–;¸$“¦Y}ó:((¶¾õ”¶˜yPy:iÅX<63KþlJƒ­´³Ó ½CùU뛚Ï(±mz7C3À‚çŽÑ¡±Õ1¯L¿d¿Â ²Õ'T·§˜öK2É¥¯ƒÌ£ï&1³ä¼q§§ìS‰U@žÅ«€…lñv”n ;Á÷}z ÑvŒk™rÛÁRâ˜LvHlçÒ42{k6SŸ˜2ùΉØQôÉŸD°Áú°ÊÉÄBsþÔ·:0·Nöñ`½ˆ;[Ø:Æõä÷{<(DÛÇÆÙl.¹~˜Xóð ·Iþ.óCj·<( †ýùŸÿ¹[3 - ¡œqÅì[5 šT ‘d1ã갷<±LÅu`dϽª*isÕvMuðÕõIƒµÉoë7ºS¿ÃS¥Dä‡ÑeÆØ–ä¿• Jû“¿%­!¬Î)ø­ÌŒÙ0×îmí¼œË!æÖ˜ÊÎC˜„ŠHÖÞ5øm™mLJVR š 圆¯dÞØ©~ųö¬µi+$µz§i©7´©W³…dcÌ)|­;¡Û/9:;•ÞXÖSÅZÈÅ­ )µY&ÏòßøßpK"I•H @Š“8…•“†©ý‰²0&eI;›a›“}eöÖ±<Òo3# RåFiÔoÛwá¤þ’ygëa,þŽ6àâVßçX²ÕZ/s¹Ä|î…ÙëÑeR®{ÖHžË%#!“„ë'"Òâµ0ú¹Ât—mH³Êr 0enÃH¾Ò‹¶Ù úRStÞ¯x{JÙ™G.}ǬÐx;}˜Ñä0/M@Xla(6 ~G½¯ò9íÌt‰ºÑîŽxdnojŠ=—Ä&ÐG p .’ÏRSá ÅxÌ–™¡¾´…ïåÙr(Ðüé„$–[J§wï&³™ñë¿J ‚_–jßRKƒi!o1¶Ð2}´}iOæ)÷”HO„;âK¤…âÂÓe]Á+Ùæ/`¢b¼Àʺ+ˬ›1“Ÿä)j?¾©ç8«+Ѧk0:Ã4؆“ÒAf&'3"ünÞ{£<“'“Ö&d¿êAÖ)r ý{5›†h-s附×Ç©ÍLÁfد‚²‘ZÔí_3ù2ûžÛê¢?+bUÜüH“Û×Ð3™ð%¯ëb¼¦çƒF~›c2Kô\ìqËïòÆ39Yk—Œ9Zë ÂÀ«OŸeýê¼Q_"-µìƒCl@™ðt&?ÀÌ|ž£ö€Ðó*û¤™Kéˆ\É`§é@»Ö°×©ÚUø UWq‹˜žEþœ-ÜÆõÉO~²Ž!‰œt¦@ÄÊNNº Ÿ ÏÝ5‘Ù³–·}|-Ö¾£XÎ]v½5émËõÓòþ“œ¼F¬mÓÈ‹J¼ÐåuéDgWÒf t5¤+y´j€¸¨EŸ±²º Û,'7„zÉYƒÍx^›Àù±)kj¦®¨ß 0'Û;\cñ[x9/E ̯)kâ@úÅ×?çŒPtsä‰9X²È3•täš#Ù)¹Ì¡Ì÷´€Ì¶Ž—¡®Íà¾YÛ•§ã o©HŸáïx†$gè´wíé ܸ(ÖþØØd&CÚPÝÂ>ä ‚þT)ö×&Ä/µMÛ@Ÿ#¦v»ï¢2/©jÖG?Òo6# ²ã…™õÆ»òR›O˜ ú@B›§ñÖ KQ¯~ £ Güþ¡À­Êvy%ºL.ù;mBâηM¨Ç垺†–YÑùtI#í‚Ðv[š•ÒÝ–7—iI8‰!쌜³"$ýšï)‘ƒÀ©‡›afÓ` de]½@½”|–Ì lg'֥͛ô’ó^(Ø·÷Žqô«Óq6ìŽ )Ïnz‡hS8`C3d_c›“/¨LÝÈ„£ž‡ódbþ-·¦»©p¢Í™"{ÖÅ']¦Ê³K4|¿Öi12î¥çmcµW› ež¹›LÅ9g5c ñP,ÃìÑSᔳ¨}Ý…™3^Q‰¶G—S=’X •Ó™5QEïýYÀ{ÏÕÙ°µÊ¥tAΑǪ"< $s©ù6¬i´=dÂ+sõ–·ì;Cê-¼´Ï™ÈoGÌV«Ø– ‹€vº‘Þ¤FfiÔ=à°§Ì&(?³s[íqsG½¯ èÿ ͨ«»õé¹§=Y›9RñÒ䤹Ð%pú´Lç8@q€¼MgówU+møÌßwíåÿ™I¤ ÖŸ>ñ…d¾ C} ÷95»A="âúúœŒûVD¦ÂS·~GA:è)8æÚÈše¶2³ì¯w×ó¸å]—Û•ËÄÑm×é{5íø„G·›ò±Œ6ÈÕû…¢º©pØQ’ GŽF g÷×ý×6›ÊXíÎ¥J¡y*³­7-Q9tØuøñûÏÀHƒøZZ®?=ïµ^ 0#¤¸w‡³ùéqê)&æ\^žœ'×@2ǺrYXÖyñµÀèÎW…ýZ@ÅÈ,9a‹%³–”Ö¥ Oh™æ+hV2Q9GëÂÉLSW¦Ï*$gh)Eð`ýT¹›„½–à[žºŸ5ùQ`“à× g2 6Õ¹+Î[ø£Q-_êë M-Q§WÖG äíM=ǹ5„à~ƒ2@ÃL-mà}e±©ˆªMÑÄñjê†ÒìSدIHb¡_'¨ ®‹>å'ó$Š{è½jèu»mBÎoýÖo™¢AÆëf„'¬¡eŠâv4PpõDùÚŒPÍÞ©‚1£î]&' {âZ?¦uÅNcÉœVÇZ; †Ùn]Ê¡TvÅOdmYæÃ<§4˜ü€êVO¥µÖ1­‰„dKzÈ„Ç)ú rÚŒ"øødÐKNL€nhk–éÕo€\ ¤×jöj'?…zʌۀ‹Óíü®AJX[0ÏL—ÄEå‹1¦V׃tL•£þ"UcÐ7»åí™N¬n7Æ&6ì;@6oO«uÝ„–éð°xƒ×bC¢èöTW;…éãc¹ðÝè´ÙW|:ˆˆNƒO&°ÙømQa¬Í(Þû“u·–dñ6âfXå»Ìå¯ëuØž:~¹=ëqù–3VEí~ƒ[A¯`˜LÀÌ#Ѝä2ÂÆ:ªET›{eNL{·òä«4V>Í œ€ZÒ÷­}ÀxœÑ:¬‚¬¶ O9z™Öl†ekaöË\û.( H·À·Ø(o´B¨¢­D÷ÐÆ}€§Áúxï¹Åϳ”ðá0Ñ`íÀKN¿ 0ëâ(ŒA'c·ÌÀñP1,¼élæˆÎ¬¯>Д"¹2§î-f@.¦GòêÄ­gey;(Ê<>Cë#|–3'×-¿,Ÿ}q¾s´v;m¶óYÉ ÷-Bßýœgå/§Ü©'³]ÊÌÄÙ‡ÅxŠ2s3}Œºtœ…0mÞ@Ãiê‘ø@f`¤Á¼¡Ò‰”ðºí¯f‰¾ÁYêq|J·1ë'{…D#3Ë'oºÓ@5ÇvíPÅüvâX8¸Æ)ë’y°*é8lc[ yä¹',[wÛ WÌ?lŸõ%¯¸µ˜ ‡[s˜ÜۀSrß}ºÛ°|F†0eûq¿6Z,.~Ȩ8sòßÿýß1Š~ǃ/ɉ*¼å)‰ˆ¶¡ ûÍ‚=SaZf¤}·À¦c“›R…½~Ýþ¤Ñ-iÓ‹ˆ€–iÇ¡JÃÑÎT>¸D³×–•Ρ¸NU]x -#ÖŽº—v^““e•¬u}¢° \˜jqB áJD®µ)N¼I°öÙוÕ×3íéÖ×z”q}Ðõ4ÿº!­·ôt@äѪMêÒ9®wæq÷Ê Œ4ºõ¦†' ³ TÒZ©ïA¸ .æ3OàŒnÅ~‘ ˜à:Ê/g$šØjÝm‡µSHûP#'9}ýeb8U`~Ç’ÉÓ{2×D. !ðœJl%kÈÁHðµˆn âœmÊ!ð&ÄïÀû3rv÷F+ýú¸{M¿ Ð+ìFÛsø&]Kœ½ NÑ¢Àˆqunˆ 8Ÿþô§Ëq¨ Úš/¶Æ6%e…îÃ=íîdï™6×DŸÄ¥ªiå S^"*ÌJcy@ h]û6‰”Í-ËìŠã>d3¨šZ=gš‡+z\‹Ê2ëÑq"#ÍÞ¨.–ÔwN¥ý†¢ZÇõH¿Ö Œ4ØÌáÞRI ,æm‚>OÕäOv2yÉ¿9úz E—A´Ñê¸Ì]¯Åã.¢è+åÓþäsÉèdìIÅÇxŒÐžÑmo…™Þ'÷÷Þã*y¥ÏîVZ^8 X`€8 “ÔW¾ò•¬Á³ÝZ¸9 .vÔéeUÌOÖ™.½å™`¢XþÒ£O€È¼ô¦¦…g—#¡Ñ­œã‚&d0–Ñ¥ÉÙÆ!¶‚ÅF”ô UiQ£ÄZ@OhÏÅj¶#I[I— öÁxúXà4Ç֮Й9ç|Zò43ï¡Öáß(…›2]itãŠx|¬xšc p‚¦ÜêstZø‘ùZ3V™ØþÀ­‘Á°`'ØVƒ½‚D)9 ?sWN±X4µ’wjÙÎÃgŠQ©e…;á`*‚—AåÁ¢±÷o}ë[=ÚoXÔòÉ1ím·qè€öO£Xç‰B×rGÍt"*Þ†„Ï£K´Y\ã‚פµB·"🠰îD[ƒ/ðòe „£kA‹OÊþl/ƒT¢!¿9j³lû *qDÔºÌÁÌÉ„ü¬äˆ­Ø½‘”eÖya” +E ØŠ…GÛí8¬ˆù^ËØ®L+¤ã£§ÑNp!Í4›:?¥ËH°©pÚùÆÔÊ× ‹§ŠÌvV9u¯»XÆhbtŠ/(ã/Þ±åqùf30Ò` ÐïïýÓ2UìØË¢AŸ£Sà–‹ek1¿A#Ôœzàá–‡*3>0”÷ORšN¶4tÒZ8}lí®?"кÄ*œÇ» rÀæUÙÿÁ ¥g½Ÿ¶ÿ¼2 0›^C³M2GtÔÏ Â²¸6¨¦.ùÃécÇUüæ7¿Y™JeÆOd2…ÅCdB33?£`ºô¦¦äsLÄŠ/ Ù„Œ…°x¤¡º|ºLf³ê7ÿ@›BòóĈ/6‰ä5ÍüðŸì¬N/ŽqûHáSø9&fZR^ ÝFAGãVd¶)þÍ@Þ¤öúK+7UlHqÔ»—æÇþ²'Æ>y©ðä ©¥‡Ù6qžëŽ©SkL1mñ¡©™ü×J˜ŠÉ ª´«‘ùµššÂ¢£7í¶*›×ä?o96;Á“‹&x&üfÚ{DeÀ¶BIÅOÜí2ïЀX­ä«ÜÙÀÅôžq-”62µoWEöè'²YyŽÌÎ7\O½xÀ]ýñWQ0$žG”H[ Bwi©¶•ϱö|RŽAÑáŠ:to6ú ÞõÁ~x¯9Áþ V™vmêe¦_–@èîAˆë2h5ÒLtÙï,’#q„&…¯m‘¬î@Î=oÕáY½öšèØYá"PÛ¬zô)SLQ§fsòÞLóÇ×kr¼h8ÉéXÅoÿöoKS¢d‹ ¼–'JoÊé4ˆ-'¼ ³+.¤+ª€鮦‡ÄÛÌÀ* JsâOÍÏä’lsËãÔh…ØÂ½¸K~SWZÏZ]`ПãÇ>ŸÓ¬òžîÙ•an]Jôí;7Í哾…Û1*O9=¨ã»a6ý/^|3ëÓC]àf™¹£ŒÙPÏ-üµü³NwZTít4Ác¸JŠa6@¤1r2|É'æ DÈ›OýQZ Á6ˆ¬»àÊa¯Wïhp®nÏz2oMTÆ@—nš““Üt¥zà©ksp2±¦wÄu“÷æËF^MF-òñ±Z¼D :ÃwT"Ó1 ü±«Ï%LjúK¬âBYÏ‹•/¾»ó6œÂÙc_ÑÞv£ñV»"ûc[™õ’t7Fõ×âcÅ’á’šyóË_þòÚìõtÛ·-Ø[6Ì\¯²ÝÅöÕ5Ò $#ž­ù!oåo¹„Òñ!³ ˜™ð–y»±L®¡HÚ;"û¥ï@ª· À3¯ ~Ó2LàÀ‘ëú à— -i!Æ2>ç@#èX³[ˆìç`ç7¾ñ ¶¸£%wM¢›a¶/}Ð Œ§À¬åõˆc·r‹°çöÅHfªl‰ŒÑx£ì3Zºø“u»u)òÍÖæË¹|qa:’´thBT±QÖ·J†’®8´´ò|óK¿»ÍFÙöæÔê@R%ûNè©‚0˾îqªºi8ZxÛÉZwçëÏ«ê%7t䨫m$a\@D-š»Kçh¸Ø!ærðšÎ 晓•U7Š[ÓÒò®q=~ßf6i°¦¨ú‚CŒŠJ˜bOD +&^«li¯;hK¨[ÌlÅ®\šV´-`-ÖyHã)q½ûdz6ö§[Ø“-< \™â #6¬ ­J|<ÃÜÅ–St‚p”ƒ’qr…“Ç?ðOaž XSûé"G6ú¶çÚ±{«¬Ï®w¥ W¡îœEZ df½CáyãXœ’G;JÕìÛ3ÆÚxéÕ¨¤¿ö¸Ä„nÍŽܱ—™óâBëÄòQÇJ‹)£à/Ê+*zÿÇZºDsÒWkëÉÚ)Ÿí{””ø‡¨A9Áv-Q9ÐÕT)rå$íî©#428FÉ£ÏÝÕN7¬5*¥äa:<+04Oeâ¹-㼿pœÂ~X½É~ìû $ĉšÒšß*íO•m Ë×+[)‚ÄÍ_·Yå{SB% ÁÄyQ˜‘¯7˽$cøJ“6¡âàç|½…îæ ¢–º‘™>LSnµ&(å“FB›\ǃ£j¢gMƒ.í…ˆúÑ~0qE~”÷ïÓô{ï!û*f\ý ìÁk›Ï7ýû¿ÿûFj–F«5€ ]bs£3oíè¯ææýCÔ|xÖw8™^߀T2I@Ÿä&—!÷èÄ› ¸£‡µûK·L ÖƒeM‚ 1-&Ç”NxA\~ò“Ÿô;G}šžÆ-Œ?9½Œ{Ö|øq¦‘+—|oän~¼ýû?Ù:ˆI ¶Y¥9JZ[¿/ÚÁ׎bZþGžhN†Âñq¦qMüçþ'ñszOš²®ó±^ܶQÖOžöýµýcZã)¤FO;–óý;VÙr°úúp´À(™·f¸-ê­‘ãeVÙus¹²‘«?ržœ6ÊhÞV> ¶#ö)ä¡‚ŒE«Rdµ@]¦…^1àþE a_ñêz:/>Éðƒ§twÓ)Tw¶HËáHÒ)“N•†³Ç€ÇUO:=W™ý‚ÙñIXÅ×µŒ4F×Dõ‹ÁöY— ~‹Z;†à7—­…—|ùâB/»“®O·rgB1‰ñ†š“¬•ˆÏѪ¸¡Ë~“*ÎéX.(­Û͵Ì9>Û#Ú]¬#O®O/ZšNžÚÍ;O‚ ¬U¤SQwâ¢å—Œ*¾9V’÷iGrmœ4³~cºjOäÂÔé id¾„ãrýêýöèÇå-3ðä&Êa#ù|õC¾¥eß-ô‚ÔåX2åpðÂrýÜÜÝ å9màƒ3[;ŠïlµŽ—Ù1”WË]澄ÌÕš½Õ*íàì.£Ô¼d$‰q̉ãYãACŽ7 ¼3r}Û­ÙÿS¾Ú½ôÖBóAim±[oŸÝ%{Tkßb_;/d\»/¨AÃ\ Ÿ4|RZ«Hç5šò¨´TѵcŸ¶Í§€½)­å¼´µvO—yú©!‡™šbZ1&*@Šy%ÛƒŽBM{šueü95÷ï|'V\;ûg@|^a‹}Í|¾éWCÿg¢šY¤éX­SãÊ«mõ éˆ„ÉÉãX‡Ÿ;œ 7žà«7‚ Zi-,ÙÁ¬JcM™/:ù¹•ì2% v·}?¾÷{¿×@ž ëÒíDÄÁŠ1du1çéöéÓe*òÁö+·Áe ávÅAžP6—ÍÑãÉíìR?ùÇèÛñï“û«¯@ƒ^¥±¼Ê]Ò¶, \Û\œÁ;ób+ü ‡ÛC#É-ßåXMNã_$Ñ_G‡[#…ZÜŽæá É›7eÍôØ6Rc<%`ÅŠU£LÚs‰ô6/k-ŠÀÿÝ0ÙŸ·O¼D°oBÀ+Ákç pü$Ÿ~‘ÑüÙbOg™¹¼2Ô½§d†¡=.ÄéúV²#ì«ÙÚ÷sƒ×Uê髿¦êÅ8õôâ®mNXa¬ùÒiFÕbë(8Gâ5²=±ÚCAÊíØYgŽq>UTª‡¹¤jÛ™êáÌm/v7’cµ™ëðTÙOO?õVU8jœ¹’`o v„ØZþÝò¸­ýÇåK)¼Q‡ŽK3ÁwK.tK"YHâ—9ùÄÝM~JI}ì!kûÁ‘H`÷è2ÚÌ(åÍ1þaá;Üji%lJ”|–ԚУíaN§/á9–aŸ¯iÌH’ºH]ÁšZK6Œ‘KåcO§3ðȼe^kã ͰÛ\AóPP#bž—>·J°6£Ä|%ÀÌHf«>—«#èd® :¿(PÉ5_:Ôu•¡ ¬S=k-;bšÈ¤¾õ– v'+’d,d»ÂœÛOÂ¥ÌÙ«Ì¿Ä6–êÝÂ1½æDXMÑÜwÚ–ö1Ä)ÓßfR[ûð\Òà¬nì4ÍkŸÇjÁ•ô2ÌÐÀEÅ\ oéNÿ+†IæqÊ–”‡Q{øæ™á÷'ñÕ7¿§A>¶–Ÿõ¥Î`% ?Å&Ä䘢ŒN& vI¾î–Ü™4­ùÓBÂIFloí¨IÌJù}ß÷}›‘ê9Nl£ö;ÈÕ"mìãÔÓnØñ×l𪥳°ðã Cu˜6(»Xgƒ;hùD×üNÔo'ºSû2ÿ®%KÇÇ®|«°/W!‰>µµÀ…/¹÷ÔÉd ûëPîV·KjA ÍXž<"ˆF“L¯§™ö-Á3½‚ÂLþšx­­m­øHo3Ùƒ^Y¹±âgʤáç*orr@]XÀ<`OþhW (*gSÚe¢¿ÑÖ°S¾?@t»¬Ê\å÷t€vʺî©0N]T¾¤mÔNàSdzýeZ.Ñò÷ˆ„@Șwé¼?[°?P4`kç%_¾DÐûŽòæ¨ ï­/~ñ‹‘Q¿XíêK¦ÿÜç>ç–­=ƒÛ‘ŒèzµužúD¡iðßêþýßÿ=B·¤³ÃdßßµA7eü©î¯-úƒ{î+ íŽëžW÷àMa7Dù¬FEJ T *–z~°8K¾|nùƒÎçAÄkÍÀl™›¦>šˆ×GsEË'y‹¶–‘Îêåµ®¦ ””?Å"SlBÅÐáM暈ZNÃÏàËív›ÓQFÈø4øû&øõ¬Íá«ÂDwKþüñé å€÷¹èKûñŠ­ýÇ¥x¡a*xhN±"&9zÇrÖý © qãÎÁšùóFRöæ°-Î)+w¦Vû`–ýAw„ž£vÖÕ5-‡M;90'q»ËUFk¥y¿l^…i’l*ÓT'.Zó! ÐDU™ œ P³¡ê‰ØKÙ™„ÙüèÕ4ûHÜ>o¶Y²¶Í1¤S%€Nr‘â€qÉ#úÙËuÊ| 0ÃíêHfjm‰>&Aã°mŠÉ‘o‰]rW!éÌZ³’͉kjA„§Rb^©HÚ<øëxÃÚò,Í ÓÄÏê•m5×SÝ’‰æ·£¿Ó±ç’HÞÞ¼zt¾C•ù—ÁLVÆhàôЫvì8ؾ“®dV‘‰SYflt W,ärl–4~lçþrR:4dÃok0¦e`w§›:·D„¿4˜ÿÈB“ð²¼2/.9s;²®©œÒ%#å¥ößñ|“³ýY¤!`˶Î[°ÊØà0“vêŽhºkù»…IJ֞úsÒˆySØ—µ_³9›‰¬Ñe<ºèkÜ×Ó¹õ äÉí­ýÑý‘®ô×N;Â1À Ð\NÅÝÓžÈdMÆàÐaжb¦"{Iõ:7¨â›msÛC_øeÀ/š½r«q$kw °™(/%’–ÔÉém`o(Y~' ÃuzAGÁHÌD"ì)Pì‰ èü6F&;°V@Üô‰Uq”°›P­µˆëÝÒ`¶ÎXÈ '[a¹ýa³<ºœ] àO J8>â‘óBBFj„‚ÉÎ!T—@tcå˜1ù¥{H¢Ë7𢢝·3} 7TE‹Vƒ—Ì&J¶ 8j‡eù Aáµ°ÚßËi³×íõ¹}Ôˆøô§?­Ï$ÓÛ !åMù‰£õ°0ãî¥ýÊi“i ëȦ»Oª §'ÄÌÀÛl“dø’Îïè9ìÍÆ¯iÈ&tä<÷˜à"r 0ƒ‰L554y¬»æäc ”¬•N1¹ºF¯µ¤íIž]&÷Kf«ârÿ¬Ð ò×Â’èÈ´µ…“™bH dÏ Ÿ¡jw;åV‘*ò¥‰æ'´Ã4òŒ¶ç(d“·9+v$‰Ee‚³ïW¿úÕÂÁICÏ­ÏСX.C`úhV‡Û°{Âyemä.Ó©ÛrÃLKã5Q©š:œÓžÙ[ç$‹–ê6 üAj¶½¸ b^¥ºV”N‘w*Ïl%ßåËFšó ¹­Óéy!g^2™„äáKxBNwÂ1œÌT/Ñ9ŽœßX½§â>ÖëÀ¾ª;±d.ÁÜõYEÿòÖF¦%˜µÏ ò´–<¦±èV4¦mtþ^ $slgrdlËÓ¶›å¿7ú»©u)ñ6›Ý¥6_T~B#{$‘û¥Ë YU—[Ó2ȶOd–&°·Šd‰pènœùtü[S`¤LR¼œ»¡DíG«j5såœrB“m¦väÂ[l’ç´)${†E×[¥;îÞ—{Àï i*[ÿfà•5˜Dãþt»pŸøs^¨@èM‡§WÁâ‰{¢zî‘…^Ò{œL ïè{d-nô#é@–ƒ®xþäí­µ¼ò}†~ÔLå½isOÍI‹œ gÕÜÔóŽ;æòj=O¯Ú–tu=”‚›7´9Ô¹s™óY¤,„”…ù½´+P·«îw³FÎs‰K3ðö$¤X@¯ouZzúÔ,|°~zäROÊÇ©sböz ™ÓÖFö[1Dê 2á ±ÓºÙñ€6JY>§N«ØV#á&®UÒÜÛ¶p2k™ uß`àŽÒ­¬î(Üyôrf ¬uŸ]:ï±VH‡eÅ1´´¹-pûr½­´}—~úà¢jò3g ®µ_ýÕ_ù ‘ ßR¼nÔÜiíy%ŠÇØgLÈŒÂDÉ7iÙöÈG\¬˜½ ù„*½0Qùèb &ßëëRk›…ogïòš¶3Óg‘h[þVká"d®{¢t6¯ÏxüÄOüÄ:´¶~ !¼¸iFÖ’«ø3?ó3žrÅ ¢Ë`bXY¦ÚSÛãúˆI§ à«i»÷âòy;=—8U&q 0ӆޱ¿üå ¢_è) ~ qÂ'óè[o¿åÝò”{-ä‹ìýk?¢ÔÈ¢›…Ð$¤âTr6t8¡ê+Ïï4¬üQŪ»Z"!ðr&¶=ÑŠÜÐC D¤Wß@IDAT¬ÉÓ1´•0Z·-·ãñ„i6¦Â`óƲ*C¹» åj,_Ã_kÑŽ£Šž*Ó­º+Îs‰u^®@˜Nå~àP‰¿‚5åÈäÅô_ÝùߟÈzµ´p|k`TÌä B¨f!ÝAóÉßÙßS+f[_ ´áe¬È•˲‡þ)H4NÇYáŒãúŒçÊéÔ¬aÖ¤º¡ÊJQÉÜ–ýäOþ¤21} :’c­hQº*6WéùæT|$®ÌÀµ5:}×qV0" ¶ßd˜òu_ UŸ—‹˜¹%Ìé7ïÐ+~¤Çê¹ÁÐpçþt<³~¬2B •xj¬J‡%ŒÎ4|\­ŠeÞÌ…f”)Jt>6󖤜cgžKŽ´lëgHt>m: D ~i‹ÝJßÒȦa…Yê†^TdIVÑˆí¹˜Êkpþjßåh”ŸË¾Y?óø0ÞxSÑ4š"åVž »¬nQàx!I±úñ~ÄÞC,aÈæ¦Œ¨SšÀÒõéºÔàµñ]jÿŽóíæÑ|¿°^ð, qÎ$$"uå¤Re HLØq×ãö^½^YÊÙ9Ú—MD{DNÎ`ªžØ×öÉŠ„ ân§œÖ’Ù·CªÇÞV+Þl4;ê^ÍGÝë³þéÃ.ãOúo¯{Qég kÞò= †>戉††’ ž¹; w7_‘t“«eOǬÞܵmÒ–(u¬½ü´Ã­"í£éÕ._á±Æi_¢év5¬¹f[éGß™à‘B3z´ 'Sûãcû«œÊ8¯<]m¥ñÞîÓú ˜¯?53I#S둸2ì¦èÝ…ÿ yWìi±dN?íz¥WÝÊ1‘G½ãøôdŵ@ÑDÐ ÍzÔÚ®.¥'º ¢Ò‡[Ú1öe6kä)†Ò´| sZ2Q†¡ç+Å•Q«ÝQÉQ!]Å»Ÿÿ©O}ʈ6Ýè<ðÂÛ/w(e3>U#]£#ó÷ZY}7ËC“Ìü˜{»ø4"Ý©˜Ò:£yŽÛèÉêÂôîÏÞ÷0}ã5p²ŸDSaZ´Y„gÓ•bÂöñ SÝ} ›!Ì4qº•5YQØÌ¶ ¿#z•[yίí¸Úí£¯höüùÌ __KK*=eê<:!²ßQÀ Þoå,^â6"›¾ ·ÝpuØŠu‰q Š =yÝ`†¹µ{\O^tú¬cfÄ` ׇÉÛ™œYÑjŽ'äÕ MÉ[ìöwË}-Zí7–žuĉÎx <ƒÛ”“Aáíä'†ÅŠÅJ–9y.™îœ(eæ!Žj·P3­Ù`m GÉl=eýk`V õn»ŸNuÑá,4µæo {wýæ'?€ÿõéôÌÀËMAË)›Æ]¤™´‰9Þ´Iƒ…öm‰M ˜„ôŒ:PÎÔ—°‚Ç p ä¶0ÍiõÁ»­áú§Jü£;¥ƒºÊ7I vÄNpÅÖyLÒz*ƒÀ©ëÉè¤5ÞØù´ò]Æ,Ö„=hš}$®ÏÀ¼Êàõõ·Üõͱˆ6ÿ"é· €™‹n¸¥[™QÝ×kµ¨o%—^ðt1]ŽwOs²J±û©%}ZFf®\Ê\ l“µ_8ºRÛ’¼go.=èϧ)°Ö½ë˜íHR±ÈlÅy33°ºÉ™Ü•¡Ã‰A‡5Õ–" /Ô3Sá ,(ö݈,Tš}QÜ#ÑnìM…i 3eJÍ_´Ï͵‚pxBc.Ͱ×޳ }¼S› ŸçUˆÐƒ¼ÜÙz§ñsvÈÕÛü'ݵ{å/_Pbañcäl8R•¢q&±V/¼jæñËLNþA í¦™k"©LãØ×[·¤çÿ´·”ßÊ?<ýˆÎ·’7^†C4fŒœpcÝc1_Ãl…Ç»œK3÷ˆbc×19–3Ö-°­ŠŽÔ…4ưúmvN$p(æüÙƒ&îÖá½kÜ«ï,â™xþ¼Ê”€¹wEjA=w×üI§Ð•夦ÙàGÞCÇðÈ…'màýÚÃá"À>¯– þ¨ØæéÄÌÀÿ§uÖ—Óü×ýB" ¢5wÒÉØ£'¬›‡o|ãN7ÝUƉ;¾ÝÙ[Úþ¹ão»TÑ"”®­Lz +9ŽÌÒ2p*Ö§øRújAdȌؒæY¤ aEYÜ“àue°|P¥sårò¾žLÆMÔ.7”Ÿn{¸ÌÌÔz$>âÀæ|/Á;â“æ×kMqþÝÀ°ì*¦|ƒFÙ׿þutB5îWZÎí ™­á[®×ôåë6ᤀ§åÉ{åOb+F”êDP¾$"F/Ûª‘gØê>£KÓ Hw…³Òs0+ñøà/vaŠ$:"‚‡xƒne¹j¤–<¥¨ +åÙQ… ÀIà^C¢À6 s6«@-¸—¨>3™Á0[ô3šÀ·éjƒmà3¦ÅäXz&Êt™4SW½ÜOøzQC­9_Ñþ eíc`gŠ„#ù°u{A„Lm’L¼;/×+î»DUtr"Gõ–×ÖžQÚëíÕ,ØEe¦ääK +˜ÄzwKG:>b+érøØ$Že®äPHu÷–Žmí)Ö]fø{+ð—3½hiÈ´ó¨ò63°­X`L›P«WSãFêú"|0O™?û³?«º×*Óî jç1|Ѐ,À•ò¶×üQAÍxW¥[ˆ!Ïv5só«K[=Yqr·ú •Áfà„^RÅ#H^ËÔT =‹Žiéíæà=ê+ì_ŸþHÿï `/öÏš&¢“µ¨Í9šò|ofÉÍ”„ŃIã.Ês†Ç9˜×ÇéÝóŒq§_Ѹ¿ÇÀ[é98ƬŠ Øa lÇ¢rlƒr¡N³’¾¹¸sE•(8ÁOËEˆò8çæ{P'Rä`¿üË¿,arGºìêtíØ#}}à¿{~SÝ]/ËÝŽ‹xˆámü¤w[;ª>çÑoéÃZƉh-ŸãzYK®iÔ> T’ljkáIÃgºñ+=ù[¢£•¦H ‰íÖ\vlÙOÎèD&¶GfXíL™g”(4”;˜r¦ËŸ¸Žrh[{亂wã*Õ£[àfrˆœúˆ#%þ)ÂØ%–ŸIcÓ‘ó|Fø–]m§03|é<5L‘‰2]r’=+¸Ôó H—:`•g´@2â‹´ ñ#Eºµà5yݳF:ÓhÞ¸Ð.=úcÉŠ2׳…æi&³½lº„Œ¯¸ŒNøAÌaªœ&úB}+åÔ—g­5±dôCÃÖÖ»O¦r½Ê\F/}ÔêR;E±Ó>œZl.Õ=ÍÿÚ×¾¦?Hh ,·³ôcƒÆöw|Ê]æl¡S¼`,¼ ž%ãÉœý«HrŸíe±bðÏ!TwÁBé<ÝÀÅÔFä:‡à%F²]o`4ÕN;þ)µ·5(î¦h^—|Î' ;a‰î‚Öêw t(ý ®Ýå›>1rÂçl'ç·ž¼ðËg k>Àw–ÒáõX°csXN¤æSk¿u sv¡GS¥Ø¨Q¡iÓX£,ü09m=¹·¶ŸâèoÍÏS®º¦@ÇÙSÞX­«âƒåŒ”\—O‘“¾¾]ë9¨«QcËW½’”ÊóåFùýÝxBlúöH|°›"[M¼¸∙¾í &y $³˜yݦ蓚"ÎÛ]FóVu­>ùô[\F^ «ù;=ÕÃÐvb©Î'%f'›¯ƒ¶ù¡ü';ö®°¯ŸDãØÙqÿV1™ûù÷ë<–€¸BQŸÿüç“7ª>QÈÇk'ÌM:i?œ:±yÌö<î]›±¶?†™ÞÚ„7ü¦¢i1E&ªý(íéc¾#K w©W}‹l¾Dߟϱg<±=ÚK÷êk¤ƒ‹vâRËï`~D;œ¾x&sƒª¡ÛØãІ E\fXÜGêA‚÷õ3HK¦#µ¯å·ÉäÞ[c'éíÃÁë'®tÒ­4ƒzh®f®×ºrWøJ“cbÑ'»eç_°‘ñ'¼R÷xëƒÝøŽíßwNp.ÊïËC<Òæðf*¢Ã@€Wü€ø§@,,”V=Ù²§7ßtUíé|FÖ©.µ§«kþ¤{hu§®ì2#LU 'L×N», (T0 –Ù¸„Ú7´¤y^ŽÆcìÖÈãrçkÖÞ¿}:“H¤ì·èCk—cPzýëÐÝ<=’{§©Ëý×»©ÖþæoþfͤtiZ]¿´–‚-ÚµzñÓTDèÇãò©çÛ/'ØL Q{FzªÓ¹( m¨½íän!‰¹¤oæ’76œ™{ÆÚ±Gú–ø ¶FGÂRPÝynß:s`£À'»ä³ÈàcVážaVÈòÉêkÎÏ ˜ *sŒs¸–/ Á ¨ê°ZNÿO’”œ:ðzP™–¹Ã A4áûŽèC}ì@Ò1ZƪÌ&tláÏé¸Ws–=¡,›áLUÃRœÜÈͬ±t¬šæúÐx)'ê@¢ѦÎõÑÔñrÔf²¨ËLµüÇJöºv½3ïì]Ã4|žÛçX­MŽ)2Qîš4SçÏÎWÅßÿ‚¼2:>¢J2 ±TLXš¤t”Ÿ÷isîÕ#e¨óËÎ1^iù¼ÕÌD`XÍø’Y°ko‹>š³J*†'†Ð™¥+–4/.•.k—'šõY¥±,Ì0&6.£%9–ßr ðè€!ÑM·b§—úûº++ÆØ±÷Ó*×3çtú0p=ç#÷¹õ®t^›ò²ÏIµ `ÿÞ*:7RßéÙnBèô{ßy„ê3õÞx‚9>&b³LêÓ±vÖíÓ³âûÁQ¶øÈÏ]ök·¾=.o™·ß ŽôCf¿ÙÙhãÀ—[º¡Œ_‘î¦ÌÎæ­ŽêîÉß쳎Êä;mÓê‘¢G»uå¡™­GÅœ’BǸ2椀`Y¡D¿ç{¾§éZ[Ρ±½g”‘sް®pjNZë¾ûiÄ–;(á µ´r;ïah©{ ê‰¦cD i/å/þâ/ž`,N³4Öcl¯QÕ?ó™ÏôЉÝ5\w¢Û=ù”g] hÏ&ÁÀƒVM…“ch©çLbnéÙ†Ra Î[Æžä³~RÏzLÑC埱wù vdu@æqEÜòÄ«L´ÔöD‘Ü+Ó‚]»”­àøazƒÅ”ÇREa—f ŽÊ+OË6Só3¬ϒöâR|¤Y– ·Ž®ó±ÚI¼æ÷+ÓãZ¶›y³òë/Ý£QΚ_ç±÷KqÎ×Âkšªq_=· ùÔ¼‘OÝÿ¦äšxûÍnmíe¦«Qþü$ ¤µ"røŒ´“x)ýA¾´$@`ž™`aó‰ØÀŀô¥ã$ƒš=÷ˆT·¬ u«X´dW~6 Ðw-“ŽdÍ”¤µ‰tÔföˆÚëÏ ÎC28'¯‘ñèi»y€Õm’·Ëgk¶Þ —Aê!©xñg?ûÙHíÒ/\µ>=µŠÂPéÑ5”3ž[B}L*O´µ8ì!³íÿÈô‹Î¤î†ÔU¡A©oã_4KtAõÀCëäSDOLåiýçÊUP2+öïþîïT¡ª‰¹P7ÎnÔ³¸‡•ð; þ×Õ®ý|áé·Ù&ÏZÅ's%9˜¾ÃÙ8ã-ýáþ|K´¶^”!*zkÅž|YºÑqóUIÌÕ™µ“[Si²¯aHÔÔúÁÜ­Ö˜G8Üœë ãÄ‚¿¼õ6Û£üÎwu®˜UNGÀèt'gr+Eÿ[gÞñË,¨B•4LëÚZά—ÂyÝë(š ¸y›ÏµÌ¤#¡& øè@õx2͹@d¼ Ò»_¦©{M¤bkÈèg´ûÒ&ǵ‘O žI•.”Âöñ‰ë“©kGkS’Ûd†£<_¼t¯>ÆŽ+M4"N•w?aÒ¶¿WËôÿ“Ù:5œKc'Œ±hIÏªÏØ‚¼ÏþïÿMÂ1{ÇC™çpT§Bú’ÊV¾·×ÊÇ ÿ6ž[•¹$a|E›L‰?ù“?Ñs&Ç ÂzkÒ†i»ÞV1–Þ6a…*6U®' ¶³!ŒØÇåÏõ#¿VC·´ù6ÛÜõ~¾¨»€Jðß.=,PëºÅÂu"¨ËÞÌù¬ $ ¿‚ gp“9 ƺzô?Jïàî 8«XÇÒS a} yUbàÉó CJ^ÍØë]ð»ž\úà•/ŠŒ2Æëc\y¤3ðÌ`ÍqoŸ“¾$¯ND“φšˆH]7ºí1(f+]]`ùs.v½‹!º…Ý·Oã¡­U:›Šõ{¬µ„FÇyðÛºÖ[Ò}DþÿgïoZu[Ú»þ{sãëùG0ØÐ†ØˆEAÑ€ ˆb:ŠšŽÑ 1*Œ— í‹$"âCP!‚hº¾û¹?k÷>®JqŽyι×^{®}Ù8gõ<ªŽúu”b ÉÛ1(ý¯ÿõ¿NÆBù¯K3™û©èº@œ¬@Òâ7‚n©Ú§®ÄÉÒø Û¨¦ü;ð†xÛf‰Vv+”Íg¸ÕËz$WZ ñ/Q”+#5Â)à€®’x[¥ÝçnbÀC+ï'k÷èþ®1¬×2ÓæY56°5~¸œ* 3Ì›{,’Ù7ÿ§¨ÎádÄ„Ìì>ajCf{ì!¿œû~ÞvŠfjü”¾ÂQ³6E-g‡æT¥.…¾‰Žw[þfý®¢vw„Ϊ13žd´A#,>9š¶ê[Ž}¾1:؆2¢Cƒ°kX"ªÚ$ÜÑã>>¥ô3îþ xÙ·C>üÇÄ„wtܧ72GÔª¦7†êHIÞ퀛<ý 8þr™þ"ǯ -‡G¡zÑÎ5Ê4c€8H‰Pû›¤#noY^¨ÔjÚL‘©8:„k-'‰ÆXÊõma+(¿ë•“,‹ºŽ•ÏŸ·éçjlÀ_aOKü¤½*×,ÚDkÉ öb<þÂ_ø kü1ü¶ îXÎcZÅ#”4i’ÒÐóf —-º‘& ˜ò)§ù glÁEeú;…J“bÃÛã©ãĬÀªu±Yº…gIQ 'J0¥[5§ûiÕ¸aãÄšú¥Q@‹´í Mk¥¤{ô6xêOOïÀéÜ áïX0f ‹‘iD,F*ŸKÍ3§l[³÷?f!Ò'ý»²ãX—à7~ã7¼ ÈšÓ£IôÒIßÉÛ>ÍR…,ÇŸ“!0ç°ðüiÉéMG¡| ¦4LÆ[~±‘I”C$ŽtÊ…D3~•»˜á„£,u¿\÷kwø #ðÚ-ç“Câõd°—‹“ E¼ÓHÓàËyôÿ;•Ž— ²÷­Í@óg2.õ’†„Xa{»Ù‘no.2Ûâ=æ]†ðûøJ§R:¥,ÁÆ"ŠÌŒV͹n<Úº2))cR!7¶{L¥ißj;ñ*°.&wgÍÿmÛ«œ÷ù›ÆI³}Gš ¦q…JÉE™ƒÓþ964j^±î‘/ ꬰƒjð¢z×C?S$Ф??Vñ¹Ç¤ÀY»<ƒÐ°4DäkÒØ‰V{ï\¹¾¨bjˆò IS´˜o”j$Ý0 ¨"ãí}YEnßç£Qê/(l‘¶Šéj6›Fñчvyü6jᛜˆRPï‡hÂX;O‚¦Y&cÃY|rÉêµy)ûmEÍ|&MÇ·@öu—Ÿ·|óU™3o‘eôê‰ÜÈ{tþT|¼&&@ =)^ 0ºMÝá«×r ¿vk;–pǬ# k¶ù?,fuçš„K5 Ôv—+O±vAÁm[UžÊ:…ÞDA§a&mËQà:¯B¸EÚPbçZ˜ ±,F"Yƒ¯ 2‚­û~›„ úêt'¿úÙA nyF¦¶¶üG0|3„ŽÊä¶Þõ,M2jçø¬R™YuIUKFê–"*Òª8B«fUK…×ïY¿ú«¿:Ž™‡åj—}N—8´œ$;$åô™˜&Â?ʵjdð+êM ¢Þv^bl…SyK ­#ˆ…VÍÙŒhG8²î÷K€:­ºoWmœ¹~†„V¢|¬ÚNøc.x|+æèHæ4™È̘öCEÜVTz9i¶ĘHÄçfoùx›ü‘÷SãQI˜d$-Á4)|¶º–É2¶Zboì"y”ál&IŠ#vs“µ6¿ÖüÔþ‰é9Û5›¡Ul{„0¯ kˆžQ Ë•éÑ\d¢ŽC”NJ§ P·Š·Yþ4E½Úô*kc>÷p3Juv5v2D¼¿æ¹€-fâ!<êšgÆydxë¸dÌØDACÓÀdø²ò/hÛ€>—365Ûoz‹4¿Pù•±Ïê,ð:š‡:KÒ1îdÖñ速{&"¹Y»s‡#p3„Æ$ùs™šR  g_…ÏEd:?Ô+ÂH.¡“Í$â}䀡M]A%ë3ù|’¿ŽÕ\ 8oQéÜ»:Fz{*L.ÒDg:8…èÔAÀkÝcØ´Ô63d*†e¹¼¥Û`gC˜ˆeœ.)$¾8âµµê~¼'·Ï¨Ù¸~ÙG%›¸Lê×dS3öBn½¦OA!|´-”,8³&hX3ï­ ³òfÀk"Ï sâ@õ®^NÕƒU´2Š*mÞn8,€>b‘NØNr©¢¢ÐyʺxNË­…öÏþ™@T1Çn¾·= 1ÿƒð€þ„µ_÷àƒ`hS}Ôþ|YÑ™œ¾=ö±‰´Ý‹óþÑ?ŠÛ ¬Ç•9ZAÍ —Ο_“ã8œÆ˜„Ùné/™Âœýöh( ˆÀpÅl±š„kQVœ‘ôGS´Æ? ')ÏÉޖƉAå¨qþL Âêh­Ú–å}>Nû3{óh‘Zª¶cmN²nŽy`¦H oˆ$SÈÅP0æözÛè)kÄš@]ÝÁ‹bäâb5ÚÆ-‰ØÑ»ŒBØ·,Ûcv¡lóî^É[½+:ßr­ÙbX­SÂúVø§~ê§ÀçM¸“šT›îñÉílkÀýx=Rõ€¥o ‰·I)¡Ë4&oÌ—M@ f=‘Tˆ :‚‘[c:vŸ ( Z‚\‚©óS˜‚ÁcöÒ9kœÒ@ßð-0Æçí§µ§ýź¶Ç T¼åÙYV ΃ŽH&…öÒwækœí¯åßámn†ðÀäü€†=¯*¦g!uŸD;èl<#“ .ï*'³°mø¨¯kÄÔ€&`¯gjKNbÃã±ÍŒH#-ëzó6qûwâœö¿ é7À£_õ΂X"_|1þ𯮛Žò\­ÉÖ<^W–L^3Bð8·R«®Ußáo2/n¢@¶Á÷ž¿\•躺@‘“5Òƒ=[GHø9˜‰M"L¹eœGoþ“òOŠlO2©ÆÃ$ž€Wv[z+×7) ¬JÂüYçNfM¦ûyèèwÂ5dÐÇmHS`v±ŠÏ:F§t¼ê¬.븃0ä´‘á u“NÇÛtž¹|B Æ$ŽtN >´Ï=Ê•®L¸·I@ž§-káŸ8܈ùMF Ö€Ö)RãÎ@o]å7 j Þò߆Úc®e²©“+'GÿûHV’’‡ŸÊ™º¦c}î‘ÆVNz ›õqùLË `;Û…á]1œ6ÏŠ^Õ/[®õ©Ï6ñߺ/Y‡»ÈÅV•ÑšýQ8|æÕh/_ÜÈuÇ_ÀX± kR51LìÐ`¾!Ú÷“"YÝÞúËÝ—Zò/fn%™î.èx¼ê,4Ȭ:á ZkWÈš—²Žò¶·[¿`dŽRÇ[‚ÓI‚ÖñÀv{Ç—]ùb˜ÀÀy@ º– ¤oˆN/žVú£y3„¾>´”Œ6°ÙFO7ó/©‰WvÍvô’ùõʹí<²Õ<šOáZé¥lÉZÃÎÒhL3xühäeá õ`‡LÂ[c#AÚûbv\«-xû– ×ÕÞaz„£GÂé:e«ë0CBë6&­^iRì¼vó¨¢û÷Ñ\l¥l¢"ñØÂGÙOãÿñ?þǾ”ió¯þÕ¿*Ab<¦§ŽdN ÉcAËt˜4ÃæZ M²­¯XG{@6£ßÏcSàH–iONbaò'RÇO­XØwt­Ãº«ÆoèÀ‡(M¿Êô×õÞ÷¶s·`G2”£¢ëˆ¯€VŒ€ˆªlìÄ£ÞÅN¯Šß5%Ï(atÓO]µ§ï.ŽîÕÅß3—ź“)uÔY]^‡" á‡ùôÅ¥;ÊŸ¼|Âøw–Ⱦ…ë7ì*ê-<ΤLÛÃ3Óª_¿æû ¯/ÿþàüƒþ;ôQÛZ¡u6ëF‰á­Ýmö¯S“„ÕµŒÂvy;ûvªpd¸$ñسÊÖ4k8m‰¯0ð7ü-ûõM÷SƒÞ Jü‚|êHf²lq0“ñѼm‚™ÃÏ«g±Ђ¿ØÂž)êNs1sê'Ͱ™ß¢Ï¢$i7®2‡Ù }¦¬’Ö\Ô -ŸuJ—ÞÖ yÀgJÂæmÄ L­å9/bíÔy¯A÷ŽjÎh`||[LšOÐÚ¼^³ýŽaż²:€öÌhKÖV.¥Æ<ªåŽŸ¸¯†"âžÚLjÏfKÓy­|6vzÕÛY͹N±¹%Ayd£ß^e¡¦ÄŸoPYW³ï®«²+ÈpÎìŠÉ/p·Ë˜hÄ£SZ¤…]9ƒ!¬‡‘*ÿº,…Ã(êÑøxH¥ÃØo®s)ÁœÜ8ª¦%wàm#pº¡²å ¼hYtZiêktÓTyÆ‘Ìi!"VÙW@¥N¢›0GCÐGy‹ãÒT”tq×é{›w™¦åð{H¼øÜZŶÉÓå6®eÈG ¬­Éf³œCtù•Y‘´ö?Óòï0MJ’ig^‹­_°8 @z:Fòè¹/*4<58<íKjŠÑúÓÐ¥ ÛÃf2EdA 3(”ºÍ8–ù¹Ätø\ײ˜ÕÙ˜®ºã]˜×£ÒfºùªË'䢶%#7ì>±Ï:èÐç#ñGõJ6JûKÖYÈÅTý>5ÞoB\wí̯Lj7D`k|[0²pzn0b½63Ãf}RžéAÆØÂT²kgk¿Ç\§6sâÍþ˜þst0¯#€þV{¢cÆ‹$7…*:²ÌQÑ‹,÷«7ŒÀ(ú€µX`¶lÛö6h¯” _ lÅ€…³“‚‹"AÇ8C`r 9x5ÆŸcpÑ@^LžTÒ‹iÿUNçOe–[tɪœZÖ@¾p€êãj,ª%ƒåAt­Ú½ À é*ʵ¢;<#p3„_ EN\L fpŒzíÐ…HÛy‚^a3Ûz$#¬”ÎYÞéɘ¤³þâËæáf»Œh,Üì6-û}5†ùFe”ss»ZüoþÍ¿ÑYfþ0:Íb%K62˺6¶á14šc0u9 (ùjøîu6žë‚ ÃÞP[Gh ‰ÀÄ›-;Ô§è6™º¥±¹Š¹hÞ¸Ÿ1‘VaùE¯`¯¤ûÐ̵z°rbU±‚nUÎZ³ëmo[Œƒó+3®Y”|̲•ðÝ>vÓ©÷·û·µÄåM!Ôcs)Î÷šQjá[ÑÉžt™CüŒ¤ !еwàÊ )Ÿ¾z†‘àÖU™ ¹}Y ”æ[·C÷±jבèm]ÓM©¶¡Ðßá(ŽžK¶f´1=©¶•7É`äÝÇõ‰7'{.ÔJ5BGé¬î9V°5ã<Ör¿i'²”Ó¶¶¹ Ô¤[k3`Ò¶ÅÏcâ*¤Ò¸Y/ÂþÃX|Ê''í˜EØ3xÃGMíO‚þê]µ»Oæ-Y†|HÌ­%h‚Òø|U![bîC$ÜÜà68ñ1º1$Ô‡ógä;^ˆžTWú ¿GO²¨à'…²jJÖœ4% EI?g+à,%jÚhLW^¨Å÷ ¾*<(kš­¯ÖpFs€ñ©­u²9ASlmX…!$nàzÛ\>š)Í8¹Yk¿ÃǸ¯Ædüm&lÈñÍãsëí«Éøå™™ Ç ê¸ˆ]ç8Ðyì•r¶®cšêØä6i±í\Þì6W§‘!òÙùº~šHXáÉWÆ„µÃÇ/ÔG roÓúIH ¯Ô©Ìʵ¡UÇ]DV¬½M)é˱wÌGá IÂFpy7’¼7TARòÉä¶ öªc¢øf M·gý^—Ö¢|‹fmÁ^… FÛT1ݱÌî8ŠÏÜöHÜN9üdêº éþ±Ì÷ã JÝãÞŽúX6ûÓFÚøi9B2BÆë¦§YD†*~{” øi+ÖŸ6„×{Ä+ææÞçà¢Cä`Žë2ßÿÛ¤u:•­ nc\ßCžB<^Ø‹ÖÓŽÓ'êq|¸›j±ZÕm‰»oV2šÛy•kÍwþêšßsDÐò+Ó4î’§é˜ ûÈ2ÙêPf2‘±˜¶ÃÚŽÊ2F7[šãcÆê¡fèü1ÍE ‹¸¤Ã*Í·þEâ‹W! ЦÄ1å¯ü•‹Äϼb1ɵ[½¶SÏ”§iÚ}ýaÂà&`‡j!ŒÃ NƼE´X?1ÙÇ•T”9· ÚÌ }µmJg£¬‘Ú¤F“²ë)~ûmHs*Œ®t°H ÁÙ€½çóáõ1iN£:LH:X.Óµµ¿É¡²R›ÓÔka_¡AóáVò-Y}»ªÃEŸ¦9F¢c³™nž9‰·ÍKj\ >ñ˜ûn)_|ä`&»>#ÉðïÅéw] ÁPL8;qþ~ûæ5ìl^¾ ù€˜´(À6…ƒp=u3ø 싀햿Ydˆæ€i/W—Ÿ‘â}Ð\Tt¸ £"5G[}«¯ŠÞ&SÚ#YìmØjÝ–›];-hÎ2ÆuZÁrà\L@hïQ³Ø÷j£µíþ}47CøÕÈäfÃÔaíब€éˆÌ™î©, ‡a‚5Þÿ¸sTV˜µs3è¹ÁEÊ“?rNXW½“q]s¤R'q'RÀ¢‰7ûWýdQë6e §ÒdŠÒ£-W4¥ &Ïr£”OC8cÂlNWÊ>ç Ÿß×6ßáçGÊI=h»ý†f}M³+ã{.¤£ûÏ7fMÙ¡>“Áô5þÅðM¥xgõb–I:Å’\e"óv ¤åÈ%Ò÷˜ÆæÈÚåæW¼ÎZÎÑÙJÞçZìz ?(@yñ˜Sæ‚ØH{°Ïši±ŒdÀ(á:¼Î|×í&ó(IAòzuG€iUKPÔ+Ì©æ£r>—øü‚¦ Ö5ª›óG(“¢Æà¼Ø/ª!<¤¿ÓË'| Dõ ÷á #€%Ò#çÔ"kjĥפY)×q0éò SÅGÔàhc³hŒiÛ‚ÆVi;] sŽ:·=FMìÓ·ÉÁU¤­ðe¥iÀ¤9 uï…O‰ž¦y¹:’ù™Ÿùƒâ¹ï£\§ñø‡¨„BÞv.}Šåú2®À‡Xï0˜wà#Ž@[›¯60L[¤ øË]ÐhñV;€ç•% þ% »kD´«JcVLk“ –b€Lo7ïGã&ô; Äѵ[|™ü€Ä-ÌmIÚvUg-K<èWÌög ‚å‘V@Ý<$ ®^MóêÉi3îÈun†ð«Ñà¿k 2Ý+í©æšpLö;³¾MÊe)‹ÂÄ#öŒrv‰^8JʦtS¯—R¥ /ýn¢M ºFɚ¥$¤±¸Oƒ±Jôƒ,úzÅ*)·ÅdÕ±¦ÚÙù{P`õL••yƒ RÒ¦ KâÃ:âo¹Ë|ˆo/`Ú43 þ›=¯Žd|͘1ÇWiÏ®»cF¡¼xÔHð*5¼ÎèmØù=*”ä‹OZœjj쇉g ¹¨ÈÖl)<r=ꦃ[JÓ—TâSf€87'3ÉC`1™rùÛôŠ“ý» $Êìɪ©T¤Â× K‡2¾Á| ŸlUþwó$&ü¢p…D|‹tVÔGYÇ ‹x‘ý;yÕ0ÆY…qÔ¬%Í´ílžMHUB¾¹ÎäéÂÅ n×ÇbC±vOy]M¡=«(v <²ÄKÚrŠŽYŠ1©¢º9’aõ=|ñôé±@Â…lÈyÉR&2þZ]å”9Ü9*†Éœø;ð-@Bó-ýÏÌðئêm«oj5¯µœOãÈSµv2‘FõB«t¶W&H¢ŒµÄçM³9¬±¯ÍüFûÅ1D±ÛVó"Þ¡|/õ:ûb “€F1É‚n²qXÌLš'sÕ“Ý'óÞÉÞ6@W“`˜GsH+^xá<ç@»h5°5—:¹ jFû`@1Ç´­C†ÁKRʼn/0L€º½ŠÏ зÛ[Ñ@íŽË]M:N[×õk~Wàm·ª#Þê¨bfnü\>Øk¿cNGàf×°d´‰^3ÍGgÙŒ®‚´®Ì¹¤MÍìUfš XZŠh´?B8E·Û­g[ å„fêæFÒ2VopÁêWTù ÔUUóLטüàkÆt-*³¡UÑi–5Òç#`nÐ:Ç’˜,Gñ~qû`¥M½y²‘ÁµÀ÷n~ꂎèŽNM¡ù.éªû …·ç-k—O ¿ý>Í#»A‹®­áÅûår ¨1V˜ˆV„8GCþ®Æ¼!mñføÝ)¦¹¹{uÞfi§KŒqâËJö939ý CãÓ #¼Ò„I†ìÄð¯¼Û`e^[.à¾Ú:s¡‘8Ìz<ÐÕäÖc -À¼¥Ü‹ø€vó´'^+˜ hDŠˆN³ÃÑåL|Œ9m4êvŽ#2ЂµÀ-ˆ»æ­#3ñêŽýZ2þ†þ=¿œnåÖÇ~kLáävj@\G´€W¯4/B‹µUwøf×@UCQDh »¾Û-M¯L}š‹~ÛæQXã2ð˜YNÚ:Õäì>öl"³?¡ÿå_þe…lt?ÿrù{hÛ›ŒsFˆÙ²¾rÁQm#ŠÖ[0b¬4¿u­…¸'ãÌÊ”Ø2€˜€§ã¿5ÀÎãVÝZõþè#`Ó“œD}+w÷¨.iâóñ„ÇÍ5G©g&<*dï˜#àCýXˢþnR§Ø‰¥W¤œü-]_Ƶòu²§§²[ÛV8w2ä5æBIØêÇÏ.•Gv2[­LO…k« ß±®,ãwþ›a9>Ÿn°/«§ÒÙ­©l½0º–öf‘¾%³ÞñÀ ~#€ZÎLدoI¢dé>U‚ç3ª&ªyÎ XËyÿa;HÝÔ žæü9!]½n(Jm XÇ-áKÃåCø/áj´G¯s Çq*ãûjäõ¬x±Øo#ÁLë4•rÃK_]ûýZÈU=žñ£ ŸgL»5,»Fh’%=墳0ø+s±aØ —[É=vGÅïý½¿7>Sö¦Ârš¾H  ³ˆí–} ßÿÑ?úG·W§©MŒ¬ÞhÌ«„†6‹†è4ïDšrI® 2Æòægd>AÀàÏä_Á˜#¬âlÀ[Æ8àœ%lff¨ ìia2©@ @(ÆHÿ¬„MÊ©ºìɽ:º‡I4S×î'šh;EµÉJbAÓØ—+“l¡ÆdrH§¾žþ l»E'>¹d]ÝÓëû“‚’µå?Êá›!Ü¿þxbìô¹IYЍ¹™×ìo9%bέó•cßÎb±ò±êÁ•1j*9GÂaSIL®3Ͳ«%bÉ<ƒ0o^`8TŽu~·{µºîHÅ¡ÌlÂø­Ê÷®™†fØ$Fê,V=¨œ­%÷ã·=¶Þœàù¬¶ä‘œVñ¤í?ÃŒcšì1ì+Üî­Y†yKŒ7¯8( Ô^(È/3‘‚Æ&c¤-+“¹%zR%M½¿þë¿nLÅ£%dgîWŒµ2“kÉ®Uˆý/Ä<;Vel*·¸rÊ߯ւê÷‘>m­å“…!¹Ì{ЖQcâuÛò¯›‘PéH[Ns©ˆ e|ßûôn1¾Þw+ ø`‹eË#Ò £Äf8ꜶäFæœ]ã-Õ»ØâÜòÕ>xÚ€tÌïÚ%‰á5È­2sŒÜ'x»"V”¡½ M•!U@–Žô&?y:îS~­êÏ6ÿU4Â9ÖO&k!פª1L@6ÃÍ×´™ù¥íÂùTLúsôš†à´"A“·@žÛòá±½òTUïêv%ÖaÌá˜KLÖt>ýÚÚ5¥ø¾ïÇõín¡Q¡P­ñÂ6ˆŽ\Ê–SÚÚ4\´777¸ä'xòÞT ˜ÒlÀ[g—ÌÆ,*x%üÒž­nô½Ûe-º“®Ïç¶n½K· šÂÃÊŒd)'Z°6C›qÏæm~¶€á)5Ënl®1€t˹•Þo'Ìã„G4 œWN <-¿CÂ5ý}Ÿ›!Ü¿Kp°Ég–û3¡%_,•³ß(ÍèŸYÛfVZoÖêzRV9$sR¸SqJ¹Ô¶´ ù`awá°ÉX Ìj…‘ßX¬·$µ¢æâ©ÜO¥…gÊ’A`™!J.±k‹p–èj“³5é~üöFÀýëlfÞ:»9H0 yTtÕØ•¢Ò)Y)¯êx^O¿˜Ë…]rmFêq,}‘'º½XþÇJP ýZ§Ý!ìò± `b2ÙŒB-j Áo¯iþW›Ï\­þÕ¿úWK€,D6Ím¼åvΪ,hE2¯mÇ÷vŒÏæ*ñü"‰zqJT¥ÙÉL®-p4ÜØxdf¬"÷‘g2ÇM]fX½ºt´Á';¸¹Áã8‚˜N4ø ÑmŸL8°å<6‹vçMo>}³œÖ‚ˆ}Öã9 À²*ÒÂMÄ‚ é¸«è-øª4;lR°”ê«»š$¼«šZF ÇŽŒ„†pFjVÃüÎÜÂÇ»GØ-pÓÛߤ‘ÐÚæ;|17C¸N‡ÍK³Ê–à×Q~ùÊøe†ð’rXÑ„nõx×;¿²Ð³œÞ%hœGÌ!psƒÛè}²ÇQH 3{£-ÈòjƒÎuÞðFæÛFi›˜6›6’ù'²`Yö¢à¨eP¥V£0é'DZ“” ®ÂãHA_aËí8WÇ{¢™ß^ö¡M_þ¡HÍáHS¸·WÀ¹¢ò¹h×0äî³;q°ü§¼“1oªeÉcÂÁÜ™kŽ8¼ƒMq f×ovúM gâž‚õ¬Rá+ÍF+=L6íHAÁºCLÿCo¹’—xµ÷h§¯Þ£(=2o‡¾§f3žŒTž¨%jÝ·‹äº uX-áa.%¦Íwà€ÍxÀ%¡à|ßš=h^™ÉÏØ•™‡©h€ª£¦2Ÿ¼"¢c¶«ÕÁ ­6°1Ù:+` GÍ7ö#/7Ù3e!­È»Œ^4]s'3ÉìMi¥ßJémÄPé˜ÒvS…$qHÔêxU…· ú•¾Ò¾óßhÛvZ}~·Ûí±ú›”÷›áÉÎC€@÷fvqÈÁ¾Îo'vˆÅ©“>)5ÖBúš—™_ –ÅdÓmê¾l6Qƒc¦ÀwÐH-×àDÂÌ´ $]›ÕZ—³C1$Ž+G#õËÐ@Ã(¯!5°†wºœõ×zùļºtjñÏÿù?‘¦W¦M%b:ûdϰ%/þ±™þ0T–¿°9ÓwÃâc!›ÞI.r'3µÓ`ø²D©rÄñÆ$@FbÕ+Äãø’9͘\ÌM“R1@È 5á#> ƒH®G4,²¹9˜b·‰C§ V¶4\…­¯)°ÄfcàÇö±Ùž6Œ‰…ysƒÛøÊǦ«1`L8fºFZ[ àœ†b Ø« DäáÑßæ©^zÀ²W(Òl‹âó[”Vf‚-Õcs~õÛ”ØÑ¼²šÚÓOí9æø½ tõö;0;à=§©ƒå : ^Ê »™\ÃîßçGàfOÆŠ]‡‰EØ6‚Þæ(ŸÑcÚÔ¾’¸¿é8¿¬ƒNJÿßù»÷ïJ’f奖m‰Ó2Ó·Õ)Ö:•K(vå÷st~­Žð^-¡ŠÑ¢ä M|Ç‹¥WK w …JÛ,Ñ—ÞÎïˆm¢5kwøSŽ€-9ÄàÓØÆ8ðbS¿h/ ›Ð@IDAT…¼(ÊÙ1W.½›”3Ö77.JðŠñaq «4£Ä©7_2§å@«5@%€MCÒÃÈ­á§–¹õæNfM)¼2–ÚjŸ RL|QKÒ–3Ù]a¤UÀPD`ÌuDÒeM²ï0ÀçG”&%3º8¹µ3ÎDßC½ÛÛG È0É[cDsÐzWþ¨¤‰ìüÍ¿ù7…Sã˜Kˆ'*ÊÚm˜%í÷ʉÀ¶÷Ã(3¶ßÉ£æ%}‹Ð §ï²ü²O~tSgõKÇ#³†Bû ‹·]ao¸ ZØÎ0Ìã Z¥?Úž\EÇ8ÉÝŸ‘šÏiáÌ÷E°µ«íººoõíè“gΪHÒÌi9›ÞÇù_͉bZk#£ØµÙ²¬®e2{CpV¶mM_8yn'Å(Ü÷AóïL/F›wUg›Šœ"ª§¹Ž‘tŒ¦–i³Ú¬2ìÀ䓞O…‰xìÁßšzsƒÇÁÿ”1IícèË—Q‚í Ò–â×{`¯vŽsÎÈŽÈ´ß‘îUüäU.¾™çô4(-LU)Ȫ–N™Ž…ª npÚ’ Ç‚ÁSÔØŒêë×ì w»ƒW9X.Ðî, –añk±wø™¸“QBïšÙÕÌÌëózsn"½4GÅoöÑS˜«f—J¶±Ýæ‡f÷Ío=ø±­@+?2fJ[;ÓªKX’€ÆŒKv5%&¶¬ÙIô;/Æ~)Á°Ä[×<K¹Vz‡?嘨qD>æ$‰@Ö ŽdµÐLHÆF?àˆÝ$Ë™ŠŠ†O›WÇ•àcfLæÌ¾dŽy‡óü3æÏôvåâŽéŬÞe2ØÃž¦GI˜ «oM™œ>X?x®) 2h5ÖóÁð òXl:SÚhþíài8—ëáþÓd[dfíÀôߣ9àäó€Ž Œ§ÑË]øÛà7Oüði|èU$œ¤#ºF9ZTžVý]EfÓS“?þAØŸ®é nê¬Gò 6," Q½„ 4Œ§ÝɦƒDãôíEdóäÑÙ2¦C0:¨ÞJü¿¥üôcçb1Vû\à1f™M¹–ó©f,­5Åi“9¡ÒÖ—„Y ÅÈ#_2[!;O:ÌUb¦£{Æ-׿]&…¿yòhlÙç1i ^l$Æ>O6q E&ãØÌ0ÿ´m¸¹Áu¬¾“ð°shÅö°iR¯‚y ]ÉFG=0 lbt¼(¸8<^½KüÈ8Sª{Â\õù®™{ ÿY9Ý ˆôµ(V±E üjLûÂÖ/{AÛÁh;á¾ÊU½²ˆÚÓ£ÜuFõùÀÍžU·[òÞž ÎTËæ„œc¯Áñ aóu›Ç§“^!Ca] z^¦—K̳Jˆ7}–ý@ô×elÑÚË5©;—BçÙÀ¤yŸ3ùŠf7yE÷˜8Ϊmýòø Ä?Ü;ö㪗ßGIýú»p pQ9 ™‡ý¡?ô‡´?²ä¼(„ ]6÷ïédË#_2ÇBVÛT ¸PNÞN½»8Kæ‘_釽ìšÝa;+ X—½UœG™©"Õ¥¶ÞóW!ܰÉÍ$þÄ·îjŒ?‡ƒ¿¼ <ÙnßdDО×B(Ùå_ëšxã NŽAý²_d̆Ü5Ç¢-ãïªc'±Ò§Ö(£_üÏ*¤x²ƒŸ&™†»k-Ò::ϺÓpÉsC7Qþ$8å5\msdrìEr½kǤÇ\Ñ#¹¤“MLn*蜓h!Â2ßk’}ú@Ú í™uû'fÜH´N-gsÕÒ>6²+ycÞÀÜÍ,¢ôˆ‰ì>b¼4Rs,çyùàDšpzfÀǻ̓ŽdŽõŠaè‘Pïgögm ÍCêšg°8Éà½É‰âÝ {¥ï*œø ²ý¢-Z•Ws©`É€½ÚŒF‰ vÂ"—ø>·%ƒ ×'}çú€ÌLUG¼r<Á¬š-–ŒÂØm|;p·–À§s2eIiæ·M9˜.½Wv® éćH¸hà·ÚïÇgFàfÏG)S”t —í£ ¤wÑ2S7ýÎ žÀ£›Ê”\š1æ\Û‘0FæCküzŠ×AÞÄÌd$éIV·ù vh>Aoå´? €®ä¼˜ª«]3™¨Ç6þ4„-È<¿G×kSïð'›t³q>Í{C~$ò`—†ʴWì“Z…©.3y±»l’ÂIvHÌ¡Þ.hibMO‹ï2Úyt'³æRNJÂVë쎓Æ2i Ç£L¯f¿±lóF#Y»ãéú¿í€¾gK£G?÷s?§U”™#~±öÎ ½¨¾ØÊéŒÊ“Ž.)L@󤹚‡e5€ºo_$h>V—׿¤Z[cÞÃcB7ME'眕GÉ‚c¼¤ouÜ Šxx‰ ‘zRáÜÍ´¯]•Æ*MšÝí™q3‘ÒsRʇgßbÝež)äÛH“NU{ `åÇ»C[d1ù•‘fÛC×öŒÉý…û¥DZÊñ5-·5û£0²Ÿ)¸Šte?Æ‘Í7ŸØ¤@N9­ÃŽÎ˜\Ô~|5v†záÏssƒÇQúNb@¯/¿ÉÉOÂO^G"2LðìÕàpˆLÌÑÞ.æô¸«wKlÉKÆ…/¨)vŸþL3o›]³‡žž}QŽôtèfUÿ¢™ýzàiGËC)âÁõ õ“$î´ ?Ê‘7Cxþõ©JL¯pÌÌ6&=„ÝÒ4'üŽì²«Ùö:›m9§t^‹ ÷ô혽>¦m§—ˆÓ£²Ð$‚L•2LšÒZ0Ä¥)4ïÿý¿ÿÒê–^¶SÐÇ›Éh§}t­¢ê{òï­ƒïV`?ƒð£ØxB¼Ü5u=2Œ.%IãÝq]È Êuçuâõ­9™cϦ+µÞúö4ü ¿ð -ºõ¢) %fFš¾•6Œß&ì † c1ƒÛª+ÁŠSa[!Ÿò±A&”¥í´7£WŒžlW2ê×…Nõ´(–FàUÜHdz%fÊ/;Ò*Êi^ÔO8ÒÚhûÕÁï\{<èN#4{ëÈ䊓©ã ‚ô ‹!šd/âÉm"/¦ÜtG‘Ý!åäööøh:™TFžéÁèl_ëÏæXì7ŒÔ8’&š#isœ’4[Ô¹Z>D*ºî‰(‘Ù²_<ŽMÁÑgãE®¢ªKÃ.’½ø*kXåàçOÝÚ½X‚éáÌ¡ú»¹ÁGìS&½¾þ2þÌElµ$tÚÁ1ö2Í44(hŸ³ ;Uî‹O2æ·M˜ÙÄŸ™m¯+®ô km;µÁ–&ë†G´kd4[k^;ú™Ìפ±´¬Úøiÿx~n†ðáX¥·ØÚ}3œHšžÈdN™‚ÞŽYö:•õ™¬ÉõË©Ötëéò±qyµ±¯×¼_ù•_)M»uZ¾üÝ!¤8q¶NØwÊÅqÛÄEqNºkm`7¯$Ýë=|l¶àÎ$H“Pïd9¶íŽùGO©íAHß„cwŽÜÔUÓQÞЯ7Œ7žDŸS‹³©¶=ÃÙvê]k7?Sà”–· Ëg"' ÞFoóÏ$A†XÉ5KÃ(Kà‘ À”ùí(pÚk_z ¶@¸ŽgŽá!ey­oÝiß}•é`äNƒ žGÊ[³WåL†ø`% >;ˆ#A¯e_¿½ñWòf»E›­Á©ÇuÁÝñš­SÓ†nt¨/~ …Á¢Iöb€A—ÉßóÚàÊ俤 ?^˜B&’é4Ê®i66ã™ãÄ/öâÍ 2xÓž)¡K¡~ž˜À®4ä«óHòÉu )u‘9¾=Aî:—(#„}šæ)WUäz­m-M9s¤*GëÛgÂ&Éèy4ƶr럷O™&ÌÖÌ ˜e °ÕŒH( ¦GÐNʲäÜ(Ìÿ pض.úúñ–ùv.‚— p*Êî™h =ö:³ öøJL* äîȳQ6Ö£í—&ÀŽÓó*à©BæÙs@I¬y§Uß‘/ŽÀIê‹IÔdf™é¸-°9jæ¿üË¿ì1¡…@6Ó]_V²õ—»¹mèrp›â›sg)!ž–®·GLÿó?ÿó c—GËáF5ñ]ÐÙ~лB‰ºêÎ?ü‡ÿ&Ö ù IVÍI£Ú£#¼SÔæpy‡^ÒØÔ5Uoº¿ó˜ëûFNþPßÈo ÍŽY,p‘mXùßæ|ÈÔ @€P2³ÔB°ÌÓF¸:‹žÎ`.ì8«: Žn]Yã܀أðÎìò¶EoyAe9þµù³½ xá ¸Ž”DWZ\ñó‹nkÀýx3„ç@gM8k uGÀË~o_4;Íì„åÍÈ™šÛ„¶1wª‰úÞÂóשŒÕ3o "è©æŽæ.)C𬫱µÄ@ÅUH–¢ÆÌÄñxbÄwt>U’beù€"xn1Ój K¹–n Í´,³8}8v÷‹ïhÈóšMBôñB@þ¨c´™a‰=æyCÄÊ_2¬·ÌiŸG•®ñ°µö'€xÑ`Õ-«É´¬R^ Ö¢¶°í*‡òÕ²½ “Y) Üä—Ù¥¨…ÿ•Æ6]ú©ˆt+öÛxa5FSsIb¬\$EkG=¥aëÐZPÀ©3v>ƒ2Ë5NŸoa'‹Ž{ùE N¥j+ƒ5M¦ì¤_DUÉYTj)cx¸:(fixÈz¶–ùiÂà”ª¨×~æO"«=ß ifš3:¥kq‰ÓßMq” Æ\/ôLGÞvù„’ùX…œË›&†é1&¬5Ò2‘L'“ÊÔj1bÉ%À&Í\z¦µ7Íl»›³­")Æâµ„­‘1äǤÄËÇ=î˜@L.åkMsô4q‘$,Q£šÙŽ;.Ò÷ y´Õ"•ú’!«J_ÌuL šúrÈöüx~^Ù>ÒµÎs¬åŽùÎG ­Ùœô¥f4³(?+mOf”õž{Ï`ÈøÉ% apQLÜ£H ‚‹ÀdÞG ‰¦Ëé$ÚbŽjóä,5”\’ô¾Zî¯cJ½’gknúù ö[¤µ¯SÖ#Q –ƒè€ºW’½ÛÓæÛh¼ÃÇ›!|øQÆLYÓ‹»° ¦hµ¨H(»…3Qeû«”´á㣥éÛïº0räÀwbçõ×°øGÁ-€NÉCë[aÛpeJf¹ÎÛ¿õ·þ–xXa±ö—eÀkKˆˆZÁܤ¤Ç,Íp2ÎÆG]kš$Ž¿ök¿¦–yö3%ý‰ÛØ[)¬Ô¾–ðiÂ9«´AF”®• zAä.)'”âk¿_NƒðŽºmÛö£^''Ý<Ë=J_õc1§#*V¥¹AkSÜ —&•#íÊÊv–,·„ª’%UY]ËŒL4µÏ,ñ[-ßö#OQõ±­Úš}ñ£L“ /5ç3§ÜÊ9ÉF`µ;˜ŒBد=¡áÈŸòÓ™¬¦¥ñŠd÷/ÿå¿ì8œÓªƒJÁtyDS¸Éëû®’4m˜o‰-ÜXÁ@Œ6ô5fæy*M Öl—ÆŸîè”®éàXj¥3éá×+ž×zNºqáîÕ§ôA}V·öôKÕB`¨ƒÊßâM³6Нt_TO× ÒVÂGyüoÿí¿M_ÈrÌ£)¯cK¹“±„-ä"7§Áqw>Mò©ŽÌ”kóÚÄ-´¾¯¢¦´_SNÏo®âÊ,±‰Ì†Ó¾?1k@±©bÇõ.P•F½/>îZ¿ÐÍÌÕES ìïˆ?Ì5ׄÝ(°ÂqUßÜà Îû `³ ¶@ô9£¯¬Ùn$óX/‹ øÙ¿ÉÖ×_¥ü#Ui—ë8dGjRçIR*ðˆµL˱:$¢ç ¯ÒòÓÒ7Hk_¬AKiÙ€7’Û $(¹”ίí¿ÃFàfÌWñã9¦…a~#ÜÄm×O«`E³0c¶™™3›­nÌ´[*Hë=n­[¯laâØµ‰š*v=@(˜8(dó|˜>Ð~6åÌíó|«¨†e˱ qÆØiz1G'õ§Š;ðbšO&ð"¼Èø Ú¸/Žs³É ëèèhzzaìdgÚñèHO…ù´áQO%ík¿À2=µNúÌßüÍßôŠˆQ]¦÷¦$Ì^:Ÿ‰HF? ËH—’æA¸¿@ØX Î8|«œXÚæÃyî }Uu‰~e÷áH´Â‘ýLÔêJžìB·®%æâåU5vEø‘EGyºŽâëáüð?ʉ¨jÆÄu“zñWâ±4ÉÅ^òoÜ`‰?"[¸±‚4-Ã|šÑI™¡È¿fh|ûÂq#ˆäN®±j‹¿~lÆnÜ}2Îçó×1ô‰}hŸÛG·YÄG‰|^š 1&›aÇó`!²‹áÚẑýíÜ©`¨žöƒtÞn°‹é(,aoó[»íV‰·Æ„¡ÅvšŠt´´ÓëȨÁ™dk`¸å•I`¯OÆqjœm¸íò2n†÷k][Ø=‡‘V‚íUa“v€ÇšŒ °¥ÔÔµeQþšþ¿‡hí÷ɶ_° íJ™j—óV’;wÖëNÂŽÑÖA´¢’Kcíuǽ5µÖxÓX$àª"ñéñÀÚÒdƒ ôöh6Ö< [)U•¢œ€t.h+èá xë´xÁõµ=wøµ#p3„/ŒXÆ1{úwÓCÇ0LÁ¦2¡Ú\Xdµ7­×ß4ŠëuF]ú—–ÙÒjSŒ©ü4†›©?çm&.vëÕ ihÕy;)³)×¶@6¶pÇ”[ÃÐ?É¢ÈMÿ9Ý*¦´;ðnGÀ¤J90—¹ZÄúØækûL~‚€ð–IuÌ.&£²G—hÌãa´ä‘ƒņìG=X-›ëTÍìÄl·=lÞ̺!è}RH2ŒåÆ|ÎZŒVbžk™îV²ÆáìY­,§¶Š¾½Gnc×Q$Wݼ¶.òc-.O^BbÂc„‹Q@[lÝô±8™`hÄdäÍáÉ‹¶§ðŽUÿÒ/ýÒÄL y„/äTÌ‘?œÑÆŒ9"•œ"@Ï>3dà—öÃ×üXl¡y¸•Æ¢r¸Ar„®h‹Ó$ ›·Óà è”®é`ô|³¡j( ŽôsmôŒÏu`2EýÑ€ù@>“5+]>¥ê³ú¸iºÖc–0Tkä‹ánWÂëµ*L‰S6ãÅrÞœ`½A! U™½åŒ^« ˆ)Ñ&˜bBenƒ³çP,qÏ QZ».ìžv"2фڔ O0ìi´1ô|T®Žd¦–5ðŒw2¸1¢;V]iÐüœ¼ZË®m¾c6‹›܆è}>`_´ßõ?À¼­p®p‰me§kAGÛÙ:…¬ KIâ:–eŽ‚£«ì8’a,S惵MªcW#V0¬ü€ñïêÆ_€Ð39Aëæ“üVBw`ËÛGÊ3]»¯›!|a¸:»OÜ›™™Fo–\s”嘅‘BÆ*J ?ÓwÖψª8—¡Ùg¶Ñ®(6o0è41ÜÄמ‰/!Ù8.²ýOˆm&=rMJ½0'ìE&.zˆvèeäÜ}7#œ)ù¼·žp¾hr#Ò àÔÁÌÆ¡­…§²ÓÂUgÕ9JFy—ÉžQÉ6¿UI˜¦¥ÑÛÐXz­º[•c« Ñ ÿÚào)œmaŒÓ“¾"Ö–0ãÑr;èˆ77ðWlÍ“Õ,N÷³=gk·–|NÆ´*Ó&ñúOÿI{ ÚR Hb@Ÿ5¹uCݯ6 {qVÐ96 S^*åû #çq…8Sï£Ài N¹¤îÓØÈà¨ZX34feÀj¤fk|‚Ñßê`À]—µ§öÌ6äøö4Æ'ð!²á\Ûà“ùp>Ÿv—ùZT“™ ôº%­ Nø¦®ï£.Ž3Ljž¦ü–"W«Öh>½.ÓPÿx¹‘IµÓYÚÓ˜.^Û"!æLyÓcäKfrðYUqt-3Àô¨è“Ñ—Jˆ3“A$îŒâVQ% £i4tjÅåÇÒ˜ü%n^åË™ëQ[ÃÍ ÇíÝÆtÀa¾@D)Àfok˲4[³¦wVúÙéô±"gs¬ã]fXn㈮’¾¦ëi=&¸ÌŠuuLŠòŒ†Cíë;j†›ÛsdCÀ;3¥„›b´'Íðakêýøª¸—‡+ÏÔ4Ô‰"쾡Ò<ĘŽM\¿ñ/þEsÚ$>µŠ&X]볟IR:fŸ™+ðÒC‡Á):}ަ“iÆÈ8¥ejÞ&×Ì1C-œÛ/rC,2´¤ÒšmcHg$?Ö$}‘fb ríË~·#0<á|>º”%H¹‡d_ "˜Œ4§I›.ƒ‰Éð^Ñ%à?:˜1ÓðñSì´ªíD;‹“Z&w2“²ÀÑ» ]Ÿ%c­­ÈieAÛQÆÐ4ÓS&+**™¥d[í¨”PJlµKT‘Vhö]”?«Ïî'küãü[ÅÏ#xý hV þ” LçàŠâx“êÖ˜P¡õïñÈ&™HFU+Ö,E£B+Û½ò‹ûЏ 'Ë8eê"tÇLÌ£\˜®Á",>UO †i¤¦¶¬4^td”N±¾tìº<•N sŒ!õ¿kÀ »Á_?‡º| ŸÉÇzw—ˆ„×Á­¢ëG/5²Å®jHô SñºŠ‹·«;NŒSé3Œ ©§åì3YÚØ‘c_ªÙ5µdÎn¾d&AS×2îõ‰µÛN!®y³kM¥,‹ØV¾Ú©®é'œÒ:i^Ù‚s}Œ›eÒ?ñ]ž ú4-¬'ÒÍ >·÷Oý™==©ÞsC(2=Cè"˜@`] U¤=níoûº·ÒO°Å3mÒšÉCÓ"õÈà¶´i‚¸‰$æHaiN)*ð¥[¬°‘8¼ŸÅ O8óÓ÷ïÌY5ÀÍ‹ÝaœÖ³º5JŸ| \¶íd©Ö-ÙH¤Yy³-K¼ºbas'³eŸR˜Ïѵ´ëÛ K)Iªsq¦=i唣mÉAù¨¨wÙº¤9,±_ÎZL®o5÷‹¸Ðu🬔âvG‘Þ¦Þïš ÝC“F×}ºP…¸<"§µImùÿûÿï5rÂY®†”) X9NšHZ„avµo¿H=›Žòÿ9„KÀ[aÍ.Ò«5¥Œ²+¤ci[ë£ÆhRDR#§L—LG&.fíæ¼08Þ¨5²°!5°†× ¯»‰OàC¤ä¡1>f|&Æù1õZ)ëJ|&c^%t?ã.´|¦–cD@ƒç¯ËÖÀA)[à^Y¶kwRA4çゎ·Æ®] äÈ€©B±i­Çµ Î*•ãºí› C7Ihi6@Ãcâ5F3´¹vr7i+„ˆÁº^_]„ÓÕ„<è©õâ÷æ/íݾêÛ¸* Ö4³0 €sÃùƒyõ%‘ʈ3Epq••$&6CÐðS jvÆ/Ò¦Ö˜ÕŠG.°Vú îÆ³Ã^­‘¸æ¶xp¨Uv)oÅŽ»o2²¬Ž•¦Íwàɸ—ê·~ë·LÄ~6 ‰EõWÕúÎÒ›KÀžMYÉ%“°I<¿Ô+.ÉG Ó‹­5žD$ “NiZ«Wº ÞÔB^8…²elÕVÅ‚¥dyRóèú%vBbê$H–& (~ª¸ŸË O>&Zó5 ó†Ñ2[žìK™äí/š6mÅ2¬j»š³=VDûÙu,:“0ÆõèNf«eó.Ó¶3¹¦ld„I°º¾ÊNÕ™I>WÕÛîæn±tfIØ^s}KᘢöËWÙN{:±¼z·ŸW/LŸYÂúeYÇ—½\—;/gðLj ²š3¬Õ…ÅW«Ñm+q“~î©Wµ‘üÛࣜŽÅºo-ÒÔ§™_òrô0ƒ=‘˜Ÿ{ȯ€Ç$ÞJ&q"ö)¡ £ E¨Ej€fh †Ðã(êZŽUQéñö¢Ò…¨Ò:h æÚ/CmÀ »ÁïCH¸òò‰ÊŸ_®ƒÁ›[ùIpˆ»À”&cZ]ò^äú†¯æо‹CÊ:èÛ¿™ƒfê e¯·´…+ä­=øºÎǶñ‚ñØÚdC#Wbš«LŸ`ÁÇŒ¹èðACà©}é1#­ =Efç­©(Ò\}•n{°Nïº|sƒ3ªŸW s(R Í<)¾™ Îu¦Š?0¯FR|÷³WC˜ëP”w5º^‰['ŒdTTsSì f¾ÜjçºW"¿ÛÑë`I°9Bè0‹f(\9’A%H7 Ô§Ò;𶸧ƭíÜ>a/I=Ò¢‚’—|Xp_|Ü8 †Å)¼š¸|HôÅ«´/‡."O…¬a#«Žƒ]il½áªuMêÙE Pµí­^ecÍV$D›{À¤Ñ­=8OJv2šõÅŒ `@žÚ[u™¦|™äÃMÅS£v'zg#0ö#`dÊ™ÿÉçÜÎ1ñ1†ÒÃ:2 »3®ÌN6`ý˜¥˜ê‹e·¢‹Y Ó«î˜^Ãlv°ãZ&f’íŠ23ó6nqN]´á£¼²B§‘xWÀîµÅ¢÷¹ÂáU%Äo s% ‰Î{à6ùµ‹6vnlÕ–°Éö¨º#³qK <¹ºÔÁ|ëžz‚*Nuè3À•V¡êû~hÐׯGe È×Ñ?ü¬' (ŠŠf-\¥ªÖmÛn¢ŸÖn<í‘é”h=.-§ª3°†× 9ÙŸ[SÔ«ùuØÚgJ0 ¥V éV홢žO7õÃOõ¥¦¢ìñ{^åNfÊ¿2T¬Þ>ò‘‹%¢1yÙ±? ˜“¨Êj#wÌRŒo7Ò7E¥<Æg•0$ºA€.’Ó?ŠAÁÚÐu3 C; ð(ËÿnG ƒŸ²¿@šïñ ÂÙøòì"M$Ëçþeµ‘u ˆ˜Qq>2ôŒŒ(¡ºÒ‚qÕF¬ÛJÌáF·½žæØ¦H0ø«¦ý/¨l®jp:âY–m»ÁoëȲ˽Eüßí—úŒv3„O}¬6•®Òʇ¡)hRrúÐŒ¸šU•âø”ó6‹µ–e³y~‘±­Êý·Wæý±)]Á¢º-Áb(Íhí{Ì! vâÞÖ9KÔÁºrÀ ]?ñLP>0ÔÜÎ jC2¤is)ÅçÉmÚ¯¢cSï˜Ïb†'àæ<7Bü¢„ûØ»œD¾ý®ìcâcÌ8˜1ýžQV‚v޼ðENl¼Ë$È7Í;jzÓ?l Zwqs»t ¡E'l'Þr}ôÇt•Õû66Qz¶·µ^Qí/Þ>êpŸñ/þEÖ’=köýØ1éA¸Â%k36«Ñì- ¼×4ÂY'®§\"µPþ¤yÆ?ÓÀ0¤Ä if:èUc¶_Ä0ÕÄÄÌ]f¢Eép©,U¤ÒiرI½JZ?V¯['»¡0 †Åà¬M2tÐ0LC:éO§—Oœ¦<é²Â=Ú§œ¦_#ME£d¿HÒÏëúöÛ!Ì ×Ø„.‘ÀëV¯o*¾/‚âÍλ%˱‘Ñ3×2Òw¯¾õè±i 1ŽdNó®&Y Æœ€à4ñ£È¹° ŒV‚¿›|4VŸE<öågüê§ó{¶™$:2ö_öYÎà_R¤|&Û Epq–@W[ƒ”U”c%lç•âÐl”yÍXÉã ã0uNñ–amX» ø <ÒRvTÁ+0ع‡àç/#² s§ê;𪸧†‹ã{3’^Eê1M“4±“ßýÁ”ÌoÌé3Û¥¾|ùÃò˜*fTìz›í´©Õ Z'–ÁìUvq;±ÕËæªÙ+¡ý2ûŸZõGþÈQZ›bî€[`] \ÎÍN±d›®UC[ŸÓúôøÓÈ;ðyÀð„‡é³šc„ödwH;ã„·M‰ÌtÕû3êÁ6GyWÇÙÚ-Ù¦q:ªãÌÀΣB9ÖWEŸR€¤pƒNŸ¯Ú/~%£ ¬§2E)<9 êÆþbSZΘh¶ÆË›BoSz«ãºo:r³6Ép´<ÓL×£–û{U®µL6¨ÚÐN±Æ?î8z&[XîM;÷L ¯J3J¶4[³|”„¡†Ýt]Ë̯Œñ‘%«ïõma{hx»ê‹3Ê=Eå\ÊìzÒ‹Ï”ØKÞ7ÈtÚÍ“…i$ý±böa¤5[vm°Ü^ìé£ïø÷0)Š}Êþ¤ l³kh'PW‚ŽÎš{ƒ)0rÚ "Jܦf² ‡cIǺÈ´LN°ÓA 4µ¾›Ë×Û~ç~yp·˜lÖ¾jú—ÿ$ƒÍõEVñZ•ÕO‘J¥c¢k¥wø #p3„O šM=ØJèÛõñfd3˜àm=é…Äd?:„uÞ¶„à<"™‘[ŸŠW眮\Ü|¯mÍãǃ9àÿÀÛÜþvˆÂy¹‚€N˜“Uv…Ä99Hï½ù¡êÈ yL¢éG*ìM×Ú’;üÙÀð„¾æ|\“ÖL~U_°X_Έâ9¢ð| J÷Ý+äy_êtuzZ/üWU¸Bæ4͸ð>2¥~ Möð½ê2‚<µAžû±"3ÑQ)näIô¹U}t·%¸~LeGrìô-“`-‡GTa¡€(…GCªi€ƒAfs¬$Ò½Œ¬)9iôjߨ +í?ø¥wå÷‹IÀ± SÍFš3KKã•kÃ&»«ð€Õ8P3$«iä8i(&vWuSg×j4 †ÅàÙ³ ž¢ž tÀò¨ß~2»3x8XŠ£± {2£d&dM/2w2Aÿ¹eþQbñ‰zgΕ[âŒîBêë‘ú’ýµ¿ö×d'\•ß±ÒyƒÁí”v°µ\]i6?×éOß¶#§ ®#;ø6ådVxª©ÂÙ3'I –q,>ãI×J˜Eó– úÍ©Y2àtÜïäÓ8 :¢å)§›ÇšG ¿ï;$£·}`·BDªHu*Uuçm4æhÒÉq‘ĺ #º£S8¿:®ûa= jˆ$85¸š¡{è4Î7aÆ:AÇxìQñ¦eŸÀ1hz}‘ø›¼uÁH‘ºŸZ™¾‹ÑKvl@›ï£ñAZû‰Sm|פ §â„¯SÎÛ”9”ÏÆ*Â5~J'Íu€ûþ’~Þ8Ÿüb˜ÀàX÷[7x=ÚŸÅÛY–iŽ›Ã"c3rñhö2(ÿ²Ý[ ¢^ƒ‹XûN4!wÖÝëjæ×m;¬‘Ly—9Ž^Ò€.ÐÛ^³û]áq°„ŽS-€ f#/þrg%^j$p~¬ôŽyÃÜ á³ƒ– · %;Á[çhÑÜ-s4ú¯èOAa7³×ß'¿'tG“6ú÷Ìc²]ÛÊ߀B² oîá¥H(?Ó”ÕO@·{y«Nô¶8¡y0Edo‰½•SkÛ·¢2Ã*Héî~mÉþ|G8hîù¬hñˆ|d^Ug3Õ3ÕWóÖ–ó'ÿäŸ|~4ègÔ‹K<ÁžD9×Ù3RMBÙa 8øÅæO¢™|Ê=†/mœ#Òªµ õT;Ç}…pÃÆ¼YZk9 ³T¬"H÷4Á3‘1º Â3‰i mmð¡¯Qò1c1Á‹½h0ø‰qýjd¿_ÿtíE‹¦»®"¼GCzß~ô¶Ë'*0_hG¨wZÝ12G‚‹+ÚŸ>0Q”9’Œ#ƒO{â#r»¢“¤Ù$û±®ŽTÙ¬;Ÿ­¸Pß%Fë®GÇ’§"-ƒÕYèjÜÝ“^a€L0øøµó†.VÓå©kªÈ Ô,£7•ËÍ Î(}î“a¾¯@ ­m.»@N³eà{–‰Äà8‚~og4º%€LX<, SÆ'ð9é $4V&£‰«À )çtÒÚòÂZ *Ì›ä~ëAU[-° rK€ÜY%én2Ü­m÷ãó#p3„ÏÕïtX¢,ùµÁIIñ³W1Y;£ß,GÊU0:Š"×ß´1y,8žÍKA/½Å¹54qf„~å$GçC¸fÉŠ@Q°¾øÖ¢ØZ±Õ¼œ¶ÍiµÍ׬ÂZé~ÿ#"¬f&¢ó¹‰ëqþs0¯sÞõ‘À² æI_£ŒË‚EKŽ6x3†PuÓ5Œ' XSÇ!û¤œ€EÖˆ#îÑá4'ãçmŒdXŽ ƒºn0EªÅ––C‘ÖBŸÂπܪ¾xvÓêÛðÞì«Ã5eºo[}3ìÎÉGÊÕ‹ö÷jðÇ‹)×a¡ÏÝ ûÄÑ1ª*Ù•Åb{™ÑÑ)gŽÕXTŽƒ|84Ã`ˆ'wœfmCaz«ðÕ¼SÕ “¤’ýjpµ•&‹=ÆŒ„îøê™˜,NsøLúcš_ÿõ_×~¼ÊÛ4ɦh|ŽBÀµGÎ<õ>Ó:U~W ÈkI&Òb×>ëwÊÌ›ZVš²O|¤ Óªãl&ûð°némŽ‘8æ¾M Ÿ{‡[z¤,ÒŠ¸­¯rôO9³Fž†•Ô–¸!ídâÜac.D; b|úkòßÜàq¬>ë0ìë/üÃÿ¶À›X36É~ÏtmÇÿô}|¡cëP Xag49Ày4ZØ ²®E wÖ1 íÂ?lîjÙ&û(z€tÐ̶ú„1êËó–Û[ÃîÇãÜ áqLÆ$#!®@‹;ªk „GaØ<þÙŸýÙTä-jz%΂‘fü> [¢Þ&Å9š‚$°l‘O% ÉUÔ4ºÛ–”Ì‘ÝD hÅBŠÒ$wÑ‹±ì"^ï/´4ê£"ýjÌZìþŒn!{ý¤Œ3?áàSQtg–Ž „±Vy7!ýémÜ—í-C£õc9ßhÚ3&ô“þxã|•6ŸZ°fó]6оäQdôh-•„u6~Ã]g’µRSt2g\rWæGùÚ¨k3{Uá?ó3?£éU¹Öİ‚žù k® çï³11ORy1–èã·]@¼Ñ"øž àïÿý¿ÿ?ÿçÿTfPÃ鲘ö­å5¢É}Á~Íy…lé{TTÇÕ.FERªt8“JÐ0ÍÓHMU‘H?-ð"2~ìÍ÷‚|ÃË'jXåß|Ö4Ï.ð“R¡‹Ù^¥‰·™Ó§Òtý +24¡ÅîbÆÉ[~ñ1žpó¤’jYæÊQ=ŸÏáê)aÒ¤f©|Õ5£º– å<²¶‰Pµ!±)ÿ;0 È+°Æ ‡œlÈÞ¢³—(m²t‹Ñjn…ÆQIX‰ZÁP èLaq¸BÉþ„aè­Ißð±Í»ÁôKi Ä.ò·]š±‹X”ØÅìßÖξ¶U–Pþè7žo@ŽšõëQ܆ â ÁÏ(8>´åE]Í·±ƒZSöJ‚9ý"£ì Yáµ”*RJ§äcÛr|¢ñÇW×1†H†ë:ÙÅÛoxù„’MþXˆ.ø8þÖoý–UIÿ7þÆß p†Ã¨¼V{éÏ t€m³C»hù3¯عڷºÆL6Õé÷FG‘B¯ÛJ-pYVISœd7 ¯ H©®³k$c“ir‡äÄ_P$ä eÞÐMú5€4uÎÿ‘þ?Osrê°*¢ä·2çíРƒ-‰?nnpöïYx4³ô¶9ô!œkbx`^Élî3aÔ¹{â3Ð&'FÔ¬„Í‘/h*Lí„ÿ*MÕÜ¢®ÃàUÈüÆ–C‡;DjR6Õ“`Ä‚+¼çF·KdÖÎþh†o†ðuß½3èÍÔ&%yɺh×ñ·¹rÐ^ÌFř嫯œè%Ÿb]›˜'ÉK„É0'㥵­¿¹ÎîŽQÍšX¸ÓºD8ßÞ¯U8நnÖù«s5ãäpšÍ8jJ¸ß³žåäøvïÓËá4 §·qÈÜÑ*8U ª§„Ä6ç&R`u'³Æ s*W°ùxHc°:ôã:",;ÞeÚS]7enÌj®D!ÂD-™™éÇË(Þl²óÀþÏÿÙªÄQÛw¹ ±6‰`uÈN}8MD=µîú³6×€ŒÂ»Ó´ØEpvØÅgÚÙå4Ü3¾X #š±1ê/f”ÀÑÑÜ^g¡űƒ C» ³Âpa?,Á03\,à2ü“^2‰7öRŠUø“ZÖ4Qëtºnù¼5DÊp _:¯ž db_x2}ÉÈÚÑÓŒù"cÑ„ôV.]eÊ„uR­a#cèHaˆÍÞVèßÜ@m‰Áv òª|{U«Ž‰[§ó)kɸ“)½zÅ·Ç9æ'ìk¶;7RÊÍ—ÌZ]†¯«îŽ"%9éfm.—ÍW-ÙA¸Â¾HÙZòΣÖ-Þ£û»küÿý¿ÿ÷Ç·bV3 +:c%Yüa\™ü¥)º¹ÁÓÑûÞDc}ôùµÙÍeHÃ@MÄ2G^¤üfÒé‡S «l`ro¶+a#2A0U «Z2ã— •¸M»X;ÉU ô›ÉO…Ë 6CHPoGVk!g7Ü ³¯í¼Ão›!|ÝЙ”Ù™˜‹(oQÊSç¼-9¯º§»e ’^{D Mô8·Â–nB‘ÑÑÇïuÍù¬`‘£Ô\[Ô4c½$Ô>Q6 ¡Ë@[„&ÓáëØ¥ùóMůa”ŠC\j§ß ™ý”|>ßžÐÄ ,o:õõÑèLõ6 “cg¡ÛtélWVûÌRn×1{‚@z›,[$[î6ñšj¹@i ìú×b8Ç»L"Òµ‘v£õ$a‹‚R(îQ¡«%np¬÷©ôô´E·j3NËô¥X¿@IDATbÙÛ`1áØE€àIv‘Þï÷ÿþßR8»¿²‹«7Ñ`eNÛp@§ ¹NvúÌ5°«ë4Ù3 @ÿÍ"ƒò³Ö˜_?nÙpQú¥Æl’û•QöàÔkš°4~kØ3©Œ Ú3‰iòú€:¾2¥©³ÈrÓ$ûâ4cΧ™ç8›A(`(ŒÕÿø»{Ú„Ä?²Xb/¦f&`Ž©úØž7Ä´NûjÓÈ㘨©ËJô‡©ÎË/ýÒ/U—.—B<}ɬíi‹GL¨‹Ï!aÁ‘W±Dš,Û#Y颱µØcXE)<7kˆî|ÓÓÑÓóŠ‰øXìub¶Zonðtľ‘©Íg9Ø;²nu´EÇbóV»ô:! â:>i­‚Õ,Ô¬.às(P*»éš È*ÍpŒ™º·‘Ó²o²foNcÌš<[Åk@ª‰ìlˆÆ÷Ê[ͰˆÖfßáo87CøêL*“À²ÝtTä£=0Y fÚ “•Zœæ.Ö’h®¸ô˜31ÄÀ¼Û¤|goNû´dÖÌœjh–Qäêç­óNÙæ_[-QŠÌN5˜\sôVàÑdTÕ¯²;Ãg5Ã$‹a\Ó£?¥Ž–½Ø!|E²I›P‘²XÇÓƒ[i¶–Ä%ã`æèNfËâÑÍéZ¨ºÙ$Òöl¾ø5>QˆÕÚa‰<ʬ®,k’ÛÌ×’i¤gM÷Õ |¼»XÚö ÅÙ׆=ÖÔ6oлˆ%Æ.:ì»x­ùÑ#tÀ§Ï¦ÑÀBÓ·Ð.: µ²‹/6ç 4ê‹Sž&é.?MsŒì éØŸ N1et<ìXi1™kö£×ñéyV‰Æuúí->§¥ÊŽ{\@+< bnO5ý]Œs£ã˜w8ó¼î []/>š'-]^Lüb‚¤£̶¥.¨e˜1åÄbÑèz Ô®ÓuüÊ´M#èXÖ¤lƒØzÄŸURu‚ª\Ëx|äHæ´íãcã* MfLoµŸæ*Rã·s­Qusƒƒö={ÕüY×2ÀÖŒ Ôeàš%Ó8€‚’Eë@Ä €Œh€”Å™"N°S®ÁyÃPK²Ê(Y÷UqÇÓ¡Œ¯rú¤í×%C–!„J0jùŽP¹ƒß x•Þ¿kn†ðÕ#™KñÛIøA^F§Ñ)fú½4&÷Ÿøbõ›$†§&}¿aÇ!²YáHRŸq§Ñg¢“µ9¬U|«ËŠé[;\ûèøÿ¡ø(…H=ªT" D=²ÿ®Àµ‘ ϦØ;ð}•'2¢¿3 Ö#…#@è˜,ÐÎ4Êí•׺ȋ÷ħ( åÏü?Íh_‰ÓkÝuÈ–Šò˜x¼Ë$Œ?Ú%Ú“RvÞJ©Ž 7ÅŒ:ýŸþÓz¬èµ1vâD³Féµy×ôÑ(t ™Ôúª°ïK¡ä¾5gÃVvQwždɶh0œl±‹¨ŠO6¬xµÄÆKylÀ31?öc?f¨óìúLzJ0¶‹c:AGÔ…r$ÐéœgopŸ ;Õœ,)—¤Q”çê‹mÈã¥f¿˜ò4AâÂG^+Ë‚ës[f‡‚ÌFû‚¥q´tÝ:ˆ°c‡0؆²¾8Ÿ¨k`âÊ2U…þÂs²üŸÿóNÛùdd7Â[Å×ׄ>SZef»ÌÏS}4k mˆ‰œ6w©YóŠl†\˜³æ$ƒg zŤœ«º“ mÖˆ•Vé²µ°¶êQØzIh• .ŸµÕ÷¢ ‘Ù6¥yôñánnðÑP/ã[ëb7:¶'2 ¨ë-°'~¶3DÛc°Ðü™!ê,n0RÅ™[_U:ñÍ„h²·Y±¡Ê!Ȳû›UÜ#H ÎÄ:£˜ZTÛ­ìÑ1Þ-sÛÜTz>ÊÜ á«‡‘äÞ~IÈþ6õÍ]VûãpUÂÔ##®N[(}pS «TüÁ%~Iô3Yϯçô%ÿ¶ ɈBY°dYÊ H7º ÃÆŸ5öܬÁ¶«Ëš—‘IbP@؃lÒôhùµ eñg¯ú][õê±»3|>#°ò„Âyh˜™Ð‘Â{CËdîaNü½¨œ2ÇÁ ðJ.èïEhN»bÛ°6™r]_)–w™3§¦j×Ï=C¯ìp³-3Øtóù´õ|8Iiëªôx>û¤LÂÚþ:‘Ï88Á.Ò˜ùÜH‚à—1Òóì"ýv1¯HÜ)»øb{‚/›Åïi.2 *ÐaeSÚøj˜Ÿ.š›yë´§V)ÙŸ@"¼yKÇ"KÞ8*Ä+Å*\§U¯‘š*}VOkü“áæ¤‰G.€>cØÌ|ejæ›s2­ÝHžÉhÒLKœ’]Ãf´îžlCÉÚVœY}U®cbÓ@#Ðß>“iÜøMé$³’-Æî•sZ¯H‚r­4¯:áß’?ú’™dè â»·;¯XyMVXâ/‰Óïñ±ä"~äHæ˜WŒ½[k}\+®»m^.Üf¶2öNläú´®;ò{6ÀØ×Sà«ÿ¨–½#JøgÿìŸú–½Éœ¾Ka˜.Ñôe)!*ŒL©ž‡Ï&*Êv¦‡G[$NÙ€ÔÌØÖ* ”Õ¬•!ÌêDäàÄ@2¨ÜÉ©:H·œQ³¹ ÃNÔò§ÁSõø†#p3„oÀv&s—„/Àa9Ùœ¬+>ÁÏ7§mlúóVL{6!M»»léU¿)ú¡±µe„µÞ¦îèfˆÙÍ*•øÏý¹?'ßô ÌÍ™¸¥Ø¶šÿ´Ñ[ÂmU×JŒZù&~5L`ó 0Z[~‡?ß‘°{%î&ðéÍ[Y#§ës¢/. &{¡6K‘~Ûþ´•|ú˜Ü=£ÓtDÇ”8ÕÙŠN]€hálZ«Î'×2ÚS™ƒMÉ,µ¼*†ý*2‚8ÿªŒ[âT£ÀåX’o žy¤8"ŸÒ˜£Åfì"Eµ‰}~u^»h’D Σ?t–Å'8[ò—þÒ_âWƒ9ì"¦eÓ.jd†î×ßÝìb8„̪QkéʘEuˆkmÚ¥F#³M<`Màš^X!ŠR`ôY*RÝÅè%‰[¯.C?6 ó“ß H+QýH¶öôh„³=‚‚ˆ¤Ü1€& ] è[ÌÉœð¹hÀÅ«\šYïop»˧”öà‘N]e®)¯ÃY(tÉø°–l46/ˆ 騰·G“ËüÊxeáϹ‰GõúîUá`狃ÐwL½V c¡e00å®=0!8C¸ja÷èsíGºã¿g#Œ™¨&À¦À›G@.­¸Rǽ’5KAA`a| X²D'|Ф¿êB˜Ê”*d³&H˜”­F¿ÚYÃLõ8ÕUÂ$ƒÊQò²ÒèÒmÉg=lÛ&bðZ{ÿ~ĸ· f̺µÓšœ9rÇpsëž­ÈDî Q7ïó!QØo€@kËlö-Ú|ËÛ H0z|b›˜7 ò/<@*ÃqØ+¹Ÿþ韖qŒª¨6ÓQd#G:& «›^Ûv‡¿Ç#0<¡ š+zF¬ç,ÁÅ ¬ô&?¶ð"ñöj°S¶mK윭»)}d3&Kܦ~m›Ù$ˆ}• ¯ø\ˈ ·Ás™ˆ«k2¾-POÙù¼-ûäÊ–,ÍÿD¾6·ŒõtÓ“%@Ïÿãüì"Úp†ói¯bßïû}±‹8F㌷Ç.ŽÉñ4ƒ6Øu#%¿+¶+”ܧ1ëðœ“ñÀÒÊ寉Zöbü*|*Ré£c~šæÇ q†|²a{Ìž C]Áš˜‡) àú#8}±¡0Êu ù%nöE ¹®}óË'Ÿdóý¯ÿõãp½*¦Ãzýª\[âLf2!fn`±q'3éãÍÈ6õ  Ue|†g#樊G¥©Q`ö÷#ƒº&{N†¥:<6{Íõ·ÿößnÖ6Pá?øAŠe1¬ønnp®ï}óÝ×)°éuü˜E—ÉÉìMêËV,l+±‘َ–àeVšN>‘VD²ÈãfÒBÊ6WýÐè;`Ø[‹(Áq°yÚ<Ó[7ÓFdtº!äïý·þ4¼·ŒsN;-‰NG´›Ó´ç-ŒÎå“tÄÜ–{Æp@ª6Ë#3<Ë&à2+‡iÖ&ÉÖÊvèÔwÙŽŠœ“¾©%U$^QI¼0@œ%ª«¥™²ˆ·ë@Wé"<:å%™¿0ЯýÚ¯%_ב[ìØ/ÓÞ??B#d$~6[ˆåàªL=ö"¿(&0?Û·8B8Îí‹Ñì$’ŠÆÁÌEâ^Ù6RU]ó3Ž¿×þíα)_;óc1^£¼²ÀË5gÛFÕv5Ù_à_^±vÁÕ«Û«J(15£5‹¤¼¨¹.<ÙðX\'~ô6•SºIÀ/9v·‰]tø„QðÁå€ 'ÖØžþÂ:èvÐY-&±Ñ£{dcÉf‰Ž+‚s\«õ¦mçïü¦(|ÃhS!ŠR b§Š €Gü-É *j7…$>0ør" ²ðUÚ='Pw˜Ee$î`Y8IöiÒ9Ÿ$ÎCÞ5KØŒ]ˆTÖô¡-¢õRøG‰Oã-+ƒ¦%iÒèi;«)rƒ¡;’Gq,*_2r«¶cšbt9Þާߪ8f‘ ]^É×~AyŘiAmÙ/tËìEi–ÒR錭tsƒ§#üýŽŒ‰2yúCô7üicçÄ£„ BGúÍû#˜„,%[ì$ÿ÷ÿþßåBr½²}î2ŽÕI–¢ÁQÐtd[g§-j•ßv% 7Î0•«ÈRj™‡?-®*’ÌV È­(¤us‹º5à~|ÛÜ áÛÆíw:=˜ µõx3%+¶ gráÀÙð¸J™Ë lµËâ÷§~ê§ŽÍâYÞ+²O¿a‘ÒäN0Û€¼nØc,]+ͲtZ]z0‡€&µ¡‚Ë^å‰Û^"ìÏQû™¥NTäüê˱awÌ÷~@Þ™¢¶ ZÁÿ31(4®ý(˜ŠùÄ33ó ð䠵К“9˜y&c~Al{L„ZûÁ¾ôíÇ’©Z#Ú?oË5B÷ófK4Ññ¨6u½6«UœØk3né;j…)Úâ_õØ¥v+±ºÎŽ]¤^[ÙÅDÈÌöâðg¾fÝ5v‘DY÷Ǭßõ$o j$;S8r:Ї҆n ÁÜx<“ùØ’‰ñµgº ܸÁ…Ü‘-ø…Y=*<Œ’× ]ÒõÛ‹Ë'®3ßæÚznn8&x2¦m‡ü6Þ2'Ø¡Fã=qk€ƒL þq -ðaì_dMÚTNßú7ó7·Z¶ÇÉD «ííõ#Û×ÖH*Pxý”ÿ´qwjK«Ш1€™®Ÿœä×í¹ß~v#¼lÚ÷kª'&0'sŸ&>³ç¦YÀÌF=ùH6Ò¶f;9f­‘F#Ÿ1ÁËõ†³ˆUp4=ŽÁÍÚªñˆ; ×[䱦µ¦/£ÆO3Ÿãƾ-R- ·Œ¯]hÇæÝ1§#p3„§Ãòr$—-æ%K^w›úMkŽz[T"óFánZ;õWÀ[ö- i„‰=*Äog·FÌi%Ë8sµd$C' X’ž"Séð7•Â=WlPˆÂ­öìaˆ¥e!§ÑZ–'Õ>Ë•éZk~Z%`WÞZu?þˆŒ‘A´»ù@ˆã®sÃösa^•þ$ipŸåÉ‹C7ÈÂ-Óu»³ë´ó<|¦yt5ëbYÓ‡äÚP‡»[S~ÖGíŸWÅXì¯Ë[äÏýÜÏMšWòjH¤zÊ'<_ƒ^ü–l7m<_B)“‘=¯lðk ‘>¶<ŸoÈŽ% ˆ'Nnäý:! ðCØŒÎçž[ uÕ"zÈq()VþåvV£¸ÊÆÐ\% ½9ÕFnå{Œe%òszÍacxmþw¼‚´ã«WÅ$bÿ†s@N'ê—Uÿh=Ù*³¨½4®OfY“uô½mN{ÙöYá5™F¦Ù¼é$²Å~-WJ˜k—ÌôÚ.¹Ö²…‘#D iB ´Êß3f½‚÷Ë$¬LFFŘoUðð¡Xò×ÌÄÎñf‘>ЭÜíGçqôÆ_N?`[óJ˜4§xäÎ2DEMWSZ$˜p¡M'»eä1'¥ˆjcRÊ’ñ§58k¹ÅO* 4ø t¶ðÿÏÞ¿Åêúuý¿¿<×C‰¸9€à•D!ÑT‘¦•ZZ» „Zi%¥V¡–b+RDjK±­i(Bw´D(µBT,†‚ˆ!zò}¿ïöbp?Ïœk®ïšk͵Öóà™ã÷¸Ç=žë׸>ãÚÞ>àÜFÖm)0j$ÓÞ¶˜ÑvˆÚL~Í€íódLœ×íµPà·,©×Òã-éÄŒ»LÜü=R¦²w›åþ,ëiþ¨î¨EW³¾¾$œùm®gÁ‹%ÖÏS¹B]‰V"g9t «L}@ŠnµÄe)ÜK“=Üœr°á¥6!ƒòñ­ó¬Ö!)ÓúÌ+vá¶QÀœ¦b$H òu½VcnŸ’ÅâÞfÒ¶ªK,?Nœ¶<Ô”[,9Tœ•ä=sŸ¶ahzøe^‡Jé‘5‰ajnMaÂâß·¨Þ×ì/_Ó¼=ÕœÂÐéç’‚mO[Ž‰é¿¤ñå·bX^Ž—7»ã]'Iø:kää9Û ³Æ]Ù.¨¥ýçìôSË€YCÎÂ_Ë#ë";žåWKHœQ;ÙÏååá¤KÚ˜ð6¥\[…Ù°Ár.õc}÷w7•f7lƒ÷|‘´ìg‰sZ‰P:A´Ó[wUSú‡9@쮞]›ðÌz†tÅðÝõÙCÙY?huW“¡N²ÒO\FFƒ¨½¾¥Ôˆ 8[¬õ–B{lŽÙNí‡Ó˜!…9Z‰+zꦶe!)(1¤{Ñj3¯˜‚l´^d—Ƥ³mYW#ád1‹’,Ñ"¼ã‡ž·³p0Ä™Q¤ÀlºÎx`ž»d"à§ÛCJ@p±ð?rèÙÌ#K`ÒY½µwW_ÕŸþ»³ªƒ Àð´1±Aå‘p(ý$”þÿòÇ`Û¦½åú¬8Œa_> ì áS ÚÇÉQÛ„&œ¶Ãdü­…ã7×ÛkMÇ(aÜWÄêä-6ŒQáÜ=Øaà’YÏJÊsŒë©[«H›|ôßt<ífyŤ:Õxøv#êß'”óÔ鵟|ô)0{B wf†‘"cG hí>àà̃í©ÌºbÖ‰¢‹5£`Å\Õ¤³Çe²)^BË2 ’^$Th7ÏfÉÆÚj²íœšt %JvŽ‘pØSû:ãå‚—Œê¢[¥·ó¼¨Á빟…†ëÃ_›µU†°×Ê»-ûù’ý§T=tEE-;ˆxEÙREGa‚.íÇ8 ]îs(-^Ú3°½ýío²ó²Ü™KMÔùù®Xð”)ǺHïκèÌw»A!v ”ynHfxõfÀ†==»ô¥|5_ðŽæ_„ò ¢­[‚­®r™%eÍU_Þ&~éÓ.oyÇ»ÛÑðŽ- âÓ¶Cè3Fái‰ ‹A’é>W±ÝEU¬­=6Çì :œ…Nf’ÎG‡Ì"wv™²™z›˜Ï0€iv•Ÿ±Ô³ÐíäÅÁ¿Æ£r*æ|S³+¥Uk¬…7ܼwƒ‡Ÿï^‚d&Òú¶ 48àyš|æv®:.5¢¥>+Ó ÍÚÔ–õjN¥ÂxjyR®£Rt MW>Ààfþ˜%d]n­§ã2Ì5}úvë0vù)°7„O˜²brSS¦æ$-ê&„Gw˜®:W"V‹1 šî£q·eƒ§‡‹ðÛ:8Ou‹þ~­OßãÖŠ’sc˱dÍ™QšSÿ—DèÓr7Ò:Ý©^%¥ä:¶]~ü(0{B0¿€ÙÅaj/ÅàØ\û¶R3ÏN!ÿ:ïšgÛÄ~ã7~ã´©`ñÉÕr4.Ç* À»,\‡ö‡Ëd¸þàÀÙØã+q£ a«™²»¦®…HaïT½—–,“aýÛ¬ŸÕ$ ­B–Ð"lA>„ÿj°F›Ç)F"oèi=©1¨9• 9’¸š®õ¥¨è‘>[­Õa•ðZÀ8§ž¹›á¶Vh Z[ðÁìz¼××íò5R`oŸ:1éðš bÁ¡d Ä%%L&û:……˜Óé` N†èq¹ª×€*}z|P¯gg«&‰Å:è1ܯù ËLÿ©Ÿú©i?Jôš9MÛ‹Š¡o ƒ`à¡L@3…§œ6c° fO”À4”æ 8Eö(ƒ;ɉÕ<8ß]а2ž±Œ]d9ÉXž¤yÐŽ+„zQ P‰°Çòn³‡1§…2ÊŒcLÁ÷¶ƒµ1H_„pÂ}qGF¾iAZNUíúƳå,”²gïÞUe{«›À]=>ó88œƒ:w¯^°ì ˆÍÒá¥e®ÚÓžvW8Û¸à¥Où”O¹v¶ÙZ™µ³Ê„眆Zú£[ì©-¯Ø¹ßƒ×جp÷4¯ {oû¢yÐÚuêbZø±ßô *‡…kz8-L ä©vuŒ„Fè>Îû·KKÅÖ’ërïO {;kbfHŸ`ðV¹u2á̳$"T m¬oC·Vì@£8ˆ©Ï×Tãà 9ÙÔ@S"²öX{pocèÓ:W©K\¼´È÷lòËÝ¡ñu6CÐä0hxÏØváz)°7„÷DÏQ Ó¿ï}ï3Yñ– ãç–J‘²½Æ´.k¯PP×^³$¹ôGÒƒYÃF€È:èð®Æ«Aß‹žìà·2OM d¹LBŒÆaS…ÞR·‡!Ý•rwä.?fX÷„Э {ÑiçLÜæ‰/¢LÃ'ÊvÑâ9ßeÆ|Q)‘Úa:„#ÿ(ÆÞƒEôtØ—×\×áÞbžÛ™äúOÿéò—Þñ.cZ^ÍW×ñçV3:¿»ßÂ"foô¹´Îä&šíÎŒr7¡&UÆŒ Ëk|P'y°,‹Üí¦¥‚B_ÜO¹þ“HÆB´6V¾dÕªåûßÿþ6¨t‡g]J-3¶PÇñÏW¶ÀZ0÷nð”h·¼&Zìà³e'ðSóL$ÿzdµªAF¾a'(I·S©dï-fœÇéŒg†â¶žûÄS †Ÿ•N¦gÎ Z×Þ]·Ö1ìòõR`oåä è½M‘Y  R1‰;õ¡7Ÿt.Ñ10‰NsFå>T`½Hw²G£§)&c!¿õ  ¬ß0Ý*¼Ueãoœo ÔXq$q·.I⿾e—oÖ=¡²tdÅž™*¦hq;MSýtˇD²2dîõw XvojÂC}—ùø1üÇÿø òs>¸[7íù’Mø{C1¦x _ðìÛe•«ÉøÓ³¶3ÔÌ#©÷¹ÓÿS.ø ¾£ ðàpIo >Œ1Ói¡ðQKÍ„¼f a[.¨KbíyEªÕƒÑ/õýÔ© ÙFݻƢhÁÉJi š>×BÎAg9ÑtMž²û­Tæù¼2˸+[%,­‡Åöôñ]s;)Ouìà³ x È© ڵ؂|*aG2®t+Æt\+{<¹À}†”G­K^O=R˜À Fam7P:Ñ€«ÛNgø¶|¿@5¬>˜=ã_G¸Ë×E½!¼'JÆÖîR“%™r˦ª¯2ö˜­Ô¤ ¨8¨€µ¸N‹X¥ORÊ#x?à“Æù/iº]Ñ3k{Zma ë7,Í mz•IG=À^…l)S^ú ì벿œZ{Þå[HS˜Â!*Àd¤iæðT9;y(ÿZèWcÕ`ÏÎÞì”¶gÌp&Á)D×Á’ƒ²Ø3òÔUÒÒZ{nŸ‰‹³³åW3Úc$ìy°#ç·Vô¹v~(¬S²®ƒ‡6W¼”¯Õ·¶ Ív÷Šž6c£0*KŸàôî%5p¹äã"6­`Iý¨äs´]Ss¶`cÆHë'`3´Ÿä§äWóƼy9AÞÎð˜6Á&­S¶ ÖK_§ýd§jˆ|´Æ/q}µ\4ô~ ßÚšlO8¦6  Ám6œêi»Ø×9KœÓJx®í¢p ÛE{Q{-[ÐÙ. í ×Á<µ²ïkü$ÔÙÍðÝö™È»â-£2H{‚˜Î#ˆB/ØÓ2ªaÜvW²Äñaç†åÝÕí Ûd'f–rßÊ)¤K*½¶Ml&ÜI$3ͦÐ.Ôòu˜Q–…"ÀtÌ2í¯šÑƒþÄOüDåb‚ãá|Tª‡Wæ+ì¦@8+vS“#ÞÌ™é ¡~à8¥›€òTãrBv÷4ÌéSái‚™äÙÙx'‚¤*¹f”Y{ƶ9Û”]&,8ÈŒ‹}bÂ|¹Í$2ûWq¶$D[.F<¯Ã¸Û²=†!q8¼ÛOÛ·ø禙ïË×H–|^sk¯6ËN Wd9û©˜KÑKÆ{Š e³‰a-²¾ä »\}êaÅ("gZ ¬¥¹zΰ+´$êgBPùOÅÜÛUZ ×̓ÕÏ%r_Ð×l†øÊvg·j/zÑ‹|YVîñÝŠždí¨ Ï¬jʹì,µUÚ.6Ûùl‹½\·‹Ž99Pæ*—YÖˆ£«ŠÖÆ/ÕvÑwa]d  2CªÐvÚ~ì°Ÿ94»â¥/èë”ðð’GJ˜aÑ0ñÚŪ"j”èÛwYmn)4›!gm¶k^™¬và§ûÆX€¸߀ïC"™i6…Cj Ž v†ÓÌ\"çg]qs@Âïnñ}ZfçÙ]Ø 4CšH¶&ÿ Z rŠÖ¦üºàŸrÍÖ˜V±~¬'õoAÀ€D ©Ý‹rOAµL^ƒ¦=µ~Z‹rõôøZ¯Ü®o4&–²i{*ðlEª’º´.:ã`í ìÂuQ`oï•’„hSÃîa¦ !lDÌÌ.Q³ÒL“^ùódµoÞ÷`ÌT9 ÆÉNu+c]LR R¦ùÑëŒí~=¥ºC¥rIÍoü 0gú$kw»Þ®¼nM¹ƒß+±öó)N÷„íN…©EyêtŠ¢Úü®ïú.þ-þ€¡;RkM0xå»rɃå‘OY3ÛÈÃ[&»Œp/£ÅŒÓ玑°JV#œ›dÍXa[‹­æ‘ÓñìÛiFq{z÷nkÒÔÞÕ®Æ+XqáZË…o÷ÊW¾ÒU£¦ýÏ~~P0¼¶+7Èæ”ƒJž~í6ukÇp\Ñ·Ö³¹Hp‰d~±AÕƒa<…ƒ×A›1?„áOÁ劇fð—lAÛÏø¢eqqgíÒgûñÅ}ýÙ¶!Kn§ïCºÃƒŒ™Œ`bÏÚœÌOCx¯²¹”é¬ʼnöñý1YÏvÑì]7ÓÏZ8lMàÌQä.'‡q^ñTÍ,vˆõ=<^FcK¹ 0gl6à˜ý×ý²Ý½èÌÃXDnÃiÑ]ÃÚϔˤO<{šHfšMÁúóäöÄ 6vEyð’!·\4Ô>=¥`hNöÕ {78´Ý…S LœžÉã<',ßtä*㲌ê“M96ÁÂé³mžf¦k•i‚ÀK—¥ª*ˆ7³$8Z³"=8p·U1y¸mTaZ;%H\}ÏÌe B{¨­ÛCžÂ†ù.\ >æXÜN^Æ$v0QM­ClÀÔ¯IÕ?aclw؇¨°?1ïH^²`eå é·-!Ŧ_ò Œ¡òh§C9}…~¸DúK!ZÒ‘šA„¾Q¶—ØÙ©ñŽKÍÔ·upÚ¹øÒ”ú"Éw¶Í+í³vŠ";}Ä]ÔŸíD…ù,èîô‘jPö¢Ý8Ù4,JN`ï©c™ò[Ä|¶µ>ÛOúi˜Ã2e·ŸÙ6€ÚþÄÒwö©³•7jZW„JjÐÖ½³í/ª´›b“aò5a´1!]rðó5•/úó•·<ÖPl› VÝ0¢)kyÚþð¸K¤£ñu¬ÉöZþŒÍ>°ÝÿR¶¯Ó§LH>"‘ ‰Ž?¿ÊøóullÀ8~;pÍã†D¬@T¼"!*Ä'AÔ [… û3IpMŸê»¥gÉÈö+¶vÛU¾ïi{* ýƒªeØ>û.ô¬>©|`'’+ÅèôõÃeÖû©ŸBŒ/=#u•ÌàÝiY!oUeKÍ ¾ÍæÙ!ö[ÞéÔ.~eèÖšÐî):øÚ™uóE˜Ò¯K ‰Ÿ÷íp®ÝÒštÚï¦ÀP`fØ l£žs×:i²)Ð ù‚AAKÐiF´7H0’0)ÕX}Zr©QÌgàÓʈ¶þã¸,Šõõ>yA‚qÝ ôÀÄ¥Y ±Êþ@ec¶¨mœfó$‹-zX£Þ7ßÿïü`ûï)â$­Ÿ2kû­Š&s_ú?RZèñÃìÇúañ¡¿ÊÃÀó›ÓÁÔ·B¥’¹W™õ¯ôeôôÔÂЯPJ:RŸNÚ‰"•¶£äqTÿ†‚W†äÚ’ji…«Ï_¼×íÏMS Œ0è“+iQP͘DšN>¡+[”éÇVjöQ$ÁÔß±P‚™lm.y$娷Óà\Ôìë¾îë4ˆ ¶¯qL =n÷Û7ƧÄÓþóg3TÖÂWÂå8T¾ 2´UÀîZ"l\©~HS°:4ú®Ý6 –¨.ä Z;jìÏiÖ‚>*ÛÆvø„>NÐÈ—-À&Ü5à só ;é8±@¼µ¸«î`& W8 ˆñû’-`œö$”ƒÆ˜D&AýÑÏ(Ggdt0ؘ֓ʄE"‘«h“ 'uN¹Cñ¤Ì¸ˆä ¦L ÕïezJeý{5#geîF¬L,u[õ«©¤šý¹)°R`µª}Ll³Èl¼œšƒ>„AON½cÄð‚³ÎV*¦`Sb”£ÈúгešxªÍdž§Î¶™ÊÑ2ÚÃðÖ+þ~îV 1•š"uéªu·m‰(©KdÛÝ Ø¾îío;vöÝݺ¤þ÷ eº=myõÆ6·«·×Ò CÅëïô©ŸüÉŸ<­4`?Ÿ¿Ó[—פ0ÖRÂMóãî|—?uö®¥U½‘ÛŸmðÔ*ͺKþüˆ—Üõ¥h4ÒØ šK—·?íªö¾½ù–ìæ(ÎO«¹¤ë¶Nü¡ÏMl’ ޳½V^ÒÿiãËÇs•ö^wѯfÀøÙ+4³ÃìØSx9õètB>bÍü(´*ž[SPi¢ˆ¡,(IéÜ:p1 KcáB^ÑSã-|hy¸¤ÔÐ9þBYs‰l Db¥iL‹A™ÅdmïJQB?"]-uO ,ܶmpȵ g)°3/m‚‰gÆÖž­À« òñ µJ¸)é¬ê*)#’wxÊÒŒÄMAÊà¥G°ƒ¸ÜÀg³ÚÚÒN²þûl7[Çì%87¹ë“¸·˜ƒÁx!Hì®?\Ã9Ù| ¶×]âJ°¾z—ï…{Cx/Ôûس´2þàQÀnbÌü6õMn‹>dÌ8®µjoìgr·13Ýñ¥Nn?Dþ¡›¡Œ<™½"||ˆÃ©vJ¿¡%í¾7’%v’Œ <ÙhÖ¹>·!̉Ü{3×´"̆Ц‘ÐÅÀ  DÛ«éÝgs«æ!ìéWÛ5ÚÒy›`œF8;av?óŸ}ÉÐúÐh£M6“ŸÉH"»©Î˜6{MZ$ÚOBšKèƒdobkÒ†§!wIãT-!S›€´×¼æ5§!rl˜ªÕæ²ÚÃ\Áw|ç0wrªÃé †¬|d0"œöìu|e=‚ñ±Ûÿ´¼¼ ©uç϶ĺù¼üYwýÛû˜:ýA´§ÚŸ6Xk8vZ‚¨PäÞzë¢òEýûÀöCèg}ö¢ök›µ¬½%Ž» ¢ù±ŒÓßëŽ5–M“ÙÑçEøI._œ»ìÙ !) kzJÂKÍ<~Z`ç ‡H^å+nM‰´-Ö"º0ÀW¤õg5ñ˜Ö}#KT@.U ,>É?ðþ€5sïO”]s À Ì6“¿9 bÂUù²€‚&ž !phCXNéÜ•ñIVš–ÖÌñš8Íä¶—€¨?êÃx\zÖ'fœ4lAÜà.qའ20Ü®5`쌴vƒÞ¨æŽGhZÃØ§/Ú5×Ltß÷NÌÜò¿Ûþù…:d³ÝšÉ]¸Kàƒ!Ž.Y›‹ÐÙ€ý´öÏrÛ¸œ®53µ„°=E‡ž»p9³™Z[ö4¯œº Õù¿ Ä4@èYàÐ+â8 Q€T˜$¾×d“Ç€èjœ˜ê–gm,AÙF,Nº5ÆæÓ$Ï+­KlÛƒÁæ 48 Tk `¯cØåûDeôzKbÖr,ñI)Î9DÁº_\{¬Ò‘"³–,šù£Ë§D »5Þ›nZ'ä”ÓÞ-¯£/0ÍS¼ðEö™œ¬ô ¿¢…uR{»ÊFî3+‚“²F;ëR'ëëvySà 4”ܦMØHá>áè Ëçaé7{¹ešŠn­mÀìÇ’dœ¬.yËÜj{©±®ŒQvέC*T›üaÌšÈC—E†D©mŠØ‰$LÄŠ|ë.æiŸ»fS`SàŠ`XÆJ ‘Ê-ì¨V’¸lí “ª$.9ñ2»)OàÓÚL6 ·RWY Ö[kÙRø+ÕUõ±vyÂ×–‡2£J!U\òYh¨}Zî,z¤p¡YsË*ĆV³wƒ’îËË)°ºÐã°­y Ôµ¿ ìÙ1’¿¦"(h*†EuR3€ÔIíËuaZ \a«@l=ƒµÀmU³@¯[`pöö€q Yç‰rº‘× €½Ža—ïö†ðÚËÉ*®œî¤yÏ4ÏÛde<Æ!d²ó…85 Ï ‚RÆÜ™z*ž\ÃvÓ£¸+ÍÝ:zÉz’ö ·òÿ&fx©¹5Ŭ…¥– §˜ä‘¥Y9i0ÔaŸ¾Ôúº]Þ¸œö„ÃÍáÉô@šføˆ,ѳ门¸èÌèy;M§q‡™Ì%„Ð4X å·¤¡';UEÈÄÚ 2gã ‰!­ê´iœ9ÃŒ‘°hûÕ¢¾æË™gwaS`Sà*H€ö™á]‚Sò-}N?سe¤ý[¢ðÔHËc¡`‘šáqKÁô°,Q‹ÉÔ—hÊÚ55§žr¥ògîàô>Î{ Ø9“[£ ÎW yßÍ©¯¥rïO©ºk.§`6ÓIa`[@.PÀ ìü² ê9‹ˆØœU)duˆ›Ö̱cÔ&€Xu ¾fŸP.–JÁßÀ]Ø+¦}wAå|Yq½/HW¬ØåûGß\Rïß;nIÏŠ-oR^dÒc˜ÍÙ߇…b›•’rŒ'ZJqP“©=[üÃÉŠ5«?8³%äÜòêhÎIü£ Æ ^}õÌ›_xήYíåÔlâÃĽËÈoÉ︿æ5R`ö„¬…¯~õ«=faÓ”³ƒ„%4Íú$iD&à#A;‰Nb—Œªã¼c‚„uN?"×E‹žJÞ è˜`XcÚ§8”Ú^´ÒÜj†· ;Ï­:T bç‘]ØØ¸:âÓÛXö4‹óàê~9bO±ª*KyÿÌg>s(pÈ9Sœ’ËLú^´¶Q¶h”ãÊ2²ê@…ez)œ²õð”K~㌀݆º:×xЧg2zÖ]YdÈèY$÷n𔪻æ*XMsÛrº¤m`/àÖsy•@D@\4!«Ášº&í:†ü±ÕÏ_Ðte̯­gÛi ôfŸ©#óFQN!X¶4¤ õ:€]¾ØÂk£­&½LÙõ˜Í]æ˜.Û’á7mâ-ãc­2³³yü!Â|{|Xë 8a‚ïVI{o)dXZ<ËReâmG;xÔƒ¸4<çS·|Ô'ìÛ³ûsSà®(°Âö@j¿6læUóYö#!:¥Jj²õ)ÿ˜ü~Îäti[(ßÉEïõ´¬„Li7Où„émd¡öÒx¨Áb6‡kÿy³H(ɇt–´ø5péOy5f@PŸ‰@a}ËÚó.o l \N5ù'©Tê`ê¡\]F¯T'3C"V­¦\¾l#ë[R¶ZmÒØ¦=3,´ÏrzÈDº!¹^Ön+3*–‰Ñçø?±L<ùgq¬5K…—´ÀÒ†{v]•O{Þ5›w¤@ù#šl)G”rا ˜šÀ^À¯4zο&gpqÞFÖ!`9• ã ×]“¹ÌLmÞªœO€Ö##ñÕ½ ¯B08 ÷ŠR-ñ¤&9N¾wwÞo ì áµQ83…‰.e<*IâipyžÒÚF’Éø¥AS:•Í´çKV0C^+ÕK½ííYD†óÊ.Ó¨j6FÂøP?Gi'ð.±jÎÛwaS`Sà@1³çr– BÚ,&G(ßËáÁ5—Ìtbµ‰«‘0#Ï4î Ó&¸iA˜šÓD2s«BaÃk=-R¶Ç‘›Í§5dM®¨FnÇQôøÙ%qíy—7®H"ç›xÛš„l~˜`žÞòÓœÎ'£ïªìH°j XNËôž½È'8Zt¢r05ÈZƒ }$¡f²ëm=“ibøAeÍÀf @èd¨¥´žìÂ}¥ÀÞ^'yKøkNs§&„>ðTî9§©ñ‡yÚ4ºÅÛ$«Véð¨ÚL.5œÌxRåºt(ö|á±58£ŸÆï~÷»§¥CÒz<HÑ ›½hoY ý_ú¥_:ï¦ÀS ÀEHÌÃXÕìߤX –†#š¨ÄLÉÇø«Œëfcp˜˜6kŒßŒ-+ßš`f2ÊL› “x-•9áðY­AÛ˲Ë4¤êÇH˜:fœsòa“2»fûsS`SàŠ`p‹Å|JƒÅH@ B¾jìÕO ¹d¦;Æ€)˃ǹ‰jßÞrÚµ*'œxÉLêµi9]¥ñYuLÅh5Ÿ“`2ÊL0!@<Z-†ó®]ظ:À³™Á¶Š@hç®y¨Ã2Ï»Ìs­W‡=.î¼t’®Ö €s^¤0‚{^§r€+u*.k_Š…±X«#‡ö€qõE!‚ÍÀóD$Í3¤]¸ßØÂë¤0‘c´YNz}øÃ.›h± sXY\Ä(1A}æöˆ?Q “IÈÁàËÑúh³úpë*O9~ÞÊI£5MpF?캆ÂßSêñªnßñŽwÌYÀÜšÖÝlý×I¬Ý×í£ÀYäT\l`“2&bL©±ÔÉLÇée´ìH˜cŒÓ·OÉyH0“»Ë@ÀC{Q=^­sInÜjG÷«¿ú«5“¬b²ËhæoÏHØR$ÆêõŠaÓi¹ ››w¤@ …ŰöaŸW¦*ò ìÆPÊþ¹dÖž“¼ƒ3X%ÖE£–©‡, ú<›Hfí°r® c3Éï´ÁX¦ y°k“#ñª ['g—ÁÓwíšM+R`< ¶áÌ: ÎÄS0½É4NçêÛÑ‹S)MìÂAÍ‘Å:\èT"obeõÐÔÄ­g 7œé 0v«X}l 6OÏP=CÚ…ûMß„5÷ûM·¤ÿRú&ðF"I’æëËœ‘l(Ù=&Á-YÃÁY—ýáÍr eß‹mÀÓIþIƬØüRtÞ™0Ÿþ韮Øt¨á‚ˆš…ÙøÒ¤±ì;^ã±ñ6}®+ÅÚÕ.o ÜNVj@IDATÁP›>2Þùß„F…¯ÑƒÄ·ÌŠÆŸøîLE §NÀÃ+öå¦ÀÝR iÙT ¶ÍNŒœä…p&ÈW3 p}KO¦Ã­TœAJðRMa=îxÀòê»4±ãD³=—4ó_Ù_}¦±wg`Á`X×t^&a€9+H$X¹Ë÷›{CxÍÎáSœC¶õ †´5t0ãK22•Œp^èÓ “ˆ-±D§â´T’8êÏxFÜE4¾éMoNS亨b1,-øp,Oü ¿ð óUåNìq»ÁÉ…ƒçSU?yÒJIÒæñ]ظ ¬è'ò'Í1ºåIŠLf#·L?bãó>ïóÚq5EçØ‰½ô¥/u9 ÓÎŽjÌêsÈ(³>‚0—ðœÑ-Ñõ®rÙeÀz+›F˜õ]ïzWzŸ\wîxˆâÚÏ.o l ´³Â†™øˆÆqdåØÛ«ñ¹æ’YI×)Û8I³Þšrû@Ë‚wáÙ«8§¬l&wh›x´õÙø…b~éõ¯zÕ«,nO.r¿ÝrWŠÔ}ÂÄü »pïÏš~‰Kåp ãs‚<°7 pÞ›ÞSàb•湩 Lv x döŠ>AÐüÑ\4VSíë’‰À­nݲ×}šÆàq&Í3ð B뜞Aî ÀÞ^3‘¹–åÜB“}lñÊÍr`7ýe›@v¾mÅ6rk‘:؉j3œ[.¡áòóv+³>îÅÆ…Z•QˆÅ¨-Wéœ$#°>ÅŒ~þößþÛ_‡|Sü¨äÕ}Í”ÚÝÝb Ìž0ÿ–tçÑCHaRû¬*I“P¾™õLg;®Ž_¬‹(Z`œxÈ(sx¤¼E }Ú§}Úáîd—1˜õÖ Õ‹ÙÈ D”5gm¹Ë››—Pàƒü &ò‡‰j6pvM–VÒ KžímBû¬å0ëÄ'ð¹fKhªsp3F·€[7WÒ@/<ŒgáŒÉý\Øiƒ¿²ìgï–¿ÖÜíûýY <ç9ÏÁ3läÐXÕËöKí‘{ô‹^ô"mx¾•G›:GœzüÆq%ÕK ¿ø‹¿x˜\ƒIU:V¾žòÙi-mðœôblñy.s¯yÍk-%kÎlÑIšåÏÿüÏ× .žJeq\ ;~ÔÛŸ×EÙš]ð“Ë陪>E†“Äøƒel×ÌDºuhR5UQóøÙB f"“ð$Ì𠯹÷)Ã~Êñ[ÁJJ¬‹ÛÓŸþôïÝàJÒ]¾ ˜uÍ®lA8pNÿ ]w½Á@àØÛ;0 ˜å `Òã§y¿§“œnêœPV•®@lªùЇ>”ÅRª˜2<€áðç†0ƒÍ9þÒ×BœÝÉÕ)°7„W§ÕU[–$ÃD÷ PÃø ”’G‘*…¸b”%DiHI_Mö¾˜ 5¤£ÃC¨OV ¬ªöÆ•“Ë=+Ò˜ö~â'~B㢟ûÜçƒ?5ÆI¤)ø›Xù¬j®úåw»M+SNš,|]lÀä%KJ L•ÈHé.%Zf·'§çj‘2»ä¶zDŸ}ÿOÿôOט§Ê³-%¢HJUyÚ`¬èk._Í ˆd¤þãßÌõNÓ>íg×l l œR éÈÆ¤t=iÖh¢Éx‘ÁŠ2vÌÃé+ªÑÿ8­Y.jV½åevt-#óI’v8–B¤òýáÿÆÊánYÖÆ‘Ïr·j¾.é¾»)pu 4!° „ê‚ !=OËà(Xÿ™ÓÓå“_í ãÚ+àÉÞ‚USÖT¦Ž¥øb ­t[m]p×0Ôt¦‰{ܧqÆÑíé«ýÝòZ(°þµñ·tB†e(5’ØÏŒ§bdýPàK “B”4šãËÜ"<äçÍO&{ݘÝõ7FÂOù”O©¦OûI ÓÆÖoLé]ò‚ûÓúO7ÊvŒêOý)Oå€W8„Ý™Nêç\šÄ*þ-ßp_l \@·CÂR„RŽhø¼³žŽ;Ÿ+S› [¹´…Ã2xí¢q•Q†(ÒøŽq}9{ëÓæð´C=øÃã‡0D9ª·-´9¨ºe18íd×l l ( Öî Öúm¿Í"Э±EŒ¿(¦—™Ããs™N?Û¦òP°hЂ`q8ÜKÕÂ2o|bpËŸþ×dúIR\ÿþ÷¿ßÈÄÓÜBg¹›žwaSà) r,„်”§MÎ _ðlmŠ@£Ê‰<)SÎ4väoå¢]Sƒ¬X_ŒDÐÍþúL w~¯¤yâ»<¨8î*Ž?×HÀÝ ì á}™¹PwмŒA\ò%j*OlÓéðr.i ÓÊuŽbSY^¬’ 1®‹ß&SSžuÝòÉÛ j£·R†ÒåHãëGí¾jl\2îŒ0–RsÜádH«Ï’’š=»?7®Tæ%Z0'®À”³¿’O"ñCõXºNJ÷”—3çׂmsv„_ñ_¡¥> N ò/ÿå¿<Û¬Êñ/ÅPœH-çO{ÚÓÖ[o|ã»Å}ta=Œ?ÀªzéÖ|:‘¦|Á¥¿ËÑ–žQ|°ñÄ·åò®9tüëý¯×ÏÄö mÍÙ¯¹+7®…³'¿$•q‚snÌf#'«¤ˆIøö·¿½×á¯U2“¿YrêAšÑOH}6¬´ž.}ø…f·ÿš¯ùšÃÝÞ’hœ0!mäíÃæ[ßúÖÊÙ?¿ó;¿óÐɾÜØ8Pà÷þÞß×Ä/íRe!·ePéáq—¬$¬N:Œ[ÒÂÓ6?Ij)(ÇÆ©s¸$Ýxæ3;LCñ„u.ÇcNdË—EÌRÖ~rïO…]s½hNÎ Ô¥-uvÜü,4½=(–ú(èF®Ç T: ´z5-IR»JàÄ-?Ð úªƒcÕÙUî›ãÛ0s½TÚ½]N½!¼œ>Oñî¯ýÚ¯¥báùÖ.«`ôâ â%#Æ#xKÌ oÈUèÝ¢çó=fS(쪀Ãê×OŒöÍèoÐOa¾øÀD÷jŒ'U–á×%>l—˜_MF˜¼½]ò‚{Š„Øm \³' ëYÏZ{ØåMMXÑGüñ eLaЧH"nØ# ‡¹=à÷OþäOÖ^: ¨4yp<Ãò7J$cAÐÞâ0]å#:{Nwv­¶M…Ñh¯´´dY¸,_‡múß…MûA iÕl?š@€7–‰´$M`€,4o53ª6AG0r¶—ˆ³¢«Ñb¤!øÚ#8:!â¦aÁ ¯» Ê qðTæSíà„¾TÚ}^N½!¼œ>Oýîç|Îç˜ôÒ%a@ÖaD.%ÞtØ&ýÿø«¤bɇ„|Pc@Ÿ4ÜZ‚•éT&¢ÊÓ¿80‚âcZžl:hl¾X‘»:¤*}ë0·þè)DÛ@>uZì'7®@SefÒ®C““ ¢gÉA+‡"d¥A›á“()öi‚ççÆÛ&Û&I¯Ê˜KÁ‘Ê@ád—Iæ•K¦=§éY)bçí»°)°)p @û7ü‚OY KšäE†ÅÚ}a:N'òy”„àûÞ÷¾*ËŸÁŸsÚ”Hãcÿ©,‰·%âÔG”}þóŸß»|Zj,8^$òp•¶$éz´ÌE‹Ø¼q6® ³4Ø„›3ž7&ËLWe Pc€°S ¥{iHë×Íü¬•ûÄEŽ®©Ý€U ¾Õ kÛˆºwçPDÍ€a²›|d ÌÀsƒÙŸ˜{Cx¿Ž FfÈ9æ5¥-$©ÍfyCÑ¢ y³f%¥bìøÐ'Î+€CöÊŠÓ`-`×¾Þw|Çw¨ìžÖ§£Ø’—4=ÚŒƒAî4í6' ÿ¢Ðü:ߟ›×HKàTÛ¿U>Éã'xé»XªMB¥-âðBõDÑ:NíºÒò`†?˜y¿Ô˜¿¨Ç5Ó¾šº­œ†e²Ëõò—¿\›¶¯½ÂåéÎsÌ.o Üf €†±•Oó_ñŠW(8ÂA0¡³SÀnQIÙßPŒ'ÑöÕ_ýÕSY@Ô€ËI$3Œ\ËêÁóœvéÍo~óä8…h-2–šI:+Ì¤Ž«·K–¯Ø.l \;€´˜"ØÖepN=€ç™õ?åáÄ5ÙAĆ4Ùhêíôì>[b1ñ7m€Õt¯Õ€²D¶nÇŠîÆ8mJa-cm€äžÒ3ðÜ`öç¦Ào.©øÅ·áueûóÚ;Þñ3žíÞw—s©Ù?rePéGÕÀ'á䤵¹ÌÛ->»ßÔ+ä®65ßþíßó;M‡D?š÷Z»¾s§:õ` »óÇ+°P¥L·á÷Úßñ!¡Àå Š UË`8’‰´ô-1Au8¬Å4¶Q<ä =›`&»]æP#ï5~k¿üË¿¬2þê.Ë@–ɲËtæ'Ä9À1Op~›waS`S`¥Àóž÷<<’®‡Ø*/¢H¤òOäÀ‚ÑÆ·ò ~¦6ëÁ3ùËMƒ5‘Ìú^ ˆ¹úôIÚŠ`|ÙË^Vè,H©¤Æ:“èl´"§ÃË®i¶ ›×NGn6]›ÏA¸9%u$°§Ùëí@`>2ÁÂ9“÷ÕOŸ3í§ìÌ–­Ë$§™BaºÞÜVË(—t´H%À8j4°ùÚ‰³;¼"ö†ðŠ„z*Í2ÁQÒôðoüÆodÓ³=Ãc‹UÆ|ñÍßüÍjÆÈ® ›Á5ëêµ÷7q•§Ü¨’gK¯æù†ƒÑX·ÊŒ*Y“»)9Uãœ3ßVpüT±ŸÙ¸K \­œ´k¢¶ñ#öìÁšØ*‹ýSðGÊò2y «ôI˜1GÌpÒ¿`ÒI0ó†7¼A³á-©9Ó­–§»®¦‡v•^$éEÀTvAö„šÅevàì<» ››ø+‰úüò/ÿr¼“£óµóš‘Kfhµò vsIþÌÏüÌ4PÈPÿ¢½èH¦6¨êªOVý<œ$ˆ-2¶…̆üãë×ï€K–¬uT»¼)p?(ÐÑ‚¦q*ÈäÎLcÏK“bn5€Ì &6p8AòßÊ•Wx vŸI´i  B‰s|e{Ѹ‰Öô€³R`|À¸f 뿚ýù€)°7„÷‘à¯~õ«c€ ÊA”ˆòÖt¢å†ÒŒÌk(NuYÜQ6Ÿì¹Ä$ãÆdÕxÔ`t0:QÎ;TÏŸÆT¨½þd¿p·4§.ÛŽÖs $•§Y¹êþܸ¸`}Ó7}“9ùŸñ%œ e‘[S’ºÛŸ$Ã6üxõÚ·¤¦p÷4ÁÌ×~í×jù¥_ú¥ëWsB}"¶töÌÝ5»Li-X%E#a¦K»;î¦À¦ |Ï÷|Ï𦨂 Šg3áO1l«½²|Qm&1f·æ³lOúÓÓœN"ýpI=xÓèÐNO¶* Â[z Ájy±t7›D+7ýA,²˜¾h±šÁì¦À}¥@¹åÍØ`[.8§Àóv`¯)]¦¥2ª‘U»±‹+€ gNpoP³ž»Å ê€U_AÙº­™úà.è« Vצd§jÈëjö烧ÀošÿîÇþ¿ò+¿«ýž÷¼ÇŒ‡}w‡ª)û›œKÏ}îs£IGMÄ™éf@Øô¦øpÜ;= ƒªOvóÄÑóñj—}`yÑäu£2c=Mg ôì¥ï~÷»»\³Úįž÷Ýcÿcí/øRà,Ì*O XйËÌO[µïÿþïggàšbž¯"M¨ú·|Ë·h32©yîSªë™þJ$CìæZ ßV Å‹_üâU†JjIñ½y<ÏÉ\š×Ü›Þô¦B¶|Z²Ö1ìò¦Àƒ¡À„H l3ÃÎx†‘áN=øçr—M—€búŽX£O\Ù±õ.AÍ”°Ý"ìÚ‚¦õXÍag‚ðÁÚÚÐí<Ḡ$öjð¸aÌ`³šýw#ø-°æFFðx¿´TNø3=(Ñ’§(6 ‰L9L"Œ«™£x¤£)‚°â"žýìg»…Ùâ4Qì´žñ]¦ö“je×øÄ·ÚÄ–x’íÂeî.1!MgçÆèß90óàˆÆ³âV'ûoSà~Sà°'¤ŒÇJäÇQ¯æ¥™‹ }ÿßøÆC—ƒÖm¡)e4˜íÜLr²P{ØQ{ÝÂ9’y§_Ëè¿ôK¿tJÃ9xMÿœùŠwyÖõ^'°•21t[ÏîþìÏþìô :'¸í×-_aü±ùÞ ¹váÁS ¬KfïÀ6ep.Ðàyj‚|ù¿PmÒ2Ù­–†¸ÀçœMmzûRÒIt‹<D•õß>LVÕ\ËË ÐÖÄt•1è›iOÄ€ñÄzl'š?sÖ7î áJë/ÚñŒO Ñ` å¸"˜(áï8pjÆcÍqFóu&ÍM¨7Þfß—•»2Æ.ÿ ßð Ú÷wj Áü[ÚUйêKæMžæÌú¯~¼§…i¹ à^?uv›W Àº'´©3!í»æ9Ñi7ðÂjÖ“©l‚`›ÒT’d^Gª *`%=ä) €¦õ—Hm^1IeF]:•ˆÛä¢?õS?µÊ/þâ/vIÚÍNò»¾ë»îËMÛLNÄ&ÙVöá‡æÒ_^-+•ª‡}qý÷}ß÷­·¦ÌìP3éaÖx{ÈŸg­äiÍæS,Ój¦°´/•“cÎ˦Š2¼ÑäîÝà}n„#bÌê™ÉcuðÚƒ5óÁ¿YîàD Rγ +˜2AÍ„¦– hŒˆPÁÔlïõÄ&RëÄt•µ7ª ž‰ë¡§æ¡Þ~iØÂû>D&`ƒñðæ”â’LêŒxåìo†3‹ÁñŠs`¬ ËYEZѵ‘;qT¡z;ûÇ„˜Gƾ°ÞRUl±#уýèGW S•¸ý¾“i¿`Sàb Ìž0e<Ñrh+áD¼ÃáÓ±»æù$•â‡!½I‘‚¶mŸüÉŸlÂwëÃþðá]懦™ã( &I½liÝwîü½=EKzxj_n ÜZ °úŽycm<’w¨ׄNä´¹RI³þðûZ?åI$£Ùð¸W0†$ûðûÔòT‡D¤ã¹§Ÿ·¼å--Nî|h£H'ʼn`NxóMÓj6œgwaSàQ ›As¸O‚ œ« à¥ôòaÂÑØÌv €Ãñ2[{XË@fƒ¡ hª70U9fÑ&ËGB¹€Ç† úÀ†‚Ç ò#×~ÑY ì áY²\ge <1ƒ`ÙØ£ÀÂQLÒ>’4(®))•ˆê¸ÂÔ9ä7ò®a¸ÃKþ„:¤Âìòìg/%}·ò;@ é즎lò ¶O^®ÛB‰¯“"»¯M»§Àì ÍRRzþà~)." ¸†Yzƒ}ݰƒä.DΠÀD–¤Ý1µœ»ŽÊ½hŒu(y÷df«%.nÿ‰wæÙ€£¸üt±sèË4Ø…M[KI'“.gI5Ær8^-+•.âÁÚP MHU-ÉSÇÊO }•˜Ý‚@ævéS~Ç΋8°¡m84ΚQû²xï‡b»pSxÆ3ž1Ó8ðÈ©Àó4ùBž@ ¡„*‡óøiÞ)/÷$)J“¶9Yƒ¯ø+æn ÇÁ¨EöR’0n¯X*Ô›"à~/ ì á}ŸY p¶ÌËI§VÔß÷~ï÷2Áçr&ÿáHGvŠ©À3Å3@À«.ÓÑ1r¯ÕÕd2mØÖS•Ôˆ ¼hDó”ƒÎmÊ.£IÜ­q õÍä><»/7nÆh0§ï=ø_ÿëM/‰•Š“_)3ÛÈSWRÍd±¦‹U¥ž’V´èÄj°¿EÀRPJÆÉ§½ž2_€¢öEö ¨m[ȵä7ÛStý]vù)P„^³4yÈÓ5€WÂ^ðÓLRP°ö§Ÿ£!íÖÀK€ìœöàhjYÕ#Xl2¨± ¬–†Ü‚¸Êàn9„ë$ŒS›‚Ê7HÀýjøÿÍO» ÷‰t$üÿq±œªÙ.³u.s•v ?ó™Ï4\ñ_ð Æ‘ØØïT‚`üf‹Ø-ÚáL™ãí죉©e:Nå Žèý‘ù—múùÓüµˆBæŸãnêÏz˜ j—ûsSàF( Dá‡~è‡:¾–(b ’÷œç<Ç4–.⫾ê«0ˆ€|ñÎw¾“ÂR¾ŠýÑ5T •»‹ÀBD7‰ËäaFˆ)$¯×p¬™G8À(K^zÑ7 i4GâÖ XL¶ÍP¥™qW·XìpòïEýïúMÇ›ùÈGú×w(èeXß‘Ùò.RÈ´U3/º+ЉzH>˜ñëN=ôþ÷¿_†åB3Šñ±¿E@b . œå|Æ›Ä4xJì²ÿw¢GÈDýزrõv ;óÐnÐj³æ,mûsSàÁS`iyÊrå4·¼XŒ¬ìT•þáþЇ>dœÍüµ  s#ƒ”àeÍNâµJͲX(t˜Æe ™‹ËÜkےꃻ@¯J`°Æ.c|$G»»?o†{[ü( Àɯkê÷.hµ›Þ%@ɈïVà¿ý‘?òGf6¤ìÝÛñ+š¤n!É&JÊ#¹ °æÏÑ1ÓÏè“£ï}ï{I5õI܈1|¹ãM{ê/xÁ Ò~ŦÀU(0¾£f¾°=2fÕ€°rÉ?“€˜1‡ÂÄl‚”Ô`FˆÓëD±ó%M~d¤KÇ—Ñn®«MºÜ*ÓÌz×¹º5ÀÚê¿ó;¿ÓeÖ‰ìð kû]Þ¸§P@08«)’‰ 5˜“Ê¢ÒäqÁVC7[Á׿þõq}܃:‘bed—p­G0~¨Ô]&ÊÚ”tÔ˺R'¬’%Ú^rxë]³ølÛà iMZŸÙ´¹„TÐ.˜ò~Ú à5Xó‘º}vRö!¡âë^÷ºÕ¹´wõ @“¾þÈÅ”¦é{âó1NŽ©ÄSôµ÷•>»óM»¢À–k2 ~Ñ}Ñ6¬^™Pÿï{ßûÚ˜¥@ýÄOüÄ9OŒ2• Ùj_¡pgéjêÜ#Y0¤w[?>3Ä'kç7ísÃN@ºT¹>²Ë›·Øg ™,â ,“Š“®‡¦Fe»ÄèÓ1Nò¤’H•ßbºÒÞßy©{:X{,ñµ¡EÒs-9¤}òé'>HÏÓ“îËÎmûÉö÷}˜)PFë&ð€·ÔA;0îäüB€«nef~“€\k‚—8bÔÝM€‚¦.‚®¹É¨h-/S@wð­[`°[ø7 $?Ìä½%cÛÂôCç9Æ8î}ø—n2•äË_þr·¾þë¿>Ncg/ÍXç;«¨­Ïð+ÅgJ—ÀnwF8ÆL˵`-piè›ÏÚQÚâ ²wˆ§tù‹²8®'¤ UÉ`([è’§YþœÄäø³a¿à¦ ¼Äœªf6„±®Í«»‡?‘W}ó¬îvJÌŒgÜAø‘i¿fSàʸ¢9ìh˜e&?a–}ìŒ]2luV)ö/™w°3L'˜8ŒôÛ¾íÛ\ÚÚ6ðŸ(m/Îe Ì‘†SËxÙvôÊßr7Üx )0éd_b“É%ÓÉŸñŸel}–„öó?ÿó±^PQ;L§È$RéSþåKÍcøóì¯ô¨Q`N62«¼çâõÀ^_ ü;Ë;*Ù÷²%ÌÎ-`È p޶…„/˜Bv×%ôÖ¹‚s¦Eã}s–y×»Þk¶&szÔÿXw'•¹ˆ/®¹ÞÉzäØöÚ×¾V)_D»KBye¨$é; ÃUOJ÷ɪà<¥×¼æ5ˆ®·¾õ­îúËIF¶nÞÛRrù²¿›¡8yFš'ÛžùˆuÝ(E‡B»GAóµv°Ì<6VʩمM§Ë›¤‚y~ê§~Š*t¦n³õ ù¥×TIL‚•¹¯pþ”åųÀ%ݪÄk²¥i#Ñ…ÏW¾ò•:Ô¸®Î~J;ÁÞÈA”þÕ›>µLÔQ¾ºÅ9‡'OùKG«ÑæþÏÿyðý>ûŠ]¹)ðXR—qÀn{×øšØ¤\2ÇN¤P>c(âL.4–=·_žG^(ÒPŽ ÛK…l#Ö¶yez¯Ôl#Þ(±ôQœÄê …Ò¡ä1æTÿ«Nv™S‚„«¹oÀ[2q öþþ+¿ò+;•ŒlûX’¤¸ Ôìx˜ñÐG;ÿ@ur=° ²sàk=´`-pëÐÍqôÍ—•8HìVðøt0»æASà±ÚÞ>Ü_& ¾µ¿Ý.<)“Ž™ø©dæ¤AY}›9Ô'ã$c~䆵<ˈ!ä}eo¼:¹×. ¦CeBñŠÎÕUg˜Î²2qÞhx¸I»Gw{)p‰ò¾„L2ÊpélbÛø xø¬Ïú¬4,ëj :"ƒ¹}&ìÈÑeŒ kª§yPP„DÞidå©÷3äð©Ÿú©ÿý¿ÿ÷B“¯Ìæ†zoïo¶¿ùí¦@~¡¡C¬TFŠØË`œÈS=—[׃h‡0fFŒÚ3_°ù IúÚ¯ýډï=¨ ïþ¾ß÷û\²7Ú7zŸ’¹ê-ësÉò²6ÛåM›¥@ìÓØ.£CŠû^xéK_§Ñ”ô²Ž&cŽ`ìVŸ&‰ƒg‚P“ÇËàQZÒÎrÉëæK¾äKXíW÷6‡q'õ3KÃÚå†Qrmª CZQr2²–Ž4½ïÔÙ/Øxª¸´Å&e”áx–»&Ó‰(BÉžÍô HS=>Ø`Ch8T›£^Ñ›Ë û5žÏ"uÅñ,åhyƒs-ÕÇ)nAÒžÂ_9>Õ/½ŸÛx$)`Ú4ÓŒ`‡š± –é[±ã¹…™â¯a·)`FÂ.?´p'þ’û~ŽÕ–Àë»ßýn}f{ÔÀ¢áEN’ˆåYø¥æ×ŸY/ZX¦Á.l <$èÜù&ü Þ€:#ÌÖì)ç’6¬±˜Ÿ$Κ[xÈ5Õ;± ZÐT·qÈ ¸ª¯ŸéÄt]½Ql†W³?oœ{Cøà~6÷Øã oxCoÍÓš¾#bf?}îsŸ+£=©É:ÁŸ¡c¸+ùe_öej°.›þøÅ©áã]‰Ûui˜Ç§ÀY¼8+òÒVýëØF4.á̓£Ô~Ó¦À]RàºÙšº+¼sòRJM!ìœ6qMy ’zuÕwzPæn ˆAð`á*!×¼_”Î*Ï÷6ö —è}ÛÎ2HOÿs?÷s k︳»ü–»ù¦À#OÒÉŒyðÓ>íÓâŸå’é=W_•gXÖæÏü™?S¥Ï mR&y„¶KtISc#ÊT™=_çÞÂc­“FÛë4H…tº¤<ò¤ß_àñ¥ÿS·¿¼u¾tÒ Ø+>ðã ÿÓ™â/²“k^—™‚«Ž4P½ÔƒÄ%¹é©Q¤kAÜÜMÞ~0¸Î½ëñýY±o¶7„ôKOCÂ9*׋qQ6E9”â1Y`?€²HzžÖDZü3F?ie¤U\¥f)›ÊÙÝ#e8¬¼~©tOc<9ؤ1ahÏ÷æ’i¿lSàî)pp)P`âÝ;É'åœúâ‘$UÃŒi@Møöoq yƨÈìväèR½Ìå…êr>¡Òä±ð$_¢£í{êmo{Ûj$$eïþ[î'6m day—§Y R.™¾^Öõa«)`:¬×å€Î¹Ë…[H0q™÷ ž-¶ô_ø…_¨Î?øA¯(VBƒ±Iª'óÃbòhS|þPH.¼ä€:ß>{‹ú*Ú¦½Â AG”¿6à%© pNÎÒЬJдP@`dmߨ~0$f/fÜ%gu‡cóM»¿Ï£ñ÷†ðþN/|á c01«wwîŸì/QòI^ûÙ݉wj_GÈÇ©ÜÔ«pg±…13Ï™v^!RgŽ––h¶‘ `>[)¼Wû’įfÚ¬i(½öË6î†+Œ‹¡’mklƒ‰:Ü!Mƒ^®/-)kƒ]Þx8)¤ GÞr¶p ]5…‡ãF,j@ÖÎÊ%; pƹšM&дC/°Ogzˆe{/†*Á!¸‹z ï¤Ä'dNzÞÎQí áýÝ%6Ä*£¤ÁÐ_\̃Ø$ŒádêöfNU hÆ0n}èC2¦q•üp´)”.ñ/þEmhCÛ¼)OŽl²Ðƒq zýK´­ÐŸNQR¿ó±ÿ¤cç>ć}£l¥”Lûe›÷@ÙšÙ2Ljd(è@¤é••ëiÀX!©4†Ãe2‘Êc£‘cƒÄ¼ºtåÕô l?¹ÞªÌÆX– »Gcøæoþænɽ¶SË̲ 7&LLdw‡²ÀÛ× ¾Ä„ÚDÔÓž¤eFê’b¥VM#ƒy±p·:èLã×½îuœþèý£Ê¶R ϳK„…¢DSìÝàg œ¦”Ï_ ¨ËŸÓ¬öw€vjÀ•§¸RO`Òw,Ÿ|ô7¡&𙕣;:†}ðÕƒ lnÝd®þ`·€á’·=¾ ƒÜÂý+g äN&¸6 Ú™Ø#Ÿo¹˜Sg[«ÑH¶ÅÛðå×ý×»µ¦rú±û1fúˆ¥‡±µô×ëFŒnù$/˱ÁM.%ëÜÚÂ=Köûîö„§™y¹0´?,µý‹_üâ&¹ˆ|Ñöñ‹£Ì¼œ ’“­æAç6"ÖÅÁ¶V"YIÊQ»hðþÂ_æí©¿ô—þ’ž GqÓ¹Û÷öu÷Ó›J'Ã÷,¶"qòçAƒ¡Dóž ͵‡d$,IïÈ5ÌØ×Æž%Œq ÛÊ|Ï{Þ“TåDÊ05!…í-–…@…ÉAúÐtqSàI ,„ \yé:Œv˜áëåhB«Œ¹ú&õ XNû'ð™xGGá2’pí7)­h ¨‡€®×¾å2†÷¯÷PQ`oôÏÁ78´xñþèŽó˜Êò­Mª §¾¨à¨L†’EÉÇÿdÛôkêsbñ'&8Ÿï5+”®Nÿ(G ½ ¬MOÍö†ðAÏ’ý¾{¦€=aÑ}¬„âÁáÓÄnøçþÜŸKø=ÿùÏ·Tïr2¡á¸D Ö›Ô †wxÚŒI!W™¹UуÕ¶s5q(sœ+™“fþrÏ_ww°)ð°S t2 &{¿U$1ÔS¦HzP²ØÂÉ–á(—×¾öµ!×5¾/|*È^8æ‹Oú¤OjFuº¨±Dä­jѰtL'»°)ðHPàU¯zÕ*}@¸œ±ÍüRkƒµ'މ~ cù–˃šE>Qp”ˆœ®«€keÝêszzÝ-(~$h{{¹7„ú·ÆB ¤ƒ¥ÇÆ-ÏzÖ³øÏP¨äî¢eŒ:§|Še2Ü¿÷÷þžöT›ú!Úd‰À÷¾÷½]º›7N"ÖåŠYÇŽQã>«¤‹õ·Ö[k4™öû6î™ã;šàè”$.1pç)(4áST`[èý"‘ÖB•±¶ÂqâæíîRŽ–v•¤ïÔRÖµù ®lUY';µÌ=ÿÔ»ƒ‡&9y׸€jf•G8Bƒ„—H¤_üÅ_WVíá/ßù4h0BðÍÏålP"ö·X ,–…ÃBñ°“roSà„‡ á ¸•³†ïÖJ°0UKçsõ<» bR|‡a¡Õ³›@ýpTk¸Rç’%bm¶Ë›3Š!†áÖ]ßÔ+~c9w9.ÊÉ)²ŒhË ß5ÁÎ'Òÿö߈N†ÏSA֍δ ‘7–Ãrù‚Á3=oçØö†ð~÷øÀ*ÎPêÝÏyÎs\®“€[mõ8s½Ë­Döá/Nnµù„Oø„2>uù¼ç=oÎQ#OT¼­,5bmŸ^·öàîäÕ¸bíWn Ü®ø„C´gc'\edÜA¸¿4ÌbP…-±þê¯þªôòÖCú”|¹q“yG(ö¸þ'9p5š}Îç|Ž•aŸîíKï§7nž&³)mbƒÃ8g ˜Nòú|ä#ñEÉHG¥"ªP¦Ä õ˜Î[;Á˜g±/ÓUËtÒ¹ ¢\qq8<µ/76 i+S8_÷€çî?Á pä. Ø™òµsu ^¡ìA'¿ö½®£_'ŒÚ­>݈Öñ¡V†‡†{<{CxsvlWæÜ³ßøßH[S¼{À‘ g’X³aÑ$k'ÀL †Ôö¾Æx~çšÀç}Þç±Ë€P`¾4ñç$³Y™¶òÁet=º÷èµ_¹)po¸"ì‹e°1ÙõìB |žò¹(‹o"S`¹w¿ûÝ425¶ëØÊX[T)|Oû9Ôüžßó{Äâoƒá½ýàû馀 l›Ì‡é½^ÎVù½S=;-Ó5OìZÚ(êGzR,¦»aºÑ„®½aR^ _öe_Üt®Úß$¬-å(W\OíËM‡“<"¦8@¸•Sø‚“z DÁE-“ƒaXú¾É2wƒAPp4ãAX2àkhÚÀ-6w;ì.áöCHÉÛ<¤½!¼™_ÿÙÏ~6Ö"±x°(`¹øùOü‰?‘˜ÄÏb' îéOº ÷X ÓòŠiw7{ÏR`UKÀ5„‰ûb=à6Ù€rzÞÎáí áÍüîŽÃÆ]âŽ$¦W ö~îç~N&Ò”%q›G¹üý¿ÿ÷ûdgwTèÁdñæ7¿Ù×~­OJÑÆyàxÜÖW¸<¸®=Ùä‰ÃÙ*ô¹7„73Kö[¯•—CÀMâƒòµ+‹ÑX'Öð÷•5´/Øi­”$còÓàhè–È”ýBB|Íptx×ç¼ö0e6 i¶ÁðZ'Âîìú)`Šš¨cŸ |(¤¦Tiò'Ú0…ÑP¸ÄGc¾8<èRƒñ[ëîákü­¿õ·Ôã¸â‰?Tºõ™¯æò¥àÐí¾Üxø)pØ \ü²ª ÀvqÝKKCêUŒÌ¢àë™Õ÷I2¢à¨Ë ©ŽYKƒïÒ+ÚR·%&w ¶€~’ÞÂî áÍüèÿçÿüŸ´,øÖùdyÂPy⥬ö؉Mˆþá?ü‡ÕóÛÖx]ç _øÂ¾‘¬¨ßѳKÎ3oyË[æoÕÌ5›»§N;7C¦ýÖMk¥À%@0ÙÖ¡/L óoþÍ!>ýãoÌ@ªÿʯüÊ4©v!Q”Éâ ¬nÂË‚ ]B«?üÃ?Üž°»õó‡þÐr´ýì$çAº †Cÿ]xx(IP—$Î:i•Uò審Ü$¯ÉÚ¶qÅJk–‹é„…C¥Ö”&5~´¯ÍJ‡W¼â*qJJ»ÇóoþM ÈG·F­yÉ"°v¸Ë›€´aœ³¬‘ЉÇo‚ˆ€¢Ûâ\0Ò%Hb¢z>APõ…M=â¯`ð5( Öj‰é ÀŸˆTø¢ííêÞÞØo=f:Ò´A”²ÉahEÎù-Ebü®ßõ»ðiWc |G—ÃE»JjZâ[ŽîGÙSå5µ¬Ñ Zw'tJùìß, 7F¬ýâMk¢ÀEpP>C“ÿs?÷sç=¦}²316ùÓâÍœe«Š£(%w fþóþÏM5“]&¹²•>`túv=wWùÿþßÿ aÉùוIâ¿ûwÿî6Îo´ 7EŽaÎáAÓD_”Mf9$D ÿ‹ÁLf“ Lã©W ¬­TÄàK^ò’É`®B•°¦ó•{d¾ûÞð†zûÝ¿ûw»%Ìéßÿû?wÓ“v öEì?waSàQ¤@“|e¥C9ͦÊ~Êa»DA .ƒ‹ øZYË‘5Åöx§G RÊ— é \±jçú‚µÑ³Ã*ôú>о cÞÂû•'n>mãH‰b›÷—ÿò_Æ6,ø;Q€¯[¸ô—ù—5&hW_µ hïcÏùò“Mø_ü‹¡çSI< »§8sc”Ú/Þ¸V œ…iXCó¶7¾ñ‰ÏXæu¯{[1HmD †YIÐüåJ0à¡Yš”w½ë]áÔSÎR³2#{ˆ=a= ð A×»ó¸JáûxÙîq†º ›€âi_þò—økNâé^"¶‚Nã$f0&óÄÓÎì] frì£R'ÿèý£¾BÚRéyÕc«¶Zb·ÔIeîgéFó|1¶ŸýÙŸíVŸ£ë9ËøkË]ÞxD)ÐñÔ+s­åS€—d j91ÐXû9¼œÞÀNà•>úÑjëM5×S Ö-«¦‹¤%¹Ðèûˆù±öÞÞØOÜéœØ#ÙÙ8ÒŒ&)ZÚIµLàµ|ÿû߯fÌÞñŽwèäƒü úþ ÆI'SÍw÷wsKûøýÅ Nÿê1ðÜoœ#Ó~ñ¦À} À)4Ì%æÔ;úû¿ÿûGóBþÍù.3(Ùh:¥ˆÍØ. FüNkF‚®œ%s§é$ßá² ^Áw”5R¼>E,Ïžr‰gý–·Æ|êOF4CÚ…Më¥ð÷ ßð Rï†#›~f;¿2j‹¤†O©\Þ÷¾÷™º&°iœñá0ÃmóLþ¼¬'æPtÓÏüÌÏ̘³-xÐ+z#ÃhÓ >]Ê”˜>Hªó’±MK…¼Ái[:Æç€ÔµÙ.o <ºÈeúÀkíÖª æ­×G©ÒÚW L"`98³N€OõmðæÌO·Úf! Ä´CR@·nAߩ܅‡Š{Cxc?G&pÈzì{<)b­XW*mÁ»£¿Ñ¾MÜi”óóŽÍ´ÿÖoýVßgŒû࣠knC¡Ã‚7jßg>oãØ–;øÚ@yool–ìß7 ö„œEMu&ÁÓþØýØ„ãh\›±ÔMHa¼“…PàßÚ /wÆåùùÿþßÿ+ͷʳÐY½HÎu/xÁ þäŸü“µ±2@½7<;Cñú¼é~ý×}}ã.o Ü —BµÁ]sÝB™2ñ2»q š“öŠ€à!0©ÙÞgÙêÅ)5žõÄ3OÍ1KÝ]§·Wc®ƒ1¼> ã_ÒЉ §ßW3íêÞ žÒg×<8»!¤”‰SR—öªñ ®éÿàüƒàâè D)×=d€øD±Rå×€ ¦&à´· ¤îÌzyÝ—Gê1 ùã÷ö†ðÆ~Ó±Ô9viñk¿ökéD‡“_ûÚ׺K¹2{6Ûjj Äf#‰_ýêWÿ±?öÇÔäG~„!>ô) ”gsIu9 ¤:™f‡‹ÇŒp6 ¬{Â4£Œ g¿Ýù/ÿåpXËi³ )Ä>1Tì6-9{¯1WXع…«³MÆ•øwdóð£nƒæ$.m®T7‡ôâžb]ùÆoüÆÕï`° ›w¤k6 €\/‡@VÏ®@$Sh’/33§`ÒvËŒQ®úŒÊΖ0€oú¦o2»¥™£ÉÖQ90pz› Áµr :´®>û³?ÛÉf¦¿|ä#Õÿ“òOb'Ÿ_ýÕ_í)üI± ªÁÃ\·ãÿÀhùùü«õ¯æA ¾ïû¾Å£:¡ßñ;~Çܽ¨ Ù÷‘i¿xSà~R`ö„&Héô¼æVwšÿößþÛiOLn{È´”ý".Ëä=²ÕÌÉõl2ù ªEÒC甯gó—:6ÆŠáÙñk­7Ÿý—ù—‹`œ¸ÄÆ.l ( ï­ÿèg"Ñbü•¿Uâ››@IDATòWø?Ó5yS_Á´49MQub M`q }©-)ù–FÚãÎ \GÂÄ7 ´× Áic¿Šcød³z:¶µ™Þ¶§èq3 Ô7ëÌŸ2°òu ‚‚³cTù¯ÿõ¿§q{H ˶ˆ &À ^Ÿ è´M#&°Ze.ßÓÄõTwW³á4Ø…‡{Cxc¿Âÿþßÿ{D£0¡GGv< uθÊ`Å1ÆWÒv{êÇüÇ»ÄÞ8-û~èV½£B¿æk¾Fa4CnqOvB¥B>Ö d]~Zf„»°)ð8Q`ö„¦½ì/Üá¸z:"‚W'Q‡ÉN¢Ž‡'Ð<©Ò`Ü_ù•_9¥ƒH'Nžqi:ü;-c=/š­`5vtù¿á}Ç:·pd<..±§æh“‡eà¬ÆPý|Á˜Š&$º™œ¦h“ͤ5uU&ÈT– TYŽ£ãºF„­ ¢\˜ŸÆ¦7„ê<Ír®æ5kVöºÃ'1G ƒ;Lx ¢ÀÖ6¥tš{7x;gømøÖåq9ðÂáÌö2ë‘8@à ÄÀ! 8\Ü-`¤,ë ÔDÌN‹™ÎÒñ÷Æ¿u•ºÓ 0ÄïÌO½ ïÔïÂCE½!¼áŸ£lõøÐŒOø½¬~exx†(”høP|ÕØÝTªzP×Q›”:n•¨†çÌ<‹Ï5Ê î¼¦¹•'ê\NŸwòÌ f6 ‹sºîÌùCË0ž ºu—m„/SAi@Ú*”ìWަ‡¸¾œpK¦ï·½ím”¯&ï…úOÿ)Úânˆ6¤Î³2>Øú,z‹Ó=uÇ?_‘S܈¤ü3ÚÇæGÜ_äê`Ê––Ðá(—ﯚQàã4£ûö.Ò´ÌíÙD5]IîpLß1““óKèb¾¶ëÃDÀ(ãä¸OOã³…”žÝ"õäþ}ûÛßNks`´QôlOÑ«OŒÝò‘£`Ö6ì”YV8æ{µ!V& Š!É(À$j¤uKw8ïJ‡bäÑÔ±-`m=¸îNã]x)°7„7ü£Z³7£þ,­Ópÿ_ñ3Ä<¼‡åøyÿÒ/ýU¨2 ƒ–™Vÿ±”²B~ãóuðÔ_ûkÍSÓþGn}—1Ç3˜]Øxœ(€‹˜$âïÿý¿ÏßwðÆüÄOüD[Á“+S¬e"–ÙOŽ+‡° Æ€6ƒVË&ac|¶¶Ç‘—qÈ›•ƒ‘0ò’âét¼Ë®uŽÛâÿÿñ?ì?çuL4x|MC5ƒœ¥FQà’¦žÍÃ1ïÂcCΖïyÏ{?–gÓ`3OLŠ>¢â¸¥°{ÒšŠ£F1EMÔ¡•9YÏIŸz6·¡É³yËæÕS`Ð0ÊB¥fðµÆD’Ü<ó™Ïœ6—ä¯ß»Áù-vá1¦H¶) k(`@p®6yD ÎÖq"¹ª_Y5@Lzª2 j–Ÿbí(ò«s ˆUЂµU®œÆ»ðR`ooþGù³öÏb˜Žê(;ô¤&­‹Ä:D®/qÃB°ãD ’÷¹P»p_cÚ—(Ó÷¸ªº Ųo( 2XUG=>Ÿä:˜]Þxœ(p9ˆ„§Á\¶µØaR†âYû¬gÝ0N-Ó”ü®½Ï7½éMdêäy7žŒ„CÞÕƒÔ©yÎ]î Ü\¾ ârÆ;{ÅËíÎxÿàüƒÃémkç»üèR€&¶¶­ßaZº´U3IL•¼@)õÿÎßù;3¥Ùú$$\)`ú̓¦4Ïj¶>3Y“PÀádå=}×Ô˜êÇÑØÓL{Nk/{ÙË´‘f}cåTŸÓÃiƒj.g䋞Úõ›@²a‡Ca4˜A;0o…v@`‡¾à>ßzõJH½“ eÝ™ ¦2Ø9^-‡ì¹Ï\XP65eàÐ}äÈ{Û¼7„7ÿ‹ÿãü±¡; pv}E÷­àïë¾îëâO‡I€€…“ÜEe”lj¦Ç·r·Çµ—íMÍ[ßúÖž}þóŸ_¡ÏBžÖ äz÷PÆä7O²=‚MûC«@É8ÂûÁßÊ0±8 2iж2\pcK;û;çïûs› ¦„‰az4 °öc3ii=då]Ì#Ú$üžïùÆs¢*déñ‹|ÕN_笳·ÌIMŸÿùŸ÷R¦ø™“O{ÚÓÔ°az\ïFéIÝë÷¡A—Waá³îÊMG‹ÀØ)£Ö´›Èö  }ëI 4‚Žj2)é’RœîÎIfdÐP LÕó`α`mÞã€î4Þ…‡“{Cxó¿ F"©R`J<³ŠØT2’ÈÏ(ã:Œ—nF–mêØYÒ Íég/}éK»EÿJƒË7¬ËâìÇÛ§ÊIrØåEŸ°;ÉŒg63 ÜPÆ}kÙw‰UyÔ¬¤Èš^ߎ–‚(Äèg«1³ÓÅH$3°ø"Öƒì^DMH2Ûm’ì [#õ¥aäï:©;Ä}ýÐýPcû…_øÆŸÑþèS‚‘âë«©‡ù¾Ê8r6ŸêúMwùa €Ÿ‰&‚§³Ûj è7•£EÈëlMÓÀd˜‘Û JV4s†€×(ӱġcjX§Ç”Ía¦áˆ"Íy¼5ÖݳÙþÝ¿ûw?øƒ?hõ¦’érRMè ›äVíñ`!Ÿ ø¦%µKoŸÁOáŽÌ;-waSà‘¦6Š›aƳ…U £AÀo2ƒ…Àá¤(³,D0Rã¸5ÙjœîÎfoµU€©ÚY†• k[=¬iÊ?ƃ߇âÇå6ƒ…õk4k¨òI¬¦öɸÁˆðFÿ¼ç=/ìs6T°Sá¹åeZeÙG§WŸÕm`ê+Œ+šK>;#ÅC1oö î.‡•qļyÎöµ[+éb·œ¡å _øB®˜qV~qüjàl·d”aT€Âsøú׿¾žWüýIŸôIØÍ&pEó5;ýÔ?h.mŒ€.{BªÙÉéÈ)Ç66!ÚeÆÀÙ9Ð"â´¹zXÐ[t"zÄæÓ–cïç§¿ÙB;À¯úª¯" ƽs6]&—‰À-¿,YCIÁÉÓúoçfª°ÝåÙµ>{(‰@=NY ¹O R¨Y…E\@ß!%oÒçs?÷su‚r0‹)0Èx,³œÖ¦OööÆ£‡Ô.]Nƒ —³í¡ñ¾Üxt)€Å&1Â,ò]®Ÿ8 ´[kVà‡¡ÀÂÙ j–ÝeÚà̓@fäÂࣻG‡†…>­#)5=X«Ëδ܅‡–{CøPü4ÎbÂ3èæYÏz–²ˆŽaEÒš·X§1õ"CfôÉÝnM¼‡U#ÖMÞÓÎ’ýµQÎ;¼˜C•̇üQó¢ «:Šíq^½ ›.—±Ãú•™Vª$}'ñ†£±Uf®gÌ8“^6‹\ù¢²ðÛ­1×àÖU#[Ÿ6xÎÚö:if(YYZìå¸ùáAY¦ý*Ô{ðì§WË‘Ã2ÓóÁcϵ5‹®[’…°(ŠNaj؇®ìN9 ÙŠìýá: îwyv€ˆßQчßÅ¥¥ž±îÅ/~±S­çóóÙùŸîóۛɫD ÉËc“¶B§Æ3ƒŸÍ#zú}É”5zÐYÜD½…z¢} É³Ä±ƒAbé­ [!OÕÌi×¶µ™Õ7òyDá†]›íò¦Àc@qþÂ+0;p´K‹hW}ëü{ßûÞYÂÊ£Ä]?9½—C4°sêÁÑêÔU©|eݵžh8ntzÛ…‡Š{CøPü x†–yØR&†×ll¢zh‰éÓëK·Ž~"}Yí³ÎËG<¬›<žKг—Z&$cõm›– :‡çÕ̶sÃ.o <6¸bƇ¯éX¶êñHç°qSc§WËœKGå:ª1×5®µÊyê"V»°)ðØPôF#nRú_´¼¨£¬l×øõHLäs‡.†sÇ&X®¤ëŒŠ^7GϨÓ)¾º$2Ú.0k'»üpR`o–ߥ|¾@FâämXÌÃc<‚„ QÞ>Tžz•ëè)†§=5-K…0Œ©q—?÷h†ÔÓÔ¦1êð™iyZÐ[+ȪýµíÜ ëWúïòãG³@39|YN˜Rbt‹°d]ùð‡?ìR¾™iÙ‘0 ããÍÇèa‰¿Xç ¾MPB·ÄQºbrFÅô¶´Æv ƒã^ˆýí ¸•rLµ—¸Rô¥|B!žëW<±–k¹÷‡ëOsWåÙþÿÙ»·Xíº³®ÿÍ?žkÔ3 §&‚ÁDˆpwAm)TvR»Á ¥H…H,› …–ú°šÆJÒ­Bk_ -T*‘´–ÄFPâ™ÿÏû~ËÅȼïu?÷ó¼ôÿÖZcÜkÌ1ÇsÌkÎqÍßµü¯N%pÜ{eàó\Î ÅOýÔOýê¯þj®È„½÷¿ÿýÀ™ÉÈSOYBGo×ÙAÁ‘ŒÇós‚-,¥¶ŸÁ-³ÒEÍø¶ql[¥A§ºåz]¤3à.l Ü% ]§g¹ÉÂrgYA•Á<íÇŸE=~®ÐhW¤çìJÀ>APp(Mç›bÌüYŠT¤$RÜ®#ìòcK->.†ï–•ÃG˯X¦µ.T8ÌVÂÎҕʨ௵whÜW  W®Y0‚Ú†§Y5~y0_(Œ¶iNÆ2hUÎf ùö<6>>8…›­ŽÓ« ¸/'[ r–£”–ÀnqS·Æ8£æƒü`a)e}ŒÛ±q˜tè_3ã~â'~b\ ósÖž`À!p¬I¬CDßu2ÈÙv\2I{Ðù¢¾'¬â'Íçòït™f„)ì¢?ú£?JàYç|ÏËd3Ê~\Û0j_Ia„EdoŒ|Å œMž!ÌœMØÄ¡Cdo‘l’Ixîÿú_ÿë9;©k%ô¹q!E/ÕA›–œYDŸNúA/°q^÷º×¤W—Þ¡—_—?퉚ñm´Ô ¯W-ý™ðoýÖo.Ú)•§ËóÐrn Ü% ]½ü~Œ <›S§…`È·º˜„YóFÓ4ƈ†“¯áX)@PëÝU°¯xEWœÆ@¬š-p{—Á¾—->.—–Åúi¾÷½ïmZÅõ¢ƒÚñ“€öëÆ€…ì×8åkKÔRoµ‹è0ì*þQ!׆¸z‡§¿¬c6¤™OþãBÁ=M ³ÕqöRÿóþÏqÑÑ,›ÛšxcÌô¥Lkk-KÑ‘Jµe8ý}›im[ŠÚ7,G5§è:“ÕƒÔ€\þÖ³kYÔW½Ñ3 $®m\tMIêvÈ¢A´ü¾ïû>¢‚ÜåŸù™Ÿ ²—$Ê\þÅHÒäHa ôÿàþ û)”,*Hr½úÝ(fðvÂCäüÅ_üÅø'²¸ú)ÑP;¾Ý)rµlßñß‘•Ø“úwÿîßÉê5(q¨GvJ7²ä§A-=k™$N›©¹ÞGTcù]¨6ÓÇÅC¬ì×Ü4˜ o=ñl½ð½ü^ûI$3“‘ðP÷‘o©-¬¾9;….tX˜sv6î$Ò°ôò ¤×‡¦úÓ_ß”ñȧVŒJV®òÓ ñÿ™¾Èx`9ÄlÛC+W :íúørÕˆU Åg„]xœ)°ÂÇèé\Áô?sòuŸ…:pâô!lû^Ö˜‡Ï,?Não“‚V%?ŸðÖm¸sT—q_³Ó¿šQ,¥A&^`=ý¨ÏüwaSànP`…ž-›îËJ,©ã,.¸­É?sŠG_Z|¤îï Ôv9F„%yÛJãæb×a×2¿‰ÔâøgJëٵ̪É4Nãx‹øÃƒqÉá[ßúÖ5%)ÅpÛUÍh•ïþîï–•p< ¾ÇpFå|ÊaªÑ÷ûäOþdÖ$‚ÓË^ö2òÏ›Þô&w*²…°=×z¬  Rð³b¼•åå/ùóž÷<{6ü±?öÇVRœ½k·LÒó|±w¶>¢>ĶÂ;’3±Ùæ–#«»âO„Ætxj‘HhŸ(PŸƒ®îù²ÓÎP+ÊGTG¯q^j¾ ž‘š<¢]¨íiFèíÑ{¥½ðÍÄ o„u•×0¸sMÛ»6nÄQð{Vh\Ûïò¦Àm§ 5üëcAË“Åo¾)-õ´ðª©Yðs'd}€"½ÕpŒVî|Aò+>­¸Q}MìoUHÅ|‰n;åïÏü·@ø=ëÂ}ÞÖ9Û§Ü€Ö[«€›$õ­ÀR¦ï/ø¤$­g½úvòkQþóµV#” þqc«Íü6uÝ ŒxÇLF¢ªÓ ²×[ØåM;@‘ [îèø@×VŸ•{fZ›Æáb ‹®åøÔéâÜZkUÊ]Q¼–$1úê"„ÛáÙ™\ïAª»Æöy›Í ˜ßüæ7Ÿ XÍj0„HX¨Ø‚n ¦‘h† Èå^d©M˜ý2’‚‹Ô濌Iº}"ñÉ8ßû½ßËØ%·ÛbäÎRã™W"áľ„Ñq¤wòŽ '|àü5 Ï{¸_ôE_„I;$Iò?1xº“ñ8‚º›š™#òõ&A½ˆ©2 Í#ðLÍüð¤Üóz(QJß‘ÞXùi¥­VãAìÇ~lîÈ)/¹«Oõ!‘L3±L‰Â¬LŽÕŸþÎÈ[<%ή¹{°H'E¼ÕÔ÷çði³4*ø|€v宯ð+É“ÃaàÐaK¸ÏS02H ^")þßÀçèÍC«X])ß°;€p¥Éc^Þácô€¤ä¶®(ŒW(森r0‡jßTh,‰.RðH³¼:ó È ðÊW'ì5J ©¢¹•,›BØŸªÃïüø“Ù"­É”¿ÇˆŽ{*› dÂÃ2Ù‡››ÿ_R`Kƒ>·G}ì(bÍÊ*8Èá€1ð H›k¨s3¥+«ð+S¨ÃaàPUƒ‘AJðÒ ùˆ‚'‹ê@5òˆek§rs lð1z@?þã?nýøûš¯ùš™–`¡Õ”G9Í ˆ?U-ý–8Ÿïì„”ÖTé){¸tÓj×xõD¢9¶b¥@ì”Kp3ûð‡?Üáá—“ºÝϦrüÖLèë>Â3ù]ظcØ2áð]Øøÿ‘[¼c¬ußÎM®f¡ èæxÖvÓl @h7äüÆq@³…€b±ë # F:¤/Ím…Ôƒ €(8:r‰5²ˆí”€ó›îk×?nØácôD$ŸàFÕ*ù3©Õìm=«n¶ë•xТ­ž÷‘þcieFôs?÷suªœAxõXóò·Ú«§õùÖoýÖi³,u‹¿Dˆ…0ÍÙ™ƒKðGŒ¨¹§²)ðñ¡ÀÈ„„¤U%œÌ’ì!gÆLŠ eëˆÇ`•–äxþйڻ©ôq)w&’pF¦EkÛYéÝVÃiSÇã?þÇÿ¸Å+)ÁËnÞ¥!vÄ‘O(ÚÙ.àÅ5†3ìSrË2RŽã:ˆH4”`5Ó¨ÉÑ ÉË…i8Bû[Hgg¯äò—B$üäOþ¤¤—ÔD.M^¦”ßiµ×îgËà'+Q2¯eüò/ÿòoþæo–™]*—¾ô¥|¢Mw3=“4Õ³Ûmq üùYŸõYƒ½ôågÅ‹)æf§ðPQ‚õBUÁŸùh\„!·®âNgØ)|D½^9{¶À töÏôÊyñÖfF½®êg×l4Ÿ­‰<ñ(æµ_û*‹<<ÜrÒš9¼~éöÝ5›·‘`ÕÄò Üj)ÅÏ3Ï’ôÉ(ašÁoqæj*q†™ÎšÃl‹ ŽÎ`jä5þìXã òqõä¿ôqžó¯§óK¿ôKX'¸ 9x«Nýúå9S}k(q3JЪS­Æ€–&øïxdP³5Í}5‡_þë<È _EQóœ–ÛYüñz™öl>n¸˜ŽÂÅ׺°~±UÍKö‘ÖŽu$Ëh•¾ÊâÓª‡’¥ùn•¥žHÂõÎ(€‹è°®%í\OÊ×ç ­#‰Â€“²˜Î˜h´² u|üAÞÑôÊæ…œ0\» ã÷ÿ÷¹µwטŒ¤o|ã]+|hûÄñ~¯WWDýÌ©)`•ؘ‚¡Ù ƒÍ£Iüf+…É¡M¯C\G,4‚ä‘EN¤¡‰\7öm)áO\¨AÌXe³u§åt9DR„6§½L`ÂxÐt[…êC—‡Ê#Z_olÔ–ètÓÂöÛtw¨G%&C®" âõÚ¢É:%{œ„kÓ$ŠÂ]ÛT¾rÑvÜ5›·—¥–˜55ë(†[â^8Í…x àçl P!XXø¸XÇ$0‰n€¥Ê ÁÎ!¦oÁ̧=l@ÖÌÕ´Óx lð±{F9ˆçÌ @H[G;š!ÊøuÆ£¶bí[ ´Ÿa JT{© ”E¯h‰Ý'V½?Îè«õ¿ÊÃ/u»m¬TÒ¸—lJyØAÇÖ±Nr—7î®§ÒcZÅå'ç8|epAÙP–”DX•&‡¤…&o‡fý ë"mÀ8ˆùèG?:CÖ¤Ï~ö³'ƒÜiËj؇ «<ŒÜ”Ì&ƒ!$1Ö°³)I×"ÀT0¨.îoaßc£“T[#'6ˆprÏà¿C˜3ÿñ:”'ïI¨`ÂO>ù$yØô—îÐ)fFV>ÔNíµŽèrò•y®gײçˆ>®»æË!ðØ1Bþ›ÿñ?þÇÙ«Ÿšð¦Ä¡ë­Á‚>.qÖŠ[Úãn¶Þ:¯Á:ÔÙ2eÿ¤£ÀÿϾW,Ÿ©0ÒoÒÜB†õb'ð÷ª¯;^¼á oèQöc{̲úž5±k–ÛÙ[Ø•›·—òf­ìeà–ïB9Àp¶ÙÜem¹–A»‰6doüæ,žV‚•Fƒ€ÄuAJÀrÚsuY‰‰GuÊúSÕ˜d:Äu¿Šµ×.?¶Øác÷hd´ÆèVóS¦¡?¥¯üʯ\gÜ\-ã·¿ýíÇÇ>¤ÚKð­rÃÔž†iÖ³šÚU¹[›~!¿€ãùr‡šö’æ­3ÜåM»J‚Ô6¥1ý«õ¯Z D«»%£PÈ®e˜g©•5þu¼øfAM¬HÐù¬‘0 K#ÉÌ¥#vA¼¹@vš]—Ȉªø¤†‚ Œ?–([ñ¢\mb‡Ë]i0”XÒÎÈGÖ0Œ…\ÁàyYúBC,Îdp$#E¦KN­ |CúJ}>4Œ&ç_JüKt¨r¸ë´×Ý §^ 4èÒi¾óïäêöu·1 œTƆ¶zÀÎPkAF6Ó6¸D£|Pñsû‰öÈ{ I0:s‹]à‘ˆ7Ynµ_}D=ôk|Dõz×»Þ•bÂhüÃóí0—–î1›Ã|ˆv¦ä•î“á%ï¡üÚ¯ýZ}YJk\GkDЄ¡Èχk=p¡ÚïÃM;@PjåÊ­+ Ÿthea_µvèÔ­0OùÀƒ…í.Ö= LÎøÕãx+a±¯ê™!Ñ!àú‚¼@Á7nm¼Ë?¶@øØ=#6wkÉ—ØÌ¬ÞX¡_g< cpмz4orjõòÔÓL³úÙ¢TÍêvõÙŸýÙk&+gçï¯ÿõ¿®3*÷Tƒ¤Ö=(:µ½Æ×g´Ëw˜—¡*4láX§¾µ4¬£Fµé\ß]›ìeZ‘ÒÜ…\íñ­ã¬Á)Üd$ŒÈ¬@óa–n$̳€PT9ƒÃltg›­•¦g#»±JQ<ƒõÖ;‹œÝùÈ?#ó0éÎÐçFV3ÚÜàÈ„p jDb§HÅ(i “MxüKÏʇs• .tV\ïý´ŒµÒ¾ó¹š»3JölKÌþÚ¯ýZe#ÝÈ%FwG­¢2 ºà±“ðI%ˆþ¯ÿõ¿Ìeoö¤F¤¸ –k¿úˆzÜúéjlY º)FZïç¡ÁŽ‚ 2†J›ž¯†·¥L€¨A´Ag}Õφ„È¢žC5›sÞ§(3¾Âå%¶¶ÜåM;CLõÀÿZ@èÕ©ÀXÀ,Ö2\:ÐnjVÈŒ ƒˆ¸™¡uÚƒ‹í;½v8W ×`ÇŽúˆ¤H: ½vßåÇ[ |ÜžÈSËìû|œLÎvϳh o݉êwÄ?q,éŒv´yý _øBãL Ф¨‰ÌbV(Ìi­©,aT¡‰!³¦§†Q±«©º}ìȺ'´)ðñ¡ÀeÀÚ—›éƒ)¬…,[7O¿¬" „Ò€û÷Æéºm]_ÜÖì#¡[Ä@ÊZªº¯ilÎàa=H„ãßX¨`úW¾ò•ï}ï{i”9—Ê‚çH4bÃ@Æ«aG§ŒE :`Mb¥­“9]š;±(’æá{ëlßóž÷°kñÿ\E/.ˆä ~¹gý$/Û“¡w£ '€±¬^™ˆ¸Lg”ß¾Š"õ™|¹mÖ™¯e‚– Iìs”ýÞ ÷ÅWö&ÿ:Õ!&¶/¸ˆGÓ¨× ör郑ð|DÍÖ«˜DçZl˜giÛMå2Ó#ä+{åJ?ã--‘ŒÛ˜^rgíaT!Êþ"#H•ùˆæbWK—¸¼¸VÚîò¦À]¢Àõ ĺcÁ°ø¼šÉê9ë±Â)¨à7PF:L[—@£B¬“, ÏY¹AZËNù¥Ç)­ àêÐô|‰îÒ¹÷²ÂÇñ)—Ub6ŸxÞóž7«NÁ^¢ë¤?á>¡³‚õ‚qû´ž[™<”¯³S…8Û:×¥\3xˆ$F0\ÃY¨ SãžB·6¢“%Ê«ûL£ÃƒkÁ:á]Þ¸c¸[ÙÁ¬¾£n@/ðÃ÷[Æ‘ ÄmOz_ËÊ*ƒï5~þóŸ¯#+ß©óäe#a´eõAë *èïìêAJž\?ü5öuw›¤>Ó¸bá‹.“Že5ßµö¿„à€2ÛqS,f,„1-¯‰0\gK“-ð ”YõÖÄ*‰¦-E ÎC´;8V¸ÐÁxÙ~x*Šaêä 5O͘DùôÈx—-±æe” FÍ‘“'‘¹ÂrÊ"Z à¼lMš# +´HHDîÍ!¬bþdl€7…P…åJUáEý™Ÿù™©?[XIáó =Vqƒ€£‚´@^f…¼ÑÚ'­L­fKQB_ @€œ¬9?ò#?¢fÒ^XVgç³+7î0±YÑ ÜpÝœÂâŸÀ˜5n•gcr׬³ýÔLùpcP°«à8Oh¬£SÀ¤O`Y3o%o›Ú;e~ÿößþÛM4bׯ»|+(°ÂÇñ1q²Àh ›\I)ø µ,-×5.¿$4Náµ/`I/n ™£ZÝ1”±‹ò¯¾U­;¤¿02lªL>W¤Ä‚0&‘ÀŠ´$.6Ÿý»)pç)px-‡>±ï®ÕÄé±¥M¦â®£Aû¾”`&ó;‡=7«i”ý]6v-¦Qp£,Ò_üÅ_¼üXÆÚ˜ØøpQD<tΤ µŸÊ¥OOäc?ƒ!ˆÚ›³Ô”,iÆÎn~P„¡aëop›×+• ë<IzëL¦ÌúŠYÆž%ÂeùÐMÍ o”êÝ3Eá³£M¥ÛqSnm¨ç–¯Œl\t²¶¸#ˆøcõ%…JMDÊ"–Ëúà=aC(Êt(pZð2xåÈŠdfŽÓ~’È“ýÁyú€ ƒöÜÅÙB»u >i=JÓËHþ/‘ŒWºîöðÐ8ýˆGöçÿüŸwHðîͯM‰Í‚’7-¨³“Ù•›w†eÅ›%Ð!nå ÃßÔû”¸w mº”çsXÖ4h*p‚ˆ*ÁÅÔ4ʲ”EÒ‚®8DAgXI¿´ÇX:›3;wá¶P` „ã“‚uò%óí§’·Øø™hæ‡þh¦©¥ýµ,çÊê&p¥6~éÎýJ°>Ê¿ñ¿¦4~(s‚ަ—ˆyÊšÁv0`äB"g&À/ú‘ˆ7 ô@Tñ8RÏiSà‘(pÂþÍ¿ù7-Ö’RÚî¬jüýZÝ€ÈìBÚRÍj²Ü¸N¬ÿÈÖïYAk®2Ã,^ñf ÷᯻rM™÷…_ø…£M?PÃ¥M಩[p#ƒ¥Ü Ût³nùš hƒåø\)§Åí/÷õªŒ6Ž…ÈnàKÁ‡“Ñ;6Z¿Ã­‡šq5[/†§ ,r£%.âÿ³ý áºDð|D¡I†å€iï’—y&œG´^ì Þ¦ÈC”Q Ég—Ò µ ›w•xæ0ykdha­; 3±œqu ¯X5†+x«½ß`žöÊ>@ (ØYàDT.V F"²Ï„ֹߧ!çïn3è”™hV€ƒÏȪòúÏÓ]}Ž·ô¾¶@ø˜>8YF­+ÆúòOþ‰‚/¨‰¶{„ÃV5=ýî*@Ÿš€Ç¡Ke2T¥¹÷ ZÙÃÉ2_/ÖTÀÁÈ,{ •³­Ò8kÕÌo‰±‰ëÐcú$ö´6®¦À) ã~Û·}Û:çÖ‰-ãRÍX/¾ý–˜Ä!é€Ù¾€rõÚøäϧ:>õR”ed¹2Yñ#ù¯Tz\ù!Ik鵊L˜50%1·Ã„48/â€Ê§èôB§5lM³ß©,$ °ã´Ù¡æ&ƒ!#i„èuV$ºàEÙÛÙOz4ä˜:¶G7è¦H,R¼¬{&àÑèæù;]_Žšìo¼3hÐ]Èå\ô@´U>4ágh4“w¿ûݤî®ÂÎFqpº Ãaþ$1L¸wƒ7&iü¬“0“&¿eôÄOØD­ÆOde݇{œCïá¼Uöf"ƒƒ‹}¼Æó~ŠS­ñ'}Ò'õñPR_®7’ö„'[_¿ÔÚ`—7î0€¥U µ®G«ЪôÀÒYMÖòÁo¬ˆ-º $ÌTv Ö€«E¿ÁH¤nIªsœ™ǃ½ý*Rñ«ù—ΤwøaÝÉ[ÛácúX%kjqf ç6Ñ_þå_nÝÊÁ@£¯¼…(@…3^ãÀè‹^ô¢ºŒBbÉP´MÕ“ÙàåQfW?Òa|@.sâq–0Ã倌ƒÙ ~¹!=¦$ÞÓÚø8Pà ­‚Ó€Š\t|à[ÈÙX‚é“`&>æ;Ó8×šÊ ¼2Ñ凸‡™£¬tÖ!¶bEµ<ürïqEá|„©P,g¶RÙê± s”"R_Ëkr9WGG‰®|ÓàlÃF½Éu*ž§J;"0dñ_M²Ð 0ô¶·½mä[B ±YœöH2Z*#®;"Íz!Â'ýÚMòar nÄí<”FŒ¨6~›è]=ÐÊjbG´Ûyÿûßß·‡'CšªÜ†<™±Ý;ÁŒ,Ê!M¸‘{a vƒ—ÑÜËCºæ ³f¾ðí Ý@ÞÛ•¤•K€‘0¿¥ÁSúìš»MYû‡Åe-€Xá:  ô±µ± @²øÛ{êW•–à˜—OM~Ú  ²¿‡>"i0Uº0Æs˜àÄ Æê”z(Ø…6ÀjòäC},îö3½]w·ÂÇôyQµ–e¶³ÔÇë©4ߌ JZëðþÑ?ê7SƒOø”ÙÜÞ„«÷§A÷<ÎÔÏêW—‡#U>ÕçYÏ%R¡ý¯¡XØQ(K•¼€äô™_(Tß/Äù˜RyOkSàã@U&,v7ÓýáRì3H›`°,'jø•`¦åcaúÜBÒó™Ÿt N-CZdqeåqq!êaRÏ¢ÙÅ^¿Æü¶3›Å_³‹ýz.7¸ÁœE¬îlNìd$ùœµvkn‡G¹w•W× ].“©d+á9¹Š|tê° qhå{(ÌšŠ­]éŽëº¦db¦ë;}ÙÝàåü¥¼­Füf‚C«k,«`âì5ï1yX—I1gM[b÷ÎBxpÝœfkÁ;™ÛgƧ¤ºßáóî‘éo‚N‰p¨LÅÊЃ,- H2·4¸R~—ïÀ¤u™´¸pà Äê,îdñ¦F Œ©xV›qÏî°eæ¿èV¬_@Q}ÛÖ’‡úÊãsvrÄÈ—m(Lí[¸žuO¸ñ¶ßãß'è{ÙZ¥7YŠªÒ·Ÿ6w5¤(ŠqøÜb%V~QÔäõò+ܿцň<™¤Æ9‚Nã)ø„»t '—!Õ8ãz K% Žjú*€†s »°)pç)02aßQ^”go¹T¢ÉDš{I…`ëÃG}]MSDŽEXžvdÛáµx9Ù &;¿@/Ϋg'¶Vúº3%…Zûœ—Ns®íež«„½æ FˆLã£~“86@2ð‡>ô¡r¬‰ïr„áầ‡,`vÅ@«ÐÌÑ­ATozӛʚw6JЄMÛäo²žÊ‡,™óÜMŒUíkê•>¢§DPÃ<‚:¦}aÉéÎÐó/Öé¨" OFr>Ü^H8•ôÈ:ªPÍ*uÙo*HJqªbæ¶ ›·Ò0óYA)+tT¹z‚^ÚìJœÿ,èÒfÂÂ<0¯K~‘«m »ˆ¨\YΧ â 2•±‹ñwé¡|æ}£®S¹ ·‹[ ||ŸWi-0þ`3K õ0\%XE™j63åYºœ¹ÛKª8 >Qõê—Wø8‹ó Ó¬êµ}šu@âÌ®è,"H‘fhôî°o¾%”[G 7ó¹‹]ظó™ÐB`úàZã;ÊRG¸’À“wHÝzYWÊÙ2/P_Y Ý1"¥|ÑØ8}æ%ð8ëßx–Îã,yBéoýÖom¶Vê2W(RÇäw9{³«XàzÑÊg# <¯4¥Ôãµ»zÅ3sQocž«ph2çq%ö‡qdšAÀ…ëëz¿»¼)pK)@&,÷¬‚ÓÂ|q­MËP oŽ:¹‹Óà†H/ ?~;%õKÆ|à^É0-/¥µš—Üu|¿Õóu‡v?Sö—kAñ5NE=ŸóSAN/\£bz#£É,<#üðùÏï|6UÓ…Ôä§zêÂOÿ±fhüXS|OnSà÷”#„¸å°p&ôíd4”Ù¾ˆc¬Ó@x†ëÛȾx*N;Aùæ5©†Å¶¬ÊŸÉjçÐWŸxsýxòýó!÷¥&sam%†$ò •†ä‚Ó+ÌD eÙå2áL˜¶ Y7ÙÏNÑXYGq-Á=c0$2 †!Vä‰,ÄrYÔùÓ^ÞðI¾‘¸n,òÒÇK÷Š‘ŽçªK›€iœÞåJé!‡÷zðsÄA¢Sÿ*"2S@Ê8Ä÷®yX3»”'Œ @ªÀ©¿Pð""½ZÜA ¨©¶—ÓôÅEÙì]‰t¾,ž¸È¥ îYßÔ- ^xûÔݦ8tšHpŸ¬ Ì†Á*KãI4 €‰:FœLyàYœ!–5¿çx½˜On|M±>£•Ât´ÏmƒÄ qWgAM•“Õ ãêé`Djüó¬rÿÞF lð±~jœ—fm³×·µ´]¿Âˆ¶B¹àȾÖÓäRæ%Ú„_Aõ}³mÕÊŸÆÃ¤^PY&ñ9«þYò.ƒNrµò™—Ÿ ™ I†MBk"Uf(ʧǚâ{r›¿×x äM/Û¢óå.£ y#ñ*ô6µÿößþ[_tk*cµ© ƒXÛTø0v¹¸|Cøa©1yÙ¢ðr{gÉüýf»‚YÝN%À³‚ 6@F€ÜDßmy¶òÔ`Hz9$m'`#E)õÎrS%×,<Óô0‹U–-Ç»©ã…z<Ó^ðÑ«t¿§ÖÑì‡7ɇ‘Ùÿ¬ýðìÕ½¤Ç8¿ç»Ú<϶¯r‚½T^-•³ÃañE&S<‚×Ã÷çÖüe|ð]°‰¯ÒØ"¸¨^îKã´Ë®Ù¸3ÈkåŸ à¤Æ‚ò PáÊ ¶“ÙôÂLK‹<[ÇékœS?¯6à_û…ÆTbw>º ãŒX¦¦5[æN)´ý,PÚwªö ëy:÷ðF¶@øX?t ´öÒÈ‚MµyÝHûÛò›,OncõðaÖ+ñ+-Ué¤À°W#ø]%ÉTJÔ5ÓÌ7|Ã7D²l’ºÓ¿ï}ï£æWæ‰4<~ó7sSçry><Ötß“Ûø=¥Àeà˼fu|ú§zÎÜ{º8±dV“Ð@;éÍ›ëæàTz*d+½F³‡òÐ–Õ Ï% ç;¿ó;–|q_l€-m+ òP¶>·ÉŠçį ¡’´àWÒ‚áÚÄ4œÔ4ü ¥fAó+Ç94sï˜ŽÐø“v««`¼,½ül“Þ#!9ña/cºÇ+©¤’ž²Ðæ°Ú= ÃE;ÄçÿôŸþÓÚ{+ˆd×× A¯So¤‘ðz„>%\ ñûp$?ãü¾(óWþÊ_qé4ÞŸÃ //ŠCã}¸)pÇ(à×Zî×J™Æ™óo?RàÊ‚²Þk–½5€±é>½ª)ùÓXU®0[k,´ÒE5ÆÕ–Cy|¯^ ( ŽÆ$TÆJÏ`0íwávQ` „ûóÊ \4 rPÉ~b9;º :ÝÖªfVi±jø$ôa–ʼÎrR>©û}Á ^PË~?ë³>KŒÊÖüd\àûT¥q|ï‹nR8¢)Ï>E9'Ì€®~M–óÇýÁìùm < .À_hÞê ëåÓ˜›ÏscCðìT³v8ï1ׄÎÃÙ©~­©‰Ü`±i]?TH¡Ë‘W]‹JH.;¦Ë³»A˜ªœy²¿Ü¸{b ³„-<c2…ÍhA¸‰õúÆaÛŸ« AÄq^DY²^«¿I‹Z=šï(´ š–A ¸{Oê^ÝÑ÷lj‰ßD}Ô3¦Å±,ÙýçèZÀ¹ƒÊN1æ…<4‹¢6œÄ²4®¢å&-oU-ý‚Y®".1‰T,JÆšïa+xŜ‰–h\R…”ü§ÿôŸ¥5&“ã£EàÌÍî¦À­£À¾šÅi´XÚÖÅ=2ѬYˆ(ä±…–RëºöÄ„ÝæaC ]ì B-óYþ «xÖºEòI뤱K3ÜÍd®/$8UÆR¸’ÊSdK#˜¡=ë>çs>gxŽkICB@ —!KKŒåB„áÙÉè{0 ^Ž$ªi/üHGfbÂl¼Bzl#á*Ba²µDܽÞò©;öŒ°gD=ˆ òá€E¦töÞ×ÊCÐàœš”ƒCy¦—°À$¦ÂŸýÙŸíQö’ë˜4톺ïÏŒ¦pa!¬ÍvySàNR:ô°0 X²XüO@T¶;°jÖ²˜¿:VH°u˜¡ÒÁ«+œkÌÔXA¾àŸz€0ÜØ§D Иia œór4Õ,ÎŽJlãl·ó×ê–¶@ø¸?8.TËþФ”¥f¶ ûƒ çœªÒæ0 R%k÷ô?{Ž¥ÈqÄèç7ƒçäL59­12<Ýãc?t•I©õá7¸Ê>ü…îàM9—òd€™R*ÃI«^ûÚ×®*sŽÏ1}æ¿ ›wž7Aá’ŒÉP ¬Q ‚d˜ä‰'žX—aô6’êÛlõ…ê‚\) aíÔÜ4*'8£ì×<š5) ^,⚎krË&€{  ý÷88¡fH.•N4kß)aHbÑ8Ò5^6 Îh Ljø-1u@›Ëñœ4srô½0Èz ÑtiÎX(’®g/”G>¤¹ïê Ò¯GìAc×Þ1þtœ D4/Ï4 ‹N„Ò„^¿ÜÕÙ!C“šå0&©‹z}q\NÙCœoZÓ`6î0ÀžR-¬Ë@“Z¶V=øÔú¨ð-ƒXÖ/Ê`XÝ'ýÊ! ÂYž5£áªA/øç@h‘Ö¦ ¼ÏŠÔSÕ¯Ü Edõ€h§@Ó§êïû}Àê~p÷áÖ¶@x žrÞ8òwÏ\ùZ–´æiÇñWC9#à–0®>FÒ²OAz:el]ƒ~­B޾¹ô°ɯ~á~/È :F`r”úBT@¶{Ç;Þqš`î™awaSàžPà, N±ÂØ8aZM¢>,5V“ujÙ­2¾‹}ƒs ªž$°Ò[àzÚzçÌC ð¬xj$…¦TÆ+ÞúÖ·®#_(s/\=H_õªW7ÚŸ=ebëß°Ì„TýCÁŽSƒ¡AHYþF¹l<;ÉC%Y ÖÌ/ h»ÆS¡«0<Â2¢Ãá*—µ§ûôÌnò/]åC/F :zaúšÌ…&—̈¸^<¦ Õ$aYéõ²ÕG-QáHuxöåŸkí¦À§À¨{†¹Fø|*0\pÂðãµ \ZàÐUÞQ§€±!׊ô,@nÆW°­Sk6mcðÏáa7BƒÚã&Xº ¶jΘë–ô ©z0u&³ ·”[ ¼®À”Ð3Wî ³2l™pŒõFl‹Ež§8 ùkÈë›ýä“Oæ6NA Ë#h´Âj䮘K·©TÝ1/Ùó¿ë»¾Kø¯À FèbÏâÌwèLè”L÷3ò.l Ü œÂâSCJQ»>Ì¥…L©,r£ÜZ›UÜk¾^CÃl€B[ø­Äù=•§ãfkc9ŠÏÙ …Gó %é½ño$œ¬NìÑÊUà¡ ×½éÔ g¨èàAl¢oêþÀzT^Ÿô_`–Gƒ\üµâ“]KÆm «³{ñ]ã#zyÞ“ÒýyÜÌ ‡l7c?<h†zyUöCb³SC´t¢›* ]7cjú§:ùЪٮ¦ïÓ×þòí³›wŒíCè×ÚáV]è¸É-ÆÒ«€+§-p+&ÓºƆ>í Ø˜Ú[Þ¡ß°ò9¬{«˜Òj¬‹àbÙȉ?,ÁËù4æ,pMU‚©3™]¸¥Øá-xp¾£0ÒØî?ÿó?ß ¤ÕöÚ£OÊЯœT£ ÍÀüÀ(ø³QŒÌÅü¦:ì×Èe,\W¬ *c+™âV ý|Ï÷|OÜGB<P£0N±4®Xž¼|Ôe½·¾Ë›÷pLx³(isïd˜¢ò,.ûX¿ˆ'‘æ;™0ëžDµ¦À IÎÚ5À+lræB— ï|å+[Ô¢Và‰Ëí׳ĉÑ[Ó‹ßäAÊó“¾™¶;VÖm€§ŸÿùŸ7 }ùW}ÕW…„œ¥Š^‘ÐzÅ›ÊîBÔ“àz‰¢Ò©ƒYì¦qNëå’ÁÖF}ÆM·Íjùë¿þëpîºêÂà05ê?úÑjöÈ>¢ëd0áÞ ÏŸ_O-'z‰øÙ½H«®ßΰP úä>Mù¨‹‡•¶ñÅ/~ñ\T½1½Þ‡~ì¦À=¡¨Óúê7 „{Œ4,!ŸzáºX¥½¯€5^— W:÷!]ŸƒuüµÀö,õ^´ˆ€>.êÁÅr™£•,3WÆ+‚ h—JASÕÈ3™]¸¥ØáíxpéYsÂSíjÖ°U ÓÌ"/P”9 Æœê×­Z½“K sq(ƒ¾© Ä€§^ ”èC)AÆúÂâPe¸Â)ø,q…Áa"a=†t)øxTή¨r…Ps•]ظÛ8@äÎs×¢NSŸ@IDATMZ/œ'Ë .’ÄY>~Ih­>°¾½¿‡ T0 ¾Á„H´» {Žz_n߹ĕ[Ég}²rYŸ®÷žiÙÅ~¸{u_[ ¼;- dºœ¦¬@Ö°¬µ-éù¤HQD¹NŸÔBm ž[-?•³`Pžâù$gj¯ålz£™?NPÓ]!‰Qºa¬$ñ/ †ã8KUÜE9»‹ááf„š™^Zä<=öS©®R–¯—ØåM;O(›âfn>Hd²Çzy$ÿ€ 9åZGýZYØ,>ƒTÓe£‰cp˼^r¨;h"H,¼Xó.tz¸zZìøØ„Á˜3¶CŠø‘ùûIœö=Ô°¹ “1 )ÌòÐÌÍfÅ9\…;aGk˳†4š'JÆÇÌŸ<öPûèÈý#‡ð 3ƒàÀRé°!”—eä…2BŒúý¡?txs.ôòè'hÐŒü6]¼<^¡!]ïÕá¢eÜÐ2EÒ ç±U¦ hz± õ»ÍCä]¸?o²«·ˆ’1–XÐLÂŽbŒxxÎéëWšYP@—A°•zàÙº<‹ñ bêxÀިƤ0Ê+£=vjÐ1×ÖtUjÀK SË;‹3Ê{3W]Ìh»p{)°ÂÛñì¸YŠœ¦htÊ(ClêEÁ*%• ,N¸€TãøNLAͦçV%LºÕS?5ˆCý‰?ñ'ª§>/åñ4S 4šÄ«€†ø¨‡s ¼sÝP¤õÅmŸõŸñÓ1wÅ´\3¾}TW$1Ú…M»M‘ ³²Ò¬÷ p‹!IþI›%³~Èû2‹ýàM´øÈ»6é‹“ªK[¿r¥œÊ뵦LŠ“Ý® «D¾}á _HúºÞÞ8J®@‘Œ2Üé¬Iðr”àDÆÝÝMCù@´qy ‚oÕÌêÊÂê#ŠÕ 1UÃRÕ_ަ‡ (S³Ôwt/ÌDZÔ¾xµ—ᦖ…t§^¼q*[ߺCÑj/³6½Ø[¼‰Ô»þnS°i›øÖ‹Õ”U0v(À¤áàš€R‰s:‚X€¸…?¡Àòè–5o]ŒÀWM ®kÍÂ) pÈÖX<tœë)1Šv°À:ô>µDq!b*®Ž§Íh»p{)°Â[óìò §Éõ«ÍgÊ.CÏÄh`‰Ò$fI¯lˆJxnµÆÚ§¸ø-O½Ç­ þ™³‚Ff…v¤I.Åò(ÓxÀ(üW_:l©ç02Œã9ÏyŽÊpááê9Я—ØåMû@‘ - k–ø‚ó)ÈY~dżwZeÔºkj(‚ÖM¤{„] ס˜°f7*ŒB|ãzv-ƒ8|DYV Ù¤ç5¤k¯ëȨ̈4Ù#ž}â'~"\5v­³&Á˃Ÿ5Š„$÷ºkämW§’çÐuy´Ó³D_·œK­ÿê#Ê£ÌøxæJ+>/yÉKä›9(Ë$SØñ¶ýÆ•b¹ùÜ´Óàaª^÷˜[²‚¯Œiôšùe $¯öŠv/sJa•½ÆóÚÒàÂûðþP`Ò(´L‚=A pH4Æ' ˆ8%kÐ ¸JâJÃ~WêM4ï,Cà gî0¸XÙµt3Hþµ±fG&Û±o||µ\2À§ÆFÎlšÎP»p«)°Â[óøÊù+T#MyÌiÊ[ÆÉc?þã?î~æ3Ü©~%Ï­~é—~i•°§£PkxažëëÊœèÈÚKŸÔn`ÜxµÕ>ý–¸£±iÀ m€c‹—½ìe3lþs(‰âLr6îÈ„ë.ç³"^“à†êWsÐ,:§`ô1ì+¤ˆ•˜"I Vè#„ ßÈ Y÷#fm“ñÜ_ƒåÌœ3‘5éqõ Åmû¹ŒúVƒ¶bÿÉŸüÉB\¦ÙõuGŸ1’'¥ ™S<çõ£MK·éf{‚7í5¯1–fq áS&bè_… iâØ`÷~@Ë2ºp¼ˆfah0—HÎûWAý€:L˜>hÔg@àP¾¤€3H0RÒá¨ûq'•À§J@´´g é µ ·š[ ¼5¯EØGÚgxæSxì>Õñ Àe\ª:;û#gllÙSö¤vêÐo¸s8Tõ£¬òu— «·§-W‡€Ô€ZØTeê¥v«7‡¢WΤf89µÝL Çí6y£q6»œŒçÊ Á.ä…éqÄÿe«oSlWĨËIXËQê­0±S|k¶7î0À˜²Å´Z-•h\²U€%i’ƃR#kÛ°Y¾l”_ØxÖÈý®àÍ }À¼âk#¿qÄ/'E•>Xíp¶öIRU“–¿^Ã2$Þáçx¯nm „·éqL­Û5§S õvíf¨”r7Â;V¿#«ŽÂðºÄ8¾ñ¿QÙY»œ5~g+÷+3^é­&Á=0À—’ :& †]h”ež0‚^øÑð)*(íSE;+ '³dײùÕª‡ž‘waSàÎSà2˜æ,Ú&&† †˜›SÀÒ?bHî zWZÕì{a9“‘æBÍÊ/5ö*6<ðê€ ¹é~ÁƒôÔ$H‘D,Yh»œ ÈÅ:✴ Œ«º_3™ÿóþ–5ûï}Ú§}ZÊ/}]ÈåF{fá{ †Wúˆ>pVB%eaÂYßüæ7ãº.Õc¶<Ç|<—ÃhW N¯rÉä‚'ËúÓuý®Ê…ñgS¿#çò <Ú…M;Lfxˆ5b)M"Ð7²Â9á¥Á*œdR|Z°¤D è½´ñW/À <«f~‡3?ÿùÏíÔóÂ`ÎúÂÎÖ\•Æ29ÛÖûRÌ#Aë‚íLå.Üv lð6=AZ¥¡ýC×y·¤×(Á¢„¡"íúû>ùðÄ0‚ÏüÌÏlØqêpýµÅv€‰@¢Ä»]êa5ê'šÅNÍ¡ÒKfÜ´§²8‘Ëz:Wß³.Š->м°R`—7î .CêƒeB~›\s-j• üjÿWs!»ÌPìšBŒÒÙYÅã§dp<„Ô÷}ß÷ñŠäb”Z‡—à5{£ÏÕ+¬&‘Ë’(tŠÄ‹ç¤·vQ<„\ôÀ(Áßþíß–pe`Ùÿã\&ž b!xôš×¼fì«¶c^·ê™ÙÂI‡C3½Õ`øh>¢s‰µ`—ê¨'+ßìzÊÓgjÀ½‡©"ŽÉ¼èE/"’l¯ œ1Ë%3£MV0ÃzÁ¦²ôMIÛ¸æY½üêλ°)p‡)º —¶vü ¼xÜødõ´Ö€"LU0)æp¢@©¨Äô· §Âs‚^€xöôuÎü ʇi§Nš¯FÝZÔÆ5a³Òk}dã}:ÞaëÙ]¾¥Øá-{p³… µ¯o³ƒÐýØ+¹šô7íÕ¦1­öña 5ï,^0’!lk r`-ÀŒœ;À´+5|ÿÒ_úKCÊø@ä|\»¨«ÐcA*¸˜Áq4‡º Ÿ‡Ø.íZ3à.l Ü+ \Ömʧ:»Í¿ù7ÿƪɉˆÀ6:]• uH=’ÒÙBN˜BÂäµ[ó…(«!_•í`—*³x3@Ç<V§cž¤Êñ MXç¨Æsyr/7™g§r‘îGÌã‹+¦q”ßµçØ‰aŽïêÙðt¨CÍYƒ¡ ›v67âv®!þa䙕­5A7nÛÉ ½\FåõyîsŸ;‰ u= ¡Ä๴yŽ@®û사–ÿþßÿûø9˃f>=3ø…—v®² ›wžÁ$«£?ž@÷ö´¦!pÈa¬e•Á%y´®Œhåõz``˜Ê`xÖ%æw\:Ô Ìüfƒ±.“ ( ñÃ. RvÖJ¯Æ2ÏY̵ÀÑ*÷ïÝ ÀoÙsÌîßÇ^–'»9C]ão)_÷ƒËX®­vÐmx°2¾Fññ€Òž=†RFÐÊý–¿4E¸Ã#_(9É`pÄQg»:Ý|ââk_ûÚg?ûÙ3Ç0 ø¦7a fã°N”7U³)Ô¥mîoÙÚÓÝø½ ÀMðº5ÈðÕ—›„ã› ”·®‰ÖÎ_ø ¡½æZG˜Æ•3šB²÷H Ÿ†uЍ)^‘µð¦ `ÃÔä ?æA: ¦ù/±Žšoš’zÒÞqŒÉ1çt˜ç÷ÿ÷$Íbt#á\pNe04½1¬5g·àF¦ÙÃ8~ʧ|Š¡pW[k\)Z›Ì“O>)áçúÔ BÄåØ)ûëA^g•ÿá.bÈ^­hÂ&É.m@Ád]i„ä¦×u½Ä.o Üy ´•¼¥Ñß@PàÉ®¤7ÞòÚK “øDE+ ¸±B}@—µfœ´N®r~ì’Oÿ›LêTÇè&e¨u=Ð)PT4]Û|z¼§¼^q!l³;j2×Yîüs¿7¸Â[öéà­Okrö™ 7ä.‰ÂÜÏØ)È[ê:JQ•ú|øÇËÙ‰º¡Ž Ì/ÅqËà)«òn‡¨FW¤¥ý”çê4ÖjpPlÝʵA›Ÿú©Ÿj¼¡À&‡s¡ äNÏŒ¼ ›÷„7ìì]œ “ú8I‚ ·GyP®9KÉšZÀ.Pã·ÕºŠúRZ‹ry¨TO6fHú/øa^)biÆqt5 Æ8\­¤æùK ïÖ$g#ÓyOú{yaYWÎóô*¦7^aÍ€»ax:ÈÔ…Á2Ì™7õ×&hÿ‡MùÖ›•ÓÁ›Þô¦ñ%iÌÉ%S³~uô:)õ„Q÷â°8߈’Ýô¢^3áÝfSàÎP\YWÐ f€+(÷oàÇ¡»‡¦=Æ ,ÅÖ|Àªin¥Œã; 2·,À¬¨œi¦0n@Ê{Á?P°DñNIO(6Âj÷)«ôÔT>kÁÑ;óìö ÀoßkPBȉn{Ma{ÍG>ò‘n‰ž¸•Ì>«Lñß¾4Fu”l¦A(D!L9‡õåDI/ºCì@¾û.Äs,//õÕSE7OŠd‡ HDiàÑ{‘ž~¡~ŸÉ³H:%à}0OoÅü=g_ÑuV»¼)p(¨X/ÖÎéŸÕäLª¦Yçµìw4Ê«nixÿ¥[¸r§A˜U¢ þ!x=‚¬¿^¡à©i{ ðƱcÅe%»ž¾œg§·+7î6¬‹S4˜l0Ša³ðµØ"râ!oÌÙÕ2a¡«ï}Î `0ÖštçpxÚ¹u]›¤«ƒ*GÔo¤å©åDP“k_Pà4ÕüÂYWx·ß„[}w[ ¼}/¯Qk¸©'V òûý¿ÿ÷—ïn¶¦É”7®íÅï¥{A®k^Yz½j0²Q/ ‰Á}R€¥i¶¡ªx¡!04ê1Šç g±@/~ñ‹k&9j#cp¢¢îç~n´V.¤êoÆ™‰k¸Ø\k6î°›‰Fþk'Áiò|”Ì€IŠÔQšr‹Ô" U(pküÕ_ýÕïýÞï%ÆZbd¤—½ìeÆY%FžBJë IM.? ìHLc’>4³&Á Q‚D©/ö`¸lo<‚±òY0¶Pzn,ÅýþÐýÐ8\É+Ã]ók¡ ÌV&fz§Uc|7õ@ƒ¡x¡IHZ{Ø»{¨›'þÏø ЈTÜ£Y?ïó>Å2Ø2Ad´Ä„GPg‘Öži4’úõŠž¥À®Ü¸óÀI€Í:¢s Ï6ƒ£0m°øIM¦ñdŒ“‚LÈ…oèÕPàЪiºâêÁ0l0 hiï»b]y€\ãô òßòw8ŸàP% XÐÑ!¾ýþÀ¨¦»5ÕûKþÜþ¢Qã.ýnðV>ÍüÄXùén¡=,Ãb®Ò;îdFpÕ¹I ¡µí—jªÛžâÌœ…jé3?Ì%s„0èüÊp:m8¯ä/P§l´*i2ާ̇a NHB¥[´õ_÷u_§œÏƒùÜÏý\‡¸ÏªÀVã>l½Ö.o Ü+ dÂBy'Üè’Ó1 $c2*Â0¯¤Ñúk¹qÄþóþÏ(ɦ”¼DÃM¶¹Ðòò)Rhk3ô8 üÔ$xÓ8ä46«‡õ %ØPx§5sû.gó†CŽð‹ËVäÒÜ‘JÔ}v&QSºF€¼`0”J>£%–èq˜ðÙëÞTé¡d|ð˜Á»>3àúbT‚žùý¯38Êôì/¢ñ–oz4»þžP89¬ ²V+šJÈxÀž­ëE")WO pjŸúÆ«V2é³ßûÞ÷:;²À–ŽFß‘•àá0–}²K¤ì;È A,ÁK Ӝũ\gµËwƒ[ ¼•Ï‘:ßÒ¥R*¢Oa·1*¥VõdQÝÄ 8î¶¹qÖ¸¤ •‡wà2ö;® ¨ ÝÒ¤O€ðr!蔨¡¼×X)õ2Ü­lW2&׆b° 6ØSrììÓß´ ÎÚ¬9€ný»)p)°Ê„ÿ,Š’ D rŽšoûÑËTyÖkÈ—þŸþÓúë¿þëWÒÐ’O…ð$¼²×¡“  ¥ƒ„ÌÓä/˜Ýç²üzRá|“`Ÿ‘Þæ‚S(IŒí¤à# ‰´™\Mà‚èÌðBáÔ`H– Ûq q÷8êîyL.}ö”\2Ãf38¤•;8¿‚Vç…IÕ~ÝÒàYÂîÊûCÉá“ùµˆÚz¢û‚7XÀ³6‡"T{wK SÈ-PUCLpk|`ñ¬ScoØ&õË9W€òÕe²Î¤ëW™‰RãŽM°t6F pÖ mœý{—(°Â[ù4Yê¬UÆ:ÀN¡xÂñå4ëßYMÝäxl~Á|A5Ìá :ûÃjt¤VO)ëêš)vƒå•=¹ãw$ÉbNà? -üј³s}©a:Ð>„+MX—V¯@I?3ÖöêW¿úV>°=éMß ŒL˜q†ƒÐjÈ·p \hE[¼ý…øSW ~(ñÃÚÿ†oø†–!gT|ãʻё;%?¥<¸!µ®™Å~þçþÊqͤ§9T -£ÿ ñkìx®bÂâTá¥doÀË%®ñ=Ìó¦Câ±Ý¨G’@^Îà×d*¿ímo[§±Ë›÷Š#f¢‘‰¤Û—¥žX5AÖ 7È1òy¢2Ö%H+÷P¤{¨B2äŸð ŸÐ²åq$”.“ ½³òkRÉeñуoò µ…úŸúSª‹få[C¯¿_^¸öQlZùÄi…+}D/_g+>‡…оÕèÐ… Ñîr÷G<P¢.êMP² ÖlüHq]ò°÷JžØáÌõ‚ ½Š§ÃîšM{B(«µ°þZÚiëTZ2 M ”@Àö8lVÎr€ø¤.µ®@¬‘ú¾ã;¾c¥0`6Ø) íŠÀÞt,0Xã."Î  ãÜHY—òH5Ž/HxoFÛ…»A-ÞÖç(M¼œà—~é—º½èE­j‰ƒ‹cçhŒŸzÑ&iæ¶ù¸4ˆ¢©/§…Kà *h9½8_[ÜYœ"SFãü$lh 8ൠ¨´ïòX4H ÞøÆ7©ã¼N)E»v˜6Žë|vySàžP`dBKÃw]¶·qtœÅb™”¼M&wÔ§ú§× ]L 9àËCí!…€‘ÌGÈÁm¬âƒÃ°Éõ(ìB~»Çxa†«)ìb;Áno¡Ì>8|^ç¦SD£oÃ"G©GðÌ\_wã`ÅÍq¡š<Ò! 2žE]Ï0hÐd ;_ »+ý˜³¨×LÐp&8þ‹ñ/ú‚Hh±¥Á¡Ì.ÜC `Y±¯VJ¿Vq–4‡`Œí|@åà ¨ð´¸&“'P4 €% 1ón¥”1‡Ä°.aƒßûªµ¯Mr‚vù¬®.nc9ƒ…Àa-ƒ‹¦1›–a=\ S›'ðyŸø}¸å-ÞÖ§\ô %Ji=*v¢…:Šg定ÀíT1~E³tçm=ü‰Ÿø‰TYõ• 0!ßTãTï÷àf&&G%}XÍüJI„e‹hOB]ò1ÐRÚâ!w> vTÃïÆ¹+yqÒåÆÐEPžQÊóǹâ²™Æ.l ÜF €ã³¬Zð‡ÀxÂæQsèƒKé Êg9Ä+2LYª/|á u„iúæRã¦B ùÔ$(½ÍY©¦«Ðþä=Å÷Õ–ñ¼ôM ^õªW¥xrGîýë¿þë'ºæ¦.¬_}DñÃL噴æ>ÚF8xòÈð Nï¡ë’ô`0|†Aƒîš|ö³ŸÝ;ãwRéÀ²Ñ¤O†S‡0¡QClOѾ<»Áݦ8>ð³”p¥CŒ™¬ *²-dŸ¡Ïl†Ÿ‡¯Ú{ˆ¥f›AZ›RO]ÓWA—™€Æž8–Èu ´ð”½ú‚aBÜ,/­­ýrˆN².@&®Õ€£j\g²Ëw€[ ¼­ÑúÌ’Fl›{ w­Z´~¥™¢ÑWÀžò˜SxD½rWР¤€ŒIhÙáËé¢0nŸõŰb<ÚÿìŸý³µ|Å+^ñÝßýÝÊ\ ‚ÀSéCUbRõÅ.©Í°!©/Œ3‰"Ê&ú¡}hdWŠ®P”îã57RvaSà>P@/ó¸:Cß#7ûSAâi© îµ”$0àõ’[<۱픘OH![ïÊÕ$Èï‘týœv<­Á¸à³±Ái˳5D¬ñ‰%†Ò0·vУŸí~¶RGÝC?~ÇGTFyY‘Î&‰ÉÛg9TcìðD½lE:\½m‡È.í‘=rÐ `À§oâw·,Ãló3±œcÁÊõ•ØÒàáîÃ{KÀ#æ`µ¢@@%±Êj`Ðg²‚7@¨£.¥Õ`e}é˜,º|G(£u ÈáðÊà–qVʇßjæ`‹Ãc2éþT‚viØ¿ö¿ŽÀa |&Öܤ‡õ"«Áα@¬3Ùå;@-Þ⇘ضº¸™‘¬,õ¯ýÚ¯U“ÊGÜÎø”;Å…½ìUãEF­wðÛÈüÐRVM½‚Hè±.rR 'ad\×bˆ:æ†Tì¢fÎŽDªï{ÉK^¢/I²“ÀFòíy2¤õ÷Ëçará©ÐtÜ›÷–7ÁtŨNd+éû-ɧUãï“?ù“+ÐþZ¡-(V£U¤¼†¤oxÃÖ¸2kŸuë²Ið¦a”‰ßÑUÝÔ¸zÎW£„ÊjGm¿zR¨á.rzöyDe]”‡Ð&Müé8k ˜GõP°Ù+5YxàÁ`è©!8²¯ƒ__ö¶4¸´ßñyÃÆCÏ7Dýš§ç¦×ìúKï–›w†9@YŒE,%eà èbñN*ÀÆ¡Û_ýý¿ÿ÷•A Õ@}±|àSQ.Ñþï©¢Z§Q +?_̼_€­Ž­ô*Ú{ú~%qè,X*ŠÎÕû&»V¿ù¦y­Üå»D-Þâ§ùiŸöi-ÕÔzòÎz.ÙÌ—~é—ªákD/¥0Óáá0; ޹6ñ §þå¿ü—3Úd¢“a4ÜøZnåï~÷»MCî©<Ùâ•¶ÆéQa }Fæ’þ¿ñ™'eÖH³v²¦±~òÉ'ƒ,4U“Ù|&ªGÈÐzﻼ)pß(pXo)…QÊBL,Lè²| ZƒD | ¦$ºLCØ…$¹š[’£ý¹Üý¦³¤‘Ñvñq:£³±šñ8À|˜ËÓ–a2æe«lsvÀ*WÑ {Í×øï|çöð¹úÿàxg·Wd^‚vô:Ûær%ÂÇCjÏñ=‚‘—»wö¿ü—ÿ’sÚ8ƒÍ˜;[Ÿ´ëv&7½`×\t·Ù¸c˜(åÁQ–$pÒW€Ð€Qïodª$=€Ë~wE+FjÓ×à“`œl‰8ž¡ðºáŠöôž5¿¶F^E¾vN|)ë5î*.:=¬’Ç420¹>¾ð›ïµr—ï¶@x‹Ÿæ|¿?ðÌmÀU£÷m™’P/™û#[ð~Çç!ÅÒœ‚ËvUãòÔû“[9DòþÑ?Ò`¼èég£Bõ6¤ž¹?Ý%Œ€7%¾rŽÊ±M>ÀTÔ:²cдqêê,„ͰÃx™ògögÏø»°)p)p²¬Êìp¿EŠ2’‚´|F*¯À©½-º N› §Q‚XDëg,ž!ý%Ó ZÉóS?õS‡ÑlS1jõ¬së>¨kcÖNS·Ošõ ¤Q¯4–#šºÖK_úÒÿþßÿûLÃs!YEðç<ç9©uš].LÐ "#õåÛ†hÔóÅB㜽,«¦'s Æ[Nü1¼< uöÕºé*»~SànS \\–ÌÀe®ñyº³xÜ5}?ó‚ eOJÙ5‡Ó—ê¤Ø 6î*À ÌÇße€¤Õ¤ ¨€+™æªœ}}À @¼à þ‚@£FG40©‘ñ•c€UFSi=‚[)nŒ.%>±*¶¸ýS×xֳ⫈ˆŒÎ=(‡.QÆã±m‚‘ó(}ÆçÜú]¸cØá-~ ÔØmhåÓì~Ý×}]Ãt¬sö:wï(\˜~2æM"™§¹ÇS?kʇ«ÊŠóU6F|M‰€³'žxÒX’ÕšB¨“ é¥DH2ø”j‰»QD¥Wk2j ·º—‰«h°Uæ«Õ=C÷aÁÊýé²^z—7îNá;K—Õë·Ì…·¼å- WU±%LD.±p%$ Þõ®w©95 í¦(Aªî‰+m”÷LèOp%Ǹ “dZò¬pB\.׿zŠçR…‰[~ è/'‡ÈÚ<“Q™ [{àkƒu§ÁÕ†0m°SÂãx üyâ#¥×’Å c }¨gc!oîZ~Q9fÃ…¬… ¢ïéë4—Þ…MûF0‰…sŠ=B  ’–-è2˜o4 ê€=ÀO5mïn-Hñd«õÀ<Á*}I}€^­ z`iÒA² VtjlpNÍ@>“Õ ADÏ\Œ]¨#Õ€”€ådp8¯ôž¸ooÈݸß-Þîç(åTœ%>ÅŒOý±õ±þÝ¡%­¦ì2T×#1æÑÄ`|Þ#€½üå/×±?à†oU®G¶¨…²tJÄ3\}ðƒ\ *8ðwz? Ú¬<ïyÏS‰‹á†ÔãAÙ3u7ræÊ&ãÖ≠ËÙ™ðŒÿÊW¾r½ô.o Ü7 @¼åfUúS°SB ½¨X²ÙX¿Å±à'ñ¨…p¨oë‹°±šo"¬ì)ÅÀXªøÌMÍ®¬çÂ4fbó«~ô£Wö=4;xdþÆ¡ôzÑð§‡24ÌœñC >ÔNƒîMÃ×¾öµ1L[Œð­›°Jò¶'ˆ٧ÁGÄ|ê]yúm9¼H§·¹k6î@‹,“ –R~× J5ÐÀL®©ln€ìI„„ÐÐúMÓ€«mÐY€ q¹’ÊhtSÆ@2À¬¾~°áÆy^*ÂhœØ@|F—`!>àr‰‹1 –C`²à‚n —8ä8½?ïÀ=¹Ó-Þú'´4¾ dµKˆçóÃÌ"´UèlØE‚Áq‡Péo´D oUÒ(êÛ¿ýÛÒÙG8)ޒоäK¾d%(”™V,Í–‰ÙW‡LcL‡/eÿøåÖF®3¬C– ôcF¥´q3·S™ðtk¯u2»¼)pç)p€ò¹ÜxJnð%ËáêGd­e‚Nà€i,Ò &Á³Ä¤c–×Ô€ú>rH!ç ÙÃ%cm“Dá ;?; •d³Ña™¡?‡c0¼ÐñšSÔü‚ñⱬš2 65LpÙ`¯g‚‘1ÿŽkz é©pì§ïø©Âp>#)ÚP˜4˜Ñã>¼B×\}·Ù¸«*fáTi tNp%¾:©’Ašéê<`νàf³Í`,Xš3Ñ Ò=¨hͰ-g`L³ÙJTK鸺ÜÀ¤¼vÞ‚‚ÎÆ²ònüÉêFºGðRAîêSÞ÷¶@xëßNAÖj©ŸXäèòö‡ïøØS«‹lC"e9 ¤Á -Þãi0ñ‡0»–9¤NF,Ú)Cá}¸I´3þ¤±rÅòb Yy©Õw?‰Ú…©x£HÞå$Š˜Ô åhæéÄ-¾É°&Íjú[`ô\}6îV@ŸêO 0Xh­S›Ô—á`]ì1X'¯'뫤çEC×"„¤ÇyØB„_ø…_ØJ§äfÚúÈG>ò¶·½­¸6=~S5™µñê#ëX=H×–[6òH\ÏþóÓ£KÃ@¬Í‚t_öe_vp 8\妠ÁC³Ë‡Yƒö¡$oM%ï‰ÿܾGÜ3BR¬‡&ž_/Òåkí³›w›ñÏS˜¡EbV  ®¨]" 03XÈIØ+ÞšmÿÜŸûsÅK+=A©7&Ö=þ —•ëŠ!ë«¿°9œ ‚Þ„Æ‘°,P0`(ÑøNÍuÇÊ^:5×éíòÝ£Àoý3¥E†œ,ãÜYØ£¶†ýeÈå€úªÎm¬Û¿ð ¿P”ÍšUÏÄ㨾_õU_å”­WÚIM‘ªÉdV© ÔÈoA¢?à2]÷÷üµhÈ„«U¼‰²-QxJÄ,»àp=z2á×Ýc]Åv ÏÜWm½¯]Þ¸u™P’ô<‚Ä›¹ ZâdB‹E‚™•¨Æ$HN:HG~åÖy+¡6¤¾œ?BWÄIØ3A–ÇãÄûATk2Ïis¡À±ê[¿õ[ÇGÔþ]þæÐ) .t¿| íyë[ßzhŒ›=÷¹Ïí¾p*÷8{ö¬-Ý~š~q˜gƒׯÊø©hpZ¹y¼Q"{{ÓÛtÞ³îTžö^5^•²êoið…÷©{B@"€aiøk½Tc½!ùF9ÅuD)ÉÐ’ÞÇÂË!Ñíqž"1: èU¯z•J0 XÚQ¹}jSÞ¯9zi†U3®à*WÀÈs*ýx`^e¿Ø~}³g~Æg|†Ã|¦{ÄLÔc¶à¥É`õÚ¿w•[ ¼ O6ðßùn†šÏ‚†—ad?ôC?D?m©gåÓ È›•ÿš×¼†*«C1įªi†)D¬cC¿ù›¿¹’¯L÷µ_Z1AhIeôëF…k&+Ó¨/v™.JÂ5`+ Î6Œ¼¿Ê`Ñmæ’Zwó|Ç;Þ±Nl—7îF& ”ó¢É*Á#õÍp‰ÖÎÁå›dÚ ƒMôë) ]RÈ; oa\_þå_þk¿ökg¯"À&Ñ »^]½æ]}D±”áu¶‹=eÜ8Õ‹º`RKÜí ‡DDn±s48½N ØxE¬?û³?«|§Åi÷|]º ®JXß ^5½*[<%쮹o!Râ´RqKÀü'°'QJà ´ IwÀf¨—SF¼·mñÕòór|u¹Ò}ö€i +ž<›VÒ —l0ÆDVä—¥±?ФüÊÓ÷é3Oý¤12BaBmŒ³Rp2Rh ´ý«õ¯:›ëBqJeÔ°œf„æ\½X&. )Ò^ò’—hƒ=; 82ZíC3•Ý—\ð3ì.l ÜC ŒLhQ7œ Á/Âë¬^F–üü‡ÿðR„[›ÞÓå,öüT ¡k‡§@ðûНøŠ¼@šoyÕm‡8Õ¤Î#¾:‡ë! 6vEyD¹ž®g+«œŒêåAÊD›%¦º2±ÓKL»Ãl»_áPúË¿üËȤC(äšÆ[ðdóÅøÞÎ×ugüŒ%ú„¹>w¬15Óqê·4ø°dßí Qkg… à1A.IRˆ¢±5´€.³è@š¡Ïæ8 A¥b+¢w­È¿mÕt¯P:(Ý;ÉæZ¶rà\•Vz&†ÿêžl9îWõ5ç”ïÀ¤f€¥A€Ìºìß;L-Þ…‡KiNù³7W[½ .:‡±qd·°CE˜šr@Ä–8pIŒ»)•ÊVß/¯ƒ¼ä*T#°çàKF-­ž m$7 µxºÛcZ=N7^XRâœiƒV=ÛÝdÛ  ËáËd’Ú¼ÿýï׆~[iM·ˆíaj²=*ø£Ÿ«Ë]xÀû6‰«Lh%2YJ-Y8VbÛdUOœ8lñÇÅ1ɇ£Ñå(¸³sÄ1¾ñ¿±ËQ3‡Ÿˆj@UèÄb—Íœ íl÷ÓJRZçOn3½Ÿù™Ÿ9msð…ºµÓfÕ8¥A}¿ô ÅôðÉx”ì,‡moºÊÔƒ¥™Y'Ê#Ñê06¯,üê¯þjÜpûá¸^ï{ßûFÒk£×|.zÐ~½ ^‰r:ÜÒà•ÔÞÍî0À†1îYÙýZ2˜ÈxtЍ‹¥¨€+@‹³ÖÆÀLñ/ÈäÄ{1®I% %þH`Ò¬Dð)¨ìüCmœ§hjL%ޱ:‚6«m€\Ò£z¯ƒ|àŸî `]üƒ‹ ch€IßÓöwp›)íÂ]¢ÀïÈÓ̶&Ïx[Â7¼¬ó`‡(þ -~k[ü X6ŒCýè®Ä7Ãjµ,­Kåùµ*æ‚jE›` & š€­ur´ ’Æ@Gƒ”ò®.œ¦ryǤf£Â¸-¯¶ü(°¿xO§“ßÑèË’·jãH˜sʵøM ×9Lxn Üa Û‚¼%lù“—w¾ó™ªþàüƒöýË?bÛ ÂH>åS>ÅV¥p”ÃÙk'¤Ð %þ¥ ²ðe®ÌtÍ8Ó†–ÿ•AØ<ÇyAƒ›|D§ïÙ¾w)Ñ« ®û²—½ì‘ƒù¤¡|ů‡rHÇuv’g+Å/eÄX­¸|؆z¸:2?ö9ÀT¹´­Úð’\py={Ý]¹)pÇ(0L>-‹pŠSµNÙÜJÌëÐâ@’6 ’´†Iæ–Ì f³Í ¸j¾À!CFRf:IcðI=Ùò@ÞÔý —z0,L¥åú7° Cç:õ=ßó=Óðÿ€@ß‚*Ý‹;UŽ?€ŽM˜)Õ¯vÎÔöá]¢ÀïÈÓvlÝâùSýÄOü„ Ã¥ûI?¤MìÁšTÈÃDÊ:§èò;Í?ö?^ GbdrN¨]ƒ#ej'LDã"U^f-?ïó>ϯøµÑá’ ý¬mTµÄéé3º\C”au¤bÏÕ¡ p…šA£}UbÍ£/Ÿ‹î¦À½¢€ ”ÌGVÇj6ŸÕ'Ž8›9ÇÂ9¤1@. 3.²ÜJ¯$&ùˆ@$Oþ™kÝ•ƒ¬ÍèËA®˜™‡A z è:Âiù²éë_ÿú”\Té)éOG¸¦f‚±el6†ì¶¶'Ý]3´1árŠ~îç~nÖ×NIÐWôX=\‚kn`È…Jµñ2$Iz=&‡áŒ¼ ›÷Š Bjè ÑêèÌHåü˜8çlt Jú5 %?m0Ïä½n38A4 ¨‚F†–@&À)e½¥} ¡7 Öå¡öÛΖ¹¾åŒQëì_Á¿µKà0 4ê@jLÖñôsp˜Ø>¼ØáÝxŽÿ×ç’óíOšÂVܘVVu8 öáQîOþÉ?9ì ]òUeª÷@ONçÓRA*q;™d®ˆðv8±,v]ê©Ç$Ñ ñ¤lûú¯ÿúµ}J¬´Q¡”\®…'J€QB­æàr½èžxÅÝò¯¨>ž¢®CÐMf…õŠ»¼)pß(0¾£·ÀY_r¦·¦Z5ÀAnE+¡ö„Ð3yÃÞ°žº\vuiÖG‰c<ä4¤ðò 7}÷»ß܉Ëñø¢á¾à#zÓPÕŸõ åÐU0¤ÉóìZµZ—G;={4ÈK?ô‘ÂÇMv<­á{–﹬¶Ê²†ö(=VLx¼ÑXç¦Wâô*»fSàÎSHÈa²UB¬še#!ê /Hf‹±S %qŒ‘'¦œí-mPg%# ¤K þ“29Nꨵ±rq7.צAX(0vH ªc° „ä®oµvÒ&¶PlŸ!R=0‰½¸å­-:<‹»z¸»ód'×<gmU6ˆ+Ç0~ð”L#D,3‘"?ðg„ù£|B/y&>é“>i*)ÀD¤sÖh\Ñ:Åcìx¸Ïjµ}4Æ›Jˆ¯üò—¿ûÙ‡›÷„€ÁˆV 3Z±6Ê'”ƒ˜$àe&p’+>¸²æ5¦$@ÎЛm㢖çÊB1.ןÖJeK½?  ôÀ­°šNùØ’Q1+@NM n(~úfŸÃZÝ)¸8×XXÎÌwánS` „wçùâS-û•õØpFe»Ñ•†¸À¿<ÝÍl¨!ޝB¡4þ³_ÉWzq£çLìcMŠ`[]ø§­ªk}m·:Ò[ô"O6Š9Š“–鸼^É¢£“_~¸6Ú°trF°l¤3ÃŽJLœt°µS\ç×›õFvySàžPàT “žc¦°A9V5¸Jq+‰~ìÇ~,$&¯5¡‹6œ¦ø‘bÏÿüÏ?»Œ\2%¦"qáKëà×”yHâ`©±ýÚ`틚 Îuel†žN6oH„ÃFÂy.䢫sæ5SÕÆ ¦½rË×$Ñõ÷ŠW¼bœ8dê¢ÑïZžW\ÝÓ™ôñ3üÍâxßò-ß¾LÄÌÍüô˜¾»°)pß(”¦%c¥”^Åá e£õ›üHÏâX2›ßäµt!ÔAD MÜÈA[—s½¾àÐJpÂLêÁ§©7ÈÈfàÖÄ.Ö üϺôG4 ³pEîŒwU¡š¥B*_ê6ƒˆË\wl#Ï©]¸«Øáz²ý¹zÎ]å½Y¨4ÝÎÆáPÇßû{/&Âôü‘?ò1Žò´.yôÓëe(3r)Õ88W@“ ]G"އ.³/™Žd¿6‘ç+åÐæÎ Åžp•<î*ï|•P&Ü©^‡Œ™8u ÍápÉi¯Òçx=Ìmn Üy œŠ ³¨“XÖL뀂|•gi"«J ÉFU-Fì…'d•–]u~Mg»«œ•Ž#=THáå<¢¼:›…·ô}7]ýB=df‡†$^t‰^VRÑ.t?œ: <4¸éUe%ÿO¦Åöz…óX/(ÆmÅüæ–¿îu¯;}ô‡öá¦Àý¡0< >ö3ñ5!€ Ђÿ¶ÀâX•àGŒ1©å¢Y=Ó6ƒ ðFGP'ÖŠÝ©”æ÷@j`I=à”Ê ”¨ZÑA¬Ó ÏÃl»î6®u©Þ}•lÆ!àþ™þ†cççD|Ë[Þ2S*bHǩم;O-Þ©G»‘Œxî äâ·0¬Dˆb=“Žï+190ñ;ÞGÕ8;#O!j¶8v–¾ÿ5¯y^”[Ó²‚ "ó1«”a“¨$™Ž“BÇV¤È\TtS}ñ1fjdš-CÉÇ·ƒ3Þõ®w墦Í0Ên» zÜ-Û‡›÷„Á€ÑɃHŠ¡Ìb­¿…òž¥ G©|ÈHVñˆ.æÏü™Ëño3Ñk )œ}J§Á¡@0û‚/ø‚æFT;MÄW{ÍÌ¡f´ûØàaœ ‡E ßûНøŠ€]íפ¦á*ÆqÊí\¼ÜwÎmòÝO‚V7'ÕG @IDAT…§:¯bk㯡ÍlÕw:d:p_±\¿^€6î!¬—¤µ¸D˪2Åw0œ*Ú” Ì6@ޱjD:%ìD­6Û 3 Þ`iõÉ u {–@ÀI½¾©ÔÁªl˜‡œ4õí¢s ±9t6üàˆ ø%y‚‚ ´’­|2LR&¹ï0¶@x§n¬ KZ3ì¥ÜŠGØE‡o•2T‰Ì£XZkLTÀÂaôaC¸¸ÆA>y™éÒ¨«s=oº4štX`q4Øn¢±§™4YM·¶;:Å)_ßÔZ~q4üW Æ'P[jÄ8 6:bðX»Hîô.梻°)p(p 3 £)oÉ\Øg‚ä¤+ÐXÈÊÙ˜·Ë$½&¤Öüà#šý¦‘±‹oÿöo¸¯<nj<õ²­¦`»ڼgNUÀZ±ÐqU½àAÊIìš ÁÃø7¢óšv<"NQOvL€ÄcscðÜÒàM´Ýõ÷Àè•bqÀ†TÛ€8T„(À `c€‡^ ºá3“ÞÉÙ5":°)NÔxÊ hŽÂv§üž\ «RôŸâ(wÑäž `«>VåDÊ&øbƒÍ¶ŠÚÀ~3%\.uD»ö—ÿò_Ö…GÙ¡Ë>ðÚ2\ŒC<~RQ“}o|Dõr3 *»úüöoÿv—;Æßý»7k'ÇË8G8l4¯%^&¶7îV™ÐÊ L@ É-œÖ ‘OxÞ2Lgßömß–Ò§fØË#ïÎRȶ }VQ6·šæfªXͧ~ê§šö­š²iS5¯Ø<¹„±¬‡&0ÁŠg=HÝBN nÊ­­}¡lÃÆrâ“î¤ê Ü­åÛV==°/Wyì·Xn}£í¶ >ñw—;FŸ~ ÅÒï ²ã€„–yT`F@(‚mŽ'‚³ÿößþÛ¡U¤ MZz Àö¨Á—¦e…ôÖl(ˆ¥ªôÝ.§|èŒ5¥gÎïÀ¹^`ðÿ´SlÕPl| +ˆ¥þ´{‡IîÃ[D-Þ¢‡õà©ò‚ÍoØõÏáAXs5qibéÎmKålÀC{±Ž)ÈÜx¨œ=¼Éƒô‘ƒÏ^ÅÜbn2 þÑeüœiX³»‹]'.âö$ö2ddïÝÒàYòîÊûFŸþS8€„² ƒX})•07#Þ„`z©Ã!kîØ–oB@.P37“Qð  .“\pæ :†¸€É¹Ö.Ü lð>åÒ s"úš¯ùšYÿV>€_Àé½ÔÈD7Îâó/ÊcžÂ,•¹z1—ÊØå©çUÛÚÐ寅`þÎßù;õúÃøsÁ_ÏæÈn—ž©Ô~òwÉ’,í{§äO7í¾töán±é׿þõvî* DŽ4^°)œW`dºsÝ™8jÐ|úý¦oú¦¹ú.l ÜO ŒLXR Æò‘”²5B뜘{ŒUÊüÃéHG­2À둽²kå#zá ÊÚ'ILcÂI¢ƒ@´¸À„[ž²µ £­§VRã›üYâ¬]®)¿á o@s~É—|‰'r¶ËÔ­á´æ£ýø}mið,évå}£€Ï}kd~1«ñf *`Ö‘é,n`€À†Ž€G^£#@ÒnX JÄZfƒ`fUâ´!!س’(²`›°´¶×,çàjí¢ŒSas+<Û²väuZj£ÈW/ ”Â^Æ< \Äó?#݇wž[ ¼ƒ8®—7&¨7~ä|ªë˜BÑ}~ÒCL#@âzù€Õrê;¤kË@äpTa‘¼ê4 mô¸ü¡}hº`I¸ðA5–@£á³¸3]x*4ŽRö4KR òÙÝ„}¿øÅ/nn~%ås SJhœ­xlÃ:òá4Nx˜ó>ܸW™Ð¢³4 Ýþ`Žu[èò^Ę´6Ï}îsÁ¦·úˆš áêÀjžásáèÕ6 ¤Ü]Î]oûÛŸá°&¹ ±œy&câuQù o§T@¨4¾±5¾µÃu~=Ü›FØõ›÷„ÃÍZ#~Μ°A@ˆiZ`Æê J²’&Ô"Õ 8Qp%þ©²Ýž‡¶@ŽËaŒ¯Zsø­ÛG× ”2X5]ªÇjfçØ&¹r°-ØãØÔuk+Øs*¸ò @1F‘_ë֕ϳ»?…-ÞÁg-²O—‹¹½‚¡WA¼KÓOï¥A;¥rKPƒ&²òÄasFNi­ð9Ÿó9ÿñ?þÇ¡àsžó•4LSS+….ÀMn¥¼^õªW…ó1cX»ˆ™Y¯Â-—ì*©¾ðå‰5R㎼î²Æû\+¿‹²Á¾ÜÞÔÛ×kæ_w¿O<ñÄ:]Þ¸‡™ÐŠà$«:©õB«ò@âHK ‰Òw’˜Ñðˆ*pã×R-ÛHÄð8ÍžyAþÏqÚ3ú™ kz&‰>&lÚs nçDb–Õp*~Å)ô¦‰É;+›h%ŒŽ1a ƒæãî¶4xÿ_öîöiß®¬ë¿ó›þ„4eNj†JM'3GP“'µÆP(±.ñ. DT¹Q LîÁÐ{m0›,Rsr0‡ššñ¿×u½asµÇy~¿×â<Öñàø®cík­}íÛùÝ·ÏgmkÛ¶µë/G/xÁ R_óíݱ(òŠ©A² ! d‚²–ÄâïþÝ¿«²OãÛˆÅ礠îˆÛ*omyÍô$, Ñ*q2ó¼ÇžëIƒ¦š—,µÿ#+•¾fJë%‹ðI£õ3?ó3륵쾉Ì匔ÚJ•Ž¢ÀB§þë »¼%p î þ.Õõ±/«)4 JÄ ¥‡*ÚÂQ?¦¢Ž"Ô™­'^¡e;ih rÒz])é(³ ½q/T‡f@{Ÿæu‚´¶WF¢z¯S¶(¢ûB½Úÿï§¡špc¢mó°-JÓr¥¥iÕ:í#„‘CDÑ})+ãÈa>ûç%H`/oæ_ùiO{š·:—w¥†ÛÀMã'½àÔW¾ò•Ê”H|kLGìdêŸlQ4óûSf*ª“ÆDer»?MÓÇ_½(DdEþ®’Ǹ#£Ô£ýh·àÇ¿þ%(b#Ów¿ök¿V>Rm8µ«1ΜèªÙÌV¢ˆØ˜[¤u¡ôElç7σêùÍNìý¤I×9ìò–ÀJÀšpÞ)¯†õŒýó(©¬*¢S±.g%B¯7Ý~ã!ÂÚI;ÑÙ<¢Vk9A ÝжŽp›e!4Œî±%;9b±ÊG†<)mƽê6‡ª™ÉMdz§KJÔj p·éAjy‘Ù_½fIÑM²ûb Í_ʳšmÞ7x·þ”»ñM•X?`= 0s(A¡}Hª€0x•|Ô—çÓ»†ZT餸ñÞDB ‹”²ADE²P5¨ËAM¦ˆê <éŠr ¤È8‡¨‹r>#Thr•â525…z¥µòÌonó=´7R×%4Ù[E .94%í3 #7õÿÃ~®k$°„×çSømE p%â÷EkÐALÅ´‰OÃ95¥)ÚëS]ã™'ï5”³AÍú6vèÛÓXªâaT¸c§Æã"§²Ë–ß‚\…Σ É[#½öÞ÷¾wí•¿„Üz*é¸lÞæ¯Ë~áNK©Õӕꑤ,âëé…Nʶ'Ð:–7EêXc.þ¾×]¹ž#4·Ø…-‹’€—Èæ’W¥hsOæƒÞ¯v”âoþÍ¿y;2ùð‡?œMšµûàC¥ûÁG”ãåU–Ö“YñÏžRx;3ámUú:ÄVÅX/„ûVºÈŽè;ÞñŽÛMJé–' zu›¤6H•œ!Ÿá5ÓÊ¢?Ç8ô¶Ã°Wƒ×m_º@ ôu Ô[3¾È@q1è’€*ÔÀ7 A\4áX^ÐŒÕõ Ñ,B‚œÐÚwè×!j±q#$§¸ªù銜Ro¨vªñ›@´Ð-—P/FÃf¶ÙÌL' u]šçg 4Ù iÅüÏÇæîvûs9Ø Âû·fÍòæ¿üå/÷­(ÕÆÓrVP.1Þûî#a:z4‰ þÁ?ø¿ÓÕÙ^kËŽÃÈ}íd¯ŠZçý´¡Ãò;U¥˜YLºÄ6þàþ 0¤T³,ê-)›Ìz"W¥o›-k;k¨¥¦Ç/&+mï”úÓXýt¬@9òèX§±Ë[(ӥŗù—÷ŽL=˪ۑ “MÎÞ¼×m4;`ÃuÆGôš'rû[!…tšc0R_¬à§g¢º©å¢3'< ÿSŸúÔ«Ö¥3½ Mt6#ü´T¸R{•m'Rȇí…u(åax´™Ùš³ŽêOÿd‡Žûç–ÀEI”ô´Ö|·à‰ ¹e¢‘Cg{ÑÊ"SßÕT„~Ì€h rB°™¡‘oâ*çܦjð =]E„b8S¸«è“öQ)ä Åš1;(b<§†˜i‰°Íæ'"Gõu Nw_Ɉ ªG ÜãGQǹ×.\”ö‚ðÆþ¹å¦ó¶ã+¾K0ÕiôW^šéºo4ˆFôñš  ­ kæ{RWe‚¢I×,í(”㘧 ’–\å›ýÌ>k%‡´,gnÁ]!ËY :wuÝ‹àþѦ%…þ#?ò#3Nyq2ØGfÚ¶]Ž¢ƒ³¹\¢‘›FÚvÝ)uÕ$Hd:#ï–ÀeJà°À°ªÉqÔ;2¤ŠG÷í‡×Ó#ñ-`¼ï«¨5á5¾‘‡Áñ› )”L/÷C›ÃOÆïIåg×8…Ò`ŽÊh?XÆøÃh~ºié—µ4S:ms¶æR!F2êׄÏv¯ÒNi*‹ŒMÒl¿ó;¿søc]3¾´%p â­îîõzw‚~4 {4bк1ª€6 £¥_Öó<›’âÑzYÃLÚÞGZF¼ÈL˜ÆAr¹ë:ÉÐ4¦}Ä)k¾^ZµÚ§®±ü?{¬!]~"l³ˆ•2'Ÿˆš¡yYÉû‰faG Ý7ßÑèâ5iSÝ?o˜ö‚ð†ýAÿäq½ù鎱0M©¡˜ÞúÖ·¦|3>î›Ê)´ªìçì+J|—î+jåOny—•º\£ºXžIí5®Yš½ýíoWÏuáÀäèßVk®2ásÞhL{ %ˆgÊš»pB›™Èaˆ\ºô‘|¤Ã²¹Å(^7]Ÿš3x¶´;l ~êR0æĹï.l \šË 93½D‘¡‡‚ ïò5"jsÞ½¡ÈÙ5>¢×Œ3!…¼ª®ßLC×R€¾‡ ^5¾eé¤+P¥ÃzÏíråBÂÖÝΫF;Ô#v§¤ö1²dñXÅtè²þ$ê Jã×÷|Ï÷þLk—]Þ¸@ €ïÙ^£púô>€¨Ÿí5$U ÜІÂù‘–pµ@0ˆÙXOoGEF¶í¶i©Afº5zƒäLcïr>œHÑÔ#K(Ó¼Ú¨”W{®V@ºŒ‰€¡a >tÈÏ•°uu¾Ñ¼qãR‰fˆoDz9i˳>‡iìŸ7R{Ax#ÿ¬w>”ôÁé>/ÿ)“*“šÈ`VJÏŒRŒèžk¯Wå‰'ÖŸòb¬3† bî¬Õ´Ãú=&7GÙà[©¡õÂ%Œ,":ŸñŸÑP¶|ZO “Û)ûyÆ3žá’.-ÕØ½^ò’—PµqÐÕÃòƝ'œ/W. ýDõŒë|ë[æ@>`c!Ó1 ÈñØÇ>v}Ì)c]“Rlm¶.Mëš0³K)šZz_ny2áê#j Tw™÷n'UéUÒ¾&¤#k¶yo÷é.ßUê…ñ´|ÅñE®«²[ †:û“&õÅ€uz ١ל Û4¾?Ç^ µ^²œö>æ’ò €¾…"èŸW%@ Ú-S8ò`£¯ÔÇHj1ÔuD?V!Û‚¨ +HK[vhLü±Y+#?94Q~¢Fc!ÒqòvŸµG£[žõBÀ:QÐÏ!fó¼)D®$Q»hÞ„F¿õ[¿µ¹¡ˆu_cÓÞ?o¼ö‚ð&ÿ‰sÑôžÀÛÞö6?ÇCÇ—Ÿé8©”™¬ZfˆRsúù+å¯Z‘Íe²:bÆl^ xLwÁýäUò†Ó£)\ʉ‚J°Ùá;&‘güÊÃ$?lEJ˧õåÜŸÌA±¦Ä\7*ž9PPJí¡LChbZFR«{.©§¸k¶.Gëš°7¨uȬ ל«X8I®gÍó+¿ò+y+92¡#L×.·_¶iv)äsÅÔ]D“ÿátÓÛ¹–žú!yÈè{·‚¯¿WÏFÚkN±—ßbD­W&3ˆ½¼^ÈûêEIXâÏk«ÜA|®à@?¢Þ„ ³[ˆTtÌ ¡:TDKÐŒlÇÚ¬«¡°=D]é¾ £¬ï©xGJ˜†KHQÜMB–üÔËŠôì.¯‡òù¡aÝëô»µŸÃ$rÈŠÚEó"cºDÂnT„‘úÕ…õìvå –À^Þà?îGÑ£ÌÉÒ”çré{ÜF5ÄŸr! sh±tªkÔÐq¤†l)g¬:bkQé˜1BÚ“»B3‘Q=­J;¯á…¥ Mô“‡FV4ÓKy²9ÓÔ&ÀÂÇ÷,×P?Óõ¢ÃËÁö6‰RÙûK=9u$²/êZ`·]…ˆqæ£òú4§Ï¾k¶nžfMèË?Vs/ +õéIƒìßmïkðU_õU«m…ý;ó¹ ¸OpžâJ§Ùìe·vzžpêƒpþ(HÞxŒ°“ßïÁ8kYßüÍßl@*ñyÏ{±ŽŸ>ÄÕ¦ÁÚž`3~Õ&›—?AþÖ„þ4kû]Þ¸@ €é«¸—I=èô¯@J(Áø±3‚§‹‡ÌÌÒÁ@3zãU¼y]¡(ñ(¤uÑ BžMêÑž–¦ˆ:d&ô‚„&!Ki›³«²qËÊÎÕ25pøŽ¼!r銨]ÓÓr¤Dm-l/ÑLNYÙú°»|³%°„7ûïûѱpËÅdEýÍá Y³RÔÄ?øÁì͹¿Ë5:Šã nð­Œ^ÙŸúqÚh/|á «yÏ{Þ3ù—sr0Ÿi\ÊKEvj…»ü£ôZIšƒ^k,íÝd¶×}.Ú”â£S3ƒô´v#𔨆٬^ž3kãb5ÆkFonÊuøKíŸ(Yö’ZË=øÁî=òÍiÌÜr‘CzÞÄÌ1(ÔûÞ÷¾ODž–L³ló Ÿ®©îÙà4(@hŽ-¥X&oÄ=V’À\Ñh³äªÅ3‘…%d½¢ªý öjðü v—›'ªi,Ýi¤@œ6 ‹JœèÛ%Ó WI*([³$aÖB-¨"z¡$†fh†r`#@#Df\BT 5êÜ/i†éi¯Pþ…é…¡F5@–Œã¥^ÛO9 xa‡îr`):öAÛzÞˆœÑP».EöfDѰò±kƒ.Îívá%°„7üþº×½Î{>þhÓ„;˱îRK lmÝç>÷ñ“s×l»ùyø¶v´—Ðõ²JœbW?÷s?w½ª²ipÿëUårg±ôO=óÞêô•Da.uò hò*ÅáÜÿþ÷oªÔå›ßüæi9)¹Ü´ƒ Çk?½O› "ü}Öðæy?ß¶™Æ.l |JK`Ö„©>NcåñÞYZðP1߇œé‡gGk:ëšÜ‡6·ü)KûzpŽÿ4gÕ-9m° Z•ÙÖãÒÙSÛŠ\SJœö½ª†wYÏK Éòwh†hDG˜ M¼tT™!šÆ^ ¸^¦@sÙ_‚þùå”Å’My¨è'(4`ÖÏ}îsGthìµÐ‰‚{ŒìãŠ\cTdÈ Š‚¨Ì ¹*`SSᙛꋭ %“?TND««¨×ÙçíÁѶ2ÖDä:“ïRˆø©AÇŽ–JAçv»pØ ÂþGG)Ú{æ3Ÿ¹JCP‚y5¤,ØŠ~æg~F9Ç}=}µ6‡oö¶\öåj?•£Ëb|0Ø«wO:×bŒ¯Âl,ÄÕ¢W ‡‚Êg#ùéŸþ髚]Uÿ–·¼%?Eľ~)¼ª×õõÔQé©ðH®9T¥³/¡ £ñÿ~´¹*÷`”Ž~{ÿûß?õ‡Âº¹š0‡žZ‘NÂŒ½<Èmÿ¼L å’ ¸Wâ4•·i4Rõ@ô#ÓÞ©ô#:„!1 HL}»úÈF5è’ÙË8ÈÉì.j€º¨ôš#33­‚ê4¸7Z¯j†&é%ñPß(–«´Ülrúyöƒ¶!o.Eo:NK”ñ›Ÿ h!r¨€(ÒÆ3á]¸@ ìáÍÿ£—NŠC¹G••n–…œÖ( ~PŒaôB¶"™¸Ê@³êŽ)Ó§Y³ª±K°Ò¦‘i‰C¥Bžš)LN#P¸àŒPþOÄkjê5žŸº¸û{ßû^õLny_ÈT1ƒ#©ã’!ór‡QvX”¾Ù ™ô^þò—öïA¤¥f§—vìlÞjþ' 57Ú…- ”Àº&”˜á÷~ï÷VÖåØúƒ èzyÍq2/ ž„š\ßx®zçùÏÿüÏÿíßþí.MH¡—½ÌÆÓåv º¤%Æ•àÐË}ù›¥Ch¡ßüÍß<48ûÓô: Çîâ©[Äi$Æ”Rߎ™.Ÿ{5x*±]sÇEy¬oŠ2øâ |Ž•1ÐÏá¨WØ ­‚ 5‡Í kútÔ˜*{s•ü %«äi³lFhLõjЛÌÜÍùY»TîĈӔ¤®auÄp÷uA{xv´-‘CêP»iƒò 5B;Q£´ˆâé¬vÍEI`/oþŸ»bšqµ=ÃÑÊ—kQÃÓMkŒÓ‡Æ çFãÛ0íå¼Z½)º_Z‰æ=Í!ã|¹¹&í5÷Žw¼CG º8™ÃŽ{’ÅÏ0Ò;†Õ$Sgœ4èÍyF!3gÔ*_ª¼`ž,|™TÆw‚Æ4È!­ß …øî5áy.VÖ„¥Xðj|Í×|My/{MØæOÌÜRP¬Úi9ôÖùlGQ1Q=J€Ý}5ÏkïîmZr¬:{JáÙ1UNРî×?‚¬à=¸œ×OX„d‰+¨8r»êîk½»k©zÌ9Ü•ØosuÀ]Þ¸aÄ«j0š)*dìÝŽºiÝè%j[/·d ³2z°Ú_Ꞹ¼Úù¢ÈÊáet¯HBÒÉ#[¤ÅU¦u&J3«Ç¨ÚszÜ‚„&!K(Ó U­š„¢èV¥Ø<ïZðì&Y˜¢zí ‚Ú­m*#s£t)¢85»p™Ø ›ÿw§}r4â>ÑÓÆÞ°¥¿ówþÎh ZÏÌsD!O‡ói–Á)»/¯†â;ÁÆ6ƒÈÓµîÔiS¢ä2zÕe¾sIeú¢¿F?ô¡5½èEƤÇ5´^œ¦ÔKð M_zYGé÷ÃQ{ØÃ4o{sÃÓq^ÈܘÛÓì¢Æöá¸Ûvxº$¦Ð·ïèüwáb%`YÒ»ÓëO™ÌËbA…—Ü]ÉHøž’:;‚ä i0tçWõW϶q÷N¾¡¯ÎÚãO{i–rÓñv&O™P=;ía›ôtL5B’2À97ìv†ÕE³Õ w4˜{ø^ ž•ó®¼( €à ʽ€}§ŠÜ›w¬—Y`”èÏšƒ x‹K`„^4´a•dGD hF,ñ@?Ê2…¬Ñ•o ƒÌ41‘œvùОµKåæp8Ä •ZOÿB´†åfßó¦3»ÂfÀ9T3´5 ¥{L*´[£….¡ˆ§ËÔÓyîš›-½ ¼Ùß=¾w¾ È9(§Jhœq“PÉ­â~÷»Ÿ‚Ï+_ùJ'óŠš¼(ãL´á®HÚyÒ7Ó¿`Í:®/JDóN— ,|Q%gàðjàÏóÍ?*•M®½äìʇ^fg#wÞŽñÓ·¥š<Îé>áL¹Ióõ¯}ö0žsPa%£C) ¨`Â-)W…ë’I?³Nl—·.P“VAà±…ÊóŸÿü^ß²¡\¿ovV\–OíŒ!.§[ñÞèÉt…ë\¿¾rw„¯W[Là5]š A]îÖ´EL2eD[Ÿ‹GVš[“®mew/‘L*Ú°[qݾהZ‡Žûç–À…HøöÒ¶Q¦A6à^q¬»šÀ7XŠ=Ah†—´£_JfÄ8ËΆqPÄýPFEIDÛš3&3d±Ao:èÏNÕš G„ŒÜ­Ñ§±î‚Võ°ë^óIE¬á-åËAÞtôAçò•í'²gÇR¹Ž%Dí¬/qžz.V{AxúNYiãi½Ð­”¨*ú1eá;¥ÙÏ<àóLH}tµ2U8‚ã¯Å%C¯W¿úÕO{ÚÓÙO*†¿f2O/O—)h¯%’W ÷˜™Ê²}JøÞQ9ÓE–dW…â¨g0ãiƒlz|ä#§¥ÂOüÄOhl¶vòÜ0ÿ¯ýÚ¯¥%ä[*dùã@ò€<@·¦ ¿à ¾@ù4õ(-6}½×.o \ ¬ º+°m\ ¼5W÷@ øÀòßf_×?¯}ík³ñ{׌S×ß‚÷WúÁôΆÞ2hðúñ]å3Ö)gYÚ‰1ÙÉL)=þñ¿ýE&¡'5K#Q\«xo9™Ý`KàfKìž®h` ²·7È”ô<‰üì˜A ïåBЀÞ2ÄÀ[–¡aX¥‡NèØ[L (â~¨?œ›¥¾[[@flÐ5`–,™¨Öñ+·dE“üD™2ß•BÐ*ekuŒo¶ê}VbV,O1„ëÕÊÈ^Á‡‘4‡Áº#ZèêzÎé wÍ…H`//âÍÅFq°~±Á{ÿ‹{F¹¸¤§,|³Ñ’ó“U»ôSéÍ©Wà|¿Rœ’Çpµ"MF5Q@9©ºã£õ¨–p÷º×½Æáa„ŽK1¤á–PkÜîÝëŽ;î˜ö Ô¢ÐÔOþ n÷¹‡5CþÓÞ$óÓ`8¤¯…e0+¢€£?#Ë}çénÚfÒø~Š7°éQ@ãÀLwñ –ñsß]ظ Ö„«»ãsûaÊóÊ«'\}}ܺÎ×UâíŒyMHáí ÞòF/xÁ ŠpFÝìÑ?T!tGäE¼ÀÿNû‘Ø Âƒ@nìÏvÞæð@Ñ—5:Ù!Wž¿$ÎcW[•ÎZžˆD]ÄÏhO½Ž»…ÿ„V_”WFúS—*}; BÔÐ*z*rŸÍFñ+ ,HÚvÁê&.¼»˜äã÷¸q̰#Ê™„ºüå_þe·ð}ò¬jIcv_'ÉöÈ”£[çÂÑóò0 WÆ‹cäW:é~ü.o \šÖ5áoýÖo »g fHïüÿÑÊF(BØ‹ùƒ?øƒ+c»}!Sùab?R8Aƒ.­šäö‡=´”Jt}¦"è¡D‡6×ü<$’‘up¯¯×¾tiµ-ä¼\c7šë'…“#hÐ ¾8á'úQÂ<âZíÝh@§¿ FC&c0ò€B4R±žÎGQä§0c9™E¤Å­jªƒRQÓ?RäFîÒeBœV¥†&YùOQMïìwLÆLÜ®“KkÙCüòµ>D]BO'¶k.P{Ax)ôÌ]Î*ÍbôÄ'>1¥P&®”ÛqD¿Äð<ö±Íþ¬Ò‘go•]õ<ýéO_+åÎRŸÑË Œs½Ö6vRy§ÙØÙó&ËŸî9¢'â%ÿû:¥9æS1~e˜Ÿ»Ûs(S…-AŽ)Mõü@ÄQÑ3ÕÖÏ~Ö¬ý ?)ë¤1€¢Öµñ:Ÿ]Þ¸ ¬kÂÕ{Õ=[nÙœÍFªƒ+é'"LmB |×Nj쾻Aƒ×Ïa³­Â ¥¾¾—«D4kb’Ô—“К`oÙ}7ظٲ§«ApdÐÀºŸÁw/x†`Âôa·f@1½(A}ÇN„6ŒC&:±®Ä ‚rýHÚ Z2Û’èJyëß±qG$ÕYë•Ñ¡v,›*²tÈÇŽP¹¤~íˆz©<û1a´ ys‘Ó« UwWÙCüÐ??QÁ¯_ªÖíò¥I`//å/.²ÅªÏ'ç+º øº‚ª';‹“—åî£,0¢é8T?O?2²¬²sB´6Òœú…r‹ÏŒ§ë?(´ií›mr_++ÿüÏÿ¼^6müΚ—G=E<޵Ï=5Ó ¢þÓúO”~^ Nƒ[¼ë]ïš,8´|kÅÿñ׫OWP;ê™S¦ouç¿b>ô¡ÍÈ»°%p™Xׄk0!"r0™ßR>ÈÓ}ÑÍ[Æþ"$ï–½nÙ€Of/²‘î™GëUw±±Ðœœ}+½Ÿ´«ºTO8m`ö¼¶î{5x½ÜöÕ ‘xÀ¼1Åâyz£ÁtmÖ½M/|á ‰¸·Sã*蹡*ÑmP…É …8„"­ýÐ# "e{2B]™‘§É™ÊKKw©„ kTªñ‘«µõÒåì'?ÕÎó3?ÓaD±pù˜£±gü]ظ4 ÌšÝ} »=›Má*á°XçþÙ8†«ƒF½qOyÊS®êr›õ‚×áÝ:¥ðú[Ð]EæH1Ûýñÿ1šÆÀÆVµs:N‰dF¥dÔß«ÁSAíš ”`à‘yG,𱟽bºä(Avð ʉ ¬÷:‚ûõpoe ?ôI¨Úý‘8X·;Fñ0f¹ ‹–dd×]9ü¨·0«2¸¸Šö ?ëª5:ôê'*e|´ê0ìÄ'÷Pë7ÂF\Õ rè\å²G\ˆ_‡%Fѳwß•(½ ¼ ?z9‘ OìÉçä½W¼âÛhÕY#iÙ¬êf-kÏ1l5~?ûÙÏÖÀJì¬L™üé#ŸÏùœÏiÖ/#¤s‹„–Žï´oƒ(¯*»yðÓ°ú5à¸þ×·ÄÊ”û{Þóž °FO[éñ‘Xo!Ç``J¬e˜uÙqCëŒúÆoüÆy|`0¾£S‰h^˜õzß]Þ¸©˜5!ÆæšD˜î-Ù&ÛœÀî…ýÃ?üúÐE %VgÕ9·pm0Aƒ"ó11jd\ÅÖ–w·ŒYf}§g&¯ A˜ös ãtÕç!‘LNï{5xwÿ»ý”H Îh’oø†o˜õ˜JÐÜqÇ5¸8(裂ý!Ïp“ôL^£º# hC+CDb)šß@© ʇ?ÉÔï÷~¯SÅ&[†0ñ8erè›? ‰­ƒïò–ÀJ`Ö„™oz;¼³×[LÄÆÄ–¼GŒè¹ÙÍË_K~öÃñ_‡–§?‘¶‰ .hÑ]Á;nn.ipÚë6k ê̾vª¾è«9Ÿú|à!$‰@ÆmÌLRÎ{5x›’ßÍn¶€i¯| ¤o°[Â?Á1PÍ]òÓîÙ]Ðýÿñ|…\ Ä4À¥(’gfY=OXÚ6ŒrPeHHá‚‘(åôoAôv£7HÎ,;‘¨]»2ùöE¢<B•Q ÅB´²:õ°óÝ#DÒHl\±luV‰Úå!¥K$ ýS¦mÜ-<½û®¹L ìáeýÝSjt³R5.æÇúT̨H¾ -SÊc¨‹µðâ(í^lQ޾áŸ2âÎqV¬þ‰Êã¶ ØËëüڮ\ÃüfÔ™Ž“™©ïK¿ôK›°oþÓXŸFЏ³\)SÃÎN ÇY• ™„}ßûÞ·ùP÷ Š•ñÝï~÷„}óX#±ðáy«›J“!½ßøßX'³Ë[(YŽ Å ÂòìÒ«v),N ~øWÜNŠ&u)C—«ã­»Ž~ê§~jBû°½$xP”ÂëT¸ãIvÜ%#ÈýU tÃÒng)c."‡,¦oxÃf â~÷»e7wLå±Éƒcüñwälu4•Ú«$#œàï$P {¡g™h(N&À`rð'èŒÚQ¾ÆEÜNf&»°%p™˜5ad®Wï4Ò†¹úoü¿á*•òÔ§>u^سB“ø!Æã[ùl›µò–' R ¥÷D³înH¡PœrsºÞ÷´,#E‡S{Rn±9ØêL&ã”±Wƒ§rÛ5(ÚŠ®$xµsh{f£|>µÐNls/àâ  »ªfÍÞ ú³Î¸„ŒÛÍÓ&ÞꥉT MÙ@9Ö?GY@‘“µ²236tÝ‘Tg”[ç= C§Õ ¹¡"TEÑB·ŒgkJ¾#féÛJÕ:ŽH£|£môEÏN`W^¦ö‚ð²þî ¤¨Ú1Y¥VXÝbNéáiÐþ[AÉ-ØÆ†]ßsP·Ïú¬Ïª>òw•¢I1Q»«ÜYËœÓ²Ó ÎN]—vµt&„KHÛáà n€«¥œËþð‡uaðk©ÆŸd½×˜ú<£›Î9\ÔŒ'Ø)ýÙT (÷N㨠;b›Š~ⲩ왿Jƒ_eù['³Ë[7[³&ôR 󚧦(²mK°å< N ÞÐÔ~¶& >»OCiN5[‡i!B³a8#£LˆÓ< Bu` 8·¨I‹°y´ñ¶EêP»iL+"~蟚«ö'g2»piØ ÂKû‹ô‡~è‡è‚¯üʯôäÜ–FS¬påêLN(óº ¦=oøSñQ»£Ð#rzM»§}–è?¸š£î啜Í4‚&ñ¶v#åƒüËÚ!~È Š’Z3>ê‚À˜2sèågGy™Lk35¨‘‰¹».¦‘žD¢NûªAº4›ÏP²) mÈÛ4@êJÊÏAù¿ ßòsöF»ò%°„øGÿh§8ÐVB·)Ž¢ã¸oñ›¢˜F•L¡Ü›~ÎVa—ÖX UŽï{ßû4 ‘Ïäֺη”YNÕ2…UàâÚQÙY=Ùç¬Íœî%Çt¨@…}ÿ÷¿ÄôF¦Ú^ :ò$q‰=O™1OZ0÷…3Ö$cÚt¸üç}ÞçqÊÏYTwe¯Aû´é#C×X(d„cäkûQ¯n‘B÷“qîû¾ïûªôÓçpÐÅÇÞÿn \¬ ÇžÝ{ÞÓpw%"›|ïšãÈÆíóú ÁëoNHá¬3u1Õ\â9¾ L×8W© ”ÛоEf„³ —,H-xèäi XOÁn‹@@<é4˜fµåÃâ=¸w Ð?-¼•Ü-Ðä¡ýÀ¶;»oí‚~´¾zÛÛÞ†–´¾òR£+HKfeoúá\ûF(K* ä':„ Aòh(SKY$j½ã”ç˜Öd5”lHZõ}[Uzº<ó£yÆÏæ~öœŒ¹Ë.\¦ö‚ðÿî%V¡YòGo)ÅæýÇüÇr=§J¤´<ªù°ÆÀ•ø²´=ÿùÏ×€W½Èæ1€9×õ_ÿëM{²¨œ?í'ò'Í»EK8Š~¬€¨XÞž¢½·ÎÇÕSðé’ÁmÖÍSð Y½>оT$í¯½ í2;ÓΙúš|C=÷¹Ï5¥Ù2­W5“tw8‚zÙË^6ËH};ÿç0çýsKà¢$À-ª—λ֋É>‰î±(PœÑQËw÷w§:îñ€º¤bit…™—ÃùîÙÈñTÚ E±Wƒ÷LŒ»× “@Gùy/|€¦ä½Ã4¼)­—\²îâ š%÷€Åzd¸5âu¤jÊÈèÕÓ?^çiŒä,ê’÷aN‚H ê'Ôë‹~¨GE&(EiEJuö‚ÆÌ]¦`p›œt „)5BʘÐäѧér(L†s}FP£=Å$~·àDêzŠhÊ×’òPçp—ýó2%°„—øwÄB•°Ö‹ÞQ`Ïr&sW{†*ÅüP£ ×|(è«ÄG§ë¸®¹sж­µ\jõõ´§=ít!ݱF‡wÍU{ƒkfT:E›«hX#‹,Zë-q'ß—‡zó›ß€8MšÇ²§ (è] â3Ýý‘*20±Í^íÃÂ…(#Ó¾‚[dÝnÐ9™6H‹z™Ê) <®F~Ð!¤h<]5k½‡>M—C¡ÝÈn}öm›‡Bç:YÁËëå‹À8ìç0òþ¹%°„ú }¶Ç=îqtФXßùß©@{¶Ï¦ÌvEÿfc£Af™tÐAìOg%ÈêŸçƒ”VkV77]5v¾u…6-¹¹Ñ©WŠÖÌ]•ûK¶®A}é8ö9UvU¨#¿ÿ±ñgäé1·]‰–â&/3/‘É2ÿà?ØUæLHõ«-kÜįÓÝoyË[&ß—öxÄ#æF»°%p™˜5áØVNY×íKf‚-ÛÚy³&üdEå )œ¥&Ãÿá̛۟d™i€¬õ{5xû¢Û-o°â8z×P"³à£½,.‚W6#P;N7 »”'~Xò§ˆ¯‚ ^%¸O˜@ùTú !ŸåÈÊQÒQ½P‘Éa3Ý‹ÇA`¦f žn66Q Dh¼™jã ‹8]å4nÝ9Ý“z–.E~ж6µBç:¯Ü§Q>3ŸYí–ÀH`/G—UlCM´1(<ºAî[9‘¦pdTwŠ´2%;®]šï³aÓ‰2‹øa¿®K¢qxçÏ"“ULj¯õàО÷;ÔkC)¯&T›9O_i¾ÌÉpj*ðȧ%Ý1Œaá3|ä#¢ÏÓï1OØóíßþíiU[|ÂÚÁ0•0éßý»‡áe&¤‚K6HÊîNé³zöt@ŽÅn]2ÔÍvaKà2%0kÂ1ç?ùÉO¾»¢  Äøõ¢y­x‚a„¹°Á฻ž¶·#1N Þ}·[-P§íÏÖØ@h’iŒ½<+¥]yihÏ*.±¥—€¶v‚¶ÞA’²Á«—ø‚`—¼’1 ¦óÇQ Æ£Æ@IDAT¾ƒìD*|Ã]=¸ú ¿7p/íOörB'æ‚fŒ1ÚhHÈ©*0—P—éƒêÇÕÓMàlìt{’ˆÓÜôP(ü/‰­ßèY+a„ m›Kæ\z¯,8Q>ôï0òþ¹%@{Ax¡ÿ >øÁÒ­Žhª-Ÿ¨ç<ç9êS¬Òôe^¢IS+.¥”Gã°çqoè ׃(ÙØºÅé9<µ,òØüþÚ_ûkÌo‡-V[÷ûÜç>‡aûùu_÷u®›äs_šÇøôP§~ÿ­ HíÆÏ(n)È:’ƒ<̳Ãa{RGâ¾ûÝïvk´UŒ^{À™€@#—…/¤Ñ†1²G#1gMK—xó¯puöwå–ÀÍ–À¬ GüËù/oÿ‘-ÿ¼tÞ&ïò4ˆð•ÕÖ:ë}pû·xÑ‹^ÔÜ$p‘Q ßÔLíòQ½é^v-Á.ð-©fV$Ðç^ {aOÎ'¿þë¿>+@H#RùYÆçQÙNþ™­—xµ]år˜Ãþ¹%pS%0kBFz/…oüévÖëß;+ÜèÔk‹b)ÓºM/ûí xÚFžCSò‘P¾«RèÖ&pÚå´¡Ìs¾¡öjðTD»æÒ$øÊÏÔK^;ÏOp9g_Q µ%œ« àW¸ ˆÁ1Ppƒépy)X×׫—ï€2èGhe”`²ÈL¯ò¯ èD[…Z¢îe†ˆÇÙ×?‡O¤uA`Œ?±|èMαÏUQ6h’» L3 hr•½^ƒÃ'͉žõø6mrtΡv†*1žKˆßáûç–@Ø ÂËýŸ€è¤\òx,ÃJ«ÄɌ҉‚š•8k\ûîØe^~ô£WãÓ«^õªú^%åot.˜ÍÉYˆRgÜBÚŽ[O€XÇmp`¯@ËVô#%ØÂ£ÈÚ^¹=Iwi1ÆM4½iœë¯þ꯮]@EqƒXgÞ£ÙìÇ~¬HGˉ:‹@¶CÆNö¡¦ÙO˜4«G?™-ïqHÒ:É]ÞøÔ•À¬ {_,áþí¿ý·×?Î z¡®:iEüŸü“bLoßU©«î¢oÞ^êC,Ðõ§dò_sÁïÕàA>ûçJäµ_×û½•½ª­jBL0š«Áky;óèÁ€8;2hî°(`}XÝ95>%Ð-À}±!@dãt¿.ÿR‘h&¨‚f´E<ÎþáÊ!¬%ê2¼Â]ô¦…%Âs¶¯Ê(Ê4 P)„jâM†hõ,}GÉf­;„- +šW—±pÍvaK ìáåþO`$KAdbÿßÿûÏêEä¶¶Æ\—Iï!yȪŒ*Ù—}ù/ù’/I{ú¦‹ë“m%ËÖU¾[ÂÁÝ”ºwF¿Oi—' ýÞ ªÓ *ÁӮ𧤩.A‚oþæon®™ÐÅë9Ÿÿ†Ó]¹N¼õ$¹aŒ<8÷Û²ð˜ED2HtÖ#—ÖY¯øhI¨AHþ$2[”u&x[%fCc–Žê¹yÜ㤅iìŸ[Ÿ¢8¬ Ñ/g7Ÿ}KµCÐàÙfS)Ñ|ª€i_ß©¿¦@eeV÷ò¾â¯8m‰ûäE¾&¤b)½D¯ÿ^ žJr×\š€]ž½Þ I(:(hÐDÔÌ»4pë9Y(ƒæ÷à_ÊgwQ/@?»sïŽ b7†Šé t©h‘‰f (âqú‡CQ<‚«Y•Ñd¦ñÑëÑ&ÿÍaId©Ü¤èÕ|#W(¢5RšB”lv\#læÙÙúº)jçvh^½N&“Ù?/V{Ax±ú;¼¸í96=­Á}œæS"Y§C&šû\jNæ.Î ©fWq ¦²4/®«ý¨G=Jcwœ¨›óýÖneæöi0ÊZ_'Srd'k5ëÒñ£›sUÁI²êMrŽ”P)Ö¼#+\âdÏ9¡Œ®ê-ærgí ú…Tf48û_±Ú#ÊTiœì ä6«Íô;`¨ È93°Îy—·n¶¼¤…¾ôR0ü¯©Ø{öu%¶ ^/7i9Ö}ïéõÝÂÖæ`Qz• Ë” x‘µ,™ÍaXÚcµy´ƒ:´ß?·n¼ÀÜ4áÝé ê•÷íH½6Öf ˜v1xõò‚Ú€¾€åÂÁôÈÐ Ä‹—32pñsuަ@¦RÁKŠ04·@$Vsm~­(ÇÚ¥2rr +Ìj*ÃJrƒäâŽ,¡L³eŠ¥ TeDG±òQqUˆ’6‘·î¨ R׽У50ìaûNRA0ãZ0sp6àßú[«yB†üuðŽ…`ƒœöÒ˜õ<¿üË¿\ýx¹ô˜REÕƭu¥¹ïx1 Ù—ÈÍ# μ7!ïgu:ì9 Óabûç–À—66®^+‹¨õ8Á냯ÎßøÆŽoÆöV=pèe(ÊÆßÓ‡‡Ÿ×„rôJøöP{5xÝþyip­â潨0•¶Ósª "(íÔ>ÀÚ)2­šê‚;³¡Xß5þp·n¬åØ»Gæùï SC- ³€D:&~ h²rL¥‚ÍC´$õâvèÊzxr- ^ƒ«ò£F’ñcYÆAŸ »*À3ºÕsõ%›ŽU"o9Ìû‰Ô5üoµ\ç¿Ë[«ö‚p•ÆÅ•Ó#RTõäÔn EÒgi+÷âá½ù¨°®r?ÈŽëݜخ™EÑU 1$9}¤Ÿ]íFT6+Z¹¿ºúÄ'>Ñ¥õ Ád²{iàIYKmó¡mL1ëÓ¸Bq‰ô~·ãwñ¡}È%aKj<{î©j^ÿú×Ï¢×Ý-9u0yú¸Äª7þ¥:ÒÎãÜÏ«d¢ºKß§‡¹íŸ[7[Né/ý¥y)ع‹õ]—^W ^/^Ry 1ä¯{Ó‹XÞV¢nr_ŸK×L&‹•ÍŠï5îLמÂãìÕà5Ü—.A m¨Â¼Ý@pÌ£Àñ OxÂ\Œ†§€¼v 6`×¥äŽ#$Z °ÎUR³@ü4Þ車䃀 ¡&€6œþQº;²1—TdÖú"*su- 6×\EŠÆ^¬Ê„8B]ŒV„¡«$=;J¦rÖÕÊ>È W¹µ´JïI×¹íò–À*½ \¥qqå±P£aó€CÈcÜýGÝÐìë*‹¾Ö·4z¢·ÏŠþ]—FxÀ~é—~iõ¦ÐËÝÃ]ÄMOájâFÖQ“¨š¤šÓtÌ„­îœ´sè+;…aÏØ Ù¹dж¬. Å>Ü‘Á¯œ¥-Ö½0<^øeaušÐtÁVKpo(½D•#G™SŠfŒŽ¦íª“‘„:ôÓãÌbrÕï×Dœmÿܸ‘°|šn¼5lù¬Ý陳Ι·/K¾0p¯:8iãd-)Š«‚|®ºÑêÈê}_Ϫñ {5x•Üvý…H L¼ ÿä üõRÄ×½îuÀ±k~z‰@' ˆu¬•µ¸‰HO³÷ÍL·8Ü¿èUøßˆZ—ò¯ h20î©Þý µ§I­:¥Ù°íI"èG÷2%´Ä´=ÎA«tSwÌv(Võ("„PФS uKÓª5¹Õ +ŽŸCÒÐ67EáT¢sÅRƒòU³¿·N%°„§2¹ šÉ+CËð püNž–¾IaÿKžƒ¹ÆG¹´˜©Ù©)näÈ¡¿õÏìŒÉ’ì̱ÿóþOm(ÇÌó~¦×Z(ÀÏ>W.º¾ûÒÂ\³ø’ÝqÇ&Ã3síRùïÿý¿ïÒ×ý׳2ŽÎ&€TcÅŽ?ó™Ï<ô*5,AÇGÅüaôçÿüŸ7šɦ‹G›g]â“Vv/•,‰5¯—«’U¼ÿýï›â¤l]ׄׄÌMwaKàKÀ"ªÓh¼2}h'T+Ò=v˜~ã„6¬·¾õ­Y¾¼¼ÿëý¯{0¬‰™ÞJÔLÛ#ìÕà=æîr“$0‘lÞˆ¹Ž°R#ºž+f9œü”e¡*0 > TÜ‘ ®h+ÈÜ- Oœ÷º€~  œ~"è’ Œ0ÌøS@-\B3 ”ñðÓã "IΙ(Ê´_ (Æèͨ/´ù!DŠÌ}´uå—†°2†’U’¦™.Cä:Ôn´ÓÎ(síþ¹J`/Wi\\™zúÓŸþ1ír×?-Wlú‘ÅX»G•sÇö³Ÿ­áÔ(֬ˤð:•c;rìaÎÉ™h]8]ä”_.iƶӾj2ÑÁb%!BIŒ¦ƒ(iÕá:–Œ®,2›vž|\MÃNƒïÿû{®T<â8K¾bœøñ¯!‹â΋lG‘§JYgf…ÌùÙŸýÙwI÷Î]P1rÀ€—$Wy’Ò€ÇYiìÊ-›-ÛtóŽxqNÏ»ÇÏC,ï/ãÕ¯~5‡«\ËðÈ[¦œ¹þ¦ùôš›üÝÝi¼~ð}uKàSK ¬Sˆ{#àæ<=ï(l÷L9~’­ÀhïfÀ d×/ ¸€º <ÓYy2ñƒÄ=¸w¯a È@6hôIÀ(&$dú"ºPhFË*ÍÐ2!#$%;uPj„Üš:¡ ÕAxšƒ1=;:Ô&!‚4w<Ъº¬)ý’'2æ\CùŒ„Ñ6#Ôe¬ÏµAö®Yvî»^ ö‚ðÿèÿ×#Ó­gœº:k}ù—y{e©’¾Ø±…êYÈ2Gɬõ ½ü@ØÕhR:‘?ûܬ²Ô w~ûÛßÞ:êÔa£aÌǽÖ#t„}똹®éñ¿_îù±âã÷8W¥lî7…Hc$¸têúoäV˜ÒQèÛØ Ç·DCƒ^`–*ÍdÂÓÎ#I¦™KŒ‘œŸ0¯G…®’AñšÕõÇlÿ³%ps%ðŸÿó«¿7Bî„OÖ³Ò?¹{ãzédüyz:+XÓ6ùOÖl÷8[Ÿr^³äùs-ð”Áìíó“»fÇ-xR 9]@jødGÀw²ÍištVT­|²Âè;謀Ì[߉¨ÂÜb k3A9Ö‘;Ì9™ök¡ Ä½Y¹ òƒQDèRdØkìG Åj+Òä烌µç95 h[d©J¤µSFóVÒ²Îs—·’À^îÿ íà~üôcYžG¿ÐV•SÊ‚¿¹]ÍÕ ¬—êê~÷»ß5ÒÌ©ý)OyÊ´yç;ßÉÒ6®-ÒD Mƒµ@¥º7úÕ™KBÃѻաÿ×~í׿ª‚àïìdã$¦Òn›\+aÃJqö®w½kíÕáòrBLÄ‚û2Ñ ¤T)jꕲæ*"b6!IÏ´kÀ<™54¤¦çÁM`²‡ F𛙝SÚå-‹’—Ë¡}ló‡CÆ>AQŒ¢•ô %:±Ðk/ò>aâæîþ©.°5!gpÞ°˜é¹«`qœtÀåP0 L ¬¯#°;//8ÊC €uY©À÷´Wîs((`ý«;b`VHÂp‰5mšQšÙ}‹ë”ŸK:°t¬ÏáA{¦:äÖ¨ÑÔœÐ*mP¬õ$5>ÈXÉê¢gjVRá'A!u…h¢y§ƒïš-U{A¸JãBËã^ž?w ª„C åùpo`dšŸ+?2ÕŒ´4æ*ЬeŒy3•ðq!Úm6 4gƒyÊàÒÙëÈÊ¢½Û¥l„ûÜç>«Ž§®þÔ•B_[äòÁ~°‘M²èó'=éIë½ä1›“y¿LŽiPT{†@`Cld"` ãåoüÆo´Ði¹–­f×z1‡ëÝwyKà¢$°® ½&W¹Ü-™`H áEë]cA?(¥Û—ýÌÏüÌÔ »ão_t»åÍ“ÀʹÆÑË¥´êA€ÉDÊñÉ `˜êXÇ, pÇé—|Ĭk?¯3@ŸE—Àý¬§W«>$ajBû>¨Eéd¦A…¼ÄÏZ”˜õà.ôÉAuÖL2·£kÔBE&ÈŠ…h}|Fû×›ºÖ£mŸþéŸ^gwÇÌj‡¬6ëLvyK ìáþŸpçvYéFå[ÿНø ª„÷áÿØÇ>vÕ5ìO¬k«µÏUÊŽÛä¤~@Û:{ùË_~Ú†šæ?nWæ#oòAÝ‹üv;:ô¬;%œßüÏ1C÷’ °4§Qæzu©‡¢|™üõ<à'Û[ˆµÎ¹\5Iœ¼ûÝïvUPA˜×ú93]-5_óš×„y™*„T'ÁLcη6ŸÜ‘õ)vyKàÿ} ¬kBÙ_ ݹÇÓ¦:ÊÍ`yéÍú…_ø…œ·å‡˜W·?8­5Rïóo_n»å”€ m¼f5ØO;ç³yÔŠãUˆDÇÌ 4{ÇÁ¨¾ µŸ@vöëÔŸfÓÀÚ˜¥â ÜOw@jh..Æ%$ÁL† f ¾‘ŠÓ^ZÒ$-ç -¤u™üí( bsº 5Bˆ)_õùŽïøóo q5š÷PÈØHU†¶ÝEßîüC sH‚ù¬›¢WÝn×_¸ö‚ðÂÿ|ìñþð‡ÓŒdùÙ—…ó:wyõ×|ÒûÌWozÓ›4ã°q@ €æÆpU›œ7FÇÑkv,W‹è—qÎŽ@?šÃç}ÞçY—¶ÌóSjf@•S«uÇýCßɸr½€ßò-ßÂ(ØáH „mñMG^+¥EM‚8x'"Ümm)KO¬ ‘)w–ΜQã†Dôt]dªY?§©Pg&»°%pã%`MˆPöF a׸!\/ 'Dçe€Z9½ºÆâpòpCÇuíúqº*êi\×(–½7x;BÛmnªNƒÙÂ@Û„Z€<À×Z D°ìj.;€–û¤VðÓO ²Æk)€ÎEdnðÝ"ÐÈ`=2pjF2Å¢H©¥= H˜RqöOöÜç>×U„d®"*èÊ,‰£1(Í48ÒW‘™ç‹\¡X#Ïk [1‡Q8t.÷(ïp÷ýsKàT{Ax*“K¬qD-#¶;%%Ë‚ŸôøD ŽÁ/FuPIRlqÄÊQòºJ‚Ž—eð3”ôÍWµÉUƒMjÁtº{áj?û³?ËA¢cXùh®ë È—êgwßÿýß?y)ĸçjòC?ôC‡[Ã’NJ|üãϬè©3+™¦; Œ‘ïЋ¢×̇WFórÀ#Ûç|Á4C31ÿ‘ž™‹‹h7•k‡ó˜Ç$LÐÅ%Gšƒxýô9L`ÿܸ Xnå_à]Àáh’»ûìheæ$KÊÞ¿óåïu¯{ÑŒÞrpJc²ÀëµWƒ·”Ønpƒ%a8…-±µ0qÖ{ÀäŠAá¸[‘m‚Nj4`ª{«G jÃÜÓUÖ³žõ,í5ÈnѨ%(è`Ý%°êús .!“a@‡€8»¹g†Í!AK“ÙW4OÔ¥sá‘™«þè(GF‡®QeÙÑ*äªDëf»~†Œ Á@Ø<µ6ýEÐ9¤ÎO滋ìú-‘À^Ž(.º@R£ÔJê#gÎŒm)  çèŸ)nUL"¶ùZ<íiOS™{ÃUÒäÅ¡Í#~hÉP§ÿ+êû÷ÿ÷é÷Iý"¬&b¯ÛRC‰ DnyFé¤eœ…zší*­ÔîísÖ¨ÖmdîÒ˜ê<¨ Û[0ðªW½ª.t·ŠYMÑïF`•?5øi&æÙÏhW÷ÆtаIGB†Ú©ãb1½LuܑƠEm€›}?À—Õê #ˆ”1 -è×Öö!AíHÓAô\ª`Àzµ®ñ€>›¯ödàÐËO´Ab¥Êê×GCB®â'º#03ÿÓ[pˆ.!K(SÉ âƒPå­01,ÕûvXΔ]„ÍúyÒ¢g³¤n½n À^îÿ"ÎQ¥\~ï÷~enüŠpíS³±ä©çÈÑ(Ü®òsH»D¹?èAbEBF¿—@,¨ø“Û/%‡öè뢥îOŠFH¡~Mþ½ßû½ãNù¢½Hw® ën^ýe¸f¤=…¹®¥gù¶oû6n-9Êžžaœ`ÚmÐ…¦Î#T”ŒqîO¦øÑ¾ùÍoNGkLM³ùµà4ÂoþæojIMëLú R¯€¶±ð]jÕÆ:‡]Þ¸‘°›ÐÃ!Ôó¼,ô½\RLøÓ檂h¢¶2ì`Œñ 7 †½¼Jt»þÆKôô"€¶õªÚ’ ¼8# ÐV{ß ¯õ ¬Xôfµ’1Ь׈±]/ Û ìŽÝ¹@ó¼¤Ó ˆ  ƒuà>tèƒ~wA: jº(§Ð¢z¤µ(·œ£èÇØ×îÊHçêç'ÚchF@$4i¤}B¥: CßI³¬oÏ2lMÏ4´ yCáª×ln½ [×K`/¯—Ïe]Í6F´²Ê{ÊÏñ¶OaåÌ0Fz h¨‘T›i¬ìo|ã¿ú«¿:„І+< Õ–çÀ+rz åà¢ã®Jß™°xÿO¶h÷´<§‹üÊÎ:¦–€Kœº›ÊWV€ŸºÝW~åWö8ôéaJøÀ  ˜üÀƒxzÖÍ÷½ï}Ìœ.½å-oY{Ø=Z^„R! ”ûx¯ pš…7+ ô²­Q—IÞ3™¬Ímý<îq»»LwÞ.o |êJÀ’l^C ïð OyÊS¼)Þ£ø8\ºÍŸâˆ²ÅÐ b~ÞñŽwôb„{5x›2ÜÍn˜À ÐY1hÊRÁ–W³ÙÚs“ÙüµÂˆs´z¨4W¡TõàÈ‚ÚI`ãÝÄ.eмvQß­š¼¼³Rô¥'-ÜÙÄÝ9s"("N A3Ò åq¸©ŸèŠÛ™Õ5yÔÑB3yíÑ!¤h¢Ý eBœrRX·=gA«oÔ+–ÐTN´ÍÈ(\¤îtž»fKà¬ö‚ð¬X.´rκÉé¿ú¨Ž’ï)´û×÷êãî’ôVI sò“ý>£Ç œlìô—3 àµaÞØ©Ä[›=õ©O=½¤æ#ùHÞ•RÈ­BLBÊÑ7_ Ð4ûayÀzÓ¥÷¾÷½ºLws{àxèå§É¸ô×ÿú_gãäÚ1J­ œ»ÌXX¿ó;¿sÐBÍéÇS¡qÜ…-K€…YDÞ\&ö™u<ŸvLñt¡x·Äb/¢ )æí£²_q·Ù·n† ¸9Å ©U+Èf•àÆJàO/PØ>$û½SÔ>ñ1÷[Ÿr1ÅS î(¬`¤@UÁ Àk ·mnðÍ ,G5€r]4jŸ·QÀª¨¸-Æ@°p<¹=ß ›Ž«  ƒõ¹µBç^ keåΚ6èN‡ ¥…«²¡þìTD¥ð?ÔåtðjJkùéHѦkƒ8¹„DÍ -bUöY©WËBAØÐ6ä-Î@3È.l ÜR{AxK]V¶·«OãbQ’w®Sſѕ£.ç$ÖÚóIåÏyÖN&Í—˜Ÿ9B_æ7!æ‡\Ò • ðùÏþŒ¼þàþEcc;,Ûœ/DÝÏžH Ïf#°/¶;8¨¸Ê‹c3Ç¼Ž£‹«,…æ *è܆BIAÚé·ŠB¾. íÕ¯~5pZ¥®‰È¬¨Ã×¶=WÛª„µÙ/Ë¢»¯IÒ¾g h³Úß["[v½Èe¶s rµO}"rðv§å ïÒOd´ÝwKàSQÀeµWèTðö' æ‚­6¾2 ¶¾Œ¥¯#(ˆ`ÑO (Wá€Ñšù¯@ÖËXà›Ù·»*Átg²× ˆŸ²ŽY‡g)Žq.¡HÂØ‚= q8¯Íð\(â1×¢âÖHËZY½ArÖýUš$çk—|£ÖŒV«(Üb¨¡µ¶DÏb ”Õ„Àh‰Î­#ïò–ÀõØ ÂëåsqW±+ZfLá¦[Õðž¯]zRȺ>Å.o \ˆ(œÑWÞ܃¹ÇB@@Ûñ=dô¶;n |*J¬ Œ†>¾g}’x*&0À^ ,÷" ÖSƒ¹Y«€¿|¼b€«#è ¦€õ°3ŸG( ndМ_€‰Ù”Àé‚€Ük³š˜QÄ`¨ˆ'E&Þú÷Ê͵3‡×ú)gBZ¦F.BlzƤç^×l!¢Læ€>Ñi3rU_ßHWO¡á_‹ç\JŒÏám’ j#‘Z›¨-GÕx¨Êóa)ã3FéT ßï¸ãŽ©¤ûVIÉÑç·µòP¦dSÄì‹ïyÏ{ö°‡W$µî„1N2>‡¯Å¡ïü,ÌÀ÷Ô ­iYéd›ñ…+È…Ý9HüþÏæeÉœidÓó¤³Òã°anf%†~Ýì¾ì cnÔ†%õGôGA&?“ æL AíkÂFDz\\{ÆN‡, –ø‘ù‘¢2 %ËYkÂ Õød‘­Ü7ð0ÈAûç–À—À'q ÷IêÆ‹}?à”Y×!Ë¸ÌøYHš,ß  `­NdjëþÈ›l. £qål<ŒŽïÌHàš x<'A3€Ó‘À=í§âÃ_ ú ìw”1@”Q…i(Ü’f䊮 -æ6lÁøˆ zcz¦¯2^wÇÂUЧuëÂÒ“ÎÒÚœû =‹ÉD!¶ux•á.ߎö‚ðv¤tYm¤l¶Nãu@¹°ä¥qäAN ­Ê¨ôÇ5`x[ÅÄBV‚¾C€ßÚF¹(íI¯"¦\j²¿úWÿêÇôÜ]y_ÜÑ|Nã fðŽ3‡[0QÍãÀ*ÈÑž¬Œí*œ5Ú±Mº5»`ަ¢ÖœŸs—âùpÇÂ$ àKöS?õSì¦ĘDÎÀ™<ç‘ï}ï{³nö“Ý” XNì ;sÖíZ''¥•k¡-»‰ß[/½ñoœ5¡AX4áÍ,y³¨¤ÊÇ÷cú2OÎeÚpG‘*mð PL*þÃþ°.Ô½«­~5ö!¢†b.Í|øð‡?\Ì`ÎeJæš±Ž›Í³žõ,53[i»!œˆµ)ÕôÌæ?Þ…- ‘À=XÚ݃."Ìý˜"`±î¤}Fî\\8Ÿ|èCb¶,…š?êsê„òà•T¦¥ (v¶ÛYFj3Ÿâ=f»°%p ¸[ ¼»Õø¤·ŸñÒ$°Æ¥v‹ŸR¤šr˜žVAÀ()§H‚Ž`k6ÓÀ™Ðà´vkRPw\ƒåÚÚš €‚Ñ9] ¼6~!ô‡? hv0­#ÈÜy¨ªèY¥Aü¡—ŸÈ@0Šœ^UƒN´ÜzûÛߎf (‡aû "IkQåì*ôɹ*ÍAc “AÔ-P)„Š>~ÃOC·Z±Û}í¾.EÌ’O„ y»j2»~Kà ìá5¹èKâ­éšòÓA€!‹`úqlo½“ÂÞÈœ ÄÕaõd]ÚyD«;Ç*Ü‚¿ã;¾c­\Ë}Ž£j$jÜ1µ“ÈËúŠÛ}šÚ´×Žk¹Ó-ÖÊÊNCj=ö+¿ò+]WbBø«xvn™g£òä(óøºè8§t$IØà_þŸãTÓíØ/'‹Œ¾_÷u_'Á£)»‹vg†Lž³/*Üâ¿ü—ÿBEY° rÂé$À’_ø…_èÔ_d¾¿ú«¿z…Þ¶n°ns™w›Ín° ö£]²@€°˜((-Q‚ptªáö Œ@RñzªÙ£#O@V,hp`nFビäA¤«àh‚ÎYÑÔAdP{úgÊîbb`2®ëRºa{'ºçu‰êçg,µ@0ÆŒ‹x ¥® µ‘¢˜^‡bcHΡ¾ŸH‘A:~#ù NèÓœ{ŒVUï{èV,¬÷ìèY ÅÛ5,èìvå–@Ø Âý?ἨJµè¢kfT(ó1Or0óxœá¸Ëó‚X*)n~ü鿾å-oÑ=lß›q*üøÿ¸6Nbz0¦rÍ £ƒ_.šÓl aÈùÃ?üéœÂw}×wé>®#üøéÖ¢&ÔçÊr6E…žwç£ E§ š/«(§÷νX@ ŠÀ|?æ1Œî^ê×Ó,t,ÈÁ%üöoÿvWÃöB€= x×8í¬û(T“nÃ.o Üx Ür±wË7^Dû/Y@auº"}€ÈœíY  7ù 0ŒZòipÀ8W8 Úæ´T?î£I~0\¶òÔ Œ×Xý²g Ê!l­ÈÜ…á¼Pà~ú'F2×"§WÕ í¶²r¬¦ÕކWyv•(M>SHÎÚB„С [ K(Ó!Î0ŸÛ&0t+П¼2èY Âò¶Þk—·nS{Ax›‚ºÄfÅ‚ÓD´ù÷}ß÷¥’J¼9;„íPuɉ´§bê„w ú}Y¶žð„'Œc ëZ9WÄ”ŸŽPp‚”Ýi¢êCȬš¸µ@ Ó4_ÆyЃdªßò-ßrz˶4×#€øxðŠ™‘õo§N§ùiYõÁÇÂ&–žšî>ÜTt{JÜ¥ fÞúYжÕâ˜ÇbjLmîÔ#©š9#âl$zŠßýÝßí$¢&0ß,»×Ÿr˜áþ¹%p$pÍ’ïšK7àÁ÷#l \/pÐvß`DðDÆÇ¸tºC¶NЀVS/xRs/àU–Qmò2p­^i]ôêÒÐÐ9‹4þ–­Ê€ìŒ_gBmÎ`X¯n™†ì)»‘uGtD Ãú‰< Í\##§gN "®¢%“8àt¨Òä 7èhò3Ž<žšT‹“D–NGhÍÙ3ÝŠ€EÆ\²%Ø’[m;d×l ÜŽö‚ðv¤t¡mÚ4£bè,©¤—¼ä% £+«ì›Þ<•KX¸âÜ!nÆœ4ÓzÑk"­ÿãüå5½ÆyÃÈ…w¯ÉÊÖÛI;V(àL‰fdº[ÍŠœX\å@2+·uçÕºÊz0щb 16H&Lak4ƒ5þ_ñë€ì”-A›’tgžtmðÏÿù?w $ÃHŽ7£îU~ë·~ëÚR@|‹Æ9’ˆ·)3$Ï™99J^21+X6ßì‘W¶´Þn—·n’Î.üÎVÞ¤§Þϲ%pÁYàc’[‚àb :ã¦C¾à5ˆÔ@€kå òÖ)Äɪ ¸œÍ½šTõ‡œsÀÏ^¢)èC² Þf&X_ïX(Hd Ð@†±ƒ÷HÅ!£éŒ–@l“NÍi¡@ôÆ“¢:cI78"„!E¨‘ŸhÒÁsªÑV{tSòÝîÅ/~q5èÙ,§Ïn‡žNl×l œJ`/Oe²k>&YÒéªR ”ZQÚ£›¦ ÙÙ&‚‡üÿñHVˆ¹#wÖÓüì.Â#ˆI˜f‡‚ƒVµña¼<\ê'ÐjµÉâ8ÖMÓ~Ô£5»‘<Žú©;u\R™œ?·€ Æí‘é_x0Û•ÿõ¿þ×ðéô !IÃÆ¯Õ>ìa+ÂÞø&\(y[‚ŽÖ ?©@…Õ;¥ý¼FGtÐj¶Ï{ÞóȧîBíÉ\ÙM«™ï³yxÞýsKà&Ià°ü;ü¼IOºŸeKà–€‚ 1Ë9P¢YCel$QÀ4·X`kFg@m6 È«1…ƒM PÎ8:¦°‚×j.ØÅå³ ¶MúÙU /2аè’ÃQ<´i‰NÌ„sCBîä"êO]sþ;2ãµAoF&D‡ü 0l0}0w;„jD4#T@Ã1eÓÖx®Üx˜íþ¹%p;Ø ÂÛ‘Ò嶃”Ö…3 ãÜhŸU5«dJ<Ö¯ÿú¯»Ä³b=©O38Aã‹MÏ•¿1©àŸÿùŸ?XûfL¢‹©9ÚÀ¼÷½ï $äã[?úT¥…4ejøÀœÕãnm|Ž.g@:Mˆþ‚?®ÿŸýÙŸ- êÈs­¯‰³™N[ø5ßFx×»Þeò „™'üïxÇdjeût¸Ó,³ƒ1ÓãŸ#ÿØqÝwì”,‘“Ûdüà»zø3©1ÃFèA’ûç–À “À,©²´R¥ò†=æ~œ-k$@í—Å ôG°(ðO=àòñ¶wzP.£cZR *§A k¥fsev|Ðg Pcàx:a0Úd+x² ¶i_#´~ʧ}ÁwÓè§WA?`ÈJ€4¬„m@ªD'N»Wƒ„è…œm€À¸uæéGrP„g|Gëˆ ƒ&…PÕ½ïÇѰC¨2k€°²k¶nS{Ax›‚ºÐfßömß–¢qÂlñÙ r¿ø‹¿8Jj<׫) üTXŸÿùŸ¯lZ§—Ô0+Ò¼îÐ8´ç£ýhˆrОRoip×>äho¹—ÐÝ) Z岞L»yWiXjlÙÌÄâk3ø~˜ëò6Œ\Ç QØ1ÎBއÌ;ßùÎ×¼æ5Šñu¼e<õl*ºÄÌ)B%Ð-Œ4 ‡Ý´sÚ$aûßùy^—òœñ— xjì\ ®X§½Ë[7I³&ôÿ¯oÒ_v?ËíH€Â_W)Þ ˆ¡ø" ¸b€ zÊCŒ@`O9×ÔÝqíÜ@­}E0ì&†Æ(À"p<|ÖO X[Ýà‚]à[ªíõH§u„¢K`èZ9eÐo(4˜çE& ¤À?Dâìî¢q&£ÁämpO´ .«±AoVgŸ™‰ByJ¤µrÊ|q›aß6ç'Õ±ˆEϪGئï.l Ü] ìáÝ•Øeµ§ÍGMfO1É\ºÊQ½6¢®_¹³fL—\SÆT©{!sîé€ùŠÀ¤õÜbb¼Ï}î“a² Ko½æó¬}'Règ·œ÷Êò‘óWèÕQXQ¯.!f¡?ªvè¾n Ü- ìáÝ×%6–›„ÞáPñÔ§>54Žý¤Ñ*øvŒìU2ú‡ÿðj0‡×ŸmÆ¡¥}³‰/çƒj³æhþ²/û²?ý§ÿ´¡Æ)ôt¨ÉX x‡6NªÈ9ˆÈ_¬BgJÆ /i^jýÐÑO8ÑŽœM†ÃU™©4cŠCÃkä$ãe‹U÷ª¯Glãz깞øÄ'ÎRÓa»j —dhò³M3ÑîâãØ¢ÆRnµ÷„À’² ÒhaßUé¹[RöÓ·³›ϲn l l l Ü t^ùh{ÊÌOÐPˆ;°€c5þ‚•Y ‚›pgÂù@`O­úVâa€ln¡˜›…âíAáºK(ÁåÑLO{»†Ágl pƒïXèîUÀ°^Mô_sRÚ %!\õwÏ¡ AE†ü (ˆÊ„Ì 0Afæ‘ÏŽÖ¡ó¨ÑÙ«**ãô™{õsh˜%z¨ÚUãìú-Û‘À^ÞŽ”.ºM9‘ù‡”ÒŠ2¢ú;%âcŠjù‡m`Œ1ÏÞNþè<ÿuÌìˆÂèÙóÆÓƒá¥“¥†+‚@IDAToŒ0·puí2åW½êU®Â³Ó3 µùWÿê_¹ {|û„…ÕêSUBÜ5¨ °:»…¼n^ö²—õh<›‡âÃ/e¬§Æ ï{ßû™”ò?gT†[µÐÝóŠÊ˜Y±Ý®Q†Òx=¢cv¶¶¶¶>%@¥¯!ô<µßÆ28 A!˜ãŠˆ”‰£4³Ñ #ä?QX>Ø^ L ÚÜHðIcÍŽ«U4£Óe 7‹*žÊµP5ð ÄAùì¤x@ßUÏ»vY˼dÍy8 … åèw=Ù_ Z‚œŒ” qu¢ú×[¬åäf̵rÊn— ævS@ÀZ«ñWË—êàè4ãì–ÀmJ`/oSP—ÛŒ©/g’¬P•eÐJkç”8zJÁ1²Ô1·îŒŸqZw½å-o¹FšÎ„m³ö?jšg#Ý<È×,·õaÌö3)ëCýüüЇ>-2J¼&,^¦é1ÂýÅ¿øóÍ€Óe-ˆ=0)¶×Ê)í×~­«âgªƒûG™Äº*¦b,¯u”Ül=É\=rfB`V{î%þ“#f”°Ô ÿóþÏF0cB;Ø\{–;äÿå¿ü—×Ô5&,4ôÍo~ó<Å.l l l l |ŠJÔ®iHhxAƒÔ¾‚ @A@ÌÖà*A @éÙAÌ,N@ÊË$µÊR  `-}ûáp&„öA#D°8v^p~uõTìi|O/©i9çYf¬wŸ[Ðýg»«DÌy84@0Ќ֢=š©¢"gר ¤2sgý鯣 R4A]EœÐ'$ªAº]ßÑ-Ô+“ô˜ÆYïµË[·#½ ¼)]z›ò}=ð¤•ÊJ"V0xH}¯:kÊLŒxÄ#œ~;ÖÁ²W?ô¡½^ %»*̺¾5g+ÌM¹õÿÀüÀD™3:2¡Q¹Žœ½c»‘âÝç*Ÿ~A“£Å°Ìo{ÛÛ+7í!¿p{v|çÞ¦Íå£÷¥V›¥¦£™^ñŠW”ôðçÖ l¾³Òþ³öÏ>ýéOo4ÉĦ½Ìl|TZ–û~üã瑩þþïÿ~K>9©!VæL |<Ž,d`cœKaçg}ÖgÍŸ¬Âê³:·Û…----O Pòb䊪Ï2¨hSñA`2:̈€ V€Kðáè¬IAËo ž€¨êŽ ; yÚ€? 8‡îG (K¹i´°ì jP pÁîé L¯{w@”¯9 ÊÜvÍ®ñ= Ú0î<èRQÄ`å¸fÑœK¡‡ÆæøÙ>-RT=š„,¡L+«é¦ó]°"ê…€©ŒŒEÌ´Ãøûç–ÀÝ•À^Þ]‰]bûo–ú9´ ë[u¬º2µE§ƒ™1Cª¤a-E`³GúIßM.ͳÒ­ž²;{µJiРË"[÷ŒLeM€—Ë‹_übùÄÀ˜‡ÿ\5ΜGtH)N ¨÷Æä’ÉV 4f1gÏPrÕô5±ÜJùÉÈß=º¾ÍR8¿õ[¿u˜`óh‰rVkyÀª?ìàñ‰­B¨ìvL¡E]²22ñJé6ž3¦Ô*´ûé›+KgFæ¶n l l l ü¿,ª;_ÄÑç «’/G÷3ŸùÌi À°*Á%eÅ—C0IÚO l/õ€ì %`—Óið§=@‹EOüÿìÝYÌnIU>pþ÷ÞxcDƒ$€Ã‰ˆhc4(  ˆ ¨A„fƒA´…´´ ‚6HPl[šAºi›) 6ƒ “€Ì ‚Êѽåÿ;ç¡×Y]µßýuŸ‰á«÷â;õÖ®]U{íó>ϪUk­2\xu7ækÑn¿Š”1ZÏ?‡¸Ñ7ïÍP¼Yé÷ú^ÎQÔʃ9$þÂ$}LŒšAÙ rx´: ¢ß^å¬Ó¨1U3¨@Q(ET# 5)cùK}¢Dey\• †¤zÅje,ŠÙœRutÕ, ìK`-÷å³®ž_ö¢gÚͳV)+’r·ÂKÌt9.‚3`ÞþáîHÖ‰=Yϼë]ïÚi–#á¦6Ъª©i0…rÿ`Mô©mù·,êë|IÍÓžö4Ó.«*ó¤å%Cf6 ™?“ÞfúµÉV§äfÕ9§ôö ÂìVÌ´Pa\ ™p[ùªô€þð‡«CÞ,õ4`^E¨+Ô›,>’b‹€w5ŸXñMèüúêüëa‰´´ÊKKKK_ËÚ ûPÞ¾|°oþñúÉÔÇ’ãq $·”´A1C\*¶ÕI¡*„•µ ¤„æj¨=D…ÝåÒÞ£z¤9ûãè*fb„[®ªˆׯ¦Qö0n¾fí4,&{KJB´jC1¾¤Ti–—” 3 –MNÜ3Â`¢ ~)8€AèùüÇlJSü·–’Œm^M¥|hÚ€Ô¬È}ÿ…/|¡DaÀ=cùkª¢ 6i†kJ, ò¤Íc‰éQ¸üÝîv·â&:ž$ ±ög¦ÜVn±½©ÆX^=là”«â^ò’—T1$Í@Ø' A’Vßóž÷Ì-Ò—¥ÆpÚ“Oß®Q`Ÿz),š^“í¥nK'jÇ«R”£©høª¯ ë15CÒ]¼ýéVyI`I`I`IàkD€:k*¸íÓa<ðê>ØOˆ 6èào|cæ&Ô  ÄQ4PNðÊÉ µzLÔƒ€Š2Se×DX:QƒÂª=jCpÕñ¡¿Þ@KitY Æëvô+*ÂE»È7JÆò˜¨¹Î𒻤ÐÑoz¢R ¨EZ‚"Ño”ŠGŸÛP¦´è|†ú|¥ðP{(?V3ªiÞº¬ÿ˜Ñ˽(Sõ𽩓}ÖÍ™¬Ê%YkA8ËdÕlHÀA@'áëfÖ_óš×(gsL!^£9 ÖWÉÇ6î?YŽŸò”§1h y\~ùåR3XJÞ-ˆs·I~½m¨CÆÂî9ƒQ˜ßæÝÂDÕ{®¹5¬žæ):7 ‹/¾¸¼SLž±0CWؼ=ÇàBð!¶>»ß¦Qе×^c¤À÷hsT=¿öµ¯-ƒ";.'ŸÏ}îs¹ÊÚªoAXÎÕõ5­æéøà4CÉ›wÕ‹PAˆA4ž'ê}êm*Sv’t×ÜVaI`I`I`Ià«"]«¸Àì@Ôüâ_DP«2&ÜÊP@"¨$D€\òPèéÄ÷Dd„’êyQ•J´•¬-ˆ !5•õ‰·jÝ’rÌ"]—ò½êÕ"ÜÌÓWDŒŽ‘2jNø"²Þ¼=Qvˆ~¸J d©™‚Ú0h#¹+‘TŽ¡“þ•ºBi¡ºdǵ.é’CÕ©É΋ mŽ•+xÞB…çDõ¢†Uf?§±]…%›*µ ¼©;¦íAvvÒøÇ3×uÅ,€`Ö û26 9úË6V¾îrþ¬S\ËNßä꟡QW"­«f.äÌÖ¾Âa¡¼ì²Ë ˆírõPÖæ„Tð7ƒâ«_ýj.(5ys`§Ü42+Æï¿‘f˜´iL†Až"ûÿçþGؽž‰ºç­aËì¦_s` ýðŸÑ^¾o‡/¹·ÌÀ*QøûÞ÷¾:®/ê}ð7û"Ó¬²7RfÈî/ä’<×uû*, , , , |H8#Ðh~òSÐ×’Ò¼ùª×*¡™?j¨5žKˆ} Å‘ |ƒhÊCF%$­À½È …•åµeoíPRîv(7½Që@yÈ—ËO’Õ¡éšL/äÜ,¢(w$ÔO(€ªÐoÊIèBåêû×Xx˸L™¡ÒPlêí 7’ô¡~ï\Î]Þ] %O&jjX4ŠÙ¦¹yîpÕ, ìK`-÷å³®ž’@‚ÐD¨bë‚J> ªÀºòVïÆÊ9Ä’€± ²¨=ïyϽ]_.‰‡×ñ`a8<5ƒ©c­°©É—±>yá_uÕUòz)²Õñ¸Zx7C£6wºÓæÛÕˆUp«åØúj“Ó“ú&$W¶º2»¦eRòxÀ9Œdï—'<á è¤Îdäv’… ¶$ÄT‰0M,ŽlÎ\â'Y¹yÎ/o±ˆ×ÔOvºè¢‹ëê) ƒ“u3éÂ|Rþîïþn…ÐR©}~pê¶­’jÍ6³uq„ø‹¿ø °ò]Ô’‘ìÒK/ÃÿôÍ@htp9xí÷aà 2qõã.$+ãiÓxˆÉí#ùH¿7eþ0¨ÖÊ7ýÌg>ÓíôV·º•‚ö"ì¡¿ô_ÆJ\x·ªöQ²_'{XQ2k%_Î.,¯µ÷(j"YÇIéÇëàzÈÐþ"ø‚Uµ‡:‘IŽ8lDÛßüÍßìЪÚ;uïÏüÌψ©s¥Šæ]òA®î­ÎWaI`I`I`Ià¼Iüá qþDmÐ Àë*`ï@^ Àw/ðÏ¡|iƒú¶âP_¢ä‚b<]°E=•m%!¦ a¡­ÚjCgI‰‰à6…oäˆ"%ºDš542}½nÞŽŽ¨}ª¨–¨<{h5=tô“§4ͨ ›ÑóÙÌ$LC’¨Kc¦G!dXd,zÿåäŒôÖÿâéáô‹h––––Αo–IS×@;¹ÖÔsس³ê>Øȇj¨©¢Œê• ”Z+æÜB¤Û+BFe4DR¨ aUW VGmÝ[ PacÈE–Éu"P4šLiˆõ£f’š¡‰ˆÛÄbªÎ³ w3Ù4(gêAÍj.dŽšÑ/‘$U$ Jb že32÷Rx¨=ä?h½ç”³ÔaÔª|ŽAõÒ&ëv (fóí«fIà4$°„§!´cz øð]pÁDðÀ>ÐWÌ”e‰²äWµ£Å|µ/&®nyîsŸ»ß,–NFGŸ: Ñ<.œ™›5 ë „´³¼L öÚëlÓU@£ ,^<7² ž-¸ápÞš’¯ Ÿƒ:‘‰;kÑ’ÞÂ⨮né…·½ímxÂèb6z½2CcÏÃ)Õãh2 ’yF`þ³QtÂrùº×½.Þ)ÿøÇ#C¡q"u/žËªkb)"ýþïÿ~òô1¯¤¸§¦føk aªëë’À’À’À’À9’€|›¨dÀá|Ôàºó™Ïô6)?æ1q¹Î«V Ÿ¹Ýâ·ÐÆÇ[@?Y­aµJšš®þû¿ÿ;”ŸÆ2¤y_œ…dlËsat÷ÆHlnq­azü¾ vbýç^á GÉ×á¯Qºy~¨U³$°$°$°$p†³Òѱ±ªÝñ æqäï! €ö¿fˆ ÔšéÓ(è£Ï¹dáä^¤SÔ2BLóš…ň‰ÔzW)£?$X`n(Qö–ñ‰=¨eÒÞ eÔ¬·rÉñ\îÊiRæVÞ­½ó”)æ@1˜/U ¥B 5#KëL[ÏT µ„r’Œ¬Ô•ºq³Ô¦”ŸÍ«U™Óª Ô¡h՚ܩ·Ð«‡UX8 ¬áií˜ÞÂÅ?Pè/[#Óc¾æŒ ”k# tî‹I^²Øÿ6cöêÞ‚ÚW½êU½°“ \\21\µì…W¼â&É}¥"ñúÕ”“8'ËT8ûüç?ò íbÐù^5ŸúÔ§_Y(ç]D†F$Ýñ]r°k®¹¦‚ÝN&qAyìc;÷lôŒØAQQñ_,µœ^†ç⥚¯–2I/Ö?ë@‡ü2£™˜T󘢒ž›}·\U ZÇIõPütWåO›'¼j––––Î\6‹@nþ&,P8×U 0ž³;õì» tP§Û…&PÆàÞ‰VpQ[‚Z" 4dPÝn®‚P˜QÐYÏìP^Ò•ç"Zœ]{¨ž‘)JÝ” Ö:.!xLd²³@ÊÒk'áŠçJ˜Å`sŠu¢‡`‘ÊAñ ~ÐLê.ʉK1[Wå\ êhFíé2™›©©£»  -ªLWùPÌ6;Y•K7UkAxS%v¬Û£“ëQèfñ9ô7Te2äëG?úÑ}a9$WKÖÊýfC³CIÀä.Žq¥È¸ÊÒjsS€¾Ç_´÷¡!uL¡òSWq,v=Z€ãšÍ46IY&"¼FueJ!¡âo³ÞP]½ë]ïBÕ>• ´îU% \5ÆJ®š<8§ 8ÛÔ-¢ ²¯È˜Z^.̺ÙW4‡$z•àÇZŽl‹ùx½"°.XÔÝ¢¿å í¯!»rMc––––NC@5[a…´ Ka€ÜW,à:VÞ·³À{½üP (gÔ!ņPPÑÑ ÒѹYeO %Í…¼Âbè,Wš+6ÉPáÀÑiŒ:“RÅXsçºB¾ñ}ͬP³š¾ˆ¾]Bå;K/j€6T‚Á‚lJ”*DÍV3ejÆ¡D©Icž]ÙyÂU“YjOÕl(Ny.ˉW¹­R½TÆãw³ŸU¹$pS%°„7UbǺ=¤i€ Qþ*ø@ÿú–Ý&Utñ¡(w!¡MV¨–ÜQ  !öãs:mÇJ¶Ã€x…¤çlC »¹ZˈLŒf…nç6â~ã7~£V3¦S  {ò1æÉ,)“ºž¢ lŸX\'2¿UÒ0]IlÍdkª\S|ErsT kü$‹K‡ô‡g?šôC­Ðhw†D²ê³W™®t‚Ž‘rÍ9Äu*.ÕWm²¢¦¤ÒTc\®-G£äñ©uã\ –˜‰–³ßloLɉq¶R°ö«½\;&Pgo"Oío)`• ß¾ÊK§-µ P]!Ý2¾²žz(#ƃŬʕÅ|rº™Aë¯ “NäÕó*, , , , Ü €P@Z Zûu½rÈBPƒëxÁhÆ»QÈ˸ªD€BMdbˆ£Ž̸COÔ£ mús&7›Y!²2Í!;”‡øÜŽ7å€4³pJ JõPè5›Š7‹äC®žˆ[3$ŽÊ7‡PSÊ• á'eñt/åÁäc>¦T„Öu®§;Ô õ‘³ßÌÕø™Ææ‡ÒEõriG¤$Wƒ›*µ ¼©[í¿Ì¦ä©Hôdžñëñßò–·”Yq–]  ýÌ—zÍ;ÞñËX³¿—xñÅkVaèÌ¥Àº»z¢¥°ÑíoûÞÿPN”…Y[‡K¾"Ñì¤ñA¢w¹Ë]Š«xïàN9ð÷áø|»dž:*-‰v¨LOaQçóÆ7¾qî! <|\ަi“¤m#Ž:a(Åjä£#z®d6X6Qš1Ö:K*¡ƒºÒmÂ;]ò¡:88 l·è*ff—تÝhž'žúcµç¿j––––Ixn[­R*ç$ÅÁpà ¢ûB€;v( äÐnþdͦ+4,ÊG±Âó†ã‚u‹d- ûS ¬“ÄuŠjCpi–ÃÐß!£!Ò4OŠF‘i9Ä"YTkP´« FÄ}è”Qv‰ÏW«† Ž©}‘ìF C9a&E¥¢îš ’$Ò£¢ÌW{ÍÝï~w#Rxze/“?e‰Ê¤Ùü)‹ÒEõÒ€Öo_å%3—ÀZž¹ ]B° “,{`“]AX÷mH%êbì|á _ؽü#5ɲ= å¾»…ÆÙK|ÙË^¶#nýëÊD]÷fhOªè!òíNwºo–ÍM<(+Ý‹^ô¢ÞO•¶GcSŒ³¨¿ÎhJÓ‚#¯»îºŠ=ˆnóÈø.Üdˆå½r.ÿʯüŠ%ª1ï0ÙZ)ii¶6Ö^ò’— éX\]=Ä+ÆÍayÈCú¸Ð8œ·"úô€Ÿóœçä ¤ÞRY¢m ô3ÔûÊzʪZ}šY¾šd­¯[FûÞoÇÊyÂBH˜Å·Þ‘'M‚Pð=ŒEÕa!2(é¡C¡Ù'¼ÝínWž?Y©bPl„’Ë|Ë?'~>zè=÷¸”>ÕU^XXX( €ÊŤ–j•ä^ðûfYK ¨€6èà`¼3ˆžûj$ R(ÊF(q$ª• ¶HtcÔS†T”„˜ÐSÍÖ Èks0¤‰øê‘«€"e=NFA¦ƒW*Âui“4uU–\ô]=+ w“Dôõ¤:¡P†$:uéiC¨šÍBÖ–;gYå.êÞÊw‰„©@¡ éwÕ‡²Ô—…5[/"\¬M¶9ŸU¹$p&X Â3‘Þñ½7˳ø=&*¯ç4; n'bÄ=³S ï¾Ç=îQ ¦ô³A²çÅ?“§Ê¾4; +¹£”'Ìf{&I#¹!wvo¬«ûhY4YïÌPæ±ìUò³¢uät˜êúº$pc$°„7FJ«Í(ö¼p´Êna|, ËÔÇᡟÒ΀'ÆàÑ~tÏ®©eðZ¨ú¦›Gç4öÄêjb­|æ3ŸY5s!qX#ʳŸýl‘ú3œðË‹#éFjwËì]…ƒ9{ôÊ^β™O™ú”9ÒBž´Ò©•Áµß®üÔ§>Õ#ã±=DäðA8]µ57x<êQRo樫OúÓr²Å§ÈUc£×–£åe]Ø nÞúäM”wÊõˆ¬x$g©0ÕõfWVOñz¾Ö_ÿ=*éðŒëë’À’À’À±•` {ZV¡Ã)˜¶ ·ìª  ƒå¨Áunà`˜ë °×ñì×>$:@ ¨¡„2²†D"U©€bâ‚tPªI"&ô”E ÂêwUÁÅ1'ynÐD…ÅG(2å¥NâÐnuÛ h:3GÜ艗ô;ŠGôdßK @¿w(S<Eb¨¾f¿”Z2Ô÷¯ž49xJÁˆÜ¨@¡Òr eÉÕî)òÍ(Zú¡zmúõqWyIà4$°„§!´uË T†É¬rV$QÁͳ€ ÚÿñóZéûNßç>÷qâ-#è,åìþh³Ãjíµ×]–³¾ VWS@oè°–KÌþ碢û±«Å›N²?&À`¸=_ED¤“CKü¼;bú=,-›Ÿ &a™ß~è‡~È@‡H$Íðƒ2¾Š0)…€è²=+ʼ?,”t{×»Þµ{‡ê-3´šÄüüÄ'>1&[r aS¶X¯µD‘ik©/Î;ÊíþÒ9°,ßžª9T@Ïòw›[¤·þ. , , g CXë–CÈ©À‚Ù,ðÒ gU–Õ³mÐ ÀÁ¸zØ ÈkÙ÷Á°µ…,P†»ÐGjä’D&è¦ø !£ì¢'wÉÔCçüNc<Õ-ÊC|"hÚh9Ê—£t™Í½¹$«1¢î«%šÖC([Á‡(:ZGîi†îÓÉ`B­NûÙÏæÖØ\+‘÷ܕɻ·ÊÓàýï?_oýÖoM·þzd,;ä¶Ic#j ›Ü—¾ô¥Þ¿TrlÆ%+œÊƒ¨â+³K'Àį}íkû]ʨ‹s‘>ZåíDuð•“L(SW јة•ŸÀ-²c«çÔ«’fÔé¼kÇx^ŽßÑ![²ž×gI`I`IàU ÎÀX5°„dp Tû¶ÈM|®#3ˆ®]/Ðõ0O…® .¨Ï-À¿›Y#g4,4C©A%%]éÑ ›ä/­Wƒ“}IUeÐR+†Ò ÊC|è¯Ú($ qç(?ôê^r¨-MlDt¬¾>Èe#îÞy•=º'RÔ_•s!kË?ú£?š/õšh,b§œ˜|…SšG¦ÆDŸñõHEÏÞŽ–Q™ê‰ª@Å¢håë!¥Oo•—NCkAxB[·|EaÉÍþê¯þª«öÔ@rl‡…ã‡d—MÂ;ßùÎi €stƒº¬Y™ô³Ôç>Qf<[ö7£âèÂïeî¡jçÍ—^÷Å¡ü`â%„•cO*çÛ!ƒ"“m85ë¨êYÁ-üRœºëÑ"@t%öãU¯zUÏ¢Y\@7)“« ¹•üä`¿dMr3k³ZÁÖèÖ–·¾õ­5æ8ôÏÿüÏô‰zºˆŽI²gxãuo¢ Ä;ˆaˆýÑU“u ‚»ò‚øêüÚ¯ýZ=WîêMrÐ-jn«°$°$°$ð ) ×quHT˜X x*Òr½ À[ xsŒ^îËåIb@;wußNðä,öâ»DLx(Ãèc˜$ŠÙŒÔˆ¹=uúC^( ‘/x.4‡ìz³¼_´èAPä!_P·$¥*ªE¸h·'çD[¨9ΖÈzçÿLÎóÀJ;m( &CtYujIýÈ ¢tŽ£®PZ¨.•w ÔL½9ÔUê)HÔ$ÊROÞU)*V²ÈRºö»ZW—N[kAxÚ¢[7~99 Ñ8 xup„˜.‘Ðü7öå…ÒˆÝY%·°‡ ò¬ÝwQc¦S^"+6{NLÂ}ï{ßÍ«©têQÖ«|í4‹Ÿ*âòñŠW¼‚¡®¶¿<#×—˜Ųêä÷ÿ÷µ”jl¶Ëæq™‚>Q‹d D„yüÍÀË¢c£ÊœÌHéeÁ†Kú 3ƒ~âŸÈÌå¿V#Q89÷üu®–oRnIœFfè¯'bú‚˜‹§³åȳÙW¸Hîê$G]¸ôÒKÉ$#®¿KKKßtà®ÖH𰃡¯ ò­o}kb¡*jLu`Ø&_e°Ô_€Ü%®éiÌAzN€ò*]ûýeÔŲ@µ¡‡Jr°-r™éÃÈHŸˆ =ùŠªÚª9 ³0‚ÍW2É¡ÈÍ*“ÝMŸ=Ô#btŒ”Q³itï͹+ãÐÉÀ¹… +ÊÃ܃Ê•ƒâ¡+Íòñø”rž7î(3_ã¾bßì9‰g(K×õ}å_ÿO¨XYç{A›=¬Ê%3—ÀZž¹ o¬€ „ñÉÂo2_–pdÂ.B”lZû'?ùɇÊBé@Û!žÁ舡¶½¬r;tvIXy?|iî9½É÷5_ª.4&†™NS‰ÆÌä~÷»_æ« —<†í°O#í±iZh£º ,—:ñ,—\rI ¢¾úÈœ†õ% pL“¯\n6ÝJÅodÅ(–Ë\pòî’†ÏÉ0¢¯oxâyHVž«Ì±X6kàôðs?÷su‚…6Î,V﮲^ ÁÏrI{=…,o"ëŸø‰ŸHWù›-Īa]žu”yª«fI`I`IàëT ®ƒ*ô`H‚ʸ_)8ñÀ&Ó‰K€7  ŠK P¨ Œ×vxwÉ]¿n©B">Bé} Tâ+ZÙ DC y@Lè I%Égz@aˆ ùŠÚj¬¡€5@Žó‚"S”š‡M·í"ß2#"åØ@ËÓu"_Q¼ÐýæÕTRÚЗv¦Aµð"<‹K™FþêÍL67NÓ!eFKŠÍΠ¹ÏÞ(K}ˆ”)WT¬ŒNé:²·Õ`Iàô$°„§'·u× H¥Àâª!§ó djžô¤'±Áôý]8½IÁ¬ýñˆÈ–Ô=ïyO0Úmu,¬¸GPx rYìeûëÐ cR5¨sömx?ò#?¢Ù`ŽÕ'Æõìœ|ºÝ—×ÈQ|¬%æÐƒ|‡¶4åûÎ+)s2òÔ£ðe÷ŽNpˆÕ’VŽ99¢T‹.º¨–mF÷ÃŽŸ‰IzæÖr¥RúqIÑ0BúÍ-<‘jG1ËQv\Fb™ J³!qLã}÷·UhŠ®Ð°p‹y!}è­­ú%%%¯ €5àÖ—[ ²ƒ¡2x’E"À„ÒlµZá$°¹ù „=>@ìw@{XõöÜꉡƒP›Î}…ncžC"Ù™D+Ã]ùzEIý<$„…¶² —´+úì§õ®Pa2“!ǪGš¨vçI’A²¨¶SjnAÊfî)ª‡¹€ÜÃe3ýõÆÙD ½º…"Aè.” *ÅÈ”~ïf915›Í«UI5¢ 露¤çùC¹òì©ßLÀ^]­Â’À™H`-ÏDzÇý^¶±€”liñ똱ŒáSð€úg<ãûò‚õYEp›Ùo™E嚉 ¿úê«øÀvþ¯’’ñ]1.@Ÿ­½§âj&ýw¯ÊIŠ çè‹´|Ó›Þ¤¬Ÿs ”}˜3ùÈG¾ýíoÏ:êîw¿»Ê_ýÕ_:¯¯<‚4è J™@'l¢å„s¢ß›ÝŒ½³ç­%bÏ­R¶fp8oÂs//«?H‚+ ëØ§K›Á÷lºÜW0¥ÞDnÐZ9‹ªdùæ)j\™ñJÅáå?@_jYyJ31Ñü,Íõ,«°$°$°$ðu-€Ö`ºÉA"`Œ›¨«lOÚ·,˜¶ –¿@—Å0 ®v—H«öPyàº÷)‚@ù†>µŽPzo)£žl¸·óPH U•Q¯ì›Hmî!5¨Ð½hÑW‰(ÑewºA¦y.ôºÙ‰ɪu'=©‘»ýf'©¤$díGm <ômIªƒš‘t5ÉdC ÙéÍ%jŒA ^Ä7ú %5ip¥Q™—U iw6$‡n××%›*µ ¼©[ío x^\'þ”~îÉã,Jáwn}É)›Î½9Þ m¼ímoëõ “O{ÚÓ k“‰¡äk®¹ƒöÆUÎbÏ<7—XÕ,)Uvöïq{Nnèë®»Ž54œ ài4à G0€Í±;eýéOŸºs«$6Ý-¨nmæÖœÆ;3YzFÿò—¿œ²#,îÁO¬qÄ»%n3¬mMê+uSÊÜù|zL@$àÐ^‡ðÞâ·ˆüÍâñÏ šaØw£CŠˆ¹Ñ·ûô)WÆN<ЧÍdØh5bˆ‡‡ý “áJdÜ$tfDBY…ÒodH´ƒqõpó›ß<HR€ÕŠÚ%'àóÅ/~‘ ¢Y>&–—‚íJ=ª4iãuìgßyMëÒ’À’À’ÀWWà+ás×ÃÞ RKèe=;cIP 0 )8̓ØìÜ‚nP Á²žAô¼ZÈB€¼ù²ÍéÄÕ) ddÕgb:D%¹ ˜L ž mF³»%Q÷ˆlØ®¬—bÄL¦[%ºÌI÷Z¢Qdj&ˆµnìD½âоiœTÛ8¥ß«L   P І<š—Bm <$Ïp‹¯±«R?æK½†“7^ÌØ¯ö2¥È¸ă—l{ÁËÊ^.Ek™J»èVù¬K`-ϺH]‡‰„fäã4ÈÊK°ƒZÎØe3;„³%µlUýéŸþiÕlD8  ”sˆ“r³h‚è¸ÜtÜgƒd}´½™<19Zàî›ý~ê§~ÊC‰‘Øœ’Êø^òý|ô£eËì 9D(Y™ ̇&…ÀÄf ›uÕ'm#ŽCÒm÷>Q,†s5±ìžqŽÌãY­i€n±oqh(!T…ì£öz™pHÒ[K”~¿4”)0z¦Ì õÃWêP6£ œÎ©?ÙF¦VQ®ÔR´†Û××%³+µ <»ò<޽ñý€VÜKr"Ÿr€¬€­hO2›œ0‰w¼ãÿ÷ÿ7Ë‹‰fü7æKCM–g;éªÓ>ë+^%Nâ{óË¿üË}bùÊiGÐvrc^vÙeÃ(ýkžÅÊ\úùÏaXؼ÷½ïí7Vµ'̯„ÃZÌè(>¡R˜â¤dX_7…½èEz@çâÑ9±\xá…ûntY°ÎNn¬Ó‡•!VD:o¤¿#=Ëp0ܘ䢞]K.9—¡´Æ¥—`¬,A2MI?µÆ£ÄÔ(QJ?ÓÛç*+û¿ýÛ¿ 3Y_—––¾%¬†ÁŽ«ÐCˆe\ `ÏìS š%H¶ · Å1áõt£ /€UÈ( >±åÀ_ŸÆBƒ“1} ‘Bo㚢A7Yš" áÆúжŒˆÂjq…Úš+WÕÌ –±nO™:œçÙwâ/t…Ð…ÜQ<¢ïÖjje€J@1ˆÏí¼‚f•ã£ö— ¹åŽw¼£q)3C¾R{(?T "ÄH£ÿWQSJÔsŸûÜ„ì?é<ЪY¸©X ›*±Õ~C,÷¶¬FRö÷w¸>·©g™ûÉŸüI +°¡¶æC)282¿Èk^ókßBt»¡uøñ¼¦nDÙÉL¬ÏGo ŸovVc¶|øÃ^] …ø²ræêë«Ù&ÿ8Îà´S¤‹ä ÇÙåùÀ ìUŽ u{² Wä„é Ť+¥ˆ9÷Æ6Œã;ײ?þñïKe=‹ò¯áâùÉŒÚÝ`8ÿÈ¡Wh£3gFÔ¤”·\§÷’v~—»ÜÅ«w5©âüs}Å©©J;ÊGŸÞ*/ , , |µ$¦j]w ¿N–€[eQú Bá!’ña ¹€P@Z``Ál÷íÂq~Ëý‘vL¥™Hì}ìÇŠ‡úhYä®ü5:BA+YFjœžQO¿±—ÚB^( ‘¡3¤Vk‰ò²XB‚;ÄÀ„ÏÌËìŒS“Aâ¨<®4õPŽôQ‚çÓ˜¨MÌÃʆšffņâQ£l¨.:¤Æ”‘—zcDªŽWŸ·œ)… MÈþÔ<J‰ŠZEÅÚkU. œE ¬áYæñí*ΉX€eýY_‘ï—G<âÊ{ØÃ¤Éæ³ÁI£›ÄP)²‘6ÍÑC\J´ÜIßAë3ÅdGúñ¸Øt>AZù—Éx™d'ÆõA`2’Ùðœ¥u`½'ræìæˆ:Œ µ§ÒZÊ®£ uFe†IdPÒ@Ürb³”êíkº1 ë÷Z^(R–3T"ƒ ¿6-É-™ Pò°V”6@ò´Dæ^´Ä¶*¸Å#¸z·»ÝM½ìçÃ)Kv×x ím¾ùÍoÎ *³žõ,ç‡ÿÒ3{¹P“`¨ÆSg9ZÓN³ü%‡aÄA¤ëë’À’À’ÀWE )99;d)ÊÀZ­ÀÐë[… 0‚Çl^L° <; ‚Öx^ÔÓ1çýä$Ñ€ºVFãÀC §õ~rDE¦½¾ÊÈëd/DÙˆ}×*”(;ŠGô›&Ѓ’PýoWBå(¹m6S™T7ÔÊ •ÆÛì–Ò üP(BÔ!Ó#|}z"åú”µÊŒCíú%³%µ <[’<Öý¼óïdµ$(&(ts´«h¾J$]+ö3àxð! z:“Üãbõ9;F/C|òàaÒs¦ mÌ*›{Ìu×ÌEä;úÉÉNåà€æB3ôP_ùx¸] øÎäc‹í‰°9Q øÊÒ3Gê¼^üâHÞí¾×W °oRÌÕ³Ð!xª d¦Ð¬¥ò‡Tû*°Cß÷}Ÿ»ŠS)+²ÆÉÒÆìC¦Tƒº`’QG°oq!d I[£Éz@3w¼R·¬×A3“™ãdjΫ°$°$°$pþ%” ' i ¾°I*Õ:p—cT‚A`c,e 2éC\…„±QÕÿoÒƒb€ –sÖ“nÌdxßN,±|m€? @è K•ù ŒÍõŠÉÌ‘NõÖ &YátåÑR+WíQž«è¯ßÛËH3B@£½¾—`¸T¢f¦‘õWãäj<ÂGë¥rôNR¦P 0þÄ84Ž3-•c¨ï_MžÒ’ã”êÿ@¦DÉ¡êPxjÛЬrHÕˆ‚T3O¡X2j«´ÊKçBkAx.¤zìú„ƒ•ž$p6 !s#¡¯7ógÂeç !ÈZU¦Î6¿ð ¿ ‘šÓrgXGBpÓg8@o~9#a޵è-YOÐfÂû߸ñÉÉL04Sßóž÷<óÇ߸ûáô~ø‡$³è ^ð‚^ßË ºá×W¾ò•½^™1XF8>3Ñ2ºˆB£84Nˆ?sïPŸ¯‚ø)¢#–Þ€Y1£:PGæ{Y ãìø]^.5fK¹ òt-W¢ô`zYgJéæè§ÚiL²¤›˜ ºÅ/ýÒ/ÕªÛ¡OŒ¯™g¦—¿âì«MÕÓ„²]9ÏyÕ, , , œ7 ¢,Ì @Öœ ¸8@—–ÚÀXñ@bP®2Ê€Mà B5§æƒÜøwaP\ûH PƒkwîM Pï*Øþ=Ö5$jÝ<ç$g‘gE7ƒxM1¡§<š¿f‚¼Pذ«éFd§=Æ GšÚxºŠ"FD»È×(ˆ#å2#ºe#nREâ¾z´Á fè-±ñ9 q¸Ô¿R0¢i ‹sm¨%”B0îø@™¡Ò˜É¦%ÚüÍR¤ ’r}ú©¤\í—û1ô¬˜]ª´9Á™KîëD³C;–Yå¢óLé‚0&Ï<B%ÌΦ™ƒ —¼ˆ$£Ãµ¢;ø5Õngö®Õiîò–³FEŸ¡ç!*F{9ñwê¦e½Õ¦bYL€zÔé?s&º˜Ø»¸VyI`I`IàœJì·Dù  à3°Êׂ¯´Ôƒ;ú`Ž:Pˆ’¦ 0câ¡CF10[{Œî  cA4 ÖÐîï*`ï577c%R\Z;¡•À/¢QF:ÝÆ‡’2±y–––ήNmÍ¡ª:׎¸\ªXnj¸z ¯î‰uô¨˜.Ï:c¨‚V›N\¼à·û.ç€'¸®çãÀ¼&æFPðÁ~µÑy™=š½®*˜RÜPQL_ !#”„˜²^ÚÙúCpYäììÅ!ʰFŽDB£È¥vqEnÈÝô §œ$¥¨|¾Ôkc¿ŸÅšRAµ0îðÞ)!T I·Ãæä-ÿú(›åìÊR‡\¥Õÿ–< ¿¥DQ«v–Л¯Ê%Ó“ÀZžžÜÖ]H¶.Xß¿ã„}»çÒK/Õ½mÜêl| Ò¸aõW¾ñ¥‘¿‹m²liARÌ$]+éÀÀ Ã.Ùì3•‘…\{fê¹=°\ñŒ¸Y¸y˜,sø¦oú¦{ßûÞÖŸR³ïï!Åœ6ì‹ó©A{ÉãÒÓ !K䨒¿žqý•<Ç¡76Ô̼‚"IvoßùDÿ²œg™‹µ`•A¤t…dŽ­ÑE¼ààÜ娥¼šáñ˜«½¶£µÔÿ1*ñáIsî[ˆh¾O2mD›ôÅêðàëë’À’À’À™KÈ€š`Ný_Y/¥à—¯”%Æ[ĺ²™¦=0ìKØØžê)8MTvÚû rËÄ–{Ár" uîÝ1æ®`&ŸPÑ6æÊ\¡.—ØðØ>å†.G =äòËÕä‘·Å¿ˆcÏÒg¸Ïa•—––nŒ x d`óÕ®‘ý"0r |±? ¸¸\ èé öѤlOZ»%×7s/@ÎnˆÔàºæÆ9Hw¯á€|N9¼éRlµuq ìsfÎÈÅèaÆ”dDC tS½) 6ý ¹C 9ݢȚyõ™¢TÄšqQ­ÊÁÂØJ9˜H|¾Ôk’5”JJJUÂPyz2(pæÍÞ›2EÅ-ÚÏ+Ì¡%å‡ Dòñ,£l†ó7ï:J”¯þ3 ·¯¯KçHkAxŽ{»e5Ì@¤õô%à!BI$úå—_~¤Œbzä°qdË çcó¶U©´Ymû ª"ïÇ>ö±N"J‚²CÌ”¤ÉÆ^ðz-*ŽŸŽfüàû YviL‰]æÃ>ê©ç¤Þ&l†}Çš5ª63%gh÷ÆV]š‡>™¨¹Üàì•£ °sÿ×ýWŸ°rúñÂIxz¶ÎÛÔÇ̪õÁ~ðp£¯*µa‡–MùyÝÿ„¨5 ¯{Eb¤ž‹”8R}SW2³I@†µýKzÝaÉ3vOW·ø°õ^wÝu5Ä*, , , œ¡@Ê0j€O¡œ¯ ©l‘ p/ æRMĺx„*À>1ð˜6l*ûœÕlñí`/p.<Ú €ƒq`ž Æyu„²á‰ÌA  dQ£›@¨­"&d[áÜ={ÓÍUe ¨!¢ÅNÖHu’a`LË‚Jà BõN³l°!‹º±ë^@ ®‹ÔÌ$gWh6Ü«àFt€PC÷tEè#nüßömß¶ydB¦Œt‚˜ÐSMl( 5m\Ú˜ âCå’ùû‹(Ñ%ÒzÈW$« ÂݼZ•([3ô]5C|Pÿ<:%ª@aèщԉd³£` ý _I;Aõ›)»‡ÆÉ³¸\š-Ô§ÆhJýëë’À¹“ÀZž;ÙÇžã@Èû?6°Âú=/v)úôèöCÂJjГ„&fHŒ2w…„p+¦TÔݲ‹€Qf8`paÅß™d?âbîYÄy‚ÈæÛ\€¹ôôÕi„æG\œ|†8½>"W"b4Üg?ûÙ^ß˯~õ«µaFMbd/ÿR¯Á÷2vßù˜ &Õ´œ·d6%Ú¤²ÈèÊ"?A G2·¤ù%_º<‘hTµÍè.¤Þ'/N#“ɈnžQ³¥àuC‹ÔïÞDQ›ú{¬§K„•«‹h•—––ö%@úJo@˜€O­ß\P` X¬²‚2€<qš»žf“þA%À,N7&h­l™ ð–¹Í`8zx¢Œhó^YŽ‘0™rÅ×–Èeè}dË¡ ÝÖW3‰ˆSUt–‡æxÕö„p€!¢Eäh;ÛŒ1 n?oœ)…µÑwŸ ruÝ÷·æ=’­q©óšYÔ £ZôÞ6ËIsdÂR÷R{2Éd›£bøWB(g'áÍÑWå’ÀY‘ÀZž1®N¾"~6ySÉ=#í²¡´³!VE¨î=´W͸ÖhyÇ;Þ±WÎeÈ$åÓX\8{¼DbËŒꦣNï6±;žBÛãósíµ×º‘ÑQŠp=V¬â6ÃGèŽ{s•ˆû†²¸r¸ÃîÐŽ9Y0F$˜×!ПòÑRä!“ÚUW]5t›¯|àÝÛß,ó³Ä߈ÏEM ß­¼hÏYµ˜t/ ¢`RÕ§°¼ž6ñkõøâ^jé½Üç>÷yö³Ÿùt/\JXÌçyœ>+5ìå}ñ¹ù8«rI`I`I Kh€Ž@Êü„vœ &¦j)¾€X¬%öÐ;c>«+ð$³BÈd@hÒŠUÐ `Áì°%hz¹O¾ÊÜU`Ò«Ôüäw©GóD¨¡;} ô±Óy:DCÚÌGÑÖpiC>õ¤¾">ô‡ãÎC*QäN˜l© ±è5P¼÷N$îÙ:Z¯÷b o!n±Ô€ÎSÕI/P'ÜBµè•›å,ì©+›W{e¶L©@©¤¢ÈŠú”š!¥÷³ÊKg]kAxÖEz¬;dÔ AÒaù¡ž2Òaõï),>3ÌiˆÿÌ~cVÀÐó~àŸN^õªW-1»&Ž\N¶Î†Ãvh4ãsØ‹gÜ sÊ2,öX4ù 9P!zÚø O,+SÙ¼ƒÊÖ›µå‚Ò§ÇÓ)΢›ÆEg[¡Õ¾¬RÙœepèÊ>ñ‰OônS¦Ü$§"ûÚí$%z†Œêßò-ßâd ˜ïÍbòä#žøÃI‰ãÐg>óšA¬ÑèHdÇ÷éÍÛh y¡°Ì!ÓFphÙ¡¼>U„˜„¨(²×÷2bÕ‰É÷so2š&%”¸Ñ÷@FI†9¤S,5`î¤×$IŒqï¡Þ&eʉá(*û˜isCù°äÇH)Y9³õ5ÛËóˆ«fIà\H`-Ï…TuŸl¥ÝzÙ³ɹÀîÁµcSpYüÁüÁæÕ^™´Ÿ}èC{å\FB·¼å-M¬{ï˜R2MËlÖ5Ó˜å õÐÒ‚Ó©f,‘óX©ašÍòø­Q^2†ÒA±0 Ä€žßÿþ÷ñǹ”¢°ÚÁ l>¬Ôþð‡7§„f²hÌòRco„K× î|S~J½ì Gºú‘"\äLiQ]ÂÄ"õû])'˹Æe,ðÀX˜5¡L µff0NHªK¥ÊD1òU´Lâ!OÎýÄŽhOŽÚß ~ífòyV«fI`Ià˜KD$H;x@ C,ù²c–K`ø‘jæk dÕN(Kj—@ wé3Aâ,yà© ­£4³Àäfko.T€v"Á80éqì̸|àh¥a‰ïŒò\§‡zbøCF¹ŠžªBX%·ŒˆÔPÛ¡Ä›ñë!î¨2Œ˜}Qêó)£fNÏ þš*·7‹Ö‘{Ýë««ÑÔ¥¡@‘ÐòȤ¦î¢œhIQz˜¿RxüðFjJ”"÷n~¨Rs«fIàÜI`-ÏliÏÌŠ˜ êõSÈ ï˜6E«#63*7w±ÁÙÄÓÿ õó×}èCZ âß\Éôö]KìÕ+«ìðXv»IX{8Že™]±f§@B#Þüæ7W'CÑT˜ø5j/Þ=DËûýGÃp—Ðvž0€â;”£„ïÿûCô¯!3dshûîw¿›EÓk’¬\ÚëD`f,ZNÔCÇEbµ„Ú×–/Ë(—§ÄÁ§ŇԙË5·dë!Éç<ç9ÔÖ(f%Iƒ)gW²Š9€(.¼ðÂÛÜæ6Õ¹™3çË„^:“Kþ“x¨´éÒSà ¿³„®é­Â’À’À±’Xœ :€ H5PpÀNÕ¨J )·«‚w Ê4k8î€èË"mÎ}0Áf†Ë_  Zkë)Jà÷-5éÜx-VõÞ<¨7sü7_1²ÈúdߨŠzô‰†JBL¡ƒL˜( óU’›yD²Š]9ÎWSƒRõC€µÌV|Q0"FÇ‘d†öe#nô}h{3›o€C#¦ž ‘$g”Šý–®†øâ¼ß8»¯”*EhP0ò'¯É›C°?ĺº$pÚX ÂÓݺñ ¢ÄÃÜbÖÂë¡€bYþœ´³CýÃ7‡h¬Ï#{J•¿«_êeöÑ8´°köú¹lßÏœjmjå0®#zŸÿüçóÏa ÐïäÓ&D5Ì£TM6ñ "Nbâhƒáóö·¿}¬ËåëÆ¡Àxƒ÷!êöNÅõ,ÂW¢müË¿ü ÍCçÝÄ‹ê9̾:Ž–ŠÂ1œ¦õ±}LžîÚ¬Ó? Cº…RhÈ!QŽä/U€÷θ˔©æ¯µ¤§W%ß=‰26g=œ–ü¸8båU}ô®yeå¨3ˆk}]X8†1~=:\ ¹ ¨éÞ逥¡ÀTY²ÀW¥(8@î€^¨ ÃÈ<ɾYÄ)8í/ØêÐ$çCkA4 î&¹0Hì:1D¹szï¶ÊÈBÿˆcsï1ÍŽ6h` œE‹‡*òÊR§¶kˆ^@…ºB‹%‡~5å:zÕÕ"\´Û_™PsœkvœtҺמümÖ’ }‡Ð«%µDŸT”ˆDï’C¼Ý|é®ùCeеúÆì7ÖVaIà¬H`-ÏŠW'7@rs1&GhG=6Ñg<㬘³mLd…8@,zp  ¹°o0ÌÖ—+¯¼RK^=[oP÷¸Ç=NË#–uM3ôŒäÄKˆìw@PeÂtÉG®6,•€ºcd¥2Ó˜±“aõSi_Ð^2b›^«þ2͵;¹¨«,X™uo.§ßò–·ÄÐØô>ÙY³”²ß)™ê@wéÛqrVÕ§ÖuúñÕã˜Ï|úŸÞ²ê;!“=£ÙË^FŒÆMÒsR-§V¹È…Í”A·ü¦’+ÕXuÄs´ÿCúþ3m€©8fûZÍñúÁoð/E퓟üd—Ã*/ , 7 ž<&èQ`•B xé>À'Ù>bSÓ8§ù‘!È\…Ò ¬·ˆÜ=í † 0`Õ׊õ^Àlº¼U }νÐ À;VääeøÆ­Û{Mĵqôú”Q ¢A7} óAIˆiX¸†[­;­ }"Álœ¢ÅáR}E¦d…XÑkOˆª#btŒ”‰1RMŠÔº}.äPŠ[çfj@ؼÚ+©%æCEé•ÿC(3Tš!dTcʈ"5õ¡2Qœ|ÝÌ—Þ‡Xå%³.µ <ë"]~™£©OüF ìÄÇ—€²f@¼8:Íhæ+Xü­ßú-‘ß‚Ôa«JñÃB±ºª‚á†âàº4d“3 JÛÏã®)êcsc­ìÞ2&é©q€˜ûØe‡AsmyZWóÕÌi'9d ˜ ?Qœv d¸|”‰K$ÃPG½¼žêxÆ¡Ã/|á 1„#õá’¯yÈCÜn&5 Iî;<—SƒëB›ùƒQ¤T=…ÔGþ_š²j––¾Þ%à‡ïç_‹½‚…^ ñT¡ä*À‰g zÄ#¡R? IŸ` XU'@ ”u¯?@—µè€`06D·€G ™¯`s–0€ Ñ€\WÁ/Åõ :±›·ƒw£€z€?w®A  m ko€PÐ r) ÐF ±yuÚ2¶OÁñ}EˆucP'E£}Pã’@¼r†3ë³,?r7/Yg¼—M¶ªÑÌÜpÞû‘¡AÂ2©(ê ¥…¸:…éÊWJUG™ÚSc _]¥2yjŸàPRZ…ó&µ ^%çÁ¸þáÝQ‚à÷â’œ©Á—‰+ã²ÒíµÚ`²°2בr&¬~†BÎÿݱË-ØEçG&³~Ýë^§“ä¡e•pñ OÓvKâÊX+4 Gÿûœ”¥òþa‘¦áh”è;“ôùöoÿv6K¾L¼bÈêV·º•Ê2`²ò59WMé}ï{ßp•L".˜]%M\tzRSRùä+W¥ÙˈÕOÿôO»ê¥ô£õrÈj|ø/yõ ÂH>÷¹Ïõ¡I)ÚXÑ*Ò¥ 1{ga‰5ãŸCùÈYôD§'õ3Í“ô£òÉë§þPÎvb>û|VyI`IàC~ò7¿ùÍO¡ÀT #8Ó—[‚šé€Olš%ДU6,ðĺÄ@\âå€^\732HŒà1! ³üNëvÐ_P` ro  ËÀ98 ®gÇEÀžÇÙÉ« d2ú@"¨¤ÄƒbMžnvX­©*ž»6S§V3ôgJÄ[RB‘‰Û¼o<©Ñ롘I¤œ[Ðtõ¿Y@ô&†ô7¯öÊIR$zå\&«„ŠPNcÓ¦Æ$^´,žá».½¼ô²‚e°¿Ô§y¸U³$p®%°„çZÂÇ´ÿD;ôãÀ\h¬VGŒ¬qíyZáûùÕ"$¸©ÆT^W_}õææÞ§>õ)mXv{úækxûÛß®OP^™6›©LˆÂ|˜ûÐ>ÇKàQQãI“9ûûßñÉx–L}§tèÄW"JœÉιÀš%s Ë(;´¸|4ÖÙ…3!3Ä’"ÿû¿ÿûy Ôd#‘ºcûqh#zÔ—,)©&/zÑ‹˜uû\¶éXÇgg$ñ$ñË"ÀË¥ÿíßþí¬âJ8õ=ïyOšoUÆÕ2IóÒ˜ãV†óhY‚zËq|¥aÄÈ]ÝÒ®žüä'—®Võ½àMy¢IŸØ*/ , |=JÀÏ<™ÌúÏ¿—¸Èz¦êAJVS@&„v€€¨û‘‚©Àà*Oø ¸Å YÝj s` ¿€ÊœýÓÅ T³ñÜKàƒâ``–sÆ­ã.ªžY [Õ@5 4,H}þ”j ^‰5·äÏòMòó’Ooü^²”Ú¥}¼ÆJöäyÍÆ­¢ "ø ^{åfùõ¯=ô„þýªt‡Û²ÑJÏU4_ ËËS²ÈÚÙƒÔÙÓ;rIª)–£SWŸIÊB¢`ÖùªÔů&ŠE×Ì™]ö Ox‚|Ù%Ëó² ozÆVçÎ;vï½ï}ïª &,[ÖBµ"r‹ yŒ?¥xùŠ~)Õ¤÷“£Dè@‡L¶Â<ôIê«;æ[܉©eÙß²±eC¯{k†lÆ’°1£Fž‚mJSyÂòm,#úø?æAä?Ð8‰èO²ØÑꢤ™sUÊ 355V¾úKŒ³×V—Ã*/ , |]HÀÙϹ~Ú)Ô® ¯‚úlûô5¸ #.`R x)";À§‚߀RB£Á”Æ «üAÀKÀ¸mbx²gFð$Ae`0Suo ´n Ç:¶yDO$vüIÔ{°_2 ú‹ vx-bD4›ÓH¥ ÂE Cdè,>5b(Ì‘à¡UêÔž<ígDNh%×ÙÔú‹Üuˆè‡yµ§P¨ q‹­Ù*P*¨ jÆ(.6âû&*€@ ÞÄXêª>&L…ð©ÃngÍ$çQQHvX¾g8[Í©ô?­L 5.¡ù_:äP­NVaIà\K`-ϵ„uÿÉQYx§%Awœà¾¢žqñÆìÌdUÙçH±2æWÃÝï~÷qÞéÐpbE$àâ"îB ¾¯€¸b?u gò<Ô õÈ8*ÅŽå’óO4¦ÙÜ…ÕâWãÂy«“¿šC"J2n†Vÿ±§FÃÀˆ:”#›BÓU¥x­Pæ@o-n¥2žoJÀƒ'F>:V8¡šyß”ËÔèêЉ þx–JË®«}èC! ÷"àîþDhÔ)}z×Y++d¹KKK\Lñ1–s|ªÜ yÒü‡Ì¬ú_¦úþ¿t˜êúº$°$ðµ)?Û¾ÏÖÔY¥”»8(ˆ'$pÈaà"¶'Òñ$7‚š¾öD1PÖÀ d.ðUÇê̹I@_Ì u£‚{A%Àtxæzpª«M9awa®¦@7wµžTȃz€_þ™±ß!…þPÃX%ûï¹Ì_ÉühÆ5mž0s…g‘pA%<=RVXd£ñ‰žÅWædJƒêÃ$ÏÀìee‰ëAr;¿©¾¤¤O8º/ Q²c¯»î:ÓÎséSåÞ›¥?£Üîv·#ªCüšT è÷ÀÛ¯ièm˜X.É»°ãà4?àªYX8ÿð#í‹“ú]ûQ÷Å€Ÿ¼~åó`8€ˆºt0ÒQÈ€€Ó{G@©ÛŒ@àÒ‹Õ ¬7×I†³ Àfn?ä±xŠu–óà<¯0ÌM{Âz&=xÀùjÕK8Ń DƒnïS”·$µŸ3Ô‰ò:ª×X)$%›gÙœvoŒ‚=ãLx q{.$ÞÆ#zoß›õ ‡¤[#Æ^éeõ6ËÔRE6¯•Ù+Ââî«“|JAšÓ¶ ½­¯KçNkAxîd»z>!ƒuß(J»KGTâ¡yWÖ~u(Ç¥Û õóWÄsãμîÒÌA¦dbìƒÔð÷¼ˆ`¸ä+Ê@è#SíDtƒtú28#èiîª× 8]!»^9”3yãnÒVoœägĈŽø5O”¸Ñ7Gå±N¿}³LIÐ …aój¯¤x 7µsŒpµk(õ¦j(?ÝÙ§&ï?Io¶ÊKçYkAxž~ì†ÃÄ…w½µçw~G¢0Ù±-9$UsõH^‰øâ“Ãj{¤4i üF€ïAV'‰13“ªQ`~~Ö³ž%&$ÆËþ´ŠûßÿþŽ¢’‚¼–ý^¡Úã¤MúOK~8ØÅ$÷s¸ITÀÊHÕèA)5ë£x}á.q.­I ÜâÒàˆâ¼cm<Ѱ,¯r40ÇÔ®7ˆË÷¼ƒ.…ƒç@rµ÷¯šÙfÊu¹Ý³´A—MTDËÕgöÛ”ô|˜§¬z‰; $þ‡Ó”S¤‡g§VôãÇ?þñì÷•a46ú„]‚L›#P÷2Ò {¨Fªþ/Qû6×ÀýÙWyI`IàÜIÀÐÏrÖ¥àÛ³à«ðÜÏ<‹‡¾T ‘ÙnÒ-`/@Ô$öø ‰=Ô|Ä(h1\‚¸x"=Ð7KHf 6ûU  Zl·NƪŠ;äênúe¤Oó ‘¡€> ‚@È¢‡‘çÙÑŠKÃWîEF( 1¡§ÞÛPFmpÕËBvÃ¥úŠ"³xKvŸªO½"YÏ‹pcËëïÝ!h4=8qD©@ëCoóWê`7Écï£x2÷3×Ä’k&”ª…‡Ú“t8ýR>ä$xgð´ÙY>¥_±îEGšµwf”ÆóFÓ ¦xò ÞÕR˜Á|)5,‚W_}µÌi¡.ëcò<‘Œ‚Ïz(EBSöíŽÉW&/Ü¡qS#+­b¿™¤f%gzN­új ö]rJ:õ}V‹g ôüF(d™¶¯!=eŠAòâlæøS9t»jh êŠ7Hu¡À”eAû|ü¢ðP{œy}õWþ¥& c­¯KçYkAxž~‡ãÊ2`Ÿ¯ºdÁW{õ|xfs ¦detÜ#nÒg'C4®Nv Ù¢Ü_Gåv|cGZF5Ž©µÌ±høÚk¯uö.£i_ŠÔƒ3ý2lwŸ>áÇ?þñZŠˆè•sYè¼fÂZæ¤Þ½qš±òvFdˆq2žuÕGo¤ÍIÉÜjó­wEƒ‰:Å×¥×§ì–ÄÚElÛr!\tÑE9y¢ž=º—ð›Í< ˆÇqëÏxÆ<„šÄ–àWÞV¥éó®w½«É:ë¡e‹Õ?gF÷ÿNÎ ŽUÛÜÌÙÚCÓuµ¡-y³xݬb&§Ùè*Z=W â…Œµ9óU¹$°$pV$à'æ‡6üôêkÿ‘ZêX ø û!Çæ¥™¸Ÿ¹»Ÿ¼~nýPò€Ð~΀%ëPpÀNM'à(EõS›O Ö´q›‡DÀ¨Aõ™¹P@ Nªn3g`»‰Ò9¢P˜ù¸¼Ë¹R(—!`&:@ ý씑ÕÙ|œT" DWî7Ó©Ámö†ÍmŒt õ‚P*b­UnäàÍnöÖ+‘¸Nz¯Ü,ge{#7èb ¤~èŠ*B!¡–PNb÷Œ`ó—´ÃkêMz(8í{hÆæä£ø†ÈI¶Õ¸P†±ÐÇæ>dµL2Ú'ßìI¢¶ÚEDyˆý!ÁÁÊæ¥xqH“` d\T‹p5;2Ö}g=¶ÉÎõ)ä¿%a¨¾zRÊFx‡ú1LžÐ(*Ôó÷)0±ƒûÏ3„KP{4þ?S†áÖ×%ó/µ <ÿ2?Ž#ÆGöÏÀ‘䊩Áœk‹Û‰„2z#ªÎ é S Ýv®h>Eóîã\V•Ýzè5$Ê|àÁÜ>Çoú:öÆø '%ð3‰{d‚³Ñ6#åŸøDõeŸ‰ßþö·k†7)³Æ•6uaí«r.|ò“ŸÌrNn·anÂ9¸]½öµ¯¥K%Ñ9âܤÿ¼à¦D›Ù´qr¤yõ«_™zp»à‚ äswÂUúL ‰ÉHÞ0ÏSM5`”õÕ;õ_‚×÷c)þôèÚdÜDíЫ̄5·›¤j0ÿp¶ Ñ#³U ‡ì3x‰<Çãã®\ÏRªáªaóAVå’À’À”€TV\õ+«‚Ÿ¡£ŸdÐÕÔOµÿrµôsö£6–¸ŸyÏË@@ ùŠnÓ#À¤›Ì@ À;ÙíDî³(KƒÄ:pô@ß°f²éÜTcS3ÿYhúLÚ ¨Á5Ð&‡’’`OÊP“øs'Uƒ,L q ªœ ¨'ÙköÉ‘Å͵!84‡ìúÄ2·À©—8,œæqãy‹vçKCMÿÒó mò•J`þ3ô¼\¢HP'(þrýתG ŠPHŒå¿ÄîAÑ’J3ŒKí©ª@Aš­¯Kç_kAxþe~GdÚ öuT]‚°5ãÆ³/#f?–×ø„ÌêJ㣈¹÷ˆ ˆÅ7®&8i¿gWeB3$ZÛw‡nÉCQ2Êüy¨e²à0j)”_Ò6a|Tbn,Vˆ– [ö;2­¹Åã.õquÞÑNWZ’’Þ„70ZÓ`.¾øbžQ}[ϬhYDâ­¬äY£gZ­IÆ%˜µXÿÑ'òàügXU½P2QÃòÚëv>T˜›ôëU/þ„J'ñiÿŸ ¿+xòûÑKĹуØLp£ÿB€rîEæ€Úm20]ËÉ÷01-´´¬µ‰ÊhÚÞ$3è;é$ j¶tÔlWaI`IàÆHÀ(˜Ó\)ûéùêÄ1HR@êëg›€4?d?g?j?ínFôÃ÷ó/„ .ˆˆ%h€Žžë°€ jjæ ( ”ª²€X6‘ÀpqÝQдÁ`¼Bc¿±—Ái¶øæuEšÅÒJP :™›þ9Hì €õ½ó¡Œ&r8Þ‘n–¤a44ôP_u…Â’vµÏÊ]Þâ#ï"ÄlÀj_·o´Ìz~s›´ß‚¸cÜÜIjZí©fE=Pã¿…”H› 1Ì\³P æõBÜ{Ì‹£ÒÔX)d±ª7ŸâÁ#jèd}]8X Âs!ÕÕç†âÖÒÝöä:ÚIý‡KóW¼p H±üØ¡p_vß?jÁ9"g[í§Í|’UïÎÓj8ê˜Ì° BœKØŒÙw‡|ß[;å‰"Âɤo͉»ˆ~³”Rã2¸&Bæ;-©Añ™áÃS÷²”Ó¥ÄoH°^¤aR•$ΦgtõÈ»ˆ1Xf‚êd(°ÇBLS¡è\sÍ5h8iÜÓ3®ÅXßå«~L5+O|?3¨ùô5^ú¤âˆ÷àb$’'C‚½Ímn“–ù+0†=8uÎùGû!>Äüi'þ¯ Œ©cïD™ò7«5î*, , ’€N_;õ_–Ÿ›]²ÅøöK~¤~ª~°Á+?a?ä~Š Æ~ì=NA{•`8€@1î*Hé@—9Ÿ,9ÀQ–‘ó€¯Z”öu¸z fþ qsc0æ8DÐÚÝ^Ìüá¾j5U®=èî[€ÝU ¿9Õšy\0PÆþNÒÑêñðè‰`Q éhSÔ†àМ{ëd´È±¦q¨€duˆp5¨ú8› ñª9T  „I©9^²æ¬@‘ NP*¨ j†J*G‡z¦ÀhL™ñuhÓ³XGÛ!Ÿ¡Íúº$ðU‘ÀZ~UÄ~ÍQwÚa,œ¹Ç‘²ôÆlåbÂñw»@Qó­€ #‚‚øú¬Lçù# BJq46C6ï¿ó;¿³/ÃúUþÛ¿ý[-Y¬g¥¡Ú¤àˆ$-Mfç¤#ê²@’ÎdX†±øJj÷”§<…¶Äy饗êúÒ‰Ñ×dZ³+»ï+Ë(RÚÌ»#«O$9¾ûbÞèøRÐ?šgU¥Ó°¬Ç"¾ã6éû=¸Q>÷§?ýéô•2ó뜧®«®ºêK_úR.o„£—d>{¨ºšíS]™[_cKÒ 7LŒ¬õ%7o?»Çúìa!µè* €s]¤t;ýP}œÍ宬Ý>$(Ÿ]‰úã¬ò’À’@IÀ%9ý矒?4?7?:?½üUZ"úaúyú‘Ö¹~¼ÔÚÕ‚z? àª~ÀBO‚4@G°˜ÌvÒ? ®+¸º£©®€ˆ«£=oBõ€áfVÒfþ À‚Y`knõì®ú€eà ¢“~¦„™B2¦€÷ýç”1ôп’[ÖÌ !#”„˜²Yšùø‹¼’‘ í< * ï çöSF¯‘9¯öoq›Ãïí£{¤ú)±&לL†Â@m sJönñðÌ©Š#µÖ‰Y²îó•^ð[Œ{,¿x냽lê¬Zßó2cÉôPFd^qã?;jù^xá…Òèf*霫D.ÉæÁ0Êë2%Ì{×BÈ]¸1pcƒ ™`Â;a(Ì…Å0v+Ž Eß%Óöò–aaŒŒõƒµ{ÂŒ¡]þmˆ‚"#Î8bd>R#pâ#‚JéMÇî ô@IDAT”±ºH3QF u9“…NÞQb0•¹fµÔÏÒAÛSÿ:gâì¯|Ó7}“Å;õweB÷ù,ŠMÀÉpµ†žóŠ€ŸHš¯ðÉ4íЯÿú¯÷çr6ÌozÓ›æGUÃÚ3^½ýÏÿü4òÖó—•I„+e3U úЇàñtôÉpÕ7‡ÌÍ/ ª[1‰vž0Ç3ã’uÏ[Êr#¢=¦‰Ouõå¤fŸøÄ'<ÕFPP¦fé]náYæÔGg–ñˆ³Š,´S…Ì4ÿòI[šDŸBÓ¶”}5÷õéOºãá.߸1€)m^œ¢&ŠTÁV˜+Ç>ÕÚaF,‰1«Ãb[Ì‹…#Ã15Ö†glŽÙ±|ßÄá!@\ä L³y7HÔä“ð!‚"⨋£ïƒÞAè}ä18¯>IlžÈ „˜%l‰\‚—øUijòÜIÕæšìU³,¤7j"¹X{ À” 3€D QF–†b>ÔAy×+½«¡L R…ð°Ùçò6H¦j‡N†Ÿ”5•mhKO‰ÏBX? ‚Æx¨m\¯„€20tËØF\þdºhÌŒ™Ÿ2x<Ê_&…ÈC{sã»æÆÀsÆÀ½!|ÎÒÃ%2—­#—Ìm+ß‚Íx[O3¬ï‰úèñŠêéW:k0Ü®Iò²Ð~ö¿è—\$ –{¹Üå,ûtußüæ7ëpΦÓ_7E“±Kæ­Nš±T„¹)‘°œª²»4‚^¸„…Weš¨!¡PŒ˜>ÐP¶ KÍ>ðáQÿé©QX= U¬”,êL«læ $!1L7_èâDïj/ o ×èú(Ò»6PR2ô;ÖˆK;6¢—‹£DKNúêêÄD Ñé¾|¾mf`$îä\:âÿÎæ…„0zp?179ܲD³ÓŽÙjÊúd¼–+Nl¶H˜Pû ~ê]¸1ðÅŒ€JV¤€e0NùVŠUÃY c@l˜C›¼ˆI±j¶4˜7GXØScm Þã,ôIè§ïúŠÿÁ—U d"܈§« ¢Éé"1EX$À[®qaÒ·ŒÄ#!IT&†°pbDB•hí‘óo|‚Wµáì—À¤’RHh=5¡†Ê<õA‰ w§¥CõP@u½j‹ò²ŽûhùäV¡û»Ë2ÅjhJv~jQ(夘µ¡5¢Ð©u„AÅSô½‡’ ÷Mzƒ^f`€aŽjémªÌtјS5ULÐèßBŒ¢zzn <,î áÃâÿižs•“D,RØ3 Üï÷Ôi^ö3sƒ^C%ðËý›»yÕ>=Sº› ¢|´´Tc¹Öψ³€¾O·áYŸùÌg´O˜Š˜¥}ô&0DFe³·WÒ¹÷ßñù²`ôxm}Kølæ‰# œþ…_Lr*gšŸ§J±­™ê 桪ÏåAr–8n:ÌÓDzƒ_ýÕ_eа’ƒŽR” eB͇?üáÌ®Âqù’Ý¢1ÍaQ ÇK=è{=$ÀIãØ:}Lºó27隊©%t &+Þ ´¾„öò<>û?‡=ǹ+=ù­·‚*=gQ†ýäÀ¾ƒHûrÜå'…ÄŸã»5dpÁ2aÉÎMaUI3L—klý]ìYþ&l1ˆ‘±sqzÖÛ,߈ˆŒHh kA°/]méMãúpNÚ'¬‘È"¸†ò“ Ë€è-1H‰Ù"Öt"6 ·t–¹榶%•&€‰ôM3(C;O£& ·otÕäiZ*ÆO³£t®t¸ÉAêuê/›ÆS1H¥&˜’¥j®@ùRÁƒ²^V⦾)qí@edïM[µ©z¦´è|éy¬f)X "-{îY޲@›4lCÏ÷ϯ6î á«á»ÿÏa€˜&µýu+\’€Ïµx¹”»jýlçå'‹ÿÿWÞ;:ýJɢǗªx:ÁIˆùÍßüMnc#”·®èHvLêþçþ4Ä4 ˜ß÷h\õD_ž&ΦÅÁ U]ùkå7“jÏ(|äó •é¿÷½ïå»=Jm'àJ‡þÝ€G›FIåþš§YzqwiúùžþYi|¥91–îHss›‚¿aGçÓL4w„rF§CFM÷bœð§(‚hÃ`)ÿšÅA}˜{åú¦\’ šº)H–ë«ÊkŸÃ†ú3œQ–T%eJ¥jI½R²ñvhÕçÄ45 oÖñ(²¦ºU`èÇô{åQù…ÄhÑ3fî©SÀ‡ÞüdÍ-ïš‚{Cø h¢ƒ щ@ìé—×ÿ"‚¯Ä“@eâûù¯ U’10ø î•ÆÙ¼ItÞ»©Oèûì¬ìjÃ>'³Ü"‚ˆBª4«m~_Ê€êÚ«Ìœn^ÿà½ÎU„÷¿ÿýÈ”9à§5KH?ÙFIÐ×÷ä’Âe·³ì°†`+˜ÝŸû9U_Ö†aåçæüÃê»¸Ãæ£Ñ—§¦É!Î@<ÕyX*B†:9¥C6 1°É+àËÔ*™‰ì­z1†QN­%xp}H ›Ü1@Óý„"ýxÅœ Ùòhžþ$ÈÑ *ü NsžÄï›]búayˆMb'ågÿ÷¿ñËðÀ»Þø¢Á"GêòSÆ úÎã`Ldw—=@˜K{ì†é°^b&1c.ÑaOL:s.vÆÔX;|ŠÙgG±=$A¡Bƒè @P ¢¦ün}]ˆ¦ÀIXõú”‰5Âm€MçÄ aH\”Ü#*³k%<ç~Ô€ *Þ½Ä ºõOŒ÷ Ÿ„<øÄþ¬ ((‹Ya7R4sÆg.¢Ï§¬½Å—[©†zjtJ“ê¤@scX —Ú¥|©à!ÿø5>Ýdf,€ÆŒzSˆÌ_*ó[¹² .¡’•ÎÚ$`›;¿kn ¼ª¸7„¯*zïÎ_òu[Ù½‹YËÒ‘4-¾ÔO¯èôÅeÈUþ9©×ÐÒOs™äôK¸''oîæÞ9/¦ôw²oÇæt-éï¢ %ÁÓ)v44m§Þ¿3xUã2L\àG;®jÉ9­7“š3ÊT.ê%®øÚcy1(‹z¼ÂN´T ÈY~ °ú”XO3fMßOÖS…l§ 7ŸšþÓ?ý-³/ÐgxÉS.Ó|ï37?9ƒnæp 'Ùƒ±„`õ˜IÈYž¬¤(ŠùÕ!T’wµ…3ñÚŸî î D ö¹{9]ŠpQMjo*ÊÅ}m䃾‚¬ì¤†»Ò˜ÛXÏßùßy¥±ÿʯüÊicY´ZC'iÏø†oø†ùž›@ l:ƒVvÝe£ŒsLÊ–š7Ì0Âi }ÀYÙ¥Š¤lá —c*øæãn×Cþ´‰ÿ’gtVlÕ¨â×Uµ„þþš¯ù}™8éêñµ¾wRÃIoA|RرÌÔ°®Žö¨ì¿Ø’˜ÓÜ–ÀÔ˜›æâÅþG+ϧ‰¼Õ 05A£_’p Á¤6ÎÝîÒŽÜÝþãPw怘õÙ;a0åúJ,Ñ+)ܸË7¾p1€˜‘t§ð*c8¼RƒA° fÁ2 yH½¼&Ø*Wþ0vSõ0 6ŽÜu‚a;º"[¼2Ç:Ý@ÏpÄ¡At ÄH>D°Ôö²w®L…å (bÊèDÖpºH¬…ÓÙ2t÷¹Üö°I¦Jä.…mz#®sœE€¢÷®©û„?Úìu› U*fxø ªlû—‡ciLÙÙ×B(ÁÁh ¨KJ“ê¤@-AâT)Öa¬ù§ö^§¬çGs Õ¯1TÌæ ªqßíÏmª†¹¢1ÓÅL3c†IðaÞ JV3§W(«ç»pcà9`àÞ>$ßC¼‰©ˆ@ôïl«³À­ŽI^ØÙˆïRoÞ¢ç/Ý÷f)ÿó?ÿ3Ę’eÏ-ÕÐñz–oz£†ó"-Gì`ÓúÒÁÑ(ôD·tëOð 5Æ4–ó—ù—=&Ç”sHušÀ…3ÒÕò"{ŸWroR´•ƒ!O…ñ$³·q¶:Ï¿`¦%ê£ýèpÉ¿›)9­+ê]ïzWc(K` .^AMÌ& ê’^Àm/†—¬ƒ=á^ïG¦¾8‰bÕìä€=æZ¥7t àp ß6aRÀ¡ÆúZ‹IËÉ*óE QÚhÆ{óžæ~¸'®lpu³¥dÈ>øwÀ¶Ç2Úwú4ïòGޤ‹€sh_t®P¼Ó+•±‰W«I)j´fŠÅrN…érp”±dö˜´ƒ 9m°võƒå1>öï|G8Ee&#@ˆ¯{D°Ôë½@%*Ñ”¤<ñEˆeZ$×ßÊÄ£wMdŽ¢LË„c¶= Ô#™X&œ‰èA¹˜ ažŒÓÃõ³Ë^k™OµÃF¡P+nŸ2Ô+°g V×ÔÔ¥FµQpÔœ® E)€™Z¤©ÈawDjcÊki.[ë|}jpÌ-ÕPúT¿1– z%s"¾QF¯ʦÌDa¨ä,Ó%!ÄÃdSÙýÜ?o <,î áÃâÿ)ŽžHúnPiþlHÞö¶·Ñ£ ¢“`ÕžÒ¥È] {|vØœð,jvt§@t\¿{—m^áêË®Œcuèdþ)² ±ó£ªÅJàvçýÁÅ˧ÈBr» 9 ät£o÷éC1-I&¾Ù¡ËÑ¥/mžá_ÎN––€(QO4±ŒÐGŸ“ÊŽš)Àã[H ”¬ &˪gí…Af ¾ÃÙ;F Ñ˜†32ƒôÐG1ÄŸÿùŸ¿ño¬~R`Òýèþ¨Ä€²Ã5g‚¬zU–0´äƵ“ŠnPZJY+*šqÍ)ë[Æ«ö¢ãb^¨ì¯ëy6 „‡1’€Ýgq—o Þ‰­0W—–Úc@lˆ¨=»Ÿóba`gLí'ÇæÙ¿uÀbaà2¢ƒÑŒ0™Å±CøA9ç¯Þˆ)`YW„Q–°LÂm¢/¢öÁÏÑ~ ‘„­ž ^â—^ÞÉÑM€oTFöŸÁéŒÜ ¤\:ÀsÙXÉ›EaQ[”–³ÊÂO,%õG R…sWU“ÎTjÕ¨fSÓïQ›ª§ô5¶"U³)0'4fZô6–•ùÁaŠ0H˜%ݘÉ4ÍB3L8‰…“Gþ 13„z·wùÆÀƒcàÞ>ø<9„û÷:ò‘İÀ)ȳ˜üÚ|Ày %U¨4ߦ]8}söuº#Ê@Žq¼N”Û†ŒüL^9â–O{%£$1¢F¯_–_xá0øv°ôhR–‹cáæœ7™¸y±þíßþmÙ•JŽÏœªùäîQ›ÔsÞ—ù²é--Å$+ ë(>ÁLÁàéDŠ×¸öÝùéÇ›æùÑ7Ó$ßöáàÁxbÊ4à³N¼ÚÝ.T×»0¤òP½ Î9úØ£¨žtÛݧî-9'€;Ïr÷é052™…w“:‚](é°[n:I‘¸V*̧±ô,˜ª¸ ÜÉÃE©Þ•±ü1IYWW®¯t„ßåÏH¡ÆÒÍ¿øÅ_Õ¹¯}íkó³Aƒ„mI‰¤ÂDX CÕ»¸»aºx‚°azƘ}'€m“Ï#cgLžÓ–Ïi’wî#äyÞ"L‚Fâ…!jœ>eB‰h" j›W1¢Ì ÄÚfßE$&ö¨Ë%3H b–°¼c2±L8ÑIߺÏ7±ŸN(‚åˆUI•€ŸZÙ„Òèbʾ±´– x°MµQpÔeçÅWC(P-)Ó¾Ï?jœåŽX>j“zêê™°o™§™Ó‚ÁÌ`lÄZLÁÄ™( ?-L—ùT3¾Ýz‘ ´ILp¶»ÍÏ;î áç¥w‡çÈ•°ŽÜ±Ëw>ô¡iCãzÊàºÃ‡*aï¯dñ W'Uð±c ’”—ÔŠå–¶ŽG³G%-aP)“ALŠ|¬ü¨YêýHsï›yêbŒð'&…O'WcVÅ_üÅ_0zLÁDf‹•C-ɬÀy)ž§o¼ ì­´S=÷BNÿ¨ç^9—Ù ÓªãDÃÙçðÚò°&X´Ð®ÀÎxë[ß*¥žë}SÇ\scDNßÁlêƒò|§O^Õ^ßË"—²Â2sš‹†ø™ŸùÖ$%ŽÂ´~û·;g?þã?®=df/Zs‰eã£ÆÃžÙu¦ìÙäE(ÌK@ï3hé°zxÝë^i,9ñRé;Èâ¾âã¨fNte!j+ØÍÍj†ÔºqùwÝåÏÈq.¥q3òFäH}8*Ę"ŸGíÎ[œ†aìS္°X}éë%1‰n±dŸ,†Å¶^6'›cv ¦CB ¿Xåp„ÑžH!XŠ=½ëQb ¢M %!¦1F¬UçC0Ì9˜!úž–ð$B RâtئOâ—&ŠK AšGÄõfó–ѳ+£`†Ÿ”H¶îÔJ=2¥Cõ@&54œ‘€ÂŠþõEÖ¥=5‡¨¼Ü­>—…|JêH€÷W(eãZ jº×/ËÔ½ÆTWOÕ’©À``6@fô¯ÆóRg~0B˜" fIV!Q¸Œ–ê°ræ\½1úÓ»|cà1`àÞ>†Uxr0d§WÂQâ% ¸Ù¢r–:•Lwi„ÚfR°ˆãÁÚHÿzZã;¹ïxÇ;øD)Úh©|i÷ÊÎ lñm\;ð2d›we÷˜\8ûðýÄõ($&‡…7SsÅnà€¤€“ÄE¦¾Ò\¦’s’yñ8‘;Þæ~L–]% (‘Q0¯pûŽ<ǰ4 ®¾ÄŠf3÷S5íYÄ#ç½–B’²ßcÉk'LHO_z¶ ¯yl¸9™DÆòu㘌嘇“÷½ï}è!˜É\X0fÁû^;Cq>Ù‘:»è1«²Û—%ðÒ²´'ÌÉ*«”õ“>wÖ3O Q £Ï¢#3eÏ÷|O…ÒîÂÁRD{¢õa#ï¾y@üX#`‡øb06‰IjÇDáPl…¹j‚˜.džذ‚î°'&ŪµÕ FÆÎ˜ºv ˜]½n±uØ I.Et =À˜!CÔ8ÄŽJàm>ä"Þ"ÐzÿC9_J ‰GB’¨$0Í‚ð̸õ/K̶Ia5ô“yåâ¡ÌN†®ê'%bt …Z¡\¨Šf^k*)W*(©½Ê£ætx%;‹~²u\N¶ L!­£x¡qö™T¿zf¼ÁÀyÀHè$Z˜7edÉÀ`f06˜ËÍ$Å+z˜ÏÃ]éhß8|ÿ¼1ð<1poŸ'¶ï±>‹!4]µþGf®lrä,5ywñÿ‰Àq½„G™Ý0ßfÑãƒöâ¦øý¹¼~zöòw÷w€ä¬¬x²{tïeÓ&¤MÏîbÿÁ ^ó(HÀ ¢­é6iԗꪇº/5¹6³‰·£=\a¾€§,µ”`©«YBbØIÂl*Yð(p6ËêÛÊðPoÍÉåµ±F=SËÐŒ65qüàóˆöµ1±=|·ÃalT¾dzåMÏ+.±dŸæ6NI û>§ìEP gJ”Ϥ¨wQueŽ‘ãA¹ï-3üH±W‹BàÌ"£(øÉ¿^5}ô*{ “‰J­¡ïÂç‰äWì\”9óLáµGÂØÁ[ù7¯cŒƒ}òS¹Ç[b·ÖbF,9œºc[Ì‹…û™ä$Ç 6ÄøWÄá@D$wTÍ‚!Lˆ‚¥¬|úÛô  ³e} ¡L ‹H„É £­Ñ‰P‚4˜!Z‡wûOb™pö"AÝëç2QÁEøÏOSCePÔÇ E˜‰S:TD QFTR óeË>D>Þ@Ù ìmªLuîÊ”2¨(èz}Y â‘Ä3}ÿe_Fõ3²F…ð ÌôÄ®®Ä‘‰¢æÊrt˜ï1~:U/_¹+o < ÜÂçó{Ägˆ¿°¤$a½ÄK>mÄO¹|:Tòù%7iÿÆ HièTª]æîå&Š’`gЊê05fyç¢(‘aÐù§ôe¼Ëþ†ÜnsK5‰PØòiUfci/a-±Zì:› H|´®à âê-cîú4wÕk’ƒ‡ÕÕc·zƒ”)Ôäš§†ûSfÇ'>ñ‰w¾ó.N æ\ÓÃimjÉíi1 yè—_åJç s¤°É Îño XMÙ¿Ì/Ù&¸›ÍÞ÷~ï÷–TÀWºB(õÝ*ÄŠêGSt»‹=¦Ë5{6dl]¸ðû ´„\ãYÿ­ßú­d¸é¤ÈdIÿ)ôA«, î4N¬¦sn üŸ1€Ì]´× ¸ÀŸšN«!f„¼‹Ô|wÓ`^L50ˆ˜%~색 TÌ…ÅÒ3bIŒ‰'65¤^TÀÔXÛ[سcyŒ?¸¨J8ýÝ^&drà_AæýiÊ„‘e,â+<Äšq‰¸9 aH$ŒÄãàÊ]Gâ´ïRæ 䈯Ó,)„<øÖ‘ð7 Š ñ)¥TF’î@©uébmÝÔô©åÞõIµ‚è;½ßhJSã½$qh‰j¦ ©iÄó aí“RFŠæ®@éSý €fÑ48rg=×0NôÌP Ï ˜7Ìm{CøàKðD çºé …Ú"øk S÷7Žôz¡#Ó=³/³7«r‘‹ÿñ$Kx}~Ê’hÙ.Ä•cß¼þõ¯ÿå_þeIç\@¯~†‚›÷4¥Nþã?þcx4ÿ”¤Nç² ÌzMö?§g‰^ÉõHQ1Ô9UMýÃõ߿ŗ©e øÝ}ªKÜÔ&|496]´Ø›ü¬°dîÝžë³0D’æq©r“w7?ÄÒø,5,¯zš±ÞS©zSÏô?îæ,QÚ mX¬R4½+LjêU”•åKž5ºQlfRù ”2+¥ï›[kÍÔ€êdREqð3°8ª fØ“¯|ðij-®³ ׇ›,Ÿ´* ÝêQÖUŸ‚4_ãÞ…ÿ7 *¤5Pl'?dÙÁ#¤›M…2’FØÈ{Øé_taâgX'g‰X Ca«.…õ€”MÈÑçC3YÀcj``ðaóC‘ Ù* ùi:ºˆ—Äž8qDX‹à"¾1¢làYâŽÐËi’fGò–ð$mÀLœv0†2ì%¸€pÕOCì°”ÃÕ®sêR (¨†R}RêsÑ®úYrîgq—O«2‰s¨¹ª9*P—ÖÚÜ)У6D+åKSÄàŒèVè:¡Ä©r Z§Ü©x œKuÛëñÜF¢æ-ØÓ9C¥wÒËý›–`¿Éï/ÞåÏ÷†ðybûë(£0¥æ_ñ¬ýˆ‘¯ôH·¶ÏЉÆÙd%éíé£K˜Ö+•ù•?ó™Ïˆ¥ñÙ"Î]zwغxËO³GœÜšiì•2qr æTYKö¼œm¢›l áôVƒûÙNÈ–6Ì‹6Bl&Ñl0VX` êIS9ýó¡­¡Ïág¼¡B˜ŽüÁÔsR pœ{—³öÝï~·AÁ<À“ŸVÇÑÁ2I7<«ËB ¥–Í–›¹)mC ™& …P¨Dþ´˜Ïþáí–·Ýí¦üÇìó2ë´qy¦êáÙÉ@õf-$ðúWúD…~O•ʆÈìbçu'î\9üÍ‚Á\¤éá¦èmiåu/êvƒ‡‚ü.Ü8ÅBBN¡Æ"ï^@Šý¨ ¡"WD›˜/"æ:|óbHÙë¶ä$0°F猃}0Q¤FÄhØ­ófÌÓA(a^,Œ‘srX=`v,ñ±¦O ªÍv¢ª0Ÿ@ê‡h" ˆ) 0ûI¬nDA§1¡§ä ßa<þ%6#” ¡*—…%b¹G·Úb; pƒfçYÓxÄ>áo§DÌ":ÙJÁÜñ¼=7ö©¤®&æ–”šq)¸J;·©šù‘јêL ")-LÉRµÞj^`ðˆš.-<Çzdu¨ûkSHN ©_7mê³$šqãÓ=h©Þ½ 7î á£ZާŒÔs%Ó¼pAà&ލ²Ô£e¾×›//Ÿ•”½½(µ}4zo¯MvnîèÿË¿üË;ü)3/â›üþïÿ~5ÿ•CÂd®³•íÃÍe_sÒç×}Ý×îEûhÉDÛgÁaÊ0´dÛ‰ëzK¥?stCƒµ$,‡ Y ëSŸúÔ [ÕÐâÙ“ÏW;ªtyº‚«DTV=ËFD“«&l¯ÒÐLÉ\4ªWØqitó½œòYŽÙéîp£žýâÙÌ_Š2ed8¸ÐÀp¾‚͘•íæ¬G®ˆÄ£,¨,öez`ó ç”@–Âá“Ý–cïºË”=|^¬ƒNniäŸèÞ“ÁèÌ+õ/ côl7…«»pcà  µ] …N„ˆ‰"TäšfEÀ~"lä±ÕîÜb ¬¡™W0K6“é+%JÓ»˜¥CŽ#`‰1=¤XÃö-¨~À‰µ1x¸¸wBÚ i¥z"%ÒŒé[š\&Ž¥QñEˆe}«¦[â.òáè£Ú䲺A!Ó¡R&„³Ý%– g"Ú‹³J Á‰ú¥[­z¦,â~¢>ªrY €¨!¨£’– ª2Ø®ƒÖÓ^ (³V6q:]–†$ükŽ”orPÇ72 è/:Ëôo †Dï¨Ì,ј‰r¤”AXÀ+0{Žººëo <,î áÃâÿI>Êèõ%F’yåÊ•<¯ÓÁô °üXÅÜòV_9ÊónŽ¿êR8çhn/¸˜žÛ ÝNŠPC%ÓÖ®…äöÂ`%èV"„XBûÓ?ÖI"¯˜>óD†šÄóp½õóOVP1A¸±Ý°ÿøIƇ-™6‰­î%îQÊø¥¿\ÿŒ¼ìœEvÍÃ¥Æç‰uÈ]½q0Ç0Õ Å)Ôt¹~#»ƒóÍroäÛ¿ýÛgßp†£­¿÷¥«DLÌaZÊC³ÕÕ”|€±†ƒvqYØÁ û°ùã”W&/?´$ñbT ,±[Ýn`±•ÓÆé ZbböaÞbíqØ[šÍ%®>PÊLÕÁø®™Þ…K d3ÓÒQ ‚D–ˆ‰"ïÞ #f$°S/z³Iaì0¼¥%ÆÁ>˜(eØêoÿöo;´X/Á™˜KbÌ!óævvÆÔýÝ*Ùm %«aB¤‰x!dˆ‡Øé»VO‰¦ˆý½#† ‹LØ|™ÀÔ!á98 *"—à­°ód¯ÔœÝ81niˆt‚ÝSB¾:9*äœöÊ­ HŸÐ>ˆÁ¡çœ"Bל釅H-RŽi©:s“ŸzÍMþRÙž¢Ê´W¾¥=³$LˆYM³ËÏ\zÜäËaØÔê(\ÙÁ.º+o ¼Ú¸7„¯6†ïþwè[Ž7¾ñGM]è'Ié•‹·¤²Ã‘là¨Ã^O-¥óÓ oñ‚s[²ŽÄz2ÖЗô\œ£]TY ½=ÇQÍNÊéŸÐ—Û\¦Aubu -+ŠÓ¨›¯'§Û’\/dg #Jõ–ÏÛîÎîgæ€9tóâîA÷Å^pZߣÈ(–M>;fÝ ²ËñïÂ}‚²m+dš£SÙ¸“E S¨ŸÌ;›·Lö*¸ *@åî ûå1Û:q*2x,bV\\S\ì–À²ºBÓC×*0›O×¾öµ5G¨¼3‡÷¾÷½Ùlö$Tã^øÊ¯üJÔx´u/äÜ…§ŒäHJ§œ*¤ÂC„HA"˾-A´H‡Œó™Aäȃadø±@ÿà Ø³dï‡}òCõ=¤0]N˨0)VŰØó#‰>ì6ü‘ ÉX„‰~O¼ ÃAqD(åLrï¯$â¡wÄŒ ŠB$<ƒ+ÿªòÁ°Ä,aÛQ"– gçNõœ™“H*ö¡"‚+s¤2†íwARª‡Ò-eT•Ë€5³¬‘𳩣ò(¾ÄXz4ÿYqª5ÒËGy_(_ Z—+±m¸—@•ÌP]Ù?{‘A’Î7Q3Y÷ÌÔúÃÝ?o < ÜÂdzO’¯ýÚ¯-•à&À < eI§QÃ@‰~¡Ú‡úåϸQ7^ÞþV¾ÅíR»/"aL¤oƒkú ”ÄYéßÍ–JÑÞ{Sf¾§Ð8A¹›„=Õ¹¹ƒ„n̯jmòv2 ùï#Il5éãÁf 1§Üc9ŠSÄmêxa¨ÿ”M.ù„–UWò¸&WúŒ$èåפïQõYI&Øp\þ}”*SíÙúò¬×pŒ Êf‘ÍX­šã ©í„l%— S/·†ªO–H×Y½+ŽË7¯Ü{1¢ eèÕ…H-R޹=H].'2T&ºDÿCýògT2[>*íÛsŠ{ñ~ ƒ$¨úé?só?ÓgðôGwùÆÀ£ÂÀ½!|TËñä€I,G©ŠÍ©W2gÊpGÄzNW¤#»ÒžŽÃEm”ãJæÑQޏ>b>Ø@…w¯0=ê®<ç:Nýs£&x©ð‚!¸“¹*RÏ?üÃ?äVÉŸýÙŸiÀ8«#¦>b/ z‰éÃáÚë—e7mt{úE)ïæ;|ÿ0ð‘|„WÞ,8ÑJo1þ|9ÚÇ YQóI bâøÛÇʲlHÉ£?l؇†WÐøõ_ÿõ —v Çdä¥>ŠËúô§?¼±óº©jhk÷ñ\,\·†k ô,ï£*›È·áL=d­áªïc]&ÑäêQºböë.LØáëÛš±´j+híæïa¤«þ¯WÄי݌ûæ©a $Ñ)dYFZaO‘Üü âì€Ó’Fع+›dA†%`–|0û`"¬„¡ðïÖÀØpØ`ÕxŽ0ï‘cùO™Q@  „1d\"…`!^–º‰8Ò†hÚûÑ·H9ý d‚‘x ƒ»eÃÑJÀ³„-‘›C×åw‡Îãœ"†úù'a,x;“¤tâ †ôC%A õDIQUµ¥«áÏZSsfAåQ|Ô__;Ê1áÁ=Ìpª A/ •ñÉRñCýò'}rÆÃ¬ª–í$Úo2²"›Ž t»ì箼1ð0poÃ*<]â`+‰IUá‚ÖÔLêð‹ß“xûÛß®ýÅ $K"fúQ$áUR–‰„ê—?óeªdÔ\6H¥lÚò³Ç›È1Ïna&…/±B²Ý¥_—fJ%þQÙ|‹£7S¦¤c¨ ù†f~rfç2ÛýØõ§t¼OQ½pó7Ï[&5 D¦ŒX#¨ðI¨Ã‹|´=Ë,¼¬‘üx}Сœ´±ì*wó¸ca¬ã%!®ŒO:F•¹£”?ŽÇF´4’øæ%BÔ¹I±ûd™³lèØvæ;œRŠyËÁ¸Wzˆë7 Žy‘qæ°¢f·L®LÇÁ ѲO¥.ÔŠíØ+ÊsbÏUÿwáé`ÀÒ#€#ÚèõÈ Q•ß!òËQ9‚D–ݸG´q¦ØøuEäHÁ‡S°ÀqMâAÿØ'.•ŒØY Ó-Å0i¶±íÑ×0{Âb±?!@ »ÓÑÜ'¥$ˆ€G(m>CQÄZŽÈ’X…Ð#ú@bÐ|»Ü¨ÉžD(AJœvÜêÈÕŒø%„÷KŒkI¤ìû–TCv짉€§ttKQCÈc°™啳MêŒRÛ|Ÿ)€%ó*E¹‡3O©]£PÁWCµÆÖhðë½› ãá¨A¯gŠä[&ubÜŸ¦ìd;8É¿ ž¹Í]scà‘`àÞ>’…x¢`P]\R3R{Il@‰Î8‰i‘Tí ²Ü¦`RˆYÌñ£w“ùà­o}ëQƒ^Ÿs?÷(rd×Íå|I°Ó•ÆQb‡Ø¼ÎR2¨aE¹%¢êFRðÆäb¼éMor©‘!S°ñãé̾…¯}†m¨É1,klŸ*À[Üó†f6¹±3tÒŠbթ¼Åõº€«„}jÃC¿ßß²- RöQ†rl&¦ž8LT„œÌKÞ‚å·ÔË Qê6¯Ïw¸½—TÄ ø–où3 À&רÛS2: F›u£|Œ¹ØUÞ+å`Äö" ïdžžr«3)jM}‡Ú§ ÕCÄŠÝ’¤›Ý¦#ÝüœÏÆ+ó£ÑyºÖzïŸ_ °Ð–;–úL C ÊGH=”™!6$‡ð"ˆ"‚ 6(Bͱê 1#i„¼yÂ=EüµŸÁXƒ [#L„•0¶Â\±¼±[1Q_ì‰Iõ llÛ)cm ŽÍût Hbp "€A\Ù%Þ®¡O"àlãþ“@ËÁcÂM‰»l5kt‚1‡®{Pš> [¯”øí£ÌekMŒë|sxUo%N‡šŽÅ¬)…B­P.T EÓEM¦ õDIQUµEy%üUMrT°:‰öÁÀ£6U¯q®/^9Kô…NÔU=l ¨ˆÁ`FóUÌå[L£ðá)*bÀ 'ÆL\·Á•O÷ÆCW÷ÏÏ÷†ðybûkú¾Äe/Ð=ôG ËôâLl«r#ŽM°èeU•s<‰ìVǺÿ•+‘⌺ÕØÑô›vLã+[Yº$ oÙ˜†–4Oxc%ÇJ±Ì:Þ”YE®Öð@K4ÇaÚIÛÐú&0ŸU=Gg›œiõbö]n¾î(²kr}ˆ­ Ì%_𲏳mä,‚ãÙ™ƒÐÜÊ«áRšÂY‡ó73<ªŸVÇõÍdTûþçB,û€-›ãÐB{Ôí^ç'u$ÈõΪ[žK0s³µcÒU*yùÓ?ýSäšÛž5„62gp?ô£Õ†…šd3N6êÃhŒ’ßýÝßõµ´XÀy׋L.ŸsôaD=äE8d¹†È¿ú«¿ºRÆ– â£á„§7Sv|!7ÆÑIiaõ.|ábÀâZâåU®"DsÙÄ@¯GH !3Ä–GÈ"E‰,g¶4yé"`d\näã;/"{Äô€ú‹^Ç2ûh„c«´ÁhFœWcfÓ…Uë¬ cd쌩‡­¦ bB€( z‡ÄEÒ“ Ãî¨7#|ž•)§?%¸ˆ/BŒ(›YЃC$ ‰D‚ÑÄOõ„äšñ~§Hp}æDz‡m.S …‚ &( *ƒâ >â ˂ֿQ7T¨!ʨ|U½gˆõ Ev:º·¬¯ÆTäpÚ;¬r¼«oV§ÿo9H.ž=%z ¹‚ЉÂPñ®Å·ÍA lwáÆÀ#ÄÀ½!|„‹òä@êv3K‚ËMË‘T%[(–ôÛ‚ƒVÄNYw¿ó;¿£1Ýß+7å$ê¸âÔÔ‰´o:çÃÞtX’xÚ4¯¨±|G‘;v>³ª\E‘KÙG!IZ º!†ã©¨¢ì èxiÍÝcù÷ÿ÷ÞU/ó=Óú,°ä ì†2çw6!bx4üd!ÅÌ¢=­S\–Ë'æPû¿LXá[Ò¾I;a¾±²3—MŠ9—Rr%’ µÜÈz…(çÜ&û¸ÊŒKwŸ$Æ€®Á"šŽ’²ÄÅoŒãaî,ÎÜòÔ_b™„íÉŸsKQvPÑßE* ”!¤ÍëÌ,÷$xLt(6qðúg }3@?kÃSs¬Âf›Ýa»Ë_(° –u8v."T°?qf8“YÚ §l±2 ý <䇑bïJY?äç åv‚H‘:‚Gö]F_`“a{É*\sà€%“I±*†Å¶Ã± ÀÂà˜ËÉU‚";.¢£_ùÖšØÉf‰ òˆP"š(bаZîº 7«@ÐAÅ ªwJ<É–˜5\¼£Ðžè&ÀámsH@u¹DA «i™(j…r¡b(}Âäë[¦½æW¾ã‡T¢ý7êýS¸€¤|{åQ)jL­5ê™ Ú3†z?­3ƒ±Áä°Ìè¾cù‰M0ÌÆL50͹ۻæÆÀ£ÂÀ½!|TËñDù…_ø…’›rg œÁ¾í&2ä=ïy[D–·;6– ML[s¢˜w*L(·Î*®ißl Ù%ûfyÊû˜¤£ï\õN˜9ÐÛ\’¬ö&ž‹.¿ñ¿Q•ËBB°xsûS §¯ }×w}%·´ö`FUÂ]lÈ;Û-]1õ,‡»øƒa×GIYr?+Bÿñ÷ÏO{ ãƒ1¡ñÇ>ö±^ßËŒ9ã2e\äpž0;×Åóð¦sí?bœ6v›Ü±M¤âÓúp)‹í‰IÄnàØþáþaT4 ÍÊ!ƒPL49w¢FʇX®Ò xc!%U†Ù™ô2&Ò8d;Û¿œÍƒµÊLde¦3«ùTåþª¯ú*¹é —±2œÁú>›ÃœáÀ$׿mÐÃ0…å”ïÊG‹Ëg-e-ë\@4†ïmÁ ¡bU¤…Àô€D‘\6WéA–+gøÌ ä f$]ä]0 ~ýc„\ÙRAÊ7¬ä‘Ƙk‰j̘èhséPy cd쌩ãéÃæ˜}ÙJ""ÙG ¢ã¨“|"ˆ "Ž¥šW †&¾L-›ÄÚQo0L$š ñxÔ&õ,4j\ùTÚÃ[&½iC°ïè!á¾Ä~Ôâ65AYPõA‰”FH?W(£¡SOmi?ËãÜá]iL9n$y½BÕjLí^¼˜cË ^õYF‚Îa†ÙhO†s-7ùû³"ÌÆ ´0T˜+h²&ΘI3ÿ2rj¬»pcàqbàÞ>ÎuyZP‘¡%7©ÛÍä“"…ö¥&19Ìþf$³V®˜Â9è9nNJåÈËóšó" ÞÀP¤[йÄÙU³)$Ù í²iSE#êor#Ü(IhD(ջ˂«)¹MAáñ\fšEʜČ!aN?ñ?‘½î>§BúéŸþiïRÿË»=˜D19U¨@²þ4eiÓ[Ú¸qáb‰Å}åa€™ªö]iš˜i5À žÍ‡î`Üô¡%]Hç’LD—kìºŽÓ Ø˜ïãqç3›ß¹nÔ½õLϘ€ž–MPq-ÇÚf)zK&F1u¾]–pâš <÷b˜ æw¾ó9Tq3Ç(Âÿ\FŠ]/2+‘blY££ölüüËÇÑÏdX~LäaÓ[ý¤€öLð¢áUs¼ ŽKfá,ß° ý§pGÐwx‘Á ž0rBTH«¿Žð"Dй*†ºhæŽh‘.Fƈ¹¿ˆÔ<²GüX »AL5¼yC¥žz Ó™ § :èýcU ‹m1oxG?ÀЃor«hLDhF\ÌÑ ñBÈ5˽Π(bаÊ%1â+N+­€ ÚäˆÕ‹Ã£á'Òa;<šFÑàÄ8léËÓ?*€" 8¶Ýß]4JÒoR7”ÎÉ% üƒí’¸SNâÜ‹K,ÐŒ<2"‹‡ÉrôÍÀÞ¥žmØiOo1P’ûá4~5Ÿ„bÌé°b½%Ùu]úÿÁüA9Àœu¡¦Û‚æ›3Ö•ƒ8iñú|ç²–‰ÉÙl¡EæÄ­kiæÉxîrWë[Þò–¥þV©s ^ž÷”òûfsËè™AªAV1õ¼S²)W^x÷~¯/¸²úN Ø(2jxsm)­oþæož7¨üÇ9­åÂï7$•¡×yÎ`yKä(^K|§µØ¿Ä³¡ÿœ9XA×x\_‡’„ù±˜Ý†ì—[.ê™&n¹$Ê´VÙ‹}£XõU­©Í¼Ö‘s— ,“Å Õ Ë=liÂ(ËÁ ă„R'HÔ‚Ø\jˆ)꟤ò$ŠP‘k²Ö¸z@ØÈ{ øœbv΂q°°†ÂV˜ËÔ0Z.Õ¹–ĘØ“ ‘¬)ïà ÍŠÄ’1âõ#9CDÄÑ<.ÁE|é(D;„HÜ{ý¼B´ê˜[&ï ôÑ5q­åüG¼òD=Oìg]bÔ’RÈæ¿Ã6”͈ŠÑ˜º ?¥‰$f):´ô3yP©ÅYTÎ)Y«Oážî]ónnPèÔz¢=)zêÞÒ ùå&Ù9;˜ Œ&C‚9]óˆx a£ÿºm>¬& ¢[æÍ²Á]ycàQaàÞ>ªåxºÀ¾äfþŽ.};Ñ£¼tWEÕÅaÉ3¸SJ:§j/öŠÿóˆÓZc·„ްf5\C'y‰°¨ªÙìÄ vå«P:áƒÔø+¾â+ö±4´]örl‚ÍÐy”ü=̬٠®Û‚g ¤D¹ÕçÍxhAnx±Cb±X„™8ŒíU¨j>üáïa0ë&ŒÊ•láNcl8w[Dzæ|CMý1aY–bGúžØ<Ûݬ¾;Hó^4/Vú &&ó‹š7ëNÁY˜Ìì e\œ½é>–Ó5§9[=«:†îÛ<=3›P~Ìh?û‡ Ù^‚4ìî„ù!cœ•³¶”••$·+;Ø5‹¡‡´a$½ùÍo¾½AïýèÕÀ¥±@ÉqÖ®´–¸Ö 7$‘s[Ćè•q¬Ò[\ž‰;ufÞQ$–Ï«‹ÿá6òTq§n×hƸâãäKNˆÎé= 2hržÃ¯Ù!ŸË|ö``cí·Žy‘c[c¡Ds?C ·hö„nòX/ÊYWöe0“]CJì<¸©²ÜmVç±-ô<[ŠÕÆ-C‡F?Ýaf «¬L+›É Žp:s @IDATqˣƮ§Úh97Œ­LÙí f(Xħ0gÝÌAH¦,9>ãf8{b4ƒÌú²ôÀXq2ô¯CÇ,Þ…`8u±XÃ3b¶4ýu32\žÆÊ!lä‘zF$€™ÈÆò4û”3®½Árw!/‚lûÃ]¯À]~ž°–#99jùR°|1+^j¡-=@ˆ!+Ž<ºô ñx9 l‚ä²e:G¢¹"ÚŽce²_™’ÿ±çž-ÛË 08F`ÇÀFîËÉîMïæb0¡ì¦æO`Süï{ßûLÜ9ÆËã|ö§(Ì/yêÝGBŠÙõ±Ñ—‡¥<Ð1ßÙ:¡[' n19ÎLÌËÕ&‰ê»2æT\ìÚÐÆŒè;‘rĶö”E5œó8ÈQíð.‚l¶I¼Q }^% @¾%X®ŽÊ„ ÔªÉûR‘„–Û¢[zP ª€TL !¤À´2˜)"Hd™c.„Š\õ¦‡eROçˆ_cŒ€0Å@xzÀ>˜+ –4áƒé4À€›h=Ì« FN²¬Á±9fÇòóÎÁ•ˆ (ˆ‹î»Éô –0Q³_PÂ*¾›Í‰¡àƒÕñHH•¦‰žªµ÷Gع/ñKÃFygˆèœÎÍ2k”*À†Þ(ê * ÔŠÆh£Ëð¹1%EUi¹ß&Õ‹‰ªÝˆY-õI¥R¬Ô«žIžM´'56í[‚*¿îŽ™^/Z@. I?ÃÙè//V²%À,û¹+o <6 ÜÂǶ"Ožý“ûöÞÅÈ)¨ý}YÉ+PiöoQ±±9겟0>]w]$[—ý\ >\•OýÑ[’Çn¡ð>ãŠ;å·ä?¦çö`ä)õ¯ÛÓž/šQ<›|-5\B¤Ü‰ß+xí5È­¿Í‰™fÒ¨t‚Ë-¢YQ £ó‹Çvñ-ã‚j(ZŒ–I‰IÛ„ìz‹Ñ,½þõ¯ß·LÞvÙõL[Ç6­US󔽓qøHF‡ù“šMœ{Gýiʾ½–Óå@ ÆÒ?Äæãi*è‡~h^‹|ä#9wxïßù’˜÷œÌV·¡¸Àèrå Fžk0>/ömßömÃÒðs#iágÍHeõöÃ"Vãp>™Y˜\m‚â:Àwùó…‡ö¥‹Ê2õã5‹h)-h–ØB[în)Úx äHÂìÈñ !„„œ²ªö ÏFöI!Ñ05¢Eºý‘2òFäéÙhØ•/üˆeꛜC'-Þ¬·Iw”9è#ÃU êðûILJ, õŸD ÁâuBfï˜Ó2ÝzžÙ¹ú$ôô*b0$ Fâ±C˜r¸ÕDˆVvî—QˆÝ 4HB£TG %›+µI}ö°´o–§Ô23ëœCIQR—”&ÕIÆÓ:£E åKSÄÔ1’¦š)èŠöÌÕDJ¼vË{ú”#w߬ž‚P{FHÕÌLQ8Ãfnp×Üx„¸7„pQž(H ¿ŒاávO@3n’r@ûëQ¦Ù2ñJîû·+ø«¿ú++ÙRQìŒnLg.ù—z`Zñ ç“™nwäðg3Ф^çãß@yý oxƒ–¢†6½å³&¡bTéic9ýØO Ð+a®¹Yဂ·›Q(~LØ•)gƒÝ±Qe&”tRÒÁä0Gȉ1·ßŽšǼLútd3#J:†bV2¬eŸ«äŒÔaÏeGàÖ‹˜m,nÄN½Žb@ydO‚`MÕi…b ¢e7â̶¹ö#}ÄŒ¤öî¨C£` ble1ËQNH,Ǧâó1&öL:â%а6¯tĆÀþÙ[{çcn—/}^˥̖ŒÈ¤:±F¸q]عÛ „¤©˜Ä&áI„æ¤wyÖ:@8Ñ5q=<šûÆ¥öûÛ¼˜0TÊeî§×˜#ÚÐ-…Õëç2äP|ÔŸÆhÒ”©ÅÚ;u„(#<*5ª”,U “ÔîÜm¯Éqâ•ínÞŠ‹„‘Ð;Ù”sÞÎÙ´aÀÔ\æºi|?º1ðH0poÉBÜ`<Ã@Y6"‘6aÁdpñû±Œ÷ÙjDºŸre²ô[.õt.ð—“þ’éå‘0ÑV.ô»×ÁCOùQöK§¾·Øa #ñ3<è²o ‘¯«j¾RÍN3¬° âs½$“2×ï>O¦#9håaG4ãAƒ¤Ÿ}¢â»èE—÷x»ØÒ©óîB«2¸;Êxÿûßo·æÆQH‚)0×kPĦÜg0G 9xdg !¦ìBÉÝ“±Ž úa‹éû³R–’ÙÇüýë¿þkk$…`¾Í“j:ö׌(ØSl>ᠷሾÖ2¥CŠȂ“"Ĉ2m@‚fj@b0´ãþšxâôŠÔ͉A=6ÔøÁä•ýU· =õ&Nÿ¨’5¢È¨38´57¯løCT¤¥¤.)Mª“-EF±jCÉsYþ¤²)nêûtŸ×ѿΙ{J¨±ò5 æÇ­¦eq ’¨wïÂGŽ{CøÈèi—íÍjÙÏ\1ÍNÏŽª“„ ž~¡ÚG ¿ýío¯šMÁµø˜à>ÀuÔ,q§¨´9¡˜÷q§°!xÆ·qÍ”GŸ|¯´4Ö’N=‚¡×'îW.ËüñI*pÅW-t'[)¦À²·ªdØ%· 1Ÿ.œäÁú_β(x:ìÈ£p2Ýæ+ÀZî—Ù^±Ÿ ·o¼ °*Xý@´9yµ™ÝÂÒ²š–rc5ækzsù§ú¯‚¼9¯ˆb˜¾/ÀO#ù» `õb|¹; \õa‡!6'3¬ùygëdÀEV_ƒ~ýJÇgË'K]o®*ýþïÿ~m ËeÓ›U™í(hmÃð]8Å4Bæ¼(l+ÔrX Ëd±,Yo lY³Ÿdv[n‹né2H¤@µÏ> Ù $„ÒÒZ~)æk Q„:œ<Ç4×?òž“»ÀvÈè›/ìa«ÈIŒ†Ý0Ý| „=1iºÚ m,-CÌÛ°¾(Ä~Ô'ÁÒë{™8"”ˆ¦´¦x äðCЩ!ôˆ¾ÞÉ\&<µ´Äéüt¨I\=œÍò3Þ%Âùt¨¤J€A­ õý'•D1eâT•ÆÐ;{š‚ ÿZÊìHá„*¤{´gï9e\à-êõJî¯DhSßsWËš|Òc6Ü_Ì90>ê•s¹0óÓ»æÆÀãÄÀ½!|œëòD¡J4K”ÇÆô‡¡#š±3N¨A¥/ÚÓû‹à…÷„|0˜öŽÀjŸ›!òTͦ ;»Ô-öÄJf š«;\à§q§Ô¹ œ²,á7õ/QF DÖÒQcÉ-F ‘½×/˹£(€g¹^a;BµpµÓãGKœ@£|ÞÊRJ“Àumj,?Q^Ë“. ¸¢KHÀðÇüÇ¢¿j²ï’Wãïÿþï¨úOhOZT‡{ú‰ç¢dJɃÍür5Ë'c†Vó¯5[åT3õŠ‹!ˤŽ,ÀÆÜ‰#œ(ØÉ(ße—؇Pf_:|pŸj€…ׇ ÅàÕ|¼Ìò,˜’Wo–žK^ö §6©¿ý€Œ|}vÕ],¦díì1â#¨Þ†‚5âé¯u)`îÂ) ê’ŠsÀjý´µ¨Ý»E±4Ò´ôãËg-¥Í[–ØB[n‹né«+„<I—Æéüpù!B¤ØûIÑ"]ŒŒž­y,ÛS`-Øa%ðÏ[b³CêØÐ(XcbÏP,†ºê?AŽåÁ‰ý÷Dä쩪u!j@Eì>DÐrˆ,‚‹ø QVy_ˆ8ãw³dè*ûªGb}ßõ®w æŸ2±¬çÓ;~ÞµŽ–¢®Ü`§D, …B­d\Іº¡tL¢†N£=©3JÍÜ)¸D{B;ª°¦sÂQ©f·¹mÞqb"£ö>ÁzÁgç6\ˆ­CýD&›Ëð¨ÿ´Ä`Γ¦?ºË73î ác^'÷íË‚ôKNï$ìgNÜ„5zZç§_‘Êëì•$íøÃ?üã{½@)ꓪ«íY:—9ŒãNüü(5’Ý ›òÄ:dÛiL‰fGQ(ª‚qä°uâ&÷/ïþ©Ã˜I§y`èõ¹}GÁ÷Êe™VöIk=Ë—°lÐ+݌ҒÙÑ£{ƒù—qî5Ìr-ÏkÍ]Á&Ê”9§ÑL>b†6f´wË-®ªõú¹œ|§lßÁÈ`L|üã÷ýn¶ÒŠ=Ú¡â ·pÎa„“Å.dņö«n|gP¾öïû¾ïÓƒ…ft¦R Œ=ä`Á4{ÿ~êyð²çæ¡ÿîïþî´q‹e9LÇ=«¶ ¤¾+<ŒÅ|×­Ñ#¡½9o KºŽA€äXí;l)›Ì\Œë@}‚?!JNH›1™¨†ðœ««±–chlÉ,œå³ˆ!Ëjq;}ZúHÄ€$T÷Ï "§ˆ†Ì"‹f:E¨coôLIÏq ˆ €;` v6Á&˜I‡Dû¼0vÃtï`̰Õ&·gæ˜àmŒ¿?y#:¢\"…`!^†Y<]ÅÏB@S„Õ€Ìü$Ür–{å¶-±©s"” ]öÖ+ d ç^yT&ê5&ö¤ÞŠS¥J¨•M´'BÊBG¡=jkÎÔZ#Ry` þªfS LQ¯!¨×M³zDYë Ì·jÓ ù’-Òê•›2cCÿhcÓÆ#4£YþöA(û~î§7ž3î ásFø=Ü Ê­´o*W™;_W;zKztí¯liÒCÒ¦IG~ÔáPŸoñÊõËŸ¹ºÀ¤›­öe{êðn‰°œXf #šU`Œ­ÂX4Pÿ—½"d……!¨Éù€è&å²Ïy9 ûà?¸´Wþ×ýW\æWKQ˜(5ßIë,Ën˜€Ù·F6ÙÌ:wíd„c%HºN\sª£’>qeŽsé1L\:7Žæí±«>1òN]2=¤CéÚ—SP)Ä.wù¯¢ÑæEa³J)H2¨Ô…ˈ5At1Ö¹Æk¥2(»̾Ö=Ì4?í ¼"S?|p%¿ãlØ*Ç}yœìuÿöÍa1p}$"ý* 1€¥Aí[0‚N¨Ò¦þul"4kH¶1ödB ä,Ã@61ôBr¬•…|åZËd±,™…‹¥ð¯`‰k¹S †k´–Á$““ã/›º|CeÞv¦¤ˆ ‘e_;äT`ËidŸ¼;`Šlç:´Ø´ÙÄb+ÌÕ‡èe,™#2LÚëçrÌtƒ‚yxJ8qAh¤ÃOÊD Cì>DA”•Ø€ú¬ŸÄᦂ®* ¦–„g¾äqÔ,õD±ÆÄ2á¼oéiøq©±RÔ¥@5˜85qíIÝP:T H³Î sêé (»¸“ŽÜC'”© "’¡þè'e­=Å}Ô`¨C1 †ú£Ÿ¹ØÉð8jú˜–fßò~zcàQaàÞ>ªå¸y1Û*bÝ“E,“ÛäœÇ,±+ýö…¨m(ã^¹Á FnZ†ÑÅëæ¬%– ³æÊÇšŒëâ x˜bƒû¤ìñ.n “‚•Õ¸´ª0høÚÙp”:0@ž‡òðgFB}üØ äª$+ÁO½ªòêJÂÀ+›XƒÆ.î8‘^"90ö·]‘Ò¡[Ióe$1cð<é7˜CzÌLŸ Í®e1 (s³Ó{º§Ùè°¥ 3Rê±mLý$Ø€y¾dÁQúwj!{DŒì@RÿjÆÐa˸4…×[ÙN³›—a·ÂÃr¼i-Ø(¾ÉV§végOhv,¶Ád7s¡œl°û¥p0:ô¾ð ¹Y²Èý ,³¨D‘¾å=|éN{+È–eZÍ[‘êMsÝv¥ŠÔ”Ÿ` bùaôÂdú¬ĆAª^ÁÔ»–ÆY&‹•MTµ´ –ÕâZb (GK? "¤vÐ,'ZÕCfˆ-d€ü–1ŠÈ5›=ŒŒkYŽÈ‘:‚_Ê% ‚MÌÂ[û¤†Ú”aÆÐ9öÜ‹/ n.f§Œý ¢€@ ú»¦LŒ&D ÁB¼tͼ"âH{譙΅Üij‚§dOT&“*á9÷3Ô˜EÖxãá'ÌïñýøÄþ>Úæ³E§J(j…r¡b†nýpð3?k²Á»è“¥F#L(Ö¹«¹Æ­/Èg7ÇÜX 3€1€Î—2v~…™bë÷T2K'L† Š*w¸¾•‡»kn < ÜÂçó{Är]­´ò\`ñðÀÙMñCç®…\mÒ ÞÍ0*sD‡¾W»ƒ =sDûýÇs[óyµµ?ÕÐy%H7ì—º¶w›2ƒIçTÎü¨×ˆQIo¡;<èÂxhbv‰0$vä|¤["w*úKj8á7.ô÷ƒ5†…$%Z²ÒúˆË²Æ¹w%:È}’xå}ÿzÙ[*-´Ñùé+2í¨±äø±Œ“‘…Y<¸Ï&üÒî=±a, §,Ñ£Y¥î,yÅzAËQ3³NX»ds0"¶%-‰Ë_ásÿX5qž’"H)Áûž“Og Ýþ¨qY?Éço>h<¹7|z[çn÷ ®¹;÷°[sõ¥¬R§ å|aøû0d‰LÀØç€{)¯`‚Uƒ <{Ç;Þ¢:9!!öGÃÆ²÷£ì Ñ?tÕŒžHÁ”MÜô‡Û€"ù¡‘¼‚Òzoí…_tbQæÓEËgËÒµ¸–8ýXô:PB Ha ABBNˆ i!0d–Aò›× ¡æ( é"`dŒ˜‘ôœ $X@çØS Gå'¬´¡l˜ô$s³iÄÔuêŽÙ— sG±$q6X]~]£O™ ‡J½~(h:„·+Wær€ØÜ̺úï‰@î‰kh!º pb|퉜¨E5PÔeAeP`¶j§áŽ”‘–SAµ)PsY¯‹æïq× 6ÊœÜ/mÅFSŠ,r…&Y¢öè'[úF¼ç$>ǰ¬nD¸Bq2S¼xz  ãHÖž#VtMUn Œ0í¯ë°|–êbBmnZ˜á¾Ýì[[Üáì¹åY¥ø+¡>t?4ÿw<úGøT_q§¾£%ÍC–@ Š òˆÕ˜“«ý6/9ìÅ+sÃQÏ ÏŽÌó©×ðVÚØÚ»8ä|U¸rƒ´f3ÎF¤‹pàà¼E !Uek=)Ø”ËMZl¬Ì~HROSàŸ®0ŒÑáˆ$jRé°…­6GÍqN'„Ic<ÒIµK³áF–s‰â&ÍüAqãÜ !¡ØD[åa™˜°P‘·ú¶$5þõ »ÃZ»:­ÊSl“ª£Z–“šz± °-ÒO†’9_”?MÓdsFWHi^Æ%¤AÝ€.è…d¨†ph^÷³È’ {ËšW,´å¶è– ƒAœ"ƒlO¿‚‹´XErsDâD¢9Ö.ºí@ª4©ú!ûMŒ–ÉÖmÈfk‡%‹5°*†Å¶˜ ïó¾Dህ‚"!:6C{¤}Ä&qT£Ï¯e9S²vóÓ¡&[GˆZn¶‡ÆÄ/ô‚–@&–O£=³`&ð‰}Ÿ XBNePÚŸ"!YP4C1 à-Rsº¥ò–OçÊxWQìüh®A¢9/¥²ç§Ëš¸™ ÔÎH`*˜5ª`<0!‘ÀÁÞò_KÀ,1)& a®Ð,i 3v9ô]ycàqbàÞ>ÎuyÒP•qI›Îˆ ±ä£õ\ñ”¾Œ'Y×l³”ÚUÉ-kCJ×?„yð‰ ù¨c“y8‡f^×r~4×€-jÉÅŒùé\c:ç‹]îÜæö‰ðÜ›JÞb1¿ô|q«™;?"y¨váOÜö4¢Ÿ"g…À^j•¸ST> QCNº»$,!¯p Ÿnb5Žƒ\œÒÒR 6ô“ca–Mj6ÿjctrËM&PÙ^ ”“aï”É2zœ·È¨ÎöBÙ#±ÚHžøÎYùsl^ìrQ>ŠˆÎŒÔ|ó^tD0ýº2´u‘þ‘»ºL ?sœÂ(YÚÙº’LÂtìF†9"W&u}‰ÎyÎÉ×Þ\jçHNu†xQ&R±ÐŒÔåýÆpœÑO6?AlÿWñ„§¿…º/ Š5µ9=é[Séøé8IJ!z!9Û•jc!,‡E X&‹Õ1c)-hÚ[b éT= †Ú ÚÛ,cíUüVF©ìµÈú‰ ‘eíf«gŒŒ3’FØÈ;[#¿¹*ŒYÒ•ûD†2Ö‹¯ÁÐXcbOLš gÁBƒcsÌŽå•ΉˆÐùuÁBmÄ!–è}bm€þ©ŸœžYèþ¨„*ÑJÀ³‰ö<ºÒi¾Ä5¡T¬ 1N˜é~zDÈ÷žÊÙ¶Qå;j·•tÔ ×Sp9(¦òzýQÁ€¹^ÔŒÈ@{Êú¨C*ž¢§îµD Éi„õ–ÁºêL F|4L†‡Y0B–°…νn!Ž€¹ëo ×D:¹#€„]Â'}ÅÕGŠóS?õS2¡s“»£"@Kÿ¬¥yèeM¾ƒ$@kùt®Ìí ÉæGsp j‰eVíÛ¤æOþäO€ÍȨG-Õ3G¢Æ–!.‰;å©Vä|ÀU¢ÙþËbù×I…X¦'w©´é‚—´W¯f@Y»ØyÎ(6)õ°9Ëëlš¤Ò–6ôÏÁœ+¼ã²r¢»y…´V³ëç!²DHÕÀäêµÕsB˜ì´©Ê¡`Ü$´¦ÃÖÑî$nU¹ðÙÇM™™kRÈ›qÚlÒØOnj Cä§=I¶ 6ÚK¦/=FlСsV5ü×ý×êÇ>¡Ž"Y9>}뻮 sJL&²ø±øPtkÄÚoÔ(")%–¶¦iàƒí[ÛÝ‚á ®` &’s¤š{ &nú2XÎ{cèÊ2i ÷Gõà]h‡|K`!,GY ¾³|q¹A²è– ƒ8ð,òXb9e‰À´GlHá-ס"WD;lìv&…Ôür •Ø$Y†è'ï‹6˜.G‘…“^À°Øó‚$9ìÂÚ›q M8˜”~ˆ‹Ó±£%DÍE=!¦ÖÏZÚ•&±IxªRüz:ü ·:‰eÂ0õÜ?‘îEâ}³w­·(‹Èê£*— ÈšRF›øùþ" Ê®Wnʉp¦L7mú£DŽPÖT6ÅA(qªÜÒã£Á“2`ÒOæ–d* ofã Á¨µc`h†´ú s™¹RcÀ¹Á]scà1càÞ>æÕy¢°å³¼¬Äú %µNx[Eɬ g®AßGâ»åcæ¤ßìpJ¾Ó+ÂHää©Õ‰ðžo cð7óÊÓâ4åÅLÙÉ|íá42xÈþAöù=Z<1º¿âR0õÌ”}¹·œÒÞziY¾Rª—O&Ý,Jðaal(°/ÀÉ[À\ò³‡•YÃ(„½ýö«ÎYϧH– Âu,ÙÖ{SÏÁEnåÙ®ô žX.´È­çŠ‘YÌÖ°¦'kR)l)–ûØÐ¦ƒ` ÕCý$O³£V9Љ­Œݘ⿯c¥Brjtâ¡oçj¸þ¡Âr@”²âëôéÝêMÁ!÷¿s‹9êê Ñ×’+ˆéä!° h©ƒ§XŒ×4«!„l±Àæt¬ -–™Ûe™¢æøàðKÔdM9ÇÑ…h(ªýy¡.m‚XÍ :þȯï‹XKc,“ŲdÃñ²eµ¸–ØB[î d»èõ¥Ó !¤Ä’Øô‰$ ÁV‡‡Ã ét‚ÈQÙ[b4Ã2ê±&ÂJ [a®º ª3bIm°'&íy_°0FÖ SoÎ$E,Z½‡^• œø8޶ÐiI|™´X”z7Bè#-G¢=ÍÂèG„ª)°Ä,aë­!<ô<ÿ$Ìã2 Þç§sM®ÉQ󣡆0y>Ô/bŠÄ]üæ$Ã5:D+ „AíR¾†"¦Ž)åáSOÅSôÔ=LRý!i0 6dYÓüÎU?˜+3fxzÿ¼1ðÈ1poù=EðýRÿŒ§= 4&Ü©Þ+ÐtÅü"µ)ƒÞ³’O~ò“BAX6|®¾yÅÔpáJ$ªí%íÈ4¡zÙ|úgn"QkhæH DÎïªÜ(0“e¸-³iæ‘TÐÈì[zއw™\¤Àv bx´ü™Ô)bmê`$q§®ñ[’<ƒE2”Ñš´>Sº˜ž´»[4‚£ç)¾¨ŸxÌ0Ðë:Ésšù@÷˜Ú§Á`N6À£[þéÍVÄ*‡39”câMÐÙŽ©Å*Õ'c}žEÕøÖspƒ¸ê« n0{'D’X/ëˬ÷QrCTœR:ñ/Ÿ·ƒ SFWn¯eŸÏJNx°v C¶XOewDö¬úIÁJׂB{VJYáY}º*g‰zg_µ·©~˜Ñ^ÿµ_û5\?¹Ò3Â;Š‚3eу?]¸z šÝBͺ ŒQû “­H€ –ù­Лk«íA¯…°^·4½ç a-¥íÞ ˜±è–>mCž"D‚T ²™ã)2ClHái«k„8—8CDä½lJ¬x e†CÑ<2YŒÀzp¾ÊXC`Þ$vŽ+«M"p ôôKD h¯×çÁ•ðH=hÄZ¢=M <óJõ ˜”ðL´çpTnõþMüÎãÎ5„¹ÎñWØnÐk¨ 4IeXß^?—© QFóÓ¹]ƒšÛ»äò"¥Iuj]”)•J±R¯Ë ë ®þ¥¬©ldO}SâT9…N­Š˜ê÷ 3`uYô0YÈØX6¨Êòõ@ãiãzë.Üx$¸7„d!n0^îóÜ„¯h÷ÒØ7{—mË= úàz(¢=#‰b“ÔAH‰„Ôe¶œ´ì´ˆ¹¸Å!|E˜¯  a-}ûšCN¦FÌë겜Ãÿ.ŸöJª0ÂÉzåQ9ÁEfÔa;jLÛ%–¬ÎšŽZ¦>·Ë ÂGœÀìaÕÍ .îý'îTÀ¤Á6åÿÚï?V‘Ñ™q«Ÿ¦(k!Ö6“b?#XŠúgX8·‘ÉC®–ŸëL<ú˨6ö–ëXÎj\ԔŮê+.ŸANª ½ÜŽ"Žv9úç=R z{Œ%VÑaô€ÌR?Ës—m‚½,t-ØËêø·NÔ-¨“™ ŠÛÞ©QÚà“¾ˆˆòûöÏS{?¹U„Î oÐÞxoÙPɹ¿·¹_¯Wé)¼L|ø×t¶MÜÑ$ Û`ˆ‚.Hƒ:LWP ±ªK0Ö¤™²Lá5–Ï1Ô˜+¡Ule,G¦;PÚÑ4v ~F„="bx4ÿÌF’‡øs¢‰€"¦íÁ!–éôa•è#‰AÂ0;Lâqn®±^ºÒÿ•Í,…ö–æÎÁ£ó+‡~×Q¬xˆ–š«AÁF R…"µH9¢|Í–tرgFð;¨`Š˜:FŸ™&5]ýï nÃuÍÍ+ HaflÚxÄP)PÍeßø~zcàbàÞ>ÂE¹Az1Ö#^‡£¼;¿÷{¿§¥œ`ó£e uîò=…½´™–¯51DWiՌד£T´ìî0°óDmÅåä .µ¡™ã”oýÖoÍþÁþG¼™ÖOß*ÔX)°×™Y ‹}®Kù¼Ù:À¸xÓ#[kvÏ0âò§«A&"¢éÔïu@"Ùzh™¼ ¦À^aˆVâw—¢mwê‘]@µÄZŽX“î -!앎b@+¯60ýi/knög¶×ÏåJnáPepBwÃ×ùç`ú ÃnøšHn 1Ä— b_÷º×y]ŠÔ£$(ÍÆÌ,äN°¦FŸÍÓÌQ=ónö¹€Ÿ”6ìW¦›(8[W›òA³š‚ Õñ©M)-;jôé¬ÀDàœÍ‘ö0,§aoÓ{žËr‡ˆšKää¼"Ï­ÀÌ áQiš¬)k-E]u:FÈ„Rˆ…Þl3zçÂrXKc,“§–ÌÂÍÒÃ[è eí]©G$HÁä)8Ä ÂK†^¤àçH׌ô˜‘tw”%©2º9.%úÇ\XL‡§Éœ4ƶÁÃNMó,†I·ÄN¢= "âh/¯4bp#â:(í³C}ŒÄã0Üü“˜ÍXÃeã¹ej²}=ÝÆ¤1AMP§Ç¤¦€>35t44òà u–xx Žš£ìæÃpèQššQ£”)•J±R¯”ìò€Ñrx‚>f¨§ôQ;:võC³þ“iafF¯œË9xÌ\N¿Í¯ß57÷†ðÁ—à`ìñ"[Ý _´hU"yâ#Ì7Y5û‚¤ê€¹¾çÌ÷-˜/®¯ð\ú²ÝÏRáºv%FXËlUg²õ/¥ÅΗ)‘ °Fž >…LŸ¹…’+íÁöT}ò¿ž¶Ô€Žg!úÔ–Joæ¢ó+ŸrÒ>‰à¬Ôé9{¢È˜¬.aT¢ž–gn….Ñkœ'Ðüœïñ2ÈV¿¼b—å_Îþå§^d×]’þÞ5¤nêõÞRfZ¶²¤œÜËÐ8ãÆ$rÃîD‹h)-¨e—  Ä€$ò@$Iƒ¤Ä3Ⱥ\àG„Ã`„Š\m5  ÈI(5ȦÑS¬QýÏl•;~íôÎp¢?Œ‹…ç®z !ÎmwØ#@ˆ‘D{, Ž&R?-4ELÁ[¼Ä×  µE0Vå¦è¹›6õð(0ßlÛª±a:”E¯\–)-‘\.CRIõDIy„5¨-Cr–I š b¦)G*ñ Ãä£@—£Ï•Ù­QÐó£e ¥¤‹Q3z`Tho¥9ÊÆ<¿b¡ qºd¢tœ\‰š™Çºkn <,î áÃâÿýÙP‘ÅþDž¶{éÜ šqÓî›ÕSŠ¥r¥çz%Ã×T5ûBv×CYb!9H·”º›3Be˜Âf¸±í¦|ØŠuéw†¦—ÿ(H' ‚…¸áE”½å-oñ„±råæ S)~†ã~‚yê„Aç¬ÒÓ-–öìÎV¼ç=ï9í\‡1 y£7Ùa1;$íÅÁd…i ‰_F̳ÿ³ã_'/¼Ó ù08ч³Ž —”Œ°Ó, îÃäàËe˜½)c5³|G¶šéð”;‡ÉWѸ±qû”¥×w“®GÑ}¶91_ù-–hd¢åF·¬dvê| †ìüµáÁܱU¾" lEðˆ£´:ÄÂ~‘uh³@5_¤%Ò,œ P;‡eÂÌå¬/VêP·§;@Ô¾)2Ó1)S3Á,–)›¸éCT@È0_Hƒ:„Æìë z‡-œ%_C…3‘XDK™m6vþG¶8ÂH¸)R™÷œAËÃ,Þòs,Ï(õ5¯1ÒEÀóáváÜÄ5Óv¨Ê¹€•0”–˜ ‹Í z & .Ó2ap8ÇìXãg¾æ T1ÃDJÒ2å™êãV9ç´DÖIHË žšê­tˆ´´'l{ýQ™èÖ˜¿ ¥}¬ÉRTŘO9ÎòQFh•b¢ž()«IaQ[åK où¿Ž®zìòÓý|µÇ#Ú_ÿÀCB{ª/¥«F…W½r.§g-ý1]æwÍÇ{Cøø×è‰B(˜$âÕ¿´Ô ¼ìIÊe¸oYOîç~NÏW¢.óŠX,íÙ§©ÉÓž2ζ„½Ý(æØü÷›fÕ9•³iz‘^²ŠÙ”/¦ÀÒ#±¸‰³u„ú¸‘ÂÏ-ÓCmŠr9PWW60ÏêÕùE­,{^ÐÈ>;&s<—|\CÚ4>—‰÷]–-&©¥ì“~6q§ÐÂär\ <Œù’¸S67;´:?UƒÃØ£sÚ¹”!±›%EõŸÿýßÿ‚;”o˸‡ýqð˺œðôpË™k‡Ø› @QƒjÖ¡ EìTA\¾æL€ý4›ÎíHˆãŸ%”†¸ÚX2(åA6ðϘî;¥ álP°FwÆ%TÏÂùŽ.ý´cénøô3ü ]vׯwœWÙë:ÉÁïÐÿ ˜$pÞLÄtL*ˆ5M“5å÷½È"ˆ‚.Hƒ:L(…X€A2TC8´Ï9l,e²X–ÌÂY>‹Cßy—Å­© $”‚ ©xÊVv …„rBTH«C›2"´ÛDÈq"Ñ  Ñ"Ýa”þÙë60B¯ŸËXIKl…¹æ§½{F`XlKäba€agL á3g:'"þ‰ B£G{fïªóŠÑíãVÙ‹:´ÐW\D_d¼U›B|‘Ä,ÚØ4Ë#B;¨¨«†;ñ‡j|b !QŠà-AŽuE­ÀJHÈòvòÊV“"Ó¥vEÞªiOi^é\{Š8hL˜ô)f40qCPýWk“H¦Å)æ™(zÎÓåbÿw³ ÷†ðQ-Ç Ìç0à*ùËöKX]Ÿ{pPŠõÀû~ð|¬fô°Ã8ì+Šil1ýNÏ»ßýîéɺBîS¸xÅNù–±«5ëî¦Ú LÆá‰ÈŸ}ìcBt‚(mqsÎFya8¶£¬w.Ã@¸Fƒ”âp"4 ÇyÏ[ìïb>û…p¦Ë’ûélj{Cø8×å†êE~q²8êJA¶½=Râ_ Ô-•ý+ h¹ Âˆ‰ˆ©}·õ”S™mçï4‹I^¡ÔéoS–Ÿ­:Ùœ?Ä;iäàÌq3HHí“ðéò©:§ Â~6›"SΣÇÁ…öŒfï „Óuá©âTvêI5QŸ# ²)bÌU$Ò'@' Y«Ë½RþFÙ1³€-(Ρ+b`9Æ1—yŸü+ÿO%ŽQ0!³U€<ðúóÄ»lc¹µ=uB3dc¼:šØP¬@Á|9 }dgÀrBhcw¡C·È6»Ö³t„b8˜ï|ó›6<Œ6ªi •dtÎWæ²ÝÕÀ©»­ŸYðñªxäÖPmBÜŽ+6WïÏO§Oö€dâ¡^,Y@^jxé2Ášb–N½ì‘ #¼ÔãK€‘M—_À$P lÀÏ32M“5eÏ(P!Š ªÎ$ OG5ä[‚ì<ûD”—Ÿ¬6–Û¢[zMTõß ˆ 9vaâiƒr†¸ÖW‘ëÐ(Èñ÷Gsûh‰•0Ì$Ú£a7L·!ƶ˜ [S쌩±väfÏ’aÿyС†‰ X†Gý'¡D4–˜êõGåÄØ€ûsÔ¼ìDê°ðOØ5/¬ QL oÄTÖµö¾w ýPÔ6¨÷TnS1ZR7>êS¦Â°u¶w&Ö+6ÕÏ4åk^³?‰­ö 1N= õ bо.eTýQÅF&œÆøX=×_Žôº½ëo (EÁN5ôúe9)4©›åÓ¹›€Š:›-kLVû‹§¦z@êÚSÇKý²"aØønùt®L®FÅüh¨qÅ0ùc´ OïŸ7¾P0po¿PVê)ÂY–QË+|Š‚ŸüÉŸÔ’åqÚ2 øËs=æôø±:Ì•’+_ïÍ+q¬27й:WøìÝ=«nKV/ðûÜpSAô+(˜´‰i¤ ˆ 5Šh$øŽ5Ó ŒZ3»ÛD1Æ@0A3ñ~€¾¿sþ}Fש9gÍzöÞçôZûO°VÍš£ÞFªú£jòf±üû혹ÐÛfNÀˆ1ŸÓpv²7}x˜b¯¨³(ö†G°bñæžôÝßýÝW Ò7WÈ|.L °pì+¬96Á~6Ü a¯› ËæÆËÌIÄw P¸½¤šôt*IÅ€KgrØF˜2XQ0Öý z­š<¸½Ùƒ‡ÈmÏsðó1€ò;å=™¬íŸb7õã ¡ºÉ#Ú§ÜS•˜ÀRAºjžÖÙ•!NŽeï…ÕNûH^ªÒ‹F‡aàÊÄýžj vï‚WqU¢½¤ÇµÎ^þ¸4¦¥ê”Íñ¤_ú¥_ÂLô:ñ´-cÚ G«f½²•¹"¤¸ôE£W™¨<úê&MÓÀTO“5|¼1«ŠÃ(¯0 ëFíc£ §/N{VU_è8ÝwuïKmîÂ0™ˆÓŠˆ_7q"TãH¬°Öi”Ê«6á¬øc€`o”D=þŸñö4 ƒÂÐ0@" Å à›ÁeˆÉs :C/›8¹rÆÀ¼=âk€ëDyò;*Dv‹L&¥Û%Æt„E8`‚:%#õQ.M1õeÓÍd¨ £~¿òÔ->˜fM¶¦\¯¾®M·2Ù™¨KS÷XîUØB€c¯*^3QZhv¬šRY¼ôšß¦s¦Å1;,·®.U%K°*‡ÆëÕi F%#ub¬Ê;;Ó¸Ðv…B>À4~§ñFÖäÅ•0$áÒ‹xÜéAjgyWŽõL˜§_(aŽ+ß<ºD4w{\]`Ê3þ¤_¸²É¸ÜB €¯ã)05¹®AH,BŒ¥í°)Á©ZŠæW~åWŽCÏI-_;Hcä'!;»– ]’9g°2·ý®])efL«\Ÿ=Ð;RéôÜL;¼qXV2”­ÌQј¡*©X6 Ä«p|ؤÒÍѨӃm˜€1éHˆEÇ#m˜‰¥)1VGd® t„îÐ)ºæ´nºR‡êV«‹ut\‹u½™eL ±ÉmFéÊvDü²[G ‰åi>" 3‘&؈’¨¯½=k°>4jCÉ€:ÝŒyöæ´Qï\U â pu0ØwæmŽØ4b2©ŽQ¶-&»™‰ëÔ-ÿtd)¨~úÅ„Iq[¾‰Ô+“ª©õj¼W­LÑ©ŒI»"K@æÉî¥-4‹ ÇWñV¸ýTC%±8j©…²bÖh Æ®}çÌl^Xî×9×[C/cóöc'`Iö¼²´T&h¼.´Bøºúë¹j›‹=3ÛfÁp'»ã|¶Ën¨ƒþlgFuÅV+ÊÛ3*ÅG.Uèí©_ÁŽ¢¬@L[nÙ®˜uS~¬CP`ÙÜ*md¸ðé]É(¤ë-¬ü¥/}É—ëhAŒfé‹J#þàþ ÷-‡¬T¬Ô°ÓúWªc@BÉe"+N–+…*ZTCebw’‰Jªª «¶Ê—nSùk¦Æj²†k>&`…·ØrªwacFìÅd¬V1l?3#>.@IDATÍÔYºLÇɰôêt.ÖѲÒéW×´ $0Ù"!B§ýˆ†à?Yi#Cfˆ(}•¸Zu ÀÕk(§Ÿú‚á`PˆaµV3SçÅßìUé‹SßË)¡¡­øo°O¯Žu»£dò1EMÂ(S“ J“MV“LN ÷hÒã!Œ¦A“¡)QLÓUU¸Øtº¹ D=&0kGƒjoŽ [*æ*`YQKÌz'®’«°Å‹4ZÈ*r <ø¦ˆÍ;WeeñEo!^d;¾²¸ÇßÁr?Æ/ „"À‰¢3€ ‚ xXà€P Ɇ/âÌ3;‡ *Ï4^Z!|QÝÑ•™9Ðl{û³ÿjúŽÇ ¿œ»ø#9=Yì4_³ëœÏ\öų30*£ˆMÏj(¼_D>àÎ/è¢ st?±ùÅáù_þå_lÕC-·ŸIÊ;Kc!¤MçŸ5'j6ÑC,´<èÜA¯¿4™Aæ¸+ÑOQï(ô8XÉíÊî5OTîó¯ãCµéñ•6êÐÜr·íÀ°9"VëM$JU˜Fq Á¹½ñÅŠß)¯¹@ɠб™ ËÜGæ.¾pÆЯ::“£€ìs3>z¶¿ž++ô/ýç”F¤|îsŸK5èBœî‚®¦JBó1`bæýØÁ¸“#.EÂI³€~ÂzV%ž ÕÕ5'8Ãr˜®PêVÌ W½Ÿù™Ÿ9ÖÁK}¦z®%‘ðØ ŠPâ²ý¤ª‘¬TLõÒ}ÇÌ5JÓ4°«á9‹2)ö˜†uuQ}±)gõÑq%õŠâzõ™AåêÜpOw/Œ*D%]@xˆPUx 9‚§DBH $±œtæªj؈3ÈâíIàãí9æ)l€$Cfzu|¬}™kŒA­˜¹3ýrÚl1˜LL)&Ó‹S gg^š>ícr‹“ð¦…Êä ÙÙxÂ’þ5]ÙuŒ1ùë¢e98¾b,+:tg¿, Íáè-aS>Wú½%rsOÖ²›Uþê0ê± a¡?¾ž@¨0X†a =~kÚ-omð”ÃùZ8Ð áké©ç­§Më`#;÷®–q¹‚eõGôGíÈÚjuTæ ŽÔ4=demæ嶇RlîÆ r{»-dxeá¶§lÁFÁ¿°îž]ê Âk²zë6<ô¶´¯”Ø¢LÀîr¬=;Uú¹Ÿû9™7S&W¹À׊¯Æx«uÄ&ôùïÿþï0^óIØN¼mf`¢e7`OˆáùÝz Ç@WI5~4Y‰Ç‚r+‹Ç•UdL‹OqPÂÎ~s¸ø«éô8)чÁP¦_lƒ¹5¹NÍ› Á»CÂN¿A+^y˜ÞâîôÔ ›ÙcýǰÓbì9É–Íj!ùÎ,e 9îe˜€ÎÀ½kH\CÏÜ¡="®¢ P{˜Î°‹7´ñHT¨„ YUºM÷ØÖ U—›ë¯¾–æ‚¶8ŸDË*Nzˆ3Q¹tŒ9ÖöH#™le®’ RœBs7ÉD¯hUE¬Ú*ï­†hΨkllkÞbV`¶`av+&{%Äj ÇvÌ—VGȇ5fq„O‡ænO”:úôüaøOH¢yÂ#!A"N„Šh0bv¥±ËœˆTâJh‰.&Æâ‰t.ò­^> `nƒåøvŠÁ®†Ó«ã£áq2ÀoMz–Á‡`Š0QD +Û²úŸþôˆŸWš,•©É…W&«SÏ“bx:ù+–m2éæž£ÉYþ¬ÇǬNcLþèw¸mAAiq™ ˜§ÙŠ´`ÅàÛ镃eQ–ÈŠYR%Ì\ÛB °¥Ü0±¬gwÌBo¹·è[ú>0`ºPC³°r¨0ø¡@€$ÒBîëõÛæÀ á@+„/¤#º+ú˜”MîWtV,ºVåXüìP~á _ ÌRÖHëêÙžþ —¼±Ø­ª¸÷¸¬üOÿôO-HÁXû:•ß,“Wß;6*·º­ïÕSñÓ -#OÃÎ¥2 fŽ m ¨Ú¹8ABë¢5Ò ¹Žù\…¹$!†¡yã\ÑŒññNäå$ [ŠÝ_”Ç,K¾6]¯ô€/4`]'€¸ne´ Ãù¹]½ë ,܆“‘ãa.W±[„QìU&–¨Êн ÞþùŸÿyüN}±`±‚ŸB3Áȶ;ôã¾äýH¯@É’[T:HoNè!Æóé-K—î0@ØxËš—ÒÇ¿´)•ÇüÀzYÁâ#Œãt±&HÅFY’óßþíß:h„“ÌSG[œ¡-ÛCÆ <–­Ìºj¨Œ*©˜ê©¤ªÆV©òš0šÂ4P3Ãd ×|L8U,S= ÄFÌÄÒÉ„ˆí˜Ÿ¬tÇbJÑ•ñhE¬‹§þÊ#‘ ¹¢Cц؞ðÿ”W¢ò!¢H ‰åtcÑÍh"Ì·ƒÚ ˆë¸1u%ŸUsÃ-qE^ d Ñ•1À sƒÝÇ|ÃÿÔîZí5˜FpÔ¢,Ó ž›jt‡i'®¼ºøªè1Lnø¹ãÒ/¡i±)tSÁ0-«¶)úxu¬F…Mûè-;]T)‹K%_rç*†¯Éê-éMe6=J$Œ6k ¶ ºÕÒìÑ2m±¶d‡ª"?³x ¥ ñâ¡H€@ÅB3e¢T£:Ðx¥h…ð•vÜsU;ËŒi×öümËmu£4¹ŸRòK± Àq%’ìðÙÒæñ% g!Ó:H— ~1;?Äœ¦¾ÿû¿Ÿqt€9ìFÛÈ•œ²¨s¾R¨šËÍöäi­Ž‘ó7ƒ(™ÀÖ‘21@XÀèÎùɸ˜Úò¿E`É<4ͼ½Û ôVh•ÇÌè_Õ\¼ƒUè9çìÐÛe·~£·r_å©]¬1Q’¡+FðÒeÉ"ùÕÁçĸŠñÚbîp²ˆ¨š/à–9™+纪›l`±Þ´c]ñ§Þ\ù ¹JGM`LBÅ ¬¬Î‰q¨é ìB™:QÍ1Ü–<Èbµ€Ëƒž9L.«*E¿Õv‰«[¾Š ø&üT—¯\)Š:Ŷ=g-&8>ºÏ}ƒeHtj΀­¶M¬:`½ž½êÄ)^Ã)§Ûþ¦$‹GE«Àèá¦z*™$ª­òš !š£Qšv%u‚-˜ƒE•°«½cÃcB‰l!!:QW"Ó­Ù‰ÐѺ[§ëz@ ÃUÉA"N„Šh°ì ‰YcwUÊá:b|ë—n8*i€¬ >˜` ÅŸSÍÇ‘h`ž©¡jÀ¶¯!|ÚÝápþê‚iÁä`Šˆ8™4Lºodû6íHn Ú1÷™ÐLkè1|Ìä*Œ>»Ÿ¦Ð+š1t¢üOM #eÂÚeÚG¿ãšst–•isê˜mb,UéM‹×ÍoATÂo4é_Ë¥E“ˆZ@µÈbjIµ°Z^5óÑuYÍ132c‰·Ð[î-ú–~ ‡ðX1àA­ŽEœeñþÖm¥í@sà¥q —Ö#]ŸØ6çúñÙ8yýÉ(;¸ÐŒŸÀ'ß\>åÞ+÷HÁûéÜ”àîuPæ8èæ¸ÎÕ½©äâ/ÌMº€»Ž%äƒ>p¼ÄÑÏÎL;‘ĶáÇZ-Â*©hørf’hË­ôJ_dX¯¸ß@N2ßtìá™–{NA+Û ¨Ot¼DRÜÙt¿=F¢×TÛ%£áKs »¿.áîewȶC‡±E÷y%»;Þ²k`óÙbœÑ}kt·å."åÖ¨È-5qHl‰âXøœÍl& ñ$ Ãß\ñ÷ƒˆÜѪ66Ó`¢‹.9b‰‡¤y .Ü Ý©{;˜ÀMµªGgr"f73±ôó‘qØì¦i¸]öôD–HÎx\àx*Æ"¤Vt*N‹:ëªE·Z"HæÚ#Ûȇ)|••¢U@5TF•¢íK«ª*¬Úò¿j—&k¸æcV„'˜»0­8°:[W˜?žÀœÈtœî‹:¤Cu«ÆæÞ©©õ¨E„„¨bCxF¬L´Â+¶ Õ ®„V¶8~¶SÝÆG!{X†Nޝ¦°Áeˆe 2è =51 ÆjÂU@Mt–n˜솼oø›ª“Cî‰5]TäiÀ„C*L>›HÈ)b“ÛBu Šs©ÉsÍçJbBÖpœ›So>zbv[’i)A|õÝ‘cæ–*ô–­ñ‘°´é>r¥tKž…Ïò—kx®†˜|?ƒËl!¶[”õšJZ¦-Ö–ìéœEòýkÂeä¿ÕGžt¸9ð29Ð áËì—®ÕÌò¯g˜‰>~Žõæ÷ÿ÷?ޏùo5 î´nÝ~ü:<+«mQáݕ璃_ûµ_sÃ5Ìå8ÂíԸݫ?F®hvâ£Ú˜çlÆÃo¢þç¶bY­?®È·þÃQQ`±¨ÁY…B*;F9©àWôz!G¶*Ÿ«@¾ê Gn®”¶lq‰kÆU¶o¯W}Ø@Ö·ð1;Ä{ |Üi)¡X±˜_ì3ŒpÓ#6¿×ý&2ò0ð;‚­ù2ù«£Æè|‹ŠS 9 òt¥j_‹’PsÉ;L¿¸cC†1@‘sÚ HÅFËÏ(¦~ÄFäêHÞYÄ·#{@^äS¹Þ¨?Œ-Î ¿dHåð²ñmÂ"qL>ÈððöîÙª^ÖýRd(ýêq'ðhòÔÍ ÕxÕvlI0j²Å+ìÅä°Û1ß+¡;tŠ®ÑAæñ´E::öa]OˆA&=‚±˜T UÊ%fk#A%®Š&º‹ Ó(C êœAahDµ6X ÇðÁ1CIÝÂÃÓ‰Ô/¦áiª¬a+à•l8¯¦…TÛD±n nÇèjò¹ÊmŒ7•éz5íÛ#Á6UFñ6yN¯NMÅYMΧS¤–fßÍä?½:>ZDÒ•§ª)Ù³ YŒˆŸ…Éòd‘ÊLB>É¡ÉDzÕqS<.Y(-—MK'a¶ŒZL‰Ÿ…Õòj‘µÔfƒißiAW9ßT˜ñlÄÜxäÉCÈÓ ½0½êÇæÀkä@+„¯±×ž±ÎŽqgò…cnÛo³±¥å–²â*UÌm ç©ö·³;kñpï¼{ `÷º†Á{±Ü•_Y>ýµ‚~×w}—3Bü£œfÉ9ø>!ã$ Ùö^¸ú€˜ñ5ZÜH1qÀ¥2·TOñWeðîÁïÄ¿ü9ùœB‰"‰~eŸ8*ÊDPn_ ôÜì\HȾ5ìŒJe’€‚¸“w.…©A< ` hžúh|th‡[ 5nÚ ÆlA\Ýœä9Õ¦Næ+=¡ªC¨âž`2°Ñ³Ih!]X¼ô wW.vî!‹^Az‰n(¾þýßÿ½ê3ø€Å®.N.K5â!æëv° ˜¸¨ }tzDøÅ&5м§€W1x&^Âàæ‰LG/Ž)*TUY U[å5!É5JÓ&†ä+ršs°è”F$Æb¯ÜáØM~]h¨F“×¹ºxÌŸd¶! éŠo¹¢oMg2!¨( -ÑËJ˜uOì îO2 Š0!ì:ý›Á…ÿ†›Ag耆¡‚Žcß°EW;ûbêfZP¨)bqM+2™jPšvŽ…Ûk‹ó§iíøö4&òoÚ<}{ŒÔ¿êƒ'ÇW§1¦zôïé„“$– ‡å#“ÅÅHKŒ…ÆrctXzä³ù#f–6 ѵؑ^ ŸšX£Ínz©¨¡U¨e÷´u§‘é\ާoO#Á¥€§oÇHb&'c|‡›¯”­¾ÒŽ{ºjóRËäki¹m¼­DëLpkŠ©¬ý—¿Íæý­Ä¸h•Ém ·\]ðä*ï¨#@M>Ÿ­òC³ðÛÉæ,nìüe©à6ù9'c›ÿñkr±^´˜,‡ÛÊÛÀ¾quÓ]Êy'Y ­ý·™#pc.rtd‡Þ¥ÉÿvÃ^…ƒžÑKdQmxåÖíM*™ÇÜĬÁg·4Hš‹šÓ/ÜÕ¸aÚ¢§¼¿t.`Á™Êáߢ ~ù(±Y£UÕpéE€ teè`"p&GM”¸fšÍï|Ò€ÆõS?õS\9ˆ?6¥Ó¶È“•{&ãy(“ÒíÙˆ_2ÅŒ¼#ç±.ÇäæwZÐ"RmŠQgá©lôq©ÕŠé»»•[𝋭¾®þzÞÚZ&ͼ~æëµ]xĶ€˜ÏÉ>Ë\P&Éþäîò÷¨kø5VNR„Mñ1r¶•CÇQ“ñÊWìýû*ðäüF>Èáü’"Þ5ÁYð´8üÔ9¹ì9q¹KÍõ° ûÄ;{äÚS­UyçF;ôî”·»ï÷w÷w ¶Ô«8²ªù­£)P‹-`åíE2׺h/˜¿ƒÏ=¢‡, 3iQI÷4ØWŽ Ù­‡«vvÜÕ\•€cxHàì2íôÛAÞKCXìkpîJ‰lWú&8eTÞž¾-V§¼ÄC{Ε1Ôän’Å÷¸V¶—ø:EÇîôwT–0\†å­±H'N?YÑëæ2âÇöÂ¥0l—\` {…`¡ëMèSó©ôõ£$¥iãxÑðdå/†` SavfhD`2Ã`n’¡.¨c`º&“V—U| “ è1¨ø)@xˆÜpu2&9‚Gü!Q$Ä2"§5¯H%uŸ¶ã§`8†ÆTz̰5¸G+‹ØPÍl©J#Ë` ˜ RóŰM•4Éh˦s&þ|4ýoÚTèé£é1ªËŽaJZ—ûcMȦe“³)Z˜®MÚÓÇRóêˆuÀ’aá ðÈ,%ù¸‚Å…ÐZh,7iÚQŸèÕ˜|uÚ^‘AEX¯¦x‹lTµ…´LI²‰lYŸâƒZ š¼Bj´î¬·6AsàÛÎV¿í]ÐØå0‘ÅlÇÉÄê…ØI†ÝÜ¿ñ W;Hru=éi>±·ì;AZ¹/dó Ía?ËçÄAoIÎí e—³‚rr•ŸýÙŸ9 €òuDGK‹¥aìþ_àõôƒö­]•1jìñ³‚SOxŒü⿨àïÂßuL•{Þ4y}ÿŠ$¼žäLé]_“Ì¡êlðt*]h,w gà ܹ啑!}Ä=ÉV4k ¦qÓA´,⑸œÀº=‡^Ûe§*0ù2‡:›šù}ß÷} äÄA1dô…Ç 6:Ð@é2Æõ}ª¼CI=s‡‡œÙ…ö:•¤!è…o°;-¢@X¦„TJ aLpŸÊ©¦­JÀ·BuÖ”Ö£$’m™\i§U4bUÚQ‡äÌ>Ã^´0H¢Q7LÃ: ÄF1 9ÆNÂ6>ê]ƒóuÖøj ëh9£Ôõ UbCxBFœÑ"`]Ϋ«q%´D—cÂL¤ 6ñ&äD=þx„ÇÎf(¥Ö.i¦Aj¨ª˜a»P‰‹'&²‡þöë&–ŒÖMÏC—éKΦ²*nÈ΋©²ÈL¡˜f:=ý¸‚é÷ª Öñ&|Ó¾Éß`!°è |\¡¦‹Ø*5yS»³0E»Û9š˜æS–žU±"â¤iÜ)~ñ˜ËB-ë šéÀ àaŠ?>æb,=¾í˜æÀkä@+„¯±×ž´ÎQLÁãÂyÅ {áš;¡dÂñ üA6?è$•[}àËr»ªOÅçX€U1ë€%3W™9»¿¦¬·ƒ©÷­…ý§ˆc `,R‡+èýã"eûŸ l á9ãJÀ‡6›¡C‹¥mæàrñ"€Q÷%rƒeº–jÄYq~á|î‘»v€Z€Ëæ‘~}¹ѹ—b×"`a»VÛG_å3KPÕe-<\Î\¢@K‰¤i&·:Ž‚uÁø«Ÿë=˜|µþÃ|‚Á›Ë?Œëº…Üм®½ÎÜJ×Mc¶ªæÒv˜Ò••§órÜ] rE½ªóo<–ö5Æ'LG¥ŽÍ¤ 2Xêsç¾ÖH§<K"aõˆ cŠ %‚õˆieÊöô°C°s°¨îöĺØöן$Hº£ºF7-¤NGKA¯ë 1 D‚` '*¹Êè´ÂIðˆ!$бkN"ºöÄÒ¸ü{¿¨j^iZvC «[b©°WÞÈS&Ú‹Þ„p»ñ¤™(M2%SVÓc¶¨LbGmÇtwü¸Bf`“¤©Ò„™V·×Ò¨É&gS´‰ÚtmÒ6u›ÀMã&sSºjä8·‚¦ª-…ŒãÛÓ zRqlï)½HËŸ$–Â+‚)ç#iÜéÕÕ£EÜä`<Þ:ÛW ‚Z ;ëf6:Ñï«ýUPš/“­¾Ì~éZp Wš‚¡%ûÍ'ŸŒâîòè|GÁ‡Mã¢éTÆ' ¿|r^HýÁÜ5 Óç2§5ÆÈEs m¿u…’ WœkÚ"ÏñU>¹ö#?ò#<…¸9”ÂóMÀétxeZQ¥ã+|* Ää¹g§Ü¹#‰çª¼:ý²¶þ=Ñ1n®46GŒ~ó7slÑU˜>h¾yc-[ŠÂ.ÐöUž¯2Q8ùèã"K Ži9T6âiÞJÿ÷Ï–òðî¸çèpè%üÈíŠá («\ ¨|9rus…ÉÑ2ÅBœÒMugb6GLgˆ3• Óyè›Æ´ªä1€cÌwÚK;M`¦ %}F=ƒhiC>}ýÌ-îŠF¼LdU ‡ÒðÊ+E«€æ¤û™Ü¾Âù`Nú»0-©°qméÒÑNÕS½ ¬u¥Õ­:7b¬ :}á^›¢ ÒFâD¨DÀˆÙè§Â²BÏrx‹¹‰7!GŒcÄ~ªêñÑð‰`@ßcr3¤6ÖŽÀ‘fŒ1üUÆTp[S JÓ‹IfÌa › LS&+S–‰ Mbù¸‚iÍäfŠ/«ÍŸ)” ?®`Ê5ñÊÄ«©W&vªQ¹m¬â£kùØYaS¢…I}ö/'³ða…Ϊ£³W5¯xË«",µsˆó§ý–²²ã¬ã*æ*€9Pjµy%ìUVßx9h…ðåôE×ä†6 ÊØÅ¿¡þÆ7lZ›¯»[Ê"ð™)I¸UÌm€ÉK›ë·”EµaÓî$\˜Nvô+“uÀÉ~µr½Ûή6¼ˆxßhÉâ0Ö Iµí¹ê•‰9Ë¥ æH²ïÎAÎWÈðÐú ØåƒÌ,šïò³Zs†}aêY|@oÌÁN¹œÑûù†Â|IÝŽSþø²Ý"°ºÈRt¿âi*Ò©¤lUðQ„àT>WîÀ¤<Àñ·VWDúοá³û9ÜœÉ*Žm¸Áål­«8½÷B=´; ó‡~,5'5®I„&sÔŠnSJ]ˆ=Šô 2Ä‹“„ Ú7+sƒQA™XgÈTW˜¯ ’¡NÑ5:H7é,]¦ãÆKSOËÕ/Ä€0 ‚A<IÜY‰Íí‰_âGåLÕY»«<Á&Þˆ‰úŽ¿†•ÞP:îM¹a`žòß4jøèMlhÇÜÆ6ÛŒé%W0á˜vðÙd"2=$Þ¦M›ãÇL‰jnzÔã¦JüY,S®‰W>&ᱪ‹pÌq&ùM^Y,òÙ’µøùX’ÔߨYW{L[.Ž‘ë°åU)û.©r‹?ðí‡jÇr#uzaŒ< 3½ªŸ»o=ͪ#›/‡­¾œ¾èšÜsÀnk&â[ÿ@y9Û þµ3ÕX*ü¬ý$ÂþñÿqÌj¶e«ŒulcAœWÎI˜pKm#åÎlð_`ÍÎZ˜üóí»ßÝÐýë_bà°#—ððB`ñvÇ€f÷’Û¦ ¨"ý ‡íp™xú¹'Ýõw<¸ÜœÎ –;ºq.ˆ„gGgSÿìs%RÏ4gñ—<ÄÉÐY¦µa3™PE"?ܺn‰:Y”ýx<¹ÝïWzüštîQ›å)}~õ«_å]¼EÀðÄ/Clý—ÍÄ®†üÓqÛä¬:´qLõʰvšÉÂTdŸTÚHÉa˜" z9Swšái$…‡ªÃâ¡xìþÇüÇOüÄOTéEz…bIbø:Íó4ÒG#TO%UU…©QÊlΘ¶ê0FŽa ÄFÌÄRŒÅ^LÆj ÇöØÃGúÓðØ›ºXGk£N?:IÌæVhñ*m$ŠãiáÓAA¤ã¯Hȉú)Í©ôœÝ5ˆvt C2G Ò1Ÿ«°! 7 ˜ 4“M¦…é¢>®0z#Ÿ²wŒÔ#x’Í—é+W0¡ “›z•[“! 11"¸ªðŸmúâ¯Méê©so•|9X,[8n'¢*Î’$ îUÌ:`É‹›À¾C .)‚üìÈCJ·_,‰¥üÈó«êYñ%1îvÚ/bôÉU†ßxuh…ðÕuÙSWØ-fa?[’;ŒÈå{î1ß!MlM^…í§ú§÷KᎥ‹‹þ¦¬¬£Al@çôêê‘——" Ÿ#þ;&±¢#¶]}|uÃI,ö„ÅíSBTŠ€ónMaIè@ƒ è\ðü×ý—H{Ò6°awGP~þç¦gMáxÐJû?hÌ!¢ñƒìOþäO\iÈ÷ ðõC…Y5eëL×-Vy®P¹…zÛ9޾$íìâCÌùƸ³¶Ðª‰ÒY½Ô'}ºmê‘ñÑý„Ñ^˜>Œþ%iL(Ü]OÏ®«-ì!ë;Qö9J¶W·3v±–ÀÖ.aª [¦l«—Շܺõ„ªT¬z;¥dÖ*kˆK%¹"¸F+N¡y[%Ž9«°j«|©…ÞjùÑLÕä‘þ-úC}tÒM:K—é8ݧu¥üu«Îûz ŒX>‰Ê­¸¹l™Â[ÕQA™±eǧCéQx ŸBƒ1gV±7³Škؼ†°l8W0Ø#äûl'N&Ó‹IÆTcÂ1í˜|LA&"Ó‘I)ü4M œ9qøôQ…³sdb<%8FšlÕ\§ßúèVÚX ÉmÅ\LTe ÇÍOÒÔÇ´¿³iÉ“Äò7eµx´°J²37V&ñZ·”WÌmHP ÀpK‰ üØ}Ó4^Z!|ÝÔ•ü&lo›µó³NÃ^øMÙ”u'žzs´39.sExü9m!³ì|YH1¾"-n?2?ëâBJK¾ò•¯(j¼=Èá ÒÒ~†86ýøA¨1ËH Ö9˜nl`ó9œ‡vò)öS¶š»S$UÚ1tdJ¬J*ˆ(Ÿlxû®4bÐvÍyuˆU–ò³°ÚtÇç|t{bÔΣޱÙ§°†-ÆžjVSV˜ãè  d Òñåƒï“\€y'²uŒiá¿|J¯›òœ‹LB÷¬äTž e-BA (ZTCeTIÅvÄIró(]Lš ; Ÿ*™GÌç´¦§=.R÷éDÄ:T·^‘‰'Q¡ É­m„°…«ÄogzŒisÖu¨êÅð¢ˆÉñÛà2Ä 4í>®`ŒÚh`’¢:Óuʱ)R•¤5˜L DÑa¢¨+è"±£Çª¿ *GR÷/J‰9Δ¸9¹™fM¶²yžY­LæèMì;E¤[2ª;n#I,L·”!°Øe£Äò·™Ä’šÐ}‹¢ÆfwÉR¾SŠéÔ@Ž`€'€ŠÌT`iÁŸô‚†ûÝN¿;hšæÀ á@+„/¤#º»ø§ú§ÌÅoö×á;Ü8”ïv¦‰@4®bL 0ÝtðBæÐ 'þZ°È­¦ç4Ž$vŽ7›{eo{çÉBzÀØ,Å•åjåÔÊŽ2ìbΖ·V‚”BT’l^(óz ¼x§ Š®u{Þ=K¸"tñí&:¨|ÿË¿üË`=˜¥!\òXrxåìv¢ Épç§k€Bo4G³ÜgsÓù GÙŽÍqH)õÙÜÎ-Å^õÎg—³1¯ŽÌ‹®úOàÜ6: ‡¬9œ³Ã7 Á±É*¨|Ma²rs±KóS加o²á¹q½è5”¿z~¯*)YÉP¶2)=ùø©žJªê˜›†Äò6F¾Ã°ŽÐ:ešt\`·®Œ†\ý;Cê³ãª@ÌbÑü©;¦lóHŒeN¤×#t¿|\Á0AOðÜð1ˆ %ê!µY¯éƒ×6 gƒÚÐ6À óÔçVÇ3uÄ j29mÚ1Ò%s“ÕBKS™£çÜžc¬T¢Sîf¦q“¹$&öÊä*`ÈÍLfª+š)Þ2¤k,IÙ:™Þž>æS †ÞíÖC%·¤j‚åµbN†€eÚbmÉÎ6„EÜRnA·¬[ÜíÂXèɘE߸6vtñíQjE/~û:êi;²9ðÒ8ð?Uh!ñýª9ð9à4¿‹ì­~ùË_¶øYÏ8†yÙ/·nÙîµwø6Í‘ w 9¿ù%ìÎ=·ù©ÛÂ_á#¬±ië‘×§ÍZ¹Ì¸a²È­k;©|ø‹­Ì¾µ-ð5=&÷àC¶®ã赦÷Öë«z{±Á ·I¸±é5[ËþÞ#P¸Ð)/H¥N.j…}\öºqš]k)Èh;™ïëÍ¥½yýëŠERçh"ÀM1ãÏþ÷US<™qù¾GÓOcãƒäÈ(§è‡SÚzd6ÁL®qö¶¿öµ¯A9õê4Àƒl…&ý³Ê 6CÃzhVII0Ç!+Þq¹Æf">>JåÇsõ–Øk#ü'æHì-áa®Ñ¿tŒ#AÅz¸¯Õ¾+@‘¨W§yê&õVÌê Àí“lGQ×_ìc@? +™j  ÇvÌQÐiΩ2d^A*ßkB½:4SäUêH Õ8eE¦IŠ1›mNY´ V;ßОÚë[‚< Ë·m*sœCcÑX%™¦G÷gÒu¢JLtZ=ŒØNÃ4Rô/;`& »U%ÇT‹°þšFŠ ˜t“‘B[ð–ê¸À÷n¶¤­a½=¾ë‹â~ø‡˜Ë'–Aòj˜ŒÉÙ»Lú×E/;¾º bTA÷…bæÍOæ]ECx|€>Ÿßž†É¶–ªÏÖß¶‚ËßZMs”î4Ã)’9× Iž–|ðÁôêêñ{¿÷{í¤ÐÓ¨g ±?]Ä™ .þ=.îbx«®‡ÞTÐñÑÆá ¦Tj¤yà›KûÙBï•^S%R—ó™Ç ;¦9ðJ9Ð á+í¸§®6] mXàMßk^ضQjÿÏÿüOˆ¸kNמZoÀR$×,¸6ñûðå´> Ô¤Lª ŸFÑù×|/Ö¡øtÉ–Z’M_Znnù;-kŒåh.ÿõ_ÿ _ÃêC²UŒi8v$8ÆøÚ„ÝwH4îIG‚)’ÓXµpz{úÈ—ÉÑ ÿ(ü”fŒžÜ‹(€DTèñÕ1Ì¡æbA*Pû‘`Šq3… ^wE¾ß]̰/Õ%ìð–8U’uX§r»`ýˆó¨n>NØ•æHÒ"OÚ£ž+A^½Â‰ <4Œá®Ñ?¾=1á9÷èG2”—Î,ìh’J¤.°—Ï%¡VÛ¿×Àlå³:ÆÈ óÅe{@ÔZ68 ]§¿˜0Êõ"a>Z·€E¸GDŒBqÒ¾ -ÏD Ó¢*«õ/°RGKÔÑA6 7mâFÑHÝb’ ËJ¤0Ù8Í–¤¥aò)Í1Òäð¿ñÆò©‰ö%ö¶KLÖcÚ,ý? 6Ä nbvÙ7YTo±Ør±G©yõʦ»Í*–ZÁgìK ”·´5ˆÛù¼[Ç-j*†xKô[ï÷“g¦B¿ O¨š §Q(…¼Q!HÑÈ[7—êã  7²èóWŠ™ c§ÒïZ­]¬~ zI°È4å' GR½ÄÎ쑼±Þ0õ3^yÔ5l¼˜¦]4IL;Ú1‡çªø5‹äV?H—xÛ>㇙ý DnŒCÃ`×¥èš+QÏ2ìðžZUž›¼¥3\‰¢ŽìOé7UÒv¼mSŒ­O¿;ôÅþ¶>|GÉno}· ·€]Øë¤"¦-(ëãªN$E¶~HEÅ_(jB øzäºã+ÊŠG ~ãjUù\¬ –9 ‹(y>.^ªn+o [PèðºØÈ =¥.;W¥ÜÆ2:<Í3Àã¢6†‰ñ¸±e™ã[û=;›®:±»‰$ÖßÛF5Asàq ÂWÔY]ÕoqÀÙn(“3¡¿ßнÅ3U$gØ.¨>mi´W î3¬ÁjŸxwñ€’žfÔbo•š¨ì€•IZAnBûÉŸüI¥d­=®ÇÓS†›P,t+VÖR,Ç5u ÃÎZÐ^ ÒM/Ð(<ô®ø‡€¿o+\rdå(»ïnDGbô³3k°{Ü€Gß$ÖH§J4g‘„~‚3ØÎ8»æÄ^ÁLt*9 «5ÄLV¬a¼4Iz®_ëü½õQ8¦*Ì„}Vî¯'’Y†:w›a§>¥PB ^ ±|Ëíy™ÃFÐ!esôßþÈýÑѲ¼¥qñ°2fu3Rèdò´ÚøiW…ÉÂè£(‰†I(éº,$N—M Y›!ZyV:N'’¢uòc"5æì·‚4ÁdkÊ5ñâ°IØTlB6-›œMÑ&jÓõ[š­TÏÂaùÀdK ¹µ¬X\,1ËE'ßLbµY’,L–'‹Ô¢E8¼µØ-h¦W–QI,©SüâÑb-‰…{gNN>  1ô4a‘s^É6ì?n‰› 9ðê8зŒ¾º.ë ÈËOP)ãÀGbUعo­r‹ƒ¥µ°bn¹Å´º¥,‚l4Úµk[‘ë@’ØyÝÑo1Ê-p¹ºÀN¶=cÀÂÙN\kÜ Èxe†ÉËa(ƒ‡X$,«où³ÄÂÓökYA‚§ïw;Ÿâ°€¥G B¦$nc¶ÀYT[®€…Å8·)8H³æO½µm„º#$>K­É`ýÎÐÁë’°„T‰‹”MÞ‰2†¯e½ÊíÌ2ü*ò*€?ìÌêÃøã°\Èl®;És,îyoÙ§Çä°&P'Hc´£#ÆCà’t¬!PIKpŒm“P%Á_f `‹©-dô..yh册Óü§H€[­¢ xÅZR"ÍnP7Å ”a›|Ò¨R½¦Ì§GãEÅTO%Uu¬¹†hŽFišTš©±š¬ášŸ|0[0‹¦œëQBìÅdÚ¬U¥Ý •$*&’C„RIBµ¯‰(AUn\‰èoè ŠM,&GL0ôƬ®Âr´tCûЦâM*cº0iTä:`"’ĤtÅS™ Í´fr3ř袛úâ`24%šM&IS¥ Ó´¹³‡¢èõHNÒeb7½›äMõ&|Ó¾Éß`!°Ä{Âa™¸š{G>PSI£¢-Ccü"li³À=”Dn–ÔEÎÓ+‹µRh­Süâ1wkë‘M½Š;ŒªTh¼´ËèzRí·/—pÏ7«]ö×õ])û©ð#Æš²ÞZÈí¹BEVÊM<ͽNJÎcG•T¬²Z@ c›ç!“ábIK»«üÖ™×[½ÎŒÙ©û*ò*¿&GžØ:ÖGå}ä@ãd¹j_äÛx¸ÁCØ1y™Uy rjÒ•\i&³‰ u¨‹Ù Îcí¹j¯xÙ~\ÀwÐ…zS™°ÞØž×R'”øÙVüU:´—Ïס)€ûÜk¯ ltñ#··÷ˆ¢wdž›@M¶_ª^UfÏq‘†ÀìàBÇF÷N™+h|z+‰_*€ò)u(Tw°EèkŠ=Âc÷YœÖvJAÆ+X—¹l‰Y#gýOcã|“‹{}cG«gPÐÐŒq‘Ra8ƒJJÅ„Åý;]8ÁÆ»x‡¥Uyº%z¢¥l,l‰¦-uøWɤJ*—°)Îo¬Øm¸J ¥!ƒ«ŽBQ?d®Ú·9œ¨Œ¬²Ùd ¹EIA§”ÉoŸ)‰ŽÇÙÃoéu±‹|L’æp#nÇ|Mu÷Iˆ±¹3µb»±©ËŒ8^Ç뱦;ˆº&³O×Pþ§ Û@“-Ÿaí=Ò2úºXôh@KMnì„Ü8 -ƒi2ühò;™uC¹›Þø¶0â ¾¯›5t­«£õ®œõ½“Í$9w÷еœ¦V“$}žëõ¦zLüŒqžðœÒ 7ëFn‰‡®¤™ß&Q%£©šÃo‰› 9ðê8°µè¾ºVu…Ÿ–%‹“méãí Çæ[-ì˜Z_ikðâ‘à4& Àé5Ù§ô"³^ÚôuKÇÍŸKç ZËÌŽ"y®¤Øè`S†§®ptä’~Ëà"ÄSšŠÌ½5PÜeVü"à8PÙ9vè°Ž8©Hí 5YBQLè£,ÊZ¼ œEøÜ×én Y-p•Ío5×v–'v¥Ô«\ƒ«zÄf|Å_ì20‰¨ {6¥[è ×ÂÁ®FÔ׊péÈ”33Ë” ”øu"¸zŒÖMcQ™RE‚wHÍ®ŠºrÌ“ñÓ_.‘)~eðÁœ§÷úÙ¬ÙTŸŽù'Fþrcb3!±Hö4.j[Û Û¨ zmÊ·]b;€,ii ¹mýe—§ˆµÑQ½@ÛøTsKlE)à€µŸœ'¥ )]@£àrƇÌã¥qÌy  *®^Ðã~r³Õ¢è4™¤±%ÊŸ¾wU mVY‡LŒzÁxOþr†˜õæ\¥k’°v-›ô+þ{eã-J6O[ZzœÅÌΈ^«W§Âæb!n´*ßv»;ä¦XŸÑuó"–ܪ‚WXgsd1Y|>?Õ‡ÃýBÿ£Ž%!5 sÅvk 5¸âŽa2o@©¹3ÄRÂ8¨‹˜blлLq:}asN*k¼ãª1kí”Ûñ‘D}½ÿõ MÖ^û)Ìknþ<æyÃÖ½>íxæ3N›Ä)T§Ó9Íæ±=_#k¶MX;­vG6^5Z!|ÕÝ÷Ô•·Z[8±\°zË JÇ¢ŽW݇¾Ü]¸l&±Êrƒ½œº0vRY2iƒ¶B÷oL‘­onHNt¸mo§4á¼kËö6I4ÛÍ»×’›C&Ì*Æì6ÿäŽ>ÏJ¾/”IN>Ô*L†–Ž8Î_ù<€eí/ÈSBˆ\G¬ö~û†œí¡±îŒT·…ÂyÐ-ÐÉ}ËW+oéi.<´…Oœ>HQ¿SЇðÜmE)B–TJ/_ âZ:a'@ÙáEØ‘õ›¶xÄ”yȸªE±§ÑŽh§P,SØ”Ûô¨\<¯jLoRQ–@yêL(¢t3X¤ÖÙÅ`r$B^ñx$ɸ瘅Ÿ²DU ¨2ççIí§ôRt:¨¯r ƒlm’±‹ÒhžÑp EîëTÕÕÞIÅ"„«t0:0Å›P8v b†«•›Ö±m¦ €òpµ#€¸zŽagÁÑo#ð•í:@#%æ[ú!/GÂiÃÈžî™] ç:¹·:â×ý×µÈÇQ“?&'TŠ3¯3Šú‡êÚÇ ÛUØÜK–‡àwx~,71´Uv‘ŒEàéúp.øè7Ñ!|®l™Ô˜+÷ïI–‰žù” V>ë@îÓÞÔ£¸NØ11·P½ÖÙÖ[¾¦VOý®b¹äÞSü¯‰ó–8™{‰+&ßî]V†6ÚÑ´ÑÇÑŠ_¬ž\f69`¾Í–ŸQsÜ-Z”Ò¯š¯…ßZ$^K»žÍâ¨d“ÏÇ$‡¨È«€JS?˜e9¿¢™âE(îdëØÜ£•Cô.0ôœ2¼z䥿 ‹+ Á¾+š)ž_K&p€•j™Þž>ÒM{ÕáätÍ)Y"mSS­Ínå¶_» ¬We3X;îéŠ_U0¼ÞOÂÆËõNY§Žš´ ~R…8Ê2BÛW8ò L/ê?¾‚ÿH mácˆ8ãÈŠw“¡Ûei—1rk¤¥ÀëcVfàbGå·IW‰+~9ëkðŽ)ƒÞ1ù˜Ä[ aÅ2!Õ°'Bïp/ÉIöÒ²Ö%!Ö-†/Îi*CÉ‘Ööô#¤}}ƒ…ü󌲉4)Uâ}Òvb7–P t›cC¦€òCCÛÿý¿1{ÒèÓP5Æ4Bè·ÏdŒUÉl@à˜­ a‘6dT’™Žÿ$U™9…æÉÙL¡úÚÐp¦KBsaæ³J¼*F Ÿþ¨tW5 +ÐhMŒZ «ƒæJ…ª@ÎÂ%Ÿ©sÇÌé·4ȇÊcIÆä c²éÎæ‘áŸji䬯MtŽk©›QÈCF¢¿Ó€­xܼÁ–ÁXmĨIaË`´gaæALï¢MM4pÿ¨Ô%Ï8¯æ\ogÑ$!çfBHýfîëvNâdøï¯äÃ'Å‘„êuë6¢¯Ý~Dl1ýNkEb‰4ýÜý®·ß®¬"ÿ¿û»¿k¢«ÈuÀ`1ÝmêiÉ cbŒ"-†Ø:ÿzk3ÅÖ•ŒãÂESŸ¦0¬Ì]8P‘h¼W0M÷¯9ðJ9 @ÒNýÙR(è-±;ô¡IŒ9ûI¸A*Å'³™ .ɪøn&AF»SP,6›©rÔM£vèùÊÊnvèÑ0bHbÕéJBñ n&q®I)ú¤ÞLB“„¿½è*‰þ¢0³8X‚˜MŒ¯¦ÙGw DÝìGágþñŸ:¡æ'ú7þQ-H&sݘ ¢=‹ä½å6[¥ô›¨QnŒ<€»}t͵äT„ ¸QÉ” õ¹Ý§¤AÆö&’Ô+9°EÇÆm Ì+Ö$v6¯Šc65¨ëc؉ ˆ&§ ©+ S™FCIµ€zéÉŽb›S xmܱVM Ï£„’Oš€"اPE«@j¢J*–„ªªÂ kÂtWŠfjlÞj>&`Eü±1‹dˆ]˜æU© ^a,öo“Ãñ¯ê,]&¹îÓ‰ŽÖÝ:]×mœN#œÆZuÙ)Y"Zƒ‚«€úéõj‘ðö•Ò ´äl耆¡#Žî°10 O 4T XÃVn†°l8/&aSAnyÉ·F!¼ ›p°Bþ¦ +š)Þ´–]Õ›^]=š4¼IDAT0˜r;}L¿?TÄi>Ùx±è[F_l×tÅî9™™Óý¬ý÷ÔßøØ'ÅvˆCgQ[àªÍT<”²63§l&AÏOnQûI ÍaÇÛ¯›UÒµ‹Ïs[lcåbzKm·Ñ®V©ÐQÀëß,™K)”ao&±‡›è·IØqUþîh¹%ëkô7×9€þÜà uJЩ'Ææ/@Ü}l­bq£9a(åSú1òŒ9s‚ÕS•T#ˆ3T ‰xL˜0@ÏÄ¡ænçç“ÆõZ†^±Eè¾² é}ûèG4ŽÆCMŠS‡«í ž?[©4"þ] ?ON •ˆ4Im;V{Œ¡SUžÕ¨[EkÌAq U´ ¨†Ê¨’Š©žJVž*¥‡h²„#·±s&1Ã@lÌP’-ö*1flÇ|] #t‡N™5V8aÅê±®Wbàª-"!Ã1‡±nǬÞ>†Øc šªaPˆa¢J†ŒcøDù(¨aepMü¹zÔ54x á+šŠ7 6-˜*rˆJoòY“os[‰î#×á¨7zßtº¦¬·9`º>½¢©@,ð„yoT¶™-:•ÏmÀr†Ã–¶[Ê"°hJbÝo»ºÅ³ô8Ž*Ûc PHp|uŒ0ûÇ·Óx?8Ð áûÑÏÛŠÜ4°Ã8ÜíM”"~2s€ØôÐ7”,Ò xÊéå6ÿÔ"Í‹i3 ²”Míë/þâ/4f廵Y„³ð¥†8Ƴ™eì<7“ðÛaS7Äm&qÎ*:[nž\§âq×£âdÅ|¤µÝ™œ«Ì¡͹ Øu2j„éÚòíýˆ\æpuTT‰yŠÑIëøZCŒ$„E4,C–†ƒ×“­¯˜€™®*[~¡¼£1ŠAƒß¬“<’C©ãI4«7ÓIÔfxcÑTôŠàoÌJb™>¾ZÄd? $XÐÔ«\elTLšïZ!|ÿúô¹Z”‹Ë`¯fÓ â ·¿ÖÊ678f°SDh¸*ÖBg–öSå$ƒãûIò¥‡Üx,Ï `âæö0-Ë­-üýZÅåï¡}tßRWŠáM•Éá>?9û´S='s”ÂÒâÌÉ-=cbýH[¸%A6ÜLãæ˜ÄÀî ;eÀ©·P¼C ØâpÕnr;}š“É$¥“|ýVÿyßi#§DCÒÏ19Ì/ol…ì¹–%SÀ…® u`˜;@_gŸ˜›0Ãy¦Å @KDÆ·ÓM$x%h&3cWz ’F€,ž¨†²Š«›Ì+R¡ŠVÕPUk¨ÂûG˜”¢#R–¿…]˜†uá!fzŒ[#‚wÕ_ Ë­ \ š1Ãí„™WÄÅBƒB L6XÒ;†A$«}ÅÀ UOIvÜ, ÿ˜[M£<,Â&™œ8˜Dh‘ÄTW“Û‚lzßSè¿x49k¸‰zAS¯Lûؾé$R©,1ŠÈ•K¹XÈ$yhÝ´\’Lýh]g>¾Íºi™#×a@Ý€`M™·±ÿ•;ÄMÓx¥èKeL ý{ÅÈ4œ²€ÅðŠ¿Ç@â­ŽÑ;MnÝMÌš^>¼À ‹¨«e¸¦ ßõ¼e¸?9â|Ë&]Î â;pâvµ€›Û„ TúÛ%­J·©8›9â‰{Yn‰™M:à'WÑÄ3ç6‰[í¿ÓûµâµåÊA¾aÀ *n‹Ú¦øCÅJ¿¸Äa0]¹.ˆeÌ}•P¾“]›–"—ïSÀYH— ‡ð¬‹8}K©` R={ÿ°CJÈ¡•¦ié Nì¨0ÏF¢«X*“¢tÆŒ¡£”4¯\CÂ;Ñ݆25w› þèG qYˆ5Ä™+¢«ÜÞ2 ¶@6+v¹œ&õÔvÃ0c¸Wl\l&ü»TÆ=¹ebâSÇĤtÇ\YÉÂ6ÖÄ}KŽúh»A$¹:+¨Qãa,ž»Ýާã)ˆ¢2f«[•klŠäë¨\“‰‚p†VɇúÏå(òTS ±Q.: sÌöm‘½‰34s¢U†béÊ“òsÓ›>—ÇÒhn©³¦NÁ‹‡^êàuWÛetU,t®G°åª†JÄXα!ðÈU&’ËüÍäDCT@Í%×­ñŠ¿ª@Åc¸#‹*̉÷ÿð+þ4`Î$Ò|wºó3Ÿ~¥¢óh=Íöi*£ b)ý¶|ªdc ?&eƒ‚Ìg«b|{vÉÁsòÖqVw ÒŒ‘N2÷‘Û_]…S+J‘ÍÄÛ93™DƦ¹ÅÅȵ™r•Åñ‹_ä9¼Ù/t9]©ù®“!„î&·bš¸-Ëž ýÖAú^ÆúXŠAC ¦+=â]¯öfZºi„¾ç ~v¦j/£2Ñ”(5îí©ÝÎ6îs6;É“ž†•Ï"`Þ³‰¦&ÔìºåhAï•Ñg×É U%Õ^ç-~ÒM#ÝÉiÞc¸Û¼“ÓlL Ò5;MWÕß¶ A„u9t^*âd’áÆ‰uc<²¢œâI—‰ËAéÒ/EV¢7—ÆÚ¾Yö?¶ñfEtªæÀ·—­~{ùߥ¿dµf&búøpµùäÊt|´PAZ,EàÎGäŸXÉŽôbAZ ºy|{8ûf͆u¬”þª†_jÙŽtB›¸?‘Mñ4»¤ ¡ÙPéá†ÔR„T¬úðPNTÞÖruTÄ@š>o“ pTÃ? Ÿ®Dî$aÝ ÖgøeÚÚIÂ9Íꮆt¡8h-R±¹,‘À°[ºC‹Ô 8­’)­l‘C^AÞô°/_PlôZb †HêÒso)u¤‘µŠ!HrìŠh"%Ò(XÂuë®úû©¼í†Ûª^è ˜Þ¡0ÐñÆ” È:ŒÇ.—äªGåv $…í˜!åŠ Må=ÖõY“e,–L† cVÅલ"ZäYquÊiYÔžXÞF´ÂŠóÁ c-“óÔK½9UEÌ3¿÷{¿G]‰EBqèÙ%h/´Ðtbý՛䂪!§Û‰4œ«7‰©6:bª•4§ÚÒ¦oVYÓqªê™pÈ mÓa<‡÷üõö!®>44LJ„T³ò™¾TÞ™OÞ‰¤‘×h.¨\4\GЊé™öqŒŽe½b®¤D‘C»¹‡¦^]ôˆºé.üŠ¯È¦xS1™aãŽŠäææ_ò¬ãìæ$CkiÁ¯4¨ü5›¾Œ5ûE0‘Mñ&Ú ¶ÛG#Ãõ6£Uò©!ŸÍ£‘EUC­èêæ~ÓB6="°K¯˜ÀØxÝ¡gH÷™Üý=ÇϦù]Jsàs ÂwÎÒÎð³æ;WF+›àr[<…¸w?±[âXó 3P æÈÇ”!°)KéÕ‚jÓXáÏ[·üÖôÞ²Q°Q3o°àôoû6›y$5 W«¦ŠÁ‹Â”“+rŠÿ·û7ºº´tBÍ9.̽GÛÃ,~¼€¨…¼¿Žõ9¡R }ð òÖ?%#}ë™û[ì•c¥ã[aý Â[z܇ªMoùSØ-McÇÜ:Üè¯H˜’š§¥Â1Ô œÆE»ƒÑI Ö•1“ptNʆSj£v'¬×` ÛüÀS²ÂX¦¤|Ò}Ê ÃÇÀìä•vÙq ïM”y¤Hs¥#êLȱšlÓyçjH4@K3”­’ É1M*r¢>Âbpo_-?­Ïf$ŒNBlà«0ËžÚFw2ÿ8øGtKLÀ_µ%É‘L Ç+©Xu\Oz*xò§1Âëe”­o÷QEðêXU’Ìåµ&›\¬ÁvÇÌ6¥:&`›GË<øX8 ºL¸èŽkR xŽQ '‚é1 Eú>8™¡‡Ð| w-0ø4\¬l5ï ZÑŽ <àd—l&J¦²â ˆ™ÜD© *ÎÓ„)H¬ÂŒ“vœD°•2*xÔxÀÑ6¿¢ÓwœÖ±ÇºèêM”,TŒí$Í+蟟µÐ×¢=ÚIÉ I¥zl’$–¦!†³"å'*´G{.Ęüç'CXÜ97Ãè*,ëm™¤’í£iJÚˆo²’VçR¾ç{¾§´2@“RMBæ~QJ_; ¹Hº„›oVôÿý?Â@ ¹ÉWU>u~´nEÅÊJˆ–HuÖË|}ÁM•¯ª2aå3›Qo¨wÑÓÛ) ìuÄ©òÌ'¼=꟫a,À2dÖ0eÚEœ€>Ù¬"‰aõå\€RG’O„ªâê”ÃúQõ˜éŒ2½ŒodLw˜j¨ˆôÀ¤¥$0уòŠVŠAG½$íaø:ÿ·ZG´dN!1è”E0œž¬Ó™=Ý%£,?£Ìo ŒTA¾â$ÄÁ6Î-=UzòNmß9.§Çš m6vÚ²©æHPdcÀÀgTÔ_v|ì@IU CFÒ¦&Ð舱";½ºz$†ƒQ`çËß+²)ÞN«½3MYÓ««GËÂL°•¸ãíâpµ-2ÃP•gÇ7Þ´Bø~ôã³·"«Åœ²Ã XÄæ¢ë»Ú¡GÃpÇ6øèfdn%yhÅ&k6+³Y=Ë6ŽKV¾üå/o&Î9ƒP²Ø£Ž © Pr[„u—‡ÀºŒÐ@{°$íüOl;cÂxUü[Âîc>ÕÐLô£v*jv”:F€Éw´€,Ê@ª¡î7–!:pF}Ô¢üÈ ¾Ñ•€;ÍÍ9Ÿ±½Jgˆƒi¢ÝÉFÁA34Xðœþõ@*·2HÎM˜®‚†yŠ„(ë+_ù Pè0€{Z“|t¢´Ù°Ty«Zav…½[ò9,Â^‡KÝGOÑâ†R,mð«˜=Û4'»Ñ@˜c<¬<ìþhxîP¡ˆ±à¯[v°GbaLfÄ¡1Ê$´ÊÅs°Té~„ü/ÿò/#ú”b© ´N¬šSÎ>޼%KôÓ‚M3˜{€H Í †3ô&)ª/æ(Å+vKg#]Òã1[Ó£‘†Ó磽`©Ý4,š)%§gªÅSyeÑ?1Ù¦FtHóÕf»&±§¹Ñuu" R:Qç9ºJö>º?²Y¥OLc£8•U1ž°9‡ŒØ.!GSdé?zÿl¸j?kgÈô‹N?m…œnLB Õ•§4ÇHxZ%™ m5ߞƘl~éD&œSšcdö[²Û;*­™'n;Í¿â×b­’f0ûVkʼÍùùìïÐ7Msàõr Â×Ûw]óoq>"aeÈ”üÖ‹‹ ',¢ÜŸ`Ü ’“h«µ“žEâäõY;ƒµ è%w*–<¬‹N,<´:Âj`+ˆKí[#™"™w\ îŸUÿqÐ/­›ªð‰w×¹»ë('¶×´ß|CͰ‡­8·ŽâhTTÇS²Þ:G@0à(Z+VT ŒGñÑvnëð™pÁpGmŠÑÆR‡þÑ@rì?¥c*аÀð‚“ó˜ˆuà5ÕŽ š†Ø ÏÙ?Lˆ&·ùHš$(¬ià¼=Ú†ÚKa“³¡üéZöÔŽ¤2ôdJ)yЮûKµÐ@ m\1øKkP£ç¬i§gÃø@‰å—(OΤÏLýÈ’l&51¡O–Ÿ‰f|”9 âL¤Ì©R§•¡þqBŽW!eÉH‰I‰ŒBEê€þ(þêyÖÅ?j¤©³x¿#Òâd)œï¤_è†ÝUþ>’ó©.0gÚx¢ÍÆ´¨™ÄÉbÚ¥<ØúáW/‰¦’jèšý£Ú× ú-ý«†˜:£ñy(œO¦øoû#©ÃjL0ÄXÀ(0¶r<Ò¯ÄA|í€8?¹PÌ¢ªå/þpzÄ:OåÞi ¶+òàŽŸ|-}'9$ü¼ŽÅÝ&±eðÕ¯~•ÉŽáî–8ÃYM ·ÕrÜ1¹Ê${ îà±õpE3Åv•åÕ@˜Þ^=Z†¬à–מW”c¼Ý+¸ÍMGñ§a3pÌ™F·A}JÓ‘Í÷‡æñþ5Þس7,-“;m±y Xø¡´úÐÄ/TÚO‚’;–ŠVû©x[©˜H™ÙOz*ÀÝO"ÿØR ÂTYãÙ@¢Yí$±¦æCjÀ÷}h`hm¼¡çÛTŒH:zK[€TJ«‚öÁ…A®c*` F„íyñ†#oúÀb x4ùgF¯ž -UA«ÁM}Gy€ºˆ1CÓøõsÑÍLt0»*¬mL$ež"G½4²­µjn„RA×½™{/“„q¼¾¢×é¶íÕ³òçHE¼¢OÝš¶r<Š\$‘¡l«Å)TÑWITXµCO–4çŠ2ñ’‰+I°KX‡Øˆ™XŠ±Ø‹ÉXá#4×:E×è ݤ³t™ŽSOXÕ~CÀ@0LÂDîmj(ÃS« Õ)+¥ÈÿQV˜:L ¦µ2¥˜XL/‹N¯.6Ye³ [¿Ä&¦ïL•kÊzkú5 «ž ¹"#1myhA±q#•ee‘óôJþúT/X¦W‹ÇøHãÍñ•…XõŒÇã««#+ò¦C¯hÆxpBFèÙáæÀûʶïý{8£w7aì´'ÇÝ6¶6ŒYYí ¢~.Œo¯Â슰 Ø]Ñã­vüИïö¯è´”ÀÑŽµð˜çi ÌʈçèlzJ0FòÚ‚w—œ=‹þ9¾½ g3ÛÁmWdc¼]s·²ÏLnÚˆvß]|Êb4³¥€ˆ„×u%C¢¿cÑ›aV¶æ”(Ÿ4ØÝ^5ë\r`ø"c~h|±Äg‰å•çž—”".ËJl¡pŠ‘.di٬ƂLý€3«~ú-ˆ?ƒW,0à5ÎÇ–’¿õ¨tkÎ"¦Î ›ûQØgŽôÄ€w+9TmÞÈ|Ànd»¦КŽÍáJÍvAýÃíßþíßFà›ò¼‹" ò|ÛzJEcdø2ØÒã;ǯ Õ‹#¥!6™¬=²Qó‘&ibæ>¶5šI™²+ä@* öTôgüøiˆ ¥”n@I³Õ‚!ì?Zšv‰áÅ`€³Zógf¹b‡aŠÔû̪~ú%”’3ü"¦/™|X8™ð,Šë©¾0"TéÑOVÉ0K‘±ïJOÝÊÓ~œiã^kª7À±t§†¦/“¶•å!· †tgAÉ!á/6a‡!™Íö;µBƒÃo°šä¢TÞÝ<6 B†¦zÖû¨ ; 9JØ Ñ•L¸ûީа¥² Ðüãb‚MsâNµ›¦9ð’9Ð áKî®Û°›yCœ÷Ζ0àÈ­eÓ[²êÁGÅÊmÙs¬¢"×ȃ? ÌgÍs?䚸޲ ¨ðAíÙYò“ÐjgÍ£ª/ ©œ§¼ ßÀ%Bjáôöø˜3`4d6ÚgŽ”c Ôî: æƒ>ãaì²ØëMú0àº\$h€>Ò¿Ãöex”AŸ•9m þ 6¥Z8nGä…EÅ…bá¹¢”ÜOµ³ß_ño Z@ÃÜÏà §Ïä—0WdPO}¨Š ©”’À1üÜŽ…úÒ†=õ¿ú«¿Ê+§p9|©¶ÎéýÂ/ü”<ê94(‚ÁK—„@âd,BgãâE>•î—„$Jg…þXÏ&ô‚ŠE%&Tªˆ)"J Õ3j9b]ÍY¼Èo¯þ¬†êI¿-…Y@Ÿ s`ÓõŸûÜç0œ ÇZôü‡á~S[û @ÌvÊìAÐÍL Ètœmö †cð¾¶³†'rcŠ4?˜ÊŒ]ïo~ÂäßlÀ­N’cnÆè»X™tYz-9Ú5-…»±©>ÔxÊ¡Ã`ô"l©‚ŒM¦a"¹“[½ú4„JOéJ”ÊÛh °önÝ ÜÅj®vËQövª‡áV:só ¼ù`Ør²”¨çNhrù­L{¦›Iȧ¥„@ÒðsLw'¡»j©Ð4mSÙ-»*Cæm•²ðóÿ¬ÈÛ@üf7? AxH‘)‚lozÿÞV  š/šfðþ5ÞÀëÈ»Ó ÆB09}·CšÜüikv? J8[Åîz(•óRAöû©âkjYe-ÙOåÙ­ßL':WklÒ#ËN6nC´T*õØ ÀTÊÊËê5y‡?žúŒAºÀ%uˆ†ïÜ]¹ù½Ã¢e¥# R³¥vÚ¢Öj˜Fó)ð€îfrU” ˆc®ŸfFèä¶ýni‘`(|Ï¢ÄäŒ «[%¡ñ˪fÜN¨:6ú^,·nX©‚ÀG44TÉ ®®* =o¹)z¦³ëV™p`«#@2ôí1îbQ%jgò¡O2“²éÍp°&Ó[òJ§ÀМåIzçÂR>í¿ª vk‚†hŽF¥DÍÔXMÖpͧqaE^a_ŒÂ.LK_x…™b0{1Yž"±ó«›tŠ®I>:K—y[ݤC½’¡L©ÓÑ€$1îñÈ[‰Mf½FœÑ"`¡'rø½€ÊQĕЎèY“ 6ñ&äDÀ{ÂoT3“³ab°ì«Iµù—äæ»!oàSÞ¦„X‡'iøôêmuºi “MqZ­ù&=SŸ Ð4¨[M‰ 5=Žü\‡M¼ªd^“ÕÛ|Ä$_1·ˇŽÐéy~ZªTŒ„Üæ?X¥²DŽ‘·áL•–ã[Ê"°Ðã¶áié¯ÈEP1 bAÓ¯šïÚBhÈ÷ï=á€5g—ûÖ]F²Ó$hÉ•âp˜mÚz4Vqž*–sŸ pæa3;’ âáß²¿GË‡Š«!w`n³ d®{aÂr ൙ʖ3 Ç&Æ(gõ6•¶.ãÔ{JÏLdÇW{ýµg ¤Ú×f±yç¶yª <Õ™.êÒ‹ÓºíG‚Dz |#ì3¾á՘܎884@·à,J+¸½Á w=Ì«O* !C —3ñªª³8²Â…-@?G— Fëfu!fØènÎuêÃÂøÍL ÄËÁ‰2Ä\›èà;hîÇÒPg÷p2ér{ö¶ºÑS´äì–]بèˆ4…ëŠF±uô~‹½ªç­¦áCâ?ã¿Ú®s­!Xš¿`êÄv 6øA[I0)ŒB&ó7¹ùK½,cj#Ïl/«›Ø ´?GFå3‘‰OnÉJø3þ‘+ò‰†@œS gë6èXÒ¼eŸ'#£ˆe¾}RRÁ$¡ô R7£p€ÄÕ0–xpÅ'¨¹h”¶À8ß–~¥í4|·ép§÷i“¢3E ¦LÆx®zŒT•MÏQI.ßÔ`ÝDSÂR€‰\é5l×Y4LM3@Œ8Yùøc“d™lÄN|Pg”äó]õ…‰šŒÕ#T~ËcéoÎdˆoö :æw‚êÑß«e‚A:Wd—w'¡íg:$ÔE—jw’ ù­ßú-·®í+Ÿ°‚-ª Cu—þ`mVhà5dööàiDí›­¸"ƒ_,xHž@|4ÆŽ`ôì@ Œ p»ÕÅâ½W£à‘p?0Ø®íú—3d˜¯±Šæ¦HTx>ËÐçØÏÝÑÀ7yZÌQÅI™ã¤1®’æ}aàg‡å-ù&g9ÔÜòƹa²êYËèr6)‰9Êü`WˆîÍÂlžÙÉ4¯Ø ½¥AÔN@ÖP7SÁ—öÔݹï³›IAKv(=)a³ŸÃ:$ùfY+…„FÇa :[é!´;Ç'0*…Ƚ 9áÀº™ó»"ƒ´hØî'¬*iÁ5O@fâ E ì«0õ›þÆdDçgaµ)îmºƒ¥ÒJ†¬<ô€9ñZcHꯣaàR 6Á÷ÐpôºwÕØý| l VÃý€Â1Gß©[òQs~†t•‘¬è“ŠÞEc¡£& xͽÍöJ‘%@'©­Ö$ú-;vu=ÕkQy8Aކ"sBÉÉ•1Ç¿‘>\y‚Э6VÌ0œ)itñúEÝ4œ&¥}"®Gʽ’D<µSAD®h¦1PúW<ˆüPþ+f ;êÏsR<‘sØŒŠÿÙøˆ  êÆöbP³ …†Ô¹Ñuz ¬`84´/#zÔT'z 5ÜÿuhÖ£‡èqm¬lø6ÊyŠV7“v¤1º(®ªyi†Z€Ìg¦u“1ŽÞ¸¦ãÓZ)‡2aá§-°L2Ô›7(`æ nœ®¢Íjxõˆ. ¤©C24ÄüˆÜÛk€UÄN€ðÛ ÆŽú¨0;-ÎnZÓwƯJÑ6ž¼5‡¯w7ÆB jVbÚµ Ž1~Îéz:’¯)Ç·Y¯ºPGGÛ|Áö‡T5†eÌ1œMÚ;«¶m>{¸®×cÓ:Üx}0‘õ¯9ðÞp *¹Ù"¾v- »I²`>ׇî§­,cÊ=÷SQÃ,üP(³ŸÊƹ‚@¢ •JÒ‘X¥8ÈÙa¥1Ž6›íÄŸž«ùô¦3-ŠvçïÜ{ ÓÐF€xʰ> ×rü} À ÛêÇ ?½†L9Û×¼Ôþ†?4&f“žéÕ#7Òƒ­ð½/ƒ1LÁýÔûåÌÎ_úÒ—lcóVâ³Tп¢iìâF@mu®›’ 3sº½ââ……9‹CJ¼[AŸä½ ¤¤Gv¨kÌ¿Âð7](vå5H•J È¦£PŽD"Ëù1uuâ—‹ •,yJ›Òµôu*f\L™ç‘ägg^w*Ži]¿˜hÇT"½Bœ%‘Pr²’á1‰HΪ¤bª—¦©°j«üi*Õäj¾€TØrJ,3ÑÈS“±Ú#Ã|]pšJ—¥b:QWÖxÑÅ:ú4‰HâAHdN`JJ q"Tc*"Gðˆ!$Š’ø¥›ˆkí01&Ìhv¬¯DÀû}óâ Ä01X †A¤¥é—‘þ3 ›FtVM,&™ˆßXìUO1vIøJ°K›²hÎ ¦³FâdH…“íñíHùnÃæXÓ»IþóŸÿ¼ ß´oò·X,…±-iÑBZFú„-OZ„?Wó˜DŒ¥PKãqJp™ýKðéÛ«HK¼²,%WS<ÞÌ0Å÷csà=æÀ‡¾ ýk¼7°Ïm‡9Xve]DoŸr‡¸h©˜ž*f'e•“êqÑä&nÆ„Š°Š3޹@Ï.8E‡" R®qCò‡±¹ ¨!·(«;È‹hÿn €ß·ºÝ67ì j< ÀeǵŒÙ„™ô62G[dh»Ýg'ÄstÊEv8Ìw”z`€Ã€U…&­SO€òØG¬4 uzèÁtG³({Q9²¦$·Íinr…>c##­ÛÏe"áQKa¬ƒ¶Ù RŠÊ#†ÚmCäZå²uLeqºãå•$ð®xñ¡á©(ñ9å5¦¢o3ÄÞj¸z2˜‡€{^Œ‡T—Iõbâs™J 2Ö˜¯¹çe/·ñy,BÏ£6`û#XxÛò“æL+ôÆ·ô›X)À“¾gÈŒeyLïL£ä2Ie;)EŠVoUF•’¡JªjzMå5a,HX3#Z®ù˜€á æ`ÑH¯ÚØè-–bl^a5†§º@GL»×eRé>(•Õ­é/ñº[§ Œ4‡¨™ž€$„JlJE#È '%¨èý0A=‰ñ´a!‡Xn%4 „”›T:Â`1d¦‚ «ú¡B“†‡Ø4 FÕ00 OƒÏ X|NåË”´†¹ÁnÈø†¿I@‰&Ó‚É!Šz2i ç§=ÁZ ,‹œ20aZD,%bfqÁa 寱ôLj¤å Ó,USß­-…R=ªté8©,Áȩ̈·k–û)þôxÈüNœtdsà½ä@+„ïe·>u£²UÏKp‡ P6Dm—îЇ†“’Ýk…#Ê~*°Éº-vÇ^g‰uPoO»éD]Óg{Ò~3àâΆy”ekY¡PÎŒ^vÙ¯†@Öi •6e÷VƒÏ¾ó;¿³lÉM{Ç€Zx×áW³øˆ….v¯ësÜW1á9Æ <ÄR¹Éd4[±Þ8éç.ÇŒê¥;HÉ I7Å~¥;ªsÝÀ!“2¾É œd+œ’†Üç¥EQ¼Ò"‰ìH¥)Aùâ%¬x»õN=Ez½"*€`T™á _ðŠ$pfKYœ<åœ xÅàˆÑ˜De4Í+(9꘎‡Qxij0LF-[þa²`tùpõd†•ÄÏ);<)þ$oÅ$EµˆzÌs•Ï^!ñÇëkF˜}!ñ”“2Wrá;^rhìD·$?q=U´óK&ø Oƒzª&G]wuMÞ þzU½P e"«0VæŠÈ+…FÓP äJ" ÂªiH”pMÓ@‘+Ï¢!1ÍaFa—·X53Û1?EèõÑ5Ré¦ô©Ž› t®.NA"#ÑÖˆDÔ'B’|R=u#HÄ)á1#lyKü¡W21þJB\ mºÛ[ÂL¤ vhj¤”Š+‰A!çò†Œ£kª2ù®€ Hn¸tž©˜!i`Ž¢»%Ùp6¨5­F¢T¾áoe^&^aIÃÔ1Žtñ¦“Œ©Æ„cÚ1ù˜‚ðUÓ[S“<#ÉóÓ"¹™Ð”®E¦8鎰E~>&|­ÿµWC,I$!m7L,XÀâ…‡2Ë™îÀR‚a™³Ø†(“ÆŽN!*º©Dè6`ÁU–B-Á·ÄE`qÇe ýqf(š1 ‹Ñ“Þ1²ÃÍ÷ž­¾÷]üt ´l›ÍG¤²f†ÞêÅ"X±,Q+œá b;ß’æ @`oÛ>·•^ªøà¾eñ³9êþÏà ÷'§‰\ñù8þ0p˜%¶³/>9¼´_jhí´ºk‚E‘XÁµ±¶Ör;ëA̶ð}Až?”|ôrD 1Œpƒ¥€æèo8iSß&4f&ŸÊŸ]ê…bq>k?óHÈ,àxWÒhò•„§£ŠÓb£ÛŸýuñ\á•Âü›œ|ks·žâ”5]£‚k&£Ä¸Gžƒd¶ÕÑ»m(j†Ši¸óœŠžò@Ê–r®ãlVP+ w¬ë(·¶®Õ‘ “ù}•î¡ 6²zL"†`Ǧ7±.›å2b™wÜqšÊå1„fZG*©àÚÅn‹:DñàÓÅM+¬Àsv6†…cA| ãzJ}baKXL=GûR’Ó"ê+ Ä@αN«§c¢Ç"C_ržJýý¸5ú%,òh•ªLd([”ŠPâ’JT£ÈÆ€j«|˜ 9WXcGÊ„±EÎKì´x¤ËáªC%Ô:"•Ñ5:(õÔe:îXŠ­»k„"!â1YA+u Z,e9‚gà{$ЧŽD—GÏDF°‰7!›ØWæ0@ ƒ¥  Cɀгƒ!VĆžhÖö„R 9£7l=ÂE/ ÃÜ`¯Z¡Ás‚iAÑÊÕÅ¥öKboJ‘3âú™vˆÖ)Ël©¸ÉMS&+ 7q¥¥•Lq&:ÓIO¼ Ð4h24%šõE5¼Rã¦SÍdJu«xùˆÊIEND®B`‚doc/example-scripts/tk-geo2.png000066400000000000000000012215771242365656200167530ustar00rootroot00000000000000‰PNG  IHDRª²¿pŸ iCCPICC ProfileH ­WgXSÉžS’@HhP¤„Þ‘^¥÷¢ l„$$¡„"bwQÁµ‹VtUDŵ²¨ˆ]Y{ßXPPÖÅ‚ ”;‡¢îÝ»ÿîyž™óžwÞù¾o¾™3Ï Šl±8 U [”/‰ ñg%%§°(€P6lNžØ/::üëóþ&@ˆÆk6„­•ýï./ ›Ó¸yœlˆ€5rÄ’|H„=ã™ùb¯‚XM„x'ù#¸‘Ài#¸mX52ähl¶„½ò¬ÚQ¤Al'â EO€Ø›#`s!.†Ø:;;‡À{ 6OûÁÿÌf§}³Éfó¿á‘±ÀžÐq 0OœÅž5üñÿ¬²³¤0_ìiy™±ðÍ„y+ä°ƒb!Ö„x…€9ÊïçûÇŒòÍÂü°8ˆÕ æº@?Š»¥™ñ~ë@~03'‚ÐÃ<¡š¢´IQ«BlÌÉ €¹'|¡.E‚¸ÄQM$—1\Eh’$'fL/È+ˆ㋊“Æôìpb¾¡¾”-h8´‚—Bø5„ü>q~4'á«]”5it,èÓtI0¡!øÏ¼¼áñ± òq¡‡1cÊù’8BÇˆé¤ ƒÃ †±avIèï+Î^Ó°/'‘Æy0†8'Š'rHð¥\v ‘[˜ll <D °@$£5 ò"Èq@È‚EÂRk!=#u’“nd¤;cì9ªBÀ…xÄÖý! ŠÀŸÐ*äyõqoÜ„µ/,¸î>ÖÖÞÛÐ;†Gcåþ6£¶ýG£/€¿ŒéfJÆðhŸ´o=þS0x 3ÀSØÕÚõØ Žõÿ>br9J&[`K±ÃØyìvkÆ ;‰5bmØqÆ5æ… "+D†ó@Ì"H‡¿Dcþþ–%é7ŨEKEg{‰@&l~ó0µðV¤P‘=f@mÄ·ù 7…ÙuÆýq/˜g˜cœ‰kÜ fÜ÷sà Ùï³ø÷ÑØ€ôál %<ƒãÈÎçæÃµrij$B¾ ŸåwKž5+Lıµf9ØÙ;bï%4¼eï©óÒw.·÷RøÛ‹PÀ6àØ3ï¿sFoào÷Ê㩤`D‡/ %øWh=`ÌaF€ ð¾ „ƒ(’Át¸† F<ƒ ”U`=¨[Á°ì‡@h§À9pt€à.ðô÷`A BGˆ¢˜ Vˆâ†x#AH$ƒ$#©!R¤Y„”!kJd;RƒüŠCN!‘NäòéAÞ ŸQ ¥¡j¨.jŠŽGÝP?4C§¡|4-B£+Ð ´֣݇§ÐËè T†¾Dû1€)`L̳Áܰ, KÁÒ1 6+ÅʱjìÖ×â5L†õbŸp2ÎÀY¸ œÉP<çà¹ø\|9^‰ïÁëñ3ø5üÞ‡%ÑI:$+’)Œ”Dâ“f’JHå¤]¤£¤³ðî"½'“ÉL²Ù®ödry6y9y3¹ŽÜBî$?!÷S(-ŠÅ‹EaSò)%””}”“”«”.ÊG99}9¹`¹9‘ÜB¹r¹½r'ä®Ê=—W–7‘÷’çÊÏ’_)¿S¾IþŠ|—üU…jFõ¢ÆQ3¨ ¨ÔÔ³ÔûÔ· † î “„ ó**\Px¤ð‰¦J³¤ЦҤ´´Ý´ÚÚ[:nJ÷¥§Ðóé+è5ôÓô‡ôŠ E[Å0E®â<Å*ÅzūН”ä•L”ü”¦+)•+Vº¢Ô«,¯lª ÌVž«\¥|Lù–r¿ CÅ^%J%[e¹Ê^•‹*ݪUSÕ U®êbÕª§UŸ00†#€Áa,bìdœet©‘ÕÌÔÂÔ2ÔÊÔö«µ«õ©«ª;©'¨ªW©W—11¦)3Œ™Å\É<ļÉü¬¡«á§ÁÓX¦q@ãªÆÍqš¾š<ÍRÍ:ÍšŸµXZAZ™Z«µ´hãÚ–Ú“µgjoÑ>«Ý;Nmœç8θÒq‡ÆÝÕAu,ubtfëìÐiÓé×ÕÓ ÑënÔ=­Û«ÇÔóÕËÐ[§wB¯GŸ¡ï­/Ô_§RÿKåÇÊbU°Î°ú t B ¤Û Ú  Í ã Ö>0¢¹¥­3j5ê3Ö7žh\l\k|×DÞÄÍD`²Áä¼ÉS3ÓDÓ%¦ ¦ÝfšfafEfµf÷Íéæ>æ¹æÕæ×-Èn™›-:,QKgKe•å+ÔÊÅJhµÙªÓšdín-²®¶¾eC³ñ³)°©µydË´´]hÛ`ûj¼ñø”ñ«ÇŸÿÕÎÙ.Ën§Ý={Uûpû…öMöo,8U×éŽÁŽó_;Y9ñœ¶8Ývf8Ot^âÜêüÅÅÕEârÀ¥ÇÕØ5Õu“ë-75·h·ånÜIîþîóÜ›Ý?y¸xä{òøËÓÆ3Ós¯g÷³ ¼ ;'<ñ2ôb{m÷’y³¼S½·yË| |Ø>Õ>}|¹¾»|ŸûYøeøíó{åoç/ñ?êÿ!À#`N@K XؤTô0Ø0˜\Üâ2;¤%”º:ôV˜n'¬&¬/Ü5|Nø™ZDlDeÄãHËHIdÓDtbøÄµïO2™$šÔ¢Â¢ÖF=ˆ6‹Îþm2yrôäªÉÏbìcŠcÎÇ2bgÄî}ç·2î^¼y¼4¾5A)ajBM‡ÄÀÄ5‰²¤ñIs’.'k' “S() )»Rú§MY?¥kªóÔ’©7§™M+œvqºöô¬éÇg(Í`Ï8œJJMLÝ›:ÈŽbW³ûÓÂÒ6¥õq88/¹¾ÜuÜžo ïyºWúšôn¾-¿Gà#(ô „•Âס[3>dFeîÎÊJ̪˖ËNÍ>&ReŠÎäèåætŠ­Ä%bY®GîúÜ>I„dW’7-¯1_ rÛ¤æÒŸ¤ ¼ ª >ÎL˜y¸P¥PTØ6ËrÖ²YÏ‹‚‹~™ÏæÌn-6(^PühŽßœís‘¹is[çÍ[<¯k~Èü= ¨ 2ü¾Ðnáš…ï%.jZ¬»xþâ'?…üT[¢X")¹µÄsÉÖ¥øRáÒöeŽË6.ûZÊ-½TfWV^6¸œ³üÒÏö?Wü<´"}EûJ—•[V‘W‰VÝ\í³zÏ•5Ekž¬¸¶~k]éºwëg¬¿XîT¾uuƒtƒ¬"²¢q£ñÆU+•7ªü«ê6élZ¶éÃfîæ«[|·ت»µlëçmÂm··‡l¯¯6­.ßAÞQ°ãÙ΄çqû¥f—ö®²]_v‹vËöÄì9SãZS³WgïÊZ´VZÛ³o꾎ýûØØ^Ǭ+;J¾ø5õ×›‡"µv;|àˆÉ‘MGGKë‘úYõ} ‚Ycrcç±ðc­MžMG³ýmw³AsÕqõã+OPO,>1t²èd‹¸¥÷ÿÔ“Ö­÷N'¾~fò™ö³g/œ >wú¼ßù“¼.4_ô¸xì’Û¥†Ë.—ëۜێþîüûÑv—öú+®W;Ü;š:'tž¸êsõÔµÀk箇]¿|cÒΛñ7oßšzKv›{»ûNÖ×w îÜ›Ÿt¿ôòƒò‡:«ÿ°ø£Næ";þ(ðQÛãØÇ÷žpž¼|š÷t°kñ3ú³òçúÏkºº›{‚{:^LyÑõRür ·äO•?7½2uä/ß¿Úú’úº^K^½YþVëíîwNïZû£û¾Ï~?ð¡ô£ÖÇ=ŸÜ>ÿœøùùÀÌAÊ`Å‹/M_#¾Þʳ%ìá³k4=€7»á½(ž: *ŽÜ†ÈÈ}bd´ôá‘ûÑÏ`·/ñóˆl` ,&Óà›8æÇùÔÑñ[ ñä¥;: „&G“CCou 4ðE244°yhèËNxî¾@KîÈŒP“á9~›.¶/ŸO¼|þ#å`l…øº+ pHYs%%IR$ðŸiTXtXML:com.adobe.xmp 1194 1458 _•»@IDATxìwàŵÇ튈 *6Ä. 6bÅö,Ø[,±4v1°&b‰ŠÆ[¢5˜D½cÁ¢ˆ{oOEQÄnÞGÎËd³wïÞÙ6;»÷ì°wwæ”ï9óýÍìÎÎÌø¯ýk=E@PE@PE@PêŽÀLuwPýSE@PE@PE@ø þi(Š€" (Š€" (Š€"Ðèð¯-¬N*Š€" (Š€" (Š€" Ã?ÍE@PE@PE@PÚþµE˜ÕIE@PE@PE@Ptø§9 (Š€" (Š€" (Š@[  Ã¿¶³:©(Š€" (Š€" (Š€ÿ4E@PE@PE@h tø×aV'E@PE@PE@ÐáŸæ€" (Š€" (Š€" (m€ÿÚ"Ìê¤" (Š€" (Š€" (:üÓPE@PE@PE -Ðá_[„YTE@PE@PE@‡šŠ€" (Š€" (Š€" ´:ük‹0«“Š€" (Š€" (Š€" èðOs@PE@PE@P¶@@‡mfuRPE@PE@Pþi(Š€" (Š€" (Š€"Ðèð¯-¬N*Š€" (Š€" (Š€" Ã?ÍE@PE@PE@PÚþµE˜ÕIE@PE@PE@Ptø§9 (Š€" (Š€" (Š@[  Ã¿¶³:©(Š€" (Š€" (Š€ÿ4E@PE@PE@h tø×aV'E@PE@PE@ÐáŸæ€" (Š€" (Š€" (m€ÿÚ"Ìê¤" (Š€" (Š€" (:üÓPE@PE@PE -Ðá_[„YTE@PE@PE@‡šŠ€" (Š€" (Š€" ´:ük‹0«“Š€" (Š€" (Š€" èðOs@PE@PE@P¶@@‡mfuRPE@PE@Pþi(Š€" (Š€" (Š€"Ðèð¯-¬N*Š€" (Š€" (Š€"0‹B „˜:uê{ï½7éßÇÿþïÿNž<ù³_~ùå´ß}÷Ý?üðýô!³L?fžyæYguÎ;vœ÷ßG—.]ºvíºà¿Å[l®¹æ i×ŸŠ€" Ø# |e•–TrP¾*Õn˜ñ_ÿú—ù¡'m…À?þøæ›o¾öÚk¯O?Þxãwß}—=g800dؽ{÷¥—^z™éDzË.»ÔRKÍ4“¾—vU¤Tå« IMTé(_i"xŽ€ÿ<Pžæ}òÉ'Ï=÷ÜóÏ?Ï¿/¼ð¯ñÌ>ûìŒÇYd󦎷vÿ~7/ïëäÅ^‡f›m¶O?ýtÅWœ2e ræ™gžñãÇwîÜùÛo¿ýꫯä!Ϻþýâð3 à]"¯ùw„ Œ6¿ùæ›Fß«W¯•§+­´ÿÏ7ß|ÅôŠ" ÔÜù š8pàK/½Ô£Gž{¾úê«+¬°ÂwÞ q)_Õ8‘Ô5EÀùòݧC9äž{îÁò½÷Þ¾1bç›l²ÉE]DIûWbZo:ü«s|!ˆÇü±ÇûÇ?þñì³Ï2â yË›7Þ¶-±ÄK.¹¤ù—ߌ3Î*Ùìç Aƒþð‡?l¾ùæ¸ãŽ;ößÿË/¿¼YáÐu “Þ~ûí·ÞzËüËÛHÞ@†JbÒ*«¬²ÖZk­½öÚ}úôûBô§" TBùê£>ZýõyæÅ£¥|¬‚?X`–è)_µ„H (íƒ@q|5jÔ¨=öØãÃ?äÉûW\±Í6Û€êÍ7ß¼ï¾ûò%Π/|õÕWo´ÑFñP+_Åã£wuøW·`@Å`ïÑGeÔ÷Ì3Ïð]žñw¼L“÷i¼Rã„wzænŠ^!þìg?c¬ÈEª#Æyê©§žBš©ÂÛBóŠ’^!š»|`¸êª«2ìß¿?BF†æ–ž(Š@µpÃW¡±Ÿ ö"/¦@Où*hZE¨"Eóë)œrÊ)gœq]©\{íµÝºu3@½ÿþû»í¶ÛèÑ£ét 2„’¬³`îZž(_YUûb:ü«Cˆ™¹ôÐCÝu×]wß}7oÏŒKŒ”xi&#¥ÕV[W|°†¹›ýdà 7|à<|øp¤uÔQçž{îlpÿý÷gn$Àƒ¼|òÉ'Ðrð38¦åí妛nºÙf›­·ÞzÌG5µôDPüDÀ1_Å óbn¥†Nù*5tZQðg|Åú »ì²Ëرcé¹p 'žxbã"|Rxê©§6Œ^Ðk¬qÝu×±nBД¯² Wéº:ü«pø^yåÆ{Œúxôõ×_‹'sÏ=w¿~ýòq0O’…7 òðÆoÜa‡ø*'ßû¡…ïŒ1þ†nØ~ûí ÒËÒ£2£•¡à˜1c>ÿüsQ4Çsð´Œq £A¾í)H»ŠUt”ÂW-x- ¤sÖÔR¾2Pè‰"P!óÕÈ‘#<ðÀ/¾ø‚Ïp8§ƒ=Ÿ]wÝ•Of:uêt饗rS8Ñ-å«DpU»0C=ª…ÀO<Á{6^å™ÌãÏN:é$¦}²ƒwm²>'@=Auüä"·(¼^Ð9Îâ2Žã~ð9à@¤WÅ*Š€%%òKLñ¥ŒÄ¿œ73زX³êöו¯ì±Ò’Š@)¸ç+a3¥Sºs;í´“3m§…¥Ö/~ñ „ØÔJTFù*\•+ðÕm·Ý&Ÿ¨,¸à‚¬Þ×hdº+ˆB =„ßzë­é„¤¨¥|•4ªèðχ(üd£.¾øbv´“ñ ÿ²¯Ýˆ#ØZÔ§ÛUØvÜqÇÅ[Eq!¾˜ã»€ ¤â‚àÜ»wo`×—Ž¡êªŽ€Ÿ|•Ëà-!¹ÄWù*Uˆ"à _±$¯é¤ïÁå'NÌ74D¬ÈG‘›øŒ ÊWŠJœèð¯ü0ñÜñÇߥKi´<¹a'½_|±|Ë,øóŸÿŒ‘VôµB°BÀ[¾ÊqØ–£(+L[R¾j…ÞW¢ð‡¯^xá…•W^™s£øH¯ ¯x‹pT u(ƥȫÊWE¢››lþåe A´Ì}öÙgöÙg—ßZk­õ—¿ü%ã,ðfXVáÑN÷îÝ1õÊ+¯´©B1 S…Š6åÝ—jvÁŸ@ŽRèÒ½ïªQHŠ€Ï|•û€-wIÑn,¯|Õˆ‰^Qš!à_±Dg‡èi,·Ür–"GŠP‡Ò Š6ƒ4Ñuå«Dp¹/¬Ã?÷˜ÿ¤ñþûï8p |ªË†ul’ÎJžå˜b­õä“O†M˜-iù"Å(L*Z+)§ àÙ9 šx ST«"àžóUACµ‚Äf¯òUv UBðН&OžÌ:yt„8öØcg_š u¢0£¬ˆ+_•…|¼^þÅã“ÿ]Ö 1ßžÍ9çœ|pÌ*ù«O+ñ½÷ÞÃZ¨„µ4íeP˜*T|ÿý÷ík•U’@q³ Á*ËÕ«ø€€ÿ|Uè ­Páã«|•@­^?|ã«Ñ£G/¶Øbt'XvŽ©FîG©¬x‡ãÞ£QùÊ@áɉÿÜâ±ÇÛ`ƒ ¾:6lØ'Ÿ|âN}6M»ì² fï¼óÎIÅP…ŠTOZ±¬ò…ИÏ +ËÕ«”…@%øÊÁðÌŠ,!V¾Ê‚žÖ­ ¾ñÕ÷ßÊ)§È”¢~ýú½õÖ[eAj  †1˜„aeY‚^å«Á©Öá_B~Ž;Ö,ÇÔ¹sgF_|ñE!šŠ:f̦DÎ1Ço¿ývR T¡"Õ’´n‰å a"X&á#ˆ%Ú£ªgT…¯œ Ìœ)Jbå«ÔÐiŪ#à!_Ñí‘5q 2ä»ï¾+d À ‹bXŠŽ\¾ö+_å‹g:i:üK‡›m­—_~yë­·–!ÄÜsÏ}ÒI'}öÙg¶•ý(ÇBRk®¹&.`|:‹¨Hu„´ÔU:«lj,Œ'pABI@m*jE ŠTˆ¯É«K—<ÊWépÓZEÀO¾úë_ÿ*ó-»uëöÐCùƒ-Æ`ÌÃÈÒ S¾*7:ü+ ÿO?ýôÈ#œuÖYil;vd¼ Mõ ‚rÕUWáÂ"‹,’ú“e*R!ˆ J®Ê9#|(a%¸U1^íTl¨_•2+E©MìBe”¯B€èÏú!à'_ÑÕÙÿýé'pl»í¶vù0 ÃÄBLMÝ©Ë1£”¯r3‘(þ%‚˪0S«/¹äùxŒ·íƒ š4i’UMÿ ™‘[ÆíûdÃÀ,cÈÒ±!ˆ„R¦O\B\îúÒQê@åøªÄaX‰ª“&›òURÄ´|%𖯞zê©=z0²b¯…‹.ºÈg01Ov¡À`ÌöÁTå+÷QÐá_Θ³â°ìì ¬·ÞzÏ<óLÎ ÜŠc¾8ŽôéÓ'ã¼Mª#QtëAÎÚ(aÅM¸sV â‡TޝJ€•n@¢ìP¾J—ö?ùŠîÍùçŸ/;­¯´ÒJãÇ÷FÌÃHL¥ƒÙŸ±ƒ——¿ÊWy!i#G‡6(Y•™0a{ÇM̰ä’KÞpà VÕ<.Ä‚Q9®ÚbÖ)q ¬¼À&¸„XbMÐ }^’UŽ"à*ò•'C/O̰Ïå+{¬´¤ŸxËW¼¶Úl³Í¤3ðË_þò«¯¾òÀF«0ƒÅr\À‘Æ2¥\Q¾r»ÿrÀ™'—_~¹|ì;×\s~úé_ýurË!{•²sh^†È&¤ˆÍK`‰r1&ܰ'¡'ð ·DÆ`¼lI;8…k‰ªWXùª8l‘¬Ã¿ô𲕠ï˜IûçÁ‰ é¦wæ¿kþðý{÷ƯÓN;í¿ïdýÅfzˆE8*²Êò¦>¡—G€$)Qú&?Þ£†x„@uùÊÛ–·†Å§òU<>z×|æ«W_}uµÕV£'3Ë,³Ð¥©Agpwp ×pЇ”¯ Š…ÿRË'ª«¬² M…cï½÷žÄòܼ`Ï•¯‚hè¹oøÌWW_}µ|ô±ÔRKýóŸÿô º,öàNчÁAÜÌ"*ߺÊWùâ)Òtø—Uf¢>\VyZzé¥ë·öã”)Sºví \ýõ‰Ñ±¨€X„£EÅ«T„d %ðŽô IôkÀ*¯¦¶Vš¯*1¸ª„‘‘Ù­| ‹^,ŸùŠËÎ;ïÌßwNê×!î>û¨|•oÃÔá_2<™$½ÑFIû?è ƒr9–ÌšbJ<û÷ï_ŒøŸ¤"(*NEY’I C2„T©ô'ea¨zóB Ò|U¡aU…L ¥–òUýY">óK—Ërß¼ûÓŸþT"JTã ¼áÄew ÑR…ò•%P6ÅtøgƒÒÿ—¹é¦›d‰$Þ\Ý~ûí jV§èk¯½Æ›+677n\qV#(B]qZJ”LzÈT†´)ÑUݶTš¯*7 ªœÁÁv¡|DCÏKAÀ[¾â»¸3Î8C¾‹[}õÕëÚi 7q–Ù8Žû^}ߨ| VºŸ:ü³ÂmêÔ©ûï¿¿¼Òaƒ”‰'ZU«`¡-¶Ø7÷Ûo¿¢mGŠPW´¢²ä“$f; ’‡*ËÕÛnT¯*:”ª¨ÙÒ:”¯Ú%üñ×g¾bñÉõÖ[¾ «buÔQß~û­?¸m ÎⲬn @Q´F{ùÊWöX5+©Ã¿fÈüç:K÷ìÙ“öϺŽ\pÁnÔîì¾ûîÃÍN:9ߢE¨Cií€üC$Œ¬ K é¶ÿÁEÏ C ê|UéAT¥'%•¯ k—*8Ÿùêæ›oîÒ¥ ½”…ZèÞ{ïv îWq÷(Ä+w•¯²„C‡-ÐcBÂÜsÏMê÷êÕküøñ-JWùö÷ß¿âŠ+âéÙgŸíÆ¡¥¨v£±-¤ Ƀ§$’N-%í£´ê|Uõá™Vu”¯Ú‡.J÷Ô[¾úꫯ9äþjs 8F]:V%€û€ h à”hLHµòUûŸ:ükŠs‡*/¾þóŸ×~òÏQhÞ,\élÓOÉ:™¨n†ZÜ yH!à%H*¯¦Ñ×`uâ_5૪œLVÝå+J=)Ÿùêù矗Gá³Ï>û¹çž[• B7p€Èû•¯ÒÅB‡Ѹ}üñÇo¼1‰Îg¯çœsNt¡]e[YÕæ–[nqéêÕàRo)ºH$ù|œÔ"ÁJ±A•ÖðUÕ‡L¡¼ª;ÊW¡˜êϼð™¯.¹äù^cùå—×ý{C`¡ÏDº[îO嫤øëð/±§žzjñÅ'ÅY¼ñÁŒ(Q»K2Ïaƒ 6pïJÜ«v¯‘t’AI0Ò̽ª±~Ô€¯j0XjÌ«8¥|ÕV½’oùŠAéÖ[oMo„cŸ}öùòË/3zZËêÀ8‚pyõ [ù*QÊéð/ ×­·ÞÚ±cG’»oß¾^­t64¿ß/¾ø"o¥fžyæçž{.?©¶’PŠj À Û:U.GR‘Z$iF²UÙµ½|jÀW5&5˃¸¦|Õ,¸z=Þò#‡E]”¿ËóÎ;ïõ×_ŸÂµ¶ªD\€tþø®|e þýV|„Æ~täôÞ{ïíì¸ÿ² Œ›nº).|ðÁe(ÿI'ª13Ê2À±^R‹Ãe’­öß=:ƶ­ÔÕ€¯j0@ŠO¹8¨|b½k‰€Ÿ|õÝwßp Òñëß¿ÿ;ï¼céN›(à’n £'€(_YB‡ÿ"qĤ2Ç©§žj _ Šñ4—yóÑG•åªåIR[½ #ͦ§Û $ž.SVîUTo=øªC#›ü©‡›ÊW6±Ö2‘xËWo¾ù¦LÆa Ò‰'žXïEÈ#C“å"pÐÑ“FÀÌ"-ߺÊW-ñÔáßO1›y›m¶!ƒg›m¶k¯½¶%jµ)À¶ž=zôÀñóÏ?¿\§030¦­¶U%ÙH9'ý¦M›VnT{U¨_ÕcPd™3õpVùÊ2ÜZ,ˆ€·|5räHÙÖ«{÷î<òHÐf=·Gèn `©}Å¢K*_Å#¬Ã¿Mš4iÍ5×$w;wîýôST“`7‹´ë*_Å€Ùîÿ÷Þ{o¹å–#e—Zj©—_~9©úÝ2S.ï¸ã¼Ã Qî4ÔRp ñH?|'IÈRlP¥•@ |UPŠ„©‡ãÊW)BßžUüä«'Ÿ|rÙe—åîœsÎy饗¶ghŠð0`‹P‘B¦òU3ÐÚzø÷Æo,±Ä$kïÞ½ÛðÅË Aƒð}óÍ7o–î¯c &a˜{Õåj$ýHB|'!IËrQí~"P¾ªÇ(u†ÔÃ}å«Ô Ð>=ä«üqøðá³Î:+jWYe•—^z©}ÂáÆS Xàd p7zãµ(_EâÓ¾Ã?¶Xd‘EHÓ~ýúµá«ÿgŸ}V¶[ðŠ1¦Ä-("[ˆ³‹$!©HB’–m²†3lk ¨|UÁOÆtªÊWÓ ÞÕ=ä«?üpã7æ/,û ýõ×õAYÞ¬ì# ÎìeYÔ«|DCÎÛtø÷ôÓO/°Àdçúë¯ïÏ4åÆðwE6[?òÈ#‹S‘N2&—R6 Ogp޵HE÷INR4GÉ*ªÒÔƒ¯ê1ìÉ%‘ê…òU.ÉP?!òÕwÞÙµkWùÛzûí·×sß<déc;àû`žòU( í8ü3fŒl30pàÀ¯¾ú*„H;ü¼á†àÁùæ›oòäɾù‹I†yé›mì!!IKÜ'EITU…çÔƒ¯ê1àÉ1UêˆòUŽ)QQ¾ñ»À ! ñåÜU¾ ‚ÜvÃ?¸i®¹æ"#Y§¨­ö0QçÕ¼,4rÉ%—˜‹^`ÂÈöœžAZÊ"Z$ªŽ½ÊL÷ÆÔƒ¯ê1ÔÉ=úõ€Eù*÷Ĩ®@ßøŠe?V]uUº|ŠvÆgxò)Zuã›ÔrvùØ’@ޤr/¯|e m¯ásä½ßn»íÖ¶û{ž~úé°áŠ+®è-†y‰©&SÛêHQ ]uh[…>èl=øªƒœ`\r<¯8ÊW9¦DuEùÆWW^yeÇŽù3ºÌ2ËŒ;¶ºÀVÝrÀ'‚p”ÒÝQ¾’´Ñðo‘e.2¯Vé)XŠÌ|èÔ©ípÔ¨Q¥`©ó0SÛvª)*ïIZ] Æ2mêT¬|UáM¡yUˆ”¯ Mÿ…{ÅW¬ó±ãŽ;Ò…ààAj{®ïàUÎy¢MD *×<å+ðo—ákË:Ÿ|XÕžs>¥±í¹çž4¿m·Ý¶Ü¶g£#1ƒm ײ ‰*ß’ººD-CÜÌ©zðU=6Íb”ãõz¥|•cJTK”W|õè£.¾øâtæž{îk¯½¶ZHÖÛZÂAP "Lå:«|ÕÃ?ö•ýýXV±=×z‘fÆFœ3Í4Ól³ÍöÚk¯•Ûðl´c$¦b°?û‡Ú˜oÒUÖ%uGø|±õVZ=øªCgIR¸”¯œ%Œ?Šüá+^éœzê©ìhÅ£OŸ>úÌÔŸ$1–BC€Á"dæ–û“6ç«úÿØðq¹å–#ÛØT­Íçôïߎ=öX÷Í,FLÅ`ÌNW½µHZÙ4&™ëá”zÑ zðU=3ÍbTÐõz€¦|UPzø)Ö¾z÷Ýw×Yg: <2¦çðÝwßù‰˜ZEha"X„ŒÀ•ˆI;óU͇Ó¦M[sÍ5I²Þ½{—>Û¸ÄGõÈ‘#ÁaÁœ2eJ¹–ØkÇT ÆlŒ·¯U¿’¤. $3)]?Õ#A |UaL)9Y蔯JI÷Jýá«o¼±sçÎü‰äC‰ûï¿ß=ª1)„I>È"p„/iõË·-_Õyø÷Ã?l³Í60[´ùkhº{÷î@áòK‰Ú-c6Æ·ù°‡–í:Hi;†Z¸Ôƒ¯ê1€)1aê òU‰)äFµ'|EÇàÀ¤“À±å–[~ôÑGnÜW-Ù X„LbGKìãµ'_ÕyøwÄGX(xƒT¹‘Ë‹/\È‚@ ê’Æò€“Ä®;êBðU=†.¡¸¸ÿY•¯ÜgŽK>ðÕ³Ï>»Â +з™cŽ9.¸à—î«®¼ p„ J𗨤rÚ¯j;ü»ð É'Ö=ztÒ<¨Yù÷ßÎ9ç‡~¸Š®a6ÆãŽTÑþm&™IiÐ ½s«¢JG |UAKé™ ÔLå+OÒ)w3|à«‹.ºhöÙgç¯aÏž=Ÿ{î¹Ü}TÎ |‘PPÂêLoHQ»ñU=‡·Þz«|WªËþ’ß»ì² íj§v åz…~b<.àH…l.ÈTR(Ho’¼ *Ö15à«z WÇ=^]= U¾Šrï–ÎWLÜb‹-ø;È1hР' V1|~ÚL ¥Ä”à–5‰·­øª†Ã¿qãÆuìØ‘4bUY?Ý¥UcÆŒ™qÆy·þöÛo»Ô›¯.ŒÇÁ|%WQ‰Mz“ä¤zíW›ƒÔ€¯ê1P Å“óz«|åI:åbFé|5jÔ¨…^˜¿€]ºtùûßÿž‹S*Ä(a%¸„˜@—bUûðU݆ü±ìø¹÷Þ{—’:^)ýñÇeáÓ“N:É+ÃRƒ îàTŠê5«Bzƒ©NÂ×̵¶r§|U!Š·YWx•¯¼M°D†•ËWìÓ=dÈžó·oÀ€ºn¢ØU¥0a%¸„˜@n‚îÞò6á«Z ÿX&äþçÈ›¾}û~óÍ7î“Æ7W]uh°ºîÔ©S}³-©=¸ Ë1"iÝú•'½Ir‚»ñÆWn9Ÿú…#G5à«z NÒ…ÏY­€¬|å,[ŠST._½þúëò,›íÂýë_ë_½â]ºd‚Kˆ 4=‚Nè›Ô&|U«á H—®]»ê!´3^ªÍ2-»£ÙìtF’“ê$}úÔf¶$ŽàŽxL’ê$ûì³ÊA§§F€p˲DŸ4 R‹JQ±ö|U“áß+¯¼2÷Üs“"ÇOæúUyë­·j¹VŠYÉ뵑ð¤=ÉOHQ]«”‚@ÕùªC‘RâžQi `W¾Ê˜¥T/‹¯žx≥—^š?p¬söÇ?þ±ßUiézYÍ‘d %\ÚSo¾ªÃðY޲gÈÏþs—™á³®í·ßÒÜc=|62m8…k8˜®zýj‘öB¨Ážõ‹N£GUç« BƒR•+5_ùª*É&v–ÂWÌôùío;묳ò§­wïÞúp³Z9“»µ$i@2$†Ëm5æ«: ÿöÛo?Ò¢W¯^Úý•VÇæ•Âó’Z~‰S²‹=næÎ2UHÚ“üD|ÿý÷¯¢ýífs¥ùªêç[ÕC |U­ tÏW&LØpà ù‹Æw Gq„.ãW­„)ÈZÒ€d %H Òƒ$)HQHlùªòÃ?ö !˜è8~üøPØÚó'‹&Éc’aÆÕ\#踩ËIˆI~š˜è>Hžç|¥ùªêÏsÃÞ¼ªBùÊ>Öå–tÏW·ÝvÛüóÏÏß26»ë®»Êu_µû†)!+Þ‘$¤ŠóêÊWÕþ}ðÁóÍ7LqÁ¸Éÿµ\vÙeÒ½{÷iÓ¦ùom: q qgÓI¨_-š€Ðhõó®Uš¯ª>ä¨G /ªå+JoOóÕ×_}Øa‡ñWŒc“M6™8q¢·È¨a%"@b’'$ iãÀ˜ZòU…‡LÿÝh£H‚Í6ÛÌAø+¡bÊ”)òhäoû[% Nm$zœÅåÔBjV‘†&4 —3ãk†aqîTš¯ª>Ø(.¬%J®zP”¯JLž–ªóÕ‹/¾¸òÊ+ó÷k¶Ùf;ûì³õOX˵sÒƒ$!UHÒ†äq€FýøªÂÿsÎ9Gú”ȤþQG&ýû÷7Wj|‚›8‹Ë5ö1‘k4üëú·‰psS¸º|Uõa†›ø–¢¥Ò¡Q¾*%g,•ºä«Ë/¿¼C‡ü5_n¹åÆgi¡ksH†´!yH¡¢Ñ¨_Uuø÷Ì3ÏÈÐÿöÛo/:êU‘ÿÚk¯ÉL3ÍÔ&Š›8‹Ë8^•m'Í6HѺT¾=Õå«J0ìTÝ’•ò•Ÿ‰çŒ¯&Ož¼ÝvÛñ7‹cÏ=÷ÔÕûüÌo­"aHɉt*ÔÔšñU%‡ß}÷Ì8è ƒ vµ„o±Å4Ö骖ÙY¬•EÉp<‹šÕ¥Q4šIÍ\«¨;Õå«J-*š-)Ì®t˜”¯RD¼Ð*Îøêá‡^l±Åøk5Ï<ó\wÝu…:¥ÂkŒÉC ‘H¤IU¨§uâ«JÿN;í4"Í5^Ý$ißwß}`Ò©S§¶š ‹³¸Œã¸Ÿ±º–§QÈV¹4“ºúX-¿*ÊW•TT+C²[[Ý`)_e~¾ðÕ÷ßòÉ'Ï<óÌüíîׯß[o½•¯ *­Ý …H$Ò‰¤"µH°‚¨_UoøÇþ²Æýý÷ß_P€+'–\_qÅIý³Î:«rÆg4—q÷‹kð-t_¦&4Ý-×=ø!å«ê'Bø·ÏÏê†LùÊŸ,uÀWo¿ýöÚk¯Í_(¾Ý:t¨ÎRñ'ú•¶„D"H*R‹#Í r§6|U±á þ 0€èî³Ï>…¶Šb/¼ðB0á›5p½‚—åe xeX¹ÆÐ@H ‹.¡Vb *ÊWÕH”kTW7pÊW>ä¾bÉîyç—¿Mݺu{衇|ðZm¨$©E‚‘fÅ-€_¾ªØðO6µ[pÁ‹þijBí(dóÃ[n¹¥Bfçh*ŽÓÚA³Â  4`Ñ­ &îOªÈWÕB¸¯‡+>å+r©P¾úòË/å[}þ*m³Í6Ÿ|ò‰.« õC€Ô"ÁH3RŽÄËÝÇzðU•†lB*ßw7¦Ï=K<äCÈò 6ØÀ.oUà> zè¡ÞZèÞ0Ù‘&3aÂ÷ÚUcùª¢ƒM¶  ¢òU0ˆîÏ å«§Ÿ~ºGüfþßÿþ÷î½Sí†i&»‰x¤_îî×€¯ª4üÛ~ûí¡­¶Ú*÷@VW û]Î2Ë,|íúÜsÏU׋ì–ã> …› @³ìF…&CÃq£Nµ¨_UtØÄ\ÏІRùªÄ.ˆ¯˜Pú»ßýN¶éâýñãǗ裪n+H6Yƒô# sÿ¦ê|U™áŸ|m9×\s½ÿþûm•ÁñÎnºé¦ôïu P’y$±¶ºKc¡É!4Ÿ¶r¼tg+ÇW0”ho ¨b@•¯ÊJ§‚øjÒ¤I›m¶€8øýÕW_•å êmOH9é’¤" ™#Uç«j ÿXÔQ6ú;ýôÓs ^ÕEÝvÛmä4_¸~ôÑGU÷%»ý€ ß”Kviµ‘@“!Ih>º2ª³˜Vޝª8TpÍê*ªbX•¯Üç[A|uÏ=÷,´ÐBüõá³ü¶]˜À}4Uc#¤Ÿ,AB’–R_©4_͈۴OÏK/½ô—¿üå’K.ùÒK/Í>ûìž[ëÆ'o¾ù&«è²³ ÿòø„/k9>þøc¾©ýöÛoY{“1€7ìl6À¿;vœþùio,@²ÄK;þ]j©¥æœsÎâL½à‚ ?üp¦z?ÿüó³Î:kqŠ*$ù›o¾Ya…ˆÝ%—\ràVÈòêšZ-¾â¹Éúë¯ÿ /ôêÕëÁ\`ª‹<–Ûð‚’ü))—¯à\¹à*_9ÈŠŠÜùŠÎÉñÇÎ9çн„[®¹æšE]4¤T -ùŠ÷W¦ÅÇl%ö¯*2>mÝ}÷Ýù7ãŒ3þêW¿bsË\ºˆÕæ«Ô£^g?ýôSÆdÞ7ÞèL©ÿІ& u †|­å™ñÍ7ßÌÖ™Ûn»í2Ë,CkÉ«Ù# ˆE8*P”¯å@!ß—N¾’+-í†n ‚4"šR¥©„ñÕâ«*¾ ¥Auù*äHA?+bå«‚2!Rlî|õꫯ®¶ÚjüÅáS|:Ù?üðC¤Þ¶½¨|UVèIE’´$9IQ5KªËWxû7xðàóÎ;o½õÖcàž×8¤êrxó¶ì²Ë~öÙgwÜqÇÀ³»3qâÄ»ï¾{ôèÑ=öØk¯½ÈÓ&ÞÚ™7x /¼°¼Ùã_¾+ÆÕW_ý‹/¾ J§Nž|òIf`N: å á‡~hÞòQžcùxÁlOÇ7{2QÄÜJwrçwn¾ùæØ€òÔ œšÕâ,ûáyä‘çž{nÍ\óÍ ñUå^ ™X§ã+æ)°¾ÅË/¿¼ÜrË!Š?ÿË/¿øàƒé0ßçºë®ëÓ§OPWÛž§à+æ&œxâ‰òÏ{ìÁ@…—¨¸å–[žzꩼt*¥Uƒ>þøã»ì² ó¡è¾²:(Øfwªª|•Ëð·8!ü©æíL3ÍôÌ3ϧ¥r’ DÊ2ðËhù?ÿùÏ!C†¬ºêªÁW|LÝd…ãŽ;näȑ̢d"GŒ–=÷ÜK¶ž~pÂϘŒýˆX„£E¦áaf` &ÅH°¹%ãa ²)Ü&eh>4"š ªM\.ÅÍ ñUå^ Ð,|ò7ô32[ÜðU¤êÜ/Úø›»ÒÔ•¯RC—¨bŽ|5eÊzÕò}×]wåg"KjY85_1Daü ˜sÏ=7£h‡~r‘[hD¬N|Õè]ŽWHNRTr•¤Íž«å+ß—~aXA´+L}³É_B¯ÛŸóŽîè£^|ñÅ¥ð/Ÿäm±Å|FÈ-û5Bžxâ †m¼äU'ü䢥% ,Q‡RT¿ Ä0Ìã–¥œP1`ÑÍ0B˜ðSРo镼¨ _Uk0¯"ý¼“ ñUŒÆ|o%õ7_íI¥)_%E,Eù¼øj̘12\áʈ#RXR§*YøŠÍ øfR>Kc^Õ믿D†Ÿ\¤ÃFŠÅïdPu¾ :^Ä9‰*뢓º$pFUä+¯‡ $HtÞå»ZkÆ0—^]¶8g"_RK˜-Àæ'²Š üºuëvÄGÜ{ï½L‹J* êéÛ·/r†*u9á'ãY)R`Æ`’ØÆ¿˜ŠÁ˜Y%æ"àP bÊ´Û-‘¼nµŸ·Dý­ _Ue_Åøs+>òå«x]9ÞMíoŽ6XŠR¾²*u±\øŠï©XüP¾§bdÂSàÔöT½bv¾¢yÊ>ü“‹œð“‹Ü2Å’ž`†aF"Šƒ/s0ìEQW— ‚FSÝ1ˆIŽç•à«J rä«–þ¶,Ÿ!¹ðU¼Š|ïfô7_câ¥)_Åã“ñnv¾bß3¾zâo £VSdòaF“*Z=¾zà¤ûÄš·ß~{<¥ ¨BÅøÂÁ»•ã« ñEœ“´¤® §IfR:µ–Êñ•¿Ã?Ö V˜ëœâåOêøy^‘G8¬Â,,âoc*Mµa„ ©Å×_0>#¢øÏùl$›‘Þµ×^,ÏOÇ„Á»‰Î1S1³‘É#¸ƒS6r€ˆ*Àh6åÛ¡ MI> qµƒ¿.}¬_yÞõϯ,ýµ,ŸNù*^x¾wsñ7_“"¥)_EÂ’ËÅì|ÅÚÝòŒ•5Û˜¹“‹UÕ’_ñ¹ k¡K?‡eð,G £0*RÝþ›¹B|å +H`³G%‰NcåøÊßáŸLq<餓ÒE¢–µd‹I¦DÚ4õ[o½•ÅT~6MEuÔQ¬À™,|›‡Ø~ýú…cüä"·(—.ÌÆx· §p­¥p ’i®€Ö²pû A¡N‹Í=âþó•çþÜù*‘¿‰ Ç'O:¾Š—™ûÝýÍݶ @å« 9žgá+v¢cyOþŽp°Ð¹”£aU•_½÷Þ{묳H2Š#Ûmºv" SEÆA”¹eR ¾²w'uIÒXV $¤7IžBTµøÊÓáŸìñйsgöHƒZVavV 5Gïà=÷ܳÆkP’ƒOéøD8ûÒFAf•—±cǯË9y“.ëÁ4ÞM}pÄ싃¸/í¾ûî@ºø’ís—E³šXûx]´§þó•ÏÝý"ø*…¿)ªÄäU ¾Š‘VÄ­|ý-ÂBd*_l¾?~¼Ê3’ýöÛ¯™%ÜŠ5+ú:îà”Ù!gãw2Øf›m°èRk¬_Eš˜ÐÄêçZYyÎWÞvô â«Ôþ¦®Ø,ñ’òU39]ÏÝß"ìT¾ÊÕÔ|uñÅó)>>Ø0óé§ŸÎÝ0ÏæÈW¬à" ÔIO‰9³øNuóæ ±‘kÆØÈ÷œ¯l\È^†Ä&½‰ ©NÂ'X!¾òqøwÿý÷=¶š5E’ ~åYJ˜Wü1¯ÔxêÀzP²^ð<óÌsÖYgM›6­øúŽè03f9VnÉ\ͻp qKpÇq?R‘¼¨ºÔÛHDŠ­ôEš•|5NC«´#žï9_ùÙÅ/ޝ2ú›±zdNÚóUdõB/áo¾+_å‹g:¾â»&y–ÊßÜ}÷Ý÷Ë/¿Ì×*Ï¥åËWtKV[m5齜}öÙ¹¼AE¢¤ˆpT¤†Ôg¾JíT¢Š¤7IN€8H{’ß¾z…øÊÇ៼¿ÒWÁ„“ÇuÇ{lð¢9¿é¦›äíÿÌ3Ï|ÀðÝÜÊ÷„U’zôèA“ày¼d PŒÂÅ­†›8‹Ë(ÂýfìôeW0^ò€Š†¼¨çéð™¯üìÜÇW¹ø›‹Æ\²ä«ÆŠE_)ÈßÍV¾ÊÌ|ÅôBùìbÞyçýÛßþ–£1••/_9R¾âa]ºÜ7aB ¬ˆ e×[¾ÊâT¢º¤: Oï‘äO45·*|åÝðé¶|9Æ&à‰܉‚Z¹Â4cR?áã«¶vØ»k­µÖ³Ï>[¨w¼sC‘Í Î ©R¨I¸ŒãÓ˜(?ó4ÙC"#ê…cá4.š -ÅìvǦz®Îg¾ò°[_(_åèoŽ¢B Ü’¯BåÝü,Îß\ìW¾ÊF„$å+Ö‡<þøãeq‘µ×^ûwÞÉË’JÈÉ—¯x§$ßÅÐ]Ùi§»s¹`‚X„KußÓúÉW¹e#„„'í“&@C 9ØÔª _y7üÛgŸ}Àš…wlPn‡2¼ˆïÞ½;˜\qÅ!o¹å™ÅÇ“ž /¼0Ëž•!É‘?'Nœ˜hJ'3?1›*TŒ˜×EÇ}y¢ À’ tXŒ€ºÕ¶?eé6š[Û"‹ãÞò•‡úBù*wshò­%_™’.OŠó7/”¯r1_½ùæ›}ûöåO'Sl’.J™‹µå É—¯žþùž={f‡.»ì²¢]CŠP‡RTgQç'_eñ(Q]Y^Uf™Ñh6Õ+ÁW~ ÿxÖª;Œ³_ýuˆÛ¡Ì)§œBîÝ»wptÇ<ëo¼ñ»ï¾ë Y{fë­·¶×Ea,t³ò ÅtHfœàC/ @n¦½ñõ.I£¡ÑÜhtõö´8ï¼å+ߺòEóUAþ$V2†¯ŠËØxÉ…ú¯ºå]å«–µ,ˆ¯®»î:ùº~±Å{øá‡[ ¯SÜù*8s6ãE98=ä+—)G !Їä}M£¥êJð•_ÿ¡C‡‚ïöÛoßÜ6)ÀΞLÒ“ ÿ¾òÊ+½zõâ"}wg+/3§<Åvf‹ˆÜç¸G&@pÝ^ (SlôèÑ ˜–{ªšŠ5>¡¡ ®Æ>ꚟ|å['¾h¾*ÔßB…ÇðU¡y#¼PcôÚÜR¾²A)¦Œ%_±v…<ê寘Ož<9FfýnåËW¬³ãŽ;‚$«‰8ž„:³„ f4[!Ï2ˆò•¥å¹£!JÍ$^¬ÿ|åÑðoêÔ©]ºtÙüãñ°¶ÏÝ]wÝ@˜Ém\æ+d™Ésçž{Î\/ô„f/“@† ’TUpêIZ7]y`‘‡^\F&Äi:±õ«EC#44:š^ý¼+Ú#?ùÊ·î{Ñ|åÀߢU4ã«¢¸™ü¢ým¦·åu嫖Űä+–È^vÙeù»À£Rsc .åV¾|õøã/¹ä’€iùʨ —y[%FŒÁ¤ŒZ|㫌î$­N£W24“øõäýç+†ì°A;a¤ñ¨kù1cÆð½GÞ~ûm|døt 'p”~þóŸ»ì²1¥ /¼p˱  ©ŽÆ»]A)¡¸MFžÀ˜\Ø‚ôVN¬¬š“bs›Êyš»Áò•Ww|åÌߢEòUîk/°hí- •T¾ bÿ³%_Ñ`Ï9ç6—âïæ*«¬òâ‹/Ú ¯AÉ|ùJÀ”mV_}õÒ¿fÂÌ ²˜D”1/KÈ|ã«,¾¤¨KÓ &%LÏùÊ£áߪ«®  ùË_RÄ£~UhŸk®¹&€œxâ‰xÇvœ;ï¼3?g™eÎ¥¿füvíµ×¦ÓKE,O7zL§Qjp¡è‹€ÉO€ÍHY¬òª.Í @hz^YU c|ã+¯ºìøÊ±¿Ô5òU‰ Á¿)¼S¾JšT‰ç+–gÛd“Mø[Àq衇ʟËÔº*W1_¾¢íÈîúhBÓ¯_¿ÔC&*R!ˆŠ×•û]à’iŒ€)$º|™»S9 ¤¹Éâ±4ÀÅÖ^”o|åUgÝ_•⯥!¾*·9ð7©ƒÊWI“òñ|uçwvíÚ•?Ð ,°Àí·ßžNEukåËW<ð€ô1æ›o>ÁÄ$ #Ö‰©£æ_eô%EuÀ¤É&͇FÔ(Ás¾òeø7hÐ @ö3˜›µ[2¨Îc'Þ•#Ðwsh²d0¦¼Š”qµ<×B£#»h€žÛé•y^ñ•WÝt|U¢¿T‡øªÜ´wàoR•¯’"Fùf|E÷ôÈ#”7Bm´Ñ„ R¯t•ùJv=×]w]o™Ã°ðGS³ïçá_¹OEš 0iD4%TÈŸùÊ‹á£Ù´í¥—^ aמ?e…®>}ú°ÇÈRK-En­¼òÊð”{4ˆv6Í®Z6ø ãsQ©fž*.d‘é_eq$u]M‰E³ u¥¼å+/†²©ÀŸþô§ÔÐש¢ì²í¶ÛJ_ŠGJŸþ¹{¿ýöÛ=zÍl-˜—ösÏ=ˆEx^2íå£<ŸXàÅ ¶¯^ã’4=РÖØÇ]󄯼ê”ó$¥h¾òÊ_ÆùªôUüµo¡ÊWöXQ2ÄWlþ&ËbÃù»ï¾{)½‹DöQ8/¾’Ù³ ÉÁ̦>ú¨k‹“‰Á2à û#ç.Ú«öНìÍα$Р¦çÂO‹ó›]½å«ò‡Ï?ÿl(ޝ¼ò·1˜gø ¨KßÍ¿ 7^Q¾jÄ$òŠðŸ»Ë´&9î¸ãJùø"Ò<Çsá+Þqí»ï¾ü¹ä`Žß”)S{Q:1³‚q7Ó)òŠ¯Ò¹½MŒ†f–¥’-¾õ¯Jþ7PZh!}CÛ“÷QÂF%Nõá9P¡›4˜Í$J|f¼²h{m<5mÑi† A“L-¤öKç+O:ßhf í¼óÎ…ò•Wþ6KoF¾ðÒç˜9ð·Ôæºò•"æDøjÞéí”u>FS¾Þ·rá+&ÓöìÙ0;tèpÙe—Õ1œÂ5ÄMœMç W|•Î…\jÑÜdqÓ½ê_•<ü;æ˜cȳC9$¬+-䨣Ž f]ºßßÏ@EÊ—âC† 1s?A8ž¢¨Ä® ËW,üÜ}¬œ@š!PÐ$+g¹3ƒËå+ºÝA¨O8á„BùÊ7ƒ¾‡Î˜jø ØCÚÝÿtàoK§”¯ZBÄb´P9¶ÜrËÊ-LÒÒÁD²óU.C£D6—R8—!®W|U Œ¢”FGÓûw+üi} ©.yø·ÄK€Ë#<2«Ý~òBl–Yf æ ßsÏ=%º?bÄÌXxá…¿øâ‹âÌ@8*P„ºâ´´” Ôò-à»ß¾¥yŽ Ð ‰MÒ±Þ ©+‘¯|èp#uÓM71G€†S_ùæoÐ÷Ès5€ûÍ7ßiƒË‹üwGù*îÊ|¢Ùf›í /lY¸Þ2ò«8î¸ãŽü}äÈ21²* '¸â¸YÄ2‘ý†¯?QÅú¦Êâ:4I¼+sø÷øãÓ–]tÑ_y‰õ×_:±Ì0|øðM2£2k‚¡—‹g¶óœsÎä AËÂõ.@3¤1‚ ³Þž¦ó®D¾*½«BŒ-³äÍyA|囿!÷›ýt`¶ðà—¾ 8ð·Ô\W¾Š‡[ÂWðùÿøÇø’µ¿›‘¯@rÉ%—IÚÝu×]W{¸Œƒ8+<ûéz^ñ•ñ«”š¡t5Ó!Y„Íeÿ GqDŽUH&nJZ´ÔŠ=G}4–ôë×ÏÁ€(BJí-,¢¤,Eƒ%w>-Â6Ç2iŒà Sa#a/‹¯Êíd7BÁƒaùú…uïf¿â›¿‰?ì°ÃbŠÕþV¾¢—ÂFÞÛ¬¾úêY6E¨(θŒãd EŠž¡W|Ubh†ÀÈáOÿªÌ៟?5_‘Ø›m¶rð ÞcЮñ-—ç,à À’ÈYßø*‘ñ9–/GÀПþUiÿ—_~ æŸþ6_ósРAàÀñä“Oæ˜j)Dm¾ù昱ß~û¥¨›º êPŠêÔr©ø?Å`†G.+*„ÆH“šgE](ÈìRøªÄŽu3o¹åÒƒSŠXžÊC›á½hG_6­!ñ–¸¹[´¿Í¼P¾j†ŒðÕ|óÍ'ë9û3߬™Á]OÍW<ð€¬Ù†:-ˆèP@þÀ8‰âå_%2>—Â4@ £1 †žô¯Jþýîw¿Ž_üâ¹€[Q!/½ô¯¿ÀaŸ}ö)׳û¤I“\Z‚:™\Ž.õ6ê"‚p”Æ»ís…& 4ÏöqÙÆS÷|UV—:?üPžœwÞy1ÅÒÝòÐßtŽH­¢Ý!´SÂAP²Ø™WÝ¢ýmf§òU$2†¯?üpò¤=×sNÇWÌ:餓̎mï¿ÿ~$Âmx(Ìî‘@”hª”o|å8|²f8Ñ+¾*mø·É&›ÀJ–qæDê–^zi@èÒ¥KŠÕ‰Åf‡Ê=z`ɹçž_²ˆ»(E5”»-! XBPŠp³*2eIšgU vc§c¾*«3æöÛoOÙxãsç+?ýG£åÝB"‚p”––¸)P¨¿Í\P¾ŠDÆðÕèÑ£I’VX!²X½/¦à«,#œzƒ)Þ¥{ÈW.ãE¤Ò½â«r†Ó¦M›cŽ9x¾Â —1ðJ×\@BpÜ{ï½åvöÙgcFY03øÄŒrq Ó2C;/“M“¤aÒÞXguÊumâĉ¥O¿4SO1¦\4™IhÒmtS®ñyi§a4/U—ã’¯Üw m¢Cs¿÷]t‘Myû2~úkoË’…:H8hª„ƾ*ÔßH´•¯B°„øJÖ8ùä“CÅjü3)_±º ûqÓ”8ÈVÝ5'×€ 1 ³\ÇC¾Êx!4=€2ëJøÃWå ÿ=ôPà`öp³½®8pÉ®h³+†Ÿ|Ut€hzdÍPùÃWå ÿ–Yfà3fLѸû)ŸupŸ£ô²Ÿxâ‰D[/|ðÁ·Þzëi§Æ8m½õÖ[~ùåX`™Ç+3pøÉEnQ€b¦ŠM̶˜dS¾¸2ò‘.Ñ)bmÃâÌÎQ²,§¾ì²Ëæ(³Ò¢Üð•ãN³}DX i–Yfá±H¾óÇøÛŒ¯ =ˆ+5_Ù£'%‹s– :a^­YUœ¿È+_…0 ñÕ×_-“„ßzë­PÉZþLÄW#GŽp–Zj©Òû‘|Å\$á+Nœñ•}bÐÑ_FÀlYÑO¾jivê4:‡f(Bü᫆¤8pÌ3Ï<ù>HN÷ ¥/vÒ·o_,2dH “'OþÛßþvà.·ÜrNqP‘êATŒ"Ì@8&•ûø œXB˜b¬­ñ-f¨Ë|`Ë¡{¡À57|å²»œ4^²ùU¾»Ìç¯3¾J cq.ËÎf„)©I…–/ÎßÙÊWA@"ùj÷Ýwç/Ú™gž,Y×sK¾b;ø}÷ÝWú3lM>eÊ”Rñ–¯ìÑ:ÙÛ0`ãëúÉWñ6§¾K£ ‘à_•0ücm»´ ¸6OJLÆq2bÄÌXxá…Ù—³Q>£ ›o¾y»í¶›m¶Ù(&£‚ 6Øà ƒ:ÿüóï¾ûî^x LdÛz pÂO.r‹£° $¤:¢ˆØÈq/f` %1¬Ñ—WL°\êõG—,×¶îုœu”ƒ~YžËÌ1ˆ`¤e•–ÅŠð׆¯yäYâ˜R´q~r1_µt³±@Ž£±òÄÊÌ/jT]Ê•‚ümôEùÊ`ÉWüÍ%ÛÛaµK¾zþùç{öì &:t¸ì²Ë zÎNZòÕ7Þ(Y‘ 88áà"·Rô¯Šv  xcÔyËW16§¾%_úу<᫆2-û7¿ùMŽö9çõ=-¤ô7Kf¬uÍ5ׄÀç£Þã?^– ÃTfm¸á†gœq;WòÜ"Tø°Ã£ÌFÓNø*@*R!ˆ¢ ÂQÑø5Æp·Ùˆ4$¹ÐŸò†–`ªÅ[á4OASõÖBg†ÍWκÈé“¿^guVºêµr÷×’¯BzC?‰øÊÔ²?i¦×^BdIDƒõ°_¿!”¯ ‘|5uêTºæÌ!œ0a‚)Y˾²¨‘ _ÑeZrÉ%iÑ<=¿îºëÄ Näa:·( ‹æ«Dت½å«Dþ¶,Ls£ÑÑôh€ÁžðU ÿÕW_´¾ÿþûƒp´É¹Ùìáé§Ÿ.×eù­_¿~Á™–$ëàÁƒ;vìH€8VYe•sÎ9'f[aÞõñÍ ƒ:>“ãà„Ÿ\l梈X‘"Ôÿ a &q·ô¯" IÈš¹Sãë4Oܧ©ÖØGK× å+7cKO‹ñí.i°ÐB åµ H¾þÚóU¤ÞÈ‹AZòU°°ýyK½ö¢LID˜!3=9)ÂßkÊWf|µãŽ;’loJÖï¤%_±"h¢iŠùBdÃWtè#Éz*„2´ž ?%¾ X°ó†©ñU"BSj›­Hì3_%ò7¾0ÍFGÓ ó„¯\ÿHVà†CèÔò'mu®¹æ" P®ƒf•±cÇŠ%LEàÝœsΉy[l±…ÍÂ<£°YÐV˜æbKï.u©ŽRT›é ˜Äó¦‰bdK9… L˜GÈB$[¨RO„Ó<¥¶œÇï‰Á™Q(_9èg…´_uÕUiL4Ê"ÇÔÍÑßD|£7æ–1›“¾ ³?·Ôk/’„‰`2ùªƒà(_ 1|Å»#ÒcÝu× âV§ó–|ùJÍ –|E3‘‰kùFî¦ÀEnQ€ƒÂTit!w¾jT%òEe¨ŠÏ|25õOša2ïoOøÊõðï` ÚçdèСøÎ ™1ïÓÜ ±ùæ›c éŠ:‚²Â +p…cûí·æ™gl̸ï¾û(Ïl³w'29[6P„ºéjgÀÌZòA6FÚ)® a"X˜GàŠÓâ­dY ÛÅ[; 5¬8¾*ºCœ–;äïÖ­›Y²,‹ÌýMÄW-õ¶,`¼nÆW¦@¢{½–b Á"dβŠËb¹û2^ù @bøêÓO?•©:üqºzüŒá+F†1¯ÔŠvß’¯(f¿—zp‡z*Fº/_Eªˆ¹ÿ¢’ŠžóUŒk–·hh2!ަ×Xžr=ü;õÔSùûԆߑë¬3Žï¬}Ò˜ .¯˜=Ö­ñ$I>À0¾v³Ÿ‘ËŒsÙÌ$´˜˜,sÄ- X:5jÔ(ùÐN“Ì0S-…TŒ`aË¥\‘‰•Ä Á$¿b â«¢»Â¹`»þúë“üt›²KËËߤ|e©×²˜àÐÈW©ñI¤×F Á"dΦ°û2¹ûtAù 4âùJfÜ”²ÒI0R7ã+²Î¼R³ß <#-ùŠÎ{ÁɳfÞ½ÿþû6Ú)&/—¨Hõf=®ùÊÆª`Üp‰#òE¥ç|ô%Å9 Ǜ͆ó¯\ÿ¤¶áŠ‚‡r©@Cßü E’%ªÂ$YûîÜsÏ}óÍ7e93-Ù£¶j/J2›OCã"~Ê'ˉþÆ d‰QLÂ0Ì.L5“Bím˱$ÁR&|9Š­„(YAŽ[ k 2²¾*´œãÆ£ò2?ûzèyù›”¯éMT¸‘¯RÞHoK-Kæ_¾–…K)¯¿A”¯@#ž¯®¼òJ5«q«Çy3¾zðÁí_©å …%_YŽâ"m³7æÈW‘fÄ_ ¾¨$ÁÂþóUÐڤ粼'.²¢|åzø'+ûÓ0"©ëE¦ò%Ì»Ã;”ëãÙgŸ-êÛo¿]Ö gËNó ¥m4Ú®]»"çúë¯o¬"iM¤GÌ¡#†1‘C†©ܨÂåB†§„¯ô)».½FÇi°Žõz¥.w¾*®û›/n»í¶Ñ?ꨣ2ŠÍË_öfHÄW)ô&­ä«,;.$ÕBFà_|±ïæë¯qDù (âù äyšÉîáŸþ¹Á­'|e94*È}K¾ŠÙf9ÄÍ‹¯ì 3%c†¸þó•ñ"Ñ MŒ†Fs£ÑEVô¯œÿ˜ Ë_¦N:yømzd„òº¸Ë.»à8Ç;3—Ìr&Nœ(φYWS––âÓ»fK3ÅÈ?öØcñ¥ÿþÍÊp‹kV ÙuŒ‘¯1OÖ&Å`ÌnVÞÁõ·ß~_8¢uþ¨ ‘ÒTq¼®ßŠ´„:w¾*¨ãÛÒ‘¤>ùäùÓ•‘¯òò÷Ïþs"¾J­7iÅ _adRœMù¤zMÅÆøJºø±ñ®'Wrô×x¤|eÃWl¿¥_}õÕ·œ4òUÌxÿ6|Å931ràÀ›`%²“êù©›2à 1\óâ«D¶Iáf£ñJðU ibÄ‚æÖ¬®|åtø'ß%³²3DjyýÉ'Ÿd)KRauÖ)×Á½öÚ 3zõê%ö¤Û\‡t ‘óÎ[ …S¸,?$`*cv !9V!p˜=„2G±þ‹’M8š}Yî¿ý-Ì—¯Šèòft°YuY®zÓM7mVÀæz^þžwÞy4= %_eÔ›¢ºá+LµA&²L ½‘r¸Hà€Ëó%þsô×à |EÜãûW_|1e¶ÞzkZ NB|•Ë+µÔ°ØðË¢ÈÊ<Õb~S.¯C‚(yL†ðÐŽAwrá« @ûóÈ••à+{¥$MŒ†Fs‹©X:_9þI+=à€b©ß-yF*Üxã%z÷ÄOЋ’×9áãºtÆÈöA{ì±G|u àrã†'ñµÌ]Ì“>s‚ñæ–û‡/1/<Ý[å@#M¯=ïG‡CŽ|UDg·8ÇW\qEâž…¯òò—¾–ØóU.zS1|åÃPøŠ —!¹HNs¼^å+K|ÿê½÷Þ£ ;îæµ“g|DÜÜ5|•ï+µÆÛðÕÈ‘#eZ ßÝäÞ±A b 1*PÔÌ…\øª™ðøë/*«ÂWñ~ïÒ¸d÷lš[ðzè¼t¾r:ü“ý/ºè¢ 5þ)›íÐX`ï¾û®,Oy2Ô·o_Ìà /uÕUW¥³ä‘GA;õ1¹"^dAªÄ—lv#eˆFŒÏå Y3]ñ× áû »¨-\âëVú.M—Í!•ö%…ñyñUîÝܾØWáÕ=A_pÁSóU^þ2‡ à°ä«¼ô‚U QÂWX[ú,PGøbÌ û|(´d œcìQ¾"è-ûWk¯½6ÅøD?É Ý2|õòË/›Wj,)é¾ÃÐ’¯Ø•Qþ¦€?»Ï']Á2(ˆµÙÚ>¾²4)TŒÐ ó¢’ÀU…¯BŽ4û)ë_ÐКë¥ó•Óáßk¬AÞ?üðÃñ Ôæ.ϺwïŽË‡vX‰~1bº?ý“ú½VV =å”Sl|¡ꨒšˆ1õ'‹§¸`£´ 2„OÌ  uznM¯i¶ñÅêz7¾Ê·ƒëê£>š §æ«¼üe1éXòU^z Â) _avé+Á_J㎷')pnæ‹ò-·eÿjøðáÛyç›ÁX­ëÂW¬²(¯ÔX=ŽíÝݻВ¯žþùž={‚|‡­‹žÎT u(Eu¤\ø*R²ÍEó¢’õd‘ÌJð•k4.§¡Å.¯Ü ÿ~øáy¹bà‘Y,lãN^z#u¥.kãŽDÊ´¹˜BoP¬á«þóŸÁëÞžgô×ø¥|e ˆ9ùÙÏ~F3g‰”˜2•¸%|…/sÌ1‡ƒWj‘˜ÄóUp„ÃÌOËG瑊R\D]pºiäØ8¾Ja›©BàX& rT…¯Œñ'4+¡‰5Þj¼R._¹þÝy瀲Á4BPË+»îº+þ.¿üòü{ÄG”å#½XÙ¸œq‘ßÒ°“O>G’Îä¤"=9ª[*j,†ÙòGJ‡DP‚Ûhg-¯Ð`ñšÆ[KïbœÊÈWyugc,,âÖ!CÒñUŽþÊÄE0°á«õ6Ã3© Ì–ÕRO K’ê Ù/|E@C×½ý™Ñ_ñKùÊ&¾Ã† £™ï½÷Þ6…}.#|…/1Ëœm _1 •ɨ˜ÇüF–(Ú’fòQ-;~EΌ͋¯ši·¹Nø@‰£B|ÕÌ/šŽÐÄš^/—¯Ü ÿ.¸à@ÙgŸ}‚Î×õœ‡R¬À)™í}ï½÷–åiŸ>}€S–eÌ›Ìë¸Ø¯ÆË wbŠz‹ ‚$%¬—ªÎá4X¼¦ñzb33²ðU.Ygž­ºêª„;)_åè¯l¶1Ûl³ÙðUŽzƒ 4ž'U„ñ¸’7MIª7h¹ð ^ôü<‹¿âšò•Mˆ™Hrò=EÕgõ _á‹å,qp•iÆW<ø6«›ðBOSM”æÅWF¦å Â(s+ÇWA7iPøBã ^Œ9/—¯Ü ÿVZi%pI· \ |¾Ýš:uªŒ ˆë^{í…ËçŸ~)Fžxâ‰hçȲ 9–˶õ2¹â©§žb«JY×H„ÇüKïŠÂTAˆ¼ÜϸmúÝwß-êp­H %Vi´šp—b‰3¥²„ ×™FO¥à«,ÝV¼NÊWùú;aÂÞ®Ó¾î¿ÿþx4ìõ _AD14enùª™öª‘ O‚q ך ´¼žH¯‘iøÊ\©ÊI:ñNùÊ2Ä4 2?Ý"O–*Š.føJV7q<³¬‘¯¾ÿþû“N:‰qÀ®»îº-÷FnÄ'w¾jTÁ Ã<ŒÄT Æl)–#_EêmvQÞ€Äêò•¸Æt0Y P:½Íü ^/—¯Ü ÿdÌ¢IÁòÿ|èС´+™¸ì²Ërþä“Oº7{âĉòñIË­'ãm“ý+yíöØc™ù 8Eç˜Y ýë_[J§‹œð“‹¬: ½g.r 8êòî.ãn³²e-®á`¼åEÜ%”¸CXÍü^Â]„"dÒ`q™ÆëIn,IÊW©;¬nܱђˆ¯r÷wðàÁdZËÕ>-õ>ûì³|õ‡?üa饗FËrË-'Îò“‹|EõfˆY ÕeU=\k&Íþz"½"Öð•½J¦ð㕯ì#(«ò§Ù¾ŠW% _Ý~ûí4êÞ½{»4/ÄWÍÆT–&5ò_…Éë8\ã1ú Aƒ8áà"‹‹„úW1|i@³‘jŽ|©7ò"Ã/‚Xi¾Â5šŽÐ¬"ÝŒ¼X._9þ±c¸0°ÞŸK½õÖ[f]þzárÇŽÍÕÈðtq—]vA;Ç3Ï<“Z_ȺUn¸!s¥6×\sÁz/¼ð‚‘¹Ã;pÍ[dÿ~š[£0U(@u„p‚À,_BâB8pÐ(rvB( (Ú ®YÝçí·ßvf€{E4Xš-.—¸é¢{¯“òUº®ª{¿b4&â«Üýý裤eÅó•Þ¯¾úŠ'S|ªú 2!¾B¢"k¬YŒ‹ÂW¸†ƒÍÊØ_·×+2ƒ|e¯ÅŸ’IýÅrå+ûðÉŽP¹<›°WšWIrƒ¿J´,’œ…ìùîìoqˆ¯šÍ¨´q6’¯ø®DÖêd†‘™½Å‰Ì,ãìùª™óTóå«fzƒ× q$|±ê|%OíOV._9þ½øâ‹Ä˜§5ÁÀ×ï\ÆB²ÆÉÍ7ߌˬëêÞÍ'žxÕ믿~ígžy&BÐò/Ý©Ã?ü“O> ¼ï¾û¸¿7ýƒe¨BEé‰(Ä $=Ç)”ràfÒºÙËËB½Q²"NpÄ›]¾‡ä!+MØCÛ 2)_¥è¤dv±ö|U„¿Ç<-:þ³C½l ³Ê*« *ÄW‘u#/ù QÍvš‰¬‰¿|€‡ƒ‘w“^´×+’ƒ|•T—å“ú‹ÍÊW–“—Ë,³Œey¯Š…øj§v¢ÕŸwÞynŒ4|Å's<'B5³œ’>åiä+øGº‘ä$Ôãj¼kÉWÍ`Á`Ìžnþÿ¯R“/_5Ók®2´>¹Ri¾¢)áKÒ×é%ò•£áŸ,;`ÀõúŒ=šØ3’÷¹xÇò'ü<î¸ã{ÊãYwíY6'˜4i’ çÃ\©ÆÙ̬¿"ë5ýö·¿9¡0—f¡:B¸ËX„§†§DN)koP´\ì'Ð2ñ•ЧvÇÿŠ4[\θ„½ÿn-´ç«ÝÓ "Î-ùª¡ Y †¯lô²Ë–,⫘ºÍn¾B b#ÃÔ¬n¨°ð6²b¨¤åOK½"-ÈW–ò}+–È_ŒW¾²Œ ‹ÔËãÆ³¬âO±_ñÉ ¤ÖYg¾ºþúëW[m5ôFî¦ÐÒ’F¾ ¾ßcÁf¸z7hÃWͤÑc4{TàNáQŽ|ÕL¯\'d¨#|ò³º|E#ÂTÒÍTJä+Gÿ¿üå/@“zç€øòá.!—Ìæûãm·Ý—ÝïFʬôr¬¸âŠYaO‘ÃӠȉfÙ+Ó­á$fñ(„ȃ%Ä"<‹m¸&¶¹_{Sö'%¸b¿|µLè“¶ù,î;®+ûvЄë-Q%_%혖èQKÕ6|U¿ò ŸWmÍŒ´ÑK_J晇øªeÝf _!6ãP^HÊ”f>&ºÞÌæF!!¾j,P‰+öþâŽò•}L:è þ’æõjÚ^oö’!¾úüóÏùHµLH•ìÂã%_uïÞ]fœFî¥/»!¾b ([ ±Ä:á`DÄgDñ( 'ªP‘ê6|#“¹TK-µÚXâ'9òU3½‹8Â'eªËWòB˜ÕÌÙf×Kä+GÃ?YÒçCiAÕ¯_~ùå4šÍ´iÓÄyl¹ùc^îC /¼0–pðD'µXù–![n¹åwß}×(‡Éž‘S=ƒÓAk! Ó­ûéK߯–WpM„à,.[ÖÊ¥˜l˜k&ÌnáJ ù ¡Ù‚6MØCÛ 2Ɇ¯uI ²3G±-ùª8·Ûn;¬_Ùèe•¼÷Ûm·Ý‚|eS ›C± áYf _áfŽñjfsHEˆ¯Bw+ôÓÒ_'˜‹Ü’½,¹\Ö)$V&—¦^vLÆ™¸,C_g½+~â;B[¸“nì’æÏOš-þÒ„ý1©hKù*¦»Y´1ä7ò•™(Njù*‘Þ8€êæALc]úRÌð¤ tÄ:æLìäœ),Ì-á+ ÇŒ¥Ó‰Òøˆ%sn _á²¹’ïI3½Íø*_íî¥5óWù*i,d«7& &­XVùF¾2–ȳž‚Z™á+ù`rذaFoË<Ò¿‚‚xä¹µƒá+>¥¡o2ÍtNd“®KÃW!íf[J¢4Ñ`§¨…ƒ…ò• I°B–ó³r|EÃ1Q£/6WJä+GÃ?yÔêx‰è³”¹è¢‹ˆúÒK/ýõ×_‡älºé¦ÜºóÎ;C׋øÉ{'yØÃHiº)öÁ03áû¦›nŠ´S^Rãû­Ë’PŒs¤ÛÄ ‡F|NÃŒsy$F³û<#¢i¨Ž,Qö,:í´ÓÌÄ×Ð;Øøê©ïVl&Ä! ¤ÉÀ-#t«Ò?i¶8E®´‰ŒñU³Žf"™>ñ•3C|•H/kÜÉr!œÆºtz¤/HgëÕW_ Žý$Á*×€T‘ÞRð.çT‘çâ;wFu|4ëRÞðU|Ý,w#õ6ã«,Š<©é¯òUÒè˜Mó&Mš”´n)åC|´þ­öÚk¯àżΠ_ÝvÛmhùÙÏ~f/Ù¬ÃR‘[;@;+¯¼2bå`]ƒÈ™D\”ÅQ¤U"Gw¨0k"$ZÁ§Ì¾\…òB Sî¬_Ñdxܺ°Ñ£ø+%ò•£áߤì‚Þ#ǃ[Ð]†72 rsLév„>h)È’½÷Þlå«3iTÓ©mTË„·.]ºDf~©|·×f9`óêOÞwñêKx ( DTlö%!ꨒnb¡Lô¢—/0"Òø|/Êg<‘®HÜ B”¯êR¤ÉŒšp)ÚKQä«È.f)V§4ÈW.ý òUR½L1 ­ÉKéȺô-(Àó©È±Ÿ€¬H1y˜e:%Á»œSE^ Ëćøp4Ö òU|Ý,wõÆðUEžÔmô·Íù*]\¶ÙfKUÖfòUÈ_Y‚Ÿ?ÁEÌÁ1|Å£^™»È"4!šý”þU³?£t6ÙdBÀ1çœsžyæ™ÍºLÈç(&婨¬°Ì}°ï_Éš:¸†ƒÅñ¡‘Îsäv#Õâ+Y·“æÓ,î-¯—ÈWކò˜Ö Z"âVÚ¥ím°Á‘¦ÊÖvo¼ñFäÝ/>ñÄÌð6›¬·ÞzXÅPIUÈGŒÔm6µ‰©çÜå5j”7¯þ˜ôÌ'Â,?ñö2æý!…§Wšê‘& 4H D>芬b.â&uq™+}ø)ë]ñzPßPÑü© —ꨶ h¨n¯lª§.ÒÏW©µøS1äo;óUê \sÍ5|³þLj±U4|)_^Ž¥Ø€;RZð¢á+.ÊþÆçw^°@̹L.€d"ËÜpà àÏ 3Û“’Ó¦McÏù矟©U¼bbFËÞÈìЉ=Ô¢z¤X™ò€êÈ»qi²Ÿsq|EhÐB˜ àJµøJú{4ŸH_l.–ÈWކ²´ÔÔ©Smàð¿ÌK/½$›ÍZ”]à‹žJñã?öíÛ—†tÜqÇ hB|‘ÏTbPe 'Oµev®–‡h\q·wïÞæº¼ú“ëÜ ð”™ÚNnQ,HjFˆì™N Hú¸Næbä°ùEœVâHá¤c`Òƒ$‰,P¹‹4[ü¥ WÎòÔ _‘Ã3SËô¹¢ð ʱ¿ÂW>úh ½ò¾âøCd]™™O¿'x—OkÌç7¤´9¸( úQXziÁ× ÁÑê¨eÿ¬7X—5Ù©køªÐ|êžlÆW…šáLxÐ_š-8·!_eé_±²³x&Â~΢–ZQ|ÿjèС$ûN¥–߬b°Åf0hYwÝu›]—Oéxä9WSž@WÚ¤;Ä`,²µÊ*«È'ʨâ¢zH?Q$«Å?®n,¼‚;8…k\ õ¯‚Å2žËÎa„)RN|ÿ*²JYi,4ŽY{,…%%ö¯ ÿUßȥˇ*2õÔâ[yˆû»ßý.Æé>&Í#°ñÖ1ÇCÃÍuL¡÷ØcENÿþý…hä‘’Q‡¿BC"Y^3â—¬‘@ÅÆØœÀõ\9Tá\ª#*ô2긋j àcŒê–'˜!’MILfÃbƒ¹˜ûI£ÞF¤¶‘*!KúTñ…&ì¿©yYˆ¿„e^2}–#üÌ<"ÇþfÑ+”ÒÌf¹ MøÞozTú‡d|H®0fðÀ[&5P½1^ÜÞ‹¼ÛX>x…º2MËq;*KoÐw—çÆ_¢ìRo¹º$3ö¯>üðCæòx"Ë$R78HëŽé_ñí= `>ŠÉ˪Þ?þñh±ÜZ]úWH`¬nHÉ& 6f̹(ã:y¼ÎY2 •q¾,tÇw˦ Õ(9A…Å`T‡îFþÄDá”ܵéçDʉ¿(éÄ,ŽPÞx«RÜ¥™ÐXhz4œÕM•ûW3MOýÇ>)yå•Wèʪ¸¶Õò.÷úë¯3À ó.¼ðB¡þtøLBäÈ'¿BØÆ×w Û‚oùþüç?Ëbµ¦<Ý)ù9|øpY ¿Äò"שŽD!ÐÔ ž`Ž`Œ|¹¼enAÙW̽$éA’*ò RîòU  hZTEâc¼Ëi¶ žÈù»ï¾Ë‰œGš7²Š¹#Ö”)â¤,½Eøb#3KŒlä׸ ã >|e*šìÉViO·Ûn;ì7;KäËV[mÅøŠõí˜j©‚‰‚ oä»Á`ÓNeFn1Šel@/BV˜”¡Ñá‡Î-¾Í3›I˜êF&*(,zÍŘ\ÀÜÁ©˜bÙoIP$@Ù¥•(fBcákI¨—hIzÕf ZèI.“ µÐF¸™Ô×l#;#$4IÀ\ÏëD^ëï»ï¾!IõÊ.¢{ì±rdrBpò'‹µÈLÚ”‡OŒßàó¼YÚ =q—M dò'Õe)ƒkÀÈäO™œ€T‰üD0ä¦ül6IX9Å"RNÒ‹Íô†äÈ6‰ñ“„CUüüÉ Lðl|¾è§µ¹X%Mñ:rba.*¼’efG²èM:ùS¨Œ˜™ü)϶˜R¥“?³D³ôº¼ñ0¤ËQº=Î È«%kýó©¿3ËÓ)jÙÏ¡—Âj™´tÆHéTDÖjÔ;`À´\wÝu‘僥uÅWP~5ÖÞâ\úWÜâDÞâFò•‘!‚µBQAaQg3ù(;FŽe?Ç”·9‘åd jV¾½Íte¹.+bÐd²¡n‰ý+G$äÏ|ä’G±sYMh!›m¶YK¥ñŸ&·¬_ f—óà§ÉñB¸++ÑÑé‘U¤äÓäàÒ/28”ñ˜ 8™å,_ý14»ºƒIèøûßÿÎéCË*O2 óžyæJʧɘ!“Ú1¬¥ñàí"u—R`ó÷¹çž›[e#'iûO“IÌh¶—FR½e•§ÙâM¸,Üë¾’Ñ#@á+™æíÒ_á+Þ““`IõJtì—~a®ZdÕ–K™+Èa-N¤o§K¿¸on¹h4c?IŒ6ä«ìý+Ùö%F2®"“K@c„Øô¯öÙgõ©§ž#'é­ÆþóŒÐbóÌZÒ’ºä‘Shapé_É#-™ÿ)|…ðÐ1xð`é_õìÙ“[R%ô°áÜB‘|h‡ê–žJß wLÉfý+S Å áÀ0BS×¾#¤è[4Y‰Ç~ãÇf&•Ø¿r4ü“'þÏ)o!®'ZÐ_>ä¥ýÄLw‹GÒ[bŽe£éÀÙ<ñâ “ìzÊ)§ˆYHÝ,KzY'“ÙäD^ý1äËcZ2´Ð\‘ÀÒ·ãÆ»ñƇ &ì,c0éoÉBêÁ׉¢T§2 ©c 21L€5:¼òìyà/Sýÿ»¸¶k¹®¸ö²o¥ÜdÿÚwÒ‚¾)%‘¬?•ˆÊ–J ©$d‰„/".!ŠH×¾%÷k»r].®ïïÑ}}ï4ïyÏ{–33gæý|ÿñùÌ{æ,¯×ã¼æùž3s滓„ËѶ ÊT¯Þ™,&¾l³dà!±ôa~mpb⦀˜^iᇪù›^q»ÇÚäêæ]øÁ¦~Yf™e¬RôûS¼øÇÝ"vò•hᇪ½Šò]ßÔÂeÛ]]·ro™¢ªË›åúʦè[k­µ<šÑ{}õ÷¿ÿÝ`îº}ow}µÏ>û%¶ì]_ÙÓ<:xØl×WÿR¥ÿ\{íµöñFmÄË]¨†¼¶ôÅ®¯ú¡Àx›~w\š”ë+—&ïÍÍéSâe¿¾Ê[»Çô6„•“¥|™ ^_ÕÔý³þ€[K·<²úK°åy«-KÕÌÃK”W±ìûI'DÉý:6ÑeIÓí<çœs(‡ö^}õUKé–Qæ+½&S:î‹ÛQÚÎÍ’‡â0èü›ßü¦¯àÅb»qN‰2º¦·ín³Ø2Êcïb^ºýMY–Ôu’Á5°œ¼ ì­Bš8KFsàÉ’8Ì4¶,iÞÙÃô%£UN¯¢—•lgÌÞºdN¯jö×éUz^%æuzå–}ç¸-úGgoß}÷e€:«sÝõýHÐÉeßséUëBƒc0ÌzU¾ùøYçGœ¥ÆËU] N¯RªàùŒuiÊ?Ÿqµ8½r{ØX{íµ!Æ›`ѽÛN¯ÆOú%—\2zÛ釸¾â³âÙzìqú„vÿCvcCÑë+ê%/…³ŸŠb×W½VÙ{ÛG¢ R®¯¢É²oÛ³e%ýÙr+ôŠœ,ÙÝï—²A½ª©ûgÓ¬Mœ8±‚À÷ÛÓv†PÓTYLµkÞç8¬qÏ=÷$(GNìeÝxãäeùuŒuÒÌåo|ã$Èò¡¹&zà 7Ø¥´XÇÒ–›§j— ·_êÅ6ìE\Ží·¯)CdÓgßi*z‡ï'–@ÀØ B(1Aø;9miôÞ©ð÷¼°…Q½Š]\.3äŒQ½ªÓߨ^å­7ªW‰yM¯¸Zz衇ìé"wÍ—}'¼9D2»´rzÕ[l¯^õkÖÞ¼ézÕ¯œ¼û{ëÍ¥Wy«k<}¯¿C®W%[ÄÞÕ3fLÈËtEõ*Åß~ô£œÚÌ*—’&ס¨^¹Œ6¬`¯½ör{7œ^¹îYô²‡,¦W6(ÝÆò: ëûõ^q±ó7¿ù û-±Ó+«×*²îeF½ÂxJѨåÞõÊæ¤Q¢µôn‡¯Wœœ ãdéµ?ïžõª¦îŸÍ¦Ít‘yÑ„ž{Ö9ãŒ32Úc.]uÕUÓgLfg)ÓþöKÏÀK‚’i'û%°ý‰c,¹Û©üšn@IDATdãȹ)nƒßbËô1BÀd—g}›o¾ùç?ÿyÆX2ÁÔ¡‡Ê}#nêðaÀ:7¨(Šn £"Kl\„VJEXK¥¤w#ŽV‹•êÅ6lòÕ˜fEÓØüÈu9š%Ë6ÍŠÙÙ×ú$lHO¥ßôÊRu#il–WNáFjo¤Ò˜^õ^b6bUu•Æôª6cz•«^§W6Ö½7¯Ó+„Èõyćb Sˆ*gÃØI‚˜^õh£¡bz•Ø(½yI6P¯‹Êµ3±Þ¼z•«Æf'ú+½*Ù(«­¶?X<ü)YNuÙczÕ¯"›`“¹û%È»?¦W–éÍÁÅMÞ”éLHÕ«£Ž:Š,±©ûœ^qÈ]‘‹ *Ó«èõ•‰ ){¯¯lê;ªÈ¨W˜mw¨mžvÇÄ»^™ÀÒ(®ŠÄðõÊž‹rš$ÚŸwgƒzUS÷ÏnBD§É˨Áô,_ÎiÆmãôÓ;ján»íF–,#£¹Ò·ÿô§?1c#-SzÑö?}¨aÊ +6·,O|›™¼68“Ûä±{WQã9d÷ÑIL–è!Û¶÷Œ­¢Þ™QbsÒôf·=67eРÀ4Ðõ+¤À~{>IgÌKØØ3)c– ’qÚ̾Ä.(×úÓ«W‰šý²·n¯^Õão¯^åª×ôÊ^•yoÞ¨^¹Q ö†a41‡bz=ʶ5(q"ôêU¬¹ó’f ^ÅÊÉûµ_½yõ*o½M¥ïç¯ôªd‹ØÝaH—,§ºì½z•X‹Ì6ÛlÌëëNáÄdÙwöê•åµé~ÿûß§åôʱ2h3vg½²ÙeЙ,Ç®¯(Ð^Ò¡ŠŒz…ÙÔ… 1ãýêM@CÐ4J¬¢Ø×ðõŠSbŸOÄüê÷µA½ª©û·îºëÂë–[né‡ Øý¬e3üÄ…¥l'ž—‘ÁVÏÄlAóô•Lí92s®¤ôT÷ØcÚ"±ww×]wqˆ3pF—gˆ:‹ÜØÓ9’m²É& òDhXÛ|eçŒ2þu_*¦M® ·I>II¥n¿Û°þ!¦º=± ´©ep9v(úÕÖ£ûËlÛÛî¢3KQöÀ“@*¹Bh–º¼§á´¥™8…½—l‰zÕïr3X/²–¨W5ø›¨WÙë5½â¦8óN™³½y£zÅíg›Ø“Ξ=àå«Ý“&Â^õBáTAEýôÊ¡NÌËÑŒzåÊɻѯ^Ê) Wyk¯?}Š¿Ò«’ÍÁ,wÄ9«™MŸ>½dQeOԫĺ¶Þzk|9óÌ3æÝ™¨WrÄGP ã¡Ò Œê•-ÌàfÚs¹4²‘ê˜þ!YïEšÍõBáõŠz1›ŠpÁÙÀ†w½¢ ¨…æˆÖ’¸¸^qRØBœ&‰öçÝÙ ^ÕÔý³ð.——\ ém¼¥›š2c6kÓg>ó™Œé&³å¸X¡—•žØ©õ{íÐ!ræ±ÇK,Ç^N×G—Zj©D30#SÜÙqœMÌîv‚Ë.øèv–Ü Y!@ç*gûí·'—÷‘¨¹l(–Ø0̲ÞI±òÌÕO¯R.:ô"»Iýôªõ*{½ÖRÌãâœíÍËq^±›â‰ÚÅ!ØøÞìV²­eºÕV[¹Šz7úå%eF½ê-3Ëž”zÉ^L¯²ÔÛTšt¥WåÛÅÞvIàS¾Š2%ôÓ«Þ2Ï:ë,Ny?^‰zÅrÔÂ\,ï2;½2b 6˜ý¹ô*æ²- AáYôмlSÈÄFHy×+sœuc÷~ \¯ìÕJN^Ë‹íiP¯jêþ}üãçôøå/Y PS¹þüç?óÀ:}¼e¢m¿ýíoñ7ûëa‰…¸®Ãcq·³ßÆþûïOÕýÆ®·Þz=ì°Ãúe·'Ѥ©çCuý,ÁHlÀàÄ6"gFwÚX‚,=çh®”m{ñ€&NIÓ{ÈF¢NUïÑ÷pÚÒœÂ!é×¶½J¿ôôkFm¥¥èUÕþöÓ«Œõ" ¬ßÀ':ˆ 1/o#3=:—GÌëcYØà+;Ý„^‰i ·,)zÕ/¯5bv½ÊÛèéõRZ1½ÊkFméú+½*ßöã{à–/ªŠRô*VÑ™Ë%K¥ÇûÚO¯lúéÅ:½bâ&nჅ‰Yœ^Q2öóa#¦W±ŒeeRø@½²¼Öq¥äXQ~õ øØIýœÖ¸^qRp9”r õ%ËvƒzUS÷Ï:ô±ÅI² i6u–X*¯÷Þ{/!Âòy3&¦7-Î8‚‘%¨š˜½EÙ!ÖÌI—Â/~ñ‹”À]®O{«È²‡bí¥¤ÇH[Þ³{“ÙÓÄC±Änܬ¯3Ö–Ä ‰c üJ ¶_‡v`ö¦pÚb6§pSÔ_oº^ñfãsì-²úÍó^cº^UêoŠ^e¬7Q¯2æ’ì—%‹^õËëÊÏ®W.K–õRHa½Êb@Íi²ø+½*ß(Ö+`(Pù¢ª(!]¯b5n°Áü~wÞy±ýžöÓ+B™þnŽÕèôÊú9<©+fIo.{¢H±Y®¯,»½Ó;lÕ¯^Ÿ& !zmîݸ^Ùø¸ýü^¿úíiP¯jêþÙLGÇ{l?î?ÿüó Ù±cǦw–-ge›¦¥üìÉyç/ᦑÝh‰½fÆ´“¶ZÎ~ô£D›ÝNÆØ‚3Üj*o¿+Ö6(bK6Ú*– úSI‰Ù±93qÍné¹ÛöÑ\½ÛYfÍéÍ•¸ûÁKãºõ“%î$'<"´„¹“Ó›9…Ã4¯ «êU–ËÐ* «¨ÌzU¿ýôÊ<ÍRo?½Ê’×ñì—8‹^õËë Ï«W.cúÆÀzÉ^F¯Òk¯ÿh±JzU¾i¸gj‹ßô{‹¤|eJ¨WÑÂmƒÄÉ¢É2n÷Ó+–¹âW²w•Þb^ÙèWê-§w½OhÅf¹¾¢›´㣥y×+›Ê†ˆÖ’¸¸^Ù˜XN£|½KÜÙ ^ÕÔý³3ðsŸû\¢ÿîœ:uêK,ÁùÌØñbæÙÔžŪ³ÎÒÞ{ïÛŸòu§vÂòØŠç¶”S¦Ì ãÊdt[ÒÕ-9!Ýþ’eËeRxÊü¥®Lµ9Ýêóv×p7]Ê$ 0¦LO`÷ ¯‚`¯"ZXzEáå´]íÇæ’–dÑ«Œ£%-©-û@½ªÎßD½rŽg©·Ÿ^eÉKEý’eÑ«~yýlЫhöÄí,õ’±¤^%VÝÈÎŒþb›ôÊKÙËc¾†Ìx1)ZÈ@½r‰ÿþ÷¿óûżMo7»,é‰zŵŠ]2Ù:4é%8½9r$¶1g^zú,G)„¢¬ÀŒ×W¶2‰cׄ~õ ì6i 1БÀõÊâE_5èÑÀ êUMÝ?[€Å× ˜@Ë'øÚ׾ƹ´æškžüjÇw¤„’CŠ­]Î<æTÍíÇaÒ¤I6OföùK¹Ïa'-]&/£@)ĺ²›ýž¢Í™‰ñ¸àܱ‡“¸éö ÜxöÙgH&NI`ÃhÜ”4)‡'‚ŠÖ!ÀR’uÈnÝ \®'(›K“Q¯²_’–´§†ìYôª"{õ*æo–zûéÕÀ¼ýdÑ«~ycöЫX ±¯ë%WI½ŠÕÛÔ×ìþb¡ôÊK3?žß©,³¼T—·,zåÊ´¹ÊSÖˆr)³lôÓ+»ÅÌÚ€Y qzdVNÎ’%= …PŸì×W¶Œaze«eÐéöÛÑÀõÊ–rjdñ%cšõª¦îŸ--’qìoFjÕ%c®p[– L3å+_áTÌ2¼Ÿ#o¼ñ†E««÷K“¸ŸŒ¶Žçm·Ýf öÛo?ŒÉûì …²Z %άXiúN²Ûxt ÌÞ÷³2­Óˆ ö§ðq3½ÒØQ0’¤y3F˱±ò4ntg®mûY%Àú­Š‘«´Û»±ñ!5ÔÛ`Ùõ*×…iƒ ¬:£^Uáo¯^õZ›¥Þ~z•’·ß¡,zÕ/oÌøÂz+Ç}ÍX¯¥/¯W®Þ¦6rù‹‘Ò+/-ågùšàÞ‹U®Œzeé?þx~ú}M»ÝO¯lBšµ×^Û™¾^¥LGŒÁ>˜ýú S©"6‰w½²Éói‚tv4d½²Qz–fñ+%MƒzUS÷ï¾ûî#ÎXm)…B8‡lQÑ]wݵŒIv‹(}öôòí|±îн¼dý=¢–7åP`ŒC4íV%ðÈÛ­¯•nyô(YÈHv€¢²ŒùŒfg³1žìwÈzƒ^ECµ­; ØXÙ¿ÚZ¨¹<öNhA#ûÂñ½%Ô¹ÇVÄæ®³ÒfëÊ¥Wy/O›u­_íÙõª £zÕÏÂ,õöӫļ‰;3êUbÞDË ëUbiÙëµì^ô*Ñ’zvæõ«¤W¾šÆî—¹×éË’Þr²ëyï¿ÿ~~p¹eåØ[lÆ=‰zÅ ÙG9ZEŒµÛâ˜WþCQ˜Ñ7&66±‚_½¸=Š  ²²^Ù÷"‹;YÒ4¨W5uÿHCp³’›Ç7&³-†›Lì1ûì³?ñIJ»,ünáò¨Q£Š)NÉÁŠÏ=÷UcÀÝw߽馛²Qx¼2C·™«Êúo¨+®fÝ’‘Ø‘ìRxð=ÆãŽàŽQÅA‡:ûF±Á´®|šÒ¨Ò¸ng B‹#̶ÙëÌ Ëi s/€ë´¼L]yõªÀEjóªÈ›K¯¼ûÕ«ï²ÔÛO¯byc_©4»^õæígsI½Š›½^ËèK¯bfÔö5¯¿&½òØ:W_}5ÊÏë÷ËôU±aW¯¯l>I†ux1 Ÿ^ÙÝÓN;-{-ýôj` Ùõ*±(Œ`ì!‡_½¢^GüDb;×+›‡‚“"fv™¯ÍêUMÝ?=šh |À±Î:ë`çÑG]¦Q-¯uë‹­ófOÌ·ÝvÛÂfÐõ‘qãÆñw̘1ÜE«f½[¸“Òøp&ÐûÕ¯~ÅþçŸ~ÚŒ|eçAd§Š%f­döGKË»ñ¸@iæ®å-Á¥)åBS’—fu¥Þ À(Š`#ä RCFNXìää­¡® ªÈ«W.Uƒòcré•wM¯vÞyçt,ëMÔ«3Ϙ¨W6]üú믟×ÇD½ªôú #›„Ü£^›×øY€„¬Wœàâtà¤ÈâKÆ4ÍêU}Ý?ëüáÈÈ¥‘d?ÿùÏicæu2eJyöÜsOJ+0e¢—… ž~úi{ƒºôÁ)\+Ü: ³•€œ·›’fÍ›±7=f3kr½GÃÙà Kðpò†cR=–Ыb¬õ¸“¥–¼zå×_§W¬Yœnmözy…ïÓŸþô|óÍ—EIFâô—œ³W ×]wõ–Ô+C‘«^GÏ£^¹2ëÙ(æ/¶I¯ü6Ð{ìA ÇfÞö[EáÒré?÷8²Øb‹ùºßš¨Wÿüç?,ÃX'¸€_ÞõªŸ ˜‡‘˜ŠÁ.G½²2A p°g¼Ö Y¯læ|N‡ËËF³zU_÷oë­·&~ñ‹_x¡VE!îŠÜ—‘?ýéOqÙ^ÀËn0ç +¼“±Ì´1VÝ 'œ@9ûàTv˜‰)íõb çý%°añ4kb±ywf4¯{ ykϘތää͘¾3ÉŠéUáËÖ¸Ð+¿þš^1RhàÖ\õRÚ7ÞÈ›;ì°ÃŠ+®H7K>lð•"÷Jm¼Yy½Êål4üêU´äJ· û‹UÒ+¿Mc¯Øy\›Î£yyõjñÅç7ûÌ(MMÔ+Æ:Q c fï— ¦W¼;Ç k>läÒ«~å³ó0S]*õ¥W®LPS ØÝžôõÊÖT,9éC¯ûÍêU}Ý¿„„bìÕeáTÅ0NÛÐ «ÎeW²½òuQ묪a#»^õãËß¼zå«Þ˜GŠõ¢WêY^©^Åê*ÿµ¼¿fƒô ¹®âúÊ®L~ô£•okï%äÒ«ßþö· Zk­µüšÓ«~+ªû­´Xi¼b]›Þ‹^%ZdP_qʼnGc;CÖ+ÂG²\%Æœøµq½ª¯û ›<禛nÈ¥Î6 ÒK,1uêÔ*ê=ôÐC‰žï¿–\‘| å¬V9räH,9å”S&$¦b0f_sÍ5U˜ôì³ÏÚºFÀO/ßxДéÉŠ%ð?<-3]X±ªÓsqªbU˜Ó¾¥[î娽òuiëÅ£,…dÔ«~Eùò7¯^ùª×ùU @/zU ^g³Û¨T¯\-^6¼øk–H¯ë*®¯Î>ûlJÞ|óͽ´¸ßBré•ëü0׃_3bzµöÚkCŒÞ¦ßZÊ—f`Ì£(/z•hxqŸ~&ÀÄv†¬W„=¾p Äl.ÿµq½ªµû÷©O} ŽßûÞ÷ʃóUÂË/¿¼à‚ b+lú*3VŽ­e9vìØ7ß|3vÈ}e ){Möä“Ov;½oüò—¿d¸#_Kx·0Z Fšµ˜Ýïwà´>ðS&¥áh>’Ñ”~kw¥~”O(ng㜪XÅiÛ¸%àK¯<^àÖÀ!‹^¥›áËß¼zå«^¼+P”½*Poo[Ô W½•ÛãÅ_Wµô ¹®âúÊ êçÉÚ=¯^ÙÐÇSO=Õ…¯¨^ÙÐǽöÚËWá¾ÊÁ$‚ó¼èU?«ÀK- î— º?d½"à«ùܸ^ÕÚý³ùsXZ7ÚöÍnéK_"L×[o½JÍXe•U¨å’K.éWËI'D‚ôNH¿¼¹öÛiI·*ðg€˜‡‘0©B¦£Ä\Ç›&ˆînÓpXB#Fwzß&©…€ô^rá÷Ûo?L sηÂNeÏèQ¯ü^æfw¡XÊz5°X_þæÕ+/õ(Ä‹^¨7±!êѫĪsíô寫Tz…\Wt}µÉ&›P¸¯ É]“yÙÈ¥W\pŽ0™‡—ªc…8½²9ü²O|+§¢¯nòÌ«ôúÊæJuGBÖ+›"žàÏâHÞ4ëU­Ý?›$wÝu×Í‹©¢ô6ÕÊL3ÍtçwVT…k¯o±Å‰µd‚˜˜=ïNÊÎüì“^æ­¢dz“NŒ¬ºïgvvKÃñƒA#–ô+=;AH(fœŠ&½(_G9UqÜ×b¾¬ª­¿zåýb·:éz•±^_þæÕ«’õÈîE¯ ÔÛ¯!êÑ«~µgÜïÑ_W£ô ¹®èúêŒ3ΠðvØÁÑg#—^Ù²üÔU¸àôj̘1¡ýzÚ/šVÝõ`Á›}é‹õŠ€§ þ*B¥q½ªµû÷üóσrî¹çfžŸ*hæ-s»í¶Ãž*¦Z‰Yò /p2pJ$Ž8· H¶ÝvÛX®ê¾2JÁÞdÒ§*¦3*l9ÆØ˜TéõHm® D¸^ê4\EÓ1† WuwÿX[ WA7%Ø”;Ë.»lÆyiKVgÙ7Úh#|N1bï•.¼ð¯¼òŠ—*òÂuŒM¹‹aï}ï{¯¿þú¼%”OÝu×Q5ðÁ˜¦z¡4 Ñ·ÛmJ®¼›K K̨b·Œ6X2 ŒŠÃÌeIƒ‰+Ò«ê.‚=²êÕ«Â…ûò7¯^e¬7c2s‘ô¥W¹êÍ¿~½Êb•KãÝ_W2Ò+ T¤WÆ™þ ?LŒÍiê7:Úܽ۹ôjƒ 6À—óÏ?¿·{òê•Ǫ]QõÊ•™²RÀ‚7%;²^Ñv6>ްw{ÜA¯êîþÙ»§¶êˆG”¹ŠzñÅçŸ~bôÒK/Í•±dâ+¯¼’J]tQës&ö7JVQ,;â.hxõîî»ï.VNÞ\Tdoú0#o ~ÓÇzã4…m4œßŠÒK#,©”%PÓSVzÔV.j¼Q*õq`áÕéU¥—ÂýÊ’ ¦WY²¤¤ñèo.½XïÀÎ)¿z•½^g@úFSz•n•;êÝ_W²mH¯àP^d[Ë;Àåì0/—^Ùl1ùÈG̯JÿæÒ+–øÕ«Œ†”«—,óä®W¶@"ŸÑñ¼ÉBЫº»¼L9ˌϔ)Sòòò•ÞºÝo¼±¯3–èÂ5ÖXƒsÃfÒOm˜±(ïÉXÿà„Nà=WÌã³Í6ÛÜvÛmÞkqR8UX]TJÕ)Ëî¹\UoÄÆâÚ¼ÿ4Yt8hÕ6Xù'pÔzªë­…ÓÓÎÓ_ôïµ¶º=•êUÕÄ%±Äôªdid÷èo.½J©7åPÔ_ïz•±Þ¨ ·Ô«¶Uáo´Ré•ѨT¯¨"Øåì°-—^ýýïç–¹Í^{íµh U´K¯ÊÛà]¯2šL›.¼³„¬WïHèHèUÝÝ?HÙûfŒ3¤öû￟ëZF~ÿõ¯-Ðl%³\~ùåˆÎB -tï½÷2B:q®‘’U”ÉþôÓO|ðÁî•åÕW_§óÏ<óL™2£y)Š)Ö:~TDuTMÓì6Ó9Ð(4 D3a'MV¿I'!J ®õ×NœžøÎ©ÚHíAUZ©^U}Y\’¤Ó«©S§–,ʲûõ7»^%Ö›¸3êfEz5°Þ¨ ·i õ*ÝÈ*üÕ(½r@*Õ«¿ýíoü.„¶œó=—^Ùã—:ß³ë•ó(×FEz•ÝãØYBÖ+Œw $ð}) ½j û÷…/|9öØc P+ŸÅÖùÌg>S¾¨b%¼ÿýïÇýW\‘¿5¬9QÀÈçž{î¨£Ž²ñ±I?„U/y@wûí·˜„,d$;…Pò¡pª ¢æUÅV_°¢±ª®®_ù„( ×~ *ÝÏéI휪•ÖҊ«֫.ŽËp6½úÖ·¾U¦h^ïþfÔ«X½±¯Îªõª_½Î€b4'lƒzÕÏìŠüU'½r@ªÖ+ûeä}WcPÙõŠÙæ8exÈS³ýõ*£UUëUF3,™=1ìÀ\Áê•YNx„ú@GŠ%D¯èþ]xá…Ý|óÍ‹+“ëŠ+® êÑ£G7Øñ`Ê]làÃSòzÖœ(FŒá —]vÙN;ídršÍLƒI/ý,î™Úx„ “&MâFć ¾²“C$ ‰mæLËNQH±! õ쇅F± Ø\éüÈý °ý„(Š mzÊ*ŽrzRu˜s|WáoJ™5èU=—È)>¦2½â%xŒLI–ëPþfÑ«›o¾ù=ïy½ÂŒ|egmzU…ã§X›¥ A½J €Šüí­Kzå˜T­WG}4'?î®Æ 6²ëÕ}÷݇#܉æÒ¥~²èU»®¯ÀhÏ ›Î3X½rfÞÄ¡îöøÝD¯èþMœ8²ô ¦OŸî—iziœoöÛê©§¦§¬úèK,}'@3U éå¨ "hkî-sbZ§S5ÝÂa8Z^Õv¡\ Él6ùOúÓòöËR¿L˜ÄðþûïO¯˜Â‘ìâwî¥ê\¦ið4´5Zªó7TÒ«(ªõê®»î"ؘ­þWâ£n¦lg×+» dj–”Òª>¬^åuܦéÀŒaê•3›À¶9ÿu·ÓãF8zÕ@÷ŽË/¿< rë­·zd:°¨SN9…J뿘î5Œº°DŸÀ 4õÞ wÂÐu;kØàĤi8Ik¨«UÔ£Wµ].çeΉ0räHFnßsÏ=y󦤯Á_.…y…ù*öÜsÏ 7Üé…X`ÙgŸ÷{ù°ÁWvrˆŒY"qE·<ªs–F¡ih Æõ*ÚÖÕù­Å¶¥W1&UëÕ’K.ÉÄ-·Ü«7¯Ùõêˆ#ŽÀ‘ÏþóXž¨WsÌ1'86jÓ«@ÀL¦ç S¯¢6Ø8BGwzÜG¯šéþY sÌ1™¦å†Ò]uÕUé)ë9zÐAaãÆ«ùh=Þµ·šƒF¡ih ¼°É¬k®Ì‰ p~oˆÚôªÎ‹æ\T+Ò«`ýÍg`âêÜ M¯ Euþ&¢–^ŰT­W‡r?LÛ«7œ¯õêŽ;îÀ‘Å[,Ø'™á M·€`&“祤 S¯bØ8BÇöûúŽ^5Óý£ß:ßSg0565‘FoÜLž~¢G¶º÷ÜsÏýä“O&&ÐΚ Ð4g>MSsÕ)Õ1”¥Î¥J8%919=9IS¬ªC5ëUýÐYZ³:½ Óß,L¦©ÔµõªRiS£ô*F¦j½âŽ­2rçwƪçkF½²'¥‡~x8–·Ñrá”>b(@½êEMHãáM÷-¿'(½j¦ûD›úæÜsÏ-t` ¶ˆv€³Øï¼ó΄Úf›m¦±±ê4 Asì²Ë.Uו·|›Ë›0Λ±@zNI pzÈÛá,uê뿌ÎÒvÕéU˜þfa’’¦R§Ô«JýíÇYz•H¦j½²©Y½)±ö@vfÑ«n¸ß»,–âT˜fØ$:Àìg^€z•h*!Møà<óÌ“N:©¶JUvàÓá!˜±¶¢»Sœ†œŒ”¯Y°#¡j½J¬Ôv6{‘ÝkXÕzš¿½÷Ô`vPzUƒ¿‰œm§ô*‡ªÖ«×^{ÍzMO<ñDº%¨WÖEÑïZÊÞ2`bÞ ô*ÑB·“0æâ‡&°ÝNêU“Ý?ÈÚø„›o¾Ù#e+êÏþ3óAÏ:ë¬?ü°÷«(yŠGŒÁÿ×\sMå«Ì^ 8Ø/»ì²Þ£î!˜ i›ðönÞM7Ý„üiäg Øêô*¥R;Ôì¥v¯yUëUhþöˆí©Áà ôªc„c_¥W1 ½_«Ö«üãüd´âžuº^þù8²þúë÷2Ôžt@{“¥W½æÅöXO•Ží÷õ5@½j¸ûwØa‡:ŸûÜç|!v嬷Þz”|衇º=áo}ôÑØÌ“(MSCcÙž¸‚½†ê|UAH$„·¯]9œ†”Ì)éöh#F :½ŠU”øµñ î˜UUëUhþÆÜ~­ÁÔ ôª£x·¥W‰X¢;«Ö«‹.ºˆŸŒqãÆE+ v;E¯þùÏ2÷ÛÌ3ÏL`k€† h `̼ ô*f[âW˜`&¤–ß ^5Üý«hÅ .¸€†;vìË/¿\¾Ùj+§ÃûØÇ°|ñÅŸ8qbmõaEà2¨ö ¤ l,'È=šÍœTá¯äëÑßbEU¤WÙ á²ÛY[ƒ^å¯s<¶Qƒ‘AéU þÆ÷~•^õ2éÝSµ^½òÊ+³Ï>;£Qª{½×©Â{Òõj«­¶âWõÌ3Ï,\þfÐ@ó=(½ŠÙ–ø•&Œ fB:1AÉaêUÃÝ?˜.·ÜrPÊz‘y¹O:u‰%– Ì³Î:+oÞÆÓóú¬=·\mµÕ\¨°q>^ ,x‰P÷{_ÙKEB`cøÁrÖýìg?ØNì¶Ûnõ‘|d`ÊÀ<þøã‹-¶¾Ð]á†JàÖ¶Â<0Zß°àm…Í)F䄟’&ã!N=Šâ4̘~È“yÔ«2$ùÇ‹ô*(k0&(½ªÁßìç‚ô*;+RV­W›o¾9?gŸ}v.«šMœ¨Wék4kp€µÇVËJ¯rá"t `Â8W®ì‰ƒÕ« º?ùÉO _~2ÃÛn»Iüy}3ü…h²„‹ÕXÁŠ-ºµ–ŵúÓÐÆ|‚ô¡‡ªßï5ä„:OØ—,Ü^7å4,YÎd÷¥WåquQ^ƒ^âo f¥W5ø›ë\^åÂUµ^ýøÇ?æúmë­·ÎeUã‰{õjà æÛŽ¼Ei«> «‚Ò«¼”]˜0Λ1cú`õ*ˆîß”)Sæž{nà¾ûîË´7Ó:Ùõ}»æñïu$º‡{T6 ”©JZ=X1êTýÛ ³¹^€ Òú ¨¨F›Éš°/3y)'§' §aEvv¬X/zå‹IP—æ5èUãþÖ`@PzUƒ¿¹ÎéU.\$®Z¯ž}öÙJgMÌëoöô½zµöÚkókøÛßþ6{!ÙD€î¥Wy›ÃÍ^KçÍ›%}ÈzD÷ˆöfäÁœhbšŸÿüç„ã"‹,Ò±«XN-› †… ´`bÓ§ïš­ñF`¦'n×QB€'ìÏ9çœÂ–sÒQ'`á†0cy½ò-¨ ôôªAk¨:(½ªÁß¼'‚ô*/1ÒW­WöÖÜyçWÀ¶f³Äôê¸ãŽã×p¯½öjÖªðk À”^àFÐâHô Æ…¤d Y¯BéþýùϦ ÞýîwO›6-e¿C^®ƒûÞø~檲ÇÇ,S~Í5×4nO‹ —­í@0¶ÈòŒ¦ÒñãÄ)|׃ӓŽ83Ö¨d(©WÞu™^ƒ^5âo •¥W5ø›÷D^å%fé«Ö«ï|ç;üˆì²Ë.ÅÌk6WT¯l!;~ßzë­f­ ¹vàØe¸Ú~}EкpÀ׫Pº _c5h†b‹YuÔQä-9 ®Šæ÷U&#­mEx¦Ø:ùä“}Ûír.t줳nÌ3§@9ÝàÃ2òy–2zUº .ÖkЫšý­¡º ôª œÒ«Ð,K¥zåÞš{íµ× [Ø`ƨ^-°Àü&þþ÷¿oОÀ«ˆÕöë+Â5ú£wìëU@Ý¿3Î8ƒ7n\Þ6ð8FÞªëLÏ…>ïz1Õ”˜s©cc\ý’ŽMK. •y5ίaU”VfÆ#N7‰S¯ ú]fa½ªKP—ì5èUmþV]QhzUµ¿…OéUatUë•ͼp饗¶°ÙŒQ½â7qÿý÷oÖžkˆø´ýúŠpÅ B·"ÚëU@Ý?~ÇŒCcÜzë­¹ÃfÀß}÷ÝsåjibâÕž¶¯´ÒJz0±Á P—]vYbšŽí$øñ7ïz'œhäâ¤Ó­„ñPX¯ Ô•=KhîUëU þV]EhzUµ¿Ùƒ9–Rz’ëkÕzuâ‰'òk²Ç{ä²*´ÄèÕ¨Q£pDŸÕöë+Â7 Ý*â0|½ ¨ûGØÎwÞ9{cø]ÿ:{½ ¦dÂâ•W^™¨m¶ÙN=õÔn?ÚÊÅ p@¨\ÙÛ›˜ÅßY¯9²{±ÓN;‘¥Ø¨Ñìµt8e½ªFh—ïUëU¥þVZx€zU©¿%ƒ_zU`¥zÅiίÉ|óÍ÷æ›o–´³Ùì<ðÀrË-‡/ú¤QÛ¯¯TÂ+r$|½ «û÷Ì3ÏpíÎ$Â<òH ˜>}:¯-Ñ~Ç{l–ôIóꫯÚ\^ø¾Ùf›=ùä“q­°#@… ƒ@T¸¨6fäÀwNNŠ,ösŠq¢qºqÒeI¯4½òêUo í í"¾j½ªÈßŠŠµFP¯*õ·d¨K¯J${Õzµêª«òtõÕW—7µÙªÖ«f½+V{€zUÌ—‹@%\ Z·ÇãF+ô*¬îô÷Þ{ošä€ÈÒ6MÓK,1uêÔ,é;–æ7¿ùͿĢmßûÞ÷2^÷w îà8îÛÒ‘K÷|è§'ç'ÅÀÄ$à#1§[–ÄJÓ@.½êWHû¼”¯T¯¼ûë½@×ÊaêUuþ:ÇËlH¯ÊÐsy+Õ«¯~õ«ü¦ì»ï¾®ºVoTªW-"¦^•H ®mù¢zKh…^×ý›0a¯“2’í…^èeÝóòË//¸à‚´_±ÉB£Eµw›ûy6q-xÍôž{îi¯/Å,Çe{¿ ægY6Í'§F:LN.N1N4N·ô”:šN »^¥—SÅÑ/è+Õ+þz,*Ö²aêUuþÆÜ/öUzUŒ[o®JõŠ·Xù;vlgîDWªW½­àž0õª<(B”@%\«˜A£-z\÷vÝrË-i”LoãC=”d¬ç–žlŽò¾²-ÿ=óÌ33(”ßòað7q— ÜÂ0xÉ©‘žÌ·Ýj«­Ò“éhõ*KQÞÓ„yY_^yñ×K!½M¬^Uäo/Â{¤W…Ñõf¬T¯–]vY~Žo¼ñÆÞzÛ»§:½ ™I°zå!J ®^J‹Ò½ ±ûwýõ×Ó0Œâ{å•WbXÝׇ~xÖYgåå¥;ï¼ÓíæÉ“'tÐA#GŽݼóÎû­o}«Ãbq qgqÇq˜[ßùÎéÀIÁ©Á âvÆ68­l̰–6Š‘)ö5‹^+ÙK®0/î«Ó«’þ–ÌžØd!ëUþ&B(¼SzU]bÆJõê°ÃãGùÀL¬º½;«Ó«™„¬W¾p¢*áê«@WN‹ô*Äîí!FÊÀí¶ÛŽÆÓ›K.ælãþûï·{{ÀYh¡…¾óïtl¹sÜÁ)\ÃA>8‹Ë1CþÕÞîàéÇÁnMé±y?>öÔ«ezÌì%~EzUØßÂû5VàzåÝß~Êì—^•¡—˜·:½ºãŽ;ø]æ-ôÄzÛ¾³"½ Kàzå”M”@¸z,ÓŠj‘^Úý»á†feM|ªó»ßýŽ£Lõ1Ì/z¥Dí5×\óþ÷¿D|[l±“N:iàË`)¥rpdÑE5¿p7±-(38)lN“^Ã8¡l²cN±Þ£ÚSŒ@º^+Óo®/ô«Ð«þÈ’ÒFáë•_SP”9$½*C¯_ÞêôеL¸äà7úöÛoïW{Û÷W¡W3 _¯<""8íÚØûªiíÒ«@»´ôÆoL sÌ1±Vë­·VYeU´Vc¬ºö~½üòË×Xc @ñaôC9ä‰'žh£;˜ñ¶Ø=¾à®µÑ‘Úl¶x9M8Yb•rBÁ“+¶__Kè§W%‹õ˜=ðË}ïz•Ëß\‰Ó¥zåÑßt%J¯Jì—½:½ªnX]?_Ùï]¯ñ‚J[¡W~áT7D¹]zn÷ï–[n±~Kl Ðïÿûìç•ÍŽ kôßV÷6®¼òÊ6Úb|x+l‹-¶¸ä’KZ±0+Fb*c¶Ù#¸ãý†Mä›-“Sc™e–'KÔN%ëEsrE÷k»<~zU¾d%~Ñï]¯2ú›1YzC´H¯¼ø›NÃËQ镌‰…T§WãÇç§g…VH¬·K;½ëUpZ¤WÞ±œ„(ê·äÖéU¸Ý?†KéðÃwôâ‹/Î?ÿüìÔ4ŽI– fÙ}÷ÝYãt|˜ñ–[tøÃìJa†ažMË‹µ˜ñšã'KC»4œ  ãdá”q;9•ØÉiåöhÃ#^½òX¸¯¢ZqéïQ¯ú;0A:ùÖéUIÓiø=*½òË3VZEzÅ“X€š*¦Ô¹ÈWzUµG­Ó+ï@lyB´wlTɺZ§WAwÿþô§?!"£Fš4i’5Œ+ÐеbaÊ͉ÓN;͆ΖÃô™6óÚk¯müQ*`ÆØ›f¦b0fówÈsÙðž/|á Æ“ˆS °œVCN¦"÷{õª¢ŠJÛ–€/½Jñ7åP:ä–êUaÓiTqTzUÕh™ÕéÕ~ûíÇÍW¾ò•hußö¥WU€j©^U‚°$8 Q¿…·Q¯‚îþÑ<Ûo¿½k*¦]še–YXçmxî*ù PWºÏÒpK.¹$líÃ"àÛl³ “jþùÏö~SÄÕÛ "ª£RªÆ€wly†ažz)1\y¿ršp²pÊØä¨ö“Ì •·¥ÏN ªWÙsÕŸ²EÝà”׫Dw¦´EÛõ*¯¿)(j8$½ªrEzuõÕWók¾êª«ÖàB€U”×+/Nµ]¯¼@è-„°$8 ÑÞCeö´Q¯Bïþ=ðÀ,ìÆÛ_wß}·UØÿýË4’òF ÜvÛmGq“©Œ1Âõ¾xFă#dŸwÞy÷Þ{¯Ç)Š)–©ÂFY½€óÇ?þ1j¡¶Ëàd/'§'§'T™•7@T¯ÒS6~´]ÃUF¯bþƾ&6G—ô*‹¿‰Ù)½ª{Ez5mÚ4[•÷‘G©Ç‘0k)£W<ê’^p?K’+"‚“Í’>cš–êÕÜs×ýan|ðÁ§žz*]vz£Gf=k[´:Lk[jÕ³Ï>ËíÞ…åpG½` qæYj©¥–^ziþ.¼ðÂðç¥2þÎ5×\åÝ<þ’å7Þà¤âï”)SžþyÆBð—u˜ZêñÇçïc=ÆÑháË/¿ü‡>ô¡ 6Ø€.Š[Í/š@ÛeÀÂLFl§Ï¿øÅSN9¥LÊ;€éÕ†nh³«Lß`‚çž{Žé”&L˜°òÊ+c­½±Ó =Ù«.¦WŒ€Úyç¹äµWÿzè¡÷¾÷½Ì/5ûì³w[¯Z×Єå7Þ(½Ê~FNY‘^}ò“ŸüÅ/~ÁÔ6Ëba󺑱˜^éúÊ{ëó›ßäÙÃ{ìqÎ9çx,¼¥zÕ‚î¯\Âòóì±µT” ºëtì¹2lŽ×ì¯Ó«‹/¾˜ÎF͵筮uƒ^qáÖ[oýË_þÂhgn>ú裾îi2$)¦¹u²Új«­¹æšãÆkQÙj]Ó!ße—]¤W®+ݨH¯.»ì²wÜquÖ±5Ö*u¡]…K¯l¯|à,õÎÄx;ì°ƒ/3Ú«W-èþÑH?üá?ó™Ïøj-•#CHà?øBßkvÙôЧå¼ué¦Û­Ù†ìÕµ®{îÚÔ©SeàFðF¾ Cà﫯¾Êèž2HBhžû1rQè6œ¿L8ìF:0ê!úNrz½am]ãÒ4+®¸"£E¤WµETzõÚk¯Ñçï“O>έ6§ÚRÑ@½²U¸ãFZuX¯*mµ§žzj‰%–˜cŽ9x˜Ä_/uµ[¯2Žmm6ï°rÿ•ÖúÆ7¾Ñ¬%ª]ÚE€S†‡Ó‡“¨]–·×ÚÖéU»^ ko`Ôly›UzUsP]EzÅ#\~z˜Ú­~T£ô  H²÷Pá=­Ö«Ð§~q­rýõ×Ór †þÇ?þávjCD …' § '§OJ2òN uzÕÆ®‚÷VëRmlPéUSX…^þùüô¬¿þúM9¥zE J€P$ ËèÎ2Ûm׫™ÀÑŠEò" sŠpÀ­0XFŠ@ã8Y8e8ql ÀÆíZ§W¼ÕÆì/ÌÃL0¼ÈΠÁái¬îyÚº1ŸÖÒ«¦B± ½Új«­¯Èdr“¦šUõ:!¡H@–ngÉÖëU™¾oÍyŸ~úi›MøÂ /¬¹jU'­#ð«_ý uã”áÄiñ0¸zÕÆGF¿.´´¥W~à oiUè+úòô£ý(¯1J/~ „„"髨èUkžþÑr,9ðío›Ïþó/½ôúˆ€$à9ðÀ9Ä)É“˜F;+%ÐF½Ò3ÀJC¢†Â[úÜOzUCl¤WQ…^í´ÓNTúë_ÿ:½jª XZ@–¯«#zå«+\O9o¿ý6kÄÑx{íµW=5ªh#NNNN™6Úß ›[ªW-}|Ô˜)ãE{NzU¦Ý}åõ®WL±8óÌ33âŽå%|©rD / $ ȼyÓwCWD&n@IDAT¯Z3õ‹kƒ|Ùº¹´Õlމ6D JÀÞãç4ád‰î×výZªWííHÔßÄÔØÞ&“^B˜á]¯6Ùd®ÖXe;eɰ°EÞ E/ŽwF¯Ú4øá³Â +sÌ1lì·ß~¬*3cŸþˆ€ü/N N ¾pšp²ˆK³ZªWÚlØä­½¥c>qSz•·­+Mï]¯4þ³ÒöRáYxùÙ)½òÒ®¹7ß|sõÕW§Õ?ûÙÏÖ\µªÀ pRpjp‚pšnê˜×^½jï¥! -s³ÕÍ$½ -VýêÕĉGŒÁ*Û¯¾újhžÊža @à~!¡XÞß.éUûZûÝ}÷ÝŒåå2÷Š+®(ߢ*AºA€Ó“‚Sƒ¤uËöêU«»Ýžt/ZÝ@Ò«ôÆmê¨_½7n¿J]tQSî¨Þa&@à~ayÓ«ö þ¤!ùðpã„N`cï½÷ž4iÒŒ}ú#CM€Óœöx|¨q„ä|{õJ£@CŠ£¸-íó‰'Ò«xsóݯ^±ð,ž]rÉ%Áø'C†ˆ€ža·;¨Wå;ÄM•À,U›nº)͹å–[6eƒêpp"p:pRh¶ÏpÅYÒj½jõ#&×Ûh{£H¯BHzõØcñÃ4Ï<óL›6-d—e[÷ráG–ô®{zÕÖÁŸÖŒåþùiÚÓO?½dÓ*»´š§'§ƒ—î­F¬ñ­Ö«¶w6‚Šb†µ½9¤WÅÚ½Î\õj­µÖâçI¯êÔÙ|ª 6\“ð+I£“zÕÖÁŸH ŸEYäÌ3ÏdãË_þò„ fìÓ:?§nŸuÖYœCçKnµ^ih8QÖê1Ÿ`”^…K)–xÔ+›ÿSã?ShëP,äJ®öÞY½*Ù'!û¾ûîKܬ¼òÊS¦L ÁÙ u ì ~NN„:ëU]Å´Z¯ÚþЩX“•«íM ½ *œãE¯î»ï>~¡œòÖ[o ¬Q DÀ ‚͆Ò+\`‡õªÝƒ?­Eiž•VZ qÙu×] ·±2Š@K ö?§'BK]*³Û®Wmï~´:Ø:_zÕ®ô¥W+®¸"¿S×]w]»Ü—µí%@°r^:¬WíüIÓò5jÔ¥—^Êû^xá)§œb;õW†ÀÉ'ŸLØüœœÃàrÛ}l»^ihSØö1Ÿp“^5<…ëõ¥WZÿ½p(c1åW{ï¸^•é•—Ë_vœe–Yn¸á†  “1"PB€'ì þŠªP±h»^uà1TE-[Q±.½ª(6j(¶¼^Ýu×]\Äó>¡&¦®¡½Tafó xÅht^¯º0øÓ5í‘G‰¾,¸à‚O=õ”Û© è$‚œP'à ûN:Øy§Ú®Wè´%Æ:€ZzÕ–`ëggy½Zj©¥øÁºå–[úU¡ý"à‹aF°rÅ ½êT÷oúôé›m¶MþÁ~P+Ì zåj› 'Ô x¾6ËÈèUº%±F ðk K¯Œ«¼&•׫C9„߬ƒ>8oÕJ/y f!—7#é‡D¯:Õý£Ùžþù%—\’Vßk¯½ ´º²ˆ@+Þ9¡NÀ·Â`™H zÕÎIbÓ²³x¥W„SI3Jê•=Yzé¥Kš¡ì"0aÆ5R±GÍC¢W]ëþŒôµ90¾þõ¯ %Ö °Ñ5‚¼ð öÖ¹Üaƒ; WÝè¢cÝ+½ 0´ ›TF¯xká…æÇK¿\…ù+caF°xÑtxôªƒÝ?‚ãòË/Ÿi¦Mjzî¹çf‰¥¶ ¤ l› o‹Í²3@ôª•ôfªùh7J¯j›ª+£WŸýìgùý:ꨣj°SU -Œ0#Øò*½êf÷&?ýôÓiþYguüøñy#@éE L3!M`ÞaZ(«Šè€^u£»R¬ù¼çêLé•÷À¤ÀÂzåe)¶@ ÈŒ` [drØôª³Ý?âò ƒâBy¾ùæ{à‚ S& Æ3!M`gÌ¢d-"нêF§¥ñ˜éFéUãT©ÅôêÍ7ß3f ¿b÷Ýw_¥æ©ð¡%@h`„Á–ÂêU—»LTµÃ;Ë,³Ì¤I“²ÇRŠ@h`˜`&¤5Õgh­ãÅžnèU7º.^´X!Ý(½*Öú-ÊUX¯l^ãŽ;®EÎÊÔ ´¸R"̲Û<œzÕåîmÿꫯ®³Î:„šk®ùÒK/e¥pº0aL0Òá&Küè†^u£ã·e3–Ö tÒ«ŒÍÝödÅôêŠ+®°K²¶»/ûÃ$`K„YFó†V¯FˆS±Ã~P?üá?ôÐCë®»îµ×^;×\suØY¹Ö=S¦Laq¿Ûn»m…V¸ùæ›m©÷î¹)Œ@7ôê¹çžÛh£&L˜°òÊ+ßpà ,°€Úw n@“^ lè.%( W,ª† ¼òÊ+?þ¸-ß% ò¥YO<ñK>Ì=÷ÜÈél³Í6ИaÖ«MÙí—Ë×_=*ÃôvÛm÷úë¯wÛ_y×%„+AKèÀ„±ú~]jÜD_º¡W\ÞÑë£ïG~ ¿Ä‰Îj§#оŸôÊ5èlÐ+.Ê·Ùføüú׿Jr³6TX–¾ßëU÷»„Ýb‹-Æ¥ó"‹,ÂÉÎ;ïÌû µÅ¢*ÂT• %t `¸pQÊØ"ÝÐ+õ³‡\7ú~Ò«ì-Þ¥”ôj§v‚À%—\Ò%ò%T`éöH¯:þî_tì/ÓÙ¤]vÙå­·ÞŠÒ¶„F€%PÑ/‚V“¤…Ö:5ØÓ ½êÆËl•6w7I¯* ’ð Ï¥WŒ¸›cŽ9FŒñôÓO‡ïš,l ‰ "´°t›¥WðЧv€•@x÷oôèÑ_|ñž{îɼUé÷tTš"@p¢*áJÐÚ"6M£z!Ð ½Ò3ÀôàéÆs?éUz+ÃÑ\z5jÔ¨Í7ßœ ÐK/½tàÈÇzN¡E€¥Ô(½ú_8é]äîå5*›ý…G+o¼ñF÷”Gm'@XÚs?•pm»;²¿ nèU7p•iÇļÝÀ"½JlÜáÜ™]¯Î9ç®A7Ùd“á%¯« @8T„VJáÒ+gˆ:ŸQ(ª%[mµÕk¯½æökC'@@–'!ª¾_ãÍ‚ÝЫntu<ÆC7€H¯<†D7ŠÊ¨W̶?räÈYf™å…^è†ãò¢YáDPZý,‘^EÉ c÷ÿÿò—¿Ø{€ÌJÇÄQ"Ú¦Š$}?‚“mÊ ÕnèU7:<^b£(¤W^‚¡{…dÔ+éñcwöÙgw€<ªŸD8Ù âÄÚ¥W1,Côî‘á>k¬±Æøñãm.PU›øàƒ ýcíŠ x„AH(„eŪøVè†^ ó3Àn<÷“^µR>j7z ^±dü‡?üáiÓ¦]y啵[§ ;E€"'‚*æ˜ô*Ä}êîX±” îuÖYç±Çãü¦›nrh´!5 ä< $µ¶{ ÌÛ[E7ôj8{€ÝèûI¯Ú«õ[>P¯´þ{ýÒÉû­ö.½JkîÄg‚ösêÔ©;ì°˜fuÖsÏ=wØÜ—¿M Ø9ð#›2Cõ¶‹@7ôªà 3FN7œ•^eln%‹HÑ«'Ÿ|’uºY¥M?QbÚÎE€à±…þ§hFéU”FïöP¿ûÅÁBtu”¿þõ¯Gi[ª @˜Y¼x„_U¨Ì®è†^u£S40ƺá¦ôj`C+A?)zŰ~mÁî~Ùµ_R<„M#½ŠÒHÜV÷ïß°œ~úé¼ ´]¯ÚÞq¢-Ûî‚ôÊç ©²R 8½²ÆŸš\Eàß0\YYðèúêßÐdø¢îß`H>øàJ+­DÍ>ûìL]58ƒRˆÀ;†à!„¤wvë¿TE ízÕêîS«'"¥WU–*·§WüJ®¿þú}Ri·$ `>º¾J 3h—ºƒÍ8Î`˜}öÙÇâlË-·|öÙg3eS¢!&@*3q,ÔízÛõª¥¨–šmÑ)½ªû,U}ï@¯¶Ûn;û­Ô_ÈK€àÑõÕ;'SŽÿêþå€Å;¦óÏ??¡Éhã+®¸"GN%2„‡ I'`›!ó^îA ÕzÕº®Të ŽÆ¨ô*JCÛõxûí·yòœ÷º_éE€°!xêØÔ¨î_¾Fœ8q"“7Ú)÷ÙÏ~vêÔ©ùò+u× †E¡BÀtÝcù.VëU‹:T-25¬Ò«}m@«õªAnÃSµôÊc[«û—&wN:é${ÙtÙe—½þúës¡ %@0ôý‚D7¥:ÚÎmr«ÕzÕŠnU+ŒL YéU"íl@«õªAnÃPµôÊo+«ûWçÝwß½Új«ÙCž½÷ÞûÅ_,X²u‚@X<„G'Ü’!Ð^½ ¼s¸yýÂWzÕŒö‡@ ½z½îÙ ½ª¢MÕý+NõÍ7ß<þøãm^DZcÇ^xá…ÅËRÎ6 é ú~!A`´ÙÙÞMíÕ«`»XÁ–ÁÒ«t>:öêUôºdƒôª¢ÖT÷¯,X¦-Þ`ƒ ì±=õÔSeKTþö ¹Ý”e„–vhOÓ ©¥-Õ«;Zš40¦¥W)APZªWA1l¯1Ò«JÛNÝ?x­þ£ýhÞyç¥8×\s}ãßxýõ×=”«"&@ÓÐ47NÓzÓ/àæ’iÿG ¥zTw+(cþ¯iûoI¯ú³Ñ‘  ´T¯‚f¼qÒ«šHÝ?o™´jçw¶Ç€K/½ôÅ_ì­h—&¶¶¦Ñ5½g`í#sh£^Òé ÄŒÁmüN éÕ;$ô¿­Ú¨WmeÝ´ÝÒ«zZ@Ý?Ïœ™›ÈM ³á†j Ï|›.Ž¥Y­ãGCÓÜM[¤úE 8ÖéUã]¯Æ ÈÕØÒ«\¸”8p­Ó«Ày†fžôªÎQ÷Ï?í·Þzë?øÁ»ßýn: 3Í4Ó~ûí7iÒ$ÿÕ¨Äz Ј4% J³Ò¸41 ]¯ ªMüh^5Øk°ê¼ /½ÊKLé[A uzÕ ª)½ª¿ Ôý«ŠùK/½ôÅ/~qäÈ‘ôFuøá‡¿ð UU¦r«$@ÃÑ|4"MIƒÒ¬4n•ªl¨›@»ôª‘nX#•ˆéUhÊÒ.íÒ«v±­ÙZéUÍÀ]uêþ9•l<ðÀÛo¿=Ý>óÌ3Ï1Ç3yòäJjR¡ ±h2ÎZ¦¤A+¨GEŠ@Z¤W5wÆj®®X4H¯ŠqS®–h‘^µ”p¥fK¯*Å;°puÿ"òàŽ;îØb‹-¬ 1ß|ówÜq¯¼òЇrUDeh š‰Æ²V£ùhÄÊjSÁ"¶èUm]²Ú**Ò«Âè”±íÚ¢WmçìÑ~é•G˜…‹R÷¯0ºÜo¹å–7ÞØº¼BÓØ«›4MFÃU_­j°´B¯jè˜ÕPE™†—^•¡§¼!Ð ½ê íÂŽH¯ £óžQÝ?ïHxà 7¬·ÞzÖ œsÎ98à€Gyd@®… AsÐ(Ö:4UKͪD%¾^UÚ=«´ð’M.½* PÙ»G |½êóŒI¯2‚ª-™ºµ¡þ·Š˜¿xË-·1b= &“dí¸[o½õßRèK€Oجž4 M£jįªB'¸^UÔI«¨Øò-½*ÏP%t˜@àzÕaò‰®I¯±4¾SÝ¿&›àoûÛÞ{ï=Ûl³Ùã¦qãÆ]pÁÓ¦MkÒ¦aªÔ»ñ§!he˜ÈWÈJ d½òÞUó^`VÊýÓI¯ú³ÑˆY¯â¶vñ»ô*ðVU÷¯ùzæ™gŽ<òÈ1cÆX'„·Î>øàûï¿¿y˺kxì^ð>M@Ct×cy&~«W;l‹ò]zå£ BÁêU‡ÛBzÕŠÆU÷/”fš2eÊgœ±ÆkX'¿¼{öóŸÿ|êÔ©¡˜Ø~;€ R÷î%và·ß9y õS¯¼tÛ¼â¥%¤W^0ªS¯:Ö.Ò«v5¨ºÁµ×Ÿþô§ýöÛoî¹ç¶~à¼óλÿþûóBóôéÓƒ³µ%€`¦Q/AÝd¦J 4½*Ùy+™ÝK#I¯¼`T!"ÐK 4½êµ°u{¤W­k23XÝ¿@Ž›U?ùÉO>øÁZw…¿ -´Ðç>÷¹›o¾ùí·ßÔèÀ̸€:‡¤€Õã¾ÀÚJæ´›@PzU¸ W8£—Æ“^yÁ¨BD`  ôj µa&^…Ù.Ù­ARwe¬ ðúòùçŸá…2m®™·è¢‹~ä#Ùu×]?ðØd•šÝ I¨Òí·ß±‹.ºhâĉfÉrË-±O|â+¯¼rƒ¶©jè6@ôê¹çžÛh£&L˜ÀùΓÿX` öY–™%ô* %¥*¢WU¸VQ™Ò«ŠÀÖ_¬ºõ3/Xã]wÝõ«_ýŠ^ÍOãÇýõ×í0³›ðzÛ‡f|:jÔ¨x¶–õÕWÛyËŒÏÿøÇ—_~Ùš}öÙ7Ø`:À[l±Å{Þóž–{)óE kÑ«]» J6ƒôª$@eF4¢Wx­Tz¥ÑímuÿºÐ¾¯½öÚ7ÞÈ0zƒ?ü°si–Yfaaë ¾ï}ïc$äˆ#ÜѶlðz*Ï9y¸g]¾»ï¾û­·ÞrÆ/¿üòô÷èõm¸á†sÌ1‡Û¯ 0 Ô¬W)¼”C…ÑI¯ £SF@ÍzU3éUÍÀéNÝ¿pÚÂ%O?ý´u“øë)±ÚÁ*«¬²Új«­ºêªöwôèÑ~jõZÊäÉ“ï½÷Þ¿þõ¯ö——³_yåWC´OKÏv‘Eq‡´!"Ð.õèUb7/qgzÒ«ДEÚH ½ª”ŒôªR¼-*\Ý¿5VnSY…ÓÆIÞzë­tŸyæ™X‹/¾8OÏx*ÈËrî/ªzrÛ 1e&<½üÇ?þ³sá…æ1æ¸qãèï1¢uÎ9çŒ%ÐW¶¨T¯b=XåTzÕö“ý"à‘@¥zUÞNéUy†Ý.AÝ¿n·ï¿y÷ /ðHÍ=UcVtôëßRÌø2Ûl³-±Ät\pÁ±3>cÆŒá9¡}æšk.z_|i9묳ò,næ²²ú'Ff¾ñÆŒ— p>,°ÃÝ&û¼øâ‹“f|X_‹Žß“O>9mÚ´^(œéÚÝSJ6æŸþÞdÚ#"ÐaÞõŠ÷„·Új«û￟wƒ¹6z衇V\qÅ«®ºŠ÷¥¥W$¹&5ð®Wº¾ª¡Õ†¹ uÿ†·õ™Öé±Çãi+ òyôÑGéñä®ZmPèRò’Þæ²Ë.ËÒ||x¹Ì2Ë Ï䥵¡VE"ÐjÒ«V7ŸŒ¡" ½ªæn£³êþµ±Õªµ™çutŸ}öYžÑñ¬Ž¿<µ{çÞdŽº;åo¾ù&Ïúf<ó›ŽMöç#GŽœñ€ð_OyZøÎƒÃÑø =Àn¸aìØ±y‹Rz¨ŽWV{î¹ç•W^I{íµúÓŸòwë­·þÙÏ~¦«¬êÈOÉz÷oxÚºO<òHú~H6øZI5*TD@Jp}¿•W^ùæ6î¿ÿ~Nš4©DÁÊ*" > \wÝu«­¶}¿1cÆ\zé¥gÏø°ÁWvrˆ>ëSYCI@Oÿ†²Ù=9ý׿þu­µÖ1bĽ÷ÞK‘«®º*c‰ïºë.äÉS *FD@ʈöýxÜ·À P¢Û©g€eù*¿ˆ€̤ðÕ¯~õ„NàRŠÉÎ=÷ÜÅ[ÌüÔSOí¾ûîLµÀE×GAJͶààh#/=ýËKLéÿÀ¿øÅéÓ§3ý½3>ŸÿüçùÊÎÿK¡-h”€ëæñ¸Ïõý°ˆN _õ °ÑÆQå" ÿKàÑGýЇ>ôo|cæ™g¦k÷ûßÿ>Ú÷#_ÙÉ!ŒÄd>(F@OÿŠqS®w]rÉ%LE5ÿüóó¾ß|óÍ‘—^z‰÷_xá&ªbV=1f ôëû9«\=tL´!"P3óÎ;oÿý÷å•W–Zj)¶×]wÝn»í¶O|âO<ñÄÜsÏýÃþí”Ä:$‰ôô/‹v ÀTTLHE¢ã?Þú~l³ÁW6Ü\UJÑa¨Œ€ëÚÅžûE+Ô3À( m‹€ÔL€.ß{ì±Ûn»±ñÑ~ôî»ïNïûa HFb²‘¡lÔl¶ªk;uÿÚÞ‚ÍØÏb=öØ*«¬²Ï>ûD-à+;9D‚è~m‹€ˆ@²ôýÌõëlÕ%"àÜqÇk®¹&ïø5êÌ3Ï¼à‚ æw^w4eƒd$& ùË_RE¥¤×!ˆÐàÏ}LàÙgŸ]a…¸ÛÄôS›l²I,Kimºé¦ŒIx衇´ªr ޾ЀÔ@ {ßÏã²h¨c¢ мýöÛÜ%?ꨣ˜î…)ôÎ?ÿ|.« ÔÅ…ÖÇ?þq¦Üc†_±öòL3é¹NC—EQ2tM^ÞáÃ?œ¾ßŽ;îØÛ÷£pvrˆ$+_—J\\G.eÌgozØËD{D@ª ðôÓOo¶Ùf¼&óÖ[o}á _à]¾b}?l##Ù)„¢(b)¼ ›UfÇèé_Ç´rwî¼óÎ÷¿ÿýÜgš0aÂrË-—Xß#<Â…÷´þô§?­½öÚ‰i´SD@¼(Ö÷sf¸ìzè˜hCDÀ#+®¸‚•ÜYØ}ìØ±?ÿùÏ7ß|s/…_sÍ5ÿùŸÿÉ*¦, ÏJÛn»­—bUHW èé_W[¶*¿:è V¤9øàƒûõý¨˜C$ ‰«²C劀ˆÀ¿p·\Ïý¢eè`”†¶E@<`Î<æG cFßï}ï{ß=÷Üã«ï‡‘EKáÛm·QGãUTÇèé_Ç´Zw˜˜i¦¸eÅpóyæ™'¥²þóŸŒIàF/%kVâP:$"à…@ù¾Ÿ3Ã¥g€Ž‰6D@ʸï¾ûx/†k'WÈÖ[oÍëïyÏ{Üž2>ø ·Ý¯ºê*W×`—^zéJ+­äöhC=ýs(´1€Àk¯½vÄGè„NHïû‘†$cƒ,dP´‹€ˆ@ ®ÃVø¹_´r=ŒÒжˆ@Igu“sZßoõÕWgév¦î¼òÊ+W]uÕÃ;ŒÛåeÊ';…P}?Š¥pª @ª£Rª.S¸òv–#ôô,¾ò•¯p &Ó§OÏ’žd$& ³¤W(@à¿ÿû¿éõ!5üe»@ ‰Y\±<dºãÄ4Ú)" )^|ñÅvÚÉu!–É qÒ#/{ï½·ÍÒ¹à‚ ò¶^Æ+«h]d!#Ù)Ÿ¢(Ð*¨ÈUŠ˜ͨmx—ˆ@ÿøÇ?æœsNÔ䦛nÊ’ÞÒ˜,d|ê©§²çRJÈH€UF]tQtfùå—÷Ø÷³Ú)b)|‘Eyøá‡3š¤d" "ñãÇ/¾øâˆ}8à€X>üáÛQ–¸ù曳s#1Y,/…PT4/QÝÿVü®waÆDh{È hð§;;´‘F€ …§Nú±}ÌIUZêwŽ‘˜,d<ôÐCßÙ§ÿ" "à‡Ý³ÕV[mâĉÇ6@~Ê}§ ¤X¾1—:cxøÎý¾è}}ík_Ûh£¸un‰Ž=öØï}ï{±EùPî’3EÂK,a]ÁÝwßÝeéW: Hf]>2’Bl°•ËBETG¥¶‡,ƒIæÒhc¨ y÷Wîg!Àª2#FŒ˜}öÙŸxâ‰,é£iÈBF²SHt¿¶E@D  :f¼¤Çï7:믿¾mœtÒIeÊŒæ¥(»V£pÛ`Fu"Ò¶ˆ@/.{Æçº³Ì2 <ô&‹îá.ùÿûÿoŽ9æ ¦¾þõ¯³'šÀ¶ÙÉ!ŠEb²$&‹f¤j pÆ`X ¹hÚî þìF;VèÅÛo¿½Î:ë ÇsL±jÈHv ¡¨b%(—ˆ€D Ð÷ã¶·]ÓüêW¿B[¸â6{vÝu×)S¦DçÝ&;…PR,…S…ÕµØb‹©˜—§Ò‹Àð@+˜¹˜m¶Ùø;×\s±"_F÷Ÿ|òÉ]vÙŤ}»øâ‹£ùêDd$ŽMÙÆÌp&aF¦¤×¡a  îß0´r)úÓŸ¢¼úRøŠŠŒd§Š*eŠ2‹€ˆÀŒYܺ£Lsç\~ùå6)1“à=úè£n® 2’½¢( ty©ˆ|–Yfõmˆ€.uöÝw_S ë²JÖwÞ™—/õ¹‘œ =øËŒ p påzEÐjÇ Œ!»Æ¦¾¨Ëë‘ÒH@Ý¿% “\ÏÁåeÌ:÷Üs‘›2}È2µ+¯ˆ@gðÜï½ï}/z‡Ùíb~=ðÀvt¾ùæË~ÓÝB2R2…P”Ûon2=ÔR0G_E`˜ ðæž­àÇC¿ùçŸ aÖ¨Â7¡xCï‡?ü¡Méù/¥›ñá+;9TŒ3ÆØDV˜gO&1³‹•¦\m' î_Û[°Zûm¡¿|à%Çm’BP0 ¬Öb•."Ð]ôýÜ*ƬÇx÷šU°¶ß~{Ô†ör½ è^ö#;…ôR¤:*µK1ú‡êö"Ò6\Þ°ÊñÌ3ÏŒ2ðz°=^ょÅHË xã7N<ñÄYgÕ‡ ¾²³L™˜dWb‰©”ŒÙ_ò¯ŒIÊÛuÿš"ß‚züq³¶¸ùc(¶ÎËDÀÐ÷³õý¸jáò%åÎ:W3¹^¤_{Ù¯ŸëTêFOi=À~”´_†„À¤I“6Ûl3롹¿K/½ô½÷Þ[†ÃìY"e®7ãc…³³À †¨%†yÎTÛÀ‰&Óvç ¨û×ù&.èà[o½µá†" »í¶[Á"z²QR,…÷ÔèK Ú÷cF–,×@_¤G—ø²_?S¨Úæ˜AÍÔìGIûE ó7>sÔ¨QNP†ì¸ãŽ7Þxc^>øàV[mE |èì]uÕUV®CH’å-c0É&1¶ò1³mG²ˆjÞJ•>XêþÛ4 ö•¯|ÅDåe|™BQV&…û*S刀tž€ëûÙµKö!_äŠ'åe¿~`1)3cÔìGIûE «¦M›ÆbÈ®¿ç†hyä‘wß}÷'?ùI{¹•àÖÒÙgŸýÚk¯ DñòË/SæÈ‘#ÉŃoûÛ±¡ž|e§> ‰É2°XªÆ»ÃEɆy‰©|åãŒÇÊĵe*A¨û×FôïïÛ=!S·èôw…+£„Æ ¤ð¯/6@E@ÚHÀõýì2…Qš¹¼HypàË~)Ù`Q3I=ÀP:$#ðÐC­½öÚ\ÏØ »ªá%ºÿøÇÎST‹¹‚^xáu°Þõ.f[9ú裟zê)— ºÁå=4{H™{ï½7Ù£ ¢Û"UM2ö»š¢:*µyh°c0)Z2Û+‹VšýÅ5ŒÖ¨íNP÷¯“ÍZÖ)î!hÁqÇǯ?ýôÓe %»½gL&1TQ¦@å\¬Øû~sÏ=7ZÄ=ìÄé^ÒQô¾ ˜ýe¿~%S‚ÝP7ÃÔìJûE KÎ9ç[CÏ ûD—X‡=ñ.9ÏëHo}E’±üú'>ñ‰;î¸# „UÖZk-Žòùð‡?œq*N’‘Ør‘=¶Uð®[í0#ö,ÑlÀl[DÞŠ2§pôQ#µÝ=êþu¯MËzÄp·°óÎ;sÙ´ù曣 ›nºiṡÈHv ¡(¶wÚi'¶©"˸…²Î(¿ˆ@k ¸¾ß ,€h0J3eº—^r¡c«ò ½E[Ùo` ±cGÍ<õc|ôUºD€+–}ìcŸ7ÞØFi²Í­mf¶K÷ôÖ[oýÈG>âúc묳Î\€€Ø„Âzîçw^z!½GÉâ‚§( ¤X ÿ—‰3z›TJÕ½£{0ÞîΓ…á ¸6#÷»pViQPÛV÷¯c êÁ}öÙ‡“Ÿñ 'N¤8&7·BL:\¬t2R …Ø<ékƒ%¨¨XÊ%"Ðy®ïg×7Œ(?3¯.¶ØbvqÃFïÊ~y©b’ g0#ÕÌ PéE è#Ù„™<;þøãí™?JÂÎì³°üãÿ`Ü“iB̯Î4ÅS§N-ÆŒd§»l3q£ *¢ºŒeâ‚yGv\ÃA{ÂÉÎ=ÛŒU(YhÔý ­E¶çᇶ«™m¶ÙÆ™rå•W" ܺóÎ;ÝÎŒd±×c(Äe¡p ¤"ªs;µ!" FÀõý–[n9»É>ÝK C ±K.ô‡ _eRFb*ê¦ð×!hÞ¬ce<{p÷¾÷½a–cÇŽåLç³Æk<óÌ3y=:÷ÜsmÔ€ÂH(n…ßsÏ=y˱ôd$»Ø²)œ*ò–†#¸c%à nâ,_q÷û½^˜·¥‡€ºá´E–l±ÅvþÇ tÐAì_a…r½xCb²‘ìQ÷(Üj¡ºè~m‹€ˆ€ëû±´ºõ©òN÷ÒË-b²C¯éøX?¹4­·döØ40˜ŠÁT¡`"%íÖ`ö[ ¹8äCX6}©¥–²«—M6Ù„i¥ryô—¿üް·æškžrÊ)[o½µÝp§L d€zÆŽÉHL3†B(Š)Öö¬¿þúT—Ë<Üqâ&Îâ²é$úÍ[“« %‡€ºá´Eó–üîw¿3á`•ј5¯¿þúj«­ÆÑ=÷Ü3v(å+‰ÉBF²Ç’Q…ÕE¥±Cú*"0´¸ÈXrÉ%†SÚݨbÓ½DòJŒMÓ}ÙϽ È!DÓçݦiåc°]|ñÅŸ|òɼå(½ˆ@8.»ì²1cÆ E -´Ðµ×^ËŠÜÙ±ëîø$ΤÒÏxnií¿ÿþÖÓãE˜þð‡®›÷È#|îsŸ‹Ž&=í´Ó&OžÜ¯(‘ :V“ìbé)–Âí…ª£RªîWTï~œ²Ûd¸‰³¸Œã¸ÏWP¤7‹ö´”€º-m8ÿf³»Í°Çy~ÅWôV0a„9昃£^xaïÑÞ=$#1YÈØ{”*8ʇJµ |/í!$À•ŠM£bâÀ_¹*Ù7»úê«m¨æb/ûEW$Yàiëq9Ë™MÁÞv.S¬òŠ€ÔO€µòèSÙ¹Ìëè³Ö¹±‘ûî»oö™ðèPÑ[s‹õ1*±kÇ“7žÝ-»ì²V)ƒÉ<ðÀØ |e§ †'‰É’ø’*¨È-!ˆÙ;«¸†ƒf.ó÷ÝJô`ɲ†aýM¦óP÷//±Î¦?ýôÓí„GSúIw•H3zôè7¶I@2“%U8¥£êÄ4Ú)"0<¸È°±âè†"Å”\âd¿v‰áb•d»ã¾ýöÛ'^'±“CÔH2DzgüŠyi³/Dg8¨z€*™BàÞ{ï]i¥•8‘÷ø¡}ˆi P‰ÕW_=|;ì°ìv25”M2LF† œ$†gw@aDý*Õþ¶P÷¯--U­/¾ø¢›ª_‡Í,Øn»í.ÎR¤‡CvõFâ»­3IiT))uHD Ûèû¹Ñ›m¶™­Ðàö0 éúë¯ÏE ú²ß1ÇÓï–erˆveSàU@ sCÂÌ`ŒÇ»xâz€¹N‰E A?øÁÜ};…ùëú]p@FÛèz¹'ft½®ºêªŒ-ý«O}êS1KøÊμ]/ªv]PLØuvâ¬pî; X(—Rm$ðÿÙ»÷híªê>üŽ_ÿhÑaÚ/ibŶéh4 Þ‚ï&rñBâ=¢Ö‚ÖxWPÁ„‚‚ŠŠˆhTTPs-i+` (PÁK5¢Œ6épàµVm†£¿ïW&‹½÷³ÏsÎû¾ç<ç¼ëù㜵×^—¹¾k®µ×\kιºø·{mÏÓ\zÔ»ô£ÍTðíoûö·¿½Yà„NX”Ì+ $“xQñ**ÿW˜IÙ_u:;VösqôHbšÌò¤Ž]lµ¤ûIc¿y7` ˆ˜º¶ ‘HUE,g4¡.Sîà<òýmG`°\‰"@„œôþÑ9çœãú„ fh¿à/à³`‘¤»òŽ;î¸R¼¤S°å…믿þ¥/}é@ŸÜ£H¯Ö Q ¨È[t¡ŸFiš–rVÐ(–Äø ®ù5Þzéìé7.þm&Ú+Z×—¾ô¥ºô¯xÅšTÒD°$Ëäu¢"½’@²5‹R]¦Y±fúž #ÐØa”ì—br€Öº{á~൯}m¤)v/kê‚¶Æ~ËouCUâ¸î´-5o mÏá yˆL¿”˜4$êàcÚÞœ†ÀE]t‡;ÜÁj„Œ”{N<ñD6,åç“åcûØR’’ÒÀ'ÿ¼ãïøæ7¿4è=}öÙåvÅIÉm½@YAñ+SRÖ½ï}ï÷íú d±ä•—]vÙzKF ’Êý RK‡K4DsjÓJ]«É{ØÃR/( Š0 4Э—’ž~èâß*ôÂÓíp›ýª\õ¾&A¶ L|O ö<Æ!•k"êTÕd,“¥§étv %ûÅã w)&Ò×ØÝ‹•GíÍ“¦é‚®iì7Ý2¦€­¶'’6(ñÑkHsÒ´.PêU@à§?ý©k`"qHþ„'<ÁÄàÍ•Åä<>ùÉO©ä%â½ìe/c ˜u‹·~¶«(”žã—ÁÕYk¶Ôþ‘Ëú8à€h]ôøÇ?þŠ+®h3zéUÒH,Km<µ)g‹mŽBŒìx-N™¥i¨™%j~Þ,1ƒTü·ƒ€`œ©´¿ZAºø·‚²©$Qy2°sgèQGµdÝ6¿s%¨) Í’©Ó«åµTZ ¦-­‡;Œ@É~V!ñ*l*°˜˜9y›Ñ]ÞØoÒSÀImÏÉÒ4! J-Ò´¬ »8‰Uìl×]wÝ}îsƒÔ‘Ú«^õ*ÎQÈ?NìÏ:묺‰Á}z%µtº'ý]ïz×£õ¨J©ÙÓQ’üû¿ÿû6ñL˜m0õºJÞnÑË_þò™ì^IP’e”}ycÙ‘‡ÈV|Õ ÑœÉ[ì5Zç'%p@$;¸€–ƒJ0s¦™ýÕª!ÐÅ¿Uë‘M¥‡›àˆëÚ¯âƒ8ºO¦’-`v9ðV<ߤ\³¼Ü8_lÛè¬2%ûQ’ŒBTÐ×t¿i·ûu¯{Ý@ÔµWÙæ’ô'ò'»ßp…D»I± 7/Q:-mO¬¹é®!æÃ4J£ Ú%ÀÝïš^BG` ð| .¦ÜÕé°‹îR.í´‹]'l¤šÿøÇ‹ªóÊT­+#½U 5öíƒó)õéOzÑÕVNÒÜýp骑¬Æ—$OÖ.™Ä9ˆ“W!ŠRàdb 1Ùµ—>?g‚Ò ™oiäd“~p tÁ ˜ $ G® ]ü[ÁNÙ<’N=õTƒ9ÛH|à×[qä=»A”ü²VÒàò¥©ºÈ@Òò{ÊŽ@G`;"ÐÊ~¹ 4[Ñq÷²L‹Z]Pw¬G0#\­ËØo¾"E)ÐÔ¤pUøMj{.*'n`Ò4Íìà" z|G`3øáøÔ§>5#úw~çw¾÷½ï©=ŠH‘dòÊ¡ýŒk“|ä#•ØH8¿,gœq›ÿäŸü“â/zOzÒ“ˆF7ÜpƒŠc\pÁ!‡’ÔÌ*Ÿüä'7†€Œ²—®ÁÁ¬ðœªS©ªsy}ªCò2ÈÎ4å­æhÔ"2@-†”“¶Ge €`L<`Á»¨¿:tñouúb³)15äj¾hL^õ¾&MLù{íú x\3Ë8A®€HÊ9NÖc:€@+ûQ§¼Ë]î’uƒs6 œëj ]ÐØ×)‚Á­îë*j2±ãBù*ŠoÏÉ”“‘åFvÍÔØ.NÕ#;›†Àg?ûÙÌ94êš+÷Ú¤åpEx¿ýöûÆ7¾1IÕÕW_]æsw¿ûÝ“9sã—æÿò_þK¥åg'è—ù—£V F€ÍEµŒËœ‰Qˆ¢Ú’U”§T Ä iòtQ4$)5M'ëR X’ÌßÀº$f”2À äÉzäê ÐÅ¿Õé‹Í¦ä™Ï|¦œ1o¸ÎÜ‹5C™]ŸÚÈ.ÚLúÉWªÎtb6™¬Gv:ÛVö¦q”Å„}ëj]û)!ÊZŽéÖô ºd­¶gi‚Ù&_¯€ªQÙeC¤ÆVó»è’Ñ“uöV§vZ†3µÉ/ùË)ٵ÷»Ýí2åÍL2©Hiü>ëYÏJ~PxËœ4 üµ¯}Í-êw¼ãSEþÒ–âRåüóÏÿþ÷¿?H¿G…(JQªŠTªj¬Y¦†hN¹-ÕLçKT-ê¼tuo3H£ dPolU9®´Çì ºø·7PÝe~þóŸ·sC훇+3Eím€ôÓO?=sÀ²'‹+D‚$!ì _øÂ†‹ê;ÕD „‡`ÂôŽbôk—zÆÝËd[Æ~Šzä#™‰ˆdµÈ/èdQãÈÖ·§b>0g™‰Ñ´lÃk¬¢ „.΀Ö_uö,œš<ä!ÉAb!)|òÉt?÷¹Ï~øá‰Ï!ãéÈ~Лßü毑mžÿüç/#¶‘Åô·âHGk|+wu@IDATÓ\½ª{ÀÀTø‹_üâz›,‹Œ²+$MðWáª(³@U{ü‹¿ø‹e„1Ò´HÈ«Écw Z”ê2{«€`&H[²4À'}ɬ·™=ýÞ@ ‹{ÕmPfÌí\êbèòQ>ÕûL{ìúÔž–@mÍd™|UWÀ‡¤ "NÛ#;A ÄžÈ~¨zЃ”%Ëšî^M°þÈÍ $¨Öدõ Ê9eËAÆ5e‰W„1tiµ=ËPÕãÕá|Éq£Ì?øÁR]œÇ­¿íì(=æ\+ÎäßÒü¤$yÍ5×´çüÆ{œäÉH”jçœEä±{ÛÛÞVÆr,Õy£\ö¸ßð†7дŒ¬’Ø}ôÑ6›ÚÚUx%de,¯B¥Àvë\u*­«üƒ¤e ó4P3C’†·ÓÚ¢¼-Ðò8þ vàšÐW.þ­B/l6 ýèGRNŸŽ8âe®z_Dâ‘G©„ûîú x\”rÍø\¤¸ÏBäšYz‚Ž@G`[ POÉ~ÿøÇÍ~‡vغš@”ŠÞ‘s9×ô òÆ/hü.PR¢€4Þ½dÉ£dG¯IvŠšcßžËÜ 8Y¸HÍL{5ÜcÒ%ÀEˆõøŽÀî#`¿ð…/Ìá[¡7½éMŽ÷i<½èE/:è ƒ2*'ÿ’¯¸»,)˽çûØÇÖ$‰Ü‹_üâ2Ãcƒ÷û¿ÿû3;ãœgžwÞy|¨Ô¡bxòtßú[Þò–ÒÛð(²<…J&‹Œ²+daªF2ÒF„!oƒC­[ïPLÂL¼@–&€_Œ§ÓE÷øÍA ‹›ƒó ÕÂð7¶È't’›FK^õ>nƒqn„›ML%~™ï6¬Gš+à‘„0Å"rÒFyLFétVuJö3´#¡Y¾Ììs%eùÒäÄ|F—i½º cmÏAÕõ¨RUg5¹.S@Ägy§á™Ù –.¼=Ð؃pÝó뜸©³®¨ Ê/»ì2ñ6}r†ê£;R–4ùø$":ÏyÎsH8¼Ö9(+Áæ’K.yô£]FqJãïwÑ­ãfš[®¼òÊO<Ñ¥|Uˆz­¬²¸ ^I ™Ä3sà |d ¦Ä]… ÁI¦ ¢9¥i¨™™å~Öò›þ8 TJ]vÍ€ Òä¬3ÁuÄwÍ5hc\]ü[\Û>±ÉÂ6ŒüoþÍ¿yÞóž'¿½h˜‘œ­ºéE@"7<Èi+aÈ@êò³ÛšÐ³t:{÷gÀµïµ×^›êîÿûà– ­6Ô<%c¿ùÄyKu³T¶é‚¶Úž·Úž3UlÌPc³ªÓüx<¾Óî4sÑó %ýUG #0‰ÀÙgŸ=¦ý÷ßÿ3ŸùÌ¿ý·ÿÖœ#2‰mE,±ãï¾ Ø"ªq‚òîw¿;b>ýéO§ yç;ß9Y”Óþ¤‘¸|®HC1A“T-i3&OxÕJ"½’`™r&Ó ye+ˆlÄOJz¢É®ù@H‰‡7àŠÙ$Cª,u@ j(]º@Xwþ“´õÈÍD ‹›‰öÖו+ŒC ²lwMº·Z“P{EñÒé¦Ñ6±G…{UûaíÛ5È‘aC~åSxͼ=AG #°j8àjXØ ¦z”ÑM%iI‚û­™ÝDD3«À.è2Úž3åÛ,wj§!ë2Ôä´µ/žêUDå™û«Ž@G`x ¯»ìÜzÇÚíCúQæD+ªàLàJà‰V¤ó´x@pÂ=îq IÖtW]uU[#§=%sÉ^{.—\ùKªS²·¾õ­ö•Èl/Û+Œ°‹/¾øøãÿwÿîßU9ˆthfÞO h–@2‰eI‹ªœ6 :•ªuª9)«. ÑœöTs@-(’ô Êu@ÈR.E$x‘0€{|] #’W×lÌE|Û´Þ}ºø·ûn§îz×»a‡Fûƨ7•(ÁÎÐÀöÆ£H¯$ØXÉH m!Á+§çêt¶Rn4¢ÿøÇ3ë­-g1·¿ýí—”vN9唚Mû-ÓÆ±.èòÚž3å›î¤-ÈCäLÊz¥Éž)Î_€€8‰éZ Tt6†À¥—^QÄ>òûßÿ~…Ð!r§¨!ö®w½Ë#å퀹5ÔÙ”Û†ÍåöɦU©5M’qÅWgj)åŒë?ü‡ÿ`s‡«ŽëlådʪŠÞ7äËFN9gu–;ÓëðPJ’ÍLñƒÒ£H¯Z™SFÙÅ+ªlU¡¢"¯%aÈC$RŒìœ‹J#½F)g²É‰Kíè h Ëá^]Áª( KpaàGKwäÔA香Zú«M@ ‹›òªTá"ÎÌðu˜aùãÿx½ô]xá…6x”0©Þ Ò+ $[oɈÉ4­D†Ú~èzaìé;[Ž@+ûÎYÇ|õ«_m.VNÀþËù/‹t¼[c¿×¼æ5‹’-ÙØV4sËòÚž‹ª@²+?c (™fŽû¬„¢dà€($u pÔ=¾#0€Ó*Gý9Ýr$U÷ˆºÀà"ó8ªŠ‚Rd³X÷qMIûµ¯}mŒYn}ë[]­‹˜´± ûßøŒVuqw÷©O}jL½2/¸à+Ê“‡rˆ}ŸLÉ;þËéÝCúPÖw×_ýÌ\ç•’I?y㢣:•ªÈ@ ’&5³4ACê`P¹“)#ÉAë€"@©h `«ÒÀ j€ƒ]²òä§SrZ¨.58`TÔ÷*]üÛ«ð®Vá5êÎ=÷\2Cb÷ät°ˆt«ºýöÛOöÅ­(8I&ñ¢rÆñȈ¯á†È=NÜc:•E d¿ØÏ{jÕbOÚÔáG—I¸dºèV'ƒ­nÆ~у"1²µÛýÆRFRKytð8£:µ|3¦€¥–X?j¸&k¸æï‚áÆû²²Î‘˜ÀÕ%Àå‘ï);AÀ1TTÉ.:oE—xq…]ä–ˆ.FµpWÕÕq–a¨œIH¿ýío[ØÔ5WT¾;î¸E‰'K0+¾ç=ïùíßþí8JÉ °è/ ]ÜçX+„—îú xYÄ/Ê+^*RݺÖ`š£Q¹SG!«É>Ù‰3kI @0FÇÞãøîv1º  Ñ5:("¢.[†UHì>]üÛ} ·G ñËbÙ»²i úKí\9ߟâšÑØþN&ö*®$žL0ŽD@Ô¨„0U 2“µð¼Ƹ´Óèl%ûÑ)ÊjÉÁbJçŠ~x„.ÎNì—ÑH{¸ac¿E­nµ=íšû™XüÈZ^-ʵ|üÀP78îÓLÍ’<¢%%±0¸¢ˆÕ%Àå‘ï);çŸ~„‡]ƒá̉‘e3:}÷¼ç=ÅøÙ_æ‹8a bÌ6Ó‰Ù3ŸùÌÈ3¡lÛb(8N<ˆ1ðhà˜ËÝXxRmIâœYŽ`:Ò0#åY󘪮$ÿ•@2‰e‘Qv…(JŠ*fr©TÕ&;M Çš¦¥ª.ÍÂ8¥ Å „ê€Ym¼×½î•Í,°çÀ \¤Ý5x§û& ï‘{.þíUxW¥p‡õµ¿ewÇ@5ä òÏþó·½ím=2>™çªÙÈ‘eÍû”%HɲTöEUÇúE$!,SyHEâ)c,*³Çw:›@É~–ÙrrðeÙá|Ì@Îo°>3üyxjÍÑ’Ú°±_ÛðE¾=[]ÐE~AÛrÖ —) â3ûioŽû4p0Áá&5~}SÂÏýÜϹPÁŠˆ^ƒ»ø˜É°…³Ùmò!‘Æ(®h«€\4<ý*² È(»B¥@Å*\*R]› 1HBØ’žW4V“ƒ’AI ˜³„J,½­lr|1ÀJgÅq¨Ä:±¯ñøìíÇ.þím„W¢üœ§E—Òx6Øêªwf¾™wžö´§Ío}á _ÈTBƒ|™VI¦"YdœI¯RUK‰Œ²9Îð!5dkÂL!ýUG #°å´²ŸpT=©>:+“?Ë”Et:{å+_ ‚dÖtŽÅæç¥E¥‰wÈÖÞäþú׿~ èîQä¤_Йb'_!©®ýo Ñœ™ë@¡™~À „V0v pêÙ6‹“5–§Ÿ~ú–ì:E&9ðÀ‰Fÿøÿã‡Ü®uƒ¼àßÿþ÷íã(-¢‹„)9昘éR‘ÅlíJ­]vÆoœµðõb6ø½ßû=7(dw;4´-ÈH{ÜãȨŒ_.¿üòœæQ¤WH&qom! «BEªS©ª¼¼E"‘ºèL¯m—†k~)¬‚8 jÓƒ˜ -bt ÀÁ.&] ;¹<*- K]©CÇ zÌ^B ‹{ Ø*Öæw­¨¸: M¢íUïŽï“àØc]D÷O~ò“ìIÛ¤Y”fŸm9e¿MŒJÍhU/ÐŒH¤rœÙD‘‹ éñŽÀÖ"PB‹ñ.Lì±È0ŠíCg‰f [CÌŒâ2ö3ØYüÖjÒ2pÍÆƪ^§ˆ3&^Eó…²È¸fám‚±uâ3£²]Ô¨6qE-ªÔ+%¸´tf—[Äz¸#PœqÆeÇ›EBÿ»ÝСºµº¸ö*]üÛ«ð®DáÑ«Œ•·«(ûÏÿù?gWŒIîàU#ȹ}I•÷ä’8¸/UgÂR5õæ øœ»›5d¦?v:«€@‰+‘ýÄÎÍÐvÉ* 5gq‹¨¥h”Mq‚£0ÉHA‹,’xbU&=,ïÛ³Õ•}FLM-9îãü€Ì–UWkÝ70\D0@ä 8€—G­–¾ íà"ôzü¾‰¥ÁÃ;,ƒ®ÿÝyèܱï¾Éê{µÕ]üÛ«ðn}áT»;[&CÔœ)s.ŸuÌ 'œ0xûÇüǦk”«¯¾zðjÍGY²¸QÈ qTRU:©€H•†`Äk‚†hΠþØèl-%¨”ìÇ>$]hÅ¥Û³ÈÍæÌÍ~Šb8÷ˆGêQšÑöœoµŒ²£ß¯Õ-ë¾:îû•_ù•ræ9Y¦,6¹b÷‚Z'‹%Y@`R¼]C×cö)야ìe/Ë0aºfœÆ±p¹ ®’s¡yƯaÿ“y¤ký­o}+ÉÈB/ùËKDá…’{LF1_ùÊWìG¿ño<úè£9;É6V²þºoÝ}’ñ“I}é›ßüæ¶[œ Ùˆ× ÑœööùA{AÉ€"@ hqà)10AZ/¨K¹Ý[‘°Üø§ÅëPݪs…u´î^´c¸Oqþ^jlÿö°+Q¬[_Œ"ƒ3²VNÒþôOÿt†¸÷¾÷½™^M’™âùŠ‹á Om2>ìaC‰¢RˆÂ3¼U7CŒ)F²­ ™eÎ9眙,ýUG #°i”pÒÊ~jÓ—ì {eOz|)c¿åoö›< ´æ(ã½åµ=çQjuA®Šöî¾Éã¾EšÇâùf‘)`|À¨€À*­@î`aÒûæŠìeÛ2>ñÄÍ—^z©‰…LÒBáÊØÌêÂßü"„8„¿êª«$¦CÄoJvº%àWœ‡L?¼"oÊ:üO>¡nÏzù»²Zò¶EXÓ4P3#]!hžÁ´ W¾åE‚7ŠZ+¬VÌ S7µ§…‰¤­[u®.N_èôóémçÊÙÅ¿•íšÝ%ÌNsä%¾€ë½»Üå.Q+š)ýÌ3Ï4À Q‚V.pçÒw7]Ë¿À TlÆ¿ŠfÈð ©Îl£ "¬Q“›èóEõ·ŽÀžE Ä’ì瘮VQQøñí›ÒýÖEÞà0ÐÌ RúH.¿ZW93‰¥Àj‹*,Pæû•6o X>`J?J¥­®DAÝ%ÀE÷øŒ€ON¥œ·_vÙeii‚ZÍO”»ãÎ8-EqLö¯E:q:ꨣڛD~djJÖ*uÂ/½÷­Ÿ}öÙ»¹ ÚŽ¤É®ùÙÀ \À "Ú 0¬GP܉Ñ%O¦ƒtV{§|ô?uk€ÒѺ[^]¶#z+NsÿV¼ƒ6Nžss#‡:[,qCçñíoû2%:Ü—ØØÎÒ§4‘–É»(MôÁ˜)C‹R¶ñF‰Ÿ&hˆækZ›¦‡;MF ’ì‡ ·»†ì÷#gûfìñeÆØoù†ÐöŒg)ÕeGIÀn´=¦Ý\¨É®E¥!U¸ê6l‘2o 0àÊ zÁØBQ€w °…¥‡w6FÍ“žô¤ ÃÇ>ö±uI5¢È¥ùùo|£6‹“^‚2êKLû× v`õ =ˆ|Âý8ÝìXáVy?þø‹/¾xô-Ï<@@ t#0AZ³e!YyKL¤.Óq!£ô?KÑLwëô¤ÄãÛ&–§¿§#ÐÅ¿1&;!ƈ¢Dd:‚ ¥!ıÞò~;ëvÔV i7¡‰JJ¾dQúê’ ÑÒ´š2–,§'ëtö%ŠŒe?'öÙ'vâç(ƒÝú µß0¢-ÄÈìâêK¿.ÚÆ¾=s˜Ýb…ÛŸV Qm]åK,‹Œµ÷_Ö}­.è2~A'›£üE¦€åhQ”ã@S£`ïà$¼=r‡!@(—u´´­‹æçîp‡‹.ºèï|'?á5f ÿE?k‰#<ò¼óλöÚk™¨½ï}ï3–k!—¡g•B/© ØÚJ{8D€ÊL´ÁLz ^ ƒºÅvQ§è8ݧu¥•Lç¶PëúrXÒ~› ‡7†@ÿ6†Ûªç2ðŒ¢\ð;<ÖUïËPÿ”§`@@À¤€m½Þù­ößÿ.t=°c¸æšk8à€ð¹Ùænw»Û"w”NÈos›Û$¥E«[$5@¸Ç=îáö)‡QìІñtˆx¤{Ë[ÞÒ=‹ìÎ#0AçUà`¾.ÐÕ5‘Ìu–.ËêÎ+]ÙZVb € êÓƒ=0É!{_.¤‹;­÷íg›Ä¾¸¶$En”Z²©×_ýÏÿüÏËå|ßòèéOz†ßg>ó™%K$“1Ó·¢¨XªBEƒ”‹s–\óš&¬™å¨EÙ{|G #°û”ò¡Ñ÷¿ñýèGmÐrûVÎÓÅç—ñn§–ÞNÎmÙf»l³›>4‹Røò¾=Íþç>y8yÜ'ñ’SÊîë‚¶¦€ÀÑ;€WN<Æë] ƒà`~ èZ »ÏÕ½„ÕA –?›Gš®ƒs¦Ä̱RT ,!JÒ Z8kb–í§Ê'—ÁÒFRç¶¥â~àvru@Øî”¼@.U|Ý¡ tÄàK!R—鸒 uhŸºXGënž›«O+êv‡k éïâß‚¿Wªv§§áÁ7³›!”Ñ2yÕû˜—±üæoþ¦,ÖLyk=”3@;÷ŸûÜçÆYæcdÉ–¿Bji¥pU¨hp÷Ë¢¢r¼,š#¦Å˰Æ.ÊÒã;=ˆ@É~Q»zå+_Ù~Ýu×Ýóž÷ÌTS»¹yô·´¾Ùñ;=k3®ž×ö\3{ í=gûmeÝ·dQ•l÷uAAQŽ ¢-r GÀ‚·ê¾øtD—[dzxû"ÀÑn™œ8²OôêW¿š-Ù§?ýéï~÷»m»²~¨‘âš§å5¢+¾†Òð€ÿøÿ£ëËÛrzxo#p°¿f³ê” è8ݧ+F ÖŸ¡ð;ßù6À XcÔ‘/†Ùƒ®ž÷6«V~ÿV­Gv‹žüàQo(ÕÊ8¯ãpiÉrO8ácrvë—öèG?Z<¤/}éKK%™ÄQ[’½•ôýoÕ-YZâiNÒG=Uc5yÉz²Ž@G`c”ìgæN^z;mQÙÏùžËÌŒø¹‰;è ƒÚ­wñ~Ñ%Ã|ìckíÛ¦íY¾=çµ=Çy1ŽûN>ùäýöÛ/4ä¯G‘»£BÙê‚®éTc5YÃ'õ9. ÅÿA` ¤­ vEÃMGdVìà ¯ûã¶CÀRá®w½kF%­fÇ€“M 5mìÔ6:2XÄ0{(,„Ÿö´§þùP7˜¬½Gn] #tG̶«]_’¡®Ìt—·:z‘öÀ$I†mÖµ(Ýpv^Æ.þí¨>}Ñ‹^dHÜï~÷K«ì™e„|ðƒ\¦ý×mçÞfŒU× ½­îL»Ä¶%uå%‹Œ'ãØ¢Z*RJuM>jBÚRš)F“'Ó÷ÈŽ@G` ÐÊ~ÂQàiÅ6ª>ƲŸñèX>¦¹™”±÷}§Ÿ~:3ê‹cŸ ­4XgƒÓö·×BalÝGÄò+Ý$ëæcËÀqi‹bé‚jÎŒ¼ €€8ñpHQhæFH‚1:ÁÈU;ð%ÐÕ5],pz`Û!Àëc<dr¨›ßÚ†Øç}ñ‹_\¢þ¯ß`ƒ)‘5^u´öð–  S2×U÷%0èÄDên>¹ÅI¤ Ã`,´%ÍÙÖ•vño[wß-ˆÿÚ×¾f÷ËöIéy>ÿùÏ7BŒÒº¼E†[>cwºÓ¤g^rË7?{úÉO~â_w×*m2¥H $“X'“©H•NïAMÈB£òJ35V“5|¸?v:{0rǃ WŒY[nƒ6öú$¯ò— “c?…0`#ö<ô¡Kƒb£egÈ|{V½kÖ´î3¥,² \³ðqˤfŽB¼&L6M“5\óÐ20Œ˜R ·{áѪPHuP—[<{x[ `37ŠE¦>B¢ø®w½«%Þ8e[^["ÝIßþ ‡(æøÈ¼êª«ÚBzxuÐ5™ÞÓY:.³YÛ¡ÂÕѺ ±˜D ƒm’#ÕÙÀê4v•)éâß*÷Îúh;ì°Ã îU*[•QQ13,8<ðÀoæîìŠ×.˜xã]TšWñÑ$±,‹’©HuhVõ¢4m|¾U‘qK£áÓŽÀžB D‹È~Š%·°ÎóS¡%®8A'ùÙÁuuÁ¡‡*™ï7íîEz\Ed+ –Q‡ìù懲¦èš»NÊœ<î{Ýë^Çü¯j¼’`c‡óç{š³HÞÐÊÁ•EÁ6s;¨K-ººCÆê¦.ŽQí1+‹àqKnk# JñZçvߢù¯þê¯Ê/Èø€Èú1«0ã7ÓEö\â÷® éUC ýUdu¢®Ì‚ðgSÿ®f¿ê«ü}!ÐÅ¿ÒËTƒ úBeÇrÙe—eØÔaàLSÏ9物eõ«_IæÕ÷¿ÿý8e¶"¼á†ƉEz¥4É$'hcT]p´ñ“áÏ~ö³i‘¦%ÆFÓ¬ÕŒšÌÛ#;u!PBEÉ~²;¹2 cÂ-Vø™‹ˆ¿ÊØ×ù¯kT¶Úž”ÆmëNž ÎHƒãã>ä-ïÌ3‡²Ô‚còÎÀyyςوÞ;LÖ{G<в” Œ¼@Vò"NpÝ‘>­Îêຘ¼'Þ8`Îqî¾÷½oNÂ1v¾æßûÞ÷Pe  \¼àÿüø‡´`øÖ·¾â©^gÓäv·»䄟þô§[Ò®^é’è Hõé2ݧ“W·êÜòézSŸÿì?–È& &…a2b!Œ$Sa­ÖÓÄ’$íƒÉºø·:[¤ ‡éoxÃrJEMÛÏk¶ðë_ÿzËÈ`Jã¸%k[/™©« ÙÀ“ uS Æ•"È¿Ääpà| xÍä¶!úJ qìt6†@‰­ì§(¶jÆZü¾øÊ 3W³‰SGgëºÙoàÛó¤“NjvÚ³ÁÚ-Vo~¤Áßú­ß¢>@Ñ Ä6ÄØNž9î›Çdp¨X…«BEª»©æŸý¼7ÖçÔ É ž®uÝOÉ3gª5¹iãBìè÷ÝQm©.ë`aÒ+ˆÀ7¿ùM.ŽŒìýò—¿¼D5l/ÒÛW^Á#Ñ †[vU؃U»\(¡1ƒ…+‘k¯½¶ÞöÀÊ" ›tVØÀ_¨+‹Z],²6ÑZ6ÀØ“äè¸Tâ1v `0lV¥õÀ$]ü›„e›E:47—·ÖÙò¿ÿýïï">?5ŠôjÍìmUÇOÌ"¯3•XsTá—j¬&GB7ü- z #°1J„Ë~D/CÏ ˜ ×((žrÊ)j‰¥ß®qy«eŒýBmÏxzqù›Üå¥íÃKçïüÎïÔ,‡ÞäÆÒàŒ¦h Nä=ê” êÆç{ŠUxÚ›™GÕërjŽÒÀà£ÉÞÖ¾(¬™ÀL.,%À…£ BªNiK¨îë` Ko9?úÑb«íÑÖÁáVùN³Ýl¯¸]€úïŽÏyÎs„½u–~饗 [*¤-J¨£d·ûܵþ¶¼£—'@gåÀ ‚œÎÕ¡¥Æ•5¡N×õé_Ì€%Æz¡Þb!Œ„0•0˜Òü°Æ[ž¤}*eÿ¶}w{ì±aôÃ?\cˆ@µßlTÔnÊd;/ºè"Ï~[kS;™r2’ºf¤MÛá~Èð¸Œç¸4 1íŽà8™æd"P—fFÞÓð `hœ¥Çt:K"PÂÃXöSBü¾P8Œj¢ËDº¸%£Ïß%µÇi{Ú÷ѯ=û¶ç ©kZ÷µgƒ‹4E‰y¼Æù ,¯Ï¹¤eà"⺠š?ãa«-$ºñA8wä€Ý£.ˆ"n¼¿´Yª»ØÂÒÃ[ˆ½¾ì€p‹ù?ô¡=ùÉOn·Z곎·Y½þáþ¡Má'=éIíòœ{¯ÿh~r'õŸœ%J¦äÒÜ–öª×…€.‹·d=˜®Ô­ñýO®@ Í>,1°GL£åòk™Sa- †ÍR2ÆkÕJ×EÞÎNÜÅ¿íÝ¿î»dšîw¥fsÚi§eTøËgî" öHrJnyÃ(|ùË_®I\ÀㆋÊV7’jûgP”†”÷gMÓÌ$ÐðL è€@ë%(±aRöSHü¾ä°q/§¾.©‹ý†ø’—¼d™ŠÖ«í©Ìñqƒe¬û쑎}ðÁǼãŸWÄ~o~¿ 1Ë@‹’å7¦ ØÐlÛà`éu:ÙcØ«+»8§Çl&ï›ßüæ(Qÿâ/þ"×­z§zÉoarçÛ²P~Î§ "³\qÂc/&{ÜÔkéO$ØÌvõºö:.]o÷?ª¼:Wëhñ:]׫ dÇØCdT!’×_ŒÔ®±fÃr^a?L˜röÙ; œ.þmïN´{¹c¼—å—óô;íº¾O|ÖÏzÖ³ÊB¯œ ”WƒÝÑ—0JCƒZÒkgm@ÛÓq_VÄ6ñ“H€X ƒÍ¶±bŸ Ûíâ_‹Æ6 ; ÃñF˳Ÿýlœ†±vŽ.µU‚ȨJ3_‰îPyæ™gJƆd`:².ŒÛÿïÿ½rhpù xÜY1±±A^Q‚lÄ+\C4G£„ÓÀ˜öŠ„L‹N;«ÀètZJT˜‘ý¤Ïæ«/.É‘—AW?¶pmƒðzµ=Í!ë¾5û"ïñ×2)ïµþ9ë|¯Î)®/Ò]Ó‹ÌÆ׫ ‚Zøº ²·N ]Õ­],Lz`Ó¸ð ³XQ®Ó†XoûÛ™«Dñ;,Mô¿æšk<º.˜6 Á.lŸ÷ºë®+jKóó÷ÿ÷½¥tà¶·,8lSV–Ø.è¾rߪ[£Q¢£[ýÏ´EÊ`¬’û¥1jw0Ÿ1˜ Ëå´9Lˆ!±åveoÓÙÅ¿½ðÞ*ߊ*ÊôoyË[žøÄ'š£Ÿ4„R7»¿þë¿.Æ0xÕ«^%#Í 6#mwè‹Ù¡¢Xîù¥L‘»S&’ª(D"Á½šP-R¥ñÓXui¸0@!w‡†ž·#°ï PBÂ#ªÿv(]ÜwFô–·Ô÷÷¸ãŽËö„ ¨¾ð…/U|³9–Id™¯|Áæ»ïÐ&¾…yio¶”ð×¾öµJð³Üw£fÊaÇ»åMîì&:1½©[unzYw‹,ýÏT1¢ê…U0ŒYÇâ:ì”UhŠR–‹gDL˜»ÐDbξDfÿv“i·,{޼"êÄí ³eø>{'Ç|‡×iHf«Ì¹y<7¬y'DeŸ DåR]µñ/ª£†:™k™ÈÜî€Èh€ [ípÕ´j¦&k¸G H‘8ËTÔÓtöqºì—…¾ð·K€ûø`ߜ滠ò€0 håñêÄï¢Eù`|Y‚;–1ùIŽÙ“ÅßÃ;là­1šŸôú¢Ñ÷¦7½ÉѽB$¾ßýîW…ôÀ¶F@WêPݪsu±°îŽ9ße—]Ö6 {` ¢{yê-¦ÂZá ê‡ åÂÉ‚E1jåÚ7]üÛ–ýnÿ#ŠŽ.ƒÒ€ˆI£ã.ûg—\rÉ mèÊ,²Óòws Êñ7-ìgš–cT³;îdVºXFö€M3ªÓLMÖp@,W§Ý;dß"Ll½ÛC­ï¨À£õ¨oûÛ€w½ë]ÇmÄøÜ.ãÛsҺφã:úœ\pÁšúœçŸ~ésŽ)ÙS1ªP?+óš¢F6â5hYj@lÞ2pI]PP+ ìÀ×m¨kFë¾Rv pOñC/g÷¾÷½õ>Üo}Ê‹ ÝÙ}ÕUW ²GAIšg<ãcgÑüŒìÏŸ±³ØXï-Sƒzûãê  +³zt…ª¢ù™NÿÏ–TL‚UÂT˜§}%ŒÁ°Y±\XCbË,ƒ1*vdܧ»ø·-»û™Ï|&†~øÃêsûÂå—_nµ‘ƒö¸¬Éb¤†ŸKÞ@UÙ8õÔSB'Óýà•G‘Q×”lüvÍ$•‡ µ xœEÓb†”öj¸”@HJ°xÑ8cét‚@É~¥J½™ìõÔ~ê‰'žHÒˆZŽæ.òAÆe|{*6lùÞ+‡-‡ïýYg5/ï½üå/ßyoШÁc¤AÄÌKƒš£Qš¦~«É>)ª‘ãŽKÊÉ;âA½«˜[_ :"éš™·$KGw pЕýq ðƒü ´1 Û²5´ó±Æ„!=æÞÑq–³Mà×J·úå?ÝG 09gÆí…4“³f‹ó Š ±%æÄ¢)ÓbÝm ×n’ÝÅ¿Ýp ²Sb&_‘|ê–…8w‰;¬{Ýë^Øú1y̘2 ¾L£¿÷{¿3Y¾³Î;ï¼qÊ™˜·½ímÊ·à˜Ù8ñ*+‰gŠ¿BL<Ú!‘*B0²Ç)5Ð[w}ú›†!ÉÀjœ±Çt:­ì'lùMÂb¯4oó77ûT¢æÍOwûU¶}»æMîƒã>ªÝLù©âŒO ÄøH¯ˆ¼7 NEÎKƒ¢šõ{HÎü‚¶‡ Žctàëµ··*VgIm }WÞ%ÀœÞ}ìÀæDgÆO–ó ¶ã‰Â# ®ÿøÇƒº~úÓŸrþ4:ƒx¬]'~ÿçÿü1ñôX¾ã,=fû"à ]¬ º»œ‚Ž÷%À05©b¤ñ¹1–+·ÉXCæpËaÔ0'ÖÅÀÛ± S>ýÉßpq=ã& ðÀ>ï¾à/H]¶.<:ÈŽÏ̸~™”Í=ôP)Ù¿ÒM²‹ò´˜'<á nÒ\†r«Èu­gÎÉŒñ,*qÖ‹“iÚH 1~C"cª‹ì6eÂ(e«áñ;Z»8Ïþó½Ô8céìã”à[( GÝ”øÇµZkIok&Еæ!çl‰‰¶g†¡¿ã›Ü‰.'óõõ­Ïvj÷wÉ{ó,4/ VÃAña`« Lj´¥Ê·‡°Ò¡!ûeÁPgé²1my+¾º¾K€c”zÌð™>ùä“3¨ë¸—EüoûÛ‡ñ²‚ðƒ<(Ÿ-VÖ$Ù¿6 x$D09d•RC`lÞ2ÎÞc¶_|qØ&®ëÅ`ƒÿƒaoÃBØi`5*1Æ“ ÄHl‰9ãT|˜ccÌ<(|g?vño›õ/Å',Ëüã»ßýnHÿÚ×¾&&ÜÌ’¡Ë¥(ƒ†¹õ2ÉZ…OBZVl6H¨l ² Íe–ãß8x5ù(™eYó€QÕÙ¡AL+X"5íBü  ÔLë§$E’'†Iàdì}Jöƒ†qê7€… n¹ÒŽN{“¤!®$½3vÛ4bf´=¯¿þúw¿ûÝöqn}ë['Wû×wýa{ض8߀³®Ç’56K™aà€èì³ÏW•<© p°'»Ž8÷ª¥ƒÄë²±QwÒ§äb€.Ô=°1| £€ó׬By»Üßã÷`ºÿ‰O|«Ã?¼­†iˆW¿úÕÐôkÛ3Š;oÿøÿXŒ¡”õú}ï{ßAâþ¸cйz\GënÒõý0C«l’öFA ejÅT¿O^Lˆ1ä®’n<9À¨ã/fn—Ç;ÏE ~ò¥ëñ«€€ÍÜ€ì>“¢§õö™iÔ¨· PƒÌîσW®ÑË`#M9QüÉO~2HG#0Ó®}ýÉ“‘¯ýë *3wÓ¨N¥ª– ˆ¤A°Wˆkræã‘&ç•D2‚krǨ’õ@G`ßA –þ­ì§ùFŠ_‹ÃøÃÚ¼÷™|éK_*+ÑLŹ®Gã±¶'&þZˆ:ÑG¸±è›~ök˜õïxy¯Er.i¹ ›Ÿýè`F`’ñ pе¯±xÝ¡SäÔA²¤§ûÚz_1Å],Lz`]8'1Šk'ƒÑÙùÈG>Âä>‡Û6¾óïÌq , Zéîë_ÿznl󿀑#~ ^÷º× h°6ë²ñÊÒ¿Ëñz`·?n_tnú]wGÞÉ)­·j¶ñ a¤,±«‘ctŠ!±e¼‰bTìŠiKN9Xcï#Ç€·øä^=°š8žÆ .ôkUœ³µ&ÓÇ–K€–~ÚÏÙ䘴«–Ri| Dº³ {岉|e‘Ñ–¼L8 GÙ³WÝfQ‘êP®j´j“¥Qš0°ˆ½¸·ûï¿¿Bü@Q•(‘@«Èèì³Ô¢ ûd×è¹ù[`&Hû2¹S+†òü0Õ†Ž,¾Ñ0ºI2ÒLm)„Ù)Žû,ø3  p@”]°ôEý)`Á›à`÷6Óµîˆ7/iT¡³Rˆîk·ÒRZKC1C—[XzxlÚò`¦¢ÝÃÏþ—¾ô%Ê>ÙîÁ™/|á ¿ÿýïWQ޵%~êSŸš˜Ï~ö³¿ø‹¿(ÆQLÔr‚=Ø›Ž‡¹“ñö &­×«ðØ‘”Hͯx 3 ®õÊ A4S°SÎ÷06 2Ï` sbÑL¡˜ëbຠ^bì½è ¤ Ù›?ù; 1;» Ô㼨îÙK{ëºL~V²ùqå•W¶PD|rÞX+ºMf´p7‡õ Z×µÿñ©O}*[ÔÏ{ÞóÚôˇeT¬B•\ WE†Âj N–‰ì\T8_5S±šo4ÂYªV!€ ´±*l¥éŽÀ¾€@-÷DzŸæ&~ÁÁêÄÔ€Š\÷™Ï|¦^åK¼+ß-þðÓ]b nòÉßÿýßï ï~®¨ÁÃŒqz~ |w=øºC§ˆÓA! >¬Áue"S€Âb‰.é3\{íµÙWÅTÖÜVÒþç^ÔlYÇÖ§gœq†ÄÏyÎsËGn†°aQgÔqåÒÞÃÆ`$ÓHÌ:09ËŽR ¿úê«g(ì¯vº8³–N×õ™å £µ'Â6RÆUŒ†c*¬%›Å[ Æóˆ °`Ôx‰÷ccÌœ1˜«²ì°Ç.þm›ÍeèîwP|ôÑGcV{Ãñ†Ìö£$7)s6èíç>÷¹AÆñ£ãµc=6ÓîAäݨ‹Ìikd¬u=.a2FFÙ©(*VáU¤ºÁ™Þd ˆÏæw{¾§™±MÒð¼Å {î+ÛÍ îeöÇŽÀöB ú“²Ÿ¶Œ~6e2ü=:O3sç²í› îJ{󂇳)¿zK†c’ÜÝ·½€ZjMk `iÖ6G¿ú]!o³=§›B¿ŽK]÷ y·®£K€cpzÌ;­±"ÉÆ„Ïq|·`°, ÇYÄð]$ÛÂÕO~ò“Ë›‘õ:Fõ/õÎ?òA·<‘ï~ƒ"kã%Ðd¥=r»#Å›×õ W8„%°&I±G,T» X ƒÉˆÙ°\N1á$ íæf΂9ìÕ×ë»~²Š•ìâßÊvÍ-s>fÇS–w“zz^.seJ+êØNΊaì:¥²´4ãk˾KO|â ¿qÊåcdWˆ©Àìá©b¬:S`?4§=LˆH¬áñ† ŠA àèæ¹úcG`Ç PKüE²Ÿ–˜~¥Ð%|Ÿû܇-ÙoýÖoMаÊ`Mñ¾÷½ïÈ#Ì1¾HÜõã¾=Å99 lï 5À?øÁ–ÌÛŸÎÒe:N÷U|uë$aÅ]œÄ§Gï|ç;µça?wjÇõ9úx%ÎË•ùFcË—¼ä%mš8/ ³—HG.?ÿó?/¥¡ÜönùnCLv¦ú§¼Eo‡utu:ˆ‡%¢M†Iêt. Ÿ­ë°`3ÙýÂx“¨7¬‹£º–ÆÞu ¶ÇümúîâßöèÊûÝï~øx0o"o€Ì‰¤£x¶eÉš&9s‹Ç[ ‚õÜñœY‡ànšj%® ã¥\Z¥! /Ï¥K¨ "¯FUs4VŒ†G8@fà ¾?vv<µ¸Ÿ‘ý€`å—É䦧ŸýÏ~MûÊ|ÃÞ຤¤èÇ}{‘lœ ÁüÌímO¥›Úá¶×YLÒ%ÀEíãñ]tQv„­¹múP0Î îzÆ3žæñ9þøãÖöaÇ:x\qxëš_…ð»EP;Nu¢­ÆJÖ­þæ¡ÞaoË` €=0öH8Ž:sG4F4³•ž<&¼†OúÓ>hùf‹TL?îÛ4>¦ tGfT}¡›t–™ç:Ý×Þ˜ÎÕÑ­OÑ–òb•.¶°ô°kÙ&g Mï£ýhNZÂQÏ}îsׄˆ ¸$À¨“÷0å0‡ /õ¯ýÚ¯)Ù^­\—]v™°åþ(¾f=ÁŽA@wW×cƒl ` ì‘‘»Þõ®Ø&ŠoiÜp,—.ãÍK«1?¬Žá£ul†Ã¸ŠíÓÅ¿Uï;¦qQzv}Ö€V÷;åÀ³Úö—‰HÒ87ÏtI³ykÍG÷Mýò/ÿ²Ò\EŬ¶t6xœ+íê5 ©²ÄU¥À\æ® U²%š£M+ M“æ d|íè¼ã2v†KRÒ“uVZÐGökå½EúœÆˆ½OÆi•˜SN9¥|½xÙO€ìÑ­û¶¤÷ë0°$ðêý¢³tY©áéÊlf{5þaƒV,†éà–ôì VÊ'm´ˆ1¥»Ç<æ1á"_Òœ·ˆœ'›ŽO–é2.:~yЃäíÇ?þñ¬4¸y‹fP4?£çœuõŸùêúÛ‡@t¾Âa†p&‰G@lƒy°Fšl~>cÅ5•ÎÂuØ;«nÅb{‘™o ‡ä¿º‹“ ³B‘¹Þàîw¿ûØú.÷ªG/"1oýßÿûÇ ðùÏþz[ÂIfòiÊ‘¦%Åè({ÜùÎw(XÏ—/±,†ì ‰Ò¦b3ŒU´Ÿœ¥@~ÒC^ÌÊÓü@–U  \À¼ê‡€¥|F±%¾Ý–±¼'ÆÇ2ž~[ Á6jÐhor7pÚ¿j¶;ä­m‘.ˆƒ»¶kÖ­º/äe_<ñéhouý$K«`žo}ë[[ÛÀ^ûÖ"ÀÄ4knšo\çÇ €¿¾¡vQ½ÅTVÆ3DÚrÊ=yÿèþh2q” ñˆG(yHnú¶TÈQO–ÝÙ„rì3YBÜÁä8 fÀYIb•Ø…y0Ò$k… 1äØ>¨Í•=ìɱz±½!ýg:Þ¶Y¶o¸‹+Ýw4›óÍ>묳„ib{“S¾Ì¡n/‘,Q‹¯÷Æón¸ÁY¡rÀ´÷ö(“•í¯ÿú¯{eº¥ª6˜TÕ£’e¸½ÊH7 Õ©´r-Ð(MCIœÜ|ñ‹_NóXÆ¢2%f”Å—©¨§élGÈ~6Gp{û³Ü>g.ç)ÞÛ­ð>üá'¥=íu±xîÉ™•·d 0i6 L¾a\}šu„îÐ)Ñ€H7U—éD]©åúEGgA¯ë1@„£):–»¸ú °—(¤°“%Žº÷½ï]7‘à¨RØa å-‹høüç?ûúœf‰×ú%£Ó¯Â´ƒ—ò,úi».ª«Çïlbç6KÔF†‰kúÄOîåb@LÕbl‰9!ŸFeé‡áëkh „Üh€l@nQ¥[ßÅ¿­B~©z[³ßã×¼æ5¤äÌ¢-Zõ1ú·)âUâÅTÊ¥jÚeG‘ÌâàÛßþö8‰Ž•˜HQsœLŒW)JbY&eEUd*å@Ôœ,³Ô´4YcÅg›01$ñ^I ´œ„´~ m‹êáŽÀNB Tø°:M‹ûÈ{m¹‰Æ 5?wQãÜ52nÅ’ÖaQyYHdÆ»âë^÷:1¿û»¿ÛÕÃ[‹€îÐ)º&«–ö W¼®Ô¡º5]©£u·N÷ˆ°AK|Iƒå5´k¶øì#aVù\RØÙÿýÃ9¾ÑõWÕ"ð•¯|Å+)ÛÈ ò“ŸÌFƒ ÷ˆ¹„­Ò$1RQ–ïí…ÆÑÁÉê’K.äíûº'`ƒ¬î„[½6lÙO|‰m-2Ï+Lˆ³S†9±h›¦Âa~ì]1˜? ZåQ…“²LÚÄÛ(ÜÅ¿Õí¬Ë/¿·ù|ðÁ9ÝÊ#©éä“OŽ6c\*g~<æ˜c|Â>óÌ3×Õ0;÷½ï}•ïÜ`~wßíR9u$n~úé9…¯º<ŠŒ$&Y]EU Ú€ŠrL¡êõn¥h j5V“5õÔSsã™ûµ³•À׈cm+é0É ªq)ıÉ?üÃ?H½‰O}êSãĹXQn4©·(‰74pÆè-ÕƒzÛû ”gNìÑ®<1·~Øi Æó z…!sš‡E'u83ï,B±e. Wˆ!ÓR2®}•cºø·¢½ƒ¥îu¯{íbé[ÙÂçëÖ—ÞU$a»ÄûÆ?ûÙÏŽb4ïš™giB¯«U´œ³)b}¡ùì4¡CÃCòR§ð˜x Æ.XÆÅª.ËJ¬×/K¾5Yà €´Kp tchìö±c {LG%ûQ¨ŽCà—¾ô¥-2vX¢hÝïfpCàOÿôOˆÁ‘Ñ~ûíçî>¦eô^2±HɈ¿6_ûÀi!Ýò°îÈŽx:Hé2§ût¢®S¿t´N— dK ¶Þ°,X(†]Üò^ÞlÅæŒN×Gxs¢ò¢½hF1'ÉÞßô¦7…ÍßÕ\Á–D±W^yå !v‡ó±\çàd&L‹ á~AxòTgPZÜÁä”3Äõ–ßåŠì4>rÀxÒcÂàƒ-s¶ŒQ±k f–c·‘mØp0(rØþ—ž öü‘I[ÂJ…»ø·RÝq31çœsÆŠón·äÕ Š”ò'R^‰¤ÉHxšfó¤êf•0؉o%†­_ÿú×ogy[ʾ2"ø…Z‘^Íd¼Rilj‘‘ÍÂA‚Ešë oß›À¸ñ?ˆÕêæ&à ÞEÅöøŽÀ¶C •ý„ã Í^L5¤5ö#ˆ÷™èyZè2µh{ô£eDg«¥.\©b{`ËÈ?:¨Nùt\¨Ò•:T·¶£NÏ ´¦€Õláa¡bª.8;2ÀŸvŽ5¸åá8=áùÆfßÁÄ’d˜í…/|¡¼VÕŽDÚ¼áÀ5Ê—¿üåÚËèD³ÉZ? ç6¶¥õð¾‰@\Èb‰° &iqˆöókµ¯0žxLØFbÑìS`Úúäaf)1v›r64 )ý2d Ÿ øØ—¼É1]üÛdÀ—ªŽOË66'pïFm6ꎤ¿w¼ãÙWÛŇ?ûcsâ;ßùN›~&ì“(aßÁ˜™ÉU¯x‡«{9S½Ç ¸ŒSu>?ˆYþfÍÔØ¶íÂKðT‘*'Qq— Þ8môpG`;"PËtÇ5šàPÝX°~Js|™¢îèÓuá…qăã±mû_þå_Љ‘ÅÊ–ç6ÝãlÛµóÂ:E×è èG¥Ët_ÛÒq_cl€²4Çe ˜ó–¨kI¶‰½¥¡‡Wû­Nزæ`#¾1ÆWLMR÷ÝQéTÎã÷8læHd|¿n|¾ñoT9.±ÌåR9HqX]¯¢ª§(ßñìh „ÃJÙûð°ÆÈ„ÖÚ£b!‘a'¬…Á Œç&¬˜0jNð°nÂ{{rò1׉2ñLcJë:À˜,v3#oñÉßÌŠ{]3DM1:ŠÙ]ø»¿û»JŸ=67“ˆaá–‘¾¯ð¡‡Jz¬Á\…Ðf~üã/‹ý’WHm–q˜‡ñ¬,•#PÇÇ)çcM$- Ú¼¥i8hrÚ‰s+ *#%¦žhÕ¹Þö@G`›"P ô’ýÒ¬œ|ùÊØÏé77H1ÜÍ`ñ×>H$=áBÀ7ì_ÿë-&7qrÈ!ÂÝéKá³j8€I7¥Ët_»Ñ}~::»Šyô3`‰¨EÄ0K%ÌSm,ë`a²3ü[äœÍ—Ô²/cv…ÆòÛd{<òH,ÄèÔ9[ܪý×ÿú_lj³·[›G¬BâE<›×ôq*Ë~ô£r¯Ÿ+8"ª·=°#¯T#>± †)L¢Ø¦Â`e|„ñ0*&¬”À®) cãXPcìJ00LkÈ8†O–£ÔÀmÌL [þêæOþ–“Ò >Àvpí%ÄCI>çtxòOG ü›¿ù1QøÄ…~8þï|'Ed;Á‰QE <Ý’”ƒeÅI†û¯¸âŠD®ë/=éÚ8Äô~JËÈÀ`Éb‘‘¡ˆ0ä ri‚†hN¶·Õ¥™«Éù–¤É‘8USÅÓ[À¢S9 ÔÒ;ÛZšd?MÈš>Žªq»µ{k›Áòâ¿8BB«á't’Äe_³ïÁ2«¨}q½¦Ër£N,j«u·NÏcýÅØ“ˆ Ã`žÊ+PlÖ%À–mvvË^óÖ Ê–>ëË4íØcÅ0ŒHs“…º¶a=®ùó-fŽíb^ß>ò‘(Á¶E¥?úè£Ã– »»:”®4=°Ï"€°ÆxîsŸ>Á0…Fö¿0U b³Øþ`<‰1a¥l˜6›bØ3K‰÷ڋ†‰Ä†LD¹ãÚ°Zòü|QÉ›ßÅ¿MƒzÙŠ²©–Ã=yòµÆ—ÉÝE{yÌú rXTZønÒ{ÛÛÞf0ĸß[ù„'<áŸøDݘñãRËö}Yÿßÿ»êª«,”lÏÃàXÅO [ ^I°|i•1¹gy‰D0²Ÿo•5JÓ4°. Ôðøkò Ég6u|60ó6ǃKnóy=ÐXjQ>–ýùœç<Çpð˲^ÀÀ©aâñÜsÏ­¶Ü˜î¦Ó¿òøâ$¿®ƒëN_ « Ø,‹!–NÔe:NÀ„©+Cí½{SÿŠÑõ‰ñKÔg¢Xó šYÌÖ%À2Ûî‘ûŠÜm­÷}k‡TCè‹<è ƒ–ißBgÇÖŽC®kŸÌ˜UAÖâùòZÁ_wÝu§všŠÙÅxÌ/>îyÏ{NØ#÷Y°)0ÂuàœO¦ÂZÙ;È*ãI† †u³efÆØ‹R¶ñ†‰b ™Š4”ÂÞâ ±·I•ek]üÛZü‡µç†"P¹ÓÌó#ùHIiEFw‹¯Ë䌦n›ôeBšõ  Ýú®;ã“3³?ЪGu²Lè¿ú«¿:¸ùÄ£È 6ÉæÕ8‡ßõŒ¤x¤@$RsÔ©@MÐÍiµº«„xÊ‘ ‰Œ;Pp…z[j-àœÙo* {`!PËñ±ìGðkÏú\ŒY— d­?pâbhø¥ùñøÂ"z€±Ž¤ßF@í#¤ê =˜ÎÒq1Ä*0»º÷ßú¤/Á{äöÔ¤ÄyUƒH¯B˜Äëò&Š$Eo …ù!ñ3›‹!ïÀ”^½q£¨ÜZ 4 â+µuodé¾. ŠèlµÈ~ÌßÙ¾Æ"cÇg*uv:ü_ÿÊ“Xdì}8íäþ7J!e·3ÈØW8€É¬«ãt_V?:…Õ¿-µ¹K{`•¬“’#a§Ö-G1^—[$·EØgŽ Lv…X¿/ú.磹H³ZzÁ„Uðϼ­‡3ìd7«s&uÿ{Œðã<&¢`mbðâÓ\Õõ@G È"9L†yÚÓžæm·`ª¤Äf1ƒÂx9E˜?ŽÃÆù>blì]ÕM \m°L¾5¸ÂÛ†›A·²+Ì.þMvßÖD2cÃR•V¬Â:ù<÷»ßÍÍìÙ-Cbéw-¯iù’—¼$ŸöúË&{9æpƒ½Þ1š³¤p–UÅ8MÅH“IYÖÔ„V5çœE›‚«ÌùÀÕW_Œ¥L’ï Ð@çÛqäÈ„`Ÿ/¹¿í¬µ/ÙÏð¡‚⨧=ñ{Ѓ”=È zιßÅ9ÏØçm񯂦õÚû²—½,ñÝéËêôþ %q£Ëtœdñœ0éÇA^l3aŒ‹ƒ$Ã6˜'a1ÖÂ`ù@ûu p€ç*?R>øàƒõ¦ó^‡ÑÜ$8ÞàÜp=ù6‘o}ë[ëܘÅÔLJ¯hë¨×VBf§÷¼ç=•>#®‰¥¡õ“›*³ _×õQUfìxr­Q˜ÃDY a$\„© Ì&ãeKtRq¬ Äó­,Ø“·¯aD2ƒe_†˜–abè•~%X…@ÿV¡n¤ß¡04'šràö‡ø‡gõæÌìõŠ{v”LjCr¹#øšk®±JÈVŸüU“µ&%IŸü\ü%ÙcûXÒÔdùƒHÉ$ÞUö­dWÈ GÕ©´´>$F‡È4°ÄÝqö6©K®Ü ¨ û@Æ6½p=øƒWý±#°‚Ôâ;²_Žû¢æ÷×6‡Ï^™÷Г¡P}â‰'zÅ7ïßþíߎە¼åñ%«Ãxhðj*âã*zÌÞF@7¥uœºtbù€Iü˜Ì{È;‰1ʶcòú‹ÍrXLØ%À1¤+sþùç§—õïš®¹s×Ô3žñŒE ‰[rü­d_ÏE)Ç$Χ¼Ukb %Þ†¯uB/þB*©rÍýèùzûۊƈ·¡°JØ a¤œ`­j{”¼Â~“ß¾J)xsÎ8‡7@$0XÚìã°áÞFªa8N°µ1]üÛZüo®=WØMÞpþÎÎDvvi*ßñŽwÄ~6?n.eqˆu\ÆÀ™gžÙ¦â°È ¥H­À»Üå.¯~õ«¿ô¥/%#ºŒ kDZµ@[ÔdX–Ü ¡2YT¸*T´«7þA2ÊYŠzûÛßî²'-ÇÕeHàÄ\]¹“i3æ.Qà·‘=ÜXAÚe÷yççL¦|üÒ‰G\–·Çw\Ì¢uüñÇ»àÛ6¤ß"7zƈ_rÕ1vðNÈû:l™aL’nŠ:ƒŽËÛò“þgƒ%˜«H¶Á )†ÜX+JVÞb9Œ‡ýÈ~mCôû']…H.ÏzÖ³Òû‡~x9K›¡íÒK/•þnw»Û8 «Š£Ž:*<`w ¶'ŽVZšq®áËõÌg>³}›x[üºyëC7æ™v(0·‰{¸#Ð"İ ¶É FÊ–e­3“ãa0¿ySRlŒ™­616öηÃǘ¨­]ØQà2¦}¡·«þ[Œ­fß ÌÍìâßæc>Q#ß•¬ö}†c=HqöÙgã|é×0>À‰ñ—à4H?~ôµŽê·Hã·b,xÝ<æ˜c¢}fõíŸ%ÔrðdöùHK›HYR¤ Õ©TÕ‹–˜q†xM˜¯ÅÛ±‘rA$\@K Ç%ìÀ/¿©ã4=¦#°å”ìg%«|#(kqf3áp_Áø¼öŠeWT\bË´‹Z‘Áè/—!I¿/bfr-*­Ço:+]YWÚÄL"Q•\˜$yc›˜Ê…²´Â`جÝqÀ„Ùìà"`·<>«d»¢ô_–$†¯­‰e`Ñ÷Ãþða{–°/P»HY-´—ja’šÓ[Àw2<Ã)-+cçÌ_ýêWkG-Z Êïû&Ø“dZÃ6˜'ª a'¬Õ‚ñrgŒØs54–N^LžýPlùÛ Ä0YRíN^0‡+uØÅ¿¶[·,®ey?IAÌLñzüØ:¾.Íþ0'sU¤½lèræV‘‹¶@h=ýéOo ‡ 0–$V_–QöšÜ¯pU¨h~ã0D"[MlꌛK« œœs–+ÞEæì`—EŒ ì1U@ÀK« ]iâÑդͫ-«óú2÷2²mÛEÊìûŒÝ½´í’Ì/_ÑQ±3ß˶„^tV6t_èÁ1ØÖ¿3FÞ «È"%Š!¨ŒX+Åb6,‡ñ°ßsÓsv>™w«^z衺hMÛûyqç°âÍ?¹M›Àå•WV|œp ¼×[‹æäBÃoþæoV|O|âÅg™Á¾ÃAŠÇpà‡>ô¡AâþØ €IŠa0S½b'¬5HŒý¼õÃY®Rbc Z‡CX=;\rµ:9!7L*ï2ÃPù†ä2‰7'ÍÜ'as(èµøÐb óà¢/(f üœ’ñ]Á¯,s;ì° ¹`É®Ã2ÞlS£‘W¾ò•ÎÄÔHe9[ ÂV44Î8ãŒÖÜLÕ’I,K–JPTÔµ®Š˜Í”P¯/»†Ì»œ…d~PàZ"ÁX¶°ç#”Esûª‡;[Ž€s¿ºÕÍòÈ Ÿf¶?WG䣛—1n|Ùø¬a%Mnp†³è›§uñÌnŒÄãKÚGê·¼ù€u!Ý ÝW¹âFÿÎø=Ç97Æ0uð‚‘°Sæ íb,‡ñ¤Á„X1ëu…cÑv…Tµ÷À"+¦ç=ïyë¢a`×äh%ó³üõT|tOêÔQ™‹¯©˜cI9 !ês8‡ GìT7¹1X]fSxPZÜ×À$±mÆB˜ ŨDZêr5¬ˆ-'õ9s¥Ä€Q1|dB@^Ó>v²/ C´’“o·$²‹[ûÍ•:˜ŽûµSN9忨[†rÕ;ÖqÁevæžò”§ÔŠNÎ-“ßüÄndÅå?¬òîq{¨‹„a.^w`Í­\7zE™ÓóøÎu‹ôJ‚Š’Qv…(Ê’¢"©¨, o&zA(ƒGQñ=™ ªóˆÀU—ÉÖðã¼À—XG TÆ){LG`3(OüÉ!Gí¼pt…–xÞ:ÀɹM‘w/^Í«~Åã‹d%7ú²f­ 'Ti=°-ÐeºR÷ÕZ·ŠñÓÑ3M°U—dئM†©J4̆ñ°_Ò`Èr2D2ì` Ý–‡ÙÒëÓ_ù•_Y%9ùWÃÏ|æ3ÿìŸý3…°Ú0 Êápß«:jnßæ[,oøÊ¹qûsF#ÉÑ´+¬ÝË—Áü ´%÷ð¾ƒVÉ”…y°FЦÖªÙ/hDG+†™'×ÌaûÜ!Ñbˆíc%¯áàÕ2ÞqÛ6 Q;po1N¶™1]üÛL´'êŠSÊ;ßùÎD£‰×ÍUïXçCñ×nD¦õÈc‹¶pjµ¨Y“…·‘Òœ~úéYöŸZÝ$ãHÓµéGqDŽPb˜=üá?÷ÜsyÎôð˜iÝ[É$–%®JÛºñUu*]’< Q¬Fe¶&œÍÎÀ¢lÛ4), &…*øº@šù…r¥ïŽÀ& P²_oM©”Æ:QÇ´¥íÙ’w/ÞúÅ_Tû¶Â³ëx¿"ã‡]Æë¯¿¾"{`[  Ëvõù­JBCvbtôŒ¼dIiÛó Ûê‚Jƒý0aÒ`K1aÑ.pÛÚGÖœx,úbN’Wwšaƒ|L}ÖÇqyãÔÍfƒr^õªWa üæ+lU »•JV*9QLæ§d¼ÁõØB©æÀ*å>0^X¢`‰‘0X›7@XCæ{‡EÛÂØXÆW¼âƒx˜ßðÖp0(²°\dL4Î.Æ”Ý`\f­;YÂÞˆìâßÞ@uÙ2©#þÂ/ü¶˜¹â&W½gÓBJÂÎiU|¯zê©ãú°fÔ,©A/’yÚ\\¤<øÁV¾ŸýU1¹ÜžÉ ‹Ø8G’ÅŠÁoWîÝQx%Áü%›ªÈÞ¡\ªF@KÏdXCb0 i“cJ , TÉ¢0WÀOžËdtÇ"ÑÉ\=²#°—(ÙÏ’úÚk¯ÅÉF½»G=êQhþúµÚžE 5•¸{Ég.t½èïUiõ*ºÓ>“ÓÛ¬oÚN¯.ÖÝ‹’NÃ`žÒtªôÑ<±"†Ä–˜‹bTñ],ÄV!wš|º.O Õ¸[ßúÖº2š½>Ó“Êr Ìæu]´›*r}±¼ÙDˆ«‚ÛÏØú('KˆT£Ûž–§³§ì`˜ÌEX(hd¥‡ÁZpâü3Î °e{páóÑG­¨E†@Ö«Ék€,m½ŽSå"²â·6ÐÅ¿­Ä?7:PYž!"W½s.#¾ûßÿþcÏ:ž1¼e øØ/ãŒh#+Ê5#ˆ¶D:.ûÄ'>¡üÒ= yEzµè0³-DXu!Ë|kŽòÕ%—fJË\X@äm¹Â €Àdiu„’'ï‡h“õpG`o#ÐÊ~ª‹Š]„ºŒµ±¶g¨ú_ÿëe$ $¶©1I0KZo®AÜ›­œùyi²À¹ dÓ‰uÍq&bá"óéúOÿ©d¥ê‰…0Ò¸9• e†!1§”-Óv-Ð1t[sÉ%—è©õñ–oa&ú3ddz¨]€JSkëÒ¡ë¬JSG4‘‡Üö¶·E$ÇŒË,Wªœè`˜¸–ÅB9XÃT™šr°ˆbYZ×ôEϳv(’&ûªó7Ž)ÜYø1É6וko'îâßÞFxaùÌÞì›bÁ/|á ‹åȘh”‹¨Ü¸6ÄWÙn«ŒùÌ—æÂV5ã 댭ï{ß‹3žæúrÍo¶ÁÆâŸY]-C‘tŸû܇¹ˆŸ@é¤I ™ÄkNè*ãS4 I‹ÐH¼FÅW3–èQZ p@(p…$FÄQƒÑ2ê”±`9ORÛ؃´ËèÈ~ bs¾=§¹)ò ÌgŒ!kÎð³½R7ÆQ\;–k¼Ê=¹*š_ùU9=°jÔ¥nÙÛ¤£uúø»{>°Š¶`›˜£c¤EªJدuþY-ë®ù5Y5Üv$=ä¢aôùÏ~™J_÷¤•¶ù¢Œq軟¾­9~Ík^SYbZ\7hS?®ÅÃûÞ÷>ÉRm×땽:óÔnBì•0U¦;lVºîÑRnŸ±¨dØ•ŽXÊÇÆbRÈLÑu—Ò0YRQÙГÞ0\2ýLí{öUÿö,žë(-~±ŠƒüÙòZ&É™a?üá 0i»]Æîqÿ€<`poÏ d.޽µíÿw¼ããCëò¢‹.rge>$j÷SûüÁ0·­dE†°$“EFÙçuPS$ÕÅÓmÉmXÓ4Pù[#\‚rÝ{?@E~t1Õmim8[Gº¦ìáŽÀ¦!Ð. #ûQºË[P.·³ˆž0pÎA+/i²W2ÞÔ̲Œó4¥¥dé ÒX¨‹ùØÇ>¶¨–¿Êè¸t¨®Ì¬›G_yí2( É1Ny½Ãc*ij'":ÆñJpõÕWK«*1áŸ6owÖDÛ„0’Ę ¿%¦à³§9°ë‹2'¦Í)B>|Xz¾ÆœUd€,Ë\ãQÓ0œ/yóßvñoó1¿±Æ8™õ‰½á†QÀˆßI”_,£ðùö·¿=>-Ù¶Ex+÷ œ°Å–Šã¤¡vUD|ržc1‰gh•ÌÖï_ÿõ_ÓWŽù¸AåG)ÈAߨ2¤r% dQBKF…(J‹v”‘MW„!o^|ÕÀ$Öär‡·`‰m$ À¥ö@ÆàYˆ hö¨;²î™tf=Nßc:{véÙÏ]—ù’aã˜q½4¯n´Áýÿþ¿V# gAï/6M¢ ˜­–ŒPæü\ bÚ•q]=feÐqº/0Õ¿º[X×:7~_Úó^,t#'M¹©†GT[*£æjÖ–»XpmUà /Ô;¿ök¿6O—Ý9±c›-fŸìù,ôk”ìEdž„¾Ï _øBiN;í4Eår6åûc-KgÛYI/1Õˆ@.ÃHØ Sa- –³ ,ì‡ ±b FÍÞ(ÖÅÀ9¸žw{!{ÃHÊ7d&à·zj7 ÛÈUwño zÁvnè¿G oFnÁØØ‡«lÍÂŒ¬òq2L|Ï^ÿú×» Ò·­lºZ´ÊÝ g0m|΂úЇ¶‘1 }Üã—ÈŒDáG>ò‘ G°ÍÒÃÛœÃèJŠìê_aî´ÍÁ"ç½I$ªÉÍ>Lˆ1$¶ÄœX4µ`Z¬Û2s—[¨7?Ì?[Hc ð"Æ«¨‚;ùÊW¾">¶ƒm‚J_ÛÜæ6ú=—Eñ·1Þw~ö³Ÿ-}Xç*Qð‰‡6÷L*$:ÔsÆ ÉUEtæÀ<ÑðŠ^C®0 ›a9Œ—c¬8(»Æ Q3 ³³fhˆ7Lr`hàÌ,üo.écPã^}ìâß^…wºp7¡çKÙÿ®2ºiºÿzlG`O#Ð.—ÿîïþŽu]¯òÏÿù?ÿ⿘ £ >Vɳ _î^¤Y‹e{T|<¾8ò*‘†YÐç;j_é{`Û!]™Í‚ôoZ¡ÓÇ>`ÊïË ¥¹ñoÒ LÔŒK—‹bÔT„u106î¾@xnÕcºÆoܘ†«®ºj¿ýöÓw®É­­¥(œ»­wœ¾ÉR[^É6ÚW ?ùÉOöö=ïyÏÁ,@aøþ貑} –ùãŒ=¦#°<ñŽ‘]Ëlnb³h§c<ì‡å°â¸@L›KÈ$È–Ä8MÅ’‰1Xr·ácU²6ïƒ`¹"á.þmvGP/Ì×—ëØÉé2Å_Vó)Í%îQ és.’‰UIö÷N«£‰wK rÜ*[ t, áܲUYféaû6æþøáæc=ÖÝ)‹5Çå/£@Å*<ß›T§j”µ’¢‡È¨§"{F![“ó(â8±â—BrkI]> Æd딸½ÖM3ª¹ãŒ=¦#°1ZÙÏÝ$1ÐÊÐ`†Úª£DyÉklºR15p÷2 #k¬ìP¶_*YêŠDZ=ÀJÖÛhü¦+Ó­º¸ˆø€ÁÞfåTiÀ{“n`°_ŒÌ[;m‰cÐvÂÆ˜¹K€H·ä1×¹½z\;/>9do©äî¾rêSñƒ@¾ãŽM&ˆ%þÝßý]ü!Pâï|ç;ñÃñêW¿:Ty;¸¥mPE쬉ʴƒ©°–06ÃláϰVœ,ç¿ý·ÿ–Cì5µã€­½3ЉS ƒhÒ[®ŒGÕdÕ[yó'akéØwjçkf£ô¨£Žšl¸ky^~ó›ß\ibÚ';#ŸË/¿\àÀŒa›cè= Α¢wq‡;Ü¡3s>®åäG›ÙÕê¾èíâ²jß³U¨HuQ¡ˆARÙæ!Á^!~Æ-˜†çip@”2V”3‘à]ÔF"M:HgUÞèì Jö³‹Q^Åp m ÷Áþ‹ÇlUþÙŸýY3v÷R¯ÈYP¼¿¸­[áñøRÉ2(2ˆ¢ 3Ь”=°-ˆÆoº2ݪ‹‹rjíÙbÈÕí¿/•,I70ØOXqÌŸ˜6Ûva*,í¹~àØÍ|¤äfOS¿Ôá^jç&1¦w.Ô;0ãÔƒÜhÏÐɼ?½| ',Jv衇Jÿ±¨Ï,Çz*LUºEÙ{|G`I¢®Œ©b¹ŠÍdŒ—°V\TT¼³`T,½(xÃAšX8W2'÷QJqf[¯ 7ƒÎÐ+EWš- ÜüIØB"öªëj7"àHœQng[¾øÌ)sû}eŽ­ýèGîÖ“ ûúd¤Eçc—µ¦dõáNsÇe®¶t¤Vi;Φ}Ñ€–¤½V©ª Bò‰TÇXE¼†hÎ$%šA1€Ÿ ÊIi¬_’ ˜ÑiQÇEéµë$;í©™k9ÆÙ{LG`]”ìg»!¦ç• ®Z¯-“Z]ÆB"ËÝKœ|´)+\Þ_l”Dõ =´‘LùYÐg;ff/©Ší•E@÷éP]YzM[j1€Ì åØïK›Rxì&߬8H™G¬çþafŒÝ´.Nµ9‘9…kw“u_:è¸ãŽk—¡ÇʇèÇñ«$8ûì³%H 6mµ"ÛÓR–GÐðäÛÞö6‘~«vö¢†ôøG#…£ÂZØ,ÇçWXqQb(fÆØ“É „Lkƒ=‰½2ˆT¡„vVÌyâSÇÉZ63òŸ„ͬx߬+›¹5*§×ý9òh‡f§u=7%„×}YÝSG Êw—7Ë3Ï<óï|'ÊFßW™åÆ\°"6©È@ ’²Ö:¤"ÙˆCNÍl½#!•1÷XD=i0ö@šƒÐñÅ:"®ÏtbsU(ªŠèìAJöËÞ$¾ÍNò<âøÀ>°šçÑš‰0ä!²Lõ¯ YOh”¦ià Hà€(»Œù൉d@ÝÆÇ>X§¤pÝ”“5ÍßÛBz¸#° d¿(Å…-Ö³õHB›äí¶Ì8€q‹QF„|ûvÎYPN×ø”Ç—JŒ²³Î:K¸ß{YÈlß@ÌðthÍüƒ¶”˜0&$–ΌɓQ ˜ÿüÿ1™¥Gî=;ì0àǃmš‹/¾x¾®è™sÌ1m²ë®».7=Æ"wʜԑq¯ZüéKP~ݲÇZÓṈ̃a[iw–A ‡xø- †Ù’ û‰ôÃ{)“{ß-ñƒÉ±z[© M«ÛÙ¾­°aUµ 4ƒ®^­Z`â“°j$î z¢<ãØŠ"¾ŽqÚIo8ÍŒz'&Ûòá*ñ­ äq°3ç ícŸý6oý|zi<ÛüXónÊUÆñš !YI¤iš©±ƒµovhZ¡Wâñw¼)¤TC£º­;Z]\•¥ù¼rÝ*C×i[5œûE-úêä Ðc‘±ß˜~,3wµ®'kcRËíoû±}¯sòŒ¿eôÛb{xe(ß,Ðuqô‘Z‚1–ð {´ñ‹Â˜-{jد'¥O<ÆÎzH-Yf©Ñèg€ó¸íñ·±Ö>;ùEÇ m¥Ÿüä'%¾×½îU‘>Ä™Iò‡d&QŽ4¿ú«¿Zi*Œù—´_ ô|Ê»ægÁÕ»À§?ýiÜX«ÄÚ\À~âÊØr\– ƒccoX½]6" ŠqöAŒrâŠBúE–„ƒ,[òØÅ¿Í€ “øVÎîB[¥ãib »_ÖóÎ;»ø~øámš„#Ÿä;JŒ‹Ëø{°ŸAY™¶dñ½Bî}ï{ÓEVõ¸¨í£9¥i»pºñ&k¸ægS "99p1€79[¡ƒøº@G”¶@¥ÏâI÷í0$«=°™”½Æ£–þ¤P7cì7&»—lñ0U¿ÇPó‹àãÿøñ[c1~ôÙ4]Sp\HY5¢ñ«CukúwÒ Kx‹=Æ*Á“-Šu4öÄ“ &#ËÃÇ„A¥Ýp«½iò '¼ño\¦–üà:š2[$=–TÙ­¾ûÝï^WDX+sì½óoþæo|‚e6S ^TZ®×äš1£Z†¼ž¦#0@À\‡©Âäþb¶$À~±"†Ä–˜s1Û©%ìaoL. †¡!` ÈnP òN>žvÚi!à›L° ‘]üÛŒ^xÊSž‚Z”m­.?ð–m[t;…ÇjÇÒGñÆ[?&ªQ«p&ær‚ÛÞö¶‰÷—sôÑG×Ý mE;)¬šwi;@%oàÄŠ7¯@7n;óìÀÖãdbâpU'N¾í‘%(Ù¯NÿpÝ2Æ~mùåîÅÆ‡/ÙØ Y›8áØð¨ËáÞømØÛÛo~ó›þv¿/cˆ¶iL,îÒ­zvò”ó^oçmZ‚@œ¾d“Ñ´¹®= j1T—_†@—7™µräþûï¿d½9±+êèØ6k:®sÄ;T±,Žˆ˜bÙJDkæ/xAL¡Êz"<™wÎ,ILOÖX/‡Áê[†ý°.VƳRXcãV£“gŽÂö˜Üqò”{’$CLzÃmòíŠDvño¯w1Ã÷;.RÓ¢“³Aìâ7é6[¹ÞbS]è”Mµ34?Ÿð2ä^oÞ–V ±µ´m`¸q›'w^.¯&4@]¹tÁ"e¤œ êÄI±|Kè•oJös1Q< ·Ÿœ5ƒáËÝKÀ¸ôo>Wޝcvå,h8 úŒ‚ÜÿÖý¾ Ú¾ñþ’nÕÅ“›9ï {Œ•SmÏ “/³.V\×ç«çª7Ä w pò^}tî—Áþ¹Ï}n™Šr î[ßúV›­2Úc»ƒP» U`üÍZûº~I._áÌrNNR{¼ QÕ«,=ÐØ#Dÿ³VÔ9¬Ã~Y bÈ쀴aßøÆ7°åØ/(VÏÉ æ7¤YtS÷€rƒ+|¾ä1û û¦=vño¯C}¿ûÝ+´Ü6®²Õˆ˜¼Ø î¾Ë9UxË_6ô?÷s?'`Ÿããÿø¸ä}!Fó§Šò¨ˆ .Ž¡È݉IYzãdbò=Ó•“o{dG`’ýl æóc”‘û—ù—óyë­oXδ­˜-»ãÆ娦«²üÃ?üCªãä#cħ®Þ ðÌþ÷·û}i‘ÙáòþR]¬»Ûve݃1N9åi° †i´al–ípŒ‡ý0¡,2+û6å¢0V—Ûgõ¯ºì‘w pb{<žŠo˜aR|\Ýgœ!}TtÜ¥—^:N›¨Ë/¿<¯¤ø ð¬&;{ѼŠ+µœSÒ[žsÆ•ö˜ŽÀ$˜*úŸa3,—d1ZÆ-æ•\ÚÚ¸VÉ>Ÿéè‚õj&ze.©Q?SÔ^}ÕÅ¿½ ïÿËe;œÍh ó`NÅ.n/æN:é$¯Æ?3l"Ù .:¹Ú»Í[™Ò5A£` ƽ ÎÕRê‚IW¢É¢ûâîle¯p4­?®%ûù,eác¿ìij/ŸYs·­ÈÙ ë©èøÎÅ™ðŒÏ˜“O>o;f¡ßòÛ¿ýÛÂ΂ªLü_ؽê~_ ™(ï/:7?ÝÝ~_r0ˆ1°GÎâ0Ì¢¶c3…`¹¬Ú1a,W×<N˜<^C°}™Y«u pì{<>²kÏn¸aͯ¸âŠpŽï㢠Ž8âirOC©ô²½$…›ú¼²¿Š¢Jšç®ù¹&ø=ÁƈþgجÔ5³û‰!•™NŒ»¾Ü†'«Ãöµ>7&Ó´‘†U,í ´6~Ã]üÛ‹‚·òm{÷»ß½¨rEV`‘Cì4Ô]º¾¯¯ýë£Y‘){ì±­ãZŽnE_Tø¾ŠV'Pàʰ€ÀiVÏÀu¶v¾Ž˜‘Òu¢Bth¦Œ} ÛÞÞ!P²Ÿsé蟔±Ÿ5wÖCË, -¾}„üâí)ÄÄ3;ã„IÚl=fË3>úë,¨ÇéKdH¼ï多teém„@yÉì—ŽþÿÙ»Ó í’²>à“JUʤÊš„0$CRde•m€P,‘ªh‰là 8€Äa`d‰   * ;²È";(âˆ&²9€˜`Ø´p¦ø€:5¬V1c%V™ßûþñ¢ÓçÜ繟å}ß羟¾?Ý}º¯ëßçt_}-­˜ö¼7‡É³JbÓ¯Öï9(“븉ðàÁ VTÀ¨lŒàÉV9¥xÖ³žµãq6àyÞóž·ª0“{erÌ’ÀKl«"ÒŠÝ©¦ºÑÊS2"°¡ù¹Šž#ŸÒ3x Ò²Š2pÆ6õüóÏ÷¬JƒñªçfÁ:/:Ó*…M´U ’ü±ý;Œœ·×îy9&Î*02Á hjÅt-0Ê_² íÜpà ‰Y'ÓVç‹_üb×à¿DÚ"r!Zä‘-1‘‘+d Tb´€u¾šGœ cø¡@íýbŠ`­LúЪ<ýÑýXŠä¹¼é*w/]ò èµéÒh=ä!Igê,¨¸§/‰I“ÙQrú*3MVøF·`ºó^P°™96¢ÀVÒÉ”I›;ºo ×8ÀWã&‚é`RÈÏ;À"ΉK~f²‹ù1­}å•W– Õ>ðÊïYç<ãÏ`‰„ËëèŠ+®HÖSžå`Ùe´òhkñö5Ø58.öCÐÊf/`<­E»¡,WË3-Ђ®’ ë:àOS¦ƒI±Ð7*¡t”¯c†…ò§öÖØþ(ú‹4’Þ‡>ô¡UÏxÝë^%¶+y3Þõ®wÍù@ ¦z>Ùu<ó™ÏôVã‡xÀ°ªå£œ,¡¿È…h @2"iø’2~—»ÜE ²oÄ”UÔÃJ%U¯(F«JŽüAÚûÅ÷M¹Y-͇?üá@µê“dÖÏ®ËW9€‰Ç—ö0§Î‚švAŸ¹àïðû²e¸÷—ðw*,èÎ{ë¸xê&gwÀ6¥Ï:n`rrêÓê&E”H3MÆpJ¢ωþ'T¼á oXÕ¸s’ÓO?]™D¯nO}»*ñ¶Í+ÆÍo~såŸýìgW‹l9ljµ¼šŸE¢‘8ˆþ'ìràÌ·!U #0^Õ“hÙd"˜­—£®Š ¥M¿Ã¯ù©çcû×±ïÀ.ö°‡Áì÷2ÏpNÑšÈãõZ<Žœc€lôã6Å-nq ù¼åœ*—rÎ9çœëî5„,!c…hñZæ2ª)‹¼eõ—ÂX€Ò˜²p¦šÕ6ænÁÆPžµ÷‹Ç¢v•—|¢„Èf}Àd¶î^¦Î^gÓE¹%_¸ôh«ä,(Þ_¼@»p2ü¥ Ú–éM§@4~Ã_c‰¾ÖK—ß—vŒ³>`(>ÄB!ZÄmyéÝÀÄã ¯’š•)`M–#nÊÞQøÀ/3÷¡bUàPqÒ¢sæ™g¾úÕ¯V2/ŠÙžÄƒZÎ[¬zÛÅ?þã?V—\»,ó³ÔšŸ³”™EèÖºü€²¨Ùº`\·ºD^›&‚é S£âXv%U™¼c»»‡írlÿNG¸‚á O8!½ßðF‘­¡+3QC^¤Fpdϲ#ök0h– ª®6±x¶ÀȰ÷kOéËØoe|ÀÄ3g¹{™¶`˜7I{´Øz|i«DË€ÃZÐgÏ óùQmËô¦S ¿a®±d'aAù}iÇ8ë´´f«ö¨9å¯ó,ªåÖãKû .]¦€éªé3v€‰ðòª«®Bç|§ÏXLÄ]ùýÐ1†ÿÜç>§°%òª\vÙeášo®–ÛbÁ[¼dÑ’¦V©­;Òƒ{¦€eSo^@˜ï]5 ®Ë·.iÊD0L mš ¦Iµ“„©äV¦U7º’‡ärlÿžWÈTŠ»ÖEyë[ßÊú¢¼SÚxdgB9…ËϧÃ(«´IAÕ÷}ß÷•NNüÁ<úÑîÚ—(€,ÈUQ\ éäøu$-_ ˆï.FÔ>ƒ° ³°¬£j4űx|Ã:ÊŒK°÷‹™DùòÓ­ž-»Ò¯u÷2%rç¦Tø¦g5åý¥œ¾¤oºšß² âôÑ#çS ¿a®®bw„Ðú}iG1õÕÍõ?ÕË L+†ùyy<¾´êÒúf²dñ¤Ã&ÑØv$:ÀËî¡sÙ§qKŽhóú€æ´VNÔ¼Ùf;ð™Ï|&›"DL&·n|ãû›“‰¡ù9KÆ‘y°(ýÏ/šÌÓèh¿ú«¿ô‚ñlÀ^SÀDPÀ¤ÈòÛ4INÕŠí´Â&WeæÄØþš°÷+…¦w«ŒýV'Ë£8f\v÷2m!:üs8ëÝÖãK[8gAßû½ßë Û·ª¼8ŸÃïKKªíI—÷— ©„`€ï³ç½u€ Nqú`ëP$¢ª›¬ü‚Ç—…ÖLœX®êž 5v€ ´Úó-Ñ7Ëâ§?ýéi'–ŸÔá:uO}êSžj3©uÉ%—¸•ß”S±t÷IOzR-‚|ì¨ ±çAŠƒE0KDÀ¿@tƒ h¿ ßÓNæª^‰h{ýüÏÿ|åH˜ Ñ-ëz“H;™Påù¶­rÓcûwÀL (®œ{î¹­s6¦€ò¥/})ŽØ¡’:Äú‚ŒÓ–Ãgøf7»Ýínw;û쳃È(]Üýîwïz™Ó?:]þ¸D¨š´§! 2¢jH*ÈH-ìˆ9·Á¬‰¡0faÆÅD8LÁVÌ‚_}8å8|Eò±’>ðYþîŠ&åæ}ï{_N°9ŒY³(ìArbœÔ7iÚ: j}9Øÿå¿ü—iù‘³éÀÖð·O2Î{AHpÊWiªH\­µ pZŒ%¼oWy|i+vií˜Dªû™V%§èŠËýP :l¡0M#d~oûÛ»f³P™ºf!Ÿ¯mþ~êSŸê*f9Ä’âË_þr–ËToº2ãrPàQ z^€~±·š.Ø€Ö(O>D|f t4M2_LÓ§¾þevÔ•?„—cûw`L!l‘2ê;öá:í4ßBÆ$.’+Å%L ^úÒ—:8ŽWYÁ(ãc dýÒÈT¥þ&7¹Inu§‚6˜mAB$ê)w‹¶H蟈á.¦¨ˆA9¤-wÀ˜ˆ•YÜäþb÷oÿöoqfGê£vyõÕWW€,&U{ÆC|ÀDl´àîeJ^OŒLL,ØYÕYPk °IËÊ>0 b+ææ¨P?­uÏ—G‡t>ã[ˆ,pjZ°+:XsG$¯©ew/Ófk­¼üNø©Ÿú)ˆµ·lôÇQ<¯8}ÐÈÙ, ÔÊ»ºõ±]†Êœ&ê] ZÓ» 9  À@Ì ’ˆ…êVޤ4eŠMu «ØHìqTOãYx\pÁ«ÚÉáF %Ræïx¾8ݵ3ä#)Ív[ý _ø‚L¿'?ùÉl¼¶¯¶ðH 8€-‡(à„A#X¶Zù ÆQ ì*ðîåt SFLŸL¥©+Ý®Êá¹Û¿ãEô@aV~ætø¶·½­»tóÈD£”ÅÂìèœ&ùÖæ“)´€ò~ÿý¿ÿ÷¶—ý×-3þB´¼Ú*G'¥¡ÚçÒE‰ÈHÓÚ#¢#^Ñ1KyŒÃ¾¶©¤#ûQ`ªQ0-lùã“0ÁòÄ0—@IDATÖŸï|ç;»v³¯pdcH[¿ìý:6—^ziU|ö³Ÿ­5?NGZã‡$òò]ßVSG'‘çU"`9àAØ"‚Gk.,ÀŽð%‹oÌÊ)öU•$°¸ÜIaýÍùn ãrϨ½ŸÅtT…g½#¬Ù>°%Z`ãfͺñøY_«jYЧ ·U†örÞ3•3[Fð£k\ñû ‚8} `¬ê_ÙÀ¸ÜÀìXk¶@ü.˜\Ù¯Žà,•öœ™¹ŸØ í u<ß÷¾÷M•[-ІœœŠ¼èE/ŠÞSç?£ Ä À ¢È7ø¡@‚eYcy(Ðʯ3vYßî-|RµPþùUܬXÜcûw0¨{å+_‰÷>x9/²—xÁ ^PMS/–C Z‘%£vŸ]ŠŠ~yÌc²3¬Z ˜¿ufènNsæÀ4¿Ê ÄõEˆÓFf_Ф-­ñÃÔRàÆ2ŒÃ¾Ö°s³]ÔlV9ж9Ò[OÚû›4G>-P1ë=lGj"–»—?ýÓ?Jkž·Ð‚£éxJä‘€Y´®F$ÌLŒ|®¹æš´YZ£ ·6šy¹•'ÖË @bvh HŠT6«1­ÃB㼇{áXiÚBå˜J&”ierµÓ­dóUr$öFÄ›Áet&÷\Ð8ˆ‰”h×ô›¿ù›ªØ“ó›ç椴fdò’­L>Çõ†ùã?þã½õsÔØ@ý²]NÇivk›‘ä¹'¼ºíÚ¯ëFr"æPÒ„ê æË±ý;î|ó›ßŒÅÛÞö6Íù foðØÇ>Vߪè‹/™‡ÙoÔIÄÄ&ðCÛ•ë®»¤(.sLä;­ÁÚ:ÆÕòýïùÊTtÁ¶úÑL#‚ â l-Ä‘å#&’*ƒ¼‰° qý0¨vã Š‰X‰¡Øª€³rÂt—]›ãr[)Ð.F¥3Ìç=ïyANg]°»Ü½ÄîwÍ5wÔWx|±@ÏQöl¼ÁZÐ g¢ŸåÊ,ñßä¬ÓÏQf)€¹~ÎÇ‘c`°JXBª€PÅËq§Ú7K…³Àr¤@¹ïÙò ™&Q¾’¦Uе“nìH·þ-Ñ6·÷Ãn,VÕýÊW¾¢€ÂÖ»1߸ ðƒt—Š]rèÝD‰&*£ù+0<ÕÅFâäP |Æç@™¥µd®þ$p7ƒzfð×Ý.§/™> ïÓÔ¹küD_ŽOþP8ˆ`,Í1f /nN“Ï<óÌÉg©çmèGoxöK&¤¸»Þ›ŒÜâNwºSd¨B—¸uÎ9çœqÆC¢V,Œ¤YrÖ—/ˆ†táw>E³Û1"jÜ ûÕ’ã°OVf­Œ¹­}pDD ßÎêáHlÚehíý2®ôZ.wv§Ë£¶Ô&’ð+w/¾" îR \ÕB…h‹Ç—ˆ*Ä[›–Ï{ÞÃ7¿ùÍ ÓA±ÒA˜V9ÛAc¯³ãz N>(` òc:Ì„ì‹È2>`,nê´gZ>9ù´n-ƒ@:ØžI¬jÇôÉž³=PR¸z³ßÍU ŽüUxô£}ÇþpÖzýõׯ*y‹[ÜB™˜bvÖPñòÂP"u£Mc‰Å¡ÚB”‘UOùƒû§@r„ –` ¢i9Ö=Ä6b‹@ìWõÁd‰ãc“ç´ÓL¥U%gþØþí—/œ9…»üòËÛ¶øÉ‘ X$Tì#ßÚœ ʿ袋Ú*m:ëÈ¿øÅ2C~úéÊGÞV¶ª?ýÓ?-ó…/|a[ñ(§‘A¥µD,sš‡¤Êt«Š–h˜¢€6ÕÂû"ò”­÷j¬·ÄƒV§¼ms¤·†í´Ûû£óçD7ö™YSáoŒ@ë9ÏyNK¥èeEÜÞæwéÚ.wÕÔ{jÓ•ˆ£Þ‰³ òûØ]Ëãrk(pìuvüï/‘aA>%€Ñxȳ)½—Íy\ü[W²½,ay§Øäkº1q²m0•¦º-í;À–þ{K¿ûÝïÆZ“¼üå/_ÕÎCúРˆšŒ8Im1š/n9­• c «È¾S+/œ¶ÖH œ äu¤Ep‚h^†Q1èT·À»tÁÀ~U'M–4›éc*­*y8óÇ'¿|‰œ d msåÿÚ—þá 7Ü3Ó ¦”¯Ú*I ;YÅ¿øÅä¼ñoT+{˜X£rú²—½L&%ûi G3'öÈ‚8(ƒPµsFÀÐIÝB^D^E¥(G)æ‡e‡}Y£›çuDÓV¼³“‰¶Fz (Ð.=§{¿ VI&ï:n`à3î^fÖ{ØÃ °ÌЧŒÇ—îX&Æ µz-è}Ø,Ó5ëÐOƒå÷Eδý‘³ÀÜüâý%ç½` –Avz° ~>©†_‡Ì >`âh«V%"¤XÓ Lܽ˜D¥ Uí$ÑNñ숳ÛK_7/¿Èiá®jA¸£{ë[ß:-û^h{ÜããI_™èŤV™ÓO뎜AG8Ú££—8%€ ®òAwúô˜³º öÓ»ÉIÔè´l™J«JÎüñÉß_²ü”¶¤¤Õœsá,ì Â*V;¸L=IVE‰}èCÊP¡©LÑ;ßùÎ2/¼ðÂ`סV‚Zr;YÅŽx"ÎW‘%'~…\ˆ†tíQLÔŠy\矾Šùa\¾ma%¶N5d ¿–G ·?ÚE窽_F±¦˜,w/¾CS 8s^ðC¿._ºè/QÕëlºâôå'~â'ò”èÃV¸PŸv`älþþMv,D ¦»,M`¢¹G–Ñ-Ç}«²«cêZÐ×Â.°N¹[Ř`Äèä(PŸìŽjC¬Ê5•EL+,ÈË„ºE˜Epx]x|‰$\»[u äQ X0gµ,ëܽTõi¢’c8¥Ïú9 àžÐ r¿ûÝoZ7Fî:+ž=刡T4Ìk)|³›Ý,Uü-ßuÓÆGΠÀ‰£à2ŠÈ#pÝéÓ<]ÔVWÆ4qË/§uÑ•<´—cû·wÖ¼ä%/Á{†¡ÓÝ7’n92¢ |—»ÜåFš_BL.<8»Ê÷¿ÿý]™ûå™-MDªãÅŠP™ä"8èB!ZGF„u‘»üî2›ð†o§a%†æ$pê¬ b]Sãr£)Ð.4×Ùûe°Ën`¦î^fId͉C·h.e¼îˆ&t`ÊéK-è£ÇÅHù}óÙŒÌ- @½Ä°;~_J£ $:0±»‹Ó—nì«|ÀL=¾tsic¹àf•»—Ù¦’ÙṈ\ Ôò-”÷Q#'½Ímn¨\}õÕm[÷îK,|ä#íݤ£ßkŌşýìg“™ƒ9ŸUp˜¶3r–€~@Û™ÆATN6x¥Ó>È•à¿Ñš ™)¦ŒÆMŸ]9{kt Óã“¿Gâó'Ÿ?ïz×»º&¾üå/ç–ä9AÊ>J‚˜»ßýî¥iÛ‚…]`J¤ÚæKûÂ¥)b·EÐ_ÿõ_ïJÁKD@1ö(8¹D®é²aóYZpd‡Aؤ¿0.”ÇÐD–ÇbŒîè Ê»5<\w”ÙÜËv‰¹þÞÏxÜÀ¬r÷2K¥Y0ÑÙ[åÁ¨sSN_ªýx‰Ïë(‚Âm݉-£À±·Øq…OÃt¨1¶`ÊFtjÎòj´ ¦U_ªý6±Ê Œ]è‚»—¶….½çéÙµsÄ/c·YϳÎ:«Bw '{¾­@·1«ØÏþìÏGÙwܪáiÖÜòWé×Uõ‘8qˆ: dÉ@ó*“ ºÓG¹[ÿˆG¿ªÂÄøäï‘)çwÞ‹ÐÕ‡­{ßûÞn•ÛÏh~ÚB$ðHÎŽ=}éK_êêæ2Þ„f]A( žÒ1ÐvÚ7¾ñ —_|±4ÖÙ¦ŽTf,ÎĨç8‘NKø©)"ª\åå krfa\v€¥Äæbt½Jê ÁaLåŒÄæR`Ÿ‹ËY70„‘ î^fiÕù€™õøÒU,0µ çç£ÊÄûK„ y/™/uw$¶Œy†Ña:ÔËŒÄÔéKK¢ŽËÈyÁãKW7—³n`vt÷2ÛT2÷9IZ>:·Þð†7‰WDENEÊ>%~Ôþå¿ü—l^ùÊW*6ëÝ'§Êî~úÓŸÝ>úÑxþ§hGK‡p¤qÐ4‚ez¨ÉÝiŸóÍx°~%M„35rlh²d½gúL[8ü9ã“¿}þóŸÇ~vwõ¦«V¢v•ÝSFbIÛ–Ä”i{7¾ñg•6ô Ú*;é¯~õ«l°'1øVŽl"QŒãÐ%_,„B®Y‚Ä©§w1kÔÅ&Ì’À¸l³±C˨[S) ð’iã#gƒ(p ËÊÎ yÁ²»—Yú´>`¨ƒÎz|é*f‹Hw% ú©8©ý˜»ÆåÖPsýŠÑå÷¥˜-¨DÙ©¶vU M´>`b:H'bÖãK[«ÒS70kº{©¦‰™ªÓfNŽ¥oãyŠ ¾_ÐòØÇ>Öð?÷¹Ï}×w}—Ë_yå•Ò߯¥O¬ˆ¿ÿû¿¿2Ÿüä'+œßì »JŽÄ À ¥@É& ,ëYà*t+§‰øÉO~RNÌ¡MÓÁ¥©¡–ib²Äsòªufµv8ã“¿¾DŒú3?ó3]e{lö€ÃÑy“2ˆÏQò=îqUœMÝç>÷QFôðxâ®vØÅ óT±0eœ'¨èG‘~ަ<.ÿïÿý¿ÕÈL>" ‚ K©È®r޼hˆÔÝ&v`Š[¤)”Ä2—Ø'Š­9à•ï‰Ùp¶4 ·€¤ÍéÍ¢À.([70ñÀ¹ìîe–På&vVS/]-ûÌh¼ä¤ºœ¾T±:ô{ç;ß ®~uk$¶Œáo1zújÊ..P™:}é¨Q>`@qG/]Ý\–ÓaWî^f[KæNØ…§lñ­¼FÞº–µUË~ÞwÖ÷ÑWÏ‘HK Ħ 'ÛÉ÷þ‰«s…ÁïMozS[~¤N&ÀE°,¥­¸Ó]nûÞ ƒz­«£öl:˜¦†ÖL“œ"L_§mS‡9=>ù»æN¤ZÀ]wÝumeÓœÛ!Ø-p ”øFRÎR¬ü…@UðdÑWLSqÒpÆg´-·é{ÈÔÙgŸíV¤ŸúÔ§ÚbG-mø&d¤’È"uñÙ[² ²bØÝ-ŒÈ kjÚÇ‹öabVÞØ*‹³·ÇôNXyúÆ…més”Ó¾”Œ™€1?ˆ(}W.0y«Ìz|éŒ&óíQ¼*SQ¼¢ªX݉-£æú£ËïK <€$Ŧ U¬ñ(rMœukÍDÜÀ˜Ù$¬²b]³µ;ði»«§ozá(v>ð,Ÿ‡ ÛÀYbëÖ"v€”Úñ–Ñ`…¡*ÍÏ|Røú¤¥ÕHŸ| d}X–¨¥ÿ™PaÞ{Üö ¼e¶AM¸±Í¤°ê3MLÅLœ¶î¥Ç'wÌ*Í+«ÿª)S<¥¼æ (H¦Fö´§=M±øÚ¶9iGåÿ·ÿößRËirö±Ÿ¾è¢‹ªñ.uÄ{Þóž i@S1+K‘Gê2þAqÈÌD®UtˆµI¬~?úêbJ[˲ÁÆDùªYÌÅbŒÎQ¡ZЮÀC±U1²ÚöGúRà¥/}iØ× ÷ÜUÖ·¼å-µéׯXÛUƒñ£…Y¥åiSŽÁóz™Õ¼zÅ+^¡)+xuklÿ¦Ü–œâo6lX?Yô£f•Ó—®JŒ´ –Ý­5/s®S£,ÍÖ¬»ª˜ ›ISxU™‘?K–ŸÖÇ”hð"²éÀ¦ã¯ÅòÛ—ØW¾ò•¸¸“_zÅ­æ'9i§e3Û‘9(p‚(~ÖÒ¥ÿY Æõô¼šºèÜõýÕȹçžkšDãÌĩЛ•8æ uüÖ§Ÿþú§êó„'RÄç âĈ%䚥@$7ŽìÒX€Ø)my,Ë/LÄPÀ\,ÆhìÆt¬0†Ô •Dié#HqÀ’ûíÕiÅ:-xhŠa¬S~”9Ê(l–©Q ,X.—ŸÞõM¦Ö|è´‘‘sP`ñÎ+¬·™iíÌ Ç¹E=åw¸ƒ41kåø2Z Só­dwDÞ)#0Z¤áUe$N&À¯ÂZznDù€ ®@+*g—^ÞzõÓD( Š`¦‰ÉÂ?|\ET±MJxíŽßš(¥>1»TñÑrÈC€ßÙ׉=pÙe—ŵùY¾jAÒ÷|Ï÷@ØìƒTùÿâ_h$š)/ÐÙÂQèrTmïqúé§«õ”§<Å_!ÎgË‘LÃ/R âDeA‘ ‘ô,dÇŒ˜%ÆaŸö±Rlp£å$àD ¾‰*S%áÙGŒÌCEƒÕ"ƒ‡¸{¡=âtÅoáPzJïÀÉ6\Mmù¦k G2}¥”˜£¢¤L[9ÛA€ñ+Fc}7.ðˆ¼L±ö`§+V—± ÷¦ã` d.D¥ÊÉk³±‡ÄÁNÛ=t`Ó«¼ð…/Q=ÊKË|õjhì6eò…˜!ŽbiOšu0'@Ñü¬eq&¤Ú‰A“I„JÝÀDÕ¥å"èJ€qÅ늫Ï.Ö_VòJúÑŸOƒ¦ÌÉÅÁ>k( -@T ÿ×qß Ù¼9â&(ûNck½õ‡,”ŒÎ'V¦ æÆ/0vc:Ök$0ŒDLû#,Çaí­\JfFàsÒºÙÕØ£GÀãKù]tx²ÐB-èóµ›ZÆS¸–œ~¡µqk£)€Ë-£§žîâÚ.P™´Ã¼(X‚"@j8Û;¦[w/­˜+.8À »ð”í¾ ×4ålΣæ4áŽÚ¥…t9ùÆÅ†â~÷»ÊDéŸøD4? W#ÏzÖ³¶›nct‡Ÿ@<– úñ\Ðê<KÇê °¥¼ƒ}Åý25LÓD1Sæð}UÇöoeúüÖ¡ÿ›ßüæøö°ß˜¾ïû¾8!šÄ‚ÄO¬í)²¸È»²o´¹æg2oU…;w M©¿Kœ÷XúAg<eóCÕ°-ytÒn†’IkÀDŸ%"ÇÙãäs¶X2±Oa¬¬xYXœI0€õ À-(xÉB€…g[‡„² tìdد6`‰ÈgÝÜ:TX2øÅ!0'CŽ9Û¶åSš¨‘SVɾQLíî9úÖN9Åî ŒË­¡.·ŒÆúnh ¹*Ñnjbu%]Fœ/©eàÜñWí€}6&B2ãÆ‰ZM•\?q SuýÇmqÉRxcãPjŸ¯}íkÛ!GrÄ‘¬ïfÂY}ä#Q F+ÎuozÓ›BE,¨%˜T´ÕGzPàäSAÑ/°Q@u ´:s饗J3H[Jw22S@¦¯ï¦FÒ&ËÉÈ>qlÿÖ%f^…¾•LëÇ~ìǸv$ðÝrâ”ób>­Tž <üáßñ1>Š6¹ùKy¶J¼3×R’Ca?OyË[Þ2[eë3 ÜðCÉx£{¹Ê'/ò†hþ"û: qLô”˜\æó=¬0 %ý@%'ÆÀ³õ\ØÊîsY).PäD­?!ºÄ·¾õ­ÁÚ»•/.0z&å1~­¹K1­Ù“Tž<"¹ÿ½ŠEÌ­­:¬»#±e8þ6:¶ ‘Ó[¡5`DF *£ ð¬¢ÈEQAâ{lM?€ÏVÓh­!LÏ5YL™U^•¿ÏIºªÙ£™ÿk¿ökA‹Íy¾¡. ³§(‚Äi0˜g?ûÙîZçV"FüüÏÿ¼LÕ~ï÷~/M¸Eº‘8U ,î/@Út)Ѽ@:ª^­{dàf„LŠÒ#5YNÕpä¹cû·1ÉùRöxܽêU¯"'ˆ½?1`¸×½îÅlµ4‘Q‡¨ÌU‰¨`±½ö!ÌΤܶUnûÛƒ`mrÜzÌcX_xá…mÉ£“6ðP)jÔ9ÙG®ÊI©]ëäÁÕ)pW²»ÄD%1´ýb7¦Ë€x À8C˜K‚P×à¸Ü ìyq 'ÿþßÿ{ØS»Ûæ G™£þ¿EœÙ–èÓ:®bm‚/²,èã”,ç¬Õ«Lœ‰iYÇ€uw$¶Œ@èF‡é­‚IâÙä,.ÈžUîì¢è~E¢öhº2W%^O€¿‹Èj‚$ô<=üö»ªÊßóô¬F¢¥½Üã`9ö‡]SùºhW96áô"/±Š|‹Áh$1»øÍßüMXÌ”2Tû ‘8™¬«Á2&T*Ц` ® Ø@^Ý«¦éûXüL–*³‰‰±ýÛ™kÜûd›–ßå.wɧë¡}¨œÛÞö¶ LÝw+Þÿ}nw~Æßý]$gqUb7’ˆäµßüæ7ÛêQ«he·×^{m ëI[òè¤ #)jÔ9ß@®Ê‘@̈ÀΖ/®tJÔž¦³rÂÖö¦ç=é `($ Ìø B€ÔVéM¡À–˜–³q÷bؽ2ê÷¾÷½Ž‹}ÚÏÌ” åñ¥42[|aÖLÔó¬§Ó`6XÉñûâËWž¯Atúô‘³¨·vg^Þ_ÊFH2ØlÃfU‹ãñðÀ¯¥LŽŒvôêöÀßVOÚ4‰¸d}70{˜˜Ó玜Ž ¿Ä8–ƒn~ʆãb9qæÙ.oâæ:‡ÃÜiÔV¿k\ œ ”È5¾^T ­Îd`—² ØÇÇÌ›¹+]!.«úÆ%Æ'g–Åx¿}· ÿíßþ­:1- ìýœ29óiÛòYíB½·w§éœ+^uÕU¹Uî@ou«[µ›½ìô¾õ­oµ-ä=«{mè¹¶À§ ÙÀq§;HA"™ÈUcGFÄ”Ù:ùdÒ)ñ«ØBâ]ïz— ßÃúœÊCÞ)1΀I÷TÜ­ƒ„ö#}j)°Û…f6`”ÙjÍ4í¼QÆ[°Ú͡­Ôtj?ÿùÏ*kîVÛÜSì<#®ª¡œ¼‹Ji<–îN¤£L®¿iGÎvP üõ»£É_FòÁH2Øìñ@¨r’__¯# pîèÈ£w¿e] ¹Ü•˜ÝNÉÙ'ŽÌ)â$#[qZ-ù¢In4¶î2KX;Ñ`’I¹xñˆGHû&N1rN>²VKàŒþ'|¶š_Ù¼«{àÔDˆ–_¦Æ84Ÿüâò|‚L”÷Å,.¾øb{†d 6mÈÈ4”W!À•Ä}þÇs£Xx“›Ü¤-Sî@mWâ|™‰š>@a[LÚ×—Õ™[%Óí lñeÎ1 º>Îw+v}ÏœS'ŸÈŽt—_~ùŽTÂʼAÚWFj@ö–x ¨ÔgÒ#d®R©ÚñÑ£À)§ÀúËMæöü~µÝZÕù¨e®r³¬Vð³>`­5•vùžSîR ;®˜ñ(ï·ª“#Ó)þú‹Ý˜.QÞ_¢ÌÙÈ›¨üugt­Ç—)AfU”«˜—pçî¥nu S&sÇ$ênµ—ëOƶÖH¯CˆD}.ãê0``? 6Å”|ìÄLjä&H‹Ãí(ÑÝõ®wmËŒô À©¢(Â'Xê@ô?]ÆkQu)aÀÀ;9¯LÀŸ‰`Rd%išT­ MŒOþŒ;çœs°Ÿ3ŽK)‚i„Ì%ƒËìš>PkO–žôK¿ôKäW¦+ãX)á%œb±Ö O¨¹lWÌeNŸc¾?½»­9›ãЩŸ!ç Ñ.ų;¤U ÙQ Ö¡Rô[f?i`wG€hl€'þ`Ài§Œ2‡“ë,:gݽ¬β˜ÎãË´‘Y0åô¥-ß:€‰^4=@~çw~DókËô6Qàï9|vW4~Á uúÒŽwê¦<¾€\[²M¯òc?9ëۦwt³Î4léÝR Êo}ë[ó-ƒŸ¬€£ßK³&¢Uµ¶å/~ñ‹Aå:ù•Ʀ´-6Òƒ§„¶‚¥§Gÿ\cÉUýÉ: ¼œ€5n~…M“B©*››Û¿%Þ9”#‰lµÞy jÏsh³tAQÓ\DRýj›-<éŽw¼#HÍZ±XMqV矾b?ò#?2mª¼·Ÿ}öÙÓ»Ûšc°âgøÓ1"”[ˆ“_dœµAGvŰ`ÚÂ4CãËqߣàt¬OÇÏŠ&DƒœÖñ4}îÈ9$X^zîÁ~Éç'k¬N{yù8¥¨ ärÕHAîà7•IåãÇÏGü¾Ä)n΂×js$¶Œ᯿ØmhÑøƒÖéK;ä)Š"SˆUs[²M¯:¬.°n¥ÕÖmÓ¶‹ v³Ë°mg¤÷L8EäàÞ÷Ë×3Ш ö˜à¥àÔ}4£;§pôbœÜúÊÞsFÅAýS ~øÁRS T·ÚËò³‡m*î ±Ø›ÿà˜‰ú`‚ì¿?§¼…±ý[bÁþèÂÁSŸúÔ*ôþ÷¿dÖÏæ'ÉÎÂgPïÕæu×]gWÀ¢ta¯Xî@=W°ª[‰D59Ñ0»3©’[“0ÌŒMf# %*ƒ»Š!àª#;âcÁTƒw¶J>>e ÊD8ðð\€©FI>PUÎHl"V-@——­ #-70%, wŒ²\çñeÚHç¦súÒ–/0•dzòþÒém¢@^G9ï5®xƒèG”Ó—vÈ­˜U_ÚòIÇ è–*~öÞ®*é´n›³JŒ²jêµuGzÿˆüš!ƒ¦b^BÿôŸþS™0Ô㻾뻊ÑJ&Ú»2_ûÚ×¾þõ¯u«BXí¿“£…A]Q Öò` œ |m5Ì | …×÷~ï÷&'¦U±îÙŽ5öØþëûD¢ÉQó-ß›_ýêWã D÷fCO îÃæ’íÉè'?ùI­°F¿f$ÜøGŽ0¾ïDsÍÞ,gÐúP]ªû«º•Œ;ÝéN¿ÕÝíK aÖ§re$ ³mjª×$âpŒèòg/±5ü(Ó±>¶—À€‘àﺧ`“vt)ZãG6Bã,=71svºŽ»—UƒaEâfÁãË´…òõlìÞ÷¾÷M‹™2Ñ銢8”¦LÀ«Ó*#g;(€¹~õ‰‰÷—À uúÒ„T' ŠÔÔãK[>é[Ę˜úhÁÝË´‘äLÝÀÌNºUÕGþ~(à]‘¯'t˜%<<œqÆÇtÚi, "ŸªTªÄ-¹»Ÿýìg?üá/xÚ¯ÿú¯ï§'£î ÀAQƒIàѤtóˆD cQ«W 𦀉`:È45ªÊAõí”´3>ùód¿þúëãèÕ¯~u•ˆ•<öSÿûÔ§>•|¦>ÂÔ±ÏøÀ¨c­ê=-ä¨ðE/zQ=hUâ?ÿçÿȲLmÝ*ŽùÐfãÏ“«ÚÙ‚üx^Í` ¼›åä¹mÇñ"¾’ÝÞB­„€ÇhìG *f1xDGÔÝò·¡Ap’Z¶Ðþ¸uø)Ð-FËÝKyGØí¢œéÛ#@mä8³Ç2Óf­¹ãæQztY²wÓ¡ªÄL^V_úÒ—’Ÿ³ «ØHl0ׯÎ{±Þe`Ð:}iG B%TÖžó´%»t)-ƒq` Ø]™5/Û9ÕM·5[ÅöLXšDÉhNBJ¬IpÖYgAÑË^ö²<â£ý¨Ë¸Å§P÷ò—¿Ü¥ßšBÕ=÷sTX“9hIàŒ¢rà ºi˜Ýì:' à?q ¢ùej¬ùÄC^l|òçÄ_?Üîv·+¿õ:£:<=ù¥‚õ¤'=)‹6ýÖÙÎy6‰B—;õÑÙnEw9ˆtŽÔuãž÷¼§ç¦Ìé§Ÿ>ml¶ÙË44¬Áx;dÉ![µŽíâk #°£mjU:ÛEUü0ë»@X*êFŒ”ž4N@%ÀV5>ò7…µ$eM›ÏãE ùÖ·¾5lDîΑƎUª@|ÀäšßŽÊïÀ8¸öP¯[åý¥rFbË(€ã~ñû’¡å£ ®Ââ& Zðø2¥U|ÀÆ ½`Ô0­ÛåÄ ŒÉ?"\®›t]™qy"((Ø~Óø»ßýî (©Þ¼øÅ/–._ÑüÌAñßøÆUï¨zs":?Ú˜Rƒ^àQéÀµô?ãK°£Y–ÂþZKyScÚø&æŒíß ×,åÿÉ?ù'¸^l&È̲‰ÄÔÁñLãYŒÇU<¸ümo{[m gkEG‚ÅìÝ.ó‘|¤–‰(Zw U&®lYó»­Éb•ÙŽDŒè[낸™ÎÐZ'Ÿ‘å Ú:£ÎÄ^`®F°CÛ=,Æî‹A­EÄ< ”nä³ `Óã:ýejÄ«ŽÝÖì3×yÐX^Su¼ZŽnsÓ©Ó—*#aÖèj¼Ÿ%¿¼¿´ÅFz›(—U”Õ3®ì¦€aa˜€Nê¶ú åëèÆN˜Aºò÷0¡îu¯{¥ÿcï·î¹ Ihœ^”ƒû83Ç‹è=ãÏÆÀ¦h~þÔOý”Lº¾‰ -½gmˆ=÷|T˜¥(¤pÆÚ"p-ýÏô °r…ËÍ{¢˜kÌöáPe³[¿ŽNxéæa&pp·à‚å,»¨ôà»Z.yð‹ïc 3dŸ¬ÂèÚÚýÆoüÍ™iy9Aäð€Ù»]&'1rØž¾ç=ïáÇò†nà^p¹‹-Ç•W^Izá³ý‚¼ 1wílô¥Aš:‚3XcÉÀ%Aq(Fº!ÚŽCŽg‚ O c&b%†b+æF'JgÊ5ö´¨à/Ø’2@ZE‚i­‘³A` þ| Þ€mìßþö·ï§ó´‚£3ÌE-˜íª©[Þò–Êûº\uÕU ãíä°bK±²Ø‰NàBÝqk)Pl-Fc}ÞŠêA8¹h­*6Í]–Ì^yZl*ò2SÌD+§#ëÔeöC§¾ñ¿GÁš*#á|ò^õªWE¬ét—}{~¼Žn‹#å²­Š$}?=u„EàQm‚+Ђ.£>0Î1`»ý‰‚}&‚I…ˆéÒ)näPmFCg>ö±ÙZðÿSÆu‘—ã““Ÿå«Ž‰X̳4½Å-nûzq5Uƒ‰ºWç|vÕSb‡ZÆÖ”s"åsÒç6/\_tÕó˜Çx®-ͪ¦67?û44„,_ ÜðÁ¤”ßbÈ‹hë 6ºàØÑÆ2Œ«hŠ­˜‹Åa.¦wUºK° JÖZ¦«ÀÖ—›Hž‡bƒÑÏzÖ³ö<„ØæeKú70ë´fAK-pŽMаªV©'´!éƒÏ‘¼ªÍ‘˜)¯W'KÈ •Ù%@( *.X“vµ\–»—Àx•má´â4ÇT 2M®«¯¾zZ`äœP pÖŠþù&€FœŽÜøÆ7–ˆ‘Å?øÁh~RÉyU„d•la}B;9X“qÜX¢oxâ4À`\¼u°ÙsÖê»ò`¼fÇNU±ï|NUÕs­¢îp‡;àú/þâ/¦cçž{n@P›Š…OC½;öiãÅ“(0ú*W¤§!³°\kñmíKݢ͜€æ>ä\ÙF¼!qíµ×ÆÉ!XÛ¦§ Å Íãº×%²¨EŠÖÒ¡F´uFѻÔ”Ç&Ì*!P¢··çþ !à»'ÆŠFgÀ)·Ì%°\Wx\n"²Ž&„Ž zvà•Ç—ró×ý×;VT 5Ò¸ããƒtU­(tÁ^«ÑçÒo6xæªvFþ¦P q>ð·:=a9ÀP™]"nlÁ)"¶5=àZî^ÊL½Q»G,\š>‰¿¬“¦•ɵPxÜ:AðMÌ*‚x+G"¦ä·ç¢}deBš¦Ç>"«‘Ò=AÍ ¬Iho–·¹Ím`\V€´&þ6¾O;-€ç±/®õM‡5×êkvéÔûÎ'áÔöã<ýõ¯=ÆSíËê-€³Žï2®>”¤î2»òþ¨Tçm3ø#ùò—¿G´N“×~vzßúÖ·ÚòDÂq5Èœ%:³Ò܈.Ú*š6¤J(çu†làFŠ ¡ä#Úšã® ¦` e7¨Œ«€lmSU½ÎO[¦ÒYÐk ¨dª× WeFb£)À°TÝ,‚§ÁH–G„P V <"k|ðƒ¼Ž€ ²LR†ø€±l ½ê¹ˆˆßÅ4‹âV¼™¹ –~ gAÕÂHl"Ãßôӱƭ&ÖЀ'’pŠè Àêfc ºùÒë|=Û6Mœì!õÙ„¾^ZâœätÜ pÀ“˜1B彄51ï”8óÌ3Ã)øÌg>#I¶Žì#NrŸÇãf)c¨À2W ÍW»lŒ Ø žðoÀTk:’˜}ú!ÌÛ¿ï0¥VäN„å&´€w™-Dzû–4‘ø q”üFÿÿ3†Ò$æ$°cTöÿ—š¿âîRg|•§·¯¹æšüÁt7ûÃYªÍÒb.Mqrb­[Ú\fÈlvaÚÉHzÖt‚Šhˆ)ñ×"YX6m¶rb¿·NÄÊÞU³ Ë=™’5T›#±¹°TS \¦½9ovtÓ£’2+½è¢‹f«T&ô~qú’À3³ëõèB5Y=ä9)Hû-œÕ³Fbã(Pç½éyÜe@¾D³¿‘&’*º†¨Pøk´*¶·L™rº`*½_Kœ“Ÿ~×»Þ…§Q,wì+öš×¼&\ö7¶'ùÇGhòóÍ:»dL'¿ó㉃-@8Ë@4~û¢ÿçô/Ö­ ðQ¨É0Ú7==¶ßá`â_EÅy^mö΂¾ShEЏ4+°u]8,!MûàHÀÏV~EÃßÉÎé³Ãèïd5©o|ãqªÁýÑÍR•\ðîÝ´q¨“†y[ꬆi°~nø³½ÏÙý:ç0X€ik0›fÛl3±;|Ÿ=liKJ—GYí±S4+WW~\n",XóÁ€%Œ~Þóž·ã(HjR%'ÃUžÆ¯‘FfŸ«X“?þã?ž0ËÁõþáV™$žþô§ë¼e·ivD°Øû;{Ô52.7ˆ9ï u»óJ€|ï@¢Ø(Båš´ä€YW²½Q@×NM44ཎŠÉ¢‘ôV•±÷k)|JÒ4h"BÅÆúÀiYÎË>üŠ»Zžäùë_ÿú)éùxè @GP„ÕÀRPÁU™èÆþØ@êîÆÆG¾‰ÐiÞuíoÜåØþ}›e>“åã’K.±¦ÚXI Ü}ÊSž¢üú¡Þ5•³¦úÚñhr饗.<‚s-ø‘ù‘Ue¸?‰ÔV1a+S,çœsΪZ›’oÆe8épÅå4d_5 äR éVìq'£dرæylÚLxXxDݪkfÞ;žØúª’#±¹°lÍi`LÓä8ý1}ÆÞoJçS’ ^LŒˆÂ#8 Tœ¨ÙnYR6¢Ó$3ûÀîtJ†0:(€ X°”ÔtkOÒÑ–rw¾”¶v«Ä~øi>¶ßæQ´¡ñˆG‹–îßãÿøuXH·06`ŸøÄ'Ö)Ÿ2çwžþùm¼x'ZuºÕ5E¼ª¼]~{ ßeºw šŽ=Æ:öimS‡*­ó†` †SN>QÃ`—¿.‰¨±êð©ëÑüLj'>ñ‰šÅšõ‡éªÀš*¦@¥¼ŸáÈIƒßúO%?,^D(ó‘Áª#èä²üb 5ë&N_º¥y­¹;0Ñ ³çŒ˜ãx<ögztøI=z¸@œ÷†¿ŠÅ6/;±„è4~ãñØÚÃ:Ë£ˆº“½<·u÷2Û“5{Á®R15bQ–Éb⌽ß,%OIfÌŸ²>þ7ÿæß8Ž®“Ý]ÄLÐ…q¥a§YÉ?%}˜¥@Þ0' ¦ è&’A°IçÔó&à7fÛÜÜ̱ý;Æ»Dr¶KVZª9üÝ‘»ÂÐÁxn;–l ÄF(*TN”“ÉÙxñ9-<ûì³ÛF¦éxÉ–ã›ßüæÓžö4Ý ¾äsÓZ‡9G·ÈÞ@ '²™ Ð`—{Ž\ÆÞæÑ€j£·#;âG =(ÐQ f' æn¸`˜³Zo Od/°~S`•…Q÷ˆ ºÛ¿cæ‰U긬‚waö:öx8­zÔ¨veú…/|Ú¨«^pq|Dy&!ݵ áp¶N·"­¿ð «ül"ÿ8®Œk#_SañN?ýt n¨“ɸc5ɉС %úß,’‰\Š•Þb"i6Þò‘ÁÛS;ìˆa-4ÛÝŠ}<´ÜìÊ´— •µŽ>€\"ášÕÛ¦Fú0S v€y±øŠ´:uz¾ãÁHFWn`žùÌg¶ã]vËÑù€‰ ‡ÕB9€ÃüjsXeFb£)óÞ0·œ¾Ôˆ:ï/­Ç—*“Ä*¤¤Æ[w/]Å\®:â6"wÏ{¿YêòÌ=èA¸|ñÅǘÂQp4‡sŠtù £;ÞñŽÒw¹Ë]ü½ÑntÊ;?:0(PÈg¶@+§~4xGßàÁÞ]S ÙšÄØþý]ÚˆóÆg]¾æIdQEN°«µ{L×(þM‘$¤ø+^ñŠÀ®âÅgÃóÜç>wZ¥ÍyéK_ª–S/2ÝÚ,‰_'ÓªÝê´µmZ‡³w5„ÚÐZŽõ v¹çÈeàH祋ގÈH=­mÌ2žœ˜æ`}ø Ó»³991Ø/aFAq¶ðÈÜ\ Ô°¢–)`©h–xba˜³n`:§/]õÎLù}©bÙ|ƈiÒU±‘ØP xO[%ÊéK §õþ2õøRÅ’˜:€Yå˜Ëˆ/ËÀµ5ö ðÆÞo–n‡!3Þ>™‰:Éứ¬.àŠíº¿~æ48ÊŸq£/ç0ôôaP È;0àTpMôÖ96`Ç~Ô>ó¦ÀöÑð¨oÿ(F¯’ùÝÿþ÷Ÿ*Y­âý4Ôûª’mþ½ï}o€[P&N¼ø¼I•¤ ˜tb´Mué¨/*,vï{ßûªîMk_â©O}jWþ_ê°në|¾1†“#øP#ª³ CHô…K±VúÍo~s½½«S êò—/_üâë'0,«»fªr(d•‰í @í}T¢|SÀnA¼ã`#Ë(70  å¬NiZ‹qN|À”ß—z¢¹‡~ÝYP‰ ¥@÷†¿þbwûi+ï/Ô"¦_ºQw`ÊÝËŽÉ´Ó ;ÊØÏtÈJkìý:jªËë®»îþÃHXéóÄÔ3kž¨ìU.ƒ±g=ëYaè=ïyO9´ìÕ@FgŽ8P4àTp ný-Ø.AàÁøMí#ÝQßþÅcgabåéˆéÚk¯]“Ó98vzC–¹fÅœhR_ýêWw¬õîw¿»p £ôs–QpMK†G޶8J9÷Üs âžË{ÊŽ=$â·¦:o qòihh˜Ë~xª\¡fz#æŽCÔ|êvuR 9=ŽÌŽOQÌržà„kz]§ýQæðP v€£{lq£#¸õûÙº™uúÒ5Uknç„ьߗ*ö+¿ò+5¿Ú³ *0›K:ï-cw;œòþ/u4×–©t‰oGw/U«Mä´ìÙ¯æÂØûµT:œé¬ÅªÕ=_Ƙ»WzÔ£$nv³›ù›µ‡DT@Ï8ãŒÃ9œÑ«£I€Μ`3„¶Ò+èF¡Ì¥xgù—àÌ»= ØÚéíŸÝ…]Dø ÐÀ½ä*Ç›³ôr¨÷iÅe+:ß´À4‡_é­}k±…]\ [?þñW; ÷ -#uèTù‡<‘ó1ÝÖùÖ¹…¡É\,"eC¥$ÒíʱJÜ®¯É2&NŒk´ð²Ë.‹é‘º‡üˆI÷2@ÖíŽ g˜m~½]]$B(äB ¤kKî˜þÔ§>¥6íX²+ð°‡=LÅ])p‚\ÄN@õQ-ìJ!°ëø<̨ .³@È‘~L×ï6í»,Á‰-שåë5‡Ù%~½ y0Þ_Ö¡çF”)¿/Ø l~³o•ˆÀcÍ%N$åà„ëÓ¡Œýþ÷¸Gú3ö~ëð0” ëßñŽwèL\ Üô¦7c¶¼Ž,iÊe]„ÔuZxú?ú0(Ó¼ÀÕ[\V"9–|9ÓŽS PwkÍì&Òöˆnÿxþ ¸’Pý%+õ-t²Äék?ËWšÁY¬ï*Ô»¦.¿ürO²ÙfWezœZDUÀ×ÚÁQÉ-è+þÁüAî~ðƒTØv¢ W¢ÝV­o¥VÕONBÇ–·©Ù®fúcà]ôöv)ƒh¨MîõûŸ·ÀW\±~%£• ;>ÀÀ Ø@.Bz¬p.8§ÙU—FáÃF;ÀØÉ`·“Þ,˜œ¥¬¿žž˜§Ã6ë 0ê.>oH¦ji-À£Šœ³ Ýª=WS#qx(P~_°5ü~Ô€!²HðX§çÀ–×)øM[[Õ`® ^7¦€‰°ªÊÈ?„ˆ»òX7<ùÉOª .¸€Ô5èò÷Ïxƃüດ =„]:²ˆ€¾ ®@[—À Ò.ýE"P—Þ•øÍ"ìÝþ½ä%/ÁW_#®«sªV–u)øCò*¿bÍÍ~ç^øÂ*¹ÛPïÀ‘àK{Üãv”«¯¾Úãø¨íjÍÆ‹ÿÚ×¾F˜a\³[ˆV©ò®ótiYIÕ  Í sUôö–Jqì‹€mæŽi BpÌÚ±dW 6ñàÑå»$p*ÐÊ1KÀæoàg\gŒ :maälˆ' ÖÓŽA`€¼\Êuh=­|D?\™ññc­D³T‹˜iaù~$£ÃûË”8šS~_"ðÆßé@8Ä-ð˜ÞírÊÝK€·¦´¤säÙ:zð·ºîAãòpR€Ú9ÞqÛCcŹŸtIî|ç;KûQºi= ËÙ­ÅáøèÕÖP CõÛÀ5Zf®ÁØ0ëXÈã¡ ì·føÝ@f> ]‰í»´mÈbÈF(§FQQ¨¥y¢……YMðìÕUWe+H&¯¹]…z%ãtèw÷wwEØ?ù“?Ñ1(gk9kêâÅgq¹J¿”¾hFgu¨d:“SxÝÓÉÙÁæ°ÞãAQIüꢷ·©ÛÌÓ¤eÌÚ±dW  0`< TKð_·KÄ ‘¬Ò2ûs…µk|\n }´èu¼Ž)àúOÿ „8­Ž äÌŠ¨ŠVÑŒˆÁÚ}j›@²\}õ«_-=¬O‹t››È9†bkøÛ ²‘ 0€¤+Ð^X^P oé@ؘ¦[c¿‚·ž€ý8÷›’k#rnûÛã ¯­¿v€yí¼ýíoÀò×¼¸«V,1ØÑÉí£@Vƒ€šSŸ·`l¼ ÑF@ðÛG„ÑQÜþ=ñ‰OÄõ3Ï<3Ÿ1†žÓ QÌsRÜm)±ð{vÎ9çhÁÐ>°H¹NÂaQ(Ï×ÛêΉ@IDAT>딯2…<‘#“Ê™&ˆ+(F—,°¦X8û RM1N2PaÚæIËÑÝHü%’™µN4ƒªbkȾÐÏxAÀ…2Ó[„M˜…eÓ» 9%P‰ÖSuœ€* Æ]#@cz°NUµ+3.·‰Á9÷+dVê˦€q¿¡¤D¹yæ3Ÿ¹@™ÒŒ6Ëtá^øŒ=³ÅúBkãÖFP ;®04üíº!‚×é:¿¦o3kصY—eìW›Ï€|Øû‰61‘!ykEAÎ(|»c˜Ùþ•#k9 QI7‘£Ï›KD. JA´Näp­£ÿüæŽwÇž¹íßç?ÿy$ž!ôð×úþýïÿ2™è ’¡ò3©@Ðã/×ýØq@œoVÍrÚìÞð÷àë5¯yŠ"“LÛìrôDXóßøÆé'G¶ÓýíÓžö4*œ:Qñ) 8îÑ‘U#iÂÓë^7.C0rÙd€ËÑÛ«z¢!`嬙ˆ“(,[§<²ƒ0€DÉ>ðÚQ²À ¨àºÎÓG™ ¥@ífG™oóÑUÊuQØ«-œb0£Ö‚€£ôiÜŪöÿð[ЍþҔζaù,¨­;Ò‡9ïÅʨ¾‡¿m?@&0€ÄŽ¿ ``V˜ÌÖqV‘ØS‹a|À ØÑF{¿–›˜þÌg>6ào4?3Š6|¨[q4%á„»u¾‰”}>äÂ|ÚË¢rZ?üÑÿ Èþk?Ý;rÛ¿H:ýèGç»8Ýf,SÓg5¦¢Gäïw÷w?èAr^ÌmII¦MEú>k6-Üæüê¯þª§\xá…mæBÚXù2hô™ÿ¹Ÿû¹6^<}ÑHì.ºè¢÷m>%k> E7tF·u¬u¢Û:Ÿek ª" DÈ-DS w,Ùˆyg,Ý»[¹ÄhìÆt¬O©_à*»%i¶åÀ ¢Z×ÙGÌ­¡@í)ÅåíY:ƒV7Lª)7¿ùÍheêÏ}îså0TXå&‚ ®µö¼ç=Oáûoÿmë&OŒB{$÷‡Ð*¸#Ÿ\ @Î{Ãʰ‹«<Ö€`ý«„’@0«"GŨÿU¾Ðf×Pt”œÇÞ¯¥Òæ¦ã¶Ê~¾eýu×]—`K˜žÀ¤á~þò¸°(Ú\RŒžo À/~k[XPA€k,€q¨WæV&¾óIØÊáuƒz÷»ßý“´ò$èÿŸÿóº2;^f 'Æ7qÂë_ÿzËôVßR³6*â•Ûo°7kÛw@”ëYµÆåçÚÿh¹ý/—¿öÚk•·'á³Ö”ÔoÎ=÷ÜzzŽ"íµ=Öì$»õ¸Ú|êFv¤uফ:\ªqb8Ùhà2ênÇX9k&< ±¬=ÚÅPlÅ\,®©b~` {Ÿ^i?êé  :ЮÙáQlC)P;@ÓBÀ*’…V;bé²;ÕV¹‰ÔóK_úúXÂm·G7yVDøY¬;0ÜPbŽn£@Î{ÃÊ:™)Êä òy ’*P‰r÷`•)QbˆV ¨à,ù ÆÀœ/ËØûµÔÛèôïxG̬¨E9ß~Ó›Þt¯{Ý«`Ï÷®"*w-ËA}R Ñ•k% œ ¨A)èví'¨wù[vy„¶>uyÆxÌÚ½6Bë3•¹<•<¿NŸá/þâ/ÞøÆ7Ú«´K7ØâëŒZ£ÏíG>òª}rôaýÇUɳÎ:KÝ×¾öµ•³c"¡ ľSòÊ+¯üÉŸüÉIuØÁ î?4O}êS¿ñoÜ÷¾÷õŠ:'MðïAQxóhÐ Ð%뢷ë¼!ˆá(³«°ˆ¦ îH±i¬Ÿ0û0+Ëy6ý°Ó±ÚêàœLÝl´ÅfÓ`™e€ 0Y¥Í™ÛAvHËY¦§Á™HéGéî9ÏyN7j+ïUn`¦‹ûÿõ¿þ—–[0yÐ_ýÕ_Q‰É[bÕYP÷Üqy8)ó^¬ÄPl ÓÕòøÕùV@P™ÞÃåA¤j¶”A4Â;™  À`<ö~EÌ­IÄÏ'+PðhõÖ·¾50#ýÀ>tþæ`ðw~çwÚò#=(pr(xpX§ÓÁ$ˆjÒ Ûö°ã²!¾@Û[[–>BÛ?vY˜Mpwg¯{ÝëöÀËXÒóÞ±P×çö-oy‹È‘²aþÚøë úÃþð 7ܰÐÂôVNð~ÿ÷zkU#4k‘ÝÅ‹ç…AEB[çãv2ªÈ‰RЪ–$ß#¢#ä¡­º!G—â¬EOä$z{=1ßC«œˆ¦)ܱd[ƒ°) aœFòÃVÌÅbŒn«ti Q~W!à«àTPÁUtëÖHl+Ú ´eú½ï}oÜ÷³Žwà¼ìrc•˜èvø C­Ýó„åŸ6éÙ³ m¥üö+Û9¬ÄP£+þJG‚Ðé´çûÒ þZw/SµhÎH <hA·ó´úÈÙP ä}‚ÑB {Àì—~é—( '¿òR!æhCG=º½¡¹è®„KM 69­©‘aFÕN~÷>ÜP ,tû¨lÿ¨öF•.§sÝÇo@í-(I#¢{·ù i_A±éžð„'DZà#uúÌ— !Äõ×_¿ÐBnÅŠcWš™ùx;Rë'”³5{`ý‰î%¥£)”]Ù‰sj³'ŸT·ÑJgôJ÷tRW»Îç„pÙÏaWÑ4ˆ€]þô#°S°¦Î^Â2ìÃD¬ÄÐiÅÙœõCÀÏVÏ=p…ºV7}¶üÈÜ LÍâˆÔTÍéJí٦㥉gnZµn`ÊïK[ÞÌÊ s|Àä Ä#H–ò»5[mÛéSHŒÃÐ0q–¿Xß½Z§Þ_¦î^¦#ÊN²ý5‘¦0žÖ9Jo}ë[?øƒ?˜7 …ò*wÅW$“ÿjº`Ä·;q|ñ„M˜7ž®ïlSÓÌ…ðÓÂ]ˆÆ;Q@ À]q¹•˜.}ÃîyÏ{2YPžºiý¾´ã¢C›T‹9IãîÒ{)O![/ûl©±Mé:ŸÁÊhè…¿åñeêR¨óþ2ëî¥#Æ=UѬì§î*ŽËM§SLφ!Ƈ>ô!#âJ=Hð—ì w/¹ä^¯“ŸéŸþéŸÞôáþo € öJ³*N-¸‚®q ¦] ß”aGbû'P8Å_L%—¸ôÒK÷@¯Šç¶‡Pï'Ô´‘–åÑ"zÓHfzÛÛÞ6h ™jÜõ®w%°'µm¤³Û•4"NÑÃë”0ËA(ç%¯zÕ«¬.»ì²òÈr€‡šÊ)¨Æ=ƒ<®çè†Î´£žö<‘ÓwµC4´EÀjÍ#‘‘ºõÿ‹Ø)XS!×ã%ûªúú‰([„e_½~Å”TpZ“ãݶ0Êo"fÐ F”·„)Cf±jh˜|üâ÷¥­bù^>`Òlî–§¤-?Ò›BŒ CK·"—Ç—v8­è*w/myð«÷¶Æ+Ü,tÛŠ#½ð9Kd)RÜ÷y¢ðÙF˜$¥úLlOâHCÉ|m˵Ûb áÐR ® kgElvà8s íº ÆÒ4xïmÍvh©1Û±#±ý;óxíèPQí›%ÄŽ™©úæí ±ÄcØ6}sB2_P ŠVùÇyì“3~PDˆh–Ó‘iÝåœU+¿®–íPi]z®3qÆfŸûÜçj«¶+¥Ó®ñºl|jÜ#êðÝCu`yã§vRÍ®“ˆ¿dDL$mmùÙ fÏ„±L÷vemX]•,’ö|®žè‚qµ<ÛMé2:Šv1c ?Æ¥ _hÒº‰ ÷À,¹ÊLL­y 9Ï™­529rVŒ‰eœþæ5Òz|i’À,¸{QäbÕœ6È($OAÛ¶?ÒÛDz1@mÇ'5ëæxËI^é5ñ®QsÓÝjÖÀ!/ßÛD1–ÃFËëaüâ+ ]çq»åè‚10G ¼ÛˆND¶û—½SÞ>¼¶NüwEÐø5~ñ‹_¼«Z)L+&Xl#ïͶCòÊÑ¿Puw¸ÃÚ] Œ‚|w¿þõ¯ÏÖf&–º3¨é­.ç=ïyG˜¥ÖO ÓdÈJBþž70yêùhP³ €á‰—|èº4½ÌašAMoÍæ TˆÙ~{<a‘7ÄDðÙº•‰eª`_T›*ÍÀ¨>õ,¼fup ð2`^³â(¶éhÓW]uÐúñTÖ pňëùÏþT´ÜÀp™«Ø‚Ϙ˜*ãW‹ï¥=È›ª…‘8…ˆ´ «᯿ FïQ`¬‡@¨ª'fÀ“Ñ4H(A&ˆF\è/èvuÇå–QÀñ/ àµq‰Æ­99±•È©ËÅ_œÇñ^`ã¯5«ì\¶ŒPc8'Ÿ `6‰rž.½²r·à À`¬«y‰-(לü᜸'~ç“âžq [¶d/SrÊ=ë1Æ}{{g%H.#Ô]‘³lŠl–âÒ½ÐLJQVex=Û²ºj1l›½ÛefYɲȖ‰VdgÛ“à˜„%{vZN>5•m°Æ=ƒuÓÏœþ9öX5^zÔµ ¯2T£ÓþÕW_]™#±À²ð«ÃÉ™z|©ñþ¢$ØL…qÖ­¨€0†…9—DÇÞ¯%é§ùoË™ EÃŒ–A`Öþ-'md »Uw—ݧo1éÆÐN4ZõxkCgÅ!b°Ù³ÀïÝÉÃÐþ–oÿ8¤ÆÎœÿ¶!vKúÈÈÅòÞmŔ⟨¿ø‹¿¸·ê±R ÜÚ®©uJihÌÕÎ?ÿ|'cSç¢îznyõ\~z…^ˆ_Saâ \ ¥’º[w åä³mD³×ÒçZ½Ü·ÜwPƒê ¸á#R„×ç‡PÈ…h ¤ŽŒ]Å5/³0ÂÄ5ËwÅâ­tAîÞ•Ÿ^&ÜE†ÒÓ#g[)P;@xöU+u>`ŽP N¶ÝítAí×~íø<8í%/yÉ}âFI¶¸)fYŸ3ù=¿²7nP `Vb_öfž…­ÁÔãKÛ I1°ió[mÏ€ ðê] µØ{¿–n[ŸŽèö¿þ×ÿj¤ûØÇ€‡5G– epuÞyçÈ”iW//}éKëîH €*ï±[+ÌÈÜ DÁ5&HìéÀì.`HO#Û¼ý˜¨ÛgŸ}öž™!ŠQô[våq¤}ÜüÀ@ÕG?úÑ6sýôk^óÕõ¨G¥Štd±qVY‡× Ø8^ƒo5£èLG>§·k>+§… ¼ž*m¼xMù¹Ë{ç: *¦pjùK¨\ÑÛU·:ÉÝåÓ¼öAñÞ­$4Lƒ5ävˇ åFµ‚+" cÛÚúiŒSׯҖ›àÚü]¥Xü@°wUwÞh Ø–  ™DÌ·Lîs óYTÁF§ šõ:è.(9ÌZ\ƒ¡Ä>‘yË[Þ²rFb#(€e‡}ÕÛx|‘9U®2à‘ÀTf«í `ùÖ€à‚b©QèÐù,Ò…å ŠNSLªxV|Ñ‹^$³~D-)âåE Ïå—_ÞéAýPœê˜u^JV‚k<ÓÆH5z^€½Ÿ>lPÝmÞþE:…Í‚§íèVdgŒÄ4Ò½ÈÊw·|,Uç²ro>c´)¾nt-»¤ X¡ê"°¬ý%‹ý™Ÿù™dÖ¡Á´…6•Ÿçõ¯}›Ÿxñ5¯¬2ßñŽw´¦ijï­b½]yÐI[“5† ¼áTô³k¤2-wxIAŠYÝÈp°“jOû¼*ãâq+W•YÎÏKg–ƒËë.*e}fÔGG:UÃ?â àOè—¼Õ6<…ÄQ HMŠÒ«¤Èã«ã )1Ó,Åì’PüÆoü†LÍþÅ_üÅ´üÈ9œÀ¬È°/=ÄÐÒ·_ÕgÀŠ•8‰mµ=A+Í*r€Wòˆ 8×üĬêÃÈß8 ®æOqŽ»lH ™¥’ðÝßýÝ­RRi勬$/ëû2Ø8ŸL R9•0¯,8l5Ë@ óÊÊé¸FC€£À Òulp2;JžµµÛ¿O|âùbÙ{¬¥}ÊËî݆zï‰ÑóÃþð.ýËŸû¹ŸY½–«Pò¡TùË¿üË|ÚÖ'?XgØú¸Ç=ŽÓÛN=lÚ`¢÷ÌîÊÔµ‹ U5Ë‘îüÁL[YŽžVeúÐÚg&âÊ´‘ÊQW·u¾ŒË3"4Lƒ5äRZ«Z]"ÁбË_ÿû<· ˆ×¯˜’û _dŽªà½Û>ŒòMÒýçÿüŸ×\ð æá‘|d&…so¢®¤I^ùÊWJßínw‹•΂ÓãTñ7æ…•­£œ(wm4éŽN磼„kÙÂx óÂßUtˆ{a %Á&â*ipJðRi0¶ÒëBP”os8ÎýVÑv»óúЇÛþ¸ÑϱI½Üò£³ÓZ&dz"ÅŸ8ES€û·í¦ÒÝÉ¡@¹-­h–[=+øûq`ž¨îæ€ÁX>HW•­Olíö¯ öi+µŸPïAOÞŒo~ó›÷ ¦³Î: ._ûÚ׮ߣ;a+ŸóœçÜìf7 Öë¯/ºh˜o|ãguYI‚3sV:r-Њ~íôX¦9󓨟Ç)¶Êu„Æð Ù Ó=¬µluÞp ÊÐve›‹tZ@Æõ Ø•Ä>-`e—¿þe–Và´~•iÉHªô¼§wGÎvS v€¾sI­¥äÅM‰nžµø“ŸüäH²R»#ª×¤e²Õ*ó+µÊeHl }#Mí®ü¸<„À¦¬¼1.Ý‹ ŸÒ˜í3HFܽDù?°@ÊŠ ´*2ÈøåÖØûÍRõˆdÒññê¸Ë]î’0€×\s'Rn^)ù ]lUòÉ.Éïë^÷º¨5)³àšøˆPr sŸˆãbX*Ð’¨3 À? ”Ùþ…he0Kì¨×¶Ï~ªêÛ¹ýKlG¼$¹ÜÏÚEÝluxÙÛ¼ì@SªYvÑ¹Üøýïcùýßÿýåb³wû’n¤ƒv*»ýn~ó›?úѦ‡ÇGiaG,¦S™¢¥bàH‚s鯫vh¥ÒžÊê€nèŒ.U#:¬Û:c¤½EŒ€G;³$Z'û0+1tòÓ2 d8{Ž™²„ñ@>}ÊÈÙn Ô0Ënº+âÕv"Ë}‡êQç9¦íŸYiÙÌ*0gÆÅ ?i9N¯jk:¿ÝäÝèÑŵVf1‰ñµX®}¦£†lòÁH*+ð€Pí06ë@8mvä ø @´D˜û+¿ò+LþŠ£¥bÒÞˆƒ³&ÉꨓÐànÿ®¿þú¨k’_îgo™Ä‰¸Ÿ%{ZàrùAËw…yÙ½EøÌg>£®=U!Žœv,xÑ •j2°ßà"…Æ6Lþ²ËÐ:‰ÊÛ_ùJ¸µ0¢8ðôò8íìštLÔÕŠÓ˜m¡á,´¼ê–vtWX'5²çÈ{vn ¿çÒIÎ) *ú:=e¶‰ÓÅ7h‘ 9“É2 Jý„}+;—ÔùøÀJ° œº9Vá¸k÷ïþD|ÀÄá­ËHI·‰†[9–:uÁ8ŒÇ -×>ݨÁ ®ƒ£´=±Ûb(¥ý€ ´ Ì´0…_×ì¸R¤ƒ= €Mð–øaq´``l9ð? E%ëPÓÐM À| ]Ú”F¶pûÇ5Hx¹Ïu6î'Ô{@ƒQgè+îyuF³b·íØ>ÅûËÔÀÚ眣$öl~ðƒËd6¤s r!Ð=×i‹‘ó ç—º2ÝZuÚ—ºHªø«º¡3º”uFûDWÆ@j7ØÞÝ1ó}dܱäBLÔ ](³|kŸ!à«ñÈtÔ+s$ŽV-ÁÌ<ûÙÏÎyxø9¯æ((qÀI 2é|ä"§/Š¥°Kä2$0#Ó¡ï§å*<‡„MÀa\y|ÁP½-þVÏ wˆ8 H@`RØ_@§–õ«€WÍŽÄQ£@Žò …‘Æþo|#ÊÂCšIª/øOüÄO$ß7”Å>¬U ZB‘:¹› áQ£áïž)3"È!@*Í2ËÚÛ-À¿²‹Î&P> æ¹ ëÒooÊe{îü)¯¸mÛ?žB|1Ò'º §®^(/xÁ ‹c0f0ër– 92rƲÛ(ámkQ§ùÔ§>Õfî6×èž»‘³ï÷¿ÿý ϵøä'?É2¹Sã´>`ûŠW¼â _ø‚œ>]pÁY1 ³óôÈW¤%êx]Årúª¢êé¶|äqêÑÝz´ëªÎkß@ºü5/‘Nud\³ül1Ô†ÎÞ]'³BÀ·Z¯ËÁhA€Á˜A°CPŸºÕYnpÜÝ ,,ÄIO·-'Ð|”Y|ò hZ:(ã—œÄ8å2$Þ_²`ZrÂt7aÆÅãKÅmù›Î€Ì xDȘ’Nü@¨Ã-@îdtéäPàk_ûZ„í+"êè¥}À$5– /{ÙË"Šž„´Øœ<ë'C­"öã¡íä y<åP >¡x"¥Š† h%þ °œÞ‚_ì¢ΡˆÖ(òòc`®Ì£Ø¶í_<4æ¶ê/'þv)‚Ó?$÷å/ùïþîïZ‘Û«´‡KYíGuÅWèÃ~6 HY#p¼g8>á OпFk¶`'vÕUWÙlÔá^QÒªV ‰Þ~Ùe—ý³öÏR@Â%¹KÅ‹W¸D€Õˆf5îË[¾¶·qÊd mæ®ÒéÆ>»³™ qWn ƒ:Ô²Ì-<ðBPH°D@M´‰¢Ûlb?eÛŽôÆQ`y9ÎYnSÖš¾dn±¯!§d.9^Špã’ý«õ¯ºÍ@Õ‰SN¬Á 0ÑK8_0±œ{µüÕÛ¸FH&H”ÎpAxºA-ƒ­+<.bEÒnÛâî.j‘ù–¥úç>÷¹ÈˆiEc(¦Å4ñ²ËóÏ?ÿHp v’¼ÁÀ&jœ1~ªh®°¥eðS8PŒ ¡uã—m$ï¡]e«¶ûØÇ@ÁÙî§?ýéÏ~ö³ÿóþOÞDØwþìÏþ,i¨³#Î #¸™þõýãö‡ø‡ï{ßûjÊ% 4¨ÒàW¾ò•Ýr:B…Ç>ö±»­Ø–g §Ÿ­¬¢½»N:!YYw¬S¸-“édñÄ'>ñ¦7½iG.ë $ =ïyü§€K™µ!¬*ªk$ËʼîÛ혎iJBsîXx¶@䑈9{wÍL¬4¢V̹fEà!' -3`«%WѪM 'Ð")ƒ10ƒ4`ƒ·A]S`¿f7F±-£À¢<Ž[âHïD-¢EÍ™„OfJåpòþøÑnäï¦m58'”yQ‡MXV_ê¡Å_ì.u©dú à$ÒLœÇTõ˜U™‘8²ˆWØ[ßúÖ%Ïì)¦˜öù„}¨´”…È1‰Ï[(ë¥À’¨·Z«Š#1(€€‘#@%ë1Šo…dX«è~òÅÀ²$\Z]wmy·gû‡‹ùt9?Yà¢b×^{­¶ìѱ¦çhäv·»·ØòZœj !+ ‘=ð–&>½DÊSo úûæw½ë] ýÙñ–ó4дUر䪬é´@7zU…ü8ÃUÝÏðiQsþf3V'é¹e—Ò-.PLaU¢’”’\xܪ[± 0UvÌG@@ÌK.ÀJ`ë´ €0€`€€ À´ÃÚ¿ xà„ ` œ ¨à:}V债¦À~¹X•‰í£Àª¥yD0êÒâËÊÀ&rB yJšË"ŽOif©Ž[ùKO» ŒÄ¡¢Ö›Â,ìË’:ý 1º{o{;`($ 0ÒÀS\°*0Gœe‹~ÛÛÞö}ï{jd##+&X9ìbÕ^rÉ%åq õ¾bZå}åk8¾kGZÓáƒD¤ð À$eéT ÕV<ï4 Œ_`™M#¸­»~`ÜÖ: éíÙþ9ÁBÇ){¶‘Ão*Âf,⣡Ξ_J5ܬ'tòqœÌÿ¡ÓHìÊ.‹>?Ž×Ô¾ÈAM+‡Ø-ªÄeò<®Øv[±Êó®¶»¢Œèíñ|“Ѳ܋™eE›¨HSæ q`¢eGöä;ù—Ù–éÃ@F/XƒAÙø…eñøRÝëíÀ@Â%xTLÈ8~ ­js$BŸ°6B¯obHu< Åäaj§Àê!Š JžwÞy×]w]ôŒ¢Ìb蜘€$ %ð€à ØÀ ¨ª|€ç.ÆÙUJ‚( &í/ïg%Ö=qS.·dûw°ë`ÎHBœîŽ‹7ÜpÿøE¶mâ›?ÿùϧ1lWs{Üã~à¢ÖX`š&Øq9b.ƒC©³‡Ý]  5Û†é­õs.i„‘«Ø°µÑÛ‰RðÀ*á¹Ï}.9qk>¤Mòc}sËOÂ¥Ìú)¬Š[Ydhª~‰Ÿýár¯t[ƒ†°\lù®¾i1ŠéL™áa¹xÌð0nG3<` Àd€›î¡>mÚø×>¥]÷ÆåÆQ [¦Gߪð‰ð/ÿ$ï}ï{ ð£ýhä 5[};Ý*‰{ŒŸÝµøñÿq‰HL7Ž8ÛÝ᜴`P¶mØT¦ÅX‰¡µÂ£1ëÑÄ­È4£™ê@µÝd£Û²dò•¯ï{Àæ/¯ú¼—Y¯Ó‚ù«¿ú«î)ôÑUV’†ÏÀI–á2‚Œ³Î:kì;¢ÍK0†€ ÀD±K> M¡r€~@XÎ\³4à#HÕ-Ùþ%DÕhÁPtˆwêY–¦ep×CT­Øní¨þgùÄÂ+=þñÿå_þeјsðÊf,áõžò”§ì™<°Ðˆ³5Æuö0™¢²’©pü3È¿Ÿ/ßþo qÑEq/>mP¦[ÝÊR5‡fšÒ f5ž†<ÎC—õ/Á¦ÏZ?=1‘a‘‘‘Á‘ñ±`GÕ_¬Œîæ–¦—á5ûs !àó,+¼è<'Ø×šŶíb=fñOúÓÛaÒOŽøÎIdÜ2 ¢BóíY}üŸ¹išøj–ÙqïN¸3ý¾¶é“LìˆÄ­üï“Caöab½f‹¿Øé: yÝDg¹6ʃ… Aë$k‚xÐÖ= 9Û°ýËÇ „aûõ®—^z)Aê:]nòZ–r¾»69„¯gŸ}ö /¼ðBâ<‹ËVçÐ)¹þ0ríú#8;Ç’ tGüðdaÁ¼¤”ûulÍÃ.ÅÎdS]#šÒ`¼J$ßßz¨nèÌ4^|ls ¡:ohh˜‰†gà{0ÃCd¤FpdGüõÍðB O¯þì!ñ¢½ÈØmu§Uò*4¦·FΑ¢@í¡‹›%Š¿"Øn‰ÐšRN®pÌÆùjê„5‘˜pwä¸[›ÃLKÉSžŽÓ¬‰®N¼…•Š­ç‡ÑØe§ÖØ/£P˜r÷5ö~§œ¿Ô§Í0öÈG>RŸ[Û¿ãÐ;lhUš†é H~sRMÔ¹‰F܉á»e¾¯[oFÓFFÎVRÓÅ +0À–@%gÀ3{x‡ €7õ΢È®ÚÝ­$ÝŽƒÚøí߇>ô!üsšt ñ¯…_ÓA³`Ú‘šU Ò¬'=éI•³çD|¯(üÿøÿ£ mBò¢ÔáU?rvkl9¢3mqðŽw¼ÃÿüÏÿ\k–tÝrÁNÌ'ŸSÝ·½ímŽ+«Ïqï)ÊþóŸ¯ü ×#4Rå5îäqµýË(”×1ÝÓI]Õá´`²¦â”¢i-'Ĭ>ì9­ºÚiÖí¶5‹?4cÜmÝiyàÏi­é0½;rŽì#"m_ "øÝÿþ÷¯Ý`k  ¨Âö΂(ogS!'F8içÞ÷¾7 ¿áæÀ #‘„Y ›Še˜ˆ•ZÜÄèÖØ¯ö{€QÓˆ¿ 4t> £7¢^)``ÄÙQ>/лßýî…+î:ZÁt -ÚËÖñ¢%ÇUìba·Ì4f«‘‘Ø2 `7¦W` Q¨P‰Ôx¦³¸ ö „%ˆjpÕZ>OÛÙâœÍÞþQ¢Ë›2„IqP™sáý4ȯ ̱ÜO#©ÃÖÿý¿ÿ÷lS18ä眃 ˜Y-g^7w48Ì”È_ÇnŒbEJ±MbÇ™Ri™!çž{®b6ié ÅÈÙžÌf*¬®ŠÙãQLÒ`Jz„yœ‡z´Ô!dÛ·Ù´ œhxkÈ1ÃC„3<LOfû¹«Ì8ÊÇâ]Õšކ ÈMoí!'/GÓa?JË{xî¨r)`áž3jî@ÈAJ«ªf“従ùì[ù ÓÜ GèðrÉPµ’ Ca2qG}¤º„‘ vÜq‰qØj`h£1ë§û= •øž±÷;Rp:ÁΗBY3”ʧ™òNž){|°uÖÖq'î³hÑ/Jp½—þñ?þÇ¿õ[¿•—µûñ£~ Ü `t±ï¢ÙL–VKý©ŸLÏÊl?ؼÑ*ƒCâd‹ÅˆWùi”#N„Cµ4Ê=ùI¸ŒžŠMë¶9äqZfx:3<ÝÓÉUý744ÌÎg݉Уs¡Ìú·âA£×¯2[2§Í€7{w·™&‚1š¦ÆnëŽòÛG…}»œžº›˜ÑÍ|Ìk´ÌÇl6J£û_ÿëM­zžð 2R#x^¡avÔK›ò>÷ƾék£Ûý^Û稴ÅFzP`Ÿþô§“©Uôª«e÷A`J]tc»åîgœáX¦ÚL`ng>É¡Þ|æ™g¦áI®¼òʼ‹h;ˆÄ¿ž;‡Š˜…vìÆô„,`(Óƒ LõÀ)h00¶’Ñ—S` ¢1FÚjá¨%6xû—ÝÚA9Ï8@[¬Ø§È!Ìš›* š#ˆƒ5ˆÏð×Ê€ ¤uwËR͇¬ X—#V¶" ˆŽaÄoÕþ: ¼ìñ4Â^.öÜ÷ˆ…ä–®ÆqSd †SË™kÈnø;:¤ÙíFz¡{9vÆè…2ëÜ"¸2 k¸}ÚšÖ³â¸è ö“ÕìHl(Ö\Ö×DžÙLýš’ê²7‡'!uÜWF}ÈŽø ò^Œ“˜²IfËÐU=\$«ªüA¢@ôŒ|”a¯Ÿ/¸à‚*Pö5/ùËkeâ}ò'ò'Êp ÀU¸º¥aÿå³ÑâÁÉO–ð¿¾L¼ž>‡ŸØš—FcwVŒ uVÍ2P‰‹ à©î@ZSË Ôˆ_`ˆ‚ëá'ȉëá¦nÿròkùå—uâ‰qê}·”f`žË¿%¼ Z0íÆ×¾öµK.¹¦ <" >ŽíÿÇÞyî9ÿŸoµ¥*Šš‘6ŠØ{Ô¦FK¨Õ¨½÷ bÆNÕÏ Em%*f‹–Ä^Q#±GÕ ªõ{ñæÊɽžçóÉ'Éóyž÷ç'ç>÷uÖëœ'Ï}Ýç:×õÕ£›c¬~öÇää|rÈ!$P26÷ÔÌ—JÇðøR]pÁl—K‡äm1/HÒÊÕDú‰$–ÔωTùLÅ”¦**Ôn’4Ds4š9pƒ¥«ÒÙè<õ«ÅÐ ÃŒG"nQ9(àD%‘PìFN»襴Ø!ö½:Áòkwg҂Æ ƒCØÏ¤·œnMõ<Ü뛥=m¥ùuä[ÆKŸ²7>¬[¼7q䘗 ñFÆ›¸Æ2Û}@5Àqw¬ÿ44Sé'“Å”1qz¸ÉLkaßêY…iyzÌP©¡C‡ê·ï!É“:Ë’…Šu›}úÈÂ9LFUV'µ´’1ü“ÈÓ‚O:¤l;uš©TÔ&W¶šn¦>—Ì8Y$,=R²xXBaÿ%ã¬xlVY!µ± 3K4­¹ÕÒRý“ëEôø°žÈiS Ûy—ËãxršÈŽaÓÈ’Å#®SðP”ZòÓ W¶GyäwÜ!;xvð°® ¿˜ò &åªDߥÂOÃÓ©°€Z¢cxá«3ÆRxè6îfpG> ªŠ‡±Õ@£|3ó'U2ýd „á0(@B€Á2dNå@@Ic— t”“X¦]jwšvéÓÝîT0^=°'²*çK¡w®|M:¤BWÒªôõ]Ó0‰ f„ñˆ/SÃLÙ«žüâ¿)|ñ:Ö'ë\?Úî“'fŒé+-Í‘>ƒ0ÓÛ)jE>ˆ1‰L¥2U*Óê%‘ö¥ ÔC€w a­ÇÎÌ{ìÁÚc[&-{ÅW™qÍâ'”¼¬ øÅOÿëÀ®Jz£îòØ +Pèã¹?mÅéÎE€IÔ˦U¢šh&=|Y1"–„žfu—òI× 2r¼Ï2K h“™ɲÔÿ‡,T–k*ÓjéΧþa(›F,:j¶tbªC ðäoý¾û¾±š/6»´^Ù çíïB8ûqÆg dõÿH]rIæ?yPfȾù¨hx÷ÜsáõØ+ç½ ^(Å‹dÁÕì¼l<0¿Ì|¸$“zêÙUg¾4Mè¡K¸0¡{tR[üt;:óãRÃ(à€PàÊ©fþ˨9äŒSLÓLw&¿—žðÕ o|Mò†¾íèž‹4ŠÇ}}‰b˜ÄAÒFj†4Àu×]œªrÊ)$´1ÅËW}1ùŠq(ŸÃúÒU[|R ßM¶¡^yå•h‰ à€(”º@G¤€¯þ8ØÉ×0L —L2Yª„éKƒY©Âh‘DÅbHÅœ6¶ÀrGë_scHsÐ#­„§yinwÞygšOš£¸Ž‹WK˜Ø¤›{8¬Òkbi˜T«K4„Âã™Ê}Ù°˜>éöL¨iйdÒ£Û,Y]±¨X$,•üYwY`©ÒH ª–ɲÔúd¡FÍ­™è|êßÙgŸÍä±óÛQá_:ÐýƳÏ>KßPWÚñØýïÿûª«®"2^Ømjò‰J†ÒBµ…Ï!F‚]¯X@ÑðÐ.½ôR¼!áESÿ‡ê¼,›c™µþòË/kg;¢:m#1ÅTÀMšHkã’np 4¿,Ms²(£t##FW©n“Ïá †ÆnÃd°éF_Š"Ò@e"ù$@ p°?ÓtÍK¦˜j©„I¯)\-+íg¦.Œ«‹Tßå«!û¾,Õ’¾ÛRÊúõ¥HQ`nïžpêË+dØñæÜ_:É+°x½ Ÿ¯gêST^$¬O´4\Sb\@Pò´ÅÖI3p†Pä_l-õÏ ÒjÊFwò™™!0AL“ 3q—ÈÊÂeË œ0v¸ë®»´Þ°CÁ0GéO>ù$­Pk•C"if¤‡žHÚEÔn!¾XÛ™J|Ùà˜2ù’Ð$2¡L«Œ?eAƤ3õÚ Ôz`a” ŠåÄ¢biel»´óÁ- ²,©‡%ªåTVU+äw2õOv<ëwÔÜtlèmÙObsÌ» v¥±cd¯õGü¨ x³åôZ~*þS=DÑð8´F&;](i< èPÐRômá“49uÑ÷JaU)‚áúCÏÛj@Ÿ·ó,³­Ù7š::C—$O';®Y\eX€:‚‘ AJ˜Aűz)ì…Ÿô‡)câ˜>&‘©dB™V&—)f¢)EÏëìa…XÇ®C5$•xâO'VtÛ·:#¼ ÅŸËóÏ?Ÿ¾™ÂG2üŸ£"á K›þýûk/‹"œÎgc*­3®xñæË¢ÿ¸( UÅ'ªFŒM°7úÃÉë{1pKÆíè(ò l2… D¦@xeÿ&†3e)v¥u—t~êóÂÎ1‰$ •7£FÒ™+¦’ eZÉaŠ™h¦›I—€–AµeË a–Vf°,?òYŠ,H-©‚±V»ìLê2÷dÚ:0P#Ïô, 6Û:ñ¼9@©À‰Ðõ×_‰¶7Ûl³^z…c­ÚŠOÎo`¤Ç>?ð¸£Õ1¼´'ú¯-¼• xÄv¼áØqÇC‘KKU¤i‹þ Ã ùu˜3ryÿ.•Ä­ÔËKµ?˜(R– tƒ¾Ñ%:†ä’—)ÌÇ,ÀvpM’ò£CNZ-ìÀg h‹é‡{ª¡âSM‡™zË€ÅÀ’`a´ãÅRdž€gŒ|Mø²ð•á‹“ÙiȨZä…Xxoªÿ%$CàÄx[!£k^Ö¦¦¨=[Ð|¯¿þz3%ÊÖ![dÖ‘~Ý:‘6X­ï14È0¬¨Ø} ÈóÉ[{† ¨µÝ|¦€|¦#ˆ1M²“ùHH&3éq× èXŒe©÷˸®Í4AŒ"dø ­ßwÙe—!ڿ袋äUK¾Jœ×aB¿~ýøRð®$~µ»wï^øµÊ´îË)N€ib²´N˜>&‘©dBÉar™b&Zw™z€þëcI”õœ…¤G2–VFFLj´U'K4#Ó‚—Iý“û¢‡wÔ<ñzIË¥ðÌkCáÇœ·³„§Ã˜˜h,JÞ@Ä\-¦Ì'ê0Ð%æž{îôõ9b´ˆS~옉X˜yé›—ŽÃ²suË-·È? 5ðŠ——èyùš9úv¡ô†®E|…š¥*f€ÐüÑ·‰ ÅAgô¬I÷¤™ÓáŠÖËnDÚ‹ W  Iƒ´¬”ò™¦† bš´<(¥?¦’ eZ™ÜÐ0¿½9Á¿, K…òañ°„XH,'K+߇<ƒ•óe¡[r ™N˜Re@k· ‹< é‹É§þÇàS¥Â½d¯° mâþW܍Н&Ð|MxS[o°¡´Áj}- 0ÅÐÄŠÁ2äüvŸ°ð¿VÞÚ3ˆ‘µê‰)ˆI!ŸiJ…Ó´JaTB‚O¦>½ë´ t,ŸëÇ7}øÁ•?NÝÒC€z¤©8 Î¥¬šùñ‚8wG?¿³2Šþç?ÿ©ˆó¬p¼ ´ïé¨c!¸¶ Lœ>0_LÓ‡0SÉ„2­rÝÉ-Œ¿˜t¦^§cX …ÏNjˆ…DU´ËbcÉ¥1$¤¢£\‡D[1ÑiÔ?ß™ZÇyzî(Ðò¸È3:g½äPãR‚έ°Â „¯ù|ÏÛˆx¾'Àö”1äý7þô6þxûËy}•ñô_±|óãÒ~wì³y}ÔQG½÷Þ{yÉ:sØž¢W²Çà]H=N>«kfû>L(IL¼éE¸U'épu*î \ª‡Q #H+Šdn1YLÇôev'˜b&šé¦f¦^õv€…Qóí Œe¦‡rÒÃ"d9Q[‡x Õ@ø²Hƒå듚/M 4@V@X¨’Ñ'—„Ü”YcÏž=ókÚ‚F‘Ìf ¯©œ˜%|õ8ÌÿÕÚ »î“Ç‹Œô=š+´ç }Óm:ÏHlJTl÷¢ÂÚ3@‘5üÁüüŒ¤’™tÌu¿ _N":¹Ç>DxÒ"äù›Ãlqc 3hÐ 2ù]®èCja„Ö:ü\Æo:Å1~–²ÇæO¥£|–¹©hË·&¦&^Í3eÚþeS;v¦˜‰fºéR–ezÀcQ±´X`,³ôíK…§8$‰ŠÓƒ“‡@ƒ´Rõ“ß ]T7@†™óŸ ˜@[ ðõi¨¯³;Ó 2`[וå;ë~ ò¥k…nÈ÷AzÏ;ï<}G°ÇQ`7]ò’”ÙpfÃdøì°Ãa'òqÀ[¡OJ%q ÿIz÷Š¡5š@q¢A0)²gš˜,õŠéÓJ`¢™V&7uE¡í;–AÅXB”¥•ÌDµÌXrä(ÍRdA’î@Ï‘]jü[Fý¥¦ÐŸ&`m%Ðøÿ¹‡SŠ€5À¶~›:£¼u¿)õýjÍvÇŽËs<†|œÚÒ÷#PpÒ“œðç¡[‹­ F™£­äæW5 Kì»ï¾rŽÁNlûì·ß~í8‡ßšS6©GÍD0š/&ˆi¢E¦Œ‰ k)&4cA&§X–Ux€gñ°„T³>Y`,3M°ð”ÉRdA²,Yœ“z°¢þΤþu  î¤ ˜€ t.¡V+ 2óæßÜÒ¤?·:S6êúmAU'ùÕÏœ <ñÄ3Ois˜\â4¸¦¥èu'e´@IDAT×]‡§ ’ದ='ÕRyÚVš¦Kt,sºÎWŸë®ÓÚ3¯hŸð™¥™”Ë$êœÖL)_š@‡}rì½à -­öÀ&«éøo„`n¸øÇ°9SzÀ€ˆñˆÏ's—¯§¾ TðÐOÄ&ÂÉÉ9™œö¯|¦¬/'¦@ž,˜¦† bš˜¬PÔ™Äðú½bº¥×á‹!2#ÁRaÁDˆHjf9±¨XZ!C‚…Ç-þ8à>Y–éÝVN[ýkåÙ÷ØMÀLà+õ¨ 1Ç/(áwŸyæλ~õ£úõÎ"8APžjúÍ”-<ˆ.Tóµ´AB¢z‘áH^œÊûv_=: L‘j}O=¤t£þÓ}é¸*|{¦b‘,x£Ÿ`~D@V0ÆŽD=ÂN˜@‡Ày#‹6 >ÙÉ7W¾tˆ… (¼ÚöîÝû†nHÿ? 5à´ÓNËW¢xËQ§ôÑ8÷®¸YTÎ+^ôä :gò¾^2L SÃ…3M\aüdÜR$£ö³0X,yŸŽÅÃBb9åG[ÍZŠ,˼LkæXýkÍy÷¨MÀL`56Êø­]`øÄ ɘ1c¦¿ÁxàÕ•\ 5…¡Ž.À Dö›ìvlªdìïáw>ž?è|æ[0:Sjn´o»Oýa°Õ¾= 9V5ÀÁ.0šº/Us*óEœcK€íüç…]k¸pgEÜbÏĪ yì¾ûîéÖ»ž0,4Ð{%ôå ~Î9笸âŠúîðIm¶úëØ)®YÀÁSÀt0)ÃÉ’nÆôå«b¢õŸ6SÏ]–‹ÚÂC K…òÑba@8–\´Ž< ’e™o«5s¬þµæ¼{Ô&`&%P­6 ñSºÆk,¼ðÂ$0Aä'ùÞ{ïßWü'gk,ºæõmĈǯxó6]Eå¾d3¸çxÑT‹ìà±ÿ–nr„NVÛs²¿‡3qþÊöaBÚ`I*Üî£3t©°«™LÈ0åïŽÏ4’{F2s ÒÀ jº!³[¦€‰àãÍ©žÄŒ°/M`ÒP8¨0ðÃna[<ij’µÿÊÀ¿±qG>ßwâ?½ÇÚ\r<,_ žBøaçw&°ÊK,$=zôH«Â¸ÝAóô&Q¨®‰à“‰`:tÉ1ML—L\êè%:£ƒL:SψÿöU‹Dÿ÷jï—%ÓKN-j² Ó»-ž¶ú×â ÀÃ70ñ*”t!~J1’ÄQ›ŠÐ’’DÈÔO,ŸÑçÆ·ñuª¶ ª!¿Hgx‹ÖDѺŠmdM{ΰE˜"Q\ ª¥r—Åî«yº/l[­=£,0£3@&àäЦ@] U¥*¦/ªuÂ&ù} õ·…íbËǪF7àíFŒ1‚ؿگÀ¢‹.*§‘…š{ì±i§áË/¿œz°ñ‹HñÚDRYdxõ­81‰YÓ!ìa|Á¤05LÓÄd!ÃÄåûððÃs—²Lz,K‚…òÔ#•’%™i‚%§âZ„,Èôn‹§­þµøððMÀL`*D¨è?ØÒð‡9âm·Ý¿Ð$Ø‘S(§ *-¹h«-hº¿—†ÿR°í!x&`ê9¿WÒ£/Ë U壿Ò(¡ÿ2{ƒeUµÏÚ“ÚÆ”*A-æÀU`¸'~7Þx㉉Ÿœé¤/S€/´ù‹™¤™¦CÇó˜ ™BD쇨ierãu™zË€ÅbJ°``ñ”¹Úа“,B–" 2SC+_ZýkåÙ÷ØMÀL €@™"!ï/28Äã¥~›ñD¢§7.ÑOøÄ4ñùçŸ/¨·( [ÐN8! #3¶ ©¾—ßš#‡ÿÄ& “^3ëi€¾½ñÆE­ÕΣ ÅcDTKå4AC…(Ô3Öž 0ueQÝ ÐɼV05¢p÷Bß(.CÜÔïKÙ”U·å»&0I ÈD™5Ì @Y[øA`Í5×,øôÓO¯¾új¾k_×ôÕÿcôéÓë¾4´ƒÂÍs86”CB‡Ÿ|òÉq`Œ¯³Ò™¡C‡–5çüö©ôí馛.þC>S îTËÔèôr~Id*™Pýh–™n&©/ë ÉB¿ *’:Íb)–ÕÓšùVÿZsÞ=j0¨"P¨N°ëÅÏ-æ”*©*~ËqáM¾ lÀ—Ÿ|Ù¯j#¹—Ú‚ò±ÕV[ñÛ_¨n•ÙsÖ<˜´VäÕrÞ™gþt_µ¥(¦Ût^Ï@0Á«AMw2io€¦7ßÂ(¤àÕÀ%ÌP9Ó¡ËÂÉJ«uÚ¦Ò>Õzë­Ç£•~ûy¹®—ô\Ös0Ýß‹#"üZë…ªLß+’Nâ%ìŽØÇ«Þ Ìl÷Qâ¨aeEi»ÕÚ ÃE–¢T .1¤”ö(¸,x¡j€«õt6?Mi6)H@à¬^ù{&©¸£ä[\ݨïV`8YÕÿ`L¨ž)(í bš˜,¦ìë©ûꃩdBå†)Λzfª’$Ë&“—,6ª G£e¯B¾ÕVÿZmÆ=^0¨—@FµÈ9 70Úž’6¸é¦›žNÏlX…Ò¢VS}/¿¿‡«Üè0D[Ðzûýe›Pz•º Õv™±“€s¹üv_-ÊÚ“óÀA… ¡ÐM™6"m¥ tôŠzS`åî%:#nÌLP8a B@šöÃÃp ß7<¢’ñÖƒ˜àù»i®_ä\.ÿùÿàƒ¢h·œo ·¤T ð裦I³I'Ó$Ì'¯i02ÌHú²N Ók»Ð¨A äü¶*Ó¡ÓÔLP3qL“HsL(Ü*ôî“ö‡EÂRA’e“æ§i™HÈþ“E˜ÞrVÿ¼ LÀLÀJ dŒP<¢e(-ú9Ç9›¶­8È«9là§—pOl‚±g•×÷ÈÉïïa0)ˆ' Üñª8škS¢p3Çþât_›¶û [O}{ÒífX½7¨ƒ«$="ƒ hrVF¹=,xñ“M‡ž™šp‡€"¿IC¨8ÝG‡e5À9±šßa‡øŸ!õãO‘›nº‰3ºúÿ‡»üaxÅWdŽÝòg̸¶ _Ë~RBæˆ5[·€€ h¢§O!oÆt‚)`"Òs}L“Å”¥±Ú)„É­I˜EB‹,˜ I ìÒ¥ ’, ÉÖ¼eõ¯5çÝ£60z ¤jF¯^½ø5Í„›Ãº’Li€z2ã롇ºòÊ+gž±Kõ=4™ŠN EŒxžB­ª(Rv‹“ÇwÜl³ÍFâK2?ª¬`u>]¢cª®Òá y‹KR4ÏB5Pàš^W £ÊÝKÔ,\&BÇœød‚â®&ÐPx9ÂD+™Mì £>^$!Ù­[·üÆQfD™˜àé]6Òÿô§?é¿)}19жÛn»Ýwß}Ñ4ʉ¾_ì5¥96lذ´6§Ë*=°Fíì6t?€ƒøqaj˜ ŒI­höyMÀä–5ª|–‹„ª(R&IÓ©ûÐv¿@,«¿ ò­þ5Á$z&`&0i „¨í;t˜´=Èá'9l)õìŧ Hl³Í6Õú^Z§Ò¿ Dû­3F|Z§ûð½Û}<‰êa”.‘É­v¸ ¥tFï³ùl“oOõ ¥@¤K>#`3Ѐ€&º_:ÑN7 1cƤ‹œ] ±cÇö“Gvmƒ_{íµ…i怨–w.eÿ!`On?Ô¬»<òH™ ÒÖ.»ìB>2„ÓÄÄ@2üÏÀŽwÓ¶œN Dñ_(è(Ô : ƒ:Õ.Ô2òLkSšIÔ›>¦57“Ãò 6–JÙ4±ÀXfÈÄÿ«,ÂL%¾´úç5`&`&P›@h€ü¬®°Â ìA•mdÉõ ÌùçŸêÂtÅQÀÚ ù%›lí°¥]ûQ0T)îCÙãc~ñt‚béÉÀê^•Y{V—JïfûÑU@K]•Áœã/¶LÁ|å[÷K‘:ݰöÙgŸXÉ$0·^zé¥÷Úk/"¼½ôÒKi·Ï:ë,V]uÕ4³0 Ãi§V(@&ÑÀÑRØWÇï<óÌ}Xf™eÎ8ã þ¿™ïÿûD»ä’K"º {Pƒ *«¶•óÁ";^`‚ h  —À)`Á¨Á|¦€‰( ÎO&‘"Ê|Êœå0K%Íd!±œXT,­ðû¥n°üRI§EÀêŸW‚ ˜€ ˜@]Ðqj?í‘r²ë®»ÊÔ“K›o¾9w#PµÇQ@<šÈgC]M&BõÛ‚ò’n÷•îÃH 󡌛ÐêÍÀ6Y{&}Ÿ ÉðåÖE‡ýâž¼¿€NÑÒ€ ÒBKQØ2¶ù tN42Â|³bµÃÿú_"þ÷À›ï {éwß}7KZÚÅã?^sDõ˜ |ðÁ4„ã™·ÞzëÎ;ïÜ~ûíCÇ£믿¾Ò7Ero\ìk-¶Øb<ð@Ín´ˆ(¢Y À4M+3 dPìòúÃD”ª0åÍaaÐ K…òañd¬ú醌#ô6­f4‘|+­cõ¯fÙc40Ž!À® ´°xä­*›Q©i"[jz>àt‡ #ÓãpÏ>ûlø±¬8¶QÑÑj[PöÐn»í6öñâíol÷UÔÉ­Ìf Å©„ª¨0 N¼µ§ªbà:” €Dý€Ð@'†ÀŒ»@5ÀE•)°îpœh|áâcf£ôïßW³Ì2‹V»>ùêéQ~µÕVËl Žqà 7¤ ¶ˆ…wÉDEYe•UYk­µt&Mä)–RÐäÿ“·3a(NAœXŽ1¢¬þVÈgø©»Tàèý¸¤ï ¬@ Xò«à`'Ÿ)H]¼d 1}ÈP0“Ÿ¿dIhëEÿÉS–?ˉEÅÒbÅa^¾ç@Àꟗ ˜€ ˜@ x”9A_Ò)»tÐAü*gìy8ƒÁËZòñf™ñhRò¶ …Û}·Þzkª¿Õ¬aŠnN¼µ§ZgÈ œáÓJz%,Ù€&×…`Ìt¾{Í¡YÀ¦,<ܲìùã«=í ºÄ¥—^ŠƒÅ_<¶$¬AÜ<²ÏSxb²¨lFU„ à›ÂBuÔQi»ï½÷NwÔZ-n°Á"Go9ì°ÃbÄvÛmW:šÖßi†ÌÀS`ˆ%bú#0AšŽàÜ~E̽L´8i&©gnñ±`X6,©£Q–¦³ˆ´Î‹|'RVÿRN›€ ˜€ Ô&P¡Š`<©^»jŸ0£æ¡Õà*@ZP>*`í¶¿•`»LõÓ\< ðfšM3´Áo¥Úó¯6c *§¹t3³MU§‡ý~Fµ£ þØ$cZðTÌihLÿüç?YáÒîòáø¢ÏÚ”#½Ñ&|²ÛSxbg¹Üe·'óŠ:Iðú†ÿpø+üþ¾øâ‹NîÚµk´Åׯ$~z#h8·Ð3Ù~Çš1­¹YÓ “ÁÆ)Ã@ÀÿG‘ 4Ð0Ï£Ôb^áu“)Ó6“5 s–âÓ’`yh‹/Šd,0ú¦ÅÆÂËÜõ¥XýóJ00h32…Ý)^᯴ÒJü c¯È~]¦‰< ¨í¾ðÝ¢{|Žgb|emÓ%UQaŒ…&h®úd`Yý¼œ.<ì'yàȤS¸4–Tƒ-C]ÖœóM  Ä·uàÀÕÝCaà[À™1HVl n²É&ì ±c¯¸!©¨–w.Ô‰‰àk¯½V&¶ÓN;!¼÷á+¹ÔRKE üZõë×ïÃ?,«¤³ç34(÷]1p €"^„)\eƒ²ÌzÁ^&C>SFUL“ÈT2¡ùS|±ÅÇbÐ1Â;¢Nn{î¹ê!K®Z²•ïZýkåÙ÷ØMÀL ýÊÔ’_ÿú×üúÊüFf¢©˜h¯Gy[œ9ÝÇN‰Ã/›£éš *Ñ¡ršˆÍÀ“±½ð°_‘Ç4ÆÝ2È!à„ t p€Êq R³Ãú:° ’Úäý ‡Ä2'Ùe¢f¶†.ºè¢2MLñ¢„jLÅ94/ çà¾2¶ùÊë5PäÌ<óÌ„(7n\t¯  ‡A14M“ËÀã”9`‘˜1OH‡^ S ÀÁžÞŠ4ÓÄd© MŸå“ÉeŠ©?³ÅÇbà. #*)K°ÀTK®LÆùVÿ¼LÀLÀÚI P9ACã×—÷¸<(ð'·à…fWõÔv_ªƒq„wÆañEýŠEÓ#ž½¸ÂHî4Ds…'+Øa+¥‡›Ìa¿(B·é0ˆÄJ/¿(B¼QÖ èDˆ®‡rvÂkv› (cX&™žÌè|‰´1ȱ±O>ù$jxçwdÌY­œzê©4Í÷ñôÓO¿øâ‹×\sÍÔ€ü9æ˜C¡¶ .¸ B™Œ¦<Áˆà04È05F>>@僨bDÒó© à!ÆDè_~‹é‹-¾Ì)¾(NBf¢,Œ4³0-S zÎ’+p&¬þy˜€ ˜€ ´Ÿ@^EAS’ž&—nÚÑÊø€‰öŽ£€øÛäŒ\Ü"ÁÝüvîÝRÛÈÇ\uK†”|¶5F|Æ·'Uš’Ò4HѼ›PºÄ@È焇È@¢Ÿ‘/B$\Ô,á<Ø(è„ t:¬jYiò`+¯ºÿh :Œ÷ðÃWKr—Ú0(廆r’á#'=1È5ô¹©¼þúë+ªEÏQmgžy&b¯¿þ: Ï"‹,B¦þ8;Ù%^á¼¢‰¹E·é|¢fPéy?†ÌÀ>½…t?àTt° 2¨^vŠiRmL\ÍÅ@s,ª¥TªÏvƒÚô:€ÅÖI'¥p\žiõ¯Ã‘ºB0h-yEE6T1JÒ# U—8 ˆm^ç·û¨-Ýî+«Ší;5ÊãBý¶ ©µ'Å©¤¬~åó`AgèRXF¡¶1jé¥ AÖk™È~™:B'‡F©KYUå‘fÊúÒ:ÝwßÎß]wÝU³ó‡r’[o½uMI ÈäïÿéÆ`|=¿nö+“m q¶­:QÅóÚEøžJ»P…2m ½ä’Kâì¤Î®6ˆ¦Û™pÉfÊ'ôáL°õÌ@¡Cƒ@–!ƒ*瓉H·ø˜&2ë1V,äY™ó—,-5ÊbËßuN°ú(œ00h'Œº‚Äï=Ï?ÿ¼!~’9éQ¡VÅQ@6î8:BYýŠ£V•m÷Uô5µ%zE»ÜRzšc‹²ÐHµ¢¡üf ×d&²_¦ÚÕÑÚ ¾6 f`f úÒ:)ùtaÁŸvÚi5‡@¬KÌy9¢W*5å £‡³4dÈ^©äO Ò ”Ÿ½öÚëŠ+®(ô]ÉÆ”4@ìÒÖñk²ÄKP<ÿG€»N)žN*b~ -§á#ŠBŸ=  C“Œ:ãSnñûB·3e)ز4 €eÀbHcÌ– ³´Ô9^ÖJä[ýk‚IôLÀL`ÊÈ(-ÚËBsãe¼|Ÿð«\èF]ç7ž8Q¡ñºÎí¾²‘×´Í[{’SV[u~lÆÎa8O®òøà€P¤rcu»¾kˆgÌôfdÝu×­§ÛúŽyä‘õ#ƒç¾DevæhcM'm¹#…'9§o´öä£x4á «3ìðÇ»*Õ¶âŠ+>ñÄ!ÜP :F÷bÔ$èÔ]ÅÐ ÃLë‘Çh îŠK i³”O`¦Eœ6& €çÖùL3ÍTÏXp‚0ŠYá)Ü| ¡ZÔÜ]$œì6ÙùÏo òµ%Æàž{îɾ‡ß¤æc<øàƒúÚ¢lôéÓgÙe—¥·ñÇæ?6íùNN©:C—¢{$è0Ý–BÎ@N¦o 1†P,ñµªÒŸ (@Z3Ξvç*TôL˜zÇÓ‹!s«ð’¥…0ˬð®3ƒ€Õ¿@á„ ˜€ ˜ÀÄ Gy晇_bÎÈQ©Þ"sùÓŸþT›l쌑©ç'ò¥)ét_þ(àDv+µ娉NžÐh;¬= {®‚Ô6öC«8Èð@ëüiWaÒàÒó™u¿BÈÎl7Þxã× ªÔ-dŸ80†£,e\%oË-·Ô]Χ=ù䓉‰·?ä£ôíÛ÷å—_N;99Ó4M¤©ŸtNÒU¨#“!d"2L+ù¾."@Å¥CÖtËYh [Mƒ©§]Z¬Ó]•:É2«G¾•e¬þµòì{ì&`&ÐñBÔ[[¶¿ÔÁôÛ¼í¶Û¦Û}h€eì$ã( +ìM|/QºŽ?þxùý£$¸”":‘•WDöcP -£ân³Í6â5 r„˺ßDN‡‹72O?ýT6“„}«§Ÿzúç\Y=Â’a7o~tká?"$yç’žL£TʼnA¶ë‰®Á81ÈXäò„×:=ô5 <˜WKúšë“K´£Ñ£G×ìU‡ÐÍeì<é£~:©7Pt;&B§ø”ýEô¿ìUÁJ/­€Y³çrnÌÕ” )¬Sÿg,ô™Æ2‹œ($`õ¯‹3MÀLÀÚO 4@ýKµÃ»Iú^<Ýî+l sJiŒXUx -,›Ïd0"¹ë±&µÍË×™CÇdFWËLUó›t¡àè˜Lë~ub·Xç% ¯áÚk¯]ÏxŽG÷à«qÏ=÷Ô# ûxœ@Cyä‘Gª‹°Ç¥M{ÞÈTHRáe—]¦¨ñú¯#>ãÄ ¶‘Ú¨ä½ÒÉ'Ÿ,cïÇ{l¹å– aÜŦ¬NsÖŠ^Þ¢Z*§‰xÏ¥Öé¡£{ºK‡é6—ùX|*Å8Ã/lK™z™ÆÌ¾h¾ÓÁ¤05Õ¦™tz¨ScQ!ÏK+qº€Õ¿B,Î40˜(©ø›ßü&Ýîãš®Ìv_ac<¬pþDúU»æ}{¦¶ Õ~A {¥Ì8ìG÷è¤ø*ä¹ÅµÅ§§+)ÀÀÑ¥u¿jz¾Ûöß<¯?êŽ<ôîÝ»NyÄ=ôPš PxÍoåÓO?­»B·–™ j'ʼn]µü‰A^âÄ·÷¿ —G ¸Ù\yå•õO¬1wÙeÔ›š=Ìô¡ð’J¨Š S#OµEÓጔ.Ñ1åÓÕxë¤mñiÃaV‡FT7€FY‚±°c‘I·©‰Ìš &úë÷ý£×‹,°š5[ÀêŸ×€ ˜€ ˜À$!€Ø­[7=[ð)mçÀT¶—u¶ÚØvòn[çRød§.¬=Ip·‹[õôŠØÚ7à°ß-·ÜROd²Æ„Œ> (pÕYÅL ó  œ¾ ®·Þz;î¸c¿~ýP$8¯5lØ0T”Ì7‘8h#üïñꫯÖ9j^ÍÌ>ûì´rÉ%—Ô,Bhr$ëq[BU7Ýt“|Æì±Ç¨4là³?Vxb.þc!;cJŒÐªAøâ~øá:X³«y R<ýo6*§9¥i:@7èLƤ¤:ÅÇÃaPBÁ0ómerÂ}3·ò—L53)e&ù"L7=dê3(X,– †eÃâa ±dƒJ+iÄÂ|µÎ«^ &`&`“ŠÀ[o½¥C&¼l>|¸š‘›fõÿN·ã(`jíYæÛ3ã”"õ€¨8ìWQœÁj“áK Ú. *Êú– 45ÖXCÊOá'ZÊs̱Ì2Ël¼ñÆ»í¶ÛqÇ'J" àÛ#™  ŽIê×7ha¾†|ðAY…‘N5í´Ó"¿óÎ;£2E>Š–b b‚ˆ‰c:4ÔÔ¡\q².Nc8*»VIâTóøC=6´ˆÂ‰V¨Š uI4Ds4JÓ!C‚ŽÑ=*ù-²È"¼©yá…þç?ÿù÷¿ÿ=Çh·Új«ÕW_ýç?ÿ¹œ¸è»Vø‰'ÄPœPx¤Tüå/Á3Ê+¯¼2nܸn¸Å-â¾ûî«Ù8S§é:ZaÞ)¼ýÁË”ŒKãÄ »vmß-d¦gùP„¤ iDÜB ãâ…JÈä¢NñÑR(vJѺQØ=ºMç‘a §P&“ Ñ"¸€:‚QŠ:`Á äzæ‚)câè“ÈT2¡L+“Ë3ÑôªžS…êžB>„]}¦Ï¾Ì°ú—gâ00Ž'Q{еô˜'‰ú½§¨[™£€©µ'^¿ë<ÂW6¼-hûû©~†¦'<K?3Êúà|hnD¥ãù~ƒ 6¨s˜¨òærÇwTaÇ “BdP„0À8]…†Ø‹5. ÿ8>Ç·P·PHRo4ÿùÏ ½÷Þ{el‰®U&CA,6%ÆfÝý÷ߟVʼn!%›E¸áe»LEæœsÎjåÿI½Ñ\xá…¸Hyâ‰'¨M†©X†W¨+ìøépºMç-_z饨¯ÓÿŠÕŸS$ƒ‚j©\/ÍÑhF .éªLÙéX F¯½öšºJð†}÷ÝGšy] W.hPü…«˜haŠP0‚FP!Õ"À-¢¹@!?+tI~Vè$]E’në`^T›I Àžü¬(pp@¨šNJuDΧ=‰.•%˜\e¢ë×åXBt›åTV§óó¬þå™8ÇLÀL`H5ÀAƒñË­ç¶×êô§’Z{Î5×\òˆ9 8‘ˆÃ~TNz*â;M×S3‰ÍIÊ2Lë~õp³Lë8ãŒ3øjàï±þ!o·Ývik\oöµ(Å6×›o¾YO[˜£ðf䜑“7š8ä†K’µÖZ‹/uæ eþÐÜÂͰ뮻Ê"@ª]F¯ãÂyæ™Ç-ñ^ +ü©N2¹…€þÃŒ†T*9 “&h(ü¬dšˆR‘` „ᄟ†)?+q¦1Ϥš$¨µ)ZO‰´*&—Ž1Ñifuš%D–Sµ˜ï¦¬þ¥4œ60˜R ~¼eÀYÓà‡ÉXQJßã“íädŽNü2‡ýh‚†¢Q:Pó}¶ Õ4(hÝoâ'Å54*Ô¾VÕ'ÁÒQjœÿ+PŸ ã"¤’™4Ñ(HxƒL~Ùå1ǃ<'Ü21Ç å?ûì3Ì,±{Ô¶!çî¶Øb y£ÑTUýdž¼0ÂNÕ g*/¿ü2ñ¤ÑQ–zdÊÏ bS¤zï.¥ò³BÇâX ¦Ût¾pPi&t*,i~EZ‘$À^!“¿…Å„tc&:·0‡Å#È,§Bg°úWˆÅ™&`&`“–@h€<—ÈÏ‚¼q¯pÃèx)Þ»wït#Žçí¶QCuTÀš£ŠÈ~T˜Ú£ÒꉊnÄÎ@¾B†@7ô†ž¡1@J¡2ä¼°sL e ,»ì²|5\?UVY…"guVýEd3Ÿƒj|%ËâdjãáÑihå•W®ðé’)5|øp9;Áûå»ï¾‹/M^ᕤk×®TÕ¾?úÌ_ûÊRЦéÝ 3t‰Ž‘I'éj¦óe— ”HþÈba) Óg€§ž™ %3™L+ 1Å™üŠKEXH2¾•'`õ/ÏÄ9&`&`“ƒ@h€zfÒ f,òn`êQ½jF¬9$”½š‘ý*TPÕOçe¬¥áhhÖýj·@ @-áÙÈõ]æâœ(«ÓPwó‰wÞyGV߸»Ìß-Ì!h¦ñ2‹¡Å_êZ† „(`7'+²DY"äz›Ô¼´•PRmq!m”ž¨~ºG'éjáò™ Ÿ² Hþn>‡y!Â;Ež¿[‘CA¦•‚Lq…Xæ–âj°2ù¾¬&`õ¯šïš€ ˜€ LB¡êIeá…æç?u“1¼äg¾Úð’7i_h\mz÷‡ý(^± ‹Š.¡@ÊÝ‹¢AY÷›„ ÈUwf¼+áûÎ7®úK‘Óh~£T[•ñÝTèÜŸ¤V¤㎶®¿þúB1".h‹g•zуpüaû=óÌ3§ö™u*xd;‹È„èi§ýG‚K2eOM”%Ò†èÝȤÃt[ƒ™Ð1XN¸­3"ÁKP§¦QaEBG4™Ü8pX!¬[,ýoŸcX³l‹ Xýkñàᛀ ˜À&  ¼—©$†—¼ ®¹ÕVØu¤ƒñdÀ)¾B™Lfæ°_ænáe~C’Ë•!0=™Y÷+¤çLø&Z@ý@Ð…ør­·Þzõ‘d;4Åà’p7ʾ¶Ùf¾æØxëk®O-þÒœ|:ïgåÜsÏŵ¦$QÛˆœ®èð…£ã¡ÝQâ ¹^¿7š|?ÃaP -6²”«ú#7´CÇŽa2¡@`r#§f‚eC–PMI dXýËñ¥ ˜€ ˜Àä&j€¿øÅ/ôÌ¡ 4~Ý«Úö•§ú–ö+¬9“™*¨ê0gz˜³î—ÁåKÈ8úè£ù²ì¼óΙüŠË÷Þ{O¦’Ï<óL…XþVýv‰8Dyå•WägEßkl&‰þRS»Ó_ŸRÒ±€“•»ï¾?+Ÿ~úiÚ+bìµ×^²`wî'?ù i⨣Ž*Ü#“[êÂÚУ8•dÂ$ÐÍÑ(M+ÆChŒiËÒ4Á`eÅÊðå 5ýÄ´ÕÂ6h0•ôier#³f‚eÃXB5%-!`õ/Ä—&`&`S€ öýø9Wô*ø»«iíYÖWõp¿©×óœè+´D"³æa¿²ú#_¶ á£¯W¯^z¨b8 *Äœ0È D8ß ët+¢ôÜ¿çž{æ+¬Î ¯$èD®6lØ7Þ8pàÀ~ýúí¸ãŽèO_á²OþWá-/w6Ùd“]vÙ…7MñEf›m¶}öÙ‡†pÔÙ½{wr–^zéÑ£G§#ßG!—•ÔÆ†1Üq²røá‡ë¿,¼­ n¥E¸” C˜"”S ±§ÝNÓL¢<ÖdP¤2ù4 †.1ºü-çÔ$`õ¯&" ˜€ ˜€ L&˜êÖ­?ê|þò—¿$‘ºi_' ¶ã°_Yël!ê¨!–S :_O¸°² o-E@ƒ{ï½w›F½ÆkðÿÉ'žøôÓOßqÇ—]vÇÆ¨„ à+®¸"ÑíbC±Â?Âôñ…åÛŠÖ”QrpÛK¸N*:t¨Ü¢œwÞyT‚=ä%—\²ÓN;É@ì$‰ˆÀ©Â²P„¯½öšö9¢Æ!:©vÄÆãÒÂ!£,)*bÐãd–éQTF4AC:TIÓt °:L·é¼¬I©ŸA14(³O†LA†Pк¼Ý—¯>€:‚Q1ãV>Át0)L Ä41YLÇôa~<ZØÏ²L*¡T[ý‹–ÕÖjùS3àü$9ÇLÀLÀ¦Ü‹c?ùÔSOqä_( Š0Oâåwæù¬M}ãA¿×]w¯ð=öXª:òÈ#å©…gÔUz›ªE˜ò4C͸,§ÚQ£FaCÅëùÌsR[«µ¼ ´‡~x¹å–kñz¤HࡇRôȬ³ª²ú× ³ì1š€ ˜@g" †=˜PŽ3æøãç¸ËÄŒ=wÌh}z鉪†mRß¾}'F«¤?ýû÷çÈ –ŸX{âkÁºßÄ̑˶&½åYpÁ[søu» °s¨÷ní®¡e ZýkÙ©÷ÀMÀL q „ˆY!PÒnºé&3LLQϰ/â •P-vG:–Óî:± Ýpà yx{ֺ_»Iº  ˜€ ˜Àd#`õo²¡vC&`&`m  Ü¨p.#1y‰hC-ߊâí`«­¶ÂÇÀÏ~ö3òˆmE…W^y¥‚M}+Õ†ñƒÑª{ÖýÚÀ΢&`&`SŽÀ+§\7ܲ ˜€ ˜€ L@€ãs¢C­Âþ¿(Zx'çßBu\Èì¿,ÔÀ1ÂÇ¿þ#Á%™X„Ê´ŽšÆ‹Ð |åQ£{ÖýÆ£qÊLÀL ± XýkìùqïLÀL … „ˆwMÇ9.mÒÖÐÓz÷î­sƒ Ä… Ž^ø#Á%h¹…@›´J:@7pNC—è˜u¿^¡º ˜€ t>6þì|s曀 ˜@K+Püv⮳~70öcÃ= ;Ï+®¸"tÃ{Ä¿bîú믯ó( Ü½¨3ÖýZj)z°&`&Ьþ5Á$z&`&ÐäBdœ¸¹ùæ›óº\AöCCÖnÉLÀL`ò¨_´î7yfÄ­˜€ ˜€ 4« 2î† ˜€ ˜@G¨G´îבÄ]— ˜€ ˜@g `õ¯3Ì’ûh&`&ÐvÕ u¿¶u 00NOÀê_§ŸBÀLÀL Œ@™hݯŒ˜óMÀLÀš›ÀÔ_~ùesУ30hqe½zõzê©§Zh¡!C† "¶8ßLÀL uXýk¹öHMÀL u „8ÿüóCá™gž±î׺«Á#70&`õ¯…'ßC70V" ƒ¶î×J3ﱚ€ ˜€ Œ'`õo< §LÀLÀš›ÀÛo¿}íµ×2ÆÞ½{Ûæ³¹çÚ£30($`õ¯‹3MÀLÀLÀLÀLÀL ÙØóg³Í¨Çc&`&`&`&`&`…¬þbq¦ ˜€ ˜€ ˜€ ˜€ ˜€ 4«Í6£ ˜€ ˜€ ˜€ ˜€ ˜€ °úWˆÅ™&`&`&`&`&`&Ðl¬þ5ÛŒz<&`&`&`&`&`&PHÀê_!gš€ ˜€ ˜€ ˜€ ˜€ ˜@³°ú×l3êñ˜€ ˜€ ˜€ ˜€ ˜€ ˜@!«…Xœi&`&`&`&`&`ÍFÀê_³Í¨Çc&`&`&`&`&`…¬þbq¦ ˜€ ˜€ ˜€ ˜€ ˜€ 4«Í6£ ˜€ ˜€ ˜€ ˜€ ˜€ °úWˆÅ™&`&`&`&`&`&Ðl¬þ5ÛŒz<&`&`&`&`&`&PHÀê_!gš€ ˜€ ˜€ ˜€ ˜€ ˜@³°ú×l3êñ˜€ ˜€ ˜€ ˜€ ˜€ ˜@!«…Xœi&`&`&`&`&`ÍFÀê_³Í¨Çc&`&`&`&`&`…¬þbq¦ ˜€ ˜€ ˜€ ˜€ ˜€ 4«Í6£ ˜€ ˜€ ˜€ ˜€ ˜€ °úWˆÅ™&`&`&`&`&`&Ðl¬þ5ÛŒz<&`&`&`&`&`&PHÀê_!gš€ ˜€ ˜€ ˜€ ˜€ ˜@³°ú×l3êñ˜€ ˜€ ˜€ ˜€ ˜€ ˜@!«…Xœi&`&`&`&`&`ÍFÀê_³Í¨Çc&`&`&`&`&`…¬þbq¦ ˜€ ˜€ ˜€ ˜€ ˜€ 4«Í6£ ˜€ ˜€ ˜€ ˜€ ˜€ °úWˆÅ™&`&`&`&`&`&Ðl¬þ5ÛŒz<&`&`&`&`&`&PHÀê_!gš€ ˜€ ˜€ ˜€ ˜€ ˜@³°ú×l3êñ˜€ ˜€ ˜€ ˜€ ˜€ ˜@!«…Xœi&`&`&`&`&`ÍFÀê_³Í¨Çc&`&`&`&`&`…¬þbq¦ ˜€ ˜€ ˜€ ˜€ ˜€ 4«Í6£ ˜€ ˜€ ˜€ ˜€ ˜€ °úWˆÅ™&`&`&`&`&`&Ðl¬þ5ÛŒz<&`&`&`&`&`&PH`šÂ\gš€ ˜€ ˜@óøïÿ;tèPƵúê«ç;ßi¾zD&`&`&PMÀ»Õ||×LÀL yœqÆk~ýG¢yF员€ ˜€ ˜@ݦþòË/ë¶  ˜€ ˜€ tV£Gžo¾ùød]»v}î¹çøì¬ƒq¿MÀLÀL ]¼û×.l.d&`&ÐÙvØaè~ëýG‚ËÎ6÷×LÀLÀ&–€wÿ&– Ë›€ ˜€ 4>§žzj±Å£ŸO<ñŸ‘^h¡…¿óî¡ ˜€ ˜€ tïþuI×c&`&иöÛo?ü¾ì½÷Þè{ü‘à’ÌÆí±{f&`&`“€€wÿ&TWi&`&ÐH®½öÚÞ½{§çýâà Aƒ6Ûl³Fê¬ûb&`&`“€wÿ&!\Wm&`&0Å |þùç:æwâ‰'†¯\Ò7n!0Å;阀 ˜€ ˜Àä!`õoòpv+&`&`S†À©§žúüóÏ÷ìÙsûí·O{À%™ÜB ÍwÚLÀLÀš˜€?›xr=40huo½õÁ>úè£Ûo¿}­µÖÊàøûßÿ¾öÚkwéÒ… ³Í6[æ®/MÀLÀL ùx÷¯ùæÔ#20ø†ÀA„î·é¦›æu?$È䈙 ˜€ ˜€ ´ïþµÂ,{Œ&`&ЊþùÏ.³Ì2ÓL3 QzôèQˆãO~ñÅ<òÈ’K.Y(ãL00hÞýkš©ô@LÀLÀ& °Ï>ûüïÿcg¯L÷Cš[ †ð…}a&`&`ÍHÀ»Í8«“ ˜€ ´<Ë.»ì·¿ý-'úž}öÙf˜¡‚ǘ1c~þóŸsJðÒK/ÝvÛm+$}ËLÀLÀ:;ïþuötÿMÀLÀ²ÆwÄG‹WÏjÝú÷ïOâÀüàƒ²uùÚLÀLÀšˆ€Õ¿&šLÅLÀLàkÇü«¯¾ÊY¾­·Þº$ÇGìí·ßÞ`ƒ ê‘·Œ ˜€ ˜€ tRVÿ:éĹÛ&`&`Å^{íµÓO?{[n¹%>]Š…’Ü^xaàÀʸÿþû¥ &÷400æ!`õ¯yæÒ#10€À¾ûî‹ñ' |ºœ|òÉ5™ì¹çžŸ}ö®_ºuë†ð†nX³ˆLÀLÀL “°ë—N:qî¶ ˜€ ˜@¶ïVZi¥¸1ÝtÓ9rÞyçœLââ‹/þÝï~÷£ýˆÈïŠXgu4hÐf›m–‘ô¥ ˜€ ˜€ 4«M0‰‚ ˜€ ˜ÀW¾üòËe—]vذa¤9Å×µkWü²›wã7;vì ,ð¯ýëòË/×)ÁVXáÁÄ >`¦žzêÂRÎ400ÎKÀÆŸwîÜs00 œþùÒýÈ=æ˜cN9å”gœñ¦›n<¹üÎw¾sî¹çþßÿýßïÿûQ£F)öï¼óΙgžáÈT‚?i·Þzë³Ï>›¹ëK00èÔlüÙ©§Ï70øŠŽ^þú׿’˜i¦™^|ñE}úüä'?¹îºëVYeBD<úè£ÌKFÎüóÏñ'Û€o½õF¤‘ï„ ˜€ ˜€ t^Þýë¼sçž›€ ˜€ Luæ™g¢ûI=#]¨ûiÛm·]yå•ñ ³é¦›¢1rä¯Z÷£ÈyçÇ'þ?û÷ïoÐ&`&`&мû×óèQ˜€ ˜@+xÿý÷ç›o>44OÀwB8TP>|8*ßÿþ÷?ξüòËÄv¯Ö­µÖZëÿø*%z#¥jÊ[ÀLÀLÀœ€wÿ|‚Ü=00RgG÷Ó©?Ö+jªyæ™gÚi§EàÇ?þqý8õÔSþùç»í¶[E;e&`&`…€Õ¿Î2Sî§ ˜€ ˜Àx¨d—\r‰b»³¡G¬vl;Çß.Jíµ×^cÇŽe+ï¹çž“ag‘Ôyl-î°Ãd]}õÕ4G£Üö… ˜€ ˜€ t66þìl3æþš€ ˜€ L5Õ 'œ±ÝqÔùÔSOË¡Ìc=¶ôÒK³OxÊ)§ì¿ÿþ]»v}æ™gج(¢[ï¾ûîl³Í††É%æãÖ¬Á&`&`&Ð8¼û×8sម€ ˜€ ÔE`ܸq§žzjˆ²õW­ûáçëMT¸#IÁÈÌ$¨PwÿøÇ?n³Í6ݺuC€¦)›‘ô¥ ˜€ ˜€ tvýÒYfÊý40øÊürÖYgEñÃó³Ï>»æškz÷îÍq>ô´ÕV[mèСF?üðòË/ov3çÿö·¿­¿þúD€xúé§Q#3¹\}õÕïºë®}÷ÝW›·ÝvÛºë®K>»Žo¿ýö~ô£|瘀 ˜€ ˜@ƒðî_ƒO»g&`&0ž\Ðý¸F÷[tÑE7Ûl3ÒýúõC'DUÃ5ËxÑ©¦Â× òy{ì±Ý±õÖ[â}ô ^ZJiª¢Bª¥r嬳Î:D'M0OÌ9&`&`‚€wÿ:Å4¹“&`&`SE ößÐÁ؎ǧ¸ 4ÛÎf˜aÔ¨Q³Ï>»2O:é$ÎøíÌ馛.Oð_ÿúׂ .øÉ'ŸÜ~ûíRí$óÆo,°ÀcÆŒÑîb|üñÇ—Xb . ñÖ[oÕ7"Ê:a&`&`@À»0 îƒ ˜€ ˜@mØy²¡‡ê…îÇY¾Ðý(É­>}ú °Å¾Ü;ï¼sòÉ'sëŒ3Î(Ôý¸5÷ÜssÌ1$öØc¶IèJ¨Š ©öÛ¼¯þ]|ñÅwÜqGt#s+sÚLÀLÀ–€wÿvjÜ100ñ^zé%œµpúÇ'91þ{ª©^ýu¶ì0æÄKçN;íôë_ÿzðàÁ[l±ÅŸÿüçT,“F“dC¨ñG}ôQGÅ]½ì¼óÎl$r&Xy6 »wï®8/¾ø"錀/MÀLÀL ‘ XýkäÙqßLÀLÀ¾!€²7|øp™}î¹çžgžyf ¶šè{³Í6ÛÅ_ÌÑ>ÜÃŒ9RJc^8rî¾ûnÜÆL;í´(„‘@‡Ä°óꫯN=…†0‰ãŽ;NYd|‡¦·œ600'`õ¯Á'ÈÝ30˜Š í8ð,9Ÿ{î¹B_¬ºêª÷ÜsÏL3Íôþûï0@qÞküÝï~‡Æˆ#PTÇ?ýéO«¬² :aY©?ü]Á±cÇ"@Ç–]vÙ2I盀 ˜€ ˜@£°ú×h3âþ˜€ ˜€ d pHïµ×^cƒîÓO?%†;]²ß^£âÍãLœv¾úê«l~{§ê_9Ì?ÿüD•@ˆ"l3Î7ß|.ºè¢vØ¹æš sÐ Iß200†"`×/ 5îŒ ˜€ ˜@–^=ÑýÈE÷›e–Y°üÌJ$×ÓO?=QþȘzê©qÐ’Ü©J¢+Ê B8Œ©ÖýÙvÛmå_”Ža ª£€U øž ˜€ ˜€ 4«1î… ˜€ ˜@¢öህ;lýñyê©§¨½Hð›¼8žr~¯ZQÌTBPå°g˜¹•¿äâ…^¨|ÎF:/é00h(6þl¨épgLÀLÀ& €rÅöš²ÝŽwmîM ôíÅ 7ܰÉ&›p8ðÚk¯Ýh£P9›·ôÒK{¿ôßaÆ-·Ürܦõ£ öìÙ³Túë÷ßÿJ+­$b>Ê~cuß500)NÀ»S| Ü00bü1'ýâf™º!=ôP„O9圸 4bü¹ýöÛñÅQCaÄ>þøãwß}wêá³P22ñû²ÝvÛÅ%ñè½4œ00hdVÿyvÜ70hi„_OÕ‹¯‡÷FÅá.»ì‚Ø~ûí÷³Ÿý '.Ò + "€ÂAäà]wÝuÉ%—TÁÇ Øl”ØÁ\!ï[&`&`&Ð lüÙ án˜€ ˜€ L@€ ê=zôÀ3rñÿ‰ñgáÙ?\°àº“M¹¿ÿýïk®¹¦Š :´W¯^ßûÞ÷ˆÎÇݨ'M<óÌ3Düüóχ ²úê«sëòË/dz  P&ô£¥ÂJSí/~ñ ö!~øa M‰¨ü³Ï>»æ¶a¾6瘀 ˜€ ˜Àä$àÝ¿ÉIÛm™€ ˜€ ÔK`Ë-· ݸ|„×#ÄÂÞ{ï]X~Ÿ}öA÷#Cè~ˆ¡ÎtÐA¨vØvFUiq2¹…bÒý¸»Í6Û<Ï1…á%°GUmGyäâ‹/ŽVf9ä´r§MÀLÀL  x÷¯'Å]20V'pçw®±Æ¢€›Möè8’·Øb‹ûøì}úôIbóÍ7'äbdOo¢0€o¼ñV‡~xz‹tÿþý8âB8<ýôÓ3Î8cÜeÕŽÓ€ìï-µÔR‘OËÒóÏ?ŸÌ|PqúB£2R­ŽI˜Öã´ ˜€ ˜€ LÞý›"ØÝ¨ ˜€ ˜@)T)vØâ6»jÝ»wçPz™\Ž=:î²w×·o_. ÑýÈD©;çœsHœtÒI ¹$“KRÝœ…Zˆ¨>ä3ŠÜ~ûíô¸ðœ '4œ ߤÇ{lzX1 :a&`&` BÀê_ƒL„»a&`&ð ѽòÊ+º@1Ûÿý•æHÞj«­†Yæ®»î°Ø¾{þùçQØR1î’À;ËŽ;îˆÑ¦\ÂÄ-.ÉäV¸o‰[$°íä¨á#<2pà@峑ˆq)it<šK…1=eï‘6'÷Ýwßô–Ó&`&`&ÐPlüÙPÓáΘ€ ˜@«øàƒлPÌ‚­9¶à já /Ì]ùYyùå—±íDéÂW'öB,“@cÄA 5‡wªÝc=ð삼¼däuyýõ×ÿêW¿"‚<6¥³Ì2 ‘Øô[~ùåï½÷Þï|ç;™"ÜR(ˆÿû¿ÿ{ï½÷ }ÆdŠøÒLÀLÀ&?«“Ÿ¹[40(%€óÌsÏ=W·ÑÙÙÃug*}Áì´ÓNhe#GŽ$qóÍ7£ÊÂ3ˤÿô§?±=H|vô=n¡ ¬ï¢‹.©LF2½$~àßþö7v9[¸á†â瓈𘡦2Jc#Šé)z&—½{÷¾æškò2Î100)NÀêߟwÀLÀLàèfØUÆq»n¸a£6ÊÓÁ9çW\'˜'žx‚`€Ï>ûlÙ^Zv³Í6ûË_þ²õÖ[“IñM7ÝôÚk¯Mòé^xÍFv»víÊÃ?üáØyæÅ”£ÝB¥ñ%!Êäo&`&`“Ÿ€Õ¿ÉÏÜ-š€ ˜€ XwÝuo»í6ÝÃ÷æc=V(‡'Ol>9ŒÇ]¶õduY(™f¾ôÒKè–ãÆ#“}<Ü{âQ&(Lã2”s€ÜZe•U01zê© Å”I ø#F^yå•ï¹çž Iß200)BÀ®_¦v7j&`&%ÀîYè~Üc«-+ñí5¡VXa®8hñú¾½Yú/ÊÞi§¦Û¸ ­G÷C˜Ðó*B‹Õºb¦J˜ó Giš€ ˜€ ˜@ã°ú×8sម€ ˜@ë ¬ßÁãÇç N>ã2“à@ ÉÄL´úð^¦àý÷߯œHd2—ï¼óNšçxa&tDF˜Ëå–[n¥•VRþ~ûíÇ ò2Î100)HÀêß„ï¦MÀLÀ¾!pâ‰'>÷Üsº`“øéhpÚI¸?"7soèСG}t…pÜz஼òJœvòÇÙ?.ãVY‚h€k®¹&Þ\p7ª`™<ù—^z©îâ•”AUHú– ˜€ ˜€ L~>û7ù™»E00  báäó³Ï>SîÎ;ï|ÞyçM ‘\ȇ'ž?ñøÂ&ÞÆo<í´Ó>ùä“óÍ7_"•M¢.r˜,§žz*÷:è NâÆ3ãV4-†Šˆ\Ë >W.h€8å€b*–OÏ9眯¿þ:ùTΆ!A#ò2Î100)BÀ»S»50O³ÏÐýÈíÙ³çø{¦>ùä“~ýú‘wÖYgÅ¿ ØXâ™“Ø á/tÂß\¡ï¡ûÍ?ÿüìàñG‚K2 …ÉÄ» b$8‚8Ï<ó ÑíË=÷Ü“æÊJ‘ñÅK÷#ΙZ´V”ò-00˜<¼û7y8»00blÁ-±Äé=b¦Ñ¥+ÍTZQ—^zé‡~XŽXÆŒƒ?O6Ù<ð@íìåKáä“­¿ÿþ÷¿xãÔÙ¼ûî»OžÓL3 ÎE)ž/òË_þò–[n!ûÅ_,¹ä’ìâTª`¾Ô«¯¾J8 âË/»ì²ôP4Aëya瘀 ˜€ ˜Àä'àÝ¿ÉÏÜ-š€ ˜€ Œ'°ï¾ûÆÅ~ðƒ 6Øõ‰è|hk‘¯ûuüãÑúN81Îdà 3Î8Ý,S„Kv øŽòvØa‡…_\âš…[ùmCÂÁ£õa_JsQ!º"Þ_h÷”SNÁ÷LäG‚Óm:=*zfô0`;a&`&`S†À—þ300)D€óuéßé§ŸþÞ{ïaiI&f“™N)ÆC>±#Ž8‚"‹.º(]¦ÔQGÅ­¹æšë£>JoqI&·HóÙÁC¥$Ÿ¾¥ùJ£.rkuÖÉß’'gÜåÜ ’ú+¬*_ƒsLÀLÀL`R°ñç·?Îþ×LÀL`òàþT^yå5‹Í'Š¡ü† ‚³Mvϳûu8ƒÁ'çýpŠd¦§cÇŽE÷{á…vÛm7öèâ.î7±íä®,9#_‰¿þõ¯l6²åˆuè¼óΫ̵×^ûïÿ;‘'®»îºŒ<—x©áÜàûï¿õÕWo¾ùæ!€5)‘*øÍþÇ?þÑ«W/å3rHwëÖmÔ¨Q¸¨ y'LÀLÀL`аñçÁîFMÀLÀ¦".Bè~à8餓ЗH >sÌ1²¥üðÃÉa§NÑÎ=÷ܼî‡*ÐÑyärô‡Qt?tBNñ}›7þ_2¹…bÊ¥~t?”LìKÇË%)Üxž|òÉdpÀ8Õ:)kUºº·ba:DBÑI00)G`Ro/º~00ÈÀYËøÃøõÃf2•Á†ï)Üe‡üvØôŠ+®˜ÊäÓR±Ø7nwqÚI©™gž™-»¼°r¸…b¿øâ‹ÓO?=éAƒ•É“^ª¾átTbÚ$3oz*…–:,C®¨Ö·LÀLÀL`2˜j2´á&LÀLÀL C`‹-¶@)Š?²g°äìÒ¥ øhù:Tûwpš‘É\=BA#úôéóÖ[oá»…â—_~yF,s‰bl*®°Â $¶ÜrËŒ@þòÑGE¯Ã ÎfAA)ºJ‡ó’Ûm·wõÇóÎ100ÉIÀgÿ¾ýYö¿&`&`“‹Àƒ>ÈV­ñƒÇg÷îÝÙyË7ŽbF@?-œsâÜå¸ãŽËËdrˆ²À.~>ñ3tèÐßüæ7W]uUF&‰Ê÷ç?ÿ™üŸüä'œìÚµk^&“³Ï>ûü¿ÿ÷ÿA1räHÌG/»ì²0õÌH¢»Ê¹(¶©Ä©_~ùå3¾400ÉFÀgÿ&j7d&`&ðT>t'½é‘¢A¡bG݉}¶:Ã'Bð÷¿ÿ=µ¡ûq Pçô +O3wÙe]¢.Ö£û!Lè¿Yguذaè~;í´S™î‡$[‘ª<®Kš€ ˜€ ˜Àä'`õoò3w‹&`&ÐÒˆªGHôï~÷»¢Ð£GÜl!†Þ³Ï>Ë-vóúõëW(“ÏDSålÆŽÏA½ìÛ·¯2¯¹æâÈg /gœqF"¼s‹ÍI…(#ó‚ .ˆ€ œá—I:ßLÀLÀ&5«“š°ë70Oo™Räð’¢Ü²­?îîµ×^„‚`CÝ?ùä_Qy gžTŽÆÅ G Ë¿¹sê©§>ðÀøžÁ’óõ×_/ ÕqJŒZA{ìß¿…0úa"‚á‡ËЊR¾e&`&`“‚€ÏþM ª®ÓLÀL ˜±ÑQ·p°)h¾ùæÓþ^^÷*lß¡;áñåöÛoÇÍ&ópµBT†¼päÜ}÷ÝÄßc÷-ëÈ#œ}öÙ9›W+BE8é·ÔRKá3æÖ[oe« =TGâ ®ºêªQg>ñî»ï.²È"o¾ù桇ŠúŠ/PN3.³Ì2yIå|òÉ'ò)ªË_þò—„ˆ@­]Á²‚Î700& Óþ400IMÿ.ßÿþ÷ÓŸ±Ûn»­°Q¶Ô¤MqÄô+”: ¥½P^™Ä‘GŸDŒ(Ô ñž<ÑôÐýçìŸjP¬*¡ªŠ†è¥è“á(õ®Y„RñGÐù yß200IAÀ&U×i&`&%€v´Ê*« üÌ6ÛlRºuë–úöú´ÓNCƒL6 •÷¯ýKNYˆÿ­Tö_Ì>)µÐB }þùçÜ#Þ_È9ÿüó³¢__³ÇÝyç—°ò  ÅɤªÂ"dÒè ]â’=0ÄhYò úG©ôoà 7¬÷-00˜¬þM ª®ÓLÀL KSÌTù!}ÓM7e…¾¾Æ´Rš^&üúu×]G)öŸ|òÉ|AB>bsʇz(î*œÝ ™J (òC† IoQœLªB ÍWš¦µIgâî 7Ü@Çpƒ9hdæ‹.ºhJ»ÖW_}5/æ00˜tìú%ý-vÚLÀL`’ÀSñ¢ö9æ˜C&”‘‰Ýwß}ôèÑ¿þõ¯7Ûl³È$ƒPNrNð ãÆKo±µ¸ýöÛóÉ‘?N Æ-"­¯µÖZø!6Cd’`—x â]†é-ŠSIT˜Þ¢Qš¦t#õVºÑF1–?üpÿý÷OåÓôÛo¿ýÆo¤908p`šã´ ˜€ ˜€ Lr“N³tÍ&`&`& ù­?T¬B88ádóí{ßû.aòÙ[pÁùiDyKïêÖ¤˜b¦ù¤±ºd_Ž"§Ÿ~zÜ’<1'òòÈIUA,Š Q2éÝHóIs¬qºé¦ãnf/Qb®èÕ«weŒJ‚òÙ¥KÙ©fjó¥ ˜€ ˜€ L"6þœD`]­ ˜€ ˜À7Þÿýi§mg¦™fâS8ðD1Ë0bÏmñÅG â€ßã?.óË믿^ÅŸþyÕƒÐL…º¼ôÒK©s†fÐi=‚ïaÛ‰íå}÷ÝW(O&UQ„j©\24GMÓÂR ÿгgϼFÇn!eñ\zÅWHÿ.¼ðÂÂÚœi&`&`“‚€Õ¿IAÕuš€ ˜€ Œ'°ë®»¦ é…^˜Ïå—_>£)p ä³ó–ß^_Ý—_âÊ1Nôýûßÿ&_Ö›{ï½w*“Iÿîw¿£Èšk®‰§ö:è ŒLæ’ )BåäÓÍqYæEŒBþóŸ#Ã(Òª8â¨Ã„Ä“ O3Èð'%#ØTØi00˜¤¬þMR¼®ÜLÀZÀ¨Q£ØgCÛÑ ܺp NÞ2÷ÜsÏ„wYiâI%2Ëë­·UaQyÊ)§˜uÖY9.X&L>Gï0PÑ ÐÑ+ä¹E…TKå4!ÓM­.B,xäøÃ¾òÊ+’Ä(TQÃ/è5×\ƒLúçÕT}×LÀL  Xýë@˜®ÊLÀL K@zZªíàï¡GyDçßþøÇ?ªÌ&›l‚¾U²U]£Î)€„öЮ¾úê"© òþò—¿¨¨£ØNp¯ä‚j)¢&hŽFKÇgf"8ƒ!‹àK.¹$—o¼qH`àÊ‘?õ×£$*`D)'LÀLÀL CLM-úò§ ˜€ ˜€ t,öñ¤Ôaý¨Ÿ4”(íbHI¼u¼¡k󫮺*ù#GŽìÞ½{=ÝàxÞ:묃$&h5‹`PŠ¥%þ9çšk.".Ð¥šEXýõÿö·¿‘ BýÚk¯]³È믿¾À Hðæ›ofø(·?ûÙÏ}ôQmlª8Áåu0jCÏÜ|óÍãÒ 00˜Døauµ&`&Ðê«pÈ!‡ˆB¼jÄágXî¼óÎDWG+#ˆi$95W§î‡0~\T9#ê–ÒŸtÝoši¦ÁåÌGQ!·¨–ÊuÍÅÝÂæñÇÏ-Ü„¢û1XvSÝ[;öýHË2–s†à*¬Ð™&`&`&Ьþu LWe&`&0žžyæ™t“ ](ãfÀ€‹-¶Þ59"È.ÿÆ—¯L}ðÁ‡~8"xRá Ý{ìQ)þU<†³Ï>¥‹3xHžvÚi¡×U¤Z*—³š£Ñ á¸E)|ÀlœsÎ9‡Æ-%¦Ÿ~ú}÷ÝWi¢ÿ‘àÜ#¸2b¾400'`õ¯Ã‘ºB00©Þ}÷ÝO<±ïGú€@óIé+ïÜsÏUŽ@uÊ.(Kï°Ã¨L«¬²Ê­·ÞŠùèe—]vÁ” ³‰GPxzÂÞ#ûlâqtÛm·å^Yò©j©œ&hˆæh´B>n6?.ÙÙ[yå•#?M þ¥Š1·Ž;î8 ¥2N›€ ˜€ ˜@ÇàçÐ&`&`&бP·øÅÂfün±óÆ¿|+[l±…dˆÅW3=_äÿøEÐ^Šaýp³™&g§vB~饗ÆÀ’Ë?þXzûì³O¡<™TE…”¢r.iHª)M—‰|lY)¨@k­µVäg蟈¥ÒQ3b¾400$`ÏŸÓU™€ ˜€ |E€Àèl|¡Î¥º Ûny:ƒF†¶<Ê//9œÔù@ì*#ó·¿ý-ÅÙ?”‚ù$Ø»ãÊÛSO=ù„àcóâ”%2#A%TE)ªLš#‡¦«c²‰‡Á† & 𪫮ŠJÒÄOÉ'v_šOúØc%¿gÏž„kÏÜ’[N*L·+iN§øè@F>.ï¾ûn\ËP­B^xá…¤q3f̘Iì "þ€©ŒÓ&`&`&Ьþu LWe&`&ðeƹ‹t,<óhúöíË]Â$HßC?” ‡­f^X9D Ô¾b~×Õ‹[ü :4ŠkWp…V@½ŒL%û.å?œé-Š«ž{î¹'Í'M£è±Ü¥™[\¾ù曳Ï>;#:úè£u·.ÚE,³2ÕÎdªþ‘`¾r瘀 ˜€ ˜@‡°ú×!]‰ ˜€ ˜ÀWt®¸èH¡Õ`ÓH4¼ ÓmÉÇ{Œ&øÃÔ3­'ÒÿŸ½óŽú«ªò÷oþ™åZc¯h€ÁBéH‹¡÷Þ!(EŠ@h&º€ô&-¤„-€„*éM@ªta—+¿'yÂæäÜû½oP&$y÷ýã›sÏÝgŸs>çf­ûyw[xá…U^Œ!D HDà#D éßGfªJD ¿#0lØ0™LÔµãvíµ×nâb9øÊò†˜!väŒ)Cõ>tèP´QÃ]S!=ð.|G‘!ù Y4g›m6Ú'tR«°Tž@fžyæÑåÒ¬-())\9œ©YCXLÙ}N¬ØË~ÚÖx@'ÆÀê·F?2¶¼€±)™=‰@"$‰À?Ž@Ò¿ÃÔ$‰@"0 ²¶Èúæ›o¾’Ì\zé¥@—\r ŸúÔ§šVA$wÚi'žâ™Y†ÌQPÃ`wvЗ^zÉëßùÎwP2pàÀVÒëÁˆgDß[laå=†£$š €fÃ’|J¤=Dýá€Ú”§Ö¼N¡To>ec« ûLÓT•=‰@"$‰@Ÿ$ýë¢HD ˜&6ß|siLYï¬p¯¼òJ¨À³QÚ¨Q£¢³lZS'Ï6ÚÈ~(ܲË.‹**–’­í‹/¾Øeà\Ú«D9ðñÇ—µJ/^>mm³ ¦`I,ìùçŸ'Ï'·$°i¦Ó%}ñ‹_¤|),š(]pù ˜¥d¶D H¤ Œ©$HþŽa{²bíJ³ÄKp»è¢‹F½k<,¸à‚½,òÙgŸÕˆGn8â”`C#“gŸ@SŸÝøC¢ø¦1ˆîä“OvÍMgÔÖéX†½C9D3#^£­’ѹÒJ+1E™5@€…NL”.Ø5Ä/Æðl$‰@"$ Iÿ>SI"$ýŒ`K-µ”¼Å„.¶ñ«$™Ê¼óÎËí¦›n FØÊ¬ŸÞÌ«Y!xå•W2 ʱcÇ~ò“Ÿ¤M˜\%ÓzküžËØqÇ[eªNëÂ3. Õ£^·ÆìYæ âáÙKÒ~WI3sÏ=÷Ø ÌÈX :à€hWv{®vϘOD H&Iÿš˜dO"$‰À‡Càç?ÿ9Ô¥Ìö)“¡°Šžzê©OúÓôxàk®¹&ýèGÓ2¥ùôÉÜn»í¦eȘ1cÐåpüøñºt^wÝuÝ`DN^zöÙgwËÇSêI0]÷Êê’6Fމü"‹,‚Ù(h àðôÅ_„Ò—·[)ÉÛD HDàA éß?‚^ŽMD ˜ø§?ý‰Êæò­a¶I§è@±‚ÂÍðÏŒG h’¥ù(œðꫯvHúˆt)ŸûÜç˜] wÊ)§Ðþò—¿ÜÌÆªx„bŸwÞyÊ“54z5yäé"CN?ýô^be?ÞžsÍ5òÛn»-ì@JjŠß)ª `·T’íD HDàA éß?‚^ŽMD ˜h±í{%{9çœsJtŽ>úhŸÎW>êÕ††}þóŸwé@{‰E¿¦ÅµÖZË<'W^ye†¯¶Új­^”tòÄ4h·‘r&4W bÿÌ^³Æk ON—iaŒ(±!C¸N<ñÄR-Eí_]^³D‰R¶D HþA’þýƒæðD Hú5x-𶤴ûA`¾ò•¯T¸ 2Dbóõ¯ý­·Þªž¶ÞnµÕV Yl±Åtãl(G=aèbYL‚6ÜŒ~ʵ—¶éä1„bô”£ óÔSOmÊGÏzë­‡ ‰[H-³âŠ+Òf©ñ´£ÁÆMƒ°)qEU\ú/ 7…³'HD ø;Hú÷w€–CD H¦ ‚¡i±A.Í£gžyƾXå—_þÿ÷KfÛb %Š7PSQXÃŒ”k CÛ4?^xá…ÕÓk®¹†±ÌþÐC•¸uI”ýguòÔrNX>¥M¶Oð2µªÉldt}VŒ`Ëlœ±úÁÞ|óÍ•f+ "—6F@®$ó6HD øûHú÷÷á–£D H&ÞvÛmªüYü H Y7+ttÂÜsÏ=ßxã ê1 Ù]b€7Œ„ˆE©tCã((ÿî»ïVÊÃÉsà 7¬y;tèPTFøÞ{ïÙCèB5‡l½õÖȯ¾úêÍG¿üå/ DÄ.W’F‰< îŽÓs lĈÈûÛßþŸÿùŸj ³¤òÔ bi S ®$ó6HD ø;Hú÷w€–CD H&þío£®,e×]w}Ÿ­Lùwÿý÷/Òž†[¦r>ø æ²ãŽ;®+ÛÛl³ º_|qf±ÂF©@:- Q ã¨Iÿ—¾ô¥^>¥ùË_œS&F'JU¶á¨Æ¯X>¥ß*í¤$-ûY$Ke ,»ì/Ûl6Îöa}p?n›.©]týåe½{䃻–j³$‰@"|(’þ}(¸R8HD` :d0€<–%]!°äTëæ˜cJ·ÌK.¹„Ìh&Lhz÷Ýwc^CÏc=V>ýÍo~£‡çá‡ýø^b~DÛe—]ÍÆOù$ÔûÑiNµÁ0 ·Æ’Õì±Çt˜W‘¨~ÐA!ðÕ¯~•’}2%Úï¼óNt4ÖYg×I£CÌGè$)òÌxÅWÐ`/wÞyg¯,ØxBBC†­éçÉf¥‘ñWR‰è•W^4М4.•߸…l—’ÙND H>,Iÿ>,b)Ÿ$‰@G€4žVb€™À÷‚¥Ð€ä”èhšÛwß}ËÎh“(eá…fIVŒñÃÍèì`Y8^~ï{ßcávús–YXBykCnÉX­Ugd µßi§V T·,›Å³ÝJÙ[c:¶ÙšÆÂô "|©Ê\5 ŒKû!¤€_Jf;HD øP$ýûPp¥p"$‰ÀDËÓU>Ÿ•²0Ãé§ŸN'Y~ÿûß÷BÈ7£* d QÚûí·_/yûq¿ÄâçŒDv ÇÓ»îºË<Òà6u4¢\a¯´¢ÕX~6ÂvË«¿•]d‘Eš[6™ô7"£¨}(ÉF"$‰@"0í$ý›v¬R2HD`âÕW_ !±1iÁR¾öµ¯@ÿõ_ÿ¥çdwFäo¿ývm‰ë¯¿>ª¨‹ðÇ?þ1ôôjRˆü¹çžÛK¦ìÇ #òàqÑà¶t:-…˶zÈÃåÊþ^moe ·ÃÖØ`/aúï½÷^ †¸w>ýôÓŠÝsÏ=­Ôš5hkå:æ£D HD ¤àä£D H©øë_ÿj$ÛÒK/ i^‘Oe»í¶ãérË-7Õø7ÖÍSÛøñã{H}Ðk%yP”ÿÔ§>Äé‰FkóÍ7G~‰%–` ”_§Á- Á©:Ìnê\¹GydªÇ=nØ‚kã7êöÔmŽŠÒ&OŒ9u"L1TEƒ#` óQ"$‰@"Ð ¤½ÉþD HcŽ9òo|CŠ„„†Þ˜Ø¯¿þzhΊ8XV•juŽEÞ±z½üòËEwKæ³Øb‹1#@IÝüóÏßYƒ‰ÙCžø½çž{ÎN}†ó=ôÐCQ+bäÈ‘hXh¡… Ž¡¶Wƒ-h¾cS½dÊ~r̈'ëœgžy˜OüBW^yeÚÕå²9ˆRC¶D HiD éß4•b‰@"ôwÈHùÙÏ~6rðÁWœ„[<;ì0Táƒ&Ñ8à€¦²“O>y]@‰…ë®oîìsÏ=7a}p?ÆvØñž}öY)Så&Ê-y„@sÄ+ÊÄLCj–yçù]vÙ¥)\ö°xÃùÜ[+Ÿöj[”Â!X&E"͌ե)’ƒ(3¬öR›ý‰@"$‰@…@Ò¿ ¼MD hG€²ãP‘µ×^{ðàÁ'¡R¹©;¿ÿýïûˆ|'ÓÂÇLäq1­ågœ]‘á›l²Iû &N|øá‡áH„ÀÝzë­Êàù‰ÿ'£~úÓŸ6Ga¬#A(O·ß~ûæS:y„@eÓ£Båøx„™ñ¿ÿû¿HH&M.¢õšª¢‡Å3°l­WÀB“¦ P "ätÀ€(©®E]”ž¬_˜íD HiD éß4•b‰@"ôkpãÄ™ßNâIÎ<óLÑyë­·4a‘÷…ö´@¶é¦›BføE˜Ä¡ŸùÌg¸mµRXO»bU¶ì2 aÞf2Oë’å…T4ÍÅÐi>Äʧác/¾øbÙä‘G2Ô®ªÓ2,¶`Ôrk!ÓÚØqÇȽ„↌VAù”_ª rÊ·6f#HD Ÿ#ô¯Ÿ¿¹ýD H¦ AƒA<~üã_xá…ÁFl ¡wæÉ”Â…cÇcÆŒA þ¢¿ýíoÃã „}ïòË/¯>áVöelÞì³Ï¦3ÆÞtÓMè&a¸«TÅíÝwßbÛ‰]Ñžk¯½6ÄlÀ?\pAÖ°Í6ÛT¸eÁèañlÁ§lŠ­!Ï6›òÑ3bÄd Õ[l± ÒêDx€Lúãb–ÝvÛ[%”d#HD ˜’þM J)“$‰@¿Fà’K.l|á _ "NÿÌ "4:è ѹÿþûa&Xá¨~nàÜšk®I “^Øa|ƒ°¡áœsÎ)eN:é$:!Ne¦Íûî»V¹jšøË,–˜§"¼3g6šc=¶TÞl#Àt3äõ×_·^qŒMIz0ÍYî"¼Oc©2=_dk(g›­æG$Ï;ï<Ø×W\»¬€„5#Ž8¸¤½Új«q48šrºl'‰@"$Ý$ýëÆ'Ÿ&‰@"Ðß ø<+0 jh<ÄlÊF¬Œ²“8E*@ôB°iì IcÉœ©5ï/ù‹'† 2UãÍ7ß”ïéÉiá8¡A‰•py‹€Ô‘|›Ë/¿<ûZi¥•:F™¨†µEp#‹4s)Ë.5Ó‹d°Ùê·7Þx£¾²T˜ð騱cY±”‘Ùx•)‘‡ ËZ9šˆNlêÏžD HD B é_HÞ&‰@"L…€EÒ ºÃª&Á+yAkJ[‚Êï‘·{ÕáqJã䊡£bóë_ÿºùJóÝï~*âr¹÷Þ{Ó&Ég7Õ¹ãŽ;4Íᜉ<Á{¯¼òJSy³13¯0jŽ9æèŽZ d¶,E2·. Êߪúï>ú(‰^æS¶Ê*«Ð¹í¶ÛÆ:`¤³¼,4’ Él$‰@"$Ý$ýëÆ'Ÿ&‰@"Ð &ž–V¸ù曉 +é‡m+ûaª’>U{@ÇN…déÍ Ð$HO8á„^øb̓L2£6FxÎ¥½„£ß|›®­ZLÈ´6†ê¨î8=Ç’ÙEX.¸àÓ‡²Tܪ™N¶‰r¶üÚ€úybŒH?‡c8E9n´áæ È®­üEàç?ÿ9=#Y%/íµ’ìOD èç$ýëç/@n?HžÜrË-ò ²@ðtb,–¶ÚÁFmÔÔ5zôh†@) Oô£ѹÀ t“ ¯G–ÑÖ\ ¡0¸‰jRÃ2ìñ´WƒÒŽrU(é%ýguÂÖœÀÈÉRãQ³Á6Ù,òlœ§„PzK¤"ÆÃ¦<ö@„^xaãØxãé©®UW]5ÃpXM=Ù“$‰@"T$ý«ÉÛD H)˜ÆSÊaø_E?&L˜€(¦'ú±AµÖOGਣŽB"gN²¤˜Ä¥»€ž‹ lj“6ópNYåÔÿà3‰ü?ÿó?ó»å–[Ný°ýŽ*-ä7Ø`# KÇËö1“ƒúÌÊÀK/½´—Xô³Yl˜læÀüÃþeþiîœãŽ;Î~ f?~[/«ÔíD HD ¤­°dg"$‰ÀD-oØô¤"ë Œ0OýÛ¿ýàxýð‡?D†d•ÂYµ|Z¬yxœBxÌuùÙÏ~ö™gžé˜‚GW]u³ÀýÆ÷ÉO~’6‰ÝCxjšŠé‘Ê…ÄžRGTud1šþ˜eÔ¨QÝÂ>µ* ^²x~âÿÙ1 º‹f¦xíµ×3ÂÎò‚us@ôpXÚòQ"$‰@" IÿòMHD hAÀü–ð ò ì·ß~%å°M¡†~øáÜbìv˜„%®½öÚHê¬8`À€>=3!cˆ1„T¢n¸! Z/s+&Y ð?n±È1„œ+Eÿs›xoRΞ[.£Q¼Ëþò—e°ô/¹ä’üâhÚøÙ².¦0Lò¾D¯†»Æ±Vg®¦ !ý\U͉^j³?HD ?#ô¯?Ÿ~î=Hv *XÛ`˜ø(X'­šL1¦üÐó׿þõ7ÞÏ\sÍ5튊^tj'D…Ô±xØÒÜa‡\Qýܱø‚F8\9†ì)+®¸"ò«¯¾zôï¿ÿþôtdò|ðÁ±ž!sþùçÇ((¡…UReX€.©,‰…نܖZÛG}4j¹ ¥Ï=÷\«LÙUã©A?8XÙB%üJçšk®Å[Œ[ެOR]êÏv"$‰@?D é_?<ôÜr"$} æ>jýí±ÇÁ7¢aºõÖ[žÖŠv­,³Ì2j ¶Awҗ믿I¸Ù“O>©ª—^zéK_ú–õ«ô›+åË_þ²u} _Òa"×$ÿùŸÿ‰Ñ…{íµW¥ %¨âQU’A1Ë0°–DFB©rGSÄðGÕ‹uå•WF3I_ªI[oeŒßüæ7ÍsÊ)§0ÖKmZS×]w];9¸V=Ù™$‰@"ˆ@Ò¿|D H¦BV£M FA%½hO¡“ÿø˜G“³_|qªñ=nôQÄORבª„Ę˜ìP^Eî±ó(xPNB2 ëAË~ÚÔLŸm¶ÙP‰-aÖÓu‰%–hͽ)ÿDmUmÂZ ,ƒÅ„BŒ‡LAœ^¯p¾»ï¾ûŸø2Çü;ï¼#‘Ö%ô4dª2â~0Xù6·q¹ñH[ÊaIJ›ª²'HD ¤ù$‰@"L… 0e87ZÑ!Ȇ ¡”£Ûäz¡aÚ©.¹ä\.Í˲ï¾û†@Ù¹5ö¬h³#~=8±—ªìOD èç$ýëç/@n?HÀÚ¶È"‹À"d2& jAƒ‹2 ¯¾úª®ŒÜZÇüm-&¡. ŒççwÃa2—]vYtþÇü‡‹„œÑY5 Q„2nvÚi§Ñ€ÿ<õÔS•Xukuêû‘sŸ>r–V2Í[ÔJ®˜H*ÈÔ%‹+‡˜™oبHbrç°B„VIY€@xT9²– £ýƒüI.’ Òi {üum´=8±i8 mÙHD èÏ$ýëϧŸ{OD`*dSÆÝißk– NÐUW]U¸÷Þ{O¥eêùdt\pÁÔO&yä‘0¼ÃZµé¦›ÒCZ”Ö”›1z²`äñãÇkŒ:t(Ì[ã¸àœ¶—£ä@{©ÍþD H~‹@Ò¿~{ô¹ñD H¦BÀ’ šþÂÏvAºo|ã4ˆ¦s0lPN5räÈ©ÔM¾1;(ö=ÒŸ4ŸÒƒ•Ìâæ³Ï>;$Ígžyf«dÕ)o”ð”Xy‹˜ò,ø¾ûî+u´cc™´CÒG&L@Òð<A»‹Î‹©\ª©HQÅj™§¿úÕ¯P‹•ï׿þµù`ÜŽ¿r¦£½è¢‹Ê*«d§MýÙ“$‰@"ÐHú×=·œ$‰@yVà°  Ãî—Xz饃c`ĨÅí|óÍGI½PqñÅËvŽ=öØè¤ñç?ÿÙ¬' ,•í¿üå/VçCó AƒÊG½Ú$ódF.Kðž÷ðÃ÷¶¢e¬UÚ¿öµ¯M‹µÐ*ÆDÎÈÔÝñTŸOF]~ùå} ’\¥0`ÒϤÀýDZÒiÍ@Ý_¹åÒºÔRKÑfäêN–Ø+R1tf#HD _!ô¯_wn6Hv´øí²Ë.¿øÅ/&Š)Y%è-¿Ô¬Ó¾të­·VZÎ=÷\M…%ÓÓ ‘ê%W¬z{úé§; l³#§Â”k‡¼!oîÓ¢W|¯f!^nÁdÈf›mF%=«ÏS~½;ü…¨eS éh35 ¨ô—·Xÿ4‡"8ôŽ~ B¸@Æ2u«¬SI;¥n!b^Ò¹æšëýŽ)ÿî°Ã1Q6D H¤ù$‰@"ЯÀú4`À¹%Îõu„~ÀFÌÌé#kÁÃmÞxã^xtÒI3ðœsÎYh¡…h[¨ —¼ýk¬±’묳Σ>ªÍjÏ=÷ì5„‚#V¦‡yóÍ7µn·ÝvÍ'žx"CØW™¸&Œ‹m^sÍ5Í!ô ŠQ¨Ey0©+l­KAy"…ñ {衇҆sNKiD€BЀifÌ[5 íÃYÁ7@IDAT{,’^Œ’pzK“îpd}Z_+ýy›$‰@"0 #ôo>ÜÜZ"$}#pÜqÇI¾ð…/La“ÿÁÊ'…ã^¡ä¿ÿû¿wk<ꨣ”ç—‚ÄõuË£I¬X²Ê[n¹ÅT¢Uñt•D¾ /¼°RûÐCY ï˜cŽ)aO3·JsˆL“ãóÏ?_¡V…BÔVÐã£ªÒ ÅæŸ~¾¨µÖG$=Ûo¿}¥¤y PÀ…0Pó ŒM™èÁH‹)|„1aže”ÓÑàÂZ{ÇwØæˆcl6D Hú9Iÿúù ÛO~eÖu¼„'œuÖY2(ÚP ²žHø5Û'5Ó§%ˆµïÕg]uÌeÓc‹c  â*;y §²¸ùN;íÂecìØ±ŒÂ¦wà 7ØO¤œÎûì³O)mMš„Þ•iW®a…!Y6X»c1Ò<ÁôVXa:‰0¤öCw§ª(lš /¸°ù´ê6l’T“·*àäqõÏ6Ûl£'* sЕ†¼MD èŸ$ýëŸçž»ND`FúipƒþÀb¶ÞzëÅ­&¦»îº«OÔpz4dޱ(!‘L¯!H®´ÒJˆ5Cé4H²ª²ÂÎ;ïŒ0î©ÆïµªÕ…ƒy>Ñ¿æšk2Ž<­Ee<™í÷¿ÿ}1ÐÄ·UÀ2ÐÌ’ÅDÄ+r‹¿%µ«)ÜËÜsÏý§?ý©zTÞ”VJô :˧Í6k˜sÎ9>øàƒùËNoaž:ÖrÛ+b³©9{D Hfm’þÍÚç›»KD 'øÂ¸ °ª8mlhòÜu×]{**üô§?e%"dk¸ 6Ó„*nrK–¼õÖ[…‚)Íý÷ß=˜­¤ëúë¯gU°Pjß5…£ÖdbŠ"XÖ—_~9š @ÐæIˆ œVS@I7c,†%±0L‹.õ±Çkê'în‘EA`óÍ7o>µˆL© h³Í6ÂÀØK8ú©*$Æ[¢´¹°dÊÕÍ>ŠÁ–N¢ì4Æf#HD ß"ô¯ß}n<Hú;ß("C“mø 3qak%i‚ؾô67nt’„=6͆Ï=÷œ¬²We¼0©‘L…Ę$ºd1ÙPb%Ù‚A{:Ì1ä‚ .@9ÆF‹2¼ÛRç@óÜ|ò“Ÿd,Œ«4T†fO<ñ„öÕðJ-G(tj:cÓX޲½úê«#Ì6ùK‹.·aNÔžÉq75dO"$‰@C é_;ñÜo"$“¸ôÒKaÇH¹iCþ€1ðõ×_7‘¦=üVÕçz!he«ä!…£ÍpŒx¥ÕŽj{æ®Üj«­z©¢MÒ¨0\v´âŠ+v[äB•EÏH†èình*dÈ¿øE"»…}Êb¬%ȨóÏ?¿{ÈgœTc) ,F?TìNг”lmKAÑ, ¥Áµá†jѵœ yDe€z«žìLD è?$ýë?g;MD` ¤*ÁÛª@e9SzÒ–'Pðà²Ë.›D#Šëì³Ïî;ëþA3ÊÊ0=œÑ„"5ñTb8¢ÝõÓ&Šv?\›ù9[—DUw«ói# ±U¬êtñ¬s‰%–è®±Ú…yíˆ#ŽˆþÖ:-NX2^1ï•“ l­µî_è,» ù3N¨+)Ô!ä—ä¢ÓR…"¦ÈF"$‰À¬‡@Ò¿YïLsG‰@"ô€)R0^á—+(½á‹/¾8å…@³pB9‰%±›1¤i_"øÍ›„ùQyïé§ŸþÄ'>äøñãK ­m²ɆMD ˜5Hú7kœcî"HiE‚g„ÞM7ÝV/,q„•ÉüÝk¯½¢¶xG>’­·Þyü3[EÎ$œ˜òôHl­Ï^}ï½÷¨™ŽZœ!µ’Qè¼’©neM1ü?y¤Kç—¿üåN!À,¿‡Û$´êÚk¯­4—·‚Ÿ{ß,•0LýÂRYp)Öl3†QÐà{ï½W»°NS’ÀD`›OO;í4qq4<%ý íàÉ´I$³À Ð ¯©¥/`’7=}ižm*ÏžD HY¤³öùæîD ¨ªm¼ñƃ5Ç¿õ­oÑèH £’‡zˆÄ$ЭÛn»²Y.´ÐB $ L³öƒæ),Wo¿ývëŒ ÄŠhx>¥84¢ ÏÕVy;O>ùdd oÐZ.I,½†˜¦ºõÀ CºN†ã}zÕUWõ‚5¸UfFäÙ¦9oØ8ÛÿÝï~§CæQGÕK[ôÇIAç&L˜ýÍÆí·ßÎ"½tF¥íá¾ýñÐèÓû´9Eö$‰@"Ìì$ý›ÙO0ן$‰À4!@Ö €9'ƒØi¨XtB:”Bç,ZÀX¼»KÉQ½]²·çž{†NøY=)2Q¦Áœˆ±‹e`f áªAªLº0õo~ó#§¥Zƒµï¨½ÎÅʹ­4W·{ï½7bdÎÄT¨ Ž”›•Lu˲Y<Âl$±Aki°e6n?:QNÂÕ^9fc³Æ""ŒÅ5töjhWDØ+TºkglMäÓKsö'‰@"Ì$ý›5Î1w‘$‰@”7W@’[ê~)ДG¡¼)Œa2'éÒ5ù.ˆÊãÐXæMi4[ þ‡þóŸË§¯½öšë°:°M² ÂXJ6Ûï¾û®[pñK/½t¯ò åXˆ¨áY9 n˧Í6¤÷DXÚ!‡Ò”iö°x†°i-[Ó¬ÊfÙr)oQ¾å–[®Š± rϘiFrŽ•:ñ´µ‹©«e AÑ-"O—4˜6¯DGIŒVýÙ™$‰@"0³#ôof?Á\"$}#`ŠKó©ÜxãVº“ H0…¥èØcíÖHlÛRK-Åpò—ð»ð ‡Q«H˜l„ —Ñê·pÌ ”3C’vÒÆP6-\Žâ{r?H% šÊ[{6ß|s¦à¢Ñ*Pu܈<,·zÔë–Åkëc;lÊl:l³I´ðÕY´•îÂ-ý‡—&ÆØaƱ `o ,,CuÄI;üÿ/¢:+8Ï<óðÔ—£›íD HY¤³üçD ¿#@Ñ<>÷qÿÓ¸Ë.»p+s£Ñ¼*3]>KÏÍ9çœäÌÔ‡ã^3ã%Æ+Ÿî»ï¾M%öPÝNWLÓ¢`æj&ÕlK\ðUr¶ÊTG}4›%(θ8n+êz6ß|ó>TW¯zݲíunŠ öªˆxÝuסžöë_ÿºÔ†åpÙe—åüÐGü㜞Ã;¬”l¶á±f‡Ä-TY_Yzxôåõh*ÉžD HY¤³êÉæ¾D ˜„Æ"ý$5 /' 4˜mРAÁ lPï®8LmÒ†ñãÇ#I¾+§cಖ` ßk¯½ÐI9¾îà@ʵGáÁ¨ØJZPM-Z»îº«‹é`˜jˆäŸ—M¾ ŽØ$[Ó~*Ïš—_~yÖOàâá‡NãóŸÿü´›Ùˆx²56غ ;0¤Àƒuçé|çw´BÕJHì·Ïel¶ÙfÎß³1RwOßW‚×£O‹bÇúóQ"$‰ÀÌ…@Ò¿™ë¼rµ‰@"|8Î8ã ¡}ÍÓÙ2bÿ¾ÿýïKâ·#犛vå‡?üa¬ƒByÆÔ‘SäÉ'Ÿ´›y2¡fÎ áfoI+¶³†7Þ8XPSÒä öC˜Ô£DÍ…‘㶃:BÀôýÉO~¢ ¡³•›¡V¾„õŒJÜZÔ’ÖMeUÎØú¹Ø%zí…~â=”}öÙ‡[ÜhÍ”C¬`Ó_ØÑÙgÅœE'O>éGéÀõö\mµÕ¬%È#^ c>yI:V˜D Hf%’þÍJ§™{ID`*ð!´Ì:¯wÞY2%3®‰¦Z™ÄÞ¿î¼óΩTL}#‡Ä¢U•ì‹@5Á÷¸5­KkTÛÔ*'î±ÇLŽ—£ îÝÆ(ëB™°’©jÔ¨Qh€^sÍ5•rnÉLcá;(Y$Y¡!CãQ3u9Öóøã«Cæút4eñÖ g¸¡}l°¹ª²çÖ[o…¤q.X#­¥Óé믿^ÊØv-¥DóiôpˆáëSÃ<8šÞpà ž6䟃6/‰‰jBC6D HfU’þͪ'›ûJD`"µø¸'·$X˜Ä’[‹ªÓ€Éð[]Ѓ^ô0Lá‰üyç׳˜&2¢ 7Ø`Äp í3‰ËÍ7߬æ]wÝEž+’o¿ýöÁÓª‰Xš!3”û+yä‘ôãYÕdǧ¹’ÅTeî¸ÕmÒäHC§¸å–[Ê)yäýfO=õÔ²¿l³lÏp6ÂJؤ” ²ÍR¬Ù>à€¥¯&+v]Ê>’D¯t;@I_ä’ ZP‘[,Ÿ&¤Áͼt–e9Êé²$‰@"0‹!ôo;ÐÜN"$S  4 Ì|.JÌñ•OJ’ȹå–[ÒS^&…-wÜqMõ‡ÄhÖ|d^Ž‘Z[Ö£>ÚKÒ~  ›÷%²°P¸ÜX>‚úšcŸzê)Sª´Öß“w±;ŠÆØÝvÛ ’éôå—_ŽÎhÐiTÄìÄb pÁ„X4Î9ç´‘9†Ê‡ÑY6X6lØo¾¶ÉfKɪ™Ä\jÓY k·ä8ª~n98É^y¸{qdcŽ9˜/PÌŒ4¸xIx=Ø2¯Ê4&ÝiNš=‰@"$3Iÿf¢ÃÊ¥&‰@"ð!XwÝuù¾§¸c°\MþÚŸ/GƒÏ}~¥^öó I@òøã÷)>–eÐt8d3 ­\q€24¬^W\qEù¨Ùv…dX)½= >43§±p1 ‡Rópn·ÝvÑY6°à­µÖZ,wGÊBðhôèÑÜBl°Â•’e›G †0>“N°”)Ûºª2yAË~Ú,=h(ã'Ùš)dØl%·TóÓG—á·Ö"!Lƒ#Ì—•ÃÒ-–ãã[i¥•PX^«¬²Š·uµ%¿(’V ìXa9{¶D Hfj’þÍÔÇ—‹OD + à‚(Úf›møîÇwÑü´Í/RrƒÝwß]]ãÆÓË®H½: ÓpôóŸÿ¼}¾÷{5ÁŸ<ÿüóßRÿ‹ŠÙáœM»Üµ×^+%Ã_1†YL*•gÐà‘>®ä¶!ÂM%}®V‚㥮§þ•:«6µà—Xb 䡚¥‡ª®•ÌÈâ«!lPû[«Mã›pÁÍLË9ï¼óvìQå®™C1fc"§ «âà8>eŒë£3.ò¾ÚÆwt5Ö° Ûä%qï¼6Õâó6HD`C éß,v ¹D H&b²âœV êøi,²vÁw?ö«`6JFÜš^‘Ä¡‘úÒ\&äÛ, Oh9'æ/JÕ‘ôµØ²Z³JbïÒÎ6vìØ¦z°ê ‰Š[¹"¥5Qg©BnPî7tèÐòi¯v”J‡ƒ•¦ÈVyŒo¶ý÷ß_#÷Xp/ƒ'Û ¶ÌÆKP/­p„M9p5QÞ&‰@"Ì,$ý›YN*×™$‰À4!@9ósê…HIýú¤7믿¾æ&oã÷Cij‡·†XYè¯)IÏÖ[o±d%sÀô'ÏÔˆç@3ŽRá OGS­¸ÈaƵÎ[ubÛpà â¼Ã‡¯ª[Œ]ºAbµXûœj0§‹fUæb©•æê–ÍZÔÁÒ×_½l¿ÜŠ£žrÊ)¬ŸŠ|}c° ÂS3‘Uôª_­…túnà ‘”þgžy¦ZÞ&‰@"Ì$ý›5Î1w‘$‰À$à-?øÁø|_f™eD„D)ÜrI‡Î:ë,o«ßVò€†]vÙEIì„gžy¦:›¿W^y%b”šÙ# ÿÓÆhiJó!‰5¯WòÌR9LR‡U†´:‘–¶qÈD˜X;R¹è>Úq VüÒ—¾ôÜsÏÁy Ò ¯Îk6€:â';JA”Ù²fLôÛ c“jÂ`ä(ËáU›ãã-ÇT=õåí ,q(è+ï¥?²Â(“A€­xfg"$³IÿfCÌ-$‰@"0êËùùŽe颋.‚Ï@üʧŸì)Gu”å/«Aò…2·FBєǴIW·3q%f«V=DÄÉvX€QsÓÈ—¬T®…et$’q^¼CÙ,¤ŸLz420²¡TËS?¬õ¾ûîóŽ’>MYLkzÕJyë-äá~­2trpÆF¶n™#‡^ކ rLV«6ùpLjÓ¢©t¸->~¶njZpëtÙ™$‰@"0ƒ#ôo? \^"$Å[Œú¨ú­aÇ@;ú{ì±AƒIÊßVû6(£a¬€T“ƉQnŽ$%嚬ˆæ&3 ± &Èg˜CS‡d ¡6Ć‹(8K±CÌz¥Ša¶5ãÙJjΦ¦¨Ch¶aòLt^}õÕå#}/QÕaŸd d/C† q_Óhœ´9šn\¼º*øÎq ÃÊ¥‚VåDꦨ‚Xžu´ñ>µ-»¦mEG«ÀÃKL²$‰@"0Ë ôo–9ÊÜH"ôw"Ñ?ô+“®Ð' n+¯¼2IZ‚Ø X_;³Y0 R¡ÜxãŸþô§B€_pòÐCÙ‹/¾ØTRöèf‰0ô¯"¥˜mÒºlòÇØsðÁ3ÎS±5ŸRºÆ‚³TÚ´náI>Òx·ŸVÚ¦Ç, -›£l°™0K¢Gž†¹µ—ý-†ÿìg?Ó¶&ã¢ÌCGД´t!.¸ÍGà\z´q[µ Í~þùç¡£ÜÞ~ûíÌh?/US[ö$‰@"ÌÔ$ý›©/Ÿ$‰À$°ÂIídY|ÁÏ3Ï<|ÄãàéI¸õÒéÑ6_ùÍê'œpOI†I ‰V|1Ü­½öÚj€½òÊ+­bÑ)=£Ø€–4,T:+~ç;ß!mLˆÙ¸ð ь…­—eŒ<+à?y×]w1Äd6Üvû^²Ë¸ÒJ+E©½jq‹B½4™‚N¦ó¶5Ñ –:ëm4=0)=Ï‚q»3fL( «/”ô2ž–Í6ÛŒáò1`ïe5eƒ’_9ÜvyG .XŸ<åO?ý´Ib ó&òAŒ—ªÕýµ©?{D Hf’þÍ,'•ëLD 'ÔŽãc‚uˆüŽÇù3HÚzë­ýeƒ¯ÿJ)–% v1ÿíok2O´Qe¾RRÞj‡„%–¡w0Æo~󛌅’éélƒR¬ÖmmÃÄÈ¡CöÞ{ïV±f'Êc“–Eí›Âf(…„“ºISÈàc9~üø¦0=æY"€j ©¦NÀ…-®U¬h9±ì¤m—D=üÎ>ûì¶µþMzKþéŸH k€¢¥8â^*Fõ2ÃVSäm"$‰ÀL@Ò¿™â˜r‘‰@"ôDÀ¨6,~¯ã›^ ~}ò¶Žy|÷— ÜÿøÇ?–zµÔA3ÈYöWmØÐCTáo¼}LÖk¬Q…±Á^¨>d“¨D)K-µ°Laz9R¦â½÷Þ«flÞ¾õÖ[Ú q}â‰'šUÏ^{íÅD- ßk·„ÞT2­·(7× Ó1i«LtB2%Ƥ®±<¾¦®fC¾ PL eÍÀ È2IØZ/Û£š98Ý€K‹"^¬:£ šÇo$€•ú’ðÂ$iUd†®™¬¹ììID ˜IHú7“\.;HINÆ—:ÁZ9£æø çß~KÒ!ÐzÁgð‡ !cæD)CÔâi40@I0ÂrxÏ=÷uF,_ɇ ƼsÎ9g¯BtBr4@’‡ærI1iÕ€)InuÈdÙT·«dÊÛ#<’•ÀúLP“¬]ÿôO±…R¸l£VLœˆI+’V Ûf³F62#£H¸Ú”){"„+ú0éXà¥ÏLùÐsØa‡…XkãgÙ¦!¥~½ü\o[4x±}2@_•ûï¿?ʇl´ÑF¼TÆv¿­ËÈÎD H¤3æ¹äªD è?ÿùÏÚjt­¤Œô?üáIàA{Ë-·´‡_¿òãÖt+bÒ´VñÕa_â5k‰ðX"y#¿õ­oÑ“¡rýÐLLLzÛm·…Xk‹“e>-nœ(1 f=£ž¡óö*MU!ªæ˜cWÒ§«$òÖB€¹™ùOQÙÎÜsÏÝÌA ¡’瘽³|c,½€;(1oe¿mT¡U¡\gT¦Ók”4åí c¦R¶Óª¼9 ˜ Ð`ãŽ̦Õt÷ÝwG ÙîDÑ¥"ÌöcÁü]€žðü¤bصpRBP œ“#Y烗­¹ììID ˜éHú7ÓY.8HI\D‡IMFô˜d…Ô”äË£Áж’4â2Ê&Ô#|òÉàR°Áp»qãÆµŠQ-]#’Ž…ä›ùë_ÿÚ*° Ý8 ~3Ÿ'+ÁÌUƒš.øì³ÏŽGø(šÛ³áˆÑÿÔSO™ÅdŸ}ö‰Îh`æÒà ùÌè§-`¨-Ã#™YË(åm“MT»¡ŒL­åâéÓ’e Ü‚Ðc³~=³P¥C{ï~ûí×\@ôàãj½xþpçwF?Õâ°A©z°\Khðò€ŒËú#»&Ù>‚ 0œ"~­ŸøËa× šÔºJìK’IC ©”Åj•¤óºë®cj&½å–[B†x9ÖH?ùE£“åÎ-AjÍÖü+0Y.åÔ±SI½0©u­ÁyÈ5ÊŒ˜¿ lìÎjì¨Ba¹ÛLmnOØ©®l†¥‰Œ‚š:è ¦ªè"ëXc{¤=¤”|Á[>NÆ£ .¡‰ sœ”Œ¬'>ø cïÒ¢¸ýöÛ×K™úÞ„œh3^ïDi4Œ¸ÁRn³ÄK°¨f+°f=|0µYaIë.ð€$N¤š:q|üñÇËI;Ú÷3r’Y¿ž¨ò,ÃZy»ì²‹ÜÂÖ‹jb=C-þ¢eU •‹A†¥3'ÐIíºSŒ’‡ ‡K±GTáú««'FËgŸ}6Vαª­ŒtF–äÅúMÄ-GìÛ¥¨FBÞ%^3½d‘A ôg#HD`¦C éßLwd¹àD è×@¬DLJ8™KÀbÈ!~ÇûËS£ïtÂ$•ä}Ѹ‡O²‡‡¡fŸÒ÷£œÄŒGçwúe/¸’’w¤z2F¢/A ‰’Øš´RŒþî»ïŽá1‡dVfÆ AÉ­Oø²5ÚÇJÖ¶Á„‚ÝžŸ¡1ëì1Ñž{îý .”¸˜vl„ÈÃUW]͈•îš,ô@Å\ŒlH£³Ù0Y(ÕüÜ2z¨î>½!ÏáòˆÓ ‡RªkЙLÕ²ÚÄ Fo¡˜¦É¡‡§Òû˜"‰@"$3Iÿf¢ÃÊ¥&‰@"0qï½÷æÜšˆ  ²;óŽÄ÷ºf@n£òm/s·`sÓç°Év0Ím»í¶ ㈠†Ð‘†S!]ŠYF ¬‰Zò7h'F*žžþù(‡W„±·—_~yD™ý½guš!ŠÄ j…ývªË$b]”7NK•sŒuÜ ‘|µ×Âðð4s‡vP(ô§ ª#RÓØTO½å˜ôAE’3"Ú³UŒNŽŽ[C+:¹-/3îØƒ1Ð?p‹…ÓBL„“_<^Â^se"$‰À Ž@Ò¿ü€ry‰@"|€¾‚xâAQ`|‹ã‰Á‡¿Æ}…ϧ2y ¶—afùÇ’Ó+”’f¸£ÐÿÁ"ÚZf†4hPkŽMÜG%¬œÌ™òÕ*³K›ÖI´±j!´f|‰×_½\QïDŒ]2ÒuÖY§´¹…¼ !€~„bÞTØT·¥‘d*zÉZ±WI–Zi®n¢%í‚ .8á„ä™ÀÒº)`LV™]+mÜb<ŒSþÙÏ~ÖˆŽXƒ^?4½ ú½BÏûSŒ~,n¬³¨L^<:yÍ*oÞ˜+‰@"$38IÿfðÊå%‰@"ðšÑÈ"›"I&¦'¸ ©Yd>ôâ#^þ·40ìß’ ÈEù‡æ(Z;ï¼³cî”I~á _hƶÊ&y䑱’0‚•Uâa=âÙ´³QA¡WB ‰Ê÷zˆýcULJËV–E§).‹@Áv’þ}ìG HD o°SYYÎ2 $5ñs3i*%?šƒvÜqGù˜ÕïøñãÍóçéðWÄåt{íµ‹ÃN¥ÍŠâxe’2¦X‚¢@ØÜ’FB›vu±<=Né· ½O-ÁÐÉKh= ^ËjмMD ˜ÁHú7ƒP./H‰|Ók1Ãv'š³HnÉ#M~Ç“OE3T˜•ß÷07‹§¯¸âаÞsÏ=X¨¸h”bx92…<:餓 'ÃîÔJáªRÇý÷ßß~LvQM­ÊÇ(É¡àD©ÄöË/¿l& bPÙ6Ú%xTvxÓM7i¸ÛgòÅ(nélN=˜Ëˆ©C’Ü0ÔÀ`×´ƒ@†X4¬ÁâÙBtÒ`ƒ²q¶ÌÆé¹æškàŠ(¼õÖ[KÉÖ6ðFF°á†6‰.tÔÄ-¶Ø¢UItz‚¼­Ö?ø­!š‘õ‡yÁ`¡a¤F"ÚxyÄkÙM8cÞl$‰@"Ì $ý›A"—‘$‰@OLôBDŸ˜¼øòæ"9ç!‡BCbCq€ÜF´ž,q’hqñ‰ÿüóÏ÷šŒ}“…–…ÂCG’þC]Ч)¬Ïž 1òS†â_ WALUwDfKÌ;ï¼tZÒ0æ-o¾ù¦Ž©r*,i’R¦µëfð(ܶЕä k«"+Lù´Ù¶8[RÄÖ¤âl¶Ì ƒé…XØú$®XêÂØ umNjg*ó§TC/úóà˜=´ãúÅ/~QšŽEŒ×ŒC™HFê±òrvL—D Hf4’þÍh'’ëID`*ž|òIø–¢HMIí>ÄéÁ u衜·v3=ãƒÞÑbaÒYýõ1åM5Íû7›-°À%Wyÿá”ñB”ÒàíhGíòzY 'úQ©ÄP=jÞ²lÃçØÛ1‰(ÃÙfå/ +v³dUi YTù<`^i1ŸeÌaµ *ÂW÷»ßU¼åÐ9z·ÃÙYåÂÛø=úè£)(­‘–Æòš‘·&h¡Vh^H^BÞ^ÑÖé²3HD`D éß x(¹¤D H>@ÀzkC† ± [)øá?Çç3]ÊGƒ¤/ü®±Æ­•f+úõ™Ä›Ñx>îqAü`šÉ-áøÜ烾̄YÉp{À Pˆá‘BvM{°°õéåö1í~,Ìâ½tÒOÖàE$ÙG;„ã‘ KXcM¢O[†\"ìÊÊ·Š•,^âäv@)l›¥mÒošt§vªyKõysºHv½|¿UØÎUVY…Õ®»îºMŽÛ…ñèÔJ)E÷%’þòò°0C%}jf^6_!Ä ¸²Yo™´9]ö$‰@"̘$ý›1Ï%W•$‰À$Œ¯ûÜç>g¦z¬ºÆ'ø’K.ÄÏwÄhϦS_Øú|Ê/TAøœ`ôÀ£ÃŸþô'‹ÈtÐA“æîq‘'=L@Ö@ÏvÛm‡Kj5‚z .éøã¯U·DÇ鿊Zœ«§Õ-URtÄG¬¾úêÌŽÿg¦X áöÔSOEýçL¾$6t6%£G– =z´uðØø´¸Œ²õ³©*²1”Û¸ï¾û4ë]tÑEå#L¯¦ð[óýP‰µ¬¡™ª4Æé[qÊ)§D'G ÃDGÏ Àkà!B,í_^òyšûGoRµ¼rˆip>î¸ãH£€(¦ËF"$‰ÀŒŒ@Ò¿ùtrm‰@"Яà£_{ n‡”› R_òu.µ0õ¢ä¯pM‚Hš2¾ËmèÂ_PL7|¾Ë¸ |¤å¤ßÃEY¤ÃwS)"5UEý:y&V£2¤c=:,Q•ߣË_lÄ¥’|’Ô åÓ²MÆKÙÈücúYª(ˆ 5Kɲ 94XœýÒ9:{ñ¢ìX« Û&¾µô`ûÕ¯~U*¯Ú‘×Ôá½2‹Æ(+°³þp¡üío»øâ‹3`«ò€ÀN?GÐÌþ ñ†EŸFT×#ã¸1xÆYð2 >´´ãâ""QŽm§–L_9‘ñB'2˜娱Œl$‰@"̘$ý›1Ï%W•$‰À¤j{ñENcðàÁZí,ú|‰š<ÕâK´¦BÐÂÐ@B—*œ¾ÙC€.üi0ª;§Çáu%Edˆ4+ÜUW]åÉin‚xüþ÷¿ï>KŒ„Ö¢Àš¤Û*·­Ö<¢ „#qe„ÀaªÒ ]‰Ì¨åŒDÄÉUÈVZöëØÉ#Ê~’L4ªÊ”û%–/‡D›e»6¢qŒ[6­ }¤Ÿ^»@×tjvI5ѪÊN^ÖÉÛ2bÄ—6ôR^süð´ºx…Ù}÷ݣߗÍ_GyD¿Ì[^׎%å£D H¤3ÈAä2D ˜ ²wD¦ ¬@Æbñ‘ Í‹t)ÜòEnòOÚ’²Ðh½î¸ãŽ©&˜| çìˆQ{ì±GS&zˆaC;Õo~ó›è´Av 8/¾£Z ± õ™¿D똲£ã–z{Ùe—•³ðH²JÅ‚ÊÑÂf¶иä’KÊQcÇŽ•ÿ´d·¸<ˆ9Š5XÌ4+ø‘ÍÅåÁÍšéUX°¦0dX-ªfƒÜ–«ªÚ$Õ„…ZȳXk­µzÑfÀ×'³WH!ÊIñbiAdwqI×cv^ ìŸ\\ùË‹ku1Z¥µÊHP™‚õ dCy hØÄÂ?°”ÂÎc‰Žº¸Gj ’6ôò« µLd"J’‹üá°kÞÎ;ïÌz %QUÞe  SzlhmcêHЇÑ(J*ËJ8nÍr‰Â̲Ùf›1u‡+)’K,±2øgÒ=,UÅâÙ‚ýlʼ)l³ÕþÐC J¨ŽØ-Ì°Ž£ô¶ UŒe1nm1ñò KªLƒ O‡¨¿²‚;‚ÍâÓ‹›5Ó)^ÇÜòÒVúó6HD`FC éߌv"¹žD H&B 18Rœ¯j<Íéñ3þŠN.RAÂ@üþžsÎ9ñî£S2ùù”ÈLG¨¹LƒøDG›OyØfy ð"Ó~àfYö7Ûd:‘B`®ì3ìÍÌ¥ÐÚ¦9‘ên?LØÔ~ðF•¼^x¡9iôφ$‰•½Ö*“±¦xmó Àv † µUÛš§@âay:‹²àf©z¶&og³•ž¸Å{6\(9÷_þò—ñ¨WCW…”2œL džË.»l8Ê–’¶y1fŸ}v„½Lc›0Ë;ï¼óý'ü‹å“±8¦²_@Ó_—\¬´Y<¯ns–ìID ˜qHú7ãœE®$H)P!Ïm|öžyæDÖIHZIÔY|‰ãjˆ‡Ïnzˆ¼Òrì+Ä;ì°^Èâ¼ç×?™`‰ÚЀHyB?ÆC´ÁBÃF×Ka$bAsV™²rñÅ#Ãjo¼ñÆê‘·0 })åZØûï¿¿U²ìdF7®1­Û5F¥D<)µý½äw‹21xÚNY* n•gƒ [n €³ž½¸¶²ZtÂê›5«„a¥]Uò7«zà>êáv`]Īß7Þ˜€vòŽEȬä4¬G#­Ž ·Ýv¯+¼ºÕRó6HD`†B éß u¹˜D H&òÏg4ì‚€:*kÓ¶\Æ M[l±…_äŸÿüçmøûÄOȽÅc Xe *!6•ËrË-¢²28OK2j¢ ƒ-¨Ûl›«k¥|¼æ;Ò´°‘JŠe¾ÐîÒ wß}·MÙ Eð*%½nqžtï$Åé%SöãÊN³¬ªÛôá¢n¹¨…ÀRãQ³aÙ äKnI<ùK]*ÇJ@#çkvN¤ãÔÔÏqp( Ä1qXªÂc“CT†tòTy_ª†ÁPaõ”¿hÀ/«E–Fù9’d¬á”¥Ä媉ò6HDàcG éßÇ~¹€D H>@€, $Tä{úì³Ï¦—òë´µœ†é/>Áùè'7‰!^ÈÌ7ß|üÎ?ÿüü–<°#ëŠ+®@ÓS3%ás†{Á<5ïô™×6¢ÁíÊ+¯t?d5Lë%…æb“o¼ñ®ªLM Ãèlm°A÷Ž0Ò!íbe'¬Ì”9’O,TÝqt˜È–Zj)ô“àÄP7†O‹™‘ÅX±dß)×ÐÚ¶`#gûPYa™e–a,‡E¦œs3Õ ¡ŒÑÙ«!ßæ€üGq1Ä: xwðItÂsî½÷ÞÖuC®ô„4‰>º˜¢ ˜mÉÂA±U'dL‚¤õÒhd#ƦK ø7Zœfâ®ñWÔw”žfQrâë4–’MÔ éèa Â,U½&Y€vN–Ä ›¯Q¶l´ÒJ+Á?[·o ÿÔSOµ ÐÉqH>=#«—$¹d˜‘5—®§•°Eíã¸iø×ßI&ifãÕ¢Á ‰+ÔCþ=z^]Æò¤ÕDy›$‰@"ðñ"ôïãÅ?gODà6ÝtS>©À]Úßø¶¦Ç¨0?бéº)½ÑÇÒrð øÛËP³!¾‚/½ôRÌU5 ?ƶsÌ1V¥Ã“š~M>©{! Ktk¬ôxK*ΈCmé Ú”çw¬H¾Ê*«„…š‘r,£b­¸/²v½æšk–Lï¾ûîÓˆù´ôH$nÍl:”­{óÍ7Ë50%¨Ba•’”I™ZlYL¬Á÷Dcv°5¦Àgù 4t'äD˜×1|zU.6G@½ Ýb9šàŸ½ø*C`nhƒîÂW+m¤ÿÙk¯½ÜTù«~ºþ}gWS£* ª0®zU:\9/s5QÞ&‰@"Ì$ý›N!×$‰ÀDý<¡F¦Urèûam-ÚºAâªGõsžZƒŽ~ò‡øKþ•¦9’¦whGÝpH‚”’¬FÛ1ìkOIrð±ÄUè³\AääÄ¥ÓP±ÖSÇZ5bý¸e6=NG¥å Ž4§Gƒ T«*Â3TSC¾—*$o§i3[l1¨fs(1æ µáQÉtÀ2ÊQ€¼Þzë±fÔöJÚùôÓOkl”T&%7J%Uû½÷Þ³¢zÅ£_ƒ Þ‚`å Ž¬ÉîTËŽ¤p0Õr"ÌËF0ânjÐiùþÈœéá…8p Ù Ì[^ËvØÁ!¼~Bäv¼Òå\ÙND ˜Hú7#œB®!Hú;Ø‘ü¦/“ˆ„¹oà 74^ŽÏnK~Ã[øææÃÝ´%´£tßâñ»ÐB …+©([°›šu ï¸ãŽhÀ+²¬lÁÓw‘{"Y3.šÃòÓK'Eä –3E Œ‘FºÑE›ej¨H/E6KÓÒ`…¼ádhlìʪïP@IDATÊ™H净 '‘Ãy¥£Žª ÕC9S0‘–X¦f¥rÛ˜%ä¤mf:½üòË]ó ,Àb¬O ÀÒT=Øí§?ý)ì2+¢äÛ‡n\ ¯òþ@ÍàŠ©Ð 2Š¿/À*ÃÓ˜¯2»¬õ!ô‹ô³±0#싱ï¾û:©)a@; ¤Õbò6HDàãB éßÇ…|Λ$‰ÀëE 3nÜ8¾ªÍ}¢á…[\þÎ;ï<ÆÅá–)ûâ7dxêµöÚkS¡ÛövÛm§‹#•éAþÕW_ý`ú©[˜æáCŸ¼£S?™ˆõŒ`Äû+êù/3j{dp ¼+{ŠN~pÇwè~©½”Ü9UZ—Öᨕ<3 Óul3†?üðÃNÔÆÞ™=¢ r`LgÑY5˜Q;! €ÿwóÏîãÃþ‰A8N­µàÛC€†–R9³©n¤ýz—Úx nä(‚)o&/³ªè¬6•·‰@"$#Iÿ>FðsêD H&Rϯä¨!NÄ”©Y¨ŸÎ7ºO± –¥ÿpóÃÕÐTŸøòiÓSÒ_¼(+pËà@èbåu©°Ùÿ;ÌGŠáiÆI1QË®š‘[é+=jÙ5ŸbõÚ}÷Ý]°VÙkö\gÎLsH~ÒI'5%íamVÔ€’ÅgúòÁ<öØc½†ÐBWÅÚ™´××ÔÀt'¶Æ\½lq ÀA  Zõh[cñqQt8sÉÐ8ÊPˆÑ½xdè1K Ô´2ý!OÇQþB•¹ÅO‹×£„?.âE !O­÷¨Z‚Mg¼Ò¼ä±˜l$‰@"|¼$ýûxñÏÙD ¿#°Î:ëø©1êÈ#4G%=XH«á^˜VDʨ?® 1š Í £’ø5½G€‹S¢&)Øcø‚ž~úée^P‚»üšïC!üÇ@>“‚òeOæÅp·Ç{„{'’øpªðÜsÏ•4'Ÿ|²Ë&Ïäã?Þ¨zŸ35(~Œ¯½öO¡:ÌŽ(n¹#b‰’bm¿ýöZDY¤˜‘Ê•~nQ"[F­<Љt›dêˆßk¤å2R7½­Ât’Uº¼¢0Ýð’ªYÁÔÜKýºe"lè&~¶¦ÛaˆÀà;Ã:›z¦êúù5&Ì+7vìXûÇŒÃØÃ;Ì[^c^Wßzà‡¼Ì¼Òñ´9Qö$‰@"|,$ýûX`ÏID ˜„ÀÍ7ßÌ÷±_ù&;ÁQÓ¯|¿›}„éI¼±Ã`xjl‘áøTãL@ŒO h–€°Eõ9<ô ½ƒÒ˜:rèС1°µaUt²€D‰ÈÉeS]ÀÚâN?‘ ²Ué=M"bŠKÌ\ʵJÚ /r/ŒŠ <ÂwTZ‹GbIÏ +. »YéÎJ½7‚õ‚TÎÈpÝQˆÚxÄt.•°Œè/`«Ó#”É*œK¥¿”· DpYd-ÒÀÊ6ígœ4>þÐTe‡ˆ$‰Hùã+Ááò²…<ÄLc ,.úi„Á™‘÷E%¼rx±Ú¦ÈÂØ• k§j5Vûºú—³—Óe;HD`:#ôo:žÓ%‰@"0>ë©ÊÀÇ´bš¡Ì«Iñ¾àã“ëÍO<Á°M6ÙÄo¼5z˯.Þj—  5¯!ÿöÛoG'i<ˆ-Dž¯s<úÌÛÑQ8Îfgp6Ýù0þ˜‹›$ÙJ4"Q?=Bcêh¾’â~¬«AnQN`çwŽš~!LƒÌ71’0ƒXùˆ6kØ[3c$&,ì9a6ÄÖÔ G*Qfa%PÇ©vòþ ¬¢åøÅ[²#RÑ®­Ø6ñ*Tu\ ð¾âIÿ"oÅBHH/ó›5S%fºarHSa"$=ÐAŽ:~Aº0¡èßH²ó¦ðM‘4U„c'¼ŽžÅ[ÌÏë0û7h§¿ÄƒiYêH³#%ÂX½šåûÊ¥K 4Ü!Ïì',eh,ÇS AeÔY%ƒåÇ’ òIàÃ?\É”·æ2ÅĆGíxÐ6ꉇ0DB.ÄzX‡/I>©Ù@Ì—ÅèäQ¨ŠE8´¯¢¹$*ǼÈã®ÉÁ…<‹ÔfȲ£³Ù`Ëæ€.¼(›Lµ˜ÚÖÊXDè(&Pûù=ôÐC-òéOº“T'-z½|hÙŽ™c¨![f‘q1¢ªÈË€fG¥¾ñÆj4fmXô£©¼/.ƒ×^ÖÍ„r³ÙND ˜n$ý›nPçD‰@"Ðß KÇg>ó>ˆÃÕDHÒè'2~žQªú!X‘Òƒz ¸qå…¼Nt4äTj€FZE[ØÈ¹çžÛj¯{æ™gä6cÆŒé8hzЉ-«Ž”òÆ‚K?ÃñãÇÃdèoÖ åä!% ÚX MÀ^à­+4Õ$j«˜¬Üjò¬GYôc§2G(€¦36d¤Ô² ]‘±pkšÊ¦0=hdÒr²~À7‹&;ÅY·¹l–j\\Aš‘g³Û7¤>ÖÍ”éP ȨâXxá…=kV…5ä‚ Û˜±ÙภtÀØ|Ê Í=«~NjРA¶ù¥6#ö½¸¥Á^ô+Ö?¬Ô‰uT1|q nôe»üòË`Sð~VÙe›KÊžD Hÿ ’þý_ š:D hAÀþõÖ[/ža“Šð‰Ïñ¼óÎëw3ÅB@@’¸Ðc2:Ò0¯¦d†[4“.R¢*˜ž“e ¶,#²¶Ûn»˜¥ÙÀÒ(¯sÎ9ǧP rÒ8™Bðîƒ0`…ÃîÄ\GydS‰=ˆYV»¹LHoƒeÒ]S“ ²@êvÈÓ˜ºÔÌ2d;LJ*ÌJ4$Ü Káªéé»ßý®ÈÐl%·p3‚…î¶Ûnâ/·tv¤ÆºéL¶ÁpzÒdÊbG4ôÕd³l9’;Tv„a­´¦†@4‚²bp#Sd<ŠØ:K«í1Ä8tß ÅN^ŒˆÉäoš&hÞRhª@y"Ši¾¶Ÿ××’T±ÞbëS'/°=þiniéõð(þÒÑmüŒeg#HDà#D éßGfªJD 'ærô³».”¸/r‹w%‘cöó‹S_¨ÐòCç¯~õ«*‡n„ANùÐ Ÿæ|Í[nŽ~ºÆ'8”’à·˜¥ÙÀŠˆ<6¢^ Zpt µ°¯ðBlª2W ©kn¿ýöæSro†)ouÖa2äO<±)\ö0pþùçw×X“n¸á†òi³ ‘FÿÿgïÞ‚·oëùÿßÿÿ¶vìýÆ–µ îcÆYdZ0FTB “!BR¡%IHh“$)¦¢$Bƪ…URb²h™f3¶Íô{Ü÷³Þ÷ç<¿g—\ñs}ïÆyçñ9–ïãø\ó~¯÷B{+aÈê¯h:*K~ÚE –ž¾‰À¿ËiÄâµ·ÛiR_mówTcÆ“a*Ö:]ƒ\k9Ö-é´Y5E…u1.Á:zÀ5p ÖjyN³Ÿ %£ƒ§ÆÁNÓ&P>~Êó'Ká;ßùξºsÖ•1“µáXˆ –3°¯^Š«¶³ë·¶¶¶>BØðï#$Ø=ì–À–À–À%Ò‹Ê«†¡3|êSŸš6ìó oxCÝ@/–ÕSÄ#ú¦ãøøå §ò‡Už¿´3¯³5n‡äx\ÙI)À†òš ™'Mî¨(ËBËcƒŠx\MLë%Z°?Èäì UÎ0lüAAøŒ…šX“æŸkÊWQy“2Ç:'÷B¦³ipº<•A/¨Fð•0 Þõrz÷Ʊ…$ãÓÖrSó™Åe9åc×asÞƒå.»Õ•±ºV{ÀùrÄ{ éMì–¹?î[?:8Vm\K‚RÈTxòònmü>‰ºõ3þ´à|;‹xtA8ëÖvyK`K`K`KàfI`ÿ›%É=ΖÀ–À–Ày àâRñ³âƒ”Þþö·OLúq”—¹Bªw5‘TO~ò“S£#ý”óyS6¦¸Ž÷a-p™“Q½®D¬ *‹Ë@Òì%?8}:5Q‘Ö&;Ül„ƒâŸþéŸNC….›#Öþ…/|apÂÚ„½`˜ú®w½+$ÆWMæ”n! årjEÉ.´ 5ˆ¬ClL_c·48˜Jpö¥"Ódb¸ýöÛ-Oc掳ÍÓ‚Å1Uc›²µÓ6kËPÖõÁN"‚fi_¼õJÖÕwí¾–qƒAV×c“ÓÆ¡;z ëÏ•8-Ùm‰Ö¸ð- Ü,Ùú*¸' òC?ôC ìuôsÀ|t ï €ÄÔ#&¸‘Ø—ïÛ,i¶¶¶¶nŠ6ü»)b܃l l l œ—€´r1'TöŸú©Ÿ¢õâs2~ƒYu‹2º²ˆ yñÁxk¶‰Äøó?ÿóá¥5÷·qþìÏþìì"d«}9ôÀ³ág¨éÖƒÎBåxqÔÈ,B‹;*ùÁ:cEñf‚…pEt4{•Aæôµ©Ï£õ(•¨° 5aS¬hÖ"ãÄàŠS?:rŽ\E9ÊPo(´[ë¹ÿýï_òHÂ×Xà–¨-_­ç”@ÕÅvÚàJušÎR-زõµQi_mÐ6}½ðÇn°,úÃ?üÃgEg :‚ ¦¹¿ÿû¿ª핯|¥ÉÍ78RÎÑ—±0Sãªl™ê‹ü4;d¯‹wõõôOÒ¶?}qÔ~òŸÆ-U™Å2>3JÐ r6ûâqíG[[[[¶6üû°E·;n l l |h '|jʨ®,¯CTÿ¨“A€\­†šÓòk¾ækÂQÊ’Î…5F‚©ñ÷˜Ç<¦‚Ï«(vwQ…ôþu¹¢˜ˆÈŸoÌ‚õ‹Ð¸²jk{e RÜ1TgÁ ¥( Œåø1Æ™¯Dl~Ã7|ÃT hÒdbU&²ÆdÜXº¹i?”i°™4Æ& vê”8]*h ÙHÒL’ºCK_¹É¯U³¡(-2Z¶ÅÛÂt´5#_–†h+“wNæN÷CØ£„ã8<¯ÏyÎsLj…X’tÐŽûºÆ•ÐÌõ8x 1j6I)ýúP>gÙø©"N›ZÉZOZ•áán]Y@ÖdÖ¿ [[[[9 lø÷‘“íyK`KàºKOB`# ì=8£;0&8WÌŒttló =H³ÃŸ”…9aý˜ë%\§%¾h…«Ü1?ð—;«â ddÆYifj& k÷)ÃHy‚ê‹‹ö1,kÆS¦®1±g%0à¡¡†r?COá Ó‚=ë«6‹T€†t*1Ýúôª²\ä!×¹²Ž§]ˆº3BëIÁØD®Zð!¶Vös›=†žÂMê”zØÃ¦€ ;m9Ë ü¼¡ÐS¹ÕÌ!&=£¹ZŽølÄW¢¡\’-HÌÐÍ4Ž¿D¤qi «œO·öÕo ©¯ÊÓFñYXnîFäªWð^“|,×ÙwyK`K`K`Kà#! ÿ>RÝcn l l Ü¡÷æg~& xx9žf£+Ë–_=˜ŠˇyÓ«Ü}ø¨ uùœÏùœP ¿©¬4)îÛ„|<+qæº'o}ë[Ï6PùŽw¼# .®¤iæ4†JQ%­Sû«†zÈCb:¾g<Í„÷Èö’¥â‹^ô¢Paõ Fþœ%M?D%è÷‚ gK5‘«Ú·ûœæªÕVovÔ0y–º¹ µÖËvÊfÞDº[ªŸËq\¶lã3¯ @¹&’9CÊR±‡EÔ¼ gº0§çîàŸÛ‘O¼ñÙUUéb„]³=m¹†¥•SÄ€ƒ“•9:ö«„òÿøë^P"†£­Jët½“[e ûØÇª7éÌ•ü½,‡K2 vaK`K`K`Kà&J`ÿ›(Ì=Ô–À–À–À% ß75—EÜŠ 2{#ð8%ãFñ+z a‚'驯kEØ/徤ážú;›>ÙiöÜç>÷ƒËºk ÿSª9ˉ;ÂnðéOz1'õ˜Ä“’þ®ÃÜñíq{œ•@\よíl gÝÔ›(óW‘N¯"-ŸÛ€ K#OçîpÙ"2a}ãß8‘N,,d5-§'FÐZêõ$YŽrC]ð¯ÓÒb·vÍ wÆ<[°M›ÕÒÆm_L—ÏøŒÏðÕ¼ôÿðÓKË‚sŠJºž™D?Lâ‡åÈH©aå¯üʯ„f/çQt=tqUÎ:àáè†M*¯k¬J“åj3BzÀg_ý´×íb»ÞÔWAek?¡ƒ¼ ù¸zeÛÜ_·¶¶¶nº6ü»é"Ýn l l Üá&—¯šÜw#ÌOН¤´aÜV„ ýç9†Š)ð »PqSjìS.òþMÂ(úˆ”S…q zÝç>÷™œ„a4—­Éµðæ7¿9G=¶Jrp}ÎèÓAÔ<ïyÏÓNàÐxh@ËPÙ/×/fIÆÂ.ÛX‚ Í+4ËHˆ:ÃSÀÏDD’“|Üâ*¢ÂLå,ïOþäOògƒUà̩ײã0àGtX@XΖ3žtŽâ…Nƒ³›µe]@²Ø0³3:=m u ¹Y;8´tF#"d§±©jŸC¬±ë§†Îndla.Ì)ë'‰‚ÐgþŠ9fsôƒ“1C¶9¦’¤ëí’·e&©]C1cXÞ:]Œ ³Ú]ØØØØø¯H`ÿÿŠôvß---óð€‚‹òZíÙîyÏ{¦@!£¤sÛ QÔÊÒ…3ž|Õ«^¥e3ݺ˜1a¡øœÆéÓ°§‹àöæ)`sA‚X´A-ž¦1Àžµ$ 21]£³¦{õ«_·s{C§ü"‘ D¹lɉ¹Ê(M&ùÙŸýÙL%mʌ뀨R¹ÇÖì ½ Ý䇘^â‚Zär?pê+¤´ †= 4S[±XŒ%i_W52ò5¬Tƒ”Ê^€vÌ€#„ï}ï{ŸbûYmƱ‰Ô™ƒ›§beAͳ°¹6.I›Z½% …¦žÁ?^ÉjD]›pW‰H}•–d —\M6ÆYçÆK?ãÏh^/‹67’)¤.ûsK`K`K`KàÓÀ†žÜv¯---+%ÀhtÞVÊž¡ÝŽöŸa'ËÆ°GȰP.òh—&^{ìë 5™çÕ©:‡À«ro^XË.dœCeW96x§ûA奲[ÚºfãЂ^,§òl½âXrrô:k°ZG0)¬4®C½óïkÒñµå2™|Ý×}ÝY¤Ä"QFx[Ð Þ`ïç©Æ §;jFC°^¦P ­·TZ†ÅÌÚ°µaõ z9*üõ_ÿ5Ä«£¿Þðš;%$×^6d"kµk}eËvѪÆtL§¼ë´,]¡¨<§äÞŒìªØ±Ëч‹›4nr×ïε¿ÿ·€ÎHµNPPm\]h¿±¾öÛ„K>Gß/‚Üz N!—ÖLwÙæv¼ [[[[ž6üûðä¶{m l l \)Ôå5š"µ;{BdNjtwüãeŒ )ÄŠ–K¥Ü_]ê~úI½~øÃSa®Ê>ƒŸ.ôßþíß"—Dæ<}:5"Fš3 €ØlPËÀHž³eÆQàÊX,JóF4ÂÃXÉÚLª ]~sxä+šh"m*+D°á{¿÷{OO^OxÂ6ô²J`0 ®*6\g¢$ŸXÀJêÖ—ž3"®C‚ $'`ì‡ù1„¨„éB,WM­" Z¯œá;Ñp4új(è«6®\ÕF}Y‚g;¡•dnc“öÉ6µ<„w»Ûݪ©Ø,a5xÆmT“ݯ°&_~Qj½>¹m l l l ü%°áßQ€»û–À–À–À]$Pè Hƒï§gE×-MlIºoê2]y,3üà§.cEôõ7ÍòñȤdk5›O-Ålä»Uõ)úŸöiŸv•gè"·–|´d±»Ëê—/ßÿý߯ Z)ÓПû¹ŸËäO¥°(ÈL|cˆŽ–v›Á´ŒØ¼ûÝïïÁ?`g`ÉjÁ°ASìQƒ[BGN÷Ó‚È¢Ái‹W¬í´Íi1ÜÔ«Oæi˵ÆàƒˆLjêõéZƒU0&„ì2„ÓÌ ž•o]B ‹'–•¶]G«ük¿ök JJzR2EámôumÜEÒ:²Óª‘wÞ.,Cä•Ó6F ²5¬?÷“‡Þz7 ½­Ÿ¶†©sñ’j¸ÚW(Äî!â±é\õÀa¹¿âæg‹XP/‹WF_•ävºÈ]³%°%°%°%pS$°áßMãdK`K`Kà “8°$E9#¤d|ËÃtßô`!õGj±Tá,ò‘Æ„‹ß¨ŒÇ“ÑaT+å³?û³=5àitÍi ù Ãu¼ä%/™G§û¾R2n÷˜Ï˜‰„W±Çµ†-Ó5À¹8XÜßüÍßL}ƒL¤ƒGoš®h¨0b?t|ÃÞ°YXÍËÔ(±$m‚: J¤C†·a$Qd“ÎWG–´ÇÊtU êŒQ'‘°¨LF'ÁcÍ\!ãÅ98ž»äÑü¹–` Ù8y ãO{6ƳŒ€÷<2N¹Ñ}^–rEôúx•¼PÓq¶¶¶¶n–6ü»Y’Üãl l \w |ó73uV\ûßÃ$2a §1Š/w¿ûÝO„ JÿY‘ï_ŠþÛßþö—¿üåÔ…v.¤d:wÉÖ*ûÅ]T_ió«ÒÌÌOx•ð’§)ýǯvƒë±Q÷ ­Ie!s}4å§=íi†ŠœDI0$'½_BóZêÞP–´Éœq*Àb¥H¯6,ˆ‡–¾¢ C ±òFH•‘³™Í€Ô4aÚÎéä%oF”QÏtº'1vêLZ°˰˜õ> *{ì¢ 4&_OdíRÙÂJMA\#sb,ù»Ñˆ—9L汉[»ÊUw;ÕÅPƒÜfF$0¶s~>0©že&-¸%­¡b†4þüÅÇ"‚‹.­Gd^`Oåœýô઻ðsÿùË5Ÿ´Ù¯¹½>^"}½P³ø]ØØØØ¸YØðïfIr³%°%p­%@ñ èÖp –ò*È!¢ªQ™g°Òy{:zpÌIÚ0ˆ­y*ßiR—•ÙF*—òN*û££'ôßû½ß3 ‰ÎÚaB˜NHö°‡ N[-­ÝÊma­Ÿ2ÒÌRýÑæ§§$d‘?,ì+¿ò+!8LˆSâ´< Ð’+“-di¶ ö‚ p«c.‹Æ—D§à"_ú¥_šdäŸ$ 2ì­QLÖ5 æøÀÖ…{kL’ï«¡&0ÏÚÅÔe±Ó¬p£jਈPaŽ4…½3ƒdlyÖðu†%¢°"¡¶"%ر …îJŽ'®48Ýׂƒ+º¦£¬Àc–oiXRÖ K=%lg(ɸT®V•#ÛÖÖþ?ÿçÿxZ<[5fO€®q53ÝëèæÔL‚ë/zÑ‹äÕP‰ìè â%2»Êk5KÚ…----›" ÿnŠ÷ [[×]iÀøŸ‘S–{ø"Z/m›/57¼§,øaŠx ±OêoL=º~Ì ÿ(à­6_ð_0@Ó±ô£…ûz!óÞ/ýÒ/i@¥fd8º¸2¿¬I|—ã¢å­é ×sµªøC+_ë+[Û¿œÔw°ä«¿ú«­J—Õ,Ú!FKõˆ©*À)„i‘QU²ƒ=z­ùéŸþé¬:u‡|]Ÿž- 9£¥öíÎÙ}È^–Ñ …x ’éΙíoÿöo×) ™FüØå,5øÜPdB˜§6œ8Æÿøo®9¸uFå?ú£?êŽI 餆µ3>DmIÚHªa?ðÐ}¾ºNÚ¸Z-C,P_ç/;g_ýŽ0x•k¥K¢Ò5ËM{°ñEÕt±cËÀ÷¾÷½¡D¿d`쵚õì–À–À–À–ÀM‘À†7EŒ{--k-RºSy'Ò&$â Š”ó@ÖŒ‡Ê€bÞw¢cµ÷ Šà—Ê“æ+C¸´ùÊ5‹E)c{ñ<¸™5äs00IÔé«´! ïdm¢ ÐUIêŒÄ’]8lÎlùÚÜÖpP§V—ºƒ.q›Ôý?øƒ?8ÿS ã4/Ìp!\ÍŒ gÀx$ê c\ÜeºhI§ö„ð’‹Î‡Ó‹½hÓ L}á _8Ö Y§ &pBn® ”‰ˆ æ&àWIDzZó, jÀg>ó™fœ?TÀ˜Uäjý¢/ú¢«,]'—Ê .˜1ùC΀ ƒ±!·I[B­#–Úk]cƒäUåëu(< L˜³öì?5óçUê§/W5ûsK`K`K`Kà¦H`ÿ›"Æ=È–À–Àõ•M:ß'öH¡œ ÔÙÀžx-úŠŠ‰d“¢]ã2¼«-<èÍêý NS!î¨úÀ“B“ú ]•Å‹X0’ Jg…À€l„åøÀx·1 ke‰&]9´$ ÷m²w-æ J éôîw¿{d™5¬­]Å4ÖXªŒH6+„1Æ r†:„$­ý=ïyÏ'=éI±yè² ³xŸFø%-0ÂÙÄîë\€hŒk¢3‚ (kƒµÌ•.¬Å,ó–“X§u±Ô‰ñ*Tfä7¿ùÍ]˜5|3Š; ­¥1y!ž=2gúÑýÑÖÏ»TaïîvŽ”s[¦Ã9ðµúN¿u¢¾UJôWt" áðÕÄ®£GÙ¬2RÑu·½\WñœÓr¶¶¶¶n\þݸ¬vË---3`bGµ=(©å@£õ¦þN¶nžN¯yÍk´‡OXÞ+Ǻzöx¬éÆMMéÖÓ^%½\ð’4ò_üÅ_<³²;«B˜–w•·§»ÙD)ÑW…è*™!:î*':SÁ0­AŒ&O€.mØžVn¢× t8"áö–óªe« ŠSŠ>j¬×U;µ;þ~f‡|€¢° 1è Šˆžrˆ†â«ÊPŠfë …Xuž Ù‚Á Ò›‹íî¯ÿú¯7…¥ZðUÛ™]c&ËÃAÒñÙAå$Itaž…mMÁ\¶Ë©ªq.¨àVbaèbçží1„ÕÑ‹_ ëÚïZ•À h.ÛáÇ‚¤ísýƒë\à"ǨïbG_·a<7ß‹à)Üè°D¬)H¬‰†BŸV¼bW v×o l l l üg%°áßVb»ý–À–À–À%À´/z ˜Z¾jÅçŒÕåò›bÔràè»ðØÄ&ðãmo{›Je½‹C›t=šÏ†E¤dMjœ™úPˆid§Çtóð¨¯OqG_ßò-ß2L#5]\NYTðiêk¢ÂÓÑrÖâ´v0æ´_PªÅ3à œ8djÄÌN†?ÅàqD.%®i©01<Å!V'\„^ =ò¢œ¬ %óHƒÖ:Êìq¶Aµær¬Œ*C2ìy²Ï,Ï"-Ì‚-{]ØZ6Er€îä?È­ò@ `{WÒuœ)Ã{&Þ¸€ÆãùJ°rŠ„  ¾ ñU\âüÈèåÜ×,3E….ªË¼O˜zþäp·lë‘HÃ5î©“*P¬¬§º{ üe$Œâ‹XNæz1 8 5Žõlži¶ [[[[7. ÿn\V»å–À–À–ÀQ)Ä"ò¯†Ê£¹B ²i§é–ÿš"›F{ƒî`bÇB9¦²#"…À-Ó›˜©é/õq÷qWe–]2ºûùŸÿùuyk9î…æõÄR”â>$œÙÐnW—6eU6ûÚ×¾vÊìÁ¶VðlDˆÍÓCj c/œÐSXKD–ÂE¢ì˜×FjI*P(Ë.qùa4_…¯,¸ÁÜ_V9‘-×^68$À<üöoÿv6¢¶€©;Dò´ÈF_‡ZË™æ˜àjbYL™Ù;•‡‚t‘-²Ñ•7u¥mµw¬ÁªS3ÑF#ÕBzÞ~ûí‡\3 æš™å”ýë·÷ÖSäð×ý×+ûéhªtç s=%óFöÈW¯Æ8þEEz/Ös,ÝñÎzvaK`K`K`Kà¿" ÿþ+ÒÛ}·¶®µPRP mu8%â@‘ÕBwÂZð{6 ÌŠå¸féC“(sx£ʧ|ŠÏþä‘Ã~àÛ]þ¥sCwk‚„ŽÄ2BnBqTsú)Ɖ±@©Ó$æj¤©Hƒ×†Þ_ÎÓAÔð²ÓV¼`C/…*C5Úrâg •ÐÔgsÓÃÆ kƒÄ@éd#Dt mrºN æ?_Už¶¬×¢Ó>Ÿ@…Oú¤O: ëbÁIÌæ¬{d›6vmÄ~*Ô‹0£y‰·Aú4²Gñ³— ¯£ÌÀõª6.O”£r¨²§.Ø„á™+ˆõRÚz_í]Ü ×ÛïlÛ8.ùtdç9ÉiÐ R\–±…½ï}ï;+ñry•¼h—M…§ý.l l l l \–À†—峟n l l \)’S¯©±5M-¡òÊ‹mÛÀp DD›ø1² ×d„X¦ˆ»”fzc+8j´B½4fbúŠW¼‚Úm”Nv†Ø!0æì8ª…Ä zÚ&û=:}ÜšYÀ-çsœ©¡G†Ø¡§ƒT#ÊHð|ì ™4t¬d‡³‚ß‚s¶m^5šzÄWùÍMm…˜À çŠuö¢p•á´w4ƒÍâdÜÚ´œ‚ewp6ÕÅ3¶Z·ýB_±ˆYciÈ¢¯X8rà– 1–„ßËéÑ5+©Ã,f %qÜWy'þÅ_üEæ¦s\$×É¥š{8—-™ûôg ™öœ ÈÙrõr½IrÒc_4 +d®•KoØ2”´’ÜGkéó´Ø¡Ów¶¶¶¶>< lø÷áÉm÷ÚØ¸îÈ\û±þ1­7êƒÎZžw~Sù‰%µ¹ä ö¿ú«¿ê3Úg ™ÏêýE|)È-!!}]ôüQÍ¡Ñ;sÄ¥œMº` ò ”g|’ƒŽ“]Ñã!ìMÑ_Ì‹Š1~±4SýÍ.ZÌa„ùJ&ñ¶³zpQôÞ±¥ÛnÓ@ÒcŃ÷à 5…7¼á iã0’-Ù‚N›µàŒÊ1h.rówç´·©\oí"%zg§e€ܺq§¯Ågoi;6ÕÞ b³¶<Ížüä'«t¬y{Ný¡@°íñó>ïó²´Ô‹Á$/ÄÙ/j± HÚS¸´Žýª”yºxŒW]¡ÁÉf7¸kæ²Ü¥ô÷ú׿¾Bœ¶uú:à\9ßTW½Øò\K/Eþ«#j¿Œp:ÕK¢ˆÛÏXʩ܅----Oþ}xrÛ½¶¶®µ(Üå`8ä/—]öœ)Ê¿ù›¿9òÊ÷‚+–‰Jö‡©ÚùGÝë^÷òȈ˜•W¤Ä*}¦CSÓ×T 9xà*øº×½ñ2SW°ø(tÊY: ´ ðpb\ûŠ×/©Ààωî˜wÜÚrÊ¢€âߌgú«ð$mPüÌÕ’vÆY ðR†µ"[Ÿ¿Y2m^ö²—­-+sÛk $†‚}ªjžNúZA:hEËÓL9åxâ” @e¤Š}š@špŽ0$§`ƒ­­£ùš ªšŠÈÖì¹ð­þ„|2;\lØÙý6¸ÅÇhu¶v€—ë¿–÷”§§ \{d1+ˆšä Sá™’æ'©¦ˆLÖÆ¢A<YŠ[)<²k\ È— ÅÕ°µ=íiOûÿøC_UzÔâÀ-xu0äÚ‹Ñi[†ÌOcÀXð\6b;6Õ˜V®ã¬eáRBw~ô£ÕiXÁ~ Ð׎ìX’ÑÞW7ê,‘Ø€\ø2uÊëSvèQs¸ ¡AÃúsî. ¤ ÅùŠäœ^kÁÅ»³ù]>2×T…Œ])hnŠd¢žÐ\§Ž£žnË ;ÝÇâtF‡´_ýêW翚Ç,ãê«ìWgÀ]ØØØØ¸ ÿ.g?ÚØØ8#škê)óNØf<²Fgå…Ç ÍWÃ}®Q€ÀC¶m Dm赌§ö t,•‚§‚Ç£eƇÎ,ëQgXÐ !©ÄšNk9ÁT˜–|üâÐhðW%“ÐÌÖ‚+æ+$ôÈ8ƒAPðŒÊÄ"jÿY[Dý^­‰°ÒøËI¦ŒE+á‹Ø€‚‹\HÈrš62L¾2ŸÍ{ø$¢É›Çêu…뇖¾ aZÚzBøÑýÑX¤¥6£ÅÛB»¶©ð¶G6{:Z5\ óöæ¬9òáWCM7ŽyÁUC1Ðm¨ƒáníº£o}º® Ì€rN™dˆžÊµ0Á`fœP®¯.-¿i`pƒm®z'Øëc ¥+Š{†‚{»KO|âU:—xcJïdWè¸.l—·¶¶¶nDþ݈”v›---÷KÁE/Ôò›BqÄ™Œ»øàò¤h1áóIñÍJ°|€9ªÒ°å©ó•£”Ïõ/¸x8’ ç,l ™8màç?ÿùÒ Uøœç<'.Å.Šºi«aÞŒP kï™óÍSÞ‰HªBj:ìwÖ·P/õá`f¢MÎhØBœg<ª¥‡ g¦åiaHÂö•]€‹ºã»Fòu×¼s§SƒÉöˆ'”¾¯ŽlÙ§T§ ˜mù¬@¬auðaEºóÓyÕ»¹Á!»ýÚ˜A²µù›è©ŽØAÇ ·TŸ.ƒ+ê^»+sùÓÀvÎ&{<å¢óÙÓ¥œ~‚Ç4 ÃW£•ßo¸;&µÖVƒ>ëʘI¨VÆæõWæÃr’x¬ûÊ3¿¿n l l l \–À†—峟n l l |PtåÔtÚ?ú‚ÚZîò óòMºÏ}î#Lÿm£Î"stQ6³)ôô*!°o4ƒ7 pµ¦SéO°“®éΣ»Œú® "¤q£¾_ù•_)Ú¾ÏÚ«± f:=ÿBä^m;‡Lt³˜Õ×ÎÈvô =èo|ã4¨ ¶Jš Úéáé|/„{db1Éœ5Xö ì?@¢opXbÊxšež³œÜ†Fö‡z…(ï¬O b¨uäÓ2Ö]{‰ì/d´ÍfãÝꇀ1‰$Û¼L™F‹i|•‡¤¥† Œæ¢:Ünšu:ô¹¾ºgý?Ýó’­ ÈyJÞ¦¾ó7¿)äÀi§àq ý´ùßø ?ŽœZuÖdd[ØÒÞ,œ93Ѭ|ûáðà+žP”&'IÏ×ÓcÚ5[[[[ løwÈþº%°%°%p¥^øÂR=Ù§ÁE ‘§YÚ+” u@IDAT z:Ãõ¥ëß÷}Ÿöôãi*÷ÇìsÂc€Žæ“¾G,â|½ÿýïïkÀoLéÞßù¶Ûžoè&ØÒÓ>ðg—NŸÝ ›1Æ„ Ø!‘9ÆÚв{ãŽÎ2l¬ Óï©ìggQ)S\L"qµµ>~ „RN’Â\6Ë|Ó›ÞjÞ„€ö‘“àoÿöoO@ì€w"bÄ’«@T’À@8y„sfg1ªÏ@³N4”Ï¡`QX}Z“×ÓU­56[„Û[ŒÉ7ÒDÖæª ¾l¹JŒâ)^Ñ^ÄâP¦r ŽÏ!NæI-±ƒvÜëépÅÅ é;r(Á Ne…ˆ8Ãö7ð/çUW0†{š|>Ðö6öŸÐ#˜Ýï#~ 1f(Ÿ©™CñúðÛÔ‘Tc¤H 0KŽ‘^ÆÃÂö×----‘À†7"¥ÝfK`K`Kà}£w¾ä%/!mõ‘|¤0˜ á“ò „åèÁš¥ñÇØhF¯¥r´ð¯ýÚ¯ýÀ·Û8 ‚s!“ûÞ÷¾S¿Qüà{ŠDºÊa¯õXÉÙ˜rW4øpD‰µ£ŽZàS·³·º ÈšPaBÀ€q«SÀçô^E¾§Jp1A1Å4Î{ßû^SWƒ´£Õ.® B`/³¤]G‘u"²°«:%´l×@ÑY™ÄPv@£1MjêI‹±$)ì@Áhw#±uS¶åIÚ^ôܸ0¾s¹Ãù}áª$~ÄÁ¡ŒÛž©™ƒ<ô‚¸J/9˨`ã]N;:<êë[Þò–·zÚ¸~-þðYî>¹ÛÝî¶>‚ñD‘ínˆJÚ8=ÍŠV¢¾_=ZOÔb‡ÙFœ^Q3/ Žý 3«Ú…----”À†7(¨ÝlK`KàºK@\DJçX±ô•j;Ìž´iH$Z5ý›ÂJ{&²{ßûÞš¥F+ e˜z2Û‹ýK¯U?ÂɰZôõ‹¾è‹Ê"8*P£S‚;,–zpñ*7N_XR‰Ú§ø;¿ó;ñuHHcŒu̪,ùÃu-KQ®kWYÜ1b =Ç)~0íjŠ,ý­ßú­C³ùª}‰,›É_Q{ >=ä! HX6œÉêuÐÑÅrúZ¶€.å%OŒì/Oêb¨27jo ™.YY€e¬ðX툑 Ù:{™‚‹!àê}&‰3 Ö‚-Ñž2ůQ|Ö6v”ó¤£q@$6|²ãsˆŽÒb}™b"B×î•]‰°¢KrúTŒŽ¶¯ÔÓÀõKIòìg‹É–ÕìúfÛ™msCEèÕѪÒ+cñ¦³r—:Â,“¯ÂÞÃó^ÉYÕ.l l l l Ü 6ü»AAíf[[×Zÿðÿ@+¥›NÌ ýÀ‰Ïë_ÿz2ŠÊÃÈ%¯ÕQ ó“‘!“ºH^ÇHò¬Í[ãòO›5ï^+Bl‚| º ,ÍÀ•®`Œ¢¶¬±mì!×äufgLˆh: ÿxÁ…ÏF›ld¥6}¶.tz- šº*Yt„DJ€¦ñU¦Ù-Ûh++Åø©êºë³eÆëšÈ¤¦> šª¯¤ Ù[š…w匆Á{ö³Ÿ=|<ï BF#–i¹„ÒÉ–v:‹iíÈq¬žtdÎñÍPÜKæyD]CG—ÄU™^kÁÕÀUd›Z=’y¢Ôz4DÈ÷ÔMP?È öI Ð;ö´Ü§¯×¤Á£²½D¾2„ÖÚVÙ#¯¡é¼’^Ì:îÏ----”À†7(¨ÝlK`KàZK !X U ÃQRñˆGxDÅfPëßþö·×RTýÑq¡*ã÷Ô °ŒQ@×ôBÙáŒƒÏ TLMƒÏ'*G²Ñ­ç‘PÀFPä,¨ÓFüÉá6[€.°â[ßúÖª°7žæÄ8õS@Mò³À SØR_èźÒWhG{t\ FÍÅÎ ‡†pH?ƒX¤´ W™J*GM-qªŒ9s{óUX” ¡n hØU&̓ñ°ž¾ÂZ¶‰(¯†õœ#¢ñèËxØg¥sgd&Bn ×jîKøŽ ³Ó½?‹¼*X(ÌÇ|ŒfîðÙ‰ú…‚äÏò–ö›¸H ŽSb`ì3ßño5  ô+ £hõ¤—[¬å…êUºü³Ç {ã5©Ò‹ãðy•¬6–ÏÈ:Šº^Ô.€MÍh»°%°%°%°%p#ØðïF¤´Ûl l \k ÈbGûd`yàaÆ… ¥SjžWZ2YL^âg䓦RHÉ‚þC´ |ÿðÿ°feg×ÇÊ×Ó?“>ýéOÇ4>ÙÔ­æ‘sNâj‘$à C¸¼ ÐÛd½ŒEߺ5eÜK)°=«ÑàüÁ€Å&™U ̸b™¡ðW¡¿èÿS¿X0Jð3 ²ˆá:ãÚØ¾žñŒg™>á>¡ŒðÈ-1nuBË@,F\¤ð’7{6zÉyÀȰ©m–ožJu/>õvW˜Î¤*-À2,f]›²S`K¹F2½ ’XfE\‡}•á-†ðƒ˜ÆÖ\îA»ppS¿ü^€—³øCÂÆÚ¬?¼ûÝï¶ ÜÚA¯ùCÕjŒ2šµàBººjºÆ®´ Ö¼5zG…jóâW§»[T¥×ÇKäÔB³”†0јë¯½J^ÏÓ³X÷¾Ë[[[[«6ü[¥±Ë[[[G 4TLËŠI09«–_¢v0FK„XC¨ô5ÖBá×~í×ÔÓSóè+}™z¬BåÆWhœ±”«f>1!Ô븚©T a£€„á ÝÉö¦ò¬9Ÿ• ¡á©YÎb6‹dŠÙ"qƒgY)Œ¹r­k]ÖA!:Û>+¾ØB–œ ,¹ÌÄB­\@È5¶Š1@ÊKá)q}Ó7}“¤yM4Ÿ¼×ÆÈs¸&ìèŸÿùŸO›Ó‚ð'#mô×X¥êÔΤ¦1ZÌØdBn(ÙÉÿ¡±È á»|ÿNçÂ!"û"ó±&FÂ$ÒUÂÀ2í,ÔqLeÏ3H–´3þPm1ic„< a?a¬8­yq×òBqö`6•.•`§îØ\ìu…Ê„i¬ìJ›bÛ’›©Õ[d»xík_ë«¿DÚ{¤K¬²J¹4!Úô{ÄSŸúÔY¶W²;æ%Ê]ØØØØ¸, ÿ.Ëg?ÝØ¸ÖÀNÄPWAЕïÔZßÿ!¡0*TX¨#åNˆ´¡éÖè™Ï|¦R=Jmí8ç‘‚} ÃŒ]èûç¸óøìµ$&‹Ôk¹PRiϵŒ£èK\¾fhýÌ5CÎ\Š(úœN3ƒPÜ Ê-`̸Ò}ÅW|Åp•H¶‚8É‚Ê$³Øf?|b™Ú&†m…=cŸ¹Z„9šKìÐ%f"v¢†Äi ŒXnç0õú˜am4"à Æ^Ê`‰‘A&»ÐA›~–"cÍ+ZI‹áÈàö0Î|%"‚2 ¡£¯þ–$¢È5Ô×ßþíßžŽkÁa5ˆãsˆë£)÷[䯔ÔÅ=òQSGbMÊ÷G~äGX~NG?”‰„+ 9Åkßç 2 Ü•Ök:Ͱ©¡ý ¨—£NVÁ{ª÷õ³ˆîõ´åsbšù¨7â*ÏÏu »¼%°%°%°%@þík°%°%°%p¥ʯMãDæ0J¤ÈR²qx#ÆÒ_‚´[cú"ì4ʶ-cÈÂñÙòš×¼F/ b´dÚÿ Š%ž6ÝšãßÊQˆáNS³°†2µÄÙìN¹«A#0ª§ý_… DÛlÇÆk•<™Eë‡`Ÿä¯Ã@>›FðÆÑkâ"Òš}Í×|ÍʬjÆœÕ,)ýÚLJyíWÕP•4®q¤ˆÕ<>ëz§ HãQ&Z0®Ñ;ùŠ.[©94²€Ó–Ɖմ¶5dÎÚ’ˆ*HivÄH˜D:ì«ã.؉‹Á®uí>eG–©¤C<›1Â(a  ¶ÒwÆÌØ…«fw3Ϧ aÉÙ•ö£C¯Æ‹}?w×Ñôh®Y?vt ½2^ËöRhV‹^7¼íÄ`硊jî•Éç0S^/£WÒ-êÔ¬gä° [[[[$°áßáìG[[×Z¬òV]6M×gÎZ Ðïþîï–ÀWDV¦¢qP¯ý¥‹ü“FÀ‹ë p¿„}]@›gf„s”SÐƨÉg8?NÏF0Œ +Ý:ú›uŠÇ¸BLJrŸE«…©Ux¬,Ów íÂt#:bÁȉK'¦¥ufI(Êé{( °ÈaÐ/icÆXšùÊ]-a^rŽ0´Gð`ç0Z(¯µ¯¯…ÖÔK3u<;5•X¸Ó`* š4èƒ35ó‡f¼Yö&XG°¦è b!œazGhĸ.µ2°šU¤‹q?éàºWfÇNFªøj ݲ]-qGy±®uýrá"µ ÃiaÑÆ)€gè.ËÏ62»òå¿%ؼýÖÀ»²{묽5^sy•F,ÏzÖ³ˆ×£¦;ûiÆ 6ƧÂÜ5[[[×Vþ]Û£ßߨø( "]SäFá1ÐÄrÐ>GeGP e†@E’næšÚÐz‹Ï‘îË“**Cn´Ö1¨­”•]ðO®‚uFãœefhè·Œ±ù¤C³²Æ-­¡¬áþ÷¿?S@1! `šXwTX ö VyE€¤Äó0<<õ¡$ ¦ËÈŽ£’gäƒÃ¡"ó¯ãÊ…À9§,ë$&†Á™AN°Gtª8†YÆ:&QXLncön=úú‹†Uã‘#1}@9ÓùŽ'–E"äÉ#q1ZÀ`Çj( >k.kL§SF;Íþð‡ËzO3YqšÅ„só°—öU HéùÏþºÓ);¾øL˜Öѯ¤¢Ùýv`äX5 ö”$$Š{ÜãZºV¶)âzº¾òg¨±‹îöÜd~€u±e üE÷’‡Ü\f½ jù=­Þëã%ò*Åø©?~¶¯™Ñ¼˜^Ï;†¾í6/ìa¶¶¶¶®’À†WIf×o l \k ä‰S‡w ÆÁ$TUv•C.¥€Î'å8µ›ÒOOÛö4.ë³>ë³` НšbZ¦¯ø–ZªLcž§WÑe5D䔸û»¿û»ÜEaiy0…sª6¢ûYÎG… ävÖ‚‘çU(ˆ¬òøšÛc§ß÷}ßWŠÙM›Cáu¯{]Ô%9`~¨@¯= B¤Â¥,MMtg¾b·¸™0°¯*WâkÚ+ðÇ{ÈCÒñ„1„8>^—gí-Ï"ãÄ,Ûâ×qÖ2G»¤] áVN€Ä¨‘žui+â¥xÈu|?ö±=\KëwÄbÉLTH;Ǿt†r…ŠFCàS¹Ä¿iý}ƒiÕ°ÃLnÊ]§ù%Åo"‚Žœ£Ù–Çâwß»`GL@ƒ—ý,Ò ^Ì:öªîÞºæ]ÞØØØHþí›°%°%°%p”ä?ñ) ¨ªT[Œ‡FH‰ÔVÚí„N‰4K¸!MúUyUÐRëÅŽ±6X8#}EafÂQfðÙÓ’¡—û¾÷Ѐÿò/ÿòðŒ)Ø|8.0€5 å~‚46>>J%Z bœq€ŠÚ M9O —ÐѲÜ[ŸV†7JTØD}â?Ù®kf‰a úÂ'¸µÃP :nr¨¿«x°µ/Ʋ¬³_ÏÒ˜Ó‹¸ÄóD‡N†Wu±Ô•ÅÛÂXºÍmó+\„Çf®C;‚=€ÃZ" ƒX %ãIhV€j@QˆT(ØËÃ$ÆF5”6Ö rµ¦r 0óÜOs…Êò“Ðúu#{T >ê£>J…ù[_©Tð*Mr|xS3õ(âÑo*£"ý6áUõÈk;—|V» [[[[«6ü[¥±Ë[[[wH  ì<£Xߥ_Ò­Õg“IËD°ÜÑî}ï“ÎÁ×à PWð}!XŠ‹ÈÿŠ›Ë“fëß°h <Æ(Áب’™ktkØlHÈfï“NlþN#‚ vŠIY?ÛJ)„ ¨pX'¢uÉ 2h‡´™|†ëì€ rÉÖ®*sÊÊ= T,” Æ1ÈÔ×SV|( Ø<ÂÁì5}4ü™9“ö¼Ëøa® ›² ¬xÀ¾ˆ}ê/{Q•i0+ðšs†&f‚>¿ç{¾ç,šÒÑ‚-»“µÛ±©6Þì¶lãBwâÕX?({ ½ZÀjâ=€j|#x¨7ì|º<س8V‡«ƒ> §Añ6‹±œ^Sp©ôu|§Y›5ø'ZOK ·À°ÊÉJáð§^³ß6ÓÖsùëòñ‰T¤Ò+V3al&ˆQ6®^Ï’j8-½¼§3Å.l l l l lø·ïÀ–À–À–À]$@S§D"ߦ¶<é)½ô¥/íQ€r_Æö´y•2ek6‘èÅÏH9V­1n„êoäï@Y­ì÷¬pV;…oüÆo4, w6""pÑÕ@îõé5ñ3f¹ZÍ 9=òà’“µE;W·9 -L‰ª#Tp–~4™Œod! =°A”E1Á3kOð 6žÂ2Io€ëDàp&ˆg°J¢’ìeì3-€iè)Æ{ÃÞP OëtCÐk#.Kµ`Ë>À[;Ëšê(M8‡HÛ€¿\‚²þuêÊ]~×ìÔ§Ô…œ%u4óõ´{MZ ÷H?‘Ôœ3dد Ñï 3]›î¡7«%±Èõhf\/œ¹Wøt#»fK`K`K`K lø·o–À–À–À% ® UàYÜ)u–?Ò´Î× ;„|HégIˆÐÝß?þã?Ö2ǼF€ÄŒ¬ êU¨Ã±dÎmا™W¢ÌPSÔ éû(Ðò"\ˆÐé‚9‹+·D"i€*ù³?û³ÙÑ –BtÜ~ûíô~iE7a²˜"¾®Š$3žsFÉŠÜH¥¼ŸGkáÅ/~qD Ä.´fÆ9ô޾Œý¾ó;¿3KQ Ô<›Ð˜ ¨•t(­Ð¡°{œx'ë¼S†=¤8ï­&M¶ñip(@PDQ{â…o1ri©u·x[°Û ÝÙ mÆé«CÌøVwî|U< DÑq¹•®Òf ÌÉí€'­6~UZ޳FžŽ¸;tGºpÚ,nþ©ý0ù´ —Íj]<×Ï%´ìn]ž2ì˜dººùe>ÍRÈÜ‹0WâÚY«·‘V…0µZ¯˜J'ÛÊíq͆²rÅ^.’´€³ŠN7¾k¶¶¶®¡6ü»†‡¾·¼%°%p¥B,|ÃÖ4àh.pb¬à¤Y§’bœ<Í?Ðןþ韆O†`ïõ—‚Ž«‰”“ У¥´U¦Ôš+ß0õówà”¦>jëE/zlÿä×Þ8C˜¬Û1oÄ£%ýáþáú¨2Õ<\*Äå)Ö‚''’Ç,C\Ä¤Ñø?Ã6’êªH˜¨žûÝï~ 0DŸ5ˆSAnƒ©4`$Z¸ÊïN/#€L;2pŠyÊ=‚¬¬ ÇPS8ûJÎ1Ôº¤ƒ”`û`[ë÷‰Œ²`˶øi|aÓ†“Þ£õ¨Æ±0ix-•~€»@ù [ž ßi¡r0Ì5˜)œH7Ðѯ6¨Ó&JÍtÌA«4ŽKåj A:›Âéµ”]Пkœ ßâM1/ãaØÌ¥ÀV› Œœ%9OÝ8^z¹¼bQ»^ºÖöÚ×¾vÖ0ޏ=òòzäEîëþÜØØØ8H`ÿƒ@ö×--ë+ŒMjëó@éš|–’ ¥€(r…ó"NŒ‰#?4Êë8¹•󖜣Էû·ù°¢5Ž ›5c„ƒ·üµæ©[D° ‰ª‚0׸)ÓÌ,¤¨3!ÄÔÙ(ˆåõ¦èŸå¸Ø 6;²"™6n Å’AòµÂõusï{ßûlTaÿê¯þêì}Éc˜1é´ *É<fÀ0ÒÖɾš›ò‘#Ì`¶–ÜɸòÖS9ü*ôÄ›A5d8ƒN §uÔ½é h„ñó´HmLmA©‘¼‚ôŒRn6eƒÃpN“žþÍßü 2 úE$ÎhŽÏ’÷¸ÇIL¿²yŒE“³#p3~W@T¦ÅùÂ8îXewòðÈWW…ϧ59 é"Ìga .ѹ„®¢5Œ§ß´qy"ëŒcØxÅž¦âõÕíòÉF·GO|âgIãRØj½\^1/š–^ºa/'¹{Ñ•|öòéÇH{FÞ…----Øðo_ƒ---;$ –}~t(Új(í„HO…Öà”fáõ5pÊJ¦´Òø+5¯xÅ+4($Ý]3$u?¢)å>×Aý ÎjnÎI‘†gß°¨!| Žk¬¯O‡¯“?ðø§ú§¸`2Nœ‹DBdRL,ë‘ÏÕ8Vwì Öˆ~_º‚Y€ !˜ŠÀ$yßaá°I5Àk‰ì¬AP±Ú »ˆÑ ÒÕ°“¡, „ØZ–ÚÃ-o~ó›×¡ ŒëpPä9tTÉÈðõ¯ýÚ¾²AJenÌXG™n¬sÕ[Œ%±,u AzRu¨ªÓmÓfõò' –†\ÓW¨— 1áN\+OQsá<õ£C¿ XÛ©«ž=œé¸ ®é¹gùNNÁÕ²)׬¤.žë7kc°zçžîòQKU’£¸Øs‡ èÚËLèQÀÞ°9 bü²Š)Úï_ò%_ïó¢õãˆWÏì^ÃaÎ6o\ ó «tK¯Jê8ëß…---k( ÿ®á¡ï-o l œ‘—Ê(PÇ!¤§ÓËßd5n$°&íÇw@¦ÔåѼ¤O•øºç<ç9Æ\A IÅôt%|¨'ää)õ¤ î ;j h™ç!G9°dvÖ—ÂNÃä4…O”Ý0B„|ŠQÍbð²2®›B—5dmäÿ÷ÿŽïøŽÀy½ m+<µ•(аë,ʦ¶€ÑÙòg«%÷<gGY& .j¥Ytg }|«È]¨m`æÓSІð¶àP±®ìjAnYí¸º£¿êV¸0žffi›Ü8®œy]¿‰y#åàºÊcº\4Ѹñ¢ûX˜×»±~^Õνbå~0Q@Ñ‹æuÓÌ­ðæU07#¸¸òÌ4—zÆÜ…----$°áß¾ [[[ï£(‡ Vfƒ\(Ó¢_Péš’òeÛ†Ô¢È+Ò#cöÆ@t|zŠCqöœŠr©»¤|DYhQbÆ¥i·>ámÀ˜h1ÄÈ;Q£sÉã“6¤xÃ@ êFçž1+@Œ˜®/ÿò/o^…S“Q΄==ëxfa‚dFÁËšÆ".‚(VðàÒfj JÇ’1#ÉøO ¤1»0þú‡žt×½@ÑäaNEâá6AJa޾°¨Ó03³ÄPóu “šÚ¦åZàå8é:˜8bêИҲã»E Uãzv(çGçÖeG¡Da4‡âhÖ5( ÐÒU„»Ähu¸§²j%¤áb˜Î%$Œ¯ë× æ¬‡å¹r.ž¾.¡«h.ײ¡ú :ØÔ¸Ú4»‹QÐTäg`È!—¿ ¤µT{ׯøŸv÷®w½«÷Λâu3ˆf^Àrc>ûÙÏî•k¨^ƒx‘Uz©W ¬~ÿm l l l lø·ïÀ–À–À–ÀûÂcìв(ë=@Láñ0¥U%ž xrèÁ©ì%(cYñ¥½Øý ›œïŠL˜Jª?L‹Òï×ý×{êÓ¼P¾Ëy ¦|ÏjÑD™ÉÑËOIÍ8kÇF€L9ÁéÍB€ˆì²ZÐÍtø+F‡å€· ÌÙªp,Jl)Ç„oÖ¦À¡Ž” ¢,„{Ú*íh¬Fmä59£€Ç¬…¢­óæñe/{™ k©G%9²Ö` J¿á‘6P óNœžŽG eXƒ›ÂD¦«Ò,ÃbÖ=V†—àÉp~ûTcº§<å)À0ëPTd #Â9‡wbÛôc›pÚÀFÆGÎ9&‡åÈœã[§®ì ·Cwô.€kÐ\ŒÓÁÉ!ÃÔƒQ¥–.^?L¸Š ³kß,BìÌÔý^`#c“ìÂwîcý[I/H .Kiyé×·äÜåñº9A¼€Ú{)¼’Žffäìúc?öc>e±‡aM qºÓ]³%°%°%p %°áß5<ô½å--»H@ 2°„‚Ž)Zp„K³ü¶oû¶êÁ›jÐÌÞ(ÐtPI<¥töõA¿Ï1¶£õÅq¥ã¦ËØV‚A½¨øžÒ’Qj$„LÖÀ X,eîg‡u¢P²<„UÒ§›q>£+ ~6ÈÊsŸû܆ó|N¡KË ‚ )1€Ù#mŒoÙ%»7ÂYC;-iä!xf¢€‚I´|,0ÜìÍb‘PÊw}×w‰°²"Iãð” fP.¾Ù (ÂRÙX3ŸH`•Îhm_™s¼Ó-;C0„3Mûjd‹±¤DA&7¹ ÇÌ®w1=u$ŠáшtæãCûdàÍJ´4ƒpá¢uºÊv¤eui‡x3Má2tß\™t `[|8†v*+‡ù@r³_Cêð'YE(TŠKŠÍsá]{_GàޚƋ_n~°±6}èC}‚‹ DQÄ.‰)ÌØ¦áC8ƒE¾Á*!±±vãÖd´Ïq$£7³Å#àÉÔp ›H>€1íSD aêî²údÄ6#ÛÅÃ#_iÕ­Y®ðÓ§œ¾zZˆŽµ<èM±¯C„²'LDsÀ³ ì™5°êHi@0̉ûS˜ñ5ÐŒ¬üÁÿMQ0 DB®È!iyPÖ¸œÕØ™BANçÀ¾B;"ÜäyèŽÁ±§„“U0qY0Ñ õ&‘°eÓ9á8Š£q@^GvX«à’+ú•áàךô\ cºö>òœ‚E==M i³‘ùļñ•X\]…þ†×ucR¿­„Öæmú@ó+ÿ Æ} ŽäYxâ!û!À;XgØ»x9O5›dœølj¶¶¶®¹6ü»æ`oKàZKÈÞw'VÇË_þræpa9=[È Í2Ú¼RE½ëƒ\ê;ç%¹Å'|¨V´^ µeVP6Œ1]ÃKP¥SYI•ÂÓtmHÈtè³Ú­¤3É;=f>`-’·Ûá)ÿ0æ4ôõ:Ã<Ëæµ¼Å|Î.Ö–ÌQ cýÿ@A‰u†YZñÕ¬„mäøæW+TÛ$Q@‹‰{‰ä ›Ñv”¶¤Ì8‹u]|UéÑjeª ,Êòs~` h!!E¯<£»CÁ5‰A2Í‘uëºÑGÀžI k\Pb!":±fë'ñJ+Œ±Dñ:xeÇ”Uªƒs|kL h+ÕXQ®*»žÙ…9} ¾Ö÷tk…l™°:š9šG?úÑáên—ÊùÇ|Ô “[/cz!É^>“ÆmÒÓ9D\âä¥t…âðÍÅ&Vc‡¥LVÑæÞßÓ_ N7»k¶¶¶®ƒ6ü»§¼÷¸%°%p^X/:"o%˜$+DpBS*l:ëšÝNG•cÎÊxÎOi2þá÷þSÑhú–ÍL™^ M)°—›¾ö¨ ’Ömp ¢œEw6’~Ö%olM‹YºËd1š¶ÁD­”ÁæÜK¬ž„êE(!µBa]4Ô}HtŸòÐÚã[«Ò6cžãñœ—¼tùë5ô XÿˆÈ ÒӢΠÞó‚[¤ûvJeOß]ØØØ¸>ØðïúœõÞé–À–À]$O»¸j©ï±e'£©½¢{Üãh–Àa̘2ý^}IÉ >H©³é¾tÍO0‰ö¨œ4¨ºà=Åo¤{äâ;Y5—?98a“hÉÔk©äë7—.┲c„@ò†j›qà[±IªéÓúÛ£§ÙÚ­OáF€A8–%!\üÉŸüIaoðŸXY5À `óTPSã´ˆûâ»uˆÔò¦7½ 0 ™[$ާœ6üÀNuÅ!k+ ñçë‚+«Ù2»Jc®¬(I’Um9îa9H{p ‹PGy°µ˾ŠÿlpGä&+5Ö&Âʺ0¢#ÀhdÍv}ª ­Å’¹9$pxê«ãk.êVË¢Á9óžS0u¤Ñó]VÍÙO @,P“Kd—–¬K´$ÓÈk_¯Õ¤0‘˜!ÄëJ“NRËÆ!=¯C—ß˹'>MCµN¯›‹'þªJÇÒϯ:zªÞG¦ÜîžL 2µ¶i¼ [[[×Vþ]Û£ß߸Ö@wÐQ\uÄÑ›=‚vF@ìߢ\JÙGL þfº)‚%:½(÷·ß~»œ=!Øà«¿b„r ê+­·3áO|ˆ¥Gø¾g•}RÁiÛ/~ñ‹¥h“JÔX=ÅZáCjðc§(‘BŒè‚a[Èzþ¤sã‘6ZB&šW U—ñ®Ù=®ÐNF‰ÜðBe`Éq©¯ÁƒÓÇ›¡ÝfpÃ2°d6YXÈÖyø$OX1µÎ!ÿÓPþ3Z[.‹‡Aì…‚ ³Ø6ðlØ!f+P\… Æ3‰<$‘wâLm§¦02¨ã°ؼhmÐ6mÖ–gû ÇÔŽ![ˆ1ŒG°Ä;†¦ugn š¶³où¼ç=Ïcä‰K,oûAg¿ºØÜar½ø©fÉ a†-i%9aþ²ÉÃfë –ÔÏ ïÆiÓ•žV@]ø­««ež„àk½ìÑ8Èí¾rä+ñ`¤_‘HÞœW °—Ñú½ž#UOA ˜©Tðš;,¼øký.o l l \C løw }oyKàºK€ “%bÄñÞ÷¾7Ý‘ ;• ù\Õ/‡ŠÐCFiyÛÛÞËQ8ÄÊ…–L Õ¦h.†¢ª676¶‚Ê'jql¤ uX3ŸÍb@Êp,+EA·l ×kD?Ökô€<€'){iŠðBybv†c[Apo¦bÖHYJ³%a¢°p´e¼"ý~– - å|tD@f±Ñ\V(‰©$”« ‡¤¯VËî” ˜JÊêÎ3SìGí-µP |جVÉ2=®0PÇ#AzŒE] ð@÷°GÜ&2IM É$“ÃÚ,Xc‹×ÞFlG9á›×fmyä£Ô4$}ý‚°Â*ÂkFXÔ]u¾ÅÉbzñ «›É’€Èâq€`^çÂŒv]²ë”ù(&üÔƒ ÌE–óአWq‘I.¬ ÙyŽ”`àñªÌ‰çì½pG½JñäêÝ1àÖ Qâ^=7¿r¯¤×svÁ›tÚŸz…°¥.‡ß/Ö6»¼%°%°%p$°áßu8å½Ç--»H †ÃYÍ;µà÷•ªŠ”›ïyÏ{è ê3 ÍåO”|5a-åà°A5W_2nÀ‰žÝ€a…ê™ %¯ltt4fRµ}‘E€«Y­›L`iàÀ†S5éÊ2Vhkà.êò°w‚«Ði_kc¯ ªahÀ †2`G|˜«¯ÂýÍÈ(JñTWŠÏ^lÍ;e[¶ñ xKƒî2vL¶ P·Æ|aá|vê©tmüùj hR?(¸i®¨ ™6îJÄN[äÆäeײ±…+³`{/N©ëtàr=§4ÿÆÜùÔø;¤-™(8µÆÏRûÉCa&XÑUTÎGÑ«‡Û–Ϧ´9öJ’ª—´•Ì)ù¸3UöéZFÛzý×ú]ÞØØ¸nØðïºøÞï–Àu—€XÙ@R™ÑÅÇP 0¢¦ A²…¬CMh:y¨›ôl …JMµeT©&'@êé„0ᨦÆ_sEå„Tæ6 ý oƒ :³&Ò¸X5ëAŠÜBò ‘HEõ +#$×^˜®#H8„€·(8 ߢe¬ÁÞ¥4„¾¨õ­­…}ÈO¼2AM€“=!‡C×Ñø'êc Ó‹`x¼Á'€ø vNƒÓ5˜ØÓlhwÓ_õT4&üaDו{ñ]£ù¯`­ßå---ë# ÿ®ÏYïn l ¼/+5ÚüAyëÑ#Ó€SF€Ð]Ø–XaRoãÊ5sM4àÃ2 )Ê ÞüÕKae–“aþu3TIð„BÑ…èÁƒ ¬Ù#(nÕtÙõV”Œ¹: (¦€Ö²Eí_Ÿ2ù gÒïWï)$ÌPšÄ¸Æ aMŠ:ÃE T˜19 2ÒµÞ:_?¬+a’õ%–ËŸh ‚n‚ŽP^8L n¤zUw R¸‚ÄX~¢ã°U+ž¼ª£3šcÒ¤‡·h©àm× ÓñÈÚxÙ­†”DA LDE\#€¨’ƒžG„Äreê4³~cBe믵qÜmÊX-„¹óõ[ƒÅie]\ªümd]COË‚ vEÛˆË3@ŽT›/ ¨kÍFÊÕØëÐ8>ò'²JŸä¦&ÛN¯•5t…@ô(ñ1õJvÉ]•æ"´‡=r(YÉÎ\9Ðú¯`jvaK`K`KàZI`ÿkuÜ{³[×ZhÊ%(UÌÌ‘ÅPy“)[P~-sdRQxpMûƉ3ñ”Á³\¿„ñøó?ÿsÊ·úþV;=¨¦ÊFs UÏrR}úk}ËQã&‚F¯m%¸ Ü ©¹­[ZAiíÿ÷?+•¤˜¥€®hŠS¯@E)Q=V“9,$CÎüÝßý]ÁPÜ @›„¬»ã‡ìšé´A ¢ÔÚ]3¡ "NM½ÚOò—za!,øÄ‹l¥îŽæC¢»ZúN°“5'íyt¶`dû²msšáÙ LTô‹C1kŸ6ÚÛˆíØì+_ùÊ8´ú’¿ôw#(b&¸m$O¼dHϧL$ƒƒ0 Cq4+y¨ A5³áò1]±Œ5D "ºÄ$\6¿¹®V8Êe[ù^ \È‚ÄLpó&·º3,-m*ûO †ö:Ì,ÅùlÙ^/‘WÉW2Ñ7ÎÓhLCU 6;½˜ó ‚G|Vç‘£é”3Ÿžz¯¿§ÄNý.l l l \ üöÙ¸ûsK`K`Kà–%rUoáî­m l Ü ° !Õl¿›m l l Üøÿo=ì-l l l |H Ýô![î[[×Dû¿…krÐ{›[[«6ü[¥±Ë[[·¦سñ4³7¦ƒ“Ñí³½LyL­&lÏFRŸ¯8ÅyÄMGÖ}ŸPöM²»¦=…KX–oÂZê^œ O‹ÛYK_™† d¢à$ õÊ.´úC¶6{yò¤Ö]Û;ÞñŽ¢_辚ð1¶,À£Å¯ñB0¢Bê7‹„âaÎh†Ê‹Ìz˜óe^ÈdQØÏ,ýHRbà Éz³üïFzDh–¼ËÄÌd8ö™"òòb&š=aS[w;®wù³úËŸÆ„hðì0a),$£A±vŽ‹³¯Y ¦•o|ãÙIÚì$H$: âÞ8Yd…Œ6™®´óÇ~ìÇH¦ØªíÑLG¼„FtÑñˆHÇ[’¨™˜f Jb¢\f êhPׯ‘9¸Ù#ËOvMÁ=olG5°Ín²¡\é"jhwcá!à§ËFÚFsýVÃQ}r³´æÊ¤7•.¹Ë–as/Ô„faàš­µ;)©t™øzMÆdÔ‹&jKÂï2ëÕ²…´‰¼¼³…ŸýÙŸí)â©÷Ÿ@SøoÁ›²vßå---[_ó¿á.l l l ܪPÄÿæeGàá6Û„Íú_~‚söhå(»ÓÞÓ>U[kõr¾õUTÜôè©OúÓ5ã°ÄõˆÊKƒÿ°ÑPR4ލ˜ •}–—Laü£”W÷-årEˆh"Dä,LAX‘"d(²ÆT'š]vµðÌ'J]°ͪ‚üÁ&ò$EyùL¢vÆ‹/j©|Þ@;@ÅDÔnˆN¥? “ƒCãhóÕßÈ'QX¸(´#¨À_  ¾7×þCðŸÃÔï–À–À–ÀuÀýrNyïqKàZK€ KÁrðB‘r…ÅÙË^FYôG ^$lcõÔÐ"éKìVƒ »OãøT³ÆEÅ»,IE6]*iI 4£¼6΄M/÷HŒŸ¥˜oÀ>)¾³6QIcl°CàÇÔ+¼å-oi©Œ^n%…Ê0¦nmO-˜ ¾fÇðÈFØÔÒDÂTs2TcÀ¨†B:!¬&v% â-niœÿÔ§¾P ~LT0º.ûÿå²¥Z°Ð#âaŠ4ñNO·?xïôÑ… 3Äe© "lçäq'ñ]°œÃ*†AâÊ ;îE—dð|Ât1šÔUD-Øf7°^–‘¼‹×PnÚš(¢ë±®¿· À¡êçª#Q-,çQœž÷E=L«F4¦ÂÉvKÕ½;Zùºàý°¦68Î6¬±Wx¬à•þü.ã«9ÿ˜Ñ8ºPîÚw—·¶¶n= løwëéÞÑ–À–À]$ÅWw)Èh¨$ .ªJ™âø®w½kí0æ7~ã7ÂipН½?*ã˜ÅŸTL@©½êU¯òœcˇÉéQÙš6ÑÐh=ÅØ.Ù£¡, M™ÙT«Ÿ©³PÇT(}ËÃ5¢ö¬òÒÅq8ëÞÑY±sFcE‰)¢^?þñg¾h^3ÃE]D€\ÉÉÖyös5z<Û€ n´¯ýÚ¯•Ì@n€ƒIa²úßûi;6ek¬b=Ä=Ä uº“=•ùš¡ã¡ñÙ¯81ÈŠ<»3³áÜ$H”Ãì5‘l ƒôˆ×õ¨‹ 3÷Ç}‹f;‘rë)¸~[²A<8:këÎKj“\}°-xæuPöÙéÔR½J±suœßh°ÜC0‚ˆ¥ƒ¾8Ý@IDAT/•‹¶Æ^áuÁÊ~˜ðȵ÷ãH†Çþ+Pï¿õ½à‡.ûë–À–À–À­* ÿnÕ“ÝûÚØ¸C%uÀ¿e-P»±š†x*“ïnŽã:Àõ—¼ä%ì9e·Ÿ P Øæ&`íˆ=¨†©p6À0¼Ÿ ê;t¨Òšö'¬ yƒœ­q˜à:2ŽˆÎ·ß,ïQz”ûÆÌcìÍrN.§+šKêéâ™X»Þê»ê5ûO/E5m¿$™™@ãårüÓ WÌ%춸Ekà‹œ[äµ­±W`*+|÷wwsùôŸÀ<-ã¼öÿ‹,gñ»°%°%°%ðaH`ÿCh»Ë–À–Àÿ ˆ”HÕQz:+föV ˜AN½Â ¤°N=²nH9JêäǤM “TdüZð6zy.p±"F˜¸šéÁQdi½ây ¡q@S`'G/cª|eVÅÑKxŒž}×w}Jn`lÿ"u¹ðX-¿H¡Z"åàRú½a¡5ÆÁ8ѧ©û4òÈ•hŸA¼¦`îsB&cO8‹ß…S €‚xZñ„Ü¥nÅU²çA§ìPÒƒ×o@¾p/~̽ ±tà o=7SÞù¢¡¸ø7ö“_ú¥_Ê`’Årˆñª©×z/_»ÚÃNƒ?kÃo¶‹b×[¹M¬`¡„à¥h#ßú­ßª W^?Íô~ /íëÜ%‘‡"õZ±œ1C›ê½Î«œý'rö¹Ú¾ª÷_„öþ»XÛïò–À–À–À­* ÿnÕ“ÝûÚ¸îxÏ{Þ“=ÛðPð-T½þèÇ«˜2züÔOýÔµRù~÷»_í‡Pù¬g=«J³;¡—¯7_©Ý˜lA¡¬Æ|þóŸ_/ÎuØrDÛ9TFõ}Vdeç„ƹ`]9}íE ñ9K4û”l¶‚ŒE™ âO¸2fBðQ„ÄuœØ!Œ9Ÿð„'0I ΰ— ð€8Š8œbÆ´åýùŸ•œ‡Ãu+cvYòŽÉaðj¶ç³çXnÐÝq;ô.ò—kL ß ¬^Á5Sï—X²òÃéñVµ$.yóƒÈåUy õW½‰¾Ê/Rwò¯þê¯D.í+"½øŸ.-L˜{žG^+/×t÷Ò0‹,Ú˜SY¡—Ñ+°ÖsYLºx ×Gþ‹Piÿi¬õ»¼%°%°%pKJ`ÿ[òX÷¦¶¶ÞG©¥Ò­†a„2¾ùCzŒ¤$OS&ÒiP¨COG;Ä .ŠdðàŽo»MPÐÂQàR  B"=¸H±— ¹i#îE¦˜ô`¶põõ9šîÔ\(àX˜ÞqšB\ FeH¹o_\›ŠäI ÇEzÀ~pB+Áò+ƒ 0–éôÅäôfBÖt}jkuЧѤ#Ì]¸YÀ×¹Zw¿ûÝÏ". X¬Úz"ʃs Aùñ±ƒVïÐý˜àJ¬‘ ¡ZÓ²=t Üg#ó{GÌÝö¦0èå=xú{Äê[˜Ýyz# ÀùŽ<䨽tÝa°vÞJ7V›Y§‚…u½ÔSïe7H?ñ¸´‡8Cýìâ?i¿ [[[·ª6ü»UOvïkKàZK@Pú+¯IO—8ø/¥GNÌŒr^ƒIḭÓApcK¦#Ëžr¨k±1ªA©U“­¦©ñ{—ÊtÚm¶g5»ñO"ú…‡’LÙLæ²hÕÝàMM~øÃÞD¬þfýÜ´rä£UT}þþïÿ>fR{{8¢S{?5*Ѐ°ÁÀ‰YvDŸT3Ý.|D%• *{Ï{Þók9”±„tp§ª’ åØgº®A«EÁÅ<ƒ^kø©;à· ¬Æ@~•âÁ®›ÅU&˜.ýdP O+Ç(b5D—Ó{5ìP˜ñ1{^Ò|qÁV/bÍpÖÆÆ÷j[ª×ÜW{|Ík^ÓÈ~ïX·à? ¯§ÿ4ü×±Öïò–À–À–À­' ÿn½3Ý;ÚØx¬EÉc¾¸ÊBÈøQ7%pÍ ÓnåšÓžþwÿð¯ÿú¯y+å+ÅÈY¨ý´Ižra*FßM¹¼üi¢Õ®Ïªîq{Ô[û®ô…½ óÌg>³­!1Šbj¯|å+g¿¢ùꃺÌÞ•6O ®ãOYwD¢p åÔVOqB™6¾bf°=k·™qþ{$€FvÐÈÞ³|`‡ ²:´2ž\ .ƒ+ábpŒó”‹à€=?7tù]°áÊ~á~¡—ç¼zÐé¸îžôUŒ–"Äô5šn~…q£ü ä©ÀBð瘤ÖÞ‡â¾ÁßM, Îdãj(l^  äÌÇÕ/&v×fÍ{øñÂ&õ_ÇÏ9îY¶¶¶þ§$°áßÿ”ä÷¼[[) ÈuFƒÐQ=Ë™îQ ÉøZrºf¦n”Zq;ßö¶·ñúôOÿô1¡ÌlìF¢žÐ,¡;¸+jb©§LCYv^~³§gKÛP¹€1•}"|FL%!48w¬*Ù[æ¿GËhURÓy÷ÍNÏêͰ ŽàAzPÄcûXšñ!ê̬Ayª8Ÿã!6KÚ…ÿA 8ÀÆïÃŒ­GV CGG¾©ñc‡_:†*\ۻخ–|¯[Þs©®Ù÷zÚ½B MP%&¦ëhc'ìצ§!1 \{“÷O¹J ï>_ÝÆ.X0¬Ñ¬J*]¼Ë½×êóoÔÞ‹Œmñõºð©Ù¬ÍÏ7hs”¾Ð¸>CªÏÀÿÁ#ÞSo l l |$$°áßGBª{Ì--ÿ1 Ð Ù%Òÿ¹ÎdžÚa)À …u¥,ԋ¶ CxA‰ôˆbʳì„[UêXÁ÷5ãG¹l¨È·ÝX`‡°V2p7P§A:1ÙF®T„©õú ûAž0žëôo åјe!‡l'n>²‘ìUøjÎî4ý»I÷çÿƒp@‹P«ë¹ÊŽ8¸å7²_ÿ.†ë1”ïÚËur©\­.§ËæÊµ}¿Œ¸·Ë=XÞ?ÔúÚ·r¿˜¼éMo´ªaHl†¬k‚ûg?ûÙ¥¤÷Êx*¸nû=ÂÓ‘yÍBz¹ zÄô´ö‚Ü‚êíVƒ¥Ô`ÍuÁWpŒEkásr ÎÔ»°%°%°%pËH`ÿ[æ(÷F¶¶îç"Z- æç|™£Ÿô¤'±iì*¨3º å'TÆà@‘0Äw‘ÇoŒ'µ2ĤÏyÎsêž=ž0†*3@Å-Ì8ÚЃ¯ç_Tûø@Ù|]UÕžúÌX.ë8+gª‡*”w’<-×՜ըC¹Ãm²ˆéô1yL0`]ÕÚWÙ.øVMŠ‹;ĺÿþ7HàŸÿùŸ¥g\‘Õádç«{è— .sÕ¸$®Š A U8§àʹx®ŸK(¾kðLºWZ‚i6…’öËÅ @}ÉÒ&ÓÌUôšøšáeé4{5TB§#{/]½t{TOcéU ï9°£µ3¦zÜ>‹î?þã?îÌ¡åy¿p¤î¼€z;ü>²Ž³Ë[[[·’6ü»•NsïeKàºK@Öõ•…Kó;ýdýÝQd¿ð ¿pžBVPý26ci2ݱMTAê§R½\ãHÌ­€~”]®L¹ŒW)R…GÌÕ º,<}æ1(Y‚Gg¹;T¡´~ž6šÂú§’Y&7')­AÜ4WžZ嬗"¼ úÊ«&JÍ`àuµLõr‡®{ÜåÿEÀÅÉßpzÜnþÁºØíÚp±ûýßÿ}{D͉ìÒ­«$;qÁþ/{÷¬oRÕwœªä2¹IURÞÄ’T%eÊ€š˜"€€%‚Ñap‚%‡$ 8&œÂp˜À( ÃAå0HD 2 A”ÄÜÏÌÍ»ÿ8ÌaÿÝý^<»Ÿ~ºûégí^]kõZë·,3‹m]*ÊÆïÁúÅ+^qðÔmQ©% %– P/Ú—…­1;¯Á&fâãd½‡uÔàš1Ôñ{ªÃNå\ Æ)Ûü§ž¹²zWFÈ©Wp0Ô£¦ƒõ&i3YÛïò¦À¦À¦À_ lõï¯Ì¿rȦÀ¦À—¸¢%Ò15ˆqPwï{ß›÷Z•®Ô¤É ¢À¹ž_ °§G¬ˆÕ³†‘Pk<ŽgÁᨒ+©Û4®ë3€š`v«Í‹_üâw!øf™šÚ|+z,p;úaQ)«¤gVâ/ ÖÈÉë$þ•x¤êõE>ÿ AâPcN_ œsÎ9kZ‘þãûoÿmf®SšÁ5æ«ÌTfŠ"èyØQÀb³äÁ"L—[—Еsµ ™k#;N¬Ls ‹%f‰qŒïp•j#Nµ(ÁVµzÚj . Pý âê2ˆJê1û ÚxäŒc>At¢‡&Y>¥ÙÐÞfÒK÷uS`S`S௶ú÷Wìº?gSàäR€žFh£êäYFäÈÍ#‚ìj×a”öä„Lí¥p(fI“”ìË¡TM¿ñ £k (R)²(áòq{œÛ«ÒXóI«cZ©‘J'ÁZOOyõ9EIÉŸþô§ÁÆh£¦dƒæ¸?TRöÖKˆ3ÿu@Fž£?Ss8p™ë“÷õ¯ d5 VÍáE«ÂíXÒ,˜”´Y0j(]åÖ£hþ‰^üe)Z–¥?ŽÓýh!jõ½h|;¡€b9ˆ‰qŒÙëÊA¯W\Jp2vr,9§9çŸ~/õ]©…ØY Öžã,¿þOÇbiÌ[ê¿›Üä&ì~MoŽ~Ö^»¼)°)°)pºS`«§ûpÏS`Sà2 PÃ:³—/PMˆ…ŸùÌgÂ_ɽmMk&ÉõHÃozÓ›H·$EB¤É€:{J¦Œ¾áѧ>%ìþÖoý–G%‹×w0ZdTK Mô”Þ]3*S9ïš Mò ’R'ן`Q”‚‘ä-oy‹¿—¼ä%‰Ëb¾ãƒÚ úŽz0¬[’=?Ï ë%OÂUΈ/÷ïþ݃Å:§Òa»ÜÁÓ¹å„ ÓHìœå—¥Î‚ "àŠ½i9…SÚ™=]ÕÅ¢ò°ÉšbÒšsé‡"î°þeœ1æüïNÉV@¥LKÄÚ¼)a½Én¯—oiÓðôNwº“+S9ïSÊ®ééêJ:/Ú…MMMÓš[ý;­ÿ}{ò››_¦@j•FJ:RA–0Ç|ÁÏ-Éh¨¾Ö ã*&XH%!Ož-Aº†Â"f©öÅ)%n^ÿú×}¡°½rI+íËó¸ü!R¥ÙT¸ ‹ßx¬Uïª ÿLƒP>ñKÓF!Ñ|ä#©v$Zæ>/áð™Àº6ž²oYm•S_ÔNoÈþuæ»|(k¬ŠnéW£DIÏ©x,„5àúoþÍ¿aF Ô²´8xôü‚õ ÕÑR·à2‚§ò¯`“Éø‡}ÖÿBG3šåÉ™O&6TKbÏÚŸ{î¹SÙY FH[§1û€^¢ u™¨˜†²dù ŒtÏ.o l l œîØêßéþÜóߨ¸,Ù]˜/{ÙË"Èq€L* Ö¢§"÷|I¢Dá*5)°¾€=â šI¤¸ Š£búá1x‘1Âÿ$Â~½«—‚8Ñ=íúËš °™xjÔ #p8Þð†7@—!I¯b4ß<’º«^Ì}‰­°^™bî|ç;ƒ·é{÷õ„S€…íßþÛ{J[´…—‹&+ß _øB-…êÍjŸE•qOKY“µyU°¤;—qkL Þ²WîP樺X/WL„•0¶¢X®X¦ïxÇ;¼ jKbOLª’^7N¤ 5\_rˆ¸f ÜÕk@ž(ºnçg31ˆe…§»°)°)°)púR`«§ïÿnÏ|S`SàËø™Ÿù‚ÚÄãUgRàŠâp饗ŽuN⾕ˆI™9¶1/xD  T†¡X< ªpH[-o™ zÑÁõŒ3ÎxÎsžS²2 ÒKàf²È3³[N¤º“§ Öùæ™ÛL»f„o¤ð÷'>jÕ ×¡h³ðy譻˛( Oæ/þâ/À®‹G„XçVrܰªdÚXcs–1Ý-é'=éIskÁgHOñ£JDÄ&˜eZFó´æsb@l˜ù1ÆÄ¤ÙôÒÙbŒüÿïÿÿ2foX|d˜úñßöô¼óΛz…ÒcÚ^ÖÊ]ÞØØ8Ý)°Õ¿Óý?¸ç¿)pÒ)À÷ŒE  Ÿ0rƒ›Oà“è|Èt›ÛÜFe† †µ©˜Áº0èyFÅ-á8ßKF …£Zñ”waw<år6BT°öP+žð„'4¸qR2ùÑU£U°Û®Mr­aèHt–!…ÓøGeîµ=·:ßÅ“m>s6NI Wd7¶¢,3‹Òõ”§<%å D ÛÝ@Ñ® ¯²PTɲ/JÐÚ¦Å1°÷”Še|åñÒÄJÊíè~µìŠ 3³[áé®XÕwqõÔÞÌ|lÎÕñ”M zÎÕú¨½|0«ÆhKÁn¶ˆ£3Î.l l l œîØêßéþÜóß8éOŒÜÆyr%DyØó‡ô´<ìä{Výˆ€êá f‹Ki$Â,k™î¿ÕÜG: à £Ëé¨ X‹Ô<$Ñ™4ÙPélÚËQÍ)µÊ™˜–Ò”Ýîv·«†æîw¿û|ÊuJµ!m …Ê$²Rf—7¾ht,{-¡£WKÎ)†¥˜å0$î”GÏDê>+“ãq ’º5ž–¥¾Ô¸11ÎècÚ›ª5 £¨Ñ:ûèztz–˜8k¤f~ëOG?1Ž­ÎSº(GÓA"¥…®ô)'§Mf­ÜåMMMÓš[ý;­ÿ}{ò›'aúñæ‚A?´à¨6Îl-‚)TôÏ~ö³‰†ÉEÁ”qO¯SJ±ÅA1¸ÑÖ^ùÊW¦¼…-Á˜ð¹Ï}Ϋ³]4ò]ïzWÀ* GGB¥­iC%VI—ƒ5/-á´9eA6 j"—8S/çÖøðëeýjì¦À_–Î)ÊP2ëê `m‡ гžúÔ§_ ïTÅoúâ²Ð8ÕXÆÍJ6öi Æã`5±RÊØDôq‡nX9ß1#–Ô2öœA¦Ð&`Ì×¾öµØÜë°üŒl+À•ni€ôÀìünq÷üÁ Ål,9]Ûj¦r666Nk lõï´þ÷íÉo œh PŸr˜LGZ@$Æ%´ =*邤B˜Ô‡ãÂg :èà‚ªŸ_ZR¦,Ø6)]n!Â(‰6ÁqúBÅxÕ«^EäÕ1™˜>ɱ­à¨Æ)¦pKoa41v‘˜«?ðsó.Å’™(<3¬p øiɃê;tØ…Mo…ï{ßûVêƒå·ÞÞìf7“Aä@cœ|›Z¬óú2©Qá ¶Lúu¡€úЇ‚iÁJJLJ?üáÚkÓøw»ÛÝÜân^ßXaOLcÆncÊëE]1,–ÇønÛô \W¢ˆ9¯‘úe¥›íEK[M‰=×G»¼)°)°)p:R`«§ãmÏyS`Sà2 àIž[“¹Ë’GdL  ø®w½ë×~í×`*»r­Lßã– ðœsÎä)Ë3ÚýïåŸüÉŸôÆCCåW~EeP.ìÙh_*Y„å’êÖOžŸ@ð3VyÊëmo{Ûµž°Æ>¿W\F”ýÛ¸J)€¡dü[—ee«q]œÜ›BK¾²vtäñÀ>p­©<¶z·Ìƒ²®·Îc¨Ø“¹/¦cñ xIìÜŒ©&ŪÅõa^Ö¼F›¾î§TuÄb{ØÃ$ˆ¿è¢‹ÀϨ©=cæPÑö’Òx: vaS`S`Sàô¢ÀVÿN¯ÿמí¦À¦À—)À“37HÉ÷ª‚ú §3+ΡþHr ?m ‚l:N >Í R ‹e #·1]4pûû¿ÿûTÈH6F aQ<â¸ç=ï©@>.aƒ ¦ŽÂ# *eVJ·.š¶ÂÁO*6ø„ÙWC ÈÙgŸ½×ĦÀÕM9Ž*4%ú•½õ0b–nʘÛ"î¾ÿû¿?& QS‘£ŠÆ ÌCuŒbÍsÈdQé³÷ã?þãùsªÇªMóâkê¢ñY +`À~ðƒXÞÚ"<=øéN‰­Ò(„å DÛ2|Úpl;W7©÷ø›››W7¶úwuSx¿)°)pµPà§ú§ jÿøÿc®Y”·ïû¾ï;*GO™˜ìdÌQOå‡?üáæ*=ÑëZ~nÀè ( Ï0ûH ‰³¯~õ«?ðêõùÏ>ïSŠ™2b¤jœ qÞ;…Ƥ¶¥Î1,ŒSè´™¹V*jÎuSS½q'¤¾ZÖôŠ) ˆ®D ëjÄà@Úú¦Í[Þò–R³¨áö욯ƒ g¸¸Æ£}uK«½[¹.Ù·KÒ€÷Ùú:£a‘ ¢)¶Šm±0?í_ø]wÄã´¥/›0¿m¶ÜzT‰µ¥ØXl/6™&éÕWLžýdS`S`Sàô ÀVÿNÿÓžå¦À¦ÀJùùh%&Î52|B>`z±hLËW-.k!›ÁÔ êÇÐçe¯^W¦O‚¾h’óÔ-ƒÆJ]Þ¸&) Õdγ&œS ¬ËZ¯lys¨^K‡,cˆë)åÊàéuƒ¸óŸÿóV"+T¯ÌïøÈIJ1,¶ÍÒ.b°<+7½éME v6$¹…pÁáëú¡¼^m‹@@ÛbjäÑ·W£±}㚤ö~צÀ¦À¦ÀUN­þ]å$Ýn l \í(;Œ;–¡g=ëY¬s)`ÿP;‹Áw}×wiyßûÞwPF?(™…ü™.·4m Å Ypö!’/¥8óT°„’_'ž RééQ+9˜ôYƳ ‚ƒ¬xüã)QÇSþBŽ™G±‚&ÆŠ2iW;•÷ 6®€áשµ†ç€£õ¼:-³?ö±U)ŠË§DzáûßÿþÑд¡= ¥ÒàÈæx€²£™`XÝ1/NÁÈŸ4λßýî쀲RütÑÆ¶`[Ä„Îþöoÿ¶š,|Gm,¶;C®æ½ú ¨²«766N lõïôø?íYn l J©ÇÚ³%È#À*$3º©ŽðG‚¬}°4´¢‰~äG~D34òœ×2fqMÊÌn)´¾ã“æ)!õSŸú”úpDGôèà÷à?¸îѧù2½”}c•ƒë›¬©læ£Í-˜PSmä}Ý8p†–ö®vVìfyOBÌ…):QÓAI'/o~ó›3ª«‡=Ã.÷¨G=ªî+(¯o7fd‰I)“ÿîßý;½ðƒÿ¸§Ò'äæGj[èøÆF1$í¤&«û ^ðõ9¯Úpì´Mã]ØØØ8í(°Õ¿Óî_¶'¼)p¢)…/|Îg>ó™RTwêϵ ±,Q’sf4O¦¤n=ïyÏs%Ï]z饉}‰’?-à~`ú·Þýo|£Ê‰'üÙŸýÙûÜç>ŽaL òGçɦ=‹_ž¥^xa¯ aJVVù¨â§ž:ø15;¸O·¿Ù‰^ôÇûã±$Ý)Ng+8¥á"-Nƒ4½Ð+ÞñŽwó‹_übò®ÉœuÖY±LtÅàa’#å=k>n~$Îç>÷¹Ü¿McNk~ìxËØ-/Êqq\‚)VΠjBjÁ †bè^M:¦Án·»ÝíŒVK°Ÿ°@;»9x#å X®«úYÇ™ž‘ÿðÿ=Ç6˜©íb<«÷èÛ²7Újt±í4Žè˜ÿGöô666®ˆ[ý»"ÊìúMMcGOúÓùzI =“ …¬–Ažüä'«¤æÛÉp£W<ò3NF†Ì/|á iƒ" ªÍ\ï}ï{'Õ1WL)þ²Ý¥’2™×~úNAìŸT×cµ€OóuüH&Ígî¦À1§€µ=ë| ñÅÜ*ðêÄLmÇ"Ó†%?pN¨-a)Á¹ý½ßû=Ÿé°^-iw }t3ÜôÈG>rºSÏVÝRýÌÁÑIØ?£Ù2ïÛ":<²iDd Fÿ4«þ¿¬ô6"ÛÑTî¦À¦À¦ÀiD­þFÿ¬=ÕM“NR<ƒxY ‘A áO¶.~_žÂ÷+ûoüÆoõ<}ô£­®ÑÈXì_X I‡ðZŽjbD@q>Y d†È Hb1£1GHzÖ«yæ0¶ µ D©p,µa¯ _Ž0Zå×¹4êEû·)pü)ЋõŒÅ®È =«Â&gC·"ÙùYÎÓxºŒHÚ*©gŸüä'ëMºù,б‰ä µü±û1YX0>lÞƒgXêh&D%¹Œf[ðÔ¡½‚MÃÖ©KQX_éÆ0èi(S¶£Zîë¦À¦À¦ÀéE­þ^ÿ¯=ÛM“KßýÝß%àd]*8þ'Ÿ1dOcˆË!óV·º•zrŽ)ðêtœ@¢lÑÔ9Þ_Y ïæJ•,ë6*åȦæf¦ $›&ªŽQb†‚*ÁU,oÕ*'Ài`-¦ñˆ§ ¨f@hæ{waSàØR ëÖ-“šL€³¤¯¨ÐMOÅ~âŸ(²nm[Å/Ìz­}¾¼±ï9aéÄ$oüåŒæ3ŸùLTRy€¦»Ž× Êd´Q„øbëÐ×62Ùêƒ{1·¡üG?úÑÎzlJS¹ ›››§ ¶úwºü§ö<7N:„ݤ[BÀj‹å¶·½­G™æíèàŸsWø’éè‹úrµ¯‚ y½:ø—å¹Gcæ]Y ÔCæQW·IËãGK,‚hÔ¹õGË%˜Ær²ã‹†þ»pü) AB«šcdˆ»Vr 3V»C–µ&=)æÊª­­§$yòÊænÏxÆ3auËäP r­ÁNv^þò—cáÕ±³qXþñx 2¶‚8·‰j¨Áh•/ï‹Mi^´ ›››§ ¶úwºü§ö<7N4^ô¢‘Ò˜àç!‚w—_!g°Ç=îqÉ‹¹_Š/’øK/Â(a.hÐD=WmÆ GHÄñ<©‘lwó"X2Yt/ß4GÐJ]¢°ÀX NÊôHÍêJínÁly®ª›4¹ãóO›iì¦Àq¦˜¢–4­éŽw¼cå‹/¾x]çS>ÑqpÍ-oyKm°@ª¶ je:Ò'eo§PV¸ ž¢Ìcó˜Z²ª3ãv8NcHKêù0ztƒÜ`MÐRßtH×7½éMßû½ß«²4÷̃CQˆ­i*waS`S`Sà´ ÀVÿN‹Óžä¦À‰¦$Àl„¹!W±¼¶Š¾ë>ˆvâeOvõ£gü2ûÁ†a—ûÈG>’Q.¥ gP¢×»Þõt'zòí]þç¾&L@ìÊÙŒŒhœ3Î8Cùüò/ÿr¾ó;¿SÁÓ°jª¼¢+k¡l©0åG‡”Tm¾w6Ž9 'µÂyb§/¹…¥tJ`-bó†Aœ¶Hƒc.,†•þÉ?ù'Ó`-`vL:Ä Äï¿ï}ïëÔÆ¾1 M÷¿ÿýkùº×½Î ôÌ}èC$QÂuÊLòöÇoƒ¤[ÚšlPóö]ØØØ8þØêßñÿín œt äxIø;оÀÈϤÜÐ0!$U_åÂÊ\=¿û»¿[™˜È_4jf¾cR¯lïÞòÄ'>±.ÏyÎsjæ¹–—& S}’.92<ÒæsÐÞ‹ˆ•c~”Œþ’K.éiJ`eÚìIÿßïï?}(ðë¿þë³Â-ãŽW˜µ Ä¥¿á¯uy×8[7ñÆ7¾ñtW ËzßÓX »½êU¯2ÎÚ¬2&Íᕯ|e•þ„%Ë_ª 6”ó=¢†ÿ$ES^Æ@Œ?é°g£õ^'5ï}ï{ 0öÆ4R[Çéó_Ú3ÝØØøÒVÿö"ØØ8Ö÷—Bõö·¿}&úêW¿šLưFqR D©L\#qžþù$Qòe8.9ï¼óéä]p õ!óB+±%>)Øê·)øGô`m²‡Lɘ`¨³Ï>»šox… ˾f ’㧺vlzóÕ»°)pœ)0Ö¿Öp •°Û;Üáj„ö™|PLë"Ÿ2†Íf>5‚î~ôGtŒáØÍYê´ÁûLåÓ«bXl‹y'€ÿÿø#Z›†ö̉ÕÐúàýªCšã@[MŽ©N…x®B]±…{—@£Øó/x½B%mØ}ÝØØ8æØêß1ÿíém œt ÿXR& !>÷¹Ï…­rp<Ÿy”ù®w½«GÄ2h jŒ 2°xj•à£×Œ*Œf~PàëÒ­k#K6îg|DËï)±U4 O°ƒðÂé~ÊBø¥E%L Êâ JåÌp6Ž9r†œÕVÓÝï~÷¯»¥N³ƒn*üoêWƒ!¦Ë’ï©öö£‘{˜·W7Âk_ûÚ¡[‰œ”}s6dÓH÷³u0Š ÔÝäë›[AgLÍôbaþáœHçE»°)°)°)pœ)°Õ¿ãüßÙsÛ8é`ñ#fË@³¼ãï ÂñòZÅAOßKÔÚ'‰3'§¬1 „Ü m—ຨ)-X¢¯æ Fž«@HÍìPl!ËC £‰€ÙÆÞÈp ÑOx0 [žl9¤ìÕE7Id¸ÐàÀÎ 1ÚI_ûûO ă³ìïsŸû(cØbÿðHŸ’6ˆ&>pºTÀkEÌât¡w˜zòDÁ+§fAŒÆŒö€uÔ h¯vô¢ŒmaèÚPÓÑÖa±x/Mï‚ .ÐRž˜XµÉsg=çœsœIg8Ó›œ3ø.l l l C lõïþSö”66.£!¬ lb²˜¥–ƒxGõ°à³›•)!B§øÓ !ÂGÓ‰ "Æ©]óÀŠÈú÷š×¼æÛ¿ýÛ½E€_Q‚7¹ÉMÜ’>¥ü*Ct“ÉÅT9±²ÊS^Г>ø‡ØSò«·s«¥o©ž§ÜÚw#ö_Û×Ó‚©P^úÒ—ZÌ1Wœ˜‡ä?ûgÿL=ö±Ú8'Ê!üo|cÝHÙ1s›ë£É¾n l s lõï˜ÿƒöô6N.Ö„é¤+r{Bî—÷¸Ç=òË> <]ñ/þâ/Xù²*@qìúWvfñƒ*»Ä*#zEvE¶»\¿X~û·[}þ]¤Ìw¿ûÝ”RàI{uaLùœÑ¯™/jÆè÷ŠW¼¢d¹¤ Hèœ`$Î¥\ã4ÎÚ9ƒ³lœÜÿþòÓ|àgé*Ⱥ‰¡bŠ €<ˆÎ´5v³¾O îXÞÖîÊñ£\¬½š±Sy»AlÇ%”Ní=åó™õ~¸RËÃæ-ÞNXÆ›C ®A…6Ûˆ7vüd{1Õ‡µëÚ¤òE/7½Ê}^sº­Ù=ßMJ­þÐüþìMcNÂVaQh\‚ZöÒ^›Ä8ØE]DÎ#)òõQA8ÐÇ’Ò’,ÉšÎþ'"h$Bb_2eúÞ½îu¯[ÜâÄ8?ÀŸúÔ§è™Ý(ø{806”þ¡6]Mo•SÍ€8Ô¯T lº$×å _øBq‰Íðž÷¼çtÜ…McN8®¥k›íæÉ¢i©[ða¥à&·“lS—xçë\iw£ jÖ Aƒ*pé¡z6:o/`òg±»Ã•3uѶgÉ](uÍ9Ð][ŠÆXØ&cÀ¼[5(¡ƒŸÿüçSA'ÙÆuÌÿM{z›››[ýÛk`S`Sà8R ƒ^` UÀ÷1—ulO+U}lÂöò‡ø ö9m>ìa#êÎíó_ü‹±Š‰ÊdD&;Ñ;eùã-–k(ÎÁ4?ýÓ?á’Z˜‘Aw¿‘ §pP?mÆÊaz<ÖÔ7&_мÔ¶QO‚Ô&e²a9¹ÇÿÍžÓ¦À©(P‚‡Ô?ËX“qIq²à-{K=Ó=P͆ᙩÒoØ­ÛS^³éa;ÝéN9mN³¬pñ¿€u±6Gm(¸ã¨²7Ú"þìÏþ¬ÝàðÕÛFl&¦gcqKŸ´Õ0 f¥ØáQ](„‚;]ÊTx*òìºMMMãB­þ—ÿĞǦÀ¦ÀP*&YÉ1,ÿ+‚]H02š9t—}«`9²»™îœ¯ÈjÿðþÃlt¬yw»ÛÝÖ¨!O p%þëç–èÆTAì×Uê ^½_í õ?ÒË›^”Q˜éÀô2}èNp$¤6É—½ìeuÖE›¾7ö¦7½éÐd6Ž9ʃÒÒµŒÍ¶PI8Ú"·à=µøa8¹eZ×F*Îìoj0õ˜ßcŠ®©|ÊúºÂD‹“P²Áñ½ôÿ¶Q(`çí9¦Ú(𾟡‘»Ú"lΕzQG0yrÚX:Q²Õ˜mÉâùP>ë ufœHm6®@}ù?kOoS`Sà$S`«'ù¿¿¿}Sà˜R ©ñÌ3Ï4?íÔ9’V:ØÞð†`` ¶ôË<¨RKæÁ\°GdÔw-¿ùÍoÖ8ƒ€îïyÏ{êõ_þËÉñ’°èÕÉv]u“Ñà§~ê§V3àÚ¬2¹Äÿú_ÿK¯æ ;4B7,ÏÕÐDµ7âoÑPÙúò+«£©ã¾n  äØÜÒµŒM8À$¦3³±ª5oñRë F› ƒÔEüâ‡q°Ï4>Ê_j0 6T  ¾éMo2ÈfR¡hÊDÁ÷‚œa4¿çõÒyKœƒëeK1¸íES A4+ߟùÌgjÓ/·}}¥bÿÝØØ8ŽØêßqü¯ì9m œd ÈåEØø'ô.:ÐˤvHï)Ô¨dÊÙÜÞûÞ÷v;æ;e2(;CqGúªñc1#”Ó÷³g=ëY*ù—–r§Lñk€\xÀ-É£¹~]>Ìe—ߥJšjé¿à ª!)^.ÓþõáCˆ!û¾þõ¯¯}ö½^Ô8Ápvûº)pü)0,féZÆ&œYO`­²¥Þš·øçÈCZÎÌÝ_ò Qk?W6ºUU‹1¦„œá×r=ù[ëoÕ › óK-„¨d¨qÚ"l¼TŸkK‰ì6õ6·žj“ýŸ#z®Ù²ÚlbuÜ×MMMcH­þÃÊžÒ¦ÀÉ¥€›„°§<å)C…½èEä­œÊ*ˆèsè®Lž»ð õ¨GM¾$3–øÀŒÎ;íq4:åB“íÊÕþ=ßó=:š€@¤|&Î4޶”"b² þÄOü„ùåbê]Ÿýìg@íS¤Y.©’šdèÀ'”W:üŒ¡Ã.l g „°²­ ‡« OÅ´Ë·iÙÇ#9y–sw¨Ê<·1NߨæÀE³Ñ†ÝÆ’™å‡q40lùZêâZª•·¼å-åDŠß=ªl£ð^›ÆYgÕ°õµ±Ø^l2©Ž`¨FåYÚ[Öx?—Ž61[Ùqþgí¹m l œd lõï$ÿ÷÷·o ; œ}öÙ„'¦ƒpÌÏÉz6‚W*9ìÃþp§ìe`OPsͦǿ«„S3éû<…ÓVVjh©ê5ƒŒ[×*zÊP mC•« :K/Ñ«Ãó¼øâ‹áOèBL%õÖ€L¬#œ ·@´aYíÓïë¦À1§ Ìa„˜%ÿjeü%L7H•TAßRø_~×ò:¨Á,cœõK²Ì[ì«©¬Ëäß³Q°±ËýjW{…¬¥‰Wø“B{ˆbbö:Z:àñšõFgIøTÀ¿®ÊؼiÛ¸2àÛÊÖÙåMMMãC­þŸÿŞɦÀI§ûXçñòª-J²ç”ýmo{Y-íkûtYñ]¸~•]šõ ³yКÙ¬”»Ù8Lm¢ŒÒÁÜú‘Y&¦HM@£—?¼Î]îrþiŠb ÑT°iÚDkí–«ª9³‘z«Lðå?Fåwµ|ÒÖ·˜äb6Ž-àÅ®-à"x󔽂eß'üÖoýV홸Çy²ƒìS U›Û~•®0ZŠ®&Ðj(‡ýèGmëyŠf³u@fíÛ ç³]d™¤¬Êؘ4IÛKûR5¶ G>ò‘e•BsßûÞWýo|ãùר¾ÔèbC›Ê]ØØØ8>Øêßñù_ì™l œt HíElÊ ->øÁvÊ.¼‡¨—ø5Wqzø©O¹%ç=ïyÏ«o•T¸2 ´“ð—0·â¿3ÕJ'hœ¼={Q 'ü÷Õ’Ð##ólÚj`K½ýçþçݪo&\pÛ^ýÖ·¾U% ІUŸU0ǹ #²zä— kãìë¦À±¥Nlź¶€;ÔèFмžZö¿¯ÿ=£e>*VÂ>j°R`-:2ÈŸ{î¹W”Þë,öȰÂI=´LoÛ©²«L Žf:ë±i4™üããoüZª4ç<´§¯ÉÇÑÏþós,ŸÇ™:g˜Ü…MMMã@­þ‡ÿžæÀ¦À—„óÑ»HTãF…(¥ì£ø%i‘½h\bRÂù½”€#¨ æMS–^T¯€CÉyɈ\@SG’c`ô®—¿üåÉ£3¾Ì«ø8]Ìêþè@&ì$øÜç>×P%‘'Ïÿ5ó‚)%ûB5‚x$×¾1€Š¬‘«  h~Ù…McKY¬g¿±‡[ÕžÆÛ‚z—ÿvsÎG¥Cƒ•4`-ìܧ„ w¸Ãêµ^±•ƒ›X¬z žY^‡ãÊúÐ#–I.ÜsôCsk÷yh»0”­£ùÀ…ª Ô²(ŠTé$hÝ%ж|îsŸ«¯MÌh¦a[›¯Û…MMMcB­þ“ĞƦÀI§@Pò)#DQy¥Kë ¯” %J² >›}LwbbÂßãÿø8í§6É,‰Í­¸; DÒÐ#P„ÿçÿüŸ ªo×{ÞóžýÂäœÖ‹¨‚’€Ÿ|ù¿ÿ÷ÿžjÖRcÞeO~ò“#¯ –~Êw¼ãÇäX²²^´ÂÊÏh»°)pÜ(Àˆ×Šu½õ­o];—’¡³˜iZü&_8®f²nη`Œƒ#0Q§*ØJPŸfx¿fìíÃ)ê;²QphÒ±‹r?/U ¹ÙO ¯uKM5ÄQyV¾Üîò?à ô )Ú:l ½k‚ ÕÓî˜âÙ$;¯± n<ð`oNìuºûv½&Ö·÷uS`S`Sà8P`«Çá¿°ç°)pÒ)PÌ›è &82âÄ‘Ÿ$K(v˜^9!ˆ\LŸøÄ'ÒGþ›kOJ£×±õd¢éÄ铆a¢þÄö€åœAj¯ ÅÄ@©žxÊmþa…=â Ö8Ùwàgj™'ªîyˆÝùÎwVæöY|”²h¢š×¬ƒ«kܼt6ŽÇZÆýÊ΢Ìv ¥JK=?g‹ß1 ëYLw£Ýhý–ìlñ†ÂVÒ´™Ž¥½–x þ)¾×¹Ê´Ç^-Ì/Ô(·ߘS‚øÕŽ—w¨ó—yÑœ M.›OZŸ£{Ž1õšã$·¶/›˜­¬pG›Ûú»¼)°)°)p­S`«×ú¿`O`Sà¤SÀayBÛ*ÒãÎÊQ°P½W¾ò•AêMNZÜHu#±ÌCðâöÉË+ù¯+°@&ÆR1J`BÃB//îd£4®ƒÈZ6ÿ0òh¥ïÿûUBª Ñfs€S:ÍnxÃ6õÒm®žzA£©žd,ÛuåÑÝ’ª×qvySàxR ›X 8ƒ‘í¯ñ¶*nHC ‚ˆòŸô%ŒÚÔ‘Gi“øEoa.#Ãà°Ëá^—ÕqK¯}ìcã šžY³ƒ+/2¡Ñ±>‡¬\ÔjÉ%—L%á¯w…öÉ,pùÇý5ãøõ DU]êK”œ—’ªgœ]Ø8¶øöoÿöY´Eèq§4[§0ÕÏjǘsBø,J°Ó|×pÁx{z–’0Be~¡Ùè€my€zËp½[CfÆubÃò6Þ%¸¨ÝLˆ`<'`v?*\Q…¶çMõ²ÍhS”è]Ž Õ»ýèGÛÄleMÉÕWÇ}ÝØØ8Øêßqø/ì9l œ\ |úÓŸÎÈF2£!׬le}èCÉ[ê ^ÎûS™†X9y’®’Øx‘­_És)W"ëêÅþ°úzDT›ûFh›‚9î< ‡ÏF“!Zh3%Þnõ ߢzɦUŠM*«aæ =íóËAŸ,»Zÿ”3ø.l O 0ƒ¦¥^Ô®un¶³˜pº-ý‰6Å=àPê³OëHEå z”Ùò'wn¹ž2ì§X†ùñÿq5ó£¶ Ç\™ô§‚Æì¼:Gi´¥PízÅ´7Õ¾N¶˜º¸ÚˆÚ‘dûÌ…›+iyD½Î¶¦ -…Ó4ª™î»°)°)°)p-R`«×"ñ÷«76¾gKÂO©™•ÊxŽqÁR†‘Ê$ÄnH6V‚ 3šþ‹˜j…6˜ÇÙ,–—¼ä%#’kàÕ°³Ö~-«á JŒSà!öž÷¼‡ ¨?4’h:áš›áç~îç„»šÍ6Ü®§#‰R=e±¬=124ƒ‰[Êj•®&<Ÿ¼ ›Ç“ _³bòÖ=;KZå…^hòc/”Ž%Ü£Éõ‚eòóT‰•ÖýGÿ詬;§L:'Š_Î;ï<ºb‡8ÚøQçF£sk¨hpý4ÃY6„^ñ¤'=éò~—]xŒ>éð%ržRðfVBÙšh€^gÀ?øƒ?Fã»Þõ®¶²<ÚÜltÓq666®] lõïÚ¥ÿ~û¦À‰¦m*±éâ‹/ï„f•Œ\H¨rKüÊÿº¦\aC¯BOIZ«ˆÆ@~]z½$6v·ƒ ŸvWX ëŸôÓƒS?BÞ„Þ}ñ‹_L”2ÞÛÄ{J;MrP4SRȸQ@àÓžö´Ýüæ7×%ˆI€FËõ4X‹”R*e`6ãƒÚ4Êb¿¾e—7ŽÞþö·ËL‡ZÒn Xkα’CæÏ9Sƒ¿ù7ÿfg%1Wì3æñZâ>-Sê&¥J•ÆÔÆÏjüŸiTƽ@¾¶—6l 6šäÄ —g–’ó'p—'>ñ‰ãAª#QYì½ÑvT>z®ž €Æë•˜Ö×+lk67 næ¶»¾e_766®] lõïÚ¥ÿ~û¦À‰¦@pƒüþ¸Ç=.Y)Œ8xÓ›ÞÔ-OË´»ÕrÍ1­ I+ * u+÷3HfâúåíIÜtËÀî—?g·_ntùâZñÔ¿ÚÎÇ A”Ô¤Ž«Bâø´YV D(A÷!uÿÔ§>åŸÍj‘\øÒ—¾ÔmÐ/TÀ–(80·fÆ![ŸèU²?þØS Lku­2.¶¤â´¬8 쳊‘ƒò‚Y êËJ 0Ô|ºÃ CùëµG¹à/LZËÃÔÒ• ×˜Êø=¨§økÍa‹°½£¥ Ç©P 4lw8;Í™a}æ¿ü—ÿò oxCXÄÚÓ!i}êßùÎw–é^c›O³²Å¹Ýq¼óßÜ…MMk—[ý»vé¿ß¾)pr)ÈÈKêö¨ œfÄÇRŸó"+pd>Þ’ÿé?ý§8›,{ ㉃iP jsfŸI>K2«û(–ç²U/W"ã»Þõ®fuÎ9稡‹Î¿ äÌøš~ðƒœú|DÉ…jMàïx‡îcj&²¨·$˜×—6NÍnÐ4ÆKŽ:¯Ø…McHqžÌbÖ¶˜-i »,—¬ôƒºÄƒzœ²›ÁÒ¬€q| &2HÁÀ}ïÏüÌÏ4,§Ê•aÒ©”*³–]òeNì¶s"eÖÅØ­ÍAÍ9 ²Z ±Êj&é|}ºp,oóCñEMޝícÈÒ¬lq6:Omz3Ï]ØØØ¸¶(°Õ¿k‹òû½›'šNëÓÍ$<ˆ¬jyNŠô–”(Ò›ô| ¢ÏY>Y ªpv®YÊÒêCúÐèêùz1ÐÝÄ_@IDAT¸T>ýéOWÓ¯Ô ÊV㌮ÕSÂ܈§ >ÿùÏ_ÿaçSnÀ©/›ŸÌjDÙ°y®ŽúJ«ìÄÐ?üÃ?Ìå•Àz³›Ý¬ú@&”GNõóŠ]Ø8†(çE;zÑØÖ,ì·½ím9ÇISJËšãN¡žÒâ0 –Á8>°ô-ªe‹ÃÂ~ZÑaL•˜´–ܲnÜÒ¾ÖÝC/:§f†µE¬Š«G%™`÷+r/äªÚFì÷ OxÂ8ô.<ÞSÖȶ›Ïxn—óЦ7†ÊcøÜSÚØ8!Øêß ùGïÏÜ8^"’­¯™•õŽÿûÞÈ‘ÃüHZGå3‘<:r K_*»À›5pްEp mŘ·¿ýí0)M™:'ú=¢|èB¢5>”yÒ!¡–þ¹R}íá[¶Ä>¤Hÿ¬eÉÍReçÔ?ÓD!C4ô>ØñI±ÆëeSj]߾˛ÇŠA_Z«i}´£œ<ÕXØŽ9$Cwe+ü#ô Ð}ÕÇÅX¦JL¤Ca+G<•¥]eWKŒ‰=1)VU“"êÀ ÓÌÁP6–ìrÜ š‰J…à[ÒZm sø¢4¨EKg1¶ãÇÔjœ™S$ÅÕíúÃÝå† J´yÚèŠ1^ÝÅ{´¯›››×0¶úw |¿nS`Sà2*1‘Y rüñÿqó¿ñ¿3g­@F®"‘¨ÕëBQ̈Ç1ìÒK/%ù^ÇÂP:¯¡ã%PÎh \¿BœÊÜ,Ù%òãeššz·»Ýmý·±04xŠâ#ùHOC¿0ŸiI03&½´úÌ}¡á³4&DÀÉ ò‰%žŽJ¾œ1waSàR`rÜåN‰ÝJ‹gI[Ø4Ÿ1e ‡ë #̇ÄûŽiÖC–X[ñšܶñ]ÃvÖ3ݱ§Îw0¬æÍ´(ßæªšL»“#þœq½h\CÅÚFæH'/jò;5Žm§o¤pnTÙÕØ¨ã¿þ×ÿúÃþpA¿3Û¬ Þ˜¦:óß…MMMk˜[ý»† ¾_·)°)ð¥ÎÅ ~‹IxÜÊî@~Å))Í#rÙ±Û2@$ö «“|yx4Èàø?¡“¦œÂéi2eR³FëjùßH3¨’’™zvþùçÏ#…׿þõžB¢Ï'Î ï¨R³µeÚ£qä¼®žÁS3BmÑM¤FÝÿßÿûžf %Z¡yjLã]ÇÜåMãFa®\(EÁ²b%7Õn)`ŸùÌg:(™zX#­,Ïù´R–ÛSƒÀ6C‡ú©Ÿú©i¦P^M¬ÚÛ1ï<¥ƒá õ—}ò'®l2Ùor“›L½ÅÎ`“QÓ1P9èÝBl2y¾½·ë°­9mÅ8ìŽ pf¸ ›››×<¶úwÍÓ|¿qSàDS@ú/’“t*B -ˆhøÑ~T*礨Ä/žd²Êü-SØxE²„®©ñˆžYä4¾×½îeä‚îNåW迆íJ㢶þcÒB==p6Ó†ô©¾<i}¼Ý²Kpã\™hÃ<¾˜A2HMAu4ˆ ùº<ä!qK|LðEËŸ_OµŽ¹Ë›ÇŠlb-Ô¹bI bånª£„Šdñk‰msüîˆDÍR„•TfÏdI\TâôÕÿS9¿kÖÔ ½ú ‡»6í tÊ?þ¥z1ëÔ›mû’mÇæ3–ùÑ!ö°‡…üd&vƒƒî>­µéµ”ñXý÷d66N¶úwrþ×ûK7®} ˆä ße щneðtw¿I¡ pïyÏ{Þªþâ/þ"|ˆÕÜ'ô¨€"RñcûX@ ze—4v`Q¬²tÒE®òêMõ}4x DFÅ_üÅ_|Ï÷|OC™Ò®Ã$pg `½|ï{ß«%£A£¥‹ Vìvàs ]×±Ì4vaSà˜Pømë®-`·A_:Xéàæ¡}hs.Ó vÀ™þ4>°r˪W/ð›ë—¶]ŒG¥GØ3Ö6B;ÆÚ>¸QÖc¦Z¡U.è7㼚ު@ãÛ‚f ª ª7f·¯=5ï'~â'æGð¡Ã SÍ…Á6xtªë´wyS`S`Sàê£À×ø>Ív¶ ›››Whe˜F@¾wÑEñÈrvÊBjá‰ÜV™µíGôG›ŽEØ£†¤%Π°½d8ZŸþô§=uâ.èn>D±)Wf&»rˆëS™=Ò ¡¡zÊ(Á|A5ÍŒ)X—´SxñíÏ8œB•i§dYD©c1ÈÇŒpLÔ,«ˆBÆûþŒ#8pÊ»°)p¬(Üò`>YøU¶¤qY+œÝ¬–±vÀXƒK¤#ã¬ÜÊ©²^·¾õ­×ñÛ ¤Í¬cbÏXÃbÛµ±ò¿úWÿJ$°Á¥ñ#áÚ†‚çE| ‚$ÈwƒÜ ã$û’1…/ ÛK©³íØ|lAs eÌ9åyÎsžcàŒ*Õ¡#𡽍d3âýô Ùúð»˜aÛ`GTëLvyS`S`Sà¢ÀÕ§Yî‘766V ¢BYø¯ÿõ¿ªªNøÙ+°‡Rêɹ<}¥„ÎlÝé]pb´T ì5®Yë¡~}çu‰è{ùË_®pżk áÑà LdœÈÀÿøÇOK…ÄYv ÐðS_ÜG5؆YB•xêSŸZ›ûÞ÷¾Í*\PJl·:¤J.g3ì.l + Œ‡s«×uìl–4õi’dJÀÐ̱€f±ƒÆ¤ò˜ÑVó>}oýÞŒãÿ‰1 …IËŠmׯ•CæIõZ‘gÂihòÔ—šB=Å/ã°42sʆ36õ͸·Æ"–,ÞP¶,¿‹/¾xp­r4˜—‚º¡Fí®ÙØØ¸º)°?¯n ïñ76¾L,~7ºÑÞ÷¾÷Q¢F"AkxÝë^—ÔåJ›šØ‡èƒ+8’“¡Ó¸œ-¯w½ë¹…˜âúC?ôC½À:Ìühº«ÿøÇ?®;yTe™ú¾<˯üé°ßq>¤ŠF€Ä¶88Ù4g·R‘Š, ¡¯Ás+(¨œŸÛÉŒƒÉU—Ðóû™j‘£|~evûï¦Àq¡@p&-Ô9¹h[Ò¿ð ¿`1‚"Q^“ÆÃÐ\T>íiOS#¾Wyªsa±:vÍÿÓŽ%õž˜4vökËÊ,o41\ÏWšP½ÖŸÉKK¨†¿©ÎSûI:ž F;ªëvÆô†7¼¡wÙ¸: *؆ þW~åW H'dØ—pü]y‡Ök_766®I lõö~צÀÉ¥€x¼”¨d#ÂPb“J𕩤X¬sŒxôÃ(Eì[]¿GÑÒñ…/|a¶8X IŸ÷Œ ‰¡tã£ÕÛ“êøƒ áƒ4£=u…›ï½,“É‹ò'¢&7-⣠„Fì·þ#ßúÖ·ª$ï†=ÃÕ­§d;õÀèÝòp ýBÍÿüŸÿ³Y;û"i¦¥ôt‹™€Ûž‚âúÞ]Þ8ˆs;v™Lè-cVµòX˵;ÖÁÖ¶¦À¾‚3¤[ѰÊa aj9`2Öa±õKS)iVï`OO±ª[\ƒyׯ•³.NtñXù.ŸÅeH“÷¨ Ü6òY°Q°ùÏ6‚Ù'c„6N…Ú+¼ÎöeÛñ½mJ@žìoq7ÿO ¸¡¶/¹ÚÎv×l l l \­ØêßÕJÞ=ø¦À¦À—)@ãºLȺüÇ ¸e2VàéW‰D<µêóêW¿zn!oÖG¦,t£9JtP5DÀ×¾öµ€zÚ5µP™Ä–k(²ûÝï~jÎ:묣ÿ$ržGŒó(ÓevJàÔ+P55æÈúªW½J!põI“"kœ}ƒxÊN ͶÑz/À@¡Œ)Æ9v5Tƒìë¦Àq£@K4»_ÀN°eŒzÄXgÎnÕ\rÉ%ÊX Ó LÑç8ŽÉ&öš×¼FwOÅÍz„­ôÂb5ëÊ5TeL1çQÖÂÇìžžwÞyºô{ö³Ÿ­r²È|¥ú²¿ÍA«9”ªŸ DÙæ“³¨í¨ŸËz^ç:ÔE[V¯ËÝ´¼ ¡¼Ð }”ßmns›ÚÇï6Æ™ÿ.l l l \3Øêß5Cçý–MM7¿ùÍ$ž"‚¨gœ-;¿g1¥À½³0mÄÜ2…%G&'%“ÇÈÐy9Áž†ÃY™àøÊW¾ò”΢0Sð+ûßùÎõ;Ègv8ú¿)JÇ4æmPØ[Þóž÷L½B^^¾Q™g—6ä]áL f;^¬ª,7 ›§20¶Ç,'}¦Û,i²J¶^߸˛LJ™à¬U¿Ü2ñu(/d¹Z¦uf@3/µ‰ök¨^‡8)TrëõmXlý^QÄm&Ô³Õ/Ãó®+’¹l~×¹§|ΛaºVsÛÛÞ–ª¦&<˜i¸¨-(žmS—Íl\¦a˶QF%\+/tbVA5¬”=më˜Yí¦À¦À¦ÀÕM­þ]ÝÞão œt ÐÒ¦ùÀHHÊÐW–¼R9w¾þŒg7N’¢24$9ùfF03À€ã±ÉÀ'S/†…Nßó“ÐiàÇxzðcKøS?²Zm¸iÂÖäJÛàé]îr—ƒG½úgögk9W-µO žT} lÓŒÛ[Ê­ÆÒZL}i’)]y iÖ@ÂL(>\û ÿÓ`~†¡vaSà˜P€£µ%zÀn÷¼ç=M¯%ÍÎßTñ‚–ì~Ãn;þ¨AðH†ÊAt>si‰Ñª)20«8Ëÿ(Š=Ŷ¯Ý [ÒÊ(]0eé%fp½1µŽ¿ ûUšÞ¤‡ñi~ô£SùÚšœF…#œécëhS>HÖ4òAñŽw¼£jd œ„úRþg ¯{Ô'šð\Pá=4ÙæíÞÔÀ9ÔW˜ršç‹_üâ^ø ¡(âer?[ ŒsÛ·›×~ð°¥¾§LR“ lƒ¤e‹ ˜pX¸XƒZuË[ÞÒÓ4@ÅõsÊѰ[JZ§-¥p¸ë]ïº6VNÃÄÈ~9 VÓÒu¥–/´9zc¾4¡v‰b€Ç`8ÎM=íÔ%ՄݩӫL±éɃz=?…ÐVyð!ûvS`S`Sàj¢ÀVÿ®&Âîa76¾$å1ɉö| rˆÚ„×Ä4;÷Üsk#ƒ–Sþ%{LÏy2Y*Q)¯°‘®ŽœÍý7¥ º¿ÈžÊ–&Æ20²döƒŽw¾óŸ Á£ tI«G¡&Ê&ŸèY&kjžî¡ü±K 8ÍÏU›?Ós¥òßù;§š®p¦´o7®u ŒÃv|Z𝸫õoa7I)Î[ɦ¹Ð€±FÉô¸D–ÃEñà» ŽÝ°^Om/dy;ðÿļêegi qëhê=傾VV·¦IžòÚñ ¿Í¹ wú£qÖB±|mJ4º\C=çvå;ÞñŽ´ÙG<âʬ—…üÙM}ÑÑYíšMMM«œ[ý»ÊIºÜØø2‚?™Sm`˜ëˆ}«½.ɉôóÔ§>Õ‘yX/¼"Gÿád•pY/†z8A"T¿ä¼¯Ü}õïK_úÒ£ÿ yù´p„?XBÝïE~“š¯6ð'2N’.r[H††ytL9Ì/”)À‡ç#Ç=¬6ð›å„9eTI h¡@R¬MWƒÖyÝ.l \+° ‡‘3‚¥ódÖ+p.CŸé}æ3Ÿ©M^ÓjJ½à°£è8yS‚î”"ïàsb(Œ€éV””’ˆŽcy½0ï¸nþÜÏýÜÁPe‡—Üï Þ­íÂ+æ‹Vî›2ÍMK§6Ù!eõ,õh 0;íntBçJ¼OÍgjŸwC$:ûì³›F^6Ì£³Ú5›››W9¶úw•“t¸)°)pŠiq`?ÙsšJ2£°bGþθ—h˜·§cõ섞ÖÞõz×»ž[Ö®•“DAMŠ™ÂÁLÉÐwôŸñßùZ„ ½ímoKô¨(£µcÖ<ɾÖJƒçÉåï@1+*IxR »åüÀ¬/cØl™@’„š`$’›E Õf®ÜÆÖ ìò¦ÀµK rg…3Ó_¦¼QÀÆÚ/š6Ô+þ ¬……§c«ptk†¹BVÀѲ†ιÒÁÎÓf·ÖW†Í›9î÷~ï÷žñ#`»“îoN Z9?ÒØ&ôw@ƒÕ7xJ¦o/I½M2Ûæ*{0Ã}»)°)°)p•P`«W ÷ ››_CÎ]r‘uB´cÓ Ú$JØ^&8·#¢½îu¯K$69¡Ï!³ö‚“óÈLo}ë[לï²WKWÝ;YWæèu饗®3 xFƒÕ†P`I¬‹êœ qrêþèæ¥ÙzJ¾¼Ü‚ø×Hu¹Ãõ93§M•,øÞ÷¾WÇâý2Œ1ddÊ^Dˆ\'°Ë›×.F« q×r-‰åJ%cS°°MÒ"·Ôã¯sÎ9§ic‡6T§/|á U–i]$Þ|Z®’µÄtS¯þIqÊ›Tm¤Cí¬§—5±©4™üButþRJ†^zT´Á‚ߢ<´Ö—½ìe5î ­µˆbo)¸ÑS§Hö“HᢌíQƒ’m›ó-3·]ØØØ¸j)°Õ¿«–ž{´MMË( FŽd#ÆÏ:¼õ‰~Ãóþ÷¿Ÿ|“RDP“ý<’Ñ”Žêrp#䥂݃«ž€å:rÞÔ@Á<¸eÊ+ÿWRoì®·»Ýí<¼Í©W~Æa‹ !"åm¦Z¯I“ð:ìÝL£qÆ»LnÆ5ȼ.ˆˆÒ»Ã>UŸ«'ÖŒœº'Ÿ±QÆDÍ€»°)pÍS —fk5õÏr-pWœ.¹g[Ø&ÀoF<OM•Óu¬ñŠW¼b&_zÌq“NÃÄbõÅtÓ²Bê"û›[oÏ'“³h ¦>hïöƒü ÷ÚRFç´EäsnÓÈ ‚¨ŒMÏõ¨G¨vKv~ñ~Úóüœö ±íl5vA‹áæêyhé6I[eÂù®ÙØØ¸J(°Õ¿«„Œ{MM¯R@¬KúžÓñU£#Zes#%'­Ù®²°A_È J‡î24üùŸÿyÀŽäGß;z¯= >gzT ¢¡ÅлÄÚ™ÓAv¹BSÏ&ÀJY€¢,ÕÕt4)Œ¦Œ &˜ìd“×%hø‘qÕqAÈûâ¿Ø˜<âL˜ô\|£Òßÿý߯æüóÏ žcà^TΡîûº)p(0 LKÔr-·;EÈô,fõvÒ-õ&qº§’¸äê¬ ¶Ï b7§ •%‹a4-1Ý´¬™Ž±‘a¿ÆÅâò'×S´ï6v+ƒÍ¡óÛCÎÕ1Ì^¶ÄÙjÚ<šŸízpg4¥%Rü&­¨î€zi…îüÏÛ WŒ_[eæD›çAìñ)?aWn l l \i lõïJ“nwÜØ85J®Õ1ù´§=míl0¬‹ú{Ýë^Þ¤Ʌ¬”ô)œu™\a“º òÌ ™4vð/ÉÈчA ÛÂ|¦ñ×9ìò¦ÀµK ßm‰:»©&”Z‹Y½…:¨¥^Þx-¦]ÆÎoû¶oÓæQzÔ|Ha±ô±O|âù`.OÝj‰ép>%mWïl%Ý[fCadõ˜ú¨›·!ñš°m!3¢l¶Žö›‰–L”cú[w!ƒ÷kP–ëÂKÛ~äG~ä—~é—:®²õ9îáD`»›œ¥Ï|æ3 î—æÔ ñ¾-ÔÓýÛØØ¸š(°Õ¿«‰°{ØM“H’Vv­#Yò¸c•lÒkI$趸®FúQ ÆMJ±Fs:ž~˜¸VeWÂ"SŠßï~÷S3x*ý&v¨3~уGÿ7ÜÏtüÓ£±$°f47ÒçÚ«'âc•DÒ›ÝìfF Úà|]ñ<=½õ­oíQ“/¿Å np5$Kaè…9†…‘,Èæ™Ëœ–ó“{Æ.o \[°[–s“»µJ*ŸYYÌÊ-l‹ÜR/&FŽÝhS /µÁ ëWt’^„­FßÃnO"ÁéRK(Šã³íi±|«gét¡wå°ª—ß»«M(56“ÚsÔ¿ÍÔ³zaóùv5tú3±ÜAÿ÷v6*1[¢M¯í‹žÌ¯Áøå4¾Ã f#»ÚHO©¸Î'ì¦À¦À¦À•¦ÀVÿ®4évÇMMC Ь_Hxâ[<άÇ? ì»Û~ô£d&mwAŒPÞS*Aƒf‚cИêXcx„æšÕ[ºò4“½]û?ýÓ?í$Þ˜n×Ñ*©KÀÏx`® òÏ”l*¹leO𽩯PNBf½©— l|5³oÌ#‰+ ’íÑ"EF’”½``úxz…ü©J„k¢5˜ìˆ3ø.l \+(ƵeÙ]US²˜{êšå_ezT¼,¯”îdýFò:b(l5²¢c½©QëœÆ«îçQé±öÚ¾²M ¥Ñcs8h`1›‰-¥GlŒ³M­*Ÿf*5až›3i1¦ñ”êQWΫ~ðƒ+x¾ÍÓ¶ió¬Á)ª¦ºo766®¶úw%ˆ¶»l l œšR–'¸p›ääéx;Í’gJº@asúN´ Ÿ‚çúìg?û!yÈÈ=â…³è{ ‹=:¸Ò£&sz /W”;k`ýXÞBkXêê{ÝmþÙ|p"]ÄZ/ksƯ˜z¶Ms&“³f dúi5‹(³zMàPÊpÞ§}øãÿøy6®E pNfœ%è.oÏ&–Û¤å=óæob”Æ5÷ö·¿½‘1þAtì¦ëÍh^xáh_v•ÌL쌱äjŠ×ûg~7š²I΀S(g©-ej`·¤×ÍWWp®Äâ7. ¾kNÁlhºgÔ7ü*‡J¦Ú«Ûˆ|{Ýe°mÚ<Ùv:oß…MMM«[ý» ‰¹‡Ú8ÑÜ’²— ’^YÓ|¦x’ w,å{tØ ¼¤ Be8ãŒ3ª'q Ê}´šõJ;Rb…M®­õŸñ‘|Äà¯fsbÍ¡wEVêëøzA’˜™aî@ÜÔ ÿ4O¥ ¬=/·AYè8ª,„ÏpL÷çD7¶Ç'>ñ‰óÕäìéµ ›×" ÊmeÎ-A Æ4±Ñ‚,ï™g BgËô­¾q°Im&ƒþšó‘a·4¨¢òD¦5 #Ìmr…©ËQ«>Æræ;¾ã;€Sãõjñi¶”q=õ”ž¶îZ´e)0åM\ŸaÙSÛÚX9Àƒ¨‰b])º½¾±t+ñi[¨Íj͸No—766¾ lõï[¡Þî»)°)ðU Àj'Á0²]tÑEp2“o\óWÇ’èÆ³q‚úò§ºÍmn0åÀ4Yû˜DÜÈ9£MA³K.¹d^ÿ¼ç=Ï#QC"c Ê@-4Èq{•Ô°I?}E1BγÅáÀk«ÑBð p& 0ßh&œÜJè÷ØÇ>ÖmvmF¾Å-n1}ßL¨Ê‘âÌ3Ïìc‰†%Qt+,pzí¦ÀµH2+X“R Ĥ,J凯-ã±·[ÞÍ“ê•óö ¿”Í›hƒeVïP;ÉÁb:oÄ€2:dCû±û1|W¦åƒö1½X¾ó›€­ ÿÕ£^£±‚hc™1R øp¼é: U³/•¼a«ÇË67J Ž)’,­8(ãF踊BÛ稴yúð¨7 0f»°)°)°)ð­S`«ß: ÷››_"ö%·e¿"ü ±è_œ„›üÁ8mÞínwsKˆd˜§‰D®„'Ñwƒ£æúTRé•î9•½à/X++;}Oæ+&ÐmRIÎĈ¤†eF8zÐ^’@Ú×k_ûÚÄ>8ïÆë§Ë¯þ꯼ëï|§zRãÝï~w ªOkÉ ¨KX/œÁư àGûp}W>ŸÜ$H>`…j£&ĈƒWïÛMk’¡ÕØhÇŠª·”ó½%]0Û]9úôZ‘-;dÁ&˜¥£ìÓ†pÀã>ÓéîEq´á£0ï«·Õ„Þ9¤ÀÔY±9f‹±¿M@Û ŽÜ¼§Ël&ÇËS}\ÃŒf«Áú£IÞô¦7]k”}‹1þç^Ù®ˆD?OõZJ6Ó$ÝÒsyÍvŠ’¶Ö™Ò.l l l \%ØêßUBÆ=ȦÀI§€sw²Ëù”é‹à¨0D!ÞÅË(ͱ,1ÈÕÓÔHÒÒ=îqêÓ§ÍDû’Ig#½yJk :åàŸQ¶±¾…Ø'9X–§áh~°?û³?#¼šOâ×k^óš¦ÁXÑò@[ß• ‚‘ Z–jù™ò@GLË ï½}cÄFºÍ( ø!;€€¥tZŽm¬"ÍÖul)3à.l \Ãp43 2\·ð3)6 t˜ðKZÒ…þÜRøî˜ —¹›ä2àö‰#Ê8-0ݼ—Ö4(eÕ?êÿ™e26×ãcÿ3oÕSÂÃØL:ˆ‘žôïý½¿§/%MP}©pG5@{…6ÜÅÇa¦j‹£ýæø:Èr?LÌŒlŠM¯à£&Í™ü.l l l \9 lõïÊÑm÷ÚØø*o}¸~•³S,„xL%qˆS8V•0Ú:,Љ¯#]å"¥Œíºx[±­É F «WRâcRæ)cxL ˜œ½èE_ñå%â×Ã]hïí+è #Æ(¢ØžuÌ*Ø‹À{fX0ˆ©®rêÄ‘’aWèû ¿ð š‘Ó–s#bfüt»J½ò‰­óÜåMkžs4ƒ=Çtoebm+¹ŸÅœÊgyszÌûQL¬§kK¬1;– "8›<=êàÓ>ðÇ7¼á "oŸò”§¨_ÝJ답›f/ÛÞ:¦×ùc–)t}¤\Nù¶/,/yPWHÞ^1اíZ¾ëmt\:ÕçÉ|6ƺÿðÿpVÍœl§FÐæèî4ÓØ…MMM+A­þ] ¢í.››_¥/ýJ¢ªjK¾ç,Ÿ`'[z’Pò«Pº\ O²$gTOKޤ"‚©}ŠÐôõh€àÙÿÁ?øå`Æfø'ò'_ÖWJešæbzÔ·S“ÉRH%;€ 4âÀýŒm!‰íŸÿóþ•7|õoXöUU“ÿö2d”¸îu¯K[†+ãCþÃøĤ[·¨—ÝRÙ<“ •’¤}õõ»´)pMQ`RíYÛÎDb ËÕû;"¡kYÌ–´+‡gÖ3¹OøjbjíWC½A4Ã,X¦/°«”…Ö4ßôßÿûŸd}GÍõZäÀÿSwÚâÑlP3`…lnkêùêpv £/;ä[©6"ú<:øqï­ÏgΓŽöøÔ§>U{[ß°î¶ÐvÂvK6ÒrN´AyŠ ÓÞ·›››Wš[ý»Ò¤Û76.£@Ñ86Þ’nAqïè0 “ˆã\ŸÝ/…-‰‡“䪀Q¯ãXØ’)kìêvÅzñ®/|á cy U6Ëæ´üŠ0¼"uþcF.ð üÇ>ö±ºRÉÈpD·¾GýG–]Þó%¹¡ å3×ÊÞ®žïhõ¤^Ã"‘è£B¡€öKSžuÑžg¬‚År⦨²ë+vySàš§ÀœË8¿°>Óß,W3éüw+[Òž¦û±že¯+iÊ`ö¾úÕ¯¾l‰_ç:˜eýt'–=ÐéfùˆÚIÖÆ•óÿœ<ïØ¹wÅà˜ýh5/ѸVs¢ÈÛ>­=ŠÃö)û²×]>÷¯¹Hî7XMë§ ´&÷W|‚-±4ögaL—3A¼ÑžÖYØÐá”ÓØ•›››) lõï/E®ÝxS`Sàk(@Kh¨ $>œ)oò©)Eg,{#è(°àM˵~-““¾æÝ—ßpI”_èXjéì¿ø½Õkkq¾ÞSÂ4U¶ð-„úÀ{mWàŽeJ¦šŒû˜ÇêªK¯ÁK3]ظf(›¤UíÚaM‘®–«EÛ£Nj,én1Ú$ñKe SL(ïA¤WR}á¬ø(£µÏÜò–·¤›«ùúÉÙÖŠ@ÆÈ™ 1·™C&µµKå|Î_ò’—tKIKmv{þùçÓÍxoÚjŽväÔ:DèK»ÊÏYöB·ÇXÓ¬Ü0Ì¡Q L0^—¶¬y.©Aãø|›íÑ ìšMMM+A­þ] ¢í.››_¦@yÞÇç›tÎ9猸C~hüs[å¡¢eD^¯õéˆJt¼Sç€âÔFbÀ8^hͯp yûÁ¿-pÂÒO3÷)ŠÑO汜¾ŽF •žË!=I1 DMÃI¿¾]}ð§É»,$dvö–^ªA Åà †„ÚWë{Þyç)÷iY0z¤¾—îë¦À5OR5´]-æbV-˼J)×M,L&·++•……ª†2Ænú®ß§WGXš¼:;3¢å• 3®í•òè‚S¨Ly`šÖö(î¦×téV½Ž¾âóŸÿüìZΛrøìu¡þí^óŒMìo|c¹pŒì·oµé1f–q> ,mJ2ÁÙ!ššÈɬšWäÂptJ»fS`S`SàëS`«_Ÿ>ûé¦À¦ÀR@›Cq‚ŽàŒß&‘ˆ2S,œ³óp/Ésé9Äš{þñ‡Dh|ï{ß[™ÛçD ¥ƒº9˜Ø¾Ý¸Æ(Î6Ì+™^Y×[®mEÀwG[C×Î>ûl«ú¬³Ît\ ‚M ˆeæ+Æ,–Ýn6Oó)ÅŒÓx 7¸Á baWìlåGå§åZ°]d‚ ³Š½}E…Ïd4[ÍL`íË^ç©p¾ÃiöFûÞ³žõ¬n]cÞ¶¾*¥»h¨v*j¡€C5’§kÍÚ 8qÅëvyS`S`Sà/K­þýe)¶Ûo l |™e´ãÅËÑx áï×ý×µÚ+ÙE0›[6Ä5„¼Î×yp­GæÎì7¯Ž®`È…t¡Szo•цˆùå }éKNýCP`4È üˆ€ÓL!C™ï bð‰O|bo't¶× ' З¦§…ÝB°[_!°ÇPNîUò+DÁHOúÓ§¥ôÓ‘ŽÃ˜Êœ?Sk³èòüç?¿Y%¤šÆtß…Mk’lS-רÙÁ„··ª[®ƒÐ|b@ëö`¹¾îu¯S™¢…)rªÌÀ…eÖoø_Ö°•O1`îø1:bØÎY<ÅÈëPºÇ;§ÜI´椗ñ¨–•kÃYÇœr“çÚžSã¸zã̧Êl’6À9ç rFà_ €!7,ÿ5vÅ #t{«[Ýj^º ›››Wš[ý»Ò¤Û7N4:ó&ßpøL±! Ã(ï©!}æ‘|ä(‡Žáy4y4™÷”%Öß™gžÉè§æ  .º‹£ãyz`Üã*dB}o”^ù”ÿ§ÜV™ Ž>-ÛžÆs¬62¤y3ÿÏu´´S&ˆ*åî°uÎSïòC¡PDOåë^÷ºÁÒ(“§9¡eJüÏðñ××íò¦À5@À*—-Ö¯ð׳Ÿýl/MåSiÑŠaS€uÔjï|dUÞh†—õ¿|sÀÍ9Ãûª_}âŸ(êVÊѰ·L÷…Ë6‹YžF¶Ãh†Ú`vŽºPrøì$ËSZ™Û\¯ÖSÎ)ÃnÏ=÷\Oy¨âÓ>ðÊëaPšŠµ†ˆ>ÙôÔó†¥T;êªÍ³Wßâ·Pã|MŠˆÙ:¤ \!jÖIîò¦À¦À¦À7I­þ}“„ÚÍ66¾J§ìîM:õWÈ,–hÅ3ÅL½)cÊ~Pþ‚0YÏ¿ mé6µéÊf茟œD‹—òÕw¥”7×)2’ß@n:/?Èè`®h9°•vï+C^ö—óF—–ÙʹWƒŽöÂLJcÚÉ£Ãó,çu#”ôLK.aÕ¸'“i‚èLÔ€Q…™"Gš=ö±UŸ0­€°3Â.l \c¸ùÍonùÍ/U¤Õk¹Z´AÔÂaÒ†?vJÎ Ó2ð0¥PoæÁb™n1Ýì!@}~]ƉZûv$p/ßõ]ßåÕ"xzåÉñWOTŽëmTÍ\¬gtt;‡M¶£O ØÛ q”¨}è£`'Aš kgR*y{ÒWçm’’ÆÝNÓl¡Þ“ܑ.ÎÔ|`zµ¾«Ò{t2»fS`S`SàR`«ßD»Á¦À¦À!ŠÞI”!6À›‹@£éœó\ʬ—#4( JXK5D"×ྸõ&Hô<À™Jê«r5¸Tª0$h6Éi•ÁSIVA°§å±\Ç —rë»äô4éŠàŒ3…Q_ÍL%±Õ[~÷wwj‘Ix ä¥lÕù•ÞÝ¢MEQc¡vRèpÊQ3ÑFe‹Ü‚ϯ²H¹Éz§%}¯ŠÐØÞÕãb{ ›üÊtÊ%12Æ,üúáãÿ¹>bN››+‡ÏµWå¼UW ÒµM¥¶ •¶šÉÏnë°Yá}SâþºvY!Ž=ŸÓœ¼I§†mRçdjj¸0°%rˆí <Œ‘ Šf‰]߲˛››ß<¶ú÷ÍÓj·Ü8éýœ|&59Z#âÌæ(<eGÔ°ÈmdÁ±û9ŒÏéKFÄQ¯oˆË,KÝ[ßúÖ*9qå`FÖt@NˆôŠ+Š| ]ÌÌ 8¶µžz#¹íÀ§Ôy?QÒ±úX¦cNÀ-Üȱ½d}=ÕËdÈmk Ñäz>°o„/?>i—Fº¢ )Ì)Ê^"ÅpŸØºzÇÕf\ìfª»°)p5Q`b¬½¢×ʱ>‹3ßÅ?‡£ šm‘l¦€^ƒ0t7ULÔÚV ·ó`þm o~ó›«ÇzÙÍ0c‡#k•éÛ ÇÇ;š^”Ió9jú«oJl 23Úl;úÚ‚lD©¾¶¦áh›@'bÁŠ)ñl¤¨œ»Dûd5 UîJÙvȹÀ­£4>®¶ß¬…§üð™ê.l l l \¶úwE”Ùõ››_C‡åASÒú-‘ÞÖ׋Ê+¯^¢àÖ§S&'5«°:§~ ¹q&c‘VPƒäÔSž©S&½—MÒÙüu¯{]e_|±^¡Ø¸’Ò3©‘MuÍñtû f~½–c',9D0RQgSe- ßBË5‘R‡¶:¸ ›W,³1€; Åj´,3åÙËb_Ε±®[Ø1š1…!N¥SÅÚiŒY-iì³~‚ø7ëŽé°ž[lˆs8åYÏ/ýÒ/iæÈ‰fÈny¤³¤å¡zÔe´—z{×£p®“Q^CpmJ(,½÷`ûÒkN¾Lc6@6½¶5ŽôC^ž … jÙÏ^J}M×}Üãg´ŽÞÔ_‘ ó`ÎûvS`S`S`¥ÀVÿVjìò¦À¦ÀR@J²hõŽpHé9#ÐLAã9ç&¦ªž4³¶9Ðý8ku|¾¢Μ@§ŒÙPPÍQÍ'ç« §×ZxP`R¦ 3ÄF›üÓ$~XÛWN0 ¼žñ!£=S‚æ2ÿý¿ÿ÷k¯Pì³XzÅ/ÿò/÷4—U^$›¦!ó‰­> bk`Z4cBôG@âïº t}û.o \åxOKÎâÀ^ËrìÕV2ë|³³f[Ò9{{4±»Í- ?ÿÉìÛBòô=š+EcÜê¥ O°[‡;0ó{IS°çÑï•ÈA/\ 6´cÄéáÛ Žöª†Š¥/4—ƒ6œ‰å³Ùޏç…5§Ï¼g؃—×>JýlŒ n&}•¶ÙŸüÉŸÔÆÑ§›piiÖL3ó®]ØØØøúØêß×§Ï~º)°)pØú‚7âåío;A$íŽt’N•<ç¾#ù‘r¸Z1èÕXåèoÊã!6TÎBÈÀ55kš‹^Pã;)§ ¿eF A­½¦ü–·¼EwºÜÒøtWGõïÿû• ÞàtQ`pЋqcpV„ô§}@…ÓµK†D˜‡}ŽoWÃ×Ô8¨´¶TN·ôˆå„åA ¤2[ノ~nŠ_Y†Gó\ƒy± ¸o7®r äKi)úç­€ñ-˰‹Ôt”sŸûÜGù?þÇÿh1[Òµ<ª¡u†’¾'+ŒÀ9s&4Õq>ÁæcÛHÜŠõ&KfuÌ?]*dl×W›×½îuóT¯PlS¹°ªwù­þá¶šNdp}‰+Vp©µûžð/e·_+•í9“m"²\^µƒ©wekýÑ9%° z´PLÛò*yðÆ}»)°)°)pJ lõï”dÙ•››_C3Ï<“D’ØAª ¤A åúŸBÖ ¸o©IpQ˜ßAÍQ„GÚÅç¿ùš×_~CxÊý‰³™Ì%ô38q0)”[Ò†·Ž“[𠩤Çfhþ’XÌ£)$Ýê;5 &3v’™#ÿõi¡>¡Gä æÛÃH4íµ¥rñ„ÅM3`žuÖY ¬"aÚ-6bÒ‡ª žîߦÀÕD,i³ä ôV¶ ÙÓòÆd¾ëíùjâšôÀ¶E~0·;ÝéN ¢;‡Ìžb•GQÄ#L‡õÖ¡bj¯[+µ¡£¦21<~ò“Ÿ\Ÿ*ÛŒvÀÔkYã5p’¥ÒöÒqükó¡è*ÛŽ&Ó€‹;,8¨Ùæyó›ß\߃_¦!±oˆyàzºÎp—7¾u ¤,µÞ¬Õ8";öäD™¼ð~ó]´¤-l],rK}f⸤ƒëy=¼È-ûLKl•Ÿ¹·¯H¡Ó ü¤5øtSÉH›°“±N/›€§Þ~ôQmØó5`îã ž.g«±áô¶ ŽRÃfØ)<úÑÖâñÔLA¦ÁÈšÞ€'w¦Æ˜ÿ¶·½ Åz:WÛ,Båÿ©Ò’Oø Û2ÚÚ¢gü]ØØØø†Øêß7$Ñn°)pÒ)P¾ –`ôF{Ä#.w¹Ë]È"¹/P"nÉ.á¤ÏŒ3xwGiê\¼q^üâ}ªŠ¸S®O×ÌÉLûØÇÖSΩò”®’ôFÞâ|5I½ô…%CÒò]ÎhÄ š·4W€9ŽÒ]ÓWÑñ—“âyäSŸú”¾H§²üïÑp C™5ð K0o:³Í´GG=ëÖWìò¦À·H¼»[l^Ìrç;ß™u‹/w·ƒ¤šQËØ’öêNˆ&K'ž ÐÈ€PC×¹a•ا ÎJú’DkãÊÓ»40¬ÚfŽÎMÎ=÷\aÀ<åÙ­À£¨9:rVMmüÊ밶ɯۦ´VNÙ¬r(X³w¿ûÝCmkd×sjÃDÀ\*:ëÑ Æ6[ãó}ÈÞ'ÛË%c‹žWï¦À¦À¦À7¤ÀVÿ¾!‰vƒMMùƒ,•7T"K^ ed&…侨#WÎH ÊV·%•\¹ø`Ö… C“¡¥ø{ÞóÉCÙüjÉ+`L¯{Ò“žt0rm’8ŸûÜç¼B’¢¤Þ2*h(ðGqü$”gfAA€p¨ŽÜ_ rT%ûîïþnõdå_ûµ_›9p$S9`÷YNÔŒÿ›Ïqë‡hèñ8ÍÜÚ#Wƒs6®B XZ-³Ô¼Yx}ìc;¯É ÛBí¥³DÇ_€Ÿ¥®l§1v5ôÕ½11V _sçv?¦.Å%2ĉ1ÎU/LBæîp‡Z®WÎç&`[X++Û@|NZ–ÃxªÚ؈RÏlMGGP¾Åø¼F•µ°aF@ŒÌ÷ŠÌ8yj¶î6±ßüÍß”C}ê«B¿±ÖŒ`xr˜§œÌ®ÜØØX)°Õ¿•»¼)°)ð5 «užÑ¯3x§ÚRfqßêÑÄà%x9Éw¦ò+¬¦‡ß#Ðó&7´Ã~*‡îkfð•Rާ¬m_©øš¿º§¹%$7/¼ðµť—^J¨r¸¾¢nNƒ4C– A;£åRÀ|~Æ7 1Óx ÜÒ„HÇFaü—‘í\€3@IDATr‡Ú4-+„ph†fB×­2³9¼çZ–o-Ϻ]º¥.4¸´=ýéOwÛ?¢íºã¨½o¯* ÷k™eÄSÈÜ×8Ô(ÒµðT‹¶f¸~¼=s°Ô93‡¸k‹>Šêr0Ïb1Vò.l…¹´i·YiÓ{jàGzWþáˆ*ôˆ9ñ¨“§dƒ6ÌØø3š­#}UÇ4À~ô£ót-dü·5­•Sæ'o|ó¹ûÝïÅì‡ÜãçËÚéE~ù±WFYOýt_Þ maÆÑÜäµosFûÕ¼}666¾¶ú÷uˆ³m œt Á’D"ü,EN¢?t ×a­“Õ¨%…½é’AlœEÕ°•%–É•ˆ‹Jí :¥tJÃ亥ßQ‡þC@ù ¥»SóÔ'b+ÄHZ,ÄÝÕ‚ OÚÂßz®* XTy2[lsÂ2ŽÇØäƒü`iªåjÑjé7æk3éìÆ‚ï)û¡”}Žlâ2Œ³Î6swL„¡æi/¾ßuÌŽç¥M €5ÈÉó”þŸEÚÇvaÓ°uÊ6b_ŠÝÆ{§ò×ß—èºy ¿‹LÎ ¹bÏéuÚÔ¬‚+í:QÛÚz‚ö}ß÷}èLwmËÍXZ/Ûõ:ø.o l l \¶úwE”Ùõ›'^,(ÀEÊ…E*bõz×»Þ¥Ò/±hAŽLcà"£ÌÓÚPG’CYžò¡0p…5CúJýûÝï~‘ a­\Ë"ˆ4Hã"uIš\´ å-q0`½Sæé*-5@öuÀ‹.ºh[;•/im œJyÿûßê}T âM&èÎyDÎSI’V“±‚€ À3ÅÍð´`ž‡£)Æ\È*DýK¦¶ŸÑæu»°)p•P`ŘmíYx)V]ÊX¨˜Ö§åª2uqUÿbмõ¥16·ë_ÿúÚK’¹Nu‚]õZýÀK<èdmŒ1Fe3Ô)µ¤wûÛß~íXÙ† —ÍÁíš×ÁbQY^A&Ê+:[±)ÁµnV¼Ç3Wzj'…¸6XË!Zi6¿T_··‘*ä.›¶ìÖ‘™ØBQ‹ÊtiFÈ6[ÿ ›ö:ø.o l l œ’[ý;%Yvå¦À¦À—pA»Ë‰ÀÄ@—6¥Þ°ÎDat9çÖ™ªFÔwÍ 5ÄåÈD±/©£â‹#ùÜ¢Ni‚3”3us ï ¶„Ê53D¾g>a•&g1¼èE/šš æÆ²wù'^ŸÏÕÚ¦4ñã¿:FJ#ÈŽZr$µ­f©Ž¦ad=¶Môx@O¤–ºZÇl¦$Ø’³¹Íµ63]ØøÖ)0ö«±óS±Zrò°w,R^õªE ØIƒ3Î8cÞžÅ[%뜠¾©§’©œ 5Ì2º&šf.¹ä4M½|w·ºÕ­TÚgÒ-ÅþÍÓ)°Ñi@) fêl[µEª¼k›Ž´ÖxÝõ©MI/Ô8`ÌÉÝG¿ «éž÷¼çÚë œ}Ò8ó‹«M¸ý$ßø®š9Ø’çæ†7¼¡ò4V>¥ƒÃÁëöí¦À¦À¦ÀVÿöØØ8²ï9N–´`²'“íX®Ê¯@ÔÈ–îG“Qãç¸zü?«qåíyŠw|¥ªîŽðY½èB#Ki’Ô×Ñm.Ê=\¤&3„9€ ¢üÿÙ»×—Û»ë ûü|§P0‚ˆX…4bM©Š­ÅV±ZZµQ ‘‰F­k«±Xh¬QDÒV„Vâ!í‹h)Õ"Ú€¥J! ¢x@üîç³ïoî‘™ù[kí»Ïó´÷ÞûšëÅuÍ5óôsαÆy°ÌlI*?ñ‰O”ˆÏ…½ùÊñWWõ8„-˜Ë 7yÖA$~ð[¸V®å–í->ó™Ï„ µÇžQWBAÞ" slÍ Àòl_üI‚¦.ôX|¬` 'Ôv$ML@!j¶_P÷Œ| 7!pØ¿›`9•O¨"ž~(Œ´RyÖ¡6Vú$Þ ]Ò#–H4xk3å5%׬) ‚¹ ¯¾è?6™ÔŒhÇ":üÈüȵ¯ Z‰H7ˆ8’¨@NGk³ Ãnæ£îÃîzkRö`Ä®¬îb¸[jfcë€ËPƒb`èKÿÁ§ÀkãB䇮oê3'+Îø²¸«¸H šÜ/ûÚß}èC3Î)ü„@¡MÖFôËÁ4z?^ÇUeœXVš¤$“ÞlºT`ìmp:m¤+æÊ¸8å ¼rk©ÊeýöoÿöäMT÷±UÂ/¹ª.ìUKo®â'm×*µ±¦{Y×)fLÌÿñ?þǵ~ÊP“· ‚ Cb#%‹_#ÍÈ¡Áj%>}§ðßø+œ+ÍX9Ûø°“:¢6m%VºV u_å\3Ñ)€ÀaÿÎ188Ø!W á:ZŠ–éío;ªk±fO-°Æ$@¥ÑŠ.é¯fǵM–†- €GD×C“<¯!ø¶^ó5­Ú$N˜ú)8a%^!+/r.‚“¢=ÅÅ•ÐÔýcû˜Ž£Œ˜›ø‡ÈYªBÐðôjÓ•B£`¢½Gƒ$ßBí‡{œå¥ÍC8š®ÊR)õ4еã9Y„2Иëç fÆS8x“ÉÎèCŽ\9èŒãX:œjTÇUM¹RèÁ|«É:ñ+¿ò+·ys½c*™Ïeqe´É4ÚUÚÚ—Å!q+#±D3Ö¬›2÷híË>Ü+°,ÈþÓõ‡R›C ½T7ní5å÷¼ç=^ðzÙ5€!©Cƒ%ñxd’:+¤Ü3ûÍÜ¡µ$u8Ï¥VY¹Gkfbxë#ùˆÅÉÉ ·µ¯O‡ý{šû~Þú@à.††È.  )$´™t16… +ŠjêV ë”ÒFkC¶=Q7kƒ‚œØëÜË÷ q²ð{1c4(мR>pá‹~285»MÔñyö™Ûòr´£g˜zá2øÄ€EkNþ†iƒÆµàþá®Æ¤\¤ÔDÆ}ò“Ÿœ–ư–%­ * ¤1.…Y“IÀ¯AêA5Y𠍯ç^²mÞóõ@à1JIÒ¡òwŽYš¨rú9É54[ͲÒ1‰,c+SJOG}›³§cWÒ5™¤,®ú+»XôÈ>®aœ\U\Ûm–¾†(ðx.~¸Å¼‚ISBB7;ªìbbWqœ§$¢ •Ìësµ˜»ª“qê×Ti0Ìëã=û“’sLë«gïê£ÌÍ2§GeËÃyfy›änü”Vöo…Æ)¼–ÅWDè³ÌÑ|jŒ»J‡-Ò_ž*Q$S©1±ôg?ûÙ0-hóÚuUÀÏÛsNc"sO±£7ù7͸ö¡œpV#wç24™!Š÷p3%´¾â­|óVb!6q<½¾]@X™°_{& ³ø ©22ò´N|oQL?þñ×À«éØ'ÛÑT¬9^^ùíw¿ûÝÛçëÀÿ dþMÅôÆé{öߥÎà{¾ç{Œé@ÎÓ¹ƒd*ã‹ÜMìûL5Žú¶Œ‚îz„QtMæ©ë£ÒUš|æ·}Û· zÓ3%›k;½ÖBs1(‰•סƒÛY­±£Ö¾Ê‰uÊmè+wäSܽrï{M;Ñ8Œ¼ÑUεΙXƒw\“åèµ}BR9àÅšNb@ü'c6îÈlô”ž&û÷4÷ý¼õÀm|îsŸ£ C!?`µö,£¯Õ³˜œâ+'@™nÞžæõZ4œ6„Öÿûÿï›Í wN*ß,è˜pB—œß0<)¶ ì(¤É a$è×Ù#%‘tW/–c9óèKõÁéhº§ÍÀ OMQã5î#@bJ¼Å¿1.’F†nT4‹h¬c¼´EJ±( ú—Z hç„"ä¹-à|=øA€IglR†_ò%_ÒéuYÙPÐ¥ÓñÔrNõýØÕØÕþ7ÿæß˜×ñVã¨Ï4.Rh-ú§zU(ðI¶ ¸²¢ìÂY›ß´ÖvUãÁ6‘MŽ¥´O^‡™4ÅUÕ?*ÐÆ[*#si*²W÷Õb&  £j¬së8_3Cà85×B¶î ØÎüEŸš/ýÒ/Ͱ¶˜X†­¦ôŸÿónG`Èü:Å©988Ãþcp p ðжJUä]TEÆKh2 Ò;©G£Ä½ü–ßò[jÖ_õèEtøÂЗRs¥Ñº<|®Ïh”ø+FM8½æ¥üÀ>À ‹O:É5 ç6N¦•7“n¥£hÁì-W}Aâq›Û€}M“‰&ýFõшrUo½ÍDhÓºA‘Ã3õ̽jŸß`Ä´ðñ3H˜Ÿ¼p5hб»ÛâFÜtšÑNá@à¹(üìÜôò p$ûHë9t÷;–ŽhcâÁº×Xµýщ²i̼Óá/¸‹ëÐ ý¡ú¡iYÁ%r5\(6¨1ud‚¬t1ïÉ•rÐݸ8W;ƒê.ûö´é O¯æ©³*"•Õº(¾ó;¿sÕXBSÝÓ«‘Bƒ°;0!ëúv+@˜`LxÏÖÜ_‡ì«PŸ1É…‰âšX½µÅ-Ûˆmüóõ@à@à@ öÏC TrdöŒµä©#&GO`·H¸ Á×eÒy” è$Ç‹øÈ6I™ ºd‚¬lP&ïOD}/WAé%¨¦#ÃËBÿõÿàTàƒ7 ¶BWZ®TÚ´A<éΉ1Õ"NÒjG×÷ Œ„FW  D’зñ/þÅ™¥BÁ0P´b)ûÌ+RÆÏýÜÏ)Ó!Lû õ¹ÆðŒîüÿãd ›–BWU-°~¤g[Æ¼á ¸%…¹â’¦f7mÿX–²Üvòp+¹¾ˆ ›¯¯+ì"'Ä1Ž îš³×Žº¢#C¥{ßôŠX2è+%›qtw1q¼×©Õ…Õzý{"-Ýáé-Ö¬Ç,Ãf›E3a±ÆgØ×ù`_gGà iSš2ô¤?¬=>y@·ÎxÊO‡ý{âà¼þÀç! –Šq–<>êð¸(|h¦5ÜK³ÄáC— }ÈÎe÷RCXÎ<,Ë._ɧE=Š/*í¿ü—ÿrsðTzq±»ùT%mÛÌ+$) ±­åˆóѬÛ#_'ü‚ÿøÿãlÌ—¥^`]yí¨&•7¶ÕYüŸÿóÞ:ƈ¢ðª'ªOm˜ÊŽƒåÚ~€f%yV“+Nð}ä,‘ÄâìN_ÿäŸü“똧| ðæ!PУ¸¸n™Cål;lùëªt»ÎŽ¥¯Ž(˜‚Cç¶ê´MqÞáwZG5½/ÖåÉš.ΤÊë#å\ïhÔ·ú¾öÔ¶#»Ô®ö´ì)Ô45S€(´c·[Ç$5ê…~Ñ ›UAh¦×VÈ+úÚêçk¶B !Æl+ÌŒÖy*C¤˜Ï/ÿò/WÞ>ûW½„œŠ6œ™+¾7ª¿g]?K:…'Ãþ=ÁM?¯| °C€Ô<Fì8C,ÿBô¼P¢0<ê#¾ÈÅ/üg#šiæ°h.H(TZ‚íôB‹pÅ)5üÍi5F#"ï|¨Ñ¾ho|ÉŒ…çûä¾È *(írÖPjdýBéð5_ó5æb¹w~ý{´;ÕëSò Ó]ºç5ÓW³ÈUÞŒƒ_аx á_a‰Fv°ï::ðsÎ{*ĨG¹z6“!‡Wàßa½f1¨§¦‚«Z~vºÂ.òÖÀW·6Ã/™÷úÔ¥Ö7+Vš÷ïþÝU”c‘91^cÕÌh™¦CbS³f„A2ã#§ÆEò _éWÁÇzַȧ±ÊP4t­=fOå¯þÕ¿Ú;ŽtŒ:ôGº.û÷D6ú¼æÀ]`K†y@»$læÿ#hûãZ¶©k•f¢q\â͸¦—‘¼è z¡W(¸®A;‰Õc–XY~®"|œêŸþÓ:öÕ Q¨+Ú”èõµYœšŒ¼ð›[‹´«AðOé‘Õh V—Â!s½l're´ò,÷6ë»õNù@à&Êwâðøtêä`Hì«c6wµ~L‰íxÏù¤E×^S8Ò™R7ÚM•]ž¶*¹î+[nÊ1ã$WZWKÇ•éøzݵX×6•š‹¼öšr¦<SSG_Œ®I!,ë:þ4޹ZàyTâ2$vÕBwXià#2ç½EB¡‰Û UÂm]Ö¿TÍ&ôßþÛË!³H­@·n(î}[êùz p ðd!pØ¿'»õçÅžA€H8vÁç$¾‚d(=•bå–`¥BÆÀ2½Á_ÿëýdñcùð[$ƒÕÈSÔƒ]_‰§†ñùÆ•y*ôbt¨+Ž47㎖¡ëžc!W @ò2Kž ×Zf O™f­:V4{­bÉX â®ua0V³õ/ã4DûÌÖ‹nDÈ–œvÊSïe#¾5{ÿûß?}SN8~Oé3¥S@£g5§|/ýŒs +Цëät‰ü¥iÏx;ÿ´t_ã;ÇPScGwÆÁ5©áæç0§ßv¼ iëÀO³)Ô¦…“‘MÊÆ›:ÓÞ§w1G¨äÂ:ó]y¦X çïüË[…GŒ¡í¶Œ+Ï9#ŒéÁ½¬zôeŠI ¨/~PõÅ ÞÌ^8A¤CªÕ°qðõÞx[9¤MéWêE˜Ö†6¸é=ÓÂÀÀ“‚ÀaÿžÔvŸ—=Ø!PTO2²sBe_R"!)ž'ÎÊPC…Õ6´T^MŒ¢åo`¢)Ü_¡ Œ&~]Ä\×ßû{o_èßËÐ%Äù=þð?ü‡ÿ054uˆHLÔ½Ÿý*ÆŒ£¬X)œ½À¦Ý+º°ÌÅê¼)û/pßÏþìÏÄŒ¨ÆâæNÉWg›ÈWãX w)jºs×iñctj(A5³5ƽðªHØ,Í<¥¯P'pü¬Ó×IOÍÀMY4 ¥’ÓåøåKæŒùÚõÌ®Ò WYË?õ§þÔŒ !¨‡+ò=s°o‡\åõâ`Þ$ôk ·õR»JêuŸ‘§Ú\rˆ5¯ƒ«Z÷̰]äi¿ÌhÍÌ)±IÚaœëXŒ ÊbÒi­§œÔfÇÌ£ ЗAÀÊWh­àœj ;HêûûÿïûÊrCMë8iþØ™Ôz”㥎×(Ahí…²·[}ÃÛ3Î:Ë)@ä#)û? y …H“ÉéÚQ {-+sâút2Ch@×±-¯Œ^Äü[G”körzYØÖ«4ÍlVëEQÄWÃ0¦hj²íIJìÂu«a§ãg>ó™ÔŒtª“-mÜŠ±­ê|=Ø @4¯å\ù¤tÝ*_0Çl¼X‡÷ûèG?ZX—ÕZ2£M½ø :ÒMTz@¾¯†úËù/‡sÊk²…Ò­Yz³ÒŸÌ‚]¢’›‚æpÍë0m ³š_Î#…?ÿçÿ¼¾}p;\µpP‡GnëÚe+w×K: ,úŠý3´ç@qÓ BjFV¬[ýú:M3 ÁVOš3 óç_àÎ?V Yv@¼1‹' á¯ãŸòÀÀ“…ÀaÿžìÖŸê ŠžñÀœ’¼üJU|ó73#Ãa 72cH\VNh¢ä=€©)šŽNìf3N;ë\¤ã„ß›)it›…ÝA%²2ݰ¦´|{R jsaʶó:ͧ׼G$Qz ¨×ÉŽçäuÌ|~âÐï×x§éBWÒò¡)€‘ÝhE›`7[¾/½p€ X´˜¢kè˜ÕY`¬nùª<¹.òÔb&ùÚUrÆÈT¦š–I%:´s‹×ƒçëëèjF_çp q™bCÕëß\‘]®©téF«o@)7çÑZH°å:¯•ÊÚ5ißÅM"£ Ôadhäêß;£ÁuÚl)[ +(+µž§>@qÏÿ^ ånÌíLQ¡‰ìŘÈ´AþÕøÛ_Ó}Ë·|KÆ´áçS?â³™‡G¹Mt¾<)öïIm÷yÙ/@ ¤yã›9”D‚ÿ¡á¦~-ü…¿ðf ïýÞïõˆ\ÿ±eQ¡ n*Ö ábaîå‹7~¬ÝdÀ›5L!·fNSSÝ¡~t­YØ}‘ˆomhŸÁÛö¨¯åìbÏ–(ˆ§™‰¦$¹†Ô1Þ˜Ó`j@ž„)%¼Ž•`ÒÖ¹Øw©´ÎvLxR¬d€)c •ùŸþÓÒ=[8é›ÅÚ|Ôøà×)Nù@`ƒVd´ULª3BÇéõô¶ÜÆ&°£5²žâ¬8¢jWØÑͧwã4’GpÌ¥^+{KŠG³)t[˜ å‘Ë¥ÞEsÝ:Õ.àïý½¿×£oÿöoߺô5›U×yÔz Aa­2(0N=N{y³{þº÷„DºÐj«\£]†—[öö &7§P™•Gå½êa ¨Õ›B³Ó,×A•ó ]ø*Dsn–q<ÿì—Á'¸Ð<9À€ñž2û÷”wÿ¼ûÓ…3Hô²ÿ ‡!J"G2r>rãð6DF…5hùÛßþvõ+ur+®¦€–¼dkƒ=#«¶ŒX/DŠsËÿ·ÿöß6S±­ïúU,umF±>RÆõõ þ¢w¹Ä¤=«Yuè­×|-Å=‰^¢ÒÇL–"¸Ý”ôG #^}&²""¬ÈºÏø èHkã)$C9 ¦ÓKƒD—kyW\øÔ2都½,]ÁMGÇuÞS~ʳ£â/V>¨1i* |Xƒ×ÌMp9–*Ñ{:´¤$™,nÛ«l¸eµßîЮ5ïBiïr­y\=0;mÜάd+Äà¹Ô8¨øÃñf0Œ;Hÿ‘|ÄÈl­·^}…@<…Ln>­2WIé·ìíW<'Tf:„ÜnŽC#šAeþÍU&_ƒf![é@Í^-5¬6w_b…”Ôø¯ ÕCYðäÿ`ºóè@à@à)@à°Oa—Ï;ìøí¿ý·£ ð3¥€ü¸´WÈ>b{N&Ã>#Þøà%¾ç{¾gTgÙ& ñ÷ÀPÊÜ™/Nó}5¯½&˳¾îë¾n{D?Yéië!eßÚÌW«By‘îœyìÈšPìc’¬³b\+Õ²T{Ó~+!£ËD‘A›®\Ò ¡¹u±]kSOO’ "5ÝfóVÊlæ[Ú£\³ã*~Ì8Öž¬a9“¼‚ššQPÁ^VÙç±#Ó¬íž ÜÜq»¥ “)ÎEë®uœ-`·Ò-†mZ¸Ž¨ãêÐj̡Ωë <]„t‰®'“ËQÊÕ ‰®Z}*!Ëë§øY,Í‘°0ãT‰}«È™«B93E”™ø+ÞOüÄOôT+.VÓëÁj{:ˆnMGÚªú M¥Ìœ ÛZ¹–sëý¿ów®•[yDl¿á7ü†öŘôŸ^×0e^—‰Û ô T® °Àa›â|=8xj8ìßSÛñó¾¯ –€P@P@E¨¡*Ä?(ä0¨ŸÜÒ¢'R4)ñ¡¿â´SàЉLp¸Dõ þï9êh<[Îâ›#ˆÂ'8M‹A–Q8Ü`§Ä¸'Ñ—ÁL¶Lg&RÅœPÉ%S—²ñ^ºyŽ=ˆc ˜hAzò2JCâÑ?øÿ`£kQ„ÆßbÐÿÔOýTÖeqdZ_9¢v4Ôãé´F‰@´ýúïÿý¿7B ­MœX£¦ÊÓ~eM×yOùÉBÀ™qûp›¯®veG‹‘¤{W1KVX•‚Ï#ÕqíQÖÑ2^öÕÅÑì¦f>­ø¤¬—«äB…|\1mãôÒnÝ ÑYÞ¼ÖŒSºZr†RîÙ„ Ji1ë_È ¬HÎro š@náœuÊ×`È[~’ \Ç4×}ÇЖ(•9ë ‘^sýrƒÒ9pfG}}úÓŸn#nÆËÙ¦>_^aöïÞÜój7 €‰å£ìVi%òêу†m€šõdù¯÷~ŠƒÊ¥„ŠÞ1³^Ø€]°fôþÑé{ 9ÿd:Ä Y~’8H]–ñ~1ùÖÝì‘#º*Êâ=ˆ98Š÷º«`)gê)ðÊsJ×ð¡,KßéuŦñ^zzM6Cª•/n}1œÞkzM!Ë…k*Â@#ºoHiÍÞÀ†RnƼº¼PÜõQ5ï{ßûLD‰º6Àú²“_“¶ÛÍ6_GošÌ®7¿¼G;`fqÀÈ6?÷8Òu§| p ðªBà°¯êΞ÷:¸ H:‚óÜNP l2Ó"~ôGt2% 1QA츑&MþâQ.Ë…À‡pLý—†ð^šu„Ndå4fi-°JÝòÅçÿFÕ`šÝósc©¥ß¬­‚èêB ¦ÁÓ€ßcžN[³(T~>[½¯™b‚@‘cj¨´" ^éÚéþ]ßõ]¦‹»f=ËèÎ#áõUn*ÁBªðq»ú‘ù59hÙʨº‚dŒJ½¸Mt/ Ú,éžÜJg£øOعÊi½b¢â%Ü‘˜º¹æŽâ`€ ´ãyܵd9´ãÚܱ¿y•’¸8®OÜŽ åZéåŠÝëµfŒà=8žØš¦¼Ép¡µõÓ§qʽ„sÒaB21{Ý•½ÝR3[¸š­Î€¡V(n³ ˜ÌPñf€ìî«dxD㶇ÉÄRŸºË¦&kÛ’ëÀ£‡Ô`>iüÚ kà^Ø>2ëhp;2k8…§Ãþ=µ?ïû¤!À”(«ÎÑé!Øp&˜ç£2AÇŒ¨ uÕ¸l«p«ž %·’S©ÃÏÜ£~ §I‰Qܦ˜¯©×y¢)Ð*Œä›Ê¢ü ÷<÷гBQvóÞ,QºR¨1"ùU›¡@qå=Š}e¦ Dd–W¤ž N¯›hYÑ‘ñ¥&Žm­ýæȇJå°ëùè-æg~F%)~+TåOh==òÔ^’ÐW êeÏß ÷âløÈòÏ@ ÔÓÁ¼õÔä_êê9~a½:–«›Ÿ–¬=íLÒ. ªÒ *û_ÿŽôäup‰\%ʵÒË[ÛWîRc}Å­ÁQÍèóyûoÿí¿%a)fÒµ»šì?¥é»ù4‹·&¨e@8Ö¿†r)ÍU½?BkY<Èð^¦AÞƒdOñ½æ‚ZE™Â£ÎP1ÕïÔTÈè}Xq}ûŒ‡]&úÉñÏ£°äàç`ê|=8x"8ìßÙèóšÏ PÐȈƒh»÷¿ÿýÌÕàÜÆÃ¤ó—·É>ÆŠéÒ¼¡ÃƒãÞCî.,ã1d"½–q&Ià6ޝénúØLck3ˆtÒSSÁ2Ð|Cåhƒ8»ÉàEz¢®¶úŠ5æèeãßz‚Äd"eÌ$ô|u¶ã‘S>#•GËNfˆ†ÝŒT¡Ä"ÁèXÙúßùÎwú»ºùiL§¡xeˆ:¤(ó;¥%úò÷ý¾ß§•Ó”ê2J?üaÚמ2ÛÖ¾>Y<óõƒó,VМÚc0qg{Ãæ®ùê¼ U¸GÑìÖ¯A;‡áÑžiå0Kå/qÔ¯/B’Á»z®KT³ëýZ»w¿pDcÂ*êIj4Í’ƒhsOöTLÝ›áC]y/ü…d š1Òž50•O…8¯9¦{ÑMÍZÀ˜•5´¹Œ& ^ò\›)C¶Ú@¼×e¼ë]ïòh„z …3O2å+!Ì0PªŸƒmŠóõ@à@à‰@à°Od£Ïk¼F²Þ¯> #­TI \LŒÒ Ö`ýKu’©áÑÁ$Fy‚ÿFˆñÀÜô½±%BhI«p3þ^{V¶14ÜM¾NÚ»ßýîY³—ºæ‹/PŠ`}7ÏöLwäoOY²•úL¥W@㲉U¾éäStÁcÖ‘-i2Cèx“û-G•K…ÏIw¡ý¦ý£ÙP‰8Ö’W\º`‰Å²§å°AÅÔ,UF9Ç¢öÆÌSámÖužòS†€åH¤5êt¹½15$Ñð4Ý(Ipç+¯Ô¢t:„¥+h±¸»h0¹ÒÊw9êF ª¸Â\¼%|00åuXÛ¸\ÝŒ 5™PŽýëý¯×ŽÊñ´7å/ž’¡Ä¼­ Ø$dµ'Ìzà)WIü¶©ç+ä–⺛J2 Æ4®Ï^þõ¬6¸¶ÙÊÁúÝêa€¬0ÚÍFó›Ws¶¾ÃfkàGaê|=8x 8ìßSØåóޝ1£Š¶Ãé±EŒ… þ/*z:%†n¨@´¿Êõƒ#quV—Wb«¼eØ4Ž.‘w¯±}ÝŒ '{ÁúhÊyÖ¥‚˜Ê­7ëïÐ:X#‰æ‹I“[ Yø•m©´¼/ß§uX¼bf™!‚ýõkƒÊ‰öo&wŽ3 ’¨^|ÚÚ=rm´}ö³ŸMYŠ!—“pmU—B€N£¤íÛ]-½Z6f¨Ide&µ˜½8À”*­äq蔟æÎƒ£ÕñàWæDùJZÑÑÒ`6×__éÀýuüB€r }Õ% 1N$Q³5R‘ÉPûÂ.Åè® 5ÒµMÙä7‡œ–Læx›•Õœî.µ.øÔl…òÊ1ªº€4z½ “?ðþ€IJõZ¿zkmÀäæj™(tç+öÒjãiu|L,C`v¾Íëà[ÊÕú½úñBÔñºëe×_J§`|lpÉßå~ÛûQجܷÏ×W‡ý{%·õ¼ÔÀ¬ƒPDûl QèžÂEF.”•Ký|Ðh¬} ×^‹”Ñýúh­a«i¨¡#•‰ç¿ïû¾õS3VûÍò€â÷ˆp‘†~|-®'Ï® ÅŤã£Ò¤Lxï{ßkvî@k¯µœBãªDÝR¦•?Ú¸Á‰{1ÝæÍÈ~R2èEPVk€53DvYc¨Ö€x3]úüžßó{Æ¢¬FG,£c™ ˆTuÄ´‹E¶>ØËU½ £ß{zÔy©Sxµ!€éêÖ'Çq*ÇÒSøu¿î×ù+lo È?MƒçøUé@ªq8|r—mLÚ0GWýÆËÖh¼ø0K®Cj1#ð•ÕE–‚+äsýËm–¢ÌSᄦö—ŠþÚ«š²ó¹à÷ä‹‚(ÆFtR¼ôòÀ<–ˆïøÞ,PDG¡jå£äÇg‚ª`¿õâ£è]hï R}üêM‘t=¯`¨>TogmJ̹8«í—Ÿ†Ç3ž§¯û÷êíéy£8®œdÞ žyÿsʼn„R9šºi p3V†$VäA°»¦§ð§ú§g¡©;GA• æ'$ÐJÇ,1E›ÅöD'y•“€Íë¡öÀaÄ©éúTú→:båEÓ¦‚Ûºç=E€øã’¤»29à¥dØ”«£µkGèî ›‘ãªOÔÛßþvZ¼p;¦NÙqÉA¨@̽ a¯å¼ÅeÈö çë+ |S‰1qtHºÙ|º¡«É¥Ö%Â:xÒ p‹èÁ Ä äðußøßèëfó쨫tìuwŠ¥¤ÆÕpA LrÓ„ÒåÒÌEÃm O•¹)¹Þ #¹^YžB—×–u}ªLFÓ«™ÂÒXò„F ™­ãú54µ%o˜ G7 ‚fù²/û2 Ý J™×Z‰·c’:}¯ˆ× °èȆÖ6Èú:M§qyk|…&UcOm߈äÖ¡Nù@à@à†Àaÿ^áÍ=¯v ðy”~· ®Iä]?üý-ˆËZ£|/,xTÚ×~í×>n¦b(ž!D])˜Ïl$:2—¤buËH×w3º¦°Ãšîf¾x†XÞ÷fø‡ÞQV= wy÷8(%Èf*ÆäŠÞºcзx3‚ë4Žðë‘€º|Û·}[¯OmÒÓþò4c³ñ¡²_ˆËlt³¸›öšiÜŸKÙÚ¼‹BêÜ"mxúùv¯ÿ3æQp<…§v††l•/>¬ gÝtDÕzx¶ ™ÅVé$³=ž»\F¾-£º£n.þŒ\‡I÷RvЛaŠ:ãÐfÆ ¬(YJ·Y¹Þqн·w]×|k@W¶fo'.¹™½b1ãÍ„Ÿ3 †ÍY!>v*höàÞ±&5à@imY9;Ø?÷çþÜõÑZý :Z+§ u{j1þ· é+¿ò+çk>Ɖ,rºŸÂÀÀS€ÀaÿžÂ.Ÿw|Ò ªBÉaQ8~ µŠ¢7šÀ×›áQŠªÌ,·4_Wø&Ú—Mþúˆ \^—梭bƒº16õÊ©‘šÏÛu(5¨@ƒ`un2`äׇʉâÔñ´†‚À«”!ýæø*ã²r‚L¾ˆÉ×é3³ˆWæf߈`Öeó1=™!z÷yTq¬ú"ò­ui\7ò´”bh8Z½¼H>?ñ?É«’C—‘1Ò=ª¥ú{q·õœ¯¯dh˜3A`NkÉ ¦€N ÖHÙ£n‡.ñ «»/ÙM¸f>îÔ7SsyÕ7èÝ£T^‡i µq„*™š)°ˆžËËu¾n)”F¬£µr-»ÚFö"SéúÇA©7rvÔ¿ëwý®i° –$)Ãp®O§œÂ3FÚÍå÷ÈË.Dan„ì Ê3û^[ÁÍÕØeÏiy{:_K CÈS¹ä‹7Nl¹ÂõÓ¼Ø*ÃÆ«EÃ:Ô)¼’8ìß+¹­ç¥¾i‘ýüËLEá“¡Tñ Töu#(6‹Ä«DXºOÍÍBñ ȼï±dGl‰y£)[û1ÖbBÐÈE)øÐ‡>ts–*Slò¨¹×&Ÿ¢ù/ÿ%Á™}i.3ÞsÚ1R5ª´ôë*ÿÔOý”‰0¢ccÉaFÑqDŒÿô÷Úwj2޽JÇ‘’ã3‰ªÆaN— ©XoFrLŒCÓ«(U'PGi.œ,øyo‡®ÕÑ›Nˆš‚d°¶ ¥LûT›1ök£ÕßäZ[üùûêA ³Àv¿¿Ž„Ðÿà ʃ×[ÐH‡ÊÑrNX-:~Ùr ‹Ò J0cÙ`UL©Qþ;ÞY–vàþ­½¯PûNÍØUÅg¦Â²³$úi*ŽQÖ «úq®yomñÀê;GõçáËÖ±¯±vPM¼ñµ øx©TôMe5på'Ë@sÏÙÈÍå¾ß“5; z\Ñ BÚP÷M¬Þ Ú¬†`+¡Ø•'¿¾é©98x5 pØ¿WcÏ[Ü€¢„e ßxd_TÅÈ}3»jÿˆÃ…%ˆJÓACâN…&+;Þ”„Õ™òl®òùop~…¿²œëoë;K̰“¼àîT&ãpxÂŽ“C&¥e'Π¶"ÑÒ‡V<9Nù VÐñwՀϪîù³ùꨧ3ß"‚NÇx¶¼ÚZí™—¢n†¾îð|ç;ßiü›¡ª\ ‹tÁ5ðq\ü+gÕM¡0ŸÅ¬è¥¦WÆ•†yBO›‚´è_ý«µv_Ëžf73ˆN3aHµ½©¹YHk-CÎP4D=ü§;Cæc4ÛÚú ))”tG3Lc©àÇâ&²º9û©<8x©!pØ¿—zûÎâA ˜HäNRç~þû‹$åU5×µ‡c,òû4FR I¶”,6'Áu}9ù Ãn²[2+0µu‚F@vaB¯ã¨aXey×$ÈÓ¸ä ÷¨1Âòdá½#Å5_<Oor¡´pIÙ‘€hÊô! „,ûÜç>—÷]ë,f-»jX|)zK(Žè6Üx4™W^+§QŸÑ,_ ÃB¤ÜӌΤwAáIê("…¯ú:2ó³¼ëšù£¸Mw¾¾b`S=Ñ>ÛúŽA/®¬³êØÄh6¦žXbZ ݤo&Ù±i›Wˆåp‹?LÀd‡ÜQwà ëð¯§Wž‡–sàÄó,¬‘ë6ªïé¥÷²…BÍrËÞæ ëL 7ïuOhÉXyN¹ud1 -ÌmÂ*Ó(¶ø¹ë•!=¨ÏPÐàõi5Iµ$hY-ᯡbkv£‡¹5,¤ u'Ò%#^õó€M±‰Õ³ãƒ}àôº†Ss p ð’Bà°/éÆe<쎲Z ¤è~éÑ ýÞ#Ë&A5›Áa££ÿx¼¬n$¸5ìÁnNÿ“?ù“FC=°ØŒÖ¼á}ÆÌnÂÓbÍ‹¬Ôò..qgM÷ -§t“dÔw fAÅN†w–¢“/^›,ЮA,¡Áµr+ÓÝiÃ`«ï+à@ÂI‚4ó¢Õ »¾ö£¸6ówT‚âߨ£‚uùɸ¬®£òÀÀ«Ãþ½ûxÞâ@`‡Y¸u†‚h¯è¼‘ûbÏJT5 éÙå¢<~¼ËtDp ã6Ö+Å˳¥äjò8´]ñx"±XcÝ4t!^©š2{-üÏþæo|GYç••zãùç]‡C»æ‹§|è•ËÔ7+¤úX# ª—ËžN`hh 'õÅÚ]–µm¡\ð·9áØ©¿ö×þÚªk:ƒ 4‘éí)æÖ8«ÏR^Lrܧô‹­ý3æÏÄ(¦¢ÞÔúúp ]×vʯ8©¶Ñýå––M¸¯.,ÓÁRt+ȱqx<šH˜Î¡c\߯ûº¯[õc¡‘Imn;º¡gÌ‘^Yfy‡­Ä¼­òÆ¢ëÓ)ƒÔ¥›š)¹>ЙȠ‹ÉÞ®e™èovoœ®ó=ûOm²ô6K  KÄÍŽ~²A/xJ÷@¤õuÍ!Ãz]ÿ jÈg ãí ÜU§ý‚mîÙGb_EK†Õ~¼¸îI“û¦D½®íÔ¼8ìß+°‰çv`ˆêQ‚Îe߈ Kèë¯`ßj5@.¾ŸÜÇzíµ¯ÿú¯× áB Õ*xfn$ÈÁ3ÉŒ‹ü…®ã¨É\ó1…‹h…ã¢ÃùGÁÕ~mZHÏ1X½N—„~ U¿µ)zžh+k½øC1Ä\¢ýµ™r¹›jLT`±.¢áWظëÂÄ_sÆêåã…åhR]jF[¨’=Þx­±Xß÷¾÷iÉÎ.lj\ÌÃXÓ¼"ŸMóúGĈííÎ×W¢¬·Õ Íý̶S[á.4p$:Œc“-¥ƒE¤‰ÒþšÖ¥ÙUÓXãôÛcŠé oL ª´Þ”m X;#l’)ȇxe¬,Uh+lí=›‚¼y §¶ñםɿVSÒõ©2„ºcwµB°«)¾ …„¡b-¡eK‚¢·Ö¯Ðû £¶v VìiÅ\͸G€|má”^1öïÛÐó:Ï 'ºRZù] DäQLJ€~ï‡Ç¸ ;œŒ^XÄUÎ:á»"Äu\ƒø›­)›ã¨Äb¨0¢hÊ{mÔ£lŒ·¹5“êÀ¤ÉΛ—¦qÓ@ÖE ȳïQiêS…Ý´˜Òb$šØ8z|ù¶ÅÛ{Ž…åøQ=òäg„8̵¦GYÍ¡qóÔ²€2C¤6ÙŒÊЩ£Æ®,cùnYp”:æŸQ€’8jœw¿ûݽ~O•ïeû˜5ŸÂˉðÑF3õL(!¤^΀ÓS‘]JQݲƒô±},¿²ºü“òO68::œåuè¾8ºEþ¼²é!~È„$^Tw†è‚Q’gMÜfékž.ÝöÔÅ\õí®í„_Z[ºæærÎï!Kº0áO?ó™Ï¸V£K¤iOõGÃ¿Ž¼•ÉzL4–¨ÛÓ¾B€ÉãHÊâW!^£õla?ˆw•Fdv­õ͹ª„$Sɾ1Ø3/Áö À1™êÝâ÷Á€çÑÀÀK Ãþ½ÔÛw pÅV!ßÍÞÏïúD}@ÙL4È耑ÄßèõªH"$7°C’©¯,ö’Rîêþ—ÊãiüE?H/ÖÜÌ™,’Öb5”ÊZ“ssÙ*‹%x/4K½R{ÙD/AçÖ#Xë¼çMTCrtäà¨é8ü0g5Bl[åì¯ÛñfðrÔ/IèIî“ñ¯ùkÏëoè6tùÂN¨7µb9¶Nçq¼©²ï}ï{;ý½§uY—wÊ/\Ã9 Ø­¬Š¾©á@IDAT…•?ñ‰OxG"ƒ˜ °Ÿ* º‹Óè`¸i¢bW”ÏáL¹ä¸:´Žn†”Ì¶×ÆÊ¼1Í?©=ß ÷ÚS¶^}-Ž‘Ã<ÌËèжȳÇLW9o\ü›ã«lÙ”{L"'«¡ññœn1ôLhGï ›Pì ®µW¶¬W¼ vm•ÐÙ š…lצ^½¦ß˜!®þRÿV`þÝûNÆœÿÖý|=8x• pØ¿Wi7Ï»¼†DˆÊ)€_÷‚ï) ]È­“¸÷«gË›°+‹ù÷sUC…¦dÁ5âj\O!A#Pu§zJÝdÌ›sUY¤Í}5+‘— ¨"%½]ŸƒŸýìgµ)$föQ7§‹?¤¦¸ù´Ê`ˆf“9ü-%g3âñnv· ^¨S° ˆÙ+( ;eEJ¹5gWãDOOü <ðd†0PŠÛt1{49½/â˜i_[0!]l»Š£íȨ¸†Ú«Á½ŒÛ¤çëKágñüílg»qk¬‘†¹;#×pl4Näœà¼lG÷*ép g|uD6y:ç+ \ŠÕåWyÕÛw/&TÌÚ]9‘Š«çŽÛÅt=EC±f/ØÛ:ö5ä꩸5„Ñ€y/øŠ8fõûýä'?©þqšPc–ãÊý®Ó…ZM1ªw+¡Js„Mnq­ðùp¼ëS†œSQ>F­Ú7àl™^·¯Tµ¦öIZdvgŠS88xÅ pØ¿WlCÏë…—ŽÍ0HvvPA~€¢"9# bö9oúWþÊ_ÑÞ‡Ýrú«É`¹œìí¿þ×ÿzƒËǰ9_3uñ§¦µÝZ¸Î>ÄXì!¯á[@5=çæX¸ ˆ­p`YÃälm|Í:b„' ´ yB¡®¶…¥‰½—)qÆ„¢-Û+LÍÍiÑœ„×_ôYäÒq mbnk³nßÍÑNåÀÀË Ãþ½¼{wV~ °C%4º¾†~ãÑ[üa6ƒIBån0†NÔmÀ-Â>ëAP6a¿ÜŠè†Ê´q²Z­VšëhdâQï5¨qœä5µ ª‘Ó¼8ÍZ–d¢òÿïÿ‰£Aþr×jr¢ÛLªÐdk¾xV^ Æ® ‘TzÙëÈX¾áÓÀÖhkhÐòLÐl£§ÛMTÚª‘ÈœÏé‚f×_íÊŠ‚ó-ßò-vvÊP$ý ¨0©Ã„7$&Øf?__:¿isÛîa-0„1üO32LOå.Lä!¼ßhSt¯*/Ç/V¡ósÖ°Äö(e5Ýb©1H‹!­¸ ÒÄ׸)®˜‹6žÆ7³·9†áÍ‘UzAWÞ[wYÔŒ°H\byd=½4uo¢Ý±{mÔØt§×fð4˜{p0„$¡JÚ\ÛçèV®•×2懴¯OךÌË›´¿Ô}“µRÙQÖzá”^öïØÄó Ÿ‡@ASür¥b¿èHTÑöëþÜ`iSÍÊ$ Ë€¸Ç±5 Ok ¶ÝDÂñM;W³ÇÊFÌR„νXç(¡!MgaÈPIÒW®†pÝ£k$Ãõ$I„¨ sµµ²2:!Û»øK! CWF\5 ZQ }ÑS橞Žòí£ýh `ýåÑ5ÆZ=¢œ¢F*o2C”ÙoR±Ù…Ú`êPÌ͘ë J÷ý£ý¿7ÂÅ 2²Ôõ:_Rˆ2ºwqOë•o[[ï8 M7ˆŒÒAJXCŽ0ïÎKP-Õ`ü4ÉŽ™é‘Ã9+ä~Œë#ŽivB wÛ“gÚ[/_Ó=RNÎ#×JÇtnæòqõnJm¼…§„\Ó÷Z(ª·ƒý …Ö¦Y˜ñ˜RÜ…˜ÏƆÍP1ÃPÐ*µ™§Sø§ÿôŸ–ÓõÔ(¸ãÔz:xo8Òµ½²YºÎ0Øöhû†‡´·úí+t½ý:Ì2¦€I.Ë…”­ûùz p ðj@à°¯Æ>ž·8x÷ Šßg~Å+m'B^ëŸk„‰ÚÐÑvON?ÏÑå^¶®šW+%jÂÄ¡1>E%UPx° +­ÿj9s)¶þpÚÄ)Ѱõ¢L.KÇ nµf_iÜé>…¬Iï…EÍŠ´>#R£á9eåJSZOþ~3ÚZÈíG<}+ŒæÖé¢96ìJêEt2ŸcNÆÍ2²µÌqŒk‚5–iìCÏCcÙnÚË`7ì1]G‘` e¬—ò½·^ßâ”_L`Öì ±j4<ŽAÖ†¶Øp 7ºø=Fài‰r"i‚w/Jú®Éëà„8„ŽböœWALÌO§”lH.¾€V‰{^j9 º¶ZºJ.ÔœU­ ¥k>úu þëý¯Ö‰1Û„JkÊvmFCîõ±^›%jQ ‡,T!¢uä­Ì°"›êÔqPD·ÚÞCƒaÎ]Ìm„ùúßùÖìªNÍÍD!À{¬íƒT¬Æ\?«†z–Ÿx`ØØÏÊÚý”^ öïÕØÇó¯åÄ/û;ßùÎõw}+ûEG–}úÓŸ~ ²¨4”ÇãfdäEx0à§>õ)k@šŒ/ ã1!›Y—–™yàHk]g)ZÚHr–´XÐ*ÞÞBAtÆã×ÓP–B,½‚­ #$¶æ<:xI!pØ¿—tãβ¾œ@N8T^~ÝýrGø»Rc›Ü×ï=çš±Íg—A£ _˜ãV‰‚(úïÊkMszKbdu?mÌ}Óâ­ßGf<Þ8›!hþ-ÄÒÓw+dWÉSȘۣ¾"׈ˆfdŠ8Þ ÆŠÏYîæ %¸ùTˆ,K½‚Áù>]óÅÿ¿ñ7<~sœÊ‘…ê³—Ý»ïÏþÙ?;(þP%âuë"Ô~a]<5ïd‘6¯F}LÇ#TÍšt1&_%J7žYy=$'Äíü+¾eTy¶Ò'+G†š¶¸š!åà~ ëæ²LžtF3‡§7uœæ0c!&¯ÃÀ!Ñ>Ü”œ{M{dmNô0-§PÔYW`j*¸&æí“„~³ªµ¥Kg–{Érn´f](ÿÈù#sžÅ el)œ/½¢ü]‡ÝÊI¬>ò‘lõó5ÍØæ YAY¥ô ±Ü–qbÌ÷AÐ]˜!ëÜÕ(w³JœCßHïrn%8=`ŸP t—qûÊ8¡z¾â+¾Bw?.7 ÖyOù@à@àå‚Àaÿ^®ý:«=¸˜"|´š,ãÆ5< ºŠ •ßrñH(š˜ôDšDà:D:¡­bh$å±Jâj,ÇÉ–ªæe´¹ÔíEÑ&fðýÁ/¹<¢ðãÿ8*pK[*P¥Ôâ#÷Ž¿ð-¶ëݪ.¥¼ÝÌ…êÐûòÃá>Tól_) /½ŸU¤å£!¼ù4CM”÷ ¾æ‹×…õ˜Ð\»KÝîê3ÒœY¦fé`#µ¯›’½Ü´/3D¦XˆMÁ30S:Ç`(HT{†gfìÓÊ‚_3³]×|j^ «Ön&JJ•¶Û¦#å ’©[¸@VÏg»â eÜa{ÅyÙIÐ<7#N‰µÈUk1§àµ¼ 8Ý×BVÙô{ãýè^ÐcóEùNÕnj&g¨ïýÞïÕæÝc±mR“BM”ÆÁ_è ƒÊRÆÒ§=ޱ\”)¨rp-Pây³8X›á™ÝŽ{g‹Á¹‘ ZÆøª^EôÁ"®ÆõòÓ0¯“åêÆè®óžòÀÀËÃþ½Œ»vÖ| ðÐGá!÷ã@w²9U)œ£ÒŠ ¹ÎÒy1d.µþücr£ü·Jºg+ø Ô8U.ùx¥×!ùà¡ÒЫ²‹v‚`|¬•kPÄ“^I™­œ`ʩ٠ұø(ñfP6…»0 †‡(6Y¬ŒÞº÷5åêêò´6‹¾|Ç;ÞÁ²í5qöh©µÌD ¡¼öªkJÍ"â²µ½CU3–‹%^‰õºˆØÎÆB+‹vW°†™BŒû¸;¯ÆG(uOQÍ•yYsU¨´‡nž¡Ná„@kö.n çÖ}QcÇ3à´Ý˜„ÔS*=JQ6¯ãÀ¨txÊ ,fR ÷}šUˆÇÓÆÇňqlÅcÃX?n]|MÁè (_³·»2¿ÿ÷ÿ~rV¼öU“lż>ýÉŸüÉUÑûeªz½¤.~sd=ס˜wº,@4ik…ý9É‹5CVPĵÊhòÑÅn#¬_¡G»U>0ȇ©Œ¯Ù‹ÅDɼ²þx¶o$t±ø"äK1ç·?^nÕì©DÅWäáóEý_ÿ"¿³ëT^юψˆ·½ SÄDJ2½•𙾅x³®dÄÏæ’6Œ‰Ô >D'º#4š4äˆlØÖèµÇõY6YûS+]ÚlT¯öÄùÖ9Ô6 y“â‰ÅŒÍ"·BY%æuP<$Ç ñ]´O1W¶Ž¾ÆˆŽÎ k]vëim›5¬.Ø3»oãl-_¬iŒ­SX’qúP0Zû:_QŠ^? ºú,yŠF\9åœÍºòÝ_'Ø/ÆÌæÆ«ÈÙ绀Hÿ-„R†ÉqJ¸ ûúÖ®fÏQé,A/˜“µAwÄM\++;öz¹c$éj¸ ÃháÙ4ø«õ¯^ûV“dø:‹"æš´$š´B¢Ô¤l;M”“áÕþÙt4\ÔAYÕC6ÏCKº§®œw„|4{œó†5¸6ßð ß@ßø}ß÷} ñ±½²=¢r”ÌïÚÆÆÏø7 |'§œ3öÜ“õ¦«¯òÍÑNåÀÀKÃþ½D›u–z °C $ýl÷#½–š¡NˆŸ1jHˆo2r3(ð´g˜º‰·®#"©Á jB°K»ø È8¯³ˆÎž¦¶¤šQâ¬g-$ü^ß õ‰"‘¿küRJ·@¾vÜÊ¥7À¡mõ}EW¡k£/Í…­-|}å'›Ÿ'Ï@ã¬SP$R¨ŽkŸÁYs]·#‰ûÊ㙚9îøæaѯvqQ{?ó3?cFÚ]¾^±f¡‹˜e(«á‘ÕæRôýØýXrD9V0jý—ÞÈnکΰ§ðÖBÿ?mí¯Ïûc,3vŽgo× ø­9$}¥žÅàð8B™ ‹sU3FŒÔw‰š V™¦ÀŸÍS)g¦¦‚Ÿc}]—bÓ±—g¶¾óuL¯½Ž(–Õ€N5e;?åoþæožö×›Iû‰¿2ÎX¶C2P „3Lo–Fnc}·é0cºé½h¥µ‡$MB›ÛóU@àÑÒk܇*ñ™‹¼jé3à·5Pút¿YHzèHÀWo ù,苲5ó˜J…+æ¹9æ©<8xñ!pؿΠîB ¸Ž~˜ã Æh¨’i*¬È5¦žwzýAaØŽ¢$|1J¢°Æ0Îðƒü`ùýƒM@ˆm<*ßã¡4‹6Åü ¶(WE%ú™‚I+fݽ”bA ¥ýÕÁ͵F|¼áp_&ó?||€ÌGŠþ:8"+>ÃbÀ¶|ñíÔ54ºyltm.rm¥&Ë™±¦‹/qŒNÑÄe]c8Šf%ìÇ(f݇šdÝgµ©Cí¬²!B„fJW3Jw}Só"@`ùÚ;¬{‘]“ÿÝ Çïø=¢à¥T³A0Ôú‡¤6ŽÍl“P½@‘ZâƧˆOsdý‹uÔkÕSš­ÙÛ]a7k_o}¤//³ËÔÊUÔ¤ÑØFêNŒ²Ž¹•SNB¸£ì?Ýâèe˜LPõÊPD¤`.¨iûŠ×̲·úík±sÚ yD£ìï~÷»Wc~«Â3常öo?7|Wzc/E±TX"ævµ¸Ëg–z /û÷mÖYêÀA@„?Õ>Òdù»½^ýìú/ ÌÇVšÆE<Å>MlÀ™Œ“̇?üa®eÃQ4>û1ÑIÓr tV9½ §òfá7ÿæßl´›ŽpkûÛ,c*‘>,ÄR^µ‘tØ¿UY7í„rÑæ1ƒÑJZÏ Æ&vMFqÌ«Zð½Ðóf)|Ëef,謜ÖBàÓœúÒÉÜiÄÆ/ýƒÅpåÊY(«ÎÍÖ+=>³,…ÿQ¦ÆáŠì tØ<“ÒÃŒö8öÏÓÚ øJ­@>å_JpŸkýmËF¸µ^Lš§dC¼ãZ[8Ëç«ÃÃgGe ª™·S7¼~ Ç<꿾/¦Å#gÆ#Ûñk‚޽+píU †D_Üε£h‡v–ìOü‰?±.ÆùĺÜc6ÁÙ½ØNˆû;*wëñ±rg:R5]—ºÖ0­× n²»Ó2NÚ4;ý¿Sg¼òŒ?gtj‹Ai:Þ,ÀÌuy<©¾! ÈN˜m%¨óÛYÙ€~tnNw*^.öïåÚ¯³ÚÏC@´€¨=äB¿Ð1~¡Çñ=&ží¹Ú›èüoz7m/x¾þ’Ag5—}øv;ÕA%úã=f–«%u™fïyÁ);$)½7Âiàƒ7pÍÇbÙQTy3e‡Üu¤‡[sÔuqì[Þ=ß<Ãe't¾EKqY#­0ï¤QQo÷`#º2WCqHÃÕŽ~öÎo{L͸ã®cBG’ÕÞCÓ8×D¥Ss-PE¦ü#³{e~’®ö^f®Éßò:ÂZÎyWBìkýµLU›±7Üî5È¥ÿ±†PDûhïÇñºŽj^@öïÜ”³¤ç@€& ·ŸäL¿Fjá7;æÿñ?®,˜Þã‘õýÆ?ðåk¼Pj=b`™Œ SôasE¥@#WóÝßýÝç%¼×‘ëq³ÒòéQëIþޏÄ çÙzÌË;å‘§cd‚æÁÚ<ÊR‹à^³ˆ9ÄJ D+%ªÑzP6øÀ¼~t/ÝÂ=ÛN~8†õÛÞTEôSÄhYø&TÆuÁ ðôb¯»=y%RÒS#³2ÝòÅ—AÑŽ¯àMºm¬]ž5ÀÞôt†Ã.šbu¤l%~>SR F!C:]ñ½ì¦c\sÊ¿”H`S¨n²¶Y¢û¤ÉI¢pæ'%ƒ¦ço7‰¬5ŒcC¿ÇO¯qjv3Å¥£èé–­nËÞ. å5{{Æêc‰zZ•iŸCËþ3—ñÚW óo çæÓ*!¸_h¡è½)¤u@ 5+¥áºT íÉ …K`7o-èôÈ…oŸ‘aåm ýéO¯õ×r96šú5¿æ×¶ì-y îtþP¥ú–F._ qÏ= ó™½8:ÞîæFL3ûn^g §  §·!ó‘©=ײcü”^4öïEÛ‘³žç@ ßéÏÓoüCü Ï€æ3D03,—š)w¯›!¶¥H^lB ϰ [_Å$À* iqdÿ+õ Œ$¥?Ì¢)¡ûJƒ^GSk÷@Ôÿ‰¾Ga;™W á​Ü].õ~ŠÕ¤Í»¹•ùJ! §-¨( t†£©0¾Á å2ͦ@Sª|S3êPоKL{†šjåŸøÄ'Xf*3Q›öS¨#‚{(Zã³g3ˆ.[Ú†x„3Ç–‡ «¯À¸j3Êm^OçCU(NàÌ~ ¿ÄÀhµŽÜX}Û&öÀ1]P5Ž{Q´Øì“Eûœ¸ºoFˉÊ,ÀØó“„ÀM1æÆõ¾Ž¢–æu84øêÐ:upGñ&ò!ÑrµG5 õ@­ßSŠ÷Ì;ãÊ W{ø¥¹ß¢X¹ì®¼»é¥šÂ_hrØ$S3&}#`Bh7_¤fÉenš‹×À[@zœ‡gR,8ð,ÁÆÌÈ®Ìø8¸KžÀÄOÓñ^ïj:Hþ^ƒê–™ v¥/:-MòºxåçâíÇÓ§o-û÷ÖÂÿÌ~ ð ƒ:¬À’è›1Ø[˜‘_Œ²Da)·øFú_'Ã¥¤ïÚÈÁkK5Ñå¾qk"F4ºJ|ŒD Í[*ò+0ÄëNÒº—ÓbŒ¦® c©eXöH×GSc–T[rJ¦6*t{Òñìâ8LNÇ­3ÌMkUÁú„%RÉîÂ5 ‹/óòØÜFö™è˜ôµŠ¶žtm±¯›†s‰ Àò‰iÑ‘_õU_eÀÍ; }©R›Tö½ŽÌÍú‹rßž>ýì@7ê ûPÜÔÍzNá ŸûÜçÚ1ÀÉ|Èß̆‹sk[m®-ŽçéÛ>äþ¬º˜`'ŽÖ1ÓnFãf1ø´\ Ž¢§#UqPב§4 Ã¶v©œ»²‹à+GDW2g?£a{\ÏTèD*×¾Õ¸€{—{ ð0¤™tµ]p×|àæú›%9„ù•)t9¼Mꎘ šÚxH‹t•hã‡Ò̇ò“lnŒç·Ñ|-ÜÎ~R›Dt³ÏUëAì&…äï ¤f!Râ'ƒû Û¿¾ä/úÓï…Ÿ¡›Bí^döïEÞ³¶ùÛˆ°21ýü2¯¶õ‡‰Ï' ²’zÛˆBóéBç³Õ_¿²ŒÒ’~`õ#Úš‘dÇem~k5C¹2Ó ÷e@”¨wáBC…µækD»ÖHŠeœÇñ<³mÃ7…¹¤~F‡ Eˆ’6&cZ î¯+QƒŒFC[üFç­ ³É÷oh>á%øM.‡t­‰Ø×ŽÊlÒ¼ÜZ¿å‹·×b3\ Ö„…Ds£ QÒFó¡‡Ùb?¤wÅ–Zúĺ××Xh ³ãZÛ#` ’K¡GqÏF|ý㤨^_á”ÿÿ‚Z #cçÚ­7¶åmùÈGšÎVª´­qb6Úv§åãÞYzxÌØÜáRÖ£’¡2G»mýŽŸC8hxÍÞž4aM+?ƒÄ›²ø *`S@—•Ñ5Õ“•¯ЦoPGp%·G}Íöãêõ‡Iö𮼋?¨&äps*K¡þÀþS³‚'AVn„k…Ù†¢gG,À¼‚¦– Õí{0G§¾é¦Üz¥á¼g»6N¥Õ¯•kÙ„Ÿ Å#˜Å¯…Q5gìÇhä”^"öï%Ú¬³Ô§Ä߯ø¿Âï1¡òp¢áÍ/´€ ( 6Qó´Gó\h°™‡­"[Ä_†Uüˆ³ïzœ÷‰g‘e0ŽGìšb¨·Œ6[$òTG"ù‰=¨e¡PN÷ô:¹©\#L¬]r¼¹ÚJQVÐBàÙVˆ‰¬€ð½º¦Ná¨³Ž¼•Û ^—z¤µyAÔ*bœ…ôõf0 ]<º©ÓàÃ7)@ù‹JÛòŧ£X£ö œXí-ƒ»æ(r ‹aÖ,–µÁ‰ J;QL‹*qe‡Ÿ03ÕÔöúçë/0]ë}q}ÚŽ.ñ$°‰)Û)[l£­­¸ l±ynk̤Ão4òÇl]A ¼©4ìš½Ýàžn½jœBoMS=Ö‹‘g«ò׼ؖ„s85#`RïVd5k¨àê~u tm]Þõ²x;¯ïš_Õ_‚A2rÞÆŸ¯8L+œkÎió­ßú­ÆÁ¯ØØ¥³ ¶ú](­-„ð8ø°–]74S\ ±¸´Ž+Ç~m¦Ú±<¨~}°õsàGÁOØhÖÇà(Æðqz*Ã){+ÃuÏÕ=Þ\Ï©<8xË!pØ¿·| ÎÞ,¢Úëoý­YN¾ñKýùÿ»’A•ÀwÂ~÷ƒ=Éì¿þë¿ Çf)+,_Ÿ»‚O}êSF௲fÛz!n"O¯4ÙÖ²*ÍË‹LlwQþF[e"ؽ,WC<¡zÉ ¶æk4öSs³¡c~n70eä^8PR@±¿â154(®[ý£,Àѧ7êVj ÁÉþ‘³ žéS½ë2ÐÊÚXêõ‘$²§¢&a<ÐEÔÊÚçé•O^cÙÓCrè2TZ Ã&ò‚Îè•h[ÉÊR 9N È>zžˆ¿^ç¹>E7ßåTþB!àr•¶$°cHlÄ*bÿæßü›F¶}jl[mng˜n*žQÀp’Ì óG½êØSÓ1é4¦cæ°þÇ!,yà=°pÔj€ÿtwbV-ŒNÌ¿©4]^‹«$hƒ˜¡ âJVï]U•'¬”!ƒÜ»q3#ÃQÍH¦¦F‰œÀ$8hÐd„oa?9H`í•òZ^+¯å$M¤<öýútjʯóÜÌ~ÚC¶–áCû΃¯£¼mÙ~&¬Šê8æœÜ­Y²ýü»½ŽÜbqC vps /û÷mÖY꓆ÀÏþìÏ¢ùPÞÙ:ö{<,¢*©?uVòZ6H/niLžäfˆ¨šßò ~ÂMy`Ðhœ¼ÈnÆ™Y°4DöMÍÍB­˜µK'Ä,™ø%-]"`ÃM'"ÝEi× ½µµ•‘˜ÚËãwÄ-k†î!Ëg$5ö즰©…ڿɹ5£€«Ú³Ûà+BVæ´ ­a+ßõ®wm±j¤œÖý3¾%•7Õܨìl½åEVNÆY@Y(‡¤Å¥|@ÃaÖèíÂuŒXˆ^‰ã™ü63e‡å^9@ª¤™î~1 àÜFè;>8:ìÁœ%÷ºÄ'zÚgûjiCmë,©#4ZAÎ]e8(ŽèX„N{É范ÅrÀ†msð?mÒPsÚ¯…ØQê#ª<‘Z2«nU™J:‡kû­\j‡‰m Â6®¤‹iyiÕßåu…Ë)ú3lØòÚ¤Ùòâó•ˆÇë|‘b-µÿ?øÁmÛW™ !m×UÄmmúÚe|€m4+³.ñ¦¶q¼#<Éœõ¯«íðøið1k kË$d¼Á¹4¯½²˜­NðÃ4}Oá@à@àeÀaÿ^–:ë|ê(ps»¡¼Göï—x¬7Ó=ðåÃG!)zCÏõCî'ßH=iâ$âj´¡¶zì‹’Vêû¿ÿû§ãÍ‚lrFÃQÜ”Žë‚?” åäøµB%ªÎ›¦Ê¨eD0‘ö͹ª,ÚÊåÚžðÛDã:%k¢5¬kxÓI^ûV¿4‚ók3²üÍ»иÐdC…J³€{9#ñQö3ìš/¾µñ›§ŠÎZ55OqtÌñŠß^Û³s‹ÇÓ…:‰°±ßòªìóÑ~4oÀ¾:Ó”­Sœò/¬"W×><ž|wsв".Q.ˆ¹ k_V×8²‰‘þpÌÃÒÌŒS¢ÚÖ–å Ëpä¦MLæ0 S_¡¾ŽÍ¨ã0ZDW¸S6‡VxSJ2ƒXhƒ/š­PâŽ}ï몺°®m-;¢ë}ÙFèk2âÐÜl ÒõLø–ê€×¤ÄX@‡Ä}u÷¿ôK¿ôÞ SŸ¶LSs³‘š~¸ùt*ˉJt55 8¤ ™CéñrÈ_hò÷pO ¦;•¦–$‰\S#zôŽw¼cüê¯þêuêS>8x) pØ¿—b›Î"Ÿ:Š\Bê?ÆWóë«À,ýüÏÿ³í7sU •xHÄÔfq»W¾Z†„ÜßDZsn¾Ý©<8xk!pØ¿·þgöçC€ðµ„]~ãçw H %%Àdy~0tQ9ÆLl…a6–`(E³ ÅOÈ=ÜôšBaèÆgê·2ý„&»†aØZ–Ù™³Jõˆ!–N´Žöú­ÓÔÂÖmÝç+§ 8jÌ8Ó˜hšQ‰Ìè=4`˜:<›¯èZÜ)òº6ì9UÞ ë2£¥¯È¥ÇK±‹›œiúú0]›È„ÓK‘•§Tkå” ïþúÏþàØÑ‚ž‚³¯4+­)¾bGÅ#|5-ßD§ˆ6µï»ZÀ¦ó$VH%ë-pƒ|ŸMöúGýcåð,õÞ$lÙð`9{ËÊÔ>í)QH5þ®g+iÿXîµe‘ï¶»³7k0H ¢®¤c“¸gÆÜøœé˜b|µý»ˆrŽºSÒ:N— ޽§ŽÐV¿~-»iîÁê]1-¶¶‘]ìÄW…ä:‚rš± ´ (bÂØâÍ.jËÊáÄPa°'³Å6—¯¹RC\×Gk “¸~r­¿–¡S/ ¿]U!{»àb 8އ̡ôU¾“GhÑ}ïY½HQ†²Î9-,¼¿«Ç#Œù z<ìyz p ð‚@à°/ÈFœeÜ…ºÇ/.ëúë[ÙϨ4”(¾”r‘€ÏKQ.xÊm³Ì×BÉg zJ Q=ÑÓcKÙâ»ÇÛóžµj¶¬ëv³ºÄ—m•JÐøXÄYÒV@ªj€7vfÖ|ñ±©WŸÌRGr²š°lêxfr!3cÔÆ§š}—S„æ0•TÜf<@e½° “áp[Þùúæ!€÷[uzÛੲ¶6ÂvxäƒI+¯ƒ) †”!¢GBõˆ¿¢ž§¯WoºÈ‘e8~eoÏõjlc{†ÎöµdÐÞîÞ –ˆL¬¦ ;Œ1~`Ë_ðŒ0"ƒ€õ—A܆Ԏ!ŒîÄ'6{ºð±‰E©¿™£uï-N=Þ/YCgiþbËØø¶`6 æxóß›‹ ?ü³öÏ‚' ÏŽÓšJÁþ:Ã(:*†MÖ Wˆ›qŒ¬°„+³6lÛºjór"½¶À0[Ø—Å•qqV¥–Sírm÷¨—JÛö ‹z svƒ'ëÈ’)#¡ÕèÛ½¸aª-¸8Lµ\ÿ–ãq,™¼½ÂÚñf9ÔJa{ó©Jˆ:…TȬú…„ÝßYjÜò^}fq7!?«Fqžn²“ºþSO·¬ï,) hׯeÚŸÂÀÀ‹ Ãþ½˜ûrVu ð ~MWff~t#;çž‘4}ÕM݃ ͘îÓԗ¥Šß‡KÏu´ ‹'>è[žÓ& ÁÇlS%eGq^G[kÐ=\ã<à"¸š¤H¥ÇŒð¿þ×ÿ2Qýе†ÎCr´»i»¥RvV÷Ð,dËêuøóõ5A [î{Ö¶kãœú!X+o–1Ø`åuƽY³„A³°ÕÕÓ" í›SœÊ·‡ý{KÀ~&=xSXÝëçç¶Bù%ç@Qîq/32HwdÖM.hšUÈù籟Z$. QÕ²Jî‘üj¢ZJ¯ñªy…N±ÈÜmKš¯(Nmè7¦æZ@þ&;_eü–$´&w )(1êÅ[æñŒT‹<Â^ ù,ͨ˜€z÷!4éj[–}‹½åuµjŠü‰nÞžòÎUbŒB±©dÿW_>6{ÉoëŽtSO2 '_<"Û#zžé‚ÆýÀ>8ßâÍÛQI×êè™G£¨ï‡?üaJË<”|h1ˆò-¯ãLq  0,`öqziƒuê7êž¹€:´ñÂ{ðOkË’;PâmS8úâå(èD­>G"§â•¾¯of‡£§öq2ñ?­!ƒOãlÍ×óh¥¦f-¸Æqœ¨Qc†.\œ¢•dcü8`oGÚõ\_ËÉDƯ¼.~Æ=%옷Žå~àFؘÙŽXdhÊ…çyÿ©ÆE¦"<P¬èp ÒähP«¯0çMÆoæ]Ÿl S-¸ÑqÚqÅ×kM‘p*‰!æš[X(n˜Õ7“ƒ~†:…· ‡ý{« æ=xÄ<ðãê3äQ_ýÅú ®?ÕM›7#÷Í@ñÅѬIü˜>%â~ì­‡Öt¤³Â›™‚”™5k®@}Aäÿ˜ùŒéej5c^ ¹ê­®×6jyæ¥ô“×ÑæëÜ‹Ë@.F÷A&®2³¾»9K•1á›G%ú«YŒ:OV®a>gÌ,¸,fjÖ‚()Ø÷É­J±ÉV|ø½V>… ÝF8_^@öïÜ”³¤gQIèWò¥ŸX\™L_£÷½ï}ê3›Ê›fTZö¯.:7[ª¤OÐx o6£^+ªä½æL’¸¢ÑJ ËÑ+ø+b6Š#þ9ëà¸MOQ´”„”œçqŠ-Dr Ó²RȪø½5D«òÊ[°‡YXº¯ÇQ‚ت•î ÞK±4-MG¢f5’D+{´™Û­ƒjS½ (°æÂ°!u¿&>Bs¶IpŽãV‚%XOT3"=º_ˆ—,Gv`á¥m%ŠvÌ€W ðsYôõŸry¢n¶)ýµeä;ÀÛW/° éÏ\ñßÈ Zà"å«·žÌ ÃÌk`ë´SY;WÈ·õ£î£o„jâE5vn å@^;V“òpåXtqà×e;ºjî ˜R†ãiïM¡Þ•´ŒRÝ€ cEÎÀk~O}(»”ÕÚw-³ÿ”»|³°¥ÐÀM!ÅVé{åcRɾXÕÛX'š2úIÁc›‚ð 2ïá ÍkÈ€¨LúU‹R™_ÖL7…ŒNÍ25 ¸‚Éþ‡¢Eg€ßSL¦å &!>ÇêhïÆ§Wð—öëép S¯à­'{غ€S€³±ñÏ.…3nt\"h2Ìr²›^P [LÑ{¡tjFRT(qo´êýx_˸6³×p#èy‡jÔÎÚ÷! â:X¨á7êžýïÜúñºŽyj^(öï…ÚŽ³˜gZ ùñUŒ½þÖ’£'J÷~/ÈT9}Eÿ+ÎÇÔß,ðâÓøš"lküe_öešÉ—µÕ_¿¦IÂy!k¢ð«¾ê«zÓy¯H ®JÂ\Ç©&‹/‰÷Tê2ìsÙ]ñÊ5Eƒªs¥ i Ë1~cþ„«Ác?]þ0cßEõb_­PFJ¼'ôúiÏTÞ{/àÒRÆ‹µ·¼ê…²ØˆÝ‚û£\׎• 4ž”ß?þã?î~Þ†½—¿¢hXÅim4 È—z”†ýao4Ñ{É0®KzR5À²Ê@ìWþÊ_é//¬ràjÑ_;B։ɋmR9ñZmÐdoýs*®€™))ýá{Þóžõ 9ó¹^;–vż>ª&eøÄQw¶{ ’àx—{ÝÕ·“AãÍ6n%¡Òf?É’»¯ÁWÔúCdž+voN7•Ø9£1Ñœš­qåi9wªÍ‚è€ Òƒú†—n×ÀüfhÓudhÖ8PîZy-g1K|}´ÕÀfƒõÓà‰\åØ5,= gkž¿Õ@˜³TšÒ {Ï›>ˆŽ³-ã|=8xK pØ¿·ìgÒGˆ½Aa̯o9áqˆé•Jë)"’tÿ;¾ã;h®®¤v œ äð`zÑçúQ¬Q,ò;‚à±¹”‰ úÂxlˆžuvæLHUZ´` æ­sBDˆy8&g:R•Ä–Ü#Lgð„ÓÏ ožfÌÅDç@q!¤Ä˜3µæ¦"LÜ3¾bx–âëͪ…ÅϯKÒQªz*&̓éØrgº˜aÈ=]#µÌˢɸ+Å)+4W¥ÌÉ¢2o*ã (&^£•µ¦¤ÎÕí[ÃÐ㑌ä1' ð:6ñ~WÛZ öͲó O° ›?'È;x¶öH3ätÎý¥/M¼v©¦´W ‘CëèÎŒæ`;Þ¹£>£ƒÊu˜škÁe„ñŒ0ÚæÄF„Skè‘–Ídú ©1sܽëkMK‚4ÖÊkÙb²ÿ\åS4YMPÖV¡Akwu«{1rfÐlÂ)èt*¯;97·{moÍ#8H>-xþÂfR\°ƒ o4&4ÒÞ ‡Û§—B²†ÇnÒë2Nù@à@à-ÀaÿÞ°ŸIîB‚þãò7AæÇuõªGe ˆ„ò;}mÌÚŠØ›+q{ÑHâGp{wú×^ãD @IDATÇq!^yæŠ\Ch^G®&ó?Z£‘Ê#rûgXçj(éuœzt6 ÏD|⮽Nê>J›•rÍŒ­EâôhKíИq…L§ÂWš¥tjœÀÌgö§\ŠíÚòÛ\µÁ€‰?ÜR«ÑϤÖ'L0´Au ­M´•6”8ÈôéŸíÓ<5²£¢YœÉÖe¾:„úõ¦‰TÇ5êߣNˆ£xÓî·ûâ:Ì€7 ÙÄ~ò“ŸÄ„à߯ßÕø¶Ä}–Qˆš7nC%ûxî\E¬ø=6r†mFàõÊÒS´…A_@¡ ¡M¯{…LèÝÜ{ ¦²5¦S157 ɳ “í©›.“;,~©g1xm}}íG„½É&ï+ˆëc<ûŸø¬B®ëh§æ@à@à­…ÀaÿÞZøŸÙv+çt|{ægu³çñCî"¯Üzh¶[œvHÁ)ú’šOß |ø¬ð–y ¡¢Oˆúg¡´¯lùNíãž|ª²<ÿB‘÷ V–Vê&Iñ…v¯½fjÂrÃâ"Ô£V%aç‡Û4$^GÌtß½H-ºçG‡U[§¸–¢å­Š”­™Øôf´/ë¦ s…Û¡$)ñ U›ärc&˜¿Ç„§À¡+‡‰?&X¸,jª<Nf‰q[á|Í\‘-6Ò£ÈòºøÄZç4žB:Éa€õ•9`ÍîÀ&p¬é¤06ƒ_PŠ>»Ö¥[f6 ¤ÜÖ‡ã¥nj/g1O¤«ªH±:ÜYŸmÕÛÞ†—FÊ®ô½Uâ:$m—ßÏÙÃW}­±Aµñׯ­ÙÛK`#°†eð¼F%LqZÖcYXQAÿ§×VpÍå@:–ÙIPc]˜FÑW‘N¶Ž}5—§®ÃͧUbÃ2‡4îCÊÀ9w:" .¨=ï “t Ÿ«|†. µ†«ÙÆ„p U·ÙªœhJG(+6³lWãbipÛ—ZOjmºí+d ¼†Ý‚ÊlÍ(`ÿ‡½»wù/Iê‡Ïýo˜þ]ÄÈÔH1SAL4cQ1Ø@ QM3#3E ÁÀ@#QÄlï×Ì{§¦¦ŸÎ™ï̬n‚ëêÓ§««»ëÝU]“`eVâœY/£œ~ù‚Î|:0+A Ç=ë‡&gÿ¨C ÷ñRàRà+¦À…_1Áou—' D¶žMq{w[#`/çâd»eq$Ûce"aæá0Âöö–|„ã¢3ôɘ¢sùM%¬ó­Ï‡PÍ\³µ>m·"ñd8U·B YÌ}‹’¹+‰ñ97Ü ØÇ«°¬ÄˆK˜®`¬WØSi!X,ŠQ±kÝ5%%ptÒ3ö°‰ .ôH7%i8;\Q‹Â­u9êé…£¨4I‡øþ˜c‘ƒÚ<‰£àê¾s-)}±ÈÌ3&¥™¥6ë šô–,Ãq“óxV%o®ÚÆÓé\”q!- ³q¬–„8êgög!s&!1‹%üâa.pˆÉ!—íi¸j+\õË*m;J¸——ß%¸ðï»d n3.>òå¯ÃVjOo†¢QÁzõ*Aìñ±Öã`Cn`e±¯¶j¢›H¯È 9&?9ª¡ FUÑ2à„>掮#.ôȨ=Ï_yö¥2Í&ÜøF)¹;Eïz{0\vk¼EÍh˜z„Umhbë›Q£Ê£x1Lƒe]T.nì %PAÄ·q%atz> ž¦eÈå‘ÂÄ«‚Ü( Ï»_*R{’ž>¹ÚF4Œr…X†>5Î,íì 8s½ÿWctYÇC3Ý:q~ù—™Ÿ¹dÝÛ]:Œ-=ÃQ‘ÏŸH–šµ[Ö ( ÷?õS?UhÍš@U•/z/?P9è™1–ÃxeZ¬dC9‡ÙaøþðÿÐÛåW(3¸aÌ +éÙ+Ö9…¼T ñÆésó~PµÄ2Ï,SFC¸CM•EQhké°€ ‹Å¤(¬ ? ŽeÇâÛÎÝé[ ŒõÌÀ©×ÈÎd¬¶%`É5ÖRÆì"Ç|¶Ç¼.Õ9QZë/½"è Ì»Z ü¡Ì|nKXYbx<üq¡\•VÀvv°4©2oàRàRà«§À…_=Ío—k äÒݼ…þl¨=oÎk#‚Š' )¨þ Š… ³m/˜‰RRR!’0†æºô"%irˆŸ)ˆ¤d(Øõrs21e+5_NëésùydE²c·æH»_šJG(C¦œ%ÄšhíØÂUC€m§Òœšñý‘°eWÁ$YX©¹tYå)âÄÜ]D£Bî…÷p(S±ê#s_,}Äâ—ºVÎ'Ò*Ât GEýú¯ÿ:%^zßéÕ%=*ßô…IˆÚPÙëóqR棅"ý@Mò¥ø0óÀ`„P®H‡{nÌÿêõ¡í)ôÑ ü~!nÄÉIŠtRÓ-œ£§5¡Ê¤Ö YºÎª¨”Ï”—¦ZÀ)@ÇM+ƨôˆÎ…¹¬e4±Y©‚U&Ä•eäCêóÑÔîȃî½O–¨Îª|ºÀ¢Å”íCáÃcÔ¤1Ã^åZÖfÇFË·"õÑBa¹è}Ì@XX ŠE¦ß tôãíYåh¸M4][*-‡–ä³1Žo†øáMâþסß`ƒ^]“øaÂc˜i Æ%Ì(„¼ç£·dDdÓy`Ë¼ê¦æ!¿6µ¹Æs)p)ðµS࿯}n.>¢?l©kãLÀñ³+ûFqPnûÄT[üFœ÷ux’xç€<ÂDÅ´A«H9„? íý´^‘üdé­Z†sFþÆÒ/Z8ptYN"¡Öø~èqOOÌò­*." Þ„†TR,Ü4ÃGϺƉܣËú{8t‹ Gþ½¢9чn„7‰£ÕIíñQ9ˆ ÖsöÄh¹,‹©\¸"1»ƹ|>–ZXôQ©Ýc×U]€zÊâW°-üßùrvòŠaÛ¬x”=ˆƒÍX€A2:Ýïc‘ï…”nÙA¾ýaêû„à¡vîVÍÕÚÿ{ÝìãEK\„Š¡#V ZƽÄ÷(¾˜)œÅC‘(––îîb§¢6€D?ÓoêbÌ`è——Órœá4S•±¨Juãa¿hªõ`5UàÏÿüÏ¥Ÿ'Ž„Ÿ¶ª÷E—ák`)äÑk‹Z$(У³Ñ+v X ,–‹Ã`o¯=¨g1Ù!7½ÈÒq¾ÒlAÓ°xîí™ÃùH©åz^Õ™ìRÔ㈮;¿Q²Ÿõ¹»è¬’Ý4úˆ|naÒç”PíU%ìÆN½óñŸ[è}L?nÝGÍÓþ]Q7þRàRàë¢À…_åo½—Ÿ¡@úg׬¿¤¨žŽõWþ»â¥'èáXõ8ó'èá@ €8H¬„¹­#«1ÙàÝSf†ëv½4a×ää%$Å9Íð¶?’xrü|öiî6”´gUfäiÏ®ÓôKD²£û%§ìtqñZN Sç@œjt}ãœFL´gÿþïÿž·îJW.u®hˆÂÔb³'c¤XŠ…e"ó‘†~à ºâ¾¿ª é.¿Š·´U¾¯\(Í%/<@TD^’nDg²x2š9ºH"¦kQ‰W8':Xúêp©·‰!,º%x3p_‚½IÕ¶ÿK¬O#¢ä`† ˜¤´£"òzé‚Í‘‹nø¤¾Þ€d€ Š™kTXÃÕ/zg~S¦–`‰™Î˜§ iFƒa³®^‹áè`”Þ‹Êç4Y&’}#tãåÊ_Šõ×' ÁÝ]V4ˆƒ,;ÃÅnz*ÊTí ÂQšQ‡šø¦¿E ,Z-±PX.b‚!åPmE„$Ž,èXcC®(SÃHoä¿©½ÙŒ²³Sò1«¨;ÎߎO]Kt14ƒåg ´J5$¨G^ï5içs‚»|* N š“›Éß äµv]D¬­ÊÉ~ÕÒäc5.M.{²|K²N7£É¦‹ ‚]$§Ôd\ª"îÅö°±Ž¶­[ŸÂ·˜ =+IbšHJ.ÁêÏWÂ8ç>w@ÐyI¼ÆSÎ uÔƒÚÆ-åú5Œ~88Á+’(¶qF0Úyå§œ¥éiïÝÿÆpnë¥éf„ò|ÕÀ#:°]½C:‘Ȉ˜IŒ¼A/²ô!ΉL©{€ô9>à&{éߪüJ­1°&I]þfæÃ=d2 Cñ&°YHƒ«Lóˆg÷ZËidœ…tëÖ¹(11 ÿË¿üËåÛDšž*RE¥1…MdUÈ^üœÆ˜ø¦¿E`7©ñ³”–‘*m€Ò "‚ŸUp…Ï{!šçöµS!g(ð¶BŠPáRJ3&¬<æ5Ùàæ!n–zÉC˜]«åW~eˆŸñR¼Ic†ùíãZcÔ’óq!ŸOóšÏ½ʼ——_%.üû*©}ëºXP ßZèÛ³(z€ÙëÚ\¨=87væBæÄ6æøPéáòLrq6 ©¥ ¡ø"™ ÚÖï¥ à#mš%v›u¾^ypó0âB×S i<yºB6¿bh#Õ>Íi<êuêeC¥û]sÜÎi;¬[&hmÎÞcr;îñÏ„N ‹ö† ãN¾G¢ÐÊßÈ©äÎŽ?{E¬­$;+â̆ DAÁRXKsAÑ ö2+œFV’;K D]cpóe¿ÁäØ×ä´‡u™s V£sRjPëË̉}7¬T^Õ÷È©ÞTÛþ7td€@ÕÙÎlÂ]½†h8Hа S> Øg(ëÍ‚ÖJ&ŽÓ·wÃÝ({—Ž=Ñ3sKTc° †Á6™k4À²cB•vó?ó×)†‰ÓYÚT¾åPšä*ÓDP‚IQ1Ë@>uðˆIr¤bš¶ÚÐMLuЧéò®c¯:·-#=rŽÅ¾É²|›HdÑMš^x3-*qæë}ˆ½õ£·ÕÀxc:˜Wñ´´›Ë•=>oŒ,L^‘»@ìu-ãÇݗéÖh¶CŠydùú¸OŸùc¹[–s#/.¾ \ø÷µýVz)ðm 0û® dÏt*<Јäí•ă'˜!Y}êWâ7`Iú¸tØ?oä½ðH4o0¤ïÍ. ´‡b €!F—SxLÏ=–Ç#y²ÑŠœ÷xáК5 I3b'Šäì2h”À÷ ˆ,׫:MÎÿÁ b’å$~8°Ç4¥nRõK>.ã‘ìÉÇõêjöÈ!HVîCiM™u)­.ø)dOPîv§ áæÑ+ÖzU .#{—¤ykìŽX—ÉB7•KÃÕ)€x ¨Ø’‘>»ýXá¦ÞkEùiêýVþûØ»>Ç—~ùKÄ/¬åè!J6Ó!ÝA(ä*=*2"fæZü‚@8Ì8‰æÆ®ƒŠO—5Øì9ÝÁ»otÙ1@µ*̆I°JZhÁ÷ïΣn±b~Jý¨XfN*ñ€L“âP‹W¬C•¹»ÀfJj€ عKzSX Mg“ºl(‚x]«;×héÀ™zñc„Ù§L• ü~Ü8ѲÖLÑ\°§™„ô,˜µ¢f‘7ôç¥X-²„,ËUé2õ©›„Ë·Cd\4Ù>†øùQ s]Ð]Óùm|Þôþb³GzÎåܘKK/‰þ}I„½Å^ ¼¢@ û6™0ŸŠ$o'Ù|¾± £"#HyEsòX.É Ê* ÇÄD)4Ü)š3úœ€Ĩo~Ûcø¥’|£å$$ö]LÅfêÄV²>Ø)ñÙ;(<<Ù¬×+Œ\J{“RªQÊåÕDXE9$Îø8•,?j b7‰6£‹2qƒÁëæÐ˜á1Òçî y1:XêÔË7¦©YX|Ñíˆa÷5”Ü%–¦@E½"(kaW;°9tŸSOq—dþBzäær`“¼¤g8$ädm3jnvi½:(²ê„ÔÓeW~ç"¬ÒÄ÷Ÿ«bon+U×¾ÛØ£cf]Cîû,‰›·àˆÑˆƒD%FÀcz‡ŸbY.JÅê„%£i–Í k’*Ë+ƒk UÚ}3ÊŽ%Ô>dçíSÞƒ»#'PYa ¬J/†òÐYF¾ü×2Ö–¡ðzÌ…çGýU>ñSM=Æçƒò\LXÓVO—Ê.”æÑÜTE!¸rªÒeUcÿ™3V 0-s_Ë]°™êê‡ó *û¸|!Ò¹Ø|¦u6ªœSÆHxù•—ž8<.Ùßá|iÃP¾Im!šãLˆ\6,ÛÖ|E$Ë#;+Íž7{÷¾h‰° «æq¨†xJðåifÞrÊNV^V×#SõC#¾(5òŒÙrÁ‰˜åCêL¶ºUj¸.E”„"Ùsµß[2‡sÛp)¬'q¤0C¯¨Â±i¤$¬˜(ŸaB€­º¦à § ˜¸ÜżÙ[<3RÆW‡³ Ä“-½‰fº™tŲ# j˜žÁB&ì²DF¿½tX2ä ·˜ ñÃ#£ÜØZëfÆÈÕÎìØêsz”ƒ•®9ŠªÇ¨^Ù¯VÌ.`)¶ £Æã¡X> òh:‘Š¢X>h m=6 ÛéožöþVØÉBwC•xl3è¢w]»ñ——_6.üû²)|Ë¿ØR ÞóKš‰ìC½Aüø±Št O»²;L¥¢w"pUYÙåà™+¹mÅíLeo^ð´TßâP²?•ž’uYÓN'uýUë#ù/EçÇ%ž– @àsì^¯Pªïؤp˜D 4½®e8f‡u?m™&ÂMÝZt"Îl|%Õu5ZÚì’’·ƒê¬½Í!A´ݯc?çñ¨‘Zò—ˆFB£È^…p<.îäà(@"H d²¬+­”’?~â'~"š½¡ä|tWu9ÎÁ½é—ï:ôôdÄ( !HŸÊ7ÊQ¨B§4Áêâ‚“’zÌgÇ{±ßÍag3Fù ãÕ£š1R{Р›eøD%%¢ÕɱÞÅ¿:!’Ýê“1ê[~yÏðůLŸVÚpÇè¨k§‹˜g@õ$ “™•¾à“,V‡9nE’Ôß  ˜˜g—ÀL1¡â¿4õæ/Æ0MCoKIe’z‹»v¥‰7@š ‰=ZGu©ËCi–#ßp¯2ìݦd§IŸ¸(7§ þPàüˆC²~¾9þˆ—‹ó\N‰gT¶÷—ËÆ¡S6‘œÁÙV ¥-ÆFc’Útz—+ŒV¶*–mËæeyWTV›J#ðÃ?üýa7|)p)ðuQ࿯‹ò·Þïu °_²FŠª ÒëV…¡‘S[ Øq¹Dè!sÏ] ±òB\®fEì³I6K‡ïî1õ„ÕMO#LˆœºÄCb0@“ˆ¶CüòÑÙ¿ÄHAuà«bD¢ …F¿¨Vd%¨pÊ+ÎFhssf~5Äüó?ÿ³bÏ)y¶”f§jÓ†¨#‚Ÿ«‘F!êS·`$‘{e¸%IoXÊ"ϵFU”€#vw´Ü +98]Û)UrÏsð©§òË¥’±„;KtSãÒHw¢¸€oÔ ÷Âlâ©­¼ªÅle$4áƒ[¸Ž,xÚ±ë¸ÒÁƒ¢*únðp»Ó„ä8ˆÑè0ŒÒAdA$ŠѰ Üël¨‰ P_ÐÁ ‡ŒÕ}…2×[bXå5Ä•L ŸÙ©yc† äKÖg¢xø“}#fÓxÍëea–Ò̪’™Jе~Ö`ʘ8Q¶‰B(Ó œ0évÓ?zZÕ%ô‚àCüühqP¯aERê;Þ‰é9kDÒ$C`Îú둹æajgµßÍÊ¡v'€ 4ŽCüü˜ËÀç@µ9AÅÀ]Êì^»êUØ2¬3¡α¡ £ ýµÙ†lFTÐ1‘@Ÿ^NÂF¶Î‰dÌÿ›9幸øŠ)páßWLð[Ý¥ÀGpá§KÛöE»¬7ˆwÑ®{ú+;«ë=!K$1kMR¦]Ÿé—«üNÊ)ÌÀ${¼èO|”Ì~ß° ÇDppp²L«6¦ËI©°A¶‰› ¨ùÃ);ïÄ2D¨#m«4¾U·u>¥½lX"ãš‚çÒC¯rÑ‘b'—‘ÊÜ1­u÷)—‘"¨U‡Ò"v—²q™2ÞJ¹ú ˆô¢ ¥©Ä»rDzÌ‹;ÒÛîmé£R²;¨Ìê öÇú´3 %ö(¿IÔEfæWÝRZy‹ÏS8•K^˲Úƒá¹Ø|2Ô¸÷3AˬMÁÖ€l6‹š›Ê̬3¶ ²Xo‡åÚ¡ö–¬V‰^;-è@C‰Áj/á†/.¾ \ø÷ePõ–y)ð@Æ–Ÿl¯ýÒciÃã X†^%~ )si^’ž˜2ì»ÊÃn p"¬D‚¤šèZ…¹"NWòæ·CL\~«zÀ™C²<æC; ´ÊBÆÕr ˜M'Ó6Äû¡žH¯b!¹ÓiT±Åb£Õ#‡pä`Rõ?<æ$“xäBâ²KAÝð/Meõ'}W‘õÒbÙu†ýmÂQJT \/’eùì*ãNà‹xÚ !â§U; ,æd¸Á»Õ±SüßøßÈÅB'½Lar¡º©XCc°@;Dµ[YH„º¡ g°ñ5"Y¯1%ià„X-V!ß%dŒ&-í¬¿†ÃG,À` :‘nÇéf½õ¥G·§ª ®·2’w "5ê¹>'»W=qråðÈp”®`A@céïmÖlˆnúƒLNÂ~i¤Æè— ¨K²c?ÉÎÊóÔby©nö€L„ºY”Ñ*Çô1‰Š{­W<ú¦Êõ¿³^::EuÛ*SÒx¬A7hs9gÅRK–޽÷%çDÖÕ9‡c ùæpJÞ|W£ü÷Î¥UŒeY,ÑE®zÕº§2Œi-þHªÁY(d¯ŸsÓö‘C7Ê›•é”°Û¤íWù P*ö¶Ýð¥À¥ÀWO ÿ¾zšß¿×)àNKìÁú¦ºÌFM<=ÒêØ’ÿáþá‘jÿú¯ÿÊR‹ÜV*½>)Fl²å3Ñ!®Í‡»b˜/úmÒŒœD(¬ê"ÙóJR1‡@ Úƒ=*»±â¡@4!id÷šàr”Chª?B Í@ Í¡'âšµ!å!}ËR| ´è˜dngƒ äuf~Õc¢rôɵ™0ús»‡”`]¥Xº-äШUÆ8z¾ÇPoˆ¬?ãLõÇüÇ®$u‘š*ïg~ægh\THnv¯‡’ó蚟䑙œvv·¡vÿL¹”DÏœðÔµ(ˆ. ®S¬^gôËò3ÙÁ< ¥º€cËùzÏŠ”üUþeØqlM.]Æ®~¡[º†ª‘¶=T#ùaÐ[–Xr£VJR®RñU.ÄßÙ^E†©lao  T–È Ü!‘½»,„þ8bÝy”I9ØObÝìÅa3W]«xƒŽáµ3Š5oó35LÓÄd麩ʈ£¤|üvyDu¡UBòxKŠÄà"8/ÅÚÌ,ÂaœñBä82­æ]îXŸiRÆ*[ue­0$èæ².?BÊd‰Ë( u/Á2Ž-évË»E¾øóÛtÿØäij5Ø l6‹¢¿MDz¤è T/¿‡]ãÌ—~–G„g¿`œÚi/á†/.¾b \ø÷üVw)ð-~j°Ë’3–FAùp3ó†jù>2qÿ˜˜Îß:5ZJží!²þ¸å ˜’D#«ÍnçZˆVDIÙ;ؘ“%Æq»º–ŸÌêYrWíìž ‰zQì"d… —ºC˜s±Þc¹”Ï'Àä’^׎ÔÞ¿£=$È#B©k飧O)mÜiÒH`‰N…£):K‡¤v¹ á÷*&öå ƒ) QÒçxe +9¯0µ¤F(“É<,vÈi6/öÔ§=å䌟êÆ#º½F}„’Ø_Ù&±¿Ñ™íéÅ‚.Êü¹Ÿû¹ÊX`RÌOÿôOŸG­Êÿ²>Õ0pZ ¿i!•Z0X"u0ƒÂ˜3$_ò@Œ¸4ŠoŠ˜¯„  3}ñÁóM‚*V!†¦Ùpä-UÞL(G!ºÄ¿kJðÂÑ6¨;Æ{øP$¯KÆ:ñ¬Ã1Ñ”£_ ÄÞ}Å›&Bt˜¦Æ¡.¯2wf<äʼ6m‡xð ÷' žì°YKò3;,ËJ\Y¬ì¼yKKY1»@ì?]kZîøØ_µXrâó¤»J+ž)¸b-Ô–k‹¶Ɋקž·ù¥ï(o °œ†¶¹l+UÑ! êXbæ:¹\Ȭƒž´ÄÖ³ûΡ–ûêRàRàCQà¿EÉ[Î¥À+ @ ]j̦¸ÔûrþÚ%’]ž£'<+Ó*{,ÖØ#Ѥ‘¡‰\˜Û8ßÚ?ò‡D^>  »åïü^ŽG)-m ‚H|6¿”’¸/ÙàU²zѤâ554B„¥S ®súè£\tq÷iÙÅ:óV/!u— ªŽŒˆ,³ üîïþ®#K‘ŒÿöoÿÖ'.ÜÛ™•œ(@ªîä,ˉoÉxe\&i(C4œ£^úî2·ûè7vöŸÆZv7‚æò)ó$È33#7“ð¢(¸";2œ¥Í#’”N¨ („Ès8ž S¦Öú°HÍ”ðvÞ‚RË[Fsk¿¤×qýiOþ¦©˜Ð„Òø"¸Néì!áŸàùÀ3Á¨²‡Ö²Ð»y¹¨‘ DéjzÞø^rç°"ƒÖ±3›ƒg@ò'ÒÇÈ*ŸµjeŸØO@t~%ëbàX§©ù«v í1îåÄ ‹.Ë©H¥„3Ú”Så`×Y葈ùâ|yXiÆ…½«uo¸Y• Ò¼9}ËÍ::®ž}zUcžùÕcSX#çWzjÆKdËr&¬ÄÃÏbnI·è¦!Ù "¤ù¡ú¡ÇeM¥¶,g굚i¼šó&C?7XL¬Hz ÝŒíçË\7òRàRàK¢À…_ao±—k ”ïa‰SÛ¥Â'~Yx>XôÙØ[?ÿó?ÿÙèõé9rgׯTR! “ɇwy±ïÜdn*|=ŒÓ ?vqò\<‘,.üû°ô¼¥] œ(Ã×¾º¶¤2ç8™×ãÍY ¤d¿—Õåç£cñø$Ö8iêx•$Ú°#>ö^«Ý_"šd/eõ“øl§*qŒ¾è»*ã.àš“I6»O”Œ`ÍL1’Œõ£Yr†\':`ae\M}bâÚñð}ÅÜ•$H¨dÓî‡Có ú÷sh<ž Òb1»SúGJ¬ôË¥ï„6ßd§¤"ÈB8"7»¢dåˆ&ø“² Nî§u)0÷ÍȲÐKY¥’b£ÔBCk(Ì”QÛÒ*¹—Œ(?–6Ÿoo.‡ì;Ž„6s¯Ú¦ËÄñhœhT:,ÔQ—aÙgr‰wÜ`€0 ÌÐïa"ÒÅJ0YÔ‚¼ˆœ¥ n] Á²#LFƒE;¹ÐY øÊ!Ž…'‘´÷ö¢råu¸ÛÙc?m3v[b΢‰ÖÅÀª‹ ôp—5°Ê1ìPK ‘]•¬H€<ãÅ¡µÛŒˆQ÷¹Ä¤%ÓëÊH9©‘TOoŽBÆÝ-Ê*S Þ¤8ìé‘s8•ÀùUÅX<³Þ¦öŽÞ‹ÎaK±9ë­%:¼dÑ®r–éòòVáçÚYÀû|1ÂuèeÕ¾Ž?ª#KË—eöy)p)ð)páß$æ-êRàDº¬HfÙù"¯,K ¿ÿû¿_²G³¨Ô—›'¬€NÕò.ž¸Õþ¨R‹Ïú®Dú¤Œoé ™•ôÆ%=xÖïnÕ¾®³ÂMÝ¡eŒß–æ…U…@¼P S%þÍ?MA¯˜]€h¥…Ó6šAÿ@¯Â B5>ØƒŠƒÓ?_c[’‹‚EJŠ…]u<ÉõEÅ ÙS"INß}[o¯ÕÈ‘ƒ.µ/EØà7i†*ê1>YÇÁ!ø{ùF )"¥ÑÎU–X*o‰æ´ÝI ê1]ãý2¤Ö-w©àŽž”B]®¥¸¨ê"¡B)ñi‘±ð2IË+F€™âù Soùw¦~‰ÍpUÍâN×bïZÐWƒ£¬ÓДG’wuÐc÷Ù“ûÈ¢UH¤4äB´ª_!iÇ<;õ‘„XJ÷ÆXPÇjÝ_¥ËŸÊGÆ)¢Úå3iN€Ù°Æª‘ `!Á®~ä£Oü¡LÓAvScˆŸóÙ:~;e±"±;-óÚj D†žÁ๊‰ŠòÍÙ%Eù–—Ê» ä+—Ú¹Kø˜X+úX-’–J fÁêšÖ2k±Ea ¯å·²WY¤ïF¿õªœïd’:ëéñ»p>Bh‹Ù%èñ¶6`u§l=¾ÂñêWîµ*û \ \ |( \ø÷¡(y˹x @>÷œ /ú ƒ¥äJŒ¸¹¼Û3Tó¹nhÈëj™Âå ÐA–€~6gJ.‡ÄÊdœæâqº#bq:[‰ÂT´RbˆKŸòÕ 2+IˆšÂÁvEîQ°Àv *¾Óz­xo)„‘qA#X‘ —ÆûÞº¾t+¸^ZS¿HIÄï‘=\êºN¬0QÕYµí õÒ8¡'°Á ·ŒëôB]Z[Ù‡@€=…Râ‰õ<©²ÄcØ5o¤ä_ûµ_ãV'ø­ ÃÙD'(ÛÔõ6-!—3%Ey‡Þª+EñLˆÔQÆJf¬Áî½%éŠñŠh…§G?Ÿ†#‹Ç Ô‰KÏI½Á_$ìÊV4iƒ0TF?V†¯â58I_¹áó$Î_ýê (A¤@dAœ®Q—‡óôÏÊ€øJ0j\°øìÇìÔ4u°<0èšt ™¹u!Sa- †ÍºzS!XCbËŽ ÊZž›d8L嘻Ñ1÷ÍGæšýX!$õ×zB‡OEÏ]m@²‹|â©…w&>šºÇ‹ÇÔY^ÎzKmýÛîÖnJ°nXú4=1IT§ -¤4¨aŒ7õjó΢} xÚãpPó±S|Þ[åY|_qWxŽzyn`îʹñ——_„þ}êݼ—o)¦õ=Ï>½üœƒsëø_yy=#Æ`,÷Þ4…hBSáWš–].B•Ö¾<ôÍ}$7µzilÕx¢ãœ@¼+L1ßê&Aþ4>žèúUÀx |#£Ð›Eî9ù§mDmõžóɾÒÌP ü` Ê"t€0ÍŠªÄm·®¢éD¨0¨/ËR©’4°LeY4F2zQCSÀIŒ±3ðüÁ[ ˆ]Ìø!eÆ­ÈÒè e¢ÌÍFÊÌ_ò(‰Ùˆ”&'ö¥K²˜»Õæ>˜k„¹Ž•r"C†™P‚F°jüÆ7¾¡qrî¾ÎÉ÷*¤Ñ/tñ TY(OÞXJ/©º‹d¸¨1½Dæ“&2n≳š§‘a†hÿ¼"ÔÆx/h­W¡ûÇ!HÊA(äB´tÜÄéYŽÚ mi¥º²¯RÂ3ñÓ{@ 1BÆsábôWc”fi2Ø)X%-ôËa¼¨å±â²Š Ž=,5¦ƒ2MÊb°h˜žÌG{ÓhÖÕþEÌ—ÊU`5€ªàk½ê L¾YÜ,)9ÛùIêÅæT¥vêN<-[â,tÃÙM:eaÄÛI#®ïÇŸÖ£|š¡GÊ|4Yש¬fôÿ½ý»°Æ+3ìôxê_‹‘Ú]‡¶Tvwˆà£r×—_6.üû²)|Ë¿ø×Wö9# Ú×—¤ÉÇ‹© —o‡Hr'iøå×™äR…p(gx¤y‹€©Øðj~tìµÀ£r"uYä3²r—}‹8$ʇDZãrP=×8ÄÄ H®!~ù˜bÏž3cÂD J šƒ=¤+Çö3šåÞÉ>ᓆYXŒ,{Ðd*¸B*ó ´rÏÓç0#¬ u¡$® »3(³î.Z—q*;¾2¿)–^Â}*VCÁmŒÖƼÐÛºôåŽÍY O{÷¼q HŸV¾g…#&–q Ë·ô+}Ô’…ëÛÜžãG"i¯N¥ÔJý« hNñ…¼tDÝ€Á؃{C]ÖñXE¦È‚8岈Gñ¤ón±© ZÆd©¿ÚÒCæš% U¯`Œ¥MAè„Y ­X%‡>]Õ©@ÝÄ`ØLRH¼§<~º-ì}”“½þšʧàâ2‡ãœøªÞ àÄdCYJÔÊ» <›Â»âó ¾Ç£–”`aы̡À¼ú³?û3) ŠY#× r¼Å±9&° Z ç…b¨‰ì¶ ƒ;¼š9¿QÄ>0Ïœ’ ƒ”/}2-Ž .ÛÍ\Ôc¼²êk“´âVEŒŠ&Ä•,¿3\Ÿ«¸1——_þ}AÞì—Ïèúd§ûè?ÁÈéuYëUìv‚Žv~ù*eÑBÄqâðj~$I§Èv–6¤1$éÎ…Ì1.Iüø¿d$ HÌÈ0v}^ ʼ«Ól¤ïô áëÌÕ@™´ §ãÊ!J¿û]o0@¢ûˆq¹¿êaŠöKSà cÀ$Ñp®6¸¸E¢å¢ƒSÇíŽÃ•9XöJsPQ=rS(Ç-²ùŽò…”D¥©‘‹j†Ù¢¤Äó–xÚµpCRã‘…ÜOdºM,‰P!t_³Ò•²Ô«ÙÃN…TÃ(ÁP;.‹¢S¢ïeË'Y‡^|ÉDPf*pRJ¶Y ¤8:Àì¡›ËG„*ª‘ÑLæ@¤ov@ìqºÖ÷TˆàÊ4š¡O>U¯ƒºÙ b•£±tímÐx¯ØÄФÅð΃ÂfU5 h:ËâF_öɽÀ6ÜÁ$Ñâb=ÅØc #ÂH \*Ù°ŸŠ–úÉ^]šÝoz š:  ¨äOr¾É‰ÔŒÞ©šΠR >TãáÀ¢ê5m¥D튙è£Êƒf²re1œ‘§…ÈèX”,M†r>Ò še %-qº9ñÿl1¬ZXK>‚픫ãÇÅPK oø”µ³»šÍô”Ιá€#'Ö}ò¢Ãð㸗xûƹNÕu——_œþ}qÞ.(°û.Y‡¸O´e]ãšB>ÇÇ|î¡Ä_GQ@n{´|1<ŽR(‘¡éÁñ¶«V\6H`Ñ¥¸«] Wõ Jc¾ø¦GqB™ÉSɸÔp/› ±Q„Ú¥ÓEævºénÛ®ñ‰'nJæËÎçd±ä! íÔ#"+š!Z÷D2˜ÏŽ ~ZÎ&™¡GK;A`LÁ¡í" o†»$ïL&4cUû7JÅ€®àF¢9O›es ?ë5pE8.üûà$½^ |†Ý×YÎDŠ;¿·ùíŽH‰e€@NRÉX¶¢ð,Å*iþ”ÜgªÿäÁy¿bÕ8ÆòþÓÿ1a¢ŸœÅÓO}ÊHî>}·ý¯ ´à‚Ù6ÑÇ/‚ã›A.°Íq>•¤„A"àEÎ ˆ@,T‘ÄÍ¥g<ç »,'wmd"åì$ž€¥;H:÷){M„E.vÁ¥ªJk¡º5:ž<¸‰*R³½5îçJÃNýs²˜ÇÀ˜· cÊø*ÆxiCþÎh˜ÂÎêÐXÀ¾µ ¯›E—Ùïõ8'„cÅÀ`s;A8D3ŽÐÃE`cМ O} ”Šv·š­Í¬ËR)æ§Wéú·J&@ÛöæP -¤•‚u,SH¿{¦ ârÐi©@!jŸ0¤ã›êiÎY*™ÒtP7uV—åÒýYë›å{ÞV/˜†Rg ¢³HUË0[#OÏÞàbäY… ` Œ=âÊ(7KMÁ™{H÷¾ ôŒK§CªkÍVc‡ ¹õïУþèx%t^ô”¦­ZLá9‡£ÓÛ£X@ÌG‹‰%ES;‡õ´ÇrdQ²4Y ¢PÕ# ³&-±Æ¤3¹ææõ#lçƒ=~–8Óܘ.T¤‡W1ÛÎsº‹‹2«c×ãȧº?Œ5³[× ¬9. CòáÆÃ öÀÚ*çÀNÕæ¸¸øP¸ðïCQò–s)° `÷µÉ9õ¯sY'0fS';Rf¿ìhåJ€¨ÄÝ‹“ò\¿!VÖyê¢úÁîÍYoÎìöSv®y¼üvSD.‡Á­]‹ M¢:D$SˆwÅ %ƒQQŒˆÉ¤ Þ#¾µƒÐ"^ó¯0—Y1LNb'Yñs&Wñ_ 1„o) Ÿ±ói¬ Eñ .n)6 ÌG½W:jœ¥[â&¹œ—”0[‘ &aÍèFô‘°7ä¼`7 CÔS‚K>¾ø ô2#¬ÃKKëÁ°M} >að‰1ºÖ”ØMŽÌ¤§+…X)‘°¦êo7׬~JžBÎ$Õ_˜,5£¼¨ÊW¬Â}E½· £«ta/³[ó¢©Üu}²è”®ºÖeå'½,xŒ:`ΉCšó)6ÙdúF‹4ùåò[ðü'qŸùŸ{_Œ6k ¥5¬Õ—”o˜06˜±Œ* ß6Ÿ)zz¯bÅz*˜’lViÆfuÓhF‰&#é`ª)I±VEíñÁóxz…¤xϸÏ6ɽdk‚žÒÞ‹´8x´Ph¡E£x,䪿– ~ÓÓݤÀŠ1ªÜ9>©6ÄÊ÷ÑJ3émš¶•ý°tKl?¤É«ÊÒ”:%¡6¤“trapw‹jHAóO[+/[<°#uŸô|Íw«aýZ„ 8Ÿ£Uʸ¸ø°¸ðïÃÒó–v)ð ä†}vM‰ƒ]¤ëI}…I6EL† :—‚ˆÔnøÐqnÎògÀ U&ÙΡ>a¾1ìâlKÜLîMEÁâH»W:‡ £Jfé4”0§£Ø „Yá°LŸ΃“‹¦ €î²!2â#Š&‘“_骢OH½„B?Ò†F€÷ê²+?‘’Êñ%ñ~‘’(^Â*UÆE{t_½5²,]ÉúNUœ‰ôŽTF›¹ƒ×Ÿ0d\Q‰k8˜‘q½z»>:ƒè(G¼†ižFñ&¥,ÌQ¨ê8ܨÄî®ö ¼ÄÏA¼½©ÊA]FðßÃTvªÆcÎðõåBGŒˆŽCWCß3öpü¦À=rç‚€jÆÁ·³^Hu$ÂÖ±¹„Š ’4A*fHÊÝ8ö\˜)rmµÇ'lâ›þ4{!ÑìY@,#´°X^¨Gsis } ÄPâüªÇX%³$öÈ]ØŽ€=0À#ªT‚Æ´ÛZ1ð»2@1˜%„ÒŒåO!8pf6S²½¹©^8<¶ñpàÐŒztHÑuììõê..¾T \ø÷¥’÷þ=Mz:w´ºs–@,ޱK¶ɤ² DNã €œ:¸£è»¸ÃT7yWÛ¼sÒL¥ð¨Ñrø­Gë¯42nH^ºËÝ6šGULÚ¤íHQñ 2Ñ–Ø=[{r¢CRÔZ©Þ•\%¢WH¨i¤a9hü.¢6h¡¢f [5¯€œ”|]TL\dƒ' )0lça®BÅÌ´àFÉö¥”ŒX¯¨‡É‘½ É“G @…$ð›!c¼Ù鎼˜JÒa/\˜:(nè{wˆÚÌÃhȳX]FÎ?|+/V‰hWv …ñ×o.Àfå°DzšƒH±üFœº˜êuU$;Ì ¯æF…*Éò–¢¯ŸÝ¤wš¤aI ©©Ú\ž'šnFº—U’xþdÛ(Ï11‘WJ¤â=Cc€§Ó ¢¡4 †Õà&cDí„`[&0Å`{³CÇ;ÐM¸”M»¹`R”¨:ÙCÎÍ®“B &HÅìú"åa…¬Œ&¯”š*ƤÆ&¸i®=KÞ0– …å¢17OßuY²Ãu¾ª=úyį˜e@â&ÅíËCdE?9Ä/£–‡îØÏ3Íð͆\çÎ`Í1Õ"0WU(à[ËbCDÃïfã2Á©(u¡ùpÒ“QüV{P¸ÏОì†/.>,.üû°ô¼¥] |JÀ˜ÚÛHo”Ÿ¾n¡8Ï´CS)´èm0ö™1b0CÞòy:‡ñöo»¸‹"Kõ—–Ø_Èl$¤ßþíßæzNÞ2å¢L%òZ»vä’É›)JÈ…(*ˆ]i‰'ªÂÌ„×Ù2mÎÈÖN§^âÏ4$v©® ¯ÑI€|s>‚,ÍÛ,Ä÷6ÄKþã1¿,n)Ÿ”ß³ad”P§È¢¸ ‡‘GÙí íõ€ÙqT 4àd¨¥e `˜é¬DvR#*…,ùkP(@ô‚ðWˆ"lXj#qf 2‘|Í-¯*І*ŠêµjU¡Á±ø< _!×"u:°¹^Áxäþ¤p¬4ˆidùáµÂ\¨\H–BtâCpGÍS©ªµ‰ê42 _úþ©D ‰U]?©–0-]}ÉÙ&¯J«-O@IDATºv¾v£mCêÐñ Ъ¡P‡3|imÑSÀ@«Â ïxÆ“u‰râ/-™Ã&¦ªÐÑP‘¢¬WN—âͨ Ú{9¥²4õÈ!¬Š:;êL.m¤ò‡ú£ kÚb)Mä®Y“Ÿbl‹û14ð½„]ØxIlÒíT¼50E}ÑâA²]î–G~á~ÂòdñÓ)•%/¾ùÍo’PÁwép® вÇ{;JÚ åŸAZÊÏg™ÎÕÍ19„v|>¿ê1¹ˆRr›Q㥃Ö%'<°¤  EÄA ’4YjÐí(?àäðÅ6i‚Ê'doUw£€J‰$Û)–fÃ5¶¥©¥.Ë+:ÏùFS­¬àzùCØàªŽ¡æOt¦“D§ÂIL.dêIÓB!&W4Ÿ Y‡òè00#}b¼®Ê0Rü@ ©©†Ä ¦þ‚=˜IìG¤X–uA)‰ìªËÄ䯪ëQ_dÔ0Tò°·ŠT"w”ŽóÝ)¤E(§úsŠf‡>âênZÔÀcê1,”é0§<Õ6DÖ<GvÄ¯ì ˜RΓèÉ ´4îë2 U¼ºN\tH¾bžM£¥]î€Þ{9¸W«L!Þ$2•L(<`r îyB£cJ‚‚ºc’šªµÌÆËÔ£CѸº9OÌjXˆlÙ©˜] ì?…üœÞÜÔ§œ¬¸å»cTÉð^N1hêlZr‚¬ºgÃË4)gyäÜÂ9Æ”IwÖëVF×tuJËù$«È9 ö «¿&ÚœàÆ\ \ |@ \ø÷‰y‹ºøˆÔ» Ý-íâB,—‚ìv|¼¡µ[>U·€Î¹ìÓQ §Ës.:„Üé“ú[}q?Ä-ª<33ÒaskÃpj g’2iŸ¾c²w%ª:&¼Úþ¹é…ÏáÜž*[Á9A¡”P)A¹GîÂ, $v”¾KxÞ8%;`0òeìÍr“Ð@àGv?`¦ìÍ ª]‘¼¶jwÉJš7.g" .ñ?Œéø÷Žó?jJûÑqA‰°"y×IÔ)èC«Ü!„[ü–~_’óHbËü2Õ¢Gˆ†¡¢Ó›ëÊY ¼‘º0Ö¿©Îc¾¯ ʪ ¾ 3´F·X§^â©Ë“îFò14©ÑãÓ –ŽÂ™q útÒ)VáªPQЂª…Iö.ïáÕ.¯Cf43Å>:±bÕ͹ïb¢G@"/r}Ú¦ £6ô6Z¾e QLI³TL9R1]¿,79½ «“î€ÊtûüÇxõh‡©k’éõ²1=ÒÜ”òðz‰ãÊÈ1Mº%vodÂ&>6‹c‰]`¯Wš°),¥é<¿ê1éòK‡“éŽe§—° Ólk@ÔæÒXÀ›),-÷ò°&=Å~8Á¦`‘q~g¶æ$E9ÑØ¿DG9ó²°˜'í§&U5‰æpשŠÏY•ͨÃ×Û9[Ž3Èï)Y¦ô£œÝrѳÜð¥À¥ÀwL ÿ¾cÒÝŒ—k DoÜßT#·hE«šlº4o/ïŠ8w×€7V:±çqá§ê:ÌGÔvm_‡(+³åXQ îQ—aÈm>‚þRt‹…7n5Ï%(UµM­WlØ$~ü²ðK©Q±¼ *&sÃQT7DI £Óñ@A€ÌÇߺ;3` üPmK —‹ÞøˆRëQPãÚG¥„Hhœþ¹«ÂÒ*÷ÍÀ?ˆñàÒêOz¦hCƒûcxJ©#ò…q5x¨£¢1è@N!®Õ9SPUOÊÄÞ”ápC¾45ÆÆìÄ08æåO•$ʦéYއѡÀ^å!C%àF=Žó¼§©NóÁñN“„uÊ[ÔÍþA†‰ƒhH§›Èˆ˜H*;òö¼C •ÆE»Ä›ž ’iùh“øyÒ/oëGcÆVºÒ‒½(ÓY2x£GÎaŒ'vš_ 11ӅÇxÌo ˜¦ƒIQmë“ÈT2¡L+“Ë3ÑryyCr¨âñ'é¯J™×Ù—¹|xöz¢‘Vh£ôF%GÃW7ñËüv8¯áÚתr†j@²ì˜älmQí¢77²B¾·f¨ÑÑ’ÃîVm(#—ó7E ä"xÜK¸áKKK ÿ>,=oißë`†T˜Àù¼3ßcxy”KŒõÑK“bk€hIÕ»±!²äMg}•]2]cŽU1 Ð38o&«‘êÈÊn9ÍÝa!gùp)ñŽ; §Û2dVH~6ª#Ož“Ÿ¥¥4Lãw‘CŽ¡ÍÃãK›1¹òqBöP‚G”ï¾æ‡ã€°¡Ü]¯ò5ï €~Æ«7ŽÝ{oÀº ì¾d(¬ØÈ1²¢ŠÉq“¿Üœ°²sCŒyaÇ*qßræRvz !ìvXKF$ӻΗñÔå.Sã4o²ó$9›5RÇEãÑ›Jh`àŠÖÀˆ¦·§?„±Üüöû¾ïû:«`ȸÇè)u>ÄÀ"ug)ŽëŽNIÕ¢Î겎Kß‹l4¹úÉbªÅ[äÙ¬b"¸S7)S =òHK“ƒ"{¡ÙÊ^'êbî[1Ë€^H† —o{dtkVìíø¦ÅðZ8 R¥ù™&9ªp ÖôY¶3Ö¤:=m@Ie–woUÓi[F¬Eo–i,8Ò[|¢…ú@W€® ‡ƒ½îÇÝúöŸŒŒçì€óLD®Ã‚Ðö†ÂQ&¿At ÉZmy·Êô*çYømÉÉs^۾ْΨ•1ëÛãwšØ¢+îÊ~——–þ}XzÞÒ¾§)À,pP»—c݃¹c¶[÷šÊ°çL»(è~ððq#O9M-!ù=¦w Ÿ|)rÍ­Ê<ÃüjŽ 0`€'=%€AˆP[„êðÏaYbcjmsïÎî“tx•GØX¯é"ø¥tã®§7¡Èú”“& k€·ær5µØ‘*°w°ªC¤ˆqw; 1¥Ÿmô±sDæÖUf»°¡…äYnQs âíù#´‚Ò|¥kQ@ͧ?yk°.ÆÆñT9ƒ=D3tßtÐ SC½ýà Ó°‡£ÐÞ™Ñö”¦°q1»nOPá˜×ž¯^Ãÿ¾»àèÄèkª¾Ï§'âóÃ~±dPpT(‡S3:mW<^cNb\šCóö>‰,CqLî} Dçh×sÇuW²b» ¨-u—òÆ_ \ | \ø÷E¨wó^ |†ƒŠ€r€pãâ 9’:e0wŒ•ÚNÞýL¹_ÉÞ ª ¯–6Ñ€ X&葼\ÎWt*½oò8÷Æ]ž\ÔY Ÿ¯´‘ 2‡Óñnç\üpG‹$Ð`ÐA @Ñf¼lsì*ϲ¯¦æ ™¦:{Ä©ýòRÓ¡€áë¥Qò(¹=D*bf)%sÜÂËÌ­!)£ŠÄ¿ü>x¯‚þJ½gªP†RÃêTšË¥¹åŒô”´³oõÆ‘LßÛPá`Q’±/h« ©é@`Â\ÔÎü¿ XŽ»ˆÊ+TDLÏ  )(ƒ’WQ¡\æÇ3‘¿]ñ‹X´»Àˆ^×µØÞTaתÓB]ÖqÝG„|f“Biç›1µ+ç,>/íq }vÕ Pá"^RÜ[‚èÍ ßÐÔþXþèu) Ã„òbH4ÁœX£b×òNNDÀÞš§ñ^ö®áTHV¶Gm¶”Ñe¡aoö.p‚Ô»‰Ï%ÞZ‡]9£ýþ©¡œUẹGÕ;ÈʉÚBÚ..BΆµªómF)g‡®• –G)ѧbs!Žjw‡C^‹¤Â-òCüüh«’’y³Åy~;ÇäÜÐðÍJû9±˜XL@þêÛm޶Hµ[NÃHš‘ŸI´,êF^ \ |A \ø÷ x³_ |›uÎúÉÎ5þ'Q³Ð·pŽBòšzÐ}€êQݔ풮 ¹£~Nˆm´ƒ¤5§ë’¹|~;ǰÕÔrÞAçWs aˆôîòDy–¢¨áÖAb×"¾v˜‹LhÂmƒF‡Ð ±h´ˆGCKâ׎.qˆc‚Ø: úc.Õ¼ùêCtvhq|GâtkÒ 2êJ§0‘Gó%Ú¢êa|1$º)d&Hu$_§œï|*–ëy—0 ¦Ã‡æ´D±”3<üáÏ>-Ü™h}“½jÀÏ®u%ä¼îfS-º cÿÞïý˜$™¿¥0@•@pd„‚„DË¿2"錚$¦öÌå«eF‘…Ù?j®‰b?äMó4Uƒ‡Æè”®é n“7— YVwz€KUûqËÔ[èö—oÄӞŀü9„] w¯¢‡-AÊG¨9„q–‹1°nbÅ¿Ó< ƺ¨‰´b å ±¸6M†øù HC>¿šcLd‰cv;¿eö©4Æ"#™ƒ‰™Èâós¬ ‘–‘¸xÅH±ÿœ‹í1a­á,£§I8NŒ-t=ÎÉ*†ºXÖ–畦ö{Sµ"wðÉù˜ òâgü¯%¶§Š\p—….oHŸMÐ_âpD‚×_ i[–y#/.¾.üû"Ô»y/>¥@>±}‹LéÆ ™Œ>”ƹâRô¬MN€H-™Ä²ÈÈsƒ;!Îw®sËW+Ž“?­ïŠDE=¦úè%#1µ¿¼eáT8’ÄYMáq–àæÉc34U3 âeJr'é_÷d3½ 8È È%ÛP¬ïÁF#, ¯ÈœÁø¤ÞX¾ùR–³éC Ãc`öÚ‘“ a0¯h;²Ï]†¸À3=e¦ˆ>nÊui)v’ïóà“¶Ñ»ö\CÃ' Ò«Xî¾nO1¥òß޲ò ¤àü"Ía‰|ºå˦š`Sd%¿üi-ÝŽß ]% ¤Ã…¢L¾‡Á¶ °† ÈAãcG§;:U¹z@bi\£c,èh?ÀŒHɽ=£h× ]ëe†æ2ö‡°Áj]ëzŒ× Æ#vÂTƒa³aÜ5CbK̉E1*víŠ(„y‡ª2—SCQ¦Éòm„Ù¤Ô—¹ ›J›ÔÔàp¯Ó«|á ¼W˱KË9§üTýÝßýÝ`>ûOÚì]½=Þ‰ZÞ|5·¯_:²¢)µ_èŪ=Ôša©ïm[†)r¥D„¡×ËÄ"±‡ôÖ[›”­Ê†eÛ²yõ½OS¥9ülŽóÞWvFpWû¿¸øŽ)páßwLº›ñRàS ðzÕD69†@Ÿ¾û$ädq™-6¢¶7n9rÚóî¶Ii~ìÇ~ŒæäÜš°Eœõ?ÙÂ%wQì“V|濳üˆ>æþ™›‡¸e¸y?FG^g§7¾˜žôGTÇ)ý¦7‹bœÄ¬¶Ø¹±%#pš)$É‘ìñ%RÑ“¸Ï“$„Éáãj—a(¸ú÷ 5$Õ†¯Šò+æp×Ebrç!W´@’ÝZгñiãOú7C<BB¡Ž_]ÛØ”yƹDú?4ÊU GiM‚¦’n5cï¤mð Ò¤=‡QÎe9j%“ÅU(Ãpÿ0Æ:R?â&é<°¡" }b¤šCŠá•G ʵ4„Á^É—C2% É!à.JfÒüüV#5Uƒ5[ãuA±º³£*ùXlOÓbA*†¡Ì£>Ò_ÑS-Ëɸ€Cç™h§SËB9ŒÇ6˜g‰‘4 Ëq-Ç„Xñlõ—3³Ó µ› <_ «–ç lp›·§0ûøÁ†ùì3ãFv¤”“QØ:ž¨Z‹¾gÏÒ°ºŒ:ˆb‡Bæ,׿–à›[=šMU½MïÂŽ]Y\˜Ìo{LOIäe€JVS>=X.U×Ð/¢°Qz9ͧ éVˆL%{t'šÿÕ_ýUoIÂùÒCއ†ÚÝms^@‡ú"ã»{zº†löˆ‰bÀ¤A+8Ô5 1±Þ°2Dâ+W¡8fpˆàƒrZ[o Ÿ&iØ X#¯ŽèNï9Nmn°¸º™ e Ð0YdG¿MгÆ])ítæUuœ4ökTè 0–ÀØc© ÅN˜ ki36Kw°¨J1a•¿ P¸I Lî$^ù’½48´$Jìì†þ •예G½éÕüÃn¾Iæn¤,óåU'Gr¹„|ngÞF™o:¼I\öŸ‡Ìy-’¸Ô‚ùòNu>Ya©ßÁ§¡ŠØ*[ð‡øáÑ–‘IJ¡ âÚPl+6—y‹àÞèØ¶l^p)æÌ½÷AÛï,CO-ÈVé¡öá1G$Êwl7¼º——_þ}AÞì—ߊsËì‚Ôçï䊗Sÿ—„‹‡Iî¹»°åÓ¿Q>SÝùZ ;Ѷvenå8¤q ÏÃéÍI¿=˜Dn½iL\>Йœ¡Q•³Çó Ä-¡ïîôøÒx¼l)(mˆbzJ9@Ð$qª9]³÷c‹KŠ˜co·Ú‚[øT&#½Þ°]8ºÊG0@™o|Éäú%T¹¬sy’ âê>ôŠ€‚ ^!©×M­eiß›Ér à„‰6ŒÃž?óHwAÛã¢æŒÁ0-¤äéŠÙ4ƒþ¤>ØûUaÀ‰ŠdL¦ŸM+™¹6æ|nÓÑÿ°xWz‚]¨À³ð­ÙÑP鈷Œ–A1 ënIöø¥>£&™6ÜÝÐÏ-I7ÝÆ6˜ -üh^Ž3oôI½÷ã5vòj_êÓŒ8&qRFÝľÛÄIfZ;ü…`ágpEX~šé9J¨O,„¶ó_ö´Š²BίæËžÁ„o ¿e;éã\Ômö‹}Mœx²˜å,-}úhÁ·ì[ü岨l :nƒ86tÖeÛMÇÖc² ÙŒlI6¦XãÛª–UϑԆʴ>̯†˜RÑ¿ìæý>^ \ ì(páߎ27þRà-âc3{$¡óÍ]š¨¡½«§H2Î Ï.FØ@*ЧóÒçº1ÈùtŒšÒ°Ý_±³š²&§ny9Þ&+¨zmsʼn졃ýUÎéߨtå¢)¹gß…£Rx‰‚ .Ã]flÄ $"þj Ô´Ã ôEŒßò9rÞpÿ;A9]ˆÓE"õ®G‰¡ìã‡é%~‰'¥T©N¶þú¯ÿÆ6Äz—o¬ü@ÅËp;³Ÿ­­â$`>÷Â])%ÓÕeÂ\iƒpàäêÅÞšäL”ª[p‘&‰ûUè¾Ô˜ ¥õG¬"o\Aê)xÀˆ±'Ž.Ž€KwÔÒ_ô‘”Ì]Gç¼@·§œÌ7~µ >IüÎ?ÒE‹ëÀ¥(0€àLixkø ¢¡4 †uÖ4j!6@y,1°Y\ä£{$‘û¥\¢µ¡là¥4îCüð.5¾˜ÿ;m¡ªrïHœÚ0OÕfÍB0ç|X –øž9§%–eítd0F MÍ£ÅGú7´’>j:‹Æ²¨´lÙ쑇p>ƒ1¬„mK·åÎ2n1·¤bË»E¾Oí_þl6ŽØR¢¶›ËÒlµÍö¤4sù|ôYéÝ]taºýýßÿ}E..÷¦Þe‚y)p)ðQà¿ïŒn7×¥À·)¯Ù¢Üra†ô’|±~!/¾LŸm˜§“. Ù’ÉUliÀHKN£üÇœo÷]J)ã"â#etó0'å½üeÛrsƒFå äuˆN<…ŠËLE£…dÖe½Cd;*ÙGÁ.hPJJ Eé[Gð*u@«Ý¢O¨‚z‡¢ƒS>ò¨k-ªŽÕS¬+»¿Ê¡‘yL²$èYV©Z«zä2ìÈ\Jž9†·„9-tyÏ)~n<ö¾$LÅñ#?ò#Æ.ûØ ´ ¥ ÁÆ»ÓzFn¸B0Œ4Ô‹Œ˜ƒ¼‡ßh*p¯ït^¥Áð«ŒÚ ‡Ç»™r#yÝÄ£è0;˜ÿUF’.°­ÑiTd0dųgƒ4TTo‡€!²Ÿö¯Mó ˆ£pQuÝê1£öhð d§f2LËíÚi  ·A7ô³4niÕùšhZ¨/R¾¹uFÿ&%•]u->B2³¬ohcu)—?:.ºÆ |¾Q©ËtV¦©=4`~|©'LÆX+Xæræ‹@¸bFéCbKe¦†•gx5/.üû¼»é/>¥€Sÿ¶œUŸ¯m×l½$ÔÇ»p©€T—ÂH«A6uÛ$·bvEJ2¨ nÑ8§gBF°MÝö¡ÏÔ³$áªou¤F˜–u™KD„ij² 9&¾æìÜ»TòöÆÿC8U8ÔïÙ—áX|=‰@&ú7‚ËÀlQj4âþ’Ø!v^ƒN)^–Mé“ʉ­&×e7¸(1æ{}šCŒr€+ûÙÓT‘ÊìY€º2÷…+Èárã´#ª3:™øæy©B9¡†cˆÀãÃeËè¬vø Ù݃Ûs(Ó»/lø ¢¡4 †51»áH|0Øã' $Æx Ą罤äN†ž ÿöKͳ4~†ŒÞÉ š¯Y°b ½\~¢^b—k—Àt–ØÔ~L\&â)%°ø(Ö¼.÷9WL}Zð8)-žÖ ©åÔ¢jiµÀZf-¶–Üâmm˜¸ÝÒm·Œ[Ì-évË»EÞRoÁ—ÅâhC^â:7yL™V6%›¡fô›,öÊvžíeRTÍÙ%K¼©î¦¹¸(páß@ûx)ð9(OKeÞ©;ª8r³”öãŠ9Ü’žR蜬ÞFàüx0*¥@Ü÷-3£#‚p€¹LdxBŠúKz˜En)§%±µ;·¤ZîJ’ 7sÄKŠ{hçd„ò¨¶vfȽPw]À¡¶¹¹{3c#q2/Ü j„$2±‘%Õ9}wµ‰f€­ ®=âO-‰Â™ŠlhÕüHÆR¦Áš_õ’¢d¬¼zdŒ6ÉŽwŠ…€XÉúÑð\¤ ð"œQ‚L Ôî4‘ÉsäÔÙÝ(2ò+ Âi½¢!L5AË x÷x˜‡ö•%a¼nõ±óÔ*HÌ…¥%öé•’I¬Àd `U4k,ëU, ;¯Kùb„ö—CÔNsaDS5F˪U‹¼ˆŒÔáÒª(Cc€ “Á¢¾ü'VÉ ñPQÄÿñ‚óæ>mŒÀ1a/Á‘PgKTsnˆFqµÅÞ•i"x€úxH‘êÜ¿Õú´^û.ŒÓ$F´Gå?À)å{Ç›1ø7åwU÷x.v>N[磷°®Ä矣·\´æcའû½±/}c‰Šró¸¯U±_°mUÌ9€]uÓQÚ#Šv½0aí|.󾽸xI ÿ^ê&»)@d¯Mz¸€1&ýÖ·œqJì¼s'Yä'’è£ß‚Êø“?ù“ªx/㎆|²UQC ‡èì£ú>-/$Vg$§ûü¡—Ò£†„ôO¯BW™ o4ùÄ–^ŸÅ—˜ qŒ1´p÷¯ç;Tòf(I¥»rz<Áȸèc§FOPa">EdgwGÄ„1¼·´Z`-³[5,Ú¹§BZR¯äÍx‹ŠÜh5Lv †x[•¹`Ðõzxµ|€3é`ïe‚éì#Tz£vîoøRàR`I ÿ–d¹‘—ÏàÕ0’³ØÿüÏÿœ¤FÝ—ú¯ÿú/1H!Y‰Ò¡$EXäEd¤.ïë…R¾Í «”gíº¯8JÓ-*±+L¨yQh›Ñ€+ê ÍË#ÊÐÑxçä;15ã+˜&0ÝßýU—Q0磟Ê Æ—&ñ93Á+û.ÀÓ‰î”ÿ¡]²Ä[‚$6¹,JåvËbeɲpY¾–‡#zˆf1œÝnY6%Cðsêmç`ÅŠ9,ûÊ·œ“y‹KcŸé#:‰“ —Ûáezöðƒy2#¹èo3I±Ê`?rÈu_] \ ì(páߎ27þRàD\Èvþx?>Ÿ@ â·» ¸F “¿U%0¸Ò˜7N\]“’XùRIEÒÕ‚NUw@ÂÄD›´^ì’IçãÐdß8¹Q¨zøË Ëì|œ™\‡XªŽ–ìÍ—'¢ryãœ]±qhñÒÏ{Í‘Q—±Š›9\2°c$÷SÈ ä’؃¼NãJß BÐÐ.?WH/-;´° ñ´PÐ!M^Å Ùå½]Jý…võ}-µð£µ˜n —þiq‚ÏÙE"¯î‹ÄçåÀ·»ÛyÀ*ŠKÜ«Ê´ŽØ+æ\cGáªW@ ìQl"~GŒé¬dlµp¶{d¬èêWTîìé¾¶õò¹”†€Èˆ˜;:$¼áXêpC¼+Š0o9 Ÿs0~¡Øˆ¦‘P¥\ñ|»3­üÇüGUCk»ª+>0ãÐßJ)@ûRß•¯J˜à½„e8Ì0µZR,,óGw,AóðÍ18VJ$ÅÌÞzdKié;Z61¤%ô¦·_²»Ì¦Ô=Y…-ûI¯¹ 8òÓì³åpÏkÃ25L–ÃâßÓ ç…½Ã??æó•ÚC}:¿½1——Ÿ‹þ}.rÝÄ—QÀÁv®ÊØŠ]mÏqãþˆ‹¸îË)™g¶Š98l°éjÒ«›89xo?GïoŰÓx¶:ç6{ËŒ C·3:´"äl(ËÁ¶oIQ}ÀZ„È‚Euù)Í­*¦qùô°€xW•FLÅvBª–DúÆíŠÄ±‰zyÅEÕÚðæ^™[ÊYLÿŸÿùª]r6?þÌÀˆ’±˜J—‡¿¡Â>“Õ)²òYíL;KãÙ!ä0ˆH¤FÛ??憘ᣎ&òrÛÈ^^}´ídWcïdB•åþž  ù2„»R†ÉL*¡¦g/ÄÍUºV”¡8Í'ú[å`æ|ˆæBšøÝ V,[2JOÝ1…’|®ÀQ;]YUŠ‚,ˆƒD•|"ÝLÏ!&<À°pˆ (–Ð$CŒÿá+Š>ª0¼ay±v-UŽZ(RhtÍÒ¤vµ,F b%8ë“ÓŒXT¾aþ¸Ãy `ø†Ñ 0tvùŽ5RË·"‘Åâ j£¦XhÍÔ¶€X=,&è òð³(Yš,P–)ÃgɲpY¾¢å®Jüð£IEî1tDó]‚!>g ª‡øÝc,Wß8ÒD™¨1±Í®´!_!—-lˆß=æ:72¾±[‰)¯ò߯îj¼ñ——(páßeƒKÏMåÚ„H™sD:ÂÄËjÜîÈêã­ž*0¢6ѶbvøÐÑ,õ‚ÏìÒôxR#aZO =þ&SJÿFvg¤„O¥õWA2¿õ[¿å ì„°ô-rÒ3ýìDÁr–ÕP•Ð €²#(‚>N¬ã™}þØ]¯½Â€fk|Åè&±JiòŠ/)߸^Ì·°huÐAò4J0ÒjÔ\Ê~úŽD #™–“ ¥SŒj"Ë£+ Y` @hw©¯ d0¦@7ñ:ê&¢ÑÈ -\>Ò×qÞƒ]lìÊ$‹î¢Òw„Æú. À·TO®Ø<µ •ëU2)ÊãàüFQÄúeE"Ãlú’9xt••q@ns ¶Ž…\ãÉôb÷—OHÉ2”Pé% cÑú0ˆ‚ŒsKÄhy.Z£L 3 é°JÑØ¿aT* å¿Ñ*› R¾\dèº%~y:–k&¸iްæ‹>šþ•ZL ¥í~ˆ#¯¹c‘±ÔXp,;HdêYˆäz¿|Å U±è@Õþ˜´ÇƒÅÊâÒ–ÄGÊd±øc ÿæ¸0>œ/·Wcl[JFÕùâk¥1Эý‹Æ•Ý>œÌö˜«$óËdùìÚy-C!÷ñRàRàsQà¿ÏE®›øRà[è#.0Õ;S„ð%˱sÊzËœOát+æ°—g»}co“ûN ÏeÖÛØO2·«˜sÀÞO`¢ÖxDÅʉ-ÜK#.òhΡŸÍ #¡Áµºg°)Šá€2|Ã_É€(‡â¾^ŰM!ŠRàà1_×x©*Œï{vegºy ¼iÏX‰n™ËD&  A‰ÈŒB}Bºى¶t\UT:p6Qf ¦„ÇQƒ@rTŸ»vtw{‡@ªIK­]½­ê¦‰VP¤Á5ÜOùŒ÷ >ʇ—V–¨šÙÎ43âaãºY›m$r™-ü£:•ªZ†’Z¥msÕqS~‰JDCºyd{L¾•…<ýŒ»gîP¹GêΕщ‚t®N Hƒáßÿýßw-ö–‘¿_š'0¿9+.T ÷Æ/Ã1¼|ـܻБIZsßä5…MdÓyÉq ®dtæáOfº5÷w[/,Dʱ( rïlhA’=r¶xZBñÌû@<©1ÆtYài þM,IcXÀÎå,c¢ÄfÃ[‡GˆFoì«þl™æÆ¢!DEÃÐ7ß¡öûx)p)Ð)pá_§Æ _ s²ƒáÍè¯òùœGmm²¨(Wgï^f±ÿs#.éi5õ×ÍE-cò• ÛÁ›q¤,Uø çªd¶´Á†ÆªËWû™&ÖLèÐ$¸ú0:ý•ÙáÃÅÊÚT²Ô€©"œoCíÌ)eXºÚv——K ¬­AúT¼áKK¢ã§¡y$“ÔÛ]ÀÅ—Oy¶—Hrþ ºGÙ7uA ÄYû¥†íj¯xgÃÿñÿa?~é· ` Hˆ,Råñ!I,;¤É+zEK/y;2”Utc¼!Áð˜+%±DÍ+âi8qOÌÏ Ý¯›@ûò0D¿ÊèкÂbn¬dçÔ$ÙA!S…8Y~£þ Å¥WÞC *8§ ÀUá«Jïú% ?œÎ,0’’[—p(Rü*Y€ Õˆ°ä0_èÖkÖˤ8–ðÊÉ#‘xÇ(6cJeGë%’¾…~ÀO2ÔYT¤‘ªfyë•2MC)¥ùïÿþï8ã©ZµN jNaS/¯0ýo:èmpC¨÷ŠYþþå_þE|Xe™ G†ñ–ƒÞ“ ‡™­“Cüü˜ b²,gŸHtpC¸gtliTŒ!˜g_b,JT£ˆàº`¥_,,f-ÔæËC¤å–Ãc4‡Ã«ùÑp³ÅoTµŒs‚c Õl?Æ¢=~vÂè»g*»”‰·Øì_6…˜„ÌéM=|ègžš¤t³l‰í5‰ô׫ÝpÎ.&+@½‚ÒMáú9¯©°b¢Zf-D}Ñ®¼=€€`¡©ÄÔÇ0ú«¾¸xIÿö2éMv)ð=N—÷’ÏÐ`°÷“Ûül„Ë€ÃïÏ5¿“ÛMm„v5‚Ë%Ú/;B°³tr.kG<ŽÍ’€HÎãd¢nPœ³@8„ò_üÅ_ŒÜ9±·Ä&N ¨zÒëÅ9½‹F40š­ñç”yËÛI‹¼òFÒ’%åû¢@NôUGÉà™X -SyÉâÄP×¢ mšVv%ª>Ž2š8˜ŸÅSCL(L0$‰–Œ-Ó² i•´*£âç€qwX®…pHÔ_s1ªfÆ&dàdX,óêøAƒç/9žÌÇŽ‘›eb‘:HI%E]@Ó¡KO ¼± †T1ªKMèƒÉi/9ä€K!ºN( é`*5+öÒð<µmýèç6d—ÆœÕ €0À>u¼gŸÃ;0E[š(ÈŠRÚB­Õ<³Fù¤Ø¿ù›¿éCU÷ÇJæU€ß\þ.¬ºcÄA¾š¦ Äñ—†çÀTI™Z. ‘»$ØV#9^àœÒ۔ܣ²×Ä©Üâ„â\ ¶Õ Â`hK%‹ø"‘å@p?§ÍÕ9³€=h«ê;ãsš£@”M¹Gß…=ÒE³¥ïIËÄxŒþMÞÀ¡”àË9¸‘¾zYfEZd m‡&Ôz6gŽæN@x/O—R¦3#åt8‡lLæ£s &ßøÆ7ú¶U-éw˜Ý“wªb_xäC4 m:¹8Ú‹ºáKKG \ø÷H¢›àRà# ؉¤ÙæÉä•3Âa¦!©íß²Pb^}.qÊÅvK¤#@|IÒö×.ö—»?Ön,j\©Ü\5˜xGI,x¨h>"ÁÓÏù: eZÜ…’³ü$Ü 9U0Ê‘žj‚”O49`˜ª‘Ü E2´5¿ °ºÔéR4i™ GºÀO’ùâÚ$¯P‰v¢dÙ€çÏàÐPF¨%i0倞&Æ­³ÝHAH§Ù½y˰~ý€ x¶LHÈ„×a¸IäBh”龓®‘PËC±ý•¶ÁQBÛ îcPXÁ„T'¥,®·õB– '¬4†îŽN2'q/n™ÛfR%b¤yÒ©Q¼ªÕ šH=è£ô7:·ÞÀÞL·&йÑä•6À™ØUFÚ BíPáX²Çö2¶Â®L[éeÁÑkº,v¼s–ÃX²¨»ZÂÞZ (ÊœáÕüˆŸYQù;µQ0^™†Øš€‡Ý-LLnÎÅ&FJ£Y¯°nŒ®“Ò,gÊÐíJ«x§Q e5ÛñAE.Fĺa°pN¯n™X¤”ÖaÜb~™È»dýb'…ÛGb8Z¯æTF]im÷%˜ùíc‚›’¸×W^ÒÎ äg‚Dù´.Ð,ÙT9¤Ì\n” xó3ˆÀÑ –xÈg¶XÃíô9ÝÐxŒô©4eŠE(6å- !IKL:Ê4:cš _Üü£^ƒ¬@w~Il2<ÄÐ'ÆÌÊäòh\@Y°7v]*ú_JFgkŽEŠ›ŒT; ¹·:mi y׌!™Ñô÷ñ–ÓMƒ5ó³r¹f÷rŒý[b ·¹Vl\m 5uUì%TÈ|TÕ¦R“H¥8Çd?Õƒå´O¸ÑÙ|0³wd_K[ XÌ †*\Všì--€ðÏB þá%·mA#Œç7À¹DÂí© {?w`øà7“ËÔæ©²“¿N‚Lö¡ñ¬ YÛÚòi‡áíühôu\=ß(è0u.m¼DÚÚyKêUSK¥Ã2íaõÊJ¥¿½áKKg XšïïRàRàL2MìÍ(ûÓ9±· œ” y÷1qäƒ;!éIWT”ŠÁ\’Höggá±´qÔý)e :“ÿHŠÄ\Ö˜D%¤³ŽØíß,mœË’-vÍsMEzpq— Ç“±$&ÙôÈC˜Dz›ý!M• 3L›zä2ìJ&w.ßΑñ¤Oý2¿ZÆDø¦Z¾uO™Æ+,°­„c2Ÿ!9h!Äœ†Œº *0ú¾FèÔW¸$FFïÕÅËÑ­GÎa`Iu¤ðùÕCÅ0Z[“bh§·üC–]ëðöÍ#C20̈Saax–~¬æ*#à¡I&Z³VÁáþVâ (Á–¶y\J™"ã1Õ$rÖ3Ïâ~étd›j¾¤Ìªn(ЪҖPÔ,??hÜiZ†¼õ˜ök<0P`~4”2ÖùU¡¨”ŒÑ`ÄB ;a*¬…Á°f{ƒu!˜3çÞãŸþéŸbr¬Žá{-¦LÖ“¥bÎxXz¶»çdõÖ—Þd¯˜]ö’ãÐ]‚!‹JoâwÑ¡¡N™­#;g[†NÑø¹øíæ¡ «ÎÝ–l¬ºÝ/˜ ÿÀ®¶£æ’'ÀVZëaXÀ1v”çlv ®xw2åüŠ<rÅÑÖvNVo{Rwë3ʊܰ(Ó‰­6ð.Ù¿¸XRàjÿv é¿ø” _ìí¯<|~úbr¤j÷*;Hr=«3oÚ?'¯2{ü2l·söI\ kXâ-“‰$7S.ÑrwÁ­ŸÏ¾ñ ð±BqT*Úw?R}¬è¥ #V$ €¼«©QV H.)!¯vø–ÖwuO©S)‘%÷ûÙ V3ÎÄ” ˆúœ0“`©¸[f\jÿ–)I©%°tJ)1™¥ùÑny„çsp¼Î_êX4Ãà.‡,y1¼í ÃT‘l«³ÄΈë—L–¡®åãçÒþ)Á7¸Ã¸,°"£§ràU1ç@% zfZá™(åúßÒÚ Y‹$æ<—\oQÛaJôrýoݵ£ÕŠÃ0Õ²àóžÔ’UÂ.`•0•'S§S,ï’%ž…0Õ¥)I žçmôrVc\?¾ÙåE3‚?0['ãÙþÖdtóÊ5Gœ¤`à]±7þRàR`¦À½û7ÓäÆ\ |†|jSD$Š*ÀeŒÏ¼ž¨†ÜÏ!L;Ò¶sOï$TÛ9ª¼Åë)ŠJÌ"^ð=àH~zÿ™¢ñ×N ’>óî“rƒ· 8´»k3?¸øFvO©ör@q@‰ d<¹œ79oÅtyñ“}úŸhE>v~F&•øEn |“&w¸«;¶'¡Â$q»_ñ»œÆ °´KVñÈ#iLª+~ |„9Òáðjù›RÈœ´·Ä‡30ëå°þ’±KíÂTy91=UO, hÑ!€¦˜¡¿"Ây—uAA„~,„¯HieiYÙ5€ÒÌ€á/ž9¥Wì<‰} #µ"K¥P)3`ΊFZ óK'p§Ašª1NC”Æ¡M‹^›D& Ëlìñð Õ7a³1…Õ†¹åÕ(Fã‹åx¹ ñ$ÀWlØè»Tm ö€·Êžpûÿ·w÷ʶäXµ€;î+òx¼Ñ…‹G{DÐíÀ«àààñ×¹÷«”ŽŽ2S™ëœªêZ{4öV*§”ÒÐOΡ9¥…ûi}µfâ.ŽÐ.2›[ôØž^¾¸´óEL‘P/´_­—NÞ¸Â.IÆ­2/Ý#·Z 3cÅòWH‚¶!xWbˆ10½tO;Grsg]Tm4"7nà âÁ—a#æ‘¥ &Vó¶Q¶?ä!¦CætέnF!WÓÓ&9,cí”ÎHè!ºz %3sé’üô–@!úÌ@IDAT¹Ó)V‹—õ©°Håç¥vËl’Wb‰7(ŒVCÛ²ÔíÔš$èœí‚–³°²Ï?OY>­0ÀÞ®´"~üTCüL 1w?yEeŠ@(ýk(;|æ}×óQ§Ãåwù6 |M}°i]Ô#JÒFr<¢&"!l tÍEá2KÀú+¢ÈÔ–_u[ž.·Ô5¬Õîæ—åÑé-uÖ*#šãn1êl,ŠÄTÖ_“’MADJãï7EzÏ‘.¢ß´7«ÔHéž(¦:özñA}r¼yªÓ %’*yŠÆIÛ@œèúÚ÷vW¡„TIb’d/ٜզúCÏ?5}ytzK½sÀÃæ´RžQ4õ(ý<»h¨TÉÁ5âi¶‰TæØvèyú$î·PÊ1MÊ>g‚ß&ÿá"È´¥Y@X*;ý,Øk ÈRA/×d X† † ÿ`ŒûÏÿt¼‡~µdru‹‹ª ›¿#<*4ÏIJ¸ æ'´ˆª9Ù…íKÉ=µX´Mææú:(pÅ(—T À”7ø§fY=Ö@P#ûš¸U{ Ë¡‹%jlqLr&}ÅÀWÁËòé€Pñ(œ´ÚUÁF¼Î£ÏSÊ•P­±Í%ÿ!™€lÃèHšå4Š…¤ÄPÓ/ò¹Í)šqlç;‘H  ±ý¦;òL¦J®Ì#ùU]1UÂV—S‘+±!h>…1wÈïLçXØ JON¼™ð!/•§ò„¿i‹Aäæ€Â°'£ë:Ò(ÃUÀè0û±†éÆW2s¼qgNÓ?1´[̓Р°IO‘G9Ûf>µX£0Š4"7³¥!©TÏé‘•1b ˜+Jtëlì7`²•Ànÿ”n ÜGEà³ `šèUŠÀÙ7e: ÛYû¼ñX aA#f ïÆŽät½äxJãa1ðšëˆ¼ Py}G)”tŽ+™%>' >¯‚ä–`•‡²>geÑñ-çùüFàÇvJCbo¡± <ÑÞdž‹Â‡aråB­í¡Ö[êfçᑈE#Ã4ªxÞ²ðÌ…9 [­—-z¦N–HŠ,ye^â¯n)ÖäøJ`ÄŠ$0bö˜£‘º½Xž†Hd»Qb+0Ùšì\²?‡½‹VÇ/QWQŒ—.úâqšuAf(¾y*s€ÏÖdÙÌ&?ü>mÉM Ù™X õ(‡ ¨+ÂV 6)«§ž£¯nYŒ,>Ã,úªu& „$g]è'|3ÀCaIÁï´ØÊÆ9œY¼éÁA@—ë Š«b£¯ƒƒ1K¢í?3z0׬WkhÍ­Ñ5½0" á´#ò* ËÉ_÷»Xâ1Iò¼…áCɤ 8ü1«Ä˜'±8 v–?¸¤o›ø¸¢vºŠ[M®.Í0™|8 XPC2á(?®xU˜9Þr@l¡Ê6ÇŸ†óc:íéÓÓÈ82Øÿ|úôiñ+ :>ǧKŒ•øÄG`ytuëÓ&‰ÏÜÃiV> Ú’<ìf㧃¬_\•¡ñE ,ün¹ïm(ªÒPžœø‚bYƒô)µÉ~d²XÙõ ªŒÕå½äxÊgRŠõˆÙ¢špBÛÈÌ|¡¹“ÉÿßÿýßçøM8 @­ÙˆåÏX™#Õ³dˆ"“Hˆ"+%åÛÁ-5öò Å®eÇK uC …(²†(:ˆ‚¾›cúŸŸLßðp>ä\øM8þNV²72y”£ù(n%#÷E~ŒOäéúðÁ^öœr„G`÷1”l=Áz¼miãç§q€(÷Ž `HaоO3V‹È{Zx­Î ‰ ø«MUA]‚Öëgèèˆ\4þ«ã„È'0Ü5é©§}˜±Ô¦AùàÞëR€L…TÔ¼Ká³@s|µ—"<ô€ €!š$Œõá©Ú1ù«1¶JÞr’TŸåm©Ñé­.§ OÖV’<=ê ·4LälÈœ¾wŽÔŸñIMLýFÀÐ0dÌ0GÌÄ#'/eí@7@ÑBsNöb\ö¹¡ŽH³ð\€%lúÊÜ–GW·Ø¸šœ¯F¼™åÓìˆÜ83Ëœ!WÁö’ãi~òôIy$Ñåï¼ß‘|04ÂÕ}ìö’ã)$ ^䜑|D^ýÆ>sFé+±Æ"0#Pú7£Ñpø‚€Uÿ±ÜNuæÉöåÙEÈÎßE^4Ï×hje”`«Ög÷àÛã-O¾s´–Ø^°‹üÖ蜊Á ¥l볋û¨2ÏÊÃÇžãâEfktV­ïæê[ïQt&aˆ¢(mŒ0ˆõÒŠ¾Â„(2’„(Ò#©A!Š8pˆ¢ÍKô°oópMZ±ùÛP·Öêpo½€$#ÏáÉy„3ýÈó>|ˆzÊÉmyO…déÝļˆ{äUŠÆgN¯^˜äïD™œ?{”]…| —»µBÁLáwò¢üZC¬ˆ›WSñ©­òG3p­Aù–$è™.ä$ ëàEÏ( |¿ çXFØ0?–*1 œó?2Hõ[•R5ä|»¼ëáí‘ j   ³ª2;á1OÂöÍ23šÐŽÅf”DVÇG§10!¯F§O‘i¦'f®œ¡¢œN 1¹qDÌ^hÓ)_Óh¼çóKÝ5âdL1Õj2Þ¢E˜kk.§l?NF„Ñq¬ÑiL P¦£Ó§ÇÈœeõЦgjUrÓì1Ÿ«nÀ’è½WK<4Èë{¢›T(1aëq— ¤ò“ÄÇîùgE“Ę]Šzz;DÑ΄6DÑ¯Š‡(ZãÇè^ÑÔÉ?¼¢ˆÓ)ióœñB×®C¹ó…(¦TºœQÓ¥Ç[ž„$qãã£Ó˜ü²Åsÿ®œ, œäFÁ¥^Û€ÇuUwª6Ò¸°>òLUƒxH‹¦÷RìX¿ÑWynâñ1-2HlÕÎ ld(Ó“ýä†Î©"c¥Ém07#—?°%†¡ÕBfãƒáã8 v’tø[^¡Óa'.sÜ Gë3IñUìÁ-ãk:n—ïo½Ž6Hk#.U~i‹%¹jšI\Wm*9V ÏYÅ7@äÃb6*²¼Úñ8)ÈßèÀ¢m‡foÇ‘ŒDGSrðC³±qeÞWm©iÚ"Û0 î¸øÃøDæÜ %µà©+–áã£cLVåžܲ0d²:fuƒvÆÿóɆS«’›fŸO›ù¼‡Ü)%ŒCµvZà%Òü©HFÙukŠÐÊ’ÜþüÌÈA#æËbcùˆ¼ @fØÛõ«+±Æ"0(ýP4P¾ 0©Bgúòà:D™öyû»¿û»k‘¯žðÌÉf§ç_DJC’Pt¾Êëì†Ö7ÎçkÀÎE ;"Ec‡ÒYÆ_ÅÅyõ¡'*V ¢çÖ3uf ª!÷ž¯Þz}“}&˘]‰G#AŠ(î!Š~N€¢ÈÚ¢Èd©ûh„Q—« †Ô÷h¨XM¶ …(²¶Y)QkáY; T]u‰† p‰¿ºUE¯Kå¹*öU<[_\uüV×2ä*3D‰?ePÄPe¬Û²ENhÈ+Œ)æ5ÞzÎÙ×¥‘½«WoâcO£FcøÇ«A“­Z;äS>´I=|†È :ƒFzª¦K£+hÅvV$¹±­©µ™lÊé‘’¨¦Êª2Kih!Yàx ÀuÌÇ+p¤xÜ¡Öã¤_äÜáOö.=Suxó™c¼ˆÛ$kðŸþô'ñ8ð Ë&<›%Çq$]šÿ-2àDF*deÅäŸâ2—JXkªØí·dõâ+Gñ»Æ‚f¾™ç¹[{H”!¿©ÝxæÇ³˜ À®ŸéÔçûXæÆ"0#Г?M/½ŠÀŠÍ#' xÀuŠ¢Éÿóêò¡¥GR}~‰–!OUáåÿüÏÿLɰ7†^rµ`¿$áZ†“pCo‘tësÈ剺Àp+œä¶ÐØÑa×V~…ü˜ç1p8¥÷¿ù›¿9>]b¬vó¼ÊîšåéñÖZ¾ÍKR1ŸžÆP=i´O~Ÿƒ7/>@·gì!þD•Dæx]8¥'àï ˜dO‹wŒÔUhQè"Eîë<›ãÙ§‰™1?Z@ƒÌ^¬c¶#†~ƆÆt¦ÏŒÈ‡¬FgÐ+ô|#¢£™H=ÜŽ29Ÿæ£Ë騼õ #c3}Yh¶[#=àÄë6•fÌJ­i¢üŸŽˆÓ‚q\PEo9Çy†Rg+)s¢l¥u&¡®…Àp3 ιZ³U;:ý¸” n¼ñI@©d¢$ Ó¬– Æ&œÜ3ôØaÒäPgc!ý•¿pN_¤·gÄó”Ÿ6 ÁZ%7¶µ |šê4RñÔ )Í>X2 5:pN¼X.¦­+ê §žFš Ù—xàh±:Ðé:Íd‰dåCŠô1ž¥Ë£Ó[½Õ"\yã }*–ÈüØkž>³›±ôZH²ñäî$4*õFKKÙ÷;gu [“bää¹Êv|zó×ý×,üVmB¢NeæH êË¢ËéHõüè4œúrÖµŽs*°DŸӊq¤c]²‹Ì|«[šT­9¿ÇŒ+ÞèØ\z‚A-“—n~iÃEàó Pú÷yÚº5}üÀÑ ¢T´+Òˆ.R -–[£õy¦.‰å!¿ õ>Ÿ~ŒK¡SÚK k¤…yzÃìźÈÌ·es쪲÷&¿}À~‡Ša„FI K¤L'àoÂω¢>ƒÞ„ ÚÇå ëâèÍ‘.¢7Þn9a³AñºªQâ©ÅìKhâᔑ8Z"´eŒv̉‰¬‘–EëbšvËh†GX!Ãî~ì¯8f²Ä ˜’#7¼ñþáP¸P5Y)  *Äôm¥ÕëäÆ%“åV©à¦êØ´õbg#M=}@Ÿ‘›ŸßXÒ.·2AneèG)-6=Ô‘iw5ÆåQtÐ%­[ïÒ-åà»âH)ž>G§Y-ÈIR©4‡!#[ QÚ%s|ŒAU>DB¬Èh2¡ Gaº–xoIª%þêÖêɑˉ4OKŒ5µŽs•Oâ­ª ßŒ~q¤Ü {j$ÂÓÌiùæV8¬ëFþœ-¦›T¬Ä&CÔ»Ò7Âydðh†<Çà[aF¥ni21‚¬0î“(†^‡¡iýáå¸Ob¡JoQSÁCë:¿\«xW¶îóÇtMl²ÕsŽÂGžf9Ïøu 0²‘xEç`òd®8¾qÄèFâ7ŸG @é߇oâVð[@´¨q¾štÝñ%;ýbј}égâ|ç×K‰éƒ=¢‹´Ö*g3 0­ QGÆ8bÈûöÓu¨G~WÍíœNWSñCò$ )ˆ¥bªÀ­<݈†Äè/ [yL:Œ3t‘8ÖÞ&±ŽÎšJ=ºÕãe…~ÓݹeRzns&€°Qm©¨Èy2˜ú·ÿÁ+jýŒ •ˆ1Êc f8„úúÃbœŠ! üí“Ñ|¢`{¡0‘¼2ìèÞŠÇÞ’U§°ê„F›^ fÅŠŦgP\éå0d:à"dKX6I²üpöÓó3gÃj}™ å›AFç1™æôºQ)åwRïV¶P˜vyáŠTRã×x”dïêΓS‹`AôZ¯Ã¦èèl­¼ÝæŸÒ& „h*]–9‘žœÑ~ ÀÄaü‚”Åó”½Î ËU”Ö %L0a ó— Kب OaÅ8çO‘ &(tzÞòóö ï2i¨»õ¬cÐ9ýß·¿Ñ\‘ÃN®ª3âct}bð'Ãõ%»òоuvMyòwªÖQÈÓ€v´;ÎÖnN–HþÛÜVZó’ÖäÃzÏ´5N.Yòœo¹rpè0Ns&Êüè*l]Ã'ãár¡L ¤¤–EŒÐÓÝøݬ‹H¸Î¯!Äyüíû;¤ ÇM?d*7$ «Qã7FX€é8®+ÏÍžWè5¾|lNÖl>v…[»"p‹í“jEŒOc6Äo’PÅÆ;êþ¤ú Ž/âøj ø¾rÈñÈ"4†6?ò>ŸÖøËü„ñ–ñ–}€ºÉª3OÃù‚Æÿ P1ûœó4D.gâßÊSɰíÜJF€R‹ûY½¿p›y‡Ìø™µ½|Ji9{±ñ4nuHˈ¹ ð #ÃNµ‘dŸ‘']“ gãCËÄ„Þè.óg g…"'»ÌEÝ„slÕQ ßš…ži/n"1pp¿GY71uŒ ÏäC©c`›û¹TÞ"ÖTJ€Ìöïj |Jÿ>O[·¦ðý˦|jϺÛ4\Ô|™èF3÷“Ê,‡¿s`¾ð%F̬åŸ&†zŠ=Žo3ý’«½Ùwt©úE§õF)ˆ/ßÕ[Žñø€K<}×·öøy“/ºu_Ú?å˜Vê ½HR4çÌiEthZcüîæGWáìraPR’+™%þ¥9¶ôÖ§t.’uk·1ãÌñÇpÌÅÑ•OƒÐw]nãÏ+Ó•n­™¸eÒ·f`ξ §,HþâG*–m¶5œA¼¶Na–¿JÎn .ˆt_…G8|§ud…Þž$vÒ×õgº8jÇ”¤ Ñ€gç@…D“'~¿ŠÁ•1?p$3eøùòQ¶¹:ظ哼ì gC¥”$»­è”¼ÑxÓýD$¿ü' ^eÿû¿ÿ;yͰù d€k\ßuš%‡ÄÉ:©ø,‰ "lÌeØ,Φ †'ưÆXa˜Ÿ7TÁ>pF›m”rÍY ÿÀÉþë¿æHj´¡ FYáÀã³!އm2?b³ªã½Zdhµá§à½nœÐ ”…›¬Ý˜Ð"0R¬®ÑãŸÈg˜dÈ\å¶ÄëoøÞCèx½NÿܬÈÌåÑiõaë&®‡I0LÄÕøâÿéWÑçÜ´Ô˜äÐ|šØ”â$òtòÃyŒKb#îœù“° Üë–Y}Lþ‰7#Y"ñ:Ÿ Sį߆Ñþø.Ûù´šÙ‰oÿñé1Æ’‡o«M˜¾}·žz‘"É\Üd˜d7%9¾«1Eàó p¾úyêßšzgV¸-·Óe—§Ë-e K¤Œ:!‡³-Ç[”ŒBL |~H€Lè7rØ ù´ó\b(>“\×(£‹q¥1Pq ß{_tIè¦KžßpK mGc "ФUýZÂU€fÀ/‡ÊXíV‘‡o¿–ºpÚ'jDcSÙ=éùÐ*P»ÑòËõ#~@×Ù©ðä[k‰jZ«VS« Ù÷ð((=ó¨¦'•¡^`Ge˜†N³bo¤\º<…˜—:8£`Btâ qšêy¤.Ræ)IÐ0íˆç°r»Œ£¦³bAëEiŠX ÇÈ/Üëþ‡=SmÞ¨‡üd„ûá¿lÓà˽ƒ\Î7’9$Ùº¯ÞÇædˆ_19³s!÷%„€Ö…( ÔñèåW¯;Æ#Nº¨(âÆf(C™ÌšýHEÆàB¼É0ø¤éYuZýJ¯Êž:]—mJ*ò®Ó^¤{hÚ3â[ÐxËà%Hã·ì…ÃhßùÑfÝu(èÿC¼Ϊœž3grNS:lÙŠÀ•Ì…šs@aTêBó£c8žäª¦ÑOOcx³Ñå€SèÕd«ÇÅf XÀ#¯›yzšÿK‘¦P•õm‚§E/Oç[8,ñPUöjûÆoߘ̸‚c°·ÂÈ^Ƭ^í‹ó$•ïìxÆ‘©ÏwŸÄÌæÃ UbOÎÛçÖ§Eà£"Pú÷Q[¶õúP ;^¨>~ÕOÚmTg+Ä|llLºŽ€`dz´SÕ“TŒ„Ž}·®I«óQß'aíÉY\ûøŒí…ó”Ú¡Ö´„y£‹(Îv¥ÁæH}tn wh3C>ìâÉÛ÷2”ZÜiQP–Ûh0ÊÀ’úËÐA« ònr¶ŸÐŠ8kÀ•çÒ1mYꎣ\ŽO1tM<%[àv#b#’svyÜÜÂÉO etcBaºA®Ø‹à?Ò&Ê™ž€:ÒzÑ’äC#tiÇÙ^4§2- ™Œ5—P »àèµ8 ÕßSŒKã“æÕ˜ž·‹™3ùæ0Üð¡³ÔÙÊGWV€%gë1¹`txÝòtܲу“­ÜŒ¸§KÀ/”d\³H,½|¾°#†5lâ—´On±=XC€²Á|d;Ÿä|e££¦& , Út_$P{Y²·Ðè3ô v-¢0¾Œú¹miá‡æ|ÌZadb|±L2ÈP²}ŒŒÞµ_Rl[@ýþ*¨ÌÅ`Ñ¥Ÿö¨ü:§yc˜3¹ «ˆQ¹oñ%- -çOËYÍ·¡ë:¿–µ`¡™ô™1¹ Ÿ^ì0Ϲ}CXÿÏ,7Ó3lwÒ"¬[&ºeòLÁ’ç¥X3¸^„ÄJò¤$º™å3«0›Ñ‘¬tóƒðd{çx»~Â¬Š´£î#rHGš÷ßnä5„„P~ÜÖb“UŠÀWZµ’­Wxˆ€íõ¸Ÿå“Óç¸TQ4©øOvn¤”cª›°Uù‡EÂ|án¹a›Ièj>~¹Ÿ$@šwJP 6‹úT"Š#y6Ì«ŠÐœ¢$ñ>B§é1tt©fÍéJ£B'’í«ê”ŠÐդŸ(ÖW‡Œ†Ö‚§2t¬¥^¯:ò ”Uì–û‹ÚMëÅ%8ñ¤ŽÇÿp)Æé­N›×å)¨¹8jP¶ñÌ¡î7^$€·¸ðdš·„m`ÃBYÐgiC{‰tñKDMs  lÌèŠxʺü5–ü)Öʯä”T´¶yã¼üGÎúÉH ´‡§Mý]ZŽ0R7R-:½ßM‘­#Ž(¬8€Ÿµ°3“:Õ·ÃÇ’’ú\OE¶- FâXkHBCçdÂ¥ˆ‡ª™ \1ÇÍE•V!ít²±P 3°V‹SÖ€°D+/¬‘è.´%×Ðô]˜“t+¡G×eŽJq\à\ã:$©™ˆDZ°GmmÇ_€/®5”ÈK>^TÊ*˜‹€¦áÝçÄ Š"§WiG¼R©:j‚M#yôÌ_”~ŽLx™gÆœ£[0ýš¯&`æÒÙ}z|Å>Æ‹N'–™Î0¥ûEƒÙßJü*s½KãBøa©ìú³tháFþ[®²ã!ƒÉó¸ö)á}=?:†µ¯®ˆŽú.ÛŽÂ#þV4| Õ=ÓõxtÀ­Åøî˜…öÔ]ʯûÙl:µ·yYS»zEã‹À§B Ö¿OÕÜ­ìû‘¬a“ ÙɳýñaIlÄÏ¡)·òha¯ H‹¸OäÉÐÛ|í¬Ð[¶§ÍïSQ³¸óQ¡Ì)‚{áñ”q•õqeÆ‘ûà°Ê‡Î¨¾Ü¶m°ÞpÅÙg;žR p ž®öaÕMàJcó££·"·á3#Ão`€!ÑØøÔ×i ëSÅnDFže‰Ênº¶y»1G€H“Ñà7îv§™0þPâÕ7O)ÐèmþTX$J¯;ŦG×çåëŸÞ¢ë d2øÓ´¨Ñ˜6ˆ±XV ® HzÿgüûÉ.™—Å”þg?¾뙿s€µ,³~U£c®£™ë˜Ï#Càè. '0þâ9t_LUæÑ·Y|±—ÉN¿dîØR ýÀS€É]QçWÏa|R H5=5yµÁÂJ©Ój_“ŸÞ^[B>çòÁúèëºÇi7“9Ë*3#úŠ–P I([‘Þ¢WÏåÙ‡e‚Êz¥«'VYN’?7B£F—Þ vO]Ö/KÕÈ]n#oxBY÷%…áLi×ôöµŽÑ½:Ç•¥ ý–Õ:н|žr!oӠܞȓñ1OZ¤˜×2®ÒZ»13”üÚÇóóî2l|ø`”þ}°mu¾»/è´œãzü1S&ë‹4F¡‡ë—>üd¨)/}²Î~ ºÝ±KL~; ëÈYóËÓÓ[…ñA¥³²5e©þTlŽÌò<Õ\’'gîQž¨’O~ûk¼Å¡õ_Š~㜠$ÍÒ¸|O9¯4HTG[#±´·ÉŸ®!ÿý$Ö¡ ôf ð³Êˆ–0—¡:Ø—‹î>j}í½QÞÍfjºžIõñyb;ÜbõRÎrÖÝ­èó¼²f+O)Iz…{È# 4`<'«‚‘A¬Md§*s#äŒ~¢¨b’Vaü]EsûiµæOcî˽H£s;ίÓt =SZÄ Jšu•#L^·O¿Õëp~“ö©q~ÝS¸(Hã,0â78÷>L‚Ks¯U<£@97ÙŽGÚ=³ð¡OZzñûG3Ìýsö”"r2÷Š}¶ãi€»¯­A7â7Å\j¼³NÝÈ;#'¾6êÂ}À4¸î£"ðéð±éUŠ€eÅ ~ß¼[4|PQò¾.·ÂC §¶°Œ˜Û@v¦ùFRén…é|±9ph¼ÜáTÄÚóˆ¹ äw1Š[I4cù# Oäɰü$ o½‡IÂR¨nOÞBŤÑÒ¬[oò§+ã·(¥ xq¦L#0ª½@̲=~‚ð°‰a” Ú ÎÃâõQ-~…‹HãäШHÔ5ÌvüZ @ô:µ`Éû¢Uða˜Â(^þî Iq§>2Y;Ö‚’ǬÁ)Ñæ1LÆ@@“†QÚ«OñÄ4râ+s÷-2(¢—ZÑÐ^ô9,‚ìxy™† ¡Iög>ܰ´TA+ ´Nc²´Á|€ ‚G{–çKº MþÜóläDgº€ƒ¹E Ç>ÅÏiºÇ3Â8$cË,⨾Gê ÂÀÑQXLN!&ãÄ là`¾&PÍ¡Q$×@š)…¼ú«¡GE„S©ÓVÝC'ÑŸñÒ$gùa–´ ÜNó$ŒGZôõTòÕHÃGQål@VÊiˆh†›Bêç a¨·d«!×_ƒÔPU ÃöÉDòš^iµÔF,Ì?éÛ¸Ó­ðHb®S_“Øó$ЖÄlÿ$‰‰—°ê?Ž »™$<ž'ÉZs…ÏÊm*½×ŠWð¸q5A˜Û€M¤Þ¢[Z>¸¶64²ß¸•¯@øTü°FÞ«|rh?Q‚r³l4¢žò|‚"E­yB/GžQ+-T˜MÀVŸFúГOcòÁ*•ŠâÈÔ°ÉyyD!ó¢û·[âOoÃ:˜8NŸžF†^FE>8F*Œ"q=:>:ÆÐ× #ðÇGW14uIØ{¯–xæz¹$øÕC›*áŸýÒ«]·\”ω·¡Y:bp´yŠ2wå+EY<“2‡RJ;p°T¨0 áHÑÑqQU³"H·Ç÷¡°bÄatx‹ræ¡Ï4¬€êÌQ ;b£“Vyæ…€ørkñ¹.ì]zB,š^Ç ªF ©¨2aDoiÕ\Tª?Ý4”ÕȲ¶¢h{mˆ"žFžï@€R€ØÏ• \½°“<"ä¯Êò–€ªi¬ÐZÍ"Í¡A5«Æ¼N>§—N¢«ü¼,îôE§‘ÈÛQ~Ä™«KÕ6·Ü eÈ´‘YeåòKüé-Ú)Ç€>=ÌÒÆKô §õ§ Ÿf¸Df®3 /ñ›[_-«}–7bó#‘ [Ÿ•9þ*l쨸®Žñ>…zaòœ]à¼<¼ˆáú˜á1&t‘¼Õ„çu?æÓ˜"ðñ(ýûxmÚ½Œ@ì©é{sPò¥×FãOõüM<ß|„,`?OÂn–¯#óÈ“TÙì÷’ò”Ÿ2{Îj#;ÙhƳ&½)^¾Öö:nd–G¶…Àê%fN5`ZY²:½åº&ŠõéÓÓHv-IüžÛéS‘|±l†äÎgÃÕ°±Hò —F/Ã;GM4r&Kt2çæQ³¨M2Ç^¼î%Šò¼HFæ éåÎÞ´8Â¥á:h7‘BÊJi™‰XØ„É+êàQÉUC T9ôÃëœÖ`Ï’=rFA~Ð-3øÈ6(ºŠÉ¤ç³r%0Ç[IÔC¼â¡ 3i}%ñqœ³Ú‡¹+Kbø\Ìçäì·¶Bw-Å,Öpø„”þ}ÂFo•¿B€×P>ì_=¸¸±œOÞ³'^1Ƀ]‚ZF;<õ»xÏÿó¹ò" ÃWsŽŒ.v“Øè®·iU*KŠ>F·ÂbÍ6?ŽŒš*âŒ"´‡©|J²’åãò$IÆžùD82>ˆ°2)ùD>O•±ÿp‚Šç²Öóü-•,ýòÓÄÐÿŸîXÜ®¬7sA¹ÅÀÎ{fj" /;gn“pè¥>¦òR ŸÈ“qr“ˆO#7B+ë·©x±ØìÎúw+ÞhŒHªï^ô$ÕAaX#éañÔÚ§b$±ÞLÓN8{yOqQª*={zq›ÄÁTvzªÍ·NnrSrjÖK-h-Ÿþ¤.Ô}çÎQñ½ë¶`³-–ñ¢:íÀ&yJõAò«³ü1ÌÆb«Uó°üÏ8F?‹_G:=â4+ô~“ƒB–½—LC<$9â2ÐÎY¹›2.=_Ëßxçó¤³*•ªùLf‡!ûù#K§ÉŸGꓚeò"pt´p'¬ˆÑêå]T@ሙ/ò–N¨z¬ ú*Ç]§Œ:Ìðª“ãrVsر…w Âf4Qß½t¾…8]IœòâÍXg°ìÂ*y§qâÏ«y*™¢¾š¸t$\‚=[ÆH mù33ê –HDê91k}ë0dÌt!-$¨ÁÞâš‚Ék¥žofb$¡â9+ãÅÕóèê¯7º¼bH§jǾz•d¯ x©å£RßCSY¨4÷&ÉüÈjþ ¹ã@æøÓ°¾¡¾Ê ®¬ œŠ-‘ŒºŒ·5áåÑÕ-ÿ„œù7ÞËWb‰Wr=Ù6èæ!¿Ie„2Ø:\ÇÒ’s•6’ó#ÝÆ‘­Fî9þ4l¬eC„±yzîQRéQPÍX ãáÄkû)úÿŠá9xßk&V7ã™ìåµo·öaã´ð,Ÿ Ò¿ÏÖâ­ïWøâÆoðö‡€“Œ“qbd6ƒ}•×Å/.囚BMŒµíBð«èßÿþ÷ޱenü ÙW¿¾q }HmÏúî×/ï|q™8¬ôÓ¡/…¾~`…Fuž!ŽÁòšËô_çtyga›þÍÄšMƒ—rÓƒœÂÊ{0KõÓ““ ”&JkÛ'B?F3Ö‡ëÆl{%9LJ j.ó z@K¶ù„š2èýQúñö"ç¾R%Cù°D™c ⺆bÉ{w ­kp9êر‘ÓW\Ez5*%+¬€†íÈM¥¥-éZhƒ £³Áƒæ‘ynuxv g-xDÅ©f5‡õm°¨¦ßD)©×øí ]Ïé(ÃihÖ,Xá@êRM1$)Ä ö—RѤO“C¤·„Žz)Ž$sˆQˆñ1ÈLVç0@âÍÍX+s7ÅZE2™¢[.‹Dácú¤‚i)M¦£zd³=)­êK¨lú‰±€>!Ìz8RmtЪ±;kH£~¬dk,ˆÌÀ'oc¡Vöv/µ(¦š&Ô CR ªùà{W#£ JÖ&®Äöñ7/!ux bï×€…ÑYvñF ÷9{ÊvÊ9ó%ÖÄ _€òOÿôO·ùG ¤ÔX c¿M¥;5Æ×ý[áèQÖ™dMã3§ÚÐ'³®(¹iô‘êŠn™Œ#½+‡»忹‰Öq/öð+¿Ï¤O‹ÀÇ@ ôïc´ckñ-P\˜/è.4!JE„Nàïi@$½!û:œ»Ç^>OÑv6§®YÖ=ÊŸ–›­ƒ‘nJ‰Â_Z£Í½‚ªAÕóµÎ–žÓ’,‘V[<À¹kyt¼eN±ï‚¾K5|¸D-ôƒ#ÐCëb^оÂöáOH¥Tš¶s™é%­™Ô8 aìl8V©gÓ°^¡óP‘Q£[j—$±lo%'Áë„ÑB*~Ì/d<¢‘;²R‘©ã”¨É(|Cì´<ÇHJ›ö ó£jÞeÇË•jºåhKÅB‹xP‹G,f ¤ºn¶Þñ#%c0 q;j†¼qdPØF§_"<Ö/âÀÍZnù÷Ëö¶9 »«#æ¬? ñÈŒüû¿ÿ{ÖZ'ê?ÅébV^mÙ²ùè‡f-W:¤¬O¶) 2æÕÊ áÄx¯ñȘÉ{ -+Z*4u®Â÷„5È¥œ-åcËòR=Ya—–¿3}R6¾¯¦Vk^süœd‰w pÝÏÔßfIa=óç]¤ødôp­°\§ôÉŠïOWNr*¯ï™Š}Y€< “_xxJ®'£ëà²W]Xk.|8ór¿M§@0 ÿå_þ%gÿÜ&©@øØÜè1»ò­ÝgF€zçÛì{`M—äÛãòm¾ ÐÉœüáæ«ãKv%¶äÀWÊ&1Ú0Ýè4ÿßxÐ\©bêëó¯"ô9u±ØŸ˜™7Îá<¥PïlíðE"/*©Õ\ú¨_ôrë¢<í!¢#:^‚:E_Ä"a:%µž©Ñâz'`Õ_-bßsëí Œ¿FGƒÁè´uò¤îÐΟûa’§:çÒgUÇ‹?êÂ"ÄvĬ<‘cMↇÔQ¶PJª3ô°nÚê±R¸œß ™1®êù ÄÒfaÊ«ÑA/g±¡²£âx‘¶` Óô^ðç*6çóÍa v„È% F×ø*«©ºÓD‡ÌЀ‚€z ë´ÖS,+œê£„4r-¨ù¼Nc–…‘–ü´²tb³Ë9À-%è~ž«nå9òÿæ@¸B5×q„ OÞ¡,´ò×4 χЄfŸ'“Õü^} ßc0¨í€åÐxeÉg\â¸kqDúÚf³.ÚfלKs«¾eŒÏoyf0GÈ]&mdØê] eºú0—©š¾gAA‹#ÀÖh˜—ÇzÊ“·üÆe º…>™4ô@‘hª§Çk‘7[ÂßB t³È‹1;±ê›X´§!²hå÷ÜYÏøŽÞ&ÑRV”ŒA³V~:â6 l}4”ø_<‘'£ZÙ1BÁ•Sy÷ yîØ~¯çÍèØ ÷iøð”þ}ø&nψmʇ1 c ýKíà7’e` оùOkñ£[tBºÎôiÌeü>EMŒ‹Æv bÒé-ÕSú³ÝâTlÎA˜‹‘Ï?•‹m‘ÿ+X”~þuô…w P¸Y½hl€ê‚º0³Ð `ò*8 Æë01ù @èÇX³×a(On½ñI³"±²¢ìJB]Ž!H¤ƒLlݱ¹ÈN!¦Èù¬+îZ6»©õôoº×ltÅܸ¼²aÒѤ;ª?µ>Täø—Of~4Œ°¦×9UjƒÛ÷÷+nFë óRË_ÖK…·ö¯îa5šò811>¨¨Ž’ßnòä|ˆ!K‚á0Ü}­)8XbÓRÌ;|üØ…r‚î!„p“"C€rÌþ ·,Â`ä¢xfD-¦ w¸¯ëz4ÓÅHŽ¿æ’á«·`Œù1À毨ض\&7g½Puæ\VþªŽ‹bÍ2l¥€°Å2ª3@Þz™Ée¸éÏz 1pbÞh®“[ürÕÂ`ÔÍ\Ë÷w­WAxI^!•ÜŒ­F«KXKÙЈlª\ŽÝ’ñ÷xÍñø3“¬¯†e”9^ª,  =Ú]ß™‹ûyEXú1ÃÓ£Þ‡ ÊïžÊ,‘æ"u·ìbH¯XžoÙT¹þbtÙ|8Ƙ`q9}ÆGÖBÆQà4Ætg€¶jt»P^Ó¬,Y9Ͱ‘Eàó Pú÷yÚº5ý‚€5fºõ÷ÿ<¸Ù]ãcIù¦ÃQ /¤Öh¶n0L(9Îa}|vO—Ââ(…¾gù1ƒ3©/q)˜º°8•æ/r_‡²­‹Of~Ïàë‡_ÝùSGÅhÆjDrJ›­ÊœëH$…ŸAoœÇp*6ާTI‹ß^Ï @•ï.a_ë·zC}Do”Ÿ’‡ÕÐóœÁ(ß å)ÕÁpz›eø(ÜLIó¬,@Ìî”?ýéO ­j päЦ Z|\kŠø8%‚~é¥ä‘·ï7:)¼2{Wh*¢¨´òŸYÄ“Tz^a'gN†7×fá_MUŠ1‹°0ÞÎûX9ˆƒ¨ºãT†-/¾Ó^@€úèVù†Ùtç] )æªH(n£² ohäÔ%£¦¨uNßBRË"r°¢¹îùÈvSëÌx Â«²‚å”&]ÂXX.]8ZßKãȈsP£J£ùAi]é Z\¼bÈDi£ kŽß8×À5pöWŒJ)¶[3À«åWq뺫KïEØL§ñ¡ß™q{Pª¬GÈã®5K>)í&9yÅ ¦ ËFr~Äq×Tðð“¡õ1mÃq[ߘóÙ„9Vp¾]C™sÐl¿ÞÂö‹pÎNÃÖP2³Ÿù§2ÇHDÎ4¢Ûc›Ç§§1Z™ç…©’!ôÉ>#4fÕåÔ?âô-,»Wøl8ÚÎx¶àIÅiWÔ5òŒrOä#cÓ‹$Ô.yÏSÅ´h©õa’,”ú¢?”'ÆÃ*³0ÿ<½G*zÃÃ$–rÉÓ’ÊC,%áñ5’@žÆl!Ùz-¿ 9Óßí*¯L– 1–ê9=^’çöûs8Íö—ˆTSª0-Ðé¯3ñˆ…¥ä3æxvc¾‚|©_Õ }›4ÁòôêÖÂ?mù?ÿGˆéœe¸Êgò*óÄ[ßa0Ô±ù,J{á>-Ò¿ܸ­Ú T= ½ð¼ÿé:'_GÑc²¿œ†j±ÿ뇗w!f>{Ì#Yj½(‹æJ…¢/ƒŒ„üdn÷5Íé}ö¢—ðÏq0ãühfù¡ÄP³ø¾nÄÆ#‹¾ü]ñ%jY"é…¾». :ÇžxÜÛhiTó¡Ìäß ’â,¼BÀë4m7²b¢¡W…!DmõveP$º2bH~€†l$­Om¢ÇS g]6aïÕ¯ØhŠäõ4‹ÌqG×2ÊkÎá$é){nŒÃ;Š“å!ïB óÜè03ÝGÔ/Où!3¹P‚y6‚…“•òÉy Ù,ÇöE^™iöè.ZN±Æ:"óSŠœ~)Ón gé¶j=*ž"½Ñ_¨j¸ñW©¼b,¨ µ}ÍR– Ý ×òÈ…›¹b§BÏtB—€Þn‹&aÜŒ•ÉELŸwŽn cØ$6ÛZ!†²ZV04¼(¢"AfL­oý…q ÁX°eO³OO'aÌÔÖfƒ‚M•Z¬Ø‹°y)Ümð"Ö1˜z£ÕÓc`“<{º,‡)³Ÿ ©oüáЩ೼BPe }\”õ7öUÓˆR…%ÎõIs‹ÂèçÉgDKÎßv«„P5û¹4Ží2E{‘`î·$A¡üßö¢«T^d6@Y9!éq"Myü5!$!§eøüw&äÃÏü¥Ï“>É|›á™Y½º*óŸý±f–ð9þ*¬­uZS>p%³Ä늖ä4ÇC’™ä„ùåšxÉóx›s¡ÌoHãq¦=Ê7¦|HJÿ>d³¶R—8¿GÓ+ xIDATÞª§oŒo?%àRî§,?N°Dü|_Š»ÿOE³cç!ÃLvˆ%›Z‰:2NÞ¾Cùñ7F*:ÓQ¼Jžê³ Pà®dŽñÌ26îy&½ ªGcF §Ò0hx¾åª6kWÇü÷1±6,2˜ Å…ŠÆ €õiJo¤UÏ‹ò ÊU °²RrUH<šÒfO ¥Ÿª±p9uwªŠK1ÈK9À—’ç–âÅ&à©W yœÜ+qa *…úýèXù›1|á$ìÔD}ÀïXÌòÂÌ/ºŽ-gåT`¬#2^áPÕ#ÿÄ œ–ó׌¤£F²\^MÔÄ"±zOõ‘ØQÌ;¸‡HšºKcé ê"-Ò%U.¿fž¿K]^!5ú§Ãè`zµ¿,TÖ§43HbDº4«½Ä:¤ã©þx*ÀG×V+gÑk剞Ù%ÃÿÄçå0ägøý2n³ç+†b ð$ÔgÌY²rÓÉ—êpgõ;ãrpÄ(s½}ªdìœ$Ý-òneÈf›áàV¥ÆïÅ…ÅèÛ6˜iD%w˃@Myì3]Ö7tf¶>sN–!Èëí^¡FF™ïo}hÍôb#ÂÞ¢›ùûó^ZÕÔÔä,ì8ßC ðêuaeP‚Û•Ì1žá‹W³YŶÕãÓÓË=#k¥#Îϧb#R-ì¤5r­bh—¿„›ñ›eØÜKÎO³Íž#º“süiXSê“ „õÆS™F@é߇oâVð Dk¥(5Ýæœ/.B¬C¾tË„ôþ ©5:ç¤YåµÄNY_Üÿã?þ£ß矽PaËVvF]äºFÛ-CaÂå,ä¯Ï.îñdÃ'Æ([>çØ œBRj–§ßÏ».Þÿr4庬ØÜ(m”E¶<<²£Q1 q¢dÓºBç(L+¬œÜ¢€RË(—´gÊ4]0diüFÑÑU’3 Ý‚% UÓ߆° ž19F>9‡eûKTQ*Á'V$ž€ƒ%Rù9'«‹…v4€žšx#ÃWzöj²Læ‘Nbg ü "Æ%ì%ñ#OÌ–f)‹öTõϸ­ËIB\Æ‘œµÑl!ñs^|ÿ47⤱ŽòrÔç•YÙ¼W›Z˜P~v'Ö "b\ê5‘2*†=–¾›§ö²Ú±„'Éׂz‚þ€5Y]2i§+[Q‘j”~ }ÄG[#ª£ ž"`z²ILÿwiy>ò˜ƒåƒDš 0@í+ §&ƒÌ2s– DðS{h'IÅ£ƒŒ+ù ðHç³J)ç¡Ç ÉU8…¡ß“÷7dUä¸L>vã™2Q~ñòçC¡“8‹Hï oÔ1øûYaûš'+ (ü`uû0FÝ[ýýMÍiÑ·5-‚ö›¯ô1KEˆ·–ò׌g|‘°ìŽ\Ò«ÍT`ÙKÎO³qÀ§'[çG§aç~Y&à ¢MOŽ‘ºŸª™Ø5÷Qà4Ædk¢…UQ=ðTfŽt¶–M›f‹•™pæ§ Ï€@éßghåÖñˆ)úBxФÏ$µÒáÝOä#c+‚ÍoèÏ«‡©¨|±è¾¨££ÏnSÙƒAÇ¥MÒÎ)·ò ú0KÒux`R•Dz/õ—º“ËG±,JñE’c¸£ýEVz2”ªZiŒ¢C?µ¦íQ=Â|J1"F tAIuèß’ÌU Œ¢4ª¨ã$±2ÕyþETÛÁˆTŸµG©”Dc©u|<½…:È`âÖ¥5éL Iô ?ÓL‘Ê£ü•Üq¯üã ûDb)Sq ¼$=]ˆ–|`H»r˜ $ÊpõÓé"LÀ¥ Ôn'sÄ4d+#€LøÜrÃ1"¶üe`ǬÆz|\àôU„a8@Ž$–cPkYi\Ú›Ëä.US‹!¶66®A©¤üñê"9n©¹lb^AKÖÇŒ…A'¬¡xãK@alhä;~xu—¯óNT|Æ@äfÄq鯮ëd)½]ŸäÙ‹Ìp¶X£OŠq€·«&Ê.ýƒòÔˆ@®pìGñÿ•×Hz‘·«…A^j5Ä#93Z ù¼Õ9õ.ÅËØLIŒY]‚5LÓ »ˆù«“Xk3¹™1,ñhˆTJ¹³šñà yò¸.ä+0¦ôIÿ´ÄÐXK¶žêÃÖ tSºñ5Á4™’˜ptr³·Y° <Ä~̀ѭ‰-4(’‚©"¤{˜"˜àxˆ°Šòè$ú³RÕà6â÷kh9ÀÑ~³è^ØSË7¶ì’4B!|+n pË “3Ý9“ ©3ž¤âb£es>ðùʆ€ ®Wø Pƒ|·hØNøxRß(¯ô dé‰|d,+š#hê´«ç©bÁcŒz˜$çž_FGÁv¸EÙÇ…µ2_ÐõéÄôBz€²Ñœh tãלÎb”£„¹ ª-~Í·?Xh*&HÖ:E;DcXè%tБ!e‹ÖH°o“ÊÎ ŽúÂhLÿÇ‘t ‘tÐ$AV5œÖÁˆhØã‰L<¥é"€X9´ϽӾšçE’Pvùqeíß_a1§})=uÅS9„ÔišÓ©¼¢*~è)gæôNVTÞSy‘Öïõ.å§ [G €Ê/RÍ«$âs‚_Î}eö‘„ Îîw•§"£{+ 'ŒG] 'žÆ¯T:›’+³È}ªø4Z3ò"þÏ^D£½J¢ÀŠM&[érö£ª]É‹‡š' ¢‚B¼ @0^¥R’¸ª94 ±øÓ ÁS[peè^¤âbâóÆÔ6^( iºÃ 8‘§’à º±ÝÈzl~9gÎñ§¸ºœõvÝ[çgêÉ V*ÆRîˆNY z0š±C†%Y¯Ë°’¿KÙtZ—~˜nŸøßÔ_eKçW—Fñ”ŸEN¥tõu©©ñnEï¸ØaøìY®‚'ÒaFn¿hVZÑ„–-f9¾ýƒôm«<{qilM?ô)Ýc2!ø˜} óž~²›Ÿšî” fÎ9~6Õ„ßòMØKæ)5 ³Åà‰|eŠÀCàw¬>­N¸B Vµ(¾W2s|tJÇÃÌ‘û0µ,ç©ÌzÕ>‰§V£cßøóŸÿ,F Z %Æ:æüš…øârõäÑd}ÚWÜב·ø–SMh‹Rò‹ª¿\æjA‰¤£ÐŠ]P­iK–´iÕ¶YÑW · ,–~4À jÖ¥CHÒlXK¨5Û ÏxE?)ï_稈sö(²_Ú+?`_ µ˜N̺˜$0—•5‚E>·˜<1ŠSX:’[ú4M»;&ш¼B+ËYXE‘b·<Ê‹aâ FtXËX¿§zžæ?rðëêR |Y£ß#Œ!C` ´ä­ Øa( El…]dŽ·Ô_Â@Ö"3ÔQlŽÁv¢+c2aJ8 ,áİÒ±—Eæx«Ÿ(¥—ËÞQf‰a 3ÐÛdÚowî"3ß*6aU%S© ‘KB­ßrmÁ+¹1>çyÖ šFIŠSI‘Xnx¸A¡`™ u†+yñ:’î$[—6~CÇ»Jeø˜`ÍE’x‘ñ"aÝû˜Ä#Ã!Œab°2†S¿bJ°VKZz”®•ü‰YÏB2E:É­nfuÃ=š-5–·#ÉŒ.íbMEGÙÆÅÑ( ¾Š)Ì2‹.·?æñ¹þ˜u-‡™*u~Ð4MàKdÍ i´ÝÎ:#››© é\£yTê >d>g\Ááešò™;ö«P©|Låp%sŒ·!•¾z|t“Ecáôi#‹ÀÇF ôïc·ok÷¿P,|ØR(sO@ÉGË2-œ}&*ˆeBN_kü‡x¶ÐŸ¬Pbht>ëýÜóºÅݧ…ÍÍZ©¯‹Ua¶#KïÔ;KÂ}}J1ª¡Ëp9 k—‚¥lt&ncУÒ%f)*ý€¥‚<ŽWòⵚ1;œÞQfyi Õ«T¸$áØ?¾çw&l-3-Ì©°+ &(^Ž‘ôWãÌœ%9šòºÇ‡Q @¦2 sVš[õûëä ¯:"Õ¼dûpLY¦Ê´Ýx‹€ºèøª™Ö­± OÈÏóÛ,ÿÛ’[ш)NwB#Á Nh¿&˜€”W¶í¸é¢N¦áp®šˆØŸòi£5Ì};øcû"óGµÁj­#1f µ:·éUØ—=o¡\É4¾|Tº÷ï·?¶„?Ù³þ3dÔ,Š@(E ‚õ`üFÑG©SëQn(ý»¨? còÃÔ¨)E "P¾æïIÞ´Eà(ý{ÇVk™‹@(E "PŠ@(/#ð^NÑE "PŠ@(E "ð†”þ½a£µÈE "PŠ@(E ×(ý{³¦(E "PŠ@(E ¼!¥oØh-r(E "PŠ@(EàuJÿ^Ǭ)Š@(E "PŠ@(oˆ@éß6Z‹\Š@(E "PŠ@xÒ¿×1kŠ"PŠ@(E "PŠÀ"Pú÷†Ö""PŠ@(E "P^G ôïuÌš¢"PŠ@(E "ð†”þ½a£µÈE "PŠ@(E ×(ý{³¦(E "PŠ@(E ¼!¥oØh-r(E "PŠ@(EàuJÿ^Ǭ)Š@(E "PŠ@(oˆ@éß6Z‹\Š@(E "PŠ@xÒ¿×1kŠ"PŠ@(E "PŠÀ"Pú÷†Ö""PŠ@(E "P^G ôïuÌš¢"PŠ@(E "ð†”þ½a£µÈE "PŠ@(E ×(ý{³¦(E "PŠ@(E ¼!¥oØh-r(E "PŠ@(EàuJÿ^Ǭ)Š@(E "PŠ@(oˆ@éß6Z‹\Š@(E "PŠ@xÒ¿×1kŠ"PŠ@(E "PŠÀ"Pú÷†Ö""PŠ@(E "P^G ôïuÌš¢"PŠ@(E "ð†”þ½a£µÈE "PŠ@(E ×(ý{³¦(E "PŠ@(E ¼!¥oØh-r(E "PŠ@(EàuJÿ^Ǭ)Š@(E "PŠ@(oˆ@éß6Z‹\Š@(E "PŠ@xÒ¿×1kŠ"PŠ@(E "PŠÀ"Pú÷†Ö""PŠ@(E "P^G ôïuÌš¢"PŠ@(E "ð†”þ½a£µÈE "PŠ@(E ×(ý{³¦(E "PŠ@(E ¼!¥oØh-r(E "PŠ@(EàuJÿ^Ǭ)Š@(E "PŠ@(oˆ@éß6Z‹\Š@(E "PŠ@xÒ¿×1kŠ"PŠ@(E "PŠÀ"Pú÷†Ö""PŠ@(E "P^G ôïuÌš¢"PŠ@(E "ð†”þ½a£µÈE "PŠ@(E ×(ý{³¦(E "PŠ@(E ¼!¥oØh-r(E "PŠ@(EàuJÿ^Ǭ)Š@(E "PŠ@(oˆ@éß6Z‹\Š@(E "PŠ@xÒ¿×1kŠ"PŠ@(E "PŠÀ"Pú÷†Ö""PŠ@(E "P^G ôïuÌš¢"PŠ@(E "ð†”þ½a£µÈE "PŠ@(E ×(ý{³¦(E "PŠ@(E ¼!¥oØh-r(E "PŠ@(EàuJÿ^Ǭ)Š@(E "PŠ@(oˆ@éß6Z‹\Š@(E "PŠ@xÒ¿×1kŠ"PŠ@(E "PŠÀ"Pú÷†Ö""PŠ@(E "P^G ôïuÌš¢"PŠ@(E "ð†”þ½a£µÈE "PŠ@(E ×(ý{³¦(E "PŠ@(E ¼!ÿ³¦Œ ‡ÐIEND®B`‚doc/example-scripts/tk-horse-race.html000066400000000000000000000773431242365656200203260ustar00rootroot00000000000000 Listing of doc/example-scripts/tk-horse-race.tcl

A small Horse Race game, originally developed by Richard Suchenwirth in plain Tcl (see http://wiki.tcl.tk/3467). The game was rewritten as a design study in NX by Gustaf Neumann in May 2011.

tk-horse-race.png
package require Tk
package require nx::trait

 ##############################################################################
 # Trait ListUtils
 #
 # Some list utilities, not part of a package we can require here.
 ##############################################################################

 nx::Trait create ::nx::traits::listUtils {

   :protected method lpick {list} {
     # return a random entry from a given list
     lindex $list [expr {int(rand()*[llength $list])}]
   }
   :protected method lremove {listName what} {
     # remove a list element referenced by the elements value
     :upvar $listName list
     set pos  [lsearch $list $what]
     set list [lreplace $list $pos $pos]
   }
 }

 ##############################################################################
 # Class Horse
 #
 # This class defines the logic, how and where a single horse and
 # jockey are drawn. The painting of the horse happens just at startup
 # time, later the horses are moved via their tags.
 ##############################################################################

 nx::Class create Horse {
   :property name:required      ;# name is the external name of the horse
   :property tag:required       ;# tag is an internal id
   :property canvas:required    ;# the canvas, on which the horse is drawn
   :property n:integer,required ;# the position on the canvas

   :require trait nx::traits::callback
   :require trait nx::traits::listUtils

   :method draw {x y} {
     set hide [:lpick {black brown white gray brown3 brown4}]
     set c1 [:lpick {red yellow blue purple pink green}]
     set c2 [:lpick {red yellow blue purple pink green}]
     ${:canvas} create oval 0 -1 18 4 -fill $hide -outline $hide -tag ${:tag}
     ${:canvas} create line 1 12 3 0 5 12 -fill $hide -tag ${:tag} -width 2
     ${:canvas} create line 15 12 17 0 19 12 -fill $hide -tag ${:tag} -width 2
     ${:canvas} create line 16 0 20 -7 24 -5 -fill $hide -tag ${:tag} -width 3
     # Jockey:
     ${:canvas} create line 9 4 11 1 7 -1 -fill $c1 -width 2 -tag ${:tag}
     ${:canvas} create line 7 -2 10 -6 15 -3 -fill $c2 -width 2 -tag ${:tag}
     ${:canvas} create oval 9 -7 12 -10 -fill orange -outline orange -tag ${:tag}
     ${:canvas} move ${:tag} $x $y
   }

   :method init {} {
     set w [entry ${:canvas}.e${:n} -textvar [:bindvar name] -width 7 -bg green3]
     ${:canvas} create window 5 [expr {${:n}*30+5}] -window $w -anchor nw
     :draw 70 [expr {${:n}*30+14}]
   }
 }

 ##############################################################################
 # Class HorseGame
 #
 # Defines the main canvas of the Game and contains the logic of
 # starting, resetting etc.
 ##############################################################################

 nx::Class create HorseGame {
   :property {bg1 green4}   ;# background color of the canvas
   :property {bg2 green3}   ;# background color of the result label
   :property {width 750}    ;# width of the canvas
   :property {height 330}   ;# height of the canvas
   :property {horses}       ;# a list of horse names participating in the game

   :require trait nx::traits::callback
   :require trait nx::traits::listUtils

   :method init {} {
     #
     # create the canvas
     #
     set :canvas [canvas .c -bg ${:bg1} -width ${:width} -height ${:height}]
     pack ${:canvas}
     #
     # create the Horses
     #
     set n 0
     foreach name ${:horses} {
       set h [::Horse create horse$n -name $name -canvas ${:canvas} -n $n -tag horse$n]
       lappend :tags horse$n
       incr n
     }

     # finish line
     set w [expr {${:width} - 20}]
     ${:canvas} create line $w 0 $w ${:height} -fill white -tag finish

     # start button
     button ${:canvas}.button -text Start -pady 0 -width 0 \
         -command [:callback start ${:tags}]
     ${:canvas} create window 5 [expr {$n*30}] -window ${:canvas}.button -anchor nw

     # label for the results
     label ${:canvas}.winners -textvar [:bindvar winners] -bg ${:bg2} -width 80
     ${:canvas} create window 70 [expr {$n*30}] -window ${:canvas}.winners -anchor nw
   }

   :public method start {running} {
     #
     # When the "Start" button is pressed, we turn this button into a
     # "Reset" button and the horse race starts. We stop, when more
     # than two horses pass the finish line.
     #
     ${:canvas}.button config -text Reset -command [:callback reset]
     set :winners {}
     set finish [expr {[lindex [${:canvas} bbox finish] 2]+10}]
     while {[llength ${:winners}]<3} {
       set this [:lpick $running]
       ${:canvas} move $this [:lpick {0 1 2 3}] 0
       update
       if {[lindex [${:canvas} bbox $this] 2] > $finish} {
         lappend :winners [expr {[llength ${:winners}]+1}]:[$this cget -name]
         :lremove running $this
       }
     }
   }

   :public method reset {} {
     #
     # When the "Reset" button is pressed, we switch back to the start
     # configuration, the horses come back to the start.
     #
     ${:canvas}.button config -text Start -command [:callback start ${:tags}]
     foreach tag ${:tags} {
       set x [lindex [${:canvas} bbox $tag] 0]
       ${:canvas} move $tag [expr {70-$x}] 0
     }
   }
 }

 #
 # everything is defined, create the game
 #
 bind . <space> {exec wish $argv0 &; exit}
 HorseGame new -horses {Blaise NX Animal Ada Alan XOTcl Grace itcl John Linus}

doc/example-scripts/tk-horse-race.png000066400000000000000000000632561242365656200201440ustar00rootroot00000000000000‰PNG  IHDRñ`½ˆ±ÍYiCCPICC ProfilexÕXeTU]·^{ŸäÝyèîé.éé.i0@BBQJJQ@PD”•DBPÁQEE»ñú½ãŽ{ÿÝ?w±×yÎ\ó¬½Î~ÖÚsÎÖ"Ïðð˜€Ð°èH#]Š“³ ÷à  Àçé®ceeþ×¶ú @»ƒã’»sý¯nÿóo”72ìååŠà[^ôŒ®BðãƒÑáF!`ŒDˆà»Øÿ\¶‹½þÁ ¿}ìlôŸð$OÏHˆ;%ÖÛ™ƒ¸–>Ì'0 z4‚5½<}`ÕG|$BCìâp‹xýkÿaOO¯¿szzúÿÅÿüä—Èõ£ÂC<ýþòÙ…†Ä Ïëw£GzRXÈÞ]nðÈ5çã©oö‡‡üæì·Ý7ÌÞö=Ìk¯åìihó‡Gëþ [Ùý±Çèíýƒ}£ þÎäijõÇccÿGÅÚüÁñvް¯þ_»_ ¡É{`´Éß{0û»,€'ðŽöÛåè?èMÑAv™¯Å$Ì[J‚"'#+»;üÿ¦íž¯»lóûÜ@Ì#ÿ±E!ÏTm9cÿ±y$ÐêŠlqåÿØø õ“Üã÷މŒýg>d+€@àü@H9 Ô60¦ÀØgà¼A‘à HI dÓ ”€rP ®€k ´€»  <ƒà1x ^‚)ð|‹`lB„ƒÈÄñ@‚8$©@šdÙ@Îä…A1P"tÊ‚r¡è"TÝ€nC]P?4 =‡¦¡yh Ú€Q0 f„¹`!XVu`3ØÞûÃp<œŸ‚‹à ø*Ü wÁƒðSx þ ¯ ŠˆbFñ¢$Q*(=”%Ê凊DAe¢ Q¨zTª5ŽšB- ~¢±h4-‰VG£íÑÞèôt6º}ÝŒîA£§Ñ‹è_2†#ŽQØ`œ0þ˜ƒ˜4L!æ2¦ Ó‹yŠù€YÅb±ÌXa¬2ÖëŒ Â&`³±ç°×±ØQì v‡Ã±áÄq8Kœ'.—†+Æ]ÅuàÆppëx"ž/‡7Ä»àÃðÉøB|-¾?†ŸÅoRÑR R©QYRùP¢Ê¡ª¢j£¡ú@µI #4v„ B¡ˆPOè%¼",‰D>¢*ÑšH…¾’þ>ý ŠŸAÁ›á8CC/ÃF,£0£ ccã5ÆaÆE&z&&¦8¦R¦{LSÌ(f!fæææFægÌ,\,:,¾,,õ,c,k¬¬Ú¬¾¬™¬×YŸ²n°QØ Ø‚Ùΰµ°½fG³‹±[³d?ÏÞ˾ÀÁÈ¡ÎáÍ‘ÉÑÈñ‚æã´áLà¬äâ\áâæ2â ç*æºÏµÀÍÌ­ÍÄÏÝÎ=ÏÃÀ£ÉÈ“ÏÓÁó‰ÂDÑ¡„PŠ(=”E^N^cÞÞ‹¼Ã¼›|Â|ö|É|×ù^óøUøýøóù»ùx,ê^R ªžì\rJjšf6Ž®~%Bщ©y"ŠU ='úX S +‡Å•ÄÅωJ`$T%Â$*$&$I’:’±’u’ÓRÌRæRÉR-R_¥¤]¤ÏH÷Iÿ’Q” ‘©’y)K/k*›,Û&»$'&ç-W*÷Dž,o(T¾Uþ»‚¸‚¯Ây…IEE ÅtÅnÅm%e¥H¥z¥yeeå2å F+•l•‡ªU]Õ£ªwUª)©E«5ª}S—TV¯UŸÛ#¼ÇwOÕž > O‹SšMÍ šSZ¼ZžZZï´ùµ}´/kÏêˆêé\Õùª+£©Û¤»¦§¦wX¯S¥o¤Ÿ©?l@o`oPbðÆÏÐß°ÎpÑHÑ(Á¨Óclf|Æx„ËÄÛ¤ÆdÑTÙô°iÉÌÖ¬Ä치y¤y›laj‘gñj¯àÞ°½-–ÀÒÄ2Ïòµ•°U„Õk¬µ•u©õGY›D›>[[wÛZÛU;]»»—ö"ö1öÝ4®5kŽú޹ŽSNÒN‡Ù[]p..—]Vöì+Ø÷ÁUÑ5ÍõÙ~áýqûûÝØÝBÜî¹Ó¸{ºßôÀx8zÔzlyZzVx®x™x•y-zëyŸõþì£í“ï3ï«á›ë;ë§á—ë7ç¯áŸç? P°¨Xø=È8¨Ô#ôv}XpXÏîqFÃÅÃÓ§"Ô" "#Í"/GAQû£Z£‘Df(F$&5f:V3¶4vý ÃÁ›qtqaqC‡Äeš7Œ¿”€NðNèNäMLJœ>¬søâèˆ×‘î£üGSŽ~8ftìJ!)8éQ²LrnòãŽÇÛR¸RŽ¥Ì¤¥Ö¥Q§E¦M¤«§—Ÿ@Ÿ<1œ!ŸQœñ+Ó's K&«0k+Û;{à¤ìÉ¢“;§üN ç(åœ?=vúÙ­3Wrérãsgò,òšó)ù™ù? Ü ú  ËÏÎÆœ*2/j-(>]¼UPò´T·ôzgYFÙÚ9ŸscçµÏ×—s•g•o\¼0yÑèbs…PEa%¶2¶òc•CUß%•K5—Ù/g]Þ®«žºbs¥§F¹¦¦–³6§®‹©›¿êzõñ5ýk­õ’õ¯3_Ïj 1 ŸnxÜxÖhÖØ}Såfý-Á[eM M™ÍPó¡æÅ–€–©VçÖÑÛ¦·»ÛÔÛšîHÝ©¾Ë{·ôÓ½œvB{JûNG|ÇJgxçB—×L·{÷ËûN÷ŸôX÷ ÷šõ>|`øà~ŸN_ÇC‡wûÕúo¨ ´ * 6)5=R|Ô4¬4Ü<¢<ÒúXõqÛèžÑö1­±®qýñOLž >Ýûtô™ý³É ׉©IŸÉ¹ç!Ï¿¿ˆ}±ùòØ+Ì«Ì×´¯ ßp¾©x+úöú”ÒÔ½iýé¡w¶ï^ÎxÏ|~õ~ëCÊGòÇÂYžÙš9¹¹»ó†ó?íûôásøçÍ…´/t_ʾŠ|½õMûÛТÓâ‡ï‘ßw–²—Ù–«(üè^±Zy³ºº¹–¹Î¶~å§ÊϾ ÇÙ̓[¸­¢mÑí¶_f¿^í„îì„{FzþÎPHûù°T نǨÿÉ{ é1’´Ã¦C²‚NÈ ú \К¬!Ιjš¨A*¦†hBißÓû0|fòfžfõa{ÃaÁÙÌÍÅIiã]âçPÔ2ÖQUç– “DK®IÍI¿’ík”¯TÈULV WÞ¯b¬ª¨Æ­ŽUÿºgB£S³V«D;G'S7C/K?Ç ×0Ï(߸À¤À4ßìŒù)‹¬½',S­Ž[·I±=n—bŸâì˜ätÔ9Á%n_”kØþ@7wo_Ï`¯ï$ŸS¾¥~Õþ7Ú¿™ ][ GEÐG F©F[ÅøÇ>x&®úP[üpÂtâÒÔQÚcìI”dþã|)”TÎ4ÖtÆ4„LtævÖjö·“s§¦s^ž<ó4wWÍýÊn²îâ"žÂ^"Þb>¾Ò~2þ²²²A2ÁÒ!’¡H¨= ®¡iå“{éàí¸áCÓñ+‰¸ÃlGD*ÓK2M¶ÏPNwþ"ck%O•ð%éËJÕWôkÌjmê¯î¿æUï=¬!öÆÑÆŒ›·.6]k¾ÝÒÓ:rûEÛÌ/w×ÚA¾“¦‹¹›ó>o`¯èÉ>™‡òýŠʃªCêö kŒh>ÖÕ37~böÔò™Í„ݤãsçû^º¾r{íöfÿ[—)Ûiãw*3‚ïißÿüðîãÀlã\É|Ò§€Ï– J_¸¾¢¾~þöd±ë{ÃRÅráì•¤ÕØµàuŸö¦›š[²Û‚¿ØvHÿÿ8” šˆX€ãÁÛP ‘DÉIÔimé1h3Þd–`©bceÏäX䲿®æùÆ+ÃçÎT @°J¨áÿ¦h“X“x“D£äu©zé«2Wd«ä.È—*œUÌUÊT>®’¨©æ§î²ÇBC[SaŸ_‡[—SKŸË€bH1¢s›p™²™1›ÓZöâ,aË«-ëM› Û-»-û-‡MÇŸN«ÎK._÷Í»¾ßÿÖí¥û¤Ç3Ï ¯çÞ¯}¦}gü>øÏÌÎÍῠΆÍø¾ GÑG Ä(ÅšÜz(1>;¡,±öp Mû &õ'÷¿“Ò˜ZV–~úDjF\fpÖþlË“:§äs„OsžaÌ%çQåc P… pûìÏ¢•âÅ’Ï¥ʦν8?^>raèâ@Å@å`ÕÐ¥áËÃÕ#WFjFjÕ=º:xm ¾ÿzÃÀG£7ŸÞzÑô¶y¦e¶õÓí/m‹w–îþ¸·Ò¾Ö±Ö¹ÖµÞ½~½g½wíÁjßÊÃåþïß¿ }zôqxfdêñ«Ñɱññá'Ÿv?»;Ñ4Yÿ¼úEùË¢W9¯Óß~5å?íüÎdFå½Ð†àãÂìä\Ï|çÒÏi _\¿}SXäÿβD³LüAX!¬Ö넟TøMÜvó µíòÿ²°J\Fjtû&Ì‘ô¼1‹Hü ÀŠ €*€ù' çh;üoüÀ :RmŠ •¦0¶À„€C ©(«ÀMpŒƒ÷`ÂC4¤‡Tˆ tèÔ=…–`:Xv€áËð©é ªQ3HÕæ®@ÄHbb0XvöÂ9ánà©ñ!øa*yªŠp€ð’hDl$q‘2H«d_òµuM­<í-:%º;ôºôƒ ö SŒaŒ¿˜N3 2ßeq`YfÍeS`{Î~ŒC”cŒ3‹Ÿk;š‡›g€rˆWŒ÷5_¿¹N G0EÈ@'Ü/’%j!F+6.^,á))!¹.õPºX&TV_ŽKnCþ•Â}Å¥ åb•|Õ\µ<õ³{ÎiTkÞÒêÑžÐYÐC#{[ÙÐÆ(Ì8ˤÖtÈì»Û^}ËH«Jë [j;cûd‡{Ž«Î.žû \Ü w5XÏ&¯U5ß$¿Á–@ÿ ¶ºÐ°p‰ˆ3‘?£}bÆêŵÄK%T8RuL0éÊqñ”ëiŠé÷2Œ3'²OþÊ)<#›;’QÈtö^q`)GÙ“óyœ+*\zP]V[g{Mþ:KÃNãü­ÉæÁÖûmÝwûÚÇ;gº×{éú$û-#ôŽþx"òÌ}²øÅ³×Œom¦sgÆ?²Ì¹ª]Øøfñ½jygÕm½sSxûÌï÷þy€(šˆÆà¼ÀDWÈň–Ð ‚çàØ‚h ~HѼ¡xD¸ õAïaæƒ à 8n‡¿¢(({ÔIT…ÖA'£{1DŒ ¦ 3‹UĦb'p¸ã¸×xU|~ƒÊêA†PF$ãˆI¤dUr=µ u9 'M)-m(Ý-zmú7†¯ŒG˜h™*™U™GYBX ¬µllË쥆˜¹¬¸v¸x¼(Ì”!Þ4>~Àß%*h&Ä(ôV¸Nä ¨ŽAlL¼DÂë7÷}ÒE2Á²:rlr?äGÏ*S> â­êªæ¤î´ÇEÃC3@+Z;Y'O·Z¯]Â`Ɉl,fblh–e~Ãâ¹%ÚJÎÚÛæ¬í°=ÞÁÀñ¸S—óÎ> ×øý­nkªžG¼z}h|]ýêüÚÕ…`BÝÃî„3GDE>Ž–Ž9»çzèA‚\bÅÆ£IPòá㛩‡ÓQ'Nf²gÕžÔ8õäth.U^MYá÷¢âƒÒås•åöñ÷ª¢/KVÏ×ÔÔ_“©_oèi̽åÓ¬ÞÊr{ëν¹ŽïݨÎj]’†ê†'G ãZOã&n=_z%ùÆgªð]ïû¥YʼÍç´/ß6–T$®ÞÿIÞtÙ®ûË?3 1 ˆ¨LfÀ ø ÊÒQp ”«ˆŠ4^ƒo 1A¢ˆJd AIˆ&t `,[À‘p Ü /£øQ¨lÔ}Ô6Z‹h4ëuL¦Ë€uÇ^Ãnã¬p—p[xüM*zªhªI‚&á‘LŒ%N‘ÌH­ˆòQDM¤N¦Þ ‰¥Y¦¦]¥K ‡é3Ê%̘ۘÞ2ǰP³Ô²³Î³d—gÉ‘Â)Íù’+[‘û=O!ÅŒæ½ËǯÌÿS 9ÿæÂLH}_)(&-¶*Þ!‘)é(%,µ!="sYö˜œ›¼¦‚€"­JiSyCeK V'îaÕÖTÓ²ÒÔIÕ­ÒëÕŸ5ĉ›™„šž6k1ŸÚK²T· µ®°yaÇdoëïøÌ™Õeß¾ó®ïÜDÜÃ=îz¼]}üðþ^A<ÁI!ïÃŒ\ <õ6Æ4¶5NôйÆÄSGˆG³’¨“óS¸RkÓUO dºg­ž<“#uz$7*Ÿ½ çlD1oÉÓ²¬óú ‹]•©—Ì«Y®¼¯m¾šYïÕ ÑÈys»i¦eèvÛk÷ª;®tݸßÑ;Ú77€âÖ{8–÷¤óÙ÷ç‚/]^g½mžžœùñ‘<'ôIgÁýkÒ⥥G?Öׄ~:mfo÷ìòå'/·=DÒEäÇ7;;ËBàrØ}?lVììlW"ÅÆ+:CþÑÖw±´”!q€ñ3Çv?ÿÝþ bD~'½ÿþ pHYs  šœ IDATxíÝ|åý?ðçN'É’5¼÷ˆCa–@hØPVÃjÓ²GàŸ&@ ü„F’ü d{•t@)íïRF”IÙ? 41Þ²ly[¶µîÿ•Ï9Ÿ¥“,Û'û£êå>÷ܳî­X|ýÜswÜ®_àͯ*XüÛþçõzà € ¤ƒÏóúÁ׺uë¸÷.™[|Ùªîìé~¿?1B€ :Žã8aÀè³—Ó ÍˆØ€´$ Šb___? ŠÒòÐèw˜6é§”O ‹Åb0äH@˜ t–Éd2 ~Ÿ—Î:‰¢Ê)'*áñxÜn·£®îßx­·¶^7XÌÇxKeÙÜS~\T^n6›é ”f~ÂÉäꮺšÆÍ¯éê2¿‹û9Î_Z^ròsË+c©^ï¨y÷‹7š{˜Îg@Wd)?ú€SÊŠ¢Ur ¾ôWGMMMyy9ýJÒ¯›ÄÐáH1 %(²‘ò©ØŒ3Ò÷H1r@ã ¿ú¸,šž{ãsªõiwww÷ç[¶ÖoÝb³Z󊊲JŠ™È:[š›»{ºÊN<ùÀ“N´Z­á-HÕkÞ|g³%+;«¤Ä\RN_Á½Mu ÝmícN®<áÔèÕßýdóöš-¶,k~QA^v¾?àomkninéíê[PyÒÑ?89Rõðñ é+@“4_|ñÅ 'œ@¿Vô—ˆÖH1bÖ‰Gî{rß@ï[_¾ò¯ê÷šêZßÛóÇñÇr²jõ)ùiâ '³…2Á×××þùç§žzªÑh”˜fq¾ûî;:딕•E`DúVêÔ½{ïճ鋭ÊÜi±¯æ®+®8ýÉ×ÏÛÏ¡ÈdÎþþÕ»¯~⸭&3Ž šüˆ1Mkc£s˶FÃþ‚®p ßR]íùx' ,ßWÏô Øô‚Éhø~˶ֹûÙíöð#mol0nßTX.TÎ0˜ò˜Þëô9¾¦¿.užæ¬|Ñ07CohÞ¾©}ÖþªÕ›Z>üÏæìKá´ÌL«ÐísîiùrÀ×ßÇÚ­öŒ€`áDFf–«÷>ä@ }(L¡p%%%}ô1”>餓èˆho¢×ÑGm³ÙhæF=¦ñw¼»éíÜ#O›Œ„x½EWeŽP4Ô‰Nk3¦ã©a•“Ô¡¥Ók{$‹êØy“UWeŠÑJµdBÉ<~1ÒOõ|Ã?ÈÈ(äEKO—ᛯøÞnú5Õ×êÜÝzžé3Œ]}ýTlÚ¬YáÃíÙ¹-'K7óÀ£Õ¤·ðBÀp~LSæ:c†×ñ–™f{g[ÛÎmþY³Ã«ÿëÛ úiûå›-Fƒ‘ëdßuúü¢¯_h7g ‚9Ël2}ÛÖLÅfV©To9H_é—T„ƒ:èç?ÿù“O>ù—¿ü¥²²ræÌ™ííí»víš3gNYYYWW‡Rýý­O¬Y³pÖñ³³ƒ§P* ’±\îH³³=T‡‚åý~Š®ÒW2dä!,!{¥Í •„¥º™€€f-ºð·—¥¡¡,ÃX$ðvÏà™àîæªÿC#×{ /pÇæŠÆúÆFÕòÜ{JgØLyv£Ý¤7›tœŽo¡µŒ:+óë­‚Ià†ò>®ujõnÖR1§ ¯0ÛhÔÓW¹ =\[€‰&‹Îà·ù|¾³Ï=Gìv¶¨V׌08ÈÿÈiaÿ‰'žHgš6oÞüÜsÏ-]º”ÖÙdgg|ðÁÔÍà|Õf^=u®XRÍØÃÿý«G¿ŸûØ?®§iN*=xC*ÿ®¿m¸ùuó3,+p½µHKx:kvüéÉß<ùGÿƒ üões²‚±Qû¿ßyøò;¶ â97=°äÔ© ÑSýðæ_xJݪû6ë¯üûæ³»þµiÍõë>,¶òéœ<=¸$¨õßXõ«{(“|ø®ËfZ#®W©ÿç3÷U|þ~­+îzfßËzäìü¿ÝsïÃ[ÿE޼àå—ÍÉÌžwžÙð›MŸã4ËÉ·üuåI&Ö·ëo¯xøeÊ9æò{n>ïp¥äWËL×ÖHð 2ˆá‹Rn @ Õ‚/rLcíê.0èm¦ ³AÐ z^ý}FWúM:€÷yÅoëïììRýe·™¼ö✌\›ÑnL:£ž¯×`‚_ï÷u&#'èí½¾nw›juÑäÉ/ɶ[m½N¯£Ч¿ƒ, ¾@€¾‹F_~‰ØÐÕ®Z=Õ°èñ ä¯H-RsöÙgïÙ³gûÛ÷Ýw_º×ÔÂ… ¥%ÃT 8£þ_!ÿô_þ׿nXàO–¼Ov :ƒ­‰ú@à‹¿ÿfÅ#ï\ÿØßJ•zRb@¤Pà™;o;zÙ¯7œínùêg¶uÏ¢Ùµo-º|Õ¼ Vþþ'?èÿfÛ•¿þ¯êžG(_ xÛ«?ýûªO/¼ñ7k*ÊõÞšg®_§_öÛ¿žRéªn4d{¢ºgÿêž³oÙðëƒM¯­¾òš;òÿ¶ö̇¯¯³f×ßþø¯¿ŸpËÚµ…¥þþ6Vö£‡þ¸"»÷ß]qç3ïÌ’UúƒÏ[ûÔi…¬³¡ÃHÝ|ý÷{V<ÒqçSžéûîÞ+n~¼èO×C§Ñö¾ÂXú¾sQX#D?Ôì­‡ÿ‡´)¼? M&«ŽÖÞÎl³M&Ž®l þ4cÏçvóýýb_¿‘ué#´ÀÓ‹Íl̲è­f]†7LÐ;ò{9ž£‰ãõý¼±Wu† :»dÉ0ëiBG§3èôFžþ¾~ŸÏðuþî ¯ÑЧZ]õˆ 4P†ÜÓEÝtqÓÛo¿ýÆoœ~úéÊ )¦QûЕí?§ˆ±Ê9ûϪ¢õ4~ ƒY>ë©›yçªõÏ2Ý6X«¯®Úá6ò ¶²ò\µèfìÌ]uòtÆ< 4¼ìÄÿï¯ñܯ¹ôø`˜ðßݿdçu¼Róã¥,Xþì»~¿xA)í=ßSPðåîÿ´öÍžqà”#Õ¥:óge÷ùÙ¾'žÚ¿îÍêž…3ü-µM½Lž¯ñ1[YEnðo(jà‡ë_¸i¿¬àx,ó¬ Ê™ßÓg›±ï4áóÁÁìÞñcÜ÷ëËö³P™Â &öí~ñ‘wÌ?ºnZ&Ïû ¦3î­ÿú?GžªˆœBY¾ k$HE½Ç|žŽ†‡ *Âå×¥r†i•§CÈÊæ239«•YmÌb èë㺻XwÐÛãéïÏÈ+TmÁ—™ë¥h$Ó¬3›ø Gó4ÇÐËgàušò¡/ *àÉÌ¥¥‡Áü‘¯|{±Ÿ×2!CG§ªŒ‚¾Ø|70à÷õ3_ Ÿåg«ö>²1lA ½”ÿÈ)ÝÚÚJË„iz†ÖlÛ¶mÞ¼y´¶F>Bea9Sôz{é—nGþi3>}ì¦Oé—ò²ÍÍ‘ªˆžú?^þ«Êu;úšo]8‹æi¨nù>ƒÅ¼:³œ êokúÖpÐÑÙ{ûÓ›sb[·×ÐË؇öK~w}ëòu¿Üö´þ sÖÞxáì\®­©‹±ïn¸ôÃÁÞ*yn_úØóîÆ_ýn‹¢víC/ž:ÃÝ/˜f£Né;€‰=ßÿõá5¿k7›6}ï;:8˜ÞÝ´à?%Ë8T&XŒfsçÞ¶þRéÜc3Œ4¢_7#Yú¡v‚!tòI92¤!í Áê#~LJÇhœ3»µµÅg³²ìÌ–Åè§93x? w/39ƒÑ/è\§aölÕü…s;zþSIwäË0½žÓc‘÷S#*FQÕp¯{S93ÿíúXuFÁ$2õ&£.ø]¬ãt"£¿ÃýfAÞ¾&“!xOn‡8퀟<~[Ïå«î¾A¼ãžK°D˜ÑuOTwïéªJSÁ^J<2ØðÒ Î9f–»öÃõOüËvÎÝe:¿°¯½åéÜ“ãƒí Ó¦»TÏf\-ã©î´ù'Š¿¿oõã³o:û¨,]_CMKþœÙöT»<§6øMáé¥óULìîlû¾îݵ_x2K÷tú+Ëçÿˆý~ÍÍþzï%'å±öÝu¾ý™qÖÒ#¯{òÖç¦ÿî´CJý=®z§8g¿rÞSóÄu×ξÿÊc«BXT<úX¯›œÿþpTH.Vý¤ñÓÕ›Öãkùä_5‚¾Âœ)P@C÷ø¢õ4<ðûj{ÝÍ:½í˜c( Qm¡×Ã2*Om¬Ëe)®ÊúÓiÐ…þÜq8z›ê»ü§ö{¨¶JLC‘ÊÊüUÓk.K·©ÌàE?­g~e3ŸOlmèêlí=¬b!S­ž&þ&b:ßôÕW_µ´´Pø"Ýpoÿý÷§K»üqšª©ªª¢3P4© þa(^tÞ‚»Ÿ¸íý'*Ö<ÿ€‘Ϧ_&úå+<ôü 7ŠWßw×:û=7ž>Gu4Ô"ý5££Ë‚¿ª~é÷ÒæªmXÙ}õoWþß‚õúùò»Ï=€òG–§¨o×Ú;ïlšŸvò]§Í¥2BñOß{Íú›\öòƒ´‡cG¯ûëuƒò{b°Âàoâ¬rT¡ûÁ™ËÊÞ~ðúů覾ä¼ãžþËú;ŠŠïûÉ‘O¬î]uë#×þs#UÊ8üW¿?¨júiWݰÞqÏò? ¶c:öWÏÌ)Éð{5}Gð«‡d™Y¬ÒH@È`¹÷áQ!hM€»û¨’ý¯XuXt½t^^×ìð}ýMAUUŽÝn >ëèoïêlù¾†Ÿ5S,,¢Sût©ux Ru_ënƒëýÂ*{N¡Íl1PHÔ×ëq9ºš¿ïðd)äí½z§¿åÛ–­ùÙ™996³9ƒþVëíëw¹ºœm½s O²ëò#Ur ¾ßÿýqÇ÷å—_Ò$ÍôéÓƒ'DO‹Ð©¨×_~ÝhU =é‰NKÍ;7Ò‘zúz<Ì`1Eš‘‰T/j¾ßÓÓG+äLS„i–ÁÚž¾>šµX+tƒùþžž>º€¦Ž¢ö¶Óß×Ó7ÔZOOÁd1Hf°=Å`‚‡íÍT4Ê¢Úˆ¢<’€€6¸_/(:h…zL#ýiHaM–ÙìÚµK××oÎ4SPâîuûMÆÜCæu¸ÝRÐEz†%í VÏÌhúòMƒ¿1ËJ+a¸Î.n@W\¼ÿ ½ý±T·Ø3¿nØÑ1P#^ú÷{õ¶ŒÊýÊæ÷töF©®MnŒ ã šºº:ºŠ›b—àš¶Áåô{G úI/é·•vÑ3Ú¦M›6¾^P €@Z pך¿à¦G"ÍÍÒ×%])J¡ =kFš¡oUúÞ¤x‚NíÓ7i”›Š¦¶z¤ƒB>ÒN€b kè§Ð(ÇO9´I»¤LÚÄД>HCSG@hî¥;ØE\ÎOß´×ívÓ߈ÜÈßž”)}‡J"y¥¶z¤Q!é( z†Wõ@¢üF«–G& É! Ôvyä¿ð¢• ®§ {ÅR—*¥¶zب‘@€Àd ˜&8¡=Ù Ç@€Àà¬Façç_M±£ÆáB€ 0Ù„~oðŒÒá//˜lG†ã @ }Úope¯É÷x?:k;?g=”/@€ Þ¼·¥>½£‡ @t·¼J‹Ê-€§‚LkI.½§Â‘â!@SA@(ÈPÜA|ðˆéŒTšyì«‚äh†y®4=^ € Y@° ¼¼!'æmœ/§Ó%±kñŽx õý›ÔÔ¢?W›h€ „ S¿9M΢œ„v߯—²„}7†&inFšª‰>I#7ˆlÆ ‹¢€  Só~{ö ÍûHâW†ÈË“4r>å ¬‘5€ hS€žë«Í%pTÐÈ­SZ¹)ç+h”HC€´)ÀG8õ4Êhk­¥„æww^]Hoi˜rd£œ¤¡PÑŒæ?F € 0$ DŸ¦Ž]èê¨ý¸‚•E33bÁ£Š;«b)™ä2Ò¬ŒÍH ʤDH@# aM’? t@ŸÅ4\ôšRhè´ýÕå¼µ©â¯1E*Ú h"©âHÇD‚B> @@³‚8JH34r>ƒÏ=?ÏýHOÈ‘x<εMÌÇ¸Ãøâ»J…œàMüäyšî÷»Ûheß3VÀ¬¿°gÿ$x9•`·›^êgn¦;N(º½TgV¹¤<¤¯qo*'`.l…&f~¿ñÝq7ˆŠ€ hM€1¦ \/´rû…F@Žk².È-ÛVYºµB¨Ô7¯o 9ÂöÛ[³.Ï){oZñSe}_¸¥½-Ï4{¾ö?WVº­’3rÎBk…42îMŠf”²ËMoeÒ€ ¤¯Àè×r/©É`ùO…jÅ‹{OEYáUEõ§Õ„`ÌÛêõ·ûôEú’Ûˤ½ý/¹ .Ñêi³àš¢ÆsêØÊÐzßVf¤éD3çE € M PL}•0ZO3h{ÑÕº¾¹ü‰iÊpæv=è¿Ù€2{8¿¦ÐõTKïcÝÌʲWäZ±÷5³æŸ5JzŠ"„5IWG‡€  ГBO'©öÆùœŸåÖ?ºž¦õæfûòlËV>SpŽ §1íg.û]%N=t·ßÕjÝ<Óä³âgËôÁyšÄ½h©¯êT õ(4XR“8|´ @H²@¬÷Ü£õ4í/µ±¡sGŠAö1ÎÀÓÛÛèq¬jPìJ6ÜR׿§ŸùÄàp|C™¦Ÿg:V5zê=¢_ìÿOÃʺðŠqɉý ¦ØKÆe`h€ ø Ñnº7´ž†fsæ°üß„®§Éº3·c½«c…‹²Ì‹­½[ºCÆg9Öê\ÑÄ(h©b9«ó¤½y—´nt:®h`NƦ1û/²Cj%bsDÔru°‡E;ÑÚ„ @ £Üs/Òmfä|Ûq6zËÏ=o(j‘ ØOÉ¢·\@Jp<Ë¿¬€]’ÿMå¹'JkFö{É‘õ°@€€&b\N£‰±Ž{QB™6c/R›€ ¤V€§sJxA€ tHàÝ{Ó㇠@ "Þsoé!KÒè00T@€¦¸€Àq*gŸv-ÞÁ¾›â28|@€ÒI@å>‡¿¼ Ž æ±Æ¾þ7ö’1wŽ‚€ $V@íö4‰í>Ñ­ËщœˆÔ£\@ND*‰|@€4(@÷åyO4†@€Bø@1Mˆ 6!@H?XŸ÷”~G†C€ 0•ø€?0•ŽÇ @€ÀäÀ¹§Éù¹â¨ @SM€±žfª}æ8^@€Àd yšÉxX1SkI.½c(ˆ"€ ¤€Ê³>:k{ \mˆ±ß-PŽf(‘×èRk y€ ¤“€ú=÷æmœŸN18ÖàóâôÚüÂL©¥“ÏÅ"âdŠf @ X„ ç,ÊIp×ñl>øÄͱ„47#MÕDŸ¤‘‚D6ñü¨Ð @ 1<Ç«<Ã21}i¨Õ– âEÝô“¡z×_[ó.Ì)ZxUQý5yúÞm=e*BöjaSÍ„ŒgÞÇÁà‘M 6!@i*0ú¹':°‚+‹ÜÏ÷Њ™ƒä.kqN×mí¶Ÿfqº³8!%S²©ÐPƒ8&%:… $T€®åGí@ÈL‹2[žv†”ô»íO·e­ÉéÜÐñ„ÖÔ&ÂM}  @`â±Þq/÷Â<ÏÛýž¦¡‹Ÿ¥Ž¿m°c§¥6¦Ÿe:îmœøhâÛB”¥¾8ñ_j´@H¹/†žPRoäm˲[j–w·¿Ü&ö‹Y ³)'ï’‚@[ sk§¼W#‰(aMÈc/R›€ hA@`1/ƒ±ŸšÕõ|»4èþÝýÝOu–ü¡\Ú¤ûöý¦´iYùàL}ÞèëŽSrä#¢–µƒC87%A§€ Ä_€î#­ÑŠUòn*Yñ‡*)‡.†ªx¥JÈ_(”©øŸ*­4ÊeÂÊ´|PrB¹W™– @€€–x15¨ÑòØcÛˆš¨µb/µì„ @ Ùt˽ÉÓ$ýA€ tž‰£>!éƒB‡€ @`Œ‚.½ò–²dŒM¡8 @H™-òU9÷´kñö]ÊÆ„Ž!@€ÀX†/\’kþò9=™±¯ÿ½ädòÁ±@€ÒZ@ˆõFÂéy”rt"'"‡\@ND*‰|@€4(@×=á@€Ò^€ža©²ž&í @€ÀàýÑŸË=ÅLp¸€ ¤ŸÀä^N“~ŸF @€ÀøxÌÒŒµ @Д¯v{Mƒ @£ 𩺞¦µ$—Þ£ ¡ @é „ÏÓ|tÖöt¹Êc¿[ ÍP"¯Ñ¥Ò² @H+çuáž·q~x¦Æs‚ÏsˆÓkó 3¥–pÿ½8‰¢@€@ÂèÙê«„så$¼óøu|âæXPEs3ÒTMôI)¸Ad¿ -A€% ˆbšDu¨víš÷‘FruaȈäI9ŸrÖÈH@€´)À³©w574ò‡Ai妜¯L  Qj @Ц¯Óië‘Oµ‡V'GªóêBzK}É‘r’†BD3Éù,Ð  @`â14µ?­¦w,%-"‰e0ªe¤Y9š‘#J„4Ru„5ªŒÈ„ hM`ô{î¹?s3Š|xæþÜ=êè+vVZFƒäGâ ~F @ º€Àñ£LÕtlj3‘I+‰;7µ›4KÍÑ|Œåf[ϳ]ÌÉØ –G‘i¶‰vQ¾ÖP"ã sÿÝÌȲnÍØÓß·±—XÎÝù–Ã-TÒÓàq®m |`>ÆÆßU*äÐEX y)'`æÕ,ìcú®ÃT.bOH÷h€ Ä ð<ÇEé%à}[½Y?ÎÎZ˜íŸ$zp IDATÝâ¡M¹pß.wñ3eeoWfonYíóå„¿Ý_öz¥õ—öŽ[\>—¯ìµ`ºm}‹TÀqmCÖ¹eÛ*K·V•úæõMrÅ8&(šQ4Ê–ç}ì§·2i@€ÒW`”Išî·»¸}y}¡žÞ”è~§K>Ô¢•%ú=oâó.Îg_ËÙÉ‚Ë i¯}a6`Ëö¦÷ ¨x±Êr˜…7ò:‹®ðª"ÿvßpÍ8¥T£šžÁ Mœ€Ñ  @@C£œîéz¥Ãz†]¯õt[÷+ö‡6u¶¡s7|¯z©oÎ §÷NôÐ2׃Nñ‘"žä¿(¬Á$MòÙÑ# @ q´ž&â¹'_›OÜèú ½ëöö¡pŒ2ã²ð¥õæfûòlËV>SpŽ­‰ûAÒR_Õ©êH h0aws4@H•€ú‹4šŽWÛ……zZó+¿…3ô¯ío&8ä>Æxz{=ŽU l,Rõد`нd¤¾@€@ „(K„Ý›zro(PÎ~Z¶k­“]˜¯Ì_:ëÎÜŽõ®Ž.VÈ2/¶öné_;±×µ¬¬wnìµQ€ M LÜ»Â%lœ© ÉËœ—™ù|03ä>4òfx"¤°\ÀvœÞrû¹çåIi¹€¼k" å¹'JkF¶{É‘õ°@€€&øÈ!&Æ—AD eBÚ½dHElB€ ZhëiR;2ô@€b §rG<÷{+( @€R+ pBš¥‡,IíÈÐ; @ˆ]@E•ûÓìZ¼ƒ}{#( @€R, p,4¦9üeé))YÜ»}ýoì%ã>H4@€Àø1áäÓøÚÓX-9:‘‘(‘J"€  ð‘ÖÓhp¬ @ˆ$€k¹#É € tàY”‡#¤Ó`¬€ LižCL3¥ÿàà!@“D€½êi’€ ©%Àóˆj¦Ö'Ž£… LNžñ“óÀF=ªÖ’\zZ  @H !|=ÍGgmO‹¡‡2ö»ÊÑ %ò]áM!€ ôT‡;oã|Õ|-gŸç§Wû CQNöLäÄÉÍ@€, p:õUÂ9‹rÜu<›>qs,¨¢¹iª&ú$Ü ²‰çG…¶ @‰à‰iWã­]_²ÿùFz‡Sž¤‘w…çÈ»€ @@#|€ŸÌÏ{RU¦€FΧ´rSÎW&0O£Ô@€  M^ŒÇµÜµ‡Vkóð¢ŒÊ±®‘ÞR9²QNÉP(ƒh& vA€4%ÀûÅÑçijZMoM{܃‘feähFŽl(ÐH] ¬75*B€’)À{G{6‚û3wð6ú‚ ÄE@ð²Qæi:6µ™ÎȤR›ÚÍš¥^Ññß ¾Í^ffæK,òP< çÚ¦ÀÇæcÜa|ñ]¥BŽúåâr•$$”0h“z\üE:]Ø•%t@Hk¾E»ò‰bßVoÖ³³f{·xhS:Z磎@»¿dSyÉ_Ëû>ž¿q\ÛuAnÙ¶ÊÒ­B¥¾y}Sju(šQ4ÊÁPp#Ç7Ê|¤!@HG~ êzšî·»¸}y}¡žÞ”è~§K:HÏëý+Ši†Þ+Šä#¯x±Êr˜…7ò:‹®ðª"ÿvŸ¼+ù Õh†¦g0C“üÏ=B€-0ʹ§®W:¬gØ¥AXO·u¿Òa?qp³…éK R¾¡l(A›´øÆõ SüFd‰ù„Ú§°“4De@€€ÆOäùÚ|âö@×í]··•âe—Èä3oƒÇPŒfh ÜFëÍÍöåÙ–#¬|¦.à4[#ïJ~‚–úªNÕÐH¤€6ÉÿPÐ# @ A¼'ò¹§ŽWÛ……úŠUò[8CßñZ0¾1œ’á\ßäk÷Qˆã\ë\ã <½½Ǫ†áü¥b¿‚)ö’):t @€@4ÁÏE¼îɽ©'÷†emûiÙ®µNva~Á/‹«Ö±ÌàuOîz¤bYwæv¬wu¬p±B–y±µwK·²zjÓ#¢–K‚c¹vMjG„Þ!@ˆ›€àÒ°Š¿T…ô“9/3óù`&oàJî*cwíÝI¾”²g£÷Þ\–{^žœNIByî‰Ò#š‘нäÈzØ‚ @@Sâ–QB™!ö’!± @€@j¦DL“Zbô@€@øÈK„“Ð;º€ @ñüÚYzÈ’{ @€4' ˆjÏ{ÚµxûNscÅ€ @€@$!À¸}‡¿¼ $grlƾþ7ö’“CG@˜B •OdJ8 ȉH]ÊäD¤’ȇ @@ƒ|€ã58, € ŒI€šÈ7ÝSK( @€R'À3.t=Mꃞ!@€À80O3N8Tƒ @@SXL£©ƒ @`œÓàÜÓ8íP € íð hg4ÉIkI.½“Ù#ú‚ @ qBxÓµ=<3-rb¿[ ÍP"¯Ñ•G‡AB€ E€ž rîiÞÆùQêhsWðyqzuÜØ&µ”u_NœšD3€ $V€ž ~šœEéôŸóà7Çò€*𛑦j¢OÒHÁ "›ÄþDë€ xLÑëžZn4ˆuÓ;ÜPž¤‘w…çÈ»€ @@#S1¦¡€FÖ§´rSÎW&0O£Ô@€  Mž×óžj­ÖæñÄ>ªüû<ô–ÊË‘rJ†BD3±{¢$ @ µ<Õ×Ó(‡UûÓjz+sÒ7-ÍÊÈÑŒÙP"$ ‘ŽaMú~Ö9 L)žF‰iÜŸ¹¡â™ûs÷d¥‘CéÇLÖÇ@“X@õ–›ÚLgdÒÕQ›ÚÍšC,< çÚ¦ÀÇæcÜa|ñ]¥BNðž7trÊr³­çÙ.ædlË¿£È4ÛR7i›Ê ˜å^ê×¾ÕÔé“6t@€@¢x‘‹6Oðˆ¾­Þ¬gg-ÌönñÐfÈ€×6d][¶­²tk…P©o^ß$èÛå.~¦¬ìíÊŒãÍ-«r~2Í(eוå^z+s† @ }èÙÑ^ÝowqûòúB=½)ÑýNWH銫,‡Yx#¯³è ¯*òo÷ÉŠV–è ô¼‰Ï»8Ÿ}-g'/¡ÍÐô fh’÷ '@€@²„@Ô5Â]¯tXϰKƒ±žnë~¥Ã~âЦ”I«m\:ÅoD6:dM'eñ´9toj·)¬Á$Mj?ô@ˆ¯­§‰Ø ¯Í'nt}ÐÞu{ûP!ŽQ¦´bFÊi½¹Ù¾<Ûr„•ÏÔ܆ck"6—ô´ÔWuª†"4˜°Iúg‚!@‰࣬îxµ]X¨¯ØY%¿…3ô¯ío¤!õ1ÎÀÓÛÛèq¬jHÔ0ÇÛnìW0Å^r¼cA=@€( D9+äÞÔ“{C²sûiÙ®µNva¾œ™ugnÇzWÇ +d™[{·¨ ÈC}ýoì%#÷†=€ $U@à’Ú]²;“£9ir9©$ò!@РÐà 0$@€ 0FÍ=]rŒãGq@€ ÀµÜøw@€ÀdÀ=÷&çˆc€ @€ˆXQƒ€ ¤½Î=¥ýGˆ€ @€xŽ›¢aMkI.½ñ€ É!@÷=÷ôÑYÛÓôØb¿[ ÍP"¯Ñ•¦Ç‹aC€ PL#§‡ó6ÎÞH“Tðyqzµß0åd¯ÁDNœLÑ  @ ÁB „så$¸ëx6|âæXPEs3ÒTMôI)¸AdÏ mA€#@‹i"5‰éO#­ºæ¿Á•=GïðñÈ“4ò®ðy€ hD€®åV;ù¤‘Ñ%fÐÈ SZ¹)ç+˜§Qj @Ц/RÓÔZ*—ܧÐ[ê]Žl”S2Ê šIÕ§ƒ~!@c8.â¹'ŸË׸´®øÑ2}¡^j×ßíoXR[xo‰±ÊH9Ýïvµop±Æ*Yö5yÖ£¬”©¦Tì¬ëÈT^š•‘£)A™” h¤ ¬IÐf!@ñ¸ÈÓ4B®½"ÏqGC٣ӤȧiUƒ}YŽи¿p·ßíÊYo>(ÓýYoÛm-ºu:óþf9|¡àFNÇwÐqoMq¤–ÇÄ] B€- D_Nc=ÂÚ»£Çõç–¼‹òÛþî û‰viLm϶Z¯²[³Ð&ýôþÒÓöûVóºŠH#}bóƒMžMÌÏÌK-Ô`¤’qÏWNÀL«ðQûvÖü}­÷ŽÐ  @©àG½ì©ðª¢¾öv¼ÞÑ»­§ð¿Šå>ñ[²É›Ö£mOýòfxÂùx³w··øù²ÒM¾foxDäP4£ h”]Pp#Å7ÊL¤!@HS^T½éžâh8ËZœÓu[»í§YœN±ø¦‹é²†§:tvëRT Kü£¯àÆ"Zš£³êŠ®/ Ûÿ Õh†¦g0Ck´@Hµ/F?ùĘßhº-kMNç†6Z#<<`ówÏãH/§Ÿ ÏÚìÍUþ¿“Ê ÊŒ¦Ö¤]C€!ÀGY#,õçømƒí»íx»ég™Ž{åAðëºßž™¡k (GÞ«’(`žzJ~²¢,õ•Î:!²I˜=† $[`”‡r·¿Ü&ö‹Y ³i\y—Ú[;¥1æ,Îë~¨³çãÑ+öîìé~¸“r¢ ßxºÉyŸÃÛì¥ÉǺáØ(J•‰ïŠÖ„4{ÉŠØ„ @@ ‚¨X!2 þÝýÝOu–ü¡\ʧ˹‹~SÚ´¬Î|p¦>O0dκ5·í¾–¶š–àýinË5hiA¹Y°¬ÐqSÓ¹õ”I×=)w%'="j¹:ØgöšäôŒ^ @H¸€ùö4,cŸŒŠWFÜ+B™Šÿαk£w¤1†Üœ†Ö¯(a+F)3b_<6”Ë„)="¬Ù~ì%GÖà @š 5ÂM $‘ƒˆÊ„t{ÉŠØ„ @ µ<‹rò)µCCï€ @ f>òrš˜Û@A@€ jˆÏFXzÈ’T ýC€ XÔ×ïZ¼ƒ}k(@€R. p,ô5‡¿¼ åÃJÄb_ÿ{ÉDŒmB€ 0z`S”«¹ÇÑ ¶ªÈщœˆ4>¹€œˆTù€ hP€Lk¹5èŽ!A€ _žá§øŠ¢5@€R!@×rOæsO© EŸ€ ¤@€ˆiRàŽ.!@ˆ¯ÏÑ£)ñ‚ @i.@r#¦IóÏÇ @€ÑÍi&ÿ#,Õ?çÖ’\z«ïC. @é& ršÎÚžnG14ÞØï(G3”Èkt¥éñbØ€ ÈêÏF˜·q¾\"]Áç9ÄéÕ~ÃP”ƒûïÅIÍ@€. ÓP·9‹rÞyü:>qs,¨¢¹iª&ú$Ü ²‰ß…– @‰˜¢÷§q­˜Î]`§w¸«YŒ€¦¸€ˆzƒšþÝý-¿rخͲ®¶qF¾ÿë¾¶?¶¦uL£úyË!Ž´qŒª2!@Zø¨Ï°t=é´\aË:=[:óAfóARºöÐjór‹ûÏ=ÌÉ*vVy<εMÌÇ¸Ãøâ»J…ºù }bóƒMžMÌÏÌK-yå3ÌùpÓÀKýÌÍtÇ E·—êÌtGãD½”0h£nîglñétaW¢hÐ. @`²ð\ÔXÂÿ±ßvBÄ3M_ö—ü¹œÒp\ÛuAnÙ¶ÊÒ­B¥¾y}“Dä|¼Ù»Û[ü|Yé¦ _3¾ ¾Zžiö|í)~®¬t[%gäœ –öÆñ'E3Ê€FÙ27R|£ÌD€ 4 ûÓD]%ÜÉtYÁézÑÄŒ”‚J\W$dí­x1Ù_FVxUQýi5ÒÖÀ?ú -Ñêi³èú)³ÿ%wáÃC™×5žSÇVJ{âùS5š‘¦gÍÄmA€4 ˆÑŸËmgþŸtI eäȆ¯ÏF*ÒËý™Ûõ SüFd{³¤ÿw2C™adcͬùg¡™IܦÈaM½Ñ @ áBÔYÆÏÓu½Ý•óÓÑ—ž´ÞÜl_žm9ÂÊgêî@ñCó4¬€yê=ÆJãˆCÉgÅÏ–é †C¢{ã´AK}U§j¨y) Á’š8I£@€@êx=š;ò+ï=wv¼Þáï ˆ^±wWoIJ}Œ3ðôö6z«äbÆÓMÎûÞf¯¿ÛïX747cúy¦cU#Å:¢_ìÿOÃÊ:¹||±_Á{ÉøŽ­A€ ºî)Ú+cVFÞý…®‡œ]w·ÓRÜ\.k]®j…¬;s;Ö»:V¸X!˼ØÚ»¥[*V°¬ÐqSÓ¹õ´I×=I™y—´nt:®h k¦Ø4fÿÅÐuUª-Ç+sDÔrI°Õk×Ä«m´@€@Šèþ4ÑÏ>1óþfócÓ‡)¯–vÙŽ³Ñ[.–{^ž”æ®xE [!ï &èb«üË Øe#2±¡<÷DéaÍÈþb/9²¶ @Є?ÊuOšäD%” i:ö’!± @€@jx‘S;ô@€&.0ŸË=q5´@€€Ö"^˽ô%Z+Æ@€" ¨ßGx×âì»HU@€4' °°ûþòÍ 3Š}ýoì%ã1.´@€@&Næ5Ârt"'"™ÉäD¤’ȇ @@ƒ<Ï48, € ŒI€n#<™çiÆd€ ¤¯@ôG#¤ïqaä€ L-ž›Ôëi¦Ö‡‰£… La)ñl„)üùâÐ!@SE€ž0Ê3,§ŠŽ€ t õ4S4¦i-É¥w:v; @Ã*ÏFøè¬íÃûÓ*ûÝåh†y®´:J € AõÔÓ¼óUÊj;+ø<‡8½|¶TjéšKâÔ$š @ ±B¤æsåDÚ¥Áüà7Çò€*𛑦j¢OÒHÁ " ~â @ D€®åžŠëiŠ®/Ùÿ|#½C8hSž¤‘w…çÈ»€ @@#´FxÊ=YŸÒÊM9_™ÀÂRX£œ³q>ÞìÝí-~¾Œ7ó-O4'Ü1ö¨ŒfBª\øH0¸Ad‚M@€@š Ð<Íx®åøG_á£%úB=ö¨× ¥ŠF5 ‘‚) IÕÀÐ/ @qàÅhËi"wçd†2CäÝZ߃é­B @`Œ㛦a¬€yê=cì+ÙÅ£,õʼn§dè€ `º–;¶2Ù@Í€\ÔxºÉyŸÃÛìõwûC.’Ëh!%¬ ^ì%C*b€ -Œ‹éä“é’Ìæ Y?“V ,+tÜßÔtn=]÷¤…#u ʨåš§Ë_:j%€ @ =x.-¦‘¯uÊ_RPñ^•¼É \ñŠ’ŠVÑ;ï¡ ¹å½Ú9tå2ae:|„ʽÊtxIä@€  AžctÛ½IþRÎÐD?ÔØKFo{!@H²?Bš$“¢;@€R ÀO½Çr§@]B€ h!Ò£–²$Ñ}£}@€ /Aõ–»ï`ßÅ« ´@€.@1Mèëð—„fMŠíØ×ÿÆ^rRÀà @“A€Ë+ÌxÿÏ&k3>" @` ´ßàÊ^“;îýè¬ít-7^€ @ í&ÿÍiÒþ#Â@€ ƒí.Â1ÔG@€  ŠiÕháƒÀ @˜­§ÁŠš ¢2 @ZÀa-|  @˜ºëiZKré=Q?Ô‡ @@¶œ†®ðÖÆØÆ<ŠØï²#G3”Èkt¹'T€ @@cBXHà¼ó56Îч|žCœ^>[*µ„ ÇIÍ@€. ÓP·9‹rÞyü:>qs,¨¢¹iª&ú$Ü ²‰ß…– @‰˜¢ëi\+¦sØéî*OÒÈ»Âsä]H@€  ©xÝ4²>¥•›r¾2y¥Ò€ m ðÜÄnOS{hµ6lÔQå®ÝCo©˜Ù(§d(”A43*# @€4"0ÊóžÒ7d‰ä+ÍÊÈÑŒÙP"$ ‘Z@XIù€ M ðx4}rˆ#}6ˆc4õoƒ Ä" 0Qõjîк踷Á·ÙK;„SôE7–ò†¡³Vmsõ<ÛÅœŒÍ`ùw™f›¨ MðXn¶…燶›”måLàÆzê³…žŸ›Ÿ”ÎÑ  @ÉàÅØÖÓ8nò·øK^.§·ßáw>âG×·Ë]üLYÙÛ•Ç›[Vž/WLB‚¢e@£ìñüÿm¡·2i@€ÒW ÖsOž7 V 9½ n(ò¼Ñ/sÑÊ}ž7ñy糯ål)¸D‚SªÑ MÏ`†&Áðh€ ˜ŸËÝÊ eFi€†2k«Î¦“6øŒR¤üáš©K!¬I=z† $D€g1^ÌǼ ižzK‡‡?FYê+uBd“Sh€ ^Œm°ád£sm“¯ÍGoçZ‡á”¡9›TŒy }F kBZ‰½dHElB€  !Ê èZ'¶wÁ¯Š÷66žYGå…“õ¿,ŠRQ›»”QË5CClÐæP1*@€Æ*qžF °ŽWÚØ¬¡y#_ò벊÷ªèM Ú”vTì¬Rv)oÊ ÕbÊ* M+— +Óá*÷*Óá%‘@€€x.ÂÅÜuó«{žëÊ»¡Pƒƒë”34ÑëÆ^2z;Ø @€@’öž[ ë6d¢%l?2 @€€†øWÙãIDAT€†ƒ¡@€ q \„>-=dÉ8›D5@€ tõsO»ï`ß%},è€ ŒW@‚åá//okš®ûúߨKjú€18@€ÀTà²s~ðùdc¦ÒG‰c… ¤±@û ®ì5ãLÁGgm§g#¤ññcè€ @@ñÔI @€ 4àù&jÒô³Ã°!@zÄÁpR€ @ èÜSø•Oix2 @S[€#Üsoj³àè!@H3ž›ª>µ–äÒ;Í>. € *÷¦+¼#ÖzvìwÙ‘£Jä5º´~` @£ µõ4ó6έ¢æöŸç§×æfJ-|.ž'S4@H°€ Šê×rç,ÊIp×ñl>øÄͱ„47#MÕDŸ¤‘‚D6ñü¨Ð @ 1t-÷T¼îɵb:wÞáªò$¼+9;±‰(aMHDZ— ©ˆM@€´ ŒvÊ(x“<ÐX.äィå.Gý5¬Y.µõl:Íd9Öê\ÑÄê«b9«óä6“–µR¿''­ot@€@‚õ>ÂáAŒœ#'¤Ê›tEwÅsÃ@åüœÐæ•Ë„)=2¬Q8ö’#ªa€ mÐzšÉÿŠÊ„|ì%C*b€ Ô L‰˜&µÄè€ $ð QM˜Ñ @ "{ZzÈ’wæ!@€@Üh°J[»ï`ß©ä# € hS@Öá//Ïœ9±¯ÿ½ä$`Á!@€&‡€0|£ßÉq@#BŽNäÄÈýÃ[r91¼)@€4/À‹QïO£ùñc€€ @ (ï‡=A€ ¤B@Ÿ¦ùè¬í©Éøû _d;Á>þæP€ t½Ù9á6ÆÖ€ Ô06kÖ¬±5“ºÒÿþ÷¿S×9z† @@+‹ð\î´ˆ^xá…sÏ=W+– @©àbšÔ =C€ 1 ð\ø‚š17‚ €FÔZ=b{ÂqopÂ#½tóèG…а€À󢆇‡¡AcÿSZ±³Jµ²Ïåk\ZWüh™¾P/ðwû–ÔÞ[b¬2RN÷»]í\¬†±J–}Mžõ(+eÊÍ*یԅ²Œœîþ »ã.ñS‘e0ÝQBáuÅBNèm?‡{Ñ1¶W°²(cf†ÜB”UÓ`›ê~¿»ãY—øÅàðË‹ôC>ra¹9!ïBH¹ÝŸfBchnn¾æškæÍ›·ß~û]pÁo½õ–ÔÜìÙ³ÇÑîøj£#TÀ$ ÿ´Gÿ¯»+d¯ÈsÜÑ ŸynZÕ`_–#4î/Üíw»rnÈ/{wýl¿»Õý¥›¸¤f¥–•éØ%;þÔ–uqn雕%ÿSÎ[ø¦[êUëJ—½]i>.Óyk“j™ðLi`áù±çP¼e¿ »tKEé«•†Ʀ›ÂëN¼—ð6‘ÄK`¢çž–/_^QQñÆo|òÉ'×^{íóÏ?¯‘¡@ qÖ#¬ú9Fן[¨‹¶¿»„BÁ~âÐÚžmµ^e·fá ý´þÒÞöûÖ(#}¢c}cí ÕµÇV·þ)Ø`¤Wù#•Ö#­:3OÓ3…×'l"¿ø >÷ütKÒðáI³5Ò|’Te|ÊN‘†&.0ÑûpÀëÖ­£E9”o¿ý–6é§” ôå—_¾xñâ>úèÃ?œ>}úoû[¹ügŸ}öÒK/É…å„\ @ œÀe-Î麭ÝöÓ,N§º˜.kx™‹Î®c]ÑúøG_ÁE´4‡f8Š®/‰Vtï¾¾oû:Ö» n¤öî þ?Åô®?²ÆýXOÞ …Ê]”®x±*8‡däu…eþíCó4ÃÅ2˜·Õëo÷é‹ô%·—Iùý/¹ n¤™/¸¦È»Í3\~d*ØõÑ5}ê-X¡>¼‘Ň¶Æ Ú2!‰ s¯•‡zè8ÿüó{zzŽ=æØ›WÞ\\¬òEðꫯJígdd\wÝuǼÜÝÊ•+srräM$  „¬¡ó/íO·e­Ééø]›å‡ŠH†úµ1‡O^½ëïô³áYµ¡9™¡Ì ¶C=¯wW¯ë×Îüû •F©DÈÀ¤Mš§i{ÑÕº¾¹ü‰iʆܟ¹]:ÅoD6 ÌNç¯)t=ÕÒûX7³²ì¹ÖcGßÌšÖ8\(rŠz÷÷ÒSpŽ­ )`ÚÏ\ö»JZþÜóAwû]­Ö̓1M>+~¶,ü:¦ºÒ¦.“Ï»8¿þ©Ð–U eŽ!Z;ØL@`¢k„害³³¯¾úêÏ?ÿ\ÎQ&èÚ¨‹.ºèý÷ßÿæ›ovíڥ܅4 åºö—ÛÄ~1ka6uwIA -йuèÉ,9‹óºêìù¸Gôн;{º(#4žnrÞçð6{é‚pǺhs!´‚¸óöÂGKBåÀäŽhž&¸ÂwèÜ‘œÍXã <½½Ç*•K“n©ëßÓÏ|bðïµ½'¦L?Ït¬j¤ØKô‹ýÿé§eЇ’ ¿®¨ t­»óGôå>,“ Ô ÿ…;Bx¿Èâ%@çž&4OC ehÍ/-þu»Ý7nœ;w®42‹ÅR]]]UU%möõõ_õõõëׯ4úZ‘Š!ˆ" ‡R"üÔIÿîþî§:Kþ041C÷Ý,úMiÓ²:óÁ™ú<Á|9ëÖܶûZÚjZ‚÷§¹-×| 9JwË ÷75¼0Û¼Ô¥¤ûþà¤KóÙÃqOé;•t‘QH•¡ñsŒÍaù¿) Ù›ug.­ÅéXáb…,óbkï–î–c­ÎMŒ‚–*–³z(Ë»´ u£ÓqEs26Ù æB^–clÍ+ÙnÆìL8R_|Ox<5\ÃtIfó…¬èÌTìÃM Ä[€Ë+ÍxÿÍÏ”¶¦çrÓ3,c|ÞÓ?ÿùOºÖ‰ÖùÒuO‡~ø-·ÜB—vÓ }ôÑÇ{¬¿¿_Z&¼uëVZÜÐÐPTTD1ЪU«¤|ºÊIJHÇRkÔƒ•ž÷¤¼TÏå• @ ës¹Ûope¯É÷(zárK2>xkü1͸ûŽKEÄ4qaD#€ ¸ $?¦‰Ûzš¸[ A@€ »€@AMì¥Q€ $M€NHÅÞ×Dï#{O( @€' ˆôµ—êmfÔ j1o¬çð´x  @cTO=ÑEOçž{îXÚAY@€ +®¸‚®¶ÂïN~itjˆ^!@€ÀhмóÎ;TJà&ö\îÑ:Â~@€ (eË–½÷Þ{¯¼ò uÀ‡ÏÓ$ª[´ @€â' 4ûì³µÊÓÿð‚ @é%p饗J34ÐÐé'<=ï /@€ N^xá'Ÿ|B§œ¤€fh= ¦iÒé3ÄX!@S^@ÐÐC$·oß>´žf‚Ïåžò°€ @ yªÍÐzQõ5Éz‚ @1 œ}öÙò)'y††JS}ÞÏpåSLŽ(@€@ (pùꫯ¤54! ~¢ñœ.…ÃC×€ @ (½&7 DxÜPÝÿ¤ö¸ƒ)xIEND®B`‚doc/example-scripts/tk-horse-race.tcl000066400000000000000000000127441242365656200201360ustar00rootroot00000000000000# A small Horse Race game, originally developed by Richard Suchenwirth # in plain Tcl (see http://wiki.tcl.tk/3467). The game was rewritten # as a design study in NX by Gustaf Neumann in May 2011. # # image::tk-horse-race.png[] # package require Tk package require nx::trait ############################################################################## # Trait ListUtils # # Some list utilities, not part of a package we can require here. ############################################################################## nx::Trait create ::nx::traits::listUtils { :protected method lpick {list} { # return a random entry from a given list lindex $list [expr {int(rand()*[llength $list])}] } :protected method lremove {listName what} { # remove a list element referenced by the elements value :upvar $listName list set pos [lsearch $list $what] set list [lreplace $list $pos $pos] } } ############################################################################## # Class Horse # # This class defines the logic, how and where a single horse and # jockey are drawn. The painting of the horse happens just at startup # time, later the horses are moved via their tags. ############################################################################## nx::Class create Horse { :property name:required ;# name is the external name of the horse :property tag:required ;# tag is an internal id :property canvas:required ;# the canvas, on which the horse is drawn :property n:integer,required ;# the position on the canvas :require trait nx::traits::callback :require trait nx::traits::listUtils :method draw {x y} { set hide [:lpick {black brown white gray brown3 brown4}] set c1 [:lpick {red yellow blue purple pink green}] set c2 [:lpick {red yellow blue purple pink green}] ${:canvas} create oval 0 -1 18 4 -fill $hide -outline $hide -tag ${:tag} ${:canvas} create line 1 12 3 0 5 12 -fill $hide -tag ${:tag} -width 2 ${:canvas} create line 15 12 17 0 19 12 -fill $hide -tag ${:tag} -width 2 ${:canvas} create line 16 0 20 -7 24 -5 -fill $hide -tag ${:tag} -width 3 # Jockey: ${:canvas} create line 9 4 11 1 7 -1 -fill $c1 -width 2 -tag ${:tag} ${:canvas} create line 7 -2 10 -6 15 -3 -fill $c2 -width 2 -tag ${:tag} ${:canvas} create oval 9 -7 12 -10 -fill orange -outline orange -tag ${:tag} ${:canvas} move ${:tag} $x $y } :method init {} { set w [entry ${:canvas}.e${:n} -textvar [:bindvar name] -width 7 -bg green3] ${:canvas} create window 5 [expr {${:n}*30+5}] -window $w -anchor nw :draw 70 [expr {${:n}*30+14}] } } ############################################################################## # Class HorseGame # # Defines the main canvas of the Game and contains the logic of # starting, resetting etc. ############################################################################## nx::Class create HorseGame { :property {bg1 green4} ;# background color of the canvas :property {bg2 green3} ;# background color of the result label :property {width 750} ;# width of the canvas :property {height 330} ;# height of the canvas :property {horses} ;# a list of horse names participating in the game :require trait nx::traits::callback :require trait nx::traits::listUtils :method init {} { # # create the canvas # set :canvas [canvas .c -bg ${:bg1} -width ${:width} -height ${:height}] pack ${:canvas} # # create the Horses # set n 0 foreach name ${:horses} { set h [::Horse create horse$n -name $name -canvas ${:canvas} -n $n -tag horse$n] lappend :tags horse$n incr n } # finish line set w [expr {${:width} - 20}] ${:canvas} create line $w 0 $w ${:height} -fill white -tag finish # start button button ${:canvas}.button -text Start -pady 0 -width 0 \ -command [:callback start ${:tags}] ${:canvas} create window 5 [expr {$n*30}] -window ${:canvas}.button -anchor nw # label for the results label ${:canvas}.winners -textvar [:bindvar winners] -bg ${:bg2} -width 80 ${:canvas} create window 70 [expr {$n*30}] -window ${:canvas}.winners -anchor nw } :public method start {running} { # # When the "Start" button is pressed, we turn this button into a # "Reset" button and the horse race starts. We stop, when more # than two horses pass the finish line. # ${:canvas}.button config -text Reset -command [:callback reset] set :winners {} set finish [expr {[lindex [${:canvas} bbox finish] 2]+10}] while {[llength ${:winners}]<3} { set this [:lpick $running] ${:canvas} move $this [:lpick {0 1 2 3}] 0 update if {[lindex [${:canvas} bbox $this] 2] > $finish} { lappend :winners [expr {[llength ${:winners}]+1}]:[$this cget -name] :lremove running $this } } } :public method reset {} { # # When the "Reset" button is pressed, we switch back to the start # configuration, the horses come back to the start. # ${:canvas}.button config -text Start -command [:callback start ${:tags}] foreach tag ${:tags} { set x [lindex [${:canvas} bbox $tag] 0] ${:canvas} move $tag [expr {70-$x}] 0 } } } # # everything is defined, create the game # bind . {exec wish $argv0 &; exit} HorseGame new -horses {Blaise NX Animal Ada Alan XOTcl Grace itcl John Linus} doc/example-scripts/tk-locomotive.html000066400000000000000000001204051242365656200204420ustar00rootroot00000000000000 Listing of doc/example-scripts/tk-locomotive.tcl

Example by <Richard Suchenwirth> http://wiki.tcl.tk/1329

  • translated from Tcl to XOTcl by gustaf neumann in 2001

  • translated from XOTcl to NX by gustaf neumann in 2010

tk-locomotive.png

Left mousebutton starts, middle slows down, right stops

package require Tk
package require nx
package require nx::trait

nx::Class create Wheel {
  :property x
  :property y
  :property r
  :property {spokes 24}
  :property {pivot 0}
  :property {color red}
  :property {tag ""}

  :public method drawSpokes {} {
    ::nx::var import [:info parent] c alpha
    set delta [expr {360.0 / ${:spokes}}]
    set deg2arc [expr {atan(1.0)*8/360.}]
    for {set i 0} {$i < ${:spokes}} {incr i} {
      set x1 [expr {${:x} + cos($deg2arc*$alpha) * ${:r}}]
      set y1 [expr {${:y} + sin($deg2arc*$alpha) * ${:r}}]
      $c create line ${:x} ${:y} $x1 $y1 -fill ${:color} -tag spoke
      set alpha [expr {$alpha + $delta}]
    }
    if {[info exists :act_pivot]} {
      lassign [set :act_pivot] item perc
      set rp [expr {${:r} * $perc}]
      set xp [expr {${:x} - $rp * cos($deg2arc * $alpha)}]
      set yp [expr {${:y} - $rp * sin($deg2arc * $alpha)}]
      $c coords $item $xp $yp [expr {$xp + 1}] [expr {$yp + 1}]
    }
  }

  :method init {} {
    ::nx::var import [:info parent] c alpha
    set alpha 0.

    set :y [expr {${:y} - ${:r}}]
    $c create oval \
        [expr {${:x} - ${:r}}] [expr {${:y} - ${:r}}] \
        [expr {${:x} + ${:r}}] [expr {${:y} + ${:r}}] \
        -outline white
    set r1 [expr {${:r}-2}]
    set W [$c create oval \
               [expr {${:x} - $r1}] [expr {${:y} - $r1}] \
               [expr {${:x} + $r1}] [expr {${:y} + $r1}] \
               -outline ${:color} -width 2]
    :drawSpokes

    if {${:pivot}} {
      set deg2arc [expr {atan(1.0) * 8 / 360.0}]
      set rp [expr {$r1*${:pivot}}]
      set xp [expr {${:x} - $rp * cos($deg2arc * $alpha)}]
      set yp [expr {${:y} - $rp * sin($deg2arc * $alpha)}]
      set new_pivot [$c create rect $xp $yp [expr {$xp + 1}] [expr {$yp + 1}] \
                         -fill ${:color} -tag [list ${:tag} pivot]]
      set :act_pivot [list $new_pivot ${:pivot}]

      $c create arc [expr {${:x} - $r1}] [expr {${:y} - $r1}]\
          [expr {${:x} + $r1}] [expr {${:y} + $r1}] \
          -style chord -fill ${:color} -start 310 \
          -extent 80 -tag counterweight
      set :pivot $new_pivot
    }
    set rh [expr {${:r} / 12.0}]
    $c create oval \
        [expr {${:x} - $rh}] [expr {${:y} - $rh}] \
        [expr {${:x} + $rh}] [expr {${:y} + $rh}] \
        -fill white -tag hub
    set :r $r1
  }
}


nx::Class create Locomotive {
  :property {speed 4}

  :require trait nx::traits::callback

  :method turn {} {
    set :alpha [expr {round(${:alpha} + 360 - ${:speed}) % 360}]
    foreach i [${:c} find withtag counterweight] {
      ${:c} itemconfig $i -start [expr {310 - ${:alpha}}]
    }
    ${:c} delete spoke
    foreach wheel [:info children] { $wheel drawSpokes }
    ${:c} raise hub
    set xp0 [expr {105 + 15 * sin((${:alpha} - 90) * atan(1.0) * 8 / 360)}]
    ${:c} delete piston
    ${:c} coords p0 $xp0 120 [expr {$xp0+2}] 122 ;#CW
    ${:c} create line 90 121 $xp0 121 -width 2 -fill white -tag piston ;#CW
    :drawRod p0 p1 p2 p3
    ${:c} raise p0
    foreach i [${:c} find withtag smoke] {
      if {[lindex [${:c} bbox $i] 3]<0} {
        ${:c} delete $i
      } else {
        ${:c} move $i [expr {rand() * ${:speed} / 3.0}] [expr {rand() * 2 - 2}]
      }
    }
    set t [${:c} create oval [${:c} bbox chimney] -fill white -outline white -tag smoke]
    ${:c} move $t 0 -10
    ${:c} lower smoke
  }

  :method drawRod {p0 p1 p2 p3} {
    ${:c} delete rod
    ${:c} create rect [${:c} bbox $p1 $p3] -fill white -tag rod
    ${:c} create line {*}[lrange [${:c} bbox $p0] 0 1] \
        {*}[lrange [${:c} bbox $p2] 0 1] -width 3 -fill white -tag rod
    ${:c} raise rod
    ${:c} raise pivot
  }

  :public method tick {} {
    :turn
    foreach i [after info] {after cancel $i}
    after 10 [self] tick
  }

  :public method throttle {} {
    incr :speed 2
    :tick
  }

  :public method break {} {
    incr :speed -2
    if {${:speed}<0} {set :speed 0}
    :tick
  }

  :public method emergencyBreak {} {
    set :speed 0
    :tick
  }

  :method init {} {
    set :c [canvas .c -width 600 -height 160 -background lightblue]
    pack ${:c}

    bind ${:c} <1> [:callback throttle]
    bind ${:c} <2> [:callback break]
    bind ${:c} <3> [:callback emergencyBreak]

    ${:c} delete all
    ${:c} create rect 32 115 360 125 -fill black ;# frame
    ${:c} create rect 22 118 32 122 -fill grey30 ;# buffer
    ${:c} create line 22 115 22 125
    ${:c} create poly 60 95 40 115 50 115 70 95 -fill black
    ${:c} create rect 60 45 310 95 -fill grey25 ;# boiler
    ${:c} create oval 55 50 65 90 -fill black ;# smokebox
    ${:c} create rect 70 32 85 50 -fill black -tag chimney
    ${:c} create rect 40 52 90 75 -fill black ;# wind diverter
    ${:c} create oval 130 36 150 52 -fill black ;# dome
    ${:c} create rect 195 35 215 50 -fill black ;# sandbox
    ${:c} create oval 260 36 280 52 -fill black ;# dome
    ${:c} create rect 65 100 90 135 -fill black ;# cylinder
    ${:c} create rect 90 120 92 122 -fill red -tag p0 ;# crossbar
    ${:c} create rect 72 87 82 100 -fill black ;# steam tube
    ${:c} create rect 310 40 370 115 -fill black ;# cab
    ${:c} create rect 310 32 390 42 -fill grey30 ;# cab roof
    ${:c} create text 338 82 -text "01 234" -fill gold -font {Times 7}
    ${:c} create rect 318 48 333 66 -fill white ;# cab window #1
    ${:c} create rect 338 48 355 66 -fill white ;# cab window #2
    Wheel new -childof [self] -x 50 -y 150 -r 13 -spokes 12
    Wheel new -childof [self] -x 105 -y 150 -r 13 -spokes 12
    Wheel new -childof [self] -x 150 -y 150 -r 30 -pivot 0.5 -tag p1
    Wheel new -childof [self] -x 215 -y 150 -r 30 -pivot 0.5 -tag p2
    Wheel new -childof [self] -x 280 -y 150 -r 30 -pivot 0.5 -tag p3
    :drawRod p0 p1 p2 p3
    Wheel new -childof [self] -x 340 -y 150 -r 16 -spokes 12
    ${:c} create rect 360 110 380 118 -fill black
    ${:c} create rect 380 65 560 125 -fill black -tag tender
    ${:c} create rect 560 118 570 122 -fill grey30 ;# buffer
    ${:c} create line 571 116 571 125
    ${:c} create rect 390 45 525 65 -fill black -tag tender
    Wheel new -childof [self] -x 395 -y 150 -r 13 -spokes 12
    Wheel new -childof [self] -x 440 -y 150 -r 13 -spokes 12
    ${:c} create rect 380 132 456 142 -fill red
    Wheel new -childof [self] -x 495 -y 150 -r 13 -spokes 12
    Wheel new -childof [self] -x 540 -y 150 -r 13 -spokes 12
    ${:c} create rect 480 132 556 142 -fill red -outline red
    ${:c} create rect 0 150 600 160 -fill brown ;# earth
    ${:c} create line 0 150 600 150 -fill grey -width 2 ;# rail
    :tick
  }
}

Locomotive new

doc/example-scripts/tk-locomotive.png000066400000000000000000000455711242365656200202740ustar00rootroot00000000000000‰PNG  IHDR_»Ô—l÷YiCCPICC ProfilexÕXeTU]·^{ŸäÝyèîé.éé.i0@BBQJJQ@PD”•DBPÁQEE»ñú½ãŽ{ÿÝ?w±×yÎ\ó¬½Î~ÖÚsÎÖ"Ïðð˜€Ð°èH#]Š“³ ÷à  Àçé®ceeþ×¶ú @»ƒã’»sý¯nÿóo”72ìååŠà[^ôŒ®BðãƒÑáF!`ŒDˆà»Øÿ\¶‹½þÁ ¿}ìlôŸð$OÏHˆ;%ÖÛ™ƒ¸–>Ì'0 z4‚5½<}`ÕG|$BCìâp‹xýkÿaOO¯¿szzúÿÅÿüä—Èõ£ÂC<ýþòÙ…†Ä Ïëw£GzRXÈÞ]nðÈ5çã©oö‡‡üæì·Ý7ÌÞö=Ìk¯åìihó‡Gëþ [Ùý±Çèíýƒ}£ þÎäijõÇccÿGÅÚüÁñvް¯þ_»_ ¡É{`´Éß{0û»,€'ðŽöÛåè?èMÑAv™¯Å$Ì[J‚"'#+»;üÿ¦íž¯»lóûÜ@Ì#ÿ±E!ÏTm9cÿ±y$ÐêŠlqåÿØø õ“Üã÷މŒýg>d+€@àü@H9 Ô60¦ÀØgà¼A‘à HI dÓ ”€rP ®€k ´€»  <ƒà1x ^‚)ð|‹`lB„ƒÈÄñ@‚8$©@šdÙ@Îä…A1P"tÊ‚r¡è"TÝ€nC]P?4 =‡¦¡yh Ú€Q0 f„¹`!XVu`3ØÞûÃp<œŸ‚‹à ø*Ü wÁƒðSx þ ¯ ŠˆbFñ¢$Q*(=”%Ê凊DAe¢ Q¨zTª5ŽšB- ~¢±h4-‰VG£íÑÞèôt6º}ÝŒîA£§Ñ‹è_2†#ŽQØ`œ0þ˜ƒ˜4L!æ2¦ Ó‹yŠù€YÅb±ÌXa¬2ÖëŒ Â&`³±ç°×±ØQì v‡Ã±áÄq8Kœ'.—†+Æ]ÅuàÆppëx"ž/‡7Ä»àÃðÉøB|-¾?†ŸÅoRÑR R©QYRùP¢Ê¡ª¢j£¡ú@µI #4v„ B¡ˆPOè%¼",‰D>¢*ÑšH…¾’þ>ý ŠŸAÁ›á8CC/ÃF,£0£ ccã5ÆaÆE&z&&¦8¦R¦{LSÌ(f!fæææFægÌ,\,:,¾,,õ,c,k¬¬Ú¬¾¬™¬×YŸ²n°QØ Ø‚Ùΰµ°½fG³‹±[³d?ÏÞ˾ÀÁÈ¡ÎáÍ‘ÉÑÈñ‚æã´áLà¬äâ\áâæ2â ç*æºÏµÀÍÌ­ÍÄÏÝÎ=ÏÃÀ£ÉÈ“ÏÓÁó‰ÂDÑ¡„PŠ(=”E^N^cÞÞ‹¼Ã¼›|Â|ö|É|×ù^óøUøýøóù»ùx,ê^R ªžì\rJjšf6Ž®~%Bщ©y"ŠU ='úX S +‡Å•ÄÅωJ`$T%Â$*$&$I’:’±’u’ÓRÌRæRÉR-R_¥¤]¤ÏH÷Iÿ’Q” ‘©’y)K/k*›,Û&»$'&ç-W*÷Dž,o(T¾Uþ»‚¸‚¯Ây…IEE ÅtÅnÅm%e¥H¥z¥yeeå2å F+•l•‡ªU]Õ£ªwUª)©E«5ª}S—TV¯UŸÛ#¼ÇwOÕž > O‹SšMÍ šSZ¼ZžZZï´ùµ}´/kÏêˆêé\Õùª+£©Û¤»¦§¦wX¯S¥o¤Ÿ©?l@o`oPbðÆÏÐß°ÎpÑHÑ(Á¨Óclf|Æx„ËÄÛ¤ÆdÑTÙô°iÉÌÖ¬Ä치y¤y›laj‘gñj¯àÞ°½-–ÀÒÄ2Ïòµ•°U„Õk¬µ•u©õGY›D›>[[wÛZÛU;]»»—ö"ö1öÝ4®5kŽú޹ŽSNÒN‡Ù[]p..—]Vöì+Ø÷ÁUÑ5ÍõÙ~áýqûûÝØÝBÜî¹Ó¸{ºßôÀx8zÔzlyZzVx®x™x•y-zëyŸõþì£í“ï3ï«á›ë;ë§á—ë7ç¯áŸç? P°¨Xø=È8¨Ô#ôv}XpXÏîqFÃÅÃÓ§"Ô" "#Í"/GAQû£Z£‘Df(F$&5f:V3¶4vý ÃÁ›qtqaqC‡Äeš7Œ¿”€NðNèNäMLJœ>¬søâèˆ×‘î£üGSŽ~8ftìJ!)8éQ²LrnòãŽÇÛR¸RŽ¥Ì¤¥Ö¥Q§E¦M¤«§—Ÿ@Ÿ<1œ!ŸQœñ+Ó's K&«0k+Û;{à¤ìÉ¢“;§üN ç(åœ?=vúÙ­3Wrérãsgò,òšó)ù™ù? Ü ú  ËÏÎÆœ*2/j-(>]¼UPò´T·ôzgYFÙÚ9ŸscçµÏ×—s•g•o\¼0yÑèbs…PEa%¶2¶òc•CUß%•K5—Ù/g]Þ®«žºbs¥§F¹¦¦–³6§®‹©›¿êzõñ5ýk­õ’õ¯3_Ïj 1 ŸnxÜxÖhÖØ}Såfý-Á[eM M™ÍPó¡æÅ–€–©VçÖÑÛ¦·»ÛÔÛšîHÝ©¾Ë{·ôÓ½œvB{JûNG|ÇJgxçB—×L·{÷ËûN÷ŸôX÷ ÷šõ>|`øà~ŸN_ÇC‡wûÕúo¨ ´ * 6)5=R|Ô4¬4Ü<¢<ÒúXõqÛèžÑö1­±®qýñOLž >Ýûtô™ý³É ׉©IŸÉ¹ç!Ï¿¿ˆ}±ùòØ+Ì«Ì×´¯ ßp¾©x+úöú”ÒÔ½iýé¡w¶ï^ÎxÏ|~õ~ëCÊGòÇÂYžÙš9¹¹»ó†ó?íûôásøçÍ…´/t_ʾŠ|½õMûÛТÓâ‡ï‘ßw–²—Ù–«(üè^±Zy³ºº¹–¹Î¶~å§ÊϾ ÇÙ̓[¸­¢mÑí¶_f¿^í„îì„{FzþÎPHûù°T نǨÿÉ{ é1’´Ã¦C²‚NÈ ú \К¬!Ιjš¨A*¦†hBißÓû0|fòfžfõa{ÃaÁÙÌÍÅIiã]âçPÔ2ÖQUç– “DK®IÍI¿’ík”¯TÈULV WÞ¯b¬ª¨Æ­ŽUÿºgB£S³V«D;G'S7C/K?Ç ×0Ï(߸À¤À4ßìŒù)‹¬½',S­Ž[·I±=n—bŸâì˜ätÔ9Á%n_”kØþ@7wo_Ï`¯ï$ŸS¾¥~Õþ7Ú¿™ ][ GEÐG F©F[ÅøÇ>x&®úP[üpÂtâÒÔQÚcìI”dþã|)”TÎ4ÖtÆ4„LtævÖjö·“s§¦s^ž<ó4wWÍýÊn²îâ"žÂ^"Þb>¾Ò~2þ²²²A2ÁÒ!’¡H¨= ®¡iå“{éàí¸áCÓñ+‰¸ÃlGD*ÓK2M¶ÏPNwþ"ck%O•ð%éËJÕWôkÌjmê¯î¿æUï=¬!öÆÑÆŒ›·.6]k¾ÝÒÓ:rûEÛÌ/w×ÚA¾“¦‹¹›ó>o`¯èÉ>™‡òýŠʃªCêö kŒh>ÖÕ37~böÔò™Í„ݤãsçû^º¾r{íöfÿ[—)Ûiãw*3‚ïißÿüðîãÀlã\É|Ò§€Ï– J_¸¾¢¾~þöd±ë{ÃRÅráì•¤ÕØµàuŸö¦›š[²Û‚¿ØvHÿÿ8” šˆX€ãÁÛP ‘DÉIÔimé1h3Þd–`©bceÏäX䲿®æùÆ+ÃçÎT @°J¨áÿ¦h“X“x“D£äu©zé«2Wd«ä.È—*œUÌUÊT>®’¨©æ§î²ÇBC[SaŸ_‡[—SKŸË€bH1¢s›p™²™1›ÓZöâ,aË«-ëM› Û-»-û-‡MÇŸN«ÎK._÷Í»¾ßÿÖí¥û¤Ç3Ï ¯çÞ¯}¦}gü>øÏÌÎÍῠΆÍø¾ GÑG Ä(ÅšÜz(1>;¡,±öp Mû &õ'÷¿“Ò˜ZV–~úDjF\fpÖþlË“:§äs„OsžaÌ%çQåc P… pûìÏ¢•âÅ’Ï¥ʦν8?^>raèâ@Å@å`ÕÐ¥áËÃÕ#WFjFjÕ=º:xm ¾ÿzÃÀG£7ŸÞzÑô¶y¦e¶õÓí/m‹w–îþ¸·Ò¾Ö±Ö¹ÖµÞ½~½g½wíÁjßÊÃåþïß¿ }zôqxfdêñ«Ñɱññá'Ÿv?»;Ñ4Yÿ¼úEùË¢W9¯Óß~5å?íüÎdFå½Ð†àãÂìä\Ï|çÒÏi _\¿}SXäÿβD³LüAX!¬Ö넟TøMÜvó µíòÿ²°J\Fjtû&Ì‘ô¼1‹Hü ÀŠ €*€ù' çh;üoüÀ :RmŠ •¦0¶À„€C ©(«ÀMpŒƒ÷`ÂC4¤‡Tˆ tèÔ=…–`:Xv€áËð©é ªQ3HÕæ®@ÄHbb0XvöÂ9ánà©ñ!øa*yªŠp€ð’hDl$q‘2H«d_òµuM­<í-:%º;ôºôƒ ö SŒaŒ¿˜N3 2ßeq`YfÍeS`{Î~ŒC”cŒ3‹Ÿk;š‡›g€rˆWŒ÷5_¿¹N G0EÈ@'Ü/’%j!F+6.^,á))!¹.õPºX&TV_ŽKnCþ•Â}Å¥ åb•|Õ\µ<õ³{ÎiTkÞÒêÑžÐYÐC#{[ÙÐÆ(Ì8ˤÖtÈì»Û^}ËH«Jë [j;cûd‡{Ž«Î.žû \Ü w5XÏ&¯U5ß$¿Á–@ÿ ¶ºÐ°p‰ˆ3‘?£}bÆêŵÄK%T8RuL0éÊqñ”ëiŠé÷2Œ3'²OþÊ)<#›;’QÈtö^q`)GÙ“óyœ+*\zP]V[g{Mþ:KÃNãü­ÉæÁÖûmÝwûÚÇ;gº×{éú$û-#ôŽþx"òÌ}²øÅ³×Œom¦sgÆ?²Ì¹ª]Øøfñ½jygÕm½sSxûÌï÷þy€(šˆÆà¼ÀDWÈň–Ð ‚çàØ‚h ~HѼ¡xD¸ õAïaæƒ à 8n‡¿¢(({ÔIT…ÖA'£{1DŒ ¦ 3‹UĦb'p¸ã¸×xU|~ƒÊêA†PF$ãˆI¤dUr=µ u9 'M)-m(Ý-zmú7†¯ŒG˜h™*™U™GYBX ¬µllË쥆˜¹¬¸v¸x¼(Ì”!Þ4>~Àß%*h&Ä(ôV¸Nä ¨ŽAlL¼DÂë7÷}ÒE2Á²:rlr?äGÏ*S> â­êªæ¤î´ÇEÃC3@+Z;Y'O·Z¯]Â`Ɉl,fblh–e~Ãâ¹%ÚJÎÚÛæ¬í°=ÞÁÀñ¸S—óÎ> ×øý­nkªžG¼z}h|]ýêüÚÕ…`BÝÃî„3GDE>Ž–Ž9»çzèA‚\bÅÆ£IPòá㛩‡ÓQ'Nf²gÕžÔ8õäth.U^MYá÷¢âƒÒås•åöñ÷ª¢/KVÏ×ÔÔ_“©_oèi̽åÓ¬ÞÊr{ëν¹ŽïݨÎj]’†ê†'G ãZOã&n=_z%ùÆgªð]ïû¥YʼÍç´/ß6–T$®ÞÿIÞtÙ®ûË?3 1 ˆ¨LfÀ ø ÊÒQp ”«ˆŠ4^ƒo 1A¢ˆJd AIˆ&t `,[À‘p Ü /£øQ¨lÔ}Ô6Z‹h4ëuL¦Ë€uÇ^Ãnã¬p—p[xüM*zªhªI‚&á‘LŒ%N‘ÌH­ˆòQDM¤N¦Þ ‰¥Y¦¦]¥K ‡é3Ê%̘ۘÞ2ǰP³Ô²³Î³d—gÉ‘Â)Íù’+[‘û=O!ÅŒæ½ËǯÌÿS 9ÿæÂLH}_)(&-¶*Þ!‘)é(%,µ!="sYö˜œ›¼¦‚€"­JiSyCeK V'îaÕÖTÓ²ÒÔIÕ­ÒëÕŸ5ĉ›™„šž6k1ŸÚK²T· µ®°yaÇdoëïøÌ™Õeß¾ó®ïÜDÜÃ=îz¼]}üðþ^A<ÁI!ïÃŒ\ <õ6Æ4¶5NôйÆÄSGˆG³’¨“óS¸RkÓUO dºg­ž<“#uz$7*Ÿ½ çlD1oÉÓ²¬óú ‹]•©—Ì«Y®¼¯m¾šYïÕ ÑÈys»i¦eèvÛk÷ª;®tݸßÑ;Ú77€âÖ{8–÷¤óÙ÷ç‚/]^g½mžžœùñ‘<'ôIgÁýkÒ⥥G?Öׄ~:mfo÷ìòå'/·=DÒEäÇ7;;ËBàrØ}?lVììlW"ÅÆ+:CþÑÖw±´”!q€ñ3Çv?ÿÝþ bD~'½ÿþ pHYs  šœ IDATxí} ˜Wunï3=šdí‹%Ù–,ËÂ+/?bä5Ž­øü=“—GIXqHÈÂ&±1Á K€ø±|ìçM–¼Û‘ly‘%Yí»fŸéé÷WŸÑÑÕ­êê꽺ûïÏ.:÷œsÏùoÕýûVU×D3™L6›D"ºA5LY[EÏ-¨ëÖ­Û½{÷e—]6yòäh4ŠÃlß¾}‰D¢½½=•JI/ò'rÕUWmÛ¶mÍš5hJìÆãñX,&[ø ”|*’$ƒ"Р`ÌUÀ®Ê?ûÙÏÚrŸt:eÎI`ÂgttôàÁƒ###B–ÐÀS·"¨Fúà–TPãþýû·oßÞÝݰ8ä:„#œ‡ƒ$(}?~|ïÞ½8Vwî܉&ù HQx[ôÂŽð‚ŒOSe("@rS‚3'ˆ[Ì?`ÄI“&Íš5 ßÅ1ÿà#‚Ñ6>>ÞÕÕv„Ј•3g"@ˆ ¥!0<<Œ‹RøV=oÞ<Ð$¾”G‡††p kpp„‰-˜’ìX¸ô"D€FD¤ˆKP`D\”Âvîܹ ĸp``T‰«UX86baÌ™"@ˆ@É€ÇÆÆp%uöìÙ½½½ØÅ•U‡±^kÆ´)Ó3㙇öîß»¿ÿØà .¹ðM—æswçC ðD Ççž{¿Á‹¯k°‚vÔ/ŽýýýO<ñÄ›ßüfÏ ¦òØÎ—_ëïxÃÙóâ¦6ÜòÑ/oíïX~ö¼D¸ódvD ª8p`ñâÅÎóàEŸkª˜žß°~Ï}÷.èì\:kæÙ—^:ÿw®ÈfÆwÜ}÷KëÖ½4:Ü{ß½±xô¼‹/ñ¼Ü÷®küžÓfMš¿lêô ~ë´ ÖâYûC?»{ß¼þÂÀÁÇïÙ‘ˆ/XuY>÷Ç7=øÄk÷NÛ}ú¢ÙÿmÉÅo9çÒÁáþ‡ž¿ëé­ïÞqàñ×îÅ£ûo=ÿRO÷ª"ÈàM†H_³pOþÙgŸ1cÆå—_Ž›óR#NW^y×T{zzð-WD Ö¾ã±Ïÿù-o½ûÑ?ì*hZ¢ÁÀ#ÿúÅÍKßõ¾Õ K àrÛùØçþü–·U3gW—Tð!€óç8˜1k¬ØÁOÄ<“<°k×¾uëÏjK-OÄgunÝ:ò‹_FÆÇ;·m]<2ÜL¤ÛRÛÖ­?°ì\üˆÛáð®Þ¶§îž9?±à¬TzZ$9ºolÏ‹øZÙÛ3=›ZÖžL ï}êîÃK–{ºïÞßû³W˜2§sæÂI“ºÇÇö½¶ÿùᱡÁÈá®Éíã‰Îh6ƒÅó½{wçC ðD'N9sæìÚµë?ÿó?qF@¾ä’K`Œ&\MÅç /Äo1±šôfÇÌ‘Çî~ä´·\¾lšÃ©±dg|QGSW §úºš=ãûŸXÿ‹®ß{oªÎùg^}üm]+.~ãlÏpPÆÈ94ç|Q¨' Ž8Ÿ vĽ–|k¯O>952þ¦öö™±lgß±Ôæ_ÇúcÂHï|=>p<‹$ÛÛŽ Álá’%nLú~¹~jO|ñySÛºÒÉÎXb|Ïø¾_àªU<³«}r,ë\Üøå¡C¿\ŸYr¶Ûýé—žLµ'ž;½£³-Õ=ÙõÊѱLvl(q¸£'‘èèéH§_:´f‹y¸»RC<; x¥ÀßøÆw¼ã·ß~ûwÞ¹`Á\`9|øðÆ—.]Š_Aáá5\ec+N6sà¶/|áŠ%«Ïžâ\˜Ì=òæ|ýô¾cqª³å{jc¾½¶ßý惿‰ëAFžùäç¿û¡[V/Ÿ‘/¢ó”îꎘ/õD ¡À—ã“ìˆÌ žõtööÎko›•ˆMNÄÚc‘ÄÀñèÖWa™N%b‰h"_–mÛ¹k—çêi¯Í=«;=mrÛät²#oÆcûñˆC¼+’Iv%Ò‰X[jþYcѯyºì?}éŒi3§àe)Î[QâÙ‘è¡ñH6ÝOeºQÀpÇØÀÒìñ}û=Ý=+¢’¸Ðã7.¾øb\G}à¾÷½ï½÷½ïÅýÈ)S¦¬X±^9úȪñÉ8#;n~ÿ{¶F"·|öÏþuÛ²¯ý¿àª¬s¿ÎlüáÍŸ¼¯ã_½nþÄëðNú9’Ë7ùÄ×¾òÒÂw{àßùÆ9ïûÊW¯™þÃÏ}þ–Ÿ†mê-¿ÿ/ùál|è‹×?ó¦?ý‹‹ÎÌŽl½åý7Íyÿ;‡þãonr$ù¦ß¿éÓï^ÚsÊýê½_ù2ž¥»éÿüÑMÛ~óúo}ࢇ_{ô7ÿýÝ›úî¼ôSßÿß—8«P§¸ «Q'y~ˆ@Ó!Bt¾×Ê•U¬õ‹Ui×±ã3RÉît{G*‘L$c Q\ƒÅî™t<3ÍΈ =zÌóœêNNž=µý´î¶É‰t{¼-qŒ$2ÉÌH[<ÝM$'÷8äéžMLŸ3erWw*OÆAÆÎ+4o·ã)ú‘¯ýp®óã)¯þݾýG7þð†§´æSŸûò™g,Ì íwÑ×¾óÑ®þ—¿òþ¿ûÆŠßúÜÛq¥düÈŽç_^8âd>>zxë¦}bÓ…×}úæk2ßûËúƃbs²»Y+Ö\ùé«¿w͇V/HϘ6¸mýÛßóÉ×~ñŽß™9Ú{¤ÍÉ.—3þõLód,JD ©°×ŽàIÏzqŵ-íèîjK§£x*ÕÙ¦vŽ Ɔ²ƒCm‘cIxéXŠ^p^êž ›“=µM;séyÉ}3ÏX²äLh_zèÞHä 7|úÝçv¢‹™3OÏ¥Œˆ¹ «ÞsÁÉ`”ˆ@3#àÜuÔûŽÌÙÁ¬;µpÁȾ=‰ž)ÑI“¢]]‘®îHg§c08=~,r¼/Ñß724Ô>m¦g„±I§‚×&uÄ;Ò±öTkG¹„;–ŠÅG° Åùƒ‘I§Å¼¾°NŸ<;ƒàÉT;þPCbÛÚ)|'NDñ³”±¡ÈØøPdzÏlÏÞÍB(Ìã2éÆ÷G,qrýúõ+W®Ä=Hu7U‰ù÷ƒÉF‡ÆÇïãÙöáM_ûÄ&ïï¾hÙTqÉŽìüÎûþìaõ‰D.üÐM}Å—ï@ûŠ vc‘ê,g³}۾˾ùЖÈÂ…‘mcN,^*ÃJ‘³ãYt=ÿ̹^FGp¥Ôcëè#r­72¸Ä.ëi›èB2’ˆµ£1D[¡F|øEG¾3¢méÙìëžœê™éî‰`Û1ÉùØ@/iMµeñƒ{ö¥Î>Û3Bfæ²#}¯.À»ÚÛjL&£IçvH6–A¡m™£}c0Ãc îA8}êâ—þ"‘·ÅSéDjR2Ýw¦žx4žà;ï®ì=0xöiË={w¤†x"€ãG¯¬=zô±ÇÃc8øüêW¿úùÏþƒüàøÞ¶_ï+«Ðç¨,›xä3;~°ý‚?ý§«†?ò©¯âónø_8÷‹>~Ï=?5§kÛ7;îÜ©˜8%žºã#ÿöÈE·ýäæy©È†O\ýPß$q²s:VâŽW^‰Þ©ÄQ['ì²Ç‡Eß6oá²ñ‡¶ÊÌL;ËSù8ˆë ÿ%-ˆÀ)ìè\©ñº. \ºÏ?ÿè–­["‘s{¦Äð‡÷ð×…Ò¹ßJ'“Y¬üb±­GFæÌí>ÿM}£Æ£ˆNZ¶fpÓ–Ý{OŸÚ[ŒÎ¥T¼R¯æÂ68“ÙÝ{3±IË×ôx\Î9sú{ûžÛ÷ú±ÅKf㲬C­1¬Û9©hj$2¾gûþt*}æŒÃýÃ'úä¿D hpü àmO=õ~ûˆßoàëµ×^ûú믯{`^]»v-âÂÌódÉŽ8tµqÓæ ¦“N§œW:íÉ.|ÃÕ·^ß÷¾ü‡eÿösïú χrÓí‹sDÏÉñö†Ž=´mÇc_|ndÒÜ׎ft殬J2øý1º>qM=ƒ9$³#Ûoû«_óÏxÛ"ôÒ>5þÊ·Ö¿²fμÎôüß¼(òÍ/|òæïþ]—L‹Þ²clùùgåÂä®Õˆ@ó àœz¹?{쬅*=‹ë‹DºV¯Úÿ«§·'’§wLJ€ñiÜwŒÅÆ3c¯÷ì'»ßúVP#â¸#ôDÚ\¾kçC©žÎÙ‹ÚO~MÅ,‰ìÙÓ¿{ç±Ìé—9VÒÜ÷¦ù¿ýëÝ÷ì<žž×6ËfÆœoÄøZ<6–=Ð{ìèþÿrú0ótwçC ðD@¨WSýë_ãÏL‚å9µåË—ã·Þz+–‹-ÂõU,°¼¶Ôì·_{Á?Üvý·þ…ÿ—¶Ø§8®gþÆ;oþxöÏoøÌ—&îãW.õì=âå«§Äâ·½gþOoúÈ=_xå{®]õõ;oüÛY³o¸zžs!TX0“Á7Ö8•“û‘è#GáÙÌÈžíãÉ#ÎÙ…öó®øãñG¾úá?¸ëÝ7|çw—¼å¶êÿÇ¿þê_<üo0oÿ¯öÍ7.Š&¦(ÇzçI-häV#Κ(¾)?óÌ33gÎô¬vÓ¦M‹îÝ3öâæ‹M<9í¼1.:8©}÷óR™]=]¸c=z,:Ÿ={ùš#ýCAÜ;'Oz±÷çG†·'£˜µ2£ÉîöçÎû;£ý>îžQIÜ`Õ¸cÇü–,èÜVÏÝ~Ã! [|äD@^;¼páBwjˆh^|ñEü¾¯oKÈD '¿gmX`âZÓñööi+VàE”²FìÃ4±cï^ܧ/îÐä‹0á~¼}ÚÒÕêÞ&î{öv?~Ö´ó»ºÞ&½c Cï»wî)èîY•DÀBü‡WÇá\j4[¡Á.šD‰gsòê¦e"@œàB‹Î€å“¯Ì0À±Â—kL":Y@)S†„Ó=_VÔ Ï[–ìâÈ÷ÔSIˆ@ ÔˆÓ|bí¨_}jƒMîö¾mÄ>õu·“æ> D€²ðaM¬2œ+D€"@š“숚@çŸ~óÇJˆ D€”„~Ö%~ΕU|°óB6÷Џ’ÂÑ©¡¸úì9ÁóÿÉK»‚›–Eõb:6¬ÐùC¡f Q¸»–ÆÊ¿!@f’5C`Y´O8ÛSþÌMÍ2`GáAÀ=»ùç{Nþy¶š ‰ìFÞ´ñ Be#æB™R ;†pPžR ÓºÛ%ìEV??òJõ1fD tb¥»Ò³ñ¨i!N¥B5>¨=@¡Š„²c¸Ç§¡²#A6Ôp1Y"@ü ;ú¡Ã¶b AºãÂÑ 5D üð¾cøÇ¨Á24 ’Ä@ìðeºDàdÇH„ì_“c$5kž-h†‚$I+ó0$VƒZ³êË.ˆ@m ;Öç"zqÓž8+Ó4(¢³š˜æK¸&×§Rc}pg¯D rð¾cå°¬D¤‚DR¾¦Y0”ZR( RcQpј„®C4.•¢+ÄÁíŽÆY»ƒMk2» 5@€ìXuëÂRîNQ§(eúö4¨:MÑA>É‹M1¼,‚L @v¬â¡L–Òîó«A¥tÔó8ª¨hnð›Cw]Ô" ð¾cµŽ„‚·i`ÊÕJȈ[ã+,V¢† hVá:Žš#ÀµcU H?ͪ’bƒ­,KI4lýG¤²6ø0}"Ðä›|€›¾¼‚”æ€Ex²ëæHËÌ?&[‰hÈŽö ŸóYºõv-¼_&™ r%sU@Ç€f-<Œ,4?dÇ“cœÕDoΘþ–'#RòB@tc¨»›Ì0j¦Jhü]ÄÒí¨("@,ÈŽ€œ^a ÓkAK bîz"àÃUùpöwñ_Ïô¨$D Å ;:€ÿ·H@3µ§PÅòY œZNzô%D ¹hv$±…á8.–ðJȹ]”]ˆh8šŸÉ‹ wP2a"@ˆ@Ýhò·ë~„i\Õ)ˆ?ÍÌŽ¤ÆðÌ"Nš™Éxx²ªåb®–}…afBˆ@ã"дìÈ…cƒ²"¤U0HAƒ ©Ò†"PKšÿ©œZ¢Ù@})c‰àþ2½§Ò³FŸ žöT"@BŽ@s²£{Zù0T$=O> YèÍ2öTZ6æn±ö¦/e"@ˆ@¨hÚ+«¡B¹É3ä§€f5H›]"@‰Ù1œãR\V&Û™²g”‚ž^T"@Z æ¼²Ú”CVs_1ö¤:Q4nJ¬X D Lš–£ÑhQÐd³Ù¢ìË4ö¤º‚1=¹0ŸWQÆù‚PþûÒ¹!ɤfiüxsoÍúbGDÀ@Ó²£»Ô°iŠ"HR]؆¯öù\uÕUµï´^=Þu×]õêšýA€÷ëp$êê:»$D€ƒ@s®ÃL?fnÝwÝÃgº¸[C¥)ó /¦…j4™ heš“«7¢BTA(̓'É$HϨd7¼UÕx²>G¡ª˜38¨/dÇRð/#=©QúÎÍ“ÅE&kÎÎ¥ ^‘>ž¼ÈQ(EšÆC€ìXĘY gíJ ÷²ÒÓÌÝ«iæ3#›ŽäHŠË…ŠCÊ€D  ;,“½||šùD8)kØs©hTJà(T IÆ! ŠŸY-

½ÂÈhä‡7Í}òÎsq‡MÿùSÓýűÿØàP,?D 5 ;¶æ¸³j"ééŠlë{âéŒ`ñ?þ¢÷>öÊg>t:v?ø?gÝð±r«ÒºaIàˆ@‹ @vl‘f™-„ÀèXVÆÁ²gå¸v::yy[dùâèõ¹'q2㑇Ç^Ú:{ܳ”ÿDöŒ@%hnêü‹Žæ—Õº ÏÚàŽ#¨2rÐU 8ORÚ²nÉ‚9m"_qÝ‹x`õÌùíOþêø;ÿ굺äÌN‰@Ø ;†mD˜(<ŒÓ~Þóf%EUžq±Ãšú¹ÿñWU6·£ÙJ™41 ve5ßc«|`µ‰Q–Fˆ¨= ÆŽµˆ="@ˆ@ "ÐäWV~øaÿAå¢Ó¶"@ZÆ[;º/®’áZóØeÕD€ê!ÐxìX=,™"@ˆ€ ÐWV±|Ô¿×Ѳ LJzÈÿ ^½zµ¿AÝ[YB݇€ "†dG#ÙjÔhÒ‰E~ëÞ|ɪSþœ‚q¾£¡zŸ¬XB ðgD€D QÙ1`yÍa¦Œ"ŒA]ë\µ‰^ÕcÑÙ…2­jS3ÁJ€%Ô yvDˆ@iKíF^&©€QÜŒX0á!0%hÑ$ µî,¤éÐ)K¨×(”3‚ô%­‰Ù1¼ã®Læ&ë"*j€§R˃ɑµ!H– CSßQÐÞ)"è† 6nܸvíÚ²ÁÝÂ`©æ„!™ÊæÍ:oŽŽF££‘Y9Ù£2ôæ.ô5V(íBcVVÐøV¿ÚKÁ„ÝEY¡´ YYAã[ýj/á/AS­—àþý•fÒÄç¯ÖH¡d|Žœ’cq\íûö·¿½råÊîîîF];úŸZЬ¿ÀRË ÀÕÆæê³çüä¥]W.]©“òÝ›7:©šÝ/]ig^HswÎañ/"c‰° ôe®ˆÌNÂx긔< ä“=R"D 4äïýg ›6*Smb€T”W¤GáÅÊö®1Á” Fé´R]°„€HVuæ@3"@|hr•FÁ§G6"cÇæ¦F\í”+Ÿ?:!¨RÇÕS©­à¢To*µJ¹¸Z&AŠ;KP´ë2 f(FºïèO@¡ ©|.µ¼<«¼"é)o¹³­¬F:;:¹y#ÒÀ‡\ðëºIDAT”ÅvÄŠELí+8 “ å#Ð0kÇ|4&Ô’ÌÊÝÁŸ“0‚ÀÜ^EiÁ‡w‚,ïñw– ƒUíQ(ê 1 >4;615ÊrMÈâÃ"bã3–>Mù|e²Ö„ †“U O(wKpcâ©©ê(xöH% %#ÐìØ"Ô¨CèIB`jÁÓL „óLc±·4n³Ò¤Fï™K¨Þ((òˆ¨agÇ&¦FB]O˜ä‘]Ô¸¨áw³ Ü-¥ö^Bê¢A?H `ÖDv7¶øÏ¿"+[1¶”Ú»æãÓlU ‚Ö %˜Aüe+[1¶”Ú»æã“­D€TP?•ÓÜÔ8±êÊ­N‹:Ò25»õj` @-{RiÊÐZ»bç©DS4z2NQ’;à ¼wÆ :ôŠþNÉúD]Js7'¹nQ£„’q‡,W¹ýïæž’wˆ¨(á];6=5be‰u¢Ôwk´IP8?Vžî]¤ò‚ë®K4n"tkÔWŽ96 ®ªÕ‹ •E ¤ìØÜÔ(C¨Ë̃2º‡ÖŸý[ÝѨ©>ä‡|Z̓Aš¥ÍŽˆPÂxeµé©Qˆ²PÀxX»:BBjfíª™xq¹¥)sW—teÆ žXpË2Sª™»Å…Ö®¦aØu4¹û²¤IE‰¨¡cǦ§FZÌwWž:ÈÂÖ)&#Z»°Á].8jK j`v«ÝUê žSWî§™’ƒöefh壉ù–‹†…àÙ*åæ®i¯²4©¥êóŽY—_Â'~Ÿª6ßÚU%3²è±ž•I „‹[„10ž“2ô˜"ÑäžCEï9f hOµÙÝp÷|L¦`X¼4N¶,«]‚Ù]A±ö2í-¹ »x¥FÁL£`þ%˜¡B(ûŸÑ!L˜)E Dìè"yP« ³0±dÉŸ¢ÌÈîIÓœšUVAâÁKÜ4Ÿ5åü]{¶¸/ëÕ¦­Y™²V AŠõL[”²öj‚|jd Õ@ ,ìØ"Ô|eÞ·8ÒâB‰æüd’¹ ÞÍ K‹?´_{­yÂ>È¿ÁKðfQ£ææïU‘Ö&(¡"80hABÁŽ­F2¿9Útv†0ATº T!H ÃÆ"B£Å<ó\™,­éµÀ[ìŽÅ¢Xªµ«ú|‚“gó–¯jê‰(ú³c«Q£Ž™pƒ¹+²Çr­$"Ä}At!1’(tRlÄÞÊM”î­e¦ÝùXŠ8Šlö+Žª 1E©»î. QGÈÒ…§™©´Ì|â«¥ØÈ®Èf¿\5ÄzQê®™†Êêt¡Mˆ¨%Ñ 6lܸqíÚµ/d;Ëé$çskПËé·±|ñ üba42 iãŠhE’×€ˆ–ŒìApÝB#}AãÙ—´J“e£Ž’³º›%xºxFSw dMU315ßtÑ8*¨#4žù@Ù|%hùˆ@S"àÃ&U­wY´ïÛßþöÊ•+»»»ë¿v¬j©aŽ©ÙLÃÚ5›üeý! ²áuTê-€¥hœÒ³¯|½ˆ´J’²ÕY‚BAOücž.%(ÉŽ%€æíR2íYá”E¯4#´¥Ð‰²ˆånê…Š` AÄÝr©Æ®víÙ;K0ñ©þŒIˆ@™T’[ðòi9Œh±`¾4§Q‹ätײQ‚ô4È×QõôVzÚôžBÉ¥j>ÇmÀòY1&PÂÀ&•dÇ«®ºJk³„»îºËҴή9Ý`V®RB± xÒŒ(e¾Cp¡a—bã×Àž%ÔdŸ.”õ°4´Š¶ú„’¦’ï…aú+X ê…@ؤ’ìX/+Þ¯LÃê</•}|•Uð1ؤ ›9T0¾•†FVÁ2(a—%”ZQ.‚0Q5sìT–÷áŒÕ‰@3!@v¬ÀhbrÁ„„Ñ™¬íD(§oÑò1Wiùl|ô>ñY‚âæƒ’ØTo4‡‚‚R²-˜0¢Á4‰ÌÕ±`4 ͇@%Ù±i.Ÿä9=Êœ5NAÁœÚ”¢à…„­‰Ìl…AÍ2d 2‚CFA °<ÖÁ½©Q3r\!B±gDÁÀDÀ0°I%ÙÑçï1ø >­:[ÁFg1(ñŸL¾"èü¥zË^»Ð ª©¶€”´ íJMU– (UJPj P*±Ç¸€VñÃ\dpiY|ØñkC(•dÇŠ€‚ rJ[Ñtê´ôõÚ­j’˜•´.¥ÕH«Ìb²µì±«zq—­D°Œ5leÿ^¤U’ÔT5K/ÉË–%(J‹@s\|"˜Æu$Ȫže>å[M!IÃÊÊÚ I’!Iç´Ýб£?ÕINaU"Ä™¬s¢»5HÌŠÛ¸Ó¨T’æ´¥U[ù‹Î\¤wõ55j† "K4SVGËÆê7ànhb£¹A€F¶šžjÔÌJOôb¤Ó€ùÃ,H4±ÑÜ$yÙJG¦FÍÐ$²Úˆ€mNÕ¸(Ä&öÚµæ–/’±Œa‰SRü|ŽÔk_õ B’†?°!I²Æi”O7þ¨JkÅØÑ¿³‚ÅÈËEñ‚l¼gÒœ,TÆyŽ×N ÃàùíÊ®µ=.?IdŽüeÎBÖK5ëtB0_щVìÑÜÛS‘¼ÓÒ š»sÄ»'âMü+­²#6";–¹à‚¶‰Kë; Ö8ºwq¬:gS® ŸrD-]i(ÚgGˆœ’2îØºû*MsJ§FˆòÏ2#XébHÒð/ $I†$ ¬Jh­;údf"+'s>cœ¥c¹W3«K>ËŠëµGäP©$…ႤjYš»˜ø°kQØ¢U¶¦£é"²ØˆlÅt«™é•Ï z«ks¬îT#‚t![ÓÑÝØhnn·Æ? ioYš»’§i¬$1ÙšŽ¦‹Èb#2,ÝÁ5ùÜÍ”¬h>¹9Ñ*ôf`«SÝ­ÆY¦Áƒ !IÃ?á$’4ü±*¹5V²gEMp5 Î^ù•!ˆÎRù+¾êU=¡âIj-sF±Ö„¥»*¸ƒ >Z¡WAldWšÜ^nOž>MVtg¥ª»*X.ØÕÌUÙ…£ÛÅSã“§O“ ÝY©ê® – v5sÄFváèvñÔÏSÝqÄš‹<ϾÜJ·M%bV狼øY¦h%„$ ÿœC’dHÒðǪœÖú¯qÊÉ(³€y6æ+ 6?Ñ¿w˜Ï¨¢úÚ'ib¢¥@Yì\)s±z™ «RãWV` ŠgíGÁßÚ•|4=ÌCÂtÙÙVsùXû³L 7…¤a¦ä–C’dHÒpãSM=׎øêa+%É©h•%N]ùMØ…¯|y±,+»[ñ$ ¦Ò´X³QŠFeD¯³ž Л²š‰î¢ñÜJíî&– ˜(ø*ˆ^WzSV³rFA‚¸·ž':²ú²ò1K€,»°ÑTµ#ÏøÚZšPñ³¬¡ÓðOžXùãSÁÖº±£œc8 QŒž„–ì_§øVã\Õ~«—¤”,%hwÜ€H+ô&J¦K¾î¾ŠÍÁê¨Xw–PQ°EvñõÅSŸoÈLã|G d3B±rõβ¢2 Iþ9‡$ɤáUù­õ¼²Šs8ß.ÖLjíê,SpS>@UJ‡ž02?>³’U¾µk1eAI&Dè­]±”VÏ®å0Z2KPl-dÌ] vkW,˳¯ ²Õ— ½Qž‡A˜¥ÙX™ËÓén%²’_¡HÌœœ,«Nh¢QÕ”#T<|敤 \ÌIC•J5* É”ÅRž™Ï7÷j4¡JiÈÁéÓo›êÉŽ:l"(4´ p@Ö&ÙUŒ³jÞ1»«`’BêR¦VjÖ¨Jf“…†ié–¥ u·vÕ^ 4衹:Ažjc ,AQµqïZ°[»j/‹õ-Vоô§òûŸbã”i/TdqkÐ*JmRAÝm*Mð (JmRA»pk^µ¡Ð@Ô‡±ìÐ5™ž¨2/È.4§ T5ê½LÓ>óxɃQí$uí¥%»SEÉŶÂÞÇ˧U:‘›;·†%O´}p†‹Ok £àDÉÍÝjið“VùѽzYÜ%­‰@}ØÑµN rŠÊô3™zô¼3·{m4LÒZ{yÖ%µ›¥¹5f«§,HšñÝuœaà¿p{– ¸ܘ»5¤¨QP/SàÐ@0õ–,éûh‚[¸Kš°°£@,ç'NZü§%ò½çÅ•Š\ß·bzî–“¤PØE®à£^knrkLß2eô厄•Es.XB”òÙTjòÅ÷ÑËX#}A’$>Žui:9ï®}&!Iÿð$’4ü±*ØZ7vÔ¹Xw®zÍM>fnÇJi´SÜ‘ËLRg%t¡“)kžJmõ¤ .––R „ Ç¢> .f†¦¬Ñ<•Úê#XÙŠ¥¥lñ|Ð3›%@¥ ‡¹%¶b`:ÖW6ϲ:f’4üI’!IÃ+ÿÖº±£LmHÎó<”sX¯ùxš‰ye¶•¤O!ùÒµÞl€Zœo[¹'ñpoßÊ],4n3éHÒ¶ZÝön3h‚/¥/–`á¬cí†MUíT½léå(Ê—°KÚî<5²³6»æTP›={ Iž¹©2$I†$ …¥¡nì˜/W=­k>ªÏçXK½&c%‰ü§w’Ê.JJ“j,‹¬î’•äl*-=âK%X¨æÛâq·šú’GAÃÊpè®zĪô¥»’ƒhdkº˜â«Wm÷³ µé×ê%$iXYY»!I2$iXà».vÔ3VÏFó¤…Rôrö[j¥ì &YBz %9ž@–±²”*J¸Éj"醭ÈÁ­®a A°ªö(hÒ‘yiÕ›JÑÃQ”"˜–Ð{ÕÝŠÏ]"Ð@Ô‡åë­û'¥zfAÈæ™)l¡4õ¥-w‚ŒPÉIJzf-þIJGø ìò+]O‚´(3¹t.4æO§-ƒ *ŒÎ¬—£Æ’—,A¯ï(è [‡®y(ª[ ¦™*ýcu¡@šú°£`'g£›Kôl4ÍôdA½ªýe¶„$Í Qv &©ìòã—vó< Ò<àd¶æb“,aŒ«¾’‰:J-é•GŸ%õk2ܦFdœ;Ö¡6æ¢JÐTð0¶\ î:7ŒϦR5D¯‚ø©¦\ÑS5Ú» ÕK£Ü2è_6õdG|•å£û\µ4ÖI«ô)+ž²Að Pr’4`’Â.0ÆUSX 5¾ rë.k¦Öë±  3åU‚¥S–ê> æXäû½ØÙÊ¡"Ç Æ7ˆK@=å” 'èÈúCÍš¢ù”¯}¹Ÿ!s\zA¿åd’4<Ñ¢·’41Rb¦Z-Yì+ŽU½Ò0û­†\··Ëq¬Ã©µÉÙ¨»>‚ø–s>ø—¦'‰îðN”f}9-˜ª§‚˜ÿɪTF/èK:õt/AÉÜ ™C¹£ 9È<ˆ]tsºT3SÀQaK“LʦYe3ˆ&§³v )ûw'¾ÇßÒ§5$iødˆ¦$’4ü±*¿µnì(#-LÙ<äX·j““fb‰]™Ù-³Šïâ8¨q’Ò#æ5ùrE¤y­©"=į€,!È0Õ`†IæY柡žhA&D¯’[k–y¦’4—òõ,Á°^£€£)×-­”^¨#D€"ÐT€¿ûÝïêU“!£TáEl+« H¨ÞõÂsM…‹!D€"‰üá @Z4 CâA>ùä“›6mºè¢‹N¸ó_"@ˆ ̓À’%K‚óÙÏ~vÅŠø \cÅ·(ûsŸÁÜgddd8÷0–ûd2ü‹>²ÙlðžhI‚ Ð××wò+[îz¾¾ÉGÜ!ãÀ“Ïœ¿/sêfjA½‚ôN"@š ´®DÂá;lñI¥Rm¹„t:}äÈ‘ŽŽŽ®®.L,ØÂ ÓÑÿ©?6²~XëIEND®B`‚doc/example-scripts/tk-locomotive.tcl000066400000000000000000000151421242365656200202610ustar00rootroot00000000000000# # Example by # http://wiki.tcl.tk/1329 # # - translated from Tcl to XOTcl by gustaf neumann in 2001 # - translated from XOTcl to NX by gustaf neumann in 2010 # # image::tk-locomotive.png[] # # Left mousebutton starts, middle slows down, right stops # package require Tk package require nx package require nx::trait nx::Class create Wheel { :property x :property y :property r :property {spokes 24} :property {pivot 0} :property {color red} :property {tag ""} :public method drawSpokes {} { ::nx::var import [:info parent] c alpha set delta [expr {360.0 / ${:spokes}}] set deg2arc [expr {atan(1.0)*8/360.}] for {set i 0} {$i < ${:spokes}} {incr i} { set x1 [expr {${:x} + cos($deg2arc*$alpha) * ${:r}}] set y1 [expr {${:y} + sin($deg2arc*$alpha) * ${:r}}] $c create line ${:x} ${:y} $x1 $y1 -fill ${:color} -tag spoke set alpha [expr {$alpha + $delta}] } if {[info exists :act_pivot]} { lassign [set :act_pivot] item perc set rp [expr {${:r} * $perc}] set xp [expr {${:x} - $rp * cos($deg2arc * $alpha)}] set yp [expr {${:y} - $rp * sin($deg2arc * $alpha)}] $c coords $item $xp $yp [expr {$xp + 1}] [expr {$yp + 1}] } } :method init {} { ::nx::var import [:info parent] c alpha set alpha 0. set :y [expr {${:y} - ${:r}}] $c create oval \ [expr {${:x} - ${:r}}] [expr {${:y} - ${:r}}] \ [expr {${:x} + ${:r}}] [expr {${:y} + ${:r}}] \ -outline white set r1 [expr {${:r}-2}] set W [$c create oval \ [expr {${:x} - $r1}] [expr {${:y} - $r1}] \ [expr {${:x} + $r1}] [expr {${:y} + $r1}] \ -outline ${:color} -width 2] :drawSpokes if {${:pivot}} { set deg2arc [expr {atan(1.0) * 8 / 360.0}] set rp [expr {$r1*${:pivot}}] set xp [expr {${:x} - $rp * cos($deg2arc * $alpha)}] set yp [expr {${:y} - $rp * sin($deg2arc * $alpha)}] set new_pivot [$c create rect $xp $yp [expr {$xp + 1}] [expr {$yp + 1}] \ -fill ${:color} -tag [list ${:tag} pivot]] set :act_pivot [list $new_pivot ${:pivot}] $c create arc [expr {${:x} - $r1}] [expr {${:y} - $r1}]\ [expr {${:x} + $r1}] [expr {${:y} + $r1}] \ -style chord -fill ${:color} -start 310 \ -extent 80 -tag counterweight set :pivot $new_pivot } set rh [expr {${:r} / 12.0}] $c create oval \ [expr {${:x} - $rh}] [expr {${:y} - $rh}] \ [expr {${:x} + $rh}] [expr {${:y} + $rh}] \ -fill white -tag hub set :r $r1 } } nx::Class create Locomotive { :property {speed 4} :require trait nx::traits::callback :method turn {} { set :alpha [expr {round(${:alpha} + 360 - ${:speed}) % 360}] foreach i [${:c} find withtag counterweight] { ${:c} itemconfig $i -start [expr {310 - ${:alpha}}] } ${:c} delete spoke foreach wheel [:info children] { $wheel drawSpokes } ${:c} raise hub set xp0 [expr {105 + 15 * sin((${:alpha} - 90) * atan(1.0) * 8 / 360)}] ${:c} delete piston ${:c} coords p0 $xp0 120 [expr {$xp0+2}] 122 ;#CW ${:c} create line 90 121 $xp0 121 -width 2 -fill white -tag piston ;#CW :drawRod p0 p1 p2 p3 ${:c} raise p0 foreach i [${:c} find withtag smoke] { if {[lindex [${:c} bbox $i] 3]<0} { ${:c} delete $i } else { ${:c} move $i [expr {rand() * ${:speed} / 3.0}] [expr {rand() * 2 - 2}] } } set t [${:c} create oval [${:c} bbox chimney] -fill white -outline white -tag smoke] ${:c} move $t 0 -10 ${:c} lower smoke } :method drawRod {p0 p1 p2 p3} { ${:c} delete rod ${:c} create rect [${:c} bbox $p1 $p3] -fill white -tag rod ${:c} create line {*}[lrange [${:c} bbox $p0] 0 1] \ {*}[lrange [${:c} bbox $p2] 0 1] -width 3 -fill white -tag rod ${:c} raise rod ${:c} raise pivot } :public method tick {} { :turn foreach i [after info] {after cancel $i} after 10 [self] tick } :public method throttle {} { incr :speed 2 :tick } :public method break {} { incr :speed -2 if {${:speed}<0} {set :speed 0} :tick } :public method emergencyBreak {} { set :speed 0 :tick } :method init {} { set :c [canvas .c -width 600 -height 160 -background lightblue] pack ${:c} bind ${:c} <1> [:callback throttle] bind ${:c} <2> [:callback break] bind ${:c} <3> [:callback emergencyBreak] ${:c} delete all ${:c} create rect 32 115 360 125 -fill black ;# frame ${:c} create rect 22 118 32 122 -fill grey30 ;# buffer ${:c} create line 22 115 22 125 ${:c} create poly 60 95 40 115 50 115 70 95 -fill black ${:c} create rect 60 45 310 95 -fill grey25 ;# boiler ${:c} create oval 55 50 65 90 -fill black ;# smokebox ${:c} create rect 70 32 85 50 -fill black -tag chimney ${:c} create rect 40 52 90 75 -fill black ;# wind diverter ${:c} create oval 130 36 150 52 -fill black ;# dome ${:c} create rect 195 35 215 50 -fill black ;# sandbox ${:c} create oval 260 36 280 52 -fill black ;# dome ${:c} create rect 65 100 90 135 -fill black ;# cylinder ${:c} create rect 90 120 92 122 -fill red -tag p0 ;# crossbar ${:c} create rect 72 87 82 100 -fill black ;# steam tube ${:c} create rect 310 40 370 115 -fill black ;# cab ${:c} create rect 310 32 390 42 -fill grey30 ;# cab roof ${:c} create text 338 82 -text "01 234" -fill gold -font {Times 7} ${:c} create rect 318 48 333 66 -fill white ;# cab window #1 ${:c} create rect 338 48 355 66 -fill white ;# cab window #2 Wheel new -childof [self] -x 50 -y 150 -r 13 -spokes 12 Wheel new -childof [self] -x 105 -y 150 -r 13 -spokes 12 Wheel new -childof [self] -x 150 -y 150 -r 30 -pivot 0.5 -tag p1 Wheel new -childof [self] -x 215 -y 150 -r 30 -pivot 0.5 -tag p2 Wheel new -childof [self] -x 280 -y 150 -r 30 -pivot 0.5 -tag p3 :drawRod p0 p1 p2 p3 Wheel new -childof [self] -x 340 -y 150 -r 16 -spokes 12 ${:c} create rect 360 110 380 118 -fill black ${:c} create rect 380 65 560 125 -fill black -tag tender ${:c} create rect 560 118 570 122 -fill grey30 ;# buffer ${:c} create line 571 116 571 125 ${:c} create rect 390 45 525 65 -fill black -tag tender Wheel new -childof [self] -x 395 -y 150 -r 13 -spokes 12 Wheel new -childof [self] -x 440 -y 150 -r 13 -spokes 12 ${:c} create rect 380 132 456 142 -fill red Wheel new -childof [self] -x 495 -y 150 -r 13 -spokes 12 Wheel new -childof [self] -x 540 -y 150 -r 13 -spokes 12 ${:c} create rect 480 132 556 142 -fill red -outline red ${:c} create rect 0 150 600 160 -fill brown ;# earth ${:c} create line 0 150 600 150 -fill grey -width 2 ;# rail :tick } } Locomotive new doc/example-scripts/tk-ludo.html000066400000000000000000002340311242365656200172260ustar00rootroot00000000000000 Listing of doc/example-scripts/tk-ludo.tcl

A small Ludo/Mensch ärgere Dich nicht/Pachisie game, originally developed by Richard Suchenwirth in plain Tcl (see http://wiki.tcl.tk/956). The game was rewritten as a design study in NX by Gustaf Neumann in July 2013.

Major changes:

  • object-oriented design (no global variables)

  • knowledge about the paths of the figures

  • animated moves

  • knowledge about the basic rules (e.g. need 6 to move out of the nest, have to move figures from starting position)

  • throw opponents out

  • sanity checks

  • user feedback

tk-ludo.png

Short Instructions

  • The active player (marked with the button) has to dice (click on the die, or press somewhere on the board "d").

  • If all figures are in the nest (start position), the player needs to dice a 6. The player is allowed to try three times, then the player is done (press "done" button, or type "n") and the turn moves to the next player.

  • When a player got 6 eyes, he can move out of the nest. This is done by clicking on the figure the player wants to move.

  • After dicing 6, the player can dice again and move the player on the field (always by clicking on the figure).

Implementation

package require Tk
package require nx::trait

Define an application specific converter "expr" that passes the scalar result of the expression. Since the converter is defined on nx::Slot, it is applicable to all method and configure arguments.

::nx::Slot method type=expr {name value} {return [expr $value]}

Class Figure

nx::Class create Figure {
    :property canvas:required
    :property x:double
    :property y:double
    :property size:double
    :property position:integer
    :property color
    :property no:integer
    :property board:object,required
    :variable tag ""

    :require trait nx::traits::callback

    :method init {} {
        #
        # Draw figure and define interactions
        #
        set d [expr {${:size}/6.}]
        set s [expr {${:size}/1.5}]
        set y [expr {${:y}-$d*2.5}]
        set :tag ${:color}${:no}
        set id [${:canvas} create arc [expr {${:x}-$s}] [expr {${:y}-$s}] \
                    [expr {${:x}+$s}] [expr {${:y}+$s}] -outline grey \
                    -start 250 -extent 40 -fill ${:color} \
                    -tags [list mv ${:tag}]]
        ${:canvas} create oval \
            [expr {${:x}-$d}] [expr {${:y}-$d}] \
            [expr {${:x}+$d}] [expr {${:y}+$d}] \
            -fill ${:color} -outline grey -tags [list mv ${:tag}]
        #${:board} figure set $id [self]
        ${:canvas} bind ${:tag} <B1-ButtonRelease> [:callback go]
    }

    :public method go {} {
        #
        # Start moving the figure if the draw is permitted.
        # The board knows the die and the rules.
        #
        if {![${:board} moveFigure [self]]} {
            # stay at old position
            :gotoNr ${:position}
        }
    }

    :public method gotoNr {nr {-path ""} {-afterCmd ""}} {
        #
        # Move figure to the numbered position. If a path is given it
        # moves stepwise from position to position.
        #
        set oldPos ${:position}
        set :position $nr
        if {$path eq ""} {set path $nr}
        return [:move {*}[${:board} getPointCenter $oldPos] $path \
                    -afterCmd $afterCmd]
    }

    :protected method move {x0 y0 path:integer,1..n {-afterCmd ""}} {
        #
        # Move figure from old position (x0 y0) stepwise along the
        # path using animation. At the end of the move, 'afterCmd' is
        # issued.
        #
        set t 0
        foreach pos $path {
            lassign [${:board} getPointCenter $pos] x y
            set stepx [expr {($x-$x0)/50.0}]
            set stepy [expr {($y-$y0)/50.0}]
            for {set i 0} {$i < 50} {incr i} {
                after [incr t 8] ${:canvas} move ${:tag} $stepx $stepy
            }
            lassign [list $x $y] x0 y0
            incr t 100
        }
        after $t ${:canvas} raise ${:tag}
        after $t $afterCmd
        set :x $x; set :y $y
    }

    :public object method lookup {position} {
        #
        # Return the figure at the provided position.  This function
        # could be made faster, but is efficient enough as it is.
        #
        foreach f [Figure info instances] {
            if {[$f cget -position] == $position} {
                return $f
            }
        }
        return ""
    }
}

Helper functions for the die

proc random:select L {lindex $L [expr int(rand()*[llength $L].)]}
proc lexpr {term L} {
    # map an expr term to each element \$i of a list
    set res [list]
    foreach i $L {lappend res [eval expr $term]}
    set res
}

Class Die

nx::Class create Die {
    :property canvas:required
    :property x:double
    :property y:double
    :property {size:double 25}
    :property {fg gold}
    :property {bg red}
    :property {eyes 0}

    :require trait nx::traits::callback

    :method set {n} {
        #
        # Set the eyes of the die.
        #
        ${:canvas} itemconfig ${:grouptag} -fill ${:bg} -outline ${:bg}
        foreach i [lindex [list \
               {} {d5} [random:select {{d3 d7} {d1 d9}}] \
               [random:select {{d1 d5 d9} {d3 d5 d7}}] \
               {d1 d3 d7 d9} {d1 d3 d5 d7 d9} \
               [random:select {{d1 d3 d4 d6 d7 d9} {d1 d2 d3 d7 d8 d9}}] \
              ] $n] {
            ${:canvas} itemconfig ${:id}$i -fill ${:fg} -outline ${:fg}
        }
        set :eyes $n
    }

    :public method invalidate {} {
        #
        # Invalidate the eyes to avoid double uses of the eyes.
        #
        set :eyes 0
    }

    :public method roll {} {
        #
        # Roll the dice and animate rolling
        #
        # wiggle: amount, pick one of eight wiggle directions
        set dwig [expr ${:size}/5]
        for {set i 10} {$i<100} {incr i 10} {
            :set [expr {int(rand() * 6) + 1}]
            set wig [random:select {0,1 0,-1 1,0 -1,0 1,1 -1,1 1,-1 -1,-1}]
            set wig [lexpr \$i*$dwig [split $wig ,]]
            ${:canvas} move group${:id} {*}$wig
            update
            set wig [lexpr \$i*-1 $wig] ;# wiggle back
            ${:canvas} move group${:id} {*}$wig
            after $i
        }
    }

    :method init {} {
        #
        # initialize the widgets with tags interactions
        #
        set x [expr {${:x} - ${:size}/2.0}]
        set y [expr {${:y} - ${:size}/2.0}]
        set :id [${:canvas} create rect $x $y \
                     [expr {$x+${:size}}] [expr {$y+${:size}}] \
                     -fill ${:bg} -tags mvg]
        set :grouptag group${:id}
        ${:canvas} addtag ${:grouptag} withtag ${:id}
        set ex [expr {$x+${:size}/10.}]
        set ey [expr {$y+${:size}/10.}]
        set d  [expr {${:size}/5.}];# dot diameter
        set dotno 1 ;# dot counter
        foreach y [list $ey [expr {$ey+$d*1.5}] [expr {$ey+$d*3}]] {
            foreach x [list $ex [expr {$ex+$d*1.5}] [expr {$ex+$d*3}]] {
                ${:canvas} create oval $x $y [expr {$x+$d}] [expr {$y+$d}] \
                    -fill ${:bg} -outline ${:bg} \
                    -tags [list mvg ${:grouptag} ${:id}d$dotno]
                incr dotno
            }
        }
        :set [expr {int(rand()*6)+1}]
        :invalidate
        #
        # To dice, let people click on the die, or press <d> on the
        # board
        #
        ${:canvas} bind mvg <1> [:callback roll]
        bind . <d> [:callback roll]
    }
}

Class Board

nx::Class create Board {
    :property canvas:required
    :property {size:integer 25}
    :property {bg LightBlue1}
    :property {fg white}
    :property {colors:1..n {red green yellow blue}}

    :require trait nx::traits::callback

    :method lookup {var idx} {
        #
        # Convenience lookup function for arbitrary instance
        # variables.
        #
        set key "${var}($idx)"
        if {[info exists $key]} {return [set $key]}
        return ""
    }

    :public method getPointCenter {nr} {:lookup :pointCenter $nr}
    :public method getPointId {nr}     {:lookup :pointId $nr}

    :method line {
        x0:expr,convert y0:expr,convert x1:expr,convert y1:expr,convert
        {-width 1} {-arrow none}
    } {
        #
        # Convenience function for line drawing, evaluates passed
        # expressions.
        #
        ${:canvas} create line $x0 $y0 $x1 $y1 -width $width -arrow $arrow
    }

    :method point {x:expr,convert y:expr,convert d {-number:switch false} -fill} {
        #
        # Draw a point (a position on the game board) and keep its
        # basic data in instance variables. We could as well turn the
        # positions into objects.
        #
        if {![info exists fill]} {set fill ${:fg}}
        incr :pointCounter
        set id [${:canvas} create oval \
                    [expr {$x-$d/2.}] [expr {$y-$d/2.}] \
                    [expr {$x+$d/2.}] [expr {$y+$d/2.}] \
                    -fill $fill -tags [list point] -outline brown -width 2]
        #${:canvas} create text $x $y -text ${:pointCounter} -fill grey
        set :pointNr($id) ${:pointCounter}
        set :pointCenter(${:pointCounter}) [list $x $y]
        set :pointId(${:pointCounter}) $id
        return ${:pointCounter}
    }

    :method fpoint {x:expr,convert y:expr,convert psize fsize color no} {
        #
        # Draw a point with a figure, note the position in the board
        # in the figure
        #
        set nr [:point $x $y $psize -fill $color]
        Figure new -board [self] -canvas ${:canvas} \
            -x $x -y [expr {$y-$fsize/2.0}] \
            -size $fsize -color $color -no $no -position $nr
        return $nr
    }

    :method pnest {x:expr,convert y:expr,convert d colorNr xf yf} {
        #
        # Draw the nest with the figures in it
        #
        set fsize [expr {$d/0.75}]
        set color [lindex ${:colors} $colorNr]
        lappend :nest($colorNr) [:fpoint $x-$d $y-$d $d $fsize $color 0]
        lappend :nest($colorNr) [:fpoint $x-$d $y+$d $d $fsize $color 1]
        lappend :nest($colorNr) [:fpoint $x+$d $y-$d $d $fsize $color 2]
        lappend :nest($colorNr) [:fpoint $x+$d $y+$d $d $fsize $color 3]
        set :buttonPos($colorNr) [list [expr $x+($xf*$d)] [expr $y+($yf*$d)]]
    }

    :method pline {
        x0:expr,convert y0:expr,convert
        x1:expr,convert y1:expr,convert d {-width 1} {-arrow none}
    } {
        #
        # Draw a path of the play-field with points (potential player
        # positions) on it.
        #
        set id [${:canvas} create line $x0 $y0 $x1 $y1 \
                    -width $width -arrow $arrow -fill brown]
        if {$x0 eq $x1} {
            # vertical
            set f [expr {$y1<$y0 ? -1.25 : 1.25}]
            for {set i 0} {$i < [expr {int(abs($y1-$y0)/($d*1.25))}]} {incr i} {
                :point $x0 $y0+$i*$d*$f $d
            }
        } else {
            # horizontal
            set f [expr {$x1<$x0 ? -1.25 : 1.25}]
            for {set i 0} {$i < [expr {int(abs($x1-$x0)/($d*1.25))}]} {incr i} {
                :point $x0+$i*$d*$f $y0 $d -number
            }
        }
        ${:canvas} lower $id
    }

    :method draw {m} {
        #
        # Draw board and create figures
        #
        set d ${:size}
        set u [expr {$d * 1.25}]
        #
        # Major positions: p0 .. p1 ..m.. p2 .. p3
        #
        set p0 [expr {$u-$d/2.0}]
        set p1 [expr {$m-$u}]
        set p2 [expr {$m+$u}]
        set p3 [expr {2*$m-$u+$d/2}]

        :pline $p0 $p1 $p1 $p1 $d -width 4
        :pline $p1 $p1 $p1 $p0 $d -width 4
        :pline $p1 $p0 $p2 $p0 $d -width 4 ;# horizonal short line
        :pline $p2 $p0 $p2 $p1 $d -width 4
        :pline $p2 $p1 $p3 $p1 $d -width 4
        :pline $p3 $p1 $p3 $p2 $d -width 4 ;# vertical short line
        :pline $p3 $p2 $p2 $p2 $d -width 4
        :pline $p2 $p2 $p2 $p3 $d -width 4
        :pline $p2 $p3 $p1 $p3 $d -width 4 ;# horizonal short line
        :pline $p1 $p3 $p1 $p2 $d -width 4
        :pline $p1 $p2 $p0 $p2 $d -width 4
        :pline $p0 $p2 $p0 $p1 $d -width 4 ;# vertical short line
        :line $m+5*$d  $m+2*$d  $m+6*$d  $m+2*$d -arrow first
        :line $m-2*$d  $m+5*$d  $m-2*$d  $m+6*$d -arrow first
        :line $m-5*$d  $m-2*$d  $m-6*$d  $m-2*$d -arrow first
        :line $m+2*$d  $m-5*$d  $m+2*$d  $m-6*$d -arrow first

        set d2 [expr {$d*0.75}]
        set d15 $d2*2
        set o [expr {$u*5}]
        :pnest $m+$o-$d $m-$o+$d $d2 0 -1  3
        :pnest $m+$o-$d $m+$o-$d $d2 1 -1 -2.5
        :pnest $d15     $m+$o-$d $d2 2  1 -2.5
        :pnest $d15     $m-$o+$d $d2 3  1  3
        for {set i 0;set y [expr $d*2]} {$i<4} {incr i;set y [expr {$y+$d}]} {
            lappend p(0) [:point $m      $y      $d2 -fill [lindex ${:colors} 0]]
            lappend p(1) [:point $m*2-$y $m      $d2 -fill [lindex ${:colors} 1]]
            lappend p(2) [:point $m      $m*2-$y $d2 -fill [lindex ${:colors} 2]]
            lappend p(3) [:point $y      $m      $d2 -fill [lindex ${:colors} 3]]
        }
        #
        # Setup the path per player and color the starting points
        #
        for {set i 1} {$i < 41} {incr i} {lappend path $i}
        foreach c {0 1 2 3} pos {11 21 31 1} o {11 21 31 1} {
            ${:canvas} itemconfig [:getPointId $pos] -fill [lindex ${:colors} $c]
            set :path($c) [concat [lrange $path $o-1 end] [lrange $path 0 $o-2] $p($c)]
        }
    }

    :public method msg {text} {
        #
        # Report a message to the user.
        #
        ${:canvas} itemconfig ${:msgId} -text $text
        return 0
    }

    :public method wannaGo {obj pos {-path ""}} {
        #
        # We know that we can move the figure in principle.  We have
        # to check, whether the target position is free. If the target
        # is occupied by our own player, we give up, otherwise we
        # through the opponent out.
        #
        if {$pos eq ""} {return [:msg "beyond path"]}
        set other [Figure lookup $pos]
        set afterCmd ""
        if {$other ne ""} {
            if {[$obj cget -color] eq [$other cget -color]} {
                # On player can't have two figure at the same place.
                return [:msg "My player is already at pos $pos"]
            } else {
                # Opponent is at the target position. Find a free
                # position in the opponents nest and though her out.
                set opponent [$other cget -color]
                foreach p [set :nest([lsearch ${:colors} $opponent])] {
                    if {[Figure lookup $p] eq ""} {
                        set afterCmd [list $other gotoNr $p]
                        break
                    }
                }
            }
        }
        :msg "[$obj cget -color]-[$obj cget -no] went to $pos"
        $obj gotoNr $pos -path $path -afterCmd $afterCmd
        ${:die} invalidate
    }

    :public method moveFigure {obj} {
        #
        # Move the provided figure by the diced eyes according to the
        # rules. First we check, if we are allowed to move this
        # figure, which might be in the nest or on the run.
        #
        set currentColor [lindex ${:colors} ${:player}]
        if {[$obj cget -color] ne $currentColor} {
            return [:msg "figure is not from the current player"]
        }
        set eyes [${:die} cget -eyes]
        if {$eyes == 0} {
            return [:msg "Must dice first"]
        }
        set position [$obj cget -position]
        if {$position in [set :nest(${:player})]} {
            # Figure is in the nest, just accept eyes == 6
            if {$eyes == 6} {
                :wannaGo $obj [lindex [set :path(${:player})] 0]
            } else {
                return [:msg "Need 6 to move this figure"]
            }
        } else {
            #
            # Check, if we have still figures in the nest
            #
            set inNest ""
            foreach p [set :nest(${:player})] {
                set inNest [Figure lookup $p]
                if {$inNest ne ""} break
            }
            #
            # Check, if the actual figure is at the start position.
            #
            set startPos [lindex [set :path(${:player})] 0]
            set atStart [Figure lookup $startPos]
            if {$eyes == 6} {
                if {$inNest ne ""} {
                    # Move a figure out from the nest, if we can
                    if {$atStart ne ""} {
                        if {[$atStart cget -color] eq $currentColor} {
                            set path [set :path(${:player})]
                            set current [lsearch $path $position]
                            set targetPos [expr {$current + [${:die} cget -eyes]}]
                            :wannaGo $obj [lindex $path $targetPos] \
                                -path [lrange $path $current+1 $targetPos]
                            return 1
                        }
                    }
                    return [:msg "You have to move the figures from your nest first"]
                }
            }
            if {$atStart ne "" && $inNest ne "" && $obj ne $atStart} {
                return [:msg "You have to move the figures from the start first"]
            }
            set path [set :path(${:player})]
            set current [lsearch $path $position]
            set targetPos [expr {$current + [${:die} cget -eyes]}]
            :wannaGo $obj [lindex $path $targetPos] \
                -path [lrange $path $current+1 $targetPos]
        }
        return 1
    }

    :public method nextPlayer {} {
        #
        # Switch to the next player.
        #
        set :player [expr {(${:player}+1) % 4}]
        ${:canvas} coords ${:buttonWindow} {*}[set :buttonPos(${:player})]
    }

    :method init {} {
        set hw [expr {14 * ${:size}}]
        set center [expr {$hw / 2}]
        canvas ${:canvas} -bg ${:bg} -height $hw -width $hw
        :draw $center
        set :die [Die new -canvas ${:canvas} -x $center -y $center -size ${:size}]
        set :msgId [${:canvas} create text [expr {${:size}*4}] 10 -text ""]
        #
        # Player management (signal which player is next, etc.)
        #
        set :player 2
        button .b1 -text "Done" -command [:callback nextPlayer]
        set :buttonWindow [.p create window 22 14 -window .b1]
        :nextPlayer
        bind . <n> [:callback nextPlayer]
    }
}

Finally, create the board and pack it

Board new -canvas .p -bg beige -size 40
pack .p

doc/example-scripts/tk-ludo.tcl000066400000000000000000000365651242365656200170600ustar00rootroot00000000000000# A small Ludo/Mensch ärgere Dich nicht/Pachisie game, originally # developed by Richard Suchenwirth in plain Tcl (see # http://wiki.tcl.tk/956). The game was rewritten as a design study in # NX by Gustaf Neumann in July 2013. # # Major changes: # # - object-oriented design (no global variables) # # - knowledge about the paths of the figures # # - animated moves # # - knowledge about the basic rules (e.g. need 6 to move out of the # nest, have to move figures from starting position) # # - throw opponents out # # - sanity checks # # - user feedback # # image::tk-ludo.png[width=400] # # Short Instructions # # - The active player (marked with the button) has to dice (click on # the die, or press somewhere on the board "d"). # # - If all figures are in the nest (start position), the player needs # to dice a 6. The player is allowed to try three times, then the # player is done (press "done" button, or type "n") and the turn # moves to the next player. # # - When a player got 6 eyes, he can move out of the nest. This is # done by clicking on the figure the player wants to move. # # - After dicing 6, the player can dice again and move the player on # the field (always by clicking on the figure). # # == Implementation # package require Tk package require nx::trait # # Define an application specific converter "expr" that passes the # scalar result of the expression. Since the converter is defined on # nx::Slot, it is applicable to all method and configure arguments. # ::nx::Slot method type=expr {name value} {return [expr $value]} # # Class Figure # nx::Class create Figure { :property canvas:required :property x:double :property y:double :property size:double :property position:integer :property color :property no:integer :property board:object,required :variable tag "" :require trait nx::traits::callback :method init {} { # # Draw figure and define interactions # set d [expr {${:size}/6.}] set s [expr {${:size}/1.5}] set y [expr {${:y}-$d*2.5}] set :tag ${:color}${:no} set id [${:canvas} create arc [expr {${:x}-$s}] [expr {${:y}-$s}] \ [expr {${:x}+$s}] [expr {${:y}+$s}] -outline grey \ -start 250 -extent 40 -fill ${:color} \ -tags [list mv ${:tag}]] ${:canvas} create oval \ [expr {${:x}-$d}] [expr {${:y}-$d}] \ [expr {${:x}+$d}] [expr {${:y}+$d}] \ -fill ${:color} -outline grey -tags [list mv ${:tag}] #${:board} figure set $id [self] ${:canvas} bind ${:tag} [:callback go] } :public method go {} { # # Start moving the figure if the draw is permitted. # The board knows the die and the rules. # if {![${:board} moveFigure [self]]} { # stay at old position :gotoNr ${:position} } } :public method gotoNr {nr {-path ""} {-afterCmd ""}} { # # Move figure to the numbered position. If a path is given it # moves stepwise from position to position. # set oldPos ${:position} set :position $nr if {$path eq ""} {set path $nr} return [:move {*}[${:board} getPointCenter $oldPos] $path \ -afterCmd $afterCmd] } :protected method move {x0 y0 path:integer,1..n {-afterCmd ""}} { # # Move figure from old position (x0 y0) stepwise along the # path using animation. At the end of the move, 'afterCmd' is # issued. # set t 0 foreach pos $path { lassign [${:board} getPointCenter $pos] x y set stepx [expr {($x-$x0)/50.0}] set stepy [expr {($y-$y0)/50.0}] for {set i 0} {$i < 50} {incr i} { after [incr t 8] ${:canvas} move ${:tag} $stepx $stepy } lassign [list $x $y] x0 y0 incr t 100 } after $t ${:canvas} raise ${:tag} after $t $afterCmd set :x $x; set :y $y } :public object method lookup {position} { # # Return the figure at the provided position. This function # could be made faster, but is efficient enough as it is. # foreach f [Figure info instances] { if {[$f cget -position] == $position} { return $f } } return "" } } # # Helper functions for the die # proc random:select L {lindex $L [expr int(rand()*[llength $L].)]} proc lexpr {term L} { # map an expr term to each element \$i of a list set res [list] foreach i $L {lappend res [eval expr $term]} set res } # # Class Die # nx::Class create Die { :property canvas:required :property x:double :property y:double :property {size:double 25} :property {fg gold} :property {bg red} :property {eyes 0} :require trait nx::traits::callback :method set {n} { # # Set the eyes of the die. # ${:canvas} itemconfig ${:grouptag} -fill ${:bg} -outline ${:bg} foreach i [lindex [list \ {} {d5} [random:select {{d3 d7} {d1 d9}}] \ [random:select {{d1 d5 d9} {d3 d5 d7}}] \ {d1 d3 d7 d9} {d1 d3 d5 d7 d9} \ [random:select {{d1 d3 d4 d6 d7 d9} {d1 d2 d3 d7 d8 d9}}] \ ] $n] { ${:canvas} itemconfig ${:id}$i -fill ${:fg} -outline ${:fg} } set :eyes $n } :public method invalidate {} { # # Invalidate the eyes to avoid double uses of the eyes. # set :eyes 0 } :public method roll {} { # # Roll the dice and animate rolling # # wiggle: amount, pick one of eight wiggle directions set dwig [expr ${:size}/5] for {set i 10} {$i<100} {incr i 10} { :set [expr {int(rand() * 6) + 1}] set wig [random:select {0,1 0,-1 1,0 -1,0 1,1 -1,1 1,-1 -1,-1}] set wig [lexpr \$i*$dwig [split $wig ,]] ${:canvas} move group${:id} {*}$wig update set wig [lexpr \$i*-1 $wig] ;# wiggle back ${:canvas} move group${:id} {*}$wig after $i } } :method init {} { # # initialize the widgets with tags interactions # set x [expr {${:x} - ${:size}/2.0}] set y [expr {${:y} - ${:size}/2.0}] set :id [${:canvas} create rect $x $y \ [expr {$x+${:size}}] [expr {$y+${:size}}] \ -fill ${:bg} -tags mvg] set :grouptag group${:id} ${:canvas} addtag ${:grouptag} withtag ${:id} set ex [expr {$x+${:size}/10.}] set ey [expr {$y+${:size}/10.}] set d [expr {${:size}/5.}];# dot diameter set dotno 1 ;# dot counter foreach y [list $ey [expr {$ey+$d*1.5}] [expr {$ey+$d*3}]] { foreach x [list $ex [expr {$ex+$d*1.5}] [expr {$ex+$d*3}]] { ${:canvas} create oval $x $y [expr {$x+$d}] [expr {$y+$d}] \ -fill ${:bg} -outline ${:bg} \ -tags [list mvg ${:grouptag} ${:id}d$dotno] incr dotno } } :set [expr {int(rand()*6)+1}] :invalidate # # To dice, let people click on the die, or press on the # board # ${:canvas} bind mvg <1> [:callback roll] bind . [:callback roll] } } # # Class Board # nx::Class create Board { :property canvas:required :property {size:integer 25} :property {bg LightBlue1} :property {fg white} :property {colors:1..n {red green yellow blue}} :require trait nx::traits::callback :method lookup {var idx} { # # Convenience lookup function for arbitrary instance # variables. # set key "${var}($idx)" if {[info exists $key]} {return [set $key]} return "" } :public method getPointCenter {nr} {:lookup :pointCenter $nr} :public method getPointId {nr} {:lookup :pointId $nr} :method line { x0:expr,convert y0:expr,convert x1:expr,convert y1:expr,convert {-width 1} {-arrow none} } { # # Convenience function for line drawing, evaluates passed # expressions. # ${:canvas} create line $x0 $y0 $x1 $y1 -width $width -arrow $arrow } :method point {x:expr,convert y:expr,convert d {-number:switch false} -fill} { # # Draw a point (a position on the game board) and keep its # basic data in instance variables. We could as well turn the # positions into objects. # if {![info exists fill]} {set fill ${:fg}} incr :pointCounter set id [${:canvas} create oval \ [expr {$x-$d/2.}] [expr {$y-$d/2.}] \ [expr {$x+$d/2.}] [expr {$y+$d/2.}] \ -fill $fill -tags [list point] -outline brown -width 2] #${:canvas} create text $x $y -text ${:pointCounter} -fill grey set :pointNr($id) ${:pointCounter} set :pointCenter(${:pointCounter}) [list $x $y] set :pointId(${:pointCounter}) $id return ${:pointCounter} } :method fpoint {x:expr,convert y:expr,convert psize fsize color no} { # # Draw a point with a figure, note the position in the board # in the figure # set nr [:point $x $y $psize -fill $color] Figure new -board [self] -canvas ${:canvas} \ -x $x -y [expr {$y-$fsize/2.0}] \ -size $fsize -color $color -no $no -position $nr return $nr } :method pnest {x:expr,convert y:expr,convert d colorNr xf yf} { # # Draw the nest with the figures in it # set fsize [expr {$d/0.75}] set color [lindex ${:colors} $colorNr] lappend :nest($colorNr) [:fpoint $x-$d $y-$d $d $fsize $color 0] lappend :nest($colorNr) [:fpoint $x-$d $y+$d $d $fsize $color 1] lappend :nest($colorNr) [:fpoint $x+$d $y-$d $d $fsize $color 2] lappend :nest($colorNr) [:fpoint $x+$d $y+$d $d $fsize $color 3] set :buttonPos($colorNr) [list [expr $x+($xf*$d)] [expr $y+($yf*$d)]] } :method pline { x0:expr,convert y0:expr,convert x1:expr,convert y1:expr,convert d {-width 1} {-arrow none} } { # # Draw a path of the play-field with points (potential player # positions) on it. # set id [${:canvas} create line $x0 $y0 $x1 $y1 \ -width $width -arrow $arrow -fill brown] if {$x0 eq $x1} { # vertical set f [expr {$y1<$y0 ? -1.25 : 1.25}] for {set i 0} {$i < [expr {int(abs($y1-$y0)/($d*1.25))}]} {incr i} { :point $x0 $y0+$i*$d*$f $d } } else { # horizontal set f [expr {$x1<$x0 ? -1.25 : 1.25}] for {set i 0} {$i < [expr {int(abs($x1-$x0)/($d*1.25))}]} {incr i} { :point $x0+$i*$d*$f $y0 $d -number } } ${:canvas} lower $id } :method draw {m} { # # Draw board and create figures # set d ${:size} set u [expr {$d * 1.25}] # # Major positions: p0 .. p1 ..m.. p2 .. p3 # set p0 [expr {$u-$d/2.0}] set p1 [expr {$m-$u}] set p2 [expr {$m+$u}] set p3 [expr {2*$m-$u+$d/2}] :pline $p0 $p1 $p1 $p1 $d -width 4 :pline $p1 $p1 $p1 $p0 $d -width 4 :pline $p1 $p0 $p2 $p0 $d -width 4 ;# horizonal short line :pline $p2 $p0 $p2 $p1 $d -width 4 :pline $p2 $p1 $p3 $p1 $d -width 4 :pline $p3 $p1 $p3 $p2 $d -width 4 ;# vertical short line :pline $p3 $p2 $p2 $p2 $d -width 4 :pline $p2 $p2 $p2 $p3 $d -width 4 :pline $p2 $p3 $p1 $p3 $d -width 4 ;# horizonal short line :pline $p1 $p3 $p1 $p2 $d -width 4 :pline $p1 $p2 $p0 $p2 $d -width 4 :pline $p0 $p2 $p0 $p1 $d -width 4 ;# vertical short line :line $m+5*$d $m+2*$d $m+6*$d $m+2*$d -arrow first :line $m-2*$d $m+5*$d $m-2*$d $m+6*$d -arrow first :line $m-5*$d $m-2*$d $m-6*$d $m-2*$d -arrow first :line $m+2*$d $m-5*$d $m+2*$d $m-6*$d -arrow first set d2 [expr {$d*0.75}] set d15 $d2*2 set o [expr {$u*5}] :pnest $m+$o-$d $m-$o+$d $d2 0 -1 3 :pnest $m+$o-$d $m+$o-$d $d2 1 -1 -2.5 :pnest $d15 $m+$o-$d $d2 2 1 -2.5 :pnest $d15 $m-$o+$d $d2 3 1 3 for {set i 0;set y [expr $d*2]} {$i<4} {incr i;set y [expr {$y+$d}]} { lappend p(0) [:point $m $y $d2 -fill [lindex ${:colors} 0]] lappend p(1) [:point $m*2-$y $m $d2 -fill [lindex ${:colors} 1]] lappend p(2) [:point $m $m*2-$y $d2 -fill [lindex ${:colors} 2]] lappend p(3) [:point $y $m $d2 -fill [lindex ${:colors} 3]] } # # Setup the path per player and color the starting points # for {set i 1} {$i < 41} {incr i} {lappend path $i} foreach c {0 1 2 3} pos {11 21 31 1} o {11 21 31 1} { ${:canvas} itemconfig [:getPointId $pos] -fill [lindex ${:colors} $c] set :path($c) [concat [lrange $path $o-1 end] [lrange $path 0 $o-2] $p($c)] } } :public method msg {text} { # # Report a message to the user. # ${:canvas} itemconfig ${:msgId} -text $text return 0 } :public method wannaGo {obj pos {-path ""}} { # # We know that we can move the figure in principle. We have # to check, whether the target position is free. If the target # is occupied by our own player, we give up, otherwise we # through the opponent out. # if {$pos eq ""} {return [:msg "beyond path"]} set other [Figure lookup $pos] set afterCmd "" if {$other ne ""} { if {[$obj cget -color] eq [$other cget -color]} { # On player can't have two figure at the same place. return [:msg "My player is already at pos $pos"] } else { # Opponent is at the target position. Find a free # position in the opponents nest and though her out. set opponent [$other cget -color] foreach p [set :nest([lsearch ${:colors} $opponent])] { if {[Figure lookup $p] eq ""} { set afterCmd [list $other gotoNr $p] break } } } } :msg "[$obj cget -color]-[$obj cget -no] went to $pos" $obj gotoNr $pos -path $path -afterCmd $afterCmd ${:die} invalidate } :public method moveFigure {obj} { # # Move the provided figure by the diced eyes according to the # rules. First we check, if we are allowed to move this # figure, which might be in the nest or on the run. # set currentColor [lindex ${:colors} ${:player}] if {[$obj cget -color] ne $currentColor} { return [:msg "figure is not from the current player"] } set eyes [${:die} cget -eyes] if {$eyes == 0} { return [:msg "Must dice first"] } set position [$obj cget -position] if {$position in [set :nest(${:player})]} { # Figure is in the nest, just accept eyes == 6 if {$eyes == 6} { :wannaGo $obj [lindex [set :path(${:player})] 0] } else { return [:msg "Need 6 to move this figure"] } } else { # # Check, if we have still figures in the nest # set inNest "" foreach p [set :nest(${:player})] { set inNest [Figure lookup $p] if {$inNest ne ""} break } # # Check, if the actual figure is at the start position. # set startPos [lindex [set :path(${:player})] 0] set atStart [Figure lookup $startPos] if {$eyes == 6} { if {$inNest ne ""} { # Move a figure out from the nest, if we can if {$atStart ne ""} { if {[$atStart cget -color] eq $currentColor} { set path [set :path(${:player})] set current [lsearch $path $position] set targetPos [expr {$current + [${:die} cget -eyes]}] :wannaGo $obj [lindex $path $targetPos] \ -path [lrange $path $current+1 $targetPos] return 1 } } return [:msg "You have to move the figures from your nest first"] } } if {$atStart ne "" && $inNest ne "" && $obj ne $atStart} { return [:msg "You have to move the figures from the start first"] } set path [set :path(${:player})] set current [lsearch $path $position] set targetPos [expr {$current + [${:die} cget -eyes]}] :wannaGo $obj [lindex $path $targetPos] \ -path [lrange $path $current+1 $targetPos] } return 1 } :public method nextPlayer {} { # # Switch to the next player. # set :player [expr {(${:player}+1) % 4}] ${:canvas} coords ${:buttonWindow} {*}[set :buttonPos(${:player})] } :method init {} { set hw [expr {14 * ${:size}}] set center [expr {$hw / 2}] canvas ${:canvas} -bg ${:bg} -height $hw -width $hw :draw $center set :die [Die new -canvas ${:canvas} -x $center -y $center -size ${:size}] set :msgId [${:canvas} create text [expr {${:size}*4}] 10 -text ""] # # Player management (signal which player is next, etc.) # set :player 2 button .b1 -text "Done" -command [:callback nextPlayer] set :buttonWindow [.p create window 22 14 -window .b1] :nextPlayer bind . [:callback nextPlayer] } } # # Finally, create the board and pack it # Board new -canvas .p -bg beige -size 40 pack .p doc/example-scripts/tk-mini.html000066400000000000000000000444771242365656200172340ustar00rootroot00000000000000 Listing of doc/example-scripts/tk-mini.tcl

Tiny Tk example scriped based on NX.

tk-mini.png
package require Tk
package require nx::trait

nx::Class create MyClass {
  #
  # A sample application class that creates a text entry field bound
  # to an instance variable. When the provided button is pressed, the
  # content of the variable is placed into an additional output label.

  #
  # The callback trait imports methods "callback" and "bindvar":
  #
  :require trait nx::traits::callback

  :public method button-pressed {} {
    # When this method is invoked, the content of the ".label" widget
    # is updated with the content of the instance variable "myvar".
    .label configure -text ${:myvar}
  }

  :method init {} {
    wm geometry . -500+500
    pack [label .title -text "Type something and press the start button ..."]
    pack [entry .text -textvariable [:bindvar myvar]]
    pack [label .label]
    pack [button .button -text start -command [:callback button-pressed]]
  }
}

MyClass new

doc/example-scripts/tk-mini.png000066400000000000000000000316151242365656200170420ustar00rootroot00000000000000‰PNG  IHDR'q˜òHYiCCPICC ProfilexÕXeTU]·^{ŸäÝyèîé.éé.i0@BBQJJQ@PD”•DBPÁQEE»ñú½ãŽ{ÿÝ?w±×yÎ\ó¬½Î~ÖÚsÎÖ"Ïðð˜€Ð°èH#]Š“³ ÷à  Àçé®ceeþ×¶ú @»ƒã’»sý¯nÿóo”72ìååŠà[^ôŒ®BðãƒÑáF!`ŒDˆà»Øÿ\¶‹½þÁ ¿}ìlôŸð$OÏHˆ;%ÖÛ™ƒ¸–>Ì'0 z4‚5½<}`ÕG|$BCìâp‹xýkÿaOO¯¿szzúÿÅÿüä—Èõ£ÂC<ýþòÙ…†Ä Ïëw£GzRXÈÞ]nðÈ5çã©oö‡‡üæì·Ý7ÌÞö=Ìk¯åìihó‡Gëþ [Ùý±Çèíýƒ}£ þÎäijõÇccÿGÅÚüÁñvް¯þ_»_ ¡É{`´Éß{0û»,€'ðŽöÛåè?èMÑAv™¯Å$Ì[J‚"'#+»;üÿ¦íž¯»lóûÜ@Ì#ÿ±E!ÏTm9cÿ±y$ÐêŠlqåÿØø õ“Üã÷މŒýg>d+€@àü@H9 Ô60¦ÀØgà¼A‘à HI dÓ ”€rP ®€k ´€»  <ƒà1x ^‚)ð|‹`lB„ƒÈÄñ@‚8$©@šdÙ@Îä…A1P"tÊ‚r¡è"TÝ€nC]P?4 =‡¦¡yh Ú€Q0 f„¹`!XVu`3ØÞûÃp<œŸ‚‹à ø*Ü wÁƒðSx þ ¯ ŠˆbFñ¢$Q*(=”%Ê凊DAe¢ Q¨zTª5ŽšB- ~¢±h4-‰VG£íÑÞèôt6º}ÝŒîA£§Ñ‹è_2†#ŽQØ`œ0þ˜ƒ˜4L!æ2¦ Ó‹yŠù€YÅb±ÌXa¬2ÖëŒ Â&`³±ç°×±ØQì v‡Ã±áÄq8Kœ'.—†+Æ]ÅuàÆppëx"ž/‡7Ä»àÃðÉøB|-¾?†ŸÅoRÑR R©QYRùP¢Ê¡ª¢j£¡ú@µI #4v„ B¡ˆPOè%¼",‰D>¢*ÑšH…¾’þ>ý ŠŸAÁ›á8CC/ÃF,£0£ ccã5ÆaÆE&z&&¦8¦R¦{LSÌ(f!fæææFægÌ,\,:,¾,,õ,c,k¬¬Ú¬¾¬™¬×YŸ²n°QØ Ø‚Ùΰµ°½fG³‹±[³d?ÏÞ˾ÀÁÈ¡ÎáÍ‘ÉÑÈñ‚æã´áLà¬äâ\áâæ2â ç*æºÏµÀÍÌ­ÍÄÏÝÎ=ÏÃÀ£ÉÈ“ÏÓÁó‰ÂDÑ¡„PŠ(=”E^N^cÞÞ‹¼Ã¼›|Â|ö|É|×ù^óøUøýøóù»ùx,ê^R ªžì\rJjšf6Ž®~%Bщ©y"ŠU ='úX S +‡Å•ÄÅωJ`$T%Â$*$&$I’:’±’u’ÓRÌRæRÉR-R_¥¤]¤ÏH÷Iÿ’Q” ‘©’y)K/k*›,Û&»$'&ç-W*÷Dž,o(T¾Uþ»‚¸‚¯Ây…IEE ÅtÅnÅm%e¥H¥z¥yeeå2å F+•l•‡ªU]Õ£ªwUª)©E«5ª}S—TV¯UŸÛ#¼ÇwOÕž > O‹SšMÍ šSZ¼ZžZZï´ùµ}´/kÏêˆêé\Õùª+£©Û¤»¦§¦wX¯S¥o¤Ÿ©?l@o`oPbðÆÏÐß°ÎpÑHÑ(Á¨Óclf|Æx„ËÄÛ¤ÆdÑTÙô°iÉÌÖ¬Ä치y¤y›laj‘gñj¯àÞ°½-–ÀÒÄ2Ïòµ•°U„Õk¬µ•u©õGY›D›>[[wÛZÛU;]»»—ö"ö1öÝ4®5kŽú޹ŽSNÒN‡Ù[]p..—]Vöì+Ø÷ÁUÑ5ÍõÙ~áýqûûÝØÝBÜî¹Ó¸{ºßôÀx8zÔzlyZzVx®x™x•y-zëyŸõþì£í“ï3ï«á›ë;ë§á—ë7ç¯áŸç? P°¨Xø=È8¨Ô#ôv}XpXÏîqFÃÅÃÓ§"Ô" "#Í"/GAQû£Z£‘Df(F$&5f:V3¶4vý ÃÁ›qtqaqC‡Äeš7Œ¿”€NðNèNäMLJœ>¬søâèˆ×‘î£üGSŽ~8ftìJ!)8éQ²LrnòãŽÇÛR¸RŽ¥Ì¤¥Ö¥Q§E¦M¤«§—Ÿ@Ÿ<1œ!ŸQœñ+Ó's K&«0k+Û;{à¤ìÉ¢“;§üN ç(åœ?=vúÙ­3Wrérãsgò,òšó)ù™ù? Ü ú  ËÏÎÆœ*2/j-(>]¼UPò´T·ôzgYFÙÚ9ŸscçµÏ×—s•g•o\¼0yÑèbs…PEa%¶2¶òc•CUß%•K5—Ù/g]Þ®«žºbs¥§F¹¦¦–³6§®‹©›¿êzõñ5ýk­õ’õ¯3_Ïj 1 ŸnxÜxÖhÖØ}Såfý-Á[eM M™ÍPó¡æÅ–€–©VçÖÑÛ¦·»ÛÔÛšîHÝ©¾Ë{·ôÓ½œvB{JûNG|ÇJgxçB—×L·{÷ËûN÷ŸôX÷ ÷šõ>|`øà~ŸN_ÇC‡wûÕúo¨ ´ * 6)5=R|Ô4¬4Ü<¢<ÒúXõqÛèžÑö1­±®qýñOLž >Ýûtô™ý³É ׉©IŸÉ¹ç!Ï¿¿ˆ}±ùòØ+Ì«Ì×´¯ ßp¾©x+úöú”ÒÔ½iýé¡w¶ï^ÎxÏ|~õ~ëCÊGòÇÂYžÙš9¹¹»ó†ó?íûôásøçÍ…´/t_ʾŠ|½õMûÛТÓâ‡ï‘ßw–²—Ù–«(üè^±Zy³ºº¹–¹Î¶~å§ÊϾ ÇÙ̓[¸­¢mÑí¶_f¿^í„îì„{FzþÎPHûù°T نǨÿÉ{ é1’´Ã¦C²‚NÈ ú \К¬!Ιjš¨A*¦†hBißÓû0|fòfžfõa{ÃaÁÙÌÍÅIiã]âçPÔ2ÖQUç– “DK®IÍI¿’ík”¯TÈULV WÞ¯b¬ª¨Æ­ŽUÿºgB£S³V«D;G'S7C/K?Ç ×0Ï(߸À¤À4ßìŒù)‹¬½',S­Ž[·I±=n—bŸâì˜ätÔ9Á%n_”kØþ@7wo_Ï`¯ï$ŸS¾¥~Õþ7Ú¿™ ][ GEÐG F©F[ÅøÇ>x&®úP[üpÂtâÒÔQÚcìI”dþã|)”TÎ4ÖtÆ4„LtævÖjö·“s§¦s^ž<ó4wWÍýÊn²îâ"žÂ^"Þb>¾Ò~2þ²²²A2ÁÒ!’¡H¨= ®¡iå“{éàí¸áCÓñ+‰¸ÃlGD*ÓK2M¶ÏPNwþ"ck%O•ð%éËJÕWôkÌjmê¯î¿æUï=¬!öÆÑÆŒ›·.6]k¾ÝÒÓ:rûEÛÌ/w×ÚA¾“¦‹¹›ó>o`¯èÉ>™‡òýŠʃªCêö kŒh>ÖÕ37~böÔò™Í„ݤãsçû^º¾r{íöfÿ[—)Ûiãw*3‚ïißÿüðîãÀlã\É|Ò§€Ï– J_¸¾¢¾~þöd±ë{ÃRÅráì•¤ÕØµàuŸö¦›š[²Û‚¿ØvHÿÿ8” šˆX€ãÁÛP ‘DÉIÔimé1h3Þd–`©bceÏäX䲿®æùÆ+ÃçÎT @°J¨áÿ¦h“X“x“D£äu©zé«2Wd«ä.È—*œUÌUÊT>®’¨©æ§î²ÇBC[SaŸ_‡[—SKŸË€bH1¢s›p™²™1›ÓZöâ,aË«-ëM› Û-»-û-‡MÇŸN«ÎK._÷Í»¾ßÿÖí¥û¤Ç3Ï ¯çÞ¯}¦}gü>øÏÌÎÍῠΆÍø¾ GÑG Ä(ÅšÜz(1>;¡,±öp Mû &õ'÷¿“Ò˜ZV–~úDjF\fpÖþlË“:§äs„OsžaÌ%çQåc P… pûìÏ¢•âÅ’Ï¥ʦν8?^>raèâ@Å@å`ÕÐ¥áËÃÕ#WFjFjÕ=º:xm ¾ÿzÃÀG£7ŸÞzÑô¶y¦e¶õÓí/m‹w–îþ¸·Ò¾Ö±Ö¹ÖµÞ½~½g½wíÁjßÊÃåþïß¿ }zôqxfdêñ«Ñɱññá'Ÿv?»;Ñ4Yÿ¼úEùË¢W9¯Óß~5å?íüÎdFå½Ð†àãÂìä\Ï|çÒÏi _\¿}SXäÿβD³LüAX!¬Ö넟TøMÜvó µíòÿ²°J\Fjtû&Ì‘ô¼1‹Hü ÀŠ €*€ù' çh;üoüÀ :RmŠ •¦0¶À„€C ©(«ÀMpŒƒ÷`ÂC4¤‡Tˆ tèÔ=…–`:Xv€áËð©é ªQ3HÕæ®@ÄHbb0XvöÂ9ánà©ñ!øa*yªŠp€ð’hDl$q‘2H«d_òµuM­<í-:%º;ôºôƒ ö SŒaŒ¿˜N3 2ßeq`YfÍeS`{Î~ŒC”cŒ3‹Ÿk;š‡›g€rˆWŒ÷5_¿¹N G0EÈ@'Ü/’%j!F+6.^,á))!¹.õPºX&TV_ŽKnCþ•Â}Å¥ åb•|Õ\µ<õ³{ÎiTkÞÒêÑžÐYÐC#{[ÙÐÆ(Ì8ˤÖtÈì»Û^}ËH«Jë [j;cûd‡{Ž«Î.žû \Ü w5XÏ&¯U5ß$¿Á–@ÿ ¶ºÐ°p‰ˆ3‘?£}bÆêŵÄK%T8RuL0éÊqñ”ëiŠé÷2Œ3'²OþÊ)<#›;’QÈtö^q`)GÙ“óyœ+*\zP]V[g{Mþ:KÃNãü­ÉæÁÖûmÝwûÚÇ;gº×{éú$û-#ôŽþx"òÌ}²øÅ³×Œom¦sgÆ?²Ì¹ª]Øøfñ½jygÕm½sSxûÌï÷þy€(šˆÆà¼ÀDWÈň–Ð ‚çàØ‚h ~HѼ¡xD¸ õAïaæƒ à 8n‡¿¢(({ÔIT…ÖA'£{1DŒ ¦ 3‹UĦb'p¸ã¸×xU|~ƒÊêA†PF$ãˆI¤dUr=µ u9 'M)-m(Ý-zmú7†¯ŒG˜h™*™U™GYBX ¬µllË쥆˜¹¬¸v¸x¼(Ì”!Þ4>~Àß%*h&Ä(ôV¸Nä ¨ŽAlL¼DÂë7÷}ÒE2Á²:rlr?äGÏ*S> â­êªæ¤î´ÇEÃC3@+Z;Y'O·Z¯]Â`Ɉl,fblh–e~Ãâ¹%ÚJÎÚÛæ¬í°=ÞÁÀñ¸S—óÎ> ×øý­nkªžG¼z}h|]ýêüÚÕ…`BÝÃî„3GDE>Ž–Ž9»çzèA‚\bÅÆ£IPòá㛩‡ÓQ'Nf²gÕžÔ8õäth.U^MYá÷¢âƒÒås•åöñ÷ª¢/KVÏ×ÔÔ_“©_oèi̽åÓ¬ÞÊr{ëν¹ŽïݨÎj]’†ê†'G ãZOã&n=_z%ùÆgªð]ïû¥YʼÍç´/ß6–T$®ÞÿIÞtÙ®ûË?3 1 ˆ¨LfÀ ø ÊÒQp ”«ˆŠ4^ƒo 1A¢ˆJd AIˆ&t `,[À‘p Ü /£øQ¨lÔ}Ô6Z‹h4ëuL¦Ë€uÇ^Ãnã¬p—p[xüM*zªhªI‚&á‘LŒ%N‘ÌH­ˆòQDM¤N¦Þ ‰¥Y¦¦]¥K ‡é3Ê%̘ۘÞ2ǰP³Ô²³Î³d—gÉ‘Â)Íù’+[‘û=O!ÅŒæ½ËǯÌÿS 9ÿæÂLH}_)(&-¶*Þ!‘)é(%,µ!="sYö˜œ›¼¦‚€"­JiSyCeK V'îaÕÖTÓ²ÒÔIÕ­ÒëÕŸ5ĉ›™„šž6k1ŸÚK²T· µ®°yaÇdoëïøÌ™Õeß¾ó®ïÜDÜÃ=îz¼]}üðþ^A<ÁI!ïÃŒ\ <õ6Æ4¶5NôйÆÄSGˆG³’¨“óS¸RkÓUO dºg­ž<“#uz$7*Ÿ½ çlD1oÉÓ²¬óú ‹]•©—Ì«Y®¼¯m¾šYïÕ ÑÈys»i¦eèvÛk÷ª;®tݸßÑ;Ú77€âÖ{8–÷¤óÙ÷ç‚/]^g½mžžœùñ‘<'ôIgÁýkÒ⥥G?Öׄ~:mfo÷ìòå'/·=DÒEäÇ7;;ËBàrØ}?lVììlW"ÅÆ+:CþÑÖw±´”!q€ñ3Çv?ÿÝþ bD~'½ÿþ pHYs  šœÚIDATxí] tUº®îª®Þ;ûJBX„ÜA "¢<œÐ33ó9Ã8 ¨3‹">At\ÎA”áãSD%QTà "„}'K§;®î÷U_¬ÝÕÀ„¦ÒÜ:‡â¯[ÿv¿ª¯îÒ·*»ÝÎ0Œ×ë%{"H%ä²t–”·}zÏ×oi`túd³nèä!·§pm·¥š5# ÑhHz’€CI†@dùž‘š››«««].— 8Ä3iO©„¸Ú}y“3«7Ç1Ínak òœWë€êSÔ‹aòæ#¡´‡À²,Ïó±±±:ŽÔA¾•––‚ZÑÑÑØ{<õVŽfFèhhµZïÒ¥KØ'''ƒx8°L3›Íµµµhô(ë:Úe¥ùª°L3™LÊk "룢¢jjjнTuú49Š@D͘ÛíÆð-&& ÁCí[CCö°F7,e§Ó‰GZÒ ©CìIZ8e±Xôzý Ë’V  aéKrhâ‡üî‘R…8ŠÆ°´ à‡O?¶_,d½â¨ÏÍh-iYã'$§§£éÄHô•¬$A2¯*È/Þý1[T`ðÍÓ85¡Szjö„¸ôŒ¶˜–æøþÓ²†"†Åd‡ñ°É–ôýǧ%‡2—Ò¸ÂO?ýÔ½{wpTœ0MNBÔÝh4ž={6++ëz$@}v8À2ÜdÂRsðàA̱(ÖwU}}ýÿåì)Ü“c³Zã““£SS/s©¨¸¢¬¬¾¡.ížìãî±Z­ƒ!Ð1Ïßû‰vÿnKtLtjª)5·¦½¤ ¶¸¨¾ºÆ32;c̽¡ÍÝýe~Ž-Úšœ“ x„Ê겊² {cXƸƒ³ƒ™æÓŽ%Ÿþù¤I“ð<Âx,‰{äá%í?üðÃQ£FµW¨?ýý9K÷þ¬Ê¢uaÕpœ>á0wÍL‹ kØŽ ”ëÖ­èÆÎ˜17 n”À­®®îûÜ=%Œmp§Ô;³ÇÿÍoú »3Éí6–•ºKNrè¸Øôt°NÑüâ¾Ýúý%&{ Hê6nTæ/M¿óŽh«ËÔ\âqÚ›OýP£áL:3?p4çàù]1©–ŒžF ÌþÅ]3vêÖ:ìle³à:sñ‹KW6̧KÊˡZ\\üÙgŸa?dÈÌ£?I¶‹/âá‚qó™3gRSS[ëqœ]ø«9ú‘¿ê›À·ªÜ6ûçW~Rßyh—¨`úgþëËþÛÞûöt+L'H¹cפ‡_L>­bZ¬ŒxÝáé!Žë‚µu•ÅÅå9¹=ô|?ŽMjrZòò\G¾a<Ë…¼ž®&›Ž3êù 9¹•Y}1ø$ª).Ò¹3)ËèÁã]s¹»ôF?¬«,:ÁËgt|SÙ—;k2û)š—T>»”Kêb6[¹zwùùŠMn§ƒ©±F<œEãe Ð3]9z`>í[’’’‚§Ò·ß~[VV–––öÀÿ8ÙF <ÎЩPˆ+\:°s_Üð{³âÅQÚʆaE]%eûV‹<sXšÜ¡·¹ñÈO­3{<üî®ÌíŠS—<Ü ¶¤jJ´Œ ®x6ŒÜ¤ïüÐ)Ѭ:ññ»gþóx¹‚¡¯Hpž[?sáÿ|ñÅߎ3nÜøeÛKì;×L3f̨Q>XØàÓ²ç¾ðøK9?A¾¬øðÖ¥ãî¾ûî{mþ¡ÚEÊ×Í\˜{èûŒ°SÈÍyb÷[3`éÛ><ëÓGºŠUûÙ ý_ÒÖ‰Íø r—øí­uõ‰¼Îf4˜xNÇé´XÑ…yÌŒ FVðhÝÍÞ¦æDÆY[[§èÁflŽJ‰5ÄÙôQ&Îh`õ:FüžáàÒ³F½†ÓEÙÝõâÏô~¡ÅËjt%¤ÆDYm¼ŽÕ±ZN£Å³×Zððn ½Þê-ª«Q4tØŽ% ñ†ùÛGyäôéÓ‡B+0zè!<ÌÈYÂ:ÿ¸\Âýÿ¹ð»%/ ˜<'»{L"‡*‰ît¼ÁU¸oÊüÕ·Ìyáþ¾±Á*åõ4×äûç“Çx|Õ+S‹Ö.Ý0í¾=ï_´aCÜ{¿[þ®ñÿüíPPøRÁ‰3]Ä+{Yÿ‰c#æ<ýÊ/…m¯x}Ï]«§ôBùOyÇ:9 #› ÈíÔŽó7îÏžóô3£z;Ë‹ùhèmïyâ_5zÒÖaÜ!Žë‚upKé9ÉfÕÌRŠ{£È:W“¦±QëtzN=S§ÃÄ¢R×H«×ël&}´Eg5±^Ës ç[‡&4k\: 8¬Ñê.9µz»¢¹7 i1˜tœV•l¬N¯EÌÛäq;ñ³£GгB½¡YÏ;ÍCV¿=Ob™ÏðáÃ1Ùû¯ýë¾ûîÃrïJ¹±iýz'3LFï~™]1®ƒŠ`b˜â“»žyq]æ´gžýÅ _·O¨,¸Xïnqæf¸”ÎéfAhd˜_þéÙÃ:1B\W†á§=³î·ÃYÆÑ…Ñy·öŠÜ¼®s;6î7_¼àw‰ÙøxìðKœ_C¸¥&7«DÚ:4Ú»`U¾K†«¼”‹ŽÑ˜Í«•±Ú‹EDÌáÐÔ×1õ œ½Áåtâ“=¸ÍqÍà‹ÙÄšŒZ¯A[¦asóZÖ…f .sæbù•[BTŠçh8ÎÀrFNoàxÜ(Mî&NÓÔ$¸ŒÛãd¢S£_鬤¶~«|ÛÃ?¼zõêO>ùäÎ;ïÄÄSnëðähnÆ‹žf§Ç#>†¼/š›w^\Ç0ýWþÇÜåú8¿ÞºðåÏZÖ‰k5÷nüßEŒ¶‰‰QЂ0*)1Nl€íÆÉ¼ íœC?½{¢Ïk³‹a,¾v åbhŸL²½œ³,7âÿ–¡ÝN‰¨ˆ¤#Îå†Tnp ›8›‚ÿ._å#}ï^••n[ÅØ¢ìMftŸ¼vF¯×ðzc«JËù^½=IY—Îfà7tƒ^¤Ö}ê|“ZAP¢ÑÔ µ n¨aLœéÛóLÕÎËêYÞÈñfQÏŠ÷(«a½ ºp.ôpk+½âú)Ftx=JÐgøè£zöì‰QÏW_}Ö½ùæ›O<ñ~B@¸À{šä€ÞnyVë#ˆÈ:ƒa¦/[Z¾rÕ“Ó^^¿}Aw *¨Ï^ò^ö¿¬±pA´õøFã0ÄéÆ&A,'?0ÐG)±œÄ"úÐAï%¤\ÌÐ'Ëc}’›W£ÅãàÈ÷yÎ{»Ê&:[œË ©Ü*àšØÍqm 6EÛ!µçòÎ1LßèmT”ÆfcŒè 1΋–J«Í«­eR;Ù† nh–õ„~öeÎã8v®¤ÌÑ9Ö†!œØ¥Ä¯WÃaxÆxuÞ’R‡CКû±»èž0°¨áûò‹u=3SÐ=)«E[çÕaª[ûOi~…‘7vOÔdoú9f˜þǽJ"á÷:}ú éOÁæ0}ÌùöØÃâû¼Ö7‡iN:ëÊš_/[8_ÿîì ãåÁ¡_•0á)²ërïQ”A,ß\¾ñÞ×â}=L$‰r¹>º‡P!ú¤Ü§"±uîó'þ°ééi&ï¹¥Mœ=|ß«/nÉŠžzWgY~‘3ùÖ¾VD%Îýr£‡¡™&ˆ“gb[IQÓÙÖÑ£*Ž~—Ïé:›Ì(‡U…×ág(Á}ÑÞXÆêl#G‚rŠì.Æqoqág|´%¥«A~áž--µ—Ö ïuНõ)$. NŸp²äã*K½1MïÑz·x¯ã†w»½•Euµ•ö[;ß5EsŵW!èW'Ožµë¯"xcþëå!½M’óöÂó&ñ#µuš 6têÔI±ÚPŠ×`Ê©»vžQ\ù¥q49kêj+.äk3{z“’1cŽžj bî®<ÇWLê›d3YxÖawU•Ö•]¸äŠÎÅwm^+Tœ®Ø“c޵™LÜJv‡³ªª®¼Úž•4.ŠMf˜O;–=zôÁÄ?uâW;BBøG‡s*XÕ*·Þzë|0xðà`q]ŽÃ[Œ²¾[0Õ°—û冥º¢§Ï+=ž]G XTT4`ÀÌ·iÖ­[‡¥Šõ ý(/Údªúö[Öá4™M M£½Q0êㆠ½Ô؈›¶d!¢Ÿ“s³¡äÄ^^(޶bD¦©­Ó4±))ýÆ\²;Ûbn‰2Ÿ*úúRS>Ç5£šu6CFß´Ûjí!Ìý’ißCô$ãââ0%…ù(TYU‡^¥ïç Ð}péaÈ×¾¡©·Ž‹@aaáÀ±â_œÂ¬&èrTTTÔ ñƒá÷_Ò¦™ÜnL””•a}3lɧèá²y½!¾÷hÉ\OÌK+Úl^ß#~ˆÕz7‰Ž&ÑK K[5WL©] ñœ¯ðÐ’Z9É-ΑC¨…ÀV2¡ÂM‚nÜ0Ø·Â:ÜCPÂâú‚‚<Îq€ˆ%¹áˆB0Ôn¬y°¬þýrŵ¢[Ê:EXnÎBÜ dYøÀ:âP;`k‹-Œn¬y@Ö´€"p \Úʺ# Iˆ,HC'²ÿ0áYµ£µ¡¨¬’'i‰ï³ª1AšE â×ݾwq•¥¢¨ Ê:u]šÍÍ€ÀMÁ:,QíµTmnªMLº”êÏPJÕOhaê¸ùiw”CT¤-©¶Q­-®"@çz£qmþ¯ÍJñrø¹ò;T4¹N…-ë'ñB4‰l$ù:EU‰Û›¤š*A›¦!!€9LIöð ¬Ûo¿c''°ëŽ;îÀ;œ %>2lØ0,å\¶l–üüŒ¾víZèôïßÿñÇ'£ËÏéþýû'Nœˆwað¡È;v³MMMO=õ c[¾|9I9mܸßÀë¢ø>ÂæÍ›±2>—""y†aO¢ùÎ;ï "âbÕò©S§$ÿ’ ¨€EgxYÕDPTYîbU;óæÍÃú:|­hÖ¬YÀ'´OÀµtéRɧäG. "Â(Çû{#GŽ$™\dÅB)(qˆ=H¹"&Á‚J® Æ"n}î/wCBà&Õ1ÐJr0”äj’J H…DÀ©7¡"òW+ksÎà…åÍf³ÝÿýÛ·o'§>Œ{ «~qxäÈ‘;wâí2,ðÅúi¢Vœ8qâý÷߇&>q¹fÍš@¿K–,Y°`ÁñãÇñ:̱cLjÂË/¿Œ/LîÙ³'''Ÿ—”â,þ Ã_|±xñâE‹!Ö ¯\¹2DDÒ‚aO¢ùõ×_£"ß}÷]vv6N å{Ed‚5Ÿøæ,*‹ðÉõ%yîܹø¦(^iE­ñÑU«VI§‚ùD¥ÞHøòË/%e?Aaè: Lª pE ¥ Ä¡ÙÏ]œRÌ?XPÉ„ÀXÄ­Ïýå.Uܤ:ZÉ£@†’Ÿý\ùB!ÄM¨ˆC`ˆ¶—h~ýûg—Ï~Xnö“œPˆÒôéÓÁ¬<|öÙgñö Z*(ìÞ½;## øá x¶AFcòÆotíÚ2‚gIJȆw®gÏž}Ï=÷às#?—1wÝu×Ûo¿M^¸pÁ.œE T_Cû€öS’ÑPüøãPQ^¹ÈøxZTb.©IüFŒ§Czz:ÊQS0VB%œãK˜ N…ð¹uëV©²ãÇô [E„QdIè`ÕWY±P^8—g,ÿ`Aå®cùù—ëûá&¯c+œ †’_EÈ¡Ÿ+¿ÃVoB$,¿yäù·Q~ï½÷°"ß çm¡^™Â ‡[&Ùð`þãÿHH/A@3E ñwð&L˜™ü¨˜ ^çC§mÒ'Ÿ|ß“ƒ(*9DD¼å Ù’Ôâ»I(‘dÉ["WIJ⋒s?d‚7R†äPÚã-;´êh䥎±tJ1(|J®Ÿ%}¹ é@†‚D9ÈÁª¯²b¡úh¿~ý@9|Ç”Ë÷øXòüùóñ,D¯ L&§ ŒIœÂ†G)&Nä&!ä`{ì±É“'ËëÂIˆS .D/s†øÐ%ž>НÌãÂã‘~8†£mYGŸè]cäJË!õKCa?`ÕŒ +ʶ´`Aå®cùùo#n~Vò(Q³ Ór½ã“6˜i—Lü\ù^óM(ùo» Á³|Ê”)¡ Þ}÷]tü01EÔü†¡¡m#æìùóçqðFjts"`oli6¥õ¶S瘙ăüÆf|£¢£Ãï–jãÙ¬ØzߨÄhÜŽ‹@ËÚÅ:N†+ò‰~EÍH-Ä,ȆŸË±Gç0R«IëNZaüw)-ÅBél„ ¦b s¥n*„ÃŒ­µÞÃTC–4Š@$!@YIW“Ö¥c ÐJSý•À"Iõ'I3lðKU»ø¹áN:<ë€ ~Aºá8Ò®7%%%˜N¿ÞQÂãŸö0Ã3BhA€²® *Qƒe]xp¦Q(-PÖµ`A%Š@x ¬ Î4 E ʺ,¨D”uáÁ™F¡´ @Yׂ•(áA€².<8Ó((ëZ° E <PÖ…g…"Ђe] T¢„ʺðàL£PZ ¬kÁ‚Jð @YœiŠ@ ”u-XP‰""á­V¼ï°hŠ@» ÐáY‡·ú#æãv¹¢Ô‰ú =Lõ_#ša¤!@YiW”ÖGýPÖ©ÿÑ # ʺH»¢´>êG€²Ný׈fiPÖEÚ¥õQ?”uê¿F4ÃHC€².Ò®(­ú ¬Sÿ5¢F”u‘vEi}Ôeú¯Í0Ò ¬‹´+Jë£~(ëÔh†‘†e]¤]QZõ#@Y§þkD3Œ4(ë"íŠÒú¨Ê:õ_#ša¤!@YiW”ÖGýPÖ©ÿÑ # ʺH»¢´>êG Ã­Hýgffª?IdxæÌ™‘g$IYŽ‹¨þzûöíS§N 4ÃÐ&½ (áF€².܈ÓxÊ:zPÂe]¸§ñ(”uô „ʺp#NãQ(ëè=@7”uáFüjãõêÕëjM mV׈š\”u×5¡ü[PÖý[ðµ¯ñþýû'NœØ§OüQ¾;vÀ9i²°'J æÍ›7pàÀ¾}ûΚ5«ªªŠä…7ß|säÈ‘>]±y”¢@÷êA€²N=ׂY²dÉ‚ Ž?¾mÛ¶cÇŽ!³Ó§O“= Ï;wÆŒ_}õÕáÇ»uë¶jÕ*©0|ÿý÷¡I”%AR ‚J ë0Ur!Ä4 CEEEuuujjêŠ+3Ûµk)‡òâÅ‹G-©-]º466V:¤B¸¶¥í´­ Ã¥ikˆ 6}ú!C‚¹ó³ ¦FËÀ(·yófy &Á¦L™"/¢rû"€7}«µ}!U·VgSrss·lÙ‚ghZZž˜xPšÍfÚÖ©ç ÒL" 9åäu£¬“£AeŠ@»!ðé§ŸJ­×µ¬ÔE øYõïÿ;éXŽë8FÌ’–S(×€(‡Õy/½ôÆr999¯½ö™×Ý(ë®UjBŠ€å^}õUB?Öa¯½äðu@OP(WƒÀ| oå$ʡŃ–e ñ´‡U\[ªK (#jmݺUêXÊ)6Zßâq ™1Ê>hiû!€o¼¶Ÿ3êI€r~4ó£’éÄî%öëׯ?~¼ëAs¢¨V%—×à•W^Á‹‘F£‘C_Q¬£mômX懭¹¹¹É·¹\., Ćede –ÊuPÙãj*­lbXƋՎœ.)ÑH—wÐKy£ÒF«%…‰°q¾çy²PV§Ó`ØL¾ KRð?ÔÐÖý?÷µùµgIEND®B`‚doc/example-scripts/tk-mini.tcl000066400000000000000000000017331242365656200170360ustar00rootroot00000000000000# # Tiny Tk example scriped based on NX. # # image::tk-mini.png[] # package require Tk package require nx::trait nx::Class create MyClass { # # A sample application class that creates a text entry field bound # to an instance variable. When the provided button is pressed, the # content of the variable is placed into an additional output label. # # The callback trait imports methods "callback" and "bindvar": # :require trait nx::traits::callback :public method button-pressed {} { # When this method is invoked, the content of the ".label" widget # is updated with the content of the instance variable "myvar". .label configure -text ${:myvar} } :method init {} { wm geometry . -500+500 pack [label .title -text "Type something and press the start button ..."] pack [entry .text -textvariable [:bindvar myvar]] pack [label .label] pack [button .button -text start -command [:callback button-pressed]] } } MyClass new doc/example-scripts/tk-spread.html000066400000000000000000000657651242365656200175610ustar00rootroot00000000000000 Listing of doc/example-scripts/tk-spread.tcl

A small Spreadsheet implementation, originally developed by Richard Suchenwirth in plain Tcl (see http://wiki.tcl.tk/1287). The spreadsheet was rewritten in an object oriented manner as a design study in NX by Gustaf Neumann in May 2011.

tk-spread.png
package require Tk
package require nx::trait

 ##############################################################################
 # Class SpreadSheet
 #
 # The SpreadSheet computes simply totals for rows and columns.
 ##############################################################################
 nx::Class create SpreadSheet {
   #
   # The following attributes can be used for configuring the
   # spreadsheet.
   #
   :property {rows:integer 3}
   :property {cols:integer 2}
   :property {width:integer 8}

   #
   # If no widget is provided, use the name of the object as widget
   # name.
   #
   :property -accessor public {widget ".[namespace tail [self]]"}

   #
   # Use the nx callback trait
   #
   :require trait nx::traits::callback

   #
   # The method "cell" hides the internal respresentation and sets a
   # cell to a value.
   #
   :method cell {pair value} {
     set :data($pair) $value
   }

   #
   # The constructor builds the SpreadSheet matrix via multiple text
   # entry fields.
   #
   :method init {} {
     set :last ${:rows},${:cols}  ;# keep grand total field
     trace var [:bindvar data] w [:callback redo]
     frame ${:widget}
     for {set y 0} {$y <= ${:rows}} {incr y} {
       set row [list]
       for {set x 0} {$x <= ${:cols}} {incr x} {
         set e [entry ${:widget}.$y,$x -width ${:width} \
                    -textvar [:bindvar data($y,$x)] -just right]
         if {$x==${:cols} || $y==${:rows}} {
           $e config -state disabled -background grey -relief flat
         }
         lappend row $e
       }
       grid {*}$row -sticky news
     }
     $e config -relief solid
   }

   #
   # The method "redo" is triggered via the updates in the cells
   #
   :public method redo {varname el op} {
     if {$el ne ${:last}} {
       lassign [split $el ,] y x
       if {$x ne ""} {
         :sum $y,* $y,${:cols}
         :sum *,$x ${:rows},$x
       } ;# otherwise 'el' was not a cell index
     }   ;# prevent endless recalculation of grand total
   }

   #
   # The method "sum" adds the values matched by pattern (typically a
   # row or column) and sets finally the target column with the total
   #
   :method sum {pat target} {
     set sum 0
     set total "" ;# default if no addition succeeds
     foreach {i value} [array get :data $pat] {
       if {$i != $target} {
         if {[string is double -strict $value]} {
           set total [set sum [expr {$sum + $value}]]
         }
       }
     }
     :cell $target $total
   }
 }

Build spreadsheet "x"

SpreadSheet create x {
   # populate with some values
   :cell 0,0 Spread1
   :cell 1,0 47
   :cell 2,1 11
 }

Build spreadsheet "y"

SpreadSheet create y -rows 4 -cols 4 {
   :cell 0,0 Spread2
   :cell 1,0 12
   :cell 2,2 22
 }

Pack the spreadsheets into one pane

pack [x widget] [y widget] -fill both

doc/example-scripts/tk-spread.png000066400000000000000000000400041242365656200173540ustar00rootroot00000000000000‰PNG  IHDRbÕâ†mTYiCCPICC ProfilexÕXeTU]·^{ŸäÝyèîé.éé.i0@BBQJJQ@PD”•DBPÁQEE»ñú½ãŽ{ÿÝ?w±×yÎ\ó¬½Î~ÖÚsÎÖ"Ïðð˜€Ð°èH#]Š“³ ÷à  Àçé®ceeþ×¶ú @»ƒã’»sý¯nÿóo”72ìååŠà[^ôŒ®BðãƒÑáF!`ŒDˆà»Øÿ\¶‹½þÁ ¿}ìlôŸð$OÏHˆ;%ÖÛ™ƒ¸–>Ì'0 z4‚5½<}`ÕG|$BCìâp‹xýkÿaOO¯¿szzúÿÅÿüä—Èõ£ÂC<ýþòÙ…†Ä Ïëw£GzRXÈÞ]nðÈ5çã©oö‡‡üæì·Ý7ÌÞö=Ìk¯åìihó‡Gëþ [Ùý±Çèíýƒ}£ þÎäijõÇccÿGÅÚüÁñvް¯þ_»_ ¡É{`´Éß{0û»,€'ðŽöÛåè?èMÑAv™¯Å$Ì[J‚"'#+»;üÿ¦íž¯»lóûÜ@Ì#ÿ±E!ÏTm9cÿ±y$ÐêŠlqåÿØø õ“Üã÷މŒýg>d+€@àü@H9 Ô60¦ÀØgà¼A‘à HI dÓ ”€rP ®€k ´€»  <ƒà1x ^‚)ð|‹`lB„ƒÈÄñ@‚8$©@šdÙ@Îä…A1P"tÊ‚r¡è"TÝ€nC]P?4 =‡¦¡yh Ú€Q0 f„¹`!XVu`3ØÞûÃp<œŸ‚‹à ø*Ü wÁƒðSx þ ¯ ŠˆbFñ¢$Q*(=”%Ê凊DAe¢ Q¨zTª5ŽšB- ~¢±h4-‰VG£íÑÞèôt6º}ÝŒîA£§Ñ‹è_2†#ŽQØ`œ0þ˜ƒ˜4L!æ2¦ Ó‹yŠù€YÅb±ÌXa¬2ÖëŒ Â&`³±ç°×±ØQì v‡Ã±áÄq8Kœ'.—†+Æ]ÅuàÆppëx"ž/‡7Ä»àÃðÉøB|-¾?†ŸÅoRÑR R©QYRùP¢Ê¡ª¢j£¡ú@µI #4v„ B¡ˆPOè%¼",‰D>¢*ÑšH…¾’þ>ý ŠŸAÁ›á8CC/ÃF,£0£ ccã5ÆaÆE&z&&¦8¦R¦{LSÌ(f!fæææFægÌ,\,:,¾,,õ,c,k¬¬Ú¬¾¬™¬×YŸ²n°QØ Ø‚Ùΰµ°½fG³‹±[³d?ÏÞ˾ÀÁÈ¡ÎáÍ‘ÉÑÈñ‚æã´áLà¬äâ\áâæ2â ç*æºÏµÀÍÌ­ÍÄÏÝÎ=ÏÃÀ£ÉÈ“ÏÓÁó‰ÂDÑ¡„PŠ(=”E^N^cÞÞ‹¼Ã¼›|Â|ö|É|×ù^óøUøýøóù»ùx,ê^R ªžì\rJjšf6Ž®~%Bщ©y"ŠU ='úX S +‡Å•ÄÅωJ`$T%Â$*$&$I’:’±’u’ÓRÌRæRÉR-R_¥¤]¤ÏH÷Iÿ’Q” ‘©’y)K/k*›,Û&»$'&ç-W*÷Dž,o(T¾Uþ»‚¸‚¯Ây…IEE ÅtÅnÅm%e¥H¥z¥yeeå2å F+•l•‡ªU]Õ£ªwUª)©E«5ª}S—TV¯UŸÛ#¼ÇwOÕž > O‹SšMÍ šSZ¼ZžZZï´ùµ}´/kÏêˆêé\Õùª+£©Û¤»¦§¦wX¯S¥o¤Ÿ©?l@o`oPbðÆÏÐß°ÎpÑHÑ(Á¨Óclf|Æx„ËÄÛ¤ÆdÑTÙô°iÉÌÖ¬Ä치y¤y›laj‘gñj¯àÞ°½-–ÀÒÄ2Ïòµ•°U„Õk¬µ•u©õGY›D›>[[wÛZÛU;]»»—ö"ö1öÝ4®5kŽú޹ŽSNÒN‡Ù[]p..—]Vöì+Ø÷ÁUÑ5ÍõÙ~áýqûûÝØÝBÜî¹Ó¸{ºßôÀx8zÔzlyZzVx®x™x•y-zëyŸõþì£í“ï3ï«á›ë;ë§á—ë7ç¯áŸç? P°¨Xø=È8¨Ô#ôv}XpXÏîqFÃÅÃÓ§"Ô" "#Í"/GAQû£Z£‘Df(F$&5f:V3¶4vý ÃÁ›qtqaqC‡Äeš7Œ¿”€NðNèNäMLJœ>¬søâèˆ×‘î£üGSŽ~8ftìJ!)8éQ²LrnòãŽÇÛR¸RŽ¥Ì¤¥Ö¥Q§E¦M¤«§—Ÿ@Ÿ<1œ!ŸQœñ+Ó's K&«0k+Û;{à¤ìÉ¢“;§üN ç(åœ?=vúÙ­3Wrérãsgò,òšó)ù™ù? Ü ú  ËÏÎÆœ*2/j-(>]¼UPò´T·ôzgYFÙÚ9ŸscçµÏ×—s•g•o\¼0yÑèbs…PEa%¶2¶òc•CUß%•K5—Ù/g]Þ®«žºbs¥§F¹¦¦–³6§®‹©›¿êzõñ5ýk­õ’õ¯3_Ïj 1 ŸnxÜxÖhÖØ}Såfý-Á[eM M™ÍPó¡æÅ–€–©VçÖÑÛ¦·»ÛÔÛšîHÝ©¾Ë{·ôÓ½œvB{JûNG|ÇJgxçB—×L·{÷ËûN÷ŸôX÷ ÷šõ>|`øà~ŸN_ÇC‡wûÕúo¨ ´ * 6)5=R|Ô4¬4Ü<¢<ÒúXõqÛèžÑö1­±®qýñOLž >Ýûtô™ý³É ׉©IŸÉ¹ç!Ï¿¿ˆ}±ùòØ+Ì«Ì×´¯ ßp¾©x+úöú”ÒÔ½iýé¡w¶ï^ÎxÏ|~õ~ëCÊGòÇÂYžÙš9¹¹»ó†ó?íûôásøçÍ…´/t_ʾŠ|½õMûÛТÓâ‡ï‘ßw–²—Ù–«(üè^±Zy³ºº¹–¹Î¶~å§ÊϾ ÇÙ̓[¸­¢mÑí¶_f¿^í„îì„{FzþÎPHûù°T نǨÿÉ{ é1’´Ã¦C²‚NÈ ú \К¬!Ιjš¨A*¦†hBißÓû0|fòfžfõa{ÃaÁÙÌÍÅIiã]âçPÔ2ÖQUç– “DK®IÍI¿’ík”¯TÈULV WÞ¯b¬ª¨Æ­ŽUÿºgB£S³V«D;G'S7C/K?Ç ×0Ï(߸À¤À4ßìŒù)‹¬½',S­Ž[·I±=n—bŸâì˜ätÔ9Á%n_”kØþ@7wo_Ï`¯ï$ŸS¾¥~Õþ7Ú¿™ ][ GEÐG F©F[ÅøÇ>x&®úP[üpÂtâÒÔQÚcìI”dþã|)”TÎ4ÖtÆ4„LtævÖjö·“s§¦s^ž<ó4wWÍýÊn²îâ"žÂ^"Þb>¾Ò~2þ²²²A2ÁÒ!’¡H¨= ®¡iå“{éàí¸áCÓñ+‰¸ÃlGD*ÓK2M¶ÏPNwþ"ck%O•ð%éËJÕWôkÌjmê¯î¿æUï=¬!öÆÑÆŒ›·.6]k¾ÝÒÓ:rûEÛÌ/w×ÚA¾“¦‹¹›ó>o`¯èÉ>™‡òýŠʃªCêö kŒh>ÖÕ37~böÔò™Í„ݤãsçû^º¾r{íöfÿ[—)Ûiãw*3‚ïißÿüðîãÀlã\É|Ò§€Ï– J_¸¾¢¾~þöd±ë{ÃRÅráì•¤ÕØµàuŸö¦›š[²Û‚¿ØvHÿÿ8” šˆX€ãÁÛP ‘DÉIÔimé1h3Þd–`©bceÏäX䲿®æùÆ+ÃçÎT @°J¨áÿ¦h“X“x“D£äu©zé«2Wd«ä.È—*œUÌUÊT>®’¨©æ§î²ÇBC[SaŸ_‡[—SKŸË€bH1¢s›p™²™1›ÓZöâ,aË«-ëM› Û-»-û-‡MÇŸN«ÎK._÷Í»¾ßÿÖí¥û¤Ç3Ï ¯çÞ¯}¦}gü>øÏÌÎÍῠΆÍø¾ GÑG Ä(ÅšÜz(1>;¡,±öp Mû &õ'÷¿“Ò˜ZV–~úDjF\fpÖþlË“:§äs„OsžaÌ%çQåc P… pûìÏ¢•âÅ’Ï¥ʦν8?^>raèâ@Å@å`ÕÐ¥áËÃÕ#WFjFjÕ=º:xm ¾ÿzÃÀG£7ŸÞzÑô¶y¦e¶õÓí/m‹w–îþ¸·Ò¾Ö±Ö¹ÖµÞ½~½g½wíÁjßÊÃåþïß¿ }zôqxfdêñ«Ñɱññá'Ÿv?»;Ñ4Yÿ¼úEùË¢W9¯Óß~5å?íüÎdFå½Ð†àãÂìä\Ï|çÒÏi _\¿}SXäÿβD³LüAX!¬Ö넟TøMÜvó µíòÿ²°J\Fjtû&Ì‘ô¼1‹Hü ÀŠ €*€ù' çh;üoüÀ :RmŠ •¦0¶À„€C ©(«ÀMpŒƒ÷`ÂC4¤‡Tˆ tèÔ=…–`:Xv€áËð©é ªQ3HÕæ®@ÄHbb0XvöÂ9ánà©ñ!øa*yªŠp€ð’hDl$q‘2H«d_òµuM­<í-:%º;ôºôƒ ö SŒaŒ¿˜N3 2ßeq`YfÍeS`{Î~ŒC”cŒ3‹Ÿk;š‡›g€rˆWŒ÷5_¿¹N G0EÈ@'Ü/’%j!F+6.^,á))!¹.õPºX&TV_ŽKnCþ•Â}Å¥ åb•|Õ\µ<õ³{ÎiTkÞÒêÑžÐYÐC#{[ÙÐÆ(Ì8ˤÖtÈì»Û^}ËH«Jë [j;cûd‡{Ž«Î.žû \Ü w5XÏ&¯U5ß$¿Á–@ÿ ¶ºÐ°p‰ˆ3‘?£}bÆêŵÄK%T8RuL0éÊqñ”ëiŠé÷2Œ3'²OþÊ)<#›;’QÈtö^q`)GÙ“óyœ+*\zP]V[g{Mþ:KÃNãü­ÉæÁÖûmÝwûÚÇ;gº×{éú$û-#ôŽþx"òÌ}²øÅ³×Œom¦sgÆ?²Ì¹ª]Øøfñ½jygÕm½sSxûÌï÷þy€(šˆÆà¼ÀDWÈň–Ð ‚çàØ‚h ~HѼ¡xD¸ õAïaæƒ à 8n‡¿¢(({ÔIT…ÖA'£{1DŒ ¦ 3‹UĦb'p¸ã¸×xU|~ƒÊêA†PF$ãˆI¤dUr=µ u9 'M)-m(Ý-zmú7†¯ŒG˜h™*™U™GYBX ¬µllË쥆˜¹¬¸v¸x¼(Ì”!Þ4>~Àß%*h&Ä(ôV¸Nä ¨ŽAlL¼DÂë7÷}ÒE2Á²:rlr?äGÏ*S> â­êªæ¤î´ÇEÃC3@+Z;Y'O·Z¯]Â`Ɉl,fblh–e~Ãâ¹%ÚJÎÚÛæ¬í°=ÞÁÀñ¸S—óÎ> ×øý­nkªžG¼z}h|]ýêüÚÕ…`BÝÃî„3GDE>Ž–Ž9»çzèA‚\bÅÆ£IPòá㛩‡ÓQ'Nf²gÕžÔ8õäth.U^MYá÷¢âƒÒås•åöñ÷ª¢/KVÏ×ÔÔ_“©_oèi̽åÓ¬ÞÊr{ëν¹ŽïݨÎj]’†ê†'G ãZOã&n=_z%ùÆgªð]ïû¥YʼÍç´/ß6–T$®ÞÿIÞtÙ®ûË?3 1 ˆ¨LfÀ ø ÊÒQp ”«ˆŠ4^ƒo 1A¢ˆJd AIˆ&t `,[À‘p Ü /£øQ¨lÔ}Ô6Z‹h4ëuL¦Ë€uÇ^Ãnã¬p—p[xüM*zªhªI‚&á‘LŒ%N‘ÌH­ˆòQDM¤N¦Þ ‰¥Y¦¦]¥K ‡é3Ê%̘ۘÞ2ǰP³Ô²³Î³d—gÉ‘Â)Íù’+[‘û=O!ÅŒæ½ËǯÌÿS 9ÿæÂLH}_)(&-¶*Þ!‘)é(%,µ!="sYö˜œ›¼¦‚€"­JiSyCeK V'îaÕÖTÓ²ÒÔIÕ­ÒëÕŸ5ĉ›™„šž6k1ŸÚK²T· µ®°yaÇdoëïøÌ™Õeß¾ó®ïÜDÜÃ=îz¼]}üðþ^A<ÁI!ïÃŒ\ <õ6Æ4¶5NôйÆÄSGˆG³’¨“óS¸RkÓUO dºg­ž<“#uz$7*Ÿ½ çlD1oÉÓ²¬óú ‹]•©—Ì«Y®¼¯m¾šYïÕ ÑÈys»i¦eèvÛk÷ª;®tݸßÑ;Ú77€âÖ{8–÷¤óÙ÷ç‚/]^g½mžžœùñ‘<'ôIgÁýkÒ⥥G?Öׄ~:mfo÷ìòå'/·=DÒEäÇ7;;ËBàrØ}?lVììlW"ÅÆ+:CþÑÖw±´”!q€ñ3Çv?ÿÝþ bD~'½ÿþ pHYs  šœ IDATxí}y`U¶~õ¾¤ÓÙ7 °‰lò. (.øpeñýfô9<ÇqCdPy 2(£²Œ ŒÏÞFqæ)È&ˆ à‚‚`„’l½¤ÓËï«TŠNW NwUåÔÛ§îrÎw«¾œsoÕi•Ûíöù|ÍÍÍÕÕÕ.—Ëãñà+†aøO®ÀKP ƒ d€J¥âôG+óŸ(h4½^Ÿ ÓéØÓ pÄÙ³g½^ott4hYÛOÊ„@gP«Õ ‰††ÒÒÒX¦p:‡äOPÑDg ¦¶„€Ü;€L&||&''k«ê=¥U¶äxK]m5\ ¹[Hú„@'€£€µp„56¡´ªQmôh½>/<{c=>;Ù;5ïðãl6â>~é͹¯øäºÂ)‹Åb0:Ô3U&:È¢…< -»áñ8.áÅʪ »Ý^V\|lÛ[Ñ]¹p3jKf¯7MJMO7›ÍXà¨ð­øß¼º¸°tûMI±±eqÔ©Ryz¦÷˜8)!=óršŸ)+Ü{d[yc £Á «—ñjR-é㮸©Wj{Íy5¤Y8qâDß¾}At5YèÁå;yòäÀ¥ii¥lœN‡×£E¨NWŸ>•o1^¶ÄEŒ•Œw|væ³ÖèèÄÔÔØiŒ©-)­,/oh¬ïuÃÄ!7Þ€µO£ÑØ2®yá®­ê=Û-±q±=z˜{¤ãN°-®+-i¨9ç½fbæø›Ûo¾÷Ðöý…;¬±ÑI©É‰qI`µªšòÊòJ[½ctæã†MkÞVIIvïÞ}ûí·ƒ‚£¢¢ÀÑ·xÓ¦¼;òš›J¶nQkTCn¸…_[|Eóâ=;Œ_~š•>0>iô؄ѓUjuÍ×›*¾Ú^tÌ^ýå§ÅZMæu7‰5ÿòðg_ÚßÓš‘•vuÎ cLt4Ù>?úÉ÷_ž-®úòÔ•J}Íð‰›·ÕGRx qqqååå»víBdq×]w ƒ‹Ÿ~ú ~Vjj*ªÁ­¸¤æ>GᇾíÏ[§²^²rD*tBCçÞ_Í~oöªg\Í»ó Ín6ÚÐb­ÂÝ ƒ6 U¥¥;vfôƒµš”&§¥ ÀõÝÆëµœ.èçj²ê´&ƒþôŽUÅÄ`ús¥%†ý›RÒµ™ÙzS"£k®p—Gü­q•Ç&ùô:}SùþMçrl~¶²äë“ÛãzXRzGEEkܧ*6¹æ\tŒÑ«µ¨| *ôKºKb—×`cG×7$ëuV“Ѭ×ê´:µVËà¿¶ë<&Ç«v7ûšš“g]]=f¿­®VSsLZ¼1Ájˆ1kMFAǨÕh­õè<.ƒÆdPiu16wƒ½&`sŸÉ•Ô#.&Úª×it°ûp®Wïözñ˜ÁàNêá+©?°y[}$%á)¡V¦OŸž——·oß>øào¬S÷DX§Mºí‘ß}?çõ!ÿ6sb߸d-@a÷tz£ëÌwÎ^2ræ«· Šа‚¦¢oÀsÿô?#â]E¥>UK=G]áÁÿ÷ûãçÿq¥åÌ×ó–-tÆ&,¹svÌÏÞøÒáÏ,Zš‘®ó–­™üÐÇ7üö/ÇTíýÛœÇæ¤m|gh¬³®1ê¿þôîÀdÕWkg¿9w÷uÛ¦! Íûdɼ5{]òç«{6íýë2 Þr³ .•6†ÿàåÙ«öLœù‚ëú;+Jõ±¬v¬7Á²¥ ¡¤æR¹Ê€@lÐÂsçp´*³5Ú`2©°—Á~šXšp5©ìvµÓés8 L½Ž]æàªáX͆X‹.Ú¬1êÕz-£mq[<Í*—NÒQ©uµNµÁ°¹QoDXa1šup;ð¹FgPÃÁö5yÝNÄJ^Aãi06ôŽ€Íe4wXª3f 6•þñÜrË-X¶*È:M¯ÁýS&³ÿàœ,¬M ŠÇÌ0¥?}ºàËs¦.ø¯»‡²2ÆSU\ÔàníÌÍhÓ2ÒÕMMºŸÑáI04“ù8¯¤îŠèè½{:Ôp¹±zµÛ}´¤ÑmÑ—|²loÿ‡–Mž‰†Sž~æË3]lÎE†ø\ù¬Úcóäcwe—ÄYoÈãqxÙ ƒµ_é'ç½ Ð„ÇÿÍyZß;ÓUQ¦SEE©¢£™h+c±°Z:ª†z¦¡Qkkt9ÆÄ”€=¸£šqƒG™5f“Ú¨WÁ›5àpëÕL?*¸¢°<ÅÊ/>’bÒðDÖ9Z­QƒÅ`Ô²ñm“»I«jjò¸ŒÛëd’bÓŽ~qg’ûÆ{Ð ïÝá˜6mÚ’%K¶nÝzõÕWc‚Ó8pвln¶!Êhvz½,óú¼>8„ïÿq9Ã\±xÊ¿jÏ#âüöo¿[ö¹“7^­ºyÕÿ=Þ»÷5xøÄ¼ås7.gÆüû‚'fŒÆ¤z}v]ï+­œkÁh{_9˜Ù®e¿y}(99†ë.z{ïÅGß;ßi¦U‡Z¶C›ÿwÞÊ@\½{³‹$˜PO³ËÁø’tç—¡0ˆ¬­S 4ePÒÈØ^,KPÐÁOb8 -¢âïqúçVUUº­1úØÆËàÓÅ>d·1ƒJoðh5ÕeúÜÜ‹¦õ‚ž”µ'3ñð•ÑÀrÞ!ѵ¬·©=èD ·Äà©kt£¢ê ZÿfÄ÷û¥ú;­OcÐèMZ}”Îdа·„F¥ñ1Xsu!è©«rä& 8zkGÒ.!ÊØ¼ys¿~ý®½öÚo¾ù4ñ׿þuîܹØ%…â¢4Û—ÝÚ8[Ã%w0Ì¿ÿ~^Åâ?ÌŸºlņÇúZ‘aâœ&Îñ³h®œüȧ7ÿ¿£»×?³lါ6Ü™ ¢ñ1ÞóÝ1®üÃG™èAd§oÏêr 7;@̬Þž»þüQ—÷ 8â©U&ôŽ.Ùþúo–5 2âB¨Äþ j™\_³‹åµ _/4e×&yC|*5Èî»#Λ³XoâüÁV`ëºH.Ô¡¿]‚»p‰ QÖ“wç¬Ã‡×åä3Ì Ø8uLŒÊjeLìÅ¡Òé|ðÔꂺ:¦GOëðaÍ×ö‚ÂQÇ;çŸ-wdÄ[± ÁFxXÓçSa5ÿt¾³e‡G5x¼ÍÀŸì›teI㑊¢ú~9iˆXXŽQÛðépíªô.Æ[VXiÒ›ú&m²±^´¼þ¢ÿüóÏAš7Þx#ž›À®»Ü¹sç€à\°Œ,¶Óábožƒ‡8ÀdÒ«[v:¢ÒFÌþÛâs÷ÿþwSõ+Öÿ&Óx«©òس¦Áýz&÷ìдޖ Åç:µjí¶ô»ÆäØò?_þuÓ„§›Z® Äûü†ÌQw1o½õ»5i+¦÷O1Ô–—¸âz[ΡŸ¦úÚê3§?Xö®—†F—7>eÔ¿V¾ýçÏûÍÎÖ”­{bÑQ†¹¾åb+Þýß¼Vòû5sG§|ÒëÖߌùâÏ\30ö¾±½å…%ÎÔ«EÃTÀEAGø¯pŽXš`㑨îcôõ×Uú¾P«Ë0GiÁx¿kØÌ÷¸‹lörÎzÍ5àˆ€=Ø\Œ1óæÒ3Ÿëc-iYFá5 硬ÌvöL½'ãf'›æ"Màæ–>é§³[ª- ¦^¯Úçq³Nþ© KFUI}]•íªŒ[P-`óðcÚ¡¹ Qåççßwß}ܳUÙÙÙ` „ØøÈÉÉ9r¤M0ú´;§Ž~qÍs_­ÉXúþòl »6¡ñº¼–ϬYðÊÌ…?c~ëµi‰žÉôØJ—ÌGxÂWÞýÄÕ½ °Å¹Ê¶ÿùû—³Œ?zÚóí9öcÙž±l}~ŽâîÿÛrÝŠ§^øíö–˜ÙËß»¡ï¸;ð§gÿóO 3mÖ¯­^;ÿ׫ÞúpöøG^*(›»ôéGPóîY Zý;U¸ä¼ õ爵d­Ÿ!9·Îš×¤ÿÛÏ}ú&Ûýøß.Þß š`—®]$œôÙEpä ÚºkßOG~è“å°*%&&ªÊËÜÇNÎÊŠ‰1±e«MÎsõu•§ Õ9ý|)©UUUدlÛžkî®Ê×W•’Ÿb5[ô`‡ÍU]V_~ºÖ7F›Ø·ýæužÊ¼ÊÏ’â¢âã­f³׋Íᬮ®¯¨± L¹1F“$Ö¼­>’’:tèŽ;îÀ'^ÚÅÓk@CÄ XÈĬǿꪫ>þøãaƉiîr4º½Å$ðÐŪúÉ=.‡ËCX-=O$[ÎÛ7æÝÀ46º4—î“Ú£1Y.´g<ŽF¾ë±$ŠR«VžF|VôӤ嫟!V9±&z£#‚œ**tÅ•ìRHšw€Û*„›01%5!«OÅÁƒÕÕ5æ(3îs»Íî1nº©ÖnG´ëmžØ×š9èÔÑ]gŠJc£±ª ª«W5iÒÒ†Þ[ks^Fó¤ñ¹/ù¶¨¨P«­C´âiÖYÙ7ÕXgk¿y[s¤#Á«à6öóz>ŒO

,ª+Ì<2‡ :@II :ÑÂà\ $Sê|§éK÷¤<‹$/©Ñ}àøá¢‡fΜ©ûñ›#Ø…á QžE ˜ 2AvÈs+;HaB€èRˆ&º^êœPò£ îÁ %`O62A šÀâç=÷܃$·HZ3nܸM›6EÄXðwDdt”è>CS§NEÒ¤~ÆJáK/½´zõêˆàÅ­ÁFdh”èVCû÷ï_°`AJJ Þ5@&¤Tá ÃÿöW_}?LŠ·|ðÁ¦–Lj8ùo¼ó(à+åœ7o^RRÞãð»§NBz¼'eÒÍ“ðÈ\·èç@ŸqéÒ¥œ> B lCH‚€ûwu[-÷ìÙsôèÑÂÂBüÒÄ /¼ÀW@R&¼ É= ±xñbü2¾VVVâæGš&®Úm·Ýöøããu2Èsûç>ù䓜üù矇}þøã»vîâû¤!@„`hâ£>jll;v,²°Ý{ï½x%Œ×uÙ²eð&p °nÝ:^þúë¯Ã}à¾"ÍüÊ•+á\à}Sä_Ù¸q#'?vìØøñãAXõ•lÛ¶“£®[xËWœO¦Â÷LB€èj‚¡‰=zàV/--=}útff&2/ñZfeeqeð˜/G¾|æÌdpã^6EFi>¸@úy,ˆ"7b„\" ´B?|·}úôáû¡!@„`h‚× (.\øí·ßò’‚‚® ðra”„{ßL¸ ‰Ý“G}¤€SuH±yáÀäñÝò… 'é/!@t9Áоøâ $¤Á?|DÂlXPÀ:Ž'žxbƌ՟5kž¡FHä¶9räV1¹jøQ<,jâ²ÆOÝpÝÂïÀâEÀ>IH]‡@04ûü¹çž³Z­ˆ~øá‡õë×óúaÁ Ú222-ZÄË……gŸ}Õ° F@òè)S¦pgß}÷] Ö&ðâ~Û†oòâ‹/â—»ÓÓÓ̽ÓÅBl‚e¾À7¡!@„~û؉Äû”ÂÿáAŒÛ•ÛË¢m›ð¯~uÞ¢jÕ™®x‹:Ó µ%‚@›GŒ¡®wøìM—N]ÄÔ„ äŽÈLÐ!wËIB€è¡¤ )D2ž*„Àå pQZÄÀ—ÓFFu”g‘ŒÀ'Uƒ@+M`½Bæ)Ï"eÌ Y!;BtÈÎxR˜ .VoBy f•gÑåÌ(Õ!BŽ@+M ëN>7rå‚ëPø”ò, jEt ::ƒµ%ºDÝbšÉHB 3Mt=jKt .Z›¸ÅH¨‡ìXüC\Ü{_¼ÎÈ‘£° ]Þ4Ž?ŽY5j§³0É$/ÈÈR•C@64±víZ¼x.œ6ž/ D ¬¯¿þZx–ÊáG‰„“x¾@¹¦¦Æf³…_+±óÈ#è8wî2bâMs1ƒ_yåJE!NxäÈ„Œ_fh'½v ¦0<ÊÐ(¡E@4 ½=öXnnn@ãñ®+RQôìÙ3àY†d$CTä 8\mm-Ò"gzÀ³$”82:•I®V­Z%%~+!‰ØY’‡¤P¶ÛíHŒ*6VûކX+’KÐRZ!Ü@ŠÝ€mÙ²I7‘ƒ7àY†¸7ü•ù¡‘Ù~ò’ñ*È Ð~=HÈ~9²~ã'â×v7ð+*¼Øà.^" ;ròg© ;´V“Êl`3JJö.žûqÄgŸ}‰0q¦d­P¶bBRð㸘AlW+¥ZrEöäåb3\‰gžyF.ÚvO=áJà'º§íбZA‡k¡g9~@x–ÊR@@èY@aÀ(õH‡ ·7„ÁÔ„ :ŠÑDG£ú„@·C€h¢ÛM9Lt‹Ö&”—`Vyut‚©>!ÐyZiBy f•gQçç›z ‚@€‚Ž @£&„@÷B Õ›P^‚YåYÔ½®M²V2´ÒTR^‚YåY$™+‡éFPÐÑ&›L%‚C€h"8ܨ!Ð šèF“M¦Á!pÑÚDp]P«Ž"Àçeð{EELÞÑþÃ_ŸOë÷B‡˜<üÒˆA€¼‰Î d[°ƒAp‰Éƒ&ŒÍÀ~Á .&£j4T šˆÔ! lˆ&”=¿d!ÔõŸ½É‚ž¨ B€P PyŠ›X2ˆ5D¡F”ú#‡Ñ„⦔ "B=7jD/£?þù®ÀoŽŠÉ/£ËWáŸà ü樘<ÂêÒðD€h¢ƒ€…¢:Ï ~‰ÉýªIð+Ï ~º‰ÉýªÑW‰#@A‡Ä'ˆÔ#"ÑDäç€4 $ŽÑ„Ä'ˆÔ#"ÀEkÊK0«<‹"ÉÝVšP^‚YåYÔý®O²XPÐ!‰i %)#ÐêMHYKÒC 77W1Päåå)ÆÅB4!³)VÆÝ¥$¾“Ù”ºt5"ºDÝi¶ÉVB (ˆ&‚‚Ý 9­M?~¼¡¡M€­ˆ›/F3bÄY̯9o §¶˜\úFñšsªòv9ÎÂÂÂÆÆFÈ-Kff¦Ñh”¾9¤¡Z«Ie6¨ü¤üZUUå÷f-BÛšš›Í&AµªÄiîwk¡¦˜<`'R §ƒ×íäÉ“qqq}ûö…¤¼¼}šËú6G<"lEe¹ 3šàï(ßÈh¡ž~Vð§Ää|É@ 8ÚªoB¦sÔÖ–î,‘Áfwž²DR˜Ò4D’žRŽ2[›d‘ÕºDÿî9:Ñ„œæ]©«ä„8éÚ‚t!„À%hõ&vïÞ}‰ºò9ÔûP–,’òŒ)uޤŒyкµÒº˜9sfÐI§!æÁ3œ>d‘tæE¨‰²çHh©2Êt(cÉ B  P×;|ö&_Ž@]„€l9€"äçMˆ½Q.Û‰ Å ©# M Ì=÷܃LDxÏgܸq›6mŠˆ•[·n0aÒ%ám‚ûï¿¿¢¢""jР„€â†&¦NŠ$¨¿üò V _zé¥Õ«WG¦×_ýé§ŸF”Ÿþœ5mÚ´ˆ¨AƒŠG šØ¿ÿ‚ àÿɯ½öÚÍ›7s0!xõÕW‘´ï>øàƒMMM¼ü7ÞÀËÅ\¼€ü·óæÍC.#dãð»Hm4yò䨨(8)“nžÄ{èç@ŸqéÒ¥ü”lß¾}Ò¤Ix=#B¾wï^þB „C#GŽÄ}0aÙž={Ž=ŠlˆÈú /ðŠ~óÍ7‡â’-^¼éUñµ²²7ÿܹs¹j·ÝvÛã?ŽDûçöÏ}òÉ'9ùóÏ? úD«];wñ} ;vì3fŒPBeB€ÁÐÄG}„4¶cÇŽÅò{ï½¹*ym–-[†ÿí8PX·n/G€÷ûúÎ;ï¬\¹ÎEttô’%K6nÜÈÉ‘[uüøñ D ’mÛ¶qrôÃu obùŠå|Ÿ|áðáÃà—·ß~›—P Bˆ@04tf¸Õ‘9 ‰‰:ù¾ûîãÊÊÊâÊ(p9‹¸¯hÂ×Aže¤*A~DÄ ©©©|p±oß>,ˆ"ç*ä=àVpMÐßmÛŸêÀ£–U6lØ““ÃAB€!ÁÐ?<’&/\¸OÍ92)sgÁ  ¾¦°Ê€‚ßt@ ÂÜYìž<úè£ œB’K¾ ÒØñÝòî,Øaúôéðnä›÷‰7“ „€d†&°ˆðÅ_ 9*þá#:6lo°Ž€ã‰'ž˜1c/fÍš…g¨‘:Ñív9r«˜ÜYüÄ5q€ „Yƒ¸náw ¸à»B 3gΜ;wb­„R BŽ@04ûü¹çž³Z­ˆ~øá‡õë×ójaÁ?ÙŽXf EIDAT’‘‘‘°hÑ"^.,<û쳨†e0v1§L™Â}÷ÝwA.X›ÀKAÂõÈ_|Y‘eðàÁÜûB\ý§žz ^ÉÀ¤p÷“¶Â±¨LG@õÏÍ;8tpê=w Èþ¢kÜ«"‚hÚ&ükE·(´ŠÝY4takÈÏQØF Ï@ë?ø¿+‡Æ›~4 !@H¢ ‰L©AHPÒ„"é"Mš²Eࢴ4ˆ¯dkH`ÅÉ¢À¸HIª¼9’º¡Ñ¥•&°‰À¿^š¾#Ý Yé¸ôøÊ›£KÛ,á :dh>©L—F@k5©Ì*bûðÒÕeRƒ{¼‚,’òt)uޤŒyº@­AºèäsA(ÑM„;ØdQW Üù>•=GÇGj=PÐ!µ!}É!@4!¹)!…©!@4!µ!}É! š¸ðb»Î*<(e® *í#€„ÜáW IMöÀÈ)‡÷¶›››ý*à« hB˜“Bh¥Ì¢AeB }“%`Z–²²2¤t>|ø!C4 ˜¢m?ít´=-e Rærê!×RæÆÆÆJY[Ò&¹¹¹œbH(‡t HRÛVOxm•n+¡”¹m1! !ÐQêëë‘_²m+{¼1\Ê\>Å./§!@\>v»½¨¨ˆw.„ eïMàQKJ™+œQ*A €÷¹Nœ8‘œrm›Ë›&(enÛ% !ÐQjjjNž< Žq 7ØéÀïw e.ÒùwªOØéÀoõïßihÅ0‘M๠N{®À'¿AÊ\È‘2—· Žv=ø¯T <4Á•¹¿9Šõȑ㞯‰TõØõà¿¢ šàyA¨7Êbr¿jô• €Ï ~hˆÉ…Õ.â á *„!À!@4AW!@\¢‰KD§ B@[ïðÙ›Îÿò’—’EÒ¿Ä•7GÒÇüò59€"Z—0•—¼”,ºü«!R5•7G‘B²KÇ¥ £Ká¥Î % ÐêMP‚Y)Ï'þëB=š#éÏ‘”5 Z·Vš@”`6h»´¡²Ì*ïªëÒ‹!"SÐØiPB@NMÈi¶HWB "MDv”Drš-Éê*–¾XL.YC¬˜Xj\1¹ ¢ !T±ôÅbò ‡¡f@@,5®˜\8ÔE;ÂT&.±ôÅbòËï™j† >{_j\1¹p\ò&„hP9ˆ¥/“‡`Hꢃˆ¥Æ““7ÑA€©z»ˆ¥/“·ÛìÄRãŠÉ¡y]2ݳS±ôÅbòî‰Rd­K+&ç´%šˆì¬)gt±ôÅbråX.KÄRãŠÉyË(èà¡ Bðˆ¥/“?µ ±Ô¸brá8DB4¨$bé‹ÅäACÍ:€Xj\1¹p(¢ !T±ôÅbò ‡¡f@@,5®˜\8”ÚjR™ ç3Ü OP™ (‚–0éJ K @4q €è4!@\´6¡¼ä¥d‘ô/qåÍ‘ô1暴­4¡¼ä¥dQG¯†ð×WÞ…Ã0ŒHAG@¦!y#ÐêMP:V)Ï$¥Ì•òìpºqs$}=ƒÐ°•&ÐXyÉKÉ¢ ®‰04Qvà0æ!(è3à4! ?ˆ&ä7g¤1!fˆ& 8 GÈ¢ ùÍiL„¢‰0NÃòC€hB~sFaF€h"Ì€Óp„€ü šßœ‘Æ„@˜ š3à4! ?ˆ&ä7g¤1!fÔõŸ½ÉæQi8B€ Py²˜,R’ˆ$D‘DŸÆ&dÑ„,¦‰”$"‰ÑD$ѧ± Y @4!‹i"% H"pQZå%/%‹"yq]ÞØÊ›£Ë³[NµZiByÉKÉ"é_‰Ê›#éc„†t5!º­ÞD÷²›¬%ºÜÜÜ®é8½æååq£MD}RÙðw—¬Íò²žJRžDá@™Æ dÑ„¬§”'€ Ö&¾ýö[‰Q£F !©««Ã–{cc£J¥ŠÍÈÈÐét Tbs!&›b4@mm-î›Í6}út^ØNÁçk}q\4Á±O¼aeeeiiiÑÑѧ¤¤$??¿ÿþüY*„±¹“‡S7‹C‘ššŠû%@dt`%6&&F­VÉHOO‡[„ýÔ$$ˆÍ…˜<$ƒR'B`À€qqqZm0žŒiBˆQ}}}TT”PBåH! 6bòHéIã¶Àk¯½ÆWP[M*³AÅ—cÁn·eeeÉQy…é,6br…™/}sÚïuG¬[·§@ ˆ`<€ýFJØÐÐpêÔ©ììl£Ñ)h\±¹“náGÀo  K—.}ÿý÷?üðCþ¬¼i¢¦¦¦°°0''‡"~F#U› 1y¤ô¤qÛG€çˆ>}úð5e¼6UôââbìnGðÓ©‚Ø\ˆÉ#¥gwN·Ãáh—_~™ó#Àµ x|4Åx¯ ë°ùÈ‘#¼å#GŽÄÆÿ• aC@l.ÄäaSŒâÀ³Exh‹Dð¾y¡°°páÂþóŸ7nä8kO=õWA‹g“pkK­Ìó‚Ÿbbr¿jô5 ˆÍ…˜< *Ñ~Ä·~Bþ«#–,Yò÷¿ÿ[›àøA4Á[BB€è ü8bÆ œO±8šPãF£éб©OB€>óçÏçc ø|x„ ´‡T"# ¶Óp Õ«W:T‹Ø¼aŠû%Ø\Åár¹šZÜ-^ÄÄ_ôųQÀ~IHD>P+¼†Ÿ8ôz½¡å@ÁÔr˜Íf<Ž„TûÿÌúvôá¹IEND®B`‚doc/example-scripts/tk-spread.tcl000066400000000000000000000057341242365656200173650ustar00rootroot00000000000000# A small Spreadsheet implementation, originally developed by Richard # Suchenwirth in plain Tcl (see http://wiki.tcl.tk/1287). The # spreadsheet was rewritten in an object oriented manner as a design # study in NX by Gustaf Neumann in May 2011. # # image::tk-spread.png[] # package require Tk package require nx::trait ############################################################################## # Class SpreadSheet # # The SpreadSheet computes simply totals for rows and columns. ############################################################################## nx::Class create SpreadSheet { # # The following attributes can be used for configuring the # spreadsheet. # :property {rows:integer 3} :property {cols:integer 2} :property {width:integer 8} # # If no widget is provided, use the name of the object as widget # name. # :property {widget ".[namespace tail [self]]"} # # Use the nx callback trait # :require trait nx::traits::callback # # The method "cell" hides the internal respresentation and sets a # cell to a value. # :method cell {pair value} { set :data($pair) $value } # # The constructor builds the SpreadSheet matrix via multiple text # entry fields. # :method init {} { set :last ${:rows},${:cols} ;# keep grand total field trace var [:bindvar data] w [:callback redo] frame ${:widget} for {set y 0} {$y <= ${:rows}} {incr y} { set row [list] for {set x 0} {$x <= ${:cols}} {incr x} { set e [entry ${:widget}.$y,$x -width ${:width} \ -textvar [:bindvar data($y,$x)] -just right] if {$x==${:cols} || $y==${:rows}} { $e config -state disabled -background grey -relief flat } lappend row $e } grid {*}$row -sticky news } $e config -relief solid } # # The method "redo" is triggered via the updates in the cells # :public method redo {varname el op} { if {$el ne ${:last}} { lassign [split $el ,] y x if {$x ne ""} { :sum $y,* $y,${:cols} :sum *,$x ${:rows},$x } ;# otherwise 'el' was not a cell index } ;# prevent endless recalculation of grand total } # # The method "sum" adds the values matched by pattern (typically a # row or column) and sets finally the target column with the total # :method sum {pat target} { set sum 0 set total "" ;# default if no addition succeeds foreach {i value} [array get :data $pat] { if {$i != $target} { if {[string is double -strict $value]} { set total [set sum [expr {$sum + $value}]] } } } :cell $target $total } } # Build spreadsheet "x" SpreadSheet create x { # populate with some values :cell 0,0 Spread1 :cell 1,0 47 :cell 2,1 11 } # Build spreadsheet "y" SpreadSheet create y -rows 4 -cols 4 { :cell 0,0 Spread2 :cell 1,0 12 :cell 2,2 22 } # Pack the spreadsheets into one pane pack [x cget -widget] [y cget -widget] -fill both doc/example-scripts/traits-composite.html000066400000000000000000000664311242365656200211640ustar00rootroot00000000000000 Listing of doc/example-scripts/traits-composite.tcl

Implementation study based on

  1. Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: Traits: A Mechanism for Fine-grained Reuse, ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006

Fig 13: tReadStream and tWriteStream as composite traits

In this example, traits are used to extend classes and other traits (called then composite traits).

package req nx::trait

Create a Trait called tPositionableStream

nx::Trait create tPositionableStream {
  #
  # Define the methods provided by this trait:
  #
  :public method atStart {} {expr {[:position] == [:minPosition]}}
  :public method atEnd {} {expr {[:position] == [:maxPosition]}}
  :public method setToStart {} {set :position [:minPosition]}
  :public method setToEnd {} {set :position [:maxPosition]}
  :public method maxPosition {} {llength ${:collection}}
  :public method minPosition {} {return 0}
  :public method nextPosition {} {incr :position 1}

  # The trait requires a method "position" and a variable "collection"
  # from the base class or other traits. The definition is incomplete
  # in these regards

  :requiredMethods position
  :requiredVariables collection
}

Create a composite trait called tReadStream based on the trait tPositionableStream:

nx::Trait create tReadStream {
  #
  # Methods provided by this trait:
  #
  :public method on {collection} {set :collection $collection; :setToStart}
  :public method next {} {
    if {[:atEnd]} {return ""} else {
      set r [lindex ${:collection} ${:position}]
      :nextPosition
      return $r
    }
  }

  # This trait requires these methods:
  :requiredMethods {setToStart atEnd nextPosition}

  # Require the trait "tPositionableStream"
  :require trait tPositionableStream
}

Create a composite trait called tWriteStream based on the trait tPositionableStream:

nx::Trait create tWriteStream {
  #
  # Methods provided by this trait:
  #
  :public method on {collection} {set :collection $collection; :setToEnd}
  :public method nextPut {element} {
    lappend :collection $element
    :nextPosition
    return ""
  }

  # This trait requires these methods:
  :requiredMethods {setToEnd nextPosition}

  # Require the trait "tPositionableStream"
  :require trait tPositionableStream
}

Define a class ReadStream with properties position and collection that uses the composite trait tReadStream:

nx::Class create ReadStream {
  :property {collection ""}
  :property -accessor public {position 0}
  :require trait tReadStream
}

Create an instance of ReadStream:

ReadStream create r1 -collection {a b c d e}

Test the behavior of the composed class:

% r1 atStart
1
% r1 atEnd
0
% r1 next
a
% r1 next
b

doc/example-scripts/traits-composite.tcl000066400000000000000000000053301242365656200207710ustar00rootroot00000000000000# Implementation study based on # # S. Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: # Traits: A Mechanism for Fine-grained Reuse, # ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 # # Fig 13: tReadStream and tWriteStream as composite traits # # In this example, traits are used to extend classes and other traits # (called then _composite traits_). # package req nx::test package req nx::trait # # Create a Trait called +tPositionableStream+ # nx::Trait create tPositionableStream { # # Define the methods provided by this trait: # :public method atStart {} {expr {[:position get] == [:minPosition]}} :public method atEnd {} {expr {[:position get] == [:maxPosition]}} :public method setToStart {} {set :position [:minPosition]} :public method setToEnd {} {set :position [:maxPosition]} :public method maxPosition {} {llength ${:collection}} :public method minPosition {} {return 0} :public method nextPosition {} {incr :position 1} # The trait requires a method "position" and a variable "collection" # from the base class or other traits. The definition is incomplete # in these regards :requiredMethods position :requiredVariables collection } # # Create a composite trait called +tReadStream+ based on the trait # +tPositionableStream+: # nx::Trait create tReadStream { # # Methods provided by this trait: # :public method on {collection} {set :collection $collection; :setToStart} :public method next {} { if {[:atEnd]} {return ""} else { set r [lindex ${:collection} ${:position}] :nextPosition return $r } } # This trait requires these methods: :requiredMethods {setToStart atEnd nextPosition} # Require the trait "tPositionableStream" :require trait tPositionableStream } # # Create a composite trait called +tWriteStream+ based on the trait # +tPositionableStream+: # nx::Trait create tWriteStream { # # Methods provided by this trait: # :public method on {collection} {set :collection $collection; :setToEnd} :public method nextPut {element} { lappend :collection $element :nextPosition return "" } # This trait requires these methods: :requiredMethods {setToEnd nextPosition} # Require the trait "tPositionableStream" :require trait tPositionableStream } # Define a class +ReadStream+ with properties +position+ and # +collection+ that uses the composite trait +tReadStream+: nx::Class create ReadStream { :property {collection ""} :property -accessor public {position 0} :require trait tReadStream } # Create an instance of +ReadStream+: ReadStream create r1 -collection {a b c d e} # Test the behavior of the composed class: ? {r1 atStart} 1 ? {r1 atEnd} 0 ? {r1 next} a ? {r1 next} b doc/example-scripts/traits-simple.html000066400000000000000000000604001242365656200204410ustar00rootroot00000000000000 Listing of doc/example-scripts/traits-simple.tcl

Implementation study based on

  1. Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: Traits: A Mechanism for Fine-grained Reuse, ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006

Example in Fig 12: ReadStream and Trait tReadStream

In this example, traits are used to extend classes and other traits.

package require nx::trait

Create a simple trait called tReadStream which provides the interface to a stream. In contrary to a composite trait, a simple trait does not inherit from another trait.

nx::Trait create tReadStream {
  #
  # Define the methods provided by this trait:
  #
  :public method atStart {} {expr {[:position] == [:minPosition]}}
  :public method atEnd {} {expr {[:position] == [:maxPosition]}}
  :public method setToStart {} {set :position [:minPosition]}
  :public method setToEnd {} {set :position [:maxPosition]}
  :public method maxPosition {} {llength ${:collection}}
  :public method on {collection} {set :collection $collection; :setToStart}
  :public method next {} {
    if {[:atEnd]} {return ""} else {
      set r [lindex ${:collection} ${:position}]
      :nextPosition
      return $r
    }
  }
  :public method minPosition {} {return 0}
  :public method nextPosition {} {incr :position 1}

  # This trait requires a method "position" and a variable
  # "collection" from the base class. The definition is incomplete in
  # these regards.

  :requiredMethods position
  :requiredVariables collection
}

Define the class ReadStream with properties position and collection that uses the trait. The method require trait checks the requirements of the trait and imports the methods into ReadStream.

nx::Class create ReadStream {
  :property {collection ""}
  :property -accessor public {position 0}
  :require trait tReadStream
}

Create an instance of the class ReadStream:

ReadStream create r1 -collection {a b c d e}

Test the behavior of the composed class:

% r1 atStart
1
% r1 atEnd
0
% r1 next
a
% r1 next
b

doc/example-scripts/traits-simple.tcl000066400000000000000000000040351242365656200202610ustar00rootroot00000000000000# Implementation study based on # # S. Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: # Traits: A Mechanism for Fine-grained Reuse, # ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 # # Example in Fig 12: ReadStream and Trait tReadStream # # In this example, traits are used to extend classes and other traits. package require nx::test package require nx::trait # # Create a simple trait called +tReadStream+ which provides the # interface to a stream. In contrary to a composite trait, a simple # trait does not inherit from another trait. # nx::Trait create tReadStream { # # Define the methods provided by this trait: # :public method atStart {} {expr {[:position get] == [:minPosition]}} :public method atEnd {} {expr {[:position get] == [:maxPosition]}} :public method setToStart {} {set :position [:minPosition]} :public method setToEnd {} {set :position [:maxPosition]} :public method maxPosition {} {llength ${:collection}} :public method on {collection} {set :collection $collection; :setToStart} :public method next {} { if {[:atEnd]} {return ""} else { set r [lindex ${:collection} ${:position}] :nextPosition return $r } } :public method minPosition {} {return 0} :public method nextPosition {} {incr :position 1} # This trait requires a method "position" and a variable # "collection" from the base class. The definition is incomplete in # these regards. :requiredMethods position :requiredVariables collection } # Define the class +ReadStream+ with properties +position+ and # +collection+ that uses the trait. The method +require trait+ checks the # requirements of the trait and imports the methods into +ReadStream+. nx::Class create ReadStream { :property {collection ""} :property -accessor public {position 0} :require trait tReadStream } # Create an instance of the class +ReadStream+: ReadStream create r1 -collection {a b c d e} # Test the behavior of the composed class: ? {r1 atStart} 1 ? {r1 atEnd} 0 ? {r1 next} a ? {r1 next} b doc/filter.man.inc000066400000000000000000000075001242365656200143720ustar00rootroot00000000000000[comment {-*- tcl -*- manpage fragment for filter method, shared by nx::Object and nx::Class}] [keywords "method filter"] [call [arg [vset CMD]] [const [vset MODIFIER]] [method filters] [arg submethod] [opt "[arg arg] ..."]] Accesses and modifies the list of methods which are registered as [term "filter"]s with [arg [vset CMD]] using a specific setter or getter [arg submethod]: [list_begin definitions] [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {filters add}] [arg spec] [opt [arg index]]"] Inserts a single [term "filter"] into the current list of [term "filter"]s of [arg [vset CMD]]. Using [arg index], a position in the existing list of [term "filter"]s for inserting the new [term "filter"] can be set. If omitted, [arg index] defaults to the list head (0). [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {filters clear}]"] Removes all [term "filter"]s from [arg [vset CMD]] and returns the list of removed [term "filter"]s. Clearing is equivalent to passing an empty list for [arg filterSpecList] to [const [vset SCOPE]] [method {filter set}]. [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {filters delete}] [opt [option -nocomplain]] [arg specPattern]"] Removes a single [term "filter"] from the current list of [term "filter"]s of [arg [vset CMD]] whose spec matches [arg specPattern]. [arg specPattern] can contain special matching chars (see [cmd "string match"]). [const [vset SCOPE]] [method "filters delete"] will throw an error if there is no matching [term "filter"], unless [option -nocomplain] is set. [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {filters get}]"] Returns the list of current [term "filter specification"]s registered for [arg [vset CMD]]. [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {filters guard}] [arg methodName] [opt [arg expr]]"] If [arg expr] is specified, registers a guard expression [arg expr] with a filter [arg methodName]. This requires that the filter [arg methodName] has been previously set using [const [vset MODIFIER]] [method {filters set}] or added using [const [vset MODIFIER]] [method {filters add}]. [arg expr] must be a valid Tcl expression (see [cmd expr]). An empty string for [arg expr] will clear the currently registered guard expression for filter [arg methodName]. [para] If [arg expr] is omitted, returns the guard expression set on the [term "filter"] [arg methodName] defined for [arg [vset CMD]]. If none is available, an empty string will be returned. [def "[arg [vset CMD]] [const [vset MODIFIER]] [method "filters methods"] [opt [arg pattern]]"] If [arg pattern] is omitted, returns all filter names which are defined by [arg [vset CMD]]. By specifying [arg pattern], the returned filters can be limited to those whose names match [arg patterns] (see [cmd "string match"]). [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {filters set}] [arg filterSpecList]"] [arg filterSpecList] takes a list of [term "filter"] specs, with each spec being itself either a one-element or a two-element list: [arg methodName] ?-guard [arg guardExpr]?. [arg methodName] identifies an existing method of [arg [vset CMD]] which becomes registered as a filter. If having three elements, the third element [arg guardExpr] will be stored as a guard expression of the [term "filter"]. This guard expression must be a valid Tcl expression (see [cmd expr]). [arg expr] is evaluated when [arg [vset CMD]] receives a message to determine whether the filter should intercept the message. Guard expressions allow for realizing context-dependent or conditional filter composition. [list_end] Every [arg methodName] in a [arg spec] must resolve to an existing method in the scope of the [vset SCOPE]. To access and to manipulate the list of [term "filter"]s of [arg [vset CMD]], [method cget]|[method configure] [option -[join [list {*}[vset MODIFIER] filters] -]] can also be used. doc/forward.man.inc000066400000000000000000000127361242365656200145600ustar00rootroot00000000000000[comment {-*- tcl -*- manpage fragment for forward method, shared by nx::Object and nx::Class}] [keywords "value checker"] [keywords "forward method"] [keywords "debugging level"] [call [arg [vset CMD]] [opt "[method public] | [method protected] | [method private]"] [method "[vset MODIFIER] forward"] [arg methodName] [opt "[option -prefix] [arg prefixName]"] [opt "[option -frame] [const object]"] [opt "[option -returns] [arg valueChecker]"] [opt [option -verbose]] [opt [arg target]] [opt "[arg arg] ..."]] Define a [term "forward method"] for the given [vset SCOPE]. The definition of a [term "forward method"] registers a predefined, but changeable list of forwarder arguments under the (forwarder) name [arg methodName]. Upon calling the [term "forward method"], the forwarder arguments are evaluated as a Tcl command call. That is, if present, [arg target] is interpreted as a Tcl command (e.g., a Tcl [cmd proc] or an object) and the remainder of the forwarder arguments [arg arg] as arguments passed into this command. The actual method arguments to the invocation of the [term "forward method"] itself are appended to the list of forwarder arguments. If [arg target] is omitted, the value of [arg methodName] is implicitly set and used as [arg target]. This way, when providing a fully-qualified Tcl command name as [arg methodName] without [arg target], the unqualified [arg methodName] ([cmd "namespace tail"]) is used as the forwarder name; while the fully-qualified one serves as the [arg target]. [para] As for a regular [method "[vset MODIFIER] method"], [option "-returns"] allows for setting a [term "value checker"] on the values returned by the resulting Tcl command call. When passing [const object] to [option "-frame"], the resulting Tcl command is evaluated in the context of the object receiving the [term "forward method"] call. This way, variable names used in the resulting execution of a command become resolved as object variables. [para] The list of forwarder arguments [arg arg] can contain as its elements a mix of literal values and placeholders. Placeholders are prefixed with a percent symbol (%) and substituted for concrete values upon calling the [term "forward method"]. These placeholders allow for constructing and for manipulating the arguments to be passed into the resulting command call on the fly: [list_begin itemized] [item] [const %method] becomes substituted for the name of the [term "forward method"], i.e. [arg methodName]. [item] [const %self] becomes substituted for the name of the object receiving the call of the [term "forward method"]. [item] [const %1] becomes substituted for the first method argument passed to the call of [term "forward method"]. This requires, in turn, that [emph "at least"] one argument is passed along with the method call. [para] Alternatively, [const %1] accepts an optional argument [arg defaults]: {[const %1] [arg defaults]}. [arg defaults] must be a valid Tcl list of two elements. For the first element, [const %1] is substituted when there is no first method argument which can be consumed by [const %1]. The second element is inserted upon availability of a first method argument with the consumed argument being appended right after the second list element. This placeholder is typically used to define a pair of getter/setter methods. [item] {[const %@][arg index] [arg value]} becomes substituted for the specified [arg value] at position [arg index] in the forwarder-arguments list, with [arg index] being either a positive integer, a negative integer, or the literal value [const end] (such as in Tcl's [cmd lindex]). Positive integers specify a list position relative to the list head, negative integers give a position relative to the list tail. Indexes for positioning placeholders in the definition of a [term "forward method"] are evaluated from left to right and should be used in ascending order. [para] Note that [arg value] can be a literal or any of the placeholders (e.g., [const %method], [const %self]). Position prefixes are exempted, they are evaluated as [const %][arg cmdName]-placeholders in this context. [item] {[const %argclindex] [arg list]} becomes substituted for the [emph n]th element of the provided [arg list] , with [emph n] corresponding to the number of method arguments passed to the [term "forward method"] call. [item] [const %%] is substituted for a single, literal percent symbol (%). [item] [const %][arg cmdName] is substituted for the value returned from executing the Tcl command [arg cmdName]. To pass arguments to [arg cmdName], the placeholder should be wrapped into a Tcl [cmd list]: {[const %][arg cmdName] [opt "[arg arg] ..."]}. [para] Consider using fully-qualified Tcl command names for [arg cmdName] to avoid possible name conflicts with the predefined placeholders, e.g., [const %self] vs. %[cmd ::nx::self]. [list_end] [para] To disambiguate the names of subcommands or methods, which potentially become called by a [term "forward method"], a prefix [arg prefixName] can be set using [option "-prefix"]. This prefix is prepended automatically to the argument following [arg target] (i.e., a second argument), if present. If missing, [option "-prefix"] has no effect on the [term "forward method"] call. [para] To inspect and to debug the conversions performed by the above placeholders, setting the [term "switch"] [option "-verbose"] will have the command list to be executed (i.e., after substitution) printed using [cmd ::nsf::log] ([term "debugging level"]: [const notice]) upon calling the [term "forward method"]. doc/info.man.inc000066400000000000000000000151321242365656200140400ustar00rootroot00000000000000[comment {-*- tcl -*- manpage fragment for "info *" subset shared by nx::Object and nx::Class }] [keywords "method filter"] [keywords "submethod"] [keywords "parameter specification"] [keywords "type specification"] [keywords "method handle"] [keywords "method path"] [call [arg [vset CMD]] [method {info info}] [opt [option "-asList"]]] Returns the available [term "submethod"]s of the [method info] [term "method ensemble"] for [arg [vset CMD]], either as a pretty-printed string or as a Tcl list (if the [term "switch"] [option -asList] is set) for further processing. [call [arg [vset CMD]] [method "info [vset MODIFIER] filters"] [opt [option -guards]] [opt [arg pattern]]] If [arg pattern] is omitted, returns all filter names which are defined by [arg [vset CMD]]. By turning on the [term switch] [option -guards], the corresponding guard expressions, if any, are also reported along with each filter as a three-element list: [arg filterName] -guard [arg guardExpr]. By specifying [arg pattern], the returned filters can be limited to those whose names match [arg patterns] (see [cmd "string match"]). [call [arg [vset CMD]] [method "info [vset MODIFIER] method"] [arg option] [arg methodName]] This introspection [term "submethod"] provides access to the details of [arg methodName] provided by [arg [vset CMD]]. Permitted values for [arg option] are: [list_begin itemized] [item] [const args] returns a list containing the parameter names of [arg methodName], in order of the method-parameter specification. [item] [const body] returns the body script of [arg methodName]. [item] [const definition] returns a canonical command list which allows for (re-)define [arg methodName]. [item] [const definitionhandle] returns the [term "method handle"] for a [term "submethod"] in a [term "method ensemble"] from the perspective of [arg [vset CMD]] as method provider. [arg methodName] must contain a complete [term "method path"]. [item] [const exists] returns 1 if there is a [arg methodName] provided by [arg [vset CMD]], returns 0 otherwise. [item] [const handle] returns the [term "method handle"] for [arg methodName]. [item] [const origin] returns the aliased command if [arg methodName] is an [term "alias method"], or an empty string otherwise. [item] [const parameters] returns the [term "parameter specification"] of [arg methodName] as a list of parameter names and type specifications. [item] [const registrationhandle] returns the [term "method handle"] for a [term "submethod"] in a [term "method ensemble"] from the perspective of the method caller. [arg methodName] must contain a complete [term "method path"]. [item] [const returns] gives the [term "type specification"] defined for the return value of [arg methodName]. [item] [const submethods] returns the names of all [term "submethod"]s of [arg methodName], if [arg methodName] is a [term "method ensemble"]. Otherwise, an empty string is returned. [item] [const syntax] returns the method parameters of [arg methodName] as a concrete-syntax description to be used in human-understandable messages (e.g., errors or warnings, documentation strings). [item] [const type] returns whether [arg methodName] is a [emph scripted] method, an [emph alias] method, a [emph forwarder] method, or a [emph setter] method. [comment { [item] [const object] [item] [const nsfproc] [item] [const builtin] denotes methods provided a [term "baseclass"]; }] [list_end] [call [arg [vset CMD]] [method "info [vset MODIFIER] methods"] [opt "[option -callprotection] [arg level]"] [opt "[option -type] [arg methodType]"] [opt [option -path]] [opt [arg namePattern]]] Returns the names of all methods defined by [arg [vset CMD]]. Methods covered include those defined using [method "[vset MODIFIER] alias"] and [method "[vset MODIFIER] forward"]. The returned methods can be limited to those whose names match [arg namePattern] (see [cmd "string match"]). [para] By setting [option -callprotection], only methods of a certain [term "call protection"] [arg level] ([const "public"], [const "protected"], or [const "private"]) will be returned. Methods of a specific type can be requested using [option "-type"]. The recognized values for [arg methodType] are: [list_begin itemized] [item] [const scripted] denotes methods defined using [const [vset SCOPE]] [method method]; [item] [const alias] denotes [term "alias method"]s defined using [const [vset SCOPE]] [method alias]; [item] [const forwarder] denotes [term "forwarder method"]s defined using [const [vset SCOPE]] [method forward]; [item] [const setter] denotes methods defined using [cmd ::nsf::setter]; [item] [const all] returns methods of any type, without restrictions (also the default value); [comment { [item] [const object] [item] [const nsfproc] [item] [const builtin] denotes methods provided a [term "baseclass"]; }] [list_end] [call [arg [vset CMD]] [method "info [vset MODIFIER] mixins"] [opt [option -guards]] [opt [arg pattern]]] If [arg pattern] is omitted, returns the object names of the [term "mixin class"]es which extend [arg [vset CMD]] directly. By turning on the [term switch] [option -guards], the corresponding guard expressions, if any, are also reported along with each mixin as a three-element list: [arg className] -guard [arg guardExpr]. The returned [term "mixin class"]es can be limited to those whose names match [arg patterns] (see [cmd "string match"]). [call [arg [vset CMD]] [method "info [vset MODIFIER] slots"] [opt "[option -type] [arg className]"] [opt [arg pattern]]] If [arg pattern] is not specified, returns the object names of all [term "slot object"]s defined by [arg [vset CMD]]. The returned [term "slot object"]s can be limited according to any or a combination of the following criteria: First, [term "slot object"]s can be filtered based on their command names matching [arg pattern] (see [cmd "string match"]). Second, [option "-type"] allows one to select [term "slot object"]s which are instantiated from a subclass [arg className] of [cmd nx::Slot] (default: [cmd nx::Slot]). [call [arg [vset CMD]] [method "info [vset MODIFIER] variables"] [opt [arg pattern]]] If [arg pattern] is omitted, returns the object names of all [term "slot object"]s provided by [arg [vset CMD]] which are responsible for managing properties and variables of [arg [vset CMD]]. Otherwise, only [term "slot object"]s whose names match [arg pattern] are returned. [para] This is equivalent to calling: [arg [vset CMD]] [method "info [vset MODIFIER] slots"] [option -type] [cmd ::nx::VariableSlot] [arg pattern]. [para] To extract details of each [term "slot object"], use the [method info] submethods available for each [term "slot object"]. doc/langRef2.xotcl000066400000000000000000000112351242365656200143530ustar00rootroot00000000000000# -*- tcl -*- source [file dirname [info script]]/xodoc-tools.xotcl namespace eval ::xodoc-tools { @ xotclCmd ::xotcl::self { The command [:cmd ${:name}] returns callstack related information } { :variant self { [:cmd ${:name}] returns the name of the object, which is currently executed. If it is called from outside of an XOTcl method, it returns an error. } :variant "self class" { [:cmd ${:name}] returns the name of the class, which holds the currently executing method. Note, that this may be different to the class of the current object. If it is called from an object specific method it returns an empty string. } :variant "self method" { [:cmd ${:name}] returns the name of the currently executing method. } :variant "self callingclass" { [:cmd ${:name}] returns the name of the class that has called the executing method. } :variant "self callingobject" { [:cmd ${:name}] returns the name of the object that has the object that has called the executing method. } :variant "self callingmethod" { [:cmd ${:name}] returns the name of the method that has called the executing method. } :variant "self calledclass" { [:cmd ${:name}] returns the class name of the class that holds the target method (in mixins and filters). } :variant "self calledmethod" { [:cmd ${:name}] returns the name of the target called method (only applicable in a filter). } :variant "self isnextcall" { [:cmd ${:name}] returns 1 if this method was invoked via next, otherwise 0. } :variant "self next" { [:cmd ${:name}] returns the "next" method on the precedence path. } :variant "self filterreg" { [:cmd ${:name}] returns the name of the object/class on which the filter is registered. } :variant "self callinglevel" { [:cmd ${:name}] returns the calling level, from where the actual method was called from. Intermediary next calls are ignored in this computation. The level is returned in a form that can be used as first argument in [:cmd uplevel] or [:cmd upvar]. } :variant "self activelevel" { [:cmd ${:name}] returns the level, from where the actual method was invoked from. This might be the calling level or a next call, whatever is higher in the stack. The level is returned in a form that can be used as first argument in [:cmd uplevel] or [:cmd upvar]. } } @ xotclCmd ::xotcl::alias -arguments { object -per-object:switch methodName -nonleaf:switch -objscope:switch cmd } { The command [:cmd ${:name}] is used to register a Tcl command as method for an object or class. } { :param object {is the object or class, on which the command is registered as a method} :param -per-object {if provided, the method is an per-object method (not inheritable)} :param methodName {the name for the method to be added} :param -nonleaf:switch {if provided, the method will have a call stack entry such it can call [:cmd next]} :param -objscope:switch {if provided, the variables created during the execution of the method will be instance variables} :param cmd {is the Tcl command to be registered as method} } @ xotclClass ::xotcl2::Object { This class holds the pre-defined methods available for all XOTcl 2 objects. These methods are also available on XOTcl 2 classes. } # # i think, we do not have to distinguish between alias registered on Object and Class # @ xotclMethod alias -partof ::xotcl2::Object \ -arguments {-nonleaf:switch -objscope:switch methodName cmd} \ { This method is used to register an existing Tcl command as a method for a class or object. } { :param "-nonleaf:switch" {} -use ::xotcl::alias :param "-objscope:switch" {} -use ::xotcl::alias :param "methodName" {} -use ::xotcl::alias :param "cmd" {} -use ::xotcl::alias :returns "Fully qualified method name" } @ xotclMethod setter -partof ::xotcl2::Object \ -arguments {methodName} { Register a method as a setter for same-named instance variables } { :param methodName {the name for the method to be added} :returns "Fully qualified method name" } @ xotclClass ::xotcl2::Class { } } namespace eval ::xodoc-tools { puts "

Primitive XOTcl framework commands

\n
    " foreach cmd [sorted [xotclCmd info instances] name] { $cmd renderCmd } puts "
\n\n" puts "

XOTcl Classes

\n
    " foreach cmd [sorted [xotclClass info instances] name] { $cmd renderClass } puts "
\n\n" } doc/man-princexml.css000066400000000000000000000006051242365656200151230ustar00rootroot00000000000000@page { size: A4; /* DIV 15 for A4 paper */ margin: 19.80mm 14mm; @top { font-family: "Arial","Verdana", sans-serif; color: #666; font-size: 80%; content: string(chapter-title) } @bottom { font-family: "Arial","Verdana", sans-serif; font-size: 80%; color: #666; content: "- " counter(page) " -" } } h2 { string-set: chapter-title content() }doc/man.css000066400000000000000000000076601242365656200131340ustar00rootroot00000000000000 body { color: #252525; font: 13px/1.231 "Arial","Verdana", sans-serif; text-align: left; } DIV.doctools { margin-left: 10%; margin-right: 10%; } DIV.doctools H1,DIV.doctools H2 { margin-left: -5%; } H1, H2, H3, H4 { margin-top: 1em; font-family: sans-serif; font-size: large; color: #89a618; background: transparent; text-align: left; } h2 { border-bottom: 1px solid #89a618; text-transform: uppercase; } H1.title { text-align: center; } /* UL,OL { margin-right: 0em; margin-top: 3pt; margin-bottom: 3pt; } */ UL LI { list-style: disc; } OL LI { list-style: decimal; } DT { padding-top: 1ex; } UL.toc,UL.toc UL, UL.toc UL UL { font: normal 12pt/14pt sans-serif; list-style: none; } LI.section, LI.subsection { list-style: none; margin-left: 0em; text-indent: 0em; padding: 0em; } PRE { display: block; font-family: monospace; white-space: pre; margin: 0%; padding-top: 0.5ex; padding-bottom: 0.5ex; padding-left: 1ex; padding-right: 1ex; width: 100%; } PRE.example { color: black; background: #f5dcb3; border: 1px solid black; } UL.requirements LI, UL.syntax LI { list-style: none; margin-left: 0em; text-indent: 0em; padding: 0em; } /* DIV.synopsis { color: black; background: #80ffff; border: 1px solid black; font-family: serif; margin-top: 1em; margin-bottom: 1em; } */ /* UL.syntax { margin-top: 1em; border-top: 1px solid black; }*/ UL.requirements { margin-bottom: 1em; border-bottom: 1px solid black; } i.term { border-bottom: 1px dotted #000000; font-style: normal; text-decoration: none; } dl.definitions b.method { background: none repeat scroll 0 0 #fcfbfa; border: 1px solid #efeeed; padding: 0 3px; font-family: Menlo,Monaco,"DejaVu Sans Mono","Bitstream Vera Sans Mono","Courier New",Courier,monospace; font-size: 92.308%; line-height: 1.35; font-weight: bold; } dl.definitions i.arg, dl.options i.arg { background: none repeat scroll 0 0 #ffffff; border: 1px solid #efeeed; padding: 0 3px; font-family: Menlo,Monaco,"DejaVu Sans Mono","Bitstream Vera Sans Mono","Courier New",Courier,monospace; font-size: 92.308%; line-height: 1.35; font-weight: normal; } dl.definitions b.option, dl.options b.option { background: none repeat scroll 0 0 #ffffff; border: 1px solid #efeeed; padding: 0 3px; font-family: Menlo,Monaco,"DejaVu Sans Mono","Bitstream Vera Sans Mono","Courier New",Courier,monospace; font-size: 92.308%; line-height: 1.35; font-weight: normal; text-decoration: none; } dl.definitions b.cmd, p b.cmd { background: none repeat scroll 0 0 #e7edd3; border: 1px solid #89a618; padding: 0 3px; font-family: Menlo,Monaco,"DejaVu Sans Mono","Bitstream Vera Sans Mono","Courier New",Courier,monospace; font-size: 92.308%; line-height: 1.35; font-weight: bold; } dl.definitions b.const, p b.const { /* background: none repeat scroll 0 0 #ffffff; */ /* border: 1px solid #efeeed; */ padding: 0 3px; font-family: Menlo,Monaco,"DejaVu Sans Mono","Bitstream Vera Sans Mono","Courier New",Courier,monospace; font-size: 92.308%; line-height: 1.35; font-weight: normal; text-decoration: none; } code, kbd, pre, samp { font-family: Menlo,Monaco,"DejaVu Sans Mono","Bitstream Vera Sans Mono","Courier New",Courier,monospace; font-size: 92.308%; line-height: 1.35; } pre.example { background: none repeat scroll 0 0 #fcfbfa; border-color: #efeeed; border-image: none; border-style: solid; border-width: 1px 1px 1px 5px; } div.synopsis { background: none repeat scroll 0 0 #e7edd3; border-color: #d4d8eb; border: 1px solid; border-radius: 3px; margin: 1em 0; padding: 0 1em; } a:link { color: #002a4b; text-decoration: none }doc/method.man.inc000066400000000000000000000043071242365656200143670ustar00rootroot00000000000000[comment {-*- tcl -*- manpage fragment for method method, shared by nx::Object and nx::Class}] [call [arg [vset CMD]] [opt "[method public] | [method protected] | [method private]"] [method "[vset MODIFIER] method"] [arg name] [arg parameters] [opt [option "-checkalways"]] [opt "[option -returns] [arg valueChecker]"] [arg body]] [keywords "value checker"] [keywords "colon-prefix notation"] [keywords "self call"] [keywords "alias method"] Defines a scripted method [arg methodName] for the scope of the [vset SCOPE]. The method becomes part of the [vset SCOPE]'s signature interface. Besides a [arg methodName], the method definition specifies the method [arg parameters] and a method [arg body]. [para] [arg parameters] accepts a Tcl [cmd list] containing an arbitrary number of non-positional and positional parameter definitions. Each parameter definition comprises a parameter name, a parameter-specific [term "value checker"], and parameter options. [para] The [arg body] contains the method implementation as a script block. In this body script, the colon-prefix notation is available to denote an object variable and a [term "self call"]. In addition, the context of the object receiving the method call (i.e., the message) can be accessed (e.g., using [cmd nx::self]) and the call stack can be introspected (e.g., using [cmd nx::current]). [para] Optionally, [option "-returns"] allows for setting a [term "value checker"] on values returned by the method implementation. By setting the [term "switch"] [option "-checkalways"], value checking on arguments and return value is guaranteed to be performed, even if value checking is temporarily disabled; see [cmd nx::configure]). [para] A method closely resembles a Tcl [cmd proc], but it differs in some important aspects: First, a method can define non-positional parameters and value checkers on arguments. Second, the script implementing the method body can contain object-specific notation and commands (see above). Third, method calls [emph cannot] be intercepted using Tcl [cmd trace]. Note that an existing Tcl [cmd proc] can be registered as an [term "alias method"] with the [vset SCOPE] (see [method "[vset MODIFIER] alias"]). [comment {TODO: refer to nsf::proc?}] doc/mixin.man.inc000066400000000000000000000106361242365656200142350ustar00rootroot00000000000000[comment {-*- tcl -*- manpage fragment for mixin method, shared by nx::Object and nx::Class}] [keywords "mixin class"] [keywords linearisation] [call [arg [vset CMD]] [method "[vset MODIFIER] mixins"] [arg submethod] [opt "[arg arg] ..."]] Accesses and modifies the list of [term "mixin class"]es of [arg [vset CMD]] using a specific setter or getter [arg submethod]: [list_begin definitions] [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {mixins add}] [arg spec] [opt [arg index]]"] Inserts a single [term "mixin class"] into the current list of [term "mixin class"]es of [arg [vset CMD]]. Using [arg index], a position in the existing list of [term "mixin class"]es for inserting the new [term "mixin class"] can be set. If omitted, [arg index] defaults to the list head (0). [comment {Therefore, by default, any added [term "mixin class"] takes precedence over previously added classes in the overall linearisation of [arg [vset CMD]].}] [def "[arg [vset CMD]] [const [vset MODIFIER]] [method "mixins classes"] [opt [arg pattern]]"] If [arg pattern] is omitted, returns the object names of the [term "mixin class"]es which extend [arg [vset CMD]] directly. By specifying [arg pattern], the returned [term "mixin class"]es can be limited to those whose names match [arg pattern] (see [cmd "string match"]). [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {mixins clear}]"] Removes all [term "mixin class"]es from [arg [vset CMD]] and returns the list of removed [term "mixin class"]es. Clearing is equivalent to passing an empty list for [arg mixinSpecList] to [const [vset MODIFIER]] [method {mixins set}]. [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {mixins delete}] [opt [option -nocomplain]] [arg specPattern]"] Removes a [term "mixin class"] from a current list of [term "mixin class"]es of [arg [vset CMD]] whose spec matches [arg specPattern]. [arg specPattern] can contain special matching chars (see [cmd "string match"]). [const [vset SCOPE]] [method "mixins delete"] will throw an error if there is no matching [term "mixin class"], unless [option -nocomplain] is set. [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {mixins get}]"] Returns the list of current [term "mixin specification"]s. [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {mixins guard}] [arg className] [opt [arg expr]]"] If [arg expr] is specified, a guard expression [arg expr] is registered with the [term "mixin class"] [arg className]. This requires that the corresponding [term "mixin class"] [arg className] has been previously set using [const [vset SCOPE]] [method {mixins set}] or added using [const [vset MODIFIER]] [method {mixins add}]. [arg expr] must be a valid Tcl expression (see [cmd expr]). An empty string for [arg expr] will clear the currently registered guard expression for the [term "mixin class"] [arg className]. [para] If [arg expr] is not specified, returns the active guard expression. If none is available, an empty string will be returned. [def "[arg [vset CMD]] [const [vset MODIFIER]] [method {mixins set}] [arg mixinSpecList]"] [arg mixinSpecList] represents a list of [term "mixin class"] specs, with each spec being itself either a one-element or a three-element list: [arg className] ?-guard [arg guardExpr]?. If having one element, the element will be considered the [arg className] of the [term "mixin class"]. If having three elements, the third element [arg guardExpr] will be stored as a guard expression of the [term "mixin class"]. This guard expression will be evaluated using [cmd expr] when [arg [vset CMD]] receives a message to determine if the mixin is to be considered during method dispatch or not. Guard expressions allow for realizing context-dependent or conditional mixin composition. [list_end] [comment { A [term "mixin class"] whose spec is featured earlier in [arg mixinSpecList] takes precedence in the [term "linearisation"] over a [term "mixin class"] whose spec is listed later. The computed, overall [term linearisation] of [arg [vset CMD]] guarantees to maintain this local order of [term "mixin class"]es. }] At the time of setting the mixin relation, that is, calling [const [vset MODIFIER]] [method mixins], every [arg className] as part of a spec must be an existing instance of [cmd nx::Class]. To access and to manipulate the list of [term "mixin class"]es of [arg [vset CMD]], [method cget]|[method configure] [option -[join [list {*}[vset MODIFIER] mixins] -]] can also be used. doc/next-migration.html000066400000000000000000012564751242365656200155150ustar00rootroot00000000000000 Migration Guide for the Next Scripting Language
Abstract

This document describes the differences between the Next Scripting Language Framework and XOTcl 1. In particular, it presents a migration guide from XOTcl 1 to NX, and presents potential incompatibilities beween XOTcl 1 and XOTcl 2.

The Next Scripting Language (NX) is a successor of XOTcl 1 and is based on 10 years of experience with XOTcl in projects containing several hundert thousand lines of code. While XOTcl was the first language designed to provide language support for design patterns, the focus of the Next Scripting Framework and NX are on combining this with Language Oriented Programming. In many respects, NX was designed to ease the learning of the language by novices (by using a more mainstream terminology, higher orthogonality of the methods, less predefined methods), to improve maintainability (remove sources of common errors) and to encourage developer to write better structured programs (to provide interfaces) especially for large projects, where many developers are involved.

The Next Scripting Language is based on the Next Scripting Framework which was developed based on the notion of language oriented programming. The Next Scripting Frameworks provides C-level support for defining and hosting multiple object systems in a single Tcl interpreter. The whole definition of NX is fully scripted (e.g. defined in nx.tcl). The Next Scripting Framework is shipped with three language definitions, containing NX and XOTcl 2. Most of the existing XOTcl 1 programs can be used without modification in the Next Scripting Framework by using XOTcl 2. The Next Scripting Framework requires Tcl 8.5 or newer.

Although NX is fully scripted (as well as XOTcl 2), our benchmarks show that scripts based on NX are often 2 or 4 times faster than the counterparts in XOTcl 1. But speed was not the primary focus on the Next Scripting Environment: The goal was primarily to find ways to repackage the power of XOTcl in an easy to learn environment, highly orthogonal environment, which is better suited for large projects, trying to reduce maintenance costs.

We expect that many user will find it attractive to upgrade from XOTcl 1 to XOTcl 2, and some other users will upgrade to NX. This document focuses mainly on the differences between XOTcl 1 and NX, but addresses as well potential incompatibilities between XOTcl 1 and XOTcl 2. For an introduction to NX, please consult the NX tutorial.

1. Differences Between XOTcl and NX

The Next Scripting Framework supports Language Oriented Programming by providing means to define potentially multiple object systems with different naming and functionality in a single interpreter. This makes the Next Scripting Framework a powerful instrument for defining multiple languages such as e.g. domain specific languages. This focus differs from XOTcl 1.

Technically, the language framework approach means that the languages implemented by the Next Scripting Framework (most prominently XOTcl 2 and NX) are typically fully scripted and can be loaded via the usual Tcl package require mechanism.

Some of the new features below are provided by the Next Scripting Framework, some are implemented via the script files for XOTcl 2 and NX.

1.1. Features of NX

In general, the Next Scripting Language (NX) differs from XOTcl in the following respects:

  1. Stronger Encapsulation: The Next Scripting Language favors a stronger form of encapsulation than XOTcl. Calling the own methods or accessing the own instance variables is typographically easier and computationally faster than these operations on other objects. This behavior is achieved via resolvers, which make some methods necessary in XOTcl 1 obsolete in NX (especially for importing instance variables). The encapsulation of NX is stronger than in XOTcl but still weak compared to languages like C++; a developer can still access other objects' variables via some idioms, but NX makes accesses to other objects' variables explicit. The requiredness to make these accesses explicit should encourage developer to implement well defined interfaces to provide access to instance variables.

  2. Additional Forms of Method Definition and Reuse: The Next Scripting Language provides much more orthogonal means to define, reuse and introspect scripted and C-implemented methods.

    1. It is possible to use NX alias to register methods under arbitrary names for arbitrary objects or classes.

    2. NX provides means for method protection (method modifiers public, protected, and private). Therefore developers have to define explicitly public interfaces in order to use methods from other objects.

    3. One can invoke in NX fully qualified methods to invoke methods outside the precedence path.

    4. One can define in NX hierachical method names (similar to commands and subcommands, called method ensembles) in a convenient way to provide extensible, hierarchical naming of methods.

    5. One can use in NX the same interface to query (introspect) C-implemented and scripted methods/commands.

  3. Orthogonal Parameterization: The Next Scripting Language provides an orthogonal framework for parametrization of methods and objects.

    1. In NX, the same argument parser is used for

      • Scripted Methods

      • C-implemented methods and Tcl commands

      • Object Parametrization

    2. While XOTcl 1 provided only value-checkers for non-positional arguments for methods, the Next Scripting Framework provides the same value checkers for positional and non-positional arguments of methods, as well as for positional and non-positional configure parameters (-parameter in XOTcl 1).

    3. While XOTcl 1 supported only non-positional arguments at the begin of the argument list, these can be used now at arbitrary positions.

  4. Value Checking:

    1. The Next Scripting Language supports checking of the input parmeters and the return values of scripted and C-implemented methods and commands.

    2. NX provides a set of predefined checkers (like e.g. integer, boolean, object, …) which can be extended by the applications.

    3. Value Checking can be used for single and multi-valued parameters. One can e.g. define a list of integers with at least one entry by the parameter specification integer,1..n.

    4. Value Checking can be turned on/off globally or on the method/command level.

  5. Scripted Init Blocks: The Next Scripting Language provides scripted init blocks for objects and classes (replacement for the dangerous dash "-" mechanism in XOTcl that allows to set variables and invoke methods upon object creation).

  6. More Conventional Naming for Predefined Methods: The naming of the methods in the Next Scripting Language is much more in line with the mainstream naming conventions in OO languages. While for example XOTcl uses proc and instproc for object specific and inheritable methods, NX uses simply method.

  7. Profiling Support: The Next Scripting Language provides now two forms of profiling

    • Profiling via a DTrace provider (examples are e.g. in the dtrace subdirectory of the source tree)

    • Significantly improved built-in profiling (results can be processed in Tcl).

  8. Significantly Improved Test Suite: The regression test suite of Next Scripting Scripting framework contain now more than 5.000 tests, and order of magnitude more than in XOTcl 1.6

  9. Much Smaller Interface: The Next Scripting Language has a much smaller interface (i.e. provides less predefined methods) than XOTcl (see Table 1), although the expressability was increased in NX.

Table 1. Comparison of the Number of Predefined Methods in NX and XOTcl
NX XOTcl

Total

45

124

Methods for Objects

14

51

Methods for Classes

9

24

Info-methods for Objects

11

25

Info-methods for Classes

11

24

This comparison list compares mostly XOTcl 1 with NX, some features are also available in XOTcl 2 (2a, 2c 2d, 3, 4).

1.2. NX and XOTcl Scripts

Below is a small, introductory example showing an implementation of a class Stack in NX and XOTcl. The purpose of this first example is just a quick overview. We will go into much more detailed comparison in the next sections.

NX supports a block syntax, where the methods are defined during the creation of the class. The XOTcl syntax is slightly more redundant, since every definition of a method is a single toplevel command starting with the class name (also NX supports the style used in XOTcl). In NX, all methods are per default protected (XOTcl does not support protection). In NX methods are defined in the definition of the class via :method or :public method. In XOTcl methods are defined via the instproc method.

Another difference is the notation to refer to instance variables. In NX, instance variable are named with a single colon in the front. In XOTcl, instance variables are imported using instvar.

Stack example in NX Stack example in XOTcl
Class create Stack {

   #
   # Stack of Things
   #

   :variable things ""

   :public method push {thing} {
      set :things [linsert ${:things} 0 $thing]
      return $thing
   }

   :public method pop {} {
      set top [lindex ${:things} 0]
      set :things [lrange ${:things} 1 end]
      return $top
   }
}
#
# Stack of Things
#

Class Stack

Stack instproc init {} {
   my instvar things
   set things ""
}

Stack instproc push {thing} {
   my instvar things
   set things [linsert $things 0 $thing]
   return $thing
}

Stack instproc pop {} {
   my instvar things
   set top [lindex $things 0]
   set things [lrange $things 1 end]
}

1.3. Using XOTcl 2.0 and the Next Scripting Language in a Single Interpreter

In general, the Next Scripting Framework supports multiple object systems concurrently. Effectively, every object system has different base classes for creating objects and classes. Therefore, these object systems can have different different interfaces and names of built-in methods. Currently, the Next Scripting Framework is packaged with three object systems:

  • NX

  • XOTcl 2.0

  • TclCool

XOTcl 2 is highly compatible with XOTcl 1, the language NX is described below in more details, the language TclCool was introduced in Tip#279 and serves primarily an example of a small OO language.

A single Tcl interpreter can host multiple Next Scripting Object Systems at the same time. This fact makes migration from XOTcl to NX easier. The following example script shows to use XOTcl and NX in a single script:

Using Multiple Object Systems in a single Script
namespace eval mypackage {

  package require XOTcl 2.0

  # Define a class with a public method foo using XOTcl
  xotcl::Class C1
  C1 instproc foo {} {puts "hello world"}

  package require nx

  # Define a class with a public method foo using NX
  nx::Class create C2 {
    :public method foo {} {puts "hello world"}
  }
}

One could certainly create object or classes from the different object systems via fully qualified names (e.g. using e.g. ::xotcl::Class or ::nx::Class), but for migration for systems without explicit namespaces switching between the object systems eases migration. "Switching" between XOTcl and NX effectively means the load some packages (if needed) and to import either the base classes (Object and Class) of XOTcl or NX into the current namespace.

2. XOTcl Idioms in the Next Scripting Language

The following sections are intended for reader familiar with XOTcl and show, how certain language Idioms of XOTcl can be expressed in NX. In some cases, multiple possible realizations are listed

2.1. Defining Objects and Classes

When creating objects or classes, one should use the method create explicitly. In XOTcl, a default unknown method handler was provided for classes, which create for every unknown method invocation an object/class with the name of the invoked method. This technique was convenient, but as well dangerous, since typos in method names lead easily to unexpected behavior. This default unknown method handler is not provided in NX (but can certainly be provided as a one-liner in NX by the application).

XOTcl Next Scripting Language
Class ClassName
Class create ClassName
Object ObjectName
Object create ObjectName

2.2. Defining Methods

In general, both XOTcl and NX support methods on the object level (per-object methods, i.e. methods only applicable to a single object) and on the class level (methods inherited to instances of the classes). While the naming in XOTcl tried to follow closely the Tcl tradition (using the term proc for functions/methods), NX uses the term method for defining scripted methods.

XOTcl uses the prefix inst to denote that methods are provided for instances, calling therefore scripted methods for instances instproc. This is certainly an unusual term. The approach with the name prefix has the disadvantage, that for every different kind of method, two names have to be provided (eg. proc and instproc, forward and instforward).

NX on the contrary uses the same term for defining instance method or object-specific methods. When the term (e.g. method) is used on a class, the method will be an instance method (i.e. applicable to the instances of the class). When the term is used on an object with the modifier object, an object-specific method is defined. This way one can define the same way object specific methods on an object as well as on a class.

Furthermore, both XOTcl and NX distinguish between scripted methods (section 3.2.1) and C-defined methods (section 3.2.2). Section 3.2.3 introduces method protection, which is only supported by NX.

2.2.1. Scripted Methods Defined in the Init-block of a Class/Object or with Separate Calls

The following examples show the definition of a class and its methods in the init-block of a class (NX only), and the definition of methods via separate top level calls (XOTcl and NX).

XOTcl Next Scripting Language
# Define instance method 'foo' and object
# method 'bar' for a Class 'C' with separate
# toplevel commands

Class C
C instproc foo args {...}
C proc bar args {...}
# Define instance method and object method
# in the init-block of a class

Class create C {
  :method foo args {...}
  :object method bar args {...}
}
# Define instance method and object method
# with separate commands

Class create C
C method foo args {...}
C object method bar args {...}
# Define object-specific method foo
# for an object 'o' with separate commands

Object o
o set x 1
o proc foo args {...}
# Define object method and set
# instance variable in the init-block of
# an object

Object create o {
  set :x 1
  :object method foo args {...}
}
# Define object method and set
# instance variable with separate
# commands

Object create o
o eval {set :x 1}
o object method foo args {...}

2.2.2. Different Kinds of Methods

This section describes various kinds of methods. The different kinds of methods are defined via different method-defining methods, which are summarized in the following table for XOTcl and NX.

XOTcl Next Scripting Language
# Methods for defining methods:
#
#     proc
#     instproc
#     forward
#     instforward
#     parametercmd
#     instparametercmd
#
# All these methods return empty.
# Methods for defining methods:
#
#     alias
#     forward
#     method
#
# All these methods return method-handles.

In addition to scripted methods (previous section) XOTcl supports forwarder (called forward and instforward) and accessor functions to variables (called parametercmd and instparametercmd). The accessor functions are used normally internally when object-specific parameters are defined (see Section 3.4).

In NX forwarders are called forward. NX does not provide an public available method to define variable accessors like parametercmd in XOTcl, but use interanlly the Next Scripting Framework primitive nsf::method::setter when appropriate.

XOTcl Next Scripting Language
Class C
C instforward f1 ...
C forward f2 ...

Object o
o forward f3 ...
# Define forwarder

Class create C {
  :forward f1 ...
  :object forward f2 ...
}

Object create o {
  :object forward f3 ...
}
# Define setter and getter methods in XOTcl.
#
# XOTcl provides methods for these.

Class C
C instparametercmd p1
C parametercmd p2

Object o
o parametercmd p3
# Define setter and getter methods in NX.
#
# NX does not provide own methods, but uses
# the low level framework commands, since
# application developer will only seldomly
# need it.

Class create C
::nsf::method::setter C p1
::nsf::method::setter C -per-object p2

Object create o
::nsf::method::setter o p3

NX supports in contrary to XOTcl the method alias which can be used to register arbitrary Tcl commands or methods for an object or class under a provided method name. Aliases can be used to reuse a certain implementation in e.g. different object systems under potentially different names. In some respects aliases are similar to forwarders, but they do not involve forwarding overhead.

XOTcl Next Scripting Language
# Method "alias" not available
# Define method aliases
# (to scripted or non-scripted methods)

Class create C {
  :alias a1 ...
  :object alias a2 ...
}

Object create o {
  :object alias a3 ...
}

2.2.3. Method Modifiers and Method Protection

NX supports four method modifiers object, public, protected and private. All method modifiers can be written in front of every method defining command. The method modifier object is used to denote object-specific methods (see above). The concept of method protection is new in NX.

XOTcl Next Scripting Language
# Method modifiers
#
#   "object",
#   "public",
#   "protected", and
#   "private"
#
# are not available
# Method modifiers
#
#   "object",
#   "public",
#   "protected"
#
# are applicable for all kinds of
# method defining methods:
#
#    method, forward, alias
#
# The modifier "private" is available for
#
#    method, forward, alias
#
Class create C {
  :/method-definiton-method/ ...
  :public /method-definiton-method/ ...
  :protected /method-definiton-method/ ...
  :private /method-definiton-method/ ...
  :object /method-definiton-method/ ...
  :public object /method-definiton-method/ ...
  :protected object /method-definiton-method/ ...
  :private object /method-definiton-method/ ...
}

XOTcl does not provide method protection. In NX, all methods are defined per default as protected. This default can be changed by the application developer in various ways. The command ::nx::configure defaultMethodCallProtection true|false can be used to set the default call protection for scripted methods, forwarder and aliases. The defaults can be overwritten also on a class level.

NX provides means for method hiding via the method modifier private. Hidden methods can be invoked only via the -local flag, which means: "call the specified method defined in the same class/object as the currently executing method".

XOTcl Next Scripting Language
# XOTcl provides no means for
# method hiding
# Hiding of methods via "private"
#
nx::Class create Base {
  :private method baz {a b} {expr {$a + $b}}
  :public method foo {a b} {: -local baz $a $b}
}

nx::Class create Sub -superclass Base {
  :public method bar {a b} {: -local baz $a $b}
  :private method baz {a b} {expr {$a * $b}}

  :create s1
}

s1 foo 3 4  ;# returns 7
s1 bar 3 4  ;# returns 12
s1 baz 3 4  ;# unable to dispatch method 'baz'

2.2.4. Method Deletion

NX provides an explicit delete method for the deletion of methods.

XOTcl Next Scripting Language
# XOTcl provides only method deletion with
# the equivalent of Tcl's "proc foo {} {}"
/cls/ instproc foo {} {}
/obj/ proc foo {} {}
# Deletion of Methods
#
/cls/ delete method /name/
/obj/ delete object method /name/

2.3. Resolvers

The Next Scripting Framework defines Tcl resolvers for method and variable names to implement object specific behavior. Within the bodies of scripted methods these resolver treat variable and function names starting with a colon : specially. In short, a colon-prefixed variable name refers to an instance variable, and a colon-prefixed function name refers to a method. The sub-sections below provide detailed examples.

Note that the resolvers of the Next Scripting Framework can be used in the XOTcl 2.* environment as well.

2.3.1. Invoking Methods

In XOTcl, a method of the same object can be invoked via my, or in general via using the name of the object in front of the method name.

In NX, the own methods are called via the method name prefixed with a single colon. The invocation of the methods of other objects is the same in NX and XOTcl.

XOTcl Next Scripting Language
Class C
C instproc foo args {...}
C instproc bar args {
  my foo 1 2 3 ;# invoke own method
  o baz        ;# invoke other object's method
}
Object o
o proc baz {} {...}
Class create C {
  :method foo args {...}
  :method bar args {
     :foo 1 2 3 ;# invoke own method
     o baz      ;# invoke other object's method
  }
}
Object create o {
  :public object method baz {} {...}
}

2.3.2. Accessing Own Instance Variables from Method Bodies

In general, the Next Scripting Language favors the access to an objects’s own instance variables over variable accesses of other objects. This means that in NX it is syntactically easier to access the own instance variables. On the contrary, in XOTcl, the variable access to own and other variables are fully symmetric.

In XOTcl, the following approaches are used to access instance variables:

  • Import instance variables via instvar and access variables via $varName

  • Set or get instance variables via my set varName ?value? or other variable accessing methods registered on xotcl::Object such as append, lappend, incr, etc.

  • Register same-named accessor functions and set/get values of instance variables via my varName ?value?

In NX, the favored approach to access instance variables is to use the name resolvers, although it is as well possible to import variables via nx::var import or to check for the existence of instance variables via nx::var exists.

The following examples summary the use cases for accessing the own and other instance variables.

XOTcl Next Scripting Language
Class C
C instproc foo args {
  # Method scoped variable a
  set a 1
  # Instance variable b
  my instvar b
  set b 2
  # Global variable/namespaced variable c
  set ::c 3
}
Class create C {
  :method foo args {...}
    # Method scoped variable a
    set a 1
    # Instance variable b
    set :b 2
    # Global variable/namespaced variable c
    set ::c 3
  }
}
... instproc ... {
   my set /varName/ ?value?
}
# Set own instance variable to a value via
# resolver (preferred and fastest way)

... method ... {
   set :/newVar/ ?value?
}
... instproc ... {
   my instvar /varName/
   set /varName/ ?value?
}
# Set own instance variable via
# variable import

... method ... {
   ::nx::var import [self] /varName/
   set /varName/ ?value?
}
... instproc ... {
   set /varName/ [my set /otherVar/]
}
# Read own instance variable

... method ... {
   set /varName/ [set :/otherVar/]
}
... method ... {
   set /newVar/ ${:/otherVar/}
}
... instproc ... {
   my exists /varName/
}
# Test existence of own instance variable

... method ... {
   info :/varName/
}
 ... method ... {
   ::nx::var exists [self] /varName/
}

2.3.3. Accessing Instance Variables of other Objects

XOTcl Next Scripting Language
/obj/ set /varName/ ?value?
# Set instance variable of object obj to a
# value via resolver
# (preferred way: define property on obj)

/obj/ eval [list set :/varName/ ?value?]
set /varName/ [/obj/ set /otherVar/]
# Read instance variable of object obj
# via resolver

set /varName/ [/obj/ eval {set :/otherVar/}]
... instproc ... {
   /obj/ instvar /varName/
   set /varName/ ?value?
}
# Read instance variable of object /obj/
# via import

... method ... {
   ::nx::var import /obj/ /varName/
   set /varName/ ?value?
}
/obj/ exists varName
# Test existence of instance variable of
# object obj

/obj/ eval {info exists :/varName/}
::nx::var exists /obj/ /varName/

2.4. Parameters

While XOTcl 1 had very limited forms of parameters, XOTcl 2 and NX provide a generalized and highly orthogonal parameter machinery handling various kinds of value constraints (also called value checkers). Parameters are used to specify,

  • how objects and classes are initialized (we call these parameter types Configure Parameters), and

  • what values can be passed to methods (we call these Method Parameters).

Furthermore, parameters might be positional or non-positional, they might be optional or required, they might have a defined multiplicity, and value-types, they might be introspected, etc. The Next Scripting Framework provide a unified, C-implemented infrastructure to handle both, object and method parameters in the same way with a high degree of orthogonality.

Configuration parameters were specified in XOTcl 1 primarily via the method parameter in a rather limited way, XOTcl 1 only supported non-positional parameters in front of positional ones, supported no value constraints for positional parameters, provided no distinction between optional and required, and did not support multiplicity.

Furthermore, the Next Scripting Framework provides optionally Return Value Checking based on the same mechanism to check whether some methods return always the values as specified.

2.4.1. Parameters for Configuring Objects: Variables and Properties

Configure parameters are used for specifying values for configuring objects when they are created (i.e. how instance variables are initialized, what parameters can be passed in for initialization, what default values are used, etc.). Such configuration parameters are supported in XOTcl primarily via the method parameter, which is used in XOTcl to define multiple parameters via a list of parameter specifications.

Since the term "parameter" is underspecified, NX uses a more differentiated terminology. NX distinguishes between configurable instance variables (also called properties) and non configurable instance variables (called variables), which might have as well e.g. default values. The values of configurable properties can be queried at runtime via cget, and their values can be altered via configure. When the value of a configure parameter is provided or changed, the value checkers from the variable definition are used to ensure, the value is permissible (i.e. it is for example an integer value). The sum of all configurable object parameters are called configure parameters. To define a define a configurable variable, NX uses the method property, for non-configurable variables, the method variable is used.

Optionally, one can define in NX, that a property or a variable should have a public, protected or private accessor. Such an accessor is a method with the same name as the variable. In XOTcl, every parameter defined as well automatically a same-named accessor method, leading to potential name conflicts with other method names.

In the examples below we show the definition of configurable an non-configurable instance variables using variable and property respectively.

XOTcl Next Scripting Language
# Define class "Foo" with instance
# variables "x" and "y" initialized
# on instance creation. The initialization
# has to be performed in the constructor.

Class Foo
Foo instproc init args {
   instvar x y
   set x 1
   set y 2
}

# Create instance of the class Foo
Foo f1

# Object f1 has instance variables
# x == 1 and y == 2
# Define class "Foo" with instance variables
# "x" and "y" initialized on instance creation.
# The method "variable" is similar in syntax
# to Tcl's "variable" command. During
# instance creation, the variable
# definitions are used for the
# initialization of the variables of the object.

Class create Foo {
  :variable x 1
  :variable y 2
}

# Create instance of the class Foo
Foo create f1

# Object f1 has instance variables
# x == 1 and y == 2

While XOTcl follows a procedural way to initialize variables via the constructor init, NX follows a more declarative approach. Often, classes have superclasses, which often want to provide their own instance variables and default values. The declarative approach from NX solves this via inheritance, while an procedural approach via assign statements in the constructor requires explicit constructor calls, which are often error-prone. Certainly, when a user prefers to assign initial values to instance variables via explicit assign operations in constructors, this is as ell possible in NX.

NX uses the same mechanism to define class variables or object variables.

XOTcl Next Scripting Language
# No syntactic support for creating
# class variables
# Define a object variable "V" with value 100 and
# an instance variable "x". "V" is defined for the
# class object Foo, "x" is defined in the
# instances of the class. "object variable" works
# similar to "object method".

Class create Foo {
  :object variable V 100
  :variable x 1
}

In the next step, we define configurable instance variables which we call properties in NX.

XOTcl uses the method parameter is a shortcut for creating multiple configurable variables with automically created accessors (methods for reading and writing of the variables). In NX, the prefered way to create configurable variables is to use the method property. The method property in NX is similar to variable, but makes the variables configurable, which means that

  1. one can specify the property as a non-positional parameter upon creation of the object,

  2. one can query the value via the method cget, and

  3. one can modify the value of the underlying variable via the method configure.

XOTcl Next Scripting Language
# Parameters specified as a list
# (short form); parameter
# "a" has no default, "b" has default "1"

Class Foo -parameter {a {b 1}}

# Create instance of the class Foo
Foo f1 -a 0

# Object f1 has instance variables
# a == 0 and b == 1

# XOTcl registers automatically accessors
# for the parameters. Use the accessor
# "b" to output the value of variable "b"
puts [f1 b]

# Use the setter to alter value of
# instance variable "b"
f1 b 100
# Define property "a" and "b". The
# property "a" has no default, "b" has
# default value "1"

Class create Foo {
  :property a
  :property {b 1}
}

# Create instance of the class Foo
Foo create f1 -a 0

# Object f1 has instance variables
# a == 0 and b == 1

# Use the method "cget" to query the value
# of a configuration parameter
puts [f1 cget -b]

# Use the method "configure" to alter the
# value of instance variable "b"
f1 configure -b 100

In general, NX allows to create variables and properties with and without accessor methods. The created accessor methods might be public, protected or public. When the value none is provided to -accessor, no accessor will be created. This is actually the default in NX. In order to change the default behavior in NX, one can use ::nx::configure defaultAccessor none|public|protected|private.

XOTcl Next Scripting Language
# "parameter" creates always accessor
# methods, accessor methods are
# always public, no "cget" is available.

Class create Foo -parameter {a {b1}}

# Use the accessor method to query
# the value of a configuration parameter
puts [f1 b]

# Use the accessor method to set the
# value of instance variable "a"
f1 a 100

# Use the accessor method to unset the
# value of instance variable "a" n.a. via
# accessor
# Define property "a" and "b". The
# property "a" has no default, "b" has
# default value "1"

Class create Foo {
  :variable -accessor public a
  :property -accessor public {b 1}
}

# Use the accessor method to query
# the value of a configuration parameter
puts [f1 b get]

# Use the accessor method to set the
# value of instance variable "a"
f1 a set 100

# Use the accessor method to unset the
# value of instance variable "a"
f1 a unset

Similar to variable, properties can be defined in NX on the class and on the object level.

XOTcl Next Scripting Language
# XOTcl provides no means to define
# configurable variables at the object
# level
# Define class with a property for the class object
# named "cp". This is similar to "static variables"
# in some other object-oriented programming
# languages.

Class create Foo {
  ...
  :object property cp 101
}

# Define object property "op"

Object create o {
  :object property op 102
}

NX supports value constraints (value-checkers) for object and method parameters in an orthogonal manner. NX provides a predefined set of value checkers, which can be extended by the application developer. In NX, the value checking is optional. This means that it is possible to develop e.g. which a large amount of value-checking and deploy the script with value checking turned off, if the script is highly performance sensitive.

XOTcl Next Scripting Language
# No value constraints for
# parameter available
# Predefined value constraints:
#    object, class, alnum, alpha, ascii, boolean,
#    control, digit, double, false, graph, integer,
#    lower, parameter, print, punct, space, true,
#    upper, wordchar, xdigit
#
# User defined value constraints are possible.
# All parameter value checkers can be turned on
# and off at runtime.
#
# Define a required boolean property "a"
# and an integer property "b" with a default.
# The first definition uses "properties",
# the second definition uses multiple
# "property" statements.

Class create Foo -properties {
   a:boolean
   {b:integer 1}
}
Class create Foo {
   :property a:boolean
   :property {b:integer 1}
}

In XOTcl all configure parameters were optional. Required parameters have to be passed to the constructor of the object.

NX allows to define optional and required configure parameters (as well as method parameters). Therefore, configure parameters can be used as the single mechanism to parameterize objects. It is in NX not necessary (and per default not possible) to pass arguments to the constructor.

XOTcl Next Scripting Language
# Required parameter not available
# Required parameter:
# Define a required property "a" and a
# required boolean property "b"

Class create Foo -properties {
   a:required
   b:boolean,required
}
Class create Foo {
   :property a:required
   :property b:boolean,required
}

NX supports in contrary to XOTcl to define the multiplicity of values per parameter. In NX, one can specify that a parameter can accept the value "" (empty) in addition to e.g. an integer, or one can specify that the value is an empty or non-empty ist of values via the multiplicity. For every specified value, the value checkers are applied.

XOTcl Next Scripting Language
# Multiplicity for parameter
# not available
# Parameter with multiplicity
#   ints is a list of integers, with default
#   objs is a non-empty list of objects
#   obj is a single object, maybe empty

Class create Foo -properties {
  {ints:integer,0..n ""}
   objs:object,1..n
   obj:object,0..1
}
Class create Foo {
  :property {ints:integer,0..n ""}
  :property objs:object,1..n
  :property obj:object,0..1
}

For the implementation of variables and properties, NX uses slot objects, which are an extension to the -slots already available in XOTcl. While very for every property in NX, a slot object is created, for performance reasons, not every variable has a slot associated.

When an property is created, NX does actually three things:

  1. Create a slot object, which can be specified in more detail using the init-block of the slot object

  2. Create a parameter definition for the initialization of the object (usable via a non-positional parameter during object creation), and

  3. register optionally an accessor function (setter), for which the usual protection levels (public, protected or private) can be used.

XOTcl Next Scripting Language
# Define parameters via slots

Class Foo -slots {
   Attribute a
   Attribute b -default 1
}

# Create instance of the class Foo
# and provide a value for instance
# variable "a"
Foo f1 -a 0

# Object f1 has a == 0 and b == 1
# Configurable parameters specified via the
# method "property" (supports method
# modifiers and scripted configuration;
# see below)

Class create Foo {
   :property a
   :property {b 1}
}

# Create instance of the class Foo and
# provide a value for instance variable "a"
Foo create f1 -a 0

# Object f1 has a == 0 and b == 1

Since the slots are objects, the slot objects can be configured and parameterized like every other object in NX. Slot objects can be provided with a scripted initialization as well. We show first the definition of properties simliar to the functionality provided as well by XOTcl and show afterwards how to use value constraints, optional parameters, etc. in NX.

XOTcl Next Scripting Language
# Define parameter with an an
# attribute-specific type checker

Class Person -slots {
  Attribute create sex -type "sex" {
    my proc type=sex {name value} {
      switch -glob $value {
        m* {return m}
        f* {return f}
        default {
          error "expected sex but got $value"
        }
      }
    }
  }
}
# Configure parameter with scripted
# definition (init-block), defining a
# property specific type checker

Class create Person {
    :property -accessor public sex:sex,convert {

      # define a converter to standardize representation
      :object method type=sex {name value} {
        switch -glob $value {
          m* {return m}
          f* {return f}
          default {error "expected sex but got $value"}
        }
      }

    }
}

The parameters provided by a class for the initialization of instances can be introspected via querying the parameters of the method create: /cls/ info lookup parameters create (see [info_configure_parameter]).

2.4.2. Delete Variable Handlers

XOTcl Next Scripting Language
# No syntactic support for deleting
# variable handlers
# Like deletion of Methods:
# Delete on the object, where the
# variable handler is defined.

/cls/ delete property /name/
/obj/ delete object property /name/

/cls/ delete variable /name/
/obj/ delete object variable /name/

2.4.3. Method Parameters

Method parameters are used to specify the interface of a single method (what kind of values may be passed to a method, what default values are provided etc.). The method parameters specifications in XOTcl 1 were limited and allowed only value constraints for non positional arguments.

NX and XOTcl 2 provide value constraints for all kind of method parameters. While XOTcl 1 required non-positional arguments to be listed in front of positional arguments, this limitation is lifted in XOTcl 2.

XOTcl Next Scripting Language
# Define method foo with non-positional
# parameters (x, y and y) and positional
# parameter (a and b)

Class C
C instproc foo {
   -x:integer
   -y:required
   -z
   a
   b
} {
   # ...
}
C create c1

# invoke method foo
c1 foo -x 1 -y a 2 3
# Define method foo with
# non-positional parameters
# (x, y and y) and positional
# parameter (a and b)

Class create C {
   :public method foo {
      -x:integer
      -y:required
      -z
      a
      b
   } {
      # ...
   }
   :create c1
}
# invoke method foo
c1 foo -x 1 -y a 2 3
# Only leading non-positional
# parameters are available; no
# optional positional parameters,
# no value constraints on
# positional parameters,
# no multiplicity, ...
# Define various forms of parameters
# not available in XOTcl 1

Class create C {
  # trailing (or interleaved) non-positional
  # parameters
  :public method m1 {a b -x:integer -y} {
    # ...
  }

  # positional parameters with value constraints
  :public method m2 {a:integer b:boolean} {
    #...
  }

  # optional positional parameter (trailing)
  :public method set {varName value:optional} {
    # ....
  }

  # parameter with multiplicity
  :public method m3 {-objs:object,1..n c:class,0..1} {
    # ...
  }

  # In general, the same list of value
  # constraints as for configure parameter is
  # available (see above).
  #
  # User defined value constraints are
  # possible. All parameter value checkers
  # can be turned on and off.
}

2.4.4. Return Value Checking

Return value checking is a functionality available in the Next Scripting Framework, that was not yet available in XOTcl 1. A return value checker assures that a method returns always a value satisfying some value constraints. Return value checkers can be defined on all forms of methods (scripted or C-implemented). Like for other value checkers, return value checkers can be turned on and off.

XOTcl Next Scripting Language
# No return value checking
# available
# Define method foo with non-positional
# parameters (x, y and y) and positional
# parameter (a and b)

Class create C {

  # Define method foo which returns an
  # integer value
  :method foo -returns integer {-x:integer} {
    # ...
   }

  # Define an alias for the Tcl command ::incr
  # and assure, it always returns an integer
  # value
  :alias incr -returns integer ::incr

  # Define a forwarder that has to return an
  # integer value
  :forward ++ -returns integer ::expr 1 +

 # Define a method that has to return a
 # non-empty list of objects
 :public object method instances {} \
    -returns object,1..n {
   return [:info instances]
  }
}

2.5. Interceptors

XOTcl and NX allow the definition of the same set of interceptors, namely class- and object-level mixins and class- and object-level filters. The primary difference in NX is the naming, since NX abandons the prefix "inst" from the names of instance specific method, but uses the the modifier objec" for object specific methods.

Therefore, in NX, if a mixin is registered on a class-level, it is applicable for the instances (a per-class mixin), and if and object mixin is registered, it is a per-object mixin. In both cases, the term mixin is used, in the second case with the modifier object. As in all other cases, one can register the same way a per-object mixin on a plain object or on a class object.

2.5.1. Register Mixin Classes and Mixin Guards

XOTcl Next Scripting Language
/cls/ instmixin ...
/cls/ instmixinguard /mixin/ ?condition?

# Query per-class mixin
/cls/ instmixin
# Register/clear per-class mixin and guard for
# a class

/cls/ mixins add|set|clear ...
/cls/ mixins guard /mixin/ ?condition?
/cls/ configure -mixin ...

# Query per-class mixins
/cls/ mixins get
/cls/ cget -mixins

# Query per-class mixins (without guards)
/cls/ mixins classes
/obj/ mixin ...
/obj/ mixinguard /mixin/ ?condition?

# Query per-object mixins
/obj/ mixin
# Register/clear per-object mixin and guard for
# an object

/obj/ object mixins add|set|clear ...
/obj/ object mixins guard /mixin/ ?condition?
/obj/ configure -object-mixins ...

# Query per-object mixin
/obj/ object mixins get
/obj/ cget -object-mixin

# Query per-object mixins (without guards)
/cls/ mixins classes

2.5.2. Register Filters and Filter Guards

XOTcl Next Scripting Language
# Register per-class filter and guard for
# a class
/cls/ instfilter ...
/cls/ instfilterguard /filter/ ?condition?

# Query per-class filter
/cls/ instfilter
# Register/clear per-class filter and guard for
# a class

/cls/ filters add|set|clear ...
/cls/ filters guard /filter/ ?condition?
/cls/ configure -filters ...

# Query per-class filters
/cls/ filters get
/cls/ cget -filters

# Query per-class filters (without guards)
/cls/ filters methods
/obj/ filter ...
/obj/ filterguard /filter/ ?condition?
# Register(clear per-object filter and guard for
# an object

/obj/ object filters add|set|clear ...
/obj/ object filters guard /filter/ ?condition?
/obj/ configure -object-filters ...

# Query per-object filters
/cls/ object filters get
/obj/ cget -object-filters

# Query per-object filters (without guards)
/cls/ object filters methods

2.6. Introspection

In general, introspection in NX became more orthogonal and less dependent on the type of the method. In XOTcl it was e.g. necessary that a developer had to know, whether a method is e.g. scripted or not and has to use accordingly different sub-methods of info.

In NX, one can use e.g. always info method with a subcommand and the framework tries to hide the differences as far as possible. So, one can for example obtain with info method parameter the parameters of scripted and C-implemented methods the same way, one one can get the definition of all methods via info method definition and one can get an manual-like interface description via info method syntax. In addition, NX provides means to query the type of a method, and NX allows to filter by the type of the method.

2.6.1. List sub- and superclass relations

While XOTcl used singular words for introspecting sub- and superclass relations, NX uses plural word to indicate that potentially a list of values is returned.

XOTcl Next Scripting Language
/cls/ info superclass ?pattern?
/cls/ info superclasses ?pattern?
/cls/ info subclass ?pattern?
/cls/ info subclasses -type setter ?pattern?

2.6.2. List methods defined by classes

While XOTcl uses different names for obtaining different kinds of methods defined by a class, NX uses info methods in an orthogonal manner. NX allows as well to use the call protection to filter the returned methods.

XOTcl Next Scripting Language
/cls/ info instcommands ?pattern?
/cls/ info methods ?pattern?
/cls/ info instparametercmd ?pattern?
/cls/ info methods -type setter ?pattern?
/cls/ info instprocs ?pattern?
/cls/ info methods -type scripted ?pattern?
# n.a.
/cls/ info methods -type alias ?pattern?
/cls/ info methods -type forwarder ?pattern?
/cls/ info methods -type object ?pattern?
/cls/ info methods -callprotection public|protected ...

2.6.3. List methods defined by objects

While XOTcl uses different names for obtaining different kinds of methods defined by an object, NX uses info methods in an orthogonal manner. NX allows as well to use the call protection to filter the returned methods.

XOTcl Next Scripting Language
/obj/ info commands ?pattern?
/obj/ info object methods ?pattern?
/obj/ info parametercmd ?pattern?
/obj/ info object methods -type setter ?pattern?
/obj/ info procs ?pattern?
/obj/ info object methods -type scripted ?pattern?
# n.a.
/obj/ info object methods -type alias ?pattern?
/obj/ info object methods -type forwarder ?pattern?
/obj/ info object methods -type object ?pattern?
/obj/ info object methods -callprotection public|protected ...

2.6.4. Check existence of a method

NX provides multiple ways of checking, whether a method exists; one can use info method exists to check, if a given method exists (return boolean), or one can use info methods ?pattern?, where pattern might be a single method name without wild-card characters. The method info methods ?pattern? returns a list of matching names, which might be empty. These different methods appear appropriate depending on the context.

XOTcl Next Scripting Language
/obj|cls/ info \
   [inst](commands|procs|parametercmd) \
   ?pattern?
/cls/ info method exists /methodName/
/cls/ info methods /methodName/
/obj/ info object method exists /methodName/
/obj/ info object methods /methodName/

2.6.5. List callable methods

In order to obtain for an object the set of artefacts defined in the class hierarchy, NX uses info lookup. One can either lookup methods (via info lookup methods) or slots (via info lookup slots). The plural term refers to a potential set of return values.

XOTcl Next Scripting Language
/obj/ info methods ?pattern?
/obj/ info lookup methods ... ?pattern?
# Returns list of method names
# n.a.
# List only application specific methods
/obj/ info lookup methods -source application ... ?pattern?
# Returns list of method names
# Options for 'info methods'
#
# -incontext
# -nomixins
# Options for 'info lookup methods'
#
# -source ...
# -callprotection ...
# -incontext
# -type ...
# -nomixins
# n.a.
# List slot objects defined for obj
# -source might be all|application|baseclasses
# -type is the class of the slot object

/obj/ info lookup slots ?-type ...? ?-source ...? ?pattern?

# Returns list of slot objects
# List registered filters
/obj/ info filters -order ?-guards? ?pattern?

# List registered mixins
/obj/ info mixins -heritage ?-guards? ?pattern?
# List registered filters
/obj/ info lookup filters ?-guards? ?pattern?

# List registered mixins
/obj/ info lookup mixins ?-guards? ?pattern?

2.6.6. List object/class where a specified method is defined

info lookup can be used as well to determine, where exactly an artefact is located. One can obtain this way a method handle, where a method or filter is defined.

The concept of a method-handle is new in NX. The method-handle can be used to obtain more information about the method, such as e.g. the definition of the method.

XOTcl Next Scripting Language
/obj/ procsearch /methodName/
/obj/ info lookup method /methodName/
# Returns method-handle
/obj/ filtersearch /methodName/
/obj/ info lookup filter /methodName/
# Returns method-handle

2.6.7. List definition of scripted methods

XOTcl contains a long list of info subcommands for different kinds of methods and for obtaining more detailed information about these methods.

In NX, this list of info subcommands is much shorter and more orthogonal. For example info method definition can be used to obtain with a single command the full definition of a scripted method, and furthermore, it works as well the same way to obtain e.g. the definition of a forwarder or an alias.

While XOTcl uses different names for info options for objects and classes (using the prefix "inst" for instance specific method), NX uses for object specific method the modifier object. For definition of class object specific methods, use the modifier object as usual.

XOTcl Next Scripting Language
# n.a.
/cls/ info method definition /methodName/
/obj/ info object method definition /methodName/
/cls/ info instbody /methodName/
/obj/ info body /methodName/
/cls/ info method body /methodName/
/obj/ info object method body /methodName/
/cls/ info instargs /methodName/
/obj/ info args /methodName/
/cls/ info method args /methodName/
/obj/ info object method args /methodName/
/cls/ info instnonposargs /methodName/
/obj/ info object method args /methodName/
/cls/ info method parameter /methodName/
/obj/ info object method parameter /methodName/
/cls/ info instdefault /methodName/
/obj/ info default /methodName/
# not needed, part of
# "info ?object? method parameter"
/cls/ info instpre /methodName/
/obj/ info pre /methodName/
/cls/ info method precondition /methodName/
/obj/ info object method precondition /methodName/
/cls/ info instpost /methodName/
/obj/ info post /methodName/
/cls/ info method postcondition /methodName/
/obj/ info object method postcondition /methodName/

Another powerful introspection option in NX is info ?object? method syntax which obtains a representation of the parameters of a method in the style of Tcl man pages (regardless of the kind of method).

XOTcl Next Scripting Language
# n.a.
/cls/ info method syntax /methodName/
/obj/ info object method syntax /methodName/

2.6.8. List Configure Parameters

The way, how newly created objects can be configured is determined in NX via properties. The configuration happens during creation via the methods create or new or during runtime via configure. These methods have therefore virtual argument lists, depending on the object or class on which they are applied.

XOTcl Next Scripting Language
# n.a.
# Return the parameters applicable to
# the create method of a certain class.
# class can be configured. A pattern can
# be used to filter the results.

/cls/ info lookup parameters create ?/pattern/?

# Return in the result in documentation syntax

/cls/ info lookup syntax create ?/pattern/?

# "info lookup parameters configure" returns
# parameters available for configuring the
# current object  (might contain object
# specific information)

/obj/ info lookup parameters configure ?pattern?

# "info lookup configure syntax" returns syntax of
# a call to configure in the Tcl parameter syntax

/obj/ info lookup syntax configure

# Obtain information from a parameter
# (as e.g. returned from "info lookup
# parameters configure").

nsf::parameter::info name /parameter/
nsf::parameter::info syntax /parameter/
nsf::parameter::info type /parameter/

2.6.9. List Variable Declarations (property and variable)

XOTcl Next Scripting Language
# obtain parameter definitions defined
# for a class
/cls/ info parameter
# "info variables" returns handles of
# properties and variables defined by this
# class or object

/cls/ info variables ?pattern?
/obj/ info object variables ?pattern?

# "info lookup variables" returns handles
# of variables and properties applicable
# for the current object (might contain
# object specific information)

/obj/ info lookup variables /pattern/

# "info variable" lists details about a
# single property or variable.

/obj/ info variable definition /handle/
/obj/ info variable name /handle/
/obj/ info variable parameter /handle/

2.6.10. List Slots

XOTcl Next Scripting Language
# n.a.
# Return list of slots objects defined on the
# object or class
#
# -source might be all|application|baseclasses
# -type is the class of the slot object
# -closure includes slots of superclasses

/cls/ info slots \
   ?-type value? ?-closure? ?-source value? ?pattern?
/obj/ info object slots ?-type ...? ?pattern?

# List reachable slot objects defined for obj
# -source might be all|application|baseclasses
# -type is the class of the slot object
# Returns list of slot objects.

/obj/ info lookup slots \
   ?-type ...? ?-source ... ?pattern?

# Obtain definition, name or parameter from
# slot object

/slotobj/ definition
/slotobj/ name
/slotobj/ parameter

2.6.11. List Filter or Mixins

In NX all introspection options for filters are provided via info filters and all introspection options for mixins are provided via info mixins.

XOTcl Next Scripting Language
/obj/ info filter ?-guards? ?-order? ?pattern?
/obj/ info filterguard /name/
/obj/ info object filters \
   ?-guards? ?pattern?
/cls/ info instfilter \
   ?-guards? ?-order? ?pattern?
/cls/ info instfilterguard /name/
/cls/ info filters \
   ?-guards? ?pattern?
/obj/ info mixin ?-guards? ?-order ?pattern?
/obj/ info mixinguard /name/
/obj/ info object mixins \
   ?-guards? ?pattern?
/cls/ info instmixin \
   ?-guards? ?-order? ?pattern?
/cls/ info instmixinguard /name/
/cls/ info mixins \
   ?-closure? ?-guards? ?-heritage? ?pattern?

2.6.12. List definition of methods defined by aliases, setters or forwarders

As mentioned earlier, info method definition can be used on every kind of method. The same call can be used to obtain the definition of a scripted method, a method-alias, a forwarder or a setter method.

XOTcl Next Scripting Language
# n.a.
/cls/ info method definition /methodName/
/obj/ info object method definition /methodName/

2.6.13. List Method-Handles

NX supports method-handles to provide means to obtain further information about a method or to change maybe some properties of a method. When a method is created, the method creating method returns the method handle to the created method.

XOTcl Next Scripting Language
# n.a.
#
# List the method handle of the specified method,
# can be used e.g. for aliases. "handle" is the short
# form of "definitionhandle".
#
/cls/ info method handle /methodName/
/obj/ info object method handle /methodName/
#
# For ensemble methods (method name contains
# spaces) one can query as well the registration
# handle, which is the handle to the root of the
# ensemble; the definiton handle points to the
# leaf of the ensemble.
#
/cls/ info method registrationhandle /methodName/
/obj/ info object method registrationhandle /methodName/
#
# For aliases, one can query the original
# definition via "info method origin"
#
/cls/ info method origin /methodName/
/obj/ info object method origin /methodName/

2.6.14. List type of a method

The method info ?object? method type is new in NX to obtain the type of the specified method.

XOTcl Next Scripting Language
# n.a.
/cls/ info method type /methodName/
/obj/ info object method type /methodName/

2.6.15. List the scope of mixin classes

NX provides a richer set of introspection options to obtain information, where mixins classes are mixed into.

XOTcl Next Scripting Language
/cls/ info mixinof ?-closure? ?pattern?
# List objects, where /cls/ is a
# per-object mixin

/cls/ info mixinof -scope object ?-closure? \
   ?pattern?
/cls/ info instmixinof ?-closure? ?pattern?
# List classes, where /cls/ is a per-class mixin

/cls/ info mixinof -scope class ?-closure? \
   ?pattern?
# n.a.
# List objects and classes, where /cls/ is
# either a per-object or a per-class mixin

/cls/ info mixinof -scope all ?-closure? \
   ?pattern?
/cls/ info mixinof ?-closure? ?pattern?

2.6.16. Check properties of object and classes

Similar as noted before, NX uses rather a hierarchical approach of naming using multiple layers of subcommands).

XOTcl Next Scripting Language
/obj/ istype /sometype/
# Check if object is a subtype of some class
/obj/ info has type /sometype/
/obj/ ismixin /cls/
# Check if object has the specified mixin registered
/obj/ info has mixin /cls/
/obj/ isclass ?/cls/?
cd # Check if object is an NX class
/obj/ has type ::nx::Class

# Check if object is a class in one of the
# NSF object systems
::nsf::is class /obj/
/obj/ ismetaclass /cls/
# Check if class is an NX metaclass
expr {[/cls/ info heritage ::nx::Class] ne ""}

# Check if object is a metaclass in one of the
# NSF object systems
::nsf::is metaclass /obj/
# n.a.
# Check if object is a baseclass of an object system
::nsf::is baseclass /obj/
# n.a.
# Return name of object (without namespace prefix)
/obj/ info name
/obj/ object::exists /obj/
# Check for existence of object (nsf primitive)
::nsf::object::exists /obj/

2.6.17. Call-stack Introspection

Call-stack introspection is very similar in NX and XOTcl. NX uses for subcommand the term current instead of self, since self has a strong connotation to the current object. The term proc is renamed by method.

XOTcl Next Scripting Language
self
self
current object
self class
current class
self args
current args
self proc
current method
self callingclass
current calledclass
self callingobject
current callingobject
self callingproc
current callingmethod
self calledclass
current calledclass
self calledproc
current calledmethod
self isnextcall
current isnextcall
self next
# Returns method-handle of the
# method to be called via "next"
current next
self filterreg
# Returns method-handle of the
# filter method
current filterreg
self callinglevel
current callinglevel
self activelevel
current activelevel

2.7. Other Predefined Methods

XOTcl Next Scripting Language
/obj/ requireNamespace
/obj/ require namespace
# n.a.
/obj/ require method

2.8. Dispatch, Aliases, etc.

todo: to be done or omitted

2.9. Assertions

In contrary to XOTcl, NX provides no pre-registered methods for assertion handling. All assertion handling can e performed via the Next Scripting primitive nsf::method::assertion.

XOTcl Next Scripting Language
/obj/ check /checkoptions/
::nsf::method::assertion /obj/ check /checkoptions/
/obj/ info check
::nsf::method::assertion /obj/ check
/obj/ invar /conditions/
::nsf::method::assertion /obj/ object-invar /conditions/
/obj/ info invar
::nsf::method::assertion /obj/ object-invar
/cls/ instinvar /conditions/
::nsf::method::assertion /cls/ class-invar /conditions/
/cls/ info instinvar
::nsf::method::assertion /cls/ class-invar
/cls/ invar /conditions/
::nsf::method::assertion /cls/ object-invar /conditions/
/cls/ info invar
::nsf::method::assertion /cls/ object-invar

2.10. Method Protection

As described above, NX supports method protection via the method modifiers protected and public. A protected method can be only called from an object of that class, while public methods can be called from every object. The method protection can be used to every kind of method, such as e.g. scripted methods, aliases, forwarders, or accessors. For invocations, the most specific definition (might be a mixin) is used for determining the protection.

3. Incompatibilities between XOTcl 1 and XOTcl 2

3.1. Resolvers

The resolvers (variable resolvers, function resolvers) of the Next Scripting Framework are used as well within XOTcl 2. When variable names or method names starting with a single colon are used in XOTcl 1 scripts, conflicts will arise with the resolver. These names must be replaced.

3.2. Parameters

The following changes for parameters could be regarded as bug-fixes.

3.2.1. Parameter usage without a value

In XOTcl 1, it was possible to call a parameter method during object creation via the dash-interface without a value (in the example below -x).

# XOTcl example

Class Foo -parameter {x y}
Foo f1 -x -y 1

Such cases are most likely mistakes. All parameter configurations in XOTcl 2 require an argument.

3.2.2. Ignored Parameter definitions

In XOTcl 1, a more specific parameter definition without a default was ignored when a more general parameter definition with a default was present. In the example below, the object b1 contained in XOTcl 1 incorrectly the parameter x (set via default from Foo), while in XOTcl 2, the variable won’t be set.

# XOTcl example

Class Foo -parameter {{x 1}}
Class Bar -superclass Foo -parameter x
Bar b1

3.2.3. Changing classes and superclasses

NX does not define the methods class and superclass (like XOTcl), but allows to alter all object/class relations (including class/superclass/object-mixin/…) nsf::relation::set. The class and superclass can be certainly queried in all variants with info class or info superclasses.

# NX example

nx::Class create Foo
Foo create f1

# now alter the class of object f1
nsf::relation::set f1 class ::nx::Object

3.2.4. Overwriting procs/methods with objects and vice versa

NSF is now more conservative on object/method creation. In contrary to XOTcl 1 NSF does not allow to redefined a pre-existing command (e.g. "set") with an object and vice versa. Like in XOTcl 1, preexisting objects and classes con be redefined (necessary for reloading objects/classes in an running interpreter).

3.2.5. Info heritage

info heritage returns in XOTcl 1 the transitive superclass hierarchy, which is equivalent with info superclasses -closure and therefore not necessary. In XOTcl 2 (and NX), info heritage includes as well the transitive per-class mixins.

3.3. Slots

All slot objects (also XOTcl slot objects) are now next-scripting objects of baseclass ::nx::Slot. The name of the experimental default-setter initcmd was changed to defaultcmd. Code directly working on the slots objects has to be adapted.

3.4. Obsolete Commands

Parameter-classes were rarely used and have been replaced by the more general object parameterization. Therefore, cl info parameterclass has been removed.

3.5. Stronger Checking

The Next Scripting Framework performs stronger checking than XOTcl 1 For example, the requiredness of slots in XOTcl 1 was just a comment, while XOTcl 2 enforces it.

3.6. Exit Handlers

The exit hander interface changed from a method of ::xotcl::Object into the Tcl command ::nsf::exithandler:

# NX example
::nsf::exithandler set|get|unset ?arg?

doc/next-migration.txt000066400000000000000000002373761242365656200153660ustar00rootroot00000000000000Migration Guide for the Next Scripting Language =============================================== Gustaf Neumann v2.0.0, June 2013: :Author Initials: GN :toc: :toclevels: 3 :icons: :numbered: :website: http://www.xotcl.org/ .Abstract ***************************************************************************** This document describes the differences between the Next Scripting Language Framework and XOTcl 1. In particular, it presents a migration guide from XOTcl 1 to NX, and presents potential incompatibilities beween XOTcl 1 and XOTcl 2. ***************************************************************************** The Next Scripting Language (NX) is a successor of XOTcl 1 and is based on 10 years of experience with XOTcl in projects containing several hundert thousand lines of code. While XOTcl was the first language designed to provide language support for design patterns, the focus of the Next Scripting Framework and NX are on combining this with Language Oriented Programming. In many respects, NX was designed to ease the learning of the language by novices (by using a more mainstream terminology, higher orthogonality of the methods, less predefined methods), to improve maintainability (remove sources of common errors) and to encourage developer to write better structured programs (to provide interfaces) especially for large projects, where many developers are involved. The Next Scripting Language is based on the Next Scripting Framework which was developed based on the notion of language oriented programming. The Next Scripting Frameworks provides C-level support for defining and hosting multiple object systems in a single Tcl interpreter. The whole definition of NX is fully scripted (e.g. defined in +nx.tcl+). The Next Scripting Framework is shipped with three language definitions, containing NX and XOTcl 2. Most of the existing XOTcl 1 programs can be used without modification in the Next Scripting Framework by using XOTcl 2. The Next Scripting Framework requires Tcl 8.5 or newer. Although NX is fully scripted (as well as XOTcl 2), our benchmarks show that scripts based on NX are often 2 or 4 times faster than the counterparts in XOTcl 1. But speed was not the primary focus on the Next Scripting Environment: The goal was primarily to find ways to repackage the power of XOTcl in an easy to learn environment, highly orthogonal environment, which is better suited for large projects, trying to reduce maintenance costs. We expect that many user will find it attractive to upgrade from XOTcl 1 to XOTcl 2, and some other users will upgrade to NX. This document focuses mainly on the differences between XOTcl 1 and NX, but addresses as well potential incompatibilities between XOTcl 1 and XOTcl 2. For an introduction to NX, please consult the NX tutorial. Differences Between XOTcl and NX ------------------------------- The Next Scripting Framework supports _Language Oriented Programming_ by providing means to define potentially multiple object systems with different naming and functionality in a single interpreter. This makes the Next Scripting Framework a powerful instrument for defining multiple languages such as e.g. domain specific languages. This focus differs from XOTcl 1. Technically, the language framework approach means that the languages implemented by the Next Scripting Framework (most prominently XOTcl 2 and NX) are typically fully scripted and can be loaded via the usual Tcl +package require+ mechanism. Some of the new features below are provided by the Next Scripting Framework, some are implemented via the script files for XOTcl 2 and NX. === Features of NX In general, the Next Scripting Language (NX) differs from XOTcl in the following respects: . *Stronger Encapsulation:* The Next Scripting Language favors a _stronger form of encapsulation_ than XOTcl. Calling the own methods or accessing the own instance variables is typographically easier and computationally faster than these operations on other objects. This behavior is achieved via _resolvers_, which make some methods necessary in XOTcl 1 obsolete in NX (especially for importing instance variables). The encapsulation of NX is stronger than in XOTcl but still weak compared to languages like C++; a developer can still access other objects' variables via some idioms, but NX _makes accesses to other objects' variables explicit_. The requiredness to make these accesses explicit should encourage developer to implement well defined interfaces to provide access to instance variables. . *Additional Forms of Method Definition and Reuse:* The Next Scripting Language provides much more orthogonal means to _define, reuse and introspect_ scripted and C-implemented methods. .. It is possible to use NX +alias+ to register methods under arbitrary names for arbitrary objects or classes. .. NX provides means for _method protection_ (method modifiers +public+, +protected+, and +private+). Therefore developers have to define explicitly public interfaces in order to use methods from other objects. .. One can invoke in NX fully qualified methods to invoke methods outside the precedence path. .. One can define in NX _hierachical method names_ (similar to commands and subcommands, called method ensembles) in a convenient way to provide extensible, hierarchical naming of methods. .. One can use in NX the same interface to query (introspect) C-implemented and scripted methods/commands. . *Orthogonal Parameterization:* The Next Scripting Language provides an _orthogonal framework for parametrization_ of methods and objects. .. In NX, the same argument parser is used for * Scripted Methods * C-implemented methods and Tcl commands * Object Parametrization .. While XOTcl 1 provided only value-checkers for non-positional arguments for methods, the Next Scripting Framework provides the same value checkers for positional and non-positional arguments of methods, as well as for positional and non-positional configure parameters (`-parameter` in XOTcl 1). .. While XOTcl 1 supported only non-positional arguments at the begin of the argument list, these can be used now at arbitrary positions. . *Value Checking:* .. The Next Scripting Language supports checking of the _input parmeters_ and the _return values_ of scripted and C-implemented methods and commands. .. NX provides a set of predefined checkers (like e.g. +integer+, +boolean+, +object+, ...) which can be extended by the applications. .. Value Checking can be used for _single_ and _multi-valued_ parameters. One can e.g. define a list of integers with at least one entry by the parameter specification +integer,1..n+. .. Value Checking can be turned on/off globally or on the method/command level. . *Scripted Init Blocks:* The Next Scripting Language provides _scripted init blocks_ for objects and classes (replacement for the dangerous dash "-" mechanism in XOTcl that allows to set variables and invoke methods upon object creation). . *More Conventional Naming for Predefined Methods:* The naming of the methods in the Next Scripting Language is much more in line with the mainstream naming conventions in OO languages. While for example XOTcl uses +proc+ and +instproc+ for object specific and inheritable methods, NX uses simply +method+. . *Profiling Support:* The Next Scripting Language provides now two forms of profiling * Profiling via a DTrace provider (examples are e.g. in the dtrace subdirectory of the source tree) * Significantly improved built-in profiling (results can be processed in Tcl). . *Significantly Improved Test Suite:* The regression test suite of Next Scripting Scripting framework contain now more than 5.000 tests, and order of magnitude more than in XOTcl 1.6 . *Much Smaller Interface:* The Next Scripting Language has a much _smaller interface_ (i.e. provides less predefined methods) than XOTcl (see Table 1), although the expressability was increased in NX. .Comparison of the Number of Predefined Methods in NX and XOTcl [width="50%",frame="topbot",options="header,footer",cols="3,>1,>1"] |====================== ||NX|XOTcl |Methods for Objects |14| 51 |Methods for Classes | 9| 24 |Info-methods for Objects |11| 25 |Info-methods for Classes |11| 24 |Total | 45|124 |====================== This comparison list compares mostly XOTcl 1 with NX, some features are also available in XOTcl 2 (2a, 2c 2d, 3, 4). === NX and XOTcl Scripts Below is a small, introductory example showing an implementation of a class +Stack+ in NX and XOTcl. The purpose of this first example is just a quick overview. We will go into much more detailed comparison in the next sections. NX supports a block syntax, where the methods are defined during the creation of the class. The XOTcl syntax is slightly more redundant, since every definition of a method is a single toplevel command starting with the class name (also NX supports the style used in XOTcl). In NX, all methods are per default protected (XOTcl does not support protection). In NX methods are defined in the definition of the class via +:method+ or +:public method+. In XOTcl methods are defined via the +instproc+ method. Another difference is the notation to refer to instance variables. In NX, instance variable are named with a single colon in the front. In XOTcl, instance variables are imported using +instvar+. [options="header",cols="asciidoc,asciidoc",frame="none"] |====================== |Stack example in NX |Stack example in XOTcl |[source,tcl] -------------------------------------------------- Class create Stack { # # Stack of Things # :variable things "" :public method push {thing} { set :things [linsert ${:things} 0 $thing] return $thing } :public method pop {} { set top [lindex ${:things} 0] set :things [lrange ${:things} 1 end] return $top } } -------------------------------------------------- |[source,tcl] -------------------------------------------------- # # Stack of Things # Class Stack Stack instproc init {} { my instvar things set things "" } Stack instproc push {thing} { my instvar things set things [linsert $things 0 $thing] return $thing } Stack instproc pop {} { my instvar things set top [lindex $things 0] set things [lrange $things 1 end] } -------------------------------------------------- |====================== === Using XOTcl 2.0 and the Next Scripting Language in a Single Interpreter In general, the Next Scripting Framework supports multiple object systems concurrently. Effectively, every object system has different base classes for creating objects and classes. Therefore, these object systems can have different different interfaces and names of built-in methods. Currently, the Next Scripting Framework is packaged with three object systems: - NX - XOTcl 2.0 - TclCool XOTcl 2 is highly compatible with XOTcl 1, the language NX is described below in more details, the language TclCool was introduced in Tip#279 and serves primarily an example of a small OO language. A single Tcl interpreter can host multiple Next Scripting Object Systems at the same time. This fact makes migration from XOTcl to NX easier. The following example script shows to use XOTcl and NX in a single script: .Using Multiple Object Systems in a single Script [source,tcl] -------------------------------------------------- namespace eval mypackage { package require XOTcl 2.0 # Define a class with a public method foo using XOTcl xotcl::Class C1 C1 instproc foo {} {puts "hello world"} package require nx # Define a class with a public method foo using NX nx::Class create C2 { :public method foo {} {puts "hello world"} } } -------------------------------------------------- One could certainly create object or classes from the different object systems via fully qualified names (e.g. using e.g. `::xotcl::Class` or `::nx::Class`), but for migration for systems without explicit namespaces switching between the object systems eases migration. "Switching" between XOTcl and NX effectively means the load some packages (if needed) and to import either the base classes (Object and Class) of XOTcl or NX into the current namespace. XOTcl Idioms in the Next Scripting Language --------------------------------------------- The following sections are intended for reader familiar with XOTcl and show, how certain language Idioms of XOTcl can be expressed in NX. In some cases, multiple possible realizations are listed Defining Objects and Classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When creating objects or classes, one should use the method +create+ explicitly. In XOTcl, a default +unknown+ method handler was provided for classes, which create for every unknown method invocation an object/class with the name of the invoked method. This technique was convenient, but as well dangerous, since typos in method names lead easily to unexpected behavior. This default unknown method handler is not provided in NX (but can certainly be provided as a one-liner in NX by the application). [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- Class ClassName ---------------- |[source,tcl] ---------------- Class create ClassName ---------------- |[source,tcl] ---------------- Object ObjectName ---------------- |[source,tcl] ---------------- Object create ObjectName ---------------- |=========================== Defining Methods ~~~~~~~~~~~~~~~~ In general, both XOTcl and NX support methods on the object level (per-object methods, i.e. methods only applicable to a single object) and on the class level (methods inherited to instances of the classes). While the naming in XOTcl tried to follow closely the Tcl tradition (using the term +proc+ for functions/methods), NX uses the term +method+ for defining scripted methods. XOTcl uses the prefix +inst+ to denote that methods are provided for instances, calling therefore scripted methods for instances +instproc+. This is certainly an unusual term. The approach with the name prefix has the disadvantage, that for every different kind of method, two names have to be provided (eg. +proc+ and +instproc+, +forward+ and +instforward+). NX on the contrary uses the same term for defining instance method or object-specific methods. When the term (e.g. +method+) is used on a class, the method will be an instance method (i.e. applicable to the instances of the class). When the term is used on an object with the modifier +object+, an object-specific method is defined. This way one can define the same way object specific methods on an object as well as on a class. Furthermore, both XOTcl and NX distinguish between scripted methods (section 3.2.1) and C-defined methods (section 3.2.2). Section 3.2.3 introduces method protection, which is only supported by NX. ==== Scripted Methods Defined in the Init-block of a Class/Object or with Separate Calls The following examples show the definition of a class and its methods in the init-block of a class (NX only), and the definition of methods via separate top level calls (XOTcl and NX). [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # Define instance method 'foo' and object # method 'bar' for a Class 'C' with separate # toplevel commands Class C C instproc foo args {...} C proc bar args {...} ---------------- |[source,tcl] ---------------- # Define instance method and object method # in the init-block of a class Class create C { :method foo args {...} :object method bar args {...} } ---------------- [source,tcl] ---------------- # Define instance method and object method # with separate commands Class create C C method foo args {...} C object method bar args {...} ---------------- |[source,tcl] ---------------- # Define object-specific method foo # for an object 'o' with separate commands Object o o set x 1 o proc foo args {...} ---------------- |[source,tcl] ---------------- # Define object method and set # instance variable in the init-block of # an object Object create o { set :x 1 :object method foo args {...} } ---------------- [source,tcl] ---------------- # Define object method and set # instance variable with separate # commands Object create o o eval {set :x 1} o object method foo args {...} ---------------- |=========================== ==== Different Kinds of Methods This section describes various kinds of methods. The different kinds of methods are defined via different method-defining methods, which are summarized in the following table for XOTcl and NX. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # Methods for defining methods: # # proc # instproc # forward # instforward # parametercmd # instparametercmd # # All these methods return empty. ---------------- |[source,tcl] ---------------- # Methods for defining methods: # # alias # forward # method # # All these methods return method-handles. ---------------- |=========================== In addition to scripted methods (previous section) XOTcl supports forwarder (called +forward+ and +instforward+) and accessor functions to variables (called +parametercmd+ and +instparametercmd+). The accessor functions are used normally internally when object-specific parameters are defined (see Section 3.4). In NX forwarders are called +forward+. NX does not provide an public available method to define variable accessors like +parametercmd+ in XOTcl, but use interanlly the Next Scripting Framework primitive +nsf::method::setter+ when appropriate. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- Class C C instforward f1 ... C forward f2 ... Object o o forward f3 ... ---------------- |[source,tcl] ---------------- # Define forwarder Class create C { :forward f1 ... :object forward f2 ... } Object create o { :object forward f3 ... } ---------------- |[source,tcl] ---------------- # Define setter and getter methods in XOTcl. # # XOTcl provides methods for these. Class C C instparametercmd p1 C parametercmd p2 Object o o parametercmd p3 ---------------- |[source,tcl] ---------------- # Define setter and getter methods in NX. # # NX does not provide own methods, but uses # the low level framework commands, since # application developer will only seldomly # need it. Class create C ::nsf::method::setter C p1 ::nsf::method::setter C -per-object p2 Object create o ::nsf::method::setter o p3 ---------------- |====================== NX supports in contrary to XOTcl the method +alias+ which can be used to register arbitrary Tcl commands or methods for an object or class under a provided method name. Aliases can be used to reuse a certain implementation in e.g. different object systems under potentially different names. In some respects aliases are similar to forwarders, but they do not involve forwarding overhead. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # Method "alias" not available ---------------- |[source,tcl] ---------------- # Define method aliases # (to scripted or non-scripted methods) Class create C { :alias a1 ... :object alias a2 ... } Object create o { :object alias a3 ... } ---------------- |=========================== [[method-protect-example]] ==== Method Modifiers and Method Protection NX supports four method modifiers +object+, +public+, +protected+ and +private+. All method modifiers can be written in front of every method defining command. The method modifier +object+ is used to denote object-specific methods (see above). The concept of method protection is new in NX. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # Method modifiers # # "object", # "public", # "protected", and # "private" # # are not available ---------------- |[source,tcl] ---------------- # Method modifiers # # "object", # "public", # "protected" # # are applicable for all kinds of # method defining methods: # # method, forward, alias # # The modifier "private" is available for # # method, forward, alias # Class create C { :/method-definiton-method/ ... :public /method-definiton-method/ ... :protected /method-definiton-method/ ... :private /method-definiton-method/ ... :object /method-definiton-method/ ... :public object /method-definiton-method/ ... :protected object /method-definiton-method/ ... :private object /method-definiton-method/ ... } ---------------- |====================== XOTcl does not provide method protection. In NX, all methods are defined per default as protected. This default can be changed by the application developer in various ways. The command `::nx::configure defaultMethodCallProtection true|false` can be used to set the default call protection for scripted methods, forwarder and aliases. The defaults can be overwritten also on a class level. NX provides means for method hiding via the method modifier +private+. Hidden methods can be invoked only via the +-local+ flag, which means: "call the specified method defined in the same class/object as the currently executing method". [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # XOTcl provides no means for # method hiding ---------------- |[source,tcl] ---------------- # Hiding of methods via "private" # nx::Class create Base { :private method baz {a b} {expr {$a + $b}} :public method foo {a b} {: -local baz $a $b} } nx::Class create Sub -superclass Base { :public method bar {a b} {: -local baz $a $b} :private method baz {a b} {expr {$a * $b}} :create s1 } s1 foo 3 4 ;# returns 7 s1 bar 3 4 ;# returns 12 s1 baz 3 4 ;# unable to dispatch method 'baz' ---------------- |====================== [[method-deletion]] ==== Method Deletion NX provides an explicit +delete+ method for the deletion of methods. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # XOTcl provides only method deletion with # the equivalent of Tcl's "proc foo {} {}" /cls/ instproc foo {} {} /obj/ proc foo {} {} ---------------- |[source,tcl] ---------------- # Deletion of Methods # /cls/ delete method /name/ /obj/ delete object method /name/ ---------------- |====================== === Resolvers The Next Scripting Framework defines Tcl resolvers for method and variable names to implement object specific behavior. Within the bodies of scripted methods these resolver treat variable and function names starting with a colon `:` specially. In short, a colon-prefixed variable name refers to an instance variable, and a colon-prefixed function name refers to a method. The sub-sections below provide detailed examples. Note that the resolvers of the Next Scripting Framework can be used in the XOTcl 2.* environment as well. ==== Invoking Methods In XOTcl, a method of the same object can be invoked via +my+, or in general via using the name of the object in front of the method name. In NX, the own methods are called via the method name prefixed with a single colon. The invocation of the methods of other objects is the same in NX and XOTcl. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- Class C C instproc foo args {...} C instproc bar args { my foo 1 2 3 ;# invoke own method o baz ;# invoke other object's method } Object o o proc baz {} {...} ---------------- |[source,tcl] ---------------- Class create C { :method foo args {...} :method bar args { :foo 1 2 3 ;# invoke own method o baz ;# invoke other object's method } } Object create o { :public object method baz {} {...} } ---------------- |====================== ==== Accessing Own Instance Variables from Method Bodies In general, the Next Scripting Language favors the access to an objects's own instance variables over variable accesses of other objects. This means that in NX it is syntactically easier to access the own instance variables. On the contrary, in XOTcl, the variable access to own and other variables are fully symmetric. In XOTcl, the following approaches are used to access instance variables: - Import instance variables via +instvar+ and access variables via +$varName+ - Set or get instance variables via +my set varName ?value?+ or other variable accessing methods registered on +xotcl::Object+ such as +append+, +lappend+, +incr+, etc. - Register same-named accessor functions and set/get values of instance variables via +my varName ?value?+ In NX, the favored approach to access instance variables is to use the name resolvers, although it is as well possible to import variables via +nx::var import+ or to check for the existence of instance variables via +nx::var exists+. The following examples summary the use cases for accessing the own and other instance variables. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- Class C C instproc foo args { # Method scoped variable a set a 1 # Instance variable b my instvar b set b 2 # Global variable/namespaced variable c set ::c 3 } ---------------- |[source,tcl] ---------------- Class create C { :method foo args {...} # Method scoped variable a set a 1 # Instance variable b set :b 2 # Global variable/namespaced variable c set ::c 3 } } ---------------- |[source,tcl] ---------------- ... instproc ... { my set /varName/ ?value? } ---------------- |[source,tcl] ---------------- # Set own instance variable to a value via # resolver (preferred and fastest way) ... method ... { set :/newVar/ ?value? } ---------------- |[source,tcl] ---------------- ... instproc ... { my instvar /varName/ set /varName/ ?value? } ---------------- |[source,tcl] ---------------- # Set own instance variable via # variable import ... method ... { ::nx::var import [self] /varName/ set /varName/ ?value? } ---------------- |[source,tcl] ---------------- ... instproc ... { set /varName/ [my set /otherVar/] } ---------------- |[source,tcl] ---------------- # Read own instance variable ... method ... { set /varName/ [set :/otherVar/] } ---------------- [source,tcl] ---------------- ... method ... { set /newVar/ ${:/otherVar/} } ---------------- |[source,tcl] ---------------- ... instproc ... { my exists /varName/ } ---------------- |[source,tcl] ---------------- # Test existence of own instance variable ... method ... { info :/varName/ } ---------------- [source,tcl] ---------------- ... method ... { ::nx::var exists [self] /varName/ } ---------------- |====================== ==== Accessing Instance Variables of other Objects [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /obj/ set /varName/ ?value? ---------------- |[source,tcl] ---------------- # Set instance variable of object obj to a # value via resolver # (preferred way: define property on obj) /obj/ eval [list set :/varName/ ?value?] ---------------- |[source,tcl] ---------------- set /varName/ [/obj/ set /otherVar/] ---------------- |[source,tcl] ---------------- # Read instance variable of object obj # via resolver set /varName/ [/obj/ eval {set :/otherVar/}] ---------------- |[source,tcl] ---------------- ... instproc ... { /obj/ instvar /varName/ set /varName/ ?value? } ---------------- |[source,tcl] ---------------- # Read instance variable of object /obj/ # via import ... method ... { ::nx::var import /obj/ /varName/ set /varName/ ?value? } ---------------- |[source,tcl] ---------------- /obj/ exists varName ---------------- |[source,tcl] ---------------- # Test existence of instance variable of # object obj /obj/ eval {info exists :/varName/} ---------------- [source,tcl] ---------------- ::nx::var exists /obj/ /varName/ ---------------- |====================== === Parameters While XOTcl 1 had very limited forms of parameters, XOTcl 2 and NX provide a generalized and highly orthogonal parameter machinery handling various kinds of value constraints (also called value checkers). Parameters are used to specify, - how objects and classes are initialized (we call these parameter types _Configure Parameters_), and - what values can be passed to methods (we call these _Method Parameters_). Furthermore, parameters might be positional or non-positional, they might be optional or required, they might have a defined multiplicity, and value-types, they might be introspected, etc. The Next Scripting Framework provide a unified, C-implemented infrastructure to handle both, object and method parameters in the same way with a high degree of orthogonality. Configuration parameters were specified in XOTcl 1 primarily via the method +parameter+ in a rather limited way, XOTcl 1 only supported non-positional parameters in front of positional ones, supported no value constraints for positional parameters, provided no distinction between optional and required, and did not support multiplicity. Furthermore, the Next Scripting Framework provides optionally _Return Value Checking_ based on the same mechanism to check whether some methods return always the values as specified. ==== Parameters for Configuring Objects: Variables and Properties Configure parameters are used for specifying values for configuring objects when they are created (i.e. how instance variables are initialized, what parameters can be passed in for initialization, what default values are used, etc.). Such configuration parameters are supported in XOTcl primarily via the method +parameter+, which is used in XOTcl to define multiple parameters via a list of parameter specifications. Since the term "parameter" is underspecified, NX uses a more differentiated terminology. NX distinguishes between configurable instance variables (also called _properties_) and non configurable instance variables (called _variables_), which might have as well e.g. default values. The values of configurable properties can be queried at runtime via +cget+, and their values can be altered via +configure+. When the value of a configure parameter is provided or changed, the value checkers from the variable definition are used to ensure, the value is permissible (i.e. it is for example an integer value). The sum of all configurable object parameters are called _configure parameters_. To define a define a configurable variable, NX uses the method +property+, for non-configurable variables, the method +variable+ is used. Optionally, one can define in NX, that a +property+ or a +variable+ should have a public, protected or private accessor. Such an accessor is a method with the same name as the variable. In XOTcl, every +parameter+ defined as well automatically a same-named accessor method, leading to potential name conflicts with other method names. In the examples below we show the definition of configurable an non-configurable instance variables using +variable+ and +property+ respectively. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # Define class "Foo" with instance # variables "x" and "y" initialized # on instance creation. The initialization # has to be performed in the constructor. Class Foo Foo instproc init args { instvar x y set x 1 set y 2 } # Create instance of the class Foo Foo f1 # Object f1 has instance variables # x == 1 and y == 2 ---------------- |[source,tcl] ---------------- # Define class "Foo" with instance variables # "x" and "y" initialized on instance creation. # The method "variable" is similar in syntax # to Tcl's "variable" command. During # instance creation, the variable # definitions are used for the # initialization of the variables of the object. Class create Foo { :variable x 1 :variable y 2 } # Create instance of the class Foo Foo create f1 # Object f1 has instance variables # x == 1 and y == 2 ---------------- |====================== While XOTcl follows a procedural way to initialize variables via the constructor +init+, NX follows a more declarative approach. Often, classes have superclasses, which often want to provide their own instance variables and default values. The declarative approach from NX solves this via inheritance, while an procedural approach via assign statements in the constructor requires explicit constructor calls, which are often error-prone. Certainly, when a user prefers to assign initial values to instance variables via explicit assign operations in constructors, this is as ell possible in NX. NX uses the same mechanism to define class variables or object variables. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # No syntactic support for creating # class variables ---------------- |[source,tcl] ---------------- # Define a object variable "V" with value 100 and # an instance variable "x". "V" is defined for the # class object Foo, "x" is defined in the # instances of the class. "object variable" works # similar to "object method". Class create Foo { :object variable V 100 :variable x 1 } ---------------- |====================== In the next step, we define configurable instance variables which we call _properties_ in NX. XOTcl uses the method +parameter+ is a shortcut for creating multiple configurable variables with automically created accessors (methods for reading and writing of the variables). In NX, the prefered way to create configurable variables is to use the method +property+. The method +property+ in NX is similar to +variable+, but makes the variables configurable, which means that . one can specify the property as a non-positional parameter upon creation of the object, . one can query the value via the method +cget+, and . one can modify the value of the underlying variable via the method +configure+. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # Parameters specified as a list # (short form); parameter # "a" has no default, "b" has default "1" Class Foo -parameter {a {b 1}} # Create instance of the class Foo Foo f1 -a 0 # Object f1 has instance variables # a == 0 and b == 1 # XOTcl registers automatically accessors # for the parameters. Use the accessor # "b" to output the value of variable "b" puts [f1 b] # Use the setter to alter value of # instance variable "b" f1 b 100 ---------------- |[source,tcl] ---------------- # Define property "a" and "b". The # property "a" has no default, "b" has # default value "1" Class create Foo { :property a :property {b 1} } # Create instance of the class Foo Foo create f1 -a 0 # Object f1 has instance variables # a == 0 and b == 1 # Use the method "cget" to query the value # of a configuration parameter puts [f1 cget -b] # Use the method "configure" to alter the # value of instance variable "b" f1 configure -b 100 ---------------- |====================== In general, NX allows to create variables and properties with and without accessor methods. The created accessor methods might be +public+, +protected+ or +public+. When the value +none+ is provided to +-accessor+, no accessor will be created. This is actually the default in NX. In order to change the default behavior in NX, one can use +::nx::configure defaultAccessor none|public|protected|private+. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # "parameter" creates always accessor # methods, accessor methods are # always public, no "cget" is available. Class create Foo -parameter {a {b1}} # Use the accessor method to query # the value of a configuration parameter puts [f1 b] # Use the accessor method to set the # value of instance variable "a" f1 a 100 # Use the accessor method to unset the # value of instance variable "a" n.a. via # accessor ---------------- |[source,tcl] ---------------- # Define property "a" and "b". The # property "a" has no default, "b" has # default value "1" Class create Foo { :variable -accessor public a :property -accessor public {b 1} } # Use the accessor method to query # the value of a configuration parameter puts [f1 b get] # Use the accessor method to set the # value of instance variable "a" f1 a set 100 # Use the accessor method to unset the # value of instance variable "a" f1 a unset ---------------- |====================== Similar to +variable+, properties can be defined in NX on the class and on the object level. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # XOTcl provides no means to define # configurable variables at the object # level ---------------- |[source,tcl] ---------------- # Define class with a property for the class object # named "cp". This is similar to "static variables" # in some other object-oriented programming # languages. Class create Foo { ... :object property cp 101 } # Define object property "op" Object create o { :object property op 102 } ---------------- |====================== NX supports _value constraints_ (value-checkers) for object and method parameters in an orthogonal manner. NX provides a predefined set of value checkers, which can be extended by the application developer. In NX, the _value checking is optional_. This means that it is possible to develop e.g. which a large amount of value-checking and deploy the script with value checking turned off, if the script is highly performance sensitive. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # No value constraints for # parameter available ---------------- |[source,tcl] ---------------- # Predefined value constraints: # object, class, alnum, alpha, ascii, boolean, # control, digit, double, false, graph, integer, # lower, parameter, print, punct, space, true, # upper, wordchar, xdigit # # User defined value constraints are possible. # All parameter value checkers can be turned on # and off at runtime. # # Define a required boolean property "a" # and an integer property "b" with a default. # The first definition uses "properties", # the second definition uses multiple # "property" statements. Class create Foo -properties { a:boolean {b:integer 1} } ---------------- [source,tcl] ---------------- Class create Foo { :property a:boolean :property {b:integer 1} } ---------------- |====================== In XOTcl all configure parameters were _optional_. Required parameters have to be passed to the constructor of the object. NX allows to define _optional_ and _required_ configure parameters (as well as method parameters). Therefore, configure parameters can be used as the single mechanism to parameterize objects. It is in NX not necessary (and per default not possible) to pass arguments to the constructor. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # Required parameter not available ---------------- |[source,tcl] ---------------- # Required parameter: # Define a required property "a" and a # required boolean property "b" Class create Foo -properties { a:required b:boolean,required } ---------------- [source,tcl] ---------------- Class create Foo { :property a:required :property b:boolean,required } ---------------- |====================== NX supports in contrary to XOTcl to define the _multiplicity_ of values per parameter. In NX, one can specify that a parameter can accept the value "" (empty) in addition to e.g. an integer, or one can specify that the value is an empty or non-empty ist of values via the multiplicity. For every specified value, the value checkers are applied. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] --------------- # Multiplicity for parameter # not available ----------------- |[source,tcl] ---------------- # Parameter with multiplicity # ints is a list of integers, with default # objs is a non-empty list of objects # obj is a single object, maybe empty Class create Foo -properties { {ints:integer,0..n ""} objs:object,1..n obj:object,0..1 } ---------------- [source,tcl] ---------------- Class create Foo { :property {ints:integer,0..n ""} :property objs:object,1..n :property obj:object,0..1 } ---------------- |====================== For the implementation of variables and properties, NX uses slot objects, which are an extension to the +-slots+ already available in XOTcl. While very for every +property+ in NX, a slot object is created, for performance reasons, not every +variable+ has a slot associated. When an property is created, NX does actually three things: . Create a slot object, which can be specified in more detail using the init-block of the slot object . Create a parameter definition for the initialization of the object (usable via a non-positional parameter during object creation), and . register optionally an accessor function (setter), for which the usual protection levels (+public+, +protected+ or +private+) can be used. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # Define parameters via slots Class Foo -slots { Attribute a Attribute b -default 1 } # Create instance of the class Foo # and provide a value for instance # variable "a" Foo f1 -a 0 # Object f1 has a == 0 and b == 1 ---------------- |[source,tcl] ---------------- # Configurable parameters specified via the # method "property" (supports method # modifiers and scripted configuration; # see below) Class create Foo { :property a :property {b 1} } # Create instance of the class Foo and # provide a value for instance variable "a" Foo create f1 -a 0 # Object f1 has a == 0 and b == 1 ---------------- |====================== Since the slots are objects, the slot objects can be configured and parameterized like every other object in NX. Slot objects can be provided with a scripted initialization as well. We show first the definition of properties simliar to the functionality provided as well by XOTcl and show afterwards how to use value constraints, optional parameters, etc. in NX. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # Define parameter with an an # attribute-specific type checker Class Person -slots { Attribute create sex -type "sex" { my proc type=sex {name value} { switch -glob $value { m* {return m} f* {return f} default { error "expected sex but got $value" } } } } } ---------------- |[source,tcl] ---------------- # Configure parameter with scripted # definition (init-block), defining a # property specific type checker Class create Person { :property -accessor public sex:sex,convert { # define a converter to standardize representation :object method type=sex {name value} { switch -glob $value { m* {return m} f* {return f} default {error "expected sex but got $value"} } } } } ---------------- |====================== The parameters provided by a class for the initialization of instances can be introspected via querying the parameters of the method create: +/cls/ info lookup parameters create+ (see <>). ==== Delete Variable Handlers [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # No syntactic support for deleting # variable handlers ---------------- |[source,tcl] ---------------- # Like deletion of Methods: # Delete on the object, where the # variable handler is defined. /cls/ delete property /name/ /obj/ delete object property /name/ /cls/ delete variable /name/ /obj/ delete object variable /name/ ---------------- |====================== ==== Method Parameters Method parameters are used to specify the interface of a single method (what kind of values may be passed to a method, what default values are provided etc.). The method parameters specifications in XOTcl 1 were limited and allowed only value constraints for non positional arguments. NX and XOTcl 2 provide value constraints for all kind of method parameters. While XOTcl 1 required non-positional arguments to be listed in front of positional arguments, this limitation is lifted in XOTcl 2. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # Define method foo with non-positional # parameters (x, y and y) and positional # parameter (a and b) Class C C instproc foo { -x:integer -y:required -z a b } { # ... } C create c1 # invoke method foo c1 foo -x 1 -y a 2 3 ---------------- |[source,tcl] ---------------- # Define method foo with # non-positional parameters # (x, y and y) and positional # parameter (a and b) Class create C { :public method foo { -x:integer -y:required -z a b } { # ... } :create c1 } # invoke method foo c1 foo -x 1 -y a 2 3 ---------------- |[source,tcl] ---------------- # Only leading non-positional # parameters are available; no # optional positional parameters, # no value constraints on # positional parameters, # no multiplicity, ... ---------------- |[source,tcl] ---------------- # Define various forms of parameters # not available in XOTcl 1 Class create C { # trailing (or interleaved) non-positional # parameters :public method m1 {a b -x:integer -y} { # ... } # positional parameters with value constraints :public method m2 {a:integer b:boolean} { #... } # optional positional parameter (trailing) :public method set {varName value:optional} { # .... } # parameter with multiplicity :public method m3 {-objs:object,1..n c:class,0..1} { # ... } # In general, the same list of value # constraints as for configure parameter is # available (see above). # # User defined value constraints are # possible. All parameter value checkers # can be turned on and off. } ---------------- |====================== ==== Return Value Checking _Return value checking_ is a functionality available in the Next Scripting Framework, that was not yet available in XOTcl 1. A return value checker assures that a method returns always a value satisfying some value constraints. Return value checkers can be defined on all forms of methods (scripted or C-implemented). Like for other value checkers, return value checkers can be turned on and off. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # No return value checking # available ---------------- |[source,tcl] ---------------- # Define method foo with non-positional # parameters (x, y and y) and positional # parameter (a and b) Class create C { # Define method foo which returns an # integer value :method foo -returns integer {-x:integer} { # ... } # Define an alias for the Tcl command ::incr # and assure, it always returns an integer # value :alias incr -returns integer ::incr # Define a forwarder that has to return an # integer value :forward ++ -returns integer ::expr 1 + # Define a method that has to return a # non-empty list of objects :public object method instances {} \ -returns object,1..n { return [:info instances] } } ---------------- |====================== === Interceptors XOTcl and NX allow the definition of the same set of interceptors, namely class- and object-level mixins and class- and object-level filters. The primary difference in NX is the naming, since NX abandons the prefix "inst" from the names of instance specific method, but uses the the modifier +objec+" for object specific methods. Therefore, in NX, if a +mixin+ is registered on a class-level, it is applicable for the instances (a per-class mixin), and if and +object mixin+ is registered, it is a per-object mixin. In both cases, the term +mixin+ is used, in the second case with the modifier +object+. As in all other cases, one can register the same way a per-object mixin on a plain object or on a class object. ==== Register Mixin Classes and Mixin Guards [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /cls/ instmixin ... /cls/ instmixinguard /mixin/ ?condition? # Query per-class mixin /cls/ instmixin ---------------- |[source,tcl] ---------------- # Register/clear per-class mixin and guard for # a class /cls/ mixins add\|set\|clear ... /cls/ mixins guard /mixin/ ?condition? /cls/ configure -mixin ... # Query per-class mixins /cls/ mixins get /cls/ cget -mixins # Query per-class mixins (without guards) /cls/ mixins classes ---------------- |[source,tcl] ---------------- /obj/ mixin ... /obj/ mixinguard /mixin/ ?condition? # Query per-object mixins /obj/ mixin ---------------- |[source,tcl] ---------------- # Register/clear per-object mixin and guard for # an object /obj/ object mixins add\|set\|clear ... /obj/ object mixins guard /mixin/ ?condition? /obj/ configure -object-mixins ... # Query per-object mixin /obj/ object mixins get /obj/ cget -object-mixin # Query per-object mixins (without guards) /cls/ mixins classes ---------------- |====================== ==== Register Filters and Filter Guards [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # Register per-class filter and guard for # a class /cls/ instfilter ... /cls/ instfilterguard /filter/ ?condition? # Query per-class filter /cls/ instfilter ---------------- |[source,tcl] ---------------- # Register/clear per-class filter and guard for # a class /cls/ filters add\|set\|clear ... /cls/ filters guard /filter/ ?condition? /cls/ configure -filters ... # Query per-class filters /cls/ filters get /cls/ cget -filters # Query per-class filters (without guards) /cls/ filters methods ---------------- |[source,tcl] ---------------- /obj/ filter ... /obj/ filterguard /filter/ ?condition? ---------------- |[source,tcl] ---------------- # Register(clear per-object filter and guard for # an object /obj/ object filters add\|set\|clear ... /obj/ object filters guard /filter/ ?condition? /obj/ configure -object-filters ... # Query per-object filters /cls/ object filters get /obj/ cget -object-filters # Query per-object filters (without guards) /cls/ object filters methods ---------------- |====================== === Introspection In general, introspection in NX became more orthogonal and less dependent on the type of the method. In XOTcl it was e.g. necessary that a developer had to know, whether a method is e.g. scripted or not and has to use accordingly different sub-methods of +info+. In NX, one can use e.g. always +info method+ with a subcommand and the framework tries to hide the differences as far as possible. So, one can for example obtain with +info method parameter+ the parameters of scripted and C-implemented methods the same way, one one can get the definition of all methods via +info method definition+ and one can get an manual-like interface description via +info method syntax+. In addition, NX provides means to query the type of a method, and NX allows to filter by the type of the method. ==== List sub- and superclass relations While XOTcl used singular words for introspecting sub- and superclass relations, NX uses plural word to indicate that potentially a list of values is returned. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /cls/ info superclass ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info superclasses ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info subclass ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info subclasses -type setter ?pattern? ---------------- |====================== ==== List methods defined by classes While XOTcl uses different names for obtaining different kinds of methods defined by a class, NX uses +info methods+ in an orthogonal manner. NX allows as well to use the call protection to filter the returned methods. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /cls/ info instcommands ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info methods ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info instparametercmd ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info methods -type setter ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info instprocs ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info methods -type scripted ?pattern? ---------------- |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- /cls/ info methods -type alias ?pattern? /cls/ info methods -type forwarder ?pattern? /cls/ info methods -type object ?pattern? /cls/ info methods -callprotection public\|protected ... ---------------- |====================== ==== List methods defined by objects While XOTcl uses different names for obtaining different kinds of methods defined by an object, NX uses +info methods+ in an orthogonal manner. NX allows as well to use the call protection to filter the returned methods. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /obj/ info commands ?pattern? ---------------- |[source,tcl] ---------------- /obj/ info object methods ?pattern? ---------------- |[source,tcl] ---------------- /obj/ info parametercmd ?pattern? ---------------- |[source,tcl] ---------------- /obj/ info object methods -type setter ?pattern? ---------------- |[source,tcl] ---------------- /obj/ info procs ?pattern? ---------------- |[source,tcl] ---------------- /obj/ info object methods -type scripted ?pattern? ---------------- |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- /obj/ info object methods -type alias ?pattern? /obj/ info object methods -type forwarder ?pattern? /obj/ info object methods -type object ?pattern? /obj/ info object methods -callprotection public\|protected ... ---------------- |====================== ==== Check existence of a method NX provides multiple ways of checking, whether a method exists; one can use +info method exists+ to check, if a given method exists (return boolean), or one can use +info methods ?pattern?+, where +pattern+ might be a single method name without wild-card characters. The method +info methods ?pattern?+ returns a list of matching names, which might be empty. These different methods appear appropriate depending on the context. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /obj\|cls/ info \ [inst](commands\|procs\|parametercmd) \ ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info method exists /methodName/ /cls/ info methods /methodName/ /obj/ info object method exists /methodName/ /obj/ info object methods /methodName/ ---------------- |====================== ==== List callable methods In order to obtain for an object the set of artefacts defined in the class hierarchy, NX uses +info lookup+. One can either lookup methods (via +info lookup methods+) or slots (via +info lookup slots+). The plural term refers to a potential set of return values. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /obj/ info methods ?pattern? ---------------- |[source,tcl] ---------------- /obj/ info lookup methods ... ?pattern? # Returns list of method names ---------------- |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- # List only application specific methods /obj/ info lookup methods -source application ... ?pattern? # Returns list of method names ---------------- |[source,tcl] ---------------- # Options for 'info methods' # # -incontext # -nomixins ---------------- |[source,tcl] ---------------- # Options for 'info lookup methods' # # -source ... # -callprotection ... # -incontext # -type ... # -nomixins ---------------- |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- # List slot objects defined for obj # -source might be all\|application\|baseclasses # -type is the class of the slot object /obj/ info lookup slots ?-type ...? ?-source ...? ?pattern? # Returns list of slot objects ---------------- |[source,tcl] ---------------- # List registered filters /obj/ info filters -order ?-guards? ?pattern? # List registered mixins /obj/ info mixins -heritage ?-guards? ?pattern? ---------------- |[source,tcl] ---------------- # List registered filters /obj/ info lookup filters ?-guards? ?pattern? # List registered mixins /obj/ info lookup mixins ?-guards? ?pattern? ---------------- |====================== ==== List object/class where a specified method is defined +info lookup+ can be used as well to determine, where exactly an artefact is located. One can obtain this way a method handle, where a method or filter is defined. The concept of a _method-handle_ is new in NX. The method-handle can be used to obtain more information about the method, such as e.g. the definition of the method. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /obj/ procsearch /methodName/ ---------------- |[source,tcl] ---------------- /obj/ info lookup method /methodName/ # Returns method-handle ---------------- |[source,tcl] ---------------- /obj/ filtersearch /methodName/ ---------------- |[source,tcl] --------------- /obj/ info lookup filter /methodName/ # Returns method-handle ----------------- |====================== ==== List definition of scripted methods XOTcl contains a long list of +info+ subcommands for different kinds of methods and for obtaining more detailed information about these methods. In NX, this list of +info+ subcommands is much shorter and more orthogonal. For example +info method definition+ can be used to obtain with a single command the full definition of a _scripted method_, and furthermore, it works as well the same way to obtain e.g. the definition of a _forwarder_ or an _alias_. While XOTcl uses different names for info options for objects and classes (using the prefix "inst" for instance specific method), NX uses for object specific method the modifier +object+. For definition of class object specific methods, use the modifier +object+ as usual. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- /cls/ info method definition /methodName/ /obj/ info object method definition /methodName/ ---------------- |[source,tcl] ---------------- /cls/ info instbody /methodName/ /obj/ info body /methodName/ ---------------- |[source,tcl] ---------------- /cls/ info method body /methodName/ /obj/ info object method body /methodName/ ---------------- |[source,tcl] ---------------- /cls/ info instargs /methodName/ /obj/ info args /methodName/ ---------------- |[source,tcl] ---------------- /cls/ info method args /methodName/ /obj/ info object method args /methodName/ ---------------- |[source,tcl] ---------------- /cls/ info instnonposargs /methodName/ /obj/ info object method args /methodName/ ---------------- |[source,tcl] ---------------- /cls/ info method parameter /methodName/ /obj/ info object method parameter /methodName/ ---------------- |[source,tcl] ---------------- /cls/ info instdefault /methodName/ /obj/ info default /methodName/ ---------------- |[source,tcl] ---------------- # not needed, part of # "info ?object? method parameter" ---------------- |[source,tcl] ---------------- /cls/ info instpre /methodName/ /obj/ info pre /methodName/ ---------------- |[source,tcl] ---------------- /cls/ info method precondition /methodName/ /obj/ info object method precondition /methodName/ ---------------- |[source,tcl] ---------------- /cls/ info instpost /methodName/ /obj/ info post /methodName/ ---------------- |[source,tcl] ---------------- /cls/ info method postcondition /methodName/ /obj/ info object method postcondition /methodName/ ---------------- |====================== Another powerful introspection option in NX is +info ?object? method syntax+ which obtains a representation of the parameters of a method in the style of Tcl man pages (regardless of the kind of method). [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- /cls/ info method syntax /methodName/ /obj/ info object method syntax /methodName/ ---------------- |====================== [[info_configure_parameter]] ==== List Configure Parameters The way, how newly created objects can be configured is determined in NX via properties. The configuration happens during creation via the methods +create+ or +new+ or during runtime via +configure+. These methods have therefore virtual argument lists, depending on the object or class on which they are applied. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- # Return the parameters applicable to # the create method of a certain class. # class can be configured. A pattern can # be used to filter the results. /cls/ info lookup parameters create ?/pattern/? # Return in the result in documentation syntax /cls/ info lookup syntax create ?/pattern/? # "info lookup parameters configure" returns # parameters available for configuring the # current object (might contain object # specific information) /obj/ info lookup parameters configure ?pattern? # "info lookup configure syntax" returns syntax of # a call to configure in the Tcl parameter syntax /obj/ info lookup syntax configure # Obtain information from a parameter # (as e.g. returned from "info lookup # parameters configure"). nsf::parameter::info name /parameter/ nsf::parameter::info syntax /parameter/ nsf::parameter::info type /parameter/ ---------------- |====================== ==== List Variable Declarations (property and variable) [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # obtain parameter definitions defined # for a class /cls/ info parameter ---------------- |[source,tcl] ---------------- # "info variables" returns handles of # properties and variables defined by this # class or object /cls/ info variables ?pattern? /obj/ info object variables ?pattern? # "info lookup variables" returns handles # of variables and properties applicable # for the current object (might contain # object specific information) /obj/ info lookup variables /pattern/ # "info variable" lists details about a # single property or variable. /obj/ info variable definition /handle/ /obj/ info variable name /handle/ /obj/ info variable parameter /handle/ ---------------- |====================== ==== List Slots [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- # Return list of slots objects defined on the # object or class # # -source might be all\|application\|baseclasses # -type is the class of the slot object # -closure includes slots of superclasses /cls/ info slots \ ?-type value? ?-closure? ?-source value? ?pattern? /obj/ info object slots ?-type ...? ?pattern? # List reachable slot objects defined for obj # -source might be all\|application\|baseclasses # -type is the class of the slot object # Returns list of slot objects. /obj/ info lookup slots \ ?-type ...? ?-source ... ?pattern? # Obtain definition, name or parameter from # slot object /slotobj/ definition /slotobj/ name /slotobj/ parameter ---------------- |====================== ==== List Filter or Mixins In NX all introspection options for filters are provided via +info filters+ and all introspection options for mixins are provided via +info mixins+. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /obj/ info filter ?-guards? ?-order? ?pattern? /obj/ info filterguard /name/ ---------------- |[source,tcl] ---------------- /obj/ info object filters \ ?-guards? ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info instfilter \ ?-guards? ?-order? ?pattern? /cls/ info instfilterguard /name/ ---------------- |[source,tcl] ---------------- /cls/ info filters \ ?-guards? ?pattern? ---------------- |[source,tcl] ---------------- /obj/ info mixin ?-guards? ?-order ?pattern? /obj/ info mixinguard /name/ ---------------- |[source,tcl] ---------------- /obj/ info object mixins \ ?-guards? ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info instmixin \ ?-guards? ?-order? ?pattern? /cls/ info instmixinguard /name/ ---------------- |[source,tcl] ---------------- /cls/ info mixins \ ?-closure? ?-guards? ?-heritage? ?pattern? ---------------- |====================== ==== List definition of methods defined by aliases, setters or forwarders As mentioned earlier, +info method definition+ can be used on every kind of method. The same call can be used to obtain the definition of a scripted method, a method-alias, a forwarder or a setter method. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- /cls/ info method definition /methodName/ /obj/ info object method definition /methodName/ ---------------- |====================== ==== List Method-Handles NX supports _method-handles_ to provide means to obtain further information about a method or to change maybe some properties of a method. When a method is created, the method creating method returns the method handle to the created method. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- # # List the method handle of the specified method, # can be used e.g. for aliases. "handle" is the short # form of "definitionhandle". # /cls/ info method handle /methodName/ /obj/ info object method handle /methodName/ # # For ensemble methods (method name contains # spaces) one can query as well the registration # handle, which is the handle to the root of the # ensemble; the definiton handle points to the # leaf of the ensemble. # /cls/ info method registrationhandle /methodName/ /obj/ info object method registrationhandle /methodName/ # # For aliases, one can query the original # definition via "info method origin" # /cls/ info method origin /methodName/ /obj/ info object method origin /methodName/ ---------------- |====================== ==== List type of a method The method +info ?object? method type+ is new in NX to obtain the type of the specified method. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- /cls/ info method type /methodName/ /obj/ info object method type /methodName/ ---------------- |====================== ==== List the scope of mixin classes NX provides a richer set of introspection options to obtain information, where mixins classes are mixed into. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /cls/ info mixinof ?-closure? ?pattern? ---------------- |[source,tcl] ---------------- # List objects, where /cls/ is a # per-object mixin /cls/ info mixinof -scope object ?-closure? \ ?pattern? ---------------- |[source,tcl] ---------------- /cls/ info instmixinof ?-closure? ?pattern? ---------------- |[source,tcl] ---------------- # List classes, where /cls/ is a per-class mixin /cls/ info mixinof -scope class ?-closure? \ ?pattern? ---------------- |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- # List objects and classes, where /cls/ is # either a per-object or a per-class mixin /cls/ info mixinof -scope all ?-closure? \ ?pattern? ---------------- [source,tcl] ---------------- /cls/ info mixinof ?-closure? ?pattern? ---------------- |====================== ==== Check properties of object and classes Similar as noted before, NX uses rather a hierarchical approach of naming using multiple layers of subcommands). [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /obj/ istype /sometype/ ---------------- |[source,tcl] ---------------- # Check if object is a subtype of some class /obj/ info has type /sometype/ ---------------- |[source,tcl] ---------------- /obj/ ismixin /cls/ ---------------- |[source,tcl] ---------------- # Check if object has the specified mixin registered /obj/ info has mixin /cls/ ---------------- |[source,tcl] ---------------- /obj/ isclass ?/cls/? ---------------- |[source,tcl] ---------------- cd # Check if object is an NX class /obj/ has type ::nx::Class # Check if object is a class in one of the # NSF object systems ::nsf::is class /obj/ ---------------- |[source,tcl] ---------------- /obj/ ismetaclass /cls/ ---------------- |[source,tcl] ---------------- # Check if class is an NX metaclass expr {[/cls/ info heritage ::nx::Class] ne ""} # Check if object is a metaclass in one of the # NSF object systems ::nsf::is metaclass /obj/ ---------------- |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- # Check if object is a baseclass of an object system ::nsf::is baseclass /obj/ ---------------- |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- # Return name of object (without namespace prefix) /obj/ info name ---------------- |[source,tcl] ---------------- /obj/ object::exists /obj/ ---------------- |[source,tcl] ---------------- # Check for existence of object (nsf primitive) ::nsf::object::exists /obj/ ---------------- |====================== ==== Call-stack Introspection Call-stack introspection is very similar in NX and XOTcl. NX uses for subcommand the term +current+ instead of +self+, since +self+ has a strong connotation to the current object. The term +proc+ is renamed by +method+. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- self ---------------- |[source,tcl] ---------------- self ---------------- [source,tcl] ---------------- current object ---------------- |[source,tcl] ---------------- self class ---------------- |[source,tcl] ---------------- current class ---------------- |[source,tcl] ---------------- self args ---------------- |[source,tcl] ---------------- current args ---------------- |[source,tcl] ---------------- self proc ---------------- |[source,tcl] ---------------- current method ---------------- |[source,tcl] ---------------- self callingclass ---------------- |[source,tcl] ---------------- current calledclass ---------------- |[source,tcl] ---------------- self callingobject ---------------- |[source,tcl] ---------------- current callingobject ---------------- |[source,tcl] ---------------- self callingproc ---------------- |[source,tcl] ---------------- current callingmethod ---------------- |[source,tcl] ---------------- self calledclass ---------------- |[source,tcl] ---------------- current calledclass ---------------- |[source,tcl] ---------------- self calledproc ---------------- |[source,tcl] ---------------- current calledmethod ---------------- |[source,tcl] ---------------- self isnextcall ---------------- |[source,tcl] ---------------- current isnextcall ---------------- |[source,tcl] ---------------- self next ---------------- |[source,tcl] ---------------- # Returns method-handle of the # method to be called via "next" current next ---------------- |[source,tcl] ---------------- self filterreg ---------------- |[source,tcl] ---------------- # Returns method-handle of the # filter method current filterreg ---------------- |[source,tcl] ---------------- self callinglevel ---------------- |[source,tcl] ---------------- current callinglevel ---------------- |[source,tcl] ---------------- self activelevel ---------------- |[source,tcl] ---------------- current activelevel ---------------- |====================== === Other Predefined Methods [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /obj/ requireNamespace ---------------- |[source,tcl] ---------------- /obj/ require namespace ---------------- |[source,tcl] ---------------- # n.a. ---------------- |[source,tcl] ---------------- /obj/ require method ---------------- |====================== === Dispatch, Aliases, etc. todo: to be done or omitted === Assertions In contrary to XOTcl, NX provides no pre-registered methods for assertion handling. All assertion handling can e performed via the Next Scripting primitive +nsf::method::assertion+. [options="header",cols="asciidoc,asciidoc",frame="none",valign="middle"] |====================== |XOTcl |Next Scripting Language |[source,tcl] ---------------- /obj/ check /checkoptions/ ---------------- |[source,tcl] ---------------- ::nsf::method::assertion /obj/ check /checkoptions/ ---------------- |[source,tcl] ---------------- /obj/ info check ---------------- |[source,tcl] ---------------- ::nsf::method::assertion /obj/ check ---------------- |[source,tcl] ---------------- /obj/ invar /conditions/ ---------------- |[source,tcl] ---------------- ::nsf::method::assertion /obj/ object-invar /conditions/ ---------------- |[source,tcl] ---------------- /obj/ info invar ---------------- |[source,tcl] ---------------- ::nsf::method::assertion /obj/ object-invar ---------------- |[source,tcl] ---------------- /cls/ instinvar /conditions/ ---------------- |[source,tcl] ---------------- ::nsf::method::assertion /cls/ class-invar /conditions/ ---------------- |[source,tcl] ---------------- /cls/ info instinvar ---------------- |[source,tcl] ---------------- ::nsf::method::assertion /cls/ class-invar ---------------- |[source,tcl] ---------------- /cls/ invar /conditions/ ---------------- |[source,tcl] ---------------- ::nsf::method::assertion /cls/ object-invar /conditions/ ---------------- |[source,tcl] ---------------- /cls/ info invar ---------------- |[source,tcl] ---------------- ::nsf::method::assertion /cls/ object-invar ---------------- |====================== === Method Protection As described <>, NX supports method protection via the method modifiers `protected` and `public`. A protected method can be only called from an object of that class, while public methods can be called from every object. The method protection can be used to every kind of method, such as e.g. scripted methods, aliases, forwarders, or accessors. For invocations, the most specific definition (might be a mixin) is used for determining the protection. == Incompatibilities between XOTcl 1 and XOTcl 2 === Resolvers The resolvers (variable resolvers, function resolvers) of the Next Scripting Framework are used as well within XOTcl 2. When variable names or method names starting with a single colon are used in XOTcl 1 scripts, conflicts will arise with the resolver. These names must be replaced. === Parameters The following changes for parameters could be regarded as bug-fixes. ==== Parameter usage without a value In XOTcl 1, it was possible to call a parameter method during object creation via the dash-interface without a value (in the example below `-x`). [source,tcl] ---------------- # XOTcl example Class Foo -parameter {x y} Foo f1 -x -y 1 ---------------- Such cases are most likely mistakes. All parameter configurations in XOTcl 2 require an argument. ==== Ignored Parameter definitions In XOTcl 1, a more specific parameter definition without a default was ignored when a more general parameter definition with a default was present. In the example below, the object `b1` contained in XOTcl 1 incorrectly the parameter `x` (set via default from `Foo`), while in XOTcl 2, the variable won't be set. [source,tcl] ---------------- # XOTcl example Class Foo -parameter {{x 1}} Class Bar -superclass Foo -parameter x Bar b1 ---------------- ==== Changing classes and superclasses NX does not define the methods +class+ and +superclass+ (like XOTcl), but allows to alter all object/class relations (including class/superclass/object-mixin/...) +nsf::relation::set+. The class and superclass can be certainly queried in all variants with +info class+ or +info superclasses+. [source,tcl] ---------------- # NX example nx::Class create Foo Foo create f1 # now alter the class of object f1 nsf::relation::set f1 class ::nx::Object ---------------- ==== Overwriting procs/methods with objects and vice versa NSF is now more conservative on object/method creation. In contrary to XOTcl 1 NSF does not allow to redefined a pre-existing command (e.g. "set") with an object and vice versa. Like in XOTcl 1, preexisting objects and classes con be redefined (necessary for reloading objects/classes in an running interpreter). ==== Info heritage +info heritage+ returns in XOTcl 1 the transitive superclass hierarchy, which is equivalent with +info superclasses -closure+ and therefore not necessary. In XOTcl 2 (and NX), +info heritage+ includes as well the transitive per-class mixins. === Slots All slot objects (also XOTcl slot objects) are now next-scripting objects of baseclass `::nx::Slot`. The name of the experimental default-setter `initcmd` was changed to `defaultcmd`. Code directly working on the slots objects has to be adapted. === Obsolete Commands Parameter-classes were rarely used and have been replaced by the more general object parameterization. Therefore, `cl info parameterclass` has been removed. === Stronger Checking The Next Scripting Framework performs stronger checking than XOTcl 1 For example, the requiredness of slots in XOTcl 1 was just a comment, while XOTcl 2 enforces it. === Exit Handlers The exit hander interface changed from a method of `::xotcl::Object` into the Tcl command `::nsf::exithandler`: [source,tcl] ---------------- # NX example ::nsf::exithandler set|get|unset ?arg? ---------------- doc/next-tutorial/000077500000000000000000000000001242365656200144555ustar00rootroot00000000000000doc/next-tutorial/configure-parameter.graffle000066400000000000000000000743121242365656200217530ustar00rootroot00000000000000 ActiveLayerIndex 0 ApplicationVersion com.omnigroup.OmniGrafflePro 139.16.0.171715 AutoAdjust BackgroundGraphic Bounds {{0, 0}, {818, 571}} Class SolidGraphic ID 2 Style shadow Draws NO stroke Draws NO BaseZoom 0 CanvasOrigin {0, 0} ColumnAlign 1 ColumnSpacing 36 CreationDate 2009-12-29 15:06:59 +0000 Creator Gustaf A. Neumann DisplayScale 1.000 cm = 1.000 cm GraphDocumentVersion 8 GraphicsList Bounds {{87.302781487907637, 305.10152616886438}, {75, 24}} Class ShapedGraphic ID 39 Line ID 38 Position 0.31776151061058044 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 22 ID 38 Points {139.69538655993259, 285.173} {146, 307.61900000000003} {98, 330.35399999999998} {165, 347.30053333333331} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 14 Bounds {{356.22000000000003, 385}, {90, 36}} Class ShapedGraphic ID 27 Magnets {0, 1} {0, -1} {1, 0} {-1, 0} Shape Rectangle Style Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 s1 : Student} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 4 ID 26 Points {254.48723521986653, 337.5} {306.85878986242125, 271.69200000000001} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 22 Class TableGroup Graphics Bounds {{165, 337.5}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 23 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 Student} VerticalPad 0 TextPlacement 0 Bounds {{165, 351.5}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 24 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 matnr\ oncampus true} VerticalPad 0 TextPlacement 0 Bounds {{165, 379.5}, {136, 12}} Class ShapedGraphic FitText Vertical Flow Resize ID 25 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 VerticalPad 0 TextPlacement 0 GridH 23 24 25 GroupConnect YES ID 22 Bounds {{371.25708768882407, 288.97903919728918}, {75, 24}} Class ShapedGraphic ID 9 Line ID 8 Position 0.57259494066238403 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 7 ID 8 Points {365.79804616302977, 271.69200000000001} {412, 305} {394.98000000000002, 325} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 4 Bounds {{349.98000000000002, 325}, {90, 36}} Class ShapedGraphic ID 7 Magnets {0, 1} {0, -1} {1, 0} {-1, 0} Shape Rectangle Style Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 p1 : Person} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 1 ID 6 Points {320.84853998899894, 217.69200000000001} {306.49451395608526, 166} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 4 Bounds {{199.62449569843844, 285.46375730627057}, {75, 24}} Class ShapedGraphic ID 10 Line ID 5 Position 0.79031980037689209 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 4 ID 5 Points {190, 242.28323423423424} {233, 255} {206, 312} {279.26800035656981, 271.69200000000001} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 14 Class TableGroup Graphics Bounds {{260.346, 217.69200000000001}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 11 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 Person} VerticalPad 0 TextPlacement 0 Bounds {{260.346, 231.69200000000001}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 12 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 name\ birthday} VerticalPad 0 TextPlacement 0 Bounds {{260.346, 259.69200000000001}, {136, 12}} Class ShapedGraphic FitText Vertical Flow Resize ID 21 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 VerticalPad 0 TextPlacement 0 GridH 11 12 21 GroupConnect YES ID 4 AllowLabelDrop Class LineGraphic Head ID 1 ID 3 Points {190, 173.57753903142077} {221, 151.42372594280374} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 14 Class TableGroup Graphics Bounds {{54, 159.173}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 15 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::Class} VerticalPad 0 TextPlacement 0 Bounds {{54, 173.173}, {136, 42}} Class ShapedGraphic FitText Vertical Flow Resize ID 16 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 mixin\ superclass nx::Object\ ...} VerticalPad 0 TextPlacement 0 Bounds {{54, 215.173}, {136, 70}} Class ShapedGraphic FitText Vertical Flow Resize ID 17 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 attribute\ class-object\ create\ method\ ...} VerticalPad 0 TextPlacement 0 GridH 15 16 17 GroupConnect YES ID 14 Class TableGroup Graphics Bounds {{221, 40}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 18 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::Object} VerticalPad 0 TextPlacement 0 Bounds {{221, 54}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 19 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 mixin\ ...} VerticalPad 0 TextPlacement 0 Bounds {{221, 82}, {136, 84}} Class ShapedGraphic FitText Vertical Flow Resize ID 20 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 cget\ configure\ info\ destroy\ method\ ...} VerticalPad 0 TextPlacement 0 GridH 18 19 20 GroupConnect YES ID 1 AllowLabelDrop Class LineGraphic Head ID 27 ID 36 Points {296.11688311688312, 391.5} {323, 403} {356.22000000000003, 403} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 22 Bounds {{279.03420446036012, 404.90156658330977}, {75, 24}} Class ShapedGraphic ID 37 Line ID 36 Offset -15 Position 0.42781621217727661 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 GridInfo GridSpacing 14.17322826385498 MajorGridSpacing 10 GuidesLocked NO GuidesVisible YES HPages 1 ImageCounter 1 KeepToScale Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo Animate NO circoMinDist 18 circoSeparation 0.0 layoutEngine dot neatoSeparation 0.0 twopiSeparation 0.0 LinksVisible NO MagnetsVisible NO MasterSheets ModificationDate 2013-05-21 18:55:42 +0000 Modifier Gustaf Neumann NotesVisible NO Orientation 2 OriginVisible NO PageBreaks YES PrintInfo NSBottomMargin float 12 NSHorizonalPagination coded BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG NSJobFeatures coded BAtzdHJlYW10eXBlZIHoA4QBQISEhBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCEhAhOU09iamVjdACFhAFpAIY= NSLeftMargin float 12 NSPagesPerSheet int 1 NSPaperSize size {842, 595} NSPrintAllPages int 0 NSPrintReverseOrientation int 0 NSReversePageOrder int 0 NSRightMargin float 12 NSTopMargin float 12 PrintOnePage ReadOnly NO RowAlign 1 RowSpacing 36 SheetTitle Canvas 1 SmartAlignmentGuidesActive YES SmartDistanceGuidesActive YES UniqueID 1 UseEntirePage VPages 1 WindowInfo CurrentSheet 0 ExpandedCanvases Frame {{48, 272}, {753, 756}} ListView OutlineWidth 142 RightSidebar Sidebar SidebarWidth 138 VisibleRegion {{1, -45}, {615, 662}} Zoom 1 ZoomValues Canvas 1 1 1 doc/next-tutorial/configure-parameter.png000066400000000000000000001107461242365656200211330ustar00rootroot00000000000000‰PNG  IHDR±¥t—È< pHYs  šœÕiTXtXML:com.adobe.xmp 5 2 1 m ž@IDATxì ÜUS÷Ç÷û¾f2„d¬ š ¡B!äERDe&ó©(“i‡§žzª;çœs²À»b„ î‚ .ðöØf*Ð~•ÄT‹Oö~R¥cÇŠ‡€‘Xñ°¶; %úÃhQÿþûïNMsá,¼ýöÛn—]vq7Ýt“;è ƒÜæ›oîÎ?ÿ|!–[o½UŽ©¦tôÑG»6mÚ¸ H2“&Mr]t‘ûþûï—ú?räH÷É'Ÿ¸“O>Ù?ÞõèÑÃMŸ>ݵnÝÚ¡=ûì³ñ¬|ôÑGnÏ=÷t›nº©ëÓ§OÜÙdÖ¬YnæÌ™rÏqá…ºF¹-¶ØÂ 80~=C‡u[o½µÛf›m$ÿß~û­5j”{úé§å¼Ž;Jå\ã"û³”Ê xS~(Kº,u²íˆFb‘x –‰|" ••­êT$†ÆôÜsϹÓO?Ý}óÍ7îÇtW\q…L—.]Ü|àÎ;ï5×\SÒãyêÕ«çV]uU9r6/;"åϯ¿þêX”ÄRiÊ)²ƒECÀH¬hPÛŠ€•$†Iˆ )9- ­‡>$äá‡v«¯¾º»öÚkÝ×_-&AÈÃï_:á„ÜØ±c…,¸Æÿÿù石K´#ÙøßÚ‚£‡Ê±Ç+ÚšÞp<òˆŠ¯Ñ 7n,÷À¸uÖYG4-NàØ²Ë.+Z׌3\ï޽݊+®è:uêäš5k&iðœc’ÕÂÔ”h$–¯(5ïÄ(¼ËCހ̨€”Ä ²T¢•}ݺuå4ZãÈ¡‡ê $}[gu–[mµÕd??8^°¨øÿñDDФTsãÿœ9sX¹ Èšœ>@Ö_}™ùBÞ!Ežiå•WŽúá‡dûã?–k×Xc ùã7ÆÏ±ì€Ä”Àh)‰½i²ÙaY¬³M+ÒvŸ¢!@Å£••S*ÁK0‘Ч¤¦¿[n¹EL“‰Î ï£o yì±Çjzâ‰'ä¿ç&K„þ<ýcìGƒbŒ¦Ì÷ß?¾Ì›7ÃÒG¦„Æÿ{î¹ÇÝyçlšd‰ %1Ê™D#±è¾ËY-PMŒÖ´š³M$¦>ú•ðð{ï½÷j8TÐ/Õ²eKÇ80Äÿâf›m&ýh§vš»ãŽ;\çÎÝk¯½&$Í›7g s$•h¸Ûµk'Ú !¯¿þºÛu×]Ý%—\"iì³Ï>Ò/‡³Ç]wÝå;ì07mÚ49¦Q'03Ze,¤üÀXü†PÊ ì`É0sbÉ_e PPi+™i«:›{ 0@´ž+¯¼ÒAD'N§‰®]»º¦M›ºùóç»—^z)®ùÿ—_~y‡÷"dvà 7Äo»ÿþû»áÇÇÿ³9ñŒ3ÎM “$%a6l˜cÐ3ý]˜:¹ÿÅ_,§‘\ø!Q„cýû÷—mî÷ÔSO¹Ýwß]Ò§¯Ì$9jBôË‹‘r¼¢päÁ 2]9 oÂò¨|ðòC‹úé§ŸÄÛws<>øà’h#8qЧ…{¼ö¹…–<“OœGTpA3{çwt—#,Òwß}'}j~ Ÿ1®ÿTÂo¼qü|Ý`€ö™é>[×D<ï½÷^”Î{±‡>ÈVXÁÑ(Q­¶æUö¯Ô˜&Vê7`÷/TêÚ>£òÑí‚Ü,M¢“ON‰N_n¹åjœƒ3 }q8–ø‚iSÝæýýTÀ¾³ˆŒm#°0"‰ÿû妔e&qîlo"ŒÄ¡bûÊ*u­”Ø.'A›b3ýh&ÅCÀ/7Å»«Ý©6‰Õ=»6²h+ZIL3ZndvÒI'iÖm](/˜¤ý™‹|-na$V ðìÒh" D¥D¦¹Õ}Ñ̹媔PN´ìh>øï;yè~[Gs±Î»°œäŸ¸ò‘,Þƒêý—,=+¿ñÆÉÛþ2@@IÌׯt_d¿*³hšXU¾öÊ~h5 ±Ö–tmMBXºÊŒHõëׯlp+üé /-+”ÕÜÓ½û ‡%ÒgšX¤_e®6øVFáô8Œ Áu‰">’Û¶m+Þ~d—Œûzå•WÜ;ì .ó¤Åø,"Ô#áýôÓOe0ò‘G)cÊpÐhÑ¢…¤)'ÙO¤ÐFfÒ'4Ýgëh!`$­÷a¹É>±ŒÄ~þùg Óôå—_J´ Æd1Ý ƒ›™ž…ù¼oF|B¦V!Æâ†n舣H`à3Ï<Ó1E BÔyBG‘&¤ÇذG}TȰoß¾yz2KÆ0|Ìœè£aÛ‹€Ojá‡$.á!CÄŒDd ÈŠ¨öÈzë­'Q;ükˆÀ±å–[:¢aàÖ“&1Ï>ûl׫W¯ð)ö?¢øe…Æÿý}ÍvÕfË4±ª}õ•ûàZéø¿~r"«ÙˆÈ ÚUYf™e$j†þg ±¡}¡^C?ùÇÙfª *¼Ê*«H¨¨ð9ö?ºh"‡Y2M>ºOP=93«žw]5Oª.ÑJ\¬u_"”lK´3"óˆ1Ѥö™…ÏSR ï·ÿå…eÇ,ÚïÌH,ÚïÇr—Zñ(‰åDÊK˜5™ÈñÌä6Ërz¬ì C \0+×7gùN‰€¶¢µem-é”pÙÁÿ!à—¥<0sby¼'Ëe–¨ùPIL/72S$lÊ ³:SN´ì˜&Ÿ©èì3‹Î»°œä %*%2­H^÷åéV–L!@9Ѳ£ÅHÍ$º˜91ºïÆrV |âªE2Y_:uêT÷Ö[oe}] ”Ä|mL÷E#‡–‹0FbaDìÙ# &!ÖÚ’.–IhäÈ‘î…^({ «õ /-+”ÕÜÈ¢["ŒÄ¢ûn,gµDÀ¯x´2J”äÃ?ì6ß|s·á†ºSO=Õýúë¯rÚ#<âš7oîvÙe7fÌ×£Gøå·Ür‹ÛrË-e¹á†dÿСCÝŒ3Ü€Üý÷ß?×6ÊmôhŽ}BÓ}¶ŽÖ'­÷a¹É>±ŒÄ>ùä×µkW7~üx·é¦›º£Ž:ÊAJÙ¡‡ên½õV·Æk¸3Ï<3NnÔàÁƒÝÍ7ßìÖ[o=wÈ!‡¸5×\ÓuïÞÝ=ôÐCn·Ývs»ï¾{žžÄ’1 T˜&– ;V1ø¤æ?ƒ›Û·oï:uêä¶Øb ‡6µÉ&›ˆFÅ`ç.]ºÈqHM‰¨'t’Ûk¯½ÜÖ[oíN;í4G誺u뺕W^ÙÕ«WÏÕ©SÇ¿m—~YáóßßWFRY5M¬*^su=¤V:~ÅãoûhÌŸ?_ÈK÷µlÙÒ±œqÆ®qãÆºÛí¸ãŽñí>øÀÍ;×]y啲ï?þsdüÛ({´ ñ ™’YÙ?X>€‘X¾Ôj$u‰Vâb­ûÂØ }½þúëñÝï¿ÿ¾[´h‘û׿þå,XßÿÅ_Ä·W[m5w饗Šù¿ýö›ûå—_âÇm£r ì¨^9OUYObæÄÊzŸö4Zñ(‰¥e¿ýösO>ù¤£oì¿ÿý¯˜¿üòK·óÎ;»ÇÜ}úé§²ذañdèïš8q¢vä‘Gºk®¹FŽC~ê¿À6Ê:Tûʤ•ÝVP†M« —iò7>‘¥jIo¶Ùf"Ã;qÝu×uÛl³;ðÀE{óÍ7]»víÜŸþé:vìè0#"}úô‘>´F‰†·í¶Ûº#Fȱ:¸óÏ?ßÕ¯_ßuîÜYöÙOù  e%ìj_>OP}9ýGðÒbÕ÷ØöÄ•ŠÚ}ThCK–,q‹/vß~û­ûúë¯ÝÁœÔ4ôÝwß qa*D˜_ì‡~'qúôéBT÷Üsç³ùðÃe¯F_¸ïŠ+®(éùûm;Úðž'MšäÖ^{mñH¥,à¨Ã»\n¹åì}Fôõ™&ÑcÙªÚ6ÓuºÔð,ô2D«êׯŸ[k­µÜ%—\"˜žC…&/=¶Ê*«è¦­Ë Ê‹–]—Ù#T]v­O¬ê^¹=p&`Z¤UN_ÙÌ™3Åw{CÀˆ¦‰Eë}Xn"„€ºÛG(K–CÀ!`šXûkÔL‘ô‹™†@á0+<Æv‡*Bàg"xôíÛ×wÞyUôôö¨†@ñ0sbñ1·;V0S¦LqÇ{¬Ä`üþûïãNüÈöh†@I0M¬¤ðÛÍ£€@²(ö vnÚ´©ÃÕš1_ ‚F† ânºé&‰«¸þúëKa=5J³&&#îø ŠF>ÿüsG,F"„àéxòÉ'˸³—^zI®—“‚Ÿ_|Ñ~øáò—Ö,8p ìK=_¯µµ!P‰Uã[·gŽ# Q쯺ê*‰ÐALD¢Ø©ƒèö×^{­Œcì‘9Ž]xá…®[·ný~Μ9BXtLÛÒ«W/@MØ*b3" nР»ë®»ÜG}$$øÓO?Iß™?‰&}iï¼óŽ\³páBwÑE‰‹?yÑèùä r>|¸7nœœk?†@µ"`æÄj}óöÜ‚€ÅžD±‡xî¸ã·ÓN;ÉX1ö÷ïß_¢p0xaúÈqÀñqĽÂ[}õÕå<~ˆøñüóÏKºË,³Œ»þúë3&ŸV­ZÉù¤Cä|žÏž¦fbT+FbÕúæí¹ ŸŠºÕÓ¯µýöÛën·Î:ëHô" Ì#¦BT‡T€ÑÔ˜“ CØnÒ¤‰^^c áùÒ¢E‹ø_‹ž‡Â6 8Fbq(l£HÅ~«­¶róæÍ‹C‚i²ÚxãeŸÎø?!ÅA¹ÞÇçGÅ÷#ìc‚L)¢ç§ÙU-Ö'Vµ¯Þ’E±gÿOÛ(ö f?¢ÙCf˜sò„×ã;ì Nô›Ó!D†à¾Oâe—]6å-05&‹žŸòB;˜´ì{ï½W¢Øó.V]uUG0g‹bŸº’ž`}b%…ßnÂQì5_ Kmò»øâ‹eÌ‘ñ?Ö¶mÛ8‘6Ž#™Hªèù™\o畆€™+íÚóDúÁ0ÑH\øM C ö˜&V{ -C -ÌôL”CÀÈ/¦‰åOK­‚°èôüríÑÊ#±²}u–ñ| @$Í7ߘ†å±Çs³gÏ–˜‰Þ`ƒ \¯ z½‰!`ÓÄŠ‹·Ý-âhtú6mÚ8NOÀ^¢nhtú‘#GºÞ½{Kô ùY~üøñ2Ëž!P™‰Uæ{µ§Êptú_ýu©”ˆsHô BEit{ÝgkCÀ(FbÅÁÙîR&dž‡Ÿ}öYü‰Þ}÷]‰¯ßa†€!P4ŒÄŠµÝ¨R Â=7~ûí7qèx衇â“gVÊ3Úså‚€‘X¹¼)Ëgd`ç—^zI"Ý}ž±{íµWdòg1ª ‹b_Mo» ž5×(öÙBÃ}pÏ'ҹߖm:v~t°(öÑyÙäÄ\ì³AËÎ5þ‡‘é3‰ôa€†@a0sbañµÔ CÀ0 ˆ€‘XÁµ¤ CÀ0 ‹€‘XañµÔËÿüç?e˜k˲!P‰Uç{·§N‚À'Ÿ|â6Ùd“$GÓïž:uª{ë­·ÒŸhg†@^0Ë Œ–ˆ!ð„¤zá… CÀ(FbEÚn]¬Ü²eKתU+wß}÷ÕÈè-·Üâ¶ÜrKYn¸á†ø±™3gºŽ;JYôû{ï½×uÔQñ´¿ýö[·ýöۻŋÇ÷Ù†!`$FÀH,1.¶·Jøî»ïÜ¡‡êºuëæ®¾új7f̘ø“CDƒv×^{­›8q¢>|¸7nœ'ÊýÙgŸíæÌ™ã §o¼ÑAVÏ 'œàvß}w·páBwÑE¹µÖZËuíÚÕùÑï§OŸ.iq?â0ÞyçB„$NÄüVXAÆ Å3c†€!#±„°ØÎjA2AƒÚ¶mëÎ8ãŒø£CLD¨'ÇÖ[oíN;í4w÷Ýw»?þøÃ}õÕWŽ˜‰LÁrÛm·É´-uëÖu+¯¼²«W¯žD½'!Ò¾þúë]ãÆ]²è÷n¸¡kÑ¢…{ôÑGåÞÌ"}ðÁÇóa†€!#±äØØ‘*@óßÎ;ïÒwÜ1¾ýÁ¸Aƒ )AL\pײË.ë03¢¡A\Ä–H '•TÑï»téâ|ðA‡gäO<á:wÙÚ0R `$–;Tù0 “aªJJeµÕV“>-´.–/¾øBL}MëÖ­%ìÔsÏ=瘮¥OŸ>zYÒuªè÷h^hbZ3F›†@zŒÄÒcdgT0:urO>ù¤ûðÃÝŸþ)}_ú¸ôkÑF´z–#<Ò]sÍ5âpѬY3÷ã?ºm¶ÙFú»è[CG•h2Ž¥Š~¿ÑF‰Éñ /the&†€!;13œì¬ E`³Í6síÚµsD£'ï>ûìR´+H®Q£Fâ¼±í¶Ûº#FˆÃžûî+çã”ñÍ7߸뮻N®ëСƒ;ÿüó]ýúõãéèÑï÷ÜsO‰~¿Ê*«ÈÚ~ya²ÄÉÄÄ02CÀ¢Øg†“U&äÅžþ*¦TYgujúh‰\O4{®}ùå—Ùmbi0ïÄ4ÙáÊG N:nÈ!îŸÿü§L¡ÂìÎt<8Q<ˆ§èG´ç€F´gúH¯´ÒJnÝu×u¸Ê¯ºêª¹žôz÷îŸ+Œ±`\GÔz„ùãÇ—ÌOƵk¯½¶ žö§„‘“íÇ0"`$–ÛYM@>²üòË;õª,³Ì2âmHDû¹sçº+¯¼Rá=¸ùæ›ëi5ÖŸ¦ÇÔ,K’"L• ZÜ×_-Î †@ªLÄibé0sbzŒìŒ G Qx¨ð#'‹h>ÿÉÒKÅ2£OLûÁXüñlj’¶}†€!BÀH,ˆý5!,¢=碭©¶•èZÝ—,Š=ì 2ŒiaFiúáL C =Fbé1²3 qÎÀá²Ák‘m¼fo~ûí·ã}_Éà"Š=îøxA4˜€Ãžoß¾î˜cŽGHHÓÄ0Ò#`QìÓcdg”˜âè¯úå—_ž‡ÌùÅ\_ô=á>_›(\˼cH8(0÷d²LúÀR ùÃQçúÎ|!¯xI6nÜØxؤ¸Xûâ⯻™cG¾´t*Tí—]vYÇ’N’E±ç:úÝXL C sÌœ˜9Vv¦!`†@Ä0M,b/IJ“˜ú˜a/¿… º ¸Ï>ûLÆ}…'®Ìí.v•!`D #±¨½ËOJž}öY7uêT·hÑ"YèCbû«¯¾S\½zõËZk­åêÖ­+ËG}ä¾øâ‹”éÚACÀ(OŒÄÊó½Ue®ÿýw·ë®»ºm·ÝVâÖ¯__¢eèš>©°cÙ­·Þê®»î:7eÊ”ªÄÍÚ¨dŒÄ*ùíVسMƒ€½ŒÙÚm·Ýƒ„ÓÉ„ $âꫯžîT;neˆ@AHLݘu]†¸X– €€†bªMÒ[o½µcLÕa‡æž{Ñ1¸.ï &.¢‰!`T&y!1È*¼SO¨¡)ÕµÆ-]Ç<é¶®s%5‚èÒ/6`À׿ÿ¤€Þwß}âÐÁ”(Œ31 ÊC V$æ„ÅÂ>ÖôMøÇÄ*¯ð¤{"ÈJ‰J·'Å>þSNØÖsÒ¥ç;v¬ôí¹çžŽY ó{Ù>CÀ¨r&1Ÿ  ,%®UVY¥B ±Ç(K–,3 „F¹A²%2¦?AëÔ©“Ë ~òÉ'%¦áŽ;î(; õ,–®!`”œIŒlCdØŸþ)kú t¿lØ!B /C‚æâM‘±²Ä:SÁãI(Û·oïN<ñD™ÐÒ¿–ù¿Ž;î8—m†@"sÄÕÄ 1ŒÊ©TBÔo‚²šDß~ûMâoML—LrNÄ<Ð1K2Ž˜_{í5wÇwÄ/ã7Ü»ï¾ëößÿø¾Twß}·Œ3ãœçŸÞ½øâ‹r:ƒ¦éW31 è"‰A`ˆš5øi&ÓQäŠ7ß|ÓµnÝZº2Àuƒ 6p·Ýv[ü6Lϸ"“è @9¡Á£¼–§t9ÄDˆw"ÁwgÏžíš4iâVXa‡ý™gžéÔŒ\ýõ¢…eËó‰.Oz nôèѲMÄùã?^¶íÇ0¢‰@ÎæDm=SAbTLÅ&1´/¦² ÅLßHË–-¥e~ôÑGË4‡zh4Q¯ò\a^:ÔœÈÿd&E´|Üêïºë.y¿{ì±G Õí¾{÷îîž{îq=ô{õÕWkœ“êoA¸óÎ;ã§¢É Ĥ:À¼­&n]W'Ñ~êœ41‰ G[Òª‰Q9%Z¹»ì²‹˜€:è ™ÖýüóÏ—4èÛà˜¶~! 6mÚHì;Òš4i’»è¢‹Ü÷ß/Iûÿ1)A`gu–{àä¼3f8¦z§" ô“0Hv»í¶sgœq†L×ÁyðÀejzŽ]pÁ’?޽óÎ;rZÞp“'Of·IŽPNT£ Q–’ic˜wÚi'÷Þ{ï‰Ù0L`šÜîqî ,@f:¸™ÊG Sϵµ! ÊŠ’—W2”¢µ?gÓÇ ?LIÌoÑêqÖý§Ÿ~ºieÞ¤+®¸Â¯K—.îƒ> éÀÈ4îÌv‹*èÒK/“˜ÿŸ¾¤]»v²æ#ÔиqãâûtƒÐCæ;ì DÆÿk®¹FÓ‡,!Ù¦M›ºË/¿\fØåà©§žê0e|òÉÒ2?öØcmæ]5‡5åíŠrƒF¯$&2màÐð཯¹æš)ï6qâDÑÀ˜\RÉK×h|&†@:(',>™¥»ÆŽ—œÌ‰T6jNd­&Åd$¦AÐ1gÎ!’‡~Xbá]{íµ¡[·nnÝu×ÑkN8áÑ~êŠøÿ‰RŽ 9e"Óˆ#¤„{Cš(‚G…·Æk¸Ã?ÜrÈ!Bˆ<d»þúëËd…8|üñÇ2éb:u2¹­B@ L=[•Èô4œ7 "p~æ™gDs×c©Öhbhý#Î#>á ‰°ÏÄH†åD5w-+ü7‰.9‘˜>Ž’™VFéH¬Y³fr)ÑÅíC£ïjРA' ÌAþ˜²úƒYýÿ‚F†©O/E „ä ZÚ×I'䔀´õv5mÚ4¹?y 6ßСCųã•W^)3¿ͳ¢Inø$¦eHSš>}ºëÑ£‡£ACƒg¹å–ÓC¯©|ÂFåD*ÌÒXèOŬ©ž’”a“ÜÀòÁ÷W•½– œ~ø–™‰›º€:‡ú‚ý”ÕÆÊá™r{k•sUÎ$¦•?k*tA2“ÎÓO?íÞzë-A•( ˜í(`é„}Ü£Ÿxâ ·ï¾ûÊ餃9²wïÞâiæ§qä‘GŠ©‘ûAhj²ä<ÝÐiù?øàƒâ À8$ÒGû£R}ä‘GĹrÃ$Iß‹Iöh_*˜  ~ýú‰x̘12þKe³ö Œ ˆ I+¬W\1ÞGš£œùfÍlîeç:ùžhx4nÜØí½÷ÞeAdÔC~™ @ÃJ+­$ž®”Žq‹–'{×ÑE gã‘´Me” ‰%‚–0ZЪ«®*Ä3xð`q°  ¡bƒ<î¿ÿ~1éùÿ!ª«¯¾Z\ª™øWzœ=h½ã8Zßë­·žkÔ¨‘»êª«jF;ƒÜèW!Zm‘ ѹ­54±LH¶Æ ìOÊŠ–Öï¿ÿ¾”D©O×÷O(Å•VVTJ>Qn!0Ê Z!¤J>Øo’Œ©Ã‹9ÝhL|ðÁ‚mv©ïlÕÄ´ÃwŒ6¦ ÿY8Nù¡!dDV¼÷“ËjEbþ ©r©D%†¹3H§®]»ŠƒÅüùóãuÔTéÿ§ŸêÑG•>.:þYhUÝpà ¢iùùc3á9çœ#dHŸl:>oD<iMò˜>!BÎPé·Ûf›m¤ÅvÀÈ€Ûpúö?3 Üæq¢¡á@C"æ¿¢ÒÊ mKMÈìc|­oö+©jžl9Ÿ~ú© Ñ#GÊ7H£”†CE I8eAIŒ2AÞÃÚXŸÅòôÿ*쬛ŸTB´`Ñ¢Ðnèc 3ž15xæd­ßy`p3­îd²xñbÉ;£‰F;_{íµ—:üᇊퟂo’T"ŒåC%þùç2¼‚ûjKb”=ʧ:i9¥ÿ ­ZÝûi©‰•¢Ìæ†`t®ÂëSûí·ß.š 8òNÙÞyçEÖZAc¡1ƒ6ÎB¡dÆ6e‘…c¾Fô-'Š@­41ýðYë¶&\ì5}\,é³e*ID^zþ&›l¢›¶®óæÍ“þÊŽ;º›o¾ÙÑ €Üò!ÚÒ¦’ÒÖ¶j~Zq)y¡…AvZvu|TCô?cÂW+ž­Ë.»LÌõhØ©”¥Âˆ2¢åC‰ ÍK‰‹µjbzn¾Êg©ž¹’ï[+ÿåê¶®+8{¶Ü µN¢­Paä›<(ZI‘KÒ×}TNª¥An,ÏwrG§|®Ä›˜@êm¬9gìåÅ_,æx)˜ê¢&”-#ÚØ¡l yAlºpŽI´șĨøðµÐÿ<®UÑ~é¥Ìå„þņÁD•~ùÉwž¨|´*QYQ9)y©‘óôÜ|磒ӛ;w®ëÙ³§8e…Ÿ“wLC…Ø“Œ­ô‡Í„Ï-öʃ.>‘iù`ÍÂ1Î3‰69“½V¼hþÛ öËŽJî0éRV Ê’Ïò£å‘Šˆm­ŒXÓÚVãž,&Ù#@_&cîj’ì»ÇÁŠa* °@xÜföwÍßšg¿|(qQNØF´|æïΖR¾È™Äxù~À-¹f’AÅxª{}¢tT‰Ë=cÄLÊ%,*Ý®mÙ #¡å“{@ZjRÓóùo’=³fÍ’€éú¼† "Á”‰|ƒù8 ¢åu¢…<EáM¥ÏCÎ$æ·¡2ïKûšg0L WÍ#ÿãã©_¿þß;l«,ðË f=D -_¤e‰Vµ–M­˜ô¾îUméÌœ9ÓuèÐ!-)}ùå—Òàdž6†¸3Š¢eE×QÌ£å)1µêµô[±©^>X -Å@fæ‚b@$¡¢Ú¶më4hP#/Qê_yå1S`²@ˆà@("„`¼¸Ó“&8SFTú-ZHšr’ýD2¡Ìèv¡3̽ü…ûÛ’;Ä&ôZCœfžzê)™¢yóænË-·tÄ)=ꨣ$˜Aøü¨üײQèrhéçœIŒ—ND´U«ÿÃÙüùçŸel­2·2µ £üÜL„  3ÞŒ€¯DÞ` 1îò´Ü·Å„‡LÑ‚!€±>¤IäyˆŽÏ!É&ÑG€²C™a±Ê#úï+œC`¤ñˆ¬`ذan¿ýö“åŒÃÌ8|øp™µ‚qt¦¡ibäZ›ýJHÉ,Q&‰–€}¢Û}÷Ý…¬ˆj ŠÁ:…iÅaKïܹ³hmþq¶ñx"MÌEgŸ}¶ëÕ«Wøûa(;&å‡!Ùvâ‰'ºÇ{L3\B=wÝuW9V~Of9.Gr&1}XmU³öÍ‹z\× ˆTMVAzUètgà©/Ú®º÷Þ{¯(¾½Î:ëĽˆpѨøñl#²¨FýíÈfØ2G€ÐLx&bM!X·ÿ-cN$tgbœÍ‰dN LÉ)U†Õe5Õ9þ1̈cÇŽ•¹¤tâJÿ8Û™Ü7|ý~ÙI׊FŽ-ŠK‚ecæ÷ Œãô“17_¸Qª×ÚÚÈ7µ"1?3©´0ÿ¼L·™zŸ}ö‘€¢LdÉÀJ“Ê@@Íκ®Œ§²§f ~)Þˆ&†@1¨‰ùf ZÓù5ðh"²=ý]DÆÆTaRþh¹A“6"+ÿ÷~úÇè+31Š@NQì׃¹@½5Š=&@Üç­b*Æ«+Ï{ÐØ¡“@ËÄÜ#zý™x³iÄðò|2˵"ÀIJ.À“ØÄ(4µÒÄ 9Kß0ÊvÚÉ}ðÁ2<¦üro9.7ŒÄÊíY~ ˆ#€·ñn»í&3=G<«–½ @ÀH¬^¢=‚!5¬_,jo¤róc$Vfï–¾H&#üᇖÊ9“=šQ@`ï½÷çëÂÛ¨ì,¸;×&êÿÔ©S…8ÃéÚC  ƒùâpÚ!Æ©‰!PHª’Ä h¡Ò&üÖâÅ‹ÝüùóÝo¼áˆCIÌI‚(ó?•‹òÁLuJÊc#GŽt/¼ðBÊsì !FÀLŠaDì!(‰>ªiÓ¦®Q£Fîꫯ–gy饗D«ÐÃ%m0`€fÀ3!¨ü¬ãÄþK@Q‚Ó\‡ ’¹ÔT yú f_0-BîàÆrÁ)ƒC`¸d2Ó¶Ÿ¶ma̤FÄþç¢a©ú÷ï/š“çÝqÇñþ?pp`š¬²Š‰2Up^€hМLÉšhD,™2áÔ˜¯í1g•¾/ô1­Çк¢Æ]­ˆý'Ô§O™;)Ñ~?MÝNe?“ýškZ³`é……hâ{ì±G|÷7ß|ßæy f_Àw Øö²b„ͤ£&†@m0« zvm&…Ä0ïaÂB‹ÁQ  ‚™œÑ 3Ì„¾`NCûÀm|Ò¤In—]vmG L€È§Ÿ~*ÓAPa‡…™f1µq*g´)ÕØô\':tè æMˆ l·b:D“< 6ÜÚÉ3‚‰ökz™¬“Eè‡Ä}ç ̈AŸ”¸Çó,BVR†dø¡ë¬×Ü›ôë‰c†¯™±­˜ùž0»²yä‘¢‰n´ÑF‚-&Hd̘1qF¦ÑQ-RÚ!!LžÉ7Ä7nb¢zxôуf@YàØ ¦?*U4&æ%ÒéÎõA19Å^ÍŠL{N…Š93ýV,$Ä®¥òÇó408KÔ8Iýúöí+ù:âˆ#Ä쉹 rÀŽéŒHåÿ7nœ4€ÔQ©òŸØž°˜Å;‘B bœX" “šZ†¯iè±å—_>Þ¥û’­“yöéùTÎJ`ìSc­'‘$ÛŸèÜ|îC; †!¤LrÝu×ÇQ­ÊÏ/D&/½€÷&0Ž%rÄÑklm¤BM k ]X8L |"Psb.f°­ïžKvÍ_ ×>DÃÄ(6̇W²E})6òÕq¿È6‹ 1“Ú#@ÚqÇWû„,C ¨—"ÎY&†@>ˆ¬&–χ´´ C ´(‰•6v÷JDÀH¬ߪ=“!1[‰CÞ°&†@>0Ë'š––!`$DÏc‚r?ñÄ ÛNC WŒÄrEή3 ¬0“bVpÙÉ"`$–!Pvš!`Ôf{f°¿‰!OŒÄò‰¦¥eI`€>c>ÓMâš4;`$@ÀH,(¶Ë0 ƒ€™ ƒk5§j$VÍoßžÝ(2FbE¼ nW+#|^'‰‰Iˆ0ùâ‹/¾(Û .t÷ÝwŸlÛOõ I™©4ªçI ¦MäŽ`&ïêyh{Ò‚"Ë$‡Dˆûí·åTf=z´l3‹2ÁiMªÈÌ­zÞ½Æþd<C ävJ+ÍQÑ‚K=¦k¢Î«ø“-î¿ÿþq MÛº² ¼PVÂ娲ŸÚžNP“"s šµE Vš˜’kÝ®m†ìúêA€¨æJfFhÕóޕĪç‰íI ‰@Nš˜Ÿ!¿òÑm]ûçÙ¶! hù`n2¶«½¤Ï¯kÅ)ü_÷—ûš™Ò™³Ž™Ôý)Êý¹òýN4]þ‡÷é±j^çLbZùÐ’æcÓÿ€Y©_5”|=»~„”-?ùJ¿œÒáÙÿÛÑm]—Ó³¥Ëë{ìá¦NêŽ:ê¨t§VÝq¾ýNxxýÏúŸÿüËx¦ûªœœ3‰ña(kÕu‚{ä}?‹M°—wh‹’ ~€¼Cý(‹Y~Šò)n³ê¢e9¼Öã¬]§H¶¬µiÓÆ=òÈ#®[·ne•ïBf–ïÑïƒ5߇®±\è7£ß^SÈ|E=íœI ðü‹ÿ¹úÞ{ï¹wÞyÇáàAzÆ s'œp‚#`hX6Ø`)ø˜!†.ÛásRý§å×0˜©yË-·Luš+0¼gÊŠßÊ¥ì8›Kžç÷Ië?þp«®ºjÁîå„×Zk­(g/RyûñÇ¥^Ôz׈ì¯×“³c‡©o™2¼O¥Zãb!!¤qê©§:ߓѿvÖ¬Y®I“&þ®¬¶GŽi³Ëf…XáNöËËÿû_¹Q.å§p9,LÊ<#‹’Øþó‰!zÌÖkª†Å_XP>('”¾ÿâxµJÎ$`€¨’®%é ]»vÒ‘{øá‡KÇ.XŸ>}ätÊ)§¸oÛ¶­{ÿý÷eÿW\án½õVÍ–»üòËݘ1cÜСCÝŒ3Ü€Üý÷ß/Ço¹åÑÊÐÌn¸á†ø5¶QX¨˜ÔL¢Û…½ctRçyµòQûí·ß’fpŸ}ö©a^Úl³ÍÜùçŸï~øá‡¤×ØÊDà×_ƒÌ´ Uæ“f÷T9“˜šƒ¸&¢ê­üñ~8~Î9縳Ï>;©ãúë¯w…˜¶Ûn;wá…:ö!Ί+®èæÍ›'$׳gO1åCGúCH˜"ž|òIYCŒÈ‚ Ü_|!ÛúÑ¢E®{÷înûí·S%Q ²Áƒ»k¯½ÖMœ8Q´ÁqãÆÅ¯³Â!@Ù¡L°°®T¸œ”&e* †К†È¨œÒ ßÇ¥—^ê5jäh¨ñ˜T”ȴþ­.4þ~ÚœILó+!Ý÷wòm>&ÃýöÛÏ­¾úênÓM7usçÎuË-·œ[guÜJ+­äÖ]wݸ»-ä¦Û™gžéN>ùdW§NÉ®¶ÚjnРAw]ÈoΜ9²J&uëÖ•ÚõêÕ“´ˆrÒI'9Ƭl½õÖî´ÓNs„Æ2)úŽ‹wÇÒßI¿*" -Lf©rwÆg¸¾}ûJãk…VpÚà¢Ñ†Å¢~ýún—]vqÓ§O—d^{í5·ÓN;9 `áÀÒÀw8pà@·ÕV[IÃñ‚ .ˆ7@úé'׫W/Ig½õÖsÇsŒûùçŸ%­›nºIÒ?~¼4¹ÏäÉ“Se׎Ê å…rCCHMЏUY%™3‰éSRña²ØD‚V…µùæ›» ¸|0Ñi ÷µhÑ"á~>PõNÄá­ì»ï¾[ê\^t"ùàƒ„!5>hí˜è|Û—_´Ìª¿ß»D+5ž“EyûD–iN1ÁS‘‘ß ÃiÓ¦Ic Sû¾ûîëp–‚”ˆQH𡇒oB»è¢‹ÜAäš6m*¦v5­C`hw|£ ç§Q£F¹ãŽ;N²5þ|÷ÜsÏ ±Ñèœ={¶¤›ì{ÏôYì¼ì€ÄÐÄ 1-C¤@Y¨f©‰)%3#*°Ï<óŒ˜@~øa1õ¡õdÚ Ovƒ%U /È §0™ÒRM$hrxBB\,˜ ÉŸIqðËNø'¥¹‹啊ˆJ‰%` ßd“M\óæÍåTˆ2an.ú“o¾ùfG/w×]wÅ“ÃêñÖ[o¹Þ½{KŸ0X¯±Æ®_¿~¢ÕѨ¤ì£YÑgýÔSOIuË–-Ý„ j4 ÇŽ+ ÐÓO?]®yõÕWã÷±Â#€&­¦Z˜5$‚n¬|AŸ LZÛn»­|„|¸ôAiëmŠ—ƒðAˆ™˜W^ýuÇB:xò2Ž‚´@ù˜iAâ4¢Âq½ýbäƒóXŽ<òHwÍ5×è©¶. úîu]À[E2iž[IŒ5S:YýõEC¢ˆÉð¼óΓòÍuô‘a’gædä£>’5? ]iܸ±ü?öØcÝšk®éÎ:ë,Ѹ®»î:1éc•@Z·n-k~°tðMûi)bšG2ùNåDûÉ ”%1ÞMµ~?a0s'FB€ñ º–?¡Ÿ.]ºA`ÄÎŽé‡ LxÝž‘û·Ýv›˜EpôPoÃPRñ¿Íš5s¤Kç’%Kdô?;wî,}d “ýl•J‡ij‹þ¼";uê$åHvĈzª­ ˆ€–,Õö!ò¼úÌå—%0]QxL•’ SþLDŠWbòû’±TÐwŒe“þ=÷Ü#U ¢×øÛþ=ÓY\$û)˜j`ºÖ²T°›–AÂ9‘˜§kžKFd˜/Þ}÷]÷á‡JŸÚæ ;è¤þì³Ïâ­Q$ã>Ì4æœï ç!8bèàh¿¦ýc˜\èÀɃj_°ñvØaâùˆVöì³ÏÆïÙŤ8ø•!eHË‘®‹“‹ÒÝ…ç¤ÒgÏ„ÄåÍ’‚àèãÛ¡!†e‚†ZXpdb ,ýû÷—¾´å—_Þá¶¿Å[Hð†¢ÌŠ4&17šDÊ –,-;ÑÈUés‘‰åšmFÅP@t?BŒ™¤‰'cX¨$æçÐJUáz#/EÃÖÅF@É,•)>Už#I¿‡˜Ó†£pÀ õã¼Ä$´˜¹7Ö Æœñ½ÜqÇîàƒ–!/¤Ãw£G²†)瘌÷¦¦D¶M*"k$h ÐZÃ&Žyp(8W|ýèKñä@IDATõ×ò!䤽‹*A€JñÞ{ï•Aê˜ÃhÌаÀƒÍ ¹R…ï‚ï†~Yýn¾ÿþ{q, ¾6ß LheX=Ò ß) úÂýéƒÔæx#0Ònó.Рñ¤æ»¡¡ÏwC_(ßoÝ(mN‹÷¢jbÅ<»£!] \µ°ðSe3¥I˜¼4-*JßZ¢ûm ´¼°f1ù V3T ¨è&†@±ð+!ÓxŠ~yÞrb¶ô»+)‰½üòËq÷ߥ³Vø= ÅC1‘!8‹¹M;Ès¹Þ®©\üŠˆrÂÕÆØ¶Å0HTø"(/˜ 9®ÿÙWíbæÄ$%€Xu¹Ú™©”ˆ ÉØ3ßa%É­lw!àW:”/ÊŠ–3ÿXAbšå„2^2¸¼¢O)š&F4 BÚl¸á†RÁë cÝ‹/¾XÎÁµ÷²Ë.‹š9s¦ëر£tDrÈ!ñÞÉöÇ/üß pA&Jiã>¬B´„DÑð‰÷ÀÈitÂK޼ï¶Ûn2ÖF¯Oô\áhüz®­ %**%¤L l ìPŽ”Ø²½¾ÒÎ/ ‰}òÉ'®k׮®r?þ¸ÿ O}òÊ+¯È8Âßà…CÐQ‚˜"„Õa@'5i¹¼7Õ~9èýpoÆ–Áƒ ª ˆÖ`ÁÉ¢á3þL§}a é·ß~+yge¼1“=W8¿—Û¬b¨t´¢2 ¬Š C® ½”²„„÷ëñjYÅœHàÑöíÛÇûŸè/‚P|AK"æãS Âì0a&s}ÛÁÒLiNTÅ‹‹«r¢ý„"€i^4ÁKéÿ›±0Ü /-†Ï`iÈmK Ž4pf,Ñ nÇ %FK!±DÏ¥žb~4~Ò21@@͈Ú–ªÂAɱ`4¤EP_„9úˆ¶[<ßa¤ˆ°1dÈÑò˜)‚oˆë)×—\r‰ ¸/±Ê2×!̨ŽÜ$ºhYñM‹JfÑÍuasVM ¢òWá‚L|aœ± ‰’MÜ6%ú”øÐSÅøbÇ1Ö&Ù~>hÒgÁLˆF…Ó\ ¹@€Hºhø¤GeCVxOàÕ/¿üRˆ8ÝsùÏhÛÕ€V@ÚFå£K2dð˼w£GkåŸñ™¤•hŽ>ÒÁ‚€9ž Á|S hÆzÁ€f¦Q!’=bsê eó£e…w¯Kµ/¯($FEïOTI\Dâ·ùÂG‡ÖÃ1´.æ CˆF`Rö£QÑ—FÜÃdûÑÜèk`áÌĽµÕKš¤y!É¢áËÁàM - 2„øXuâ‰' 1§{.MÇÖ†Pùd[ñ …a'lá¤øøÍѧ(c2ïÖ­›Dï aHy¥!Æä±LÕ‚ØœzŠVy¬ý:Ì'´òÈ}árY邘1¿A.´&Ñd|!Ò=zј ‹Ó!áqˆ ±Ñ¿…¹1Ù~?M¶5ú÷¤I“ä­T*Ø,¾¦Ó(˜I—4tz ®‡!ÂdÏECÝ·(ߊ¢­¿òQ2Óµž^û!ÔˆÒ¥›£9öTˆè@ã!" @9˪…ÁæÔX"ûC9ÑF®Ó•È>L3V”>1ŒRácÖ#²ävØA4'µý'ÚŸœ9Ž8â1±|þùçbNáÃF’EÃ×tÈ ýpDÌǤ ‰ažá:$ÑsA`Ì­Ñøé›31* Ê-k¶YR å),:G=¢l0×µ*‰®ÑcºÆÊp饗ºîÝ»Ë.ú“­á¥èDoM9Ñ÷ª„½\?GE!1 Ó­>>^>„(ÜhES¬ï¹çžQ#«EŒ0®eþ$Z ~T²ý’ ÷CߤBT|úÃ40‘ð“EÃ÷+Üê œŠö™úÓS$z.níGã÷²b›I@3ÇÌÅŽô92'³  WšPA`| TJ~YËôYSÍÑ—i:§ E„q||§&ÑC@ËæÌ´°¿(‰q;ÿH_Bx‰D Œc˜9Tž‰$Ùþð¹hCÚÏ>FaàãUaÙO„˜B£b  Nåžk âËÜ_„ÑAR¥!'ØO$ ‹Ø€Dnɇk=Þ¡ j'ü˜«àÁ1e≷)næ>úh$ñ SuêÔqK–,Y*4ã. Ctw eÉDˆ¹H(+C œ(Š9Q£½3“NdÌF:øØÝÄ80åvóÍ7Ëx,¦]ÁÜÑùQî©ì05ÑçÁ}é;¡BJ•F9½˜jÎk1†&PÆ(ƒ” 33A9 Ä4mÚ´„Y&è/ƒŸibT:EÑÄü(öôO =©Wcî¹ç™/,U,7Hú èó´ÔùXqf cÊhµ¦J£Ò_¦=_vµ…ðD­ˆ²ü—>_€•ƒ çž{®d{ذaŽ…!Ýÿ5kÖ,™Âˆ0)Áž4ŒM_¡î'Ôu <¢Ñ¨ÐÏx衇ÊwFÂÆ!DÕÑlügÚ$b:š¥B ($†éÆ´áG±çÃBÛBRÅr£ã>Q”û0p©ÒŸkÿ 08 1½I¦ýHáë ñÿÞ{ïu,½{÷Ó(cÃc÷òË/Ëíˆ&Cdz† wÜq2 ^›D¼Aˆ‚Â4.>ø $‡Ü~ûíŽùû ‰^MR$D÷ aá Ã&Mš8¥*l'3kê9¶6 ‰@QH Ë$Ú;ÚF‹çúÒdQîÃà¤J#|®ý7Âà®OC†J; B¬N~gžy¦ÄãÔoÂÏ[«V­d><‚Sãð‚'&$„0~Œ¹òˆ„ê|{ôž~úé2D™Ë1É«`ª§Ÿ GÓ¦Mý×ôÍ1…m˜íœaÓ§OmV¯³µ!PlŠBbÉ¢½‡Vc¹abd!–Ú’,Ê=Çè##Ê’* 9Á~ 4“ '1ÉEAð®TÁ“YÂÒ¢E‹ð®øâ}j˜+Lïú­àµóÎ;ÇÏÃCT…0` S@»ƒÐU˜\sÛm·•ê VGãˇަokC [ŠBb~ûÆ;ú"ˆb̘=˜þ¤Aƒ²Ã‚9ƒhôÌFDzrOü=Dúõëçe”*ðýì¿!æcì•tÄŸóŽa:^¦yKÙ„°^~?˜ãÆÕþ°Ã“oì믿v=öX[·Ó$NY8_™¥D (Þ‰<`¢hï~{ÎIË-U”{úøèè7ヵxp iRÐèY¢ K¦‘öꫯʠp43£p!aÕ¶ÂÇüÿXHèÓÂŒÈ|}8o@P q 1Éq48îï &Å«®ºJvar41J‰@Q41}@HJ§aÑ}áµÆrKÏ ’Jåžq@ÚâL•Fø~ö߈:xbÍ ß‹q\é"pý^ eI%={ö_¾5”Ú'† ±W¯^2ó%äIšgŸ}¶$Ç7ˆ¥Oa3%¦BØŽ¢ibÅx»‡!eh\± þ:•‰FöÇÉ òÒF &Eú‰‘ÁƒËZðø%ú=Ñ>è{ã7ô“šéßÂs‘ÁјðýÐ]Œ£ãž\c&FL÷* s9üðÃõ¯­‹„€–¡ðºH·ämŠª‰EË”!*mÆ@1ye¾E òÊ4}< •À2ÉϲË.+”î\LhT>é5ÜS#ãcå`(ÌG}äˆì¦Ç;“â! å…uª†Oñr;•%‰ñó1އ4¬0D£ F-¸Ž3p^½úò?¿Ü)©åû…HÍÏá§Ÿ~ÚA”&ÅC€rB¹ñËNñîÝ;• ‰å.ô¸Ïž=;Õ)v¬J ’nÛ¶mÞŸÞ¯ˆ¨Œø¯ÚÛQ_ÐÚðfŽ·¨çµ’òGA¤¼Ð¨â¹ô¿šseÓ'fñà¤ÜÚO‘7nœ›2eJÞïæW:TH˜j{þ±¼ßØ,k .Ê e$¼”õƒå!óEÑÄ^ýu‰ÃF4žP¸èâ"LK/'ÐÌó$Š@O6­¿LâÁQ)0žŒžxN 80®~®S§NâéÅø3ú=L 0‹-—võæ[”¨¨”¬üåÝêH²C9Rb«Ž§Nþ”E!1BÔä—I  )µiÓF"0Y!nÃÄ‚ÓôÌE§:cËh3(óÔSO•èéâÁ‘ƒ31Ò¯ÁõcÆŒ˜’ƒ„Ã8=—;RÔ¯_?­{z.¸PéhD%dX.(Vï5ÚR(KHx¿¯–uÑ̉D˜Ç«‰—A¤LÏ¢ÞMhMD ð#Ðóws÷ÝwKtn?…5éNÇÄ0A&×á>Œa›Èßݺus3gΔy¤p&ž£’›œh?†@‘ Ìb1о°j¯„Š{EÜFË eˆm%³ŠxÀ¢h$†».À#Ë/¿¼#P© n¾x x•i篼òJ9ľd²“ŃƒàvØaMZ´8Bç09 Ä!*Ô&†@±Ð HûÁT3«öJ¨Xø—û}´¼(yýõF‹bNäVM#UAÊ&}²ô Gzªh¬9L’ô‰i?ë?þXO³µ! ¡—¤ËÔ&…«x …lå§«š;OêZå?yê',‰¥ÎÆ_GSE Ï&±ÞËB…Á̽&TQ tRAúßè‡31¶ß~û‚†Rò+ÕÀt­ù°µ!ʉ6‚tmeǹH‘Xªô™Æƒ#Â=îøxAÒïE$‚½öÚKÊ^‹Ì›D:*,Ò41@“3ž±”ÁB –ZÖl³˜é œh—ŒZºkªáø?`²þ‚0ÅÑ_Ťxx›9ލpŸÏ!É8Ö\Ë̵H80÷Ôxpñ l?Epþ ïÌòŠ—$SÂX+ÆG¦8Û`Î,ÅôMšwDðfLÑ“ÌL\èÜ=ZHÖB e›2Œ•`ñâÅb à»aXœœjóÝ*Ï–n4à»Ác›àÌ„ÿZ}õÕ¥_Ÿxš|7JnÑÈmqsQ4ÇŽL‹—&/½–07™„º¡"LæB¿[61èôÞ¶®l˜€µXYQéh#ªTÄ]¬çµûäÊŒ¿hùÉOêå›J¤Ì‰å £åÜÈ*] 0ú{ó)~Ôú|¦ki•Ê ‹‘WÍ÷߯§fÚöÏ0BhÄšV5¦š˜ ]b A@Ë å…²còFbVª+ºvíêŽ?þx×¾}û¢á ¦-kú4èï`Feúlñš¥¿™Ù™ÿüóOY2í/#Pö&›l'ÆB<‘ì™’åÍ7ßtlwïÞ½·‰lšDú!”ÑÂ@0dÖ:mMm2®DÅXZær£¿˜t×Xc éû⸚­ÑóÒFbµ)qvmY#0lØ0!Žb€i%‰Q)Ab8¶@8%AX£ÿc_"á¼ HüQ=ÎB÷bü%³L3¾’á*Ä>}å•WÄÉ ÷Œjš„ÌDÒq…¼WÉÀ†ñª¼Ûl…4¸£Lน±Í>Ê åBBœ_í’=ÊÕŽ˜=E €Gà€ܳÏ>[´ç¡âÑEIŠJ‰Ê‰Ê ‚,¨˜Ø‡'n2cvæ‡~X*ÌfÍšì𢄨Ð>È+ƒÁñ¤¤Re( ^¦Õ&=zôp#FŒ÷Ã;R™D "¨³`xàº;ãµ’D¥å£|(‰Ar,”!ÓÆÊh*–ŒKhd€ƒào¾ùæ¤^¬$‘Ó)~ Z[ÜT‚J`TL˜©ÐÊpÇg¿Ž'ÓàzÆŒB&…мÐüpaÖ†»´lÙÒwÜq54>ò‚É“Pn¸}W›ðÌ̈AC‚Ɔ/¼×Ã?\ˆÞߟɶObeìȚ† ÷à\me’v¥žcšX¥¾Y{®”hÀè”'à ’•äD%…ö…ø¦Zç„IŒ(ûÌÖ€6”O¡Ÿë¹çž‚Ä,†™•Y#Ð ýwLWƒ6VB_ ý‚½š|y‡‡z¨kذaN@L>‘AX”5-²†ÜT 33M,§‚fµA€JŠJ"C|ƒ0T3SíŒûþØIZù…ÒÝe—]„ Ó–Nn@ßO½zõÒZ±Ç¯¸â wØa‡‰„CðñÉ“'‹ƒÎ 'œ´ %%ʆ–Ò¥\øæDŸÈ’¥U-ûM«–7mÏ TƒÈ¨ˆZÚü§²òMˆDó¸êª«Ü_|án½õÖ‚çÚ´iŽAßxJ^|ñÅ)+`ˆ‡†¦M›æ]#,øƒæñx ^~ùåîÌ3Ï &øå2ǹçžëÆŽ›õÝ( º@fºP>Øf­åˆuµ‹‘Xµ—€*z~¦úiݺµ8)à] ¡Òhe#™VH˜¤¨ XÏ™3ÇõêÕKœ¨$óm:LôìD²¡¯ðì³Ïv8.Œ5*i0d ‡L›Õ.ûï¿¿˜|‰ÕÊ”Sž¯8¾äHËéPNtQ2ƒàØÇy,Õ.µ"10¼N*“[¶k×NLÏ?ÿ¼|¼tó1ðÁæâÉ“è>¶¯<ȤÌäûIèëׯŸŒëÉwÚÙ¤G%D?•„¥ÿÙ†àXðd¦4b žp·ß~»˜÷Ûo?!²]wÝu©, 2óMœëÙ³çR0àŠïËã?.3jhÙ÷ùÛz\µ1þû¤åoû×Uëv^H,ðˆ.ÏKÄ~ΠδP 1Z+ 65ËÅÊ;‡T?Z]â)™ñ?fúŽ‚PAùÙ’%KĬ¨y;ÿüóe“sJ!gu–Ìò€“}eôýø‚‹?³ACCôï[ÎÛ˜k¯¿þzi $‹íÊóéw kö)¡éqÿûªYr"±Dr¢ý .ˆÊwÞ©›Uœ1;&ÕƒeIU^ò‰QãÑ}ôQ© ò™vmÒâùYyä×»woñró1)é3áXB«ÿÉ'Ÿ”¾2úåpóFìÛ¤I1êù¶NãʆîöØcÞѹsç¤øå€“Âÿ“^X…j=Ü[É µrªB푳@€râ—ÊN!?R'Ð(è„’VаW§œrŠ8nøß’~OŠU±×T¯ OnÊ”)b-¡¯ŽþD"Tú!±bç«Üï‡%€¨'xv¦zý&t¥rµ¼ä¤‰ñ ./‚Ö 6~=&öc$@€r¢å…5e©¢ RƒôÛ‰ÌxÀ¸þýûKÞÈÓ˜1c¤r,)¸ý3'ŸIöàüæíË·ß~+ó„ùûl;3rš“NiL ´$Yˆ!Fx^nÁ2eÁÎϨ~Ì9¸†mf–E;KK5ç辨®!'ZïŒm¡ß„ ¦ôƒâÑÆ¤~hEü×HhKxêÑ(2)-]ºt‘wC?bXÓÅÄ·L,ËwÍû5©=D>4hx…Ö>µêJ!gM ˜¨¨¨thE²P ut9¤…pœÊ ÒÃûÊ$7ˆÇƌȇrHäÝšUK‡””Äè“‚Ì(ìã˜jc”½&7tìª|!À¸´ùAü¿d1% =wî\Ðk–/Ô m`Œï}÷Ý——ˆøùË]´SÊI£ÃR"4­1ÕÆÐÈ|I8L,ç„%…Qß¾}]x€nomɵF gs"„qAPTª æCÆ„é%0ÓÂjý®ÜÉ'Ÿ, ð¸îÂRbQm Â"¿®.üWm 2ƒôLŠïþC ÜÈIÓV³šyhHM+.>ˆM Ťvl³Í65HŒ†´¢™.=J¢eAËd¦Z»¿f›s´LEéª!/×]w{ûí·¡àL rD 'Ó¥â¡кVÓäÅ51*éZ¯·uv@bx.aþA” „ {TD Iɉ5 ù¥Œøk%;½&*ÏP›|`À|uyúé§ÝàÁƒ%vi9ä7êxZþJƒ@Î$¦•“þ×J ÂâX˜¸Øg’€ íÒ“‡FÎÎ-ÕÂ\¥åAט–Ö>Áé9érÒ¢E ‰‘î<ŽQâÝwßuûî»o&§çíœË.»L¼ûhlDYˆ}H´ñãÇ/5cs”ómy3ÂäLb$DåCe¤Â6¤EE Y…=ÏÖ¹!@%Žc³é2³ïyç'ñO<ñÄÜ,ÂU”‰pᶺO×™dÍ!SY°`›9sfÑI ‡›Dƒ„3Íw1ΣϚˆçœsŽÛ}÷Ý‹qK»‡!P0rrìH”Õ°ÂëDçÚ¾Ü`:tfÓEÐ4˜&ãÕW_uL'e k[Ù—>‘"pgÆ¢~úè´ÓNstc2ÂY³fI$Â%ÎéwÞMBcæI |´³DiL:Õ 2D_ úeæòzê©§Ü:ë¬#ä„Ç­ ³20oæCz·nÝ$¢Å 7Ü §EnÝ+ƒ‘¡…™e@@:&eŒÀ„ bW_}u?AæYoÞ¼¹œ¼þúëÇbŠaÍbÍš5‹}°±@;sìµ×^‹Ñ&bAäõX0³®œ8/Ä A,˜-!Ö¶mÛ„i°³]»v±`ž,I{àÀ±`>¯X@br~Ðw»öÚke[-&hƱ` zlŸ}ö‘Ý{íµW,ð¯§Dnn,( D.o–¡Ò ô±—æÆyºk­Ì‰eÏàðôkT›0›0±æ4<úبºÓN;9f×%݇~‡…xhkŒ³#þ$Nã½÷ÞmKg*îÓ§hmé ˆQ¢Ï0ÿ Z0Ú/i²à)J¼Ð(ËsÏ='A}_xá…²‰ÁeN„ Ô˜_~ùe™-YûÃi@Š„öB¶0qâD‡# æZÆQa²%-• 8f7FèÆ#7jS½h^YóL˜c‰@¿ñÆû‡l»Иbþ°L…卨$F¤Láå,Fbåüö,ï‚à î¾ûn™\’™˜Ÿ«aÆò¿{÷îníµ×­Š¡ ó9……"ÚWFÌO´9Ò8ꨣÄyoGî¡‚K:N8H0”ä¸ãŽÓC‘[£%ÒHÿáž{î¹üUb†>úè#7jÔ()?—\r‰ ‹Á på•WJ_ë 'œ sŠ1'A èë1c†L˜É°‡Ù³g‹…>WÞ_8 Xô&o^C£‹¸µxÇÒ`ëØ±£»è¢‹j@K?-ÞÌÄ-EK$V& 1òyôÑG×8·¬þäÉ,iÉ”`:ûX0 N‰sQÚÛӃĂAöñL±€`ä?Çìÿþ9²Óû¡¯ˆþ3_wtéwó÷év &=¦ç”z}ì±ÇÆsj©³QU÷J± ˜rŒµö×$ ;± Àr,h\ “'O–~Ø~ýúÅÇ"é×¥ß M±€ˆ¦˜¹cA9?˜¸T®=ãŒ3b©8^¬S§N±W^yEÒá‡óƒ®ùÚXР‘cÁüpñsÊuÃ4±²jr$ÏìÃ?ÉÙ‹“ç8ÿGÁå Þ‚*z,Ý ÞDóµ¥ ˆKdþ(Ë­·Þ*Ӫ̙3'ÊÙ¬è¼1$ÏXLÖô›2T†€ÞS’hójúúw1ýÒ‡‹æ†Æ…„ÓÀ3vï½÷–t‰ØƒŽLq“8Ö5sŒ>·:ÈЖwÞÙ„ÇyUÄãTïCPùRpM E“Ô…^(&'œOLJƒÄä !â0S¿øâ‹bŠ~ä‘G⇧OŸ. š˜~uhJ8 úÎÔyéÑGu7® ½ahÈ]wÝ%&Jº&†å0$Á) ­1M¬Þ$-6> Zx&†Ð ïܹ³»ýöÛ]ãÆ ”!@„&·EË‚PS¢4@éÏÂñrÉïšãœ&'EËf\bÐ áè°<зEŸN"Ì;¨Òºuk™« '%¡.½ôR=Töë¼ v.{$Êøp,Ðx•eü–õÓÄ|4l;ÒÐé'ë°àÜâ»!3.†˜p鯅…Ó)Çÿ8õ´oß^BdU’×Y9¾ Ësñ0+>ævÇ`< æ2BL1*ˆä/Ó  …T£0pWì‡z(>O[5â`Ï\˜cGu¾÷²|jŸÀ˜+,™ãZµj%q?üð²|¦Úfzܸqþ°pPãÚ¦m×WA8©ŠöR5M,‚å”I«Á VèÁˆP>Õ\qYŸPBŽeOC Œ@ß¾}]0×c°s¥JÕ8v0¿O=2~ô©ÐÚ/¶GmèСžmÙÝ’¯fÔJýáÇ•]é-|†ôLä{¢Öc­¨d©MŒ—ʔ쉜½`"0É¢oÂJt^¾÷ïì7ÞÈw²–^!€K0ƒ´c’Î`æé z2{”|!@ýÅä§LÑRi:ÂU&Æü> ˜Ì1E”h¢@s‚c>(æ\ ¦4qŸ}ö™ÌïC,3æòéß¿¿¨äÁôŽIä¤N`™ß§M›6mŠ`¼„9餓„ ¹wX˜ßs“Ì“Iôt~Ÿð¹ößP.¸àéã |š‰hÔ¨‘̃Wé&Ï^®sÈd›o›ß'[Äìü("ÀSÁ¬Ò±ÀœÅìYžJ„óàU«T¥wbxn›ß'Q[ÎöE f¤&º9ÓnÔ­[7jÙ³ü”ÂŒŸºùóç—(¥½mÕ˜}˜ýA±ì·ù}|tl;Š©„©5pú¡fbÐÕÑ«W/wÌ1Ǹ‘#GV- U©‰…ß¶ÍïFÄþG úl™7Š>ÛC=4JY³¼”<™;ì­·Þr+¯¼r sRÚ[Wwb&0Ûü>™ dç>}ú¸Y³f¹iÓ¦Õ­Uì|Túý‚>¥È?"Úã#‘o¾ùÆ­µÖZ%ËsTfI0+Y°é¸ï¾ûÜi§æ^~ùå’VXésÝ3‘“îÓutsÿwÎ-ÆØÕÿûßïŒÈV˜Ðüÿþv!²kæÄB jiy@àí·ß–ûÇÜ,G<}’Â,Ë‚°_?iÿ|©·ß{ï=·ÝvÛ¹gŸ}V†ý”:?Ü?9€€ýþ¢yMt¾«ÍÚH¬6èÙµ†@X¼x±8r 2Ä5oÞ¼@w©ìd•¤X+…× à[™>}ºD †W¸-¶ØÂýú믑ʦFÎQcí/—žS"3sb¤ŠƒeÆøKKÀ±aÆ‚,ÇáNt©S§NŽ)ÚeÙ"@Cl™e–‘E‰LÉ,Û´RošX*tì˜!Pˆîòý÷ß;úÃLrG"ƒ¼˜o5NûM ‹¤Åd´Š5Ú؇֛o"3+ì»´Ô ¬`N0æE{饗¤›ÕÅv² Ö 1*Tb§ši‚;B`,á1ºùÈ‘X>P´4 < @çý‘Gé~øa·Î:ëä!ÅêMBµ0ÕĨPYLЇÀ/¿ü"7ƒ¼TûÒu>sQ•;| L+ÍÄ(%K–,×éÁƒ»í·ß¾”Y©ˆ{Cb˜®|-L+ÕŠxÀ2xð¦á@ýJcÂ×ó™ýª×Ä6Þxã|âii9!pÄG¸¶mÛJ¡œ°‹j  ¦ö…aJ4«QÁÿàE‰cDzË.wðà½ä[ªžÄP {!쵚¾­ d0¥Ê¢E‹dî§dçØþÌPCSs¢õ‰eŽ_¾Î¤áÀäµ`O”ÞG!H¬ê͉úÂHøôÓOë_[EAàÑGuÆ s“'Oއ*Ê«à&T˜¾IQ½«àÑ#ñˆ¾)‘÷ $–o"3ûßëf†\¦3ØqÇÝ«¯¾‰B`™¨lèÅŒxï½÷:‚P›ä%0]Ó7f$–?|3I ÜY E`äÃHìocß}÷u„ùafgB»˜…D€™¿‰Ç˜°Ö­[òVU™6䥆IQÍŠU F‰òw%°BeÃúÄ[çž={ŠâñÇ/yþR.LJ:Χ0©.Õ0™îN–r%0öë{(D½b$F>ÁÿAƒ¹I“&I¿ÅI'äæÎë6Ûl³g–×.-P~Ó}´žý_^OíÜñã?vÄÄ‹º‰KÇõ(‰éö·£„¸–Y-×ä­ÐÚ@”ž?*ys]üw¡ï'_ù4ËÉ5×\ÓwÜq²`ç W<»ï¾»kÒ¤‰˜…0 m²É&¤ZÚS´ ù…L[NZàôXisZYwÇyèºë®ߨsU´•´t *„¥Û… !¤÷ÏÇZËxx´-è `$–å»`Ì‹/×\s›9s¦#d}ï¿ÿ~ü0n¦Â\{íµãûJ½¡µ’•)ök ŽZ¼7³õÖ[ïfy¼ÓO?ý$­kˆŒ…²U,-ƒvwÚi'™aû¼óÎË:é}öÙÇ=òÈ#ñë7nì:è éƒ_}õÕãûm#{ŒÄ²Çl©+š5kæX˜¼0,ô¥Q`!ˆ-Z8Hð‚,µ¨–¥¯ª]*Á%Ê3_Cà•Ú'˜è™mßß ‰QNÀª‘Qf8V‰òÃ?¸Ûn»Í1 ^Ë:t¨Õc^xá…nÅWtÏ<óŒ»âŠ+Ä+5Q½Q«›TÙÅæXàŽ)’3R÷îÝ—2½ñÆî‚ .®ÌÜšŠDò‘UÒ×MŒm‘%™Ü~ûíâ„°þúëKL¿]vÙÅ1¾I…qÊ”)ú7ã5¤H%H?cmäþûïw ‘0)<:ö³º6„ ]f ýT”_´,Æêò«eË–î±Ç“Û2›Àĉ3ŠöA¿ùE]$3$Ëógœáúöíë(³ 7nœœº`ÁñV­_¿¾ãû¢¿yíµ×$owÞy§;ðÀÝ 7Ü uÈÀÝV[m%eRøïàÆot›o¾¹[uÕU% u ‚÷5ißtÓM¢rÞØþµrb¹ý`RB¾ùæ›X`‚Œ¦…X+vøá‡×ÈÍW_} ܱkì«ÍŸ â‰ÚW,¨Œb–ûöÛoc .Œ}ðÁ¸&.•t@¬±À\kÕªU,Ï=ztl¥•VŠ­¼òʱà—ó"ŠuéÒe©kÓíøüóÏåž÷gºSSïÖ­[¼§¼ÈfåƒrBy¡Ü¦E)G”'ÊUT„ü}’Ç€bA% ‘R~•ñ ¿[ÊOt9Ö¦MÙ&Ƶ*óæÍ“ýçœsŽîZjÍ·Kú~øáRÇ:vì(Ǿþúk9öÊ+¯Èÿ€0cAC2˜—å»êÑ£Gl£6Šq¾½@c“óV[mµX Ç‚ŠÝrË-±@ód Ær<˜¶GÒ?~¼üß`ƒ b]»v–“XÐŽýøã±9sæÈ1öíºë®±€0åвT~k»î¹çžØSO={ýõ×cŸ|ò‰¼ ô—Ô?µMß¿Þ4±íR šZ¿~ýÄû1(ü.( 5²óüóÏ;ÎÁq$(”nÚ´i5Žçò'(ÒŠÆ”ˆyˆÖu²¸ro½õ–œ‹ý¾sçήW¯^b^9æ˜cÜwß}'ÿi‘c¹øâ‹Ås“Ví„ $kDfçÿ¨Q£ä?-\̪ÌPòÙOÖ夃>X̵}úôÏÐÃ;̪¤«‘V‚Šb)§›7°?µF€ñm”Ê Z|ƒhlAƒ£FH3îI>×!ÌšPÎb$±·‡½Üfø¥_ SÛao¶1cƈI“„‘‰ø$†YSb8]Mg›m¶‘>Ì$ûí·Ÿ˜"vØañ°#x²zbÒç·á†º Åç^xᇩ¡ÒãÿgŸ}&&úÖ[o=Gÿ€ DHúô^{íå>ýôSé?ÔgzñÅÝðáÃÏK%´8Ýõ×_/Û˜MÌ#•Ú7£8•zM9¡¼PnÔÍ»«!Z‚·1hœ²Îôgçw–,«¬²JÒK0É7 fì¦ “!N"óçÏ—ó1kV‹Ê"º¨ðíÓ·ŽÐ§NÃö¬³Î’2·+± œÊø&•Hi@ê~Ù~è¿GêÖ­+ëdß¾,ƒ#±2xIôaÿ¦O­S§N5rL!壡ã™ÁØ#FŒ¨qœѯd´ÕÌ>*!´1*¤d¢ºãŽ;-6ú¯øH³‡Ü-ï4bO¢¥´5îƒMþÉ'Ÿ”V¤žŸIk”ç Ì:Q…–çÔ©Sý(¯½öÚ¥¹9>Rb´;Ê>eu·Ýv“oGÉäì³Ï‚ˆX®¼òJÉ?¾ç0V™À4(Z_`ÆÍL„䙣öqcÝAüg¡>©$1+ó·É k:ŠéT¦åÆ¤Š¾\uÕU.°§»víÚ¹Àžï‚>¶¸ H‰ŒJI ½­n}N28a8$±Íš5KOIºVÒä>^„)G?:J&­QUÝ‘1¥$#^IÜ~ ‚€jaïÖ_ rÃ"&ÊðLx˜àH&¡A‡SH¶¦EH ‚ãZfö&dáïhHr N ýû÷w| Ë/¿¼d•o S$¦MbròýCZáÆo6ÏõsÄ¢þ†²ÌŸšô2L ˜æèK¢°3åäEåÃò{óÍ7“’æCú²ÐÏ<óLù0Hß÷¢÷Ãã áCB‚ŽlY󃹡ï ùòË/eÍO&­ÑJkAƾŒ6ÐÚUC“ײTF4«4öÈŒ ›†!}·Ù 1ú¢R5¥‡–Dÿ&@›h`‘ßÈó¯Ã‘>2,/|“5’.ÎÁLÉ‹²TIÂx0ú]ij?«>dÁ7“J°N°$Ês*a04 ßZ N—øð}qÁœ¨ DßTˆÉá.|_4Xù~µax×H‹cá´Så1ªÇL‹ê›)B¾¨„Ô£*Ü—¦·‡\p¦ µ‡s­;> lú ÜFèûŠ10´&>úhǘ7<© 4±SO=U<éßkÚ´©›}6­Ñø…Á䈣òªúÇm;„ LCù»CiSb ˜ÀŠ™£À½>N`éî yùæŸÏsð})ùÇ*mûA!Lݼ¨´'®òçQMŒÊžB„Ä—–.ô©Šž‡xBaQ/GõÌ"]4"¿CZ¯Ñ~¹`\Žîª±·FkLñ‡©Ú‚MqšÊÞ'ý,4TÀ ?/(%}2ªç˜|Þ.ƒh) ¡¼Rn‚æÂPpà¤aEŸVØŸ·LUYB”†Ð@²t`ª¥nÀûŒóÙ5`æÄ*+\þãò1ë‰iˆ‚È’HGÅ¢’ª5«î¿znxMk41˵쯡¬h¹Éþêè]&ÏbRž˜9±<ß[­sM%D˹’*£Zƒb dŒ-mÈŒ5‹‰!P*L+ò%¼/Ä¥•P˜Ä¬B*á‹)“[Sfü2T&Ù¶lV(FbúbS=–¯AZ>‘ùÛ©Ò°cÕ‡@¢N¢}Õ‡Œ=q)0sb)Ñ/Á½}’ò· Yý{ç|ÕôþñõûËÐ0tùÊ% ]EDEcHÒE5"]E”{¥h IR2ªé‚jD%%R$t1&º©äÚÅ¥é§"C¹Œ0þ1¿ÏëùÎs~«Ý9ŸÏ9ûÜö>çyföÙ·µ×^ë½×yÞëY—g1ï†Îs“ø#@9a£ìèF®òY~âšå Ÿ‰åÝÆí+ _ñøÇ¹N6@™c´âã—ŸÌâŸCËAÜ0‹ÛËqzU©r F_±Œ‚ëÓ§7nœLœdˆ2ká: ÿ…¸¡RÁ‹=Nxñ£ˆ'pdçÎâ-Uxé&>\cáì7RxE >dÚ´i²1‡L]ÿÈ û‰ ÁòBÓ´Oh‘I¨%¤l0+›O<£( Ý’…`ž ó=p…_9H©bÍ%ñÂ#_ü12ß ç©¸¡ÂÛ>õ¹9ATLp†ØpRJ|,ðW±–˜,¬ ââÁoª™¨É²3&ÑC@ËŠW© ·â–¢ª°U!Tb÷©I«"ò­/ÿ8˜e&,Oœ8Q†äã£b;Y–pL|ÆÝ_!9\Ù0±¯÷4#6kÖLVƒf‰‰“N:Éá_޹fÄÇ„G*°V5‰â?°i¢ÐúY¶¯d‰<â³t ÏöcÇŽ§ÂôsíØ±Ã 0@–VÏö]ö|ñÐJû(Zcª”•ÀP¨øödÐ}¶¿ýö›;tè8 Æ6ƒqżÿk'Oƒ/¸ÒÍ€3húÏéó¦G¿àN ›<¦ì®šûìð‹ÝÓ뉅ñb_Uf±¶ð®|ñV¯Î~Q¼—‚nOPDqðb±î‹Sª'{FÐB\4yãÕžU 1Ê,át4#±ÌÊ&V%1úÅ•È 16œrClêÁ²SbËìM©C›%–›’¼£RÈ ÑsÝg“i ±ñ(qLAg3‰?ZV(Cz¬û(åÒU%KÙcm-”,„Ez¹‡µ@ËH,ó/§Ö¤9©5Ælºì Øc©EÆs¹#±\¢iq†@$ð¬*Ym2„|õÊ–]ì3‰I"À˜M+ ˜V‚$F%±\gÏH,׈Z|†€!Pt”ÄÔÚ‚¸Øô:Ê–¹‹¾Æ}“ÌP ‹JV-ØjÓ"ÇÚ Ã·ÿ\‹‘X®µø C ¨ (•¬ÔJP‚Bé¢TµL­3šu+jâcörg%2ðÕæE%1%0ý6¹Ì¦‘X.Ñ´¸bÞö™*`«üÆú3Jâ}åÊJ¯©’¥ ‘¾0Î,ü7Wk ¢ÒJƒZ_ì!76kN ±=i¤…n²æÎk$–Zф҄œ%0U²Ú¦¦ûèç*Z)TËÊÇWÉ âÒã|h˜ÇŽh•‰H¦fåÊ•®iÓ¦2ÿ£{÷îâ ˜„âÿG¿=zô_‰8íeè>‚3ß:¸êÕ«‹ŸEõTã_¼âã[±}ûöî½÷Þ“ðézË_»v­6l˜>|¸«S§ŽÃ—ãÿVøcDðŒ?kÖ,9ægüøñnΜ9rþÁ¸N:¹Zµj¹ž={ÊpëDÀŠƒ ·}Îñ¦?uêTצMñé;%Þ²e‹ëׯ_"Š™3gº&MšÈ6eÊ”Äu;((UD•( «Àﳡ߆~16†€3ܶÌ076r€#˜jS¢o…ùd—ëa$–kDK,¾½{÷ŠGùI“&¹]»v °” Â=<Ó÷îÝÛ-X°ÀmÚ´I<Þs…ϲ,WýúõÝÀ¹,K­à‹qëÖ­2!šIÒÔ–Óõ–ÏDUˆ…9(Zƒ ^ð÷ìÙãöïß/ïá‡sõáÈ208&Ôº§OŸžÇAÐÛ>ó‹XÈsÉ’%îþûï—9EÛ·oO<Ã#ˆyã7d©0b2-k§ÍŸ??ÖŠƒ€™Z 68~ÛrƒAÀ¨D ú òQ¬O,¨–PœóæÍ‡¾¸”BÆŒãj×®-“E9ǪÁ:Cºuë&о¥ 6¸Õ«W‹r=z´ÔÖ «Î;‹g{jnXw„€P ®Ê[>áx?Ë· ¤p:\¯^=!&î%:ï™xOG–‘™={v"ýņcbj“,dˆUÉ245jÔpk֬ѠGì!ÄAƒ‰åÉÍ¡C‡ºE‹‰%wD`»PP”ÈP¦Ú´ˆBEô¼  *ƒ—)æºÏw–ÄòpÌãÇbË–-¹@ÑC@xæ@XŠE…ëX04ïA,ÔnšF%Çt¤Cv¬?‰ù’Ž·|³܋þAx'JI­"?>ú=š5hî»÷Þ{ŪbaOÖ-«JêÖ­+–,ùPùöÛoÝæÍ›Ý„ 䤉j-´Ìhª‚çzÝöñBÀšãõ½ žZ–]Ù·o_â½ôuA4¿!Z«M¨8@cù(‰ ÔY“ó¾}ûJ3"$¸bÅ ÿ1é¿8ìBŠ\©ðìÄ:C)ÑT¨Bs"ÂdÖÖ­[‹‡}ãjhäÈ‘,í}0n­Éã+ŽU©I VÜòåËӎ׆@xŒÄÂcWOvéÒÅ­ZµJÂ$Ãôµk×.a %×SXbøÙC\±lÙ2ñ[wðàAGœM•adÛ¶mŽ !ŽÆËÞ»qãFiÄ\¿~½„ÁOÞùçŸ/>óX"†þ:úÖ‚BŸI*oû¬@Íb Ä ™-\¸0ñ8ƒKè £ù‘>Cš9M C ÿXsbþ1Žõhºƒ´@Ѽys(±téÒ*óôøã‹Õ…Ńå‘Õ¬YÓÝT±Š3„¨E¬#<ÜWw®*ršùè‹Ã²ƒ4t}s¬ Í}š&!„÷ÒGÓg«V­ÜÜäÉ“xïmŸ'¾'ñA˜_vÙeBFÈc×®]}s¬f=cÆ ÿq;6 ´ÿgô Yèbbg€rÂFÙÑ 8âZ~üÿùÐsÝsÍ$;ü²‘ê8›7‰eƒ^ ŸõP> T !±$g€€*w¿ìpÍ¿–A”E ªù`¯ƒš4zî'LÃû×ì89Ár ç´!œë±žË?Fb!+•ÇôkÒRù¢ùÍG°¼ ŒTIå÷͹‹]ËºŽ²dšǺq_‰Ló›»·—OL>Yi9a42ÇàËž ɦ ‰•O™JšS nIØECÀC@ËŠ*”‘’‚,ò‡¤ÿ—¤c9“ € eII.L ŒÄ ãgøÓª"ò•ãìYÒ󌀒—_^´<åùÕ9‰žtC`ÇLlçñó”“—Y$G @Ys¾D†«7µÆŽœæ#±4*•`ª„ÈlàRùÂùÍå„M‰KË^ËïÛs; ” ï,Xc¸.3)xÍÑ.´ü°k‰îÛEæMüµðÄIùD@KHÊOœ„ô²a}Abÿüó‘X? $†þQ₼²)GFbþ€Qx…‡B¶æ…•%1ˆKÙ„}“i®°ÈÅü9Öª1éU)ÅI!ÅüÄ2ù_|ñ…«[±e†­ã*4%¢<Ù£PM ‡x³i¿$«] a-1#±Â}»È¼I-0ö ‰á® ,Õ—Yßë?þµ·è„Õ?|ØB™Œ[BªD@+6(ÊÆsÏ=—hꡌŒ7NâЦ ®mÞ¼Ù5mڴʸ£@-1”¨*Ò¨¤­Ò¡SØT§è>LþÄ óg‚V 먣ŽrG}´ôP¸È{~»u̳nɯÊßaУFr³fÍ’…FÉÇæ éÔSOeŸ*Š“² ‘™­ke‚}6b$– z1|V› ±ÂPTÔ¶©MC`((þÔ÷!6­5Ù=†;D’µ‚C™ ‘1Â=ñÄŽU­µ‚CÔ”áÃ‡ÇÆÛ…ÖöQšëÞÊvˆ‚’Å#”!%0¢á[è· ­‘XÔbþ ŠJ›QV(# –*(­…+©q=Û‚sÈÊ*ùj‰)‘QÁ™rŸ~ú©kÑ¢Eì0Ò2M>L ‡¸kBÉK÷aRa$µ˜?㓵m-Pd ÅEíÛ'0îÛ=æ=ƒäS>ü¾R*9” úL{öìé<(VÚ Aƒ\:u\·nÝ\«V­ÜâÅ‹³šï“ACUeµò¼ÿ~ùïÕ¬Y3tÞâþ ~›Lóa$–)b%%Em›?2¤¥h¬3­}«eFS ÷ð€«ì² •¿¹™rQ«V-·|ùr×¶m[wèÐ!iJDéÞzë­nÆŒrí¸ãŽ++¼V­Zåî¼óN·dÉwÎ9çdœ÷_|Ñ=óÌ3nË–-Ò¯xñÅ»‡zÈuìØQâzòÉ']Æ ¥¢iä—^z©Û¹s§ûé§Ÿ2}4þ믿–ÊÉ5×\ã5j”¸¥#±(}¤…(‘ANÔ´õœ8ç—6#‰àÃDè”-~³3eãÌ3Ït»wï–òaqŸæÆ{î¹ç°¬[·Î1ʵS§N‘·ÎKxš'Λo¾)ä I„™0 A 8Ð]pÁîÕW_•ÁX·×]wÛ·oŸ;þøãÝÈ‘#ݵ×^ŠÄøßj5ÍllÛ¶mîÁtgu–‘ØèØ…¢"€òÀPL«Ò¢Æ y)qåâPÔŒÚË3F@IŒ=e‹Œ½61jŸ{ | #!¶Áƒ»»îºË :Tš ƒá¢~þú믻™3g:”y›6mÜèÑ£…Èé\±b…´\T•‡1cƸO<ÑÝqLJݾ}»üÏ ©îÝ»Ë=*6lp¿üò‹6l˜ÜÿðÃÝ#<âºtéâî¾ûnÁ²wïÞìß¿¿»å–[„ üñGwûí·KåÕW_è»$blMÂ4 _ýõbñqÒ<å”Sd€dzá…ºG}TÞýØcDöT¯^Ý]qÅr¥³Ä¢ô5 ”æ+!UVýdÜ7+Љàk”œ”È´¢£åDïsžLN?ýt·iÓ&i&›6mšX-(Ë8 VŸ>}\‡ÜM7Ýä¦N*ÿ‰ùóç q@$U™0²“ytA;÷Üs¥bððûµk׺«®ºJ¶^½zItX½È±Ç+}¿ÿþ»Û¸q£ôKrýÏ?ÿ”smz¤¢€uH¼Ë–-sß}÷ƒxÒÀ?òˆc¦N@˜Œ.U«ò?ÿù«[1‘¢«Q£†»ï¾û„Ü>ùäÙGur»‘˜|âòûQ"SåiqÌÀ=—û);”¤’‘–Þ« jõôû 8} ºK.¹ÄµnÝZš°ýûQ8¦I”tc¹ üiýõ×_3NÚ /¼ MƒÁÏ8ã 7oÞ<™Â@_#˜b%Í;Wˆ‚<ï¼ó„4W¯^Œ"qÎÿ•ûíÚµs+W®”&_%A1ׯ~ýúË’°4>ÿüóBbÜgQÐ/¿üRÒ ‘½ýöÛBÚ po½õ–X||Ë(Š‘X¿JÒ„bBtï–\ äØk"Œ€’XØ$bÝûÒ¯_?Q”ôù|öÙgîÀbh¬+ýnX"Aúù矋Gú¢Ø _AŸÖ + /]ºÔ¿íš5k–«s# PqÒ¾}{y/MllŒÄÄ¢ÉTúöí›òšÙh®¤y)Vä^™øÿQ¬%š ÌÁ·‚ hòÃ?HS"{ÂW«V-%ø¨ž>8ä„N9z/ê{#±¨¡¦OÉŒWúÇL‚½ªL ‰ ÁÚ¡¿ÍFýaõ@@¸@£ÉË—›o¾YžÁJRKɿ֮][ü;B„”gUú¯Y³FH ’C™kŸ”éÂrùæ›o¤yëè¶Ûns]»v͉ßÈE‹¹§Ÿ~ZúŸ á³Ï>[úÛ˜²ðÎ;ïAbäÑA$ÌÙS¡O‹|Ñ„¨¢£éçÖ&É ™kXºâ*‡—œ¸æÂÒm±E h¥‘š +“?þ¸²Ûî8â>Jž‘·lÚ¿³~ýzé>"pň„¦½±cÇÊ•={öH“]:ͨ~| ›§ù Ë®AÎ#–Í}ô±!]t‘ìyï…H4h × Sú¬&L˜ çü@ØÌÕƒtíHÓ V‡Pa ¿Œ¾7HQ Òx饗ä~ªÍ+–áµ-Uøb\OÞ+[Œ”Ø; CÀˆ Da~MˆÞI­‡U“‰lݺÕ11(Í›7wsæÌqõêÕs îèÑ£‡X•“&Mr;w–à7Þx£cåFz2ð‚!ù_}õ•ŒøTKVãeô!#‹ Š¥©DÈ}×ÐlÉ=¬Næ÷¾*iÙ²¥4½Ž?^úƪ _ŒûÿSabÇgE»b dï4 ’EKŒf9šðR©Bš÷îÝ+M}ù‚fMFž|òÉG¼‚>;<èÐDŠÐÄŠU™ŒLijüþûï¥OŒ0AáYúÎT“ì~0¼ž3¹‹9™Õ¬aÒÙóÎW^yE&ÎcÕÑG¾)IÜÁfåtâ´æÄtP²0†€!P¶ \ý‘~ù‚æMmâ ÆOŸ› Í©„~³ÊÒʳ•=Ÿ*^%ÐT÷‹yÝš‹‰¾½Û0 C +ŒÄ²‚Ï6 CÀ(&FbÅDßÞm†€!FbYÁg†€!`#±b¢oï6 Ã`„ £M t0K) gÄéÓ§»Ù³g§Ö¸JåŽ)íH2 ˆë)\%á³ÐÄH#±t‘²p†@Œ`áÊn¸!í,\¸P&Φý@îÚµËáÝâòË/ÏAlE¹ `óÄÊåK[>ËÅ‹‹»¥¿þúK,¶ÄUÑĉÅ7!nšþþûoÇ:T8ºÝ±c‡xt`ù<=0Ù–ùE¸Ç .Îeñf^™3Øà}ŒxXú#™`õAšÅÖó:xð ëرc±’Pvï¥"Ä:g|wÊ)È-há§Ž‘X:(YCÀ(I ‘BdìÙ ^ȘA.;[ªeZJ”d +‹Š[µjÕ„¨°Ð!.*4ìuîSAÂS¿Zg™$Áš3Aˆ@I!€’eÐ '¤¥¼U +‰•T¦óœšÙ %°… °¶ +]7 ܱºÂX^Á䉱sCÀ(hÂB¡ª¢%ãÊ—¹qØ¿ÿþ+[Ù€’ƒŒ‚!$æW 1%2î±)‘eÓ/f$–ƒfQ†@<€¬è¬”*ŠW Ë 3É HL­Y0VœÁkL›Ä2ÃÕB†€!p(Wú»Ø°ÊTÙB^8"æº?Iû°‡í$%XV˜²AZŠ­SYàd§M)#¬ä†Yb•€c· C ´@‘"(Q%1ÈK›•Àl`Gfå!¡Xµé …‹• ƒ9GS¢>¶™.BÂ!%0_ÈŠ=¦ýa>e“<#±lгg C v@JlXYª\Q¸«ææ°>±pŸÒR"Ó=Ö—63r_IŽã°bóÄÂ"gφ@lP KÉJIÍß“9ÎMÂ#y±AR>©ùç&ì[ŒÄÂ"gφ@¬€ *ÛÈœ‘XvŸX-,ˆ Q" ËÍ?Fb!³Ç C 4Uð¼4rYü\(‘‘ÿ8Û”‰e‹ =o†€!P4Â÷¦-ÉöbCÀ0 C࿉YI0 CÀˆ-Fb±ýt–pCÀ0 #1+†€!`±EÀH,¶ŸÎn†€!`$feÀ0 C ¶‰ÅöÓY CÀ0ŒÄ¬ †€!`Ä#±Ø~:K¸!`†€‘˜•CÀ0 Ø"`$ÛOg 7 CÀø?Ÿ÷ŸÙÛ:kIEND®B`‚doc/next-tutorial/languages.png000066400000000000000000004160751242365656200171460ustar00rootroot00000000000000‰PNG  IHDRÔÂXÕOÂUiCCPICC ProfilexÕYgXͲî™ÍËî’sZrÎ9g$Q¢’sfÉA$((" (¨ˆ¨ J”$ Šø!¢ b"ˆQ1 ((wÐ/œûœsþÝ?·ŸgzÞ­ª®®ê€sÙ;** f <"–fofHuqu£â&p hoߘ([[+ð_Û× m3Êlëú¯bÿ™Áèçã d‹°}üb|Ã| ØÐ7Š j¡&ÄF!}Á,4Ä@?߯¿ñÊ6öù…1è_2ŽöF`8À“¼½i…:5Þ7ÑC6Ëá³ ‚u}ƒ¼ýà,Gd¤ÃÃ#·ñ-‹ûü‹žÀÁÞÞ>ëôöüÿþ/ÈHdbãà˜¨0ï¤_?þ/»ð°8äyýjLHOŠÛ¹í6äZðó6¶Dî<Èõ3*ì—ψË?b—BÛÆÒ>;mþĺ4S{#c!Û¨XÃmŒ<3( *ÖÖñOzZrÑN“úÿ“¿ôœ ñ¶Øö¡7Óâìw!XÁ}1ñ&FVô:9ÈÑùO™/~þÆÒa8 ØÔü· Ìk¾= âsÁÐHËm¹`U` €?ˆ4¤2À ã?{¼N<‹¡à ‚Ñ‘ȘHSÿ”3ú7Šé¯qȸÿ­‘ |Ù¸¿çü=™ó/ÁÀÁѽ‘9¶yÛÖÅxgþ3ç_Ûú~Y#ß ¿(¿ù—MhQ´"ZmˆÖAë¢5͆æ2he´:Ú­‡ÖBxÀ¼F4þeã¶þðæ€øòÈ$M§ „»ýß}þâ§_ÒÁÿþ7 @ðÈrÛò_럈ìŒ"£’hÁA±TdçúKSÍ#|e¥©Šò òÛìÿ7mûÌúmìgû_gÄvÿZ¤ÛgÌþh^ïh A¶)Ó?4Ñ6è:æG‹ÿ­½}Ã" GV('àB@yΊ@h}`,€ p®ÀY?AȤ 2@(‡ÁQPªA-8.fкÀ 0†Á(ÏÀ˜oÁ ø 6 ÂAdˆâ„ø!H R„Ô!]Ȳ‚ì!WÈ „" 8(Ú@%Ptª‡®@Ð hz=…¦¡EèôFÁ$˜æ…Ea9X6€-aGxGÃÉp\—Ã5ðE¸¾Ããðü^EŠ %€’A©£ŒP6(7TІJCå£ÊP5¨Ë¨NÔmÔCÔjõ E3£©hdî@ïBû¢£Ñièƒè ôyt+úú!z½‚þ‰!cx0RMŒ9ƈIÀä`Ê0ç0-˜Ì8fó‹Å²aŰjØXWl6{{ÛˆíÃ>ÀÎbWq8'N §ƒ³Áyãbq9¸ã¸‹¸^Ün·Ž§Ãóãñ¦x7|>_†¿€ïÁáçñ‚A“`Cð#$Šg„û„9‘‘(FÔ!:CˆÄrâeâñ9ñ3 ]0]:]9]ݺiºo$&’$Ɉ´›G*$Õ‘úHOIŸÉd²(YŸìFŽ%’ëÉ7É/ÉëfŠ,ÅœâGÙG©¤´RÆ(ïé ô"ôôôÉôeôWéïÓ/3DŒ¼Ò*:3¬223*0Ú0†3d¼À8ĸÀ„ce2aòcÊbªeºÉ4ËŒbb6böeÞÏ|†y€yŽË"ÆbÎÂRÀr‰e„e…•‰U™Õ‰5‘µ’µ›uŠ Å&ÊfÎÆVÄÖÌ6Áö—݀ݟ=ý2ûû7‡>‡?G>G#Ç8ÇwN*§ g(g1gç .4—$—W×I®®enn-n_î|îfîI˜G’Çž'…§–çÏ*/¯oïqÞ›¼Ë|l|ú|!|Gøzøù™ùuùƒùð÷ò/QY©Ô0j9õuE€G`‡@œÀi A1Á]‚™‚‚/„ˆBêBBG„ú…V„ù…­…S…„'E"ê"A"ÇDn‹¬‰Š‰:‹m]ã3Kk{.Nׯ$•P—•8!1* KªHIVJÞ—‚¥T¥‚¥NH=ÆHkHGH×H?–!ÉÈÄË4ÈL˲ÉZÉfʶɾ—–s“+–»-÷S^E>LþŒü3& …L…N…OВоЕДÈJ¦Jû”Ú•>*K)û+ŸT~¢Â¬b­r@¥_凪š*Mõ²ê¢š°š—Z•Úcuu[õƒêw40†û4º4¾iªjÆj6k~Ð’Ñ Õº µ -¦í¯}F{VGPÇ[ç´Î”.U×K÷”€ž·^ÞŒ¾¾Ÿþ9ýy ƒƒ‹ï å i†-†kFšF{úŒQÆfÆùÆ#&L&»L*L^š šš6˜®˜©˜¥˜õíÀì°ÜQ¼ã±9¯¹¯y½ùŠ…šÅ^‹[–$KË Ë+I+šU§5lma]jý|§ÈΈm6ÀÆÜ¦Ôæ…­˜m´íu;¬­]¥Ý{ûTûÛÌž¾::9>Û%¾+nW¿½Ón§z§5gcçç)9—½.î\®Á®ín87'·sn«î&îGÝçv«ìÎÙ=±GlOâž!.0nOzOoÏ«^/g¯ ^›Þ6Þ5Þ«>æ>U>+¾F¾Ç|ßúéûñ[ô×ñ/ñŸÐ ( XÔ , \ Ò * Z6 ®þ²#¤:d-Ô&´.t+Ì9¬1îÞÁq+’/21òA”TTNÔT´fôÑèš%í\ ³'¦=– ïʼnÇeÇMÇëÆWƯ'8%\MdLŒH¼—$™”—4Ÿlš|6â›ÒŸ*š‘:½×`ïé4(Í'­ŸÐ¾¬}séféç3ˆ¡dÊg–d~Ù_3‹7+=k6Û,»!‡’CËy|@ë@u.:78w$O)ïxÞÏ|¿ü»òe›}Þ=¤p¨üÐVa@áH‘jÑÉÃØÃ‡'ŠõŠÏ—0–$—Ì–Z—¶¡É?òå¨çÑ¡2å²êcÄcqǦʭÊÛ ?||³"¨b¼Ò°²±Š§*¯jí„߉±“ú'/WóVT?|êÉi³Ó­5¢5eµØÚøÚ7gœÎÜ>«~¶þ×¹‚s?ê"ê¦ÎÛŸ¿U¯V_çBQÜ×°xq÷ÅÑKÆ—Ú/Ë\>ÝÈÖXК⚖®x]™h¶l~õò5‘kU-Ì-ù­PkRëJ[PÛT»kûƒ‹ŽþN­Î–ë²×뺺*»Y»‹zˆ=Y=[½É½«}Q}Ë7oÌö{ö?»éróÑ-»[#–wMoÞ6¸Ý{GçN׿PÇ]õ»mêí÷Tîµü¡òGˈêHë}µûí££´ôŒéÝxhüpð‘ù£áñã&vM˜Ž…'Q樛h3ô#L8–ÛKÅëp„ĺ*R¹ŽòœÑ’)yˆ•‘m7ûEN4—7w7/•ï ÿº€Ÿà¤ðN‘!19ñB‰·RæÒÕ2_åŒä)Œ*‘• UbT«ÔúÔ§4~h±kKéhèšèÙëûÄf3n0é5}h¶¸cË‚ÕRÚÊÈÚ}g°M¼m¶]‰}µCƒc²ë‡Ç\žº¾r›u_ØýnÏ‚Çsϯ^ïFŸ“¾‡ü’ý쵂„ƒ)Á_C^…†Õ‡ŠˆŠtˆR‹æŠÞ¤½Œé‹­ËŽH0O”J"&-%ßKiJ-ß›•–°/:–‘œ™¿ÿtVwö«„\í¼¨üÚ‚‰CÄBí¢ðÃ'‹GJ~‘>ê^–¬µ|ª‚®R¥ÊóDÞÉæêg§Ñ52µNgö=îAÝz=õ‚UCêÅæK5›Š®|¸ê~í~«MÛ£íÎØëõ]Ï{èz•úœnD÷gß,¾U6P6X|;÷Îþ¡w º—ýGìˆó}Ùû£}RÆTǾ>|ü¨c¼bbïcÏ'†OE& “ïž=xÞò¢âåÞW^SÆÓ3 3ßfß¼ž˜zscþúBÇbÇÒÙ·…Ëñï<Þ›|ZaXYý8ù©çóé/Ù«A_-×äÖ™××¾=ÿÞ·Q³™õÃÿ§ñ–àÖâ,àB¢ÃD0€DtVÐa謄Ä^ŸQž¨ $jz‰ÂR°m8<~’PE ¤3$é)Aôé §o0-²°²³%±7r|à’å¦ñtñÑñ;Q/l gˆôŠnŠ«I„Hž–þ$Ë*§$¿CÁ]1P)Z9Ie¯j²Zˆº»†•¦Ž–¼¶ «.^÷»Þ;ýiƒqûF=ÆWMêLËÍrw$˜Y¸Zî°R·ÛÉjƒ¶ùb;c÷À¾Ï¡Éñä®\§gsW%7^w¬û{ä¤ïö¨õÌ÷ŠôvôQö%ùÎøuøj1½ ¾Rê¦N>Ñ™å-…¬‹‘˜S±´8£xÖøù„ŽÄƒIÉò)pÊãÔÆ½iáûv¥ghfjì×ÉÚ‘í’qà@îÙ¼›ùÓ?ñj9Ž)>\r±tèÈ›2øO¹êq»ŠðÊ‚ª‹'FO~=%xÚ¶fmÇ™çdë¢Ï_«_kи˜z©§4\Ùß

¬yÔ8Þ51ôxâÉÜÓ/ÏPÏY^ˆ¼Te58];³øZlÎíMÎü……Û‹ÓKëË”w"ïõ>¸¯¤ý¬ô¥tõóšýúµïÙ›ë?~ù $Ø ÒA×kB±P ÃÖð)x庋ÖF·bÔ1ýX[ì,.Ï¿M8Dô¥Ó&q“~’g(Ãô- gË™ ™sY²YsØ ØK9ª9¸Ú¹»yºy{øzù{¨×Z„NˆÄ‰îÓ”Ï$Û¤ ¤d¨2K²-réò– l ÓŠ JqÊz*•‡ª'Ô‚Ô•Õ×5z4³µ,µ™´'ujtCôõ6õ Š ÷I­ß4)2u73û´£Ç<ßÂÉRÀò­U«uúN+6›iÛ»{-Øá®cÉ.7'ªÓ¼óe—8W-7ØmȽh·Ã¶=O=*=÷xñz½ð>á³Ç—ÇwÒ¯Üß)€9à~`AI0@ÖK|¨BèrX]¸OOÄãÈ񬄿è´ä¥˜åسqñìñ÷$ê%®'5%§PSž¦Ùë˜Æ™6·¯=ýHFRfÀþÝY®Ùî9þâr³óÊòÏ´<4^8Wô¥UÂ\*xDþ¨f™Ñ1‹r»ã®>•‘UûN”ž¼X=|êCHmÒ™Ñsbuiç'.È4d]|vY¡1·ée³êÕ‚k¯Z•Ú´?ïTºžß5Ó£Ý[Þ÷µßñfË€Øà™;2Cá,Þ»ò¨~¢éÉÉ/À+ùéº×9óùKmïé?æ®r¬·l:oûÿwmiû€Uàì,N§°s N ‘J¤l‚Ô;lÉ8jذ@OÈìòßï2C²üpɇÀ[ˆ)@» dèÔ=ƒ6‘üNösà ð}ø Še€ BFu fÐtHýÀ ÉÈÚѯ1L=Læ4fKÄ`±ÍØeœ8.W‡[ÄËâãð½:‚á""º›é(ttc$uÒ)2žL#¿¤XP:èÅé+È  kŒ‘H¾âËôŠÙ‡yž%œå+k…í»ûMwŽUÎb.®‡Ü <¼<£¼ø ùÿ j¶€µ §à‚Ðuáb‘QS1q’øªÄŒä˜Ô-éN™«²MròÍ íŠ}JÃÊ/U>ª¡ÕY5„4e´´åu$u©zLú°þƒg†½F5ƹ&‘¦.f†;äÌù,è-Q–ëV+ÖK;çlfl§í^Û¿uøìøÃ‰àÌî"æªáfíî»;eÏQ&ä=ö·â«äçê¿/ 6p h6øG(S˜@¸d„l¤L”D´ -†ó=v1ž+Á:1+©7ùgªÉÞÒ´·éÖ×÷+gu䘘Í;P pðr¡~ÑTqq©ËQcæÇ*NrŸ¢ÔÀµßÎ~ª{_¿Ü°|éCãê•×ð­ÜírÆ]®=Á}ñýi·Ò÷Þ‰¿vÏk¤`´}li\àñž§ÕÏÞ¼T˜Ê˜Ÿ“šÏ]œ_6{á#Ãç”ÕwëßçDý:?è,°CªQ ¼†èj€;”…düÃÐ$»×„½à\¸ ~ŠB!9»+*uõ MFN•Pt%ú$ÿVÀøaª¿Óc­°yØ;8"ÎW‚›Ä‹àiø~!Œ0H"fçè,è:IR¤j2+ù Kɤô (†\F ã1&A¦Ff}æq–pV,k ›Û {‡ Çg—<×w)Ï^4o?ß~~S*‰:!P##d*Ì'¼.2!Ú&vJü˜D±d¡T¡t‰L…ì9¹ù; /×”YU4U}Õ Õ»5>h‰h{êTê>Óç3ð3l4Ú011-06ÇX¨YúXåXŸÛyÃfÒvÅíÀæ(¹KßÉÕ9Æ¥Èõ²ÛˆûÇ=l:ž^ÅÞ=>ïý„ü]Šƒ~„(‡‡  G)F{Ñ c®Ç.ÄÓ'¨%z%$·§ÌïeO3ß·7½9ci¿P֞슜'¹ìy®ù' ^’)L,,æ(‰,½wT¾¬²œr<¯’Tuô¤XõíÓÁµ¤3ÍçÜΣë›讌|òüüa5}~ýøw¾ê\?‹~ùŸ€(¤rtl!¾€ŽCÐgX¶Gj8mð2R«qAöû…Ô“ÑmèUŒ &ÓÅ`m°•Ø%œî0noŒ?CÀ¢ωVÄ>:5ÄÓ†¤{dWò%ž•¾‰Á†á#c“.Ó"ó V2ë]¶/μõDÀu=´Ë CÀ0 CÀÈ7âdzR²vT1R ÌãPgŽ™]a†€!`†@Þ"ßæñ.è¯LÛ»mûG–º…í¥ÄºáfW†€!`†€!¯‡†¥"Ól7Ó™Ï$ŒPG¢a†€!`†@¾ €{‡i ¶}t“Ïò¤GÀuzŒ,‡!`†€!`ƒNóØ'Lcñ¢ñÀ0@…Èc@†É²†€!`†€!P(`å9¿C¾]…Ò§|og(Àó½Ö>CÀ0 CÀ0 *°ò\ j;;9 P!ò¡’e1 CÀ0 C PÀêP„[CŒPGdT#Ôiņ€!`†€!Ø uî­`„:÷˜[†€!`†€!`dP+Ôh€­PGd#Ôiņ€!`†€!Ø uî­`„:÷˜[†€!`†€!`d ÛC5h“l„:)4–`†€!`†@á!`+Ô¹·™êÜcn5†€!`†€!5@¨Cñ;;‡::„<ºê¬$CÀ0 CÀ0 l"¢ê¥D;6/:+¡ŽK+É0 CÀ0 |@ ¡¶»Dg*#ÔÑai%†€!`†€!¡Î±ŒPçp«Î0 CÀ0 l"ö¥D´ÁΡŽÈF¨#ÒŠ1 CÀ0 C °=Ô¹·‚êÜcn5†€!`†€!MlËG6ÑMP¶ê X”!`†€!`…Š@Ø-öRbtn]QV’!`¥@ØÉª4ÐÈ^/m²Ï¶Vrq#€{§žSi;‰<»Òf² ¡0B &Ëd¥‚@=Érú¬T€Ì¼Ÿµ^ŽJg #Ü™ƒlW” aŸEµî»’A(⎡ŽP+Î0 t¤ ½I´5.ìdUX`ägkƒ>ñ¸ íŒ`ç§­U¹G xo$kÝ3ÉÉ<Þuæ˜Ù†€!P$™pH”}²œÈïÇ%C"Lžd×–J|(§êw5‰F&a«ë«Ó|›QH§¥;»ví*ÃWÓvÓ¾”˜¢ÐŒP‡†Ê2†@1 à“®xHÐDÒ凟,.^DµRþêó¤E@Ä8QFg5qŽgTX˜++Ov6bV‹+v4þÓõÓît…O7B+ËiŒ@‚ &H’EÌïkö<˜.4”OaÓuG F†q¹È±¿Ä&r­4iÖ¦ëh‹ñ´»‘BdRbèy•²Ûvo¤„'£D#ÔÁe™ C H@¦EÔ|2¬8jÅ5»¯|A?ÔPYUVûG@„˜AùƒZ„:‘öI4ÓiƒêëTV“RB Á3/Y÷uŸ$K·ø¡ ”e3 ÂD 0±$"Ã>ú.‡;®=\[8=7}âìû‘ŤŽ'wåõ(ïC¸7áÞŽ—Mâì“gæe˜6 –ã0œ­Æ“’@c=ðèKÜmÛC—ºÄjb¨Ëµv!`y@`F!Ñ¢üúŽñtŒóu?„/ƒë·7œI~ °͘wÜV8i‘kŸhûq±á`¤ˆ™”zÞ¥ì+^^ä=bF¨#ÑŠ0 üC ™q‰fX®5ü¿‚Æ^íµ×^»ûõëçºuëæÚµkÇ0£MrˆÀúõëÝÊ•+ÝìÙ³Ý'Ÿ|ÒUßw!Üàþ§H" ÓO›úÚH51)~Ï¿¤¶˜I¡É8ÁuÆÙ†€!P`\QDºH²è'3öý ÷uyyù¾ºìâ‹/.kÕª¢L;wºçž{Î]{íµnþüù‡ =‚»î18Ù—ZÄÚ'Ó"×FªIq#vË Š‰ÜõŽCÀ0Š ÀêŒO´|M?H¦ÛÀÅÈtŸ>}Ü¢E‹ÊHÜŒL•<þw`èСnÞ¼yîŠ+®`«šÀM„G;ÒɮԴ+íO?…~C 5Öm…:º¡ ‡Lt%ZI†€!`ä|Æù„š~­L‹p‘„‘˜Ý wÉôœ9s\‡4ÉGøÁŠ;î¸Ã]wÝulíÇýÔü7mëkÚ[c@QF¬ ‚Iñ"XTHÚQ#ÔI¡É8CÀ0Š$‰ˆŸyr"_Ô'À á6ÊÊJ×¢E‹¢Á£˜;2aÂ7`§cÿ]¸Ú'ÓôËÖšë4xê‡âx½‰!Plp¬§#Ôi! Á(¡¡²Œ†€!P`ðù&E¿ãD¦EÀør›ãži[™&…!\©¾å–[bA ÿÓ@ÛÒÑÞÒ¡H®31 î¡Ó;6/ Jáòðcb†@) BM-ÒEÍ­Çóå¼€X 8UyK—.]xÆ4íØ N?’D¦ewj’ ‘k®R‡"¸ÆÄ(4ÂŽm{)1"Ë¡ŽH+Æ0 ‰<ùdÊ÷“l‰x 3;Í£áMX§ 8P×õ†Gv•–ÝE¤}¢áûU†iC àÀ³ã>­Ø9Ôi! !à¡K³Œ†€!`ä>‘‘–æØÛ‡roM>xL CÀ(6D¢¹h ¿O¬Dº¨`çùÑ–†•­nùo¹5Ûۯ㑮KÛf!š³Õ½ƒkÖ'½f‡[þÏ®ò™9îÍ•kbåµhq°ëÞ·¯|ʉ ëXýÎnÅz,Ùó§F-Ùî¶»®ã¡‡»¶åù3}4kVÕ>h2[Îc³Ô:‹š~ŽiÆÓob#¡~0ÚK‰Ñ™>žˆÑõÉJ2 ÒFÀŸHD¦©éHªµ:-û¬xƒqã7ª×WÝ,4°×ísÝ‚+¹%àš‹’]³î wÓ¨^n|eò2FÞø„ûåõgºêõ]·Ñ=rz/wÙÂä×(eÌ=/¸_Œ=ÉqI8„sÉrÐùDººÉü×8HóšEƒ€·ý-eŸŒP§„'£DÛò‘\–Ù0òo j‘j>ï­RçÇsK±½'hdçf —‡kCŸôšÕîç'údz»üÆÛÝĉ·»ËGò½½*™:þ,7øÚén‡" [vö)¼“ÆìúÝ43EŽI¢-Eª©}{³A r ›E‰@Ø-ö¥ÄèÌŸItý±’ CÀ |¶ÑùdZq$Yr"]ˆ*Y=ówU|•¹âÆJ·v÷Lw×õWºK/½ÒÝ5eûxá®"ÞÝ…·]åžýÀ§Ôñ„^·»÷·ïvÛ·c›GÜmÙ²Ö-š3Ù ÒµãäžNtmÃAI‹Pûö¦Ÿ6™Ö¸ 61ŠPcÛV¨£3=2&†€!`šH¨å¹±ò uÑ=W-y;nÏ1îÎëO‹ è¸mÏ3Ýä9·Ç£ºe+6úÉUþÎû9n“nܸqµkÖ¬•ëÞÿ|÷˜íª×Ö.-W1´% µ¯é§ÓxQ&†@Ñ" g_Ê¡N OF‰|𘆀!P,ø“ˆO Ø?+jj>ÿ¨.*iÑ&öž%úÔ&ÖÉDkuôPwù énkéh‘`*Øèªª¸f|õ/?E¶•]©¹üÎxµ\a{1Qˆ˜.¼-p)ûd„:%<%&xŠft½e6 C ß ™ò •Èãø ôIx¾÷%Tû6¯y7žï6wÚ¹û»_ÿ×H×·{{W}SË{º»f¦ØÝ2yU?þtO"NÉ#‘MiWiÚšn'\p ªJH>ŒX ÓÅ€Æs# ë´]Á—í…Ü´(…Ë`„:N—+ì¯Ó‚ëXž5Ø&á<3Èr¬ÕG’( Ã"TÔ"ZŠcž¢‘ßçz«tÜF½pêUn?"8òòS]¿¸¾Gõp]íäÊk0ì@÷ß]ï6‚’6÷^Yܱe{kÖ#nTÅmñÌîËGð…y#²­ìJíù©)ÒU!ûk¡Æ¶ÍaÑœ“C€d9¤è’?¡˜Ïd‹Z°ÖõʲˆHð'úƒKá¢\¡nÜ~ˆ›1ï¡ê«]è¦Þ}›wÖP×§Ç¡®eó2wîMºÅë’à¾ð*סIΣnRíš·lçúT\#꼪âžñ®^ñéZ?œd{<¨ÃŠ“V¼iC ààܦF¨Ã .êp85h® iKБ_k.¨ý<æ¯I´ÒáÄRáàuÕ²¶K`N‹ª'´…v ¿ÚñpÐ^Êäâ‘¶½ÏqOíÞî–-œãºçv7fä Z›:~”ëÑú\÷Z2R]ëŠ=×Lžëþ8¶÷žˆüðÉæ²1þÓxÎV[+ ˆQæ¸O+vl^ZˆBg°-¡¡Ê}Æ$¿059¨AþM#¿´ò$Òaò$º®”â2yY‰y•_Ø*\æÛÔV²:”dVB¿O°ä—öófµQ¹/¼±ëÔ³Ì3öJwïä­nõò%ÆÝ_ižêÆüzŒ[p}ÿ@óƸ9‹®ÄG_üMÒê×Ý=?àÆëóäC=Dª2Å_ÿ™©g_2]¶N>˜Ox¤ì¡æœd"^eEÔ$dš~ßñÆÐáë~ˆ¿ Žoí g’,A3¦ÀÝ·NDZäšÚ÷ÓÖ±°‘j '*Ö ûK÷StµÖµ$¼ûWýïÊ«ÜPîÙH"½nŸë\yl,µú/ï±—Üé®y ÒÝ#Ì“ÜÐØ×ÑkÔf›5+΢•¿×nâèž ¹Ï«}U_Fñ‚…à•]ÕVÙ\šñ¾_ùLņ@¨qn‹8Ñ™ÝutXf\R2­‰ž7ý¾kð¯à†Á9þ˦_¿~®[·n®]»v 3Ú$‡¬_¿Þ­\¹ÒÍž=Û}òÉ']QõpÂýîop´#‰3E~Ú•qÒ±aa9B™èò d\~‰·U#]Ãzµkϲg{GyKo›Hã.îÖy»¿¹Õ ½ì g¹Y5 ä®™|ûÑÈ“\[o&(×Âw‡r|Ô¥ %•mS¥dg­Ñ†@"lËG"T²ç=F³[‘•ž=ä©éH®¨ýãŸè§; îêòòòÝW_}uÙÅ_\ÖªU+D™44;wîtÏ=÷œ»öÚkÝüùóA{þw9Ücp²/µˆµO¦T˜, ±ÏOÁV{wïv÷fÒºT×4në†\z—Û=æZ·ø­ÅnÙÇŸÅJÞ{ŸV®mûŽ®k—À§Èc©åîœ)»á2iDÞåMdãDqy×pk!!¡Æ¼-ÞD‡¸êè°Ì¨¤Àê4¾\L3,bÝþ™îÓ§«¬¬,ëСCFõZæì"Àÿ :Ô}ýë_wW]u•ûÅ/~ÁeÉpÜ_ý2 4>è\-´ûN¸j2 ­ýÕÕ¹ÍcdŠ@³¶®{o¸L¯³ü†€!P°8FÒ~¡N MÆ œÄMhjjh‘iþð!1ãÿn#™ž3gŽ32 4òT5jäî¸ãwÝu×±…´÷Sóß´-Ãt²}P#)ö‹ÚÄ0 CÀÈnùsò¥py8‘›ä$¿9øi߉|QŸ7„Û<°2íZ´ÐJÄšä-&Lp `ûøß…‹à|2-R-›3ŸÆOý°û“ˆD#Zõ—öK¥±u+ß5)D¼ƒ øßm§*Ä®X› ¨E¨m…:*¸«È[t¥YI™"@Â$%R¥8‘i0¾Üæ¸gÚV¦3…¹áòs¥ú–[n‰5y4<üOmKG[Kk„zâ:“ôøäYþDúSõá‡Ü•cRˆlÙ‚ÏBV‰H‰â¨dûh˜¿5—اǣ œÐMò‘j’h‘.jn8žÿšÁ ˆùÓZkI(xK—.]xÆ4íØ N?’D¦ewj>E®¹JꡈkL’#$S$Õ"Ö¼jÿ¼þúëT&ˆÀÒ¥KÕê qì+­t†ƒãAi¦ bB ¿¯°û!"«‡<¢º¬ à$‘'êDŽdKÄ‹Î61³Ó<D!ÊÀÕìÞðȮҲ¿ˆ´O¢}¿Ê0’'ª ‘R˜“ɸM‹-r‹/W²åʶoßî^|ñEµg<´©ìKMGQ|UhO¼Â¦ bB ÔÜa+ÔÑ™œ¹IÃ#ÀO[P‹HKs‹@ì(ž3mR˜´mÿJ]ÕW-ƒDÚ_©fõ ”.ÌN7L«Ež’Õî-æa˜ûnÿÁÀÍ7ßLeR@L™2Å­[·Ž-^÷I¼éþ8 ßdzÄT²x?ù ‚CÀ[¼KÙvÛCžŒ9±›4 "Ñ"ÒÔ>±é¢>€MäG[²/;ÜòÎp•ÏÌqo®\«®E‹ƒ]÷¾}ÝàSNt]Ú6«Ù„­«Ýo­pÛ›ìçŽìÙÅRkæÍAhÝòÅîÝ5›Ý~uu]Úë 9¨8MÍšU#³²òG'rÚ–š+g´?ý¾®¨!Ù$ˆ'ÄÓO|…ñ_á?ᡇj|þùç— á~yý™Në­ßzÄõêïŸrs7ÌtÇ6(‡Ýè?­‡·•½O3'ïMƒ¤Ð®tšì}ÍIž„šqÕ‚•†FxèÕˆ«N4O2ˆ±¤ˆÿüswöÙg»+V°‰ÜDýw8Ù•Z¶¥¦(NáªXûk!˜3B-RÛ±yÑ_Nt%ZIIðFwP‹TÓdÍÔt"_9°Ój÷ó}2=È]~ãínâÄÛÝå#ù]•L–|íô_&QZ>覫ZѹóR|»êG“ìÍs,Èåe  Q>a¢_aù¥E¾¸åƒÛ!¡^€íeÇwÜîiÓ¦!h’|ôÑGî”SN‰}•íÛÇsÞudí+Ûú¶Ftl,(NajC Øà<’Vl…:-D¡3䀨…nK©e‰â —_š¤ZN¤+«ø¬žy» +»”Š+ÝÚÝ3Ý]×_é.½ôJw×”îã…O¸Šªd·ð¶«Ü³`Cð‘gºyøÈÌœ¹Ý‘ º:o\þ+ÚØÿ¡Ä°~DQ‹Lk\„z(æ·´…>ÁÑ’ c˜þáþµaƲaƹ3Î8ÃNÿ ù"Ÿ|òIìƒIGqÄî—^z‰Íâæé_Á­…“-ùp’}Ež}ä˜0ÎÄ(J¼¼”ý³—S“Q"'v“Ü# ’$òĈL‹`ù„šqY•UKÞŽ—?ÆÝyýi±súü Ûö<ÓMžs»k=à*D/tËV`Q¨=NkÖÞõîßÞÏóW}$£±kÖ ClÇ:÷ÎÒ•nóvçZ´<ØuéÄÓãªdÝ︕ØóŒwС\ÛòšC²F9º(®wàCœ9½ýÉÁàV·zù2· õ¡)®Å~¹Ú»@•Á‹¢Ó–ì$›.-Ûk<0LB`R7Dž|RE?ã'òEM[Èñã)pÀ ~òÉ'›Â¹Î;ï>æ˜cÊxüáÞ{Çv`!9»‚•#ž i%,³eÕªUî­·ÞÚýÊ+¯¸;wê>y}yn5í(G˾²·l¤ê1 0¤™fb ¡¶B¹k²—èʵ’’#àrM "Ì"VÔ$Ô´µÒáÍŽ´h{ï…·‰Uš¨–VGu—šî¸–î€UCgëâ]ó£}¤›·aŠëUêoÜëZö‹{¨òdwÅ(7Ë+°×˜‡ÜÓ7÷v]ÔÃ]د}ùC Ý]çôŒåÞSNÊ~*Vvu1[ßp£š÷rS1qÞw)+N!ËgÞëF<®F;ª²÷r×Lžän<ÿؤýNQl]’d[Ù•šdŽñ*Wa›ð…HzM¬ˆ›™üÔ"XÔÄ]$ŒšaþÖ¢Mfý7î¨wß}·%¼¹‘îÝ»;ž_޽ÜY?Ê?\Ïž=ÝŒ3ÜæÍüq›·B»ÒNKá^…{ Ž?€h7ß1ììûe$›ňr£0?Èññ1Þ+& `„:#(‚dÊ'T"[Œ£8‘dU6¯Y¸Ívîþî×ÿ5ÒõíÞ¾æ©å=Ý]3gÖhÇöíú0ÙÆ©‘º;¢‚”·¦,œ4Â:©fœBwèåúöÞâÎé^}*F,‰,§¦lmœ¬Šã\š\Öýó^w(È´¤WE…ë¼á]W9‹{\ºÛF}Õmi½ÔÝuZeɦ–MiW6œ¶¦ãĈªþûÎV„FJMÒä ' © Ÿaâî“1ù9ä¸`:ýÁp|·5mç߯úáƒèR§û¶Gûãsõisü;vJÜP£Ôº‚ØÄJêØ±c{HÛ£>ú#¬¯ª{ñõ¾Rí£–Í¤7!ŽGâ­€ãži>xh#ßÉv´©O¤ew•+l1âÍ.1ÎÄ(øüÓûaP —‡ƒInÐ ×$̉™Â°µˆ–â˜'kÒã›ã\¯q• — ˜S¯ràøQ¿‘—Ÿêú0Àõ=ª‡ëŠ-å5ynö$~ °—›8í1wÉînÇ/¹‹: ˆ­*Ç.t›;ùwl§ænùÌ߀ôò´禽¸„ºj•:?@Ò+¡ IDAT‰ÊN½&­+{÷e½\Vá*Mv§u¯Úr²qùKî’C«Ú³`1þß;BMÛÊ®ÔþXŸš"]²¿aa"‘"¾~Xd‹äK?jè'1 ÞsÄž×SV‘lr(2¯•ƒ6CRµMé-GuÔ!'œpÂQœ ß|óÍ×ß~ûm®’gUðÉõ÷q¢É°Ã?¼9õ¼¬VV³p‘Xi¦ÒO»Ñ‰‹(ÓFÛâÎ'Òô+¯¡_Zå¨,d11ŠPs‡í¡Žn h²ˆ®D+),þ`×ÄM{ÐùaNÜ~Þ°åg”¯qû!nƼ‡ª_<¬ºx¡›z÷mnÜYC]Ÿ‡º–ÍËܹ7=èsÝ.¤Œ|è1w)È4;Ѭ}wå=ñ+{¹iSo™&-nì:4ÊMŽ'müœócT²Ã}¼ªj¯bâøj2ÍÒË;¡=«*5ý­ØrdTµ¦(Ç·­oëD6VœtŠb-)>9IóµO¼|R&ÒF½Ž«¡Ô Ó/Í8¥+^×êåI«±Í£íñÇ?\ºÑ’%Kþ—îø-ô´×Õ7êZ†#è>kÑ¢EËC=”7dÖëL€ñfò+¬ö0,|¥i7ú}Mò,-­± B­0²šŇVžCÍMš4á½`ä9&¹G@?d8àEªÔ †9¥gM·í}Ž{j÷p·üWÝß^yÕ½øòt7iê¬õM?ÊMÿ‚›»vŠ;vÏ»…5òì rc†vß„ïÓåïV…]âŽm_#)KÆnÈ­3Ýî[QüŽ­nÝêÜÚ5kܼï>Zòº»é²ø&î–UKŽYj„_¬l.3ì;j’?“ð_—\ÙM8Š@‰l‘LciuZ6RQº–åheZZvd^ú%‰Ú¡´Z§Vtèß¿ÿìgÜkÙ²eÿ|á…^ª•)‹xéïíN:}íèŒúWf±ªDE_:Š5ñþèÑ‘nŸL3¯ïD¦YŽ?üúT]7ý&†@± êd+ÔÑ™ÛutXfZ’?Ø5sBöýš¬ý¼™Ö“a~¬÷ìs猽ÒÝ;™'c,q/ÎxÄÝ4î¶Ø–‡Mc~=Æ-¸¾š²[º}#lÿN<ÓKrG`Qßj÷ô¯nu—ݼ½QíRM^ƒRhK«€osÙYZטΠ&i^MrE¬I¬è§ DØO"FrF?E×*¿´ˆ´85ít,#”vØa_8p`>°ÐdåÊ•ó§OŸþ¿¡.Œ0V©ß ¡îСÃ(öù‹[ñ¥P{j:ŸPÓOB­ÕhÚËw"Û‰H5m.›RS® Ù_C ˆH²¸P«‡F¨kARçˆÝ©s9vaÝqòµ&fNìrJ¯[-é®ÚúŽ»÷އÝÇ[ðíå±?u'uò6J7næÚvééΤ^áÎmýÕØè…³þåÖP×yåŒÀ®s~«å&ÒkP…ëÕµ³ë;ð·uÚYéŠ4]6 jV"Ûûöf>‘HRÄ…‰(Q;â)&¹"éòñfºÂðÆD×3?‡»œ~ùväE¾íN) ±žtÒIÃA¦›~üñÇ þú׿>‰ XoNeéÒ¥K°BþiÓ¦M÷ÇÖ“/^ü^NPU™Æ9±¦_¶þ>¡öIµ¿Ä'ÕºÎ/K6e\Îq®ê¦ý5²@òØÕ¬Û¶|ÔÄ£>¡:ó¡úTZâ×j•šÌ¥/0¯®‰Fo_ï¦ïÈ9Guu—Äå¶êã*Æ`}z“›ÖL'.=³Ø-Ÿ¹÷C\±uqe5™îuùd÷ô#]'ïàé™ÿ_1QV„YÙU6–ްº’.ФI$Š@ˆX“lkŸ {å±ãs’~jeZ¤Zå 9VµDe*\­>øà|^ãÆ[¬]»öÿžzꩇ‘H¢× ‚¯þëCÔµk×#A¨ßÎQ#dV§¾‹ìŠKûd™+Ó$Øþ µüÌÇ2tµÊTˆªn’ß´!PD$}öø}´júùP׿(®æ„Îï;–˰&ûP7/ª“4iâŒ_8õþ‡Ý ïw]ŽŒÝ¢¹ñŒØ²‘uá´“ îSNž¼ñø¤gJ{âÞ=Çú9wÕåçƒLûy–»Ï`ûInÅ·s"¿Z“]›«–"Ó$GñuŠ‹@OúI®„-ÃZ©†7&ºŽZ„ŒwG#5H5¯W¹Ô¾C0¹`kEë¯ýëc°:Tޝ2¾õç?ÿù^Ll¬¯Á'мJBÝ®]»žØËýp'ZZjáOâ+R,M[Éù„š¶‘S:Ã"Ò*Kå«…‘ÕÄ(.ðäó)­ìرƒ÷‡I„<‚z¬ˆðøtø«ê“³YOwAü´ 7k¼;¼ÉX7}1?>¼Ì·ú—ÜMßêàÆÇùgÅÐã] nª¼êæûµ‰—6Ë|é½®êkç[Ý♿ÂSÃÕÄi5./Îz-63¸ñƒ7ÜÏ¿u¨»-ç|Z­‰‘» c˜ÔU±ö·>ˆD‰œ‘lqt€‰Œ‘¤iû€N–ÀF¨êS/øÕºÏ<ç‡åç™É¾óóvà6™¾dºÕgŸ}¶_büåöíÛ¹ ªF¾\‡±íãß8ícÚÕòË_þò!9¨_±ßô«ÿ™šø WÅû6ñmF¿l*bM-BMm$ ˜?\[ÓK|ù•÷„IpµÅ¤áþ ñoúé´ –ÕV{Éîš*ãs’ÚcŽ¡äµYãfÅ>€âUßëF7qtO/¢Êëq×ê´DqJäLç ™Œ/;}Ù]ŽˆØ«„Sǹpé$XFy¯ÁÕeLÂ\&Ý5ÈU OUuñJ›5εroK~Î8÷µ–hñ®Õ-²@2Gê½áÈL©yÄýdžôïGNùíŸýìgî†n€7˲uµ[üÖb·ìcÎwhÔ>­\Ûö]×.O‘g¹{ŠßáVãÜè50§6iî:tÂÅ¡÷dJãÛèÞyc‰ûdóvפÅ®c×N®m³ªBvl\íÞ_»ÙµhÙÚµm•,´Û„ ØÆ?Ã=GpIHHHàè§&1 &ÙÓJÜ®$DYL‚x“  Ðñ+ïgXû¡9¦öýJ×õº†šB­:¨}©Æ ˆ'âk„7`Ïâ{8ïr‘·ÎÏœ/þ .¸àþÃðbâÕ³gÏ~-¢v'š`XÕS˜î“aúµêìfù¥yÏ(¯Ÿ?X?7®ºp‰‰!P\à,ûeèQ§t½Â6¯C‡ ¶<]>KO'C &ÍÚºî½ájÆ6`¨1=pûú4¡ÜuéÙÛuIPDãò¶—d²¨‚B€?>ûa¿_~‰4 ãøœ$9fuШX>j¦%l¡˜Ù¼yó&óçÏ)_É4¾~ýúÿÅÖ”‹;vì8ÁY ;“H\ÚÇ·ãåD’yöý"Ö~~•E-ñýŠ3m 5É»e{¨“c“iŠêL³ü†€!PÄI5Û*’+båÇ©/Ló 5‰ 4ãègôË1tˆª!ª7¦±?ùó¿üå/OÔÈ‘‡wÞyçjÿ-[¶Üm)üoIT’ˆÈúqô˱NcÆÉïëDÚO×u*“š«Ó1M¿‰!P¤èù“²{9<Í'e;Š!ÑuþXQxi¿e± -Þ]òÛfþà¡¥\$ÕEšÎ*Á{Šø“S+šŽq"ÒÊG›1¿´ïGt,šÂ´‚¬ /ýÊW¾òÎÇîyâ‰'ûôÓO?Ÿåù6`U²…ìÁ8ÙŠÚwÌCû(o0MשLd­¶9ý&†@Q"àý‡.eÿì¥Ä”ðd”h„:#¸"Ï̇¼DþDúSfúðÕ×t!€Ôb¾D•H8ñ›DŒ@|•šDX÷kŸäW~å‘fšÈ±ü~˜å(ô3Ìr V6mÚô(^¢ìÙ¬Y3¾Ã‘lÌFÕ?Ù€åѯ0µî Å‹0+MZñÊO­k¤[œfØÄ(jðìkR¶øá¬{&m^Ë#Ô©ñÉUjp@ûÛ°Š^=W§X±6“(À¿*Žç S4©KWÅVÅǃÒL×¤š³Œ—?ãÐ S3~:jx«ã”¦8_ý ”<ú裞|òɯâŦç¨áÄÜ¿–f3d3Æé¡N¯ë”×s(0ÞÄ(øŒJ+¶å#-D¡3¡ UV2ê¡ÏÂýIÂsBX·iÑ¢EûâÍ{×½{þ¼.Ȇš¤Fíp/¾ø¢2-‚G@Z“¼ÂÊ«x…Mש+6‰0V‰4'%Ú…¢i‘iÂRœûÚ×¾Öå_ÿú×òÕ«W󥹂é7 ñšUÓ²…ïWÝ/JK¤Õ#ÓBÂtI `[>rofM¹¯¹tkÔ‘ ¦k¢`†¹Gð Ü|óÍT&„À”)S^Hc‹WÀ}oº?è÷Ãñ,1•,ÞÏcþĉµ0î¼ß9Þwt$ƾSœÎ=fZì¨C9÷£Ã;lî7¿ùÍ«âqÊcºöyÑ„ïˆÈù8'òóá/ûÐvôë¹)¨Øª´9I$LJ ÿÒ¾ãO=“汄pØ u8œ²•KºÊ÷Ãôk’§ÿ¯p'<ôÐCÏ?ÿü2œi«kLç1k×®õÏMõmêûic_‚a?Íüõ@@¤:¾‚ÃIGXÓj-6T¯<ÇãV>x«¥lôèÑ?Äg»¯Eù;ñá–YH!!4IŽ€pæÆ3ìÇÉïÛÁ÷³<[‘¢já’B7@¨Ej¼'¡û©¤ðÉFgPgÕpeú“64ÃÔò3¬8ê5pÏÂU >|÷Ë/¿\vä‘G"h’¯àÀ|wöÙg»+¸0í–ÂýNvõm«šâæu&YB @¬ƒµÐ¾øÄšñ5 ӣA¦ÿ›ebÅgÌÔ©Sÿ׿ØüuB@÷AP³0Æ)Þ/œ&Hïç1¿!P ð•Vì”´…ΠU˜ÐXÆHðøþÄ ¿´È—þÅIB½ÛÊŽ;î¸ÝÓ¦M‹¤1VHô|ôÑGî”SNqÏ=÷ ßw\ðßÒ>“í©)ÒU!û91Võ'hߺ¥uOî¸è¢‹†ƒLÿ†|~ðÀü^¦›Ë ƒàáGÌéöÔÕ÷LÇ-rÕñÈcb”,¡–§Žê膈êè°¬OIœüÉÂ÷û ýÂý [(ÃçBÝgœa§|‘O>ùÄÝqÇîˆ#ŽØýÒK/±YÜ<ý+¸µp²%‰ƒlì“6ù‘#B"GÚ#×ôÆHª–]d3éÝcÇŽ­hÔ¨Ñdäi2}Õ}÷ÝwüJ7ÂÙ×Â0'›ø:GCê1 Ü#¡V¨ñ'Þ[& PÐ#(ÆŠƒ@ü#Äp óãtÜvÓ$®›Bï GM×®Y\·ð4ãN†ãFjæs;wÞ}Ì1Ç”uéÒÅáS¢Œ*H)Ôñ¸jÕ*÷Ö[oí~å•Wø‹_²7a~o5¢¦Ûwôo…ã¿t|)‹šd›~jpj@c7+pÈ7nÜ4¦÷õÞ0Í÷ÜsÏÏò¦q7ä /<}ü«ïüo‹‰!`ä98¥‡‹8­Ò5Œk}ê©§ÆÞšO—×ÒS#`{¨Sã“Tþ‚!áòÉÈO­j‘**:†ù–;‰øl8k5î¨wß}·%¼ù+­Zµrt¯¾úªã–ˆúHûöíceñ8º>ø >EEy-íJ;-…{î-8‘dÚNŽydgß/û#Ù$Ÿ3fÌÀ½öÚëI´‘dú—ÅL¦±¥å`liY„¾>7,Ÿíbm3 j´°S‘ȃc]Å?%[\¡Î¬ˆ²/‰•ˆTÐϰˆ´È˜4‰5UN‡› w0\[¸Öp´«VÂySÉÁ[CBÝp5®¨GŸ3>ø ƒj}ðÁB­ããüƒØøi5üûí·ßþpN:é¤møÅâý£öQËfÒ›Ç>­€ã*‰4mä;ÙŽ6õ‰´ì®r¥‘-F¼cËÓ ˜4<_|ñ1Øæñ´¤9¶yÜ ùI÷*{-X¿~ý§mÚ´ÙŽCÐ÷¿ýíoßÏ^mV²!`Dî×PÛ¨÷Ýw_Î7& `„:ëP„‰‰¯Ù"ù¢}DÂH̸2MÇkD˜©)+áVÁqûóðZå¡ö ´â-ûRÖ±cÇ#XÓûï¿ÏÕÛõõ©õßÿþwYïÞ½O+//os 'ìÀJõüú”—äZ=h¤™~ÚN¤Ø·‘ÎÓõ‰4ýÊÃkè—V9*KcYLò lóøf©ép嘰¦Nš4éê|kf¤íyüñÇ7¡ßÏ Ïg¢àápwEZfÙ@ÀŸï“–o+ÔI¡É8Ad,ã í‚HðÉ™Hš¯}âå“2‘6jíÃ¥f˜+£ÒŒSºâu-ÃJϺ>üðÃ[cow³Í›7¯Ã~cî÷¨WXÜ2wîÜQŽëÖ­[¯8€?$êUf‚뉑p“ŸuÈOíש¼Â˜šv j’ghij…‘Õ$=zt7ÊçЖV ÓOâŒñó¡y¿½à~{”D¿[ôµE€žUF¨slG#Ô9“IR"á¤,%²¥Mj­vЍ‰¤),‚'r|á-Hü‚¤P×gMãeÉCØñ?üp T$õ¼óÎ;ËQÞ"¬~7ÆÞìã¢*7A9ÂU¸)Ì~(N~†i¿²—oSýXò@i PûcÅ÷#É$×`qgì#~õ¶Ãm<dúl¬Üò^- ùôÓOŸE¿7bŽ>†X”D§­“†@a#ŠPã{ 6¿Ddg#Ô™a1"LÒ¼\+•$X¾ß'a>‘ ’;…IªE¬ƒÚ'ÊŸU½Ó]ØI`ž|Y]8šîyœªñyÛ¶mÿâ¿ø…(ËöÊòñ–‰ú Í4úe/ikßž>©¦Í5ô€SI& …Àˆ#ZáÄ@&;  /‚LŸ2M›–Œ ¿÷•ì0°ønÉtÜ:j(øŠPã]$Í7ÚÓüi¶í¡n[ˆ(Q“@sàóÇV+I®H¶/Çtùቮg~:ÚSNû§y¼±2¨s&]»víдiÓ–øóúÿ@¢¬äfóÒ¥KgaÛǾ}ûžŒ½Õÿ/(‹líCaù¾­„?mFÒ,'"í“kŸTë:¿,Ù”qô›40-[¶üšÐÔkØoøÍ8¹làVå¾zü x µŽ„&¡¾-÷-° C ÈÒ æe›gÒ¢.‰–IÃ"ÀÁ,$jþj&‰˜Z¢ÕQÅq%É?ëØ3žaß麬iêØËˆ«W¯æ1‘׃UêY[¶lù¸Y³fmO<ñľ×ÄO²>ŽÊG­4­XËnÒ´% ³µì®q€¤="g¼=pä܇#ñ^ÄAŽyï½÷–ò9ÌØGý¿Ÿ' õ¶97„Uh¡ÀßPü#ã\d¶Bˆ™Ar„Î_Ž"OôÐŒ£ŸäJ¿,ÖJ5¼1ÑuÔ"d´#Iš¿:­j•Kí;s#xàYÓ²eË^‡ú,êZ±"íæÏŸÿ(>Ç~éa‡v2ê{ÇòÕë‘@Eh©…?m&R,íÿ"yf˜š¶‘S†E¤U–ÊW #«IC!ÿAñ[Ò‚ÓL¶ã´'ñøßöñ³’Ä:oä1{¨FêFb¾äñwüÛœ?Éšà³ß :´WëÖ­ëӧϳóæÍ[QeÄ[ŽEÊ>&‰–£mD®é÷ɵÂL÷¯÷ý¬+&ñÕQM ŠNû˜…¼|ŒFtÃ×"Æ9ÜÙ8ÿ½Aûh•Å€@ü?ái»²nݺêù&mfË#Ô)áÉN"IR‚Á.ÂFb%!ùÕ`Wº4‰²É2mIÍxjß!XƒP3L öªØÿâô îiv8{úE(î?Κ`ë{¨ç±/|á ßûÒ—¾t¶ü$ÞÇ3Óº…½®ö,Ó÷ûäX6!q±fœÂÁ8i¿.•íÇ© ¦ CÿùÙ‰mOàùõ}Wù]4ÄuƒYÃ*6R"j~Çѳ6Ϥ„1|"I—IÃ! â$h"Y"_"cz©Z'GHóE8½ '¿ÿr¢^¦S\*ÍëêíŽ8∖ØîÑÿ&Þü÷¿ÿ}ne¦+cúôéS±½ä|D¦ó°aþž.¿—ă{½ƒqÄ„qÂWéåCÆËïÛŠ~•Z¤[vf˜cAáØÎV§HŽ„?pÇŽû-¼lwpŽª,Øj0.cyA¾S°°†EŽ×íÂtñÈ#4B¨yŒP‡)Yd)H¦ü°È—H5‰™Z"bMBG¢'rG­°t2ÞwA"Y§ðQGõU”ë¶nÝú2çK‚u*'“ëXÏòåËïd½X©>Ÿ;oòz¿ÿAœ„—È´Ÿ—x+]~jjii­VS“@ûdÚ°ù~d3É& Ó¿ÁŠëŸ7n|s6ë)†²±Íãox~­ÄoCñ$vC¿¬†@‘!ŠPãÝ&›k"2¼ꈀ¬K1qRífúµJék‘° ©i# ’9‘ŽL'þʧ¾É&Ô´—~ÉvÔ´§œlN-ÇO<Óo’.¾øâ_€ŽCU´ó}9¨² «ˆÍÇÙ ûÈKA›Ò_Äð¿naºwà 7Ø\¨ylur%8 I¦ùc‡ZiÔtŒÓ>iå#9c~Þ@t¾ÁêýÓA?ÃY¼ŒØÛ.¾„É÷sl÷x•xæL°z|ïÞ½¯Ã˯¢ÒúÖíÛ€}-dÆÉVÔ¾cÚGyƒiºNe"kµÍé7É" Ó7 øŸ`œnÃÖ¤3¸úšÅꊦèøi?F‡¾wyÑtÌ:b†@0B]GࢺŒ«=ñ_’$TùIŒå'Q¦_šitùý°âcâyä§f9Y“C9¤ o„¯0½€ºðc9êepFT©lÀâèW˜š„˜¢xf¥I+^ù©ut¬Ž zL²‹^®»÷ÞÏ7ðœ 2=-»5OéÀêuà7 øm,ž^YO â@€œbÆŒaæxN76ßDdö2Ã2"$ëY o€xÔA¿O”™Æ°ò)¬<~<ý” öãb²ñ§¢¢âxìÏzsÉ’%Q~d%M S¦Oz™_aiÆù$Ù'ΉâuòÅÊ´‡aȾ€ ŽÃ-÷[âÕÖó@§d¿V«Á0 ì#0a„FýúõãBAJáãoðàÁü·IØ u FQ„ˆÔ^]]*‰WP'b-R–ˆTëZm…³®+++gg½’ÜW ¬Y³HqЯ<ÔÊ“LóZ ‡€ìZc³‚ÎO>ûë/ü#ÓYÙ 5 B'wh-] lÎI‡PéF¨3+YIªâ¤š7„»Z° L÷ojiå÷ó0.V>Ó‰‚©ŒWšüɾ }?M®k‚å[8b@¦Ï™žŒ{¬ +ÓWàsÚ÷D\…g†@ƒ"€ÿ —a…:mð´¹'-Já3¡UÎrÆ –öV'"Ö$d"ÍLAf<ý>aC0&Ê“,¬xÓ{Hö° Æ3ìÇÉïÛÁ÷³#Ò{pΉdz(ÈôØDö™¾d:v¼bN*·J CÀÈ8268ß'«YsU²t‹Ï#Ô€•ë¬b¬>HÐxù7Q0¼ÞÂõG@£ fÉŒS¼_“iùqšÇ‰8âíO¨noà ÓrTµá?· IDATUc†@NhÕª•Ï’ÖçIÓ-!3ŒPg†Wƒäö½V­Ù’5ÿ¦ †SµÕ¿.U¾bK+Ã*åOxòȃ>ø¯:t.AV1IÓ`¿à]c:ðã#ø`Ë_PUsØâÈOsP­Ua†@ƒ °ï¾û†ã“Î[ Òð¯TÛ ¼¥Ó|’kOvÁO²Æ›‚Ž~ß)>¨ý<%ã=zt7üËÿ6|½rĈ-X…Á!ˆ£y,ö:ÔcÒ@àÔÑX™æqxûÂMÁÊô% Ô«Ö0 œ °iÓ¦PÜŽ¯’ä¤A%R‰­P¡I°“t#Y|’ìõ>|ø^?þxÚãzê_Sæ%àáñ&NK{úøòòòñ€íŠÌK±+ óÎ;¯M‹-žC{÷‡û¾Ø9*ŽR(ÝÊËvÆ÷§ü÷¿ÿ}ݬY³ø5PCÀh š7oj…Úž‡Ñ(Ô¯˜h«´ÒŠLª}[·n½¤õ‡ùØG><¶oßþ´«È—b+@|l§µ)ðŸˆ6°ó~(íi¼„xv¾þЋ¦· [ þ Às½¯êÖ­Û Û«Ý0ð•âP„Hå|Ñ­˜­c„º˜­›ã¾a;Å…˜TùÒWóRî¿ÿ~î¾íl‚}µ¿ÊËFZ£"AçK¿½víÚ6¿ýío+°Õc{$…Z! À=?#žðÝ„,Ò0r†ÀúõëCj̃F¨#´ŠêÁ,å¢pcr,U¬Væ3X¥¾í[7«égæs[­mõC«Ò›êW‚]Ï?ÿü äÛ…çÀБ#G¶ så1 ì €ÿÎ…"Ô¨Ýu„&0B!˜¥\¶{Éô@¬T½sß}÷-Ìg,°J½íû¯xï;v,_P41 :"0yòäU¸÷_ÄåM÷ÙgŸo×±»Ì0"@ I“&¡5îY#Ôà­"ŒP ÓõEàôxOÖ· \\£ÓîÃ³äŸøpÜ5¹¨Óê0ŠGÙ?ÜO¶í£È mÝËoÂjôÂu„¦4B!˜¥\&Q­Jý¹p™æqA?à/t´ý§X¥îRí¶6ùŠÀ–-[øáœ¸Ÿó„•|m§µË(vpl^¨jÜ«F¨# F¨#³T‹ºð ¿ŒóPpÓñò×ÜBÁ«Ô¯¢­¿‡kŠ*ï.”v[;÷ €c÷Æ>økðƒ¨ßžXó5¿ÿýïù^Âópql—öÑF°:  €û/·‹/,f! ôˆê²bŠœ–¡íæŠo!usçÎW£ÉëÑæo€”ZHm/õ¶òÌsÓø(~ÌÝ ggŠçÁ€À½Ûö¦Ø¶<°‡5¡4Àœj…èÔ|ïÖ4Bï*ŒöÅ5N÷(ˆí>¤xò#€ÇUêaÆ5õÓÍŸŸ€@7™þ=4·­Ã£ñùÙÒÒjþÕüzü9ì2?P¿PZ½·Þù¶_…"Ô¸OPGh2#Ô‚YŠEát#pS‰¾¯[²dÉìBÄàí·ßþ5Hõ›hûá;vüI!ö¡ÔÚ ²ö[Œ»°ÛFô}H¾Ÿ,S*ö™:uêØäYô—sËY¥Òoë§!OàCK¡5îU#ÔÎu„`–bQ 5Úîñt¡~r˜íÆs%öuGôç¿.¸à‚Ž¥hËBé3öLß ;A{·À}nù{¡´½DÚùû Ù¶1¸u3¿K¨Ñj#ÔšÎu„`–bQØ&#Ô ¤·Ý÷^¦œ…><Пm½ÕO3þ 2=6ú1lµ Û<¾KçäOë¬%D¶ù Ügð~/,w2T C ·lݺ5Ô 5ž¥F¨#4Mã˲¢J ‹.ºè`üþ ºý¾”ö¿…Þ}´+ð2ÇIèÇ…Þ—bl?¶yü?à¸WšG³}Ÿ/ø1WŒvÂÓÍ_|ñ_зïâ<Üï@ÿ¼ûÙ}ÂøE˜²ÅP7~$áÌÆK‰r\€åI>ŸÂ– äû)Òý{~Â<¢öeè˜ O9<÷Â}P“ô/ó½ŠkT\KÞúßp£áö‡c{€|›¡LŒP7ô…_1Èô·1¸Ë0ˆ§áKi[ ½G؇»rôèÑ]6nÜøy¡÷¥ØÚ•éK@¦ùæùáçc›GAÿG¤Øììž <}å»ÐF¨ƒà¤ ó™š&Kªäú\›ªÜRH«EžÓÙã»Ö5ùTØ-¶Ÿ‹M$°tg5ú¬H¸wÂåKˆx é ¾sâ‰m ÏfLŽø™†2H°7Ðï ÛÃwŸ~‚ôƒQÇû^ZN½ÀĨ+Ú?]_G ÓÉx`ãã?¾-L^Ë“°2}jú5kÃÃòbé‡rS³ÕRWÞ{ï½é°ÕzLp½ùâr]Ë)…ë€Q IÐgÎÓA·â¹`> ׯ.&‰ðd\0õ–†C í$jûöílsZA“¹@‘R§)Ü7‘‰ģ’J/qüáúÂq™r6Êá´)<ªvóÝ_çË4/°Ðóß ;¸ï{q+=ν¶BsÈ‹£B~ ‡Ç÷Ǥ¹ Ge=S½²^äX™>áà8Yý{¦'å[­=µxöÙg?‡íþˆ”Ñp=áÞ®«´câc:ǹOÌùý¸àõ ‡É£¼¥ªk¬°¦y•_Ø*\æÛs£ÒÓ}rØj¯OµïKZÂÍ ‘—+Ç߈â¯D/C\+¤¯ƒæJ2Iy+@Ãpg¸„‚üó‘À•íO‘7¶ª¸C¦£L@ü 1Ÿs¿A‰öK Ãßi/ÇÓrªŒPçîâ© –¿Ü7cð>Å£²Š§gÖ“|A„lÚò0ÆØ^8ã|<öæÞ•/m³v¤GψŸÀn3>ýôÓ§Òç.Ï"dê4Ã~œV|²8•!­ü ›N@*âëÊjâ/Raa®p¬<Ù÷A,œ¾ÑåK¨#l·pHFÉãéð“ôî\£Þ^Z /Òo@É4å *û{¾çŸàùùŸË—½Ûê¤5¡æþW¿]æ7B#€/ÕíûÄOlÆò:¡¯·Œ†@2°ÍcÏÂ5)û9ÈôUÉòZ¼!P`,‹x©¹ +.‘_q~^«øDå(ÎtÝ!¢–_%ižSš4ÓSùIø‚e©Ì¬è3f‚á¶<]áhÖ{ƒî”,ŸÆ,Ûÿ È÷³xÞ½%<¸*Üñ+¥1ém¡>ާwƵËâþ*PÎ)È7C¦Uëˆ?ZñÒHç‹Ð\%_†ôΊϥ¶ê\¢]dua¯ñ¦"ë’u'àv¢-Z<¦4Ãñ7F¦óÀ(Ö„z!€É^¤Xåh«OŽG­ø æõÊô3L ÖUkS!à^ùƒZ2‘f^åg:m 0É(/NÕŽHÒ¶mÛVÖ´iúþ²Y©* ´Y„8Õ%Lk|±¿Sº‹âéÿ×$Å>™&žZµNöÍŸ‡„úÐ\ão³3B-$LI1bD+ì/»ÿþû×&ÉbÑ"€Ó7{K¬œõûßÿ~M C‘“®ãÆOQÑYNôÈIV‰! âdBE“hQ8ÙûŽñtŒóu?„/ƒë·7œI~ °͘w‹‘¹ö‰¶U_\½„%ÔQµå¬FçxÞ4W‘y|]_j¸£à&Ã…‘î9,/pAÎÉq þPA#Ô¡`²L%ŽÀRôV©ùű%Ž…uß0B †L‹8‹D‹TS·†ûÜ0VƒÌv÷ë×ÏuëÖ͵k׎aF›äõë×»•+WºÙ³g»O>ù¤+ª¾îB¸Àý N?H ) ÓO{ú:'¤:ìK‰h[”Dõ+(D˜«Ìà^‡ %…×Jb§z(×Ä3‰²_¡ë5B*ËXªà×2iŸ‚›þB|r>3¯T±°~†@ƈ ˆt‘dÑOfìû> îjügl÷ÕW_]†¯N–µjÕ Q& ¾¦ëž{î9wíµ×ºùóç‚öpßîåpÁɾÔ$ÑŸL‹\gTïØ±£¾–XÕ‚ÔÕÎÔ¹B¤bžÜl<"ï›ÐÜò±Ü ¸IpÜ.#‚迼O ÂcòÞ¯òîù‹¸](wb…Û.‘(žû¯„PÓÀ&†€!ìã]Œä»áøúuìi˜"¿%†@m j†¯}eáÅž>Ñò 4ýtÄ…dšGÅÈtŸ>}Ü¢E‹ÊHÜŒL•<þw`èСnÞ¼yîŠ+bÛ„› iáÀÑŽt²+5íJûÓO¡?ëvËñĘ'™æÓw4Ü7áxúFõžh„7úàšr„ÇýÈO øß‹‡Ï Ä+xIÜóª"r­eä\×kõ(8ά÷È‘#[hóëÜìµk×Þˆ‹?€û*ÎGNvC×¹|»Ð(fðŸÝ»w߀çÇõÅÜÏ}ãëjúI²OG?I‰´F2=gÎסCMòVwÇw¸ë®»ŽÍ£ý¸ŸšÿF=E¬ioiDe—XçšP“H£O|i-ü‰î-ì4„«ÕA9ËxÜó½ÿ/ÁS<ørnµÄë?4ñ»ê„{h`C ø×ã ¸ÿØwß}ù‹¼¤„Gâ<äŸÆ;};ˆÿeRGF]Žñô ÜØ:a—˜àI0šCÿçדt­àI+ιr"_Ô'À á6ÊÊJ‡c#ƒ×[8˜0a‚0€‹Ó±ÿ.\Íq.2--{3ŸÆOµÈÿ§ÇAÖ_CІ¨V¨?õ ~Øó³Ÿ\¡Þ?[Ö÷Óá¿<ævÍ4?XéÞF¹±›Zd^É3åɵΚAsÝ«/û`°ŸÁËó4ßÏ~mùW¶~ðAñ\;À0!ÿZX-‘{ï½÷3h-Wú¿]­¶VÖÜ;oàúõkݺõIõ)«€®åü*E¿ãD¦EÀør›ãži[™&…!\©¾å–ªÅWÌ £Ñê&p´-í-­q@u »B¹<B/çÎxÇ&Àý…q<šr'ò·{ž BIµ:Í#y¶õ÷c9«úg(Âþq¶§!ß®xžœ+ÜÄH‹.þ·âŸL{A‘fÀË?À »p|ÿ /ä‘@& €ÕɽݟqIà¸z\—[ÖÂFàÑxó¿SØÝ¨sëE¨I¢Eº¨¹Âv™0C EòK~¸‰¯‰wùßûÞ÷tþe ¡YW±ošß?¸mÄJåøžÚÌ ±Üì[¥†þnÁw&uD¢E¤©}b%ÒE}‹âG[ŠI¶®^ìþxïMnì¹çºsãnìnrOÿ§[9ÅjXäš5ã¢lL8h5mëÛ\¤Zc#k„Û‡Bq;܇ ¶ß¸ ®âú ôâê²õ&Xm=û‚ .¨þ©­| C=á?îãûï¿ÿoŠ/e Bø;<ŒþŽ׃÷ÙgŸØÙI¥ŒGª¾c\Ýœ.^›ñƒìT¬L¿ž*¿¥/ø1Å`ðÍÿ lûh^¤=õ‰’Ožè'Á¢Ù¢ŽmáÇÅ!;ÜkþÈ5o×Ã5n¼›4uª›w“îïF íãZ79É=üÚÅÑÝš½ =ådkj‘ij|ð^ˆœ‡á]ŸuÔlâžꮵzOªù2E rCfÚËŸ?àæº­iÓ¦«ðbÌl Ë@¤aë¯í•ö‹¶Ê^Àa7È_P¤üxuÍKæOK€ËM?—£mp߯ô—ò§uÖ’\#û¿‹qðwÔ»/öÓŸšëú³YƹHŒ4«£ŸN„Ê'W"^E4otüQ÷ÕQw³ï1éU1ÆÝ>q¢›xû5®‚;^c2Ëøj÷«×xHCQ m)»RûöfG5ü1ÂøHÅ‹éÊ5B¡ Ò‹èFΠז5<Ò†cb nÈ»±Íc9WÇÅ/(ùíqb*¾Òú;`Ŧ_ùiæÇ 7î*`ÃÕ{¾xò¬ê?g¸±mÐżíƒÏQ:ŸL+Ž$KN¤«(Æâ‡¯pgݽ0Þ—Aî¡yï»OÝ뮼ôRwé•·º§lq +o¯îëe_½Ú-ÞZ,m,BíÛ›~ÚÜ'ÔŒË ±»‡Ïe#Ô0BTBƒš1poÕ8#’7&½¯À} ‰Uë›ñÕ³>YxAñÀô)°‚“PZ.U`œ|˜ü7BXÈßuÈôS†!G€§p"6ø)BTD’DžØE‘++ŸPÇ<¼c±»k„>„Wá^øx¦;§wû€y›¹ž§]éæM¬ˆÇOrO¿¾:§ ƒ´% µ¯é§ÓxQÙ<{5ÓU`„:B¤Ó°&†€Ø$µSÆ'Àøåû_8.îØ² +wÁñ÷’G¿ûÝïVƒ0~ nö—¯õñ+U?ÆÆù±#ËXé‡K ëwmpTâûˆ} ®9>ðsZíãŸ@±S|NÊ‘Pkuº(žŸËgü¡ú»Ò÷ÜäNªõ6!¨’ÞçþĉRºnƒ¢‹AÓ–ú±$?Çýêg0¬øzk¼«Â²Ó žÓF¨Ó¢>ohC@ÔX¡V$µO®ãñǽ±ƒÆŒs<âR}24~IqªøÖ{Ñæ™Ž1q?Ç Èô@¦ï/N«[¯êƒÆÈ£"P·}‹/-|ðÁ <íÇ÷ÜsÏÍ¥Õ{ëm&p«ò¿€‰¿Iyyùé™\[ y}¢D`)\$+ÔëÜ[ÓgU™fP?×¹wƇxÉl핤8iÅ×[㿃¡¸î?;‡ºÞhï)€7³‰!C7WÚj’iþú…Ò4Ý 3„ÆÎç)lÚH…&ýÇpöò×çL¸Rå-°4‘‘+Ÿ0‰Huu1ØÜæî _™x³J-,›Ë¾ ûŽñÊ“52Ë9: ðœÏÃä³<á qM ˜àÒêøú4þ‹Áf†@]ؼyóqí¿áVÔµŒ<¾Î'4"P>™áR\w%LÓš¹Î$Ô”YÓÜ»iÂÛá–¿ñš{íµ×ÜâÒN;UåÆ_ÚZ{äew_³ gU›‡F¡ŽÐ¶B!˜EPTÚ-øAû6Ü÷ì—mXÛº`4 øz7ÝvoÀ&d»j'_ÓOWd„=ú\pVº…˶ºÞÝSl¤ÞºÐêõUÇM"½nœë\¬..tíÛÕ÷³_²½Æã˜'ò•ê°+Ô¨Û5­‘И&†@ Ü„é– 6a›/!®7ÈR#À£ãp¤àsxQï Ô9-Õ0Š‘%iuÍ'SJó㔯`õ¡}ûU·}Tšo8}0û12Í Ní[tÈDvõm^S¶ÝÝßõáî®p×>½8aŸv|0Ý]2ô¶xZ…«8¡SÂ|´u0¬ni(©»B­¹=ÒÊK¸0#Ô%lü`×í¡æ Ç›ê¬L?¼ÆÂ‰رcÇÏ‘²ÐÍß$ÎUx±øJæX}ÿÜ}…×zk±!ÐàH ‰®‰f[wÑä‰ÕQ·Uôpߺéa·xõÆØÌnÇF·xæ½®O‡¡®2žkäCÿíŽ-ÎAdcjöX$:¨«1‹Ê–P£>Ûòè(Çu„`zQxã¾Öêø9síÚµ×zÿrÙ~|5q9~ Ä–a€áÿ >œ/ª´œwÞymð•ÌèÄñøupAwÆodàü*"ÅšE´D¼²ßšÔЪ÷¥nÞäË«kª?Âõh×Ò5Á¡eMZº's:dÐÓÜ}çÝzÙUÈæÒŒ÷ýÊ©¶-‘º°à úBËX… g±VUmùXñùçŸ÷‰'žØÕý,Äñ¶mÛ¶;ÐîÿÀõjݺõ%…صyäÈ‘-›7o>á£@¦ß„;Wi¦ C %$PÁ¹6ë¤*e‹²˜Øûü»ÜÚEÓÜ僒URá&ÏYêf^?Ä¥xm1ÙÅ…ŸÊ¶©Ò"ëNùŽ·„eã9ù ‘ +*‘ÈP —µº†@ÖºhOoœ‚rXQ ®PŽ/š9yòä59îËã¾t¶¨uAD·­x`i¹æÆ .¸ m4+ã&Œ;¶Å¾ûîû lð\¼}:Û>ɸ »À0rB¨æV݇¸»fîvkß_êæÍë.œçæÌ™ã.}ßmÙý”;¿—†nb6ëOdãDqYkçË…Û–@…ÉfÇæy(…„‰~€„¸^Mùéݸqãæ6mÚT7ÿ6ú!¾„øOD$êwu¾z‚7?ñ¸ í@ú‚ysØÌ=Uáã7Á~ãihßP¼ øßH½'5ÿ}Æ kzÈ!‡<…öóþø5ÛY>Ìÿ–[ Ü'Çà¶=÷L¡µÝÚ›Ví»8:“Ü"À-Øž—¶R<×óbžLÛÐÉ/D©Aá"£Á0ñ‘ãXÆùñŒSz2Í<áÞ{ï½ÍhkL°Z}ÿ}÷Ý÷rÑödØã…·oÅ1/ãcReѪ¿Šk( ’pÜ6¸Q$ ÕŽLë4hPãN:=OÁµ«Ðþ“¹7<Ór,¿!Œ¯{‘¯/ñ&¿å1 ÄpæKœR+Öu-HêAR²RE·j <B‘8úý°âLþ º&ˆ£ Æçm_­Ú‰öî™~ý¹çžûiÛ« þ KËV"Ø Ç$•”·%¨èÎøCî×q“ºëZ ÚØ¨{÷îSpýipk°2}J¼u-Ò®3Ò!ðzü^ýnºŒ˜.â"íwa[·nõãÌ_@`ÎTk9‡V™kvŽÁØDã1×Í-šúHFJNâíjÂDƨåQ£‰&–_Ú£? 9Ì[RûëÝÿüç?ç-[¶Œ7Z®Û)ìü"Ä™6ðÛÂ8†•æÛ*hC…]؇ Ê\ð™å[€íJÜw̘1£#¯ ‰öMOB‘$6Ðî!X™þ¿«°¢ ZàßÔ2ã­XµOVäO¤?e¿?üÐvR‡B”-[¶¨ÙÕß‹TD\ç”dÛ§Çèç(H"RR’€T‘.ùEÐHØ‚Î'v>ñÛyý0óù± üØö1jÆŒ«°íÂWXú¸)Mš¶¡_Zv“Ö#Ù·ÁHõþð‡Ï0ö~‚¶² ·1¢ýù(ø—ûÝhãh›ÍXy9+ÓÿÈÇvZ›Š õë׿ˆ1÷Æ^Wü ë]D½ ’)’jkvsÿ¼þúëT&ˆÀÒ¥KÕê qì+­t†ƒãAi‘i;6/2(3*ˆd¤dj+ I—D~j‘¯ ŸaA\{8žØ üür}?²–L›6-_œèAÄ8~öœK9übãÛp> èˆ=óè¡E›Ë!¡å*X­xäͪüö·¿}duêT^^~*ûAV+dÞµª IDAT¬CáØã} .»Ž+-ßÂi/ס»ÄÈÇ|'ˆô¸?~ÇUj¾]È¢gû g’ú£0Ÿ[ܶiÑ¢Eû.^¼Øa«•ò˜.°νøâ‹jé"xhSÙ׊W^¦eEpÿ„â! 1f¥ÃyR¨až4'{Í 0- ïOÇ8_ó„ƒËàúÃq%Ú$?àD4Ž_íãD=ÄD®©}?í óyÓ¬øþЙv[߇Õßðç… =×—kјXáŽÏÌ?Ÿ ³F” øW5·}P‡»ª!îÑz‚Ž$é¥|ÔÜwËÿxóÍ7»©S§Ö³ vy.˜2eŠ[·n«\§ãDe_ÆÓï‡'I¯ô:é°çP£p·&!ÀOJGTTþƒ3‰”$H¦EœE¢–k ÿ¯à†ñb¡Ýýúõ+ëÖ­›k׮Ì6É!ø·°[¹r¥›={¶ûä=»Ü{hW{ÿ'-ÍN¿4¼ÕÎ×9¿@\ïÂäùÔ/aÕzÔЂ6ýmâXß2=d:¶Ÿµ¡Ûeõ—|Vc,.G¯;bž€qøJ¡ À¶£­œ;81p±Š~nYã"Œ\Sø›Å]shº}àøåÑÛà?ÿüóeƒ†×$ßÀ„ÝÑGíV¬ —v÷À͆ãiYÜTMÍ…:þÇo[Üm‡¦ÛÇS±£œ‹p¨ÀPð“gQv:™vòÉ'ÇøMºŒ–ž’Y¡ŽC!bM­‡5€|øÉÏðapPãß󻯾úê2ü;¼¬U«¼ÝúЦ–Ž`²uxh¸k¯½ÖÍŸ?ÿôüOp$©ÁɾÔ$ÓjÚØ×±90Ê+J'¨ïä9•÷yàé º̳¦q<ÞqÆ™NgAKσ¸'ýqVÙ¾‹z †P0Ñs†Ñú1O-?ÓéæÇ³H€*†¾ûå—_.;òH;=xä­à Âîì³Ï™æ&ê¿ÃÉ®¾micŠâ®ŠÍÂߨĢܸ3?DNËŒ¢–ÀÀò‰–O é§ã ’i~Ý$F¦ûôéã°·­ŒÄÍÈ4PÉá†êæÍ›ç®¸â ¶Š+AáÀÑŽt²+5íJûÓO¡¿Aû’¹ü¸yx ­hFx•>û쳟£?ƽrÈý^’y œ#€˱ÿŽ`<ž§û5çí¨G…>a¢_aù¥E¾¸BÉÕJêØ>PvÜqÇíΣ÷YÐ,>úÈrÊ)±EÄo„».¶Ò Mûʶ¾­KSœÂÔ‘J€÷¤*›m1‰¢ßòá ,>˜ƒ„š$‹ñtô‹Pÿþ!$Óü\j‹-4Ég®¿þzwóÍ7³‰\é9ŽGQéÇÉJ8jÿǽԌ31 èümœ`è¸ýƒ/¾îËpîôÓOwøï¨ëÛ·/ƒ& Œ·Nž<™sÍî 6Opóôoà–Ái›Çgðs›ÃÜê!§-œä4GíŠrµø…^øêHûÅQÔùWl/ú&òšD€oö¢Lû}Ô*¥|A2}2á6ÊÊÊ2#Ó>tùëŸ0aBìÇ~ñ¿ ÁÝG[óÁÅqN-áCLã`ÆI##Õ‚Æ´!Ððà~|÷åupÜöQ„ÚC?ØýíòK󼈵È5É׸à?ùä“Má\çÎwsÌ1e]ºtq{ïÍíØÑ ~´ÄN>ÖÑš¤$Ø5IJ~G¯ZµÊ½õÖ[»_yå‡ÿ (:ž4õ™¥å|ûÒO'ÛÃ[½¸#0¤™‰°aÞñ‚Í#¯;’h!EM¨=›4‹@ùDšqZ™&ôóå6® ”uèÐ^“@û-Ý-·Üâú÷ïÏÉg(ÿ:Þl=0h[ ÃzÈ1ÎÄ0ò¼GAH¨Ï4hÐ÷gÍšåÿ Σ–Öj Ÿ/œWôÜaù©E°¨Ù'‘0ëíˆã³j6ÜpàŽz÷Ýw[ÂÁ­~øáîÄOt$Œýë_#)œÄ6skÖ¬q ,ˆ¤Ì<)„v¥–½ ÷ÑŽ¾cÙÙ÷ËþHήÀlkZÁ<©±™6¯eH@©ê "Õ"Ñ|€Ñµ‚;òÝ|1x‘…óœÀ⸂óÎ;ïÐŽ½àü§9rìý´qŒ\ódÂbb4<x1öM¼œø&nË/víÚõ´hZ÷*T ‚…ÄŠq"X¾Ÿq"Ò>!£ŸÄšÛ ¦ÃÍ„;®-Ožâ¼ÅçŸ_¾C°†¤œÃð¼<訣ŽjÇ+pZÅGPtõ–/|á Í8∮(s3õR¯À 6^R^xÕ>jÙIzâx¬Ô 8î™&‘¦|'Ò¦>‘ÖP¹ÒÈ‘áÊùŒ…‡¶Å$"Š–P{J‘è &‘&tÇÂ5!1³DÊÀI¨ÙòÞpü—œ/zQs(Ì<|õÃ…§ytìØqö¤r01ò¾œxSü´B!ÔÄTÏ)ÿ9#-Í9G$Œ÷¤v4Gñ™D?e%Ü*8îÅÖœEÍt擃7&Á°â]óæÍ÷ƳàĶmÛ¶Ãv-\¸p.dqu†zzpbPÑõ³Ï>c›ÿQÏâ²y¹ž÷Ò¬‹~ÚI¶¢öm¤}Ð>‘¦_yd[i•Ã0ýðfO°8Ô( §F¶É$"xC—’èEí?”ˆTÏ™6)L0I¨á|¹Gã› N<´¹hŒã8 PûÕXd1ý¹ð ÇD7Úµè×ÑÅÔ7ëKq"°cÇŽG›4ir&ýoñÇ O£)°žúäŒÏ› # ã3Šš¤L~’6ix«¯ãæi–Á|$hÌ#§¹ Qɫǭp:ÅP¼´ÿ¶mÛ¶¼ôÒKÓ—,Yòaò+2O9à€øBnù`¹|9¯DÏâ+»ccfÚI¤šZa_ë:–E¿_&Ë–c|VÄ[PLY¾ýW6%<'Špd|a]  <òë—=ãˆÜì?ÚR7Ùá–ÿs†«|fŽ{s%›À«Û-vÝñ†öàSNt]ÚòÅîüuË»w×lvûÔÕuiOî‰ÿ/&ˆ‹ªµ‰Ê^ýÎnÅúí®M×^®Sy4C±Y³jŒù±þHâË…ë!I›Óïk=ðÝpò½ï}o¬}Ý#x“œ§”D"øœó!7~…u„{=’B­C ËÜÿýK±õnªéƒÿ¬ðGàkY®²^Å“œ$!2z¾P‹l‘ ñ$Í9Ió’æ)DÅDÏ.^ÏgóI³ :Š4ý,£†ôìÙó°c=v(ž{oÚ´éãéÓ§?S+¸}!RÁwc«Ø—ý> .„AÄ—ŽâcM¼iÚŒš¤™Ž~ö‹Î'ÕŒ÷¯cr*Û¯ÉÕuÓ•Ô²’‚Õï$É Ѱ˜LjÌm^Pé!EM§˜LԱקü[«ëÞp7êåÆWÖJ©Žyãî—ןÛWÙ žîñÓz¸q ±Ñøö¹nÁ•Üé’(Ñ[×¹w–ak]ó6®S§¶±§xæMNTöF÷Èé½ÜehÃís׺+-jd^tò+hO:=Ì|͇'ÆU &Ã=ícŸ}ö9 ùMÓ¦M‡BŸVݰzxFu~d¼€"Á|ÿ7¸sëQœ]jäíÛ·_„çñiøÝüœV\¿ÊD˜¤YÉ•ž9ôsþáó‡ŒñÔþÊ´®eæ—Ö|¥ù‹Zsš¯½GN:餰½? ?ÎP~ó™gžy?ÜYg¤Âí$p­±“d'¾h»…×xÆFZY´…©ÔÄžšŽj:‘j•Z~‘måÕµ,‡eȦÔ…«BþþvÊG„x†-Š7gщ·JÀŒD>¼èø ’_ä‹á:Èj÷óA¦A«d»üÆ!®ó~νûúCîî©U SÇŸån™ææÝ:¤ŽÄTå×_7íŒ2ЬÎ͸ˆ[%‰â6¾5ÅÞç2däæn˜éŽ­ZÌÖ%¡u¢²[ÆÛà5!ty!2Ò–AB͇ã©5þ?{W'Eq~ eåFåÑÕ„5‚DŠ®xATŒŠˆ ˜x%‰&¢âAˆĘÈ!FÁ44À*¨b¢ bäT¹Õÿ{½óµ½sôÌöôôÌ|õÛÚª®®ã«÷Õñº¦ºšn( ~âžÕ£-h¿çá…¬®øÈÊôªvÕUW5Âä6 y;«R]'Nœ¸½*yjZE H°Jý”G›KFˆ]’)Ž1w„¤ A³Ç Þ—kx#éŸVæ)ºöüÅ´´4Æ3Ax¨>çïx÷Ýw'}ðÁ3R¦ÌAÔ”yã£'+áäÂþiÑÅÑe¸bqm²Ìq™óŠjÛe<ÑŸ¤•<¥ D)7Ü*$~¿\¯„:eûU‡\Ì'GX]ÚÐÊEWü€ÄÚ±`xÊæËO–FÒô3W ÓåÁÛ]lÆÍjvˆ€…fù*l[kV¾ÅaçNŽ9ÕMÍš(~/¶W,ûÜlGw¬]ÿÓ¦åÛ 6®ùÔ|Ž=Ïü ÖA­ZšÆq÷ï4ëV,7_".{uíý2Í[63q£#N\ƒábçv>|Ól1›¶BÖZØ£\¡ÕøX^yAÎÿr\à­l`Ó0Ô%rÐWô.íו8„eÍ,]ºô1¼{ Äa/â­äT…á^lé"] ˜ŸìÚµëÌñãÇ—oêO53¯(ž A‰!Å2ÆpÌ¡ŸËž‹8>É5¼‘ò%dŒã‡sº´2o1?É—nÔ{ì±ÅøÒo_üâUcésæÌ±råÊuˆ“QÓ¨Q£f,`Ó¦MËà|“ÑÂüË\-]Ñu@KˆK]‰%¦Ÿ.u#VîóZÒJ^’¿”!׈ê¯ñJ¨Q*ePãìùjìAJ©/]±œd’û)aR»‘ó.#Ò4r2Š•¸ÁOº˜›qØ}ii7s`m‡]Ë‹Fò#ØÞæé—ž4§54‡UbJJJÌá­šcú?mV¬[bþtA5Ó°ùáNxIÉá¦Iý"óë§yæE³bÎHszµZ¦I«£L >›ÎO§uxsS¿èsÇ“ï8½¿bŠW;™+‹ª™&'p»M™éܼ–©vÖHçN†øZ3tÌ^3ïÑ+#¸›ÞSÓ]æ .퉇~¶†K{€×1îk Üå,0'ßÀ‚1(Þqíµ×’Š< {±§"íIÈg%ìxÁ‘Çm©Qì! $JÈÉI ˜1’3Ù>À•Ú—~®¨Ð’¨Šµ¯ÿÙgŸ}2¾ªx=É4VŠßþùçï™^n¥‘´¾»uëÖ=å˜5kÖ, ¢¼*–Á-)´ÄA\úgºÄ_0–p['¶Î„\ ©W5ÝŒ‘hä5‘¯9F¯ãy0O(¡ŽNáåÌ.„9šDH´(’, # OÙl_ÿY$Ísþ•˜Ç~ßÓt,nf¢çMðn½vfØœ9qòžhzt›XéÞÂQ=L«Q•‚€Gz”˜Žíw˜+ŠËKÙøþHÓªó€hä’nÝLë-Ÿ™ieÜ‹²Ð é}‚ÙÑp™v~›hœ´<õËSe¦<’é‹M§›¸9¦Û³zÂÅq,¿ãå?õJÒr`r͉Lt.íAå†Oö ¡YdpNtöPO†(—à…¬‡!Ù¥"_"Û<ŠZ´h1q΀ý{;cêÊDiôž" dCH¢â÷|Ã4Bº8fÉøEÂM¿Œa2o1=ý4ÓLçÎOkݺõÃ@j§¼ôÒKÏ2<ƒ#Këá³èñBÜÎeË–}‚2ƒ НJ”S,óÐËyC,uC¿fy0²¯y_ÒºÝ(.™šk°mÐi!¡Aù”MOxݧ²‚ÎF+!MRW^Ó/V)¹NYΣÎà|– Nh:ÕÜÔªvŒ¹ò×w˜‘ÏÍ4ï/Ya¸["±)1Ãg,6{Àåv¬žkzÚ‘Ko7o/ß‚¯)í1ËgÞ™ñ:ǬróÙ3"¾nfÚâ fÁÔ©fêœfËòòZ°dDOîÖlg&@–µoKy¥föêæû©ý ßKô·¼rÎ;çO™î9¬EYÍ’K/õ)èÖn â§K#nùU–ÿc…á vßTƒ(—&ñxÿØów=ÒŸÁcÇ’¥ÓûŠ€"à/qH’6!W69#!³-W:i¹ZMËÙƒ–+£´öJ©øwëÖ­+vìØ±[Çî™gÅ­/áø:â¡È×`‹'&÷JoÆËO£N²BOÙÜØºÃx_ô`ë„~[wôÛº}GI4îK[°ÃìŸÁ<àu>˘ þÕ&wr"ÑÈwc7,ú…\ѵ¯Éê츞q©Þì3kþ$Ó­BŠ…fâ#CÌ€î]L‡£Z™úµª™+ï}Ò,ÙX!Rô¢ç¤gÍç;Ë5›bn!¹•˜0Ç·$­nZžÞÛŒ‹ÜÚº‹}—f¯Yûeùûf݆2çÿ°÷º^Kä5¼+¿.ý¥9¾Y…[1.ª›s˜c¾çëk{wšëÖ˜ ë×cÛjóÕ'ïš{e ¶kø£pËë}T«Nƒ±Íãꪒiæ':óÚ¶Òèr  Á¾Ç?ãë†}0èý¨¸¸ø8,–XÁæÏ}"+Ú?Ÿ+ž†)¹ŒÞ'(Á¨‰ ¿Á# ch Éú-Ç’c8ÞØ×¼/ƾÇ_ט†a¶™Ãd ³ývâÑ•2éÔÔ«W¯- ľmî3$Í5#:¢~l1\,ɱøI ÝþDdšyбýæ§ë©d’ÔûY™\É‹5ßݰègim¿V<°‚ÜîsEÿÛÌÈ ¸{v˜µËš)#nn 1f¢é÷Ø$Z•\¿ËërÁIŽ<ÅWßgG$+x‚yz 熴[×îkÉXÚ\‡Î‘ø„úSdÕá1¸l«ŽÁ^Ò·tÏ´ ¡n¾#2ò ëˆ>pY®ÔÕ"PB®(:É–½¢)dZ\4’·(QëÙ³çù­Zµz§á¬ùŸàžMö8 ÈuàîW_}µþeëׯ‘M9Ò,›83ÁP®&~ÛÝØDÚÖ¥M¤Å¬œX]¡&ÜÁ›èÄ|ÑY/‘D*=m‹ŽãåúDNÑ0eƒÌáEýÍÌ%Ö©ð’àºOç™{/hnExm·.?sŽž³³©’Ÿ]=b^/ûá.[×,ÂGaZ™!¾ðé-fí.Áø\Þ®=ŸhZš›r<.7=.}Ô¤pП$s»¢c!Ô¼/$ÚíºÓ†æÕLÌ+1P¶ïׯ_ŸÐ¦‚("°qãÆ ¸mèÇãýV]¥¢Hªi‰Û’pÙÖ&ÔŽ_Lý[\îÄGS88Öq}ræÌ™„Ÿ#±MôäZÂ#Öo¿ýöJ<ø_òâ‹/΂L•ëcYÄŒ‘Ènc)š®hêL®mýQ¿ö5.#ú—댺X¡öÄíÐ(«ŸðºOee3w=…HQ&!ZŒc‡{–÷ø_>lnîªeºàÔŠjÇœnN?ýç%Á&‡w2ƒ"[2LÉ`3¼o»Jy³÷Æ3ìµ¶ùa=º<´^Ɇ{hFá.E(û”]¿y‰‘“蜛eLýÓï0\øuçÁû±Âœtå‰M—VõMµ ž4&ƒåÕk××Ì04G.úA„Ô|¢WI%º—á¶_â…ÒEžFÖÉ“l5Ÿ.úÄ|½})ª} 9´mKÓ¸&«dë:³zÃvS»~CÓ¸A„É&Ï0cãºufoõê¦f­z¦^$Oc2W^´à=ÔÛ=÷ÜÃT/ÂN‚•üI˹ªÃUúérà”U 8_M€ O4À¾ÉZ ÔŸ&Œ¨7@à’K.Ù¯aÆ_aœ?˜ÅX±ý8—«m‘ŸèC>^<¼}~<êUvþ¦M›.~æ™gV[õdÜh|+\½þ! äÈí²†I¸]"9j¬p;NÆý³gÏæ|øJh ê¨3Î8£ÂHzÓ3ålËst˜šMq{ؤ3¡žiÓ®½i#ëêõãeÅ7<5hÜ8FÌÌ•£°‚ ©Çí·ß~³Qùo1ñˆPV@ ­´"€m»±ú Ѥ“«Ô÷ä2*s>ƒ>¡N\ðš´fÍš~”–¹² IDAT/¿ü2¼h¯ñr¶LÈžˆǽ…nLæ)øe#)F˜SâÖ+ibP %Ô• ÑE `•êÇ ¯@šú´Ç…qàR*E¡!Âð,¿"šó„Zt‡_¢êá«§OáúØoÑ߇ã0²V°Ýä'qvÇ“"ÔM XB'®SÏ.°ñÚFr¨>äëE!jéâÚºáÏÿ/~ØaêÏ!0ÑŠ´²ÏM®sÊÅ*U[éY˜LbЛöñÇó'<5Š€"pÚÇìF­Cÿ(F_9Ä“çç¬ÁÃóáxñpês4*±}þ2lñz•J@zbÍa9‹ îÖCX²Lµ %C(…ûÉH!³Gµøc¹›X‡/¾ø"ÄUQÑ!°c·I;f—x\n”q»ÂCs‰Éµ%ÄÙ°M1™þkåÊ•—–••ñ¥5Š€"°íƒÌÏ ô®Rç¬ÁªôY ÓÿF=ŽFÿ‹…L‡½R¹Žñ<ûÚk…]ÖB’zñX_áA£k´D ¡ ÜdŠÉnP_2â»ï¾+ñÕÍ1–-[&o‰xD¿âÊ}^»ÛƒÜËš‹Éõ`¬LsÏô¡°ó0Á^0}úôxŽœHS_ÂU£ èϰ²psîøüúcÔcê† NÈ¥—A¦/ßwß}gâׂۥNêf´%¯Ü.ts`öÑK_¯ §_B8R’< ¡r)¹fÃúvÛâŋ͒%KÂ!¹Já|ðÀ¼þúë1<Ô©è—.-„—_ý.×Yq1¹ˆÉ•Û<Ú`@|oëÖ­çbrÝžH˜Hš/1¡MMOï)ù†ú8W£¿´ÆKŠ?ÍÅúAö3!÷>X•¾õ¹+ïÛr©ÿ4Ê ù7ä’Ü «®PgAÉùL¨…<ŃÕ&ZŒÃkþŒø/î»ï>:jr &po%%^ûuDt»Ðo_G¢8N¼p;NÆü Æûcrz–?û.Ú»wïÙ'N”Uö¸å¢¾<+Øçb/iήÔÅ­ ÞPâ €~B3™·AèrrÛäï÷í·ß¶™¾‡•‰SÕ0w¤pxATÖ ‘–0x"Ô9ÚæB„tEQò™PÛ5u)ûš~®Xʪ%?m»gÒ¤IßÏš5ËÎCý!F?•Úg‡ÿ¢Ú:µýîIË}x-{õêUÒtŒí1À-Å{æèÑ£=­øðËqH#?·>¨d é'Ñþw£ïää^é=O<ñÄçY‚¯JÅöîÝ»&pÿ1ðÿzøO•2Óľ"}x"Ô˜w²>ÿùZñ,gV„šdJŒgºâ·Éýëa¹§­ö¥~ÿÑGÁ«&ÌìÚµË\~ùåfÕ*.Ln¢þ7¬è•®èš.„ÉuyhþsRªS§?¥ü3ØøPŘ`¿JE”Q£FAüw1¹R»víß§’Vã*¹ŒúÊBºVx ¾)—ë‘‹²×¨QãXÈ]ã·x0H¸5-ë—Ë2C'ž5úNÖçÀ\ÆÙ-{¾j»±Ð/×âWÈ·|ð4êø9½Ú‰'žøýŒ3p©&Œ|õÕWæÌ3Ï4¯¾êœ0µ2>K= iÝÚºfUäš.¸åWüÇ6¢š5kNAQ§c\û« ÇŽë<¤R|dP¼>âÞÂ#÷RI¯q\Fdî ~ì%—닲c¼q¶{ÀÕíáS 'B ±Ÿ÷•å;¡v#ÅÆ#Ëí c8ýOÂþgË–-Õºvíj.ºè"=ý€„Å|ýõ׿Á4GqÄ÷óæÍ£XÜ<ý(,·Jˆ.ùp$z¦îÝAŽ |Pá©XD˜ˆÒÏÅ„ÄýÞg€|Z.Nêÿüq®ÊEžûÁO=M¡(~#€-XMðà|U>nÅÂ8ãj¸J¨ýn8UÌ:ñD¨#‹0U,M“ ùþa!P6©¢Ÿá&ä‹. ˜X¾è5v ì/¼ðB XÓºuëïúÓŸVkÓ¦Á'¡q+7 :RN þå—_lÃùþ­·Þ2ØkÌAƒöCX®ô®ƒ¥ÅRÇ¢_Ñ·è·¢m@À—÷2j4hðƼK ‡Íxñ,ì™®òÞ"l¹mò"äÛÇY­ ÜJ¢FP²€ú`GlÁzýñüÅñ‡óIÞÔË!ÔøeM uÈ´Ê=ÔØT*ÝC¢”"ä3¡&9"Ù²I’øé Á¢K-$Œ.¯÷Àî ûì"ØN°?þì³ÏêÛæÐC5íÚµ3¯½öšÙ¾=o¶¹Q¯ÔÓ2Øÿƒ%å< ‰k“iÛ/úG’ìLFg¡ämøº‚LûòB¶‹¬ÃvAÈ÷Qœ ; û³_7nœ~ö3;*ÖR é+AVF¡Ÿ× ó0öNË'8zöìY¿nݺG ~w9?ª ÐKr6 y± Cþ£Æ'òPÛ0±á‘rûy-DZȘ¸$ÖÜN0vì!°aÂ?6\Y)A óbÚ·oßúàƒ®×´iS<|Æ=Æ4Ä!ÌFä£+:—ç´r‹Ä*XÖ‡:ud[ÑujiÑ»ä+.¢9X‰>óË8¾ d'a…}ßT_@L&^ÎúΤ¾ñJ°Zý[¸÷&K£÷EÀ¸• ¿>=ˆÚ_Gr|Í7áLŽOycjÕªuH[5Œc𤒼©XžT„ºñRªÐK<ã |&ÔD@‰‰¯}-d‹ä‹8 ãàÀ•iZ¦ÂL—æsX~Q±–q˜Vâ ¡¶³„!Zð¦aÆΠjøH– ëG¤S‹K è§Þh…Û:âKH´6‘¦_â0 ýâJ>’—´D Þ`’s²}-œŸdÆ*õ (_Ç ÙíøŒùSX_ák!š™" TB}­!fŸÅ3@4y”ßõx·á‰Jó  ÎvÔS·{„PŸ^·|@t{Î aMrK¤|'Ô¢ ›œ I³]/!Õ$eâ'a"ͼ$ 7OÓÏx$hB¼Ýä·²gößÿZ8Ú¨öèîÂK|!p¨tpâ+z#ÆBŒ…0SOBª…X»]IǼè·ódÞbž7fĈsñ帧Q¡+ðó?ý(o*§Q €vß _Lý>è‡H¼xØ®¨¨ˆ_+= $ó+é‹@¦ßL jNßBýB W u5 ½Ø‹zq%D[•ù6n½á›,zOò˜ ‰M¨„l A£K’f5ÛO²Æí´Ü—J»#âJ¸}OâØ÷õ7kÖlÈg¾ùæ›up-;Íò3º6–â—:H<['¼'ú²uJ¿iêßnB¨옼\ð‚"·{løz!V¬ÏŒÔOE ¯ÀØÿ"ßÃÖ‹À>ô‚ýÒá× ·ìa°ïb+×qùL¦Ù€€³C¨QW%Ô$dF uv’—„Ú‚R“¸¼e“+Ûo“05!kñÈ:’j!ÖnWˆ Ä ÔmҤɬðæÍ›¹E%вÓ,ÏÆK°Œ%·M¬é}‰KbmëÓ&ÕÔ¹´!Ðr[ùaÆŒ³5¹7R›Gyæu~ÔLk¡ÄGDbìØzq^üXþÜ!iÁŠø`é)ȱ.ì„;wvÊÕ/zE…G¢ê-@ª·â—€½¦ÓxÁ!À-^JCÛ•9ÐKt“|ßò!D‰.W&ÙÈø!+•$W$[ Ëûâ‡×1’žñi‰›XÙ?Ítbáuò ›5ƒýÓX8õ*8¹tÄõCC¬m] þÔI³X!Ò6¹¶Iµ¤³ó2Œ~ß V†Ã¤söKó§àÀ Õa0û`ò+Fá7Á>¸Z " èoÏ ½Ÿ÷R;9SE÷íÛ·ú÷Dä>,Ç—ßbUzX¦Ê S¾x!ÑY†Ló³ŒÕa±àeA ‡Ij ¿ŒÌ}I ÎÓ$€…bØp„D¹‰š½šI"&-Öꨄq•$•–~ûÚ“{’.0·~ýúM!—Y»víJ8•›fYnüCÊ-Šë¾'+Ö¢7q©KøB¨EïÒpëãçà‚ŸÏFÎorõ*ÈŸŸ¨1|û¤úÆHØûžú|Dû§¹ZÌ>ß•¤7u‘n‹tÞAÞçcÌØ€>vN¡iâ‰1Mµn÷ÈDó'OO„E)¡öo'—¼]¡&9Š<¥ yâÃZ64úI®¤ÑñZVªáuŒ¤£+„Œx‘¤Ù«Ó²B-ùÒµ-.ƒ7xA¦:>&Ð0|·råÊÿAÊv#›®àOÑ ¦k?‘<óš.ë(VâðZÒJ^’¿”!׈Zuƒ ·Þ‚9ñåÕ‡qò²b@ª_…<× ðY@ UD€[ÐÞçbì? }ð=ÁÏâ‘7ßGàÊ÷[À_€29¾ŒA½ù5VU0•αŠB7ûxY¤FΉj|B o u|„D1 ‰M,²É†&q‰ ÷¡ÒO.®j† ‘¦?–áý@Ìá‡Þeß]»v­ÝH¡éBœÅˆŸ®à/DXÈ´íRwBžmr-a¶+ùˆn¥,)»Ê.¿Ž†œ £ZÔžÀ‰·V9Ó*fÆT1 M®ä ÷ž°§¡^×WB¼G O’épæûUxXëQ¤Óê~Vå'ãK¬d¬͸J zâÐ¥ïs`•ÏñÄ…@¨¥ÁDÅ3îÆ'DŽ. q¢%y£K-Vˆ4¯i„L ¹. ø?NøhÊ"ñ’Ìÿà|pñéG¼Å2!¾tÅ’L‹¥n„\Óo“k¹æ}IëvY–cüXxt&ñ™ËøSóÓX2)AE@½çÑCYgòlh¼8·ÁÇrù€¼?úöx?Æ å ,+n%CaJ¦C<­‚Üœ&^&Ñ90^ ÷Ž@^jx1žÔ„°‘\‰a㓆%÷Å%QG²LÌè2œ®mqYPóšÆkã.íü؜ÙlÛ¶í8Üsf#Ø‹Œ‚=udûmr,:!qbÍ0¹v‡ ™¶Ë’¼í0‘!%+ÓG`ÿÚ[C4»©üñUpí6–R~YPÒC„ïkœ¾1}ñll}ãìO¤—SåTØ+Í­\n团„¼'¦d˜£ª<÷Å̸@óšP[:•FC‚CÌk›ì|É 3¼V4Ÿ8‘¬1ã ‰R 'LV¥ÝÚ}Íø4ñÂËïVáíÚµ[1ù–-[–ÀÉÚ^Êà2¢ v_K¸èGtÅkñ ±¶IµøÅ¥ž˜FâŠ_òüªü¹q~Ldš8x?_ZVVÆòÕ(Š@và¶¾Ìm¾êìTEKURC¿ÒTÜ”4âÄ›“¦Õ•È{BÍ'0ëiMÈ‘ˆE¬!ûI3ÉÈI0ÃèºýrâÑÍYfæÉ ^H<Œq>ÿüóEp¾Bí®ñ¦¡¾l1\¬M”…<óžø…XÛñ%/ºbl¿„yvûöíÛ oü“L‚¦6dúØWɽÜjE K€P¼²0}òÔk¯½¶)Άþ*K¢h±Š@àXœ'aÙäG #èÍ” IÌ{i4vá_ˆ–í’„‘‰Ý?É‘X9žm'ÂäÌcúÅ2Lü\+a’>£nqqñèPõ1©løôÓOùŒ–—fþ‚‰í ^6޼Oü%žÔEtB—ú]‰îè ©¦+:§+¶J«Ó}úôi 2= ùñáå]¼ñn6Oô€ jE`ÛÇfŒû|Ÿa_|9±{* dë˜ËTdÔ¸Š@¼.èq.Tãy¿BíÂÉÝxH¦ùPAWî Ùb˜léx$fŒ/«Ó¶ÁÑÕi·Ÿ×™¶mÛ¶fa{÷î]‡D4WŒ­Ê,º KЈ®èÚ–qlâì¾'é$OæEZ¦wïÞàW€Wø(Ø… Óçàø¬­ie–…Dxò· Í@<~£«YP€™q° ð ŽÎë†ö} ãKŠ úÄÁèSð•Åc°«û¤W'L 7"€vì‰PëØï¯ †P³áD™M¢ÄOb,~6DúÅå=ZñÛ×îDˆÄ?]O ÛNPU½zõZ3êápE7WŒè€òÒ/×tIˆi$\³ÜWÂ%>]I#®“Oºƒ V°êâ¥ÏéÈäXØóY>Ÿ$@ù2jðsxOPñ_¸c2Z˜f®dœpô2Þ%áv·ŸáפCÇŽ»*ž Ð?EŸà ‡Í0.,Çò–xq5\;hÃzu”$Ä0 E_¤E lbEÂÅ•M!bôÛ–[¸€®Xn3-âJ ¬½!pÿÖ­[߯êÌ¢/¿ü’_ ¼ü*”)X †r-غ]Ñ õ%º±ui“iñ#ª³Óƒm eƒ•éš Ó/ááìDN¼ ÓgäâþLÈþ+z áj{Ê@hE ä<õÔS<.ôe.¤`kÖ¥ñÄ™¾÷æÂ6ƒ}}÷îÝ?Í¥_› s µé û.ðjHZHÚ°Í{Ilq"/Ñ5N f…Zp£½Å"XÆ6P’2ya˜XçFŒk Ì}ùå—ß@a§V`f ¬™»ýdû%Že¹Ë)Ùx?-ƒm#°v V±:câý<­Œ²œçc?‹ÉqúÁi ÷Bœ²,’¯øŽºú3hã—ÂrÛÇCv¥¥¥Õ<òȇpï&†#î_qÜåÍzBRÿõÀê8àÔ¡º¦4¡» /Ijô”¤¥AˆaJ‰ò!2!]¬ŽøIÌbYY±–UPqee”+©´²b*«Â®n9>ÉpÜè Æñ\æ%ø‹~¨;ú…`‹‹ gUú»ˆÞy–Aú†H¸Š+Ó¹þÉaÔ$z/&ÉëpBIZ€h"E ĬZµjÄãö½zõj"¢^uÕUðòö+$ÓèÓ»a¯ÁCæõJ¦¡Š.¶†ñ+ÁÇpü„YPñ®^… èÈ¡¦2Ã&{.ËS°„šJcc‚©@º Ë0Z’9º6a“p!s6á#ÉKtmßSÿX Ñvcb‡Ûx iv»¢7q¡ˆ43¢Á¤{þ’%KÃ6Åå!¹ûû¾ÿ‹öÿW ž„ô¥­Ü­©J^¨LŸ>}¶¿Ý‚v> {ª7><ÖªUë]xù+Þ—°§¡_«&>í0NÔ„]ÊTâGÓ;!AÀ¡†¬J¨}TXÁmùˆ…Y5‹XäÌ6ŒcÇs_ÛqÕïÒéÝ.swt£ªUâǸ~P>­b¢»Ðì/§`ê L–O§Œ¦T‡ÚôhHEkÐÆ»ãäqhóuÐöÿm[¿À/Mˆ¨ 9q¸L%©¡ÏŒÌ‘• *€‚^¡vë˜+bH¢…¨Ù~†É꧸öÊ©{…•÷Ô¦ŽA<‰9ñìéFèŽ[:ÄFÃGM"«My“æƒ<Á$NT VrŒ}ð2Ýýhã“I¦Q‘ñ+W®ì¤dÚ›J1®*¡öU(báW{Ñ/®Lº‡:.4iÝPB¶±Ç!iˆj“l7©“{¶kÇQE"œCñK|çÚ"ÎB y_MàçîñHö6l3œ`2(,4‰"ZzöìYïßNƒ€wÀîÅØq3Θ¾šÛAB+tÈÃCˆC¨áê uÈtKèÉ¡&Á‰•^ÃÒC@ u ¸±ñÅ1QR‡ûêÏ  OAm5 /(^h|(¹ŸS?2I½­älËuëÖ}ž »íül<@Ï áC"$¶ÉÔÆñ#ˆ³wÆ „D,#àÓž¸ôª Q pLõ–'ÐSÍTãg ~ÅøÙž_xT“&øiøÀñ¾4“çd2¼d9ƒëh ÄE0æd%ThEÀB€+Ó8òM´éb/™îˆv>ÇŠ¢^`[À±Àp_ŒÿI1 ߆O¯ Ÿ”™“hüøñëñPö´£¿¡”aø¥c¦®HeoÍ9³DöHŸÙRò?wÌ'º:÷Ôì•èju«+Ô>‚‚¬Ž‰È° ²ä”X™>$š_UãO›ƒqúŃ9UŸ„5jÔHdõØ–xAñw>e«Ù(¡@àšk®ù1úúD<<7…@9 ÆC‡Pûí·ºBú¢ˆ˜Ç<jèV µ:UBí#˜!ÈÊ!Ô»wïVB‚2úõëw*ÆŸ`÷Ãøòg¼´tW Éó**êÏ—Tnˆ ´·õèÑ£A^UP+SÐàF?G?ïm ú°è¡%ôîÝûD;ãÁÎO>ùä¿’h” }y"ÔøõA µúRBí#˜ÙÌŠ&ŠèHÛ±Uá“lÊ’Kec¥ê§T^†Ìµ0ÉŽ™¾%—äÏ„¬8RìM´#®ÐŠ—ºtÀÍÈšgVÀ‹‰¯²`Œ•Ýõåíä*À ÊÇ+’³ÿäÓ­’×<çcx"Ôçu|÷QÕº‡ÚG0³™>Ùî±}DÂñ  üô{ 抙°õ€ÙDlwø%¶zxH™ÿQ€?öâ|ð%ÿk«5ÌEºvíZ£eË–wCöïðø{/u=zôÐï—¢Ï-M§!Íl/é 5ŽîŸÎYÍ+¡Î‚êt…:  g¢HLB¨u»‡€y>-0ãjUép"ÀÕú â8¢„ôßf-Z´x¢pëÆÕ©ˆ„~ÿ,ãý,•t×Ù?±Q÷OçVðÄíðÀ¤‹o>êÕè>–§YeL?‰d­„: Æ×^{mk¬èseª &Š™ Ó—ã4 ~Ò\" „ôßÐߨw<úïrØ®©ˆŒøÏ0>Ü qªMQ*i -.0>šuÞ³gêÜR¾®PgA_J¨³z†ŠtV¨1*¡N0ßîßwß}g'¾åÿ:Èô… Ó»$Ñ[Š€"ðq¼XøúïÁ Äe°ñÞCJcâ„t‹GCTë¬T-”bདྷ¿@°ÆŒóq(T¡b"€¶­„:&2™ Ô=Ô™Å7ÜKKK«ÿ“Ä÷XIXH¡9X?Ô‚—ìfa¬i ¨ÞVçé9Ë9¨H¹ààwä‘GþÃ7°òè¿ùøã“î‹rHÏ#2Ûá'ïK‘Ý? PÆ»õU£… %ÔYЇ®Pgt¿‹lÛ¶í‘ȳ&ˆÏ°’°Õïüó!?ž‚2ý/`TŒÉtÁÖ­[»(Vù Y­C¾#€m‚L³ï’Lóë‡}°Ê|cºd:‚—³y]€±¡f¾c¨õ+,Ю=j6 %Ô>‚™­¬0Ñ8Û=H³%CØË­Q£ÆÈNoß¾ý¬I“&m »Ìa“+ü 7?›\*Oþ"€-?Á¤ÏýÒ§¡–k°áTéqU­1V^?Eï"ßzRÚƒ]Õ²5½"ž5äPBí£2”Pûf³’>>È¢ a/šÇ Î…Ÿzꩵa6Œòa…ÿN›àر›Â(ŸÊ”_ ]Šöö&jÕöméã@„ßñ«–œ—Aªõ´¿@Õ|BÚ´'B> „ÚG)¡öÌle…IGµ®PÇQVµîƒ=çÕ®ŽEƒ“#03圴Ð4yt¡¤Ž¸À>xyx\îs®Æ®X±â4é/RÏ-~ |J{r„Pœ‹¼ÔSï(¹…>=jp%Ô>ªV µ`f1+‡PcòQBE%ä{Ñx ™Ž:þílœ´04ßë«õ l)Ú–_.åùÒ{AxoÄCpßéÓ§ïò[š'žxâsäÉðZ 48ßïü5?E [`ŒöÄí@¼õj•ä tËÓ¬|F W¯^MåA˜x6câYîsöš"PŒ¿7#€/†õÂOò'V¸©Š@¸êª«ü¶+Ú××ÈêL<ÄñضŒ”£Û>2†®fœ-Ї<­PC>]¡öQIJ¨}3Ya_+ 5¿ú¥ïgCV&_æ y02`?WÇk™ªn­Zµ:¢=öõÁÞ½{ÃÁk™*KòݱcÇ”Ç:Í“€$¼Ð]üJÐ/„žZè8äjýѦ=jÝòᯆu2ôÏÀsÃÑobÕðL@ý/\ ,H0X] òÓ«Ô×$ZißÀjôLŒcÇàcKÇ=z…ïÄÈ/(£—Áî‡Ó>~#JÁ ð¬}^… ¿'bVp …¿Âžô†q\W¨}Ô¥~ØÅG0³‘U¤CLÉFÙa+3rúÄ98ïbL”ß„M¾|‘«ÔÛ±zõ|dã9Ôéþk®¹f І|©ŸÖ#{`_sà¦Â:¼ñ ¸ÅÙ«yxJÆ ÇGAšF°ü¬»®ð¨&I”P§‚–Oqu…Ú' 5›ì"2}+&ÅG Å™xaŽŸV“AFõ<æÚÙ(¢ð¾?ƒEiÖŠ@FÀÊøSø•ï\¬Žß—Ñ‚r$s<(w¤¨OuaŽèÌ-&t§„Ú J×J¨Y‹ÈÀù5 IDAT, Ó0~<ÈÕLŒ½ÇŽ»*³%jîDÇŽÝÈ÷û~X¥>VQQrüêòOýrj¹öЧB±T uŽ6hèСÆÃ“þᣎ•Pû¦f<Øzp%Jý[¤ä_båtBðRf‰øy~1Èô£¨ý>EEE|AÑÓ ^˜hi­‰ž}ÏÄÓ­ð"€nìjì¡VB^5%“ÌÓX â­„:’)ÜWBX5\€L_„A‰VSnÅO·#Â%aþK³{÷î{0&óƒ?ÃÉ|¸Q£TB k×®5@¦Ç£«¾Šö2²R øÀÍ~¤ö;ôíù¡J…Hô3OÜó¦žC2ºñx=~r½£dé. ÓOcàØcÂÝøÉöáìHRØ¥F~&¿(@C{öìY¿°ÑÚ»ÀŠtó–-[ÎCûè2Í—…‡¹ãèu8hذ!Ét Øu L8t’ŽègžV¨1‡ê u:ÇI£„:0^0AŸ†½_Ïc‚ÞÇC Ó÷„WÚü— ? L„øÅ¹ƒêÔ©swþ×Xkè<øþ q߃íˆ6òì‰h/ò {¯Ùh¼€€~œípu»G@˜g¨O„e+¡öQJ¨}3è¬ðê­˜°ú]n6ËÑN' |~š¸}îòøm6åѲËÀ¯×CßbÅã†È±[ M#€-@× =”~Éu6>¢Ò¿‹ –PW‹¡†«„:ÔšJ,ôç‰P£_*¡N eJw•P§Wx"÷éÓçPô™1aý!âZ¥¥¥ùèË!Ôx@VB 5¾ŒJ¨ãc“±;9hd Í3Æ'ÇÂâ0~`±Y+ “t1Èô«€Ÿ~~ýúõ½õé:kêˆY0HõMX~nÓ¦MoÄŒ yôclzãR'TvˆY<øŽÏ•Šóaûˆ?kԨч¹s®È퇜½zõªS»vm~ÔeϪU«>ð#OÍ#;`nDLΩñЫ+Ô>ªH µ`œÕ1‘ò\nàÅõíÛ÷0Ë6 7†IúòÉ“'¸ Z`B0ˆóñ×FÒ›y‹Ï"™žŠ‰¼ÚÂj¸‚Lÿ;—*¼téÒï‹‹‹yÒÅéܺģ!sIþªÈŠO¯·‡Îö…îLŸ>}WUòÒ´YG 9›†ˆÐµjU¥[>|3à¬Bkä5¡Æ¤vÉ4úæèû¯íܹ““ôž€±ÖâE Øâq9úé›$ÓˆöÖ®]»Ž{üñÇsŠL³zeee{1μ@?~»Œn¡Ô×ÙîúëvW:ú¡ê,èP u@÷©H‡P£ßäõOsø¬õƒ¨ck`ö6öaž7nܸ>á§Ù(Š@@ßÜÛ$†Â}YÕ‚íX¥è§_V1ë¬%Ç/`ϰpÔéÒ¬ ‘…‚Q_‡PÃUBüý,Ež¸¶|è9Ô>ï tËÓ¬|@€{Ý0èµA§Ùì–øeh³À üßQÏQX™î‚mÛB+¨ ¦ ÒûÃþ“òmè£{`…UékÑO9.å¬Á;<™d-ì‘|:g+’¢àПC¨1æ*¡N»F×ê,(E÷Pgôª‰Gxø>†>Ê÷íxÑí%Ô“V" „<Ôß{DZ{1ްœ"ñÒ…ïg` ËÔíWxXà¶¼þ@aÿ{ClÙá"ÍöÍ›7˜6xš0 í*¡Î‚&t…:  ûP¤l÷ÈëýÓ>à¤Y(Š@†ùš û¶H—/dZ B½œm¸.ˆm{öì!Ûûª¾ð-­ w]´_O„ûæõ¥DÕ¬„ÚG0̪`NøS-*à'óŽXíû_·¼>Ùk–YD¿Ž-ᇕà®Ì¢)¿Œ½ Rò9úZáÅè2RHˆ2åáx‘´åÊ•+¹"¯&÷ðD¨ÑÆ•Pû¨k%Ô>‚`V¡Þ»w¯®Pº•:¯¿)© w(öÛò5Š@è Ñ@»}–‚ÊicÇŽ]§Çå…¾izP µW¤|Œ§„ÚG0ƒÈ ƒ|i§ÔA"m貓9iƒ’낤¬Î•Où&C +—Æ–€/ðÓjëdqõ~î!€±ý·š'´\„- gä^ WâB&’Øöñ.ÆY~‰¶¶-u*ÜV 5Ï5ø‹™uµ”¼ÇQBí«PÄÄ*ŸìŸÎ‹7Ï1Q=Œ:ýàîW¿~ŠVæ¯8Jm5r¼—¹bPEþ– ¹eôÍßÀnÆCPŸLäŸ yâÂY¥Æ‹—º·8¦2:x}Ư0zµmF µ`‘IÞœðUéÁèø¿ÁÓôntì_äãËMA´‰\(ûxz^ Y‚ÎoÊ™ UÆÞ½{בž== ÕÁ˜S°ç¿[y¹¨´´T™-ÔN‘{õö´Bþ­/%ú¨[%Ô>‚DVhÿò¡œÞ?UJn¸v/&îËðóê+Aà§ed~ìääÆH郠ÿƒ³#‰–šl»:¤FóÐ'{"‰ôÅø…ar¢4ù|ù‹0æ~<lÛ¶mç|®«Ö-@›õD¨uˇ¿:WBí/žÏ ;?ê’Ó/$bõë—ØÞñ'Tƒ?7] ûEÖIM~#À‡& ôSцëÁRÿjB„~1: ì{ÐÍqë8›øD‹÷BˆDÌŠ(h³Î™Ô³tÛGV4 …¦€'B¶­+Ôi€/‰êxÈ„4íÿCØ㈣OB*bB±°2y"<ÆH¨Çu Ó“&Лy…ÈÚ¯¡÷p{¢-œœW•ËáÊà!·ôR½4E5fLw=zôs¸J¾‰\œ}ÔÈðÀÇQñ­ð gtÕUW5‚î—¢/ò×B5y‚€×vж­„ÚG+¡öÌ ²ÂŠÑÏaG?ȹ— 0p_ŒŽ>&ÒÙƒzŒ 3-#<àj9Úî)ÚÁc—\rɾᑮð$‘*B¿|º [„m9ðßýé …‡Fìã—îýö¿ùF@jÕªuôÞ¶}ìÚkhŽ"àéÁ/ßÚs¶u¥/Yd[R>&í®¨êÓ¸÷Ť={‡HÕµš.p~úPìÓ½mᘠ Àí¿º¢èeôêÕ«I:užCQ§`b݉~ÙäqBEç\xì‘sB{zïˆ~Șó=D×(9‚ô µ&çÔº‡Ú_…ê µ¿xjn1À*X)‚ŸGç ØŸ@¦#ÔbDÕ @`ܸq;1àó¨Dƒ}©÷¢}XÕUñòaé÷ Éôçt•L‡JEƒ1¹# ¸ün j!A!œMCô}Ýòá£F”Pû¦fU¬LŸÒôîšè»™X9–†hSQgžìÒmãB«6ë‹—{T¯^}d8}òMYy¶_‘\«) "Û­œ­»víRýç—î=q;ÌÍ9·u4Ìjòz˜+ ²…k®¹æXH7¶.ìx¨ëÃ+­J4 r7‚Ðí¡î‹/ž,¡&ƒ@L?„"&ÂÖö£`Kñyø¯2X¬fR°Ýêhô½ÚïùòÕÝB ±t…: ¨+¡Îè…P$~Æ/Æ*Ø«°@}§àE§¾˜¼õç¥BP¾Ç:F^öâ^zŽC7{L¦ÑÒD aÆÑôtÃ=°×aUº?r÷¤™&ËqМípu»GŽë2†øž5¶ú蜼tƒô¥Ät‘Ót ÀOI· ¬ÿ ÛöÐÏŠ'D¬0o¢m FÍk``ÿga"\­õ7xÀ]†_ú`Uš[>Ô0£•Pç©þÑÏ=jŒ J¨}lJ¨}3ÓYñ“À|¡+Óåø‘?úé0Øq¢ÃȬ«`~€š‡y`…t;ªå¼ ˜‡Õ U•€5ß_ÐwB¥•ì Îåj¸ºB=5d¤d̽ž85~EVBí£”Pûf&³ÂÓQ8jì’>}ú´ÅG]Öe²,?òÆÏÉ!Z5Š€" ä x7äp7vîÆÿ’«¿¼uíÚµF‹-JÀº¾Û°aÃûy£­ˆ ài…Z·|\þ¸º‡ÚƒÈå| ~ûƒT× ¢0-CPE 2XÕ»Û%†5jÔ¨O廹Ò²eËc0Ÿa%s1 ¶å†Ô*¥W<-O#3è_W¨½‚ê!žj e; ÎŒmŠÒm3¿4—my´|E@P ÿcÝ1_ž«`eÒÙîùu»G®*1±ÜžV¨•P'1Õ»J¨SE, ñ±r ‹©^˜…âµHE@Ȱ-¬3ìH~1ÄÍY·mÛÆ3Ôwa<>§œ‹‘!»ê\T`™A”=q;´=‡: –©Üöz*jÜŒ àjt’2’{2åêy’kRE@ðé[‘Í+ yýjÕªõ²Ô,â 0qâÄ-‹§ã6çÏîq¢…:ò;+ÔX©VBjM¥'ÆO+Ôº‡:=|ã¥RB™…£o8„"-‹X¥¥¥Õ1‰?ý„_‚TŸ¹TŽü@ ܯ_¿ËbÕ«‚ÝÑö^Œu¯ÐÂð±–ZÀjƈQ÷}0AÞÓ<^-4‚®/ð~†eÂÙFƒ–'•òÐfêBîbêÝ›6m Íœ’J4nR<j´Ï{¨ÑföÝûlÜüqë·°ÇEüÂ_H±ÄgÛ´5™(ñ#®ýÙÃãû"¿²â|nù÷*¡òÔ <ôÐC„ü‰c)Vv¤–ÚÿØ 6à ˵ <ÛñŵŸãóѺϘ >G´¯;W/ήW¯Þr´¹ß l_ƒû:×ýè[ݰͥO²z Þœƒ<ñ8™¼²eË–ŸFÎ}O–TïûˆÚ£³íYæÔ¶={öü2?Š>ÅmBjòèÖ¡FŽ»åƒyÀðþNØ—Á„x\9>7çïÈöTØå°ïÁ6@¸j›”_‰¿qN…eü¥°?A¸ê.‘8$à­à§¥¹ñn…]û7\G·"^Üí%NÊ þSBApýÈ#4Û=0ß ynFÞ û ý|±Öé¿ágô÷pó0´µD+(y³B­-|‘§5ì~X}~{Ç›Ùàð4ì—žƒûý¾ãA¬Jÿ6W¿Ôg×-WýÓ§OßÙ_¤üÐKNmûÈUÌUno`Øš0ÆÙ¸ãkdì%GÜ'’_ëʘûšÅØ~ ûR8¼y—ã@²¹_ú$ÄY…>x2ú`¥|$½ºÁ!]8Û> ›œÚöBZR6@{ôÄíöîÝ›ðjŒ±Ž‰Ôa­Çºň×2FØ‘0î£þ2_MK?,Ãh®ä?„s>p¸ÜI ‹až„µŠÄ%³Až@Ϭš;k ؘ@ÃpܲV ÙÈÿ²1yô׉<ZÈï29ø4þÎHÔÔùéa‰jÍQ÷[FNI5ô÷ðr!_®©m ŠêߢE‹ØJÀ …oÈB÷›o¾9}ð};®ú³‡À'Ÿ|2MñkèçhŒáy³){ˆjÉ~ €6™pü”2­PKîn+ž¬.;Aè”ÅÞ;í„CÆ—­4ôŽ‹Øh0âÈV/õ±ÇP/ñ£åøå©îWFšOú ½u† òÊ ö$Î\^û%V¦À¿“èé—^JÈs äÍY~ F§—“¦Râ#€6ö=ViûƒXòçó¼f›‹Ÿ"ºbaÐ?HbÞJ7Ì÷ÐÇNƒ|ÝÝuŽ\_ú]û4£ÞôÔSOÅú9µ<†þ²²²½x|À¯ \¥^¸Z "P„ã§Dǘkë„Üöìb¼Z‡!›¼… €·ÀÏÓ=ný1,‰r%ƒ8w»eˆ¼Üȕ駑÷ˆç‹œ®ò|¿TBí;¤©gˆ°âr’PóíFý…õD+L€r¾Ç‰döKsrRB0.?ƒ±zTÅ}Ô¿¡ˆ*Rá!Pa<‰W}¿u$‰·öX¾„÷0Œo\(¼ –fìAè?|'c!ÉÐ'°­`yÔÞ¯àn†MÅd…€ë–TT”¡¸ØÇô!Ó·°±Wëê¡è«@x ÀüL³«"«`ŸIçã ïC<òÈÉ<5 V•ogˆ2åò$¡˜Ìï‹OÃ?7nÜNœ<Ð ƒü¯‘o܇=W™9û3;^2äÛè\Á‰kÐi®Ân«¸‘ôFVˆœv´zjƒ-:\™S£d^ð“Pƒ+ðÀåóaçÂ.€åª3¯í~±×ÜÂ&†/Ÿ;dšÌÎä&Ü“Æ}ÕË#au"®Û‘pûEFwœŒ^+¡Î(¼Þ2'‘@û_§Äìˆ{ìû©¾åUÀƒ Uw¤ýVÇ^©®eK‰Õ븞†û5ÑÙ™Ž¾,eÇS¿")øk2ÏÆ úµ‡2J<Ä ]lqi ¡{¬°˜Š=ºöAI5Z¦àd;™å`Ü<)Ó奛?Æþð`öë=zô¨ÉcÐcòw•qºP¬EÚr£—<5Ž"à7Xõ›R}ÚáÌn¼?'W¨±_üô3þ4êÉ î1è›OzЬ‘G`×®]÷`<„¦L ¼p¢ÍqÛПñ¡¤ž“h´ÜEÀ¡ÆöÑxãjJ5'‘F‚ ´ð7‘øþHبˆ;ÓŠÓÖò‹·®xàN‹øÿq[¡ .HDM¤üV‘€±Ñ{”P x¼âÀ<j! ×c%oO¼üb…c…« „Ú1Ì ×°…d–¬ZìÞ½{&†³‘÷5R–ÄWW¼'°bÆ ?C;ü;ÚiµXíÁû£]·R®ª–ù¨O¬ú$É»;êzG’8z; àWÆM3ïÅ»ë²P¼×"OdDŒï+¼&Ðx¹‰ÇK/’£-øB¨Q–½Ê\áHOˆÂjY<¸5"×ç–|#N+¹†Ÿä\V£,q…X3l)â9¿ØEâ“Ì‹™#ž ]%ÔA#¿¼Eñoýp‡ÄâY®àýê͇¤Ã,±™ýp~Z¿~ý×zõêÕ$21¼Š2¾“xê*É`[Ê„™2eÊN•h¿… 1Û$VnK2Qv¦òÄ lA]öaþÉpußG]{E¶m¹oéµ"H[ãKcïßQoä/=q;¼OsLM”Gbþp$]'´1.Ö½LarjÇȶ•qàn„sýóân¤Å5ɱð_!îvÆË}Ö¿¢†÷¿±â;øw>âùR'É0×è©d¨qÓC€'}xL¹ èqÝÑnpX×%uêÔ™Ë@¬0õ*œ€“š0‘4VÙ‚TÿeWX{%Â)«,<ª©Êe‘^Zë0OD=<¯ !*ßxcıx§á¨'Ÿ|ò«x qÑŠ€ ŒëmÑföG[Z¾ô…ë¶^據W¨íwZ*_h[\}¾Ç*Tˆ4ƒî‰ÜÞÆõݸ8?PN’…H3˜äøoÖ}’j^ßf…Ùñ/Åý—­{{y<‰š €Ÿ·—ãçÜ­ôê%`0ÄÕ‰âĺǟ™ñ4êL䜌cÅAØ‘X=›‡=ÕÇŒó¿8q48ÏHÐ>¤æ±Äãµ)ISe{úç\qÅ'à×”çY‰dy¹:–L%î\P+̤0É0ÆÄ@ýÜÎ;Ÿ?~ü«ù´£Ž:jHÂż®Cr]~h{4fø<7©xÊNLƒzpy~ÇŽÏO˜0ÁþÅ*ÖÃB”D#3ÁV& è=[§È[îÇ,_ó¨¾#‡~¸J¨óWÍvÍdL°Ã*ùS!Ô•Ç @#!^çvÌ`¤á"¡ç…BÄç¶Žå13Ëb ê,‚ï.d!½J„áÜT/"‚äîq§KvÍ#ºð†÷e’O²ø¸ß üuü<}V?ð_£ä0l[.ñym‡Ù×/Ì•U…|Ü÷Òº~ã7vÃ^v½wcÕ·->Ó]{åÊ•öçoÓÊ;S‰.ºè¢#ðp½;üâ´ öÅmÛ¶=ÿÌ3Ïðš†Ä7ÞØÌ‰DL”8GäZt$×t£Ó$u tqg…®êPy„2$­)xŽIQò!Þ í=éììU©h¾çùL:/"2œâq-œåÙ0Ä“i ¢R†UóÓ°oÓþÉÙSb~"íÊÔM’…˜I¸í2û¾ä%ñä:#.NTx[@>Ä1`cŽ=ö؃PÇì?)<ÅLq<åCHRÄd$Ñ8^mêæÍ›§¾ð B¢y+Öx,]Z{µZȵÜ—yÑOC]T§Þ•T;ØÄ¿ÒÒÒêÅÅÅDz²[·n}¯ *­•”±9!‹dœHOozC Öî-¥Æòüü²+ñò݆ãìn‰u#Y¿¤ˆÉüºdñxŸ“¬E²þ‹Îv?Hü"j/É5N!`éY¤¢f“a £+án—é%žÛÏkOƒ{yÔÔþc ÈÜÎ;ŸRÝ)šZ™}ùå—Ÿ¼[`?ôŸ×¯_ÿÒË/¿ü¡Uj<™í‰NünWu,—q%>ïSríôs%Õ–|ôò*Xù+C–Ïcì¼ÏǬÓÊ _Åýò^Ë&MšÄSÔä91Æ÷˜5ÎÄ–˜H`LöV u]5±¥Ã!Ô.bK¢{^\“ŽÀpÀè\ Oîò"p>ʹÁT†cËG:Åjš#àlc‘a›@»ý¼æ‹ ÜšÔ ¶1¬Œ#6q¶ýˆ’93{ölÉü,ñ„Éýç?ÿYkË–-Ó"2—Ö«,ñyM»–§5œ/…¥!q¶É3ãñš:sçÃ- ÎC4î©ñš5kÖAv?= ãk¶öWß|,É[VÆuÿ´7¨ò)–§ñœ£Ò¸O ]™ƒ.7cå¹ÈBÆÊÉPÆ›±Åbêp¨äÁð#ØáéÖ ùÝ y¹]äë¬T1ox •ËO IDATßÄýûp‚È+/Q™L+ñÔÍ\:%Ñ¢áàk[†Ó2ÌvOÆõM°§Àî«Æ ÓbU) O™ûìNX!ÒB®m¢m‡9ÍAû2óÑ<þøã«1î¾pǯƒ?GÖ<•&krèþ鬡Ÿ‚ѧyÎ}Ò±”ãŸÈ)BíŒþéW}!ÑG‡9+×XWTÝòš´n„šP{hœ܆GÎLl‹Po®jðçØgA¦ç«žIëˆýØœ\+¼ÝOLi÷tìé|gܾÉ(2ܤ™×Nˈäë8:aÛhdÝ/º¡KKÝÓ%3¶ý¼> öït±OùûßýîwÕ®»îºj p˲šl#€ Ѽúê«æŽ;î0|ðA Èó<ìͰ$t¢_ºB¬m2M]óÚé®ÚG„f2ð|ÀvíÛ·o=lÙãY¹ìçÆ”ší eÞ\-0+@ßî)8¦µjÕrÏá1ãi 78 †Î°%иã5åËÉ^& ;Lüñ\¦ ­Åùä#™ÞúÕW_ý.]Yñ¢V#œÒqó² V)^À›Þ?ÅÖŽ A¦ßýXXÄÃÎδ ³õ aî_®ÑòÿN€þ W}IŸ¢këÚ¼nëé:˜Å‹W#qS2 TBbøë@—.]ÌüùóÍ­·ÞJ©ø’ãpØN°Ô#­Ýo©WÑ;¼ŽŸ®à¶d7¶~VïæcÖ)e…Eîåføcþö”kä\F€}<©Ñ=ÔI!J);ZhŒkÒ§\lvð¯%<^˜»^ߪëM›6-Æ Nõ2uêÔu.-µjÕªÒÖfå¸:"=' <„cº>fŒ—|=½ÊŠóŠ®LóF®s¹vò=C.çº<‰þyБ~C×&Óô³mÐ}ö0’é¹sçšÚµæ„ 5aCÏæÁ4X4÷ÝwõÇýÔ?ƒÝ‘•ý•_+£Ë6`»ì‡lÚ‚_ÃÛ3ë:Á^Š<'ú•o*ùàK÷O§XžÄ•96Yu”P'C(µûX³n¨|Kú퉟~Ûr²·'~¹v‡qbአ-ý¡·ÿú׿þòûÁóÏ??² òa í}7:ÌøÕ«WwÀñw×Lšbž‚1¦.l,mÜm=ÑO]Š>íëèVÜW“A\}KJ²u":³õx"žÃmÓ¦MS2-¨…ܽçž{L§N(%]¸Vú)uL¿èš.´öG +¿£ÿ«„Îcð·°g÷èÑ#+{¤ S‡PCÝ?]%mæ\böë¤mT¢“¢ä=ج𓽠êlÒ($Œ®„»]ÖCâ¹ý¼¦‘üʯBøŸ',[¶¬/\ÖÅ®giqTÓ©è(ÿB>™7ož·GRœŽ±;œøÝ.W»hb¹Œ+ñyŸ:k=`h¤ÿHÛ—:2Í1~gÿ=÷L7oÞ<@µ¨ª À•êûï¿ßœrÊ)$È}A¤‹ä'}Žº¥á5õÏ>)÷àUãøðÐ:œö1zà9é"ß1~åB>kw¶FOoJ!­FÍQÐïcP«Ê•©[·®öýʰ¤—ÓÒN\Õ„.sp#~ºBœÝ~^׃=6çáBŽ|6î†ÂkÚDçáJº2qÛaôÓ f±!–Ë·ÿ­~&}ˆ.­¬X ‰¦KËÕ´÷ð+GõuëÖé ˆ#×Ìá‡n>ý”?H™óaÀr»‡X¾ FË>)n´j_*>êÞèc‘Ý,ì«>Ó§lSʆ/&Ž7ŽÇ)ª)fÍšµí®~²êbkéþx#ãçz&“#_îsòÌŠ±&y–ÏÉFȳ¸2ñóš~qõ<\¢>ó DšËý›znôcõ3»ÿH¿²]{…úxˆ^Ä£ñôÄ(1 N=õT!Ô표±ýPË6 ׌Ã1Vty­¦ ìÚµëE|5q²(íÕ«W“§žzŠ+Æ%ÓŠÂ0î³_'5Û¶mãCµŸÈ ¡¶&yVCo“hÛoOú ÿQØ®Lˆ4=—@dÑèy¸Y?ý¢åÁ”®iq¹5ÈÙãÁs¦Õä&|±9bø+žŒóœ<9žR×B¢Æv@CWÉ´…?ÿ@f7a•úÌyçሲîÈõ¯þ䬹(ñÀ¯L.š;îþûï¯ý=64i…Ê@›VbÙ9ýìérÀ·ý¼> VÏÃa4znµRA&é_Ò¯èJ?£ŸcØ™’m «Ù¹n‰ùÇ SÌ¿ÞXjä,°ÚaN=ûç¦ËíMƒllYާ}DL¸|HâÄITè ±¦Ÿº—áô«ñžöqìeÈV µØjVqn7o`ˇö÷„¥v3ðiÇõØD¥‹•‰ÞžäÅ_á<\œ: /J¥¦çŒÇƯÎy¸gŸ}¶8p y衇8‰‡ýö XNÖÜÃ)†zçþMê]VÉ´s :þºöàJ¿ô5úÙÇh9ˆë|Vœ: ŸÙkÞyò·æ„Þ<ѯ²õÈ –šIoO4WÏW+ ÞP¯´ìcnké(P£÷ d\5> °qãÆ—5j´Ytíµ×‚—>÷![ÍBˆ‹€‹gÅ·cÇs㢓ú N¬Ù2ö¤Î‰]&w†ÓÊäNbVá<\=uˆ„ÔÈy¸øÃ(!'rî§æKn¢O†‰îÝ.n9퀮š*"` ªì[b¤¯ÙýLüÔèGâ‡ÈÝjžûu‡ dº¤[?3tøp3|èí¦[‰ˆZfzœÐÜ<úÎF (d—º½Òe?}iva¸Ÿ˜V[³ »Ö¦M2M=òyêWt,:מÜõ+Šþ)ÈX+`Œ"{ºB¦éò:TfŬ§L”N¸×œ=Ä¢²˜í¯¼Å¥Þ´qKå Ùû©ùÓ•˜ .èof®ð¸ŒNšØ¥*º½Òe;`¸´‘Ç}-áêV]‡/à½aÆӪ˜•§ä8Yäц ~Ù¿çcO‰4RÞ g7@¥úa¿êJ¨¤ØÄi)Xrº±¬Lì$^r®~\"}g5%ÏØöp…P‹+ú·'x‰Ï05™AÀÆ8 Ùb8u:ì·¬ø(‚D©¹ñÂv§Á)f*¾„9óÀùmbGÚ±Ù¼5qš™6m”Yºžï‹y0é¤ñm†£ˆN©WêY¬´ºÔw[ã5n©ñ‘#GnÇ~j­j%B—ÁˆÓ—xÊ‹šCcûuRƒï èËÇIQòÁèÞ³K9&qÐ9ÐË O—[ô<\€Ë&Æy¸Ô-uNK‹Þè¤^arç 5i# XÒ•þÆÌÄ/ø»‰VÚf"á¦Í²ÒÜÜ=® %atáÍ)™tÒ¤T@F"»û¯í¶ ~º4â–_éÿœCàšk®i ¡ƒT}=zôè9WØ<õã>úHW¨ý@;’ÉMÐ†Š–I\ü6±¢Lb³znêgÝî4+}dÖï1fÿC6m§6óïܸ¼5»Ì¼ùþóù*œ®[µ™âŽ'˜ŸtŠi×’;'⛽[W˜Y/O3s_ÿЬg´ÚµÍ!›Ž§žaN;¾IMšøå¤rGÏÃM­ŒÆµXéwî~Èkö=;nF…ò–ùFóÑ̲ò¨¥'›Ö)3ád¥¤3 ¦“&™¹O×±t,atu¢Íˆ:2ŸiõêÕ;²¬P¿›ùÒ´„0"àõ¦»ï¾ûû»îº+ŒUÈI™‚ždÀ&XôÛ«%²BF™è§›¥ópÓ<ëvëÓ»äé¿dèÛfÁmܱâÅl53ÿôKÓeàÄÄ‘{5 ÿv›iƒP,zî^SÒ}P‚ô=Í”…6·K°ù4AjnQŸ´î³pyÍÉ›m¡ÂÏOô<\€âƒ!¶4vŸ+ùáá–ql+÷CàÖ2ƒ-øerÂG"íÝivFOA¯nªoýƬŽd³yÓVì©.ªp¿fM4ÕtÒ¤!Z†“ˆÎE·¼¶-Ã%N…þ—a¹4û !€•éŽ;¹åI u†0ÎlÙ§“´}pNŠ’÷L3n¬§%[É2¨Ë@k•:ù*P…³n‹ŠÊ÷¨ ÃÖ5¹cŋى³uOv‘éRsóíƒÍСƒM¿×5fâ@SR¿¿yó¿mÖÍûS2]ÚófçlÞ¡ƒoÆ'.ÄL4ÝKÎ03×DY…ÜÊ¥.…TÓµõM¤=Øm„ájüAÀÆ•~éw¶ßó§T_r©iZ“PÓ”Í0Ÿ%}‡p/~)zǼóÎ;fÉv–­æÉ‹k|ú9b‹LQ“N΃/³Ô¹¹©V$÷àvƒé¤an¡3Ô¯ô5ѵíR`^«É0ßê užèR«‘[d°&œÈ9ð‹Ò(¢AŸu»ä雬³u:m¡Ùñý3ì;Ím·ÝiFN]`Ö.œ=µÀଃW>‰é^Ì3îú‘‹nfÚâ f΄aÎÙ¼·Ý9ÌÌÙ³ÖL,g,4›Uás…’K®M¨…¸ÉDO—“ºXÞ×IÞ¥¦¶kc.z‘ûþKnŽ»$á4³pyF½s¡óKÑ 'œ`.Sþ2ã.Ù‚-Ù$r×ïrúH:ie›¥{¢S·KqD÷¶¾OM–à¯rýúõ;§s'÷ûöí{˜;¯`:0|ïÞ½ºBí¨®Ù¼™7)Ÿu BMJ]~Ömê„zÝ[S#Á˜nÃÇÅ$ÓÑZ×koþ8w¨Õ©|5zØS¯™žœcª['ìˆFv{cKÉÍfæè¦þ1:lÖ#€kÑ­è•.) Ã¥=ˆr­OЂˆ®·àÌÜ¥?Ê}ÿJô!§zí/4CK8…æ‘næŽÒÅ8¯ò"ÞÞ53Í/» ‰”ØÍt;©¥ã¯×¸¥)¶»èÎ-ÎÁè$ÔíÛmšµ¬ü ›Nªêw¢ãx®”ÇûjBßV¢º‡{*>„ÕÚ.ä8áÏ0¸¿yðÅÆHw6ܳym™}AÒï ÙÞºuë{“&M"ÑVS}ôÑ^û²Î¯>·‡ u,Ñ9yËÎF d‹a”ÍkÃ@Ôª›tκ­J©KÞ,‹$/1.nŸ4«§ô4ƒKšA`öJ³lÿx¤Ûùæà)™ž;šf °zm™vW`ûÇV@ð^Ñ)õJ"M]Ó~ ën*7˜ªé“´ ‘—}Lú`F ð'ÓÆæÚqÃÍÀ79Ù év”ùhð$óÇç™Ã×3Õ÷n5Kæ>m.í<ÀYuf¤ž“þhŽ¯Ì“#âà(žˆÙµG–¦%$ž›Nšxy.:¦¾e\ç.\¡È­ Ï<#ú”ˆ®ŽUwãè&§X÷±ãcvGk†rîg`ýúõ >Œ¶Œ«Ø°ópöãîÈz?àc-ûà£>I+„6¨¿'E)µAj{ð¦Ÿ<ø…T Ñ’ëòXü÷ý¬Û„2ã(°²¡.ía:¸¿¢3m=shkÜ¡6õk”¯4×,17Þ^b¦ a öHwïd¸†]RÚÓüüœ“M§;š£ŠÛš– Y6Ô§ýÀÄkê^ô/~º4â–_é? æ¶±1üE/v¼Pø´¿ÑÌ÷™éÐûGžiƒz˜iƒb‹V:x†yâŠÊ+ØÑØàÆò  KæI'M²<3_ô*%‰ÎÅe¸í—xêú„¶w´¹½æ2¸ÇxÉÄ7!¡Æ—?9ÿy&ýp $ïpZ”-_Hò"†ÆÉAð±Oý™í"«j‘ÝlÂÚʦŸ2ˆµ¯^¡ÎôY·nhk™FÑ“ Ê—ãÝ1*_×2u…O{Ã|âü(XÝœþÀ,3éöž¢/,›h† `ºtê`Z5©oªs¥yrNÜ-yÒfèÂÖ­6qÝEJ×}_¯«†€èÂÎ%'°nõ0³añ ss©-ºíïfÆÍ]fæÜyNâs×q"OÓH²úµ=®+¤“Æ-{þDºMt/{çIÉ ½×á\èÿH¡=Æ+‰A¼„„:’ÏÇ^`B¹ŽŽ‘æCØ¡^ÒhœÜE nݺ^û´jŸÕLB¤‘ò¨p7™âu,|‘³n*͘=fÛg\U†)Ýßã¾æf½,«ñÑ]ÍL0{6,7s§M2CoïgJ#§Œ•€ÿ 'šÞ2Œ|?°Gt.:æµm¥=ˆ°x[uS¦Añ9fØœï͆ÕËÌü·ß6 Î7sçÎ5 —­Æ 9SÍÕ§´I^ŸšíÌH,Ѐ`àå^yJM’,4I² èv,Ç HœÂ)féÒ¥O ¶³¤ÆBnå:ž‹x ÷P3âxZ!!ù¦AüþØîñþ¥x…kxN#ðÕW_yêÛhJ¨}Ö4ÉKÐÆV6ý6Á¿¸vÜ ËYÕ³nS/º±~­ÇŸŸ÷šDGyUoÐÒœrþæ¶Fš9 ¾7;6¬5 çN1·[gYOp«™³.uY}HA]Ê–Ñ»í²ˆõíC4‹¬"РYÓþøãM»víÍ)§àK¢mš%^•Ϊ´Zx!"PVV¶wË–-<¹cY*õÿNñÒáÅDO„ĉãêÈÇüÍxyixþ P§N¯ó¨jŸÕž BÍ*ˆÂm—~Z!Ótå>¼˜è–ÞY·©IXÏt<½[y’…ƒÌkŸ&]0{WÌ67•EJiÛÈÔ‚wÅœ'Í÷Þaî9Ǹs¨Ù ±iwÊÅæœe=„l )3ÿý_V^ø¶õjûY!ѽ­oÆQ£(Š@N#À6@~ÏC%6{­âGg£i<jó/`— ½•GàVö<·fhJ¨ã¢“Þ I‹(Y\‘˜×¶e¸ûZâfÔ-?ë¶¼žu›ÈT>ë6QìØ÷ú±sþ¾ssèc¯ÅŽd…Î}êoÑ«¡}:;«qëÿ;Õ 4Ä ð¬Y¾7z»’§äŒ.‘°sàþ¤âY1±ôÊ0q˯ô¦ÁT\»¼Ý¼Ø¹ÓýˆfGQ˜!ñxŠNôBÕ lµXòí·ß^N]x!2ˆ›”PCÇ'ª…UÎ(ß3™O”§Þ ?8ŸÜë\kìC,a„Ú ˶ –4º¼'÷Ýé2v]~Öm${žuûRì€xgÝV¬FòO7;½—¹=’há#]ÌÏ-ª…}ñéÌ{MçAe‘ ÛͥǗÀ¥öòjÕ(óÔôOí$ükWȽ…æëíYÙFçÖµûZä•v ×êú‡€=€Š?–»‰E~ñÅþ•¬9ŠÀŽ;¤¼xÄLI¶ ;jÔ¨ Á·q †Evc–¾{÷îxz‹ÆÇÑyKq— G¶zücĈÏE©'ïؼy³§9”Í0ïÁ¸‚Ù$ÔN¢+ Q~Ö­„ñ¬Û î}Ú,Y·Õ98Ùð¬Û9#M‡æ]œº0^¼³n§Í˜afΛcfΜÓ¾¿‚o¶4×Ï,Å™!ÝKÌé¿i­ÙX^îìݺ¼ô§+Íá]~8lÄüß"e¹)îÒ'úYò!Ý7ýiÖY ‹;·®3óž¾×4ï,é{š“öøVT2_=¢c!ÔÌ\·ëkÁšY7™âÀj®_2æ»ïê—‹£ˆå˜gÙ²è–Ý-ÑE¿âJxínrO]ŸÀJñÃÈr<Én"R½aƤ„GçíD+˜W1·íÚµë—qîipž"€S><ñ:´í÷>·çEù\j9q¶3µúi…xÙñ2î¯ÒY·Xø•C8Ì´¦ ?£Ç” }Û,¸íxÓìô;Íâ);ÌQÝË¿îVöÈSÏ ž¶Ìôo_¾:íÄip¼>ív3­[yúQ7u1£ðí‹ÒÒR³~}N@¨˜Óà÷™öÑÓA*ÞËð•èUŠ‹ËpÛ/ñÔõ’'!Tn"%×d?ݶxñâºK–,1ÅÅÅþI 9e={ö˜×_]ÊY u*ú¥KK#áåW?„˵º@`ÅŠý[´hqxð‰ñ²Ÿ1c†³í*Þ} Güµµ\Û.ÓÆŽ»ÊSþ#PTTäu•q ÿA ¨†žžd2( ï–ÁkcȘXiŸukS›L¸’&µ£QŠ/þö®nŠân/Ò›ˆJ‘" (ŠŠíSE1‚ Ø0"ˆ4[lÑ˜Äø%ÆXòÅØ ˆ hÄŽ†(ˆ`/±D# ˆtDå{ž{ïy™w¹²÷¾{w»wÿùýæþÓvvæùOyvnvöÏÞògf8[jô;ï_,õþ0pÓ#Á: ü³÷Õ;z§ö©ÌÒÃÛåUÈtŸa—âú¯¼?ôë°1Qá]™t›)®ð%-;f4ï+ú¹ïö5Bpùå—S˜‰'Nôðµ4–˜dê›dÑÝv@·ëO&Iˆtáns×iÓ¦­Ã飑EJ² "ü=l =` IÊ}‰¸ü5¬rßTƒbÚ¥1E`åÊ•æR<Œjc1…¡(Å.Ö uªÊj©.ÌG˜Îº½xÑ<ï“Ͼñê6¬ë-[¶ÊkŽ£ºº¦;ž+yNí­Õ(P“Î'ÎÖ½øÓ¹Þ{Ì÷8âå¯QóV^çN]7ù”¸ÿ­vèÝŠ³y/ÇõsçÍ÷Vq}×·ÀõÛ¸ÞŸ_žü©tœ*,O·/ûl9€ºƒ¨ë§›ÄZäúq¸÷Ç uFŒQ«oß¾ðš‰: QÞ%—\¢bþW§®ÛmLï÷+“y@àöÛoÿr̘1ƒ°šø<²ß¸ºˆŽ³i/óÍñÁ˜Ý$â¸þGíS¦L™Âc3e†€­POáQ"ÔÅC!ÃyÖ-m¡L‹ݼ`«kZáZZ3†€’)ý$òL)·K¶è^; vÐñÇ¿áù矯µÓN;Ák&ª`¿¬7dÈï³Ï ŸÜDý*¬ôJ)]SÒ(LþŠÐ2úù,æý[§žzêá>Bî”eãΨ |Ö|îf›mV™†×S_Âþ6l1ëVY&:Hô«˜'oàËœôn: _š`ÃÏÙr4 ¨ àNftË/·¤ÈW¶x# õÛØ>Pkß}÷Ý€}ðš‰"ø:šw衇zÓ§Ogñø*Çí°Ô£H³tëêщ¶ 0ù)coH.³™ •$)É»ÅÉâ>—±œ"8|!1н±_Þ¿B=õêÕ—½>OémUS=TÍÈ|YJ¨‘û½™ˆ¡–R%Ýê%^̰óp]HâåÆ_*°&wùMö5,¿”žN÷°oâkoµ?üpïØcµÓ?HTÌ7ß|ã]uÕUÞöÛo¿á¹çžc±¸[ìFØ¥°Ò%ޤgêÞo”0©Æ`ÅE^úI[Šs¾ó[.零þtyóãäË@¢bù“e&¡t¿;ï¼ó\›8æ’×boö™ØCøú ÷É1]*<毓ê›Xwõ‡´fª‰ŽÌ¬Ä5SÀ;Öý=SÝŠW¬-®"åN%í<ÜbµŒîkçá†dͲaߢuIÝn˜È% ˜,'牰‹`û>ôÐCõ`½N:mØ{ï½kuîÜ9±×q±4 #¡—[ynäG¡ßÂ[¼x±7gΜ /¾ø"I”V3ßÇî‡ý–z”ÕÑôïêɪ´ úÃ…¹æÉˆ¥ÈZ˜(ÊM#·¤Ò¤’AÒ¤º.)ÞÐÏ IDAT§°÷ßL÷îÝ;£*»¢ýp!‰4¨á*õ>X¼¸oܸq3áÎåÚ ÷È”.—ö´J/l富su <Ÿ© —D[€„kFL ׌ðT+²X„Z…åÀîšÊ• \LiçáºÅËmçá]_ìS`ÝIInJ—d‘D‹„QÒϯq‚ž Ë/õ†Ýù“O>i géüUêñåKkæáÈÀjWŒ«ø5ò¸Eæë¯Émóf¨WêécØ—`çÀòHC’L#=»néÑñ3.éJ–žx¸dÂõ+<]˜¥÷‡çÅw¾ßb‹-~Þ¦M›gQ/êÀs3¨þƒ¶»ýG}ta.×å¡"WReíÎõL禕_˜ËŸH#=Lë¦aØT›d3ÀÕÕI¶äÀ6@^¹$Q‡á5t»Š•Ÿav.Š©±óp#¡¸Ä¤ä”„ýÊíc®›q"Ò"c’$ÖÜNðìÓ°í`[ÁnËq„犬H"¨ŠÑ„Y%°ˆž*Ø€oÞ±cÇ·Ùf›õ Ô$§Õ28¶ª}«V­Z 4èÇ™3g~ŠÊãésÌP壔ž$W"ŒGâ}ËüI¤©#×JwÔ©K¤Õ”¯$’UŒÅQ&/"X,lÒ¸í-•[aj®”›Y)ò-¨œ:uêâŸÿüç'5kÖì¸qà¹_Lü_V¼xöìÙ쟯Ës娦hÔ¶8>ȰýÑ(NRa”ÔE•pê=Êí’…Ž‚±=ÔÅÓB!;;G&Ãx ôLG?'ž‡{Ðå—_îMš4 N3qAÀÎÃŒ¦41±qbsý"[$_DÂH̸2M«½œä41.„›ÿ Õ…e^«´"&”2 “?rŸìõpRF‡úõë7lݺõ<|z½roj.…1cÆØoÞ«]»v]ö³ŸuÄ?lÏ¿ñÆȃz¡‘”›z£)vuÄÕLZ—HÓ­4¼†nI壼Ô$ú&™V{tÛ—Â(î—¬¬ÒùÝôÓ𚂚ûî»mçqSö«@fÁ‚=ýôÓ 8ð52®^"Ûe. “d¤I%ÝöÈxê@×ñH@#ÕD.ƒ±-ÀÉsgžoƒ›pðÒÄK7;?ÿ—­wý¤mIÛ¶,?X•‡ â`xîn»í¦#¼nA™gî†]“”èø˜ܶmÛnï½÷þ…Þ÷ÑÜêí#ÿÓ¥K—žl¿Xñ~ùÙgŸ}3‡40“TÐMÉþ b,ÂLò¬þ£¾Ä>F7¥úý²LÏë)•ŸòNÜE¦Œ”qÚ3ËÅv%ÛÚžëf\Ø}aÛÀòß¶[¦“qÝ 3™;j¿º’~Úï`¿€å~ÿ`iØÖíRéü~…3=»ýfR žÔ瓞"ªJ \„1ªm•@óÔ (5Ê$Ç‹ÙQ4²S¸‡nYÅÙy¸9\ìävn±5PqN:>¢‚©ÏQŠH‘`±_J’x»+Îê³®$x¼^dZ’é”V’×Ä‚¨`UújlûàDóo¼ºæ©§žz/å~·óÎ;„3¼÷Å–’úØW=;@~UÈÒkL¤¤~D‚I¦iæ’e†‰43N–×¹yiŒuï‡$ ýRFÆøÚ±Ú•³¤ÚýtKö‚û\Ø`ù@h&üŘ{;,üÔÙF©;µU¹+%Úƒm €RÌ¿µ4àZHf ‰·™(4¡vèârKª#iâày¸qî®<÷ÿøG­þýû‡ƒe<wðàÁ^ò/;7,`k–ú•$scß"é`_£›ä™n’/†S’”ÑM£k•^RDZœ’Ÿß2ȼDûŸ=÷ܳ‹-Úb/â÷ØŸÊzVÛ¼ð /ƒT/Ûk¯½tèÐa÷£>ºþc=65@¾º/%±§¤¥^4.ÒMò,M}¹–áLãZ^Ë|(¥SJù+|ùÍB¦EœÙÞèv-÷÷óÁÃa=ü¾¡W¯^Þ;ìàmµÕVô3ØLඪ… zØïíáÈÇ®¸õ¥°c`Ï‚}VãÛ( ýtS¯®L4 . ÜŒƒ@Ð-¸Ä°sp ÃYhBí/3ÊN’ÊjàgÝwÂÇy¸»ó<ÜcŽ9Æ»ð =LT6Slxî„ ¼Ë/¿|Ï,Fyø‚ÌͰewn±u‘âþ"Jêo"êwì_$]šÌ/¿²ÔõLOËñC–ìÄ%3tÓ0ؘe˖ừ7lØp›®]»n‰­ókZxìŸ~«FK±0të­·Þœõ}ôÑ{p®*g2Ô ±vu%ü]Bí’jæ+bí’j]çæ%2Œî(µ%µK¶1ºÕöä¦;Ø{)›4i²sE­3Î8£”d¦Øàd’ÄGˆ.ºè"ï­·Þjòð7¿„½Vú¥d»¤¡¤¾]i¤šÈøLPBm#>àBðzµ;àq"® KÉ=›ü+Ž’V{7)&ý”ÜÇyl_X¦³óp BMŠópYî»–g†q§öMkµ»¯“? ÉÝ”"”ì÷QŸèQÌhgu“‘K@DBÔEŒµGÚ•LC¿¤Ûw™kuJÉ _ öG1`Ûm·í=þü§žxâ‰'UðšÊN:µ>øàƒG×­[·Èô¢iӦèÿ¶uµw—L0L„XÒ%Ëê;"Ò©H5ãÜk”ï#Ëî¦û"¸¸Æi¿,ˆÚç¹ÕŽé—»%ÜüWs»=öØÃÃˇm<ðš‰üð×oû[ïꫯfÑ8öû<,Û ý•íîÄ| S?a{UA”l5k~ói6$Ûì¡î-ÅG€c¡ =B·ñËM©Ž£Ž¤Áž’‹ÌÙ°ïÂö†-ÉópQ¯*§xû쳇7¹=l{©õJ=} ûìX‘dêN–i¤g×-ý#ÚL˜p²I’aLâAˆ”PtÓ0Žº’ŸaºŽ’ii9n°?RÒŠP‹äH2Y8ãa¾øâ‹wH¨qô]g”xUX¥ÆñyƒD_uØa‡…ð6xòL«÷7œÒð•ïÄš†Røûû õ ~EéjêFVièç5ʇRùëò#*r&U›fÝj‹×à ÓxÔÖ‘«Œ¨¼<—øÒ'NÖá¿›Ôßí°ûÁê„¶S¶aJêÙ•l¯_(Í$Àv²ÚxhÏŠæbi&D ¹BÍbkdÇaçÐ꘻¦Uj®FÓjeZ«ÖЧäuí`[Á–Ìy¸¨KƒU–-±µ¥íçŸþö_ίY,M¸ìˆtS¦:—dš–+Ñ:i€«ÓôkušR+f’šü™/ÝäƒÖéDML’P‹Ø²ßɪ?RЍORÊÍ~Æ>+«p棾¬¼æÞ ÞM ã#k@À6?餓Ƣ€µñ¾Æ™Ø÷ɶšÁ¶ƒF Óçã>]0ù­xù嗯ũ"ÿÅ ÔÏx/¹Ý>Ǿ@+2íJfõ—\+Ìg W~’‰{±Ó!®è&ÙnUµ+µ]Jµ9µ]Êýaïç6lשe+Ó‚/Ú’+Õ}úôñøÃåêë`Õ¾ýí•án¿àDÁ4f€ÀôéÓ;aÛdzÌ>Á õvÙÒY|p8Ò¸€¢ëg'Qb¹Ü‰€¥O^Ç ™’f!ìbXNòîÀªtšÜ0~¿Â#+?üðÃú ÕÇã+ZM±jö¾¾æÿ›8ì²kB•dþtk⥞4ÈQOœ¬9‰‹Ó/+=J·’¼žn奶€ 3y@@º$î錟èJç”Ô#û%-uK©~©¾Éëé¦QÿŒ]ÃvŒUx‘ðCÞî=zôè„l_­¨R8¿ø—iÕƒ>x1^P<¿qãÆ{î·ß~⿾òÊ+o'ï ¾àêLºpû úu£~¥þ¨~'¿ú™®w¥îÕ“4–SºVíMí’m/·ñý#Ó"&†+ÕW\q…wÀð˜ÝÑ {7%‹®¶©q…~¶¶_Å%“š A÷P#©ár“á@T CEººßÍI€eÓd 7 ;“Œ®ã ÝLljÃ?èº× :^_^[‹Õéð7ôN8 ŽÞz®€5P§#¾Ò›;‘kâvIµˆµ_ê:æ¥ ^yRÊ2ÞLH`rÒ¶7ÇTX“ 0œFñ’œÐ¤?ö'ö5J†»ýM}M’yʸn…ERâÅÚ×H¨ñ—î(àì° ¹jÕªÕ÷Þ{ï%ÇwÜÙX±> Çê ¡~Ñwa¯¾G¿Ûwä¦^Ô¥#ù)Ý0^£üàL݇2FímíO–oîB±/ Æ¡"VÆð–Î;{óæÍ£{À꓉üm”~ê˜má'þ›Áqî²7øjf­€Ÿ7¼Bn-ònÒL輯vÿÄÀN‰€Rƒ%Ýê@p& „& ®’LOK#Iw,Z¼ ý 5öSwÃDÿ\€Xךâ«NçbMÌ5QSº«ÑÚîá’i¦q-'xéRy»÷Cpå½é6Ss¤KbÎ>!Ü•3uÄþ&ãêˆéÙ·¨;¦aZ†ÑÒOICÉ>&Ë0™t}/]¸®+¸Äì+8‹údlØ7ç¶¥°Ížt€/ãýöT¿‚ÍxSm-¡h¤+·¿¨¹„YnIê‰×(­Üþü"µ:íl÷P{¢LeÙöØ.i÷­Kbf§y‰š<„š%ï Ë—Û]Ã6+˶ 7Óp ¡¿ì Vû§ö~Sá TH£ É{s gçÐ@Ï’nMØ”îÊ´®UzIօ׺– Ëo?ƒ þsüUü_LÛí½÷Þ;âÏ— T âKCIì)i©ꎒV¤šºò[Æ)¤&xJé”’Fþ ŸýÖßC­ôÈ|51IÏî½Ü8õK†±¯±_1LýËu#8‘/%ãcgðûæî»ï¾/÷´Ã¹ÅM±íŠÛÊòbž|òÉ©i2–NÔä§”U?¢Ÿ}Kár3ÞM£þÆtêopVqÓEöÄö§¶§1Ÿ’[þÚÂ&Ι¦4?°¥Q…æW-©W¶UêcŒÛ4¶PºmÞò5A 52ÌBn&j°!g›6;uJv š8Øs"`¸¬Púet½& ÖC–Ž×¸–×¹×Ó;3gΜg÷ßÿí:uê´>‹<;ÀÇ!ª#õC㟘…?u&BM)BÍÕj¹]R­ë\½K§ £ÛLÈ$I5sU_æn˜îÊ8ö!¥¡ÎÔ·èfn£ßoTÅè¾’U"£äY¿~½‡³£_ÅÇÙqÇ{‚P?œ§ò¥jënݲ,‚ú Ãäveª¾åÆë:åI©Õi–Ç1l+jgr«2\ã>å–¼Žm‰ƒùzÞ»ÞgßáI€ š¤·M×]¼6¤šYÌ·ŸÎõ>Y²Úk¶MW¯s ²äW¨hžö‘4 ‰ Û'u«v«1É•l߉v YöÆuñšj± ;€: ËÀÂŽ‘n²f¥g¦eùé¦ä ëZæ§ÎDþ”2‘ŸÔUPÉwß}÷ýž={.Æ$¿ NýØá¥—^zSqyÒ³&Ö4¸4iKºd™š[DÚ•L'ëZå©{ I…±¿¤„Dè’ºu ±WÿSœôÏ8¥£îÔ¿ÔÇ(Õ§'㺠‰mÏ‘P7oÞ|/øÞÚÕo+]PªŸPÊO·,èŸtqºŽñ´4’¾èüª=±Dþv¦±^c?%ߥ‰ÉWx÷ÓÃ;÷–8¸¹òå¥ÞoöáãLf…7eàŽÞéȻǕ/{oÿ†;abg¨OZµkW²½j¼ª¬¶ ÕÆœÁtem°P+à±yQí÷±ÕlAŒó—3•ÈF¯¡’“€PÆi¥΄Ñu”"c,?Iš:ŸY^¯|)] o|ÍÿûßÝ»w? _qëBý|žk¢G)ü5°QtSRW².¡¦ndO¿®U^Ê_÷IÍ„‰€¯*kâMÃ~#7û Ý’Œ£¥‘Ûõ+<‘ ™FnJæ+ƒ]V³·ÜrK¬ ÖÝç¿-…2ÒïG·ü”ì34 wûÝJ£p¥Wœ®£¤a“»"¤È¿Îþi·ÍÐM«¶§‡<ú5þ«=¹Án_¯ÒåH¨ë\ÍVÞ‚^¬È…L%½ºí˜óÃ)Õ(Í8Ô©S'P?@·×Øà\mΚ À¨ØFËÁŽBCÒå7š(Yn4/t«óQº­:]º»Îˆ3kgwëÖm^Ll¿Ûn»µÃ^ÏQç°;ÁÊM)]i£¾¨WRw"Ï.¹V˜+•t«{! 3ùB€*IZ„7ûtàö ÆÓ/½ÓMËþ¤>¥0ID%Ò¸Òï¦?ò¬ÿéСÃùxkþ[–m¹†˜o8pàÞ õ£ññ—«ð´–´,‹t&ý(,U¸®cœ ›ãlÔÖÜv§0- Pr Œ‰iâ~`w2G䩃Ý/ýõ¯÷oEHoæçÓ¼Þ[T ¦ÓTn‰PP©Jê˜:e{%JôS¿œk(®1‡qjßp–·á µòQœ6À[h£ÜØýe`Gq ¯azJv.–›–ä’L–‹×ÓOC?:_…/¦¿ÜÛ‰Ï}ÿ ,ø9ŽÚê BýF«¢AÊÕ™tA}Ðr€“¥nD®évɵüŒ×µ~©ûDyO'Š#"bí¯L¥œ…©oQo4þ¾åfVý v™XQÕâü‚Lï‡Éq Ž; Y³fCp~õ+É’P'Ò‹ôA©ðtR‰™V{rÛÛœ¬Æ|JÎjªc¤e0è:,µcš7Óé¦^ó&ˆö%AêµÞ¼wßö-«xÆkÞ¦³·cç6)Ò9ÇÏI]²êšï)¥wµúÕöá4CluñÚÁ¦}5eáîü§;iàw;;Œ& ÅKrðS§£d¸:œ$‚*Y Î sÝôÇʼúê«4èè¦M›îÙ¥K—füñ!W@Ø+[aïNØt»äX:áÀGë÷ûÃx½òƒ3atÿýo2d’}’¹º}ŽzIe¨·ï°ŸÑHVøª¦a˜{Ò˜L@eûæ™g®=øàƒwÆÙÊGà„‡‡ 6bÒ¤IÓp©¿¯øý®]wˆ4‘qÛ Ý´jgß)E¦5þ#¨tÍŠ¹S½_ì8È›ä¯bS½=tµ×¯³¹?AìüÒ­ôJ)R­ö JÉ_ÙoQŽ2ÇJ ‚”Xm J¨RJ‘èÙqèw}vv ÆÉ2=ËMÂæv2u@JJu4J×øýŠK®øHÈ/¿ürÍŠ+f`Åê|èåHêkP0éBYøý —~¤+úå±vI´Ü’Ô)¯QZ¹•‡ò³Õi!^ ™Xõ°Ë> 6@ÐPª_1^ý„át+œ•Fià÷+ÜäF„{eÎäÅwVþ[?n©/*Þ3f̘³ÇwGe¢MõÅ(¿NâB¤jmâdtÇu‘-†qN(é6¶âÝ;½¦=FV‚ÒcÐ ¯å£z³òÎm^ÿ.«½wÖLôv)]!Ò)õ*>@}sþð·U’Ér'жB­ÖPxɆYPãkì~2åú]"ÆÅíüKRG²¹’`X Ë0Jù%¦p†ù-?¬ ‹c¼&¢¬Hu¿­·ÞšMuËíÇ@¹’yûqÆ —[ºž¤7JêS’“=ýÒ7¥ŒëV˜É `\/ªP~KIo~=º~7ú°tÏtfÓcàâUéÆô·ß~û™8&ó2ð…ÍðÒÑͧžzêÒH/žR†jýÉ7î*.ÊR䘒Vs•Ü"Uûh凳Í×ÞØa#++vÃÌùÞÛ<â=½a÷úä_&Ã'y7=>¯2MÌÔ§«Wúݶ 7%d…¯Œ1N‚Ïe S^ªÎFZp“Ü]eÒíNrsÂp'`‘i6Z8?™s‰ âüdPit}ìäo¼1ç侈NÑ wïÞ,ª[aâJ‘mGÆ{¥Óý¤JêLºrõ'@)SÊÚê4À(¶aÿLöCéÆuKwê§”þ¾šNïn0÷FRœ ð*ý§Ÿ~úétèæG¬Bý¤ú¦ƒ:ˆÍEº¡›}ˆZ–ºŠ³qÉÝœ³d]I¯P¯7ÃûmòDa·¼îsp‡¤Në{=O¼Ø»’ê†yùƒo*ñÿuuK}ËOé7 “ôÇ—•œ (q"§WBÅ4~…rb`çq'¦¡e˜žX•Ž“:‘ë†7᧤a\I¼œ8§ì‡UêÃP±±y¨œôãJW¼¥tEéZ¦£~(Ýp7½ò¢¤‘¬ðÙoÑ ±v ‘p;¶Çdér:'Ksæ‚Àm0§vÚb\s/HõiøŠãÖ;v2aÂ>à–šÑ˜íŽíª#ãRYÅ—”üü•g’õäubO_ÝZxgLýÀ당4ć\JÄHçÒ1ý®e¸ÒpN1“D è–ߨnø…€@Ñ5•™œ˜ÝÉVnv¹Ùiè–Tcõ妤ñK7,‘?̧$Ì¿þõ¯§O:é¤ë±RÍ –+Åaé€ùÒ-?¥1…‹4+NRáJO©k$”Xœ¦ßLÄÈ0›þ £»G±:ÝçcCªW¯ÞŒ¡C‡œ"Ñ~YÝ{”Ôuvl^ñÔ)B-´z#±Öjµ¤È™KâHåwÓ1LÖOÍ¿‘H aå—©0e˜ÒÉ­t”2‰‡%þ(À¤!’ív&Ë ÂÚ7jåæ)·Þzë`”ó*Ø÷¿ÿþû¢VêÀåñÏS"RÌ@DKÄ+p¦QOÈÕ‹JS§³wѳ7$½³¼Á=¶òv=ø`¯Vƒ¶ÞÐëgU„›ììÙ"áÎÇê•e)ŒCzÕݤsI†»n¥+{i[>Š×ŠúRb¶j''1&Ó‰ 7~ÜŽD‚æú™& š.Ýõ垉§ƒþ\B]®ØY½Klû˜-' :ÜöqsÔª•ìo¿‰Z¹B,ÇïLä:Ä['«ºMôòáÖ^úUËÐê€s¼¯^ßÒ;e¡Þ£ˆzgV’HÃ=ìÒû½kwœWñáñº^Ë­y0õ;^“z¾Lªfu_¦ù:S\Ôë•×òÙ–¼Â›1óHj·ä¹fp‚Ä9Ob~R—®³ùÓ¹·( 7>ô°yƒ 6àH½* y¨\,}úÉÃí,KC ¸`¼y*Y‚>ÇüfS¦Lá?3fŠƒ@º1¾8¥ é®ÝN¼Õۛδêy¢÷ȆÞ§sç{Ë9Â×mèmÓ¶ƒ×ª‰;•×÷Ž»õmoCúlÒeµðT:Nµrµ</ÊVŽ*sx¶ÄŸÿÓ~ö+"”‚$.ÑWÂÊNâC¯·oßþuìóÜ Øä³þU P³°¢yA[* Ñ„ ßRj¶W^nb™Yhâuè¶‹·Ë.°Ý:ûÈtÖ‹-A‰#Às¨VÑþATÐdAšŸ¥+>ë1áwÇß>Ç¿(VC ä˜Á¡Eê´’CÙ*dÕBÀV¨«[(¡Æèd‚§ÓY¬¤RYI Ò@ý*±í“Vä^L̆0'Z~aqÔ¨Q­²¥p¼þ¦–t‹Êã==#膙;F`þRi¹ªÒ£@“ÙÀ»¶Å`8HÕ‡²ßÀR¤EÀuZhâ±páÂÉ(ùWè,=1yÏZX© È"0 ¤š“ý¾ØGÝ8²¥LQ0|íq7¼T9Ÿ* Çêí’"ITƒÜbX IDAT‰_îTr+ðÅ_DµV®,¬YSy8MºƒJŒdgÁÎY’(Z}H~“5DÀu ŒÚåÓ¦M[‡§üÄ ˜ð0°~Á‚üäÞý({3ì'O?ýôãb  NüšüýDJ~’«ÿÀ®üàƒ¼¹sçÆ ZVDÌ[Þ3Ï<£ à N¥_JZ…Wø6†Ë_Ö2(¡H³¬ñ ³òF¨ÃD3"yÝqÇü¢Û$§v:uô­ˆ”ΊaÄ|‰,A¨Q‹Ø½˜È°n¹å–ðÀý7”¿ƉûÎ8㌳"¨‘l“=ã]bE?·â¼Æº\~ùåfb„Àĉ½o¿ý–%þ ö›dÑÝv@·ëO&Iˆtánš²pÛêâ©Ùuñ°ÏëÑ©®OÞ`ÄСC+>Ÿ•×;Zæ†@y €>^FMW‚Œvã'¾A¦©>[Ã~‡zpøHõÖžŸH¹~ºI¬E®‡{ýäÉ“7<õ”Ž pͬh –.]ê]rÉ%BãŸp¸:uÝ~âì÷+²•¶B]<Õ¡.öy½óm·Ýö>nð$:W£ÆŸ–×›Yæ†@!À­ ¥‰ÿ¦!cµíÃUêñêûö"lÿ˜Ð§OŸ:nš¸I¦dDž)åvÉÝK`§ÁÖÂK£æÌ™§™(#°nÝ:oÈ!ÞgŸqaÚã&êWa¥WJéš’FaòW„Úolå Äëø`m…‹@ àý¥åV(0Y^Ë{TŸ…•´º…º¯ÝÇ(´zï8פú.L¬`WaœC=бBß¿.vÕ\ÂD·ürKŠ|qËH¨ßÆöZûî»/¿ ¯™("ðå—_z‡z¨7}útoìí°Ô£H³tëêщ¶ 0ù)ËÞØ–â5#ÔÅÃ>ïwÆdÉQê=L”míC/y‡ÛnPF¬\¹rHèx<´ò˜ÊXlÿxõèƒJðÝ‹ÃagToÁJ‘@‰`ù¥HÃé¾öÍåË—×:üðýc=Ö³Ó?€HDÌ7ß|ã]uÕUÞöÛo¿á¹çžc©¸yšßPX +]òáHzyv%¢†af’ ÿÚ)Ej µ0)éÖvÛB €£±Fá/ ñÐó˜8÷(Ä=톀!?°å£+JÍmb0^ðë‡;v~¡k’$\ì!1Ø,i¹…ÿ²QÖƒÝ<)én[?)¹ºN?%Ã嶦ó:uê´aï½÷®Õ¹sg¯nݺ·PbŒdt¨†sk@n“ó}ã:o/^¼ØÃ6œ /¾ø¢ç¬¤r{âý°_Ãò jÚÕIK7O¬â¹Ô´üx%É6Ý”"à”€¦¼I Þ8 íî `‘Ѧ'ûöíÛ/c"‹Ì #Ô9Á¿ÄX©×¡C‡(ùVÀÂÞêÄÞÏøÕÄJlùF`äÈ‘ÛÔ¯_Ÿ[&v‡]Œ1£?ÆŒ·ò}_7ÿ$¡&™&©¦%‰–%©&™¦$I–%‰v‰´H6IuKØÞ°;Ã6Ý|óͽîÝ»{»ì²‹×°aCï£>òž~úiD…gößÿÄ=ž|òIïÓO? /ãÒɉä÷cØ—`çÀ’$“8»„Zdšá´$вëá&™6B \3sæL’ä¹a©Ü ÔO€P÷OgaÕC€ƒ”™F€ÇdáïÛ›±åãØóPU#Ô%¬o«š!P&L˜°_T<«¶ƒØ‚•Ûg0~ôÁö±7j’o5®õÿuÊ¿þæn›’MË•$^ÜNðÄÖ[oýún»ívHûöí·G½söY¯Æ¹Õ\p y#‰§õ›Taþ4 ?W¤±'xÛí¶Û®F~—ÿ>FÉ`˜ÆM˜y‡‘—ÊG)I®DÄû –{¦I¤©#×JÔ)­«gåéJ$I¤I,OÓSΆ+ÿhßY!@[%†fBDÀuˆ`F5+~è«2¢ à‡^ÆÇAÞŒ!`› 0~üø8!ãð-¶ØâNŒCð ÎU¬Bj–K¤‰„Š+Õ®_d‹ä‹ó˜H‰Ùmb…»K—.Ûîºë®}·ÜrË]P†y8¦í“wÞyç |æ '­µK ÆË2˜Ú 8h›m¶!™^?{öì_}õÕ⌕F¤ˆ™$kE·ˆ°H±«#w¥9™fz^K©‡&奶€(3.hãnûu£ª¸Ë}kL0Bò¡ È(gýàœÙI(ãL޽!?Žry­l†€!P\¦L™ò=æå¡xãjl‰x§¸¥©BÎDÒ\IÂ%RMr–pc¯t·®]»hÒ¤I7–/^þˆS%Þ|ã7^Á#ùòÓUo¸I¶IF¤2°©W¯^ökÙ²e{|øg-þvÿç'Ÿ|òeà J'¡H5õ#Rí>üPW´Ô“H5¥ü®ÔuÌË%ÒÊ[¤š~3I‚j$—® »à€b¦ X²dÉyXqz«Õ÷”Au­Š†€!PC’+X_™æ}Ó),’,’3à„Äþïzx`ï6mÚ ÉmO@¤×,Z´è…W_}õ9¬óe7½ÜH2í'Õ¼Ä%ÔYWû4h°ùQGux³fÍÚa|]}Ó.\¸P_úc~åbDpY_‘iꊖú‘¾HšiÆ-´.©f¸kE¦•—òvï‡KŒ ´÷Ú·|O3!"`„:D0£œVœ¸wm|”Ëhe3 CÀA@„I’Q$W$¼$t“ÿ]¿ýö;¡E‹C@&Gþa¥øéáX¶çV¬XÁ´z¡‘ni^/«í®Dtzƒf5™>²5N YþÄOÜûÅ_põ»\H¥ˆ/Ý.¡¦[¤Ú]¥–[d;©¦îÔ(iä¯ð•ùoš‡ÑMP±-›@Rã#Ô5†Ð20 rFÇÍí‹ú‚÷þŒIJ„¢œ! «î"J”ÄUÛ1è¦%¹úaðàÁÇ‚HÿÛÙÁïa•x¾ºwÿ3Ï<ó"H.ɲK¤E¦9÷‰T“ »Þ”/'2¼Ò`{Gã#+àßU&(_‡ú±vuEBg"Ô”"ÒZ©Öjµµ®sóR›`Ýf’%ÔHn¸…ÜjŒP‡ ¨egå…&°_¡ÆÇb¿ñ{”Wí V[Nþ"Q¼iå*5V‡"™þþûï_ÂÞè‰8‡—ÛTt´¥ˆç;ºE¤µ2MÉüDØáLø)eWiZ·nÝ¢_¿~£±­dKœä±ø±Ç» /:òÔŠr5Òë/B-²+B,é®@“<“8‹T»’阇®£Tžº‚*Œ­¸Và€-vʇE¥ên·3 ’CàMÔèXºŸA– ¡ÆÊüCx˜è"{Nùo˜Z%9J®´‰D‹@‹ô’\%H.>[}^«V­®xë­·¸oY«ÑºŽR„ŒóI¥Kª]2Í<] oUÓ±cÇ­>øà_à䤿«W¯ž?uêÔ›–-[ƽÙånˆ5 ¥ð'ñ¥¦Ôʳˆ´$u#«4ôëZå¥üuù‘ÔŒ­P¯ ¡.övgCÀ(°"ôÈôåà€}K :¹TOÙgV¿ˆ³ª/ÐYÕ"Q,'‰–÷ùçŸ/‡å9Òî|Fò¥´ 'AÓˈ$Ð £Ôê´Hµ;¢61µvØa‡ö½{÷>û´›®Zµjî#x5¶{š©@€xË2DÄ×%Â$Ó²ÔÈ5Ý.¹–Ÿñîõ®›÷J#‡B"!ýý¡J¤ã©ÄÏ 3g 0B]ðâ|)>ܰ&ÃÃ0)>Å3gã\+»!PLÐ~™|e8dëPÈqÅ,O¡îÍ“ƒ°2}$îwHõ0šÂ?+Õ÷Ô¤ 83¿-ò:çGŸ†|š"o¾¬&LEØH¬dH D/I¢,—nešá"Ò’.)©…²Ô™þ_’i¬H?÷Àü/<2_3±ž:rÝ.9–NHœiý~˜ˆ4ó“QÞn˜âÊVÚêâ©Þuñ°/êñ†ú (À$È!Ï-jaìæ†@Ì| Äïؾ¨ŠÈ_Ìk•½ø ÏëQçá Ò_@þWL‚»5¯É~uÕ¸Ž_2¼¡C ¹=ƒŸ’ž¯^L',I ¯Üp& É—V˜ ò¥ôœçHؘ†iE é¦Ôª´K¦ìÕ‰^ƒ“<Þ‰^øÐC݆#ù˜·?Ó–ºñ“W¿_õ'>4Òýr‹X»$ZnIê‰×(­ÜÊCùÙçÆ’k°ýLmÚ ÞľE Í„ˆ€êÁŒSV˜¤ÞÂj £Ñøkõ’É“'§ò[Y (!€þ4#92X ý*шR±C)K²®¿!^„ª_ ®ÆŠ}ê ‚à€´‡  àº~,®ùö>`zÕí·ßþ:ñD0­È“‘4¸~†É¸q$Í$ ã|Ç|¦<]7‚+ 6㫘‰'ò43ÙQ£~\1\Ö%Ê"ÏŒ“[ÄÚM¯¼(e\·ÂÊZ&ûK » (å†É-i)!€¿VŸ@}Ã_D¿Ãä÷—Rª›ÕÅ(4èOóqÏèO{ ?ü ƒ…®oªûáèÀ@Œï¤Îãêîܽ±ULŸ>}êà¿ÁHÇUí݉¹hüãá¼nìØ±ó&ƒp`W’$ÓOé·$Ê “[äYá”n~r#¸Ša8d…¯¼S7ŒnY"%bÌ0¹]™Š@»ñºNyR²Í0œzsËîu‚Ò9ÿqÈ!‡ Iiá¹#`+Ô¹cV2W`²ãjÒa˜«ÎÂêÒ5©&¾’©¬UÄÈ?|!o úT_Ȳ$ÔxyðXqþõöD`±ÕèÑ£Á±z‰3šñîFc|„e ÈôywÚ# ÍbŒEË~c³üS– Q—$~I¦HŠ)'ÂÅ0i¥#isI´ëFT"Ž’†qfrCÀÕ¯”.(©éŠÒµLCý(­?N×)O$­Ô9Ýf’$>³â‡bi&DŒP‡fܲžŽUµ÷Ðw†=執:Xy ! Ô(_Lük„ÊUТङX©>«dÿÂûâ¼ægðÀ~2Èõ‰xútŒ5ÍY Ìçsa¯ÁW 'â+ƒ|ñ0­áäŸ$ . ›äWn®"Ó-É8‘c¹]?¢+ãýnúmUš(3ÒSÓ-?% 1ÂE˜'©p¥§Ô5’‰|Ø&è0Sd?©˜Úgø¥Æ¥Ú¡F¨« ]i\ˆ}Š×aâ±é|ÔÈui¨ÕjQ°Êú4&3NR½FŽY„ e{>1Vªß:å”SöÃØò$0Ùömà’˜ë1Ö< ÷Õx <RÄ´‰ 65’].—øR"ÔŒ§›V„ÎÊ0Å%Âðx×™3g΃Gdƒñf‚#@Ü„¯’_’aÒÃ覡L®ë”ŽiÙn&É>’"f“ ÃpHj 'õšåbWÇ… ’D…NØ“«J±­ˆÜ(2 ˆüRß›èKõñ±“^E.NÑo ?ïÙ™“œä×Á2V°„} îœ't^Cƒ<ýV„LR{rù’›ëÖÇC¸¯Ûµ?à ñ«ºvíúìðáÃOqâ˜Þlp ˆ)1fU0Nâ*PR_òKw”Ô¯ë‡7a¤ùMúÀƒ} ‡@ôÉœûŸïVæõ!`„ÚH¹yùW+:àͬ7V“¸JmÆ0ªÀ ^ŠÉŠÛ>ÊÞð!_Ü@pûG=àÊI©&Æ"×.sÝ.¡&y“eøíÛ·ß2}[:ux–ö*|¬e6ÂI]2hîìxˆDS ãt’x&ðOJº©3Ij‘>ªü§¤Þ‘ÌLjÃ2‚ÕŒ2B]MàJé²õë×ߌÎÅ¿§ÄÄÒ¥”êfu1 ‰NŸâý@ùb¢ p÷Ýw¯š;wî@8|® ’°JÒ•t‹L“̉¤‰¨ÑÏð™Ã×7ëׯß= ÓÇ ¯ïpÞô€ûï¿ÿßÉ4$}é¡…oÄF~LÜðÞöÒ‹+év-¼‰íF¤‰D@ƒwñºdß ˜«% ‚€í¡‚R‰§¹ãŽ;¾ÆË‰“PÍ1˜X~ yV‰WÙªgä<œ>ùŒ§»ãE¼-“Û@òr¯bgŠ=Ò[ã_­³ðð°ß÷ß Nòøoº2Íš5‹dëŠtñ5 '«ÆõÚ_íÏŠÍ5•{¦8âˆÆmÛ¶}ú: ¾™>òž{îá^o3á"à>ð0gùåvýº3Õš*\ñ&Ó À-A85Ò¾i0¬np '™êfn׿œx}²´#ð¡—ñ)¹•Ôˆ|óyXš}£S²ðJ‚cðvÀÑxÜ"ñ)êø{ðž> ÖÛ†w‡êå”``?$Ñ$ ´®[þŸ† Ò¬]»vOLã’ϱͣÈôëH﮲rUÕlî¸Ò- © º)e+IôÀ•hÙÊp¤5“x‚$Ö†q rHc+Ô9€UÊIñVþûX¥æù‡5nÜø4ÔÕ>ôRÊ ·ºå |–úÿ@6ùµ¿’ZíÄŠ{/Ð_ã…ËÉI›äçayÆ—òh52ö‘…qÑÀ¶¶Vx˜ÿ.Èú¿øWáлîºk~ŠÛáHJ5ƒª`éÓO5³´ËÒ ˆPãÚ*:I“—瀀})1°J=)&ÌŸaÂ|ƒÝç°ðw5÷À™1 2E¤³6¶vM" ÷>„cÃZػ࿧uü'NÐ`Œkr?Ûu˜C2­*‹âT+«! |)q4ÇeJ“Œc"ÇHgI"`+Ô*‡d ÐÓñWîû˜lºÃÚ‡^ÊAéVGC ø¢a|„e$èù :3 (¼û¥oâ{).«vîÓ ¤ýdìÿ¼ãÐÕÎ(Ã…Ûº¢.$ÓíQ—7`™fÌ%ƒÚ·­PI›F¨‹|„o{Ê6“ _NäÕf C L5jT«zõê 2}&æå-YmŒó ®]ºté„)S¦¬É Ó'"ß qϰG»ˆî‡aÞ«ì=°j7ùoú<Ïœ1iÒ¤åaÞÃò2¢€Úx B~`[>BV˜ê{v ,˜„sYzl÷ºXù C X!îŒyøé“qEÎɘo_ÁŠñUø@ËÃpó%²¼l½¸{³Å}ÿŸ*å9+Õ¯„qCäµö´óì¨ÇtØ£A¦W‡‘·åaD ô¡@„å6B²òì”{vüÐ Žê‰Ig÷¸×ÅÊoÙálˆ9øuØÓÑïë㊩x±²7öGÿ^6|ay%Ó,á¸qã–‚¼çã°-Q–§IªWƒ<ú`õû)äA2ý0VÙ€¨™® ¨vm¤@? ÊëòÞ¯# T ø<ÜÚ²Œ*“'Oþ“Ží-Œª‚¬\†@ˆ|ûí·ë@`ïG–cA:w;vì ¬J?â-eE¢»dÉ’£P†ñ(OCáG°ïyd ‹S$B<÷ˆj ;dz0¶¬ð+~f ’E€í>`ål…: PA“Ù)A‘²t†€!`ADú2ðn=ó°âö¿ Û®Îqèÿâú±Ò~%ˆºˆê€h×Ä ™3gþþ{¶B£;ÜÜ·oß3³¥³øà¡Ž•¥4 C '°å€GQ~UßÙ9]h‰=`w°» PÔÆäÿwês íojk†@f̘qf²ßdH•ˆú;ŽÍ;+["‹Ž€mùŽ•¥4 C '°Êz.˜†cè¸í  §el‡•Þ›`—ÃVk…· öÝ š[PÃòë“g¢S?üðz¾dæ5 ô•@[>Яì·0œF¨Ã@Ñò0 C 5 ¸A‹-L~(¶9ì òy?NÍøˆDw ™_þòŸ#^Œ|èÇüæþe¸Û±8húÈ‘#›çÿÎvC ¶"Ô¨êUl„:d@-;CÀ0„-O˜ð û*,ùב>Dúäÿ ¼ÇAòóç w1½:÷-Dž|A§Ž€º,D½zãh¿gQ×¶…¸·ÝÈ ‚”ýÉu rHc„:°Ê5é©§ž:û˵þVoC º€ÎH^{huóÈt·@€D†}é#áÄ<ÉÕÜ¿@v‘;'SqˆÃ±zïá û¡NsPÇ] _ÄÇZv„»6ƧþŸ¡‰C]¬Œ†@>ÀK¸5öY¡YF¨C´³CÇ»öfL\–bý¬N†@¾@¿yy¯ñëÒ×:¬û :´ò»¨C‡ó‘÷8ä»#HæÈóq<ܶx òw؃œ—Ox‡U‡\óÁçÎ?Ãù½PÏPçöøòáó“¦@NƒÿO¹ægé RD}!¯C?²|Cn€ùž–]Ì@Ç{˜EÆÄu~ÌŠnÅ5ŠŠHízônÃà—k¼í[:ÂÞФI“Ï@Ö¯@¶ü¢é›X•úá‡v‘¾g-¯äýJÑðŒ|<0pµŸçf7ÇB.Gýï„4c”= ÔV¨1Ù uÈ­Å>=2 ¥˜> |3>|!úé‘cƌ邿_?.ÅzZ VnC L¸åšY³D#ÔYQÊ-AvÔsËÏR— ø«õkL^“PµÚ˜ÔY‚U´*yC\âÅD¿j¯P£ß݈žûò¹yöÀjt¿r#Ó<á£aÆ30¾À #Óyk½–qÜ@ß°ê")Íu‘€Ûm1q]Ÿ,óîߌ[ù­¼†@±Àêéû$˜çZcÏo÷ê”úr\÷[äÑ òdl%y·:ùÄùšáÇo…>fƒ}ç|؈mœëde7ò€@ BûÚ uÈàÛ–-Õì8qaïæ“˜Ìkܸñi¨ç_Jµ®V/C pÛÇpônûÈ™‚@¿‚ëhËÒ`ËK»Fq¥Øñ€ßÇé-,K0¬Ò†@0Æ"ÔHf„:ŽÕ‰²êê V¦×`•ìZVñ,œ0P·La°j9# mè;›lûÀ6†mFµmΙ–Ék:cÿùs¨îX•~kݺud#ÓüJ¤}U±LˆU³ kj\d„º r5÷¡®9†e“VɦcB{¤ -^h8¾l*n5jˆúLb5äzÅöÝp~ô$¼d÷¶2ÌéÓ§ýcèÙ[d€Ùs°1ö¼2݇ïtø’Uñßnx‰z¾ªø†=¨TÆò|:9½Tp²z©@[D¨Ñ/ŒP§°aF¨k^¹^jz)WÍ[½³!€ÕÕ]ÀÙîÂ\õ æµ_#}S¸gÁr·3ü7 üyC¹6[^å¬~ ;dú4ÈíóäõÈçL`ù{ØÚÀ~,v.-Wl­Þ¥þAD¨íK‰á·…ZpÂÏÕr,yø¢þF}ígÕŠ+¶”ÿÖŒ;ö\Øù¥S»Ò© ôôOèéÔh Hõê™xd‹Ò©¡Õ¤Ü@»D¨ñ`i„:äÆb[>BÔ²3 CÀˆ6ø’â8£úI”²xÅÜ~ø¡ß¸qã>v©­t†@v°åã2pj¾3Ñ Ýÿ[>.ϘÈ"sBÀV¨s‚ˆ@¹!€— w‚=Õ>eŽæ±Íb3¼xV†w'ÇÜs?~ü‡Øþ±/HÅÛ ÝêÖ­û"öXoŸ{Nv…!9­P£Ô¶B²êŒP‡ ¨eg¥HßA Ò£6ïtݺí¶Û[5+^-@¦7Ç6‹ûQ‚[êÔ©óÇâ•Äó°ýã‹•+Wò0³PŽ6Ðñðb–Çîm„Úq Bvo„: À<ê8ns†€!PÖpõ´eË–Çb®ù5æ¥=97Á½«™wà“Ö•585¬f+Ô5±š×¡®&pv™!`ÄSN9ek¬Ôœ 2}æ¡Äi |Á}-Èî]üÚ^>j 2½ÎDžÑ¨Q#n){D%OžÑ 2Í#6{Û(W_éÿD¥|VC ”@?3B]$…¡.ðv[CÀ(<å/¢ý ûx¹o¶^²$/âïÒ«p^ñT¿}¡/[Í0ç-Ä=¸‡±ïÈ‘#ë狸g+G¾ãsà<õÝ Õýl_ì]&©6cùA ¡ÎuËú0÷‘ðŸºe°Û%ǯMj€tüBì_ˆ·á> éŸwÂ*ÕHOÞúØÑ°ÍaYžñ°ã«!‹f²o´)ZÑìÆqC€“'^äšÕ7{y+nÊ+“òbµ´Úè£ y` ?Õ®‹AøaØýpvôþ {ÀW2M¨qŸopÿ·`ë£,½RÁ½½S…Ç% «ÿp4Ýs¨#Éô»Øæq€‘é¸hÏÊWÐ×j¤Ëõ¥Äƒ lGØMî~^–yºd^oWXŽ÷Ð#“kz^‡kš@¬‡ý,ËBCIÿ*Ä·e@±Œ­P ù¼/&Ïv¨Öxòíâ2“'¾C è$_6| m3ñip ¼k0ŸÜ…‚]‹/eûVÃg <»ÃöE9žb›5k¶ü‡Òb?w„kÒ(:†¹dzGniÁ5mó«8ç¹?Îy^šKQI‹“^®BYº §ðA(*å²r© QMîC{´p€üø_$žêÏÃçÔñ? ÷ɰ[Ârà86y]‹û¾7M®éyûÞÅ5ð_ ;öï°4 a7!û‰˜ü¾å°[”Xá{ÕàcmAŽ/*YJ-Zp¬Ûƒù7°—®[·®ˆôÅ"Ó„åàDC3¤íaè%Ø‚òúÎ¥;6–äçK£Ïp@g¯_¿¾o\É4•ó?¨ËQ°/pÕ½"È~ h"ÀS>‚” ãLÆj´w¦á{$É4ÒqåøÈä}ïE¿?v>ìk°-¾,—órMÏkqMGZš?!ß `¿†½~Ž— ƒt)ÿñS|>¥ê|¢[†yãoÝëXm4òó˰úVåˆ"ÀK–,YÂ}m@¢ÿxÇw|]Œ¢Ž5ª¶D€m'㱂Ë}œ(ÚÃ’°5ó•é Ÿ¿¨^”yÿl`éYH× XOÃK|DeE¶ë¢:pìMèg{ÔíE¾PåòZÙÊ´Ó@„ý3#¡ÎÅ–Nú‘Ž[ÎÞIGs;#rMÏËGð'iþ$%ªò¼ãÿ…ã.¨ÓuAá.ý›-\¸p2jù:MOL<–~­†qAÇ£­)Æ6$¬jnè¿Â¾‰mQ_‚Hß ÌFÁ¶Ï‚Ýâ,ñ‹Æ®f˜´þ É¿XSÔïP¤áiL;ö¨RxÙuXŒ3ÈD}f¢âÛ@Ï@§§Á â#ˆP£˜ 5Ú;ãÉkÃÉ<«Xø]³Þñ¸n»c·†ºi\wºô ?*ùv²lJ+É“hö­…ÿ5B]xÌKúŽÓ¦M[‡¿œøŒ‡‰ÇV©KZÛѨˆÜ¾ z·@ˆF‰ª–â»ï¾ã ˆÜ6°lÐÉŽ™Di…ú½þ&þ V¡ÿ·j =ØŽøÇ`!î<¸ )ÆÃ‹¿\aù¹Ê¾téÒÃ1‘óa¨)ƶ¡Î?+ËÇ ôÁ@cLRZ¥LC“ üªJdzOÝQR„)(kúdør# íR™“ƒb*“š„¡® zvmJ°gòfôAî»:û)»¤Ld†@ À€Y$ú(šçá~Dï4ÈkeÞ.ÅÊøxȆ>¡}„Aï BÍÓF€í/Yèääz9p?ÛW Æ×Cü ØR32Ð O¾<"íå`ð0º¼xlŽÂÞ‹6˜À%ҷ•aí¡Î4~Iæ 9(ÑWHðg¸apçš>ÈCBCçAÒ;ÉÃq¡GËÅA€ûSч&!¨6öÚ„ã`cΚ!À3›AæN‘ù$šŸ±Þäm)&‘+–/_^´½sÙj"¶iNgº$)Ív '¢Hj¼,y&ʲ Y+Yþ°R=B•þgÁ 2ýË õÓµq’¬ty>ÏÚõЯC[¼’¸Ä©VÖÒE h[ ³Ÿ"/¾“Âó¦i~…2<»'ìøùpݶÒäšj•¼2(:ŒPGQ+%P&¼œx}²#†Ê·|ÍÕFàä“On âò‡úõëŠAúVØí1(wîêÕ«Ûƒäü~òäÉßVû¸dó>Üæ.”=ùB*:¡æ QÜ*[·X~à?7Ç:| ç#èàŸ€1·€.¯'ƒõÜîV©þºŽDy­e…@ ñˆ„MR÷DžË’Hó%ÄÃNHúS‰\Ò­“îvÝ”oFi„:#<Y]0Á¾kù‚R#|vø´êæcו7øXÐv +75hÐ`ˆË¥@c+Åtö´v‰»ñî»ï^”pÒ¿6/Hy‘®è„˜³ïn•ª¼èÛ›ÁÞƒ—g{§JSÊa8&”{9€] {°z,îã)e}•QÝñ:ôÝP·ea¼ú–‹gaŸ…åŠõãIÿ^2KèÈ%=Ò²¬óy ßÓHe>éP§BÈÂâ‹V¯eéÑqϲ՛øê±%Çv‚½a§à+‚¡ýœ‰24À9 ²V÷âj/÷&£l5¹'ʼõá^oN>}׿¾_“ÛVëZn¯AåâºxAïA<ôpEªì Úá |´æ Tœ/l†í1³†žò¤ìÀ±  @«¹ÙÆŸ\ q­-ò} ö@ØÝ`À>†¼öV~ð'ŽÑÌ5=®_Ìãdåå“Úò÷’/¼`Þ@O2+ݨ¤ÀêátTÈ>ôRRZÍ_e0ÀÖ1û îò ì`Ø1O€Üäå¬ Ά;Ö†@ÂÃæY_ÔmR ûaâĉ_³¢8âo4ŠØ:@ ÍãX©vW¡\VIn¿ýöסÏý ·yÀkÏF½€„Î¥Q;«E (8¡F»çÊ4¿„ºî¶)0Ó‹Š·1.×ôÉüþž”q}«¤;!’ùuL†ÝáÆÒm„ºh—á½0É$öRCVÙ‡Y†PX•³ Bv Æ©°½Ñ^¸ï/A¤GÁÎÉry¬¢±%Šu{uÝdòc⾄݄lª’غ°9¶0ü6èýPä&HÿNõÙ9è5¥”‹ó°—?ÔéuØ.ÀâþRªŸÕ%>püXÚ0Çíæ­ïqïâp…ºy2삤Ì5=/{4y-ÿ¹lH¤È<½4OWˆÂÿ¡.<æeuÇ ð´ûÐKYi½z• ù ò-¬ö‡ýÑÛb%÷w *EßG\½Úd¾ õü  Ét”^Që­ #Pƒm3×¢j,&¶-°EgF¹•‰½ü_aüA@eÚ°;ùWÊ|†@À؈Pc¼ PãžÌëšdµzc,ØËsé®O’_ƒd‰í¹¦g¾¸f·ÿÑ ¯Bþ|+ã2‘î'y -ëú†v¿òB€zÁߟ7£ó^{jÿLy!`µ ŠôuHK[ÇK~†¾q*&~U;P÷éÓ§Î;ìpaP%¸e‡ûì©Þ×~ôúRJÇ=ò¨Ï)¥T'«KìpÇ‘´…g¿M™:â'x“k‘ÝÂØþÿ˜L'"MïŸI25·š™À üý˜& oø'Á¢½Ø­[·a(N§Ì%¯»ŽuÀªìØš³êÄw'̆@H1–¤,úì&¤8eÂd ’óÁŸÛÑR¾ûÁdˆ»¢>l¤Ý²n2Ϊ&×ô¼×\±,Ç(Þ‡’ŸGŸYTc+ÔE…¿Êí5ÿL"¿_ú‹¡x7ñ‚V T˪!œ±0c©Jµe¬tž#Pç`ËÞ(Fµ-Î-æ¶¾Õ$Yçw@ ù‰æ`_íú|ààL.ù¥[V·uýnZÆ»~×­k "qžñuØu(Hmolú 7-(¡Æ*ùQ¸÷Žne1é¾òwëÛo¿}ï믿¾:—©\.vݼ”~ÙdV›øáÐkB–Ú¤>rêv äéx€êóÁÉŒ!xð­…~œ5?¤ñ÷Ϭ×X‚̡Όņ@@wÃÀ|VÝ~ŽdOž"‘Æ6Ÿ'3\Z£¨$‘vÉ/ݲÌ[n¥ñû•&•dXQÌüùóOÛn»í^\¼x1ߨž) HžNöøú{|ùòå·Ýwß}/87ZMÔéȳ?>•?¡·R#Ö ;ãQ§aw¯S§Î‹øˆQ?¼Èùcs5B 96fÍ£ÔV³V¸ ‚(ŠÝÂ0â‚¶vü Dúר¢Ð7Yf~J{2‰ô[ùª‡3Y¤#Ê ×òL&7㔇Š+¿¤Â &§OŸþõ!CÎÊ0_òKì§.ÄÍqÏþÀvk|îüú ³fÍÒ)#¹”Á%Æ*¶Hµç†es't²qëòPÞ±“X‘^ˆ¾³? þOÔioØðPzúÌ¿cW+pT4~•BŠšxüIÔÊdå)sFÝ«e›ã\âùeEäªÆg E¡~ËãŠHrVÀŽƒózlëXÀ°|M®ô“gÆÉ2Nîzp³Ì]`ÛÃrÛ‚›¼iÒ¥M÷ˆöíÛ·Z´hÑ’~ø!è‹JÙ&7žnnù™û!,OÐ}(/Iw*‹à§f\ìÍðáÃñ…òF÷£"ýÑxòÑqX©~"ö³ ™3gND!†e+?N:ôÐC'eKgñÁ°êàXYÊ 9Â]›a%ìeËíXñ<_[;a§ÂÞ©8“ÅGúýh^"p#ì- Òßå»tI2-b+’ì—"Ð"Ëôï˶t,¿®e&ø¢iŠÐ¼}Žœ„ý;,÷Š“LSW”.±¦›:VX¢ ÍÅžTã˜ÁUø€Î@Qx;*5u| Û?FƒTß ·C Ú {$‡ËÌYØêÌøT'ÖuuP³kjŒþöüzý‰x1gì%ìŽAà|µ>ÌážžüÔåp3ÀËi¯BG\~çIß“¯;ü¥NÎ$V4éH4 ™kÙ~.‡åÃýîÝ»oèÑ£G­N:yxhcP¬ ûE\ ^²ôæÍ›ç½úê«?}òÉ'mQs`GÂò‹g7Ê<³’$Ìå•hTîÆ„q:ý`^Y¹ w%áqëq»¤€tFõ!Fí‚’–ã¥4¥ì6pO…Ý~×]wõ>øàƒZW_}5É4‚ÌD½öÚË{ðÁ½iÓ¦yM›6å ô1°·Ár‡Vº¤že]½#8ñN†•„Áû"7ßðß~eîôÅ{ùq¤T•:t("ž€ýsªx +o¸.´ŸØo RÏB¦1B]H´Ëø^Ü "}7V ç~Ú8yË_Æh†Wuì㬃¿™‡@7o@'ßÂÝ/¼Ük–ÊÉA“ƒH´¤ŸL‹Œ‘˜µîÕ«—÷ /x8‚®f…°«ó†@ÿþý½—^z©V‹-8±sûù°Ò£H5õOKï’*-ƒwø’b?¢ïÐôßb‹-ž6lXS·–ø0Lý&MšLE<·ÉÃ-sn¼¹   13#h?F¨3"”{$'3†@Þà„²6OÃoâ&'¡s bŒPA©ši°úÕ«`¿ÄKQó09߃lø·;·t,¨f–ù¼LÄZ’ã–¬ˆÛÕ ØýÚ´i“XÅ) ù,“å;í´“‡³®©WNîçÁ¶ƒ•N%¥wJšJ‰¶+wEḺR=g€÷F5¸¤OãÆŸE?mÍjÁ_ç½ßÙ‹d²ÆÕËc^e+~Ȱ]É’m(H:K#ÔÁ±²”Õ@`Ò¤IËqÙuޠסNƒ÷˜1c¶H•5˜“3pþ«_Ÿá-ïë öÐÍ\üÝ| Nz脽›s²fR¸š$E¢%E¸´ªÉݼ?þñÞV[mU¸RÚj„Žîò°:æÑ†¿„¥>¥[Iêœi$Õ&TZgU¿ƒ€÷c¿DÿÜöE.L ïþî£9–BòÈ[Ê~ïUZXmjˆ@ ¾ÁöSÃûØå>88™1òŠV]nÅ ÎâMrèÄF¨ShdøxœŠò&Òœú.¶rì„kù•¶ùÈöBHî]úˆ¿šw‚‡=­Ü¿ ƒòiR ¤u‰Ý´$["ÓíáÞ©yóæþ‡ÓLœøýï¯âr뉵Kª]}«=PÒHVøJäwܸqŸ®Y³†DùEØm±Íã2t‰Ó9~ªo8òŠ©¶U#ØD‚dcÇæA)·4¨ÌyG+Ÿ7cô\Nœ²ÝÉ*_VÌ–¶\âI¦ÿò݉gB©7®9dúq¤}v®­ƒ<€{<èô†^ ¢ ÷ÊSM”"ÑZµt õ¾¼?Î6å‘xyÂ.6ÙvëÖÍ£…iË}Á.¡¦ž©{¶¿M¼ˆð’3wÝu×ôOn︕;‡ýýWý!QßdXoŒ‡•V¡ê"P¥dÈ$ë<œáZ‹J-3†@AÀ*è wu0'\“jrðÂV¨@ð·î`¬(Lv$Ü¿†xÄIRéÄþèÍZ¶ly,0þ5ÒïÉ9îÕ°ðWòµãÇÿoeâè:\âä'S"Ö"]uQÄ.]ºD·FV²Œì½÷ÞÞܹs™f;X§© ŸÇ{Qçô˪}( ¢JϠ߉Z]Íš¡oB”†ñàr$y’éÌ”7jÙPàœ-Åç†)3†@ÁÀŠËµèÇÜrq¥iŒP'µ’$Ó|q0ñœ÷ÃÃIbUÖUV¤‡bôv Þò+ü3p1þ>nìÏŠ™N1!Hp¬r­V©‰ mX¯Aƒ„ ÓLÌhݺµJÌ£ùD½JÏ™V©7!šÊ(Î’ý}ý8ñ®.Ø×Ñ÷N—ÆÂË4‡@¼m†ªfBD 1A‡˜ŸeedEÄîJ þ\©¾<9l2!"ns…‰¬Íša 'ð“iVØ/®Ró ß„I®LßèÚÀõ#Èk×®]{WŒ1d=ýÖ%X"Ó$_ÜwË•{ŠÐÍÚ¯çys>û4¯¥·Ó.¼ú¡ß¡æ~=ï]/QD¢À¬_ïyÛtÝÅkÃÏ«d1ß~:×ûdÉj¯Ù6]½ÎA.È’_ªèúõ+QåSuË ßµz Ò*ue6ìK*bî@¿ï†*q«VÖ'DÖÕEõ¹ÏúQH#J1× ‹¯ù!c6¥Ô_2V´€‘F¨ ¶Ýj#Ø¿{&: @—°ckRؘ3j:\¥.[Bªã€OåÊ´‹M³AHÓ(\‘öð)ð±—òp\³¶×<4qŸX]2-2%)bÍ1L«—.D¡ºçÜ{Š·Ç¹³gïååO{û ¡¡ kf+¼{éáûNÖ„U\ùòRï7ûpÛr&³Â›2pGïtäÝãÊ—½·³O¦ÄaÄQŸÔ+??N·$Û3õOé¶’!Ò¨—7¿ª[·î“s:Íé»cL=YLb>fÊTsi*$8‡¤ ·°ê#ÀÁÉŒ!P@úþ„>}9€T[ÊöÅD’iÀr/“ò¡79h²ÿžï*Ÿ"~{ðŒ3™YbÕä¦d})E¦%¹&KwÞLÝzm“y7MìEÈÛjq½N¹_\?àj¶òîô‚Ü‹â^A=³ÝÓJÇ “þ%Õ6U:¿ú9jSù”“jlÌPÛKø±¦ ñUâ ½°_d5ö¥Ä¬圀“C µBê‹1\É{ú'L,Í Y–BÞ«ð¬2‹²¤%Óº„x!ÝÉ£Fj¥°’"K~I‚%rE·HW™eM¼Ñ¬ñ°W¾Ò®ß°Æ{öÊAÉ&ÑÇ›ùùo½Ï´¿èYÉÛ¢Ôt¨KiIé\zV»`¹]w”ê‘SY4þàøú¥K—¶A÷û Ãf„¤ñ±&žæc&AñŒiº@í…óGLëÙbÛ“ldU~Á8¾Ô ך\›ñ¶ ÕaE–{ªÅN®r&W¨óvߌ… 7r“Kuôߟ>+ÿ@xÖ¾™Ì£¾žv&Ò_âÏ+Ž~.Ô½kI¦è÷.b%¢gá ö¨ãfu¼úõY„µÞ¢yó½%«×ã辦^Û.¼&iµ¸Ö›÷îÛÞ¢eß' Û¼MgoÇÎmª*ý‡µÞÚ;ö3›¯±yñ’õ^Ë®;bßó¦שƒt¾àæÍ´7¥©×¼ â±MÙ—$Qî¬eI”²`?Ô%‹É­@ A®F+ýÇî_ÛFUÒ›ûï¿bï 1bÄÎèã£á> y$¶¸ãdŠ\þ0xðà‰Èc]Џr Údì%ÙtA|c ûHVó:f­_1e*FEËñž4\“MR®ÔŠ_ºiBwcUæBèY^•ž[>B¿Wòôc)•ºá ˆÇ%Óˆ8‰³¾¼ä^·Ú%-±’tqs±,hµV¼{kâT‘ Fz÷L½Õ;ªVè½=zx;îØÑkZwWïÆçmR¦s§‚5ðºôø¯wïÞ Û£K[¯î®§yOÌ[‘L¿Â»ó¸‰üGÞ:Õ{ಣ¼­:"ï=zx“æ(Í&Yç¬,9g[Ó ¨_êÕ•®þÕ&\YÓ{†~½;öºãZòF™Ú²ÚtBÞyç`Ñá‚×^{m[{9ý}V¦Ââ^íZ´hñ ¤qûI¹º«` LäO…G%¬YtW™.ŠŽm-e1P§„¥FlTfJ  ¾jùp,š4¸0LîtRRèûR=V军ÿG‡~Ÿä™;ÒNÓ) IDAT¸ð®2}4^Â̺ÍCظúÞGåtÃJÄÍöIão§ÂQmƒ«™J›¸ °?“¼¡ƒN÷Ýä¦ïxçöîï=±ˆ‹¬fÅ»wzMwTùÖXAƒðšcÒ¼s›×¿Ë/¼w“¯ájyqÒ郼Á;¹Ü÷¬lÓÉ\Ê’.<…S¿Ò-ÝÔ­t®¶àÊ<£zÙ¦U~ÕMõRÝ4¸éª¸_ýõpìåƒ ×ý¿ùæ›~üñÇ+QÂÊ'6— áÅäßî»ï¾M/ËIVÁ ¤ò»x«-¹a¼¦ÒH§”•u *£í¡_UMøÙ[Ž…D@Þ¹§kêYƒ†pI5Ð’ ø-§qZx¨~ê_ÿðÃãq„šo†šò ‚•ô°ö@S]2Mœ’æ|è¿Tú³;!¨Ýú¥ðs¥°(ŠüÝý¯{Ë×oÀ?˽Uî]~Çûhñšdy¾öÆYY¶fÎ÷Þ~äïiìu~}ò/“ᓼ›Ÿ—p'άLÝÇ»áÑ—½çîÑ#Œ}Ϲ•¥²…qP×.¹‘Ûm,I±ÉNŠñ×m›*«F7Ç7Õ2ÝxWeLyðÁ?»ýöÛ/{ອ^½z0ÆÊâZwL«îÝ»Ÿ“!¿t÷)…ð*X¥ÀÀÅ[néE~¤?aRèYQQ‘¬KVƒ6ã¶—¬é-AvŸ=KQLRtpv~ê6Ý ÎAƒVƒ¹üþ0ÆgœB€Ç÷+ê»±ÊÂ-¡ç_À<…¥êAŒ©›„øðáÇàåËII?Ä&&Ð^>´ÎØ}ô&W—F€Ú±Ú51T±”¿hµ6áïÏÇõLî™nâõ;çbO¯¾?÷óD¹ÖΛáýöŠ"»åu;$Ë[ßëyâÅÞ•=*¼/ðM2\¢‡7ùƒiÞ9÷ñ:wh“a_¶Òg—Õ/Kö¼CHAºÖÕ5ÃåáV5Ï"Åø«²«œòSVéÿI¿;60^6Õø¡´ ‰•êÚwß}÷ ,D ý裺¯[·îR¬PÎGV ÏÝ}÷Ý·„³Ê5eä÷ã'\]<&?Ý®®¨?ú]=Ò0Ô»ÜQ’AËåþ£¥òǹ,lHfbŒ@ŠÎÀFƒÝ sÅKºéünúi 6€TuÔQ{ážãl\B,wB6ìp|Ùo*—¶ŽôX÷eÐóR¸inø— ü[,20ì[ÆÃÎe☵1µËt~¶eMt«¡úƒ¼SíRõ¾õ;yýÁ¨ÅN/¿Yˆûü•g’iygسjzœ’vÆÔ¼¾øxJC|<¥Štž7 [åGOªDU×Sí²T÷†¹]Gûu«±‹9©mä–kR£Ï©}ºårË'7¥Ú¨¤Â”F%ôû•·âSÊY³fqLøí±ÇÛ û¨OÚu×]ÏxóÍ7Ÿ.OyQij¼U-éwÃäO%y Wpi©¥s£ŸúÇøëæÉø¢Ç×,SǶ|¤Æ¥&¡F¨k‚^‘¯õõ Ò,•Ü”œýnúy?_Ý–G¯©=ð×­°‚ÈGðw8Ì!¹YþoReÐmÙ²e£eË–í÷í·ß¾‚ð+¬RܨQ£±2¿ {#—âå£%ß}÷Ý·³gÏþEÓÀ®AÝ•,9Çô*ùç¿:»ƒÛþävÛ2Ãè/ªi¤žSYŠ:Ÿn¬ô{ÞêÄÚó–;¡M:tózv㋇ƒî‘訊 CV·,aÜ;@Ò)e6 »ü$qÆ_•‘7’[RmUm”’¶!,ÇÞma·‚e â5®ñûݸŒnl aü'ø@ ŸÄ.̘¸<"ýã#ýì‹a?†}–a´o•žºÒø gBG"Ùô'ØkÄÆß@í‹1ª#ëa&6™BÈÓ²(Î`λi°Ö .©Á›~º%{Á}.찛Ú)0K–,ñœ_®bøÅɰ·ÃòÕ4x®n5°SÔí!Áª^ †õ£q¥Ün;f˜ëO\ÙŸzäP4]½-‹}&K”ÊRŠûK½J·Ò¯Â(]#?eÁ‚3þª\,“¿¬ô+LîöØCaóÞ ð`ŽÛ˜ €ÀWHó0ìõ°_²-i¼…³’HkLvÛZ¤HµÓ6Yî´&bi˧#ÔqÒV²¬¾ÚF»pú5ˆKò%¿a‡å» ½zõòð!o«­¶¢ŸÁf ˆV¡½… zX‰ö°’ÿõ_ ;ö,Ø`¥Wî4ôÓMº2Ñ,Jhd=eäþpb}³Žó4Í—Þ7xO±3ÿrÌ»8zï‘·¿ò¶Ý¸7â`vÕ<š(•%u5©ÓTzwÛ€âSç§PgüuË¢ò2LnJª”\¸¸výÈcNáðvÚi'o›m¶±± ÚàeNoÑ¢EÞË/¿ìÍ›7ÿœ{,õt,õ&RMIÝRŠlÃYi¢Dªõ‹š+*•Pl‡êbk f÷WÇ¡¤å@©A\nú·ƒå±lÛ5iÒdÃ…^XëŒ3Ψ…}v2Sl°ÍÛ>}ºwÑEyo½õV{”‡ÿÙòè‡û`¥_Jè4”ð%KT³þ4.jóŠ«HÁߎ{õG©°©öÑyûôãN+™¹ÞM8z³xŸ+–wB¥²G¦Ó¯«ãtn'›¼:UFÞÄmƒ{&ËAv"ì¾Ø‚±áì³Ïö.¸à‚Z­[·F™( €1×»ôÒK½‡~˜#ývwXþ‹û=¬ßhušc0ÛÆäHjeç™Ï_ô~ÛC½‹°\ìðfb„€¯§hP§Ô îè|`¢¿%l‚Lï±ÇÞ|P‹ÄÍÈ4P‰ˆá¿ýû÷÷pÖ,'[–ª.ì °½a©GZMДԫôgÂMYjÆm㪛&·â"+›ôè›xBbÿ¯ÿ0ïw¿®(ëÚEÞ=çý"A¦pTßí+Âóø¥²¤¨&uJ“NVÄVÄ+Âò*}ã/ï¥qW}ÒßGyúá°û¶k×n믾Zëšk®ñŒLºè˜ÝvÛÍ{衇H¨½Æ“0†½ –ã.Çbw f›“žáL´S†´-òÆiL rØ uôjÌFa&žPwêÄ’.™¦[ƒ÷…mG2ýì³ÏzmÛ¶gË Ô8*лꪫ¼ßÿþ÷¬-õÇýÔ\á’>&Ýû%¢"3¨³,aµsW†}¬ù¥Ú•š*Lf©Ì°Ngï¢gùŒD3ËÜc+o׃öj5hë ½~VEð°ÉÞÈžT·çmr}EŠjÿV)c‘Ë Ô1d…¯ê˜§°BKµ?õ?úé–uû*_ì ½á¥—^ªEâf&ºàd)ïé§Ÿ®…Ó—Hª‡Âž +}R¿¥kÊ*m4ÅC×ÿ³÷&pRT×þøEÐAA .hdL ¸$ ·ˆ‰â3JÄ… ‹þÞSó{QŸùGbÈbˆÉSóüE#DÀÄ5¢q‰AQ@£Ï‡ˆ‚ ‚(‹¢ 0È ÿó­©os¦¦zºz¦»ªºçœÏÜ9w_¾çÞS§oß¾%Yâ#i}ÊIvuNˆòÎ ø¼kµEA ËB *sÈ” ükâÎÀ1Ùr‡W‡øFË(íüä'?ñ^I-ýÄ· £ÅA‘S™“CÖ\Ü.ªB•²åB{½‡Z¡V‘ùß~®ö«„**°ùÚW®ûßÎ?#]ÕÑ/ Ñ]]áÖ-œ•¹£zñ\ß–´îsë¦ ÷oô¨p]ö«»˜ºªÝîòºþ(þŠ*®÷†}Œ»/Qú›#OQeœ£m¬+¶¯çüг\‹X›Ô¿‡ˆ¬Û%;Ÿ­d‡Z‚FiG`À€N^÷NY_'ýÅ]Þ”)ä s ?9ËIT2„#QZ¶#QPÊ/îP̯„åN ¥Ð¹xÁƒ‹J`†¸“~þóŸ{çsÅoT",X°À 4ò²N¿,ÝÆ&c­¸‡3|ÚáÆ„KŽd¬œÏ|€a.âĺà`Åâ*0¸ö¾ÛS8v“Îùñìn¸áñ¦6»•ËVÈ›¥ŸÜþ=zº®Uj”޾@nø )ô ¸™â¶ˆÃk&á°a‡Ûop®Èq=pM|.ó¾h4_ÿÂXã<%Ç|¥ƒ á~)îyª“W†‹×¨”8ãŒ3Ü_ÿúWt?äÇ]ÞœgàЯäÐÇcîÁyº·˜sQÚÈJsæÌY sõkY3ø ²C=ð´ÓN{.W>K޽Qé#@¥yR©ƒã»ã¯â“(~€XúÃlY#À ,½{÷Æ/Ð!GlW.RPyÓ€çƒþs<ü“RèªqzKL—U¹ž}úƉO#m¥©/™nr^g"’ôøÆ4ºÀ5ÆîP÷‚CçbÂáÃ~…ÊßDÀkTBàwF¾A}®tFµ&Ó7ˆs•F5æã¼ qþSsµÑf[Øó¡Q, •%`T¨E™Q©keN¿VêÇIÞ 1Ìì6qXO:é$F÷Öä”9ç8IûWÎü ®cÇŽå<Ʋnºñ)ãa„ðÄ Õ½¦ôÚƒÎe~8¬×ÎGyä.qâ5*5ðí œ}Ç\²BÓä„Ä…ßäž[A  3 ir,þÈ„{¦J®]ñKp"7hHCÖTîÈDEJޏ–@0¶¼ë­Ž;Ÿ!JuëÖ±Û›è pÈ9 †5ºEÝËõǵ‰5 Ýûdêׯ_K[‹vYlb9¹+œ6R jÊi7dÌù Þº°ÚC\œiÎÙuáE‚‰aTZpáb1Ó¯ +.vpü˜Â{i xÚ¨fãJ÷üSsÝs//r«WÉ[`å÷S:tq}ï¾úµA®oOœtPT³Þ-ym•¨5èµ($G,+öw}ûtϹv£[¶ø·µbowXßÞÕsî„稬ÄQapN‡AÙ‚ã+FÌø5çW]v„±jÇn„çí·ßv_ûZÎãƒ,cÐ!þR&m$icJû1>†qLàÅÏ>û¬Õ5×\SÊãnq}ï½÷Ü/ùKÊûQz†Éùð§¼Óàƒ°®¸¶È©o™¦ÃqôÉڈȗÏTrÊœ=ÑzWûãé%: ¥1Û¡Ž‚R~y01ŒJ.pú¹˜Á©ÌÁix¥gt5KÜÏ/˜â÷§ÚÝ·t;¯Oó]ûžíÚ´ÐíØ¿îu̳Gº–}Ç]Ò§ÒUV§ìžu¿¼”Z;Vî)—KzƒQÖäÂ… ѽõâþ ²Ô?ü¢O9kùKt¬„µE‚Ÿº7¸6_šT³ÜMþþ9®}çnîÈêj׿׿úH×£sGwÌ9?t/®©3¢—MéäÝž›¾ ïÛ©O5Ëïv~úOC¼Bë®ËœªÿÔ¹|¾2Lù³³'ªEçé9Ê~5àvm^Hšá•.\Ì\À4¢OÓ‘V\Ã_öÜ\¿©j7î<\ÓÚ8u4ÂMðT/~á&¿›ÝÝvvC3?~¬–ݰÁ™F§\9Ä]>}I&œre ¹òá­åt΃ÌP¢~ý—) )öHQôkã FÜ+âî÷9Þú•¯|Å=ñÄ8Ÿ+QFi@?@üÅ/~ápG³2¦o—¾ádåHš2æ|ò8†¤õ(üZïÒϵˆõ ‡ø¤núwuãn™í÷}°1b¨÷V)D,ž}£;¾ÇÕn¹ØÔ‡šß]ëwNÕE/zô^?}¨û—/ãúÏèug*NòÔ²¥Ü©sNCõ|ÍÚÛ¡Î M“0AŒJ.½€Ñs„¹˜Áil1yR@ÝksçÖõc𮄛ìäÔ´;¨—Á‘ëŽ\¨Fº~þíî‚™~ÄÐ[ÝŠû¯p=eÆ×¬Ñ]wêñG¹g޼ÓÝ0âf×M•K©—²¥\Áõ\ D^*ÿ0˜ØwøƒNZôÓ#_ å6ŠûöË/¿¼Ï!Cܾûîë𒜃:Èí½÷Þ’d'ø@ƒ{¦q5nóð€9ãv–»Åá <å§9dŒ9@YçÂqúŠ5§‰kPsø¡ƒ9‡uþôû7¿áîòmé¡w3®?£î7(36»ù“/w'ŽƒB]äÖÈGŸÞ=Oq·vîʹÎͽv¶[yÍq®gf„+Ýì+ýŠÆ|ÏÐIò©ùÓE«<=2ÏZʘ²×òÖþØGuÅ®Í+¼h0AŒJ ½XáÇ‚¦ÓaÈVçMx”í]—cd»y®X±¢€£M¼ön/Ù›½À½Y3ÖõË\Ëœk85îoS®õ3p gÔÓˆ¨ìzœ›0e¢»åx¤?íÖl“»ºýœ)fZ¶TâäÁnSîàqÁ>4'¬û ÐѸ"×¾†Ã¥Æ¿w¢¸þ6lèòÀˆ7Ô¦M×·o_ÏàĹâ2'Èm…¸gÅáÌ4Î Pnà”'å œ•!o¾cÇ-Ä8ášaÁ! §74$XZT»y£ûÐëòP7þjߘöÂUnÐÅßwCÅ ž- ü•w6ºA:¹o\~•Ô·HŽÝsËÆ»žò;PÍ’¹SG·~o§ïó­Û/žFC¾ZÖô3aÌWâ¼¹é…âû‡vsR1ÖKÎFË}[ÍBƒ÷ŽhPosâK`Ðà®WdcZò×®pÏÎôJÊyíï»~4Ìë¢\UÿÑnéÂSå~®î°ö~dºeNù"¬♇ =Ý#ŠÞ;SÚÀ‚Ÿ†¸6ÈäWTža³]øF¾tœâÜìß?êÖ\ÑÏu¯]î¼¶®ðà[¿ãz++#¯º#4cÈ“úœ²æ<ˆ±+7%Æ4ú–“äØÖ‘QPS½€µZUÅF€ÊZs.l.tp¦»?ê¯rN–±Ì–=ŽÅãÝ3˯vÃ{7¾å\»ò)ï|žWùa]\~ÉêÞ».…:¤K„þ¥>‹–«ö£ã”½–7ò”ºÂ¤Á„qp<4¦Ài¨Aу¡¦xÌ꣑F£šX†a¨ñÌTRhÏ¿øÅCdgºÃ–-[>zçwVºþê® Í)ràÎ]hÓäˆ×޲¤¬´Ìág;âÍPX\&±ÀÌ=_8OÀuZ›Í¿ºÚ5óÝÝö¸ÛÞþpwÑu—x¿!a-µ;2[Œr›—LvݪÇeÂN~Ž8T^¶Õ«ÿ÷µÞ5îü xŒŽY*Ý)òF[‡ãu¢×Ÿ_y½;gÓÙ³®£Ë‡ögÆ&Ô)še”/Â$¦çDœs}aÛìW(·3Ô¡°4+Ò êfÁka.r6ްvˆ†™7Q¾ÿÑP®u_N¼í7üæ3íϼ»~—IŸø½S¼ïé3¹<;¶zß+#Û +>ÿ=ë—Ø¸Äýö¶‡Ü'ír︤ÎP£ÿarEˆ¼.T¢ÿq®Ïÿ¾!íhHÁÈ‚ÁLc  2è3<ÔÄu!/ÞÍ2Èö ”èx¨OŸ>G %1¦—kxÿX<Ý(D+AyÑ&Þ”gXt4ªÉiLƒ£ê!×mÀ"¯ þp.[@:æçy0_ìám¼â®½§™‡ºo\%µ:ú¶ú¥§ý©qüŠÆtµ›öÂÃnÄq=½Eåe\ÿ„wÛ‡p/S¶Ó g»1îZﳟ{Ùí·áñº´Á“Ü)ø¸GM«Û/œ&cÈDÙ×…vë”)öÜd›¾[…f¢B=±÷-´#eÉ QFCj1C¡ÒÆ¢¥Ãàƒ ?5€t?ù"wߛŷ q?¼öC8-â§î”ñsýÄëÜ·Û}<#¼D ¶ê07D6ÄAsÿ¤[SçÍü_þømîÊñãÝøkïrðxO?QÆÙ8G€ôr"(}*~mTÃO£ „Füà0Öà` ÒÃˆßæ§“3o,¼K—.;w>D‚µÿøÇ?^Q}¥ýÚÓ2 ŒhLS~!pmTc@îtznHt,ÄõEý|†"i±t¨ñFÚùÉï¸Õ›³ç¬S;ä;uyª¯›è.ÑÆ´D¯üû<ïÂ¥µ´éãFLìEϼ ¿;Ñ¿Ý㪫Îp»µvënÐXb+–9âHLg8)®û”µ¶Cš&'è‰ÑäJ¬`*À"J‘"ä§û·§&dn<¿ÚüýÉnÉšž5„„ÚÍ+½±:d|&ߤ…W÷—3iÙ=UîŒï]U—,_EŽøáýÞZD¬yùn7šolümw”ÚµÉ^_jR(c­¼©@ƒ<5γ#4”4§N# œÆ8ii4Ú´Jã<ªÓå æïׯß±#¿~ýú×>ýôSü`¯`u¹®¨¸1Ç…0e®åEùáÃüüÀ¤å9Aâüpøfƒ‘EäzÍ¡®7>G.bò«º}—.~Årÿd·Ò7˜×¼8ÝW÷m¡«þªûu ÿ›•Å7þÙ½¸ð ÕnvKþ•;d(ïí@$wžáwnÀù—Öy2ÿ‡ºï î™ yž&Ö]¿’DCZþ¹ÖʼnvL7îç£BýfP‡ÂÒ¬H*‚fUb…cG (7­È¹è¹Øcï\c v?ùz·ô¾ë2YæÞ2ÎU÷èì*äWÉ¢\EÇCÔ‹Xœ›0û-7Výx0SyìfÛxéyö½{R‘uîç»Î­Žq'ŸÜÊõè÷«tÄO»uxÝ=«ø„'y ‰re×(srÄk?ó•:§ÑDãŠF5žø4¾hŒÑH£§9;pÜMÇ0ýàô3œi…äÛä^ì/I›î7ÞxÞo»õ³.âB޶à'g|CˆÓr¡ÍA4¦µA­?PÁOš\¢%ê[púío#wEOìwcæ8wHÇVî˜cŽq=Ž™Ùm¾êúo9¼v÷þŸ*×àÕÑw|· wò9ç¸c*:ºê¡×úñuìÊþåÆå™¸Ê>ÿ’y —yÕ÷H7 IDAT\é^DÓëÎ4’¬º•ú•\÷( ë¼EõãÈG”ìÈG”ò˃…oTº`áei1%9ä>çýÂmzë)wÕà콨z{|éGîú³{gÏ„”Š ï>4x«äuâõ©«»âéunÖuþÙy„ðÝ2®z„»oá:wIß:­_QAí_å²­_OjBɶ±´Ô JGÔn#%ð A¥w-ƒF5Œ5i4à‚†]cÆæ)L–ovX^éÜ«mÛ¶ûÔÔÔ¬ûç?ÿùª´Ùì:cª#ˆ ñÇæC锉–?Á˜¦A Ó¨¦¹Àù ç‡DÇF%´Ö:¹±,v凅¤Å‹wŸ‚së rú?â(rÉàÚ¨¦ŸF69ËéºØxH½^ä8O½°Ê¬òOh´‘t–¼Ø%mýN®Â5kuá°Lª&. rÝüÈÇ™R×”–_¾–c‡a€dŒ,WŽ]GüHUÿ0·Éᇠb¡ã™̸3 5êÒµ3†5ü$¯ ’wíÚõ4Ô·zõj\µ€³Å¥JÀZå@® bÎað0G#šeuÖI×íz|A?ú¢‰}Û9ÝóÏã·¦F¥ŠÀÇÌ®ã“QPÖHÓqÚÏr±s»å#vÈ3 šA¢¤°lIò]jŽY“qœûHƒ1†h8øiDk®hí—ìõ(Òé^‰ƒ>ø›È²yóæ…•ú™âÏQSŒ‡<à×~Ê)è§Í2ÓÂô ¿.&Þÿº/ºeÄcLøõyûe› 6´Úwß}uó—[¶lqóçÏGO!Ó•â Wιlò—,É’,‹H›Ôr Ç’l‡Ë¨u3¨KS˜z1Ã…NbqoŠûtéÒ¥{-[¶ÌÉ+Ž™Çx °cÇ÷ì³Ï²§KÅC…NN…È0ó2žá’å0šÔÑŒF1~ ^ŒŽF3ÒiX#ãé×N‚™ºßåJÏZ¶wïÞ+++ÿE2Ô.Y²ä^áÙ¾yÈZGB ¹æÓ5‡?è —|Ê#¿®G‚©!Ý/úÑ9·[*»€Õ³åê¹Q£F¥¦ÃÖ‘h<ðÀ8. ½ç(> ‘0ƒÄ8Ìy](Þÿ‘ô“íP^(fPÓbÕ˜k"ôaÿ+î_~ö³Ÿ¹™3gŠ×¨T˜1c†Û¸ÑÓ㫤ϼôUÏøuX-[¼ÎSJ~Œ ÌqmT#Ž83òa €#9ýÕÀ¨Fy N²;½·|@¨Ø¾}û_äëäÕo ™ ƒsaÊœu”æðÓ1¯æºŒŸ³Ûb}éþiÿ ’V=qâDwñÅ;9æãe¶éG@Ö§ûñ YB<'Žó3È‘‡ó@¼!.1Š´=-½³%^D¶Â i5Ri³-æÇ"‡ÿ/â¾6kÖ¬6—\rI«SO=•eŒ§>úÈÝpà ìá£â¡"ÊaMÁ°N+I?Œ'ÿ!±á,h3cƒŸaðlÆ4ÊÂèg¸.¶áÿ\é K„ÄÌ;w‘¼¡ìt1¦ß‘dïGÃ!ÙJ! øf#¦ÓO}Ä8pÆ‘ë8~´‡é€pRĶÑg9â9pÌ=ü"ñ¬7ß|ó€o¼Ñ]ýõ4*®»î:·bÅ ¬ùÕâ GÈ”ò­7%ž„t’Îø¸x$]euáÅauá1-vX´Ü¡ÓJœ~½ðáÿPÜcâ†6lׂ ZuÔQ4J+Øùîw¿ëV­ÂÆ´Ã!êÿG¹‚SÖà Æ1\[FÿaDŒjbÀ‡gÄ#˜s§a:ñfüˆ RX\0O“ÂO>ùä?šT0½…ÂæãÀµr!Ž~=™_s¯Ê€ˆ™7Ö®î?üt˜“wÕøñã[yä‘­Î;8»mÍå‹Àm·Ýæn¾ùfÃm38eLŽù@?å-QÉ“,‘=DWæìˆ|°G¿ ˆ ³ViU­Ôág˜~r.p(t(Ô‹äø@«N8a×ã?.A£4"°víZwÚi§91ºÐ½Í⦊ƒƒÊ[ËZ’½¹À8†ÁˆQ/`­Èƒ Ê`º¸WäE­Î<óL÷­o}ËÙí鑯Üànºé&wøá‡ïòYŽÃÓ¿÷‘8*s'”3dtåQìJœ ÇÁ}#‡cG“ðÍ[ÐÑÀÓyt8Ì0´¸è†5° bʸ <†Ì´ 9={–tCÃVŠÄNè'‰~ÝwŽcDÆOÇ`W_}µ“—ú¸G}Ôɵ‰¬Ëx‚àjÒ»ï¾ÛÉ7·»~ó›ß '~0Ìo)O 2¥£¼uX’=òæ.1óÜÛÓÒ!™è£Qh•=UÀ•aUþ'N|øÁBií;שÞN\[ŸÃß^\¥Ï;(ޏSÄ*ù\¯^½v‰‚o%·8¹FQ%I¥:?øà'çiwár¦ŠðUÂ}âÖ‹ÃEÔpxùü¸j ·CÀawÊ~p)àMƒ]6‰. ìÆ? Žþ ×iù‘f”?Á‡4Ãä¬aÆ‘ë4oâ2"I.s º—úz—~ê_èWøÁ¡w©{¡á¾$îqĹÎ;ï8p`+ùíöÚk/D•$•ªzùä“OÜ믿îä.ø]rEõÄZŸŽ)zVë_êá þå7\нøàM<ý;gΜµ2g»I?%yÞìwú駯k4“%æ…€¡Î ®D3㡃E¯>ôƒóS28*.l.t<ž·D܉⎖{R;ŠoiÓ€¼_Ñÿýï/Å@®Õ[â0|ML#²£CÊYû)InY¤?,ÈC$lð\#HÓ´?˜VÅEG@cŽR:¬ý¬1§åÉÄ”pök68Ãðë5É5«ù?%ÏÛâ¾&îËòÃãý~§AŒR€ÀjéÃ+âðÒ†lº²„œé({rÄß4u[hìG‰¡°4+Ò êfÁkáàBåâ .n†¡Üµ2§Ÿ qœà qO‹;P\WqÅa>p'‹’N¼õ(Ò‚­W¢¸]}ûö=ZvØ[/\¸p‰œ…i"Êý#ÿTü¸o•8œ™†2‡Œ´£ì SýÐf=¬—\²ym¤f—*6Œ1`á;؈ãÜõÒ‹Ý'«¿õ0È«^ÆÐwö_û±µÓk•~¬elfp—{¾øÿGvÝ‹m¦Sïr®JR=Ê_/SLbSsy7ÃþiN½‰füh8<¹Ó NÙQŽ|¦¢|PKT†X"ú7 ó2 zÌ "Òü°ÔÍÇ0Ψȱ`¡|u˜ ‹r¥ 2×  åA«Å} Ž_c¢,ó†)vÆI¶ôœë%uÇ}öÙçõuëÖmJ¸gZy³+ˆƒÜ¨ŒÁµŒ´ÏfL#6¥Î¹ YŒˆ@ˆÑFÙ0K(úP -ˆ=zôQRßS¦LÁeÙQÆå6FÌmÄr­q=sM‡é^èSêc¬qè]êP|ÝŽßHhÝ‹¼4¬Å›ÑÓðƒ¨·ëBö? ÊG§iYiyÑ`‡|°¡Îx­£áGYò` K–äHÖc$õÕ¾}ûHº0¹‘”^ËX¼F¥‡o׊Š!¨Ð9b–Åáiø‘:•ꕸìJoíСCǪªª=Ä ÆÎCZˆJ øRnúÃVØ4ª©ÐƒœåPüºNÔM‡x£f P(#qäÈ‘ûì±Ç8ƳUêÜ¿]²¢É#Àõ }ˆ5¦×ÃZ÷ÂýË ñÖ3ÌQFë^„Q&¨µ1/ÉFy ԉĘœ2¢AM #ÊjýËyb›u¡øÿGš/ö£ÄÂ Æ êÂcZðñpÏò‘ ›‹™Æ29”9:š6ŽYõ`. 9•ºDÕ+i±¢Pœ$¯éÆÑ ü¸»=ØeHiŪ±¦2‡Ì '(o8øÑw½Kxí¨ÌQžº=Iòæ¸QBÈkÆGHÓU²„_H¨ Öl3Ñ¿XsÐak:kéÔ»ðCoÒ‰×+«iê]pê^æ§Î%Gy£ÜP‚ƒ¨/õ³’zUo^À¯u0óhÎ:¨‹Ù–×ü †_tŽoâ4"·›£"€ÅkT:p‘’£çXÐX@XðC‰ÃÅxp(.2–e~r*t”§Ó ~INÉ5ÎÓ NÓ5ð{p8Èò¢’¦Q YmæeYÔƒ:(SpÃu!ûŸ$£ýƧ$Ù k» `½Ñ¨…_ë]øá°é°n¡K±žYNëäÃÖ4õ.8õuK’QD¨É!3:`¯õi˜þ¥QM}¬ó³r-Wø£ìûoõ»$›O‰ö³~oÊ#duiɑЋJ —‹šJ‚Æ/ÓæhYžŠó€ŽÊõÒ¡êH-‰Aý1:'Ç>°ëƒëåÒDxkY(j*tp*p­ÐO…Îrº.Êqð%ŒÀرc“ãÕÒuò›pw¬ùæ!€5Èu†Ú°ÖŽ5 ?u&ü ‹×óƒƒX×1Ö6õ/Ë“#?üFMCXÀ7üÄú”\ë^ê`ræAYÈaú)O‰Ê´•ÈÑ!Ì»œ$ßê—œy-C4Ì Ž†Ssa1è… ¥¦ØÏÆüT?üàzw„Fµ~ •:딢ɓ¼®Û3¨å‡¸6 ;Ô”À\@àˆæÚic ÊšŠ\s䣌Yžu² ÉRGøªš~ãñ# _½z»Ó"†?Lž<²3*/°æ Áé°.!kêNêIê^äCìJƒc­ký =«õ/ÊÓ‰×#ÖɰñúPïñ'§¡ ¹ÁAç’S#¬óRÞä”/¹dO†²mÐ3¨@Òì3¨› a<À8ò , h(Wø¡œ©hÆâgX¼ãå‘ò‡¢*uÖ ®ÓG555¸þȉA(lII5œr£þaJ œJòÑÊ~–e]¬Ÿm0,Y’@`Ô¨QUßAÛòªé;’胵Y8”þÅšÓD=Œ5 ¢®Ôú—ë‘ëy‘®u/ô/ê ÓˆC òºýÏ…qG>­+©w)pêXòÆô0òÓézÑ m'E‘æ‰lB%ÙǤ°)j»XÄF¥•4FE‚R>óBîPèØ%¡"'§B§"Çâ„?Œ"-ܰ‚…ŽƒÚ{ã“\×AêNÚ ÖŠŠ~pâO% yAšS¡#^+u„ƒŽõP¶lK²%‰€ÌÃáÒþžbˆ=#×彑d_¬í¢"€µG=޵Ì0æºçÚG:ô-ô­vеԽäº>Iö(,Ži-™u ñ&Z_ÂOY€kÝJLý‹0.¬aÉžíÞ{k¼{ï½w«Æ XjN° J .,ÞlT¶(ƒüàPTæPA¥N%%B„:ƒõz iø'¯ŽÅ]ÚNv«„%mPàM‡8Ê€Š\+hÈF+w*v*{„‘®Ëk?Úò;jôO÷˜šHëÖh1Àš‚Äš #¬Í qýƒ#ú–::aèXrÔxê[ê\êaI2Šˆå¤e@9h½©õ.õ/u/Â4¬™®Ë7èNÂú—s¦A¿t„üîX,b£A‹4ä|••FƒÅÅÂtr(k(8­È°vôÂàz‘j?Ò§?þØ3¨Û´iÓQ:“ô‰=q!ö‘ökÅL™Ðx†©ô?ºn‹uë8öÁxL\zé¥_–výe¹~´jÕªbjÖš)2!úW¯7ê_èF¬ON‡:ù¸†¦Óz~Ô£3”:ý›éY:ûì³Ï^ÂÓr50 Êa*ap†á§C•7ãÈ™œN¼™và7Jy˜yÇ=d.ÚîtøÇØ$Öt&Ö$t£æ̬E®Uê^æÕºWë]êYêdÔ¥ýEC@ëF”€Œ@ŒG˜ò?,Ìxræ×u NÓÈ“Â|ÉIï¿ÿ>Æ`T@Ì . ˜qV%‹—G?ô¢ J™~,.øÉ‘F¥M¿Kr&=èG8ÒbEƸiçÎëäÇ`xýøÞÒvήR€~†Á©|¦°GŽzàgrÄcJ l”òcÄ£eþ/bøDºp_Bݰfc@kM><¡%üÚ$‡.eë‘:Vû‘Ži k.É^Ä‘´ŸqÆÃêBÈ„„4†ÁfÂŒ£¤ãöd:0Nò_ÝÔÌ=UŽ:ê¨ FIv»,Ú6ƒº„Åè+u¬. ø¹èõŠB:ó!~8*tñfâ˜Æ8̓~„SCr Ð íÚµ;[~øŽt W¥€=åƒþ0LŽ8Ê qTÌàañ,Ç|(©€x£cÚ»*Oº0KÞŒ˜ôM3 "Ñ2šöõ/KÝ ?×%t+ã‡0Öh¶iIòò€k̰æðåF€:1Œ#ŽñãÀ'ÞŒLÏ|HKÓδ×ù‡ù““^{í5ŒÃ¨€´²çpÑL°*ÿÜ9”8(Œk¥ÍtƱ<Ê2þÔÓØnõêÕiØbMÒ Yû™'¨Ü™GóL]fLŠdù¸qã^’ô÷e9?½(ÙÞXëq" ú—:ÍRg6Æ™_çAYÆÃ †ëbí>Po² Ãà †_ëa¦ãŸFcÚ=õÔSè+æU£tê©§îaÏŽF!Ê;Ñv¨ó†,rì–; ¥¡•69óë<ˆ †™/5\Œi\ÈŸ¢Âö‡ ñô3o0LE޼Ú‘³ ÒŒF@n™-7|ìoÆt‚H yYŠú‡Š\ÃЗA‡ÞAÏr-Ó±ƒ Óµaqu¹íaú‘qšS¨ƒ~ÊPç£?•†´Àæ‰#N¯íPljvLmv«Ñª^`4š§ãƒaöVç ÖÅ<Æë#Q¼õ£3_12šJ[‡á§Rú6C()E °[^R‡jK=ÌQúód‹Óéæ¯Û¤â¦“Ãô¯ŽÓ~¯>|p Vœ¦0žýsæÌ‰ÒÇ]§œrJØœKÓpJ®/¶C]r"ËÝaX\’‹?Z .6­ä‘7–·pó€|@AÎ8Æ{™üfHk4Ìo¤]jcƒëœÆ5ô0ýàÔËfä_®e‚ÖèòLO(ÓLDJ=7ÜpçT£=ÄÃ¤Ñ –Ø$Ì nl¥QH-m\c!éE 768]®±|–VÆ”WÖ´RQâõ‡j!CÀ¾þõÖ·o\áÚ×z•†u0¯ÎL³pvˆq¶ÙÒ!¾liÙêJ<^nîˆ:OJnl‰ƒ¡fPG©²”ƒ·˜”’.®l‹2˜¯ Ij õ° È'©>Y»†€!PÔúάû,ú×toðT™‘ã•|U’\nîh5pàÀœ}—¹×ƒœ…,CNÌ Î QùfhD‰Øb+_±ÛÈ C dÑ¿¦{S ›RíÂíYpH6Ï‚ˆ lçµ ¢Ua†€!`†@’têÔ)’AåÃ\’]/‹¶Í . 1Ú ˆÀ˜1c.”û€^tÑEÝgÜ(øÊþ²Ë.;¤u[†€!`ä‹€¼%8’A-õÚu¾àFÈou,Ké ÷Cìœ~:t\:½¶ž–"òÁí‡Òïw„ŸQŠý·>†@y!°víÚHµ¡.ŽÜÍ .®VkBÈWYëÑ´ðýê‚5ÛcÇŽý1hŠëæïFwëÒ¥K[–øo‹.HÿŒ°°!/2¿Æ ŒÌ½)ù–µü†€!`O>ù¤UçÎsV-úË êœ(åŸÁ êü1³)@@ ™ur^útE”CäI¹¿FÎl .½ôÒž§É\ªùôÓOg„d±(CÀ0bG ]»vQ†fPA:‘ÎÛ¡]«Òho¾ùæÅ y'j%’ôÙ¶mÛž‰ZÆòaˆ1=Jâ¡;ïŸ5kÖÆ°<g†@܈nŠdPãawßZB{fP·)—áçÎ[+;Ó£Mò‚Üu×]8Wmd4 9§ßZžEßó OmR%VÈ0 " Õ –¦Í .þfPT«2V®\9]Œ›÷£¶öùçŸ?5¯å3Âó‰gʳ’öºü¸u^X‹3 C äZ¤jÑafPA@fPT«2{ì1Üèñ먭‰Amç§£‚eù²!0 òAîŽl,Þ0 $°ê$PßݦÔ»±0_ "°uëÖÉÒísu;Ùr½Ùâ\ù,ÝȆÏ„€ IDAT€¼;ÓgÊ\úLèÙòY¼!`I PYYɦf÷PA@‘À/B»V¥!Pp&ZvžoÍU™|Åõd®<–n4†€Ì³ïÉþ·°t‹3¢ 0zôèÓ$_OqoËÑ¡§¢”±<†€!`ĉ@TƒZúducu@µ*ãG`çÎ7‹Ñ\ãЙÈÎb+‰{yòäÉ2‘æ1òD@v§ùfÄ;‚s,Ϫ,»!`EA ¦¦&Ò5‹Eé@ ¯Ô ê>Êeø²k¸V ;`@Ç$ñvÜ#Š…## »ÓûIæ³ÄÕnß¾}Zä‚–Ñ0 hÓ¦Mƒç_–æÍ ÎLs¢Í nzV6mÜ$Ú²ƒh÷O§MR%Ôùõ4ùœV!óê‘iÓ¦}PB]·®†@ B ê‘gd B©xCmS¼ª­ærD l8Eã\5vìØ™ÒÇ‘ªO›ÞxãRÞoÕÝ:¯)¼$!WäýµmÛ¶·ÈÊy›Lb´† C Å# GÓl‡:ÁY`u‚à§­éfQrQ‡½cÇŽ‰bü\,xß¾ˆaúÔ3Ï<ƒ;7SÑ¿,ƒoðõ[.Y˜ÁÉ"DûWä}¿U[•†€!` ÚÚÚ=äù—³>y¾Ø=Ô9QÊ?ƒùȳ²)£MSÈÀ0?‚÷ð†¹`¾DÂbü,—kÍàXÄðÄí‰ô%vÃðD\°ß™Znðs¼Æ CÀ0Z&vä#Y¹Ûu²ø'Òz F™6ÌÂü:.[ߣäÉV¶ ñ[¶l¹©ªªê|TöÉ'ŸàŠ3§i¢;Òty™ŸØ2œIÓ2µÝëFд$CÀ0Ê;ò‘¬`Í NÿX[×F—ß0 4iˆÒaÆg‹ó«È0æÏD$å‘{©_3fÌ2ÞÞ÷ÜsÏ{Ò´ÎsÆaPé¯ä2†³Ÿ‘abΰWål†u¬g†@y" ¿÷hÕ®]»œƒ“g„÷¬È™Ñ2ä…@Z ¼a™G€–Ê4’i˜1^s ¦³*æc85|ãÆÿÕ±cÇoI‡JeŽSÁÑ8Æqk¦‘#å ‹zñ»Մи!`å€íP'+ßR16’E©„[1¦i¨ic˜qàŒr À|A? ”IÝwß}¯ 4h­t¦"ÊÞ ÄÈAÓ ãÚˆF:d)oF5`52 C üˆjPÛFKqæ‚ÔÅÁ5µŒé0cXÐA?ÂUâN×]\Wqœ/ÚpÖ~É’š?~z:“»'4‚™“†ò'ñ¾¸WŽá'ÂpÖÆ3ò" ™ëq2œ)PAÆÈ0 2F ê‚ω2†%¶¡Ñ@Š­Ak(²Ó0~µƒFCZó¥¸AârßÁ#™ŒbAàMie†¸©âjÄѦq­ mçM3ª±4räÈ}*++_¼æOš4é{ŠXCÀ0Gg¨#^›gu¤eu@MºÊÆ4 gÖðk×Y¿w¦8'Ÿvw 8ÐqÄ®[·n#Ú(Fä–·zõj'wi» 6&MOw©¸÷œ8~@‚ B~ÈUs3ª($¤‹$ß¡ز(ù-!`i@@^=½Ÿ“d³€Ï‹œy-CtÌ ŽŽU)æ„q¢Ñ…Å?,cíGø âþ.WÎíúÏÿüÏV—]vY«N:I”QÒìܹÓ=ùä“î‡?ü¡ûÇ?þq°ôwm_%îq”/8%8d¬¹ÕH.’sˆ£‘G:ÓråµtCÀ0Ò‚@Ô3ÔÒ_Û¡.‚ÐðÀ5*#»ÓÚÐÒ4üpø@cº‹8Ϙîß¿¿[ºti+nfL *)!|;0dÈ·páB÷ƒü½Â-ñ*ìÅAŽp”+8ä ùÂß(£G>^²-ÆôZqäÈnɆ€!`¤ySp$=/v‚ÔE¶E¨ÚªLÈVÔðsgšŒ0f·ˆûŒéyóæ¹=zHÐ(È„»é¦›Ü~ô#tòÃyj|ÙjÃòæ —(3¬B6’.cü´é“'OÞ‘-ŸÅ†€!6¢îPËfÔE´Fe‚@`wš£¢aYÓÑøÿš¸3pÌcöìÙ®C‡,g<Åüä'?q'žˆÍiïÛQÐÆ4ü”5×8çný`ÊùŒ1¢£x‡áaS[[{‡c†@)!Õ –1™A]ÁÚƒµ ¦ JÈ•ütˆ£1M ?ns83m;Ó@¢4;Õ?ÿùϽΊ’ ¼Få jÑ4ºÀqT૸ÍC~€Xþ(”ÙqKïÞ½ñã9ȱZ?$ј¦ÜÁaPÓ¸Æ.µØH€°Óïä 8Fcd†@I!`;ÔÉŠË êdñ/XëÊ@¢ñ¤)퇱EÃë8ñWˆaf·yLñVtÒI'±Á~â¡\É)wÒÚˆÖ~ÖÑb¹ü±¿¬!`ø¡\Søç „ Ü0Je4:kk4ƒ%6 ,åþxnµ»rq–ÌY¢'¾ð‘»F7šo³»÷ì#Ý8©»zâ nÑ58I;a-ÃÁX:mHg:&Êwùæy[4‰1ýÁ¢J°X0uêÔ¥- ¼!`”,QÏPÛ‘âˆ8Ò×ÅiÚj-êSiР¦Q 9‡íRÇ*ÿ6=ÏsKgñš_çFy£[Ù„•nBƘ–÷l?5³ž1]³ü~70`Lq•›0q¢›pÕï—y¬òÚ¡Gº §¿Ì k×+ã쩌¸›Íº{E-¹‘3B–4ªÁµ¼Q æÂF» ¯|]:ew”ù CÀ(-”-«ã¶C ¡&¤ÇjP5¡V$ S8Oô“ÃÈ¢£Ñ%QñQŸá¿t“†²½ÝÈ_½È€Ç_üí•îF?¦úªÙձšeîÊCÏw™MæÝâuÛÜÓ3nv×_s»þæÉnѶunöÄLnæÈþnòË›¥Æ*7êþmnÛ¶ÝnÇ®mn^&ï`÷Ô{ÛÜ•޼—÷S[ãõzšºdLƒZË~ÈœÆ4çx‹'ù1bµ<„Ž“›…îoñ€†€!P²È‘µHz]tžÔE2¶Fåƒ8ý4®hXiƒ:ùwrc§>åû˜Ï½öx7} ^çpÔãø+gû)cÜ=7žíYˆ~„[ö§Ÿ;n!V_uŸÛ4ã×·kæ q]¶Ê®îìkr‹§íÞ 7áW#©mÚT:œ9¦kã*Ý>{óÈIG·OU¥k£Ò‘ªôÌ-_ò¢›?¾ç–,_ãj½øTýƒ,Ñ]Íá‡ã| _¢Œ€€ú1âÌ{ï½w›¡b†@©"`;ÔÉJX£ò@€4F£ („iXàæît2òïz²›6û:ôË£‘Õ·¸5µkÜo¿ÍsÓÎMZøK×§ž­¼ÆÝwóL¿ÄP7eÂy²çœú^ò3773ƒfOtO®iš ¼yÙÃîÂVíÝ¡ÕÇ{o&ÄÛ «íá*ŽëžX^÷A ®‘ÄÿS¶/ý˜ðs>°“Á0ã[ƒz-Ç=¦¶¸ÁÛ€ C ¬ˆjPÛêâˆ=ƒª8c±Z"ùjƒŠÆâ`TðJ„zž=Þí>N=Þõ¨èá®õÏr ž0Ï µX¿ÌÍõÓ«¯»Âט5í¨«qól‹ÝŠò7~7/™î:94óãÇê¡C3;ënñ7äÐËÝl}§ƒ(S~Xâ7ˆ§k`HGUÀébQzñ'1¦%?FÌœ$*J+V©!`EF ê¥v䣲ÀƒÖ¨< qL£‰²E˜xÐÐJhô•nø¯_p#´~›vý ±›WýÓÍõcÏ;µoƒô°ˆª®»¯lçÞË·ÞÝ>bd&éÖ§V¸E=äž–s× g]åÇÏt·ýey&OÂÊVËZÏúÁAäu¡úÿöÛo¿ròäÉ×¶ÐáÛ° C Œ3ÔÐÿQ¨Åßî¤|óD?ßz-rhC þ Åp¢;Ô>sœä…¥rWOŽl²Öó=a9ㆀ!`”(¶C¬àÌ NÿB·NyÒ¸Ò é /tò¨o¥»ûþ±®Ø•“qa‡3vlýp÷í[ضéýL™ŒXª.Û{/>ëçêþmx¿@ÙN—º… º{FHK,H™S¾k‡xæ72 CÀ(#ò8ÂgG>Š w1¾Q5æ…¬yFžr×e62 CÀ(CìÚ¼d…ju²ø«uNšÃ— ƒzó˓݉×úÖtõ·ô­ÝWéÝrþh÷ôúúÐTò%ÇÃ!7Þó¼w ^ýÁP­›ÿ§ßg"»ïÝ>ãäi×ÁÏv˜Û7Ï¢‘ê/|&-WíGK”=çâlí#CÀ0ʨ;ÔvËGqnÕâàšD­4–ÈÙmL1MÇ1_|¼f‰ûAÿq™ö¦Í¼Êõé}²û]æÚ¹î”oOv39ÄSuëÏkð¦üT®ÁÓ‰!þõóÜõ·ø7 ¾ÕÒ;Ú¹ëLMÛ×úÞµnCÈíÄKžì~úÓŸºéO¯ÌI'L®Zæ)è¢uÁ0 C D5¨¥m;òQ˜A]PSR%d« ,mX!é1w·Æ=|ÝˆÌ ZO|Á]Ò·î<¼EñÖÁ~wæŽsWL_¢úÖݚȻ«»¡C~èü÷Á¨<¾·v¹ûé·OÉœŸ¾îšó\¾ï;svæfŽ™#G¸ûWî~!K÷3þÍMà.õâ]uÇ“Ýä'–¸5ÌSãV¾,/b©8t÷Ùì¡“ÜÕg¨×—«ÖóVUŸêx9ÞC¤Kü3(5kÜÝß¿<óàœSo¬š$Ò(cÔèè O¢‰¶9vìØ‹Ç÷´ð}íˆ5n†@°êšgufPç X‰dÊ•†ºOC‹†W|CZ?ß]>d|¦½YK¯npE^›îg»™Óx;õbwþÙÿåvïww×/X쮣Q-7SRí:·¯p¢Hĵw‡ôßý"7x‚{ëOc#ïNïÈôLÙí—ºöîÚu–[¹l…Û„-ìŠnÿ=]×*Ý@¥;oò"·+{5Ùª/t|˜ŒÃâ Ýn©Ô7ÃzJ©tØúi†@TD·EÕ÷‘”(U“ö'Š;Dõã/â.› î°•ü_‘´;Äãç_$ü’wŽ®Çšß_Š%nq‹Ã•^㥠Þw+ÁøI[ñ·n-ÍA ²“ëÓo¸æT¥l•ëÙ'ÚëΣÔfyâGà²Ë.;DZ=MÜ¶ššš™ñ÷ÀZ4 C ¸ˆqÉ Ã3’A-Õ½#=>$¤×ß”¸M’^-Uen0¶•Æòðþ›¤ýEòž¥Óš7_£ú?ा¥÷tqúƒÇâlÛÚ2 C >ÿüóKEÙâasÿ´iÓ°£ad†@Y!PÈW‹º¼DÀ9Ä:³—¸nâô7|‹}½Šoþð }mLãäâ°C ú¦äÉÔù毫"sy‚¿‡þü|ZMOÜ ê$P·M~%×­†€ìØé8ó—b(²·;Å“ 0Ò¸sÆ k-š‘ÀBðÒƒÇ0 ²A Àg¨oö1ÝYv~Wˆ[/n¬„=}ê§÷÷ù½>«”|KĽ!îKæ&ÆÃ*O^ùÅ?DÊÂ~"õâ úó; òbåŸäHÜÜ ê¸§=m<ÓƽIþþûïÇÓ+k¥àlÛ–yëL¶‹FZ¼‘ݹsg|=Ù]ï²É“'/(¸¬BCÀ0R€€“­¢tCt!íÐìR lÃ}üijBòÿA,ùq|˜g¦ÿä>Îg~É[•o~¿ì%¬CøO”ßI{Z¯_®Óâô›A'Úñ·4¦°ˆôBòÞJòÒK/Åß3k± ¼õÖ[¬‡çÊ(_r¦#œL+wîýQ9µÜjã3 –‹€ª‘ jA(ø|‚¦ÓÃŽQ´Ð×ò2/e¡ t¾ùQü¿ŽE!>’ðCIÐ u,þÿfPÇy-b1pA€kCŠaĽ)îÓ¥K—ºeË–‰×¨”رc‡{öÙgÙå¥âL)_p8ãëB»ã.[>zôèepgˆþLÜ]e;P˜!`´xäH[$›Nìnm4À «Œó=„¯hÁ9­Kï“ô¯©©qØÅù®ŸÿcßàÎçͳ=¥l¾ù£ì¼ó¥èZ”üþ ÇÌ .–iª ‹E/†ŸN¹k‰¯IvÌš5kל9¡×DJ²QÚøè£Ü 7ÜÀn=*-Sí×óùƒaÖQvÜWþßÃÀjkkí¸GÙIØd¨µ”Éû9 u“rØ„;Ño»Î]}±YÞý-v‡Âê7ƒ: •ÒŽƒ1EÂ$Dœ~mlÁÿ¡¸Çĵ’Ûv½öÚkâ5J3Û·owßýîwݪUؘv8Dý?â(WÊ›yè/ ¥„‚ä¸Ç×å!p°ÔõÖwÜ1·uZ†€!`¤bÔRg_qxvÜ£Æ[6:‰«UqQ½ë£fôó!¾;Ή<ëÌ ÎS²)Ï®'ü ÓO Ÿ6± `P/’ã­N8á„]?þ¸ÒˆÀÚµkÝi§æž|òIto³8ì¼BŽ4š)[-kIÎ|¨ÒsñeKrÜûU”þâ8 Ì0Z6¾6WÐ]"ˆ.V¨âí=EÞ âàÕ_oWÒÔñ3Î+¿´‡çÚ q =ëXƒÿŒÇõ~‰è{3¨Ȥ¬"0©h`90ÄÃ?]Ü+›6mjuæ™gºo}ë[ÎnÿDRB6lp7Ýt“;üðÃwÍŸ?½ÂáéߊûHe‰G”3dtåQ"ʆÇÁG޹¿< ¾)zu‡øà'Çpv=ÿüóÎß}€Â|UÜ}âðUäHS¾”7e/I™9@0È‘VVÔ®]»á2 è·§Nº¶¬gƒ1 C ¨µ¢û¹3ß^!Íe¢$}½z.þANÊ$Öy~ðäõŒé|óKùÿ'îDq¸Å£+Údî„x?|'ããæfPÇxñÚñ¥ ýà4°ÀaDÓGx‡¸Öâž·D&îÑï¼óNGqâM'‰Á磌«~P¹eË–¢ví 0ÀÍ;×½ýöÛEm+P9ä 9½%Ÿ¾qЀøaˆ\ÓÚOùK‘–A¢lW‰’Å=ë7¶ŒÛ( C ¥# ׿µjÝñÆIt#mƒÐŒ¾Ê´¿K¸‡x¤‚ñàíÄámˆxÁhþq'JþK$nºø±uµ0¼\ tMóþç›_ßoýú$mlcßÔ’ž¦'nÞJ:w›Ö^ ƒ „•„c<ø ޳Lðck™ ®Òw¸jþö>g:8Ê(®«¸ÎâX/ÚÓN‚õˆý©YŒÀˆ#ŽØk¯½Ú=òÈ#o½÷Þ{a_E¡Ù‚Lò£>ºÓÀ¥õ¹œa~gÅŠŸhLì8>ô£ \÷¶JÎLCqÑáñÛÄcìàpHÿL>(‘ÓðFýðËò÷Φ‰×È0 C ”¥sÅx ×Dï?xê©§~+[>©ãpI{=[z ?P¼AÊÀæÀFNV’|Û ßü¨TÊàXvªI¸id„Ÿ-m<¢Â±zm‡:V¸‹Þ˜6È0¹u†äNãŠ;Ó4ÆQ“´Zvú`œ#Ê2o˜QÍ8ÉV\ú¾°ŸÓÕ555[ĘžWÜÖœûç?ÿé8à€cåøËÑòÃÀƒEy=!»÷™¯òhr‘ÓCŽ»ËZF0ŠiCftÌCÙ’³ÖŹ E CÀ0ÊŒÁšclúÙ–õˆ°È,qøÆ»3Ÿ‹ÁÛQ¼ïŠÓF.’Wˆë)ßü('e~'m`§üW~=ºo'iL£?fPûR)3†ÅBã,Œk£† ll4¤ Ëb§~䃆r ä"1ªo“ÝàF:ž~p8ÁÚ˜†Ü pȎƳ6®§¹®‹õKñò#9Ë>VFu…\ŒŸ­^GJíŸÓcÐgÖ´¹sç#CÀ0Z²óõ[VÏ€´Ú ê´H¢@ý€±¤)ÔfX³U£Æ k,PÄýå庑·I4pàÀSåë¬J¹Ec¡©o7©’ Í›7o¾ôó 1¨ûI?ß‘&s)/à B>-3ÄÓiC™Æ3Òè§a­ó³.ݾöKñÒ¦±cÇî-óý|¦þïK{4Ö{CÀ0šŽ€zö7Z ”e£,±I˜AÝ$ØÒ]‹Åÿ J#—†:Î8iÚ †aÆhø‘étDÕ#¶A^/±©®]»ž‰²rÜãϹ*/jߟy晿K^8R˜ÓqðÓ¡ cÄѯy˜­ÓYŽu‚—Ýî´Ìó Äu¡Í™4iÒrŒÑÈ0 –ˆ@Ô3Ô²áã=Z"FųÔÅD7ùºƒ‹ cp¦Ã!ކ4óÁhC~ÐÚ/Ñ^8iE#¹*ï@ù1â—å³Â§Ï?ÿü£ÒPMÑ‹¯b-´JYPˆ£¬ÀµCȇyƒi,Ç:%kFæð— övV½\Æhã0 C '¶C¢¢f0ƒº¨ð&W¹¿K C‰~¿ô39Òhӯè‹á aÔSpcz»Œi‹Ð]rMÝÇo ™ )´?Ãà0ˆAŒ§ÁÌ4rÆ3?8Ë{õ`NÀS.$?F ç§¿$ÃÚðÑG=T.ã²q†€!ÐD¢>ËêYÐD¬ ^Ì ê‚Cšž a@ùŸX¹x°Øhpé…‡t„Á‘? jñfâ˜Æ8̓~„ B/¾øâÛâŽÊ8–‚Ô›‚J0=&†ÉÑEÊ qðƒÀÃâYŽùSñeEòµ¥·;-sürE!îä62 C Å" j~ч9Ç/yôó!g~Ë 3¨£áT²¹hH…,²0‹q0¤±*¹è¸#McZ¯XÆ•,F)é8±FwhýÌÎ<Ù8Ê‚0(׺˜2ø/oFÜKÞŒø] Eî$·ãe S‚!`4yÎëgsÖÊÊñ™u°1&˜A#ØI6…ä¯5,8X4Ђ]Cº^˜4¨É™_çA\0Ì|ÆrTáO"‹^ï¾ûî¹?þøj•D/d„é§Ì‚a-Cí‡ÈY†õ– ÷é½dˆó~ÿûß¿^6³†€!ÐD¢ÔR}Ù>š]AŠ™A]K£ßÀâ10Ãf¤Ó@F<üÚ`“ GÌ“-Ìxã>"‡Z9®pÄÁüЩBsæÌÙÀ¤H4žM%¨å ýÈ13Ë•#·#–£TmL†€!Ðd¢Þò!†wKxF4Ǧ4ƒº©È•p9ßà¢aIÐ@Ó†5òÃÁòÎÀêÕ«¿'Æô“¢Ô¾Ô»wïÙµµµ§É5{›U1*» GÄ1^i1†´7nÜ1‚ÝüÆí۷߯A0¿!`-àW(-dÓ%tìÅŒänd1Û°ºSŠ•O0¢i¨i?âÖWµáe"aiær` Ç<6ÊM%g öoÀ¨îӧσ§Ÿ~:>ÜSbÜá×øgŒi)ÿ¹r™xÉ_îä½Q9cÚ´iåp}b¹ËËÆg1 Ï“Hµt¥%=/b@¾® 3¨cƒ:Ý ù†5™g¨I±èà´A?ãƒ<˜Ï ±ó0yðÁ×ÊÎôé8ÎPêÕ«×=ýû÷Çz$¦ÄÎ +Ã9cDKÞG#Gެ”A_€ïرÃ~ŒØâf€ Ø0²!`u6d≷#ñà\’­ÀºÎÒñlñY²7-Z^+}œœ5>òöÛoŸÞ´R_j¥Œñ4éå|Q„ß0`Àô… ^ã9õ=O¨ƒbD·m×®η„|b/r)¾V³!`”&Q|ȳ&–o™KŦ÷Ú ê¦cg%‹‹À%~õÓ‹ÛL:jŸ2eʳғaâp¥Þÿ'7YüßtôÌza†€!P àÈG”~Ê3Æ ê(@å™Ç ê<³ìÅGàâ‹/î" Ï·SČⷘŽ&MšôˆŒy¤8(»_‹Q=2=³^†€!`¤Û¡NVBfP'‹¿µ‚@eeå¢ÚJÒ_åëý÷C²”m”Õ3eìWøŠqªÕç–í`m`†€!`…D Òµ4h;Ô…DÝ¯Ë ê"€jU6ù:꿆iÍ«©4KË­&·É&õŨn-#ø£¼ªüÔÒ‰õÚ0 C .ü˜œÍùß‚æÌgòCÀ êüð²ÜEFoÁ“&¾, ^.røèá"7—Úêe§z‚`p‹(ȶòÃnþH­¤¬c†€!`¤;C¬Ì Nk½!Þ¹a1&ïné79Èq—ÿ+ òÑÏÂÔrbd‡~€|Ðúv˱Ô0 ü°êü1+d {±K!Ñ´ºš…€\W! Á{ žTÔ"{hý¯å~£ãZšذam»téòw7yKâ_åUã·4 l¼†€!`DA@žŸ‘6I%Ÿ½<,  yæ‰~žuZvC IÈцoÊBßW /–ÝÙ—›T‰*+:uꄸt“‹Ì˜.+ÑÚ` C ÀD=ò!ÍÚ Œ=ª3ƒº Z•MFà¿äô&×`Ë ù5ƒzjY Ìc†@ ©H·|øß~¸u«Î j›©@`ôèÑûIGΔ…¾cûöí3SÑ)ëD¢Œ5ê 2N‘Nlgs"QiXㆀ!vÌ NVBfP'‹¿µî# ;‘#Ä‹3ýÝyçë C mÛ¶£üĽrèCÄ0 C ;Q|ÈóÖŽ|d‡±É)ö£Ä&Cg ‰€,ðKüúZü£àz饗vnÓ¦Í+’÷i¹^ðR¹eg”riË#·wœ)ýŸ+ýÇ.t†Üæˆ#ŽéƒiÇ=2ȘÇ0 pl‡:—¸bm‡:.¤­¬ˆQõI¼&/ºyŽñÆ CÀ0²"é µ”¶ê¬6=Á ê¦cg% ‡€gH‰ñôùjGáª-ßšpbçÎß̶Šá9ê²Ë.ûuc£=óÌ3Ûɵ„}Ë“P®JÜ[Üÿ÷1ª_”~^*þqèŒÏv§Œ5k¥…€èÍHµèU3¨‹ Z3¨‹ªU™²¶ï÷­wß}÷úüJ¶ìÜS¦Ly^ÎÌ+Ø}&Hü‡¢? CDîrÞëàƒ~Ttí­aé ÇU°}Ç.éã±rüFôé”Ï ;—0ݸ!`†@vDgF²éDÏÚ=ÔÙalrJ+@“ [ACÀH1¤Ï#ôOÒ“=d=_&¯-ŸÄ^ùg­‡¡Š¸ÚÚÚ£¦Nº”éIsÙ‘^/}ÃÝãYIÆôqS¶lÙ2kæÌ™›²f´CÀ0Z0sæÌ¹AôésA úô†SO=õ'¹òYz~Dú4“_•–Û0âD@ŽÜ'íyG$D™þ?yU÷wо\;×]~¸8Æ4âäìõeà)¢ÌuXŸDñc×úKòáwUUU8k}§¸ÂòZœ!`-Ñ•vä#Á `u‚à[Ó†@¡îá˜ÄµâÄöÜã.9Sý½ŠŠŠ¢_¿hã"ÙÑîˆK2ØèMCD ûIvØÚ6 C صyÉJÅ êdñ·Ö ‚! Fõ¯ÄØœ(Fg…ð;„÷ V.q{KÜð`|‚áFw¨Ù/ϦÝ!;ò?b¼qCÀ0 :D¿Ûu‚“Á êÁ·¦ " 0[êÜÞ˜b•ìË‹Ðn“ª”~6ºCJaLûãyøÃ?ô޶4©1+d†@y#É ¸AQÞhÄ<:3¨cÜš3Š…€å8]êþ›¸vÙÚðwz¿,yË–'®x1’¡rê ÓÒïçĘþN©¾À&.L­CÀh¹ø9ðŸ9óY†üÈù0˯:ËmDC·O\pÁ¢å¶\¹ê'Êôq{6–— Wxâ»ÔC† ‰zÜãÕÍ›7Ÿ|›bcã´4CÀ0Zv†:Y‰›A,þ-²u¹¹½Ü>±¸cǎϵH kÖ¬Åê‡Õk†@9 €_¤G‡Þvu òÌ ü<ë´ì†@£téÒå\1’zH¦OÍh‰9cz `‰{§qu¤sq’TÙ¾}û‘9(b†=÷Ü3×ùéeH_—[¾WÄnXÕ†€!`” ‘ÎP‹ÝéYQ. Ä53¨ãBÚÚÉ F ¹é™Hó4 ¹ñbày´¾[ŒdOIJ8’²”ücaY7©á’¦Û¡Þ*/¡ù¦ŒoYš²* CÀ({DõGÒçQŸeXhuµêG@vTCj°,èùÖ o÷3j&0:e÷ÁwNçcX*/9­™Í7¹¸Ü“m‡ºVæÇùòFÇš\¹4 C å!É X"mº´<øš7b3¨›‡Ÿ•΋¥æÝŸÅ´#ñÃNr.š2eÊëòÖñŸ}öÙQbÎ’ª½sríFH—窷XéÒ¿z;Ôì§|0¸Tæ^—;E‡e3 C u@aFéum”¼–':fPGÇÊr6ß:‚A šVÇ쫱” @ë¹;ï¼óM1H/Ú¾}ûÑ¢8gJz¨aí+Õo^tÑEëˆ#¼sçÎÌ(Ñà ì?åƒÁŒ8Ú—6P.Y4(`†€!`¤ÙŒˆdPÛêâÌ êâàjµ† Ç=‰ÁÒ[’V‰Á÷TH–4ÚBut­%.ÌóeÂÓ§OKp¹uëÖ¾b¨60¬ý~´®¬¬Ò^¦žb¥ÕÔÔd jôE·HS¬öBê ÃqÁ±gRd'Å CÀˆè§(­b#J>Ë“xpq!0 ‰át—¬çwm±ØP€Ú€ƒA‡8fäé2ðg3[Ϙ1ãm1T/ýôÓO«÷ÌQ)ã‘ìVŒª®®ÆË`²ÖQŒ4١μ€FæÄŸdgú?‹ÑŽª3ˆY¶0ñÖr`Ê Þ#Ê4ꃌ匆€!P2º)GÝfPç¨)Éx8EG@Žì)FÇyhH ©éEo0E ÐèR]ÒÆ²6Ú´fÜâG|A‡sÈpÁøa¹Ëy¥Öc7lØÐOnи[Œë~Ÿöïׯ߹Qê(d™•h_Œé§æÌ™ƒWŠ7èsã¢`E†É);Ø™Xˆœ1<#CÀ0bA@t:ôTN]Õâ6´r‚R€ x€EG@î>_ÙKÜü;î¸ã­¢7˜‚``ºAãÑa~ƱœæôËšÈ|àVJ®ËÏ<óÌ›ºwï~uëÖ­¿-7nŒ–¸‡s—.\QþíĘ~åÙgŸ½èí·ßFÅIè#îÔ€Ãé:L#G_áA.õâ!wÓ½LöÏ0 b#òÌ mÒôS(,ÍŽLâÖìN[¥‡€,à‘²Ø[Ìîtˆb£¡ŒÆ1ãÀä6óýƒX_](âÿÇ{lµd½ò”SN¹¥gÏžßÿú׿~Ä_ÿúWϲXE³²ÉNùÇ+V¬øî믿þ™T„Ýã8I¼ô9 ê0޼ÌtÈ€aþÀ’á8Çem†@ËE ê³ÀtSæˆÔEÕª¬À¨Q£¾ ; ƒÄ¨ÞòñÇß[?µüBc:ÌÖtÐp•¸Äu×UשV–Ú/YšNO=åý>tÔ0¬éµä_ò™gžÉ¿PáK,ÃáJÇ÷Ž*î q ÎÚxF>„!³`= m'H12 X<{²¶iz)+4ÍJàƒºY•XaC 1Ú¶m{‰¤ãkðî½÷ÞOË[êi…C ãW;ÄÃ!Nó¾RÜ q™0Äo”,oJó3ÄMW#ކ4kmhë8o:ØÃK32 ¢# º&ð oÒ®Í Ç¥¹±fP7A+ß(XÝãÆ»™Ä;íÿoïlପ꽿‡w…Ä7T0L±S*ÍÊ.oÞ Ÿ›Z)™\ѼJ]³«O™o]3+µn*¤Š–˜/`/^QI4Ó¼h€’ÅRAa`çùÿÎìßaÍžó²Ï™³Ïœ³Ïo}Xó_o{íµ¾kŸ½g±ÎÚ9 WyfèNÓÎшÓ÷·ðOÌ[ÓÜò©O}Ê~øánŸ}öAÉre$ðÎ;ï¸×^{ÍaÝ–¦|ÀN}•ù¯š¿ÀüÍó 4âcL}›º,$ªŠœˆ@ÜpÊët?Ê‹¨¨ÔEaÓAQ ØÞÓ£¬ì û¯´&ž´WdG=´šËñ¦FÑ‘…°¿3Âðï7ÿK؆††–ÿøÿ¨;ï¼óêúõëgIrMÀv"q>ú¨»ì²ËÜ_þò—ƒ­=÷›Ÿfþ^ó_X k_LS\KT 9Ø à^”×IPçETTÜðåD N'¡rûß‘äqhvÚZa18¾ÈBL¿Ï|JL1½üòËunÓF¥Bþw`ܸqnÑ¢Eîâ‹/F«ðãÉ›Ìoã1¥Ç¸bü‡‹ô€k-ª¿" "P<Ðs(WEí~ó‘«°ò¢Ð u4N*U$›á»¿¾¾~/{©–4Ô‚ƒò5ÂY¾à¢ ¾ÑÒß1½páB׫W/‹ÊU"[s访þzgo•t×\s Æë©3¿)h/f¦›Ígš¡ÆÃ ×bAND 6iÕÅDuÞÊ­ îSr%&€Š•¸JU'µE 4+@AM KA K1ýI ߇e˜™8p`mA«ÒÞÚ¾ÙnäÈ‘©/@Ö…š¿Á<^C1M‹Òqƒ…O=Àì~«™Á(=Û± _ô¿š¯f»3vìØÛò•S~að°—( LbšiÓÔðøq›Ãši‰i¨‡™êï}ï{©ÆÚ©³-€% [xÿ˦‰8ö”Ø äŸž¶&$yùeì„sœ7|9xàóÏYi /üâð8ìæa?@Œç̪56ØeÈ!x(a‡›ç—$ŠjŽ;,pרé&ÒÏŽ‘‚Ø=)ÒýEÛæ„5raÜðåD@Š$à $Š'_Lùaˆ- ¯c,ÜÕ„™vó(’{gö™Ï|†M8ÚWZŽ;…´ÿóìCVD@JA êýEk}KA;Tr" ¥%à ) iZ,H-˜Æ>ÓrÕI`À¼À2åðVKÞG±>bcí¯æCV2ƒ '"PzÞOÎʵä#'ž¢3ù (º(""@ AÅ0„g+9{ »7ŽÀK[âvëV,u¯¾cÓáñ\S“sû}`˜;21Û¸z¹[ùö6·ç~pC2ÐèV¿ðŒ[ðôÝâÿ÷š[¿Í¥v2yß¡îØOç>ý±a®_<'±v¼Ž|ç(<»}®·YÐ…PÆØÂRX#Œñ§E:Âr" "PröcÃ:üÎ#ŸÓ’|„ŠË— .Ž›ŽŸg ‘F1 QÍÙiÚÔkÅãâ÷Ëî.Zb-(À]÷ìwÉ1XœËmqs&á¦ZÝï{Ö-¾«Xv»-+qç:ÎÍÞ”1tÝÜ—Ý%†fÌ+E+.m"î¡ðËaï éôYm©‹Í¡¬œˆ€”Œ€f¨K†²¨Š$¨‹Â¦ƒ²˜4iR_Û îAû¦üˆ½ñúlå’îݼ‚š¢S™f©óO!”P÷C¬¢uˆ³Ù¬ûÐ+~í>uè©mN;rÒ47rØþέ]á~}ãŒtÞ·&á–Î\äî: K‘w»RÔ±»¶XC˰ ÆvyH‡Åµ@oA9ˆ‡¾¬G©Y_è£P*¼L$ø…W«#j•@Ÿ>}&YßGÙû5ÆŸ%x_L3³Ôœ¡†-ƒkpgÿz»Û¾}·ojÙî^718÷H÷øëÛ]“—²çov:GÓ—»‹|1=é:·ä­ínpß¹ä÷¦»ÅÛßrsÓmpnöänú [vWZŠ:v×wcÌYj¼Æ8SL󺀕’Ð.%GZP…¸éˉ@) LFe6C=§”•Vx]I° S\QXQTC|•ísW_ß#õv?¬ù…¯w=Ü^{rt_·Wƒ¥y»Ë€v£[±ô9÷ÔSO¥üÒkRo.AN.·üWßs3‚çÝç6ßu‰6 ½Þ¸5§Ç7ᒇܒ™SÒUM½êa;c«+EéŠã`,9¦´{^ˆóºˆ¿E:ƒˆ@­ˆtŸÑã¹<Êö`§ùªµ’L™2åC63ýQû°njjjz¨’Úc[ü˜/ pJ +Xvº¢?w[–Ïs_®ëé~¬;þøãS~ø¡]×#Ïu¬ðf’ÛA]ãî»av:Ñ͸êGéÞ®¨% ;ëwvq†›{{t ^2XŠ:R5–ëÇãË0®„y=°-á8ÓeE@D pÉë$¨ó"*ª@E?Ø‹ê‘ê4ö#»É8¹‰ê_Μ9“ŽÖžN<1>W¾ ¢ØBf1#Ýô:£ý[–Îr}˜˜þ1áð‰ÝH6dÉ 7îÐóÝÒl#»n¹[¬×~é…î˜\j:Uç7醫‚Ú—¸Uo˜X/Eloy,ÇãÊÿ…ðÇÛ¿Ò-²ÏHÅ^éF* "PU¢ÞW$¨ãVÜìåD ÃFŽYoR¬Ÿ†›•ú[(Œ`áù™bqxŠ-Æ-©ÒÜ:wˤÉéFÝôø*·ø¡‡Ü¶îzÑÝÓ‚ôÙî¿~³"]Ælyõ%· H8eÌ0?+k¸aÀî­»ÛwRÔ‘õdñdpl9®°þµÀ0,mkLE@D Dìéþ‚·ô–蔪Æ#€›¿œt˜€½¤dœ};Þ×>ÐËn¹å–?w¸Âê«À¿‘!XŒWì uãŠùî[Á ó¤[¹ G F¡‡;úôËÓË3ž}y}ÆÑéºç©÷p#sŸ½Bë¦3á\Ͼû§yúùU®ud9U\ÉÙÆÚ¿xn¦Ñ2]VD@JA Ò½E3Ô¥@ݾ<ÜåD &£û Î*ÎKàl IDATEeUV¿˜âf†°S£ÛŠëâëÏ=´i¢»àô¶ÛØ9×Ï7ïe7Æ^äÒË^ä’É5m{;½^¦üLiÛ7¯Mó±èš¶=“Žg*Ÿ)-\G¦21¦qÌ9¾ˆûž×¬öžŽq TµÔ:-ùèÜ+@‚ºsù'âìçž{îÞöA>Ñ:Ó¼sçλÑ©Â;Eç‹,?ì‹.–­»m‹½Ê0å6glSà¡îhNZ»\?Ntnõ&äç]Díš·íÈx.$–¢Ž¬•—.ãË5ÓkßâLˆË‰€ˆ@¬lB«‹=‹óžÃÊèË}^J…À^N:DÀ>œg˜Çë@±#¾Ñ¡Êªû`ÞÉ|‹0<Å4,ó+«·Ý{íù€Û»gáMküÇ®¯½÷™ô6xÙkjvOýêötö{öt¥¨#]ayþ¸úaœcï7Êȉ€ˆ@ü{M®úµ†:"óts/œkC`2b»víšÕ&µ6"¼Ñ²×ˆûéá8ËV†ÝñfÐŽ7Ýúíí›´tÞtwõÕW»YO¬nŸ‰”†ƒÜnƒ7ãjÛ/s±t꺅î;7‹¶GÞäF±u×¥¨#}‚²2+Òàh[cú+" 1°‰­H÷­¡Žg$¨ãáZ3µÚÞÓ±Ïð‘Öá·7mÚôpÍt<{Gñ™òop°Èc~ö:)gðÇÆgžëæþ1¬†—»ÿš8Õ]~ùåîÎÿÉöŸ¸³¯»4¨c‰›8î2·4ÛÊæîê/ŒN¯—¾ô’Sl•6\)êšPëpœ­àuÀ¸¬ˆ€”š@¤ûŒu©±·Ö'Aך©•{O[‡ïž3gÎΚéxôŽâW±"ÚïFÃð1Ž›ã];n’ûõÒu­ÙkÜ=_??ýÄ“ÇæÖ&|Àg/pWq–zɵnxßQnú#KÝÆF¼´®Ñ­~Á^ÓõPwù‚Ö7ñV÷ÍÏDLR— Žteå pŒ)¨qf>ܶ|­Ò™D@j†€ eÞkrö¹K—.Zò‘“Pq™ÔÅqÓQFà´ÓNëfàÓÃÞŒ8 ¶Æ]øóäß܆§ðªTM~+ꇸËÞ¤,p§ßÇ9j”«ë9Ðqc ~'Ýí&Ý:—œù焸ï<½Ä]JQm;SO7ÜõïÙ/ü1ßÓ ±ûÅ1näUîï¿:7˜fcJQëŠÝr\y"Ž9-Òý0ËÉŠ€ˆ@© Dº×h†ºÔØ[ë €x΢ZI ÿþ'šHÚÛ>œ‹o»í¶ÙÉâ:…›Zø³éFWÜé ;ªk|¸¯ë…Ÿ’znÀ§/to-º;ýãÂ% 8ìܤ«îsoÍ<=Ø»£«{ß¾­ª¹¡{¨’†aî?opßÊùnïéàp7íÖß» O|Çaét;WŠ:ÚU[B®±Í•[ƒT±ˆ@í°çq¤ûu<׆¶Í‹‡k­Ôšz3¢}8gÕJ‡‹èg¤\õ}ÈÐÓ§»óÙÜ€£OwµœäV/_å6c »k/·ßÀAn@ƒ»èáN™¾Øµd­¦Ÿuî ®å+—»åË^r«^Ûè\·nö¯·0hˆûÀ \¿ºŒ)E+.ub¦1ΔVêóª>H°gq$M­7%¦‘•4÷‘VÒ³©²¤Ø`à466ÖêÞÓIO¯? nÐÐh¯÷jìÑÏ =úÓæÛgEN)E‘O¦‚" "PµÂÿ3š±#ï™Ë˜¡Ä î¾Ú>øÖ[oýjmPïE@D@D 2Dšž¶¦jÉG<ãéÛL<§V­"hü5­ßÙÔn(6³ï§)\E¼ ž]ÖlÍöTÑØ©©"TXò¥oZò…Ráe$¨ g¦#D _<3œÉnBk×®ÍVÒ+œÀöíé7ßdÞìD"»ÂGPÍDˆ$¨5CÏØKPÇÃUµÖ6ðŒ%D5…5ȤތòüóÏ×6¥*îýßÿþw¶~sàøÒ2ñðõÀÿùÏ;íþaD*Ä­_¿Þ]ýõî°Ãkyê©§Ð*,žþ‰ù æ9–ørÄñ¥xö­e§ÒäD@D .‘–|hÛ¼xðëM‰ñpM\­S¦L9Îf¦³Ž­Ù´iSjš.q,¾CO¾¨BéL£ø‚…£Ç>ÆxuûócxàîæÝ!‡Òòñ¼nÈ!®[·n–ÕêìFèvîÜéºvíêì ˜\±í­F÷Æo8[†ÓòÌ3ϸ]»vá!ÿÿÌßg~yŒ#=ƘãËñæØ[Vú Zäɉ€ˆ@Ih—’`,º ê¢ÑÕÖ&¦'=¾sΜ9r­ Ž ¶|‘Ä0,,D4E,âMæ÷0ÿóKÍoþÃ+W®ìkÞ‚­Îø»¡C‡º£Ž:ÊõéÓǽôÒKîé§Ÿf¶l<0®§¿›ÿ“y,tÇ ~¢E޳æø[¶œˆ€ÄN÷¬¼N3ÔyU@‚º(lµuý±—͆ž†^Ûìè¬Úê}ÞÞB4ùŠB*FœBšbŒÂË 1ÿ„ùͰ™è½<òÈý?ô¡íß³gÏ®–æì¶¿òÊ+ÿ°`øÝå‘n¦¨£L.̦L§|¶–cF»ÕÒ°%~ˆ5ÓÒ#ßsì0¦¾æ¸³^Z+–ÞN4 (%»¯D𤶠Ü“äJL@‚ºÄ@“X}BÿÅúÕ×>¬ºýöÛÿšÄ>v°OLRX‡áÇ)¶ ¾ðy£ãÌ4f§q <1¬ëѣǛÇ{ì`[ò1ÌDu/¤Ù:ëu63ýÂ’%KV[e}Žã¹¶ø¡E.Â7xŠbŒvZ:¼/¤fƒ0-ëa]¼¬ˆœˆ€ÄJÀ&d=‘¾ÐgEÓ¡ ê᫃MPOz:«6z\t/}qF‘æ[/Šjˆ2†!ØRBº¡¡¡‡ éO4èÓõõõÒkLD?kbzuP®ø¶4¹Â PTc|8nÁÆÌ'Šj ë°åqÓ~Ô%¾-NkÁ6³ÕˆËe'@‘‹º3xŒE5g¥‘†%ð¾˜Fºïqëñ¯ÿ|V$uNX9({$u±gRÞúl 'îOr%& A]b I«ÎÄôW íƒúÀìÙ³7'­%ê-ª…¸‚àÅ aˆa„!À(¬wî»ï¾ýŽ;î¸S 0Þnr=,K;–›~Ôf¤WY”Bšw̰·$¹ðë‹jŒ5ÂÕþ,5ÃÈó5å˜óz€…c¼5¦¿" "PZùÕ´OoJ,-tÖ&AM²í@HO:õ+Ȱà¬v”@J°S¸©QLS\Al¥E° 龟ùÌgÎÝk¯½&PHÛÿ¼hBz®ùUV?@Äç“ÞŸFÝðp‘n ­Eõ7DcçÏ,#Lï jˆg iÎTs¶š¢šÇ¡^Ž;¯ Ä–Xà™¥büïj”r*S êÂxÕTi{ÅøñÖá÷Ûgï?ÿùÏŸ˜1cFMõ¿ÈÎâFE…*(¬!¶(´»Œ=úœ¾}ûž†Û»ï¾ûŒÍFÿêÅ_\ee°é4>—(ëÏLST³ËN k„á"ÝP[‹Öä_Ž:‘ G±KALëÏ@CL`9n¸¾á)†a9óL!M‹±¡gÄy,ëbý<ãVTND@JN€ÏšœkÉGN}úŠl唞&ÀÄT6º8åa!Èðy„‡xƒõ—{PH# Žbu†ëMП¬À›…8¾†ˆ¦ÇØ LÑì‹k¤!Ž|ÿx?Œs¥œd$!+"HÏ݇b oUJPÇõêk51=9èĬªïLÌÀ͉3gŸ}öp[*ó-þ¡µkמ4oÞ¼5Þéq³£¸¢ £…P¦€ƒXÆgÖŸ™FœBšÖ¿úa+*"@öL&{ jÄöÅ1Ç„b:§ØF:ÂÒþ¹x?͊ʉ€ˆ@éx¢œ•Ú3J÷¢œ„ŠË” .Ž[â²懬“›6lØp_â;[‚ÚëÙG˜ˆþŽU5!¨n«½áŸ/,:.Î0#â å!ÌPe) )ª-)•áL4ºl‚:[:K¢ ?0Âqö™ãƒ|ŽÃ ¤ÁÂûâÙOc<Ö´Ô¹ñÅËÂr" " {q¢%gý»víÂ}I®Ä$¨K 4)ÕÙ³ÿŸì‡ ÍsæÌÙš”>ÅÑ)S¦gÛÞ}ÇndŸEýÆm›ùé?ºÿþûß²$Üà(®PÄw~D3nrHÃç"iÎ~Ø’Så`kQ,£ß¥p|¨`|ü1B:=Ä1Ãþ 4ÃÖ,ã‹i_@ûáR´]uˆ€ˆ@˜@¤ç=£t? “+A\‚º“X…­›^šÄ~•ªO&¤?cÿmv¹ùQ¨ÓîO[ÌܼsçÎÍœ9óm SChÁ…ot¸¡ù‚BŒ3Ñ£<òéYŸo-»ã9hÛdÖh$ÓƒÃOC˜ˆ(Œ‘ưo3 h?ŸÇ±NXýäD@b%`Ï!›ÛÉû×¶yñ ƒu<\UkB Ø‹nÆZ× ¤?….Ú ì»ý¤¹¹ùÆÛn»mC–»„1lJ`–â‹Bšå ÚPžâÙ[r*yr…ðÇG"N1€ãXÁúå0>°~º_žuÁÂѶÆôWD@â!_MÛyµm^<ð%¨ãáZѵÚìêa¶òêßýîwxã›\¶Fús&–±´ã7!½ÁÌ fzë­·¾Ã*,Î)_D1 ñË0n|Ó"â˜a?nÙéüpñH7R”KP` 8&°ÆpL§hf-ÓY–ÇЦêÁ5€œˆ€ÄI Ë„N»SêžÔII$¨K‚±º*±5¿|ðÁ_4‘x^)noA\T]=(Okqs²é“ílß1fGgźèÙ5oζ¾7«àÆF!±KÁå _äSP#ax j ¦Ó˜Ç4߆ÈËå&à‹^”dœi3¤! ›)DZÊâR@ºœˆ€”ƒžyf¨ó"*ª€uQتþ .&øú›¿À„â&±^zÖ¶mÛfßyçŒrFÀ¾pÌ730L­5^×ÛÈ–—O߆´\ŽBÊŽ Ë$°˜!(Ê8CM1íWÆ´pýŠF€¬qEq8Ì2°,“ÍâX8\×Öý Ø-Ç9þc"óÉl*Ý›2£éPªu‡ðUÿÁxèÛp˜õäG½{÷¾ÎÄõo-i–ÅkÂ/¬¨e··±Xeþ‡¯¾úêmÅ,‘±cÁ ñ‡71 ´0[äûwC jZ–÷Ë -g9ÙÌ8á\¤3álq ý0†œÇ„ëW\D@â$éY êx†@‚:®]k«ÆkýÜù_g-}‹O„·¬3q}·íW9Ó–„,©èÅÔ8[ý‘RT,N<…,E3òySD:Â,gÁ´c&„ãL—ÝM€Üw§´†Âéˆûi ûãà‡Q †™åZkÕ_(/>GržÕ~T¾å,¯Ìh$¨£qª‰R¾¸¶°ø4Û^gÚyç÷¢i…™[·n½cöìÙ›kF …uø á²/’ÃñðñŠwœqØ¢f¤1Ý?†5Sº_Fa(ÿ™‘õ|š¡ÎЦCÔÂW‡„s”Này¶îêçQ Wz™ÓN;­ç^{íõ%kçë3fÌøïr·×`¾¸†(óo†áx®fúÇå*§¼¶r á¬y6~á/?mkULD@:‡@¤gu<ƒ#A×DÕjâoæ'Ø K+¡c6c>Ð^ ÒçöÛoÿk!í9óÌ3{÷êÕë¼þýû_l_*öµ>­´ãß_H¥.kmð…[*ì}áñópêl7Ëp¹R7³–êkÃ24>µÄA}¨2Þ³#gË%¨sâ):S‚ºhtU}`6a–©SëLTŒ³(®Ï”Yî´ý×н{÷ù6[ŽuÝ_ŒrþI“&õµ\^`búëvÃÙ;8æyë×7¢_î29D\±Wîvé|" " •KÀž‘4u·nÝô,‰a%¨c€š *·Û7Ù“LL¯¨„>Ù6v{š˜Æ#ì®q¸Å¯°¶-ÏÖ¶3Î8£_ß¾}/jhh¸ÐÊô Ê=c7«í‡d;Né" " "P…"M–ÙfÔ1 ®u P+½ÊH_amg s§›`}®ú,×ø­µ…/X±-´»\fñ3Ãí3¡½·å}ÝÄô–×ù&¢ÿ`7‘«mÇ’'Âåj'ñÙ®WÇ4ÐÔ1­Öj±ÜJ3ÓLL?T ý?~|w{³ãƒÖ¬OzíÃ7ìÓM<_ÉôsÎ9g_Ûë£Ï³¼Þh»•ÔüÕVæiÄåD@D@D ¡"ÍPk u<£/A׊®Õ&4s»6R¬ZÆlIÄOÛè„„‘#GÖ~øá¿²öŽõÚç‚`ßìËìGŠ—[Ó.±-þÎ1Û3hæoì¦qM¥Ì°w:RD@D †Ø3oAÎÛc{Vj§¢¼” /ið«ÕÕFÀ«÷™˜þf%´¢ÙÄô,3'{íK7 i9Ë<Öx_hÑæljj:ú–[nÁÚïŠX®bm“X à™åZC…Ráe4C]8³ª?"Ó‡i&Fÿ¸cÇŽ3ÍVÄl9ÇÍÖ¬3Оlm¶Á@»»™½·¹¹ùšÛn»í¥ª u@D@D@ ' A]8³’!A]2”Õ]‘‰ÒWLVÌ^ÓöÚ󘈞šML“6ò-ÜdeÿÝÄôëL—Z"`ÃLsOíØnY1iÖ®aUž %Wdk—ªÂãCêö[ãíE)›bêCdœh—‰éoÛß„XÆ¡¶¶‰c†ú’6Šˆ€ˆ€ˆ@mÈù¼$ ý(‘$JkkZPbÌ7Ä{bpÉä‘—Íg*_©iþ‡n›‰é‰wÜqÇê,}.E²1kÇzÊ”)Ù\EL[{Se-`›|œ³o$#" " 5Eb&J‡µ†: ¥ÂËÔÔ’ Ä¢ïü‹‘aÚlåüôªÛ·Ó.¶½¶”Ûe?à›4kÖ¬­á·åpYÿ«éì³Ï>Óö¾¡1ãÚ[æY´"~PY:‡ˆ€ˆ€x2é/»5hË;³>‡ÛVBdaAùÀj*Á´ÙŸqE<Ó2ͤ"/Sz¾4|Y©Ho‚55î&¦/þÅ/~ñh Û™ ò}&ˆ§ØŸuÖY§Øëħ·ŽTz¬,» w^êRÐ*," "  `Ïvꜜ½éÑ£‡uNBÅe&z†ÚÑÎ Ä Žiá8ʤgP–ùH‡óã~¸5·:þvÙ¹sç&¦gZsËy „?Ä©ø—¿üå±öŸem!÷‚)Ú?LÜ`ždý’š!`ÏÀHÏP{þkꮊrŠ©šŸ¹JOH£D¯ï‘Æ‹ŽéŒ‡-óq ât~˜iUa·lÙòð½÷Þ;ßÛãßFTŸrÊ)ÇõîÝ{¶µ¥kx›M,¿ay)ok¿Þ°ñ}Ö­¬±å!olß¾ý ›Ù^3sæÌ·q¼•Õ" H%‹€ˆ€$—f¨Íåí`Ïž=Û<‡ó ‘t† ŠÔ°b yW“/†3 e¤¡ ,ÃØ-â£æ1?мÏǯϲª×™˜FãÑÏÎt-{î¹g/È£Ö¬Yó÷mÛ¶mµG¿aKQþfãøì¦M›–®Zµj›5~zf†a) 1>¸›t‘¨6r" " µFÏÁ¼Nk¨ó"*ª€/‹ª ’Ê ¦)‚}KñL! {¸ùiæÇ™ïk^® Þyç÷àƒf;„2Þtø ó˜G>%œÍî ÂfÒéKTƒ‚œˆ€ˆ@Mð4PÎ~KPçÄStfbµw!ùâ™aŠgĆínþ*ó“ͧXuÔQnذaî ƒrö¾{K®>g,ª¯ÑÖb›Yvëׯw+V¬pO>ùd-çø„%ÃO5ùWÌCTSLÃbÑa¶Z¢Ú€È‰€ˆ€ÔHû_`ÂÚó{óg™Ú<Æ’³Õ¾À¶äT:¬D5(ȉ€ˆ€Ô¨k¨ûôé#Añ™$ÇÙJ.xŠihz|‰èa~¶ùc>øà–E‹Õýô§?•˜6 •âzõêål+=÷Ê+¯Ôzê©hÖ^æï1ÿAóCŒ-Ɣւéñæøsjäɉ€ˆ€ˆ@’ àÙ—×i†:/¢¢ @ŒTµóf§y!QLùbaˆ00Øÿ4ÿÑAƒµüùÏ®;òÈ#-*W‰ ¬ñ#Ê3Ï<Íëe~f`³‰êðuÀ8Ž—DðôPÎþIPçÄStfÕ j¯çNè/¨! ‡¥˜þ…'ÙVk-óæÍ«Ûw_½­ÚxT´ÃšðéÓ§»øÃhçóg˜‡ æ—$Œ-Æ–ãmÁVõ&Ãò²" " "PmlÉG$Mgï}ÀRI¹ˆ¿Äç,YužP‚ˆ¢£ ¢¸¦è¢»È vùÚ×¾V7|øp#[áìஹæšT+mÜÏ·Ç•bšã¶¸üë£Â{ªæ‰€ˆ€ˆ@Q"=ëìÅ.ZC]ÞÜUµ öº†‹ÈRÈB¢‹«·ùÑæ[.¾øb3rÕD`„ nÿý÷Çn Ø#ü0óÕ´œ¡–ˆ®¦U[E@D@:LÀ›dÌY—í %A“Pq™IÔþ72 ) i_LãM|Çšïõ±}¬nà@h2¹j"`7 7räH6ù `L}1ñ¦ÇµØvl®uë’œˆ€ˆ€d$à롌øî»ïJPg¥S|ÄHU:ï›. ˆ%Z ' +ˆ*ôþ@ó©}¦aåªÀ¦† ïgcŠ<Ö„qÌ-˜JÃ>Õr" " "xQ·Í³ßŽIPÇp5T­ ÎÀ‚š¢"šBš}Mýq¿ýöËpxù“×­pË^}'5ÍÚu¿º¡`7¿L®Ñ­^ºÌ½ÝÔÕí÷#Ü I¶LýÍž†µÔÃŽ!MOq ‹kV7 ýH0>÷rvqëÖ­z&æ$T\&gs‹;º2ŽÂÏÙI?Na ‹åx3¢³>`:Ý-ûå9nĈn¸ù#^êV4giÒ–ÅnòðVv¸›½lK–B%NnÜèV,_îV¬^ç²5«ÄgŒT–}ƒÈ%Öð¸8þákB{R9dðþç>g7nÜYP[§™_i/K£ØÂ ™Nbé5ÿ¯,Âc2•õÓ¬LóÍã\é½_a˪7ÿCó(‹öÀ"ŽI¶NuÕ.¨) ÑPÒȧ؂EzŸ®ÝýuÜ7º¿ù…Ìm³/,Ù£Lß¶,»ËzÄ@ IDATîÐÁ_p‹Ê¤á3w>k*ÇD0®ˆÃûcï(öã•ĈôŒ[»vm$Am"u¥‘¹×üà¡-¾Ùò‡ùéŸnñçÍû/÷@x¾å=ì—ÍeixÛ`óûau@Ä7™ÿwó( ‹ø»–O©„ô²;ªs- Û#Lqå ,Î`Vlç^4ÙÍ[]!óÁ]S“ùƪoj¸¡A8sL)¢ÇøûM÷¯ÄåD@D@D Q¢îCmλµÉ¬³¬Ü`óp›Ìb~ó3ÌÓ-¡3‹·¦0Ã,ö%>Üüâ íD+sRNKë¤ÏO'f,ñ²~da´çk^Úk^¸ìÁªÔ!JKÐûq_h!\áý]â&^t»Ûê`Þhó·zùR·t©ùå+ܺ-Dys£kllõ™êKå57»fóXãѸmGPl³Û´¥1•–é¸NLÃ8C@ÃûcËpøZèĦêÔ" " "/¦xîåuW\qE”ꂊ ¦û›X_e~ùs->Ù;Ɉ <ÇKëaå–šÅüG,uÀÍk5&ÔZÚacw:óÃÖŠ¶4x¸+­Þ‹Í£=7[üÓ©Tûcå>Åp¹m… ̼8xñТ?T 3N›·Ò²˜t“›yÕÄÖÓÎꮚ·:rV?5ÝêÚ× >b¸Ã‹j†q¨Û§oW÷å«ïqkÒººÑÝsJO׳g«¿ì‘¶õ¯žwYkž--é:õwF×:·Ï±mXàFìéêN˜î¶DnUY úã˱…ŵ|x9¨xöåu&Bs j¥xvîTtR†òwx'iå1±udö5+Ï9;•+‹eŸ³¼ƒ®ôÂx7ÅÓ^/~ëWÍ‚#|á /H£§¸B:g2+³¿[úºÏ_ú}7)¸nœx‘{.‚z]zÏ×Ýà㧺ÁqÃmf¾ûqöåg¸'üÄ­Iåõp§Ý47wí8¯þO¹É¯ j˜ä]ûO™Uhß HåŒ/…4ÆÕû×Ã)kfÆ+§'j‰ˆ€ˆ€T_p¿–¡YxÞú.µƒZ0×Ï‹¼´÷!ˆt<»»XÏå+‘žÃä-Ž ýMð‰pF¹â•)0£õ>,Œ{ i_|E«½œ¥6Û—¹ú¡îÇ_œu®;öòy¹w×Xóˆ›tÆ­å‡Os Wmv‹ŸxÂ-nzËÝ=mdkú‚‹Ü•¿^‘ ךà˛¬þ‹ñ?/Íîž O òëžý±;zÀ0w—}y}ëÙ›Zëp#Ýã¯ow-ëŠýZTTjƒ±Æ¸bŒÃb:õ! Nˆrp¼6Zcú+" "  !PÀd‘/–3ö‚Õž™»«2ºÓK»ÏŸôâk½0ƒ\òøÇ™˜:‹ý âo1=lƒ¾qüîp~¿?°ƒ `‘¥ªâ’«YP£Ç©, 0ÍïÒ º`+ÒauÆ€QÓÜÝ»§©ÝÍ/lÌÚÖ§nÿk]?Ñ-üà îÓƒ¹[?À~Ãý­‡Î˜û\z©ÆÐÓ¿ïn ÒÝŒ‰nÔÉ'¸3f·–~Õãî’c¤Ï×£W÷ Ü×íÕÞû9_Œ/Ä4,Æ•ÞçŠok¯œˆ€ˆ€”„€­‹Žô¼ƒˆzÂpYªØÚîI;þKA›¬Ì* ïÄ£˜AQ …ÊD雿m^”ò¡St<ê‹ÎŽ×Öy5<úC qEÏtKª<eèløô?ns­î¢×»ÕA¸­Ùè–-XК4ü87 y[³z[³~µ[·q»;ààˆ××{3Ýýܹ?ß]ÿ‚¹AîR÷À¥£Úž¢òco8œ¦C>½ŸÎ|YH~ðƒ|&æëOdAíWdBú4‹ãíÃÇé˜uÞ= $ÆdŠjsLmÉZmµ ÿ¢xBgÙ/¦!NŸFEd ånN/͸Ö]4}©sí6Ÿ®w ÜmqÉ·Üûìãè„ìöé?Ьq r¯øë±­þ™s§µéê­/_*ú6éÁØr†šMEÆ™³Ôä#,'" " ‰#°lÙ²HÏ8ƉS+?,8ûQÓa—~æÓ[0#‚]¡L¸H¤¾yÔG︫OFµï.Å3r( `‘Î8EÊT¼zú5îºëf¸oÙšŽ¹S/rwmÎÿO™8ÉÖqø¢Ùëáæ†qî 6‹Ÿ›ÝËÏr[ÈÖ‚Ë_ZåÜСÞQUôÇãËñfã™Ï¸¬ˆ€ˆ€$’€ÍPã˜×™λ5+1!}–…g2n?üû‚Õ±ÍKCð1/Ž—­…wùÀó˜î Dµh³µe••l¾·ùLŽéØÞO‚:¡ˆi¾xb˜"‹é‘.¸ˆçŒ±Øwáw»oz†c7üØvçÚèá—>îúÏQíòí;½qËvWßÐ¯Í ×=q­wí‚6åo<õ näªEn ªú~å§?ÆH÷ã´~žÂ" " "PõúôéõIlbfÚ øbúÓ©«²€ò𵿕 —;Ô;î/\HðVx°ù¯˜¿Â|Øq»¼?…3ʯ™G¶‹ÇOG˜qÚŒ•UZb!§»g¯™¥Y]]C0ë¼äÚ™î…Æp±F7Ëöîß¿¿ëëï½å9wÎè˃ÂÓÜܹÜÉÃ^(3áÇÁ{áº*6αõ¯a¦¡ÑþxûáŠí&" " ÅxóÍ7#=ç‚åQNѺï‰cÒu9Ä4¶Àó—qÜ‘¡ò1ÍÊúâ›ÉQìÏ‚BØÅ£ÍÚm‹÷³¼ÁAþ/[vã‹‘²Ÿ¼„'Ä…Dj†c,úÊtäU¼;æ?w\íÌý¥[ÝÃôuŠáÙnÄ9ÓÝjŠêæî‘œã&Ïm-9å«c‚ê-îžóuA²»êñËÜ„ ºÇ¯ÙZÐÖbŸ?ý… L6»·6dYO’¡t'$qlqjŽoR®ëNÀ©SŠ€ˆ€TÞ½{óù—¯éyg¨ÊzþdñæËà±¾š[‚Q4oigñ` ÓÂ'ñK˜^„¥|Á¡¯X½©]=Ìö³ø¯¾'¼pYƒI¸¨è 3ê…Æò•a뇸˃ý ùU‘ k8zŠ»oZ ³gOuƒ{Ö¹“¿|²;²k7î[³[‹¼Î}÷ô!©ðŠyצ·ÈsSîsßÕúoÔ¥7»ôîÔSG¸{–‡Å³-9Ü×Õ<+Û2m6©3¬?Îákyá´Îh£Î)" " ±°7!GÒ96CœWP[Cý`l‘÷šù¿fð&—š‡óÅòLº-ð–þƒT®ý±S_Ïp¡ÖŽÝaÇ|-8n/³ïZý˜íöÅô+yxPWÉLR/$Zûˆ¼Lù~Ù²†»6Û&öÍ~Ú~Çœïæ^:2] {jc D{¸SnxÎ=~+å°ý€qöÜ`ojçFN›éVýîwŠ6¯pÿå½ ñÙïŸbGÎ^(óýE·2æÎøÞÃ“Ý GœänåžØÈÅËg*ËeÏŠãÊB¦Öˆ€ˆ€$‘@·nÝ¢>û¢êà `´ e! 5³)ñ«,--;2ä#i½—ž±vŽ›­Œ/Ü!¬éðcɇé ‹u1qÞÓ¾•àÂÁÖhËõæñ«RØnæñß°<„¡ZÆ/@!ÿÏw¿û]g› [0®yã÷ò+¯¦®âÞ]÷tû2ØÐ/ßµ­ï×­sÍõõ®GÏ×Ј;×aÜ®¼òJ4âAóxcһ淙Ç÷€íæwaئ Ž­}°&ì{vÍWßEo —Lî»ï¾>ö»©ð1g*ºuôèÑÁ¯°2ew<Í$–aÔ„5ØQÚùÄV?´ßÁæÑÔ½ºžë¯Œ„\ÇÔ÷;À ;&5ݱŠ2Ýo€ÿ?? (ID@D@D S tïÞ½”3Ôꋉ[,Å(öLJyÏmõcYǪ¼Ë\€Ë!Ê|ZNJB@3Í%Á¨JD@D@ª™@¯^½"é¹@ŒVsW+¶í‘ b[Ÿ¹aY´~)ü—¿kjÂJ¹j$`76K8äD@D@D æ lݺ5Ò µ-—H?DkZ‰$YPgB•Z,ÿÆodÊSZhläþ€©õÑ™n L£­‚^©‰" " "P<®]»FÔv=‹ÇœóȤjn“â_( 36õ+Ò¥K—æ„¢ÌÊ%ðÚk¯±q[ƒÇ—éwĦeYH úúúH‚Úþ—WÏØF=)‚šxx¡ÐBl!Ìø_-¼ãùçŸoyýõ×yŒl•À}`Á‚lí+à¸"c0Ói‘&'" " ‰$UP[çõ\Œé H’ æL%.z`CyðØJ ‘×ýð‡?4#WMæÍ›çÖ®]ël ØÛÖn|#â8SL3N‹î!,'" " ‰%°}ûöH3ÔZCß%PÍ‚šB‰Ö§DAÅhÇ”ãŒd?K™lé~…E@D@D êì±Ç‘µ–|Ä7´Õ,¨}*K°ôȇà¢G:v†xÕüÓ¶ÓGÝ„ ZÞ|óM‹ÊU2,õ˜:uª{饗ÐL ØBóرcJQÍq¶¤ô5€|:?Ì4Y¨zQµuTϘF; ‚Ú¿8öã~Â÷š_¹zõêºüã-‹/Ž ¯ªí(mÛ¶¹/~ñ‹îŽ;î@UX²3Ý<߃NMËñ†¥· œˆ€ˆ€$—@sss$=gK>𼔋@¤ˆá¼¥ªÒ¿0  à(¤(²`‘†Ùi¾~‚l†ù•ÿøÇ?êFŒÑòoÿöonåÊ•–$W  ¤!¢?üð–9sæ Ix­8Ä4þ‡cŠjŒ'ÇÙ‚é±ç5À4X8¤Ë‰€ˆ€ˆ@¢D¡Ö’ø†½ÿ^mξaa­¾ Àâõé{án滚ïîù†ïm¾§ù^A¼Ù“Ík>õå⨣ŽrÆ st³}-¹ú\5Ž))¯_¿Þýïÿþ¯{òÉ'[LTsMöÊ»Çüjó؈z›yìwƒ8Âø’„lZ¼½ÂÂ÷“ê»è­ár" " "‰ÀüùóèÒ¥KÞíËìñ·f̘13Õ¡´Ž€­v¡1Ì™JX&Î^Â2LqE qöùçÍÿ“ù¡/¾øbw󔫘~Î<ÆcÅqÃx¶Éç˜#a^°r" " "X;vì¨ëÙó…¹vùÈͧ#¹Õ,¨!”03 çÏ8"bŠBšŒ– ³˜kÌ?`³ØƒÌ¿ÏüžæÁ³¤ð©ì l¦ãlj»ŒNHðytÂé#m {ŒfŸ7š‡˜Þ`³ÎþL3Ç c_ÖÇñ÷9¤Âš6jr" " ‰"uɇuÚ.&ŠAgw¦šu˜ÅÒ)ª} ña†>ÃB ÃS,C¸Á­4ÿšy ˲<Ê!œÉU’°ÎÔ¾r¦…?¸~œ¢—cþ’ƒ8Æ„‚cFï—Ec‹ºiöÏŽ[¶œˆ€ˆ€$ƒ@TA­I¥øÆ»Ú5…,…™o!° ¸ Žá†(C‚˜Ö‚mfKqP£.OQmIi1-ñ …9ŽŽgŽÂð0§¦¨ö-òéY-êã9`åD@D@D ÑlýtT=¢çbLWBµ j`¡xb˜B˜B –¢:Óˆã!¦qøPŒsvÚŸ™æñ´V\.ð˜cƱ‚oŒ ,Ä3ÂaQÍj¦³<-ëC Ãò|”dعsg]÷îØ‹!·ÓêÜ|:’[•‚ÿeìôÁ¾S0ÑBDÁAXACpÑBŒQ[0µä– Lp,=ËC8C`ûyrù plXqŠ^ `Œ=Ä3…3,=g°iÃbšãh‡¤…4ÎGÛÓ_HúúúHZÄäõQz]Y]¨JAí!Ä…AKqá ¡‡‹ iˆC„!Ù‚)‡z(êüÙiðAYxœÇòx æu([k.Ÿhe>Ç‹ìÉŸã O!Ííðg>Žãø2Œ:áåD@D@D ñ¢®¡6|'žI¹;X킼(̆€eœ¢  ‚Ž‚¸5Öú—å}A.дÕ¬‡6S}~Ý ·ð?ÀºäK!LK±LA/BÔ¾ ÏPûõ1Œs1œÚ€Úâr" " "(XòÑ­^ÑÛiÉGn>ÉM‚ öûOñ¡KA a!Œ<:””E9ÌPûbš3Ó°pŒ·ÆÚþõën›S»10;¤ÑCD# þ/„}ÑÌ™i¤! Ë|KAŽ:àxŽÖ˜þŠ€ˆ€ˆ@ DýQ¢vùˆoð«VP{ë¨!šà ¢ h)¢ ®Â×£<²¢épF"uˆÃÉcœ©† FE3ÄÅ5-òÂÞ’Ri°pÌoéo6惷ïÏtX? qß[4ý?™ŽA¾ÖN§(舀ˆ@Ò h†ºóG81‚Ú›¥U 8^ .„!ªá¸ìƒùœ•F^XP# åî;¤ÉO€cä iÔ†ñòÅ4Ó8ް~–¥Ey‰éý¨ÍÍÍÚ壓:1‚#ˆjŠe_Xû¢ÕP8#¢9ŸÅqrù @ôÒQ 3Ë´°¥XÎ+¤Q‰]<Q9H4¨K> ‚ÿN4“rw.Q‚ð¸>(¸¸ü ‡ÂÅÂÂÚOóÅÅ5ò)ª†C<œ–ÊПœ(ŽýB§L–"åy,-ÓX†Ÿu0MVD@D@M ê’=#ã» '¨‰ MQl_XçÅQËñ”²Å Î*’­Úpá1Üá4æÉŠ€ˆ€ˆ@¢ h†ºó‡7±‚h)²²kÒ§ÃL(gœiýj–—‡€?þ˜NÛ&cì'*," " µDà½÷ÞÓ>Ô<à‰Ôdë‹.×L†…Hk“dR¼ùË?2• ŠËtyûÕdJC>†3[ž¼Â" " "P¢ÎPëùßåP‚ÚǾ˜2lÏ$ž%â|ñ‡Ó¼Ãcÿ©u¨Q×P[ÒÏÖêé]u´´æuxX²ˆµv\ÔoáúÏN ûì(GD@D@D l›W_Ÿ_Ò™–i§oÚU¦„¢ä§_TµÉ;Hâ/ycª‰€ˆ€ˆ@Dô“–‰o´õ£»øØªfˆ€–|ÄŽ8ï $¨ó"R¨\Qg¨­ZòÓ0JPÇVÕŠ€ˆ€ˆ€ˆ@9ìÚµ+Óf íN­5Ôí”,A‚ºd(U‘ˆ€ˆ€ˆ€”Ÿ€-ùˆ¤çl µ¿pùšà3F€÷_]ª& êÎ> êε@D@D@D@Š& 5ÔE£+ÙÔ%C©ŠD@D@D@D ü¢ jm›ߨHPÇÇV5‹€ˆ€ˆ€ˆ@ì¢ jkˆvùˆi4$¨c«jE@D@D@D ´†º”sŸC‚:7劀ˆ€ˆ€ˆ@EÐ uçuçZ " " " Eˆ*¨µ†ºhÄy” Î‹HD@D@D@D r ˜PޤçLxkꘆ1ÒÄtnU+" " " "ÐAš¡î À.A]ˆªBD@D@D@:‹@TAmíÓ.1 R}LõªZ <úè£û˜@oooyϜނ°ï,þãȳòGF<ÍûæÏŸŽ7Ç*ZöØctÖô¦¦¦ Ÿýìgÿ±þš-&A]³C¯Ž‹€ˆ€ˆ€T5kÖl>ðÀo´víiÂ6Ý<ÍžÎ3-Ÿµc>jþ¿QίñlõÕ××Ó²%¨)‡Û=R9 )KD@D@D@D ~guV£eNügŠt†æ;wÎŽT²Æ IP×ø î‹€ˆ€ˆ€T[uqG…´è÷ãÇ£BÚRÑÍ ®èáQãD@D@D@jÀرcÿh}^ÑÙý¶uÖ3;» Õr~ êj)µSD@D@D f˜˜½³3;k³äëV®\ù›ÎlC5[‚ºšFKm¨ »ví‚ îÌmîfO™2¥©&`— “Ô%€¨*D@D@D@D ”l«ºU6K¼°”uX—–{L‚ºX**" " " e$Ð)?N4!ÿü˜1c––±ŸU* êªBu@D@D@D ‰¶mÛök·Û:¡oš.ºuÀT\D@D@D@ÊA`„ [ì<–ã\Þ9wìØñ+/®`Ô ©ˆˆ€ˆ€ˆ€tÛí£ÜË>úÜç>·±3úZÍç” ®æÑSÛE@D@D@MàOúÓãÖÁ×ËÕIÛ]äå:W’Î#A¤ÑT_D@D@D@Eà»ßýî{Ö¡²¼þÛÖk¿øD1,Gg$¨ËAYç" ˜Ð-ײ;_dKk÷0 êÚ{õ\D@D@D  Øv/c+»˜›ÚÒÔÔ4+æs$¶z êÄ­:&" " " wÆÙì Ç·"Îs$¹n ê$®ú&" " "uuu¿´ŽÄö*p«_{OwàJ‘ î<*" " " å 0zôè·í<¿‰é\[mw_ÇTwMT+A]ìNŠ€ˆ€ˆ€T;½±,û°åsN8á„w«Og¶_‚º3éëÜ" " " "‘ÀªU«~kE×G,¹˜ jí=™Væ‚Ô™¹(UD@D@D@*ŠÀ”)SšLüb-uÉœÕ÷·±cÇþ±dÖhEÔ5:ð궈€ˆ€ˆ@õ(õ«ÈõcÄÒ\Ô¥á¨ZD@D@D@D v¶Öy‘dY)Nd³Ó»¶oß~g)êªõ:$¨ký PÿE@D@D@ª@IÞœh³Óžx≯W[ç+±½Ô•8*j“ˆ€ˆ€ˆ€d!`Ë>fÛìò{Y²#'[=Ú{:2­Ü%¨sóQ®ˆ€ˆ€ˆ€TûákÐci” ò ÍÍÍó:R‡ŽÝM@‚z7 …D@D@D@D ZttÙÇ=öªñÕÒÙJo§u¥Ú'" " " !7n|Ð’¶„’ ‰jïéBhå)+A²E@D@D@D Òœzê©ÛmÙÆ}E¶kñ˜1c^,òX–€u(JJ'PìžÔú1béGV‚ºôLU£ˆ€ˆ€ˆ€ÄNàŸÿùŸŸ²Yê•…œÈÊïܱcÇÝ…£²ù HPçg¤" " " "PqL·Ø^Òwذ‡mïéõ£âyHPç¤l¨T;wî¼³À¶iïéE).A…’ʈ€ˆ€ˆ€ˆ@°­ïVØDõÓQšfåÖÚî D)«2… .Œ—J‹€ˆ€ˆ€ˆ@¥ˆ4Kå!¶;È®Jk|Ú#A„QTD@D@D@j™Àë|c>»víÒr|ŠÌ— .œJ `{J¿cíx(O[ž9á„–ç)£ì" HP N‡‰€ˆ€ˆ€ˆ@Èù*rí=ïHIPÇËWµ‹€ˆ€ˆ€ˆ@ì6lجŸ“îÎIDAT0?:Ìt"KßÖÜÜŒe!r1 Ž ¬ªr~l˜í…-÷Ûn ›ËÕ–Z<u-Žºú," " "89^Eþ‹Äu¶Â:Tgÿ PaMRsD@D@D@D@Š!ðØc½`ÛãÅcMç­;vì³|„ƒÕ u PU¥ˆ€ˆ€ˆ€tÓÍm~œhñYÓñ„uüŒu( ûñá=v¢æàd-MMMmvYQƒ'‘ ®ÁAW—E@D@D@’IÀ~|¸Îf¤‡Þ™}|üøñ«“ÙÓÊê•ue‡Z#" " " %pgPÞŒØQ’— ŽJÅD@D@D@D lܸña›^iöÁjhoÚ¨]>’0Šêƒˆ€ˆ€ˆ€xæÏŸÿQÛÝã¼$c$ A#\U-" " " "|Zò‘ü1VE@D@D@D@b$ A#\U-" " " "|ÔÉcõPD@D@D@D FÔ1ÂUÕ" " " " É' Aü1VE@D@D@D@b$ A#\U-" " " "|ÔÉcõPD@D@D@D FÔ1ÂUÕ" " " " É' Aü1VE@D@D@D@b$ A#\U-" " " "|ÔÉcõPD@D@D@D FÔ1ÂUÕ" " " " É' Aü1VE@D@D@D@b$ A#\U-" " " "|ÔÉcõPD@D@D@D FÔ1ÂUÕ" " " " É' Aü1VE@D@D@D@b$ A#\U-" " " "|ÔÉcõPD@D@D@D FÔ1ÂUÕ" " " " É' Aü1VE@D@D@D@b$ A#\U-" " " "|ÔÉcõPD@D@D@D FÔ1ÂUÕ" " " " É' Aü1VE@D@D@D@b$ A#\U-" " " "|ÔÉcõPD@D@D@D FÔ1ÂUÕ" " " " É' Aü1VE@D@D@D@b$ðÿc;è˜îƒ€pIEND®B`‚doc/next-tutorial/next-tutorial.html000066400000000000000000005526311242365656200201760ustar00rootroot00000000000000 Tutorial for the Next Scripting Language

Abstract

This document provides a tutorial for the Next Scripting Language NX.

The Next Scripting Language (NX) is a highly flexible object oriented scripting language based on Tcl [Ousterhout 1990]. NX is a successor of XOTcl 1 [Neumann and Zdun 2000a] and was developed based on 10 years of experience with XOTcl in projects containing several hundred thousand lines of code. While XOTcl was the first language designed to provide language support for design patterns, the focus of the Next Scripting Framework and NX is on combining this with Language Oriented Programming. In many respects, NX was designed to ease the learning of the language for novices (by using a more mainstream terminology, higher orthogonality of the methods, less predefined methods), to improve maintainability (remove sources of common errors) and to encourage developers to write better structured programs (to provide interfaces) especially for large projects, where many developers are involved.

The Next Scripting Language is based on the Next Scripting Framework (NSF) which was developed based on the notion of language oriented programming. The Next Scripting Frameworks provides C-level support for defining and hosting multiple object systems in a single Tcl interpreter. The name of the Next Scripting Framework is derived from the universal method combinator "next", which was introduced in XOTcl. The combinator "next" serves as a single instrument for method combination with filters, per-object and transitive per-class mixin classes, object methods and multiple inheritance.

The definition of NX is fully scripted (e.g. defined in nx.tcl). The Next Scripting Framework is shipped with three language definitions, containing NX and XOTcl 2. Most of the existing XOTcl 1 programs can be used without modification in the Next Scripting Framework by using XOTcl 2. The Next Scripting Framework requires Tcl 8.5 or newer.

1. NX and its Roots

Object oriented extensions of Tcl have quite a long history. Two of the most prominent early Tcl based OO languages were incr Tcl (abbreviated as itcl) and Object Tcl (OTcl [Wetherall and Lindblad 1995]). While itcl provides a traditional C++/Java-like object system, OTcl was following the CLOS approach and supports a dynamic object system, allowing incremental class and object extensions and re-classing of objects.

Extended Object Tcl (abbreviated as XOTcl [Neumann and Zdun 2000a]) is a successor of OTcl and was the first language providing language support for design patterns. XOTcl extends OTcl by providing namespace support, adding assertions, dynamic object aggregations, slots and by introducing per-object and per-class filters and per-object and per-class mixins.

XOTcl was so far released in more than 30 versions. It is described in its detail in more than 20 papers and serves as a basis for other object systems like TclOO [Donal ???]. The scripting language NX and the Next Scripting Framework [Neumann and Sobernig 2009] extend the basic ideas of XOTcl by providing support for language-oriented programming. The the Next Scripting Framework supports multiple object systems concurrently. Effectively, every object system has different base classes for creating objects and classes. Therefore, these object systems can have different interfaces and can follow different naming conventions for built-in methods. Currently, the Next Scripting Framework is packaged with three object systems: NX, XOTcl 2.0, and TclCool (the language introduced by TIP#279).

Languages
Figure 1. Language History of the Next Scripting Language

The primary purpose of this document is to introduce NX to beginners. We expect some prior knowledge of programming languages, and some knowledge about Tcl. In the following sections we introduce NX by examples. In later sections we introduce the more advanced concepts of the language. Conceptually, most of the addressed concepts are very similar to XOTcl. Concerning the differences between NX and XOTcl, please refer to the Migration Guide for the Next Scripting Language.

2. Introductory Overview Example: Stack

A classical programming example is the implementation of a stack, which is most likely familiar to many readers from many introductory programming courses. A stack is a last-in first-out data structure which is manipulated via operations like push (add something to the stack) and pop remove an entry from the stack. These operations are called methods in the context of object oriented programming systems. Primary goals of object orientation are encapsulation and abstraction. Therefore, we define a common unit (a class) that defines and encapsulates the behavior of a stack and provides methods to a user of the data structure that abstract from the actual implementation.

2.1. Define a Class "Stack"

In our first example, we define a class named Stack with the methods push and pop. When an instance of the stack is created (e.g. a concrete stack s1) the stack will contain an instance variable named things, initialized with the an empty list.

Listing 2: Class Stack

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
nx::Class create Stack {

   #
   # Stack of Things
   #

   :variable things {}

   :public method push {thing} {
      set :things [linsert ${:things} 0 $thing]
      return $thing
   }

   :public method pop {} {
      set top [lindex ${:things} 0]
      set :things [lrange ${:things} 1 end]
      return $top
   }
}

Typically, classes are defined in NX via nx::Class create, followed by the name of the new class (here: Stack). The definition of the stack placed between curly braces and contains here just the method definitions. Methods of the class are defined via :method followed by the name of the method, an argument list and the body of the method, consisting of Tcl and NX statements.

When an instance of Stack is created, it will contain an instance variable named things. If several Stack instances are created, each of the instances will have their own (same-named but different) instance variable. The instance variable things is used in our example as a list for the internal representation of the stack. We define in a next step the methods to access and modify this list structure. A user of the stack using the provided methods does not have to have any knowledge about the name or the structure of the internal representation (the instance variable things).

The method push receives an argument thing which should be placed on the stack. Note that we do not have to specify the type of the element on the stack, so we can push strings as well as numbers or other kind of things. When an element is pushed, we add this element as the first element to the list things. We insert the element using the Tcl command linsert which receives the list as first element, the position where the element should be added as second and the new element as third argument. To access the value of the instance variable we use Tcl’s dollar operator followed by the name. The names of instance variables are preceded with a colon :. Since the name contains a non-plain character, Tcl requires us to put braces around the name. The command linsert and its arguments are placed between square brackets. This means that the function linsert is called and a new list is returned, where the new element is inserted at the first position (index 0) in the list things. The result of the linsert function is assigned again to the instance variable things, which is updated this way. Finally the method push returns the pushed thing using the return statement.

The method pop returns the most recently stacked element and removes it from the stack. Therefore, it takes the first element from the list (using the Tcl command lindex), assigns it to the method-scoped variable top, removes the element from the instance variable things (by using the Tcl command lrange) and returns the value popped element top.

This finishes our first implementation of the stack, more enhanced versions will follow. Note that the methods push and pop are defined as public; this means that these methods can be used from all other objects in the system. Therefore, these methods provide an interface to the stack implementation.

Listing 3: Using the Stack

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
#!/usr/bin/env tclsh
package require nx

nx::Class create Stack {

   #
   # Stack of Things
   #
   ....
}

Stack create s1
s1 push a
s1 push b
s1 push c
puts [s1 pop]
puts [s1 pop]
s1 destroy

Now we want to use the stack. The code snippet in Listing 3 shows how to use the class Stack in a script. Since NX is based on Tcl, the script will be called with the Tcl shell tclsh. In the Tcl shell we have to require package nx to use the Next Scripting Framework and NX. The next lines contain the definition of the stack as presented before. Of course, it is as well possible to make the definition of the stack an own package, such we could simple say package require stack, or to save the definition of a stack simply in a file and load it via source.

In line 12 we create an instance of the stack, namely the stack object s1. The object s1 is an instance of Stack and has therefore access to its methods. The methods like push or pop can be invoked via a command starting with the object name followed by the method name. In lines 13-15 we push on the stack the values a, then b, and c. In line 16 we output the result of the pop method using the Tcl command puts. We will see on standard output the value+c+ (the last stacked item). The output of the line 17 is the value b (the previously stacked item). Finally, in line 18 we destroy the object. This is not necessary here, but shows the life cycle of an object. In some respects, destroy is the counterpart of create from line 12.

object-class-appclass.png
Figure 4. Class and Object Diagram

Figure 4 shows the actual class and object structure of the first Stack example. Note that the common root class is nx::Object that contains methods for all objects. Since classes are as well objects in NX, nx::Class is a specialization of nx::Object. nx::Class provides methods for creating objects, such as the method create which is used to create objects (and classes as well).

2.2. Define an Object Named "stack"

The definition of the stack in Listing 2 follows the traditional object oriented approach, found in practically every object oriented programming language: Define a class with some methods, create instances from this class, and use the methods defined in the class in the instances of the class.

In our next example, we introduce generic objects and object specific methods. With NX, we can define generic objects, which are instances of the most generic class nx::Object (sometimes called common root class). nx::Object is predefined and contains a minimal set of methods applicable to all NX objects. In this example, we define a generic object named stack and provide methods for this object. The methods defined above were methods provided by a class for objects. Now we define object specific methods, which are methods applicable only to the object for which they are defined.

Listing 5: Object stack

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
nx::Object create stack {

   :object variable things {}

   :public object method push {thing} {
      set :things [linsert ${:things} 0 $thing]
      return $thing
   }

   :public object method pop {} {
      set top [lindex ${:things} 0]
      set :things [lrange ${:things} 1 end]
      return $top
   }
}

The example in Listing 5 defines the object stack in a very similar way as the class Stack. But the following points are different.

  • First, we use nx::Object instead of nx::Class to denote that we want to create a generic object, not a class.

  • We use :object variable to define the variable things just for this single instance (the object stack).

  • The definition for the methods push and pop are the same as before, but here we defined these with object method. Therefore, these two methods push and pop are object-specific.

In order to use the stack, we can use directly the object stack in the same way as we have used the object s1 in Listing 3 the class diagram for this the object stack.

object-stack.png
Figure 6. Object stack

A reader might wonder when to use a class Stack or rather an object stack. A big difference is certainly that one can define easily multiple instances of a class, while the object is actually a single, tailored entity. The concept of the object stack is similar to a module, providing a certain functionality via a common interface, without providing the functionality to create multiple instances. The reuse of methods provided by the class to objects is as well a difference. If the methods of the class are updated, all instances of the class will immediately get the modified behavior. However, this does not mean that the reuse for the methods of stack is not possible. NX allows for example to copy objects (similar to prototype based languages) or to reuse methods via e.g. aliases (more about this later).

Note that we use capitalized names for classes and lowercase names for instances. This is not required and a pure convention making it easier to understand scripts without much analysis.

2.3. Implementing Features using Mixin Classes

So far, the definition of the stack methods was pretty minimal. Suppose, we want to define "safe stacks" that protect e.g. against stack under-runs (a stack under-run happens, when more pop than push operations are issued on a stack). Safety checking can be implemented mostly independent from the implementation details of the stack (usage of internal data structures). There are as well different ways of checking the safety. Therefore we say that safety checking is orthogonal to the stack core implementation.

With NX we can define stack-safety as a separate class using methods with the same names as the implementations before, and "mix" this behavior into classes or objects. The implementation of Safety in stack under-runs and to issue error messages, when this happens.

Listing 7: Class Safety

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
nx::Class create Safety {

  #
  # Implement stack safety by defining an additional
  # instance variable named "count" that keeps track of
  # the number of stacked elements. The methods of
  # this class have the same names and argument lists
  # as the methods of Stack; these methods "shadow"
  # the methods of class Stack.
  #

  :variable count 0

  :public method push {thing} {
    incr :count
    next
  }

  :public method pop {} {
    if {${:count} == 0} then { error "Stack empty!" }
    incr :count -1
    next
  }
}

Note that all the methods of the class Safety end with next. This command is a primitive command of NX, which calls the same-named method with the same argument list as the current invocation.

Assume we save the definition of the class Stack in a file named Stack.tcl and the definition of the class Safety in a file named Safety.tcl in the current directory. When we load the classes Stack and Safety into the same script (see the terminal dialog in e.g. a certain stack s2 as a safe stack, while all other stacks (such as s1) might be still "unsafe". This can be achieved via the option -mixin at the object creation time (see line 9 in option -mixin mixes the class Safety into the new instance s2.

Listing 8: Using the Class Safety

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
% package require nx
2.0
% source Stack.tcl
::Stack
% source Safety.tcl
::Safety
% Stack create s1
::s1
% Stack create s2 -object-mixin Safety
::s2
% s2 push a
a
% s2 pop
a
% s2 pop
Stack empty!

% s1 info precedence
::Stack ::nx::Object

% s2 info precedence
::Safety ::Stack ::nx::Object

When the method push of s2 is called, first the method of the mixin class Safety will be invoked that increments the counter and continues with next to call the shadowed method, here the method push of the Stack implementation that actually pushes the item. The same happens, when s2 pop is invoked, first the method of Safety is called, then the method of the Stack. When the stack is empty (the value of count reaches 0), and pop is invoked, the mixin class Safety generates an error message (raises an exception), and does not invoke the method of the Stack.

The last two commands in Listing 8 use introspection to query for the objects s1 and s2 in which order the involved classes are processed. This order is called the precedence order and is obtained via info precedence. We see that the mixin class Safety is only in use for s2, and takes there precedence over Stack. The common root class nx::Object is for both s1 and s2 the base class.

per-object-mixin.png
Figure 9. Per-object Mixin

Note that in Listing 8, the class Safety is only mixed into a single object (here s2), therefore we refer to this case as a per-object mixin. Figure 9 shows the class diagram, where the class Safety is used as a per-object mixin for s2.

The mixin class Safety can be used as well in other ways, such as e.g. for defining classes of safe stacks:

Listing 10: Class SafeStack

  1
  2
  3
  4
  5
  6
  7
#
# Create a safe stack class by using Stack and mixin
# Safety
#
nx::Class create SafeStack -superclass Stack -mixin Safety

SafeStack create s3

The difference of a per-class mixin and an per-object mixin is that the per-class mixin is applicable to all instances of the class. Therefore, we call these mixins also sometimes instance mixins. In our example in Listing 10, Safety is mixed into the definition of SafeStack. Therefore, all instances of the class SafeStack (here the instance s3) will be using the safety definitions.

per-class-mixin.png
Figure 11. Per-class Mixin

Figure 11 shows the class diagram for this definition. Note that we could use Safety as well as a per-class mixin on Stack. In this case, all stacks would be safe stacks and we could not provide a selective feature selection (which might be perfectly fine).

2.4. Define Different Kinds of Stacks

The definition of Stack is generic and allows all kind of elements to be stacked. Suppose, we want to use the generic stack definition, but a certain stack (say, stack s4) should be a stack for integers only. This behavior can be achieved by the same means as introduced already in Listing 5, namely object-specific methods.

Listing 12: Object Integer Stack

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
Stack create s4 {

  #
  # Create a stack with a object-specific method
  # to check the type of entries
  #

  :public object method push {thing:integer} {
    next
  }
}

The program snippet in Listing 12 defines an instance s4 of the class Stack and provides an object specific method for push to implement an integer stack. The method pull is the same for the integer stack as for all other stacks, so it will be reused as usual from the class Stack. The object-specific method push of s4 has a value constraint in its argument list (thing:integer) that makes sure, that only integers can be stacked. In case the argument is not an integer, an exception will be raised. Of course, one could perform the value constraint checking as well in the body of the method proc by accepting an generic argument and by performing the test for the value in the body of the method. In the case, the passed value is an integer, the push method of Listing 12 calls next, and therefore calls the shadowed generic definition of push as provided by Stack.

Listing 13: Class IntegerStack

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
nx::Class create IntegerStack -superclass Stack {

  #
  # Create a Stack accepting only integers
  #

  :public method push {thing:integer} {
    next
  }
}

An alternative approach is shown in Listing 13, where the class IntegerStack is defined, using the same method definition as s4, this time on the class level.

2.5. Define Object Specific Methods on Classes

In our previous examples we defined methods provided by classes (applicable for their instances) and object-specific methods (methods defined on objects, which are only applicable for these objects). In this section, we introduce methods that are defined on the class objects. Such methods are sometimes called class methods or static methods.

In NX classes are objects, they are specialized objects with additional methods. Methods for classes are often used for managing the life-cycles of the instances of the classes (we will come to this point in later sections in more detail). Since classes are objects, we can use exactly the same notation as above to define class methods by using object method. The methods defined on the class object are in all respects identical with object specific methods shown in the examples above.

Listing 14: Class Stack2

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
nx::Class create Stack2 {

   :public object method available_stacks {} {
      return [llength [:info instances]]
   }

   :variable things {}

   :public method push {thing} {
      set :things [linsert ${:things} 0 $thing]
      return $thing
   }

   :public method pop {} {
      set top [lindex ${:things} 0]
      set :things [lrange ${:things} 1 end]
      return $top
   }
}

Stack2 create s1
Stack2 create s2

puts [Stack2 available_stacks]

The class Stack2 in Listing 14 consists of the earlier definition of the class Stack and is extended by the class-specific method available_stacks, which returns the current number of instances of the stack. The final command puts (line 26) prints 2 to the console.

stack2.png
Figure 15. Stack2

The class diagram in Figure 15 shows the diagrammatic representation of the class object-specific method available_stacks. Since every class is a specialization of the common root class nx::Object, the common root class is often omitted from the class diagrams, so it was omitted here as well in the diagram.

3. Basic Language Features of NX

3.1. Variables and Properties

In general, NX does not need variable declarations. It allows to create or modify variables on the fly by using for example the Tcl commands set and unset. Depending on the variable name (or more precisely, depending on the variable name’s prefix consisting of colons ":") a variable is either local to a method, or it is an instance variable, or a global variable. The rules are:

  • A variable without any colon prefix refers typically to a method scoped variable. Such a variable is created during the invocation of the method, and it is deleted, when the method ends. In the example below, the variable a is method scoped.

  • A variable with a single colon prefix refers to an instance variable. An instance variable is part of the object; when the object is destroyed, its instance variables are deleted as well. In the example below, the variable b is an instance variable.

  • A variable with two leading colons refers to a global variable. The lifespan of a globale variable ends when the variable is explicitly unset or the script terminates. Variables, which are placed in Tcl namespaces, are also global variables. In the example below, the variable c is a global variable.

Listing 16: Scopes of Variables

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
nx::Class create Foo {

  :method foo args {...}
    # "a" is a method scoped variable
    set a 1
    # "b" is an Instance variable
    set :b 2
    # "c" is a global variable/namespaced variable
    set ::c 3
  }
}

Listing 16 shows a method foo of some class Foo referring to differently scoped variables.

3.1.1. Properties: Configurable Instance Variables

As described above, there is no need to declare instance variables in NX. In many cases, a developer might want to define some value constraints for variables, or to provide defaults, or to make variables configurable upon object creation. Often, variables are "inherited", meaning that the variables declared in a general class are also available in a more specialized class. For these purposes NX provides variable handlers responsible for the management of instance variables. We distinguish in NX between configurable variables (called property) and variables that are not configurable (called variable).

A property is a definition of a configurable instance variable.

The term configurable means that (a) one can provide at creation time of an instance a value for this variable, and (b), one can query the value via the accessor function cget and (c), one can change the value of the variable via configure at runtime. Since the general accessor function cget and configure are available, an application developer does not have to program own accessor methods. When value checkers are provided, each time, the value of the variable is to be changed, the constrained are checked as well.

person-student.png
Figure 17. Classes Person and Student

The class diagram above defines the classes Person and Student. For both classes, configurable instance variable are specified by defining these as properties. The listing below shows an implementation of this conceptual model in NX.

Listing 18: Properties

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
#
# Define a class Person with properties "name"
# and "birthday"
#
nx::Class create Person {
  :property name:required
  :property birthday
}

#
# Define a class Student as specialization of Person
# with additional properties
#
nx::Class create Student -superclass Person {
  :property matnr:required
  :property {oncampus:boolean true}
}

#
# Create instances using configure parameters
# for the initialization
#
Person create p1 -name Bob
Student create s1 -name Susan -matnr 4711

# Access property value via accessor method
puts "The name of s1 is [s1 cget -name]"

By defining name and birthday as properties of Person, NX makes these configurable. When we create an instance of Person named p1, we can provide a value for e.g. the name by specifying -name during creation. The properties result in non-positional configure parameters which can be provided in any order. In our listing, we create an instance of Person using the configure parameter name and provide the value of Bob to the instance variable name.

The class Student is defined as a specialization of Person with two additional properties: matnr and oncampus. The property matnr is required (it has to be provided, when an instance of this class is created), and the property oncampus is boolean, and is per default set to true. Note that the class Student inherits the properties of Person. So, Student has four properties in total.

The property definitions provide the configure parameters for instance creation. Many other languages require such parameters to be passed via arguments of a constructor, which is often error prone, when values are to be passed to superclasses. Also in dynamic languages, the relationships between classes can be easily changed, and different superclasses might have different requirements in their constructors. The declarative approach in NX reduces the need for tailored constructor methods significantly.

Note, that the property matnr of class Student is required. This means, that if we try to create an instance of Student, a runtime exception will be triggered. The property oncamups is boolean and contains a default value. Providing a default value means that whenever we create an instance of this class the object will contain such an instance variable, even when we provide no value via the configure parameters.

In our listing, we create an instance of Student using the two configure parameters name and matnr. Finally, we use method cget to obtain the value of the instance variable name of object s1.

3.1.2. Non-configurable Instance Variables

In practice, not all instance variables should be configurable. But still, we want to be able to provide defaults similar to properties. To define non-configurable instance variables the predefined method variable can be used. Such instance variables are often used for e.g. keeping the internal state of an object. The usage of variable is in many respects similar to property. One difference is, that property uses the same syntax as for method parameters, whereas variable receives the default value as a separate argument (similar to the variable command in plain Tcl). The introductory Stack example in Listing 2 uses already the method variable.

Listing 19: Declaring Variables

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
nx::Class create Base {
  :variable x 1
  # ...
}

nx::Class create Derived -superclass Base {
  :variable y 2
  # ...
}

# Create instance of the class Derived
Derived create d1

# Object d1 has instance variables
# x == 1 and y == 2

Note that the variable definitions are inherited in the same way as properties. The example in Listing 19 shows a class Derived that inherits from Base. When an instance d1 is created, it will contain the two instance variables x and y. Note that the variable declarations from property and variable are used to initialize (and to configure) the instances variables of an object.

Listing 20: Setting Variables in the Constructor

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
nx::Class create Base2 {
 # ...
 :method init {} {
   set :x 1
   # ....
 }
}

nx::Class create Derived2 -superclass Base2 {
 # ...
 :method init {} {
   set :y 2
   next
   # ....
 }
}

# Create instance of the class Derived2
Derived2 create d2

In many other object oriented languages, the instance variables are initialized solely by the constructor (similar to class Derived2 in Listing 20). This approach is certainly also possible in NX. Note that the approach using constructors requires an explicit method chaining between the constructors and is less declarative than the approach in NX using property and variable.

Both, property and variable provide much more functionalities. One can for example declare public, protected or private accessor methods, or one can define variables to be incremental (for e.g. adding values to a list of values), or one can define variables specific behavior.

3.2. Method Definitions

The basic building blocks of an object oriented program are object and classes, which contain named pieces of code, the methods.

Methods are subroutines (pieces of code) associated with objects and/or classes. A method has a name, receives optionally arguments during invocation and returns a value.

Plain Tcl provides subroutines, which are not associated with objects or classes. Tcl distinguishes between +proc+s (scripted subroutines) and commands (system-languages implemented subroutines).

Methods might have different scopes, defining, on which kind of objects these methods are applicable to. These are described in more detail later on. For the time being, we deal here with methods defined on classes, which are applicable for the instance of these classes.

3.2.1. Scripted Methods

Since NX is a scripting language, most methods are most likely scripted methods, in which the method body contains Tcl code.

Listing 21: Scripted method

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
# Define a class
nx::Class create Dog {

  # Define a scripted method for the class
  :public method bark {} {
    puts "[self] Bark, bark, bark."
  }
}

# Create an instance of the class
Dog create fido

# The following line prints "::fido Bark, bark, bark."
fido bark

In the example above we create a class Dog with a scripted method named bark. The method body defines the code, which is executed when the method is invoked. In this example, the method bar prints out a line on the terminal starting with the object name (this is determined by the built in command self) followed by "Bark, bark, bark.". This method is defined on a class and applicable to instances of the class (here the instance fido).

3.2.2. C-implemented Methods

Not all of the methods usable in NX are scripted methods; many predefined methods are defined in the underlying system language, which is typically C. For example, in Listing 21 we used the method create to create the class Dog and to create the dog instance fido. These methods are implemented in C in the next scripting framework.

C-implemented methods are not only provided by the underlying framework but might be as well defined by application developers. This is an advanced topic, not covered here. However, application developer might reuse some generic C code to define their own C-implemented methods. Such methods are for example accessors, forwarders and aliases.

An accessor method is a method that accesses instance variables of an object. A call to an accessor without arguments uses the accessor as a getter, obtaining the actual value of the associated variable. A call to an accessor with an argument uses it as a setter, setting the value of the associated variable.

NX provides support for C-implemented accessor methods. Accessors have already been mentioned in the section about properties. When the option -accessor public|protected|private is provided to a variable or property definition, NX creates automatically a same-named accessors method.

Listing 22: Accessor Methods

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
nx::Class create Dog {
 :public method bark {} { puts "[self] Bark, bark, bark." }
 :method init {} { Tail create [self]::tail}
}

nx::Class create Tail {
  :property -accessor public {length:double 5}
  :public method wag {} {return Joy}
}

# Create an instance of the class
Dog create fido

# Use the accessor "length" as a getter, to obtain the value
# of a property. The following call returns the length of the
# tail of fido
fido::tail length get

# Use the accessor "length" as a setter, to alter the value
# of a property. The following call changes the length of
# the tail of fido
fido::tail length set 10

# Proving an invalid values will raise an error
fido::tail length set "Hello"

Listing 22 shows an extended example, where every dog has a tail. The object tail is created as a subobject of the dog in the constructor init. The subobject can be accessed by providing the full name of the subobject fido::tail. The method length is an C-implemented accessor, that enforces the value constraint (here a floating point number, since length uses the value constraint double). Line 25 will therefore raise an exception, since the provided values cannot be converted to a double number.

Listing 23: Forwarder Methods

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
nx::Class create Dog {
  :public method bark {} { puts "[self] Bark, bark, bark." }
  :method init {} {
    Tail create [self]::tail
    :public object forward wag [self]::tail wag
  }
}

nx::Class create Tail {
  :property {length 5}
  :public method wag {} {return Joy}
}

# Create an instance of the class
Dog create fido

# The invocation of "fido wag" is delegated to "fido::tail wag".
# Therefore, the following method returns "Joy".
fido wag

Listing 23 again extends the example by adding a forwarder named wag to the object (e.g. fido). The forwarder redirects all calls of the form fido wag with arbitrary arguments to the subobject fido::tail.

A forwarder method is a C-implemented method that redirects an invocation for a certain method to either a method of another object or to some other method of the same object. Forwarding an invocation of a method to some other object is a means of delegation.

The functionality of the forwarder can just as well be implemented as a scripted method, but for the most common cases, the forward implementation is more efficient, and the forward method expresses the intention of the developer.

The method forwarder has several options to change e.g. the order of the arguments, or to substitute certain patterns in the argument list etc. This will be described in later sections.

3.2.3. Method-Aliases

An alias method is a means to register either an existing method, or a Tcl proc, or a Tcl command as a method with the provided name on a class or object.

In some way, the method alias is a restricted form of a forwarder, though it does not support delegation to different objects or argument reordering. The advantage of the method alias compared to a forwarder is that it has close to zero overhead, especially for aliasing c-implemented methods.

Listing 24: Method-Alias

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
nx::Class create Dog {
  :public method bark {} { puts "[self] Bark, bark, bark." }

  # Define a public alias for the method "bark"
  :public alias warn [:info method handle bark]
  # ...
}

# Create an instance of the class
Dog create fido

# The following line prints "::fido Bark, bark, bark."
fido warn

Listing 24 extends the last example by defining an alias for the method bark. The example only shows the bare mechanism. In general, method aliases are very powerful means for reusing pre-existing functionality. The full object system of NX and XOTcl2 is built from aliases, reusing functionality provided by the next scripting framework under different names. Method aliases are as well a means for implementing traits in NX.

3.3. Method Protection

All kinds of methods might have different kind of protections in NX. The call-protection defines from which calling context methods might be called. The Next Scripting Framework supports as well redefinition protection for methods.

NX distinguishes between public, protected and private methods, where the default call-protection is protected.

A public method can be called from every context. A protected method can only be invoked from the same object. A private method can only be invoked from methods defined on the same entity (defined on the same class or on the same object) via the invocation with the local flag (i.e. ": -local foo").

All kind of method protections are applicable for all kind of methods, either scripted or C-implemented.

The distinction between public and protected leads to interfaces for classes and objects. Public methods are intended for consumers of these entities. Public methods define the intended ways of providing methods for external usages (usages, from other objects or classes). Protected methods are intended for the implementor of the class or subclasses and not for public usage. The distinction between protected and public reduces the coupling between consumers and the implementation, and offers more flexibility to the developer.

Listing 25: Protected Methods

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
nx::Class create Foo {

  # Define a public method
  :public method foo {} {
    # ....
    return [:helper]
  }

  # Define a protected method
  :method helper {} {
     return 1
  }
}

# Create an instance of the class:
Foo create f1

# The invocation of the public method "foo" returns 1
f1 foo

# The invocation of the protected method "helper" raises an error:
f1 helper

The example above uses :protected method helper …. We could have used here as well :method helper …, since the default method call-protection is already protected.

The method call-protection of private goes one step further and helps to hide implementation details also for implementors of subclasses. Private methods are a means for avoiding unanticipated name clashes. Consider the following example:

Listing 26: Private Methods

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
nx::Class create Base {
  :private method helper {a b} {expr {$a + $b}}
  :public method foo     {a b} {: -local helper $a $b}
}

nx::Class create Sub -superclass Base {
  :public method bar     {a b} {: -local helper $a $b}
  :private method helper {a b} {expr {$a * $b}}
  :create s1
}

s1 foo 3 4     ;# returns 7
s1 bar 3 4     ;# returns 12
s1 helper 3 4  ;# raises error: unable to dispatch method helper

The base class implements a public method foo using the helper method named helper. The derived class implements a as well a public method bar, which is also using a helper method named helper. When an instance s1 is created from the derived class, the method foo is invoked which uses in turn the private method of the base class. Therefore, the invocation s1 foo 3 4 returns its sum. If the local flag had not beed used in helper, s1 would have tried to call the helper of Sub, which would be incorrect. For all other purposes, the private methods are "invisible" in all situations, e.g., when mixins are used, or within the next-path, etc.

By using the -local flag at the call site it is possible to invoke only the local definition of the method. If we would call the method without this flag, the resolution order would be the standard resolution order, starting with filters, mixins, object methods and the full intrinsic class hierarchy.

NX supports the modifier private for methods and properties. In all cases private is an instrument to avoid unanticipated interactions and means actually "accessible for methods defined on the same entity (object or class)". The main usage for private is to improve locality of the code e.g. for compositional operations.

In order to improve locality for properties, a private property defines therefore internally a variable with a different name to avoid unintended interactions. The variable should be accessed via the private accessor, which can be invoked with the -local flag. In the following example class D introduces a private property with the same name as a property in the superclass.

Listing 27: Private Properties

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
#
# Define a class C with a property "x" and a public accessor
#
nx::Class create C {
  :property -accessor public {x c}
}

#
# Define a subclass D with a private property "x"
# and a method bar, which is capable of accessing
# the private property.
#
nx::Class create D -superclass C {
  :property -accessor private {x d}
  :public method bar {p} {return [: -local $p get]}
}

#
# The private and public (or protected) properties
# define internally separate variable that do not
# conflict.
#
D create d1
puts [d1 x get]   ;# prints "c"
puts [d1 bar x]   ;# prints "d"

Without the private definition of the property, the definition of property x in class D would shadow the definition of the property in the superclass C for its instances (d1 x or set :x would return d instead of c).

3.4. Applicability of Methods

As defined above, a method is a subroutine defined on an object or class. This object (or class) contains the method. If the object (or class) is deleted, the contained methods will be deleted as well.

3.4.1. Instance Methods

Typically, methods are defined on a class, and the methods defined on the class are applicable to the instances (direct or indirect) of this class. These methods are called instance methods.

In the following example method, foo is an instance method defined on class C.

Listing 28: Methods applicable for instances

  1
  2
  3
  4
  5
  6
  7
  8
nx::Class create C {
  :public method foo {} {return 1}
  :create c1
}

# Method "foo" is defined on class "C"
# and applicable to the instances of "C"
c1 foo

There are many programming languages that only allow these types of methods. However, NX also allows methods to be defined on objects.

3.4.2. Object Methods

Methods defined on objects are object methods. Object methods are only applicable on the object, on which they are defined. Object methods cannot be inherited from other objects.

The following example defines an object method bar on the instance c1 of class C, and as well as the object specific method baz defined on the object o1. An object method is defined via object method.

Note that we can define a object method that shadows (redefines) for this object methods provided from classes.

Listing 29: Object Method

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
nx::Class create C {
  :public method foo {} {return 1}
  :create c1 {
     :public object method foo {} {return 2}
     :public object method bar {} {return 3}
  }
}

# Method "bar" is an object specific method of "c1"
c1 bar

# object-specific method "foo" returns 2
c1 foo

# Method "baz" is an object specific method of "o1"
nx::Object create o1 {
  :public object method baz {} {return 4}
}
o1 baz

3.4.3. Class Methods

A class method is a method defined on a class, which is only applicable to the class object itself. The class method is actually an object method of the class object.

In NX, all classes are objects. Classes are in NX special kind of objects that have e.g. the ability to create instances and to provide methods for the instances. Classes manage their instances. The general method set for classes is defined on the meta-classes (more about this later).

The following example defines a public class method bar on class C. The class method is specified by using the modifier object in front of method in the method definition command.

Listing 30: Class Methods

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
nx::Class create C {
  #
  # Define a class method "bar" and an instance
  # method "foo"
  #
  :public object method bar {} {return 2}
  :public method foo {} {return 1}

  #
  # Create an instance of the current class
  #
  :create c1
}

# Method "bar" is a class method of class "C"
# therefore applicable on the class object "C"
C bar

# Method "foo" is an instance method of "C"
# therefore applicable on instance "c1"
c1 foo

# When trying to invoke the class method on the
# instance, an error will be raised.
c1 bar

In some other object oriented programming languages, class methods are called "static methods".

3.5. Ensemble Methods

NX provides ensemble methods as a means to structure the method name space and to group related methods. Ensemble methods are similar in concept to Tcl’s ensemble commands.

An ensemble method is a form of a hierarchical method consisting of a container method and sub-methods. The first argument of the container method is interpreted as a selector (the sub-method). Every sub-method can be an container method as well.

Ensemble methods provide a means to group related commands together, and they are extensible in various ways. It is possible to add sub-methods at any time to existing ensembles. Furthermore, it is possible to extend ensemble methods via mixin classes.

The following example defines an ensemble method for string. An ensemble method is defined when the provide method name contains a space.

Listing 31: Ensemble Method

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
nx::Class create C {

    # Define an ensemble method "string" with sub-methods
    # "length", "tolower" and "info"

    :public method "string length"  {x} {....}
    :public method "string tolower" {x} {...}
    :public method "string info" {x} {...}
    #...
    :create c1
}

# Invoke the ensemble method
c1 string length "hello world"

3.6. Method Resolution

When a method is invoked, the applicable method is searched in the following order:

Per-object Mixins -> Per-class Mixins -> Object -> Intrinsic Class Hierarchy

In the case, no mixins are involved, first the object is searched for an object method with the given name, and then the class hierarchy of the object. The method can be defined multiple times on the search path, so some of these method definitions might be shadowed by the more specific definitions.

Listing 32: Method Resolution with Intrinsic Classes

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
nx::Class create C {
  :public method foo {} {
    return "C foo: [next]"
  }
}

nx::Class create D -superclass C {

  :public method foo {} {
    return "D foo: [next]"
  }

   :create d1 {
     :public object method foo {} {
       return "d1 foo: [next]"
     }
   }
}

# Invoke the method foo
d1 foo
# result: "d1 foo: D foo: C foo: "

# Query the precedence order from NX via introspection
d1 info precedence
# result: "::D ::C ::nx::Object"

Consider the example in Listing 32. When the method foo is invoked on object d1, the object method has the highest precedence and is therefore invoked. The object methods shadows the same-named methods in the class hierarchy, namely the method foo of class D and the method foo of class C. The shadowed methods can be still invoked, either via the primitive next or via method handles (we used already method handles in the section about method aliases). In the example above, next calls the shadowed method and add their results to the results of every method. So, the final result contains parts from d1, D and C. Note, that the topmost next in method foo of class C shadows no method foo and simply returns empty (and not an error message).

The introspection method info precedence provides information about the order, in which classes processed during method resolution.

Listing 33: Method Resolution with Mixin Classes

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
nx::Class create M1 {
  :public method foo {} { return "M1 foo: [next]"}
}
nx::Class create M2 {
  :public method foo {} { return "M2 foo: [next]"}
}

#
# "d1" is created based on the definitions of the last example
#
# Add the methods from "M1" as per-object mixin to "d1"
d1 object mixins add M1

#
# Add the methods from "M2" as per-class mixin to class "C"
C mixins add M2

# Invoke the method foo
d1 foo
# result: "M1 foo: M2 foo: d1 foo: D foo: C foo: "

# Query the precedence order from NX via introspection
d1 info precedence
# result: "::M1 ::M2 ::D ::C ::nx::Object"

The example in Listing 33 is an extension of the previous example. We define here two additional classes M1 and M2 which are used as per-object and per-class mixins. Both classes define the method foo, these methods shadow the definitions of the intrinsic class hierarchy. Therefore an invocation of foo on object d1 causes first an invocation of method in the per-object mixin.

Listing 34: Method Invocation Flags

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
#
# "d1" is created based on the definitions of the last two examples,
# the mixins "M1" and "M2" are registered.
#
# Define a public object method "bar", which calls the method
# "foo" which various invocation options:
#
d1 public object method bar {} {
   puts [:foo]
   puts [: -local foo]
   puts [: -intrinsic foo]
   puts [: -system foo]
}

# Invoke the method "bar"
d1 bar

In the first line of the body of method bar, the method foo is called as usual with an implicit receiver, which defaults to the current object (therefore, the call is equivalent to d1 foo). The next three calls show how to provide flags that influence the method resolution. The flags can be provided between the colon and the method name. These flags are used rather seldomly but can be helpful in some situations.

The invocation flag -local means that the method has to be resolved from the same place, where the current method is defined. Since the current method is defined as a object method, foo is resolved as a object method. The effect is that the mixin definitions are ignored. The invocation flag -local was already introduced int the section about method protection, where it was used to call private methods.

The invocation flag -intrinsic means that the method has to be resolved from the intrinsic definitions, meaning simply without mixins. The effect is here the same as with the invocation flag -local.

The invocation flag -system means that the method has to be resolved from basic - typically predefined - classes of the object system. This can be useful, when script overloads system methods, but still want to call the shadowed methods from the base classes. In our case, we have no definitions of foo on the base clases, therefore an error message is returned.

The output of Listing 34 is:

   M1 foo: M2 foo: d1 foo: D foo: C foo:
   d1 foo: D foo: C foo:
   d1 foo: D foo: C foo:
   ::d1: unable to dispatch method 'foo'

3.7. Parameters

NX provides a generalized mechanism for passing values to either methods (we refer to these as method parameters) or to objects (these are called configure parameters). Both kind of parameters might have different features, such as:

  • Positional and non-positional parameters

  • Required and non-required parameters

  • Default values for parameters

  • Value-checking for parameters

  • Multiplicity of parameters

TODO: complete list above and provide a short summary of the section

Before we discuss method and configure parameters in more detail, we describe the parameter features in the subsequent sections based on method parameters.

3.7.1. Positional and Non-Positional Parameters

If the position of a parameter in the list of formal arguments (e.g. passed to a function) is significant for its meaning, this is a positional parameter. If the meaning of the parameter is independent of its position, this is a non-positional parameter. When we call a method with positional parameters, the meaning of the parameters (the association with the argument in the argument list of the method) is determined by its position. When we call a method with non-positional parameters, their meaning is determined via a name passed with the argument during invocation.

Listing 35: Positional and Non-Positional Method Parameters

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
nx::Object create o1 {

  #
  # Method foo has positional parameters:
  #
  :public object method foo {x y} {
    puts "x=$x y=$y"
  }

  #
  # Method bar has non-positional parameters:
  #
  :public object method bar {-x -y} {
    puts "x=$x y=$y"
  }

  #
  # Method baz has non-positional and
  # positional parameters:
  #
  :public object method baz {-x -y a} {
    puts "x? [info exists x] y? [info exists y] a=$a"
  }
}

# invoke foo (positional parameters)
o1 foo 1 2

# invoke bar (non-positional parameters)
o1 bar -y 3 -x 1
o1 bar -x 1 -y 3

# invoke baz (positional and non-positional parameters)
o1 baz -x 1 100
o1 baz 200
o1 baz -- -y

Consider the example in Listing 35. The method foo has the argument list x y. This means that the first argument is passed in an invocation like o1 foo 1 2 to x (here, the value 1), and the second argument is passed to y (here the value 2). Method bar has in contrary just with non-positional arguments. Here we pass the names of the parameter together with the values. In the invocation o1 bar -y 3 -x 1 the names of the parameters are prefixed with a dash ("-"). No matter whether in which order we write the non-positional parameters in the invocation (see line 30 and 31 in Listing 35) in both cases the variables x and y in the body of the method bar get the same values assigned (x becomes 1, y becomes 3).

It is certainly possible to combine positional and non-positional arguments. Method baz provides two non-positional parameter (-y and -y) and one positional parameter (namely a). The invocation in line 34 passes the value of 1 to x and the value of 100 to a. There is no value passed to y, therefore value of y will be undefined in the body of baz, info exists y checks for the existence of the variable y and returns 0.

The invocation in line 35 passes only a value to the positional parameter. A more tricky case is in line 36, where we want to pass -y as a value to the positional parameter a. The case is more tricky since syntactically the argument parser might consider -y as the name of one of the non-positional parameter. Therefore we use -- (double dash) to indicate the end of the block of the non-positional parameters and therefore the value of -y is passed to a.

3.7.2. Optional and Required Parameters

Per default positional parameters are required, and non-positional parameters are optional (they can be left out). By using parameter options, we can as well define positional parameters, which are optional, and non-positional parameters, which are required.

Listing 36: Optional and Required Method Parameters

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
nx::Object create o2 {

  #
  # Method foo has one required and one optional
  # positional parameter:
  #
  :public object method foo {x:required y:optional} {
    puts "x=$x y? [info exists y]"
  }

  #
  # Method bar has one required and one optional
  # non-positional parameter:
  #
  :public object method bar {-x:required -y:optional} {
    puts "x=$x y? [info exists y]"
  }
}

# invoke foo (one optional positional parameter is missing)
o2 foo 1

The example in Listing 36 defined method foo with one required and one optional positional parameter. For this purpose we use the parameter options required and optional. The parameter options are separated from the parameter name by a colon. If there are multiple parameter options, these are separated by commas (we show this in later examples).

The parameter definition x:required for method foo is equivalent to x without any parameter options (see e.g. previous example), since positional parameters are per default required. The invocation in line 21 of Listing 36 will lead to an undefined variable y in method foo, because no value us passed to the optional parameter. Note that only trailing positional parameters might be optional. If we would call method foo of Listing 35 with only one argument, the system would raise an exception.

Similarly, we define method bar in Listing 36 with one required and one optional non-positional parameter. The parameter definition -y:optional is equivalent to -y, since non-positional parameter are per default optional. However, the non-positional parameter -x:required is required. If we invoke bar without it, the system will raise an exception.

3.7.3. Default Values for Parameters

Optional parameters might have a default value, which will be used, when not value is provided for this parameter. Default values can be specified for positional and non-positional parameters.

Listing 37: Method Parameters with Default Values

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
nx::Object create o3 {

  #
  # Positional parameter with default value:
  #
  :public object method foo {{x 1} {y 2}} {
    puts "x=$x y=$y"
  }

  #
  # Non-positional parameter with default value:
  #
  :public object method bar {{-x 10} {-y 20}} {
    puts "x=$x y=$y"
  }
}

# use default values
o3 foo
o3 bar

In order to define a default value for a parameter, the parameter specification must be of the form of a 2 element list, where the second argument is the default value. See for an example in Listing 37.

3.7.4. Value Constraints

NX provides value constraints for all kind of parameters. By specifying value constraints a developer can restrict the permissible values for a parameter and document the expected values in the source code. Value checking in NX is conditional, it can be turned on or off in general or on a per-usage level (more about this later). The same mechanisms can be used not only for input value checking, but as well for return value checking (we will address this point as well later).

Built-in Value Constraints

NX comes with a set of built-in value constraints, which can be extended on the scripting level. The built-in checkers are either the native checkers provided directly by the Next Scripting Framework (the most efficient checkers) or the value checkers provided by Tcl through string is …. The built-in checkers have as well the advantage that they can be used also at any time during bootstrap of an object system, at a time, when e.g. no objects or methods are defined. The same checkers are used as well for all C-implemented primitives of NX and the Next Scripting Framework.

value-checkers.png
Figure 38. General Applicable Value Checkers in NX

Figure 38 shows the built-in general applicable value checkers available in NX, which can be used for all method and configure parameters. In the next step, we show how to use these value-checkers for checking permissible values for method parameters. Then we will show, how to provide more detailed value constraints.

Listing 39: Method Parameters with Value Constraints

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
nx::Object create o4 {

  #
  # Positional parameter with value constraints:
  #
  :public object method foo {x:integer o:object,optional} {
    puts "x=$x o? [info exists o]"
  }

  #
  # Non-positional parameter with value constraints:
  #
  :public object method bar {{-x:integer 10} {-verbose:boolean false}} {
    puts "x=$x verbose=$verbose"
  }
}

# The following invocation raises an exception, since the
# value "a" for parameter "x" is not an integer
o4 foo a

Value contraints are specified as parameter options in the parameter specifications. The parameter specification x:integer defines x as a required positional parmeter which value is constraint to an integer. The parameter specification o:object,optional shows how to combine multiple parameter options. The parameter o is an optional positional parameter, its value must be an object (see Listing 39). Value constraints are specified exactly the same way for non-positional parameters (see method bar in Listing 39).

Listing 40: Parameterized Value Constraints

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
#
# Create classes for Person and Project
#
nx::Class create Person
nx::Class create Project

nx::Object create o5 {
  #
  # Parameterized value constraints
  #
  :public object method work {
     -person:object,type=Person
     -project:object,type=Project
   } {
    # ...
  }
}

#
# Create a Person and a Project instance
#
Person create gustaf
Project create nx

#
# Use method with value constraints
#
o5 work -person gustaf -project nx

The native checkers object, class, metaclass and baseclass can be further specialized with the parameter option type to restrict the permissible values to instances of certain classes. We can use for example the native value constraint object either for testing whether an argument is some object (without further constraints, as in Listing 37, method foo), or we can constrain the value further to some type (direct or indirect instance of a class). This is shown by method work in Listing 40 which requires the parameter -person to be an instance of class Person and the parameter -project to be an instance of class Project.

Scripted Value Constraints

The set of predefined value checkers can be extended by application programs via defining methods following certain conventions. The user defined value checkers are defined as methods of the class nx::Slot or of one of its subclasses or instances. We will address such cases in the next sections. In the following example we define two new value checkers on class nx::Slot. The first value checker is called groupsize, the second one is called choice.

Listing 41: Scripted Value Checker for Method Parameters

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
#
# Value checker named "groupsize"
#
::nx::Slot method type=groupsize {name value} {
  if {$value < 1 || $value > 6} {
    error "Value '$value' of parameter $name is not between 1 and 6"
  }
}

#
# Value checker named "choice" with extra argument
#
::nx::Slot method type=choice {name value arg} {
  if {$value ni [split $arg |]} {
    error "Value '$value' of parameter $name not in permissible values $arg"
  }
}

#
# Create an application class D
# using the new value checkers
#
nx::Class create D {
  :public method foo {a:groupsize} {
    # ...
  }
  :public method bar {a:choice,arg=red|yellow|green b:choice,arg=good|bad} {
    # ...
  }
}

D create d1

# testing "groupsize";
# the second call (with value 10) will raise an exception:
d1 foo 2
d1 foo 10

# testing "choice"
# the second call (with value pink for parameter a)
# will raise an exception:
d1 bar green good
d1 bar pink bad

In order to define a checker groupsize a method of the name type=groupsize is defined. This method receives two arguments, name and value. The first argument is the name of the parameter (mostly used for the error message) and the second parameter is provided value. The value checker simply tests whether the provided value is between 1 and 3 and raises an exception if this is not the case (invocation in line 36 in Listing 41).

The checker groupsize has the permissible values defined in its method’s body. It is as well possible to define more generic checkers that can be parameterized. For this parameterization, one can pass an argument to the checker method (last argument). The checker choice can be used for restricting the values to a set of predefined constants. This set is defined in the parameter specification. The parameter a of method bar in Listing 41 is restricted to the values red, yellow or green, and the parameter b is restricted to good or bad. Note that the syntax of the permissible values is solely defined by the definition of the value checker in lines 13 to 17. The invocation in line 39 will be ok, the invocation in line 40 will raise an exception, since pink is not allowed.

If the same checks are used in many places in the program, defining names for the value checker will be the better choice since it improves maintainability. For seldomly used kind of checks, the parameterized value checkers might be more convenient.

3.7.5. Multiplicity

Multiplicity is used to define whether a parameter should receive single or multiple values.

A multiplicity specification has a lower and an upper bound. A lower bound of 0 means that the value might be empty. A lower bound of 1 means that the parameter needs at least one value. The upper bound might be 1 or n (or synonymously *). While the upper bound of 1 states that at most one value has to be passed, the upper bound of n says that multiple values are permitted. Other kinds of multiplicity are currently not allowed.

The multiplicity is written as parameter option in the parameter specification in the form lower-bound..upper-bound. If no multiplicity is defined the default multiplicity is 1..1, which means: provide exactly one (atomic) value (this was the case in the previous examples).

Listing 42: Method Parameters with Explicit Multiplicity

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
nx::Object create o6 {

  #
  # Positional parameter with an possibly empty
  # single value
  #
  :public object method foo {x:integer,0..1} {
    puts "x=$x"
  }

  #
  # Positional parameter with an possibly empty
  # list of values value
  #
  :public object method bar {x:integer,0..n} {
    puts "x=$x"
  }

  #
  # Positional parameter with a non-empty
  # list of values
  #
  :public object method baz {x:integer,1..n} {
    puts "x=$x"
  }
}

Listing 42 contains three examples for positional parameters with different multiplicities. Multiplicity is often combined with value constraints. A parameter specification of the form x:integer,0..n means that the parameter x receives a list of integers, which might be empty. Note that the value constraints are applied to every single element of the list.

The parameter specification x:integer,0..1 means that x might be an integer or it might be empty. This is one style of specifying that no explicit value is passed for a certain parameter. Another style is to use required or optional parameters. NX does not enforce any particular style for handling unspecified values.

All the examples in Listing 42 are for single positional parameters. Certainly, multiplicity is fully orthogonal with the other parameter features and can be used as well for multiple parameters, non-positional parameter, default values, etc.

4. Advanced Language Features

4.1. Objects, Classes and Meta-Classes

4.2. Resolution Order and Next-Path

4.3. Details on Method and Configure Parameters

The parameter specifications are used in NX for the following purposes. They are used for

  • the specification of input arguments of methods and commands, for

  • the specification of return values of methods and commands, and for

  • the specification for the initialization of objects.

We refer to the first two as method parameters and the last one as configure parameters. The examples in the previous sections all parameter specification were specifications of method parameters.

Method parameters specify properties about permissible values passed to methods.

The method parameter specify how methods are invoked, how the actual arguments are passed to local variables of the invoked method and what kind of checks should be performed on these.

Configure parameters are parameters that specify, how objects can be parameterized upon creation.

Syntactically, configure parameters and method parameters are the same, although there are certain differences (e.g. some parameter options are only applicable for objects parameters, the list of object parameters is computed dynamically from the class structures, object parameters are often used in combination with special setter methods, etc.). Consider the following example, where we define the two application classes Person and Student with a few properties.

Listing 43: Configure Parameters

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
#
# Define a class Person with properties "name"
# and "birthday"
#
nx::Class create Person {
  :property name:required
  :property birthday
}

#
# Define a class Student as specialization of Person
# with and additional property
#
nx::Class create Student -superclass Person {
  :property matnr:required
  :property {oncampus:boolean true}
}

#
# Create instances using configure parameters
# for the initialization
#
Person create p1 -name Bob
Student create s1 -name Susan -matnr 4711

# Access property value via "cget" method
puts "The name of s1 is [s1 cget -name]"

The class Person has two properties name and birthday, where the property name is required, the property birthday is not. The class Student is a subclass of Person with the additional required property matnr and an optional property oncampus with the default value true (see Listing 43). The class diagram below visualizes these definitions.

configure-parameter.png
Figure 44. System and Application Classes

In NX, these definitions imply that instances of the class of Person have the properties name and birthday as non-positional object parameters. Furthermore it implies that instances of Student will have the configure parameters of Person augmented with the object parameters from Student (namely matnr and oncampus). Based on these configure parameters, we can create a Person named Bob and a Student named Susan with the matriculation number 4711 (see line 23 and 24 in <<xmp-object-parameters, instance variables name, matnr and oncampus (the latter is initialized with the default value).

4.3.1. Configure Parameters available for all NX Objects

The configure parameters are not limited to the application defined properties, also NX provides some predefined definitions. Since Person is a subclass of nx::Object also the configure parameters of nx::Object are inherited. In the introductory stack example, we used -mixins applied to an object to denote per-object mixins (see Listing 8). Since mixins is defined as a parameter on nx::Object it can be used as an object parameter -mixins for all objects in NX. To put it in other words, every object can be configured to have per-object mixins. If we would remove this definition, this feature would be removed as well.

As shown in the introductory examples, every object can be configured via a scripted initialization block (the optional scripted block specified at object creation as last argument; see Listing 5 or Listing 12). The scripted block and its meaning are as well defined by the means of configure parameters. However, this configure parameter is positional (last argument) and optional (it can be omitted). The following listing shows the configure parameters of Person p1 and Student s1.

Listing 45: Computed Actual Configure Parameter

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
Configure parameters for Person p1:
   Command:
      p1 info lookup syntax configure
   Result:
      -name /value/ ?-birthday /value/? ?-object-mixins /mixinreg .../?
      ?-class /class/? ?-object-filters /filterreg .../? ?/__initblock/?

Configure parameter for Student s1:
   Command:
      s1 info lookup syntax configure
   Result:
      ?-oncampus /boolean/? -matnr /value/ -name /value/
      ?-birthday /value/? ?-object-mixins /mixinreg .../? ?-class /class/?
      ?-object-filters /filterreg .../? ?/__initblock/?

The given paramter show, how (a) objects can be configured at runtime or (b) how new instances can be configured at creation time via the new or create methods. Introspection can be used to obtain the configuration parameters from an object via p1 info lookup parameters configure (returning the configure parameters currently applicable for configure or cget) or from a class Person info lookup parameters create on a class (returning the configure parameters applicable when an object of this class is created)

The listed configure parameter types mixinreg and filterreg are for converting definitions of filters and mixins. The last value __initblock says that the content of this variable will be executed in the context of the object being created (before the constructor init is called). More about the configure parameter types later.

4.3.2. Configure Parameters available for all NX Classes

Since classes are certain kind of objects, classes are parameterized in the same way as objects. A typical parameter for a class definition is the relation of the class to its superclass.In our example, we have specified, that Student has Person as superclass via the non-positional configure parameter -superclass. If no superclass is specified for a class, the default superclass is nx::Object. Therefore nx::Object is the default value for the parameter superclass.

Another frequently used parameter for classes is -mixins to denote per-class mixins (see e.g. the introductory Stack example in Listing 10), which is defined in the same way.

Since Student is an instance of the meta-class nx::Class it inherits the configure parameters from nx::Class (see class diagram Figure 44). Therefore, one can use e.g. -superclass in the definition of classes.

Since nx::Class is a subclass of nx::Object, the meta-class nx::Class inherits the parameter definitions from the most general class nx::Object. Therefore, every class might as well be configured with a scripted initialization block the same way as objects can be configured. We used actually this scripted initialization block in most examples for defining the methods of the class. The following listing shows (simplified) the parameters applicable for Class Student.

Listing 46: Parameters for Classes

  1
  2
  3
  4
  5
  6
  7
Configure parameter for class nx::Class
   Command:
      nx::Class info lookup syntax configure
   Result:
      ?-superclass /class .../? ?-mixins /mixinreg .../?
      ?-filters /filterreg .../? ?-object-mixins /mixinreg .../?
      ?-class /class/? ?-object-filters /filterreg .../? ?/__initblock/?

4.3.3. User defined Parameter Types

More detailed definition of the configure parameter types comes here.

4.3.4. Slot Classes and Slot Objects

In one of the previous sections, we defined scripted (application defined) checker methods on a class named nx::Slot. In general NX offers the possibility to define value checkers not only for all usages of parameters but as well differently for method parameters or configure parameters

slots.png
Figure 47. Slot Classes and Objects

4.3.5. Attribute Slots

Still Missing

  • return value checking

  • switch

  • initcmd …

  • subst rules

  • converter

  • incremental slots

5. Miscellaneous

5.1. Profiling

5.2. Unknown Handlers

NX provides two kinds of unknown handlers:

  • Unknown handlers for methods

  • Unknown handlers for objects and classes

5.2.1. Unknown Handlers for Methods

Object and classes might be equipped with a method unknown which is called in cases, where an unknown method is called. The method unknown receives as first argument the called method followed by the provided arguments

Listing 48: Unknown Method Handler

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
::nx::Object create o {
  :object method unknown {called_method args} {
    puts "Unknown method '$called_method' called"
  }
}

# Invoke an unknown method for object o:
o foo 1 2 3

# Output will be: "Unknown method 'foo' called"

Without any provision of an unknown method handler, an error will be raised, when an unknown method is called.

5.2.2. Unknown Handlers for Objects and Classes

The next scripting framework provides in addition to unknown method handlers also a means to dynamically create objects and classes, when these are referenced. This happens e.g. when superclasses, mixins, or parent objects are referenced. This mechanism can be used to implement e.g. lazy loading of these classes. Nsf allows to register multiple unknown handlers, each identified by a key (a unique name, different from the keys of other unknown handlers).

Listing 49: Unknown Class Handler

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
::nx::Class public object method __unknown {name} {
  # A very simple unknown handler, showing just how
  # the mechanism works.
  puts "***** __unknown called with <$name>"
  ::nx::Class create $name
}

# Register an unknown handler as a method of ::nx::Class
::nsf::object::unknown::add nx {::nx::Class __unknown}

::nx::Object create o {
  # The class M is unknown at this point

  :object mixins add M
  # The line above has triggered the unknown class handler,
  # class M is now defined

  puts [:info object mixins]
  # The output will be:
  #     ***** __unknown called with <::M>
  #     ::M
}

The Next Scripting Framework allows to add, query, delete and list unknown handlers.

Listing 50: Unknown Handler registration

  1
  2
  3
  4
  5
# Interface for unknown handlers:
# nsf::object::unknown::add /key/ /handler/
# nsf::object::unknown::get /key/
# nsf::object::unknown::delete /key/
# nsf::object::unknown::keys
References
  • U. Zdun, M. Strembeck, G. Neumann: Object-Based and Class-Based Composition of Transitive Mixins, Information and Software Technology, 49(8) 2007 .

  • G. Neumann and U. Zdun: Filters as a language support for design patterns in object-oriented scripting languages. In Proceedings of COOTS’99, 5th Conference on Object-Oriented Technologies and Systems, San Diego, May 1999.

  • G. Neumann and U. Zdun: Implementing object-specific design patterns using per-object mixins. In Proc. of NOSA`99, Second Nordic Workshop on Software Architecture, Ronneby, Sweden, August 1999.

  • G. Neumann and U. Zdun: Enhancing object-based system composition through per-object mixins. In Proceedings of Asia-Pacific Software Engineering Conference (APSEC), Takamatsu, Japan, December 1999.

  • G. Neumann and U. Zdun: XOTCL, an object-oriented scripting language. In Proceedings of Tcl2k: The 7th USENIX Tcl/Tk Conference, Austin, Texas, February 2000.

  • G. Neumann and U. Zdun: Towards the Usage of Dynamic Object Aggregations as a Form of Composition In: Proceedings of Symposium of Applied Computing (SAC’00), Como, Italy, Mar 19-21, 2000.

  • G. Neumann, S. Sobernig: XOTcl 2.0 - A Ten-Year Retrospective and Outlook, in: Proceedings of the Sixteenth Annual Tcl/Tk Conference, Portland, Oregon, October, 2009.

  • J. K. Ousterhout: Tcl: An embeddable command language. In Proc. of the 1990 Winter USENIX Conference, January 1990.

  • J. K. Ousterhout: Scripting: Higher Level Programming for the 21st Century, IEEE Computer 31(3), March 1998.

  • D. Wetherall and C. J. Lindblad: Extending Tcl for Dynamic Object-Oriented Programming. Proc. of the Tcl/Tk Workshop '95, July 1995.


doc/next-tutorial/next-tutorial.txt000066400000000000000000002645661242365656200200600ustar00rootroot00000000000000Tutorial for the Next Scripting Language ========================================== Gustaf Neumann , Stefan Sobernig v2.0.0, June 2013: Written for the Initial Release of the Next Scripting Framework. :Author Initials: GN :toc: :toclevels: 4 :icons: :numbered: :website: http://www.xotcl.org/ .Abstract ***************************************************************************** This document provides a tutorial for the Next Scripting Language NX. ***************************************************************************** The Next Scripting Language (NX) is a highly flexible object oriented scripting language based on Tcl <>. NX is a successor of XOTcl 1 <> and was developed based on 10 years of experience with XOTcl in projects containing several hundred thousand lines of code. While XOTcl was the first language designed to provide _language support for design patterns_, the focus of the Next Scripting Framework and NX is on combining this with _Language Oriented Programming_. In many respects, NX was designed to ease the learning of the language for novices (by using a more mainstream terminology, higher orthogonality of the methods, less predefined methods), to improve maintainability (remove sources of common errors) and to encourage developers to write better structured programs (to provide interfaces) especially for large projects, where many developers are involved. The Next Scripting Language is based on the Next Scripting Framework (NSF) which was developed based on the notion of language oriented programming. The Next Scripting Frameworks provides C-level support for defining and hosting multiple object systems in a single Tcl interpreter. The name of the Next Scripting Framework is derived from the universal method combinator "next", which was introduced in XOTcl. The combinator "next" serves as a single instrument for method combination with filters, per-object and transitive per-class mixin classes, object methods and multiple inheritance. The definition of NX is fully scripted (e.g. defined in +nx.tcl+). The Next Scripting Framework is shipped with three language definitions, containing NX and XOTcl 2. Most of the existing XOTcl 1 programs can be used without modification in the Next Scripting Framework by using XOTcl 2. The Next Scripting Framework requires Tcl 8.5 or newer. == NX and its Roots Object oriented extensions of Tcl have quite a long history. Two of the most prominent early Tcl based OO languages were _incr Tcl_ (abbreviated as itcl) and Object Tcl (_OTcl_ <>). While itcl provides a traditional C++/Java-like object system, OTcl was following the CLOS approach and supports a dynamic object system, allowing incremental class and object extensions and re-classing of objects. Extended Object Tcl (abbreviated as XOTcl <>) is a successor of OTcl and was the first language providing language support for design patterns. XOTcl extends OTcl by providing namespace support, adding assertions, dynamic object aggregations, slots and by introducing per-object and per-class filters and per-object and per-class mixins. XOTcl was so far released in more than 30 versions. It is described in its detail in more than 20 papers and serves as a basis for other object systems like TclOO [Donal ???]. The scripting language _NX_ and the _Next Scripting Framework_ <> extend the basic ideas of XOTcl by providing support for _language-oriented programming_. The the Next Scripting Framework supports multiple object systems concurrently. Effectively, every object system has different base classes for creating objects and classes. Therefore, these object systems can have different interfaces and can follow different naming conventions for built-in methods. Currently, the Next Scripting Framework is packaged with three object systems: NX, XOTcl 2.0, and TclCool (the language introduced by TIP#279). image::languages.png[align="center",width=500,title="Language History of the Next Scripting Language",alt="Languages"] {set:img-languages:Figure {figure-number}} The primary purpose of this document is to introduce NX to beginners. We expect some prior knowledge of programming languages, and some knowledge about Tcl. In the following sections we introduce NX by examples. In later sections we introduce the more advanced concepts of the language. Conceptually, most of the addressed concepts are very similar to XOTcl. Concerning the differences between NX and XOTcl, please refer to the _Migration Guide for the Next Scripting Language_. == Introductory Overview Example: Stack A classical programming example is the implementation of a stack, which is most likely familiar to many readers from many introductory programming courses. A stack is a last-in first-out data structure which is manipulated via operations like +push+ (add something to the stack) and +pop+ remove an entry from the stack. These operations are called _methods_ in the context of object oriented programming systems. Primary goals of object orientation are encapsulation and abstraction. Therefore, we define a common unit (a class) that defines and encapsulates the behavior of a stack and provides methods to a user of the data structure that abstract from the actual implementation. === Define a Class "Stack" In our first example, we define a class named +Stack+ with the methods +push+ and +pop+. When an instance of the stack is created (e.g. a concrete stack +s1+) the stack will contain an instance variable named +things+, initialized with the an empty list. [[xmp-class-stack]] .Listing {counter:figure-number}: Class Stack {set:xmp-class-stack:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create Stack { # # Stack of Things # :variable things {} :public method push {thing} { set :things [linsert ${:things} 0 $thing] return $thing } :public method pop {} { set top [lindex ${:things} 0] set :things [lrange ${:things} 1 end] return $top } } -------------------------------------------------- Typically, classes are defined in NX via +nx::Class create+, followed by the name of the new class (here: +Stack+). The definition of the stack placed between curly braces and contains here just the method definitions. Methods of the class are defined via +:method+ followed by the name of the method, an argument list and the body of the method, consisting of Tcl and NX statements. When an instance of +Stack+ is created, it will contain an instance variable named +things+. If several +Stack+ instances are created, each of the instances will have their own (same-named but different) instance variable. The instance variable +things+ is used in our example as a list for the internal representation of the stack. We define in a next step the methods to access and modify this list structure. A user of the stack using the provided methods does not have to have any knowledge about the name or the structure of the internal representation (the instance variable +things+). The method +push+ receives an argument +thing+ which should be placed on the stack. Note that we do not have to specify the type of the element on the stack, so we can push strings as well as numbers or other kind of things. When an element is pushed, we add this element as the first element to the list +things+. We insert the element using the Tcl command +linsert+ which receives the list as first element, the position where the element should be added as second and the new element as third argument. To access the value of the instance variable we use Tcl's dollar operator followed by the name. The names of instance variables are preceded with a colon +:+. Since the name contains a non-plain character, Tcl requires us to put braces around the name. The command +linsert+ and its arguments are placed between square brackets. This means that the function +linsert+ is called and a new list is returned, where the new element is inserted at the first position (index 0) in the list +things+. The result of the +linsert+ function is assigned again to the instance variable +things+, which is updated this way. Finally the method +push+ returns the pushed thing using the +return+ statement. The method +pop+ returns the most recently stacked element and removes it from the stack. Therefore, it takes the first element from the list (using the Tcl command +lindex+), assigns it to the method-scoped variable +top+, removes the element from the instance variable +things+ (by using the Tcl command +lrange+) and returns the value popped element +top+. This finishes our first implementation of the stack, more enhanced versions will follow. Note that the methods +push+ and +pop+ are defined as +public+; this means that these methods can be used from all other objects in the system. Therefore, these methods provide an interface to the stack implementation. [[xmp-using-stack]] .Listing {counter:figure-number}: Using the Stack {set:xmp-using-stack:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- #!/usr/bin/env tclsh package require nx nx::Class create Stack { # # Stack of Things # .... } Stack create s1 s1 push a s1 push b s1 push c puts [s1 pop] puts [s1 pop] s1 destroy -------------------------------------------------- Now we want to use the stack. The code snippet in <> shows how to use the class Stack in a script. Since NX is based on Tcl, the script will be called with the Tcl shell +tclsh+. In the Tcl shell we have to +require package nx+ to use the Next Scripting Framework and NX. The next lines contain the definition of the stack as presented before. Of course, it is as well possible to make the definition of the stack an own package, such we could simple say +package require stack+, or to save the definition of a stack simply in a file and load it via +source+. In line 12 we create an instance of the stack, namely the stack object +s1+. The object +s1+ is an instance of +Stack+ and has therefore access to its methods. The methods like +push+ or +pop+ can be invoked via a command starting with the object name followed by the method name. In lines 13-15 we push on the stack the values +a+, then +b+, and +c+. In line 16 we output the result of the +pop+ method using the Tcl command +puts+. We will see on standard output the value+c+ (the last stacked item). The output of the line 17 is the value +b+ (the previously stacked item). Finally, in line 18 we destroy the object. This is not necessary here, but shows the life cycle of an object. In some respects, +destroy+ is the counterpart of +create+ from line 12. [[fig-class-object]] image::object-class-appclass.png[title="Class and Object Diagram",align="center"] {set:fig-class-object:Figure {figure-number}} <> shows the actual class and object structure of the first +Stack+ example. Note that the common root class is +nx::Object+ that contains methods for all objects. Since classes are as well objects in NX, +nx::Class+ is a specialization of +nx::Object+. +nx::Class+ provides methods for creating objects, such as the method +create+ which is used to create objects (and classes as well). === Define an Object Named "stack" The definition of the stack in <> follows the traditional object oriented approach, found in practically every object oriented programming language: Define a class with some methods, create instances from this class, and use the methods defined in the class in the instances of the class. In our next example, we introduce _generic objects_ and _object specific methods_. With NX, we can define generic objects, which are instances of the most generic class +nx::Object+ (sometimes called _common root class_). +nx::Object+ is predefined and contains a minimal set of methods applicable to all NX objects. In this example, we define a generic object named +stack+ and provide methods for this object. The methods defined above were methods provided by a class for objects. Now we define object specific methods, which are methods applicable only to the object for which they are defined. [[xmp-object-stack]] .Listing {counter:figure-number}: Object stack {set:xmp-object-stack:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Object create stack { :object variable things {} :public object method push {thing} { set :things [linsert ${:things} 0 $thing] return $thing } :public object method pop {} { set top [lindex ${:things} 0] set :things [lrange ${:things} 1 end] return $top } } -------------------------------------------------- The example in <> defines the object +stack+ in a very similar way as the class +Stack+. But the following points are different. - First, we use +nx::Object+ instead of +nx::Class+ to denote that we want to create a generic object, not a class. - We use +:object variable+ to define the variable +things+ just for this single instance (the object +stack+). - The definition for the methods +push+ and +pop+ are the same as before, but here we defined these with +object method+. Therefore, these two methods +push+ and +pop+ are object-specific. In order to use the stack, we can use directly the object +stack+ in the same way as we have used the object +s1+ in <> (e.g. +stack push a+). <> shows the class diagram for this the object +stack+. [[img-object-stack]] image::object-stack.png[title="Object stack",align="center"] {set:img-object-stack:Figure {figure-number}} A reader might wonder when to use a class +Stack+ or rather an object +stack+. A big difference is certainly that one can define easily multiple instances of a class, while the object is actually a single, tailored entity. The concept of the object +stack+ is similar to a module, providing a certain functionality via a common interface, without providing the functionality to create multiple instances. The reuse of methods provided by the class to objects is as well a difference. If the methods of the class are updated, all instances of the class will immediately get the modified behavior. However, this does not mean that the reuse for the methods of +stack+ is not possible. NX allows for example to copy objects (similar to prototype based languages) or to reuse methods via e.g. aliases (more about this later). Note that we use capitalized names for classes and lowercase names for instances. This is not required and a pure convention making it easier to understand scripts without much analysis. === Implementing Features using Mixin Classes So far, the definition of the stack methods was pretty minimal. Suppose, we want to define "safe stacks" that protect e.g. against stack under-runs (a stack under-run happens, when more +pop+ than +push+ operations are issued on a stack). Safety checking can be implemented mostly independent from the implementation details of the stack (usage of internal data structures). There are as well different ways of checking the safety. Therefore we say that safety checking is orthogonal to the stack core implementation. With NX we can define stack-safety as a separate class using methods with the same names as the implementations before, and "mix" this behavior into classes or objects. The implementation of +Safety+ in <> uses a counter to check for stack under-runs and to issue error messages, when this happens. [[xmp-class-safety]] .Listing {counter:figure-number}: Class Safety {set:xmp-class-safety:Listing {figure-number}} [source,tcl,numbered] -------------------------------------------------- nx::Class create Safety { # # Implement stack safety by defining an additional # instance variable named "count" that keeps track of # the number of stacked elements. The methods of # this class have the same names and argument lists # as the methods of Stack; these methods "shadow" # the methods of class Stack. # :variable count 0 :public method push {thing} { incr :count next } :public method pop {} { if {${:count} == 0} then { error "Stack empty!" } incr :count -1 next } } -------------------------------------------------- Note that all the methods of the class +Safety+ end with +next+. This command is a primitive command of NX, which calls the same-named method with the same argument list as the current invocation. Assume we save the definition of the class +Stack+ in a file named +Stack.tcl+ and the definition of the class +Safety+ in a file named +Safety.tcl+ in the current directory. When we load the classes +Stack+ and +Safety+ into the same script (see the terminal dialog in <>), we can define e.g. a certain stack +s2+ as a safe stack, while all other stacks (such as +s1+) might be still "unsafe". This can be achieved via the option +-mixin+ at the object creation time (see line 9 in <>) of s2. The option +-mixin+ mixes the class +Safety+ into the new instance +s2+. [[xmp-using-class-safety]] .Listing {counter:figure-number}: Using the Class Safety {set:xmp-using-class-safety:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- % package require nx 2.0 % source Stack.tcl ::Stack % source Safety.tcl ::Safety % Stack create s1 ::s1 % Stack create s2 -object-mixin Safety ::s2 % s2 push a a % s2 pop a % s2 pop Stack empty! % s1 info precedence ::Stack ::nx::Object % s2 info precedence ::Safety ::Stack ::nx::Object -------------------------------------------------- When the method +push+ of +s2+ is called, first the method of the mixin class +Safety+ will be invoked that increments the counter and continues with +next+ to call the shadowed method, here the method +push+ of the +Stack+ implementation that actually pushes the item. The same happens, when +s2 pop+ is invoked, first the method of +Safety+ is called, then the method of the +Stack+. When the stack is empty (the value of +count+ reaches 0), and +pop+ is invoked, the mixin class +Safety+ generates an error message (raises an exception), and does not invoke the method of the +Stack+. The last two commands in <> use introspection to query for the objects +s1+ and +s2+ in which order the involved classes are processed. This order is called the +precedence order+ and is obtained via +info precedence+. We see that the mixin class +Safety+ is only in use for +s2+, and takes there precedence over +Stack+. The common root class +nx::Object+ is for both +s1+ and +s2+ the base class. [[img-per-object-mixin]] image::per-object-mixin.png[title="Per-object Mixin",align="center"] {set:img-per-object-mixin:Figure {figure-number}} Note that in <>, the class +Safety+ is only mixed into a single object (here +s2+), therefore we refer to this case as a _per-object mixin_. <> shows the class diagram, where the class +Safety+ is used as a per-object mixin for +s2+. The mixin class +Safety+ can be used as well in other ways, such as e.g. for defining classes of safe stacks: [[xmp-class-safestack]] .Listing {counter:figure-number}: Class SafeStack {set:xmp-class-safestack:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- # # Create a safe stack class by using Stack and mixin # Safety # nx::Class create SafeStack -superclass Stack -mixin Safety SafeStack create s3 -------------------------------------------------- The difference of a per-class mixin and an per-object mixin is that the per-class mixin is applicable to all instances of the class. Therefore, we call these mixins also sometimes instance mixins. In our example in <>, +Safety+ is mixed into the definition of +SafeStack+. Therefore, all instances of the class +SafeStack+ (here the instance +s3+) will be using the safety definitions. [[img-per-class-mixin]] image::per-class-mixin.png[title="Per-class Mixin",align="center"] {set:img-per-class-mixin:Figure {figure-number}} <> shows the class diagram for this definition. Note that we could use +Safety+ as well as a per-class mixin on +Stack+. In this case, all stacks would be safe stacks and we could not provide a selective feature selection (which might be perfectly fine). === Define Different Kinds of Stacks The definition of +Stack+ is generic and allows all kind of elements to be stacked. Suppose, we want to use the generic stack definition, but a certain stack (say, stack +s4+) should be a stack for integers only. This behavior can be achieved by the same means as introduced already in <>, namely object-specific methods. [[xmp-object-integer-stack]] .Listing {counter:figure-number}: Object Integer Stack {set:xmp-object-integer-stack:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- Stack create s4 { # # Create a stack with a object-specific method # to check the type of entries # :public object method push {thing:integer} { next } } -------------------------------------------------- The program snippet in <> defines an instance +s4+ of the class +Stack+ and provides an object specific method for +push+ to implement an integer stack. The method +pull+ is the same for the integer stack as for all other stacks, so it will be reused as usual from the class +Stack+. The object-specific method +push+ of +s4+ has a value constraint in its argument list (+thing:integer+) that makes sure, that only integers can be stacked. In case the argument is not an integer, an exception will be raised. Of course, one could perform the value constraint checking as well in the body of the method +proc+ by accepting an generic argument and by performing the test for the value in the body of the method. In the case, the passed value is an integer, the +push+ method of <> calls +next+, and therefore calls the shadowed generic definition of +push+ as provided by +Stack+. [[xmp-class-integer-stack]] .Listing {counter:figure-number}: Class IntegerStack {set:xmp-class-integer-stack:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create IntegerStack -superclass Stack { # # Create a Stack accepting only integers # :public method push {thing:integer} { next } } -------------------------------------------------- An alternative approach is shown in <>, where the class +IntegerStack+ is defined, using the same method definition as +s4+, this time on the class level. === Define Object Specific Methods on Classes In our previous examples we defined methods provided by classes (applicable for their instances) and object-specific methods (methods defined on objects, which are only applicable for these objects). In this section, we introduce methods that are defined on the class objects. Such methods are sometimes called _class methods_ or _static methods_. In NX classes are objects, they are specialized objects with additional methods. Methods for classes are often used for managing the life-cycles of the instances of the classes (we will come to this point in later sections in more detail). Since classes are objects, we can use exactly the same notation as above to define class methods by using +object method+. The methods defined on the class object are in all respects identical with object specific methods shown in the examples above. [[xmp-stack2]] .Listing {counter:figure-number}: Class Stack2 {set:xmp-stack2:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create Stack2 { :public object method available_stacks {} { return [llength [:info instances]] } :variable things {} :public method push {thing} { set :things [linsert ${:things} 0 $thing] return $thing } :public method pop {} { set top [lindex ${:things} 0] set :things [lrange ${:things} 1 end] return $top } } Stack2 create s1 Stack2 create s2 puts [Stack2 available_stacks] -------------------------------------------------- The class +Stack2+ in <> consists of the earlier definition of the class +Stack+ and is extended by the class-specific method +available_stacks+, which returns the current number of instances of the stack. The final command +puts+ (line 26) prints 2 to the console. [[img-stack2]] image::stack2.png[align="center",title="Stack2"] {set:img-stack2:Figure {figure-number}} The class diagram in <> shows the diagrammatic representation of the class object-specific method +available_stacks+. Since every class is a specialization of the common root class +nx::Object+, the common root class is often omitted from the class diagrams, so it was omitted here as well in the diagram. == Basic Language Features of NX === Variables and Properties In general, NX does not need variable declarations. It allows to create or modify variables on the fly by using for example the Tcl commands +set+ and +unset+. Depending on the variable name (or more precisely, depending on the variable name's prefix consisting of colons "+:+") a variable is either local to a method, or it is an instance variable, or a global variable. The rules are: - A variable without any colon prefix refers typically to a method scoped variable. Such a variable is created during the invocation of the method, and it is deleted, when the method ends. In the example below, the variable +a+ is method scoped. - A variable with a single colon prefix refers to an instance variable. An instance variable is part of the object; when the object is destroyed, its instance variables are deleted as well. In the example below, the variable +b+ is an instance variable. - A variable with two leading colons refers to a global variable. The lifespan of a globale variable ends when the variable is explicitly unset or the script terminates. Variables, which are placed in Tcl namespaces, are also global variables. In the example below, the variable +c+ is a global variable. [[xmp-var-resolver]] .Listing {counter:figure-number}: Scopes of Variables {set:xmp-var-resolver:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create Foo { :method foo args {...} # "a" is a method scoped variable set a 1 # "b" is an Instance variable set :b 2 # "c" is a global variable/namespaced variable set ::c 3 } } -------------------------------------------------- <> shows a method +foo+ of some class +Foo+ referring to differently scoped variables. ==== Properties: Configurable Instance Variables As described above, there is no need to declare instance variables in NX. In many cases, a developer might want to define some value constraints for variables, or to provide defaults, or to make variables configurable upon object creation. Often, variables are "inherited", meaning that the variables declared in a general class are also available in a more specialized class. For these purposes NX provides _variable handlers_ responsible for the management of instance variables. We distinguish in NX between configurable variables (called +property+) and variables that are not configurable (called +variable+). ========================================= A *property* is a definition of a configurable instance variable. ========================================= The term configurable means that (a) one can provide at creation time of an instance a value for this variable, and (b), one can query the value via the accessor function +cget+ and (c), one can change the value of the variable via +configure+ at runtime. Since the general accessor function +cget+ and +configure+ are available, an application developer does not have to program own accessor methods. When value checkers are provided, each time, the value of the variable is to be changed, the constrained are checked as well. [[img-person-student]] image::person-student.png[align="center",title="Classes Person and Student"] {set:img-person-student:Figure {figure-number}} The class diagram above defines the classes +Person+ and +Student+. For both classes, configurable instance variable are specified by defining these as properties. The listing below shows an implementation of this conceptual model in NX. [[xmp-properties]] .Listing {counter:figure-number}: Properties {set:xmp-properties:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- # # Define a class Person with properties "name" # and "birthday" # nx::Class create Person { :property name:required :property birthday } # # Define a class Student as specialization of Person # with additional properties # nx::Class create Student -superclass Person { :property matnr:required :property {oncampus:boolean true} } # # Create instances using configure parameters # for the initialization # Person create p1 -name Bob Student create s1 -name Susan -matnr 4711 # Access property value via accessor method puts "The name of s1 is [s1 cget -name]" -------------------------------------------------- By defining +name+ and +birthday+ as properties of +Person+, NX makes these configurable. When we create an instance of +Person+ named +p1+, we can provide a value for e.g. the name by specifying +-name+ during creation. The properties result in non-positional configure parameters which can be provided in any order. In our listing, we create an instance of +Person+ using the configure parameter +name+ and provide the value of +Bob+ to the instance variable +name+. The class +Student+ is defined as a specialization of +Person+ with two additional properties: +matnr+ and +oncampus+. The property +matnr+ is required (it has to be provided, when an instance of this class is created), and the property +oncampus+ is boolean, and is per default set to +true+. Note that the class +Student+ inherits the properties of +Person+. So, +Student+ has four properties in total. The property definitions provide the +configure parameters+ for instance creation. Many other languages require such parameters to be passed via arguments of a constructor, which is often error prone, when values are to be passed to superclasses. Also in dynamic languages, the relationships between classes can be easily changed, and different superclasses might have different requirements in their constructors. The declarative approach in NX reduces the need for tailored constructor methods significantly. Note, that the property +matnr+ of class +Student+ is required. This means, that if we try to create an instance of +Student+, a runtime exception will be triggered. The property +oncamups+ is boolean and contains a default value. Providing a default value means that whenever we create an instance of this class the object will contain such an instance variable, even when we provide no value via the configure parameters. In our listing, we create an instance of +Student+ using the two configure parameters +name+ and +matnr+. Finally, we use method +cget+ to obtain the value of the instance variable +name+ of object +s1+. ==== Non-configurable Instance Variables In practice, not all instance variables should be configurable. But still, we want to be able to provide defaults similar to properties. To define non-configurable instance variables the predefined method +variable+ can be used. Such instance variables are often used for e.g. keeping the internal state of an object. The usage of +variable+ is in many respects similar to +property+. One difference is, that +property+ uses the same syntax as for method parameters, whereas +variable+ receives the default value as a separate argument (similar to the +variable+ command in plain Tcl). The introductory Stack example in <> uses already the method +variable+. [[xmp-variable]] .Listing {counter:figure-number}: Declaring Variables {set:xmp-variable:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create Base { :variable x 1 # ... } nx::Class create Derived -superclass Base { :variable y 2 # ... } # Create instance of the class Derived Derived create d1 # Object d1 has instance variables # x == 1 and y == 2 -------------------------------------------------- Note that the variable definitions are inherited in the same way as properties. The example in <> shows a class +Derived+ that inherits from +Base+. When an instance +d1+ is created, it will contain the two instance variables +x+ and +y+. Note that the variable declarations from +property+ and +variable+ are used to initialize (and to configure) the instances variables of an object. [[xmp-constructor]] .Listing {counter:figure-number}: Setting Variables in the Constructor {set:xmp-constructor:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create Base2 { # ... :method init {} { set :x 1 # .... } } nx::Class create Derived2 -superclass Base2 { # ... :method init {} { set :y 2 next # .... } } # Create instance of the class Derived2 Derived2 create d2 -------------------------------------------------- In many other object oriented languages, the instance variables are initialized solely by the constructor (similar to class +Derived2+ in <>). This approach is certainly also possible in NX. Note that the approach using constructors requires an explicit method chaining between the constructors and is less declarative than the approach in NX using +property+ and +variable+. Both, +property+ and +variable+ provide much more functionalities. One can for example declare +public+, +protected+ or +private+ accessor methods, or one can define variables to be incremental (for e.g. adding values to a list of values), or one can define variables specific behavior. === Method Definitions The basic building blocks of an object oriented program are object and classes, which contain named pieces of code, the methods. =========================================== *Methods* are subroutines (pieces of code) associated with objects and/or classes. A method has a name, receives optionally arguments during invocation and returns a value. =========================================== Plain Tcl provides subroutines, which are not associated with objects or classes. Tcl distinguishes between +proc+s (scripted subroutines) and commands (system-languages implemented subroutines). Methods might have different scopes, defining, on which kind of objects these methods are applicable to. These are described in more detail later on. For the time being, we deal here with methods defined on classes, which are applicable for the instance of these classes. ==== Scripted Methods Since NX is a scripting language, most methods are most likely scripted methods, in which the method body contains Tcl code. [[xmp-fido1]] .Listing {counter:figure-number}: Scripted method {set:xmp-fido1:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- # Define a class nx::Class create Dog { # Define a scripted method for the class :public method bark {} { puts "[self] Bark, bark, bark." } } # Create an instance of the class Dog create fido # The following line prints "::fido Bark, bark, bark." fido bark -------------------------------------------------- In the example above we create a class +Dog+ with a scripted method named +bark+. The method body defines the code, which is executed when the method is invoked. In this example, the method +bar+ prints out a line on the terminal starting with the object name (this is determined by the built in command +self+) followed by "Bark, bark, bark.". This method is defined on a class and applicable to instances of the class (here the instance +fido+). ==== C-implemented Methods Not all of the methods usable in NX are scripted methods; many predefined methods are defined in the underlying system language, which is typically C. For example, in <> we used the method +create+ to create the class +Dog+ and to create the dog instance +fido+. These methods are implemented in C in the next scripting framework. C-implemented methods are not only provided by the underlying framework but might be as well defined by application developers. This is an advanced topic, not covered here. However, application developer might reuse some generic C code to define their own C-implemented methods. Such methods are for example _accessors_, _forwarders_ and _aliases_. =========================================== An *accessor method* is a method that accesses instance variables of an object. A call to an accessor without arguments uses the accessor as a getter, obtaining the actual value of the associated variable. A call to an accessor with an argument uses it as a setter, setting the value of the associated variable. =========================================== NX provides support for C-implemented accessor methods. Accessors have already been mentioned in the section about properties. When the option +-accessor public|protected|private+ is provided to a +variable+ or +property+ definition, NX creates automatically a same-named accessors method. [[xmp-fido2]] .Listing {counter:figure-number}: Accessor Methods {set:xmp-fido2:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create Dog { :public method bark {} { puts "[self] Bark, bark, bark." } :method init {} { Tail create [self]::tail} } nx::Class create Tail { :property -accessor public {length:double 5} :public method wag {} {return Joy} } # Create an instance of the class Dog create fido # Use the accessor "length" as a getter, to obtain the value # of a property. The following call returns the length of the # tail of fido fido::tail length get # Use the accessor "length" as a setter, to alter the value # of a property. The following call changes the length of # the tail of fido fido::tail length set 10 # Proving an invalid values will raise an error fido::tail length set "Hello" -------------------------------------------------- <> shows an extended example, where every dog has a tail. The object +tail+ is created as a subobject of the dog in the constructor +init+. The subobject can be accessed by providing the full name of the subobject +fido::tail+. The method +length+ is an C-implemented accessor, that enforces the value constraint (here a floating point number, since length uses the value constraint +double+). Line 25 will therefore raise an exception, since the provided values cannot be converted to a double number. [[xmp-fido3]] .Listing {counter:figure-number}: Forwarder Methods {set:xmp-fido3:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create Dog { :public method bark {} { puts "[self] Bark, bark, bark." } :method init {} { Tail create [self]::tail :public object forward wag [self]::tail wag } } nx::Class create Tail { :property {length 5} :public method wag {} {return Joy} } # Create an instance of the class Dog create fido # The invocation of "fido wag" is delegated to "fido::tail wag". # Therefore, the following method returns "Joy". fido wag -------------------------------------------------- <> again extends the example by adding a forwarder named +wag+ to the object (e.g. +fido+). The forwarder redirects all calls of the form +fido wag+ with arbitrary arguments to the subobject +fido::tail+. =========================================== A *forwarder method* is a C-implemented method that redirects an invocation for a certain method to either a method of another object or to some other method of the same object. Forwarding an invocation of a method to some other object is a means of delegation. =========================================== The functionality of the forwarder can just as well be implemented as a scripted method, but for the most common cases, the forward implementation is more efficient, and the +forward+ method expresses the intention of the developer. The method +forwarder+ has several options to change e.g. the order of the arguments, or to substitute certain patterns in the argument list etc. This will be described in later sections. ==== Method-Aliases =========================================== An *alias method* is a means to register either an existing method, or a Tcl proc, or a Tcl command as a method with the provided name on a class or object. =========================================== In some way, the method alias is a restricted form of a forwarder, though it does not support delegation to different objects or argument reordering. The advantage of the method alias compared to a forwarder is that it has close to zero overhead, especially for aliasing c-implemented methods. [[xmp-fido4]] .Listing {counter:figure-number}: Method-Alias {set:xmp-fido4:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create Dog { :public method bark {} { puts "[self] Bark, bark, bark." } # Define a public alias for the method "bark" :public alias warn [:info method handle bark] # ... } # Create an instance of the class Dog create fido # The following line prints "::fido Bark, bark, bark." fido warn -------------------------------------------------- <> extends the last example by defining an alias for the method +bark+. The example only shows the bare mechanism. In general, method aliases are very powerful means for reusing pre-existing functionality. The full object system of NX and XOTcl2 is built from aliases, reusing functionality provided by the next scripting framework under different names. Method aliases are as well a means for implementing traits in NX. === Method Protection All kinds of methods might have different kind of protections in NX. The call-protection defines from which calling context methods might be called. The Next Scripting Framework supports as well redefinition protection for methods. NX distinguishes between +public+, +protected+ and +private+ methods, where the default call-protection is +protected+. =========================================== A *public* method can be called from every context. A *protected* method can only be invoked from the same object. A *private* method can only be invoked from methods defined on the same entity (defined on the same class or on the same object) via the invocation with the local flag (i.e. "+: -local foo+"). =========================================== All kind of method protections are applicable for all kind of methods, either scripted or C-implemented. The distinction between public and protected leads to interfaces for classes and objects. Public methods are intended for consumers of these entities. Public methods define the intended ways of providing methods for external usages (usages, from other objects or classes). Protected methods are intended for the implementor of the class or subclasses and not for public usage. The distinction between protected and public reduces the coupling between consumers and the implementation, and offers more flexibility to the developer. [[xmp-protected-method]] .Listing {counter:figure-number}: Protected Methods {set:xmp-protected-method:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create Foo { # Define a public method :public method foo {} { # .... return [:helper] } # Define a protected method :method helper {} { return 1 } } # Create an instance of the class: Foo create f1 # The invocation of the public method "foo" returns 1 f1 foo # The invocation of the protected method "helper" raises an error: f1 helper -------------------------------------------------- The example above uses +:protected method helper ...+. We could have used here as well +:method helper ...+, since the default method call-protection is already protected. The method call-protection of +private+ goes one step further and helps to hide implementation details also for implementors of subclasses. Private methods are a means for avoiding unanticipated name clashes. Consider the following example: [[xmp-private-method]] .Listing {counter:figure-number}: Private Methods {set:xmp-private-method:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create Base { :private method helper {a b} {expr {$a + $b}} :public method foo {a b} {: -local helper $a $b} } nx::Class create Sub -superclass Base { :public method bar {a b} {: -local helper $a $b} :private method helper {a b} {expr {$a * $b}} :create s1 } s1 foo 3 4 ;# returns 7 s1 bar 3 4 ;# returns 12 s1 helper 3 4 ;# raises error: unable to dispatch method helper -------------------------------------------------- The base class implements a public method +foo+ using the helper method named +helper+. The derived class implements a as well a public method +bar+, which is also using a helper method named +helper+. When an instance +s1+ is created from the derived class, the method +foo+ is invoked which uses in turn the private method of the base class. Therefore, the invocation +s1 foo 3 4+ returns its sum. If the +local+ flag had not beed used in helper, +s1+ would have tried to call the helper of +Sub+, which would be incorrect. For all other purposes, the private methods are "invisible" in all situations, e.g., when mixins are used, or within the +next+-path, etc. By using the +-local+ flag at the call site it is possible to invoke only the local definition of the method. If we would call the method without this flag, the resolution order would be the standard resolution order, starting with filters, mixins, object methods and the full intrinsic class hierarchy. NX supports the modifier +private+ for methods and properties. In all cases +private+ is an instrument to avoid unanticipated interactions and means actually "accessible for methods defined on the same entity (object or class)". The main usage for +private+ is to improve locality of the code e.g. for compositional operations. In order to improve locality for properties, a private property defines therefore internally a variable with a different name to avoid unintended interactions. The variable should be accessed via the private accessor, which can be invoked with the +-local+ flag. In the following example class +D+ introduces a private property with the same name as a property in the superclass. [[xmp-private-properties]] .Listing {counter:figure-number}: Private Properties {set:xmp-private-properties:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- # # Define a class C with a property "x" and a public accessor # nx::Class create C { :property -accessor public {x c} } # # Define a subclass D with a private property "x" # and a method bar, which is capable of accessing # the private property. # nx::Class create D -superclass C { :property -accessor private {x d} :public method bar {p} {return [: -local $p get]} } # # The private and public (or protected) properties # define internally separate variable that do not # conflict. # D create d1 puts [d1 x get] ;# prints "c" puts [d1 bar x] ;# prints "d" -------------------------------------------------- Without the +private+ definition of the property, the definition of property +x+ in class +D+ would shadow the definition of the property in the superclass +C+ for its instances (+d1 x+ or +set :x+ would return +d+ instead of +c+). === Applicability of Methods As defined above, a method is a subroutine defined on an object or class. This object (or class) contains the method. If the object (or class) is deleted, the contained methods will be deleted as well. ==== Instance Methods =========================================== Typically, methods are defined on a class, and the methods defined on the class are applicable to the instances (direct or indirect) of this class. These methods are called *instance methods*. =========================================== In the following example method, +foo+ is an instance method defined on class +C+. [[xmp-instance-applicable]] .Listing {counter:figure-number}: Methods applicable for instances {set:xmp-instance-applicable:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create C { :public method foo {} {return 1} :create c1 } # Method "foo" is defined on class "C" # and applicable to the instances of "C" c1 foo -------------------------------------------------- There are many programming languages that only allow these types of methods. However, NX also allows methods to be defined on objects. ==== Object Methods =========================================== Methods defined on objects are *object methods*. Object methods are only applicable on the object, on which they are defined. Object methods cannot be inherited from other objects. =========================================== The following example defines an object method +bar+ on the instance +c1+ of class +C+, and as well as the object specific method +baz+ defined on the object +o1+. An object method is defined via +object method+. Note that we can define a object method that shadows (redefines) for this object methods provided from classes. [[xmp-object-applicable1]] .Listing {counter:figure-number}: Object Method {set:xmp-object-applicable1:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create C { :public method foo {} {return 1} :create c1 { :public object method foo {} {return 2} :public object method bar {} {return 3} } } # Method "bar" is an object specific method of "c1" c1 bar # object-specific method "foo" returns 2 c1 foo # Method "baz" is an object specific method of "o1" nx::Object create o1 { :public object method baz {} {return 4} } o1 baz -------------------------------------------------- ==== Class Methods ========================================= A *class method* is a method defined on a class, which is only applicable to the class object itself. The class method is actually an object method of the class object. ========================================= In NX, all classes are objects. Classes are in NX special kind of objects that have e.g. the ability to create instances and to provide methods for the instances. Classes manage their instances. The general method set for classes is defined on the meta-classes (more about this later). The following example defines a public class method +bar+ on class +C+. The class method is specified by using the modifier +object+ in front of +method+ in the method definition command. [[xmp-object-applicable2]] .Listing {counter:figure-number}: Class Methods {set:xmp-object-applicable2:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create C { # # Define a class method "bar" and an instance # method "foo" # :public object method bar {} {return 2} :public method foo {} {return 1} # # Create an instance of the current class # :create c1 } # Method "bar" is a class method of class "C" # therefore applicable on the class object "C" C bar # Method "foo" is an instance method of "C" # therefore applicable on instance "c1" c1 foo # When trying to invoke the class method on the # instance, an error will be raised. c1 bar -------------------------------------------------- In some other object oriented programming languages, class methods are called "static methods". === Ensemble Methods NX provides _ensemble methods_ as a means to structure the method name space and to group related methods. Ensemble methods are similar in concept to Tcl's ensemble commands. ========================================= An *ensemble method* is a form of a hierarchical method consisting of a container method and sub-methods. The first argument of the container method is interpreted as a selector (the sub-method). Every sub-method can be an container method as well. ========================================= Ensemble methods provide a means to group related commands together, and they are extensible in various ways. It is possible to add sub-methods at any time to existing ensembles. Furthermore, it is possible to extend ensemble methods via mixin classes. The following example defines an ensemble method for +string+. An ensemble method is defined when the provide method name contains a space. [[xmp-ensemble-methods]] .Listing {counter:figure-number}: Ensemble Method {set:xmp-ensemble-methods:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create C { # Define an ensemble method "string" with sub-methods # "length", "tolower" and "info" :public method "string length" {x} {....} :public method "string tolower" {x} {...} :public method "string info" {x} {...} #... :create c1 } # Invoke the ensemble method c1 string length "hello world" -------------------------------------------------- === Method Resolution When a method is invoked, the applicable method is searched in the following order: [align="center"] ++++ Per-object Mixins -> Per-class Mixins -> Object -> Intrinsic Class Hierarchy ++++ In the case, no mixins are involved, first the object is searched for an object method with the given name, and then the class hierarchy of the object. The method can be defined multiple times on the search path, so some of these method definitions might be _shadowed_ by the more specific definitions. [[xmp-method-resolution]] .Listing {counter:figure-number}: Method Resolution with Intrinsic Classes {set:xmp-method-resolution:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create C { :public method foo {} { return "C foo: [next]" } } nx::Class create D -superclass C { :public method foo {} { return "D foo: [next]" } :create d1 { :public object method foo {} { return "d1 foo: [next]" } } } # Invoke the method foo d1 foo # result: "d1 foo: D foo: C foo: " # Query the precedence order from NX via introspection d1 info precedence # result: "::D ::C ::nx::Object" -------------------------------------------------- Consider the example in <>. When the method +foo+ is invoked on object +d1+, the object method has the highest precedence and is therefore invoked. The object methods shadows the same-named methods in the class hierarchy, namely the method +foo+ of class +D+ and the method +foo+ of class +C+. The shadowed methods can be still invoked, either via the primitive +next+ or via method handles (we used already method handles in the section about method aliases). In the example above, +next+ calls the shadowed method and add their results to the results of every method. So, the final result contains parts from +d1+, +D+ and +C+. Note, that the topmost +next+ in method +foo+ of class +C+ shadows no method +foo+ and simply returns empty (and not an error message). The introspection method +info precedence+ provides information about the order, in which classes processed during method resolution. [[xmp-method-resolution2]] .Listing {counter:figure-number}: Method Resolution with Mixin Classes {set:xmp-method-resolution2:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Class create M1 { :public method foo {} { return "M1 foo: [next]"} } nx::Class create M2 { :public method foo {} { return "M2 foo: [next]"} } # # "d1" is created based on the definitions of the last example # # Add the methods from "M1" as per-object mixin to "d1" d1 object mixins add M1 # # Add the methods from "M2" as per-class mixin to class "C" C mixins add M2 # Invoke the method foo d1 foo # result: "M1 foo: M2 foo: d1 foo: D foo: C foo: " # Query the precedence order from NX via introspection d1 info precedence # result: "::M1 ::M2 ::D ::C ::nx::Object" -------------------------------------------------- The example in <> is an extension of the previous example. We define here two additional classes +M1+ and +M2+ which are used as per-object and per-class mixins. Both classes define the method +foo+, these methods shadow the definitions of the intrinsic class hierarchy. Therefore an invocation of +foo+ on object +d1+ causes first an invocation of method in the per-object mixin. [[xmp-method-resolution3]] .Listing {counter:figure-number}: Method Invocation Flags {set:xmp-method-resolution3:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- # # "d1" is created based on the definitions of the last two examples, # the mixins "M1" and "M2" are registered. # # Define a public object method "bar", which calls the method # "foo" which various invocation options: # d1 public object method bar {} { puts [:foo] puts [: -local foo] puts [: -intrinsic foo] puts [: -system foo] } # Invoke the method "bar" d1 bar -------------------------------------------------- In the first line of the body of method +bar+, the method +foo+ is called as usual with an implicit receiver, which defaults to the current object (therefore, the call is equivalent to +d1 foo+). The next three calls show how to provide flags that influence the method resolution. The flags can be provided between the colon and the method name. These flags are used rather seldomly but can be helpful in some situations. The invocation flag +-local+ means that the method has to be resolved from the same place, where the current method is defined. Since the current method is defined as a object method, +foo+ is resolved as a object method. The effect is that the mixin definitions are ignored. The invocation flag +-local+ was already introduced int the section about method protection, where it was used to call _private_ methods. The invocation flag +-intrinsic+ means that the method has to be resolved from the intrinsic definitions, meaning simply without mixins. The effect is here the same as with the invocation flag +-local+. The invocation flag +-system+ means that the method has to be resolved from basic - typically predefined - classes of the object system. This can be useful, when script overloads system methods, but still want to call the shadowed methods from the base classes. In our case, we have no definitions of +foo+ on the base clases, therefore an error message is returned. The output of <> is: ---- M1 foo: M2 foo: d1 foo: D foo: C foo: d1 foo: D foo: C foo: d1 foo: D foo: C foo: ::d1: unable to dispatch method 'foo' ---- === Parameters NX provides a generalized mechanism for passing values to either methods (we refer to these as _method parameters_) or to objects (these are called _configure parameters_). Both kind of parameters might have different features, such as: - Positional and non-positional parameters - Required and non-required parameters - Default values for parameters - Value-checking for parameters - Multiplicity of parameters TODO: complete list above and provide a short summary of the section Before we discuss method and configure parameters in more detail, we describe the parameter features in the subsequent sections based on method parameters. ==== Positional and Non-Positional Parameters If the position of a parameter in the list of formal arguments (e.g. passed to a function) is significant for its meaning, this is a _positional_ parameter. If the meaning of the parameter is independent of its position, this is a _non-positional_ parameter. When we call a method with positional parameters, the meaning of the parameters (the association with the argument in the argument list of the method) is determined by its position. When we call a method with non-positional parameters, their meaning is determined via a name passed with the argument during invocation. [[xmp-posnonpos]] .Listing {counter:figure-number}: Positional and Non-Positional Method Parameters {set:xmp-posnonpos:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Object create o1 { # # Method foo has positional parameters: # :public object method foo {x y} { puts "x=$x y=$y" } # # Method bar has non-positional parameters: # :public object method bar {-x -y} { puts "x=$x y=$y" } # # Method baz has non-positional and # positional parameters: # :public object method baz {-x -y a} { puts "x? [info exists x] y? [info exists y] a=$a" } } # invoke foo (positional parameters) o1 foo 1 2 # invoke bar (non-positional parameters) o1 bar -y 3 -x 1 o1 bar -x 1 -y 3 # invoke baz (positional and non-positional parameters) o1 baz -x 1 100 o1 baz 200 o1 baz -- -y -------------------------------------------------- Consider the example in <>. The method +foo+ has the argument list +x y+. This means that the first argument is passed in an invocation like +o1 foo 1 2+ to +x+ (here, the value +1+), and the second argument is passed to +y+ (here the value +2+). Method +bar+ has in contrary just with non-positional arguments. Here we pass the names of the parameter together with the values. In the invocation +o1 bar -y 3 -x 1+ the names of the parameters are prefixed with a dash ("-"). No matter whether in which order we write the non-positional parameters in the invocation (see line 30 and 31 in <>) in both cases the variables +x+ and +y+ in the body of the method +bar+ get the same values assigned (+x+ becomes +1+, +y+ becomes +3+). It is certainly possible to combine positional and non-positional arguments. Method +baz+ provides two non-positional parameter (+-y+ and +-y+) and one positional parameter (namely +a+). The invocation in line 34 passes the value of +1+ to +x+ and the value of +100+ to +a+. There is no value passed to +y+, therefore value of +y+ will be undefined in the body of +baz+, +info exists y+ checks for the existence of the variable +y+ and returns +0+. The invocation in line 35 passes only a value to the positional parameter. A more tricky case is in line 36, where we want to pass +-y+ as a value to the positional parameter +a+. The case is more tricky since syntactically the argument parser might consider +-y+ as the name of one of the non-positional parameter. Therefore we use +--+ (double dash) to indicate the end of the block of the non-positional parameters and therefore the value of +-y+ is passed to +a+. ==== Optional and Required Parameters Per default positional parameters are required, and non-positional parameters are optional (they can be left out). By using parameter options, we can as well define positional parameters, which are optional, and non-positional parameters, which are required. [[xmp-optional-req]] .Listing {counter:figure-number}: Optional and Required Method Parameters {set:xmp-optional-req:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Object create o2 { # # Method foo has one required and one optional # positional parameter: # :public object method foo {x:required y:optional} { puts "x=$x y? [info exists y]" } # # Method bar has one required and one optional # non-positional parameter: # :public object method bar {-x:required -y:optional} { puts "x=$x y? [info exists y]" } } # invoke foo (one optional positional parameter is missing) o2 foo 1 -------------------------------------------------- The example in <> defined method +foo+ with one required and one optional positional parameter. For this purpose we use the parameter options +required+ and +optional+. The parameter options are separated from the parameter name by a colon. If there are multiple parameter options, these are separated by commas (we show this in later examples). The parameter definition +x:required+ for method +foo+ is equivalent to +x+ without any parameter options (see e.g. previous example), since positional parameters are per default required. The invocation in line 21 of <> will lead to an undefined variable +y+ in method +foo+, because no value us passed to the optional parameter. Note that only trailing positional parameters might be optional. If we would call method +foo+ of <> with only one argument, the system would raise an exception. Similarly, we define method +bar+ in <> with one required and one optional non-positional parameter. The parameter definition +-y:optional+ is equivalent to +-y+, since non-positional parameter are per default optional. However, the non-positional parameter +-x:required+ is required. If we invoke +bar+ without it, the system will raise an exception. ==== Default Values for Parameters Optional parameters might have a default value, which will be used, when not value is provided for this parameter. Default values can be specified for positional and non-positional parameters. [[xmp-default-value]] .Listing {counter:figure-number}: Method Parameters with Default Values {set:xmp-default-value:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Object create o3 { # # Positional parameter with default value: # :public object method foo {{x 1} {y 2}} { puts "x=$x y=$y" } # # Non-positional parameter with default value: # :public object method bar {{-x 10} {-y 20}} { puts "x=$x y=$y" } } # use default values o3 foo o3 bar -------------------------------------------------- In order to define a default value for a parameter, the parameter specification must be of the form of a 2 element list, where the second argument is the default value. See for an example in <>. ==== Value Constraints NX provides value constraints for all kind of parameters. By specifying value constraints a developer can restrict the permissible values for a parameter and document the expected values in the source code. Value checking in NX is conditional, it can be turned on or off in general or on a per-usage level (more about this later). The same mechanisms can be used not only for input value checking, but as well for return value checking (we will address this point as well later). ===== Built-in Value Constraints NX comes with a set of built-in value constraints, which can be extended on the scripting level. The built-in checkers are either the native checkers provided directly by the Next Scripting Framework (the most efficient checkers) or the value checkers provided by Tcl through +string is ...+. The built-in checkers have as well the advantage that they can be used also at any time during bootstrap of an object system, at a time, when e.g. no objects or methods are defined. The same checkers are used as well for all C-implemented primitives of NX and the Next Scripting Framework. [[img-value-checkers]] image::value-checkers.png[align="center",title="General Applicable Value Checkers in NX"] {set:img-value-checkers:Figure {figure-number}} <> shows the built-in general applicable value checkers available in NX, which can be used for all method and configure parameters. In the next step, we show how to use these value-checkers for checking permissible values for method parameters. Then we will show, how to provide more detailed value constraints. [[xmp-value-check]] .Listing {counter:figure-number}: Method Parameters with Value Constraints {set:xmp-value-check:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Object create o4 { # # Positional parameter with value constraints: # :public object method foo {x:integer o:object,optional} { puts "x=$x o? [info exists o]" } # # Non-positional parameter with value constraints: # :public object method bar {{-x:integer 10} {-verbose:boolean false}} { puts "x=$x verbose=$verbose" } } # The following invocation raises an exception, since the # value "a" for parameter "x" is not an integer o4 foo a -------------------------------------------------- Value contraints are specified as parameter options in the parameter specifications. The parameter specification +x:integer+ defines +x+ as a required positional parmeter which value is constraint to an integer. The parameter specification +o:object,optional+ shows how to combine multiple parameter options. The parameter +o+ is an optional positional parameter, its value must be an object (see <>). Value constraints are specified exactly the same way for non-positional parameters (see method +bar+ in <>). [[xmp-check-parameterized]] .Listing {counter:figure-number}: Parameterized Value Constraints {set:xmp-check-parameterized:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- # # Create classes for Person and Project # nx::Class create Person nx::Class create Project nx::Object create o5 { # # Parameterized value constraints # :public object method work { -person:object,type=Person -project:object,type=Project } { # ... } } # # Create a Person and a Project instance # Person create gustaf Project create nx # # Use method with value constraints # o5 work -person gustaf -project nx -------------------------------------------------- The native checkers +object+, +class+, +metaclass+ and +baseclass+ can be further specialized with the parameter option +type+ to restrict the permissible values to instances of certain classes. We can use for example the native value constraint +object+ either for testing whether an argument is some object (without further constraints, as in <>, method +foo+), or we can constrain the value further to some type (direct or indirect instance of a class). This is shown by method +work+ in <> which requires the parameter +-person+ to be an instance of class +Person+ and the parameter +-project+ to be an instance of class +Project+. ===== Scripted Value Constraints The set of predefined value checkers can be extended by application programs via defining methods following certain conventions. The user defined value checkers are defined as methods of the class +nx::Slot+ or of one of its subclasses or instances. We will address such cases in the next sections. In the following example we define two new value checkers on class +nx::Slot+. The first value checker is called +groupsize+, the second one is called +choice+. [[xmp-user-types]] .Listing {counter:figure-number}: Scripted Value Checker for Method Parameters {set:xmp-user-types:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- # # Value checker named "groupsize" # ::nx::Slot method type=groupsize {name value} { if {$value < 1 || $value > 6} { error "Value '$value' of parameter $name is not between 1 and 6" } } # # Value checker named "choice" with extra argument # ::nx::Slot method type=choice {name value arg} { if {$value ni [split $arg |]} { error "Value '$value' of parameter $name not in permissible values $arg" } } # # Create an application class D # using the new value checkers # nx::Class create D { :public method foo {a:groupsize} { # ... } :public method bar {a:choice,arg=red|yellow|green b:choice,arg=good|bad} { # ... } } D create d1 # testing "groupsize"; # the second call (with value 10) will raise an exception: d1 foo 2 d1 foo 10 # testing "choice" # the second call (with value pink for parameter a) # will raise an exception: d1 bar green good d1 bar pink bad -------------------------------------------------- In order to define a checker +groupsize+ a method of the name +type=groupsize+ is defined. This method receives two arguments, +name+ and +value+. The first argument is the name of the parameter (mostly used for the error message) and the second parameter is provided value. The value checker simply tests whether the provided value is between 1 and 3 and raises an exception if this is not the case (invocation in line 36 in <>). The checker +groupsize+ has the permissible values defined in its method's body. It is as well possible to define more generic checkers that can be parameterized. For this parameterization, one can pass an argument to the checker method (last argument). The checker +choice+ can be used for restricting the values to a set of predefined constants. This set is defined in the parameter specification. The parameter +a+ of method +bar+ in <> is restricted to the values +red+, +yellow+ or +green+, and the parameter +b+ is restricted to +good+ or +bad+. Note that the syntax of the permissible values is solely defined by the definition of the value checker in lines 13 to 17. The invocation in line 39 will be ok, the invocation in line 40 will raise an exception, since +pink+ is not allowed. If the same checks are used in many places in the program, defining names for the value checker will be the better choice since it improves maintainability. For seldomly used kind of checks, the parameterized value checkers might be more convenient. ==== Multiplicity ***************************************************************************** *Multiplicity* is used to define whether a parameter should receive single or multiple values. ***************************************************************************** A multiplicity specification has a lower and an upper bound. A lower bound of +0+ means that the value might be empty. A lower bound of +1+ means that the parameter needs at least one value. The upper bound might be +1+ or +n+ (or synonymously +*+). While the upper bound of +1+ states that at most one value has to be passed, the upper bound of +n+ says that multiple values are permitted. Other kinds of multiplicity are currently not allowed. The multiplicity is written as parameter option in the parameter specification in the form _lower-bound_.._upper-bound_. If no multiplicity is defined the default multiplicity is +1..1+, which means: provide exactly one (atomic) value (this was the case in the previous examples). [[xmp-multiplicity]] .Listing {counter:figure-number}: Method Parameters with Explicit Multiplicity {set:xmp-multiplicity:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- nx::Object create o6 { # # Positional parameter with an possibly empty # single value # :public object method foo {x:integer,0..1} { puts "x=$x" } # # Positional parameter with an possibly empty # list of values value # :public object method bar {x:integer,0..n} { puts "x=$x" } # # Positional parameter with a non-empty # list of values # :public object method baz {x:integer,1..n} { puts "x=$x" } } -------------------------------------------------- <> contains three examples for positional parameters with different multiplicities. Multiplicity is often combined with value constraints. A parameter specification of the form +x:integer,0..n+ means that the parameter +x+ receives a list of integers, which might be empty. Note that the value constraints are applied to every single element of the list. The parameter specification +x:integer,0..1+ means that +x+ might be an integer or it might be empty. This is one style of specifying that no explicit value is passed for a certain parameter. Another style is to use required or optional parameters. NX does not enforce any particular style for handling unspecified values. All the examples in <> are for single positional parameters. Certainly, multiplicity is fully orthogonal with the other parameter features and can be used as well for multiple parameters, non-positional parameter, default values, etc. == Advanced Language Features ... === Objects, Classes and Meta-Classes ... === Resolution Order and Next-Path ... === Details on Method and Configure Parameters The parameter specifications are used in NX for the following purposes. They are used for - the specification of input arguments of methods and commands, for - the specification of return values of methods and commands, and for - the specification for the initialization of objects. We refer to the first two as method parameters and the last one as configure parameters. The examples in the previous sections all parameter specification were specifications of method parameters. ***************************************************************************** *Method parameters* specify properties about permissible values passed to methods. ***************************************************************************** The method parameter specify how methods are invoked, how the actual arguments are passed to local variables of the invoked method and what kind of checks should be performed on these. ***************************************************************************** *Configure parameters* are parameters that specify, how objects can be parameterized upon creation. ***************************************************************************** Syntactically, configure parameters and method parameters are the same, although there are certain differences (e.g. some parameter options are only applicable for objects parameters, the list of object parameters is computed dynamically from the class structures, object parameters are often used in combination with special setter methods, etc.). Consider the following example, where we define the two application classes +Person+ and +Student+ with a few properties. [[xmp-object-parameters]] .Listing {counter:figure-number}: Configure Parameters {set:xmp-object-parameters:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- # # Define a class Person with properties "name" # and "birthday" # nx::Class create Person { :property name:required :property birthday } # # Define a class Student as specialization of Person # with and additional property # nx::Class create Student -superclass Person { :property matnr:required :property {oncampus:boolean true} } # # Create instances using configure parameters # for the initialization # Person create p1 -name Bob Student create s1 -name Susan -matnr 4711 # Access property value via "cget" method puts "The name of s1 is [s1 cget -name]" -------------------------------------------------- The class +Person+ has two properties +name+ and +birthday+, where the property +name+ is required, the property +birthday+ is not. The class +Student+ is a subclass of +Person+ with the additional required property +matnr+ and an optional property +oncampus+ with the default value +true+ (see <>). The class diagram below visualizes these definitions. [[img-configure-parameters]] image::configure-parameter.png[align="center",title="System and Application Classes"] {set:img-configure-parameters:Figure {figure-number}} In NX, these definitions imply that instances of the class of +Person+ have the properties +name+ and +birthday+ as _non-positional object parameters_. Furthermore it implies that instances of +Student+ will have the configure parameters of +Person+ augmented with the object parameters from +Student+ (namely +matnr+ and +oncampus+). Based on these configure parameters, we can create a +Person+ named +Bob+ and a +Student+ named +Susan+ with the matriculation number +4711+ (see line 23 and 24 in <>). After the object +s1+ is created it has the instance variables +name+, +matnr+ and +oncampus+ (the latter is initialized with the default value). ==== Configure Parameters available for all NX Objects The configure parameters are not limited to the application defined properties, also NX provides some predefined definitions. Since +Person+ is a subclass of +nx::Object+ also the configure parameters of +nx::Object+ are inherited. In the introductory stack example, we used +-mixins+ applied to an object to denote per-object mixins (see <>). Since +mixins+ is defined as a parameter on +nx::Object+ it can be used as an object parameter +-mixins+ for all objects in NX. To put it in other words, every object can be configured to have per-object mixins. If we would remove this definition, this feature would be removed as well. As shown in the introductory examples, every object can be configured via a scripted initialization block (the optional scripted block specified at object creation as last argument; see <> or <>). The scripted block and its meaning are as well defined by the means of configure parameters. However, this configure parameter is positional (last argument) and optional (it can be omitted). The following listing shows the configure parameters of +Person p1+ and +Student s1+. [[xmp-object-parameter-list]] .Listing {counter:figure-number}: Computed Actual Configure Parameter {set:xmp-object-parameter-list:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- Configure parameters for Person p1: Command: p1 info lookup syntax configure Result: -name /value/ ?-birthday /value/? ?-object-mixins /mixinreg .../? ?-class /class/? ?-object-filters /filterreg .../? ?/__initblock/? Configure parameter for Student s1: Command: s1 info lookup syntax configure Result: ?-oncampus /boolean/? -matnr /value/ -name /value/ ?-birthday /value/? ?-object-mixins /mixinreg .../? ?-class /class/? ?-object-filters /filterreg .../? ?/__initblock/? -------------------------------------------------- The given paramter show, how (a) objects can be configured at runtime or (b) how new instances can be configured at creation time via the +new+ or +create+ methods. Introspection can be used to obtain the configuration parameters from an object via +p1 info lookup parameters configure+ (returning the configure parameters currently applicable for +configure+ or +cget+) or from a class +Person info lookup parameters create+ on a class (returning the configure parameters applicable when an object of this class is created) The listed configure parameter types +mixinreg+ and +filterreg+ are for converting definitions of filters and mixins. The last value +__initblock+ says that the content of this variable will be executed in the context of the object being created (before the constructor +init+ is called). More about the configure parameter types later. ==== Configure Parameters available for all NX Classes Since classes are certain kind of objects, classes are parameterized in the same way as objects. A typical parameter for a class definition is the relation of the class to its superclass.In our example, we have specified, that +Student+ has +Person+ as superclass via the non-positional configure parameter +-superclass+. If no superclass is specified for a class, the default superclass is +nx::Object+. Therefore +nx::Object+ is the default value for the parameter +superclass+. Another frequently used parameter for classes is +-mixins+ to denote per-class mixins (see e.g. the introductory Stack example in <>), which is defined in the same way. Since +Student+ is an instance of the meta-class +nx::Class+ it inherits the configure parameters from +nx::Class+ (see class diagram <>). Therefore, one can use e.g. +-superclass+ in the definition of classes. Since +nx::Class+ is a subclass of +nx::Object+, the meta-class +nx::Class+ inherits the parameter definitions from the most general class +nx::Object+. Therefore, every class might as well be configured with a scripted initialization block the same way as objects can be configured. We used actually this scripted initialization block in most examples for defining the methods of the class. The following listing shows (simplified) the parameters applicable for +Class Student+. [[xmp-class-parameter-list]] .Listing {counter:figure-number}: Parameters for Classes {set:xmp-class-parameter-list:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- Configure parameter for class nx::Class Command: nx::Class info lookup syntax configure Result: ?-superclass /class .../? ?-mixins /mixinreg .../? ?-filters /filterreg .../? ?-object-mixins /mixinreg .../? ?-class /class/? ?-object-filters /filterreg .../? ?/__initblock/? -------------------------------------------------- ==== User defined Parameter Types More detailed definition of the configure parameter types comes here. ==== Slot Classes and Slot Objects In one of the previous sections, we defined scripted (application defined) checker methods on a class named +nx::Slot+. In general NX offers the possibility to define value checkers not only for all usages of parameters but as well differently for method parameters or configure parameters [[img-slots]] image::slots.png[align="center",title="Slot Classes and Objects"] {set:img-slots:Figure {figure-number}} ==== Attribute Slots Still Missing - return value checking - switch - initcmd ... - subst rules - converter - incremental slots == Miscellaneous ... === Profiling ... === Unknown Handlers NX provides two kinds of unknown handlers: - Unknown handlers for methods - Unknown handlers for objects and classes ==== Unknown Handlers for Methods Object and classes might be equipped with a method +unknown+ which is called in cases, where an unknown method is called. The method unknown receives as first argument the called method followed by the provided arguments [[xmp-unknown-method]] .Listing {counter:figure-number}: Unknown Method Handler {set:xmp-unknown-method:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- ::nx::Object create o { :object method unknown {called_method args} { puts "Unknown method '$called_method' called" } } # Invoke an unknown method for object o: o foo 1 2 3 # Output will be: "Unknown method 'foo' called" -------------------------------------------------- Without any provision of an unknown method handler, an error will be raised, when an unknown method is called. ==== Unknown Handlers for Objects and Classes The next scripting framework provides in addition to unknown method handlers also a means to dynamically create objects and classes, when these are referenced. This happens e.g. when superclasses, mixins, or parent objects are referenced. This mechanism can be used to implement e.g. lazy loading of these classes. Nsf allows to register multiple unknown handlers, each identified by a key (a unique name, different from the keys of other unknown handlers). [[xmp-unknown-class]] .Listing {counter:figure-number}: Unknown Class Handler {set:xmp-unknown-class:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- ::nx::Class public object method __unknown {name} { # A very simple unknown handler, showing just how # the mechanism works. puts "***** __unknown called with <$name>" ::nx::Class create $name } # Register an unknown handler as a method of ::nx::Class ::nsf::object::unknown::add nx {::nx::Class __unknown} ::nx::Object create o { # The class M is unknown at this point :object mixins add M # The line above has triggered the unknown class handler, # class M is now defined puts [:info object mixins] # The output will be: # ***** __unknown called with <::M> # ::M } -------------------------------------------------- The Next Scripting Framework allows to add, query, delete and list unknown handlers. [[xmp-unknown-registration]] .Listing {counter:figure-number}: Unknown Handler registration {set:xmp-unknown-registration:Listing {figure-number}} [source,tcl,numbers] -------------------------------------------------- # Interface for unknown handlers: # nsf::object::unknown::add /key/ /handler/ # nsf::object::unknown::get /key/ # nsf::object::unknown::delete /key/ # nsf::object::unknown::keys -------------------------------------------------- [bibliography] .References - [[Zdun, Strembeck, Neumann 2007]] U. Zdun, M. Strembeck, G. Neumann: Object-Based and Class-Based Composition of Transitive Mixins, Information and Software Technology, 49(8) 2007 . - [[Neumann and Zdun 1999a]] G. Neumann and U. Zdun: Filters as a language support for design patterns in object-oriented scripting languages. In Proceedings of COOTS'99, 5th Conference on Object-Oriented Technologies and Systems, San Diego, May 1999. - [[Neumann and Zdun 1999b]] G. Neumann and U. Zdun: Implementing object-specific design patterns using per-object mixins. In Proc. of NOSA`99, Second Nordic Workshop on Software Architecture, Ronneby, Sweden, August 1999. - [[Neumann and Zdun 1999c]] G. Neumann and U. Zdun: Enhancing object-based system composition through per-object mixins. In Proceedings of Asia-Pacific Software Engineering Conference (APSEC), Takamatsu, Japan, December 1999. - [[Neumann and Zdun 2000a]] G. Neumann and U. Zdun: XOTCL, an object-oriented scripting language. In Proceedings of Tcl2k: The 7th USENIX Tcl/Tk Conference, Austin, Texas, February 2000. - [[Neumann and Zdun 2000b]] G. Neumann and U. Zdun: Towards the Usage of Dynamic Object Aggregations as a Form of Composition In: Proceedings of Symposium of Applied Computing (SAC'00), Como, Italy, Mar 19-21, 2000. - [[Neumann and Sobernig 2009]] G. Neumann, S. Sobernig: XOTcl 2.0 - A Ten-Year Retrospective and Outlook, in: Proceedings of the Sixteenth Annual Tcl/Tk Conference, Portland, Oregon, October, 2009. - [[Ousterhout 1990]] J. K. Ousterhout: Tcl: An embeddable command language. In Proc. of the 1990 Winter USENIX Conference, January 1990. - [[Ousterhout 1998]] J. K. Ousterhout: Scripting: Higher Level Programming for the 21st Century, IEEE Computer 31(3), March 1998. - [[Wetherall and Lindblad 1995]] D. Wetherall and C. J. Lindblad: Extending Tcl for Dynamic Object-Oriented Programming. Proc. of the Tcl/Tk Workshop '95, July 1995. doc/next-tutorial/object-class-appclass.graffle000066400000000000000000000530021242365656200221620ustar00rootroot00000000000000 ActiveLayerIndex 0 ApplicationVersion com.omnigroup.OmniGrafflePro 139.7.0.167456 AutoAdjust BackgroundGraphic Bounds {{0, 0}, {559, 783}} Class SolidGraphic ID 2 Style shadow Draws NO stroke Draws NO BaseZoom 0 CanvasOrigin {0, 0} ColumnAlign 1 ColumnSpacing 36 CreationDate 2009-12-29 15:06:59 +0000 Creator Gustaf A. Neumann DisplayScale 1.000 cm = 1.000 cm GraphDocumentVersion 8 GraphicsList Bounds {{294.22932457169577, 352.66014462624321}, {75, 24}} Class ShapedGraphic ID 9 Line ID 8 Position 0.34574294090270996 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 7 ID 8 Points {328.65168972523446, 336.346} {329, 372} {289, 388} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 4 Bounds {{244, 388}, {90, 36}} Class ShapedGraphic ID 7 Magnets {0, 1} {0, -1} {1, 0} {-1, 0} Shape Rectangle Style Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 s1: Stack} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 1 ID 6 Points {318.49562705626812, 238.346} {298.67755225194736, 138} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 4 Bounds {{175.8580453437697, 281.61092918333759}, {75, 24}} Class ShapedGraphic ID 10 Line ID 5 Position 0.52006572484970093 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 4 ID 5 Points {190, 239.57152252252251} {233, 255} {206, 312} {260.173, 301.06811536100446} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 14 Class TableGroup Graphics Bounds {{260.173, 238.346}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 11 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 Stack} VerticalPad 0 TextPlacement 0 Bounds {{260.173, 252.346}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 12 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 \ } VerticalPad 0 TextPlacement 0 Bounds {{260.173, 280.346}, {136, 56}} Class ShapedGraphic FitText Vertical Flow Resize ID 13 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 init\ push\ pop\ } VerticalPad 0 TextPlacement 0 GridH 11 12 13 GroupConnect YES ID 4 AllowLabelDrop Class LineGraphic Head ID 1 ID 3 Points {190, 163.76146014560317} {224.07365923802806, 138} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 14 Class TableGroup Graphics Bounds {{54, 159.173}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 15 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::Class} VerticalPad 0 TextPlacement 0 Bounds {{54, 173.173}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 16 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 \ } VerticalPad 0 TextPlacement 0 Bounds {{54, 201.173}, {136, 70}} Class ShapedGraphic FitText Vertical Flow Resize ID 17 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 variable\ create\ method\ ...\ } VerticalPad 0 TextPlacement 0 GridH 15 16 17 GroupConnect YES ID 14 Class TableGroup Graphics Bounds {{221, 40}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 18 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::Object} VerticalPad 0 TextPlacement 0 Bounds {{221, 54}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 19 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 \ } VerticalPad 0 TextPlacement 0 Bounds {{221, 82}, {136, 56}} Class ShapedGraphic FitText Vertical Flow Resize ID 20 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 info\ destroy\ ...\ } VerticalPad 0 TextPlacement 0 GridH 18 19 20 GroupConnect YES ID 1 GridInfo GridSpacing 14.17322826385498 MajorGridSpacing 10 GuidesLocked NO GuidesVisible YES HPages 1 ImageCounter 1 KeepToScale Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo Animate NO circoMinDist 18 circoSeparation 0.0 layoutEngine dot neatoSeparation 0.0 twopiSeparation 0.0 LinksVisible NO MagnetsVisible NO MasterSheets ModificationDate 2013-05-04 13:20:30 +0000 Modifier Gustaf A. Neumann NotesVisible NO Orientation 2 OriginVisible NO PageBreaks YES PrintInfo NSBottomMargin float 18 NSHorizonalPagination int 0 NSJobFeatures coded BAtzdHJlYW10eXBlZIHoA4QBQISEhBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCEhAhOU09iamVjdACFhAFpAIY= NSLeftMargin float 18 NSPagesPerSheet int 1 NSPaperSize size {595, 842} NSPrintAllPages int 0 NSPrintReverseOrientation int 0 NSReversePageOrder int 0 NSRightMargin float 18 NSTopMargin float 41 PrintOnePage ReadOnly NO RowAlign 1 RowSpacing 36 SheetTitle Canvas 1 SmartAlignmentGuidesActive YES SmartDistanceGuidesActive YES UniqueID 1 UseEntirePage VPages 1 WindowInfo CurrentSheet 0 ExpandedCanvases Frame {{522, 231}, {675, 756}} ListView OutlineWidth 142 RightSidebar Sidebar SidebarWidth 138 VisibleRegion {{1, 1}, {537, 662}} Zoom 1 ZoomValues Canvas 1 1 1 doc/next-tutorial/object-class-appclass.png000066400000000000000000000610761242365656200213520ustar00rootroot00000000000000‰PNG  IHDR¨®åð pHYs  šœÕiTXtXML:com.adobe.xmp 5 2 1 m ž@IDATxí]¸EÖ½’$'AÉ—¨bBAôÅ1€(†EÔ5`ÞETL˜×ÈŠ«®pQI b@PWTW]Q‚(ŠŠ‚äû¿§˜ší7o潉==Ýç~ßêP]áTÕ©ê[Õ5{8Ž#"@ˆe•]æ–"@€ÉŸõ€"BHþ!,tf™"@òg D€„’ Y&D€üYˆ !D€äÂBg–‰ $Ö"@ˆ@ ù‡°Ð™e"@ˆÉŸu€"BHþ!,tf™"PŽ" ²‡ q>º—73‹óÄ"ø>Ì9L@ê–è­fˆÂœ÷ØŽ z†N‚#ÿ0Wýç=éÃÄiÍœ }«v‰`»Q‚wes—ãzTí‹`;’¿«ð0pX‚·Äë"Ãî!p0C áC,ñÃu+:‚]è‚ÚüQü”@!õ[â/«™³ºN9ÂTig53 üª;"Šíô0XBòVy27ÿC$ÒG‡[A5/s\?ÿü³,[¶Lš6m*uêÔÑd$'›6m2+W®œÜô•‘ACy Ävê±£×Ëèè@ Ä¾ò*SÌLxˆ3êùƒø+zÊçŸ.;w–FÉA$uëÖ•O2oÞ<éß¿¿<ûì³rÔQGÉ믿.çž{®) MÊ®]»`SŽ[`C† ‘ë®».î½d/¾øâ‹rýõ×'ëþDðšeÉß¾5Ú7@áCòTq23ÐXQ·aîÁnOÕ¸¶“o¾ùFŽ8â5j”ôíÛWÚ´i#C‡5„üä“Oš{vd>hÐ éÞ½»üøãœÈĉeøðá²zõêbçÿüç?å‡~Ë.»L^xá9çœsäwÞ‘®]» Fü|ðy?ß}÷{ì±Ò¢E ¹á†L‡€ë³fÍ’™3gâP¶oß.Æ “fÍšIÛ¶meĈæºý9r¤tèÐA:vìhÒ¿råJyê©§ä½÷Þ3^zõê%Û¶m³Þé&Fõõõõu /”Áë0ê ƒ ÔHcEÃE#ÞKµ±j[ÕCT5›Eåã?ưÛ)_¾¼Ó­[7§^½zæ\I×QRwöÙgGÍ5ΤI“Ìõ‹/¾8ÀÙgŸm®-^¼Ø\sŸ<ØÜ›:ujÔ?nºé&sýñÇwÞ|óMs\®\9§gÏžNãÆÍùøñãÍ3j2r´C0Ç·Ür‹¹wÜqÇ9úaŽÿö·¿™{cÇŽ5çJþN=Ì1Ò2eʧuëÖæüÊ+¯t”üþÄGõ ROþ .êMmÕ*ª¨Of¾HŸ _&#A*æ%ý¦#~¼¶WUÝ[µ™jGÕ#U‹µzKþýúõ3÷æÌ™cÈò¯ý«9·ÄZ¡B§~ýúΚ5k¢aèèÝyî¹çœõë×›kîsµí›ptôõƒqãÆ™ë sKþþóŸŸ¥K—:eÊ”qþô§?™s7ù7lØÐiÙ²¥³qãF:%}0þРóZµj•9¿ä’Kt>59™øÌ JDõCµ»*&ZPoêªVSE} ùæE!AD¯éèPÇ1rƒ&”ý÷ßßÜ«]ƒ=‘Í›7÷Ì3Ï”;î¸ÃØî¯¹æ©Q+w &d¡VÜçXÙÑÎDš4ibŽñ£9Þwß}£×0 Áİ’¼1÷DoêL>¿üò‹¨U©‚ènÑŽÈ|ÿý÷æÙZµj™óG}4âƒNÀäƒ:“uu ŠÎ!0‚ŒQˆ@Ð@½¶.3uBÁª›x›ù×_mn=ñIJuëÖxÞŠ]ƒí2mÚ´"÷¦OŸnÎí}œ|ûí·æ–vb%ûnè¨^ªU«&íÚµ“E‹Eõ«¯¾2ÏaÀv¸ f#yþùçÍ=þ¤Œ –ü-?‚ø'6sË3zÐ`Q¿í :%Ù²e‹¨ùDªW¯nVÌ,\¸°ÈD«Úï¥K—.‚uü÷9Vô´jÕJF-W\q…Œ3FN=õTùâ‹/ÌÄòM &fѱè|‚åsÌ1Ñ{ö@mùæíÅ_~ù¥èü„¨éÈÜ>á„̤óˆ#DÍT2`À™1c†¹§f$ã¾ûî»æÍÁœð§$PO Hâ×|#(íþšÄQ£f¨ËS«4,–uÖTm¤ÚNõÕST‹Ù|­Íÿ¾ûî3÷tdmläºÌÒÑ%’æøÞ{ïutÄï4oÞÜØÖçÎküº'xq!öþ”äMˆúÇ?þÑùí·ßÌóÖæß»woG?ä2÷Õtd&šáÁmó×FÆ~_©R%ã¯}ûöŽvF&œåË—;Ú EãÁ½%K–˜{ºÔÓiР¹§oæâ#)#Ô“nª¨7¨?¨G•Tñ¡W ÚV^¾xT )D 'èŠD¾°uç΢TÔ¿=ÀrT;'`¯Ñ-Š@ÏSõê U¬ßŤÊFÕ-ª[ã@}å‹× :h´y{}¡»I=غš¨ˆL2c®În + .÷$rì}," ÏQO Þ&)77Hþ¹Á•¡æk)ÈÆŒÑ;>ÜÂ<ÅSÜõ¶ ù{Z¯™G ÁÚ‘¾%DMºìu’’Y4—^zifðéT°õ¦C{Œú(s„äo‘ $ìhÍ}´!çÃæ$`ƒœ—ÈÀÀÖ›U[wìy`\ôp"$lã l£ Raù0/¶þ¸Gÿöš“›~’HþécÇ'ý‹Fü¨ÛpÑpqÈWwÍ%»¸ë êûí1»1å94’ž €Ñç÷ˆÍ6âœFÈÀ ;X° ì äo‹˜nÐp?ŽIþA+aæ'#HþÁLJ}ˆ@"’ww>L6“ä#ÜuõÉ}î£df–’føñi"€ÆjÕ¦0 ØfŽnÖp×t‰YØ«Iþ^!Íx¼DÀÖkKøpí5/ÓÁ¸ ÔÀ?Š… ¢ð+'sÀ¾®ÇÏ.¯f¼n ’å¼Ä Žä^ ¶äÈ- eä·lغn´oŽöšßÒšQzHþÁLJ}ˆ€m¨pí1’i²“Ì$ùÔ[WP,Gºë’’›~RlÆÒO"`k¬ëÏÔ2U~Aõ%u†äï—*Çt"@ý©&fŸT£¿#`oRvöìÙÒ¦M›Ró4eÊÙo¿ýdܸq¥ú¥‡‚FÀÖ÷èß^+èŒÅ&žä‹σ€Fü¨ÛpÑpq÷Õý€3fèí’å•W^‘ÁƒËŸþô§’=òn¡#à®+¨?î·ÇBÏ[‘ô#£"TÜ#6Ûˆ‹äuÑ¢Eò—¿üÅ\{àdÔ¨Qrúé§KÆ ¥_¿~²eËyê©§dêÔ©Æ}þùçe×®]rã7Jƒ Œ¿#FH&æ¤" âI¾°ƒ›Ž¸ƒ{³]Úü ¹ô˜ö’p?Žã’ÿúõëå³Ï>3á,]ºTž~úiCò—_~¹œ{î¹2~üxéÛ·¯¼öÚkÒ¹sgéÝ»·é ¦M›&sæÌ‘M›6IŸ>}¤Q£FrÞyç•”Þ#¾B€#_“â’¼†ëî FsÆgÈ©§ž*Ý»w7¤>þ|©U«–T«VMêÖ­+5kÖ”þóŸrÉ%—HãÆ¥uëÖré¥—Ê /¼0LÞ(8ÜuõÉ}^p™I”`’"dx½@cµjó‘T†)ÇJ•*UÌê{nÝ%K–È!‡bO¥yóæ²bÅŠè9€»þ H4¨(ØÌ’ü ¶è˜ð°õÚ>\{­„ÇÔS™Ò½µk×N~úé§h8 ,Ã;,z΃@!€º8âG •^ÓUŽÌLˆÈÙë:ìþcÆŒ1“Á˜èÅdpÏž=Cm(²Š‰Þ@’¾-=’¿E‚n°@VñÀåÓO?•-ZHÛ¶m¥R¥JrÜqÇ¿0æÉÖp£}s´×…Ç\¢¨ò }fô+Ͳ ByÕJªø·ºê^ªuT'f«¾ãk_,­^½ºYò©aS ÈΧj60³Zu­êFÕͪ۴îìT70Â¥ž)Jf$;Z‹uc¼¥wZ¶lÙ¤¾ N/t>•GP_rRgò˜§¸QÓì^$D€’°Ë—¹#D€ÄE€ä^ 3Û¶m sö™÷ @òIA3›É!ðÃ?˜Ý;“ó]Ü×믿._ýuñ¼B|†ÉßgÂä6Øúá£>*ìL0õ¡@€äŠbf&KBiuéÒE:è yùå—‹x}â‰'äøƒÑGy$zoæÌ™Ò«W/³ßöZ³fŒ9RÞ}÷]¹í¶ÛdòäÉò÷¿ÿÝèYg%Øù3Ñn &L(²UôÊ•+åàƒ–uëÖEããÈ:X÷L%A©Ú@°Î¿¢jmÕÆªíU»«bý¶f³¨(Ñ:úÏ\Î}÷ÝçüûßÿvtçNGwè4žt§iӦΛo¾é|ùå—Žþé‹£[:›{;vtôœï¾ûÎÑà%wa{ì±Î]wÝå(q;C‡utS8gÈ!ÎÂ… íídœüÑÑ ãLxº‹¨9/_¾¼³zõjö³Ï>ëtëÖ­hBy–sP?"õõõ¦‰*ê¾)« Wr䯥J /ï¼óŽñ_{íµrä‘GÊUW]ãÑG5;vâëÝ:ÈW\aþÉkûöí²|ùrÁž>ØÊyôèÑfûçÚµk 6ƒÛ{ï½Í. o?ü°´lÙ2án ØôÀídLÜø×°ÓN;-š\ @òϪ ³`€™æðæ÷ÐCûí·rÇw2¡_ýõ†ôu”.0=øàƒÂG§€!ž€Ô­”´(þ@æÕW_¬4š>}ºÙVÚ>G—ä’.Pe˜ƒ¶cÆŸ¸XÁ– VjÔ¨alöåC—-[fþÔݵkW³½Ã‡~h¶}¾á†ìc Ý’vÅH#?Þ2ðOb"KHþ¹D—aû“N:IÞ~ûmY¼x±ìرC^zé¥hš>úhsŽ¿r„žþùrÿý÷›‰Øý÷ß_Ö®]+jû7÷¸jÕ*ó¶}ؼ[Á—’vmÒ¤‰1 6Ìüdñ§y…dîí“]<Z!ЪU+éÑ£‡Ùär 'Ds€Ñ<:‡fÍš™•::u’ÇÜlævâ‰'š Ýð§.¿ÿþ»<ôÐCæ9lí¬½R¯^½h8ö»ê„°Ù T'™ëÞ ¦˜–ú÷ïo¡Kr†wõÌ´ 8¸võ¬¬ñWQ­¡ŠuU'`ÅF<=[3ï³Ï>EnÃ?Þ ØÂÙ-x£|lëì– 6˜°ð+%íŠÿÐF2qâÄØÇxî‘]=1ÓŽ]=ñ*‡µ¶T¹«§‚@!D@—tÆÍ!–ô­ÇDÏ`TŸHíŠÃ`Rzê©§=ÊëD «ÐæŸU8H¼E€ø1‘L!^ @³(3ÏH×ìãY‘o›Ù‡#ßVE&Œ";Hþ¹Ã–!"@|‹Éß·EÄ"@r‡É?wØ2d"@ˆ€o ùû¶h˜0"@ˆ@î ùç[†Lˆð-$ß FˆÈ$ÿÜaˉ ¾E€äïÛ¢a¼B`ܸqfËfÄ7{ölùä“OLÔ?ÿüs±¿uô*MŒ‡ä’®fø¾G»m~óÍ7&ø÷.ýkEsüé§ŸÊE]äûô3D ¸½C:¨ñß"Àí|[4¾O·wð}1D€")4ûdŠ Ÿ'D€ $ÿ,4&™")$ÿLäóD€D€ä_€…Æ$"@2E€äŸ)‚|ž"P€ü °Ð˜d"@ˆ@¦ü3EÏ"@ ’“LˆÈ’¦òy"@ˆ@"@ò/ÀBc’‰ ™"@òÏA>Oˆ(@HþXhL2 D SHþ™"Èçý†€Iuý–>¦§p@²õȺ…“úRRJò/ Þ.Xlc…»+¢›&Ü3PWPg¬z±×‘ü½Fœñy…€%}ëîô*bÆSРž@Ý@Ag(QâË%ºÁëD €áCÜÄ¿çú‡{˜;ü!Å@}A=±oжáCæª-".Ž›ª6Qý]µnÄ}BÝʪð_^µ¬*¥8è,a!Ê›“l×SÝê8ÎÇÈEĦïnà¶‘ctßCu–êOªè0Ú³&?¥(À¤­êݪo©þEoUsT U,‘»Gÿñ[òGÀÈ?vô_¨ùM˜n’BhxÃï(á¯R²ï§é|U݃ô£ˆ%4pŒîAb°é÷Q½I¶]4r;êÇ=š|„é¬ç?«Î\¿UÝÛUó—‘k…èà­Šêæt¨8¶ä¨O’àŠ4\RŸ£Ä§æz¼ºGèù6uÝäF òÿ?ÕUªµZÒǨmÀ¿uõEè¢:MuE ¸ÃTïP½Zu±j!Š»~ØõÄš|àZò7~µ^®Ø#€y*ÄÊÈ4gˆ€þËÄZŸ¯Ðc¸ícT’Ÿ :Uþ@ø¸?Pø'ñ+.>艹¤ºÒu‡Ç¨^¯:@õ7ÕB;úÇÛ!dÅ[:(ο ’?GþZº”@ pæâSåý™êNRE£µ„ÞDP¨Š†nIßš{Hþ JŒ¯ç_¨~s§À·†êcª}Uש’`oõÄvpAúö÷7â×<!ù[$è,JøGiâǪŽTý‡êçªß©¢£ñV}VË;Aô–üIú Fé¥×Ç«³xòˆ^¬«ú„*æR0J.$±¤×’<ê Ž¡8ñã8B³O ‹5™RÒ‰Ã}‰êÙÚPßÑk8ÙªŠWø=UR=Du‰ª%~=4‚sJQªèébÕ6ªkŠÞ*vö/½‚Ñò¹Åîøû‚›üq«&~ Éßß”©K€€’|½âÁZþþJüø ËˆÞ{IVéµ?ëñùzŒ”`»v Fý”ø ÐËýTOŽ»ÈULžOWýDõš"w ëÄtZgl§PX©O#µ4û¤É/Jè‡k @ðϪ×öj^Ñ]©º@aÿ?SÝKUoŠÓ¨CÓÈ]¸$u¨¸õWc³¸&½ß@ïQÌ¥à ß\<©Ï|­.¥àÈ¿ ‰IÜ€’Fë˜ØÅó\%š7wß)þ«^;êÕÿ¨®V­¯~IöÅa*vEq«¥ˆ`¶QÏë±%úzÜJ¦²O#ú\…w‰º”B€#ÿ*¬0'UI¨¶æ#ý½TT²?¡èý/õ™꡺*: ’¿‚„àmiê$Å£z¼UY¢ÇÆx*¶CÕ¥8$ÿ/À0$_I“µ0ó`­þP%L0–*êºQRCßB죊·¦ÏÃ_ìãZèNµçt š} »üŸz%œ+5“XÑsÑ”ÀgØÇÔ²h­É{CË¡¹“ɤ%‰GþIEoÞ" DƒˆžVm¤êÞ·ÇÛ„067 õdo-›šÚ”¶Ôý}ˆ@NÈ_+l¬ëî>ão¨Pˆ»z$­>°5Ûí°Œs[¬ž{€–þ ¼˜~Ûû0Æl"ò=ˆÞ­H§ûvÙ,¹Â ­f²Uë‰=6n¼Î@ýüYýߦz±ÞŸX8Ù MJ1ùKò@qgDþ1¤¢‡‚äáÆî›BòWPB& y;Ú·ÇX=‚kE¢žìB' ÇÕôøIU¬?T¯-V—â?0ò?ÑÉbŠRE mò!~½%üõzL¢Oµ$Â㈴ŒÖ¥êŽW}Gõ%þ­êRü‰FþÃý™4¦*Ò^í!KøèDÐàSïßµñ¦’ú ZoÛ½T±\› T½CuˆÖ›Ô¥ø-?´y ððáÜ:'•I+´Gþ.Z1¤o‰O=΋¬_¿^¶mÛ&{í^¡øìǹOµ£jÕùªŸ# „ÝçšLLÊÿÛçÉeòJ@ 0e‰Œúñžñ—WŨ¿’ª§2oÞ<éÚµ«Ô¨QCêÔ©#5’Ñ£GGÓЪU+éÔ©Sôœ¾@ ½¦â]U˜}º«.R¥°ûƒü)Œ@&#?a€üAüž’?FûÇwœüüóÏrÒI'I—.]ä¹çž“AƒI¥J•äÌ3ñA"Ň¼¤i¡Š]9aþAˆfHÚ  Ÿ ìþ=}žF&¯ÒùG„ÉÖ=ò¯/¾o¾ùFŽ8â5j”ôíÛWÚ´i#C‡Å~Ùòä“Oš{O?ý´yÄݽ{wùñÇÍùĉeøðá²z5öçqŸ#<ÿ5×\#S¦L1þÞ}÷]iܸ±LŠ¯Ô‹Ê† ä /”æÍ›KçÎ媫®’7O›7o–#FH»ví̽믿ޤ7çÏŸožÃ[űÇ+“&M*0ÏREàÏúÀkª¨?PkBÔCJ À‘R©I§ª(+FûUU÷Vm¦ Ûm7U ®¨|üñÇÍ9åË—wºuëæÔ«WϜϜ9ÓQRwöÙg§nÝºŽ’ª¹~ñÅXâ½[Î>ûlsmñâÅæ‚û\GöæÞ«¯¾j½s[¶létìØÑ\¿ýöÛÿþýû;§œrŠ9¾õÖ[ͽ'žxKo¼Ñ9묳̽—_~ÙÜëÙ³§£†s×]w9jBrj×®í¬[·®X\¼P:¨ª0õ ¾ Þàß P`6,£!¤\ùŒ·˜i9aÀ·Yµ ±÷ûlâîÈäg­bô†Î Ä _Œú•ðeòäÉêU‡~¯½&5kÖ”|PV¬X!JÊR¿~}¹ûî»Í}ühG`L9{ï>¦èùO?ýd®aŸŒ´oß^üq3'0`ÀóÈ·ß~k\¼1(ùK­Zµä¦›n2iÜwß}eÇŽòÁH™2eD;3fŒà-eÓ¦MÉDI?ñÑ£¾€DPwPP§(€€æj¾TÝ¿’Ë$&@ ]ò·ÁÙNÀ6âÉÿýw×9›çajÀ6s VëÀ„ƒÉ[+‡~¸ 8PªVÅàPÄ}ŽŽòÅ_×þ¬\¹2j&²×àÂ4vìX©^½º1ãàzRÈàÁƒÍJ!ijÔC=$*TråÊóÐ’%Kä´ÓN“ƒ:Hf̘Q$&þ¤‚€›ümJåyúÍ?°û˜ÿd0é" ùÛ‘\(:hB)[6þí÷Þ{O¾þz÷©ùE¶nMîŸðmþ‡ÜôéÑ8Vý¨ù&zÍœþùòùçŸ â‹í0Z·n-jž2o§Ÿ~ºàM`ذa&-xûøì³Ïäž{î1ÃÈ‘#Í܃ —nÊ`´Êà®îã”äž#@»¿çg7ÂLœµ!œRÉ?^Ò·lÙbFÝc’uáÂ…fâÕú… «x0± qŸ_rÉ%fþðËÚðå–[n‘=z˜;&”c«ƒ4h Íš53¦&÷ýK/½T8àÑùÑycþÙsÏ=o'mÛ¶•k¯½Vzõê%ðÁ=JÚXâ·®H¤ ôŽü=‡<˦3 IÙãCšªUÛ©b¯¯ªYTì„ï}÷Ýgn,Z´ÈL¨2ÄQÂ7Ç÷Þ{¯£#~3±Š‰á¹sç¿î ^\ˆ=Ÿ={¶Ó¢E âVóóüÃ<‹÷„¯ŽØŠ+š‰Ý~ýú9:ßàx þÊqæÌ™ã¨IÇQÛ¾¹¯óé§øw:Çyà5™8*W®ìôéÓÇѹs?©!€2R=U‹°Þ¿‘j-U,æ„oLxkYaÎvÛJZ8I_€¤µ½ƒNŒ‚üa·EPMµº* ù˜•€Êàµ,]ºTV­Z%ø¨ kü‰®Ò1s 0 ÅLôj­:—¾`9¦Kþ¶ñ¢ÁZ±×ì9]"KôîÁ‰ÐòéuèÅ9 T;ú4‰LV ¤KþÒMü8GX±×pBâ!à®/˜ø¥&\ï_˜åf;“¤»;ŽÜ2A2\Ïb€Q?êŒ=ÁÉ­]ñœ…$'nòN5Ëh¸n{-ž·ç©†EÿáBÀ’>ˆÇPßwãPH¿ùRi¹Òš.ù£‘Ú†j¯ûš+ „XÒOè7|vUüƒNúâ‹_J!.ù»³ˆ âǨ?á¹Ãæqp@±äoÁÍm@s¦“¾Øâû¢wh›­LÈÚ6^Kü‰Ë îAŽ3©‹9I MÚý“†Ê?3mpî[¦aù¦$רz×j®ãdø¹C€vÿÜa›³3%lw#¶¯ð9K, ¶ÞØÑ`2ÒŒpä_€Ÿ.ùÛÆkÝÌ:“œGÜÔ![¬›Ç¤1ê4ø¯>ÓQ'}ù½Fàåë‘tÉ?_ée¼D€ø ôÅ–ÜKTñ¿”A€ä_ ÅdŸ#@»¿Ï (6y$ÿXDxNˆ@:ÐîŸjy|†äŸGð5ùXa’ü ¬À˜\"àS0éÛI'}É)>- Ød± bá9 )# “¾ëô¡ŸUÛ¦ü0È $ÿ¼ÀÎH‰@  Ý¿€Š•ä_@…ŤŸ#@»¿Ï È<’¿ " ùg‚žÇÏ’ü=œÑ#€‘ÿ:éË/µ  IþPHL"(tÒw¦s¹j«BHoØÓ˜)ùÛ=ZbݰãÊü—ŽëLé¢Úý ¤Ô²Eþ’]&Ó‡ `GàÂI3I´û§ œ×¥Kþ¶±ºÓ‹k´õ¹áqI ®Ä«G%=Ã{þG€#ÿ—‘IaºäïΞ%}¿'ƒ¶¶;‚dó¿Žüý_F&…å2H§m¬ÖEP8v8ÛŸªÁÔÔ‘H6w¢¾?ËáÉ¡Núþ®íj =ÆûR|Š@¦äQÞìè îZè>Í.“•o\SW4=îú“ïä1þì `Gÿ$ÿìà™“P21û ñîRu^Y×®]²cÇŽœÅ@³Š€ý£þ¡Al=Ú}ÆßBF€vÿ(½tÉß6^4X+öš=Ϫ{ÕUWÉM7ÝTb˜S§N•“N:)®Ÿë¯¿^† ÷/zŽ€%z÷à!¯Ïv„väì\xî21û¸‰0Ø×÷œ@âW“ANÂf yAÀ]_øß¯y)‚œEú‰†Ü%g¡3ଠîÈßFî~>áÈmàÀ2aÂûŒ9¾ä’KÌùo¼!=zô&MšÈÙgŸ-¿ÿþ»¹þ÷¿ÿ] gu–Œ1Bžþy™8q¢¹÷Ë/¿È™gž) 6”c=VÞ~ûíhØ[·n•AƒIݺuåÈ#”E‹EïÙƒ7Ê\ 7–£Ž:J>þøc{‹®7 ǨuÆ{3cÉ9:燯|7é`­iÎ#ci#à&ïTAÃuÛkñ¼=/VË–-eìØ±ÑkcÆŒ‘öíÛ &†¯»î:¹öÚkå“O0Xyøá‡ûóÏ?ËðáÃ¥N:Ò¯_?ùþûïå‡~0÷Ð)Ô¯__>ûì39çœsä²Ë.3aáæ»ï¾kžA‡€gÑ¡ÄÊE]$+W®”·ÞzKp|üñÇ˪U«b½ñ8úÌ@èP(ž"€:cÉÇ”`!€‘ÿ¥ÁÊR°r“ ùÛÆk‰¿Dd0úÇjœuëÖ{=<¿ÿþûrÏ=÷ȇ~(ûí·Ÿ<÷Üsò /”Þ  wÝu—1Ûà¡R¥JÑgìœ.€ôá¼à­f$¼9@ð6°çž{Z/t½AÀ=hÀq܃7Ia,9@ÀŽüs4ƒÌ™68÷ˆ­Ä°Î8ã yõÕWeÆŒrÊ)§˜´Ã\ó ˆ£ù—^z)j»O”95ìõ½{÷–2eÊæÜòå—_ o0-a2¹lÙÿ-&iÖ¬™™(¶sK—.•víÚÉ‚ ÜÁð8·Øz×jncdèž" íÿ狯ýy1#K ;‰PÜØ¾ÂÇ}¬yóæ²Ï>ûÌ2Õ«W7~N?ýtY²d‰À¼ƒ `ØþaÊ™Øÿ¬…âÂ><0çüöÛoÅRvôÑG›=AúÐóÏ?_î¿ÿ~ã“Ã={ö4Ä}{° 4lˆlÖ†?†”†ñÀ"@¼FÀŽü½Ž—ñ•„@:“0^J¾‡sñÅ;ºY›£[*;'Ÿ|²£[8› _ý÷.Ü6‚É[ý£§^½zŽÚð%{GÿäÅÜÃd0®c²øðÃw´1aéÛ€óøã;µjÕrô‚Íp¢0"ÑÐÉ#Zw8á²Im-sìâºNµ¶V=NèûO7vß«`”ÓL"AåÀ.Ÿü­£[0º_±b…Ù­×—/_.˜è…ào1/€ðK ÃxæOÞàÆnyƒ>¯k¹ÿ[p‡¶ÍéyM#"à)ùGcåAh ù‡³èµÜМÿ¦äO8ð_®=±ùû/ÛL #@»¿Ç€—É¿4„xŸl À/}³bÃ(Hò‡Mßd*aQˆÈ9ø“ìzjþI<á—ó$07Cþ .ݤ›¸pÀî|¤u¬«†dΜ9i=ˇˆHdíRߟ«fÞp“–>K@ `È[9<öØc%d…·ˆð9´ûû¨€˜kn¹å³=¶Šøê«¯Ìõ]»vÉ7Þhþ˜ûÿŒ1"jÚùå—_Ì—Á­Zµ’¿þõ¯æƒ3óˆðÚý½@9Ù8@¢©ª†ÒG^JÚŽ®¿7f©™Åi×®£[48'Nt¦NꨰÄ9·nÝê<øàƒN÷îÝ%páá™Ë.»Ììþ©‹óÇ?þQ³á8ºiœ£óã?šÝ?±3èÓO?mîüñŽv<Ò0hÐ ûÙ³g›{üñ`®ŠÝ_¹«gLÛSLð1”[18 ’vÐüÀö¤<å2/Ѻ ­3ež.íOööÑÂ6;n>ðÀR¦L³¶zèÛ·/n™Q:öìqïê‰ëvWOìù¿Ï>ûHåÊ•¥~ýú‚â°¿ÏÃ?l»ä’KäOúü± žkÜ;M‹Ù%ô…^þýûËÌ™3ͳØû =ûì³Æˆ@>ÐŒm܈$Á5ˆûÞî+…ÿû­f ³¦êÆÂÏNNs`W¢W« æL fЊ¡ÚîÓô=#6ˆ²çž{ŠŽþ£©.W®œY½“ÊŽœ 4ˆ†‡ âìÞ>è9ähØÍ›77_c’ˆ‚ퟱ=4…äñ£A€äáÚc¼U»‰ß}¬· ^`Ÿ=TuVÁç$ûpº=Þ‰.®WëŽÑXoÉ;ž‘?¶](MRÙ‘3QxèT~úé§(±/X°@;ì0A'›?6–óp±A…x€‹øAøhÐõª ú0ÈkaÈd–󒯦Š:‚·Ó)h]2=€ž§,¨|¾‘£N¼«'Þì辤cÑ1cƘ]AÑ)꜂٠´I“&Ò¬Y3 2yòdY¿í’'*i¼x ë¬z²*fòGje~Ku*æuÚèµ Š›ôËk+¨š­VPg©Ä ¶ ~¨î©Šú‚Áî½z6òׄ–*Xу}úAÒXµÓ©S'Ñ;ÍsúŽòÍ7ßÛþàÁƒ†5pà@Ñ¿u4«Šô/"‹ƒ`îãOað–0)yCì̆H P±¡n±÷Ü× úX;4kÞ‹† Íw±<.[¶LÊ—//uêÔ)vBƒþ:×Ê=ù§möñÝÆnèñíê‰/r·mÛVìO`,Ö…IÇþé æÜ¢[D›ÿhÙ²¥hCtßâ±D0?M£Â+,6ûª¬ü° X=½(Ñü[3Fû ~4h¼-FÝ•gžyFF%Ÿ|ò‰©¯0ab£+׌×{î¹GZ·n-}úô‰}4éó¦M›3©þvÒÏУ·DÚM \²E[lSE'°KëNñÊ£7Jß‘I‰å½ÂGÀEþ+47[UoT=Z€[`“[¨:Oõ+Õ´~ÛÉ/=-L‰?ˆŠNù®ª:7¶ýâ«v¬J;ðÀÍw)0Sâ?®!æµðw¦0‡bÕœý¯k 'eÁb ̕͘1#ågù€7DÚMG oÃè ¶ùÛÕ@z99Á«'…ä ñ¥ªª®SÅHâ¿J÷ÞS­¯ZÄD©á%Õ[Uû«¶×û…"Öìƒ7Û`ä_L¾þúkcþ¹Ÿzê©rÞyçÉèÑ£å‚ .ü7Îa}ÿý÷åæ›o6Ï?ùä“Ò¹sgÁhVâ²­è70Ò¡C騱£ :Tôϓ쭨‹ù²£Ž:Jþþ÷¿G¯ñÀ7` €7E¼1fló'ùû¦\C74÷TßWE‡R|KG3sTG«^­Š··¼¬'°Ûª:Ú}C;ƒjª½Tk»¯çûXÓƒôBÐî@þhÀ¶Ðâ’ÆÈ~øðá‚… 0ÿ`óC=dV¯í·ß~æ,[Æ2f¼)\xá…æÍf ì…uî¹ç?Xè0dÈso¯½ö˜‹®¼òÊ"b94æÓð¿Ùƒ *r'¾@ÄúòGýÚ:¥‡© Í>©áEß"á?ØüaöY¥Š¿}•ÅÈÿ Õ£•ì{«›–hÕõÁIª˜ÑÿMõ% ï&uó*òwÛùaîÁ.—µTgh‹¥ïÅ_”믿Þ|œˆ›øV=>÷ÜsƯÛìƒ7…·Þz˘ðØXèð믿šUrXI÷Á˜ù.ýËSc>¶SÙß¿üþûïf4æðQ%Å?DÚ &zðº†6ƒ·ãMª0ÿìкC³A)`´öŸ6ñ#ëúü:Õcô¤zŠêT£¢ éÕ6Ñ ÞØ‘Fÿùcô†Ñ\\Á—éø{Wa,Œöaš™5kV1ÿ0õ|þùçf•>`„YGq0þF£FÄÁ×ô ~+sçά(Ú¸q£YUd¯Óõ8@Qw ¨KVõ05A"H”ø06OuJL±Bâ]í g¨ZBŽñ–³SÄg0È@1Á$.Ì<Ó¦M3²W_}µÜwß}ÆßoÀRVTÐ9`Ë’aÆÉêÕ«mßúÀÄñš5k쩌?^žþùè9–>_wÝuæ9,…¦øÔÔë¦MüÈÉ(PB…€v_j†›¨ŽR½Xõª^Šmwh¼ {^$ -Z´0K•r}Ìô·Z¦a$ó¼Æƒ‹å°½4Týƒêª0MiÅEM<Žn}î¨m¸9ºìÓìpk}ÒÝiuÃCçÄOtÔæïèw-ÆŸn[îüßÿýŸ9ÖïfœåË—›Ýn´}ûöŽîƒe‚©W¯žñ‹“W^yÅÜ?ùä“mt}€@¤Ü0Wv¤*G Ý ‡Ój7œðUä(Þ!±°$œðÕv–·µüš¶šŠD+U¬¹ÇDZV%b^‚}Kö ˜ìEÇWWu’Æ©N|Álj°ÇÇ~´ߘ¸ÕmÏ_´céçÒ¥KÍÿdĆ„ðñ–€ ±×¥pˆ´›34ÅX(ñ»ê:ULú¢žnO§Ýĵ5j`":´­ÑF¶Ÿf|¢º‡ëù9Á²|Ò¯î°ÉCã ¶}€B`Úwß}ãy3Ë<Ý‹û/ú]š¨¸fÂTKòO1ú4Jø/(ñã à_ê¥ç˜ζXòG¸èpŽì’î ð %T˜:¢9ŽÖ×qZ@d¥I&f¬\P»£ñŠÉ(¬%†üüóÏòòË/›cþŸ €Ý1Ñ+éA#F†bg V;Ïç<§ÿ1Ô[wôÔŒþq±îî³$=#즉]9!Xc¬­hŽñÇì]t‘9æðJ†X"ÚEõÕ¦¤ö‡€B’AÀÖ;`H晄~<3û`BÊŠ{}±þ÷nôÀÞ§K‚ˆ€v&nÓŽmÀp)D 0ÂG`Gú¶%ól1?žü‹ÅÌ D œ áZÂw7âp¢Á\§ƒê­GÖM9’Êñ°  ó¯ Uw/£É^¦Ñp­©'¶È^, )ÈØAƒ­Gö<¥<“üS‚‹žÃ‚€’>Öã«j :ã¬k˜±#6Ûh­›q Ø:iÝ´2MòO 6>Zhñ ¬ýlg„OÒÏ6ªÁÏ=x@N3ª?$ÿ`Wæ.}°Ìóßé?žðIÛ€áíÏ.÷Løo¨?¨3nâOë €äïB•‡DÀ…þ=ì9×y.Ój¼¹LÃö%ömÑ]_ÜÇ)%Ú³¥ž)¥Šž‰@žPsϵ9L]hÈvÔïÅå0Z@öY©/ù 60 D€„¬?Ð"ù‡¦Î0£>@£6ûšní¹’Ç$ø7_Ûúã¾–RòÓ~0¥X虺sÕ?Hr™L"6$ÿ´¡ãƒAC@IíáEÕAËóCbÈ”ü­ý)Ö‡çD ?Ö™þšÈå:ÙûNlbyN‚†@¦«}l.Ìw Ùzd]ïbÄõߤ§—{9#$y@ ]ò×Hc?<ÈCve €º‰WvßÉÏo§~Ñ›Ÿ1V"tÉßœ%}4dÛ¨Ý÷yLb@=q×Ô¼vJúHO®¶rˆÍ?ω@ÞÈ„ümƒE£ÁŸnCñ—wî=Ëõ”BŠ €zƒzbë ܼ‘Ôñ„„LÉ0¡á¢ØÑêÖŠhUu+«VTÅÖ¸˜`æºf!RÇHÿâ³YuêZÕUª¸Ž vô¯—¼µõ£Žž£ú/ý#}" 2!„ŽŒ† ECߢЯn÷AÇ}4vÄUV•|ì[!–ü7ê1êê®ážý£ŽØgôÐSù§Æ¶Äï)æŒÌ¤Kþ¶¡ÂEFCÁ£qï©j‰÷Ðб7:®Ù¯ÒôpPö¶üQ/ ëU7©¢NØÄoÉ_½õ÷ÐØS=ß»Xðé’?RâG£E·£~üU®¡C°ÄQ?Gþ BÄPìè#~¿}Àu(üXÿzè(ñ£.>¤z¹ŽúÑ1Qˆ@¨Èù[’Gc‚Mßv ~4t¿5ùpä¯`„@lÀàÀvÖÜ¢E'äoßðŒg¢„x;{!#">C äïùÛFFÆí&~Žú}Vø9NêÔ’?êÖäƒcKþÆì£„ìi ñçCl->ö<iaœ……Ú‰[Ü×콤ܴÈT_›"ùCpÁ&6~;âÇA¦[Ih”BÀÖ[GÐ Ø·D·‹cø  †!TE .©Ör’•z”ù» C",ùcD‡c(ÂŹ5Yâ·®Þ¢[9-©Ã…‚èQ?Ü.üb2þü"Ï5iÒÄœ7oÞæ¥"÷xì ÁnÜAC²9åÈ?Y¤èdÛ€Ú^ÝmcƵ„Ò Aƒè½*UªÈæÍÜ. HxPWPgÜÄŸTý‰…ˆä‹ω€÷$ÕxõÛïSÆý„€}[t×÷qJiemJ .zÛ¶m3Ë"ó˜´;4d;êwâÒNL=è 8Ù›6„…ð È>+õ…ä_Å] iÄJ”ï¾û.©ÔbYã#<’”ßlzš1c†téÒEV¯^Í`}ˆÿ¸ãŽ“Î;ËÆØM@²BüÀ…ëüX;<ÈRÜuþqV¬XÑŒ>KKî]»ä°Ã“9sæ”æ5«÷ï¹ç³nþ¬³ÎÊj¸%]ç¯#s ¶°ÎKq°Ö¿†*Öù×QÍÚ¾kÖ¬‘š5kj”  ™Àï§ùÁ¾Xç/|ñÏxæ2¸Î_‘ än¸A~ùå¹üòËåꫯ–£Ž:Jn»í6“ iÓ¦™5êX±òᇚQÿ·ß~+Ï?ÿ¼Àň+Xî¼óNã?^XÝÒ·o_9òÈ#Ë!cÇŽ•“N:Iºwï.Xï–… ÊÙgŸmÒ/_ÿýw³Lò‰'ždP…ÄÔ’Ír¾ðúM%)Ö꿘œvÚiÎâÅ‹¸/½ô’££{§GŽvΉ'žè(;6lp”¸;w:|° cÒ¤IÎ÷ßoŽÕdáè—­qÃ8ï¼óœ>úÈ„{ñÅ;+W®tþøÇ?šsÝ Á9ùä“‹¤éÜsÏu¾úê+îéײÎ]wÝå¼òÊ+Eüåød‘†oç0ò¯¨Šý|ðÙn;Õnªø:+ÇÉ`ð…Œê‡*öö9Rµƒê¾ªxkÄ>QemKÅå:EŽ’}:è ³Ö[À¾®;iÊ Aƒ¤råÊæØcýúõå¯ý«ñ¿|ùrÙ±c‡¹Æ_|!‡rˆ¹§;‹Šv0‚­ ¬ ¦$·|ýõ×Ò®øUa½ÿþûîÛ<&¡F€äêâÏ]æ±ÏŒ[`’™9s¦üôÓOfk7c ƒ_|QªW¯.­ZµŠ~ F:uDwÝXÎt矾!u˜qÖ®]+S¦LqG){íµ—Ùçþõ @ôÍA,ïf¤Xå¤[lGñ-OB‡É?tEžŸ /[¶L`ïÁ«©ÆL c¤ŽÑ;ö¢¹æškŒi¤E‹ÅHܦ~6-ëÙ³§´mÛÖFþjnŠÎ/Xÿ˜7À~7è4š6mjöı÷ÂèâËßéÓ§ܘæ¹(\íSž%‡@ÜÕ>¥=нj ‰CÔîoTÿ`ÅLÆÖ® ¦._Ð Ë`Š'0 A±ªÈ &r±— ¾zøÅ—°6ÎØûœ{¾Ú'Qž`‚#ù'BÇß×¹ÚÇßåÃÔ•‚ØMÂ0ë€ø!c:4ñö,v¿}6ñ[ÿî8q­Päƒ>+¯¼Ò¼5nÜXŽ>úhY²d‰I>–¬>ùä“ѬÜ}÷ÝÑP0¯õêÕKêÖ­kÞ”Ü+›@þ˜ÿÀ›æM(áE€y…·ì™sŸ#°jÕ*óç,Xº‰Žó!7Þx£I5&ºaJ³‚ó_ýÕœÂÜuíµ×š?aZ{ôÑG­7³Ô»ÞtÓMfI.÷ŠBºÚüCWä̰OÀÀ«£j‰ŸââOZ@øxí¿ÿþû¥Y³f»ªÉü‹VLabß>Œ=ÚL’[?7ß|³tíÚÕ(ÂÅ·:u²·é†ŽüCTØÌª/襩xAõ+ÕáªE—Eé·`,çÄÈKkñ%5Ì:øòÚ : ¬ ¬D ‡(ùb·<¥'“U“ÚYnÞ¼y…Œ3Æ,ÓĈÜ¢_;›•Q˜ž5k–ñƒï ößóÝCÇŽÍ7˜; XbG%±÷yNâ!€M¥@b”ô«â}u¯¢Vº¬ò ýQ¿kø!ÆKJ§X•ƒo`ÖÁØýë_æyü£&nq_Hc%«¦t+ Á¿àkh,ƒ}衇Ì=þ7\çïFƒÇ¾G@Í{i"+è&ÿ[ê’‡Tk:êk´}U±Aѹ6 zû¬ì6ÀÛ‹WïáM;é]=ñÅ2ˆÛPctÉ^ #*Ì>Xáãþ‡/{þaòÁ‡p”ÂG RîÜÕ³ð‹’9ÈWôÙ¼.Oцø–¦¶l4ô¬jTÔC°ÓÇû_^\Güˆo$þ4ÀÑ#4û„¨° =«JºØ÷;µ½íU^4Îf×V%õ_\qþMÿ­×¶¹®eý°uëÖ¢;“f=\H€ÉŸõ Àºø/•t·ç2ÑJøõ4üÁª0ë`uNWÕ¨hüùç\@þP È\í“ Tf®(¯Ãì“UQ²ÿŸ!}wÈ«ƒ††¨ÖW²_¼û2‰@pàÈ?8eøœ( OÓLB3åû}4ãUñÑÕOª×ªÑxn‰Ò!E€äØ¢eÆ! Ä€Þ{GuºêªU)D TÐìªâNf•À¯Tm/GzÒSõ:Õ©¾ãÛ,ï¥#üÓUG«®¹ï‹Ó’võÄ2Oì̓Õ> 6”#FØ¿Š”=z˜ÿFÖ¿Ä4›ÁMœÈ¾Íê³Dü}V LNÒ`"v¶ûVÕù1OUÕóaªðƒÉÙ+U£¢d¿N5îZü¨'”´«ç¨Q£dÚ´i2gÎyçwÌÇ_öOíõÿŒå™gž‘ &ÈwÜ!\pÙöÁYbü„€63b K ±h[ª¨Ú¸Ò®éÄ` éMêÜ'OžìèÈÞüA½æÏÙ°aƒ£{ô;úµ¯£;q:ºc'.9r¤sì±ÇšcýGß"w§wïÞŽîã=çAá! uÆQÍê¸s䯈R mÆ[T—nJNy¢]=ñ¯ý3{„€ÀV¬X ¬eË–Ñc|ð…MÞ(DÀÉ߉€ÏH´«g»víä§Ÿ°Hi·`ÿþÃ;ÌžÙÃîܹf'ÐèME€äÏj@|Œ@¢]=Õ”cvùÄfoúæ#S§N5jo³2nÜ8sˆNá?ÿùO‘ŽÁú¡n¸Ô3ÜåÏÜû˜lâíê9pà@Q¿À,TµjUãwÜqÑܼÿþûÒ¡CYºt© :T°½3…¸ ù»Ñà1ðXÆoWOÝ2Z`ÎÁŸ¶è¶ÑÅ6xÃÞÿøc—5j˜óY¶˜ @ò÷A!0 D $쮞±~Ê–-+mÚ´‰½=O´ãgÔBmþ¡.~fÞϤ»«'>þª\¹²Ÿ³Æ´ùþ™‹ Iú1[Iÿ™K8Pa.“A@ë¼ñÏ\’‹~ˆ D 14û$Ɔwˆ E€äØ¢eƈ ‰ ù'Ɔwˆ E€äØ¢eƈ ‰ ù'Ɔwˆ E€äØ¢eƈ ‰ ù'Ɔwˆ E€äØ¢eƈ ‰ ù'Ɔwˆ@.À?2AàZ5øCJA`—ë¾­;îk®Û¥’üKLj>ˆ@¶°ÄïÏ6b÷5xØúcÝx~’¾FòO*z$YCÀ6^¸¹ùþÏä³–s” ¨+;"Ø:”vxÜÒ9mèø HKúh̶A;ºy—Ù½+íPù`@ñÛú’v'@òß ‰€W ±ZòGcƨîÕÚª5U«ªbOæJªåUñ†ÎŽAA¸¸ëÅÍë&Õ ªkUW©nSE}±ƒ¸¶>éajBòO /ú&™"`+\4d4h(ûfU=÷¶«†•ü«hÞ[«~¦&±åŽ:òßQÔÔÛØz¤—Ò’z¸ñ)"2úGë0íà9ŒØÐÈmC%~¼  ‘ÛÎ Œssõ4ÿ‡ªþ¤:_5,‚º’‡¢^ °êƒ½‡º¿i É?mèø H ŒØ,ù£!oUE#Çë=ÚŽúðÇ/h£è1ÂföiÉóÿ©ûjXÄÖ Ô;°æ?ê êµÿ§ÝüE ðkîqú1²·×Ѹ+ªÂ܃öYV5Œäß2’oÌ}4SýH5 bÉßl⇠\³óEi›HþŠ"…xˆ€m¬h¼K츎k 4r?ˆ6©¥Ƥ7“Þg©¾ª Œ‚.È£íP0ÊÙÛ7Dã:øƒ51-\HþŠ…xˆ€»q#Z?1ÜŽøí*ÛAè­PÈ!šK`³“¿‡ª¾Ž“€ êˆò·\?÷-ùëazBòO7>EÒB£4ôµ°ǖüÑ&AönsÎÃ$kfAøVp|…ꋪÀ)èâ®–èá¢pŸÃ4-!ù§"é# ü¿Ëõ=—m¼¶qc´oGüˆ$l£~ä¹+~bf Þªcc®íÔÖ¸èèÜ.Èç¨+p1–€›–üӂ̈th¸ wۀѸÝÄHÂ6꯯y®§Šy8 æŒþ«båKÄ{$£öØv¸œñ#€=2è8ð<…,  o‰Føa# y¶*&¼±üõ1Õ&ªa!}Íjt0€c+f€Æ·2uIþ™"Èç‰ÈÚ'®ÐÀ;*ç-ËY$! ¯˜"@ˆ€_øBÖɯ‰+ät‘ü ¹ô˜v"|ž ~ó“Cš}òƒ;c%D€ä®öÉ+üŒœø£5Eu|—*&èmMÖJ&­ôTqä_:FôA„À‡šÙxëìÄA²y=\=ÎJÖ³ßüÑæï·azˆ  @ò÷dFAˆð$¿•ÓCˆð’¿ 3 "@ˆ€ß ùû­D˜"@ˆ€ü=™Q"@ü†Éßo%Âô"@<@€äïÈŒ‚oزqKI’2(Ñ1>ú¨Œ=:iF%Û·ã_½•Þ½{ËÈ‘#½´€cã¾\xL:Èžðݶm›àïÊ—Çû¥K÷îÝeÚ´iR©þ{Ý;éСƒÌ;×»E ú _îíãeUa\D ˜8q¢ì¹çž²iÓ&ùðÃåûï¿—zõêÉ< ëׯ—¡C‡ÊæÍ›åä“O–ÆË7ß|#7ß|³Üu×]Ò¿Ùºu«T¯^]ž{î9;vl±0Ê•+'×_½|ùå—ràÊC=$sæÌ‘GyD.\(Ç—“N:©r¸÷ꫯJ5äž{î‘3fȲeËä©§ž’AƒñË“øÐì^%D ‚ÀªU«dÍš5²víZóðæ›oJÓ¦MÍè~üøñrôÑGË+¯¼"°·sÌ1Ò¶m[¹õÖ[ qƒˆ§L™bHú½÷Þ‹ƤI“¤I“&òþûïKÍš5å×_•›nºIžxâ yã7ÌñÎøGÇݲdÉ™5k–‰ÿÊ+¯4¦ž‹.ºHZ´hAâ· %á’ü“‰^ˆØÀAd0Â_½zµœ}öÙ2uêTéÚµ«”-[¶L•O˜0AÎ8ã CÖx;€Ä†ñÖ[o™·Ü»å–[àȼyódðàÁ2dÈóÖŽÇ LJ={ö”2eÊÈá‡nÞ$ì=ºÉ#@òO+ú$¡G –à?úè#yæ™gdúô鯬ƒÁ &ŠAÒãÆ3æ ÝS,Å:‰6mÚÈâÅ‹Íc÷ÝwŸq1Ї‰zÎ9çHíÚµm°Ò®];Y´h‘9_°`ì¿ÿþÑ{‰ˆA råÊÒ¯_?cêÙwß}¥V­ZÒ¾}{cªéÑ£‡<øàƒÆ<ƒNæÜ‹ûe—]&ÿú׿ÌÜææ)§œ"7n”£Ž:ªÈ#xËÀªžȺuëäöÛo/rŸ'É!ÀÕ>ÉáD_D ,[íSZÆwìØ!˜Ø{ï½£^aâÁjs… ¤bÅŠ²aéZµjÔOìAì}?©S'þËà-M¥ WûüóXs5ð!)“¿óàU’ šüió÷ªš0"@ˆ€ ùû¨0˜"@ˆ€Wü½Bšñ"@|„ÉßG…Á¤"@¼B€äïÒŒ‡"à#¸ÎßG…Á¤ 0CÓ°ÔËtÌŸ?_ÕŽú Àõ㮟¼Œ;ø~Ïðù¼>Î¥žy…Ÿ‘p# _ýþGÀÆ=W9Ž3;Ühx›{’¿·x36"j”ìË(Éï² èùz>ΞÓõ’¿wX3&"J”à±ãÛ1ªgªNP²ŸJ |–iNøú¬@˜"$”ø{h~–©Þªú_Õéª À _“@‚€€=¶Þì #û÷\ù٤LJêµï\×xè8ò÷A!0 D PP¯ z‹êGš‡%ª=T£¢¤ÿ1‰? ‡¯8ò÷Uq01DÀß(Éï«dþƒ+•íWV¦úÞÛæºÇC#À _“ ”0÷(ðœt×ô÷U=N{(×SÍ™hâä,pE€ä…‚D 5º%z릨?|WÔdlq%åY=ž§ú–êç®ë^ÆvÑsv™ÁOòÏ ?>Bâ>æÎìüHߪE§:‚ÆšØS#z›ºù\•%x  ºø6×cUØ ¸PJáäŸXôJ€@„ü-Á[âuáÕÝ!àܯr¶&ìfÕWU_Q©ê±ƒYâ‡ëVtæ;€Ô‹‹äŸ:f|"ÄÄ?>^²ºN a„_襯¡º#¢¦PòG'@I®öI,z%@ò }´¸T9Rr-‘η¼Æc;Zì äèõ"ÛFä:AŸä„Rdùd±øvíÚež?ú裓~;)/ø’¿/‹…‰ ÑŸyæ™R¡B9ï¼óäÕW_5¤Ž¼]pÁ2gÎãÆæÕ’ø¬Y³boÉ`®vØarÑE :ÿm·ÝfL5 64÷áÖ¨QCzè!Cê‡rˆép~ÿý÷?HÓ3Ï<#:uÌ “ÀÛˆ[n¼ñFó<È %@àJ XJ¯Úì˪bÏÚªUÛ«vWÅß*„EE‰Ùl7|úé§;:ŠwÔ–îL:µˆ§›o¾Ùøùøã£×—.]ê<÷ÜsÎW_}½fÔ¼ã\rÉ%N•*UÌsˆ·víÚΘ1cŒç¸qã̹š‚5-9›6mrÆožÑ7gÅŠæ¸W¯^Æßwß}çôíÛ×yúé§MZ®¾ ? °ÑçÝEºT7pþMTQØï§¬&|–$ùk¡\ SJ—.]DIWºuë&ýû÷—Õ«W—U£F&¢víÚó[®\9có_¹r¥¼óÎ;rÅW˜ùŒþãÙè11B€äï£Â`Rˆ ^!@ò÷ iÆCˆð$“Bˆð ’¿WH3"@ˆ€ ùû¨0˜"@ˆ€Wü½Bšñ"@|„ÉßG…Á¤ø»ƒu}Ÿà$Ðì;É'Ë%…'ù§½–dàîŠ(Áñ`nIß–…w±$&’@ ’ÙðKúÖåfbžÂ/Àêî¼MAb+€<0 DÀKìHÓMü;4Ø6y/Ò¸€;ð¶o\¶Ëø¿_l­ )É#`ñÝ®lUݨº^†ÿÁÄu`nGÿ$~#U!ù§Šý‡7ñƒp@@– °Ÿüžª¶Mሠäó*ß „ÄîÆ{öoRÝ¢ \qÏšl¹è%J²ØŠš¬ú#D`÷Èß’HÈMü yÿ6Õ ªhc$!±#ÛÚÑ?Þ 8‡â¾ý³P0R’*hÑ/øŸÉÄcÉ £{˜% ¸†Ñ)F©ÖäÃ… F b‰Üv° z`jÉØ¢Óýë%J²ü“EŠþˆÀnì¨Ä„Ñ=ˆj¯[â·£~ØûiòQR;¡‹Î8Cñ†eñű%øþ” ù§½†üq¹.è±$oG¥vToç,écÔöe‰ßºá1ùÜ»q¶ÈÞš|àZò7~Q>ÉOŸ$Ö"nR“ u{ d„6e'yÑ1à>‰_AHCìè­íl1ÞÐ!X…?JŠüSŒÞ‰€"²±„n‰ä2²f kî!ù+(ipµ ¼m`q¶ç¸”؃oJ)"Fï¡GÀõ1—Ùƒè-Ù[ò'ég^S,©Ãµ$ÒDZíD9 Ç” ù§½ ÓXP,ñ»Ïí1ÝÔp“?Žc•ÄŸ:¦Ñ'HþQ(x@ÒCÀÕجIÈžÓͦ3àÄnvÀ$ùgG†Bˆ((ðšJ!D€!@òY3»D€ @òg= D€„’ Y&D€üYˆ !D€äÂBg–‰ $Ö"@ˆ@ ù‡°Ð™e"@ˆÉŸu€"BHþ!,tf™"@òg D€„ÿÕ•—*…IEND®B`‚doc/next-tutorial/object-parameter.png000066400000000000000000001404001242365656200204060ustar00rootroot00000000000000‰PNG  IHDR±¥t—È<YiCCPICC ProfilexÕXgTM—®î‰Ì 9çœsN’s’EÒ“d0%(J’¤( ("J•$ˆA@1¨€¢"H ÛøýÞ³g÷ßþÙ:§zž¹Us»¦Ÿª¾÷>°y†…Ã4„„FEØéò89»ðà¦P"]Èx’#Ãt¬¬ÌÁÿÚÖžhop\rÏ×ÿ:í õö‰$Y!Ã^Þ‘äßFð9," ¸ ÁOG…!…tÀ,Á'÷°ß?¸l{ýƒ~ϱ³ÑCæô€'yzFø@|‚ØybÈ~ˆâXºPï€PèÐÖ$û{zÀ¢Ì‘ 9´‡Ã,âõ/?~ÿžž^}zzúýÅÿüä—Èõ"Â=üþòy ŽFž×ïF‡\I¡Áû÷¸Á#}ÞÛSßì þÍÙo»O¨½í{¨×~Ë?Ø7ÂÐæ‹Òý¶²ûcó×ÛÿûDüõèijõÇmcÿGÆØüÁqþvް·þ_»o€¡É{@”Éß{2û»,€' GùÄîñô…‰ðóâÑAv™I(YJ‚GNFVfoøÿMÛ;_ÿ,vÅæ÷¹˜Fþc‹Dž©ÚrÆþcóH ÕÙâÊÿ±ñê'¸ÏOŽŽˆùDz•À  €p~ $Pê@S` ì€3pdàB@8 @"HYà (% T‚«à:h-àèÁ xž—`|ŸÁX[á Jˆb…¸!AH’ƒT MÈ2‡l gÈòƒB¡h(J†² \¨ºÕ@7¡;PÔB/ hZ†6aL‚`NX–†U`Ø ¶ƒÂ~p8§À§á"¸¾7Ã]ð ü ž†?ë(€"¢˜P¼(I” Je‰rAù¢"PÇP™¨BTªÕ†êC£¦Q‹¨Ÿh,š̓–D«£Ñöh2:} .A_E7£{Ðãèôú†ÃǨaL0N?ÌaL¦sÓ„éÅ<Ã|À¬a±X&¬0VkŒuÆbã±ÙØóØØNì(v»ŠÃáXqâ8 œ%Î…KÃã®á:pc¸¸ <Ï—Ãâ]ð¡ø$|!¾ߎÃÏá·(h()Ô(,)¼)ŽPäPTQ´QŒP| Ø"Є ;B !‘PD¨'ô^VˆD"Q•hM ž ˆˆ3ÄŸ$:’IäJŠ&&U“:I/H+”””B”Ú”.”Q”§)k(P¾¡Ü ¢§’¢2¡ò¦:NUJÕL5Fõ•š‚ZZ‡Ú:Žºúõõ" 'Í1šRš;44«´ô´²´–´!´Ù´µ´ý´ót8:!::oººJºt³ô(z~z=z2}2}}/ý,ƒ0ƒ C CÃu†a†%F:FFÆXÆRÆûŒÓL(&!&¦`¦¦F¦çL›ÌœÌ:Ì>ÌÌõÌcÌë,ì,Ú,>,™,7Xž±l²ò°°±žema}͆fc³f;Ìv­—m‘]ÌžÉÞÈ>ÅsˆqØpÄsTr q¬rrqq†qs>à\äbâÒæ äÊçjçZà¦çÖäàÎçîàþÄÃÈ£ÃÌSÄÓóÄËÁkÌÍ{‰w˜w‹O˜Ïž/‰ïßk~¿ ¿/>7ÿ’·€…@‚@À” … Š ¿à9Á>Áu!a!G¡t¡¡yaaá8á:áW"”"Z"á""OE±¢*¢A¢çEŸˆÁbŠbþb¥b#â°¸’x€øyñQ Œ„ªD¨D…Ä„$IRG2F²NrFŠIÊ\*IªEê«´€´‹ôYé>é_2Š2Á2U2/eédMe“dÛd—åÄäÈr¥rOå)å åË·ÊWWðQ¸ 0©H¯h¡˜®Ø­¸£¤¬¡T¯´ , ì¡\¦<¡Â b¥’­òH£ª«z\õžêO5%µ(µFµoê’êAêµêóû„÷ùì«Ú7«Á§á©qIcZ“GÓC󢿴¯–§V…Ö;m~moí+Ús:¢::×t¾êÊèFè6é®ë©éÕëÔGéégêÐØ”¼1ä3ô3¬3\2R4Š7ê4Æ›Ÿ5ž0á4!›Ô˜,™*›5í1#™Ùš•˜½330o³€-L-ò,^íܺ¿ÅXšXæY¾¶¶ ·ºkµ¶².µþh#k“`ÓgKoën[k»f§k—c÷Ò^Ä>Ú¾ÛÚÁÕ¡ÆaÝQß1×qÚIÚé¨Ó 3›s€s« ÎÅÁåŠËêƒ>¸*º¦¹>?(|0ö`¿›[°Û}wjwO÷[GZmOKÏ ÏU/¯2¯%²ùù³·¶w¾÷‚†O®Ïœ¯†o®ï¼Ÿ†_žß‚¿–¡ÿb€^@IÀ÷@ãÀòÀõ Ë ê Ý`Çà!ø;¡t¡A¡=‡¸Å K ›W /_Š0‹¸ EŒlb@™¡h‘èÔè™͘ҘÇoÅÒÆ†Æ;’qd.Î0îr<:žßÀ›˜0sTçè¥cÐ1¯cÝÇù§ÿpÂèÄÕDBbPâã$™¤Ü¤ÉŽÉm)œ)'RfSRëÒ¨Ò"Ò&ÒÕÓËO¢OœÎÏ(Îø•é9%“U˜µMÎ8%{ªèÔîißÓÃ9J9Î`Ï„žy~VëìÕ\ÚܸÜÙ<‹¼æ|žüÌüîý… …åçç¢ÏM™µ Ÿ)Þ.ñ/yVª[z£Œ£,£lý¼÷ù± ÚêË9˳Ê7/\œ¼dt©¹B¨¢°[Sù±Ê¡ªï²Êåš+lW²®ìT‡VO_µ¹ÚS£\SSËQ›S×E×-\s½öäºþõÖzÉúK7˜nd5€†è†O7=n>o4kì¾¥r«þ¶àí²&ú¦Ìf¨ùHóR‹Ët«sëèÓ;ÝmêmMw¥îVßã½WzŸñ~N;¡=¥}·#®cµ3¬s±Ë¯k¶Û½ûå§O{¬{†{Íz=4|ø O§¯ã‘Æ£{ýjýwTZ•›‡‡š+>nVnQi}¢ú¤mtßhû˜ÖX׸þøÃ§&OŸí6úÜþùä„ëÄô¤÷äü‹àß§b¦¶^žx…y•ùšæuáŽ7oEßÞ˜Vš¾?£?3ôÎöÝËYòìç÷‘ï·?¤|¤üX8Ç=W3/7oÁpáɧŸ>|û¼µ˜ö…öKÙW‘¯·¿iZrZúð=âûîrö ëJõ…Ý«V«oÖBÖ¶Ö37X7®þTùÙ·é¸9·ux·]´#ºÓöËì׫ÝÝÝ0ÏÏß¹ ¹Â¾¾,W@é ýÿä¿¿g é1’´Ã¦E²‚NÈ ú \К¬!Î…b†¨A*¦‚¨ChÞÓyÓf$3Ͱx³¾a·àhæâäŽàiã]æçPÔ2ÖQUç’ •DK®KÍKOÉ ÉvÈ5ÊW*ä*&)…)T1VUTãRǪÝ7¡Ñ©Y«U¢£“©›¡—¥Ÿck˜g”o\`R`šovÖü´EÖþ“–©VÉÖÉ6)¶Év)ö)IމNÇã]bDº† póv'{øxyE“½Oû”úVûÝòox84<²º† §‹ŒT²Šö‹9zøllõ‘¶¸áø™„åc¨ã4'Øy’ø“ùRxR9ÒXÒNRg2Ñ™;YkÙßNÍŸžÉyufòì³Üñ¼±ü±‚Ñ‘s‹Š•ô”v–Ý;ßz¡©üöÅ–Kí*ŸV½»üíÊÎUB S-Ô5Õëúõûo85xÜôk ¹qûpS|ó±–ÄÖ”;émw³îºº=§#·³ «¤»üAUOmoýÛ}MZûïÜlê|Ü5Ü5ÒñäîhÓØµñЧçže?Oœˆ™ |qpÊú¥þ+å×bo8ÞR¾ý5ýefê]ïìÍ÷¥R?šsž×]ÿÄðiçóÇÅÇ_š¾žÿ–ºúÝiYwEòë*fuiíÕúÛ?Ë6Ӷ¶]vô~Iî2íîþå_…ÓQ6hA V gŒ¥¸E„I^”íÔB4ytú F4S<ó&«/Û MÎ"®Ï< ¼þ|9ü5M‚­B-Â7E®Š–‹ŠgI“ —ò’¶—1’U—“”çV U„—•f”Ÿ¨´«ÖªªŸØ¤a¯©¥%¦Í¦C¥‹ÖÝÑ[Óÿf0o8c4i¡—hšd™l•b•j™¶?Ýü¤I†a¦n–V¶Ú)¥Ó²9’gÄΊäŠä ç  ,(æ+á)å,c;Ït¾œö"Ý%† –Jî*áËÒW”ª5®êטÕÚÔ9^;xÝ«ÞïFhCÌÍã· n_jºÞ|§¥§uäÎTÛìÝ/÷ÖÛA¾“º‹©›ão`¯èCÉ>™GòýŠʃªCê÷ kŒh>ÑÕ37~jöÌò¹Í„ݤã ç©/]_¹½v{sð­Ë´íŒñ;•YÁ÷4ï~x÷q`®q¾d!ñ“ÿgËE¥/œ_Q_?{ºÔõ½a¹b¥ðGöjâZÌzІÇOûMÓ-ÍmÙÁ_¬»¤ÿÆ,JMÄ,Àqãm(N†H¢”‰TiliÓk3Üb’`®beaËd_â´æªæþÆ+ÃçÎ\ @°J¨áÿ–h“X“x“D£ä ©zék2We«ä.Ê—*œSÌUÊTNVIPPóUwÙg¡¡­©€°Ï¯Ã¥Ë¡Ç©ÏiÀcÈcÄcÌeÂiÊjÆdNcAÚ³„-w­¶­·l6m·í¶í·¶:­9/»|=°àúþà[·—î“Ï='¼^_{ÏøÌú~ð›óŸX\šž ™ ?ô%l-ޤ‹ˆVŠ19| 6äHB\v|YBíÑ$šöŸLìOêI¾›Ò˜ZV–~ædjFlfPÖÁlËS:§ås„Ïpœeȥ̣ÈÇ  AáιŸE«ÅK%ŸK?”MŸŸº0^>rqèÒ@Å@å`ÕÐåá+ÃÕ#WGjFj×=¾6x} ¾ÿFÃÀÍÇ£·žÝžjzÛ<Û2×úéΗ¶¥»Ë÷~Ü_m_ïXï\ïÚèÞx°Ñ³Ñ»þp­oõÑJÿ÷oƒ_†>=þ8<;2ýäÕè䨸øðÓGϺŸß›hš¬Q=Uþ²èUÎëô7GßFNûÍ8¿3™Uy/ôþ#ø¸879ß³Ðð©ôsÚbøׯFß–ø¿3/S¯V k„uÂá'Å&~ ·ÝÁüBíB{üÿ£ƒìŬWݾ s¤½` €À?¨°¢ÀNÀü†s´ö7~` 0"Õ¦Rijc` <@08Ò‘Š² ÜÀ8xV!<ÄICzH…xJ‡.BmÐ3h¦…å`8¾?E¡‘šîª5‹Tmè ôGŒ$&Ó‰%a`¯ã œî&ž Œ¦§(! ‡/‰FÄF')ƒ´FéC9AeFÕE­FÝD#Os›V‰ö..Ý ½=ý4C(Ã/Æ3L‚L÷˜˜WXrYX_°`eãˆçäçäŠâæâà9Â+Æûš/ß\'Ð#˜"d Œîɵ£/–ð””Üz$],"«/Ç)·)ÿJábƒR…r±J¾j®Zžú¹}ç5ª5okõhOè,ê¡‘½­lhcjœeRk:döÝ‚u¿¾e„U¥õ„-•±}’Ã}Ç5g Ï®n»šGŒg“ך·šO¢ï ?s€_`[0mHpè@˜DøÙˆŸQÞÑc‡õb[â¤â« «:!˜x5Y<åFšbúý ã̉ì€S¿r ÏÊæŽä‡2ž»_PÊ^öôBÞEç Ê—V—ÕÄÔÙ^—¿Áܰ۸p{²y°õA[÷½¾öñÎÙî^Ú>É~‹ÁðÇÅ#½£?žŠ§}éø¶¹¬ú#aíÁOÊ-—º¿ü3 •É 8oDY:Nƒ2p Q‘†Àkð ‚!FHQ‰l¡@(Ñ„nC#Ð"L€Åa 8.{á?Ê•z€ÚA« cf£ŽIÄôcé±îØëØœî2nEAGE1IÐ$\&RcˆÓ$3R+¢|Q©’¨6©c¨Wh¢hÖhãé`ºLzúr †6F3Æ·LÑÌT̵,Æ, ¬§ØäÙ^²§pHs¼äLçRäzÏ]ÈcÆ óÞã‹åWæÿ)ÐŽœsaF¤¾¯ “[ïÈ”t”–Ú”‘¹"{BÎM^SA@‘F ¥´¥¼©²­«÷±hkªiYiè¤êVéõêÏâÄŒÍLBLϘµ˜Oï'Yª[…XWØLÙ1ÚÛ:ä;>wfq9pà‚ë;7÷0{^²«wƒ/ÞÏË¿#;(1ø}¨ñ¡ká4‡#ßF›Æ´ÆŠ9ÏpúñxV"UR~ gjmºêÉL÷¬µSgs¤ÎŒäFæ³ôœ /æ-yV–uAÿ"t©«2õ²y5óÕ÷µÍ×2ë½49ní4Ͷ Ýi»{ý~uÇÕ®›:zGûæPC¼ÃzOÆòžv>ÿþBð¥Ë묷Í3“³?>RÎ }ÒYtÿš¸tyùñu¡ŸN[Ù;={üGúÊËíE‘tùñÍî\öÞ[»»;•H±ñ €Îà´õ½ÉXÊ8ÀøÙ{ŸÿnÿÒE%ä~õ pHYs  šœ IDATxì½ \\×u/ú§Ù)’m)ޤHv¥Ôž!ªT×_ƒ\Dzn#Õ©iT7Àuò$ÔÞXõZnQj=ˆoú幠ܥBäœ å9èöH±ë¢Æ{h/±äHJØàˆq÷Üÿ:3hñÍ|¬õ›5gŸýuöþŸuö:ûã¬`Y”E@PhDà·¢±ÐZfE@PE@P%¦r (Š€"µ¨‹Ú[§WE@P%¦2 (Š€"µ¨‹Ú[§WE@P%¦2 (Š€"µ¨‹Ú[§WE@P%¦2 (Š€"µ¨‹Ú[§WE@P%¦2 (Š€"µ¨‹Ú[§WE@HTXG ¦ŽáüÃDWïE`”YÚ“u£uŽ™j%èýŠ™{©ƒÀå% ËÉÎØ:"áD#öÝFI…:û©2‹AP%÷IK9 Æ(/QPFy…s›Üµgfˆí£(ªaetÿ'Â&lø¨ÊŒ¨D0épbß-Ú´p*®0'Q`ÂN·‰#2 NÜJ±‹À°rbÅm—QbïÓOØé/ñ”"Ubzc´XSC Ø 3ÊÉ(-‘sQ^}dímM ÚxH%Ê*-(#F™ý–L©²7&JM)P%7E‹4#ŒU`d®ó0|îGßÅ è’®]ˆ¥‹Sf¤ršÉÌ#|²åÄ‘»(3}ñqiNyЕ˜@ÀÑ ¹v*1i˜®™ëJtB^z.[+V`Ù’T¤ç” ]úƒ¤ö£·üœ£íQÿ¨ØšÀð­h0‰?ε5âÔ©ÓèñO"Y|GYN"›¡g-m+#T.ôÆDèÑbM ysÙ–‘aQ` ÈsG}/³z3ŽxyIOÊÊ‹‘í¼U{áZ¸çì’¼gÿW”í–Ì\xr³pƒ4§S"?Ný·Mؼ¹?ŸRú¸L$râTb¢È´'Á¢ J,‚oŽmJ&°é……Tb¾®“ÈËÉÁ¾’Ã8—…ôôtäì9Šs>N•ä!‡aj;™Íª÷ñ|kªÛ¤+åGgãI­®EgŸtsFŸ·ûh¢¯« ƒ'aWÞ“8ÖzETdÀATœ ¨19«ûöW°'o«}í¼’“öÄt>xÃ"¬\”ŠD{п'Kö =·ôô,H¼Il“§« ‹e—°œ=‡Ñvчs§¾Œ|)KRôùÃèôbëÿ¸ˆœÈK¼:K)¬ŠŒ D$ÉwbÊŠA,È0ih¤ÉO&§’—W?FÞ@f5GS¿·\&óìöXÔ1¶ÛSîµ;Žò­òÊ€ÛUl]°³èµŠƒq‹Zzéã<Ó*÷ò)÷öºà…ú@>î²VkÔµ]®àµ`¹‹[˜¦ß*wKn«¥Ȫ/ †³ŒÛ–«°ÁÎÛ[™Lë²Ü.S—Jë•ãÃyÂ]`)ʨré‰ý³àõ»äß!/'‹üÜ@y²{dÄIÛËÃ@{b”N¥˜B@™y{–†G”š¼Q;'æÊe©ñ^j)cTÒ{CH^óZ˳yR…üyt¡îä_`©„#›ëŽ£¼¼|TkŒ>O±×ox°~Åè… WÜn§ ;L¹ÇÑÿÚkè÷VÚ^M{ëìáÆkìH©HòyQ~ЗDsÕ1«l€‡1½+ÑîëAý¡*;]Í™V4¾vEªb¼†þÑATJDÆö~ÿÖ.ŠFÿ®@ÀôÂDnD~Œ,iOì ¨"ÃCn”’"‹Hd”˜™ã[Ï•¿m¿n];Z×eäýró«pDRcËróÈ$bݖǰΑãÈù~h/ƨÃÏ á=~õÓ×ìýŽtžßË  $Ý–‰€ÊlÁ/Ç,æèŸ:ܻ̩þ.àrßÏð²è7bý*)Û*—¹:Ò®`ð\a$Ë‹*ÊKAˆTÒžX¤Þ-×T0‘k9e&Š,<±çŠºj+ LKËÑ82Åi0|œ;¦áóܵYÔÿ•ïç¸xâëDÅS¥¶ÿÆ;n¶ò×ßýNÐ=Dµ#´rÎàqÊ-à_ow?z»Ï¢µ¹ ÍÏâö”p“æ'AÅǹ¼Ã°ï€Ìët?¯uægÇ׿08çÂDžœ²&‰zÏ'æÍgôÚŠÀL"`ÞœE¶[Þ¨Mjâ׺x ŸÛ.JÇ‚—«Ã¦Ï *.ß¶ ,Àa{ üèóuü{ÈGv`aV.ÁÖka ¢_È J2S4í݈=\X²ïs;yRÁ½°;UâºþfÜ%#„(Å׿ù¿ÐðÝRdÞ{/6Ýû¯_Gr%¬…û£úðnlÞ½k»‘Ê™œ÷ì._¾ùµSºÌ^`º:‰œ˜#?ÚN^·ù‹¡•:Q+2À§HéqÉè›Lʯ$Kóy+ùŠõ œ‡’É|ËSÞj‡ v˜ó—­šÜÀ"‰l.ò°¬³VAp!GaýYž÷[dz‹7Æž3Æ™+×,´¦ue[^Y"98•Ãá. .Àè·8ŸÅ²y¸°Ã²†.´XÁR^ ×j8;Ȩ·Õ*u\«ùÂÖQcw¸ì| ô?\ñ_x¼—œNù9y¹²?–gZ]ÜÊ‚’"0 ?H•7i™×´ëÈ7yÏK4·$;ºíI$¥„µØá÷  oÀ´Åi.£¯ 6¬Æ~o.¼ƒX'ë㸌¿¯o~¿)‹™âò£‡×âTÎÂ¥£Ãüòô3×ääÉwFç«ù½š|xNú¹—ÜG–QÜwÉ2G&_óÙo<*E*Õt3´(3Š€S[™¡Å½ÀÄ2KDÚÒ¥¶A¾ñâ'&§`±­¨$ÖÛ ŒNWV û3¯´´0Ù$b1¯Š““§0–*§¸ñm&2㔡¸©|´UT•X´Ý1-ïÕ†GX¢±|µ´ž„»T¢¼÷zÜõàV.FHÉâ vw,XO3¦Ê,‚o¼*±¾9Z´I#`iˆŒ[2·ÍbO<”„’#/ΰIÕk¾P%6_Èëugip¤ñ1 ÙFCç€gõ(Ï;Ì Ž‘¥(¯]ìßt—c·†Z³xDÀ(0óö<íù¶ŠZÂcékƒ›¼²vY Å ¿ð°cå(Š«»EמXìÞÛx¯™yƒ6ŠlZx|ø~‰õ‘ðy$¯DQM ®[¤Tx¢"D—‘#CQQðx-¤öÄâõÎÇ~½Mc$G!s œÿ}]µÈÉãGÂ{ì½½²¸ßWã铨“%{y% ¯â4—§ÓªÆy/^=™–áKΗ/Š/Ðu²YYûÐùîÚþµ oÓ;g óÜgç!{‚ÕvÊŠm¥(AÀÈÊ´{ðQRߨ.¦*±¨¾}Zøq0oÓ¦A2ÇQI†._BÕ‘ÝØQ š›k°Œû}mÚèÁ‚ÏÔ£¡¦Gò7¢Š{˜ô¿Q…§;ßÂò>…¬¦#Ø´÷;èëyŸóìEê£Û°oáDéA¼òÖ yîÅŽo -Í(öÔaû£Ç‚–>F]^O"#+F†"³”Z*ûPAˆULC$sÒÓ ¹ÐrìIl ]†ëÊܨjüs}ö!$ú—ÑtÔ~ô_–o]ÑØ/6$®¢é¨J”ºhNªŠÞî2tçe°ûÕÆ“[…b(¤ùùgp?ëºë@9öºêñúÀ.dˆí¥HF@äÆÈ‹‘ãÉåŽÛ²iO,no}LW\ixœl¤1C ;hH7à-Ö_]Ykû‡à´6o&¯ËFíI •—åb± >JžãŽàwÉÓ¡rN ŽÈAÀ(0iu81rîKØ’¨ D!¢´ ½o<’pTžoîÐ<:sgluGÒ&š!Åñå&rÊ×%Q q}ûc«ò\/ Œâ+ö×ÓJ¾‘ü!rÍÜ[±çU•¢ù¤‚Än6.‘å›™ÈT+ö!’I{b‘|w´lÓAÀ9,$n344<5mü à”Ÿø©uÖT•XÞ4-ò¸„k|Ä_I˜ce%œLM$/3˨›e€5{E@PÙC@•Øìa«9+Š€" Ì2ªÄf`Í^PE`öP%6{ØjΊ€" (³Œ€*±YX³WE@˜=T‰Í¶š³" (ŠÀ,# Jl–ÖìE@PfUb³‡­æ¬(Š€"0˨›e€5{E@PÙC@•Øìa«9Gþ7q´¨oÊþ+~´T— ¼ñM»ä¾7QTT7ýÑP-£"Ÿ¨‹Ïûµ6¶Í1týoáO?WÞ’Ý›}xõ«{ñÜ+oÙqýï´ãé§¿Š·®²¿XèŒÕ7Š02cŽQ\•Ø/ºZ±ý{75T+öqs«g¥¢A+öÛ˜y/ÙX±ÿ5ݗɲ]÷wAPÅF "‰´'IwCË2“Hcc‘sãžÉkh^±‹€ýuÊPìÖ4Êk¦J,Êo ÿ LÃ3ÖòøXËäW$TE` ¦}Ô 1ÀDÒ©l ¨¤Ä ¦±1oѦ^F±Y2RefPÑãXŒüˆ¿‘#/ΰ±éô|P%6àë¥g ip¤ñ1 Я4­9`¿oþÄ$ëS3k7n>3ó‚cdi>‹¦×Ó]'Š)Q‡€Q`æí9ìœØ»m%HH(±gñÇ«åÅS´  ž/š†E?Ã/<¬ÊX9ŠþÚÅ` ô2oªVÉFÀ¼AE–ä?€š:`AÈPãéÇÊ÷…õ|jƒñÔcl" ŠËÈŒ‘¡Ø¬iŒÔJ{b1r#µW `#9 ™cà,øïï?¶WÏÛgµûs %yYì% =ï0ºøXgí±ƒŠ7£¨¶“Žœ:œgÇ‘x{*é£CY Ûƒ¡ºF}UT‰Eý-Ô „AÀ¼M›ÉGE÷÷wâàÓo@>uºôTíÏÇË«ž@KC%ÙÏiÃMw|Iå)†çΛpú°›w·àxK:šËQš¿ žÃm£òÕ“¨FÀÈŠ‘¡¨®L¬^‡cýÇoýLC$sÒ…n’®±J’ÿ÷úìã¨}räÁx¶øv²'–¶æ~luý›7còóÈÙÝ„¢æn<¶a1c­Á™ã/auñKØ•ÉG)š¹1òbdÆøEs½b¶ìÚ‹Ù[דÆG'›),0C q­_c+0‰””¼(wÈ6×€÷chÄ÷ƒ#ïË2î¼?F·ÚX âÕ£À¤mÔáÄ(¸•ªÄ¢à&i'Œ€(-CïÒs†9‚Æ8vE©…£®Qb¿ú—¨ý~KF¼Â%Sÿè@@dEdFXHŽÚVÚPDޟޘȻ'Z¢é!àì}Iã#oÖ3'ç)+ñ¨ØýT5ΉÂëkCéö*`Û:”8½û)©òczï3'?‘RË*‡¾;ÆÐÍÔª„E@¦ð´.dÏ‹9b9ϯIeÀ5ⓆǫšñÒ²{±bA~ 6|œùâ=Ž”êŒbDa™!Åñå&Š+KEŸ–ƒXBëýp¹»4@òb&«5äÓ¯ëÉ¢~n$ˆ\3sFÈ}è¹Ø‹!^fÉÒ´áy4^C)JÏ%HÛÉ—ÈÆŠý»tËâUµbO"‘´'‰wEË48‡…Äm††f"o摌ÅK—ÎP^šM"à”Ÿ,žÉ  c½ =Æ áñWR&‚ÀXY 'SÉKãÌ2ªÄf`Í^PE`öP%6{ØjΊ€" (³Œ€*±YX³.|Giq+Ú¦d Ñsm§ÑÙãøØ,ºª¯¥U¢UbQwË´À³‰@`¥S?WN…ü8•¹k¿ùúTkE@˜ªÄ¦š&‰-Î5V ‹Ë«ÒsðÅC‡X¹T¾ó£­öÒ%,!}”µú.¦Ùjû' ‹Öî;Ùsë¬.„ýåØnœ:Gë÷ûWr%9 Øjmý^âeåUìiÖwyY98eM[XkmG@¾›QV bAøpÈK™|#&߆ÝL^E¾“ì&o#³š£©·µÌþ°5»¸Æ¢åzËe/Å÷X­ý–u¦&×+«k±ZÊm·§ÜkYƒ­3´ kZ¬³gš­BI“]cõuÔ[´vo¹rˬæŽnË[î±Ó¸² ­š– VK™›ç.‹Öï-Z¿·ÃÜe­V¿7à®ï”í¬}Ý\ëÌÐè²êÙì" ò”“,EnD~n"ÛŸºóø[,¶—†~'FÉTŠ_Þxé·X9ŽÊ åú—Zû‘šI?ôàùGà.oÅ®-<߀Öòzd>ׄ¾ÇÌÆ˜IH]¾û»;àùy®_³Ì¶vÿnܳf1Úÿ…VñÝåxéXR|íÈÙÚú=vyPÀ+ÿ—.<´mšž;Wq VéÓ¿‚©5Ÿ0:œ8a¨4bì!àÃÏ~ØWÖˆåz·fﺼ@S~æðæ—™ùÜs;z)ÓŹ8¸= “°àÑCèúu"­v8¬Ý¬!Úxp=´>`Sq\ë÷Ë‘SæFÕs§10ðjölI=¸µFŠÀ,  Jl@Õ,£ß°¨Þw~=\à$c011à(¨;ÃÝWÑ?8ˆîŽÔÿÍ}X0ЋŸúýûqÆÛŒòô×±ãÞRœ›ÀV,á¬ßì‘?§ÆüŸ¨¬¬€…xpMòp™Ô¡(áP% ‰y’ñûÛsý_@mg࿈oü­,ÍàHâr< ÒÒçqn0)CgQ¾v#6W…ÿlÖ®^‚ïŸã¤Éº{ðÐ'6J¤£Õ½KïtãŠEöW±~Ÿ¼êPìjÂîÝGà)ß5hó§œ)t¢R'jcEøLÈKÙ¤vXV·u<×e/²`ZË•-‹1²­ÖA¢Òïµ ]ÃÍ-¸Š¬.ø4•Ž4’®°¦C¬Öòl;/w™×òr!‡«¸Õö—¿¡ ÍVv`ñ@àzžbëŒ\'HÇ IêÎꊃÉ\å>’ePYvDØÊBÈE5jÅžÒª­ØË8 (²kÉבee™X±—mšÃZ±ïë¹b §¥0š“üèëé¶ÃÒæ\k!iú/ó — -e$Äçó!19yTÜ‘Ã[¿o¯È‚ëÛŸAãguo²ÀæÌå°bßË‹²k®ÌÁX+öf£Ì9+—^h|Fž¼ñãi¨"Ó¤-7€—ˆpa⟕d*°ðÂú=W.îût6ÖyQÖú¼*°ðàiˆ"p:'v$ê¡Ì1‰‹°qóã8ÞÐ'2B©Å9.^Nˆ"´'E7K‹£$.Å–¼]1Z9­–"0»hOlvñÕÜE@PfUb³®føûºpº­ ò™Ø@ûaÚ`<Œ)Á†ké (i“µJŠ€"0ªÄÆCGÉ pþylÌ|ÞVbvtZú0ßLO$ù•q¦—úÊüÔGˆ]T‰Åî½ÕšM_W-rh…¾ºbm^*+§§ObO–X®O@^Åé r mÑÞ×Y‹]{y¥½xxOµ½xß:È/!!'åCj›B[±— ÏiØš¸fÉ´QŽ·Æ1Ÿþ+Š@ÈÇÂ}T¦þ¡?¶S\">ÎòR6©yðÃãææšá‘ +ë­†š"û£äÊŽÁ°í‡úÏZueò‘r6-ÕŸ±ú‚éá)²š[[¬²l~@ë*³äépVì­¡‹ÆA,¸ ­†–«XÒ𼬵—©”æ Áœ¬;‡ù¨˜÷!"õED*RÁÒrE¦›ûÂhŠJÌeµØ–8hqC¶Kñ·l›C^{k•²ÖŸZÅ´ÜA‹ö¼T€Ze›*'Q3CÜÎÅUnŒ|ȶ*.«9˜ßP‡œsk—n¯­ ‹šƒû­0Ý™ã´îÁ<ºmÅ7R‹×•­^T‰Áž£ƒ*±È~¾)!õ•'Rr•âššÇÜ4Ô!³QÃVíýC¶É¼×Þ¢=ãÊ6ÐÞ÷‚»Aò»#˜Ÿ&¹0¯ Œ†Ž|Õ²,ãn¦û1~ñ~ ÍG±ÄxÔ…+í/Jz%E@…€*±QpèI|"0b"JôÑÚc‹öFïŒÆm$¿Ñþ@(+ö‹mCFí¸$ËmêÖ91…«  Jì*i°"€ÜÖ¢½Ž­ùÞA÷À°º´¡ëWâQŽî~ªçÄÄ}_J·WqæNÜrëíô¨BÅwÚ¸ˆÄöÚ¯áÈ9¨‡" „B`dl#T¨ú)ñ€‡î ^Üž„ev@2¶<Ë]¾îvauª¬D$¹ŠÐñìC¶‘ߤÄîân¬¾û\¤^‚#?‰*6ˆ“’ÒðxU3^Zv/V,í^Hžbœùâ=ö2ÄŽº"¬õd¢t‡2¸&IOACIµb?:ULÇŠýÄ*Þ¢½Ÿ–ëý‰‰H&Oá­ØûzØ›.]¬ËëÇqVBÕŠý¬À:ë™^퉛õèèA ¼Eûð[¯Œ­]+öÁ(‰)‹±4ô$ÛØLô\P‚èœ˜Š‚" (Š@Ô" J,jo\PE@•˜Ê€" (Š@Ô" J,jo\PE@•˜Ê€" (Š@Ô" J,jo\PE@•˜Ê€" (Š@Ô" J,jo\PE@•˜Ê€" (Š@Ô" J,jo\PE@•˜Ê@|#àG‹ŠÐø¦˜–÷£¥ºåoÚ˜øÞlDQQ5Þ oœ>¾±ÓÚ+€€*±¸ Z„YA@¶š2ÇÀÙØÿ[øÆÓOã•·âë_Ý‹ç^yËŽå§O?ýU¼%úM)ž02cŽñT÷¨««Z±º[¦‡@Њ½µ¾†¼€|=9•|#y¹V¶8WRB!´bÿ)†õ’ß&sKnüš|™,ÛoQ~T€D$‘öÄ"énhYfil„eßd9¾OVR®†€È‹‘s¼Z ŸGT‰Í#øzéYAÀ(/ÉܸEIƒ¤¤\ #+F^Œ ]-†ÏºŸØ<¯—¤Á1$aYš1D¶8d”`"èQƒ€ÈÈŠ(2£ÌŒsÊÖ˜dz:Ÿ¨›OôõÚ³…€ixD‰I£$Ç‘Ÿ!”|+ùf²øÉ|ÙwÈ—È «’#aHð‘Ñy!ˆF¹Yò Ë’YÑó.™{jã²øUdôRŠTT‰EêÑrMQLw×ù6W‘?L%õ‹àñ?!ˤýä³d!^àî?“÷‘_&·„‹áþæÅF”•,Ö%fX”š½€ƒGé‰9_†Ä­¨‹À›¢Eš<%ÜÄTÿL~‘ü:ùgäÓdQX²fþƒdY©(+¯#ËêÅO’ÛȦ¦JŒ`ŒCËVKv“å@°Ž&2JI”(±ßMo̹ Ñ(2Ó‹WÁwY•Xß-ÚÄàÊç*²d QXÿ'9‰,Š+™, K—4TBÒˆI¶‘\A6JL†u8‘ „ Áó#ä¿'·’ÿ’,Cp äh"é‰Éý%,r`zbæ(~¢ä$ž°¤QEF"‘T‰Eâ]Ñ2MÏ0¡ô¾þŒ|”,”4FÒÖ†H$ñ»,–4È&\ŽJ¡¸“Þ]äóÁà§x,"Ë·TõA¿h8ˆ ådäCz^¦Wæì‰I\[é7bD"I•XÞ-ÒÔ`#ó.{c[™ú_É?&‹‚’·jÓ»2 ˜4ROåÃÖM/ÌÄ¥—’Ñ-¸ ^Br, "‹"û9ÒIî¿a£Èä…ÆôÈD.D^œJÌôÂ䨨Ŏ¼)Z¤©#@%&Jèrù.² /&Y^ÚdˆQV$Ê|ÎCd“˜Qbt*…@àÿ¥ß.òÏÆ„ÝÂó¯‘å¥ ÒI”˜S1‰…&ÊLX”˜ø–0‰ÿŸÚ# HÚ‹À›¢Eš:ÒÐP½À¾AþÿÈdi˜Lã%Ò§ÉÿB>Oå¥JŒ ŒC †½"ŽôÂ#'ËRõï’#•D„LoÌVN<7ŠJ—(297¬ Œ`D2©‹ä»£e›*ë˜P•,©–¼›,$ “(¬GÉÏÅ&žQbt*…A@À4EI…"ù\áȲ°FÏE–Jüœ$JMi4nžÊp¡,„ G¢Èþ,½àËäHí‘¥ä<ŠÛ(,ã–s°coÅ­™*ˆf IDAT¨‹Ìû¢¥š$T^²8ãÿ&ŠìfãÓ.YÐÿ“<˜…?¤û3䯓eò~¬Â{Î(qO×ß% ®‚ÙxÔÅ@éáV’/‘ÿ‰Id—³Lâgü‡Ýª¼œE¶[•Xdß-Ý ¢ZÄhb:J†€2Ùõ™dtÿ”áÆó²4Æ2“I–ù'©s¢1â¾Î#‹I¦±t =î"ßO–ÞÚÿAþù¯ÉÒ#‹d2Šk¸Œ”•+ü†Õ±¨‹Ø[£›TPÒpʪ¸o’ÿŠíÐÃ?ôûã‰âúùG&XOc Š’eÊöqºu£8ɇËò-Ò1ò~f{Åü×t¯¡é'ŽïÇaì\Þ™SRæQbeåüæÆé6•S%fˆ£Sy·m÷d”eL–ÆËœVÓEê7HñqWƒµä=YHçY²Ì‹]팥E`v˜Öpbå%ŠKX”U(–Ú¨"bŸœ½$qå5ì¦üØ(Œ§ÌGdT”—¼íßǸ?±éß¼#À{ÑËûó3äãä–y/ .˜² *0£¨Œò’£Lº›s «Ôâè8­´(,a!ã6æ|ä\äCl†´Œ@ÿ% —ù/™›YÏF3Ô·J RšG~ÀkßOV%F”æ) '˜”Ö©´Dy ËD{à›%E` Ò#K#Ëð“(4£Ô¨£Fæ¸(còv/ó__'ÿµò¨að>yX¤'x{>aEÓâÄ ÓQb¦·e”—ôêäcÈnmoâDz¦PM6z’j1Y”˜X —£é¥ÙÛ]0Îgé'Kè§,äQ)Bེ‘E;O¾‘÷JçÅ"ô>År±¦<œHP¤5%&ù\C%6/äèà ¡Ä”4¤L§VóRú¸»h2klìð™–(²D6Їxüò=l;yTŠ`xÞæ=û)‹(&½^Žà¢jÑbéMMŠ(°FyIZQ¢¼„¥qšSò{{²° u!.\ˆÔ¤t¨•o0…ptk¶VØ“*?ýDNä¥G6«ù‘¡[ÈMäeþKˆ’û排²j1c I+±`ýE‘ IzgOlí;W}/âá÷¢Ô~„rQXÍ+{±» 9G"£Wÿ\H¯3ADNœJl=ÏO“eK±À¡wŒ@D©‹¢›kEŽ«À¤Q ÙóuD^Nö•Ƽ,¤§§#gÏQœóùpª$9 ;P+/Þ¨ÞÇó­9¨nëã¹'q´º}2Ü>ú¼ñ+OÛ¯î®Üãèo¬À3‡Ž¡·µœñ€ªßÄEÛåøóÃÑ}9Hgg2== yûŽ¢SÖ½ tÙaÒÑLHH„Ù×zÚObÏÖt1†Šô¬­8Pý"|Tú?5œJì3ÌBV ~ü¥©e§©æf^#ŸQRæ¾õÊòæ 3K'½0’†H&ueh59|™Ù¦~o¹%þ6»=M_ÛnO¹×ì8 ˶Ê+ nW±uÁ΢×*Æ-jé¥óügV¹;O¹·ßqÁ!«·»Ûêîî·†¬~«ÒÃ8îrº†¬šÜ@|—'×Êõ¸ìk¹ŠÄÜU_Ë-*¶ ³a(l`P‡Åþ㺬¢²b‹K±ìte£®é¸¼:ÇE ("'™äj²Ì§ˆ[äIäÊ^lÄL&,“wþ±â}ÓS2©÷M1˜SFc*$ŠÌôÄÌQæ7Æ}såÖàtÅ6 >ŒÔ»9µ?„ä5¡µ¼™ùUÈß)Eq¡îä_`©8‘‚ÍuÇqÃ/~ƒõ[¤üâuø< ÞTñÉÆúÛar&í`ÚbYü&dºYâöã¦ûÊP¸ÈÏ>‡y¿‚#u^x›~„¾§îÀO[$°èÖãOù8–ßý&nùØRø/sY„[ïxÖUbsïõX¿Hª«4EnfºÿNþù“ä²È‘¦¦S)ÊøË{?ùE²’"0wLö͉%“ÆFZðëÉbŒU&â?FÞHÞLf–£ÉôĤç%4|^ÖŒØaå{8(­‹&BýV¹ô²˜®²cБ`Ð:ÛÑauœ¹0¦'fYgêË,·+FÒÙì‘^šeu/œn\Öp†!¬rÓ3 †¹<…Vóç5—Wç¸q¿Äãß“EnD~DŽDžä%H{bQø&Ïûö0¹‘7NßÂõzŠ·(¤éé‘I>Ò«¿gÇžW(ꪭÀPZŽFy/7ä÷Áǹ³a>_€å+90I:ôÿ4ˆ Ðëz°aë”ÈÃÂ{m¿‚:mÂ>qþ¹®Åßù)ª¿RÊuŒAJìGEæFlÞîÁ+ïÞ„>yw@Á¹"é—ßCæÆMØ~àá7¾ ÿe³t8Ôt81ˆÞT¯2‘È‹a§o])öîôç *[ppËr;½ý'sgÉC~czK±‰ ë¹7–U]'.ø–£ ¡Ì>ßëÙˆÌMùTp.”ú >ºæQL·9Ó…Íù¼†»…èŽ\D]“@À¼ôL"‰FšXFw”S‹CLÚì—ÑŠâ“aCÑD²¢ì:òA^ÄcŒSÏ-ùÐsñW¸<”„Ô›–"-äBÿ@‰úz8ô˜ŒÅiÎÅ ÁÒúÐÓ=@[HIHY²x”å?-‚tpƤXºXLÿ)MùL´Üdù&LÞâe“K±â1Dù™kâe•¦‹ïí'˜Ç>Þ¾û¦›—¦W&ŠÀL(1™¿!È¢Äjµ š(üñ/¨Ä¶±æ¢ÄúÈ2N,Jì2Y•AˆV⽕Únò"¶މìh­‘–;!FøÈ›²ayµ6oζ…Ù~ÝŸ\Câ#+‚ÿ¤ÃÈ’ñÓc"@Åõk>ú²âé.rcVA‹…LE‰9¡Psj“îÝE!nZä)"|Á1JK^vÌ E¡)E7?`ñï'«#J³@(%4™«š·hIãl&“‡Æ?BÉŠóå(þ‰7±*îØ©ŽÖ$Ò˜Ž“´²ÊL$“Ï´¢¶Š‚¦3B!Ço½Ü©,k—)¥(FÀ)'F~Ì1Š«¥E'ÿJÎ`[}))³ŽÀT†¥PÒà˜¡iÄ-~Ó¢?P¯Ä>>ä•(ª©Áu‹¦ZìðYkÈœ"0VVŒü8•ÛœH/63p^ì2ؘÛï“ÿyfrÕ\ð˜Tø¡CLc#GiÆýÈÙ×U‹œ¼Ã¨®Øc[‚ÏÊ)AãiZ†Ï‹ñ È«8moïÛÞ‹WÏ_ƹS%´Ÿ‡Æ‹b¹è:Y‚¬¬}è|wmÿÚ†·éȳ„yî³óHHØŠÚNYì¦%8™È¡È’‘«(©‚3 Môw‡ SoE`F˜ªsBž÷ÉæmÚf»‡._BÕ‘ÝØQ š›k°¬j/6mô`ÁghÊ©¦Gò7¢ªÓ‡þ7ªðtç[XþÀ§Õt›ö~}=/âsž½H}tÖà-œ(=ˆWÞD ϽØñm ¡¥Åž:lôØhSSW”D="‘£¸Ä-äTlýFT‰Eã]‹Ò2Og\Î48r4ni” -ÇžÄ~c|]™UŽ¢Ï>„Dÿ2ZÜØþËò­ë"¬°‰«pÐ[‰R×,¬¢·» Ýyì~µÙyí}ÐíBóóÏà~{|×rìuÕãõ]Èñ³Pÿ" ‘ó"5ŽìDZ±µŽ;‚Æ3¦…ÅøƒR”!`zðãÈO”Õ(΋üÐùÂpwœC¡ÕŸ¦¢ÄL±Œâ2ŠÌø‡9&›¸·ÎU¾ç÷u€çHÀDo~É·ÂN§+¦ ê=78eOdh‚r47…Ó«L0‡û§‹f \gCr•¨£‚Í[³ixœ½³Q§vr_^MózÜDó™ j'ö×vM(«Ð›½L(©Fš;D^Œâ2²4Ã24w•Ñ+…D ‰¾î!ê©Ì SUbc‹pue´ÓÞÄ$ ËlßD{§MqvV?ÃY27¾´ KWmCK™¥Ûÿ§û’ða;nðÏ‘gÀ'I£3wÆVwä à”㎜ÒiIfc&ë8/&¶U•YC`Ò&¢(”¢øDUÈ.¼òA£Ìt ËìÔB²&J¡O*HbÅþ-ò;A–ÉcØÏ9³Z‘ÞJÑŠïµôÆJx?룵ZîÈG`:=1iLz3dŽ‘_s-á|#JvT~æû®ÌìõE‰¹g6KÍM€iHFûê™" (ÓG@•Øô1Ô®‚€*±«¤ÁŠ€"0eN3åZ+¦N9M¨\UbW(Ò‚}Ü™úâÅ‹dÙ¡ZIˆ\8ö–N,zß¹¥Ô’E;q©ÄÞm/§½Å´G•èAõž,,X¸Ë–-#/Á±Ù´Ik&Y Y8-û$¡¶Ö·ÄÞFyLÐNý8×v=QÖê¥Qæð:÷Ïѵô2qˆ@\*±À}>_ì GÁmïCÅÖ%ØQ T6xÑÝßî³­(˦½H×BíÑ\I!6ÀXpë8^ó€½”tò•õãTæF¬ýæë“Oª)@çÅT f¹Qbþœ,É Z›OÀž£/ÚCa¾ÎjlÍ«¶Æ1rîÃ)ÆßSrû¶¦ÛérÔ¢Ç@á;‡£û¶òËÊAõé‹vˆmÙ~Ojеü­hcÛ~îÅ öPÖò·Ò’¾Óн|#ÿETÈ.[^ÉÉáët5V`k0m–¤ êŠpþ¦xrì¬Ý‡œ(É˲óNgú.vfÂZèg˜ïÌIäå•áMQ®¾3L›‡“gÞEÏ‹_E~p¼ãûølÖ:,NIÁâåØu¬4C‰û=ÏT¶/¹Í¶ùlñ!ìdo+¬…~]¿.Ÿ·ÝïáYêØ˜ uzŦÉ5ö'mOçŸ÷—èeïíý*;¾…mkˆË†gQþµ#x®þu<¶™ç¤Dö1Ÿ?pîòVìÚ’AŸ h-¯GæsMøù]×à }¾ý$2ˆ]F™?½|ﯸ[©Ùÿ!Ã{Ö0@I˜<|bí—Ü““Oª)ñ˜“áÄ5Ÿ:€"O6¯]†$c=ñõÓºVÔÊh ê°aO÷Ê%Ãî›n§BÂ)¼ñvÀkûêÁ!Àe4QEzÏXM\„í6{?<å…ûÑL[I”Å÷ìBÅS[hÒÅGöwñ;—0¯$.šX‹ý^`å ,IÊÇðdq.nÏĤ,xôº~ˆÄpþì†ÿ¥4®õAKýt'%/»º…þᨶãr?Õ“÷QÊ×ÄeéºKlÍï†k…©] Öï Fv’ïºX¿¦üÌ v È”qÊEÒ’] 1¬§’×á™cOa]ò2‚¯3Cu+BÀ(± EÖHŠÀd˜%60p-ûj?Ù·6ÇM¥»±³’­©M>{WgqôüR:RÃtiØÅ°‹?åÙCXùA6ÚÒÛºÀ1¹Àap°Þæzì÷Üæˆ-Î$,¢WÓ¹á™4ôµŸDIp>.yDz=8òH Îöö³ÔJö:úÙžûz±âS_ÂÐP?Îx›Qžþ:vÜ[Š®·CûŸ“y¬±$=¯ +§µÐoÒÞzçCtîÅ í# 8açP»» ®mwuº‡£øà}Ù`Ì)‘Ê™TPw†õDÿà º;ZPÿ7÷!Å.àOpÉÔÃß…£%Gíy¼`j=(SE@¶eYÅy±´©f ép̉ûÉ×Vcõ•è]°YŸÄ}œ¼±Én8÷¢®­þ¾6üÅ&hÙ½žq¸Ñ»û/R t¢|à Öà–Û6pM/*¿÷Cø¹ÀaàõïÁuïf¼ðKÓúrÄÏÎ<w=ÂÙ§ƒ{p²‹ù3/»<ØK]hú*¿ ªÏìÛ?Šåi)\RòÎè‚ÿlÖ®^‚ïŸãÓ·î<ô‰v®ïÿ<´ø±>;™ã/¼…~ß›(*ªÆE©ŠïM-*Aã›>ö ³QL従å?úb|>*üv”l]Á!@7åÿž?!Ãæ¿8Цïiû.v²“õø+G®¸ä¥¥ÏãÜ`2×Y”¯ÝˆÍUgqý­wðÕ _ÿN;QñáÅç>‡{ÿ]UfÆ·‰Kït‡ì Žd®.E 4œk‰~‰|èê«LÉNæñR¢ødüIÚÌ›ÉW]Ø1x¶Á^À¸2ÉCζÎòÒ¬òlãË- 6Üå—"X•Á…#i ¬Öà î³€#ÖSÜ` 1·ÀÂ啬mêµj ¹(Â\—‹AZ{‡†PH¼ÖÊGx®UT ñ]VK77È¢‘òÖt0×pþÁK^.Æplˆ—9ï8. +ÜVCp-ôóÜcµ°nÞRº N¬Vë~^»ÔTºÿŒU–=º´ráã³·ŠÄ Æ€?iö5üd¾)öõúz.¢ŸöÕSù‘ñHùÀ2‡ö•ÿLú 𺽗Ù×$ŽËˆ£AÌy¾žø¹ ?-ÐçôwFÒZ“Z»‚ÃŽ‚y·'˜ŒÊƒ¸ö ø‘’–†dG€½¿Ää‘M×›Œ›CJ};¹—Ìo/ïy—ÇA²LÌÉûˆZ±'±D¼ï¿ËúüOÞ[W,ÕKë2ÿÌ¡›Lep8!»‹[`=)«•¦Š€¯³¯ÝŽ&W9ú_Ë^ä2Õü¦›N•ØtŒÎô¼ï`Éå¥e%™sº;:+¤¥Ždh0ii®CÖÛ#°lÑU¤ÄEÅŸWÖÀûÏ:ï ,ºÓÒÎ$T\ï3¿É÷Ïd¾š—"¡=1½1±Š€öÄbõÎ^½^¼÷ÿ±VP¡}áê±5†"01"´'6±Âk,E@ˆ*šXZ~Ä¢¤ÌªÄfKÍIPÆGàG þ0{djúe|œ4t¨›XUP¦Ž‡eÕ鿵76u5åT‰DOE`Vøs¿V¯ ™ÇªÄâêvkeyG@çÅæýÄVT‰ÅÖýÔÚ(‘ŽÀ«,à-œ»)Ò ªå‹T‰EÇ}ÒR*1çÅÄœÏÈ:/wtþ+1U%Ê®Ôü×FK ¨lÅÂ]¿:¤8>>: ¦ªÄäÒØw¦á1ÇÑEð‹Eö"Û"»ØJl©.Ayã›vc¹ýM±s­O„’ÐòO¨ÄG]›XMí‰ÅǽžõZNG‰™Â™†G¥ã9êè ßxúi¼ò–ØxõáÕ¯îÅs¯vHö¿ÓާŸþ*Þrì½5*­žÄ""3"+FvÌ1ëªuºvz}ˆób·\¤>ŠÀ䘪Ù)±o.VìÅŠ½X°¿Š{ÆPŠ{Øp ÆŠ½X²—]NÇZ±W¥&(Å0Qžgõ¾Ë)²ã1\M­Ú àØlcÊW3½/ix´ñ™2Œq•Ð)+"?F†â geÙ¨ ÌÑ‹îa¥îgµ¿‹•›á:ÙíjpQÌ gýÙMU‰™FHŽòÐÙ –ãŒ~„´3€‘“¯ Cy2~qq >'FiÉÑ9¼/ç&,ñ‹ö»ÈI±X¹¨“yN†”óœØ~ªÔ(OE‰P%çCgîÛ¤‡(MB=Æ>Á†Û<ŒÎ†ZÜf±GLáP^¦þr”gÉy4a‚…Ó-ç±@?c%’W’ š¡:˜gÀ<#æè|ٳݔ#Ù!ÙÙÏP¢+›©(1g °â‹š³®êž9BÉJ\<Œc˜(.Ãò,E&H FrnHÎc^b…6‘kc­bÓ¨yä(í«a9—5ܲ/›È‚í¯Š £w¦'0“!yÀÆ>læL8_Zß23V!Ñ߇¶{+/icÕ«¯ î™ØêÀŸáëHÍæŠ€×vMb£G?εµbðÖt¬Yœ<áòiÄYAÀ)'¦±6GyHc™¤ž¦®ò ‰¤ Ë®ÇâoôIVø+ñVé)ÔWžé¹Êó1äHïìH8¼ãÇ9VUL´æò° xB®¸§öž3AëI>Éo óÞL´ZW*±ä•(ª©Áu‹Xä_Êe¯™ä`º§27"¿Ì k×:É@iþ++F~œÊmþJ7KWöÂ$wQ^Â2d”˜Ã ¥ÐeGV„¿Œ!ŠLž Ù‰ég'Xçy¦B0Df =²q©óäadq —x#}ë>œ¾è‡¯³ºö2Ý^<¼ûþûƒÔf¤Ì {ÐÎZINJ*c+Óþ÷óhû×6¼-ê$ÞKï·Q´'ÇÎ/!kÏ Øi;«÷`Oµ|† Îê}<ïDgu!òÅk· N£Ã¶ÚH·Ë”Ž=äH§ÿ³Ž€S‘‰Š,¹šõ‹ÏãŒå%ÏÍÉa†p4'ðÌÈscxëžÃèTaÇ[8o—%&,r#òcdÉù,Ñ;¾hªJ̉’4<ÒÅ5oÓÎ0ÛíïªÆZÏn¤oÆ™3­x<å 6>Qÿ²õ((Ëeœl<ñØ&x Êèv¡ø©?âÎyïàåª*ìÍß›Š+q÷N” ~0-Ù6áà[ëÑâmFQj)6­Ø.*¸Ëo5¢ôüe‰`Óå·^àù;X–ñ xèãÊ-Ãý+®EWí瑹}?¯kAkÃ(Íß„œŠåL®‡ÙC@äÅ(.q ÅúÃ(õ3l˜ôÆÂ(1¾v••ÛãA6ÙÅÈu¥»±ö‰jš PŠ3DN„E‘ ÷àã ƒ+ª;%fóPJæ¡ß¤f ŽÊ¢è±{°|ÙJÜ¹Þ ¼Ñ¡”åxøÁß¡f¹¸Á…õ»i‹fØzÒ‚uAÝYT<ùYdÜèìÉÝ ô¨ pöØ.lXwž:Ö‡»/p¾ Lïr 4&ÏSÖÜ­¼ì¢ 7îY<àÜå­Øµe2²òÐZîAÝsMöÄ„} ý› DvŒ †–dlˆm IDAT¹(ÅÜ^Cê,ÂlÞ¦E¤Åh@H È»Ï~ëŽ8×ú[ì—1TÕã'Ôb¾‹§q '+0‘•ƒŠS]v>¾®“ÈÙšƒ}‡+žxAèÂÑ}ÁÑ‹„täí;ŠÎ>Ú ùºP±g«£·w]A-Ùu²[s º–£"ééHOÏÂê6{•A ±þÏ×ò:Òs7ÃÐòìˆ<±“ήzœ’hóÉ’,ÓðÈÑ3Tf‰‰IøEÓA¤zêF‚ÝÚ¯ƒ2²ë}Ïž©Löæ+‡Ä:o“˜rX¹Ræ2Ck¥=Ëi‡$%a¥í>Žè=¿l ØáìÁä÷x ß%tyÙ—ËÏDB¾#²{³ãDs„€éÁ‡•Ÿ9*Ç\\ÆÔÑ(0i„ºjœ É£Á;_oŸý\×âZ_;v/Ûˆ# rggãG.ò›ªð^k/v&ýUuUì¶‘%|½§º°ó [TŒE?=Žƒw2íG`=³‡^Ý|TàöÀs©Ž½½¨{í=ô6æáò/^f6ôcVÞº&xwdâÖŒA|vMðmÓ¾ŠþÍ2ò²#œ°H„(1£ÈèŒO2oÁS©½Q\r—:ŸÿKä—¦ ùL7pX¸PW\2ó“W&¥N¡¡+“èu¦$ì NN6 àõØûûœQ© ÔÁ5fÿà º;ZPÿ7÷Mbµ£…þM§ì‰ ]Uަ~©ˆIi”˜¥þ¢ÌD•Pz9©Ssb Vlæ@:)÷Üü³&[!û8¾]Y‰o×ð¹"}­ö´=¾oŸ¸ Ñz¡ÿôÄjü´ÅöÁ¢[?Ž?Ùså啨ۼï¶¿P`ž2t7žÀ‰×. ØÍ¸Mù¨k—Ñ;a!GENœhDC‘ò%ó²¼m*Í!"+c{bªÄ¦xÌÃh9KC¶¾ºY¾àðÇ—=¥2êÓ·;_ï {€ÊÊv7¡óõž°yä^îÅþÚNÐxø)>عxÄxؼ_«çPˆ_ü{ä³Ç•j'bþ—ø{§¾Äåx$(-}ç“‘2tåk7bsÕY;¦þÍ:"/FqYW†f½DswS_Ó“£yë [ —ôÜnç± ¸g˶àç?l įÚ%|ó[»ÏÉû‚wx‘’çÑ?AÆÒ$&¦áÁ?(¹ƒ;7Á•¹ ùùÿ€³CI¼JÏf7ø„’–bó£Ù¶«ßñ¹<8*²xIà‰²#èß\" ŸÈŠyÙ1/Ï©ïð1Øu>Ÿªc\evÛƹ.V$´óÏ-¤9¸¾ßd)ývcõÝ7 ¿õläÌõ×XÕ]…e;É1 ¶Sü•n_Ë™0!*[+±ŠQ)€{·«ȪG7²™ß;NÖ?‘ oþf<|ƒÏzQx· «S%ÉU„Žg²‡8ú?‹8åŸgñr“µ44Ò9Y%y»‡<¨üþ dŒé¯õÜ–É4upÕãù/þ>/œE×9¾.^CkÜß ä'ÃçBük>ú7>ÈúW¾Wƒ½¥Uؽé+¸ÿßרQÞè|ƒãTëø øÐþR•íçü{oX¡^aꞤ14l^†âVyÄ©8§;dƒ”¼j ‡úÑÓíCòÂÅHáÃøÿ> ÿ‚$'f`ˆÃyþÄDºñÌkCØÏ‘ÂääDlp~ú¼džÏóhn%æzú˜gJ O©Hй'¬SÒÒ˜çHõ2òŽaðO¿ŠÄä@k ×úbO7¯‚´4¾­ŽDU×ì" 2RVè?^Øì–jîr7JLÞ¤Åí|£Y 3'æ L[“nŸ6ÕÇwÖþïÔ=…½U^.Xò¢î.gLºûQÁï$奯˜‹¬øäÝpQ‰y] ñ¡u÷q8RêÁço©Äïáß°³JÒð˜4 ýPÜJ€€¼ðH3%ò",ÏŠa:㓦Ón xò ‰[ÈgÎ*ŠÅÖ0”˜2¢4D©Œ„ÊļIsÅQò\<’çp¸í?|6Ê‘<*óD¤-–ž Ò< JvÂËÏ<p–/)u–ÆhDüÇ^Ôï”ã‰K·àL}þhónäo¯²Sº *Qõø: ãeûóÜp€'¦üÜȈD÷êó¥Ž# ãR0îá”%Á¥>}è¹ØË©é$,\ºøj«MƽŒÎ,ÁçæQæz‰,ÏŽó¹‘Un~ÊOL>7¬Û¸4J„Ç©Š€"0“˜·hsœzÞcF9ÆÍhܸÉ-Ñ‘‰qñ‹ŒÀéËLdÔcFJa†tf$3ÍDP&Œ€™”£qO8±FŒ[D9Ûí¸—'s.ï¶—óSó´Kgxh ý0Ò/Gv¡­Ä…„¢§×ÄÝbÿÅ6cO¨1cÓàÈQ#s”çP6“¶˜¡nÅÂ)AYáÁ&c Àœ¹2çquŒ€áÄóø5?áš²W‡¶†ó_BÝo>2ÅbcŠ9j²˜A@óò(õr.¶G'=?Í4Jq‚š¼ô™ŸÀ™þ?L³Ź+†­ØoÍ£n™Ò’ý‘Œÿ"ª»n È+9 óÉsWc…mÉ^îc–¤å¶P8ÿ@èÈ¿¿§Í¶g¿Ù¤ç úôÅ@à8Öð/Ÿ§åûî !aß9Úœ Ú•£}ºáôÌåŠzõtáÀƒ™vþ¶5þ`YGJ£®8FÀÙI£$oԢЌR‹ch´ê@@äGHŽÆ-rdzõw4'Ï@ûQ¬¸7ËÊÐÑZ‡”#´Âý•GÝþÜfìØ5­èh©Áë{=8ÔHeÃÍ0?·)«iÁÙ3ÍØ(iŸ¨…?œÿ¨\åäö/ÉÄþh=sõócêËPm4akøýoЂ~ç[L߇ЇW`ç +QßÊïo°Óמó#d½ÊÏà§5þ°¦]¯(¨zÄ>ÎG!yþœ Rì# 5œ.æ¥Çã^~æD‰½^ˆÆ3*ñ\^ÖdlÁß5—ñãʾQ[IÜì>D%ñmlËX·ß· xᕟÓUÐâ—ý¦.¿û»;вçÎðþ¾t¶³ÕI·ÛOá -z´{«Vá¡]‡QƼ‹O¾Nas;á­á¯£!_gªò»ïÄCë°åÉçPÄTU§:º^ïácNkü0`;ݧFÓÏÒà8ç3Ä-~¢Ü®$¿ì©·— '=0G´çèˆõøP{ôI&µûs %|ÞdôaÏá“h¬- î·ÕífD÷Ô»ôˆö9™2Ê+´ìâÄÅÿ(±üð­<š9l\wñ=»PñÔ–Qß¡$òS¡ܹ„],\‹ý^`å ü¬&åcx²8·gbaR*¥N|ÙåÁÞŸŽt†~Ü^/ûöb9-œ{±;›xO|CðŸ­cj ¾XÅ 0úÄFûf½ÿóÐþ4ך´ 0-ÔßzUP¾r¢Ý¾ÊÅ¿a>á¦fµŽ„·†/J¹m/*¿÷Cø™ïÀë߃ëÞÍxá—‰áëe+ΉXã·«¢ñ‡@¨!Å(È+ÔÍ˂É˰™£ÒÛޣÅI â^Ù lQÀ”ˆŸëw‚æ²9* yÜp]`\ÛÞ¿ç£öÔc2옙Ïýþ˜‡RÄ"`”•(1ãH:b«2s›“›¥[ö£¦ð5xV/ ”ÜU€Ö/þpþÇ{ƒž}ð‹Q^Q~ Ò\ 6TP~é"7á£Ê[¸iAƒoŸ ã?ÉU[žâPàÐ͡ʤԛxý€…á”uá­á½'&Ê´xã >†‹½>–'møúaë…Ukü&=^ÙÊJºÇkÙ+XÏ£t¹ï¡=8çà/½b‚Di9$3Y?áÊ…Ü£Ï]æ¢?«¡àžzî©÷÷“”ØN”rO½ý¹õºgÍø‰5t¾0/Acרóù*ß¼\wŽ”X n)i48:N5“P/)0,h¢‹µù4sâ8†ówD±‰T>KÉ!iŒ5üÎÚ=ÈÜÛŽûÿÞ;9KVø!Üò%T½&aß™Yü¸cUo K‹¹‚,²(ý#æH¯˜"Ó3ÊK>zAIöžz×8¬ÇË`Ÿ€4Þ}e£r ä1²/Ÿ¦"‰ynÑ=õF!'òò#ƹ‘"ǵ³˜¬ål¾% 2å³Vì»ÛNâû?Kƒgë=H›S5/·$¶Iæ^Hbž‚,ï4"K¡HÚêk)Ÿ¦Á'ªüXw©«|Ûo¬Ø‹^DþÇIWÕ¶H?²GŸ` ¸Gs›0ùѧ{êM­ùŠ|nŒ{ùFB¦:Å”‚¬å‘gE­Ø¥ Kø-Ûg3ŽYF@Ùä]dyU»¢@Ì>¼üXçɇØÈË×ç6Ñÿ<—·Ò¨ –×¼1Ëq,O­c,Ò;÷è›x†º§ÞıШ˜F¶"¶€³Y°ñ'”góÊš·"ÀoÈ ‚›\O–7J'½À“¿%7‘%Lö®sR õB7ùUò÷É£öáùMÎÈîŽëF(ÂïM$oìËÊ ïN –ùq®­ƒ·¦cÍâXB‰¤çhÆËÒËóÉëÈGÈ‹Éòp6²§ÕÀcHbØú ¢å%•—,*ùCòï“OÍÒ¥¦mRˆ’?üjê˯¸Àùç±1“ã«Ö“ oø% Î´²3Ñ„?4s&T·"0ÌIO,´µ÷>Û4¯m—°ûv=¶M{îòVìÚ²Yyh-÷ î¹&ûm3œ•û”5÷c«›k”3Üo4Ÿ?>yÄY/P‘ý†ü]òSX<»H©\×òÂs‚{ñe¡äd§íïï?¶WeÑfÀj}^ÉQZ¼OÀ'ÿêËxе—¾{ñðžjšÕzß:,£b^*'‡7óëÃÉ’<Û?+gþ¡¾ ‹‚ÓÊ]Ø·5=&}+Ÿêb>}¨¦eü}'Å s§X¶¼ê»£›8zTf9Pbá­ØÖT/Âòàø.¡‹oŒMù™Á‡m´-·°Vî1P†ïÑbÜUò˜M 5ïØ@€ ýÃä½äp߯ÍiEåCºý»[ñDK+êËnÃ^ÏZT´ó{°þN|ú {5‹X­?²w'Ž£ÙÜ‚²\¦ÊÆŸÞÀÒä)+ÅÎÆ…hnmAY6Ç==F¥ãGã¾ûáÙK¿úfãs; ò¸d[Â@âBnu4Jëö ×ôûì½þR’%¿‹Ø—° —jΠbÛªq³×À™A øÜ|š¹É˜¯° 2 Ës#3™jÅž ()Š@6 sY^ÊfC©&0oa¡ðÚ+Α1)9q’¡õ«¨¯@fc,á\!0š1|žÈÅVÌÐ×Y‹O¯ÝŽ:ä¢c«*°a|æÆaz`Fnææª~•9NŒp,´xŠ€,ž|~–˜\Ç(FÓ09¯±îÄÔ[±¹¬-gŸÅš iɈ­J´L˜³Ý¾ê P´Vt¢åŽ1{p­o™«0Ø~©Ù‡zm×ð²â‰Vx$Þ*ÒSñNe/ž4s #êRî#ÿ2Ã0˜GŽÒ™£4J{Ñó†½{ç C£Ù]#;M^€œ²â »J6±5J S´þ–SCâ¬õÖY¨¹48æMZ†,åü}ò¤ç§™F)Np¼àˆ¼8Xœ ¾šæa cBlkõy‡Q]°ß–•S‚ÆÓ'±'Kl¹% ¯âtðsæÐèe~âöàh çpÀœä½§¢Ñžý”jø{Nã@Ð\^É´ñ“›QsÙ3PWÍ"ú ÜÜÌZàPâk3\g$ ‘¼QË38'Ïá ×E³›{D~„ähÜ"GÂæœÎø¢9yx†.ÓZý‘Ýüxò47×`YÕ^l¢úŸ©GCMŽäoDU§/h~?§ûÖ†'Pš¿ 9íH\¶~‚öà€Ó‡=ؼ»Ç[:ÐÑ\nçá9ÜF Ö‰Ï/Ùˆýý ¡¥«ÚwÛ;0Æ×íÖÚN*¯_’¿>‘¸“Œãlp¤Ñ‘çÏÙ M2;‡˜—sTùáÊÉ0…FÀ“åõ²¬^ÞXe‰Òd7YVs1»ÑÔï-'Ð.«¥?àßZæ¶à9nÉZjkÈky˜¦¬õ§V± ­Ø'¦{ ®2«W¢u”Ñ]n Òmòkæ7Ô!ù{¬Ön¯Å©2«¨¹{83dzí<ºÇ”A®Ëóº’»Ò\! ò”“,EnD~n"‹<‰\ýË2)™Œ–ø¬› Êg)²ÔpÙE¾‡,Ösu ô:Qˆ€ÈùÉ›È'ßF–öWž™±‡££åY˜ÉrŠBš’Ïvàvù*†$ˆ»²Ö–û‡ì…ð^ï¸Vì'dŽy] |pdªoYÆÝ´tðcüâý@>,WàQ6!ò…’"0G˜ž˜\NÜòüÉQ(%EàjŒ•9ëwµ7²°£—üvßåñ2Y&üÕv"Aˆc³ÍQª Ûƒ£zZ¼T,y_I‰)‹±Ô,î¸2X}â*Ê9¬ºy‹Žû¡ 9Ä<.%ò",Ç é[AÐa [ôO˜¤1^n4¥hooŸ‰l4ÈC@äDØ(3UbC'„"OPµD±‹Àؘœ›7ëè²»÷(Rk&²" L^zDfŒ,™#½â“T‰Åç}×ZÎ3ÈHÄ·ÉNüç9E!ó6-ëneNC Ü@–ÁnÙkLæÌÄ*šÌ¡É\ÚDGM~›q_'Kþ³ER¦ÛȲFæÆ’ã‰2YYaÙËë"ù<ùdÙo:$÷Ìô¶D&d¾Kæ½dß°wÈ"+¦÷îTdôŽ_R%¿÷^k| ¤Í±%æT`ÒhIã$…ˆB«²úÅ4h¢ÀÌsJ‘-gø9²¡Ç,oež¢¸>Jþ¹‘,ùtofUÔÌÒþïö¾>ªêLÿEƒjÀ€A]¡  *‰…µÒ±\׬–I[ÔV’*­&Ôíh-Ù° mCWÚ-„íº®†–@µÁ–°¶ðwKÒ¢kCmØ2(P-¨‰$š(‰&zÿÏsgÞäf2ù˜d2™óþ~ïœ{Ï9÷|<÷½ç9çÜ3çN…rQwœ!.zÏNã˜Ø„’p‚Þs^£ö@› QRyLb{ªDÇøÚ)R^ñ%úpÄW­MmãŒÂÆ„UÐ9C6D΋£…þl¬Øpñ¥¿†áлÆÙI IDAT–Ëñ;zú'Ûgp~82$QQY&’äv(ËÆíÞ€ò Ýñ&?A…¿ e'ƒ÷N…?ʧ¿PÏ>º$"µ bMåhL•$F‚£Ò†Tã–À€A{ÇF ñ„À¨ìbŒÂŽ„±ÒÚH‘”¨8ºâ¹v(éÏÆŠJ?%1ÆcoŸòwPôIè`Œ¼˜× è§ S „n„¾uÊGpò*´Þé'ǬóN(íˆ#h§dÊ $ú`„÷žÄä$2¦Á‘‰LGdJdŒG1$æÅÁüâ×cCX[6>TŽÆØ icÇLÏuQI Aíò:Ž„êuí<˜Œë?%Aò]Ïo C9"$|‡wZ(0ü¶¡Ž×AIô¼OÞÓŸAý Ÿa}ÚÓp;4¼×$2ÓFÔ†èœã÷O† xzìtyÜ!m¯Èæ‡7Ë_Ý·\Òÿ*Aª·ýPþtédqú_IË+•²vóërߊ{AFâûDuiGm̽gƒóû’]=„f78 S6xl˜(ú’{Û…]ýôT§Õ…×  G@s ¿„.†þ/Ô_.…Çq(ãÙÜnAœßˆ§^ú 8]ÚÏi/Tµ=æy\K7&ÝpEÎHè…ÐÑP¾ä}‡´ì×ÈY’QS/Ëg—W’<’Q#žå3åƒÅ’”ºCjšªd&S2ÓxÛoûߣp*†ÿ1â:¹òŠóýö¿á†TïýHó'`ŸJ !Í#˜Ä|DFR¢’´FAu´E2Ó0¶K8H¬=3Ìn²æ”™S¾Œí§§9îu½ú5èKb:NSÕVçí~°eç5’Œ}¯P—Ù’Ä.‚òeæ/ ®@ÁH@|$Ɔ“ÄÞÅù ‘òäΟ…î†mr ,bÄGf(Ð@§B•¼ÂMX0¹žÿ­ƒòݧyŸ(EPNý'FúŒG¸ß†þ”#ÝžD ÊŸÌâ~ Ñ GTgï€ó´T#Þ 8§Gz‹? pG8?P"ƒp1Œ3›¡ü#ñ('1ávX‘@`EQƒfBk › Ï@ÝГÐ)ÐßA¹àÀHß`g…ÿïc§ªWì`Jd}Ï!Îbö—ÄœäÅ¡­6HlœÌF¦qfDAV—¶£$¦¶C—ª2wéI¬ºÏ>ûìô7ß|ó⌌Œ_^pÁ· žTö²ƒ©òy{0ôÊÙ•Ðo¡,Ïùâß ŽöA³ WA"<¨Ââ#Þ}qlÇʳN?sÜ7ú3È^"Éo8”=I*žSŠTN)ê1ßr1œÓ9@yÎ÷ãÐHKÎ òÒ°Eg£¦.Rà(ˆeæ”a”Óyoù”ï¨\À÷aÔð0 îf¨‘ÞØŒ(‹zÖ÷hPËûm܇ûý¯BØø‘4/&""\_Eÿ <:À¤âîr’JÅÙX±ÑâИÊF‹„u>”„Ç06f\B?ª‘ภ—|º úzð—‡í ÚUíIŒD¥dE{àhÌ9"ã5F†4¦ßD&AIV]쳈s>k¬ <ñ¸—Û{}Ø6$±xº¶¿$¦ ]’#íus„¦DÅ)"ú“Ôè¯ù‘ÜŒ‡;”;¡œêñð$BE§yïIbJd‘ÐØÑa‰ŽqiGt !h@oAö_‡ÞÐA!ìUÄÙ0„E´¬9»Dí·ÓËËËgß{ï½KpøŒÿ¤ß‰Eö…ä å”´¿Ó‰:¢Rrât!—Su ‘Çôgã«ÆëH`†ÄBÂÀ:(Àï $³H’vnHT$1ç´"GdÎeõ gGÈ&5<Ð_Âñf¨‘ÞØŒ(‹zÖs 4œW"ÆóÐÀoϱM¨{pžîçgN#@œªõÏWGFÁ^ÍFŠDƇ*]6DJNlÀØ ±áRc^ŒË8‡F‚@€ï”Æ@Ùy¸ z1”KœIj‘$Jbti:êR"ãhLýh;í§Y(<Â* 0¾k­€~×XX¡7™…þ’³æÊFH…äD?’)6ZÌÃI`$2#ýC€Ó8$1 G¹×@×@—@#e÷Ÿ¢ÄD[ (™©]°ÓC?†1Ž‘¡CàQd}f¦‡î˜œû‰@Ð$Æž2znÌNÉŠ'JZêÇÆIG`:õÈxJ`v87ýZ¨âÇ©ÚK¡¿€ò¿F‘"$0 íBÉŒdEÕmDÃ5Ž ‚Œ„<ËK‘Ïtè'‘ŸÉà j‚&1GØàø7>êGâb#¥ïÀè*á°½汑¾#@sCù¾‘BB l†FÊH Ei¥«=(YÑÕ‘íƒöÃs›¼ØA±‘0!»Y@?è9kbÄ uô‹ÄØØøFc: cŵQbC¥$FâRò¢«#é/à6úÚøÿÇüç-4Ò„„DeYÕå±–~´ £Û½´‘µ…ûåÁ‡ï±WuÑÒT'§ZåÊ”¤^£†2ÂñÝkå­ã¥bË}Þœƒ«žß+pÅÐ/âq>ÜÕ&¶A rè‰9ŠÏÉId<'Y±q¢?UIŒÇF†Àq9ÿH~ú5(߇ýt#4R…6A‚Rå¹úѵ…#=è&^" >n¿ îçÙr´D&m¿U¬‡oô ÌÓ&ùUþVyðÏF:qš+[¿Ø+“¶A`°P‚ :6:\Hu6J)pTFåt_àS9]at`p[šë¡‡þúUh>4©Øê;0uÙÁ¡ÚúŠóž¥å ùåö?H Fd«³seYn:÷”ÜÍûq]“ì^›mŸ–-•ÇÉ÷ZŠÏ‘mGšdÿ¶eK“4èêíG€T 4DŽì\íK#M6î«C¼S²Ñ—OZö9EËvˆ3þÚÊSHö¿%Ïã‘G~ôÿì­IQ#íž—€û¿EZÁLy A#@ •"s}éŒ$©Ê)F£¡Çà Àõ¡ÄVï{·öv/ât–¦ß[ŸžQdÕ7×X.tœJjê-«~:P9Ö±†½pÝÖáfxÕlµŠÊ[õÕE–«p/Òh´Ê ‹,¬ÓVªtŸF*Ò¨A¼Ö“åVª«Äú}±Ërרå¨)qãØÓQ¦Æ½HËíM·±ÆrÛ×Ö[E©nkŠF)E^}~†a'ÿõ@Gs‰Û#Æ{€‘¾!àB´>Ûk_ât:ÏB‡°göÍqj Ç)݈Kv4ÿ7ßÚ*£S‹ä®™|~LŠR7È™óo=%“eúHôÜKe×÷2$§¶ÿN×&cÇ¿%y7¦ËèqUâq•HB€4Žíÿ½xr¾,3ùÇ…+ÈÊÙ–½XªÊ’ìÊÑY´L¦é•¦C¿Å’ˆoÈ ÆO¼F2\'äå7Ú$q–å†ô©òe#×O!™oAgãQµ A²&‰´Û.kŸýkYuߌ¡]½šê0’Oœ()Iá4žw0ƒñUùåøå²±åìZò¡÷a/ÙˆA Jà¬4…ÌAI°IãƒWŸ“Æ}ÙîÝ^,’ñ`…¼ÇÉìø—GÓ ² ~O¨”eå"g§1êòiøØHµýá,9³S23—K¯N•œŠ2Ù²c‡üøÊeò_#^Iº"Ml’œ+Y#³ÂÚ‹î(JoG 0îðòKh1ìéÞâ›ð#Ðö—?ÉSÕoà=évÉÍ]&¹é°+ØÖæý ö;ÙµÙÞwºÃð®õ¥—wÊC«]Þ‰5ý¾Èúôw~o5zŠí÷µ50¿Ú½–d•[ Õp]¥°,Ëòl-²Ê7ZÕ…n«po-ÌÍc–ãM.^áîZj¥VL£¶ºÐ’Ô;Þ±ò¥–»ø9«$U¬b¾+†o‰ ÇŸÍçžñ}y6zJ¼×Öïµß3¿ð0JÈ߉™‘žî‘ÛQþ÷'N%A’SR$Ù9JHô}6!Q’““¼¸À/±‡Y¿Ä¤dq&Á©Ëä”d_:]¡MJN‘”dÞì>Ô>è•?€2ÌÞ;Ôe‰«ü9ö¥´¶ˆ«ð2æ—r}†¸ +t†™ãAA`RM*å¶&©«m‘Äñ)íÓ×m-m’À¹î–&ihŽ)ðDikiÁŒ¶N‹ûçÐ&M M2<9¹}½.¯­Ã"Ç>ïtI›4ÔaaI¦Ý‡nœ8Ñ6C&±:ãú5.)Ž 9ï¼óÖߺ9÷·ÄæºÞX‡õÐÕ½E4áC„@B’¤Lè)±6ñ 1©d@`ÝK‚$À: ®Méöï{ãNñcà$VI,n©‚A x0 »Wý-ô®8 >s…A º0$]÷Ë”Ö Ð- °¿Aà÷¡7¿"éÓ<Ý–ÙŠ€!±"h® ¸[¾Â+W®ü—œœœ¿âŠ+Þöó?üñÇç:tè¦ë®»îwóçϯNLLôÛ¾×ÿЍ=·—œÀRPƒ_@Eù¿W£ö^˜‚±º°£XÎ<£1KâôßÑPp4ÔÜ—g,iºaçÃÿõÄ9öü~x3Û‚º²3úè3¨ïÊØ®mDÖ.ø…Y°Ê,ì Ì&“HEàsNCãÍ<7B¿å¦ˆ@mA¼ÿ§Çqàr ‘ÿüVÔÕTÑ Ð 3Ø sÉ€˜viù@`Óp¼ú<ô—Ð2hÜ pXˆJú7À§÷ï²ÅBa©0§ª¹å´‘Þ¹ët#28‰bq¯È?‘YÝ®¥ñ©ë¡Üî­®1ŒO˜ˆØÿ¤ÂF¾ ~ ûx.LX„=›óžãeÈïûn¶ß÷©ü¾O]S¸×ðû>ÙøZqßËÙ§ÊÄ`$<”-qN`œJÝý*px!o±©Òyý6’à(³1+qCbæû>1kÃqW14NüBúÐÇA`üF˜ƒ€?߆Ç'¡Ÿ†ÄôTgܘù¾¿›ó(F` ÊÎ?2¯ˆâ:˜¢.'üm 0ì3Û?$öÖËRõÊ»øºýÙ´é„äVÚ½·Ë¢u{ä­?•üÓ_|ßG<-âyß%k ÝRø£2C^—ãc³¤ñÀ9]4YV—¿0º}?÷ö Á|äØÊQòo?{^~’1W®-­ÇnÕ2gÇ<Ùt°©Ýš¿*ÓγÑ3G2æÿD&fdɧgIÁ®Y0bèŠFawÂ÷.èÝh âö}`WdâÛvÁMÀÛ¶Q/ö7$&ú%ƒßæ1ß÷i·}sÁ ¡ºÅûO(?­Â½ø%gn3v(^¡ˆët‡•ÑÌ÷}:ÁbN"4T¡p;¡ÿ;±5 °‰  ›‘áAï[Æ–Q-±7ß÷‰0Û3Åé#h¨ØÙ|úKØÏúx™‰ûpå!·`»vñnìW7p ͶSŠKü~ß'j¶Ò[o.Hì_Qg®4»UûÖZñ†Ã@ê ‡us}wþÝDrïá(®6äß,Îy‰: Ó—`¯Î;G ÍYÄzÌ|ß§€LðP €¶÷³È7:ÓXðwÀ¼HXNu& ¯V®Bÿú”¯àüRIm(EI*kùð·Ã‹Ð ‰ åí7yz@ Àtÿz+€7{ˆj‚üð#/”’WwÇšB¤ŽÌ¦¢€5Л¡#´°à’ œÆc®š¥j˜íâž`¥vèGf†Ä€´ƒ@¤!€~4ÊÄ…_Çs¿?ÒÊ%åqÿ N£:5«¤ÇãH’›Q˜Ç ÷@_‚vZNó¡%)æï$.=æÔ7U M]Æ™ ”&!ƒ@hð"¶"µÝ °Ÿ†&ÕøIŇŸ’“’Û:’ÿü©£­ÞnÒ¶Þ"DP8 .J¬•̸Gc$³‰!±Ai2„ UH‰ ߇é?l@I\J`üoM¬.fë?Jƒp¥¯#aãíHždò„!1ÂæÐ 0Ôàá¿ex: =Öpï@=ÔÕpþ¾Æ“éèLIŒ "é]Ò€ë $¢Œþ¤ÅÅç…r4fH, ,Á1>ÀÃ}5jZ ½ùñQëA©%N*‰Œm•6j$|Äô½™ºþ¤6àÒÄ*‰Ýd`¯’——7yÉ’%¯N™2%^{½fû¢^­dð#€À.D.ÐØ?ǘÎA Œ£0*§µ ‰…ÿ–³ ÖÅt©ºÐ#d¥1óÃ!ƒÒ$dè? 1~ú Øâþ§b®Ž$0B$y±!åŒ+ú’ û€1#ƒ‰€÷6Èȃ;‰PÏAßó)ÿ¬ý!îCHnD¬ŽÄ€QpÐϦì%1„Ø?©r)ôî°fû™é{1¾ÓÑXì×:rjÈÑïûPó~PC:¥Èxø“› p"›» ù} ú9t¢øÀ8:¨$ÆÎºÙÀS7)ôŽ‚‰;•÷€÷%¤†ôlV¤kDäŸÂѨTC¯7€ØÙG‘ÇèØkƒ_œ¤¯¤6˜$2*QŽÆŒ„%/âÏûA—¢®÷l€¿f:Ñ ‘_¡QÙSî =ú'_P\9À@ÿzwçïÏœ÷ ¾£áBŽoAÙq éƒÝ·"ô«Óû <Î{¼22•Ä´ñÔ÷d‘Qºø(…N#’gü‰,ds@‰ç”ïÄv8¼ìC40%8àê1’wQxÓˆiéCÏZjP ‰…ö~oFrÏCù-¨H|¤Åÿö ˜öê2º!ßuÁN4´?J^´e*1f£j$|(æz/¥ ‰Ä‡'|÷=§FÔÏC?ý<Ð7€È¸‡YÔŠƒ¼´‡ä44=Öú"5 3nðäá’ÉÐ[¡|ùi¢F7~è#µh"3›Ž4Ìc±<ζeÐêgH¬Ђ°øÍžMT<¼]^ïa†>GEüãp#VüŒ†Fe/UÕu>øô32p8U½JãóiÏ ÿÓCÑÿ÷¨ËÙ Ú•dÒÝf(„e£h9½gÞs=6îà#àlGüïEÈr´(d¬„@PüÕ)ßÀÉ\èÐUЫ ¶à!gO{4®«óy ©ãG`J\ti Ѽ9êâÚÌÿ¯× õ%$8îéÈôï(‘HdJbN¼Öˆ¶”e7¦Šä{dÝ=3œéöá¸I6g–Ee£º—Ë÷ —È4þ{-öEï‰ÞºÚaPí ‰ >ïÅ (6HÔâÉñ]ÚQGÜè¯ÿÇâ…ÓK{HtiTŽ0»ÿó{K“œ©çæ£dì„”¾m‡‚ØFb_ȶÔJ%VDFÛ I£Iˆµ5Ï¡#òìcß•õ÷›þý×¾•ö=>ý—Û-áz**¤b}žT¼y±4o¹Ç ½Ü Ý í´žýÐ5л¡ u6}Î'Ȉ̃÷^G`\zÜí»™ƒÛW˰‘£eâÄIÐñ2rXºl|æ”7˶:Ù·{·Tî;nwϽž}ümÙ/Ù¨næÆý}¼ P´69µ¿RvïÞ'ulh¿´v|t$?ØyjúÇw®–Ìì²yÛÉLK“´ôlÙì³ïæ£e2kö<ÉÛTÕKÚäHåN¤±]Ž4t5DïÃå–<¾C¶ìØ!«ÅÍËvÉ¡‘–3ûduvºðñgþw{ßH´ß)Ù™Ù²bÃF„§Iöƃ"MÇeóŠl;î°ai’»b³#ÏÙ·mµ¤#oZ¹²óHƒ]v¦•›´Önչ钆ºf/Û,§T ;OF‡Ï8èJè“PZîçýÀy t”Ó¯¿ÇHGÉ‹½i6F£¡C/‡^EÒ¥õØVö®¡.« ¸Ä**Èò‹Uq²“«ËÅðÔb«¹ó¥½ŸáZ<È–«¸¦÷¸ÝÆh¶J\ÞòÕ 8F¯ÈT¸´Ú%í‡vD{â»±!oKX(9ƒ“t—@§@ù¿O˜©,€vÈSân·éÔT{ŠË>/õ4"n«ÕÜÚjÞ[dû¹»µÕz«È~NÄ*¬®÷Ë£Ñ*u3]·UÍ$!Í'wyŸɱ7x¬ßµ®¬,+Õw\\So5zJÚËÆ²»Šÿ`í*ð–1§°È*ÈJõ†ì±Ó=¼5Ç?ÕÊÊrµï:ÝÚ9-—»=w‰Ç¾6”?,+ôNh:ôcPÚ ïm†÷'döbFb@s(†Ã‘Újèç¡|à`Ïä8;‹^ÕŸ¡O@oéü ï9UGa4(*£.rúÅl?wq±<¼$W–?¼EjJXÄTy­ö%ÙøÍ•RÅž<ÉÛ°[ÞbÏ1=SVïìèIæò|û;SÏlöövÓ2eí£UÒ_Z5¥»Þ¨´’ ¹Ùèq®•k—Ù=ÈôÌevóÔîïËb»URøäH´÷*½PDò/íĹƒŽÆ`ša™El¼/¥ *ŽÉ–ϾE~Zuy%HbB‚$]4¦—|“$£b«”””Êíõ¦×õ‚ ™=Ú;B9)ÃûÜäÜ.—¾\e¯“¬­òDi©kÒÍ ÚçË8ú¢K£ÅÂVnùâ­ìCŠ\s3û›Ú;W¶g¯? 2cþ=2£—x©.·LF×­©§Ý–%K—Ì—W’齪l¡Œ‡ªx~呦/zÉÓ}÷eæ/9Þz?H®j½¬Y4OÖØ‘]R¼ç&Ih:+‡xžºPfMð6íWÍöÖEÓ¤;ASŠa IDATyæ_ÛHë(>öÑ/f$÷ÄôôEèVèûù÷8zt7H-׎ó‰Pç}VÓÑ#jÀ¦?iÆ]RQ˜e'Y±i,¾3C¦O/ÃÒ–Éþ橲òé2±CÝ%òëu ì„3²Ž®ì”}çG÷s;¥å‡í/¦>ìsþ4ê¾7Ú†Òy_·ì­­”•ÇÄî«zž“ó?³F0Uq‹ç×ëdFw`F1 ØòQÙJê(Œ¶D»Šb9!'Þðrqs]­]Î%m-ÒÒÒÓT€[J½ö[)• ­[¾@®ÄSwéU³ìl\…»¤¾¹QNóÈÞ={doY–\¤ÐÑR[º³_]å²gO…-åÓW%yó~$ÇñïÎ× ž‹W}Åh:óªB§ºhZvHôÿ8·è¯MÖ¤öô»PNG~”úÁðMœ¿ "û-ܵP¾Ë`ƒÃHõSH²Ì_¹EZkåpÍ^ÙZR(îTDó¬—‡¶`Š¢¹c*¢Ó²KJç¤æYïÈë²+ìGM¦edÙ/*õäó•Þ+Ø>\¦ß¹Þ>·{£ÞðT†\ÏÒËxI³‰‹ìÛjß¾ç:ŠÂS#ƒƒ L;>´Ú’êàä8該çã÷¤E²aãZùüÜ|;G×Ç®"ç&Ù¸`¤Œ9R6ìçÈ.°´°Ñäiiväªí[åçOUIÙ·³dî¼yò­çÎvM$¡Q6Κ-wºåï\"·|f&!©ceä…3äîžUÈ¢VË6,T¹{ÿñƒÏ#Ì™l»±øcH,Æî*ˆì}g•pÎA ŸÆ…¾½ÊF‡ÂûŸ ½ÄöoÈ´W9}³êœL›y“Ü“»R/õÎ¥Û£-6g¶Œè<Ïéóm;÷¶ïh”L¹Úe'ÚxÌ ž8ä}/ßžz£É¾ºsتRFušhõú™ß#À;ÎQm‡v¤mˆÚ¼¢SÜnä-ηmÒ]´K¾y“ÝcòUÆgejlª˜ä›ÑþL8"ÙaI:1á˜æË±]Å’êÁ:È)¿Ì#®¥¥Rö•íñ’Úó½R–îA\„ä»gˬy‹A¿©R¼îK2~é…;¥$+U¼óy{fÑv€FnÈW™2„çÀ6Ùàðã;6>¡“ ÿ}ú.·¢³ÔúVe1,§¨Ä*-)´0³ìóòc–ÕXc¯0äÊ«Ò=‡­†öÕTn«¨´Ø·K,®ê:½«À¾Žq‹KK,L„Øç k=]á KͲJÊ+¬"ߪ+WN5ûòp—XÞÅ]ÎÕ^¾Õ‰b”ì²jÍ ÅÎ70„g¼çÐÏ@ç@ÙÂN‚Ž‡ÒžØ ²ÿgˆ,‡¬MAhãlIòO^uA{Xè¶ó@oÔÂÊ`´,¾%È]H,˪Áó~¸|©]&T¥} s€Š¯"à»÷Ÿ…;Ê90Úíˆöµ$VSL{5¶3@óèt9ìÏdXH¬ûP#±…€o§‚ØâäÛG c|z1Üí°D8/­ëjëíý¶FŽ É~Ë@ìÚXŠÌåÈ”¦†ä$I‰ÞsgŠM uÒ‚u$)Élûü¤­ ùà® î]OÛðB½ ³\‰òÓ8ư&ðy(n®ÔCùò‡‹Šš¡ïAí1Ü!Ÿs¶¤: ÊQ" óiã åþ6^w°Rþçàûòé»nߢ>D32|¶ÂÅfg¡´Ú Õi+â|ÀÒµ…p’&(D€/è{–Îκ–ÄÄά–”Ì6#°$%§Ø­JÀЗo)qÀðn<¿1ænÀ ­·ÚŠÍh¡MzhRK™‘.Ao‡84E5¹@€ÓKFâ ßÔ›=ÜgCD¥_Ì4Jñu;Ã^[í=«½¨ý„½ &Cƒ0×ø¶m€Ú‰ ÓÚ8Å72¦öpÎ5·ÛL ˆÆÏ . ‰… éÈÈG!•ŽÂy¬ÄeÞ‘FÆ}ŠÈRø:8jC´§ ©D–Ý*vP#ŒÝššu‡€ö¤>¨ P[ÃqÙ·¿»ÜwWrã?”h‡‡ÓŠ´ž;mi(ËfòŽC ‰ÅáMG•¤¥ Ñà!ñ—'eö¬'£}ƒ½ÁÃ'ºRVÂR2‹®Ò›ÒƆÄbî–öZ!Þs6@Úéj³€¶ß.Ù¹dÛÆeöÎéÙkñí°²,Ý»wîÆ}^rj;#ÛVë7ކIîÚÂOY·Ù.·¦æã(_þ~Ù6©³Ó[‹ôVØé –)Û}ß;:²}®Û,k³ñ± û–ÇxFÚn¨ë´§ˆ( )Dü  F?565Õi }Ð$­Ü•~Sž,Üu™ìÝ[.ËòeÞl·ŒüÒ.ÙS^(›Ï–2|ÿäà#²pÕi)¯9,‡«Ëåh¾[ÖUž‘„‰7ÈÒâd‘%~áFn§—/ ŸÙS½WŠÜrçÝ[ì?´ž=$›òáË¡’u#wÇ2¡è Euut)ÅUÛV7RÊoåpÚÅ Ý ³°#ÞÌÊ[_…ñŒÇ½XªToY.7â/£)vIYåýRxßm’Ð6{ǯ’ÆsÍr©k쪙!·a¶üµÑ…}@¶ÿñUy8ýFùû[¯Å†#䎧HëÁß »TÙûäÃrþJöÉÕ%’ŸºKŽ6-Á~XØkÛU"ÏnÉíþd,±‘¡D@G]½ØÌP±“=kCJÂUÒÒÂÅQæNÑc½z>`8ÌHlÀFmÚ±NRó«7bX(Wû6×àV©éÓ¼ÿÍhk%íØ’€~¹ŸhÁç FŽ.«°Áýä1ÞPíî=ïÙ»}x7vøŠ\çû/´wSoo*­È*õ¶ ù0PG¶ÚrÚR¤ÛÙXê±!±ðÞâÍ™ºzB^Cb!‡4¢T²¢AéÝãêØ Ã&.ŸLzG¶d¹eÓíår²¾06J©K¤‘üט)€n€‰|oµ!ÚÓ†Ô?Òj öÍÆ´Ç©óH+x ”G ÌIb¼!Ó–„ΨKŒÅÆGµŸh³¯Ëºú£r%öC<õÌFYT…™ÁÛ|O²·¥¶©ÍÞ-6P&Œâ· 5~ˆ€ÚL¤‘—6’t•hé²A¥¡Zæý@að…øïA'2Cbƒ3#-gãCC£öÜqŸ{Ÿt&šá2Ñö¿Xæ,[*y‹fIÙ"x¤æHáR—¬ÊÏ‘}_= ic¸çbžL3FΔ1ܾÈñ3ZðLCbD"øPgoh7”ÞíÇ/œ¿j×tI`lL©ì+qgõ1ÐÑPn̰¹!6•+u©=?ˆ`ľïŠ-¿aÈy—sPnÍ~߆Ò_±W2ƒWhÅìÐZ<#:5ô@ٱ㢻ØóMõ"(w±ï²Ã7üú,-ؾ©-A’S’‘I›455c3ú$;C{—yÇ.÷}NÔDŒ|;’‘ÎB¹39_f¾ eãÅF,v±'‘ˆØßâ®ÔTÝÍžÄE£ò ôWc|>JÒ84Ò Jbì°¸C=mAw¬'‘©ÐNΗŒÿ¿ÝwÀbFb†0&àƒ?`ƒJÄîô{Ù'H’~ꉛ]æcÂN´´•H­è¢N#²ádÊO´ä4œ/;u$1ú}Ú•ø*‰_ ‹ÊsvntDFÌÜÎ NbH¬qqÂÆ‡ª«6FêÆ¦’B í¨]…¼‘êGIµ ΑS’•á:½è$0†i=ph¤ˆ¯c‘Èœ£.{ä?RÔûÓKò} 6$Ö7œL,ƒ€A z`#ÉÆÕ9 #ñ*ùª¿ŽÌ8 Óp/#½ @œ‰¥âI²bgª£2ºôg§ñl ÕT"Ò³ç€é1 QGß»;'‘q„ 3 ôgƒJ?ZdgÞ¦ñd$ÄQÕ9Slu‘®’ãé} dÿ¼ÌH¬¸™«bî¶_sBdÖÌ)¦wý÷WXöþ)$%úñœäEÓ Œ#03•‚%$uu´¥DFòR#îJbAdÑ{TCb½cdbÄ önû˜Ì·–‹â{î±ÁtŠ’ýÙ ²íã(AÉK]3(}âIqâªØÒ%‘©o%±C9•ˆtÛçˆylÄ SÏl–L|ð™Ó4é¹kå`mS„»Îg¯Þ(ksÓí°4ìvœ3ය}²:3Íö–ž-Û÷sO{¬½=^)+Ô?-S6ì>nû÷u·|ÆËE6®Íõ晹Bž9ãÍôȶe²lÛA;=þÙ¶çGìóã•uØ G¸Ø!wÛ_¶Q¶oæîý™òûšm’™»Í^;ÌËZŽ8ÏÛdÿöÕ’fc”&Ë6V¶ÇsdaÃXm<érT ïk¸œ‹èªrEÿçd´w t"]ÅOsðÔéDâ®÷@‰^!°"¶ 2ÀlØÛäj,þ_æRèèÇ .è(;I¤ùðV»§•S²Ç:y¬Ú*LeÏ«Ð:Xž·æ.*·ª÷”ZHÄr× ä¤UÀš«Èª9æ±JsxÍRø6ZÅôÏ*±<ÇŽY{J—ÂßeU7b£*O‰–¸‹,ì–oe1´ t—…ÝòíãÒÃÍñÆác5V¡Ë—6þ¡TSœjIQu{ùÛÏ›kì²”W£{½eË*·ì?5ùb·6ž´*ŠsO–U^}ÌjÐòˆÛ*ÞºÇzùÅ+¶ê}ñk:Εó:±Š+ª­š=Þz¸K<í刕ÖJ;I‡Ònh?—@iO´«óP׈iOPät!G^œ>d9ùß0þGŒÿ£êÿ%Õe}ŒöŽâÅ?Sõ¿wü§ qæ´-qg»3h¶a¦®‘î8´³HĽUŠ1Ú¢e®üí^Ù>v®<}d©ÌzÿcÌÚ*Û—/°-õEëdú`MwËÄÝóÄr™™"2³Ø#/Û!-Ãŵ«BjfÝ*3’¤î\bpdÞûnù2œ¼9\š+Ó`½+ŸØ#ÛÇÏ“ O¡Ì‘qØ „í”W†ëy+;à”á2úÊeUíaq¿Ú! IWÜmOíIGš¾Àhv=íß娅<¹z6߯‘%óg"èF©)Ù%³©’†Ü’lG6?C;Ì3:µÈÆT•àè:ÅÿÜfŽ;°±í8µìΜϯý·A1÷‹šSCb¡Á1FSi’çw{Äu÷ŒŽ?1¼H&£¶µçÚì¹™Ô|;ÚÃoxâ8ïÂZ{&¡@¦¡ñ·%q†<¼e[d_ýÉï/ 飒Sï»åÛ—¥Î”‰j¹IÞòx§ßÛµê^¯òîE’t,/Ê‘Œ;gÙä*®Ùú ›ÎÒy·}†“‹:þ½Ý)r;¶œ•ã¨LÕâY2l±#Š+Ãqb‡%3”áZO$ÕSØPV!ÒóîBh̽ìì™1tƒÆ2W¡®å”·OZÏÉ ŽåcÎ~ûDÇ;6»É!9ë}u~9.›×n–?ŸxZf/\…l–úfL;5×`³ÑÝN£·Ýò}yÞÖ,áÚ_!ƒë+O¢ÒK“ýh$ÔÖT/“>ÿ]imm”cž½R’vTÎ]/§´|ŽÔz>l±ßT3NSÝëÞ= ¼ù-­8†ô›¥±¹YjWË®ïÜl>)Ó3˜CÊÆµá¢£ÁcÐÒpÞ\CbáD;êòJ”nÏY•#;í•M²{MFQKÅ5Óá%éŠë0­W!ýü ýyæ‘dQþïåƒwëqAªL›>I’dç÷Œ•äÝ–`Ø„Síù²fç¸MR¹a%ÒÈ’[¯ñ–Çóè.,.i“3Ïü§,Ɖ/6ÚNVÈô©ãå×§ðgÆMrÛß͆/DGsÞ3ïkì[ÊÝöU:s¾Tìo¶†ýòõy˜0‡²$\)„hýú'åTs¢$µž”’é³%£ì¤&a\ƒ€A`ðŒ1+“t„ À¡?Uç©yLQ×{æû½rþ÷dëÒO‹{:é€â’rÏ*¹GöyÇŽ$%]vî*’I©²~¡7¼xO©\sÝp)v/÷$¾WGJ9’•ºFæÝòH—ÝíÛÓ²cênùæºÆ=Ý;5ˆðâ=§eF\M·ƒ\óÜ2ud>SGÚ"§q”8ã‹Ršó¨¸§jD ÊÛu°“÷ý ï´Û>Ér´Œò$iæ)ÉÊ“E³ÆÊ"„¸\ze‚ÌÿG æ¤ÊÔÑÌ’Z(‡p[ŽôÆÄo Û¡ý´¡˜¨±©DÄ"`v±Ø[ú‚áuGÞd¶Ðd]]Ôë.öMug¤³Šc'N¤.A=HK“ÔaT“”œ,‰í×´IC]­´%Œ•”dÎõµØ;ß'uDè!AÕþ 2zÑi<ði9Ó cS„É´KòÄ_:çé m@Q‡ÑcÇKr7•èy·}–½¬˜àz­W’$ã›jíÕm/Xôø^'݉šœ…ˆöÊ9ßÌ—šC¾‹=Ê`$ΈÅg-Înဪ«=ê^§•“R@^Áf•˜$)N‚±¯ç§Z&t¤”€)¸ ¬/á<|7• )tåHGrXf()¼ƒùö¶Z°çÝöYönGy:ÕËQ¤;ô_ü`F_1vƒ£­:A5ÑV9SÞ€è´]6HÔ¨iˆF^q‹”ïò#ÖÎx6ÚñQ2‹*ûlpLúáGÀXø1¤µj'¶^– GRÙMY€³³Ón3á/†ÉÑ Ð€!±,âáH!’—N!ò˜J1ïH½8˜ßø:8jC´§ ©€+#Û«›Ž›>‘]øè)]ûàºüPßX(PŒÎ4h@jDêFgML©Ã‰€6îœV¤ÝðÜiKá,K¿óò#.ÖA ™ijõØyN?#}CÀß.´±§¤qìTJf†Äúv3b-–ëÅcóÆÚ¼úhõ6ã 0롪çêE­§ºô3Ò3þí‹Ú ]ñÕNЇz?úKf†Äz¾±ª¨>”ÜÕˆA hCu•¼¾ü« ¦¯ì,7ëÀgÀß…—í§Ï‰Ö•þFzFÀi!Š’ÏýUw¶'¾Jn=§ÚM¨!±n€‰aoí©Ñ˜Œ‚A@)uiKjOÁ¤3TqIHNÒb;øT k¨ÊùÒNÆú°Ö¶‡vd÷/ú33$fÓµŽÎ‡U{B]cƒ@Wh/Qi3¾Q˜–_‰Œ{²PÍ¢¦®÷:ä>¾{ÀO´è} Ë}ÞúÝ2$òÛ5 ª±À<6bè :â" Ðnxî´¥¾¤1”qXV…)±Q5>¸[¶9´Þ%1µ¯>—ÆXŸ¡Š‰ˆNÉ֩ ˜¸Q\ µ!ÚÓ†Ô?Ò«¦#0ºlÿH`]ö•‰ôJDyùˆ·’]µ¥~Ù!±(·†_ i€É˜ËãÚL´Ú6’J`JbÞ©ãè&qUùUmµ#˜.ò°W*û^Œ7ÓHü"@rSü"ajÞWüí%ÚMGb$0N'r3lª‘ð!À‘GÀÄŸ+Cuu¨v2àÕw1#±¾c«1Ù â‹ÕŽhÅjMM½Bí¤½çŠØIU±!5Ó‰a¼ >¼ß‡ËÏõ‘ƒx/úE`¸ÎN€®‘øC@ç¡u8o¯rü&þ15î Žºf@_ž„òÛ:2ÃaTKÿщÌHøàÈ—#1)‰©ÔèÞŒÄÂwã"!'5u•Àسf¯è.è(¿ù(ßPùSµ‹C#1Šmj î2( m¤À>êXÁ¸üTöAŸ48¬6 t ‰…÷®orU…÷D}n$1m¬èrhOÅǺÚ{H8´ãp„¦FgHŒ¨Ä¶¨]°Hdÿ ýô2h $„W Ú1ÂaÄŠŽÂX@=ÖÆ4b ƒ 4 3$ƒ7z°ªÄÆF§Ù`±±¢’À”¬phÇñ÷ï·¡1A#Q€ŽÎÙy!‰Q ¡ß†^e¤ÒŒƒBßF‰i¹I¼Jbêj˜q޾œ#0ÞÕ sg/ÄHü! DƆJ+ÚnŠŽÎœs×jtÞæ7 ]¨mhçFGèÿˆ°ÿ€Ž‡êômâ“ЙÐ}Ðh%0ÖÃHø`;£ÚoòÒâS$âÇeO›†C¢RÓ‡™ ˜{ÜTÚr}Ð×Hì"@û Ð6œDÆÙBèÐq¾°Gàž„Òïÿ Ÿ…Ò~"N[NiÙœ£1í¼iXxÝ–&9S_ å uÐ7¡Bï‡rAP4ÒÅ~BQȦý›eάŸJþá ¹gZRÐIܾZRï\å¸Î%%{K%÷¦+ñ$ÖɾßÔÈ»ÉWÉܧؽIGÄ^›dsöhYT–%5[dfðE³Óok8"¿yæ¤LúÄ-2-%dtá?Ïýýz©_GpÈJÕ‘¤9Šœ$¦Æ£~Jb´ )y©U4EìNsÚm‚Ê‘ÙËЫ ´…&¨v|~äÜi%N3¦@ÿþŒSÒtæˆüɳ_6e,jöæ9œ´ßæ#0—ß-½þ¬ä¯)“Ås'Ée'[eþÄ×¥ #CªR‹¥ùÀ’ I }PÞüð¦õ[Þød¸K±§9”$ÖïâºÐX TbßOŸ>6XTíy³¢mÐe%/uáe$  PÔ>èÒ&èÒNÔå1ý5Œü¥×ÂÛ–Wðûè@hœvü!¢pJ2ФEömÛ EùR¦re-•—­3SähÙÝ27ŸôÕ‹`4µûçÿ#õc§Éçn›Ùiªðô‹/Ø»‹‹åá%3pœ+·\9Jf-®–×j_’ëWJcxò$oÃÕRtûûò<&W/ý7Y9Š´ß)y8¿üÁïÉÊÓ0pÛ/k¾þl÷œ•Ù 0ó{ÂNÞ÷³Nv®}XVæ¯IuÉì…Kå»ËçKJÛ)Ùðÿ"/Ž›!3/zMÙZ)ã&§ËÒï­’[Gî“‚â­Ù±2OF,Y!¹éWz _Cbt3ÂQ66¾÷ÚP±ñQu’˜!°pÜÈ˃¶@Qû ëT%.õ£Ii\ûBþÀï/p> [».—ègBùî,j¤iÿ&4öù’šU E ÎJþªõRUvN޵n”Ô¯ï“æ¯‹xä 2;¯¢û:5¿,ù b´V$õVg;ÙKy©’ùbdüí\¹ù¶ï; `[ŽÈê'ÚÓ=zâi>wVʪ*Ä•¹JV"¤õí×dSûù)Y5~–¬±¯H'¿ýZö?v¯¸EÜkÈÄnqK…lʯ’ê·öÈ‚‹¤rS|(.q»/Ù“æIÅšR9¸|™xk€°ÚrSÊqY6lª¬÷<'g&.—§—Êð鋤¤lŸäΈÌݹ ‰ñ&Ç¡ Ñ`ãÄÆD!—"-†‰lûðUWÕ>z%/˜`nºxHƒ6ãàï¡ÿ M…ŽGœs8¶ç÷à€ÿ?k„6!¬Æðý < ‡#Uþk¿ðïá<Ê6žzÔ)$W]}°œþ™…â^L‚ɓٛ¼—f–KÆ|g2½'Lù÷åv)Yæ¯Ü"­K(ÇŽ‘ýÏÿV~þÈ*©ð¬—‡¶dK墎Ë8 XÈŽ(ö‘;ã“ö‹H™ði¹? #¨2o/QUÈ܉„Bå´œÓfî ¹žo0ñŠ47 Íȑͭ޾Å{­,!1€`$ÒÀï ”’š“ÌœÅ5$æD#B_ýõ‘ÉÉÉhñ.¸àµ‹&ðz˜ÝYl¡;vløe—]6Çíy^rÉ%w½ÿþûcÚÚÚ>òÁp ´»¡í2jÔ¨­ ²Î?ÿü÷Î;ï<þQÿ•ö@\sÍ5ç9òuÄ;6zôèwŸzê©këêêjkk‡¿öÚk#ž{î¹/¿òÊ+Ö£>ÊålÙ἞ÇÍ2I¾±«B¾|Á…òÎ ¿‘Ÿ?ºFÊVÝ)7ÜÞ(K‚Xî×ÒÂâ%HbbgÚ¿!Sfa*riÅIY7ÿ&™6ó&ùÜ cdä¬<»@íÿÆÃ&ûdkå–Òvîmï}dÓX[çó;-ÏûŒo3í5²T<µ…r¹Ôˉ#¯È[ØYîj&Ü1¼ûV¨£tF5Ô©›ô¢©ù—½½ñ0ç…ÀÇPNÑEL:µKYAÆþ~ï¾û®ó”ÇÂ_oº¨ IDAT|ñEÿpIII±õÚk¯•[n¹EÞyçØ2Dla:/ÒÅ®‚­RüÅ ùËó¿Â(É#ñÍã9#wwܲ_¾0r–T¸Š¥¾r‰=4Ô¨W|Œ 8+d½{’œ+*‘OŒ©•‰Q.™0†/½¼äS±C¯œ-™öH WäÊÚÑé²{Q¾—?I“oÄ-Œ¼VÍ“Ü%2åà#²ÞÚzá¥òÉT\‡Þc?û”|jÄÿÊ‹Z(§›o÷œ/²Ã±IÓÇœ¬Ý ×|û¹iJ²#F„²#nÔ``l fl`;"FzG ©©éh†o‚r­Ë­µÕÖRWçw‚î‚r«ÞÓS’ÅÎURÓèðu6×X˜Õ³Ä]buÑjyÊ -ð‹Ó¡æﱚí$N[Ån +B¾Vy«=®^ç.©ñÆÞS܆Zßq–Å¢µžö¯K޵ç$riöX9Ê×h•Úyz¯k>Yaí´R‹ªëß¡¯Žœ*¾zôjèèEPÎWž‡”ƒâ¤a¼ÀˆAÀ 3ÌAM¢j$6TÈs$–””Ä‘ØXèŽîÚ†º:ûÿ’˜,)Iƒ0yÕÖ"uµõötáȱ$ÙïÕ“=™€éH(¥©¡o“$ÉozÒliº¦6I¨Ó/·ICC“`ж›p;…?mÒÒÒ&öb–VSÀL!ôM(*b·ï>ù>”ó®ïã>#,  °HÓ4žƒ€A fH! ª`¥cÊFKbbg:JJîaJDÛ9º#ÑÁûRÇy_»¾Ïëë•áˆhZ8ò5y ƒ€A`À0„&ƒ€AÀ `* ‰ ò&_ƒ€AÀ `0†Ä ¡IÀ `0† CbC…¼É× `0Œ€!±Ch0D:-²³°Pös!sŸ¤INò-+ïSüÐDj9¾[²Óre¿w{‰Ð$jR‰y ‰Åü-64$ÈõwÜ!—w»ôÚ¡–C2R)¶] ¯zr­ŒZ¹¼ßp oiMn‘‚€!±H¹¦AC EþðÔSòFK“l_‘+ËVdsãgIËÝlÿÛôøîµ’†súm¨|IvàÓ!ù²jÛAiØ¿MÒ7m˜d®ØŽøÓh8²S2}iän܇¿Õ¶È3sí4‡atUyŠÿcí&Gü쵕ø3íوσlZý¨ìµã"sd€€!± /ƒ@l!Ð*§·×`ÃW‘³¿Ú$'®\†m}jeÁ¦E²ëøëòdF¾äk«¾FZþxD>Uðq§ÉŠ{fÈGŽËýžF9pà¤L^³Z^ÀT_ 4~<Ý-é5H÷˜ŒZü#y¾z“Ì}d&¾¡eIcÙµ2oþO{ô5È"~Òµ¬F™³{žl::Q¾\è–Â-“0ñC·jŠ­[µ ÉvQ†Ä¢ãf›R†À8^Þ&ïsË7ã¹µÐ%ÇÏŒ»÷”ÈM)ÃÆJbêÕø¾¿Pâý^ÖðKÇË®¼9’ž¹ÊNFH€4Ž”ÝØ!Ýmïì>EÖY[$ùe|‹Ë³XegJöÚâšíýK!M/ ~¡|ný’ä“™nyñD½ Ó Â¾‰!1Þ“ð ·•rØ€î!±ðÞ<“›A b>b´Èù¯JÕ —I7]=½Xò2þIŽðÛÍLn›÷ˆ,|ò€Tî(l+Ãý6p·Ó¸èI“J9l䔬HÏ•×/Ǿ®9»dÇ–²ã‡ß×åv|Y$鯻äÎyÏWÈå— ,C#l@ƒÚ«ohŠS¹o%2u¤Te ‰—‰lˆF†ËˆÑ$‰¸ŽFA#οL.}}¥Œ–)™Yâ*\,S/#—xòäÁÍÇ%­`œdŒM“ôô<9ŠÏ†¬{ü¥®iÈòµŠ’1ïΆM’C·eÉœIŠŽfȰôt6~®Œùô'Ò^)¹å·Ë<¤›™> [È—Èâ½{úq0.Á;@6œ¤lm‡+ç¸Ï_8³IŒ¸+‰õ³‹}¿¡3"~íbßÔP‡¥؈6YIK3Ú¸sºw×ôáØ86‘Ë5°èÂÿãŽí(`áHCëHIvìôÞT‡á®uxµGokj†–|ãKól ˾IöÇ /¼ðAd6ú4ÔÞb=,™Ço&ì8dBë¡oCùÇw¡\ÎCm3»Ø#ƒ@p$%§`\æ/ѧc×tîfîŒãwœ˜Ôå3"I)ÝïšžÄO›ø¥ÆSßHŒ£êg¡ü¦ _’¦²¶|AÈuœµ23W¡ÂÑ1å'5߇ò3+$*‰‹~ g<§â4x û[Ôà‹h®0 ¡GÀGblhßó)ÿ§m"G s’Nô%&%1ްHb$3bL¼FU2ë÷;1½aHˈAÀ `ˆ|$ÆQ• ­Ž¸Ø³å+:úQχé$$bH\‰#±%y©ò\GcúNR¯ šÌ ‰M#ƒ@ü!pÞyç±Ád#ËÆU§ µñåhk1•Ø ‰Œ>Š’sJ‘xgŽÈè:‰LGnøß –É)†Ä‚ÌD7bßHŒ+tP•ÔØÀê4"É‹í¤Yôú(Jb:¢%™g*±¥òØ9ØNdðJ ‰—‰l0Ä ŽéD%(…±‘å(ŒFå(M‰‡Fz@€¦£)}ßEW‰ŒÓˆJh$1N$öýCbý‚Í\dˆX8]s0bK!ìհ?üðe‡*…(Z%/¶$/’˜!0€¤O* ¸’¬è’¸¨úNŒ~‡Á‹ùŸXð˜™+ (E£/}÷¥#,º$,º$0%.u•Àt´†(FzA@GbJNtuÄå$4úi؇Áþ?LË`FbŠ„q ˜G€ ¥ÈØxª°ÑÕF–ä¥jL Þu™“ÌÚIˇ¹†Ÿƒï 3ë7tæBƒ€A ZpŒÈHTþ¤ÅsŠ’˜÷Ì,îPzs•ÀǪÚqPâR}‹àW%j! ‰)Æ5â ™N:ÉJ5L1ñ?WãvEÀIbª¦a6± „¼4aCbŠ„q ¸EÀAh00•Þý”°:Å q94$æDà ƒ@T! s¿QUhSXƒ€AÀ `0CbÆ ƒ€A j0$µ·ÎÜ `0 ‰0 ¨EÀXÔÞ:Spƒ€AÀ `0$flÀ `0¢CbQ{ëLÁ ƒ€AÀ˜±ƒ€AÀ `ˆZ ‰Eí­37 CbÆ ƒ€A jøÿAFëJ£ðIEND®B`‚doc/next-tutorial/object-stack.graffle000066400000000000000000000331321242365656200203600ustar00rootroot00000000000000 ActiveLayerIndex 0 ApplicationVersion com.omnigroup.OmniGrafflePro 139.7.0.167456 AutoAdjust BackgroundGraphic Bounds {{0, 0}, {559, 783}} Class SolidGraphic ID 2 Style shadow Draws NO stroke Draws NO BaseZoom 0 CanvasOrigin {0, 0} ColumnAlign 1 ColumnSpacing 36 CreationDate 2011-01-08 08:39:13 +0000 Creator Gustaf A. Neumann DisplayScale 1.000 cm = 1.000 cm GraphDocumentVersion 8 GraphicsList Bounds {{279.50000375109323, 186.00000036830778}, {58.32952880859375, 24}} Class ShapedGraphic ID 13 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 11 ID 12 Points {307.23684210526318, 166} {311, 179} {297.95348837209303, 230} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 10 Class TableGroup Graphics Bounds {{221, 230}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 14 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 stack : nx::Object} VerticalPad 0 TextPlacement 0 Bounds {{221, 244}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 15 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 things\ } VerticalPad 0 TextPlacement 0 Bounds {{221, 272}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 16 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 push\ pop} VerticalPad 0 TextPlacement 0 GridH 14 15 16 GroupConnect YES ID 11 Class TableGroup Graphics Bounds {{221, 40}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 17 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::Object} VerticalPad 0 TextPlacement 0 Bounds {{221, 54}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 18 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 \ } VerticalPad 0 TextPlacement 0 Bounds {{221, 82}, {136, 84}} Class ShapedGraphic FitText Vertical Flow Resize ID 19 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 info\ destroy\ object method\ object variable\ ...\ } VerticalPad 0 TextPlacement 0 GridH 17 18 19 GroupConnect YES ID 10 GridInfo GridSpacing 14.17322826385498 MajorGridSpacing 10 GuidesLocked NO GuidesVisible YES HPages 1 ImageCounter 1 KeepToScale Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo Animate NO circoMinDist 18 circoSeparation 0.0 layoutEngine dot neatoSeparation 0.0 twopiSeparation 0.0 LinksVisible NO MagnetsVisible NO MasterSheets ModificationDate 2013-05-04 13:21:45 +0000 Modifier Gustaf A. Neumann NotesVisible NO Orientation 2 OriginVisible NO PageBreaks YES PrintInfo NSBottomMargin float 18 NSHorizonalPagination int 0 NSJobFeatures coded BAtzdHJlYW10eXBlZIHoA4QBQISEhBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCEhAhOU09iamVjdACFhAFpAIY= NSLeftMargin float 18 NSPagesPerSheet int 1 NSPaperSize size {595, 842} NSPrintAllPages int 0 NSPrintReverseOrientation int 0 NSReversePageOrder int 0 NSRightMargin float 18 NSTopMargin float 41 PrintOnePage ReadOnly NO RowAlign 1 RowSpacing 36 SheetTitle Canvas 1 SmartAlignmentGuidesActive YES SmartDistanceGuidesActive YES UniqueID 1 UseEntirePage VPages 1 WindowInfo CurrentSheet 0 ExpandedCanvases Frame {{118, 346}, {574, 881}} ListView OutlineWidth 142 RightSidebar Sidebar SidebarWidth 138 VisibleRegion {{1, -1}, {436, 787}} Zoom 1 ZoomValues Canvas 1 1 1 doc/next-tutorial/object-stack.png000066400000000000000000000334321242365656200175410ustar00rootroot00000000000000‰PNG  IHDR°,cd9Ò pHYs  šœÕiTXtXML:com.adobe.xmp 5 2 1 m ž4ëIDATxí¸UÒ†Ï $H€`!Ü!gqw[~\– îîÎâÁ×ÅÝÝà‚;½ÅÔÐw2s3Ý3wîHÕóÔœî>ÚÕßTWŸ®>õ·cpr 4ªÆiÔû¸]HÀì8hh 8€úòùàÀކ–€¸¡/ŸÞìhh 8€úòùàÀކ–€¸¡/ŸÞìhh 8€úòùàÇ«¥þ&TËþ¼¯®‘€ø×ÔÌÁæoÕè«0 ¬–v4½×®–@!˜óûÕzE¸p1IÌ,¸Æ&D³I¢¹Óþøã0bĈзoßЫW¯²ëþøãZ¶Ge×ñ‚Ù%S|ݤSr¿Ëv”ããˆÈ©Èní©*ѾðNª¡*~î¹ç¼óÎf˜a†°ÐB …©¦š*¬²Ê*áÓO?ÕÖï¼óÎ0î¸ã† /¼°hoK,±D˜gžyŠæ•{ðÆo GuT¹Å½ÜŸ8/ÙLÎLrÉà\OIí €oM¦ÿ¤ñ…‹Þ‡_}õÕ°ä’K†3Î8#¬µÖZaÀ€a¿ýöSP{grë­·K-µTøàƒ¤¹†þùφo¾ùfŒýóÎ;/¼ÿþûaçwW\qEØl³Í½÷Þ_|ñ€æ}衇´?ï¼óN:thèׯ_8ðÀÔä‘GÂ<Àføõ×_ÃÚÚÚÂì³Ï† ¦ÇíçÔSO  sÏ=·Žÿ«¯¾ çŸ~¸ÿþûµÈJ+­~ùå+îii €ðnÀàåÆžÄh¦´œëÎÈ”Â3 Ï.¼ˆ°4מžxâ Ô_ìÖ­[4hPìÝ»·î p¢3N3Í4Qnýñºë®ÓãÛo¿}¾M6ÙD½ýöÛz,¹¿í¶ÛjÞÍ7ßœ/ÏÆÁ¬ÇÏ>ûìxûí·ëöxã—_~ù8ãŒ3êþ5×\£uÄüˆjÝ>ôÐC5o…Vˆ¢Éuû¸ãŽÓ¼Ë/¿\÷Àq饗ÖmÆrÓM7ÅÙf›M÷wß}÷(ÖòþS\à ‡“9$7SO$ žôùIj–ɲ &•ŽÐ¼Ü&žZ¸MxnáÁÂcŒÜ¼þúëkÞã?®|ß}÷Õ}G÷îÝã´ÓN¿ýöÛ|¢Eã%—\¿ÿþ{=–Ü[WÛ-œ/ÏÆÕW_­Ç¤x‡vÐ2~øagœqâV[m¥ûIO?ýô±ÿþñ‡~Ðþøc‰&Örš?à×_­û;î¸cä‰ù¢ýéŽÿt(ð!¼”0àf*áI„ÁSjcTB¨|ÀL;üƒà’4ß|óiÞSð§ á§Ÿ~Òtà 7 ‡~¸Ú²{î¹gèÙ“Y–?‰‡,Ø(¹ÏŒ$ˆÐ§OÝæGþ0º=ÓL3åñ€ñ°'@UÓ!Ÿ)˜Ÿ|ò‰þ¡'š…ð'ÉŸI7Þ}÷]­;ùä“ëþé§Ÿž+áI `>€Ì %€—MTÌJÔµŽI+IÌ#lÈW^yE³Î9çœ0zôèbÅÆ8†- ÝqÇíòîºë.Ý·|vÞzë-=Æ´3É<2D»†I&™$Ì9çœáÍ7ßÌóK/½¤õ°‰ Ì$\vÙešç?©%€¢3oj²Ê©+æ*Ð)mØ?‰A¥¢Ÿþ9È­8L:é¤ú$ÿÆo´{x{6,¸à‚y^(¹ÏLì³Î.¸à‚°Ûn»…K/½4¬½öÚáùçŸ×‡ÅùçŸ??¶øsˆ}­Úv¹å–Ëçنضzàáï…^b¯1C4{å•WÖÉaÆ1yÂFmî¾ûnÍ“DÓûî»O5¸îøOG'0‚ƒ3Wê©ÁšÚ–jtÊ”ÙdÂ3Ï)¼¤ðšÂcØ@f{챚'NmF™ÂŠ2ý¤ÛÇsLÍgžyfµ5_|ñE-›|hã@á>å¨Ú}믾züì³Ï´¾ÙÀ«®ºj”—š/fˆ>‹i1ìß×^{-läÈ‘AÖ6trf‡óbzï÷ßòGË—· ¦úÌF¶cž¶—@NžkËÑ/„™å!ãៅG‹ŒS½CW‹è8û­ ÂQÊ$0‹5'³íÊðàˆíÍCd’0gàBBøÉÃÂ|o¡DJbMW)€í–Zµ•<åNÈ@‹òr»Ù©¦H↎3¹Ó©i\°F4•g¿îi§vªû16Ñ 7˜¡¶ ^R™&JL;Öü`ºÂ¶ò´¾%Sn`%I†ä±²¶ùd!@掳têušF†Ÿ¤¶c©N2+€éÍK}R:g;Óm@ê9µ–’X?É»x*IT`ë(ùϱXž§.b0…gy™_¥N‚—m°]Ok"¬.Ô$ krÞIÃJ ‰ð”Ü/û¤²˜èÐØ:Ì4«ìiËI ‰@\J1–L%¶ºZR;V²CÏp ‘ØI ^Ú©p™U‘“ðC­'ŠÝ*0"7gúµÞ5ó3ÎaA€?»ƒgÂOV[g¤¶Í€l0l;¹JIœVÀá0‰¥RuÛ·Ší¦Ø± ÓMxÑ”x© f*p ÊÞO¹ž$à®§«ácI-pj‘y…z’€¸ž®†%µÀ©EæêIàzº>–Ôp§™W¨' 8€ëéjøXRKÀœZd^¡ž$à®§«ácI-pj‘y…z’€¸ž®†%µ²Ø<‰,MݱWp ä$ôLK§¬6é[‡¤™¿,µÆú¨Æ†ÛÅUa–Yf ehlE=¿±%`&CR Û±TgV‰ Œæ¥>)³]ô6@¬ ‹'!eJÒ 7Ü ñ2$VÉ2žÑHbü$ïâ©N†*¥ä?ÇÒ®M¢þì³Ï>zì„NЈ뮻®†»’Øq@/a‘€…šý‡ð°tPØZŽà*•˜&íä;]-Sx6Ž¢ŠÏ2;J+±i7 ^¶‹X‚†gžyFÇ!5ð6€Ýe—]4¦1!«A{Ë-·hàn È¢ '|1àELdb¼m±ÅÚŽÿ¸@V \¨Ò^Ð%%¼Þzëé²þÄD˜X!¾qÚˆ6?Ùd“â K4Ì ¡aƒ„r ¬¢Nå”Sòy<ð@Xi¥•Ô?ÿ‰o¿ý6œzê©á¾ûî ‡vX¸ñÆÃi§¦¼ñÆ<ÖJy±]{íµ!é¦ùÕW_……^8Œ92ߟo4 xË•–å4™ž@x á…ç^J˜ù=i®= X¢|a=öØø¿ÿý/Î;ï¼Q<Ë´øÇ¾}ûÆÛo¿=¾ð Q¦Í¢¸SjÞÜsÏÅ8¾óÎ;qíµ×ŽÐH[C‡GydðÅýöÛ/Š#PÜu×]ão¼àQþ(ñƒ>ˆâ$¤í]xᅺ߭[·øÍ7ßhÛ_|q4hPûú^§K|äp^ÀMapÄ;…qe©ð˜ª°5NGÂeXÜ%ã2Ë,“à1Ëëa¶ežyæ™qõÕWò{÷îO?ýtÝþî»ï¢¸bj±5×\3Š;¦n`m4Ï<óÄ .¸ÀvЖ¿è¢‹FñhÓ<þ'Ÿ|r¾œoÔF‚›ª¸&&·ü%–XBÆþ' l3¼õÖ[áðÃSO=µòþûï>ÿüó Ú2`Zœxâ‰aŠ)¦»í¶[øõ×_óõ’ ,°@~·#/6œèÿûßÿf@îºë.uéÌWô†”@MŒ+$ŽìF¼6êÙ³§Ú°€1b„:¶²Å_\_%?üðÃêryàZµ’iG^l묳NSEÁ;pà@ýÒ£dCžÑ¨ €W[mµpÏ=÷„·ß~;üöÛo᪫®Ê gÈ!º×¼å–[†ã?^®æ›o¾ ¦C[8ðéÑ×_­õxÅ\̇˜Ì޼Øúôéú÷ï8à€€6vj| Tê Q–fuÖ°ôÒK«WN9+¯¼r¾Z€·µµé ‚ذáì³ÏVžUVYExplÿòË/ÃI'¤õp«Û7ˆœoÇ6ðb›W½Øø4o¶¤ÀÅLÙ`ƒ ¬Š§ ,J½ÑzȹO$ÜS˜'É©„¯•ÇIÆ$ìSÜ"§™fšv™”G;C.IÔAÛâR™¤Q£Fi[hãBêÈ‹ c¾~>|xa5߯rÞhëHW|^Ã-•yÌQÂ]â&ý–O2]V´0'U\+XªNG Ÿ”òbãæ ”:5‡jb׋¨Ð怗‡C§æ@MMˆæ™ŸE%¨¶ Ñå˜Y ^ÿ621åWÊî/÷¼°Ûa§tèr³PÉE]TtÔ,dòꫯÍÛAy­Xk­3°â“a/V˜çfm‹Jˆ‹öÞ{ïJšhɺ]àŽ¤Ž3‹ýe!>•×ÒYªŽµw ñ½£GkY/й¨€ï¼óÎ0×\sÞ¼‰Bøì³Ïògfk>ð:™Õx {챇¾™£ 8ú„ÁƒëJ=hmó"ã¶»ï¾ûêkh¼ËÐæhCæ—yä]¾*ßQn·K.¹DN¡Ž8i¿h|–¸b)+è‡~Ûl³ö)¾á‰'žÐãâ‹¡©8åËòjO;fM’œRçg>š9rñýh÷¦R÷Ÿò$€ Ó²´ÌäkÙÎûl·Î¼3c“¥¯t 8áõ†7Þqrþñù矢#íá($¯¼£LÕi{ò'P¸RçÑ€7ÊzpñÜsϲBQü¿ÿû¿ä0›r¹ ãµØ8Þh⬓,WåÓO?UL@"¯óKlÁ¸ä’K꾘 ,š2_†?®‘ò2#â}fî—ÀÓ _~ùåqÅWÌ×InÐ.î›Ðƒ>»wïeBÝß}÷ÝÕES^[+ùÃáQ'/B¢œ‰–Õ>01ÌSŽ•2“¦O²m!4²õÞ::ïŽ<ô¬OÇ.šøBà!öÒK/åGóñÇ+€yz‡ðs0bö énÉqìfüxl$¦A˜vÚiuÝ<ØŒ°EËYKblë³Ñ'`„gø0þøã[WíÒbíutÞæ¡‡£„‡n£Né$P Œ‡ÀâcNˆé­å–[.ä&µÃ•W^©ZO†/P¸Ph,±¥µ¾˜êD}ØçÁ o¾ùæº<+à+屦Œåç"1 ´mŠâ yà¤_k{¥šêè¼Éc!oÚ`Üxë9¥—@M40~–'n ˆ ÜN›r G#8¼Ñ¸¸IB#³6®L«1;ÀŇp¿dVú€–'{ÖfF€9d¾ƒ“¦dsemãOA=¼×pªÀxÀaâ@x¸É§Qª9K5ØÑyóG[vÙeÕD á1þ´¥Úôãí%PÓWÉh´,@.ô"CaJf#4 ±5ÌÆ%óƒ}l×$0\5“íò¢·df$Ë—»fGëÒ¶Ì„´«Æ¹°÷بÔyó“ m—^+Pµ_%×Ài.oºöÚk/}P³¬4õ½l}J Ú®‰ œE”œ¨L‡åg²´áuš_5±³ˆqçwÎRÍë´˜º\7ƒ7Z)Ì`;u®ºÀèVnäQfCøô©9ÆŒŠSåèrwt õêVnäÑŽÎÍóª#š¸”W§QKo4ñ•Ûo¿}^zL…ñº˜Ï÷y»·á†ê ÞüÙ˼ÛX{™¾®NF¥¡C9Dßþ1/|ÄGäÛfC–µ }ÅC9áRqîJ­ ×®!ß).&ÑÓ²´Ô°Þhòr#Êë`u“G qÓd3ÊKu¨ÁÙ§ÖiÃÛL|‹um7œydiWõb“à‹ZGÌ(/MÔˆµØpìÁ‘ÂI¸'Ÿ|R=éð”¿mO­e:ZN 4Ù`ǽÑ*õFÃýRVÈTh°FÚYg¥Û=ôz¥Z\+6 `yËåEŠ–Ã Ó,ËDÑÈzWK¼ælÝ6 à$ήÇF{àRkÃYfK« àšL£uä•%' ½Ñøü=IIo4;Þ‘7eÄW×ŠŽ‘²Êk¤‰»¥š ,¤1#Bè[¯:ß'+bL9å”ÉCºÍ4–z½îºëô­« %©ðÜŠyÚ‰†ÇsŒVãía9ÎHÉ>Zy»&6pázeY½Ñlý4R{paäbóªÖ§¡ä:lv<™²Ö0Å &Î'ö“/;6Úh£€;&nžä•Cؼx©acË›w™ÕMzÚ™­oy¤¥Ö†K–ñíÒ¨ €;òÊbhµöFã `¾xhƒ!þÅŒO3Vð)‡xÀÃ{ŽXvÜiXˆPnûùªÖŽØÅzWHjd •Z.߀ot,„–¥ÅTqØqâ¦5|z#·ãüƒŽ}¶#€Š¢ £\Ðü×<ñI$߸i}qä‰âðeŠM‹6×OŠÄÙFË ïz\ÀŽÝך‘û‘[¶~~d_Up˜Oh›¸åÛ<}8ã“£¤ÍJ¹¤ ÌC_`p^|¦$ËÀFqÎÑ…´±ùòBæ|Õ†>ú裩ޮ=ìf¹ hŒ›˜/Uš•;U}ˆ«©3O)¯,þbõâÆñ^C£bV0ÍVÌöeÌF¸bv˜'&_qÑf=Ô\°cÉTÀZrm¸d¹fØ®¶3OMœæ¸7Zi5NÙj¸&6pñº7Z©µ^šL£e«{£e‘ZëÕ©[ Üz—ÂÏ8‹ÀY¤æuêFູ>,pg‘š×© 8€ëæRø@²HÀœEj^§n$à®›KáÉ"p©yº‘@ÍÌšgø@|ùÔSOé6®•â\®ÛþãH+š˜šïB"Ðë·b V<½‚x¥·—w ¨ê֙ǯOsJ eœyšóòùYU[53!ª=poÏ%€ÀŽƒ†–€¸¡/ŸÞìhh 8€úòùàÀކ–€¸¡/ŸÞìhh 8€úòùàÀކ–€¸¡/ŸÞìhh d°-¿hiC ÁߥC†#KËPV[Ö!é9¶L>ø1S‚z©¨'iEËr«€8† ù×™ùPéC£4ÕÒ„#Ö×ÂÂÈõFáz$°›r¬`ÁÌØQx…ZX¥£j˜A¢e'lÀ6ðšöÅþuóA„P”ºm°0w¼Ë„ë‘Ъf^‚˜ñ.Ø6›–Cé(€‰ # Tû—™vµApѾôcàµ4ÝH½4øJ¸-' ŒWF›;VOIÜ0€5óÔ¬eÁ•KE™œë!9@L;ÆÀhÛÜ7ù^BôçÒF50¡l®)lŸ<Êe¢ÌΙ€’x-å_fvàfº4E+`SÈø}áC„Ʀ¬72`.HÁ  5Ph“íL”Àô–1°vK0ðR bß©r ¼ MpÍîæ%Ññ· ×+%Ìv!W^Nº"Ó &ú=ñ ÙþQh_§êI€e<û (|ŸðÂ< 5 )V˜©xÜ89‚"3p'‹ùveÀl¢,Ú$ù@dnÊ¢²V´¶ßÚðÂåîtgà~U‡œiyÕªŽÀs T ×ÀÏ«v½À] |HÀ\𺪪ØÀ; ÏÕUý×S¿n×ÓÕ(c,¹¸o¥è,2ñeUšºˆkàÆ»¼dÈ_:xÿ¼pàì>Dª2Ï]Sà¾"û¬¦C‡ «®ºj^+kãÖ[o Ë-·\XvÙeÃ3Ï<~ùå—pØa‡…!C†¨VýáVoú‹î¼óΰúê«ھ뮻ôóüóχ£>ú¯B¼%ïÕù0Ó9½ ¹AóÏ?¿›~úé£hØ8jÔ¨8ß|óÅß~û-Î3Ïùä“pÕUW…¹çž; 4(l´ÑFá¹çžÓ¶´€üÌ4ÓLA´µîŠvr“OÎÙÍCà]ËI'4\}õÕá¶Ûn _}õUØe—]Bß¾}uã7SM5•÷§Ÿ~R ½ôÒ˜^ŒN8aXe•U¶1Ú-NµU¸ýöÛ³ôa4Á¨m¼Æk„ßÿ=l·Ýv–Õ4©‘‘ýRâ|hÚêbãÌ…ñÆûSwˆMºuë¦Ú–¼©§ž:0GK¾•)ì0cš}ôÑGaÚi§USÅŽY*6pèÑ£GÑ<+ÓAú²äÕíçK®;¸r‘@“4þø,õû'Yš³#Œ…4à 3ÊïO2 «P5'µ7Ôšóý¬šXà&¾¸­pjàV¸ÊM|Žà&¾¸­pjàV¸ÊM|Žà&¾¸­pj>\§WYæxñw|SüžªÓ!ÖŰÀuq|Y%à&DVÉy½º€¸..ƒ"«ÀY%׉õÄþÝCxöNì¢išv¸Î.¥ÇâÁM-põû­®$渮.‡fˆü>êà-ïÂ8€Ë“S-K •Îî¬e‡Ü—›uvõÄ„X\†ô¼hàö_gÖÙ8ëe8àz¹>ŽLp"“ؼR½HÀ\/WÂÇ‘IàLb«~%±}Ç~^xÊê·Þ¼-:€ëçÚ.ÌPäáí«úRýÄ\?×h9ÊÝõ3œÆ‰¸~®Ó²2ŸÿMy=j:&öÝ_ ¤¨o ˆD켚PU\˜VKkrBÞIÝI Ìùýj½¢…MŠ“ÄÌ€klRu0›$š;̓4qšÈ6Çól7äJ€\€sƒ3x SŠ%A«æIBÓÉ6»Ik†M@ xI“ ˜ÿÈYAœÀ9íkàWbžsÎ9Ú‘DÝÑ'Û³Ï>; ˆ#õ—Yf™øÂ /Œ1k¯½6|ðÁñ믿#O°Ä]wÝ5Ê*öïß?J(ªøæ›ojì Y=.±ÄQ–ó²o\rÉ%ã–[n©m¼úê«Ú¦D*º¿øâ‹ëøäN‘ïS‚¼è1YÓWm½õÖºÏ9‰†Ž®+J@Í£?bcˆ×ý÷ß?Jà–(átLÄÚ0zòÉ'5Æ2Úl³Íâ£>ZTÎV¾Ö)×Zxná6áÞÂØÃà<©ù)c*“eL6JG¨þI8ŸÌ(ÌGˆ‹!“Ï>û,Šö‰«­¶Z<è ƒbÏž=#€àBHT½p²òxDøÿþ÷¿uƒ 6ˆ²¹nË­VÛ”•ÆuŸ‹×§OÝ&ÈIÀûï¿¿§bÄŸ‡1ÊmyŒl.º,6 ÐX)·óÎ;k¹›nºI÷©/·{Ý6à0ÊZ°•ÂýÉ&›,Š Åöm×瀴žÄ­ˆà^½zi0YÀ:²b»þ‰hŸsã:pàÀ(k+@‘ƒ˜O RYù=ÊJðQ̓ȟ‘ãl‹y6†œÛ ¤†;œ‡08™CÜL!<‘°ÚÅ2”T˜LUØ—Î0=¸L,ÌŠÍü›øW CfJ/Ժ뮫`CÝ|óÍZÎòdi|Ý¿þúë#šTV!×\sÖÛtÓMã_|¡Û+­´’–{çw¢E‰^xaÀJJËðç(EDºä’KTÓ–À\p±?óÌ3ç‹ñ’¸QVNWðXƇ~¨mJX=”Üç<É /lÅó)ò Onïyß{z/144OB´ð#<¢Ç÷Ýw_•ù´qÈ!‡Ä3Î8C·/¸àmã²Ë.Sq§+”s~5Þ`¬ÂK Ï# n¦æCVðT3 l¦cný…^Nx ‘jJ¢ç¨pÉ—`%ñÒK/Õr…‚}ê©§ôHåÑzhg¶-,U²ÓÀäÃ묳N2»ìmË­Û³ÆnñhÖwrIâ_¨6D›&Éî"„Á2 Œ¬ nýô…¹ƒÃ6øòË/ÏÁÆBÊŸÙ° ©PÎ…ùµÚÏyyIÁK?a3#Xªž™ˆTJµ’‡8꘤Ø0­—/;F„wfĦÕ)!žº¿ûî;+’OÅ®Ôh;D¼$t”‘ؤ3Bì`=DzÖYg¹ÐVDƒ ŠmÄÎ ¢ióÇÓl0PŠÎ<óÌ|Ö©§žšßۆćӸÉ(žÌ ©S4¼†Á²6ÔÉFSf,’D| h¯½ö ì<sÌ1¾‹<“‘±7êŒ0À Â6 ‚£Ôd•SWÌU0ð2˜AADæm{ì± 6e’|` %Ex)y°Òi$b›µµµ Ú—oKìæ C N1?Â~ûí§Q}¨cD¨).¤Ló…=öØCÿÉ–g©h4¥F¤Ì4ÄEnш?ÄzclDÊ„K|¶‹/¾¸è¾Øã:&y¨ÒðZb"åCli(ID/úÏþˆgŒl† ’ÌHQ§ÔÄÔ œÓsôÍ–Y εH´ºÎð »B9·k´ö;àp&ðJ=¹ß¤TÙ”¢S¦Ì˜˜AxNá%…×ãnDtJÈ&šh"½Åñ„I¼3mÁG@ͦ·[ö×_ýÈÝÖï¸ãݧ˜Ù"`š í a“ XHc{ˆKÚªfB0þ9æ˜CðDsF³C‰ÊIÿ…m…ûŒ[s±±ó€%àÍÏLÌÎ]§æ’&û <ÊŸ(ßÖæ›ož@ÜsÏ=óÇyÐcÖ*”³삟Üùƒ“AÂàü€#f"R›Õ0O’ Ã|-á’bLŸ#ì@ò!1-ô¡­X9Êð°Äw£³ÌÈœw”PZ%‡Ï¹I`Ä|þ+¯¼¢ d'IØáŦ9F&Ïd¤œ“Çkµ >r8À„ïÀ“ g°>õIåT$ ÌS#Z˜¹I…óÁüå%qª–0, 8»™; á/„¿þN˜50~Fƒ›#™Å VBv»bT°#·i–¨+S‡›—´I(‰N)3nŠ>t•)$:5ãÛ¬ƒ‘™/³)/ÖB0Ü ‹yŸ1U%¯W˸jh ~’ZØŽ¥:±¬¦4/õIéœí¢·¼¬ÄIF=ƤLIâaEzJæ{FÓH ‰𓼋§:ÉJl%ÿ96ËÓTæ€54hP Ò:t×]w……Z(ôíÛ7Ø.Þ,ÉëPÍ?á„ôÅø ™72'¬o³È”9ÍÀÓ8¾µ¸$òrÄÞ`ñ‚a®¹æÒ!¸-:Õ¥LáÙàŠ*>Ëì0åöž–¥A€Êkc& û3Ÿ·”ðÚÂÒ\{Âñ†ãòjX]y‘±è¢‹FœzÄ6Êë[uLáÁl³Í¦•q£Äk‰xyèê0C&“ýx‰É›0õ»J·qkðª»#®¸nâÈâT?¼/,v/fà 7Ü v.kN­)JM>«ŸH˜oûùX‹/LÝX„àT\9Âü¿–R#…G g2!RmE 8€[ñª7Ñ9;€›èb¶â©8€[ñª7Ñ9;€›èb¶â©8€[ñª7Ñ9;€›èb¶â©8€[ñª7Ñ9;€›èb¶â©8€[ñª7Ñ9g°¹›YÚD"ñS©±ÀáÈÒ²‡ÀÖuHŠs²“K Àa(M½|Ùj˜˜{\EƒÉÊ7š]\s©ÌŒ›Jü ¸ÛÌ6ëÛÚÀØwr $%`¸AyÚ6xÉt¯À ÌÀšL©/2(\ òµÕª!Å®i#§ÜÀJ’ ;Écemg5!l™;.kt% •Z[­Dq?\0ü$µ°K5Ú¬¦4/õIéœíL·©çÔZHbü$ïâ©$Q €­£ä?Çbyš€„èD¿!ð%ñ!C+ë@G}´*Ñù!¤j2X ŸÇó&«ôØçó”-¶¶šµáiÝKÀž 4³â«ÀIð²]À¬ÍÀòª|Ô ˜guV]NŠÑjÊ&±ýO?ý”Í@ÄÂI± ™œ~úézœŸ+®¸B#þuˆrÅVöÉö¦•@Ö‡¸¢@)%ÝNh½{÷VÐ2AqüñÇëG™ÉÕuÚ–Öf3YœO—Šb™( |’/Vlm5â±95Œ’XOÉý²O¢ L‡ÆÖaÉAôë×/¿°뜱ØI±E®-â<+¸Kàk]À„à~»í¶[~l:+gm5”§u+$~q)ÅXò*°Õ5Ð’Ú±1:”ðNùchV ÐÊhä¤&Ƥ€˜*cåJÖ –eXÕD8ðÀóm[7-Ÿé&°“¼œdIÀ•)²U¿Ä0Ä:,Nfµž7Mìd`-ƒ¹0ß|ói8Z­²Àµ¯s¦¢i¦Þ2ׄÕ¶ú¤âÒ·o_IÀD¬„S…Xÿ—5~Ég‰(f( ^½z…UVY%°Ä*ËH}ùå—ᤓNÒ<ÿix VP v·c©N®Ò…MÊZí¦›nRðÿ‚é³¶¶¶¼=Ìh1!˜yH®lgAyfÐØN/œ—Ac®†ÝÊ[´Bâx1ðRÍìÔt@ۚƵ4ÓIVj—Õ)+¨K@ê²Êz!—@ TÃkØÂŒµ°p ¤@M4pŠñxQ—@* 8€S‰Ë ×›ÀõvE|<©$PwäÆq★ "ç°aÃ4ˆ g±ôÒK‡Ë.»Lckà4|øðT'ç…›_5pGÞhgœqF¸ãŽ;Âã?î½÷^}Áaî”*¼è¢‹‚Dë ‡~xØf›môsó_?ò%Àç?iY'ÈˬÊNÄÂCÍJ¬¶(VÃÌJ_qÔ¨QQ||£¼•‹âAÅÓŒÃJâv‡ªÛï"ŠöÎåĸꪫFñÎïûFãI@°Â›7^d,•ÃMIÁ/ÅÆ•3J…Çšh`X(寛6^ñ¢ƒ@…Fýû÷·M}©c“KÀ$P3—òF›sÎ9ÃG}dãQÿ_b¼%}€_|ñEõ`³é¤“ŽáÔƒï0Îí={öÔ¨ö%;ðŒF•2YZö¹d°u`’✠—¤RÞhDß0`@Éz¥<ÕJVðŒz—83Æ™Ç[© lÀµ´hœÛ¬Þh¼àèуXŠNM&p'œé³:´ünÂÌ©“`ßDëœRøfáŠ|<¥¾SóJe·ª0Q: öÍÇ’? ©óWyïð..eÚQ%&„©:äßô[Ž7’tra=‰0* x^€85¿ìŽü«œêhá„*‘é-ÇÁ‹iáT •zyÊ à$xéœÁØ`ù'/lm“ÇI`4·kgB àLb‚xÈhÚŸ…Áy” O²™Ž déjýUšŽm  ( ^€ xî.L_`B i`S^¦…ÑÄ0û0ùà%˜ Ä•89H(Z–[Ä1þmüëÌ|¨ô¡Qšrj M¹V°`(¼B-,‡ÒQ5Ì Ñ²€6`xMûbÿºù BhB«šy >`îІ }h“ý¤–Ýt” Àø@ÉçÑTû—™vµApѾôcàµ4ÝH½t£I ‰îÆkæ©i`- ®äX*Êà\Ér`Ú1FÛöà¸ÉwðŠZˆL £äLÑ À ¨)—‰*0Ò±ÒÀË@˜™f:8€E(-DàÁœˆ ¶Oå2Q¦Ö“˜^Ó°€ÕkvàšÀZ/5`’P.Ûj¼ÙÎD˜ @lƒ0ð&÷mÛÓÖ‘@ÀlrEàEŒ8y-`¶Ã¦¡mßÓÖ–€:ËÃZ)±UÀ¥:ñã.Î’·z'—@ÃJÀܰ—ÎŽÀŽƒ†–€¸¡/ŸÞìhh 8€úòùàÀކ–€¸¡/ŸÞìhh 8€úòùàÀކ–Àÿq̈¿Zɱ?IEND®B`‚doc/next-tutorial/per-class-mixin.graffle000066400000000000000000000624101242365656200210230ustar00rootroot00000000000000 ActiveLayerIndex 0 ApplicationVersion com.omnigroup.OmniGrafflePro 139.7.0.167456 AutoAdjust BackgroundGraphic Bounds {{0, 0}, {559, 783}} Class SolidGraphic ID 2 Style shadow Draws NO stroke Draws NO BaseZoom 0 CanvasOrigin {0, 0} ColumnAlign 1 ColumnSpacing 36 CreationDate 2009-12-29 15:06:59 +0000 Creator Gustaf A. Neumann DisplayScale 1.000 cm = 1.000 cm GraphDocumentVersion 8 GraphicsList AllowLabelDrop Class LineGraphic Head ID 19 ID 10 Points {362.173, 304.10599999999999} {362.173, 274.053} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 9 Class TableGroup Graphics Bounds {{294.173, 304.10599999999999}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 11 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 SafeStack} VerticalPad 0 TextPlacement 0 Bounds {{294.173, 318.10599999999999}, {136, 12}} Class ShapedGraphic FitText Vertical Flow Resize ID 12 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 VerticalPad 0 TextPlacement 0 Bounds {{294.173, 330.10599999999999}, {136, 12}} Class ShapedGraphic FitText Vertical Flow Resize ID 13 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 VerticalPad 0 TextPlacement 0 GridH 11 12 13 GroupConnect YES ID 9 Bounds {{329.23142275820402, 358.31565478004751}, {75, 24}} Class ShapedGraphic ID 14 Line ID 8 Position 0.55582332611083984 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 7 ID 8 Points {363.84399999999999, 342.10599999999999} {367, 378} {362.173, 387.69200000000001} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 9 Bounds {{317.173, 387.69200000000001}, {90, 36}} Class ShapedGraphic ID 7 Magnets {0, 1} {0, -1} {1, 0} {-1, 0} Shape Rectangle Style Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 s3 : Stack} VerticalPad 0 Bounds {{181.15389598293871, 297.47410116179435}, {72.10552978515625, 24}} Class ShapedGraphic ID 15 Line ID 6 Position 0.62633061408996582 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 per-class mixin} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 4 ID 6 Points {294.173, 319.94065347289455} {231, 317} {180.73288414687903, 277.51900000000001} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 9 AllowLabelDrop Class LineGraphic Head ID 1 ID 5 Points {178.25987156357274, 179.51900000000001} {229.0263150257764, 138} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 4 Class TableGroup Graphics Bounds {{50.34620000000001, 179.51900000000001}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 16 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 Safety} VerticalPad 0 TextPlacement 0 Bounds {{50.34620000000001, 193.51900000000001}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 17 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 \ } VerticalPad 0 TextPlacement 0 Bounds {{50.34620000000001, 221.51900000000001}, {136, 56}} Class ShapedGraphic FitText Vertical Flow Resize ID 18 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 init\ push\ pop\ } VerticalPad 0 TextPlacement 0 GridH 16 17 18 GroupConnect YES ID 4 AllowLabelDrop Class LineGraphic Head ID 1 ID 3 Points {335.8324339647437, 176.053} {315.37656540679257, 138} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 19 Class TableGroup Graphics Bounds {{294.173, 176.053}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 20 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 Stack} VerticalPad 0 TextPlacement 0 Bounds {{294.173, 190.053}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 21 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 \ } VerticalPad 0 TextPlacement 0 Bounds {{294.173, 218.053}, {136, 56}} Class ShapedGraphic FitText Vertical Flow Resize ID 22 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 init\ push\ pop\ } VerticalPad 0 TextPlacement 0 GridH 20 21 22 GroupConnect YES ID 19 Class TableGroup Graphics Bounds {{221, 40}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 23 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::Object} VerticalPad 0 TextPlacement 0 Bounds {{221, 54}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 24 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 \ } VerticalPad 0 TextPlacement 0 Bounds {{221, 82}, {136, 56}} Class ShapedGraphic FitText Vertical Flow Resize ID 25 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 info\ destroy\ ...\ } VerticalPad 0 TextPlacement 0 GridH 23 24 25 GroupConnect YES ID 1 GridInfo GridSpacing 14.17322826385498 MajorGridSpacing 10 GuidesLocked NO GuidesVisible YES HPages 1 ImageCounter 1 KeepToScale Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo Animate NO circoMinDist 18 circoSeparation 0.0 layoutEngine dot neatoSeparation 0.0 twopiSeparation 0.0 LinksVisible NO MagnetsVisible NO MasterSheets ModificationDate 2013-05-04 13:23:28 +0000 Modifier Gustaf A. Neumann NotesVisible NO Orientation 2 OriginVisible NO PageBreaks YES PrintInfo NSBottomMargin float 18 NSHorizonalPagination int 0 NSJobFeatures coded BAtzdHJlYW10eXBlZIHoA4QBQISEhBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCEhAhOU09iamVjdACFhAFpAIY= NSLeftMargin float 18 NSPagesPerSheet int 1 NSPaperSize size {595, 842} NSPrintAllPages int 0 NSPrintReverseOrientation int 0 NSReversePageOrder int 0 NSRightMargin float 18 NSTopMargin float 41 PrintOnePage ReadOnly NO RowAlign 1 RowSpacing 36 SheetTitle Canvas 1 SmartAlignmentGuidesActive YES SmartDistanceGuidesActive YES UniqueID 1 UseEntirePage VPages 1 WindowInfo CurrentSheet 0 ExpandedCanvases Frame {{522, 410}, {758, 764}} ListView OutlineWidth 142 RightSidebar Sidebar SidebarWidth 138 VisibleRegion {{-30, 1}, {620, 670}} Zoom 1 ZoomValues Canvas 1 1 1 doc/next-tutorial/per-class-mixin.png000066400000000000000000000633601242365656200202060ustar00rootroot00000000000000‰PNG  IHDR¥¨æLúp pHYs  šœÕiTXtXML:com.adobe.xmp 5 2 1 m ž@IDATxí]¸EÖ½’sR%+ DÅ ¢˜]T0»bZT û«ˆYqMkDYÀ¢b@Y]*®3AQTÉ9÷O1wì7oÞ{ÞÌôôœû}w:TuwÕ©ÛuêVÕToãyžPˆ D€ AHÓ@ˆ D”hD€"HJ) &„"@HJ´"@ˆ $¥ÀBˆ $%Ú D€’R`Š‚ !D€’m€"@ƒI)0EÁ„"@ˆI‰6@ˆ A€¤˜¢`Bˆ D ! D 8Û¨?Ë3aC@×þäâŸ+ÔmX&+&'£”@6F@¶ÍhxóÀ"KPÑc’WöÊŒžRö°æ“rˆ@2B×µu_ƒŒL-•$(C"ÜÛ(ñø²¹E÷q>ªæ8“œ|(eh—¤”!`yÛ@"`Äc„»E¢ýDÈL0QAD1BÂÖ¯ ¨- '`Êœ”2‡-ï"^’REM–é ݧGr `2@DuU7E #-Ý¥d’R&På=ƒˆÈd›Ç¶ŠjNÆTýõW™?¾´hÑB¶Ûn;MFb²fͱF‰]ÀXi!iÌTÖ›XÃe³î{z¾‚zK 'J°®Š Üš·$¹G Ž—R!UËvê>ÿüséÚµ«4iÒDöÜsOiذ¡qIJ`Á—”×_]*V¬(=öXܤõèÑCºté7,Ñ“/¼ð‚Üzë­‰Fg¼­v{9Yw/qÉ $¥ ‚Ë[¿—R!e•”V­Z%ÇsŒ|ýõ×2pà@yâ‰'ä€W^yEN?ýt”¶¾eË–-³ˆ Ü!Cä²Ë.‹–èÉgŸ}V®¼òÊD£3žÜR#%ó²Ís">@€¤”PyËÀ!€J¶Žn;´x«ªÆíûöÛo¥gÏž2räHéׯŸ´mÛV†êˆâ‘GqaæÉ 4HößùùçŸõv"&Lk¯½V–.]ZìøÑG•Ÿ~úI.¸àyæ™gä´ÓN“7ß|SöÝw_‡ôÞ{ï¹kðóÃ?ÈÁ,­Zµ’«®ºÊοÿþûòÎ;ï`W6nÜ(Æ “–-[J»vídøðáî¼ýŒ1B:uê$;wvé_¼x±Œ5JÞ~ûmå°Ã“ 6XtnKFv{ÝÀ~`KpÀIL DF­2*1« èKƒÊ *—mU›ª¶SÝKU³]T>úè#¸)^åÊ•½ýöÛÏkÔ¨‘;V2ð”l¼vØÁÓn7oâĉîüyç½Á)§œâÎÍ™3ÇóŸ}öÙ.lòäÉÑøØ¹æškÜù‡zÈ{íµ×Ü~¥J•¼¾}ûzM›6uÇãÇw×hן§Dåö¯¿þzvÈ!‡xêq¹ýþóŸ.ìé§ŸvÇJJ^ïÞ½Ý>Òòâ‹/z»îº«;¾øâ‹=%%Ÿ?ñ€D줽na7 TkªÂžÜx¤^Éú³œ1  å (4X/©VðÐýRKu{Õ–ªU{©«Œ” à¦OŸî*ñ+®¸Â[…_¥JoÇwô–-[½‡z;Þ“O>é­\¹ÒóëØ‘»zKÑøØ7nœ;’1RúÛßþæâÌ›7Ï«P¡‚wæ™gºc?)5nÜØkݺµ·zõj÷<¥zL.H ¤ºdÉw~”Ý~óæÍ£ç0 ‚ J>®Û.¨;èºûí·ß\ƒ²fM4ܷФÛùñÇݵõë×wÇ<ð@$7) €®;Ø ºî °!ؤE)g0…„عU&Ø¢’AeS¢`\<Á˜Ì7ß|ã‚~øaY¿~}¼hÅÎal2eÊ”"ao¼ñ†;¶pÌž=ÛÃpÌÌó‡!@½ ©]»¶tèÐAfÍšÕ¯¾úÊ]‡1&#(œÐî?;v¬ ãOÒ ñb¤dõ%‰’! ä Ýž·%A ìÝZ¼¨h’’uëÖ‰vƒI:uÜ ¶™3g™` ãC²Ç{þ‡ñc†]›6mdôèÑrÑEɘ1c¤ÿþòÅ_¸ ݺu‹¦@x:^å¼¢ƒ:(f;:Vä¼5L˜1c†èø—h  >üðÃÝd‹áÇ‹v7ÊI'$S§NuaÚè¶o½õ–ó´ÜJCvpP’‚QÑNUŽ+ƒÐÚ€¾<¨H0ý»žjÕª=UU-6¦`cJwÜq‡ SOÄÁètlO§R»ýÛo¿ÝSÉÛyçÝØÍ—_~éâú'6àDì1â)ù¸{àÙпüå/Þï¿ÿî®·1¥#<ÒÓ?Ⱥpít,Á?¦¤3þÜøPõêÕ]¼Ž;zJ’î> .ô”£ÏAØÜ¹s]˜N ÷vÚi'¦ž˜;ÇŸøDÊv²Ÿ*ìö;ª®Š?Іö½ÉeÞròv-P È :s¤„i©ë¨b–R˜€—/Û‚Õ0&Ô²eK±1«Ø4`º6¦qëdŠh¦xc<é»ï¾‹ž[±b…è„Á˜”–2ò…©ê›7o%Ïh|ÛÁ´us²sÜE ‚g=û‡*æùcÐnµê:ÕõŠ1WuP Ê[à–Rˆ@¡!€Ê$gÝ0 ?ÙÄ_g÷‰ƒÉËÂD ¿ ++¨Pý“'bÃIH±ˆ”x ;f¿õRb’Â@R wù2w"`ÝYyYÉÀÛÁb1EÉ*~»ÁƒIN†Ÿ¤”a€yû@ €ŠÄ<##%$ ‹kÚù@$´¬DœþùeEaxù!`vƒ.`Û‡½°Û®ü0.v'’R1Hx"„XëÖ(ZÁäbL)„ø†2K‘‹ÙŽåÑlÇŽ¹-gР0#`• +“0—ræòföã÷–ì\æžZÀw&)páPÖá!ÁÖ±E…‚}vÁ(”2ðÛ ìÇïm—y1#$I)yÌxEþ"àoáZå’¿¹aʳ€5bìYlÌÚ’”2,o8ü„„}’RàŠˆ "[»1ˆ3%‘Ÿ¤Âœæ-}ü¶{ò§wÞ¡ô”ŠÀÁƒ"€JÄԲȊÅà6üöb*©±“Ƚ§HJ¥€Ã Ð `vnD„­ M&™‘¬ Û!!ej¾˜—·ìv Tqä]brº4UÞ¡•F‚IJi€ÇKó#&¶tó®èr–`³Ô•æiÛ¹œ%*Ì&)…¹t™7 `¶¶óVÁ`ŸBJBvb¶û±:ÓoK%]Ëó) `§p)/!y…€U"±Û¼Ê›u`/´™,ÂNRÊ"Ø| D€”ŽI©t|Jˆ YD€¤”E°ù("@ˆ(’Réø0”"@²ˆI)‹`óQD€"P:$¥Òña( D€d’RÁ棈 D tHJ¥ãÃP"@ˆÈ"$¥,‚ÍG"@ˆ@é”JLJ¡D€"EHJY›"D€Ò )•ŽC‰ D ‹”²6•l…gÛæ$|h(ð¯N{ÊP‘’”2,o8¬Ál£dðR2×1n’”’ŒÑó«TìSFRy›!&<+øí¤ÝdöJ¾?oO‚€*ÿ7qlßÛF% d‰€Ù ï¶{¡§Áâ")e\Þ:0XëÖ(ZÁxž%ŸÖÍ›7ËÚµk¥V­ZÉ_Ì+@¤Ák f;O¾&ÝwùZrLw¢X¥’PeòÁHÛ¶m˼÷‹/¾(»ì²‹Œ7®Ì¸Œ×˜ýø½%;—× jâIJA-¦«<€‡[Ç öãvÁtëÖM¦NªÁ¥Ë¤I“äì³Ï–3Ï<³ôˆ Íwü¶ûñ{Ûùž·@¦€Sˆ@¡ àoáZåR$ï³fÍ’Ë/¿Ü»ë®»däÈ‘rüñÇKãÆeÀ€²nÝ:5j”Lž<ÙmÇŽ+[¶l‘«¯¾ZvÚi'oøðá’N·`‘ñ ×X#ÆÒ·1cܦǔÒÇwÈü„„ý¸¤´råJùôÓO]ŽæÍ›'=ö˜#Ÿ /¼PN?ýt?~¼ôë×O^~ùeéÚµ«y䑎¸¦L™"Ó§O—5kÖÈ1Ç#Mš4‘3Î8#?a*‰@€ § Â`R2‚@\òÑ'ùIªÄŸp ҿÙÿýÙ|÷ÝwR¿~}©]»¶4lØPêÕ«'>ú¨ * `‚É( @“”²2Œ˜Êµr9õÔSå“O>‘V­ZI»ví¤zõêrÈ!‡&ÓLHZ˜­ ®4OÛÎ¥uc^m8u5>0<ô_ù5'•U««bé…:ªÛªn§:¡¼ì«;`:y:uÜÔp½7%ψ¬@Õ_³Â¥ªËUW«®UÝ ¶³Y·”rF€SÂËPÞ.°Xë6v[. ®X±bB+A”ËÃx“l"{ɈÍd3ùô,vßåSi1­D€#@R y3{D€|B€¤”O¥Å´æ 6ää¹|((DHJ…XêÌsÂüôÓOn5ð„/ˆ‰øÊ+¯È7ß|s–‡D€”„I©$dxž”X‚èÃ?,‡;ñD 0 )F93—I €?¿î±Ç²çž{Ê¿ÿýï"W>üðÃÒ¾}{§÷Ýw_4ìwÞ‘Ã;Ì­‡‡õò–-[&#FŒ·ÞzKn¼ñFyá…äþûïwzòÉ' V/iuñ矾È'1/^,Ý»w—+VDŸÇ"Zð? *1« è‹‹ÿ)USm ÚTµ£êþªøÿ‰f»¨(xú%YïŽ;îðþûßÿzº¸§+~»Hú %¯E‹Þk¯½æÍ˜1ÃÓzúé Ö¹sgO¿ÃäýðÞ.àê)éx¸×ÁìÝrË-žŠ7tèPOsõ† âÍœ9ÓSÒò”ü¼ŸþÙÓ…^ÝýtUrw\¹reoéÒ¥îÞO<ñ„·ß~ûM(2Žì#b'°ØM3UØþóVQÀº3ÐSRë¢CàÍ7ßtÒÿýßÿI¯^½ä’K.± yàÜ àX­¡S§NrÑE¹/ÏnܸQ.\(XóŸ¬=z´ûÌEƒ ‹¸n¿ýönUqÜÞ×½÷Þ+­[·.quq¬6¾û’Ÿ{6¾r{ÜqÇEÓÁ"fHJa.]æ-iÐÝÖ£Gèu{ï½wtöìÙróÍ7;’Ñ\y啎ŒÔ«tëÝ}÷Ý"Y¨â ÈÆ¤´ÕÅñaÁ—^zI0óï7ÞpŸÏ°ë¸%aF€¤æÒeÞ’FŸÀÇýL°tIݺuݘ¼"èüùóÝÇþ@û[fhÚ´iîóW]u•]Vâ¶´ÕÅáÁS!Á+×o)D  )B)3 #pÔQGÉþó™3gŽlÚ´Iž{î¹èµ}úôqÇø$:ô¯ý«ÜyçnÂn»í&Ë—/[rŸM_²d‰»Ë­]‹¥ÒŠKi«‹7kÖÌuñ 6Ì}޽øÕ¼(>Uá\¯Ÿ¯ð˪U«Ü½à5ÅJi«‹ãM:ãO&L˜{³€@d•pÌ0Á*áp}1'•*W W2%ô”2…,ï›×èÔï¸éGEKF±¤kà•$%­.Ž?Ý¢kpÔ¨Q%]ÊóD ”pL)”ÅÊLå;ðº@H˜@A!…„»ï ©´ 0¯©vß TÌr 쾋$K‡ô”²4Cˆ e#@R*#Æ D€,!@RÊÐ|  D€”I©lŒƒ"@²„I)K@ó1D€"P6$¥²1b "@ˆÈ$¥,ÍÇ"@ˆ@Ù”ÊÆˆ1ˆ D K”²4“?Œ7Î}š)þàƒäã?v‰ÿõ×_‹}=rÅ”ü@€¤”åÄTf¬Þýí·ßº'âk³ú‰r·ÿÉ'ŸÈ¹çž›Å”ðQD ðà2C…Wæ•c.3TPÅ]®™å2Cå gÂ7£§”0TŒHˆ ™F€¤”i„y"@ˆH’RÂP1" D€d’R¦æý‰ D aHJ CňD€"iHJ™F˜÷'D€„ )% #"@ˆ@¦ )eaÞŸ"@F€¤”0TŒHˆ ™F€¤”i„y"@ˆH’RÂP1" D€d’R¦æý‰ D aHJ CňD€"iHJ™F˜÷Ï5^$¶ÍuzøüüE6dvdÛüÍM@SNR hÁ0Y厀U"Øn‰h¹?„7 °ØŒiè2´ ‘”‚V"LO¦02²íæL=ˆ÷ °¨ŸœB•Á e¦RÐÄô "‚ø iŽõCnÛ¸þâÀ^`'æY›ᘒ!HJ–· ¨Lb •ÍIª»D´©nwPÝNuGÕ¥ª/¨R2ƒ@g½íoª‹2sû„ïjv±Q¯X¯ºZu¥êrUØÎÃVÌ["!)™’R&Ñ彃€*ʪ»ª¢"ì ÚVµ•j3UTB U—¨þ¬ú¥j{UTP¨˜(™A ›Þ¶êlÕ÷T7¨æJ@8FJku•êÕuª°„!Ô8ºKÉ$¥L Ê{ 1š˜“U¿WEøƒê۪Ϫ.SaÕV­Ñjº=ZõU„S2ƒ¼{Uû¨žªú†êªÙó” S;‡p<%“‚)!)e YÞ7(ŒÓ„¨z¸êbUO ÕZª5UAH¨pÐ*FëxÕùª3U)™Cu<ÓÇUá±¢+µ“êÓª T³%F0 xD !Ø‚‘l硈Å5” !@Rʰ¼m`xIS‚.»çTUE7fB­•l„TEÏ”^W]¨JÉ À•¿aŒí'ª«^¬ú¦ê8U”U6Þ<Šn<³ ì)!IIAÈ”lãyÄ7Sàò¾Á@@'Ø€ÐG…r®jUUTŒèª³}4жU¨ÚOgæ):zϧTŠsïúzn°jWÕ{T§©fZ¬q/Ȉɼ&óœÌ[Bø&­7Yqf¨THJ–· ‘ißè¶{Ou¼êªK2!aÿTÕ.ªWª’„ Ézß§T*åþðX¯UEßͪèRͤ Áµ.:#!xMŽˆt‹s.II‘Èàe¤B@žÏ1ª«ÎUE·žµŒ­;ï/zîVÕª$%!CÒ@ï‹òÎ%É›ðŽê9ªð^ÿ©ú˜j&^©Ÿ˜Ì>Œ¨F)%à»'IÉwÉZµo©›æÝv˜õ…©ß?¨¢õ Rj¯ºêUV< B•<&¬,ã(«U‘8;&¿ŒÛ•leŽ­‘Ò‰}(öEM û” "@RÊ ¸¼u0ˆÒ5šš³Uû¨¢‹­ï½UQ1‚”¨ŽUE ž’Y*êí1»  â ¼Ô“U¯UýTµ§êLÕLŠŸ”°«$¤L¢ï»7IÉwÇ€¦}?®ÚXuméþ®ÛOõ<ˆéiÕÃUÑúE%¸Ÿ*¶)™E¤O)ÖGêù[T«ž :]5âH ^v.^ÈÏ$)ré‡<ïJ<Í5‹/¨¢µÝ[ë Z›ü]wÐUw›ê[ª?hx¦[ãúŠ– f=®U¼£]azn=wŸ*Êl†½¤[J"€n ZÉÁëùPõ1­àÎTõºb0Fp¼ê±ªW¨>®Jɘ éºIµœÚ©NÒãçTïWE™í¢J)PHJZðaζVrçjþ&¨žªäƒI qEÖjÀþªMT_‹‰'3H©ž–Ó(ݾÑÖZOèþ•ªÃ4¬¶n)ˆ»ï °ÐÚe­È`Ï ¡T÷ÑJnNYyÕ8¿i¶Ì˪|ÃÿÐÛ¡Œ>PÝEË :5\÷¿Ör|YÏ_®z*¥ÀàŸg ¬ÀÚ]­È0ûyU̦;I+7l)E@Ëk{-£…ñ’§aMõüªí4Îïñâð\x`÷]x˶`r¦•X'ÍìÿTßW=š„ü¢/‰r ›§›Ñª×á˜RXÐS*¬ò]n•0QáaÕ ´2Ã`9%h¹6Ðl`6äÞZ®³C%f!AHJ ÅhÁB@+-üÁòZÕ³TáaÚ7%DhcÒCW-[ü±™R ” ¤ Ã”M­¬ð‡XÌÔÚQõX­´âŽM„)Ï…˜-gÌÒÃd•£´Œ?)D 1ÏS*ÄRÏãYÉËÑP -ëÍšMŒ-ݦ6€qDJÈ )…¼€ó={ZUV©ù¸P3±þ“ïybú“C@Ë|‚^OŒ LîJÆÎG8Ñ!K­@Ò¬dÄ?ÄHY—•Mµ…^ç1Õ]•¤@P”"@O)¤›ïÙÒJ¨³æˆ¦ú­ˆ¸BC¾jé×ò[/ÿNëRBŒ=¥n¾fM ©Ÿ¦ý!Uþ!6_ 1éŽ4T^×[·R’²/ÒfàI¼e. )å}>»ZéØbiþûY‘<(xÔDÆ(³Õ6n(x0B I)¤›oÙÒʈ}Rµ‘*ÿ›o˜¥ôª´ÐGá´m•˜þÐ-%dpL)dšÙ‰T4øŒ¾ot€V6\¡! 2 iVÛ˜«·tMÇGäzJ9ü%$̪ÂBª7k…3âÏîø¨Í`V&&=tW›ù!~,žÍWè)åkÉ… ÝZ¹üM³1Nß?"!… L³‘µ•Eú|Ìñ¦l<ÏÈ.ô”²‹7Ÿ¦(UÖ VeØOÓ½ÙÚU (‰# 6„1H|Òâ0µŸÏ¿’1ƒŽ=¥ —PÈÒéz™ªÙj¬ŠO–“BVÆÙÈŽÚÍj}Ϊ\¬5€gñô”²v¡?J ˆ}Qu¬ê5Z±x…ŽIó¯å…)ú~±cÛúÃr±_Qú*ÖE|+ (ÀgúßÙè~y¾Ë$¥´ª\dYë·þú\ü!v°0Æ‘(E BF ëI‰Ý…”€ þh}©jOP2ŽˆÈÈ[,˜kâÂÒ%(’’ÁÉmFˆTp×éÍÿªŠ?IJÿ?#H—ÏMµ¼@@P<H%U#)lMÁ"½£|!‰ qéhþlk„´IÏm‰hÚÄDR ±å:kZÁÕÒ4<©ºƒ*ÿ›ë)ãù>B2b­P Qˆ@<@BuU˜°…³’JI`tå*‘–1îIC.WdóîfÍ5Åh¹â£|U7™m¤ëÞë½(™Aï, õ¼$Ì’dÃUA ÄG òNWÑPØVo‡íà]¯*1• )E†DÅS=M‚$˜ê=VõÕUa°5çÚ«½¸-ŽIPŠBDË$„rÂZU•M©2þ|©\¹²l·þÏJ)Pªk¾Ñ˜€˜°o]{8—”¤EJ1dd.¿6ŽcI*©Ä1r^"€®º§T©¾§ZM®<ˆÈú£[#(’“¢“{á½E½BBeWüq9r¤|üñÇøß™ì³Ï>rõÕWË¡‡êâßvÛm²ë®»Ê1Ç÷úDN¶hÑBÚ·o/¯¼òJ"Ñ'wÀNìý¶çðC*ïuZ®¹>Óˆ[kaá»7 # ˆ‡ŒÖú Ñšr¥Æ‹-%G軌÷DECN…ÎвÑÍŸ2sæLi×®ì¾ûîrÅWÈÊ•+åüóÏwäôË/¿H½zõ¤R¥JÒ¯_?7.õ‰–;tèÐA¦Núçù(Ônž.ªø”ÈÚˆnÐ-<¦Í©¼× “”Dc^µ¬Ðÿ —ß 9ž®]»V`ÔñÂJ;·qãFY±bEÒוvO†Å/£LâûˆØ ì-rØO…ˆ=é.%ÛD°G9 l@N(h\Oé›o¾‘-[¶8Òéß¿¿œqÆ2zôh9묳dÉ’%îáï¾û®\wÝuz‘GyDºví*ð~N<ñDùôÓOÝyüŒ1B:uê$;w–¡C‡ÊâÅ‹£a¶3fÌ9à€äþûï·SÜš4dð>Ã~`G)Kʤ¤Oăq=aî>Wf̘á\ùm·ÝVÚ´i#;ï¼³\ýõqãÆž¼ù曥ZµjrÈ!‡Ä9ÙÝrË-òÝwX«‘`l¼ÂoÄir€óšOIÃûŒ2¢Œ ÅäOèÚk¯•#<Òuãíµ×^rÏ=÷¸÷z—]vq×Ô®][š6mê¡çœsŽó¤Ð÷â‹/Êé§Ÿîâ<óÌ32dȆºÝ~_|q‘g~øá‡röÙgËš5kdРAEÂxPïÃÃFC|Mý}NµU¬…#!pñë«î¨º³ªÞ²¸ì¶Ûn^5¼|Л4i’·÷Þ{£OÀ{ê©§ŠGŽ9Ó²eKO‰Ì›6mZLHÑÉ'º{j—AÑ”¹jkÕ&ª¯­ £v†¬ ¥7œe {T  :ªl¥º»êAªqmGÉÄS¯Ç½oˆS¡BïÔSOÆ­X±¢wüñǻ㯿þÚ»ûŸ~úÉûþûïÝuÚÈtaêýx:QÂSË<ØSrû5òÔƒò´ÏkÒ¤‰·`Áwž?ÁAe¯zˆêªx¯ÁõTÁ ˜—ôûœôö} *T&fÈ-t¿£j1ÄÔ•÷ªT©âuëÖ-¦ÞŒwÑEy&Lpç¾øâ ¯OŸ>^ãÆ½ƒ:È{ôÑGÝymQ9ƒ×Ù= òÒK/¹{ÁX;î8oÑ¢EÎØA|x¾vxÿþ÷¿=|õþö·¿¹kð£ýÞÞþûïï­[·.zŽ;ÙEå£ÚUßNªhÐÀýG+«Ô®_MiÊöÊkKƸ«)¡RÙUµ»êaª¥ÈW_}åÝyç^ëÖ­Ý»g G?)­^½ÚÃ{¬cMΫåU­ZÕÝĆFg<)áùÐúõë{Ú­/ÏåHù¥Û}TÛ©bMKì©¢&-éwÞNÒ3žr2w?n÷†Ÿ”0\?r—.]\·‰s÷1 ÑV–|þùçrì±ÇÊo¿ýæú§¿ýö[×Õ‡®‚Zµj ºô%£>Z–/_.J^2yòd9üðÃ]÷žš»¶Ú²’Í›7Ëc=&JB‚1©'žxBjÖ¬)úB¸xüÉ«@!˜»ov˜ºËŸ³¬„êÁ(¿â½.&˜¼€îº)S¦¸‰—^z©ÜqÇ.Þ«¯¾Z,¾’–{÷† &K—.ucG &–-[f‡2~üx;vlô¸nݺrÙe—¹ën¼ñÆèyî Ø xÀlïqêïr*Ly *tÝ¡ ¦¹j'Õžªqy[Ç{¼Þ½{{JPÑÖöM{sæÌñ6lØàÜû7ß|Óûý÷ß½3Ï<ÓÅG©S§Ž§SMÝþ%—\â¦OŸîi³§}Õî-¶Øî;}Q\˜¾<žººýQ£F¹ûð'7À>TPÝMuUtÁÛNÙÝל$Ýã5b¦Ø£A#^+ZvíU{¨£ZÌP>ùä×{ѬY3ïÿø‡§Dâé,9÷~i#ÑÅG—œ—‡÷þòË/wað¢ð.¢@Ï D'9¸°›nºÉuåkÔyUƒ§tàz›6mòtj¸ëæC÷%8À>T±®åþªè)kª ;r]òšÒ¤ßͤ/ÀCT`Ä %Œ RAå‚Jæ@ÕRé ?Z½gŒ:¨éâ묚h\uÜGD]˜Ÿ”t Ô…!ܯˆKJsçÎuq´%çÝpà  ]}”Ü!)³Cu»—ꮪèÂÃqTŠ)õAknR²c^·7Å=)¡y¬j\cÑÙp^¯^½Ü;…8:=Ü5,-ò AƒÜòGáél=o§vrï"ƆA4¸ Ò… z{ì±Gô]îØ±£‡÷b¤„}ŒCãí%Á!%  LTAJ½Tá˜4SÍ)¡e‹n#ÕÖª{ª¢Z .A÷îÝ=uãa×Ñ.97A»éœÁÁúñǽ{ï½×Ç#%;ž¡ÿ]ðfÍšUý¯D1RÂÃöÜsO†Ž±¤¾}ûFŸÏÜ €²Sý‹ê¾ªh‘cƒ1I)«ø)a,À<%úÖK5ízó~ýõ׸qЂ÷¢]éQ²‰Œ1gHŠ’_À>TSí­Z.¤„>ÀtÅúѧ·S=Ñ—|ß}÷ɹçž+êÑÈyç'«V­…ç¶Hþ(‡ÿ7`¢Åã¶þít‡øG9Æž0Õç0~¤3€\ØgŸ}æþ/䯡Þÿ}ÑI.œ?9GÀß ‘ ð¡<.Œù¨7õX~cÁ¼›Í›7cÎÃßD(y‹þüû±JÛ¶Ég²ªOÑÂSB· ,³.ÐòE 8.Õùå—ÞÀÝ,Ä;¯íN;øàƒ]†0müt©ƒ8·Þz«»—¿û'ôÿnŠ(âÀÛ²iåJRžN¤p}Ö:©Á]ûÃ?¸{aÖº)¹Ee¦z¼joUkY5Ð}ôAÓSJá}ÔMúö_£¸ã}F÷)<%x®T­ûÎ_Ù¸wIø%f°¼Ï½Tñ>Û˜ì)¥Ùwn ®^œ”hËFŒ1%Ì¢Âdº`ªN‚Á—$ðh”(¤U«VîsþxÚ 6õŸŽ»jöìÙÒ¢E‹bñááPíBp÷ìÙ³§¼üòËqïÅ“ÙC`«éÈ úÄ?T—¨bêÕ*Õ5ªÔv`ä”,"à{Ÿ15®ÿ}žXÚûœÅdòQD ò>£ j‘*Þg|êŠ÷y“ÚNÒ ³níïÒ«ËA@TÐRE=Ñÿ4#$\„©Ü‰â¢;+CÄ‹.œG—Ž'¹å‰°6%0Àî`+%·^“Ô‚IˆÕöcËBÁZ…­Ø;í·™”Þï¸c@I$5#Æe)%"‰ç%ýÝXÖÿgÒîÁ¤®eäŒ";ñÛN ì&£9à͵5kÕÀ»ì/ ÿ»À”3IBdÛ1rJ9ié’R¬#Q0ð@³ý±/e”xay"à¯ôì¾²KTmcËeÂr)0#H1»f;°ÿ~J·K‡”ü7ãu†Ì>è”Ê¢ .ò5Xü6ƒ¼›=AÊd¤LPhíZ¹XË7HIeZ‚‰€ßfB¼Ë)¿ÏÖœJV-!©\Ëk 3XÚPpì ¶LX6Á)› §ÄlÇø¶“²ýØM’Ê4ú õKˆ]›r"ìÜ~Ã¥íä¾èQ¨üÕ,—Ü—K>¥ÀÏ ~;J*)‘Rœ'Àx‘ qpxªf'ØÚ>lÑoÔÅ.≌#àŸ¾Ë²È8Ü¡y€ñÞe³ÿ»TFífI]ä°ëŒ,Avž["³lmß?žïžË<Ö0ðW.™*Ÿð.›í`›’¤DJ‘'Å>Ô*—”‹ ?!Ñv‚QüöN[׋#uLEÐð¿Ó)§5RòW$þý”à ÚOî‹>–€ÌkÊ}ʘ‚|A6dœkO çÁnð¾ˆöP…âß÷Eå.(‚lÇo+86{*‘YAÀ=Ë"+‡ê!~ûAÆÒ²¡tH GÅâOá"P°ÓÒâ1ŒA ]R ù[¼³™&Ö’2Ûac&M@y9 ©’’U*~¬‚ñŸã>ˆ‡Òa/$£xèð(`R%%ƒÌON¬` nËBÀì&Þ¶¬kNˆ@ˆH—”B ³Fˆ ÙF€¤”mÄù<"@ˆ(’R‰Ð0€"@²I)ÛˆóyD€"P"$¥¡a D€d’R¶çóˆ D DHJ%BÃ"@ˆÈ6$¥l#Îç"@ˆ@‰”J„†D  ˆ]‘%ö¸ À`f“B ö㰔퇤”öŒLB‰€U V™Ø1Wi eq—{¦leÿ͆üçÚ')%#‚@I¼ ¦ 2ÏL¦…€­giddÛ¤oZ)é+x aE Ö3BÅâm£Ö 3_i#àoÈ”‹¤JJÎXa°EΰO!‰ `vo›ÈõŒSþ”ôþnãy%•"xÇüB Ò`—d’vï[Ú7°”è–n¿ î–ŠìöÂÚ®T˜²huµv±õW6YM –WÀVìöÛLJï·bªà¡fĸGJ‰Hõá¼.o€øm‡v“âTOÈ_þ²ð¿ÛÑ~ðÁÒ¶mÛèqI;Mš4‘/¿üRpû#FÈÆKŠÊóù€)9¥œ£”\óˆËVYŸZ]µ¦j]ÕªÛªNRkÌzžBPé T]¨ºDu™ê*Õ5ª´ó·´ô%è;]EŸƒwÙÞg¼ËЉ[9K÷"²~ýzùã?¤SšüüóÏÒ¨Q#©X±¢TªTIV®\)µjÕ*í†å‘áÆã4Ù‹T—ª¦ý>§ã)Y‹ d$äöaÄTbÏblE£¶cö„s”,"idâÝEƒÀÞekùKɬY³äòË/wçïºë.9r¤üñÒ¸qc0`€¬[·Î… 2DæÍ›'Ç{¬;Þo¿ýdÍ´=(!CÀo3ÈÞå”ßçtHÉ’¾æÊ—ù’K.‘k®¹ÆEyå•Wä›o¾)-:Ãò3ؤl(³›)-“ËϧŸ~ê2Ò6l˜ 8Pžyæùè£düøñ.ìã?v$tï½÷ºã'žxBªWGç %d˜íŸÀvJ´Ÿ²òžÒì;mcš¨%ÄžQj"Þÿ}çÊ[äx[RÄ”G}TŽ<òHiß¾}¼¨<—ÿø ·TÛÉÿ¬æEP¨Tüݧ •Ë 'œ ýû÷w™<æ˜cä»ï¾+’áfÍš¹ãwÞ9ú~‰Àƒ° àç¿%•¿”H)Î`¼HP‰F WþÎ;ï”Õ«WËÝwßíJzè!©_¿¾Œ=Zºté"cÇŽ•5jÈÚµkå­·Þr­±m·ÝVŽ>úè8ä©hÿ5FH– ˜Û7W¤òùí·ßäµ×^“æÍ›ËÕW_íâüøãòÓO?ÉÉ'Ÿ,Ý»w—óÎ;OúôéSì^<‘÷˜`kûhYAå}ó4Ö0@9X¹”™• RªFʼ/#ä°³”ßåt¬)ö¡ qݺu¤mÚ´‘ÿû¿ÿ“ï¿ÿ¾ú 4´¸¶ß~{©]»v‘0„؋ٌmC“¹<͈½ÓÖõbÇie]ò .NrH Æ|¸ØÿN§œÞtHÉ_‘ø÷ËLÌ;ìউ""¦ˆ¢»ŽRÐ$e?Tæ2K@æ5¥ýDÒ!‡"]»vuÝ÷iß7*°!ã”X{J8ÍéŒ)ÙCýŠ¿ÄDÐÝ/šB €íømÇfO…‚AòéǾ̲ØgŸ}¢“0>ì—áÇGùå—è>fÓ.[¶Ìõ€DOr',øíy*Ó†J˸±ZiqJ CÅâO¿´ëÊ ÃîèA• S¾G€ý˜æ{^˜þ2¨W¯^1LþtµRÅ$äoñ¦zŸb×õíÛ×ýgi„ ÅÂx"ï°–”ÙN¹5fòf€8©.3 Ë USµeIêë~CÕIø'yȪU«ÜŸíà5QÂ@äh'hnþPå2C(V-¼ÏXf(vÙ°íô\±e†d&! DÞçšœßUñ>¯P]©Še=Ö+øÿf §Ê–t»ï¬Å‹'•{k“ HHebÆ0»‰·ÍÃì0Éé °a÷,Y:÷àµáA ]R Ì  9A+=Ä®‘“„ð¡@€¤ˆb`"ˆ Dä%)mÞ¼Y "@²À{ï½'_|±üýï—¦M›ºUWæÎërÛm·É#<MÔ­·Þ*XˆòÎ;ïÈa‡& 6¬—‡)â&o¼ñ†ì¹çžÒ¢E ù׿þe§¹-@ò’”° ÄÍ7ß\€ÅÅ,Ü#°dÉ÷Ñ>LñAae[* ßPš?~4‘8^°`;¾ðÂ Ý .XI|Ë–-òÀDãa…q¬&ŽE™‰BSp;éüy¶àÀb†‰ØŠ>Þ"Âì+,´Ü²eKG4%ჯÎ.\¸Ð-)¶ÿþû»E˜W¬ÀD­­rÝu×ɾûîë÷9s¦[¤Ù¹-²æ)õîÝÛ-ÄŠ¥Fв²ÿ}òÉ'îÃ`9n=å”SÜá¢E‹äÜsÏ•wÜQvß}w™>}ºEs_¾ÄÇÃvÒI' ¾†I!D ;´jÕ*ú ¬S‰UZâMV°nöÊ•+ËÃ?ì¾€µ-/ºè¢"ŸGG7  îGOÉÐ(¼mÖHéƒ>Ç\žþy×õvÖYg ¦‚â¿HþùáØŒûÁtŸ²˜1c†œyæ™rÎ9çDKýÔÇwœ»ˆÌ>,À"@2†>ôgè÷ßwßKƒç„®9tßAð®ÃÂWk§M›æH窫®²hŽÔ¢Ü)h²FJ@ù†n´°ðéäž={ºÏÒÐÿõ×_ÅPÏ?ÿ|÷eK‹{à=zôC=4JdÎ- ™Cà믿(d̘1Ò®];„ú}øá‡î“èxwñqOºêvÛm7Y¾|¹tîÜÙõŽ`lŠBbÈ*)µnÝ:ú|̲A‹)V6mÚ=…¾e| ]tèþÃ÷–Lðyu~XÌà–d¼¿˜A×¶m[7éÁfÜá ´_~ù¥ Ýëö=´í¶ÛNŽ8âÁqcH—_~yv˧äYè€Ö¾‘ážxâ‰n?ÖÝ·eаš>ˆ±¥çž{NN;í4÷@\Ä•tü!9A qãÆ2uêTדI‘åfd—]v‘yóæ¹wþ/Ò"‘˜ê}ÅW¸®;xV&þÕÄqnöìÙÄm"UOiܸqb|ÔïÿûŸ` ||y_›…«rzöÙg£Åpúé§ –¼‘ýõ¯•J•*¹nhî"30¹«1!YBp>–, ”Ÿì<·DÀȪ§ôî»ïJ§N\KjèС®o ;C…ÁöêÕK/^ìÒ7dÈ7þÔ¥KÁÀ*&: €Bˆ@îØu×] F Èé®n« ×ÑÄ5PcÄ]%¼jÕª‚‰ ˜…ƒÏ¡cÈ/˜½ƒRLõËÒ¥KÕ¾}{Á=(ù@¤UU…KZ%œKud¹ˆµLÐcÂU³Œ{ç{ŸËm•ð¬zJ(„’Üz|"=žÔ¯__ "@ˆ?YSÂLº5j„Qæ"@RF kžÖ´¢"@ˆ( ¬yJ¥%‚aD€ä¥­Ž´èA7=¦><ú?ü×pìØ±»ÔXþäœ)Í$¥l Ìg!PÚ*á#GŽ”)S¦¸u*ß|óMyê©§¢Ÿ®(i©±Aì”$¥r‘· …†€­Žÿb•ð×_ÝýÏðÑG•Áƒ»ï,aêxìò`É.5Vh¸2¿YüÈ_i®; ºcÇŽnª8–)ÁôpÈ7Þ(hy~øá®;ó/CÄ$D 7”´J8þ¿×^{E…?×þñfÿo•D–³¸Ü&Yó”Jrݱ$ɀܒösæÌq_¥Äê |Seذani|™ä…(D€ä’V ïСƒø— Âê-X¹ÅÄÿ %,5‹Büd”ðÐx®;V†ÑöíÛ×­Ö€8¯¾úª[U×àŸãXåÆŽU ^~ùeœ¦"CJZ%üÈ#t«†¯[·ÎMp˜â„È©§ž*|°ûDM­ZµÜöC‰¦²¤¥Æ¢¸Sðd•”â­Ž%‡¾úê«hA`)"ú¢!þn¸û%­ü½wˆÈ8%­ŽOÊà=ÅÇüêÔ©SlôŒ”´ÔXÆÍäYí¾‹çºÃÝÇxV ‡¼øâ‹rÐAEWž8q¢›Ü€/Òâêø8 …Ü#PÒ*áø¬ ¾³TÒ’b8»öeîsìzJ%¹î ¡6mÚH·nÝÜwX&MšÅ][»v­`QV|…Bˆ@îHu•p.5–»2˧'f•ðùóç˲eËÙüN9å÷¹s|ÜŸQ.©å•O€zZ}« s•ð€W HAäa2|ïsøV Ç'Ï¡ñ®>ÝýxÈð D \d­û.×½_¿~Ò¬Y³p!ÎÜ"@ˆ@‰d”RY%¤D!D€ÂA «³ï Væ”"@RA€¤” j¼†"@2‚I)#°ò¦D€" $¥TPã5D€"HJ•7%D€T )¥‚¯!D€Œ @Rʬ¼)È;¼˜ÇÇóDØÝݘúN'¾KRJ+Æ$aEÀÈ*ÿ6¬yf¾Êxöb6•ôSHJICÆ ˆ@h°Šd‹æêo‡6ÓÌXÚ˜­˜Ýà†fKIßæ«!!Nʆ¬×RÒD BJ¸ ÞU¶FPØBñc ±s[Â÷»¯f ¤Ô]vO†ëIÔy—Æ É9¼Ë¼Ï&Ø§Ž¸R%$Ü<]R‚ÑBA]cÖ{PÊ rÂ]˜ì ±Çv>,Ûó4#ýUû&¡AçTÕÞ ÄÍ÷(xŸý=.¯÷7eRBªÔphŒ˜@@FDFFØÂ€Mu—RÀXMýn¾061Zu¹ô¹€ ³˜h½Ö@“ùjµKŒ–*‘zðct»Æ¶ÔÈ ,´H w!&T2FP¶od„-¥°0R²­Ÿœla—êËüÏ‚†¹ *Z§½®iøç»qÒ¸¿ž{'Îyü/ó2ÕmÕ–q-%EÒ&%jŸ0_Ök,Œr'¥ ¦•·.ô¥ßI³ù‰êÁúòÊ.P²áÈ"I)ó刮6  JD¿ib.T}S+€“0&„¬ @O)+0ó!É" „t¨^ó%)ü‰B=¥ÌI)óó )" À®JJ<¦@ @RÊ|1°û.óó )"à'$­ Z§x^Fˆ@!@RÊ£Â*ð¤öVb:«À1`ö‰@èÀê "ŒÓD~¡ÄT_=¨;ò!ÁL# É#À1¥ä1ã‰#€W±ðj¹ÈüQë™gž9é€xµS§NóÊ妹½É£úør›>=8¦” Z©Å%)¥†¯J ¬¶kbQ 2Öšë7 2çyši’Ræ ŽcJ™Ç˜O D€ )%£"@ˆ@æàD‡ÌcÌ'¼G@»­l½7Ûæ}žRÍ€BÁÆ|ëX¦Š/I)Uäx1Bå 2"ŠÝ†R³†¯l²ØJÿøBDt_Ù¢3c‹¬úŸ H$¥TPã5D ¤DÈæ„`k¤„}¿‡`„…x…&5 -Ú_|ÿ b¤ƒcìã«Ò؇V0’J‡œHJŠ$…eÅÁ uöWªš‡¤»E` Qˆ"bª£Š/Jƒ˜@R#¯­GIü’”’‹Q‰@È0ïd„.*ÔüÛˆ¯ÐýõWiܸ±ïLaïF2U`'ª &·ÅùT½%¿;^Øè2÷y‹@÷îݳ’öÝwß=îsxà=ztܰ|9©uˆˆ„­VVúçÏŸ/‹-*+Z±ð5kÖÈúõë‹wbݺu2kV0¾ùHBŠWBÎN@L°Ø‘ÙRÜÈe$)•…É@·ß~;.gŸ}¶œzê©qÃòè$Z¸V¡€˜ªªV/)ý?þ¸ 1€Jz‡vž={Êk¯½VRôèùÍ›7Ë^{í%5kÖ”ýë_Ñóñvf̘!ÇsŒl»í¶Ò¦MÙyçåúë¯F}á…äÖ[o§²s饗JµjÕdéÒ¥©\Îk¶"€16Ø ìÆÆá,Á¦’Ü„B²†ÀC=$Ÿ|ò‰ …½ÓN;ÉwÜá*T.ÿýï]÷è£ÊäÉ“eÚ´iòÍ7ßÈÔ©S£é[±b…\yå•‚ žË=÷Ü C%¯¥J•*ò·¿ýMŽ8â¹óÎ;åý÷ß—úõëË 7Ü ›6m’¡C‡ÊÚµkå裖¿þõ¯ÑëÇŽ+o¾ù¦Ì™3G6lè-t΀äŒ3Îûî»ON?ýt—¾§Ÿ~Zží·ß^ªV­*hý#Í?þø£4jÔHîºë.iРAôþy°ƒ źîJ$¥™3gÊ AƒþãÇ—•+WÊùçŸ/ÇwœüòË/R¯^½³úùçŸËG}äp?öØcKŒ‡àýý÷ß;'le‚:餓äÙgŸuŠòLU`‰zl©>£®CãcIþI6¶”töé)% /HtõÔ©SG^zé%W©HÐÂF¥BèÒ¥‹<òÈ#®²ÿé§Ÿ\åïÞĉ¥Y³fòî»ïºÊoÁ‚Ñà¹sçÊ”)Sä±Çsd‡Vù˜1cD×Ë“!C†ÂQ‰öéÓG&Mš$èòËòåËÝ9xD¨h÷Ýw_yå•Wä–[nqÑ@6 Òƒ:ÈÝ-}Ö’%KdÙ²e‚ëÑ8D~Z´háÒâ¿P÷#-Z« ÐPEWL‰¤„†Â–-[¤_¿~Ò¿Gè¾<묳È'ʰk×®‡OýôS™{î¹–ÿýïòÁÈÆeذaÒ²eKi×® >Ü…cfñ×_-mÛ¶u× ï좋.r˜Q£F‰y®‡v˜lذÁ5T<ð@iÒ¤‰ôíÛWÇäã?–Þ½{»ô Qñá‡ZPt û@Ùž|òÉ$ª(* 혧»A£/‰ž’ƒ‚?G•=d=öp]8•*UrÞÅ_|!«W¯vçѽO]+¨´@"h£B¼öÚkÝõþ®œ€W‚вV­ZÎk©X±¢ Ý5TàUhÌ3F£xÜ .4Ð… O v‚H¶†îEì ’qãÆ¹FâšÀîÖÙ³gË;ï¼ãIÆm™ÀNÐÂÛ ŠÆ 6˜ô’Xë(¥‹yH¼øÏ>ûÌUN¨äN;í4AwºgŒ*TØjž¨¸@FW_}µ«€Ð½A×ßÂ… ÝþªU«\7<)*)t­a<• *µ{ï½×µÑâ~ã7äÉ'Ÿ,FðØLpx2bÄç!`ÛõƒŠ/O`›¢Rþ †/SÛ byùå—]×Ý.»ìâÊÑÐ%v÷Ýw ¼`4 н‰2Qó„œp Ž(@X­[·”Û‹/¾èÄ<<ÞÍ—_~éìãVð¤øá9ꨣiá^x]±Ï?ÿ¼€ØÐ €ÀVž{î9g èÊE8º‡ac~R¡Q„.Ünݺ¹kù“0°“ʪ0~ØMüGÜ€B²Š¼Œ½`z-*5Ï™gžéº½Py¡%ûí·ßÆMÈë‚ .§žzʵ”QáAà¡å±$Œ¡büùçŸÝ½^}õUY¼x±\xá…îøàƒŽ¨Î;ï<ç!aì tƒÂ›Œ÷GtÝýöÛo. ž± ºB! «·ÞzË5<àù€¤ÐŠÆ€‘]S¹re¢{ã€ðt!x.ºå hü@Ð=õ òA7 %i`'f3°¿&}3Wh(8*1È€ |§÷,"Ç÷´+ÎÓo#9ƒyóæyJ&ÅÎÇ;¡•]¼ÓÞï¿ÿîÎ+1yZé¹}œ³}œÀ¾Åsr÷ÓGów/R‰ µ[WÂi« Wã(Õbè¨çáéÌ;OÇ΢a(SÄUoÖ»ñÆÝþí·ßî©·êõèÑÃSÔÅÅ5ˆ§Þ;Ö.8¯C‡žIÕ±G÷ŒË.»,ú ô´ñáéDwNIÑÝ Úˆqû‡z¨§cžzÅîX½/OAn_'̸ët|ÉÓñ@gÚPqa:yÆSbôZ¶léá9”Ä@Yª£ÚCµƒjSU´`Oõ.IÛ7=%EŽ’=03 -çí¶Û®ØC“ñ8àÅóœ0ebçìã!±ç,¬À·[ûKÿìÆ³ã"°´jÕJ0qãFðŒàÑÀc„ Û c2%#çQbö#¼˜x‚î9tÛal cD˜Å‡ñt£aš6f;b Æœ0Ž„nZëš³î]xSfJpn¼q*E%*׫$é&V`¶¼hÿÌK%?7æ¤&×Í{ùå—ÇK.ÏÅGvÏÚßmçßUIgSa2^“<û(fÅ<%Åò'Aó”ÐÂ…§Ô^µ§*ælÿ™Zßžv¥y½zõò”à§¡Óó=íRs1tüÏÓYŠî<¼%·¯ã;λÂ=ÍSRrðàñèä§cÇŽžN”p÷ѱ$¦]q. ÷Ôî9^D§„GŸ£Óñ½ƒ>ØÅÓF§3ìܾN#wquâ„;Ƴ®cKî¼yJ:ƒÒSòót Ò«[·®§c•.œ?¥#dP·&XjãWë"Š®ºXÁµ¾ªG©öÃ…$ˆ€¢û÷¼|k4zJIÁÅÈI"p—Æß6ÉkB]ÿZM§¿ƒœèŸDëè4{ꤪê4«é °ú‡á­KSlÒZ7£UÑê„~¡z–ª &©º&¢QŠD@>Ɔ0ÈŒ>} :GE ê#=ˆÎ£WûŸƒèLð¯Q Jÿªúœê»ªñˆKOSˆ@@@FBhPÙq‘H‰Ð5O)Æ#!B@Ii†f§ƒêjU|Øè1Õqª TmöÿVBÓ …G@í'1%]½Ð媰)4Œ0û ¬¤„žRRp12 /kN¾S}@õUŒ-a¬ „äjÝRˆ@"˜—dRÒÓÀý!)ùÑà>(´{¥eU[»~‚[d®‘ÿ¼Eç–Ä"{1[Á~EßqlÜ2ÑÿG!D€ÔFJFLD†”…Éìûè²KÙ~HJeÁÍp"P8XebäT89gNÓAÀ¼$Ü]wvlÛ¤îMRJ .F&¡G‰µrSªTB3‹€2ºîÒ’RZðñb"*ŒŒ”lªL23厀 ɼí”4$¥r/Þ"PP€ˆüÞRZ™çì»´àãÅD TÀ3²†ªm¹ö]¨Š¸Ü3›Wdž‘í§ìe“”ʽŒxC"—XåbÝ/È„;Ç?Ïæeyf%Ñ‘¿ø½$ì&eR²ÖPV2À‡"X¬¥‹ñÔ FRM08Œ0ÿ~R %)%#ð! ž¿U‹V/*””+•ð!Ä•€ß^À)v\ÆeñƒIJñqáY"Phø=#?IÌoòøíÅ5þsIÝ‘¤”\ŒL‡@d\­[T(ÖÒ _F™£L!`^µyHi­}GRÊT1ñ¾D ?@ ×Z¹¨d(D ,`/f+Ø÷ÿo©¬k‹…“”ŠAÂD `@}`¤dÄT°`0ã #`ž’‘<î”퇤”0îŒHB€U&FN¡Ï03X.˜—„›qí»r”7!DÀ°V/Žý•…sKb@cÆ„kßÜ"6FHÖõ’Ö€uÚ©á ò³¤×¼í”4ì¾Ë—bg:‰@öAý-àì=OÊ76i‚Í^¬A“r¸ÌPÊÐñB":P¡X¥‚-*®}ºb.× ™ {11;²ã„·$¥„¡bD"jP‰Xƒ–/tƒêIªõUëªÖQ­¡Z]uGÚãzJð0Û€M¬U]£ºJu¹êRUØ ÂŒ˜¬ OO%/$¥ä1ãD ¬Xåƒïƈ®Ó-*¢*ªèîG*¡Ê‘ãB@½y¦êU`v]€hPîP’)ò¿^öb‚} .×%-$¥¤!ãD \`í;]ÕÁ*«XPù Â©ª ÂÀ5Âp…º„”ò€¶^›¯ÒL޼ï®úb¾f"‰t[cÅßPY­×CAN~R²¸IܾhT’RQàI[÷.¶8â‚">4i!)% / ¡E• *󀰵ʕê K²º£Ð<%xŠ;ªB€AÕU©†YÐ0-˜€€`æQc 5BÒÝÔÄ +µ«y aA•Zÿ¨Tü‚ck£¾€÷„x~âÒ‘ý4§¨ŒkEr ¨Þ9ëÆ<Ø .PضFH؇½@1TIOI "¨@t²*¿X„ÊDä'$¢t×LcZ¼ Æ×ŽP½]u¡ ÙÖÈ[Ø„©‘¶P³ÝM­ën“"™áZ  !BÀ÷]%Ä<#›Zw]ìÖ]P?Ÿi›Æä¤ý¼ê…1çÃthžòòÀ#¾óŒ"[Ý8/Éâà8)a÷]Rp12/o ´ ­_ˆ‘¶Ömç ì !­TňQQÃ;úCõ-U ü‚ !¯æ=Ù>ÌçRzJ)CÇ ‰@¸ðyNȨyF–éØc;ömÍ H{‰êϪ6¶¤»¡•’HÆ>m22ôHJ†·D€$PÒ^¬ÑwÕÚ8ì3ï’@%ý¨æ–§'Þ"PXÌÓì¢;RŽ”ÊLÞŠ‚BàZÍmØWsÈz²û.ëóD€"Pœ}W2”]9òÈ#eĈÙ}hž>Ë åiÁ1ÙD xOó\äϳ6l]­[*W®œûï¿¿L™2EªWÇç²':u’/¿ü2{é©›–Í–׳¸¢Cy!Éû"u&L˜ U«V•5kÖÈ´iÓäÇ”FÉ]wÝ%+W®”¡C‡ÊÚµkå裖¦M›Ê·ß~+×]wÜrË-2pà@Y¿~½Ô©SGž|òIyúé§‹Ý£R¥Jrå•WÊŒ3d÷Ýw—{î¹G¦OŸ.÷ÝwŸÌœ9S®½öZ9ꨣŠäa/½ô’Ô­[Wn»í6™:uªÌŸ?_F%ƒ *—Å`÷]qLx†û,©ÄÏœ9S ä®?~¼Œ5ÊÝã¸ãŽ“eË–¹óW]u•<ýôÓIÝ762tÆ ±§yœ$¥@b"@²‡ÀĉåÐC•¦M›:â™3gŽ{øìÙ³å/ù‹Œ9Rîº ~yá…Ü6öç†n|0ö´|óÍ7²eËéׯŸôïß_Î8ã =z´œuÖY²dÉwŒðwß}W®»î:wý#<"]»vx?'žx¢|úé§ÑûŽ1B:uê$;w–¡C‡ÊâÅ‹£a¶3fÌG¤÷ß¿â¶4ð…\*1  Ð2aZ÷ á‹Õê«6QÅ'Ä÷S=VUYT~ÿýw¯J•*žvÏyW_}µW·n]邏N*iúôéžz6^… ¼>ø H˜àݺu³ÃèV ΫT©’W¹reïˆ#ŽðÔóò~üñÇhø7ÞèéÒH^ëÖ­=%#ïûï¿Çºpž’’§]‡^5¼Ž;ºøêM¹0%%¯wïÞnÿ”SNqa5ò<ð@—¾ªU«zÚ]è­Y³&úœlî ýª'¨öRí¤Ú\µ*Ö¬¨i *1A‡é –±²<ò¯<´ÒKŠ”ÔKr•ûñÇï©·âM›6Í›ä믿öî¾ûnï§Ÿ~r…ëªU«æÂ´Ñ‘›zXîxðàÁÞÙgŸíöAJ «wÜÑkÒ¤‰·`Áw>?‘|’”|1  ¸ $KJ«V­òöØc(ahž§Ý_Eêr5çÝtÓM.ÎW\Q$,™ƒ¯¾úÊ»óÎ;W„Šñ“’Ž]y§Ÿ~ºW¯^=w¾víÚ<ªeË–n?ö¤d¤W¿~}O»õb£dí8ßH‰cJZb"@‚&`Lé?ÿù\|ñÅnBÃ9çœ#Ë—/LFÀX‘Vø¢d$걈IR 7nœìµ×^2eÊéСƒ\zé¥rÇw¸{¼úê«Å%O<ñ„ 6L–.]êÆŽ,fÚäœÃÄ ÿŒ@íz”Ë.»Ì]§Ý‚v·e @R* "=@ Íš5“?üP.¸àѱÑñÑ.6GL˜àððÃ;"Y·n{,†¦ŠË>ûìã&IĆ´jÕJ>þøcÑÝrË-òüóÏ fÛA´[Ðmñ,í”Y³f‰znî\=Dǯäý÷ßwÞ?NbÊ7ˆjøðán¶žŽ}ÉÔ©S]|ü¨ÇçžÑ¾}{793ÿ(  >$»Xˆm€6Ð* ß„':èTjO‰Æ«Y³¦ëþÒÿy÷Þ{¯ëêúüóÏÝøŒÞÏ…aò€þù5n7XIݽzõrp¯ÝwßÝÙtʸ›Ð€‰:[ÏÛi§ÜóÚ´iã&/àL˜X¸pa‘®FL€˜;w®»MtÀþçÉ]ôÑGÛ#²ºà•7cJÛ(:šf  D üЙl %|mõÃ×Oëªbæ×vªKªÐ7oÞ<Ùe—]4ZQÑÙr¢3èÜ”ñ¢!É¡KÿwRÒ)váÆÿ5ªU«–›BŽ´4oÞ¼X<¤^ÕæÍ›eçw.„ZHÆÕßU—¨®P]©ºNu½æa³n#$¥ÀB‡@ª¤>$r—£|#%´b(D€"@I)ÅÀD"@ˆ )ш D 0”SL D€”hD€"HJ) &„"@HJ´"@ˆ $¥ÀBˆ $%Ú D€’R`Š‚ !¡DÀ­ûÉ™ísm³ìõß#­üç|Á¹Ý%)å>"¨Y!†¸0€·‘Q $¥[!³F„€U„¨M”¼Ð'%o•B_Ì  A@Àˆ•㦈zºX¨[Â: q€ùU”°Ç±©îKHJÁ*¦†„T€«±Ý¨z¢j½ˆÖÒm U|{©²jEUöä(I¸®W]«jŸ¨Àç*bÂg* FJV6z*BR F90D Ì â3O [TŽø–*Ϊª !ˆ‘ê%%9 ?ð]£º:²c ñGFš&'$%C‚["@Êý€ºèp_T„¨Q1‚”¬5ouZò8‡‚¤p»ö„Å<ó–@øFJ &` ì˜P3ˆ@&މ"D X…é'%Tš¨†Š-yxM %œ'))Ip4â7‚÷{K (iYü$‘¨$¥ìà̧BF É*D´Ú1^d„„cê#S’’‚‘¤cp6b¶h@AJ6áq?4PBR Tq01D ”Xågžç¬eÊÆ‘P'‘„ij5@B6†g]xöè]%)¥4/!D ÐzoKdê7È"Ä c "?!Á‹B\JòWÃÖÈLjɈÊâ$÷,\AO) óD€¸Š0€l@D~O çÌC"!))Šy= ì[w¶ $(ÎÛÖâë©àÈ6ôÞ‚ƒSBˆ@¹!ñ–ŒtàŒlkûxö)©!¢1õ“Ι‡äƒØu‡,“”€…¬ àëÆñøϷ㬤%„Ù˜ø÷Œ.ò‡YüÀlIJ) &„’ògœ^’Ôö‹NÐI(6‹$¥XDxLˆ 9Cý»"@ˆ @€¤ˆb`"ˆ D”hD€"HJ) &„"@HJ´"@ˆ $¥ÀBˆ $%Ú D€’R`Š‚ !D€’m€"@ƒI)0EÁ„"@ˆI‰6@ˆ Aàÿߊ ÿúìGIEND®B`‚doc/next-tutorial/per-object-mixin.graffle000066400000000000000000000607411242365656200211710ustar00rootroot00000000000000 ActiveLayerIndex 0 ApplicationVersion com.omnigroup.OmniGrafflePro 139.7.0.167456 AutoAdjust BackgroundGraphic Bounds {{0, 0}, {559, 783}} Class SolidGraphic ID 2 Style shadow Draws NO stroke Draws NO BaseZoom 0 CanvasOrigin {0, 0} ColumnAlign 1 ColumnSpacing 36 CreationDate 2009-12-29 15:06:59 +0000 Creator Gustaf A. Neumann DisplayScale 1.000 cm = 1.000 cm GraphDocumentVersion 8 GraphicsList Bounds {{336.66165741469348, 303.02532815884581}, {75, 24}} Class ShapedGraphic ID 11 Line ID 10 Position 0.53307443857192993 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 9 ID 10 Points {352.90962682497189, 277.51900000000001} {381, 329} {375.43063138316342, 338.26348389126144} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 17 Bounds {{330.173, 338.69200000000001}, {90, 36}} Class ShapedGraphic ID 9 Magnets {0, 1} {0, -1} {1, 0} {-1, 0} Shape Rectangle Style Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 s1 : Stack} VerticalPad 0 Bounds {{77.418645028395531, 310.12415423604705}, {75, 24}} Class ShapedGraphic ID 12 Line ID 8 Position 0.61400783061981201 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 per-object mixin} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 6 ID 8 Points {173, 356.69200000000001} {118, 329} {118.177374052806, 277.51900000000001} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 4 AllowLabelDrop Class LineGraphic Head ID 1 ID 7 Points {178.25987156357274, 179.51900000000001} {229.0263150257764, 138} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 6 Class TableGroup Graphics Bounds {{50.34620000000001, 179.51900000000001}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 13 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 Safety} VerticalPad 0 TextPlacement 0 Bounds {{50.34620000000001, 193.51900000000001}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 14 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 \ } VerticalPad 0 TextPlacement 0 Bounds {{50.34620000000001, 221.51900000000001}, {136, 56}} Class ShapedGraphic FitText Vertical Flow Resize ID 15 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 init\ push\ pop\ } VerticalPad 0 TextPlacement 0 GridH 13 14 15 GroupConnect YES ID 6 Bounds {{274.86819601096846, 313.266292721502}, {75, 24}} Class ShapedGraphic ID 16 Line ID 5 Position 0.42326045036315918 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 4 ID 5 Points {317.81022166395883, 277.51900000000001} {308, 335} {263, 356.69200000000001} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 17 Bounds {{173, 338.69200000000001}, {90, 36}} Class ShapedGraphic ID 4 Magnets {0, 1} {0, -1} {1, 0} {-1, 0} Shape Rectangle Style Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 s2 : Stack} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 1 ID 3 Points {313.12218652157128, 179.51900000000001} {302.06388601522673, 138} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 17 Class TableGroup Graphics Bounds {{258.173, 179.51900000000001}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 18 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 Stack} VerticalPad 0 TextPlacement 0 Bounds {{258.173, 193.51900000000001}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 19 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 \ } VerticalPad 0 TextPlacement 0 Bounds {{258.173, 221.51900000000001}, {136, 56}} Class ShapedGraphic FitText Vertical Flow Resize ID 20 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 init\ push\ pop\ } VerticalPad 0 TextPlacement 0 GridH 18 19 20 GroupConnect YES ID 17 Class TableGroup Graphics Bounds {{221, 40}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 21 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::Object} VerticalPad 0 TextPlacement 0 Bounds {{221, 54}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 22 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 \ } VerticalPad 0 TextPlacement 0 Bounds {{221, 82}, {136, 56}} Class ShapedGraphic FitText Vertical Flow Resize ID 23 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 info\ destroy\ ...\ } VerticalPad 0 TextPlacement 0 GridH 21 22 23 GroupConnect YES ID 1 GridInfo GridSpacing 14.17322826385498 MajorGridSpacing 10 GuidesLocked NO GuidesVisible YES HPages 1 ImageCounter 1 KeepToScale Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo Animate NO circoMinDist 18 circoSeparation 0.0 layoutEngine dot neatoSeparation 0.0 twopiSeparation 0.0 LinksVisible NO MagnetsVisible NO MasterSheets ModificationDate 2013-05-04 13:24:25 +0000 Modifier Gustaf A. Neumann NotesVisible NO Orientation 2 OriginVisible NO PageBreaks YES PrintInfo NSBottomMargin float 18 NSHorizonalPagination int 0 NSJobFeatures coded BAtzdHJlYW10eXBlZIHoA4QBQISEhBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCEhAhOU09iamVjdACFhAFpAIY= NSLeftMargin float 18 NSPagesPerSheet int 1 NSPaperSize size {595, 842} NSPrintAllPages int 0 NSPrintReverseOrientation int 0 NSReversePageOrder int 0 NSRightMargin float 18 NSTopMargin float 41 PrintOnePage ReadOnly NO RowAlign 1 RowSpacing 36 SheetTitle Canvas 1 SmartAlignmentGuidesActive YES SmartDistanceGuidesActive YES UniqueID 1 UseEntirePage VPages 1 WindowInfo CurrentSheet 0 ExpandedCanvases Frame {{823, 399}, {728, 764}} ListView OutlineWidth 142 RightSidebar Sidebar SidebarWidth 138 VisibleRegion {{-15, 1}, {590, 670}} Zoom 1 ZoomValues Canvas 1 1 1 doc/next-tutorial/per-object-mixin.png000066400000000000000000000606451242365656200203520ustar00rootroot00000000000000‰PNG  IHDR›wsæ pHYs  šœÕiTXtXML:com.adobe.xmp 5 2 1 m ž@IDATxí]ØÅÕAQšŠ E@QAE±°`¯Ø[ÄnìÆkbŒF±—$**Š Ä_»Æ®QcEÅX°Ñ©Âþï;ÞsÝï~÷~Ü~w÷¾çyν3³³3ïœ3gÚÎ. ! „€¨$Í*™¸ÒB@!@dl$B@!Pqdl*± „€26’! „€¨826‡XB@É€B@T›ŠC¬! „€Œd@! *Ž€ŒMÅ!Ö„€B@ÆF2 „€G@ƦâëB@!°¸ B 1‹ûÊ'iàlHY¥J]LXW i=&ä0"fXÌD^•‰ª#ixÒÿe”J¯ lJÇP)Ä,F†SÈ6L#cl¥‘á1$’í¦ J¨˜ qMÿ4Û@WF'„R—26¦è±FÀ ŠšL—…  XV™/’ºa¦áYH£#ƒC˜ '›Â1Ó1C 5ª1CÓÙ7þ×ÁĬ>«˜]˜vàŸSÌŽˆ#\Š A@Ʀ´7ÎШÐÈPæé¶×dÍòË/¿t“'Ov={ötË-·²‘Íš5ËGlÕªU~7(VI¤:)K ë,ÀuÿfÝÐèˆ @À¦ ¸EQ…@|È2ª¡±¡¡YªÚ¥xóÍ7ÝZk­åºuëæÖ]w]×±cG·Ã;¸¯¿þÚgå±ÇsÍ›7w7ß|sÖ¬m¼ñÆnÍ5×Ì–¯çý÷ßï.ºè¢|£+Þ/rBy¡Ñ±iWáR26E€¦[b‡@xTCcCCSUc3sæL·ë®»º÷Þ{Ïí³Ï>näÈ‘nóÍ7w?ü°;è ƒ< è-»… rM +ÀǼ;õÔS³†åëy×]w¹3Î8#ßèŠç‡‘fllTl#áS26€¥¨±E€eÓgì¡. Î:õþûï»M6ÙÄ]wÝunذa®OŸ>îôÓO÷à†nða6ò8ôÐCÝf›mæ&Mš„äœ;v¬ûÃþà¦M›Öèÿ7Þè&NœèŽ=öXwçwºßüæ7î©§žrm´‘ãˆæùçŸ÷÷ðçÓO?u[o½µëÝ»·;묳¼¢ÿ‹/¾èž}öY^ºùóç»3Ï<ÓõêÕËõíÛ×þùÞß~®¾új׿7`ÀŸÿ)S¦¸#F¸ÿüç?>ÊvÛmçæÍ›gÑåæF€rBy¡ÜP~(K0Ëàˆ‚ˆ½(±0Hª @Ø(°¡`£±,¸;¸/x}0ŠÝ^}õU+‚%–X"ØtÓMƒÎ;ûÿhä‘ S§N¦¿‚{ï½×ûuÔQé8àï÷É'Ÿx¿ðÿÃ?܇=øàƒéø¼8çœs¼ÿ?ÿùÏà‘Gñ׋/¾x0tèР{÷îþÿ˜1cü=˜‚ `€üõ¹çžëöÙf›#$ý·¿ý͇ÝqÇþ?ŒM0dÈͼ<ðÀÁj«­æÿŸx≌¯ŸìPRr²:\ÊMpk0åɯ÷áNµŸyb  òJBO¥B£À §AÚ€—÷7jeÌØì½÷Þ>ì•W^ñóïÿ{ÿßò-Z+¬°B0}úôt·Þzkðã?z¿ð¬Íøt0ºIÇçÅèÑ£½?‡›ßþö·>ÎçŸ4kÖ,8äCüÿ°±éÚµk°Ê*«?ýô“ F8>åÔ©Sýÿ£>: ±#a Ï?ÏÿÑO“P>À›¹PF¹én ¦<ÉØØvrR$êŽphx(óì™’sÒÀ}X‡ìÌ:7{ölïî»ï¾î‚ .ðk/'Ÿ|²k׎;c!.à“Âÿ¹óŒãåV\qEÍ7Ý£G´7¸‘FÅOŸ¥qÁ)´¯¾úÊw[·fGû‚áóŸ}ö™¿·}ûöþÿµ×^›Š!§8…F™á™2DY"Ó‰òD€À‰„@Ò œ[#A—‘œÄ]aÙˆkãÆóA×_½›;wn¶hü¸vBzôÑG„=þøãþ¿…óÏÇìý¸Õ™;ÕÂa À¨ÅµmÛÖõë×Ï}ôÑGi~÷Ýwý}\Ã1ÃCLùQ£Fù0ýŒ;%fl¬½¤¡ˆ€WàmŠ.b‡Ê»õPÙ€DsæÌq˜ŽrK/½´ßÑ5~üø óXqƒ r|†þÏg«®ºª»é¦›Ü 'œàn»í6·ûî»»·ÞzËoDX{íµÓyáB> Öƒü(f«­¶J‡ÙÖbüèŠ Þ~ûm‡õ%‡©8¼ýöÛûM 矾ôŸÛo¿ýÜO<áÃ0-çݧŸ~ÚŒüý4…å„LàÈ24¡(¤¥Öm„AbeJÁ‚Ûœ—w÷oÞ ÜhÎÞÖl.¹ä†‘ƒ_ãÀ¶ã[†ýõ_ÿú×#š`¥•Vòk#ï¼óŽÞ@ÌÿŒ£âÓà³É;ï¼sðÍ7ßøûmÍfÇw ðâ¦ÇTœß˜Àá5ì€óë/-[¶ôñÖXcÆÏ§óí·ß0zéç0l„ > [Ÿƒ.]ºø0Œœ¼Ÿ~²#ª#Êɦ`Ê å‡rÔÌ;«7•([MÞ FE‰„@UÀU.èÒàpqwi0w¥ñÕý±TªjOàšK¯^½œ­ eæÛ’¹]›ÒAÜÊÌõš>ø í÷Ã?8lp\ó ïÆe¹¸%{Á‚F1ß.¸=ÛÖtÌOnCRxîßïÀÜÏÎE±ŸÀsÀs±Nù‡‡"!Po°‘¨Ùt H؈d»ÝÄ᦮qƒB˜8¥GÎ$6”áM™á24™ˆäüO9!W¿W’3Kñ ±‰g½)×…#`ÓJ±l<8:ዚ\çU°ÜðÁ2:EÂ/cS$pº-V°°‘Œ€‡*š, tÌ1ÇÄ"Ÿ É¤É §bíšò¢é³"*XƦÐtKì°Þ¨–tÃQ‹5›Ø¡W§NuDLv “û/7Oh±EB ÉXc¡F"ɵ\¹²™ü„G7æW¹§&0e›VªŠÔŽh(ëtÙPðZS!A´H²Bù Žy³"üŠ€ŒÍ¯Xè*ù„{¤Öh$¿Ô*a)XçÄÒP'Å(Е±)0E-aCÃk›ØV¥2GdlâXkÊs!ä2*aãSHzŠ[„e…òþ_hYb›"Óm±B€ƒ±e\ †!!7ÂòCƒ“«“OZuGƦ.«½î mrn†®ùÕ*pIPvdhŠ€P Whº%–hú#–Õ™L×ôˆ£È PBFdlJO·Æ38ê™Æ®êj–a“¶•626¿še*Ž–±‰c­)Ï… ` ]»æýÖp’–âÖ”“ʵ™aYª?TŠ(±WÄ­ºEÄ k2ÝXB™­:”ÉL`—±)ˆJB! šF@Ʀi|*„€e@@Ʀ * ! „€h›¦ñQ¨B@”›2€¨$„€B idlšÆG¡B@!PdlÊ¢’B@¦±i… ! „@±)ˆJB! šF@Ʀi|*„€e@@Ʀ * ! „€h›¦ñQ¨B@”›2€¨$"€Økn¤3«ÌEð Ð’§«JƦ@À=¶Xã@— BšBîSÜ26…zAÀ ;2ÞŒO½”_å,°Ü0ÉMq8ºÅ‹¼O· 8!À"üM»Å© ÊkU0¹a§Ü®)/Q 26E€¦[b‡€õFͰ¤Ž ° ÂË´`Á7{ölצM›ÂoÖ‘G ÕÉ“Èç?jÔ4ZÔjDù)7ÖXäÕH¼ôÒK®OŸ>‹ÌÃ<àV^ye7zôèEÆU„X#`òݘ_¬ VíÌËØTq=¯pDCY§Ë†‚×Y§BÖ^{m÷ÄO ¸iºï¾ûÜá‡î9ä¦#*4î„e…òǽlUÍ? zA Ü#µF£AÙ?úè#wÚi§y¿Ë.»Ì]wÝunÏ=÷t]»vu{ï½·›3gŽ1b„{ðÁ½;jÔ(·páBwöÙg».]ºøx矾+ez®A†ô§ÖXçÄò‘µ“brs# 5›ÜØ($Y„ ¯³›üѽñƾäŸþ¹»ù曽Q9î¸ãÜAäÆŒㆠæzè!·ÖZk¹wÜѤG}Ô½òÊ+nÖ¬Yn×]wuݺusÇO‚*(lJO·Æ¬F9ŸœÙk¯½Üî»ïî6Ûl3oD>øà×¾}{×¶m[×±cG·Ì2˸o¼Ñ}ôÑ®{÷înµÕVsÇsŒ»óÎ;s¦©€Ø!–ÊSøì S« ËØÔ y=·š°q0¶çæÕ`pj̨uëÖ~÷™ý7w„ nýõ×·¿n¥•Vrß}÷]ú¿.@X~hprubQØJBƦ¨*ͨ!`rn†®ù5™×fÍ­_¿~î‹/¾H§óᇺ 7Ü0ý_‰B€²#CSD•.Z“ŠHT·"P±é®ÛÜvÛm~ó7póÀСC#²TÜ #S€26%€§[c‡€œ²6x {ýõ×]ïÞ½]ß¾}]Ë–-Ý6Ûl;p”ᬘ¬°­´‘±ùe½AžÙXL[4³#ßd €·À›£$K€[‚ùªÿÒàeÁËÇ–Kþyš·M/½ôÒ~ 4ÒÅÔIF»£\€›žþ <<²³®(O´õ9O -öXo4Ó-KÁš7ož×Éey˜©&”—ŠÈL5 …gi- µ <! Ž€ŒMÂ+XÅB@D›(Ô‚òiæÍ›éü)sB ÈØÄ¡–”Çš!0qâDºs±xøá‡Ý¸q㊽]÷ Ä c“˜ªTA¢ˆ²yùå—£˜5åIT›ªÂ­‡Å¾”9hÐ ·îºëºÿû¿ÿkå믿ޭ¾úꞯºêªtسÏ>ë¶Ûn;^ÏS›>}º»úê«ÝÓO?íÎ;ïé>ùä÷óÏ?»»ï¾;ýŒ-¶ØÂÿç§¡É|°»ôÒKýÂýÀÝŒ3Önü磧Nêïã16³gó(­ÆÔÔiÑ+®¸¢Ÿj;óÌ3ýg©ß-!/t6Z¼êK¹­0«®ºª2dˆ?½™NÛ~ûíÓOäh…ƨW¯^~'Ùšk®éþùÏúÃ7wØa'?¢öý÷ß»+®¸ÂßÇO `c€ëܹs:»àiÑØ@àO‹Æ¦ï†O‹æT§êöÙg»E®ˆ-:õ9¶U§Œçƒ@èÔçVˆßÜÌEÁ÷pGQ6âz ?ЩS§ÁŒÏQ‰Ÿïá(†ŸÓÌ™3}ZådRS§Eó9ØçÆŽ›y›þWÔ©ÏÜ™ÁSŸ9TåÞó™`ú  %l ELñëlqÎZN6@™FÆ"溇£–\”ë´h¾ Ê)º#FäºUþB VhÍ&VÕ¥ÌÖ %ÑÐpãH$M£%¡U†œ;–3AÔ šF+oUkdS^<•šB@dA@Æ& (òB@ò" cS^<•šB@dA@Æ& (òB@ò" cS^<•šB@dA@Æ& (òB@ò" cS^<•šB@dA@Æ& (òB@ò" cS^<•Z=z´ÿ„‹òÒK/¹×^{Í—êË/¿lô™èWEUA@Ʀ*0ë!qB€§1¿ÿþû>Ëü:'>Õì¯_ýuwä‘GÆ©(Ê«ˆ :®&2U¡ŒTWS Të#MWSÞzÖȦ¼x*5! „€È‚€ŒMPä%„€åE@Ʀ¼x*5! „€È‚€ŒMPä%„€åE@Ʀ¼x*5! „€È‚€ŒMPä%„€åE@Ʀ¼x*5! „€È‚€ŒMPä%„€åE@Ʀ¼x*5! „€È‚€ŒMPä%„€åE@Ʀ¼x*5! „€È‚€ŒMPä%„€åE@Ʀ¼x*5! „€È‚€ŒMPä•(‚TiÌMTáT˜ª"@292·ªˆóÃdlâ\{Ê{!Xã@waŠ ¹_qëÊ eƸ>Q(C©elÊ¢’ˆfdÌ]‹\+“µF€rBZç)–Ï_<–¹V¦…@aÐÀ†ægþDzó!ú ¼PNl$lrÄÿ¢±)0E%l$2 ‘ýÀ+§¸;ÜNàåÀ+€§ï‹šF ‚;€¿n:ZdCM.æ#‡sÁ?ÏSèOY±Ñ À(†dlŠAM÷Ä 6&K€W÷÷÷¯fãò-x*xøðê`6Ì1L#š"kRƦHàt[|`/45ºY¹æôÙ•`®)| f–ÆfuðràGÁjPÂ"hC„?þ1G<˜}Àw?Ž2YÓ5£B#Ãk2¯D‰×¢"Ð4Z é–x!24ç ׇƒwÛTÙ¸fcIcs!˜†ç\°hÑL@”MÁŸ/"*7\ðœ½ÀܵU ^g² M‰5§‘M‰êöh#CÃíÍ·€»‚¡gú Ü7àOƒÃMÛƒÙ[ÝÌÆ“‹Â¢¦ vœ>û¸éh>”Ûͯ? nî}âñãGÅñÈnôs)cý:R‹D¥nå‘3o€‡ Ýàb¯Ñɸà”ÙÅ`6„Ÿ"|<\Ñ"®Cå1àÕhJ a¶UŠ˜s í„lñá/ªdlꤢ뭘hô8J ¾×hü Ξðü/x=ð-`Q~lhS`Ø׃Á4,;;‚ÓÀü8W$œÖl$‰C à‘(ÔŸÁû¡±kraq» Þsà-w"\Q¯VæÛõÄwsð@0×b¸Y`2øwÀ‘Ól"!ЛpèOœ@CÈ‘:{ÜlwR¯ºüµ ŒW@ªO‚ÿ¦!8ó¥G‡0®É|^ ~ßÒO$ M£rc:n[¾ÌÝe룱£+*3À•£—Õ³%‹0NM>ƒ°-Áwf‹#¿úE€[>EB Ö ëpíåEð.245­NŽv¸A@$  i´pèOÜ€¡Ù y¾|,ŒÌÝqËÒò‹úX eâN5î 4šFKC¡‹8!€FÇÉü|x4nÜÞ,ª1¨‡Q5ÍÀ«àú£gG26ª e%?ÐñEÍ‘`.V¯ƒFM‹ÑùAW­X6•&cS-Äcð­ÙÄ ’”Å_€¡áô ×ff€7—¡ù›]q4ß» 426i(tu`h6CùNÇ™CÁ|;]=8²Ùõ¥ö%zuS³Ij½\h¸xr0·6ï#ó¶DE 5Úœ„ìñN‘ðhÍF‚i`d–@ù¢&DÙ Ù§‘ΰ2gغͫæ!·¾ÐȦ¾ë?Ò¥‡¡á‹šœÿï–¡‰tm5Êœ›Fò¨Odlê³Þ#_jšÈ$_Ô|¼3F4: òµÖ ƒÿÁ¿õP-øêOÝ" cS·UÝ‚£†Üñü­ÓadÎÑÍ­r– Tëä»O›f —_ý! cSuÙÃÈþˆ ^Þ –Nˆlmå•1M¥åS}DÒú¨çÈ—F†/jÞ æÇ·øEM½¨ùZ[d¹ÞvÝ"c)B]  ‘M]Ts´ CÓ9| < ¬5BBˆ;ÑVBýr£‡¨Î±©s¨uñÑqKóËà0š9 ¬5k])ez>êr’zÌOˆê›:€Z†æ·xþh0¿¨yu-ó¢gW ­ÛT Úx%¬O Ä«¾‘[¾¨ÉS¸S‰Ûšõ¢f"j¶q!P×}áû0ê¸WãPùÔÙÔSmG ¬h|8ÏÞnWð†24¨” fõû>’oz_¹‚QÒ1@@Æ&•””,¢Áዚ¯Ÿ닚I©ØE—CSi‹Æ(ñ1dl_ÅÑ( ÍîÈÉSàÓÐÛ=¬5£Q5ÕÈ…ŒM5PŽø3´fñ Š{ö`dCø¢æÁ`ŽfÞŒ{™”ÿ€ðÝ©qàåPÿ »[±“‚€^êLJMF°hdÚ [·‚;×EC£5#XO•ÎêýkÈÂWxÎÚ`N£Šê²{²$sù§ßzC  |?øð>àŸM6Ðøh €d#Ã(–$âTÚP”祉ŠCÀëM\õ§,Óh)¡bdcš$¥)NLêë.ni¾ü0•$+ÇUqPž²QýaúÔ›¤¬«n‡²Þ,Êë˜eÓ¦B²8ù§Zƒ˜%› %¡R© ÍC×aoQÂà”Ù³àCÁσ©œ§7—×aöJ…AÞËJÐ!ÓÓº6ã`:ÅgÒ?Îijï>÷ÏsAªœ÷°îx]Áóy2ƒéPlô§TccŠB—†ÌïŽÄ]1PQ… r´ÿ N+ Œ •§®(eh̠Х‘ù,ý©+I(¨°ÔŸ¥Á¦;沿ưȒõ  Î`hTcJ´øfx“lΜ9îóÏ?w«¬²JAÏüùçŸÝìÙ³]Û¶m ºO‘£…@Jn–D®Ø ’itü#¯,Ìg¹(CØIËK&Ožì–Xb ·Ür:Û²\u§tRrÓy¦ÞxÝkFÆÜH‰†¢XbCÁûMQÀR¹{ûí·Ý®»îê–]vY·êª«º•VZÉ{î¹¹¢7ð¿à‚ ÜRK-å¶Ùf›þ™Æï.¼ðB÷Á­‹"Œ å… ,åÇ .놬ÌyéÏ-·ÜâÖ[o=×µkWשS'·É&›¸Gy$ ÖÅ_ìî»ï¾ôÿb.zöìé¶ß^K*Å`Wå{ZâyìØSØ—ÒŽãö*G^Å0²Ç²Áà\l{ð à•À¾‡šù3pàÀ U«VÁ?þñJl°Á´ÂÁí·ßžµÑÿ^½z0PÁ /¼Ð(,ìqï½÷ú4GöÖu„`ƒ9¬íf÷œCUvR¼ÑAV‹’Ǹݗ*/õ‡Ûé?]ÀYõçÃ? š5k¬»îºÁ=÷ÜÜ|óÍ^—Z·nL›6Í×nóæÍƒ=÷ܳ¤šîܹs°å–[–”†n®,êOo0{ZLýaç-òúSŠE l¨4,p+p#üî½÷Þs}úôqGy¤Ûe—]{j'œp‚±ðŽ| è®[·nnèСnĈ>áÇ»‰'º©S§:&ï÷àƒºuÖYÇuéÒÅAÁÜ”)SG5þóŸ}ø_þòßËÛh£ÜÑGíýøsì±ÇºÁƒ»¹sµ>™¥6ì ÐÀØè†rXŠ,Ö¦¥=•úÂܤþŒ7Î-\¸Ð 6Ìí¾ûîŽ:qÓM7¹Ã;Ìëÿ3ü¹çžsüã}®n¸á·ÖZk9ŽVöÝw_÷Æ¿î8¾úê«]ÿþýÝ€Üé§ŸîõÇßú¹í¶ÛÜæ›o暯.#‚@XL†(Oäè A¡ŒÒ°PÂѸ°WF+»x8«i_ýõý¨@!‚矾A<úwèÐ!€AV_}uJ` -hÑ¢E¥ .½ôÒàwÞñ½¼•W^98ðÀL¯˜^`‚wÜÑß·Ã;ÿýï½?ñÖÌ›7/hÓ¦M°ÝvÛ5x®þTÊxCp?p0G7ìÝsZ rS°<ÆñžTyÙhtw÷¯nT!Ÿ|òI°øâ‹X« (Û×^{mðÙgŸ¥ãwÞy×¼°ÀÈ 1›:? Zc5|ü;î¸Ã‡ÁØC† ñ×p€³‘ÍK/½,¹ä’^fÍš•~Ž.jë¼xu0å&6úS”b£€46l¨,,lpð&à¬5‚‘‡n*ãi`¨H4—_~yðÔSOß|óMpÈ!‡øðýë_>­¥—^:ØvÛmýõI'äÃ^yå•€ŠpÄGøÿï¾ûn9vÉ%—ø°G}4@¯Ï_cÄ”5ò¬©ºßî@ðÊ`n•æîöîëÂØ œ¦?œéî Þœµ"î¼óNßáb8™Ójìl…§Ñ0‹àõ‰0vÔØé"a´âf üŒüƒÃ?Ü_ÓØÐ­°Â f‚¯¿þÚûë':¤êŸú³˜ú³<8úSêÔïç0Ž£2§Ò²wŸ=ýôÓì 8~!’SgÒsw ™Sl\üü¿ÿû?Ÿª¸QZèÑy?Œ”Ö€Üõ×_ïÿúé§ârŠcã`Èz‡~ ¯QDyTʉM¡Q~Øø’뉬̦Cì¼åÔŸ}öÙÇQöÑ©rá;Œì§º^|ñÅF˜qêìÍ7ßtk®¹¦Ã,Ÿ&3]bœªnßžÎa””Ö!þÇÌ›éç íþ̸ú_U¨,¦(”2娞ÈôÇt‡x°iDØðâ¨?ì4õë×Ïýîw¿sµûxÿþ÷¿ŧ19r¤;óÌ36øµ‹Ô·o_7}útûëÆŒãF•þß®];wê©§úû0=—ö×E¤°ŽšéŽéO¤uˆ™,•¬ÀTœFÄžBÕUWùÑ ÍQGåfΜé°+Í»¼‰ŠÄ…Nì¶ñiXo,œ æ™ý_n0øê«¯¦Ñý,Xà0µàÃþ÷¿ÿù…Sþá膽Aö÷Øc®Ÿš#@9¡¼°Â"­ U@ŠsêOïÞ½Ýk¯½æe[û±#ÍuÖY>kÔeÒ¤I¾óE½"m¼ñÆë/^öM—¸µ™ºxþùç;¬ß¸ýöÛÏwÜü ø4h}€#¢ë®»Îo¼±0¹‘A€úC™1—‹¾Q åTÁh]ù&8·lòÓ¯w#¹ÆÄ…}Œh ßýœ3v’˜6 ¾ÿþ{yë­·öþÜ}ÐAùë‹.ºÈ‡…×lèñ‡?üÁÏ)óY\ô¿=µ}Æ'ÀÔ_,EÏÎß‹é5Ÿç´¹$ª-¬30ç7‡€¹Î·"˜‹äÜVOk6œ2³Í5\ìݼ+8kaÊ,ÀNJ¿Q€q°Ó¯ËXäC=Ôoàn¬¡~1_àvf^s}ôÛo¿ `PüúqãÀ„ |2¶A€øzñsÔ!7°NÀÃÀƒÁk€©?”#ÿú²Xp{^­{š|ÛÈJ©·X9äçËEÜ$@£Ãs±ó>f>qÂõöÖNƒh_~ù¥ëر£Ãî³þÙþpôñÇ;ÎOgÆgÏŽ~dl>ðiò%¸‡z([Rò«"©:ß ü<Ì9vÅgçAvlMJ(äÜÍI¦þÐàRîmJf̘á×S¸í?“æÏŸï·õszšúÁ“:8•œILŸ£ ê"_®Å”þ°³fú3×<"l6x>êvÜHRÖi¯"sJËÑÐzdI#ŒœGÕðíè|‰Ó<‰ QÙHœ:ã;?üðƒ;æ˜c²E•_màe%w¯¤6ùªõS©?\S!g#ÛhÃ0êG6CÃ06X¹Â.Š‹”•¨•¢TcÃÆ"\èH5TJ‘³ÕV[9LÓE ûzÎå$,;‘’›*V GqÔ²aRÅÇëQ1FÀd'6E(ÕØ˜’X½Â¤¦ ̯æ®íÜ©yF”"Ͱ„;,õˆ’OÔô§+$Âe¦þ˜¼˜.e¶Ã‘Ì~)Æ&\Pk,<MÍ9Geªj„Ò°Ìðù&OUËKd³^§*jµÆeÑã«„@JlTãÛ[<ÚtÇÜ*妰Çpî¼X²Æ¢Øûu_ý"`J!úµ—Ji0\êW2Tò|0Y¡ =*ÊØ`äÂZa œXØ2+·æP^LfÌ­y¦ª‘”þ„ËlXdêT5²£gݼ„å(Ò%)ÊØd)  ›%ÿòª.¦ ¦4|:e±žÛ°¾X¹ —êÖ†žG(+¦3&7æF²iT=N±Æ&[FíôÑlaòa(w”5°aT~ˆ°aˆþ †PbE¥6áB«ñˆUõ×,³Ö3Ù©W¹ao” “šUŠ+Lvb“éÅKÌ©)‰%ãf1yÈd3,õ./,¿aH2$FÃPL^L—øß®Ãq#u]б±ÂYÁY0©²*3@ ÔRVHæÖ›Ð°¼Vöð|ûbÒ/úÉ‚@JlTãÛ[D3Ý17˵÷*eÍ%¯RtëÖͽóÎ;MÆ=餓Ü9çœãã<üðÃnܸqMÆW`l0¥(H†b[Ú¦3n c. îx饗\Ÿ>}øeûc:FcuõÕW»ùóçg‹&¿d `²B7zTt/ v ²¸ ¸¸xYð}Ùzf“&Mr;wv-Z´@”ì4uêT‡t]ûöíݰaÃÜŽ;îè9äì‘åKX¿ ½ÀSÀSÁÓÀ3S<²îåÃ;™–DÉZ‚Û‚©?íÁÔ¡Fú3wî\÷Ýwß9“¦Èt¬yóænñÅw?þø£kÓ†ê)J )ýÙåùLúü#ø'p¤õ§”‘ Ê–&¶ MZØã?Þ}þùçîí·ßv|°»øâ‹ÝJ+­äÖYg÷Ö[où„FåÆŽë{eO?ý´;ï¼óÜý÷ߟ~ˆ.ƒ€õÄèÚ5eÑzk‰)h ë‹•Û°hpÛG}äN;í4ïwÙe—¹ë®»Îí¹çž®k×®nï½÷vsæÌña¦c»í¶›ÿ¿é¦›ºY³f5HKƒeÅtÆäÆÜH²(cëšY(SSšF…}íµ×¼àÿôÓOŽF嫯¾r<òˆëÑ£‡;ûì³}üÏ>ûÌMœ8Ñí¿ÿþn½õÖsGu”Ûb‹-¥%Ø#`rB×®m:ö…˳Ô!+{¦>5H‚#”7ÞxÃû±Ãvæ™gº}öÙÇÝyçîÕW_ucÆŒña¦cW^y¥ÿ?räHײ%O¢"@ÙYË‘ÉS$‹[ÊLÉ» íÚµsì¡q¸Ê)§¸áÇ7§C‡®uëÖnùå—wmÛr–A”@(/&3æ&°˜‹”ꬅËÆ¢ñ >{íµ—Û}÷ݽ﮻îê>øàƒ1V\qEÿŸ3û… ¢êO<0ÓÙËQ¤KSÔÈ&U¢p!Ã׋,p§N¼¡aDÎ)Ïž={‘÷(B¢(H~†ËNf’ÙËZÔ.]º¤ýÙ)“þ¤á¨— ›0¹‰…þ”blL1 _ç¬øfÍJylÎd¬gf9λ¡µà†1 Bä¥;,·ô'µ_ZL^(3fx˜¢µÉ¥¥^¡»KmõYØpÃ×%e™Sl걕an¦üÇ!¿•Ê#1(qêŒI›Êi²¶×:-ek{+UØR´ìù:t¨熻ÓD‰C€r–È+J…j ÜC-Û#hh¶Ùf·ÖZk9nÈ%ÊG5±¡¢Þ³Aωå{6K[ƒí=ޏnôžüŠ¢™3gúÝ4划@jÁšïÙ|æ{6ÓÁ|φ{tçÕÃ{6!ýá{jd{Oúso¶÷Ôà_0MŸ>Ý-³Ì2ß§¢‹@JöDMfàšïÙpá›ïÙp‡Z$©”Ýh,õPíº¬…Ô ie…3J‰™Üds£”ÏJæÅFsa Êú<š²Â¥ÄÂ3R&?a¿(å5—Èg0S]! „@l±‰mÕ)ãB@ø Kc³`ÁG !P8óæÍsåZ*ü麣^ˆ¥±á©\pA½Ö™Ê-JB€' dž:PR‚ºYä@,MåR! „€ˆU36C† ñprïÿª«®êOw&¯¿þº?¹Ö0áa‚p€ÿûý÷ß»#<Ò­°Â þtèW^yÅ¢ù#×yº-ÃöÛo?ÇcØEB ‰<ÿüóîÄOt'Ÿ|²ëÞ½»?œv„ ¾¨<=ý†nHû¢‹.r<€“ôì³Ïºí¶ÛÎuìØÑñ<5n…6züñÇݺë®ëzöìéþþ÷¿›·\!P1ªflø¨[n¹ÅÝsÏ=~ ì°Ãsœ;æ»4á¤ñ¿ ñÿñ¸V­ZùÏð»6GqD*Ô{ìáÓ£²“oÓt!‚¿óÄ¡q+3 ;kvR:¿a3yòätIùÿ믿öÿ;î8Ð-O†^¸p¡»öÚkÓñxb4O‡æÇ O§u¤¡ÑE…(õ=›‚²õ§?ýÉõîÝÛó­·Þê{^K,ÁwC³Ó—_~™VœcŽ9Æm¾ùæéˆüž?E@ÚvÛmÓ*AB AðÃ40|©ïÒK/u½zõò$Wù•Îo¿ýÖ}øá‡n³Í6s7Ýt“ûá~gëúãÿè6Úh#ÏLwüøñnÍ5×´`¹B ìTmdܯ²Ê*épøþ /¤ÿÛÅÏ?ÿl—^¹xܧÊ8 ÇïÝ…¿ZÈ“ou”!#7‰°“–z{Ü~ƒGÒØ @¸¼¶K“¸ë¯¿Þ]~ù原ì8á„|&šÓqF:9Ú[IªjlÂ=«wÞyÇ&š…ã߈Ӷ-“ÇÔðküпßñ›ßüÆO»1®Ž°1ÄäÖü€šG,ß|ó× Lýa’~„@ÂhêÔg¾ÍÍ7ÜÖµkWwþùç§OàhJ癊Ex4L¡ŒrÐHñ¥—eÀ]ÁÜÄ¿1x0’kL-Z´¶ÜrËÇg8I À ¶> àx ƒ|÷Ýw>)àXtŸÚ ðNNðÌ3Ïï¾ûn€w‚ûï¿¿qâò‰ ”ð^àÍÁ<ù±xYpKps¤`yŒÛ=('õg)0÷ôw÷o Þ Ü¨.)óØÞ ó཯#Ô Nƒ äõg¥}úô n¾ùf–Kç| ~b‰åLý î¦þðGnõ´þ¥Ø(TQÆ=´tï¸ã޾©à‹›iãÂ@¼œF0³P¡pŠ@ú|V À)éÿºˆ¬[°ŒMÆ#—£_áø G€oÔ8M ÀéNtN ÏÖ[oíÿÓØdÓ¹td]ÄþÄÎØÐhT²úÌ“œ×[o½tør'OÀ(Çû…ïá›ÓÙNŠN߬ !PrúÌx_ú4Ý¡_Xø‚§ôÇ’[mªjl²úܯ_?÷Å_¤ËÍ#mhlìMéði·<):×Iét!ˆ@X§>gê¹å·FÙtÎÂä j"PUc“íÔgL§9~¢–'?“xÔV[m•>ûéÞ{ïõ›øϱcǺM6Ù¤šøèYB ä:õ™úÃS y8'æ„܃>膚Îs6KêBTª À2å:õ™Æ…Ÿº]{íµý—9ï»ï¾4mÚ´ña, E2ù û…‚£sY¬±ÉU)L.däŸ É˯¨X£ñ«®„@v̰ÄJJ56VXž¼ ;6ò  œ­59j©þ„ 䊭"–ˆåæçT±Ñr|b lh@€“I+LÝž\¨óÁÖ¸Zƒ›Üç.±0ý±Dú“/…üÒA£î˜¼˜þDÞè”blX8c+8‘}ÀÀíÀmÁ­ÀK——ò<Ü.Š” ÊÃ\ð,ðLðà©`ú³cb£Ƽ² å$+¯éñ .û‚3õgIøµs&"I9–‰íÂOà(óÂoj? ž]ã Q.æ©?ÌËàài`úS^ÇØÚbxE“JmüYP6¦(a˜ Ó¦b0œJÄÿÍÁ¢ú@€²A¹ LPYØ P.(T ÊeƒyEAËMVfÓ!âA\L¨+ŒC ÙYK¢þ†r±Ü#ÁQ b¼xðXð÷àZÉëž2A½1¦.QFˆÃMpm"¸SÂL™²Ð,¼)Š¥Ë0‚Åž•GÆ Ô YCi½3*KØàXïŒ2—ñë‚Rúòš¡!™5âaþ6(u•ÏŒ ñ;ÖI°m˜&Q 6æ4î{Ç€?ׂL6¬³f£ºlSéÏ6—ñÈ‘×3 ÈkÁÄ fmHl#bŠÂg%IYò¬="rè[od%"ÜšÁ¡Â0ÌŒ ñ‰¼Â0“e"k$Ì P?ØÐ¥þP§L¨Cì¨%EX>NW±\,sKðpˆ_]̶lðCàÇÁÕ&ÓÊ€éu'¬?”r]‚AE0e å°×ÂJ£%IYPœ¼¨#b>2¯ØÉŠdÊBØà°qá(‡òA¹1e¡ÂÔÑИ±¡~°¶Žq OŸ%©³6e£!±\Á¯ðOèc䯆Dc¸˜kh×€«)£f@¨;dvÎÈÔÊõ‡ÿM†¨oäÈ+ºbáXX6,8‰ ÙÿLCCEª'ê„Â~ fo©Þˆ²AY0e0ƒc½4Ê‹ÆáìR¤•y,'…ñ¡^°ñ ™XQwŒÙ‰3c„ËXËq ØŒ Ë8|#8 ô.2± ˜††Dƒ¿>¸;x8¸Z–¶§” ºfdèò?Ù¦¨ëO)ÆÆÀ@YÓd~€i“)\6ê©7cÃx_‚ëuÍzgaƒCÙ ‘1åa˜ÅÃe}†…©WXþ0 bD½ ëuÇfp[Ú9çŒG˜úàOTtdòÂŽËçJUÌ#ÛQ]ÓÊ„1óg×”rä©hcC+šÚ$.¨œ ‰ºTãȃRæ ¾…ô¸e±ÞˆŠblrA×”Ä ý(N¦`õ„Ën0ʸPhX¨Ÿfd’ ?42ÇÙ ebðaÏ]sdcFùbÝ ¾Ìé«j‘É]¯#p™ʇ¹ôçµÅÅet©hcÃ"¡}`ïŒÃB“è 30 3%1¥‚WÝÐMuSÒì5%0—ò‘É–=…ûÒÀBˆ10—GRõg5”­xfª¼œB£¢»ø5p­é}d 5˜ÓTO‚û‚Ÿ×b*œ2A ËýÂìõ‡²äcFøg±rä15`ÆÄ ‹]φ&ÂÕ^Õ¬™x¥À“¸qP’J£µý¡>%‰hXz€¹qfEð›à À§¦®áÔœFÞÓøm þx ˜éj’éŸiz6@><.:TcF?¥8æeÈþË­ob¥µªª%Y.¾\§ùk­p.à¹!î£à« ¸§Qc­?%M£eC3ÃÊzp²ÅKº_ªÁXx¼—ô²ª|åE ¤C‰ÕèÇr@m<Êj=õò‚XÆÔב\&‡ü–±èeM*iCô²‚Sbbkàþ1%¦¡Û…@RàY_ŸÄ¡p00óÁehJ«­²lJËN¢î^¥á6J‘ á>1ÃKŽ€F6•«`›Êa«”…@ÕÀtÚê`ž( *lŠ-Ï[hÈŸÉ3®¢ !}>DïÁY#³¢ŸÝhå°ì»Ñ¢U<åF("€»ò5v¬6A ßC‘ᅢ¹ù‡'aˆòD@Óhy¥hB@”4ØA©GlÊ_+‘d9wÙ”?‡JQ!Peâ>²9xEêMä)S¦´_f™eÆ"_QÛyòt[•åKB@xânl: =¢T—:0Kné(å)•—(æ)‚0)K•D›g¸=¹/¦ÒÞªäsâ–öœ9sÜRK%û½PM£ÅM*•_!o¸yæêjauÖÉûQü±{衇òŽ_®ˆùË_ܾûî[®ä"›NÜG6‘V5B€k†‘.8qâ S§NåðŸ!+'-Ÿ-±ÿüç?Ù¼³úMš4É=ûì³n‡vÈ^)Ïçž{ÎÝxã•J>2éÊØD¦*”!PZ"•­Ê’RYqÅ´B’o”äðáÃÝ=÷ÜãvÛm7·ì²Ëº>úÈp nذaîÒK/u/¾ø¢kß¾½ûÓŸþ䮺ê*÷Á¸}öÙÇÑð\{íµ®E‹î·¿ý­7@ÙÒxøá‡Ýe—]Æ/xºK.¹Ä­±Æ.rÏ<óŒëÔ©“7"­[·Nçë±Çs×\sã´Ù©§žê–\rI÷Ö[o¹‹/¾Ø??1‰)Æ| ò.Êc-Îu­¼çW½ò‡úˆµöÚkû‚víÚ5€ fΜ 80ÀyfÁšk®Ì;7@c<ÿüóÁ“O>œvÚi>þW\,\¸0øöÛoƒÁƒgMƒžC† &OžìÓ>ÿüóƒûï¿?€±ññ/¼ðÂàòË/÷×ö³Å[?þøcðõ×_Ûo¿½÷Þf›m‚éÓ§[”j¸wã!U×'l’؃P™„€h€@çÎ]÷îݽ6)¸æÍ›»£>Úm¸á†®oß¾î‚ .pŸ|òIúnôáè§M›6nÖ¬YÞ?3ñãÇûÑ ýIgu–}öÙg~´òÓO?¹Aƒù0þ|øá‡®K—.>M¦;oÞ<7mÚ´txÒ/dl’^Ã*Ÿ'27@á‡~ðÓj¯¿þº;v¬»òÊ+ÝŽ;îèã`ôã§ÆÞxã ÷¿ÿýÏd¦Aã…Š¿ç«¯¾rwß}·0`€ÛtÓMÝ~ûíçÞ|óMÇ´Œzôèá0 ò1jr]ù)< Oº+c“G Û3SØò¸UQ„€ˆ K/½´=z´û÷¿ÿíðnœ;î¸ã\Ïž=ýÿý÷ßßuìØÑ™Ù³g{£ðî»?OÕ²eËôZG11C9Ä=òÈ#Ž»Ûø #nmÆ4šÛu×]Ý‚ ÜGaAõáZCS·*k6œÏµ¹\àä Â|ðÁöw‘.„/xÿý÷Ï"0ý<Ðþ6p÷ØcCþ~yüÑšM æ©Q/ÕžךMÊ`Q¾ùæ›`þüùö7ÀÂ}Càÿ3ŒƒÓ Ž÷ ýÀÐøõWðùçŸûu¡°Ÿ]cT•3ÌâTØÕšMœÌ{Ÿ>}üÐ;ß<¿ð ŽCgÞ—qùŸÿügÖ¨#GŽLü `Y .O!Pf–_¾áŽiî3²°E½lÙªU+»%ívëÖ-}yѶmÛL¯ºøßp"3Eæëà÷0pï½÷ž/ÕwÜávÚi'·Ùf›ùí&LptÛh£öþ7(9·@:Ôñ~[4|å•WÜ&›lⰃĽýöÛŽ‹€Ü®HÊL›~ÜVÉgq˜ipû%_Þ⢠‡î\¤\ýõÝïÿ{Ÿ?æ‘eøôÓOÝyçç‡õÇ{¬¿…[0_~ùe¿É9bÞÿ»ßýÎm¾ùæ>®¥+WÄêzõq;òݬØs£i4Ny­»îº~X‹ÝÁ¡‡`N6Øyç½ßK/½ì²Ë.v’Xà l¨lÃV‘`ï½÷öCi¼l`O¾ßiÛ%_{í5?}fÓhÙÒf|·UÞwß}öâçœsŽwí9t9†Ñ‹¢c/~£`vØaAxm»í¶óÓx˜Kö·Û4],Júrq & P8ùÌkM£Å[ÖóÕÓ¬Óh&3™B‘í?·übÁ<[PEýüñ ÿþ^_+ú %®i´¦Œb!al°ãöÆUW]Õ Äþ%-.ü‘8EâH…Cewæ™gºÞ½{û?Žj¸`ã7v't’ÛsÏ=ýèÛ%yüÅ;ï¼ãïçO¶´ùâÖ¶Ûnë_ƒÑñqaäÒ÷„/8*áge™W¾ìÆm–Ü)¦sÏ=×~8šÊ$V_VÛ¹r…ªò®\f6ô?â2õжØÝ~ûíîøã¯j©¸3Œ#üÔù‚U}¶Vy7FÈ8E…áôçG¹‘2§»þþ÷¿{ãÁx¶»Œ»CÆçxàׯ_??ÍÆpî‹Çˆ†—ŽÓn$N¹1ŽQ¶´¹.cÓoÜ•‚—Å,z#׿ˆis^óÓmþóŸE¡ E!Àw@rM½>úè£^'öÚk/ǵEN#s'Õ¨Q£¼‹—ý*N“²MßòÍ{¾—‚  )Ûô²ÀßQát7;[W_}µûþûïÝwÞ鮿þz‡-šÜ!HcC#A!¦ò`úÊ¿´ÅÆœ#*‘ÈE\Ãá‚ûäO9å”ô{sÁû<òHwÌ1ǤoçB~fÚ1qÿ=½àQi1^æšM:‘&.n½õV?úâÑl,h¼DB P¾øâ ¿^H¹¤<>õÔSîé§Ÿöï}°±§lÞ|ó;“Ä Gù4Ås£ Gÿcƌə×/9*á1-\WÄùgþ½vàþú׿ú£YÂyæóÎ8ã GCg1ê × ñ‰ŽpT]'ŽbÌY×lN<ñDü×LÂôÝwßùã*Â~¹®¡,‚¸e‘óÙ$¼°å׃,R¶´y,E˜0Eþ[ík­ÙÄ[ÖóÕÓ&×l¸vƒ‘¿—=“h®MâEĀǦÀùõÊõÖ[ÏÇá':m~o¿Üæ›- ®i†é®»î ÖZk­§{ÆÆ—p°_W5órï½÷<Þ…kœ¢Š# 5›rpnSäœo»ví%·ÜrË5òËåÁÃù2)¼e‘‡í Ç!FÙÒæ‘aâK`"!Pk2§^9jçiÇý`sŒã)ÄFx¿ÌÁp8¾ÉuE›îÍLƒòϵFêG8xÍO]sZlÆŒ~ŠÚÒ¤ËC1Ñóñ±Æëg$ªL\Íï]„*g¬Â[Páô³&Ÿ¸¸€_È7,²¢’‡'ç—EB ðN9Ó `צ_Ëä&¿Â£WN>ùd?ªâԧŲã0 v°¸Á&<½ÌõKnã×}xŠ2TO¼uƒ2ÃÁÕ¼þõ]„*>F›sùm1†ù¸Š­é£Cakš~ îÿu¥ÄÄ~;_Ö¹6áeTñœë>-Ž2H6zçq*d±ÏÅ{Û!Æ£ñ³½ÄÈ{yÖ9ü$ïåˆ>|Ì>ã’—ÇÁØ3ñ­ê/G6+Uõ‰©‡ÁØpêe"¸ÚàµÈCµŸ™¸‘MµÔó„@Èlð9MfSeá)â\††p ?9Lá{Ãþ¼fÜÌçfÆIêœÿ |;G%µœár%r7Z¸€ºB@D±È×/GMG4ƒåÌ–ŒM9ÑTZB@üxQ¿©ÿý±ŠÙpÌ«¬+³B@ø"€©´ï‘û3â[‚Âr÷ ÜŸ¼taEŽFl¼(×/ˆ~WÅÜLų~Y®âCõ¨ª#PÔªç2¬Ùh¿º¹ˆ»±©.Ze|G ¹7Àס‡ë-e„EI•Ž€ŒMþÖÔØ  èÕ玴º ›U3.¿ÌïÆî¡ãÈ#6„ü/–ʬ¹ü¾ŽMY"˜ÑFÈÇ/§Ç.:³½¥à­Ï‹N6‘1>ƒ¯Rå’ñyë‚·ó›Ò§‚£B™r±+_GXƦ†Õ Aç)𧻢NùL¤)d`lc ]˜°‘Éüé2E,sae·kºfh¼ Y±ÿÙ²¿<7ÉP ?|]ötœmöÎ[+ëv^¼“³ÞûYŒ.ËïÞ˜ ¦‹eaiE\ ½Ù8%ýåED+g0O+`óU0i ®ÉK¥xn˜(c&oô·ÿ”3ó§Ý±kÆ)˜dl †¬ü7 ïŽzü¼ü)—/Å”¡¡¢›1—›LìÚ ¹åË@òS2E6Eg‰©ìG6flÌÐ¥ô“nÚ2Öü›p,Èá¿À”Á²žeòf½iëàT{D_ö²Å,A2ñ¨‹*†F6EÁVÛ› ä“Àg üâTzGt´7xõ2ç.¬ølälXM!U6´œÎ°&u·¢Æ²ÔÌ—?ï ®6±ŒÖÐY'§Úy¨ççÙ¨†rgí@Ñx°"E1E'ó…0nI| Ä—po(c&¬üìe‹ª‡–lƆ@Q[Qqߢè DàTÊv£·¹ÌáÖ¸ÑeYYvÉ\™A^Drá‘´ÉÜ"nÉ,c“›Ø… Aø†¦;2><Ì9î¿K%*;‰.ÙF8ôU34VÖ—å雎Hh ÈÐÓ¡ÏÄÿ)¡ÿÕ¼´ò±¼l§(sìa‹ª‡€É±7£oõRp.Lp ¾Q7D4 Á‚¹[èêp.Ñ üÜ?ìWÀµ ™uT ¯ Q­giø3IÖIÑYh~‰ð]®U‰ÕÐÐX6¬c™Ãå¶p¹•E€ÆÆ°§K*Zæ,_’Ño¢@c‘¹€Ëi·‡Ñ¸ü¼È—ð§Aãƒ{ÂÇÞŽ¨zo²élÑJÊr\ï ¾ ¼äåúPX”.YV+”ò•ô¼wvrè–,o&¸IMåhLFÀY|2xMpš`XþR6øÙÚëÒ‘~½ÜüŠEµ® óõ”ÏÃQ—Ü8r ø90{«F ÃÀ£Á³Í3®•Ñ\f‰×áÿÈfâ³P´ÌeCÆË&¿"€F…ôøàŒâmŒ†èðª)ÿ½áö>l²…Ÿê–X"¼T•=Ë’¹ì¸”Í—±00€`õ³'L£C7“Ùs¾¼;Ã2é›o¾ Z´hì´ÓNÁÙgŸ´k×.Øo¿ý|4ô,ƒ ¸&|ðOÆÈÿÏüak¯½v¦wÀ4_|ñG°Ã;×^{mðÙgŸ¥ãwÞy¬Ú?Ä9…7ò¿ ÌMÀFÆ&TùÈ\>ÆF2÷+¨)™“±$2*UÂB×Ì^&{5ÝÁk€7g563gÎ  ”6˜J 0 •–â÷ßß L¡¥ý‹¹x÷Ýw¬ùQ òã Ó ›Ÿ~ú)8è ƒ‚e–YÆû·mÛÖT†Óz¼lD46L“Œ5¦ÓkâTË#•nØ îÄ_Æ&þ¢dŽQó16¡$s^Ö™Ì•ÕØp¸*eC€‹êœ?òÉ'݉'žèÐØ»#Ž8Â͘1Ã}þùç~çýn ý…<|ôèÑnýõ×÷óðýúõs¿ûÝïÜ%—\â“ø÷¿o¶â¦„‘#Gº3Ï<ÓM›6Í¯ÍØóúöí›ÞT@?n85j”;LºSO=Õ߇鹴.~]hªQ¢úئd®y®c™+|é4dlÒP袰Á_qÅÝË/¿ìŽ=öX‡µ¿8‹©.¿ ð›m¶™{úé§Ý¹çžë®ºêª¬Ýpà ýæ‚Ì@n_å#° /¼ÐÝsÏ=Ž»ÏHl°wù¬I“&¹>úÈ¡×ëý6Þxc‡õ÷â‹/úQ1=¹µ™ˆ;ä¸{ kK~ƒ¿?¡ùg¬¾úêîºë®sÜ•$ŠMÉ\!¹•Ì‚Vq1^Ô””0È)©‚¦Ñ°e8Øm·Ý‚Ö­[û)(¼ \yå•~ZbµÕVKOM!]½òÊ+g²ÈµXËÈœ– ActiveLayerIndex 0 ApplicationVersion com.omnigroup.OmniGrafflePro 139.16.0.171715 AutoAdjust BackgroundGraphic Bounds {{0, 0}, {818, 571}} Class SolidGraphic ID 2 Style shadow Draws NO stroke Draws NO BaseZoom 0 CanvasOrigin {0, 0} ColumnAlign 1 ColumnSpacing 36 CreationDate 2009-12-29 15:06:59 +0000 Creator Gustaf A. Neumann DisplayScale 1.000 cm = 1.000 cm GraphDocumentVersion 8 GraphicsList Bounds {{240.5, 474.88}, {90, 36}} Class ShapedGraphic ID 27 Magnets {0, 1} {0, -1} {1, 0} {-1, 0} Shape Rectangle Style Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 s1 : Student} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 4 ID 26 Points {240.66513076390902, 332.13200000000001} {313.22215198541994, 231.38400000000001} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 22 Class TableGroup Graphics Bounds {{131, 332.13200000000001}, {179, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 23 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 Student} VerticalPad 0 TextPlacement 0 Bounds {{131, 346.13200000000001}, {179, 42}} Class ShapedGraphic FitText Vertical Flow Resize ID 24 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 matnr <<property>>\ oncampus true <<property>>\ } VerticalPad 0 TextPlacement 0 GridH 23 24 GroupConnect YES ID 22 Bounds {{374.2937385737556, 312.18396536633583}, {75, 24}} Class ShapedGraphic ID 9 Line ID 8 Position 0.57259494066238403 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 7 ID 8 Points {353.83415112395051, 243.38399999999996} {412, 305} {384.95999999999998, 405} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 4 Bounds {{339.95999999999998, 405}, {90, 36}} Class ShapedGraphic ID 7 Magnets {0, 1} {0, -1} {1, 0} {-1, 0} Shape Rectangle Style Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 p1 : Person} VerticalPad 0 Class TableGroup Graphics Bounds {{260.346, 189.38399999999999}, {136, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 11 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 Person} VerticalPad 0 TextPlacement 0 Bounds {{260.346, 203.38399999999999}, {136, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 12 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 name <<property>>\ birthday <<property>>} VerticalPad 0 TextPlacement 0 GridH 11 12 GroupConnect YES ID 4 AllowLabelDrop Class LineGraphic Head ID 27 ID 36 Points {210.54026845637583, 388.13200000000001} {194, 434.63200000000001} {240.5, 492.88} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 22 Bounds {{143.02340741637138, 429.66073293035294}, {75, 24}} Class ShapedGraphic ID 37 Line ID 36 Offset -15 Position 0.42781621217727661 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 GridInfo GridSpacing 14.17322826385498 MajorGridSpacing 10 GuidesLocked NO GuidesVisible YES HPages 1 ImageCounter 1 KeepToScale Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo Animate NO circoMinDist 18 circoSeparation 0.0 layoutEngine dot neatoSeparation 0.0 twopiSeparation 0.0 LinksVisible NO MagnetsVisible NO MasterSheets ModificationDate 2013-05-21 17:38:10 +0000 Modifier Gustaf Neumann NotesVisible NO Orientation 2 OriginVisible NO PageBreaks YES PrintInfo NSBottomMargin float 12 NSHorizonalPagination coded BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG NSJobFeatures coded BAtzdHJlYW10eXBlZIHoA4QBQISEhBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCEhAhOU09iamVjdACFhAFpAIY= NSLeftMargin float 12 NSPagesPerSheet int 1 NSPaperSize size {842, 595} NSPrintAllPages int 0 NSPrintReverseOrientation int 0 NSReversePageOrder int 0 NSRightMargin float 12 NSTopMargin float 12 PrintOnePage ReadOnly NO RowAlign 1 RowSpacing 36 SheetTitle Canvas 1 SmartAlignmentGuidesActive YES SmartDistanceGuidesActive YES UniqueID 1 UseEntirePage VPages 1 WindowInfo CurrentSheet 0 ExpandedCanvases Frame {{307, 272}, {753, 756}} ListView OutlineWidth 142 RightSidebar Sidebar SidebarWidth 138 VisibleRegion {{1, -45}, {615, 662}} Zoom 1 ZoomValues Canvas 1 1 1 doc/next-tutorial/person-student.png000066400000000000000000000573561242365656200201750ustar00rootroot00000000000000‰PNG  IHDR\jV5✠pHYs  šœÕiTXtXML:com.adobe.xmp 5 2 1 m ž@IDATxíØÔD׆çû콋Š(н" *X°+vì vE+ذ ØPQ±wš]Ä®XQ ‚ {CDAEE±~ùç>zÖ°ïî¾Û7Éžs]Ùd3“)ÏLžœœÌœùOàÅ™†€!`TÿV<ËÀ0 C@0µŽ`†@•0­Ж!`F¸Ö CÀ¨F¸UÚ²1 CÀ×ú€!`UBÀ·J@[6†€!`áZ0 C JáV hËÆ0 #\놀!`T #Ü*mÙ†€!`„k}À0 *!0g•ò±lê °O¤ðqÁ`Õ !ðŸÿü'õ/|œ:YF¸uÐÈÕ¨"¤ªÄÊþÿûŸd>Ç ý_2YµE Lªëöÿýïß/×áóµ-mur7­ΉÎEÉ•}xSâe¯[¢°Ê5@ L¨,ÿç˜cÙó_Ï…ã5H$A'ŒpÔ˜µ¨ «Äú×_9¶\°E±@¸úPVs‚îP½¬U°aaY¡±€|PíÍÂ5kVÖË:vì(ŒÞX«­¶š;ýôÓÝŒ3²^cÉDà—_~-—~ÃÛ‘š¦”„“YkçLÃMjËV¡^a —ç×_uÜHI¯^½Ü|óÍç^xáwÉ%—¸e—]Öp ]fá BàçŸN}8 Ûq±ç&Y’]»$·\ê&\5)äÒpµÈ't’;묳܈#ܼóÎë† "AŸþ¹Ûm·ÝÜ2Ë,ã6ÝtS÷ôÓOËù &¸M6ÙÄ <Øí±ÇnàÀ¢I_pÁníµ×v-[¶tgœqFê•2‡rˆ¤³Ür˹#Ž8Âqƒ#ƒ ’´‡ æÚ¶m+Ç÷ÝwŸ„ÙOõ Ÿ`VàAMßAÃE’®ááV¯%*'n Ýx%T“7Q¾2iÒ$ÑŠ•¸wÞyg÷ÔSO¹í·ßÞ}ñÅn§vrï¿ÿ¾ƒ@ÇŒãºwïîzè!ÇGÈ÷œsÎq:ur묳ŽëÛ·¯8yC¶·Ýv›[c5\óæÍÝM7ÝäŽ:ê()Ö§Ÿ~ê^|ñE!á&Mš¸±cÇJºzÃç[v‹W¼ )á†M ¥¥ý«p£ßF‘.!DŦ¤›á¢Y¶hѵjÕJêiB|o¾ù¦;î¸ãܵ×^뮿þz!ã¡C‡¦ê¿Ê*«¸wÞyÇuëÖÍ=óÌ3òJºØb‹¹³Ï>[ÈvÅWt_}õ•CcíСƒ{öÙgÝK/½äÚ´iã†î¾ûî»TZ·ß~»9r¤;ñÄåš7Þx#f•G Ù&]»U#ÜÊ÷­Dç Ú)„«¤ÛX…›6m*š'v[̧všCóD°éÎ?ÿün‡vÿü±ìùÙu×]ݪ«®*ÿ<òH·ÄK¸=zˆ&;`À7÷Üs»?üPÂÛµk'{~0GðP§¥d¿øâ‹K¼|lÏ©í døÀŠ)Aµ[Ú§×>š•Üuê7½A¤ËMÔ˜ÜÿýnÉ%—œ-š_Ïž=]×®]SaL¢P]h¡…RçW_}u7nÜ8ùð†¦z÷Ýw;>ÆA؈^>ç™ô3B„”l•p#\Ô²Í4ܲÂY‰A¶º•ró qB¨ñ—_~én¸á1Œ=:#¨Ç{¬˜$øÀvÞyç9L óÌ3c¨Ùšk®é}ôQwå•Wº~ýú‰ik˜L¢}…MûûzÓpë¡•«PG½ax5,F–Zj)±³2òû+rðÁ»Ýwß]>˜É‰Ð£ fLä½ÁȘ^4×;î¸Ãíµ×^îøã—+°ýò-鳘BðDþPû‹î#_à2ð?¾Âõñh)`–ÌßÐm™Àð†\1yRÓ§O—¡[¥t+†‡¡í¢µ6&Ó¦M“(vXÈ›-ÌH#Û0:µ=¦-øˆI›a‡§1a»ÇŸdsi¸µí{±Ï›­V ­¢U0VXa=ltŸN´zåa$„Iô(GŸ‰~-ÿ.¡n\Z*¢åT²Õ›F‰W÷-¶+"Ðot‹H‘*Z #܊›üÄ!Öt²¥Öz.ùX E ÓÃ8Ü M/Nñm”BœZ+be “j=i)k†X'tÃ})–ʳÐF¸yeÑ" Z‰’m½Ü4 ‘ˆþ¦H?øàƒ‘*hzÑþ©B–¹0F¸e´^’K×P´Þé7‘ž·}mxíµ×Ü5×\SÛB„r×~Þë÷€P´Äá&®I«_!ÈW X÷™J±ãŽ;ºx@&4¬´ÒJ®JEc¢ão¡Ð¹sg^F .»ì2·Ï>ûȨƒK/½Ôáé‹™f믿¾{õÕWSiàa­µÖ’ b•”Ï>ûL¦$gw¬aÏ?ÿ¼øj`úq³fÍÜV[m•šÂLÝÙ<ð@ÇØcÒÁƒÞ͘úÌ9%#pÁY“7˜Øqï½÷¦ª6qâD·Å[Hú8íùñÇ%,œ>S¨Ï<óLñ+¯Š£>Ú…}TÜy玉$^x¡Ã3[&É–)~cç´Ÿèžø Ó:7v}\ÃpãÚr5.77FúÍÁ;ҋˆsš‹/¾ØÝrË-Ž)¼> Hç”SN‘sJ W\q…\Ž×°‹.ºH¼‚]~ùå2¹áꫯ–É ¸pÄc‚«GÒîß¿¿ƒ@ÐæÔí£D(ÓNsp¶³Þzë¹ï¿ÿ~¶ú¦‡Îl·E]Ô1c²„T&mPv¦ï»ï¾â6òñÇ'>ø— ìLÖ@^~ùewë­·º{î¹GHw“ø"€\·Þzkqi‰oaưtÐArM8}<¥1 Âfú3+\…c<«AxÛn»­Û|óÍ%¯ð4í\ašN¡{í+´¿ë¾Ð´bßWÖÄ(¯‘ÞãSàI%˜~Ѷ±C²ÂøÀ~XI€ÿa• 4bœû<òÈ#²qŒøW{ùȇKÒâCoØ×s…iš…î©g¦²šNœâáÆ©µ"^V´ bn ´-^©yf¹^Ó½m/µdN>Õ†x•fôn9Ʊy9…•* ]HÒe}5\ "ÙÂxýGƒÄ¼Á4ÜNf’.]º™c‚€”1³°Ô oë®»®˜Xé˜vo¼±¬¹3 ÔXN(“l¸á†îÝwßu‡v˜CªàÄujN  <ð0W0j„‡€j¶¹Â2åר9ú‰ö—búLcéG5ܼ…Eµe"^.l…aoa¼òª·0\*ª¡RÝ>úHHmŒôøêÎ"“ù y’qUZ [ìÈ™„0L¬DÁ:mæu›£Ž™-l´Ø!óì‘l&†@”0ÂRk$¼,,›ãg[5¨%SW³Ù.Ã^®ÆŒ# G6H à |00œÉäoßÄŒÈ÷ae˜U#ÜÊak)ç‰+öò%?“DÍËU¦2Fý#Jð¥€íÖ¤¶áÖÿºËÇ5ÍýØTFàõ a(3¶\^®Ǭpî¹çJmÚ´qo½õ§å<ãPqã}(8œÞ¨L™2Åí¿ÿþ2Þ”™Y~¿eóš¥×eÛ3€ñ´™–p‡1 Ç0é^¾˜èÁÔY´Nâ ÞÇ„ŒAfØ×ž{î)3Ò8Ï,¯Aƒ¹Ž;ÊP1ï[BÆÊæý 8Ù0³ŒIãÆã´KOŸñÂã›qŠ£cq9ÇxeÆ3éç8™Ì0x'ËF& à;°‰!P0þÆG'ê¼æwÞ+~¸QVç58añž¨?S*ð³ÀÄŠŸÉà„Æ\’2àtůâx,ðdxï_r'M‰ç‡~Ý»wü<ÿÀ“hÊA Ž_<Ù~ì«8uñcMƒ“N:IÒôD,ÇS§N <±Þ|!e¼Ù!ð.#SuÇ) Ži²‰Kx3EàWš <ÁÔY%SõõÞ¼ÿ@ üL±À7ç<^kü¸SIÃÏö p&CŽ}pã§Ô¦Êå‰9ð$<û쳂‡ÿØà!Ìy–rø‡—àæ 4HOß“6ñ®ï:RÜÐnˆ÷Hx"–ôýRó Ö‡¼³…^¨xzç5Þ+š8/;u^ã'^š\¬â£˜#P,áB0*~zjà}4 ܰ­°—+ˆò!oÄϾ ü,+9ö³¹¯ Ê1?§.ž¶¼O!Y¼SqÃû‰BtÙ¼f¥òÄÑò ðãWSÁ¹Â ÜL^¾ D?¹CH†„¼¯ÙÁòŸeôŽ|„TÉS‚ôî?É$ðccÅÓ–†ñà’ž>$Fz`€øjxmZÚ@þøoO¼‹GyøéÑŸ¾¬A9ÃR‘ò8¨gÂ5“‚o}“ê!Àk­Š×šdvšþ×}6/W„3ø_§›2cJýàSÀ»Ô$dÚ«þaº*³ß˜€c•°×,ùPÆ:>`±Òš.D‰W/„‡ eå!–.Ly¦î`ƒà¸­™Õxà |€Ãß/o‡ø¥z Y´hïxÝy›zÖ°°¯bIÈ~r"`„› l nf•ð±žKߣÉ1’ïUŒ“mlÊjº—«ôôô?Sh½íRF?0Ì RD I"aƒèI“%~L^³$ íÿ³,‰ã?ð¸nݺÉh ][,WX&/_iIK™!XpÁí"NÔuÔqy# LM xACÓ÷ ÅC˜®ã†³pê˜.-žÇ0™@¬D‹/…ýöÛOþ3ng;䆌é-É&Šø¡¯¤o$“O*"»È\bÞÂ"Óñ*ˆÿ62›·0Vj@›Äd°Ûn»åô† Ñê ÝXÍÓ½\e‹­–‰ha³ñÉ“Woœâ`fÀ‰¸ÚPÓ½feK?ý<éd3qFsyùJO2R.ÈUÓeAMlÓ¬U¦«a„¯cl-Z-¶íÆØ6ž¹4}l³úÐh¬.ZžpÞÅCªhþÙ¼…©¾Øô£|Ùp£Ü:1(›j$Ü$º5Vìe—]¶±(³…«—«ÙNføƒùA—ßIç y@¶Ù¼f¥_›í. ‡eóò•ž.e —3ÎÃBmµáóØlÕn>ŸéXÉöÆoçìaß¹áò¦_›+,=n¾ÿISû‹jºù^çx¦áƹõjXvÕpñ:…m;hPØKYÝÄÈ…«,óÐÃ>Ì[ÞJðçkn.ä,¬nPÍ„=7 &ÚèðáÃe¸¯À|¥gè¦^ûã$[’Å7É­[áºé E6-›žãæ‚ØÔn ÑÂ]ýõezlïÞ½eÖY.˜æ†/3'äB)s˜¨j˜xyÐÑG b%ÛÌ©$ë¬n²Ú³ªµÑнÞ4újÈÄM¥Z­.TM¸ª… e†ç0ïvÐ1%·±ÙnŒ1Î'^(y;üpÿ _¤oø?áÄOºá&½…+\?n½YôãFB{Áf«š¯î+\œ¼’g†ï™.ËÒ3Þ¡yÖ› Ìc=6“ÂPÕ>Â^·ðCZûNá9Äë #ÜxµWäJ«7 „Ê17“š ”dÙ«„õ\-ö8mÁGÁ!ޱͳÏ>+Ž`ÐdÓÅ;ú¯\é¾ÒãÙÿÌ(áªýƒc%`=ŽÇ¹¤ŠnR[¶Êõâ†Ñ›†›)*Äš ÖU{î¹çÄ7,Äð”‡'àxã#IyÐ~¢ûò¤TŒpãÑN±*e˜|£^p~5q·ÈG4¿»ì²Ëä :eÿúë¯e¥â™¥"`½¨TíúX#€;É.]ºˆ»@|õ¢Ñ2ñáí·ß–zá21NCÂ>õþz7lMŒp£Ù.Vª*!àJÂÝj«­dÄŽwpŒ¾Å[È23q#\Œ¼8üðÃ]j§J[6!Ì=c ;¬/®¾újwë­·:¿°ŒªמEù°†O4ƸŒR`!É:Èøg†æ1æøÌ3Ït;í´“|¨ ×ÑŽ«€nõ1·#€&´Ø1cÆÈ7™ŠÄÄ F0°ŽYœd£6rãÆK™‡³ºXVè¸ãŽk°F*¢T#ÜŠClD œ¦c§ÅtÀZhI,L©ë—iýÚ†ÖˤFh˜T³áVs˱Æ é±R|öýû÷—µæöÞ{oyçzŹìöÑ,έgeÏf±r”pšR¯òÞ{ï |04©>F¸ÕÇÜr¬2Œ¥Ýa‡äë|³fͪœ{ô²Ãå$+. }þkR̤Pœ-—!ÀÐ(Vt¸æškœ‘íß dË¿‡~ؽóÎ;5júËÖ·þÚ¼®jÌ@&8ìµ×^uUï|+»Þzë‰óž{î¹'ßK,^ ˜·°À³K£À]wÝåF-À¢]ÒÚ•Žñº,Ï*8î±ac•m Óp+‹¯¥^#>ûì3‘€34ÉŽ@Ë–-žÿ“Ê"`Í*‹¯¥^XÚ·:ur=zô¨A â™%CçÔ)8ÖóhŽJµ i¸•BÖҭ矾hµ'Ÿ|rÍÊÇŒ•l);#úõëÇjDºÌ¦áFºy¬p…"ðâ‹/º=÷ÜÓ½ñÆ©™V…¦añÿ^Y¯cM›6u·Ýv›[xá… –2 `n@´$¢ÀŒ3ÜuAÈh”2¥@ÛåƒãòË/ï°ñ2^פtLÃ-CK!"0Þ¶I“&nàÀ)Q2ŠÁhM7ÝT–‹OFjW #ÜÚao9—[n¹Elޝ¾úª­°[F\5©?ÿüS|éòÿ÷ßwsÏ=·Ù¾̤PX5š|ðÁâLüÎ;ï4²­Pá¸\åÔSOu6„LÑ(lo„[^;b`[Ä”pÁ¸µÖZ+b¥Kfq:vì(×xàdV°‚µ2“BÁµ¤+Ú°FŒQùÌ,‡˜n Â"œ[n¹eê¼äFàß÷„Üñ,ÔˆLI:t¨›0aBäÊ–ô±&ÜøñãÝ“O>™ôª–µ~¦á–NK¬ZLŸ>ÝáxeÈ!¦aU ôFò¡M–\rÉFbÕw°Ùpë»ýc[ûC9D´×Ùè4á£>êE°$¦áF°Q¬H¹¸êª«Üí·ßî˜UfγscUÍP&ž´nÝZl»,bž*\ÍrD9/#Ü(·Ž•­o½õ–,>vìX×¢E‹áv¢¶|÷ÝwŽuÓ6Ûl3wî¹çÖ¶0ÌÝL l+Rf~ýõW†S#ÛÌÕúìâ‹/îüq›•–¥!LÃÍŒŽ,õýý÷ßËÈ„è•ÎJ” ~ø!ñËÒg«{úy–Žˆý$#GŽ”õ·&NœÉòY¡²#€ç¶Ÿ~úÉí¼óÎÙ#ÕIˆ™ꤡã\Í)S¦¸#<Ò 6ÌÜư!7Ø`wÄGˆ›Ç¿¬E6 ·¬pZbåF€U:wî,Ëål²É&åNÞÒ«øÒ}þùçݶÛn+¦ÖO«W1 ·^[>&õ¾ôÒKžªzõê“[13!°Új«¹—_~Ù=ýôÓ™‚ëæœ}4«›¦Ž_E™¯£”×^{Í5kÖ,~°7@à矮ëE=MÃmÐ%ìD˜9s¦Ûo¿ýÜ5×\cd…)Sê}e#Ü2u$K¦¼tïÞ]|$à‘Ê$y éb›¿þúë“W¹5²f9À± Ú €#ñ—^zI‚¬M ,×J#€¦Û¿×¾}{÷Ûo¿¹ãŽ;®ÒYF"}#ÜH4ƒBøì³Ïp½8ÿüóëiÛ'¥–ZJÚ™õÒ[l1ÑxXÍÙªdÍfƒÃþÔ¿þúKæàïµ×^îä“O®eQ,ï*"Àª_}õ•¸vLúCÖ·Š˲Ê@ï޽ݘ1cÜc=fž¦rCe¡1EÀL 1m¸¤û…^(L5·~Ik]«"`£ Û× ü¨xàîæ›ovMš4©Y9,ãÚ#зo_×§OŸÚ¤B%0“B…€µdóGÿ©M›6u Èÿ"‹™H~ùåתU+‡yiÿý÷O\pפñªÐM7ÝäèÆçæ™gžxÞJ[Xtë­·vÌ4lÞ¼yEò¨U¢F¸µBÞòuï¿ÿ¾k×®Ã~»æšk"†@ ÈvÒ¤IbjJ’Mß7ÕÄvPM Ô¶m[wôÑG»®]»V3kË˨F¸5ƒ¾¾3îÙ³§ûè£Üý÷ß_ß@Xíë VWÍÊ>ùä“nøðáÎVoˆF{D¹,©Ä„˜%—\2ÊÅÌ»l6,,o¨,b9˜6mš;øàƒÝàÁƒ š¹À=¾t¿øâ‹\Ñbf&…Ø4U2 ºÓN;¹õ×_ß]tÑEɨբâœqÆŽ…( Tñ¼*n¥¶ôS\yå•îŽ;îO`sÎiÖ¬0véÓ§‹–ûæ›oÊx휑#h„ñJJñ¸Y¶ÜrKo»òÊ+'¥ZV*!ÀšhØréCq#Ü8·^LÊ>kÖ,׺ukY— §Ó&†@½"`„[¯-_ÅzwëÖMlpC† ©b®–•!=̽6IT‰FŒ!îñfbÔ;6,¬Þ{@ë?eÊwÔQG¹¡C‡º…^¸‚9YÒõ€Žm6Ûl3÷ûï¿Ç¶ºF¸±mºhüÿûŸ,™rüñÇ»7Þ8Ú…µÒÅVƒàÃÙèÑ£cQÞL…4ÂÍ„Š+K/½TnÆPšåB/b£F*WrUOÇ·ê'?ÃW^yÅõë×Ïñ‘ì¿ÿµ.–ü¯^ ãN¸6J¡z}¥.rš9s¦Ì$»ì²Ë\§Nê¢ÎVÉê"ðÓO?¹…Z¨º™–)7#Ü2iÉü~p$~ýõ×$†€!†€ KÄþÀXuwüøñÅ'bW FÀ4Ü7n5«öé§Ÿº6mÚÈ œÓ˜†@Cì‹FCLìL0Tg¿ýösgu–Øo ¼Ü¢!À÷ÓO?½ k¢Ù7*-ãrœ{î¹n±Ås'œpBŒkaE L¢Á1yÅl¸qlµ•™ o¸áY½!I‹ýEb+J‹,²ˆøæH;‹¿fÃE3E³3fÌpë®»®î;ìÍBZ©‡À?þèð‘G7ŸF¸‰ëŽÕ«Ð^{íåš5kæú÷ï_½L-'C ƘI!ÆWË¢ßxãîÃ?”Ùdµ,‡åmÄ ÓpãÔZ)ë{ï½çÚ·o/NDÖXcˆ”ÊŠaD¥ý6ŠT qÇ04²TÓXab€€i¸1h¤(±Gî“O>q÷Ýw_”Šee1b€ÙpcÑLÑ(äã?îîºë.Y) x!`n¼Ú«f¥6mš[o½õþ¶Øb‹š•Ã26⌀ÙpãÜzU,ûAä?üp#Û*bnY•†À¯¿þZZ¸Ú· &-É+®¸B¦R2…פ¾xûí·ß|3Ô¤I“ò^¶x;ï¼³»òÊ+Ë–^¹2“B¹Lh:'NtxÙ;vl,gö$´YjV-Öªûí·ßÜ|óÍ—WÎ9ç·É&›¸wÜ1¯øåŠÄ È7ß|³\É•-ÓpËeòš5k–ÛgŸ}n§Q&¯Ej_£?þØõéÓÇ}ðÁîÀ”U=˜Ö̓2f•æ=öØC¼yMž<ÙÝsÏ=2„á„çwžëر£Ûe—]ÜgŸ}–1 jxùå—»Í7ßÜ¡¥þüóÏîË/¿tÇ{¬7y§ËÀݶÛnë˜ùøÑG¹ë®»Î}õÕWnJZûÿ‰!®]»;wÎj§ë·Þz+èÒ¥KÀÞû=¼¶<óÌ3'ÚÀ¯exXü°ÁÀ“^pöÙg<òˆÄ»êª«$Ìt ¼æ›1 ?ä0Øm·Ý$þ< מtÒIwlxÒúh*øé§Ÿvß}÷L˜Ùn»íœºòLOƒÙ‹˜Ç{LVY{íµNLJêºwï.ê4aÂ0o L9‡ì£,¦áF¹ujP6ìpÞŒàN<ñD×¶mÛ”À²Œ+Ë.»¬Øü¯ ùys‚Ø`™>xð`!âcŽ9Æ¡áÎ÷tÙtÓMÅÝ'ÓÇ!èûï¿_%eH"~p韻îºkê²víÚÉh„8Àá¶ñüóÏO…EñÀF)D±UjX¦‹/¾X^ÓÐHxM31 E€V¯ _ÕðǸ™3gÊê ì\pAÒ`ŸÑòæ…;ÐL‚IƒUG¢.!\o¤NÕ;|œ:i‘D`ܸqòùõ×_wM›6d«Q(^wõ•·ùYõƒ@YL jú„z. §pèÿôÓOnÿý÷wþK²[j©¥ÃxêE”\uO½•tuoÚ~½ô†ÊÖ³$ W •=*?[øXÿS[ÙêXêÅ"pôÑGËk_=®Þ ¤ª„Ëž9,›†ëÿb1¶ë ¢ W ”=_$!Wö¹ì2·!7°%B¾l¯‘nÜZ0Zå-ɤ d ÑþùçŸb§zœ71âŽË4Ö9çœÓÍ5×\²WEƒ0C PŠ&\íxJ¶Øüò±ûñ“λä’KZV‹oTWEI–½k˜í |(jÜj°jF`¸dËðlr뭷ʸN¾~7iÒÄ1ÞŽÍ*—\r‰cvS¡óºÇ\ëRdĈ®oß¾¥$a×&ú4nþxƒC¹PE#Uµ*U¢—rA¶aÂ¥S2 9“¼ÿþûâK•°»ï¾[œJøùÐâlbÆŒrÉ™gž)3I2]ߨ9-Kcñr…3«êŒ3ÎÈÅÂêú4Z.J…‘ní:m¾þúëÚ L9M¸<éé€j»EÃÍF¸ï¼óŽs§NÜž{îé9äwóÍ7»#Ž8Bf“ðÒôÎ'\ïÞ½w‚!ž AÂÆÅ›zÿaþ4ó¶×\sM!ð0Ÿþ¹ÌÁ^f™eD‹f¿ Þ„¼s ¹¯¶ÚjŽÙ),õMºÏ=÷œDÃ\>¦MÓöÉF 7L¸ôS}ÃKvÍ£U;ÖÒãÍ8îR´ W+ÎkO:eØÞ¥áì™êLJ|cŽ=Z\´á¦Í{ù‘h-Z´=óö™IòÃ?8ï(5õ‡üÇ©š4îßÈ«C‡®W¯^r-?Ü ¸tc.6äúì³ÏºvÚI°@°Ìñ~ðÁÝÒK/íš7oî† æ–Xb ÇÜî…^X\º1—Ûìs)HëþÂgžy¤³W“{ë'uß=  ( —ΦOzöªåf#\|©ÞqÇ2{éá‡{+$«žã™sÍp›–-[ŠÖ›«hÁ܃ Ç\«‚“lœwÜqîÚk¯u×_½4N/T¶æ]Ë ñ¯¸âŠÎ»Ž“ÙUêô‚q¨|Ô31@€·  6í÷†Ž!P E®fDçc£#Bº¹^ÅqFÁòÚö~饗4¹¬ûð+ܨQ£$ž.d¸ûî»§®ûôÓOå˜póÏ?¿hÄœÀi² ä 9„¹×·‰! 0ájÏ×Ηð¿ð srKùs­\ŠE®’ {6%ÝLEeimïXœ¢àNíä“OwkÄ »pÓkçw^9ÔEàX1VEW`«E†ôÅ_\ÎõìÙS\¶á¶íÒK/•óü0¢ÁÄÈ Õlõˆ×¤:`ä­ÿ¸I¢ —ÊC´úÔWM7(«¬²ŠØOñ±Š7*–ÝàòñÆË“$ AboEn¿ýv1 „‡|©k¶³Î:KF5àœX¥U«Vâ¿—nxºá†\›6mÄ| q²íÉñÞëí£H6êð¼’­ösU4ꊚT™¡¢á·Øš¢Œ™–D¸árÐ!³uFˆð¶Ûns+­´’|8Û{ï½Z,öR>j!Øs±¿â±Zø¿Ägf·nÝ\‡$?h¸Ç¼Œd`M¥uÖY'†ÓF6p=× ÙÒXù4D¾Ürˉ§zÕ¬S ÛAÝ"@¿F²õíº¦ çí‚o>ºòC²¬xEùR b[˜ð2ŨœóÍ72ÍÕ9‰Ë¨È-]t´ƒúcÀ¡0_‚3­:@^ä“m¨Ú2×ê#3.~5Ó±³ÿåG€¾Ç¸qäÌŒÄö , Ê#ô­¨ü9[Š 0}út÷î»ïºÍ6Û,1€”4,  où B§eË$Œ`¨V6A‹Í%+¬°B®à¬a…tÖ„, Q m¡h@Âl&•G€‡\’ÈÄJ"\à)¯­vDÝnbÄíËú±Uûzœëde¯E.‘ÎÇGí”Úu_»jYΆ@é„ûµÙrKÇÓRð j± @ªé6¬ôÿŦm×QAÒÕ·¸0ëqTÊ™´r0´óºë®KZµŠ'\:\X“å%;!þ“g’<èGW^y¥ÌèŠjí ]³áV§uø ¯¦é'MŠÖpõKáú9 +Çþµ×^s×\sM9’²4"†ý†¡~Ù¦†×²¸< ({ íãae£–åKbÞ¸m]wÝu¹iÑ„KCkçã8—9aâĉ²@!“˜c™ &8¦çâÏ€©¾*x à ~XÏ™4i’L”`0~HïÐCuLáeª.žÃHaD6Æè^pÁr®Þ~˜®Ì¤¥K8Œ±Êƒ¬™lrï½÷Jtð?´Nâ O<ñ„Œyf„ ßt†_Ÿ>}įΈêwâ‰'Ê4o®aøá;½å–[:VFÒÓÇÂé!C†¸Ã;Lþóóí·ßŠe¦3a†oéB¹³…¥Ç-ô?$KßV²­ä[\¡eKjü§žzʱÆ^"Å?© ßé?7ðcoï¿ ðdxÏ\ïø¬­Ó =O”ÿÊx׈w0øé½ò%ñzè¡ÀwâÀ¹ üòÜÁ†n¼ýöÛ¿)ï¬A[œrÊ)÷€x‚ ¼[ÌTu3…ùé’ÁÖ[oøÙ}Ÿ~x‚µ›üXhIÃ;÷ ü˜f ó¤øiÖA×®]ïÆRÒöÄx–ö÷~2ï23ðÎÜSa~Ò‰”Ã{f“vñ¤§ï}]H¿¡}öÙgøñÐ’†Ÿ0x"–ô½ƒ¡À“wàÄR­}/[˜Æ)fO_öD.ykÙèóþ¡p˜…"ÐóHÎF§ã¦àñg£„ËͬÔk©ŸÙ•Ê bå&ônåæ'€³mÛ¶÷U+ñ¼Ç¯À»g”cRñc#åÿ‹/¾¬ºêªr ázw‹r\/?Þço± !?XÌB“þcšhoá/ØåÊ7 é ™²„‘ÿ8&Ö©¡£r…ák-ÅhÈ™Þ*YÁBà´ ^ØÂn11馦…ÓN;MÌ=Œ¥daεÖZK6ÜlêÊa̹Á¶ß~{-±"-ŽKp\ôë×O“!9Ø Õ#OÅý÷ß_Ö{¿ nÔ¨QRÏ|½’áE ¯V=zôOV[mµ•Œ_%¦ÃâiK¥oß¾2•”ÿÏ?ÿ¼óN[d˜Ð>ûì#C¤4{Ƈ²ü»z5ãØëäBýT^}õUǰ*ïë˜Õêé‚öLN`l¦Ù:F=²Õ?ÝÛéðq‘ ´öUöÇtâLÞÆ(0m78<…1¶Rg´…Óg,m/Žg<¼ØÑÕS]1éÚ5õ‹@UÆáâ!—·©ðÀ{Ï£2“ùøóÎ;¯<ü˜¡Ã˜Ðp¼;BçÉVœb‡ŸŽùx%#>ÓUU $O4%Õ6Ãé1à aÒ¯ü'Ÿ|²h«Ì¨ClLš7ÿ×ëVzܰ¦†÷+|0uaVÚ].Áa7Ú$åÂBXr…/WýÃÞÖh¦ýª Å«—0Îét^Ž©+þ/˜-ˆo‡ð¤Þp&ބӗÿü`~ð/~h4Ì(0ϰa®ÀÔ€ósÞjr…Ñ®&†@T¨JolÌÛT¦›²aZ¯Ä+&ÿ8àyEåÆçu7,ùÎ: {¸"ÌøàF‡T0) ¼*3OfJ*Ø •ô´ý³^’`j-¦ʆYãá‡Ι<þ 0‹à ·ŒLUÉFœlõ×ëuŸÞvLÓå#‡ŠÚfùÉ‚!uÁ¶®uaŽjc¢®1Å`' Ó„™òë ‹}ÂÕ¾“+,œ†µD *„Û˜·©L0{ W§ûa7ÄG*öG4%Òäfã,FüªŽ ! ÿJ.çÈÛ’M-›`Ä‚ãü`'ž‰-8]ry5c9!>\¡5B¼Ã‡O]·;l·äˆ;¼ôP*bè€2 ß~ûíŽrhŸ¸+Ķ+Œ$²Õ?”¼‚3øP&~…±§j¼LÞÆÐ¶±ëà {z‡Roz-ût¼xsð«LÈÆ1ÂiÒâAÇ6ù\ar±ýD<Ìññ»Þ¤*&…°·©V­ZÉG(u p¾ˆ£ÍrƒñÚéò1‡¯Þ£Ð:ñ$ÆÇ,Îç+¼þ¢A¡1C&úŠõºÐ Ç<"ä»ÓN;É×z^±ýjwréöjÆÇ¼°&éAîó__³©ã.»ì"ȘWë|—‰Æ\é¢ñ3̆5Âp]ˆ¶Ÿ)Œr“¦ú‡ËË1EÐViC·°g䈊zããgØÛ17´hÑB^ûy;É$a¼ð.©ò`¢½Õœ@Àœ7 òK®°p<;Ž(ܯ|@¯7©ª·°LÞ¦œWyÈ-lçåÒâ¦Äƒ¤Éë´®PÐXš¼®B:,V‡¶‰&­ö\®…ð™ž'aÄÇF if“Ƽša¾àa]8,hÚ Ó‰%¯±cðÈfZ! SE®ú§§Ï5˜RÀ:Œ ¶SFÐF™¼Ñn„ƒU®q–éxñàa¸Z:ÒX}²Õ5½…þ§Oð†eÞ E.w|ÿaZú‘Ž Ê;Y¡UÑp24F¶B„5|“ëµát¸áò%[½ž=& ]µ ý|¦<‰ƒfؘ4æÕŒ2 7x)D«iæ" pX¶úk:ºçš\ð²aÅ[[c¢x¡•óæƒܰ†p™ÓÓÊ–×þ×Þ&þé­}ajP‚ªØpkP¯œY2ÞóàƒÎ'Éåª9¼…qæÃ7¤bØ@ûdzãø"ÀT¾Å¨¹(¾5)®äU5)WD»Ê¨ fR¨ îIε.5Ü$7¨ÕÍ0¢‹€ntÛÆJf CÀ7a jÕ1¢ˆ#pX¬”¢õ,F¸õÜúVwC Jà-éù¹†SV©(5ÍÆ·¦ð[æ†@} pË-·ÈìÉú¨möZáfÇÆB C  à·ƒ 0aßÏeH6–ITuâC,²B†@I,´ÐBŽ)à&~²•`†€!PÊB¸ê‡@÷Õ)ºåbTí׺¯N®–K(‰pÕi±:é@¸JººOcÿgG€%¬Ìœð/&En¦Îùf:ÿoVvdÄU*¬oÞvŒ½e­B<ã™ü@Q„ލ›vÎp¸qF€>î×Ú×ã\§j•'Dx‹cÕ“¿(z”‚v<ÜãÑ!Ùã½!ÌÄHôiíÛôsëÛù·*+…à Þä_J"\MF;$~MñuÉÒ38gpùÃíN¦q$m’^¿À‹!4&ÕE"Åï2 –2 Gë8T_l±Åä<¤« %3Òm¼}pâÏÊ#,ooò/E.I@´a²e6¿¼µÓiµ3óŸF€XLfG\xPqƒwìØqö@ûWqèÃ( .}—•D ^6Îé²ÑŸ#ÝÜÍN¬‡g2;E®>íÙ+áÒ1é J¶aœG»e3Â|þ 1²8%K¹@º&ÕC@û0}U—¾ŒÖËyH—¸‰’nõJi9%¢W+OÇ ktLD8Og¥Ó²ææ´[Óp½¿×êb¡HVž–ÞtMª‹ýíUµ\%]ŽUÃUÒ…xM b(špõi¯r…LUc€|Y.Ís„‚6gò7àrñÅË¢‘sÓ³ìÈÒK/mUUÂZ®ÚsQÔ´ ñŒps7н÷ÞëX¨•U¶MfG hÂ¥óA°ªáªæJg„„é°h¶éd«ñf/F}ý—=zÈJ¸#˜^ŒpkÓÂʃög5/ÐÃv\ú=ñt³·D«+.gUŸ!E®v6}âj·œSÂ…l•p^#\çzõêåÞÿ}y ).`Ö¬Y3$®€T‚•=³IDATq¯ª}WI‚šh6%Ü*/VYýøã²èàÁƒcUîj¶(ÂÕÂÑQéœÇÚYÕ„ ¶[5'á:÷ðË֤8²K—!I&ÕEEè»Ú§!Wþ‡÷JÌÕ-]¼r5j”Û{ï½­gi¶¢VíÕ´ O%RöJ®ìÙ çØÈœŒQ0`€ãƒ71ã”ùHÃÌœ•VZéïHö[UÂd ëéªV«{âšÅ P²†K'Tá‚E+€\•h¡¿÷Ë,³ŒÛ|óÍÝ#<â<ð@׿‡“f4\l†&µA"UR¥Ú·Ó÷µ)åšJÒpÃ(©¦ïÃqìøoøP¶Î:븫®ºÊm·Ýv¢áNœ8Ñm¸á†QH×`•p#P4+BÌ(áÆ‡ªÿ’K.q/½ô’1bDUóµÌ J"€ÒÀ”èW\±’ÙÄ:m#Ü4ã“ùȘ®IÕ (–eÌÐ7Ȩ›ra*;v¬kÞ¼ydе{¬$ndPYAÌN³«Bq3©žÓ}ŠQt¯¾úª[b‰%Ü +¬0Û‡ò¢¬À…éäþ>®@Ö©$pSPØ!P„ÊGgÙÃyÝÂ% ÇŸ¯åñC=$ß#t"O-ËÎ;‘ê‡QÂtÓk2Å×°rìpËbi0L®_¿~n×]wu«¯¾zWX”z@@ •½’múÂ$E\˜½ûî»»Y³fE±x³8T!Ýð¦ç(|%I×l¸Uê]t‘7nœ{ઔ£euÂd«c×Ù›Oäêµ3ãÆÊ¦¤[ÉQ)¦áV¡m¿øâ ÷ÿ÷nüøñUÈͲˆ®’-Óàõ•œó&•E‚Åq”b­3 y›¨éáV¶M%õ .¸ÀsÎ96\¦ XÇ%‹°vË ÎÏÆ¬C“ê!Àˆ!œ!0Ä[)1­²¡t™Âkb¤# Ú­:yâæg3©¿üò‹h¸jN çJi·¤m„ &†@ €pÑn!\Õn!“ê!ÞlØQ‘¾}@Âå#Ür#jéy" 766\5)Dõ+žUŠ]4ðæƒ™®ê¡~`*U‘=ÏT*‡:M—›¨gÏžîÓO?­S¬Ú¹P²EÃU.ö[³áæB­üaà­;îYÚ‚¶©”áVYì¶“&MrÍ#4ͱBUµdK@€›[ot5+”œ]Z ØÌÁ]Û@ ·R¤k&…(Ÿè<5wkÎiòA«~ãpSë Î ¯¶ÜúE¤ú5‡lõ£¥¶E¥È–Ú™†[6¾õÖ[]»ví\ëÖ­+º%™¸±•t¹ÙÙ¸ùMª‡x«v[I¢Õá*eÜwíÚÕÝu×]eLÑ’J2JºÜøl&ÕC@É–õXIâ5­^ÛZN†@ ½©Ã79Çh¹&ÕC@ß,Øk[»¶O¹Kb„[nD-=C Â7vø¸€$,jŒ0Â-ccñÅóþûï¯ØÓ±ŒEµ¤ˆýoƒ 6p¬(RŒtìØ15½•Aÿ«­¶š;ýôÓeÑÓbÒ³k"`„Û“¢Ï 2ÄÝyçÒi‹NÄ.4 D`ÆŒâú³C‡î7Þpß~ûm)̽W¯^îüóÏ—¤!ïÛn»möö¯hŒp‹†®á…8aÞyçØC D ¾M6ÙÄÝwß}®mÛ¶®M›6îñÇ—T¿ÿþ{yÐç3-øÞ{ïGJ\“MN:é$wÖYgɰFV'A‘@>ÿüs·Ûn»ÉR:›nº©{úé§åü„ ¤lƒv{챇8p øÅÅiÓÚk¯íZ¶léÎ8ãŒÔ›+TrÈ!’ÎrË-çŽ8â÷óÏ?KZƒ r¤=lØ0©'ÇÔ9)b„[¦–dìí¨Q£¯e&†@¹øøãݘ1c\·nÝÜ|óÍç^{í5qøëÏ•VZIÖƒ¤ü1£½æ"\Mƒ‰;˜)°-óQ eâ©§žrÛo¿½#ßvÚɽÿþû¥lÝ»ww(L…|ñשS'Y¡ºoß¾©qé-Zók¬!ƒnºé&wÔQGI¶ÌÌ|ñÅ…„›4i"õ"ݤ|L4ÂÕÞUâþË/¿”'ûâ‹/^bJv¹!´Íçž{N&Öè7ƒì±†}ôÑîöÛowK/½tÃÀΠA·hѵjÕJÎ@š,ùæ›oºãŽ;Î]{íµîúë¯2:th*UVYŽóÎ;òPxæ™gÄ´¶Øb‹¹³Ï>[È–Õ|¿úê+ÑX1<ûì³²z5ÚúðáÃÝwß}—J‹2Ž9Òxâ‰r ¦’$ˆn™Zqå•W–פ2%gÉØxãåüV[m%{ô…Hûöí]—.]Ü‚ .˜õ²¦M›Šæy 'ˆÙà´ÓNKùÁ´Ár:;ì°ƒ\æ­ÂòQ«®ºªü=òÈ#eQÉ=zˆ&;`Àñ;ûá‡J8ƒT0• Á†ÓR²W&s‰¦å½MírëXÙ 4X5»¦×RK-•£ô¿Œ´YrÉ%gKH‰‡LLìQ¸µ,á¥X·%¥^xáÑTï¾ûnÇÇ8A¡×އó¬¤OZ-{-ö¦áÖuËÓ(>}ú8^ãù …l¹å–¥Äë=¯ð…jÆhœ*d̵7Üpƒ¤3zôèŒù{ì±b’Xf™eÜyçç0-Ì3Ï<2ÔlÍ5×t>ú¨»òÊ+et¦>¬arHºá–©…¹JŽS¦¢X2 F`‘E“«IP¨¿>JñÁ­P7hÒØY±ýb½ôÒKå£+õfF%`ÓÅôÀH>ì1¦ÍõŽ;îsÃñÇï09`Âà#Z%~g*[-ÏÙª½eB._pÙ›!À~}ºÛsÏ=SC¨4^ãùPE<È’u¸^xa ®êžáah»h­É´iÓ$Jºéƒ‘Øl!àæÞ…i-È–<1uP6L&<Ì0‘0 |+±¶™Ùpë1nD °­³E[a…òÎ6hõBÈŽ‘õ&F¸õÖâVßX"À˜W^籃šÄ#Ü2µƒÉk­y”©*–L`È›I¼0Â-SûxàeJÉ’1 ¤"`£’Ú²V/CÀˆF¸‘k+!`$#Ü2µ,CeN9å”2¥fɆ@0Â-S«2T†@ H71 C F¸™P)òÜ~ûí'n銼Ü.3 „#`„[ÆÞÿýÅqr“´¤ C Aá–ИLOüý÷ßS)àä‰'ž(xžz*;¨t*«î©xø¸n€¨qEÁ\·pTª-"I¸W_}µ»ùæ›ón –å`^z5…9ïë®»®x§ç‹“› FÄŽs! 7»î“ê–0µ SÜ)C¥H6\¿H.΋q’œ¯àÅèÏ?ÿÌ7zYâ}ôÑG²ŽS¡îñÊ’¹%’ô†‡hÙ*á0%1`U "à­ØWƒp#9ÓŒ…îÐñ¤ÄúFŸ|ò‰,8ׯ_?Y? 7o³fÍ’íš5kæÞ}÷]×»wowñÅ;>\áM OJ,ÓÁÈô4Xs ÷q'N÷vx£g ¿c”k1í²Ë.©æeÍ&ÒÇùóZk­%><‰‹›»—_~Yˆ7Ùà/P,3mbdC@opöÜôôK6Dò]kç˃x§“.mQ)‰$á²¶.Ò U:Þc=æÎ=÷\Y¥2cy´àk®¹Æm³Í6‡Æ89†,?üpñÁ‰dÖ~úᇤ!3Œ ÓéN:UÖ]¹2y’&‹Aª¶Áßžþ…^(ΓYÛ‰%ÑY$]ð©€W~Ì"¦§£cÿú¶’­î\sÍ%ýŠ{€åÏgΜ)Šë—a6í#ßLòC@ñW”¸X@\0âŠ÷’¸a„tÁ_ãæ—rq±"I¸áªl¸á†òM–•F;wî,K|ÜrË- ÖÄ{î¹Gˆîƒ>UE¹8= ¼Ô£Å"J¸o¿ý¶8çhˆZ—a9jõ°ß¡Cw×]w9F$d®¿üòËkB¡+qg‹oçëLdËOßacÝ05“Ñ þáÞWÀO ElÙ8æ<›jºÚ.…ç’ß‘'Üt²b9æ[o½U: Ë5ï½÷Þ©š¢±n»í¶nß}÷ÏJ€‡¤§ÁòÌØ`Ù_vÙeb/FƒÕHIGÉ–ëñXã¿“•K7Úh#NçV:½êª«Äiô1Ç“3®Ö/ôQú'7¼’ZäŠ&Kd‹vk„[x?_}{Їøê¦ÎÆÁžxªé*wžcî+"O¸éÅçÉ¡bF` $^ ÖYg1  }öïß_LtÔ|PÂÒÓ8è ƒkݳä4¶^Ö]"Í=öØCl¯éf€8À±¼4ñéô˜2&Mš”žlƒÿ=ôCsF+I'ý‘íDÝ!&%[%UV±Ur`ø!ýˆó&ù#ÆlÜ`Ìà ’SÕr9Gq”póÏ¡°˜±\b‡N‰ ‡Ì*Ø^’%HpÞyçûW®å ±…ÃùÐE:ÙüÚbÒÈgY-“í l ½Bžôe6¾+`§¥ÿñ±˜=ÿ9Önp³!šý¼j®á·øA‰—c64`¸£’ÄKÂÍ­…ñAòDk “.š,D˲%,ü± ¢¶f…µ±¾I@¤Jº«š”hÑr WÂ-,—übÇΤ_µ¢ë믿v#FŒm,¾gb€@˜ø‘ê951(!CÎF¶ Tœ¨– ™*ñª)A‰²­´IÁ·¸ö+èª&Mšˆ©ƒ%£ªFÚ À ®¢d !@j³U²Õ½Æ·}~€«nà­¤«Ä«ÿ+M¶”ÖL ùµYɱÐNX°e˖.*9=K ¨y@É’eã<çô¼ÆKF­«_ ч{ˆ’UÖp+”ùÇ·Ì€æJŽì|°Œnݺu®¨VG(™B®*J¶áÿzlûÂPeŸi#Eˆ·Òb„[i„-}C ”|õ’ôÿzÞö¥!&àÒR*ìj#ÜÂð²Ø†€!`@åu袋–ì /ŒSCÀ¨ŒpkÔÖ|…îСƒëÛ·oJ`Ù†@µ°ñIÕFüŸü˜åÂÔ_V‰À§Ãî»ï^£’X¶†€!P-̆[-¤³äóÆoˆ+H\P®·ÞzYbÙiCÀHF¸IhE«ƒ!`Ä³áÆ¢™¬†€!Œp#ÔŠŒ¹ ~PѬ(†€!PŒpËb¹’€pq¨Îºl6à½\¨Z:†@t0ÂN[ÈÔBVøê«¯ÜI'¡’YQ C á–Å2¦oΑ#GºqãÆ9V61 ä `£’Ó–VCÀˆ8¦áF¼¬x†€!Œp#Þ–,±2mÚ4Yr%âEµâ†@#á6P­ƒYâ½÷ÞsM›6ucÇŽ­uq,CÀ(#ÜÀ«Ö¥›nº©»ãŽ;Ü®»îêÞ|óÍjekù†@™0Â-3 •JŽõІ âzöìé&Ož\©l,]CÀ¨ 6J¡‚àZÒ†€!`„0 7Œ††€!PAÌnÁ­dÒŸ|ò‰ûæ›o‹Q»ìºNN߇˭aásvœ]/+KÏ¥ïÃqì8ù˜I!Æm|òÉ'»—_~Ù=õÔSnÈ»&¨n\¤KqsÌùðΙ‡äÊJ°a’Õ•a9§[q©ÛUqDÀ7Ž­*óGáÐvyäÇ´àÆD‰V‰rý믿„d9“-qÝ7–¶…»ÙÈ<”t!Z¶9æ˜CöJÄFºõÕkŒpcÞÞá5×\ãV_}u·å–[ÊÍœ«JJª¬n /¼p®K,¬Œ°x(¤«›q³°¤"Œ€n„§ÜES­–Ùk-û?þøÃ-¹ä’¦Å–ì é¡ÍNŸ>ݱ€(vwH—½j».±S CÀ>š%¬AsU “îï¿ÿî~ûí·\—XX™øõ×_S7XÛ„c“ä#`ÃÂÔÆh¬Ã† s/¼ðBÆZqs«¸í/¿ü’1®¬ à îú–A{Ð.&õ€n‚Ú™×Sf¤uëÖÍ]|ñÅkÆÍ­dË?kÖ¬ŒñìdeopWÒ5²­ ÎQMÕL Qm™"˵Øb‹¹'Ÿ|Òµk×Î-¾øâ®k×®’7¶ŽHPû-¯·¦á t‘—7Fì¸ú¶¡#̬P$¨1ºÌ4Ü5V¾E]f™eÜøñãåƒØ»ï¾;Ûe¯.š.v\“ê!Þà®&Óp«‡}r2 7 ­P2,²È"®{÷î³¥¬Z®’.#xµ5©à îj» {®^),§Z!`n­¯A¾­’­j¹Üü&ÕC¼U»UÒ¥MLê#Ü:hgÌ ½{÷–1 T—\í¹F¸ÕíªÝšf[]Ü£’›nTZ¢‚åXsÍ5ÝŠ+®èpdŽÃ„Þnú ‚ž%i}Ø=bÚm zÚ7¡ ›^­C=ÔuéÒÅí½÷Þî§Ÿ~’`½ùÓãÚÿÊ!~È…x+‡y”R¶fQj —¥W¯^®M›6B¸óÎ;o…sË?y†§}ñÅnÕUWÍÿ¢˜Ç lø8æÕ²â7‚€i¸”´àÍ6ÛÌ1l¬c>!Ê 6ØÀ]rÉ%EÁ4qâD·ûî»»%–X­¶Újnå•Wvçž{n*­#F¸¾}û¦þr0pà@ñUðÌ3ÏrYƒ¸ÔíhpÞNÅ `„[ ju~ÍŒ3\¿~ý\‡Üo¼á¾ýöÛ¢9äCd’iAjMš4qçwž:t¨¤7|øpwÆg•¶šKJÕÏ<óÌTyŠ*ˆ]d„0 QO‡3gÎt·ÞzkÎ)'MšäŽ:ê(·üòË»í¶ÛÎÝwß}Ñ÷ßïî¼óμf©Ý{ï½îœsÎq\ˆðí·ßvk¬±†Ì†Ûm·Ý¤<'œp‚ÃÜqÓM7¹çž{N.ÙqÇe‚e8òÈ#SÉìµ×^îÀ”ÿØCÏ>ûl×¢E qSùù矧âqðÐCÉêË-·œØ±õ!1hÐ ù˜ˆжmÛʱ֓é⛂Q&†@©á–Š`L¯_pÁ…`Î:묬58þøãݨQ£d£ ;>¸­´ÒJnìØ±â('ëÅÿ ¹žþù “æˆ×_Ý­¿þúbJÀuá€\§NÜÒK/íÔO/¤LüW^yEHZóD»ž0a‚ü…$/¸à‡öÍ8×þýûk4÷Ö[o9ý‡~pÛl³oÇŽ%üÓO?u/¾ø¢Ã‘;6õbÂD y# -´kÖ¬™Û!P F¸¥ ók×Zk-‡—I ­Ñ£G‹¯V>fÝqÇî–[nÉK« §wôÑG»Ûo¿]4|žcÒÄ,ñæ›oŠ)akïÇìvÙe!dâAžøÈ%O<ñ„ØlsŒFÚªU«Tô›o¾YSÅu×]ç:è 7nܸÙÈ›2Ž9Òxâ‰î«¯¾S 3¾j[¶l)„œJÐ "0Â-¸¤\áe¬œtÒI W÷ 7ÜPÖNcÊp!Ò¾}{ކF.9µ¦N*Ú2Z'Ò®¼òÊô¨þGg¼1š1ÂÇ8– B6Úh#7ÿüó»ë¯¿^þCì*JÐ8üAÌ©"cûr"`„[N4”sþÑNyåçK=¯õ!6Ùr6TðÔSO’Üo¿ýÄF 1³>[&™o¾ù##Ttÿ±3Ož<95‘à믿Öh2 ‚?,¶ùÁ¤6–$RA“51*€õ²J#ÓôñÛŠÆØ³gOÇG«c=Vj’ÏB•á*óZÎØß/¿ü2|ZHi ßÂ…$6Xžyo¼±ÄUD æ#ñ;ï¼#&4XÀAdl´ØoñÌo:Ò0ÕâùH8eÊùÈ9|4&”pµ‰!P*F¸¥"˜Ðë]tQùØ…Mt½õÖÛ&$·Ç{TcL¯½öZF¯d,éÝ}÷ÝnÏ=÷tØa!_†‰!»îº«cTÁV[m%š-ËÂã;–=dÖ­Ò¹sg±µ^{íµÎCB…0FJ<ûì³n‹-¶o—^z©ËÇ<‚½3æC TlÉRŒÉõ|uÇ+š+Z$_ì!­iÓ¦ Ùå¯úÑGÉ+{¡Úm¾Ð ibO]e•U2NÈ œ8VGø˜‡9‚éB°×.»ì²óCº€Á‡~èš7oîæž{îôà¬ÿq8ƒ‰%“:ëEiIf©¥–’ú@ø¤IY)jô.µS AÀ¦ö&¤!+Y U©<ÐZsMëU²%>æe"[ 4f«em… #$%Qhš¿>0“B}¶»ÕÚ0j€€n @·, C >0­Ïv·Z†@ 0­è–¥!`Ô'F¸õÙîVkCÀ¨F¸5ݲ4 úDÀ·>ÛÝjm5@À· [–†€!PŸáÖg»[­ C áÖôZdÉ,,„½×¢–ç¿„ÛÂÚä_\’|d„›äÖM«[ø¦fš+ÿmþ~Hþ› ÷p»T8{K¾ÆáÖ¸j‘½Þôø0À7Iõop·‡]õ0RNv·E©5*\½ÉuÏφ˜–UaðÿI^1ׇžî«“»åRkŒpkÝUÌRÕm /X,GŽ D\6âÔ›åepGˆ;G\š†$Š»EÜY²¤‹P⊯gàÖrµ= ËÁbÇ#ܸ¶\‘åæç†W²…ðÇ Áâk!Œÿø5Â-hbUlX`!^p朒.8Ó&õƒ€n´µjR7þ¼óÎ+„ ¹âÀ€Ðnq žÏ24uaÞÕT³Z®â éB¸üw6m m›¼3°ˆ±EÀ7¶MW\Á¹É!nxˆUI•óæÈm—-×JÅ• ùW…ß"À³›jºªåÒànR?áÖO[§4*%\´-•sÄ Ú-æ3)ÞATsSµå‚-nXËUÂ5Ò-ã¸^a„×–+²ÜÜܘ YH€=/D‹iAµ^ö„›†xB¦º-Ä&_Ú€Íȶ0lãÛ7î-X@ùÕVÈM úú61¨)Á´ÛÀM‹ ÆŠ-Ä ¹B¸àÌ1{C<“ú@À·>ÚYjÉÖ¨¸éõ$€F«f#ÛÒ;Xë¦Ú®’/ç9fo„[:ÖqIÁ7.-U¦r*Á’œ’š”l Ô ÿMòGŒuã*Å™sa’5²ÍÓ¤Äü¿©ÌH—”Ö,°¬6¿j´F²‚ØHôtâ%:ç [“úCÀ·þÚ ActiveLayerIndex 0 ApplicationVersion com.omnigroup.OmniGrafflePro 139.7.0.167456 AutoAdjust BackgroundGraphic Bounds {{0, 0}, {571, 818}} Class SolidGraphic ID 2 Style shadow Draws NO stroke Draws NO BaseZoom 0 CanvasOrigin {0, 0} ColumnAlign 1 ColumnSpacing 36 CreationDate 2010-01-29 09:48:33 +0000 Creator Gustaf A. Neumann DisplayScale 1.000 cm = 1.000 cm GraphDocumentVersion 8 GraphicsList AllowLabelDrop Class LineGraphic Head ID 79 ID 129 Points {317.22199999999998, 429.32791353754141} {196.13920000000002, 308.83987271990873} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 81 AllowLabelDrop Class LineGraphic Head ID 79 ID 128 Points {106.88579015298748, 432.88299999999998} {106.88582432955573, 409} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 80 Bounds {{361.33300000000003, 79.825999999999993}, {90.285700000000006, 22}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font Helvetica Size 10 ID 97 Shape HorizontalArrow Style fill Draws NO shadow Draws NO stroke Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs18 \cf0 type == datatype} VerticalPad 0 Wrap NO Class TableGroup Graphics Bounds {{51.501199999999997, 635.89999999999998}, {144.63800000000001, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 98 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 mixin : RelationSlot} VerticalPad 0 TextPlacement 0 Bounds {{51.501199999999997, 649.89999999999998}, {144.63800000000001, 12}} Class ShapedGraphic FitText Vertical Flow Resize ID 99 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 VerticalPad 0 TextPlacement 0 GridH 98 99 GroupConnect YES ID 96 Class TableGroup Graphics Bounds {{24.154699999999998, 597.48299999999995}, {165.46199999999999, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 100 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 superclass : RelationSlot} VerticalPad 0 TextPlacement 0 Bounds {{24.154699999999998, 611.48299999999995}, {165.46199999999999, 12}} Class ShapedGraphic FitText Vertical Flow Resize ID 101 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 VerticalPad 0 TextPlacement 0 GridH 100 101 GroupConnect YES ID 95 Bounds {{370.45387680756761, 212.24799059225015}, {58.329500000000003, 24}} Class ShapedGraphic ID 102 Line ID 94 Position 0.50699383020401001 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 89 ID 94 Points {375.16895272724531, 186.30099999999999} {404.67099999999999, 233.65000000000001} {409.90650938920737, 261.12200000000001} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 86 Bounds {{179.298, 697.33299999999997}, {30.857099999999999, 22}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font Helvetica Size 10 HFlip YES ID 93 Shape HorizontalArrow Style fill Draws NO shadow Draws NO stroke Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs18 \cf0 . . . } VerticalPad 0 Wrap NO Bounds {{102.60994700682967, 571.95901688144068}, {58.329500000000003, 24}} Class ShapedGraphic ID 103 Line ID 92 Offset -3.908935546875 Position 0.20789507031440735 RotationType 0 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 instance of} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 91 ID 92 Points {127.1945299827546, 558.88300000000015} {146.70099999999999, 619.39400000000001} {151.34568450590089, 674.26499999999999} Style stroke HeadArrow 0 Legacy LineType 1 Pattern 1 TailArrow FilledArrow Tail ID 80 Class TableGroup Graphics Bounds {{80.127099999999999, 674.26499999999999}, {144.63800000000001, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 104 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 filter : RelationSlot} VerticalPad 0 TextPlacement 0 Bounds {{80.127099999999999, 688.26499999999999}, {144.63800000000001, 12}} Class ShapedGraphic FitText Vertical Flow Resize ID 105 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 VerticalPad 0 TextPlacement 0 GridH 104 105 GroupConnect YES ID 91 AllowLabelDrop Class LineGraphic Head ID 80 ID 90 Points {122.38148080033758, 635.89999999999986} {113.85795490799903, 558.88300000000004} Style stroke HeadArrow StickArrow Legacy Pattern 1 TailArrow 0 Tail ID 96 Class TableGroup Graphics Bounds {{264.81, 261.12200000000001}, {295.14800000000002, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 106 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx:methodParameterSlot : MethodParameterSlot} VerticalPad 0 TextPlacement 0 Bounds {{264.81, 275.12200000000001}, {295.14800000000002, 12}} Class ShapedGraphic FitText Vertical Flow Resize ID 107 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 VerticalPad 0 TextPlacement 0 GridH 106 107 GroupConnect YES ID 89 AllowLabelDrop Class LineGraphic Head ID 80 ID 88 Points {106.88570184457316, 597.48299999999995} {106.88570732153661, 558.88300000000004} Style stroke HeadArrow StickArrow Legacy Pattern 1 TailArrow 0 Tail ID 95 AllowLabelDrop Class LineGraphic Head ID 84 ID 87 Points {352.01146512998173, 148.30099999999999} {287.2446029162823, 39.584100000000007} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 86 Class TableGroup Graphics Bounds {{281.66699999999997, 148.30099999999999}, {163.327, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 108 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::MethodParameterSlot} VerticalPad 0 TextPlacement 0 Bounds {{281.66699999999997, 162.30099999999999}, {163.327, 12}} Class ShapedGraphic FitText Vertical Flow Resize ID 109 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 VerticalPad 0 TextPlacement 0 Bounds {{281.66699999999997, 174.30099999999999}, {163.327, 12}} Class ShapedGraphic FitText Vertical Flow Resize ID 110 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 VerticalPad 0 TextPlacement 0 GridH 108 109 110 GroupConnect YES ID 86 AllowLabelDrop Class LineGraphic Head ID 84 ID 85 Points {196.13920000000002, 119.99070764884014} {267.89824345555076, 39.584100000000007} Style stroke HeadArrow UMLInheritance Legacy LineType 1 TailArrow 0 Tail ID 79 Class TableGroup Graphics Bounds {{211.673, 13.584099999999999}, {135.654, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 111 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::Slot} VerticalPad 0 TextPlacement 0 Bounds {{211.673, 27.584099999999999}, {135.654, 12}} Class ShapedGraphic FitText Vertical Flow Resize ID 113 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 VerticalPad 0 TextPlacement 0 GridH 111 113 GroupConnect YES ID 84 Class TableGroup Graphics Bounds {{317.22199999999998, 336.14299999999997}, {178.50700000000001, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 114 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::VariableSlot} VerticalPad 0 TextPlacement 0 Bounds {{317.22199999999998, 350.14299999999997}, {178.50700000000001, 112}} Class ShapedGraphic FitText Vertical Flow Resize ID 115 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 arg ??\ convert false\ incremental ??\ multiplicity 1..1\ accessor true\ defaultcmd\ valuecmd\ valuechangedcmd} VerticalPad 0 TextPlacement 0 Bounds {{317.22199999999998, 462.14299999999997}, {178.50700000000001, 238}} Class ShapedGraphic FitText Vertical Flow Resize ID 116 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 getParameterSpec\ reconfigure\ add\ assign\ get\ delete\ ? __default_from_cmd\ ? __value_changed_cmd\ ? __value_from_cmd\ - init\ - checkInstVar\ - getParameterOptions\ - isMultivalued\ - needsForwarder\ - makeSetter\ - makeIncrementalOperations\ - handleTraces} VerticalPad 0 TextPlacement 0 GridH 114 115 116 GroupConnect YES ID 81 Class TableGroup Graphics Bounds {{39.058700000000002, 432.88299999999998}, {135.654, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 117 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::RelationSlot} VerticalPad 0 TextPlacement 0 Bounds {{39.058700000000002, 446.88299999999998}, {135.654, 28}} Class ShapedGraphic FitText Vertical Flow Resize ID 118 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 nosetter false\ multiplicity 1..n} VerticalPad 0 TextPlacement 0 Bounds {{39.058700000000002, 474.88299999999998}, {135.654, 84}} Class ShapedGraphic FitText Vertical Flow Resize ID 119 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 add\ assign\ delete\ get\ - delete_value\ - init} VerticalPad 0 TextPlacement 0 GridH 117 118 119 GroupConnect YES ID 80 Class TableGroup Graphics Bounds {{17.632200000000001, 31}, {178.50700000000001, 14}} Class ShapedGraphic FitText Vertical Flow Resize ID 120 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs24 \cf0 nx::ObjectParameterSlot} VerticalPad 0 TextPlacement 0 Bounds {{17.632200000000001, 45}, {178.50700000000001, 280}} Class ShapedGraphic FitText Vertical Flow Resize ID 121 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 name\ domain\ manager\ per-object false\ methodname\ forwardername\ defaultmethods \{get assign)\ accessor false\ configurable true\ noarg\ noleadingdash\ disposition alias\ required false\ default\ initblock\ substdefault false \ position 0\ positional\ elementtype\ multiplicity 1..1} VerticalPad 0 TextPlacement 0 Bounds {{17.632200000000001, 325}, {178.50700000000001, 84}} Class ShapedGraphic FitText Vertical Flow Resize ID 122 Shape Rectangle Style fill GradientCenter {-0.29411799999999999, -0.264706} Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs24 \cf0 getParameterSpec\ destroy\ - init\ - makeForwarder\ - getParameterOptions\ - unknown} VerticalPad 0 TextPlacement 0 GridH 120 121 122 GroupConnect YES ID 79 GridInfo GridSpacing 14.17322826385498 MajorGridSpacing 10 GuidesLocked NO GuidesVisible YES HPages 1 ImageCounter 1 KeepToScale Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo Animate NO circoMinDist 18 circoSeparation 0.0 layoutEngine dot neatoSeparation 0.0 twopiSeparation 0.0 LinksVisible NO MagnetsVisible NO MasterSheets ModificationDate 2013-06-01 16:42:39 +0000 Modifier Gustaf A. Neumann NotesVisible NO Orientation 2 OriginVisible NO PageBreaks YES PrintInfo NSBottomMargin float 12 NSHorizonalPagination coded BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG NSJobFeatures coded BAtzdHJlYW10eXBlZIHoA4QBQISEhBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCEhAhOU09iamVjdACFhAFpAIY= NSLeftMargin float 12 NSPagesPerSheet int 1 NSPaperSize size {595, 842} NSPrintAllPages int 0 NSPrintReverseOrientation int 0 NSReversePageOrder int 0 NSRightMargin float 12 NSTopMargin float 12 PrintOnePage ReadOnly NO RowAlign 1 RowSpacing 36 SheetTitle Canvas 1 SmartAlignmentGuidesActive YES SmartDistanceGuidesActive YES UniqueID 1 UseEntirePage VPages 1 WindowInfo CurrentSheet 0 ExpandedCanvases Frame {{28, 36}, {1021, 992}} ListView OutlineWidth 142 RightSidebar Sidebar SidebarWidth 138 VisibleRegion {{-145.0228400978813, -29.022840097881307}, {862.83219357080509, 877.48959210258545}} Zoom 1.0233739614486694 ZoomValues Canvas 1 1.0233739614486694 1 doc/next-tutorial/slots.png000066400000000000000000003302301242365656200163300ustar00rootroot00000000000000‰PNG  IHDRGÜíâw pHYs  šœÕiTXtXML:com.adobe.xmp 5 2 1 m ž@IDATxì˜TU҆Ϫ,DÅ€ 3*ˆ #Š˜³¢b˜ÁìkÀ5bÀ1aV̘3‹qUDŜּF‚ŠWïÿ½ÅœöNÓ=ôÌôt˜©zžê›Î=÷œ¯oßûu:UK’$¸8Ž€#à8Ž€#àLC`&ÂpGÀpGà/œý……¯9Ž€#à8Ž€#œùMà8Ž€#à8Ž@ 'G)0|ÕpGÀp'G~8Ž€#à8Ž€#BÀÉQ _uGÀpGÀɑߎ€#à8Ž€#à¤pr”ÃWGÀpGÀprä÷€#à8Ž€#à8)œ¥ÀðUGÀpGÀpœù=à8Ž€#à8Ž@ 'G)0|ÕpGÀp'G~8Ž€#à8Ž€#B`–Ôº¯:Ž@‘ø›¤¦ª¸,RÍ^M3A AšI_¼Ž@³Càoþûlvß©w¨LÔ¢H†XFËl\Æcej¡_¶Ì@†¢Ò”?ã¶%àpq*'G•ó]xKª1‚A‚f–²•}Qµjë,]š7Ñ:IË?¤£´Úq'IÍûfðÞU>¬V=ß•·´BÈ"F¢¨“µîÖ¢ ýÞÊÜ,ÈP[)D)M–Øïâ8eFÀÉQ™¿¿|³@„…RÄo m%-‹eöÝwß ;wÿûßÕ—JD †P·VÛ~Oµ/a¿[Rˆøª#P&x »8Ž@〠Er+™µñU^Ão¿ý9äСC‡°ì²Ë†ùæ›/ôíÛ7|õÕWVÉ 'œôÞ ï½÷Þ +½÷Þ{ÃYg5Ãr^ Ñ@ޏW Òéá×FWì8Ž@ãprÔ8üüìŽ@ Z G¼è F³—š .¸ \vÙeaË-· ÷ßèׯ_¸ýöÛCÿþýëÝŒÛn»-wÜqõ>ÏO¨7³é È–Fî G ðc%| Þ†jG «ñ’ãe‡E`Ž\:thXgu­·ÞzôèaëwÝu—½úê«mûºë®³í}÷Ý7¬»îºáÓO?µíSN9%\qÅ™jÓÛãÇ·ý [l±E€,üñ¡k×®™òé•qãÆÙµÚ¶m–[n¹Àµ‘k®¹&üë_ÿ²õÍ6Û,L:ÕÖý£IàHC¨¹wâ}¤UGÀ(+Låwu ühØ= /0 Dm¤óI;JW®%Å}¤–}ôÑ8Ü&³Ï>{"+O2Ë,³$ -´PòÇ$'NLÚ·oŸÌ?ÿü‰“•;è ƒ2çˇ(Yyå•snŸ{î¹Vžs8à€DV£ä§Ÿ~Ê”4h—?Ròý÷ß'sÍ5WÒºuëd—]vI\pA;v÷Ýw'£FJ–YfÛ>âˆ#‘£L¾R<¸¤=¤]¤¤í¤¥Œ¯š®æÏfÇÀï2Ý|™€÷_óxðëe9ÂRÄÌ£öÒ%¤+I{K§{›FrtÇwØ1YzŒˆŒ;Ö¶‡nÛ!HÓ¤I“2uÜrË-É×n×®]rÓM7Yù49ºè¢‹¬Œ†áìØG}dÛn¸¡mï¼óζ¹¯¾éºÒ¥¤óK!ØNŽü™ìïå ¸x°»8Ž@ã`8M;dC˜òŠ,@vLÆ–?ÿü³-eÉ Ë/¿¼ gýßÿý_…'SÇ®»î6ß|óœÛ²@™Ï‘¬BáÉ'Ÿ ‡~xøõ×_mhMV©Ì9¬|ðÁ¶½ÖZ·‚Íl“õ(³ßvúG)ˆþF ©ñ,F¹\G Ì89*óà—o6Ä— åÅ—Wfš)÷OŸ·ÞzËλꪫ³Ð ‘<Ð|ˆþüóϰþúë‡ /¼0l·Ývá³Ï> o¾ùf­*4ôfÛ‘$ýðÃáÛo¿ q­Â¾Ñ”D_£xï4åµ¼nGÀ¨¹ŸÐõ¨À‹:Ž@æß>ÿúùME‚T/h°ôì¿ÿþ'if‹1í~ðàÁ™:Ö\sͰÇ{äÜfêþ³Ï>k–¥o¼Ñ¬H#FŒóÎ;oXj©¥2ç°²õÖ[Û´~êÆ\~MëÒ6Ûlcå"q{ꩧ̼_ëdß(&XŒâ½­n9*&Â^—#Ð@øqº8Ž@ãˆÄ¨ÁäèÔSO ï¿ÿ~8ûì³mX r3dȰÓN;…VX!¼òÊ+µf¥·O<ñD³2Ýwß}a¯½ö ³Í6[`¶™|œl=ݽW\Ñ,KÛ~ûííÐŽ;îŽ<òH[‡<=:ôîÝ;0ÜG].M‚÷Jš91j˜½RG þ”%‚oý›ég8•‰€âñ‚ÿ1§'!‰Ž'¯¥•?þ8,°ÀA3âê¼0–*†Öäøm¦ìš=æ™gžìݾ]È)ÙNút‚t²tŠô©MÔÒÅpÊ„€[ŽÊ¼_¶Y#­eéd§N ºî¬³Îj©|…åC¦hû£¥¨ô ºh]ðŠæ‰@ÉÉQMDáæ‰¦÷ªê`Žv‘M=(/¼bÕY¤¦y5Š@¼Oœ$UèäÍj¹•eŸøƒÝôzËEÛ{^qÔ o¤Û_XìˬπDQŽ{ëZ –H”G xoÅû&>#ãþ¬â¾é8¥D (ä(EŠøãƒÄõø£Ÿ¶wÚg®}éã¾î” ì—Ûi¥F•‡$Åû™e\§÷é=—é¨yf¦ïÊÄíéÊûGÀ(-&G©yœyÁeŠ*?öH–âqírq*?Õ¢H”XO+å$ Â˱ÉÞÎ·Ï û‡#Pƒ@¼ox6ÆuÇp*F‘£bD7øq£1fÇZ÷AÁ¥ªà…Å 4îmÂLsO×7­ö£ñGy¿ÿ‚KÄû…eæ~ÉA¾ë¬Ä:Ž@ñh9ªiN$FÔ…fr¿¹ùkTn©ðᇖK.¹dÁ±Y0L™2%(—•iþ+ø‘! ¤¦–Š,›ƒÔ¦éÿžê~D¦^`ñG‘l’”:ÍWéàÏdZÒ÷Rz¿¯;Ž@‰€Ø4FxPG$F¼gmL… 9÷ꫯ¶Ô]ºt ݺu³Ø,§œrJ¦*e)3Ïû죪‚Yc =O?ýtÎmŽ!÷ÜsOÀZD&óçŸÞöÅc¶¡^¾¤NXuÕUÃC=d»IððÃ[+v|úé§–cŠ,å묳Že8·‚úÀòÓ«W¯@=R4¼ð vÞi§fEÎ8ãŒY‹²É&›iºüòËÃV[me¹¯èãO?ýd„nñÅÝ»w·´ í!ŒcŽ9ÆÈä Á2F9® Ñ"mrþùç[o¹å–°Ê*«XÛ j|p nÚøË/ÜÍ߯k®¹&ì!ÝÄÔ©SäPß /H+A¦wäµ×^ ä÷ºùæ›-©éÅ_œ!„ [l±E€,A»víjçdŒ7ÎÚLþ°å–[ÎúF™\íÈ>·LÛÜÏÜÛ(÷:š-!Áe£âÛ…"À½ã÷O¡hy9G @N¢j/¬EsHç“v.']SŠKF-騱cÒªU«d‘EIÖ^{m{h8ÆÊŒ5ʶwß}÷DV [ɰcj±mYwrnkÈ*ѰX"_£Z×Ó§ɾûîkëòI6ß|óD¹¢LEå²²cJô™(ùf¢—z¢´ Éž{î™,¶Øb‰¢'ò¥ID½ÐE N”Êö³®¡´D„Æê9H^zé¥äºë®³íË.»ÌÚ$–ˆ”Ù¾ÿüç?‰È”­ï¼óΉ2§Ûºˆ•)Kæšk.Û·á†ÚµÁSä(9üðímʳee `å”þ!Yyå•m}Ž9æH4¼˜,ºè¢¶=tèÐ:ûöË,³Œ••e+ñJ”x4Yb‰%%9µ~öèÑî'‚jåhß,³Ì’Ð?YêlŸ2ºÛw'«Q"ògåù¶ãCÎmݺu"’—ÐWúv÷Ýw'ÙíIËÔQ®Ú&]Mº¬tQ)iA°Œ¦ýêøƒÀÐÇ:JW”ö’î$-WÓýºŽ÷†t[iO)ÏÍE¤¤ž„[u¡AÏf?Ïqó{ ñ÷@ƒ|úóRÀRÄlòH-.í.í%îÑ9‚L@V¶eÝÈ”ÛvÛmí¥ÌKbå³Ï>K”e}úyQfs;6räH;É„AÎá „…se1¢¸òÊ+í ŽADƒ†,°MûeI>ùä»–¬GFX8É„FÆ ä•P" Õ ¡ª«_ÔI£ ˆ’Úú¿ÿýok_$«àÉý‘•‡”ÒŽØGêi×®]rÓM7Ùñ49ЏGÒøÑGÙµ Hº¶£ÌôEºŽ3X'éüÒ6Òlr„u‰$d‹I»I{IûJËÜ¿|¥"À½!ähy­GrÄ3Õɑÿ›uÏû¹EÀ‚ÓX¡†Z‹~G9ëd¨çd„¼Mqȇm|sð úßÿþâ0ûe1gæå—çùQ{›lá" áõ×_$ÑL‹^î¶)ëOf·H”­Çå³Ï>›9Æ C^™ÐIÚýôÏÃáa(ðÎ;ï´!BÛ‘ãƒ>r}†ÔŠbÈMVs^>|x`h )„: ÃŽœƒ0”öꫯÚp ÃP q¥ËR pþ)1+ü¯Ú´á Ϻúe…R²lÙÖꫯn\uÕU¶ Qî[j©¥lSdÖ|Žh—ȧ ò]0´&K\<Å– £"k­µ–-;wûmgå}ð²â¾ŽÙXLsIÜÿg®ƒ¾ÏÈ?zî”õxiÕÅpʉ@cÉ?f”zÐè{¤Õé…cù¿œ(—\rI\á"^ÆÑo†˜…ïd,’öÇ—p|ÑG¢Ã1rà?£á¶Œž}öÙ™pȤI“l9vìØpÅW„o¾ùƶs}Èjc„èÖ[o52¥!%+†Ó2„‡6ãÇ“-Ìž‹rÞyç…n¸ÁÈYÒ³ÛËå[FÒÄñ\ýÊ>uäñÇÏô,Ö_ýLÑtû<ð@ó!‚ØRæÂ /4$YøþOiÄ!ñ{øá‡·ß~kä.]®ÂÖæˆÄ(Þç¹~7¼ÜPVø?[óG ?Ü'‘Q*Þ?ùÏð#Ž€#Pr=äë{áHŽ¢³j~”§f¨5l4´f¿8õF§ã1cƘ5ÉÞÆÊÐP\аMÐp•95SkTZ=ôÐpË-·§±àôîÝ;}8ÈoǦõkØËÃq„Æyû™gž1+×(áÀ-?&#,êBp6ž0aB­:smRgg,'ôµ.Áy‘Ÿ–9škˆl:ËQ]çÓæºúʱýO=õTXo½õ¬:Â|ùå—fÙêÕ«×tV xM¦îc“/WÐð§Y‘FŒ YѺËbq¢=ƒŠ tÕ»Í6ÛX‘t;²­c±Ž2,¹Ÿù‚Qîõ\_j,yáErDL$ë³/‡ô= {„{&Hì‹÷’V]G \ð¸XÂ˃I½êdvÔ~ûígqˆ ß}÷ »ì¿ÿþáÅ_4K Vš7ËMz{5Öò²_qª¼œ¨ɇ¥Vߘâ/ß{Y1kKQšÌ`Ù`F/oÂŒ/HÃGGuTÀ’óè£Úd@ÎÅa:,X„ ÖÏŒ„uG}tsºÅb¨ñí·ßÎyÚÞ{ïmCr#†Û t ÏEëWΓ²vÖÕ/ŠBZFmu3{˘2ˆå ­^YU[øbÝwß}†CÌz£¬§Œ°,qLNívˆÙpòs²õìvdŸŸ®«„ëÜÓ(Ä(j®ËÇá‘HŒ¦ªÐR|‘02Ή37ÃtÑ¥Õª,hk´ƒ–ƼYZLé§Ê>’Ž•NûWPÌÚËS÷Xý&"¥_“¥ü‹bš iÓÅp*sükHCô/2Ä“7 ã@¼“aæÚí¥þç¯RŒÅÒ’K Cš5g"ŽCJðå!XaÚ׉a9†ðJ C[„€¨dG‚ÆÊþìýéóã:CJC,/3†¬¦’ûŒŠÎðx¾~q"}‹ý嚤N:ÔÎÇ·i0âÈv>a”º5ËÎ,LÙåÒíÈ>VÊmþéK¶“~'åeÆKñl:]ÍïŸ$n8~  ¶Rœë§åwÁïƒYOÓi³¢2·„t)é‚ÒO¤ïKé/éai1åpUö‚t%)c²/J!Õ,æHޏo G?H¹—¸§§ÿQÊ~'éïÿôüt ’€pqÊ…@1É/Þô8–0m«\}ª×urb8ŒèÌX.\ˆ@ 9ê£ío¥ñEö³Ö³É– HSú!!~‘A˜ F§\´Fiµâ„v/W£íµ$´ù[RHQô£Ú\ë¥ÏK‹)'¨²³¤ÍÞÒ.Ò1RH$£Ú„‡ í7,‰Ü7Ü?‘ ¥‰Ç GÄ$L¹8Ž@¨×Xíä!€%©ê„! ‚E²tq€÷~| Æ!^rü¶ AãåÈË—~Ü_I¿HP7)›¤¯Iï—¾-¥OÙ g¤ßdhä6Xa¥ƒH\#]P 9]Mz·ôiµ ä(ÞX…¸7¦Ô(ëf)Ò’{„²ÕHÕlG y!P rÄÍÆ´dÝTÿºã¾æ…˜÷¦%!À½_VÜ϶ٟ–øR‹ÊÅû+Ä‹ÃQüæ8^IäèrµçIéÒñÒØg­N'´{éËÒbyA" Gà…|-…¨­ Ý[º®ô:)Ö¬j{ª¡ô …ôE Ä+²ãàž}oi—‹#à”b£Øf~Ôñ…ÐàáºX™/r#PCðã=_ZµH ¾!*Ç1^n”JóÙÏ‹/úñ{Ú”.£Í²ËijÁAÒ륒ºd ü¯ôóº 5ðN2ì”–§´n"=JÊ0ßÒ/¤•.‘ðÐ7È3d™{R•ýç~qq @ ±äˆ3z^,ÿOÃÁ?›Ùd&×=ÎK0$zN¶yFR1Š¿•ø{Ñ®²ËµዤGJ± å“Åtàu)>GÅð¢?䩸6í¿Kº‡rô/é i¥ ÷@ÔxÐÇ4IŠëìDªRûãírZ %Gñ¥ÁáAPI}k”8EB€{Ûîq,F©:ã:¿^vlÇ—!– 9BâïfÚVe|B>‘B:Ž—>(Í%Ki'ä Sl à3£ºS™¤«PVEÊ*Ü Qã=ïú•}ÜVñ^*k£ýâŽ@KG 1ä(×8/1Âá™hʃ¶ÄúQâYÃÿ¡‡²”ÄîYwÝu-SÜÉ:O %sµØFœß¡CËXO,#2¹¤!Í1t‚ vØa¶îŽ@HßëÓÝã¼Ðj\ìì§ëQ>¾£¥ˆ%R‰ÄhZË]+ÛHïÎ#½Rš-+hÇÍÒ졯ìr Ù†(ào”¯nÂ#œ$e¶ÜÉÒ[¥Õ Üñâ¾Hk¼W¸Øïâ8•€€~q¹¾Kµ‡<³oxˆv”v“ö’î å‡^KðÐÄ*Zr饗Z’Ù)S¦XÂTåMKD0ùúë¯å,KsÈÎ%K¼$&J¿a™ÛñíP0Ä„¤¨$;á²r÷ÜsO¢x<–(–úˆ1!1¬‹#и¥…žRHÁ¢R^ÐøåLÊ~i´ñç£Ur¿”)ý•¬¨}L3?/«„(À‰ š¢ýÿU½rÔ=›ö,%¤Â¥Ò¤Mqý¦ª3~ïÑ)Ÿ{‚{ƒûÍy/éÞmгÙÏsÜühü=À´¡’þÿÕYV£®]»š’ƒœ]l$¯ZÏž=-(¢²Ø‡^x!S‘”c¤k"\÷ïßß"RcQ"Ú5¢,ïáàƒ›l²‰m“ÓläÈ‘–ÏÌvø‡#Ð8¸¿Qîù:ïuÈX,SãЭMέ9Fì-]Q:\:Lº”á®Ò÷¥ù|‚t¨Q‡˜ðÓŠÒC+7I!FHßV³ØýSsŸTs?¼íŽ@³F 1ä( Ì _&ŸXÒRüòË/–bâå—_¤ ™,yäý‡‹¦/øôÓO›¿Ñ<þûßÿ¬>z0fŠä:'s°f…¼_ª ß|ó)õPŸ‹#P"ñçÞŽ÷w±~7Eh^qªÐoçó¥›@Œ¨UKbñ@ŠÞ“Ž‘®!}YÚTÂsdfµå-ß–N.#ÝW:·öﬥ‹#à8MŽ@±ò±™ ;d÷èwÞ±¤­K,±D ?ÝõPÎ.VçvïÞ½í JHkÁ!‡ _|q‹«ôÚk¯Åb¾tš-"F««s×K·©˜ï¢yôöBucIõoËæÑï…#àT %#GûÅ-ºùæ›-`ã+¯¼b@\vÚi§pÁXœ"òªááD§OŸ>ñúØcµ$³7ÝtSXgu‰'N‹GÐȇz(Üu×]){èС!ÔÏ?ÿ¦L™¸6$íᇶ˜J'œPmu ÿp F@Äa)%ÝSÄ襂O¬‚‚êÏïj&Ñ»/U?[WA“½‰Ž€#P%#ÎÑ »JJ1cÆX$lÈO—.] 7Ü`çAtÖ\sͰÑFÙö)§œ\pA É,I;ï<-¥RûöíCÿþýC=–§=öØÃÎÁâ4|øp‹s4a„°È"‹„±cÇZZ+PóA9¬Q$­8p`Ø{ï½Ó‡}ÝhVˆ04‘tÇŠH<Ô¬:WÓõë õò1ÒS›c½OŽ€#PzJb9zá…B‡Ä™sÎ9ÃrË-gëÿùÏŒì؆> @sÌ1GøöÛomÕQˆšσàL:ÕÍ6ÛlfYâ\ˆÖW_}O©µä8ç!mÚ´ ¿üòK­ã¾á4DÚ¨/K¯¸®¹ô+O?ŽÔþ#ÔçŽyŽûnGÀpê…@IÈ>> gE?#–"dùå—Ÿþy¦Ñ_|ñ…‘#ÎAfšiÆMdèmâĉáý÷ßï¾ûnèÖ­[¦¾ôJ!u¥Ëûº#Pˆ$´R»ï–¾ b4¸ûPŸ6«Ÿ©üyR|\GÀh43f¾D‹-¶X ),ÎÔȽ÷Þ~üñG[ßrË-Ãc=>ùäÛ5jTØpà ƒð¶]ÈÇ;ï¼cÃr¤t=ûì³C©à¼LsEKSõi®Ìѯsµ¯›žç8æ»GÀ¨%ñ9¢E8?ã#tê©§|V[m5k(ùÐ CÌb[yå•mHìž{î©W'9äpÐA…aÆٰٞ{îN?ýtóWªWE^بrDÎTˆýCfû¿¦„Vy¿fÔ|õõ7õ2x™–Ëk{Ú˜ûŒNô㎀#àä@àozˆäØ=ã]zá¼ów)!üç”Î%m'űhd®z'OžlÓô—Zj©é,CÿýïäI“Œ$E¿ ÕS°à?„ŸV*ä›o¾ ,°@Áç{AG Ýãì"‡×wÒ ÒÉR,28«MÕ=þ§–#j/i5—®¡¶ÑÞ'€™yÏ©ÿÄ@rqG A””5¨…~’#P&ª‰©­8²Õ¯)b0mŒºL¸•ó²Â¡³®?VÚU8|QζøµG z(‰ÏQõÂã-w*‚žj%ÉdI Òb‰ß”úÏLK¤ç³íâ8Ž@CprÔÔüG B1"&ÆÒE <äû´ï¿«5„ÍúÓ6ýÓpú!àä¨~xyiG bÐË5†XFGˆ=Q1 +sC„ůj¾W8g—lÒI™»í—w""à䨈`zUŽ@©ÐKŸ H/^ªëVËu„ S^‰thµ´ÙÛé8•ƒ€“£Êù.¼%Ž@Aˆ1Kô>éc"Ä÷qÉSûO^ æ>ì{GÀÈ@ÉÈÑf›mn¼ñưúê«[ºÑ£G‡ÒƒX–ä°‘´wÙe˶ñƇ'ž˜6Zðúë¯[BÚ!C†¢g¯²Ê*™ä²œwÒI'…e—]6tïÞÝ’Õ²¡¾­·ÞÚò¹|òɘH|ð£ÎõÖ[ÏR›gí‡~°ý—^zi@wÛm·0xp³0l}öê@@/ú¿©¥·HÉ‘sTu´º<­q|_W¾JzNyZàWujE dähüøñ᪫® ^xaXi¥•,ð#9Ñî¿ÿ~Kù1bÄÃR²ÐB …W^y%Ì2£‡\˜2eJ¸ùæ›ì<üðácÇŽX’“Æzè¡p×]wê:th†8~øáV–Ä´}ô‘#:7Dhƒ 6Ûl³Mxúé§yÛ¸B “O<1Ì7ß|a§v²}þáT¤È˜Oº‡~ RV!)Q3øw³¾8åÚ%ºž_Æpš<_¢ê;A ÙNÚQJB³^Ò¥<·kÉ"‹,’ˆÔØ>‘‘Dd$I±í#Ž8"9öX‡'É3Ï—öwêx7¼K:“ƒ!4+ ‘¬Û´icÛ³Ì2K±u‘Ÿ°í¶Ûš5h…V°}ñ£}ûö–„mÎ%*6‚Š´$XŽæž{îðë¯LV áÅ_ óÌ3O ~„õe–YÆÖZÚ•Ž¢-BD®ì8Ãv.Ž@¥ ËÇ.jKŒ~=¹RÚU íÐËj¤ð;Hmí/%P¦‹#à8u"P²a5Z!«Mùã?®»îjÃ[¤yä&ãü%ùÎ?ãŒ3Âĉƒ¬G6D×­F,ýMœyf"ƒôD!M B~7|šH3õÓO?5ÿ£XÖ—Ž@%  ûjÇEÒMô¢÷¨Ï ûRH­r²°$½‘‹#à8u"P7[©óÔâ„à`¹ÙrË-HÝtÓM]äwÞ m´QhÛ¶møÏþ4äfÃË-·œ-±(!7Üpƒåoc}ýõ×O=õT€!ø$õêÕkºœovÐ?2! —ùŠºômÒíEŒÞ*S3ªþ²ÂîmuâzéªïŒwÀpšŠ"G8@3k ‡môÕW_ «­¶Z8W‹ü‚Óö 'œÖ\sͰÇ{˜åéôÓOX¢pÐ¾à‚ Ì"Iêܹ³ ë­±ÆáÐC $Á…DwÜqA~Jù/âG# b„/߃҃ôrºÄ—oŽ—;EÚL¸öhŽó>9Ž@ñ¨Èij }Í1Çf ‚àLš4)È»Î^ãÄP¾LCeœóÒK/Yø|™ð=b&Ö"üžï¾ûΆ޺té’Ùgü£Å# —(Ðõ;é)¾>S¤8»Maùk¼V;Š)º6ž—^¢ë¸ŸL‘À®»«ª#¤=šòû+Rs½GÀ(IŽŠ‰Å&›lpìfØíöÛošfÖ¤b^Ãëjž”‹éºÌ†{Jú/½Àmžè–¯WÂ÷]ýFaK $GÀp¦C ¢†Õ¦k]vàgÄPÚ=÷ÜV]uÕP¨S.íU8õF@/n~“#¥0<®Þø … 0@… k¬s.Ž€#àL‡@³·M×cßሀ^ž”,é°š®y¥®ÙIº¥,Óâ[hÃ¥¸ç‹Uãß…1Sü]ŠŒ€ð%X“ ;¹¹^#0ÍÖr4uêÔé:[Š Ûá'åâÔ½PNÐ98 33͉Q}¬_ùU|[a¾rýN«ÌÒêrXµî2µg¹\íÑþM¤Ëæ9¦CÕ\Íô}-fKŽæœsNó/ÊþŸþùеk×ìÝo?øàƒá­·rϨ>ì°Ãlæ[Œ¥”¯Ò%–XÂfâå;îû[z!ì­^ï'ÝTÄè§–‡@i{,Œ'éŠ [åm\Ú«7ÉÕèÃnMRsñ+][UvÎSm5õ#O|ws@ Ù’£|_!î»„æ “aÆ…^x!çÉ×]w]xüñÇ-™nξÓÈ€ÞÍ›i÷YRrÖ|£ˆïj®Wµ¼Œ÷jšêKZ+V£%u/õ“f¢çjýiéÍÒ»¤wHI…CL·]¥÷IÇH—jlkU×.}Luu«¹û‘Þ#=^ºöï eýïÒ“¤Ji¡+ÊÞÚí┌`‘ıݻwK/½t¸óÎ;3è¿þúëa½õÖ³XDÄ9"),BZt·Ýv ƒΔ+D¾&¾Ñ /”CÍÊèåÈJ§NÂ’K.n½õVÛOÚþóŸ™2$Ã%ÎzñŸ!L’Ï2Ó­]»v¡OŸ>–Èö’K.±À‘¤*¹÷Þ{cQ[î¾ûîáçŸ,¿üòKÓ]vÙÅÚE$î'žx¢Vù¸A &fÓá4~î¹çÆÝ!&™¾Ò,Ð aUuäF鶺wßkª’No8gŸ¥ïa®*iv¾fòðú@]ºNËŸÕŸå¥ ký,m%]Zº½ôxé‘:ÖNK²jo-ý‡ôiFtü)¤&ê­–ÚfÿC™¦­œ©A6·’.0mWÀL Úµ­–äÝü^z‡ô )òŽm®%¿}¤Eí‡êsq†Ï‡†¨®6³t6)?2?ÿzIs&ž%Ñìl` bGŽ™(ZòÛo¿%“'ONü19ï¼óE·Nößÿd›m¶Q“4‡YÉh•-ÑpUòÞ{ïÙ¾ô‡ÈJ¢h‰â%Š’,»ì²‰‘ázë®»n¢8GV·ò¯%"F–ÀVdÄÊh["òd qEFì|8;¶öÚkÛu©ûÿøG¢\k ImIH{æ™gZ2\+XóA¹YgÕ®G¢[•LŽ<òÈ䫯¾J4CÎê&™.²øâ‹[RÝ·ß~;¡-Ÿþy2f̘D¹ç’wß}·NLj.ç‹  {™—çvÒžÒ¢'žUKH¿’â|Ý ß¡Ÿ×xÜ„ÿÒ‹«Kµ&é¿éƒ¤¯”€ø°õ‘2Œ•éŸÖÇJ!Fã¤ÃktTºÿÚ7«”ç{ÔÖZ‡dÅm[fózÜV¹¤³5¤#¤·I¿Î.=Uº™”wÈéÒ»¤KÏ–µ±=¾lü拉aØà²nâz“£gžyFøN¥I{ì±äꫯNzô Û4Q ǤU«V‰›AF¢È×'yå•WLåp¬¸âŠÉµ×^'¥Xr$ÿ Ì1EÏN.ºè¢ZäH±’sÎ9'SæòË/O¶Þzk#(Êã–(°¤“E(‘µ(Ül·Ýv‰"igÎI¯@Àh?B_üñG;gܸq<±2„*’#Y“%¾5¢Ä9Ÿ}ö™£º0¡œKià;“6 9R½äøúPº¿zÓàß¡ŸÛxìôðï[i×jÅSmO“ ˆË¿¤OI!9£û¥ !BÈGK»H¯¢¿¬f{¤û®mLíÏ¥‹¦ô¾§³Îa8m®š:_Ò:ähŒtaié—RÚÉÑFZ¿¼¦üQZ"-j?ÒíóõÆÿVZ†ÓÒÕëŽ,…ª# Ã]ä@#²õøñãƒHB<.#Ç"‹Mfß¾}3ÎÐ"áã?¶è×±€H‡EÉŽÛ={ò‡šàk4a„¸iK†Øˆ }öÙüa AŸ ë“ÕË0—,A¶_¤§ÖPœíœÁ¹·ÝvÛ bdÃf¹Š÷îÝÛRÈÂæŸþ°×^{Ù0!íʇ ‘Ã]ª Gð%2$q“6WWwoª¿õú&è;ÁÊ2Tú×C£Šº¦>ü©>Ì$ÝIë#´üTÍÿUë¿jž,#…,Í)ý‡ö¿­ý‰ômC˜˜½—ß%³Q{eÚòö¾¸u‘VRM»¨–OKÏ«Ùþ@K†ñÞ–2¼·›tU•¿\ËÙ¥´±µ´hýP].Ž@ƒ()9—(’ ^þøä@XðÉ!Hc|vHó‘-œ“–å—_>hHÊü…د!)˯ˤMþ°L;&«P˜íÆL:ÚU(&Ùuúve# ¿¹;¥ãô:¹²[Û¢ZI=PßÏ®ú^†WiÏ¢0ïF[›öåèd)æð©ìÒ’þÎW³´8Õw¿ê{B•Ì¢õk*TsøÏtVÃ'ê-Ñr-•k£õ‰Zgɾ²ö£Q øÉÍL˜%ùÙµ 1XlH»þú뛓3ùÎáÇœ·õ±íº>44g¯!5úQ…ûï¿ßÒ„Äsb4lؾöÚk!mI¢ –Hç£ýúõ ò}²ülXŽî¸¿A ž+ÊvœáYÂÚU—Lœ8Ñ,_´OÃsy£r“ÎäàƒF ›m¶™Eð†,6“ºÚåÇ*kԊߥUDk¼†€ž,ì/=WϬ+U'êÃÒ©jÿ©jüÏZgh ùU:AÛ“9n{j>´ý´ÑÄ(Ö©º _‘Ùîškàp‰þÌN-í!ªåïÒ‰5û~ªY–½´Ã¥e#PRËÑÓO?m–†Äälºuëfèzè¡!7âÿL™2Åfµòµì±Çfaa6"–Ì0‹òÆoØÌ89z‡ÓO?=Ì>;–Û¿dРAa«­¶²™b åɇ)\yå•V€ÙqX(ÃP !Gm_pÁÃöÛooû²?ä`˜uÇP³ÝÖZk­°Új«…fêçœwÜѬK b)#îÎ;ïl×k(&ÙmñíÊA@/­ÁjͲÒõõBø£rZæ-}'ÿÖw„cðIÒì«FQ?²‡ÈƪhUIséGUîÍ P²ô!­[·Lgˆ‰a£l߆À8Þ¥K# ™Î`…!¬÷ß?´mÛÖ¦ôg' #¤ˆk"Ï=÷\8ðÀͧ‡mýÇ~Ȫ‘+[©ù ­´‹PiaØa2¬Hu צŸ´vNš4)Ì;ï¼µNᚉg>GíÛ·¯u¬¡˜ÔªÄ7Œ€^”œËlŸï¤ L–ò/›½Suï`m(HT–¢ÿ“®©ó¨Ï¥Ð÷„£üÛÒžúžXº8Ž@ D ää«J¹DÓꃦ×[äì8LV®¶øu+b‘#Õ³z{…t-½pÿSù=oÙ-Ô÷u¨ îÔ- ï½#Ðr(™ÏÁ³‡µJ ;³ÑðçÁ¯ÉÅ(zÑâp:Lº…£R ^”k0km}w;¥6¯Äpª’YŽªop‹G@/G0hð°šÎgjòÓÒÝEŒ¥2—ê@@ßÝ:jémÒ¥õÝ™qu´Ü[é8Å@ d–£b46]¾:zh¥wÕ{? ÔÅ(6z¹‹BôNŒŠnÓ×§ïì]e´ô„¦¿š_Áp* ª!G!r›14†0ÃK)C…çù矎>úèFÕá';ÙˆµÕ>f=ý÷¦ìã¾]50cí }ŸKVM‹½¡Ž€#Pª†1Õž ‰ÊÇV”Ž{%Ž@S  éßU/MLjÕ×ð:Kƒ€¾¿¯t%r]Rš+úUG R(9"Èá7Þhé>”K-Œ=: 0 (Ùªe½'£=Bœ£ýöÛ/tèÐÁœ§_|ñEÛ¯œf¶$c,«Ül?¨S§NAyÑì8D¥&Ó=Ó÷‰Eôõ×_Û1¬OJk±ˆ–M¼¥(´(ÝJdkq.½ôÒx(<ôÐC˜r±Å³(ÙL±G”¬Ö¢| ›K;ì°ƒ›\o½õBÇŽ- d¬„²ì§_Ä@"Z¸KóB@Ä'¥¥Lû?¼yõ®Åö†”ôÕ2ãÐÅpZ ††¨ð™YJNŒvÒŽR":ö’î(U•µe‘EIÈt¯8CÉ(FPrÔQGYÒÕå–[.“@v·ÝvK”“,!Éì­·ÞšÌ3Ï<‰ò¬%}ôF‰"][2Wê[c5ågK† fõ‘ V‘¶“¹çž;A²$°Ši”ˆøXcHR»ôÒK'<ð€%¼U ¢DSûíõ‘W$&1JH<+¢f×Rš’DSÿ‘¬„öiæC_èÇᇞüûßÿN(§˜FÉwÞ™(Z7y‹,Ù¬Ò„$ a(úv¢€”ÉþûïŸl³Í6µò­ŠC€ûM +ï)]^º¨té¬Ò™ÔàZ¿í;Wú4dzùvm¬ª }Ÿ½¥û÷Z½ßa5ÝoÞÖʸÏj=Üëó¥èAQorôðÃëI¢HÙ‰¢A[Öz¶8âˆDQ§¥Î0²ˆ¢©÷‰Ò€$JäjäˆL÷dFX,QÄêDiBEÂÎ!*¶‘AQâÚdèС™s”Þ£9‚PEQàG«‚4f #$I¢!½ä¤“NJ…Û¶!G19uÛ¶¢Z'²HÙ:´qìØ±FÄ ^Q¾ýöÛD)C“Œ»|Yè/˜©,–"‚Σ®4øwåçV&vú^É}t²?•ùýø÷âßK±ï’ «éÁbCh,I“ÁUL ;Ë,³˜£µ¬Cß"†ÝHP‹Ê"“ãÜ´0D…HÔä<#UçG!ê4ÇDH,‡›¬WñPå)³ÎJ:6m£>"a¿üòËaÙe—µ¡²ìà‘¤ý Bp’áF‰ýúàƒ,"wì“,eÖOYÄbQ_V1²b-ý‡tcý@'VqW¼éù8J‡ÓwÝ)?â8Í’’£H"ò‡„‚óÍ7ߘ~þùç¡òAN/¹êƒœpNR’@Ž˜Ý†¦ýŒH;’–\õ‘nÈ!ACqt B«•wF)D¨Ÿ~m¼ñÆ™>Ñ7í¦É]º¾^=èe¹žZ;Tº©ˆÑgÕÓroi}ÐwËCå)>H.Ž€#ÐÌ()9š–;wŠÊD°†È@vÞ}÷]#$—茯. •µ?ùä+2jÔ¨°á†Úù»ùæ›­ˆÎO<‘¯šÌ~–¤¸Ú #FŒ°!“LVˆÊýÔSO!¢8º{õêU‹dP©0DŒVP“î”î —çø kž7§øœ§*—×÷¾iñ«öG ’˜¥’ƒFNÓ–­þ‚ .0+|‘,»=픯OèÞ½»%šÍ×nfA†äxV^yå@>5f¡!{íµWØ`ƒ lf™^fa£6ÊWMfÿŽ;îh³ÎVYe›IÇL3Úvï½÷Ú°_¦`+ ßÉ),µÔRF²˜‘Is©jpÎ&–ÑÝKÿªêžxã B@ßóT£ƒUx¨–]Ø.èD/ä8U‡@E¦!–Ö"|€²Õ’Ù^NÐ3Ëe!Ié¡/¬?ï½÷žM×ÇŸ¨PùðÃÍçˆa?¦áã75ë¬LZ*\À0_—.]ìüÂÏô’å@@/@.›+}±Œ DyôaPjA¢ûânu÷e}÷Ä@rqfˆ@E’£fˆ³w© ÈCŽþ§®0”öœ^Ž^½ ¿×Æ6Y÷E'Õ1VÚ]÷€û™ G ¹!PQ>GÍ \ïO³CSÒ0é§Òcš]ï¼C! Bô± b1¼  ¼#àTNŽªî+ó—Ótí6ÒÊØ¿te p¶š±Š¬HTFs¼Ž€#PLJJŽ,1(ða1Ûߨºhêâ€À¾*C­=¥Ó²p’ižÈzô«zFàÏKEZ5Ï^z¯–‹@ÉÈSê™?r$f+CÎ?ÿüpôÑî6RßFE·‚i;Kw—þXÑ-õÆ• ¤QºØ¤$GÀhF”Œ1^9ÅÂ>ûìÓŒàó®´VT‘2”öM è¯w±~¦âÇÉz´PýNóÒŽ€#PÉ”„]sÍ5A‰XKâû"DÉ[mª>Aœ ¬¨„¬áî»ï«­¶Z8ãŒ3ÂÀ ?¦Ï¯ºêªˆÕõsÌ4ŸXåX³ Š¤$Ù}÷ÝSæ%5U²ØÌ5Ž;î8›Þ¯hµ¢e{h¿ýö³¨Õm|ñÅ­‚@rþ%—\b×PbÚЯ_?‹šMÄmâ)®•åC¹×,Õñ˜h%¿ 7ÞxcX}õÕ-½ÉèѣÀ,¥JŸ>}2Á-óµ#ÖãË’#@ÚˆAR,.Ž@-d=ú@;.— ÒÅpš %!G¼ü×Yg@E¢T+ùkxä‘G,oÚ“O>n¹å–pà 7¤o¼ñF4hl\sÍ53ÃpäXƒ„<þøãVŽt‹.º¨‘*†Æ Qä@C.ºhZèb xâ‰+i§v ×_}¸ë®»Â…^ GœŒràr)ùl`}ÓM7 J„k¤…!Ad,püòË/ƒéZì#ˆ2nܸQãDÒ¦Ÿ‘8?>\uÕUví•VZÉUg ’GL'Ê#ùÚaý£«‹¾QŽ û5«þ­#ëѺUÓbo¨#àÔ€þùÁ¨ïRµÎ,%Šb;iGi7i/)I8U]mÙsÏ=‘Û©t‰ˆI¦€¬2‰rÙ¶¬1–Åž l´¬÷Ê•–œ|òɉ¬B‰"\[9¥IÞ~ûíDd%3fŒíSðÈD–›D‘´m[ѵ3õ²£oß¾™6°-¢–y䑉HP¢@‘‰rº±ÛD֣䦛nJÆŽ›(ðc"k”íî¹ç’yç×ÚÆŽgŸ}6Qäk;¦@‘‰òµÙºˆV¢¸‰¬e¶- Y"2eë²~% "™üøã¶}ÄG$´µ®vXAÿ()ÜÇÒí¤=¥d^T:”èŸ3©1 úíøyÍ7ÝÛK!Ñ3û÷Ûü¾_ÿN[ÞwZË‘µäã?¶á¥¸“á©o¿ý6nÚPD¶fˆëùçŸ"%á¨£Ž k‘¸•a¶e—]6`}ÁbÄzÇŽÃ}÷Ý—©‡†½¢ßlíµ×Ž›´ÈG}dC}"3–d0«Ö×_mÇ;uêDˆlöíÛg¢n·iÓ&üòË/vŒ¶œwÞyv«©KÒ‚¥ !º6C€œ‹uû÷ߟa;¬°8Ž@Å! —'AùÁ“^ÄÅpª²äV#™¬¬Aa¹å–3øV‚LD‘y:®ÚðÓ3ϯQ%ð;ÒUøõ×_mX¿›|I`I"‹OÒ +¬`Yì×]w]s²f?yÑ0#ò¦á»ƒ 4—p]Âþùç@îµ'žxŠuîÜ9à>|øpÛ†@A mõÚB? F-[¾¶ä,èZ˜@IDATª·XíÈU·ïs¦E@¿uþIáÈxvÓ^Ékw¦F ,äh=öòå K.¹¤%ae8J~B9ûJâXŽãÐ@ŽpœŽähÇw Ó1|êÙ³§9Fß{ï½ÓÕ·×^{e®»òÊ+Û…¾Ã9›™lX³Ðƒ>8à8]9äClV0ú(?«púé§[ÜBê)V; ¹–—q&AàTÕº‘¬G™Â›ä2^©#à4%eKEþQø0ÕGŠÑŽú\ÏËæF fx·Žb‚t²tŠ'³©²ü©¥‹#P Ý7»hÇ?¤«ø=R ßpª²‘£ªAÈÚbprÔb¿úFw\÷ÎU2\äèŠFWæ8Ž@É(˰ZÉ{étG ´ ÐåNIúkški¯ïWsF àä¨àù©Ž€#àäB@#bÝ"=3×qßç8•@ÙÈq}ô)I“&T®œ…¦NZpêj'õ4D~ú駆œV5çĘSUÓ`o¨#‰akYVu0G º(9"#)Aê›É±FÚ‘†ÊK,^}õU#.äGƒ”C véúˆ¡”+&R}®õÉ'ŸXH‚úœCŠâ?‘Ó­¡òàƒ†·Þz«¡§tž"‘‡þóŸ•ÍUˆ”21=L®ã¾Ï¨4ôŒøAm"äP¤¿‚·UZC½=Ž€#0e#GÓµ$ǦûcI¹çž{r­ß.’ÝvØa™`Åb××Ð692l¿ýö™»¡u 6,¼ð =½ ó FD8o¨×޼s…Zz?Ï(27ª¾?¤ýŠ\¯Wç8Mˆ@ÉȉZ‰MÜ"^tLåBJõÖ[/tèÐÁ¬DLÅ'ˆ"/S–û%©k¯^½lš¼ò¬å;³ýDȾúê«cuᬳÎÊ$²;·Ûn;[%A 7Ûl³@4íÕW_=6dôèÑaÀ€–Ì–D¹”AH4»ß~ûYÛHeòâ‹/ÚþìúØùØc™¥‹t#—_N¢îiBÀIP+׊BÌUW]ÕÎ#¹múXh¡…,†S>+ÛK/½duÆóÀˆNÄ…"™/±–¢ø6Æqºøâ‹m7Ö/Òªœzê©!Wl¨|˜çk_¾ýß¼óN2,„À½@âß.]ºåÌ Ä‡úàƒ,5L¿~ý,â9–8bWÅĽÊ_æœsÎLâa«È? G@dßþÒ³d<š»Â›ëÍsJFŽ 8Äâ%IÇW^yÅš"È6Ûlc©@ˆOÄ úè0pàÀÌðÊE]du|úé§ñ:~«lgç5‹e! Ä7?~|€,\xá…ì‘ ’쇬›HÛ…ö>ú裶¾é¦›%ˆ ÙõQ–´%ìÇJr衇fr®í´ÓN¡k×®¬ò„N0Kí£ž]vÙ%ì¼óÎáÜsÏ­E许âŠ0ûì³aØgŸ}ÂÀ%¦ð‹y߈±D}³„tç¼tÄçÌ3Ï \põ òFäñÝvÛÍÈáAz÷î]«~žëù0Ï×¾|û!¹ "‡~¸Å¤""9yíH£$¼FD¹? OJÒkeÀ+Ê2Ë,cßCÜö¥#P èwôªÚy‡tp5´×Ûè8B€`CT§Î,%Šb;iG) ËzIw”ªÊ¿D/íD/úDm§^è–é^/îDË^KËÏ(iÕªU"ãDD%‘¥ÈÉ‚“Œ3ÆÖ54–ÈÇ&QTmÛ–Å'ŠU$ýû÷Od=²mY ’qãÆ% þÈ?¸D/aÛ¯t!‰^À¶®ül‰HYæØG‘{챉ÈK¢¨Õ‰^ìVŽY¥>ÉYŸ¬,™r .¸`"_§D'åTK&Ožœ9¦(߉†²’Ûo¿Ýê‹DÜ‘BÛYI¶ÝvÛ fo¾ùf,–Y*=I"+•µ“<ð@¢ÈÜ™ã`+Ÿ+ÛVZ“äœsÎÉ9Jd½±mYÁ’k®¹&s,®Ô…y¾öåÛ¦":‰|¾°g‰€1øˆ,'J.œˆè¶£²±j" a"r7›|Éý"ÅäØSº¼”ÌÁóH‰.:“РߎŸ×òp«¹oÈ·¶¢ÿ-ïû÷ï¼ú¾ó’XŽðgaÈlþùç׳!Øð!„á¬8D‘FÙ?Öš´`ÕÁ!wÙe—5‹Â}÷Ý—>\k==dWë@ÖÖ)k­Û´icÛDÙÆq«maØ-¶á­|3§ècØbÉÁbB›É·…!#"g3œ…cz”5ÖX#®Z†ôVc(‘zÒÂðÔ—^zi˜gÞ×Ó†õ~Š’®œIeûu‰6Ô%uaŽE'WûòíסݴŒÖ±Eiß¾½¥sa›ï £tìØÑ¢ŸÇm_:Õ‚€^ŽÕÖAÒ¡ÕÒfo§#Ð’( 9‚ 0TI Ëø²Çgã7¶5/k”a±4Ñà ’uÇ|Qd±!4†fâ–˜(œ_ˆÈbQg1ÚÆKœ¶Æ¶}þùçA–©œç媶ø¥Ûǰù×À…$·QH§…VHL“_qÅmª;ùÑð ÂT‰P?Ö©_ýÕüz4†§XgÖ "$s"³Él§>êÂ<_ûòíu2ã ’†c8ÖAHmbhsFÂ0'äÔŨVÄ^RÛï—žR­}ðv;-’&ž•S²ùë@„²-C ™0ÜQ¨ëE‰•…—;–fjQ–Yp Ó@^xá:¿7¬sÏ]¿µÄFÂZDÝÙ¥Ðú d´Ò}›bC?þøc›)‡¿MZ&Nœh„RINú8ë/†Ì^cÈŒöø|Ÿ|òÉpå•W9~Ûiüq¥ d,- Ùác!Ì–|˜çk_¾ýÔ‹E‹ð„PÀZÆ÷‰ÃrX×÷Nû —„} ¥š{´®Ë ÒÉR˜5ŽPS…ç_c¹Úáâ‚€î«ùTî麺‡š6új! ò2Ž€#0%%GÓ]Ýw4¦ÇcÓL4³Jae"”$îdMçÇb„¨’„öbýÃbqƒøÄÐ ùÚI¨œ·cÌ«|効ßÉQ1ÑôºÒèÞ:XÛ;ˆ­ŸÞï뎀#P”lX­2ºÛüZY|,0 A˜ö œ}öÙGŒøð3b(Èç 3*4 ¿úI¼'G ™ p¹úÑN$i§fÒï†#ЬpËQ³ú:½3ÅDÀ-GÅDÓëÊF@÷×ZÚǘ÷Ò² æ™]‰o;Ž@“ à–£FÀZè,«F\ÂOufŠ€Ój—žØL»èÝrªi‘øª¶ùåi¸¢x‡Í7ßܦÑו—™wXÎ8ãŒò4Ô¯êT(ú]ü-«iÙÛY‡›íæ1êÙ›‚ãZ-ÿ tÖl»[öŽM‹ÿRÓ ÔZÛeo7 bprÔ€¯bìØ±aêÔ©æ3Ó€ÓýG Å!PC†"Âb¶ZÇýqÙ’ð™¤Îž)½LºeKêxú‰Pf©û’õ8뮕¡y~ÉJB ý€jÒv除¨-$"6Á£©™Ľ!NN H˜o?Ó¹×[o=+Oü ¦ô#ù2ÂçÛÏô¦Â3EŸv <8X’D¸wß}·Åc"†R’—eœ9•/s}<‡%Ò›m¶™¥P!~Q’cŠ?ñŸè;1I¯áâ4 E’H†ø3ÖJ—·"O±Z²^¯þ/"Ý¡…ãPŠ{€ûàoÜ{(÷"J “x¯jÕ¥Å#Qnˆ 8n&næ&ž…kˆ)9òÈ#Å"²Ä­"C‰ÈI¢?‰ò%£FJÞzë­DSÑ-il¾ý$pÕ4õDÑ”DA.‘œvÚi Nï'Qtè¤[·nuî¿ä’KÍ–Jc'Q4è„6]wÝuvŽâ%й”P†D¹Qd1²d¹ŠÓ“(µõA åzK”s-Q–{K°Jy’×Òo„¶<þøã‰& Z™ˆˆÙ~Ê“`–¾+‚x¢Yg‰4Ú1ÿ(/º·ùé‰gøŒÐ·'øÌBÄË'!òOƒ®ŽA¹ïîEîIH÷(ï5O*݈ß<¿ûæ <´J"[l±…¥ÿ !+1nHMA|››o¾9l¸á†–Jƒ†ˆŒXàÃ|ûGŽi±Þ øó`ùÁCÉ-šHÖXb|û‡ ÈÑó¸q)N°F!\«NZZµjeSæ DHâZÒ‘(ýEìf¨àФ2I Il ÐH Éu×]×¢ncí"Xâm·ÝfÉw‰îMÌ«®ºÊÒ†ì¾ûîé*|ݨ:ø®F£öÂÑ2þSoð,ÙªÁ\ÑÔܣܗܧ¿×4öìç¯YE7Þפ”lXhȲXpBIYˆŒÄmâÞì°ÃF Qâ~²Ë3̳Ë=šá1Òa0DIÊÎdŸo?×&Rs¢Wãl…T%3’º2×Çs!TRf´k×΄‰t´½G™þüûßÿ¶(âñ\_:UŒ/œ8¤Æ 庋#`t·‡2 Ãl ÷¢ñ~åÞuiÁ”„‘2b×]wµÌòGy$9(•¤¬dƒÏ·ŸTø,Åìò,I=õ‡Ô¹2ÙçÛOž®Ï?ÿ<Ó,;k®¹ff{ÚßÌfΕº2×ǰ(‘h–¾=ûì³æSEäjúBú ü—bhOÿþý㩾tªh5ÂBBŽŠ/™1žß1¿5þˆ ¤Ãašþ£–¯Q$Ê>묳ì0“1HWÄÆÊÅ_lÏ,oó§.¶‰%Ï,ßäv¬Dyï½÷,G&# ‘믿ÞþâëIÚ¤uÖYÇžÝÔõÚk¯§Ÿ~ú «nl;j.a‡ qF+gÍ!_´TJBŽ>²³å–[ZÂÖtDdöáœMTgH+ùîXÙ\ûy`ð0!Ç·D« òa²Äª4G šÐïb•ÿÊ!FsHK*³Ï>»MÚ_Ÿ]—aoö* }–`‘k‘gUc…gõ¥Go:+r<¤àÿø‡=H$þרkëü7ß|3üñfůoš}÷Ý×N#}‰ÆI"ΈAœ¨RhiGêÜ #È;‰÷¢[ŽBK–’#ò|áÇA@_}õU›6pàÀ@fzH ³ÕHHKòQ²ËçÛÏ?ÁC=ÔÊ2¤ÆC‹’/#|¾ý{ì±GàŸ ~BXª"#ïW}¤®Ìõ±úϾQ½{÷'t’=ü°hñÏ>п'0rqªHŒxÆD«/Ÿœ¬dèСf=Àçaf, wÝu—MZ°m†ÁñìÙ³g­T2§œrJ¸âŠ+2peo“hºS§Náù矷2£ìß¶˜¡ºà‚ ÚµHÚŒðlù׿þeëÌ6e8á¾’ü¹á7‹uùñÇíYG=üÞñYÄ*„@„È{HbhþäÅ?xv°æƒ49|)Ï<óL;L«¯¾:tïÞÝúÂÌßW^yÅöŸþùfñ~ðÁ-8áÏyÀ˜&çh2L¦}úMN1¼xÒþ4fK>Lsµ#ûÜ·!í­¥xîW'G¡Å ÿZ¢Ž‰›ª ÙjºF¢éü ³Íù %k¶u>˜¡¥ ™í¸’o¿†ç‘¬DÿâbQ[êǕ衑ÈTÐ~ÚñöÛo'zèÖ*_ß ùB%z@ØiôQᦫ‚ÙuÌJËÚ*SrBŸ\*ÝÛ8dúlµ<#„/ÈP[é‚Òťݥ½¤Ó}ÉG}´ÍZ’E'ÑË;ѰR"ßÁD›ÉÊ9š¤è…oåôBÎÔ¡?TÉÊ+¯œs›Y¥Ô#kmÒ¯_?+£—½Íœ¥Nýq²ktíÚ5áÚ{î¹g²Øb‹%:KdÁµk/³Ì2vMfÂ>÷Üs¶®?6ÉV[m•h8ß¶E¢¬n½ðm[¡F ÑÛº\ 올$¶-¿ÃDä/‘qͶé‹ÈŒ­÷êÕ+Ó ½Û>åH´¶Ð^DÖuk«&¶XYýY´r:LÚ¶mk3€™¹KùwÞ9ÑŸM[i´òóÏ?"?ÈDÑfþRŽô,Á^®‰|;“™fš)™KôGÒ0qMdå·ïˆóô§/y饗%¹¶kèÏe"âgëJm×QLhåE( kÚ5hç_vÙe6ó8vœç:eE°lW>Lsµ#ÖQè’ëHבv•v’Î'ꉥÓ&¨®½#ý¼êÆ­Á_ºnœz“£BoX/çTºÇyp:9jÀËA¸AŽðå˜[º°tié&Òé¾ÞHŽî¸ã;&ˆ½ eÙµmÂ]ð¢æÅ IË-·Ü’(&ZfWz;’#BÈ¢‘hv© •Yý£Hxdݰãò[´c²ðZŒØfÅ!³uÂq ò9´mBvðçri‚C¨ÚÍŸ<ÂŽ@ªù@Ò(Ÿ&G²^'²d'òñcþPiø(‘Ÿ“H›,aFV¨'’#H$ QŒ¶äÊ+¯´þDRÁA G²ÒÛº,dv sÙ6ý MòÌHM±z"1}ã7Yõ¬œfÛy² Y²’%²ôXûÁAº×…°%ôk¡`Û—&G3Â4»±þB—5×ï­%æú%¤ù9¥NŽð»î æ•v.0GÀpš †×xΠqx-ïµd²cÌêD•à‡ÃP ³^ãŽÐ“=Hç%{›ýÌJÅ?P±Æ¬X¼ C!C† 1_¤M7ÝÔ¶™MšO˜=‹ˆhØ’a5fÒ"L¾ˆÂÚM] ?1äÄL[„Ù»ÙÒºuë ËU }²|j8GDÂÜV\qE~gx‹—IZ:è @¨„ *ø2Ê’dÃkìK—g¨‰8Çp&sÎ /†3E°ÃG‹·H6. ©ä—¡9®O ;¦Ëá&!K›Ï‡§MDÑ "®6œ‡ÿ§ˆj¦ +3´Vá†o`á„ Å÷!÷,êÒ‚àaåâ8Ž@S"À‹K3Ï›:Ÿ9² äl±Ä¢ íœsΉ›-!øø]tÑE6)‚gQ"AÀçÀŒ»C›6ÄÌ-Ì&Ë–H”â ãq2B\4¢ûCRðW‡@d þA¨l@Üpà F˜ Aø?½üòËµŠ¥Û«!DóiÂ_ â᪯Ì;ï¼v „_¨(øS=öØcq3ªBÄuò嚌¤‹“ˆUGN=õTóñdÖ0~XE² à˜eF˜2‰¥½É—TéäZ°ä~U õÙÐØnñ¯•.Ž€#P0ñ_8Ëø*ød âHÃ6/O,1L£ÎÈg“+¢do³ŸÉŸ…x` I ĉ—7é‚pøÆ!ËÐ3ÏûLwȦ¶*LeeZm®ú ­ÃË5|Ÿ’>RBø2Çx²”yÙ¿H§êw3¢€È%ÂŽ?^<˜ºŸ~>àp3¢)Ÿª¿ÁÂôz¬HÊq8]Äk˵?» }ƒ´ðbïÔ©“=bŽAZˆâ5«>‚Õša¤ôW]ç_çcô/ª«l]Ǹ.Ãô…P+iÁ¢Å¾¸ŸkbI¢5¿ŸtñéÖ‰ýF¨ˆ_]R¦œ—ÝŽºêJ«i#“.¾•òÿAŠ™îW)¿ñ¦{‘é.•‹°’æjb1¾)€Ø š2j#ü«=z´íÇ4˸9N’X†ˆµÁ9‹IKŽãÔ"`"ÁÞ(G G„±k†ª4•×¼bÍ!UÙ"vÚ±a6Œ¦˜š¿Ö(þ(hZ±I‹8qF!Y-ÕðxôÑGTÅcq‰S'³I8Î:³_²ƒ›A‚0 ã\‰5 ¢†‰™‡˜ðâ…í˜T·úãy¾tÊŒÀßt}ÁÒf‰—«cPî{@÷c$DÜ—Œ†Äm­º´d¦ŸrÑhɧJf=0¶Œ£LŸýäcÌšÌRÀ¢¢˜%F~øWS#þÌ$aÜ™Ñ$ Q‚„DÁâƒ%$ÒSˆ@šc¯d‰™18b’ÆyˆºQpªìÛ·¯m—(¾´- ¦wü¢M¦ÀBÚ°BEÁ‚Æ´W,Pq&mN±X6½,´þô9¾î” ^4é— / Rƒ‡óËÔ¿l3E@ä,wîÓ y×zú¾m¦½÷nÕ…@I,GÄé,sœ>‹I™0úìÇš1Ђə$¬ !FdJQ§m=ß–¬RQ²g>Äý,£3'ë˜ã´Uچňva­ºï¾û(’HSRd[„ð5`x«ÄUµœÓvc=qÉÐa>‰ímLýùêöýŽ@!­F¼hXË&ºœWë4x¯Rë.-’XŽ ^ðf Œ1Âüq:‚L0žŽàÓC¢X$î³>ˆ=Âð×C¨;=æ}ë˜æŠ&—(â­ùû`ÑÂoàÆo 1·åÓþGX¾² Ö.ÚS ÈÆµîVá ¸D¡_ÇbÕëõ¥#ЄD2”~ÙüuS7á…½jG DcAú~m@5~Js@ Þ MÚœ±á„àXŒE†i¦Xmî¼óNÛÏL ¢¬¦#ÌÚ?˜æŠŸ¾D â–DÁ ½ëŠ/‚ƒ6ST!FÄ5‚ÄERE] ™AîˆWB0³6Ú(^–ô“~ÅkÐ'‚œAú²¢˜Ïf ç1䇅¸&H}êϾžo;%D #–¢¸í/ž~ ~©‚àþŒ÷¨x=÷Ùvi¡”„-3°˜Á…~E8K#8Yï-_"†¨¼†Srzöƒ*ðƒø$X\¸¤…!°X3ˈ=é!ãv¾YÄÜ€Œ0ÄEÐ8ê Ð3ÊH ®Áo‡©øiðašþá4Žâ›”œsh#Ë‘¦ëÁ1]‰<*q¤9ms¼>õ§ëóuG Ä@‚â '>gâ˧ÄMñË9u"ïËôýʺK F ÁŽ‘"!8ô¢à8G883,•=u“gÌ*ƒtà[ÔPÁêC@7|˜pÜfV×lq æ:´¡)¹´Å±; YÑg §múQÊ'X¯°Ñ×ôÐ^vyâsàçéɆ՘¡–å ­?»Nß._MsT8d™’ÂBÔJJ²/œ c4rRÜîÊ…‚KÙ¨ù“䎙=“¤ø[ç×ßÝz$Z¨””55ÆÄ4ÂGˆè° gM—á(r¹8õEÀÉQ}û«| 9ÂÑ.;¤“£¿`òµ2#Pó'd ôú£Ö IÆãÿ99 -TJâ]*lžÃjĬµ˜щQ©Ð÷ë8Ó!‡)8×ãÛt…}‡#P&Ò~pqÂ@z_™šå—-'ÍŠ14…Ïêâ8¤ˆ/›è‡T óF85p¦}ŒXwrÔÂoÿ×Âoï¾#P"²_@%º¬_Ƙ!iâîÄh†pµŒNŽZÆ÷ì½tÊ@|•»~}G ¢HŒXº´pšÕ°Z ÿ.½ûŽ@%")ŠÂxé°žÈÖ‡-*ñÛjymŠDˆû’)Ñ µ<$¼Çµˆ­Z;›bc³Í6 ÷ÜsÅ2"!è£à@ML!¢?ßçë¯¿Ž‡ÂI'd¯»wïÎ8ãŒÌ~rœä‘|dÄ"AíÖ[oÈevòÉ'‡C9Äò›eNðGÀ(5¼hâˇkó¼1ÿ#¦ò»:å¾t?F’Î}ʽiä]Ëô}«M—–†@ÉÈæÌ3Ï ×]w%j=üðÃ-É+SîÉBÁX¬#âõë×Ͼ‚/’L–ä®Dª:thxíµ×ìØo¼ d©Fˆ²M}Ä%"25ùÇ(KšGÀ(¼xP^4éeÙävò ïU³îÒÂ(é°Q²»víjŠˆT!<ð@XsÍ53i8N9囆OàE,I¢Q“à•´cÇŽµô|oGu”EÝ&ÍÇóÏ?o‘­ ÚHfû[n¹¥…µÞ}G ìDR”~ÙðïÜŨD¢± }¿Vb;½M%@ Þ %¸T¨é™HØä#A+죴oßÞ¶~ûí·9š@Žìƒ@-:-1‹ý‹/¾hQ¯!F°—Yf™tQ_wÒ"‰Ë8S-ýï¼´­ñ«9u#À}ïY#ð²nÀšûÑ’’£™fšþr$eýüóÏ38ñÅFŽH%‚u‰ŒöX˜HÅÑ­[·L9V¢O'ñ8/Åþ¿ÿýo­²¾á8%E"_8ñ‡_>%mˆ_̘ñ¾L߯¬»´`âC«l¸õ±Ç Ÿ|ò‰µaÔ¨Qaà 74âC®4’³õ Ó³Ï>kNœÙ%¹+Ž}ø&!7ÜpC˜4‰49.Ž€#PfâE\ú°Z™¿¿|N C¼#AbBe¼gsžà;›7%õ9Ê%þD¡¥—^:¬¼òÊ6tƬ6„gtP6l˜%fÝsÏ=Ãé§Ÿn3ÚÒuÍ9çœæ°¿Ò‘GiõtîÜْŦËùº#à”^6þ‚))ä~± ïQˆ{4¸å¨@6§S*&ñ,Ã`X{ Ié õø%á´Øb‹îß|óMX`j}üñGx饗ÂꫯpÎÆ÷h¡… Ÿ~ú©¤ZHùF}¨ùãØGçĤ”“µ>EJÆî©²VºD@äaÇKæïR϶­ÑvZòã¥×Å(7©ßø·jË)¿ñŸ¤üÆw¿#¡ÐB¥ì–£ˆ;dÍ–Ùf›-CŒ8–MŒØ™"±’†»ýöÛÃzë­çÄp\ò" ‚DòïuV$\* îÏô}Éz´&UTC½1¥C šKwÅ&º~F;wΚ¼é¦›šèJ^­#à4^@Ù/¡Tã§8M†¤È‰Q“Á[]WŒå¨±°-¸à‚æ£ÔØzü|GÀ(:ñ¥9ú£èµ{…Ž@ãàžD¹?îW—Ž@³!G…|ø&!iŸ¦BÎ+¤LcêÆÿ_©V­Zr)/ãT#éçV«Æo°y¶"ô?išÀ7Ïžz¯ê…@E“£ãŽ;Φô§sªÕ«wY…Ï?ÿü€ã7Kfµˆò´ÓNË*Õ°ÍtÝõ­á…^p€åˆ«ï¹^Þ¨`xáDkQ\þ¦}{Iç‘Î%CŠÓvk)ÿšÍP¿úRiÎ?WZ£JОxNÕµ~•âlýƒçkœ°¯Q|$I,]Z0MŽšò{ùç?ÿ™ "Ù”×ñºŽ/^:ü;çå„òr‚ A„âqHÏ#2£»4 ªZÈÁ£R¾–"ÜcsHP$GÄÌÌS­Ç{”r”G]Z0%û—¶Ùf›eœ¥qœ¾ôÒK3°?úè£6ÓŒ\jÛo¿}øúë¯3ÇâÊ”)S,Z‡Âúë¯H…ä´½zõ²Ym»ï¾{øî;f^ëîÖpÖ'Âl½õÖ$·Qn¾ùæpçwÚf]m£nâ/­³Î:\’XKH]uùå—a—]v±\po¼qxâ‰'ì>H°Ë¬:08÷Üs3û©ïä“O$Ñ]uÕU‰u]*G€MÔHŽ F¼”È òèDé÷RþÍ»ƒo„kGé.Ržý- cî/î7ˉûbŽr¯Fë‘V]Z2%³?>œyæ™áºë® cÆŒ ‡vXØgŸ}Â÷ßvÚi§0räȰÒJ+…N8!ôë×/<øàƒµ¾—<0@ R¯½öZØtÓMÃ|`yÔŽ>úèpÖYgYŽ6’Ñ’x–á²ë¯¿Þ¢f_xá…ÂrÄGØðq›a5$_Ûˆɹúê«í:ÔMÜ%¤®º!~„%xå•W,ú7Á,ßzë-Kríµ×†Gy$|ôÑGa×]w5ÒF}‹ûî»Ïp4hP¸÷Þ{9äâT#ñŸ7/þ±c1âÄ ):×A˜ØÇ6ŠÕ¨dØt­–&ãÕá.Ò¶Òý¥#¥/H[‚pr¿q/rÏñ ç^Œ1ØÇ=-Hú¿êÁ¸„G‹•’‘#}ú„Ë/¿<ôìÙ3L:5qäá‡6B´ùæ›Ûv]d#WÛÈ׆ÅiÇw´ó!t0¤®º·Øb‹°âŠ+ZŽ8¬D¤Aùé§ŸŒ Ä’ –´÷ù矷Ô("R¤@êÈ?7`À#Žv!ÿpªøOœ%/ž4ù‰C"†Ø8–>®M—"#ðŠêë+…€ séâÒË¥ƒæ,‘A~"J±±{’ãå]Z0%%G /¼pj¬6Xa°àôèÑ#³¿}ûöF* Q°²T6]+$„ ‘/¿ürØÿýÃäÉ“טþùíÔ§žz*`‰²Æk˜u&n§—¹ÚFη¥–Z*SŒó£ÔU73϶ÝvÛ0nÜ8B‹çôîÝ;0,·îºëZ÷Úk/³”qœëÇļsÌ1GÆBÏõ¥#PMð¯[–W¬G¼dxéT/¾€ÙÏ¿xþ½ó’æ91BÜr4 ‡¦ødX-ÜpVm¯"e|ÿ(éÒæ(ÜoQ¹ï¢)Z3!IÜ›ñ˜#ÑÒ¥¤ä(¾üÓ /¿üòµük°Ö@_œ?4Ó_$R‚@¤8†`Mjݺuxúé§Ã!C,)íK,n¼ñÆpë­·ZêÀϨ[·n¶¥ª]»v¶žý‘«mLù'IfºEÉW7Sú.c‘!@¬Y8ò†e +Õ“O>iC‹XÒ°”5ExØV_:eD Mh/þc1BÓ#ˆQúå­M—"#ðžê뚪«Ý|Ò¤¥æ*܇ÜwÜ‘°³ A‚¼GÇ)ïÒ‚()9Ê…ó–[niNÊŸ|òIèØ±c5j”%¢­Éyc§à¼¼È"‹„áÇ›…ÂC5†ä²b b„ÅfĈæ,͉Ôã5Ü£ãY®¶dï[{íµmXŽëaÙI;‘ç«ÒÆ0Ç!\éHݤ5yöÙgÃ5×\pÇñz„ F޲¯íÛŽ@3@ £HxØŽûxA†"9¢L,§U—&B`Œê]N-u8ÇóðP)NñÍYâý)ÊVîÇ 1r£æ|Ö·²“#|„6ÜpC›QƬ°¯¾úÊfµ¥›UGfˆ ³½ +Ç{¬9pãƒtÞyç…UVYŶ÷Þ{o+ƒÃVl°ù!á[ýšÒu×µ¾ÝvÛ…7ß|ÓfÂA¼ 48#ùêžo¾ùmÀ¹+ÕZk­V[mµ0pàÀpöÙg›E «ÛÿûßÃÎ;ï^ýõºšáǪD fh¶ó"Š/&^@¼˜£•(.ÓäÈI’j"yVõî!ÅœÍwñ™´—”塀 ÷ïA–‘e/c;Á?Z.ƒ44DdÙᇿ?²9¥tcÌ ‡Ÿ‘õ­—!«I“&IÊ7ÄôÛo¿ÙŒ/¬8´|øá‡fybø gnˆÇ¬³ÎjÖ¤÷Þ{ϦÎÇá­ôyu­ãŠ\ÄX ¾ýöÛ…ëÉgRyŠŠsEÜ û)ô¼ó΋ò^:$ŠDE ÃÅ+®¸Â®'²ã-Êq£ÕQ^°£f²Ù±ìÏK/½åü1ʉ¤•ÁyJ³,rw­Ñ5Ëåi×¢bÌÅAƒE‘µì¥|½àUÚBiM%¦?/ 4›1©¦Ðekzwü<Ç­#?z¶‡*=íϸ?§ù9mëºÕEsôã?´h‹Fgy!®Ú|`»‡ Vˆ‘Æh Æ+%¥òc¸Î}ˆØ<ó.Ž€#à´úè\¨r·R±¯‹#Ð-¨ 9zæ™gŒh0,„Ì4ÓLFòˆ3”…?!†ÕúôécfSžEY$­Ú0¤ˆ@´Wã…: ¡9 }ûö ;í´SfÈ<\~dÆgLÙÊ.!hÄnKB™¬$ ·%¡Ì~ÀarcÁ7aM `«®ºª…Eiœ£ñV©üSO=u¸øâ‹PŽäøC€0¹8Ž€#ÐVˆ ýCéë¶*ßËu::u!G 49„Ñ@XBlòBØûï¿ßòjøÈÈÍwß}gÙ¾ýöÛBö¿ýíoF:Ð"as„P4,ÍüñFj° ",Î+ÊÕp\!k*3í  ls‚AhÑ*q¾†ÆÊžR*ÿĉ->[Ò’Aİ•rqGÀp¶A ù¯|+\w¡…2mÏõ×_o¥AR²d']‚á¢{ï½7üú׿»ì²‹ ½1$‡Üzë­f„ Yºå–[Âk¬aÆÒže! -$‚`¯²?²a/4/3ˆ(È.Éò¥†öŠi}0ð†LqmÆ5ï¾ûÖbðM [´ZA4]”UJJå'NAl!{¬­·ÞºÉ}”*Ó÷;Ž€#Ðd˜½‹Òï[R†ŸëtFêBކ!³ÝwßÝf«rÊ)©>Øþûïo³±ÐüÓlÏ=÷,˜eøjÑE ixo©Ü!C†€sÌ1t–øgØ9¡µ¢<4WÛl³M£KÊ`:Èp;ìºë®öï¸ãŽf´ð †¼ú÷ïß(OsûÝ3צ<†øN:餒3ÍJå‡Àm¼ñÆ6sŽ¡Bf´¥Ù}ÍÕÁ;Ž€#ÐBžÒù÷‰ õla9~º#Щ¨kø´Ÿ}ö™½lEb( › ™§vZ˳Ã;œFB0(#kóCì”Þ{ï=#BY»"†¤pôˆ­¶?ÅûòÍ0à 3ô‡ ÍOþz2–Ù@+…­š3[(´b¥¤\~0á8dÍ¥>4<£uµ/”P;NPú^ #³‰Òþ2ž«.Ž@WD@ïÁÁº¯ ô¼÷ëŠ÷ç÷äC ®ä¨X*Ù—ÈÑ>ûìSIvÏã´ NŽZF/¤“# ÷€†›”nAº¹“ߎWߨ¦óé+:­¾™XоÔ÷Ê~5GÀp:{«ÆmZp [É}U ©£Èª>™\VG Ó£V¿s/Ðp®‰Àou[kwÍ[ktW8cuqÚºd·Ií[¹P쌒»bEóÍ7Åvû>GÀpGÀèBtr„÷hüµDÎ:ë¬pðÁØ6Œª™ÙƬ¸r¢ …Ùtåòù1GÀpGÀè¸tŠaµJà#€l6²p! 6¶?ýôSÛ|á…ÂþóKwÞy§1EB„úõë gO<ñ„9ˆÄ_rå•WšçísÎ9ÇÈñ×òÂpä"C8CotPàšÈ¹çž›?-\tÑEÔb†“IZ"xÇ &Áwo¼ñÆ0jÔ¨píµ×69ßw8Ž€#à8Ž@û P×a5HÚ"=Þpà áÝwß5¢„cC´)„!¸ê +¬P Ä6lXUèöƒð!sÌ1G@ó„—l®à §Ž0â¸pÀÍT±Øk„!.^²!J0Íšk®iCmxÑ&°n^þùψõG;†\pÁv?Éã6doºé&ÓvåËðmGÀpGÀ¨?uÓqk /!ï¿ÿ¾‹]i¥•Ìc4^£vÂ{ö˜1c,6šeÔ䣔”šY†f b„@ÀÐçŸ~¡>”AY.Ž€#à´¯¿þºµA•–ÿÀ˜ýf¥ù[+A¿±uqÚº’£t³½zõ óÏ?¸îºël¥wïÞáí·ß¶ ± ¯1|…0ü–h†°6#Ö3Ôš†³Ð%"Å5Ñä@hxÿò—¿Øµ )=ôPsÅYƒ±ì²Ë†ßþö·aÒ¤If7”ñ [8Û*î Ñ.»ìbdz<[#ÖI;óÌ3 çùŠ#à8­¦ þóŸ+.ö©§ž*h»+>©2~øá‡EÝ©´BÑ^„#PíBŽÐª`üÌRšµ…]ÎòË/6ß|óá…Æv' †È6Ûb‹-Œh¤c¥– •í·ß~æˆkqÍË.»Ì²C`˜¦ÏÐvNÉ.¨TYìßrË-˜1DÈŒ:l^~ùe3´Îž‡AùV[med:`=çœsšQ:CzD†åXÇpÜÅp¶BS†#FX0mfó’‰I0LAOûD›zØa‡™ÿæ›o6ûL\˜0…Ûnºi€¼ aÏ—A½éä­µÖZÖéĤ€¶›v}ÕUWµkç뭷žµù‡¼ÑIMís>¿o;uE­G-I•œRi:¥Ù•z(-£ÔGiK%Ù¼Hs_}õÕ('‹M2O˜0Áök–[”!tá¸ìŒ¢^ºÂv¥+\ã•W^‰ÿýïòÓO?E©œ£4Uö7·!ۡȹuýᇚœòÕW_E9†ŒÜgVÔE50–²û}½c!Às¬´…ÒšJ½•PšM‰°S¨¶5½;~žãÖÆÏÀù7‰vtÇwŒ,Õñ´vPõ(RŸþù(²c§Üzë­Q%}ôÑQšoË'3;¦‰#ñ˜cŽ)Z†L$¢f[~ùƒ³s‡¥é"XQÄ*ªY¨ù·ÞzëH{.S…(ó;¶òÊ+òT°òŒòø;è´É3P×Ùjú 4’i§6,³ œª©Ì<ó̶#í¬L1Å6Û,»¯’u´6¤¼L5ÕT¦½Êïon›aµ$©®i;-g›m¶@Ê Czå Íóù}ÛpÖB­ü4ÓLcv—ÌF >묳šæžaÌ ’ åÇnM¦©½Î—K4Q”‹ÛäC)ÌØE“”µŦ ­íùꫯD¤Ò%}étÚeX­š;Ÿk®¹üÅ©0Ïë8Ž@ É—Áì]†Æ\¤`¿‰égœa¶¥ûî»oXzé¥S±v,M€ÁÖ“ G #!Юš£J€À 9ù(ª$¿çqGÀ¨Ü`‰V¢¢!5sC‚O8&¬@š˜IŒæˆãäÍËk¬a{·Ùf#SDC`d€¨ÌÐÅ®‰(IV[m5›•†Ã_õžp é/Àÿc̶Q‚îÇ4JØͤ4‹öGs)ÝÔ\¹²×±\©'¢sJÊ7ß|cjß’ü€#Ð4<›U4ña¾Rš ô½ÒJõŒÿâWB;\‚ÀªÇAÕÖchˆRÙQšó[ÚjÜ`"ÀrÆgLYš,óÇ!Efã:¥˜0¬WÌô XÞ"ûˆÉÔ¼sº"'ú.G 9ÚmXqæl¼±b•µÅW2dH±ÃíÃ6H†Øf°…ÿ ^tGÀpÆd‰Gpx‹% ˜rĈ¼ùãØ•"FäOå²îât$ÚUÓì™JªÙ•d/›‡ Óë5[­l>?è8Ž€#à8ݺ‘#f<à'cÑE5¿>ÙЮ½öÚÖÃ@KÄ4Þ³±5bɸ5‚¡`Ÿ>}Î wØa‡@ä{Õ—\r‰­ósê©§†«®ºª°Í ><ü%“¶ÃGÀpGÀÈ P7rÁùÕ¯~e~8>”ÿ«D¨_¿~6ý“ÐLÝi§,È+äÇ8!Æéàƒtrò¹çžkeàýšñò$lg§²?å…4%uqÊïKGÀpGÀpu™­F˜ŒÇÜÿnƒ2½HGÀ¨ 9âJGuTPàC‹ÎŒ¶è÷¿ÿ½U€ˆõûí·_Xd‘EćP^ Fýû÷Ë-·œE•ža†òY|Ûp®€Àûº R»‹:º8j®ÎéßÕ™ý¸Ý+äp*D ®²±",D(oÍ´|<©b¬ÍŒµRBüž=z˜wmfº‘—Yp «¡5˜»œ¸·írèø±, Ϩ{È΂âëŽ@•è=ú³Nù?‘£S«<Õ³;í†@]ÉQ»Ý¥_ب'G5€æ§89ôqöX‘£Á¹C¾étXê6¬ÖaðŠ9Ž€#à´"ESáNŒÚ a/¸-¨Ëlµ¶¨¸—é8Ž€#à8Ž@[ Ð)ȳӲSõÛ/ÓpGÀp¨9ÂË5ÎËÉðáÃÃÑGmYî½÷ÞðÆoØ:3Ó®¼òÊ¢§2Ãí•W^)z¬¹Ã† #GŽl.›wGÀh²;šJiP ŠðSº"P7›#|Í3ÏüððýöÛHÌÞ{ï’CG4I »áAÊ Äkë­·6ÇøB"î$ ¹à‚ ìZO=õ”.È1Ý’à}{³Í63…#IGÀpVG`œJ\ªÕKõ6@ nÃjÙºã!û¬³Î SN9e8è ƒÌÛuöøì³Ïð`M0Z‚Ô" [mµ•­c+të­·Ú¹¶C?×\si–Ö[o=Û%·õ6Œ‡£ÈË/¿<rÈ!æ|”]tQ˜8q¢å#l ±ÝN>ùä°á†¦â|é8Ž€#кܦâ–hÝ"½4G m¨›æ([ý¹çžÛˆûˆsöÃ?d]_sÍ5 û—_~ùðÕW_¶YAë„¶) ×€`ýë_ÿ •áy;ÉN;íT°e" $a;GÀp¶A@¶Fc•.k›Ò½TG uhrDœ³j…ð"IJKL¶¬ôîÝ;Œ?¾°‹P$#ìš_|ñðÉ'ŸŽ=øàƒfÿÄŽ<0Üyç›-ì¶p‚¯8Ž€#à8Ž@·A z–R'hÐæd5J7ÜpCøé§Ÿ±Ñnºé¦†ÏRu˜ÙéÁf ð¬»îº6ûmÓM75[%Œ¸!I;ï¼s˜uÖY-ß‚ .–^ziۇѸ‹#à8Ž€#àtoÚÅæ¨È!?‡vXaúÿB -dZ Ó²Ë.kÓü³å,¼ðÂF†]tѰ +XÚÛo¿Ý²`˜=Z¤©¦š*~øá[¦Tƈ#Ì& [¦‰5êâ8Ž€#ÐZh?ñ=5´vQk•éå8m…@‡<ûÝw߅馛®`ŸÄl7†× J¥ÍÚ%HÚ§¬0£m¾ùæk²?›Çׄ@ƒÏ-˜2cº¹MPú^ #¹‰jäÖÒÅp*D@ï3afÔ»3yFL…çy6G ÞthrTo0üzŽ@'GY4|Ýh9z§èh,.rô‹iË‹õVG ÃÚµúzŽ€#à8íÀ¿U™Û»~}G 9œ5‡wGÀh-°7ú±µ ór¶B Ã’£Ÿþ9Lš4©Uï;9~l®PÂ`ïTÁ>ÊÅp†ÓNWú¸;Ü«ßcçF Ã’£»ï¾;0¿ÁOSø‹ SüûÛß;Ôhç“wµ ç¿òÊ+A€…9ÁA^pNùûßÿ¾‰wð|>fÖqÄùݾí8Ž€#à8m„@‡Ê_Ëý<öꫯ¶8iµœÏ9Lÿßc=®»îZk…óÐ~á; WSO=ua?+ÔMVr7Ðè o8Õþ¿†ê¥e®­WÍpº0‘{“"–­yŸuÓ=þøãækˆ€°ÄHKÃI8j$ôG¶ñnà´q·Ýv³ ²k¯½vøûßÿnû™Î¿×^{…yç7¬¸âŠáÙgŸ =òÈ#ÃØ±cÃ~ûígùÐ>ýîw¿3 Ím·Öç¹øâ‹-„È’K.FŽi.»ì²À9,ÿò—¿Ø¾c=Öª”ØöäTú ãi]füiÅg`*µMSgÚ¨Vé´ÑÀÕE ,›ÅãÁ.¸à#3¯½öZøñÇ_ìó^}õU#ETê‘G1bóÐCÉB BŽ~úéç2üµçž{†çŸÞ´4 †¡ˆ½¶í¶ÛÈ ÃWp@á>ï¸ãŽpÊ)§X9ø=‚¬Í1Çæüñž{î !ËáÛŽ@ŨQIÄ-i%È‘9ŽEÛéÉ1ðgÀŸŽò Ð6eÚ)?ð›š;ru!GhCV[m5³zê©§, ,öAIùöÛoš$Ø%AsYl±Ålˆëþûïüq4hPØi§šL½G[C¸$É^‰m4B矾"H!GNËËÉ'ŸlõáÜ·ß~;,³Ì2…,|; %IV«Užx≀ u .ÇÉߦ_J,…!÷B¼8"÷B?~|ØgŸ}~9Ù×ê QI bDúUõÅø] ÚAÚ"G ‹¦1ÙolöX×i£ Ftälø¿¡£Wu5êBŽB[~ùåÄ Œ`l½õÖ¦¢¶hJ}ôQ«8CXYÁ¦ˆyÂ>-¶?;ï¼³ ¡)a&ä€?‡%iÜ`«ôÞ{¤o¼±Ptß¾}ÛœC¢ 4DyÁÈ›¸3Ï<³‘ˆ]"r ™1ôÅù|ð‚çχàL1ÅŒ±óyØæ: ÕáßNÔ-]'å/…a¯^½l¸/a!ìÝ»·‘¹t®/jhhLhH©¡™Vë3TSN{奣‘f2+ôW¿ú•i­[Z&nÐþ •ÆÆ÷;%Ú&C`2Ð;Klkl+÷Ýw_3kX|ñÅMƒfÒ¤£É\©4'µÖƒv-aIÌ=¥ý´Ïå„úakJ{‹`›Ê³Ñ’Žmºæ"|+¯¼²P'ꆉƎ;îh£)GZfß—jëÅ÷yóÍ77›]♢”8î¸ã Ű¯R3”–Ô£pÁ0JoÚ°š´GœØæÂp†Ì?CL0’>äCìºC‡5CjˆÁÃ?ld)UBµå–[iøÓŸþTð÷Ãôx^N†ºVYe3Èæ^¿ùæ›6 Ÿ?2µÄK˜ ÏŒ329YÐZ}ùå—b9cýÐCM‡ K^|®ÃÆŠá8fÚ¡±úÛßþzöìi³Ò¸¯¼@Œ°kÂÀ›Æ´”p,fÞ-µÔRf{„18 I’RÒXÓ 34ÈÌ;öT`çâ´È‰ O‡ÞïÔY@»Ëǽ%ÃÞé†é°P^¶ãBB›@{Æ7 hn;š`—HQ‹`&“T˜ÍKgò¯ýkMÚé–ÔƒºcSúôÓOnƒ*û*‘×_ݾ!´ÛQžìÿYI9Åòð\dGȳÙf›Ù³ÁÄ l`³“‚Š•Ñ^û²ïKµu2dHxðÁm²ì~™<•Þ¿jÊkI=2ס‚¡é†ßj"Gö`ðpT›tANK¦DõPb̩ҖJ*®©hè'¾ñÆMÈuÔ°Y“ýìÐ¥ý‰²Qjtœü/½ôR”æ¦Ñ~©|#å%ášêá¤ÍÂ’r¥´TØYdEÆÒQ$ GdËSXWƒåv °]jEC…¥5Ú/[¨Â}JË¥ktœ ࠙jQÎ%›œã;jC€çX  k*õVb¦ÍlJ¼€S¨Ôªß›Îp÷¦DウhN¥…”–TZE© ˜úpFÙ÷E5ˆQ[¿å–[,Ÿl m[Þ¶ÕëšxPx§ÔËŒšŒ`Ǥ½µ¼r£WXa…(÷qܸqQ¨(Õ9‰¼ï¤>>Q±#-±í—V9ª·ç™gžxÌ1ÇD¹µà?Œš˜5Û4ªƒfåé£hù¥‘œC9²aŒ»í¶[¡ýàýVç(ª7ÕÑŠšEjeq-ÚÊeÍœµ}§ŸŽè¹wõš£:`q›m¶‰ÒbÙ~îSº¨aô¨‘½Ó¼»}ûöšôåÎ$JSnyiãÀŒEN¢:?Qš ÃZ©([è½å¥íŠê°Eix¢f»Ú~ÊâÞ¨¯f²Fî½~r^ku»æšk¢4QaâvÛmgçÒ>!à"­K<úè£m[5;®!7ÛNuži¦™¢:§†ŠÕÃNÈüdŸ‡Ìî(íŽ],øÓ÷"ýÿÜÛ“O>i§h†°=?ÜóàÁƒ£Ì3"uS‡ÑÊPg5Ž3&j†°m_xá…Qæ–x†“”ºŽËüÁž? DÍ"6Ì4ãÙN½âŠ+¬\þ³$êÛ>Úo™D‘h{¦y>Dš Ïœ”QD;J5úa§—{ŽÀ£5ß.X ?öçŸ[Ù»FM„2¬9ŽH+eÓû¿È"‹D™£L>¨_™³Î<Ò¶EižìXþ½-œPŠπҊJ‹)ͧD[ G™ZÅTÝVW}Bºˆ.X59ªâ>=«#Ðîèçeë®äˆÞ×,JŒ,¬´œR¥&ÿKúȨop 1Ùǘ(>`šåÃ>{ï½w¡Œlã ¢|>j#Ög˜aûÀjB†mó!£Ü¥—^:r=( ØpĈöñ¤Q–²9’†Õ>6šñjåÈ—™ÕaàÀ¶ “†ØÖ!ˆ´#¶-—q5Ö°r©S)r”Häˆ1yùó± ®„)Çd/5d¥5¶ב¶ÚÇéÉäÀòr1i£ã´ÓN5TS¨¯†ó¬\ˆçI[ÙǺ4îQ^ÿíƒÄ6b:R¥ð“ýc¡nü|à)ƒsùå6ÅpÉv@Ó}sÏÒÂÛ}QGiLŒ¤r®\¬4©d./Ùç!{,‘#ÙRÚýË/\äãLÙš‰lKÈDŒ4¥í·ç „\’"×/r4Ûl³EÈ ×ÖÄ#SåîƒziäÂÊâÃ…°•"GzH׆AZY‡0Ëž­KÛb· ©ç¹à8$¹ÜsÔïK)ü¨\±çvå•W¶º‚„.T»ýdÉÑõ×_oyy—eZcXƒŠ€ü{›Î¯f fJÌÂZJ©§;:xNŽªÒó:Í! —Š—­»“£ù…Á¢J8ïÛ@© l‰ièÀŽ¡¥!_Ò”Ðg›ÄGÃÒ…2®½öZÓ °#5öôØÑìj(Ùš 3[±cÇÚº†ÂM“¤É¶MãŒ@„øØ!Is¤apÛN4,šÐaçeµ?rkW>Žôî)+i‹ÑòpYr¤™¦Q®7ŒrŒ+¤FC8QÃQöQæÇ‚ƒ¤ ÷É€$Wfv-´k”E>‘#ê‹@,9Æ=#\m‚Ö‰‘†ðM;)Esƒð!æ<¤~ îU6:–­=ÿ D•2H9´KH–¡iâxÒš$ÇÉÖÃvä~²ÏCöP"G`†Ö ­ Ï÷ŒFƒkòaNDI3víÙ€Ìq d"çhøôÜj¨Ï¶e®ayyþÊÝäœûçžÒÈ×È“#È$ÿO L9¢øç?ÿÙê—8Dñž@h‘rÏQ[¼/åðË?·Ôg„÷r$ˆ£=H–¥FzŸdcdùÑŒ!Ù÷ÖvTùÓpý¾Z.¯ÔK‰ŽÝLJÓ¨¨ªA¨Ï]GÀÈ"À}6%»#f€”i{ì˜>¶Ä‚Í“DÂÿøG›-jô#-MØh£Ò¦-ñ3¦ÆÖÜ~èô±Éf;ìó 8±7Áé)¢±-‹ýà)¡l„ºàÒa&mì õÁ³²D‚Ìf1ÚbxšiHÌ«=÷.QÀ®;G‘!‹¯ˆ1*v€Ø5Ò@gED'èÃi¡…pÕ3Wî7yóÏæOõÖØŠH>Ô4nK°;ÂÞˆõ¬H3c›Òhس¡¯mÃ!Wɳ‘½ìA™=6Ï)FÈÒÄ¥â Ki¤ìÙ`rxž°CÅ_ŸˆMôÁ1’ý¯±—MqE+yŽZó}©¿ôÜRož‘G4Aã|i†ì]ÆHû¼óÎãp#áÙĶ8½O¼kH+Ïr£bT‹e‹øM‹NÖÅ]G ë"@#C9J NÉ»¥á/&=öX6Åñ±Â¶VI¤‰óqðJÚ䤔ðÏK"‰$q<­3ßjW§d#/L¶à£‡ÓUi" „BÚ óŒÀ=‰zÓùS „ÙSLþàÃÁÇIÃcMò–Û&+$4áÁR“&§&òZ?ÊJiS¯ßH#3òÎ9盄‚:d0+yL™eKí´?›·Öuˆ!’¤ ¬g%‘GÙ5Âz—’Jžì}à Ì5TW(²˜Ÿ¥÷Ÿ™äHk>*ŽvŠ”:wÅ%ehNj>±¹‚Ûò8Þ®e h—àOH ovk\Ÿ©Ÿ<àôlÏ8ãŒ&³Zã^†#ÐA qÉJjt²ûš]ÇÕ3¸p‡Ál)Þ©O<±p^š ZØÑÌ =Z> hXˆÃHŒCzÿV±³!iLφ”zèhyˆkHO—ðFƲ٥2¢ °™©¾0öôö+ —YVÌš–ƒy"Yù2R^ˆ Z+ôZ–Rùóç§m oYzïxÜOD+WzùÌd-‡_*/-!ŠL“§$°7Z$Üð!MÚ¥”ÿÿ1J´ h—ÀÉÖ£ØýUò< -Â- Žsóä ­›& Í û¨CºvÒüXÆ?åîM×åþˆlÀìàbĹDÑæÏ™Ûh£˜ XNÒ³QÉsT¬œjß—røåËçÿ‡øãâþoêÌ,ò¼ y¥=à™`ößT ¥´d•¾·ùrsÛô‚h«·É·c¹ìe6y@kI ¨j¶š®Ó*‚=@š­€A[2®Ìîoé…˜QÀ =ø6+Ž™ŒQ»tôŒ3ÒmŽh\x·Ãé¡´¬Ò:J[)5y’톆k옜§‚[Ä~[Ö1RÆvˆ™_Øå¤VØW`|$ i$l[ä%Š0غˆŽ•ƒÝÂL/}Hmå3Û%ÍhÝM3Î0€Æð6Ù©!¶ó˜MC~ ïÙ¶>°fÄ>6"ÌC°À šýØ ©÷oëÒ­f'5ü`w„-çrÌ”c[Œd»‘î“SÖ_};N½¹òʇOÁæHÄÍJNö=”0 ;»lz°ƒâ| À“Ýv5©>—Â/Ù¥ëQ.ÜàÅ}P.åÓ*6‡Ù±½ÿ+yIØa…äëa;3?Ùç!³»0[ ›#›}LͶ*ksÄ1ì‘’¿´;&3 £E¢‚„lŽÒs› ¥5\gùËÝá"˜v<£ØA‰,ØyÅf«Ù†q{ž¨†É×c„ð_ò\')÷µÅûÂuKáWì¹å=æ™Ã¨ÿšgLDÉŒÚ)+ksÄ7U$¹ð\`´¯8©d3ɾ·i_5ˆç Î89³k™±6«Ò4JU{ö¯‰©Âº–±³ŠÉ³A0àãá°d¼ÉÍ?ðÀÖ2sƒÙ#iê=ÓÒ1¨ã¡Í>´ÌDcµ‹©²ò±`/%û^GÊäšê1Ù~>ùf°†GÌÝÊeúj^¨Ÿz…ÝFòBbPçÒ=Ð3΃ÞÉQÖMG?a±5˜t$aRê(eë…‘3q%‚Q-ÓÔ1f=+lCh’ ì±æÖi+Ò‡·¹¼׃ÈJò–ËÃìŸÿÓ¬¨ü½pN«Rø•*ŸrÑ)•‡ý¸!áÃÉ4úb’¯G±<-ÙöÂCÈóÂsQl>Ûåîƒc<7Åp.VVvÿQ¥nWª}޲×ɯ—ú¿óïK9üòe²-[@#â•`Á}ólsU“¯G±k•Ú×Ð^c9ZB©Eä(©žTNÛ t¨:Q§á4m÷Ýw7£HÆ­ñ˜z‘1HÆ“EÄ*ͨ1^Q“ñš^Pïe+jº¾}ûÚx}¦LŒäÔ8™#Gœ6b4†3FŒã0^$ ês=æÛì ’` :dCš¤c¾tº8¨¦[ÞvÆ0Ã×¼`ƒ“l#òÇòÛ =ð~3ÌÁzVØÆà–ø‡Õ ô/• ¶,Ÿ·TÂìÙ³g“{¡ÜS_:]ÔÀ°L=°.rk~Ž€#Ð…€¥6‹ö«ù} 0j>±DyewË3©‡¨È}»‘‚¸.Ù É3hçQ»Ô¬ ó—À°$ˆ S³Êk~C3¥ø.–»&†Ú²ÎÞ²e[Ç Óo™zŒ3;¢?Ws~±2}Ÿ#ЉHmCvIÃãâ8Ž@GF€6+iFjÖÕuXMÓDÍH²rØa‡£A†ÐÊà¿#¢û"rç4 4àeV3 Ì ;oĆ­ea§”†ää’=hZ±iž4=±b#<Êé%ãLœÊiŠ¥ ½Q>>P\n‚@V{ÄzJÝäöý6G “"4F-j³j¶ÐøF;L­`îTgQbê/þù ^£Å/‚›}†ÌpÛA4CgYaL1—Œ$%{ ŽãdŠ!-l…(£˜0ÔÆl’ìyäÃxšp ÑU:s%_>¤ û'Êpé^4ØÔ]¡ô•Ò%,ò1R›¨g<õN´ÙuD÷MÏ+½Û¼×ø áÝÆïQ“w[û\GÀhWÚkü}­„Á0v<ß*ý ¶z¢–UI]5GÔ Ï Å£kR^˜òÙÜ´ÏRÄVÌmþå¶™— W.¯sº¨£iR/§E½°.„‹ßŠ#àt\h·²6G©ýªºÆÉž ê«=a®JücT[®çw6A€FßFÙñûºµmrG^¨#àtu G¤I KÖk’ºiŽR,´šjé'9Ž@=È6(ôÂØ®¹VÏŠûµG [#@;E›…ÙËšÛ-ï =GÀ(Ф(›ŠfòŽ€#àt0 FH¶£7yO…¿’ ><$MÔ½÷ÞœÏn7»¿Âû¯8Å8∊ó{FG “#´E©÷Us¬“ãàÕw΃@¶b½frT·aµÖÄbÔ`™.½ôÒ€¯$f³e÷·æõ¼,G ›#AÂþˆq|GÀp:*´Q¢D’Ò²êúÖMsÔ§O øJ0W|%ÔxôèшŽ)þxÂþì³ÏìFòº×^{Ù,6ü =ûì³¶ŸÀ±œÞyç…Gy$Œ1"ÜqÇV~*×&ÊäšÉGAlq(yÚi§Ùô|Ê}õÕW­\~Ž=öX =Â9øHrqº)ÙbA²†‡Ž‰'ÇÀŸ:Ò3 ö‰6+µS´YHÍä¨nš£§Ÿ~Ú<\ß|óÍáå—_»ï¾{À 5Dhë­·xÏ^~ùå ñÔ.»è¢‹l†„†ã{î¹§b§1}Ÿa4‚˶oß¾GiZ?e®¾úêášk® =ô‘®×_Ý|A®†j¡Ip ÉLº»îºËêuß}÷…[o½Õ|&õë×/l¼ñÆæ„r2Îþët9¢‘IZ£Ÿ´¾³~H8+#D;áµ§VBjVcO>ÝGÀ(‹ímþæþ£„?#¾ØÏñdÀ²&©9¢vÇ|Xxá…-]}õÕáñÇÏ=÷œ…ÁÓuʃ7jâ°á4ÙȰaÃŒÙFÃÏì³ÏnÎ$ /’õED¨‚ÚBœ>;xð`Ó2ÝsÏ=6ü†6鬳Î2tÐA…!C†X‰ì¿îºë¬~_}õU É‹/¾èä( º¯wRo+Û Ã‰Ú•hðÆJÛA>"öCŽê¦‰Öµ\G û!@›C»”mI¢b?)uè´Z›Ô•-²È"…Zû CÐÙ•VZ©°î¹ç6ƒWj4: ár5Ö|°‘›Bæ+h–_|q#F) ®?ÿüs;Ÿk$oÚhšRl6¼l3D‡æhÖYgmÇ-•åKG › z_ôÆR#°!=5¦É"4@4D#Ú´F®9.Ž€#Ð&¤mRÒA޾S¢m¢¢­J¤D¦´«:©+9B„–ùÛßþ¶Ýv[³ 7n\¡Öh‹ðl ™aÈíþûïØÝxãa§v*¨-œPd¥wïÞ›£Ÿþ9L1ÅäÎ,Án d‹¤}ùS±1"\ š'4N û¹8݈‰F†v‚FR”ÚŒD–39â˜kŽ‚‹#à´DБh›ëAJûhŸè¼‘È_µ¤†®êk9»!¦ÃCTžþù0räH³):ûì³ôôèÑ#Üyç@´zAföÛo?3¬Fƒtâ‰'6Ñæ JšŸT'†ÃÐLaœMàZÝBxV[m5&KùòË·Þz+l³Í6FŒÐ>¡ÙÚ|óÍóÙ|ÛèêИ@ŒRãA¢Jš!ö³MO r)¢-q­‘@pq6E µK,ÓðZjA¢ÍJšïŽOŽžx≰ôÒKYÁ:Å=ƒ 1ƒm…V0£Ûo¿ÝÝÿýÜ,»ì²°dÏ9çœPÇV‰²°SÊ Cr;Í8ûøãÍX{šihÇK˾ûîöÞ{oséBSuÒI'™1wé³üˆ#Ð¥ –H ‰‰—‰„ÆÍ’òMÞò_GÀpZDt’fˆ%Dˆ6 ‚”Õ¥<Ú]½ü?E«­þ,!Í"ãtJ3)½›ÈÝs)5‰Ü=í´Óšõĉm(¡³¬0 öÍ7ßIJö@g˜ëƒ>0[!Ê(&ß}÷]À^({ù¾ÿþûðÞ{ïÙ]šÅVìüì>´PØ;-´ÐB¶;¥4˜Íçë]´—’J_(}¥4A õ-cÜõîÐ3é’¢{GDâ=OÄ;Ï2 ¥%‘#ââ8mŽ@ê¤A~X‡±Ab=%Úgöÿ¬¶ºj¢Swr”×ü¨â.Ž@‡D ›“#Èäˆ%äˆõ,!ʧ,1bÝÅp¶B¢ñɦ¤%bI"-kíÄÖÍæˆa®é§Ç%Š‹#àttèi‰¦Þcû©A‚(Ñè@Ž´tR4ÿu¶G µM©]ʶQ‰q,嫺FuÓU]3?Áhgº³æ(A/ ’V(¿$KÚ—R:Í—Ž€#à´5‰ü$ó–iצW39ª›æ¨­QòòG õhh\Ð"%"”.¶‹#à8D‚ZDˆ²7Òîä襗^²ð!Lµo/yçwÓø7Ûl3¨f8ÿüómÖÚÔScbáâ8‰$9Ž€#àt\®™!£F²ÿÇ‘¸øï™èâ8Ž€#à8Ý º‘#¦ÕlvÁ´iÄT+&_|±MÛ_rÉ%ÍIdʳá†â±­¼òÊnäÑGµà± ,°@8p`øÏðG7yú~±ë¼G’§všMí_qÅ-ˆ-£#<2Œ;ÖNn±ÅVΚk®®½öÚ°ë®»Ú6?_~ù¥]Oß}úô °]n¹åÌý'“p­µ×^ÛmäwqGÀpN‚ÃHµ$ݳVðq„o£JË(õQÚR -|#Ù~ûí£¼MÇ7Þx#^ýõq¶Ùf‹"QZ›¨`´–WΣ<[G… ‰"QñÑ¢ˆ“×ë¸úê«G‘˜(gQ>â_~ùå("/¿ürËWê:œ7ÕTSEi…¢/¹ä’¨@º…ýr(iKŽ* û|¥s Às ääÈI`Íí¤žt?×1ðg >u1È–6Æ‚ÀŠ4è[3YfÛxãm-í{÷Ýw-æÚé§Ÿn»~úé§ íQ:BC‚ëäõZ¡@Þr×ùíoæž{î‚mÎÍÇd+\(³Bl¶»îºË†îD~‚ÈQá¨ÈUa]/‹ÅF™ÕÍzկމ!¹¼WðÂɾâ8Ž€#à8º£Yf™%@`æšaA‚ÍOòp ACc¶ëÇ,رcŠ)Ê›H•»Ž†Éš=?Õ#»¼ùæ›63 bÔ¿›…ÆÐÂÐÇ·Ùf›F§ÊöÉ´IÌ “ÍSXf™e‚l”lÖ„ŠwÜóe—]Öè<ßpGÀpŽ‹@]Çà;-Ë|óÍJ Å€ñ½÷Þ3Ä4‹­&ä*¹N¾`l–&NœXöûæ›o‚f Y6ŽÛ0Yvº>C‚ÿüç?í†óÒpa*÷‹/¾°ãK,±„ÙH¥ý¾ì<4hûªÆ_(}¥4A ÆüƒÒD=«Ém½6]GÀpº u%G´K/½Ô†öÐþ¬¶Új…[Hä¨É+dô•N‹@9ÚN70^ÉÉQ§ý'½âŽ€#àT‡@] ²««RÇÊýÁذX–QãŽ:*L?ýô«²^›¶@ຨң i´–å´ÃÅpG ë àš£®ó_ú´2 š£›UìOJo(a$·ºÒJc”RzXÃkŸiéâ8Ž€#ÐE¨‹AvÁÊo£{"ðšnïï·)ýAi ¥J¯*m¥ô–HÔ8¥s•(Í¢}.Ž€#à8NCŽ^zé%›RÖï¼óN¸óÎ; ö§Ÿ~º‘£ÈRÿN _yå•PIþáLJ£>ÚŠº÷Þ{ƒBž”*¶ìþ½öÚËâ·•Í”;ÈÔÿ¬£ÉÜa߬?8âê¥4CæÒ «] ÑZΡ4Déc¥¡JãEžW‚D¹8Ž€#àtB: 9Êb«xlA¡El× +¬ÆŒa„£2©$?ÄHqÛ¬@ ²Ÿyæ™Ê oÈ…GmÅ ÑeÖ[%;餓‚b¿Åk«äÏS&é2ã”V*v9f¬)½ tšRåYOi9%†à\GÀp:!u#Gn¸a¸úê«>#ò裆¡C‡ZHœ.®»îºæI;aÉ6Sæ“àQHŠ•fþ„ ‡rˆ1bD¸ð ÃFmdîs-(VZ:Õ–Ùüÿûßÿ~”ðrM®¼òJË£`·iûçw^À¡#å^{íµHŠ•V(o÷Ýwoä ;À[6Sûgši¦´«Ù%†ßLÿ' ‰K‡CàEÕhÕæj%mÑï•ç.%^×\~?î8Ž€#Ð1¨9‚0 I9çœsÌù#¤gºé¦ wß}·ù>Â36òÚk¯™wìa?ð[”ä7¿ùiW–[n9óZ­à³!7„á6üðC ciÚ{cà†©§ž:ÜsÏ=V¡C QÙxqé:Ôáì³Ï³Ï>{ÚÕìBÇ9 ÈÛl^ÏPw^ÐW)wU£ßé8ÛÎ"F÷•ËëÇGÀp:6u#GÀÀp!6vÜqG {üñÇ›'jb‘Al*‚Î@–iôÅâ•í¼óÎaíµ×¶ð$x­Nd¦XÙxíFë„7ë 6Ø \tÑEæÔ1å…Ü B³„(¡å8¤êw¿ûi¨R~_vYç„éÎSì3ÄhˆˆQc6^ìßç8Ž€#С¨+9Z` 1ËfœqFÛ&¶ª‹ C_ÕD' aHžzê©´ÙdÉP¬“ì´ÓNò#mç—ƒ 2š, Â!K.Ý ÏÖÈß­ˆÑŠÚ÷¨Ò®"F“ÕŠùL¾í8Ž€#Щ¨+9"nY%¢Œecì믿®ä”BÎIÂPZ¦R²øâ‹‡O>ù¤p˜¡8‚â–sɇæhðàÁ¥²úþ®‡ÀÓº¥5³·%b´‚¶ïWzV©oö˜¯;Ž€#àt^*c+u¼?Îb¬0­½˜ iúáÂ[5•[o½ÕŒ°¿ûî;3ª^c&ýÂI›nº©]M$‰!¹O-e"˜möZh‹0‡XÍ?ÿü)[EK Ëo»í¶BÞ›nº)|þùç…m_éÐäÉ3Ò F»)m¦´–ÈÒÙZº8Ž€#àtr:9Â(šéìø%zøá‡d)/Ì{óÍ7î»îš?dCu‹.ºhÁ'ÆÙ¥d—]v ãÆ y¯²Ê*áðÃo2Ãl½õÖ3[©t–¡5ŒÆwØa‡RÅ–Ü |%ÁöŠûpé|¬ZþGiq%œBBŒö–óN¥ïµÞOi ¤s´tqGÀèÄ´$|Äj%Œa<4«F«s*ý5 i½jùþûïÍ0z¶Ùf+y.6J'N4ƒé” ²úê«l‡&L˜P±±4CióÍ7Ÿ‰§²²K´P̬C‹D¹)f´Í<óÌÙl¾ÞÅÑáŽpôø…ÒWJG(ýŸÒžJ{ëÿE ¨Ê?‹c”žÖ±ýµtqGÀè„´FàÙÉB!üÜpÿöEi Ì#•¦Õ“ŠI%çgÏ[pÁ³›MÖ“á8š,f¸A¾œ5©«îÈ>Ïoè&ORê¯ô`þ†Eˆ&ˆ ¡AzPËó´½_>o;Ž€#àt|Zƒq—¤v¢Ã™$FÓm%Ÿ}öYèß¿Øj«­Úê^nÇFàJUïB¥âo: Bôo£õ´ A:_ÛûjÝÅpG !ÐÃj¿Òý2¾Ä°ã`s)ݤ”íqkÓÅèt@úVûRék¥ JÿVúQé'Ÿ¤5Õfc1Â=:Ú¥—”†•ËÛøLßrGÀhoZKsÄ}ð¡H„¨fÒÕÞ€t„ëò„ÙsÍ ÷u„ºvå:ˆàð<§g:¡Š4¤"Cßêt4H£•.꤮ü°ø½9Ž@—B ¢†¾Ì'BD9ùH™Ó*;„×lœ-&™vÚiÍ366@¿úÕ¯,ÖÙ%—\’wˆ%Êb²•rjY®’ÄmÞiá…6/ß,9æ˜r§ø±ú %I\1¥²W‡ )‰iÿ6­²çøAGÀpöG ¥ä(}4’1v"K­rgL}5jT£²8Ëì±ü1œ~úéí£>j”§=7~þùgsEWi}p%€'⻽ÿþûáæ›o—]vYÀw“K‡@€çÝžñJ‡É”ï;³¾Ò2J9A .Ž€#àtpZBŽŠõž“ö¨ÉmüñǼ•pÇw\Øwß}¿AÄCciÈ! ®o½õ–9[„ í·_ñI?iE»òÄOØõî»ï¾Ð§O3ÊfZ?Qî‘óÏ?ßÒöÛoN<ñDÛwì±Çš#GØž|òɶŸ 7Ü0\}õÕaå•W¶ ²8¤Ä÷¡O0øþÏpuîvß}w«3õxî¹çlÿ[`¢"WÊk®iyKå#Ï€Ì)$~›ðæ{€yæ™ÇÇ©uéÙ³'›vo»e?¾œ’ï%ŽÃý„_Á×ñá’{å•W²Û¥:²ÏzÉg¼T‘ ‰n½•þì©R¾ßp‚€nfØTT}>Ó*a„ÝC‰žq%bj¨ÈÆ¢PQ„(¾ôÒKQΣ>¶.¿AqÎ9çŒgžyf9ˆ{ì±GiˆÒ¼DE©k­µV±²Â“-¾üò˶.¯ÕQ¦£|EÍ"‹ÒØD…öˆwÝu—m‹Å£Ž:Êò*m”Ϥ(ç’QCuv]’øúë¯G‘°(O×ñ•W^±¼¬ËWR)‹{î¹§•àÚu—\rɨ©ü–ò7ß|óøÆoÄ믿ÞÊÿòË/£4>|H㫯¾ju*•Bä/)Š,F ÃEÂxÁDîqÛm·"Eqüøñv­ôñ~ýúÅ¿ÿýïvïòæm8•ÂóÎ=÷ܸÌ2ËØ}‹þøã¶±’f(jÚ½mCŽÖ_ýB9ï½÷^ @h¸¾†¯ì8äHqÓl])#+Š×fÛp@¤,ê)ƒÌ%‘ö(^sÍ5QÆÔV?Î)—ó GÜV¤©ŠÒžE9¥4Œ6Ûl³HîÿÉ'Ÿ,dßd“M¢â¼Y¥0\vÙe£´M…s®ºê*#……¾R†g rD ´>8ÄÂÑcMäHÒ©F×ò2%'HUKðóäø3àÏ@=žÖš­†ÉZ~-›CNx»Æža}±Å³uìkRbØ' v;"i³Ñò‘G K/½4Þˆ-Z f‹ë^x!HóTðŽ=×\x˜,+®HðôÉBÞ#F˜-±Ô°_Ê ChˆÈˆ Ñ%'ÔCkì¨#ÃTI>ÛxãÓ¦-+É—­'1¼HBÒ“ö*ˆè™‘7ûYd& ·=õÔSû­†|ðA`(3 ß.U!À³MBÒ3ž–“÷Vñ«—ú{=»ê”{•.ÓúîÚ—löª(ɳ:Ž€#à´­EŽRãÎG#}H u&ìAW!‰Ì0U™e–Y‚´:áöÛo/äÇ>iÞyç-lgW 3sÌ1Gv—­cwtÚi§Y .¶:îj’Ø}ýõ×AÃSæéš´YIuÌîË®Sgˆ’4GOÞ”Çlº¬T’’‡ ¯Û곸rìÃ~ »¦‡zˆM챑„i.HCUCä&¬)@š&³µr7“ñ¬à—?ˆç:%¶§¨à¼’YréR'H%¡ªÛýü¯“_ƺ]Õ/ä´ ÖYS»bËV)Ñ iY#ŸÁ/Û¨4ùƒd«cêð4ëJC;á›o¾±Ó1fF”fœi(ÍHmD¶E™Ë”^Å€[CHF,ðtã7Ú5‹A^ÊBF 8h_ªy®zõê4ü¨+Bl6Ù;…·ß~Û4Z+ ·ËåË× -š($¡Œ1cÆØ}¥ü²³²U®õüóχUW]5”ÃâG=™=IÚyçMã–Êóe³$R”26y¾Ój–zÞ°ìßHé7JhZD¸ª¹¶ç š,SjAšZ˜ÐQ$±NbèÔ“cÐÑžô|¦ezf§â9nxžy¦³ßd=Ê.Õ"ÐZš£ôÁàiÒÈÏ4ÓLFVŽ?þø0|øð°Â +q€,¿üò6á"4> O1# aQëwÝuW‹if;Kül¹å–AFÝa*Ê`Ö› ºÃwÜÑä fÊí½÷ÞáÒK/µ`² 5tÒIaРAMòÛ&Œk\“k@Žd‹d÷B~BŒ0£ ÍT¹|ù²ñÙ´õÖ[›f¨gÏž¿GÌšËú:BCưbº¦Œ­­fôÃp—]v±xË…lâ.€ÿÃ¥bx¦IÙg¼â“Ëe„ © KClWh}íKZØr§ú±høhÐFñŸN©”Ú«ìz:^ãUü4G MH¶Ô&±¤ÝøŸR¡­Ò³®æÄÛ“Zÿ 3­ådOc«NáCfÖ:ã]>DŠV' ÓÉÑrð¡G«ÃGša3´E$„i÷ ½a“ö±†œš DK^DÆÖ¡Gv † ( ‡‘yA#ùH±Ø>ÿüóÂpU>o©m´1hpd<4ã®Q64c "åò5:Ià†6KÝa©¥–2ò–ò0lFàÁ]“Rr>dŠzBì\*C@Ï8*áâ+%Ôh|Hiy´l±è:Ó©»•þ©4DÅ:Aj1ªM Îü¡ŸD„XÇ•ýÑZº8Ú#&Œü¤DbÉÛ!Qƒ´9¢aGÁŸ2»Rr¤}¦MácÏpÖ_ÿúWsäÈЗKå$r”'b•—à9«A 9š ó¿Wb¬rÔj$&C>VÙ;·fÙ*ÏEãDŒRÇŽí¯…µããtzÚ+“”&*%‚ô?=ãþWùÓ8ÔE°3êÕ«—^ÿîw¿ šö^—ëv¥‹Èo“…OéJ÷ä÷2µ].¦;2á5t®â› M«ü ϬÖ{ ðmªRn•«µ}!h‰ìÛþŠ“¯€Ýbr®[¯kúuªB€gšç›!žù”´êR u#Gx~ÆÖ碋. ûì³O£¡³j*Üó}ôÑNŽºð ‚„O‰M”о^í©Uÿì<9bj)Zﺉü¤Ù„¬ÆœÙ©ð6¼ïW#¸÷Àvaýr‚+†Ò¦Ï‹ÕÚµ±·lNð¬Ï„&¢Ì=÷Üa5Öòg§USñ2O9å‹€ÐÜ5ýxML¯³x¶!ÿ$¾ñuûÎëZ]FÚ 4l‰\Ó×ež#¿‘VB  m¦â H®Aj%\UL"G´yLD¡‡]WÍQšð1zôh]z²¤uEH»*ZÊ9n8õÔS›µ“D³ÔRí„f·Ýv³zaA¼GE*Ô9Í:®¨ÒÊ<âˆ#Ì·]¥çx¾ª€ðó\'‚dš£ÍiUu÷ÌíFŽ¢#<ûì³5áÏËzçwÖt®Ÿättη°¸Ö5H­òÑÖñ¡€‘øxÐËn"ÌE3rá…šF þXé@IDAT_aÌF¥3‡ÓZâ&&gª×^{­åůB¼Cf—â÷,¿M›‡="Ž?ðÀæMÞî->¢ÂYGl3!!š\v0‹—Ø#GŽ [d& I#ÄLWfÈöìÙÓ|Ÿ)L“›~n¸ás ‚I1(‹ dˆ÷H¹ÿ‡~زq-Ú-Þ!Cl.>Øð±–êDÊaù˜AL»­ˆ–_sYÿmù2|»f G ©¡5â9ŸR áÙw©^øZ’®èüa÷PZF©Ò–J*²¼Ûìé§Ÿ.Ÿ©ÄÑ|X‘Ù|·#Ð"xŽ•¶PZS‰ð!¸N'\=³)TxMïN¥çé|Àù’ݨ4e¥çy¾¦ÿ‹ðãc1ƒÓJ{*+ou%ÁÕXäÑŸÿ݉E}äm;…'"6$Ç5±®ªQ3}-#aŒÒq ­ÙºÈQüÃþ «¤É/V¦4>vŒø•m´‘]krmâIRƉ'ž5£8ÊEˆ+â5‹7j–o¤lî…ëÖIQìúÙÐIÙr¸¨H”•+OÿQ¤ÎÖ·Ûn»øá‡Ú½rMÊÑ f«£ÿ´઴–Ò²J¿Qúµ“¥hGÚ¼½Ò]´i{XïòéMÕEðzCB¢É%žéýIJE”Çðo¯½ö²iÿø/BÓ„Ç#<2Œ;Öü#±­à®^Ò=3fÆ1ÝžŽÓZO¿ILßOB&ë™;í÷¥#ÐPcð_Õcs¥™•®—‰ž KmÐs¦½#±YšìGD+ÅM‰QÁWÚ=÷ÜcÙþô§?Y$‘¤@»¦€Ñ·øOC‹”¼Øç·³Ck”K†§03À'ÙÍ7ß°-\e•UìZ¸&I²ð ›ÆhèСi—-q’|ºqŒk£áÊ Îc©?Cq\ó±Ç˶¶ûøKÃ.ôâ‹/¶°J8ÅGh°7¢ŒaÆ™Oº¤=ËôÉ'ŸXX&ÚâG}ÔÚi´Uh®ÄÏ:g´ìwiu BI;šž÷V¿Hw(ðê"êÁ˜¿!TÃÄMKj_|¡JF‹ƒCü¥—Ž—T½ S7óRa\È‹ºÿþû› ™±k¼H3ÄvÛm·éŸŽi,hXˆ¯F£C<7õ|ì忆¹.uÉÆG« ~G  Ò2£’¤*°+‘•6b„æ»,9ÂA-"mŽ-!äCó7†ï¶lLE†Î M)c~Ò@ø#:p$Ú$—¶%ët1x¦=Cè-'!_6¶bÚÏPš´76lF4Ú×ìyäKí\Z +Ä`DðÐO›‹MòþûïÛr›m¶1ÿkãÆ3g»8ì…0ÑIÍJ"e«­¶Za7C‚ Ë¥² |¥-à™æù¦†ÔÚâ:]¾Ìº#»ÒKb Ÿ—â‚#H„ „çÀ´ñrÆ¢ï½÷^£Æáazié­+ òDãÁ œâ¯Q>†‚n¸¡½°¼ŒŒÙã¯ÜŒ±C†ðh}×]wÙu™iAÏ…1qG ## >KÐ 1Œ}ƒkjþ·hïøh¤GÙG)‡©tÈpM‚àÜö™gž±õJ~h÷ 9hUî¾ûnëÒqCÎl^HN`;î¸&Å•òlOdêCg[§ä5?[@"-‰ äó$xÐA™g¼û“N?ýtk£!ØG&‰¶úŒ3ΰâóöK)Øwº™ÒºûgËþ#m¶Î3ÎsMJÏ:K—*¨ 9¢ñ ÐizqxÉéá ¼8)¢<=2öÓË ÷ƒ_ ù Aô¸h4Š =§Œ–<©wD^ˆž¤NC¥MàU 5 S2ìE;ƒq1dg=ö°a1ªÂ1:]tìŠm³v‚Å4ü4K ŸEäƒöïŠ+®°í¼Èvæ~Ò¹h©dÇiÚœüy —]{íµt›Ø}ûömT !h—ÑXQw ¼¹'Ÿ|20œ÷ /˜æžiø ýaÚ€¤á¿TfD9€4wÞyᬳÎ2"ˆ±8‘ RPof»3æNåø²fhx¾Õ áä±ÍÍcóÉΈe":Ø­¿þúð)1k2EÏ çÒ 0Ü–‚R•¦q?~|á0/:äˆ:*„ã$ÊM S!³¯8‚„8ªs'HµýWé£QÓÙ„sÏ=×:`4Ú 5¡aAÐt¿øâ‹Ö+¶Í>´Ú½†XaN€@DhÑ;‘ Ú3çšf†¡‡A‚ >8‰LZ"Χ“ÈpåQï¤)JeÓqÅ.ˆ*QîgóÍ7·qJN|™‰G§’LŒd‡) òÃpUÌþøÇ?š­ç‹ÐnܶOhÎ\ZÔhÑsÞêµêŒÒè%é^QÛU<[™zit©5Ý•t›­ÆŒ556“cêÝD«³ÕXD ƒÙº¦‡F½¸Q ¨¡¸ÈLD QTÏÆÖù‘*ØfZ¨‘²}ʋ̔H¢ÆÇf…¨!I»|éE€gT©Ýf«©REßMÕ‰ÞáJ¦L]*ŸïŸŒŸ0âC¡$Ø!ãèK(aÃPeÑÿ¾½vªc¥Ù®úòÌ6Km^©“¥2/(u¸°ŸYeÒê¶³+òkÕáÌî*º.íWÔ¨@A‹¬ç…ö¼–ûÌ—ãÛ¿ À³¬4Him%fc.¤4›R]fת&E۫κ¿&µ²À®Z"£ç2bÄ›EÆÌ1µl©ˆòô<è¥ÐÓÀwÙŒ[s.= Œ´§Ï *àu×]×fÅÑãùôÓOÍHCEofÔ¨QÙÓ|Ýè4¨±ùI½ðÁªð_IZß’}æê\QaÑZH¬S¦%tî~VêPÂŒ°Z ÃVå$¯-*•7ã.vM?©9oì§JI2X/uÜ÷׌Ï8’–“·ü·jêx–šM˜0Á¦Õ3㢡±*T¸TDy QUc‹DàÕ$L}EuœLŸŽ3¥ï­Œg +©Cl ë¡ÚvqJ!ÐðŒbçó…Þî&(}¯Ä´¥V <«òªÕ ‰= Ãm—"+4Gô SlzÔs*¡}3椥‹#Й€m­ô¹Ò×Jß(7æ?J?©}èpÕ«ÃJÝ4G P®×F¨Ølfrò´WR)Áˆ;ÍfKyðøzù嗛풣„Š/;+j첤›E;A*ûoòñ€±ÄÞ’EÍDëâtôþól'‚N„ZøÏÔÅ »…ulµÓqÙ¿3lµB½ G š¤Ë3í’aµ[4$íX£}iÚ»DŒøˆ´‹ÆˆÙ²Åm83Õ\ ži–éaJûZPl÷;µ[‘£m·ÝÖìž²ÃsÝï/÷;îj4$Ôé|uoAúeü¹«Ýlí÷“´F©Ú¾º~4ðœÍ¬4f£aKÉÐ?‚sÉáÇ›"fÓ{ì±¶ßàY'aW‡Ôõ9Ÿ|ÉÎÿÛ­ÈQçÿ»üâ4¤mtôG%'H%`Òn>I²ëi_›,qB{ÒI'Åc³¬=ŸsÎ9v-| áÆ7øÿÁÙ-ë.Ž@ð]'eŸïìzÅv¯Ó:9¢×„ó´r‚s3¢c#DwƉ£‹#àü‚@Fƒ„Ñøm®Aú­¥Þsúh¤íF™ÒN ñ÷‰Q0ÙÀ„äüóÏ·D{‡ G#\Ò—¦å3Ʋàdͧâ—CËuWD°¥ÙV‰Y*·‹ 1CËe2|4H´{%?Â0|ðÁPIƒƒãG§²8BdòdoÑ„;ÂûôZk­?üp‹éh™3?¸#!-‚S[‚×â@!öcrmByćÜd“Mì˜ÿ85 ˆ?Ë”j(¦{ŸÒ!È`åÒþ â£Ý~ûí66OBœŸC9ÄbÑS#à!þ‘\G 1 i;íÅõ€¤_àI ŒØÓä—£ kØÑù‚ Ì:ë¬>ƒjI 2%\’ aÂwû ;k¬±FÊVt ñ’SZóÙF[—eï³Ï>áŽ;îhâÁ:›Ï×2¤Y²ëeNñCÅè䕳¼²Zýˆ³Füb Ñs#ú5qˆpùÒK/™"œC«‡@‹.Ž€#Ђ´½Žàïä× F|,Еu9Ýtәƈa|+¦`ÕV‚~V\qÅ´Fm$)íÈÇKûÓ’€³ ÑvØaiWaIèÛ.Ž@¤oz𩿩…@Öxze§ýøãfhˆ±!öBÍ cù44óÌ3iÒ9ÓL3Å š~ú雸1Jy|é8ê6Nbƒ }©t§á~º³$mQúpÅâ‰'ž§vZ¸çž{lF4aW4/ñш™$Í@KÛù%ž¬ T›°fCÄòÚ¤ìq_w*@€au„b„x'óß$–Yâpëì&"SXI»í¶[³…D1 næQs»8Ž@õˆ AvPú—Rw'HéƒÁ2%­6–·ÞzËBa<=iÒ$›aÆpX1!Ø+fUC’˜yVN @œSL/Ò‚´;æû HÓ÷ÓëĨBàòÙêBŽý¡Àˆ–è•5'ÅzUÍãÇG 8 iGýTé®n®AJm^Éá|²ˆá³¥–Z*¬¹æš¦ùÆ(/D¯G«DLÇõÖ[Ïò¢Ý.%'âLf³=ðÀÅù>G è•$ÿÕÔóÖ5|Hk*ÛµI­…¦—Ó€ é#¾³îõJ¥»µ¾©ös©Ë‹î5û¡H=é´lrÿ„+zûí·mÖšÚ›¡C‡HÏ€åÿî»ïÂYgeq g´×^{YÈF™2 R1Á]‰‹#Ð äŸ÷ìv+ß=ŠH½¨Nu·Ì yóÍ7î»îÚ©êí•uÚ ÒÕa¼iúö¬OG¿6Ãj#„XŒø ÊËSO=h³Ö.¸à‚ðàƒÚ”þ|>ßvÎ…@ÍAÕ°2¶‰þ#Ï™”fQš]i.¥›ÔkÑvB¢‰'šã´¶»Š—ܘ¬p…¿Rbj<1‚›Ø@6´Ú¹D÷E§èr¥Jwu ’î—žsj«fÔúÌJ³)Í©t{KÚ*Ìn½õÖðØcÙäB%G*ÛŨ íÕ–ºàçJß4¤ï´LíU2ÖÖ.—æè´ä¨¹óãŽ@Khhlº9—ÂAê¥A‚ôuIi¸×6!G]0¿©N‰€“£ÖýÛ:å°ZëBà¥9Ý‘!T»ŒK¿¯tÖº ]玿ùE‹#à´NŽZ I/Çèd4$|k¼§tŸ¤ŽùÂcÏ;ï¼€)A^þõ¯™wî!C†ä5Ú&¬‰;Ím‰o8eprTžö?Hƒ8¹“ßþuñt=Ò·•îAÂ&Ç¥!ðóÏ?¢àK)/Ä™Äö’K.Ž€#Ðztrôë_ÿ:|ú)n\*—i§Ö|”0V›RG󱄸gŸ}¶ò›òœŽ@•4¤=uÚ›Jhº=AÂ;6áC\pA#&ÉuáBð}4Ë,³„Aƒ…Ï>ûÌÐf:ÿ…^ð‘4ÿüó[PZ"\uÕU…€³ddøk¥•V &Lßÿ}Ø}÷Ýí묳Nxî¹ç¬,œPn¿ýö¦-êÓ§OØb‹-l?þ–¥”„°Kìf™œëësZh¡°Ã;X¨’”?-üqóÄ=×\s…­¶ÚÊêıRõIçùÒèNtrTëŸF¼643)ÑKsqº i/Ý÷Jˆ 1µ[ q·ÞzëpÆgXì4‚ÎŽ92üãÿ°ýgŸ}¶ù@‚\ì²Ë.†Ç¶Úf›mÂõ×_oDç¯ýkXyå•ÕW^iăŒ.BA®ð‰ôå—_Ú5Xß`ƒ ÂW_}eèÎ;ï4oÛ‡z¨¹å\ˆqß’,°ÀFŽp²{üñÇ[vðÁ[LÊ^xÁ² 7/ì&n%dŒöH©úäÏ÷mG ; Ð!Ƚ."z1LƒÅë, Ìb‹-fnüÓ‹Îrì±ÇZŽÀ³'Ÿ|rÑÿèOú“õȘfÛ\oQ1!FQ¹iĸ֣>jÙò½ºwÞ¹àÕ–)½DåN‹˜LcÆŒ±óJÕ§rxÍÅwAw?þøã°é¦›†E]4Ð0rI^{íµ@ˆz³Øüûßÿ¶CçŸ~ ÑÛ$.‹#P  ioû7%†Øº%A"Ⱥë®kïáK,aÚ¥k®¹&¬ºêªæ {Î9ç4B‚¦&½‡´_h“ÖZk­°ùæ›Ú ´O´cø?Bx×™îÿõ×_‡n¸!@´¸¤ŠvæÞ{ïµ|h WÄZC „üæ7¿1 ·mè§“óÎ;¯&ˆÚ­Q£FY»5묳š×nˆ]V¦ÿüóÏÍÉ%ç\~ùå6¬¹údËðuG ; Ð!Ƚ.ˆÎÔˆLMÄjz34FcŽ9Æþ ×ÒABn¼ñFSc¿úê«þ§‹.º(\zé¥á”SN SN9e³½¡ûï¿ß)*Ò»ï¾kåÑs$ø-ap÷O£Ç°jíl¯ŽÞ#ªv„²{ã :ßÁ*ørõ7n\8òÈ#­êÙ³§©Çq6GM¯ÍBܯ_?óÐK¼õî´ÓNvìŸÿü§aDƒM½]Z€ I H¯)¡AÂ'P·ÞyKbB<؆¯K2÷Üs›Ÿ5Œ¢‘lLÈf˜¡àÅŸwò®»î ž‡zÈÊ"Þ$(ÊÃ$€DÛ‘†éh æ˜cŽt©Š–h•èHBÈðìÍ5ó¡ºøâ‹”Í>ûì„©¹úäËñmG «#0Ùýkß%­ ´"y|Ð{BhtöÙgk8Мì¸#a¡äeRZœë®»ÎzD¨ŸÛÇ qÙe—µãh› E샴¤Þ„‡Æ††ÞãñçP§$¨›õqx¾½ûî»Í3. #³EP‰/¿üòÖÈÑ«£ñBó4bÄ;÷ÿ4„œ 1ã>æ™g#Tåêݶà„=3õ§¾¨Ò‘›nºÉzŽäE “4ÆhÆ4OÅTèvÐ*Ð;°ží u©¿¶'«)«,§3fç½CK›äïÿ»uŒz÷îèÌ$¡SBÛF)e¯ˆF‰NÞf›mV[m5k7h›ð¾½Pj؇$§Z„NÓi§fí𮫝¾ÚÚ·lYoSî‰{¤¡sFVª>Ùó}Ýè.ÔEsD¯„Þ)æFÅ›­ãè$ƒ¡g„f Òz;o€ÍpªeÔÆH%½!ÈÇØ±c í ½/B$¡Låf{u¨Ði0éñAª0ž¤<*TóHsõ&À%òÌ3ÏØÄ™i¦™ 8@ðvK½Lð¡ç‰v Ie؆ÿ8­€€žç¡*æE¥ÑÝIƒÄp:l蜡]áýf?ÃcìGÐ óŽÓA*'¥¥}ch!5¤W¯^Ö¹£Ó„ =‡|Ó-/”ñÊcçó° ©¢£1š4i’i×i“²‚šáË,³ŒuæèhVSŸly¾îtUêBŽøÓÈ Å¤T¯+›—^½+z=4"¼ÜY¡±Bó3lØ0#*hšRobC?~¼i¥²çå×i¤°9J¶Cçz²¼`\ 1a¦ 3Ë K‰¥i®Þ©q…€asNKˆ½¬¿þúvé^>úè##S–Á6@@×}U,†+ê9%DP—4Û!:H‹,²ˆ aÓéä@†8N[@gìC©4Êï¼óŽÙ"q>ì}ð?DûH¢Ý‚¸ä…¶±ÿþf“”4ÅùÊÙÖÕ0EÙÙº´BQѳ ÛÒ¶Dš¨Þ•å'ŸzOQZž¨¡4;Gd"Ê€±P¶4TQ‡Ëÿ¨‘ŠÊ*œ§F%ÊÏHÔ]T#Ù(»Œ #ǥƶýRËG §EEì¶írõ!jT¾([+;ï–[n¡Û5\gIš¬¨^«»öÚk£n[—VT#këþÓz€½ÒJk*õVB½9›H§Ð•jzw:ãyºß‘JÌ5Ÿ¥³Õ_uFµ3­Ò¬Jü‡¨¥WW Tò‘V6jê}“ãêÀDÙZûÒä`•;4”e7e·Ôì™ê6›‡ Ò2GÙY^iˆ¢´éMÎS§Ëî! šúäÏõíöE€gYi°ÒZJ|±äçûÌwzJÕ®Û´W­q¯uÑéiÙwß}Í8š^vH$ŸtÒI?\„!,ÆÝ1êF…\iï,_AŒ°‡hF=9†1 g¸¯˜Ð›üâ‹/Ì®€ãhè]&[‚JêÊåºØq>öSØ!«¬²J` .=Yz™ô8/»ì²tš/6E@ÍþºÀX¥1Ò A2º¼`°ŒÆ6/ÌÃ. mKK#4àhtšf U" «¡1G0 `‚G^0 àòRM}òçú¶#Еètg±)bvHšÞÊv8åϲ ‹aÀ\I#”ÊB…ýÞ{ï™ÁåŒ3¶Ì/^5õƈPrKu‚„aJÃVЬ¥¼¾l ØT)_(}¥4A øåúg­w+&gë†×PZO„é›Îpóª3š#z6ô y‘gVB#¹^¦‹#ÐÙhh¯¹Áè–w“ôRj¯&Ûkh‡Kót:rÔü-yG uhhlœåà.gi×ÚJëŠX|;Üá6U_'Gî_ñ µ6 í•“£V¶S «µÒ={1Ž€#ÐDˆÔé(1ĆÆ¥ #€¤ìä”ì­‹÷–=îëŽ@gEÀÉQgýç¼ÞŽ@;" ‚t.ÿ°ÒC"H}vÁ_3Õ:»àÏ-9¤-w/Ø(½òÊ+M²` 0|øp³‰b/®’à#‰|ÌŠÃop]®„€“£Nøoâg…ä¶ðÏëBUÖów°ng ©+¤VX¡ö§3ÿ]D ÀwZ­B¨l:ñºKæ¦PN%œãâV…ðOL:)¥]ªõú~ž#ОtZr1Àk5*_×(c8Í,1¼gËE€9;kO€ó×®´7—?íK.¹ÄÌð!D›‚¿"<Ù#LŒÀÿ³Úp›Båã%–Šïˆö§ÌJŇdƒ2ˆŸHøÞ&!o±¸Š¥êGÛøÈ#˜çëäçBƒF¬\\Êt=– ™Ñ†Òž2ãYzh¢ AÔ,Hâ_©9'•–Ù΂ÖZ’î¯ÕüéúU >ŒT‡øí·ßÚ¹ ºÚÈ×>Að¤ÀŽU—Ý–'H5¿êK(f\TãH²p®®:UåçHN\£‚ÅÚS€6Mç7ß|s|ì±ÇÌoZòg&§¯qÿý÷øO™Šr¾hçà» _g"(QîD"m“Ñš_!i_Ì?>”¸ŽÜDyàŽøsÓÐUÄ—þͺ(ŠlÚùø*šÈRÄ?Ñ{ìEÀÊÖòå46ŠxYø“[øúë¯GyÒ¶¶DCi…ú–ò÷fô£pIQ$(RnVD”¢Â>ÅÝvÛ-»Û×Û=ç´Wƒ•ÜÏQ3í•þž²íÇ›ÍPªý­FŽä :qÄFf‡(*FXÁ¹#Žyñä~?öéÓÇž;E®7r¤^PTÏÌ ¼#Dù&ŠŠgDö¨^ž½ÄšÎ¥u‰cÆŒ±ýŠù·Ûn»8räȨž™í“vÇÖq>IÖ´Ñ8É+nT/2ÒÊŸR„tÈ'QÄyc–¸üù϶’F’ûA¸†|•Dõºâí·3ƒ8ÆbùØOÃJ¢n'œpBÄ1f1RÅ=S&Êû¸5´Š¿fx­³Î:Ö˜r4l4¼RG…!‰ê±r;O±•Ì9&Øp-<2%Õº5ú8Ó¤>IÔcµÆœýÒ^¥Ý]jÙÐØ89ª !Ñ/¸ÂÉJ¯*ÍÉvGIªO‹Èd$ d‰¶J®A¢ÅêGÆlGL.I¢4bv>‡òR{’wDk™2?8•„œI{•Ù;y•6aå•W6¹MúŽº" çÜÉQ+¶;bXM½2 +¯Ïæ@‡øBöÚk/‹Fä{ÖõÁÄJAV‰†ãǼ ž¾ñÆÆnh‡Î?ÿ|S å瑌‘ëÉ5U0ÃMŒ¯zè¡¶ïàƒ¶0$i|=]‹øG„A…ŽÊG“\pAPCg q‰ ÆFU.­•ÕaÔ¨Q{Ѳ`º{ï½w`h¬T>Ê@eOyøebP!jT9ÔHf)/¾î\ÔXœÅ=ù䓿H§’jü‚z¸¦NWäm à€0dðÀXDð‡~Øê™‚ÝËû¸âŠ+ bL¡:'†Î59à–àðK÷F@ïÓBàn%†Øæì*hà- CL*3Œ¤GÁÁ"mÐÑG]ðK–b6ß‘¡ª: Œ>'Á‘#fÍÅU,V¿TFZ6ß1å+¶¤­#´ÉÒK/ÝäðqÇgÎhÝçZh|G'G`²Õ6¾ Æè¥½°«Ð¸àý9+÷ÝwŸã š”lcˆ£và 7Xã ÅÿgïLà嘲?~1&[ÄÎ0ɘÁ ‰-’K$öal!–ØbßÅ&– b‰Xc'ˆÅš HìËXÇ3ˆ± Á FýÏ÷¤O«×¯«_w¿Î{ÕÝç|>·k»uëÖ¯ªoýî¹çž£Ž%ˆ) àȇ_b-Ž9 ¡ÁéÙ³g6Ð#ñ„ÊÈõ™aÁ^´Rš—ú‰ÚXmxøøCfˆKDÀ[lâF;í´S5²žG܃>X qÐðÚ@˜hL°G@ ŠDÉ×Çi%Ae åã<°€xvØ6ä ÷Øx¿„-Q/â`B£FN#K]‰GcLÃn‚Ñ&õ-™î¢Þ^â‘Ð(Ú(Ò0B ±Çž§j⌜°a¯‹# éy÷pŽù¨,{ÈöçÕŽŠ‘—ø}`»ƒ±2ÿ{mKä9%Ú`?†àÜC®p>B ´]ZC EÈ‚IìbaˆfÃþø”²š'Þë‚\f̘¡ÚšF…Ȉ $‡'ÄuÅvG?è|øJêGl¶‰'j= RñF¨˜^õfÏêÌýP^®›-d ²—+b$Ä04{¿ ™™p} MJj05¡Ñ&vI¾û%¼‰ VÅ}2¬h3bì<_Ö7òádŒyŒ¤š%HhU‰ƒH‡„ál:A¹A)7¾£•Un\EÈC}bà­f ±ŠKi×Ë]âë(_›I'³ÓZçžçÛŽ@U#û/'ÉMWÌ ã`éDÌ¢"ê<Æ3#M´:‘ ¥I#"ªäˆ™BxÔ R>þz,w¶šîÌü`À(jììL Ê“úë, ¥qËfÇ@šYˆ¨Ê£m¶Ù&Õ±ncü(¤@×ùÁHšº Ì6Á€Á ƒqéµib6FœˆLׄPèz¡|bÏÔ`öôLuÖŒ$=—i°"!Nºd[J$Ú¬ˆYlȰaÃÔ(œuÖËœÇg㈺=’¡­'Ƥ¢R#lÎÁ ”èã&žËšˆöïß_ŸǸ?ž³l5³ä‘äÙe¶ò"|-cêK±ÝI®Í¸{Ií%¡†eh#Iý$5ë}•¡%ìÑT!´m2Œ–ÜÑTþ|ÇiǘaÆ5‹fóÒŽ"Bb´}µs­í´m_V7¼Ë’|¶Z…Ú™ÑÉ+(2Lµ3ôÀèí`ÇÂU¡^ Æ»ñÙÁZ!Á¨Y>æ¡K—.šð‹=ϱÇÛè4ü™0ÔĶIÔh¥hFdö—Úå RghŒ½‘xo®P¾ÜŠ1Œuî¹çªWZ4cØa߀Š]È\6;C„2ãO5Môfe¦‹ã~±]Úo¿ý²yYA-.Q{¿³Eûf¶R 2Æ6ÀhêÔ©jèMOû³ÃŠeóUGæ1D`¸EÒTÑÀ.]kÐNŇž“î¯Ñîiǰ›äšÅ ÃÝ´£ÿm ØÍvS»ÉãâÔ+©<Ëxø’K.©ãá2EUg¥aLÍGÁðá!ˆ¸0S‹ÙYÅÃ[Øá0îÎPçš­QîùÌ–ƒØ0ä'¾J´AŠÕåæÏÝ–BvƤ/.‚ÓPÑhÊ?ÇÖ©7XP3ƶc²>\½ûBð gñ!AŒF±‹2ÃP;21´—bzΣf¨ñÜ jµ(ìv”{ûBÒ—’fI‚‰/éyvnl!@#‚å)’ooI› nŸsN¥òȵÑÁ(˜Öº$f,&‰ÆŸ²pqªL{µ‹Ü®33i¶,­½úŸ¬»‰@‹ÌVkª.3%ü˜cŽQƒÅ‡~XmZì<ëuÙv|Y,1â›ÝÁ:Ä$‰qCcHC©Â‹šKЬŒ¸ñr¡|–?¾¤Þfß_G«–Kœ8ñ$å ebÓTŠÐ{E³æâƒ€sä]‡L>.Ë'HÅÔÑó8Ž€#`¤‚õ—!/ÇCŽ [å}ÙñêöÙgŸ†;}ËH B† 1BMÃiÎÌ‹”ÔÏ«á8Ž€! ›#4ر0ó‰q­UÔ—M#àä¨iŒ˜¶ÏµÌ“÷„ ÔGQ¡rsëãÛŽ€#PòÿC s©$4Hsâäwê\Í…F䢋.Rm árÐòÐÆ£‘ö×Åä ‹ÅSÌ=Oóh§°¹ÜxãÕXn¶Ñî@¦h×tÆg„~ýúiûeù͇íë´}<ð€ÆÍ0GÀ(T#B‡XȪÍö'ŸÌq…’Þnß=Òø0öM¯Ž iPCˆ¤ô2O×J¦yäݦDžÁc¡r5£ÿ8Ž@YAºXN¤×óxZn?,t®D³c‚ï3Ú\!~c©b†×œ‡×|ñ9f~ËÌ×~Òš²G‚pa„6ŠÀå\smG hrDÔ{¼L“è•*žˆñLñ·^Î ãÇwÄÇ„˜eôúÇG#dA1Œ<äC´ç!²ôô°P“3¦q#Þž­‘Bƒ„Wk$©\»®/G <2 FÚ+”WJåÎbm1ºJ§Mt)‚¦Ú„Ž`’ †Óˆ•H›‡@Îè4Et!F;wÎ<¶Ðù~̨gZ„½÷Þ{:TÅp•“ÝË÷Úl‘á9öôÓO‡çž{.0Ë"~›­q‹ç§÷•/=y‰|ÚrÄø;Ãh4F4^æ 1©\«§/G |„X —³Ñ"1Ķbù%µÌ™h¥±¢]$Ý~ûíy/Lû"qó358Ú²xÐçø “aVp×]wénÌÐ ÅE0Sm—‚å4SØ¡E*Uh±gBè8ÞsÏ=¥áùªE EÈ!/$³å‰#ÆŸ:·?nêíø>ÖùãKUÕ™1"û“òŠ@ß«W¯ðä“OªVŠÙ#äEE“¤rí¸/G yA!%\$éñ´$†Á°G"&!íE>¯ô §Èv\hÑêÐi“`ÓaÍ5׌n°Ž£ml”˜‡/¥¸Ð>寛„S±œØ‡—_~y¸þúëõÌÌç‘‹#P7 .' @è‰UÔAó@ùWw—DEŠ,^ÄÈP#ßÅ^fgD2£"£l- )2üc=‰£æ™2eJ$"Ž-šŸHÂZd/.ÆØ‘Ø év¡ôÒs‹$n[´ÝvÛiÞÓN;-’ð"‘¨®u»P¹Ù‹ùJM!À{,‰ùÑøŒÀèƒabrµ•4¯ÜlYÿ?¯iÜßÃ$1­ë·ÍÅKʘGRIaä®&i#Iý$IñͱŒÄ®§`!2l‰MRƒ<‚'úüóϵݒ™n Ž%mHœIÍŸtœý_}õUö°„eŠÄ<»í+µ‹ï²$¬î7•Ä÷í+ßg¾ÓóÉ{{U-¢9’SP0d¼ž¾:è•" ‰Ñ;#6[!)û$Œ™1‚0uß h”\G e†íó$1ÄÖQ–©†ÛóÅ,ŒW˜á}êïg³Ü`йyl›˜†q3Û_Z¼Il,¥CØd»?××G`óÀ&Ëi° VDºn'i!IôÊ`©KHº³ÔrVc†ZS|)»YÂÐ^©è›uA?¹jwœº£9úBÒ—’¾–4[Ò$¡*À˳Ë\D@žÁÁRü‰’6¼ÿYÎ¥¤ $m=hÚªE$¡¤­Wj[%çTD† ¢Ä¥];šÐÊ ³t·Ùf›Ð­[·Êî%¦L{ÅÈÍg’ppE¢½ÂàöjŽ5¿l¸4@%È nRip—DƒsWk58rmG "d›í¥0ÈÑW’hl¾“dMy= )À¥xä9 ”Ü'KÚLÚ•朜r~*ÉQ©÷áùBdÚ+'G…@*áX¥b«ÑƒæCÁÒ{Ó%<Ïšzìö÷º••¢+¥á§}y\–Ýeû½VªŠ_ÖpêJ#-#FxAÃ;Z$˜ŽIÔ Ž~›µ‰ï5ï3ï5äÈÞs–.-ˆ€¢QÒ¤€ûcNJ~Ò¤I¡cÇŽø¶´3=·#P¿T‚YÏšˆ‘£½d1}†Úrcè>\¯8ïj’ÑÅhŒðü ×ú¯$†Í°-š%‰¡4Ècõ$× ­%B®Ê¤Ç3éÝÖªK5]Ãì>}ú89ª¦‡æumušKŽâ½i>|H0VÅ&H› iâã1rr$ 4SºÊùëKº[ÒŒf–å§Ï!>¼£öî~+ë¼Ãj[$KŽ9²w^v¹´4B®Î!HïÌ:ÜÿýÇ ¶Ì\>|xWê« §ŠøÿAƒÇýþýû'î§nW]u•žÏúÀàAƒXÕ8jC‡UG‹Ì¸%3ͦNòí'ÄÒ ¾âpIˆùF°[Œ»7ÜpÃp÷Ýwqs¢åóƒ?¤G}4¼øâ‹A\“„?üPáàÿL̲#þå"ĵ$tÊ>ûì£!“¶ûàƒªo%⺭¿>ÍŽ‹#Pû4—=o>$zÞ$>.V6>:øqr$ 4SÐÆ­!é.I›J)ɇy„2²Câ=…ÀCˆÐ}“YçÝåŽ$ùF—9ÍS riý5Bxf6ÄVQ‚Ä£=î¸ã”(@ð˜“YŽ ÉAx¡wß}WƒÑâþƒ¡«|û_ýu%:W^y¥ÎÄÝu×]•¤ì±ÇáðÃ×0G8äz”}Ê)§$îÿóŸÿ6Úh# ”=yòd•FùxÄ&°,äé„Nh.×!x6ä )Lí5jTÀ­ åA°ÄwRöB ¶ºÓGbJ>ôÐCor«­¶Òh)G Ö0SÎ}Úpû°Ì/ë|\¬\>6|\Ð"qŒýn‹$ 4CúȹJš" ’´ª¤Ç%¹”€‘#Ó|òÎBLƒÄ{lÃkäuie„À\—£Aú¿JU‰P#GŽ ›l²‰mÅ#5¡Šë®».üñêý»&í‡ðÃÏÕšB†@’ODØ4SœO¨"b¦åÛûDGœä⦡"0v—.]ñ+ï¼óN%^z¡ÌDJæ^xa,BøÓŸþ¤d/“%ïBIj8B3¡!CË„f H,y$ñÛ6äI¾¿î[pHÈ󸑼ȔJ$ œ×Zk-%F-ÜqÇ“Š HìÅpë­·jX¡/¿üRÉ $%i?v>œOˆ$dß}÷ÕµGuTXgu4lÁf±ÿÁˆ¡µ|û%Ƥj‡0¸&˜,†Õ+Á· aK*Ì'+®¸bØ”JÚˆP0mÚ45òîÔ©SàšÜ+4®Gp\ÆËW–ïsj f‘#>Ò¡ âãAY$ûȰž·‘"Ó9APJté‡Jú‹$¦™Ç…ía’Nr)½iXš‰e|w›ã.)C Cx6io.Ûo•[EÈÇ…^~b(‰Ùh_|q¸÷Þ{•Ül½õÖJpЬœtÒIjËéÉ·mÄ ²ÖÒ bxŒ!*Â%A:¾øâ‹ìŒ¸|û¹Â`H–é_ÿú—Ãk­)!îä‰'ž–Yf™FY!m›wìØQg©AævP`Á½CŽ(û&G (;|Hœ A‚ô@€ò%Ž9)ŠƒVÚúPÉŽîØ„ÓÀöqIh‘Æ%äñÝM#'H¡Ü$ß\×5 cë嶈ñ+: ±½IMdÿXDI±Õ˜öÛßþV‡—Ðè@DÚ¶Å][P²±É & ‰ÈÝ/õЙmœ‡qw\°aB«·câxÒ~Èõbè‹`¶ÅÊìÙ³5°vn}9¿P\Kf²¡ãžpeà’^æ¼æÚAöØjxL!GÔ#ÓÑY‚$!¶mëºÓŠF#ì‡$­-éËgu•c7HZKR~z“ýPV#A2íiŠtÌÈqJ?Ò¡a½P’¤LÛT29Jÿz _prô •XkÖ°Z¼™G™z_Œñ3~‚°!*ŨÜ:5ç¼bï§9×ðsjFÀ삪ùj²îÒ°ãx°¯$ìŒÐÅ–˜Gäì³ãgÉþ9ó†eÅŨgät‡Üÿ I÷KZ­,Þÿ}ÖßÔ¹„ðÀ/°ËâŸáuyûí·5$H9å4uN±÷ÓT9~ܨe\s”â§+ ;þ‹H DzÃ+ÈŽ]å8¶H.Ž€#P;åð|’î‘Äl¶Iª¸Œ7N}í·ß~Í.û…^Pç“}ûÒ?rq–FÀ5G-¸_ÏpZ±rQlòn“´rS †Ùzë­§qÈî¹Nõ‹öcµÕVÓ4b£Þbäwíµ÷ŒåÍ7߬ûN?ýtõœ½öÚk«o"Ý)?½zõRÏÓñí>úÈ6N(ñ„CF‚Ó"8_Ä ÷ +¬ ¤CwxÖÞ}÷Ý5°-.AòÊ+¯„Í6ÛL]à«É¤ÐýX_:Ž@ Ñ>¨÷W_VòøÐ}èϬzž™?«Ö{Vò_Á`¯¤ö’ô…¤}$õ“$¦¡üûßÿŽÄf(’Ñc= ¹‰~ó›ßh&ÑEâ01zà"!'‘–HÈP$Þ²£vØ!:óÌ3# ³ÉY$^£×_=²‰·éèå—_Ö2$N[ôÆod/*þƒ"ñ]‰¶(’‰o¡H/F6g‘ø<Š$„G$ÃvzÞ†n{§HüEÌ6zæ™g¢ÕW_=ç’‘xÑŽ„ 1s8ÿLQ¡ûÉVÂWªÞeI;KÚTÒš’V”ÔAþ½æ“ôo} ø°š¼5.Ž€#P7`{ÄÇâbIØç5Âi¹þØcçø\%ÔšäòË/‡zhèÝ»·n 1Q#b§õžµíÛ·×Ød„Þ@£C˜Bq0T†‡ì¦Ã綾^:´k×N½VŸsÎ9ªmÂÓ6r饗ª“HÖ¹æE]¤ñÖðn=ÑŽ;îÈ!uÜHˆ‘÷$ˆlÒýhFÿqF89j‰ïpG๿v’NÏwŸ>úhØh£²‡DS“]çwÂsÏ=Î?ÿ|Ý÷ã?êpW6CfeguV;v¬’%Î& 3Ü Þ²ã´î#MŸ>]É“¢%(¬h²EÞ„úºŸlf_q¸ÍQ8|ÃpꦎÌw¯„æ ˆ Ú¢Õ_vÙeá³Ï>ÓôñÇ«=·%ñψbϹ„ß ¸l\â@¾ùæÍ?–»1â:&”ùÄTN 4ÛŸ´,t?Içø~G Þ¨ r$cçóæ¤ùd;ž~%Ûu“ä¥eæ ![êæž3÷æñu{?$[ñ¡êýÏï÷žÉ‡ÃW“'OÖf?ýôS¸ã¼Ì†®ØFD"àlÜðÙòaTMÀWÌþãÿO=õ”Ú{pœxmbˤYzË'h}ð—„ôéÓGëÃZ&†ò>ýôÓ|§åÝWè~òžà;G@ƒÄ¦ýÊeH‘T!`DV ,-ýZÖë)qÿ™ÖÓ=s¯ö¼Y² ¼–xO J*²î┌ÀÊ+¯ºwï®CYØ Å:b{$ΡS§NJrX?á„]ã°Ã §žzjèÚµkØk¯½ÂÞ{ï°š9sf8äCtÆ>‘°o‚,å 6Bo¾ùfÀ-õ 1[ ?HØ$‰ñwî)‰Û…î'ñ$?àÔ9 <[Iù²Iy$>v,ùøAŠïgÝ$¾nûju¹œÜØ’êêkõn¹/Æ"æŒGÌÙ‡ãK¶Yb¸Á:K¶‘Ÿ™‚2gÕëL{™Æª‹HZLaæÇ%½"Øú`;„qt\È/³Ëtä)IÐü|þùçaÅ™4tn©¥–Òõo¿ý6à¥z±Å¨F~Á^ˆ<2SM3`Ø-³ÓÔØ;ÿ…÷&ÝOá³ühµ 0ç³v‘ú~&if&Í–%*Èä½-lÜ&™\~A uä(‡!B[4KR= _ž’¯ƒ$ˆÞO’âdIÚ'HÅX«yÊ%GµŠ‡ßWm"à䨲ÏÒ‘F1íõ£ÇÇ2uD®ÒÀ}÷ÝwZ$Sx+!ØD`Xj! *QfZˈ}yw Hˆi“ælù¯#à8Ž€#PhfÒ&qb„M ‰ ¬-&¨¼aáñ„ae=töISaŒ¿&÷Þ{ognÙâ:wîÖ_ýìv¹+xÎÝ~ûíÃâ‹/®ö ÌV9ãŒ3²Å[?N8ï¼óaªDxOŒLÛP,¼É5ŽUò½šŽ€#à¤TiŽ21>d|ب ãÛʨR¤ RKÿþýu– nùï¼óÎð—¿üEýœ”RNRÞÛo¿=N<ñDÍrüñÇf©4W¨3Ó‡q·Ì2Ë(EßÉ@IDATϽJ”¨)‚*Nå [U öž˜’“¢*xh^ÅÊ#`¾“°QrqÒH³æˆº¡H$GÌæØxãÃW\¡pfs@401ÁYÛ&›l¢³D€å–[nѼ7Þx#›A\ì‡ÓN;-ëc$w›<Ì&9ùä“Õ;.Sn1Ì|õÕWµ|Œ%9ÖIf­à‡äì³Ïæ”F2{öìpà´7ÄXÂÛ.š)b0=þøãšë­·VÃˇ~Xg¯X!xãåžÐZ/‰k#…î›{—zÞAúõëF­Ó۶ͯ€Kº$ëçŸVŸ*ĉªÁà–÷m#,“/Cßq©–K-!pÒI'i{–{¢ctÜq„’sqrH㇃Þ>‰›Í0á£×H ø9òÈ#Ã_|fÍš¥Ã@O>ù¤ºí‡¸ÜtÓMêÞ­ÌŒ3ÂN;í¤å0T4dÈ,9ÊÝ&“Ä8 Ÿ|ò‰:`›4i’Î6aj.£48y:thXe•UÔKíàÁƒóú;>|x¸úê«Ãl ‰mü¢0kÒƒ@€(ó¥—^ÒÀ‘ìC£Ät`ˆÔ6Ûl£Þp7ß|ó ±’B¡û¦œ.]º‰í¤C{ § ×µ°”oRè:C„¼¬É%Gö.UAÕ½ŠŽ€#à8©AMCZ’€ÂÇŒž?¬ù³¿“ÔERwIR͆òì³Ï2|ýùÏÖ_d[üŽè¶8J‹dªl$~At?ÁM„@E¢EŠÄC­îŠo ùÐü”OŒϸšŸ@’2ì yÑ2D«‰I±_<âêºDôŽF‰±u$ÃrZžø=Ñc»í¶›në†üˆvIƒG²-1 É= bû£y%"xÔÔ}¿ýöÛ‘øiÑó­þÔ‡à–H¼~…®C^[î²Ë.¬¦Z2÷¹‰,;Kú­¤%%1m-’óË ¤æ]÷º´Ü³àùK¢]!ðìo$­&‰ø yÏî¹çžú_µžÿíÁ¬›ÒIŠD‹Ig!’xj‘LÕ×ý¢±ŽD{¤ë={öŒ$ž™±-3Ý–áy (K Û}öÙ'’îg“‘tà"ÚÖ^ýõº_:R‘„*Ñÿ íí€h±#±Ô6FB™h>ÑðFÔÿ¶8}Œ?üðH´ÔzÌêyŸùFxàÙ µóiÓш!,©Ú#>n4l‰‚¦éÐÄâÔ!ãYí Þdñ‚æfÛm·Õãü; çlæà-w›ø@í‹ J殩kݛƑF•€’ætŽý&ùî½ ÃAæŽ)Ú)õ¦»ÄK†!{q)æ:ñü)_ç=1;#ÈQÚÈÊáóê€h¢Ã\ ÿ= › ÜzÍ5×h§käÈ‘jËH‡ ´)Åʘ1cthýè£ÖS =Ë-·œCfú‡˜‘®¼òJíÔ‘SþÇv¢ÅÒY­Ô±Â¼€ÿ>ÂLXGÀ(´~< HõãcW‰Ãˆ¸FØ1 ì€P­sŒÈÖ}ô›¶ugìÇH Á$ `øŒ}=EŒ¿±ïÉKÒvêÔ©1±hÚ4¾¹Ú$f‡Ñû8p`¸ë®»´±† '©0}Ÿ:Ž1"`Mo’2°SÊG ›ºu„ÜÅ5V…®ßÊÇxOxgâïµ½K­\5¿|µ €!ćÿ%¤£[·nN޲ѡQ&äÇøñ㋺%›9Fg‡IhƒI«­¶šNx Cƒ÷j&v˜ Qâ8"Cp¶[µM¶Ÿö’†PW´ß&ùþëvÌ—Ž€#Ð4ñHÓ¹[&‡}ÌX’¨#©$¡Cåß ¦£zè¡áµ×^Sm ѽð Ú;Ì·{1ÈÃv ¡'27½9ÔßôìÐÌä›ÑuÌ1Ç(A¥ÀP\ûöíu¶å£þ†8õèÑ£žcÄk²™fbó£š#›UÆñB!"ö®¨ÛC=¤D‰,¹ÒÔuh¤iЙeWb7UPe¯b@{$6Šêãk÷Ýw×*2†ß¯‰'ê$ †ì™‘O¬³CçÈ:T‹.ºhØrË-ub‡Ø.ê’Že×Çœ-Š™« ©!Ö‰Ê̳ÂlXËÏá*éÌä¹ßå¤þÄiI 7f-&iEIkHÚLñb¤šé *E;a™$ä1ÃÍ|y$fR¾Ýºó„œDbG•˜'éFžgª›[è:Ò;Í®[þ´-y?$õ•ÔM]ðå%-* {µy¥¾©yϽ.-û,äùÃ`xŠ2ÈæÝfò‚h‹£e—]6k4ÍÄ !7ŽÄ•G$CXQïÞ½u;n-éH:2º_†Çx/µ¼éÓ§Gb{½ÿþûzì–[nQj6Î>ûìH|E¢-ޤ¥×•NX$Ce‘ø(ÓüüP'™‰ªÛL@Á0íxÄ&ˆp>ßn­ÐÔÍï™$7È®P[_Öp•<€–ë–ñÐS)ZÛ”ü¤ 6u¼PðI4R¤rµ;ö ÅH¡ëÌ?ÿüTÂûBÒRÔ׫˜RÐÄ`߃VÇþ¿hp1Š^wÝuÕN?`ØðåÚ÷ MfŸ¼ Ý3‡0Ô%³Èô?Éd ÜtÜ|óÍz ÛDüq]´ÔøM®±X‘™ojìÍp °°Å[{ªçs<¤*^™¨¨ÑÑÃ#Ñ2ÐÓc VÈwò§wqò!ÞÀ÷ç’fJúZÒ7’ˆHý“¼;?ËÒ¥wÂl>Óp´Óô:ƕڮ09‰aˆYg¹V!>Øåëøàw [GlŒ87. 1Ôžo²E<_¾uÑÑ«}#öQ.õ…@¦ d”å3I´¤Ù’h÷œX“.E"FÍ1ÓÅo¿=ùöÇóøzý"`ÌÙ4GIïQý"äw^Ì9*…™V)·`\|òI!--šªr²fÆÚå–áç9ŽÀÒHލ:#BÙe©=¼9·˜îß™3gª‘vºk™þÚeˆsœ IbŸíOÿx GÀpZ†±Ò*|ܨ_«Ü e8P$ž‚¿ 7ÞxC×ËýÁ¡¤x Ø-’´Ål*T×”3"Äûc)%Uój8Ž€#àTi#Göa£^|ØÌN„õV‚¯4(;í‡p¥8ËWqÜ`“ДÉ|çú¾DxOì¶w)1³pÒŽ>’ÌORšêjþ•*]§´Þo¥ïÓËK?ö!IcM›´Áù"³<ð=Â,f‘àªМzê©j܈!™*«³88†—kf†àØm×]w m!Qî¿ÿþ:C… ¯ÃL÷ã3ÁÑ#3Pp¸†‹›¥"ñÎt¬Ÿñ~œ/šà§Hb«©†_A&8¦ÄK.Ëèîûï¿_KâlRb;i°XËoËRëmçÕÑÒH4K[¯£Û÷[m-æ–†—P%çœsNkÝVÞëŠ+÷`3wâ‹-.Ž@k#Vrd#ðI¬£M…Ź"žl™A!B®¸âŠðàƒñ¦L™ħH6”Óiit ?(œ+"x”Æ[­9MÜj«­‚ø Rg’' Ã`8–Äët=” :T§ôÞqÇð\ Áé$(™fÛ±cGÝÇo!G)ña¢¤­˜˜M¥Ö;{Áú[1räÚ£ú{ö~ÇŽ€#à4DâÑì’Ë/€õ²ž?¸Ä^gémH”j%<ÿûßõÊ }áo„Ù„ÀC6AX±Â;-ù )Ä4‚¼àÅööÛoW’Ã[¼_¯½öÚj_d¡CÐNžƒY(¸ÿÇ ÄŠ²Å\èܹ³º%†’ $è°Ãk೟Aâ\NÃPåBªúôé£ÆÙø*!\\Ê©wüü:Y·wrøÎÔ ~›e"@¼2Bö˜°N[‚£áíÕ«—j…í|¶-LÚnâÒ.ÑÑÂÂpÞêiWè|=Z÷óƒ}"ÚkÚ q™Þ§Sˆ÷n4ãxÞž">ÞtJ˜¼QŒ†7_‘âÁÄ Ó2csˆ¶;G4Ý4JæÐ‘¡wˆµC´Ûo¿½†B—$:ö”ñB„4A iÎÁa£6Êoç°£Øz£%³û¡ý´Ùn…ÊÎ^ÐWV@ ä(þ1+ûGÏ„éò4r4:4lØá–=6ˆÍé§Ÿ®=6þ°ô‚ãOL–"1â\lèåA²î¾ûníAѸA®0gÛR„ž! Cpô陾ôÒKÙÙp”UN½K©C å5‚”Æw»†`®í[A{D‡ W )!¦á8qbøøãuH>I3l›o¾ùFí9m¶A »[úàƒÔþhÕUWÕ2ɇ<üðÃYûÚ¦\pa‰Â\€É'qIÒ¨£çš&ÜBÝLsnuCó޶mWÜ­»I¡z'ÕÁÎeY¨ìx>_wZþÄiIró´ê iEI%m&igIRÍÒ„¨ôo¾ùf$ÆN”á³H96Ú/ÚŸHl"ipÕsvŸ4zå#Òû‹¤ÁДÍPÆŠô,5Ú7§Šf+íT£RÊ©w£Bjp¾’ºIZMÒò’ÚKb\t^¹åÔ¼ç^—–}òü!̼¼¨ax?P…ô“”÷ß v8‘LĈĦGÿ‹d5jT$äFó‹b$CA‘tšt[lj"éXéº.Gb‹£ë2<Æ{QÞôéÓ# 7ÉTx=&v?‘hqt]\DBv"Ú1´Ö늶8’!§Hf´j~¸Ž kiû$Cþ‘tÆô˜ éu8gÚ´i‘t²²çО‰öI·…èEÒqÔëPéti½iËd8-¢D”ÖAì™"ÑžëùÖ>q¾•Wl½Á’²±åŒÄ ]×Å$!’¡·H:zß”Ëý¹”Žï™$¾•›JZSßP¾¥|S瓽 ,ƒ4ÚÉsÌJ\‹”ÝYì =.z6ù„qõ|‚Ãmù¤}{ÚÖ9‚ñ· ½G4TÍ•bb6•SïæÖ«ŠÎÏ÷¾äÛWE·äU­0|@Lâë¶O—h4šbV™ÅOCÃË9^4ÈýÅŽ‡‰¹v2SpÀš—^h”†£pÇÁ°ÿuʸùæ›õÃñø^ãºhpÐ`36IÐ$s}4ÛL,aèÏì‚ØŸ$ÌÃ^ˆöª]»vª¥fÈË4çÜ#÷„¦#jÊGºwï®x4îfÄþRëÍ9qa¦0FêÔ‡7Zy—Š ønW¤ô:(d^È´ˆ 4GñèÙ°¢g/%iLšê*õqI™áí¥JŸKš)i–¤o%1mˆˆÔ6D+›.õ„€¼äù%µË¤EeiíÊ=¥¶+Ìî‚ð@b°„\`´ˆv5‹-ÆeÊ_|¡Sûîçܸ@J ù†Òâùla1ì p µáÐw#I‚}#öSäg,3Ô 'L¹G0BÇÎ’:@ÀâÂÐ?æ Æ\)µÞñó±zûí·Õ4ò]ÊC Ó¢9úBÒ¿%}-éIÈz( ”"NŽJAËó¦'G©}4©¨˜¼Öé‚ ÁX`,ï-•É9U+ø4b6ì1Ç£Ú!4a2 –Yf™ª½'¯ø2màN²1‚ AŒœ åHÚ‡Õʹ'?Çp| &7C}4‰h”"ù¨ÔÝÐ+¡ˆLð•äRð~3ú‚Ø’õô Q›*k(ª¤º•¯&cû'Ÿ|²ŒO1ÒÖu|y0#­à|ͦÅ“Ÿ)¹L奧z饗ªóÉBåç;†§]ü‡¸8Ž@Éð‘PB$KÚ>%Hü=9ÕþdÞí9~#~ Ú.»]ÊAÀ5G1Ôpô†$¦ñCŒJéP“ßÔ×ø64h32î_Œ0%ãM|7á/ÅÅpJFBdÚ#>"u§1*±"NÀ5!Lе“*¢HÏR>öN³4;KÛW~©uxfUkŽðp¿2Ûû¸ê'v“æŒGø-1Á?‘ïã‚&‡dxeö D?F35ØÆÏ1€lüüx~´;øEb îó©!Âp‘8IþŒpã¿ß~ûé6?øI"„ †žqa´h3_âÇ’Ö ÅXJ:Ç÷;5Š€iìöhû*úшkžñ<ŸfµZ¢-È™h•i­%mŠyÉ.µçž{®:ÊÅk7FÚ´¿Ÿ|òI©ÅxþÊ#À»N²¡µŠ¾ç•¯n:K¬jrÄŒâ ÿ ÏÖÌÄ ^Þb¯¸âŠ@¤{H.õm¸ŒÇÀ6yã‚{~HÉÀÕ9$aDĈfÿ$ˆ¸DÄQÛÿý³^­Œx~ˆ. E » 0@³AÊðn{É%—è61“ H+f™ 8˜cæ‹MÖòÃì¦ØZèÛ_hiS… P‰§_ˆÕ©§žZè?æÔ2öѰ{d»b‚æ9N„éú´5$ÂsÀšPi:޽.Uð´M‡ǘ´­$È æ.­ŽßuRüýޝ·z«¡UMŽ_ D€†ØôèÑ#tïÞ]µ5æ;î͵ÐAËÔX\ûçó/"ŽØT;…'Æ ãåÒƒLwÞyê#åŒ3ÎÐ@“Œg›ÐH"ø4a¿)„^=°J‰ÅXBãEl$¦éº8uˆ€õží£aÛy¡ Ù:묣ÿu:1 –ÌÄ3/úDµGr5Ϻ3öÃð9*‚Y[ø¼óÓ‰B#Mœ5“~ýúiä{üa£Hç 9CWxØž‘èØA ™²Ði ê²ù曇gŸ}V÷óCçL8†¦ÝÂ4e3øJ1üò±™“Û&s®çÉ Põ䈙榎ÄC3Á‰x²µÍìÒþ¤ÙE¬˜á5YqÄHØ|‚/N:eýžà·£){$×øñãUEØ€J\'ÅXÊWwßçÔ8|4H´{‰ >È&kйaxžÂ;Ãøhq‰ˆÖ…¡ñ\ͳfŽý0Ü‘‚ à|‘ð@0"ÒSD†ß_{í5%-8H¤­”ÐÎ1’vØa‡©y±šsˆ¾Œ ]hzð­D9tÎ 3&¥$åã608í´ÓÔ÷šqœ5BÚè(6Lë†fš¶ ¡“H™„R¢MCÓİ#‚o%ÌØÇ:ÃrÄ·DSOÙýåCF>õDÿ)#þ,-{®çË PõÙÅbm‚a\“cû -i8è!ôæh<ó ½báºAÎhürmœâç¢)b¸ŽF¤sçÎÚCŒoκÇæ”áç:5‚€}(Paؤѭa{Ø·oßì‡} "aA4æ"6…CøcÆŒQr”«yfâD|RZb lÆ…ul%!h…â"5Âð9Ú-8û)ŸØ‹h|ªCLsÎÿÍ9ô7hlМCª B6$–Šc´Mtž k/†ó僄!h²Ì$€mÚAâQbpüñǫƒ‡öÜA¼m£ÂŒàöÛo×I%?a¢‰ƒ8Q÷^½z…í¶ÛNÏAGgÓ¥$¬ÀIñõ’ ñÌszP5=þxüÑ Iü9ó d&IKˆ0,E¦k×®ùŠÐa24GØ'!¨äÑ Å…“ÆÌÔë4šh¦èiYÏ+ž¿©uzµØ3!ôðî¹çž¦NñãŽ@="ÀÇ‚^†ª¿ô–r “×Ç£ÐCxèÈÐQ"¡]¢3”Ohð†M’X‰ÚYt{4Êh¤ '´%¹†Ì µ› éÞ~ûíUk‹K1šsê ‘±:3$H;ˆö&.Må‹×‰óhW17xùå—u˜ ”ÄIËÇ,ж¿÷Þ{zmÌ ¬> 5b7Jû Y2!ôJ%M ¬Ü_Úh½ßNÊ|àd™§WÇiôPèUñÇcØÞQ>AUÌX¾Ùh¨£Ñêl°ÁÚ JŠ¿F¹ŒÉÓë£WHï‡4.#zž4&Ö+ƒIÜKüœBëôf¯¿þzÍBïUµ‹#à4BÀ´Eöáh”t’òE®ç†¹,r=á;ЈäÊ!”)w‚ÚŒ´ÑìÐáÊmOLã„ZÒê0˜Ã>ø`ƒKq¦„:c«dufÉýa×—bóqáF°«4¡åž 9A„É0&3ˆ ×ÀÜ ,VŸ3f„ƒ>XÛgð4Ú]—’0?G¼ë#ÄÞû9[þ[ir|%5†¬\g1I+JZCÒf’ü#UmžHï,’xG ‘?t$=µydŒ^£`‹V&uvƒcI£("!‘Æ1{X\úG¢ÏnûJið~Hê'©›$ºŸËKZTA¯æ•ÒÜÑ_c ïáCÚKZVÒj’6’´½¤F/šØE¢ÍÕÈô´2ÄÉÌ3Í'CS‘ ûD¢aÖDDyÑøê1ÑrDBœt]l“"é5*ÛvÈðQ$ÃXº)Ú”Hb°e·¥ó‰M’B‰-e$ZÝ–!(ÞóH†ù#1¤Žd˜ÍŠŒdÂE$±ì¶hv"™=‰f9r‰ö\‰T$Z]x$C_º^(Ÿ ‹5([:}Ñ*«¬‰Ý‘žËØaE\¬H4?‘ ÝE´©Ò9ÄfIÛV!R÷I‹DûÉÐ_$$1G¹‘h4Ÿâd6²æóŸ¦à½Äl¾™|;a¿|K½ ,£íK»Í‘1Þ‚==yøE =—¦Ÿ$IÂ8}±‚ÑcSùÛ·§­:ÖŠ¸G.ÍFÀzL4ˆ-çlùo½#`ÚrÞ‹¼ï~È^ýuµaHK>ÒYÇ«ÌîÂ&†a"†¦°5j”bŠæÛâ”5åÜ£j܆`çƒöi†ër'cüµ¿Ì.ëÒ¥‹úMëÖ­›Úþ0ó”ýÅÂÌFc˜ ÷h«±B°#Bc¯5쇒òå^MZjÊ’¤Ú"†Én»í¶lVÚRfÉbªÀPÆÙÜ+×ASŽQ;~ßÀŒûCp‹Ý&Ã…hÓÌ'\¶P_)¾—´ƒöý,æÏ“ƒ@ÚÏÒpµÉ¤…e {è iIIt!dÑò2dÈ ÔˆáâÜ|1商ϥ<2Ãh>—4S^4Ñë‘U`E¶”åReÈ»ÁGÂÏÒûYD=ê%$ËmWîÁï~N2eŠ |ª!ä÷Ýwu¡ó¸`(=Q1Ã]†Ê°9D¸.#Ÿ0Ü„A6Ãs µQ?†ëJlŸ°Iļ€Î› †Ù¢Q×òÙ—”ÏòÇ—äÅV‚wÂì« } ³åvE³®Ã‰ì‡ÆuðcÎ¥x2m Ó1‚£ $a¹Oȉ ¹É¦KS¤‘OB#Ö^’‘£1²îLX@pÉ‹ÌyI_HúRÒ,Iße’“#¢^¥TrAÛ‚ý!n¦ó£1*g²D½bnä “KË à䨲8§uX¡ì2·‡W ô¶|=:zN f^®RŠõ¼)A ó4Õ"ï ‰mK)©©W#í0¤Ã¤ |áùç†4»îL#VüYžÓHi%G ÄÇa¶Š|Üð¢Me® þÅÙX|Ênnß®r ’‘몹¯hë#À4r’Ky˜¯¥òÎö³ÖGÀŒ[¿&sj`6êÅGÍìDü—–'”îz¡¦–ö.¥»Æ^;G ì–LÓsÈ7G …H9Šß¶YÜÇ÷5ZÇ÷îÝU…Ë87‚Cpøb¶Þn™a‚Û{fœpŒÞ2U¿4ÍÒÖ«þ¦üê¼q3³ -7í“9w­üNt Vrd#PJ¬#è¸ãŽÓ€ªÖˆ˜{ûÑ£GkÜ¢áÇ+9bú¨É€4öÓYñ‹{}—šBÀÈ‘kjê±ÖþÍàÌõ‘G âïGgÉå:‡¬}üt Hû쳚ÏG 1i#G¢Üœ‘¤FµÇŸ#œ†ç'ÃŒà‘LjÍØZ<Æjã°ä’¸Nš¸5¶KM `„È–vS‰ïeð¥#`@Pî»ï>%ŸBÑê ÝCGŒ¸aguV8äCÔa$X õAÜ0ü$‰Wê,i¨ :TCþàÌ‘xi\“}øâ\Èmìèè3Ü à0–xŠa@‚3aÛâ´½öÚkê‚€°â•Zà ¡)'â=!†¶Új+ zkçúÒp~A m䈚هÌêbû¥æ²FÜzNÄ6¢ ‘±éøÄO‹Û§a?½2³3bi½3Íà?µ‚@.Aª•ûòûhÐ0ãô-A§i7ˆjÉ †QäÑÄ0\ODz¼OC6®¼òJu¨ˆçkˆZ$zè¡ ¼G£I"V#ûñ¨MûD^Ú/®×fÓÑÃn?KD­Ç.É´PMAAñƧon£)Ç $b‰íÈ}¸8Ž@cŒ€4>Òú{ EF”lÙ V¨¿qß'[þüwÜqG¶Ñ`¨ ›"zd§É“'ë¹øÞ@•mnîñeØà¾Qmðž)2R÷Ý©¶óú¶,ó@](Z}n$y†»ÐD³Ÿ Õ&+¼a›Ä=FfíP’<üðÃJ¾ì¸iÁm;¾´NŸí[wÝuuUâ·éPCh¥Hhµëâ8H#9ŠÌ ~àèIÑÑHưÉ&› بÎÛP%û;$ÆêÍæ°`ìÙ ¨²“w©Œ ¥ñÝ®ëéF E«gH?>¬‘Ac½úꫢ͛HPÕеkWÛ óÎ[üë‰ãZ†ËLâ×CÓ„}“I<ûL“Î= BKN˜õsÖ†œ/†ÿmxÞÜÚ2bÄÒ×2m@ƒëJ$kσڛñu¢žîÝ»·ª¨ñnËÌzMõ>ëREi„ j”ˆab¡ÞXƒ‹úFšà=!Ù{mïSšëìuK9t¬Ú2âqë­·ª¡6Ä— l+ ÒB§ŒÉ!h®oºé&Âm iËl'±‚t¡ºì²Ë²Å  ÇŽ’a@:ŠÓ¦MË‹¯ -Ç0œº"Fà m.Ž€#дOÓ*êãÆ°š Mè)%[Ó“òÀ††TÍ,ó½/ùöÕÌ ûÌ}è8%E«ßwß}Õ.‰a3ÚŒ¦ Àº×^{iÈ4× ›±¤ÓVŽ™þõ×_WB†ùvP õ!;í´SÀ} À„=éÑ£GÞK }Â>ŠŽ!vG#:“]ºtÉ›ßw:õŽ@ÚÏÎ'„pÑ H²À³DÏ&Tõ˜b%¯K!>Ø^nâQxö[Iÿ‘DDê_Æd‡Ký ï9ޮЃ¢]!ü¸bÛ•BÑê!ØÅc7¢åA“M‡-nc$×,I›9s¦ÚVr+Ì~5j”ŒSÃjÌP+æh¸Ð‘— º.µƒ@¦ ÜEîè3I´¤Ù’0h£ œ`T6\šF 횣¦ïÀs8Ž€#Ð@&’3Ír¢„MRs’ÅÜàÁƒõúLç?å”S²Åb¿T 1â„6mÚw,™…ÎWDÌ6#1ƒpGÀh=.»ûî»Ãû￯lÏ?ÿ|k½ù•ÚGÀ5G™g|ÔQG©mÀ!Cjÿ©û:Ž@U!°Þzë©Gíªª´WÖ¨bœe*ë̘m?N¯º#à´$æW(nkd×ÿñÇÕHÛÛCÄ—Ž@õ PõÃjÄêÞ½{`J+1‰0šDXâ"•4~pxVh?#Q]#xÐÆ#-ždßÇÃ,³Cˆ¯Äì¼r3;…r_yå=ÇG þ¸è¢‹4Z¾;g ¾µ;ùŽû>GÀH/UMŽ˜eBpFâc aZ+‚ÿvíÚ)¡Ùo¿ýÂXp?ÎÑÓGCB¬6|‚à#‰XGxÑ&\$ òD<%òàPÒÅpGÀpjª&G¸Ü9r¤:\Ãñ¾Dž{î9}:}ô‘:Ec#×(i¿=RüˆÔ_ ølÅÕâøF¢·ˆwmH™;Q3ä|éÔIi:dø2âÿv™)ü&tšp É1ÖÚ›÷¥#àTUMŽX`Õ1]-Îøñã³È£ÑAÓðÃn\6i¿ˆ·lªZsôÖ[o…µÖZK‰Ãa z}1Ž&Mš¤Ñ§1¢FD–´ßáF(ƒÞ!B‡wZGÀ¨ i¤‰±†¡µ‰Åd„<áb„B$)t‘ëKGÀH/UMŽˆôO ¶È¬±5ÖX#ÐÓ{饗½¹Aƒ…]wÝU#aÓ€awÛ¤ýöˆhÔ YØÑØA’Úøë_}ÀÅpjÓHOœ8QƒÉ2AÃ왥·3"<Â~lŽÌΈ¥ å×:^~Ž@-"PÕÃjØaýî»ïªÍÚ¡C9D‰ cÿf|M·ùȶÛn›w?d¡Q#Ò“O>ÐFQ&vKøÃ´ÜW_}UóñƒþxC™=à+Ž€#PµÒH÷éÓGmŠ6ß|ó0kÖ¬0yòdõV+:Q ëãR„3\]G :ðÀ³yžѳÑDÏèÎ;ï ³gÏVmRž¬¾+%dzöÛKu<ðlJžIZª!ïFIg¿úê«€¶™ Lêèß¿¿j’¯ºêª°Î:넞={j›Àð;mif°ÒVìµ×^êsY­\vĈZVZ°ðzÔ.™6ÐÏVè;9Ê$®ïºë®ðÚk¯©M³P|X-P)Ú•iœ¥è™¤¥*òn”DެÞqô×_ÕH£Q~ûí·US„}R\Ð&}ú駪iμ“ñþîÌ52£ !ìä¨B@z1­‹@¦aprÔº!•W—w£,r”Ê›ñJ9 89J¦ÌÝUmïž™zëâ8Ž€#à8Ž@¹Ô9ÂÑJ+­Ô$äyùå— æCm~ß}÷ÌãGÀpG ö¨)rTÉÇó /hh’J–ée9Ž€#à8Ž@ú¨zr4a„°Þzë…?ýéOáž{îi€8³K˜ÆObÖH>ùÛßþ6Ûl3õiĬ /™Ê{Ê)§„iÓ¦…Ã?\OË—/_y¾ÏpCà¨£Ž ƒ¶M_:Ž@• PÕäèË/¿ »ï¾{Øm·Ý°aÃÔ›µáŽŸ‘¡C‡ê\œ: ö–[n±Ãº„1-·_¿~ÇoÌH#ÌÝp¹öÚk‡“O>Y S¾| ó GÀpr€}ôÑ9{}ÓpÒŽ@U“£)S¦¨ÆèØcUí½4“Ë/¿<z衟E;wx¹3fŒÖ%Û!¯Ž;†¿þõ¯räÇ ˜m×®:€LʇGÀ¨=î¿ÿ~ XsGœ:£ayÐAi»€gþgžy¦àþ›o¾9Ü}÷ÝšÚ}ûöÕ¸kgœqF8ì°ÃÂ;ï¼ÐJâè¼óÎÓöˆr_yå=ÇG u¨jr”ç(itÎ9ç­¶ÔRK…“N:)|öÙg P&¯9Nbø€³DÔŽK±ùâçøº#àT'8w<î¸ã®çŸ^oâ’K.Ñå•W^©&Í~ûí§a‰8´Ÿ"LAè ýö·¿ ·Þzkxï½÷ÂW\¡^´édA¢ O¶%Ï©§žªçø#à´U>­O<œ‡Å9J<Ô2$ì±ÇŠ,Ag¿ûî»(“gË-· ãÆËî§"\H\ŠÍ?Ç×G :øþûïužX¸ùýï,‹’'±šiˆ Iûõ üà8rúôé ’D­ø0?m ^¶ç›o>%eØ?º8Ž@ë!PÕš£í¶ÛNcáÉ–ÆÛ"“=zè6¤ˆ„Úú /´Ãº¤aCûôÁè6=ºîÝ»«Û0I¤P>Íà?Ž€#P3àõѪ«®ªZœñãÇgï š:P´\6i¿øì³ÏbAÒ® ¬¯²Ê*vX‡ñ!FaK¬íÉfðGÀhQªZs´òÊ+kõÇ?þ1,·Üra›m¶É‚Çl3ÈÁ *[k­µÂ¨Q£²ÇY!~³Ñ*‹ï#SosŒÙoo¾ù¦ªÎ¯»îºÄ|äuqÚA€ÉØÿ<õÔSÚ.ÜxãP–;„À0ô…í1&p0l–´ßPá8Ú%Ú¢yçÓ'ýøãípv_v‡¯8Ž@«"0ç_ÚªUhÞÅoºé&s„a$cø~ø¡Ø¡C‡ðä“Oê,4¹‡z(,²È"z M3ѳÏ>[-¢i3,‡*Á@{ÆŒáÒK/Õí¤|zÐG fÀ•):L¦‘Æ ÙgŸ}tÒ6Šh£Ñ¡™NÚo `ÏHcÇŽÕ]7ÜpC˜9s¦ö¥#ठªÖ–™|B¬ìš¢j“reþùç$“¤|vÜ—Ž€#Pýì²Ë.:Ϭ1´ÉýÅþçâ‹/¸ÁÅÇ!Oß|ódÓ.$í74^xaÕ4yæ™Yµë¬³Žjµq¹rqt!àgÓõ<¼6e" º¸½œþ¹$ºä³$ák/ÏÒkÿY–.uˆ€¼óÈmÿZÒ’’„ y1IôˆÆ™VHÖffŽ¡Â'D¦m۶᫯¾RÃj´AmÚ´Éž“´Ÿ ÿûßÿÂsÏ=6Ø`ÕFQ&vKØ;R®‹#Ð\2mà.RÓ²iI³%aŽÏ:ë,}by}é8Ž@s@#ýÆo4§?×pR‚@U«áEv÷ÝwW_}µH@Ö<Ë2ívèСó‘»îºkX|ñÅ5œÈ„ Ôë5^´]GÀ¨h¤ûôéãÃõ•ÓËpZªÖ¡"Ê5~Gzõ꥾FlZîå—_®±z÷î:wî¬AÇŒp¹à‚ j Y|¸8Ž€#‹€k¤sñmG ¾¨jÍÑÃ?¬¡?ì‘ÄäwÞQ¿"矾îúñÇ5V’÷¥#à8ùpt>T|Ÿ#P_Tµæß!4–ÇUD”ëË.»,|öÙgš86qâÄúzº~·Ž€#P2®‘.2?Á¨9ªšm´ÑF3xjx … ™`OD`H\󓈃tá…êaH•Ù&Y~_:Ž€#Mi¤Ï9ç–'¾ÚI'¤/GÎpj ª&G;ì°C8à€B÷îÝÃï~÷;uÇoîüO9å”ðïÿ[®€õN8AŸjî¾ûîÚzš~7Ž€#Ðl\#Ýl½G ê¨jrijÐÞ~ûíðü#ì¼óÎÙ™"^?ùä“á‰'žO=õ”j˜,À¨ÃqÛoO(.GÀp~AÀ5Ò¿`ákŽ@½"PÕ䈡4´@×_}¸ï¾û±Ç«dÉ&ø~ÿûßk²}¶\h¡…4Þ‘mûÒpp´¿Ž€#0M}OBfæ“zÄ£g·—m"<.%iL¾º>ÿüóêÌñÛo¿ [mµUØrË-%«K½!‰H*ðsID£ž%é[Iÿ‘DDêŸeéR‡È»1Üv¼]YD¶iW–4.·]A#=sæÌ@àYN2eJ5j”‹•üüï¾û.«:^³gÏ ,°€w¼ÿiI2mà.rÍÏ$Ñ’fKú^màÿdéR$U=•Ÿ{$€#ÉÅpJ `iì—Xb‰pÆglMø¡‘Î'h¤]G ú¨êaµRáÇ×Qn/±Ô2<¿#àÔ6Ë.»¬NÖxÿý÷ÃÔ©S¾Òp4ëâ8õƒ@]‘# -Ÿy時O—©¹'Ÿ|rÁ<,b´A¶ZRPó¿üòË-yI¿–#P— 2dH¸è¢‹|¨¾.ß¿ézG ®ÈQ¥öÏ?ÿ¬¡JþûßÿVªH/ÇpRŒÀ?üâÚyÕG ÒÔ49ú׿þ¶Ûn»°òÊ+«#l Lþö·¿…Í6Û,¬°Â ¡ÿþá믿¶CÙ%FÞûï¿¿æÙ|óÍóÏ>«Ç˜Í‚l²É&á»ï¾ Iù4SžŸ>úHí¤0ú48p`¸çž{tóôÓO×P'k¯½vøë_ÿjY²Ë^x!üùÏÎnc”¾çž{f·¯ºê*ui°Új«…#Fd÷ûŠ#à”ŽÃkhm›’b4»¸af­‹#च&G mÛ¶ 7ß|³:|ñÅõi@„zöìúõë§~~ýë_‡½÷޻ѓ:è ƒô¼‡z(°Îl¸/¿ü2\rÉ%š÷†nЙ)Iù˜Ù±üòË´N<ð€îùæ›oÂ7Þºví^zé¥@Ð˱cǪ‡ï+®¸"¼òÊ+ ŠbFÌo¼‘ÝÇö[o½¥Û÷Þ{o:th¸øâ‹õü‘#G†[n¹%›×WG õ cÃÒÅpÒ@U“£O>ùDÉ„âóÏ™Áý‹2cJÈÅú믯dáW¿š39o̘1êQûè£;vTí̤I“Td%|õÕWáöÛo×óþøÇ?†ÝvÛ- É!ߊ+®¨ÙðÊö')Ÿ••o‰ægüøñzˆ2»uë–Yf™@L¸[o½U5?K.¹d€HÑ +—_~y8ôÐCCïÞ½CçÎÃG¸_GÀ( &¨v÷OúSV£kg£™Í§™¦ì·iÓ¦…Ã?\‹Ë—Ï®ãKGÀh=ªšá{ĦòC(âòôÓOëpYxá…³Þ³ßyçðꫯfã#1ü„!FLÞ{ï=ݱ"† cîO?ýÔ²è²Ø| N’ È¢Ÿ~ú)Œ7.ì¾ûîš)Ä€[zé¥U“lJ(Ä{óØO††/Ò@;Ìÿ‘ѰaÃb“b4³Iši:Sƒ ÒN“>’òÙµ|é8­‡@Uû9Â6‡”Ohˆ°9ÂΈXI, 1‚ ÁY$¤Ä„¼Lá5!š&ÎYpÁu7Ú$‹ÝVj>ËoË?ü᪵ÂÁÜäÉ“UÃÅ1lŒ¸ÎÿýßÿÂ`3•O s&|ðAÖEõf–Í{졇Ѡaåâ8Å!À÷‘£Ž:*ëç(®™å˜ifíÿƾ¸fšmþÓË-·œÎn¥ÓÓ®];mk®¹æš¬;žFksØïâ8-@UkŽ ÁÅÐW§NÂm·Ý¦ÙèñaÛƒ`\ýè£j|5¶Ñ:¼6ãa”]z.CZ¦‘úðÃÃꫯþþ÷¿k¾yçWI×HʧøA{tüñÇ«†h±ÅpØÔvˆ(#ˆqár}3H#ÑþóŸªÝbXϤGjk)"í»ï¾ª‰²ã¾tÂÐ6àöÃdà 7´ÕPŒf¶Í4›/{q_qC fÉžzê©:ÛŒÙj)ÓDhìóG{þ®½öZ=f?h›®»î:=F¶<]ºt #lz°A‚€$å³²’–#Œ­mH|‡v˜Öãì½öÚK Å&‹ÏlÃN „-„ -™ 6 BÚ Q¬Ÿp vØ—Ž€#ÐüŸè ™ Å5A3{Ùe—BŒ>þøã0qâD;¬KÓL[–hw™—bóÅÏñuGÀh!ÐJ¤%É-[mI$aõ¼¦¤Í$ážVªYº©ˆDÛÉ0T£“ň;§Š‘ÌktÌvù‰„ÀDäÍþÊîÊ—Oì…¢|‰:‹D3”Í"vNÙõø e‹ÿ•ø.]ç^¥A×Ôè`îàýÔOR7I«IZ^R{Im%Í+·š÷ÜëÒ²ÏBž?±ÕÚHâ}ø$ÞTC¼/þ´¿ùÍo"ÑìDâè5’NŠn“Q&qD2Ô}ÿý÷šÄ.)û!-CHU$“C¢éÓ§G¢ùÍþ‡e¶h$1Í#Cn‘¸ÑõBù4ƒÿ8% À»,igI›JâÛÉ7”o)ßÔù¤(oKÀ ªmŽä7)ôÎHù„¸I¤B‚Ñškòž5–öíikçH¾|W^y¥n°Dã³ë®»6ØßÀ(Ûfıcð|‚ýB>)û)_~ßç8¿ €¦™avþ§Ø m³Í6Ùƒhf±D3‹ÝßZk­¥Ai³d%®™Æ÷6D¸AÐ^¿ùæ›a¿ýöS³i°sóifÿqVC`ØdZD>êhŽâѳaãÀƤ©®R—!±Û^ª„O‡™’fIúVÒ$¡^ûÅ‚]v¸Ôòn 9Š·+‹È6í =£qIí 6}tTr;!ä÷ÝwåÔ€–c_|ñEÀá+$ _j&„Âã¶]'å³ü¾tŠA Ó2Êò™$Ú@ÒlIßK¢ üÅ ²ìp)Œ@ÍkŽ ß¾uG ?Øöå>B¿ÿýïój°/I3=ÿüó’IR>;îKGÀhyjÚ »åáô+:Ž€#à8Ž@µ#PÓäHŒ*ÕÙc¡‡„“ÁƒkXµKh›ˆG‰@¬ µæÝÜíHÿ)ª&G%ÜgQYÑ,¹F©(¨<“#PœqÆáñÇoàáº97‰t@ŒHd‘Xnêz$.ˆ!>´Kqaæ£8 Ìš5KɆ•ÏÇz|È.÷X9ÛLfA Œ«®ºª#+²E]î2gRfû Û¢Õ”18äÏâ&hÐLâöOh¶ ?Úo#P>ú¨j¹ìBºäâhÇ|éäCÀÕ)1T¶Øb õyÄì5GÀ¨o0XîÝ»wöã[ 4 Øïia Ñ@°ÝÙrË-•`@2H ‹A¦â¶EhiB;âˆ#² 4=qÃqί¤˜¦ ²Ãµã×ÂŽ³k×®z93a(÷Ú¯øðàÃ?œµ2ÒU¨lpfÎM•‹#P i#GèÍ–ÍŠ‡5cÆ ç‹¡ dísñŇ!C†è¡`ù[KúRÒW’¬Qpr$`Ô³ÈGc®#¦Õç46T–{œY]qãäÜã-½Í”û¤x’ÅÎ.·ÎIøaˆ½ë®»–[lÝžç䨲>­äíÑ‚’èåÑ…@{d ÂÄ~Èù˜q7Ÿ$'NB Úq =h† AhŠ Dhè1¡5úFyè£9JŠT*äÒrÌ-rÔrwàWršFÀÉQÓ•’#SùùˆñDH¹8ûšȶ’ŒÉjÝH'¹S¬ÆGHÊU•BI+é}IÅJGɸƒ¤›$}!)»ñS&ûà;I$–¼¼+$òð9!\GÀpJC m䈙%Óð±³!~öóñƒñAd©ž¤{æf xôRÎCœ %åÓ ? Ë~°fÌÒ…æ%m»äùSWH3ÄÈ†ÐØ†4“ÈçäH@p)fKá§§ÞÄ\ ”cë37±ÂQ!;¬¹qm|2aוÑÌÌKx™)D mäˆø 1¢~FŒlØÌ>ŒØ&‘ F¦M’Õšîj`´†¤%ÅRô‘$lµ0:ý»¤bd ɶ| ö–4$³-‹Tˆ–ÔÄ»A&A G¦9‚‘ÀÉÅ( âƒasÃŒ),2k«­¶*©ŒJe&¼¡:ZÒW¡I[l±l8ŽJÝKsÊÁË6!BÌ KsÊ*åÜ6Ú(Œ1BgÍ•ržç­nREް vÎÇ/®€øl?@†RŒÕ9ÚD¤e$!ÌN0ù¬¼#ésI(?!©é(™À€ôøùÈ™vB„¡­ß2+ \ìcê)AŽî•ÄÇÿAIJ²ûg(Œaµ·$¡YÁ¨½£$;^h É0A{´¶$ºÊ…ÎiÍc`ǯy?ÌîȆÕTk䯨‚ŒKÑ0žèñhkœ6âP±9NíâÄQ8p m¦Óuž©ýxÎ&š=ñ”Ïq!ž±¯¾úêìùçž{n°ØlL«Çµñëóì³Ïfó%­0|v 'h`YêAÀV“Ï?ÿ\}% ä/ù‹:däXR=Ѷៈ:2µ?L¯¼òЦ§œrŠúvW41æÃ©P½‰ÝFpq|AAT‹Ü àÇ©C‡ÏÍê€Ïªo¼1l°ÁŠùc=¦m GBÜ7{¾ÜN(ñC66ÄX̵=Oí VrdĈÉ´q‚ćÑD©Ú²•%=& Œn—Ô[’Ý;ÄèuIïIZD¶Cý%ÙñBKp¶¡KYUw ûÈò’ ×’Ç DFŠXB”I¼h Ò<î|Œh˪‹#Ph‡†®v&Lr$€)ÎKõã“ïj|˜! æ|‘ íÚµS¿>—]v™Æ$cXb‡vX#IxÏÆã³ Ûø4B:è öÐCé:À_~Éß3Y¬KV‚ÈžuÖYJˆñ†@ºÐžÝu×]ËiÿHR=¹'¼RC.(‚‰£H„2&Mšn¹å–°é¦›ªSGü#!Iõ¦îE‚Ì6,Kõ¤?/Â@Šð#5`ÀÍsÌ«®ºJŸ-Ž+{õê¥Á|!`„>ÁÓ8B~|-q/Äcãy¸Ô©VþØÐ64G,Ñ’øðQgH‰yüƒ.›5-{ËÝÝ' BÀÇÿIÄÕ$M—´–¤©’huv”Ä¿ý@I]$=&©P^.Yn+û®’´¾¤IJ‹ðNùá½`wÅÞÛÇ~G l Ä>›:•¿Uó…ö*«¬ˆF˜"´!dÛm·Õ° .¸ z¾&LÈìÙ˜Ò5-_}õU¸ýöÛ5xmÇŽÕ$DBÂ`’\wÝuáøãW‘¸òÊ+³Á[ñĽÇ{è©-ꃪ'Î/ºè"uڈݤ¹ÿþû•|¡"Ýw͘ôt Ô‚B^ÊAŽ:ê¨ÁdugÎCŸÓ§O<òˆCj!¹ÈàÁƒ5þÚ H*¾–8ަŽsqˆÉ³FSˆ­>—L3—s)߬qRGŽÀ[^äŸ33øXÛ‡Ž%Û|ㄈ}ñmÙ¬YÙEî ²ƒ&;Ú’[$A„&KÚPÒ™’fHZV•Á’è:M’TH擃`‰0<Å6Z¨s$½#)-Â}ç&HPï½÷žzÆfhÌMD¦@ &h¬Ln2°Ù°S¡zâUÛf¸A:¾ÿž¦$´Y Q™„Y`…êÍ1Œ¡M8§)á~:uê¤Äˆ¼xƆ™Ø=1ã8t¦ d6õÁ¾ŒaIˆ‚COB¹¸Ô©$G<’, IF~؆AŽLê…q¿‹JÚ_Ò4Im$í+ r4BÒÆ’Jš" ’fØcqüÞL’EAy_ŽþZÒK’ C£¿HJ³ðN˜4 LNŠ _–‹Óø±]©ôÇïÏ̺êÛ·¯j]|ñÅÕ®»b­A$˜²ž/äÆx \†Õ°ïAcÃ-DA+Ó”4Yña:4ZìCŒäèFæ2V¨žIëÔºZÙ\s‰%–(Xï‘#G6^Ìl=Êÿì³ÏOêáa¸Ì´gIõ³{K†¹OÎg ¦.õ‡€i R{ç$Iÿ“„—c ‚D²ízXbüüxæžÛËòºÌ:è!I,O’ô_It×~#i1IÅbƒjIëI:LR/I¤bÏo|öðnxOx_\[$Î¥yðíµ×^ ÈHóJœs6ÁPÑ`0#Î†Ô 2Ø·MžøM7Ý”÷Rh;Ðn0üƒ–dÚ4úJsŒÆ1ä&’=Âtw”`KSH0<æˆ „…€­íÛÓ¼ä—bë™{6 lx¸$ÉŒ«Ñò$Õ›º¡YÃ6 m•Ùå–ßÊÄÆ aH -]±bçßvÛmz Á|“BÁ[¦ç«NROŽraåÃ'°ža^ÚZÚb‹-ÚHï°Á>;fKQÏÆq%Û.rùM&ßìEYäi´v*ò¼lÝš™¿œ_Ês•ìs$÷=ñmG ¹–N6œÔÜòâç3´† ÃhZ”þýçD¸ÇXøå—_n`ocçî´ÓNªMÁ®h‡v=zôÐCh9°"z=š.Ò¡‡(«0» _NhL¶â|†’’¤Øzæžþùç´^Ci¿Â&›l¢C•…êÍl±îÝ»ë°çÙXnÙ¹ÛgŸ}¶ÎÚãœK/½Tï)7O¡mŒÈ™õÇõÑäa÷äR¤*¶ZýÁ_ôœ×»z3Î’ª'w[«÷¾¼æ­ˆ€|”ç‘Ë3d¼€¤…$-" ­ê’Æ ½–Eé’UžË%•Éð†ÆI‚ö†a1éœèSýv‹ ÃjÌP#Z}® ™A[Ä1ˆ ’TOêA}4MœY)FŠ©g¼Œ¹±ãá¾æŸ~¡Ö³gÏl€Ù|õ¶óÑ1Ĉ=B^pÉ'†/Ã’Û|å;/w®À #õ9¯OnŽômg깋Ôì3IDš-‰‘„äüŸ,]ŠDÀÉQ‘@µr6'G­üüòÕ‹€|4æ 9JŠ*æ%i(†©ñ wµ¤$ÕClìŸZBði„¡cŽ9F‰Ø…^¨C‚Ë,ÃH~iòÆos+{fkà›[‡ÖÚvrTYäUϹUš“£¹…¬—[óÌ-rTóÀUð1l;v¬ÎüƒakµÒJ+Uð ^”“£Ê¾©­VÙÛôÒGÀpZ †ëð›Drqªª3È®P½ŽŽ€#P`ßR’dÃSM÷δý$[¯jº¯k:pr”Žçàµp!@œ0fƒáŒ±[·n£µªGø ƒç†?ÙXýû÷ŸÅ·h™¸ x悸8ÍGÀÉQó1ôG ÆÀƽ8$qÎâÎkåv_xáu89nܸZ¹%¿G "89ªŒ^ˆ#àÔ  ‚¦â‹Á© ÓÉ+áï§„ÌBÅp!?˜:ží>›YVˆxõÕWgw{î¹ÙØ_…¢ÛgOˆ­àùùè£VÐ$Ø*Ó퉥†o ü !xëF{Æy|,™[ã\ ³Ý0¬ÆŽ‰·ÖZki Z- ÀÏG}¤È;tèvÜqG Kv|K1³ ½`óØci@\g’Ïžxá(D„&ÁèÛŨNŽ*…¤—“ˆÞ|]jAÕÇ×pذyä‘:ìT¬ÂB÷ÉŸ€§dâĉ¡]»vJ>’¢ÝÇËû4>†LØÆç’ÝÞòæ.!#F2ÝÒA@X¼WC6ð}A¼øâ‹ÕK5~Šp‰pì¯ý«’¦åŸx≠½xö&4Êi§–{¹FÛ”M¸W^yE‡ !hÈ«¯¾šË3À‰e¯^½ÔÏÑ„ Ô‡“yÊ&?¾šð¼wq†]J!à³Õ*…d+”³îºëÔâÅÈ;ï¼£ KS(‹)«”<4 Ï=÷\6\@)çz^G µàƒ†…Hí•ÈÚb˜á"b!D E»oêÚ…¢Û[\±Ü2pƸì²Ë*ñ€(“ŒŽ >„p<É·k×®êÑšs!PLÃÿú믵(4I»í¶›®ã ñàƒVM®¼ƒOŸ>=<òÈ#Jpð ù4ƒj‚ÅrmÊ‚LrmŽo¹å–êYœzòLÐèAÚðÙ„VÎŨ®9ª’­PÑ‹z˜•jà‹½&ùžxâ‰@ØÅ¨FøhãÀ(î•4&ÄûâOì0l›‹vgë#Ž8¢¨ËÙPÒ{Ážá@†¡–Zj)M'Û0XQ…I¦Ž;f=rC )Ï„÷nŒ¸• øX€^¦í㡺@j®4oáxÀ†e|õd˦\â™Ö޶ÌJ#¾Ü +¬ ÄˆëàxÓ®_èº~Ì('GÅ"•Â|6ÄY5ÄÚl³ÍÔÑUEÕMÏŽýŒí1"Dñ¥—^Ò%=0‚\¢ÖGò•1iÒ$Uiãæ•5 ÞYg¥ñœèíÚЀ ?Ø'iœ²éCÆP™c'áâTLãïÝ»wÅ?ºx¥æ÷àƒêL8´4¢Ýï½÷ÞJ>8–O qÃp:=6A  ÒŒ3T›“¯œböáÉ›2LhG GÄaCšŠpoçå[¢A£ŽFî < ™4U6uÀæÈÎgɽ»8•BÀÉQ¥l…r¬1`ØŠÙ5‚.ÒP0öä AƒTõ̲_¿~:†O¯Æ÷úë¯W£Jªž[û0¸¤ÁB­}ÿý÷ëtæ6mÚ„)S¦¨ÑeÜ0”ü "|smì6ÝtÓ°æšk†!C†pØÅ¨*âax(NF*qDEëÂÖ†ÔŠv­ Z'þÇÓ¦MÓ*Šn_né<ÑÉyÿý÷µì‘°ÿ1íN¹år÷Aïºë.-†!1´iÅŠO‡ÐñK ÙRl™žÏˆ#ð«ø†¯W'Ø bFh¸PkrÈ!:fÏXü9眣•vw6;UµÍüÈ-›ÔèìGN9åí…BÈС5Zo½õ¬Èl°KÊ$¡e¢Áwqª—2s ›B‘ê˹?†Ö GØ!ñh÷ü?ñ­„ÿ¡c=V ­íhƒ/¹äþúÿöÎÚ²¢:ÿG0QüÇ F¦~EEL7“ S36‚ “„AÂ(\4 ˆÈ 2H#‚È$ÐLÒÌ44( È!aI£Ì'Bý¿ß~w_Î=ïÎÃ{÷Ý·÷ZuÏ\§ÎwëÔùj×®½±š:uªâ?÷ÜsÍ5ÔLc$As»‰ƒ 1l•UV1ÃïnNù§#‡¶Œ¶T«6CGq„‘W´ÙhÎÀ+$è[­[Hö6¦qœS¼…dc0þ|;̾Ûn»Íz|4¤—_~yv×]wÙÚ"Æõ§L™bÃd ±aD‰mB1yóæÙ‡i´¨¯™!âQ¼iÐ|ðÁìµ×^³™)ܘž,=M ,éi3G96ÜpC»–Æ« yYç,ÚÄyqJ Ð4ê0¼M'c4´ˆÒß)MRZLi ¥Ùn¬õ–¤–=·«•'ïÛÙT»Y3Ñîy¿˜¡V-â|µèöµÊéïsµrä÷Q&\@’ aÍ e©åyÛq  ­ö,ÍÜ<N2‡ÿæf®ÌsJÏO|–ç”^*¥×´ü£Ò_T'Ã×€hVBsÔ,Rãè¼I“&e—^z© …1Åuß}÷µž&Ccø1av¤èü£>òÈ##žIfÏ`‹„–íÓdkî²Ë.6¼Æì7îáBCK/–ž0Ãz{챇Še 0œyæ™UŸ -S­!žFQãѹ@D°G* ö8µÈÃÞ eç¥V9Ñ*cÿÔH(S¾\Î÷ãO>ù¤Í|óíüÒqÀàºÖ³äϯµÉj²ãU+‹ØTE 4GUaé»U5GJ‰Á#*z 5zrLߥqå³ZÐøpÜÏ)æÉ°D)ß+ÃH“ƲZ/’~[ª+æ]e;4GU@‰]! ºÛÍQg¥Š«î"š£î⚣îâÙW¹A~òB¯ÒÅÕSñs.D§(ù)¼ÅcݶÍ(æÛ@ @¯ˆÙj½F8ò@ q…@£qõwEa@`¬@sJˆ‹z²ÿþûÛäÎÁÅÆÏþóz§Ç±@ è3bX­Ïþ(N ô7ørµJš÷ö|Î9çØLÎðà\ ­ØôAŽúï?‰@#€CU<Ð3‹ŸBx{>묳²Å[Ì| •ç©Øë1#—x˜gæŽXC@ ÿõa5fŽ”d!-#5¦Æ3ÛfBÈÔ «ŽÜxÈ® pß}÷™óTÈ$?`sæÌÉ–]vÙ Ç„ÎRñ,ë |ˆí¹çže‡])Dd=E çš#¾vN:é$s]7ëwÞ¹âT\iàý™Ù¡1“³šØúž’#}pœ A„p­š_æ9aêk°Æªprêx¹îMrYR+s”>î;ê,ù¿¥´osúé^Œ{)o–27R¤u–xŽ-/K„/HR ¨‰¶`ˆŒÄçË_þrM±ã>½›Ã0ZH ŒzFŽôqÍ$ˆÄ½^Q "$º$/´ÏçZ8w¢ 1"¤uòdnö©Â¡ELˆyFjFEo&8'úž‘#=ª“"4FÜço”Â#·@é/JDžá<Èѹ%…8…m#€f)4JmÃc‚¦ë¢ ùºæÈ‰Ñ[î™»~ÇȰ’಺ì–<þøãùö¡¼Se‚ÀS_½þöa1£Hã õÖ[Ï|:$Æ=!Gzô<1âcǧ׶$=AœHÔLÍkÙh£,¾C=$b¤8òâ‹/nX4fÐpÍ/ù˺çrüë_ÿzö‹_üÂÎch#RM'BL·}öÙ'[zé¥mÚóK,a/1|Eš-ç^uÕUÙqÇÇj·…úI=… ¡í49áF] Ê[ðãÿx¶ÆkT8ƒ$àëoû[;)þÇsŒ­„ùþç,(s9“X ¾FâÒ+ñ ´F#ƒtõêΜïQG•#íå—_ÎN?ýôl‡vÈV]uÕl¹å–ëø©}ôÑìð÷¼ðݲá†f‹.º¨Í´é$s>”•éÌŸýìg³o¼1;õÔS³¿þõ¯Ù•W^ÙRÖAÒ¡‡ÚÒuMœLýDEæ6G¾„ÅÐZÆ)µÀX;$ƽÒù° [RMÍÑW\ab4 ;î¸cöä“O‚ǼõÎ~ÿûßÛögœaÛ¿þõ¯³{ï½×Ö¿ûÝïf[n¹eöÁ~ÐŒ(åÈÎ¥§†Ã5<Ù®µÖZÙ­·Þjûzè!»ÿ$[l±…}¤9pÚi§YoÞÞçŸÞÎÏÿ0ÄD/ðŸÿùŸ³•VZ)#D¾N´-cêïú믟ñL.µžÏûµ;äçÅ_ô]#–L†À€ Xñ¼šàÚk¯Íþå_þ%û§ú§lë­·®ú df”òäÉÙŒ3ÌAZ#ïé~ík_Ën¹åËwöìÙÙ«¯¾jåxà KÍžËðöK>.[mµ•ÍêùÊW¾’¡mÛ~ûí³_ýêWvØC-Pö7ÞØçñ ô¾«I­ûÌš5+›;w®]ò™Ï|¦ÛÃs#H¼kŽ E¤@ ‰†.]º™„>0t•p2Y‰¯àZJ6=:ÿ³`Á‚ô·û·i“M6I^Iò’ôaµSD6è±'©ªm[¤Å¶¥áH7Ýt“­K‹’¦M›–äKĶ¥¡H" IÞ$µéóŸÿ|Zf™e’|% ¥;ï¼ÓÎã>oûÛçÿð‡?´}\óéOÚÖ¥‘±{Št%&[y°cÛm·]±²õ¯~õ«vLvI¤)iX*i.É¿Izå•WR½ç³ s?Ü“ç9Ìí^°cO<ñD’–$B”–_~yÃî™gžI" I3gÒ>ð$ÒdÏ+çsv±‹]Ë󓸇ÈQÒeÃècûX’ú$­ŽI"ŸÉñæ^"‹ö߀·Ué´s¥ù±{È^ÒP_ZrÉ%Óšk®iÇ4”fÇN<ñDÛ–¿˜ÄzÙe—%ÍáÓo¾|õîsõÕWÛ3SþýöÛ/Év©œG'+ä§´¶õt²õ–ú ©_Hywõ‰üFOý‡´IßE•–Rú¨ÒšJ›)uRuâÚ@ o .+m¥D[¶¢Ò2J‹+¡˜XXv¬ z¥9¢1"oÚ#ZÓj¥Üu×]¦yÉ6Ø` ÒˆÖ¡YÁéÚÍ7ßœ=øàƒvÉE]”ÝsÏ=f °ï¾ûfgžyfvöÙggúÓŸ2‘ r¶ C¡qÙk¯½L‚ Z \ý³o‘EF*ºD",LÀ¹çžkš2C;òÆodóæÍ˘֋ë‚ .Ⱦ÷½ï™ÝVža§óÏ?¿î0ùÿã?þ£in°B†¦ˆ2a8Í3â§E¤0›?~ÆPY^(+Ã\Øîðœ8§ãD ³]vÙÅNÝi§l¨.÷a(¿/Üãî»ï¶Ã —¹0tðÈ#x &à&‚Ÿî…3Kþ ´ZÜ<‹Rï>"ÐÙÊ+¯l—ð üg]ˆ‰¡fêl¯Þ e2ˆ0Ñ€PH Œzõ€!NŽ|xmxoî—!(lf¤MȦL™’I+SwX)w©­®¾úê¶„ì0,'-JưÂÐñ°Ažzê)[ò³é¦›‘aWÿ ‡ áƒÏG¼(ä1`X‰!4„ÆP(bã¾ /}ò“Ÿ4†÷ÜVžO*«gŸ€Á4Ä€ûCÆ|(Œg@V[m5{f/þ™9ÎPD#s†Æ>l¦A÷!²O}êSd“ Ù¥ïgÃØ(!`éÓ—ÁL¹Û%È*Ck> jéÇó«w?·ËKN£®R½û²Ë·‹ì ê¿OfèôÙöÞ{ïò>/:DÌÀ^¯aø\X»”!ðuÖYgD6tN±e &2½"G`ÊG…üó‰ýBo ›l\4Tb6<|øÑR MBø"Ï=÷œ-ó?tDÐJh¸)c&.ûl\4 UN'œp‚íç'ïÊŸY_/½ôRùD­šVã _ø‚ 4LØ.¹0 ­eaÌŒ 6DõžÏ¯oeI£ Nh‡°ž‡²ÔA‹–æû·«Èž€™ßÿþ÷˜`Û„U3â!œ¼hÈ0ÃÌ÷“‡{ .æ÷Å/~Ñl•ÐlQž“O>Ù콘ÙSÔly~õîSÌ¿KÛ®1¢ÞæS—²l±GàþûïϰÝD°7DãoWi¯®»îº²f|ìK%Æ^“#žª.9ºþúëmˆå§?ý©M÷–Í —ø`¤{ âÂÑÔ©S³UVYÅÈ3¡Ð$a8ŒvІ š0M¢à÷ çÉ( †É a ™öÆ£!`AÆ0†À Ì*«÷|~½/<òH+'enFšB Gà†œwÞy‚ɾ¢f£r-ÕO~òs͘#D¿ð ¶î?hÚ豂d2HÞÍD‡°2¼Î ¢Eºä’KŒÐ1L˜—F÷ñ22üéåÎ_ßÁº¥ ‚Œ@€÷:aÓ§O¯è(ÉÒ4Çhh„ÁÃ?lZ´Ï˜й@x‡9äÞ&@-ïoQ 4¼Ë®÷ãäI»G‡ ­“1¼Óö‡?ü!£SÂ<û15@«…¶ˆ!qL0@{̤ :© W㛉€º(ÙšœcùP— å >0ÝL‰Š?.©uŒ1ÆÞ\I·ªi]̸٠ª1è=å”Sì$i‚’´0 â'Å0JúÛzÞ Ceil?†Àô"êý$ù!±ý\/;š$UtÙ [¶3v?ê9%‘§ò¹(«!²ãyƒl5fè,’¶ÝvÛ¤!¤¤†ÇÎ#?5|–†à›o¾yYKõžÏ.Ìý4c‘·‹?»ŸE‹D& Ú> Í¥ /¼ÐNÍ"ÃåòK[h×wúSzž-´¤ž® ´”c¬Ôã0dìr¡ÿ««mN£üôæÛ$þÛŽ ²ig¾ô¥/%͈M|p¹  íÒ0RG sÎ9IšÔD•<©ƒ¤µMþN»ï¾»µeÔ[ê:>4ܕԑ²wƒI²Ñ³c=ö˜ÕwÙïqz1±wHž$’c“J( -˜$¤ „É#LZ mûö·¿m÷ -Ò|Z{íµí]ç<ÍLMúgÕd·ÝvK´+Èa‡–4#7ÉY’¥µÅÒþ&‘µDû"-pòrÙñ3¦¨^S7 »KíKשBCÔymâÅÕPŠoV,y9i òâ³Õ4½Ûˆ³å—×™…%-Hy»Ö /=„¨ÚL±ü54tNÊïªXç E©÷|Ås;ÙVOÔJîWK8ÇÉ_µs¤!3L«“–ÌfÆ©gZípÃ}|œPÕ;¹Ñ}šùOëåŸ?VjT¦k 9âÃä¨K ŒpîzÓjžú?["GtÊ4Dn r‘Hˆ´—‰ú‰@~¤6Ñ~@Ž 8.Ìè”}_’溢ã@ûÁÌNfl2³UCø~‰uJèüAŽèPÐá’6©|¼HŽ4œn0N€”Ñ™C¤Ùµš—Ÿ<ÙL&Ù`Ú:?²I´²ð>Ñ™cFéí·ßnÇ¥iO´<›´ÉFÊèœAŽ˜éÛnP¾y¬tÕó G]lo>è•ðGÑ(!ùõá=…_i$2MC/ìÞ$òu½™I\‹ê·š0ôä†ÖÕŽû>†Œ˜a…zºž`ŒÍ0Q-áN+J½ç+žÛÉ6ÃNøâ~µ„sxÖZ‚Ax­ë±Cï6Nµò¨µŸáŒäI£û4óŸ6ºGá8uÔ…õü¶ïå@{>†áILÀÈ‹:6¬îöÌje8œöaØÌEÚp›”€ý¶Ì %1”%ÂaÈ!yf™raâv|a×ÚF·õã½eˆÁë<>ØVcèK’ªY Ù°Û5×\cf´]Ò,Ù¹˜)Hní“(òÂ{ÜnÏ'Ö~E ×äˆçv‚Äz¢éFbLÙu×]»’_7Êytç¿m©—J^OóïD$^² &„ÍÁˆT$L¶6ÆŽ ³Æò“7Ü&.™ÏZå:O8©…H‘Ÿ4UåÓ±Yò$p€Í,ÓÐ[ÍÐ?ÕîGf¦9sæ˜ÝNr!]noX¾Yi»"&`£)skOyvl/¹Ž‰7ÜpCñ²Øü‡ ›ÊGÅóö-¥Gs5{”!þƒ|P]…åë)ÆÙˆïÞŠß šè¡¡¡ìG?ú‘aÁÌO´.õ„šL €!h£ÐæÐ ÀwÛ7’$ÛȲ; È^äÙ'»¢z·qŒkð3†¦ £mÜi0ë—¥k—ü¢m¶ÙÆf 3K¢„0Aׄû€á»-$˜H0}¹WÂÇñùðVüý‹À›¥¢Qw½þöoi£dc‚36Ѫ0ó‹¡3R=Á³Ã˜™É°;Ã]®m‚¸0Õá|ˆ‹Œ +ÜŒ¯lšìZf‰®°ó d g«ø3c–-³W1À›Œ¼Íá+ŽcB,QF4VÌlC8—Yu8]eV~Ǹ–¹Ì‚ ·Ñƒî¶¨Gô剓¢I¥„ã!Òì^ÜOù†m#@^²¥Aül÷²>^Wú«ê¬“&m†Œ7ôÿòc„‡Û{ÂÂÐ.1ãÁ¶Ú$ü1]×Í ç㣢MÄ„¼Ü~¨Ù<ë‡ö›&l˜òv R~ˆ]#aÈó°µd¨-ïS­Ñµq|t(µc[ë®Ï)á¸D;†!3š†j#¤1½Òñ1É«Q’Ð 5þ?⌱GÀë©×_ßû’E úÈM+Ĉ‚£©5™#oÈÝ­‡dÒBµ‰ Ln©7Á% º] na„íhÄrÐð@·Ÿ“|½·í—º]êÈo"#P¬§^£ÞNäZÏ®“£’ Û?(¾ôÍ„9z\ @%EW2Þ!Ður”+j¾'κoçN‰Õ@ ¯ð÷!êk_ý-Q˜ñŠöMòÊ_»r¼>K”{b!àƒn?5×±No<ÈQ·QŽüº€×S7\ôínß'ò›`(<ˆù,òÇÆ@g¨8nÄ¡$3ÁˆÙOÂäâ§ApZ+’ÙXá;iùå—7ŸNÄuk$Åû*$’9Ñlt]º‰@¯ÈQ¾Œ>LáËü±Xú 'õ”+}¿ý;ã´<Í¢å1 8fÄÿÐ 'œ)dHÙR?<&^¼q €¦V…Ò×e¶ÞÁ,XtÐA|—ízR¼/8A®BÑD —äÈ5FNŠ|9šÏ÷ šEÀë§/ƒ5‹Ü;hõ|ü™’ôÑGgûì³OFˆäá‡6Àì3üAˆbO$>òø;ª&8Š\n¹å²;ï¼Ó+F›9ŠÄé$Ü "késŸû\†¿%ÞxÚV°çLdm?øO:ÿüó³ÕV[-SPèìöÛo7†&fúôéÚ¿K 8k»)Çüùóm¿ÙÚrÊ”)vn­óx>ʃ– ç–÷Üs=«‚ïVÌ–#šÁäÉ“3<~#œ‹¿'ÊMÈ#¼t#ÅûBÐÜkø7Þhá‹ð:ŽçoH¢ÀßÙw¾óóíD()´UND® ÅËpx‰;‚@ !¨0»™tC>*ø9—Ï>¬´ºÒ&JºUH Ð_P/Kõs -©¯8¯YT‰z¼°JÛÕw$ò]<õz›ÄJPa‚—­©´™RË•q«­¶J"D˜vÇw$$’­”ZSõÓ7¿ùͤXfi÷ÝwO›m¶™qVœ´¤˜eIÄÊî'WÌ• ‚Ø^zé¥ISå“>öèUΓ¤ôI¥Ï(1da?–CÕêåFJÿª„þžzû÷J«J‹Š¾4]oW:ÉSÿ¡·IM‘#9>4Òñ³Ÿý,yT{ÿzÊþÆHK„hö ¯açËf(ICã§Ú\þ„’†Î’B„¤ 7ܰ| b r•“ìÒ1ÇcÇ¥¡IÒvغ†´’4Ciƒ 6°mÈÑúë¯_ÎçÉ'Ÿ4rÁ ÷Ÿ5k–‡T(¾š­K#•¸§¼eÛ6d‚¼(?¤ 2çÉQ¸¤À³V>®©wäHÞ½“´[–…´iI±/=»Š%ÄI^»må™7o^ù¸B•$Å–«¸/{ì±e2ÄþgŸ}ÖÊ)åy £.l~øáIÎ.“<€§ÓO?ÝÖ9WZ(?m –¥úä¨KíO¯œ@Ri>:·’þ¢4C Ï´|x OïR¢wn!-iÄBz‹=ŠG”øoz)ôÎñF<¿—7i2o¯^ÿ¬ëþW ï±/+½ Dýôºj®¶¿vx+~³Î:ËBvð  ×äƒÏ2ä„cEB} ¬»= CkØÓÏÌûb”Uâ­; ÏÆÒˆX 3Î[d‘E²ûî»/ÓÇ>ÓÇÜœM2$äâ!>ü\‹Z„<|(ÉÏe ɢáüZKK“QF†Ý\>Ûxã}Ó–õÎÃ7Ãeî ’!Ep¹*ãä™1l¸ì²Ëú¦…Eñ ò¸ë®»2†öª‰\E9ß÷¾÷™÷nã"y§œxõfØg—gŸ}vFßC9$“Ö/;ꨣªeû zIŽøø:9zCë||pcNXû}{a­9=–e”ÿÏ•“ÑKyZ™£1„|ôƒx}¤ÎaŒ@‚½^Z§~’8Iâü ˆ€45fÇSíÑñMYišl‰Œ´:ÙìÙ³m›ì“ò^¦Ë´™qB‘ß8þøã(‹ »! wåO)¯ccD˜ MYˆÙæÅ˘ߗ_§Ì%ˆ‡‡!¿|¸ίw6Gyaæ¤Û# ëåeò²øq¾›,'“K|ëÇóKâÊ=òýºaá ÌÄ¥Cª=+aRˆ >؃ö_û ê!0üv×;£Åcè)K—ä‰=u>F|ˆø ½¢ÄGó%%zí|¨YF uϱ~H÷øÁ(ܧÙ:C]£ž‘¼î½ªu4HÔKê(s–«i´;$±’´$4qhj‚κ/´A²±c|€1:F3‘m‘íoôÙ `,ÄíË%—\b÷¬vçjˈÍË[Mpµ+*÷ e¶*“ Ÿ!!?þ¸•¦Þy•9f¦ Ó™©uâˆ–Š µà€a¶‹l­l•ûÝ{ï½™ì‘*îëç±$O »e¿ùÍol÷ÕW_­»îºv~þ¼ü:ä‹à¹5Ù™¡¶†ó§Äz P^iŽ F$Æø­Ó2ä5FôÒRãûÑ…æH ôXžSþà<¬‹îñÍú,{'ì!êŸöj‰úëD¿Ï#Š3V¼ûÝï6²òÕ¯~5Ûÿý³UVYňÃV|„™öÁ~Ј ÃSÌÆBЦ<öØcÙ.»ì’ɺnñ·ÞzëLFÝÃgäÁ¬7tgW]uÕˆë˜)·çž{fÌ C«õùÏ>“mŽÍäqr•\Cy¸'÷€ÉÉž…Óeëd³ÉмÔ:O¶Y#r†Ê`ÚfÀ1´'Û-Ëç§?ýiYCÅEhÉZôûB`ü}m‡~˜Íbf¸Ëæ¨BKççå—IJcˆ!7fí1ëïä“OΟë@UÞÖJ/£jUvª§Ä ["ÈÖò ŸšuHÛNŒÐ`‘‚ „Ë•?8ÿ²Ç÷é·ì!:#Cgh‰ IvHËüÇ9ïM½#A’ÄxµGÔwÚ"ÚŒn&)-¦´„ÒìVþ^"Ó£áàCë65 ›¡-‚ !|€òaª¿ïc?Ú†y|øŠ}õDÆÖfŸƒ¶ yá0²(h¤°»Á¦ˆøPUñÜZÛø2B{‰(ÇE3Æ Rï¼jyãlj|ú*ÁeèœÀ„a»".ùûæóFÅ1Hä®Áß8ñŸ ª Wólk=`4å$FjhÛþ¢z޲"¤IzEŽœì ‚‘œ¹¶ˆ–„ãy­Q#Ò\ÄÒÉK4H®E‚$9)b 1‚L…ŒcºIŽ€­ÆÇ>ö1κì²ËÌ‘#C_!­!àä¨HÆZË%Îv‚9ÝYBLº.|PJ ’÷Òý¾ ÝçÞ,!RÍÑ2®ØK¥E;3«JíÚ–nWÄÒ‰ɉõÔÏ×jH ðØÉŸŽ 鬺êª64okï& ¡Òè¼8Œ=Ññ %rääädÈ5E,]Ãd—ð2Pl¯§ù„Ò!}ôTN|| 9r’Ä:ûÙ†‰çÇšp×ÒmÍѸ# ?°„樻-¥'ÂGE–€|é"îĨ'È7Ì”!Î#•Žjxfç'üJYl Ä¸w¿ˆ‘†¥×K–¤2)â8¢eH Ô@{¡¼]UÓbw 0ôLômñKˆ‰aöž0ëEòüýž¾t;“‰¸dfÖç•0Níõó?¥{,5 ÷iå9:óá3ÈÉë©VM°}Cá°?Hi,ŒÙWõ‚§2óŠ*Cl³¢Œc8U¬'‡vX&ÏvŠBrd íaëÌ;òHú6­ ³Ê˜I×-aJ=îBAD gš#«Ôû¶xá#Ó«Ž}Øtÿü’â°¶M ‘eO+AZð÷ÓKù¥2_¹—7h3o¯{,];äuõGdÍ1Kª»¶ m°Ll¹ûî»3…¤¨ ž™!#ùWø ÂQÑÁbÝŒtiúøøÁÇÄr½E §š£bÑù°äÄfi»+KÝË?rÜ–ÖƒgƒüågÊ1CŽä3ç˜;Ó³zîÉôÙ‹ÿ74öQ'H¾M=¡î¸Ýu ~_&1‘%%ß@Ì\ÊN<ñÄ2,j×Ì[6Ž ]®¿þzs éÓϓ̜úqB†ì°Ã¾iK"ÝãX’!ø9ÂoÑë1 Ç¿‘ $Œ 8;Ä+7ž¦qîˆÇî[n¹ÅO+/ñÀ­XpåíãŽ;Îòd¾•vÛm7óQ„cKB¸\{íµFèømºòÊ+}w,C çš£QDÌ 3OhFâcVýO˜Z}wìÍ!áþ{%´Iž‚Ëqm†L$ 1hðNcÄn¸Á†É¶ß~ûlÓM7µØhøCš>}zvÞyç™cFðÔl±ÅeÇ…øòá2޳] Åñ¹Ï}.ƒ@R¦NjqÁˆÆ~œ,â,ÿ@×]wÍüÂ_ÄßKh®ð(£Èü}¸~™ðYä¶ó~ò… ÝxãÙC=”)`nFÜ8ÒExÈ‘‚»úå±  G¥Þ<$ÈI‘kŒÚžGì$@Œ ë6{Þx¨9W’!¼jœ¢vžpôªëaØN¾£qM©^¡AÂFÉ…‰Ô« HŽÈ€-ñ¼ü»ßýΞ û¢|Ð×ü£¢¡Áá"§L™’ýä'?±0ho¨øCB£¨öVÿo¾ù挠¶­Èâ‹/nä§Žxçvùð‡?lo!?›o¾¹iq<.¡ ?iB«á‚x5#ÄU»øâ‹ Mž<Ùœ&¸»'PBŠ‚`ÿDœ²@`ÕaµèZ#Jóa³¶oùàƒZc„º¹ž4{^½<ªÛh£¬'§ï°-±aÀ»+ W#Á×ýò—˜üÔŽýë_/÷VéÒCh:z¤ôV‰ÎM#Ž“·m¶ÙÆÜý“o³åã\†ýœ×Cñ!77õ‹ÚÈ>ÖYC`:"Õ PŠ&‡pk¯½¶&£Eò¨÷< b«¡ÕA,ò§øœxÜnE¶ÝvÛìšk®Éþô§?ÙÐçò0ñÞ}ùË_n*K4]†âtø¢ƒ‘ »`ÁÞË’]}õÕ›Ê;N Æ#¡9ðNŽÐñLôöߥԶ0^ÏGy­µÖª›G³çÕͤÎÁ£Ž:Ê 8éž~úéf“@£Mœ¡NåÑGµ1ä‰A}N˜€VCËeÅ&CRÔó§žzª…OhÕN2H"ÞÓ‰×#¾>«eÈ€"@'ÔHx'‰±6sæÌìÖ[oͰE"Nï§ Ck §AŠX/ DÄ…¡­V’t8hŸÊ#òü{Þóž ’ÃðÞ;4Á‹,‚]¥Ðy*Þ› Ë®„ ¶ÎmFägœqFÅ,=â­…ƒŠ½àAÄ(oX;âÙ^Ïñ¼ÐhB~h|j *l¦ôbG°ãŽ;ZãçöØ"œ’xHŽ|þùç«fÃ3­´ÒJÙäÉ“Íf[´FÇsŒÿµ¯}Ízžä˳0 €<ðÀöœ“&M²™2Ž Çê=§Os¦ì¨ø!K<ŽjRë>³fÍÊæÎk—0-š†~ „/‹×)êõ,$Ȳ÷Þ{ghu©Ÿ¢bÄwÚ´F¼«˜¼,»ì²až¸_•ZZa ¸‰ V:5 ûAä|H¶„v€û.´ÐBÙ\P¼Ì¶‰ÃFX´NÜŸxCàtø\c†[‚VXÁ´É›l²‰µÄ|C;!S ²øPÆ=9RˆÏ‘·7b(Ä{ü3/Ðb0„†„0Ñ´éq=ýôÓÙ^{íeÃ?$Òx‘FˆFƒ†æÒK/Í>ø`;Ÿ%ùó¸Æšô°˜M‚梋.ÊN9唊2øD‚R¡ÎÆ‚ˆÞ”ÛáGy$Ûl³Ílv ‘ªi|Š+ $h=ö°a6TíÌžÙi§Ì~€FaI‘ž+ÏLCL#(ÏCCKɇ2#õž“ˆÙÈk¬aF£W\q…‘#fÜ¥Þ}Àb† Ùþ«‹9ô|›zD}‚y3ÿG=¿sÜ ¯ CrÇwXPUê:6HÛm·]E™y¯RËxÑv‰Î Csƒ€ ¹©&ë­·žMá§3U†ÖxÇy·†ÒvÞygkÓh×öÏÛ ùõ´]tb(F┈1<ˆŸ%\ €äEgaBÊK›˜Bô|c ¨qÇsÒ9bÊ5ѶÿAiHiE¥u”ôh•¢*Éší”*£Ú´ë®»Ú¶Û–/’¤i¶¶.µ¹IIŠ*DNl¿Èˆí/ž§Þ '2aÇÙV£gëÅŸyóæ%iŸ’45ÅCI=Q»åË'‘3;Wƶ_öI†ãIÄŶEš’´V¶.Û¡¤¡³$ÍM’öĶÃ$ÃJËC¤ÅÎó<5teÛÒ %:[×ð˜+[Û³ízÏ©èãI$3I-o×P~–&‘K»6_¾F÷ÑÇÆò° Gù§„û:ZRŸ¨WÔ/êõÍŒýU¤qýþL„òó_)¡ý[Ti)¥*­©´™RGµJö=+¬4.!IDATuI$#©Óv>\+­hÝëi#¤­©{Nþ ˆZÒ°Ÿíâº?üáùö®aRçoÄ~vH£”4S-ÉØ|Äq ¹uô¼#2Œ]A€º¬„áÙÚJ´YË(-®„ö{aÝ$Úª0XH  ‚Ðøñ,,éáóñ¢1¬*š™ †ÃˆK¢2. Z †ËÚ¡'ÈpQ-¡ˆí"BUU%Î1Œ*«×Ãv€a)´6¨É}( ›„²`øÉŒ¤èy—ž!½Gf¯Ð DåÎKÒH|([dHš3ßù~öÕzNl°9â^Øb€ê{†ÖÜð“ëϯÞ}†Ï³_êõɇԼžYâÆýƒš\f„½ï}ïk»P\ËÐ\=¡pÿHõÎócLåw­+×aTÚ†ä« 6F+®¸¢i¢ŠÇiS:yÞb~±ô#ƒDŽ F {ðL|ÈH]¦õ3T†Ü{ï½6üT+óV±Zyø~TÚøa vC¨Â±‡òÆ{l¦<1–ü Pnˆ Cc4x͈8yyå•Wlʲï'ZωŸ E±£ <'Ÿ|²©î±_ÀƨßÌBƒH0Ôu 'T .Ùì ™%B¼§Úz׺*›a2  -Âx’¨ÝÑ.ašŒ399 rêÔ©6ã„á7ž‡á6\`ðœö£ñ9è ƒ2Œ7 PqüÖH˜Ç3áƒão¦3›‡¼ŠÓŠÝòtûí·[¹ÑÞ¯oT–.wÂ=Ì$;¨W](Kd1ÍžuÀŠíÐx{ôAyŽñ†{”·;´íAº;·ï<5 |¼ðk„ÑƲ°Œ‰Þ«tùitëц®’ –jÎaHKFÔU …v‰k«]­ùcÃàCyU3©±ˆ½vQõ¤Ñ}Ä~k4¥ôq‚þ^é%ÂѼ¦Ä¼j³ Õ2¤Ï¨ÒFLR‘©LK(Íî·6‚N 3jë9YÄ&¨èÚ‚úÊ»Þ/ÒÌsôKY¡¥öjk=ËsJ/•R¾½öô9; Ï0Hš£<\mkò™Œ‡u†ÐÕΩEŒ¸®žA8!08mW mÍH£ûŒ61*”yÂÔ§ÂsÇf#€¿2¦Ø‡@÷ð¡‚îç<692 ‹D÷©ºPcƒEܵ;x]òºÕ\#—q‹CÄøûÂ^pH³8¿ýío—Ÿåá‡ÎÖYg›Ë6\˜Q꾃Р¹à ‹tr9ä »A†ïéœpŸO<Ñ/±eISP±ßýîw曌!v&q0,c€µ§vZöéOÚ†¼ š‹`Wè“(Øf†)“=œL2ìN^8ŒuÁþ÷øQb¢H½ç¨… Ø‘(ö!@ß €Jy<'I¯ža5†Óða‚šcŠÒt%>lþQ‹e`Ñj þP¨OÔ+êõŒú~ŽÆIÛÁ¥„K†®ø9’ÓÔ¤!£¤~Ò‡=I3›ä Öü i2BÒ Ñ„/ MÚH"j^S™2csæÌ±ëD6’lí˜Âö$yœOø,ÛU>×TΤÉIŽ_“ˆQ’sÚ$g“IÞ°ÍWi<ÉÍG’“Ùr’ ¢å'oþ Ÿe²Lòâm>ÆD˜Ìw›´ÄI3H“É&E Hp€]CY5ÑÄ|6±p\#íTù1i"W‰g—Í£]ƒÿ69„L"[I$0Õzü-ÕÂ…râ?N¡W’œYZ¾ñÓÔ¥­”ÖV ?G¶Oãš© ©XÃÇŒ">ZË(99ÚBëíÕ²¸*®\T0ê‘“#êõŒúä¨ÃƈG#ñ_)5MŽœ@ ž{î¹ïAqËÊûå+ȃÂëiò8P”ÿ¢$›¾$/×éßø†Jò¢Ÿ4Ù É}’=^ù> H äè–[nI²×KšMj×Én°ìØr$·Iž¹ËI´`@ÖÜ$*0n:çœsŒ‘·;„¼þúëíZ΄I{“Î<óÌ„X¶… INºä·ÌžOQìäˆgFê=G=\ G믿¾å?! :䨋mÊ ØÑø‘èé#¬‡ÝBÀëõËëZ·òŽ|ú f}z¨›“N:Ɇ˜ŠEÌ;OÄf;L\`x‰ .Hã •cøGc†+ÂL.†ª׃Z÷÷…#Z†Þf–ÏQäÆŽ3óóˆ#ްcü࿌¡®¼l–|Ý$ÇpÖ*’g§aè+È—0D„(ÒgÙ|‘^ˆá:B!Ì †ÝplËĉ¼Û©÷õpáZσõ@ _÷6Gpí˜|¸|Û?h¹Ã±´Œ€×#ꕯ›J²åœâ‚q66øô"a{SM˜äPl|¤1"!¯òÃ1lk|¿´SÙu×]gÄ[÷OÆÒ½ßKû“)|‘9_åZb;^uÕUÅÛVl£¼ó³Ö6ÈâVœ¨ fBL¾óÈÒÝòƒvçwfÄsC°1b¦(nCȧèDÖíž `µž£.v“ø ú‘ox²É"ùÇË ’“¤&/ÓªPŠu«ê‰±sb#€7xœ•Bˆ"Ûcø @ „#T4/¤/|á Þëqì:44dª¹ò£8j¬f²² ¯„Áð¢³V;1÷Ci´C¤Ï“;ÕVÑAŽ0ÆF{Ž0ÄÆ¨Áˆ¢„& Ò†s×Êþ¨V÷9êá2|uüý‡À¸VSÃã½y'E Ìºïï?Ô£Dã |]r’Ä›#¶CC¿DûyÿÀ>ÉHÛ°rç±ÌHƒ¡ÕÁù)ÃwCeÄXdø - ³¿œ®[­ ~ÆðC¦@Ìv¬Þç‡ÖmNY«ù0#ÈçCŽ´G2Ï4Ú¶÷ÙgóŽ/›% Ä0ß±Ç[v k'•~ü¾Å稇KþúXú Aq‰,N qî†Á,³Røx'zÆ!PâÝ8t§j/k¹Ù8ük#¡0"«b2ÃÐÅöÔ ¤ ž-$frU¸´ œ ±å–[®9†Ð°ý!¼N©ÞÙq=j—iuÜC~Å…56 fÜ bUÏYË+vcO%ãrÓq€¡Á¼]UþäZÏÁ9µpÉ_ëí#Pª7á²}+®$rô.=Ùß)AŽWš²õœ!=B ÔØTóýºnä¨G¸w;[ý£JŽº]þÈ/h GÍ Ôü9ƒbsDãçC¬‡ÝFÀëUyh­Û7ˆü@ úA!GŽ&® IŽF,»@žyÝêF¾‘G @Ÿ"0hä˜ñEã©Oab#¼.± ÆfÃùÔÿ1/L P‰ùн{>dÐÿ,kt QŸ\+隤Ñ-EÜm`˜;wn¦-?ÏqÇ—áÇHa:2Œ³r${öÙgæ£P!™¼^Ûywãè1$ê#0(äÈ ‘ÀþOMJ©E Ú©ÔŸR=²ºTÚv¢¤Í@`tÀó5cqÐÑ!áˆrÿý÷oX¦áãÁ¹ÿþû3….ixMœLtƽŸ£Òè¤È?foh?=þJ‹)1ƒíÝJÌh#ÆÓz…êQú^> ´unŸ–Âó¥?+1 /|LÛQ‰zD}Ê$êYÈE‡Œ3fÌÈN?ýtsˆHHK/½Ô42 Hk¾ð>!wÍ4Ö4>‡rH6mÚ´ Ô^ýõl‡v0-ÐöÛoŸ½_IO=õ”{ê©§šF¶ -‚$„©ôÜ—$.gŸ}vvòÉ'Ûæž{î™)˜«*S*Nœ…8Áñ$áL¸d«ÖýðÈ(Æ[†K|… A!Güg|°øˆ‘ø ñ¡Ã dÈŸ“ýï,m/¬eÈè ÑXJ‰e? ćzCà(Òÿ*A’¨?Ô#'HœÄH Ld!~^¬!"çwž9R$Ìȵ×^›)x«yÂÆ6ç“›nº)á¢oñÕ¶ÜrËlÙe—Í F :k„è°Ã˦OŸn¡;pº8{ölóR}ðÁ›³F®ÁãöÊ+¯œMž<Ùþ 3Žø4d‹Þ?‰‰7F>l®`_#0J‚Ön9¥Fé~­ÞÆÉ‘׈ÑkJÔê‘×)¯cT» If;â“!K-µT9(lþY<òH‹W†a4‰h8[$¶v=ÈÆolž°ñ6­(÷Šãµ×¨VRCê:4F„ë |‚ö §p€mãᢃCGÚ†é‹ 6H%…6 ’ƒ Ò%—\R‹ÍCf‰]xá…Í›kÐl+ ©w?;!~ ŒÀ #ì?ÜîˆÂ64d)OŒ N¤ÑC€i6ýj¯?N¨7#×±„ù¹Z ª#°ÄKd;ï¼³M/¾øâÙ§>õ)³áAC„FÉ‚²ÞzëÙ¬¶‹.ºÈf‘¡UÂÆ[£üàvê¿ÿû¿Û4|ö/¿üò¦-‚`q ò•¯|%Ûd“ML“ôæ›oš­Ãùzè¡Ùûßÿ~+Ãc=–í²Ë.f¿Ä¬µj÷³ ã'˜ÀŒûÀ³þßÉ(²ƒFÄâç„È÷…ÆH Œ‘<¥ûbáèWq K'I,!J¾ïÍ0Æî׿¯z¹JmÚcƵN=Ii1¥%”f÷Ò¶Ãnì…&Mšd¶E/½ô’Fë¾5¥^ôzŽaoAbj^xŽ'Ÿ|Òv-·ÜrùC†à ë¡E‰a>Ê…Ô»_E&±Ñ× ³­UÈç”^*%ŸXòÕÚ²&rÄóæRž(ù:Äq‘oïíý¯ß·÷wêÏ;<ªbMSz¶‹ñAÐ !N’ئAa›u?O«]¿_9³ ^e(º¶2–ä¨k rÔ ʰš=véÃòª$ÿ°9 ‚œô’ 7÷*Þ§¸­S&¤àP‘®îÓ}þôÔÿK×:²Þ-)æåÛ¸¥p"ƽ¨Ò~¬[÷Ž|@  0PäÈŸUß“|²Ä‡ñåðVw~ÉRÄG”Yž ùºv›ôâþžw¿/Ñ÷cwÔïun´Èˆß‡%ÉUÞ á!Ôá7Uuíx$Ã$~@ úýCÕrÿ uœo‰pA~ÀÒ9"ìÄD&@züºòÙºGã PGѰA’¨_F–à÷¹z¬Ý!ÀÄCà7¿ù=4³ùÞêóN<â‰{oH{€¹à&ƒO³áÒGÌb.Å2phµP‡”ðÉEò:ƾ Ýadîܹٔ)SZz”ÇÜÈÆÔãYð~õÕW7|B¨âÄå»ßý®y*Ÿ:uªÍ¾ÃÛ÷7¾ñ kgýœ^.óå&Þ\#›½,Kä=:9jç’Öˆø¡9"³­¦à-—ØI|,™9Âl‘‰$L/îös3ˆ™6(Ô%7‰:ï©@ÿÜÿýÙgœÑÒƒà’¯à×_½Íƃ¨Üyç8wæÌ™-åÕîÉùr¯²Ê*ÙÍ7ßÜnVqÝ8A Ýöþ(ÿ`¡9rbô–ûÛBž4„ À‘/6Spéõ úЇÌ' ëãî/~ñ 6{&Ä‚B%OK.¹¤ÆlDÚzè!»çräøã/{û}ðÁí¹qrשP~¢ŸSfbE­µÖZÙœ9s,ÛVÊ7Zx·ñ¼#4G#õ NÚ#1Öò™Ï|&;ÿüó³ÕV[Íêáí·ßžíµ×^‡€±î™ïÖ3f̰zŠ'ê[n¹eDÑ9—kpøˆ<üðÃÙ:묓-½ôÒæ'‰UQ8'¼_8zÄs6õÁ37^´ñÄ;»‚à@’D¬5'gŸ}vöÑ~ÔÒ©§žjçñÓìóá‡i·Ýv³²â¯iþüáAµÊG»†?¦»ï¾Ûü9q/±á&#Ö\µÎžÀ ç²êª«r‰É +¬`çÞ…Žlj3·ÑFY<ºýöÛÏ·pr­rRð8í´Ó¬ œ[­<År?ñıíî}ÄGØ=iÀ–0Ž8ê¤ÜCCC†¿ÐÏ·¾õ- )ÃþO<ÑwDzŸhUíç[ŇA†ð—ò>¥!¥”ÏHYi¥•’H¦[o½5½ð I/CRl%;QØÒŠ+®hëW\q™'Œ™I÷|ï{ß³û¨QN"9I¡¬ Ü[±™êÞI$Ç®U#P÷<ʧJÚzë­í<ÅJ"FiÞ¼y ¯«w‚†’B7$ÅL?úÑÏ"OÃI>[’¼§VÊ7Zx×{žâ1þ¥•¨SïWˆº _Hç[ãËæqnJÈæ¢JK)}TiM¥Í”ŠAÃm}Óšk®™ô‘O{챇Õs‹M ’D6’‚ÃZJûï¿¢îËCvÒPLÒÇ4‰L%ú¤ŽHÚpà Ó¿øE;_a@’œG¦o~ó›é¿ÿû¿Óî»ïž6Ûl3;¦´½w\Ã}åé:}éK_J¼[l±E’'n;Obí]ñOúÐ'ù6Jú˜'9L‹-¶˜]Ã1}´ÓäÉ““:IdÆÊ&‡“–G³Ï'b‘;.ýüç?OrFiù?ÿüó5Ë÷ç?ÿ9‰$u“ˆ£a!’“®¹æš´`Á‚D~"VÊ ~Ï=÷œ=÷SO=eûó?Þ²cO?ý´]+Oá†í#<’>ò‘¤«®ºÊN¯UNiƒ’bâ%»ôãÿ¸fyŠåù™²¼E¬’ÈO’WóÄÄL›„€£ªá+bjí–ˆZ’Τ@ĉrßqÇIqúì´‹:ø¡.+m¥´¶mÈ2J‹+Ñ~,¬¬£íhƒ«°¼r©¢Ñ›—oI¥å•èÖŒ¨Ú4žŠ¼m Ç®»îj/‘zJåÔÉÜÊ+¯l/;dJj[k¢´´Ò{”¨kØ´9jï}m‰i¸6ýìg?³Äǹ(|ô ˆ†w’‚ϦW_}Õ¶¥±0"ÂöCˆ Núÿõr¤˜jIñÒï®´=v­ìjìcjúùýïomïE‘Éæ&i¨ÞN½ë®»íò‰O|"9Éaûûßÿ~zôÑG­LÒ^±ËD^³“´×¾™¤Ý¶÷Í<<:?8H$òP«|¼ÓB¢9@ tÒ6Ø`Ûvrñ79Ï´ýùÇ { È¢ ÏMÛ[¯œ#H¦´UvY½òäË'Gàíd˜L KŽ38*®žÉ:È´Ÿ´¥ÒìYààoûÛ1îTÀB)ÈQm„°Á…bXMµ©M¡Á?zô¥ª6Gxª%r7q”pÓ]c¦FE—¼% »ée²,‰—„:™a6¼áOId'S¯ÒÎa ÜÇâ=VÒ[¹ шR/r~wÅ:+‰èÍ ‘;F˜TÅÄ„b\]U¦—7³a¿Š ´±ãŽ;fzá3õ^3†P³ž€0Cˆ  Ú&ù3ÏÜU¼µzû¶‹w›!!i l›Øen4¡4+¡DDÞ+²à]¸÷Þ{³_ýêW6ÆAÖÿë¿þˆÄcÈ‹:/mLŵlð^àíáÞÞðîHkbûù!ù ¿¹p/†Æ¹ hA?lC„lÔz>ir¬l Ýy¼§",–G­ò•o h™HNŽÞQ-«ƒ:È/GÔ®v ,ñŽiT¬af†/¼†l† $Â…‘™7Ýt“ï*/1Ä.’SK4`¤‹’Aäob8ÑÈÑHwéÌ3Ḭ̈M ÊwñCÁGÛ oq“fÈnEˆ‘Ë¥3’“/†Ô<“4hF!p|ˆ¤ÊÔÓ+ŸJùi0±àH8ÄyÝu×e{ï½·50|ŠRï>4TE¼‹×á6u M‘×/ˆxÈ(! †ìwŠ¤Æ‹@Ý®'\K 5ê%Ën¸¡âtÞk ÿdÒ*g ³ctˆ°M‚¤x"­üóÔº?³¨¨÷.´ tnŠÂ½°Aòûp ï“K­üý8×Céäy&²öƒs]Ï9즸/÷ë¢Y+žI.©fýI«löœ–®|œö‹ö²Q9Ëh¥™òäÏgÛ'žÛEéLZA߬ŠR Ì©ü´g,_+ý@ý7¼?ÊØ¥ðËC š!Rìiœå˜èïƒ 1ìÁ,"Œ„àG²©’N)%Ëé›pÃJŒ¥½ÈåÙjÌ !58‰YD³5Éäƒá!º^,[¯ö#•sR£“Ô»q¸šAö…^h÷’¶Èfˆ¨AMÒȘñ(†ßRO'õx* že3aרa7ch É)¯ÏÁK7ƒIŒ9¦^² 2›„{h ±ÍÎaV ×K{”4içJƒee—í•mcüJ>"MIÉý”I$ÉŽ«×VQ> ]ëݧˆ÷ Æ`)MWZKécJT,¦„RÌ8©b8©¿Ézíµ–ÂÍßÙ®ÌVk¥ZèÃW6¶Å€Úëå!6†»)·+Ìžâ~õ„wMKõΫwL“Ĥ Êܬ0ãÎ'_p:FIšr»Ãdi–ªfÅó`\ÎÄ‹¢Ð.Ê4Àò•]cñpj¥œµÊS,·ß„r1¦Ú}ýœâ’ÿVZz›ÉX<Öî¶ê9mGd7h„oÝö‡æHµ©C¡á%uŒ%ƨ™é%¡že<š>T¶ÜrK3ìÄ¢ÑìëÑ`pÝŒÐCèá¢qÁþÕ?j}4F–’ò‚:˜žêøüãeMô z| Ói:sþ²ò0½@Í0±^3*z ´ !0ÔÆyôx1ʦ†q5~C0Ï‹gÔºOïüµ}°Þq}êƒg˜ðE`"ŤI“ ´nü[´N´C·+Ø)¹Áv­†Ì‚kÔ(5ºOïb9Gs»„½?¦½¨ô²ÒkJT¢{ýZ†´€€0õwv]FE„­P¡—PšÝwVù„Œ!Ø1©…ÛD•RÛ±µžŸ¶ã¥Rò¶ã/Ñv´V3‚µ†pohÑÐb1üJWDCÛ" qz¥n+íd>0äˆFθ G£Uɽ³AŽZ/Î7”ÚŽ G]údžu™]Êl‚eã*"Œf‘7•p|y ÚE€zåu+ŸGµ}ùã±>J0q!–^½êh[fêUþ£SÜ&ׄ]C{DȱóuH‘iâÐE Ú©Ô¡\‚±ÄH ô‹`óÂLÍ^ ¾{˜5*/Ýå™Wµî…= ÁcûBòm\„@{„æ¨=ܸÊ?\|¼ H!@·p2„VÒëyûþnÝ'òçÕƒ 2¹_` á3Ο*ŠŒ=®ýû’Œ“Hà«ü‡ ‚t ­d·3üÚGÏÓø¾™zð̓wíj‚§z÷)$ÕN±Ok3œ³RVfŽr¯3fX˜iª8_åkðTͬ+fºA´ÜY£Óf묳ŽíßY¾›˜ ‚È-9€eF¾ŠÐv…ƒ‚@£öþIˆ½z'Ec{8ÆUÕ >I¸oW¿"öŽ*ŠBoa?ðÐŒãFˆý&Âá葘aˆ‡Áƒôá‡n®9¸vþüùž’‘]wÝÕ¦¯+`¬9cd8­(„— Î".+ð$ !“o²âi#¶q‰ÃGs-°ó¤@ ›P·H^·¼®uó‘WÐ a÷Cʈü©Õâþ5Šê^í´2عËj$ƒèò ‰AˆHh¶°jFÜ%yo¿ýö6,FDxâº@Œ\[Æ>b)ò<ÜÛ$¿/Kúî9sæXùш1܆*$‚µ÷/òÁʬÐå·ÛË5® †ðúÅ’ºåJ'JS"ÀU3QæóEÇ«óÐÐP†‚mÑ«¯¾š?ÅÖ§Nj¶F Ñ0KØžVäÅ_4‘ä=´Ž_Ï>Œ³±=rR¸`Á*»í¶ÛŒq.š4´D.ì˜~üã›F‰òà›‰²…ƒ€@£öþEÿxùÕl–ŽF,;EÀ5FN†Xúz§yOôëó˜üzǸԋê^/s†§° b¶F×Ì,+ FÕ eA¤#Ä:3ÛZ†Ò˜m¶òÊ+["Ž"÷€œ{C˜­†í†×[l±E¶úê«›Á9ûRc8oÖ¬Yv[f´aGEdyÎà »•k­”=Îm®Öí–ï>DøÿÄÒ°Z>¶Ú»•qšqšZÄ3N¯D 4‚Õn>|Èÿj;b«UBÕôVéý]ð®RòxˆKh»+ñó…QÔx#0hR˜íѨLÕ¯#N šHH©ø¡ò’Yeät,–á3lžòŠ–ˆ Õ—Ž8’d8-/LÛÇ›á7žÇmöJ'‚»†Œ¥z3]%øƒ¡‡ð¹€’¶ƒØj®ÖfH#‚5B¨p¼ÔÐÉÑ¢:í½JWÓ€…í"Pjà6ÓõÏ+WÀ³AŽB'"\yg‰­AâË¿¸ïíuñÎ …q@®íxACû˜Çe rÔâ?Sù[,w:,ÈmðÚöW¥ˆ­–(VÛB€zE}¢n±dÛ“VC:@_íý-ux†Äo 0~ ~{»ÁSø÷Éëýø}²1(y£ö@§Ò!4²o(±Ír†ClôH')ÑK}§*}œF† àØ_´þg¥×•P£%¢×G]‚l;IòFN»B:@Àqd ¶à¿­Ò{”Ð&1Dþÿ”xgÑ4Å;+Bú Úíõ­2Ú!Ú†ÒØ_l;´+¤U‚µŠØ[ç{Cë,•’©ŒïB†0 åû…Q­À1¨4b4p#:óz䤛ó¼ççKí ipõwÖÉ©¿³Lªà½f?äˆíxgBHß àßo;¨»#ÚÖÙŸï\E›!@Ú‘ G-¢&û†Î¨p4°$*b¾‘SUŽñ¡ÃJ‘}Ñ !†õ‡DÝq2Dãæöì£N‘¼1ÔjH8޼—þg'FÞ™s:3¼³Ñ> „¾B ØvP‡½så‰}´-Ôy¯÷Z ixù[Aë­s© †–D#ëˆ-Ĉ(8G/T „ÔêÉ  [ž Q8–oä´Ò&Õ0‡AHÞe0÷!5ÞYShŽBHß @=¦®:ɇQoÑ<“X‡à{ÛÁùž´Ò,AŽšEjäyþq£"Bм!¥"²#ohµ+$0¨'ÞÀA®½‘Ë÷þ\ÃÁyÑÀlmÿ8~Þ¡o'>k:3®5¢3ãïµVC¾A€ö€z â[C¢s^«íàÜrÔ"`¥ÓiL½r²ËZox©¨nÐéÄ(Z ê‰×!¯G|œiäèù‘|›ã$¢§sMHûðQA|ü%7>2`ž'FþÞr<$è¼ `éÚ!Ú Ú ï`±dŸ·34~v‡4ƒ@£fP*œ£zöfiú/•¡Á¥òù‡.ßÈBŠH!@ïRohÜHì£qcúÅ6u+7Ð8†,ÁÖ|Ñ¡á]Î#×.ùù± ƯËÞ~x»!"9iâ8)¤ ‚µZ鯠TDÖùÀ‘À” !r;£h`FHUh¼¨?Þ9!bI}b?KÎ izÎ¥‰àêï¨ãËÇ…}hxgã½!}€· ^‡YRóKo[¢íh㯠rÔh\RÒ±JCJ%D¼á… ¹¶ˆuOœ84Z$4Ä Û¾ßÖù¸ÛñÓ Ž!8{‡|ý}uRä÷ðs|;–@¿ àu¹ØV8!b´ü[>¤ð¸´4¼F#ê +ëÞ¨—\ä Ë‹7j죄yC˜?/ÖÛ@ ÷¾rµ¿«¼»Hq9¼7~þDÀÈŠæíƒµ¥íh;:üÏ‚u _^jtÙtBT\÷ScÙŸüVÅZºP´f÷.kyÓ7¿0‘#ÑÃe(q§ü»ÛÃ;GÖ@×È·!Ñ~tÞ G]1²@ ÁAÀÕȃóDñ$@ @ t€@£À‹K@ @`ðr4xÿi CanvasColor w 1 ColumnAlign 1 ColumnSpacing 36 CreationDate 2009-12-29 16:06:59 +0100 Creator Gustaf A. Neumann GraphDocumentVersion 4 GraphicsList Bounds {{221, 179.053}, {136, 112}} Class MultiTextGraphic FitText Vertical Flow Resize ID 145 ListOrientation Vertical Style fill GradientAngle 304 GradientCenter {-0.294118, -0.264706} TextList Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\b\fs24 \cf0 Stack2} Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\fs24 \cf0 \ } Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\fs24 \cf0 \ul \ulc0 available-stacks\ulnone \ init\ push\ pop\ } TextPlacement 0 GridInfo GridSpacing 14.17322826385498 MajorGridSpacing 10 GuidesLocked NO GuidesVisible YES HPages 1 ImageCounter 1 IsPalette NO Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo ChildOrdering 0 LinksVisible NO MagnetsVisible NO ModificationDate 2011-01-08 18:45:19 +0100 Modifier Gustaf A. Neumann Orientation 2 PageBreaks YES PageSetup BAt0eXBlZHN0cmVhbYED6IQBQISEhAtOU1ByaW50SW5mbwGEhAhOU09iamVjdACFkoSE hBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCUhAFpFpKEhIQITlNT dHJpbmcBlIQBKxBOU0pvYkRpc3Bvc2l0aW9uhpKEmZkPTlNQcmludFNwb29sSm9ihpKE mZkLTlNQYXBlclNpemWGkoSEhAdOU1ZhbHVlAJSEASqEhAx7X05TU2l6ZT1mZn2cgQJT gQNKhpKEmZkUTlNWZXJ0aWNhbFBhZ2luYXRpb26GkoSEhAhOU051bWJlcgCdm4SXlwCG koSZmQ9OU1BhZ2VzUGVyU2hlZXSGkoShm6KXAYaShJmZFE5TVmVydGljYWxseUNlbnRl cmVkhpKkkoSZmRlOU1ByaW50UmV2ZXJzZU9yaWVudGF0aW9uhpKgkoSZmQ9OU1ByaW50 QWxsUGFnZXOGkqCShJmZC05TUGFwZXJOYW1lhpKEmZkGaXNvLWE0hpKEmZkITlNDb3Bp ZXOGkqSShJmZD05TU2NhbGluZ0ZhY3RvcoaShKGbhIQBZJ4BhpKEmZkOTlNQTVBhZ2VG b3JtYXSGkoSEhA1OU011dGFibGVEYXRhAISEBk5TRGF0YQCUl4EVL4QHWzU0MjNjXTw/ eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8+CjwhRE9DVFlQRSBwbGlz dCBQVUJMSUMgIi0vL0FwcGxlLy9EVEQgUExJU1QgMS4wLy9FTiIgImh0dHA6Ly93d3cu YXBwbGUuY29tL0RURHMvUHJvcGVydHlMaXN0LTEuMC5kdGQiPgo8cGxpc3QgdmVyc2lv bj0iMS4wIj4KPGRpY3Q+Cgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBN SG9yaXpvbnRhbFJlczwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQu dGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8 L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9r ZXk+CgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBh Z2VGb3JtYXQuUE1Ib3Jpem9udGFsUmVzPC9rZXk+CgkJCQk8cmVhbD43MjwvcmVhbD4K CQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJ CTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0 PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9r ZXk+Cgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwv a2V5PgoJCTxzdHJpbmc+Y29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk+ Y29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk+CgkJ CTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVu dGF0aW9uPC9rZXk+CgkJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJCQkJPGtleT5jb20u YXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI+MDwv aW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk+Cgk8L2RpY3Q+Cgk8a2V5PmNvbS5h cHBsZS5wcmludC5QYWdlRm9ybWF0LlBNU2NhbGluZzwva2V5PgoJPGRpY3Q+CgkJPGtl eT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNv bS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50 aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk+ Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1TY2FsaW5nPC9rZXk+CgkJCQk8cmVh bD4xPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxh Zzwva2V5PgoJCQkJPGludGVnZXI+MDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJy YXk+Cgk8L2RpY3Q+Cgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVy dGljYWxSZXM8L2tleT4KCTxkaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tl dC5jcmVhdG9yPC9rZXk+CgkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJp bmc+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJ CTxhcnJheT4KCQkJPGRpY3Q+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9y bWF0LlBNVmVydGljYWxSZXM8L2tleT4KCQkJCTxyZWFsPjcyPC9yZWFsPgoJCQkJPGtl eT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVn ZXI+MDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk+Cgk8L2RpY3Q+Cgk8a2V5 PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxTY2FsaW5nPC9rZXk+ Cgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5 PgoJCTxzdHJpbmc+Y29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk+Y29t LmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk+CgkJCTxk aWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTVZlcnRpY2Fs U2NhbGluZzwva2V5PgoJCQkJPHJlYWw+MTwvcmVhbD4KCQkJCTxrZXk+Y29tLmFwcGxl LnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVn ZXI+CgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUu cHJpbnQuc3ViVGlja2V0LnBhcGVyX2luZm9fdGlja2V0PC9rZXk+Cgk8ZGljdD4KCQk8 a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwv a2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRv cjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJ PGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJy YXk+CgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0 LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPGludGVn ZXI+MDwvaW50ZWdlcj4KCQkJCQkJPGludGVnZXI+MDwvaW50ZWdlcj4KCQkJCQkJPHJl YWw+NzgzPC9yZWFsPgoJCQkJCQk8cmVhbD41NTk8L3JlYWw+CgkJCQkJPC9hcnJheT4K CQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9rZXk+CgkJ CQkJPGludGVnZXI+MDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJPC9hcnJheT4KCQk8 L2RpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTUFkanVzdGVk UGFwZXJSZWN0PC9rZXk+CgkJPGRpY3Q+CgkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRp Y2tldC5jcmVhdG9yPC9rZXk+CgkJCTxzdHJpbmc+Y29tLmFwcGxlLmpvYnRpY2tldDwv c3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9r ZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50 LlBhZ2VGb3JtYXQuUE1BZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCQkJCTxhcnJheT4K CQkJCQkJPHJlYWw+LTE4PC9yZWFsPgoJCQkJCQk8cmVhbD4tMTg8L3JlYWw+CgkJCQkJ CTxyZWFsPjgyNDwvcmVhbD4KCQkJCQkJPHJlYWw+NTc3PC9yZWFsPgoJCQkJCTwvYXJy YXk+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5 PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCQk8L2RpY3Q+CgkJCTwvYXJyYXk+ CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVBhcGVy TmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQu Y3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmlu Zz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJ CQk8YXJyYXk+CgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYXBl ckluZm8uUE1QYXBlck5hbWU8L2tleT4KCQkJCQk8c3RyaW5nPmlzby1hNDwvc3RyaW5n PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4K CQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJ CTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYXBlckluZm8uUE1VbmFkanVz dGVkUGFnZVJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQu dGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0 PC9zdHJpbmc+CgkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8 L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q+CgkJCQkJPGtleT5jb20uYXBwbGUucHJp bnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhZ2VSZWN0PC9rZXk+CgkJCQkJPGFycmF5 PgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw+CgkJCQkJCTxyZWFsPjAuMDwvcmVhbD4KCQkJ CQkJPHJlYWw+NzgzPC9yZWFsPgoJCQkJCQk8cmVhbD41NTk8L3JlYWw+CgkJCQkJPC9h cnJheT4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9r ZXk+CgkJCQkJPGludGVnZXI+MDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJPC9hcnJh eT4KCQk8L2RpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5h ZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5w cmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0 aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1B cnJheTwva2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBs ZS5wcmludC5QYXBlckluZm8uUE1VbmFkanVzdGVkUGFwZXJSZWN0PC9rZXk+CgkJCQkJ PGFycmF5PgoJCQkJCQk8cmVhbD4tMTg8L3JlYWw+CgkJCQkJCTxyZWFsPi0xODwvcmVh bD4KCQkJCQkJPHJlYWw+ODI0PC9yZWFsPgoJCQkJCQk8cmVhbD41Nzc8L3JlYWw+CgkJ CQkJPC9hcnJheT4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVG bGFnPC9rZXk+CgkJCQkJPGludGVnZXI+MDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJ PC9hcnJheT4KCQk8L2RpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZv LnBwZC5QTVBhcGVyTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5w cmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0 aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1B cnJheTwva2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBs ZS5wcmludC5QYXBlckluZm8ucHBkLlBNUGFwZXJOYW1lPC9rZXk+CgkJCQkJPHN0cmlu Zz5BNDwvc3RyaW5nPgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0 ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJ CQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQu QVBJVmVyc2lvbjwva2V5PgoJCTxzdHJpbmc+MDAuMjA8L3N0cmluZz4KCQk8a2V5PmNv bS5hcHBsZS5wcmludC50aWNrZXQudHlwZTwva2V5PgoJCTxzdHJpbmc+Y29tLmFwcGxl LnByaW50LlBhcGVySW5mb1RpY2tldDwvc3RyaW5nPgoJPC9kaWN0PgoJPGtleT5jb20u YXBwbGUucHJpbnQudGlja2V0LkFQSVZlcnNpb248L2tleT4KCTxzdHJpbmc+MDAuMjA8 L3N0cmluZz4KCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC50eXBlPC9rZXk+Cgk8 c3RyaW5nPmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0VGlja2V0PC9zdHJpbmc+Cjwv ZGljdD4KPC9wbGlzdD4KhpKEmZkNTlNSaWdodE1hcmdpboaShKGbrZ4AhpKEmZkWTlNI b3Jpem9udGFsbHlDZW50ZXJlZIaSpJKEmZkVTlNIb3Jpem9uYWxQYWdpbmF0aW9uhpKg koSZmQ1OU0pvYkZlYXR1cmVzhpKElpcAhpKEmZkMTlNMZWZ0TWFyZ2luhpKEoZutngCG koSZmQtOU1RvcE1hcmdpboaShKGbrZ4AhpKEmZkSTlNSZXZlcnNlUGFnZU9yZGVyhpKg koSZmQ5OU0JvdHRvbU1hcmdpboaShKGbrZ4AhpKEmZkKTlNMYXN0UGFnZYaShKGbopeC f////4aShJmZC05TRmlyc3RQYWdlhpKkkoSZmQ1OU09yaWVudGF0aW9uhpKghoY= ReadOnly NO RowAlign 1 RowSpacing 36 SheetTitle Canvas 1 SmartAlignmentGuidesActive YES SmartDistanceGuidesActive YES UseEntirePage VPages 1 WindowInfo CurrentSheet 0 Frame {{522, 419}, {537, 755}} ShowRuler ShowStatusBar VisibleRegion {{0, 3}, {522, 657}} Zoom 1 doc/next-tutorial/stack2.png000066400000000000000000000300521242365656200163520ustar00rootroot00000000000000‰PNG  IHDR°˜DHcYiCCPICC ProfilexÕXgTM—®î‰Ì 9çœsN’s’EÒ“d0%(J’¤( ("J•$ˆA@1¨€¢"H ÛøýÞ³g÷ßþÙ:§zž¹Us»¦Ÿª¾÷>°y†…Ã4„„FEØéò89»ðà¦P"]Èx’#Ãt¬¬ÌÁÿÚÖžhop\rÏ×ÿ:í õö‰$Y!Ã^Þ‘äßFð9," ¸ ÁOG…!…tÀ,Á'÷°ß?¸l{ýƒ~ϱ³ÑCæô€'yzFø@|‚ØybÈ~ˆâXºPï€PèÐÖ$û{zÀ¢Ì‘ 9´‡Ã,âõ/?~ÿžž^}zzúýÅÿüä—Èõ"Â=üþòy ŽFž×ïF‡\I¡Áû÷¸Á#}ÞÛSßì þÍÙo»O¨½í{¨×~Ë?Ø7ÂÐæ‹Òý¶²ûcó×ÛÿûDüõèijõÇmcÿGÆØüÁqþvް·þ_»o€¡É{@”Éß{2û»,€' GùÄîñô…‰ðóâÑAv™I(YJ‚GNFVfoøÿMÛ;_ÿ,vÅæ÷¹˜Fþc‹Dž©ÚrÆþcóH ÕÙâÊÿ±ñê'¸ÏOŽŽˆùDz•À  €p~ $Pê@S` ì€3pdàB@8 @"HYà (% T‚«à:h-àèÁ xž—`|ŸÁX[á Jˆb…¸!AH’ƒT MÈ2‡l gÈòƒB¡h(J†² \¨ºÕ@7¡;PÔB/ hZ†6aL‚`NX–†U`Ø ¶ƒÂ~p8§À§á"¸¾7Ã]ð ü ž†?ë(€"¢˜P¼(I” Je‰rAù¢"PÇP™¨BTªÕ†êC£¦Q‹¨Ÿh,š̓–D«£Ñöh2:} .A_E7£{Ðãèôú†ÃǨaL0N?ÌaL¦sÓ„éÅ<Ã|À¬a±X&¬0VkŒuÆbã±ÙØóØØNì(v»ŠÃáXqâ8 œ%Î…KÃã®á:pc¸¸ <Ï—Ãâ]ð¡ø$|!¾ߎÃÏá·(h()Ô(,)¼)ŽPäPTQ´QŒP| Ø"Є ;B !‘PD¨'ô^VˆD"Q•hM ž ˆˆ3ÄŸ$:’IäJŠ&&U“:I/H+”””B”Ú”.”Q”§)k(P¾¡Ü ¢§’¢2¡ò¦:NUJÕL5Fõ•š‚ZZ‡Ú:Žºúõõ" 'Í1šRš;44«´ô´²´–´!´Ù´µ´ý´ót8:!::oººJºt³ô(z~z=z2}2}}/ý,ƒ0ƒ C CÃu†a†%F:FFÆXÆRÆûŒÓL(&!&¦`¦¦F¦çL›ÌœÌ:Ì>ÌÌõÌcÌë,ì,Ú,>,™,7Xž±l²ò°°±žema}͆fc³f;Ìv­—m‘]ÌžÉÞÈ>ÅsˆqØpÄsTr q¬rrqq†qs>à\äbâÒæ äÊçjçZà¦çÖäàÎçîàþÄÃÈ£ÃÌSÄÓóÄËÁkÌÍ{‰w˜w‹O˜Ïž/‰ïßk~¿ ¿/>7ÿ’·€…@‚@À” … Š ¿à9Á>Áu!a!G¡t¡¡yaaá8á:áW"”"Z"á""OE±¢*¢A¢çEŸˆÁbŠbþb¥b#â°¸’x€øyñQ Œ„ªD¨D…Ä„$IRG2F²NrFŠIÊ\*IªEê«´€´‹ôYé>é_2Š2Á2U2/eédMe“dÛd—åÄäÈr¥rOå)å åË·ÊWWðQ¸ 0©H¯h¡˜®Ø­¸£¤¬¡T¯´ , ì¡\¦<¡Â b¥’­òH£ª«z\õžêO5%µ(µFµoê’êAêµêóû„÷ùì«Ú7«Á§á©qIcZ“GÓC󢿴¯–§V…Ö;m~moí+Ús:¢::×t¾êÊèFè6é®ë©éÕëÔGéégêÐØ”¼1ä3ô3¬3\2R4Š7ê4Æ›Ÿ5ž0á4!›Ô˜,™*›5í1#™Ùš•˜½330o³€-L-ò,^íܺ¿ÅXšXæY¾¶¶ ·ºkµ¶².µþh#k“`ÓgKoën[k»f§k—c÷Ò^Ä>Ú¾ÛÚÁÕ¡ÆaÝQß1×qÚIÚé¨Ó 3›s€s« ÎÅÁåŠËêƒ>¸*º¦¹>?(|0ö`¿›[°Û}wjwO÷[GZmOKÏ ÏU/¯2¯%²ùù³·¶w¾÷‚†O®Ïœ¯†o®ï¼Ÿ†_žß‚¿–¡ÿb€^@IÀ÷@ãÀòÀõ Ë ê Ý`Çà!ø;¡t¡A¡=‡¸Å K ›W /_Š0‹¸ EŒlb@™¡h‘èÔè™͘ҘÇoÅÒÆ†Æ;’qd.Î0îr<:žßÀ›˜0sTçè¥cÐ1¯cÝÇù§ÿpÂèÄÕDBbPâã$™¤Ü¤ÉŽÉm)œ)'RfSRëÒ¨Ò"Ò&ÒÕÓËO¢OœÎÏ(Îø•é9%“U˜µMÎ8%{ªèÔîißÓÃ9J9Î`Ï„žy~VëìÕ\ÚܸÜÙ<‹¼æ|žüÌüîý… …åçç¢ÏM™µ Ÿ)Þ.ñ/yVª[z£Œ£,£lý¼÷ù± ÚêË9˳Ê7/\œ¼dt©¹B¨¢°[Sù±Ê¡ªï²Êåš+lW²®ìT‡VO_µ¹ÚS£\SSËQ›S×E×-\s½öäºþõÖzÉúK7˜nd5€†è†O7=n>o4kì¾¥r«þ¶àí²&ú¦Ìf¨ùHóR‹Ët«sëèÓ;ÝmêmMw¥îVßã½WzŸñ~N;¡=¥}·#®cµ3¬s±Ë¯k¶Û½ûå§O{¬{†{Íz=4|ø O§¯ã‘Æ£{ýjýwTZ•›‡‡š+>nVnQi}¢ú¤mtßhû˜ÖX׸þøÃ§&OŸí6úÜþùä„ëÄô¤÷äü‹àß§b¦¶^žx…y•ùšæuáŽ7oEßÞ˜Vš¾?£?3ôÎöÝËYòìç÷‘ï·?¤|¤üX8Ç=W3/7oÁpáɧŸ>|û¼µ˜ö…öKÙW‘¯·¿iZrZúð=âûîrö ëJõ…Ý«V«oÖBÖ¶Ö37X7®þTùÙ·é¸9·ux·]´#ºÓöËì׫ÝÝÝ0ÏÏß¹ ¹Â¾¾,W@é ýÿä¿¿g é1’´Ã¦E²‚NÈ ú \К¬!Î…b†¨A*¦‚¨ChÞÓyÓf$3Ͱx³¾a·àhæâäŽàiã]æçPÔ2ÖQUç’ •DK®KÍKOÉ ÉvÈ5ÊW*ä*&)…)T1VUTãRǪÝ7¡Ñ©Y«U¢£“©›¡—¥Ÿck˜g”o\`R`šovÖü´EÖþ“–©VÉÖÉ6)¶Év)ö)IމNÇã]bDº† póv'{øxyE“½Oû”úVûÝòox84<²º† §‹ŒT²Šö‹9zøllõ‘¶¸áø™„åc¨ã4'Øy’ø“ùRxR9ÒXÒNRg2Ñ™;YkÙßNÍŸžÉyufòì³Üñ¼±ü±‚Ñ‘s‹Š•ô”v–Ý;ßz¡©üöÅ–Kí*ŸV½»üíÊÎUB S-Ô5Õëúõûo85xÜôk ¹qûpS|ó±–ÄÖ”;émw³îºº=§#·³ «¤»üAUOmoýÛ}MZûïÜlê|Ü5Ü5ÒñäîhÓØµñЧçže?Oœˆ™ |qpÊú¥þ+å×bo8ÞR¾ý5ýefê]ïìÍ÷¥R?šsž×]ÿÄðiçóÇÅÇ_š¾žÿ–ºúÝiYwEòë*fuiíÕúÛ?Ë6Ӷ¶]vô~Iî2íîþå_…ÓQ6hA V gŒ¥¸E„I^”íÔB4ytú F4S<ó&«/Û MÎ"®Ï< ¼þ|9ü5M‚­B-Â7E®Š–‹ŠgI“ —ò’¶—1’U—“”çV U„—•f”Ÿ¨´«ÖªªŸØ¤a¯©¥%¦Í¦C¥‹ÖÝÑ[Óÿf0o8c4i¡—hšd™l•b•j™¶?Ýü¤I†a¦n–V¶Ú)¥Ó²9’gÄΊäŠä ç  ,(æ+á)å,c;Ït¾œö"Ý%† –Jî*áËÒW”ª5®êטÕÚÔ9^;xÝ«ÞïFhCÌÍã· n_jºÞ|§¥§uäÎTÛìÝ/÷ÖÛA¾“º‹©›ão`¯èCÉ>™GòýŠʃªCê÷ kŒh>ÑÕ37~jöÌò¹Í„ݤã ç©/]_¹½v{sð­Ë´íŒñ;•YÁ÷4ï~x÷q`®q¾d!ñ“ÿgËE¥/œ_Q_?{ºÔõ½a¹b¥ðGöjâZÌzІÇOûMÓ-ÍmÙÁ_¬»¤ÿÆ,JMÄ,Àqãm(N†H¢”‰TiliÓk3Üb’`®beaËd_â´æªæþÆ+ÃçÎ\ @°J¨áÿ–h“X“x“D£ä ©zék2We«ä.Ê—*œSÌUÊTNVIPPóUwÙg¡¡­©€°Ï¯Ã¥Ë¡Ç©ÏiÀcÈcÄcÌeÂiÊjÆdNcAÚ³„-w­¶­·l6m·í¶í·¶:­9/»|=°àúþà[·—î“Ï='¼^_{ÏøÌú~ð›óŸX\šž ™ ?ô%l-ޤ‹ˆVŠ19| 6äHB\v|YBíÑ$šöŸLìOêI¾›Ò˜ZV–~ædjFlfPÖÁlËS:§ås„Ïpœeȥ̣ÈÇ  AáιŸE«ÅK%ŸK?”MŸŸº0^>rqèÒ@Å@å`ÕÐåá+ÃÕ#WGjFj×=¾6x} ¾ÿFÃÀÍÇ£·žÝžjzÛ<Û2×úéΗ¶¥»Ë÷~Ü_m_ïXï\ïÚèÞx°Ñ³Ñ»þp­oõÑJÿ÷oƒ_†>=þ8<;2ýäÕè䨸øðÓGϺŸß›hš¬Q=Uþ²èUÎëô7GßFNûÍ8¿3™Uy/ôþ#ø¸879ß³Ðð©ôsÚbøׯFß–ø¿3/S¯V k„uÂá'Å&~ ·ÝÁüBíB{üÿ£ƒìŬWݾ s¤½` €À?¨°¢ÀNÀü†s´ö7~` 0"Õ¦Rijc` <@08Ò‘Š² ÜÀ8xV!<ÄICzH…xJ‡.BmÐ3h¦…å`8¾?E¡‘šîª5‹Tmè ôGŒ$&Ó‰%a`¯ã œî&ž Œ¦§(! ‡/‰FÄF')ƒ´FéC9AeFÕE­FÝD#Os›V‰ö..Ý ½=ý4C(Ã/Æ3L‚L÷˜˜WXrYX_°`eãˆçäçäŠâæâà9Â+Æûš/ß\'Ð#˜"d Œîɵ£/–ð””Üz$],"«/Ç)·)ÿJábƒR…r±J¾j®Zžú¹}ç5ª5okõhOè,ê¡‘½­lhcjœeRk:döÝ‚u¿¾e„U¥õ„-•±}’Ã}Ç5g Ï®n»šGŒg“ך·šO¢ï ?s€_`[0mHpè@˜DøÙˆŸQÞÑc‡õb[â¤â« «:!˜x5Y<åFšbúý ã̉ì€S¿r ÏÊæŽä‡2ž»_PÊ^öôBÞEç Ê—V—ÕÄÔÙ^—¿Áܰ۸p{²y°õA[÷½¾öñÎÙî^Ú>É~‹ÁðÇÅ#½£?žŠ§}éø¶¹¬ú#aíÁOÊ-—º¿ü3 •É 8oDY:Nƒ2p Q‘†Àkð ‚!FHQ‰l¡@(Ñ„nC#Ð"L€Åa 8.{á?Ê•z€ÚA« cf£ŽIÄôcé±îØëØœî2nEAGE1IÐ$\&RcˆÓ$3R+¢|Q©’¨6©c¨Wh¢hÖhãé`ºLzúr †6F3Æ·LÑÌT̵,Æ, ¬§ØäÙ^²§pHs¼äLçRäzÏ]ÈcÆ óÞã‹åWæÿ)ÐŽœsaF¤¾¯ “[ïÈ”t”–Ú”‘¹"{BÎM^SA@‘F ¥´¥¼©²­«÷±hkªiYiè¤êVéõêÏâÄŒÍLBLϘµ˜Oï'Yª[…XWØLÙ1ÚÛ:ä;>wfq9pà‚ë;7÷0{^²«wƒ/ÞÏË¿#;(1ø}¨ñ¡ká4‡#ßF›Æ´ÆŠ9ÏpúñxV"UR~ gjmºêÉL÷¬µSgs¤ÎŒäFæ³ôœ /æ-yV–uAÿ"t©«2õ²y5óÕ÷µÍ×2ë½49ní4Ͷ Ýi»{ý~uÇÕ®›:zGûæPC¼ÃzOÆòžv>ÿþBð¥Ë묷Í3“³?>RÎ }ÒYtÿš¸tyùñu¡ŸN[Ù;={üGúÊËíE‘tùñÍî\öÞ[»»;•H±ñ €Îà´õ½ÉXÊ8ÀøÙ{ŸÿnÿÒE%ä~õŒIDATxí xSU¶ÇWÚ´´´Å§Ì-*>øF‘±€2>®W‡¢è0£€:>@||^‘Á8 Ê( ¢- " *ÊKp>¯à£0ŠÊ³-åQ–A*-´Ø´MiBö]kçìô4MN›“fí~;ç±_ëüÏïìî$;{Y„ÀˆVâ¢Õp¶› `æ ª`€£úö±ñ 03Õ 0ÀQ}ûØx˜ˆjਾ}l<Ì Dµ pTß>6žf¢Z8ªoo — >êöuÎG6>Õh1ÉçÝ´8wª×i E¬ NÚê÷õ6ªóús¼ß>ð†UÓÖ³ªPŸÀ^àÒp„U[µO·G«¶tŽCûVÀ©v™.Ü*xÕ>m)Ç*¿ûL_ƒBèàU ¸ãµ­:¦-ʧ¢<Á/íVª‚’@ÕÇ“Ú1ñ !¦g0 06¬`Tиñqqqe“qŸ+ÐB—ËeÇx6&Ä)xzb÷aÛ_ƒXë}©=¼ xlEãÒ?Þv 8gL)ЩS§xÁ‰^~’¸ ´ `l˜z_5D ž—à%£(BZZmZ††Z(¯®ÂóÉ‘• I-sð™ØP€n}£îRÕƒzb5ìÐ%ûßUãSÿ9ü§(ˆõûerÇòçÀ’œ]»ž± ¤ÄçÀëËܵ;ÂæÏ>ƒ [J[<–þ›ož"vÁ–È¼ë ¨mž$oYƒ{õ‚ÞýûÃÝæÂ®j‡\|Ê$ˆêì¨ãS§À½ù 4b+>б3Æ®»cì…‘þ 4 'ö¿'Ÿ°¸¸~bÂ̹bÚøáê‰kÊ…˱Kôƒ8Ñ©Ï aoV²í.{‘„u¤_“'ª½ŠÕíq·O¶åäô’m'wxBôÊLJáW€îÆ ñBÜ?Äñ‡Äc°=°ê}©< CèI¢±ÏøðŽ]˜pË+¹ðÂc£á©é‹aÛÜQ`±\‡+÷üÇ'ÂÇ×>cg¯…üÛ¾ü%ÈÁ³Æ›†?kvVÈ:Ó6½ûœ––w=:ŠÊí2-EŽlÜFì_• #G‡»ŸZ Û?š-Ó'~vòó7Á¢—€ýÄ˰º°F«“7&+@œ/ò}n‰#Š÷¯5BÐÒ' é³0ÒÓÔãÕ[<Æ?ïxÍÓãñ¤˜·l­()«ù\ö½â™œÞžô.e_=/©Ç1âNqXD‚u¨Øew‰¢7î•i¿è—m•û¯™/lØ…x‘9|‘Ø¿ý-OùÕeõâXɱn]¾8hsa›61pºL¯´¾…­|"¼ 'Ä qCüGÄq%¿›@+ÚÔ·)“¾2j#=91R÷&Æ 0þãµ}(P-VNºKBCé*&÷# «ÜC‚¯ó<9„øqó fŒo}½_;¼EŒÉî âã‰-¶ 15»£,ÿAi#¶óoñÌà>¢cÿ‡ÅÎêB °ª›àÿ ¤ÆË–j±düYž‡^Ò˜t¨Ýâ„x!nˆâˆx"®L8ûÆn{b¼ã1ú•–loÏ,e'HÎÚ&\6wïIÓøµ±b‹xrpS¯LuÀå[ä8×|4¦‡€òR$€??L»CãáoÄýàî±éÁÙZE½1³ÐîqB¼7ÄqD< p°c`lËÔø…ºŠ-Âö™·âx×¾¨ƒ ú\ ÷ŒžKß~Eæ£Ñ»*e±tÀA´¾|þFxiM<·¼ìŽr˜‘ãþXÎbMƒ.8Tjt–@E-qÚëfM§&/€ÿ4Ð1¤âúÁˆCÀåÚwŒ^$?‘ û`\×~ð&Ö=jÖ§PQ”}3nÉø% (V;A×y**Æã>ÕEÛáŒK/—çfÜt.Œš>Ì›Cï'ÏuéÚI~¤]‡çñ?†%ëwA]ûb&¡¶.É…Ç×Û Î‚oÑR»ÃŸÈ€“'?†ñgÁ;³ƒ?Œ sWU@¾- :2ßóæ¿OXáØª‘0kÃ(Y< æ€ûc³ò]+aÜðá0¤ÿX¹×ÖÂV>aŠz^CÁõ(ø/$ q0^5D]=už]0ž‡±ÆŒ·`ôñ_É!Š–M‘oÆ(]ÅQ³¾Ð>6+3Ÿ&ϧtœ*ömjzÓGC‡aÚpb!Ži]U…â‰ì$O‰‰÷‹op¨à²ï”C†ØÐ‚Ÿ¾|Iæ¡áÆÒ¿ßìɯڦík?û°•O…SMâ$#qCüGÄSÀCˆ€g£i_#Ó¿ú„ÞÔ0v£òäé¸ýÀàl€ÊÊjùeERFdx}èÖÐЀà +$a„†j¨´9!-Ó×7vN¨,¯Äz¬üž™#ù %1ÜŠñÆãk0Öb¬Çx£“ ܶ)ø³¶RRßåûÚ÷_Üš™Yô©‰ï”¤#:)2u‡ÍKX ëiž—"P_ÜèÏ…àVõð;¢Õ’œL`zàV[8|øp«y8Cl*€³ÑBzáa˜{àÞ#®Ì@8ƒ4Nb"^8âoh¤l¤§E¼ pÄß"6ÐHØHN‹xàˆ¿El ‘ °‘:œñ 0À‹Ø@#`#u8-â`€#þ±F 0ÀFêpZÄ+ÀGü-b`€Ôá´ˆW€Žø[Ä)À©Ãi¯ñ·ˆ 4R€6R‡Ó"^8âoh¤l¤§E¼ pÄß"6ÐHØHN‹xàˆ¿El ‘ °‘:œñ 0À‹Ø@#`#u8-âà•y"þ±F „`^ÍHòØNãµÑbûþóÕ{)uc`Ѱr,ñ0§° jŠ_‡Ä„a°[óáumòPæÏmÒ§Fóm)ß¼„Ñ‘Êvn]ƒs[@žFû±Ó¨ÕXO‹:€-IÝ`Ò²%Ðÿ ·ã—“®r°{»öqW’hýä–¡­å[–ô>ã„{åÀ K¾÷NhãñI¹Ty3s6MÓÞƒž3©ç¤%æ/¾iåȯòËÇy%pEèÓo~Í9‹Y¾¹@|:s=-‚ÚЗÿ~éCòüì6‰íù¯Êý[ç5»: tÔþÓË6‰²Ò½!‰.׈ŠÒÏ¥“™ô!SEa™ûzh¦2úO¥Åbþ§‹‰cD™½PôÃkÙåo¤¯m>â·=Ì•´×8!^ˆâ‡8"žö—¼¨¶8ÀU{ÅGyrثĿ^¹^:ö&§†_<™å†÷|8ZW¡rÜ?R¼^|Vp„Jˆz,ÿLv²¸|úwžšçpkù–(·²6‘—"®˜¾Íp¡­Jz8o¶çÛæüYÚXeGçŒÅ%¢¤céaÑhÛêxù6QÕWì› ö£•v1?§“¨AïÏåvwC…ÛÑ"yW?üYéitP\¶˜ôÂHùðä"¼\~ÛsÛm¯ÈOH6eaMH€ó_€4 $$w†ß[q¥Ã+îšÕkÞƒ} µ°aÑûÐoæ]ð_x•Vø–Þsþ›M€Ž/€ç‹ípV'÷?{zмƒaþ¸«á’³•ǘT¸rØo›o8 ÅÅ'àÓÑ}=C–¾cVJk÷¼ =²{@= oöB¨M½žzaL»½/d$XàW·¿ ûê¬h¯]ìà½itmüÛã|HáüL÷˜Ü’ô[˜¾x ôÄ1zŠk7Lyj¾Û6‡ì,Ð7ž¿öš_B¬™pÉÒ'aì¼$ØXzìØ‹úp¸ª¥æ³ÿ‹oaá[ `áš}Ó…x¾Ý9 >¹ám8XeÃÿ6ÀÞ jÝEZÜ+. óã¸Òóá@lûn_ó:¬ èçÔãV}‡lv;)Ù+žY?6›MÆŸŽŒƒÔÚj8çöç1Ÿ öo„{€û¯ùü§iHnl¿SHO£UZ~á<oN_ð“rÔ¸°àøbÚeðx¿Çä§+Ž6µ×ürbéÈ€'XãÏ‚³ºuQ¾¦ß’–Îä(–ÂY04oÌúËX(2úgQ/ë¾»×ÿæ|8+#Ê6.†‘ëk@4¸½mÊbº§æ…Ó_~r+2tì8ŠÕV®„‘kŽÃð?œã©Ái=Wz]0{9º¬í)΃0çÂÿ{Þ-Ã+5(ÆI1®€)càä3¯ƒ…ï¹@}æKs5ˬ Î2é1pñUC‘ÝéV÷7V¾Êð9VÀ”Øi+ƒíE?Hµ÷,ŸwOž /Œ¼V~mÛgs o4´YfåÇ÷ä뮔y¯¸üØåc¯LäV0`ÇÏû`îËä7oŽÊ½°xÊØzÞHØ”ÿ¤Íÿ+Œ~};~ËV +rç@a]*Üüè4„û"˜<ñ8ÃÏ<^¾{¬)` À`uäx¥±2‡/‚eî€ËrîƒW¦ýŽS¬ f’pVBŸÀï-§ÃÀ[®Â!ß(VÀ¿æ¬kŸÞ—u¿4[ñŠlgèRµ]ü6‹‚Ãà§BZNÞĸ¦,õvÏâ‘»F4$pïãx¶~ù¿ À­ÙETãÈû+[ËÉé1®€i[~ãVÚ»Sm:އ,œ“›ˆss!) .ËN„»z]ëó×Ä1~Ïøòu ˜2M×^»8[‡IIMˆP˜³F¨Q9-8-­op%¹Tì(`Ú"v$å+5SØLµ¹­+À‡\R®ÐL¢`¹†®Š3g§çgÆfêÅmE˜Q0€{ ±Dˆ0)Ùœ_BS¶—®„|æN%g ÑÚhß–Ûåõîyg®…¶Ãsíîc\[ ƒ¿5ÁègòËÞ˜wöê 뛸x»ÌÏ/±§€);êÂüùOäAIi úy.ô?çI¹ˆýØ7°òG7Ì$¿û˜Ö€Ø÷÷ÿ d/ûp 2¸ä põ¨erÅZ„$?ï¸.÷+XûÆc0íî¾ðþ¿›êˆ½Û»WlÒ·HL¼¾Y4Îǧ,ÿ>ê’«wN…+!¿kZ2ʪŽÚ"&+têvL9R ·ýHÆ:¹‚ÍsùŸÃ}ý~ âªT4"ªjà䨽“1zå¦ôÀ¤mrÏ>ÐU=.iép.ö¢j½öGr'ŒÖ£²§Ÿ¦ÁŠ3×Zÿa»¾ÞoO ˜°sŸ³x :7ÑÏ×ŵ°ûÿj ¿ó¿&˜Z4Åh[{ºE|-F ˜pÔÕO„é«èÍY-äÏ|¾±Þ×÷ %av.ü'þ¬È ?~ý*<\\©xÎÿd²¿°RõOÝ9¦Þܦj-å®?çãÏ…lƒGÃ%ãn†ó’'âÊèý`ÎB£ÑoÇì¡0ÄBÒ>sp\ƒ —t5ÍZ“§Ý³Øœo? 6ü¹P&úMæÀ è0©ne6š7KoþÚÈ\ x‘„Ó/õ³/›gä£XVÀ€ƒ™fI:¾\¶Îe±Ìg«×nÊ„öV­à 1£@¨'´›ô)DÌܾP“`€Mœ› ­ phõäÚLV€6Ypn.´ 0À¡Õ“k3YØdÁ¹¹Ð*À‡VO®Íd`“çæB«@(¦YîúZ ¹¶ö¨€žù+‰`/2€?%c‚½.u xsbÄ”áŰ¾B}Ã.L ÈhMÅŠžŸÖÊøL?€õ+ƒNúl…O²Í N3zŽšçjÃQ(f£‘!dE¹êtbb¢Z}š–açÀ *bŽøPÌ?A‡S˜ ROC?V£x§ÃáèŒ[úEPG-vÀ-ýðœ\°,¡g#^ç ŒõZ¤eú«0*V(ŸbHާÚ‚Xßí“ôDQ$ck1ÒÐDão+€&O. 0ŠAL ä§#±A‘Î)f(¯ž' ActiveLayerIndex 0 ApplicationVersion com.omnigroup.OmniGrafflePro 138.17.0.133677 AutoAdjust BackgroundGraphic Bounds {{0, 0}, {559, 783}} Class SolidGraphic ID 2 Style shadow Draws NO stroke Draws NO CanvasOrigin {0, 0} ColumnAlign 1 ColumnSpacing 36 CreationDate 2011-01-08 19:24:38 +0100 Creator Gustaf A. Neumann DisplayScale 1.000 cm = 1.000 cm GraphDocumentVersion 6 GraphicsList AllowLabelDrop Class LineGraphic Head ID 440 ID 443 OrthogonalBarAutomatic OrthogonalBarPosition 2.2564773559570312 Points {221.997, 89.5171} {245.983, 109.83} Style stroke HeadArrow FilledArrow HeadScale 0.5 LineType 2 TailArrow 0 Tail ID 434 AllowLabelDrop Class LineGraphic Head ID 441 ID 442 OrthogonalBarAutomatic OrthogonalBarPosition 2.3118362426757812 Points {180.008, 89.4617} {147.8, 109.885} Style stroke HeadArrow FilledArrow HeadScale 0.5 LineType 2 TailArrow 0 Tail ID 434 Bounds {{209.173, 110.153}, {109.654, 29.8672}} Class ShapedGraphic ID 440 Shape Rectangle Style shadow Fuzziness 4 Text Text {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural \f0\fs24 \cf0 Tcl Checkers} VerticalPad 0 Bounds {{69, 110.153}, {109.654, 29.8672}} Class ShapedGraphic ID 441 Shape Rectangle Style shadow Fuzziness 4 Text Text {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural \f0\fs24 \cf0 Native Checkers} VerticalPad 0 AllowLabelDrop Class LineGraphic Head ID 435 ID 438 OrthogonalBarAutomatic OrthogonalBarPosition 2.3323459625244141 Points {331.468, 38.6146} {367.407, 59.0793} Style stroke HeadArrow FilledArrow HeadScale 0.5 LineType 2 TailArrow 0 Tail ID 436 AllowLabelDrop Class LineGraphic Head ID 434 ID 437 OrthogonalBarAutomatic OrthogonalBarPosition 2.3546905517578125 Points {274.737, 38.5923} {234.051, 59.1017} Style stroke HeadArrow FilledArrow HeadScale 0.5 LineType 2 TailArrow 0 Tail ID 436 Bounds {{252.058, 8.5}, {105.5, 29.8672}} Class ShapedGraphic ID 436 Shape Rectangle Style shadow Fuzziness 4 Text Text {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural \f0\fs24 \cf0 Value-Constraints} VerticalPad 0 Bounds {{336.328, 59.3267}, {115.48, 29.8672}} Class ShapedGraphic ID 435 Shape Rectangle Style shadow Fuzziness 4 Text Text {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural \f0\fs24 \cf0 Scripted Checkers} VerticalPad 0 Bounds {{149.154, 59.3267}, {109.654, 29.8672}} Class ShapedGraphic ID 434 Shape Rectangle Style shadow Fuzziness 4 Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\fs24 \cf0 Built-in Checkers} VerticalPad 0 AllowLabelDrop Class LineGraphic ID 417 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {227.423, 338.207} {227.346, 351.255} {244.346, 351.255} {244.346, 351.255} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 418 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {227.499, 325.159} {227.422, 338.207} {244.422, 338.207} {244.422, 338.207} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 419 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {227.576, 312.111} {227.499, 325.159} {244.499, 325.159} {244.499, 325.159} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 420 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {227.653, 299.063} {227.576, 312.111} {244.576, 312.111} {244.576, 312.111} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 421 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {227.73, 286.014} {227.653, 299.062} {244.653, 299.062} {244.653, 299.062} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 422 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {227.807, 272.966} {227.73, 286.014} {244.73, 286.014} {244.73, 286.014} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 423 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {227.883, 259.918} {227.806, 272.966} {244.806, 272.966} {244.806, 272.966} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 424 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {227.96, 246.87} {227.883, 259.918} {244.883, 259.918} {244.883, 259.918} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 425 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {228.037, 233.821} {227.96, 246.869} {244.96, 246.869} {244.96, 246.869} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 426 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {228.114, 220.773} {228.037, 233.821} {245.037, 233.821} {245.037, 233.821} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 427 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {228.19, 207.725} {228.113, 220.773} {245.113, 220.773} {245.113, 220.773} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 428 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {228.267, 194.677} {228.19, 207.725} {245.19, 207.725} {245.19, 207.725} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 429 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {228.344, 181.629} {228.267, 194.677} {245.267, 194.677} {245.267, 194.677} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 430 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {228.421, 168.58} {228.344, 181.628} {245.344, 181.628} {245.344, 181.628} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 431 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {228.497, 155.532} {228.42, 168.58} {245.42, 168.58} {245.42, 168.58} Style stroke HeadArrow 0 LineType 2 TailArrow 0 Tail ID 432 Info 1 AllowLabelDrop Class LineGraphic ID 432 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {228.449, 132.734} {228.497, 155.532} {245.497, 155.532} {245.497, 155.532} Style stroke HeadArrow 0 LineType 2 TailArrow 0 Bounds {{247.094, 149.213}, {71.7328, 221}} Class ShapedGraphic FitText Vertical Flow Resize FontInfo Font Helvetica Size 11 ID 433 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\fs22 \cf0 alnum\ alpha\ ascii\ control\ digit\ double\ false\ graph\ lower\ print\ punct\ space\ true\ wideinteger\ wordchar\ xdigit\ } VerticalPad 0 Class Group Graphics AllowLabelDrop Class LineGraphic ID 407 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {78.1457, 220.773} {78.0687, 233.821} {95.0687, 233.821} {95.0687, 233.821} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 408 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {78.2217, 207.725} {78.1447, 220.773} {95.1447, 220.773} {95.1447, 220.773} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 409 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {78.2987, 194.677} {78.2217, 207.725} {95.2217, 207.725} {95.2217, 207.725} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 410 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {78.3757, 181.629} {78.2987, 194.677} {95.2987, 194.677} {95.2987, 194.677} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 411 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {78.4527, 168.58} {78.3757, 181.628} {95.3757, 181.628} {95.3757, 181.628} Style stroke HeadArrow 0 LineType 2 TailArrow 0 AllowLabelDrop Class LineGraphic ID 412 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {78.5287, 155.532} {78.4517, 168.58} {95.4517, 168.58} {95.4517, 168.58} Style stroke HeadArrow 0 LineType 2 TailArrow 0 Tail ID 413 Info 1 AllowLabelDrop Class LineGraphic ID 413 OrthogonalBarAutomatic OrthogonalBarPosition -1 Points {78.4807, 132.734} {78.5287, 155.532} {95.5287, 155.532} {95.5287, 155.532} Style stroke HeadArrow 0 LineType 2 TailArrow 0 Bounds {{97.0007, 149.04}, {83.3936, 91}} Class ShapedGraphic FitText Vertical Flow Resize FontInfo Font Helvetica Size 10 ID 414 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\fs22 \cf0 boolean\ integer\ object\ class\ metaclass\ baseclass\ parameter} VerticalPad 0 ID 406 GridInfo GridSpacing 14.17322826385498 MajorGridSpacing 10 GuidesLocked NO GuidesVisible YES HPages 1 ImageCounter 1 KeepToScale Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo Animate NO circoMinDist 18 circoSeparation 0.0 layoutEngine dot neatoSeparation 0.0 twopiSeparation 0.0 LinksVisible NO MagnetsVisible NO MasterSheets ModificationDate 2011-01-09 23:32:39 +0100 Modifier Gustaf A. Neumann NotesVisible NO Orientation 2 OriginVisible NO PageBreaks YES PrintInfo NSBottomMargin float 18 NSJobFeatures coded BAtzdHJlYW10eXBlZIHoA4QBQISEhBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCEhAhOU09iamVjdACFhAFpAIY= NSLeftMargin float 18 NSPagesPerSheet int 1 NSPaperSize size {595, 842} NSPrintAllPages int 0 NSReversePageOrder int 0 NSRightMargin float 18 NSTopMargin float 41 PrintOnePage QuickLookPreview JVBERi0xLjMKJcTl8uXrp/Og0MTGCjUgMCBvYmoKPDwgL0xlbmd0aCA2IDAgUiAvRmls dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGlWctyG7kV3eMrsKQWauPZaGxHmVTF i1QmZiWLVBYamrKooSyblMf5/JyLBm53o5stuiyVqlhHwH0c3BfAr/I3+VUq/HofZeis PO3lv+Vn+e7urOXuLHX6Pe/krWq8pL/Rwgf57h/7027/5fXb/VGcDhCljUvinJPamUZ7 28q2M00brJW7Z/nub89a/uUlqdWWFCuJtdbExpowrBX9WsNrDa81zjfKYy1Ewp5+oR0W sgGtbiJ+Otla3XSujVnoYMBgrFEwNhgYkNdOjQUdhuhQElS8++f+eP96+HN/93J8OR2e 96+nwy67nxy6BWu2a9oYQFfXNc7DCNj5yxYMKqWM3O6k1nmtlre2xeK2wwffeLEFTX/V jQL12we5+f3l5bi//3wjt0/y123P3HVqxCU1xoC4Ss3h8+v+0/50nRpiEt4s+hBnsv8j Ny83FEEG3pQPT/0HsdkXZPd6I/8rt++v9nJJPUKiIhDad8ei4758OJ+vVbbiq26XfH1m j+BQcjprFT9nSA6hJa+RPgtuM9eD12zaOiWCykIJ0IvBvGQJkr4KLYED+FJoZ0tO/GlO FxuJaBwCQsv3QsunvjTdfUgpoOSHu7GlKtUwBGdA1nWqDbDdNME6LRPmTddGqT3SzBot jjLi0wSTR/lhmmNjmdXaQabQLRyHTOwn3V6TnrZrfKcIgx7CujF2QY9I+w3ZXvYnPTZ4 ktnppjVdljnDlmRSAEPmeG00PR8mJj6iQ4EOWSZhsHPAVmRW+5MeY8h3o0ITjC8ya2xN 5nRtkqmdg03GqCYEm2XOsBWZ2hF3vD/JVC35btBEOoNITmc0wyqZBq0stjhRb2xDcZOL w4WyrpqubZ281Yo6Z1Vw74+fvz2Pyi2Cm3ocmsdMy3rzKFq6MFICadQ87o9fHpFpC63j kpJJUhfRwS3VF07h8+5QEhwfhpQd1ZCZuqWyWtShc9d0URXnJoJm2HeTvsCKzYn/hUp/ nf7rOHXI4KqfbD4ePh2g+Ac45fAQi+xS16+j4+PLt9+PKITXaFmjcqHVg8qHaWOSm9wg xeYMnT9E4KJHi+3oUzm1ofZzX3i8VutVCUdzYZVtx5fvF4abH4nM5YbPXpw4DXKEis2l mWamdam753xY7u6s9VvhldPi4iQ107oSOvNOLhE5Z1bL2b/LISPq6XSmbcVHasl8Zn3x grYyQJ3Yxx8MT8GpNx1WM7PUYFlrnre/Hz7u14bhmVcrHKJZ1/Lh1fdyYFy1Th97CBMi MqGvbczvZApaK6kr9KKb13UMhvwvq5UbDlxOUkYuRXCai8h3YzDwuA4zwzBrJYyuNGXW Qn+li5ursKq/TmVO1mY9CRtmraRH43rFs1LWM8Yw5y3pIbrSfqNG+3vMOgtsmLXIdsLE CFuTOdqfZi3SY9rJXCVIZoWt2jld29upO/heZi34STInGHhfs3O6NsvUphM8K2WZwIb5 6Q2ZuPwOa58hqmuUpfmtn7VMjoUaW7NzslaQnaGJdK02rsWQFWl+M87NsMsy+/04BOwP mOh6mV1HfvrYRBx0llljF2TCz9BM12aZykNmoMmxZZkVtiSzzy3IxFQ67O9lBkzemBPb RmncbXrfa2xN5nRtL7PF7UaaGBvVpnsM8VljazJHazGj9zJ9gO9W43EFP9nOGluTOV3b y3QRtcYaBLrnc6+xNZnTtVmm0ZCJRxvkUrHTVdiqTAT5sL+XaR357nVjfLoHEZ81RjLz WxuaHv3igUm7mF/OIi4nCkGjVcRROJwOgj3oSM90D4sXX96LGLa4RJrZXoG9We3wpjVu K8o2EVplUB2uzjGUtmLy/cTIWxjYmSBdNRqjpfzCnZobCD++lG5+y//iiUXeoFPjeeiu dD9ug2j4fT/c/cEfT5PHm/qtUli8p/WvicyfRuuh4H6LPt460Dfd+hZ7Fp0W7w5vsad0 9HaJvw+7YYjkUasQxwRw35Z5kL+7ET1LP8Eb6p7JL6uU+RYRrRWNZYU28n056ngnzVPT TRSq83AbLrtWOcQ4EQbKPV2X0ZbpbXEcbqExLigEXD2s/etGLNyQrpKpGm1CxCGwTB47 efTi0OWo5iO45VDlKY6j+fyaw3m46cxDvhzqJJZHeVjenYTB7TtQ/0R58tGYSLW1YGhf Dpin2soY5rGCWYSj117iIpswmhEKRqVJ6balvYzhXSNjo9qUH9tQm3ghHneaEDtLzZgu sKQEXSVhSSDewyrscb4brjxA+Hv8PYkF54cdHjGQDBvp84OxBrNn0lcwODrSN9pNbuEt EX9PixXU4hsAem2iF3ztWryUoaD0EMj2gIhshnAmBWpD4xQqLj0a1Bi41og2A7NsWQdT EwZxZBTjRG2HRgRycOxNJJlEbcJov0+YGGGPi7uZ2mVH2Y6QbRvpG9lW9MkRNtIHansv npMXmrqYQKSQ5f3XL7hJxoA+stzF1s6CZWiNdPUYK2oZb9VjjYc6alUtzIy6/xKqri5I Ee1x0Avt7O+l+3BJKGnLCf0nl4ShGKeWJTY/VYzR8vtvpC7Rt1KOy96LtFVVWaQvtr7i 6MoTZIshIEZ8H3aZN5QMPKx3owKK/dsHsdkuP1tdIRI3hUok1+ThC4uKZnk1zdNvNUp9 xTWPHglClF1E4ms8vD8PWAyNBRaRo7wOI3LCkMva4ZmbeCoYrSuYVvgGssPYNsFoukrY qL6Cf/pNs18W2AZ8zYkqQsZg3MJcjsMIGM4JI4HtDHtk1WK0e70IjIxFgpBhI306jYLe JUezvoLBhkEf0pJ3v1FfcU2i72XBMuj2OigLjYyBbgcstbOyDtQyhusteo4nuhMGy+jK m7BEt4VdIwRkE0I2MZq4oUPGTIGpENISryHJcmMk+Tjft85oWS8SJ8m7oidx11uY9TCC XjX4AS5pH1n92/8BymqEGwplbmRzdHJlYW0KZW5kb2JqCjYgMCBvYmoKMjE4MgplbmRv YmoKMyAwIG9iago8PCAvVHlwZSAvUGFnZSAvUGFyZW50IDQgMCBSIC9SZXNvdXJjZXMg NyAwIFIgL0NvbnRlbnRzIDUgMCBSIC9NZWRpYUJveCBbMCAwIDU1OSA3ODNdCj4+CmVu ZG9iago3IDAgb2JqCjw8IC9Qcm9jU2V0IFsgL1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFn ZUMgL0ltYWdlSSBdIC9Db2xvclNwYWNlIDw8IC9DczEgOCAwIFIKL0NzMiAxNSAwIFIg Pj4gL0ZvbnQgPDwgL0YxLjAgMTYgMCBSID4+IC9YT2JqZWN0IDw8IC9JbTMgMTMgMCBS IC9JbTEgOSAwIFIKL0ltMiAxMSAwIFIgPj4gPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9M ZW5ndGggMTQgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGgg MjQwIC9IZWlnaHQgODggL0ludGVycG9sYXRlCnRydWUgL0NvbG9yU3BhY2UgMTcgMCBS IC9JbnRlbnQgL1BlcmNlcHR1YWwgL1NNYXNrIDE4IDAgUiAvQml0c1BlckNvbXBvbmVu dAo4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae3QMQEAAADCoPVPbQ0P iEBhwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg wIABAwYMGDBgwIABAwYMGDBgwIABA48DA/eAAAEKZW5kc3RyZWFtCmVuZG9iagoxNCAw IG9iagozMDAKZW5kb2JqCjkgMCBvYmoKPDwgL0xlbmd0aCAxMCAwIFIgL1R5cGUgL1hP YmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAyNDggL0hlaWdodCA4OCAvSW50ZXJw b2xhdGUKdHJ1ZSAvQ29sb3JTcGFjZSAxNyAwIFIgL0ludGVudCAvUGVyY2VwdHVhbCAv U01hc2sgMjAgMCBSIC9CaXRzUGVyQ29tcG9uZW50CjggL0ZpbHRlciAvRmxhdGVEZWNv ZGUgPj4Kc3RyZWFtCngB7dABDQAAAMKg909tDwcRKAwYMGDAgAEDBgwYMGDAgAEDBgwY MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA gAEDBgwYMGDgYWD/wAABCmVuZHN0cmVhbQplbmRvYmoKMTAgMCBvYmoKMzA4CmVuZG9i agoxMSAwIG9iago8PCAvTGVuZ3RoIDEyIDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlw ZSAvSW1hZ2UgL1dpZHRoIDI2MCAvSGVpZ2h0IDg4IC9JbnRlcnBvbGF0ZQp0cnVlIC9D b2xvclNwYWNlIDE3IDAgUiAvSW50ZW50IC9QZXJjZXB0dWFsIC9TTWFzayAyMiAwIFIg L0JpdHNQZXJDb21wb25lbnQKOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0K eAHt0DEBAAAAwqD1T+1pCYhAYcCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw YMCAAQMGDBgwYOADAwwvAAEKZW5kc3RyZWFtCmVuZG9iagoxMiAwIG9iagozMjMKZW5k b2JqCjIwIDAgb2JqCjw8IC9MZW5ndGggMjEgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0 eXBlIC9JbWFnZSAvV2lkdGggMjQ4IC9IZWlnaHQgODggL0NvbG9yU3BhY2UKL0Rldmlj ZUdyYXkgL0ludGVycG9sYXRlIHRydWUgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVy IC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHtnOtXUlkYhwXOgQOHOwcQ5CYginIRxUso hVG2aPASZWqszNSF2XLUbDQHJ8vJZCRTc9S8zFiaadY4Wpo5Oa751+ZAriUXZ76z134+ uPh4fntvns1xve+blgaBKwBXAK4A0CtAiUAFjGio/9w2Mi6NhqAkdMCIZEJoVCrllOxk agRlYEwcZ3NAg43jLIwRiZ6UnEKloQwmm8sXEoREIgUKiZgQCnhsFoYiScHJ2BjOFUpk CqUmU6sDCW2mRqWQSwkeG6MnBqdQydh8Qq7WZeearFYbSORbzXk5eo1CIuAw6bT4k05B GDhfrNAZrfbS8rOuCpBwnXM6imymLJVUwMZQapzbqCiTSyj05mKn21NV67sGEr4r1Zcv nivNz1ZJ+XjChlPpuFCus5xxe+v8zW3tHSDRHmi5WV9TWW4zKAkuhsQddBrGlaiNxe4a f2tn74PgIEgEB+53BZquXnKYtekCVtxBpyBMvkxndXr9gXvB4VB4HCTCoSc//XC36cr5 QoNChMfnRlmCjGy7u6713sPQ5Mz8IkjMz06NPe670+gpzVOL2fRYsVFQXKTMLfX4O4Oh Fwuv3qyDxNrK0q/hoZ7maqclU8plJOQmNKbyqube4YmF1XdbOyCx9X5tafppf8DnsunS E3OzxZnWs7Vt/aMzrza3d/dBYm/nw+rL8ODd6+5CvYyHxe83W6LNd/nafwzPr23tHhyC xF+ftzeWJoY6Gy7aDfJTc1+9M/hs4e32/tejf8Dh6PDLx3e/Tz7uaqxMyk2lc6Q6W8W1 jsHxxfWd/UNwUpNJ/j74tLk8Ndx9o7LIIOdjtJgfqjA33G9gzjo85/D7few26DXoNei1 FF8B6HPoc+hz+F4C38dSXOTHjw99Dn0OfQ59Dn0OfZ7KKwDvMXiPwXsM3mPwHktli588 O/Q59Dn0OfQ59PmJE1P5E/Q59Dn0OfQ59HkqW/zk2aHPoc9jfZ7v+lanub1/CFJ9Klmn +TG2TvOUeuSTutwjgPifulwKStZhW12+wMDY3Oofu1++gsTB3p9vF59H67CzkuuwxRqL s6a17+n08sbWp73PALG7vbkyOxbsqHMX6GXchLp7nFCbHN5bPY/GX75ef78FEh82VhYn n/S1+Vz5uuT+EpHSWHKpsWNgZGLut9dvQGJlef5FaLCryVtu1kg4CX01ZB+VofC873Z3 cOTZ1MwcSMxOPx992BuorywxqoiEPioE46VrLWWXG1q7+od+Hg39AhCjI48GegL+GpfN IBfG982lkX2SYmWO3eWtb27/vrfvAUj03e/uaLlRe6HEpJHymWhcfyjZFyuQZeYVuzy1 1/23breCREvzzQbfd+4zFn2GiJPQF0u2vXNFcm1egcN1weOtqgGJaq+nsqLMbtYrxbz4 tti0NAqNzuISco3BZLOXOMqcIFHuKC0qsBh1Cgkfx5DYn6lkbirKwLmidKVGn23MM5lB wmTKzcnSKuUEGRtNmHMQDc7iCAipLEOpUqs1AKFWKSNjLQTcSOz47SZfy8jxLXQMJweZ CIQiAjBEAgGPgzMZZOw4m0ffRskJLtHBNSwcPFhMjEEnU58Sm8wemctEQ6KTiiKTfQAC QWiR0Mmbffz/h28bHx3jBNaf2HzwM1wBuAJwBcBcgX8B0bSpawplbmRzdHJlYW0KZW5k b2JqCjIxIDAgb2JqCjEyMzcKZW5kb2JqCjIyIDAgb2JqCjw8IC9MZW5ndGggMjMgMCBS IC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMjYwIC9IZWlnaHQg ODggL0NvbG9yU3BhY2UKL0RldmljZUdyYXkgL0ludGVycG9sYXRlIHRydWUgL0JpdHNQ ZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHtnPtP WmccxgUO1wNHLgdQFLkZHAqiFi/F0h5Hs40No0Ot3UgmBiOatnaaeBnGNXZOVnXONcyB 6arV6lxXRlM62umM/9pecGQe0P0Bb76fH7z99j68z5PX5Pt9ysoAUAAUAAVAgUsU4OTg 4kv+fJecHf0ZHZ3HI/gIAb7kjkfw0GfMuUAIpADBF4rEJCmV4YyUJMUiIZ+4QAUOl8cX iqWUXEnTGo0WVzQamlbKKVIs5PNKrgKSQERSSk1ltd5oMlswxWw2GfVVlRolRYqQCGw7 cLhIAjmtM1jq6u1OZzOmNDmd9vo6i0FHy5EIRReBQwhJubraYnO6Oq5dZ7owhWGud3a4 nDZLtbqcFPLYucjliym6utbR5vH6unsD/ZjSF+jt9nk9rQ5LFU2J+WwzcAWkUmdpvOr1 DwRDI5FxXImMhIIDfm+Hw6JTkgK2BjwRpTHY2rw9wfDdqdnoPK5EZ6fuhIM93lZbjZoS 8c6HIocQyystTo8/ODoZXYytrOHKSmwxOjn6ud/jtFSUi4nzgcDhSxRVdS7vQHhyIfZD PLGFKclEfH15YTLc73VZqxQSPlsDUqWv7/AF70ZjPya39w4wZX9vO7ERi94JftRu06vI Yg1oo/1ad2hqcT25e5h6hSnp1OGzxPriZMjfaTfQxRpI1Sbn9d6RmeX49m/pTBZXMunD p/Hl6XCPp9FIS4vugVRjbmICka9WEs9TmXfHmHL0LpPaS6zMjQZuOE3qCzXoG5tfTe6n s0cnp3jy91H2j+fJ1WgkwJRowBXItJbmrv7x+bWtg1fZYzwVOD09Oc6m97fW5sf6mCaz RsZ6JIEGZWWgAWiQezPDPQAN4B6c/fcIXgAvgBfAC2cKgBfAC+AF8EJBAcgDyAPIg4Ib 4K0MeQB5AHkAeVBQAPIA8gDyoOAGeB9AHkAeQB5AHhQUgDyAPIA8KLgB3geQB5AHkAeQ BwUFIA8gDyAPCm6A98G/edDEnM3qprPHuM4r52d1k6vzY4GzWV32Dkd+bv2/me0TPPm/ mW0OH2ngZAKjc482d19m3h5hyl9vM78/23w0N/rphbP7amOjpyc8/e3jp7+mXr/5E0/e vE4d/PJ4KbfD4Sjd4SBpg93tH/rywdrPOwcvUpjy8sXB9ubag/tffOK215Ts8qCdLlv7 h5+Nzy2tbz7Z2cOU3Z0nP32/NDd++wO006Us3mdCu33WK+8Hhu9Hl1Y34pu4Et9Y+SY6 EQp0tVh1Cgl7v5EQlVeYGzs/vh2emPn64XLsOzyJLT9cmJkID/rcDrO2XEQU3kf572jX V61/z8X4B0ORe1PTs5gyMz11LxIa9DNX6vS0TMja9UULDKSi0tTQxvh6bwWHhsO4MjwU vNXrY1obTBWK4p1vVAFBqXTmhhY3c9Pn7+7BlW6/7+aNqy31Zp1KhkogWF7g8AQSitYZ rfZmV7u704Mrne52V7PdatTRlERQ1AGBukCEJKWq0Btr62wNdgem2O0NNmutUa9VUaSQ YF+DXDkSXyiRKWhtZZW+xmAw4onBUIMqYbS0QiYRFtehIF+geiSBiETlQAqlisYXlVIh p6SkSIAKklhpkP8FNSTlS6IkJNZIJPmKqNJ2pDNFcj1pPCLfFpar1MIU4tKqsHP3Il+r hu+XcyeFH0EBUAAUAAVKFfgHs7nA2AplbmRzdHJlYW0KZW5kb2JqCjIzIDAgb2JqCjEy NTIKZW5kb2JqCjE4IDAgb2JqCjw8IC9MZW5ndGggMTkgMCBSIC9UeXBlIC9YT2JqZWN0 IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMjQwIC9IZWlnaHQgODggL0NvbG9yU3BhY2UK L0RldmljZUdyYXkgL0ludGVycG9sYXRlIHRydWUgL0JpdHNQZXJDb21wb25lbnQgOCAv RmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHtnNtPGlsUh4GZ4TY45TKgIJSB MVIEGW0JWhQzliYNDQRCtZfwIAYjNbEXSbwU0zY2Vk7V2EuIFdNUo629pARTemijMedf OyMmcwRsz/Ps7O/BbH3av73nm4zJWkskgsATgCcAT0CQJyA+RgIG1Sy/vwUuJoKgGIcU DI6joAgikYjPCM2lRTGZXIHjqiZQUOG4Qi7D0DMSiyUIJlOoCLWWJA2GZhAwGEhSqyZU ChmGNFwxF1eOE1qD0WyhbHYaBOw2ymI2GrQELucC1z7SYgkXV02arLSjw80w3SDAMO4O B201kWoucN0Fi1EZrtabaSfj7e0fYAdBgB3o7/UyTtqsV+MytPadJcEUBGlu6/QFgqFI LD4EAvFYJBQM+DrbzCShwGofaIkU15poz+VgeDiRHEtPgEB6LJkYDgcve2iTFpfW5kXk hMHq9AWjidRkZjY7DwLZ2cxkKhEN+pxWAyFHTr+wxKhCbaSZQDgxPpVdyC2vgsBybiE7 NZ4IBxjaqFbUCCzGlJpWhzc4nJp6nHuZ39gEgY38y9zjqdRw0Oto1Six0y8sMYbrLB29 ocRkNve6sLW7BwK7W4XXuexkItTbYdHh9XlJyt0fSWYW1go7n4v7IFD8vFNYW8gkI/1u iqzPq9LbmIHY2MxSfutLqVwBgXLpy1Z+aWYsNsDY9Kq6+1UZ7F1sPP1oeeNDsfzrEAR+ lYsfNpYfpeNsl91wZt4bd+dXCh9LlYOjf4TP0UGl9LGwMn/3RmNeibSpme4eHJqYX93c 268cCj8tl+Cwsr+3uTo/MTTYTTc31XxwwLwA3DC8X/6TEj7P8HkW3AlAf6G/8HtDcNry G4b+Qn+hv7wOgltAf6G/0F/BactvGPoL/YX+8joIbgH9hf5CfwWnLb9h6C/0F/rL6yC4 BfQX+gv9FZy2/Iahv9Bf6C+vg+AW0F/oL/RXcNryG/4/f7vYk3rCUuUQiPrJw0rppJ6Q 7aqvJxRj1frY/+pFjwDgD/Wi1bwMGx+fe7G+86388wAEfpa/7ay/mBuPs8wZ9cB6yhOI pqafv3n/qfj9x9/C58f34qf3b55Pp6IBD9VQ742TVrc/PPLw6erb7b2vRRD4urf9dvXp w5Gw321tqOfn+jWcPdfuTMwtrq2/294Fge1362uLcxN3rvU4G/s1uH6c9ktX4qMPsosr r/LrIJB/tbKYfTAav3KpvaEfB5Wfa7F7+q7fTt2fefJsKfeX8MktPXsycz91+3qfx95y Tl7TbyXi+un0lgteNnwrmb6XmZ4FgenMvXTyVpj1XrDo6/rpRFy/pMZoc/nYUOxmYmQ0 BQKjI4mbsRDrc9mMmvp+Sa7dmdCZ7K6LfvZqKByJgkAkHLrK+i+67CYdwTU88/8cHS/E iFRJkCaq3d3t7fH3BUCgz9/j7Xa3UyaSUEqR0+2DXF4JJsMJXYuFanM4Xe5OEHC7nI42 ytKiI3BZfT97NbCySUM2G1st561WSvhYrectrcZmUtOkbIwrEnHjN6RynBtIodHqSDDQ aTXcOApcLuUGcNTYW/2Fm8BRHTiixMFBWR030jh94yT98dwcBK1OlDkexQIAKMpNk6kb RdFw09UxO2D8aMgG/wBPAJ4APAHhnMC/Q8ssjAplbmRzdHJlYW0KZW5kb2JqCjE5IDAg b2JqCjEyMjIKZW5kb2JqCjI0IDAgb2JqCjw8IC9MZW5ndGggMjUgMCBSIC9OIDMgL0Fs dGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4 AYVUz2sTQRT+Nm6p0CIIWmsOsniQIklZq2hF1Db9EWJrDNsftkWQZDNJ1m426+4mtaWI 5OLRKt5F7aEH/4AeevBkL0qFWkUo3qsoYqEXLfHNbky2perAzn7z3jfvfW923wANctI0 9YAE5A3HUqIRaWx8Qmr8iACOoglBNCVV2+xOJAZBg3P5e+fYeg+BW1bDe/t3snetmtK2 mgeE/UDgR5rZKrDvF3EKWRICiDzfoSnHdAjf49jy7I85Tnl4wbUPKz3EWSJ8QDUtzn9N uFPNJdNAg0g4lPVxUj6c14uU1x0HaW5mxsgQvU+QprvM7qtioZxO9g6QvZ30fk6z3j7C IcILGa0/RriNnvWM1T/iYeGk5sSGPRwYNfT4YBW3Gqn4NcIUXxBNJ6JUcdkuDfGYrv1W 8kqCcJA4ymRhgHNaSE/XTG74uocFfSbXE6/id1ZR4XmPE2fe1N3vRdoCrzAOHQwaDJoN SFAQRQRhmLBQQIY8GjE0snI/I6sGG5N7MnUkart0YkSxQXs23D23UaTdPP4oInGUQ7UI kvxB/iqvyU/lefnLXLDYVveUrZuauvLgO8XlmbkaHtfTyONzTV58ldR2k1dHlqx5erya 7Bo/7FeXMeaCNY/Ec7D78S1flcyXKYwUxeNV8+pLhHVaMTffn2x/Oz3iLs8utdZzrYmL N1abl2f9akj77qq8k+ZV+U9e9fH8Z83EY+IpMSZ2iuchiZfFLvGS2EurC+JgbccInZWG KdJtkfok1WBgmrz1L10/W3i9Rn8M9VGUGczSVIn3f8IqZDSduQ5v+o/bx/wX5PeK558o Ai9s4MiZum1Tce8QoWWlbnOuAhe/0X3wtm5ro344/ARYPKsWrVI1nyC8ARx2h3oe6CmY 05aWzTlShyyfk7rpymJSzFDbQ1JS1yXXZUsWs5lVYul22JnTHW4coTlC98SnSmWT+q/x EbD9sFL5+axS2X5OGtaBl/pvwLz9RQplbmRzdHJlYW0KZW5kb2JqCjI1IDAgb2JqCjcz NwplbmRvYmoKOCAwIG9iagpbIC9JQ0NCYXNlZCAyNCAwIFIgXQplbmRvYmoKMjYgMCBv YmoKPDwgL0xlbmd0aCAyNyAwIFIgL04gMSAvQWx0ZXJuYXRlIC9EZXZpY2VHcmF5IC9G aWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4AYVST0gUURz+zTYShIhBhXiIdwoJ lSmsrKDadnVZlW1bldKiGGffuqOzM9Ob2TXFkwRdojx1D6JjdOzQoZuXosCsS9cgqSAI PHXo+83s6iiEb3k73/v9/X7fe0RtnabvOylBVHNDlSulp25OTYuDHylFHdROWKYV+Oli cYyx67mSv7vX1mfS2LLex7V2+/Y9tZVlYCHqLba3EPohkWYAH5mfKGWAs8Adlq/YPgE8 WA6sGvAjogMPmrkw09GcdKWyLZFT5qIoKq9iO0mu+/m5xr6LtYmD/lyPZtaOvbPqqtFM 1LT3RKG8D65EGc9fVPZsNRSnDeOcSEMaKfKu1d8rTMcRkSsQSgZSNWS5n2pOnXXgdRi7 XbqT4/j2EKU+yWCoibXpspkdhX0AdirL7BDwBejxsmIP54F7Yf9bUcOTwCdhP2SHedat H/YXrlPge4Q9NeDOFK7F8dqKH14tAUP3VCNojHNNxNPXOXOkiO8x1BmY90Y5pgsxd5aq EzeAO2EfWapmCrFd+67qJe57AnfT4zvRmzkLXKAcSXKxFdkU0DwJWBR9i7BJDjw+zh5V 4HeomMAcuYnczSj3HtURG2ejUoFWeo1Xxk/jufHF+GVsGM+Afqx213t8/+njFXXXtj48 +Y163DmuvZ0bVWFWcWUL3f/HMoSP2Sc5psHToVlYa9h25A+azEywDCjEfwU+l/qSE1Xc 1e7tuEUSzFA+LGwluktUbinU6j2DSqwcK9gAdnCSxCxaHLhTa7o5eHfYInpt+U1XsuuG /vr2evva8h5tyqgpKBPNs0RmlLFbo+TdeNv9ZpERnzg6vue9ilrJ/klFED+FOVoq8hRV 9FZQ1sRvZw5+G7Z+XD+l5/VB/TwJPa2f0a/ooxG+DHRJz8JzUR+jSfCwaSHiEqCKgzPU TlRjjQPiKfHytFtkkf0PQBn9ZgplbmRzdHJlYW0KZW5kb2JqCjI3IDAgb2JqCjcwNApl bmRvYmoKMTUgMCBvYmoKWyAvSUNDQmFzZWQgMjYgMCBSIF0KZW5kb2JqCjI4IDAgb2Jq Cjw8IC9MZW5ndGggMjkgMCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0 ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4AdVYZ1QUTZeu7onMDDnnnHNOknOSHEXS kJNkETCQJShKkqQooAgoIkoQlSSIBEFABTGBqICiIkiQINv4Hv3es2f33/7ZOqd6nrlV c7umn6q+9z4AsBR5hoUFwzQAhIRGRdgY6fI4Obvw4KYAEVAiXQDIeJIjw3SsrMzB/9rW ngNob3Bccs/X/zrtfx6g9faJJAMAWSHDXt6R5BAE30bwEjksIgoAuArBTw5HhSEYhXTA EIEsEMEn97DfP7hsD3v9gxt+z7Gz0UPm9ACAJ3l6RvgBQHyC2HliyH6ID+ISAFi6UO+A UADo0AjWJPt7egPAoo/MkQgJObSHwxAs4vUvP37/wp6eXn99enr6/cX//Bfkl8iN9QMi w4I9j/z+8n95CQmORp7X70aHXEmhwfv3uMEjfd7bU9/sDw4L/s3Zb7tPqL3tH3uo137L P9g3wtDmDw6L0v0XtrL7Y4/z19v/B/tEGvz1E+hpavXHHhFtY/8HR8bYGvzBcf52jn+w t4/+X7tvgKHJH3tAlMnfewUdMvu7BhAALIAnIEf5xO7xDvQOhR2JCPDzj+LRQXaZjwSP SShZSoJHTkZWZm/4/03bO1//LHbF5ve5gZhG/mOLRJ6p2hByBsb+Y/NIAKDVFdniyv+x 8RMQ6icBuM9Pjo6I+ccfspUBwAACoAYMgBVwAX4gAiSBHFAC6kAbGABTYAnsgDNwA2Tg D0JABDgMEkAiSANZ4AwoACWgHFSCq+A6aAQt4B7oAg/BIHgCnoGXYBp8AJ/BElgDWxAE 4SBKiB5ihbghQUgckoNUIE3IADKHbCBnyAPyg0KhaCgBSoayoFyoBLoE1UA3oTtQF9QP jUIvoBloAVqGNmEUTIIZYE5YCJaGVWAd2Ay2gw/CfnA4HAenwKfhIrgCvgY3w13wIPwM noY/w6sogCKimFC8KEmUCkoPZYlyQfmiIlDHUJmoQlQFqh7VhupDjaOmUYuon2gsmh7N g5ZEq6ON0fZoMjocfQydjS5BX0U3o3vQ4+gZ9BL6F4YSw4ERx6hhTDBOGD/MYUwaphBz BdOE6cU8w3zArGGxWCasMFYZa4x1xgZi47HZ2PPYG9hO7Ch2FruKw+FYceI4DZwlzhMX hUvDFeOu4TpwY7gPuA08Ec+Nl8Mb4l3wofgkfCG+Ft+OH8PP4bcoaCgEKdQoLCm8KY5Q 5FBUUbRRjFB8oNgi0BKECRoEO0IgIZFQRKgn9BJeEVaIRCIfUZVoTQwgniAWERuIj4gz xJ8kOpIYSY/kSoomnSZVkzpJL0grlJSUQpTalC6UUZSnKWsoH1C+odygoqeSojKh8qY6 TlVK1Uw1RvWVmoJakFqH2o06jrqQ+hb1CPUiDQWNEI0ejSfNMZpSmjs0EzSrtPS0srSW tCG02bS1tP2083Q4OiE6AzpvuhS6SroHdLP0KHp+ej16Mn0yfRV9L/0HBiyDMIMJQyBD FsN1hmGGJUY6RgVGB8ZYxlLG+4zTTCgmISYTpmCmHKZGpudMm8yczDrMPswZzPXMY8zr LOws2iw+LJksN1iesWyy8rAasAaxnmVtYX3NhmYTY7NmO8x2ga2XbZGdgV2dncyeyd7I PsUBc4hx2HDEc1RyDHGscnJxGnGGcRZzPuBc5GLi0uYK5Mrnauda4Kbn1uQO4M7n7uD+ xMPIo8MTzFPE08OzxMvBa8wbzXuJd5h3i0+Yz54vie8G32t+Ar8Kvy9/Pn83/5IAt4CF QIJAncCUIIWgiqC/4DnBPsF1IWEhR6F0oRaheWEWYRPhOOE64VcilCJaIuEiFSJPRbGi KqJBoudFn4jBYopi/mKlYiPisLiSeID4efFRCYyEqkSoRIXEhCRJUkcyRrJOckaKScpc KkmqReqrtIC0i/RZ6T7pXzKKMsEyVTIvZelkTWWTZNtkl+XE5MhypXJP5SnlDeWPy7fK f1cQV/BRuKAwqUivaKGYrtituKOkrBShVK+0oCyg7KFcpjyhwqBipZKt8kgVo6qrelz1 nupPNSW1KLVGtW/qkupB6rXq8/uE9/nsq9o3q8Gn4alxSWNak0fTQ/Oi5rQWr5anVoXW O21+bW/tK9pzOqI6gTrXdL7qyuhG6Dbpruup6R3V69RH6RvpZ+oPG9AZ2BuUGLwx5DP0 M6wzXDJSNIo36jTGGJsZnzWeMOE0IZvUmCyZKpseNe0xI5nZmpWYvTMXM48wb7OALUwt 8ixe7RfcH7q/xRJYmljmWb62ErYKt7prjbW2si61/mgja5Ng02dLb+tuW2u7Zqdrl2P3 0l7EPtq+24HawdWhxmHdUd8x13HaSdrpqNOgM5tzgHOrC87FweWKy+oBgwMFBz64Krqm uT4/KHww9mC/G5tbsNt9d2p3T/dbHhgPR49aj21PS88Kz1UvE68yryWyHvkc+bO3tne+ 94KPhk+uz5yvhm+u77yfhl+e34K/ln+h/2KAXkBJwPdA48DywPUgy6DqoN1gx+AbIfgQ j5A7oXShQaE9h7gOxR4aDRMPSwubDlcLLwhfijCLuBIJRR6MbI1iQBKZoWiR6NTomRjN mNKYjcMOh2/F0saGxg4dETuScWQuzjDucjw6nhzfncCbkJgwc1Tn6KVj0DGvY93H+Y+n HP9wwujE1URCYlDi4ySZpNykH8mOyW0pnCknUmZTjVLr0qjSItIm0tXTy0+iTwacHM6Q zyjO+JXpnTmQJZNVmLWdTc4eOCV7qujU7mnf08M5SjkXzmDPhJ55flbr7NVc2ty43Nk8 i7zmfJ78zPwfBe4F/YUKheXnCOeiz00XmRe1FgsUnyneLvEveVaqW3qjjKMso2z9vPf5 sQvaF+rLOcuzyjcvBlycvGR0qblCqKKwElsZU/mxyqGq77LK5ZorbFeyruxUh1ZPX7W5 2lOjXFNTy1GbUwfXRdctXHO99uS6/vXWesn6SzeYbmQ1gIbohk83PW4+bzRr7L6lcqv+ tuDtsib6psxmqPlI81KLf8t0q3Pr6B3TO91t6m1Nd6XuVt/jvVd6n/F+TjuhPaV9tyOu Y7UzrHOxy69rttu9++UDpwdPe6x7hnvNeh89NHz4oE+nr+ORxqN7/Wr9dwZUBloGlQab hxSHmh4rPm4aVhpuHlEeaX2i+qRtdN9o+5jWWNe4/vjDpyZPB5/tfzb63P755ITrxPSk 9+T8i+AX36diprZenniFeZX5muZ14RuONxVvRd/emFaavj+jPzP0zvbdy1ny7Of3ke+3 P6R8pPxYOMc9VzMvN39vwXDhyacDnz58Dvu8tZj2hfZL2VeRr7e/aX8bWnJa+vA94vvu cvYK60r1D4Uf3atWq2/WQta21jM3WDeu/lT52bfpuDm3dXgbt120I7rT9svs16vdkN3d MM8Iz9+5AAq5wr6+ACxXA0DpDAD9EwAInf/kv79nIOkxkrTDCKZFsoJOyAz6BhegXNCa GAWsIc4Hf4VihqhBKqaCqENo3tN5039mJDPNsHizvmG34Gjm4uSO4GnjXebnFlAW1BEy EtYTURNVFBMR55KglURLrkvNS0/JDMl2yDXKVyrkKiYphSkfVDFWVVTjUseqf903odGp WatVop2jk6mboZeln2OQa5hnlG9cYFJgmm921vy0Rdb+k5apVsnWyTYptsl2KfYpDkmO iU7HneNdYg9EuoYeDHDzdid7+HgGeUWTE71P+5T6Vvvd8m8PeBQ4GjQVPBuyGLoWhgqn ixCMVI2yivaLOXr4bGz1kba44fiZhOVjqOM0J9gSeZL4k/lSeFI50ljSGU5SZxAy0Zk7 WWvZ307Nn57JeXVm8uyz3PG8sfyxgtHCkXOPiwaKH5X0lHaW3TvfeqGp/PbFlkvtFY8q n1a9u/ztys5VQg1TLX+d1DXV6/r1+284NXjc9GsMuRVx+3BTfPOxlsTWlDvpbRl3s+6d un+6Pacjt7Ogq6S7/EFVT21v/cObfU2PWvvvDtwfbB/qfNw13DXS8eTuaNPYtfGKp+ee ZT9PnIiZDHxxcMr6pf4r5ddibzjeUr79Nf1lZupd7+zN96UfUj8emnOe110Q/8Twaefz x8XHX5q+nv+WuhT63WlZd0XyB+sqZnVp7dX6w42bP8s207bCtl129H5J7jLt7v7lXxca hdNRNmhBDBtWCmeMD6W4RYRJXpTt1EI0eXQE+gxGNFM88yarL9sLDk3OIq7PPAq8/nw5 /DUCTYKtQi3CN0WuipaLFYpnSRyTDJfykraXMZJVl5OU51agVYQUl5VmlJ+otKvWqhWq n9gXpGGvqaUlps2mQ6WL1t3RW9P/ZjBvOGM0aTxs0mPaanbN/KJF7v40yyNWYdYBNr62 ZDsve3eHg44uTvbO1i5mBwxcNQ8qu8m6i3uIeAp7iZDFvCV8pH1l/GT9ZQNkA2WCpIMl Q5BQe0gqTDFcJ8I60jsqLjon5vLhO7HDR2biVhNwR1mPiR5XPqGXaJpkmWyVYpVqmbY/ 3fykSYZhpm6WVrbaKaXTsjmSZ8TOiuSK5AnnCxcIFwqdEywSKOYr4SnlLGM7z3SBvpz2 It0lhgqWSu4q4cvSV5SqNa7q15jV2tQ5Xjt43ave70ZoQ8zN440ZtwpuX2q63nynpad1 5M5U2+zdL/fW20EHvpO6i6mb4wFvj2Cv6EPJPplH8v2KA8qDqkPqj/cNa4xoPtEe1R0z GDd+avbM8rnNhN2k4wvnqQMvXV+5vXZ7c/Cty7TtjPE7lVnB9zTvf35493FgrnG+ZCHx k/9ny0WlL5xfUV8/f3u61PW9YblipfBH9mriWsx60IbHT/tN0y3NbdkdwV+su6T/xn8s SgVNxAAswHHjbShOEoZIopSJVB9pbGkf02sz3GKSYK5iZWHLZF/itOaq5v7GK8Pnzn9c oECwSqge4f+WaJNYk3iTRKPkDal66WsyV2Wr5C7KlyqcU8xVylROVklQjVDzVXfZZ6Gh ramAsM+vw6XLocepz2nAY8hjxGPMZcJpymrGZE5jQdqPs4Qtd622rbdsNm237bbttx22 HH86rTkvu3w9sOD6/uBbt5fukx7PPSe8XpBfe8/4zPp+8Jvznw9YCFwImg+eC5kLnT/0 JWwtAo6kixKIVooxOXwgNuRIQlx2fFlC7dEWJJr2nxhM7E/qSb6b0phanVaWfuZkakZs ZlDWwWzLUzqn5XOEz3CcZcilzKPIxxagCkHhzrmfRavFSyWfSz+UTZ+fujBePnJx6NJA xUDlYNXQ5eErw9UjV0dqRmof1z2+Nnh9oL7/Rn/DwM3HjaO3nt2eanrbPNsy1/rpzpe2 pbvL937cX21f71jvXO/a6N54sNGz0bv+cK1v9dFK//eBb4Nfhj49/jg8OzL95NXo5Nj4 +PDTR8+6n9+baJqsf1E9Vf6y6FXO6/Q3R99GTvvNOL8zmVV5L/SB/iP4uDg3Od+z0PCp 9HPaYvgX169G3xSW+L8zL1OvEH8QVglrhHXCBuEnxSZ+C7eN3cH8Qu1Ce/z/o4PsxQSs EgBXkBrdvgkAc6QGvWAMgMASEj+oALCiBMBOFcD8EwCGcwC0E/Y3fmAACTAi1aYIUmlq AGNgCzxAMDgC0pGKsgrcAg/AOHgPViE8xA5JQ3pIhXgISocuQm3QM2gZpoXlYAc4Ab4C P0WhkZruEKoaNYtUbR7oCvRHjCQmGtOJJWEPYK/jIJwT7iaeCh+MH6aQpyghoAiHCC+J RsRGEicpg7RG6UM5QWVG1UWtRt1EI09zm1aJ9i6dLt0gvT39NEMowy/GM0yCTPeYHZhX WHJZFVhfsJ1gF2Uf44jn5Occ5Iri5uIe4DnCK8b7mi+P31wAJ9AjmCJkIIwT7hfJErUQ oxEbFy+W8JSUkNyQeiRdLBMiqy/HKbcp/0rhgWKDUoVysUq+aq5anvq5fec1qjVva/Vo T+gs6qGRva1saGMUapxlUms6ZPbdgnW/vmWEVaX1hC2VnbF9ksN9xzVnCRfPAwWuA26Q u5pHjGeT15q3mk+i76A/c4BfYFswbUhw6ECYRPjZiJ9R3tFjh/ViW+Kk4quPChyrOiGY eDVZPOVGmmL6/QzjzInsgFO/cgrPyuaO5IcXMp67XxxQyl729ELeRecKgcoflx9Wl9XE 1Nlel7/B3LDbuHB7snmw9UFb972+9vHO2e6NXto+yX6LwfDHxSO9oz+eijx3nyyeev6a 4a3NTO7s+EfmefdPtYub3yy+V63srrltdG4J75z9/f7AIEohI+AGokAeaCIagwPwAocQ XSETFCNaQit4BF6AT2Aboob4IWVEIyBDcYgqcA3qg97DMMwHG8CBcB7cDn9F8aDsUadQ fWgUWgedhO7FEDE2mDLMHFYRm4qdwEngknGv8ar4IvwmhTvFQ4IMoYxIIMYSP5IcSA8p VSnrqQSpyqk5qEtpOGkqaEVpb9Np043Qu9F/ZTjGSMNYyaTKNMoczEJgqWW1YF1hK2U3 ZF/huMRpxbnL1cDtxcPEM8SbxqfDD/i7BFIFzYQYhN4K14kcFtURI4iNiZdIeP3mvk+6 SCZIVkeOVe6H/KhCo+I5pRPKh1TIqq5qTupO+1w0PDT9taK0k3TydKv12vUnDJaNKI3F TIxNA8yyzG9avLBEW8lZk23O2Q7b4x0MHJOdupx3D2i4xh1sdVv3UPU85tXrTe3j6lvn 9yvANrAuGBPiHno3jCk8MuJJlHT02ZjVWNcjD+PlEiqOMRzPSISSjiZvpR5NR508lcmW VXtK4/TTMyG5FHk1BWaF34uKSwxKV85Xlttfwlfcr4q6Ilm9UFNTF3Rdpn6joacx97Z3 s3or853tu4v35zu+d6N6OB6qPXIdSByqG54cJYxrPYuduP1i+ZXkG+/pwne975fneBZs Pqd96fi2uaz6I2HtwU/KLZedur/8MwEeIAYUEZXJDDgBb0RZOg5OgzJwDVGRhsBr8A2C IUZIFFGJbKFAKBHRhG5DI9AiTIDFYQs4Ai6Be+EVFD/KAZWNeoDaQaugYxCNZgOjjknE 9GPpse7Y69gdnBXuMm4b74C/RUFHEUUxSdAkXCZSEmOI0yQzUiuifBRREamSqDapY6hX aKJo1mjj6WC6THoG+nIGCYY2RjPGt0zRzFTMtSzGLAusp9jk2V6yp3BIc7zkTOdS5HrP Xchjxgvz3uOL5Vfm/ynQjpx/c2FGpL6vFA0QkxZbE++QyJR0lBKW2pQekbkie0LOTV5T QUCRRgmltKW8qbKtBqsT97FoCGuqaVlpB+ik6lbp9erPGeKNxIzNTEJMz5i1mE/vJ1mq W4VYV9hM2THa2zrkOz53ZnE5cOCC6zs3Efcwj3teBLKrd4Mv3s/LvyOQOygx+H2o8aFr 4TQRhyPfRpvGtMaKHjkfz5Bw+hjxeFYiVVJ+CmdqbbrqyYFM96y1U2dzpM6M5EbmsxX0 nAsv5i15VpZ1Qf8idKmrMvWyeTXz1fe1zdcy670aNBo5bu00zbYM3Wm7e/1+dcfVrpsP OnpH++YHUEO8w3pPAsbynnY+//5C8KXL66y3zTOTsz8+Us4LfdJZdP+auHR5+fGPjXWh n05b2Ts9e/xH+srL7UUPAJF0Efnxze7uihAAuFwA9t4PWxW7uzuVSLHxCoDO4H+09b3J WBoAypA4A8DDuNkTe5//bv8F0n9FJQplbmRzdHJlYW0KZW5kb2JqCjI5IDAgb2JqCjU3 MDgKZW5kb2JqCjE3IDAgb2JqClsgL0lDQ0Jhc2VkIDI4IDAgUiBdCmVuZG9iago0IDAg b2JqCjw8IC9UeXBlIC9QYWdlcyAvTWVkaWFCb3ggWzAgMCA2MTIgNzkyXSAvQ291bnQg MSAvS2lkcyBbIDMgMCBSIF0gPj4KZW5kb2JqCjMwIDAgb2JqCjw8IC9UeXBlIC9DYXRh bG9nIC9PdXRsaW5lcyAyIDAgUiAvUGFnZXMgNCAwIFIgL1ZlcnNpb24gLzEuNCA+Pgpl bmRvYmoKMiAwIG9iago8PCAvTGFzdCAzMSAwIFIgL0ZpcnN0IDMyIDAgUiA+PgplbmRv YmoKMzIgMCBvYmoKPDwgL1BhcmVudCAzMyAwIFIgL0NvdW50IDAgL0Rlc3QgWyAzIDAg UiAvWFlaIDAgNzgzIDAgXSAvVGl0bGUgKENhbnZhcyAxKQo+PgplbmRvYmoKMzMgMCBv YmoKPDwgPj4KZW5kb2JqCjMxIDAgb2JqCjw8IC9QYXJlbnQgMzMgMCBSIC9Db3VudCAw IC9EZXN0IFsgMyAwIFIgL1hZWiAwIDc4MyAwIF0gL1RpdGxlIChDYW52YXMgMSkKPj4K ZW5kb2JqCjM0IDAgb2JqCjw8IC9MZW5ndGggMzUgMCBSIC9MZW5ndGgxIDEyNzgwIC9G aWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ab07eXxTVbrnnLtmaZqkSbM0aZKm SZruOy0tNJauQCtQlrZSaAuFUkELlGKdgVcVBAqiiCyC44gLq0goFQKIj2FAxJlR3BUd lxEZnZk+35uHzig0ed+5KbX06fz8w9/c9Dv3bPec73znO992bzuWLGtBEagbMWhSfVP7 PCRdaa0IkVVzFjW1h8tR2XB/ZU5nhz1c5hIQYhbOa5+/KFwWH0FIbp2/sGvweV0JQubH Wlua5obb0XW457ZCRbiM6XjxrYs67gqXtX1w715455zBdh2tLl7UdNfg/OhDKNvvaFrU QhsQSoPxUXz7nUs7pCJK/QTuc9uXtAz2x7WA32sIQ60d3Ylk6HYkIILU8GtASPhCbkUs tNJ2uOYmKzbPjiz8GmlEqTy76kHp/pLjhXf+2XLdo9gkfgsVshv96Z33Br0IKTG09ys2 DbVIz0FiD6CapACqBCgCyAFISrrFiLrxbvQQwBMADFqA16MugHUAjwKwQ7l9UDqO1/ey ou8E7kJmPN6nYG1TdSabUa6wvRHAfN/jtveNn53EJti9T7GpNwLJbpHjJ/Cv0Vxkw88g F74bVaAEvOOId6GtEZr2oXaAbgBGSjHe1xubaXsRJyMXi+EZN4pl8VHbnzNSbJ9nBAju tZ3xBFi4/SYWSr5I22nr47b/tM63vQhwINy03ws9jtr2WRfaNscG8I5e28PWAIZnNoVv y6zw6FHbIu9W29wMqX3i1gA50GvLh/bpPoUtN89hy7FetqV5AiKGcop1oi0x4w+2eHgQ utlhUJdPY7NYN9tGQ1OstdQzGuAk3o93okS8s9c13nYCsrDcI5XevK0B/IsjFQkZrgC+ 25dbkbDVW+FxeSfaXN4yjwfy018WVgm3CbcImUKSkCC4BYcQI+hEragWVaJSlIuiKATw s71FNv4kPoCKgCwHjoi8yAXwc1DJnsQHpcqDx0RWJCISdYHQJ8C8GOkC+ECfmuYgc5SX cnwAHzwSrjros7E0x0oNakLzkECKCBYJGo/8+IEAj1ZHdxYZi7RjNfllJT+WNEotN9Kk H7+M2OrfOqGm1r/fWufPpJmQte5Gd+ONzI/eO5ZBU0txUtKEKV1HOtvb5pW2OEsbnaUt AI3+9Z2tRn93s91+uK2dNtj9jLuxeU4rvTe1+NudLSX+NmeJ/XCn9NyI5nm0udNZchjN K51ae3ier6Wkt9PXWepsKqk70ly8pOGmudYNzbWk+AfmKqaDLaFzNUvPjZirgTY307ka 6FwNdK5mX7M0F1186YKa4qUdwJ320gUT7P6EGn/l5Ppav72priSAd0NlyTLEnUZq7hRK 4LqRmU1DNoRC7wNcovfgtNAV7jxSBxeF/ocpgE09ToEEiwrRafQA2okOIR7thXwCmoW2 owu4Dc72TNSH3sGxKBV1w7kPoIno9zgUeh3NQ09D/w50Bm1Bh5ESnlmE9NC6EbtCd0PZ B/lmtCr0JIpHeeh+dArlw6gbUX9oX+gItE5B09B+dACe/x12ksNsVOi50GUkoskw5ipo eT00MXQIaVEyKkaToHYVehG7mEuhVmREBYDdY+jXaBf6Dfobvhf3hVpDnaGLoU+BVY3I gmrgtwL34U+ZQ+z9ocdCfwkFgRIJKBFmbUSb0VMw/iH4nQbRWopvxx14M95CfORe0seu 5gzBAaCDF5XDrwKk8lqgwHF0Fv0dfYu/IkZGzXQw50I5of9FCjQBVklX0oI64bcGfhth TScxj9PxODwJr8CP4C34TZJIppFaspzcRa4w1cxMpot5k13K9nIbuO28Ivh16GTofOht ZEBWdBtaglbC6s6gi+gq+g4zMJYFu3ABLsaz4NeNd5LjeBc+Tibh0/gi2Y8/xp/hr/A1 whEl0ZMk0kE2kwPkDHmVWcBsYR5lPma+ZsdyhNvFfc67hA+CzcF1wVdDBaFPQ/8EESsi B+xMMapGs1ETrLYdZaP/gFUchN8h2LWz6By6IP0+wxbUj/4JVEBYi804E1fBrxrfiufh BfhxfAJ+L0q4fENgI4iMaIiBWEgNaSaLSDd5m3QzMUwiM56pZw7B72XmHeYac43l2ChW z5azlWgDu4jdAb/d7F62l32Ny+fGctXcdK6bW8dtYOZwr3Pv8Cv5jXwv/xX/3yAWJwp3 Chtgdy4Az/4GePn7i8XxgH0mugPNwSW4GW2F3diFm1APcNdcvBbo1Y4SQg3MSqacpAM3 vIh+Ady6A61A65iZaFfoPWY/ehc4ZSEM2Y32sMXIym2D3bkXpQMXDf583kRvgsftinfG Oewg8i0xZpPREK3XRWk16gilQi4TBZ5jGYJRcqmzrNHudzf6WbezoiKFlp1NUNE0rKIR jrLdX3ZzH7+dPtcETTf19EHPeSN6+sI9fUM9sdpeiApTku2lTrv/DyVOewDXT66F/AMl zjq7v1/KV0n5h6R8BOQdDnjAXmpsLbH7caO91F/W2dpT2liSkoyP+4Ac8pRkKjh8SEEH 9qNxTStAwKJxtEep3+wsKfWbnJCHNsZV2jTXP2lybWlJjMNRB3VQNaUW5khJXuAHPNF6 5Vzn3PUBH2pupLmmmbV+pqnOTxrpWJokv8FZ4jfc/bnx++KNXOmGYY1+4ipraukp8/sa 1wNxabGRlpo2QGlCjR2GJavrav149SASFMc2wJSiG9YJrsY2u1/mLHa29rQ1AnHRlNpe s88sCV8/mlTba/KZpEJK8nHjygIHrP54yi0pt9B7gcO4Mnz/833h+jdO07tx5dlP4D5h yhABMKWAsxLw9NvnSJM4Adk8mrTkoZ45eUAnuOowLHMB4DPOT4BnGJefc1U2+btrbqDR WhJGrrGtpFdmMktKqLgO+jf2qEfDTkF/tdPe8zVo60Zn/99urmkarOFd6q8RbaQbPcQr ftx0I99JlaULVt1qdLbS/e2U9hTKTmPpsAooU9JQnP06UOCTah1+ex1UgDWZPCGAZJNq D2O8sS6AQ6sDqMR6HGxUZvYsaE6mrLagBOaHQkoyVCQ6IJeabC+Dmcsor9h77D2Vc3vs ZfZWYCbWJd2hoaWnLg0oWFMLdEJTYUZfXcxQtqWubjSMk0bHgUege08djNA2OALcpaq0 AeiUngzKlHFPqp1c6+8uifH7SupgF4B9T0+q9Z8Gzq2rg14ZQ5gCxisWGAdxzgScMxKh PSs8Ctgu3TBEXU8PHbOm1unwn+7piemh5y1cDmA0ssI3WBFAtAsleQB3T4Jn4eZ0xEh7 4HA6AK06StNsYOkbHAU2+7+mcO4Q3vDkKMA2V6Jw3s9E4fyfQuHRP4nCBUOY3kThQsC5 gFJ4zL+PwmNvonDRv6awbwhvQPIWwNYnUbj4Z6LwuJ9C4ZKfROHSIUxvonAZ4FxKKVz+ 76NwxU0UrvzXFB4/hDcgOQGwHS9ReOLPROGqn0Lh6p9E4VuHML2JwpMA51sphSf/+yg8 5SYK1/xrCk8dwhuQnAbYTpUoPP1novCMn0Lh2p9E4bohTG+icD3gXEcpfNsQhX0xfjRc DnePELvoZxfMM4eRHCwlTouKST44zvvRNG462ga+Vz3AY5BvZD9DDrhns0vRFIBOcLoL 4J4HUAHPWOA+BmAVPk8hdAnauyG/jt+PVtF6ANq/E8ZeB210HgOUuyGvgDm09A4o3Igd KcGjeQHKdlRHXfkfuciP1P//agbBBD9wcUN1PESyEHgXP3TJkBywU0JESIUiIdalAT8v CunAf4wGH8gIj5iQGcWAJ2dFsdSHla5s8EymgC86D79A1pKjjI75iK1gX+FyecRP5K8K JcIS4VnRIo4VT8iiZRvlSF6mcCs6FReV8yJ0EdUwBgE/B7EXwT9mALuicOxLTAMDBUBU BxC6CEDLkGc+DCAWAEFe+BCdkNYzPekEjMKh6UnpGVkah8YDUMxuDFz/E3fqu3EBtuoa xFGAwtPwR2QC2Qbz2H1ylMZgM4dMLERnio84TlRASKP6svoKSqvqz0iPcugd0/A3QTnZ Rv1xBm0DzmEBRznQIwHd68urj6jXtJG2iDbN3WS5Q6iMqNAQq2iLZG1RsQh5xFgDUcR6 RDYjZkFkhtOcKNO7EqJN3sQAnn3E0TmPzna1sGqgsFr9TVX/1X5UNFDUr81PG8jHGm1+ Rvq4Lp/WaOZEk4t3C0Y2CXNmMQmjJAwhh3vuwQ0NOHNUbk62x+10aIZlGYed+joCL0SH 715Mzq4su2NZ8b3BX+GDx6ozHpy4Irjst2Q5Jot8t3qrFufNqVsd/GhgMzPJOerBhzIt wfyB+rZxs58YbRu4xkXtuG35+ro0T1Ju476NS58FKteHLnGLuc8lHjjsK4jhtuGtHGPD NvZevIZbF8XViMz9Vo1Gz4+2MsrRelksiY01MRmkQJ2hMdtlGSaTzb7L0RYmQFX/4PJh 5aioqF8igRroD8sfjSwGV5Rb5YpxK6JlmShCp87EWk2kWrBAiUNMJsaEZeRGZSaK1EIi mvlMzGJIaFwGqwvVhUlJ4ZRW3NOAG0RscKZiZxzSqLVZQMBRWbzAO+wet0Y9KtfhZGNx tuaM41zv+8Gv/+erD5eOiT1jfvhQ8N0Qeu7zZ0/g8gTu8+Clkxt3B18LngsGg/+5r27T F786tfMP+FlcevFPEi8/BnySCHxCT1OnTzcK5/FEwAbsweW4lnACJiSAd/oMWp4XiCAy mECAUM7I5ZgXCUPbnudYs1IQaS+5DJkUyiccne3D2YVyDfBIvkSywqJCyLJrUpPWrDiX kY5gkVFZWJOlcWL4e+yv5MqpjwciXySjuVPX6tnd341jn7l2G+BHz11j6G32G9jLNPCf g75Z3kiP0+3OVeU4yt3N7rtVy+Nlt4tGlcFF6lStqv1xjFw1Oi4+Ts6wFuP9urS0JMto HcOOTpKlE7lK1MTH2RLS0zVGl6FSdCWYM20uTSVypZkyMp9wtK2TFkAZ/fsN12ry8ykM 23iN1pCfOpDVsFjigKqEVI0NicRN3Cku3mV2M8koCaWkSjcuEc6DNcqWhGL0xiRsMuIU NgnJPIok7FLgVMgLXkhitRZojIYEYujADmqJM6QscAe97rnnHqAaHBfKEPREpWG3x52T HZ+VyeqdkHXG8XqdIdpG++h1rBPYZRTGsUL2nO/aZ/ZOmPjk+d9O3oC11/6Mx52MzLjt kn9HfcHFV7dM3hD81V+D/7VzJ0Oq8KUV1Q/bxz5xV1amKyU5Z+axl4Iff91ZtPSR5oWZ 9vS0uIL5Z6++sWH9f7GgHjDEfhB7AfZIQNk+M+ZjkUBYUWYwmNE1wrg49hpvEjfMMiZV q69WXS0cKLxaXdpScgXISDkjIx3rHRqnxpHDXghqXglquFOHvvs7pzoEWw5jZwN/5sLY PHrQV/IQfgITH56KSTTGd3FXMJnPtnJrWcaUQFxahmGRC/iUwxxheAYRjhVFwIIlzOMc wo/zJmEjYGECNIxVA/n58GeSMDGiosIiA/AlXlMFfJlqpDFgnxJDyIWBlwWY8NwacYX6 rJQAvg2oYfHiJTKShZ0Yq/FuvOvjgS/eHPgSWNbKfvbdOIldQQZPCX0oRaQiIdZYiP7o y0tMx3K1IkZp8WRVqBfI2tRCvqhVypiYTCFeZlUrrQVJJNVbcKyAFGQmurRqgRMtnjiD JYB7fE6D1SZ4rKkKYs1RFAqFhRad4E3cG28eG+O1jI/05JnGjH0Bb4PNOI63opvY9/LA 2SF5VdQPUpuybQMcydT+1H4qvjWGsARPyB2lj0PY5MK5kQ5kjI1xoGi7zoEdcWgUcSCz 1eCAzYKE8iYIrO/ZsSFeYscxWIUjMcgoPc6lzAm8CKLdORZnZQIvanTQCaZQgUTzuD30 BnybOyoKq5ZUz67b6mjNXNScUYP7xuqV9939QIFDvpf7x1OnOpcZXMpYTWKyuyExWjbq 1V9uOXViW89r9cmVuzfpLbwqwpI2Hy8Uk40pM2smJta8tLOiYvvANkscw6xW8sVOX0Xb 82u3PB2FL1MZ0hn6iHVxZ8BaiEXtvtTdwh7LuxYmToyMJWB2GKycoJHHWhUKnUc0282p 6lTsRRpQAWscpxpuCLXLlyWhgED+w58mXxOmnlEbzcujeZ0ba+WQ6AWDG0fJYt1ALJDw 9NRGZWkoKbQaHZEooHfGh4kkHdiszkMFTze+/O03l+6empm/m8zbtOmBXxx3l5/hzgz8 tWpysD94NRj0Fzir1q344sV9Hx19fdusw5JchCgsc5GtlqydPb60PSa83bhX3G9kxoua nTqG0fFWsxBh1SlihJgYg9qjxYyHaMxWucdgssCrKeGIY8mKQY6BlRVW9efnU90+TNBJ 8i0bmUSXUi93I1WUGlZJdZsJSqDbHJJuU0RHuEG3QSIz8m6q2xw/oNuo+GpA0WHNBqwS 5oosyg4kR42yBPLOZ4ZD6iUrnx2fvvbh9vtMh2L/++Qb32HtWxa22v/unPv2Lnpi14fr lr99DmddgRDyaA72NS90iemHfVUgK1ruyxylKlfNUO1h98VwLlFHIq1qJFqtQpScWA0K LjUqVe3VaM02hcdsirWtcSwpHr78gcuXR+6t2WiRyRHGRgWszQIJMhE3kseIbligtLuw Ki1lb4npeT0yRBuoVsuhy0I52dqsbx7etWLX7rvX7sM9NeljDj5Z9OydR4LfffURnv3F uxd+99uLr5BR2bETiPW7sVvm1OKU7/6CZ4AMqQCr3QxRbQu8AXFhpa9rm/ioeY+N4VQk ktPpVdpIvc6n9OlErxlPUBxlzuOXmPMx74nvy96xvef8wvCFU3Fec15LZoqcIz5yR7Q1 Pp8XhGiH1SLIrdEKl7DNssdyDM4A64qOdFk4k1wpaFSeSKuHM3viUwWPyeT2vOXYHWb+ qoEw6781kA9anCp1YJOGIT4B2a7uh1qJW8qQk+UYeGWAOZa3gcWiVUepdWqWV7riYuLd 4EVY3TjWKjMIbqTQq9w4QuU0O6CKg0Q0Al9FqCGR1KAkayR5k5iUeA9e3IAWN1AWohrO EQtHippGKgyyhpeMJZRFdSIoQrBf+t7Jy9Wqr3/FPbTtganpusPCrRlTum6Z8nLwL9j4 J2xTJIw/+Mu9HHay5bdPm7xw/JNPnWvILS/YlDrJogb5zoMWKA66l5Xde6QH0xfzoJMs IEgM3BvgZVT5kgQrL7cyOFKXHx3Ba+UmUDiqCI3XoBW0kSqbiqiu60xG03XH/JVhFhto yD+bRgk1XAkW9b8FRqR2VG5WZrRBnwosw+uz9E5Qi86crJznnUV9mniDxaSYYu/t692y hSvOnknI0wRPe27j9bnMYxv3Al4MGhMsYL4AXrGhFHjTdsxXlaurFCtltWKdbK1yX8xe 6z7P7qTjMQqfyETHeVVn5XGgUljeazXJtVZ5ZKqQmspZmNTo1BQvZ05XqjwRY90eiykt fdgBudqfT4XfwOWvYZ8HJQRIQWnbw/ue7Ewwxyo08S612xnrdqMEMyQahcqBIlXKCJc1 zo09MV6QE0otGA5hRRJWJWHThp6cnCwN+ASOOLcna9DQkbRFvAbEA5KUiiQ1QMVg8stZ WTm7C9uDFw7+TXUswjPmvtd8biZ3+4rngtewcAKXPP0fL5a5Nv/yzK3JwdfZ4rHOcWuu Z/6+89LOZyo8hQ9P/+OUSf/AVhyBU4O7TvfO3vH8qUNzVpEUaZ9XAVGpTIlGNb5kODWi QTCIHtYTtUxYJopRESRKj5DGygt6pTzCKzcbsd6Lok0GI3yxcMTRHJYpQz4TNTKotsjH 9IBIygB0ZFgxOjXUM4JN1zhX9fmyZtz7ZU3K8diMNe1H+0D4fzjZkf9U3eMDk8lTnaNq d7wz8DLlQwJvMxEuAGOD+qG5PovwOQvMyTNyanoB33oFBgS2bP/3mJwdKDw7xHZFkt8I IkoDnLbqGFxs4rV3uFO/p2OHLgUn4TxpbA31ZWvAZaWsPx2+7cDUl6U+LtxlF+Ftrgo6 aNJOAB5I8mkxMC0ei3NA/sEugpL34Ly+vuCTXRl97iJ/hNXG9l/8Npt1zmSPXhu1bHQz oQ/C4N1A73/CnAq0xze3juDRIjYRONQGfgY3n+vi7xLWcMeZC8wlRs5xPHy8IGPIKvII HASG5GtlMpaDF2X8Ii3slCjAKzOOl4kcdbnAPmV4ucDLeXOEjMi9SGFSRvQ6mo/j6LCl RDepEAzDK9QUBBeliFpIGIAahGD6/QYcFmNSA7dCfVotForUaKUiaAkodJwlAzEhaJzd B/GrV4Lz8OErwd5tB7lT1w/g88E7B5qJpSd4h7S+dbDIMRJNvT7gHFgFbBTsE2LAqx+2 TeBkDxrI4NjDBq3r66OBAWkM2HPexZYjN1rtKxBEQcVHGkSDyhDpET0gtitM0xXzFUqn S262Ok1ywhpcDqvBGsELiI+xuJgoeQLMqfHCBxy41+yl361AaAGnuuBAmjwJARwxnHEv q6/2Xx0YRMZQSA32fpDvQBo4/De4WD/IxYYbVh4w8yAvD+PqXl923eLu6uT4widb3qtO PHl7Vdujx8ze9nl7+ti07bfGjymKL5te89jUjQOjyBe3T9q4e2ATObkoc8Ljr1Ful3id 6QfZRmM6s3wZx/jzPGF5He/RdfIdAqdTEp1RDdYb4o0KuVkwm5HSKzNbcKrRa0KmGDCh bzqSYTUWlmCwrv7vjyX1R/XDlkLPJegVFYaziVcdmLi/9fKk5GPW9JU+7/i8lJg+vAfw nzXl1zOepOezuXBuRHRxzuIFA68BsnAiCkLvsw6wzZQQkTKhh3xZ28Wt6kejn2H3irvV +6ID4sviu+znqi91ytEibzUKSqtWYRJMJj3xRJpjZB69yRwTwDKw0AY1cNghHZK9kqpN hrCdWxElA22pIW4sGCDHRUBOrlO64bxCIkaDQcaoIKFGu5QkgckSr5ViMtRjjM7SQpiB OMBakYywT1anTzzxzNatT8GHG9eD//hj8DrW/pnvwJG7t8565HrvgcvMpeDfwCQdCD6H k66D4e+jdlhncBrrgqWrUBzq8CXvE/cYSIJot2hUvFUvRPIqq0URpyIeozleDta1wxsX aXLG/6B1LZnXGonPILxiiY5BnNnNulEMLIyLhgSbVG7EGKQ1SSuiNja1qMN7Rp3gLJwV 5k94mU5FEbgdGid5aY+r7MTJUhekwdRDub7bfnE0eKxjR9eU9IK+rjff6J55+OTcHb+c sZs5vLEyoTD4Jazxya2zc2IrB/5I5RScY/IwnEENutXn9jDuiFFMOcuqRDVRyTQypUek bKiRi+YoTO1MZNJGBXApHKywCQDCBtiP+r9VRWcHzlIrKhxDozpBYr0hGwDO/gH907dz Rqs6Rr32YTgqx3N3EuZFhhxaMrCdnovi0LvMUXYC6Ps0nOp7ME+2nduqfVS3Xb89kU+I d3lyHWWO8vhyz/T4GZ558fPdXcquiC5Vp7MjvsPV4d4duzc5igHzi0thU6OQWR9jsBj1 KbrUhEjFAtHtynURV1yEnE2KMr5ksUYJrDV1R5IiTZCp1ERAaY40s80YbfQYxia4BU+C OUNl86jHIk+qKT2jd8hmBBESthny1ZCjy81Pg3QwkgIxNEmkhEMoE3EKceshdOJQ2RxI Bp+sYYieOCBGBTmrFupidEYHtkfGOZAjThUheuQO7HbJ5BBNccB3ipDEaiwOGkEJe6nh gJoUVQszveSzIohHNkRJpoXELoMhFEkbC/8/hhLWYV+JrpK9c7eP8Sx9cN0tHR8c//vt 48h+zj320XkLShOql58pXvD+R1+dF/AxPKk+fcaM20rjwdqOS6y8Z/sLG+tbx2SWV/vK Ek1R1rTk0kcevPj+E+Rb4CVD6Csi4+pBOkx5PiJVflqFA7jI52Kj8w0Mr5JrzCCu4esd L9Kr9JGMjSHM9WiTyQz25KDHNsKeTAtHVfrVA5cl5UGtSHoObvjd7hxqUu49euCAW58R EauzjfOsrN+0iasPvr15oDQvSoHJRpl4z3xybjPoG4K6Q59BhLxSiqjP8o0O6F7WEVmU qDNFmXQJ/HLmXVC2iFPJER8h50B2GQWjEdzAVLlXqTCbsZci+8YNC0QKn1L2h+0P245F hZQhwnFAHEYUjDrqOI2SbHkIbGlcOM+cft8LJa6+/cSZPX/z5zUp+BALkecp2Y17639F VNdef3xM4tRHp6wj75np+YSgFPMXNg2BDeRLLcbnMEHzUStpZebza9i13B60l4jwVRYp Zcdz97PruPPsy5xYmbA0gUYxQdRKpjqEgAKh9j5wXuxsAN93jGEWaQkmEH2/zxfLg5UB M3E8y+DBaBOYHnIabWIOkROYWmarjuBDvMlUTYNNn3wyMDLUBMvW5gtgXqirL1cJ4VvS hMldPhfxSqEs77BQ1o3BwZg5BKGsoXF/KIjFCeok+AMTBdykhsVRMkzjVB/iWJx0Lrjw dHAZm3Z9O9N67XWgEIZ3JojbBTkltvtWlrP7ZbD9uEyoVKxhesTV8lfIWeYl4YL4kvyC QjFPaBNb5AsUnUKX2CnvUqwWehRy2peUM8vRXRwzIyE6AXQLW4AL2AfxgywvYzGjIGCI KTkEYWMFI8hVQCOB43eKDHtWTmRnFQjvVJoiKM3BABuKzI2Iz4HxAQE6iFpRCik5oI0A X9dplUoFt0adBH+wXX0y+FZKHsDrfVFaCEcIPMvRjrwgE2Vy2Nn1PpWWZRmFEpYtPRoO 96lXnDVy1LwDO++clFkDsb6hGmrqLV68GKy9GJIVQ2mpAHK+++rrr7zxQV/wwslLb54M /g5I2sdMvH6cKb/2OjPm+m+BoIN8+ClkFSiHWtBgKVNTmgHgAWRgMROIayLghBPwJuxG ThzMpWdEwXwGsC6pgRn75TfffhDchruuBL8JBi/jLjYtuAZ3cQPXBj7ADwfvIC44rjCe dIVa4FvCH7rsUMnAuzIX2I8ZKBeNgq/2SlApKpO+UqyED3Ppt4hV8B3frdL3klPgG8hp aDqagWrhnV89fFdIL8o1WMrxMBaaWF5eM7U4qaJlYWdLx4I5TVIPqRmStQCPADwNAJ8O w5eACL0FcBngKgzFAugA4gGyAUoApgLMBegAWAXwCMDTAH0AZwHeArgMcBUWzALoAOIB sgFKAKYCzAXoCA1eMBcaymNkH1Gm3t7wdni/d1OZWt7D2+lbwOHlmhFlmP+m9ukjypRC w59vHlGeM6IMa7mpv7S3w9Yzb0T7/BHl1hHlBSPKbSPKt48o0+8Xh+Mr/R/EsPmpdzO8 /c4R5fYR5SUjyktHlDtGlJeNKHeOKC8fUb6Llv8PdO3s3wplbmRzdHJlYW0KZW5kb2Jq CjM1IDAgb2JqCjg0MjAKZW5kb2JqCjM2IDAgb2JqCjw8IC9UeXBlIC9Gb250RGVzY3Jp cHRvciAvQXNjZW50IDc3MCAvQ2FwSGVpZ2h0IDcxNyAvRGVzY2VudCAtMjMwIC9GbGFn cyAzMgovRm9udEJCb3ggWy05NTEgLTQ4MSAxNDQ1IDExMjJdIC9Gb250TmFtZSAvTEdH U1RCK0hlbHZldGljYSAvSXRhbGljQW5nbGUgMAovU3RlbVYgMCAvTWF4V2lkdGggMTUw MCAvWEhlaWdodCA1NDAgL0ZvbnRGaWxlMiAzNCAwIFIgPj4KZW5kb2JqCjM3IDAgb2Jq ClsgMjc4IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDMzMyAwIDAgMCAwIDAgMCAwIDAg MCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAKNjY3IDcyMiAwIDAgMCAwIDAgMCAwIDAgMCAw IDcyMiAwIDAgMCAwIDY2NyA2MTEgMCA2NjcgMCAwIDAgMCAwIDAgMCAwIDAgMAo1NTYg NTU2IDUwMCA1NTYgNTU2IDI3OCA1NTYgNTU2IDIyMiAyMjIgNTAwIDIyMiA4MzMgNTU2 IDU1NiA1NTYgMCAzMzMgNTAwCjI3OCA1NTYgNTAwIDcyMiA1MDAgXQplbmRvYmoKMTYg MCBvYmoKPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1RydWVUeXBlIC9CYXNlRm9udCAv TEdHU1RCK0hlbHZldGljYSAvRm9udERlc2NyaXB0b3IKMzYgMCBSIC9XaWR0aHMgMzcg MCBSIC9GaXJzdENoYXIgMzIgL0xhc3RDaGFyIDEyMCAvRW5jb2RpbmcgL01hY1JvbWFu RW5jb2RpbmcKPj4KZW5kb2JqCjM4IDAgb2JqCihNYWMgT1MgWCAxMC42LjUgUXVhcnR6 IFBERkNvbnRleHQpCmVuZG9iagozOSAwIG9iagooRDoyMDExMDEwOTIyMzI0NlowMCcw MCcpCmVuZG9iagoxIDAgb2JqCjw8IC9Qcm9kdWNlciAzOCAwIFIgL0NyZWF0aW9uRGF0 ZSAzOSAwIFIgL01vZERhdGUgMzkgMCBSID4+CmVuZG9iagp4cmVmCjAgNDAKMDAwMDAw MDAwMCA2NTUzNSBmIAowMDAwMDI1OTE5IDAwMDAwIG4gCjAwMDAwMTYzNzcgMDAwMDAg biAKMDAwMDAwMjI5OCAwMDAwMCBuIAowMDAwMDE2MjE0IDAwMDAwIG4gCjAwMDAwMDAw MjIgMDAwMDAgbiAKMDAwMDAwMjI3OCAwMDAwMCBuIAowMDAwMDAyNDAyIDAwMDAwIG4g CjAwMDAwMDk0NDQgMDAwMDAgbiAKMDAwMDAwMzEyNyAwMDAwMCBuIAowMDAwMDAzNjU0 IDAwMDAwIG4gCjAwMDAwMDM2NzQgMDAwMDAgbiAKMDAwMDAwNDIxNyAwMDAwMCBuIAow MDAwMDAyNTg3IDAwMDAwIG4gCjAwMDAwMDMxMDcgMDAwMDAgbiAKMDAwMDAxMDMwOCAw MDAwMCBuIAowMDAwMDI1NjUwIDAwMDAwIG4gCjAwMDAwMTYxNzcgMDAwMDAgbiAKMDAw MDAwNzE1MCAwMDAwMCBuIAowMDAwMDA4NTYzIDAwMDAwIG4gCjAwMDAwMDQyMzcgMDAw MDAgbiAKMDAwMDAwNTY2NSAwMDAwMCBuIAowMDAwMDA1Njg2IDAwMDAwIG4gCjAwMDAw MDcxMjkgMDAwMDAgbiAKMDAwMDAwODU4NCAwMDAwMCBuIAowMDAwMDA5NDI0IDAwMDAw IG4gCjAwMDAwMDk0ODAgMDAwMDAgbiAKMDAwMDAxMDI4OCAwMDAwMCBuIAowMDAwMDEw MzQ1IDAwMDAwIG4gCjAwMDAwMTYxNTYgMDAwMDAgbiAKMDAwMDAxNjI5NyAwMDAwMCBu IAowMDAwMDE2NTQwIDAwMDAwIG4gCjAwMDAwMTY0MjUgMDAwMDAgbiAKMDAwMDAxNjUx OCAwMDAwMCBuIAowMDAwMDE2NjMzIDAwMDAwIG4gCjAwMDAwMjUxNDQgMDAwMDAgbiAK MDAwMDAyNTE2NSAwMDAwMCBuIAowMDAwMDI1MzkwIDAwMDAwIG4gCjAwMDAwMjU4MjUg MDAwMDAgbiAKMDAwMDAyNTg3NyAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDQwIC9S b290IDMwIDAgUiAvSW5mbyAxIDAgUiAvSUQgWyA8OGMyZmFiOGYyMmZkNDBmYjg4NTEw N2UyMGExZDBhMTA+Cjw4YzJmYWI4ZjIyZmQ0MGZiODg1MTA3ZTIwYTFkMGExMD4gXSA+ PgpzdGFydHhyZWYKMjU5OTQKJSVFT0YKMSAwIG9iago8PC9BdXRob3IgKEd1c3RhZiBB LiBOZXVtYW5uKS9DcmVhdGlvbkRhdGUgKEQ6MjAxMTAxMDgxODI0MDBaKS9DcmVhdG9y IChPbW5pR3JhZmZsZSBQcm9mZXNzaW9uYWwgNS4yLjMpL01vZERhdGUgKEQ6MjAxMTAx MDkyMjMyMDBaKS9Qcm9kdWNlciAzOCAwIFIgL1RpdGxlICh2YWx1ZS1jaGVja2Vycy5n cmFmZmxlKT4+CmVuZG9iagp4cmVmCjEgMQowMDAwMDI2OTUyIDAwMDAwIG4gCnRyYWls ZXIKPDwvSUQgWzw4YzJmYWI4ZjIyZmQ0MGZiODg1MTA3ZTIwYTFkMGExMD4gPDhjMmZh YjhmMjJmZDQwZmI4ODUxMDdlMjBhMWQwYTEwPl0gL0luZm8gMSAwIFIgL1ByZXYgMjU5 OTQgL1Jvb3QgMzAgMCBSIC9TaXplIDQwPj4Kc3RhcnR4cmVmCjI3MTQ5CiUlRU9GCg== QuickLookThumbnail TU0AKgAACraAP+BP8AQWDQeEQmFQuGQ2HQ+IRGJNiKACBxKMRmIAGOAAUR+NSGRQOCSK TSeUSmGNeWAAUy+VTGGyxry6YTKcRaLzmeT2ctdoMUAPd8AUAPsEBIABJ+vcAA8JAgAO 4BhQABd9N8AN9+BEAA4CASCvx8WN6AB2vKCA4GAaLAgBAB+vN6xYEhAABQPCEAPFxtub CmfSqSYPDYeNSxpgAFgEGAB2OZ0gAGhUAgByup8gABhYUAAWg93ABxvOCPvNgAMBwK2h sty5AwH4x8uepggOgAJgWzvR8vzOBwWgACO2tS/BYiRzvlc3nQeaYHnybo8jpxjC9ftY aKNidSWFPrxAADeWc+J9eTzQuOZeP5/tw/s/H6drqzeZffk/WU/P+P+hZrQEAD2pU6yc QEa0CI67CBAAFUIP+/0AQogxRQuAAnw0p4HtnCqUk5EIADJEkJOY/7uu/D6HwK974xTE 8VodCb6sUxgAqMBoSM+ryznQdaxAwDKxHwsoAAQAh5gAbhwLOBa2AADILK8sYAHed7gA iCKpIw/T4uiDAFqMe4JBAAANAZIh6LKsLgH4BDHgIfB3gBJQGqGchwAABIOBIAEtoSeh 3rOBAIziADgHwfixSQhMaPolhmAAcJkmoAB8tkAAPBm4Z3G8eQABkFYFgAZ5rLqFQSKM cB2PSgqxHgaTAAUEoHAAbbYAACx/1AB4SAUzB3BEAAsic+CDS87bogWdjAGYeU7hCFTh gCahbgAZZ4qUEZ+nWAB4KgAASh+HqjmorTV1IaZmmQAB7AcD1Lm+2AGA82Z8AGDTdHy0 YMiCJyPMe77wP/ZKULI4ACUan2DOvhqEH5QQAHxQoATTAFHy+lsDxkheHufj+OoVjLtx hByHnDlIAA/liTHFl9NA9eKMRakGSorGORZHnL6ZNgiGHzoIABhoiTGho4AAPpSGZrY+ daehmSO1EJORHEuOk9rIACxrgAAVr+obCg6SZ8+J7bPr2wRYjsXIfsqMSKsryrcAW6uV puxJCkmQo0skjAQqSxJ5viEYefiyUQ4kjrCsayrKqWFsRwm86igbqg6y5TFZSwNg+pSx KcfYHg2uRyHRlYhBuAB5XOvILrcbxwW8AIIKUeJqGypYTdIfR/AOzh6LOEIeiBTUucnZ GNvwgp8HMZwAGCah/MoCS8Asfh2L6fp2gAdYBAs8h1tGBQLqkdR6LcBh9n7iwFfYAgQB nUIPYEhXkcpR3LY2Eq+Hedxox4DxVAAMu55ADvTHkPA4AFQQr6H2OZPQBQLJmH6OwcS7 h/gJLkPgfZxAGHAH+PwpQDgBqgH+BZeIEXBP3AAx9xA7HxEFASUoBA9h1FTACrYBoBCn DtHaWVr8GiClOHsPp34FIPure+poCyXH7PKP2/giDe4oP4hZCxCsWIpMDbK/80YEowMi bw25nDJyDDxjQ0lpbgInH0jHFs+TPAADejoAAEUd44EmHLHsygDU7gOkBHmODGRpSFAA AWRBCB+jeGkAAcwDQMnkjOPYuoBwCluH0PJ9gBgHrDCQEJPyFR2SjAAOmUwAG6lxQ6WI boxkFLeIKA8CwFy5D0VAAQCUkQbA4OG/WQRhmMuGYkWWIRBmFFib8UcfZwAEgJMe4BwS FHCKCLOAxQzzDesWYtG2X5zmMjanAAAE04yEDmG2MsAA4hywdkuXEfYBXBTLXcboAAOg dAlSOjIbc+wAAhn8cRxjzAADTGGu0dQ8oOj9HyWUCQJC+AMAwn4FETZunKYyLmjDMWZk GA6BUrw8lXABHyPZb474LjmADLQEQGTcghAxL5D41KZLubRH5O4EAIGPH2PWDo8h0G2A OCQEzFh5mjHqA8D4AASUUoqYdjMdBvR2jwQceg4jvDJGgnoAgFQMGUAKQQAo/T0gFA6C 8jwGpuIVj2OWPsf5A0CoIM9dwBIOgDAM9YE4KlNMXqac1jIvLAAAA3YMhAHgKmPHUO+k g8B5npAnTigBRoZlWUNNFFaCaaUkAXZsAFOKdU8IKAJ9hxE7gSA1V2vlfaLRyG/a2fs/ yDpzhuOcdZdQBuCH5aMBFLEz2pZ0OS4CHDZ02IMcAdQ4hxqvKkPgeI8AAD1AIY8CYBzg D0q3HZNFqicsZGBd0q4F5aEHAqXAqY+T0jkHKnS6kzAUg2fnTBjo2b5XPHqXWZsGktQa Hhego8MyxkEH4AZYACR4vZAOCIFk/amXaJixm1pWp/F8qoOY2A5B+LAAUPYeLEwBlSma VIfg9iy23LHbq3iaLLHTuAOSztkAGYvIQO8c0Fx8AKKUAlxA/wGF4S3inBhPGMjFyEUu MJBwLANK8P0AyShtDUeyAwA9JB1D8NaCAC5l70XqcOnu9t7z4jczAAB4JZ2lO/S0W4bY yhuwYucOcfgEwAA4ByDus9acfk4YyODPQAAQZ9xiOIc0hwNOfOBQAsehR8DyceA6tLCn Fn0HHpFP6WmLYwIPjLQIAWwFhLEPIeSSgCAFg0A4CJeHI53JQxkY+q8Wl4IOAQdY2itj 4LiO4fBjwQgXIIOpd6fx9XJHoAF3+hgDAaB0AAIQKL4HOwe6vTx6i3Rgd+NoZJsB8AGL EP8fRwB9quSfXUFAQwAA9A9nbVBGc857z6mYnLiFFuBQppG5MYClWbVIlVIpBzgRs3PN 6OUhZGyIKMQgcDzxtD+VIO0fJVgND4W89PDYBwLmtHHbUgo8y4gpB6D4AAN6JnxZSOHZ 0t3GRgLiOAZhixsj0g0ArbB5AHgjAAE6UG/cgRyIenMs4BJr4+jyy+C4E+hGMs5zZCrU ujEMxWX2NMqTdATVIN4Zx3h9AKAGoh9adR60MBMuUHoKEqdJJN0jsRCegF5AoVZr6wKB DYGQYAEAMH+jnhuAMCq8QLW+7L2PnHeyEjn8AlZK6CzL9C6uNoZ/Ih+ANLiZFOj7ClA9 CYEJM+5u/RxjN5chvIQAAV882ntlxd86FYg4oBmp/Nbo776lSbKvPGt7WQYso0xkDQYm P0gh6T08SBcAAGgJOw+sQb5n4RCBnfH8IkdwHaCpDjG6ZsDwICrD7H8ekBIDnwU5598U hvZPNZ6T1eCWl9/ZUDGQpbhBRqdzsH5huO2dAX1o+55hn/8wANH9sQfMvnQKwaHEG4LK 7SLc00cCH4M2lwNysO+2/sIK+88u/Au+/GmauKkcjqTeVsYmg6YUKMOAMeA8uzAYIXAc 78pkUsH7BO2g/4TuHY+QHAkgPIHsKcAqiQueAEq6l4X1BCZ2+I/s2aAzB++UicLKHWHO LKS0MecO329PB0IlBG72sxBOfecYAsAsLcGoGkToAEHmhupAKkB+Co8o2XCYIRCc7LB9 CA34303ycUAAHUHMToAcAUSIAQKMAGKcT2AScEoCy2uiUO79DK7EnA1kPQM4AG6vCoWA GKE6FgTOCidU5QK0H6AaX0AkAUM2AgiEG8HCLqAwA+VsHQHSg0CCCY2OAtAW1REA6Sqg sEsI/2JMcQUIm0ccTe+U/nFS6Mn2MBEI6cvAVIF8EoFQT2CEBym0PSHu/cAMASLieCPS Ho1sVCCUCOI8wW9ZFu5tFWsGdJFdDGJ7Gs367OzG+TF6AAG0GUUsHgAEK8AaAWemARAm AqBUBkeNG5DI9XHo9keCcSKkHsHIJqH4BAOG/lHufzB5IHIMJyICAA8BAAADAAAAAQBA AAABAQADAAAAAQA7AAABAgADAAAABAAAC3ABAwADAAAAAQAFAAABBgADAAAAAQACAAAB EQAEAAAAAQAAAAgBEgADAAAAAQABAAABFQADAAAAAQAEAAABFgADAAAAAQA7AAABFwAE AAAAAQAACq4BHAADAAAAAQABAAABPQADAAAAAQACAAABUgADAAAAAQABAAABUwADAAAA BAAAC3iHcwAHAAAYmAAAC4AAAAAAAAgACAAIAAgAAQABAAEAAQAAGJhhcHBsAhAAAG1u dHJSR0IgWFlaIAfaAAsADQALACQAMGFjc3BBUFBMAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAD21gABAAAAANMtYXBwbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAEWRlc2MAAAFQAAAAYmRzY20AAAG0AAAA8GNwcnQAAAKkAAAA 0Hd0cHQAAAN0AAAAFHJYWVoAAAOIAAAAFGdYWVoAAAOcAAAAFGJYWVoAAAOwAAAAFHJU UkMAAAPEAAAIDGFhcmcAAAvQAAAAIHZjZ3QAAAvwAAAGEm5kaW4AABIEAAAGPmNoYWQA ABhEAAAALG1tb2QAABhwAAAAKGJUUkMAAAPEAAAIDGdUUkMAAAPEAAAIDGFhYmcAAAvQ AAAAIGFhZ2cAAAvQAAAAIGRlc2MAAAAAAAAACERpc3BsYXkAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAABtbHVjAAAAAAAAABIAAAAMbmxOTAAAAAgAAADoZGFESwAAAAgA AADocGxQTAAAAAgAAADoZW5VUwAAAAgAAADobmJOTwAAAAgAAADoZnJGUgAAAAgAAADo cHRCUgAAAAgAAADocHRQVAAAAAgAAADoemhDTgAAAAgAAADoZXNFUwAAAAgAAADoamFK UAAAAAgAAADocnVSVQAAAAgAAADoc3ZTRQAAAAgAAADoemhUVwAAAAgAAADoZGVERQAA AAgAAADoZmlGSQAAAAgAAADoaXRJVAAAAAgAAADoa29LUgAAAAgAAADoAGkATQBhAGN0 ZXh0AAAAAENvcHlyaWdodCBBcHBsZSwgSW5jLiwgMjAxMAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAWFlaIAAAAAAAAPNSAAEAAAABFs9YWVogAAAAAAAAc1AAADrMAAAB0lhZWiAAAAAA AABgfAAAuFwAABQ3WFlaIAAAAAAAACMKAAAM2AAAvSNjdXJ2AAAAAAAABAAAAAAFAAoA DwAUABkAHgAjACgALQAyADYAOwBAAEUASgBPAFQAWQBeAGMAaABtAHIAdwB8AIEAhgCL AJAAlQCaAJ8AowCoAK0AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA8AD2APsBAQEHAQ0B EwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGpAbEBuQHB AckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqIC rAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPH A9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwF KwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbA BtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8IMghGCFoIbgiCCJYI qgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9ClQKagqBCpgKrgrF CtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0NDSYN QA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/s EAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMT AxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJ FmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0a BBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1HR4dRx1wHZkdwx3s HhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1IaEhziH7Iici VSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9Es BSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFK MYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3 JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0i PWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5CMEJyQrVC90M6Q31D wEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mpSfBKN0p9 SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR 5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllp WbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9h omH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnx akhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw4HE6cZVx8HJLcqZz AXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnnekZ6pXsEe2N7wnwh fIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VHhauG DoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAG kG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia 1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWp phqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqx YLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7urW7LrunvCG8m70V vY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dBx7/IPci8yTrJ uco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV 1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj 6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy 8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23/ /3BhcmEAAAAAAAMAAAACZmYAAPKnAAANWQAAE9AAAArAdmNndAAAAAAAAAAAAAMBAAAC AAAAEQBPAMABSwHvApUDWgQ+BTQGRgdlCKYJ4Qs9DJkOAQ9tEOQSZBPrFWMW4RhkGd4b TRy2Hh0fciC6IfIjHyQ3JUEmRydDKDopNSooKx4sES0ELfcu6C/aMMwxvzKyM6M0kjWC NnA3XThIOTU6HjsGO+481z3APqo/mkCPQYpCiUOLRI9FkkaTR5RIlUmVSpRLkUyOTYtO iE+FUINRg1KEU4NUhFWEVoJXgVh/WXtaeFtzXG5daV5kX2NgZWFrYnVjgWSOZZtmp2ez aL5pyGrRa9ps423sbvZwA3ESciVzOXRQdWd2fXeReKd5unrOe+F88n4DfxCAGoEggiOD IoQghRyGGIcUiA+JCooEiv2L9ozvjeiO4Y/ckNiR1ZLTk9KU0pXRls+XzZjKmciaxJvA nLyduJ61n7Sgt6G+osij1KTipe+m/agKqRaqI6surDmtRK5Or1iwYLFnsm2zcrR3tXu2 freBuIS5h7qJu4u8jb2Ovo+/ksCVwZrCn8OkxKrFrsaxx7XIuMm7yr3LvszAzcHOwc+/ 0LvRtdKs06LUl9WM1oHXdthq2V3aUdtE3DfdKt4c3w3f/uDt4drixeOx5JvlheZv51no Qukr6hTq/evm7M3tte6d74XwbvFY8kLzLfQZ9QX18Pbc98b4sfmc+ob7cPxa/UP+Lf8W //8AAAARAE8AwAFCAdEChwNSBCUFGgYuB0gIbgmzCwIMYg2+DyYQkxIKE4kVBBZ7F/oZ ZhrZHD4dmB7rIDQhaCKPI6kktSW4JrcnsSioKZ8qliuLLH4tcS5iL1UwRzE7Mi0zHzQR NQE18jbhN9A4vjmqOpY7gDxrPVU+Pz8qQBpBDkIEQv1D9kTvRehG4UfYSM5JxEq4S6xM oE2SToZPeVBwUWlSZlNjVGJVX1ZdV1pYVVlRWktbRVw+XTdeMV8rYChhJ2IoYypkLGUv ZjBnMWgxaTFqMGsvbC1tLG4sby5wNXFBclFzZHR6dY92pXe5eM554Xr1fAd9GX4pfzeA Q4FKgk+DUIRQhU+GTodMiEmJRopCiz+MOo02jjGPLZAqkSiSKJMnlCeVJ5YmlyWYJJki miCbHZwanRaeE58RoBKhFKIYox+kJ6UvpjanPahEqUuqUqtXrF2tYq5nr26wdrF/somz lbShta22ubfEuM+52rrju+28974AvwjAD8EWwhzDIcQlxSnGLccwyDPJNco3yznMO808 zj3PPtBA0ULSRdNI1EvVT9ZS11TYV9lZ2lvbXNxe3V7eXd9a4FPhSOI44yXkEOT55eLm yuey6Jrpgepo60/sNu0d7gPu6+/U8MHxsPKi85b0jPWB9nb3a/hg+VX6Svs+/DH9Jf4Z /wz//wAAABEATwDAAUIB0QJ4AzgECwUABgAHHwhSCYgKzAwpDYEO5hBTEc0TQBSzFiwX pBkYGoob8B1RHqcf7yEwIl8jfySVJaQmriexKLMptSq1K7Ussi2vLq4vrDCoMaQyoDOb NJc1kjaKN4M4fDlyOmY7WjxNPUA+ND8qQCNBHkIcQx1EHUUgRiBHIEgeSR1KGUsWTBBN DE4HTwJP/1D8UftS+lP8VPxV/Fb7V/lY9lnyWu5b6VzkXd9e21/YYNZh12LZY91k4WXj ZuVn52joaelq6WvobOdt527ob+1w9nICcxJ0JHU2dkl3W3hteXx6jHucfKp9t37Cf8mA y4HJgsSDu4SyhaeGnIeQiIWJeIpri12MT41BjjOPJ5AckRSSDZMJlAaVA5YAlv2X+Zj1 mfCa65vlnOCd2p7Tn8+gzKHKosqjy6TMpc6mzqfPqM+pz6rNq82sy63KrsmvybDKsc2y 0bPVtNq137bjt+e46rntuvC78rz0vfW+97/3wPfB+ML4w/jE+MX3xvbH9cjzyfHK78vt zOrN5s7jz+DQ3NHY0tPTztTI1cLWvNe12K7Zp9qf25jcj92H3n3fc+Bn4VniSeM45Cbl E+YA5uzn2OjE6bDqm+uG7HHtXO5H7zTwI/EX8g/zC/QK9Qr2CvcK+Ar5CfoI+wf8Bv0F /gP/Af//AABuZGluAAAAAAAABjYAAKZFAABVtQAATMwAAJ5IAAAk8AAADQ4AAFANAABU OQACI9cAAgKPAAH9cAADAQAAAgAAAAUADAAVAB4AKAAyAD0ASABTAGAAbAB5AIcAlQCk ALMAwwDTAOQA9QEIARsBLwFDAVkBbwGHAaABugHVAfICEQIyAlYCfAKmAtQDBAM4A28D pwPjBCAEYASiBOYFLQV1BcAGDAZbBq0HAQdYB7EIDghsCM4JMwmaCgMKbwrbC0cLsgwd DIkM9g1lDdcOSw7BDzoPtRAzELQRNhG7EkISyxNVE+AUbhT+FZAWJRa8F1YX8xiSGTQZ 2RqAGykb0hx7HSMdyx50Hx4fyiB5ISoh3SKTI0wkByTEJYQmRScHJ8koiylNKhAq0yuZ LGEtLC34LsgvmTBtMUQyHTL6M9w0wzWwNqI3mTiUOZI6kzuXPJ09pz60P8RA10HsQwRE HUU3RlJHbkiLSapKzEvxTRlORE9yUKNR11MOVEhVgla9V/ZZLFphW5Vcyl4BXzpgdmG1 YvZkOmWBZstoF2lnarpsEW1sbspwLHGRcvl0ZHXSd0N4t3oue6d9JH6kgCWBqIMrhK+G NYe9iUiK14xpjf6PlpExks+UcZYVl72ZaZsbnNSek6BZoiSj9KXGp5ypdqtTrTOvF7D/ sum02LbLuMO6wrzHvtPA48L4xRHHLclNy3HNmc/F0fTUKNZf2Jna1t0U31LhkuPT5hfo X+qq7PrvTfGk8//2XvjA+yf9kf//AAAABQANABUAHwApADMAPgBKAFYAYgBvAHwAigCZ AKgAuADIANkA6gD8AQ8BIwE3AUwBYwF6AZIBrAHHAeQCAgIiAkUCagKTAr4C7gMgA1UD jQPHBAMEQQSCBMUFCwVSBZwF5wY1BoUG1wcsB4MH3Qg5CJgI+glfCcYKMAqcCwoLeAvm DFYMxg05Da4OJQ6fDxwPmxAdEKIRKRG0EkASzxNeE+4UfhUQFaMWORbRF2wYChiqGU0Z 8xqbG0Yb8xyhHVAd/x6wH2IgFiDMIYYiQSMAI8EkhSVLJhQm3yerKHcpQSoKKtIrmixi LSwt+C7HL5gwazFBMhky9DPRNLI1lzaAN284YzlcOlg7WDxaPWA+aD90QIJBk0KnQ75E 10XyRw1IKklISmlLi0yxTdlPBFAyUWNSl1POVQhWRVeDWMFZ/1s9XHpduF73YDlhfmLF ZA9lXGarZ/5pU2qrbAVtX267cBZxc3LQdC91kXb1eFx5xnsyfKJ+FH+JgQGCfYP7hX2H A4iNihqLqo09jtSQbZIJk6mVS5bxmJmaRZvznaOfVaEIor2kdKYtp+mpqatrrTCu+LDE spK0ZLY7uBe5/Lvsvei/8cIDxBzGOshcyoHMq87Y0QrTP9V417TZ8twt3mTgluLF5PLn IOlS64btv+/68jn0fPbD+Q37Wv2r//8AAAAFAA0AFgAgACoANQBAAEsAWABkAHIAfwCO AJwArAC8AMwA3QDvAQIBFQEpAT4BUwFqAYEBmgG0Ac8B7AIKAisCTQJyApoCxQLzAyMD VgOMA8MD/QQ4BHYEtgT4BTsFgQXJBhMGXwatBv0HUAelB/wIVgizCRIJdAnYCj4KpQsN C3YL4AxLDLgNJg2YDgsOgg76D3YP8xB0EPYRexICEooTFBOfFCwUuhVLFd8WdRcOF6oY SBjpGY0aMxrbG4QcLxzbHYceNR7kH5YgSyECIbwieCM3I/kkviWEJkwnFSfcKKMpaSov KvYrvyyKLVcuJy76L88wpjGAMl4zPjQkNRA2Azb7N/o4/DoCOws8GD0nPjo/UEBpQYVC pEPFROdGCEcqSEtJbUqQS7dM4E4MTztQbVGiUtpUFVVTVpRX1lkYWltbnlziXihfcGC8 YgpjXGSwZghnYmi/ah9rgWzkbkhvrHEQcndz33VKdrh4KXmdexR8jn4Lf4uBDoKUhB2F qoc5iMqKX4v2jZGPLpDPknOUGpXEl3GZIZrVnIueRKABocGjhaVMpxeo5Kq2rIquYrA9 shyz/bXjt8y5urutvae/qMGxw7/F0cfoygPMIc5D0GnSk9TA1vHZJdta3YvftuHY4/Tm Degm6kHsX+6B8KXyzfT49yb5WPuM/cT//wAAc2YzMgAAAAAAAQxCAAAF3v//8yYAAAeS AAD9kf//+6L///2jAAAD3AAAwGxtbW9kAAAAAAAABhAAAJy1AAAAAMZ644AAAAAAAAAA AAAAAAAAAAAA ReadOnly NO RowAlign 1 RowSpacing 36 SheetTitle Canvas 1 SmartAlignmentGuidesActive YES SmartDistanceGuidesActive YES UniqueID 1 UseEntirePage VPages 1 WindowInfo CurrentSheet 0 ExpandedCanvases Frame {{422, 692}, {961, 676}} ListView OutlineWidth 142 RightSidebar Sidebar SidebarWidth 138 VisibleRegion {{-124, 1}, {809, 567}} Zoom 1 ZoomValues Canvas 1 1 2 saveQuickLookFiles YES doc/next-tutorial/value-checkers.png000066400000000000000000001030211242365656200200610ustar00rootroot00000000000000‰PNG  IHDR €ëš¶oYiCCPICC ProfilexÕXgTM—®î‰Ì 9çœsN’s’EÒ“d0%(J’¤( ("J•$ˆA@1¨€¢"H ÛøýÞ³g÷ßþÙ:§zž¹Us»¦Ÿª¾÷>°y†…Ã4„„FEØéò89»ðà¦P"]Èx’#Ãt¬¬ÌÁÿÚÖžhop\rÏ×ÿ:í õö‰$Y!Ã^Þ‘äßFð9," ¸ ÁOG…!…tÀ,Á'÷°ß?¸l{ýƒ~ϱ³ÑCæô€'yzFø@|‚ØybÈ~ˆâXºPï€PèÐÖ$û{zÀ¢Ì‘ 9´‡Ã,âõ/?~ÿžž^}zzúýÅÿüä—Èõ"Â=üþòy ŽFž×ïF‡\I¡Áû÷¸Á#}ÞÛSßì þÍÙo»O¨½í{¨×~Ë?Ø7ÂÐæ‹Òý¶²ûcó×ÛÿûDüõèijõÇmcÿGÆØüÁqþvް·þ_»o€¡É{@”Éß{2û»,€' GùÄîñô…‰ðóâÑAv™I(YJ‚GNFVfoøÿMÛ;_ÿ,vÅæ÷¹˜Fþc‹Dž©ÚrÆþcóH ÕÙâÊÿ±ñê'¸ÏOŽŽˆùDz•À  €p~ $Pê@S` ì€3pdàB@8 @"HYà (% T‚«à:h-àèÁ xž—`|ŸÁX[á Jˆb…¸!AH’ƒT MÈ2‡l gÈòƒB¡h(J†² \¨ºÕ@7¡;PÔB/ hZ†6aL‚`NX–†U`Ø ¶ƒÂ~p8§À§á"¸¾7Ã]ð ü ž†?ë(€"¢˜P¼(I” Je‰rAù¢"PÇP™¨BTªÕ†êC£¦Q‹¨Ÿh,š̓–D«£Ñöh2:} .A_E7£{Ðãèôú†ÃǨaL0N?ÌaL¦sÓ„éÅ<Ã|À¬a±X&¬0VkŒuÆbã±ÙØóØØNì(v»ŠÃáXqâ8 œ%Î…KÃã®á:pc¸¸ <Ï—Ãâ]ð¡ø$|!¾ߎÃÏá·(h()Ô(,)¼)ŽPäPTQ´QŒP| Ø"Є ;B !‘PD¨'ô^VˆD"Q•hM ž ˆˆ3ÄŸ$:’IäJŠ&&U“:I/H+”””B”Ú”.”Q”§)k(P¾¡Ü ¢§’¢2¡ò¦:NUJÕL5Fõ•š‚ZZ‡Ú:Žºúõõ" 'Í1šRš;44«´ô´²´–´!´Ù´µ´ý´ót8:!::oººJºt³ô(z~z=z2}2}}/ý,ƒ0ƒ C CÃu†a†%F:FFÆXÆRÆûŒÓL(&!&¦`¦¦F¦çL›ÌœÌ:Ì>ÌÌõÌcÌë,ì,Ú,>,™,7Xž±l²ò°°±žema}͆fc³f;Ìv­—m‘]ÌžÉÞÈ>ÅsˆqØpÄsTr q¬rrqq†qs>à\äbâÒæ äÊçjçZà¦çÖäàÎçîàþÄÃÈ£ÃÌSÄÓóÄËÁkÌÍ{‰w˜w‹O˜Ïž/‰ïßk~¿ ¿/>7ÿ’·€…@‚@À” … Š ¿à9Á>Áu!a!G¡t¡¡yaaá8á:áW"”"Z"á""OE±¢*¢A¢çEŸˆÁbŠbþb¥b#â°¸’x€øyñQ Œ„ªD¨D…Ä„$IRG2F²NrFŠIÊ\*IªEê«´€´‹ôYé>é_2Š2Á2U2/eédMe“dÛd—åÄäÈr¥rOå)å åË·ÊWWðQ¸ 0©H¯h¡˜®Ø­¸£¤¬¡T¯´ , ì¡\¦<¡Â b¥’­òH£ª«z\õžêO5%µ(µFµoê’êAêµêóû„÷ùì«Ú7«Á§á©qIcZ“GÓC󢿴¯–§V…Ö;m~moí+Ús:¢::×t¾êÊèFè6é®ë©éÕëÔGéégêÐØ”¼1ä3ô3¬3\2R4Š7ê4Æ›Ÿ5ž0á4!›Ô˜,™*›5í1#™Ùš•˜½330o³€-L-ò,^íܺ¿ÅXšXæY¾¶¶ ·ºkµ¶².µþh#k“`ÓgKoën[k»f§k—c÷Ò^Ä>Ú¾ÛÚÁÕ¡ÆaÝQß1×qÚIÚé¨Ó 3›s€s« ÎÅÁåŠËêƒ>¸*º¦¹>?(|0ö`¿›[°Û}wjwO÷[GZmOKÏ ÏU/¯2¯%²ùù³·¶w¾÷‚†O®Ïœ¯†o®ï¼Ÿ†_žß‚¿–¡ÿb€^@IÀ÷@ãÀòÀõ Ë ê Ý`Çà!ø;¡t¡A¡=‡¸Å K ›W /_Š0‹¸ EŒlb@™¡h‘èÔè™͘ҘÇoÅÒÆ†Æ;’qd.Î0îr<:žßÀ›˜0sTçè¥cÐ1¯cÝÇù§ÿpÂèÄÕDBbPâã$™¤Ü¤ÉŽÉm)œ)'RfSRëÒ¨Ò"Ò&ÒÕÓËO¢OœÎÏ(Îø•é9%“U˜µMÎ8%{ªèÔîißÓÃ9J9Î`Ï„žy~VëìÕ\ÚܸÜÙ<‹¼æ|žüÌüîý… …åçç¢ÏM™µ Ÿ)Þ.ñ/yVª[z£Œ£,£lý¼÷ù± ÚêË9˳Ê7/\œ¼dt©¹B¨¢°[Sù±Ê¡ªï²Êåš+lW²®ìT‡VO_µ¹ÚS£\SSËQ›S×E×-\s½öäºþõÖzÉúK7˜nd5€†è†O7=n>o4kì¾¥r«þ¶àí²&ú¦Ìf¨ùHóR‹Ët«sëèÓ;ÝmêmMw¥îVßã½WzŸñ~N;¡=¥}·#®cµ3¬s±Ë¯k¶Û½ûå§O{¬{†{Íz=4|ø O§¯ã‘Æ£{ýjýwTZ•›‡‡š+>nVnQi}¢ú¤mtßhû˜ÖX׸þøÃ§&OŸí6úÜþùä„ëÄô¤÷äü‹àß§b¦¶^žx…y•ùšæuáŽ7oEßÞ˜Vš¾?£?3ôÎöÝËYòìç÷‘ï·?¤|¤üX8Ç=W3/7oÁpáɧŸ>|û¼µ˜ö…öKÙW‘¯·¿iZrZúð=âûîrö ëJõ…Ý«V«oÖBÖ¶Ö37X7®þTùÙ·é¸9·ux·]´#ºÓöËì׫ÝÝÝ0ÏÏß¹ ¹Â¾¾,W@é ýÿä¿¿g é1’´Ã¦E²‚NÈ ú \К¬!Î…b†¨A*¦‚¨ChÞÓyÓf$3Ͱx³¾a·àhæâäŽàiã]æçPÔ2ÖQUç’ •DK®KÍKOÉ ÉvÈ5ÊW*ä*&)…)T1VUTãRǪÝ7¡Ñ©Y«U¢£“©›¡—¥Ÿck˜g”o\`R`šovÖü´EÖþ“–©VÉÖÉ6)¶Év)ö)IމNÇã]bDº† póv'{øxyE“½Oû”úVûÝòox84<²º† §‹ŒT²Šö‹9zøllõ‘¶¸áø™„åc¨ã4'Øy’ø“ùRxR9ÒXÒNRg2Ñ™;YkÙßNÍŸžÉyufòì³Üñ¼±ü±‚Ñ‘s‹Š•ô”v–Ý;ßz¡©üöÅ–Kí*ŸV½»üíÊÎUB S-Ô5Õëúõûo85xÜôk ¹qûpS|ó±–ÄÖ”;émw³îºº=§#·³ «¤»üAUOmoýÛ}MZûïÜlê|Ü5Ü5ÒñäîhÓØµñЧçže?Oœˆ™ |qpÊú¥þ+å×bo8ÞR¾ý5ýefê]ïìÍ÷¥R?šsž×]ÿÄðiçóÇÅÇ_š¾žÿ–ºúÝiYwEòë*fuiíÕúÛ?Ë6Ӷ¶]vô~Iî2íîþå_…ÓQ6hA V gŒ¥¸E„I^”íÔB4ytú F4S<ó&«/Û MÎ"®Ï< ¼þ|9ü5M‚­B-Â7E®Š–‹ŠgI“ —ò’¶—1’U—“”çV U„—•f”Ÿ¨´«ÖªªŸØ¤a¯©¥%¦Í¦C¥‹ÖÝÑ[Óÿf0o8c4i¡—hšd™l•b•j™¶?Ýü¤I†a¦n–V¶Ú)¥Ó²9’gÄΊäŠä ç  ,(æ+á)å,c;Ït¾œö"Ý%† –Jî*áËÒW”ª5®êטÕÚÔ9^;xÝ«ÞïFhCÌÍã· n_jºÞ|§¥§uäÎTÛìÝ/÷ÖÛA¾“º‹©›ão`¯èCÉ>™GòýŠʃªCê÷ kŒh>ÑÕ37~jöÌò¹Í„ݤã ç©/]_¹½v{sð­Ë´íŒñ;•YÁ÷4ï~x÷q`®q¾d!ñ“ÿgËE¥/œ_Q_?{ºÔõ½a¹b¥ðGöjâZÌzІÇOûMÓ-ÍmÙÁ_¬»¤ÿÆ,JMÄ,Àqãm(N†H¢”‰TiliÓk3Üb’`®beaËd_â´æªæþÆ+ÃçÎ\ @°J¨áÿ–h“X“x“D£ä ©zék2We«ä.Ê—*œSÌUÊTNVIPPóUwÙg¡¡­©€°Ï¯Ã¥Ë¡Ç©ÏiÀcÈcÄcÌeÂiÊjÆdNcAÚ³„-w­¶­·l6m·í¶í·¶:­9/»|=°àúþà[·—î“Ï='¼^_{ÏøÌú~ð›óŸX\šž ™ ?ô%l-ޤ‹ˆVŠ19| 6äHB\v|YBíÑ$šöŸLìOêI¾›Ò˜ZV–~ædjFlfPÖÁlËS:§ås„Ïpœeȥ̣ÈÇ  AáιŸE«ÅK%ŸK?”MŸŸº0^>rqèÒ@Å@å`ÕÐåá+ÃÕ#WGjFj×=¾6x} ¾ÿFÃÀÍÇ£·žÝžjzÛ<Û2×úéΗ¶¥»Ë÷~Ü_m_ïXï\ïÚèÞx°Ñ³Ñ»þp­oõÑJÿ÷oƒ_†>=þ8<;2ýäÕè䨸øðÓGϺŸß›hš¬Q=Uþ²èUÎëô7GßFNûÍ8¿3™Uy/ôþ#ø¸879ß³Ðð©ôsÚbøׯFß–ø¿3/S¯V k„uÂá'Å&~ ·ÝÁüBíB{üÿ£ƒìŬWݾ s¤½` €À?¨°¢ÀNÀü†s´ö7~` 0"Õ¦Rijc` <@08Ò‘Š² ÜÀ8xV!<ÄICzH…xJ‡.BmÐ3h¦…å`8¾?E¡‘šîª5‹Tmè ôGŒ$&Ó‰%a`¯ã œî&ž Œ¦§(! ‡/‰FÄF')ƒ´FéC9AeFÕE­FÝD#Os›V‰ö..Ý ½=ý4C(Ã/Æ3L‚L÷˜˜WXrYX_°`eãˆçäçäŠâæâà9Â+Æûš/ß\'Ð#˜"d Œîɵ£/–ð””Üz$],"«/Ç)·)ÿJábƒR…r±J¾j®Zžú¹}ç5ª5okõhOè,ê¡‘½­lhcjœeRk:döÝ‚u¿¾e„U¥õ„-•±}’Ã}Ç5g Ï®n»šGŒg“ך·šO¢ï ?s€_`[0mHpè@˜DøÙˆŸQÞÑc‡õb[â¤â« «:!˜x5Y<åFšbúý ã̉ì€S¿r ÏÊæŽä‡2ž»_PÊ^öôBÞEç Ê—V—ÕÄÔÙ^—¿Áܰ۸p{²y°õA[÷½¾öñÎÙî^Ú>É~‹ÁðÇÅ#½£?žŠ§}éø¶¹¬ú#aíÁOÊ-—º¿ü3 •É 8oDY:Nƒ2p Q‘†Àkð ‚!FHQ‰l¡@(Ñ„nC#Ð"L€Åa 8.{á?Ê•z€ÚA« cf£ŽIÄôcé±îØëØœî2nEAGE1IÐ$\&RcˆÓ$3R+¢|Q©’¨6©c¨Wh¢hÖhãé`ºLzúr †6F3Æ·LÑÌT̵,Æ, ¬§ØäÙ^²§pHs¼äLçRäzÏ]ÈcÆ óÞã‹åWæÿ)ÐŽœsaF¤¾¯ “[ïÈ”t”–Ú”‘¹"{BÎM^SA@‘F ¥´¥¼©²­«÷±hkªiYiè¤êVéõêÏâÄŒÍLBLϘµ˜Oï'Yª[…XWØLÙ1ÚÛ:ä;>wfq9pà‚ë;7÷0{^²«wƒ/ÞÏË¿#;(1ø}¨ñ¡ká4‡#ßF›Æ´ÆŠ9ÏpúñxV"UR~ gjmºêÉL÷¬µSgs¤ÎŒäFæ³ôœ /æ-yV–uAÿ"t©«2õ²y5óÕ÷µÍ×2ë½49ní4Ͷ Ýi»{ý~uÇÕ®›:zGûæPC¼ÃzOÆòžv>ÿþBð¥Ë묷Í3“³?>RÎ }ÒYtÿš¸tyùñu¡ŸN[Ù;={üGúÊËíE‘tùñÍî\öÞ[»»;•H±ñ €Îà´õ½ÉXÊ8ÀøÙ{ŸÿnÿÒE%ä~õ pHYs  šœ IDATxì} xUÕ™îG Ø ÁB«h ƒZèÈattú(5T­\Âm«½*´äö;´–A+Å0<ÁÎH¸­“Øiç ¹Ñ™nÛH vŠW“Ñ“ŽA C°ÀØPMÄ„&ºïûî³×aç䜓sN²÷9ûœï{òe­½öú}×·Ö·þö:,Ë%E@PEÀo>äw‚šž" (Š€"@T©(Š€" dU@Y]UE@P¤2 (Š€"TevMTPE@Ê€" (Š@VP”Ø5QE@PU@*Š€" (YA@PV`×DE@PT© (Š€" dU@Y]UE@P¤2 (Š€"в’ª&ª dó}0M¥ÜF€·*³?ÃýÊzÃrnWÕéÜ©:…ÚGñpuà ðíÏr_0œ:; 9‚ý}ÔÙ¹ŸkÍ¡.Á© (#àŒ‡ƒ3vhJÁAàld•uV%¤}[êMg@¨$Í¢ïpæ3<Å÷”5Á± Pìærg@:  š>„UäÈšDpp–r¨€8’þppr®9%`*îÛ ².u?Hä0é45‡+G³–¸üFt&Xg@Y©‚ŒŠ“ÁXkß–1ŒþÔJòkM)8èPpêÊSì= ˜ìÛôô¢´«ÊÁJÑ,e¶ 3 Êzf4)#0É©7U>)C–]ª€²‹¿¦ž»%”»9ÔœÅ"ÀAƒ*ŸXTrøY!äpåhÖ²‚—m g%%:Ð'Ǻûpvo¢L›>]8(P2u§Ëo ’4‹¹…@_k­L˜°@ö–±ãû¶ØîÏîõ4Ð&+p¹BMÛ»Q§ñ°´íÞ$&O•™3gÊÌ3dò„%²ý…cãuLCr¸íi; E— ´JÊ]ÝÚ“4ÔPÏAy¡õ $@/iX}LT³Þ4×YD ø“Ÿ’å–?ípåb@žÛ±N¤ì+òÇÓ,, Ê„858è 76ë]«%tÛYS×,G»{¥·«Cv®©¸jæ9¶”zHö„®’ÐÞH/ªI³å¡úzYr¡ùL'Að#?‘«ýDPxòÑYP>Öª–É[&Í— tò;û©DÇô}¯ÊŽ'DVÝs“” “]›V`64ÁæÕ[ž–ãÌ‘K/ Ø%ËVï3—þ<$­˜Õ,°Ã/µµÏFý +ØÐyì®'¤¼¦E[y\PR,ÅÓçÈìêò2 è²½Þ·]–9yY²z‹´õDæv? +6ÕÊ–ÕKì|.X½MDR8øl­+Ì69€Œص^îæëu‹äÝ„áWoÙ.[VLeÛZ¡Ÿ”{¨O^ÿå/åhï Ü-+‡]µ8ø,“ÝzdàÀn¹!.ë俬.CÇåé-«£®Ý¾Oœ¬E2¨ÿóÞs¥¬¨ Dd­šÙç€ç€¯ó[ÆÔ®á×öVCç ý®«©ÏeVs·e…«C¶½¾¥Ýjß_o•Áßú¦£–Õßb] ûÖ–n«·¥~ª-x·ÉýÜQ¿ÊŽ»ºa¿ÕÒI§¼&ìøt]MVñÕµ÷º‡[ûÛwÚq­ªi²:;ö[•!Ás¥…ÜXášrû]yU½µ¿©ÎÎgYu‹O;Ïõû¦Ù‚®µdy½ÕÕÑh-§½¼Êjé솗å•VCËÑÄåî‹”»Šåvp“²õVÓþf«ªñ…€Co§ÕPÍr/·ê÷wXÇšÖÃ^n5†;¬p±†7ñ=ø6ðà‹ÀüˆøCð®ý[cà“¡º”E %Šçß(è˜å‡{;äÖ•Ës?Ú€¾³^®À·ø}eIcË|¹qa‰ `ŠT-±û¥ßÈ#Ÿâí>9ÖhŒ>÷ÈO6=!e˜ÕÜ{ëBx¾RZjeÑã{¥ç˓߾q"rIÐÄ©ò±÷~…Àr •&^Úzõé*ôã;¥³LxèͲ{Úbùé5²èT/ò¼Svó öäì;UI§Ñ%‰2uÖ•²¡«]Ê3QJæÌ”Ï@3]ºTÎ*–6Aø²é}rµ0Ç'&/÷é‹õBÒü“Gä`õ©M5².Ô(‡ä^ù/7ü¡Hè,¹ùÊ9Ò¹ýUĈX'N–yKî•£á+å?3UÊ+t .¯ªS ã³ä ÕeÒÀe¸v{ù­rÕ§íN¾HÞ•ª˜å£‰2yÚ<Ù™}ŽKùÄÉdôíÀiƒÿ½w/Š.?-º»Aä¼³äÝWëd^hžÌ›¾x‡X¥W r /¾aòLÄCò®m²}ß«òâž°”-úTÜäse6¼u½7$܉ ]17º28qÒy‘Š?)߬Z%›o[$Ó&NÉ_|Lž,‚¿A9E§"{XƒxÝx…­|èœZ¹ÃWä2^šŠÄEêçCø”í6÷󛤲|‡,7S&bùðž¾ ƒS+Z;ý8T®Ê4ù‚@¨üt˜?ïnû.ÔÀr¹íŠéÈZŸ<¹¼\ž¸©^:q(À²z¥³†^»çŽÍù@týïø[èÍñ¾h¢}ÿÏš†LDú¥·¿_ºÚ÷Kãß–óþ…ôöâ Í÷J Ž\ÏD5¾<<â¾Y×}òJO‘œw ”Y×{§ß¾‡Ù†ÈŒ)ÎÜ˵±QØÊéë–ÒÏÿÒp³Ô,xCîZ¼U'=ž–N¹‡m‡Î›ËÖ×7Eîü~¯ô÷•–¦òÑ­÷IE4³R^!  (¯ªS ã'E³>#ÕeaÙ°î UVÈ\ûã›H/½üÒ?Y8pxß.©Ø‹\ ˜îÝÉ¡ý¸Np4y¨§Uþâ3›íYŽ],7­Ùºõ'r¸’vJͼ«déŽN,’âb4°‰á0ă;—ËÞu‹e5*ï¾c­²©ü*Ù‹¥¹e—ÊŒlÃ*yš§ ÷l^…e»5R67ñlb¨³3¬òc#lþ5rãg¯Šdºe;¿wºâŠH±Ü‘˜Fü·á°ÿ½#]}Còê.–‹¯«“îÉÈÂ%·È§—¢ù€€nÒé&¥ÊÀi@›Né‚Ù ï¨_co»´ÔEÜ6ØWY•kÊà'díï Û›ø[Ã}~ÔªYnoœÛáËÊ`/«±ìã½ak½}XÀyª´Ÿ3è·šk\éÙi®±š;û,v[;×ðP„I«ÌªG6óÃÕeV¨ ‡:ýÜeÕ­r‡Á€úvÛWKÍr;.†;í?Áhåþ[”»7Œ8t`ŽMD%”[-ÈnG}$ŸxßÕÙd•Gó̼/·š¢e29>m:åÓC9|àµ5â@ˆþÚ#$WI0àè4OP}|¸™ ']è9Ž£ÄER2½ó–!éëë—ɘ¹DØIÏqœR˜T"%ÅÃß𛛞ã]2TT,%˜Iž‘œv8ÖÝooÚϰÓî£ïø1éÆJÜ´™Ȉ¤†{>õ L/ÂL6cXþ¤hÒ¤¸yJ­ÜÑ$FX†÷PQ‘LcÚ(ÇÿV'N‘^üvyÝîc=Ó>!ÿêN`ä*©ÊÕšÑ|eñR@YÉ|'ª (˜•¯{@Á¬7͵" (G@Pà«P  (Š@0PÌzÓ\+Š€"xT¾ µŠ€" U@Á¬7͵" (G@Pà«P  (Š@0PÌzÓ\+Š€"xT¾ µŠ€" Q?°f±4׊@ÆD®ƒ‰\ÃH,|ähfŸqŒÐÌu±õçGÚšF†¨Ê8 –÷ð —÷Á+À³Á烧ÏW»Y‚¸žç+]‹‚ý£±~~æ-«¿³¾èn”¬J¹ŒÀx5¤\.£æMH£|ØÁñ N‚Ùѱ½ðÚçñj7ü¶wÀùJ“Q0¯ÊÇ:âýÙï:Œ‹ðìºR% ‚BãÕ‚R^ͧ" ASÑðW|ØÁ½ 6ʇêxîòâÌ|&/ËGÄ;®üyVÛÎ:cÝéL ä:©ÊõÒüù•GÑœý°s£òá–²³ã,ˆ³Þ˜=^ô›ñŠ(Gãñª|f–Ê*ëÅ(!΄ŒÊQH4[U@ 5 À_˜Á™* 3ûál‡Ê‡KIø8{&4ž3 ×g>“—å£â@J‡Ë¤œѤÛ¬K˜J9Œ€* ®ÍZÖ 2Ë;ìä8ÊfçÆ™g?ãy*îMÄ—ÏäUù¨\Ìl•uÅë‰ÌgÖ›RŽ#  (Ç+H³—رqÇtpìܨxŒòOô[Ä›ÏäeùÜJÈ Xoïcò£ (R¥?H€JÒ,fçû*.¹Ñ4ŠÇ˜cÍ—Žx¨!_‰³.[zET@$£ˆ¨t¸òfÜí—ú/wP”»u£9Ës àØWŽ—2Ë9´¸—†òqÖ¨¤ÄE@—à⢎AGGù޹çûH}<k]$5ÿqPuÊ 8½Èu”@')Oæ’’åŒS•b’ óû•* ¿×ôA ×:ûS†"É‹©£äøèÛà" § ‚[w^å\eÂ+d3ŒWP†Ài°œG@e;ç«È÷ ªLøyòµB’ã£o†–Y> >lŸ„y|uÀŠ Ùg ç€[í{0ß×s]†èP†Ài°œE€2ýað™NÏÉÙœjÆ|A{Qï@éÌ@bSÀü.©Ä—„5‘QÐШ©‡ !€Î&Œü¾éä™W²ì Rþ5¯ž!ðbæå²¼ÝüÏRшÓB@PZp©ç€ ð]ä“M¯hQRvÎ~xUÏ^…#7Лr£4㈖[¸ìÆŸQ¸ èqŒZ£ 0‹dÿYÈĪ#¯²® (¯ªS £(Š@pÐCY®+ŒÊøµ>—‰ô«ý,×EÉó+¶! ªõÌÑðSY ¡@¾SP”¥:w#•Ž}Å¿ö_Yªˆ1$ëÔ!OÛ½»þ@,œ¸ß¬²ž£ :»ÚÀìüÀ´>öÕCÙ«y*þÀ™—×Õg¯t…“2NJè 4@mOñ븨¬ÇÇ&\Ým ­•e¯ú9d£ä· JÁE`*²Î£½\ŠHkôÜ"§žsg„LY§’>;õê3@ð»;åŸÛ l )‘* ”`_OÎH™’³Syã›ˆÆæç"!6<ïåwGzì ¸ˆ#bö3”õb—»Zóž:¥Ü³ Sˆé’ÐÊ™Q¡* ,UÀ8%Ë— 8›e* GÀ( ³à¬²>›|y2m€´–àTeOØYéºxö𯔹¬dïÁäŠSZ p¼2‘Ãñö3Ä(Ÿ~<‡«Àó¬E÷€RZ:%-Ïž£p`£$›YPá”<ÿJÊ‘½™ý¨òY¿F©¬Ä&_\¸¼ÊÙë8­6  ({"àn˜ÙË…¦n7z¦*“¸E2NÄ_úQʆȷWÈÔêF9ÚÝ+½]²s½ÈÝ‹o•g§^üI^'õ ×rônHö„®’ÐÒ½4=±\صZB·m5uÍÃò_qÕLÙsÌi€åÄ1¤^F~zÖ›Ž÷ó« (Ç*dxvfË'Í•9sçÊÜù åÎ;—áõ^ééΖ­Þ¸Ÿ{„åå#ø9³Àúþ!ÙtÃ";êEW®•¶ØÑÒPŸ¼þË_ÊÑ^Ä{p·¬X½EvÕ>à̾–Éî#Ç}­OÉÝ{Eê;vË®œ#ÅÅÅrÁÜ%òhG½”AÙAeª„Ÿýß²iõ;®%«kåð€SÂÌžXIcÉ ÙåšÙÞW‹Ñodö·lõ6‘»] +—W¼SÙ_ P—T üYàKÀׂQé7\ƒë,BVuC“ÕÜÔd55î´Ö”ኋ²j« ^{[ªñ¾Úêv‚¹Ÿ[¶Î·¤êW–Õßb!rkkË›VKý‡¬ªúf«{pxZV_Ä_UK·I—鬷šö7[Uå°‡ª­Þ˜ -Õe¶»I?æµý®YŽ4ÅZ^Ý`µ4ï´Ê`/«nÁ»n«†e ­±[ÂVCU¹í¯¾sé×EÂÔ4Yí- Ör„‘ÊæHyC5V?J_Ͱ²Æ 3S]MVˆñVî´Âáfk}ˆñVÙ…k"ñÊòJ«¡å µ“e)¯±Ú;;¬†Jäx´ôÇËyjn,ø6ðåà Ágƒ?„ÐÚ¦ €úÏßÜÁöH½£~ÖWÕAÃÖQ—Ð&”»Í°NÊ­êMÖ¡ÿWûVH[¯UúGÚkê÷[íûël™ A®º;#i•WY-½VGý*Û_uÃ~«¥‰mP¬òš°e!O«Xßv»h²ª–Gâ«F›FŽ<ֵǶ˜Ó¾¢m¬¼ÒjnÙoU3.§%Lßê´0 DúÈgGت[Åô×XhãeRf…ѦÃu‘öV³Ÿ=D²6)—ÁéÕŸ®G\åVc¸Ã 7±Ÿk}sL¹Ng˜~Áåà?Ÿž)ËÊÓ‰Tý&¯TRŠ ˆ‚²BäHEÛB×‚ÆØŽ( #æîç–­×Zó«ÐÑG„i0l j •O—Õn·ÚÛÁG­A—?£øŒüõÛºÜj1 !8)\Ãü´Œ¸ÿ¦’ZÕul©B9¯þöˆ’©ï0½ÿQ«åcC§)«‹*¼®æjkUeƒuÜÉǻ᯷ڠ%SiuRéïl°P”™Ç²'.tBTN«ê¬Ž.ì¶ÂèØ\ýZ4Ÿ©ZPLKP’ø¤¯€Pýè˜×D:Tg«lM=°H.#!«‰ý/¨/¼õ³Õê£â eMcäþw6¬Á;Tú­:¼+£’A§])«á )B-Ä@9s„ûM;@{B¯kÅ* H;D{1¢m"r™Ñ6æÄ5ØN…À6–8ý7åjÊfõ‡­õX…{ZUYå(N[ù¿$m̤oâj¯ã@m¹ÕØŽ¾ù<Þo…;ýQ@ºéÎ]*“–^‘W^£‘v·È*Ù*ßøqûˆ,ºžŒý Ò`?–¬^­“y¡y2oøâÑ%†H¤§`|E.ãü  g•ù,Þ¾1`_üd{4ÿ0íßµ­Ö>ˆÀ°¡?äÄ B'±D"•Û.žìLûgʾ9õ޼¸',e_\½4lú5÷JíC· ¿öi­\›‘N¾hç¾Õ™é,ÙM.å`L¤óD¿ ¢(¡¯pâ*–Ï=Q'eOTÈÅ3îÄkå©—K‡J9…ÀP_ôM[$÷>ö$ȃÒ= Æºõ²wk…|kwx9Oα.)^ÙìÑr~ôÒ°ï‘ÿèJ<6J!ÎG¤ Ëj{ï^]ŽZtwƒÈygAÎéë.ù³MZT*K1*4+ÊŒ‚T\z5– äÅ7F¬sË »¶Évû E$®Ëœ¸p€„6–$}d~ÖËÜéô š4_yò!™²KX6¬{"âþûˆaÈÈ6æ4>9ÓÜÏoÂòæY:o&{L{~ø‚ N1uâóÈPä°^D[T’?Å:Ö‰w°¿cÓiÐwü-ô¶£§Š¡R¼ð/ìC<8ÐÛ{¯½r´~ù’«¸µNþ1fCéØ/jå®ûÇïa;1¸Z¨ýÁA*´4…’@úû{%ÜÜ(Ê?!ç]‚àðéµíž¶§e Ö¤OÙ p„†eÐ#Ç~Û+ƒ¼I`Ú;YO]FðÑè’©ò£/†å§_)•.|(=#æf…4Œ¢IfYŒíªK†ŠŠ¥ÇÀÝ­…òÓ…5¯i¤p³óßM׉“G¤’Pâômæ8>Ã(.)‘”šq’66<rìðoõùh*es;m€ëñ‡Á¿÷ ~ÑˤFª€RÃi\}Ÿ×lidi"  htÀƤ€F>E}²mÂT¹¯j¿Xß¼2Å0ê-ƪ€ÜÊ=•ôÔ" (C`²ÜÔÜ —Mç>ŽR.!  (—jCó¢( P$s®¹Uæx³F96ôÜØðÓЊ€" ("  (Cà4˜" (ŠÀØP46ü4´" (Š@†¨Ê8 ¦(Š€"06T ? ­(Š€"!ª€2Nƒ)Š€" Œ =†=6ü@# IDAT2 í¾RƒqXø Ëþ¬>Ó5\V0õÈÄÍ•"ÆÌJ†r4Qƒ‰—ÊzŽÖRfÙ2uËÐn{J±©J &Ï<±ÂÞÿwp)øcàià)à3À~ÐHd 1þ§cŒc¬ÁY_¿óâ߀Y¼z'íÆ‡0…BĆ‘ïÏSÖÏOëà „X—üm^iÏ›θ ¨zY"6FV/¥åÝI'fðÊZ¿’ ÄÏÊs¹˜BŸMb±¾Þ³¾pÑ—­X_¦ƒ…U)vZĈʛO„JœuJ7Ý !£€âµ¾#§Lª€R†j\=²’Øa±Q²A²2ߟ fÇ=ýT@æ‡ê‘lNS¶óÉzcý°Å-°v'J;;Q&Òj|ð_DLˆ e-*ž·Á”obÉ_ÚÐ@™6À>Ë´ÄX—i·U@Ù«uV;.v`Fù°‘r”ïw£|ir!ŸîŽ´ ±CeÇJ÷p°*!á"3Ó73£|(ûFî]ÞÕ(ëT:ì«Ø¨ŒØ¨„Ò’U@@, dF…fDÈ%V$g?¬?—%:^(òil€lxìP¹Ç•­ÒHˆ™Ù7 \Sî‰Z”u„‘©OžM`{ bâ»”IPÊPŸGŽ’qÈtd¬D*$6P6Hþ~ŠŸËoHN:ù/Ô™y4#z*3ª7£?U@1Qÿ²ÎމX‘ˆeÞȺŸ-¦¯4vÜm€ŠÇ0Ñi‘* ´àWϬD6Lv^Æ436J?G…ü!© P.ä“õff°¬76:ò;[˜J# .Ĉ&^ì°Œ¬SÎý”u$§4FL ü»ÛÀûé¶ýAº1ÖÄX‚;ßC°ñQá¥ãwƒäúÃc)‡Oas%Ÿf›&¢ÝÓmx>a–3ɸd³{#óÌŸ*Ÿœ©¥”32nm@Pʘç§Gt è;­œï‚’Ïü”-•"à ºç ®)ÇŠŽ•#èl*3šI9ÏYòÈ}3/óJE¬ûYª\M¶0P”ýzç$› (û¤¯•ƒÇÊ-…ªE ðÐ_áÕ¹–XPœ@@Pö«KpJÙG@ë!ûu 9(0Te±Â±ìcE…é÷w?Y,uî% ü§!W‚Y’{¹Ó)ù‹€* ,Õ-:»Ç‘4¿àÆúž?™¥¬t²ÀýVp̺èÆóg -¼"à#ª€|;&©çñÌ{ÄxW¹t€•üG IòÃHÞ@Á‚ù¬¤(>   È$ñOpç¿$~ §¼xµ‹’Ï÷7äA'Ù_ãùMŸ³ É)‹€* ,U=::ŽºwƒYµYʆ&AàÂà,”¦’" ø„€Þ„àÐñ’Á~ß½Ê(コùƒêá¤Ô þꂊHIP|@`\²~PéC¥5 t²^Þ(VöTfÒ‚++žsI^²€&êcR@®Îƒ7ܪòñ¬š<‹˜Š¨ŒoßÀ82£òâY•z±-/H:)g1ž–X#÷Œ¯âAG½ ²}»­Ê£§õäIäŽ28&Bð*uO;¤CYQyñ¤6½Ô‘þŽ?௿r ¡¤dŒHÚá3ÊçLžœv —à/°Ú?‚çt0žä qSñpÀÃô”‚‹Àdíþ /å%¸ðhÎÓA ãaXކ‚ð[2é`Rh~ÏF9óá2Ù«Y;.v`JÁE ØÉ:e…3 ·.³žóLg@f4k2ë…Ñ d„ÀT„b'Âe8ò¸“3R¦Ìè€eÜÑõ=BžtË‹* ß« L{ ÎÕ™¨Ê9à –ŠõÉûм8LÂ8?g@œq)X¢Ë¶Á-†æ<H[9™6 ÷tI%j2ó]×™[‘šñ\FÀ÷ÙKÛöÕܸ”m/ô¸pé“íË&ȲÚ6—[ëP´îk•h•2ÄSÝêŽ'N˜T†ËöµKdò´2sæL™1mªLX±EÚìÄDÚ¶-‘[ZS-©¿¾¶m2aÁ6ûÇ€’zÔ—)!кm™-S”«X^¯ÎÚdüÖ´½7þ¶Ý›dÂä©¶Ìœ1C&OX"Û_8fûﺣ\-Ù6>r·0ê¨ä0¾+ 9õž Ç}WUÊA×W'§zEâR&£C²hñ"9Äp“fËCõõ²äÂñ˜¥—ÚJ¥bëT©ßß!½½½r´½IÖìX'¡kª8;g“Æqÿ<œ¬œú..)ß$û÷ï—–p‹ì\BÐUÒØ–¸U~öȨ†å\O Ž”·»VKè¶ ²¦®YŽv÷JoW⩸j¦ì9æ,ên¼$98ÁŸÁSR ÿPä­òÕ¿y!úDËyÎSßÁgåe "£ÙËdÛžƒøDò lºa‘ícÑ•k13é“×ùKtÿ!ÛV,‘-{;¡EŽ=»E–¬Þn+޾{dõ’ÈÈxÙêmÑMÔ3,}­OÉÝ{Eê;vË®œ#ÅÅÅrÁÜ%òhG½”É!9•™*ágÿ·lZ½ÄÎ×’ÕµrØt˜==àŒÂ—¬]Îh™iÞW+Kœ‘9Ó?;aÃÌ«q.Ûô´ýsœ‰ò{`÷²zËvÙ²3EŒ˜޽ ›V8MX!»ÛŽ»‹TPöâYóåÊ+¯”…óÊÂYP8¡…ríÂù²nó¦¾.›Œ,¡nv·§D ÏCä±»žòšylå5r–`‹§Ï‘;Ù!Õåe>ÐåàzHþaÛZg¶µBžŽVê´bö´À®ï²¶öY×,·GžÞ™ýO˜°@ؾϮowEõØ-+@–Ÿ¥,'ˆ í`ËŠ²¥v›,C:Û°pðÙZÛÎÙßÊäUI¼2F¡¨´xôúc`7o#ŠÔ(\SfIyÕÞ\Å+_¬úŽAìµjÊÄ*«n±íÕ¼fyîè°šêÖÀ_™µ¿·×ji¨†=dUÕ7[Ý=-ÖµðWÕÒe5®ÿ²:ÄBêµê—T6YVWhÇ*«Üi…ÃÍÇ–„ª¬.Ûßé-ÕÈS¨Úê>í4®YnçwyuƒÕÒ¼Ó*c¼v~»í¼Kh…Q·ÕPU)Wç Õ®‹„©i²Ú[¬å,We³ÕÛ‚r„j¬~䤚y•5V˜™O’ßpM$^Y^i5´´v–#\yÕÞÙa5T"ÿÀ¥¥D¶“:ðàËÀ3À¬Wû†tLK.’ùGœìòKÀ—‚]êò’´q^Úòl#²ÐiaòÙ¨²Z:ÂVݪÖýÙÙÚSãþuí‘Ðq¢GÖØu*å•VsË~«z9â„ì0DGý*û]uÃ~«¥)⯼&lG³¿*RG;÷‡­ý;×ÛþjP锽²šv«ÿh£-«¡ÊFÈE’¸z[¬òH½Y«ªê¬–7ÿŸ-‹ëë÷[q¾[^o±U7±ÞÀ·/_æ7AB:ã&+Waa™¶àPàÀcS@vç;éDíÆÛo+H‡Þo…ÑÉw¡Z]v'NÑ £±•Y-l]®NÄîÐÑ7³?én¶òÎÄcwÚ•ÖQ8“ú;ì†ßåà&»Ó’Q•Ôª†h°–ªªj±úÛ#J¦éEè¨U‰†Êއ~N+Fè—æjkUeƒuÜîÄÊ­5ì¼d½ÕîM–_;e¦c…¦2]UguØ8u[atlÝËÍk" ê‘xä­2ʢɌ8úÃÖz(ð°3x‰U@½apÊ“*òHœ5GG ¶SÑ Lo·U…:)«á *B-”?l.룒ˆAÃ*«ƒ§H—ÛJDÖ7:Š#Y\´¦¡3’Hï~GµØõßßÕníoéP¤J1í¾å{*“¬Ð Åw~¿IBáûdóžWåÌ™X wrò^÷+R1c²LÀ ¤¡ ¸N¬¹cíž4ȳvQñ›d„¥ñ×Çåø ÿ[¥\?‡:’ûCd¦³6¹cGP牘Κ ב¡aim×¶Z{ÙŽ)‡þƒ¾Mœä,F²$·]ŒüÚéÌDŠ SïÈ‹{ÂRöÅEv.è4ýš{¥ö¡[í{oDd뺞ˆì/Ñš$¿ƒ§þW8qË瞨“²'*äb§kå©—KQ¢¥%;îBüÐd½Ìî”}Ò|yäɇd>E#—^éYƒ¼øFìÖ¼°k›lßÇ¥1Æy—\æl=âÀ$˜G¤ {C{ï^äÈÁYtwÖ•ñÝ&öšŽÂ×Çf80~»õ¡ZY¹£[ƒìÅÿPÿïñ”,.¼Fj2{ö4úDðOÊ7«VÉæÛÉ´‰dò“ƒ'‹®2FéE 7Èš²‹?}‰ì¨[.›—.’»ÐÏF[:öS¹ê® rWc;ŽCC#÷· SèuuÒöÉØôæÈ«*“Íè”ëj¶b…ï6aÓ<…fª–.t½½ýÒßÝ)MÍrGÈt‘h.¹j,ëäÛ†w<Ç~Q+wÝ÷¸¼mš³ÙóaÜ‘ è[Ø„¤éh¿ âðB¯„›eCù'ä¼KÐ!>½7ÓÓö´lÁÚÿ)»ƒÊ<†â|BBë÷ØÊ/Õüb×JzϹFP®îÎvi¬»I6ßýÙ“'‹…kØ•ôªœr ÀþÉvì£tÕã0p0à™ ‡5¾<ÌYúZdý]÷É+=Æy’‘ã€EƉöTk: ýÒÛß/]íû¥ñáOG §O. ɾíÛ"{G”Ñ5ÒÙR#á­åò7û¸G•B\ƒ‘B õuKéçÿÊ–ÃŽp³Ô,xCîZ¼U›2ŸÎ¡ÚœC » pÌ_Y%•ÜI"ap9ÔÛ KHæÎ+•’"lÜ>ú {txr-ÊîPöÊ7Nwêv8ü[ôù{Dpjm]CHî½i®í|ÑeØnÿ@žkïÁÁ‘Ÿ¼O>³ô¯å­˜Æ9iîM‚½¹;T.»Z˾9¸o»,]ºª’+¸{‘€Š/¹RÊ1çª{æEB"}o<#¡ÅKå_Þ*’OÝ´FdóZyú` õGÿº×#7Jh¶\rÁ|y ¹JdëRy›É©æW¤K6λXVìKɬ¹rãM×Ù›q ²X€Îœ¥`rpÑeÀ¥A~øã6(øÙ÷øW¥bݯBÀ éÁËeïºÅ²Ž÷ Hß±VÙT~d°\V”ÍJˆå`ÑÅrÓ*TåÖŸÈáþIR<Ø)5ó®’¥;:‘‘KäK˜|ß÷P® ÉñÖ²¸â>é,²E:tþ ™µpµ4@\Ö-þ–JWL†:dÞÅ3äÿ`r6gþ5rãg¯ŠøÐÙp Rú˜“¤»î‡BPiq#³CuXîe8ËØÑÍznصª¹ÁÙŸ°ÊV­·–Û‡°Ñ;Ø9H€ýžý]‘uõ¿ ÷E"Á†>&AØ€Ýél@Ó¹ßjt˜ø¸î—zÛ­ªåسqÒ¥Y¾¾ÞêröUÂØâž!÷s×~gcÚ [^Õ]˯_ÏÍg§<8¨Ð‚{¯Áٸ桉öæx¥Õ™$¿îô˜‡öúÈF¶‰;„ý %3Ya:ao„™G‡ _8äâlÑX‘Ã.§ê&ì8{2[£²ã†¦ßj®áÁ—Ó2È&Í‘ºáu‡Úsöóì ½Øc¢¬š°¡JËœgìÚoA?Eß•c/yd½Fö>ñ€ýKè){ÿÇËÉ{µ}j…ùîÂáŠár»¾¾/Æœré!„,ì• 2}ߟñ#MûÄ+eÂ>ЙàsÁ<95 ü43;~4$=Ç»d¨hn#€®ÂHt¨HŠ'qX‡±,–P&ÙöÔR´o7À¬§¸dº22ìÃM}öMNÚ©%u{~9? “‹K¤$&Æ9êÍ ©æw¨ï8Ž¡¿'§L“ ¦_VL%Ûܳ-óŒ1×O±ç‚9n„tˆ:3È•Q®r=;ÎI Ê$„º9Þ7„ú/‘”Åf õ‰½Â‰“eÆô’‘Kn “3²[,%8Æ=\Ü ×Xæ4RFâG—,®á!zŽ“^|b7RÇÊßpŸ™?9òr;b8~ ÌEIjfþ8’"69ª€Ò.‡È‚Q@`£AF"  h$&ê26²¾4¶ìkhE@P "  (¨5§ùVE ਠxjöE@*ª€‚ZsšoE@PŽ€* €W f_P "  (¨5§ùVE ਠxjöE@*ã¡€Æõ£Å ©ùVE@Háj§Ö(c2”…ÔìÏêS‹B}å¦cÍñΞ;~~5¯ò2ÞûëÑ®?Ö¡“¤1ýɦ’Wd¢€ÜPßÿ9øBðGÁ¼r…wÅÅήp7´Mo8¦þ#ÀkXyô;`^¥rÌú#›ŽVOˆñ›4*`/ó>A^õ:+/pJ‰>_,×Á”|«§t ráoD¼ þ˜òâ®GU>D)s2U@<6z2¯Æ-T6ó.1#´± ;>ºÑRv0 ˆ¿;Á:ã#±ƒ1ŠVψrAEÇô(3'ÁìØ(ƒ¼çü p&Äø^å*ô’‡1m™õD¦¼°û1`A2JùŽ@¦ ˆ¸°ÓbçP²ñ›_K¡€ò²ÒXTê¸ý¦’ÿ°3a±Ã¦zl:Ö#;¯È(¦CåÃt™>åù‰7c†sJĸ¯ÊUJp¥å‰õfÚ8Û7Û9Û;ëƒÊ“’"1™* vVF0ÙP8©p(œ“ÁÁ±{BsáF?o‚•üG€‰éP8ûagB%À™HtT;Þ7a#nCfÀÂô(/œñP†˜ÊKì€N)J‚¢ÊUJp¥åÉ Ø®ÙÎùc]F ±î(.ô£¤d„@Ú ˆ‡óìL¨„ØqQ )„ìØ¡áwMíÎ%VQx™Þ€•²ƒ€©7ÖgB¬?Ö#;¾÷‚LGÆtØ‘QùpÍ´9û¡\dª€¨ÌX•+€0ÎÄz£Lp–j”÷i§¼x9kFôJùŽ@Ú ÈˆÙO ¢¡£j£|ØÁÄ* *'Žtƒ•²ƒ€éPØyÙ‘P)°ƒaòý¸“3h1³f*>vjT\ª¥ Æ“8§DìY•«”àJÛ늲AyaQfÈžÉ âV*2U@fDK“̌ʅ G²±ÊNöh› Ч¯”²‡;SFQ9|@Eáa¶7ÓcúLåe,ÊÁíãR¹"ãO¬72ëÍ T¨|X‡^Ê ¢WÊw2R@Έ–Ø!¤@R8©xâ)8Û£Tš±*eÓ¡DMÔ);ψñcé–é‘)7F^2]z3y¥òá HåÊ 2þ¦‘ÆL9!³ »’"1) ¦æ¾÷=!“‰D ÈŒšØa(eß;Gf¨|2cäĘ™"¢r•)r©‡³«Í§R}*IÈX¹ãŒ̸£"gôKÅåéHÛ/µç..™‰+/©æ\å*U¤ÔŸ"{Œuù#±£ëh7ôÔ¯" (Š@#à§òŒ„KÁßk"ˆãCUƒ¹9®”ûde`ùø8˜';•E CòF9åç7&JŠ€_œãWBšŽ"ø©€ü©~£ÒC`Î`jM…Á¾Òq£ûý£¹›÷4á£+ìÕŽgIϸÜowܧÃíe×»çè®ä)žËêó~W]GåÊ” ïj¦|‘/ç;˜·“;¬¶lмü¸LÿןsìcžÅ›|©©ä:ãr!G ÉoLJÁ¼ù0x ºæ¯Àuà³ÁðEØHÇã„é°wÁd8~X¹ü ðÁ'á~9ü½»R@ý•"Û[ÀüÎhxês³c‡aÓ ø.˜·:Ü~<< L¹3DÙ ûEàÅ`Ê5?_þwp7â 2sv%E ¯ðSí’ûиþrŒˆž…ÆÉoHb‰D3Þ½ÎH‡ ši¾®€;oj ;G±TBñÜ£ ï©hÁ7 -‚¥üsĵ&;Ÿa„´~N¼ÆÍ`U@ÄZ‡hÏÖÇ=ªÌŠ»÷NÄBÜŸÿI‚4¨|þ~OÁïßÃ^3n|Nx*¥oÂ?g@­tƒýyšx67†ðQIÈk’5’q-¸i`0'Œ‘ã)“WŽ<ÝDåÃQè·#ìá$în¯ì 8S2Dåõ&x˜ÊJ£áDÄkg”¼E€’±ÊTÂv…p²Oyùø‰ŠbÅø±eyŒuwÏŒ(gJŠ@Á!P”G%æÜ×Ñal@[§bxü¨S¾ïÜíØ9ûù˜Ë ñÜo¶AeÃÑ)÷{ØI½žæ2Kîmp·×ûñ¬”Ÿp‰ì)Ôõã¨kþØb:D%Ãó£{K‚€œ¹s¥¤ G~F‚û.f´ù·(™Kܰ¡{˜ %‰;½âò•ÃQù<Œ°ÇaV€ÃŽûßÁÎìA0)v4«³ .AýÏAÊN]÷ÂÞ >žíúŸïÁÏÍNØm°»ÉÈN±2ãö§vE oàÒ…o…cC±ázFHƒñŸ‰tN¹óY|NÕ=NØA„Þâ€øìÙ#܆è—éÂî˜î °ÝÝW™BšrËB2ø)x†‘“d~õ"Phä*´ ,ôòú¡€ c-¿"àù¸çV¯" (ŠÀ8"à›ÂH•?ÕmֽDZU#ÐÙ²—W -¾"8|S@@æ£àß!Íp®#ÀƒÓr=“š?E@‰€Ÿ ˆÇ—»Ffa|\0 žÃý€d±á=¯Ê)IæGßž ;×ï\CŽF½Œ~x…Ïý~çMÓS‚‚€Ÿ h:@áf¯è7ˆxÖ(‘ ïž=Èn¶ç *Í­"[ø©€<Ö©à q’x!è.°ýíÌb0¿hÿ6˜ßõÌóBÑçÀôslwb0§8ÏtßæG¨ö1_çÙÄÉyüšKÒÝKé¦ä+žÏ€P¯œÉ˜zç‡ÌÃïx©ÛO©ã{ž[ðîþºÃ÷2['ŒŠ@Á O ˆ£Ñ¯85·fø p3ø;øãç0y• ¿jçE'À{a§r¹Ü &±ÓxÌ/Ô/ó.7ôö]c_†q^æ½vTZ¼Q~®+ù‹€§3 Ôq)Šc.#eݯrÜ`?X¾ ÌöÄ뚨pÜmk!Ü(3߃;LlÛ—ÙÂäÀìG1þᤤä?¾}„ö€“s,Ô…Á™ÎBü‡#>?åÇ‚fVr(bµ×ãy=J%˜7pIŽ#xÏ×÷ÀŒãMú…I­ÃÎbÌŽ‡ «ü˜ô4øzðƒÏF˜0•|FuÄzHtÍMª¹9Žú£"ˆKHc>^Ìÿ øëàÙ`ÊØaG®¨/…Ý^b†îEÒÏmà¨l8[øSà‹ÁT^¼Ú‰«2N%E `pÒ¼.4gß`ƒÇU> 2n+¼ãU'±Ä‚ô¸ü:¸|&ØMî°\ê9æzIeú¦ëY­ÙAà’ýÚdŠò˜Lùpé6 í2ÒÞŠOú=xx˜ò·¬¤$E>–ÚÌL|L2nRF‡Ã™_þÖ—`/…ýg`[!ãy>ÜÛðü·`C¶²;÷{è3¨„— ¤¦ç°"̀‹ĮF¤£]FJŲ¼²q©“‰ÃŽÏàŒG/³‡Œºv‡ëS‰½î(XŒw”…‰!”ÂäRÈ‹°³ó:¾Ê…vº›‹FçÂÎûã¸4Â¥†¥*Ÿ‡án/¹ÀÎ}#¥ì @öréêûˆ´ËH)w÷9²ñì×;²k\Ù¨€»‘±¿ƒòy¬¤\~ð¥ÀhœÛPÒ£™5B>Xf»Ð´##\vû=Ýœç?Ãs ?À3G³¯ñL›àv,Ã.&u^©‘P|gU>ÃÒ‡B@ÀOäyG1†~`6â!׃§áùçã¯Æáœf»óž 9‰*ÏJª+y†€Ÿ‡r¡£H©úЙtÂ#Y)÷ÈúÀòÂoΔE Mt”&`ê=çÌÀ&çÓ )YFÀO”•Ž{9¥àïÄâì¸g´tƒ°óÁ~b›}}>@Ög@§³ßY‰^” ;»|2¾OuU ?{ŽË– IDAT;ÑlvïÆ©Ö#p›Ç='~˜øñT<ªÏÈÊÀ&ÍRýþg9ahòp‹’"Pðø©€<ï(0²\éŒ09Êäi6C_Æó!çO·‘ŠÁëi{¢‹Iñ*zÑ(ãäÌÇ„g|,“Rvð|`ƒjv_4j×?Üb/µ?@…;/¾íS^(#”3Þà±Á‰&ïTR ?§úÕ¨Í:ðÙàI`ÞB|;L^§S þ,˜î¼L’ʉ——®“N€÷b3™ å^p7˜´|˜wÁñÞ7Î|¾&Ý ÿ-áE‚ëÿqBÀÓ d¥ùŒwé—à~™#3!Ø_“zÁ8î¿‚2CYû ˜DSoа¡Ð…Ž€Ÿ ˆKV?@ƒæÈ0S>”¤Â¾‹whøïOÁλÚ8Zn†ÛëŽû5xf‡bß󆼃sÁ]°Sa]欈£Ö;ÀW#Ü`Çž“Çmùå:¯êQÊ>¥ÈÂÎ1Èe±=Q1PßxGó90åÆP,¥» &?Nžût祽爰wây!Ø–5çoM |*)€Ÿ ˆ—‘®D£äß™òì$5Ƶuî븉£OÒ‹#úÿ•¨íô7$ïÁ­ü:¸Ìëvb‰ÊG)·àþʃLQyÓu\‚R¹/(GÃ.#E˜çá6 Lóg`ÞšÎÙX,©ÌÄ"¢ÏŠ€ƒ€Ÿ ˆi}à!ò"îï»âçì‡#QŽ>y‡ÛçÝ“0£×¶ #1Ëh¼˜ô¼ãȶö“0;ÁÅrFt9 ³4G'®í+e¯åêjѾŒæšâB(_ÿrò8L3óáR.‰Ë¶”úÙG»’" ŒD h¤“g.šÎÞ‹Dxsµ½YìDÎe7Þ\=Óy> ;­`ú½l谼漧ÛÎ .ŸtÃÝ,½wŽzy™¤þ†‹T ¯åŠƒš#¨ë;œ26ÃäRðGÁ¿uÉ•¯pâ/e†M\Êå¡.½))Š€ ?§#U4~*·Ùhøg±|x<å˜\úƒ³Ý#¸/”äk›à÷u¼fþÎÛ“òÜùCc Z{t)vþ 7—n¼T¨Ì‚Òèx-WGQÕ¶l ºm™â3ìœÍS 8³~ß¼ƒù¼ãŸƒ#3¼)ûŒ”)Û¤]I(tüT@^Tíº4AlÅÂÊÂt œÿµñç~oÜŒ‰w¦#1NìHTùDÑȪÅs¹Š• ©–ÛþrF®r&Í"{pôèéHÕ/¤ +•«Âªo-m!à§ÊÊHÂoƒçäQiQ†#¹ž…ÑŸ ƒgíëzF÷ñ¡²›*Rê/¨ø©€t¤T)Éí|E®ø!ëk¹ ¥æNð?ç#UŒï›k~îqC ÷ØË#yíÏÑÆºÛ£T¸_튋׭(å&Y‘+ÈÆ°¹t”&¿õ¡<Õ:läðrçO]òýJ0ñó5ú)Ç‹þ•|FÀOäéHøjTÔ÷Àüðoíp³;ØIÀá;,¾ÿÞ3O_Ç»T’_°Ïvü/…ß[ð¬”{dK®NŠùX »¹%ãØ9¸áÅ·àá‡ß“ñUÒ߃yßàÍ`º'Š ¯”üE€SúR:tà-Hˆ7 Œ…^B~ÍmÃâAü/Ãa'Þ?Êx.†Á†ÍŽaøøSà‹Áì$•ÍSà¿·!ŽSO`xé(;—Ûq˜J9†êéß‘¥OŽ1[Ï£~‚U¹âǧo! 369ò2ÿþc¼ë„•ãû`ÊÙù`^÷T÷ûa?ö°O‡½+A\”éExw¦’"w°øEo!¡›ÙÐÆÀWŒ’Ù“IÞïÀ;*š÷À›?äåy؇]*‰NËDðõ`*+Þ”½¦Rî!@¹ºn 2EyŒ«|\E•«³]ïÜÖwܰ»Gw”§x”j\ñª›"hüT@ìÔÝ r¼{ÞÏH¡,8BíówX qÆBgóc˜öu=|¿µ0b/•äò /1m…ÿ;arvd_0 S)·  Go&ð kñäŠ3cÊÎ|ÇäEµæê&ZãåéÜ8/Ó-͸âD£NŠ@ð(ò1Ë^w¼`”?6g”ÜÃPÜ6E¬€Å\ù ììD¸´ñ'`Îp¶À$ñRÉ~<³ãáå’¶#þ]h,jæ¬ Sç^d,ž\ñ‚ZΚŒ<1ÝOŒ’8gÞT4ö V{VYãA„tã%)}­?÷€þ/ ù´7šž3•*@nĨØyÇ‘ª}OžY~»ó‚•³¦è¥’Ì Ü8S⥒vçA7¥ÜBuÔŒ} uDÓ3B:#ä nT~“ÁýFŽ’e€þùK7®déè;E (äÓ ÈÆ <á%±ïÜì#” ÜN¥" 8Ÿ^πʕ#?#ä&Q]¸å-ÖOºqņ×gE ˆpYÌ/ò¥£ð«0šNÎ àõÒnÎT3¢ä~* í(òMzr£<:°ÉzÐ\(i#à§ÒŽ"íêÑ)  ›@R/Š@."à§Ê鎛ÀóÁiã0IÎÅÊ-<ù:°A]ógßÉsÀIOßáý_c=À´jHh1”ðó‚¯EJ¥î)ŒÇÙàÎáÎ)=}8%_êÉ üØðøô ðoÀ³F)}+‡ãçE(!þ”wR¥5J|úZÈ+Òñ¡ôžvhÜÓÁ/ƒŸóÛŠ{Àßqìt§äHÔ}ɨùp•£’ÑÈí‡ay›a9KbÜä”Â-Q8·»~ã^‚Êt”ÒBÀó ꊲÄ:çÕ8«ÀTB¼s÷ R.¦€);ô³Ì‹F™¯22¬‡èô û¿þS?×Å™(Ñð×Áühô{`o5àHõÏÐ\ “×ñ°óàÕ([àv;̯I·F Û²›yæHw=üñ;!Î’®3ìNXXíNˆ~÷°p|¢ûl'>s±i¢KPíú/e¼ØðZʯkZ¦Œ‘xËÆWl[D!=;å‚2Æ‹F)sÁ\z[&}!bèE@ ~* "½çÑis”˜)7ŽRmo£“ ~Ž9þx[1¿åyÌe“r0o@¸¼LZ?üvˆî¼ˆ”K$T` ‘Ï0Ù™pÄKÓŽßñÏÎæŸÁ6% g¼üâ[‰‡Ká÷˜àR¸í‚É¥RúLG–1ÈeñŸ’${Þ} uÖî„7dðþ6^­ÃÙO M¼{L9ªàs Q¶H¿‹ú_Pˆ€Ÿ èÒ» ”_ƒgÊFi$ª½sc^ØÜÜ>²3èwüqÆó°c· t(¼EûøÓàŸ€Ÿ2‰y~ßXF Geu=øpü®ÏÃÎQ5ÍŸàÎQ³RztÃûA¦(‹ÿ5I’”9΂ ý§±8&gÆn22çv3v­_ƒ„šŠðS±ñqv‘M:ŒÄKÑáüÌYÈ&ðe® QùœÇg¼ÿ¸ V£ôöÂ΋½;;Ψ Å çø§ßa›ÂûN±— jeÐLÝôZ®AV¶1;¨3Úù6˜3bCölïæ;¼;.q ¢¤(~* ¦5â~¶q®‰ØŠ;ú“P(?†Ã?£³°—a?nÛOœ‰„ÁGøìòÃe¿-à3Áƒ9S¡"}ʼnVyü&-qÂ}ÎæbS†ã’Îcào¹E7*(^‚ê5>H&ïÈk¹úsÕzuÆúû˜“ÒÎ}`L]R6 ½e,j*ŠÚU¤ýxg RY…ôhf•Îbxñ(×ì£wâÁN„ÎY°rÿˆvŸµ;ÚáÄçaaé”$Óq±)üs gØ%¨ŒG)5€ß¿Ãçí¨šžë‘ÿéØòa‚;g`®Á«ðx)ì¯Á>b6K¿±áMÌ¡ãv?¬üéŽZ˜¯¹ß7‚â…áIBÀOäõZ}U›Õ…@Öå Še6òC®OÃóÏ]ùS«" $@€›ª~‘/#U¿ £éä 9!WP:@„¬¤()" 3 Ro9‹@Ög@9‹ŒfLÈqüT@žŽT±þ^ ¶O,ù…9Òãu: ;eè§àa¦"N~C´<LyuljG%E ¿ðS è ‘q´˜)ïH?l3:ú×Á§`¿¼öN˜!ðçø 6Ô G¤»`r$: vâÁ|vÁÎÌE`v6ìî_ø>ó˜-•Œ›*ñðçð»fµë;*˜0™Vø$˜ôøSੈ³‡Ji#Àºé¾™ÊÃ}?Iª¬÷x—‘~î¨7ó×ñ\›$[!ÁÿÁü®l-ø\¤ínƒ¼Úémðµ`sÔ Ø•¼DÀ-ü^° \‚ÆÇ¯Á3åå£dòŘ÷¯ _7ÎVÌ•8¶äáyX8ª¥ù3ðØÐ{°ðÂÒ×Áà襣°2Ǥräè•a7;n4îÏü˜wÂÑGÎ9Ü‹|rV¤”>TæöÌs rõÕ$É&ºŒt‰ G93dfò%-àöŸàó½*⦠±M]Ѥò‰uÎäï8¸1þ)ondÜi–0 ÈV@Žý<?‡LœjjÛÈG˜ÀBùAXb:Œt#=šžÒaÇp&Ò9e‰uÃ3;{®Ãsg Œa—ÿpwùdÂÆ.¿1*ÆÅ2•ˆýžñÀÎ=**nTàÑN—þàδéŸKrJ ‹`!`HÓ3B:Ü'Œ'tgFåÏðOY \h`Œ+Ö?ÃrÒbظrfü©©»Ãô©T #ãx§í4øaA¬ž£Jö¬wñ.a§ûΉ‡Ó‹—Î7wZjO ¿äj˜L™œ¡n¹'““¸aF‹Ó¼WSÈ82÷‹˜V´ã÷+QM'ïP¹Êû*Öæ+~* _FªùZQZ®„¨\%„F_(¹€Ÿ HGª¹- AÍ/r…}?ÛJPëBó­¤…€Ÿª Gªè¸ækç•–L¦ëÙs¹BýB¦øƒsqÛ ÝÁ§ƒ¿~ÌÑh­):ìî‹?mw¸ÑÿËo„™è2Qãï9øa¼¼˜’éÐÎð,ÝŽ¸Dn&‡èĸÌŤ¼”’i’Ï.0¿RJ¯åŠßåÞ`EFÈRäuä?Þ»å€W/ÙãÎï‚”E£:öœô ðy^¥…¸Í§óa7_¬óƒÁR0Ó —öR°ñÏ΀ßrl¿Ìü¦ƒi¡ã·;wˆa¨0˜†ùxµv“öí.;?>5îæÃVvNÏMJ»ýa¢Wåc¼Àì$øl¯ÊÆ:S(¥Ž=V–Œ\q Ζ/GŽ(¬û¸òáUž5^ïû3Åx|0fCò‹x¿Õï0KzÕ¨ø5 "àe¤x߯÷H‡†ñ «mÿžÿ„ïÜ„÷ò~*aüæJ˜7€Ýô6ü½ÄˆÇñ| ây¸̯ٯZ??†º3O:/·;%Òâˆaÿ¿þmíçrSëèP†ß=]5£ˆã£ Ðÿew:±þH¿ƒŸãH‡rW–l_‘ÿ¼l”×+1 9#än?Žx×ÿŠ@a"À‘›/„†x!˜7Œ…׌’Ù_¹ÞŸûÛhü×Áä>ÌGÀO€Ñ¼ØæÇ¡›c<ó<è<óBQCì`’]"iÂ?OÁÿM`˜F¹¹œÔšTÚ“Ç(W‰”;yˆRJ²ÄÐõ`bx«úJ˜¤Ñä#âKÿ+„€o ÈLÙÁßÏ¥1ÒwÁ€¯?…êq˜ÿ NDwà¯tá¨ÔÄ‘Èo¬ûa8”"ìÏÁÜ3ØæoâÒé_?Áþ€í¢ÿÆ‚·_3ÇÑd‰rC9äçwÂ| L9M>àEI(<ü\‚ó Ý(!¦Å'÷j. ©|Ì%1ú1TKØ û ì|wÌNÇíÃè$:.µ­›ŽKÛ_ÏÃä ŒûL㤓¬öÑTÊ¢ü:]ù}¤O–L;:…÷O€yd†MœùM"ÆŸšŠ@Á!àÛe¤^#‹^Š4V¢±óDÛ°ËñÌÞ zA)£—‘Â=Jp·;Ä1DG†ƒÝ(•¨¿DxçxÄ%’îx`g‡É#½ì¬”ƈðd]M2u6ÆèF κ„§¤²/”ƒ3'3ëµãuÜGÈǨ‰ªE O0#·|(Þ™(Äù,HlçŽg*‘h‡Ç¸#f¸ÛЇqœp‘‡þÃ4 ·ww<°3í¸þÜaÔž2~΀ŒLDëÏ©Ïa™M"ÑpÃèƒ"P äÓ ˆGÂÃFZ¯SlÌ*PåVt½«` ®Uò¼Q@yPZ„4€ò¡âù@PšÀ©wE Gà¬AI*T@)ïѵšoE _P”¯5[åòuÿg+)…Ž€* B—€`—Ÿò÷Z¥ñ*fV#®_s‰f7˜Ó“Ü—ÔVà™—ÙÒßß9ïx™­MŽûž@œ\Š#¹Ãó9žb£»’"—pÙ"/ ¦…ÊЩŸ‹RvB†izBHc>"æå¶/1G‘ÌÆs'ŸÝ„wö¡¼¢;ž9Cû6x;ø0˜”Úï`WR ¼Ïg@@§ü"tÉ3`~8ÊY •É¢rq+Ø9º;\L;H•ÏÔÔ¡ÐP!×~ÀË…pŠð::všžÒáÞ gB¼ Ûž ¥šÂNß„ót©0Õü¨?E —Ðï€r©64/é"àÇ ˆJ‡Ç¬ÓR<¦ «·³0ÔTbÐ%¸@ô1PpE71UešYEà4ª€Nc¡¶à!àù KhÇžV RšcE P”ƒ•¢YJ¿f@礜#õ¨()#  (e¨Ôc"àù È]f(÷€yíÍð¥àgèÏy¦»½· ó90/-åE¥´3Ü!0?h¥^lJ¦»¶E‚¢TP¨ÐTuç]aýšàø¡èƒ8XÀtׂùaêðÍ`Òõ`~“4 …Jh1øwàà½N¸{ag8Ò àU`~W¤§äˆˆRA!  ¨ ª;ï {JÄvã b,\12ˆ:ý@QðfšÛi‚fðÞÏ„±ü øËà?7ƒ©¬¨”x;öí0/Ó?ï„£{ÜZéGIÈwôv¾×pþ—ïM(ƒY>3Þ¤Löð7Á5à«Áæ ç:ðŸƒÍ)½÷`₩Â{¬¤4:*èê|á)¿~-]&Z˜¹Ø¤Â¼œÏP~¼áïÀ\† ;ϰ +h?žú5¬Ü+ê×ÁN…¤¤4:*èê|á¹¼e:x/ ó—ø®A"¼pÔ¤õ ZðŠhÝÄ ‡ÁËàn®Þ¡¿×\á® '%E ÐЫx ]\~tè#û{ÐÑÓô…&5Ídp?ÒMYù9áÎD¸ß§ΗBi"Š@–ÐP–€×dÇ*ƒ”•Àx¤è(´—Ïœp¼ÒGIPtHE!Èø¹dœ4ïŠ@N"  ('«E3•"¾Ï€RÌ—zSP”Hê%gÐPÎVfLU@£c¤>rœ™áÁ_KÞÏk›K’¾+(ôBAUwÞ6—f@¦€n~fƒ;Sð«^¼G@Gcy_Åy]@Ïg@˜±ð2Ñó[ Z" “î/;¼Ned<“è¾ l®*†Ý?‡`g¾•‚G@PÁ‹@ ðcÄow¾æµ:Á« ?îIwÞv°¼<lߎ“î}`ÞU× þøk`Ò­úPý¯¨R2üh‚™idjÞ’*N(60o6¿güÃíQp¼ï{î†;¯ ú2x•ömØÛLX5BG@÷€ ]‚]~Êo+:÷EãW®øÏŠd42˃ƒ£yÔ÷Š@¡" 3 B­ùü(·éä½, Șeå$ò]˜Œ!Aþ ƒ’" U@*AF€òë×mØ\êCz³ÀÜÓ!Å΄Þ8øoü=7¼ÌTÛ݈ԡÐËH ±Öó¤ÌèȯDQ¶a ަ'„4JñJ¤±ö³`ÆÛïI9mÄÁ6GE¦¤<:+x4~Ì€xÚí|¢4Våãġʇ@()@@ŠAðcˆ? ·6È iÞ\E@OÁåjÍh¾RAÀóf=ÜcJûçRɼúQ º»ü~Ì€‚æ^ÈaTåpåhÖFEÀóШ9£l_:Æh4¸"HT²Ú4Óù0úÊòšÖ¨"Pˆ¨*ÄZÏŸ2{>Âì$Ùe¤Ï(á—ΓF\FJpçÏ1˜ë‚è¿ÎGœw+i*)…„€* Bªíü+«3 Ãþ:8ö2Ò³áv³ Ò;`ç½q¤—‘BÙL;Žá 0ýÑÿõàkÀ¤¿ú_(ô\áÔu>–”×ä,á¬bŒ…ûN»¹ï{sG½Œ”ŽH‹—‘R‘\Èg™ÛŒ/#ålçËp8n¿ §—èʳ ºûu£’SRrU@¹Qš‹Ì˜Œ`?EçýÙÌ‚§Ê­œÜ—‘Æ*w„fv澌4Öÿû@E¤¤$ºWÕž7…6¼—Jt)ǹ˜É¦Ó>J&öâ})üšKMo9˜æhaáEIÈ?Tå_R‰(¿~-]Å^Fú&ÒæŒ†éw;Á‰ˆKlÇñòa°‰ç¸ýÏöG®PL÷' ¬îŠ@¾" —‘ækÍ@¹Ðiß„bÞƒŽœ¦'„4JqÂËHñ¾éó‡êR"ø§Òü; Üô‚Ò”ÐSOù†€îå[Vyü˜%½ŒÔ­HRþ9c6kƒÛXQ¤’´úQr6`%E ¨ø±¤—‘U:4ß9€Î€r¾Š4ƒIð|äÌXô2Ò$• ¯LÐP¦Èi¸\@ÀP.”Só ä%ª€ò²Z ¦PžÏ€ I-¨"TetMrÜÐиA©)þ#  ÈÌ5ÅñCå•@:IDATÀóMƒŸáIi‡íŸN€½|¿Ë½”ÅÂs ¸Çq§iÜaÏ|Ç?/;5þ™Ž¶I£T¨°D5çm!ý˜} è]†ÃL‹÷À™ŸN¸ö»ÀlC_r”G7ìkÿ Ë{àH;Á—Ï_Ã?ñ#Öëÿ}°óÆ%E  PTÕœ·…|%»¹™djÚ³š(ñ6¯ÐÙ“×èLrüñú*o4 }Ì[²Àÿ=07;a§òâí×WÃïàŸÃ>|!˜t>¼Ü±ÊbÇTCÈ{Tå}çuy1èSèÐy“ÀXøõD(!Þçñn˜æÏÀ¼J‡ …Ô1†ý?‰§‡ÀŒóp¢{Þ†ðŽ÷À‘ú#†<“³)%E  PTÕœ·…¤ü~àeé kjÿÿ€"z&/%Qñ¸õöÃéŸÔþ ŸAŸÎr>ÉØyÓA'øN0÷‰.‡Á¥:ó}ѿ Üî"ð`%E  ÐQ ¢šó¶T^_có-¤Ñ¥±ÅA‘3®ðüžïƒùmÇËqïãùŸñ<“δóÆl.Ç-wÃnâ¹þyø î'ÿ°ÊTþSR U@…PËù[FÏg@P¼ÅúÁþEÓ÷ñ|Ê“Ëçí<ÀÝž‰Á¼~¹W4H7„i”$‡]^ŠWÛáö÷ð?v7•üG@Pþ×q>—ДƒY.3x–Â2ìVkó­H`7ÊÇ~Gîý #¸Qy©ò†Š>½))AEÀóP`¦ÆS&Iüë+E@ˆA@P ú(|›Å¢åÃov”E` ¨x4ëds”õÂk #  (è5XØù÷e„C<6­¤(㌀* qT£ó¿f@/òøš¯%ÓÄ@@PTrÑóôÎ!¿_À΋C_vx#L^t;ßÃ$ñ­¨`ð½¹èê<®-š"1ª€2†Næ~Ì€ø)é à3Á À¼Xt3xؾí&‰ï¨ƒ6Âü2˜^Þ·˜JŠ€"àB@ µ~$zk¦aféš¼ë-ñÊÒï"†}µÎ£8—ì»ûà÷Wà›ÀF-2áÕTª€T‚ŒÀÇùÿ e0–‹H–÷²F‰ö€xù¨¹ïÍÄA¥uÌ<Àä£oºžÕª(@@oBP12žï¹À™è²+z3›G1 3?§Àw¶²b3?0÷>Üfð…’" œF@Ði,Ô<8ƒ÷ô6l$oÁΔ3Kr|õ=0/*åu;`C7ÀŸmøºãð0”— •EÀ…€* j žÏ€ 8¨\ K¢—ŠFP©À™Jð ØÝw¼qˆA¢—’YͰ"઀|Y“ð ßf@Ž"QÇÝ­|¢~ð.ÙA…¨?µ(…а’"T<ŸÍ·"T¡–4‰ðm”(ê®(™#  (sì4döÐPöë@s dŒ€* Œ¡Ó€9€@Ög@8h0̃ Ãn¥àžaŽ1 vߤãÃ>É0~´Ž@Fòì|¨ÅÂ-C.Ì€~øgÅ©~ znw·ýævˆcÃíãqÜÕI<ª€_…]Ïg@˜}<žI”aÖ‚W:vÎpvÁ>¼ÁqãlžÏÕàNÇýCpÎyw¦9ÊÍp%x6—œòÃU†Î WKÄ0$úëÓùÈ•ùЦ ûF2ÁŒ›.ÜY2ãÑ>€`)e¾¬À®‰Ž~Ì€Ž"¯_uò» fcæóàsÀ_AG^ “³•+À¼!a)ØÐ XöâX6ó{/Ø\ýóØyC™`^dJ¥Å°‹ƒèk`Ò­`^lÚ¾Þ‰‡¿Èºþ¦Àt§ûm<ó.:R¢toÀ;–e6âòëC^æGI†€* apèCÀxù}0Gò™òhðf¤qâŸn°q:ý;ðü$xÐq§éD‡þø=؃υ_¶1.ÅñÆþtÃE`ÎN¨°ÞG¿B¸Ÿ;a†ûͰóû"Þ¼Ðæ­Ú¤óö–ˆÕNãfØ__“î5|7JºÌ•O'ý*)ÙB@P¶×tÇÙˆd#:R^(š)Öþi°Ãþ!˜3‘¿?検…úSX¨ Q±0œå8P)õƒ_W€y?\"âþQ,EÇ8HOyÉ)÷‘¸eˆ³$R&éFBêEÀ'Fk|>eC“Q2B€KZ¦£Í(‚ÑAÉp†Ô æL£ ü¿À\¾zì¦z÷x¸TvÇ?éö!^ÛïHLÓ̈RIפÿl Àe‹l¤«i*cFï#ˆä]È0Íœ äÉÔ!OÃ;g'g‚wi5:†5a`¥Òˆg{ïÏœ}ÏÛarïg#M<Û&ž3N—ñ()^" 3 /ÑÕ¸½F€kZ¹×BÇ?Lñ˜ôàÎ|F÷TL'¬ívÎøÜñp¦T=óm˜¿s©pئ±¤kâPSð U@^!«ñú€_Kp~”%£4¨ |ˆ«è£àÿ ·¸J0£4"à!ª€<W£öœ›y^â8 8³Õ&+)A€#'%E ¨ü (¨§ùVˆ€* •ƒ #Ø–Íx¥Ni<ðáN⇵Ú>ã¤nyƒ€ xÞTeA$¯g@Üß)ÈZÕB ª€ ¦ªó² ÿ¿½³yµª ã0TbX&9Ið*4¨hPبQƒÆ Ä( „ *#(° ú@èDüš•Ö¬ˆ°„ŒÈ[H“„¨hõüöÝëº;{‘ôè}ï~^x]k¯½ÎÞk=ï=ûçÚçì÷Ì|Ä*ä~cèQ&玾>G=¦}ÞRíîÛZ‚Ñ/Ù·µd£y 5YòÚXkÏ1nK{Œú¢’ö´ué(“ÈôiVGßíøI<ö þ¾Å•Sphø j…(9Æ©¸Ð?ËŽ\ÜÏÇNsÁ¾a©pެHŽâ7ã-ÏZÄ&ÏßDP>Áßà)³j‰øìÃ#*§z¡Hûßlodûgʶ½•ê÷ø&Ú#tiÏXNã¹ývíYÑäÜ'ªÝJè¶ÓÁÙ@ûúüBývêó”šJpT"Lr ›h*æóð%ŧ?çqÊå’‘nfÿ©¾o+†«˜ˆJ{ŸËó9Ã>YåÄ"vÍRÿïnåQj(G ½1Ê ÜKùûéõ[ŽŸ–Œô9Ú‡yá²úy_´þÉõöx¤î3%ªYÍãàiÏoåÛTÙ}ó:Ý:µ£(B@*(‡9•ÀÌ¿„Пõå”\ô“‘z2é;Ù‡8äVYåýò%ƒXD§ÙV*ù|(}ÚŠé õ;íŸQ¿ ŸfÉp1|?ÓçNêùÍ¡Ó^`›V:?Zér|KàÂ›ÕÆ\˜S^Rc,k3Æòçr¡ß:ú´ŸVXìJû´o¿-ôç:Cÿ¤áÉ{8¢¦I Sñ” ™˜ù-¸Á¹–­¢Ë O{1ýþ#>ÙGû9‰Oßwñ\¼Nñip-Ëð\¹9à‹u npJ«À…" ](’çRX1+ K1yÏ)ê ê÷ø];þξ8¨xG>ü±Ê°.N_.ùõhö_Þ÷I™t=׎<†NÄ L}E¬€&¾°®'–bKß¿ØwS_>I=Ïi%h”a_5“žù ˆJ|v–R®Ç³ÒIoIA÷ ˆ¾@½[Õ°'žþIHšãlÃ3惔Éûö"¾‹z{0•MMã! 'Ö«q¦3  =ƒG$î§ü‰ÕËáI…³Úcll¤ÿ6Ê·ðä‹{OÞ¶»ñXö}ˆïÇs¼.uM£" *Ü«n²_1£C\ì»,ÿ³l™ ¦ÂéÅ&Ù ÞÇ“`çYŸÎÔ_ÃóüNn¥5û•JžÓy4¯Çç©Æ'í;’ÓM“À( (@£ ûê˜4öWñd8¿åhä'†výpƒzRäLZ> j)w²ï'üªT4 H`€ä_‚–!Àjg»ó“ ÷âslï¦Ì­¸äk?·=Û–/¼›6úå3¡]øÙž°«'¶Ý”Àh(@£ µýŸŽóºý¬²>¥Ü„ïÅ7à¤cˆKRáÀc©wFÿ·©|ÝïO†ë$&ý¦ÛyöŸ¨æó¥¹³MÖ$0¹u1žÙ:S \@Gþ·†÷P>óù—±/«£µìû<;z!ÚÂö|¶›Ñž÷ oÂÄrT Q…ÛÉ^,èÊç:‰ççò­·ØuŠÍÿ•@(@þH`F¡üDC÷9ÂÓ­„ft*+’ ’asЀêðKõcè $  ”$ • ›ƒ–€$PŸ€T?†Î@@I Pɰ9h H@õ (@õcè $  ”$ • ›ƒ–€$PŸ€T?†Î@@I Pɰ9h H@õ (@õcè $  ”$ • ›ƒ–€$PŸ€T?†Î@@I Pɰ9h H@õ (@õcè $  ”$ • ›ƒ–€$PŸ€T?†Î@@I Pɰ9h H@õ (@õcè $  ”$ • ›ƒ–€$PŸ€T?†Î@@I Pɰ9h H@õ (@õcè $  ”$ðåÿc°X´IEND®B`‚doc/next.3000066400000000000000000000167431242365656200127130ustar00rootroot00000000000000'\" '\" Generated from file 'next.man' by tcllib/doctools with format 'nroff' '\" Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann '\" '\" The definitions below are for supplemental macros used in Tcl/Tk '\" manual entries. '\" '\" .AP type name in/out ?indent? '\" Start paragraph describing an argument to a library procedure. '\" type is type of argument (int, etc.), in/out is either "in", "out", '\" or "in/out" to describe whether procedure reads or modifies arg, '\" and indent is equivalent to second arg of .IP (shouldn't ever be '\" needed; use .AS below instead) '\" '\" .AS ?type? ?name? '\" Give maximum sizes of arguments for setting tab stops. Type and '\" name are examples of largest possible arguments that will be passed '\" to .AP later. If args are omitted, default tab stops are used. '\" '\" .BS '\" Start box enclosure. From here until next .BE, everything will be '\" enclosed in one large box. '\" '\" .BE '\" End of box enclosure. '\" '\" .CS '\" Begin code excerpt. '\" '\" .CE '\" End code excerpt. '\" '\" .VS ?version? ?br? '\" Begin vertical sidebar, for use in marking newly-changed parts '\" of man pages. The first argument is ignored and used for recording '\" the version when the .VS was added, so that the sidebars can be '\" found and removed when they reach a certain age. If another argument '\" is present, then a line break is forced before starting the sidebar. '\" '\" .VE '\" End of vertical sidebar. '\" '\" .DS '\" Begin an indented unfilled display. '\" '\" .DE '\" End of indented unfilled display. '\" '\" .SO '\" Start of list of standard options for a Tk widget. The '\" options follow on successive lines, in four columns separated '\" by tabs. '\" '\" .SE '\" End of list of standard options for a Tk widget. '\" '\" .OP cmdName dbName dbClass '\" Start of description of a specific option. cmdName gives the '\" option's name as specified in the class command, dbName gives '\" the option's name in the option database, and dbClass gives '\" the option's class in the option database. '\" '\" .UL arg1 arg2 '\" Print arg1 underlined, then print arg2 normally. '\" '\" RCS: @(#) $Id: man.macros,v 1.1 2009/01/30 04:56:47 andreas_kupries Exp $ '\" '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. .if t .wh -1.3i ^B .nr ^l \n(.l .ad b '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ta \\n()Au \\n()Bu .ie !"\\$3"" \{\ \&\\$1 \\fI\\$2\\fP (\\$3) .\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n .\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. .AS Tcl_Interp Tcl_CreateInterp in/out '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ .\" Draw four-sided box normally, but don't draw top of .\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .if !"\\$2"" .br .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ .\" Draw three-sided box if this is the box's first page, .\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .nf .sp .. '\" # DE - end display .de DE .fi .RE .sp .. '\" # SO - start of list of standard options .de SO .SH "STANDARD OPTIONS" .LP .nf .ta 4c 8c 12c .ft B .. '\" # SE - end of list of standard options .de SE .fi .ft R .LP See the \\fBoptions\\fR manual entry for details on the standard options. .. '\" # OP - start of full description for a single option .de OP .LP .nf .ta 4c Command-Line Name: \\fB\\$1\\fR Database Name: \\fB\\$2\\fR Database Class: \\fB\\$3\\fR .fi .. '\" # CS - begin code excerpt .de CS .RS .nf .ta .25i .5i .75i 1i .. '\" # CE - end code excerpt .de CE .fi .RE .. .de UL \\$1\l'|0\(ul'\\$2 .. .TH "nx::next" 3 2.0 next "" .BS .SH NAME nx::next \- Skip to the next most specific method implementation .SH SYNOPSIS \fBnext\fR ?\fIarguments\fR? .sp .BE .SH DESCRIPTION .TP \fBnext\fR ?\fIarguments\fR? This command is invoked inside a method body to call the next most specific method implementation in the list of available methods. This list of available methods is specific to the current method-call context. This context is set by the usage context of \fBnx::next\fR (method combination vs. method-call interception; see below). The optional \fIarguments\fR are the argument values to be passed into the next most specific method implementation. If omitted, the arguments of the current method call are automatically forwarded. To call the next most specific method implementation without arguments (or to suppress argument forwarding), \fIarguments\fR must be set to an empty string. To pass an empty string as a (single) argument value, protect it as a list. The result of a call to \fBnx::next\fR is the result of the next most specific method implementation. If there are no more further applicable methods, the result of \fBnx::next\fR will depend on its usage context: method combination or method-call interception. If \fBnx::next\fR is used in a method body for method combination, the result will be an empty string. If \fBnx::next\fR is used in the body of a filter method for method-call interception, the result will be an error. .sp When executing a method call, the NX dispatch mechanism computes a list of applicable method implementations for the method name requested from a given object receiving the call; in support of method combination and method-call interception. .sp For \fImethod combination\fR, the computed list contains any object-local method implementation and any method implementations inherited by the object from the classes in its precedence list. Examples are overloading method implementations in the class hierarchy of the object, as well as from mixin classes of the object. For \fImethod-call interception\fR, the computed list contains the applicable filter methods, again ordered by their definition order according to the precedence list of the called object. .sp To retrieve the next most specific method implementation to be invoked by \fBnx::current\fR from the internally computed list, if any, use \fBnx::current\fR. .PP .SH COPYRIGHT .nf Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann .fidoc/next.man000066400000000000000000000047751242365656200133260ustar00rootroot00000000000000[comment {-*- tcl -*- nx::next manpage}] [manpage_begin nx::next 3 2.0.0] [copyright {2014 Stefan Sobernig , Gustaf Neumann }] [titledesc {Skip to the next most specific method implementation}] [description] [list_begin definitions] [call [cmd "next"] [opt [arg arguments]]] This command is invoked inside a method body to call the next most specific method implementation in the list of available methods. This list of available methods is specific to the current method-call context. This context is set by the usage context of [cmd nx::next] (method combination vs. method-call interception; see below). The optional [arg "arguments"] are the argument values to be passed into the next most specific method implementation. If omitted, the arguments of the current method call are automatically forwarded. To call the next most specific method implementation without arguments (or to suppress argument forwarding), [arg "arguments"] must be set to an empty string. To pass an empty string as a (single) argument value, protect it as a list. The result of a call to [cmd nx::next] is the result of the next most specific method implementation. If there are no more further applicable methods, the result of [cmd nx::next] will depend on its usage context: method combination or method-call interception. If [cmd nx::next] is used in a method body for method combination, the result will be an empty string. If [cmd nx::next] is used in the body of a filter method for method-call interception, the result will be an error. [para] When executing a method call, the NX dispatch mechanism computes a list of applicable method implementations for the method name requested from a given object receiving the call; in support of method combination and method-call interception. [para] For [emph "method combination"], the computed list contains any object-local method implementation and any method implementations inherited by the object from the classes in its precedence list. Examples are overloading method implementations in the class hierarchy of the object, as well as from mixin classes of the object. For [emph "method-call interception"], the computed list contains the applicable filter methods, again ordered by their definition order according to the precedence list of the called object. [para] To retrieve the next most specific method implementation to be invoked by [cmd nx::current] from the internally computed list, if any, use [cmd "nx::current"]. [list_end] [manpage_end] doc/nx-small.css000066400000000000000000000022021242365656200140770ustar00rootroot00000000000000body {font-size: 10pt; text-align: justify;} .nx {font-size: 65%; color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {font-size: 65%; border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D; font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} .listingblock {font-size: 65%;} div.toclevel1 {display: block;} div.toclevel2 {display: block;} div.toclevel3 {display: block;} div.toclevel4 {display: block;} @page { @top { color: #717ab3;font-style: italic; font-size: 80%; content: string(chapter-title) } @bottom { font-size: 80%; color: #717ab3; content: "- " counter(page) " -" } } h2 { string-set: chapter-title content() }doc/nx.css000066400000000000000000000022461242365656200130010ustar00rootroot00000000000000body {font-size: 10pt; text-align: justify;} #header {text-align: left;} .nx {font-size: 90%; color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {font-size: 90%; border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D; font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} .listingblock {font-size: 80%;} div.toclevel1 {display: block;} div.toclevel2 {display: block;} div.toclevel3 {display: block;} div.toclevel4 {display: block;} @page { @top { color: #717ab3;font-style: italic; font-size: 80%; content: string(chapter-title) } @bottom { font-size: 80%; color: #717ab3; content: "- " counter(page) " -" } } h2 { string-set: chapter-title content() }doc/nxsh.1000066400000000000000000000143241242365656200127040ustar00rootroot00000000000000'\" '\" Generated from file 'nxsh.man' by tcllib/doctools with format 'nroff' '\" Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). '\" '\" The definitions below are for supplemental macros used in Tcl/Tk '\" manual entries. '\" '\" .AP type name in/out ?indent? '\" Start paragraph describing an argument to a library procedure. '\" type is type of argument (int, etc.), in/out is either "in", "out", '\" or "in/out" to describe whether procedure reads or modifies arg, '\" and indent is equivalent to second arg of .IP (shouldn't ever be '\" needed; use .AS below instead) '\" '\" .AS ?type? ?name? '\" Give maximum sizes of arguments for setting tab stops. Type and '\" name are examples of largest possible arguments that will be passed '\" to .AP later. If args are omitted, default tab stops are used. '\" '\" .BS '\" Start box enclosure. From here until next .BE, everything will be '\" enclosed in one large box. '\" '\" .BE '\" End of box enclosure. '\" '\" .CS '\" Begin code excerpt. '\" '\" .CE '\" End code excerpt. '\" '\" .VS ?version? ?br? '\" Begin vertical sidebar, for use in marking newly-changed parts '\" of man pages. The first argument is ignored and used for recording '\" the version when the .VS was added, so that the sidebars can be '\" found and removed when they reach a certain age. If another argument '\" is present, then a line break is forced before starting the sidebar. '\" '\" .VE '\" End of vertical sidebar. '\" '\" .DS '\" Begin an indented unfilled display. '\" '\" .DE '\" End of indented unfilled display. '\" '\" .SO '\" Start of list of standard options for a Tk widget. The '\" options follow on successive lines, in four columns separated '\" by tabs. '\" '\" .SE '\" End of list of standard options for a Tk widget. '\" '\" .OP cmdName dbName dbClass '\" Start of description of a specific option. cmdName gives the '\" option's name as specified in the class command, dbName gives '\" the option's name in the option database, and dbClass gives '\" the option's class in the option database. '\" '\" .UL arg1 arg2 '\" Print arg1 underlined, then print arg2 normally. '\" '\" RCS: @(#) $Id: man.macros,v 1.1 2009/01/30 04:56:47 andreas_kupries Exp $ '\" '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. .if t .wh -1.3i ^B .nr ^l \n(.l .ad b '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ta \\n()Au \\n()Bu .ie !"\\$3"" \{\ \&\\$1 \\fI\\$2\\fP (\\$3) .\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n .\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. .AS Tcl_Interp Tcl_CreateInterp in/out '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ .\" Draw four-sided box normally, but don't draw top of .\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .if !"\\$2"" .br .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ .\" Draw three-sided box if this is the box's first page, .\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .nf .sp .. '\" # DE - end display .de DE .fi .RE .sp .. '\" # SO - start of list of standard options .de SO .SH "STANDARD OPTIONS" .LP .nf .ta 4c 8c 12c .ft B .. '\" # SE - end of list of standard options .de SE .fi .ft R .LP See the \\fBoptions\\fR manual entry for details on the standard options. .. '\" # OP - start of full description for a single option .de OP .LP .nf .ta 4c Command-Line Name: \\fB\\$1\\fR Database Name: \\fB\\$2\\fR Database Class: \\fB\\$3\\fR .fi .. '\" # CS - begin code excerpt .de CS .RS .nf .ta .25i .5i .75i 1i .. '\" # CE - end code excerpt .de CE .fi .RE .. .de UL \\$1\l'|0\(ul'\\$2 .. .TH "nxsh" 1 2.0 nxsh "" .BS .SH NAME nxsh \- Simple shell containing NSF/NX interpreter .SH SYNOPSIS \fBnxsh\fR ?\fIfileName\fR? .sp .BE .SH DESCRIPTION .TP \fBnxsh\fR ?\fIfileName\fR? \fBnxsh\fR is a shell-like application that reads NX and Tcl commands from its standard input or from \fIfileName\fR and evaluates them. If invoked without \fIfileName\fR, then it runs in REPL mode, reading commands from standard input and printing command results and error messages to standard output. It runs until the exit command is invoked or until it reaches end-of-file on its standard input. .sp \fBnxsh\fR can be used like \fBtclsh\fR to make NX scripts directly invokable from the shell, by providing the following first line ("shebang") in the respective script: .CS #! /usr/bin/env nxsh .CE .IP A (more portable) alternative is: .CS #! /bin/sh # the next line restarts using nxsh \\ exec nxsh "$0" "$@" .CE .PP .SH COPYRIGHT .nf Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). .fidoc/nxsh.man000066400000000000000000000022701242365656200133140ustar00rootroot00000000000000[comment {-*- tcl -*- nxsh manpage}] [manpage_begin nxsh 1 2.0.0] [copyright {2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT).}] [titledesc {Simple shell containing NSF/NX interpreter}] [description] [list_begin definitions] [call [syscmd "nxsh"] [opt [arg fileName]]] [syscmd "nxsh"] is a shell-like application that reads NX and Tcl commands from its standard input or from [arg fileName] and evaluates them. If invoked without [arg fileName], then it runs in REPL mode, reading commands from standard input and printing command results and error messages to standard output. It runs until the exit command is invoked or until it reaches end-of-file on its standard input. [para] [syscmd "nxsh"] can be used like [syscmd "tclsh"] to make NX scripts directly invokable from the shell, by providing the following first line ("shebang") in the respective script: [example { #! /usr/bin/env nxsh }] A (more portable) alternative is: [example_begin] #! /bin/sh # the next line restarts using nxsh \ exec nxsh "$0" "$@" [example_end] [list_end] [manpage_end] doc/nxwish.1000066400000000000000000000144601242365656200132450ustar00rootroot00000000000000'\" '\" Generated from file 'nxwish.man' by tcllib/doctools with format 'nroff' '\" Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). '\" '\" The definitions below are for supplemental macros used in Tcl/Tk '\" manual entries. '\" '\" .AP type name in/out ?indent? '\" Start paragraph describing an argument to a library procedure. '\" type is type of argument (int, etc.), in/out is either "in", "out", '\" or "in/out" to describe whether procedure reads or modifies arg, '\" and indent is equivalent to second arg of .IP (shouldn't ever be '\" needed; use .AS below instead) '\" '\" .AS ?type? ?name? '\" Give maximum sizes of arguments for setting tab stops. Type and '\" name are examples of largest possible arguments that will be passed '\" to .AP later. If args are omitted, default tab stops are used. '\" '\" .BS '\" Start box enclosure. From here until next .BE, everything will be '\" enclosed in one large box. '\" '\" .BE '\" End of box enclosure. '\" '\" .CS '\" Begin code excerpt. '\" '\" .CE '\" End code excerpt. '\" '\" .VS ?version? ?br? '\" Begin vertical sidebar, for use in marking newly-changed parts '\" of man pages. The first argument is ignored and used for recording '\" the version when the .VS was added, so that the sidebars can be '\" found and removed when they reach a certain age. If another argument '\" is present, then a line break is forced before starting the sidebar. '\" '\" .VE '\" End of vertical sidebar. '\" '\" .DS '\" Begin an indented unfilled display. '\" '\" .DE '\" End of indented unfilled display. '\" '\" .SO '\" Start of list of standard options for a Tk widget. The '\" options follow on successive lines, in four columns separated '\" by tabs. '\" '\" .SE '\" End of list of standard options for a Tk widget. '\" '\" .OP cmdName dbName dbClass '\" Start of description of a specific option. cmdName gives the '\" option's name as specified in the class command, dbName gives '\" the option's name in the option database, and dbClass gives '\" the option's class in the option database. '\" '\" .UL arg1 arg2 '\" Print arg1 underlined, then print arg2 normally. '\" '\" RCS: @(#) $Id: man.macros,v 1.1 2009/01/30 04:56:47 andreas_kupries Exp $ '\" '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. .if t .wh -1.3i ^B .nr ^l \n(.l .ad b '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ta \\n()Au \\n()Bu .ie !"\\$3"" \{\ \&\\$1 \\fI\\$2\\fP (\\$3) .\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n .\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. .AS Tcl_Interp Tcl_CreateInterp in/out '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ .\" Draw four-sided box normally, but don't draw top of .\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .if !"\\$2"" .br .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ .\" Draw three-sided box if this is the box's first page, .\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .nf .sp .. '\" # DE - end display .de DE .fi .RE .sp .. '\" # SO - start of list of standard options .de SO .SH "STANDARD OPTIONS" .LP .nf .ta 4c 8c 12c .ft B .. '\" # SE - end of list of standard options .de SE .fi .ft R .LP See the \\fBoptions\\fR manual entry for details on the standard options. .. '\" # OP - start of full description for a single option .de OP .LP .nf .ta 4c Command-Line Name: \\fB\\$1\\fR Database Name: \\fB\\$2\\fR Database Class: \\fB\\$3\\fR .fi .. '\" # CS - begin code excerpt .de CS .RS .nf .ta .25i .5i .75i 1i .. '\" # CE - end code excerpt .de CE .fi .RE .. .de UL \\$1\l'|0\(ul'\\$2 .. .TH "nxwish" 1 2.0 nxwish "" .BS .SH NAME nxwish \- Simple windowing shell containing NSF/NX interpreter .SH SYNOPSIS \fBnxwish\fR ?\fIfileName\fR? .sp .BE .SH DESCRIPTION .TP \fBnxwish\fR ?\fIfileName\fR? \fBnxwish\fR is a shell-like application including Tcl and the NX extension as well as the Tk toolkit. \fBnxwish\fR creates a main window and, then, reads commands from its standard input or from \fIfileName\fR and evaluates them. If invoked without \fIfileName\fR, then it runs in REPL mode, reading commands from standard input. \fBnxwish\fR will continue processing commands until all windows have been deleted or until end-of-file is reached on standard input. .sp \fBnxwish\fR can be used like \fBwish\fR to make NX scripts directly invokable from the shell, by providing the following first line ("shebang") in the respective script: .CS #! /usr/bin/env nxwish .CE .IP A (more portable) alternative is: .CS #! /bin/sh # the next line restarts using nxwish \\ exec nxwish "$0" "$@" .CE .PP .SH COPYRIGHT .nf Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). .fidoc/nxwish.man000066400000000000000000000024301242365656200136520ustar00rootroot00000000000000[comment {-*- tcl -*- nxwish manpage}] [manpage_begin nxwish 1 2.0.0] [copyright {2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT).}] [titledesc {Simple windowing shell containing NSF/NX interpreter}] [description] [list_begin definitions] [call [syscmd "nxwish"] [opt [arg fileName]]] [syscmd "nxwish"] is a shell-like application including Tcl and the NX extension as well as the Tk toolkit. [syscmd "nxwish"] creates a main window and, then, reads commands from its standard input or from [arg fileName] and evaluates them. If invoked without [arg fileName], then it runs in REPL mode, reading commands from standard input. [syscmd "nxwish"] will continue processing commands until all windows have been deleted or until end-of-file is reached on standard input. [para] [syscmd "nxwish"] can be used like [syscmd "wish"] to make NX scripts directly invokable from the shell, by providing the following first line ("shebang") in the respective script: [example { #! /usr/bin/env nxwish }] A (more portable) alternative is: [example_begin] #! /bin/sh # the next line restarts using nxwish \ exec nxwish "$0" "$@" [example_end] [list_end] [manpage_end] doc/object-class.graffle000066400000000000000000000430631242365656200155450ustar00rootroot00000000000000 CanvasColor w 1 ColumnAlign 1 ColumnSpacing 36 CreationDate 2009-12-29 16:06:59 +0100 Creator Gustaf A. Neumann GraphDocumentVersion 4 GraphicsList Class LineGraphic Head ID 14 ID 17 Labels Label Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\fs18 \cf0 instance of} LabelVisible YES Offset 0.0 Position 0.52006572484970093 Points {329, 92.8868} {367, 104} {375, 139} {329, 174.982} Style stroke HeadArrow FilledArrow LineType 1 Pattern 1 TailArrow 0 Tail ID 13 Class LineGraphic Head ID 14 ID 16 Labels Label Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\fs18 \cf0 instance of} LabelVisible YES Offset 0.0 Position 0.52006572484970093 Points {329, 229.317} {429, 231} {425, 177} {329, 206.955} Style stroke HeadArrow FilledArrow LineType 1 Pattern 1 TailArrow 0 Tail ID 14 Class LineGraphic Head ID 13 ID 15 Points {261, 172.173} {261, 122} Style stroke HeadArrow UMLInheritance LineType 1 TailArrow 0 Tail ID 14 Bounds {{193, 172.173}, {136, 112}} Class MultiTextGraphic FitText Vertical Flow Resize ID 14 ListOrientation Vertical Style fill GradientAngle 304 GradientCenter {-0.294118, -0.264706} TextList Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\b\fs24 \cf0 ::xotcl2::Class} Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\fs24 \cf0 \ } Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\fs24 \cf0 create\ method\ object\ parameter\ ...} TextPlacement 0 Bounds {{193, 24}, {136, 98}} Class MultiTextGraphic FitText Vertical Flow Resize ID 13 ListOrientation Vertical Style fill GradientAngle 304 GradientCenter {-0.294118, -0.264706} TextList Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\b\fs24 \cf0 ::xotcl2::Object} Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\fs24 \cf0 \ } Align 0 Text {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural \f0\fs24 \cf0 info\ destroy\ method\ ...} TextPlacement 0 GridInfo GridSpacing 14.17322826385498 MajorGridSpacing 10 GuidesLocked NO GuidesVisible YES HPages 1 ImageCounter 1 IsPalette NO Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo ChildOrdering 0 LinksVisible NO MagnetsVisible NO ModificationDate 2009-12-29 16:17:47 +0100 Modifier Gustaf A. Neumann Orientation 2 PageBreaks YES PageSetup BAt0eXBlZHN0cmVhbYED6IQBQISEhAtOU1ByaW50SW5mbwGEhAhOU09iamVjdACFkoSE hBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCUhAFpFpKEhIQITlNT dHJpbmcBlIQBKxROU1ZlcnRpY2FsUGFnaW5hdGlvboaShISECE5TTnVtYmVyAISEB05T VmFsdWUAlIQBKoSXlwCGkoSZmRlOU1ByaW50UmV2ZXJzZU9yaWVudGF0aW9uhpKakoSZ mRZOU0hvcml6b250YWxseUNlbnRlcmVkhpKEm5ydlwGGkoSZmQhOU0NvcGllc4aSoJKE mZkUTlNWZXJ0aWNhbGx5Q2VudGVyZWSGkqCShJmZC05TRmlyc3RQYWdlhpKgkoSZmQ9O U1NjYWxpbmdGYWN0b3KGkoSbnISEAWSdAYaShJmZDU5TSm9iRmVhdHVyZXOGkoSWlwCG koSZmQtOU1RvcE1hcmdpboaShJucpp0AhpKEmZkQTlNKb2JEaXNwb3NpdGlvboaShJmZ D05TUHJpbnRTcG9vbEpvYoaShJmZD05TUGFnZXNQZXJTaGVldIaSoJKEmZkPTlNQcmlu dEFsbFBhZ2VzhpKakoSZmQ5OU0JvdHRvbU1hcmdpboaShJucpp0AhpKEmZkKTlNMYXN0 UGFnZYaShJucnZeCf////4aShJmZDU5TT3JpZW50YXRpb26GkpqShJmZFU5TSG9yaXpv bmFsUGFnaW5hdGlvboaSmpKEmZkNTlNSaWdodE1hcmdpboaShJucpp0AhpKEmZkMTlNM ZWZ0TWFyZ2luhpKEm5ymnQCGkoSZmQtOU1BhcGVyTmFtZYaShJmZBmlzby1hNIaShJmZ Dk5TUE1QYWdlRm9ybWF0hpKEhIQNTlNNdXRhYmxlRGF0YQCEhAZOU0RhdGEAlJeBFSeE B1s1NDE1Y108P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJVVEYtOCI/Pgo8IURP Q1RZUEUgcGxpc3QgUFVCTElDICItLy9BcHBsZS8vRFREIFBMSVNUIDEuMC8vRU4iICJo dHRwOi8vd3d3LmFwcGxlLmNvbS9EVERzL1Byb3BlcnR5TGlzdC0xLjAuZHRkIj4KPHBs aXN0IHZlcnNpb249IjEuMCI+CjxkaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFn ZUZvcm1hdC5QTUhvcml6b250YWxSZXM8L2tleT4KCTxkaWN0PgoJCTxrZXk+Y29tLmFw cGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk+CgkJPHN0cmluZz5jb20uYXBwbGUu am9idGlja2V0PC9zdHJpbmc+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0 ZW1BcnJheTwva2V5PgoJCTxhcnJheT4KCQkJPGRpY3Q+CgkJCQk8a2V5PmNvbS5hcHBs ZS5wcmludC5QYWdlRm9ybWF0LlBNSG9yaXpvbnRhbFJlczwva2V5PgoJCQkJPHJlYWw+ NzI8L3JlYWw+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFn PC9rZXk+CgkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQk8L2RpY3Q+CgkJPC9hcnJh eT4KCTwvZGljdD4KCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1Pcmll bnRhdGlvbjwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0 LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmlu Zz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJ PGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3Jt YXQuUE1PcmllbnRhdGlvbjwva2V5PgoJCQkJPGludGVnZXI+MTwvaW50ZWdlcj4KCQkJ CTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxp bnRlZ2VyPjA8L2ludGVnZXI+CgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJ PGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTVNjYWxpbmc8L2tleT4KCTxk aWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk+CgkJ PHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc+CgkJPGtleT5jb20uYXBw bGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCTxhcnJheT4KCQkJPGRpY3Q+ CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNU2NhbGluZzwva2V5 PgoJCQkJPHJlYWw+MTwvcmVhbD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tl dC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCTwvZGlj dD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZv cm1hdC5QTVZlcnRpY2FsUmVzPC9rZXk+Cgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5w cmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc+Y29tLmFwcGxlLmpvYnRp Y2tldDwvc3RyaW5nPgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJy YXk8L2tleT4KCQk8YXJyYXk+CgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJp bnQuUGFnZUZvcm1hdC5QTVZlcnRpY2FsUmVzPC9rZXk+CgkJCQk8cmVhbD43MjwvcmVh bD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4K CQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9k aWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTVZlcnRpY2FsU2Nh bGluZzwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNy ZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4K CQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJPGFy cmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQu UE1WZXJ0aWNhbFNjYWxpbmc8L2tleT4KCQkJCTxyZWFsPjE8L3JlYWw+CgkJCQk8a2V5 PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9rZXk+CgkJCQk8aW50ZWdl cj4wPC9pbnRlZ2VyPgoJCQk8L2RpY3Q+CgkJPC9hcnJheT4KCTwvZGljdD4KCTxrZXk+ Y29tLmFwcGxlLnByaW50LnN1YlRpY2tldC5wYXBlcl9pbmZvX3RpY2tldDwva2V5PgoJ PGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTUFkanVzdGVk UGFnZVJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlj a2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9z dHJpbmc+CgkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tl eT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQu UGFnZUZvcm1hdC5QTUFkanVzdGVkUGFnZVJlY3Q8L2tleT4KCQkJCQk8YXJyYXk+CgkJ CQkJCTxyZWFsPjAuMDwvcmVhbD4KCQkJCQkJPHJlYWw+MC4wPC9yZWFsPgoJCQkJCQk8 cmVhbD43ODM8L3JlYWw+CgkJCQkJCTxyZWFsPjU1OTwvcmVhbD4KCQkJCQk8L2FycmF5 PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4K CQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJ CTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0 ZWRQYXBlclJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQu dGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0 PC9zdHJpbmc+CgkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8 L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q+CgkJCQkJPGtleT5jb20uYXBwbGUucHJp bnQuUGFnZUZvcm1hdC5QTUFkanVzdGVkUGFwZXJSZWN0PC9rZXk+CgkJCQkJPGFycmF5 PgoJCQkJCQk8cmVhbD4tMTg8L3JlYWw+CgkJCQkJCTxyZWFsPi0xODwvcmVhbD4KCQkJ CQkJPHJlYWw+ODI0PC9yZWFsPgoJCQkJCQk8cmVhbD41Nzc8L3JlYWw+CgkJCQkJPC9h cnJheT4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9r ZXk+CgkJCQkJPGludGVnZXI+MDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJPC9hcnJh eT4KCQk8L2RpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNUGFw ZXJOYW1lPC9rZXk+CgkJPGRpY3Q+CgkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tl dC5jcmVhdG9yPC9rZXk+CgkJCTxzdHJpbmc+Y29tLmFwcGxlLmpvYnRpY2tldDwvc3Ry aW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+ CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBh cGVySW5mby5QTVBhcGVyTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc+aXNvLWE0PC9zdHJp bmc+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5 PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCQk8L2RpY3Q+CgkJCTwvYXJyYXk+ CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRq dXN0ZWRQYWdlUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmlu dC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNr ZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJh eTwva2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5w cmludC5QYXBlckluZm8uUE1VbmFkanVzdGVkUGFnZVJlY3Q8L2tleT4KCQkJCQk8YXJy YXk+CgkJCQkJCTxyZWFsPjAuMDwvcmVhbD4KCQkJCQkJPHJlYWw+MC4wPC9yZWFsPgoJ CQkJCQk8cmVhbD43ODM8L3JlYWw+CgkJCQkJCTxyZWFsPjU1OTwvcmVhbD4KCQkJCQk8 L2FycmF5PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8 L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2Fy cmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYXBlckluZm8uUE1V bmFkanVzdGVkUGFwZXJSZWN0PC9rZXk+CgkJPGRpY3Q+CgkJCTxrZXk+Y29tLmFwcGxl LnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk+CgkJCTxzdHJpbmc+Y29tLmFwcGxlLmpv YnRpY2tldDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRl bUFycmF5PC9rZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmFw cGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJ CQk8YXJyYXk+CgkJCQkJCTxyZWFsPi0xODwvcmVhbD4KCQkJCQkJPHJlYWw+LTE4PC9y ZWFsPgoJCQkJCQk8cmVhbD44MjQ8L3JlYWw+CgkJCQkJCTxyZWFsPjU3NzwvcmVhbD4K CQkJCQk8L2FycmF5PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0 ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJ CQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYXBlcklu Zm8ucHBkLlBNUGFwZXJOYW1lPC9rZXk+CgkJPGRpY3Q+CgkJCTxrZXk+Y29tLmFwcGxl LnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk+CgkJCTxzdHJpbmc+Y29tLmFwcGxlLmpv YnRpY2tldDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRl bUFycmF5PC9rZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmFw cGxlLnByaW50LlBhcGVySW5mby5wcGQuUE1QYXBlck5hbWU8L2tleT4KCQkJCQk8c3Ry aW5nPkE0PC9zdHJpbmc+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0 YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCQk8L2RpY3Q+ CgkJCTwvYXJyYXk+CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tl dC5BUElWZXJzaW9uPC9rZXk+CgkJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJCTxrZXk+ Y29tLmFwcGxlLnByaW50LnRpY2tldC50eXBlPC9rZXk+CgkJPHN0cmluZz5jb20uYXBw bGUucHJpbnQuUGFwZXJJbmZvVGlja2V0PC9zdHJpbmc+Cgk8L2RpY3Q+Cgk8a2V5PmNv bS5hcHBsZS5wcmludC50aWNrZXQuQVBJVmVyc2lvbjwva2V5PgoJPHN0cmluZz4wMC4y MDwvc3RyaW5nPgoJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnR5cGU8L2tleT4K CTxzdHJpbmc+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXRUaWNrZXQ8L3N0cmluZz4K PC9kaWN0Pgo8L3BsaXN0PgqGkoSZmQtOU1BhcGVyU2l6ZYaShJychIQMe19OU1NpemU9 ZmZ9oYECU4EDSoaShJmZEk5TUmV2ZXJzZVBhZ2VPcmRlcoaSmoaG ReadOnly NO RowAlign 1 RowSpacing 36 SheetTitle Canvas 1 SmartAlignmentGuidesActive YES SmartDistanceGuidesActive YES UseEntirePage VPages 1 WindowInfo CurrentSheet 0 Frame {{464, 180}, {537, 755}} ShowRuler ShowStatusBar VisibleRegion {{0, 0}, {522, 657}} Zoom 1 doc/object-class.png000066400000000000000000001415111242365656200147200ustar00rootroot00000000000000‰PNG  IHDR^X<ÌhiCCPICC Profilex­—y8ÔÝûÇÏÌ0–ƾo‘}ßweß÷=k–±¯ÃØ ‰Y’,Ù÷„!$DB²D<Ù*Q)![”5¿zô\¿?¾×÷Ÿï}]sל¹Ï}îÏç}æ>× Ù€ãøúšj©²YYÛ°¼0€Ô€P;¡±cc}Èå?Ø÷)Ȳq¡“X(–Ë·™;ò©Ô-÷®/ÓŸÿ‹N§I¡ € BÔî¿Yù„³ù ‡a‚ F{8¹@|bÁ@sS5ˆïALêþ›[OØù7œpÚýdík”~.ž~¬B¬èâŠEC?Ÿìëà‚EûB|âo¾¾þP|²“ø¼hL ´–¬bΓ÷ç 8œùß9›Xj£¡ôÿã†r¡~ @Çì¿s[¦¿ÞŒvë&!þ+ ¥ þÜññ7”[G·ŽJÊ@¼à©:80ä—/ô 0hçÿöý÷3ÿ³‰ Ó‡K#hñøð½ÏˆúÎØ“’âÈ))¨žÐ8Ó‘ÐW0J15±pŸue½Ë6Â8ä9ùš¸7xÅøüùk…8„mDRDÛÅ–$È$%¤,¥ýdnËvÉ­+ПW»à®˜ T¡Ü¥2¥º¢¶¯A¨I®E£Í¨Ã¢Ë¬Ç¢Oo@gHgÄk¬dbjêbjž`‘iYr±Öê‘u·Í°í´Ý'ûKGŽ$N,ÎBheSW 7cw} Oe/%ïó>²¾2~2þ²…Å@M¬qM°[HXhbXnø½ÜåûWJ# ¢r¢ó¯æÅ^+­Š«¹Ž‹¯K¨»QŸXT‘œ3=%650Í-Ýû–_övHfè ,¿lχ\³»šy²ù<T? —‹Æ‹ÛJŠJo”aË1þ•~U~÷¼«Ýï;×ØÖšÕéã4êdˆ6ò5q6³>dnazÄØJѺÛ6Ú^ü8¨C«“¡sõIWWæSŸnõ–ž½Þ©gû2Ÿ‡ô[( ò ‘ ­¼èNA¿”¥ý:öü¯¢W‘ã¶²“4“ߦ†¦Ë^G¼1~Ëùvg¦÷]ölðœå{™´¶?ŽÍßûýÙbáàËÐbözYxy÷kïJòªùóÚÂzÏFÉ·«›öËmÑn}Ûîß)üôCg—ew}¯g?ëÀãðüåÑâÏŽc÷ãcHfØ{8Q÷yŽ …ˆ8%K²H–I¡B¹FE+E7Ä`ÈØÍÌÅbw6µžm–”C•ÃUÊ=Í‹âSâ÷(Ú¡U³ÇJ$IJ=’ž–Ù—£“—Q0>ïz!:™ÊE*•ªÕjÕêUešEZ¹Ú:7t¯êEèÇd–Õ?6yfúÂlÌ|ÒâµåÌÅ9«ÖŸmmWí¶íÐ) r>ã¼^r™rískq¯òÈöLôŠôÆúxú:úÙù[c,,m±ŽAhè8‡š„©„‹Gð\f½BIEMv•$†ø~,,ö0nûúJüû„±]‰µIw“‹o¥”¦–¤§çÝÊÈH¾•‰¹ã”eœ­˜ÃŸK•{tw1o$¿©  0¾(®øZÉÕÒè²Ëå¡•žUŽ÷¬ªîkÕ(ÖJÕ á¸êÏ6Ð? n$kÂoZmî{˜ÓâÿH£•©u³m ½ôqT‡m§ü¦'Ç]Ÿžt7÷ä÷ÞxÖçö\·Ÿ{ ¼lºûâʰãˆúKQªÑý±O ¼ÂgLO^œ’ž¦žÞx=üæéÛÆ™ÊwY³ñsAï>h›§Ÿ?ø4÷¹s¡àËåEë%™eêå¯#+¸Õ’µüõ¬Ôoñ›‘n¹l[îh}—þÁ¹K¾ûsouÿõAßáÃ£ŠŸ™Ç6'úƒXX<QŠ·„T#h%R$E¹’“åRP¶S+ÑàèÎч2ô03˳xœÍbd;då¸Ä™Äõˆ{ž—˜O”ß@ÀCðŠP²p–H¡h‰X±x¡D¾dT‘t±Ll®\†ü …Ëç}.X+j( )S)ï¨L«¶«¨GiØi*i±i“hé¬êÎè é?2(7L7Š0v512•7ã4'7?²Xµœ½8bõĺÁ¦Ð6ÖÎÞ^öå¥u‡Ç:§4ç@´•‹²«€½;û¾Çºç‚ל÷kŸW¾Ã~ÏüÛ1¸€ŠÀBl^Pnp^HahiXUxuÄ}¨.ÔDÞ‹*‡êBZLìµ€Xç8Ûë&ñ: Š7ÄÏ%‘%í'º9”‚KMOäëßâÍ€g¼»Ý”™t¥’Í•Cš³›ûñî`^}~VAx¡}‘J1W QÉzéxY{yqEb%¶êÒ=j‘û¤÷jZjSêœpÒõ¨úÏ Oä7F4Ù5«<äi!kÙ{´Ø:ÑÖÛÞúø~Gj§ç•.¦®ïOÇ»ôÜêÅ<3é{Nù|³l fðúý ÉaÂᙑû/cGÝÇŒÿ’Å9N2¾=ñz²}ª`:êµÝ¹·4o¿Í ¾+™˜3}Ï÷öáÍÇ¡ùÎOuŸ³¢¿x,š/©-Ë|•\Q\½¸³ÞñlóΖêïï}Ö£€ýß}'wR€2w¬Äп@)ÜúP1`L€¹€ S8jÀ†‡Á?÷toÀÄ€ 0Î âA.¨Ý`¬Áð ú"3‚yÁ`å°Ø<ÎׇÁ à/p„"QƒXÀcųÇËÛÁg·Ç/Á_D #C‘=dŽ„HÂK„­DTDÁDÄÄ·ˆ7ÏXœiCCÝDí‘x’Ì’š’¾ Ó&ë'×#EaK±HJEHUD-Iý‚ÆŠæ mŠ®„^Ž~’!‘’ñ!“5Ó1s‹ËÞÙ VÖ#¶šs¶ìÄì—9U¸¹Æ¹ yG!õ|ì…E¬F£â¯¤¥ž¤Q Ù¬5¦½¡³¥Ó?cÀl(l¤ Õ³XÓ³ „¥äEO«Jë[;oû‡—ÕœËÐS®DnjîQ^0o-Ÿßiÿs¿€N,*È.¸6ä0Ì ¼äò™+9QbÑ}1×â²âEúm“ÖoF§’¦ßâÎhÉT¼Ó“mœóãnM>º¤¨­Ä¥Œ´¼¥Òþ²º¾Æ¡Ž7ØרÙLòðÃ#\›iûQGÙ«§DÝ}½‰}ýt+C½Ãå/¯ù¿²šPŸ’{Íÿ–rfl6ù½îGòùÉÏE_0Kš_YWŽÖæ66q[wvâ~øïY¨‰þÒ È ¤¿ÐÖÀD‚tPÚÀ(ø~Bú‹Ã aÞ°$X-l¶g‚k±ðrø…PGD"!¶ðDñ0xxßñðcñG HOäc /‚~BÂ$¿‰ìˆ‰eˆ+Ïй‰ÂCE£~’Äf±‘5‘ë’¡ˆ£ä¤   f î£ñ %¥m¡s¢'¥ï‚þï|ŒóL…̶,L,3g XØØÙ–ÎáØÃ8ô9E¸¨¹¹?ñ ñ6òåñ_ð4’f‹,Š‹5ˆgIDIzJYKëË(ËJÈqÈÓ(à+ìœ_¸0­8¬Ô§üTå±j§Z·ú wšÚÄ:œºRzú.1†¥FCÆßMyÍÌó,Þ_ä¶ ±²e³‹¶ÿà ä˜íô-îã:á.àqÝsÁ[Óç¾™0f6P‹ ¦ ‰ ]7ޏb¹Ãy­+ÎæúVB|"CRÕM‰”Î4­ô¡ “Û“w̲¦r‚ïòåÍdé–Ö•;URW UÇÖ¨Ôâ&*¯6;µ´žk›}|§Ó¤‹üéDOñ³Àçê¬C°Ë#£ýuŽ7O6LãÞÔÍ„Î*¿§þð}þíçÇ_r–0_eWÖ×Ò6˜¿emoYn'ìToÿѼ[±¾¯¹ÿó êÐøpæÈùhùçcõý÷K¿z |O'¨wúßš¯OðiLJ(2ÊÏÙÐ ¡Ï&Èø¤üÅØ3SvóÔÔ9e'u½SŽðP3eW? ³SÆøüêoÿÙKõ¿+VãO„‡ùÅSÿÀ`S‹Söö×ûãïâªþ'7?CèNý³gΟü'0Ný»Ï„<’€|Ÿênî;þŸ¹†A=(jþ˜ð@Ow 6¨ËvdÓñC ²‰‹ŠÊÿ*7Úñ›¸ IDATxì \TeÚÿ5èÀ#XhhAÅdh…®ÃÓjÖÚøRɦÖ*檡®R¶À“ô²(O˜‰Ö¦É†µ©dåjûglu+41¡„Zaƒ1&™â͹gî93 ÌÌËïþè™ëÜçºß¾‡k~sŸ×mmm€€€€‘ÀFŸ   zFü€€€€H£¬€€€¤   `FÒh†+   iÄ߀€˜€4šáÀ €€@ñ7  f f8°  Fü €€€€H£¬€€€¤   `FÒh†+   iÄ߀€˜€4šáÀ €€@ñ7  f f8°  Fü €€€€H£¬€€€¤   `FÒh†+   iÄ߀€˜™­õíJ[[[ß6ˆÖ@ÀU 0ÀU»Ž~ƒ€ èOi섳s>Øêf ~n¶C1—&0 ¨}6sÚ;¸4etì!`¡Ž«¬‹L‹U{Z€€M}4k´:ZmŸcÑW ‹­X7#@"gñ7o!{´JIêC«nÃ'!Ð[Ò( `6TÊá™ÌàKž/…ò믿JWaƒ€{¸â +×ĉj¨×?&Š\ y¾4vøV÷…Ñ@è-i¤®Kƒ–l¶Ê–${Ìàn\-òûšg ÀC€+‰%…_erÈ–sãEx)W'ƒþƒ@ÿè•Y#“7>0ZµHì2—FéV*E«|É*a9¼B à˜È±±pÁ£U&uÒ% Ê!dùÒRds–%€@OôŠ4R‡(PY¬2ƒ–\ÉhllܱcGSSÛÊüùRjÝ~•eb ®N€DN:¾Ê ¾$ÃÏÏoÞ¼yC† !Z¥$=7IqÄ2¥µÁè6K#—:Ö! Eäê¸}ûögŸ}¶ÛFAð@MË—/§s]drÈæ‘ ù0äƒ!ƒ€ 8R¹.2ƒ/™"Òò—_~aö?þHc˜ÆšñÙ(¦XøX´EZÈÂ|+4’£€]%ÐÉaW«2øS”’ri$ƒ4‘BšK#m%»›µ÷i1Ý‘ «_sþøSŠö®Œ¶Z—ÍLm审Š8[n³ÂïÚ]u|fˆa×ò3•ð2™6­Ë6=ºç ýáDvA+«ŒM[Û½Zܱ“F>2EYlvHKfp Ý&`¸Ô­Û奙²ß³lIÁL‰i!³Ié—/ÙÒ‚Nk›É†c~E4n_2_2^EbR¢R²^°jáÁZ$Ã~³~óBs]T%(.«©«)+ÚmÖ†P2뉷4Ö*¶1”0=ELIéÄøZ«¡çy2Ù@^‰ß@Ç@纴AQC±ÃÀH#Ktn´êÒ#EçA ß 8þ«‡Â’ý˜eK¦ˆL)°)±GŽ\§©,;Ûê%ÎyZ[ýF….8ÑV–ŸiÚçSãºÊ£ûvýí£’ï…Áƒ…K—„¢'=÷P?§ÕÔ7ž=]À»ùÝ·Uµ#üäþAþ™®±úð¾}ŸWÔ r:Ò*1.zò¶Îj«Ž¨yªÂš¿)ƒdÂúµ¯2Ì”JÞýgõ”9]ž ÖÙ¶ÊT³ J/Ì_«d€ƒfn,lŽ]{ÿ¬L£GÁ¢­GU+£xWDÃÇ[&Ô—)øô«¾ƒÎk¿[5=ZròQ4}áãZQ¸er}Y]ãÑÂ}þãë!!!Úº¦€›ïˆ½#4ÀÊèΡiÏž©æ½ú®úLmýp™·® &ljHKºâ†rˆÙ_<Ÿœ! îþÞì‰MÁI¿g/_¾¬Õj/]ºDOºihh¨¯¯¯­­­®®®¨¨(///))ùòË/‹ŠŠ,X@}¥ùGç-VíNQ(•*•ŠnóHÙ]apn­H¡ ÊÕ/S*èäKs±R:tÕÖfѵ®0Eš^Ô`¨¡¹,]%Ýb²·éOæ´µ•äXõP±ªÉ!߬r^EBVa‹¡ª$žçgë[o­Ú­0f©²ŠŽm ‡ÓÙÏ·>ü¶6kù-ÛMMÑm1YÆ¡òȨIám ‚"å°¸­y«d  •’wÃ`¨Ò+øxZ$œ¦&ꊷ·+¦/Ÿ~€eý°MZ¿¾CR¤I‡á6E ± ¨ùì³Ï(‚(Ž(š(¦(²(¾(Ê(Ö(â(î(ú(Ùi …!ƒ€£ð)ñ{¨gŸÝb“E6M¤på‰òíi§¡¦˜_Æ"Ìh2Ñ6ð Gü.héʑȼ”àØ4ƒCÁ¢—ÌNi^ÏsH ’¬‰ö×;hKû*¬\¾)Î^4þdýáµ1:ë§Ò çåNlšµÌ0Ë34jüÈ]{É·lçÂ0c†ù'«W¡JJ†óæÛ¼Z™`Y¾ XË×]–!MJ‰‡Ê«gFÐüu‰iq†+LKmb,ÜJŒgVM% ’GjN® ³‚ù†ë²“õG6N\ų¥F^òTÜtêÔ6´8ÁÀWZ‹ “wå¨yYwZ£¨¡ƒ.4Yd‰Mkú›7xr§Qc, Ð/s®‘Å$OŠdi"ìª4ÊÓe,¦/Mi®ÀŽ– Aʵù &€iÈܶa½AôىﮟÂ~ÉxBª‹Šø¤¬¬ÉÄIP'OÜQ©ó`R¢t"&¨’“îFµÔ«ç›é¢"!1A2ò½RÞÁéBY蜓è¤~±e—Ïú­O™ÔÅÇ×ð{E:PÓðÁJ¾®NrìWõÀíLÈL@˜|ëXS–ŸAÛL9„B/å!%ÉÖJÜDÓðC¥ú3]T&$&Æ+M¾«ÆLýº=ÐdÃLI’URRâŒ[¤£7UîiE Š&ip‘ÍãŽaa«ž†ãGpŒ4Ro¤¡Èm®lâHÌ » ™ çŒúr5¦ÂMÆ+Gäsþ\dúF/É\´Ê¤€YÅ©aLn¾˜¦æ¥“òËNîÌX¹2uok•ôëüû¢çel|Kr°1þùM3–Òý¥¹ì~ }=Ф²–“[6n9YW$ÒÜÃ’IoϺѸcÅÝ’Ó„ŠGï 78v0|¡}¾¶é”¤òŽ&YòcMˆ ª,õ›†•PÔprïν­ %IµÏüË'¢¾IÚÍú#y™A&é3•ð °–Kb6em~âûqÙ¦IÍêö¬Sò’­Æ9&å(gÜÅU•VeÁ7Ó´Ïx•ègÿÖÔÆ ^ÒxäV’A¦êw·š.òŒ˜—±wžùöN×*Õ›fÆ.“ôUP$æ°6ÆT¨ƒá íóå~£LÅ:´´çÊÔ|£*d¨åοþÆ@Ó)OYð×ÓÁTƒÿÙ&)KA6ЄP˜Ìk—–jÜ#h’z=Τ¨aáC?yL1ƒG3<  Ž&à°ª¼cQJ¡Ërø^ oÊäþ5äwÄÜaV¡âã­íúìs'ò­ê‹¤\ù°L‡G 3i/Á Yð(%ÏêèÐ%wèÀ Ç³ÅŽ6×Å„œÃE瘤¦ƒ’Ö³eÒC¥û¿¢I¯•TwêkSnÓeóñÓ–QC¥ÍˇÞdb8§k*.Z^¾W[äXY½@ 9š•º=*‹¢†b‡Ç +_<î<Š  ½D ÝDÀíð(åÑKÌËtD;íêh<òD\®YnÞüõ ¿MUÆŽš@s2ÃÌP¥)¬$i(6N‘á¬xá«d«ÉÔÕœUóµKyÑi뛵2™ ÓÉ‚:!{tCÜTéQT!~wÙ«3à G}yå]1¤ó7!ùÅ‚å{JeN¬ª>o£éÌ b²ñ(³©™ó !„Ó6œ2$5hm>ËWI9ÿ{ÇÕ—/³ ýSÍÅtùòà›½@ã y¬Aȱæ±p0pp,ÇÏ¥ý“3‹a iúÙK¶Ô­c[G÷i±$=é¤ãÙ:i¶fÇò‰¦S‰ÆJÓbל°vMLÁª·+>ôYÿå~SYe쮟vfÁ®CÕÆLݾ?  Ô/¶”ZkRtÕœØ4~•©)UJ~C=ÔÔª.òq²ûìM íóåÓ$÷Y,úã¶Ü[4èy°+“Õ¦¼ø)·˜V VÞžc¦éfc‰m Ò[\fO{hæÌ9Æ4í¶±co£4ööI·1ŸwZ»®zb&JL íŽ&O„†1ƒ@÷ô¢4²¦nñ–þÔµ§»åÛxÓ›J E´¥ ¼Œ¹^ N/¬ÞóÌ|ÄТ?îb^>áÓ’L³g.ÞV-ŠN}é®9M'ÊT3 7üI.ŒDŸßÜcº5{Ö«õuןøëŸLóÕø»:”ÖêõQËL] Ó•1þ‡vIÒŽêRƒ>Y¾ XÍP.K—ÿÌ]5vñ†•t?xcmåÑŒ¹·M4>nGߺ2çñÓYRÞŸ´Ø9;NÔÒjcùžGÇ›€(ï±¼’©Œ•”]´§¼Q¿ª«ß±büÈÑ£GR=:8ósº~©{К´V'«¼MÏ2(Žèg%"2˜(ò@ó,-ô&Þ’F®´”0³íd鵊‚ébEdF¦ieóÆzõ‚YÙTüöІâ,¾*äÆe¨õßø‚,tq~"Ï/É]4ÒÛ‹®å 4{[…jÝ‚hÑGvõpî[0qè€cW”j…ˆ‡¥w1Lé;60j¾éècÂŒó‰¯EWý¹é˜¦˜»hêÔYq’4~⾳̿ƒá ä,ÏÛÊ"£$wUÔhšÅ =>9ÏÔ;Ú”¿í÷Vg©‚ žL@††Ï2MlaE<]qj%ùGΕè1=¸|èXbá8_r%TÎ#·SIû¡É®òå-©“ÇSgVì(ç9žl8$¦< Æöp°4Z•=.Ì`?{í§ôªa[ã¶E±j“_âs‡úG.’>ë«ÒÓ_(…Îy>?Q2½2•2XY‡7³èÊœ['˜&ˆúÍ%g[¨uå»Û%Ï =1¼xJ¬îtüóœŽN4jj Ítüq}wþâ±°æ°äõ&9E5s$¯×2ÌŒé=bj&~k1MG»*Öì; ”äšž^$fª²/ŽMÛ MeNü¶HZð\“ÿ}É VЫ 6 8X;i-_vâÉ7yùæ6FŒ “™ærÂpºP[úþ"É'ýð:ñ‘Ø>‹³ò%_ôyëÿÊÉúÌÙx¼x»äÌoC™x ¢yeLÏ››ž“¨ä«‚ÐÄì°y[ªçH7°|eâÖªãüeˆ^ƒLýæ­,\óEãñ_VÀÚÒÇøº ëçÛH¬b« ŠYÙZW’eÖgSªÄœ²†ãK£M¤m¾ì¬jI‰ HÈɱĒ’_²sa¤©Šv–,dJasÅÖ$Ó³Œ.ÊœÃU{WÆð_ vBdéælÅ·=kõàOö˲KäÁ´0tèýûÁ{TX˜-ýŒe—‹/Øh¥ÇSúYLìRš1ÑÖ·ß~{ß¾}ôÐäÔÔÔž·ÞÍ´õ¥%ßÖ^üÉËË‹îw  ‹3Þlo^£VÓØÜ¢£›Éº›ÝGò¦¼ô«êzC !a·†…tpœÒ¼Â>XÓ6V—}S~öÜ¥Áþƒé©ÓCF…ÝäßÁq^i‡´•Uu?Ñ˯cÂÌ^ž¡-ë­0œÏUæ4.eªÊJëk˪þÝÒ*\¾tyàÕ×ÝaºÖUZ½`/4-]ïKÏuÐS÷öõ‘s…5«ÌCV(LÒÒÒ¦OŸNGÞ¯ºê*v¦].¦Ab¢ ‚)Ñ_2¥+¯¼’¨ñç¬z" KÀñß8üWmG†ÕAŽ•]µÉ"¢"ìp•ûøË¥"À‹È|Â"cÂøª3rÿȘΦ{õVîjýrY¡±æŒôp°Å22ÿ þÄ¢Ž*§|{¡ÉýÍd¹“=eûÚQXñ|OÁq‚@opÀU“dtÞUæÙ¹¶:'ÒM3†Žž¥æ öÕ Fêsö‘ý!ÙçÝGƒ à ®1Pô²§Ì&ΉM¶ãàlO›DyèÆ~Áîzê.›¦&æ>?3ÄõÆ€ƒ€€}®Ñ¾võ^jµš®/°ßžýHà×Ö;žzj¼ð«p…·|à…/¥Rgðz‡¾Ù#)}ÓZ`úS‰ {@@œŠ@JãäÉ“•J¥Sá@g@À Ь‘~F:aÇÐ%pWý)¤‹8 ê®X— P˜@ÈU€M¸ Ç&"8€€xH£gíoŒ@@À&H£MDpð,FÏÚß-€€€MF›ˆà  àY žµ¿1Z› 6Á@@À³@=kc´  6 @m"‚€€€g€4zÖþÆhA@l€4ÚDÏ"iô¬ýÑ‚€Ø$i´‰  žEÒèYû£°IÒh@@<‹¤Ñ³ö7F   `“¤Ñ&"8€€xH£gíoŒ@@À&H£MDpð,FÏÚß-€€€MF›ˆà  àY žµ¿1Z› 6Á@@À³@=kc´  6 @m"‚€€€g€4zÖþÆhA@l€4ÚDÏ"iô¬ýÑ‚€Ø$i´‰  žEÒèYû£°IÒh@@<‹¤Ñ³ö7F   `“¤Ñ&"8€€xH£gíoŒ@@À&H£MDpð,FÏÚß-€€€MF›ˆà  àY žµ¿1Z› 6Á@@À³@=kc´  6 @m"‚€€€g€4zÖþÆhA@l€4ÚDÏ"iô¬ýÑ‚€Ø$i´‰  žEÒèYû£°IÒh@@<‹¤Ñ³ö7F   `“¤Ñ&"8€€xH£gíoŒ@@À&H£MDpð,FÏÚß-€€€M2›½ç V«SSS{¯~Ô îA€"Å=‚Q€€«èOi<$&W!…~‚€xþ”ÆÉ“'+•Ja‚@· Ь‘~Fv»8 ‚t•@J#é"¨vu‡Áß P˜@=p¿cÈýHÀ/ÃÑiµ:®Ù¢i—$à¼ÒX¹'uÆŒsS÷hº VwbWêØ^ÞÞ^^^3Nt½|—D7"ПT;ÇøSMqAAÐtÏ«ûµÛZ}p]T\¦1»©Õhá@@ì!à¼Ò¨»Ü¤À…Ëö CêÓpú[Oßš0iÂM>Ò°A@@Àç•Æˆ%4,h¼¼ým Ár³lCÕ“kF:ïø,»u'!à¼Ò!“Ë}erA0ôP§Õh[Zo_¹LÐ6Vž©ªýñ’0pðu£Æ„ð‰¡þ²›–fvvq¸Ð¬Óù 2™iŒÕ¥ÇŽŸñÐzî§!7ED…™¶9ÉA7@@ú›€ó^†SºéºŠÆkÊ&&t¥›çúê÷ªúà†±ÞCG+¢&R52Ð7vÅ®F‘£¦t‹×¯ñ« ĵܨ¡T~Á ­¸¦­Ü´xìÐ‘Š©³fÅŽŸ8uÖÔñáÁ^cW¨+q•Nÿ ¢}p2Î+‚ Îý ÀdÅÕ‚U±SW•˜CTgÇ=ºé„>O×þĤF ‰¤¦t±÷èe¹†rJ•JÁj(ÉŽí»éVóJ±  ੜYÅ}"^‹c±w²Ô4´´¶6Ίg› ÞýÍþ|"–477¦«ô™Šô ZiÈôÔëãs™Ÿ*«¢¹µpïÞ“m »SD7AX•YͶb   ‚àôÒØn'ÅçoY9%È_.“ùĬ|5K)z\¨k Ù¡Lîã㵟8¿¼Þ/VüåBíÁÄLq¾¨HªÚ»2Ô‡^ôŸ™š¿;‰Î|Çvœ‘ KÀå¤Q¹r~¤doù+gˆ“¿’âØ9E¾Í8ÝÔ\8ͤ&¬[·ê ù´Çb…ê*³-Xð`®&Ê{FñËQÅÝf8)^ìų_·(î»Ã\IGDªvPÙ  HÀÕ¤ÑÏÏ»‹{é*ßëÄŠQíîÔy™ël«†;€€€;p5i4&µ_4Õ°‡ãÔ\lnW¨åRM»å³àÿ(E %¹ËFúzÅÆÆðžŸÍîéˆ?¼a6=ª @@'–ÆAbý؇ X¬w × Á¢9Ü›_ÚÎS2å`M‘áþ~AP«Õ†Òñé% ;cxIc¥øð`Ϋ  w¶-ÜÉwÅ*Ï[¸¥má¾J†UOYPtFaëŠòÒòêzºòâEaÔØÈÈÐiAØ   @œW{a÷È‚Â"ƒÂz¡bT   àFœø€ªQÆP@@\ˆ¤Ñ…vº   Ð }Am€€¸H£ í,t@@ /@û‚2Úp!FÚYè*€€@_€4öe´  àB .´³ÐU¾ àzÒX}0c,=uFj¥¶/¡ O#àzOÃi8ýY‰þ!¨~tB¨ƒw—®t×–}•Mòw/ŸízhLÕ€€‡p½ï™àÃö•—ãw™öÓ×–éßT¥ÈZ0/Úßñõ£F àzT{ê ?±úëö‚îöjÇQ9€€€Ã¸À¬±¾²ôÜO^~W ­²À°/ã[ª,hêKOýñÒ%að kG†Yõéj+KË+êéåŽÈü¿Û IDATä8fld„éå:­¦¡þ;±Öïš4ZA&ó‘ËÊnn¼}ÉÖ5V—~óý¥ÿusT„¿‘å;~ÆkD@빟†Ü&y-²¾¬þÍÉ2}i‹þj5:zˆÌÇo‹´@ƒUè_Î=kÔ”gÌ8Z¥=:<|äбsSwŸ¬i‡L{tÛÚ¾Šñã'ÆÆN5:xè€ØêJÔS[}dE¬Wðè¨Ø©ú4kÖTÅèÀ±‹7U‹—ó”n[àí;2™½Þ¸$y¤¯·ï½[¨|é–8ß¡C}n9qd“×БQ'NÿÄv¶rÓâ±CG*¨®Øñ§Îš:><Øk¬©ÝúþC}éß½¯•ö„ìÒMT­/¥ G-6a@@  8±4jJû†'˜ñ)ÉKKÎV›e š]+Æ_”ižI/,ÎŽ}×®r£:jO,9‘U*Ì¿$wÙHïÔjšÚýlôä]¸,šâ©Í¼eQ—ñ-zƒºç=zY.ÓRA©Rk¤v}7Ð ^À­S”zWAýƇµ¢a\ÔïÛ̦º{ŒÕÙ­ÑŸ  }NÀi¥Qwp}|.áH<\Q×ÚÚRSœ¯Òç4ˆm¬ÞóL\6Ó§øÝ%5­mmm-5ùIJqkI\øËL“Êw¾”'f%n/nik+,<ÙÚP–ϪJÛW¦‰XœßÜ\‘Â2)ÍÍÍÿZn¸à‡µDKeb~áᢒ·¢|5ïž*«¢¹µpïÞ“m »SÄ ²¨Ìjò÷÷‡±pIÚ'’{MtÕ…†éiÂcQPFŽ€8g•ÆÆ/^Íd‚—Tq|cLh€L&Šœ³·æ€R0LÔD€µoÌÊ Õá†3#Äó|ò 9ç'd&yù^Í€OŸÉÎíÉüÃV¾úšRÌ=Q|NÉ}|¯»^\¿>0Ї’ùéÁ„í …ç(c¢#BdµY÷IU{W†<ýg¦æï6¨ræûú‰£í±‡XF¡ºŠŒ€qÓãÅõ‚7ö©ÖïÛÈf° ³0g4£‡p RÙqбNTýŒ· ´èVÐ $†‰£î§f¶U<~Æg†ƒ™,§é;¦­ÂٽЅ«æ «Äœ‚ÌYú“ UüìØi3UÓ"iJÊÊt¶Tøš6Ÿý∸¢¸ïse¤yâˆhꇡmr’G$$)òhŠY’öÏʵsBåºê#ìhª2eA˜ ›Z…  Ð'œtÖ8ô–(qøÊð@Ë[FŒ¶J¦À<©‡]õÂH :§¡ì@¢ñZÊ))ÈK[5?jtà€åìŠSѳƒ…ÙE:Wù^'º)F¶7™—ÅÊÛ1Ì_wî×S-;ø.kâ‘oí -dƒ€ô'vßìýÙSÛ §‹Å• ß5è¢ÍÏù;jy#yªÒw??}TK ¯¡ÕËË[&ÎoD8Ëõ›²qïÉçk+O•ûÇþOþž™«f ’ÓƵnœb?‹¦šSbÑš‹4kµÐî–K7—øDLO–ÑDµ`óþÆ¥c>y›MMša¡¡¬7X‚€ô3g5Á”9n!4º3åÆù |TDDdt4ÿ9æªÚ3”Î ^$yÚRõž]»v¨Kë}‚B£•sÖfl)lk5^ò*'¿6™•ÖÙ±="úNq£zÿׯˆFçÚc¨E{x°Ÿ1/ä²Äƒ½%ß§Þ½WܬÊúËC±Fo|‚€ô/'•ÆàÛb—ì?噉Om០·jè·ûŒùá"—U›JÍŠyy!݉O÷âï;KÇB›÷%Ί‹›ûÌ~ñòV·,(rÊMÌ>هߠÎgþA#˜cfZ~½YÉú·^`wX*šdÒ¾ˆÙsE/õüØùj½¥xlv„˜ƒ€€€ÓpRi”…ܗîª)Iž»¡R£W´ÆJõÜà©fsFyÄÊt¥5Wqoê‰zQµµ{2æNLSëóéé[ú†Þ!zÌ_ºIMhÓ'¦tÏ뉦ìë-?³Õ‚ÿ÷ÏòÚFzV\I:k+ëžzÕÝô<±{‚¶vÇŠ»õO'§¿n’äq²XC7Å‚rÉäÎÅ—ùa   МTiB¸øÿîÒÈ[5Ú×kìØCGDzÓtŒS«ø½r›ánuZT ÷òóž•Ìù{VèÝä³×æ³R¹Ëb‡z1#v¬—¯b–áÎûüçÏúÜ«Ýò¦†½k³ôÚÖ«„º·àÿ0Wýót|½è ’Ôî|ãÃo˜m~ 2`ÖïÙÍÿú WLç÷+Ä'€€€³pZid!SŠª Œ71–°Ù¢*½°0GÌîÍæ]òÐŒã5Û“Ø$Ž.<5Î*•‰…ÿ¢›%iY蜺âíÜ© €_ÁŸ_\ÇÝÂg§°#´úRÃÄ£¬ƒX£ü̧yÔ½ƒ5EIJ¶UP«Õ+>½¤agL»ûA¦Å}UüÖt¬ÕP   NCÀüûÞiºÅ:"Qn9Ù²®´¤òü%ÁËëê Ù‹2N¶-5ë©,h^ÆÞY«+O}[}©Uð¢7o b©N‘óö¶Íª<ñÕɳõÂàÁÂ%aĘ0EDˆtzG-îlkÙPÛL¯Ýðöõ¥C± w¶-ÜiÖœqEQغ¢¼´¼ºžÞ¡qñ¢0jl$Ý%iÜnþi$­H\NÏ™CpZÆ/l§í  ‰ˆ¶g’åjk>¡‘1¡‘{É‚¤rÙ¹³,(,2(¬sýÖÒ¿îY±t’óC·=x€€€ûÀ·t/ï[M呯š·~9™xúSµ5.Ì~Ýí御zk Ö¨8.Os¦`âÄU¼¾œçãp0•Ó€a'ÆFý;ÎNŸ>]QQqóÍ7ÛY n Ý&iì6:û ÊÄkyDß”ü’¥xŽ}Øà%%P]]M«ß~ûíÿþïÿ<øÖ[o½í¶ÛƧP¯R“zÃè1HcvZOÄâ–æù-­­^Þþ>8’Ú)+lìˆiáž={FŽÙÒÒRWW÷/1‘3É$Ý5”™™9p éXG• @À~FûYuÏSÿHhb÷Ø¡””@DDÄœ9sèÛ4},+++--­©©ùøã“’’|}}¥ž¿üòKCCC` å[k¤>°A:!iì6€Ó>|ø¨Q£fΜ)—Ë›šš®¸âŠ   ‹^Þwß}t£m@@ÀرciÆIÇ]IVé$åUW]eá‰U« V± \€Í  2<“BÚ݉'×××ÿCLl‰èõ×_¿hÑ¢µk×Jaƒ´'à¼OÃißW䀨CàÙgŸ=þüwß}Gï0}þùçÿçþ‡&Ž2™Œ.p}çwÚ×pàÀ;wž8qB«íð¹ÁíK!ܘf–;·|×Ú‡Öt}ìºçôâºÊŒþ½éÂä{RgÚ|Re'±6 „„„Е;÷ß?óÔétô’¶öG_5ͽ÷Þû믿’Û•W^9fÌ:úJGbÙÁXšhÚl à~óÌ3ëׯo_ 9 àÞo¾ùæˆ#bbbüýyÔXxa\›€GK£Ìx?¾ÜËŠöî¬ÑµÿlÐ{÷$@÷G²C© ïÃ?\²d 9 0à–[n™4iÒÿ÷ß}÷ÝÉN a“Ëp^iÔi5Ú–VÁÛ×G.4õ¥gÎþxéòÀÁA7E„ú°^këËËΞ×gþ×µcƒ ¹Ò] «¯,û¶öÇÖVaÈÕA#Ç„úKn0¤S/uçë˜÷Å‹ Z­L&§–L‰^n¬ÓÔ–¢×~´zy ºqLh€•“եǎŸñÐzî§!7ED…Ìšj-m}iÙ9¯¡~B“0”j’I›²pÅ*8/iÓ¦=÷Üs,**¢{+)½öÚktùëí·ß®R©žzê)/k?4w<èX#à¼_Ð¥›çF­*TY‡— §šC*ªÂª¿}¾1<.M:¢¤ü²Œ9¦W`èj®‹Ÿ©–º‰9…Ï/UêõM[ºÀ[!>ð[ï;’êÊ*n^i?¿¦îH:߬ eRþ’ks´•›g.Ë5¾!’5¥H,Üó¼2ÔX˜Y¾'#|–þÈ­1)R²f×Wð .DÀÛÛ{˜èÈê±cÇ:ôÉ'Ÿ9r„”’eU*•.4t¬p^i” Õ¥`ÕÄ‹žÄŽj‘E«™qá·ÝÞÂ^J¬)ßqWø|sÉÒ—È^›]žß¬¿ôT§i_… 9†Z<Õ²]A÷ø 7ï\¡/ª)]ì«È5V¢T©.è[,ÉŽSܰ4Òp¦tÇbÅ|îÈ ”¤­jß;c]øW @G_ïSrròO?ýôé§ŸÒCì&Ožì }GAÀ׸¯1iûá†–ÖæšÃ ’á(“¶WÔ5·4Wå'©Xö΂2Ѩ|ƨ‹ñéjZZÛÚÚêŠó•Ì);îeu½ Ìoi©8ÂòRTÐó·–ÅŒeÒ’f™5ÍT¼¹hk"ËÌ{û¦©êõñ¹SeU4·îÝ{²­awŠ¡'Ë¢2«YÚƒ\·®kiÕwXtã¡9g®N€ž³CZçÏŸO' -ÆBùt>r×®]ôø:‹MX§%àÒ˜°½,c^Œ¿\æ“v É€2~ûÇóèäŸÜ'dNjšA‘ÄmµêíÙ¢¡J?¼sí” ñb@äœ+òÙ{ ÒòŽèž³dxÈd`@ ]Â.9©/Oín\ªOaúD/|>G)Vzáb}ÖĻ}Фª½+'?ýg¦æïNb~™ïŸÐ¿Hèè[¯ŠÅ„ÄüŠ cäôHUêðÞÂe æ –nM ªªêðáÃqqq¡¡¡¯¼ò ýuëábpnBÀù¥Q™0ËtÑ?(„ÏzrºTÉ$gö´ÿÌc'•ë–ÇH÷’èÈ*=ÁnÅŠ7Ýt=ÄÎ}‘¹<ç½ Ç€V2´‹}4=nYÝQð5ÙÞoªÀŽÛ½Ê÷:±ŨöN2/ÓÖx_´ñæ:+J¨RÈSÛî<@À Ð9Hz¥}ûöÑóÍéÉ;>øà¥K—p§‡ì\·BÇ_ÿN2\;T¬ƒžªv??ªU×jØLŸ^ÞÞâxu^#,•ª]¶ÛTsJ,Ps‘ΛXTÕrÉtW†N`×ìä;µi^˜kmU¡º]£Èw'0}útzd+]•óóÏ?CÝ}o»ðøÌ¾®]x’®ÿl´G…GDšëV呃'ë/ u[DÏÆ="úNA ÃAêý_×*•AÆõŸµÇ>P‹ëÃýH5G‰¶ýyÍÆ™Òc¯ºº³ß±MX‚€‡ ‡˜?üðÃ6h ×Å8ÿ¹Æ®õù*^,SðÒÎr³ÂõꙧΚ5+vÕ>‹›Êèb’Ðæ™–_oV®þ­2Å ÅC“BYHl‚Áñ u­Ô±pÓF\ *@œ‡€ûI£1{©Rœ·(ŸÐˆèP[;Mî`v©-óí² °È 0óþøcz,@NNŽÃÀÐû™€Píg2h@ ¯ Üxã4_¤Ùð¦M›èñr}Ý<Ú#H£‘>Aœ€À„ rss©#O<ñ½èÊ z„.x"H£'îuŒœ™À#NðpÙÙÙÎÑô `ÖèÎ{cèHc7 ¡€€€;€4ºóÞÅØ@@ºAÒØ h(  àΜYuôLa}ÒêøÐiY–Æ”%Xqãþ0@Ü•ÀgŸ}¶bÅŠK—.¹ë1®~$àÄÒ¨)½ßWL÷nÑ •nžËò¶”ó¬¹Ýñ  à¶Þzë-ºZõÍ7ßtÛb`ýGÀ‰¥Q&¹´ Ù@f^Öó¬¹Ýñ  à¶&MšDcÛ»w¯ÛŽë?Ž—Fº·£tÅWЦ.6Ø×›—d°|½½xžÁºYnÃ:¸ Š GE™›ŒÓŽaÜwß}2™ìСC/^´Ã. ÐFiˆÚl™¶MOA¹“^?Ciç<¹Ñ;b¡!oa˜aúhÕÍèŽOpöÇN—BÒuùûûOž<¹µµõ£>rÝQ çÎIÀÒhÏÀ¤±Êl{JÁ@€@qܘ1cÙ{öìá90@À!zE­Æ°4Ó!]G% ài,‚HºÊlO¤qÿþý—/_ö´±c¼½J W¤‘zl´gGhµWG…ÊAÀý´"‹(s¿!Û½Ü8**ª¹¹ùÿø‡Mg8€€ý,Qíºò2(¶Y"Ûþ.Â@€´ \œOû¾É]™3gÒÐpºÑ]÷o«ß¼Á¢”Ç3[eÒÈF«V«SSSûkäh\…E ë*ÿeiV´ê*cqx?ÿûßçççßvÛm¯z2^”FÂÊä‡1 ì+¯¼rðàÁ´•®º¦äÉô1v°ŸE Å "©Fz².½k¯½ö믿¶#hñîQ‡ÔˆJ:%Àâ…\HíȦaKZ¥›öhéååEËÿú¯ÿ¢¨1„øÁ#‹¬†N[ÃF»8^Y³Òp¥ˆ•&ŠkzØÛôéÓúé'RD¶Ôjµ¤‚ô½LºH›‘Í­R…dÛ58õŒ= öý÷ߟ={¶ñ¶ÑžUˆÒ ¸ Š ¤UúuB9r¹œÔñª«®bKÒH©'/Âc­ó¶°@À~Ž”F¢Ô6Ufð¦À¦D³@ u¶$göÓ˜VI&iIZH¥È Ù$-Y‚4Ú¿G{âYXXH¿NŽ=:mÚ´žÔƒ²v ÐàÁBÊG6Ó?ZR\L’<±¢%)^œÔ.7ììÜ@Úp¤4Rí<,ÉàÑË i rrcKö]@?ÙÑTr#!dÁOZÈVÉ`êØ¾ëÈq,ÿûßgΜ¡:iY__O§p[?jkO€Å ý©“Aá@“=6wd)]2”J#a‰j úY…íB€@—8FY@’†QÛdS¬’¤1ƒÅ-i!-©~(•M"™'é%m‚4ZÝyÌ,**ª««£o–%K–äää]ZZ:~üx6ªÚ ¡L&idÐ’2ù¬‘¢€ÙLiÉ㈕â!ƪbM°Ìö͹q΃>H—»WUUAÝx/÷åÐ&ÒNKÕ˜‚™Ž,òé<"ùPŽôü"s %;ËHJIŠÈ“´~ØŽ%@?·?ù䪓»5tèPZÒ›ð('::š®ql[¨MJ@)´Êæ‹&8lÖÈæ‹´$f“Á“…@J+÷(»¶¶¶©©éôéÓôGëQÇ`{‰€ƒ¥‘…:õ• Z–(Œ™Â‘ì‘A›è‡0?jJΔOžLiIñOK’F¶‰•í¥ñ£Z"PPP@êxã7N˜0ViIïOÿöÛoIãâ
÷°x¡?~j‚„ ¶¤ ›-¥ÉçŽÌ`ÎäI‰ª¢JX…½×a§­9 €úFG;œ¶‡è˜kp¤4²°¤%*“@.Š”C6Å3—FZ%RLI™'E¦š´‰‹"Ù®…Õ…zKWß|úé§´×,XÀÅ@'{ݺu”?uêT\Ó{{“â‚*gÃäÍiI™RdŠHKÊ'JdP’ÖÀìÞë³sÖH£ËÇœ³{è•Ëp¤4ÒàYXÒ’‚–„r(n™Âqc9,žI™'Å?Ùüø*/"H—ƒëþë_ÿJ´ï½÷^š5ò“M÷o|üñÇ´5%%…çÃp,ö’Æâ‚‰_{¤Š¶•%–Ö¼UÂlŠs±VÇõõä=m1öÿüç?ô¬Tzt-J7nœÅV¾úå—_ÒaUz˜'=[õšk®áù0B€þ¼Ù_8-Iä¨NZ2›–$Ò%SA¦‹\™Áа²T ­RrHQI§´õµ‚Ü7ÀÏ nt䬑E#SG²)PIÞ˜ÁÈM‰I c²É %ý(fòIžLY=Ò¥ âuÞ.ggg“.Òµ6wÝuW'½¤­ä³ÿ~òå•W:ñĦn p R|I…åÐ’­²Zr™$ƒm]Ì|XÄB}¨‹ÚÒÅÞŠ\UNóÞ¥=ÑŽþy70öO‘ú£‹ïŸ[B§7´­õïŸN ÕÞ"àHiä}¤àd6E/$o,\™R2µ#›¶’Í„–äÉlVD*ŠÌfubÙsôìR;š >ýôÓtÛ\ç’½J—üçÏŸçãtΪ«[Y¤ð%7XàÐ’rø’ –X&Ùd0›´ìjà¯Ó¦Z¾»ÜÚ³ºJ_‹ZUSܼ4²' Û³Nt¥téÎõ¢.*âÓÇyw¥ |]‚@¯H#œG)0“=2(Ÿ‹­2]”Š"ÛÊr¤Î¼”K0uòNÞçž{Ž:¹lÙ²Q£FÙì-ù'M©ÝÎÁö£ÍRp°‡n0¼´J‰Ù´¤Äs:2(ßžïãùAC]‹àÝÃi“·¯¨ˆâëßÉ^¨ñ*±Ã9%Ç—FôÖ·h/ôUÚKÀÁ;•Å'S2«Ø”C‰r˜ÁzGßÑ|˧%m’.™'˱wLðë”=îÔ©S×]wÝOvÉk™Kc ÇIµµ»6g¼öFá}»Ãf¯HY³P飫ݱ>cÿ!ý¡ÉeO,¬{d麅12AW®þë¦Ü÷NÖ4Q¾ß \°øá˜Pý–ÍV¡öè®”õë?o&\¸p}lüS«—Ç„&£ÚÚ›7¾´÷£’ ÆÝ1öžø¥ •aú'éXMõ'ö¬OÙXø@ ŠÙOÿiÙ½³öȶ ›ÞΣ"ËžYÚ¤Z°r¡q€VkA¦ päe8|øLç,VY&[²y!Ï!OÊ¡U lZ²‚̇WÃ!.^¼xûí·766þå/¹ÿþûí¯óƒ>xôÑGýýý;†'8ÛÏÍ~O&~äÏ¢€–\¹à±L¶Ê—¼ îÆsúÂМˆõR+³ WúkNÌð*hת"éÀñŒ)2¡6clp²^Jå0:J¯wŒßÚ°mÂ£Þ S)eNsáÒ3›æF-Ók T* ®Bâîª3C­¥;+æçêË*”е¾AAq æø” Yã‰mC£é3…B(7 )ªR§„ˆ™f‹ò+Âçgë³”*å…VQÒ™›b}—©ÎЬ†“+{8i6Ö…Og! ÿ)êðd¢<†)Ô)ñKìÈ «oxbù®R’c dff’.þæ7¿¡ËS»T3ùS)*K5t© œí$ÀÿòÉ "ÒUvé  "SØWìž×¤'srݵ4:H\¼Ù‹›~–\ìc½•sú‹chžºqŽøÐ8YÀ¼uÿû^î,A{ISvT?Tm}EÀ–’ñž‡yxºÒßìKˆiŸO̼ùwm_1÷¥“%tH”øÔW¢Ÿ›²ÔA+ÂÏúÍŠ šª_ ™¹Wüz)Ý$e-XôÀ Óá[Ö·Ë:‰æêË­b=±‘׉kl!ŸðßñB^ÞÎ6 ‘üªeA‰?L&`öWÙKã`,•:óÒ³‰,‡w€œ-rø&Ý#@÷ìÓ)zcjj*›µw£*ûÎ;ïP={öì¡C¬Ý¨E:!ÐÉß|û]Ö‰s'MôŦëý$73x™î ’Gli­‹k[îÛyyê’‚¼lú·LP®+ŒáRÊú§-_ᮟáQR*ã’¢oV­ÊüX–¸ì¨q£"|¸ÄÕ`êýáXJMúÿú%U|¼P£æmÒ\–Ï–ac†JW™îbÝÙÌ+.N /¤‘!jÉí£] S*¥Ò|ØÝ @wS?õÔSTnÀ6lX7j`E¨,Õ@÷rPmÓ§O§;#»] Zh R‡Î·J=ÖÖÖ–~^Þ6wíÎ…kßÒ6V—Û’2•¶¾ønéÞ¥ÒnkÊ>Öë¢2¥d˜ÿÆj÷¬ZÅ4MêhÅÖ]ÖëßgGÏ \okÕ+–d ªu«ƒGÑ&ERaa†’ –+O|sA¸q´ÙÑTÚÄê9òéÙ¥¼oÚâ#úù®ÿüÙè¹ñG¯\†c'/Šv¤¾!ðòË/WUUÑÑǼ‡-R TÕFuö°*·Ÿ€1åÌnU=;~ÍÎRê¤Lî9å1:تOt®ÑÊôN™L<ãxCŒAÍž×ßÔ{ø ²ù[~DôäXðÆ›¥†)¢NýzZvAÁÙŸ½‚o‹¡M%™¹Gø[ë<::jâø¨#õ¦>è„‘Q´Ì[–}ÂP]FôîKâ‘à˜‘ühªèŠ…;°ù—Öoƒ¦o~kÛ½þþûïé²RÓ¨Q£ØÍþ&—t©]bÓùtê9yò$ÕùØcÑý‘Õ†| ÀSÒh¥¶ùàéŒ\Ä”¹¤Yy‹šÓ9Ëï>}ä£Íiäõà´p}Qq¶·ù‰¸êGžZ7Q,œ;unpδëšöoLÎcg ÞÞ¼gÂòߌš7AkÔJ@ÌãYÊäUêl…ïÙ¬­ª‹ÿocšXxù¬YPx~‚—›71°&%ç÷£…¯_Z–ɮٙdùM \–®HK.Éòý!+ÿ±QÍ'þ´(œ•Y뢡ŒíÈ»a·Droqqq½ô‡K5»7:ŒÎ6–º¸F g¨’«Ô6”lÞªŸ&”èoå ûô÷`HSÊî2æXW”cȧÛé^Ž}­Æ¤Ì9p@¼¹ƒ2Òl´BݨÊÑß°Á“j;ÝbHuùIlªjتJÊo0n³ül.K—Ö#$dêÇ!¦’­ú{O¶–èÇä~L·Øó¿#îD€ÞHL7]Ю=ƒ¢ëk:Dog¤Û¬íñ§™ýêÕ«-ÞlOAøx.m}yÙ¹¿Ì{ä˜0éi;­¦‘ž8ç+—‹wië««Z¼¼üC‚|ô“:zÍE³Ì×ß_\± °±¶šj“y{XL 5õÕghòºÊo¨`€Y¬ÔZ_]þCS«N' 9&Ä–³•òÈrMF×Üo½Ókº5---%%…ŒÞiµ‚€€ èÏËp\º  ày ž·Ï1bN @;Ń  žGÒèyû#蔤±S<Ø  ày ž·Ï1bN @;Ń NFà—_~¡7£•——;Y¿Ð·"it«Ý‰Á€€ÛxöÙgé¡/¼ð‚ÛìGÆ~„¦AºF€žÖDï½òÊ+/^ܵ’ð®€4v…|Aú@ccã#kÖ¬Ù¿``à_ÿúW: ê|DÜŠ¤Ñ­v'nI€.IݰaÃÀßÿý·#åT Nµ;ÐKMMMË—/§Ü×_§-é`½wX¼È¬wA­  Ð]ôBЇ~822òÑGín(]#iì/xƒô1+®¸âÍ7ßìãFÑœ‡ÀUÿÀðA@, @-‰`@@Àà @=üç#@ÏG}çwœ®[è'€4zÒÞÆXAÀé |ôÑGãÆ£ënΜ9ãôEÝ–¤Ñmw-.G 77W¥R]ºté±Ç3fŒËõvF·Ù•¸6zü=4œ÷§?ýé7ÞpíÁ ÷.N7o¸øD÷AÀõ ´¶¶.Z´hûöí2™lóæÍ .tý1a®MÒèÚû½W'@»yà>ù亵׮]÷Þ{¯«ýwF7؉¸0§žzŠtñšk®ùðÃo»í6 ºîF n´31pA÷ÜsÏ… ^zé¥Q£F¹`÷Ñe÷$itÏýŠQ€«˜%&Wé-úé!p…ª‡ìh @@À^F{IÁ@ ç{^j^%iìU¼¨@ÀD€îè¿öÚkñÎEXÎJçuÏ _ àF~üñÇ„„„wß}—Æ4}út7†âž î¹_1*pŸþ9=õìÙ³~~~tG?ÙÎÓ7ô¬ÀU«X  à¿þúkzzú¤I“H£££‹‹‹¡‹ÀŠ*zŸf½Ï-€€G ÇÜÌž=ûÿøÇ€ž|òÉçŸÞËËË#I`ЮGÒèzû =— ð·¿ýt100ŽJ÷õ»DŸÑI` øKèsæÌ¡zï»ï¾áÇ÷J¨z¤±×Тbðlƒ¦×.z6ŒÞU à2WÝsè7€€@/€4öXT DàСCãÆ{üñÇ=h̪[€4ºõîÅà@ — h4šeË–ÅÆÆ?~üüùó½Üª>"iì#ÐhÜÀÁƒ#""6mÚDwe¤¥¥½óÎ;î7FŒÈ3 à2ÏÜï5ôˆ=ømõêÕÛ¶m£Zn¿ýö7ÞxC¡Pô¨Fg"it¦½¾€€+ø×¿þE7fœ;wN.—§¦¦®Y³æÊ+¯t…Ž£ `/H£½¤à À¼øâ‹¤‹wÞy'MÃÃÃܤÑýö)F½K ;;{áÂ…¿ûÝ﮸+ô.jÔÞ_ ýEí‚€«“«öý;àGŸàžJ@§Ó•••yêè1nÏ%iôÜ}‘ƒ@çè=‹·ÝvÛM7Ýôþûïw àf n¶C1pzŸÝÈSRR2fÌzÕ¢*E à: ®³¯ÐSèï½÷]wJ7òÓ-O?ý4©ãu×]×'-£p¸ ÇYöúýN ªªŠ&‹~ø!õä®»îÚ²eË-·ÜÒï½B@ ï @ûž9Zg$PYYyë­·Ò3Q‡ ’‘‘±xñâ8cGÑ'è}ÆÞgŒ@ÀE 6L¥RýùÏÆË‡]d¡›½EÒØ[dQ/¸ÐÐгgϺVŸÑ[è%¸ §—À¢ZW%itÕ=‡~ƒ@· ÔÔÔÐk‡³²²º] ‚€{€4º÷þÅè@ÀŒ@[[Û믿~óÍ7ÓÕ§ï¾û®Ù6¬€ @$ð .N@«Õv>‚o¿ýV©T.Y²„Þ¶8cÆ º±sl%iôØ]»S§NMš4©¥¥Åê¨~ýõ×—_~™îÍøôÓO¯¹æš]»víÙ³'88ت32A øw °téÒcÇŽÑ͈íSQQA÷ï?ùä“4­|ôÑG¿ù曇z¨½r@8H#G\•ÀÛo¿­V«©÷däääX #))é‹/¾¸öÚk?þøã7ß|Óßßß« p_£¬‚€‹ ‡«W¯æ^¹r%½.ãÎ;ïä9kÖ¬¡çƒÿáðóóã™0@:!iì6€ xæ™gêêêxGé ‹>øàñãÇùmîw€ `“¨ÚDp^_~ù%½"âtÛb\\i¤E>VAì$Ðÿ³FºÑÊξ­· °}AKfôvs¨ß<ã›®;¥Ã¤VwÖ¡C‡Ö®]KW¥ÚÓ|@,@-€`UOÀê·-Ðô N¤‘nÞ?zôhG½zå•W&L˜€‹Q;âƒ|è„@_H#ÿžåë­òntÒWlêm4 ¡&hùË/¿ôv[¨ß&&Š´;È`6/B«t~199™çX5{챈ˆˆ›nºÉêVd‚tD /¤‘ÚæÊ'5¸-uà•nå™0z•cNK¦‘½Ú*· `!~Ò­´GôÚh|{"”C×Òµ©R·öö¥K—fÏž]TTäëëÛ~+r@:"Ð[ÒH¡K‰·ÊWY&[å6¹Im¾Ê‹Ãè|ÖØÚÚÚ7-¢)©øQ>[åK©A§éFiÙŽì²²2š;â™pñA>X%Ð[Ò(mŒ !åHsçÎ577S&}3]dÒ‚°û˜Àùóç©EZ–——÷qÓhNJ@ª‘W\¡¿Œœ¦}#FŒ | úáòÄOHý;·OŸ>]ZZJGV;wÃVN@i|Å!×?ªÙKʧGþ/X°À!Í¡ðÛ·oàHéúšgŸ}¶óQ“[LL̬Y³fΜIï(îÜ[A,ôâ¬ÑB¥«ô(dêǰaî¾új‹aµ Ð36éâŽÀÀ@¹\ÞÝ@ÓháÂzö)8üþûï_xá ¾:pàÀßþö·¤ˆ*•Šö#χ Ð%–F¦Ô.„ìx)­’ÁmZ%ŸåË—§¦¦v©»p$@a’––FQCwñÿñlÿz :Üzß}÷Ñ‘–dóã±È C‡p°4²>q]dH™<‡Ô‘VÙÒ!@% à!(jèùà}ô/= îþûï§7/ÆÆÆ4ˆ¢ÈáÀžp¤42ýc½!›ÏɰH¸m®'û̾²:Mcc‹þIa2™··¿ڇ͉½èp÷SO=E¤s‡Ó§O'Q¤g£^yå•L)â˜.’A>ÐH'Þ“èš p¤4Z.ÓK¦”\ YôZõw§ÌÊ]kg¾öÅõ~~Mßù<½oÛ”‰>iëþ³ðèוMú³ËG„GOþí„Gì]½ú­œ´Eij)J…*é÷>2÷¡ˆiÊׯ-ùB þ5ùL~z[êÉ6iaØNA ²²2>>þw¿ûÝÍ7ßL—­’(R·(” ‚N±{Ð ÷"àˆïbs"*È䦉dÐ’N–0üs®éŽlXýâÑóà ?\Rdm[ÚEéøéü©µºDÜŒ†W¹4VÌ9ÕÊsL’¶­ŸÝ“]¢©<øøè©yíq–d®¢óÓT­bØ®k9U ¾åO¿>ãUÒØœåŒ=šž˜*“É(ˆH)ÐØ’ vƒõ• (¥í3tÅe ôä{ØÊ )JÛ'>Y”V ;]–ö«½ÙjÖ-պׅ®_ïÓ~LšòVu‘<3ç?u¡hïÊèö¥ìÉÑVîò×¹gòÔ‘òÃ5+c‚ôn2AÒ¿^—ÄÖþ&À~S’ø‘Áô–”,"ŽºI™ýÝY´®MÀaÒÈâ“`ð@• !›/Ò¯]2Xr lƒ$o~õr ªÆíKæKÆ®HLŠ=™™­6f¬Zx0îø” n4V¿a¦¹.*¶®ˆ=¸Ný—õiylîªofÕÄ5Ê–‘]œ;ˆÏ~#@ÞRD‘EsGJ¢2Tk$3¨—È~ÛU}Ò°F£ùÏþóÃ?гSè¢åŸ~ú‰ö8¦K”û¤}7o¤_Á] B2Ù^ YNj±éªÓT–mõ§=­­~£Âƒ 'í´•ågZ…öùT£®òè¾]û¨ä{að`áÒ%á†èIÆ=dT ­¦¾ñìéÞöwßVÕŽð“ûùsdºÆêÃûö}^Q'èï”=ùN[ç µUGÔ¼NUaÍß”¤‚ë×nX¼Êp´äÝVO™Óå jý‘mÉ&ù”I»?ȘÉ&…Ê)s~(5|Vš±á¼ý_nˆŒ 0®ZùÔ5Vî+8òõ„!Cä‚< ô– wÜâßÎU[~tÿÇ~þVð&C®¹ùeì‘Òš¬ˆ¶¾|ÁÇŸŸüNðÖ;^sÃÍÊØØÈÐÎúЮ-OÏ À!ud‚ÇæŽ”C«,ÇÓéxÌø«ªªè¾Uz^iaûA?÷ÜsëÖ­“æ“|ÒëÉèQJté]·…¿)œÎl>Éë¡Áâ–~ÕþüóÏô†~ÑгèG =ŽvgEE=ÎñäÉ“'NœX´hõ)%%¥óF«v§(”JºyY©T¦ì®08·V¤Påê—)­mmÍÅJéU[›E׺ÂivzQƒ¡†æ²t•t‹ÉNÜZDõQ*ɱê¡,bU“C¾Y弊„¬ÂC3TI<ÏÏ*Ö·ÞZµ[aÌReÛ§³žo}ømmÖò›·Jû«L¯ãUŒÖí¦¾Šô"}vK±)O™cYkñö$Þ3C™$ÂXcÍáD>3?!ý€qgé}[‹rÍ·×Té–¡V|X!@‘BÈ(jèÅÅAGMSY_ekqw}ƒ¤ V*B–+ £kô%Io£Ç~ñÅ]¦‡Å³+°®ºê*ºVùÎ;ïœ6m‰åܹséNqºVËÂ_úøÜàà`ò¡¿" ¬¶'À§@Æo«R<,¥;ŽÊ”–ö´ÓPSÌ/cf4Šh›ŠMø]Ð ¡>‘y…)Á±ÆYQÁ¢—ÌNi^ÏsA‘t`M´8éÑ–.öUävÐ|ö¢ñ'ë®Ñ]¶êáÇrOlšµÌÊÅ.´5wUì%ß² ì–X½ …PR2|ÍŸ Ék q¶jÌ¡Oë÷š¯k8ý©dâŠøvÓ1Ù¬ô¢Ý]¤)2Í‘‡Œcò6·J·-ˆZd}h‚:3jö5u…+õ•ë*WOÌ6/Ë×’§ŽQÖÿüsBJÇŽ£W¦°Ž“1~üxé ¢££éÇíh;œÒÃÿ.¦Ý»w÷Ýw¯Š‰Ž»Òï­qãÆIk†-% r±£’…(R R¢0¦A,ÑïY–hÕžF¥—‰D‰ŠIsv´TR®ÍO0U™ö‡ÌmÖK¾ß]?…ý 8’ñ„TñIYY)Ò—:yâŽJíð˜“Ms*ªZ•”˜tÏ0ª¥^=ßL ‰ ÒéSÞ¢WÊ;Ÿ,tÎIÂtR¿Ø2ËgýÖ§VñÞûø~¯Hj¾9C¾¶á”éhªjÎdã5¨¼RA𠉞9sŠ>ÍœÚþÐ(s­ß÷¶D• Y99I <ê³½gmá^Uâö¢¢Âíñ /ýõ+±ÆêM³¸£"1k{QqÑö ؼ7ŽÕ³¦±´A€¢æòåË<‚XL± "‹5®”dب ›‰À¤I“è€'iØ‹/¾HïT!9¤«‘éFììì?ÿùÏí{êççg§.²²S§N¥çîÒ„’æ‹«W¯öññùàƒn¿ýöeË–µ¯9Œ€á[¸ç8XXòzh••eJ—L)i÷ì̸,~ ‹M¿“Ýd|y’|Ο‹væŽ/`›J2™´FÈ*N cm<ú¢ä~¿¤ü²Œ9z}Z¹ü÷ŒL6æoÜߺq^FôÃ7œÍ[dÈŒ~SF„XIéÎ\“ )’ÊŠ2ÂhÊ·.anàx£ªä®øs˜8g2õ´C«qÇŠ»W©ùfÅ£÷…V:¾Ð>_ª¢‚0¸Û›jÎæñž(² WŠºø†¼Œ ¿E~øúïqÒòÕó”$ÆÑ;ÿZã;‘UPR\¦b|4 &?ÅìÕËç…È„èÈáòšñÉÌQýÍeæg‡E É¡xýLS,ÖhâÈK²­˜Ar ÎlÐWâÅ‹‡ B³Ã &Ðé@2zéáÒ·‰‰N=¾üòË999Guf2ýÛ7ÇH# E6n“A{…4-é×.ýæ¥DáM«ö ;bÉΚ¸fú. "tŒ¡ˆ<òƒºšfÍÌ7ˆã-0Þ IDAT©úDo>R0ÕxXÕØ€"¥pe¤a’¤©:b”?APæ¬uQï( Y“•Ÿ\g(TXF3™ Aû³±Aднùú¶4ŸN—¾¶Z¯‹”¢“·'iÞûÞÇGÐh‡xÛ¶±4u¶B"Ö‚2ý5~yªõá ‚µ|Ù`±=]ø(ö•”´x]%´6µCuµÕ¥?|ÿÍ¿¾m¢f¥…Ì©#3ÊÄø‡¦OŠþ¿²ªQ#ƒå´ÇÄk“̼KÒFz¥)ãÿ{gU•ÿÿë6Ѓ 6X@h cPì·†uµÚZ[¡`Õm+1¶ÝòË×ým…ýµ¡lP+â»ÐJ¶%ö[fÿbµÚ9lJ )l¢`0Ê(ÓòÿÜ{fîÜy‘Çyøž—Þ9÷<Ÿ÷ÃgιçaÅÒ‡¢îÿÉ5Á~rƒÞ@[™[Pú­Í̓E:¸ŠxbÓí6tzä3Ið\Ð]{G0^^^uuuØÕOÌQ«5ÿØÝ'ÎÝÍÏÏg»òN\.ΞòÈþ‚_f-­š(¤Q4Ì ×%)W(ÍÒgŽ¡ð³ç 1[¼¡*í­„"É÷L•µk£ZŒ)= W½ônQU@p3†ùŒ¢§ýð+‡<Ìß_ÓÈ­˜”`‰}ðVóK½ð•ù»WZú{צ)Y“*)+§J«z{C´9ÒÕçlÝ †oÌÑü¯†Qy`xXó¾W‹^|­Ô´¢ÓnJAÑ1'L­¦([SÄ‚ªSª óâ#„Ÿ2Š91jN𒦲ÿXÀ”Í5y¿ZfúÉc7r4ÛŽØš`›ó5‡&ÛTèíí… bÏ[Ì–bÅ ´ó²cJJ*•ç))€ƒgj„{AÑ8ŶÊì¬ ã×. 츲$°=;{)ÈÓÂ]u FðDóõa󂿬i,–yËgÞh~U<ÓδN¬íÈ¥~_nÌ\K]L)n8¸5^ªÖb.—¶X ¨Vþó˜y ZŒ«koÚg2Mív!uÕé·©–$¯‹éµ¾¡XòvWÌMiBd@zu‹àæ·¾²ÁâM¬$div\€Gz ¿U™K@«aÍGlMÌÂZÓHi¼tŠbbà­Þé£s¶bÅŠ?ýéOÐÅ   uë֡߈ÙŤIC}œSOi´*Z©Ô°ŒÆ w«ÀãsÛsà©é$tWmÒtЉßaî“Å^#: ý7æÜqL|Â:ŽkD¯syÅ~Þ]]üeˆY8Æx‡¶$,‘Ž¢r‰5G»·­‹RŽÅü†²Èƒ¢Õf¿­¥šoŒ6Ã_7Eâm<3«*Ío¥!õÍåÒ>·:%§¼¦þhGG½Å.²èuÛûNÔ•¦Äª¥‰0{Q³ÍC™2zÛáÁu…Y)vÂqEÿµ£Ù6:¹Ø@«aÍGÔBiû² O.SEàoûÛûï¿§sÏ=÷477?~ïöpdT§ªT—ÌsW±$'ƒ^2¤;t©Æ©ºøBH›®TѰş½#ËÍ ×UF&—‹e5à s–Éä2ÑY·sýBÉŸ1‡¼˜_-뫈°éŽÕf¾Ö–b*G×?ß3ÇUÇ̳ o (ý¬­þ{{ô26BbØóKß8“.kûÖ…ÛOB×T² ÓœUlNÕ+¹ñÆw¡Ò´y»ýêÛs÷¹{E"§1-É;±Ÿc4=MÛ¤¿žxè&“Åç‰FÖÕãÓªŽn5¾‹Õ7|h.0ï§o+Ë/ïÂFxOèñÂîŒmúž¶£ŸÖþ¥4³@|«Ã¹ú6Í–òø`rî–Ç^ÈÈßÖÓÙöéþÚÒ—2+5|J¼¹8ü ˆ®š^ØãÕ;H\mšt2!›8•ýÚk¯õñ±hÁ=öØ-·ÜrÓM7arÍ$”a¼²ÀäXÌ\Å©ŸÐuêÚNH¯ÑêÇ,»Åóc´gXFò8[ÊVãG3—˜úúæÕ&WÕM¦î]û®gV‰–-R¯Œüe5 ¥»?ËìU´lmY» ½]ÍÕñ ³EŸØ¥Ñì›~AtÂj@cQqÏ}æµEqOíkçÓîjúóoLºÈq‰w)­í›"S%©r±Ñ>¯–˜;5͘ÄûÕÂ=bU†d—””_ÝÒÞÕÕÕ¶¯$Ý×"Óœ¸!d›ãúYÖ¸úøû2{סâôT6‰“ûô½•— “™™™³ýP'\B"Ôù[¬ÆXûÏ|”ÍD¸Ì%ÿUÞià|”!êøŒ-ÿmPÌ–,C¾Y‰­lÈøä1~>ýôÓÿþïÿž7oNAIH0Mß3¥™5X‰ï\ºˆ²c­Èõ×_ßÐÐiáŸhS]ðSìrMHÝÀ— !,ì§®x;’ü¤Òdî[$/Êt¬ËÑ¥Ym^?Ç%–·¾¨ªõÌ4æRšŸøƒ\µ’“…¬­J+H(bîÚÒä Òd›’Än\%8ÊfÍ=kúNÃ<íÁ­á¦¨RÅ=Ik—y©Õ*ÓBø”¥áCŒÚ?*“,ÉK–X:ð[Õçg÷ Qý!ÜQ¿+O©]eÖçÊì„J³Ü›3)ÖþJi¾³°y\iíÉ‹YÔ³ù^m» J5ÞÂG›µ]˜¬ð ¸Stá2ìˆMY~“Wã_‹$½ËÙ3dœ÷L±[ŽY;©/§dýØ«ûHQ©9 B²õEièÆ’kMhA¬)á–¹àjî&ŠÀgŸ}V%ìÓÆòP*•X€8QùMnº¨ Þ†.\¸ðÏþsXXØo~ó›ÉÍß±rç^£ÐZ Õ®}³á·ž²älÚÿ}4Ä'"Yº×†Uõð!B⟫j3!…†—Ù¶9ØêóÖ;,¿ôÚãüáÀ>ê×Ë-:=ºˆ•Ž/Äõ‹CßÝa.æ¶äCø\Â9tå¶ÆíiÃʪ9*é•üÈàÐ+Yü3µ9¾¶(;“×Eó[Xížý­ _Рæ=ŒF[[šWPTkÆeÕmIJlqpÐâ=¥¶´ @ª‹Xÿ²Ù¼õ)9ú´G@Úvì6.D’ºÛKƒÜFIƒ*• äÏ>û,tÑßß„i4š¯¾úêç?ÿù(u¼h¨#†°0,j8pÀñ 8y%gi .6QXÄþ"~ð²ß¼pCcñð6¯Ö÷ˆÁ©õæ¾7 õÍoIFü¸Í CxiR¬-¬2Ï9å*7ý™ É*â·~ÒXžc'_uZ]kŸñÀ&Á;4isqšZ²—Ù!B'Š¥Ì]¶ýÄ'ùâZ+ÍåŸ)¼~ï;ËFv%IÚXžFaµ_},#±‹EH'bÍÖî£uEÓͪoíË_*:`ÅŠ™ä³ø£OÔ[Ú*éÀ,Übsjë ÅXE/ý/ÿÃ'zß©ÆB»kT‰å 'òM§BFmxS[S¨ãK,‰9å'úsÍoD%^dµ%ÀškD¬¡e±¦„+³ØÆ"—q!ðúë¯c6ÍÌ™3SRR0Ñûº•””Ü{ï½®÷r÷¾ûîÞøj­\¹gzŒ =gL„?ìmìåF"¬Åb– ˜âÊøãŠýŽõ&ƒía…ãÎ;ñ¾›øåææŽ=÷Q¦ ïjÖ~Þyö¼‡‡Ö;ú††‡Ú? J¯ëéëçw!á<<¼ÆC=øL º–æOÛ»Œ)†ÞjçlŠQ–nŒÑºÚ[>o9ÒuŽ?W„ó@ånUZÌ6}][ó±3ý8³Äkvм@Ÿ!F‡…$ô=íG<{Ñ“?¿d`º2$xn Ÿ½N³¾½åèÉγžÓ§c,k”ߟ7×OÂrØò¸»'š hcßËGy›JcR^¶ós›ƒø{zzâŠo)^táŠ?Ù0´ä¼¾:ؽ3n"##ù¿®nð'ïJ±M:ÄX|âêÕµ_¿ yÌü/XÁ@/ñ)æ ;SP©£è;Ù¹_x”_ør•+|ävg›Ê¡ÑÒ^Ø›¤ CümfŠðÉKÂá’‘ûFD¤ã' }‰†+‚»øÙm>ÌWfÜ…ÅÔ1jZ^^¾gÏžôôôgžyFš öžÞR¶ã7VEE6•ÃKÇíÛ·ãÇ– Wv¨ªMˆ4J33µYþSêNv"@FM€šÕ¨ÑÙFüâ‹/ ˆÊÂ9_Ì]pÛ`n助ªØÜÕ=uÏzÂ¥‘ßíQ²‘#š´UWÒ­¾pTY"0j¶mǪq:e÷Œˆ£€!‡¯¾úê‡~ÈãÛªU«°*Ã=™HkÑcé­»Ù'\ÙÏ[†Uj‡ ¶]ç@»wª/9é%ÒF$µ<5 ɤ¥¥•••ÁŽž~úÓŸ®^½›×HÇ(w&0ÒÈÚ§ØJ¥·¶v)kv¨¦Ô…ìD€\’€m³’º ºx ;ý­Šçƒ>ˆÉ5˜Ü‡ÉMC#w÷$0Ò8jp˜ =AÇ’ºH‘8 Ó§OK;ŽXBG.&ÆcSS HË E„‘ºˆ¦Rׯ_?•‹7Dd!ŽM€-Þpì2:bé°üC¦•••ß~û-ÁÀbvG,¥3” Ë:ñ"Ö}æèâ™L¥4:ÃW‚ÊHˆ€“À¼J¬=Àªƒ¦¦&Vô;ï¼ó—¿ü¥“UÃaŠ‹…ÿñññ~~~ØúÇ}æîNÔn8óX© D€¸ “'Oþìg?Ã^ ‘‚.â} öµÇ.6˜ƒ ut ã]OLSŠˆˆÀIµµæ­Ç;‡K¤Ñá ˆÑøío‹þâÅ‹q.)¶ÿúë¯_xál|:ºÔ(–Hà¿øìlB¯èèÚPuíçKµ#nDçŸÝzë­<ð@PPU{⫚””ôë_ÿº®®cª×]wÝÄg8õ9P¯qꟕ€Ë"ðïÿK¿>úè#«XO<ñ颖±ßúúúb6/vkyå•WÆžšS¤@Òè‰ Iˆ¿^sïÞ½?ü0NÜ}ê©§Ð!(“F`Íš5ÈkÇŽx “–éfDªSŸ²&D`D¾ùæüQ.--ýòË/Ç_,_¾§‘Œ(2‹-«ܞžžÖÖVwØH¤q<¾5” C ³³;ºíÞ½'%!‡n¸!99³B®½öÚ‰ÉRµOÛ*áU®}?Wt%itŧJu"®B³"ßxã tñ®ëñÇÇÔSÚúÎUž­C׃¤Ñ¡Ž¸9ƒÕŠnŽ‚ª?™H'“6åEˆ€}x‰…•ˆ˜_ƒ]¿¥!°Þ<11QêBv"0 H'2eAˆÀ°ãå—_ƧØ]ÃŽŽŽ!ƒ’‡{Ð÷tõè9o??ÅÄ -ÞpïïÕžLìÌ EÄdwÝuÎÖëõ÷Ýw¶±ž¢â8M¶-;×âmkISÏØJlÐëtzÃØÒ˜ÔØ†¦²t/_ÿ€ÿÇX÷•{âÅwDÅ @D€¸ óçÏÿêW¿Ú¹s'ŽKDýýý{ì±µk×⢻ C=ûÏ|ؽütÝ1]s‚wd­º¸¯~b ÉL^T}sNr²S©SÂfzMB¾$“™² DÀL`Ïž=/½ôîÕj5&b…¢ûœç`¦0Z[Äú·O%õ{ùøŒ6!žBÆ+âŒ+Fä^|ÕÅŸÔ¯›œ2Ӏ꘾`™Ë%-|íµ×Ž9R__ÿÈ#.^À®Æ¿ÿ¾øã=bu([»vƒ¦¥MS–»³xçÏŸ¿4]ÓÎ{1ÓÙTžÄ;ÏŸ6?&)WÓÆwÓ;íL_»Y [mòšôÜ|xCOËÎüt>Þ,ݰegKq¼õ’¹púöêüµÈ„/ALRþÎC|6F£oÚµ%i)_†˜¥kówjº†Å5tîÚ’.Eð¥Jö±ÀúvMnú³•HP“ºnC¾´‚¦\&à»þŒÑ`c½ï¾û r/\¸€¡’ÞÞ^œHŽMÛÛÛ;ö¯ýë“O>ùàƒpÐ6~-â]Ne“ÐrrrƘ5E'î@-M­ïäЂЎКЦвоÐÊÐÖÐâÐîÐúÐÑÑÑ*Ñ6§^¢±caâ–Áõ²n,ŒÅ—¡°±UcvYP×€oG=ÿµá ºçÂ'.ÅÝÚb>Ñ6ö v7š&«Ôj•É+­•Oæ¹ öiSLÄlTYuBÔîí¢ŸÊ”¬*G(Ÿ²…éצ™Ò‰U›¬Y­ýƒ}…¦[þs³Pw‹¸pC½F)s²"0>ÿüó§Ÿ~zΜ9Xw½7±ÍÛ8$JIdžæ—ƒ¢=§FÛyèÓfñ¤ù×7}§«)ä7Ò˪?5ˆîùàÀÁb^þRÿòiøºÝÝyyToî̈Pô{ï“%w ®¯?<Øßš£Æ}Ñ{Gùîß°¹pžªTYÚ>ä3xªq;Š -x±QǵUç&ÃO•Öp¢oððáSÓx¿¼ìW[ÃÊÈŠ—¨Þ|´op÷îúS|`®`Í–CŠˆ S Qà cL¶Êxˆ[’Æ!À3 —I%þå/ùኃы½ýöÛá2{öìËL‰‚_UV}î²p9b(Â{‚׿ú'8n€ëå9ùQS'VösÑ7—äi ~5{7… ’í±ú÷Y]ʾ³ïUðbW\ùûè@ÞOæõû×·ÃRYÛ q…ÇÓQ×WoÒ‘ùE5{ùáYS=Y…8{™œ7š[J©%€‘ÛgŸ}¶¤¤gÁ£$Ó§OGSl S[0wÈ}ùOÌg5ûßéjí³è)œ¦´2{I%¶>UŦ-ýÙªåê;].yHtr¬gyqú¦ÆÃ_Öjø×6fˆ\ ‚ÈÝ9ÏG”Ùâü݃ùP»¦ÒZ>•Ôć÷Þ`J®Wpê½`–\æc`éÌ””Ntt¾òL{·äҔȤ|Šõ™”Ü("@\‘@KK ;롈8ù»Ø¸bE±NþÓÍ‹ÃÏ¨Ž†[žþ]½×Æ—qæÀrªúÿ­Wû™òãzš|Æ)¿oÝ déhÓs&•5œø'ßÿéor™ ½kœ Ê”p ˜þZ^^ޱS¼V´ª&Ð’.Z1™Ê[ýñgÄ,Týª™_š!óQ†,^÷?¯0sìJcSx³¸(ƨ‹º¶]/ñºè)3÷G…¨6Åœ»Ôp¬ýÓëÍÌÏÐY¿1³¨¶öˆ‡wÈüôTmiÅ!æ…ëüŸG.\™ß$º-ŠÙwñ…«üŸ ÑK÷VÉkpRß%dµŽ7q÷$Ç–R&®CàŸÿüç“O>°zõj,&ijÿ„¹N]ª&ò°$^™*U^I%ÕûvU—¬YÅË]ÊR~¾Œa€í¬Ýºf-Ö5¼…NÜ’EI%;wæoHòžÇS_/z¹Ù´ºq8~ÿŽ_YQ´J…µe%¹·,Ñà>m}„Bvÿ†*X+3Ì_›»³z熤ù ³‘°ªfc Ü-rÍK›áR™‰tví«ÎMº;¡HÃq±¿{Ü8Á¸×~Ù2©ñ¹£ÕñáH©—$ÐÝÝ]QQµ‰Ÿ~ú)« ¶~湧Õ_ßÓÞÑÃÉd^¾6çbèP„^lK'$}‰‡ú®–cß `Ö‡ï¼ÐÀK®@cõ³£ÞcM’â"à¶oßvÏ=÷Œ®,ø!ûÊ+¯à‡ìý÷ßeû±±±PßÑ%E±¦€_`¨_ ýü±ÊCB¥áä>¦‰5RסìrŸ@{ËB„àŠÀP‹”‡Jƒw—û…†›'í r‚ýH'0%O&FhÒÓÓ!lèöZ ÅgŸ}9ÄûÅI¯eH¦˜MÙâ@Ùñ%püøqÈ!tÉîÞ½{øÄñÊc×®]?ýéO±­“.Z1¡[7!@½F7yÐTM· °wïÞ”””³gϲڞ-Æ% $ã‚‘!D`|`f tñóÏ?ar˜¼úöÛo00##$@Ò8BPŒ‰%‘ú l©Ú××wY9јêeá¢À#!€W׿ªª4Cu$ß C¦žþ½ð ˜q:Š¢ÔÕÕaOTlJ>Џ…Ø%àÚ½F’F»‰€#X±b6uÃá60PJÌÄÁv©˜CîÜ9(¶³Ao¦W0#ÖÁ°QN\\œèB"0F3gÎD 8úeŒé8ft’FÇ|.T*"`M?Òƒ‚‚F~(´É”‹Ïh³k t?6¿ýío—,Y=¶d46I£ƒ>*#l‹Š“¯½öÚ1¦Cщ€]ØG‚m%a××Ùiñ†³?A*? D€Œ3’ÆqJÉ"@ˆ€³ itö'Hå'D€q&@Ò8Î@)9"@ˆpv$Îþ©üD€) ðæ›o~úé§Sñ¤dIÒ8)˜)"@ˆ€ ÀIgXeûä“OºP,ªBÒhƒnˆ Dà’pJèàà Ï%C:i’F'}pTl"@ˆÀ”øâ‹/÷¼y󦬜1Iã¦ä‰ .GàØ±c¨I£Ë=Xª D€Œ–“Æo¼q´ 8z¼©Ü(îÌ™3---ŽNˆÊG¦šZÊTò'\~@u*¥ñEÁXð¦"@ˆpl8òåäÉ“2™,88رK:úÒM¥4âL“Y³f¾ì“¸Ó§OSÇÑ=µsÔ+7pª6ö‡::G‰/¿”SY±õë×ã¼òË/3Å îEÍ$//ϽêLµu`8"¥»õÖ[¸Œc-ÚTJãXËNñ‰ D`Ò ÜvÛm8›¤qÒÁS†D€"àÀpˆ±—nŠFëÇ"%Aˆ ®D€TGò4 ÍÕÛö´õÊç,Z¿2Š…!D€8/ú;?’g§ÿ/¥fk8NU¸ze”Ën8†"àh@uDùÊB°<=Fœ"@ˆ€p‚^cW[SÓ᮫æxt}ÍÍ»íÖð@I·Í ×õõs^Þ ¹ÌÐÓÞüÙÉsžWßîc¬–¡«íèçß p×ÌRÍ ñ‘ÛT=mÇÚ:/  Ç¬Ày!JI@äÑÝõ¥ñËÞnž“É9!]Wó‘ãßž;ÇM¿Fy]PˆRRØW¥à ‡6&.(ÐXDM+®nZ!qÓµižYSdŒãÔ…u/g,EÀæ²ÕªäJc mvw6§.î«_'$¢?T–» ¹@’¢¦Õ—=§|{ö IDAT‘fbáO7D€'%ðÖ[o•——wuu¹¼4:ð€jOS’w˜YU¦ïRm^ä½›:w‚U¦štÑèªkÙy[€µ.¯(5Æ;½ZgJ‰ÓJškÖEµZÌC“¹$,ÿ@ð‚9¸1Þ™‹‚EW¾ÀZá¡)Š™{wu‹M,cdú D€8+ªª*ýpÖ Œ¸Ü+†}¹«ŒµÄ£§úöuÌR 5Óæ½ÊtKZOuZU}ÃAí«‘Š¶gÂV±žfâæºŽþ¹yª±ŠE办ךÇq-5¥µB ±9UÈ¡¾þðà@÷Áíi,Õ½‡ŽÃ¾¶ª¯¯5‡‰¦*§µ¯¯ïƒõäö]Ï$3©ÑvðyôwT™Ê—ö¼I¼Ybt%D€87üñ{ï½÷¾÷½ï­X±Â¹k2‚Ò;ª4vÖ?mž´ÖW3Býøw eT~e «TåþãµK)ï®ß¯ŽŽ ìÒ” ~±›*6,V ïý"âßm­b—WyÀ è?߯Éq‰…¹ñB'ó‰ZSP¬æ]gpœ™\¡ð¿þÞ…»Áß_ƒQèÎqÆLº+–…+ùi¹2>ÿݪ4c&ûÛôBº"@\À®]»ôzý=÷Üsíµ×ºB}†­ƒƒJ£î̬G–R•"}ª¼·¦0'''+ý×IꥪË[išý¢ß_Év›To\- ÃÉCbÓc‡ÒúvAÓ«[[[;N•…Ãôz]{Óë¯k¤ñ$ö^£]ßö¾1Í£M ~òåëž`|Ü!‰IV"@ˆ€sضm*˜˜èÜÕY饲3²“êø?°|"nö·ÌÐgYFî2K'¬7ôó §¿avÍ‚Ÿ,e‹.Œž½µlü”;~FÏa¢ŒLá"ç¼µcëûMG¾ùâËZå„1Mk‹á|Ÿ1ìK?dzkÊãKS&!µŽM÷D€'# Õjf̘AÒ8•Oî*ïëYö #(‡Åœ—+Å“Š.F‹Q´t-Õ? KÐXû^Þ}í™0^G Mˆ€())AéV¯^=}út.æ¸ÍA{½GX=Ù ?‹ú ¬-¼dÉckŸ ¥ëáå%Ä2xÌáß]¶ouQRü䊨ï+gΞ¢äÊ–ú&û~ÛÞÄn®yî¡`á„æ9àááÅŠ6cN˜mxr!D€8#Ý»w£ØO>ù¤3~e¾¤ÀŒ"Íqˆ2'ê.ŽãÕ©ýx7!Y#hhYëVÊq±Å»×EØÍé‚É58,<‚—@³i;°ïp×¹k‚o —qú¶ ˜OJyß¶•æ< Í™.ò±åÁáá–Åеí{ÿð9îšÛ‚”­Ùˆ ##ðì³Ï~÷Ýw7ÝtÓÈ‚;}(†ãs½±ËUð›Jé*ˆ®†?Caf{{ Á^qO,{K\û?-aº4Ë.‰‹‹‹É܃XÃÀ6—4ñŽf]ÄÜÓ†=ÆE#ž6Ú6ãJ椘w1Ì’fˉ¨ž_Ãç³ç¸Å0¯EIè†"àT’““üq§*ò˜ ë Ò( ¼¿œ‰6û¾µeí:Ì(Õ·(YÃf†ªVühîPõ_¾N-øU&‡åV71åêlÞ•äÃfÙälZ-”Ïðf·•[ÿt¨—1}O§¦lC@L6K¹¶©±‡Ù°îßèô·ý-=Ø+Nž±Y-¸•ªÈmê2ÑwîÊOZ˜§áÝU›W„KWK"@ˆp*P®•[ØÔOmir·Ç´i^s¦21‹-._,¬$ [ÌÕñ‰.«Éb^y ‘^Ӧ͟6-@gì ¦Õlˆöƒ¯Lyo±1ƒ‚AÞÓo@Œt×·ÒU¾Ó’„N¡âîµ`å’°ß»_†Fe”e±^§&/ÒßkÚüùÓ¼â²Y&ªª]|dˆ DÀ 8¬4rœ_ôînmŽÅÊVo®Ñ¾)¾e4ÎF ža9ö²,¿ãàv1*TDN)®ïÛºÌôþÑg]ÅQ›ôc·ì8u°Ðô(;ú…aËsÌkyfzò¾òüO:ʳL™hM™`ÕÖâ±(„  D€8'KIq´:ø„çîLoo9ÑÝ¢ÉfÌTò{јLøšŠÁ5¦;‹OeԚ݃ mÍGÚOŸÃÜTœ¼1'8ÐOÁ¡¹»oknëDÏ«•×™ÎÜÈè;µüŽôðòõ†EåêŠÁþ-}˜ëåím*•)WæïŽ{ºíÈçíçpfNÞ˜='0ÐOR@‹"Ñ  DÀ‰œ8qâìÙ³·Þz«•y¼ŠêÆ}C}GW_EHxTÈ%¢Ê”!J›@ ¿@›W…r?éYU¦d±m@”ŸM|“/}"@œ‘À—_~yûí·cg¸ÞÞÞ,–sÆ*WfP®ØäGˆ EŠøÓŸþ´§§çÇ?þ±ê"°’4NÔw‹Ò%D€8)§žzê“O>™7o^YY™“VaŒÅ&i#@ŠNˆp)………Û·o÷òòzóÍ7¯¾új—ªÛˆ+CÒ8bT"àê°Wê/ùK,f+--?¾«WwÈú‘4‰†<ˆ nEÅõë×£Ê/½ôRRR’[Õݪ²N0CÕªÄtKÜ@g'¿[âßÿþw äééy¥`ä‚™9sfffæu×I/å°ÿG}tÅW`4ìšk®ÁAB0î9™Âݾ*c©o?tqpp°¨¨È­ö„³ ¤Ñ.r$DkËP`l‹µ`Á‚„„©ûŽ;0Bê;΂@BD_{íµïÿûR_Hé‡~èáá1kÖ,h-ÔãiÒdwx¹˜››ô裺C}‡¯#Iãð|È—L=vÜÁ=÷Üó£ý¿ï †©—···Z­¶*âøÃ‡~ø›o¾ùöÛoûúúpÅÒ´s‚ù÷¿ÿõjV҈ѳ´´41t7}}}!“0sçÎ}þùç¡—¢/³ oAòiÅÄn7lØàµ—*4Zcl©Þ°bÓ_oˆÙX±5ÞfÕ¿uàÑßÚò^³·÷̽é»r—…Œ>Šé6 ‚øEÕUWá×=®l@c«¶#¥˜sÿ—¿ü õ ÊD`+fK–,Y¾|ùW_}uæÌ™Ó§O#X—`lÿþý8Õ}ñâÅÒ(˜Ð¿nÝ:tC‘3³M* —&;pFn-†®¦meïõròE«×G™ö+8}ot´3O[lY>îÏVßûa­Fƒd—öŽ{Ú” °%%ƒ±u‡Khh(¦é‹^_e‰+¤ô®»î½˜é g‰õà0---V¾xÍyÇwH?ûì3¨)^|B@¯5Ø¡îÒ`dŸ4X³ø‡?üá‹/¾xýõוJå¤åëD¹µ4ê¿Ö¤fgãim^´:Jéczlƾ¢‡é~B>eËfÆ„¤N‰Ñ€²NàPI<òÈ#x»ÙÝÝ}J0¹Å'®0P;Ûÿüã1³Ã65ˆ¥J¥ª­­õñ[Ÿ1‡/Ø6¹ŒšFÞ~ûí_|ñÀHCo'i´ËÓ­¥Q&ÎÐÀ)vtpb{vŸ9'!€qZ¼€„ɱï˜Ü`_ý5ÞtB>qeŒÜ‰€{ÀÓÀÀ€•4Bz÷îÝ+&‹¾Þk ÃÃ3ï¼óέ[·Š^Ì‚AÈ#GŽ@•ÑÁÅ<á)ïoaåv“imm…(¢¦(-Ö­žŽ+ÒÒg•7d­¸Ã£ç¿ X(ŠŒ:«¼ì鸯îÚMO%ð:VQ{4>#‚ãÚž1ébâæºç3b”rYWSu|d‚Šž‹ÉUGTõ÷wìÏŸ»„ïæÔµþê.™Â¢«wô27¬Z¨Tè•=³ ¹.•¯½ÿÇ5á§Ù”h,IlakÅza·gWîÏãòø’¤F<4˜ÏŸ2Ù¹OÔÅ´í “îð6tÔ>ÿTB^­JÅiIˆ  ¥BíFžVž`Ì¿eJƒM` 1˜œþÜsÏA¥©=ôÐCRuÁKP$“IH©íÙ˜ÿ‰·z+F.V&<<3¤‰ÃŽƒ‘þùóç¡Á08ŠYpEà§Ÿ~Ú*ü¢E‹­1€Œ®!fHY¹Óíè84¦”Í_)Œ”*£óê²J—ðUM,7¥ðêPŸ›WQP+öñ:5弈q\ìæ†Š Ñ‚•󋈷•[07b”Wy`£z~L( ¼üýü­b!ß­,_Nµæ¹â׊R5wæl7Ç):÷¥²¦Ê:±;Ãtвϲܪýqü0nÁ[MY>‡^}‘ ­ªuk<[¿Ÿ»ÛŸ‹‰ÉC02D€L6t1aÄ\ÿóŸÿ@™Lb¼ÑöOÁŠ+ض Ø9SZ a¸…A ÆÄ„O«^)¦·`‹˜¾Ô‚}þ ÄX–*uÄtßüãRÑŽI¤Ø¼ WÑ–µk×¾÷Þ{è b©kpp0DãÃÒ0d#Ç—FuJœù ¢Ò(C…¿~HÐEcõ%¿ñôû+Ù BõÆõF]dä!±é±\2$´´¾½dYˆŒ3˜à]”¥-ò…FÝËij9mã7:Î÷̬¿—²q­I™ÜÿØ NxÃY¯9‘aøß½L²ÓÒºhJýñÿVåiX"¦RÐ' S@½@öÆñÆo´›=æI§á%(’É$V>Xé"RÀJ¬HÁ@®­A¯ÑJ~ãÆ555ø±Ž¤¬LDD„•."¼Uyì–™ÇHÀñ¥ñŽÙ ÅlèjNÃ<5 ~²4Öbî[¯©kyüŒž³œ(c›Þ3%ùÂÛk†Q±Îãø?øUA§úñ–ʈu s¢ðsÔØ…Õ}ý¡F˜ø¬ß}û(1ªBÒ(С p&x Š.Ú0½´(ÁŒ¼J°…yx 9 ^c}/³Œæ¡I ­AöZ;ØÞÇúïUÞ× 1TÁ¶dæ.¬i]Êý ,uVP5W©±Í˜\ˆ D`Š ýçŠ fÊ~*f jõ[Óø\ð€aÀèŒO//¡¾9ÖJe—ÃêÃaLoÇÁ·ãlDÎ2`ÿ9óª ÇæÁ–~|¤de¨ký‰zeDº#D€Ç `ñçÚ1Š4ÖR\0%a©[möî:wMðmác«÷œ¨»„ASÍ{ÿêT«•¦ ùÏÎßÖ÷³f@5ƒ;WôQÇÖeÒ±Wéã_2/º"@ˆ€ƒøžƒ•gìÅQÜ›(¤Rû?-Éui–-\“¹ÇjQ£§ //Ãø(ç°ÐyU]ñº^ý­0–S­øA ' ŒI1ܡ锬/ÙJ/¥@ÈNˆp®'\øòujperXnu“^°w6ïJòaj”³i…ùu à»wχ]=,¤àp‰‹,$n;›û­É\´¶¤]'LÒwîL_”­â&nü¿aœìGiÅ,­¼˜€-š6>œ¡G³%i‰Õ>=,]‰ DÀ¸„4š^éyúD—Õd1{^B¤×´ió§M PÅ·…K«ÙíÇ|ÁQjÁ†­oü}_nêaîÃ_…——ŠÕ¨cqµ¥©AÞ111Ó¼V1ñMlزœ å*Â×Öe±€\fÌ\”ÅÃ7&SÜ¢nø¬È—"@¦€€K#›i:Ã<áxdÆ[oé¨Ìk6Cçi$²,¿ã ±_'qè2¥¸¾oë2óûGö2e£¯Æˆü‡½|ÍîÜl6•G¸x_ÇA“êqØ·‰“¸YÛ]í'¾Ì”-η¡Ø8®*–%§¦~{¿)†Â[ )D§  D€L5iØ—vŒe@ 0ØQ‹aq¢[èzQ0ƒ­`ØNH𭨨xçwrrrpZÍs6º®­ùHûés˜›Š“7æúÏì°ˆ¤ïéê1pr™—·=o‹°¶7†Î–æ–ö.΃;{– žbì’ZÕwµk?o;7ÀyLŸõý›Âíáaƒn‰€‘šI^^Þƒ>ãXŽ+VˆÃ`=8 öXa{’a2,¼Ã2vì C‰¸\®ÝeQ„„G…\ ‰ÜÇÏbŽé¥Â[úË”¡ÊPK7{wr¿À(?éU{È"@€€¨:* D€¸!’F7|èTe"@ˆŽIãptÈ"@ÜI£>tª2 D€ G€¤q8:äGˆ nH€¤Ñ :U™"@†#@Ò8ò#D€7$@Ò膪Lˆ ÃpviÔïÚ°tþüùé;-Ù°­²¾%7iéÒ¥IÕmV§nØ%"@ˆpkξŽ¡ãH­VËÝp¦ÿÑÐßXY[Ëq1y/^"äå{ºš¶•½×ËÉ­^ŸA†"@œ˜€Óÿ·Ø}|¸1Ð+øŽüä©á³ôÓ­IÍÎ†ÛæE«£”>–žtGˆ NFÀé¥q¤¼‘oêÐdÞ~ã/]2™ñȹÇå‰<ÒÂS8"@ˆ˜DN =íÍrÌcŽßÀ×篹)<2ÔÞ¥'_‘žö–ÏÚ:Ï x\¥¼åöp?óéSð”yûøyãöƺ®æ#Ç¿=wŽ›~òº !º}]mMM‡»®šãÑõ57ï¶[Ë“FN>ÅÙÙ³Ýz½L&—Ûf2‰Ï”²"D€1pì¿áú¶’´e©¥ây‹BUUiõ»žS‡(¤õîmÿ%éO¥i$ŽªÍu¯oXl:Cßô°W$Þ56öeDˆqõ‡Êr$Hbqœ:­¾Ì2}]Kþã+²+-‹›ÓøÊƯ£«½TâÁÄy1AyÖYX¤M7D€"àøx†ª®y­×\QÕ±±üÉ¿0Ú¢˜¹Þ%M=ìŽ]5©–ºÈ‡Ë^¶V2sÕ¤‡¦ˆºêôÖº? Ò¿»ºÅ4‘µ§)É;̬‹ÆBp\m^ä½›:9ƒ)œ)UþSÌBêHv"@ˆpŽ+šM‰¥Œalakß@ýî݇»krb™[jdA»5aUa¶o`pp »®0‘y–®ú¯&{Úßö]Ï$±Ž`b¶ñû;ª²ÔBDmBØó¼Í°/w•±S˜XxôTÿàáÁ¾Žƒ¦Py¯þsNUk]Ž‹Ë©kíëë{"bü_g²ôéJˆ “@ÀQ¥±s_Z [ª¬»3Blà×gYnUQ— Þ²ì87þ=cq8Pæ³8ãÕº,Ö¿«ýÓ_ÛìqìÜW$¸Ç6tW, Þ_Ê•ñùïV¥±ˆyûÛô\gýÓFùLk}5#Tx{©PFåWÖ°4+÷ÇIëJvëïç¯P(,^qÚ˛܈ DÀ‘ 8¨4êÎ|Á:t)×Zð“ßÿØ æP¯9aö‰-^eÑW“Ŭý5ó=ÞÁVm˜Ã¦o{/aÔ›7F[ôñäË×=!øp>î0£*=DúZVyoMaNNNVú®C`‹Àqi(Õ„‚>‰ ÎK@ú÷Þjqü„Ò¨~|§¥2rœ|NU1¡Fjbï»Ûô*Ñè, ˆÄ ª8AFvÃù>æ¢É^°ôCã -séýÒ˜64õx+q³±_hJÇgYFî2Ó }"@ˆ€+pPi¼Êûz²*Øß¦„2+âyˆ}¹!üMεü&9v :›¦bp 4³Æ"r"D€¸$áqŒZöv Òq½;«wwýç:l Ù{êœÛˆb7×<÷Pp¿y›¹/¶öqÆœ°îWY18OÎv-?4b•¤ƒQå) D€Ø#à ïçDÝ%”VóÞ¿„‰¢’¢w~ü¶F¸0CtÖüýÅbŽÓûÀ8šê)†²g‘‡‡GDE‰ÿ¢"æ]Õy æ4ç!3ƒk?ÞmÙвvšÌÒ’& wº!D€ç'à Òè£œÃØäUuYPîzõ·l…¾jÅ$¯!5™¯Zˆ£¾¦(•ÅKz(Ü"áF1ï¶¼£6³¤Ùr[ÕϯY³ç¸Îçú0·à7•R‰îjø3[X2ÛÛKš¸§Ì¶s)õ'; D€8•FYHÜv69F“¹hmI»Nxq¨ïÜ™¾([#`MÜøË ã2,/;ТÃ0§¾³zë˜vÅnÿq ½1OyxÆfµP©êܦ.Aõ»ò“æ ¨6¯ÀJÀûË™„j³ï[[&Cßv dQ ›ßªZñ£¹B"ÆËÞ=vvõXJ­ÔŸìD€"àT9N±úuj ¶45ÈÛ#&&fšWÀ*Ó"ý†-ËÙ+È fÈšä…aÞåô H(ÐÎ꺗W5g'*£Ì¸ôQ“éï5mþ|¤—ÍFaUU»2üø$ä+·40Ö–&£Ó¦yÍ]˜Ê–Ä—/äY¥ò«Í^àïû²å‚KÁ‡.D€"à4V9Yàâ}â¾3ؾM£1BMÜ¬í®ˆö3ö½éK,,/NS[PW¥5tìcÒeá.ÞÈCò?é(Ïb‡}å˜Þ {¨¶~bšüã½»[kÚ„GŒ¬Þ\£}s]„ñ^¡Ê1í¿#†  D€8){ƒSö©HoiniïÂѳg¹àù!BwÎXHÅÊŠÁ•ìfå£O·œø¦wàâEYׇ…šÄmèúÈ”+ówÇ=ÝväóösœNÞ˜='0Фºb<ŸðÜ݃éí-'ºù™¬²³ƒ•ÆýyŒaäꌊþÕ[z œ\æåí3TOUL‘,D€"à¸Zl2eh„2tD}C}$Ss¤q Zã„U©«`Wø…Dù…Ø8[; “8 *÷ñSZG¢{"@ˆp>Ž/cfjèijøøÍ{cNˆ D€· àÒ¨?–³Dc|šª[f{»Åƒ¥J"@ˆÀh 8î4œÑÖÈ&žÌc†Ñ-±ªñ‡›˜c•ˆ DÀ ¸A¯QñæÀ–;Êär7¨­~‡©ÊD€q&àbNi³ÓqþâPrD€×%મûð¨fD€"0H'‚*¥Iˆ NL€¤Ñ‰"@&‚IãDP¥4‰ DÀ‰ ¸Å4gy>†®¦meïõròE«×GYž+â,U r"@\€I£=DýךÔìlhó¢ÕQJ*…"àNh@Õž¶LæÉJ#÷ #‘è¹PQˆp7ÜkęĦuúú®ö£ŸŸ<;0à1}ÖÍ‘á>öJÝÓÙv¬­óâÀŽÐ˜2/Diuò†^§ÃÁÞ œ™ahoi>yúÜÕ×ßh:%CßÓvìXç·|È%dÞ<¥E½Nß?Àyy+°s€®«ùØñoÏ]ôœ®¼)<Äx ‡¾«åèñÓ¼ãÕ×Í ³:›ÃøÅBÄ#ˆxŽÃ)×…Hº†Ø”àÔéS,ØÙ³Ýz½Ìz‚¡ãò±P¾¾~VÊT~‹|é†"@†&à誵™q–ºˆªhSc‚6ìk7VJw(i®YÕj•©²šÌ%aùºL·ÓˆÔ…F]Ý›J’̺¨R«L hJ3Ã~²EŒ/ó¨Í´ÔE$Säk¥‹p-H«nÓ›rÑU§/°ÖEøiŠbæÞ]Ý¢C§ÿmÌ€àrɸ,žP¼ÊT“.Ú$FD€"02Ž.B-TÅ ­ýƒƒý§ê‹ÓX½ –d7 ºÓRSо LlNÕ©þÁúúÃÝ·ƒí=t\𴾤W5<¸ëñHÎÐR‚Îob«´§×><Ø}â`HMýq{’•UÞÐÝ?Ð×Ñ"IXUÞzª¯¿ïDUV,s®¨=Ê,í»žI(Ò öÄmÇÀàà`GU–ZpÑ&„=ß)¨êïo­ËaásêZûúúžˆàgâ\:.‹#½ªÓªêj_d?¤^d'D€Kpi̪ۻ.:ïýdr?õº­uYLµ*Kßã;Žýg¾ê˜X˜ïÇ^Ê|¢Ö«yçœù,ÛµÝ[×ÅGGE…॥¾ß¿¼0>ÜOHŠó Œz®Ì¨R¶ñSÊæ¯Œö‘ËÊ輺,…K,7eˆŸB®ŒÏÍ3j£Ñ¯sG­mè®X.,Ë+ãóß­2*pÞþ6½\.Pú³þ~þ …B¨Íˆâóa)åÝõ[ãÕÑQá;\nQ`º!D€8‡—FÕæôÅJ)´Åé¿gÚxøx7ÜÓ«[[[;N•…ôz]{Óë¯k¤‘ÌvUVÝšpɺExõ‰ÖÖe+ `ö®§½æµ·Ìq,lê”8óD¥ñ•gᯒNÚ‘öÖômï³—êÍ£%9sœ|ùº'XÚ>î€Å`Êè"džR¹‘Ç5EUÕå­´ÈÄäAŸD€"0B߯˜9ÃÛª*>JH#F'gî2…z”ÞÚ±õý¦#ß|ñe­† \ZEoo4v M.2¿À®åÀŽÜ­M_ùBû¥F;|wÌ–h (f¦Ôì|Î÷1WMö‚¥Zô'{¿dc¹ÜñŽ^;1!–—WågÍËnÂäHˆ CptiTÝq£D‰X5,ôH×Rý“°Í´ñ¸h墫Þð“„‚'è;fµµF-´*„}a´ 4²¸ö^Z¦CwD€"0<1ü™>áqòÕžü7fÛHÇ'ñÎQ’vû&QÕ)ÅO®ˆú¾ræì9!J®l©/Ö~\ÒtîÛ$êbJNñŠÅQÊY3g…x+ñV±%–iŒDÄ,cˆw±›kž{(¸‹+fÀÃÃK&ÔfÆœ0“£ýϱĵŸ"¹"@ˆÀ¤23D©uî0ŽFŠ¥ÐŸø‡qýŸ'^Å}d\'˜RÞ·m¥YA Í.b­áþ×Yªò£¬×0r\Ë?ˆ9Ž›Ea‘š®mßû‡Ïq×Ü|©1–¸YÒ  D€\‚€ÃOÃѤn?Ô#©„þõß'³Û˜»ç.à½#Lâ ̺Èq {Lò9¼ä.p,™ª¹Ò0 ‡% °è£²Œ/óª˜wO¢¥6³„-88ðüš%q01{,׈xÊŒcGWLœ,D€"0:/*.sÁÏwjǦq˜yºsëJ…šªr"òÞlÎLåÖ?jç_³é{:5ebøMºaj›¥ºÊ%Wùµ^ìNó—?Òñ/1õÍš 1AÙæ^ùÏcÃ&ÀB •‡glV AJUä6u ë1õ»ò“æixwÕæáRaæöîù°³«‡wùqùÉ"@ˆÀ8¼4òÒW»jAÞËy­2Í—).OÇ’™òÞb6åS[° È{Ú´i^¾1ÒÍØJWùNKb}5{Tdw=f\>Q°j·Ÿ@€*Æ” Ï5Yå›TÖ|y„ œU^QeÆÕ˜š¼H¯ióç#Ÿ¸lÖ1UUíÊ`³fÁQj!§Úì%þ¾/7ñª<¸b Ë>Ä{²"@ˆÀepxiäb³Ò,<`ô´ªñÔ:a§ŽóYWq4ÇÊŸ‹Ý~°ãÔÁBŠŽ~¾;(cý²Ù3ýD櫈Xw´Æ¸ºßž‹MÛ~¢¯Ã(ºX(Ò%̉½RðŸÁ>ŒaeÆ[oÓ(ï!óšÍü=Á8yHþ'å¦]r8q}öPmý žßÏ@0 UN!|5EÄçã o?‡ÃApòÆì9~¶éê{ºz œùúX{$qGU(ŠäèÐLòòò|ðÁøøø«®ºÊËË WlŸs¥`<ã!™LvÅW|O0qôêQùˆ€ã°ýËì`eì½€Å>¡Q¡C—L¦ ‰P†Xû+üħ¬½¬ïʈ(‹=w„Š@œ‰1®ûDùÙÔ2 ¹ŸmQd$q-S¢;"@ˆ ÇPµ· êhjJqˆ D€Œˆ€ãJãSùi^‰‰}"@ˆÀdp\iôfc™ÞÓf&ƒ åAˆ nMÀaß5*VV ®¬pëgC•'D€)!฽Æ)ÁA™"@ˆ i¤ï D€ $8è†"@ˆI#}ˆ D€X i´ÀA7D€"@Hé;@ˆ DÀ‚I£º!D€"@ÒHß"@ˆ H-pÐ  D€’Fú"@ˆ° @Òhƒnˆ D€4Òw€"@ˆ€’F tCˆ D€¤‘¾D€"@,4Zà "@ˆ $ô D€"`A€¤ÑÝ"@ˆ i¤ï D€ 2‹;Ǻ1ètz¾D2¹Bn,§A¯Óx7¹Ba*º`|2D€"@FEÀ{ºæŸx æm:SÝš_NbnÛšMnö‚™‚Ó' D€Ë&0©Ò8M0#-£Œ 00Ydž f½h0¹Ù f NŸDÀÕ\^#rµÚS}ˆÀ$˜(id Øö:šjx{‰Ñ®4Ú¼½W8°ðbË]Ö‹aLˆ4²F‹\E‹h‡ Ú?Ú6”O¼Bü„? ü!Ñøá ¥dÑa‡e˜:pRâ×ßp|íqËdOtÇ­ÔÀ5 ±E°[T_´8) *6p(ã/bÓ•6o4{ôüÐÈaAýñû—]˜¹ãŠðpdÈ!:”¬¿ˆ+ŒCQ£Âq!À Ú,hhh ¸õôôÄ•õa‡#|™aXSÂUleb»—‚Q"DÀÍ Œ¿4Š@Ŷ q›±haެà „»" ’ÝÂ"&K"à2ðõÛ4vèŸ({LùÄb«a±^¢q&T"àÆSÑJ™’±æŠê1ÖÔ!oèb˜ʇ¿¸âç0 w„„»…¯’‰"I£#|W¨ ãN_x¤‰+¾ÿ¬;i„]ì/¢í0;|YÖšÄ[‰ 3¢Ë¸—–$îC`ܤÍRJ ·¬‰Û«©Ëˆ† QdW4oDÁa ‹pÄ߈"„Å‚/nFš8Ù‰€k`ßs|óQ\Ñdpev¦…L™]ô²m\HJd"6Ñ…,D€\ñ‘FÖ,™€ÁŽvËä Öž!rhÞÌ?„á;®ˆÅÅI¸0Q„õý²jE‰€SÀ÷åDA«´\Ù°*š ì¬×È®h ƒð, ‹ÎÚk‰HÑî(¨DÀ¡Œ4ŠU$³àŠvkeX“†"Âv´v($·HîˆÅôRH³ p¬™HŽ ¨2idWæ‹+³Xµ&ÜŠm X˜ÝeøPEˆÀTgidÕ@ûd²ÇvüÎe^pA ‡â 6ŽŠ[„×Å‹!‡ø ;Yt\a¦ åK&ŽS2&oh¬áà¿aÇޏ¢ù Q0;ô†9"$ Üa`A”‰+*¥LÜŠÀDI#k¥bSš°± ÃmWÖSû‹L ¡‚Ì‚?ð‚Áó`W·z0TYw €vj²Ö‘Ã_~XÐLàÅ.Ì‘—AÉš @x$ÂG&it‡/ ÕqRŒ§4¢e²¦ycvXÐŒ¥;”.hç ̓WÖ_D'QpË ‚Á‚+¥‰¸&f¬ÕàÊ Yë€(ÂÅJ q _ш2‰X0b ®Á‡jA¦ŠÀxJ£UXCeR‡‹6 ƒÁØÍ2):2 ³^#»"¸ÀX%N·DÀðj&|ÃqE¯hh#¸2xÁ°F„«h/»…ÅhPˆ€ãidÍ5„†UU´£%3 „òÁÍÃK¸H÷¾a¢ˆˆð…é¢ã|]¨$ãNM µ\!â•õ™/V$’¿@5E;,,Œ˜Î¸’$îF`œ¥Qlá°ˆ’;3hÃà ;<4o„Á *\àGXà‚Ž+ sÄ•Ùa!C\‰ûæã JÁÃä©®pa}G¸3Gæ.7wÙ­•£+±¢ºÉ$0nÒˆ6‰rCÃ`Afu` ½@æÈzŠ@ø¢Áã‡0ìÒþ"“L^…þ¢·¢,DÀe°VêƒVÃÚ k,¸2 d}GæÅ:‹ /Q)YDv‹`HvJT"0ùÆM­ŠŽÆ‰¶Ê:‚°@Û˜ ‚ÁÂ$ùÂ…Y˜Š¢(•C°JŸn‰€ @Ókv;\XKÁ•éHÑfƒ‰1)²"0Fã)h¢( SAÑ"6c±ïÈà Ãv&–,n™ n™Á­ÉJŸDÀu°&Ãê#¶æ( ¤DdÁpe‰`0`kqLT"0éÆSí^l·hÌ ¨ž—ˆL ™²0hù¢"Š»Y#pR¬u ðb3‘Z˜(ŠZ(Z†ÙaqÒºS±‰€#0O–¯R2aCjR ë2\™Šaà†LÙUŒ( .¢,DÀeX › tÆaUÔÑJÿà ©#³#¤Q´¸ "ª˜ØkD+E• j¬I3Ùc•D“†.ðbBRTAÑ2%\(S"0ÉX{A¦¬½°[\™…5vË®¶º8ɦ숀 ÿ^#ƒÅOj]¬,RÉdr(½º0zª`(½2_&¢»Ô;»EH©]š,Ù‰ ì5Z•†5]È,¢:Š [Xô•:’¸<»ò&í/‚€Ý0.O†*H&™ÀDõ¥Õ°•:© ìbà¡ìb²W% ý™h×n+ж.® ‡êE&™Àäõ­*Æ?´PúWÀ* Ý÷! mR;Xݺª)˜*“Ñk´­›´wÈ|m]lc‘ p+¶ŠhëâV@¨²D`Ò˜7㘴,)#"@ˆ ŽL`jzŽL„ÊFˆ nN€znþ ê"@ˆ€5’Fk"tOˆ nN€¤ÑÍ¿T}"@ˆ°&@ÒhM„î‰ DÀÍ 4ºù€ªOˆ ÖH­‰Ð= D€¸9’F7ÿPõ‰ DÀšI£5º'D€7'@Òèæ_ª> D€X i´&B÷D€"àæHÝü @Õ'D€k$ÖDèž"@ÜœI£›¨úD€"`M€¤ÑšÝ"@ˆ€› itó/UŸ"@¬ 4Z¡{"@ˆps$nþ ê"@ˆ€5’Fk"tOˆ nN€¤ÑÍ¿T}"@ˆ°&ðÿ~/ÿ( 2ÏIEND®B`‚doc/property.man.inc000066400000000000000000000106201242365656200147660ustar00rootroot00000000000000[comment {-*- tcl -*- manpage fragment for property method, shared by nx::Object and nx::Class}] [keywords "property"] [keywords "slot object"] [keywords "call protection"] [keywords "public"] [keywords "protected"] [keywords "private"] [keywords "switch"] [keywords "value checker"] [keywords "multiplicity"] [comment { At the time of writing, the only material difference between property and variable is a different -configurable default. }] Defines a [term property] for the scope of the [vset SCOPE]. The [arg spec] provides the [term property] specification as a [cmd list] holding at least one element or, maximum, two elements: [arg propertyName][opt "[const :][arg typeSpec]"] [opt [arg defaultValue]]. The [arg propertyName] is also used as to form the names of the getter/setter methods, if requested (see [option "-accessor"]). It is, optionally, equipped with a [arg typeSpec] following a colon delimiter which specifies a [term "value checker"] for the values which become assigned to the property. The second, optional element sets a [arg defaultValue] for this property. [para] If [option "-accessor"] is set, a property will provide for a pair of getter and setter methods: [list_begin definitions] [def "[arg obj] [arg propertyName] [method set] [arg value]"] Sets the [term property] [arg propertyName] to [arg value]. [def "[arg obj] [arg propertyName] [method get]"] Returns the current value of [term property] [arg propertyName]. [def "[arg obj] [arg propertyName] [method unset]"] Removes the value store of [arg propertyName] (e.g., an object variable), if existing. [list_end] The option value passed along [option "-accessor"] sets the level of [term "call protection"] for the generated getter and setter methods: [const public], [const protected], or [const private]. By default, no getter and setter methods are created. [para] Turning on the [term switch] [option -incremental] provides a refined setter interface to the value managed by the [term property]. First, setting [option -incremental] implies requesting [option -accessor] (set to [const public] by default, if not specified explicitly). Second, the managed value will be considered a valid Tcl list. A [term multiplicity] of [const 1..*] is set by default, if not specified explicitly as part of [arg spec]. Third, to manage this list value element-wise ([emph incrementally]), two additional setter methods become available: [list_begin definitions] [def "[arg obj] [arg propertyName] [method add] [arg element] [opt [arg index]]"] Adding [arg element] to the managed list value, at the list position given by [arg index] (by default: 0). [def "[arg obj] [arg propertyName] [method delete] [arg elementPattern]"] Removing one or multiple elements from the managed list value which match [arg elementPattern]. [arg elementPattern] can contain matching characters (see [cmd "string match"]). [list_end] [para] By setting [option -configurable] to [const true] (the default), the property can be accessed and modified through [method cget] and [method configure], respectively. If [const false], no configuration option will become available via [method cget] and [method configure]. [para] If neither [option -accessor] nor [option -configurable] are requested, the value managed by the [term property] will have to be accessed and modified directly. If the property manages an object variable, its value will be readable and writable using [cmd set] and [method eval]. [para] A [term property] becomes implemented by a [term "slot object"] under any of the following conditions: [list_begin itemized] [item] [option -configurable] equals [const true] (by default). [item] [option -accessor] is one of [const public], [const protected], or [const private]. [item] [option -incremental] is turned on. [item] [arg initBlock] is a non-empty string. [list_end] Assuming default settings, every [term property] is realized by a [term "slot object"]. [para] Provided a [term "slot object"] managing the [term property] is to be created, a custom class [arg className] from which this [term "slot object"] is to be instantiated can be set using [option -class]. The default value is [cmd ::nx::VariableSlot]. [para] The last argument [arg initBlock] accepts an optional Tcl script which is passed into the initialization procedure (see [method configure]) of the [term property]'s [term "slot object"]. See also [sectref description "[arg initBlock] for [method create] and [method new]"]. doc/require.man.inc000066400000000000000000000010421242365656200145540ustar00rootroot00000000000000[comment {-*- tcl -*- manpage fragment for forward method, shared by nx::Object and nx::Class}] [keywords "call protection"] [call [arg [vset CMD]] [method require] [opt "[method public] | [method protected] | [method private]"] [method "[vset MODIFIER] method"] [arg methodName]] Attempts to register a method definition made available using [cmd ::nsf::method::provide] under the name [arg methodName] with [arg [vset CMD]] . The registered method is subjected to default [term "call protection"] ([const protected]), if not set explicitly. doc/tutorial2.html000066400000000000000000006055501242365656200144640ustar00rootroot00000000000000 XOTcl - Tutorial

XOTcl - Tutorial - Index

Version: 2.0.0

Introduction

 
Language Overview

XOTcl [Neumann and Zdun 2000a] is a successor of the object-oriented scripting language OTcl [Wetherall and Lindblad 1995] which itself was an early highly flexible object oriented exitension of Tcl [Ousterhout 1990] (Tool Command Language). XOTcl was so far released in more than 30 versions, is described in its detail in more than 20 papers and serves as a basis for TclOO [Donal ???]. XOTcl 2.0 [Neumann and Sobernig 2009] extends the basic ideas of XOTcl 1.0 by providing support for language-oriented programming and makes it easy to host several object oriented languages by a common environment...

XOTcl runs in the tclsh and provides a few extension commands. These are offered via the Tcl namespaces ::xotcl and ::xotcl2, and can be imported into the current namespace to reduce typing and improve readability. All Tcl commands remain available (and are also applicable on the extension constructs).

A central property of Tcl is, that it uses strings solely for the representation of data. Internally it uses an dynamic type system with automatic conversion (which enables efficient type handling). For that reason all components (e.g. written in C) once integrated in Tcl automatically fit together and the components can be reused in unpredicted situations without change. The evolving component frameworks provide a high degree of code reuse, rapid application development, and ease of use. The application developer may concentrate on the application task solely, rather than investing efforts in fitting components together. Therefore, in certain applications scripting languages like Tcl are very useful for a fast and high-quality development of software (see [Ousterhout 1998] for more details).

Tcl is equipped with appropriate functionalities for the easy gluing of components, like dynamic typing, dynamic extensibility, and read/write introspection. OTcl is an object-oriented extension to Tcl, which encourages a Tcl-like programming style and is composed of language constructs with properties similar to Tcl. It offers an object-orientation with encapsulation of data and operation without protection mechanisms and single and multiple inheritance. Furthermore it enables to change the relationships dynamically, offers read/write introspection, has a three level class system based on meta-classes and offers method chaining. These abilities are integrated in XOTcl with only slight changes to OTcl visible to the programmer.

Extended Object Tcl aims at complexity and adaptability issues that may occur in context of large (object-oriented) software structures and in the context of component glueing. In particular we added the following support:

  • Filters as a means of abstractions over method invocations to implement large program structures, like design patterns.

  • Mixin Classes, as a means to give an object or a classes' instances access to several different supplemental classes, which may be changed dynamically.

  • Dynamic Object Aggregations, to provide dynamic aggregations through nested namespaces.

  • Nested Classes, to reduce the interference of independently developed program structures.

  • Assertions, to reduce the interface and the reliability problems caused by dynamic typing and, therefore, to ease the combination of components.

  • Forwarders, to delegate calls efficiently to other objects or classes.

  • Slots, to manage values of instance variables with a common interface.

  • Meta-data and Automatic Documentation, to enhance self-documentation of objects and classes.


  

Figure 1: Language Extensions of XOTcl
new graphic, extension of the features above, history with OTcl, XOTcl1, tcloo and XOTcl2

 
Introductory Overview Example: Stack

To give you an impression of the language before we go into the details of the extended language constructs, we present in this section a simple, classical example, familiar to many from introductory programming courses: the Stack example. In the later section, we will present the soccer club example, which focuses more on the dynamic features of the Extended Object Tcl.

In a first step, we define a class Stack. A new class is defined in XOTcl via the command Class create yourclass. The stack will have a constructor (in XOTcl, the method init) and the methods push and pop. In the following example, all predefined commands (some from Tcl, some from XOTcl) are emphasized.

#
# Create a stack class 
#
Class create Stack {
        
  :method init {} { # Constructor
    set :things ""
  } 

  :method push {thing} {
    set :things [linsert ${:things} 0 $thing] 
    return $thing
  }
  
  :method pop {} {
    set top [lindex ${:things} 0]
    set :things [lrange ${:things} 1 end]
    return $top
  }
}

The three methods are defined via :method (which means: define a method for the current class). Variables are set with the Tcl command set. Variable names starting with a dot "." are treated as instance variables (variables of an instance of the Stack, i.e. an Stack object). The other variables are scoped to the methods.

The definition of the class Stack is typically saved in a file (say stack.xotcl) and can be used e.g. in an interactive Tcl shell (tclsh) as follows. The percent sign indicates the prompt of the Tcl shell, the reminder of the line is typed in, the result of the command is shown in the line below. Comments are lines starting with a hash symbol #.

% package require XOTcl
% ::xotcl::use xotcl2
% source stack.xotcl
  
# Create Object s1 of class Stack
% Stack create s1
::s1
% s1 push a
a
% s1 push b
b
% s1 push c
c
% s1 pop
c
% s1 pop
b
# Delete object s1
s1 destroy

In the session above, we load XOTcl into the current shell, import the names from the xotcl namespace and we load the file stack.xotcl. At this time, the class Stack is available in the scripting session. In the next step, we create an stack object named s1 and push into the stack the values a, b and c via separate push calls. Then we pop values from the stack and we destroy finally the stack s1.

 
Object specific methods

The definition of Stack provided above is pretty similar to stack definitions in many other object oriented languages. The next example shows how to define purely object specific behavior. We can define an object stack without the need of a class Stack. Notice that the methods of the object stack are defined exactly the same way as in the previous example with the class Stack. Instead of defining a constructor, we can set the instance variable things directly in the definition block of the object.

#
# Create an object named stack
#
Object create stack {

  set :things ""

  :method push {thing} {
    set :things [linsert ${:things} 0 $thing] 
    return $thing
  }
  
  :method pop {} {
    set top [lindex ${:things} 0]
    set :things [lrange ${:things} 1 end]
    return $top
  }
}

The object stack can be used in exactly the same way as s1 (the instance of class Stack) before.

 
Refining the behavior of objects and classes

So far, the definition of stacks were pretty minimal. Suppose, we want to define "safe stacks", that check e.g. for stack underruns (more pop than push operations are issued). Checking safety can be done mostly independent from the implementation details of the stack (usage of internal data structures). With XOTcl, one can define stack-safety as a separate class using methods with the same names as the implementations before, and "mix" this behavior later into classes or objects. The implementation of Safety uses a counter to check for stack underruns.

#
# Create a safety class 
#
Class create Safety {
        
  :method init {} { # Constructor
    set :count 0
    next
  } 

  :method push {thing} {
    incr :count
    next
  }
  
  :method pop {} {
    if {${:count} == 0} then { error "Stack empty!" }
    incr :count -1
    next
  }
}

When we load the classes Stack and Safety into the same script, we can define e.g. a certain stack s2 as a safe stack, while all other stacks might be still "unsafe". This can be achieved via the option -mixin during object creation.

% Stack create s2 -mixin Safety
::s2
% s2 push a
a
% s2 pop
a
% s2 pop
Stack empty!
Note that the definition of Saftey can be used not only for instances of the class Stack, but for arbitrary objects supporting the same interface. We can as well use Saftey to create a new class SafeStack. In this case, all instances of SafeStack have the safety property defined above.
#
# Create a safe stack class by using Stack and mixin 
# Safety 
#
Class create SafeStack -superclass Stack -mixin Safety

SafeStack create s3

 
Stack of integers

The definition of Stack is generic and allows all kind of elements to be stacked. Suppose, we want to use the generic stack definition, but a certain stack (say, s4) should allow only stacking of integers. This behavior can be achieved by defining an object specific method for the stack s4 that checks the values to be pushed. In case the pushed value is ok, the push definition of Stack is called via next.

# 
# Create a stack with a object-specific method 
# to check the type of entries 
#
# s4 is a stack of integer 
 
Stack create s4 {

  :method  push {value} {
    if  {![string is integer $value]} {
      error "value $value is not an integer"
    }
    next
  }

}

 
Class specifc methods

In extended object Tcl, classes are objects as well (objects with certain properties; we will come to this later in more detail). However, we can define as well methods of classes, which are not inherited to the instances, but which are to be applied on the class object itself. This can be achieved by the modifier object which is placed in front of method. Such methods defined on the class object are actually exactly same as the object specific methods in the example with the object named stack above.

In the following example, we will define the method available_stacks on the class object, that returns the number of the currently existing stack instances.

Class create Stack {

   # ...
    :class-object method available_stacks {} {
      return [llength [:info instances]]
   }
}

Stack create s1
Stack create s2

puts [Stack available_stacks]

The final command puts prints 2 to the console.

 
Introductory Overview Example: Soccer Club

In our second example, we will focus on an application example where one can benefit substantially from the dynamic language constructs of XOTcl, the soccer club example (the full code can be found in the xotcl/src/scripts/soccerClub.xotcl file. All the persons and characters in this example are fictitious, and any resemblance to actual persons, living or deceased, is coincidental.

Before we start, we introduce an instrument for making the documentation of programs more easy. In order to document source code files, we can use the @ object, which is used generally to provide any kind of information, meta-data, and documentation on a running program. Here, we just give a file description. Then the makeDoc.xotcl tool can automatically document the program file later for us.

  @ @File {
    description {
      This is a simple introductory example for the language XOTcl. 
      It demonstrates the basic language constructs on the example of
      a soccer club.
    }
  }

All things and entities in XOTcl are objects. A special kind of objects are classes. Classes define common properties for other objects. For a soccer club, we firstly require a common class for all kinds of members.

Common to all members is that they have a name. Common properties defined across all instances of a class are called 'parameter' in XOTcl. In this example the instance variable name will be initialized by default with an empty string.

  Class create ClubMember -parameter {{name ""}}

A special club member is a Player. Derived classes can be build with inheritance (specified through superclass). Players may have a playerRole (defaults to NONE).

  Class create Player -superclass ClubMember -parameter {{playerRole NONE}}

Other club member types are trainers, player-trainers, and presidents:

  Class create Trainer -superclass ClubMember
  Class create President -superclass ClubMember

The PlayerTrainer uses multiple inheritances by being both a player and a trainer:

  Class create PlayerTrainer -superclass {Player Trainer}

Now we define the SoccerTeam class:

  Class create SoccerTeam -parameter {name location type}

We may add a player by using method. Methods can be defined in XOTcl2 either by :method in the class creation block, or via "ClassName method ...". The added players (as well as other club members) are aggregated in the object of the soccer team (denoted by :: namespace syntax).

  SoccerTeam method newPlayer args {
    # we create a new player who is part of the soccer team
    # "eval" is needed to pass the provided arguments seperately to the call of new
    eval Player new -childof [self] $args
  }

A player can be transfered to another team. The player object does not change internally (e.g. the playerRole stays the same). Therefore we move it to the destination team.

  SoccerTeam method transferPlayer {playername destinationTeam} {
    # We use the aggregation introspection option children in order
    # to get all club members
    foreach player [:info children] {
      # But we only remove matching playernames of type "Player". We do
      # not want to remove another club member type who has the same
      # name.
      if{[$player info has type Player] && [$player name] eq $playername} {
        # We simply 'move' the player object to the destination team.
        # Again we use a unique autoname in the new scope
        $player move ${destinationTeam}::[$destinationTeam autoname player%02d]
      }
    }
  }

Finally we define two convenience to print the members/players to the console with puts.

  SoccerTeam method printMembers {} {
    puts "Members of ${:name}:"
    foreach m [:info children] {puts "  [$m name]"}
  }
  SoccerTeam method printPlayers {} {
    puts "Players of ${:name}:"
    foreach m [:info children] {
      if {[$m info has type Player]} {puts "  [$m name]"}
    }
  }

Now let us build to example soccer team objects.

  SoccerTeam create chelsea -name "Chelsea FC" -location "Chelsea"
  SoccerTeam create bayernMunich -name "F.C. Bayern München" -location "Munich"

With addPlayer we can create new aggregated player objects

Let us start some years in the past, when "Franz Beckenbauer" was still a player.

  set fb [bayernMunich newPlayer -name "Franz Beckenbauer" -playerRole PLAYER]

playerRole may not take any value. It may either be NONE, PLAYER, or GOALY ... such rules may be given as assertions (here: an instinvar gives an invariant covering all instances of a class). In XOTcl the rules are syntactically identical to if statements:

  Player instinvar {
    {${:playerRole} in [list "NONE" "PLAYER" "GOALY"]}
  }

If we break the invariant and turn assertions checking on, we should get an error message:

  $fb check all
  if {[catch {$fb playerRole SINGER} errMsg]} {
    puts "CAUGHT EXCEPTION: playerRole has either to be NONE, PLAYER, or TRAINER"
    # turn assertion checking off again and reset to PLAYER
    $fb check {}
    $fb playerRole PLAYER
  }

But soccer players may play quite different, orthogonal roles. E.g. Franz Beckenbauer was also a singer (a remarkably bad one). However, we can not simply add such orthogonal, extrinsic extensions with multiple inheritance or delegation. Otherwise we would have either to build a lot of unnecessary helper classes, like PlayerSinger, PlayerTrainerSinger, etc., or we would have to build such helper objects. This either leads to an unwanted combinatorial explosion of class or object number

Here we can use a per-object mixin, which is a language construct that expresses that a class is used as a role or as an extrinsic extension to an object.

First we just define the Singer class.

  Class create Singer {

    :method sing text {
      puts "${:name} sings: $text, lala."
    }
  }

Now we register this class as a per-object mixin on the player object:

  $fb mixin Singer

And now Franz Beckenbauer is able to sing:

  $fb sing "lali"

But Franz Beckenbauer has already retired. When a player retires, we have an intrinsic change of the classification. He *is* not a player anymore. But still he has the same name, is club member, and is a singer (brrrrrr).

Before we perform the class change, we extend the Player class to support it. I.e. the playerRole is not valid after class change anymore (we unset the instance variable).

  Player method class args {
    unset :playerRole
    next
  }

Now we can re-class the player object to its new class (now Franz Beckenbauer is President of Bayern Munich.

  $fb class President
  # Check that the playerRole isn't there anymore.
  if {[catch {$fb playerRole} errMsg]} {
    puts "CAUGHT EXCEPTION: The player role doesn't exist anymore \
         (as it should be after the class change)"
  }

But still Franz Beckenbauer can entertain us with what he believes is singing:

  $fb sing "lali"

Now we define some new players for Bayern Munich:

  bayernMunich newPlayer -name "Oliver Kahn" -playerRole GOALY
  bayernMunich newPlayer -name "Giovanne Elber" -playerRole PLAYER

If we enlist the players of Munich Franz Beckenbauer is not enlisted anymore:

  bayernMunich printPlayers

But as a president he still appears in the list of members:

  bayernMunich printMembers

Now consider an orthonogal extension of a transfer list. Every transfer in the system should be notified. But since the transfer list is orthogonal to SoccerTeams we do not want to interfere with the existing implementation at all. Moreover, the targeted kind of extension has also to work on all subclasses of SoccerTeam. Firstly, we just create the extension as an ordinary class:

  Class TransferObserver {
    :method transferPlayer {pname destinationTeam} {
      puts "Player '$pname' is transfered to Team '[$destinationTeam name]'"
      next
    }
  }

Now we can apply the class as a per-class mixin, which functions exactly like a per-object mixin, but on all instances of a class and its subclasses. The next primitive ensures that the original method on SoccerTeam is called after notifying the transfer (with puts to stdout):

  SoccerTeam mixin TransferObserver

If we perform a transfer of one of the players, he is moved to the new club and the transfer is reported to the stdout:

  bayernMunich transferPlayer "Giovanne Elber" chelsea

Finally we verify the transfer by printing the players:

  chelsea printPlayers
  bayernMunich printPlayers



Object and Class System

In XOTcl every object is associated with its managing class by a relationship called class. Classes are special objects with the purpose of managing other objects. "Managing" means that a class controls the creation and destruction of its instances and that it contains a repository of methods accessible for the instances.

Since a class is a special (managing) kind of object it is managed itself by a special class called a "meta-class" (which manages itself). Meta-Classes are used to define classes and to provides methods for these. Most classes are defined by the predefined meta-class Class. One interesting aspect of meta-classes is that by providing a constructor pre-configured classes can be derived. Meta-classes can be used to instantiate large program structures, like some design patterns (see [Neumann and Zdun 1999a] for more details), where the meta-class may holds the generic parts of the structures. Since a meta-class is an entity of the program, it is possible to collect these entities in pattern libraries for later reuse easily (more details about meta-classes are given in a later section).

The methods common to all objects in the XOTcl 2 object system are defined in the root class Object (fully qualified name ::xotcl2::Object). All methods can be predefined (defined by XOTcl) or user-defined. All objects of XOTcl 2 are either direct instances of Object or instances of subclasses of Object.

The most basic meta-class is Class (fully qualified name ::xotcl2::Class). All classes of XOTcl 2 are either direct instances of Class or instances of subclasses of Class. Since - as noted before - a class is a special kind of object, Class is a subclass of Object. Therefore, all methods available in all classes are the union of the methods of Object and Class (see Figure 2a).

Figure 2a: Basic Classes of the XOTcl2 object system

When we create an application class such as the class Stack in the examples above, we create it as instance of the basic meta-class ::xotcl2::Class. The application class will have ::xotcl2::Object as it superclass, unless we spefify this differently. When we create an instance of the class Stack (such as e.g. the stack s1) we create it by using the method create provided by ::xotcl2::Class (an instance can use the methods provided by its class).

Figure 2b: Application class Stack and instance of Stack together with the Basic Classes of the XOTcl2 object system

XOTcl supports single and multiple inheritance. Classes are ordered by the relationship superclass in a directed acyclic graph. The root of the class hierarchy is the class Object. Note that it is possible to create as well objects from this most general class; we have done this already above by creating an object named stack.

A classical problem of multiple inheritance is the problem of name resolution, when for example two super-classes contain an instance method with the same name. XOTcl provides an intuitive and unambiguous approach for name resolution by defining the precedence order along a linear "next-path" incorporating the class and mixin hierarchies. A method can invoke explicitly the shadowed methods by the predefined command next. When next is executed a shadowed method is invoked. The execution of the shadowed methods is called "method chaining". Method chaining without explicit naming of the targeted method is very important for languages supporting a dynamic class system, because one cannot always predict which classes are currently participating in the inheritance hierarchy at design time (often necessary in inheritance models, like C++).

An important feature of all XOTcl objects is the read/write introspection. The reading introspection abilities of XOTcl are packed compactly into the info instance method which is available for objects and classes. All obtained information can be changed at run-time dynamically with immediate effect. Unlike languages with a static class concept, XOTcl supports dynamic class/superclass relationships. At any time the class graph may be changed entirely using the superclass method, or an object may change its class through the class method. This feature can be used for an implementation of a life-cycle or other intrinsic changes of object properties (in contrast to extrinsic properties e.g. modeled through roles and implemented through per-object and per-class mixins [Neumann and Zdun 1999c] ) . These changes can be achieved without loosing the object's identity, its inner state, and its per-object behavior (methods and mixins).

xotcl2 changes until here, reminder is missing;

  

Figure 2b: Object and Class System

Basic Functionalities

 
Objects

at least the first paragraph has to be rewritten; "2 commands" don't really hold

Initially XOTcl offers two new commands: Object and Class. They represent hooks to the features of the language. This section discusses both of them in detail and shows how they function in the context of XOTcl. Note, that even if most of this is compatible to OTcl, a few changes occur. For this reason, this section is no introduction to plain OTcl. The Object command provides access to the Object class, which holds the common features of all objects, and allows us to define new objects. Objects are always instances of classes, therefore, objects defined with the Object command are (initially) instances of the Object class. But since they have no user-defined type, they may be referred to as singular objects. As all other objects they may be specialized by object-operations and -data.

The object command has the following syntax:

  Object objName ?args?

A command of this form is a short-cut for a message to the create instance method (forwarded automatically by the unknown mechanism, which is invoked every time the message dispatch system discovers an unknown message):

  Object create objName ?args?

It creates a new object of type Object with the name objName (in fact it invokes a create call on the Object class). objName becomes a new command, which allows us to access the created object. Similar to the Object command it may be used like a normal Tcl-command (using sub-commands to access the object's methods). Therefore, this form of access is called object-command approach. A simple example is an object which holds the information of a kitchen. It is created by:

  Object kitchen

An object creation calls the constructor init of the object's class. The destruction of an object is handled by the destroy instance method. The general syntax of destroy is:

  objName destroy

E.g. the kitchen object is destroyed by:

  kitchen destroy

To invoke a user-defined destruction process, it is possible to overload this instance method in every class derived from object.

Note that the destruction of an object is performed by the method destroy of Object (since every object is an instance of Object, every object can call destroy). When an application class overloads destroy, this method should contain a next in order to reach the base class and to actually destroy the object.

Data on Objects

The Object class provides a range of operations to manage objects, including those to manipulate data-structures on the objects. They are similar to the same-named Tcl-commands:

  objName set varname ?value?
  objName unset v1 ?v2 ... vn?

The set instance method with given value option allows us to manipulate an object-variable's value or to create a new one, if the variable varname does not exist on the object so far. Without value option the set operation queries the variable and returns it's value, if the variable exists, otherwise it produces an error message. The unset operation deletes one or optionally a set of variables from an object. For example the kitchen object can store information on the color of the wall-paper by:

  kitchen set wallPaperColor white

Similar to Tcl-variables the object variables are dynamical; they may be set at run-time when they are needed and unset when they become obsolete. E.g. the persons in the kitchen may be stored in an array. If there are no persons in the kitchen the array is deleted:

  # Peter enters the kitchen to cook
  kitchen set persons(cook) Peter
  ...
  # Marion enters the kitchen to take one of the seats
  kitchen set persons(seat1) Marion 
  ...
  # Both Peter and Marion leave the kitchen
  # the array is deleted by unset
  kitchen unset persons

Since XOTcl variables are internally realized through Tcl-variables they may be treated like all Tcl-variables. For that reason they have all Tcl-variable abilities, including the possibility to handle them as lists or arrays (as seen in the last example). The array command of Tcl is mapped to an XOTcl-command directly. An object-oriented call to an object of the form

  objName array option arrayName args

forwards its arguments to an array Tcl-command for the object´s instance variable arrayName. It could be used like the same-named Tcl-command, e.g. the command

  kitchen array names persons

returns all indexes currently stored in the persons array.

Similarly Tcl´s incr command is mapped to the object system. A call with the syntax:

  objName incr varName ?value?

increments varName with the given value (or without given value with 1).

Methods for Objects

Methods in XOTcl resemble Tcl-procedures. On objects one can define object-specific methods, called procs. Instance methods which are defined on classes are called instprocs. A new proc is defined using the proc instance method of the class Object:

  objName proc name args body

The arguments of the proc instance method specify the name, the arguments as a Tcl-list, and the body of the new proc. All of them must be given, only one of args and body may be empty. An example proc would be a method to let persons enter the kitchen:

  kitchen proc enter {name} {
    [self] set persons($name) [clock seconds]
  }

Here the predefined self command is used in one of three possible ways, which allow us to access useful information when working with XOTcl-methods, these are in particular:

  • self: returns the name of the object, which is currently in execution. This command is similar to this in C++. It is automatically generated on each object. If it is called from outside of an XOTcl method, it produces the error message "Can't find self".

  • self class: the self command with the argument class returns the name of the class, which holds the currently executing instproc. Note, that this may be different to the class of the current object. If it is called from a proc it returns an empty string.

  • self proc: the self command with the argument proc returns the name of the currently executing method (proc or instproc).

The method enter can be written in XOTcl as well with less syntactic overhead by using the predefined primitive my instead of [self]:

  kitchen proc enter {name} {
    my set persons($name) [clock seconds]
  }

Note, that there is a difference to the realization of these object informations to OTcl. XOTcl uses commands in order to make XOTcl-methods compatible to Tcl-procedures and accessible via namespace-paths. OTcl uses the three variables self, class and proc, which are filled automatically with proper values by the interpreter each time a method is called. To gain backwards compatibility XOTcl can be compiled with -DAUTOVARS to provide these variables additionally. By default this option is turned off.

Each XOTcl-method has its own scope for definition of local variables for the executing method. In most cases when a method uses object-variables, it is likely that the programmer wants to make one or more of these variables part of the method's scope. Then the Tcl-command for variable handling, like set, lindex, array, ... work also on these variables. The instvar instance method links a variable to the scope of an executing method. It has the syntax:

  objName instvar v1 ?v2 ... vn?

It makes the variables v1 ... vn, which must be variables of the object, part of the current method's scope. A special syntax is:

  objName instvar {varName aliasName} ...

for one of the variables. This gives the variable with the name varName the alias aliasName. This way the variables can be linked to the methods scope, even if a variable with that name already exists in the scope. Now the enter method can be adapted slightly and a leave method can be added, which uses Tcl's info command to check whether the named person is in the object's persons array. To demonstrate the alias-syntax this is done with the persons array and the alias p.

  kitchen proc enter {name} {
    my instvar persons
    set persons($name) [clock seconds]
  }

  kitchen proc leave {name} {
    my instvar {persons p}
    if {[info exists p($name)]} {
      puts "$name leaves after [expr {[clock seconds]-$p($name)}] seconds" 
      unset p($name) 
    } else {
      puts "$name is not in the room"
    }
  }
A method defined via proc can be deleted by proc using an empty argument list and an empty body. The following example deletes the method enter:
  Room proc enter {} {}

Information about Objects

XOTcl offers reading and writing introspection. The reading introspection abilities are packed compactly into the info instance method which is available for objects and classes (there are special info options for object aggregations, nested classes, mixins, filters, meta-data and assertions, which are explained separately in the following sections).

Options for the info method on objects

objName info args methodName

Returns the arguments of the specified proc (object specific method).

objName info body methodName

Returns the body of the specified proc.

objName info class ?className?

Returns the name of the class of the current object, if className was not specified. Otherwise it returns 1 if className matches the object's class and 0 if not.

objName info commands ?pattern?

Returns all commands defined on the object if pattern was not specified. Otherwise it returns all commands that match the pattern.

objName info default methodName arg var

Returns 1 if the argument arg of the specified proc has a default value, otherwise 0. If the default value exists it is stored in var.

objName info precedence ?pattern?

Returns all classes in the precedence order from which the specified object inherits methods. The returned list of classes contains the mixin and instmixin classes as well as the classes of the superclass chain in linearized order (i.e., duplicate classes are removed). If the pattern is specified, only matching classes are returned.

objName info vars ?pattern?

Returns all variables defined on the object if pattern was not specified, otherwise it returns all variables that match the pattern.


For example on the kitchen object

  kitchen info procs

returns enter and leave as a Tcl-list since these are the procs defined on the object.

Classes

Creating Classes and deriving Instances

There are different ways to create a class in XOTcl. They have in common that they derive the new class from a meta-class. Initially the Class command provides access to the meta-class Class, which holds the features common to all classes. It also allows one to derive new meta-classes. The common way to create a new class is:

  Class className ?args?

Similar to the object short form, this is a short form of a call to the create instance method of the meta-class Class, which is also executed by the standard unknown mechanism. This mechanism is always triggered when XOTcl does not know a method called on an object. Supposed that there is no method with the name className, defined on the class-object of Class, XOTcl looks up the method unknown (which is found on the Class Object) and executes it. The standard unknown-mechanism of XOTcl calls create with all arguments stepping one step to the right; in the general case:

  Class create className ?args?

This may also be called directly. Besides the indirection when using unknown, in most cases there is no difference in the action performed: Firstly the memory is allocated, using the alloc instance method; as the next step the constructor init is called on the creating object, which is in this case the class-object of the meta-class Class. In seldom cases the programmer may want to suppress the init call. To do so the alloc instance method may also be called directly:

  Class alloc className ?args?

As seen in the preceding section objects are created in the same way. The difference was, that the command Object, which accesses a class, instead of the command Class, which accesses a meta-class, was used. The user-defined classes may also be used in the same way to create new objects:

  className objName ?args?

Resembling the creation of classes this creates an object objName of type className using the unknown mechanism. That means the create instance method of the class is called. If there is no other instance method defined on the class-path so far (which would mean, an user defined creation process is invoked), the create instance method of the class Object is invoked. This method is similar to the create method of the meta-class Class. It firstly calls the alloc instance method on its (of the Class class) which allocates memory for the object, and makes it an instance of it's class. Afterwards a call to the constructor init is invoked.

Now we can specify the object for the kitchen by the class to which it belongs. In this case a kitchen is an instance of a room.

  Class Room
  Room kitchen

A set call on a class creates an instance variable on the class-object. This variable is unique for all instances, therefore, it may be referred to as a class variable.

Methods Defined in Classes

Methods which are defined in classes and which are provided to the instances of these classes are called "instprocs". The syntax for defining an instproc is:

  className instproc procname args body

It is similar to the definition of procs on objects, but uses the keyword instproc to distinguish between the methods defined on the class-object and those defined on the class. Since all rooms (in the modeled world) have ceilings, we may want to define a simple convenience instproc, which is able to set the color:

  Room instproc setCeilingColor color {
    my set ceilingColor $color
  }

A special instproc, the constructor init, was mentioned already. Now we are able to define such an instproc. Defined on a class it is responsible for all initialization tasks, which needed to be performed, when constructing a new instance object of the class. The constructor of the Room can initialize a variable for the color, in which the ceiling is painted, to white as default, since this is the color of ceilings without painting.

  Room instproc init args {
    my setCeilingColor white
    next
  }

After this definition, all instances derived from the Room class have an instance variable ceilingColor with the value white. The args argument used here is a special argument in Tcl which allows us to use a list of arguments which may change its length from call to call.

An instproc can be deleted by the method instproc as well. If instproc is called with an empty argument list and an empty body, the specified method is deleted, as the following example shows:

  Room instproc setCeilingColor {} {}

Information about Classes

Resembling to objects, information on classes may be gained through the info instance method of the meta-class Class. Note that this instance method does not only support the class info options, but also the class-object info options, since the accessing command refers to the class-object, which itself is an object and, therefore, offers its informations. The following table summarizes the additional info options available on classes.

Options for the info method on classes

className info heritage ?pattern?

Returns a list of all classes in the precedence order of the class hierarchy matching pattern or a list of all classes, if pattern was not specified.

className info instances ?pattern?

Returns a list of the instances of the class matching pattern or of all instances, if pattern was not specified.

className info instargs methodName

Returns the arguments of the specified instproc (method provided to objects).

className info instbody methodName

Returns the body of the specified instproc.

className info instcommands ?pattern?

Returns all commands defined on the class, if pattern was not specified, otherwise it returns all commands provided to objects that match the pattern.

className info instdefault methodName arg var

Returns 1 if the argument arg of the specified instproc has a default value, otherwise 0. If the default value exists it is stored in var.

className info subclass ?className2?

Returns a list of all subclasses of the class, if className2 was not specified, otherwise it returns 1 if className2 is a subclass and 0 if not.

className info superclass ?className2?

Returns a list of all super-classes of the class, if className2 was not specified, otherwise it returns 1 if className2 is a superclass and 0 if not.

The full list of info options is provided in the language reference.

Inheritance

Besides encapsulation of operations and state in objects, a second central ability of object-orientation is inheritance. XOTcl supports single and multiple inheritance with a directed acyclic class graph. Automatically each new class created by the instance methods create and alloc of Class inherits from Object. Therefore, it is ensured that all instances of the new class have access to the common features of objects stored in the class Object.

To specify further inheritance relationships the instance methods superclass of Class is used:

  className -superclass classList

E.g. in the example a kitchen may be seen as a special room:

  Class Room
  Class Kitchen -superclass Room

Now all instances of Kitchen are able to access the methods provided by the Room and the Kitchen classes. Note the transition the kitchen was going through: firstly it was a singular object, then it was an object with a user-defined class, and now it is a class. This is possible because we can provide a per-object behavior, and because classes are a special kind of objects. Both properties of XOTcl's object system lead to a seamless connection of the run-time behavior of objects and the descriptive properties of the classes. It is possible to avoid the strict distinction between objects and classes, known from static typed languages, like C++, Java, etc.

Moreover, since the syntaxes of constructs expressing the same concern are nearly identical, we can re-factor a solution with very few changes to the alternative. We will see similar "ease of refactoring" throughout the XOTcl language. E.g., we can also easily re-factor the class hierarchies or exchange class hierarchies against mixin solutions with only slight changes in the code.

Besides single inheritance, as seen, XOTcl provides also multiple inheritance. This is syntactically solved by giving the superclass instance method a list of classes instead of a single class as argument.

  Class Room
  Class 4WallsRoom -superclass Room
  Class CookingPlace
  Class Kitchen -superclass {4WallsRoom CookingPlace}

Now the kitchen class is specialized a bit more. It is a special room which has four walls and it is a cooking place. Multiple inheritance, as seen here, is as simple to apply as single inheritance.

Most often when the disadvantages of multiple inheritance are discussed, the name resolution along the class graph is considered as the biggest problem. The question is, which method is to be chosen and which path through class graph is to be taken, if more then one method of the specified name exist on the class graph.

In the example such questions would arise for an object of the Kitchen class, if two same-named methods are defined on CookingPlace and 4WallsRoom or if a method of the class Object is called, which is reachable through two paths (along CookingPlace or Room).

Often - e.g. in the inheritance model of C++ - the path through the graph is not clearly determined and/or the rules are too complicated to be understood on the first glance. The programmer often can only determine by trial which method is found firstly. Than an explicit naming of the class is necessary, which means storage of non-local information in sub-classes. Often different compilers of one language behave differently. All these issues make code reuse difficult. Moreover understandability and portability are reduced.


Figure 3: The example classes and the following next-path


XOTcl goes an intuitive and unambiguous way to solve this problem. It resolutes the precedence order along a ``next-path''. Firstly the class of the object is searched, which is Kitchen in example. Then the super-classes are searched in definition order, which means at first 4WallsRoom, then CookingPlace. Each branch is searched completely, before changing to the next branch. That means, Room is searched, before the CookingPlace branch is visited. At last the top of the hierarchy, the class Object, is searched.

The usage of next in XOTcl is different to OTcl: In OTcl, next is defined as a method, in XOTcl it is a primitive command. Furthermore, in OTcl, it is always necessary to provide the full argument list for every invocation explicitly. In XOTcl, a call of next without arguments can be used to call the shadowed methods with the same arguments (which is the most common case). When arguments should be changed for the shadowed methods, they must be provided explicitly in XOTcl as well. In the rare case that the shadowed method should receive no argument, the flag --noArgs must be used.

Destruction of Classes

Classes are destroyed by the destruction of the class-object using the destroy method of the Object class. The destruction of super-classes does not destroy the sub-classes. The super-class is simply removed from the sub-classes' super-class lists. All classes have the super-class Object, if no super-class is specified. Therefore, if all super-classes are destroyed or removed, the new super-class is Object, not: no super-class. The destruction of the class of an object does neither delete the object nor leave it without class. In XOTcl a deleted class leaves it's instances with the class Object.

So all empty class- and superclass-relationships are automatically reseted to Object. Note, that this are differences to OTcl, where the destruction of an class destroys all instances and an empty super-class list remains empty.

Method Chaining

A special feature of XOTcl is the method chaining without explicit naming of the ``mix-in''-method. It allows one to mix the same-named superclass methods into the current method (modeled after CLOS). The previously described next-path is the basis for this functionality. At the point marked by a call to the next primitive of XOTcl the next shadowed method on the next path is searched and, when it is found, it is mixed into the execution of the current method. When no method is found, the call of next returns an empty string, otherwise it returns the result of the called method. The syntax is:

  next ?arguments|--noArgs?

As stated earlier the usage of next in XOTcl differs from OTcl, since the next call without arguments in OTcl means per default that no arguments are passed. But most often all arguments are passed through to the shadowed methods (since these will most likely have the same signatures). When all variables should be passed through, in OTcl it is necessary for correct variable substitution to use:

  eval $self next $args

To avoid such difficulties, we made the passing of all arguments the default case; a simple

  next

performs the task of passing all arguments to the shadowed methods. These arguments are called the standard arguments. If the standard argument feature should not be used, optionally arguments can be given or the flag --noArgs could be set as sole argument, which means that the shadowed method is called with no arguments.

E.g. the following next call ignores the standard arguments and sends the arguments 1 and 2 instead:

  next 1 2

As an example all classes involved in the previous example should get a constructor instance method, which simply sets an instance variable on the object:

  Room instproc init args {
    my set roomNumber 0
    next
  }    
  4WallsRoom instproc init args {
    my set doorPosition 0
    next
  }
  CookingPlace instproc init args {
    my set stoveType electric
    next
  }
  Kitchen instproc init args {
    my set cookName -
    next
  }

After creation an object of class Kitchen gets automatically four instance variables cookName, roomNumber, doorPosition and stoveType set up with default values in this order (since this is the order of the classes in the next-path). Note, that the order is important, because one missing next call, in one of the init methods, means that succeeding init methods will not be executed. This mechanism functions equally on all kinds of instprocs, not only on constructors.

The constructors use the args argument, which allows us to give a list of variable length as arguments. To ensure reusability of our classes the constructors should use args in most cases, since they may pass through arguments for constructors further up the class hierarchy.

If a proc with the searched name exists on the object it shadows all instprocs. A next call in a proc leads to the normal next-paths search, starting with the object's class.

By the way, an observant reader might notice that the example above can be rewritten without explicit constructors, just by using parameters with default values.

  Class Room -parameter {{roomNumber 0}}
  Class 4WallsRoom -superclass Room -parameter {{doorPosition 0}}
  Class CookingPlace -parameter {{stoveType electric}}
  Class Kitchen -superclass {4WallsRoom CookingPlace} -parameter {{cookName -}}

If an instance of a Kitchen is created it will contain instance variables for doorPosition, cookName, roomNumber, and stoveType, as the following statements will show.

  Kitchen k
  puts [k info vars]

Dynamic Class and Superclass Relationships

Another property of XOTcl that distinguishes it from statically typed languages are dynamics of class relationships. The realization of the definition of super-classes as seen above with the superclass method suggests already, that it is not only available at the class definition time. In the above example its appended to the class definition with "-superclass" as a short syntax for method invocation at definition time (all other available methods can also be called with a preceding dash ("-") appended to definitions).

At any time the class graph may be changed entirely using the superclass method. Suppose the rooms and kitchens created in modeling of a house should be displayed to a screen, but it is not determined, whether the user of the system has the possibilities for graphical outputs. Two classes TextOutput and GraphicalOutput may be defined, which handle the output. Both have an instproc paint which does the painting of the virtual world on the chosen display type. The common output requirements are handled by a derived class VirtualWorldOutput which calls the paint method of the superclass using next. In statically typed languages it would need more sophisticated constructs to change the output class at run-time. E.g. a delegation to another object handling the intrinsic task of the output object would be introduced solely for the purpose of configuring the output form. With a dynamic class system we can use the superclass method to do so easily:

  Class TextOutput
  TextOutput instproc paint args {
    # do the painting ...
  }
  Class GraphicalOutput
  GraphicalOutput instproc paint args {
    # do the painting ...
  }

  # initially we use textual output
  Class VirtualWorldOutput -superclass TextOutput
  VirtualWorldOutput instproc paint args {
    # do the common computations for painting ...
    next; # and call the actual output
  }

  # user decides to change to graphical output
  VirtualWorldOutput superclass GraphicalOutput

Sometimes, such a change to new intrinsic properties should not happen for all instances of a class (or the class hierarchy), but only for one specific object. Then the usage of a dynamic super-class relationship is a too coarse-grained means. A second form of such dynamics is the changing of the relationship between object and class. This means, objects can also change their class dynamically at run-time. This feature may be used to model a life-cycle of an object, without loosing the object's identity, inner state or per-object-specializations through procs. The class instance method enables this functionality.

An example would be an agent for the virtual world. Agents may be placeholders for persons, who interactively travel the world, or programs, which act automatically. When a person decides at run-time to give a task it has performed formerly by hand to an automatic agent, the agents nature changes from interactive agent to automatic agent, but the identity and the local state (that means the parts of the task, that are already fulfilled by the person) stay the same. This is a scenario for changing class relationships, e.g.:

  Class Agent
  Class AutomaticAgent -superclass Agent
  Class InteractiveAgent -superclass Agent

  # create a new agent for a person
  InteractiveAgent agent1

  # the person does something ...
  # and decides the change to an automatic agent
  agent1 class AutomaticAgent

Meta-Classes

Meta-classes are a special kind of classes. Similar as classes are managing objects (where managing means: control the creation and destruction of instances, know what instances exist, provide methods), meta-classes are managing classes. So, meta-classes are used to define classes. In other words, every Class in XOTcl is created by a meta-class, in most cases by the meta-class named Class. New user-defined meta-classes can be defined as subclasses of the predefined meta-class Class, or by adding an instmixin class (see below) containing Class to the precedence chain of the class. By defining Object instmixin Class one can even change the object system of XOTcl in in a way such that every created Object is a meta-class.

Since the concept of a meta-class are sometimes confusing to people of a background of some other programming languages, we explain meta-classes slowly with the analogy of classes and objects.

When a class Foo is created via the command

   Class Foo
it has no private variables and no special methods. This is somewhat similar as creating an object via Object:
   Object foo
This plain object foo can be configured directly, or one can create a class that configures the object. Instead of writing
   Object foo 
   foo set x 1
   foo proc hi {} {puts "hello"}
one can use
   Class C -superclass Object
   C instproc init {} {my set x 1}
   C instproc hi {} {puts "hello"}
and create an instance and call the method.
   C c1
   c1 hi
The same holds for meta-classes and classes as well: Instead of writing
   Class Foo
   Foo set x 1
   Foo proc hi {} {puts "hello"}
the following can be used:
   Class MC -superclass Class
   MC instproc init {} {my set x 1}
   MC instproc hi {} {puts "hello"}
The instances of meta-classes are classes which can be defined the usual way:
   MC Bar
   Bar hi
   Bar b1
Now we have a class names Bar which has a class-scoped variable named x with the value of 1 (set via the constructor); the class Bar has as well a class-method named hi which prints, when called, the string "hello". The class Bar can be used to create instances of the class like b1, b2 and so on.

Note that the command Class is a predefined definition of the most general meta-class in XOTcl. Each time we are creating a class, we use this meta-class. In order to define a specialized meta-class, we can do this the traditional object-oriented way: we subclass. Therefore, in to define a specialized meta-class, we can use:

  Class myMetaClass -superclass Class

This defines a new meta-class myMetaClass, which has all the abilities of meta-classes. That means that the programmer is able to specify new class features or override old ones. Later she/he may instantiate these into new classes.

This is a very powerful language feature, since it allows one to give some classes further abilities than the others (or to restrict classes). This way large program structures, like certain design pattern parts, may be instantiated. Meta-classes hold the common abstract parts of the structures. They allow one to form libraries of such structures very easily.

Example 1: Overloading the info method of classes

As a simple example we can derive a new meta-class NoClassInfo from Class. Later we override the info method of Class. Thus the classes created with NoClassInfo, have an info option that only produces an error message. All classes created with NoClassInfo, like Agent in the example below, are not capable of accessing the class info method anymore:

  Class NoClassInfo -superclass Class
  # redefine info ability
  NoClassInfo instproc info args {
    error "No class info lookup"
  }
  # derive agent class from meta-class, which
  # can not access class info
  NoClassInfo Agent
Now a call like:
  Agent info superclass

triggers the error message.

Example 2: Defining Classes that Count Their Instances

Meta-classes are frequently used to define some bookkeeping about the number of instances on the class level. In the following example we define a meta-class named CountedClass which defines classes that count their instances:

  Class CountedClass -superclass Class -parameter {{counter 0}}
  CountedClass instproc create args {
    my incr counter
    next
  }
  CountedClass instproc dealloc args {
    my incr counter -1
    next
  }
  CountedClass Dog

  Dog piffie
  Dog idefix
  puts "nr of dogs: [Dog counter]"

  piffie destroy
  puts "nr of dogs: [Dog counter]"
Note that the behavior introduced by meta-classes can be orthogonal to the behavior of the classes. One can define Dog as a specialization of Animal or defines a special kind of dog such as Poodle using the method superclass as usual.

Example 3: The Singleton Meta-Class

Finally, a small example, which is more practical. Some applications have the requirement that only one instance of a class might be defined at a certain time. Such a behavior is frequently called a "Singleton". In XOTcl we can define a class singleton by overloading the create method of Class: when create is called and there exists already an instance of the singleton it is returned instead of a new instance.

  Class Singleton -superclass Class
  Singleton instproc create args {
    expr {[my exists instance] ? [my set instance] : [my set instance [next]]}
  }
If someone wants to have a class e.g. Manager to be a singleton, you can create it by e.g.
  Singleton Manager -superclass FOO

Create, Destroy, and Recreate Methods

XOTcl allows since version 0.84 for a flexible destroy and recreate scheme. create and alloc are both Class instprocs handling creation for their instances. I.e.:

 className alloc [self]
and
 className create [self]

are used for creating an instance. A similar method dealloc exists on Class that handles physical destruction of an object. The method destroy on Object which lets an object destroy itself in fact has the following behavior:

  Object instproc destroy args {
   [my info class] dealloc [self]
  }

However, this behavior is not implemented in XOTcl, but in C. create distinguishes between the following situations:

  • Create a new object: By default create calls alloc and then doInitializations.
  • Recreate an existing object: When the specified object exists, it is recreated through the recreate method:
      givenClass recreate [self]
    

    The method recreate can be customized like all other XOTcl methods (e.g. by overloading or interception). By default recreate calls cleanup followed by doInitializations.

    Note, that recreate is not called, when a someone tries to recreate a class as an object or an object as a class. In these cases, destroy + create are used.

        Class c
        Object c ;# here, "c" is downgraded to an object, no "recreate" is called
    

For create and recreate, the method doInitializations is called automatically from C and has the following default behavior:

  • Search for parameter default values,
  • Call parameter initialization methods,
  • Call the constructor init.

Each step has a method call that can be changed, intercepted, etc. Of course, cleanup, recreate, dealloc, etc. can also be overloaded or intercepted.

Consider a typical case for overloading recreate: a structure preserving recreate that cleans up the class but preserves the existing class hierarchy (subclass and instance relationships):

  Class StructurePreservingRecreate
  StructurePreservingRecreate instproc recreate {cl args} {
    if {[my isclass $cl]} {
      set subclass [$cl info subclass]
      set instances [$cl info instances]
    }
    next
    if {[my isclass $cl]} {
      foreach sc $subclass {
        $sc superclass $cl
      }
      foreach i $instances {
        $i class $cl
      }
    }
  }
  Object instmixin add StructurePreservingRecreate

Now the following code does not change the superclass or instance relationships of C:

  Class A
  Class B
  Class C -superclass {A B}
  Class D
  Class E -superclass {C D}
  C c1
  C c2

  # recreate -> is structure preserving
  Class C -superclass {A B}
  C c2

  # test
  puts superclass=[C info superclass]
  puts subclass=[C info subclass]
  puts instances=[C info instances]
  puts class=[c1 info class]
  puts class=[c2 info class]
Starting with XOTcl 1.4.0, xotcl provides also a user-friendly way for a structure-prevering recreate implemented in C. Since this version, one can configure "softrecreate" as follow.
::xotcl::configure softrecreate true
This command causes that recreates are structure-conservative.

Methods with Non-Positional Arguments

So far we have introduced methods only with positional arguments: that is, the position of an argument in the argument list determines to which local variable the argument is bound, when the method is invoked. Sometimes non-positional arguments -- arguments that carry both a name and a value when the method is invoked -- are useful. Before a non-positional argument can be used, it must be defined in the method definition using the following syntax:

 className instproc methodName ?non-pos-args? args body ?assertions
 objName proc methodName ?non-pos-args? args body ?assertions

The non-positional arguments are defined with the following syntax:

 {-name?:checkoption1, checkoption2, ...? default value} \
     {-name?:checkoption1, checkoption2, ...? ?default value?} ...

Only the name of the non-positional argument is really required, all other parts of this syntax are optional.

Let's consider a simple example, where a method with two non-positional args is defined; one has only a name ("a"), and one has a name and a default value (b):

 Object o
 o proc someproc {-a {-b {1 2 3}} x y} {
     puts "$a $b $x $y"
 }

We can invoke this method as follows:

 o someproc -b {4 5} -a 1 3 4

Here, the order of a and b can be changed; hence the name non-positional arguments. As b has a default value, we do not need to provide a value for it. In the following invocation b has the value "1 2 3":

 o someproc -a 1 3 4

The ordinary arguments start after the last non-positional argument (here: "3 4"). We can explicitly end the non-positional arguments by using "--". This is useful if we want to provide arguments that contain dashes ("-"), e.g.:

 o someproc -a 1 -- -b -c

Sometimes we want to check or control the non-positional arguments. For instance, in the above invocation, we might want to check that a is not forgotten, because otherwise the method cannot execute properly. This is the role of the checkoptions. There are three predefined checkoptions: required, boolean and switch. required checks whether a non-positional argument is given, boolean checks that a non-positional argument is of boolean type. For instance:

 Class P
 P instproc someproc {-a:required {-b:boolean true}} {
     puts "$a $b"
 }
 P p

This method requires a, and b needs to be of type boolean (is has the default value true). This invocation is valid:

 p someproc -a 1 -b 0

This invocation is invalid, because a is missing, and b is not a Tcl boolean type:

 p someproc -b " a b v"

The checkoption switch is similar to boolean except it does not require an additional argument. If the default value is false, the switch can be turned on, if the default is true it can be switched off.

The checkoptions are extensible. In fact, they are defined in an object ::xotcl::nonposArgs. We can extend this object with new methods. A check option method has the following syntax:

 someobject|someclass proc|instproc methodName {?optional nonpositional arguments? argName arg} {
  ...
 }

argName is here used to denote the name of the argument, and arg is the provided value.

Of course, the non-positional arguments can also be introspected. The following info options return the non-positional arguments of a method:

 objName info nonposargs methodName
 className info instnonposargs methodName

Message Interception Techniques

Even though object-orientation orders program structures around data, objects are characterized primarily by their behavior. Object-oriented programming style encourages the access of encapsulated data only through the methods of an object, since this enables data abstractions. A method invocation can be interpreted as a message exchange between the calling and the called object. Therefore, objects are at runtime only traceable through their message exchanges. At this point the message interceptors can be applied to catch and manipulate all incoming and outgoing messages of an object.

Generally interceptors can be applied to attach additional or extrinsic concerns to an object or a class or a class hierarchy. For instance roles or aspects can be implemented this way on various levels of scale.

We have already discussed some interception techniques implicitly. E.g., the unknown mechanism intercepts messages that have not be found on the object. It can be used as a very useful programming technique, e.g., the define a default behavior for an object. The interceptors presented in this section have a different character: They are applied before/after the original method even if the method is defined for the target object. Thus these interception techniques may be applied

We will discuss the message interceptors in this section in detail. The table below gives an impression, when which interceptor may be applied.

Message Interceptors Overview

Applied When

Primary Target Structure

Coverage

Per-Object Filter

before/after a call

object hierarchies

all methods

Per-Class Filter

before/after a call

class and class hierarchies

all methods

Per-Object Mixin

before/after a call

object

specific methods

Per-Class Mixin

before/after a call

class and class hierarchies

specific methods

Unknown Mechanism

after method was not found

object

all unknown calls


Filter

The filter (see [Neumann and Zdun 1999a] for more details) is a language construct to implement broader extensional concerns either for a single object or for several classes or class hierarchies. This way large program structures at the scale of several classes or class hierarchies can be managed. It is a very general interception mechanism which can be used in various application areas. E.g. a very powerful programming language support for certain design patterns is easily achievable, but there are also several other domains which are covered, like tracing of program structures, self-documentation at run-time, re-interpretation of the running program, etc.

A per-class filter is a special instance method that is registered for a class C. A per-object filter is a special instance method that is registered for a object o. Every time an object of class, C or the object o respectively, receives a message, the filter method is invoked automatically.

Usage of Filters

All messages to a filtered object must go through the filter before they reach their destination object. A simple example would be a sole filter on the class of the object. To define such a filter two steps are necessary. Firstly an filter method has to be defined, then the filter has to be registered. The filter method consists of three parts which are all optional. A filter method has the following form:

  className instproc FilterName args {
    pre-part
    next
    post-part
  }

When a filter comes to execution at first the actions in the pre-part are processed. The filter is free in what it does with the message. Especially it can (a) pass the message, which was perhaps modified in the pre-part, to other filters and finally to the object. It can (b) redirect it to another destination. Or it can (c) decide to handle the message on its own. The forward passing of messages is implemented through the next primitive of XOTcl. After the filter has passed its pre-part, the actual called method is invoked through next.

After the call of next is processed, the execution returns to the point in the filter, where the next call is located and resumes execution with the actions of the post-part. These may contain arbitrary statements, but especially may take the result of the actual called method (which is returned by the next-call) and modify it. The caller then receives the result of the filter, instead of the result of the actual called method.

The pre- and post-part may be filled with any ordinary XOTcl-statements. The distinction between the three parts is just a naming convention for explanation purposes.

The filter uses the args argument which lets us use a list of variable length as arguments, since it must filter a lot of different calls, which may have different argument lists. Furthermore, it may pass through arguments to other filters and the preceding filters may change the argument list.

Since any proc/instproc may be a filter, a registration of the filter is necessary, in order to tell XOTcl, which instprocs are filters on which classes. The filter and instfilter instance methods are able to handle this task for per-object filters and per-class filters respectively. Similar to the XOTcl language introduced so far, the filter registration is dynamic at run-time. By supplying a new list of filters to filter/instfilter, the programmer can change the filters registered on a class at arbitrary times. The filter instance method has the syntax:

  className instfilter filterList
for per-class filters and:
  objName filter filterList
for per-object filters.

Now a simple example should show the filter's usage. In the preceding examples we have defined several rooms. Every time a room action occurs it is likely that the graphical sub-system has to change something on the output of that particular room. Therefore, at first we need a facility to be informed every time an action on a room happens. This is quite easily done using filters:

  Class Room
  Room r1; Room r2;       # just two test objects

  Room instproc roomObservationFilter args {
    puts "now a room action begins"
    set result [next]
    puts "now a room action ends - Result: $result"
    return $result
  }

  Room instfilter roomObservationFilter

Now every action performed on room objects is notified with a pre- and a post-message to the standard output stream. We return the result of the actual called method, since we don't want to change the program behavior at all. E.g. we can set an instance variable on both of the two room objects:

  r1 set name "room 1"
  r2 set name "room 2"

The output would be:

  now a room action begins
  now a room action ends - Result: room 1
  now a room action begins
  now a room action ends - Result: room 2


  

Figure 4: Cascaded Message Filtering



All classes may have more than one filter. In fact they may have a whole filter chain, where the filters are cascaded through next. The next method is responsible for the forwarding of messages to the remaining filters in the chain one by one till all pre-parts are executed. Then the actual method is executed and then the post-parts come to turn. If one next-call is omitted the chain ends in this filter method. As an example for an additional filter we may register a filter that just counts the calls to rooms.

  Room set callCounter 0;  # set class variable
  Room instproc counterFilter args {
    [self class] instvar callCounter
    incr callCounter
    puts "the call number callCounter to a room object"
    next
  }
  Room instfilter {roomObservationFilter counterFilter}

Filters are invoked in registration order. The order may be changed by removing them and adding them in new order. Filters are inherited by sub-classes. E.g. in the preceding example for the next path, an OvalOffice was derived from the Room class. Without a change to the program each OvalOffice object automatically produces the same filter output as rooms.


  

Figure 5: Filter Inheritance


Filter chains can also be combined through (multiple) inheritance using the next method. When the filter chain of the object's class is passed, the filter chains of the superclasses are invoked using the same precedence order as for inheritance. Since on the subclass there may also be a another filter chain, without sophisticated computing in the pre- and post-parts one can produce easily a powerful tracing facility. E.g. if we want to distinguish an OvalOffice from other rooms we may want to add a filter solely for rooms of the type OvalOffice:

  Class OvalOffice -superclass Room
  OvalOffice o1;  # test object
  OvalOffice instproc ovalOfficeObservationFilter args {
    puts "actions in an oval office"
    next
  }
  OvalOffice instfilter ovalOfficeObservationFilter

A simple call to the o1 object, like:

  o1 set location "Washington"

produces the following output:

  actions in an oval office
  now a room action begins
  the call number 3 to a room object
  now a room action ends - Result: Washington

As seen already, filter registrations can be added dynamically at runtime. But they may also be removed. Perhaps the counting on rooms should stop after a while, then a simple call of the instfilter method is sufficient:

  Room instfilter roomObservationFilter

Filters can be removed completely by giving an empty list to the registration method:

  Room instfilter {}

Per-object filters operate on a single object. E.g. if we only want to observe a single Room object room1, we can use the filter method to register the roomObservationFilter only for this particular instance:

  room1 filter roomObservationFilter

As a filter we can register any method in the precedence order of the class or object. Thus we can also register procs as per-object filters. Additionally, meta-class methods may be registered as per-class filters. Filters are linearized so that each filter is only executed once, even if it is registered multiple times.



Introspection on Filters

In order to gain information about the currently registered filters on a certain object/class, the class-object info option filters and the class info option instfilters may be queried. It returns a list of the currently registered filters:

  className info instfilter
  objName info filter

A special call-stack info option for filters is self filterreg. It returns the name of the object or class on which the filter is registered. Since the filter may be registered on other objects/classes than the one on which it is defined, this may vary from self class in the filter. The command returns a list of the form:

  objName filter filterName
or:
  className instfilter filterName
respectively.



Example: A Simple Trace Filter

The trace example primarily demonstrates the inheritance of filter chains. Since all classes inherit from Object, a filter on this class is applied on all messages to objects. The Trace object encapsulates methods for managing the tracing:

  Object Trace
  Trace set traceStream stdout

  Trace proc openTraceFile name {
    my set traceStream [open $name w]
  }

  Trace proc closeTraceFile {} {
    close $Trace::traceStream
    my set traceStream stdout
  }

  Trace proc puts line {
    puts $Trace::traceStream $line
  }

  Trace proc add className {
    $className instfilter [concat [$className info filter] traceFilter]
  }

First we define the object and set a variable for the stream to which we send the trace outputs (here: stdout). With a method for opening and a method for closing a file we can redirect the trace stream to a file. puts is helper method for the filter to print an output to the selected output stream. In add the traceFilter is appended to the existing filters of a specified class. The actual filter method (see below) displays the calls and exits of methods with an according message. The calls are supplied with the arguments, the exit traces contain the result values. We have to avoid the tracing of the trace methods explicitly.

  Object instproc traceFilter args {
    # don't trace the Trace object
    if {[string equal [self] ::Trace]} {return [next]}
    set context "[self class]->[self callingproc]"
    set method [self calledproc]
    switch -- $method {
      proc -
      instproc {::set dargs [list [lindex $args 0] [lindex $args 1] ...] }
      default  {::set dargs $args }
    }
    Trace::puts "CALL $context>  [self]->$method $dargs"
    set result [next]
    Trace::puts "EXIT $context>  [self]->$method ($result)"
    return $result
  }

As trace message we write the callee´s context (class and proc), the invoked method (using calledproc), and the given arguments. In the switch statement we avoid to print whole method bodies.

With

  Trace add Room

messages to all rooms, including all instances of Room´s sub-classes, are surrounded with a CALL and an EXIT output. With

  Trace add Object

messages to all objects in an XOTcl environment are surrounded with a CALL and an EXIT output. In general, it is possible to restrict the trace to instances of certain classes, or to produce trace output for only certain methods. This requires registration methods and a more sophisticated implementation of the filter method.



Mixin Classes

Per-object and per-class mixins (see [Neumann and Zdun 1999c] for more details) are another interception technique of XOTcl to handle complex data-structures dynamically. Here, we use mixin as a short form for mixin class. All methods which are mixed into the execution of the current method, by method chaining or through a mixin class, are called mixin methods. Mixin classes resembles the filter presented in the preceding section. While the filters work on all calls to all methods of an object/class hierarchy, the mixin classes are applied on specific methods. The filter is defined in a single method, while the mixin is composes several method in a class.

Supplemental Classes

Mixin classes cover a problem which is not solvable elegantly just by the method chaining, introduced so far. To bring in an addition to a class, the normal XOTcl way is to define a mixin method and chain the methods through next, e.g.:

  Class Basic
  Basic instproc someProc  {
    # do the basic computations
  }
  Class Addition
  Addition instproc someProc {
    # do the additional computations
    next
  }

In order to mix-in the additional functionality of the supplemental class Addition a new helper class (sometimes called intersection class) has to be defined, like:

  Basic+Addition -superclass {Addition Basic}

This is even applicable in a dynamical manner, every object of the class Basic may be changed to class Basic+Addition at arbitrary times, e.g.:

  Basic basicObj
  ...
  basicObj class Basic+Addition

Now consider a situation with two addition classes. Then following set of classes has to be defined to cover all possible combinations:

  Class Basic
  Class Addition1
  Class Addition2
  Class Basic+Addition1 -superclass {Addition1 Basic}
  Class Basic+Addition2 -superclass {Addition2 Basic}
  Class Basic+Addition1+Addition2 -superclass {Addition2 Addition1 Basic}

The number of necessary helper classes rises exponential. For n additions, 2n-1 (or their permutations if order matters) artificially constructed helper-classes are needed to provide all combinations of additional mix-in functionality. Furthermore it is possible that the number of additions is unlimited, since the additions may produce other additions as side-effects. This demonstrates clearly that the sub-class mechanism provides only a poor mechanism for mix-in of orthogonal functionality. Therefore we provide an extension in the form of class-object mixin classes, which are added in front of the search precedence of classes.

Per-Object Mixins

The mix-ins methods extend the next-path of shadowed methods. Therefore, per-object mix-in methods use the next primitive to access the next shadowed method. Consider the following example:

  Class Agent
  Agent instproc move {x y} { 
    # do the movement
  }
  Class InteractiveAgent -superclass Agent
  # Addition-Classes
  Class MovementLog
  MovementLog instproc move {x y} { 
    # movement logging
    next
  }
  Class MovementTest
  MovementTest instproc move {x y} {
    # movement testing
    next
  }

An agent class is defined, which allows agents to move around. Some of the agents may need logging of the movements, some need a testing of the movements, and some both (perhaps only for a while). These functionalities are achieved through the additional classes, which we will apply through per-object mixins.

Before we can use the per-object mix-ins on a particular object, we must register the mixins on it with the mixin instance method. It has the syntax:

  objName mixin mixinList

For example we may create two interactive agents, where one is logged and one is tested:

  InteractiveAgent i1; InteractiveAgent i2
  i1 mixin MovementLog
  i2 mixin MovementTest

At arbitrary times the mixins can be changed dynamically. For example i2's movements can also be logged:

  i2 mixin MovementTest MovementLog


  

Figure 6: Per-Object Mix-ins: Next-Path for the Example



The mixin option of the info instance method allows us to introspect the per-object mixins. It has the syntax:

  objName info mixin ?pattern?

It returns the list of all mix-ins of the object, if pattern is not specified, otherwise it returns the matching per class-object mixin classes.

The inverse operation of info mixin is mixinof finds out, into which objects an per-object mixin class is mixed into.
  clsName info mixinof ?pattern?

Note, that the constructors (init methods) of per-object mixins (and per-class mixins) are only called, if the mixin is registered already during object initialization (when init is called). For per-object mixins, one can achieve the initialization of a mixin via an idiom like

  Object o -mixin M -init
that registers the mixin before init is called. When a mixin is registered after object creation and it needs initializations, it is necessary to define special methods for this. Note, that the behavior described here is introduced in version 0.84 to ensure consistent behavior of intrinsic classes, per-object and per-class mixins, and to achieve predictable behavior for dynamic registration for all kind of mixins, and as well during recreations of objects having mixins registered. Older versions used heuristics for the initialization of per-object mixins.

Per-Class Mixins

Per-class mixins are exactly identical in their behavior to per-object mixins, but they operate on classes. Thus they are the class-specific variant of the per-object mixins, like instprocs are a class-specific variant of procs. Therefore, in the language the per-class mixins are called instmixins.

In general a per-class mixin is a class which is mixed into the precedence order of all instances of the class and all its subclasses it is registered for. It is also searched before the object's class itself is searched, but after per-object mixins.

Per-class mixins are linearized according to the precedence order like classes on the superclass hierarchy. I.e. from the full list of per-object mixins, per-class mixins, and intrinsic classes (and all the superclasses of all these classes) always the last occurrence is used.

From the point of view of language expressibility instmixins are not required, because they cannot express anything that per-object mixins cannot express already (like procs can express any instproc feature). As alternative to instmixins, we could simply register the per-object mixins in the constructor of the class.

But there at least the following reasons for instmixins as an additional language construct:

  1. we can at runtime determine with info mixin and info instmixin whether it is a class- or object-specific mixin. Thus we get a better structuring at runtime.
  2. We have not to 'pollute' the constructors with per-class mixin registrations. Therefore, the constructors get more understandable.
  3. If it is required to add (and remove) dynamically interceptors to a set of objects, which are instances of a certain type, per-class mixins are much easier to handle (e.g. add an instmixin to Object to intercept e.g. all calls to certain predefined methods).
  4. The language is more 'symmetrical', since any object-specific feature in XOTcl has a class-specific variant.

The mix-ins methods of per-class mixins extend the next-path of shadowed methods in the same way as per-object mixin methods. Before we can use a per-class mix-in on a particular class, we must register the mixin on it with the instmixin instance method. It has the syntax:

  className instmixin mixinList
The inverse operation of info inmixin is instmixinof finds out, into which objects an per-object mixin class is mixed into.
  className info instmixinof ?-closure? ?pattern?

Now consider that in the given per-object mixin example all interactive agents should be tested. We could either build a subclass TestedInteractiveAgent or register the per-object mixin in the constructor of the interactive agent class. The subclass solution leads to the same combinatorial explosion of intersection classes as discussed in the previous section, if more supplemental classes are added. The per-object mixin solution pollutes the constructor and does not prevail the structural semantics that the 'tested' property belongs to the interactive agent class at runtime

Here, we can use a per-class mixin:

  Class Agent
  Agent instproc move {x y} {# do the movement}
  Class InteractiveAgent -superclass Agent
  Class MovementTest
  MovementTest instproc move {x y} {
    # movement testing
    next
  }

  # now register the instmixin
  InteractiveAgent instmixin MovementTest

The per-class mixin now operates on all interactive agent including the instances of subclasses. E.g. for interactive agents i1 and i2 we automatically have movement testing. i2 is also logged, since it has the logging class as object-specific mixin:

  InteractiveAgent i1
  InteractiveAgent i2 -mixin MovementLog

  i1 move 3 4
  i2 move 1 2 

At arbitrary times the instmixins can be changed dynamically.

The instmixin option of the class info instance method allows us to introspect the per-class mixins. It has the syntax:

  className info instmixin ?className2?

It returns the list of all instmixins of the the class, if className2 is not specified, otherwise it returns 1, if className2 is a mixin of the object, or 0 if not.

Per-class mixins are applied transitively. That means the per-class mixin A of a per-class mixin B is also applied for an object in in B's scope. This is exactly the same as how superclasses are applied for instances. Consider the following example

  Class X11 \
     -instproc test args {
	puts [self class]
	next
     }
  Class X12 \
    -instproc test args {
	puts [self class]
	next
    }
  Class X \
    -instmixin {X11 X12} \
    -instproc test args {
	puts [self class]
	next
    }

  Class Y \
    -instmixin X

  Y create y -test
  X create x -test

Here the application as a superclass (for x) yields the same result as the application as an instmixin (for y):

  ::X11 
  ::X12 
  ::X

Precedence Order

The precedence order is composed by the precedence order of the superclass hierarchy (as explained earlier) and the message interceptors. In general, filters precede mixins and the superclass hierarchy. They are applied in the order of the next path of the object. Thus per-object filters are ordered before per-class filters.

Mixins are processed after the filters. Again, they are applied in the order of the next path of the object. Thus per-object mixins are ordered before per-class mixins.

Finally, the object's own heritage order comes in the order: object, class, superclasses.

The three precedence order lists (filters, mixins, and classes) are pre-calculated and cached.

Filters as well as classes (mixins and ordinary classes) are linearized. That means, each filter and each class can be only once on a precedence order list. If a filter or class can be reached more than once, than the last occurrence is used.

For instance, consider a class A is superclass, per-class mixin, and per-object mixin. On the precedence order lists only the last occurrence as a superclass is used after linearization.

Guards for Filters and Mixins

Message interceptors, such as filters and mixins, are applied for potentially huge number of messages. In many cases it is possible to reduce the effective number of cases in which interceptors are applied. Interceptor guards offer this functionality: they are boolean conditions with which you can specify in which cases a registered interceptor should be applied.

Filter Guards

A filter guard is a set of conditions that determine whether a filter is to be executed upon a certain invocation or not. Syntactically we can append a filter guard to the filter registration, or it can be registered using the methods filterguard for filters and instfilterguard for instfilters.

Each filter guard is an ordinary condition. A filter guard is executed in the call frame of the filter to be executed, if the filter guard returns 1. Thus, the call-stack information are already set to the values of the targeted filter - and these values can be used in the filter guard.

Let us consider a simple program:

Class Room
Room instproc enter {name} {puts [self proc]}
Room instproc leave {name} {puts [self proc]}
Room instproc loggingFilter args {
    puts [self calledproc]
    next
}
Room instfilter loggingFilter

Now consider we only want to apply the logging filter for enter and leave, not for any other message sent to Room instances. In the following example, for instance, we do not want to log the set message:

Room r 
r enter Uwe
r leave Uwe
r set roomName "Office"

In this example a filterguard can be applied to restrict the application of the filter to those two methods:

Room instfilterguard loggingFilter {
  [self calledproc] == "enter" || 
  [self calledproc] == "leave"}

Here we limit the filter application of the logging filter on rooms to calls to enter and leave. All other calls are not filtered at all. Note that the same syntax can also be applied for filterguard. Also, there is a short form to register filter guards directly during filter registration. The following code has the same semantics as the filter and filter guard definitions above:

Room instfilter {{loggingFilter -guard {
    [self calledproc] == "enter" || 
    [self calledproc] == "leave"}}}

The filter guard language construct is registration centric. It only applies for the class or object on which a filter is registered, not for all applications of the filter method. That is, if we use loggingFilter on another class we may give no or completely different filter guards.

If no filter guard is given for a filter, we assume that it is to be applied on all methods (equivalent to the filter guard '1' which is always true).

There are introspection options for filter guards. In particular, we can use info filterguard and info instfilterguard for getting the filter guards for a particular filter or instfilter respectively. For instance:

puts [Room info instfilterguard loggingFilter]

This prints out the content of the above guard definition. We can also append -guard to info filter or info instfilter to obtain a filter definition with guards:

puts [Room info instfilter -guards]

If we call a method from within a filter guard, as for instance callsMethod, we might require some parameters from the guard's context, such as calledproc. These parameters can be passed as references, as in the following example:

  Room instfilter loggingFilter
  Room instfilterguard loggingFilter {[my callsMethod openURL [self calledproc]]}

This example works because the filterguard is already set to the scope of the guard. Now we can use this dynamic calledproc context in the called method:

  Room instproc callsMethod {method calledproc} {
    return[string match $calledproc $method]
  }

We simply check whether the called method matches the given method name or not.

Mixin Guards

Similar to filters, there are mixin guards, defined with mixinguard and instmixinguard, or with -guard during mixin registration. Consider a simple example: there are a number of birds who have two mixins: Fly and Sing. For Fly there are limitations: a bird can only fly if it is at least two years old and is not a Penguin. Such problems are be solved with mixin guards:

  Class Fly
  Fly instproc fly {} {puts "[my signature]: yippee, fly like an eagle!"}

  Class Sing
  Sing instproc sing {} {puts "[my signature]: what a difference a day makes"}

  Class Animal -parameter age
  Animal instproc unknown args { puts "[my signature]: how should I $args?"}
  Animal instproc signature {} {
    return "[self] [my info class] ([my age] years)"
  }

  Class Bird -superclass Animal
  Class Penguine -superclass Bird
  Class Parrot -superclass Bird
  Class Duck -superclass Bird

  Parrot tweedy -age 1
  Penguine pingo -age 5
  Duck donald -age 4
  Parrot lora -age 6

  Bird instmixin {{Fly -guard {[my age] > 2 && ![my istype Penguine]}} Sing}

An invocation like:

foreach bird {tweedy pingo donald lora} { $bird fly }

yields the following result:

::tweedy ::Parrot (1 years): how should I fly?
::pingo ::Penguine (5 years): how should I fly?
::donald ::Duck (4 years): yippee, fly like an eagle!
::lora ::Parrot (6 years): yippee, fly like an eagle!

There are similar introspection options for mixin guards as those for filter guards. In particular, we can use info mixinguard and info instmixinguard for getting the mixin guards for a particular mixin or instmixin respectively.

Querying, Setting, Altering Filter and Mixin Lists

The methods mixin, instmixin, filter and instfilter are system slots having the same query and update interface.
  • If one of those methods is called without argument, it returns the current setting.
  • If it is called with one argument, the argument is used to set the specified list as indicated in the above examples.
  • If these methods are called with more than one argument, the first argument is used to specify the action. Possible values for the action are set, get, add and delete. See below for commonly used examples.

obj mixin same as: obj info mixin
obj mixin {C1 C2} same as: obj mixin assign {C1 C2}
obj mixin assign {C1 C2}sets the mixins for obj
obj mixin add C3 adds the mixin C3 on front of the mixin list
obj mixin add C3 end adds the mixin C3 at the end the mixin list
obj mixin add C3 3 adds the mixin C3 at the 3rd position
obj mixin delete ::C3removes the mixin C3 from the mixin list. Use absolute class names. delete supports an optional flag -nocomplain that does not produce an error, when the specified class is not in the list.

Note, that the list of possible actions can be extended by extending the class ::xotcl::Relations.

Querying Call-stack Information

Since the presented interceptors are normal XOTcl instprocs they can access all XOTcl introspection abilities introduced so far. In instprocs all recent information is accessible within their scope. But the interceptors are mechanisms, which cover more then their sole scope. The meaningful usage of the meta-programming abilities often requires to go further and to get information from the caller's and the callee's scope (e.g for delegation decisions). Therefore, we introduced rich call-stack informations for the interceptors. Note, that these are also available for ordinary methods, but the "called..." info options return empty strings.

All call-stack information are packed compactly into the self primitive as additional options. Note, before XOTcl version 0.84 these were implemented as a part of the info method. They are part of the self command for conceptual integrity: introspection options in info can be expected to produce the same result, when they are not explicitly changed. In contrast, all information provided by self are call-stack dependent.

Querying Call-stack Information via self

self activelevel

Returns the stack level from where the current command was invoked from, or where the last next was called (whatever is closer to the invocation). If the current command was invoked from an XOTcl method the absolute level is returned (e.g. #4) which can be used in the uplevel or upvar Tcl command or XOTcl method. If the current command was not invoked from an XOTcl method, the value 1 is returned.

self calledproc

Returns the name of the method which was invoked in the original call.

self calledclass

Returns the name of the class which presumably (if no dynamic class change occurs afterwards) is invoked in the original call.

self callingclass

Returns the name of the class from which the call was invoked (if one exists, otherwise an empty string).

self callinglevel

Returns the stack level from where the current command was invoked from. In contrary to activelevel next-calls are ignored in the computation. If the current command was invoked from an XOTcl method the absolute level is returned (e.g. #4) which can be used in the uplevel or upvar Tcl command or XOTcl method. If the current command was not invoked from an XOTcl method, the value 1 is returned.

self callingproc

Returns the name of the method from which the call was invoked (if one exists, otherwise an empty string).

self callingobject

Returns the name of the object from which the call was invoked (if one exists, otherwise an empty string).

self filterreg

In a filter: returns the name of the object/class on which the filter is registered. Returns either 'objName filter filterName' or 'className instfilter filterName'.

self isnextcall

Return 1 if this method was invoked via next, otherwise 0

self next

Return the "next" method on the path as a string, i.e. the method which will be called by [next].


Note, that three options with the prefix calling represent the values of self, self proc, and self class in the scope where the original call was invoked. In the following section we will show a simple program in which all of the info options have different values.


Filter Call-stack Information Example

Now we discuss a simple example that shows that all filter introspection options may have different values:

  Class InfoTrace
  InfoTrace instproc infoTraceFilter args { 
    puts "SELF:                [self]"
    puts "SELF PROC:           [self proc]"
    puts "SELF CLASS:          [self class]"
    puts "INFO CLASS:          [my info class]"
    puts "CALLED PROC:         [self calledproc]"
    puts "CALLING PROC:        [self callingproc]"
    puts "CALLING OBJECT:      [self callingobject]"
    puts "CALLING CLASS:       [self callingclass]"
    puts "REGISTRATION CLASS:  [self filterreg]"
    puts "CALLING LEVEL:       [self callinglevel]"
    puts "ACTIVE LEVEL:        [self activelevel]"
    next
  }

  Class CallingObjectsClass
  CallingObjectsClass callingObject

  Class FilterRegClass -superclass InfoTrace
  Class FilteredObjectsClass -superclass FilterRegClass 
  FilteredObjectsClass  filteredObject 

  CallingObjectsClass instproc callingProc args {
     filteredObject set someVar 0
  }    
  FilterRegClass instfilter infoTraceFilter

The invocation of callingObject callingProc produces the following output:

  SELF:                ::filteredObject
  SELF PROC:           infoTraceFilter
  SELF CLASS:          ::InfoTrace
  INFO CLASS:          ::FilteredObjectsClass
  CALLED PROC:         set
  CALLING PROC:        callingProc
  CALLING OBJECT:      ::callingObject
  CALLING CLASS:       ::CallingObjectsClass
  REGISTRATION CLASS:  ::FilterRegClass instfilter infoTraceFilter
  CALLING LEVEL:       #1
  ACTIVE LEVEL:        #1

The filter reports for self the value filteredObject, since this is the object on which the set call is invoked; infoTraceFilter is the method of the filter, and therefore, the actual proc, while the actual class is InfoTrace, the filter's class. The class of the actual object is FilteredObjectsClass.

The called procedure is set. While the program stays in a XOTcl-instproc all calling-info-options are set, the calling procedure is callingProc, the calling class is the class, where the method is defined (namely CallingObjectsClass), and the object from which the call invoked is callingObject.

In this example, the calling level is equal to the active level, both are #1.

Slots

A slot is a meta-object that manages property-changes of objects. A property is either an attribute or a role in an relation. In a nutshell, a slot has among other attributes:

  • a name (which it used to access it),
  • a domain (object or class on which it can be used) , and
  • can be multivalued or not.

We distinguish between system slots (predefined slots like class, superclass, mixin, instmixin, filter, instfilter) and attribute slots (e.g. attributes of classes).

System Slots

System slots are predefined slots defining e.g. some relations between classes, or between objects and classes. The predefined system slots are:

  • superclass: every class in XOTcl has one or more superclasses. The name of this slot is superclass, the domain is ::xotcl::Class, the slot is multivalued, since one object might have multiple superclasses.

  • class: every object has a class; therefore, the domain of the slot is ::xotcl::Class, the property is not multivalued.

  • mixin: every object in XOTcl can have one or more mixin classes. The name of this slot is mixin, the domain is ::xotcl::Object , the slot is multivalued.

  • instmixin: same as above, but the domain is ::xotcl::Class.

  • filter, instfilter: similar to mixin and instmixin.

The system slots were introduced earlier with their semantics. Here we just point out, that they have all the same interfaces for querying, setting, adding and removing of slot values.

Every slot can be used set and query the property from its domain. The syntax for setting values is

  object property newValue
and for getting its values is
   set x [object property]
where property denotes the slot name. Every multivalued slot provides the methods add and delete. Here are a few examples for using the system slot mixin which we have introduced already in the section of the mixins
  Object o; Class M; Class N
  o mixin ::M      ;# replacing the per-object mixins of o with M
  o mixin reset ::M  ;# same as before
  o mixin add ::N   ;# add N to the front of the mixin list
  o mixin delete ::M ;# delete M from the mixin list
  puts [o mixin]   ;# query the current mixin list
Every system slot (e.g. superclass) has the exact same interface.

Attribute Slots

Attribute slots are used to manage the setting and querying of instance variables. We define now a person with three attributes name, salary and projects.

  Class Person -slots {
    Attribute name
    Attribute salary -default 0
    Attribute projects -default {} -multivalued true
  }

These attributes might have a default value or they might be multivalued. When an instance of class Person is created, the slot names can be used for specifying values for the slots.

  Person p1 -name "Joe"	

Object p1 has three instance variables, namely name, salary and projects. Since slot projects is multivalued, we can add a value to the list of values the add subcommand.

  Project project1 -name XOTcl \
     -description "A highly flexible OO scripting language"

  p1 projects add ::project1
  p1 projects add some-other-value

The value of the instance variable project of Person p1 is now the list {some-other-value ::project1}.

Attribute slots are implemented via dynamic object aggregations (see below), where the Class objects contain the slot objects with the information like default etc. In order to prevent name clashes between the slot objects and the methods of a class (like e.g. create), an intermediary object named slot is used as a container of the slot objects. In the example above we create an object structure of the following form:

  Person
  Person slot name
  Person slot salary
  Person slot projects

This object structure can be used to to query and modify the slot properties or to add additional methods to the slot objects. One application is for example to used slot-specific methods for checking slot values, as shown in the next section.

  Person info vars  ;# results in the list of variables of ::Person
  Person slot name info vars ;# list of variables of the slot object ::Person::slot::name
Since slot objects are ordinary XOTcl objects, they can have their own slots as well (such as default, name etc.). The following example sets and queries the default of the slot name of Person:

  Person slot name default "gustaf"
  ? {Person slot name default} gustaf

However, due to the current implementation, it is necessary to re-init the slot object when the slot properties (such as e.g. default) are changed. This can be achieved by calling the method init of the slot object.

Note that a statement for creating a slot object like

  ... {
    Attribute name
    ...
  }

is a short hand notation for

  ... {
    Attribute create name
    ...
  }

This is exactly the same situation like every where else in XOTcl, when an object/class is created. One has to use create explicitly, when a name of a slot object conflicts with a method of the class (e.g. one has to use "Attribute create class" if a slot named class is created).

One cannot define on a meta-class an attribute named slot or slots and use then "... MetaClass Foo -slots { ::xotcl::Attribute x}... to create the slot objects. To handle this naming conflict, one has to create the slot objects outside of the aggregation and to provide the values for the properties of Attribute (domain, manager, .... ) by hand.

Setter and Getter Methods for Slots

When a slot is called via its name, the call is delegated to the slot object. Per default, the slot value is read via the get method of the slot and it is set the assign method. By redefining these methods, it is possible to provide custom setter and getter methods. The following example redefines the setter methods assign to check, whether an attribute value is within the range between 1 and 99.

  Class create A -slots {
    Attribute foo -default 1 -proc assign {domain var value} {
      if {$value < 0 || $value > 99} {
        error "$value is not in the range of 0 .. 99"
      }  
      $domain set $var $value
    }
  }

  A create a1
  ? {a1 foo 10} 10
  ? {a1 foo} 10
  ? {catch {a1 foo -1}} 1

For the most common simple cases with single valued attributes, where neither setter or getter are redefined, XOTcl optimizes the slot access function and replaces the delegation to the slot object by the the C-level implementation of instparametercmd.

Note that it is possible to subclass Attribute (e.g. in order to store more properties for attributes, like when attributes are stored in a relational database) or to register mixin-classes or filters.

Backward-compatible Short-Hand Notation for Attribute Slots

XOTcl provides a short-hand notation for creating attribute slots, which is backward compatible for the most important options of XOTcl version prior to 1.5.0. Instead of writing

  Class Car -slots {
    Attribute owner
    Attribute doors -default 4
  }

one can use as well

  Class Car -parameter {
    owner
    {doors 4}
  }

The various features of the prior implementation of parameter are deprecated and will be removed in future versions.

Experimental Slot Features

Value Checking

Attribute slots can have types assigned which are tested whenever the instance variable is altered. The slot salary is defined as integer whereas projects is defined to be a list of instances of the class ::Project (a list of instances, since projects is defined as multivalued).

  Class Person -slots {
    Attribute name
    Attribute salary -default 0 -type integer
    Attribute projects -default {} -multivalued true -type ::Project
    ...
  }

  Person p2 -name "Sue"	-salary 1000

It is as well possible to define custom value checkers and to normalize the input values. We extend the previous example and define "my sex" as value for type. If the value checker consists of multiple words, the type check compiler assumes that the value is a Tcl command, to which the actual value is appended as additional argument before invocation. my refers to the slot object. In the example below, we define for the slot object an object specific method that returns 1 or 0 depending on the success of the check. This method (a) checks the values via switch and (b) normalizes and resets the value via uplevel.

  Class Person -slots {
    ...
    Attribute sex -type "my sex" -proc sex {value} {
      switch -glob $value {
        m* {my uplevel {$obj set $var m}; return 1}
        f* {my uplevel {$obj set $var f}; return 1}
        default {return 0}
      }
    }
  }

The slot values are actually checked via Tcl variable traces whenever the associated variable gets a new value assigned. This means that the values are enforced now matter how the variables are set. Therefore, the checks are performed in the following two commands as well, although the slot values are not accessed via the slot names. The checks will throw an error in the second command, since 1100x is not an integer.

  p2 incr salary 100
  p2 append salary x

Similarly the second command below will throw an error, since some-other-value is not an instance of ::Project.

  p2 projects add ::project1
  p2 projects add some-other-value     

When a check throws an error, the instance variables are reset to the previous value. To restore the original value, an associative array __oldvalue() is kept as instance variable in the object.

**** NOCHECK is removed ****

In general, checking of variables can be turned off globally by

  ::xotcl::Slot instmixin add ::xotcl::Slot::Nocheck

This mixin replaces the methods check and checkall as well as mk_type_checker by no-ops. When the mixin is active and the Attribute definitions are loaded, the specified type has no effect.

Value checking can be turned off also selectively for each slot via using ::xotcl::Slot::Nocheck as per-object-mixin; if attributes are subclassed, it is possible to register the Nocheck mixin on a subclass of Attribute.

Init Commands and Value Commands for Slot Values

An init command (initcmd) of a slot is similar to a default and is a command to be executed when the value of the associated variable is read the first time. That means that when an object is created the associated variable has no value. On the contrary, when a default is used, the variable is set to the default value, when the object is created.

The primary advantage of slot init commands is Lacy initialization: When an object has many slots and the initialization of all slots is costly (e.g. the value of each slot is fetched from a relational database), and not all of the values are needed for each instance, only the relevant variables of the object are initialized on demand.
  Class C -slots {
    Attribute x -initcmd {puts {init}; set _ 101}
  }

  C c1
  c1 info vars  ;# ==> returns ""
  c1 set  x     ;# ==> puts init, returns 101
  c1 info vars  ;# ==> returns "x"

The initcmd is executed only once, when the variable is read the first time. For later reads of the variable contents, the values are returned.

A value command (valuecmd) of a slot is similar to a a init command, except that it is executed whenever the value of the variable is read. A value command can be used e.g. to implement live updates for variables or for abstracting from sql sequences or the like.

Finally the value changed command (valuechangedcmd) can be used to specify the behavior, whenever the value of the variable is altered. This option is used to implement the value checking described in the last section.

The slot parameters default, initcmd and valuecmd have to be used mutually exclusively.

Nested Classes and Dynamic Object Aggregations

Most object-oriented analysis and design methods are based on the concepts of generalization and aggregation. Generalization is achieved through class hierarchies and inheritance, while static aggregation is provided through embedding. Since version 8.0 Tcl offers a namespace concept which can be used as a mechanism to provide dynamic aggregations.

A namespace provides an encapsulation of variable and procedure names in order to prevent unwanted name collisions with other system components. Each namespace has a unique identifier which becomes part of the fully qualified variable and procedure names. Namespaces are therefore already object-based in the terminology of Wegner. OTcl is object-oriented since it offers classes and class inheritance. Its objects are also namespaces, but an object is more than only a namespace. Therefore, two incompatible namespace concepts have existed in OTcl in parallel.

In XOTcl every object and every class is logically implemented as a separate Tcl namespace. The biggest benefit of this design decision aside from performance advantages is the ability to aggregate objects and nest classes. Contrary in OTcl every object has a global identifier. Through the introspection abilities of namespaces nested classes are also traceable at runtime and can be changed dynamically. In XOTcl objects are allowed to contain nested objects, which are dynamically changeable aggregates of the containing object.

Nested Classes

The notation for nested classes follows the syntax of Tcl namespaces by using ``::'' as a delimiter. For example the description of a oval carpet and a desk can nest inside of the OvalOffice class:

  Class OvalOffice
  # general carpet
  Class Carpet
  Class OvalOffice::Desk
  # special oval carpet - no name collision
  Class OvalOffice::Carpet -superclass ::Carpet

Nested classes can be used exactly like ordinary classes, a user can sub-class it, derive instances, etc. The information about the nesting structure of classes is available through the info instance method:

  className info classchildren ?pattern?
  className info classparent

The classchildren option returns a list of children, if one or more exist, otherwise it returns an empty string. classparent results in the name of the parent class, if the class is nested. Since nested classes are realized through namespaces, all functionality offered by Tcl's namespace command is usable from XOTcl as well.

Dynamic Object Aggregations

The nested classes only provide an aggregation of the descriptive not of the runtime properties of an object. We have pointed out the difference of object and class in XOTcl. Because of the splitting of a class into class and class-object it is possible to give each object its own namespace. The internal implementation of objects enable them to contain nested objects, which are aggregates of the containing object. In XOTcl these can be changed dynamically and introspected through the language support of dynamic object aggregations [Neumann and Zdun 2000b]. Suppose an object of the class Agent should aggregate some property objects of an agent, such as head and body:

  ClassAgent
  Agent myAgent

  Class Agent::Head
  Class Agent::Body

  Agent::Head ::myAgent::myHead
  Agent::Body ::myAgent::myBody

Now the objects myHead and myBody are part of the myAgent object and they are accessible through a qualification using ``::'' (or through Tcl's namespace command). But in the common case they will be accessed, as introduced so far: the explicit full qualification is not necessary when such variables are being accessed from within XOTcl methods, since the object changes to its namespace.

The information about the part-of relationship of objects can be obtained exactly the same way as for classes through the info interface:

  objName info children ?pattern?
  objName info parent

Relationship between Class Nesting and Object Aggregation

The classes Head and Body are children of the Agent class. It is likely that all agents, interactive or not, have properties for head and body. This implies a static or predetermined relationship between class nesting and object aggregation. Such predetermination do not exist in XOTcl, but are simply build, when specifying the relationship in the constructor, e.g.:

  Agent instproc init args {
    ::Agent::Head [self]::myHead
    ::Agent::Body [self]::myBody
  }

Now all agents derived from the class have the two property objects aggregated after creation. But still they are changeable in a dynamical manner, e.g. with:

  Agent myAgent
  myAgent::myHead destroy

The agent turns into a headless agent. In companion of the introspection mechanisms such constructions could be very useful. Suppose, that in the virtual world the agents heads may be slashed from their bodies. The graphical system simply needs to ask with info children on the agent's object, whether it has a head or not and can choose the appropriate graphical representation.

Simplified Syntax for Creating Nested Object Structures

To ease the generation of nested structures, one can use the predefined method contains. In essence, contains changes the namespace, where objects are created to the object, on which it is executed. In the example below, we create three nested rectangles, where two of these contain two more points. The outer rectangle is r0 containing rectangle r1 and r2.
  Class Point -parameter {{x 100} {y 300}}
  Class Rectangle -parameter {color}

  Rectangle r0 -color pink -contains {
    Rectangle r1 -color red -contains {
      Point x1 -x 1 -y 2
      Point x2 -x 1 -y 2
    }
    Rectangle r2 -color green -contains {
      Point x1
      Point x2
    }
  }

  ? {r0 color} pink
  ? {r0 r1 color} red
  ? {r0 r1 x1 x} 1
  ? {r0 r1 x2 y} 2
  ? {r0 r2 color} green

Every object in XOTcl is realized as a Tcl command. If nested objects are created, these commands are available as object specific methods. So, instead of calling the contained rectangle r1 via the fully qualfied name ::r0::r1, one can use r0 r1. This is exactly the same situation as it arises, when e.g. a global Tcl proc proc o1 {} {...} and an XOTcl object o1 (created via Object o1) is created. Both commands cannot coexist in the same namespace.

Copy/Move

Often an object has to be copied/moved. This is a very useful functionality when XOTcl should be used as a prototyping language. The XOTcl method move provides this functionality. Another common behavior is implemented by the copy method which clones the actual object to a destination object via a deep copy operation. The two methods have the syntax:
  objName move destination
  objName copy destination

Copy and move operations work with all object/class information, i.e., information on filters, mixins, parameters, etc. are automatically copied. Copy and move are integrated with class nesting and object aggregations. All copy/move operations are deep copy operations: all nested objects/classes are automatically copied/moved, too. E.g. if we want to reuse an imperial march object of star wars for star wars 2, we can just copy the object:

  starWars::imperialMarch copy starWars2::imperialMarch
Note that move is implemented in current versions of xotcl as a copy plus subsequent destroy operation.

Method Forwarding

As you have seen from many previous examples, XOTcl's primary command for method forwarding is the next primitive. next calls the same-named method of the current object, usually with the same argument list. However, frequently method forwarding is required between different objects as well, commonly referred to as delegation.

In general, delegation can be achieved in XOTcl without any special construct using simple methods containing a few lines. However, In several situations, forwarding is as well needed to plain Tcl commands, for example, if object oriented stubs are implemented on base of non-oo function calls. These functions might access instance variables of the objects. XOTcl uses this functionality in various situations, such as for instance in the implementation of the set, unset, append, array methods among others.

The fowarding functionality is suppored by XOTcl be the methods forward and instforward that address these requirements and provide an efficient implementation for these tasks.

The forwarding command specifies that whenever methodName is called, this invocation is delegated to callee, where the actual argument list (from the invocation) is appended to the argument list specified in the forwarding command. Like for procs and instprocs, we can distinguish between forward and instforward, depending on we want to the method available for a single object of for the instances of a class.

The general form of the forwarding commands is:

  obj  forward methodName ?options? callee ?arglist? 
  cls  instforward methodName ?options? callee ?arglist? 
where valid options are -objscope, -methodprefix, -earlybinding and -default. The option -objscope is used to specify that the command should be executed in the scope of the calling object (i.e. instance variables apprear as local varibales), -methodprefix means that the called method should be prefixed with the specified string (to avoid name clashes), -earlybinding means that the function pointer of the specified command (callee) is take at invocation time (should only be done for (builtin) commands inplemented in C), and -default provides a means for providing default methods when none are specifed.

Each of the arguments after the method name (including callee) can be be substituted an invocation time, or they are taken literally. The arguments to be substituted are starting always with a percent sign. These arguemnts can be %self, %proc, %1, %argclindex, or % followed by a Tcl command, and it can be prefixed with a positional prefix %@. We will introduce the usage of these options and argument substitutions based on examples.

In our first example we define an object dog and an object tail. If the dog receives the call wag it delegates this call to the tail and returns its result. In this introductory example, the method tail simply returns its arguments.

In this example, forwarding is achieved through the method forward that creates a forwarder command. This method receives as first argument the name, under which the forwarder is registered, followed by the object that receives the delegation (the "callee"), followed my the (optional) method name and optional arguments. More about this later. Here we register the forwarder under the name wag, the callee is tail, and the method is defined to have the name of the forwarder. We could have written here dog forward wag tail wag as well, be we use %proc which refers to the name of the forwarder. Using %proc is slightly more general in cases the forwarder is renamed.

  ###########################################
  # trivial object delegation
  ###########################################
  Object dog
  Object tail
  tail proc wag args { return $args }
  dog forward wag tail %proc

With these definitions a call to "dog wag 100" calls actually "tail wag 100" which returns the result of 100.

The following command shows the delegation to a Tcl command (instead of delegation to an object). We define a simple forwarder that forwards a call to the Tcl command expr with some arguments.

  ###########################################
  # adding 
  ###########################################
  Object obj
  obj forward addOne expr 1 +
The invocation obj addOne 5 returns 6 as value.

In our next example we want additionally that the Tcl command should to be evaluated in the context of the current object. This means that the method can easily access instance variables of the delegating object. We define a forwarder for the class X with the name Incr (to avoid confusion with the already defined method incr), we use the -objscope option and specify incr as the callee. Since the forwarder is defined via instforward the forwarder is available to all instances of the class.

  ###########################################
  # evaluating in scope 
  ###########################################
  Class X -parameter {{x 1}}
  X instforward Incr -objscope incr
  
  X x1 -x 100
  x1 Incr x
  x1 Incr x
  x1 Incr x
After the three calls to Incr the call x1 x returns the value 103.

In our next example, we show the usage of the %-substitution more advanced argument handling. This example sketches the implementation of the mixin add, mixin set methods as shown above. In order to obtain extensible subcommands (such as mixin add, mixin delete, etc.), we define an object for which the subcommands are defined as methods. We will use this object as callee for the appropriate methods. So, we define an object named mixin and define a forwarder with the name Mixin (again we capitalize Mixin to avoid name clashes with the already defined methodmixin ).

  ###########################################
  # mixin example
  ###########################################
  Object create mixin
  mixin proc unknown {m args} {return [concat [self] $m $args]}
  obj forward Mixin mixin %1 %self
We define here the method unknown to see what arguments are passed. The following invocation will lead to the call in noted in the comment.
  obj Mixin add M1       ;# calls ::mixin add ::obj M1
You see that %1 was substituted by the first argument of the invocation (here add) and %self was substituted by the name of the current object (here ::obj). The second argument of the invocation (here M1) was appended as usual. However, in calls like
  obj Mixin
we have to deal with cases, where the used argument (%1) is not given at the invocation. In this case we get either an error message, or we can specify a default argument via the option -default:
  obj forward Mixin -default {getter setter} mixin %1 %self
This definition means that if no argument is specified in the invocation we call the method getter, if one argument is given the method setter, in other cases we use the specified arguments. Therefore the following three invocations are delegated as indicated in the comments.
  obj Mixin              ;# calls ::mixin getter ::obj
  obj Mixin M1           ;# calls ::mixin setter ::obj M1
  obj Mixin add M1       ;# calls ::mixin add ::obj M1

When we implement subcommands by delegating to other commands (as shown in the last example), there can be situations where naming conflicts might arise. For example, if we want to implement a subcommand method class we might not want to implement a new method class on the callee, since this would overwrite the standard definition of class. To overcome such difficulties, we provide the option -methodprefix. The following example shows how to prefix every called method with the prefix @.

  ###########################################
  # sketching extensible info
  ###########################################
  Object Info
  Info proc @mixin {o} {
    $o info mixin
  }
  Info proc @class {o} { ;# without prefix, doing here a [Info class] would be wrong
    $o info class
  }
  Info proc @help {o} { ;# define a new subcommand for info
    foreach c [my info procs] {lappend result [string range $c 1 end]}
    return $result
  }
  Object instforward Info -methodprefix @ Info %1 %self 
With this definitions, the following call is rewritten as indicated in the comment.
  x1 Info class          ;# ::Info @class ::x1

When a forwarder is defined, the callee (the target command) can be omitted. When the callee is not specified, the method-name is used instead. When the method-name has a namespace prefix, the method name is the tail and the callee is the fully qualified name.

  ###########################################
  # optional callee
  ###########################################
  obj set x 2
  obj forward append -objscope
  Object n; Object n::x
  obj forward ::n::x
With this definitions of the forwarder append and x, the following calls are rewritten as indicated in the comment.
  obj append x y z        ;# ::append x y z ... returning  2yz
  obj x self              ;# ::n::x self    ... returning  ::n::x

The forwarder append forwards the call to the Tcl command append, which accesses the instance variable x and appends the specified values.

The list of tokens executed by the forwarder might contain Tcl commands executed during every invocations. This makes it for instance possible to pass instances variables to the callee. In the next example the object has the instvar named x which is multiplied by a factor of 10 when the method x* is invoked.

  ###########################################
  # command substitution
  ###########################################
  obj set x 10
  obj forward x* expr {%my set x} *
With this definitions, the following call is rewritten as indicated in the comment.
  obj x* 10               ;# expr 10 * 10 ... returning  100

In certain situations it is necessary to insert arguments always at the same position (e.g. at the second to last position). The positional addressing can be achieved by prefixing the arguments of the forward specification by %@POS , where POS is either a positive (argument positing from the beginning) or negative integer (argument counting from the end) or the constant end (denoting the last position). After POS a single space is used as a delimiter for the rest of the argument, which might be some other %-substitution or a constant. The positional arguments are evaluated from left to right and should be used in ascending order.

The following examples show a few usages of the positional arguments in the forwarder. The forwarders f1 to f5 are created, followed by one or more usages. The first argument of the usage is the call to to forewarder, the second argument is the result.

  ###########################################
  # forwarding with positional arguments
  ###########################################
  Object obj
  obj forward f1 list {%@end 13}
  ? {obj f1 1 2 3 } [list 1 2 3 13]

  obj forward f2 list {%@-1 13}
  ? {obj f2 1 2 3 } [list 1 2 13 3]

  obj forward f3 list {%@1 13}
  ? {obj f3 1 2 3 } [list 13 1 2 3]
  ? {obj f3} [list 13]

  obj forward f4 list {%@2 13}
  ? {obj f4 1 2 3 } [list 1 13 2 3]

  obj forward f5 {%@end 99} {%@0 list} 10
  ? {obj f5} [list 10 99]
  ? {obj f5 a b c} [list 10 a b c 99]

The construct %argclindex LIST can be used to substitute an argument depending on the number of arguments when the forwarder is invoked. For example, it is possible to call forward to a different method depending on how many arguments are specified. The number of arguments is used as an index in the specified list. When the number of arguments is larger than the number of elements in the specified list, an error is generated.

  ###############################################
  # substitution depending on number of arguments
  ###############################################
  obj forward f %self [list %argclindex [list a b c]]
  obj proc a args {return [list [self proc] $args]}
  obj proc b args {return [list [self proc] $args]}
  obj proc c args {return [list [self proc] $args]}
  ? {obj f} [list a {}]
  ? {obj f 1 } [list b 1]
  ? {obj f 1 2} [list c {1 2}]
  ? {catch {obj f 1 2 3}} 1

Finally, the concluding example defines a class chan to use the I/O-commands in an OO-manner. The proc open is used to create a chan instance. For the channel object we provide the method close (to close a channel and to destroy the channel object), puts (to write on a stream), blocked (to check whether last command exhausted all input), and fconfigure (to configure the stream). Note that for puts we specified that the actual stream should be inserted as the second to last argument.

  Class chan -parameter stream
  # create stream and object
  chan proc open args { 
    set stream [eval open $args]
    my create $stream -stream $stream  ;# make an object
  }
  # close stream and destroy object
  chan instproc close {} {
    close [my stream]
    [self] destroy
  }
  # handle other subcommands (methods) via unknown
  chan instproc unknown {m args} {
    set valid [lsort [chan info instcommands]]
    stderr puts "unknown chan method '$m' $args called; 
      	defined methods: $valid"
  }
  chan create stdout -stream stdout   ;# define standard stream
  chan create stderr -stream stderr   ;# define standard stream

  chan instforward puts puts {%@-1 %my stream}
  chan instforward blocked fblocked {%my stream}
  chan instforward fconfigure fconfigure {%my stream} 

  set c [chan open /tmp/junk w]
  $c puts -nonewline "hello"
  $c puts -nonewline " world"
  $c puts ""
  $c xxx                                       ;# trigger unknown
  # The stream instances denote the currently open streams
  stderr puts "currently open streams: [chan info instances]" 
  $c close
  stderr puts "currently open streams: [chan info instances]"

Assertions

In order to improve reliability and self documentation we added assertions to XOTcl. The implemented assertions are modeled after the ``design by contract'' concept of Bertrand Meyer. In XOTcl assertions can be specified in form of formal and informal pre- and post-conditions for each method. The conditions are defined as a list of and-combined constraints. The formal conditions have the form of normal Tcl conditions, while the informal conditions are defined as comments (specified with a starting ``#''). The lists containing the pre- and post-conditions are appended to the method definition (see example below).

Since XOTcl offers per-object specialization it is desirable to specify conditions within objects as well (this is different to the concept of Meyer). Furthermore there may be conditions which must be valid for the whole class or object at any visible state (that means in every pre- and post-condition). These are called invariants and may be defined with following syntax for class invariants:

  className instinvar invariantList

or for objects invariants:

  objName invar invariantList

Logically all invariants are appended to the pre- and post-conditions with a logical ``and''. All assertions can be introspected.

Since assertions are contracts they need not to be tested if one can be sure that the contracts are fulfilled by the partners. But for example when a component has changed or a new one is developed the assertions could be checked on demand. For this purpose the check method can be used either to test the pre- or the post-conditions. The syntax is:

  objName check ?all? ?instinvar? ?invar? ?pre? ?post?

Per default all options are turned off. check all turns all assertion options for an object on, an arbitrary list (maybe empty) can be used for the selection of certain options. Assertion options are introspected by the info check option. The following class is equipped with assertions:

  Class Sensor -parameter {{value 1}}
  Sensor instinvar {
    {[regexp {^[0-9]$} [my value]] == 1}
  }
  Sensor instproc incrValue {} {
    my incr value
  } {
    {# pre-condition:} 
    {[my value] > 0}
  } {
    {# post-condition:} 
    {[my value] > 1}
  }

The parameter instance method defines an instance variable value with value 1. The invariant expresses the condition (using the Tcl command regexp), that the value must be a single decimal digit. The method definition expresses the formal contract between the class and its clients that the method incrValue only gets input-states in which the value of the variable value is positive. If this contract is fulfilled by the client, the class commits itself to supply a post-condition where the variable's value is larger than 1. The formal conditions are ordinary Tcl conditions. If checking is turned on for sensor s:

  s check all

the pre-conditions and invariants are tested at the beginning and the post-condition and invariants are tested at the end of the method execution automatically. A broken assertion, like calling incrValue 9 times (would break the invariant of being a single digit) results in an error message.

In assertions we do not check methods that modify or introspect assertions. These are check,info,proc,instproc,invar, and instinvar. The reason for this is that we want to be able to recover a malicious action in a catch error handler, like:

  ...
  if {[catch {my assertionBreakingAction} errMsg]} {
    puts "CAUGHT ERROR: $errMsg"
    # remember checking options, for turning them on later again
    set check [my info check]
    my check {}
    # recover from broken assertion
    ...
    # turning checking on again 
    $fb check $check
  }

Meta-Data and Automatic Documentation

To enhance the understandability and the consistency between documentation and program it is useful to have a facility to make the documentation a part of the program. There are several kinds of meta-data which are interesting for a class, e.g. the author, a description, the version, etc.

Older versions of XOTcl have contained a special meta-data command metadata. This command is now (from version 0.83) deprecated and replaced by an integrated solution with XOTcl's API documentation functionality. The object @ is used for documentation and metadata issues. Per default it is not evaluated at all. Everything that is send to @ is simply ignored. That way we do not waste memory/performance at runtime, if we do not require to parse the metadata/documentation.

If we have to know the meta-data/documentation, as for instance in the xoDoc component and the makeDoc tool, that handle XOTcl's internal documentation, we have to re-define the documentation object. Alternatively, we can partially parse the source code for @ commands.

With @ the meta-data/documentation is handled by first class XOTcl objects. By defining alternate @ implementations - as in xoDoc/makeDoc - we can evaluate the meta-data/documentation arbitrarily. xoDoc/makeDoc are only an HTML back-end, but the basic idea is to provide support for several other usages as well (e.g. XML, RDF, on-line help, documentation of dynamic structures, etc).

The object@ handles comments via its unknown method. xoDoc adds the appropriate instprocs to t@ to produce HTML output. The appropriate command is:

  tclsh src/lib/makeDoc.xotcl DOCDIR DOCFILES

The source of a documentation is structurally very similar to the XOTcl constructs being commented. E.g. one can copy an instproc and add comments at the right places, like:

    Class C
    C instproc m {a1 a2} {
       return [expr {$a1+$a2}]
    }

can be commented as follows

    @ Class C { description { "my sample class"} }
    @ C instproc m {a1 "first number" a2 "second number"} {
       description "add two numbers"
       return "sum of a1 and a2"
    }

One can do essentially a copy+paste of the source and add the comments via attribute value pairs. Every basic language construct can have a "description". If you want to include other properties to the description, you can add them like:

    @ C instproc m {a1 "first number" a2 "second number"} {
       author "GN+UZ"
       date "Feb 31"
       description "add two numbers"
       return "sum of a1 and a2"
    }

This way, author and date are added automatically to the generated HTML file. In addition, there is a @File hook for a per file description, like:

@ @File {
  description {
    This is a file which provides a regression test
    for the features of the XOTcl - Language. 
  }
}

Additional Functionalities

Abstract Classes

In XOTcl a class is defined abstract if at least one method of this class is abstract. The instance method abstract defines an abstract method and specifies its interface. Direct calls to abstract methods produce an error message. E.g. a Storage class provides an abstract interface for access to different storage forms:

  Class Storage
  Storage abstract instproc open  {name}       
  Storage abstract instproc store {key value}
  Storage abstract instproc list  {}         
  Storage abstract instproc fetch key        
  Storage abstract instproc close {}         
  Storage abstract instproc delete {k} 

All kinds of storage have to implement every method from the interface. E.g. a GNU Database Access, a relational database access, and several other storage forms may be derived by sub-classing (therefore, all conform to the same storage access interface).

Checking Commands for being Objects, Classes, or Meta-Classes

Since XOTcl is a hybrid language containing several Tcl commands, sometimes its necessary for applications to distinguish between Tcl commands and object commands for XOTcl. method of the Object class looks up an objName and returns 1 if it is an object and 0 if not:

  objName1 isobject objName2

If one can be sure that a command represents an object, it might be unsure if the command is only an object or also class or even meta-class. The two instance methods isclass and ismetaclass check in the same manner, whether a class or meta-class is given (since ever XOTcl class is an object, they also return 0, when objName is not an XOTcl object).

  objName1 isclass objName2
  objName1 ismetaclass objName2

Exit Handler

A task for a programming language, sometimes of similar importance as object creation, is the object destruction. XOTcl ensures that all objects are destroyed and their destructors are invoked when XOTcl applications terminate. For that reason objects and classes are destroyed in the order objects, classes, meta-classes. Sometimes further destruction order is of importance. For these cases, the XOTcl language provides an exit handler, which is a user-defined proc, which invokes user-defined exit handling just before the destruction of objects, classes, meta-classes is invoked. For instance, the exit handler lets the user specify objects which have to be destroyed before all other objects.

The exit handler is defined as a proc of Object, which is per default empty:

  ::xotcl::Object proc __exitHandler {} {
    # clients should append exit handlers to this proc body
    ;
  }

There are some procs of the Object class pre-defined, which let us specify an exit handler conveniently:

   Object setExitHandler body
   Object getExitHandler
   Object unsetExitHandler

setExitHandler lets us specify a proc body that actually contains the user-defined exit handling:

   Object setExitHandler {
     aObj destroy
     puts "exiting"
   }

destroys the object aObj before all other objects and prints the message existing to the screen. With getExitHandler the exit handler can be introspected. E.g. if we just want to append the destruction of object bObj to an existing exit handler, we use getExitHandler:

   Object setExitHandler "[Object getExitHandler]; bObj destroy"

unsetExitHandler deletes the exit handler.


Automatic Name Creation

The XOTcl autoname instance method provides an simple way to take the task of automatically creating names out of the responsibility of the programmer. The example below shows how to create on each invocation of method new an agent with a fresh name (prefixed with agent):

  Agent proc new args {
    eval my [my autoname agent] $args
  }

Autonames may have format strings as in the Tcl 'format' command. E.g.:

  objName autoname a%06d

produces

  a000000, a000001, a000002, ...

Integrating XOTcl Programs with C Extensions (such as TK)

Because all XOTcl commands are in the ::xotcl namespace, it is usually no problem to integrate XOTcl with other Tcl extensions. Most often it works to import the XOTcl commands (like Object, Class) into the current namespace because there are no name-clashes with the commands defined by other extensions.

Consider you want to perform a deeper integration of an other extension and XOTcl because you want to benefit from XOTcl's object system. For instance, you might want to introduce composite TK widgets (sometimes called mega-widgets) as classes and inherit from these classes. Here, you have two options: you can change or extend the C code of that other extension to provide XOTcl classes or objects, or you can write an XOTcl wrapper in Tcl. For the first alternative, there are some examples provided in the XOTcl distribution. XOTclGdbm provides an OO Tcl interface to the GDBM database, for instance. XOTclSdbm does the same for SDBM, and the TclExpat wrapper provides a class-based interface to the TclExpat XML parser.

Consider you do not want to change the C code of a Tcl extension. Then you can write an OO wrapper in XOTcl for the commands of the other extension. For stateless commands, you can simply write forwarder methods. If the extension maintains some state, you typically associate the state handle with an XOTcl parameter, acquire the state in the XOTcl constructor, and align the XOTcl destructor with the stateful instance.

Consider you want to wrap the Tk button widget. You can acquire the widget in the constructor, and maintain the widget ID in a parameter. You now can forward invocations to this widget ID (e.g. when using "pack"), or register command callbacks (like buttonPressed). Note that we let the "self" command be replaced in the scope of the current method so that TK receives the correct object ID for the callback. In the destructor we destroy the widget as well (we use "catch" because sometimes widgets can destroyed by other means as well (e.g. by their parent widget, when a widget/object hierarchy is destroyed at once).

  Class MyButton -parameter {button}
  MyButton instproc buttonPressed args {
    puts "pressed [my button]"
  }
  MyButton instproc init args {
    set ID [namespace tail [self]]
    my instvar button
    set button [button .$ID \
      -text "My Button $ID" \
      -command [list [self] buttonPressed]] 
    pack $button
    next
  }
  MyButton instproc destroy args {
     catch {destroy [my button]}
     next
  }

  # a test -> 3 buttons, destroy one of them
  foreach b {a b c} {
    MyButton $b
  }
  b destroy

The "trick" to substitute "self" within the current method scope works for all kinds of command callbacks. Extensions such as TK, however, often work with bindings to (global) variables as well. Using global variables is frowned upon in the OO community. Instead you should use instance variables of objects. As Tcl can only bind to existing namespace variables (and XOTcl acquires the namespace of an object on demand), you have to make sure that the namespace of an object exists before binding a variable. That can be done with "requireNamespace":

  GUIClass instproc buildEntry win {
    my requireNamespace
    entry $win -textvariable [self]::entryValue
    my set entryValue {Init Value}
  }

Note that in the above example we have used to tail of the object ID as ID for the widget. Usually, it is a good idea to the object name, if possible, for TK (and other extensions) IDs as well. Another option is to use a autoname to get a unique name for the ID.

Sometimes you want to simply send all invocations, not implemented by XOTcl, to the wrapped command. Here, it is tedious to write a wrapper for each of these methods. Instead you can use "unknown" to handle automatic forwarding. Consider you want to wrap TK commands like pack and replace XOTcl object names with their TK widget ID, so that you can use both IDs synonymously. You can rename the respective TK commands in the following way:

  foreach tkCommand {bell bind bindtags clipboard event 
    focus font grid image lower option pack place raise 
    selection send tk tkwait winfo wm} { 
    rename ::$tkCommand __tk_$tkCommand
    TkCommand ::$tkCommand
    ::$tkCommand set wrapped __tk_$tkCommand
  }

The XOTcl class handling the ID substitution for the TK command might look as follows:

  Class TkCommand -parameter wrapped
  TkCommand instproc unknown args {
      my instvar wrapped
      set args [Widget replaceWithWidgetIDs $args]
      # now call the command
      eval $wrapped $args
  }

References

[Zdun, Strembeck, Neumann 2007] U. Zdun, M. Strembeck, G. Neumann: Object-Based and Class-Based Composition of Transitive Mixins, Information and Software Technology, 49(8) 2007 .

[Neumann and Zdun 1999a] G. Neumann and U. Zdun. Filters as a language support for design patterns in object-oriented scripting languages. In Proceedings of COOTS'99, 5th Conference on Object-Oriented Technologies and Systems, San Diego, May 1999.

[Neumann and Zdun 1999b] G. Neumann and U. Zdun. Implementing object-specific design patterns using per-object mixins. In Proc. of NOSA`99, Second Nordic Workshop on Software Architecture, Ronneby, Sweden, August 1999.

[Neumann and Zdun 1999c] G. Neumann and U. Zdun. Enhancing object-based system composition through per-object mixins. In Proceedings of Asia-Pacific Software Engineering Conference (APSEC), Takamatsu, Japan, December 1999.

[Neumann and Zdun 2000a] G. Neumann and U. Zdun. XOTCL, an object-oriented scripting language. In Proceedings of Tcl2k: The 7th USENIX Tcl/Tk Conference, Austin, Texas, February 2000.

[Neumann and Zdun 2000b] G. Neumann and U. Zdun. Towards the Usage of Dynamic Object Aggregations as a Form of Composition In: Proceedings of Symposium of Applied Computing (SAC'00), Como, Italy, Mar 19-21, 2000.

[Neumann and Sobernig 2009] G. Neumann, S. Sobernig: XOTcl 2.0 - A Ten-Year Retrospective and Outlook, in: Proceedings of the Sixteenth Annual Tcl/Tk Conference, Portland, Oregon, October, 2009.

[Ousterhout 1990] J. K. Ousterhout. Tcl: An embeddable command language. In Proc. of the 1990 Winter USENIX Conference, January 1990.

[Ousterhout 1998] J. K. Ousterhout. Scripting: Higher Level Programming for the 21st Century, IEEE Computer 31(3), March 1998.

[Wetherall and Lindblad 1995] D. Wetherall and C. J. Lindblad. Extending Tcl for Dynamic Object-Oriented Programming. Proc. of the Tcl/Tk Workshop '95, July 1995. doc/variable.man.inc000066400000000000000000000100571242365656200146730ustar00rootroot00000000000000[comment {-*- tcl -*- manpage fragment for variable method, shared by nx::Object and nx::Class}] [keywords "variable"] [comment { At the time of writing, the only material difference between property and variable is a different -configurable default. }] Defines a [term variable] for the scope of the [vset SCOPE]. The [arg spec] provides the [term variable] specification: [arg variableName][opt "[const :][arg typeSpec]"]. The [arg variableName] will be used to name the underlying Tcl variable and the getter/setter methods, if requested (see [option "-accessor"]). [arg spec] is optionally equipped with a [arg typeSpec] following a colon delimiter which specifies a [term "value checker"] for the values managed by the [term variable]. Optionally, a [emph defaultValue] can be defined. [para] If [option "-accessor"] is set explicitly, a [term variable] will provide for a pair of getter and setter methods: [list_begin definitions] [def "[arg obj] [arg variableName] [method set] [arg varValue]"] Sets [arg variableName] to [arg varValue]. [def "[arg obj] [arg variableName] [method get]"] Returns the current value of [arg variableName]. [def "[arg obj] [arg variableName] [method unset]"] Removes [arg variableName], if existing, underlying the property. [list_end] The option value passed along [option "-accessor"] sets the level of [term "call protection"] for the getter and setter methods: [const public], [const protected], or [const private]. By default, no getter and setter methods are created. [para] Turning on the [term switch] [option -incremental] provides a refined setter interface to the value managed by the [term variable]. First, setting [option -incremental] implies requesting [option -accessor] ([const public] by default, if not specified explicitly). Second, the managed value will be considered a valid Tcl list. A [term multiplicity] of [const 1..*] is set by default, if not specified explicitly as part of [arg spec] (see above). Third, to manage this list value element-wise ([emph incrementally]), two additional setter operations become available: [list_begin definitions] [def "[arg obj] [arg variableName] [method add] [arg element] [opt [arg index]]"] Adding [arg element] to the managed list value, at the list position given by [arg index] (by default: 0). [def "[arg obj] [arg variableName] [method delete] [arg elementPattern]"] Removing one or multiple elements from the managed list value which match [arg elementPattern]. [arg elementPattern] can contain matching characters (see [cmd "string match"]). [list_end] [para] By setting [option -configurable] to [const true], the [term variable] can be accessed and modified via [method cget] and [method configure], respectively. If [const false] (the default), the interface based on [method cget] and [method configure] will not become available. In this case, and provided that [option -accessor] is set, the [term variable] can be accessed and modified via the getter/setter methods. Alternatively, the underlying Tcl variable, which is represented by the [term variable], can always be accessed and modified directly, e.g., using [method eval]. By default, [option -configurable] is [const false]. [para] A [term variable] becomes implemented by a [term "slot object"] under any of the following conditions: [list_begin itemized] [item] [option -configurable] equals [const true]. [item] [option -accessor] is one of [const public], [const protected], or [const private]. [item] [option -incremental] is turned on. [item] [option -initblock] is a non-empty string. [list_end] Provided a [term "slot object"] managing the [term variable] is to be created, a custom class [arg className] from which this [term "slot object"] is to be instantiated can be set using [option -class]. The default value is [cmd ::nx::VariableSlot]. [para] Using [option -initblock], an optional Tcl [arg script] can be defined which becomes passed into the initialization procedure (see [method configure]) of the [term variable]'s [term "slot object"]. See also [sectref description "[arg initBlock] for [method create] and [method new]"]. doc/xotclsh.1000066400000000000000000000144011242365656200134040ustar00rootroot00000000000000'\" '\" Generated from file 'xotclsh.man' by tcllib/doctools with format 'nroff' '\" Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). '\" '\" The definitions below are for supplemental macros used in Tcl/Tk '\" manual entries. '\" '\" .AP type name in/out ?indent? '\" Start paragraph describing an argument to a library procedure. '\" type is type of argument (int, etc.), in/out is either "in", "out", '\" or "in/out" to describe whether procedure reads or modifies arg, '\" and indent is equivalent to second arg of .IP (shouldn't ever be '\" needed; use .AS below instead) '\" '\" .AS ?type? ?name? '\" Give maximum sizes of arguments for setting tab stops. Type and '\" name are examples of largest possible arguments that will be passed '\" to .AP later. If args are omitted, default tab stops are used. '\" '\" .BS '\" Start box enclosure. From here until next .BE, everything will be '\" enclosed in one large box. '\" '\" .BE '\" End of box enclosure. '\" '\" .CS '\" Begin code excerpt. '\" '\" .CE '\" End code excerpt. '\" '\" .VS ?version? ?br? '\" Begin vertical sidebar, for use in marking newly-changed parts '\" of man pages. The first argument is ignored and used for recording '\" the version when the .VS was added, so that the sidebars can be '\" found and removed when they reach a certain age. If another argument '\" is present, then a line break is forced before starting the sidebar. '\" '\" .VE '\" End of vertical sidebar. '\" '\" .DS '\" Begin an indented unfilled display. '\" '\" .DE '\" End of indented unfilled display. '\" '\" .SO '\" Start of list of standard options for a Tk widget. The '\" options follow on successive lines, in four columns separated '\" by tabs. '\" '\" .SE '\" End of list of standard options for a Tk widget. '\" '\" .OP cmdName dbName dbClass '\" Start of description of a specific option. cmdName gives the '\" option's name as specified in the class command, dbName gives '\" the option's name in the option database, and dbClass gives '\" the option's class in the option database. '\" '\" .UL arg1 arg2 '\" Print arg1 underlined, then print arg2 normally. '\" '\" RCS: @(#) $Id: man.macros,v 1.1 2009/01/30 04:56:47 andreas_kupries Exp $ '\" '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. .if t .wh -1.3i ^B .nr ^l \n(.l .ad b '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ta \\n()Au \\n()Bu .ie !"\\$3"" \{\ \&\\$1 \\fI\\$2\\fP (\\$3) .\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n .\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. .AS Tcl_Interp Tcl_CreateInterp in/out '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ .\" Draw four-sided box normally, but don't draw top of .\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .if !"\\$2"" .br .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ .\" Draw three-sided box if this is the box's first page, .\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .nf .sp .. '\" # DE - end display .de DE .fi .RE .sp .. '\" # SO - start of list of standard options .de SO .SH "STANDARD OPTIONS" .LP .nf .ta 4c 8c 12c .ft B .. '\" # SE - end of list of standard options .de SE .fi .ft R .LP See the \\fBoptions\\fR manual entry for details on the standard options. .. '\" # OP - start of full description for a single option .de OP .LP .nf .ta 4c Command-Line Name: \\fB\\$1\\fR Database Name: \\fB\\$2\\fR Database Class: \\fB\\$3\\fR .fi .. '\" # CS - begin code excerpt .de CS .RS .nf .ta .25i .5i .75i 1i .. '\" # CE - end code excerpt .de CE .fi .RE .. .de UL \\$1\l'|0\(ul'\\$2 .. .TH "xotclsh" 1 2.0 xotclsh "" .BS .SH NAME xotclsh \- Simple shell containing NSF/XOTcl2 interpreter .SH SYNOPSIS \fBxotclsh\fR ?\fIfileName\fR? .sp .BE .SH DESCRIPTION .TP \fBxotclsh\fR ?\fIfileName\fR? \fBxotclsh\fR is a shell-like application that reads XOTcl2 and Tcl commands from its standard input or from \fIfileName\fR and evaluates them. If invoked without \fIfileName\fR, then it runs in REPL mode, reading commands from standard input and printing command results and error messages to standard output. It runs until the exit command is invoked or until it reaches end-of-file on its standard input. .sp \fBxotclsh\fR can be used like \fBtclsh\fR to make XOTcl2 scripts directly invokable from the shell, by providing the following first line ("shebang") in the respective script: .CS #! /usr/bin/env xotclsh .CE .IP A (more portable) alternative is: .CS #! /bin/sh # the next line restarts using xotclsh \\ exec xotclsh "$0" "$@" .CE .PP .SH COPYRIGHT .nf Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). .fidoc/xotclsh.man000066400000000000000000000023341242365656200140210ustar00rootroot00000000000000[comment {-*- tcl -*- xotclsh manpage}] [manpage_begin xotclsh 1 2.0.0] [copyright {2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT).}] [titledesc {Simple shell containing NSF/XOTcl2 interpreter}] [description] [list_begin definitions] [call [syscmd "xotclsh"] [opt [arg fileName]]] [syscmd "xotclsh"] is a shell-like application that reads XOTcl2 and Tcl commands from its standard input or from [arg fileName] and evaluates them. If invoked without [arg fileName], then it runs in REPL mode, reading commands from standard input and printing command results and error messages to standard output. It runs until the exit command is invoked or until it reaches end-of-file on its standard input. [para] [syscmd "xotclsh"] can be used like [syscmd "tclsh"] to make XOTcl2 scripts directly invokable from the shell, by providing the following first line ("shebang") in the respective script: [example { #! /usr/bin/env xotclsh }] A (more portable) alternative is: [example_begin] #! /bin/sh # the next line restarts using xotclsh \ exec xotclsh "$0" "$@" [example_end] [list_end] [manpage_end] doc/xowish.1000066400000000000000000000144631242365656200132510ustar00rootroot00000000000000'\" '\" Generated from file 'xowish.man' by tcllib/doctools with format 'nroff' '\" Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). '\" '\" The definitions below are for supplemental macros used in Tcl/Tk '\" manual entries. '\" '\" .AP type name in/out ?indent? '\" Start paragraph describing an argument to a library procedure. '\" type is type of argument (int, etc.), in/out is either "in", "out", '\" or "in/out" to describe whether procedure reads or modifies arg, '\" and indent is equivalent to second arg of .IP (shouldn't ever be '\" needed; use .AS below instead) '\" '\" .AS ?type? ?name? '\" Give maximum sizes of arguments for setting tab stops. Type and '\" name are examples of largest possible arguments that will be passed '\" to .AP later. If args are omitted, default tab stops are used. '\" '\" .BS '\" Start box enclosure. From here until next .BE, everything will be '\" enclosed in one large box. '\" '\" .BE '\" End of box enclosure. '\" '\" .CS '\" Begin code excerpt. '\" '\" .CE '\" End code excerpt. '\" '\" .VS ?version? ?br? '\" Begin vertical sidebar, for use in marking newly-changed parts '\" of man pages. The first argument is ignored and used for recording '\" the version when the .VS was added, so that the sidebars can be '\" found and removed when they reach a certain age. If another argument '\" is present, then a line break is forced before starting the sidebar. '\" '\" .VE '\" End of vertical sidebar. '\" '\" .DS '\" Begin an indented unfilled display. '\" '\" .DE '\" End of indented unfilled display. '\" '\" .SO '\" Start of list of standard options for a Tk widget. The '\" options follow on successive lines, in four columns separated '\" by tabs. '\" '\" .SE '\" End of list of standard options for a Tk widget. '\" '\" .OP cmdName dbName dbClass '\" Start of description of a specific option. cmdName gives the '\" option's name as specified in the class command, dbName gives '\" the option's name in the option database, and dbClass gives '\" the option's class in the option database. '\" '\" .UL arg1 arg2 '\" Print arg1 underlined, then print arg2 normally. '\" '\" RCS: @(#) $Id: man.macros,v 1.1 2009/01/30 04:56:47 andreas_kupries Exp $ '\" '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. .if t .wh -1.3i ^B .nr ^l \n(.l .ad b '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ta \\n()Au \\n()Bu .ie !"\\$3"" \{\ \&\\$1 \\fI\\$2\\fP (\\$3) .\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n .\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. .AS Tcl_Interp Tcl_CreateInterp in/out '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ .\" Draw four-sided box normally, but don't draw top of .\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .if !"\\$2"" .br .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ .\" Draw three-sided box if this is the box's first page, .\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .nf .sp .. '\" # DE - end display .de DE .fi .RE .sp .. '\" # SO - start of list of standard options .de SO .SH "STANDARD OPTIONS" .LP .nf .ta 4c 8c 12c .ft B .. '\" # SE - end of list of standard options .de SE .fi .ft R .LP See the \\fBoptions\\fR manual entry for details on the standard options. .. '\" # OP - start of full description for a single option .de OP .LP .nf .ta 4c Command-Line Name: \\fB\\$1\\fR Database Name: \\fB\\$2\\fR Database Class: \\fB\\$3\\fR .fi .. '\" # CS - begin code excerpt .de CS .RS .nf .ta .25i .5i .75i 1i .. '\" # CE - end code excerpt .de CE .fi .RE .. .de UL \\$1\l'|0\(ul'\\$2 .. .TH "xowish" 1 2.0 xowish "" .BS .SH NAME xowish \- Simple windowing shell containing NSF/XOTcl2 interpreter .SH SYNOPSIS \fBxowish\fR ?\fIfileName\fR? .sp .BE .SH DESCRIPTION .TP \fBxowish\fR ?\fIfileName\fR? \fBxowish\fR is a shell-like application including Tcl and the XOTcl2 as well as the Tk toolkit. \fBxowish\fR creates a main window and, then, reads commands from its standard input or from \fIfileName\fR and evaluates them. If invoked without \fIfileName\fR, then it runs in REPL mode, reading commands from standard input. \fBxowish\fR will continue processing commands until all windows have been deleted or until end-of-file is reached on standard input. .sp \fBxowish\fR can be used like \fBwish\fR to make XOTcl2 scripts directly invokable from the shell, by providing the following first line ("shebang") in the respective script: .CS #! /usr/bin/env xowish .CE .IP A (more portable) alternative is: .CS #! /bin/sh # the next line restarts using xowish \\ exec xowish "$0" "$@" .CE .PP .SH COPYRIGHT .nf Copyright (c) 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT). .fidoc/xowish.man000066400000000000000000000024331242365656200136560ustar00rootroot00000000000000[comment {-*- tcl -*- xowish manpage}] [manpage_begin xowish 1 2.0.0] [copyright { 2014 Stefan Sobernig , Gustaf Neumann ; available under the Creative Commons Attribution 3.0 Austria license (CC BY 3.0 AT).}] [titledesc {Simple windowing shell containing NSF/XOTcl2 interpreter}] [description] [list_begin definitions] [call [syscmd "xowish"] [opt [arg fileName]]] [syscmd "xowish"] is a shell-like application including Tcl and the XOTcl2 as well as the Tk toolkit. [syscmd "xowish"] creates a main window and, then, reads commands from its standard input or from [arg fileName] and evaluates them. If invoked without [arg fileName], then it runs in REPL mode, reading commands from standard input. [syscmd "xowish"] will continue processing commands until all windows have been deleted or until end-of-file is reached on standard input. [para] [syscmd "xowish"] can be used like [syscmd "wish"] to make XOTcl2 scripts directly invokable from the shell, by providing the following first line ("shebang") in the respective script: [example { #! /usr/bin/env xowish }] A (more portable) alternative is: [example_begin] #! /bin/sh # the next line restarts using xowish \ exec xowish "$0" "$@" [example_end] [list_end] [manpage_end] dtrace/000077500000000000000000000000001242365656200123335ustar00rootroot00000000000000dtrace/README000066400000000000000000000140201242365656200132100ustar00rootroot00000000000000DTrace provider for the Next Scripting Language This is an implementation of a DTrace provider for the Next Scripting Language (nsf). The nsf provider is designed to be used with and without the DTrace support for Tcl. Therefore, nsf can be configured with --with-dtrace also in cases where Tcl was compiled without it. To enable DTrace, run configure with the flag --enable-dtrace. The DTrace support for nsf was developed under Mac OS X, other platforms might require some fine tuning to get it running. Please report improvements back to the nsf developers. Once DTrace support is compiled into nsf, one can run D scripts like in the following example: sudo dtrace -q -F -s dtrace/timestamps.d -c "./nxsh tests/object-system.test" DTrace requires normally that dtrace is run with root permissions. In case the provided sample scrips in the dtrace directory don't work the following hints might help: * Make sure that a "package require nx" works for root as well (install nx, or provide a TCLLIBPATH, etc.). You might want to add e.g. the following line set auto_path [concat . $auto_path] to the begin of the nxsh script, or add the path to the dtrace invocation (see below) * If dtrace compliation fails (e.g. "... nsf*:::method-entry does not match any probes"), start an nxsh in a different window to make the nsf provider and the probes known to the kernel. -gustaf neumann Examples % sudo TCLLIBPATH=. dtrace -arch x86_64 -x bufsize=20m -F -s dtrace/execution-flow.d -c "./nxsh dtrace/sample.tcl" dtrace: script 'dtrace/execution-flow.d' matched 8 probes dtrace: pid 65393 has exited CPU FUNCTION 0 -> MethodDispatchCsc ::nx::Object ::nx::Class.create (2) 0 -> MethodDispatchCsc ::o ::nx::Object.public (4) 0 -> MethodDispatchCsc ::o ::nx::Object.method (3) 0 -> MethodDispatchCsc ::o ::nx::Object.__resolve_method_path (2) 0 <- ObjectDispatch ::o ::nx::Object.__resolve_method_path -> 0 0 -> MethodDispatchCsc ::o ::nx::Object.__default_method_call_protection (0) 0 <- ObjectDispatch ::o ::nx::Object.__default_method_call_protection -> 0 0 <- ObjectDispatch ::o ::nx::Object.method -> 0 0 <- ObjectDispatch ::o ::nx::Object.public -> 0 0 -> MethodDispatchCsc ::o ::nx::Object.init (0) 0 <- ObjectDispatch ::o ::nx::Object.init -> 0 0 <- ObjectDispatch ::nx::Object ::nx::Class.create -> 0 0 -> MethodDispatchCsc ::o ::o.foo (2) 0 -> MethodDispatchCsc ::o ::o.::incr (2) 0 <- ObjectDispatch ::o ::o.::incr -> 0 0 <- ObjectDispatch ::o ::o.foo -> 0 % sudo TCLLIBPATH=. dtrace -arch x86_64 -x bufsize=20m -F -s dtrace/execution-flow-args.d -c "./nxsh dtrace/sample.tcl" dtrace: script 'dtrace/execution-flow-args.d' matched 8 probes dtrace: pid 65419 has exited CPU FUNCTION 1 -> MethodDispatchCsc ::nx::Object ::nx::Class.create (2) o :public method foo {x y} { [self] ::incr x 1 -> MethodDispatchCsc ::o ::nx::Object.public (4) method foo 1 -> MethodDispatchCsc ::o ::nx::Object.method (3) foo x y 1 -> MethodDispatchCsc ::o ::nx::Object.__resolve_method_path (2) -per-object foo 1 <- ObjectDispatch ::o ::nx::Object.__resolve_method_path -> 0 1 -> MethodDispatchCsc ::o ::nx::Object.__default_method_call_protection (0) 1 <- ObjectDispatch ::o ::nx::Object.__default_method_call_protection -> 0 1 <- ObjectDispatch ::o ::nx::Object.method -> 0 1 <- ObjectDispatch ::o ::nx::Object.public -> 0 1 -> MethodDispatchCsc ::o ::nx::Object.init (0) 1 <- ObjectDispatch ::o ::nx::Object.init -> 0 1 <- ObjectDispatch ::nx::Object ::nx::Class.create -> 0 1 -> MethodDispatchCsc ::o ::o.foo (2) 1 2 1 -> MethodDispatchCsc ::o ::o.::incr (2) x 1 1 <- ObjectDispatch ::o ::o.::incr -> 0 1 <- ObjectDispatch ::o ::o.foo -> 0 % sudo TCLLIBPATH=. dtrace F -s dtrace/timestamps.d -c "./nxsh tests/object-system.test" CPU FUNCTION 0 | :END ::C ::C class = 4249 ::M ::M class = 4347 ::o ::o class = 4435 ::m2 ::m2 class = 4444 ::M2 ::M2 class = 4462 ::C0 ::C0 class = 4501 ::c1 ::c1 class = 4611 .... % sudo TCLLIBPATH=. dtrace -F -s dtrace/timestamps-q.d -c "./nxsh tests/object-system.test" .... ::nx::Object info value ------------- Distribution ------------- count 4096 | 0 8192 |@@@@@@@@@@@@@@@@@@@@@@@@@ 16 16384 |@@@@@@@@@@@@@@@ 10 32768 | 0 ::nx::Object objectparameter value ------------- Distribution ------------- count 32768 | 0 65536 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 4 131072 |@@@@@@@@ 1 262144 | 0 dtrace/execution-flow-args.d000066400000000000000000000053061242365656200164060ustar00rootroot00000000000000/* -*- D -*- * * Execution flow trace with arguments * * Activate tracing between * ::nsf::configure dtrace on * and * ::nsf::configure dtrace off * * Since this D script accesses the C data structures it is sensitive * to the representation sizes of the data structures (e.g. pointers). * Make sure to call the script with the appropriate architecture flag * on Mac OS X; on SunOS, there is apparently a -32 or -64 flag. * * Example: * * sudo dtrace -arch x86_64 -x bufsize=20m -F -s dtrace/execution-flow-args.d \ * -c "./nxsh dtrace/sample.tcl" * * -gustaf neumann */ enum {maxstrlen = 50}; /* * Needed data structures to access the content of Tcl_Objs. */ typedef struct Tcl_Obj Tcl_Obj; typedef struct Tcl_ObjType { char *name; void *freeIntRepProc; void *dupIntRepProc; void *updateStringProc; void *setFromAnyProc; } Tcl_ObjType; struct Tcl_Obj { int refCount; char *bytes; int length; Tcl_ObjType *typePtr; union { long longValue; double doubleValue; void *otherValuePtr; int64_t wideValue; struct { void *ptr1; void *ptr2; } twoPtrValue; struct { void *ptr; unsigned long value; } ptrAndLongRep; } internalRep; }; /* * Handling "nsf::configure dtrace on|off". */ nsf*:::configure-probe /!self->tracing && copyinstr(arg0) == "dtrace" / { self->tracing = (arg1 && copyinstr(arg1) == "on") ? 1 : 0; } nsf*:::configure-probe /self->tracing && copyinstr(arg0) == "dtrace" / { self->tracing = (arg1 && copyinstr(arg1) == "off") ? 0 : 1; } /* * Output object, class, method, number of arguments and first two * arguments upon method invocation. */ nsf*:::method-entry /self->tracing/ { this->objv = arg3 ? ((Tcl_Obj**)copyin((user_addr_t)((Tcl_Obj**)arg4), sizeof(Tcl_Obj*) * arg3)) : NULL; this->i = 0; this->o = arg3 > this->i && *(this->objv + this->i) ? (Tcl_Obj*)copyin((user_addr_t)*(this->objv + this->i), sizeof(Tcl_Obj)) : NULL; this->s0 = this->o ? (this->o->bytes ? copyinstr((user_addr_t)this->o->bytes, maxstrlen) : lltostr(this->o->internalRep.longValue)) : ""; this->i = 1; this->o = arg3 > this->i && *(this->objv + this->i) ? (Tcl_Obj*)copyin((user_addr_t)*(this->objv + this->i), sizeof(Tcl_Obj)) : NULL; this->s1 = this->o ? (this->o->bytes ? copyinstr((user_addr_t)this->o->bytes, maxstrlen) : lltostr(this->o->internalRep.longValue)) : ""; printf("%s %s.%s (%d) %s %s", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3, this->s0, this->s1); } /* * Output object, class, method and return code upon method return. */ nsf*:::method-return /self->tracing/ { printf("%s %s.%s -> %d", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); } dtrace/execution-flow.d000066400000000000000000000015561242365656200154570ustar00rootroot00000000000000/* -*- D -*- * * Execution flow trace without arguments * * Activate tracing between * ::nsf::configure dtrace on * and * ::nsf::configure dtrace off * */ nsf*:::configure-probe /!self->tracing && copyinstr(arg0) == "dtrace" / { self->tracing = (arg1 && copyinstr(arg1) == "on") ? 1 : 0; } nsf*:::configure-probe /self->tracing && copyinstr(arg0) == "dtrace" / { self->tracing = (arg1 && copyinstr(arg1) == "off") ? 0 : 1; } /* * Output object, class, method and number of arguments upon method * invocation. */ nsf*:::method-entry /self->tracing/ { printf("%s %s.%s (%d)", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); } /* * Output object, class, method and return code upon method return. */ nsf*:::method-return /self->tracing/ { printf("%s %s.%s -> %d", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); } dtrace/object-create.d000066400000000000000000000002461242365656200152110ustar00rootroot00000000000000/* -*- D -*- * * check, if every object is freed. */ nsf*:::object-alloc { @[copyinstr(arg0)] = sum(1); } nsf*:::object-free { @[copyinstr(arg0)] = sum(-1); } dtrace/sample.tcl000066400000000000000000000003111242365656200143130ustar00rootroot00000000000000package require nx ::nsf::configure dtrace on nx::Object create o { :public object method foo {x y} { [self] ::incr x 1 return [expr {$x + $y}] } } o foo 1 2 ::nsf::configure dtrace off dtrace/timestamps-q.d000066400000000000000000000014711242365656200151270ustar00rootroot00000000000000/* -*- D -*- * * Quantize time between method-entry and method-returns for calls on ::nx::Object * * Activate tracing between * ::nsf::configure dtrace on * and * ::nsf::configure dtrace off * */ nsf*:::configure-probe /!self->tracing && copyinstr(arg0) == "dtrace" / { self->tracing = (arg1 && copyinstr(arg1) == "on") ? 1 : 0; } nsf*:::configure-probe /self->tracing && copyinstr(arg0) == "dtrace" / { self->tracing = (arg1 && copyinstr(arg1) == "off") ? 0 : 1; } /* * Measure time differences */ nsf*:::method-entry /self->tracing && copyinstr(arg1) == "::nx::Object"/ { self->start = timestamp; } nsf*:::method-return /self->tracing && copyinstr(arg1) == "::nx::Object" && self->start/ { @[copyinstr(arg1), copyinstr(arg2)] = quantize(timestamp - self->start); self->start = 0; } dtrace/timestamps.d000066400000000000000000000015321242365656200146670ustar00rootroot00000000000000/* -*- D -*- * * Measure time between method-entry and method-returns * * Activate tracing between * ::nsf::configure dtrace on * and * ::nsf::configure dtrace off * */ nsf*:::configure-probe /!self->tracing && copyinstr(arg0) == "dtrace" / { self->tracing = (arg1 && copyinstr(arg1) == "on") ? 1 : 0; } nsf*:::configure-probe /self->tracing && copyinstr(arg0) == "dtrace" / { self->tracing = (arg1 && copyinstr(arg1) == "off") ? 0 : 1; } /* * Measure time differences */ nsf*:::method-entry /self->tracing/ { self->start = timestamp; } nsf*:::method-return /self->tracing && self->start/ { @[copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)] = avg(timestamp - self->start); self->start = 0; } /* * Print aggregate with own format (less wide than the default) */ END { printa("\n%-35s %-35s %-40s = %@d", @); } generic/000077500000000000000000000000001242365656200125055ustar00rootroot00000000000000generic/aol-xotcl.tcl000066400000000000000000000047531242365656200151240ustar00rootroot00000000000000# # Load Next Scripting Framework, XOTcl and some related packages for # AOLserver 4.* and naviserver. # # - aolserver: rewrite essentially _ns_savenamespaces to include the # serialized objects # # - naviserver: just needed for the package require # the serialization logic resides in ns/tcl/nstrace.tcl # # We expect to find the package in standard Tcl package search path # (the auto_path var) The simplest location is to put them under the # "lib" directory within the AOLserver tree. # package require XOTcl 2.0; namespace import -force ::xotcl::* package require nx::serializer ns_log notice "XOTcl version $::xotcl::version$::xotcl::patchlevel loaded" if {[ns_info name] ne "NaviServer"} { # # We are loading into aolserver. # # Overload procedure defined in bin/init.tcl. # It is now XOTcl-savvy in how it treats some # special namespaces. # proc _ns_savenamespaces {} { set script [_ns_getpackages] set import "" set nslist "" _ns_getnamespaces namespaces foreach n $namespaces { if {$n ne "::nsf" && $n ne "::xotcl" && $n ne "::nx" && ![string match "::nsf::*" $n] && ![::nsf::object::exists $n]} { lappend nslist $n } } foreach n $nslist { foreach {ns_script ns_import} [_ns_getscript $n] { append script [list namespace eval $n $ns_script] \n if {$ns_import ne ""} { append import [list namespace eval $n $ns_import] \n } } } if {[catch {::Serializer all} objects]} { ns_log notice "XOTcl extension not loaded; will not copy objects\ (error: $objects; $::errorInfo)." set objects "" } ns_ictl save [append script \n \ $objects \n $import] # just for debugging purposes if {0} { set f [open [::xotcl::tmpdir]/__aolserver-blueprint-[ns_info server].tcl w] puts $f $script close $f } } # # Source XOTcl files from shared/private library # the way AOLserver does for plain Tcl files. # proc _my_sourcefiles {shared private} { set files "" foreach file [lsort [glob -nocomplain -directory $shared *.xotcl]] { if {[file exists [file join $private [file tail $file]]] == 0} { lappend files $file } } foreach file [lsort [glob -nocomplain -directory $private *.xotcl]] { lappend files $file } foreach file $files { _ns_sourcefile $file } } ns_eval { _my_sourcefiles [ns_library shared] [ns_library private] } } generic/aolstub.c000066400000000000000000000147471242365656200143370ustar00rootroot00000000000000/* * aolstub.c -- * * This file provides the stubs needed for the AOLserver, to load NSF. * Please note that at least AOLserver 4.* or NaviServer 4.99.4 or newer * are required. might have to have to apply a small patch to the * AOLserver as well (available from www.xotcl.org) in order to get it * working. * * Copyright (C) 2006-2013 Zoran Vasiljevic (a) * Copyright (C) 2006-2014 Gustaf Neumann (b) * * (a) Archiware Inc. * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #ifdef AOL_SERVER #include "nsf.h" #include NS_EXPORT int Ns_ModuleVersion = 1; #if NS_MAJOR_VERSION>=4 # define AOL4 #endif /* *---------------------------------------------------------------------------- * * NsNsf_Init -- * * Loads the package for the first time, i.e. in the startup thread. * * Results: * Standard Tcl result * * Side effects: * Package initialized. Tcl commands created. * *---------------------------------------------------------------------------- */ static int NsNsf_Init (Tcl_Interp *interp, void *context) { static int firsttime = 1; int ret; ret = Nsf_Init(interp); if (firsttime) { if (ret != TCL_OK) { Ns_Log(Warning, "can't load module %s: %s", (char *)context, Tcl_GetStringResult(interp)); } else { Ns_Log(Notice, "%s module version %s", (char*)context, NSF_PATCHLEVELL); /* * Import the Nsf namespace only for the shell after * predefined is through */ Tcl_Import(interp, Tcl_GetGlobalNamespace(interp), "nsf::*", 0); } firsttime = 0; } return ret; } /* *---------------------------------------------------------------------------- * * NsNsf_Init1 -- * * Loads the package in each thread-interpreter. * This is needed since Nsf Class/Object commands are not copied * from the startup thread to the connection (or any other) thread. * during AOLserver initialization and/or thread creation times. * * Why ? * * Simply because these two commands declare a delete callback which is * unsafe to call in any other thread but in the one which created them. * * To understand this, you may need to get yourself acquainted with the * mechanics of the AOLserver, more precisely, with the way Tcl interps * are initialized (dive into nsd/tclinit.c in AOLserver distro). * * So, we made sure (by patching the AOLserver code) that no commands with * delete callbacks declared, are ever copied from the startup thread. * Additionaly, we also made sure that AOLserver properly invokes any * AtCreate callbacks. So, instead of activating those callbacks *after* * running the Tcl-initialization script (which is the standard behaviour) * we activate them *before*. So we may get a chance to configure the * interpreter correctly for any commands within the init script. * * Proper Nsf usage would be to declare all resources (classes, objects) * at server initialization time and let AOLserver machinery to copy them * (or re-create them, better yet) in each new thread. * Resources created within a thread are automatically garbage-collected * on thread-exit time, so don't create any Nsf resources there. * Create them in the startup thread and they will automatically be copied * for you. * Look in /modules/tcl/nsf for a simple example. * * Results: * Standard Tcl result. * * Side effects: * Tcl commands created. * *---------------------------------------------------------------------------- */ static int NsNsf_Init1 (Tcl_Interp *interp, void *notUsed) { int result; #ifndef AOL4 result = Nsf_Init(interp); #else result = TCL_OK; #endif /* * Import the Nsf namespace only for the shell after * predefined is through */ Tcl_Import(interp, Tcl_GetGlobalNamespace(interp), "nsf::*", 1); return result; } /* *---------------------------------------------------------------------------- * * Ns_ModuleInit -- * * Called by the AOLserver when loading shared object file. * * Results: * Standard AOLserver result * * Side effects: * Many. Depends on the package. * *---------------------------------------------------------------------------- */ int Ns_ModuleInit(char *hServer, char *hModule) nonnull(1) nonnull(2); int Ns_ModuleInit(char *hServer, char *hModule) { int ret; assert(hServer); assert(hModule); /*Ns_Log(Notice, "+++ ModuleInit","INIT");*/ ret = Ns_TclInitInterps(hServer, NsNsf_Init, (void*)hModule); if (ret == TCL_OK) { /* * See discussion for NsNsf_Init1 procedure. * Note that you need to patch AOLserver for this to work! * The patch basically forbids copying of C-level commands with * declared delete callbacks. It also runs all AtCreate callbacks * BEFORE AOLserver runs the Tcl script for initializing new interps. * These callbacks are then responsible for setting up the stage * for correct (Nsf) extension startup (including copying any * Nsf resources (classes, objects) created in the startup thread. */ Ns_TclRegisterAtCreate((Ns_TclInterpInitProc *)NsNsf_Init1, NULL); } return ret == TCL_OK ? NS_OK : NS_ERROR; } #endif /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/asm/000077500000000000000000000000001242365656200132655ustar00rootroot00000000000000generic/asm/README000066400000000000000000000223771242365656200141600ustar00rootroot00000000000000 The current implementation of the Tcl-byte-code engine is built around a large switch statement (the technique is called switch-threading). It is actually quite hard to understand and to de-mangle, since there are many goto-operations for optimization, etc. There exists several other implementation techniques for execution engines coming from the field of direct threaded code. In particular i invested into differences between switch-threading, call-threading and label threading. implementation mechanisms around, namely call-threading and label-threading (direct threading). First of all, threaded.c is a short introductory example, showing in a small concise program the differences of these techniques. On my machine i got the following results: 1: 0 seconds, 94591 usec 2: 0 seconds, 84749 usec 3: 0 seconds, 59811 usec [1] switch threading [2] call threading [3] label threading This experiment shows that the implementation of switch-threading is the slowest, followed by call threading and label threading. However, the implementation of label-threading depends on a non-standard C extension (label address operator) needed for computed gotos. However, the label address operator is supported by at least three different compilers (supported on GCC, clang and IBM's XL C/C++). Based on this study i built an experimental new code-engine for tcl (integrated in nsf/nx) based on the following ideas: - Use Tcl's basic implementation elements as base elements of the execution-engine: Tcl_Obj, Tcl_ObjCmdProc (objProcs). - Instead of defining a "byte code", implement the code essentially as an array of objProcs, were as well the argument vectors of the objProcs are built as far as possible at compile time. This makes it possible to implement quickly an execution engine for all of Tcl. - By using objProcs as basic mechanism, every tcl-command implementation will become effectively an "instruction" of the code engine. By defining new objProcs, the command set of the engine will be extended automatically. Specialized instructions with the same format can be registered depending on syntactic properties of the source (e.g. not all argument test will be required, part of the syntax errors can be caught at compile time, on could implement specialized versions for Tcl's "set" for setting and reading variables, etc.). - The code engine is designed for allowing different resolving strategies for cmds (objProcs): come commands could be resolved at compile-time, some commands must be resolved at runtime. Similar experiments can be made in the object oriented case for objects and methods. It is not completely clear, if the binding time flexibility should be an option for a production version of the execution engine, since this would cause differences in the semantics of Tcl (when commands are redefined). However, this option provides some good evidence for potential speedups. - The current byte code engine performs double dispatches on object oriented calls (one dispatch on the object, a second dispatch on the method). The new execution engine allows direct dispatch on methods. - Exploit typing information: NSF/NX provides "optionally typing" information (e.g. x:int). This optional typing information can be used at runtime as well for optimizing the code. - Provide an intermediate representation ("Tcl assembler") instead of "hard wiring" instructions to Tcl commands as implemented in Tcl. The Tcl assembler makes it easier to implement high-level optimizations to provide different implementations for e.g. a sequence of tcl instructions. Note that a Tcl compiler (a program translating Tcl source into Tcl assembler) can be implemented in Tcl as well. Furthermore, for some high-performance requirements, it is possible to simply write Tcl assembly code by hand (this is at least an alternative to C-coding). Note that the Tcl assembler is not machine dependent, the instructions are those of the "abstract Tcl machine". Currently, the syntax of the Tcl assembler is a nested Tcl list. - As explained above, label-threading is the fastest implementation technique of the compared approaches, but it is not supported by all c-compilers. Implemented execution engines are sufficiently different to make maintenance of two (or more) execution engines hard. In order to address this issue, i implemented an assembly code generator and an execution code generator in NX from a common, declarative source. The assembly code and the execution engines can be included in the source. Depending on the C-compiler, the "right" include can be chosen. Note that it is certainly possible to implement some more different (optimized) execution engines as well. - Replacing for Tcl the complete byte-code engine is quite a large task. In order to experiment with the new execution engines i implemented asm-proc and asm-method as counterparts of "proc" and "method", which will take in their body the tcl assembly language. When such a command is called, the appropriate execution engine is called. This way, the new execution engine can co-exist with Tcl's classical byte-code engine, it should not be as well quite easy to follow this approach in jimtcl. - In cases where the new tcl-compilation and assembly generation fails, one could fall back to the basic implementation (e.g. tcl byte code engine). Some preliminary results: - If one executes just the objProcs (e.g. for "set" Tcl_SetObjCmd), the old code engine is faster, but the new ones "work" as well. It is certainly possible to provide new optimized instructions for certain tasks (accessing/setting variables, increment operations etc.) using the same format. - Using such instructions, both new execution engines (for call-threading and label-threading) show to be much faster than Tcl's current bytecode engine. - The current implementation based on call-threading is for the example below up to about three times faster than the Tcl-implementation, the label-threading variant is about 5 times faster. Current shortcomings / work in progress: - The current implementation is not re-entrant (no stack integration, proc local variables are currently stored in the assembly proc structure, not on the stack). - For allowing "uplevels" and "upvars" and compiled locals, we will need in Tcl the proc call frames, which will certainly cost some performance. For high-performance situations, these frames could be made optional (these are not available if one would code the procs straightforward in C). Note that the example below with the for loop and incr, reentrancy and stack frames are not required/needed at all (since the code does neither call a proc (or eval) and the code is not recursive - it is not clear, how often such optimization would be possible). - The instruction set is just sufficient for the about 30 examples in my current regression test set. The instruction set is just experimental, and not sufficiently thoughtfully designed. - The assembly language is very primitive: one has a set of "slot" declarations which are constants or variables. Later references address these slots via their indices (starting with 0). Similarly, the instructions are numbered as well (the code is a C array of instructions). A "goto" simply addresses the next instruction via the array index. All "declarations" of local vars have to be written before the "instructions". - It is not clear, how much Tcl compatibility is needed or wanted (interface to compiled locals, variable-/call-traces, etc.) - No effort so far on compiling Tcl source into Tcl assembly. Below is a short example in Tcl (and the Tcl byte code engine) and in Tcl assembly in two implementation variants, one with Tcl_Objs, one with integers (in case the typing information is available or inferable from the Tcl source). The timing results based on clang under Mac OS X: Call Threading: asm/t.001: 6.64 asm/t.002: 4.04 asm/t.003: 2.46 Label Threading: asm/t.001: 6.58 asm/t.002: 2.74 asm/t.003: 1.33 I'll push the version over the holidays to our public git repository. All the best -gustaf ================================================== package req nx::test nx::Test parameter count 100000 proc sum10.tcl {} { set sum 0 for {set i 0} {$i < 100} {incr i} { incr sum $i } return $sum } # Implementation in Tcl assembly, using tcl-objs for "sum", "i" and # the constants nsf::asm::proc sum10.asm1 {} { {obj sum} {obj i} {obj 0} {obj 1} {obj 100} {obj 0} {var obj 0} {var obj 1} {duplicateObj slot 6 obj 2} {duplicateObj slot 7 obj 5} {leIntObj slot 4 slot 7} {jumpTrue instruction 7} {incrObj slot 6 slot 7} {incrObj slot 7 slot 3} {jump instruction 2} {setResult slot 6} } # Implementation in Tcl assembly, using integers for "sum", "i" and # the constants nsf::asm::proc sum10.asm2 {} { {obj sum} {obj i} {integer int 1} {integer int 100} {integer int 0} {integer int 0} {setInt slot 4 int 0} {setInt slot 5 int 0} {leInt slot 3 slot 5} {jumpTrue instruction 7} {incrInt slot 4 slot 5} {incrInt slot 5 slot 2} {jump instruction 2} {setResultInt slot 4} } ? {sum10.tcl} "4950" ? {sum10.asm1} "4950" ? {sum10.asm2} "4950" generic/asm/asm.tcl000066400000000000000000000144031242365656200145530ustar00rootroot00000000000000package req nx::test nx::Test parameter count 100000 #nx::Test parameter count 10 proc sum10.tcl {} { set sum 0 for {set i 0} {$i < 100} {incr i} { incr sum $i } return $sum } # implementation in assembly, using tcl-objs for # "sum", "i" and the constants nsf::asm::proc sum10.asm1 {} { {obj sum} {obj i} {obj 0} {obj 1} {obj 100} {obj 0} {var obj 0} {var obj 1} {duplicateObj slot 6 obj 2} {duplicateObj slot 7 obj 5} {leIntObj slot 4 slot 7} {jumpTrue instruction 7} {incrObj slot 6 slot 7} {incrObj slot 7 slot 3} {jump instruction 2} {setResult slot 6} } # implementation in assembly, using tcl-objs for # "sum", "i" and the constants nsf::asm::proc sum10.asm2 {} { {obj sum} {obj i} {integer int 1} {integer int 100} {integer int 0} {integer int 0} {setInt slot 4 int 0} {setInt slot 5 int 0} {leInt slot 3 slot 5} {jumpTrue instruction 7} {incrInt slot 4 slot 5} {incrInt slot 5 slot 2} {jump instruction 2} {setResultInt slot 4} } ? {sum10.tcl} "4950" ? {sum10.asm1} "4950" ? {sum10.asm2} "4950" #exit proc incr1.tcl {x} { incr x } # currently we have to set the local var of the argument nsf::asm::proc incr1.asm1 {x} { {obj x} {obj 1} {cmd ::set obj 0 arg 0} {cmd ::incr obj 0 obj 1} } nsf::asm::proc incr1.asm2 {x} { {obj x} {obj 1} {var obj 0} {setObj slot 2 arg 0} {incrObj slot 2 slot 1} {setResult slot 2} } ? {incr1.tcl 10} "11" ? {incr1.asm1 10} "11" ? {incr1.asm2 10} "11" proc incr2.tcl {x} { set a $x incr a } nsf::asm::proc incr2.asm1 {x} { {obj a} {obj 1} {cmd ::set obj 0 arg 0} {cmd ::incr obj 0 obj 1} } nsf::asm::proc incr2.asm2 {x} { {obj a} {obj 1} {var obj 0} {setObj slot 2 arg 0} {incrObj slot 2 slot 1} {setResult slot 2} } ? {incr2.tcl 13} "14" ? {incr2.asm1 13} "14" ? {incr2.asm2 13} "14" proc foo.tcl {x} { set a 1 set b $x incr a [incr b] return $a } nsf::asm::proc foo.asm1 {x} { {obj a} {obj b} {obj 1} {cmd ::set obj 0 obj 2} {cmd ::set obj 1 arg 0} {cmd ::incr obj 1} {store instruction 4 argv 2} {cmd ::incr obj 0 result 3} {cmd ::set obj 0} } nsf::asm::proc foo.asm2 {x} { {obj a} {obj b} {obj 1} {var obj 0} {var obj 1} {var obj 2} {setObj slot 3 obj 2} {setObj slot 4 arg 0} {incrObj slot 4 slot 2} {setObjToResult slot 5} {incrObj slot 3 slot 5} {cmd ::set obj 0} } ? {foo.tcl 100} "102" ? {foo.asm1 100} "102" ? {foo.asm2 100} "102" #exit proc bar.tcl {x} {concat [format %c 64] - [format %c 65] - $x} nsf::asm::proc bar.asm {x} { {obj %c} {obj -} {obj 64} {obj 65} {cmd ::format obj 0 obj 2} {store instruction 4 argv 1} {cmd ::format obj 0 obj 3} {store instruction 4 argv 3} {cmd ::concat result 1 obj 1 result 3 obj 1 arg 0} } #puts [bar.asm 123] ? {bar.tcl 123} "@ - A - 123" ? {bar.asm 123} "@ - A - 123" proc create1.tcl {} {nx::Object create o1} nsf::asm::proc create1.asm1 {} { {obj ::nx::Object} {obj create} {obj o1} {eval obj 0 obj 1 obj 2} } nsf::asm::proc create1.asm2 {} { {obj create} {obj o1} {cmd ::nx::Object obj 0 obj 1} } nsf::asm::proc create1.asm3 {} { {obj nx::Object} {obj ::nsf::methods::class::create} {obj o1} {methodDelegateDispatch obj 0 obj 1 obj 2} } nsf::asm::proc create1.asm4 {} { {obj ::nx::Object} {obj ::nsf::methods::class::create} {obj o1} {methodDelegateDispatch obj 0 obj 1 obj 2} } ? {create1.tcl} "::o1" ? {create1.asm1} "::o1" ? {create1.asm2} "::o1" ? {create1.asm3} "::o1" ? {create1.asm4} "::o1" proc create2.tcl {} {nx::Object create o1;o1 destroy;::nsf::object::exists o1} nsf::asm::proc create2.asm1 {} { {obj create} {obj o1} {obj destroy} {cmd ::nx::Object obj 0 obj 1} {eval obj 1 obj 2} {cmd ::nsf::object::exists obj 1} } nsf::asm::proc create2.asm2 {} { {obj o1} {obj nx::Object} {obj ::nsf::methods::class::create} {obj ::nsf::methods::object::destroy} {methodDelegateDispatch obj 1 obj 2 obj 0} {methodDelegateDispatch obj 0 obj 3} {cmd ::nsf::object::exists obj 0} } nsf::asm::proc create2.asm3 {} { {obj o1} {obj ::nx::Object} {obj ::nsf::methods::class::create} {obj ::nsf::methods::object::destroy} {methodDelegateDispatch obj 1 obj 2 obj 0} {methodDelegateDispatch obj 0 obj 3} {cmd ::nsf::object::exists obj 0} } ? {create2.tcl} 0 ? {create2.asm1} 0 ? {create2.asm2} 0 ? {create2.asm3} 0 proc check_obj.tcl {} {::nsf::object::exists o1} nsf::asm::proc check_obj.asm1 {} { {obj o1} {cmd ::nsf::object::exists obj 0} } nsf::asm::proc check_obj.asm2 {} { {obj o1} {obj ::nsf::object::exists} {eval obj 1 obj 0} } ? {check_obj.tcl} 0 ? {check_obj.asm1} 0 ? {check_obj.asm2} 0 nx::Object create o { set :x 1 } nsf::method::create o check_obj.tcl {} {::nsf::object::exists o1} nsf::method::asmcreate o check_obj.asm1 {} { {obj o1} {cmd ::nsf::object::exists obj 0} } nsf::method::asmcreate o check_obj.asm2 {} { {obj o1} {obj ::nsf::object::exists} {eval obj 1 obj 0} } ? {o check_obj.tcl} 0 ? {o check_obj.asm1} 0 ? {o check_obj.asm2} 0 # info exists is byte-compiled nsf::method::create o check_var1.tcl {} {info exists :x} nsf::method::asmcreate o check_var1.asm1 {} { {obj exists} {obj :x} {cmd ::info obj 0 obj 1} } ? {o check_var1.tcl} 1 ? {o check_var1.asm1} 1 # check for existence via method nsf::method::create o check_var2.tcl {} { : ::nsf::methods::object::exists x } nsf::method::asmcreate o check_var2.asm1 {} { {obj :} {obj ::nsf::methods::object::exists} {obj x} {eval obj 0 obj 1 obj 2} } nsf::method::asmcreate o check_var2.asm2 {} { {obj ::o} {obj ::nsf::methods::object::exists} {obj x} {methodDelegateDispatch obj 0 obj 1 obj 2} } nsf::method::asmcreate o check_var2.asm3 {} { {obj nsf::methods::object::exists} {obj x} {methodSelfDispatch obj 0 obj 1} } nsf::method::asmcreate o check_var2.asm4 {} { {obj ::nsf::methods::object::exists} {obj x} {methodSelfDispatch obj 0 obj 1} } ? {o check_var2.tcl} 1 ? {o check_var2.asm1} 1 ? {o check_var2.asm2} 1 ? {o check_var2.asm3} 1 ? {o check_var2.asm4} 1 # # self # nsf::method::create o self.tcl {} { self } nsf::method::asmcreate o self.asm1 {} { {obj self} {eval obj 0} } nsf::method::asmcreate o self.asm2 {} { {cmd self} } nsf::method::asmcreate o self.asm3 {} { {self} } ? {o self.tcl} ::o ? {o self.asm1} ::o ? {o self.asm2} ::o ? {o self.asm3} ::o generic/asm/asmAssembleTemplate.c000066400000000000000000000207411242365656200173650ustar00rootroot00000000000000enum asmStatementIndex { asmObjProcIdx, $STATEMENT_INDICES }; static CONST char *asmStatementNames[] = { "cmd", $STATEMENT_NAMES, NULL }; enum asmStatmentArgTypeIndex { asmStatementArgTypeArgIdx, asmStatementArgTypeArgvIdx, asmStatementArgTypeInstructionIdx, asmStatementArgTypeIntIdx, asmStatementArgTypeObjIdx, asmStatementArgTypeResultIdx, asmStatementArgTypeSlotIdx, asmStatementArgTypeVarIdx }; static CONST char *asmStatementArgType[] = { "arg", "argv", "instruction", "int", "obj", "result", "slot", "var", NULL}; static CONST char *asmStatementCmdType[] = {"arg", "obj", "result", "var", NULL}; static CONST char *asmStatementInstructionType[] = {"instruction", NULL}; static CONST char *asmStatementIntType[] = {"int", NULL}; static CONST char *asmStatementObjType[] = {"obj", NULL}; static CONST char *asmStatementSlotObjArgType[] = {"slot", "obj", "arg", NULL}; static CONST char *asmStatementSlotType[] = {"slot", NULL}; static CONST char *asmStatementSlotIntType[] = {"slot", "int", NULL}; static CONST char *asmStatementStoreType[] = {"instruction", "argv", NULL}; static AsmStatementInfo asmStatementInfo[] = { /* asmObjProcIdx, */ {ASM_INFO_PAIRS|ASM_INFO_SKIP1, NULL, 2, -1, NR_PAIRS1}, $STATEMENT_INFO }; /* *---------------------------------------------------------------------- * AsmAssemble -- * * The assmbler, takes an assembly script in the form of a nested * list and emits the internal representation for the execution * enigine. * *---------------------------------------------------------------------- */ static int AsmAssemble(ClientData cd, Tcl_Interp *interp, Tcl_Obj *nameObj, int nrArgs, Tcl_Obj *asmObj, AsmCompiledProc **retAsmProc) { AsmPatches patchArray[100], *patches = &patchArray[0], *patchPtr; // TODO: make me dynamic Tcl_Command cmd; AsmCompiledProc *proc; AsmInstruction *inst; int i, result, nrAsmInstructions, nrLocalObjs, totalArgvArgs; int oc, currentAsmInstruction, currentSlot; Tcl_Obj **ov; CONST char *procName; assert(nameObj); procName = ObjStr(nameObj); if (Tcl_ListObjGetElements(interp, asmObj, &oc, &ov) != TCL_OK) { return NsfPrintError(interp, "Asm code is not a valid list"); } /* * First Iteration: check wellformedness, determine sizes */ nrAsmInstructions = 0; nrLocalObjs = 0; totalArgvArgs = 0; for (i = 0; i < oc; i++) { int index, offset, wordOc; Tcl_Obj *lineObj = ov[i], **wordOv; if (Tcl_ListObjGetElements(interp, lineObj, &wordOc, &wordOv) != TCL_OK) { return NsfPrintError(interp, "Asm: line is not a well-formed asm instruction: %s", ObjStr(lineObj)); } result = Tcl_GetIndexFromObj(interp, wordOv[0], asmStatementNames, "asm instruction", 0, &index); if (result != TCL_OK) { return NsfPrintError(interp, "Asm: line is not a valid asm instruction: word %s, line %s", ObjStr(wordOv[0]), ObjStr(lineObj)); } offset = (asmStatementInfo[index].flags & ASM_INFO_SKIP1) ? 2 : 1; if ((asmStatementInfo[index].flags & ASM_INFO_PAIRS) && (wordOc-offset) % 2 == 1) { return NsfPrintError(interp, "Asm: argument list of cmd must contain pairs: %s", ObjStr(lineObj)); } if (asmStatementInfo[index].minArgs > -1 && wordOc < asmStatementInfo[index].minArgs) { return NsfPrintError(interp, "Asm: statement must contain at least %d words: %s", asmStatementInfo[index].minArgs, ObjStr(lineObj)); } if (asmStatementInfo[index].maxArgs > -1 && wordOc > asmStatementInfo[index].maxArgs) { return NsfPrintError(interp, "Asm: statement must contain at most %d words: %s", asmStatementInfo[index].maxArgs, ObjStr(lineObj)); } if (asmStatementInfo[index].argTypes) { result = AsmInstructionArgvCheck(interp, offset, wordOc, asmStatementInfo[index].argTypes, nrLocalObjs, oc, wordOv, lineObj); if (unlikely(result != TCL_OK)) {return result;} } if ((asmStatementInfo[index].flags & ASM_INFO_DECL) == 0) { int cArgs = asmStatementInfo[index].cArgs; /* * Determine the actual number of arguments passed to the * emitted instruction. This number might be determine by the * instruction type, or by the actual instruction being * processed (and later maybe for {*} etc.). */ if (cArgs == NR_PAIRS) { cArgs = (wordOc-offset) / 2; } else if (cArgs == NR_PAIRS1) { cArgs = 1 + (wordOc-offset) / 2; } //fprintf(stderr, "instruction %s need argvargs %d\n", ObjStr(lineObj), cArgs); totalArgvArgs += cArgs; nrAsmInstructions++; } else { /* currently obj and var from the same pool, will change... */ nrLocalObjs ++; } /* * optional, per-statement check operations */ switch (index) { case asmObjProcIdx: /* {cmd ::set slot 0 slot 2} */ cmd = Tcl_GetCommandFromObj(interp, wordOv[1]); if (cmd == NULL) { return NsfPrintError(interp, "Asm: cmd is not a valid tcl command: %s\n", Tcl_GetString( wordOv[1])); } break; /* begin generated code */ $ASSEMBLE_CHECK_CODE /* end generated code */ default: break; } } nrAsmInstructions ++; fprintf(stderr, "%s: nrAsmInstructions %d nrLocalObjs %d nrArgs %d argvArgs %d => data %d\n", procName, nrAsmInstructions, nrLocalObjs, nrArgs, totalArgvArgs, nrLocalObjs + nrArgs + totalArgvArgs ); /* * Allocate structures */ proc = (AsmCompiledProc *)ckalloc(sizeof(AsmCompiledProc)); proc->code = (AsmInstruction *)ckalloc(sizeof(AsmInstruction) * nrAsmInstructions); memset(proc->slotFlags, 0, sizeof(int) * NSF_ASM_NR_STATIC_SLOTS); proc->ip = proc->code; /* points to the first writable instructon */ proc->firstObj = proc->staticObjs; /* point to the first free obj */ proc->locals = proc->staticObjs; /* locals is just an alias */ proc->nrAsmArgReferences = 0; proc->slots = proc->locals + nrArgs; //fprintf(stderr, "args = %ld\n", proc->slots - proc->locals); AsmLocalsAlloc(proc, nrArgs + nrLocalObjs); /* when freeing, we need something like for (i=0; i < nrArgs + nrLocalObjs; i++) { if (proc->slotFlags[i] & ASM_SLOT_MUST_DECR) {Tcl_DecrRefCount(proc->slots[i]); } } */ /* * Second Iteration: emit code */ currentSlot = 0; currentAsmInstruction = 0; for (i = 0; i < oc; i++) { int index, offset, cArgs, argc, codeIndex, argvIndex, j; Tcl_Obj *lineObj = ov[i], **argv; Tcl_ListObjGetElements(interp, lineObj, &argc, &argv); Tcl_GetIndexFromObj(interp, argv[0], asmStatementNames, "asm instruction", 0, &index); offset = (asmStatementInfo[index].flags & ASM_INFO_SKIP1) ? 2 : 1; cArgs = asmStatementInfo[index].cArgs; if (cArgs == NR_PAIRS) { cArgs = (argc-offset) / 2; } else if (cArgs == NR_PAIRS1) { cArgs = 1 + (argc-offset) / 2; } switch (index) { case asmObjProcIdx: /* {cmd ::set slot 0 slot 2} */ cmd = Tcl_GetCommandFromObj(interp, argv[1]); #if defined(LABEL_THREADING) inst = AsmInstructionNew(proc, objProc, cArgs); inst->cmd = ((Command *)cmd)->objProc; #else inst = AsmInstructionNew(proc, ((Command *)cmd)->objProc, cArgs); #endif inst->clientData = ((Command *)cmd)->objClientData; /* use the assembly word as cmd name; should be ok when we keep assembly around */ inst->argv[0] = argv[1]; /*fprintf(stderr, "[%d] %s/%d\n", currentAsmInstruction, Tcl_GetString(argv[1]), 1+((argc-offset)/2));*/ AsmInstructionArgvSet(interp, offset, argc, 1, inst, proc, argv, 0); break; /* begin generated code */ $ASSEMBLE_EMIT_CODE /* end generated code */ } if ((asmStatementInfo[index].flags & ASM_INFO_DECL) == 0) { currentAsmInstruction ++; } } /* * add END instruction */ inst = AsmInstructionNew(proc, NULL, 0); /* * All addresses are determined, apply the argv patches triggered * from above. */ for (patchPtr = &patchArray[0]; patchPtr < patches; patchPtr++) { fprintf(stderr, "wanna patch code[%d]->argv = code[%d]->argv[%d]\n", patchPtr->targetAsmInstruction, patchPtr->sourceAsmInstruction, patchPtr->argvIndex); /* set the argument vector of code[1] to the address of code[4]->argv[1] */ (&proc->code[patchPtr->targetAsmInstruction])->argv = &(&proc->code[patchPtr->sourceAsmInstruction])->argv[patchPtr->argvIndex]; } *retAsmProc = proc; return TCL_OK; } generic/asm/asmExecuteTemplate.c000066400000000000000000000031521242365656200172310ustar00rootroot00000000000000int AsmExecute(ClientData cd, Tcl_Interp *interp, AsmCompiledProc *proc, int argc, Tcl_Obj *CONST argv[]) { int i, result, indexValue; ClientData clientData; NsfObject *object; Tcl_Command cmd; AsmInstruction *ip; static void *instructionLabel[] = { &&INST_objProc, &&INST_asmStoreResult, &&INST_asmSetResult, &&INST_asmNoop, &&INST_asmDispatch, &&INST_asmMethodDelegateDispatch00, &&INST_asmMethodDelegateDispatch11, &&INST_asmMethodSelfDispatch, &&INST_asmMethodSelfCmdDispatch, &&INST_asmMethodSelf, &&INST_asmJump, &&INST_asmJumpTrue, &&INST_asmLeScalar, &&INST_asmCopyScalar, &&INST_asmSetScalar, &&INST_asmSetScalarResult, &&INST_asmIncrScalar, &&INST_NULL }; /* * Place a copy of the actual argument into locals. */ for (i=1; i < argc; i++) { proc->locals[i-1] = argv[i]; } /* * Update all references to compiled arguments. */ for (i=0; i < proc->nrAsmArgReferences; i++) { AsmArgReference *arPtr = &proc->argReferences[i]; *(arPtr->objPtr) = proc->locals[arPtr->argNr]; } /* * Set the instruction pointer to the begin of the code. */ ip = proc->code; proc->status = 0; //fprintf(stderr, "AsmExecute jumps to %p\n", ip); goto *instructionLabel[ip->labelIdx]; INST_NULL: return result; EXEC_RESULT_CODE_HANDLER: if (likely(result == TCL_OK)) { ip++; goto *instructionLabel[ip->labelIdx]; } else { return result; } INST_objProc: result = (*ip->cmd)(ip->clientData, interp, ip->argc, ip->argv); goto EXEC_RESULT_CODE_HANDLER; GENERATED_INSTRUCTIONS; } generic/asm/asmExecuteTemplateCallThreading.c000066400000000000000000000033011242365656200216470ustar00rootroot00000000000000 $GENERATED_INSTRUCTIONS; /* *---------------------------------------------------------------------- * AsmExecute -- * * Define the execution engine for the code * *---------------------------------------------------------------------- */ static int AsmExecute(ClientData cd, Tcl_Interp *interp, AsmCompiledProc *proc, int argc, Tcl_Obj *CONST argv[]) { //AsmInstruction *ip; int i, result; #if 0 Var *compiledLocals; compiledLocals = ((Interp *) interp)->varFramePtr->compiledLocals; if (compiledLocals) { fprintf(stderr, "compiledLocals = %p\n", compiledLocals); } #endif /* * Place a copy of the actual argument into locals. */ for (i=1; i < argc; i++) { proc->locals[i-1] = argv[i]; } /* * Update all references to compiled arguments. */ for (i=0; i < proc->nrAsmArgReferences; i++) { AsmArgReference *arPtr = &proc->argReferences[i]; *(arPtr->objPtr) = proc->locals[arPtr->argNr]; } /* * Set the instruction pointer to the begin of the code. */ proc->ip = proc->code; //fprintf(stderr, "ip %p\n", proc->ip); while (*proc->ip->cmd) { //fprintf(stderr, "will execute instruction ip %p cmd %p %p/%d\n", ip, ip->cmd, ip->argv[0], ip->argc); //if (ip->cmd == tclFormat) {AsmInstructionPrint(ip);} //if (ip->cmd == (Tcl_ObjCmdProc*)tclDispatch) {AsmInstructionPrint(ip);} result = (*proc->ip->cmd)(proc->ip->clientData, interp, proc->ip->argc, proc->ip->argv); /*fprintf(stderr, "%s returned <%s> (%d)\n", Tcl_GetString(ip->argv[0]), Tcl_GetString(Tcl_GetObjResult(interp)), result);*/ if (unlikely(result != TCL_OK)) break; proc->ip++; //fprintf(stderr, "ip %p\n", proc->ip); } return result; } generic/asm/asmExecuteTemplateLabelThreading.c000066400000000000000000000026541242365656200220250ustar00rootroot00000000000000 enum instructionIdx { IDX_objProc, $INSTRUCTION_INDICES, IDX_NULL }; /* *---------------------------------------------------------------------- * AsmExecute -- * * Define the execution engine for the code * *---------------------------------------------------------------------- */ int AsmExecute(ClientData cd, Tcl_Interp *interp, AsmCompiledProc *proc, int argc, Tcl_Obj *CONST argv[]) { int i, result = TCL_OK; AsmInstruction *ip; static void *instructionLabel[] = { &&INST_objProc, $INSTRUCTION_LABELS, &&INST_NULL }; /* * Place a copy of the actual argument into locals. */ for (i=1; i < argc; i++) { proc->locals[i-1] = argv[i]; } /* * Update all references to compiled arguments. */ for (i=0; i < proc->nrAsmArgReferences; i++) { AsmArgReference *arPtr = &proc->argReferences[i]; *(arPtr->objPtr) = proc->locals[arPtr->argNr]; } /* * Set the instruction pointer to the begin of the code. */ ip = proc->code; proc->status = 0; //fprintf(stderr, "AsmExecute jumps to %p\n", ip); goto *instructionLabel[ip->labelIdx]; INST_NULL: return result; EXEC_RESULT_CODE_HANDLER: if (likely(result == TCL_OK)) { ip++; goto *instructionLabel[ip->labelIdx]; } else { return result; } INST_objProc: result = (*ip->cmd)(ip->clientData, interp, ip->argc, ip->argv); goto EXEC_RESULT_CODE_HANDLER; $GENERATED_INSTRUCTIONS } generic/asm/genAssemble.tcl000066400000000000000000000507331242365656200162260ustar00rootroot00000000000000package require nx ###################################################################### # The code engine ###################################################################### nsf::proc generate {threadingType:class} { set suffix [string trimleft ${threadingType} :] set dirName [file dirname [info script]] foreach {var value} [${threadingType} generate] { set $var $value } set template [readFile $dirName/asmExecuteTemplate$suffix.c] writeFile $dirName/nsfAsmExecute$suffix.c [subst -nocommand -nobackslash $template] set template [readFile $dirName/asmAssembleTemplate.c] writeFile $dirName/nsfAsmAssemble.c [subst -nocommand -nobackslash $template] } nsf::proc readFile {fn} {set f [open $fn]; set content [read $f]; close $f; return $content} nsf::proc writeFile {fn content} { puts stderr "writing $fn" set f [open $fn w]; puts -nonewline $f $content; close $f } ###################################################################### # Basic Class for Instructions and Declarations ###################################################################### nx::Class create Statement { :property {name "[namespace tail [self]]"} :property {mustContainPairs true} :property {argTypes NULL} :property {minArgs 0} :property {maxArgs 0} :property {cArgs 0} :property {asmCheckCode ""} :property {asmEmitCode ""} :public method cName {} { # prepend asm and capitalize first character return asm[string toupper [string range ${:name} 0 0]][string range ${:name} 1 end] } :public method getAsmEmitCode {} { return ${:asmEmitCode} } :public class method "generate assembler" {} { set statementIndex {} set statementNames {} set (ASSEMBLE_EMIT_CODE) "" foreach s [lsort [Statement info instances -closure]] { if {[$s maxArgs] == 0} { puts stderr "ignore statement $s" continue } lappend statementIndex [$s cName]Idx lappend statementNames \"[$s name]\" set emitCode [$s getAsmEmitCode] if {$emitCode ne ""} { append (ASSEMBLE_EMIT_CODE) " case [$s cName]Idx:\n$emitCode\n break;\n\n" } set flags 0 if {[$s info has type ::Declaration]} { lappend flags ASM_INFO_DECL } if {[$s mustContainPairs]} { lappend flags ASM_INFO_PAIRS } lappend statementInfo \ "/* [$s cName] */\n {[join $flags |], [$s argTypes], [$s minArgs], [$s maxArgs], [$s cArgs]}" } array set {} [list \ STATEMENT_INDICES [join $statementIndex ",\n "] \ STATEMENT_NAMES [join $statementNames ",\n "] \ STATEMENT_INFO [join $statementInfo ",\n "] \ ASSEMBLE_CHECK_CODE ""] return [array get {}] } } ###################################################################### # Basic Class for Instructions and Declarations ###################################################################### nx::Class create Declaration -superclass Statement { } ###################################################################### # Basic Class for defining Instructions independent of the code # generator (label threading, call threading) ###################################################################### nx::Class create Instruction -superclass Statement { :property {execCode ""} :property {isJump false} :property {returnsResult false} # The property "execNeedsProc" is just needed for call threading, # where we have to pass proc via inst->clientData :property {execNeedsProc false} :public method getAsmEmitCode {} { # # For every instruction, the C-code allocates an instruction record # append . \ "\n\tinst = AsmInstructionNew(proc, [:cName], cArgs);" \ "\n\tif (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);}" \ [:asmEmitCode] } :method "code clear" {} { set :cCode "" } :method "code get" {} { return ${:cCode} } :method "code append" {value} { append :cCode $value } :method "code mustAssign" {value} { if {![regexp "\\m${value}\\M\\s*=" ${:cCode}]} { error "code does not assign variable '$value': ${:cCode}" } } :method "code mustContain" {value} { if {![regexp ${value} ${:cCode}]} { error "code does not contain '$value': ${:cCode}" } } } ###################################################################### # Code Generator for Label Threading ###################################################################### nx::Class create LabelThreading { :public class method generate {} { Instruction mixin add [self]::Instruction set instructions [lsort [Instruction info instances]] set labels {} set indices {} foreach instruction $instructions { append (GENERATED_INSTRUCTIONS) [$instruction generate] \n lappend labels &&[$instruction labelName] lappend indices IDX_[$instruction cName] } array set {} [list \ INSTRUCTION_LABELS [join $labels ",\n "] \ INSTRUCTION_INDICES [join $indices ",\n "] \ {*}[Statement generate assembler]] Instruction mixin delete [self]::Instruction return [array get {}] } nx::Class create [self]::Instruction { # # This Class is designed as a mixin class for Instruction # :public method labelName {} { return INST_[:cName] } :method nextInstruction {} { if {[:isJump]} { :code mustContain NsfAsmJump :code append "\n goto *instructionLabel\[ip->labelIdx];\n" } else { :code append "\n ip++;\n goto *instructionLabel\[ip->labelIdx];\n" } } :public method "code generate" {} { :code append ${:execCode} if {[:returnsResult]} { :code mustAssign result :code append " goto EXEC_RESULT_CODE_HANDLER;\n" } } :public method generate {} { :code clear :code append [:labelName]:\n :code generate :nextInstruction return [:code get] } } } ###################################################################### # Code Generator for Call Threading ###################################################################### nx::Class create CallThreading { :public class method generate {} { Instruction mixin add [self]::Instruction Statement mixin add [self]::Statement foreach instruction [lsort [Instruction info instances]] { append (GENERATED_INSTRUCTIONS) [$instruction generate] \n } array set {} [Statement generate assembler] Instruction mixin delete [self]::Instruction Statement mixin delete [self]::Statement return [array get {}] } nx::Class create [self]::Statement { :public method asmEmitCode {} { set asmEmitCode ${:asmEmitCode} if {[:execNeedsProc]} { append asmEmitCode "\n\tinst->clientData = proc;\n" } return $asmEmitCode } } nx::Class create [self]::Instruction { # # This Class is designed as a mixin class for Instruction # :public method "code generate" {} { set code ${:execCode} regsub -all {\mip->argv\M} $code argv code regsub -all {\mip->argc\M} $code argc code regsub -all {\mip->clientData\M} $code clientData code if {[:isJump]} { regsub -all {\mip\s*= } $code "proc->ip = " code regsub -all {\mip\s*[+][+]} $code "proc->ip++" code } if {[:returnsResult]} { :code append " int result;\n" :code append $code :code mustAssign result :code append " return result;\n" } else { :code append $code :code append " return TCL_OK;\n" } } :public method generate {} { :code clear :code append \ "static int [:cName](ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv\[]) \{\n" if {[:execNeedsProc]} { :code append " AsmCompiledProc *proc = clientData;\n" } :code generate :code append "\}\n" return [:code get] } } } namespace eval ::asm { ###################################################################### # Declarations ###################################################################### # {obj a} Declaration create obj \ -mustContainPairs false \ -minArgs 2 -maxArgs 2 \ -asmEmitCode { proc->slots[currentSlot] = argv[1]; Tcl_IncrRefCount(proc->slots[currentSlot]); proc->slotFlags[currentSlot] |= ASM_SLOT_MUST_DECR; currentSlot ++; } # {var obj 0} # obj is intended to be the varname, but currently ignored Declaration create var \ -minArgs 3 -maxArgs 3 -argTypes asmStatementObjType \ -asmEmitCode { proc->slots[currentSlot] = NULL; currentSlot ++; } # {integer int 0} Declaration create integer \ -minArgs 3 -maxArgs 3 -argTypes asmStatementIntType \ -asmEmitCode { { int intValue; Tcl_GetIntFromObj(interp, argv[2], &intValue); proc->slots[currentSlot] = INT2PTR(intValue); //fprintf(stderr, "setting slots [%d] = %d\n", currentSlot, intValue); proc->slotFlags[currentSlot] |= ASM_SLOT_IS_INTEGER; currentSlot ++; } } ###################################################################### # Instructions ###################################################################### # {noop} Instruction create noop \ -mustContainPairs false \ -minArgs 1 -maxArgs 1 # {eval obj 0 obj 1 obj 2} Instruction create dispatch \ -name "eval" \ -minArgs 3 -maxArgs -1 -cArgs NR_PAIRS -argTypes asmStatementCmdType \ -returnsResult true \ -execCode { result = Tcl_EvalObjv(interp, ip->argc, ip->argv, 0); } # {methodDelegateDispatch obj 0 obj 1 obj 2} Instruction create methodDelegateDispatch \ -name "methodDelegateDispatch" \ -minArgs 5 -maxArgs -1 -cArgs NR_PAIRS -argTypes asmStatementCmdType \ -asmEmitCode { { Tcl_Command cmd = NULL; NsfObject *object = NULL; AsmResolverInfo *resInfo; if (strncmp(ObjStr(inst->argv[1]), "::nsf::methods::", 16) == 0) { cmd = Tcl_GetCommandFromObj(interp, inst->argv[1]); //fprintf(stderr, "%s: asmMethod cmd '%s' => %p\n", procName, ObjStr(inst->argv[1]), cmd); } if (strncmp(ObjStr(inst->argv[0]), "::nx::", 6) == 0) { GetObjectFromObj(interp, inst->argv[0], &object); //fprintf(stderr, "%s: asmMethod object '%s' => %p\n", procName, ObjStr(inst->argv[0]), object); } if (cmd && object) { // experimental: bind obj and method resInfo = NEW(AsmResolverInfo); // TODO: LEAK resInfo->cmd = cmd; resInfo->object = object; inst->clientData = resInfo; AsmInstructionSetCmd(inst, asmMethodDelegateDispatch11); } else if (cmd) { inst->clientData = cmd; } else { inst->clientData = NULL; } } } \ -returnsResult true \ -execCode { { Tcl_Command cmd = NULL; NsfObject *object; // obj and method are unresolved result = GetObjectFromObj(interp, ip->argv[0], &object); if (likely(ip->clientData != NULL)) { cmd = ip->clientData; } else { cmd = Tcl_GetCommandFromObj(interp, ip->argv[1]); } //fprintf(stderr, "cmd %p object %p\n", cmd, object); result = MethodDispatch(object, interp, ip->argc-1, ip->argv+1, cmd, object, NULL, ObjStr(ip->argv[1]), 0, 0); } } # methodDelegateDispatch11 is an optimized variant of # methodDelegateDispatch, emitted alternatively by the assembler for # the above instruction. Instruction create methodDelegateDispatch11 \ -returnsResult true \ -execCode { // obj and method are resolved { AsmResolverInfo *resInfo = ip->clientData; result = MethodDispatch(resInfo->object, interp, ip->argc-1, ip->argv+1, resInfo->cmd, resInfo->object, NULL, ObjStr(ip->argv[1]), 0, 0); } } # {methodSelfDispatch obj 0 obj 1 obj 2} Instruction create methodSelfDispatch \ -minArgs 3 -maxArgs -1 -cArgs NR_PAIRS -argTypes asmStatementCmdType \ -asmEmitCode { { Tcl_Command cmd = NULL; AsmResolverInfo *resInfo; if (strncmp(ObjStr(inst->argv[0]), "::nsf::methods::", 16) == 0) { cmd = Tcl_GetCommandFromObj(interp, inst->argv[0]); if (cmd) { //fprintf(stderr, "%s: asmMethodSelfCmdDispatch cmd '%s' => %p\n", procName, ObjStr(inst->argv[0]), cmd); AsmInstructionSetCmd(inst, asmMethodSelfCmdDispatch); } } else { //fprintf(stderr, "%s: asmMethodSelfDispatch cmd '%s'\n", procName, ObjStr(inst->argv[0])); } resInfo = NEW(AsmResolverInfo); // TODO: LEAK resInfo->cmd = cmd; resInfo->proc = proc; inst->clientData = resInfo; } } \ -returnsResult true \ -execCode { { AsmResolverInfo *resInfo = ip->clientData; Tcl_Command cmd = resInfo->cmd ? resInfo->cmd : Tcl_GetCommandFromObj(interp, ip->argv[0]); result = MethodDispatch(resInfo->proc->currentObject, interp, ip->argc, ip->argv, cmd, resInfo->proc->currentObject, NULL, ObjStr(ip->argv[0]), 0, 0); } } # methodSelfCmdDispatch is an optimized variant of # methodSelfDispatch, emitted alternatively by the assembler for the # above instruction. Instruction create methodSelfCmdDispatch \ -returnsResult true \ -execCode { { AsmResolverInfo *resInfo = ip->clientData; assert(resInfo->cmd != NULL); result = Tcl_NRCallObjProc(interp, Tcl_Command_objProc(resInfo->cmd), resInfo->proc->currentObject, ip->argc, ip->argv); } } # {self} Instruction create self \ -minArgs 1 -maxArgs 1 \ -execNeedsProc true \ -execCode { Tcl_SetObjResult(interp, proc->currentObject->cmdName); } # {jump instruction 2} # TODO: maybe define later jump labels in asm source Instruction create jump \ -minArgs 3 -maxArgs 3 -cArgs 1 -argTypes asmStatementInstructionType \ -execNeedsProc true \ -isJump true \ -execCode { //fprintf(stderr, "asmJump oc %d instructionIndex %d\n", ip->argc, PTR2INT(ip->argv[0])); NsfAsmJump(PTR2INT(ip->argv[0])); } # {jumpTrue instruction 6} # TODO: maybe define later jump labels in asm source Instruction create jumpTrue \ -minArgs 3 -maxArgs 3 -cArgs 1 -argTypes asmStatementInstructionType \ -execNeedsProc true \ -isJump true \ -execCode { if (proc->status) { //fprintf(stderr, "asmJumpTrue jump oc %d instructionIndex %d\n", ip->argc, PTR2INT(ip->argv[0])); NsfAsmJump(PTR2INT(ip->argv[0])); } else { //fprintf(stderr, "asmJumpTrue fall through\n"); NsfAsmJumpNext(); } } # {leIntObj slot 4 slot 7} Instruction create leIntObj \ -minArgs 5 -maxArgs 5 -cArgs 2 -argTypes asmStatementSlotType \ -execNeedsProc true \ -execCode { { int value1, value2; Tcl_Obj *obj; //fprintf(stderr, "leIntObj oc %d op1 %p op2 %p\n", ip->argc, ip->argv[0], ip->argv[1]); // for the time being, we compare two int values obj = proc->slots[PTR2INT(ip->argv[0])]; if (likely(obj->typePtr == Nsf_OT_intType)) { value1 = obj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, obj, &value1); } obj = proc->slots[PTR2INT(ip->argv[1])]; if (likely(obj->typePtr == Nsf_OT_intType)) { value2 = obj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, obj, &value2); } //fprintf(stderr, "asmLeScalar oc %d op1 %d op2 %d => %d\n", ip->argc, value1, value2, value1 <= value2); proc->status = value1 <= value2; } } # {leInt slot 4 slot 7} Instruction create leInt \ -minArgs 5 -maxArgs 5 -cArgs 2 -argTypes asmStatementSlotType \ -execNeedsProc true \ -execCode { { int value1, value2; value1 = PTR2INT(proc->slots[PTR2INT(ip->argv[0])]); value2 = PTR2INT(proc->slots[PTR2INT(ip->argv[1])]); proc->status = value1 <= value2; } } # {duplicateObj slot 6 obj 2} # TODO: should force first arg "slot" Instruction create duplicateObj \ -minArgs 5 -maxArgs 5 -cArgs 2 -argTypes asmStatementSlotObjArgType \ -execNeedsProc true \ -execCode { { int indexValue = PTR2INT(ip->argv[0]); //fprintf(stderr, "duplicateObj var[%d] = %s\n", indexValue, ObjStr(ip->argv[1])); if (proc->slots[indexValue]) { Tcl_DecrRefCount(proc->slots[indexValue]); } proc->slots[indexValue] = Tcl_DuplicateObj(ip->argv[1]); Tcl_IncrRefCount(proc->slots[indexValue]); proc->slotFlags[indexValue] |= ASM_SLOT_MUST_DECR; } } # {setObj slot 2 arg 0} # TODO: should force first arg "slot" Instruction create setObj \ -minArgs 5 -maxArgs 5 -cArgs 2 -argTypes asmStatementSlotObjArgType \ -execNeedsProc true \ -execCode { //fprintf(stderr, "setObj var[%d] = %s\n", PTR2INT(ip->argv[0]), ObjStr(ip->argv[1])); proc->slots[PTR2INT(ip->argv[0])] = ip->argv[1]; } # {setInt slot 6 int 0} # TODO: should force first arg "slot" Instruction create setInt \ -minArgs 5 -maxArgs 5 -cArgs 2 -argTypes asmStatementSlotIntType \ -execNeedsProc true \ -execCode { proc->slots[PTR2INT(ip->argv[0])] = ip->argv[1]; } # {setObjToResult slot 5} Instruction create setObjToResult \ -minArgs 3 -maxArgs 3 -cArgs 2 -argTypes asmStatementSlotType \ -execNeedsProc true \ -execCode { //fprintf(stderr, "setObjToResult var[%d] = %s\n", PTR2INT(ip->argv[0]), ObjStr(ip->argv[1])); proc->slots[PTR2INT(ip->argv[0])] = Tcl_GetObjResult(interp); } # {setResult slot 6} Instruction create setResult \ -minArgs 3 -maxArgs 3 -cArgs 1 -argTypes asmStatementSlotType \ -execNeedsProc true \ -execCode { Tcl_SetObjResult(interp, proc->slots[PTR2INT(ip->argv[0])]); } # {setResultInt slot 6} Instruction create setResultInt \ -minArgs 3 -maxArgs 3 -cArgs 1 -argTypes asmStatementSlotType \ -execNeedsProc true \ -execCode { Tcl_SetObjResult(interp, Tcl_NewIntObj(PTR2INT(proc->slots[PTR2INT(ip->argv[0])]))); } # {store code 4 argv 2} Instruction create storeResult \ -minArgs 5 -maxArgs 5 -cArgs 0 -argTypes asmStatementStoreType \ -asmEmitCode { codeIndex = -1; argvIndex = -1; for (j = offset; j < argc; j += 2) { int argIndex, intValue; Tcl_GetIndexFromObj(interp, argv[j], asmStatementArgType, "asm internal arg type", 0, &argIndex); Tcl_GetIntFromObj(interp, argv[j+1], &intValue); switch (argIndex) { case asmStatementArgTypeInstructionIdx: codeIndex = intValue; break; case asmStatementArgTypeArgvIdx: argvIndex = intValue; break; } } // TODO: CHECK codeIndex, argvIndex (>0, reasonable values) //fprintf(stderr, "%p setting instruction %d => %d %d\n", patches, currentAsmInstruction, codeIndex, argvIndex); patches->targetAsmInstruction = currentAsmInstruction; patches->sourceAsmInstruction = codeIndex; patches->argvIndex = argvIndex; patches++; } -execCode { ip->argv[0] = Tcl_GetObjResult(interp); Tcl_IncrRefCount(ip->argv[0]); } # {incrObj slot 6 slot 7} Instruction create incrObj \ -minArgs 5 -maxArgs 5 -cArgs 2 -argTypes asmStatementSlotType \ -execNeedsProc true \ -execCode { { int intValue, incrValue; Tcl_Obj *intObj, *incrObj; //fprintf(stderr, "asmIncrScalar var[%d] incr var[%d], ", PTR2INT(ip->argv[0]), PTR2INT(ip->argv[1])); intObj = proc->slots[PTR2INT(ip->argv[0])]; incrObj = proc->slots[PTR2INT(ip->argv[1])]; if (likely(intObj->typePtr == Nsf_OT_intType)) { intValue = intObj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, intObj, &intValue); } if (likely(incrObj->typePtr == Nsf_OT_intType)) { incrValue = incrObj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, incrObj, &incrValue); } //fprintf(stderr, "%d + %d = %d,", intValue, incrValue, intValue + incrValue); Tcl_InvalidateStringRep(intObj); intObj->internalRep.longValue = (long)(intValue + incrValue); //fprintf(stderr, "updated %p var[%d] %p\n", intObj, PTR2INT(ip->argv[0]), proc->slots[PTR2INT(ip->argv[0])]); //Tcl_SetObjResult(interp, intObj); } } # {incrInt slot 6 slot 7} Instruction create incrInt \ -minArgs 5 -maxArgs 5 -cArgs 2 -argTypes asmStatementSlotType \ -execNeedsProc true \ -execCode { { int intValue, incrValue; //fprintf(stderr, "incrInt var[%d] incr var[%d]\n", PTR2INT(ip->argv[0]), PTR2INT(ip->argv[1])); intValue = PTR2INT(proc->slots[PTR2INT(ip->argv[0])]); incrValue = PTR2INT(proc->slots[PTR2INT(ip->argv[1])]); //fprintf(stderr, ".... intValue %d incr Value %d\n", intValue, incrValue); proc->slots[PTR2INT(ip->argv[0])] = INT2PTR(intValue + incrValue); //fprintf(stderr, ".... [%d] => %d\n", PTR2INT(ip->argv[0]), intValue + incrValue); } } } ###################################################################### # generate the code ###################################################################### generate ::LabelThreading generate ::CallThreadinggeneric/asm/nsfAsmAssemble.c000066400000000000000000000416271242365656200163460ustar00rootroot00000000000000enum asmStatementIndex { asmObjProcIdx, asmEvalIdx, asmDuplicateObjIdx, asmIncrIntIdx, asmIncrObjIdx, asmIntegerIdx, asmJumpIdx, asmJumpTrueIdx, asmLeIntIdx, asmLeIntObjIdx, asmMethodDelegateDispatchIdx, asmMethodSelfDispatchIdx, asmNoopIdx, asmObjIdx, asmSelfIdx, asmSetIntIdx, asmSetObjIdx, asmSetObjToResultIdx, asmSetResultIdx, asmSetResultIntIdx, asmStoreResultIdx, asmVarIdx }; static CONST char *asmStatementNames[] = { "cmd", "eval", "duplicateObj", "incrInt", "incrObj", "integer", "jump", "jumpTrue", "leInt", "leIntObj", "methodDelegateDispatch", "methodSelfDispatch", "noop", "obj", "self", "setInt", "setObj", "setObjToResult", "setResult", "setResultInt", "storeResult", "var", NULL }; enum asmStatmentArgTypeIndex { asmStatementArgTypeArgIdx, asmStatementArgTypeArgvIdx, asmStatementArgTypeInstructionIdx, asmStatementArgTypeIntIdx, asmStatementArgTypeObjIdx, asmStatementArgTypeResultIdx, asmStatementArgTypeSlotIdx, asmStatementArgTypeVarIdx }; static CONST char *asmStatementArgType[] = { "arg", "argv", "instruction", "int", "obj", "result", "slot", "var", NULL}; static CONST char *asmStatementCmdType[] = {"arg", "obj", "result", "var", NULL}; static CONST char *asmStatementInstructionType[] = {"instruction", NULL}; static CONST char *asmStatementIntType[] = {"int", NULL}; static CONST char *asmStatementObjType[] = {"obj", NULL}; static CONST char *asmStatementSlotObjArgType[] = {"slot", "obj", "arg", NULL}; static CONST char *asmStatementSlotType[] = {"slot", NULL}; static CONST char *asmStatementSlotIntType[] = {"slot", "int", NULL}; static CONST char *asmStatementStoreType[] = {"instruction", "argv", NULL}; static AsmStatementInfo asmStatementInfo[] = { /* asmObjProcIdx, */ {ASM_INFO_PAIRS|ASM_INFO_SKIP1, NULL, 2, -1, NR_PAIRS1}, /* asmEval */ {0|ASM_INFO_PAIRS, asmStatementCmdType, 3, -1, NR_PAIRS}, /* asmDuplicateObj */ {0|ASM_INFO_PAIRS, asmStatementSlotObjArgType, 5, 5, 2}, /* asmIncrInt */ {0|ASM_INFO_PAIRS, asmStatementSlotType, 5, 5, 2}, /* asmIncrObj */ {0|ASM_INFO_PAIRS, asmStatementSlotType, 5, 5, 2}, /* asmInteger */ {0|ASM_INFO_DECL|ASM_INFO_PAIRS, asmStatementIntType, 3, 3, 0}, /* asmJump */ {0|ASM_INFO_PAIRS, asmStatementInstructionType, 3, 3, 1}, /* asmJumpTrue */ {0|ASM_INFO_PAIRS, asmStatementInstructionType, 3, 3, 1}, /* asmLeInt */ {0|ASM_INFO_PAIRS, asmStatementSlotType, 5, 5, 2}, /* asmLeIntObj */ {0|ASM_INFO_PAIRS, asmStatementSlotType, 5, 5, 2}, /* asmMethodDelegateDispatch */ {0|ASM_INFO_PAIRS, asmStatementCmdType, 5, -1, NR_PAIRS}, /* asmMethodSelfDispatch */ {0|ASM_INFO_PAIRS, asmStatementCmdType, 3, -1, NR_PAIRS}, /* asmNoop */ {0, NULL, 1, 1, 0}, /* asmObj */ {0|ASM_INFO_DECL, NULL, 2, 2, 0}, /* asmSelf */ {0|ASM_INFO_PAIRS, NULL, 1, 1, 0}, /* asmSetInt */ {0|ASM_INFO_PAIRS, asmStatementSlotIntType, 5, 5, 2}, /* asmSetObj */ {0|ASM_INFO_PAIRS, asmStatementSlotObjArgType, 5, 5, 2}, /* asmSetObjToResult */ {0|ASM_INFO_PAIRS, asmStatementSlotType, 3, 3, 2}, /* asmSetResult */ {0|ASM_INFO_PAIRS, asmStatementSlotType, 3, 3, 1}, /* asmSetResultInt */ {0|ASM_INFO_PAIRS, asmStatementSlotType, 3, 3, 1}, /* asmStoreResult */ {0|ASM_INFO_PAIRS, asmStatementStoreType, 5, 5, 0}, /* asmVar */ {0|ASM_INFO_DECL|ASM_INFO_PAIRS, asmStatementObjType, 3, 3, 0} }; /* *---------------------------------------------------------------------- * AsmAssemble -- * * The assmbler, takes an assembly script in the form of a nested * list and emits the internal representation for the execution * enigine. * *---------------------------------------------------------------------- */ static int AsmAssemble(ClientData cd, Tcl_Interp *interp, Tcl_Obj *nameObj, int nrArgs, Tcl_Obj *asmObj, AsmCompiledProc **retAsmProc) { AsmPatches patchArray[100], *patches = &patchArray[0], *patchPtr; // TODO: make me dynamic Tcl_Command cmd; AsmCompiledProc *proc; AsmInstruction *inst; int i, result, nrAsmInstructions, nrLocalObjs, totalArgvArgs; int oc, currentAsmInstruction, currentSlot; Tcl_Obj **ov; CONST char *procName; assert(nameObj); procName = ObjStr(nameObj); if (Tcl_ListObjGetElements(interp, asmObj, &oc, &ov) != TCL_OK) { return NsfPrintError(interp, "Asm code is not a valid list"); } /* * First Iteration: check wellformedness, determine sizes */ nrAsmInstructions = 0; nrLocalObjs = 0; totalArgvArgs = 0; for (i = 0; i < oc; i++) { int index, offset, wordOc; Tcl_Obj *lineObj = ov[i], **wordOv; if (Tcl_ListObjGetElements(interp, lineObj, &wordOc, &wordOv) != TCL_OK) { return NsfPrintError(interp, "Asm: line is not a well-formed asm instruction: %s", ObjStr(lineObj)); } result = Tcl_GetIndexFromObj(interp, wordOv[0], asmStatementNames, "asm instruction", 0, &index); if (result != TCL_OK) { return NsfPrintError(interp, "Asm: line is not a valid asm instruction: word %s, line %s", ObjStr(wordOv[0]), ObjStr(lineObj)); } offset = (asmStatementInfo[index].flags & ASM_INFO_SKIP1) ? 2 : 1; if ((asmStatementInfo[index].flags & ASM_INFO_PAIRS) && (wordOc-offset) % 2 == 1) { return NsfPrintError(interp, "Asm: argument list of cmd must contain pairs: %s", ObjStr(lineObj)); } if (asmStatementInfo[index].minArgs > -1 && wordOc < asmStatementInfo[index].minArgs) { return NsfPrintError(interp, "Asm: statement must contain at least %d words: %s", asmStatementInfo[index].minArgs, ObjStr(lineObj)); } if (asmStatementInfo[index].maxArgs > -1 && wordOc > asmStatementInfo[index].maxArgs) { return NsfPrintError(interp, "Asm: statement must contain at most %d words: %s", asmStatementInfo[index].maxArgs, ObjStr(lineObj)); } if (asmStatementInfo[index].argTypes) { result = AsmInstructionArgvCheck(interp, offset, wordOc, asmStatementInfo[index].argTypes, nrLocalObjs, oc, wordOv, lineObj); if (unlikely(result != TCL_OK)) {return result;} } if ((asmStatementInfo[index].flags & ASM_INFO_DECL) == 0) { int cArgs = asmStatementInfo[index].cArgs; /* * Determine the actual number of arguments passed to the * emitted instruction. This number might be determine by the * instruction type, or by the actual instruction being * processed (and later maybe for {*} etc.). */ if (cArgs == NR_PAIRS) { cArgs = (wordOc-offset) / 2; } else if (cArgs == NR_PAIRS1) { cArgs = 1 + (wordOc-offset) / 2; } //fprintf(stderr, "instruction %s need argvargs %d\n", ObjStr(lineObj), cArgs); totalArgvArgs += cArgs; nrAsmInstructions++; } else { /* currently obj and var from the same pool, will change... */ nrLocalObjs ++; } /* * optional, per-statement check operations */ switch (index) { case asmObjProcIdx: /* {cmd ::set slot 0 slot 2} */ cmd = Tcl_GetCommandFromObj(interp, wordOv[1]); if (cmd == NULL) { return NsfPrintError(interp, "Asm: cmd is not a valid tcl command: %s\n", Tcl_GetString( wordOv[1])); } break; /* begin generated code */ /* end generated code */ default: break; } } nrAsmInstructions ++; fprintf(stderr, "%s: nrAsmInstructions %d nrLocalObjs %d nrArgs %d argvArgs %d => data %d\n", procName, nrAsmInstructions, nrLocalObjs, nrArgs, totalArgvArgs, nrLocalObjs + nrArgs + totalArgvArgs ); /* * Allocate structures */ proc = (AsmCompiledProc *)ckalloc(sizeof(AsmCompiledProc)); proc->code = (AsmInstruction *)ckalloc(sizeof(AsmInstruction) * nrAsmInstructions); memset(proc->slotFlags, 0, sizeof(int) * NSF_ASM_NR_STATIC_SLOTS); proc->ip = proc->code; /* points to the first writable instructon */ proc->firstObj = proc->staticObjs; /* point to the first free obj */ proc->locals = proc->staticObjs; /* locals is just an alias */ proc->nrAsmArgReferences = 0; proc->slots = proc->locals + nrArgs; //fprintf(stderr, "args = %ld\n", proc->slots - proc->locals); AsmLocalsAlloc(proc, nrArgs + nrLocalObjs); /* when freeing, we need something like for (i=0; i < nrArgs + nrLocalObjs; i++) { if (proc->slotFlags[i] & ASM_SLOT_MUST_DECR) {Tcl_DecrRefCount(proc->slots[i]); } } */ /* * Second Iteration: emit code */ currentSlot = 0; currentAsmInstruction = 0; for (i = 0; i < oc; i++) { int index, offset, cArgs, argc, codeIndex, argvIndex, j; Tcl_Obj *lineObj = ov[i], **argv; Tcl_ListObjGetElements(interp, lineObj, &argc, &argv); Tcl_GetIndexFromObj(interp, argv[0], asmStatementNames, "asm instruction", 0, &index); offset = (asmStatementInfo[index].flags & ASM_INFO_SKIP1) ? 2 : 1; cArgs = asmStatementInfo[index].cArgs; if (cArgs == NR_PAIRS) { cArgs = (argc-offset) / 2; } else if (cArgs == NR_PAIRS1) { cArgs = 1 + (argc-offset) / 2; } switch (index) { case asmObjProcIdx: /* {cmd ::set slot 0 slot 2} */ cmd = Tcl_GetCommandFromObj(interp, argv[1]); #if defined(LABEL_THREADING) inst = AsmInstructionNew(proc, objProc, cArgs); inst->cmd = ((Command *)cmd)->objProc; #else inst = AsmInstructionNew(proc, ((Command *)cmd)->objProc, cArgs); #endif inst->clientData = ((Command *)cmd)->objClientData; /* use the assembly word as cmd name; should be ok when we keep assembly around */ inst->argv[0] = argv[1]; /*fprintf(stderr, "[%d] %s/%d\n", currentAsmInstruction, Tcl_GetString(argv[1]), 1+((argc-offset)/2));*/ AsmInstructionArgvSet(interp, offset, argc, 1, inst, proc, argv, 0); break; /* begin generated code */ case asmEvalIdx: inst = AsmInstructionNew(proc, asmEval, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} break; case asmDuplicateObjIdx: inst = AsmInstructionNew(proc, asmDuplicateObj, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmIncrIntIdx: inst = AsmInstructionNew(proc, asmIncrInt, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmIncrObjIdx: inst = AsmInstructionNew(proc, asmIncrObj, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmIntegerIdx: { int intValue; Tcl_GetIntFromObj(interp, argv[2], &intValue); proc->slots[currentSlot] = INT2PTR(intValue); //fprintf(stderr, "setting slots [%d] = %d\n", currentSlot, intValue); proc->slotFlags[currentSlot] |= ASM_SLOT_IS_INTEGER; currentSlot ++; } break; case asmJumpIdx: inst = AsmInstructionNew(proc, asmJump, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmJumpTrueIdx: inst = AsmInstructionNew(proc, asmJumpTrue, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmLeIntIdx: inst = AsmInstructionNew(proc, asmLeInt, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmLeIntObjIdx: inst = AsmInstructionNew(proc, asmLeIntObj, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmMethodDelegateDispatchIdx: inst = AsmInstructionNew(proc, asmMethodDelegateDispatch, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} { Tcl_Command cmd = NULL; NsfObject *object = NULL; AsmResolverInfo *resInfo; if (strncmp(ObjStr(inst->argv[1]), "::nsf::methods::", 16) == 0) { cmd = Tcl_GetCommandFromObj(interp, inst->argv[1]); //fprintf(stderr, "%s: asmMethod cmd '%s' => %p\n", procName, ObjStr(inst->argv[1]), cmd); } if (strncmp(ObjStr(inst->argv[0]), "::nx::", 6) == 0) { GetObjectFromObj(interp, inst->argv[0], &object); //fprintf(stderr, "%s: asmMethod object '%s' => %p\n", procName, ObjStr(inst->argv[0]), object); } if (cmd && object) { // experimental: bind obj and method resInfo = NEW(AsmResolverInfo); // TODO: LEAK resInfo->cmd = cmd; resInfo->object = object; inst->clientData = resInfo; AsmInstructionSetCmd(inst, asmMethodDelegateDispatch11); } else if (cmd) { inst->clientData = cmd; } else { inst->clientData = NULL; } } break; case asmMethodSelfDispatchIdx: inst = AsmInstructionNew(proc, asmMethodSelfDispatch, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} { Tcl_Command cmd = NULL; AsmResolverInfo *resInfo; if (strncmp(ObjStr(inst->argv[0]), "::nsf::methods::", 16) == 0) { cmd = Tcl_GetCommandFromObj(interp, inst->argv[0]); if (cmd) { //fprintf(stderr, "%s: asmMethodSelfCmdDispatch cmd '%s' => %p\n", procName, ObjStr(inst->argv[0]), cmd); AsmInstructionSetCmd(inst, asmMethodSelfCmdDispatch); } } else { //fprintf(stderr, "%s: asmMethodSelfDispatch cmd '%s'\n", procName, ObjStr(inst->argv[0])); } resInfo = NEW(AsmResolverInfo); // TODO: LEAK resInfo->cmd = cmd; resInfo->proc = proc; inst->clientData = resInfo; } break; case asmNoopIdx: inst = AsmInstructionNew(proc, asmNoop, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} break; case asmObjIdx: proc->slots[currentSlot] = argv[1]; Tcl_IncrRefCount(proc->slots[currentSlot]); proc->slotFlags[currentSlot] |= ASM_SLOT_MUST_DECR; currentSlot ++; break; case asmSelfIdx: inst = AsmInstructionNew(proc, asmSelf, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmSetIntIdx: inst = AsmInstructionNew(proc, asmSetInt, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmSetObjIdx: inst = AsmInstructionNew(proc, asmSetObj, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmSetObjToResultIdx: inst = AsmInstructionNew(proc, asmSetObjToResult, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmSetResultIdx: inst = AsmInstructionNew(proc, asmSetResult, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmSetResultIntIdx: inst = AsmInstructionNew(proc, asmSetResultInt, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} inst->clientData = proc; break; case asmStoreResultIdx: inst = AsmInstructionNew(proc, asmStoreResult, cArgs); if (cArgs>0) {AsmInstructionArgvSet(interp, offset, argc, 0, inst, proc, argv, 0);} codeIndex = -1; argvIndex = -1; for (j = offset; j < argc; j += 2) { int argIndex, intValue; Tcl_GetIndexFromObj(interp, argv[j], asmStatementArgType, "asm internal arg type", 0, &argIndex); Tcl_GetIntFromObj(interp, argv[j+1], &intValue); switch (argIndex) { case asmStatementArgTypeInstructionIdx: codeIndex = intValue; break; case asmStatementArgTypeArgvIdx: argvIndex = intValue; break; } } // TODO: CHECK codeIndex, argvIndex (>0, reasonable values) //fprintf(stderr, "%p setting instruction %d => %d %d\n", patches, currentAsmInstruction, codeIndex, argvIndex); patches->targetAsmInstruction = currentAsmInstruction; patches->sourceAsmInstruction = codeIndex; patches->argvIndex = argvIndex; patches++; break; case asmVarIdx: proc->slots[currentSlot] = NULL; currentSlot ++; break; /* end generated code */ } if ((asmStatementInfo[index].flags & ASM_INFO_DECL) == 0) { currentAsmInstruction ++; } } /* * add END instruction */ inst = AsmInstructionNew(proc, NULL, 0); /* * All addresses are determined, apply the argv patches triggered * from above. */ for (patchPtr = &patchArray[0]; patchPtr < patches; patchPtr++) { fprintf(stderr, "wanna patch code[%d]->argv = code[%d]->argv[%d]\n", patchPtr->targetAsmInstruction, patchPtr->sourceAsmInstruction, patchPtr->argvIndex); /* set the argument vector of code[1] to the address of code[4]->argv[1] */ (&proc->code[patchPtr->targetAsmInstruction])->argv = &(&proc->code[patchPtr->sourceAsmInstruction])->argv[patchPtr->argvIndex]; } *retAsmProc = proc; return TCL_OK; } generic/asm/nsfAsmExecuteCallThreading.c000066400000000000000000000225431242365656200206330ustar00rootroot00000000000000 static int asmEval(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { int result; result = Tcl_EvalObjv(interp, argc, argv, 0); return result; } static int asmDuplicateObj(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; { int indexValue = PTR2INT(argv[0]); //fprintf(stderr, "duplicateObj var[%d] = %s\n", indexValue, ObjStr(argv[1])); if (proc->slots[indexValue]) { Tcl_DecrRefCount(proc->slots[indexValue]); } proc->slots[indexValue] = Tcl_DuplicateObj(argv[1]); Tcl_IncrRefCount(proc->slots[indexValue]); proc->slotFlags[indexValue] |= ASM_SLOT_MUST_DECR; } return TCL_OK; } static int asmIncrInt(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; { int intValue, incrValue; //fprintf(stderr, "incrInt var[%d] incr var[%d]\n", PTR2INT(argv[0]), PTR2INT(argv[1])); intValue = PTR2INT(proc->slots[PTR2INT(argv[0])]); incrValue = PTR2INT(proc->slots[PTR2INT(argv[1])]); //fprintf(stderr, ".... intValue %d incr Value %d\n", intValue, incrValue); proc->slots[PTR2INT(argv[0])] = INT2PTR(intValue + incrValue); //fprintf(stderr, ".... [%d] => %d\n", PTR2INT(argv[0]), intValue + incrValue); } return TCL_OK; } static int asmIncrObj(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; { int intValue, incrValue; Tcl_Obj *intObj, *incrObj; //fprintf(stderr, "asmIncrScalar var[%d] incr var[%d], ", PTR2INT(argv[0]), PTR2INT(argv[1])); intObj = proc->slots[PTR2INT(argv[0])]; incrObj = proc->slots[PTR2INT(argv[1])]; if (likely(intObj->typePtr == Nsf_OT_intType)) { intValue = intObj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, intObj, &intValue); } if (likely(incrObj->typePtr == Nsf_OT_intType)) { incrValue = incrObj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, incrObj, &incrValue); } //fprintf(stderr, "%d + %d = %d,", intValue, incrValue, intValue + incrValue); Tcl_InvalidateStringRep(intObj); intObj->internalRep.longValue = (long)(intValue + incrValue); //fprintf(stderr, "updated %p var[%d] %p\n", intObj, PTR2INT(argv[0]), proc->slots[PTR2INT(argv[0])]); //Tcl_SetObjResult(interp, intObj); } return TCL_OK; } static int asmJump(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; //fprintf(stderr, "asmJump oc %d instructionIndex %d\n", argc, PTR2INT(argv[0])); NsfAsmJump(PTR2INT(argv[0])); return TCL_OK; } static int asmJumpTrue(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; if (proc->status) { //fprintf(stderr, "asmJumpTrue jump oc %d instructionIndex %d\n", argc, PTR2INT(argv[0])); NsfAsmJump(PTR2INT(argv[0])); } else { //fprintf(stderr, "asmJumpTrue fall through\n"); NsfAsmJumpNext(); } return TCL_OK; } static int asmLeInt(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; { int value1, value2; value1 = PTR2INT(proc->slots[PTR2INT(argv[0])]); value2 = PTR2INT(proc->slots[PTR2INT(argv[1])]); proc->status = value1 <= value2; } return TCL_OK; } static int asmLeIntObj(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; { int value1, value2; Tcl_Obj *obj; //fprintf(stderr, "leIntObj oc %d op1 %p op2 %p\n", argc, argv[0], argv[1]); // for the time being, we compare two int values obj = proc->slots[PTR2INT(argv[0])]; if (likely(obj->typePtr == Nsf_OT_intType)) { value1 = obj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, obj, &value1); } obj = proc->slots[PTR2INT(argv[1])]; if (likely(obj->typePtr == Nsf_OT_intType)) { value2 = obj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, obj, &value2); } //fprintf(stderr, "asmLeScalar oc %d op1 %d op2 %d => %d\n", argc, value1, value2, value1 <= value2); proc->status = value1 <= value2; } return TCL_OK; } static int asmMethodDelegateDispatch(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { int result; { Tcl_Command cmd = NULL; NsfObject *object; // obj and method are unresolved result = GetObjectFromObj(interp, argv[0], &object); if (likely(clientData != NULL)) { cmd = clientData; } else { cmd = Tcl_GetCommandFromObj(interp, argv[1]); } //fprintf(stderr, "cmd %p object %p\n", cmd, object); result = MethodDispatch(object, interp, argc-1, argv+1, cmd, object, NULL, ObjStr(argv[1]), 0, 0); } return result; } static int asmMethodDelegateDispatch11(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { int result; // obj and method are resolved { AsmResolverInfo *resInfo = clientData; result = MethodDispatch(resInfo->object, interp, argc-1, argv+1, resInfo->cmd, resInfo->object, NULL, ObjStr(argv[1]), 0, 0); } return result; } static int asmMethodSelfCmdDispatch(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { int result; { AsmResolverInfo *resInfo = clientData; assert(resInfo->cmd != NULL); result = Tcl_NRCallObjProc(interp, Tcl_Command_objProc(resInfo->cmd), resInfo->proc->currentObject, argc, argv); } return result; } static int asmMethodSelfDispatch(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { int result; { AsmResolverInfo *resInfo = clientData; Tcl_Command cmd = resInfo->cmd ? resInfo->cmd : Tcl_GetCommandFromObj(interp, argv[0]); result = MethodDispatch(resInfo->proc->currentObject, interp, argc, argv, cmd, resInfo->proc->currentObject, NULL, ObjStr(argv[0]), 0, 0); } return result; } static int asmNoop(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { return TCL_OK; } static int asmSelf(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; Tcl_SetObjResult(interp, proc->currentObject->cmdName); return TCL_OK; } static int asmSetInt(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; proc->slots[PTR2INT(argv[0])] = argv[1]; return TCL_OK; } static int asmSetObj(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; //fprintf(stderr, "setObj var[%d] = %s\n", PTR2INT(argv[0]), ObjStr(argv[1])); proc->slots[PTR2INT(argv[0])] = argv[1]; return TCL_OK; } static int asmSetObjToResult(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; //fprintf(stderr, "setObjToResult var[%d] = %s\n", PTR2INT(argv[0]), ObjStr(argv[1])); proc->slots[PTR2INT(argv[0])] = Tcl_GetObjResult(interp); return TCL_OK; } static int asmSetResult(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; Tcl_SetObjResult(interp, proc->slots[PTR2INT(argv[0])]); return TCL_OK; } static int asmSetResultInt(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { AsmCompiledProc *proc = clientData; Tcl_SetObjResult(interp, Tcl_NewIntObj(PTR2INT(proc->slots[PTR2INT(argv[0])]))); return TCL_OK; } static int asmStoreResult(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *argv[]) { argv[0] = Tcl_GetObjResult(interp); Tcl_IncrRefCount(argv[0]); return TCL_OK; } ; /* *---------------------------------------------------------------------- * AsmExecute -- * * Define the execution engine for the code * *---------------------------------------------------------------------- */ static int AsmExecute(ClientData cd, Tcl_Interp *interp, AsmCompiledProc *proc, int argc, Tcl_Obj *CONST argv[]) { //AsmInstruction *ip; int i, result; #if 0 Var *compiledLocals; compiledLocals = ((Interp *) interp)->varFramePtr->compiledLocals; if (compiledLocals) { fprintf(stderr, "compiledLocals = %p\n", compiledLocals); } #endif /* * Place a copy of the actual argument into locals. */ for (i=1; i < argc; i++) { proc->locals[i-1] = argv[i]; } /* * Update all references to compiled arguments. */ for (i=0; i < proc->nrAsmArgReferences; i++) { AsmArgReference *arPtr = &proc->argReferences[i]; *(arPtr->objPtr) = proc->locals[arPtr->argNr]; } /* * Set the instruction pointer to the begin of the code. */ proc->ip = proc->code; //fprintf(stderr, "ip %p\n", proc->ip); while (*proc->ip->cmd) { //fprintf(stderr, "will execute instruction ip %p cmd %p %p/%d\n", ip, ip->cmd, ip->argv[0], ip->argc); //if (ip->cmd == tclFormat) {AsmInstructionPrint(ip);} //if (ip->cmd == (Tcl_ObjCmdProc*)tclDispatch) {AsmInstructionPrint(ip);} result = (*proc->ip->cmd)(proc->ip->clientData, interp, proc->ip->argc, proc->ip->argv); /*fprintf(stderr, "%s returned <%s> (%d)\n", Tcl_GetString(ip->argv[0]), Tcl_GetString(Tcl_GetObjResult(interp)), result);*/ if (unlikely(result != TCL_OK)) break; proc->ip++; //fprintf(stderr, "ip %p\n", proc->ip); } return result; } generic/asm/nsfAsmExecuteLabelThreading.c000066400000000000000000000216061242365656200207760ustar00rootroot00000000000000 enum instructionIdx { IDX_objProc, IDX_asmEval, IDX_asmDuplicateObj, IDX_asmIncrInt, IDX_asmIncrObj, IDX_asmJump, IDX_asmJumpTrue, IDX_asmLeInt, IDX_asmLeIntObj, IDX_asmMethodDelegateDispatch, IDX_asmMethodDelegateDispatch11, IDX_asmMethodSelfCmdDispatch, IDX_asmMethodSelfDispatch, IDX_asmNoop, IDX_asmSelf, IDX_asmSetInt, IDX_asmSetObj, IDX_asmSetObjToResult, IDX_asmSetResult, IDX_asmSetResultInt, IDX_asmStoreResult, IDX_NULL }; /* *---------------------------------------------------------------------- * AsmExecute -- * * Define the execution engine for the code * *---------------------------------------------------------------------- */ int AsmExecute(ClientData cd, Tcl_Interp *interp, AsmCompiledProc *proc, int argc, Tcl_Obj *CONST argv[]) { int i, result = TCL_OK; AsmInstruction *ip; static void *instructionLabel[] = { &&INST_objProc, &&INST_asmEval, &&INST_asmDuplicateObj, &&INST_asmIncrInt, &&INST_asmIncrObj, &&INST_asmJump, &&INST_asmJumpTrue, &&INST_asmLeInt, &&INST_asmLeIntObj, &&INST_asmMethodDelegateDispatch, &&INST_asmMethodDelegateDispatch11, &&INST_asmMethodSelfCmdDispatch, &&INST_asmMethodSelfDispatch, &&INST_asmNoop, &&INST_asmSelf, &&INST_asmSetInt, &&INST_asmSetObj, &&INST_asmSetObjToResult, &&INST_asmSetResult, &&INST_asmSetResultInt, &&INST_asmStoreResult, &&INST_NULL }; /* * Place a copy of the actual argument into locals. */ for (i=1; i < argc; i++) { proc->locals[i-1] = argv[i]; } /* * Update all references to compiled arguments. */ for (i=0; i < proc->nrAsmArgReferences; i++) { AsmArgReference *arPtr = &proc->argReferences[i]; *(arPtr->objPtr) = proc->locals[arPtr->argNr]; } /* * Set the instruction pointer to the begin of the code. */ ip = proc->code; proc->status = 0; //fprintf(stderr, "AsmExecute jumps to %p\n", ip); goto *instructionLabel[ip->labelIdx]; INST_NULL: return result; EXEC_RESULT_CODE_HANDLER: if (likely(result == TCL_OK)) { ip++; goto *instructionLabel[ip->labelIdx]; } else { return result; } INST_objProc: result = (*ip->cmd)(ip->clientData, interp, ip->argc, ip->argv); goto EXEC_RESULT_CODE_HANDLER; INST_asmEval: result = Tcl_EvalObjv(interp, ip->argc, ip->argv, 0); goto EXEC_RESULT_CODE_HANDLER; ip++; goto *instructionLabel[ip->labelIdx]; INST_asmDuplicateObj: { int indexValue = PTR2INT(ip->argv[0]); //fprintf(stderr, "duplicateObj var[%d] = %s\n", indexValue, ObjStr(ip->argv[1])); if (proc->slots[indexValue]) { Tcl_DecrRefCount(proc->slots[indexValue]); } proc->slots[indexValue] = Tcl_DuplicateObj(ip->argv[1]); Tcl_IncrRefCount(proc->slots[indexValue]); proc->slotFlags[indexValue] |= ASM_SLOT_MUST_DECR; } ip++; goto *instructionLabel[ip->labelIdx]; INST_asmIncrInt: { int intValue, incrValue; //fprintf(stderr, "incrInt var[%d] incr var[%d]\n", PTR2INT(ip->argv[0]), PTR2INT(ip->argv[1])); intValue = PTR2INT(proc->slots[PTR2INT(ip->argv[0])]); incrValue = PTR2INT(proc->slots[PTR2INT(ip->argv[1])]); //fprintf(stderr, ".... intValue %d incr Value %d\n", intValue, incrValue); proc->slots[PTR2INT(ip->argv[0])] = INT2PTR(intValue + incrValue); //fprintf(stderr, ".... [%d] => %d\n", PTR2INT(ip->argv[0]), intValue + incrValue); } ip++; goto *instructionLabel[ip->labelIdx]; INST_asmIncrObj: { int intValue, incrValue; Tcl_Obj *intObj, *incrObj; //fprintf(stderr, "asmIncrScalar var[%d] incr var[%d], ", PTR2INT(ip->argv[0]), PTR2INT(ip->argv[1])); intObj = proc->slots[PTR2INT(ip->argv[0])]; incrObj = proc->slots[PTR2INT(ip->argv[1])]; if (likely(intObj->typePtr == Nsf_OT_intType)) { intValue = intObj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, intObj, &intValue); } if (likely(incrObj->typePtr == Nsf_OT_intType)) { incrValue = incrObj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, incrObj, &incrValue); } //fprintf(stderr, "%d + %d = %d,", intValue, incrValue, intValue + incrValue); Tcl_InvalidateStringRep(intObj); intObj->internalRep.longValue = (long)(intValue + incrValue); //fprintf(stderr, "updated %p var[%d] %p\n", intObj, PTR2INT(ip->argv[0]), proc->slots[PTR2INT(ip->argv[0])]); //Tcl_SetObjResult(interp, intObj); } ip++; goto *instructionLabel[ip->labelIdx]; INST_asmJump: //fprintf(stderr, "asmJump oc %d instructionIndex %d\n", ip->argc, PTR2INT(ip->argv[0])); NsfAsmJump(PTR2INT(ip->argv[0])); goto *instructionLabel[ip->labelIdx]; INST_asmJumpTrue: if (proc->status) { //fprintf(stderr, "asmJumpTrue jump oc %d instructionIndex %d\n", ip->argc, PTR2INT(ip->argv[0])); NsfAsmJump(PTR2INT(ip->argv[0])); } else { //fprintf(stderr, "asmJumpTrue fall through\n"); NsfAsmJumpNext(); } goto *instructionLabel[ip->labelIdx]; INST_asmLeInt: { int value1, value2; value1 = PTR2INT(proc->slots[PTR2INT(ip->argv[0])]); value2 = PTR2INT(proc->slots[PTR2INT(ip->argv[1])]); proc->status = value1 <= value2; } ip++; goto *instructionLabel[ip->labelIdx]; INST_asmLeIntObj: { int value1, value2; Tcl_Obj *obj; //fprintf(stderr, "leIntObj oc %d op1 %p op2 %p\n", ip->argc, ip->argv[0], ip->argv[1]); // for the time being, we compare two int values obj = proc->slots[PTR2INT(ip->argv[0])]; if (likely(obj->typePtr == Nsf_OT_intType)) { value1 = obj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, obj, &value1); } obj = proc->slots[PTR2INT(ip->argv[1])]; if (likely(obj->typePtr == Nsf_OT_intType)) { value2 = obj->internalRep.longValue; } else { Tcl_GetIntFromObj(interp, obj, &value2); } //fprintf(stderr, "asmLeScalar oc %d op1 %d op2 %d => %d\n", ip->argc, value1, value2, value1 <= value2); proc->status = value1 <= value2; } ip++; goto *instructionLabel[ip->labelIdx]; INST_asmMethodDelegateDispatch: { Tcl_Command cmd = NULL; NsfObject *object; // obj and method are unresolved result = GetObjectFromObj(interp, ip->argv[0], &object); if (likely(ip->clientData != NULL)) { cmd = ip->clientData; } else { cmd = Tcl_GetCommandFromObj(interp, ip->argv[1]); } //fprintf(stderr, "cmd %p object %p\n", cmd, object); result = MethodDispatch(object, interp, ip->argc-1, ip->argv+1, cmd, object, NULL, ObjStr(ip->argv[1]), 0, 0); } goto EXEC_RESULT_CODE_HANDLER; ip++; goto *instructionLabel[ip->labelIdx]; INST_asmMethodDelegateDispatch11: // obj and method are resolved { AsmResolverInfo *resInfo = ip->clientData; result = MethodDispatch(resInfo->object, interp, ip->argc-1, ip->argv+1, resInfo->cmd, resInfo->object, NULL, ObjStr(ip->argv[1]), 0, 0); } goto EXEC_RESULT_CODE_HANDLER; ip++; goto *instructionLabel[ip->labelIdx]; INST_asmMethodSelfCmdDispatch: { AsmResolverInfo *resInfo = ip->clientData; assert(resInfo->cmd != NULL); result = Tcl_NRCallObjProc(interp, Tcl_Command_objProc(resInfo->cmd), resInfo->proc->currentObject, ip->argc, ip->argv); } goto EXEC_RESULT_CODE_HANDLER; ip++; goto *instructionLabel[ip->labelIdx]; INST_asmMethodSelfDispatch: { AsmResolverInfo *resInfo = ip->clientData; Tcl_Command cmd = resInfo->cmd ? resInfo->cmd : Tcl_GetCommandFromObj(interp, ip->argv[0]); result = MethodDispatch(resInfo->proc->currentObject, interp, ip->argc, ip->argv, cmd, resInfo->proc->currentObject, NULL, ObjStr(ip->argv[0]), 0, 0); } goto EXEC_RESULT_CODE_HANDLER; ip++; goto *instructionLabel[ip->labelIdx]; INST_asmNoop: ip++; goto *instructionLabel[ip->labelIdx]; INST_asmSelf: Tcl_SetObjResult(interp, proc->currentObject->cmdName); ip++; goto *instructionLabel[ip->labelIdx]; INST_asmSetInt: proc->slots[PTR2INT(ip->argv[0])] = ip->argv[1]; ip++; goto *instructionLabel[ip->labelIdx]; INST_asmSetObj: //fprintf(stderr, "setObj var[%d] = %s\n", PTR2INT(ip->argv[0]), ObjStr(ip->argv[1])); proc->slots[PTR2INT(ip->argv[0])] = ip->argv[1]; ip++; goto *instructionLabel[ip->labelIdx]; INST_asmSetObjToResult: //fprintf(stderr, "setObjToResult var[%d] = %s\n", PTR2INT(ip->argv[0]), ObjStr(ip->argv[1])); proc->slots[PTR2INT(ip->argv[0])] = Tcl_GetObjResult(interp); ip++; goto *instructionLabel[ip->labelIdx]; INST_asmSetResult: Tcl_SetObjResult(interp, proc->slots[PTR2INT(ip->argv[0])]); ip++; goto *instructionLabel[ip->labelIdx]; INST_asmSetResultInt: Tcl_SetObjResult(interp, Tcl_NewIntObj(PTR2INT(proc->slots[PTR2INT(ip->argv[0])]))); ip++; goto *instructionLabel[ip->labelIdx]; INST_asmStoreResult: ip->argv[0] = Tcl_GetObjResult(interp); Tcl_IncrRefCount(ip->argv[0]); ip++; goto *instructionLabel[ip->labelIdx]; } generic/asm/nsfAssemble.c000066400000000000000000000347751242365656200157130ustar00rootroot00000000000000/* * nsfAssemble.c -- * * Support for the experimental assemble feature. This file is * only included in the source when NSF_ASSEMBLE is turned on. * * Copyright (C) 2011-2014 Gustaf Neumann */ //#define LABEL_THREADING #if defined(LABEL_THREADING) typedef void (* InstLabel)(); #endif #define ASM_INFO_DECL 0x0001 #define ASM_INFO_PAIRS 0x0002 #define ASM_INFO_SKIP1 0x0004 #define ASM_SLOT_MUST_DECR 0x0001 #define ASM_SLOT_IS_INTEGER 0x0010 typedef struct AsmStatementInfo { int flags; CONST char **argTypes; int minArgs; int maxArgs; int cArgs; } AsmStatementInfo; typedef struct AsmInstruction { Tcl_ObjCmdProc *cmd; ClientData clientData; int argc; Tcl_Obj **argv; #if defined(LABEL_THREADING) int labelIdx; #endif } AsmInstruction; typedef struct AsmArgReference { int argNr; Tcl_Obj **objPtr; } AsmArgReference; #define NSF_ASM_NR_STATIC_SLOTS 30 typedef struct AsmCompiledProc { struct AsmInstruction *ip; /* pointer to the next writable instruction */ struct AsmInstruction *code; NsfObject *currentObject; int status; int nrLocals; Tcl_Obj **firstObj; /* pointer to staticObjs */ Tcl_Obj **locals; /* pointer to staticObjs */ Tcl_Obj **slots; /* pointer to staticObjs */ int slotFlags[NSF_ASM_NR_STATIC_SLOTS]; /* same size as allocated slots */ Tcl_Obj *staticObjs[NSF_ASM_NR_STATIC_SLOTS]; // static objs for the time being TODO overflows/dynamic int nrAsmArgReferences; struct AsmArgReference argReferences[10]; // for the time being TODO overflows/dynamic } AsmCompiledProc; typedef struct AsmPatches { int targetAsmInstruction; int sourceAsmInstruction; int argvIndex; } AsmPatches; typedef struct AsmProcClientData { NsfObject *object; /* common field of TclCmdClientData */ AsmCompiledProc *proc; NsfParamDefs *paramDefs; int with_ad; int with_checkAlways; } AsmProcClientData; typedef struct AsmResolverInfo { Tcl_Command cmd; NsfObject *object; AsmCompiledProc *proc; } AsmResolverInfo; #if defined(LABEL_THREADING) # define AsmInstructionNew(proc, instruction, argc) AsmInstructionNewLT(proc, IDX_ ## instruction, argc) # define AsmInstructionSetCmd(inst, name) inst->labelIdx = IDX_ ## name # define NsfAsmJump(index) ip = &proc->code[(index)] # define NsfAsmJumpNext() ip++ AsmInstruction *AsmInstructionNewLT(AsmCompiledProc *proc, int labelIdx, int argc) { proc->ip->labelIdx = labelIdx; proc->ip->cmd = NULL; proc->ip->argc = argc; proc->ip->argv = proc->firstObj; proc->firstObj += argc; return proc->ip++; } #else # define AsmInstructionNew(proc, instruction, argc) AsmInstructionNewCT(proc, (Tcl_ObjCmdProc*)instruction, argc) # define AsmInstructionSetCmd(inst, name) inst->cmd = (Tcl_ObjCmdProc*)name # define NsfAsmJump(index) proc->ip = &proc->code[(index)-1] # define NsfAsmJumpNext() AsmInstruction *AsmInstructionNewCT(AsmCompiledProc *proc, Tcl_ObjCmdProc *objProc, int argc) { proc->ip->cmd = objProc; proc->ip->argc = argc; proc->ip->argv = proc->firstObj; proc->firstObj += argc; return proc->ip++; } #endif void AsmLocalsAlloc(AsmCompiledProc *proc, int nrLocals) { proc->nrLocals = nrLocals; proc->firstObj += nrLocals; } void AsmArgSet(AsmCompiledProc *proc, int argNr, Tcl_Obj **addr) { AsmArgReference *arPtr = &proc->argReferences[proc->nrAsmArgReferences]; arPtr->argNr = argNr; arPtr->objPtr = addr; proc->nrAsmArgReferences ++; } void AsmInstructionPrint(AsmInstruction *ip) { int i; fprintf(stderr, "(%d) ", ip->argc); for (i=0; iargc; i++) {fprintf(stderr, "%s ", ObjStr(ip->argv[i]));} fprintf(stderr, "\n"); } /* *---------------------------------------------------------------------- * AsmExecute, AsmAssemble -- * * Define the execution engine for the code * *---------------------------------------------------------------------- */ #define NR_PAIRS -1 #define NR_PAIRS1 -2 /* * Prototypes needed for the execution engines included below */ static int AsmInstructionArgvCheck(Tcl_Interp *interp, int from, int to, CONST char **argType, int nrObjs, int nrStatements, Tcl_Obj **wordOv, Tcl_Obj *lineObj); static void AsmInstructionArgvSet(Tcl_Interp *interp, int from, int to, int currentArg, AsmInstruction *inst, AsmCompiledProc *asmProc, Tcl_Obj **wordOv, int verbose); #if defined(LABEL_THREADING) # include "asm/nsfAsmExecuteLabelThreading.c" #else # include "asm/nsfAsmExecuteCallThreading.c" #endif #include "asm/nsfAsmAssemble.c" /* *---------------------------------------------------------------------- * AsmInstructionArgvCheck -- * * Check the argument types of a assemble statement. * *---------------------------------------------------------------------- */ static int AsmInstructionArgvCheck(Tcl_Interp *interp, int from, int to, CONST char **argType, int nrSlots, int nrStatements, Tcl_Obj **wordOv, Tcl_Obj *lineObj) { int j; for (j = from; j < to; j += 2) { int argIndex, typesIndex, intValue, result; //fprintf(stderr, "check arg type %s\n", ObjStr(wordOv[j])); result = Tcl_GetIndexFromObj(interp, wordOv[j], asmStatementArgType, "asm statement arg type", 0, &typesIndex); if (result != TCL_OK) { return NsfPrintError(interp, "Asm: unknown arg type %s, line '%s'", ObjStr(wordOv[j]), ObjStr(lineObj)); } result = Tcl_GetIndexFromObj(interp, wordOv[j], argType, "asm internal arg type", 0, &argIndex); if (result != TCL_OK) { return NsfPrintError(interp, "Asm: instruction argument has invalid type: '%s', line %s\n", ObjStr(wordOv[j]), ObjStr(lineObj)); } //fprintf(stderr, "check arg value %s\n", ObjStr(wordOv[j+1])); if (Tcl_GetIntFromObj(interp, wordOv[j+1], &intValue) != TCL_OK || intValue < 0) { return NsfPrintError(interp, "Asm: instruction argument of type %s must have numeric index >= 0," " got '%s', line '%s'", ObjStr(wordOv[j]), ObjStr(wordOv[j+1]), ObjStr(lineObj)); } if (( typesIndex == asmStatementArgTypeObjIdx || typesIndex == asmStatementArgTypeSlotIdx ) && intValue > nrSlots) { return NsfPrintError(interp, "Asm: instruction argument value must be less than %d," " got '%s', line '%s'", nrSlots, ObjStr(wordOv[j+1]), ObjStr(lineObj)); } /* we assume, that every declaration results in exactly one slot */ if ((typesIndex == asmStatementArgTypeInstructionIdx) && intValue > (nrStatements - nrSlots)) { return NsfPrintError(interp, "Asm: instruction argument value must be less than %d," " got '%s', line '%s'", nrStatements - nrSlots, ObjStr(wordOv[j+1]), ObjStr(lineObj)); } } return TCL_OK; } /* *---------------------------------------------------------------------- * AsmInstructionArgvSet -- * * Set argument to be passed to an instruction of the assemble * code. * *---------------------------------------------------------------------- */ static void AsmInstructionArgvSet(Tcl_Interp *interp, int from, int to, int currentArg, AsmInstruction *inst, AsmCompiledProc *asmProc, Tcl_Obj **wordOv, int verbose) { int j; for (j = from; j < to; j += 2, currentArg++) { int argIndex, intValue; Tcl_GetIndexFromObj(interp, wordOv[j], asmStatementArgType, "asm cmd arg type", 0, &argIndex); Tcl_GetIntFromObj(interp, wordOv[j+1], &intValue); if (verbose) { fprintf(stderr, "AsmInstructionArgvSet (type %d) arg[%d] := %s[%s]\n", argIndex, currentArg, ObjStr(wordOv[j]), ObjStr(wordOv[j+1])); } switch (argIndex) { case asmStatementArgTypeObjIdx: inst->argv[currentArg] = asmProc->slots[intValue]; break; case asmStatementArgTypeArgIdx: AsmArgSet(asmProc, intValue, &inst->argv[currentArg]); break; case asmStatementArgTypeResultIdx: inst->argv[currentArg] = NULL; break; case asmStatementArgTypeSlotIdx: case asmStatementArgTypeInstructionIdx: case asmStatementArgTypeIntIdx: inst->argv[currentArg] = INT2PTR(intValue); break; case asmStatementArgTypeVarIdx: fprintf(stderr, ".... var set [%d] = %s\n", currentArg, ObjStr(wordOv[j+1])); inst->argv[currentArg] = wordOv[j+1]; Tcl_IncrRefCount(inst->argv[currentArg]); // TODO: DECR missing break; } /*fprintf(stderr, "[%d] inst %p name %s arg[%d] %s\n", currentAsmInstruction, inst, ObjStr(inst->argv[0]), currentArg, inst->argv[currentArg] ? ObjStr(inst->argv[currentArg]) : "NULL");*/ } } /* *---------------------------------------------------------------------- * NsfAsmProcDeleteProc -- * * Tcl_CmdDeleteProc for NsfAsmProcDeleteProc. Is called, whenever a * NsfAsmProcDeleteProc is deleted and frees the associated client data. * * Results: * None. * * Side effects: * Frees client-data * *---------------------------------------------------------------------- */ static void NsfAsmProcDeleteProc(ClientData clientData) { AsmProcClientData *cd = clientData; /*fprintf(stderr, "NsfAsmProcDeleteProc received %p\n", clientData);*/ fprintf(stderr, "NsfAsmProcDeleteProc: TODO free asmProc\n"); if (cd->paramDefs) { /* tcd->paramDefs is freed by NsfProcDeleteProc() */ fprintf(stderr, "NsfAsmProcDeleteProc: TODO free paramDefs\n"); } FREE(AsmProcClientData, cd); } /* *---------------------------------------------------------------------- * NsfAsmProc -- * * Tcl_ObjCmdProc implementing Asm procs. This function processes * the argument list in accordance with the parameter definitions * and calls in case of success the asm proc body. * * Results: * Tcl return code. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern int NsfAsmProc(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { AsmProcClientData *cd = clientData; int result; assert(cd); assert(cd->proc); //fprintf(stderr, "NsfAsmProcStub %s is called, tcd %p object %p\n", ObjStr(objv[0]), cd, cd->object); if (likely(cd->paramDefs && cd->paramDefs->paramsPtr)) { ALLOC_ON_STACK(Tcl_Obj*, objc, tov); fprintf(stderr, "not implemented yet\n"); #if 0 { ParseContext *pcPtr; pcPtr = (ParseContext *) NsfTclStackAlloc(interp, sizeof(ParseContext), "parse context"); /* * We have to substitute the first element of objv with the name * of the function to be called. Since objv is immutable, we have * to copy the full argument vector and replace the element on * position [0] */ memcpy(tov, objv, sizeof(Tcl_Obj *)*(objc)); //tov[0] = tcd->procName; /* If the argument parsing is ok, the body will be called */ result = ProcessMethodArguments(pcPtr, interp, NULL, 0, cd->paramDefs, objv[0], objc, tov); if (likely(result == TCL_OK)) { result = InvokeShadowedProc(interp, cd->procName, cd->cmd, pcPtr); } else { /*Tcl_Obj *resultObj = Tcl_GetObjResult(interp); fprintf(stderr, "NsfProcStub: incorrect arguments (%s)\n", ObjStr(resultObj));*/ ParseContextRelease(pcPtr); NsfTclStackFree(interp, pcPtr, "release parse context"); } } #endif /*fprintf(stderr, "NsfProcStub free on stack %p\n", tov);*/ FREE_ON_STACK(Tcl_Obj *, tov); } else { int requiredArgs = cd->proc->slots - cd->proc->locals; //fprintf(stderr, "no compiled parameters\n"); if (unlikely(requiredArgs != objc-1)) { return NsfPrintError(interp, "wrong # of arguments"); } cd->proc->currentObject = cd->object; result = AsmExecute(NULL, interp, cd->proc, objc, objv); } return result; } static int NsfAsmProcAddParam(Tcl_Interp *interp, NsfParsedParam *parsedParamPtr, Tcl_Obj *nameObj, Tcl_Obj *bodyObj, int with_ad) { fprintf(stderr, "NsfAsmProcAddParam not implemented yet\n"); //CONST char *procName = ObjStr(nameObj); return TCL_OK; } static int NsfAsmProcAddArgs(Tcl_Interp *interp, Tcl_Obj *argumentsObj, Tcl_Obj *nameObj, Tcl_Obj *bodyObj, int with_ad, int with_checkAlways) { int argc, result; Tcl_Obj **argv; AsmCompiledProc *asmProc; AsmProcClientData *cd; CONST char *procName = ObjStr(nameObj); if (unlikely(Tcl_ListObjGetElements(interp, argumentsObj, &argc, &argv) != TCL_OK)) { return NsfPrintError(interp, "argument list invalid '%s'", ObjStr(argumentsObj)); } result = AsmAssemble(NULL, interp, nameObj, argc, bodyObj, &asmProc); if (unlikely(result != TCL_OK)) { return result; } cd = NEW(AsmProcClientData); cd->object = NULL; cd->proc = asmProc; cd->paramDefs = NULL; cd->with_ad = with_ad; cd->with_checkAlways = with_checkAlways ? NSF_ARGPARSE_CHECK : 0; Tcl_CreateObjCommand(interp, procName, NsfAsmProc, cd, NsfAsmProcDeleteProc); return TCL_OK; } /* cmd method::asmcreate NsfAsmMethodCreateCmd { {-argName "object" -required 1 -type object} {-argName "-checkalways" -required 0 -nrargs 0 -type switch} {-argName "-inner-namespace" -nrargs 0} {-argName "-per-object" -nrargs 0} {-argName "-reg-object" -required 0 -nrargs 1 -type object} {-argName "name" -required 1 -type tclobj} {-argName "arguments" -required 1 -type tclobj} {-argName "body" -required 1 -type tclobj} } */ static int NsfAsmMethodCreateCmd(Tcl_Interp *interp, NsfObject *defObject, int withInner_namespace, int withPer_object, NsfObject *regObject, Tcl_Obj *nameObj, Tcl_Obj *argumentsObj, Tcl_Obj *bodyObj) { int argc, result; Tcl_Obj **argv; AsmCompiledProc *asmProc; AsmProcClientData *cd; NsfClass *cl = (withPer_object || ! NsfObjectIsClass(defObject)) ? NULL : (NsfClass *)defObject; // not handled: // * withInner_namespace, // * regObject, // * pre and post-conditions // * withCheckAlways ? NSF_ARGPARSE_CHECK : 0 if (cl == 0) { RequireObjNamespace(interp, defObject); } if (unlikely(Tcl_ListObjGetElements(interp, argumentsObj, &argc, &argv) != TCL_OK)) { return NsfPrintError(interp, "argument list invalid '%s'", ObjStr(argumentsObj)); } result = AsmAssemble(NULL, interp, nameObj, argc, bodyObj, &asmProc); if (unlikely(result != TCL_OK)) { return result; } cd = NEW(AsmProcClientData); cd->proc = asmProc; cd->paramDefs = NULL; cd->with_ad = 0; if (cl == NULL) { result = NsfAddObjectMethod(interp, (Nsf_Object *)defObject, ObjStr(nameObj), (Tcl_ObjCmdProc *)NsfAsmProc, cd, NsfAsmProcDeleteProc, 0); } else { result = NsfAddClassMethod(interp, (Nsf_Class *)cl, ObjStr(nameObj), (Tcl_ObjCmdProc *)NsfAsmProc, cd, NsfAsmProcDeleteProc, 0); } return result; } generic/asm/threaded.c000066400000000000000000000126231242365656200152150ustar00rootroot00000000000000/* * threaded.c -- * * A small study on implementation techniques for threaded code. * * As example a small program stacking two values on to the stack * and adding the result is taken (example from wikipedia). This * program uses the abstract machine instructions PUSH_A, PUSH_B, * ADD, and END. These instructions are coded in different ways: * * * switch_threading(): realizing instructions as C enum * values, the program is represented as an array of enums, * placed in a large surrounding switch statement. * * * call_threading(): realizing instructions as C functions, the * program is represented an array of C functions. * * * label_threading(): realizing instructions as labels, the * program is represented an array of labels. * * The test show, that label_threading is clearly the winner in * terms of performance, but this depends on a non standard C * extension, which is implemented in at least three different * compilers (supported on GCC, clang and IBM's XL C/C++). It * would be interesting to define the instructions in some * e.g. scripting language and to produce different * implementations from the same source to address the * protability issue. * * Most probably, one needs a larger program-code with more * instructions to provide more meaningful results. * * Compile e.g. with: * cc -O3 -Wall threaded.c -o threaded * * Gustaf Neumann (Nov 2011) */ #include #include /* *---------------------------------------------------------------------- * DiffTime -- * * Compute the time difference between two timevals. * *---------------------------------------------------------------------- */ static void DiffTime(struct timeval *t1, struct timeval *t0, struct timeval *diffPtr) { diffPtr->tv_sec = t1->tv_sec - t0->tv_sec; diffPtr->tv_usec = t1->tv_usec - t0->tv_usec; if (diffPtr->tv_usec < 0) { diffPtr->tv_sec += (diffPtr->tv_usec / 1000000L) - 1; diffPtr->tv_usec = (diffPtr->tv_usec % 1000000L) + 1000000L; } else if (diffPtr->tv_usec > 1000000L) { diffPtr->tv_sec += diffPtr->tv_usec / 1000000L; diffPtr->tv_usec = diffPtr->tv_usec % 1000000L; } } /* *---------------------------------------------------------------------- * timeit -- * * Run a function multiple times and measure the performance. * *---------------------------------------------------------------------- */ static void timeit( void (* fn)() ) { struct timeval start, end, diff; int i; gettimeofday(&start, NULL); for (i=0; i<10000000; i++) { (*fn)(); } gettimeofday(&end, NULL); DiffTime(&end, &start, &diff); printf("%d seconds, %d usec\n", (int) diff.tv_sec, (int) diff.tv_usec); } int *sp; int stack[100]; /* *---------------------------------------------------------------------- * switch_threading -- * * Define an enum for the instructions and use a switch to select * the instructions. * *---------------------------------------------------------------------- */ void switch_threading() { typedef enum {INST_PUSH_A, INST_PUSH_B, INST_ADD, INST_END} InstEnum; static InstEnum mycode[] = {INST_PUSH_A, INST_PUSH_B, INST_ADD, INST_END}; int a, b; InstEnum *ip; ip = &mycode[0]; sp = &stack[0]; for (;;) { switch (*ip++) { case INST_PUSH_A: *sp++ = 100; continue; case INST_PUSH_B: *sp++ = 200; continue; case INST_ADD: a = *--sp; b = *--sp; *sp++ = a + b; continue; case INST_END: //fprintf(stderr, "end %d\n", *(sp-1)); return; } } } /* *---------------------------------------------------------------------- * call_threading -- * * Define for every instruction a function, the program consists of * an array of function pointers. * *---------------------------------------------------------------------- */ typedef void (* InstFn)(); void pushA () { *sp++ = 100; } void pushB () { *sp++ = 200; } void add () { int a = *--sp; int b = *--sp; *sp++ = a + b; } void call_threading() { static InstFn mycode[] = {&pushA, &pushB, &add, NULL}; InstFn *ip; sp = &stack[0]; ip = &mycode[0]; while (*ip) { (*ip++)(); } //fprintf(stderr, "end %d\n", *(sp-1)); } /* *---------------------------------------------------------------------- * label_threading -- * * Define for every instruction a label, the code is a sequence of * labels. This works with gcc, clang and IBM's XL C, but not on * every compiler. * *---------------------------------------------------------------------- */ typedef void (* InstLabel)(); void label_threading() { static InstLabel mycode[] = {&&INST_PUSH_A, &&INST_PUSH_B, &&INST_ADD, &&INST_END}; InstLabel *ip; int a, b; sp = &stack[0]; ip = &mycode[0]; INST_PUSH_A: *sp++ = 100; goto **ip++; INST_PUSH_B: *sp++ = 200; goto **ip++; INST_ADD: a = *--sp; b = *--sp; *sp++ = a + b; goto **ip++; INST_END: //fprintf(stderr, "end %d\n", *(sp-1)); return; } /* *---------------------------------------------------------------------- * main -- * * Just call the testcases with timing. * *---------------------------------------------------------------------- */ int main() { timeit( switch_threading ); timeit( call_threading ); timeit( label_threading ); return 0; } generic/gentclAPI.tcl000066400000000000000000000507331242365656200150270ustar00rootroot00000000000000#!/usr/bin/env tclsh # -*- Tcl -*- # # C-Code generator to generate stubs to handle all objv-parsing from # an simple interface definition language. This guarantees consistent # handling of input argument types, consistent error messages in case # of failures and eases documentation. # # Copyright (C) 2009-2014 Gustaf Neumann # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # set ::converter "" set ::objCmdProc "(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv \[\])" proc convertername {type typename} { if {[info exists ::registeredConverter($type)]} { set name $::registeredConverter($type) } else { set name [string totitle [string trimleft $typename -]] set ::registeredConverter($type) $name } return $name } proc createconverter {type typename} { set name [convertername $type $typename] if {[info exists ::createdConverter($name)]} { return "" } set domain [split $type |] set opts "static CONST char *opts\[\] = {\"[join $domain {", "}]\", NULL};" set ::createdConverter($name) "ConvertTo${name}, \"$type\"" set enums [list ${name}NULL] foreach d $domain {lappend enums $name[string totitle [string map [list - _] $d]]Idx} subst { enum ${name}Idx {[join $enums {, }]}; static int ConvertTo${name}(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; $opts (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "$typename", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } } } proc addFlags {flags_var new} { upvar $flags_var flags if {$flags eq "0"} {set flags $new} {append flags "|$new"} } proc genifd {parameterDefinitions} { #puts stderr $parameterDefinitions set l [list] foreach parameterDefinition $parameterDefinitions { array unset "" array set "" {-flags 0} array set "" $parameterDefinition switch $(-type) { "" {set type NULL} default {set type $(-type)} } set flags $(-flags) if {$(-required)} {addFlags flags "NSF_ARG_REQUIRED"} set argName $(-argName) switch -glob $type { "NULL" {set converter String} "boolean" {set converter Boolean} "switch" {set converter Boolean} "int" {set converter Integer} "int32" {set converter Int32} "class" {set converter Class} "object" {set converter Object} "tclobj" {set converter Tclobj} "args" {set converter Nothing} "allargs" - "virtualobjectargs" - "virtualclassargs" {set converter Nothing} "objpattern" {set converter Objpattern} *|* { if {![info exists (-typeName)]} {set (-typeName) $(-argName)} set converter [convertername $type $(-typeName)] append ::converter [createconverter $type $(-typeName)] set (-argName) $type addFlags flags "NSF_ARG_IS_ENUMERATION" } default { if {[info exists ::ptrConverter($type)]} { set converter Pointer } else { error "unknown type $type" } } } if {$converter in {Tclobj Integer Int32 Boolean String Class Object Pointer}} { set conv Nsf_ConvertTo_$converter } else { set conv ConvertTo$converter } switch -glob -- $(-type) { "*|*" - "tclobj" - "args" - "" {set typeString NULL} default { set typeString "\"$(-type)\"" } } lappend l "{\"$argName\", $flags, $(-nrargs), $conv, NULL,NULL,$typeString,NULL,NULL,NULL,NULL,NULL}" } if {[llength $l] == 0} { return "{NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}" } else { return [join $l ",\n "] } } proc gencall {methodName fn parameterDefinitions clientData cDefsVar ifDefVar arglistVar preVar postVar introVar nnVar cleanupVar } { upvar $cDefsVar cDefs $ifDefVar ifDef $arglistVar arglist $preVar pre $postVar post \ $introVar intro $nnVar nn $cleanupVar cleanup set c [list] set i 0 set pre ""; set post ""; set cleanup "" set intro "" switch $clientData { class { set a [list cl] set if [list "NsfClass *cl"] set argNum 3 append intro \ " NsfClass *cl = NsfObjectToClass(clientData);" \n\n \ " assert(objc > 0);" \n \ " if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, \"class\", ObjStr(objv\[0\]));" } object { set a [list obj] set if [list "NsfObject *obj"] set argNum 3 append intro \ " NsfObject *obj = (NsfObject *)clientData;" \n\n \ " assert(objc > 0);" \n \ " if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, \"object\", ObjStr(objv\[0\]));" } "" { append intro " (void)clientData;\n" set a [list] set if [list] set argNum 2 array set cd {arglist "" ifDefs ""} } } foreach parameterDefinition $parameterDefinitions { array set "" $parameterDefinition set ifSet 0 set cVar 1 set (-argName) [string map [list - _] $(-argName)] if {[regexp {^_(.*)$} $(-argName) _ switchName]} { # non positional args set varName with[string totitle $switchName] set calledArg $varName set type "int " if {$(-nrargs) == 1} { switch -glob $(-type) { "" {set type "CONST char *"} "class" {set type "NsfClass *"} "object" {set type "NsfObject *"} "tclobj" {set type "Tcl_Obj *"} "int" {set type "Tcl_Obj *"} "int32" {set type "int "} "*|*" {set type "int "} default {error "type '$(-type)' not allowed for parameter"} } } } else { set varName $(-argName) set calledArg $varName switch -glob $(-type) { "" {set type "CONST char *"} "boolean" {set type "int "} "int32" {set type "int "} "class" {set type "NsfClass *"} "object" {set type "NsfObject *"} "tclobj" {set type "Tcl_Obj *"} "virtualobjectargs" - "virtualclassargs" - "args" { set type "int " set calledArg "objc-pc.lastObjc, objv+pc.lastObjc" lappend if "int nobjc" "Tcl_Obj *CONST nobjv\[\]" set ifSet 1 set cVar 0 } "allargs" { set type "int " set calledArg "objc, objv" lappend if "int objc" "Tcl_Obj *CONST objv\[\]" set ifSet 1 set cVar 0 } "objpattern" { set type "Tcl_Obj *" lappend c "CONST char *${varName}String = NULL;" "NsfObject *${varName}Object = NULL;" set calledArg "${varName}String, ${varName}Object" lappend if "CONST char *${varName}String" "NsfObject *${varName}Object" set ifSet 1 append pre [subst -nocommands { if (GetMatchObject(interp, ${varName}, objc>$i ? objv[$i] : NULL, &${varName}Object, &${varName}String) == -1) { if (${varName}) { DECR_REF_COUNT2("patternObj", ${varName}); } return TCL_OK; } }] append post [subst -nocommands { if (${varName}) { DECR_REF_COUNT2("patternObj", ${varName}); } }] set cleanup [subst -nocommands {$type$varName = ($type)pc.clientData[$i];}] append cleanup \n$post # end of obj pattern } *|* {set type "int "} default { if {[info exists ::ptrConverter($(-type))]} { set type "$(-type) *" set varName "${varName}Ptr" set calledArg $varName if {$(-withObj)} { append calledArg [subst -nocommands {,pc.objv[$i]}] lappend if "$type$varName" "Tcl_Obj *$(-argName)Obj" set ifSet 1 } } else { error "type '$(-type)' not allowed for argument" } } } } if {[string match {*[*]*} $type] && $(-required)} { append nn " NSF_nonnull($argNum)" } if {!$ifSet} {lappend if "$type$varName"} if {$cVar} { if {$type eq "int "} { lappend c [subst -nocommands {$type$varName = ($type)PTR2INT(pc.clientData[$i]);}] } else { lappend c [subst -nocommands {$type$varName = ($type)pc.clientData[$i];}] } } lappend a $calledArg incr i incr argNum } set ifDef [join $if ", "] set cDefs [join $c "\n "] set arglist [join $a ", "] } proc genStub {stub intro obj idx cDefs pre call post cleanup} { # Tiny optimization for calls without parameters; # ParseContextExtendObjv() is just called for procs, so no need to # free non-static objvs. Actually, the api for c-methods does # not allow to generate structures which have to be freed. # we assert this in the code. if {$cDefs ne ""} { set releasePC "ParseContextRelease(&pc);" set releasePC "assert(pc.status == 0);" } else { set releasePC "" } return [subst -nocommands { static int ${stub}(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; $intro if (likely(ArgumentParse(interp, objc, objv, $obj, objv[0], method_definitions[$idx].paramDefs, method_definitions[$idx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { $cDefs $pre $releasePC $call $post } else { $cleanup return TCL_ERROR; } } }]} proc genSimpleStub {stub intro idx cDefs pre call post cleanup} { if {$cleanup ne ""} {error "$stub cleanup code '$cleanup' must be empty"} return [subst -nocommands { static int ${stub}(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { $intro $cDefs $pre $call $post } }]} proc implArgList {implementation prefix arglist} { if {$arglist ne ""} { return "${implementation}(${prefix}interp, $arglist)" } return "${implementation}(${prefix}interp)" } proc genstubs {} { set stubDecls "" set decls "" set enums [list] set ifds [list] foreach key [lsort [array names ::definitions]] { array set d $::definitions($key) lappend enums $d(idx) set nrParams [llength $d(parameterDefinitions)] set nn "" gencall $d(methodName) $d(stub) $d(parameterDefinitions) $d(clientData) \ cDefs ifDef arglist pre post intro nn cleanup # # Check, if spec tells us to pass the original "objv[0]" as an # argument. For unstacked entries this is the only way to # determine the name, under which the cmd was called. # if {[dict get $d(options) -objv0]} { append ifDef ", Tcl_Obj *objv0" append arglist ", objv\[0\]" } if {[dict get $::definitions($key) clientData] ne ""} { set stubNN "NSF_nonnull(1) " set NN " NSF_nonnull(2)" regsub \n\n $intro "\n\n assert(clientData);\n" intro } else { set stubNN "" set NN "" } set stubDecl "static int $d(stub)$::objCmdProc\n ${stubNN}NSF_nonnull(2) NSF_nonnull(4);\n" set ifd "{\"$d(ns)::$d(methodName)\", $d(stub), $nrParams, {\n [genifd $d(parameterDefinitions)]}\n}" append decls "static int [implArgList $d(implementation) {Tcl_Interp *} $ifDef]\n NSF_nonnull(1)${NN}${nn};\n" if {$post ne ""} { append cDefs "\n int returnCode;" set call "returnCode = [implArgList $d(implementation) {} $arglist];" set post [string trimright $post] append post "\n return returnCode;" } else { set call "return [implArgList $d(implementation) {} $arglist];" } #if {$nrParams == 1} { puts stderr "$d(stub) => '$arglist' cDefs=$cDefs ifd=$ifDef" } if {$nrParams == 1 && $arglist eq "objc, objv"} { # TODO we would not need to generate a stub at all.... #set ifd "{\"$d(ns)::$d(methodName)\", $d(implementation), $nrParams, {\n [genifd $d(parameterDefinitions)]}\n}" append fns [genSimpleStub $d(stub) $intro $d(idx) $cDefs $pre $call $post $cleanup] } elseif {$nrParams == 1 && $arglist eq "obj, objc, objv"} { # no need to call objv parser #puts stderr "$d(stub) => '$arglist'" append fns [genSimpleStub $d(stub) $intro $d(idx) $cDefs $pre $call $post $cleanup] } elseif {$nrParams == 0} { append pre [subst -nocommands { if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[$d(idx)].paramDefs, NULL, objv[0]); } }] append fns [genSimpleStub $d(stub) $intro $d(idx) $cDefs $pre $call $post $cleanup] } elseif {$nrParams == 1 && [string match "Tcl_Obj *" $cDefs]} { array set defs [list -required 0] array set defs [lindex $d(parameterDefinitions) 0] if {$defs(-required)} { set op "objc != 2" set newArg {objv[1]} } else { set op "objc < 1 || objc > 2" set newArg {objc == 2 ? objv[1] : NULL} } append pre [subst -nocommands { if ($op) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[$d(idx)].paramDefs, NULL, objv[0]); } }] if {[regexp {^(.*),(.*)$} $arglist _ arg1]} { set newArglist "$arg1, $newArg" } else { set newArglist $newArg } regsub ", $arglist\\)" $call ", $newArglist\)" call append fns [genSimpleStub $d(stub) $intro $d(idx) "" $pre $call $post $cleanup] } else { switch $d(methodType) { objectMethod {set obj "obj"} classMethod {set obj "(NsfObject *) cl"} default {set obj "NULL"} } append fns [genStub $d(stub) $intro $obj $d(idx) $cDefs $pre $call $post $cleanup] } lappend ifds $ifd append stubDecls $stubDecl } puts $::converter set entries [list] foreach c [array names ::createdConverter] {lappend entries "\{$::createdConverter($c)\}"} if {[llength $entries]>0} { puts [subst { static Nsf_EnumeratorConverterEntry enumeratorConverterEntries\[\] = { [join $entries ",\n "], {NULL, NULL} }; }] } set nrIfds [expr {[llength $ifds]+1}] puts [subst -nocommands { /* just to define the symbol */ static Nsf_methodDefinition method_definitions[$nrIfds]; }] set namespaces [list] foreach {key value} [array get ::ns] { # no need to create the ::nsf namespace if {$value eq "::nsf"} continue lappend namespaces "\"$value\"" } set namespaceString [join $namespaces ",\n "] puts "static CONST char *method_command_namespace_names\[\] = {\n $namespaceString\n};" puts $stubDecls puts $decls set enumString [join $enums ",\n "] puts "enum {\n $enumString\n} NsfMethods;\n" puts $fns set definitionString [join $ifds ",\n"] puts "static Nsf_methodDefinition method_definitions\[$nrIfds\] = \{\n$definitionString,\{NULL\}\n\};\n" } proc methodDefinition {methodName methodType implementation parameterDefinitions options} { array set opts [list -ns $::ns($methodType) -nxdoc 0 -objv0 0] array set opts $options set d(methodName) $methodName set d(implementation) $implementation set d(stub) ${implementation}Stub set d(idx) ${implementation}Idx set d(methodType) $methodType set d(ns) $opts(-ns) set d(options) [array get opts] switch $methodType { classMethod {set d(clientData) class} objectMethod {set d(clientData) object} default {set d(clientData) ""} } set completed [list] foreach parameterDefinition $parameterDefinitions { array unset "" array set "" {-required 0 -nrargs 1 -type "" -withObj 0} array set "" $parameterDefinition lappend completed [array get ""] } set d(parameterDefinitions) $completed set ::definitions($d(methodType)-$d(implementation)-$d(methodName)) [array get d] puts $::nxdocIndex [list set ::nxdoc::include($d(ns)::$d(methodName)) $opts(-nxdoc)] } proc checkMethod {methodName implementation parameterDefinitions {options ""}} { methodDefinition type=$methodName checkMethod $implementation $parameterDefinitions $options } proc classMethod {methodName implementation parameterDefinitions {options ""}} { methodDefinition $methodName classMethod $implementation $parameterDefinitions $options } proc objectMethod {methodName implementation parameterDefinitions {options ""}} { methodDefinition $methodName objectMethod $implementation $parameterDefinitions $options } proc objectInfoMethod {methodName implementation parameterDefinitions {options ""}} { lappend options -ns $::ns(objectInfoMethod) methodDefinition $methodName objectMethod $implementation $parameterDefinitions $options } proc classInfoMethod {methodName implementation parameterDefinitions {options ""}} { lappend options -ns $::ns(classInfoMethod) methodDefinition $methodName classMethod $implementation $parameterDefinitions $options } proc cmd {methodName implementation parameterDefinitions {options ""}} { methodDefinition $methodName cmd $implementation $parameterDefinitions $options } if {[llength $argv] == 1} {set decls $argv} {set decls generic/gentclAPI.decls} set ::nxdocIndex [open [file root $decls].nxdocindex w] source $decls close $::nxdocIndex puts { /* * This source code file was generated by the C-code generator gentclAPI.tcl, * part of the Next Scripting Framework. */ #if defined(USE_NSF_STUBS) int Nsf_ConvertTo_Boolean(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToBoolean(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Class(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToClass(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Int32(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToInt32(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Integer(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToInteger(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Object(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToObject(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Pointer(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToPointer(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_String(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToString(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Tclobj(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToTclobj(interp, objPtr, pPtr, clientData, outObjPtr); } #else # define Nsf_ConvertTo_Boolean Nsf_ConvertToBoolean # define Nsf_ConvertTo_Class Nsf_ConvertToClass # define Nsf_ConvertTo_Int32 Nsf_ConvertToInt32 # define Nsf_ConvertTo_Integer Nsf_ConvertToInteger # define Nsf_ConvertTo_Object Nsf_ConvertToObject # define Nsf_ConvertTo_Pointer Nsf_ConvertToPointer # define Nsf_ConvertTo_String Nsf_ConvertToString # define Nsf_ConvertTo_Tclobj Nsf_ConvertToTclobj #endif #if !defined(likely) # if defined(__GNUC__) && __GNUC__ > 2 /* Use gcc branch prediction hint to minimize cost of e.g. DTrace * ENABLED checks. */ # define unlikely(x) (__builtin_expect((x), 0)) # define likely(x) (__builtin_expect((x), 1)) # else # define unlikely(x) (x) # define likely(x) (x) # endif #endif } genstubs puts stderr "[array size ::definitions] parsing stubs generated" # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: generic/mk_predefined.tcl000066400000000000000000000037151242365656200160130ustar00rootroot00000000000000#!/usr/bin/env tclsh # # A small script file that creates a static array from a Tcl- # script for inclusion in c programs # # Copyright (C) 2010-2014 Gustaf Neumann # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. set f [open [lindex $argv 0]] set content [read $f] close $f regsub -all {\\} $content && content regsub -all {"} $content {\"} content ;#" regsub -all "\[ \]+\n" $content \n content ;# remove trailing space regsub -all "\n\[ \t\]+" $content \n content ;# remove leading space while {[regsub -all "\n#\[^\n\]*\n" $content \n content]>0} { ;# remove comment lines } regsub -all "\n#\[^\n\]*\n" $content \n content ;# remove comment lines regsub -all "\[\n\]+" $content \n content ;# remove empty lines regsub -all "\n}" $content "}" content ;# newlines btwn braces regsub -all "\n" $content "\\n\"\n\"" content puts "/* Generated by mk_predefined.tcl */" puts "static char cmd\[\] = " puts "\"$content\";" puts "" generic/nsf.c000066400000000000000000035136611242365656200134560ustar00rootroot00000000000000/* * nsf.c -- * * Basic Machinery of the Next Scripting Framework, a Tcl-based framework * for supporting language-oriented programming. For details, see * http://next-scripting.org/. * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * This software is based upon MIT Object Tcl by David Wetherall and * Christopher J. Lindblad, that contains the following copyright * message: * * "Copyright 1993 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty." */ #define NSF_FORWARD_WITH_ONERROR 1 #define NSF_C 1 #include "nsfInt.h" #include "nsfAccessInt.h" #ifdef COMPILE_NSF_STUBS # if defined(PRE86) EXTERN NsfStubs nsfStubs; # else MODULE_SCOPE const NsfStubs nsfStubs; # endif #endif #ifdef USE_TCL_STUBS # define Nsf_ExprObjCmd(clientData, interp, objc, objv) \ NsfCallCommand(interp, NSF_EXPR, objc, objv) #else # define Nsf_ExprObjCmd(clientData, interp, objc, objv) \ Tcl_ExprObjCmd(clientData, interp, objc, objv) #endif /* * Call Stack specific definitions */ typedef enum { CALLING_LEVEL, ACTIVE_LEVEL } CallStackLevel; typedef struct callFrameContext { int frameSaved; Tcl_CallFrame *framePtr; Tcl_CallFrame *varFramePtr; } callFrameContext; typedef struct NsfProcContext { ClientData oldDeleteData; Tcl_CmdDeleteProc *oldDeleteProc; NsfParamDefs *paramDefs; int checkAlwaysFlag; } NsfProcContext; /* * TclCmdClientdata is an incomplete type containing the common * field(s) of ForwardCmdClientData, AliasCmdClientData and * SetterCmdClientData used for filling in at runtime the actual * object. */ typedef struct TclCmdClientData { NsfObject *object; } TclCmdClientData; typedef struct SetterCmdClientData { NsfObject *object; Nsf_Param *paramsPtr; } SetterCmdClientData; typedef struct ForwardCmdClientData { NsfObject *object; Tcl_Obj *cmdName; Tcl_ObjCmdProc *objProc; ClientData clientData; int passthrough; int needobjmap; int verbose; int hasNonposArgs; int nr_args; Tcl_Obj *args; int frame; #if defined(NSF_FORWARD_WITH_ONERROR) Tcl_Obj *onerror; #endif Tcl_Obj *prefix; int nr_subcommands; Tcl_Obj *subcommands; } ForwardCmdClientData; typedef struct AliasCmdClientData { NsfObject *object; Tcl_Obj *cmdName; Tcl_ObjCmdProc *objProc; ClientData clientData; NsfClass *class; Tcl_Interp *interp; Tcl_Command aliasedCmd; Tcl_Command aliasCmd; } AliasCmdClientData; /* * When NSF_MEM_COUNT is set, we want to trace as well the mem-count frees * associated with the interp. Therefore, we need in this case a special * client data structure. */ #ifdef NSF_MEM_COUNT typedef struct NsfNamespaceClientData { NsfObject *object; Tcl_Namespace *nsPtr; Tcl_Interp *interp; } NsfNamespaceClientData; #endif /* * Argv parsing specific definitions */ #define PARSE_CONTEXT_PREALLOC 20 typedef struct { int status; ClientData *clientData; /* 4 members pointer to the actual parse context data */ Tcl_Obj **objv; Tcl_Obj **full_objv; /* contains method as well */ unsigned int *flags; ClientData clientData_static[PARSE_CONTEXT_PREALLOC]; /* 3 members preallocated parse context data */ Tcl_Obj *objv_static[PARSE_CONTEXT_PREALLOC+1]; unsigned int flags_static[PARSE_CONTEXT_PREALLOC+1]; int lastObjc; /* points to the first "unprocessed" argument */ int objc; int varArgs; /* does the parameter end with some kind of "args" */ NsfObject *object; } ParseContext; static Nsf_TypeConverter ConvertToNothing, ConvertViaCmd, ConvertToObjpattern; /* * Tcl_Obj Types for Next Scripting Objects */ static Tcl_ObjType CONST86 *Nsf_OT_byteCodeType = NULL, *Nsf_OT_tclCmdNameType = NULL, *Nsf_OT_listType = NULL, *Nsf_OT_doubleType = NULL, *Nsf_OT_intType = NULL, *Nsf_OT_parsedVarNameType = NULL; /* * Function prototypes */ /* Prototypes for method definitions */ static Tcl_ObjCmdProc NsfForwardMethod; static Tcl_ObjCmdProc NsfObjscopedMethod; static Tcl_ObjCmdProc NsfSetterMethod; static Tcl_ObjCmdProc NsfProcAliasMethod; static Tcl_ObjCmdProc NsfAsmProc; /* Prototypes for methods called directly when CallDirectly() returns NULL */ static int NsfCAllocMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *nameObj) nonnull(1) nonnull(2); static int NsfCAllocMethod_(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *nameObj, Tcl_Namespace *parentNsPtr) nonnull(1) nonnull(2) nonnull(3); static int NsfCCreateMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *nameObj, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(3) nonnull(5); static int NsfOCleanupMethod(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static int NsfOConfigureMethod(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *objv0) nonnull(1) nonnull(2) nonnull(4) nonnull(5); static int NsfODestroyMethod(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static int MethodDispatch(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Command cmd, NsfObject *object, NsfClass *cl, CONST char *methodName, int frameType, unsigned int flags) nonnull(1) nonnull(2) nonnull(4) nonnull(5) nonnull(6) nonnull(8); static int DispatchDefaultMethod(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *obj, unsigned int flags) nonnull(1) nonnull(2) nonnull(3); static int DispatchDestroyMethod(Tcl_Interp *interp, NsfObject *object, unsigned int flags) nonnull(1) nonnull(2); static int DispatchUnknownMethod(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *callInfo, Tcl_Obj *methodObj, unsigned int flags) nonnull(1) nonnull(2) nonnull(4) nonnull(6); NSF_INLINE static int ObjectDispatch(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], unsigned int flags) nonnull(1) nonnull(2) nonnull(4); NSF_INLINE static int ObjectDispatchFinalize(Tcl_Interp *interp, NsfCallStackContent *cscPtr, int result /*, char *string , CONST char *methodName*/) nonnull(1) nonnull(2); /* prototypes for object life-cycle management */ static int RecreateObject(Tcl_Interp *interp, NsfClass *cl, NsfObject *object, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(3) nonnull(5); static void FinalObjectDeletion(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); #if defined(DO_CLEANUP) static void FreeAllNsfObjectsAndClasses(Tcl_Interp *interp, NsfCmdList **instances) nonnull(1) nonnull(2); #endif static void CallStackDestroyObject(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static void PrimitiveCDestroy(ClientData clientData) nonnull(1); static void PrimitiveODestroy(ClientData clientData) nonnull(1); static void PrimitiveDestroy(ClientData clientData) nonnull(1); /* prototypes for object and command lookup */ static NsfObject *GetObjectFromString(Tcl_Interp *interp, CONST char *name) nonnull(1) nonnull(2); static NsfClass *GetClassFromString(Tcl_Interp *interp, CONST char *name) nonnull(1) nonnull(2); static int GetClassFromObj(Tcl_Interp *interp, register Tcl_Obj *objPtr, NsfClass **clPtr, int withUnknown) nonnull(1) nonnull(2) nonnull(3); /*static NsfObject *GetHiddenObjectFromCmd(Tcl_Interp *interp, Tcl_Command cmdPtr); static int ReverseLookupCmdFromCmdTable(Tcl_Interp *interp, Tcl_Command searchCmdPtr, Tcl_HashTable *cmdTablePtr);*/ static void GetAllInstances(Tcl_Interp *interp, NsfCmdList **instances, NsfClass *startClass) nonnull(1) nonnull(2) nonnull(3); NSF_INLINE static Tcl_Command FindMethod(Tcl_Namespace *nsPtr, CONST char *methodName) nonnull(1) nonnull(2); /* prototypes for namespace specific calls */ static Tcl_Obj *NameInNamespaceObj(CONST char *name, Tcl_Namespace *ns) nonnull(1) nonnull(2); static Tcl_Namespace *CallingNameSpace(Tcl_Interp *interp) nonnull(1) returns_nonnull; NSF_INLINE static Tcl_Command NSFindCommand(Tcl_Interp *interp, CONST char *name) nonnull(1) nonnull(2); static Tcl_Namespace *NSGetFreshNamespace(Tcl_Interp *interp, NsfObject *object, CONST char *name) nonnull(1) nonnull(2) nonnull(3); static Tcl_Namespace *RequireObjNamespace(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static int NSDeleteCmd(Tcl_Interp *interp, Tcl_Namespace *nsPtr, CONST char *methodName) nonnull(1) nonnull(2) nonnull(3); static void NSNamespaceDeleteProc(ClientData clientData) nonnull(1); static void NSNamespacePreserve(Tcl_Namespace *nsPtr) nonnull(1); static void NSNamespaceRelease(Tcl_Namespace *nsPtr) nonnull(1); /* prototypes for filters and mixins */ static void FilterComputeDefined(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static void MixinComputeDefined(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); NSF_INLINE static void GuardAdd(NsfCmdList *filterCL, Tcl_Obj *guardObj) nonnull(1) nonnull(2); static int GuardCall(NsfObject *object, Tcl_Interp *interp, Tcl_Obj *guardObj, NsfCallStackContent *cscPtr) nonnull(1) nonnull(2) nonnull(3); static void GuardDel(NsfCmdList *filterCL) nonnull(1); /* prototypes for forwarders */ static void ForwardCmdDeleteProc(ClientData clientData) nonnull(1); static int ForwardProcessOptions(Tcl_Interp *interp, Tcl_Obj *nameObj, Tcl_Obj *withDefault, int withEarlybinding, Tcl_Obj *withOnerror, Tcl_Obj *withMethodprefix, int withFrame, int withVerbose, Tcl_Obj *target, int objc, Tcl_Obj * CONST objv[], ForwardCmdClientData **tcdPtr) nonnull(1) nonnull(2) nonnull(11); /* properties of objects and classes */ static int IsRootClass(NsfClass *cl) nonnull(1) pure; static int IsRootMetaClass(NsfClass *cl) nonnull(1) pure; static int IsBaseClass(NsfObject *cl) nonnull(1) pure; static int IsMetaClass(Tcl_Interp *interp, NsfClass *cl, int withMixins) nonnull(1) nonnull(2); static int IsSubType(NsfClass *subcl, NsfClass *cl) nonnull(1) nonnull(2); static NsfClass *DefaultSuperClass(Tcl_Interp *interp, NsfClass *cl, NsfClass *mcl, int isMeta) nonnull(1) nonnull(2) nonnull(3); /* prototypes for call stack specific calls */ NSF_INLINE static void CscInit_(NsfCallStackContent *cscPtr, NsfObject *object, NsfClass *cl, Tcl_Command cmd, int frameType, unsigned int flags) nonnull(1) nonnull(2); NSF_INLINE static void CscFinish_(Tcl_Interp *interp, NsfCallStackContent *cscPtr) nonnull(1) nonnull(2); NSF_INLINE static void CallStackDoDestroy(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); /* prototypes for parameter and argument management */ static int NsfParameterCacheClassInvalidateCmd(Tcl_Interp *interp, NsfClass *cl) nonnull(1) nonnull(2); static int ProcessMethodArguments(ParseContext *pcPtr, Tcl_Interp *interp, NsfObject *object, int processFlags, NsfParamDefs *paramDefs, Tcl_Obj *methodNameObj, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(5) nonnull(6) nonnull(8); static int ParameterCheck(Tcl_Interp *interp, Tcl_Obj *paramObjPtr, Tcl_Obj *valueObj, const char *argNamePrefix, int doCheckArguments, int isNamed, int doConfigureParameter, Nsf_Param **paramPtrPtr) nonnull(1) nonnull(2) nonnull(3); static void ParamDefsRefCountIncr(NsfParamDefs *paramDefs) nonnull(1); static void ParamDefsRefCountDecr(NsfParamDefs *paramDefs) nonnull(1); static void ParsedParamFree(NsfParsedParam *parsedParamPtr) nonnull(1); static int ArgumentParse(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], NsfObject *obj, Tcl_Obj *procName, Nsf_Param CONST *paramPtr, int nrParameters, int serial, unsigned int processFlags, ParseContext *pc) nonnull(1) nonnull(3) nonnull(5) nonnull(6) nonnull(10); static int ArgumentCheck(Tcl_Interp *interp, Tcl_Obj *objPtr, struct Nsf_Param CONST *pPtr, int doCheckArguments, unsigned int *flags, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(5) nonnull(6) nonnull(7); static int GetMatchObject(Tcl_Interp *interp, Tcl_Obj *patternObj, Tcl_Obj *origObj, NsfObject **matchObject, CONST char **pattern) nonnull(1) nonnull(4) nonnull(5); static void NsfProcDeleteProc(ClientData clientData) nonnull(1); static int NsfParameterCacheObjectInvalidateCmd(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static int GetObjectParameterDefinition(Tcl_Interp *interp, Tcl_Obj *procNameObj, NsfObject *object, NsfClass *class, NsfParsedParam *parsedParamPtr) nonnull(1) nonnull(2) nonnull(5); typedef Tcl_Obj *(NsfFormatFunction) _ANSI_ARGS_((Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern)); static Tcl_Obj *NsfParamDefsVirtualFormat(Tcl_Interp *interp, Nsf_Param CONST *pPtr, NsfObject *contextObject, CONST char *pattern, NsfFormatFunction formatFunction) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int NsfParamDefsAppendVirtual(Tcl_Interp *interp, Tcl_Obj *listObj, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern, NsfFormatFunction formatFunction) nonnull(1) nonnull(2) nonnull(3) nonnull(6); /* prototypes for alias management */ static int AliasDelete(Tcl_Interp *interp, Tcl_Obj *cmdName, CONST char *methodName, int withPer_object) nonnull(1) nonnull(2) nonnull(3); static Tcl_Obj *AliasGet(Tcl_Interp *interp, Tcl_Obj *cmdName, CONST char *methodName, int withPer_object, int leaveError) nonnull(1) nonnull(2) nonnull(3); static int AliasDeleteObjectReference(Tcl_Interp *interp, Tcl_Command cmd) nonnull(1) nonnull(2); static int NsfMethodAliasCmd(Tcl_Interp *interp, NsfObject *object, int withPer_object, CONST char *methodName, int withFrame, int withProtection, Tcl_Obj *cmdName) nonnull(1) nonnull(2) nonnull(4); static int AliasRefetch(Tcl_Interp *interp, NsfObject *object, CONST char *methodName, AliasCmdClientData *tcd) nonnull(1) nonnull(2) nonnull(3) nonnull(4); NSF_INLINE static Tcl_Command AliasDereference(Tcl_Interp *interp, NsfObject *object, CONST char *methodName, Tcl_Command cmd) nonnull(1) nonnull(2) nonnull(3) nonnull(4); /* prototypes for (class) list handling */ static NsfClasses ** NsfClassListAdd(NsfClasses **firstPtrPtr, NsfClass *cl, ClientData clientData) nonnull(1) returns_nonnull; /* misc prototypes */ static int SetInstVar(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *nameObj, Tcl_Obj *valueObj) nonnull(1) nonnull(2) nonnull(3); static int ListDefinedMethods(Tcl_Interp *interp, NsfObject *object, CONST char *pattern, int withPer_object, int methodType, int withCallproctection, int withPath) nonnull(1) nonnull(2); static int NextSearchAndInvoke(Tcl_Interp *interp, CONST char *methodName, int objc, Tcl_Obj *CONST objv[], NsfCallStackContent *cscPtr, int freeArgumentVector) nonnull(1) nonnull(2) nonnull(4) nonnull(5); static void CmdListFree(NsfCmdList **cmdList, NsfFreeCmdListClientData *freeFct) nonnull(1); static void NsfCommandPreserve(Tcl_Command cmd) nonnull(1); static void NsfCommandRelease(Tcl_Command cmd) nonnull(1); static Tcl_Command GetOriginalCommand(Tcl_Command cmd) nonnull(1) returns_nonnull; EXTERN void NsfDStringArgv(Tcl_DString *dsPtr, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(3); static int MethodSourceMatches(int withSource, NsfClass *cl, NsfObject *object); static NsfObjectOpt *NsfRequireObjectOpt(NsfObject *object) nonnull(1) returns_nonnull; static int ObjectSystemsCheckSystemMethod(Tcl_Interp *interp, CONST char *methodName, NsfObject *object, unsigned int flags) nonnull(1) nonnull(2) nonnull(3); #ifdef DO_CLEANUP static void DeleteNsfProcs(Tcl_Interp *interp, Tcl_Namespace *nsPtr) nonnull(1); #endif #if defined(NSF_WITH_ASSERTIONS) static void AssertionRemoveProc(NsfAssertionStore *aStore, CONST char *name) nonnull(1) nonnull(2); #endif #ifdef DO_FULL_CLEANUP static void DeleteProcsAndVars(Tcl_Interp *interp, Tcl_Namespace *nsPtr, int withKeepvars) nonnull(1) nonnull(2); #endif /* *---------------------------------------------------------------------- * * NsfErrorContext -- * * Print the current errorCode and errorInfo to stderr. * This should be used as the last ressort, when e.g. logging fails * * Results: * None. * * Side effects: * Output to stderr * *---------------------------------------------------------------------- */ static void NsfErrorContext(Tcl_Interp *interp, CONST char *context) nonnull(1) nonnull(2); static void NsfErrorContext(Tcl_Interp *interp, CONST char *context) { Tcl_DString ds, *dsPtr = &ds; assert(interp); assert(context); Tcl_DStringInit(dsPtr); Tcl_DStringAppend(dsPtr, "puts stderr \"Error in ", -1); Tcl_DStringAppend(dsPtr, context, -1); Tcl_DStringAppend(dsPtr, ":\n$::errorCode $::errorInfo\"", -1); Tcl_EvalEx(interp, Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr), 0); Tcl_DStringFree(dsPtr); } #if 0 static char * NsfErrorInfo(Tcl_Interp *interp) { Tcl_Obj *value; assert(interp); value = Tcl_GetVar2Ex(interp, "::errorInfo", NULL, TCL_GLOBAL_ONLY); if (value) { return ObjStr(value); } return NULL; } #endif /* *---------------------------------------------------------------------- * * NsfDStringEval -- * * Evaluate the provided Tcl_DString as a Tcl command and output * the error stack in case of a failure. * * Results: * Tcl result code. * * Side effects: * Output to stderr possible. * *---------------------------------------------------------------------- */ static int NsfDStringEval(Tcl_Interp *interp, Tcl_DString *dsPtr, CONST char *context) nonnull(1) nonnull(2) nonnull(3); static int NsfDStringEval(Tcl_Interp *interp, Tcl_DString *dsPtr, CONST char *context) { int result = Tcl_EvalEx(interp, Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr), 0); assert(interp); assert(dsPtr); assert(context); if (unlikely(result == TCL_ERROR)) { NsfErrorContext(interp, context); } return result; } /* *---------------------------------------------------------------------- * * NsfLog -- * * Produce a formatted warning by calling an external function * ::nsf::log. It is defined static to allow for inlining. * * Results: * None. * * Side effects: * Output of the warning. * *---------------------------------------------------------------------- */ void NsfLog(Tcl_Interp *interp, int requiredLevel, CONST char *fmt, ...) { assert(interp); assert(fmt); if (RUNTIME_STATE(interp)->debugLevel >= requiredLevel) { Tcl_DString cmdString, ds; CONST char *level; va_list ap; switch (requiredLevel) { case NSF_LOG_INFO: level = "Info"; break; case NSF_LOG_NOTICE: level = "Notice"; break; default: level = "Warning"; break; } Tcl_DStringInit(&ds); va_start(ap, fmt); NsfDStringPrintf(&ds, fmt, ap); va_end(ap); Tcl_DStringInit(&cmdString); Tcl_DStringAppendElement(&cmdString, "::nsf::log"); Tcl_DStringAppendElement(&cmdString, level); Tcl_DStringAppendElement(&cmdString, Tcl_DStringValue(&ds)); NsfDStringEval(interp, &cmdString, "log command"); Tcl_DStringFree(&cmdString); Tcl_DStringFree(&ds); } } /* *---------------------------------------------------------------------- * * NsfDeprecatedCmd -- * * Provide a warning about a deprecated command or method. The * message is produced via calling the external Tcl function * ::nsf::deprecated. * * Results: * None. * * Side effects: * Output of the warning. * *---------------------------------------------------------------------- */ static void NsfDeprecatedCmd(Tcl_Interp *interp, CONST char *what, CONST char *oldCmd, CONST char *newCmd) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static void NsfDeprecatedCmd(Tcl_Interp *interp, CONST char *what, CONST char *oldCmd, CONST char *newCmd) { Tcl_DString ds, *dsPtr = &ds; assert(interp); assert(newCmd); assert(what); assert(oldCmd); Tcl_DStringInit(dsPtr); Tcl_DStringAppendElement(dsPtr, "::nsf::deprecated"); Tcl_DStringAppendElement(dsPtr, what); Tcl_DStringAppendElement(dsPtr, oldCmd); Tcl_DStringAppendElement(dsPtr, newCmd ? newCmd : ""); NsfDStringEval(interp, dsPtr, "log command"); Tcl_DStringFree(dsPtr); } /*********************************************************************** * argv parsing ***********************************************************************/ /* *---------------------------------------------------------------------- * * ParseContextInit -- * * Initialize a ParseContext with default values and allocate * memory if needed. Every ParseContext has to be initialized * before usage and has to be freed with ParseContextRelease(). * * Results: * None. * * Side effects: * Allocate potentially memory. * *---------------------------------------------------------------------- */ static void ParseContextInit(ParseContext *pcPtr, int objc, NsfObject *object, Tcl_Obj *procName) nonnull(1) nonnull(4); static void ParseContextInit(ParseContext *pcPtr, int objc, NsfObject *object, Tcl_Obj *procName) { assert(pcPtr); assert(procName); if (likely(objc < PARSE_CONTEXT_PREALLOC)) { /* the single larger memset below .... */ memset(pcPtr, 0, sizeof(ParseContext)); /* ... is faster than the two smaller memsets below */ /* memset(pcPtr->clientData_static, 0, sizeof(ClientData)*(objc)); memset(pcPtr->objv_static, 0, sizeof(Tcl_Obj *)*(objc+1));*/ pcPtr->full_objv = &pcPtr->objv_static[0]; pcPtr->clientData = &pcPtr->clientData_static[0]; pcPtr->flags = &pcPtr->flags_static[0]; } else { pcPtr->full_objv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj *)*(objc+1)); pcPtr->flags = (unsigned int *)ckalloc(sizeof(int)*(objc+1)); MEM_COUNT_ALLOC("pcPtr.objv", pcPtr->full_objv); pcPtr->clientData = (ClientData *)ckalloc(sizeof(ClientData)*objc); MEM_COUNT_ALLOC("pcPtr.clientData", pcPtr->clientData); /*fprintf(stderr, "ParseContextMalloc %d objc, %p %p\n", objc, pcPtr->full_objv, pcPtr->clientData);*/ memset(pcPtr->full_objv, 0, sizeof(Tcl_Obj *)*(objc+1)); memset(pcPtr->flags, 0, sizeof(int)*(objc+1)); memset(pcPtr->clientData, 0, sizeof(ClientData)*(objc)); pcPtr->status = NSF_PC_STATUS_FREE_OBJV|NSF_PC_STATUS_FREE_CD; pcPtr->varArgs = 0; pcPtr->objc = 0; } pcPtr->objv = &pcPtr->full_objv[1]; pcPtr->full_objv[0] = procName; pcPtr->object = object; } /* *---------------------------------------------------------------------- * * ParseContextExtendObjv -- * * Extend Tcl_Obj array at runtime, when more elements are * needed. This function is called to extend an already * initialized ParseContext. * * Results: * None. * * Side effects: * Allocate potentially memory. * *---------------------------------------------------------------------- */ static void ParseContextExtendObjv(ParseContext *pcPtr, int from, int elts, Tcl_Obj *CONST source[]) nonnull(1) nonnull(4); static void ParseContextExtendObjv(ParseContext *pcPtr, int from, int elts, Tcl_Obj *CONST source[]) { int requiredSize = from + elts + 1; assert(pcPtr); assert(source); /*NsfPrintObjv("BEFORE: ", pcPtr->objc, pcPtr->full_objv);*/ if (unlikely(requiredSize >= PARSE_CONTEXT_PREALLOC)) { if (pcPtr->objv == &pcPtr->objv_static[1]) { /* realloc from preallocated memory */ pcPtr->full_objv = (Tcl_Obj **) ckalloc(sizeof(Tcl_Obj *) * requiredSize); pcPtr->flags = (unsigned int *)ckalloc(sizeof(int) * requiredSize); MEM_COUNT_ALLOC("pcPtr.objv", pcPtr->full_objv); memcpy(pcPtr->full_objv, &pcPtr->objv_static[0], sizeof(Tcl_Obj *) * PARSE_CONTEXT_PREALLOC); memcpy(pcPtr->flags, &pcPtr->flags_static[0], sizeof(int) * PARSE_CONTEXT_PREALLOC); /* fprintf(stderr, "ParseContextExtendObjv: extend %p alloc %d new objv=%p pcPtr %p\n", pcPtr, requiredSize, pcPtr->full_objv, pcPtr);*/ pcPtr->status |= NSF_PC_STATUS_FREE_OBJV; } else { /* realloc from mallocated memory */ pcPtr->full_objv = (Tcl_Obj **) ckrealloc((char *)pcPtr->full_objv, sizeof(Tcl_Obj *) * requiredSize); pcPtr->flags = (unsigned int *)ckrealloc((char *)pcPtr->flags, sizeof(int) * requiredSize); /*fprintf(stderr, "ParseContextExtendObjv: extend %p realloc %d new objv=%p pcPtr %p\n", pcPtr, requiredSize, pcPtr->full_objv, pcPtr);*/ } pcPtr->objv = &pcPtr->full_objv[1]; } memcpy(pcPtr->objv + from, source, sizeof(Tcl_Obj *) * elts); memset(pcPtr->flags + from, 0, sizeof(int) * elts); pcPtr->objc += elts; /*NsfPrintObjv("AFTER: ", pcPtr->objc, pcPtr->full_objv);*/ } /* *---------------------------------------------------------------------- * * ParseContextRelease -- * * Release (and potentially free) the content of a * ParseContext. This function is the counterpart of * ParseContextInit(), * * Results: * None. * * Side effects: * Free potentially memory. * *---------------------------------------------------------------------- */ static void ParseContextRelease(ParseContext *pcPtr) nonnull(1); static void ParseContextRelease(ParseContext *pcPtr) { int status = pcPtr->status; assert(pcPtr); /*fprintf(stderr, "ParseContextRelease %p status %.6x %d elements\n", pcPtr, status, pcPtr->objc);*/ #if !defined(NDEBUG) { /* * Perform a general consistency check: although the contents of the parse * context are at release time sometimes only partially initialized, the * following holds true for ensuring correct release of Tcl_Objs: * * 1) if one of the objv-flags has NSF_PC_MUST_DECR set, * then the status flag NSF_PC_STATUS_MUST_DECR has to * be set as well. * * 2) if objc > 0 then for all objv entries having a flag * different from 0 must have a * TCL_OBJ in the vector. * * 3) for preallocated objvs, all elements of the objv * after the argument vector must be 0 or * NSF_PC_IS_DEFAULT (sanity check) */ /* * (1) make sure, that the status correctly reflects MUST_DECR */ int i; if (status == 0 || (status & NSF_PC_STATUS_MUST_DECR) == 0) { for (i = 0; i < pcPtr->objc - 1; i++) { assert((pcPtr->flags[i] & NSF_PC_MUST_DECR) == 0); } } /* * (2) make sure, Tcl_Objs are set when needed for reclaiming memory */ if (pcPtr->objc > 0) { /*fprintf(stderr, "%s ", ObjStr(pcPtr->full_objv[0]));*/ for (i = 0; i < pcPtr->objc; i++) { if (pcPtr->flags[i]) { assert(pcPtr->objv[i]); /*fprintf(stderr, "[%d]%s %.6x ", i, ObjStr(pcPtr->objv[i]), pcPtr->flags[i]);*/ } } } /* * (3) All later flags must be empty or DEFAULT */ if (pcPtr->full_objv == &pcPtr->objv_static[0] && pcPtr->objc > 0) { for (i = pcPtr->objc; i < PARSE_CONTEXT_PREALLOC; i++) { assert(pcPtr->flags[i] == 0 || pcPtr->flags[i] == NSF_PC_IS_DEFAULT); } } } #endif if (unlikely(status)) { if (status & NSF_PC_STATUS_MUST_DECR) { int i; /*fprintf(stderr, "ParseContextRelease %p loop from 0 to %d\n", pcPtr, pcPtr->objc-1);*/ for (i = 0; i < pcPtr->objc; i++) { /*fprintf(stderr, "ParseContextRelease %p check [%d] obj %p flags %.6x & %p\n", pcPtr, i, pcPtr->objv[i], pcPtr->flags[i], &(pcPtr->flags[i]));*/ if (pcPtr->flags[i] & NSF_PC_MUST_DECR) { assert(pcPtr->objv[i]); assert(pcPtr->objv[i]->refCount > 0); /*fprintf(stderr, "... decr ref count on %p\n", pcPtr->objv[i]);*/ DECR_REF_COUNT2("valueObj", pcPtr->objv[i]); } } } /* * Objv can be separately extended; also flags are extend when this * happens. */ if (unlikely(status & NSF_PC_STATUS_FREE_OBJV)) { /*fprintf(stderr, "ParseContextRelease %p free %p %p\n", pcPtr, pcPtr->full_objv, pcPtr->clientData);*/ MEM_COUNT_FREE("pcPtr.objv", pcPtr->full_objv); ckfree((char *)pcPtr->full_objv); ckfree((char *)pcPtr->flags); } /* * If the parameter definition was extended at creation time also * clientData is extended. */ if (status & NSF_PC_STATUS_FREE_CD) { /*fprintf(stderr, "free client-data for %p\n", pcPtr);*/ MEM_COUNT_FREE("pcPtr.clientData", pcPtr->clientData); ckfree((char *)pcPtr->clientData); } } } /* *---------------------------------------------------------------------- * * CallMethod -- * * Call a Next Scripting method. clientData has to contain the object, on * which the method is to be dispatched, methodDobj denotes the method, * objc ( which has to be >=2) and objv denotes the argument vector. * * Results: * Tcl return code * * Side effects: * potentially via the called method. * *---------------------------------------------------------------------- */ /* * call an Next Scripting method */ static int CallMethod(ClientData clientData, Tcl_Interp *interp, Tcl_Obj *methodObj, int objc, Tcl_Obj *CONST objv[], unsigned int flags) nonnull(1) nonnull(2) nonnull(3); static int CallMethod(ClientData clientData, Tcl_Interp *interp, Tcl_Obj *methodObj, int objc, Tcl_Obj *CONST objv[], unsigned int flags) { NsfObject *object = (NsfObject *) clientData; int result; ALLOC_ON_STACK(Tcl_Obj*, objc, tov); assert(clientData); assert(interp); assert(methodObj); assert(objc > 1); tov[0] = object->cmdName; tov[1] = methodObj; if (likely(objc > 2)) { memcpy(tov+2, objv, sizeof(Tcl_Obj *)*(objc - 2)); } /*fprintf(stderr, "%%%% CallMethod cmdName=%s, method=%s, objc=%d\n", ObjStr(tov[0]), ObjStr(tov[1]), objc); {int i; fprintf(stderr, "\t CALL: %s ", ObjStr(methodObj));for(i = 0; i < objc-2; i++) { fprintf(stderr, "%s ", ObjStr(objv[i]));} fprintf(stderr, "\n");}*/ result = ObjectDispatch(clientData, interp, objc, tov, flags); FREE_ON_STACK(Tcl_Obj*, tov); return result; } /* *---------------------------------------------------------------------- * * NsfCallMethodWithArgs -- * * Call method (passed in methodObj) on the object, with the often * provided arg1 and the optional remaining args (passed vis objv). This * way, we save the memcpy in case no argument or an single argument are * provided (common cases). * * Results: * Tcl result. * * Side effects: * Called method might side effect. * *---------------------------------------------------------------------- */ int NsfCallMethodWithArgs(Tcl_Interp *interp, Nsf_Object *object, Tcl_Obj *methodObj, Tcl_Obj *arg1, int givenObjc, Tcl_Obj *CONST objv[], unsigned int flags) nonnull(1) nonnull(2) nonnull(3); int NsfCallMethodWithArgs(Tcl_Interp *interp, Nsf_Object *object, Tcl_Obj *methodObj, Tcl_Obj *arg1, int givenObjc, Tcl_Obj *CONST objv[], unsigned int flags) { int objc = givenObjc + 2; int result; ALLOC_ON_STACK(Tcl_Obj*, objc, tov); assert(interp); assert(object); assert(ISOBJ(methodObj)); assert(objc > 1); tov[0] = object->cmdName; tov[1] = methodObj; if (objc>2) { tov[2] = arg1; } if (objc>3) { memcpy(tov+3, objv, sizeof(Tcl_Obj *)*(objc-3)); } /*fprintf(stderr, "%%%% CallMethodWithArgs cmdName=%s, method=%s, arg1 %s objc=%d\n", ObjStr(tov[0]), ObjStr(tov[1]), objc>2 ? ObjStr(tov[2]) : "", objc);*/ result = ObjectDispatch(object, interp, objc, tov, flags); FREE_ON_STACK(Tcl_Obj*, tov); return result; } /* * Support for variable hash tables */ static NSF_INLINE Var *VarHashCreateVar(TclVarHashTable *tablePtr, Tcl_Obj *key, int *newPtr) nonnull(1) nonnull(2); static NSF_INLINE Var * VarHashCreateVar(TclVarHashTable *tablePtr, Tcl_Obj *key, int *newPtr) { Var *varPtr = NULL; Tcl_HashEntry *hPtr; assert(tablePtr); assert(key); hPtr = Tcl_CreateHashEntry((Tcl_HashTable *) tablePtr, (char *) key, newPtr); if (likely(hPtr != NULL)) { varPtr = TclVarHashGetValue(hPtr); } return varPtr; } static NSF_INLINE TclVarHashTable * VarHashTableCreate() { TclVarHashTable *varTablePtr = (TclVarHashTable *) ckalloc(sizeof(TclVarHashTable)); TclInitVarHashTable(varTablePtr, NULL); return varTablePtr; } #include "nsfCmdPtr.c" #include "nsfStack.c" /*********************************************************************** * Value added replacements of Tcl functions ***********************************************************************/ /* *---------------------------------------------------------------------- * Nsf_NextHashEntry -- * * Function very similar to Tcl_NextHashEntry. If during the iteration of * hash entries some of these entries are removed, Tcl_NextHashEntry() can * lead to a valid looking but invalid hPtr, when the next entry was * already deleted. This seem to occur only, when there are more than 12 * hash entries in the table (multiple buckets). Therefore, we use * numEntries to check, if it is sensible to return a an hash entry. We can * trigger refetch of the hSrchPtr, when the number of expected entries * differs from the numbers of the actual entries. * * Results: * Hash Entry or NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_HashEntry * Nsf_NextHashEntry(Tcl_HashTable *tablePtr, int expected, Tcl_HashSearch *hSrchPtr) nonnull(1) nonnull(3); static Tcl_HashEntry * Nsf_NextHashEntry(Tcl_HashTable *tablePtr, int expected, Tcl_HashSearch *hSrchPtr) { assert(tablePtr); assert(hSrchPtr); /*fprintf(stderr, "Nsf_NextHashEntry %p expected %d numEntries %d\n", tablePtr, expected, tablePtr->numEntries);*/ if (tablePtr->numEntries < 1) { return NULL; } else if (tablePtr->numEntries != expected) { return Tcl_FirstHashEntry(tablePtr, hSrchPtr); } else { return Tcl_NextHashEntry(hSrchPtr); } } /* *---------------------------------------------------------------------- * NsfCommandPreserve -- * * Increment Tcl's command refCount * * Results: * void * * Side effects: * None. * *---------------------------------------------------------------------- */ static void NsfCommandPreserve(Tcl_Command cmd) { assert(cmd); Tcl_Command_refCount(cmd)++; MEM_COUNT_ALLOC("command.refCount", cmd); } /* *---------------------------------------------------------------------- * NsfCommandRelease -- * * Decrement Tcl command refCount and free it if necessary * * Results: * void * * Side effects: * Free pot. memory * *---------------------------------------------------------------------- */ static void NsfCommandRelease(Tcl_Command cmd) { assert(cmd); /*fprintf(stderr,"NsfCommandRelease %p\n", cmd);*/ MEM_COUNT_FREE("command.refCount", cmd); TclCleanupCommandMacro((Command *)cmd); } /*********************************************************************** * EXTERN callable routines for the preliminary C interface ***********************************************************************/ Nsf_Object * NsfGetSelfObj(Tcl_Interp *interp) nonnull(1); Nsf_Object * NsfGetObject(Tcl_Interp *interp, CONST char *name) nonnull(1) nonnull(2); Nsf_Class * NsfGetClass(Tcl_Interp *interp, CONST char *name) nonnull(1) nonnull(2); Nsf_Class * NsfIsClass(Tcl_Interp *interp, ClientData clientData) nonnull(1) nonnull(2); void NsfRequireObjNamespace(Tcl_Interp *interp, Nsf_Object *object) nonnull(1) nonnull(2); Tcl_Obj * Nsf_ObjSetVar2(Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, Tcl_Obj *valueObj, unsigned int flags) nonnull(1) nonnull(2) nonnull(3) nonnull(5); Tcl_Obj * Nsf_ObjGetVar2(Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, unsigned int flags) nonnull(1) nonnull(2) nonnull(3); int NsfCreate(Tcl_Interp *interp, Nsf_Class *class, Tcl_Obj *nameObj, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(3) nonnull(5); int NsfDeleteObject(Tcl_Interp *interp, Nsf_Object *object) nonnull(1) nonnull(2); int NsfRemoveObjectMethod(Tcl_Interp *interp, Nsf_Object *object1, CONST char *methodName) nonnull(1) nonnull(2) nonnull(3); int NsfRemoveClassMethod(Tcl_Interp *interp, Nsf_Class *class, CONST char *methodName) nonnull(1) nonnull(2) nonnull(3); int Nsf_UnsetVar2(Nsf_Object *object1, Tcl_Interp *interp, CONST char *name1, CONST char *name2, unsigned int flags) nonnull(1) nonnull(2) nonnull(4); void NsfSetObjClientData(Tcl_Interp *interp, Nsf_Object *object, ClientData data) nonnull(1) nonnull(2); ClientData NsfGetObjClientData(Tcl_Interp *interp, Nsf_Object *object) nonnull(1) nonnull(2); void NsfSetClassClientData(Tcl_Interp *interp, Nsf_Class *cl, ClientData data) nonnull(1) nonnull(2); ClientData NsfGetClassClientData(Tcl_Interp *interp, Nsf_Class *cl) nonnull(1) nonnull(2); Nsf_Object * NsfGetSelfObj(Tcl_Interp *interp) { assert(interp); return (Nsf_Object *) GetSelfObj(interp); } Nsf_Object * NsfGetObject(Tcl_Interp *interp, CONST char *name) { assert(interp); assert(name); return (Nsf_Object *) GetObjectFromString(interp, name); } Nsf_Class * NsfGetClass(Tcl_Interp *interp, CONST char *name) { assert(interp); assert(name); return (Nsf_Class *)GetClassFromString(interp, name); } Nsf_Class * NsfIsClass(Tcl_Interp *UNUSED(interp), ClientData clientData) { assert(clientData); if (clientData && NsfObjectIsClass((NsfObject *)clientData)) { return (Nsf_Class *) clientData; } return NULL; } void NsfRequireObjNamespace(Tcl_Interp *interp, Nsf_Object *object) { assert(interp); assert(object); RequireObjNamespace(interp, (NsfObject *) object); } Tcl_Obj * Nsf_ObjSetVar2(Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, Tcl_Obj *valueObj, unsigned int flags) { Tcl_Obj *result; CallFrame frame, *framePtr = &frame; assert(object); assert(interp); assert(name1); assert(valueObj); Nsf_PushFrameObj(interp, (NsfObject *)object, framePtr); if (((NsfObject *)object)->nsPtr) { flags |= TCL_NAMESPACE_ONLY; } result = Tcl_ObjSetVar2(interp, name1, name2, valueObj, flags); Nsf_PopFrameObj(interp, framePtr); return result; } Tcl_Obj * Nsf_ObjGetVar2(Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, unsigned int flags) { Tcl_Obj *result; CallFrame frame, *framePtr = &frame; assert(object); assert(interp); assert(name1); Nsf_PushFrameObj(interp, (NsfObject *)object, framePtr); if (((NsfObject *)object)->nsPtr) { flags |= TCL_NAMESPACE_ONLY; } result = Tcl_ObjGetVar2(interp, name1, name2, flags); Nsf_PopFrameObj(interp, framePtr); return result; } int Nsf_UnsetVar2(Nsf_Object *object1, Tcl_Interp *interp, CONST char *name1, CONST char *name2, unsigned int flags) { NsfObject *object = (NsfObject *) object1; int result; CallFrame frame, *framePtr = &frame; assert(object1); assert(interp); assert(name1); assert(name2); Nsf_PushFrameObj(interp, object, framePtr); if (object->nsPtr) { flags |= TCL_NAMESPACE_ONLY; } result = Tcl_UnsetVar2(interp, name1, name2, flags); Nsf_PopFrameObj(interp, framePtr); return result; } int NsfCreate(Tcl_Interp *interp, Nsf_Class *class, Tcl_Obj *nameObj, int objc, Tcl_Obj *CONST objv[]) { NsfClass *cl = (NsfClass *) class; int result; ALLOC_ON_STACK(Tcl_Obj *, objc+2, ov); assert(interp); assert(class); assert(nameObj); assert(objv); INCR_REF_COUNT2("nameObj", nameObj); ov[0] = NULL; ov[1] = nameObj; if (objc > 0) { memcpy(ov+2, objv, sizeof(Tcl_Obj *)*objc); } result = NsfCCreateMethod(interp, cl, nameObj, objc+2, ov); FREE_ON_STACK(Tcl_Obj*, ov); DECR_REF_COUNT2("nameObj", nameObj); return result; } int NsfDeleteObject(Tcl_Interp *interp, Nsf_Object *object) { assert(interp); assert(object); return DispatchDestroyMethod(interp, (NsfObject *)object, 0); } int NsfRemoveObjectMethod(Tcl_Interp *interp, Nsf_Object *object1, CONST char *methodName) { NsfObject *object = (NsfObject *) object1; assert(interp); assert(object1); assert(methodName); /*fprintf(stderr, "... NsfRemoveObjectMethod %s %s\n", ObjectName(object), methodName);*/ NsfObjectMethodEpochIncr("NsfRemoveObjectMethod"); AliasDelete(interp, object->cmdName, methodName, 1); #if defined(NSF_WITH_ASSERTIONS) if (object->opt && object->opt->assertions) { AssertionRemoveProc(object->opt->assertions, methodName); } #endif if (object->nsPtr) { int rc = NSDeleteCmd(interp, object->nsPtr, methodName); if (rc < 0) { return NsfPrintError(interp, "%s: cannot delete object specific method '%s'", ObjectName(object), methodName); } } return TCL_OK; } int NsfRemoveClassMethod(Tcl_Interp *interp, Nsf_Class *class, CONST char *methodName) { NsfClass *cl = (NsfClass *) class; int rc; #if defined(NSF_WITH_ASSERTIONS) NsfClassOpt *opt = cl->opt; #endif assert(interp); assert(class); assert(methodName); /*fprintf(stderr, "... NsfRemoveClassMethod %s %s\n", ClassName(class), methodName);*/ NsfInstanceMethodEpochIncr("NsfRemoveClassMethod"); AliasDelete(interp, class->object.cmdName, methodName, 0); #if defined(NSF_WITH_ASSERTIONS) if (opt && opt->assertions) { AssertionRemoveProc(opt->assertions, methodName); } #endif rc = NSDeleteCmd(interp, cl->nsPtr, methodName); if (rc < 0) { return NsfPrintError(interp, "%s: cannot delete method '%s'", ClassName(cl), methodName); } return TCL_OK; } /* * obj/cl ClientData setter/getter */ void NsfSetObjClientData(Tcl_Interp *interp, Nsf_Object *object, ClientData data) { assert(interp); assert(object); assert(data); NsfRequireObjectOpt((NsfObject *) object) -> clientData = data; } ClientData NsfGetObjClientData(Tcl_Interp *interp, Nsf_Object *object) { NsfObject *object_ = (NsfObject *) object; assert(interp); assert(object); return object_->opt ? object_->opt->clientData : NULL; } void NsfSetClassClientData(Tcl_Interp *interp, Nsf_Class *cl, ClientData data) { assert(interp); assert(cl); NsfRequireClassOpt((NsfClass *)cl) -> clientData = data; } ClientData NsfGetClassClientData(Tcl_Interp *interp, Nsf_Class *cl) { NsfClass *cl_ = (NsfClass *) cl; assert(interp); assert(cl); return cl_->opt ? cl_->opt->clientData : NULL; } /*********************************************************************** * Utility functions ***********************************************************************/ #if defined(NSFOBJ_TRACE) void ObjTrace(char *string, NsfObject *object) nonnull(1) nonnull(2); void ObjTrace(char *string, NsfObject *object) { assert(string); assert(object); fprintf(stderr, "--- %s tcl %p %s (%d %p) nsf %p (%d) %s \n", string, object->cmdName, object->cmdName->typePtr ? object->cmdName->typePtr->name : "NULL", object->cmdName->refCount, object->cmdName->internalRep.twoPtrValue.ptr1, object, object->refCount, ObjectName(object)); } #else # define ObjTrace(a, b) #endif /* *---------------------------------------------------------------------- * NSTail -- * * Return the namespace tail of a name. * * Results: * String. * * Side effects: * None. * *---------------------------------------------------------------------- */ /* search for tail of name */ static CONST char * NSTail(CONST char *string) nonnull(1); static CONST char * NSTail(CONST char *string) { register char *p = (char *)string+strlen(string); assert(string); while (p > string) { if (unlikely(*p == ':' && *(p-1) == ':')) { return p+1; } p--; } return string; } /* *---------------------------------------------------------------------- * IsClassNsName -- * * Check, if the provided string starts with the prefix of the * classes namespace. * * Results: * Boolean. * * Side effects: * None. * *---------------------------------------------------------------------- */ NSF_INLINE static int IsClassNsName(CONST char *string, CONST char **cont) nonnull(1); NSF_INLINE static int IsClassNsName(CONST char *string, CONST char **cont) { assert(string); if (*string == ':' && strncmp((string), "::nsf::classes", 14) == 0) { if (cont) {*cont = string + 14;} return 1; } return 0; } /* *---------------------------------------------------------------------- * GetObjectFromNsName -- * * Get object or class from a fully qualified cmd name, such as * e.g. ::nsf::classes::X * * Results: * NsfObject and *fromClasses * * Side effects: * None. * *---------------------------------------------------------------------- */ NSF_INLINE static NsfObject * GetObjectFromNsName(Tcl_Interp *interp, CONST char *string, int *fromClassNS) nonnull(1) nonnull(2) nonnull(3); NSF_INLINE static NsfObject * GetObjectFromNsName(Tcl_Interp *interp, CONST char *string, int *fromClassNS) { CONST char *className; assert(interp != NULL); assert(string != NULL); assert(fromClassNS != NULL); if (IsClassNsName(string, &className)) { *fromClassNS = 1; return (NsfObject *)GetClassFromString(interp, className); } else { *fromClassNS = 0; return GetObjectFromString(interp, string); } } /* *---------------------------------------------------------------------- * DStringAppendQualName -- * * Append to initialized DString the name of the namespace followed * by a simple name (methodName, cmdName). * * Results: * String pointing to DString value. * * Side effects: * None. * *---------------------------------------------------------------------- */ static char *DStringAppendQualName(Tcl_DString *dsPtr, Tcl_Namespace *nsPtr, CONST char *name) nonnull(1) nonnull(2) nonnull(3); static char * DStringAppendQualName(Tcl_DString *dsPtr, Tcl_Namespace *nsPtr, CONST char *name) { int oldLength = Tcl_DStringLength(dsPtr); assert(dsPtr); assert(nsPtr); assert(name); Tcl_DStringAppend(dsPtr, nsPtr->fullName, -1); if (Tcl_DStringLength(dsPtr) > (oldLength + 2)) { Tcl_DStringAppend(dsPtr, "::", 2); } Tcl_DStringAppend(dsPtr, name, -1); return Tcl_DStringValue(dsPtr); } /* *---------------------------------------------------------------------- * NsfCleanupObject -- * * Delete an object physically (performing ckfree()) when its refCount * reaches 0 * * Results: * None. * * Side effects: * Frees memory. * *---------------------------------------------------------------------- */ void NsfCleanupObject_(NsfObject *object) { assert(object); NsfObjectRefCountDecr(object); /*fprintf(stderr, "NsfCleanupObject obj refCount of %p after decr %d id %p interp %p flags %.6x\n", object, object->refCount, object->id, object->teardown, object->flags);*/ if (unlikely(object->refCount <= 0)) { /*fprintf(stderr, "NsfCleanupObject %p ref-count %d\n", object, object->refCount);*/ assert(object->refCount == 0); assert(object->flags & NSF_DELETED); /* * During FinalObjectDeletion(), object->teardown is NULL, we cannot access * the object and class names anymore. */ if (object->teardown && NSF_DTRACE_OBJECT_FREE_ENABLED()) { NSF_DTRACE_OBJECT_FREE(ObjectName(object), ClassName(object->cl)); } MEM_COUNT_FREE("NsfObject/NsfClass", object); #if defined(NSFOBJ_TRACE) fprintf(stderr, "CKFREE Object %p refCount=%d\n", object, object->refCount); #endif #if !defined(NDEBUG) memset(object, 0, sizeof(NsfObject)); #endif ckfree((char *) object); } } /* * Tcl_Obj functions for objects */ /* *---------------------------------------------------------------------- * TclObjIsNsfObject -- * * Check, if the provided Tcl_Obj is bound to a nsf object. If so, return * the NsfObject in the third argument. * * Results: * True or false, * * Side effects: * None * *---------------------------------------------------------------------- */ static int TclObjIsNsfObject(Tcl_Interp *interp, Tcl_Obj *objPtr, NsfObject **objectPtr) nonnull(1) nonnull(2) nonnull(3); static int TclObjIsNsfObject(Tcl_Interp *interp, Tcl_Obj *objPtr, NsfObject **objectPtr) { Tcl_ObjType CONST86 *cmdType = objPtr->typePtr; assert(interp); assert(objPtr); assert(objectPtr); if (cmdType == Nsf_OT_tclCmdNameType) { Tcl_Command cmd = Tcl_GetCommandFromObj(interp, objPtr); if (likely(cmd != NULL)) { NsfObject *object = NsfGetObjectFromCmdPtr(cmd); if (object) { *objectPtr = object; return 1; } } } return 0; } /* *---------------------------------------------------------------------- * GetObjectFromObj -- * * Lookup an Next Scripting object from the given objPtr, preferably from * an object of type "cmdName". On success the NsfObject is returned in the * third argument. The objPtr might be converted by this function. * * Results: * True or false, * * Side effects: * object type of objPtr might be changed * *---------------------------------------------------------------------- */ static int GetObjectFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, NsfObject **objectPtr) nonnull(1) nonnull(2) nonnull(3); static int GetObjectFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, NsfObject **objectPtr) { NsfObject *object; CONST char *string; Tcl_Command cmd; assert(interp); assert(objPtr); assert(objectPtr); /*fprintf(stderr, "GetObjectFromObj obj %p %s is of type %s\n", objPtr, ObjStr(objPtr), objPtr->typePtr ? objPtr->typePtr->name : "(null)");*/ /* in case, objPtr was not of type cmdName, try to convert */ cmd = Tcl_GetCommandFromObj(interp, objPtr); /*fprintf(stderr, "GetObjectFromObj obj %s => cmd=%p (%d)\n", ObjStr(objPtr), cmd, cmd ? Tcl_Command_refCount(cmd):-1);*/ if (cmd) { NsfObject *object = NsfGetObjectFromCmdPtr(cmd); /*fprintf(stderr, "GetObjectFromObj obj %s, o is %p objProc %p NsfObjDispatch %p\n", ObjStr(objPtr), object, Tcl_Command_objProc(cmd), NsfObjDispatch);*/ if (object) { if (objectPtr) *objectPtr = object; return TCL_OK; } } /*fprintf(stderr, "GetObjectFromObj convertFromAny for %s type %p %s\n", ObjStr(objPtr), objPtr->typePtr, objPtr->typePtr ? objPtr->typePtr->name : "(none)");*/ /* In case, we have to revolve via the CallingNameSpace (i.e. the * argument is not fully qualified), we retry here. */ string = ObjStr(objPtr); if (isAbsolutePath(string)) { object = NULL; } else { Tcl_Obj *tmpName = NameInNamespaceObj(string, CallingNameSpace(interp)); CONST char *nsString = ObjStr(tmpName); INCR_REF_COUNT(tmpName); object = GetObjectFromString(interp, nsString); /* fprintf(stderr, " RETRY, string '%s' returned %p\n", nsString, object);*/ DECR_REF_COUNT(tmpName); } if (likely(object != NULL)) { if (objectPtr) {*objectPtr = object;} return TCL_OK; } return TCL_ERROR; } /* *---------------------------------------------------------------------- * NsfCallObjectUnknownHandler -- * * Call ::nsf::object::unknown; this function is typically called, when an unknown * object or class is passed as an argument. * * Results: * Tcl result code * * Side effects: * Called handler might side effect. * *---------------------------------------------------------------------- */ static int NsfCallObjectUnknownHandler(Tcl_Interp *interp, Tcl_Obj *nameObj) nonnull(1) nonnull(2); static int NsfCallObjectUnknownHandler(Tcl_Interp *interp, Tcl_Obj *nameObj) { int result; Tcl_Obj *ov[3]; assert(interp); assert(nameObj); /*fprintf(stderr, "try ::nsf::object::unknown for '%s'\n", ObjStr(nameObj));*/ ov[0] = NsfGlobalObjs[NSF_OBJECT_UNKNOWN_HANDLER]; ov[1] = nameObj; INCR_REF_COUNT(ov[1]); result = Tcl_EvalObjv(interp, 2, ov, 0); DECR_REF_COUNT(ov[1]); return result; } #if defined(NSF_EXPERIMENTAL) static int NsfCallArgumentUnknownHandler(Tcl_Interp *interp, Tcl_Obj *methodObj, Tcl_Obj *argumentObj, NsfObject *object) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int NsfCallArgumentUnknownHandler(Tcl_Interp *interp, Tcl_Obj *methodObj, Tcl_Obj *argumentObj, NsfObject *object) { Tcl_Obj *ov[4]; int result, oc = 3; assert(interp); assert(methodObj); assert(argumentObj); assert(object); /*fprintf(stderr, "try ::nsf::argument::unknown for '%s'\n", ObjStr(nameObj));*/ ov[0] = NsfGlobalObjs[NSF_ARGUMENT_UNKNOWN_HANDLER]; ov[1] = methodObj; ov[2] = argumentObj; if (object) { ov[3] = object->cmdName; oc ++; } INCR_REF_COUNT(ov[1]); result = Tcl_EvalObjv(interp, oc, ov, 0); DECR_REF_COUNT(ov[1]); return result; } #endif /* *---------------------------------------------------------------------- * GetClassFromObj -- * * Lookup an Next Scripting class from the given objPtr. If the class could * not be directly converted and withUnknown is true, the function calls * the unknown function (::nsf::object::unknown) to fetch the class on * demand and retries the conversion. On success the NsfClass is returned * in the third argument. The objPtr might be converted by this function. * * Results: * True or false, * * Side effects: * object type of objPtr might be changed * *---------------------------------------------------------------------- */ static int GetClassFromObj(Tcl_Interp *interp, register Tcl_Obj *objPtr, NsfClass **clPtr, int withUnknown) { NsfObject *object; NsfClass *cls = NULL; int result = TCL_OK; CONST char *objName = ObjStr(objPtr); Tcl_Command cmd; assert(interp); assert(objPtr); assert(clPtr); cmd = Tcl_GetCommandFromObj(interp, objPtr); /*fprintf(stderr, "GetClassFromObj %p %s unknown %d cmd %p\n", objPtr, objName, withUnknown, cmd);*/ if (cmd) { cls = NsfGetClassFromCmdPtr(cmd); #if 1 if (cls == NULL) { /* * We have a cmd, but no class; namespace-imported classes are already * resolved, but we have to care, if a class is "imported" via "interp * alias". */ Tcl_Interp *alias_interp; const char *alias_cmd_name; Tcl_Obj *nameObj = objPtr; Tcl_Obj **alias_ov; int alias_oc = 0; if (!isAbsolutePath(objName)) { nameObj = NameInNamespaceObj(objName, CallingNameSpace(interp)); objName = ObjStr(nameObj); INCR_REF_COUNT(nameObj); } result = Tcl_GetAliasObj(interp, objName, &alias_interp, &alias_cmd_name, &alias_oc, &alias_ov); Tcl_ResetResult(interp); /* we only want interp-aliases with 0 args */ if (result == TCL_OK && alias_oc == 0) { cmd = NSFindCommand(interp, alias_cmd_name); /*fprintf(stderr, "..... alias arg 0 '%s' cmd %p\n", alias_cmd_name, cmd);*/ if (cmd) { cls = NsfGetClassFromCmdPtr(cmd); } } /*fprintf(stderr, "..... final cmd %p, cls %p\n", cmd , cls);*/ if (nameObj != objPtr) { DECR_REF_COUNT(nameObj); } } #endif if (cls) { *clPtr = cls; return TCL_OK; } } result = GetObjectFromObj(interp, objPtr, &object); if (likely(result == TCL_OK)) { cls = NsfObjectToClass(object); if (cls) { *clPtr = cls; return TCL_OK; } else { /* flag, that we could not convert so far */ result = TCL_ERROR; } } if (withUnknown) { result = NsfCallObjectUnknownHandler(interp, isAbsolutePath(objName) ? objPtr : NameInNamespaceObj(objName, CallingNameSpace(interp))); if (likely(result == TCL_OK)) { /* Retry, but now, the last argument (withUnknown) has to be 0 */ result = GetClassFromObj(interp, objPtr, clPtr, 0); } /*fprintf(stderr, "... ::nsf::object::unknown for '%s', result %d cl %p\n", objName, result, cl);*/ } return result; } /* * Version of GetClassFromObj() with external symbol */ int NsfGetClassFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, NsfClass **clPtr, int withUnknown) { assert(interp); assert(objPtr); assert(clPtr); return GetClassFromObj(interp, objPtr, clPtr, withUnknown); } /* *---------------------------------------------------------------------- * IsObjectOfType -- * * Check, if the provided NsfObject is of a certain type. The arguments * "what" and "objPtr" are just used for the error messages. objPtr is the * value from which the object was converted from. * * Results: * Tcl result code. * * Side effects: * None * *---------------------------------------------------------------------- */ static int IsObjectOfType(Tcl_Interp *interp, NsfObject *object, CONST char *what, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); static int IsObjectOfType(Tcl_Interp *interp, NsfObject *object, CONST char *what, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr) { NsfClass *cl; Tcl_DString ds, *dsPtr = &ds; assert(interp); assert(object); assert(what); assert(objPtr); assert(pPtr); if (unlikely(pPtr->flags & NSF_ARG_BASECLASS) && !IsBaseClass(object)) { what = "baseclass"; goto type_error; } if (unlikely(pPtr->flags & NSF_ARG_METACLASS) && !IsMetaClass(interp, (NsfClass *)object, 1)) { what = "metaclass"; goto type_error; } if (likely(pPtr->converterArg == NULL)) { return TCL_OK; } if ((GetClassFromObj(interp, pPtr->converterArg, &cl, 0) == TCL_OK) && IsSubType(object->cl, cl)) { return TCL_OK; } type_error: DSTRING_INIT(dsPtr); Tcl_DStringAppend(dsPtr, what, -1); if (pPtr->converterArg) { Tcl_DStringAppend(dsPtr, " of type ", -1); Tcl_DStringAppend(dsPtr, ObjStr(pPtr->converterArg), -1); } NsfObjErrType(interp, NULL, objPtr, Tcl_DStringValue(dsPtr), (Nsf_Param *)pPtr); DSTRING_FREE(dsPtr); return TCL_ERROR; } /* *---------------------------------------------------------------------- * NameInNamespaceObj -- * * Create a fully qualified name in the provided namespace or in * the current namespace in form of an Tcl_Obj (with 0 refCount); * * Results: * Tcl_Obj containing fully qualified name * * Side effects: * Allocates fresh copies of list elements * *---------------------------------------------------------------------- */ static Tcl_Obj * NameInNamespaceObj(CONST char *name, Tcl_Namespace *nsPtr) { Tcl_Obj *objPtr; Tcl_DString ds, *dsPtr = &ds; assert(name); assert(nsPtr); /*fprintf(stderr, "NameInNamespaceObj %s (%p, %s) ", name, nsPtr, nsPtr->fullName);*/ DSTRING_INIT(dsPtr); DStringAppendQualName(dsPtr, nsPtr, name); objPtr = Tcl_NewStringObj(Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr)); /*fprintf(stderr, "returns %s\n", ObjStr(objPtr));*/ DSTRING_FREE(dsPtr); return objPtr; } /* *---------------------------------------------------------------------- * NewTclCommand -- * * Given a provided prefix in dsPtr, make it a name of a command that does not exist. * This function is used by the *new command, when "anonymous" objects are created * * Results: * dsPtr will be complete to represent a new (unused) name of a command * * Side effects: * None. * *---------------------------------------------------------------------- */ void NewTclCommand(Tcl_Interp *interp, Tcl_DString *dsPtr) nonnull(1) nonnull(2); void NewTclCommand(Tcl_Interp *interp, Tcl_DString *dsPtr) { int prefixLength = dsPtr->length; NsfStringIncrStruct *iss = &RUNTIME_STATE(interp)->iss; assert(interp); assert(dsPtr); while (1) { (void)NsfStringIncr(iss); Tcl_DStringAppend(dsPtr, iss->start, iss->length); if (!Tcl_FindCommand(interp, Tcl_DStringValue(dsPtr), NULL, TCL_GLOBAL_ONLY)) { break; } /* * In case the symbol existed already, reset prefix to the * original length. */ Tcl_DStringSetLength(dsPtr, prefixLength); } } /* *---------------------------------------------------------------------- * NsfReverseClasses -- * * Reverse class list. Caller is responsible for freeing data. * * Results: * Pointer to start of the reversed list * * Side effects: * Allocates fresh copies of list elements * *---------------------------------------------------------------------- */ static NsfClasses *NsfReverseClasses(NsfClasses *sl) nonnull(1) returns_nonnull; static NsfClasses * NsfReverseClasses(NsfClasses *sl) { NsfClasses *firstPtr = NULL; assert(sl != NULL); for (; likely(sl != NULL); sl = sl->nextPtr) { NsfClasses *element = NEW(NsfClasses); element->cl = sl->cl; element->clientData = sl->clientData; element->nextPtr = firstPtr; firstPtr = element; } return firstPtr; } /* *---------------------------------------------------------------------- * NsfClassListFree -- * * Frees all elements of the provided class list * * Results: * None. * * Side effects: * Frees memory. * *---------------------------------------------------------------------- */ static void NsfClassListFree(NsfClasses *classList) nonnull(1); static void NsfClassListFree(NsfClasses *classList) { NsfClasses *nextPtr; assert(classList); for (; likely(classList != NULL); classList = nextPtr) { nextPtr = classList->nextPtr; FREE(NsfClasses, classList); } } /* *---------------------------------------------------------------------- * NsfClassListAdd -- * * Add class list entry to the specified list. In case the initial * list is empty, *firstPtrPtr is updated as well. * * Results: * Returns address of next-pointer. * * Side effects: * New list element is allocated. * *---------------------------------------------------------------------- */ static NsfClasses ** NsfClassListAdd(NsfClasses **firstPtrPtr, NsfClass *cl, ClientData clientData) { NsfClasses *l = *firstPtrPtr, *element = NEW(NsfClasses); assert(firstPtrPtr); element->cl = cl; element->clientData = clientData; element->nextPtr = NULL; if (l) { while (l->nextPtr) { l = l->nextPtr; } l->nextPtr = element; } else { *firstPtrPtr = element; } return &(element->nextPtr); } /* *---------------------------------------------------------------------- * NsfClassListAddNoDup -- * * Add class list entry to the specified list without duplicates. In case * the initial list is empty, *firstPtrPtr is updated as well. * * Results: * Returns address of next pointer. * * Side effects: * New list element is allocated. * *---------------------------------------------------------------------- */ static NsfClasses **NsfClassListAddNoDup(NsfClasses **firstPtrPtr, NsfClass *cl, ClientData clientData, int *isNewPtr) nonnull(1) nonnull(2); static NsfClasses ** NsfClassListAddNoDup(NsfClasses **firstPtrPtr, NsfClass *cl, ClientData clientData, int *isNewPtr) { NsfClasses *clPtr = *firstPtrPtr, **nextPtr; assert(firstPtrPtr); assert(cl); if (clPtr) { for (; clPtr->nextPtr && clPtr->cl != cl; clPtr = clPtr->nextPtr); nextPtr = &clPtr->nextPtr; } else { nextPtr = firstPtrPtr; } if (*nextPtr == NULL) { NsfClasses *element = NEW(NsfClasses); element->cl = cl; element->clientData = clientData; element->nextPtr = NULL; *nextPtr = element; if (isNewPtr) { *isNewPtr = 1; } } else { if (isNewPtr) { *isNewPtr = 0; } } return nextPtr; } /* *---------------------------------------------------------------------- * NsfClassListFind -- * * Find an element in the class list and return it if found. * * Results: * Found element or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfClasses *NsfClassListFind(NsfClasses *clPtr, NsfClass *cl) nonnull(2); static NsfClasses * NsfClassListFind(NsfClasses *clPtr, NsfClass *cl) { assert(cl); for (; clPtr; clPtr = clPtr->nextPtr) { if (clPtr->cl == cl) break; } return clPtr; } #if defined(NSF_CLASSLIST_PRINT) /* debugging purposes only */ /* *---------------------------------------------------------------------- * NsfClassListStats -- * * Print some statistics about generated Class List structures for * debugging purpose. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void NsfClassListStats(CONST char *title, NsfClasses *classList) nonnull(1); static void NsfClassListStats(CONST char *title, NsfClasses *classList) { NsfClass *cl; int count = 0; assert(title); cl = classList ? classList->cl : NULL; for (; classList; classList = classList->nextPtr) { count++; } fprintf(stderr, "%s class list starting with %s has %d elements\n", title, cl ? ClassName(cl) : "none", count); } static void NsfClassListPrint(CONST char *title, NsfClasses *clsList) nonnull(1); static void NsfClassListPrint(CONST char *title, NsfClasses *clsList) { assert(title); fprintf(stderr, "%s", title); /* fprintf(stderr, " %p:", clsList); */ while (clsList) { /* fprintf(stderr, " %p", clsList->cl); */ fprintf(stderr, " %p", clsList); fprintf(stderr, " %s", ClassName(clsList->cl)); clsList = clsList->nextPtr; } fprintf(stderr, "\n"); } #endif /* *---------------------------------------------------------------------- * NsfClassListUnlink -- * * Return removed item with matching key form nsfClasses. * Key is void to allow not only class pointers as keys. * * Results: * unlinked element or NULL. * In case the first element is unlinked, *firstPtrPtr * is updated. * * Side effects: * none. * *---------------------------------------------------------------------- */ static NsfClasses *NsfClassListUnlink(NsfClasses **firstPtrPtr, void *key) nonnull(1) nonnull(2); static NsfClasses * NsfClassListUnlink(NsfClasses **firstPtrPtr, void *key) { NsfClasses *entryPtr = NULL; assert(firstPtrPtr != NULL); assert(key != NULL); if (*firstPtrPtr != NULL) { NsfClasses *prevPtr = NULL; /* list is non-empty */ for (entryPtr = *firstPtrPtr; entryPtr; prevPtr = entryPtr, entryPtr = entryPtr->nextPtr) { if ((void *)entryPtr->cl == key) { /* found entry */ if (prevPtr) { /* later item */ prevPtr->nextPtr = entryPtr->nextPtr; } else { /* first item */ *firstPtrPtr = entryPtr->nextPtr; } entryPtr->nextPtr = NULL; break; } } } return entryPtr; } /* * Functions for computing Precedence Order */ /* *---------------------------------------------------------------------- * TopoSort -- * * Perform a topological sort of the class hierarchies. Depending on the * argument "direction" it performs the sort on the transitive list of * superclasses or subclasses. The resulting list contains no duplicates or * cycles and is returned in the class member "order". During the * computation it colors the processed nodes in WHITE, GRAY or BLACK. * * Results: * Boolean value indicating success. * * Side effects: * Allocating class list. * *---------------------------------------------------------------------- */ enum colors { WHITE, GRAY, BLACK }; typedef enum { SUPER_CLASSES, SUB_CLASSES } ClassDirection; static int TopoSort(NsfClass *cl, NsfClass *baseClass, ClassDirection direction, int withMixinOfs) nonnull(1) nonnull(2); static int TopoSort(NsfClass *cl, NsfClass *baseClass, ClassDirection direction, int withMixinOfs) { NsfClasses *sl, *pl; assert(cl); assert(baseClass); sl = direction == SUPER_CLASSES ? cl->super : cl->sub; /* * Be careful to reset the color of unreported classes to * white in case we unwind with error, and on final exit * reset color of reported classes to WHITE. Meaning of colors: * * WHITE ... not processed * GRAY ... in work * BLACK ... done */ cl->color = GRAY; for (; sl; sl = sl->nextPtr) { NsfClass *sc = sl->cl; if (sc->color == GRAY) { cl->color = WHITE; return 0; } if (unlikely(sc->color == WHITE && !TopoSort(sc, baseClass, direction, withMixinOfs))) { cl->color = WHITE; if (cl == baseClass) { register NsfClasses *pc; for (pc = cl->order; pc; pc = pc->nextPtr) { pc->cl->color = WHITE; } } return 0; } } if (withMixinOfs) { NsfCmdList *classMixins = cl->opt && cl->opt->isClassMixinOf ? cl->opt->isClassMixinOf : NULL; for (; classMixins; classMixins = classMixins->nextPtr) { NsfClass *sc = NsfGetClassFromCmdPtr(classMixins->cmdPtr); if (unlikely(sc->color == WHITE && !TopoSort(sc, baseClass, direction, withMixinOfs))) { NsfLog(sc->object.teardown, NSF_LOG_WARN, "cycle in the mixin graph list detected for class %s", ClassName(sc)); } } } cl->color = BLACK; pl = NEW(NsfClasses); pl->cl = cl; pl->nextPtr = baseClass->order; baseClass->order = pl; if (unlikely(cl == baseClass)) { register NsfClasses *pc; for (pc = cl->order; pc; pc = pc->nextPtr) { pc->cl->color = WHITE; } } return 1; } /* *---------------------------------------------------------------------- * MustBeBefore -- * * Check the partial ordering of classes based on precedence list in the * form of prior ordering from the topological sort. We compare here * orderings based the class hierarchies with single inheritance and prior * solved multiple inheritance orderings. The test is true, if b must be * before a. * * Results: * Boolean value indicating success. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int MustBeBefore(NsfClass *a, NsfClass *b, NsfClasses *superClasses) nonnull(1) nonnull(2) nonnull(3); static int MustBeBefore(NsfClass *a, NsfClass *b, NsfClasses *superClasses) { int result; assert(a != NULL); assert(b != NULL); assert(superClasses != NULL); assert(b->order != NULL); /* * Check, if a is in the precedence order of b. E.g. * * a c1 object * b c2 a object * * If so then b must be before a to preserve the precedence order based on * single inheritance (monotonicity). */ result = (NsfClassListFind(b->order, a) != NULL); /* * When the partital ordering can't be decided based on the local order * test, we take the specified multiple inheritance ordering in superClasses * (e.g. coming from -superclass {x y}) which is not taken account by the * class hierarchy. */ if (result == 0) { NsfClasses *sl; int bFound = 0; #if defined(NSF_LINEARIZER_TRACE) fprintf(stderr, "--> check %s before %s?\n", ClassName(b), ClassName(a)); NsfClassListPrint("miList", miList); #endif for (sl = superClasses; sl; sl = sl->nextPtr) { if (sl->cl == b) { bFound = 1; } else if (bFound && sl->cl == a) { #if defined(NSF_LINEARIZER_TRACE) fprintf(stderr, "%s in inheritanceList before %s therefore a < b\n", ClassName(b), ClassName(a)); #endif result = 1; break; } } } #if defined(NSF_LINEARIZER_TRACE) fprintf(stderr, "compare a: %s %p b: %s %p -> %d\n", ClassName(a), a->order, ClassName(b),b->order, result); NsfClassListPrint("\ta", a->order); NsfClassListPrint("\tb", b->order); #endif return result; } /* *---------------------------------------------------------------------- * ValidClassListTail -- * * Debug function to assure that the provided class lists are valid. The * tail of the class list must be a base class of the current object * system. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ #if !defined(NDEBUG) static void ValidClassListTail(CONST char *what, NsfClasses *classList) { NsfClasses *sl, *tail; for (sl = classList, tail = NULL; sl; sl = sl->nextPtr) {tail = sl;} if (tail) { /* fprintf(stderr, "check tail what %s %p\n", what, ClassName(tail->cl), tail->nextPtr);*/ assert(IsBaseClass(&tail->cl->object)); assert(tail->nextPtr == NULL); } } #else # define ValidClassListTail(what, classList) #endif /* *---------------------------------------------------------------------- * MergeInheritanceLists -- * * Merge the PrecedenceOrders of class cl. This function is called, when cl * is defined with multiple inheritance. The precedence orders of the * specified classes are merged in an order preserving manner to achieve * monotonicity. * * Results: * precedence order. * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfClasses *MergeInheritanceLists(NsfClasses *pl, NsfClass *cl) nonnull(1) nonnull(2) returns_nonnull; static NsfClasses * MergeInheritanceLists(NsfClasses *pl, NsfClass *cl) { NsfClasses *sl, *baseList, **plNext, *superClasses, *deletionList = NULL; assert(pl != NULL); assert(cl != NULL); #if defined(NSF_LINEARIZER_TRACE) fprintf(stderr, "=== working on %s\n", ClassName(cl)); #endif /* * The available multiple inheritance list is in reversed order so we have * to reverse it to obtain the specified superClasses in the provided order. */ superClasses = NsfReverseClasses(cl->super); /* * We distinguish between a * * - baseList (which might be later an result of partial merges), and a * - mergeList, which is merged order-preserving into the baseList. * * The first baseList is the precedence list of the first element of the * specified superClasses. */ baseList = superClasses->cl->order; assert(baseList != NULL); #if defined(NSF_LINEARIZER_TRACE) fprintf(stderr, "=== baseList from %s\n", ClassName(superClasses->cl)); NsfClassListPrint("baseList", baseList); #endif /* * The first element of the result list of the merge operation is the first * element of the baseList. */ plNext = NsfClassListAdd(&pl, baseList->cl, NULL); /* * For every element but the first (which is already in baseList), we have to * perform the merge operation. For n elements in superClasses, the merge * operation is performed n-1 times. */ for (sl = superClasses->nextPtr; sl; sl = sl->nextPtr) { NsfClasses *mergeList = sl->cl->order, *baseListCurrent; #if defined(NSF_LINEARIZER_TRACE) NsfClassListPrint("mergeList", mergeList); #endif /* * Merge mergeList into baseList. We start with the 2nd (later probably * nth) entry of the baseList */ baseListCurrent = baseList->nextPtr; assert(baseListCurrent != NULL); while (mergeList != NULL) { NsfClass *addClass; ValidClassListTail("baseList", baseList); ValidClassListTail("mergeList", mergeList); assert(baseListCurrent != NULL); /* NsfClassListPrint("baseListCurrent", baseListCurrent); */ if (mergeList->cl == baseListCurrent->cl) { /* * The first element of mergeList and the current baseList element are * identical. The element is in the result, keep the element in the * result, advance in both lists. */ /* fprintf(stderr, "\t\tadvance both\n"); */ addClass = mergeList->cl; baseListCurrent = baseListCurrent->nextPtr; mergeList = mergeList->nextPtr; } else if (MustBeBefore(baseListCurrent->cl, mergeList->cl, superClasses)) { /* * Check, if current element of mergeList must be before the current * element of baseList. If so, insert current mergelist element before * baseListCurrent, */ addClass = mergeList->cl; mergeList = mergeList->nextPtr; /* fprintf(stderr, "\t\tadd from mergeList %s\n", ClassName(addClass)); */ } else { /* * Two cases above do not apply, add from baseList and advance * baseList pointer. */ addClass = baseListCurrent->cl; baseListCurrent = baseListCurrent->nextPtr; /* fprintf(stderr, "\t\tadd from baselist %s\n", ClassName(addClass)); */ } if (addClass) { /* * We have to add an element to the precedence list. When the class to * be added is already in the result list (which might happen just in * crippled cases) then delete it. In the final step it will be added * again to the end. */ NsfClasses *deletedElement = NsfClassListUnlink(&pl, addClass); if (deletedElement) { #if defined(NSF_LINEARIZER_TRACE) fprintf(stderr, "\t\t%s is redundant (in resultList)\n", ClassName(addClass)); #endif /* * When plNext points to the nextPtr of the deleted element, search * the list from the begin */ if (plNext == &(deletedElement->nextPtr)) { plNext = &pl; } NsfClassListFree(deletedElement); } /* add the new element */ plNext = NsfClassListAdd(plNext, addClass, NULL); } #if defined(NSF_LINEARIZER_TRACE) NsfClassListPrint("pl:", pl); #endif } /* * mergeList is processed, we have a final precedence list in pl. In case * are at then of superClasses, we are done. Otherwise, use the resulting * pl as next baseList and continue with the next mergeList from * superClasses. */ #if defined(NSF_LINEARIZER_TRACE) NsfClassListPrint("plFinal:", pl); #endif if (sl->nextPtr) { /* We are not at the end, use pl as new base list */ baseList = pl; #if defined(NSF_LINEARIZER_TRACE) fprintf(stderr, "=== setting new baseList\n"); NsfClassListPrint("new baseList", baseList); #endif /* * Add old pl to deletion list; these entries are deleted once merging * is finished. */ NsfClassListAdd(&deletionList, NULL, pl); /* * Create a fresh pl for the next iteration. */ pl = NULL; plNext = NsfClassListAdd(&pl, cl, NULL); } } for (sl = deletionList; sl; sl = sl->nextPtr) { /* fprintf(stderr, "delete from deletion list %p client data %p\n", sl, sl->clientData); */ NsfClassListFree(sl->clientData); } if (deletionList) NsfClassListFree(deletionList); NsfClassListFree(superClasses); return pl; } #if defined(NDEBUG) #define AssertOrderIsWhite(arg) #else static void AssertOrderIsWhite(NsfClasses *order) { register NsfClasses *pc; for (pc = order; pc; pc = pc->nextPtr) { assert(pc->cl->color == WHITE); } } #endif /* *---------------------------------------------------------------------- * TopoSortSuper -- * * Compute the precedence order for baseClass based on the superclasses. If * the order is computable, update base class and return 1. Otherwise * return 0. * * Results: * Success/Failure. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int TopoSortSuper(NsfClass *cl, NsfClass *baseClass) nonnull(1) nonnull(2); static int TopoSortSuper(NsfClass *cl, NsfClass *baseClass) { NsfClasses *pl, *sl; assert(cl != NULL); assert(baseClass != NULL); /* * Be careful to reset the color of unreported classes to * white in the caller on all exits to WHITE. * * WHITE ... not processed * GRAY ... in work * BLACK ... done */ cl->color = GRAY; for (sl = cl->super; likely(sl != NULL); sl = sl->nextPtr) { NsfClass *sc = sl->cl; if (sc->color == GRAY) { cl->color = WHITE; return 0; } if (unlikely(sc->color == WHITE && !TopoSortSuper(sc, baseClass))) { cl->color = WHITE; return 0; } } /* * Create a new precedence list containing cl. */ pl = NEW(NsfClasses); pl->cl = cl; pl->nextPtr = NULL; /* * If we have multiple inheritance we merge the precomputed inheritance * orders of the involved classes in the provided order. */ if (likely(cl->super != NULL) && unlikely(cl->super->nextPtr != NULL)) { pl = MergeInheritanceLists(pl, cl); if (baseClass->order) { NsfClassListFree(baseClass->order); baseClass->order = NULL; } } else { /* * Add baseClass order to the end of the precedence list. */ assert(pl->nextPtr == NULL); pl->nextPtr = baseClass->order; } cl->color = BLACK; /* * Set baseclass order to the newly computed list (the result of this function) */ baseClass->order = pl; return 1; } /* *---------------------------------------------------------------------- * PrecedenceOrder -- * * Return a class list containing the transitive list of super classes * starting with (and containing) the provided class. The super class list * is cached in cl->order and has to be invalidated by FlushPrecedences() * in case the order changes. The caller does not have to free the returned * class list (like for TransitiveSubClasses); * * Results: * Class list, NULL on error * * Side effects: * Updating cl->order. * *---------------------------------------------------------------------- */ NSF_INLINE static NsfClasses *PrecedenceOrder(NsfClass *cl) nonnull(1); NSF_INLINE static NsfClasses * PrecedenceOrder(NsfClass *cl) { register NsfClasses *sl; int success; assert(cl != NULL); /* * Check, of the superclass order is already cached. */ if (likely(cl->order != NULL)) { return cl->order; } /* * For multiple inheritance (more than one superclass), make sure that * required precedence orders are precomputed. */ if (likely(cl->super != NULL) && unlikely(cl->super->nextPtr != NULL)) { for (sl = cl->super; sl; sl = sl->nextPtr) { NsfClasses *pl; #if defined(NSF_LINEARIZER_TRACE) fprintf(stderr, "====== PrecedenceOrder mi, check %s %p \n", ClassName(sl->cl), sl->cl->order); #endif if (unlikely(sl->cl->order == NULL) && likely(cl != sl->cl)) { #if defined(NSF_LINEARIZER_TRACE) fprintf(stderr, "====== PrecedenceOrder computes required order for %s \n", ClassName(sl->cl)); #endif PrecedenceOrder(sl->cl); #if defined(NSF_LINEARIZER_TRACE) NsfClassListPrint("====== PO:", sl->cl->order); #endif } for (pl = sl->cl->order; pl; pl = pl->nextPtr) { #if defined(NSF_LINEARIZER_TRACE) fprintf(stderr, "====== PO order: %s %p\n", ClassName(pl->cl), pl->cl->order); #endif if (pl->cl->order == NULL) { #if defined(NSF_LINEARIZER_TRACE) fprintf(stderr, "========== recurse\n"); #endif PrecedenceOrder(pl->cl); } } } } success = TopoSortSuper(cl, cl); /* * Reset the color of all nodes. */ for (sl = cl->order; sl; sl = sl->nextPtr) { sl->cl->color = WHITE; } /* * If computation is successful, return cl->order. * Otherwise clear cl->order. */ if (likely(success)) { AssertOrderIsWhite(cl->order); return cl->order; } else { NsfClassListFree(cl->order); return cl->order = NULL; } } /* *---------------------------------------------------------------------- * TransitiveSubClasses -- * * Return a class list containing the transitive list of sub-classes * starting with (and containing) the provided class.The caller has to free * the returned class list. * * Results: * Class list or NULL if cycles are detected * * Side effects: * Just indirect. * *---------------------------------------------------------------------- */ NSF_INLINE static NsfClasses * TransitiveSubClasses(NsfClass *cl) nonnull(1); NSF_INLINE static NsfClasses * TransitiveSubClasses(NsfClass *cl) { NsfClasses *order, *savedOrder; assert(cl); /* * Since TopoSort() places its result in cl->order, we have to save the old * cl->order, perform the computation and restore the old order. */ savedOrder = cl->order; cl->order = NULL; if (likely(TopoSort(cl, cl, SUB_CLASSES, 0))) { order = cl->order; } else { if (cl->order) NsfClassListFree(cl->order); order = NULL; } /* * TODO: if this holds, we can change the fn to returns_nonnull and the else-branch is not needed */ assert(order); AssertOrderIsWhite(order); cl->order = savedOrder; return order; } /* *---------------------------------------------------------------------- * DependentSubClasses -- * * Return a class list containing all dependent classes (i.e. classes that * inherit via intrinsic or mixin hierarchy) starting with (and containing) * the provided class.The caller has to free the returned class list. * * Results: * Class list or NULL if cycles are detected * * Side effects: * Just indirect. * *---------------------------------------------------------------------- */ NSF_INLINE static NsfClasses *DependentSubClasses(NsfClass *cl) nonnull(1); NSF_INLINE static NsfClasses * DependentSubClasses(NsfClass *cl) { NsfClasses *order, *savedOrder; assert(cl); /* * Since TopoSort() places its result in cl->order, we have to save the old * cl->order, perform the computation and restore the old order. */ savedOrder = cl->order; cl->order = NULL; if (likely(TopoSort(cl, cl, SUB_CLASSES, 1))) { order = cl->order; } else { if (cl->order) NsfClassListFree(cl->order); order = NULL; } /* * TODO: if this holds, we can change the fn to returns_nonnull and the else-branch is not needed */ assert(order); AssertOrderIsWhite(order); cl->order = savedOrder; return order; } /* *---------------------------------------------------------------------- * FlushPrecedences -- * * This function iterations over the provided class list and flushes (and * frees) the superclass caches in cl->order for every element. * * Results: * None. * * Side effects: * Freeing class lists cached in cl->order. * *---------------------------------------------------------------------- */ static void FlushPrecedences(NsfClasses *subClasses) nonnull(1); static void FlushPrecedences(NsfClasses *clPtr) { assert(clPtr != NULL); for (; clPtr; clPtr = clPtr->nextPtr) { if (clPtr->cl->order) NsfClassListFree(clPtr->cl->order); clPtr->cl->order = NULL; } } /* *---------------------------------------------------------------------- * AddInstance -- * * Add an instance to a class. * * Results: * None. * * Side effects: * Add entry to children hash-table. * *---------------------------------------------------------------------- */ static void AddInstance(NsfObject *object, NsfClass *cl) nonnull(1) nonnull(2); static void AddInstance(NsfObject *object, NsfClass *cl) { int newItem; assert(object); assert(cl); object->cl = cl; (void) Tcl_CreateHashEntry(&cl->instances, (char *)object, &newItem); /*if (newItem == 0) { fprintf(stderr, "instance %p %s was already an instance of %p %s\n", object, ObjectName(object), cl, ClassName(cl)); }*/ assert(newItem); } /* *---------------------------------------------------------------------- * RemoveInstance -- * * Remove an instance from a class. The function checks, whether the entry * is actually still an instance before it deletes it. * * Results: * void * * Side effects: * Entry deleted from instances hash-table * *---------------------------------------------------------------------- */ static void RemoveInstance(NsfObject *object, NsfClass *cl) nonnull(1) nonnull(2); static void RemoveInstance(NsfObject *object, NsfClass *cl) { assert(object); assert(cl); /* * If we are during a delete, which should not happen under normal * operations, prevent an abort due to a deleted hash table. */ if (cl->object.flags & NSF_DURING_DELETE) { NsfLog(cl->object.teardown, NSF_LOG_WARN, "Class which should loose instance is currently being deleted: %s", ClassName(cl)); } else { Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&cl->instances, (char *)object, NULL); /*if (hPtr == NULL) { fprintf(stderr, "instance %s is not an instance of %s\n", ObjectName(object), ClassName(cl)); }*/ assert(hPtr); Tcl_DeleteHashEntry(hPtr); } } /* * superclass/subclass list maintenance */ static void AddSuper1(NsfClass *s, NsfClasses **sl) nonnull(1) nonnull(2); static void AddSuper(NsfClass *cl, NsfClass *super) nonnull(1); static int RemoveSuper1(NsfClass *s, NsfClasses **sl) nonnull(1) nonnull(2); static int RemoveSuper(NsfClass *cl, NsfClass *super) nonnull(1) nonnull(2); static void AddSuper1(NsfClass *s, NsfClasses **sl) { NsfClasses *sc = NEW(NsfClasses); assert(s != NULL); assert(sl != NULL); sc->cl = s; sc->nextPtr = *sl; *sl = sc; } static void AddSuper(NsfClass *cl, NsfClass *super) { if (super) { assert(cl != NULL); /* * keep corresponding sub in step with super */ AddSuper1(super, &cl->super); AddSuper1(cl, &super->sub); } } static int RemoveSuper1(NsfClass *s, NsfClasses **sl) { NsfClasses *l; assert(s != NULL); assert(sl != NULL); l = *sl; if (l == NULL) return 0; if (l->cl == s) { *sl = l->nextPtr; FREE(NsfClasses, l); return 1; } while (l->nextPtr && l->nextPtr->cl != s) l = l->nextPtr; if (l->nextPtr) { NsfClasses *n = l->nextPtr->nextPtr; FREE(NsfClasses, l->nextPtr); l->nextPtr = n; return 1; } return 0; } static int RemoveSuper(NsfClass *cl, NsfClass *super) { int sp, sb; assert(cl != NULL); assert(super != NULL); /* * keep corresponding sub in step with super */ sp = RemoveSuper1(super, &cl->super); sb = RemoveSuper1(cl, &super->sub); return sp && sb; } /* * methods lookup */ /* *---------------------------------------------------------------------- * GetEnsembleObjectFromName -- * * Get an ensemble object from a method name. If the method name * is fully qualified, just use a Tcl lookup, otherwise get it from * the provided namespace, * * Results: * ensemble object or NULL * * Side effects: * none * *---------------------------------------------------------------------- */ static NsfObject * GetEnsembleObjectFromName(Tcl_Interp *interp, Tcl_Namespace *nsPtr, Tcl_Obj *name, Tcl_Command *cmdPtr, int *fromClassNS) nonnull(1) nonnull(3) nonnull(4) nonnull(5); static NsfObject * GetEnsembleObjectFromName(Tcl_Interp *interp, Tcl_Namespace *nsPtr, Tcl_Obj *name, Tcl_Command *cmdPtr, int *fromClassNS) { Tcl_Command cmd; char *nameString = ObjStr(name); assert(interp); assert(name); assert(cmdPtr); assert(fromClassNS); if (*nameString == ':') { cmd = Tcl_GetCommandFromObj(interp, name); *fromClassNS = IsClassNsName(nameString, NULL); } else { cmd = nsPtr ? FindMethod(nsPtr, nameString) : NULL; } if (cmd) { *cmdPtr = cmd; return NsfGetObjectFromCmdPtr(GetOriginalCommand(cmd)); } return NULL; } /* *---------------------------------------------------------------------- * GetRegObject -- * * Try to get the object, on which the method was registered from a * fully qualified method handle * * Results: * NsfObject * or NULL on failure * * Side effects: * none * *---------------------------------------------------------------------- */ static NsfObject *GetRegObject(Tcl_Interp *interp, Tcl_Command cmd, CONST char *methodName, CONST char **methodName1, int *fromClassNS) nonnull(1) nonnull(3) nonnull(5) nonnull(2); static NsfObject * GetRegObject(Tcl_Interp *interp, Tcl_Command cmd, CONST char *methodName, CONST char **methodName1, int *fromClassNS) { NsfObject *regObject; CONST char *procName; size_t objNameLength; assert(interp); assert(cmd); assert(methodName); assert(*methodName == ':'); assert(fromClassNS); assert(cmd); procName = Tcl_GetCommandName(interp, cmd); objNameLength = strlen(methodName) - strlen(procName) - 2; if (objNameLength > 0) { Tcl_DString ds, *dsPtr = &ds; /* obtain parent name */ Tcl_DStringInit(dsPtr); Tcl_DStringAppend(dsPtr, methodName, objNameLength); regObject = GetObjectFromNsName(interp, Tcl_DStringValue(dsPtr), fromClassNS); if (regObject && methodName1) { *methodName1 = procName; } Tcl_DStringFree(dsPtr); } else { regObject = NULL; } /*fprintf(stderr, "GetRegObject cmd %p methodName '%s' => %p\n", cmd, methodName, regObject);*/ return regObject; } /* *---------------------------------------------------------------------- * ResolveMethodName -- * * Resolve a method name relative to a provided namespace. * The method name can be * a) a fully qualified name * b) a list of method name and subcommands * c) a simple name * * Results: * Tcl_Command or NULL on failure * * Side effects: * none * *---------------------------------------------------------------------- */ static Tcl_Command ResolveMethodName(Tcl_Interp *interp, Tcl_Namespace *nsPtr, Tcl_Obj *methodObj, Tcl_DString *methodNameDs, NsfObject **regObject, NsfObject **defObject, CONST char **methodName1, int *fromClassNS) nonnull(1) nonnull(3) nonnull(8); static Tcl_Command ResolveMethodName(Tcl_Interp *interp, Tcl_Namespace *nsPtr, Tcl_Obj *methodObj, Tcl_DString *methodNameDs, NsfObject **regObject, NsfObject **defObject, CONST char **methodName1, int *fromClassNS) { char *methodName; NsfObject *referencedObject; int containsSpace, tailContainsSpace; Tcl_Command cmd; assert(interp); assert(methodObj); assert(fromClassNS); methodName = ObjStr(methodObj); /*fprintf(stderr,"methodName '%s' comp %d type %s\n", methodName, strchr(methodName, ' ')>0, methodObj->typePtr ? methodObj->typePtr->name : "(none)");*/ if (methodObj->typePtr == Nsf_OT_listType) { int length; Tcl_ListObjLength(interp, methodObj, &length); containsSpace = length > 1; } else if (methodObj->typePtr == Nsf_OT_tclCmdNameType) { containsSpace = 0; } else { containsSpace = strchr(methodName, ' ') != NULL; } if (containsSpace) { tailContainsSpace = strchr(NSTail(methodName), ' ') != NULL; } else { tailContainsSpace = 0; } /*fprintf(stderr, "<%s> containsSpace %d tailContainsSpace %d\n", methodName, containsSpace, tailContainsSpace);*/ #if !defined(NDEBUG) if (containsSpace) { assert(strchr(methodName, ' ') != 0); } else { assert(tailContainsSpace == 0); } #endif if (tailContainsSpace) { CONST char *firstElementString; Tcl_Namespace *parentNsPtr; NsfObject *ensembleObject; Tcl_Obj *methodHandleObj, **ov; int oc, i; /* * When the methodName is required, we have to provide a methodNameDS as * well. */ assert(methodName1 == NULL || methodNameDs != NULL); /*fprintf(stderr, "name '%s' contains space \n", methodName);*/ if ((Tcl_ListObjGetElements(interp, methodObj, &oc, &ov) != TCL_OK) || ((referencedObject = GetEnsembleObjectFromName(interp, nsPtr, ov[0], &cmd, fromClassNS)) == NULL) ) { if (methodName1) {*methodName1 = NULL;} if (regObject) {*regObject = NULL;} if (defObject) {*defObject = NULL;} return NULL; } /* * We have an ensemble object. First, figure out, on which * object/class the ensemble object was registered. We determine * the regObject on the first element of the list. If we can't, * then the current object is the regObject. */ firstElementString = ObjStr(ov[0]); if (*firstElementString == ':') { NsfObject *registrationObject; registrationObject = GetRegObject(interp, cmd, firstElementString, methodName1, fromClassNS); if (regObject) {*regObject = registrationObject;} } else { if (regObject) {*regObject = NULL;} } /*fprintf(stderr, "... regObject object '%s' reg %p, fromClassNS %d\n", ObjectName(referencedObject), *regObject, *fromClassNS);*/ /* * Build a fresh methodHandleObj to held method name and names of * subcmds. */ methodHandleObj = Tcl_DuplicateObj(referencedObject->cmdName); INCR_REF_COUNT(methodHandleObj); if (methodNameDs) { Tcl_DStringAppend(methodNameDs, Tcl_GetCommandName(interp, cmd), -1); } parentNsPtr = NULL; /* * Iterate over the objects and append to the methodNameDs and methodHandleObj */ for (i = 1; i < oc; i++) { cmd = Tcl_GetCommandFromObj(interp, methodHandleObj); ensembleObject = cmd ? NsfGetObjectFromCmdPtr(cmd) : NULL; if (ensembleObject == NULL) { DECR_REF_COUNT(methodHandleObj); if (methodName1) {*methodName1 = NULL;} if (regObject) {*regObject = NULL;} if (defObject) {*defObject = NULL;} return NULL; } if (parentNsPtr && Tcl_Command_nsPtr(ensembleObject->id) != parentNsPtr) { /* fprintf(stderr, "*** parent change saved parent %p %s computed parent %p %s\n", parentNsPtr, parentNsPtr->fullName, Tcl_Command_nsPtr(ensembleObject->id), Tcl_Command_nsPtr(ensembleObject->id)->fullName);*/ DECR_REF_COUNT(methodHandleObj); methodHandleObj = Tcl_DuplicateObj(ensembleObject->cmdName); } parentNsPtr = ensembleObject->nsPtr; Tcl_AppendLimitedToObj(methodHandleObj, "::", 2, INT_MAX, NULL); Tcl_AppendLimitedToObj(methodHandleObj, ObjStr(ov[i]), -1, INT_MAX, NULL); if (methodNameDs) { Tcl_DStringAppendElement(methodNameDs, ObjStr(ov[i])); } } /* * cmd contains now the parent-obj, on which the method was * defined. Get from this cmd the defObj. */ if (defObject) {*defObject = NsfGetObjectFromCmdPtr(cmd);} /*fprintf(stderr, "... handle '%s' last cmd %p defObject %p\n", ObjStr(methodHandleObj), cmd, *defObject);*/ /* * Obtain the command from the method handle and report back the * final methodName, */ cmd = Tcl_GetCommandFromObj(interp, methodHandleObj); if (methodName1) {*methodName1 = Tcl_DStringValue(methodNameDs);} /*fprintf(stderr, "... methodname1 '%s' cmd %p\n", Tcl_DStringValue(methodNameDs), cmd);*/ DECR_REF_COUNT(methodHandleObj); } else if (*methodName == ':') { cmd = Tcl_GetCommandFromObj(interp, methodObj); if (likely(cmd != NULL)) { referencedObject = GetRegObject(interp, cmd, methodName, methodName1, fromClassNS); if (regObject) {*regObject = referencedObject;} if (defObject) {*defObject = referencedObject;} if (methodName1 && *methodName1 == NULL) { /* * The return value for the method name is required and was not * computed by GetRegObject() */ *methodName1 = Tcl_GetCommandName(interp, cmd); } } else { /* * The cmd was not registered on an object or class, but we * still report back the cmd (might be e.g. a primitive cmd). */ if (regObject) {*regObject = NULL;} if (defObject) {*defObject = NULL;} } } else { if (methodName1) {*methodName1 = methodName;} cmd = nsPtr ? FindMethod(nsPtr, methodName) : NULL; if (regObject) {*regObject = NULL;} if (defObject) {*defObject = NULL;} } return cmd; } /* *---------------------------------------------------------------------- * CmdIsProc -- * * Check, whether the cmd is interpreted * * Results: * Boolean * * Side effects: * None * *---------------------------------------------------------------------- */ NSF_INLINE static int CmdIsProc(Tcl_Command cmd) nonnull(1); NSF_INLINE static int CmdIsProc(Tcl_Command cmd) { /* In 8.6: TclIsProc((Command *)cmd) is not equivalent to the definition below */ assert(cmd); return (Tcl_Command_objProc(cmd) == TclObjInterpProc); } /* *---------------------------------------------------------------------- * CmdIsNsfObject -- * * Check whether the provided cmd refers to an NsfObject or Class. * * Results: * Boolean * * Side effects: * None. * *---------------------------------------------------------------------- */ NSF_INLINE static int CmdIsNsfObject(Tcl_Command cmd) nonnull(1); NSF_INLINE static int CmdIsNsfObject(Tcl_Command cmd) { assert(cmd); return Tcl_Command_objProc(cmd) == NsfObjDispatch; } /* *---------------------------------------------------------------------- * GetTclProcFromCommand -- * * Check if cmd refers to a Tcl proc, and if so, return the proc * definition. * * Results: * The found proc of cmd or NULL. * * Side effects: * None * *---------------------------------------------------------------------- */ static Proc *GetTclProcFromCommand(Tcl_Command cmd) nonnull(1); static Proc * GetTclProcFromCommand(Tcl_Command cmd) { Tcl_ObjCmdProc *proc; assert(cmd); proc = Tcl_Command_objProc(cmd); if (proc == TclObjInterpProc) { return (Proc *)Tcl_Command_objClientData(cmd); } return NULL; } /* *---------------------------------------------------------------------- * FindMethod -- * * Lookup the cmd for methodName in a namespace. * * Results: * The found cmd of the method or NULL. * * Side effects: * None * *---------------------------------------------------------------------- */ NSF_INLINE static Tcl_Command FindMethod(Tcl_Namespace *nsPtr, CONST char *methodName) { register Tcl_HashEntry *entryPtr; assert(nsPtr != NULL); assert(methodName != NULL); if ((entryPtr = Tcl_CreateHashEntry(Tcl_Namespace_cmdTablePtr(nsPtr), methodName, NULL))) { return (Tcl_Command) Tcl_GetHashValue(entryPtr); } return NULL; } /* *---------------------------------------------------------------------- * FindProcMethod -- * * Lookup the proc for methodName in a namespace. * * Results: * The found proc of the method or NULL. * * Side effects: * None * *---------------------------------------------------------------------- */ static Proc * FindProcMethod(Tcl_Namespace *nsPtr, CONST char *methodName) nonnull(1) nonnull(2); static Proc * FindProcMethod(Tcl_Namespace *nsPtr, CONST char *methodName) { Tcl_Command cmd; assert(nsPtr); assert(methodName); cmd = FindMethod(nsPtr, methodName); return cmd ? GetTclProcFromCommand(cmd) : NULL; } /* *---------------------------------------------------------------------- * SearchPLMethod, SearchPLMethod0 -- * * Search a method along a provided class list. The methodName must be * simple (must not contain space). While SearchPLMethod() allows to * specify a flag for filtering the command, SearchPLMethod0() is a lightly * optimized function without the filtering option. * * Results: * The found class defining the method or NULL. * * Side effects: * None * *---------------------------------------------------------------------- */ static NsfClass * SearchPLMethod(register NsfClasses *pl, CONST char *methodName, Tcl_Command *cmdPtr, unsigned int flags) nonnull(1) nonnull(2) nonnull(3); static NsfClass * SearchPLMethod0(register NsfClasses *pl, CONST char *methodName, Tcl_Command *cmdPtr) nonnull(1) nonnull(2) nonnull(3); static NsfClass * SearchPLMethod0(register NsfClasses *pl, CONST char *methodName, Tcl_Command *cmdPtr) { assert(pl); assert(methodName); assert(cmdPtr); /* Search the precedence list (class hierarchy) */ for (; pl; pl = pl->nextPtr) { register Tcl_HashEntry *entryPtr = Tcl_CreateHashEntry(Tcl_Namespace_cmdTablePtr(pl->cl->nsPtr), methodName, NULL); if (entryPtr != NULL) { *cmdPtr = (Tcl_Command) Tcl_GetHashValue(entryPtr); return pl->cl; } } return NULL; } static NsfClass * SearchPLMethod(register NsfClasses *pl, CONST char *methodName, Tcl_Command *cmdPtr, unsigned int flags) { assert(pl); assert(methodName); assert(cmdPtr); /* Search the precedence list (class hierarchy) */ for (; pl; pl = pl->nextPtr) { register Tcl_HashEntry *entryPtr = Tcl_CreateHashEntry(Tcl_Namespace_cmdTablePtr(pl->cl->nsPtr), methodName, NULL); if (entryPtr != NULL) { Tcl_Command cmd = (Tcl_Command) Tcl_GetHashValue(entryPtr); if (unlikely(Tcl_Command_flags(cmd) & flags)) { /*fprintf(stderr, "skipped cmd %p flags %.6x & %.6x => %.6x\n", cmd, flags, Tcl_Command_flags(cmd), Tcl_Command_flags(cmd) & flags);*/ continue; } *cmdPtr = cmd; return pl->cl; } } return NULL; } /* *---------------------------------------------------------------------- * SearchCMethod -- * * Search a method along the superclass hierarchy of the provided * class. The methodObj must be simple (must not contain * space). The method has the interface for internal calls during * interpretation, while SearchSimpleCMethod() has the interface * with more overhead for introspection. * * Results: * The found class defining the method or NULL. * * Side effects: * None * *---------------------------------------------------------------------- */ static NsfClass * SearchCMethod(/*@notnull@*/ NsfClass *cl, CONST char *methodName, Tcl_Command *cmdPtr) nonnull(1) nonnull(2) nonnull(3); static NsfClass * SearchCMethod(/*@notnull@*/ NsfClass *cl, CONST char *methodName, Tcl_Command *cmdPtr) { assert(methodName); assert(cmdPtr); assert(cl); return SearchPLMethod0(PrecedenceOrder(cl), methodName, cmdPtr); } /* *---------------------------------------------------------------------- * SearchSimpleCMethod -- * * Search a method along the superclass hierarchy of the provided * class. The methodObj must be simple (must not contain * space). The method has the same interface as * SearchComplexCMethod(). * * Results: * The found class defining the method or NULL. * * Side effects: * None * *---------------------------------------------------------------------- */ static NsfClass * SearchSimpleCMethod(Tcl_Interp *interp, /*@notnull@*/ NsfClass *cl, Tcl_Obj *methodObj, Tcl_Command *cmdPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static NsfClass * SearchSimpleCMethod(Tcl_Interp *interp, /*@notnull@*/ NsfClass *cl, Tcl_Obj *methodObj, Tcl_Command *cmdPtr) { assert(interp); assert(cl); assert(methodObj); assert(cmdPtr); return SearchPLMethod0(PrecedenceOrder(cl), ObjStr(methodObj), cmdPtr); } /* *---------------------------------------------------------------------- * SearchComplexCMethod -- * * Search a method along the superclass hierarchy of the provided * class. The methodObj can refer to an ensemble object (can * contain space). The method has the same interface as * SearchSimpleCMethod(). * * Results: * The found class defining the method or NULL. * * Side effects: * None * *---------------------------------------------------------------------- */ static NsfClass * SearchComplexCMethod(Tcl_Interp *interp, /*@notnull@*/ NsfClass *cl, Tcl_Obj *methodObj, Tcl_Command *cmdPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static NsfClass * SearchComplexCMethod(Tcl_Interp *interp, /*@notnull@*/ NsfClass *cl, Tcl_Obj *methodObj, Tcl_Command *cmdPtr) { NsfClasses *pl; int fromClassNS = 1; assert(interp); assert(cl); assert(methodObj); assert(cmdPtr); for (pl = PrecedenceOrder(cl); pl; pl = pl->nextPtr) { Tcl_Command cmd = ResolveMethodName(interp, pl->cl->nsPtr, methodObj, NULL, NULL, NULL, NULL, &fromClassNS); if (cmd) { *cmdPtr = cmd; return pl->cl; } } return NULL; } /* *---------------------------------------------------------------------- * ObjectFindMethod -- * * Find a method for a given object in the precedence path. The * provided methodObj might be an ensemble object. This function * tries to optimize access by calling different implementations * for simple and ensemble method names. * * Results: * Tcl command. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_Command ObjectFindMethod(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *methodObj, NsfClass **pcl) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static Tcl_Command ObjectFindMethod(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *methodObj, NsfClass **pcl) { Tcl_Command cmd = NULL; NsfClass *(*lookupFunction)(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *methodObj, Tcl_Command *cmdPtr); assert(interp); assert(object); assert(methodObj); assert(pcl); if (strchr(ObjStr(methodObj), ' ') != NULL) { lookupFunction = SearchComplexCMethod; } else { lookupFunction = SearchSimpleCMethod; } if (unlikely(object->flags & NSF_MIXIN_ORDER_VALID) == 0) { MixinComputeDefined(interp, object); } if (object->flags & NSF_MIXIN_ORDER_DEFINED_AND_VALID) { NsfCmdList *mixinList; for (mixinList = object->mixinOrder; mixinList; mixinList = mixinList->nextPtr) { NsfClass *mixin = NsfGetClassFromCmdPtr(mixinList->cmdPtr); if (mixin && (*pcl = (*lookupFunction)(interp, mixin, methodObj, &cmd))) { if (Tcl_Command_flags(cmd) & NSF_CMD_CLASS_ONLY_METHOD && !NsfObjectIsClass(object)) { cmd = NULL; continue; } break; } } } if (cmd == NULL && object->nsPtr) { int fromClassNS = 0; cmd = ResolveMethodName(interp, object->nsPtr, methodObj, NULL, NULL, NULL, NULL, &fromClassNS); } if (cmd == NULL && object->cl) { *pcl = (*lookupFunction)(interp, object->cl, methodObj, &cmd); } return cmd; } /* *---------------------------------------------------------------------- * GetObjectSystem -- * * Return the object system for which the object was defined * * Results: * Object system pointer * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfObjectSystem * GetObjectSystem(NsfObject *object) nonnull(1); static NsfObjectSystem * GetObjectSystem(NsfObject *object) { assert(object); if (NsfObjectIsClass(object)) { return ((NsfClass *)object)->osPtr; } assert(object->cl); return object->cl->osPtr; } /* *---------------------------------------------------------------------- * ObjectSystemFree -- * * Free a single object system structure including its root classes. * * Results: * None. * * Side effects: * Free memory of structure, free the root classes. * *---------------------------------------------------------------------- */ static void ObjectSystemFree(Tcl_Interp *interp, NsfObjectSystem *osPtr) nonnull(1) nonnull(2); static void ObjectSystemFree(Tcl_Interp *interp, NsfObjectSystem *osPtr) { int idx; assert(interp); assert(osPtr); for (idx = 0; idx <= NSF_s_set_idx; idx++) { if (osPtr->methods[idx]) { DECR_REF_COUNT(osPtr->methods[idx]); } if (osPtr->handles[idx]) { DECR_REF_COUNT(osPtr->handles[idx]); } } if (osPtr->rootMetaClass && osPtr->rootClass) { RemoveSuper(osPtr->rootMetaClass, osPtr->rootClass); RemoveInstance((NsfObject *)osPtr->rootMetaClass, osPtr->rootMetaClass); RemoveInstance((NsfObject *)osPtr->rootClass, osPtr->rootMetaClass); FinalObjectDeletion(interp, &osPtr->rootClass->object); FinalObjectDeletion(interp, &osPtr->rootMetaClass->object); } FREE(NsfObjectSystem, osPtr); } /* *---------------------------------------------------------------------- * ObjectSystemAdd -- * * Add and entry to the list of object systems of the interpreter. * * Results: * None. * * Side effects: * Updating the per interp list of object systems. * *---------------------------------------------------------------------- */ static void ObjectSystemAdd(Tcl_Interp *interp, NsfObjectSystem *osPtr) nonnull(1) nonnull(2); static void ObjectSystemAdd(Tcl_Interp *interp, NsfObjectSystem *osPtr) { assert(interp); assert(osPtr); osPtr->nextPtr = RUNTIME_STATE(interp)->objectSystems; RUNTIME_STATE(interp)->objectSystems = osPtr; } /* *---------------------------------------------------------------------- * ObjectSystemsCleanup -- * * Delete all objects from all defined object systems. This method * is to be called when an Next Scripting process or thread exists. * * Results: * None. * * Side effects: * All commands and objects are deleted, memory is freed. * *---------------------------------------------------------------------- */ static int ObjectSystemsCleanup(Tcl_Interp *interp, int withKeepvars) nonnull(1); static int ObjectSystemsCleanup(Tcl_Interp *interp, int withKeepvars) { NsfCmdList *instances = NULL, *entryPtr; NsfObjectSystem *osPtr, *nPtr; assert(interp); /* Deletion is performed in two rounds: * (a) SOFT DESTROY: invoke all user-defined destroy methods * without destroying objects * (b) PHYSICAL DESTROY: delete the objects and classes, * destroy methods are not invoked anymore * * This is to prevent that the destroy order causes classes to be * deleted before the methods invoked by destroy are executed. Note * that it is necessary to iterate over all object systems * simultaneous, since the might be dependencies between objects of * different object systems. */ /* * Collect all instances from all object systems */ for (osPtr = RUNTIME_STATE(interp)->objectSystems; osPtr; osPtr = osPtr->nextPtr) { GetAllInstances(interp, &instances, osPtr->rootClass); } /***** SOFT DESTROY *****/ RUNTIME_STATE(interp)->exitHandlerDestroyRound = NSF_EXITHANDLER_ON_SOFT_DESTROY; /*fprintf(stderr, "===CALL destroy on OBJECTS\n");*/ for (entryPtr = instances; entryPtr; entryPtr = entryPtr->nextPtr) { NsfObject *object = (NsfObject *)entryPtr->clorobj; /*fprintf(stderr, "key = %s %p %d flags %.6x\n", ObjectName(object), object, object && !NsfObjectIsClass(object), object->flags);*/ if (object && !NsfObjectIsClass(object) && !(object->flags & NSF_DESTROY_CALLED)) { DispatchDestroyMethod(interp, object, 0); } } /*fprintf(stderr, "===CALL destroy on CLASSES\n");*/ for (entryPtr = instances; entryPtr; entryPtr = entryPtr->nextPtr) { NsfClass *cl = entryPtr->clorobj; if (cl && !(cl->object.flags & NSF_DESTROY_CALLED)) { DispatchDestroyMethod(interp, (NsfObject *)cl, 0); } } /* now, turn of filters, all destroy callbacks are done */ RUNTIME_STATE(interp)->doFilters = 0; #ifdef DO_CLEANUP FreeAllNsfObjectsAndClasses(interp, &instances); # ifdef DO_FULL_CLEANUP DeleteProcsAndVars(interp, Tcl_GetGlobalNamespace(interp), withKeepvars); # endif #endif /* now free all objects systems with their root classes */ for (osPtr = RUNTIME_STATE(interp)->objectSystems; osPtr; osPtr = nPtr) { nPtr = osPtr->nextPtr; ObjectSystemFree(interp, osPtr); } #ifdef DO_CLEANUP /* finally, free all nsfprocs */ DeleteNsfProcs(interp, NULL); #endif CmdListFree(&instances, NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * CallDirectly -- * * Determine when it is possible/necessary to call a method * implementation directly or via method dispatch. * * Results: * 1 is returned when command should be invoked directly, 0 * otherwise. * * Side effects: * methodObjPtr is set with the Tcl_Obj of the name of the method, * if there is one defined. * *---------------------------------------------------------------------- */ static int CallDirectly(Tcl_Interp *interp, NsfObject *object, int methodIdx, Tcl_Obj **methodObjPtr) nonnull(1) nonnull(2) nonnull(4); static int CallDirectly(Tcl_Interp *interp, NsfObject *object, int methodIdx, Tcl_Obj **methodObjPtr) { /* * We can/must call a C-implemented method directly, when * * a) the object system has no such appropriate method defined * * b) the script does not contain a method with the appropriate * name, and * * c) filters are not active on the object */ NsfObjectSystem *osPtr = GetObjectSystem(object); int callDirectly = 1; Tcl_Obj *methodObj; assert(interp); assert(object); assert(methodObjPtr); methodObj = osPtr->methods[methodIdx]; /*fprintf(stderr, "OS of %s is %s, method %s methodObj %p osPtr %p defined %.8x %.8x overloaded %.8x %.8x flags %.8x\n", ObjectName(object), ObjectName(&osPtr->rootClass->object), Nsf_SystemMethodOpts[methodIdx]+1, methodObj, osPtr, osPtr->definedMethods, osPtr->definedMethods & (1 << methodIdx), osPtr->overloadedMethods, osPtr->overloadedMethods & (1 << methodIdx), 1 << methodIdx );*/ if (methodObj) { unsigned int flag = 1 << methodIdx; if ((osPtr->overloadedMethods & flag) != 0) { /* overloaded, we must dispatch */ /*fprintf(stderr, "overloaded\n");*/ callDirectly = 0; } else if ((osPtr->definedMethods & flag) == 0) { /* not defined, we must call directly */ /*fprintf(stderr, "Warning: CallDirectly object %s idx %s not defined\n", ObjectName(object), Nsf_SystemMethodOpts[methodIdx]+1);*/ } else { #if DISPATCH_ALWAYS_DEFINED_METHODS callDirectly = 0; #else if (!(object->flags & NSF_FILTER_ORDER_VALID)) { FilterComputeDefined(interp, object); } /*fprintf(stderr, "CallDirectly object %s idx %s object flags %.6x %.6x \n", ObjectName(object), Nsf_SystemMethodOpts[methodIdx]+1, (object->flags & NSF_FILTER_ORDER_DEFINED_AND_VALID), NSF_FILTER_ORDER_DEFINED_AND_VALID);*/ if ((object->flags & NSF_FILTER_ORDER_DEFINED_AND_VALID) == NSF_FILTER_ORDER_DEFINED_AND_VALID) { /*fprintf(stderr, "CallDirectly object %s idx %s has filter \n", ObjectName(object), Nsf_SystemMethodOpts[methodIdx]+1);*/ callDirectly = 0; } #endif } } /*fprintf(stderr, "CallDirectly object %s idx %d returns %s => %d\n", ObjectName(object), methodIdx, methodObj ? ObjStr(methodObj) : "(null)", callDirectly);*/ /* return the methodObj in every case */ *methodObjPtr = methodObj; return callDirectly; } /* *---------------------------------------------------------------------- * NsfMethodObj -- * * Return the methodObj for a given method index. * * Results: * Returns Tcl_Obj* or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj * NsfMethodObj(NsfObject *object, int methodIdx) nonnull(1); Tcl_Obj * NsfMethodObj(NsfObject *object, int methodIdx) { NsfObjectSystem *osPtr = GetObjectSystem(object); assert(object); /* fprintf(stderr, "NsfMethodObj object %s os %p idx %d %s methodObj %p\n", ObjectName(object), osPtr, methodIdx, Nsf_SystemMethodOpts[methodIdx]+1, osPtr->methods[methodIdx]); */ return osPtr->methods[methodIdx]; } /* * conditional memory allocations of optional storage */ static NsfObjectOpt * NsfRequireObjectOpt(NsfObject *object) { assert(object); if (object->opt == NULL) { object->opt = NEW(NsfObjectOpt); memset(object->opt, 0, sizeof(NsfObjectOpt)); } return object->opt; } NsfClassOpt * NsfRequireClassOpt(/*@notnull@*/ NsfClass *cl) { assert(cl); if (cl->opt == NULL) { cl->opt = NEW(NsfClassOpt); memset(cl->opt, 0, sizeof(NsfClassOpt)); if (cl->object.flags & NSF_IS_CLASS) { cl->opt->id = cl->object.id; /* probably a temporary solution */ } } return cl->opt; } static void MakeObjNamespace(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static void MakeObjNamespace(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); #ifdef NAMESPACE_TRACE fprintf(stderr, "+++ MakeObjNamespace for %s\n", ObjectName(object)); #endif if (object->nsPtr == NULL) { Tcl_Namespace *nsPtr; nsPtr = object->nsPtr = NSGetFreshNamespace(interp, object, ObjectName(object)); assert(nsPtr); /* * Copy all obj variables to the newly created namespace */ if (object->varTablePtr) { Tcl_HashSearch search; Tcl_HashEntry *hPtr; TclVarHashTable *varTablePtr = Tcl_Namespace_varTablePtr(nsPtr); Tcl_HashTable *varHashTablePtr = TclVarHashTablePtr(varTablePtr); Tcl_HashTable *objHashTablePtr = TclVarHashTablePtr(object->varTablePtr); *varHashTablePtr = *objHashTablePtr; /* copy the table */ if (objHashTablePtr->buckets == objHashTablePtr->staticBuckets) { varHashTablePtr->buckets = varHashTablePtr->staticBuckets; } for (hPtr = Tcl_FirstHashEntry(varHashTablePtr, &search); hPtr; hPtr = Tcl_NextHashEntry(&search)) { hPtr->tablePtr = varHashTablePtr; } CallStackReplaceVarTableReferences(interp, object->varTablePtr, (TclVarHashTable *)varHashTablePtr); ckfree((char *) object->varTablePtr); object->varTablePtr = NULL; } } } static Tcl_Var CompiledLocalsLookup(CallFrame *varFramePtr, CONST char *varName) nonnull(1) nonnull(2); static Tcl_Var CompiledLocalsLookup(CallFrame *varFramePtr, CONST char *varName) { int i, localCt = varFramePtr->numCompiledLocals; Tcl_Obj **objPtrPtr = &varFramePtr->localCachePtr->varName0; assert(varFramePtr); assert(varName); /* fprintf(stderr, ".. search #local vars %d for %s\n", localCt, varName);*/ for (i = 0 ; i < localCt ; i++, objPtrPtr++) { Tcl_Obj *objPtr = *objPtrPtr; if (likely(objPtr != NULL)) { char *localName = TclGetString(objPtr); if (unlikely(varName[0] == localName[0] && varName[1] == localName[1] && strcmp(varName, localName) == 0)) { return (Tcl_Var) &varFramePtr->compiledLocals[i]; } } } return NULL; } /* *---------------------------------------------------------------------- * GetVarAndNameFromHash -- * * Conveniance function to obtain variable and name from * a variable hash entry * * Results: * Results are passed back in argument 2 and 3 * * Side effects: * None. * *---------------------------------------------------------------------- */ static void GetVarAndNameFromHash(Tcl_HashEntry *hPtr, Var **val, Tcl_Obj **varNameObj) nonnull(1) nonnull(2) nonnull(3); static void GetVarAndNameFromHash(Tcl_HashEntry *hPtr, Var **val, Tcl_Obj **varNameObj) { assert(hPtr); assert(val); assert(varNameObj); *val = TclVarHashGetValue(hPtr); *varNameObj = TclVarHashGetKey(*val); } /********************************************************* * * Variable resolvers * *********************************************************/ #define FOR_COLON_RESOLVER(ptr) (*(ptr) == ':' && *((ptr)+1) != ':') /* *---------------------------------------------------------------------- * MethodName -- * * Return the methodName from a Tcl_Obj, strips potentially the * colon prefix * * Results: * method name * * Side effects: * None. * *---------------------------------------------------------------------- */ static CONST char *MethodName(Tcl_Obj *methodObj) nonnull(1) returns_nonnull; static CONST char * MethodName(Tcl_Obj *methodObj) { char *methodName; assert(methodObj); methodName = ObjStr(methodObj); if (FOR_COLON_RESOLVER(methodName)) { methodName ++; } return methodName; } CONST char * NsfMethodName(Tcl_Obj *methodObj) { assert(methodObj); return MethodName(methodObj); } /* *---------------------------------------------------------------------- * NsfMethodNamePath -- * * Compute the full method name for error messages containing the * ensemble root. * * Results: * Tcl_Obj, caller has to take care for ref-counting * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj * NsfMethodNamePath(Tcl_Interp *interp, Tcl_CallFrame *framePtr, CONST char *methodName) { Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL); assert(interp); assert(methodName); if (framePtr) { Tcl_ListObjAppendList(interp, resultObj, CallStackMethodPath(interp, framePtr)); } Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(methodName,-1)); return resultObj; } /* *---------------------------------------------------------------------- * NsColonVarResolver -- * * Namespace resolver for namespace specific variable lookup. * colon prefix * * Results: * method name * * Side effects: * None. * *---------------------------------------------------------------------- */ static int NsColonVarResolver(Tcl_Interp *interp, CONST char *varName, Tcl_Namespace *UNUSED(nsPtr), int flags, Tcl_Var *varPtr) nonnull(1) nonnull(2) nonnull(5); static int NsColonVarResolver(Tcl_Interp *interp, CONST char *varName, Tcl_Namespace *UNUSED(nsPtr), int flags, Tcl_Var *varPtr) { Tcl_CallFrame *varFramePtr; TclVarHashTable *varTablePtr; NsfObject *object; int new, frameFlags; Tcl_Obj *key; assert(interp); assert(varName); assert(varPtr); #if defined (VAR_RESOLVER_TRACE) fprintf(stderr, "NsColonVarResolver '%s' flags %.6x\n", varName, flags); #endif /* * Case 1: The variable is to be resolved in global scope, proceed in * resolver chain */ if (unlikely(flags & TCL_GLOBAL_ONLY)) { /*fprintf(stderr, "global-scoped lookup for var '%s' in NS '%s'\n", varName, nsPtr->fullName);*/ return TCL_CONTINUE; } /* * Case 2: The lookup happens in a proc frame (lookup in compiled * locals and hash-table vars). We are not interested to handle * these cases here, so proceed in resolver chain. */ varFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); assert(varFramePtr); frameFlags = Tcl_CallFrame_isProcCallFrame(varFramePtr); #if defined (VAR_RESOLVER_TRACE) fprintf(stderr, "NsColonVarResolver '%s' frame flags %.6x\n", varName, Tcl_CallFrame_isProcCallFrame(varFramePtr)); #endif if (frameFlags & FRAME_IS_PROC) { #if defined (VAR_RESOLVER_TRACE) fprintf(stderr, "...... forwarding to next resolver\n"); #endif /*fprintf(stderr, "proc-scoped var '%s' assumed, frame %p flags %.6x\n", name, varFramePtr, Tcl_CallFrame_isProcCallFrame(varFramePtr));*/ return TCL_CONTINUE; } /* * FRAME_IS_NSF_CMETHOD has always FRAME_IS_PROC set, so it is * handled already above */ assert((frameFlags & FRAME_IS_NSF_CMETHOD) == 0); if ((frameFlags & FRAME_IS_NSF_OBJECT) == 0) { /* * Case 3: we are not in an Next Scripting frame, so proceed as well */ return TCL_CONTINUE; } else { /* * Case 4: we are in an Next Scripting object frame */ if (*varName == ':') { if (*(varName+1) != ':') { /* * Case 4a: The variable name starts with a single ":". Skip * the char, but stay in the resolver. */ varName ++; } else { /* * Case 4b: Names starting with "::" are not for us */ return TCL_CONTINUE; } } else if (NSTail(varName) != varName) { /* * Case 4c: Names containing "::" are not for us */ return TCL_CONTINUE; } /* * Since we know that we are here always in an object frame, we * can blindly get the object from the client data . */ object = (NsfObject *)Tcl_CallFrame_clientData(varFramePtr); } /* * We have an object and create the variable if not found */ assert(object); varTablePtr = object->nsPtr ? Tcl_Namespace_varTablePtr(object->nsPtr) : object->varTablePtr; assert(varTablePtr); /* * Does the variable exist in the object's namespace? */ key = Tcl_NewStringObj(varName, -1); INCR_REF_COUNT(key); *varPtr = (Tcl_Var)VarHashCreateVar(varTablePtr, key, NULL); #if defined (VAR_RESOLVER_TRACE) fprintf(stderr, "...... lookup of '%s' for object '%s' returns %p\n", varName, ObjectName(object), *varPtr); #endif if (*varPtr == NULL) { /* * We failed to find the variable so far, therefore we create it * in this var table. Note that in several cases above, * TCL_CONTINUE takes care for variable creation. */ Var *newVar = VarHashCreateVar(varTablePtr, key, &new); *varPtr = (Tcl_Var)newVar; } DECR_REF_COUNT(key); return *varPtr ? TCL_OK : TCL_ERROR; } /********************************************************* * * Begin of compiled var resolver * *********************************************************/ typedef struct NsfResolvedVarInfo { Tcl_ResolvedVarInfo vInfo; /* This must be the first element. */ NsfObject *lastObject; Tcl_Var var; Tcl_Obj *nameObj; } NsfResolvedVarInfo; /* *---------------------------------------------------------------------- * HashVarFree -- * * Free hashed variables based on refCount. * * Results: * None. * * Side effects: * Changed refCount or freed variable. * *---------------------------------------------------------------------- */ NSF_INLINE static void HashVarFree(Tcl_Var var) { if (unlikely(VarHashRefCount(var) < 2)) { /*fprintf(stderr,"#### free %p\n", var);*/ ckfree((char *) var); } else { VarHashRefCount(var)--; } } /* *---------------------------------------------------------------------- * CompiledColonVarFetch -- * * This function is the actual variable resolution handler for a * colon-prefixed (":/varName/") found in a compiled script * registered by the compiling var resolver (see * InterpCompiledColonResolver()). When initializing a call frame, * this handler is called, crawls the object's var table (creating * a variable, if needed), and returns a Var structure. Based on * this, a link variable ":/varName/" pointing to this object * variable (i.e., "varName") is created and is stored in the * compiled locals array of the call frame. Beware that these link * variables interact with the family of link-creating commands * ([variable], [global], [upvar]) by being subject to * "retargeting" upon name conflicts (see * tests/varresolutiontest.tcl for some examples). * * Results: * Tcl_Var containing value or NULL. * * Side effects: * Updates of Variable structure cache in necessary. * *---------------------------------------------------------------------- */ static Tcl_Var CompiledColonVarFetch(Tcl_Interp *interp, Tcl_ResolvedVarInfo *vinfoPtr) nonnull(1) nonnull(2); static Tcl_Var CompiledColonVarFetch(Tcl_Interp *interp, Tcl_ResolvedVarInfo *vinfoPtr) { NsfResolvedVarInfo *resVarInfo = (NsfResolvedVarInfo *)vinfoPtr; NsfCallStackContent *cscPtr = CallStackGetTopFrame0(interp); NsfObject *object; TclVarHashTable *varTablePtr; Tcl_Var var = resVarInfo->var; int new; assert(interp); assert(vinfoPtr); #if defined(VAR_RESOLVER_TRACE) unsigned int flags = var ? ((Var *)var)->flags : 0; fprintf(stderr,"CompiledColonVarFetch var '%s' var %p flags = %.4x dead? %.4x\n", ObjStr(resVarInfo->nameObj), var, flags, flags & VAR_DEAD_HASH); #endif if (likely(cscPtr != NULL)) { object = cscPtr->self; } else { object = NULL; } /* * We cache lookups based on nsf objects; we have to care about * cases, where the instance variables are in some delete states. * */ if ((var && object == resVarInfo->lastObject && (((((Var *)var)->flags) & VAR_DEAD_HASH)) == 0)) { /* * The variable is valid. */ #if defined(VAR_RESOLVER_TRACE) fprintf(stderr, ".... cached var '%s' var %p flags = %.4x\n", ObjStr(resVarInfo->nameObj), var, ((Var *)var)->flags); #endif return var; } if (unlikely(object == NULL)) { return NULL; } if (var) { /* * The variable is not valid anymore. Clean it up. */ HashVarFree(var); } if (object->nsPtr) { varTablePtr = Tcl_Namespace_varTablePtr(object->nsPtr); } else if (object->varTablePtr) { varTablePtr = object->varTablePtr; } else { /* * In most situations, we have a varTablePtr through the clauses * above. However, if someone redefines e.g. the method "configure" or * "objectparameter", we might find an object with an still empty * varTable, since these are lazy initiated. */ varTablePtr = object->varTablePtr = VarHashTableCreate(); } assert(varTablePtr); resVarInfo->lastObject = object; #if defined(VAR_RESOLVER_TRACE) fprintf(stderr,"Fetch var %s in object %s\n", TclGetString(resVarInfo->nameObj), ObjectName(object)); #endif resVarInfo->var = var = (Tcl_Var) VarHashCreateVar(varTablePtr, resVarInfo->nameObj, &new); /* * Increment the reference counter to avoid ckfree() of the variable * in Tcl's FreeVarEntry(); for cleanup, we provide our own * HashVarFree(); */ VarHashRefCount(var)++; #if defined(VAR_RESOLVER_TRACE) { Var *v = (Var *)(resVarInfo->var); fprintf(stderr, ".... looked up existing var %s var %p flags = %.6x undefined %d\n", ObjStr(resVarInfo->nameObj), v, v->flags, TclIsVarUndefined(v)); } #endif return var; } /* *---------------------------------------------------------------------- * CompiledColonVarFree -- * * DeleteProc of the compiled variable handler. * * Results: * None. * * Side effects: * Free compiled variable structure and variable. * *---------------------------------------------------------------------- */ static void CompiledColonVarFree(Tcl_ResolvedVarInfo *vInfoPtr) nonnull(1); static void CompiledColonVarFree(Tcl_ResolvedVarInfo *vInfoPtr) { NsfResolvedVarInfo *resVarInfo = (NsfResolvedVarInfo *)vInfoPtr; assert(vInfoPtr); #if defined(VAR_RESOLVER_TRACE) fprintf(stderr, "CompiledColonVarFree %p for variable '%s'\n", resVarInfo, ObjStr(resVarInfo->nameObj)); #endif DECR_REF_COUNT(resVarInfo->nameObj); if (resVarInfo->var) {HashVarFree(resVarInfo->var);} FREE(NsfResolvedVarInfo, vInfoPtr); } /* *---------------------------------------------------------------------- * InterpCompiledColonVarResolver -- * * For colon-prefixed (":/varName/") variables, we provide our own * var resolver for compiling scripts and evaluating compiled * scripts (e.g., proc bodies). At the time of first compilation * (or re-compilation), this resolver is processed (see * tclProc.c:InitResolvedLocals()). It registers two handlers for a * given, colon-prefixed variable found in the script: the actual * variable fetcher and a variable cleanup handler. The variable * fetcher is executed whenever a Tcl call frame is initialized and * the array of compiled locals is constructed (see also * InitResolvedLocals()). * * The Tcl var resolver protocol dictates that per-namespace * compiling var resolvers take precedence over this per-interp * compiling var resolver. That is, per-namespace resolvers are * processed first and can effectively outrule per-interp * resolvers by signaling TCL_OK or TCL_BREAK. * * Results: * TCL_OK or TCL_CONTINUE (according to Tcl's var resolver protocol) * * Side effects: * Registers per-variable resolution and cleanup handlers. * *---------------------------------------------------------------------- */ static int InterpCompiledColonVarResolver(Tcl_Interp *interp, CONST84 char *name, int length, Tcl_Namespace *UNUSED(context), Tcl_ResolvedVarInfo **rPtr) nonnull(1) nonnull(2) nonnull(5); static int InterpCompiledColonVarResolver(Tcl_Interp *interp, CONST84 char *name, int length, Tcl_Namespace *UNUSED(context), Tcl_ResolvedVarInfo **rPtr) { /* * The variable handler is registered, when we have an active Next Scripting * object and the variable starts with the appropriate prefix. Note * that getting the "self" object is a weak protection against * handling of wrong vars */ NsfObject *object = GetSelfObj(interp); assert(interp); assert(name); assert(rPtr); #if defined(VAR_RESOLVER_TRACE) fprintf(stderr, "compiled var resolver for %s, obj %p\n", name, object); #endif if (object && FOR_COLON_RESOLVER(name)) { NsfResolvedVarInfo *resVarInfo = NEW(NsfResolvedVarInfo); resVarInfo->vInfo.fetchProc = CompiledColonVarFetch; resVarInfo->vInfo.deleteProc = CompiledColonVarFree; /* if NULL, Tcl does a ckfree on proc clean up */ resVarInfo->lastObject = NULL; resVarInfo->var = NULL; resVarInfo->nameObj = Tcl_NewStringObj(name+1, length-1); INCR_REF_COUNT(resVarInfo->nameObj); #if defined(VAR_RESOLVER_TRACE) fprintf(stderr, "... resVarInfo %p nameObj %p '%s' obj %p %s\n", resVarInfo, resVarInfo->nameObj, ObjStr(resVarInfo->nameObj), object, ObjectName(object)); #endif *rPtr = (Tcl_ResolvedVarInfo *)resVarInfo; return TCL_OK; } return TCL_CONTINUE; } /* *---------------------------------------------------------------------- * InterpGetFrameAndFlags -- * * Return for the provided interp the flags of the frame (returned as * result) and the actual varFrame (returned in the second argument). In * case, the toplevel frame is a LAMBDA frame, skip it. * * Results: * Frame flags, varFrame * * Side effects: * None. * *---------------------------------------------------------------------- */ NSF_INLINE static int InterpGetFrameAndFlags(Tcl_Interp *interp, CallFrame **framePtr) nonnull(1) nonnull(2); NSF_INLINE static int InterpGetFrameAndFlags(Tcl_Interp *interp, CallFrame **framePtr) { int frameFlags; assert(interp); assert(framePtr); *framePtr = Tcl_Interp_varFramePtr(interp); frameFlags = Tcl_CallFrame_isProcCallFrame(*framePtr); /* * If the resolver is called from a lambda frame, use always the parent frame */ if ((frameFlags & FRAME_IS_LAMBDA)) { *framePtr = (CallFrame *)Tcl_CallFrame_callerPtr(*framePtr); frameFlags = Tcl_CallFrame_isProcCallFrame(*framePtr); #if defined(VAR_RESOLVER_TRACE) fprintf(stderr, "InterpColonVarResolver skip lambda frame flags %.6x\n", Tcl_CallFrame_isProcCallFrame(*framePtr)); #endif } #if defined(VAR_RESOLVER_TRACE) fprintf(stderr, "... final frame flags %.6x\n",frameFlags); #endif return frameFlags; } /* *---------------------------------------------------------------------- * InterpColonVarResolver -- * * For accessing object (instance) variables using the colon-prefix * notation (":/varName/"), we provide our own var resolvers. This * function is the non-compiling var resolver; its services are * requested in two situations: a) when evaluating non-compiled * statements, b) when executing slow-path bytecode instructions, * with "slow path" referring to bytecode instructions not making * use of the compiled locals array (and, e.g., reverting to * TclObjLookupVar*() calls). * * The Tcl var resolver protocol dictates that per-namespace, * non-compiling var resolvers take precedence over this per-interp * non-compiling var resolver. That is, per-namespace resolvers are * processed first and can effectively outrule per-interp resolvers * by signaling TCL_OK or TCL_BREAK. See * e.g. TclLookupSimpleVar(). * * Results: * TCL_OK or TCL_CONTINUE (according to on Tcl's var resolver protocol) * * Side effects: * If successful, return varPtr, pointing to instance variable. * *---------------------------------------------------------------------- */ static int InterpColonVarResolver(Tcl_Interp *interp, CONST char *varName, Tcl_Namespace *UNUSED(nsPtr), int flags, Tcl_Var *varPtr) nonnull(1) nonnull(2) nonnull(5); static int InterpColonVarResolver(Tcl_Interp *interp, CONST char *varName, Tcl_Namespace *UNUSED(nsPtr), int flags, Tcl_Var *varPtr) { int new, frameFlags; CallFrame *varFramePtr; TclVarHashTable *varTablePtr; NsfObject *object; Tcl_Obj *keyObj; Tcl_Var var; assert(interp); assert(varName); assert(varPtr); /* * TCL_GLOBAL_ONLY is removed, since "vwait :varName" is called with * with this flag. */ if (!FOR_COLON_RESOLVER(varName) || (flags & (/*TCL_GLOBAL_ONLY|*/TCL_NAMESPACE_ONLY))) { /* ordinary names and global lookups are not for us */ #if defined(VAR_RESOLVER_TRACE) fprintf(stderr, "InterpColonVarResolver '%s' flags %.6x not for us\n", varName, flags); #endif return TCL_CONTINUE; } frameFlags = InterpGetFrameAndFlags(interp, &varFramePtr); if (likely(frameFlags & FRAME_IS_NSF_METHOD)) { if ((*varPtr = CompiledLocalsLookup(varFramePtr, varName))) { /* * This section is reached under notable circumstances and represents a * point of interaction between our resolvers for non-compiled (i.e., * InterpColonVarResolver()) and compiled script execution (i.e., * InterpCompiledColonVarResolver()). * * Expect this branch to be hit iff... * * 1. ... InterpCompiledColonVarResolver() is called from within the Tcl * bytecode interpreter when executing a bytecode-compiled script on a * *slow path* (i.e., involving a TclObjLookupVarEx() call) * * 2. ... the act of variable resolution (i.e., TclObjLookupVarEx()) has * not been restricted to the global (TCL_GLOBAL_ONLY) or an effective * namespace (TCL_NAMESPACE_ONLY) * * 3. ..., resulting from the fact of participating in an bytecode * interpretation, CompiledColonVarFetch() stored a link variable * (pointing to the actual/real object variable, whether defined or not) * under the given varName value into the current call frame's array of * compiled locals (when initializing the call frame; see * tclProc.c:InitResolvedLocals()). */ #if defined(VAR_RESOLVER_TRACE) fprintf(stderr, ".... found local %s varPtr %p flags %.6x\n", varName, *varPtr, flags); #endif /* * By looking up the compiled-local directly and signaling TCL_OK, we * optimise a little by avoiding further lookups down the Tcl var * resolution infrastructure. Note that signaling TCL_CONTINUE would * work too, however, it would involve extra resolution overhead. */ return TCL_OK; } object = ((NsfCallStackContent *)varFramePtr->clientData)->self; } else if (frameFlags & FRAME_IS_NSF_CMETHOD) { object = ((NsfCallStackContent *)varFramePtr->clientData)->self; } else if (frameFlags & FRAME_IS_NSF_OBJECT) { object = (NsfObject *)(varFramePtr->clientData); } else { #if defined(VAR_RESOLVER_TRACE) fprintf(stderr, ".... not found %s\n", varName); #endif return TCL_CONTINUE; } /* * Trim the varName for the colon prefix (":"). */ varName ++; /* * We have an object and create the variable if not found */ assert(object); if (unlikely(object->nsPtr != NULL)) { varTablePtr = Tcl_Namespace_varTablePtr(object->nsPtr); } else if (likely(object->varTablePtr != NULL)) { varTablePtr = object->varTablePtr; } else { /* * In most situations, we have a varTablePtr through the clauses * above. However, if someone redefines e.g. the method "configure" or * "objectparameter", we might find an object with an still empty * varTable, since these are lazy initiated. */ varTablePtr = object->varTablePtr = VarHashTableCreate(); } assert(varTablePtr); /*fprintf(stderr, "Object Var Resolver, name=%s, obj %p, nsPtr %p, varTablePtr %p\n", varName, object, object->nsPtr, varTablePtr);*/ keyObj = Tcl_NewStringObj(varName, -1); INCR_REF_COUNT(keyObj); var = (Tcl_Var)VarHashCreateVar(varTablePtr, keyObj, NULL); if (likely(var != NULL)) { #if defined(VAR_RESOLVER_TRACE) fprintf(stderr, ".... found in hash-table %s %p flags %.6x ns %p\n", varName, var, ((Var *)var)->flags, object->nsPtr); #endif /* make coverage analysis easier */ assert(1); } else { /* * We failed to find the variable, therefore we create it new */ var = (Tcl_Var)VarHashCreateVar(varTablePtr, keyObj, &new); #if defined(VAR_RESOLVER_TRACE) fprintf(stderr, ".... var %p %s created in hash-table %p\n", var, varName, varTablePtr); #endif } *varPtr = var; DECR_REF_COUNT(keyObj); return TCL_OK; } /********************************************************* * * End of var resolvers * *********************************************************/ /********************************************************* * * Begin of cmd resolver * *********************************************************/ /* *---------------------------------------------------------------------- * InterpColonCmdResolver -- * * Resolve command names. If the command starts with the Next * Scripting specific prefix and we are on an Next Scripting stack * frame, treat command as OO method. * * Results: * TCL_OK or TCL_CONTINUE (based on Tcl's command resolver protocol) * * Side effects: * If successful, return cmdPtr, pointing to method. * *---------------------------------------------------------------------- */ static int InterpColonCmdResolver(Tcl_Interp *interp, CONST char *cmdName, Tcl_Namespace *UNUSED(nsPtr), unsigned int flags, Tcl_Command *cmdPtr) nonnull(1) nonnull(2) nonnull(5); static int InterpColonCmdResolver(Tcl_Interp *interp, CONST char *cmdName, Tcl_Namespace *UNUSED(nsPtr), unsigned int flags, Tcl_Command *cmdPtr) { CallFrame *varFramePtr; int frameFlags; assert(interp); assert(cmdName); assert(cmdPtr); /* fprintf(stderr, "InterpColonCmdResolver %s flags %.6x\n", cmdName, flags); */ if (likely((*cmdName == ':' && *(cmdName + 1) == ':') || flags & TCL_GLOBAL_ONLY)) { /* fully qualified names and global lookups are not for us */ /*fprintf(stderr, "... not for us %s flags %.6x\n", cmdName, flags);*/ return TCL_CONTINUE; } frameFlags = InterpGetFrameAndFlags(interp, &varFramePtr); /* * The resolver is called as well, when a body of a method is * compiled. In these situations, Tcl stacks a non-proc frame, that * we have to skip. In order to safely identify such situations, we * stuff into the call flags of the proc frame during the * compilation step NSF_CSC_CALL_IS_COMPILE. */ if (frameFlags == 0 && Tcl_CallFrame_callerPtr(varFramePtr)) { varFramePtr = (CallFrame *)Tcl_CallFrame_callerPtr(varFramePtr); frameFlags = Tcl_CallFrame_isProcCallFrame(varFramePtr); if ((frameFlags & (FRAME_IS_NSF_METHOD)) == 0 || (((NsfCallStackContent *)varFramePtr->clientData)->flags & NSF_CSC_CALL_IS_COMPILE) == 0 ) { frameFlags = 0; } else { #if defined(CMD_RESOLVER_TRACE) fprintf(stderr, "InterpColonCmdResolver got parent frame cmdName %s flags %.6x, frame flags %.6x\n", cmdName, flags, Tcl_CallFrame_isProcCallFrame(varFramePtr)); #endif } } #if defined(CMD_RESOLVER_TRACE) fprintf(stderr, "InterpColonCmdResolver cmdName %s flags %.6x, frame flags %.6x\n", cmdName, flags, frameFlags); #endif if (frameFlags & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_OBJECT|FRAME_IS_NSF_CMETHOD )) { if (*cmdName == ':') { #if defined(CMD_RESOLVER_TRACE) fprintf(stderr, " ... call colonCmd for %s\n", cmdName); #endif /* * We have a cmd starting with ':', we are in an nsf frame, so * forward to the colonCmd. */ *cmdPtr = RUNTIME_STATE(interp)->colonCmd; return TCL_OK; } else { #if defined(NSF_WITH_OS_RESOLVER) /* * Experimental Object-System specific resolver: If an * un-prefixed method name is found in a body of a method, we try * to perform a lookup for this method in the namespace of the * object system for the current object. If this lookup is not * successful the standard lookups are performed. The * object-system specific resolver allows to use the "right" * (un-prefixed) "self" or "next" calls without namespace * imports. */ NsfObject *object; NsfObjectSystem *osPtr; if (frameFlags & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { object = ((NsfCallStackContent *)varFramePtr->clientData)->self; } else if (frameFlags & (FRAME_IS_NSF_OBJECT)) { object = (NsfObject *)(varFramePtr->clientData); } else { object = NULL; } if (object) { Tcl_HashEntry *entryPtr; Tcl_HashTable *cmdTablePtr; Tcl_Command cmd; osPtr = GetObjectSystem(object); cmd = osPtr->rootClass->object.id; cmdTablePtr = Tcl_Namespace_cmdTablePtr(((Command *)cmd)->nsPtr); entryPtr = Tcl_CreateHashEntry(cmdTablePtr, cmdName, NULL); /*fprintf(stderr, "InterpColonCmdResolver OS specific resolver tried to lookup %s for os %s in ns %s\n", cmdName, ClassName(osPtr->rootClass), ((Command *)cmd)->nsPtr->fullName);*/ if (entryPtr) { /*fprintf(stderr, "InterpColonCmdResolver OS specific resolver found %s::%s\n", ((Command *)cmd)->nsPtr->fullName, cmdName);*/ *cmdPtr = Tcl_GetHashValue(entryPtr); return TCL_OK; } } #endif } } #if defined(CMD_RESOLVER_TRACE) fprintf(stderr, " ... not found %s\n", cmdName); NsfShowStack(interp); #endif return TCL_CONTINUE; } /********************************************************* * * End of cmd resolver * *********************************************************/ /* *---------------------------------------------------------------------- * NsfNamespaceInit -- * * Initialize a provided namespace by setting its resolvers and * namespace path * * Results: * none * * Side effects: * change ns behavior * *---------------------------------------------------------------------- */ static void NsfNamespaceInit(Tcl_Namespace *nsPtr) nonnull(1); static void NsfNamespaceInit(Tcl_Namespace *nsPtr) { assert(nsPtr); /* * This puts a per-object namespace resolver into position upon * acquiring the namespace. Works for object-scoped commands/procs * and object-only ones (set, unset, ...) */ Tcl_SetNamespaceResolvers(nsPtr, (Tcl_ResolveCmdProc *)NULL, NsColonVarResolver, (Tcl_ResolveCompiledVarProc *)NULL); #if defined(NSF_WITH_INHERIT_NAMESPACES) /* * In case there is a namespace path set for the parent namespace, * apply this as well to the object namespace to avoid surprises * with "namespace path nx". */ { Namespace *parentNsPtr = Tcl_Namespace_parentPtr(nsPtr); int pathLength = Tcl_Namespace_commandPathLength(parentNsPtr); if (pathLength > 0) { Namespace **pathArray = (Namespace **)ckalloc(sizeof(Namespace *) * pathLength); NamespacePathEntry *tmpPathArray = Tcl_Namespace_commandPathArray(parentNsPtr); int i; for (i = 0; i < pathLength; i++) { pathArray[i] = tmpPathArray[i].nsPtr; } TclSetNsPath((Namespace *)nsPtr, pathLength, (Tcl_Namespace **)pathArray); ckfree((char *)pathArray); } } #endif } static NsfObject * NSNamespaceClientDataObject(ClientData clientData) { #ifdef NSF_MEM_COUNT NsfNamespaceClientData *nsClientData = (NsfNamespaceClientData *)clientData; assert(clientData); /*fprintf(stderr, "NSNamespaceDeleteProc cd %p\n", clientData); fprintf(stderr, "... nsPtr %p name '%s'\n", nsClientData->nsPtr, nsClientData->nsPtr->fullName);*/ return nsClientData->object; #else assert(clientData); return (NsfObject *) clientData; #endif } /* *---------------------------------------------------------------------- * SlotContainerCmdResolver -- * * This is a specialized cmd resolver for slotcontainer. The command * resolver should be registered for a namespace and avoids the lookup of * childobjs for unqualified calls. This way, it is e.g. possible to call * in a slot-obj a method [list], even in cases, where a a property "list" * is defined. * * Results: * either TCL_CONTINUE or TCL_OK; * * Side effects: * None. * *---------------------------------------------------------------------- */ static int SlotContainerCmdResolver(Tcl_Interp *interp, CONST char *cmdName, Tcl_Namespace *nsPtr, unsigned int flags, Tcl_Command *cmdPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(5); static int SlotContainerCmdResolver(Tcl_Interp *interp, CONST char *cmdName, Tcl_Namespace *nsPtr, unsigned int flags, Tcl_Command *cmdPtr) { assert(cmdName); assert(nsPtr); assert(cmdPtr); if (*cmdName == ':' || (flags & TCL_GLOBAL_ONLY)) { /* colon names (InterpColonCmdResolver) and global lookups are not for us */ return TCL_CONTINUE; } /*fprintf(stderr, "SlotContainerCmdResolver called with %s ns %s ourNs %d clientData %p\n", cmdName, nsPtr->fullName, nsPtr->deleteProc == NSNamespaceDeleteProc, nsPtr->clientData);*/ /* * Check, if this already a namespace handled by NSF */ if (nsPtr->deleteProc == NSNamespaceDeleteProc && nsPtr->clientData) { NsfObject *parentObject = NSNamespaceClientDataObject(nsPtr->clientData); /*fprintf(stderr, "SlotContainerCmdResolver parentObject %p %s\n", parentObject, ObjectName(parentObject));*/ /* * Make global lookups when the parent is a slotcontainer */ /* parentObject = (NsfObject *) GetObjectFromString(interp, nsPtr->fullName);*/ if ((parentObject->flags & NSF_IS_SLOT_CONTAINER)) { Tcl_Command cmd = Tcl_FindCommand(interp, cmdName, NULL, TCL_GLOBAL_ONLY); if (cmd) { *cmdPtr = cmd; return TCL_OK; } } } return TCL_CONTINUE; } /* *---------------------------------------------------------------------- * RequireObjNamespace -- * * Obtain for an object a namespace if necessary and initialize it. * In this function, variables existing outside of the namespace * get copied over to thew fresh namespace. * * Results: * Tcl_Namespace * * Side effects: * Allocate pot. a namespace * *---------------------------------------------------------------------- */ static Tcl_Namespace * RequireObjNamespace(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); if (object->nsPtr == NULL) { MakeObjNamespace(interp, object); NsfNamespaceInit(object->nsPtr); } assert(object->nsPtr); return object->nsPtr; } /* * Namespace related commands */ /* *---------------------------------------------------------------------- * NSNamespacePreserve -- * * Increment namespace refCount * * Results: * void * * Side effects: * None. * *---------------------------------------------------------------------- */ static void NSNamespacePreserve(Tcl_Namespace *nsPtr) { assert(nsPtr); MEM_COUNT_ALLOC("NSNamespace", nsPtr); Tcl_Namespace_refCount(nsPtr)++; } /* *---------------------------------------------------------------------- * NSNamespaceRelease -- * * Decrement namespace refCount and free namespace if necessary * * Results: * void * * Side effects: * Free pot. memory * *---------------------------------------------------------------------- */ static void NSNamespaceRelease(Tcl_Namespace *nsPtr) { assert(nsPtr); MEM_COUNT_FREE("NSNamespace", nsPtr); Tcl_Namespace_refCount(nsPtr)--; if (unlikely(Tcl_Namespace_refCount(nsPtr) == 0 && (Tcl_Namespace_flags(nsPtr) & NS_DEAD))) { /* * The namespace refCount has reached 0, we have to free * it. unfortunately, NamespaceFree() is not exported */ /*fprintf(stderr, "HAVE TO FREE namespace %p\n", nsPtr); */ /*NamespaceFree(nsPtr);*/ ckfree(nsPtr->fullName); ckfree(nsPtr->name); ckfree((char *)nsPtr); } } /* *---------------------------------------------------------------------- * NSDeleteCmd -- * * Delete the Tcl command for the provided methodName located in * the provided namespace. * * Results: * Tcl result or -1, if no such method exists int. * * Side effects: * Command is deleted. * *---------------------------------------------------------------------- */ static int NSDeleteCmd(Tcl_Interp *interp, Tcl_Namespace *nsPtr, CONST char *methodName) { Tcl_Command token; assert(interp); assert(nsPtr); assert(methodName); if ((token = FindMethod(nsPtr, methodName))) { return Tcl_DeleteCommandFromToken(interp, token); } return -1; } /* *---------------------------------------------------------------------- * NSDeleteChild -- * * Delete a child of an object in cases, when the parent object is * deleted. It is designed to delete either objects or classes to * be a little bit more graceful on destuctors. Not perfect yet. * * Results: * None. * * Side effects: * Might destroy an object. * *---------------------------------------------------------------------- */ static int NSDeleteChild(Tcl_Interp *interp, Tcl_Command cmd, int deleteObjectsOnly) nonnull(1) nonnull(2); static int NSDeleteChild(Tcl_Interp *interp, Tcl_Command cmd, int deleteObjectsOnly) { assert(cmd); assert(interp); /*fprintf(stderr, "NSDeleteChildren child %p flags %.6x epoch %d\n", cmd, Tcl_Command_flags(cmd), Tcl_Command_cmdEpoch(cmd));*/ /* * In some situations (e.g. small buckets, less than 12 entries), we * get from the cmd-table already deleted cmds; we had previously an * assert(Tcl_Command_cmdEpoch(cmd) == 0); * which will fail in such cases. */ if (!Tcl_Command_cmdEpoch(cmd)) { NsfObject *object = NsfGetObjectFromCmdPtr(cmd); /*fprintf(stderr, "NSDeleteChildren child %p (%s) epoch %d\n", cmd, Tcl_GetCommandName(interp, cmd), Tcl_Command_cmdEpoch(cmd));*/ if (object == NULL) { /* * This is just a plain Tcl command; let Tcl handle the * deletion. */ return 0; } /*fprintf(stderr, "NSDeleteChild check %p %s true child %d\n", object, ObjectName(object), object->id == cmd);*/ /* delete here just true children */ if (object->id == cmd) { if (deleteObjectsOnly && NsfObjectIsClass(object)) { return 0; } /*fprintf(stderr, "NSDeleteChild destroy %p %s\n", object, ObjectName(object));*/ /* in the exit handler physical destroy --> directly call destroy */ if (RUNTIME_STATE(interp)->exitHandlerDestroyRound == NSF_EXITHANDLER_ON_PHYSICAL_DESTROY) { PrimitiveDestroy(object); return 1; } else { if (object->teardown && !(object->flags & NSF_DESTROY_CALLED)) { int result = DispatchDestroyMethod(interp, object, 0); if (unlikely(result != TCL_OK)) { /* * The destroy method failed. However, we have to remove * the command anyway, since its parent is currently being * deleted. */ if (object->teardown) { NsfLog(interp, NSF_LOG_NOTICE, "Destroy failed for object %s, perform low level deletion", ObjectName(object)); CallStackDestroyObject(interp, object); } } return 1; } } } else { /*fprintf(stderr, "NSDeleteChild remove alias %p %s\n", object, Tcl_GetCommandName(interp, cmd));*/ return AliasDeleteObjectReference(interp, cmd); } } return 0; } /* *---------------------------------------------------------------------- * NSDeleteChildren -- * * Delete the child objects of a namespace. * * Results: * None. * * Side effects: * Might destroy child objects. * *---------------------------------------------------------------------- */ static void NSDeleteChildren(Tcl_Interp *interp, Tcl_Namespace *nsPtr) nonnull(1) nonnull(2); static void NSDeleteChildren(Tcl_Interp *interp, Tcl_Namespace *nsPtr) { Tcl_HashTable *cmdTablePtr = Tcl_Namespace_cmdTablePtr(nsPtr); Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; int expected; assert(interp); assert(nsPtr); #ifdef OBJDELETION_TRACE fprintf(stderr, "NSDeleteChildren %p %s activationCount %d\n", nsPtr, nsPtr->fullName, Tcl_Namespace_activationCount(nsPtr)); #endif /* * First, get rid of namespace imported objects; don't delete the * object, but the reference. */ Tcl_ForgetImport(interp, nsPtr, "*"); /* don't destroy namespace imported objects */ #if OBJDELETION_TRACE /* * Deletion is always tricky. Show, what elements should be deleted * in this loop. The actually deleted elements might be actually * less, if a deletion of one item triggers the destroy of another * item. */ for (hPtr = Tcl_FirstHashEntry(cmdTablePtr, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { Tcl_Command cmd = (Tcl_Command)Tcl_GetHashValue(hPtr); fprintf(stderr, "will destroy %p %s\n", cmd, Tcl_GetCommandName(interp, cmd)); } #endif /* * Second, delete the objects. */ /* * A destroy of one element of the hash-table can trigger the * destroy of another item of the same table. Therefore we use * Nsf_NextHashEntry(), which handles this case. */ for (hPtr = Tcl_FirstHashEntry(cmdTablePtr, &hSrch); hPtr; hPtr = Nsf_NextHashEntry(cmdTablePtr, expected, &hSrch)) { /*Tcl_Command cmd = (Tcl_Command)Tcl_GetHashValue(hPtr); fprintf(stderr, "NSDeleteChild %p table %p numEntries before %d\n", cmd, hPtr->tablePtr, cmdTablePtr->numEntries );*/ expected = cmdTablePtr->numEntries - NSDeleteChild(interp, (Tcl_Command)Tcl_GetHashValue(hPtr), 1); } /* * Finally, delete the classes. */ for (hPtr = Tcl_FirstHashEntry(cmdTablePtr, &hSrch); hPtr; hPtr = Nsf_NextHashEntry(cmdTablePtr, expected, &hSrch)) { expected = cmdTablePtr->numEntries - NSDeleteChild(interp, (Tcl_Command)Tcl_GetHashValue(hPtr), 0); } } /* * delete all vars & procs in a namespace */ static void NSCleanupNamespace(Tcl_Interp *interp, Tcl_Namespace *nsPtr) nonnull(1) nonnull(2); static void NSCleanupNamespace(Tcl_Interp *interp, Tcl_Namespace *nsPtr) { TclVarHashTable *varTablePtr = Tcl_Namespace_varTablePtr(nsPtr); Tcl_HashTable *cmdTablePtr = Tcl_Namespace_cmdTablePtr(nsPtr); Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; assert(interp); assert(nsPtr); #ifdef OBJDELETION_TRACE fprintf(stderr, "NSCleanupNamespace %p flags %.6x\n", nsPtr, Tcl_Namespace_flags(nsPtr)); fprintf(stderr, "NSCleanupNamespace %p %.6x varTablePtr %p\n", nsPtr, ((Namespace *)nsPtr)->flags, varTablePtr); #endif /* * Delete all variables and initialize var table again * (DeleteVars frees the var-table) */ TclDeleteVars((Interp *)interp, varTablePtr); TclInitVarHashTable(varTablePtr, (Namespace *)nsPtr); /* * Delete all user-defined procs in the namespace */ for (hPtr = Tcl_FirstHashEntry(cmdTablePtr, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { Tcl_Command cmd = (Tcl_Command) Tcl_GetHashValue(hPtr); if (CmdIsNsfObject(cmd)) { /* * Sub-objects should not be deleted here to preserve children * deletion order. Just delete aliases. */ AliasDeleteObjectReference(interp, cmd); continue; } /*fprintf(stderr, "NSCleanupNamespace calls DeleteCommandFromToken for %p flags %.6x\n", cmd, ((Command *)cmd)->flags); fprintf(stderr, " cmd = %s\n", Tcl_GetCommandName(interp, cmd)); fprintf(stderr, " nsPtr = %p\n", ((Command *)cmd)->nsPtr); fprintf(stderr, " epoch = %d\n", Tcl_Command_cmdEpoch(cmd)); fprintf(stderr, " refCount = %d\n", Tcl_Command_refCount(cmd)); fprintf(stderr, " flags %.6x\n", ((Namespace *)((Command *)cmd)->nsPtr)->flags);*/ Tcl_DeleteCommandFromToken(interp, cmd); } } static void NSNamespaceDeleteProc(ClientData clientData) { NsfObject *object = NSNamespaceClientDataObject(clientData); assert(clientData); #ifdef NSF_MEM_COUNT ckfree((char *)clientData); #endif assert(object); /*fprintf(stderr, "namespace delete-proc obj=%p ns=%p\n", clientData, object ? object->nsPtr : NULL);*/ MEM_COUNT_FREE("NSNamespace", object->nsPtr); object->nsPtr = NULL; } void Nsf_DeleteNamespace(Tcl_Interp *interp, Tcl_Namespace *nsPtr) nonnull(1) nonnull(2); void Nsf_DeleteNamespace(Tcl_Interp *interp, Tcl_Namespace *nsPtr) { int activationCount = 0; Tcl_CallFrame *f = (Tcl_CallFrame *)Tcl_Interp_framePtr(interp); assert(interp); assert(nsPtr); /*fprintf(stderr, "Nsf_DeleteNamespace %p ", nsPtr);*/ while (f) { if (f->nsPtr == nsPtr) { activationCount++; } f = Tcl_CallFrame_callerPtr(f); } #if !defined(NDEBUG) if (Tcl_Namespace_activationCount(nsPtr) != activationCount) { fprintf(stderr, "WE HAVE TO FIX ACTIVATIONCOUNT\n"); Tcl_Namespace_activationCount(nsPtr) = activationCount; } #endif assert(Tcl_Namespace_activationCount(nsPtr) == activationCount); /*fprintf(stderr, "to %d. \n", ((Namespace *)nsPtr)->activationCount);*/ if (Tcl_Namespace_deleteProc(nsPtr)) { /*fprintf(stderr, "calling deteteNamespace %s\n", nsPtr->fullName);*/ Tcl_DeleteNamespace(nsPtr); } } /* *---------------------------------------------------------------------- * NSValidObjectName -- * * Check the provided colons in an object name. If the name is * valid, the function returns 1, otherwise 0. * * Results: * returns 1 on success * * Side effects: * none * *---------------------------------------------------------------------- */ NSF_INLINE static int NSValidObjectName(CONST char *name, size_t l) nonnull(1); NSF_INLINE static int NSValidObjectName(CONST char *name, size_t l) { register CONST char *n = name; assert(name); if (*n == '\0') return 0; /* empty name */ if (l == 0) l = strlen(name); if (*(n+l-1) == ':') return 0; /* name ends with : */ if (*n == ':' && *(n+1) != ':') return 0; /* name begins with single : */ for (; *n != '\0'; n++) { if (*n == ':' && *(n+1) == ':' && *(n+2) == ':') { return 0; /* more than 2 colons in series in a name */ } } return 1; } /* *---------------------------------------------------------------------- * NSGetFreshNamespace -- * * Create an object namespace, provide a deleteProc (avoid * interference between object and namespace deletion order) and * keep the object as client data. * * Results: * Tcl_Namespace * * Side effects: * might allocate a namespace * *---------------------------------------------------------------------- */ static Tcl_Namespace* NSGetFreshNamespace(Tcl_Interp *interp, NsfObject *object, CONST char *name) { Namespace *dummy1Ptr, *dummy2Ptr, *nsPtr; const char *dummy; assert(interp); assert(object); assert(name); TclGetNamespaceForQualName(interp, name, NULL, TCL_FIND_ONLY_NS|TCL_CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1Ptr, &dummy2Ptr, &dummy); if (nsPtr->deleteProc != NSNamespaceDeleteProc) { /* * Avoid hijacking a namespace with different client data */ if (nsPtr->deleteProc || nsPtr->clientData) { Tcl_Panic("Namespace '%s' exists already with delProc %p and clientData %p; " "Can only convert a plain Tcl namespace into an nsf namespace, my delete Proc %p", name, nsPtr->deleteProc, nsPtr->clientData, NSNamespaceDeleteProc); } { #ifdef NSF_MEM_COUNT NsfNamespaceClientData *nsClientData = (NsfNamespaceClientData *)ckalloc(sizeof(NsfNamespaceClientData)); nsClientData->object = object; nsClientData->nsPtr = (Tcl_Namespace *)nsPtr; nsPtr->clientData = nsClientData; /*fprintf(stderr, "Adding NsfNamespaceClientData nsPtr %p cd %p name '%s'\n", nsPtr, nsClientData, nsPtr->fullName);*/ #else nsPtr->clientData = object; #endif nsPtr->deleteProc = (Tcl_NamespaceDeleteProc *)NSNamespaceDeleteProc; } MEM_COUNT_ALLOC("NSNamespace", nsPtr); } else { fprintf(stderr, "NSGetFreshNamespace: reusing namespace %p %s\n", nsPtr, nsPtr->fullName); } return (Tcl_Namespace *)nsPtr; } /* *---------------------------------------------------------------------- * NSRequireParentObject -- * * Try to require a parent object (e.g. during ttrace). This function * tries to load a parent object via ::nsf::object::unknown. * * Results: * returns 1 on success * * Side effects: * might create an object * *---------------------------------------------------------------------- */ static int NSRequireParentObject(Tcl_Interp *interp, CONST char *parentName) nonnull(1) nonnull(2); static int NSRequireParentObject(Tcl_Interp *interp, CONST char *parentName) { int result; assert(interp); assert(parentName); result = NsfCallObjectUnknownHandler(interp, Tcl_NewStringObj(parentName, -1)); if (likely(result == TCL_OK)) { NsfObject *parentObj = (NsfObject *) GetObjectFromString(interp, parentName); if (parentObj) { RequireObjNamespace(interp, parentObj); } result = (Tcl_FindNamespace(interp, parentName, (Tcl_Namespace *) NULL, TCL_GLOBAL_ONLY) != NULL); } return result; } /* *---------------------------------------------------------------------- * NSCheckNamespace -- * * Check if a namespace with the given name exists. If not, make * sure that a potential parent object has already required a * namespace. If there is no parent namespace yet, try to create a * parent object via __unknown. * If the provided parentNsPtr is not NULL, we know, that (a) the * provided name was relative and simple (contains no ":" * characters) and that (b) this namespace was used to build a fully * qualified name. In theses cases, the parentNsPtr points already * to the parentName, containing potentially a parent Object. In * all other cases, the parent name is either obtained from the * full namespace, or from string operations working on the * provided name. * * Results: * Tcl_Namespace for the provided name * * Side effects: * might create parent objects * *---------------------------------------------------------------------- */ NSF_INLINE static Tcl_Namespace *NSCheckNamespace(Tcl_Interp *interp, CONST char *nameString, Tcl_Namespace *parentNsPtr1) nonnull(1) nonnull(2); NSF_INLINE static Tcl_Namespace * NSCheckNamespace(Tcl_Interp *interp, CONST char *nameString, Tcl_Namespace *parentNsPtr1) { Namespace *nsPtr, *dummy1Ptr, *dummy2Ptr, *parentNsPtr = (Namespace *)parentNsPtr1; CONST char *parentName, *dummy, *n; Tcl_DString ds, *dsPtr = &ds; int parentNameLength; assert(interp); assert(nameString); /*fprintf(stderr, "NSCheckNamespace %s parentNsPtr %p\n", nameString, parentNsPtr);*/ /* * Check, if there is a already a namespace for the full name. The * namespace will be seldomly here, but we have to make this check * in every case. If there is a full namespace, we can use it to * determine the parent name. */ TclGetNamespaceForQualName(interp, nameString, NULL, TCL_GLOBAL_ONLY|TCL_FIND_ONLY_NS, &nsPtr, &dummy1Ptr, &dummy2Ptr, &dummy); /*fprintf(stderr, "before create calls TclGetNamespaceForQualName with %s => %p (%s) %p %s %p %s %p %s\n", nameString, nsPtr, nsPtr ? nsPtr->fullName : "", dummy1Ptr, dummy1Ptr ? dummy1Ptr->fullName : "", parentNsPtr, parentNsPtr ? parentNsPtr->fullName : "", dummy, dummy ? dummy : "");*/ /* * If there is a parentNs provided (or obtained from the full * namespace), we can determine the parent name from it. Otherwise, * we have to to perform the string operations. */ if (parentNsPtr == NULL && nsPtr) { parentNsPtr = Tcl_Namespace_parentPtr(nsPtr); } if (parentNsPtr) { parentNameLength = 0; parentName = parentNsPtr->fullName; if (*(parentName + 2) == '\0') { parentName = NULL; } /*fprintf(stderr, "NSCheckNamespace parentNs %s parentName of '%s' => '%s'\n", parentNsPtr->fullName, nameString, parentName);*/ } else { n = nameString + strlen(nameString); /* * search for last '::' */ while ((*n != ':' || *(n-1) != ':') && n-1 > nameString) {n--; } if (*n == ':' && n > nameString && *(n-1) == ':') {n--;} parentNameLength = n-nameString; if (parentNameLength > 0) { DSTRING_INIT(dsPtr); Tcl_DStringAppend(dsPtr, nameString, parentNameLength); parentName = Tcl_DStringValue(dsPtr); } else { parentName = NULL; } } if (parentName) { NsfObject *parentObj; parentObj = (NsfObject *) GetObjectFromString(interp, parentName); /*fprintf(stderr, "parentName %s parentObj %p\n", parentName, parentObj);*/ if (parentObj) { RequireObjNamespace(interp, parentObj); } else if (nsPtr == NULL && parentNsPtr == NULL) { TclGetNamespaceForQualName(interp, parentName, NULL, TCL_GLOBAL_ONLY|TCL_FIND_ONLY_NS, &parentNsPtr, &dummy1Ptr, &dummy2Ptr, &dummy); if (parentNsPtr == NULL) { /*fprintf(stderr, "===== calling NSRequireParentObject %s %p\n", parentName, cl);*/ NSRequireParentObject(interp, parentName); } } if (parentNameLength) { DSTRING_FREE(dsPtr); } } return (Tcl_Namespace *)nsPtr; } /* *---------------------------------------------------------------------- * NSFindCommand -- * * Find the "real" command belonging eg. to an Next Scripting class or object. * Do not return cmds produced by Tcl_Import, but the "real" cmd * to which they point. * * Results: * Tcl_Command or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ NSF_INLINE static Tcl_Command NSFindCommand(Tcl_Interp *interp, CONST char *name) nonnull(1) nonnull(2); NSF_INLINE static Tcl_Command NSFindCommand(Tcl_Interp *interp, CONST char *name) { Tcl_Command cmd; assert(interp); assert(name); assert(*name == ':' && *(name + 1) == ':'); cmd = Tcl_FindCommand(interp, name, NULL, TCL_GLOBAL_ONLY); if (likely(cmd != NULL)) { Tcl_Command importedCmd = TclGetOriginalCommand(cmd); if (unlikely(importedCmd != NULL)) { cmd = importedCmd; } } return cmd; } #if !defined(NDEBUG) /* *---------------------------------------------------------------------- * ReverseLookupCmdFromCmdTable -- * * Allows for looking up objects in command tables (e.g., namespace cmd * tables, the interp's hidden cmd table) based on their command pointer * (rather than their command name). * * Results: * NsfObject* or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ReverseLookupCmdFromCmdTable(Tcl_Interp *interp /* needed? */, Tcl_Command searchCmdPtr, Tcl_HashTable *cmdTablePtr) nonnull(1) nonnull(3); static int ReverseLookupCmdFromCmdTable(Tcl_Interp *interp /* needed? */, Tcl_Command searchCmdPtr, Tcl_HashTable *cmdTablePtr) { Tcl_HashSearch search; Tcl_HashEntry *hPtr; assert(searchCmdPtr); assert(cmdTablePtr); for (hPtr = Tcl_FirstHashEntry(cmdTablePtr, &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { Tcl_Command needleCmdPtr = (Tcl_Command)Tcl_GetHashValue(hPtr); if (needleCmdPtr == searchCmdPtr) { return 1; } } return 0; } /* *---------------------------------------------------------------------- * GetHiddenObjectFromCmd -- * * Obtains a hidden object for a specified cmd. The function uses a reverse * lookup of *hidden* object structures based on their commands. This * helper is needed for handling hidden and re-exposed objects during the * shutdown and the cleanup of object systems. * * Results: * NsfObject* or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfObject * GetHiddenObjectFromCmd(Tcl_Interp *interp, Tcl_Command cmdPtr) nonnull(1); static NsfObject * GetHiddenObjectFromCmd(Tcl_Interp *interp, Tcl_Command cmdPtr) { Interp *iPtr = (Interp *) interp; NsfObject *screenedObject; int found; assert(cmdPtr); /* * We can provide a shortcut, knowing that a) exposed cmds have an epoch * counter > 0, and b) the commands originating namespace must be the global * one. See also Tcl_HideCommand() and Tcl_ExposeCommand(). */ if (Tcl_Command_cmdEpoch(cmdPtr) == 0 || ((Command *)cmdPtr)->nsPtr != iPtr->globalNsPtr) { return NULL; } /* * Reverse lookup object in the interp's hidden command table. We start * off with the hidden cmds as we suspect their number being smaller than * the re-exposed ones, living in the global namespace */ found = ReverseLookupCmdFromCmdTable(interp, cmdPtr, iPtr->hiddenCmdTablePtr); if (!found) { /* * Reverse lookup object in the interp's global command table. Most likely * needed due to hiding + exposing on a different name. */ found = ReverseLookupCmdFromCmdTable(interp, cmdPtr, &iPtr->globalNsPtr->cmdTable); } screenedObject = found ? NsfGetObjectFromCmdPtr(cmdPtr) : NULL; #if !defined(NDEBUG) if (screenedObject) { NsfLog(interp, NSF_LOG_NOTICE, "screened object %s found: object %p (%s) cmd %p", Tcl_GetCommandName(interp, cmdPtr), screenedObject, ObjectName(screenedObject), cmdPtr); } #endif return screenedObject; } #endif /* *---------------------------------------------------------------------- * GetObjectFromString -- * * Lookup an object from a given string. The function performs a * command lookup (every object is a command) and checks, if the * command is bound to an nsf object. * * Results: * NsfObject* or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfObject * GetObjectFromString(Tcl_Interp *interp, CONST char *name) { register Tcl_Command cmd; assert(interp); assert(name); /*fprintf(stderr, "GetObjectFromString name = '%s'\n", name);*/ cmd = NSFindCommand(interp, name); if (likely(cmd && CmdIsNsfObject(cmd))) { /*fprintf(stderr, "GetObjectFromString %s => %p\n", name, Tcl_Command_objClientData(cmd));*/ return (NsfObject *)Tcl_Command_objClientData(cmd); } /*fprintf(stderr, "GetObjectFromString %s => NULL\n", name);*/ return NULL; } /* *---------------------------------------------------------------------- * GetClassFromString -- * * Lookup a class from a given string. The function performs an * object lookup and checks, if the object is a class * * Results: * NsfClass* or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfClass * GetClassFromString(Tcl_Interp *interp, CONST char *name) { NsfObject *object = GetObjectFromString(interp, name); assert(interp); assert(name); return (object && NsfObjectIsClass(object)) ? (NsfClass *)object : NULL; } /* *---------------------------------------------------------------------- * CanRedefineCmd -- * * This function tests, whether a method (provided as a string) is * allowed to be redefined in a provided namespace. * * Results: * Tcl result code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int CanRedefineCmd(Tcl_Interp *interp, Tcl_Namespace *nsPtr, NsfObject *object, CONST char *methodName, unsigned int flags) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int CanRedefineCmd(Tcl_Interp *interp, Tcl_Namespace *nsPtr, NsfObject *object, CONST char *methodName, unsigned int flags) { int result, ok; Tcl_Command cmd; assert(interp); assert(nsPtr); assert(object); assert(methodName); cmd = FindMethod(nsPtr, methodName); if (cmd) { if ( NsfGetObjectFromCmdPtr(cmd) != NULL) { /* * Don't allow overwriting of an object with an method. */ return NsfPrintError(interp, "refuse to overwrite child object with method %s; delete/rename it before overwriting", methodName); } ok = (Tcl_Command_flags(cmd) & NSF_CMD_REDEFINE_PROTECTED_METHOD) == 0; } else { ok = 1; } if (ok) { result = TCL_OK; } else { /* * We could test, whether we are bootstrapping the "right" object * system, and allow only overwrites for the current bootstrap * object system, but this seems necessary by now. */ Tcl_Obj *bootstrapObj = Tcl_GetVar2Ex(interp, "::nsf::bootstrap", NULL, TCL_GLOBAL_ONLY); if (bootstrapObj == NULL) { result = NsfPrintError(interp, "refuse to overwrite protected method '%s'; " "derive e.g. a sub-class!", methodName, ObjectName(object)); } else { result = TCL_OK; } } if (likely(result == TCL_OK)) { result = ObjectSystemsCheckSystemMethod(interp, methodName, object, flags); } return result; } /* *---------------------------------------------------------------------- * NsfAddObjectMethod -- * * Externally callable function to register an object level method * for the provided object. * * Results: * Tcl result code. * * Side effects: * Newly created Tcl command. * *---------------------------------------------------------------------- */ int NsfAddObjectMethod(Tcl_Interp *interp, Nsf_Object *object1, CONST char *methodName, Tcl_ObjCmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *dp, unsigned int flags) nonnull(1) nonnull(2) nonnull(3) nonnull(4); int NsfAddObjectMethod(Tcl_Interp *interp, Nsf_Object *object1, CONST char *methodName, Tcl_ObjCmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *dp, unsigned int flags) { NsfObject *object = (NsfObject *)object1; Tcl_DString newCmdName, *dsPtr = &newCmdName; Tcl_Namespace *ns = RequireObjNamespace(interp, object); Tcl_Command newCmd; int result; assert(interp); assert(object1); assert(methodName); assert(proc); /* Check, if we are allowed to redefine the method */ result = CanRedefineCmd(interp, object->nsPtr, object, (char *)methodName, flags); if (unlikely(result != TCL_OK)) { return result; } NsfObjectMethodEpochIncr("NsfAddObjectMethod"); /* delete an alias definition, if it exists */ AliasDelete(interp, object->cmdName, methodName, 1); Tcl_DStringInit(dsPtr); DStringAppendQualName(dsPtr, ns, methodName); newCmd = Tcl_CreateObjCommand(interp, Tcl_DStringValue(dsPtr), proc, clientData, dp); if (flags) { ((Command *) newCmd)->flags |= flags; } Tcl_DStringFree(dsPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * NsfAddClassMethod -- * * Externally callable function to register an class level method * for the provided class. * * Results: * Tcl result code. * * Side effects: * Newly created Tcl command. * *---------------------------------------------------------------------- */ int NsfAddClassMethod(Tcl_Interp *interp, Nsf_Class *class, CONST char *methodName, Tcl_ObjCmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *dp, unsigned int flags) nonnull(1) nonnull(2) nonnull(3) nonnull(4); int NsfAddClassMethod(Tcl_Interp *interp, Nsf_Class *class, CONST char *methodName, Tcl_ObjCmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *dp, unsigned int flags) { NsfClass *cl = (NsfClass *)class; Tcl_DString newCmdName, *dsPtr = &newCmdName; Tcl_Command newCmd; int result; assert(interp); assert(class); assert(methodName); assert(proc); /* Check, if we are allowed to redefine the method */ result = CanRedefineCmd(interp, cl->nsPtr, &cl->object, (char *)methodName, flags); if (unlikely(result != TCL_OK)) { return result; } NsfInstanceMethodEpochIncr("NsfAddClassMethod"); /* delete an alias definition, if it exists */ AliasDelete(interp, class->object.cmdName, methodName, 0); Tcl_DStringInit(dsPtr); DStringAppendQualName(dsPtr, cl->nsPtr, methodName); newCmd = Tcl_CreateObjCommand(interp, Tcl_DStringValue(dsPtr), proc, clientData, dp); if (flags) { ((Command *) newCmd)->flags |= flags; } Tcl_DStringFree(dsPtr); return TCL_OK; } /* * Auto-naming */ static Tcl_Obj * AutonameIncr(Tcl_Interp *interp, Tcl_Obj *nameObj, NsfObject *object, int instanceOpt, int resetOpt) nonnull(1) nonnull(2) nonnull(3); static Tcl_Obj * AutonameIncr(Tcl_Interp *interp, Tcl_Obj *nameObj, NsfObject *object, int instanceOpt, int resetOpt) { Tcl_Obj *valueObj, *resultObj = NULL; CallFrame frame, *framePtr = &frame; int flogs = TCL_LEAVE_ERR_MSG; assert(interp); assert(nameObj); assert(object); Nsf_PushFrameObj(interp, object, framePtr); if (object->nsPtr) { flogs |= TCL_NAMESPACE_ONLY; } valueObj = Tcl_ObjGetVar2(interp, NsfGlobalObjs[NSF_AUTONAMES], nameObj, flogs); if (valueObj) { long autoname_counter; /* should probably do an overflow check here */ Tcl_GetLongFromObj(interp, valueObj, &autoname_counter); autoname_counter++; if (Tcl_IsShared(valueObj)) { valueObj = Tcl_DuplicateObj(valueObj); } Tcl_SetLongObj(valueObj, autoname_counter); Tcl_ObjSetVar2(interp, NsfGlobalObjs[NSF_AUTONAMES], nameObj, valueObj, flogs); } if (resetOpt) { if (valueObj) { /* we have an entry */ Tcl_UnsetVar2(interp, NsfGlobalStrings[NSF_AUTONAMES], ObjStr(nameObj), flogs); } resultObj = NsfGlobalObjs[NSF_EMPTY]; INCR_REF_COUNT2("autoname", resultObj); } else { int mustCopy = 1, format = 0; char *c; if (valueObj == NULL) { valueObj = Tcl_ObjSetVar2(interp, NsfGlobalObjs[NSF_AUTONAMES], nameObj, NsfGlobalObjs[NSF_ONE], flogs); } if (instanceOpt) { char firstChar; CONST char *nextChars = ObjStr(nameObj); firstChar = *(nextChars ++); if (isupper((int)firstChar)) { char buffer[1]; buffer[0] = tolower((int)firstChar); resultObj = Tcl_NewStringObj(buffer, 1); INCR_REF_COUNT2("autoname", resultObj); Tcl_AppendLimitedToObj(resultObj, nextChars, -1, INT_MAX, NULL); mustCopy = 0; } } if (mustCopy) { resultObj = Tcl_DuplicateObj(nameObj); INCR_REF_COUNT2("autoname", resultObj); /* fprintf(stderr, "*** copy %p %s = %p\n", name, ObjStr(name), resultObj); */ } /* if we find a % in the autoname -> We use Tcl_FormatObjCmd to let the autoname string be formated, like Tcl "format" command, with the value. E.g.: autoname a%06d --> a000000, a000001, a000002, ... */ for (c = ObjStr(resultObj); *c != '\0'; c++) { if (*c == '%') { if (*(c+1) != '%') { format = 1; break; } else { /* when we find a %% we format and then append autoname, e.g. autoname a%% --> a%1, a%2, ... */ c++; } } } if (format) { Tcl_Obj *savedResultObj; ALLOC_ON_STACK(Tcl_Obj*, 3, ov); savedResultObj = Tcl_GetObjResult(interp); INCR_REF_COUNT(savedResultObj); ov[1] = resultObj; ov[2] = valueObj; if (NsfCallCommand(interp, NSF_FORMAT, 3, ov) != TCL_OK) { Nsf_PopFrameObj(interp, framePtr); DECR_REF_COUNT(savedResultObj); FREE_ON_STACK(Tcl_Obj*, ov); return NULL; } DECR_REF_COUNT(resultObj); resultObj = Tcl_DuplicateObj(Tcl_GetObjResult(interp)); INCR_REF_COUNT2("autoname", resultObj); Tcl_SetObjResult(interp, savedResultObj); DECR_REF_COUNT(savedResultObj); FREE_ON_STACK(Tcl_Obj*, ov); } else { char *valueString = Tcl_GetString(valueObj); Tcl_AppendLimitedToObj(resultObj, valueString, valueObj->length, INT_MAX, NULL); /*fprintf(stderr, "+++ append to obj done\n");*/ } } Nsf_PopFrameObj(interp, framePtr); assert((resetOpt && resultObj->refCount>=1) || (resultObj->refCount == 1)); return resultObj; } /* * Next Scripting CallStack functions */ NSF_INLINE static void CallStackDoDestroy(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); NSF_INLINE static void CallStackDoDestroy(Tcl_Interp *interp, NsfObject *object) { Tcl_Command oid; assert(interp); assert(object); /*fprintf(stderr, "CallStackDoDestroy %p flags %.6x\n", object, object->flags);*/ PRINTOBJ("CallStackDoDestroy", object); /* * Don't do anything, if a recursive DURING_DELETE is for some * reason active. */ if (object->flags & NSF_DURING_DELETE) { return; } /*fprintf(stderr, "CallStackDoDestroy %p flags %.6x activation %d object->refCount %d cmd %p \n", object, object->flags, object->activationCount, object->refCount, object->id);*/ object->flags |= NSF_DURING_DELETE; oid = object->id; /* oid might be freed already, we can't even use (((Command *)oid)->flags & CMD_IS_DELETED) */ if (object->teardown && oid) { /* * PrimitiveDestroy() has to be before DeleteCommandFromToken(), * otherwise e.g. unset traces on this object cannot be executed * from Tcl. We make sure via refCounting that the object * structure is kept until after DeleteCommandFromToken(). */ NsfObjectRefCountIncr(object); PrimitiveDestroy(object); if /*(object->teardown == NULL)*/ ((object->flags & NSF_TCL_DELETE) == 0) { Tcl_Obj *savedResultObj = Tcl_GetObjResult(interp); INCR_REF_COUNT(savedResultObj); assert(object->teardown == NULL); /*fprintf(stderr, " before DeleteCommandFromToken %p object flags %.6x\n", oid, object->flags);*/ /*fprintf(stderr, "cmd dealloc %p refCount %d dodestroy \n", oid, Tcl_Command_refCount(oid));*/ Tcl_DeleteCommandFromToken(interp, oid); /* this can change the result */ /*fprintf(stderr, " after DeleteCommandFromToken %p %.6x\n", oid, ((Command *)oid)->flags);*/ Tcl_SetObjResult(interp, savedResultObj); DECR_REF_COUNT(savedResultObj); } NsfCleanupObject(object, "CallStackDoDestroy"); } } static void CallStackDestroyObject(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); #ifdef OBJDELETION_TRACE fprintf(stderr, "CallStackDestroyObject %p %s activationcount %d flags %.6x\n", object, ObjectName(object), object->activationCount, object->flags); #endif if ((object->flags & NSF_DESTROY_CALLED) == 0) { int activationCount = object->activationCount; /* if the destroy method was not called yet, do it now */ #ifdef OBJDELETION_TRACE fprintf(stderr, " CallStackDestroyObject has to DispatchDestroyMethod %p activationCount %d\n", object, activationCount); #endif DispatchDestroyMethod(interp, object, 0); if (activationCount == 0) { /* * We assume, the object is now freed. If the object is already * freed, we cannot access activation count, and we cannot call * CallStackDoDestroy. */ /*fprintf(stderr, " CallStackDestroyObject %p done\n", obj);*/ return; } } /* * If the object is not referenced on the call-stack anymore * we have to destroy it directly, because CscFinish won't * find the object destroy. */ if (object->activationCount == 0) { CallStackDoDestroy(interp, object); } else { /* * To prevail the deletion order call delete children now -> children * destructors are called before parent's destructor. */ if (object->teardown && object->nsPtr) { /*fprintf(stderr, " CallStackDestroyObject calls NSDeleteChildren\n");*/ NSDeleteChildren(interp, object->nsPtr); } } /*fprintf(stderr, " CallStackDestroyObject %p DONE\n", object);*/ } /* * cmd list handling */ /* *---------------------------------------------------------------------- * CmdListAdd -- * * Add an entry to a cmdlist. Optionally, the function checks for * duplicates (does not insert a duplicate) or it allows to add to the end * of the list. * * Results: * The newly inserted command list item or a found item (never null) * * Side effects: * Added List entry. * *---------------------------------------------------------------------- */ static NsfCmdList *CmdListAdd(NsfCmdList **cList, Tcl_Command c, NsfClass *clorobj, int noDuplicates, int atEnd) nonnull(1) nonnull(2) returns_nonnull; static NsfCmdList * CmdListAdd(NsfCmdList **cList, Tcl_Command cmd, NsfClass *clorobj, int noDuplicates, int atEnd) { NsfCmdList *l, *nextPtr, *new; assert(cmd); assert(cList); if (unlikely(atEnd)) { l = *cList; nextPtr = NULL; } else { l = NULL; nextPtr = *cList; } /* * check for duplicates, if necessary */ if (unlikely(noDuplicates)) { NsfCmdList *h = l, **end = NULL; while (h) { if (h->cmdPtr == cmd) { return h; } end = &(h->nextPtr); h = h->nextPtr; } if (end) { /* no duplicates, no need to search below, we are at the end of the list */ cList = end; l = NULL; } } /* * ok, we have no duplicates -> append "new" * to the end of the list */ new = NEW(NsfCmdList); new->cmdPtr = cmd; NsfCommandPreserve(new->cmdPtr); new->clientData = NULL; new->clorobj = clorobj; new->nextPtr = nextPtr; if (unlikely(l != NULL)) { /* * append new element at the end */ while (l->nextPtr) { l = l->nextPtr; } l->nextPtr = new; } else { /* * prepend new element */ *cList = new; } return new; } /* *---------------------------------------------------------------------- * CmdListAddSorted -- * * Add an entry to a cmdlist without duplicates. The order of the entries * is not supposed to be relevant. This function maintains a sorted list to * reduce cost to n/2. Can be improved be using better data structures of * needed. * * Results: * The newly inserted command list item or a found item * * Side effects: * Added List entry. * *---------------------------------------------------------------------- */ static NsfCmdList *CmdListAddSorted(NsfCmdList **cList, Tcl_Command c, NsfClass *clorobj) nonnull(1) nonnull(2) returns_nonnull; static NsfCmdList * CmdListAddSorted(NsfCmdList **cList, Tcl_Command cmd, NsfClass *clorobj) { NsfCmdList *prev, *new, *h; assert(cmd); assert(cList); for (h = *cList, prev = NULL; h; prev = h, h = h->nextPtr) { if (h->cmdPtr == cmd) { return h; } else if (h->cmdPtr > cmd) { break; } } new = NEW(NsfCmdList); new->cmdPtr = cmd; NsfCommandPreserve(new->cmdPtr); new->clientData = NULL; new->clorobj = clorobj; new->nextPtr = h; if (prev) { prev->nextPtr = new; } else { *cList = new; } return new; } static void CmdListReplaceCmd(NsfCmdList *replace, Tcl_Command cmd, NsfClass *clorobj) nonnull(1) nonnull(3); static void CmdListReplaceCmd(NsfCmdList *replace, Tcl_Command cmd, NsfClass *clorobj) { Tcl_Command del = replace->cmdPtr; assert(replace); assert(clorobj); replace->cmdPtr = cmd; replace->clorobj = clorobj; NsfCommandPreserve(cmd); NsfCommandRelease(del); } #if NSF_DEBUGGING /** for debug purposes only */ static void CmdListPrint(Tcl_Interp *interp, CONST char *title, NsfCmdList *cmdList) nonnull(1) nonnull(3); static void CmdListPrint(Tcl_Interp *interp, CONST char *title, NsfCmdList *cmdList) { assert(interp); assert(cmdList); if (title) { fprintf(stderr, "%s %p:\n", title, cmdList); } while (cmdList) { fprintf(stderr, " CL=%p, cmdPtr=%p %s, clorobj %p, clientData=%p\n", cmdList, cmdList->cmdPtr, interp ? Tcl_GetCommandName(interp, cmdList->cmdPtr) : "", cmdList->clorobj, cmdList->clientData); cmdList = cmdList->nextPtr; } } #endif /* * physically delete an entry 'del' */ static void CmdListDeleteCmdListEntry(NsfCmdList *del, NsfFreeCmdListClientData *freeFct) nonnull(1); static void CmdListDeleteCmdListEntry(NsfCmdList *del, NsfFreeCmdListClientData *freeFct) { assert(del); if (unlikely(freeFct != NULL)) { (*freeFct)(del); } NsfCommandRelease(del->cmdPtr); FREE(NsfCmdList, del); } /* * remove a command 'delCL' from a command list, but do not * free it ... returns the removed NsfCmdList* */ static NsfCmdList *CmdListRemoveFromList(NsfCmdList **cmdList, NsfCmdList *delCL) nonnull(1) nonnull(2); static NsfCmdList * CmdListRemoveFromList(NsfCmdList **cmdList, NsfCmdList *delCL) { register NsfCmdList *c = *cmdList, *del = NULL; assert(cmdList); assert(delCL); if (c == NULL) { return NULL; } if (c == delCL) { *cmdList = c->nextPtr; del = c; } else { while (c->nextPtr && c->nextPtr != delCL) { c = c->nextPtr; } if (c->nextPtr == delCL) { del = delCL; c->nextPtr = delCL->nextPtr; } } return del; } /* *---------------------------------------------------------------------- * CmdListRemoveDeleted -- * * Remove all command pointers from a command list which are marked * "deleted". The condition for deletion is the presence of the flag * CMD_IS_DELETED, with the flag bit being set by * Tcl_DeleteCommandFromToken(). * * Results: * The cmd list filtered for non-deleted commands * * Side effects: * None * *---------------------------------------------------------------------- */ static void CmdListRemoveDeleted(NsfCmdList **cmdList, NsfFreeCmdListClientData *freeFct) nonnull(1) nonnull(2); static void CmdListRemoveDeleted(NsfCmdList **cmdList, NsfFreeCmdListClientData *freeFct) { NsfCmdList *f = *cmdList, *del; assert(cmdList); assert(freeFct); while (f) { /* * HIDDEN OBJECTS: For supporting hidden mixins, we cannot rely on the * cmdEpoch as indicator of the deletion status of a cmd because the epoch * counters of hidden and re-exposed commands are bumped. Despite of this, * their object structures remain valid. We resort to the use of the * per-cmd flag CMD_IS_DELETED, set upon processing a command in * Tcl_DeleteCommandFromToken(). */ if (Tcl_Command_flags(f->cmdPtr) & CMD_IS_DELETED /* Tcl_Command_cmdEpoch(f->cmdPtr) */) { del = f; f = f->nextPtr; del = CmdListRemoveFromList(cmdList, del); CmdListDeleteCmdListEntry(del, freeFct); } else f = f->nextPtr; } } /* * delete all cmds with given context class object */ static void CmdListRemoveContextClassFromList(NsfCmdList **cmdList, NsfClass *clorobj, NsfFreeCmdListClientData *freeFct) nonnull(1) nonnull(2) nonnull(3); static void CmdListRemoveContextClassFromList(NsfCmdList **cmdList, NsfClass *clorobj, NsfFreeCmdListClientData *freeFct) { NsfCmdList *c, *del = NULL; assert(cmdList); assert(clorobj); assert(freeFct); /* CmdListRemoveDeleted(cmdList, freeFct); */ c = *cmdList; while (c && c->clorobj == clorobj) { del = c; *cmdList = c->nextPtr; CmdListDeleteCmdListEntry(del, freeFct); c = *cmdList; } while (c) { if (c->clorobj == clorobj) { del = c; c = *cmdList; while (c->nextPtr && c->nextPtr != del) { c = c->nextPtr; } if (c->nextPtr == del) { c->nextPtr = del->nextPtr; } CmdListDeleteCmdListEntry(del, freeFct); } c = c->nextPtr; } } /* * free the memory of a whole 'cmdList' */ static void CmdListFree(NsfCmdList **cmdList, NsfFreeCmdListClientData *freeFct) { assert(cmdList); while (*cmdList) { NsfCmdList *del = *cmdList; *cmdList = (*cmdList)->nextPtr; CmdListDeleteCmdListEntry(del, freeFct); } } /* * simple list search proc to search a list of cmds * for a command ptr */ static NsfCmdList * CmdListFindCmdInList(Tcl_Command cmd, NsfCmdList *l) nonnull(2) nonnull(1); static NsfCmdList * CmdListFindCmdInList(Tcl_Command cmd, NsfCmdList *l) { register NsfCmdList *h; assert(cmd); assert(l); for (h = l; h; h = h->nextPtr) { if (h->cmdPtr == cmd) return h; } return NULL; } /* * simple list search proc to search a list of cmds * for a simple Name */ static NsfCmdList * CmdListFindNameInList(Tcl_Interp *interp, CONST char *name, NsfCmdList *l) nonnull(1) nonnull(2) nonnull(3); static NsfCmdList * CmdListFindNameInList(Tcl_Interp *interp, CONST char *name, NsfCmdList *l) { assert(interp); assert(name); assert(l); for (; l; l = l->nextPtr) { CONST char *cmdName = Tcl_GetCommandName(interp, l->cmdPtr); if (cmdName[0] == name[0] && !strcmp(cmdName, name)) return l; } return NULL; } /* *---------------------------------------------------------------------- * CheckConditionInScope -- * * Check a given condition in the current call-frame's scope. It is * the responsibility of the caller to push the intended call-frame. * * Results: * Tcl result code. * * Side effects: * None * *---------------------------------------------------------------------- */ static int CheckConditionInScope(Tcl_Interp *interp, Tcl_Obj *condition) nonnull(1) nonnull(2); static int CheckConditionInScope(Tcl_Interp *interp, Tcl_Obj *condition) { int result, success; Tcl_Obj *ov[2] = {NULL, condition}; assert(interp); assert(condition); INCR_REF_COUNT(condition); result = Nsf_ExprObjCmd(NULL, interp, 2, ov); DECR_REF_COUNT(condition); if (likely(result == TCL_OK)) { result = Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp), &success); if (result == TCL_OK && success == 0) { result = NSF_CHECK_FAILED; } } return result; } /* * Generic Obj-List handling functions. */ /* *---------------------------------------------------------------------- * TclObjListFreeList -- * * Free the elements of the obj list. * * Results: * None. * * Side effects: * free memory. * *---------------------------------------------------------------------- */ static void TclObjListFreeList(NsfTclObjList *list) nonnull(1); static void TclObjListFreeList(NsfTclObjList *list) { assert(list); while (list) { NsfTclObjList *del = list; list = list->nextPtr; DECR_REF_COUNT2("listContent", del->content); if (del->payload) {DECR_REF_COUNT2("listPayload", del->payload);} FREE(NsfTclObjList, del); } } /* *---------------------------------------------------------------------- * TclObjListNewElement -- * * Add a new element to the obj list with an optional value (stored in * payload). * * Results: * None. * * Side effects: * allocate memory. * *---------------------------------------------------------------------- */ static Tcl_Obj * TclObjListNewElement(NsfTclObjList **list, Tcl_Obj *obj, Tcl_Obj *value) nonnull(1) nonnull(2) returns_nonnull; static Tcl_Obj * TclObjListNewElement(NsfTclObjList **list, Tcl_Obj *obj, Tcl_Obj *value) { NsfTclObjList *elt = NEW(NsfTclObjList); assert(list); assert(obj); INCR_REF_COUNT2("listContent", obj); elt->content = obj; elt->payload = value; if (value) { INCR_REF_COUNT2("listPayload", value); } elt->nextPtr = *list; *list = elt; return obj; } /* *---------------------------------------------------------------------- * TclObjListAdd -- * * Add a NsfTclObjList element to the obj list indexed by a key into a * sorted list of elements. Duplicates are appended to the payload * elements. * * Results: * None. * * Side effects: * Add element to the obj-list. * *---------------------------------------------------------------------- */ static void TclObjListAdd(Tcl_Interp *interp, NsfTclObjList **list, Tcl_Obj *key, Tcl_Obj *value) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static void TclObjListAdd(Tcl_Interp *interp, NsfTclObjList **list, Tcl_Obj *key, Tcl_Obj *value) { NsfTclObjList *elt, **prevPtr; CONST char *keyString = ObjStr(key); assert(interp); assert(list); assert(key); assert(value); for (elt = *list, prevPtr = list; elt; prevPtr = &elt->nextPtr, elt = elt->nextPtr) { CONST char *eltString = ObjStr(elt->content); if (key == elt->content || strcmp(keyString, eltString) == 0) { /* * Found the element, append to it */ /* fprintf(stderr, "TclObjListAdd: insert %s/%s equal, append to %s\n", keyString, ObjStr(value), ObjStr(elt->payload));*/ Tcl_ListObjAppendElement(interp, elt->payload, value); return; } if (strcmp(keyString, eltString) < 0) { /* * Element not found, insert new before as a new entry. */ /*fprintf(stderr, "TclObjListAdd: insert %s/%s before %s isshared %d\n", keyString, ObjStr(value), eltString, Tcl_IsShared(key));*/ TclObjListNewElement(prevPtr, key, Tcl_IsShared(value) ? Tcl_DuplicateObj(value) : value); return; } } /* * Element not found, insert new as last entry. */ /* fprintf(stderr, "TclObjListAdd: insert last %s value %s\n", keyString, ObjStr(value)); */ TclObjListNewElement(prevPtr, key, Tcl_NewListObj(1, &value)); return; } #if defined(NSF_WITH_ASSERTIONS) /********************************************************************* * Assertions **********************************************************************/ static NsfTclObjList * AssertionNewList(Tcl_Interp *interp, Tcl_Obj *aObj) nonnull(1); static NsfTclObjList * AssertionNewList(Tcl_Interp *interp, Tcl_Obj *aObj) { Tcl_Obj **ov; int oc; NsfTclObjList *last = NULL; assert(interp); if (aObj && Tcl_ListObjGetElements(interp, aObj, &oc, &ov) == TCL_OK) { if (oc > 0) { int i; for (i = oc - 1; i >= 0; i--) { TclObjListNewElement(&last, ov[i], NULL); } } } return last; } static Tcl_Obj * AssertionList(Tcl_Interp *interp, NsfTclObjList *alist) nonnull(1); static Tcl_Obj * AssertionList(Tcl_Interp *interp, NsfTclObjList *alist) { Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); assert(interp); for (; alist; alist = alist->nextPtr) { Tcl_ListObjAppendElement(interp, listObj, alist->content); } return listObj; } static int AssertionListCheckOption(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static int AssertionListCheckOption(Tcl_Interp *interp, NsfObject *object) { NsfObjectOpt *opt = object->opt; Tcl_Obj *resultObj; assert(interp); assert(object); if (!opt) return TCL_OK; resultObj = Tcl_GetObjResult(interp); if (opt->checkoptions & CHECK_OBJINVAR) Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("object-invar", -1)); if (opt->checkoptions & CHECK_CLINVAR) Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("class-invar", -1)); if (opt->checkoptions & CHECK_PRE) Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("pre", -1)); if (opt->checkoptions & CHECK_POST) Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("post", -1)); return TCL_OK; } static NsfProcAssertion *AssertionFindProcs(NsfAssertionStore *aStore, CONST char *name) nonnull(1) nonnull(2); static NsfProcAssertion * AssertionFindProcs(NsfAssertionStore *aStore, CONST char *name) { Tcl_HashEntry *hPtr; assert(aStore); assert(name); hPtr = Tcl_CreateHashEntry(&aStore->procs, name, NULL); if (hPtr == NULL) return NULL; return (NsfProcAssertion *) Tcl_GetHashValue(hPtr); } static void AssertionRemoveProc(NsfAssertionStore *aStore, CONST char *name) nonnull(1) nonnull(2); static void AssertionRemoveProc(NsfAssertionStore *aStore, CONST char *name) { Tcl_HashEntry *hPtr; assert(aStore); assert(name); hPtr = Tcl_CreateHashEntry(&aStore->procs, name, NULL); if (hPtr) { NsfProcAssertion *procAss = (NsfProcAssertion *) Tcl_GetHashValue(hPtr); if (procAss->pre) TclObjListFreeList(procAss->pre); if (procAss->post) TclObjListFreeList(procAss->post); FREE(NsfProcAssertion, procAss); Tcl_DeleteHashEntry(hPtr); } } static void AssertionAddProc(Tcl_Interp *interp, CONST char *name, NsfAssertionStore *aStore, Tcl_Obj *pre, Tcl_Obj *post) nonnull(1) nonnull(2) nonnull(3); static void AssertionAddProc(Tcl_Interp *interp, CONST char *name, NsfAssertionStore *aStore, Tcl_Obj *pre, Tcl_Obj *post) { int new = 0; Tcl_HashEntry *hPtr = NULL; NsfProcAssertion *procs = NEW(NsfProcAssertion); assert(interp); assert(name); assert(aStore); assert(interp); AssertionRemoveProc(aStore, name); procs->pre = AssertionNewList(interp, pre); procs->post = AssertionNewList(interp, post); assert(aStore); hPtr = Tcl_CreateHashEntry(&aStore->procs, name, &new); if (new) { Tcl_SetHashValue(hPtr, procs); } } static NsfAssertionStore * AssertionCreateStore() { NsfAssertionStore *aStore = NEW(NsfAssertionStore); aStore->invariants = NULL; Tcl_InitHashTable(&aStore->procs, TCL_STRING_KEYS); MEM_COUNT_ALLOC("Tcl_InitHashTable", &aStore->procs); return aStore; } static void AssertionRemoveStore(NsfAssertionStore *aStore) nonnull(1); static void AssertionRemoveStore(NsfAssertionStore *aStore) { Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; assert(aStore); for (hPtr = Tcl_FirstHashEntry(&aStore->procs, &hSrch); hPtr; hPtr = Tcl_FirstHashEntry(&aStore->procs, &hSrch)) { /* * AssertionRemoveProc calls Tcl_DeleteHashEntry(hPtr), thus * we get the FirstHashEntry afterwards again to proceed */ AssertionRemoveProc(aStore, Tcl_GetHashKey(&aStore->procs, hPtr)); } Tcl_DeleteHashTable(&aStore->procs); MEM_COUNT_FREE("Tcl_InitHashTable", &aStore->procs); if (aStore->invariants) TclObjListFreeList(aStore->invariants); FREE(NsfAssertionStore, aStore); } static int AssertionCheckList(Tcl_Interp *interp, NsfObject *object, NsfTclObjList *alist, CONST char *methodName) nonnull(1) nonnull(2) nonnull(4); static int AssertionCheckList(Tcl_Interp *interp, NsfObject *object, NsfTclObjList *alist, CONST char *methodName) { NsfTclObjList *checkFailed = NULL; Tcl_Obj *savedResultObj = Tcl_GetObjResult(interp); int savedCheckoptions, acResult = TCL_OK; assert(interp); assert(object); assert(methodName); /* * no obj->opt -> checkoption == CHECK_NONE */ if (object->opt == NULL) { return TCL_OK; } /* we do not check assertion modifying methods, otherwise we cannot react in catch on a runtime assertion check failure */ #if 1 /* TODO: the following check operations is XOTcl1 legacy and is not generic. it should be replaced by another method-property. Most of the is*String() definition are then obsolete and should be deleted from nsfInt.h as well. */ if (isCheckString(methodName)) { return TCL_OK; } #endif INCR_REF_COUNT(savedResultObj); Tcl_ResetResult(interp); while (alist) { /* Eval instead of IfObjCmd => the substitutions in the conditions will be done by Tcl */ CONST char *assStr = ObjStr(alist->content), *c = assStr; int comment = 0; for (; c && *c != '\0'; c++) { if (*c == '#') { comment = 1; break; } } if (comment == 0) { CallFrame frame, *framePtr = &frame; Nsf_PushFrameObj(interp, object, framePtr); /* don't check assertion during assertion check */ savedCheckoptions = object->opt->checkoptions; object->opt->checkoptions = CHECK_NONE; /* fprintf(stderr, "Checking Assertion %s ", assStr); */ /* * now check the assertion in the pushed call-frame's scope */ acResult = CheckConditionInScope(interp, alist->content); if (acResult != TCL_OK) { checkFailed = alist; } object->opt->checkoptions = savedCheckoptions; /* fprintf(stderr, "...%s\n", checkFailed ? "failed" : "ok"); */ Nsf_PopFrameObj(interp, framePtr); } if (checkFailed) { break; } alist = alist->nextPtr; } if (checkFailed) { DECR_REF_COUNT(savedResultObj); if (acResult == TCL_ERROR) { Tcl_Obj *sr = Tcl_GetObjResult(interp); INCR_REF_COUNT(sr); NsfPrintError(interp, "error in Assertion: {%s} in proc '%s'\n%s", ObjStr(checkFailed->content), methodName, ObjStr(sr)); DECR_REF_COUNT(sr); return TCL_ERROR; } return NsfPrintError(interp, "assertion failed check: {%s} in proc '%s'", ObjStr(checkFailed->content), methodName); } Tcl_SetObjResult(interp, savedResultObj); DECR_REF_COUNT(savedResultObj); return TCL_OK; } static int AssertionCheckInvars(Tcl_Interp *interp, NsfObject *object, CONST char *methodName, CheckOptions checkoptions) nonnull(1) nonnull(2) nonnull(3); static int AssertionCheckInvars(Tcl_Interp *interp, NsfObject *object, CONST char *methodName, CheckOptions checkoptions) { int result = TCL_OK; assert(interp); assert(object); assert(methodName); if (checkoptions & CHECK_OBJINVAR && object->opt->assertions) { result = AssertionCheckList(interp, object, object->opt->assertions->invariants, methodName); } if (result != TCL_ERROR && checkoptions & CHECK_CLINVAR) { NsfClasses *clPtr; clPtr = PrecedenceOrder(object->cl); while (clPtr && result != TCL_ERROR) { NsfAssertionStore *aStore = (clPtr->cl->opt) ? clPtr->cl->opt->assertions : NULL; if (aStore) { result = AssertionCheckList(interp, object, aStore->invariants, methodName); } clPtr = clPtr->nextPtr; } } return result; } static int AssertionCheck(Tcl_Interp *interp, NsfObject *object, NsfClass *cl, CONST char *method, int checkOption) nonnull(1) nonnull(2) nonnull(4); static int AssertionCheck(Tcl_Interp *interp, NsfObject *object, NsfClass *cl, CONST char *method, int checkOption) { int result = TCL_OK; NsfAssertionStore *aStore; assert(interp); assert(object); assert(method); assert(object->opt); if (cl) { aStore = cl->opt ? cl->opt->assertions : NULL; } else { aStore = object->opt ? object->opt->assertions : NULL; } if (aStore && (checkOption & object->opt->checkoptions)) { NsfProcAssertion *procs = AssertionFindProcs(aStore, method); if (procs) { switch (checkOption) { case CHECK_PRE: result = AssertionCheckList(interp, object, procs->pre, method); break; case CHECK_POST: result = AssertionCheckList(interp, object, procs->post, method); break; } } if (result != TCL_ERROR) { result = AssertionCheckInvars(interp, object, method, object->opt->checkoptions); } } return result; } static int AssertionSetCheckOptions(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *arg) nonnull(1) nonnull(2) nonnull(3); static int AssertionSetCheckOptions(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *arg) { NsfObjectOpt *opt = NsfRequireObjectOpt(object); int ocArgs; Tcl_Obj **ovArgs; assert(interp); assert(object); assert(arg); opt->checkoptions = CHECK_NONE; if (Tcl_ListObjGetElements(interp, arg, &ocArgs, &ovArgs) == TCL_OK && ocArgs > 0) { int i; for (i = 0; i < ocArgs; i++) { CONST char *option = ObjStr(ovArgs[i]); if (option) { switch (*option) { case 'c': if (strcmp(option, "class-invar") == 0) { opt->checkoptions |= CHECK_CLINVAR; } break; case 'o': if (strcmp(option, "object-invar") == 0) { opt->checkoptions |= CHECK_OBJINVAR; } break; case 'p': if (strcmp(option, "pre") == 0) { opt->checkoptions |= CHECK_PRE; } else if (strcmp(option, "post") == 0) { opt->checkoptions |= CHECK_POST; } break; case 'a': if (strcmp(option, "all") == 0) { opt->checkoptions |= CHECK_ALL; } break; } } } } if (opt->checkoptions == CHECK_NONE && ocArgs > 0) { return NsfPrintError(interp, "unknown check option in command '%s' check %s, ", "valid: all pre post object-invar class-invar", ObjectName(object), ObjStr(arg)); } return TCL_OK; } static void AssertionSetInvariants(Tcl_Interp *interp, NsfAssertionStore **assertions, Tcl_Obj *arg) nonnull(1) nonnull(2) nonnull(3); static void AssertionSetInvariants(Tcl_Interp *interp, NsfAssertionStore **assertions, Tcl_Obj *arg) { assert(interp); assert(assertions); assert(arg); if (*assertions) { TclObjListFreeList((*assertions)->invariants); } else { *assertions = AssertionCreateStore(); } (*assertions)->invariants = AssertionNewList(interp, arg); } #endif /* NSF_WITH_ASSERTIONS */ /*********************************************************************** * Mixin support ***********************************************************************/ /* * push a mixin stack information on this object */ static int MixinStackPush(NsfObject *object) nonnull(1); static int MixinStackPush(NsfObject *object) { register NsfMixinStack *h = NEW(NsfMixinStack); assert(object); h->currentCmdPtr = NULL; h->nextPtr = object->mixinStack; object->mixinStack = h; /*fprintf(stderr, "MixinStackPush %p %s\n", object, ObjectName(object));*/ return 1; } /* * Pop a mixin stack information on this object. */ static void MixinStackPop(NsfObject *object) nonnull(1); static void MixinStackPop(NsfObject *object) { register NsfMixinStack *h = object->mixinStack; assert(object); /*fprintf(stderr, "MixinStackPop %p %s\n", object, ObjectName(object));*/ object->mixinStack = h->nextPtr; FREE(NsfMixinStack, h); } /* * Appends NsfClasses (containing the mixin-classes and their * super-classes) to 'mixinClasses' list from a given mixinList. */ static void MixinComputeOrderFullList(Tcl_Interp *interp, NsfCmdList **mixinList, NsfClasses **mixinClasses, NsfClasses **checkList, int level) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static void MixinComputeOrderFullList(Tcl_Interp *interp, NsfCmdList **mixinList, NsfClasses **mixinClasses, NsfClasses **checkList, int level) { NsfCmdList *m; NsfClasses *pl, **clPtr = mixinClasses; assert(interp); assert(mixinList); assert(mixinClasses); assert(checkList); CmdListRemoveDeleted(mixinList, GuardDel); for (m = *mixinList; m; m = m->nextPtr) { NsfClass *mCl = NsfGetClassFromCmdPtr(m->cmdPtr); if (mCl) { for (pl = PrecedenceOrder(mCl); pl; pl = pl->nextPtr) { if (IsRootClass(pl->cl) == 0) { NsfClassOpt *opt = pl->cl->opt; /* fprintf(stderr, "find %p %s in checklist 1 %p\n", pl->cl, ClassName(pl->cl), *checkList);*/ if (*checkList != NULL && NsfClassListFind(*checkList, pl->cl)) { /*fprintf(stderr, "+++ never add %s\n", ClassName(pl->cl));*/ } else { if (opt && opt->classMixins) { /* * Compute transitively the (class) mixin-classes of this * added class. */ NsfClassListAdd(checkList, pl->cl, NULL); /*fprintf(stderr, "+++ transitive %s\n", ClassName(pl->cl));*/ MixinComputeOrderFullList(interp, &opt->classMixins, mixinClasses, checkList, level+1); } /*fprintf(stderr, "+++ add to mixinClasses %p path: %s clPtr %p\n", mixinClasses, ClassName(pl->cl), clPtr);*/ clPtr = NsfClassListAddNoDup(clPtr, pl->cl, m->clientData, NULL); } } } } } if (level == 0 && *checkList) { NsfClassListFree(*checkList); *checkList = NULL; } } /* *---------------------------------------------------------------------- * MixinResetOrder -- * * Free the mixin order of the provided object if it exists. * * Results: * void * * Side effects: * Frees potentially the mixinOrder list. * *---------------------------------------------------------------------- */ static void MixinResetOrder(NsfObject *object) nonnull(1); static void MixinResetOrder(NsfObject *object) { assert(object); CmdListFree(&object->mixinOrder, NULL /*GuardDel*/); object->mixinOrder = NULL; } /* *---------------------------------------------------------------------- * NsfClassListAddPerClassMixins -- * * Append the class mixins to the provided list. CheckList is used to * eliminate potential duplicates. * * Results: * void * * Side effects: * Appends potentially elements to classList and checkList * *---------------------------------------------------------------------- */ static void NsfClassListAddPerClassMixins(Tcl_Interp *interp, NsfClass *cl, NsfClasses **classList, NsfClasses **checkList) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static void NsfClassListAddPerClassMixins(Tcl_Interp *interp, NsfClass *cl, NsfClasses **classList, NsfClasses **checkList) { NsfClasses *pl; assert(interp); assert(cl); assert(classList); assert(checkList); for (pl = PrecedenceOrder(cl); pl; pl = pl->nextPtr) { NsfClassOpt *clopt = pl->cl->opt; if (clopt && clopt->classMixins) { MixinComputeOrderFullList(interp, &clopt->classMixins, classList, checkList, 1); } } } /* *---------------------------------------------------------------------- * MixinComputeOrder -- * * Compute a duplicate-free linearized order of per-object and per-class * mixins and the class inheritance. The precedence rule is that the last * occurrence makes it into the final list. * * Results: * void * * Side effects: * object->mixinOrder is updated. * *---------------------------------------------------------------------- */ static void MixinComputeOrder(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static void MixinComputeOrder(Tcl_Interp *interp, NsfObject *object) { NsfClasses *fullList, *checkList = NULL, *mixinClasses = NULL, *clPtr; assert(interp); assert(object); if (object->mixinOrder) { MixinResetOrder(object); } /* Append per-obj mixins */ if (object->opt) { NsfCmdList *m; MixinComputeOrderFullList(interp, &object->opt->objMixins, &mixinClasses, &checkList, 1); /* * Add per-object mixins to checkList to avoid that theses classes in the * class mixins. * * TODO: we could add this already in MixinComputeOrderFullList() if we * provide an additional flag. */ for (m = object->opt->objMixins; m; m = m->nextPtr) { NsfClass *mCl = NsfGetClassFromCmdPtr(m->cmdPtr); if (mCl) { NsfClassListAddNoDup(&checkList, mCl, NULL, NULL); } } } /*fprintf(stderr, "%s ", ObjectName(object)); NsfClassListPrint("MixinComputeOrder poms", mixinClasses); NsfClassListPrint("MixinComputeOrder poms checkList", checkList);*/ /* append per-class mixins */ NsfClassListAddPerClassMixins(interp, object->cl, &mixinClasses, &checkList); /*fprintf(stderr, "%s ", ObjectName(object)); NsfClassListPrint("MixinComputeOrder poms+pcms", mixinClasses); CmdListPrint(interp, "mixinOrder", object->mixinOrder);*/ if (checkList) { NsfClassListFree(checkList); } fullList = mixinClasses; /* * Don't add duplicates or classes of the precedence order to the resulting * list. */ for (clPtr = mixinClasses; clPtr; clPtr = clPtr->nextPtr) { NsfClass *cl = clPtr->cl; NsfClasses *checker; /*fprintf(stderr, "--- Work on %s\n", ClassName(cl)); CmdListPrint(interp, "mixinOrder", object->mixinOrder);*/ checker = NsfClassListFind(clPtr->nextPtr, cl); /* * If checker is set, it is a duplicate and ignored. */ if (checker == NULL) { /* check object->cl hierarchy */ checker = NsfClassListFind(PrecedenceOrder(object->cl), cl); /* * If checker is set, it was found in the class hierarchy and it is * ignored. */ } if (checker == NULL) { /* add the class to the mixinOrder list */ NsfCmdList *new; /*fprintf(stderr, "--- adding to mixinOrder %s to cmdlist %p of object %s\n", ClassName(cl), object->mixinOrder, ObjectName(object));*/ new = CmdListAdd(&object->mixinOrder, cl->object.id, NULL, /*noDuplicates*/ 0, 1); /*CmdListPrint(interp, "mixinOrder", object->mixinOrder);*/ /* * We require the first matching guard of the full list in the new * client data */ checker = NsfClassListFind(fullList, cl); if (checker) { new->clientData = checker->clientData; } } } /* ... and free the memory of the full list */ if (fullList) { NsfClassListFree(fullList); } /*CmdListPrint(interp, "mixin order\n", obj->mixinOrder);*/ } /* *---------------------------------------------------------------------- * MixinAdd -- * * Add a mixinreg (mixin-class with a potential guard) provided as a * Tcl_Obj* to 'mixinList' by appending it to the provided cmdList. * * Results: * Tcl result code. * * Side effects: * Potentially allocating cmd list elements added to the mixinList * *---------------------------------------------------------------------- */ static int MixinAdd(Tcl_Interp *interp, NsfCmdList **mixinList, Tcl_Obj *nameObj, NsfClass *baseClass) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int MixinAdd(Tcl_Interp *interp, NsfCmdList **mixinList, Tcl_Obj *nameObj, NsfClass *baseClass) { NsfClass *mixinCl; Tcl_Obj *guardObj; NsfCmdList *new; assert(interp); assert(mixinList); assert(nameObj); assert(baseClass); /*fprintf(stderr, "MixinAdd gets obj %p type %p %s\n", nameObj, nameObj->typePtr, nameObj->typePtr?nameObj->typePtr->name : "NULL");*/ /* * When the provided nameObj is of type NsfMixinregObjType, the nsf specific * converter was called already; otherwise call the converter here. */ if (nameObj->typePtr != &NsfMixinregObjType) { if (Tcl_ConvertToType(interp, nameObj, &NsfMixinregObjType) != TCL_OK) { return TCL_ERROR; } } NsfMixinregGet(interp, nameObj, &mixinCl, &guardObj); assert((Tcl_Command_flags(mixinCl->object.id) & CMD_IS_DELETED) == 0); new = CmdListAdd(mixinList, mixinCl->object.id, NULL, /*noDuplicates*/ 1, 1); if (guardObj) { GuardAdd(new, guardObj); } else if (new->clientData) { GuardDel(new); } return TCL_OK; } /* *---------------------------------------------------------------------- * AppendMatchingElement -- * * Call AppendElement to the resultObj for values matching the specified * pattern. * * Results: * void * * Side effects: * Appends element to the result object * *---------------------------------------------------------------------- */ static void AppendMatchingElement(Tcl_Interp *interp, Tcl_Obj *resultObj, Tcl_Obj *nameObj, CONST char *pattern) nonnull(1) nonnull(2) nonnull(3); static void AppendMatchingElement(Tcl_Interp *interp, Tcl_Obj *resultObj, Tcl_Obj *nameObj, CONST char *pattern) { assert(interp); assert(resultObj); assert(nameObj); if (pattern == NULL || Tcl_StringMatch( ObjStr(nameObj), pattern)) { Tcl_ListObjAppendElement(interp, resultObj, nameObj); } } /* *---------------------------------------------------------------------- * AppendMatchingElementsFromCmdList -- * * Apply AppendMatchingElement() to all elements of the passed * Cmdlist * * Results: * 1 iff a matching object was provided and it was found; 0 otherwise * * Side effects: * Appends elements to the result * *---------------------------------------------------------------------- */ static int AppendMatchingElementsFromCmdList(Tcl_Interp *interp, NsfCmdList *cmdl, Tcl_Obj *resultObj, CONST char *pattern, NsfObject *matchObject) nonnull(1) nonnull(2) nonnull(3); static int AppendMatchingElementsFromCmdList(Tcl_Interp *interp, NsfCmdList *cmdl, Tcl_Obj *resultObj, CONST char *pattern, NsfObject *matchObject) { int rc = 0; assert(interp); assert(cmdl); assert(resultObj); for ( ; cmdl; cmdl = cmdl->nextPtr) { NsfObject *object = NsfGetObjectFromCmdPtr(cmdl->cmdPtr); if (object) { if (matchObject == object) { return 1; } else { AppendMatchingElement(interp, resultObj, object->cmdName, pattern); } } } return rc; } /* *---------------------------------------------------------------------- * AppendMatchingElementsFromClasses -- * * Apply AppendMatchingElement() to all elements of the passed * class list * * Results: * 1 iff a matching object was provided and it was found; 0 otherwise * * Side effects: * Appends elements to the result * *---------------------------------------------------------------------- */ static int AppendMatchingElementsFromClasses(Tcl_Interp *interp, NsfClasses *cls, CONST char *pattern, NsfObject *matchObject) nonnull(1); static int AppendMatchingElementsFromClasses(Tcl_Interp *interp, NsfClasses *cls, CONST char *pattern, NsfObject *matchObject) { int rc = 0; Tcl_Obj *resultObj = Tcl_GetObjResult(interp); assert(interp); for ( ; cls; cls = cls->nextPtr) { NsfObject *object = (NsfObject *)cls->cl; if (object) { if (matchObject && object == matchObject) { /* * We have a matchObject and it is identical to obj, * just return true and don't continue search */ return 1; } else { AppendMatchingElement(interp, resultObj, object->cmdName, pattern); } } } return rc; } /* *---------------------------------------------------------------------- * GetAllInstances -- * * Get all instances of a class recursively into an initialized * String key hash-table * * Results: * void * * Side effects: * Passed hash-table contains instances * *---------------------------------------------------------------------- */ static void GetAllInstances(Tcl_Interp *interp, NsfCmdList **instances, NsfClass *startCl) { NsfClasses *clPtr, *subClasses = TransitiveSubClasses(startCl); assert(interp); assert(instances); assert(startCl); for (clPtr = subClasses; clPtr; clPtr = clPtr->nextPtr) { Tcl_HashTable *tablePtr = &clPtr->cl->instances; Tcl_HashSearch search; Tcl_HashEntry *hPtr; for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr; hPtr = Tcl_NextHashEntry(&search)) { NsfObject *inst = (NsfObject *)Tcl_GetHashKey(tablePtr, hPtr); Command *cmdPtr; if (unlikely(inst->flags & NSF_TCL_DELETE)) { NsfLog(interp, NSF_LOG_NOTICE, "Object %s is apparently deleted", ObjectName(inst)); continue; } cmdPtr = (Command *)inst->id; assert(cmdPtr); if (unlikely(cmdPtr->nsPtr->flags & NS_DYING)) { NsfLog(interp, NSF_LOG_WARN, "Namespace of %s is apparently deleted", ObjectName(inst)); continue; } #if !defined(NDEBUG) { /* * Make sure, we can still lookup the object; the object has to be still * alive. */ NsfObject *object = GetObjectFromString(interp, ObjectName(inst)); /* * HIDDEN OBJECTS: Provide a fallback to a pointer-based lookup. This is * needed because objects can be hidden or re-exposed under a different * name which is not reported back to the object system by the [interp * hide|expose] mechanism. However, we still want to process hidden and * re-exposed objects during cleanup like ordinary, exposed ones. */ if (unlikely(object == NULL)) { object = GetHiddenObjectFromCmd(interp, inst->id); } assert(object); } #endif /*fprintf (stderr, " -- %p flags %.6x activation %d %s id %p id->flags %.6x " "nsPtr->flags %.6x (instance of %s)\n", inst, inst->flags, inst->activationCount, ObjectName(inst), inst->id, cmdPtr->flags, cmdPtr->nsPtr ? cmdPtr->nsPtr->flags : 0, ClassName(clPtr->cl));*/ CmdListAdd(instances, inst->id, (NsfClass *)inst, 0, 0); } } if (subClasses) NsfClassListFree(subClasses); } /* *---------------------------------------------------------------------- * AddToResultSet -- * * Helper function to add classes to the result set (implemented as * a hash-table), flagging test for matchObject as result * * Results: * 1 iff a matching object was provided and it was found; 0 otherwise * * Side effects: * Appends optionally element to the result object * *---------------------------------------------------------------------- */ static int AddToResultSet(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultSet, NsfObject *object, int *new, int appendResult, CONST char *pattern, NsfObject *matchObject) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); static int AddToResultSet(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultSet, NsfObject *object, int *new, int appendResult, CONST char *pattern, NsfObject *matchObject) { assert(interp); assert(destTablePtr); assert(resultSet); assert(object); assert(new); Tcl_CreateHashEntry(destTablePtr, (char *)object, new); if (*new) { if (matchObject && matchObject == object) { return 1; } if (appendResult) { AppendMatchingElement(interp, resultSet, object->cmdName, pattern); } } return 0; } /* *---------------------------------------------------------------------- * AddToResultSetWithGuards -- * * Helper function to add classes with guards to the result set * (implemented as a hash-table, full version as a Tcl list), flagging test * for matchObject as result. * * Results: * 1 iff a matching object was provided and it was found; 0 otherwise * * Side effects: * Appends optionally element to the result object * *---------------------------------------------------------------------- */ static int AddToResultSetWithGuards(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultSet, NsfClass *cl, ClientData clientData, int *new, int appendResult, CONST char *pattern, NsfObject *matchObject) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(6) nonnull(5); static int AddToResultSetWithGuards(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultSet, NsfClass *cl, ClientData clientData, int *new, int appendResult, CONST char *pattern, NsfObject *matchObject) { assert(clientData); assert(interp); assert(destTablePtr); assert(cl); assert(resultSet); assert(new); Tcl_CreateHashEntry(destTablePtr, (char *)cl, new); if (*new) { if (appendResult) { if (pattern == NULL || Tcl_StringMatch(ClassName(cl), pattern)) { Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); Tcl_Obj *g = (Tcl_Obj *) clientData; INCR_REF_COUNT(listObj); Tcl_ListObjAppendElement(interp, listObj, cl->object.cmdName); Tcl_ListObjAppendElement(interp, listObj, NsfGlobalObjs[NSF_GUARD_OPTION]); Tcl_ListObjAppendElement(interp, listObj, g); Tcl_ListObjAppendElement(interp, resultSet, listObj); DECR_REF_COUNT(listObj); } } if (matchObject && matchObject == (NsfObject *)cl) { return 1; } } return 0; } /* *---------------------------------------------------------------------- * GetAllObjectMixinsOf -- * * Computes a set of classes, into which this class was mixed in * via per object mixin. The function gets recursively all per * object mixins from an class and its subclasses/isClassMixinOf * and adds it into an initialized object ptr hash-table * (TCL_ONE_WORD_KEYS) * * Results: * Tcl return code * * Side effects: * The set of classes is returned in the provided hash-table * *---------------------------------------------------------------------- */ static int GetAllObjectMixinsOf(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultSet, NsfClass *startCl, int isMixin, int appendResult, CONST char *pattern, NsfObject *matchObject) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int GetAllObjectMixinsOf(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultSet, NsfClass *startCl, int isMixin, int appendResult, CONST char *pattern, NsfObject *matchObject) { int rc = 0, new = 0; NsfClasses *sc; assert(interp); assert(destTablePtr); assert(resultSet); assert(startCl); /*fprintf(stderr, "startCl = %s, opt %p, isMixin %d, pattern '%s', matchObject %p\n", ClassName(startCl), startCl->opt, isMixin, pattern, matchObject);*/ /* * check all subclasses of startCl for mixins */ for (sc = startCl->sub; sc; sc = sc->nextPtr) { rc = GetAllObjectMixinsOf(interp, destTablePtr, resultSet, sc->cl, isMixin, appendResult, pattern, matchObject); if (rc) {return rc;} } /*fprintf(stderr, "check subclasses of %s done\n", ClassName(startCl));*/ if (startCl->opt) { NsfCmdList *m; for (m = startCl->opt->isClassMixinOf; m; m = m->nextPtr) { NsfClass *cl; /* we should have no deleted commands in the list */ assert((Tcl_Command_flags(m->cmdPtr) & CMD_IS_DELETED) == 0); cl = NsfGetClassFromCmdPtr(m->cmdPtr); assert(cl); /*fprintf(stderr, "check %s mixinof %s\n", ClassName(cl), ClassName((startCl)));*/ rc = GetAllObjectMixinsOf(interp, destTablePtr, resultSet, cl, isMixin, appendResult, pattern, matchObject); /* fprintf(stderr, "check %s mixinof %s done\n", ClassName(cl), ClassName(startCl));*/ if (rc) {return rc;} } } /* * check, if startCl has associated per-object mixins */ if (startCl->opt) { NsfCmdList *m; for (m = startCl->opt->isObjectMixinOf; m; m = m->nextPtr) { NsfObject *object; /* we should have no deleted commands in the list */ assert((Tcl_Command_flags(m->cmdPtr) & CMD_IS_DELETED) == 0); object = NsfGetObjectFromCmdPtr(m->cmdPtr); assert(object); rc = AddToResultSet(interp, destTablePtr, resultSet, object, &new, appendResult, pattern, matchObject); if (rc == 1) {return rc;} } } return rc; } /* *---------------------------------------------------------------------- * AddClassListEntriesToMixinsOfSet -- * * Helper function of GetAllClassMixinsOf(). Iterate over the provided * class list (mixinOfs) and add every entry to the result set. If the * entry is new, GetAllClassMixinsOf() is called recursively. * * Results: * 1 in case the operation is finished. * * Side effects: * The set of classes is returned in the provided hash-table * *---------------------------------------------------------------------- */ static int AddClassListEntriesToMixinsOfSet(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultSet, NsfCmdList *mixinOfs, int appendResult, CONST char *pattern, NsfObject *matchObject) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int GetAllClassMixinsOf(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultSet, /*@notnull@*/ NsfClass *startCl, int isMixin, int appendResult, CONST char *pattern, NsfObject *matchObject) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int AddClassListEntriesToMixinsOfSet(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultSet, NsfCmdList *mixinOfs, int appendResult, CONST char *pattern, NsfObject *matchObject) { NsfCmdList *m; assert(interp); assert(destTablePtr); assert(resultSet); assert(mixinOfs); for (m = mixinOfs; m; m = m->nextPtr) { NsfClass *cl; int rc, new; /* We must not have deleted commands in the list */ assert((Tcl_Command_flags(m->cmdPtr) & CMD_IS_DELETED) == 0); cl = NsfGetClassFromCmdPtr(m->cmdPtr); assert(cl); rc = AddToResultSet(interp, destTablePtr, resultSet, &cl->object, &new, appendResult, pattern, matchObject); if (rc) {return 1;} if (new) { /*fprintf(stderr, "... new mixin -closure of %s => %s\n", ClassName(startCl), ClassName(cl));*/ rc = GetAllClassMixinsOf(interp, destTablePtr, resultSet, cl, 1, appendResult, pattern, matchObject); if (rc) {return 1;} } } return 0; } /* *---------------------------------------------------------------------- * GetAllClassMixinsOf -- * * Computes a set of classes, into which this class was mixed in * via as a class mixin. The function gets recursively all per * class mixins from an class and its subclasses and adds it * into an initialized object ptr hash-table (TCL_ONE_WORD_KEYS) * * Results: * Tcl return code * * Side effects: * The set of classes is returned in the provided hash-table * *---------------------------------------------------------------------- */ static int GetAllClassMixinsOf(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultSet, /*@notnull@*/ NsfClass *startCl, int isPCM, int appendResult, CONST char *pattern, NsfObject *matchObject) { int rc = 0, new = 0; NsfClasses *sc; assert(interp); assert(destTablePtr); assert(resultSet); assert(startCl); /*fprintf(stderr, "GetAllClassMixinsOf startCl = %p %s, opt %p, isPCM %d\n", startCl, ClassName(startCl), startCl->opt, isPCM);*/ /* * If the startCl is a per class mixin, add it to the result set */ if (isPCM) { rc = AddToResultSet(interp, destTablePtr, resultSet, &startCl->object, &new, appendResult, pattern, matchObject); if (rc == 1) {return rc;} /* * check all subclasses of startCl for mixins */ for (sc = startCl->sub; sc; sc = sc->nextPtr) { #if !defined(NDEBUG) if (sc->cl == startCl) { /* * Sanity check: it seems that we can create via * __default_superclass a class which has itself as subclass! */ fprintf(stderr, "... STRANGE %p is subclass of %p %s, sub %p\n", sc->cl, startCl, ClassName(startCl), startCl->sub); continue; } #endif assert(sc->cl != startCl); rc = GetAllClassMixinsOf(interp, destTablePtr, resultSet, sc->cl, isPCM, appendResult, pattern, matchObject); if (rc) { return rc; } } } /* * Check, if startCl has a subclass which is a per-class mixin of some other * class(es) */ { NsfClasses *subClasses = TransitiveSubClasses(startCl), *subClass; for (subClass = subClasses; subClass; subClass = subClass->nextPtr) { NsfClass *subCl = subClass->cl; /*fprintf(stderr, "... check subclass = %p %s, opt %p, isPCM %d\n", subCl, ClassName(subCl), subCl->opt, isPCM);*/ if (subCl->opt && subCl->opt->isClassMixinOf) { rc = AddClassListEntriesToMixinsOfSet(interp, destTablePtr, resultSet, subCl->opt->isClassMixinOf, appendResult, pattern, matchObject); if (rc) {goto subclassExit;} } } subclassExit: if (subClasses) NsfClassListFree(subClasses); if (rc) {return rc;} } /* * Check, if startCl is a per-class mixin of some other classes */ if (startCl->opt && startCl->opt->isClassMixinOf) { rc = AddClassListEntriesToMixinsOfSet(interp, destTablePtr, resultSet, startCl->opt->isClassMixinOf, appendResult, pattern, matchObject); } return rc; } /* *---------------------------------------------------------------------- * GetAllClassMixins -- * * Computes a set class-mixins of a given class and handles * transitive cases. The classes are added it into an initialized * object ptr hash-table (TCL_ONE_WORD_KEYS) * * Results: * Tcl return code * * Side effects: * The set of classes is returned in the provided hash-table * *---------------------------------------------------------------------- */ static int GetAllClassMixins(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultObj, NsfClass *startCl, int withGuards, CONST char *pattern, NsfObject *matchObject) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int GetAllClassMixins(Tcl_Interp *interp, Tcl_HashTable *destTablePtr, Tcl_Obj *resultObj, NsfClass *startCl, int withGuards, CONST char *pattern, NsfObject *matchObject) { int rc = 0, new = 0; NsfClass *cl; NsfClasses *sc; assert(interp); assert(destTablePtr); assert(resultObj); assert(startCl); /* * check this class for class mixins. */ if (startCl->opt) { NsfCmdList *m; for (m = startCl->opt->classMixins; m; m = m->nextPtr) { /* we should have no deleted commands in the list */ assert((Tcl_Command_flags(m->cmdPtr) & CMD_IS_DELETED) == 0); cl = NsfGetClassFromCmdPtr(m->cmdPtr); assert(cl); /* fprintf(stderr, "class mixin found: %s\n", ClassName(cl)); */ if ((withGuards) && (m->clientData)) { /* fprintf(stderr, "AddToResultSetWithGuards: %s\n", ClassName(cl)); */ rc = AddToResultSetWithGuards(interp, destTablePtr, resultObj, cl, m->clientData, &new, 1, pattern, matchObject); } else { /* fprintf(stderr, "AddToResultSet: %s\n", ClassName(cl)); */ rc = AddToResultSet(interp, destTablePtr, resultObj, &cl->object, &new, 1, pattern, matchObject); } if (rc == 1) {return rc;} if (new) { /* fprintf(stderr, "class mixin GetAllClassMixins for: %s (%s)\n", ClassName(cl), ClassName(startCl)); */ rc = GetAllClassMixins(interp, destTablePtr, resultObj, cl, withGuards, pattern, matchObject); if (rc) {return rc;} } } } /* * Check all superClasses of startCl for class mixins. */ for (sc = startCl->super; sc; sc = sc->nextPtr) { /* fprintf(stderr, "Superclass GetAllClassMixins for %s (%s)\n", ClassName(sc->cl), ClassName(startCl)); */ rc = GetAllClassMixins(interp, destTablePtr, resultObj, sc->cl, withGuards, pattern, matchObject); if (rc) {return rc;} } return rc; } /* *---------------------------------------------------------------------- * RemoveFromClassMixinsOf -- * * Remove the class (provided as a cmd) from all isClassMixinOf definitions * from the provided classes (provided as cmdlist). * * Results: * void * * Side effects: * Deletes potentially some entries in the isClassMixinOf lists. * *---------------------------------------------------------------------- */ static void RemoveFromClassMixinsOf(Tcl_Command cmd, NsfCmdList *cmdlist) nonnull(1) nonnull(2); static void RemoveFromClassMixinsOf(Tcl_Command cmd, NsfCmdList *cmdlist) { assert(cmd); assert(cmdlist); for ( ; cmdlist; cmdlist = cmdlist->nextPtr) { NsfClass *ncl = NsfGetClassFromCmdPtr(cmdlist->cmdPtr); NsfClassOpt *nclopt = ncl ? ncl->opt : NULL; if (nclopt) { NsfCmdList *del = CmdListFindCmdInList(cmd, nclopt->isClassMixinOf); if (del) { /* fprintf(stderr, "Removing class %s from isClassMixinOf of class %s\n", ClassName(cl), ObjStr(NsfGetClassFromCmdPtr(cmdlist->cmdPtr)->object.cmdName)); */ del = CmdListRemoveFromList(&nclopt->isClassMixinOf, del); CmdListDeleteCmdListEntry(del, GuardDel); } } } } /* *---------------------------------------------------------------------- * RemoveFromObjectMixinsOf -- * * Remove the class (provided as a cmd) from all isObjectMixinOf definitions * from the provided classes (provided as cmdlist). * * Results: * void * * Side effects: * Deletes potentially some entries in the isObjectMixinOf lists. * *---------------------------------------------------------------------- */ static void RemoveFromObjectMixinsOf(Tcl_Command cmd, NsfCmdList *cmdlist) nonnull(1) nonnull(2); static void RemoveFromObjectMixinsOf(Tcl_Command cmd, NsfCmdList *cmdlist) { assert(cmd); assert(cmdlist); for ( ; likely(cmdlist != NULL); cmdlist = cmdlist->nextPtr) { NsfClass *cl = NsfGetClassFromCmdPtr(cmdlist->cmdPtr); NsfClassOpt *clopt = cl ? cl->opt : NULL; if (clopt) { NsfCmdList *del = CmdListFindCmdInList(cmd, clopt->isObjectMixinOf); if (del) { /* fprintf(stderr, "Removing object %s from isObjectMixinOf of Class %s\n", ObjectName(object), ObjStr(NsfGetClassFromCmdPtr(cmdlist->cmdPtr)->object.cmdName)); */ del = CmdListRemoveFromList(&clopt->isObjectMixinOf, del); CmdListDeleteCmdListEntry(del, GuardDel); } } /* else fprintf(stderr, "CleanupDestroyObject %s: NULL pointer in mixins!\n", ObjectName(object)); */ } } /* *---------------------------------------------------------------------- * RemoveFromClassmixins -- * * Remove the class (provided as a cmd) from all class mixins lists * from the provided classes (provided as cmdlist). * * Results: * void * * Side effects: * Deletes potentially some entries in the class mixins lists. * *---------------------------------------------------------------------- */ static void RemoveFromClassmixins(Tcl_Command cmd, NsfCmdList *cmdlist) nonnull(1) nonnull(2); static void RemoveFromClassmixins(Tcl_Command cmd, NsfCmdList *cmdlist) { assert(cmd); assert(cmdlist); for ( ; likely(cmdlist != NULL); cmdlist = cmdlist->nextPtr) { NsfClass *cl = NsfGetClassFromCmdPtr(cmdlist->cmdPtr); NsfClassOpt *clopt = cl ? cl->opt : NULL; if (clopt) { NsfCmdList *del = CmdListFindCmdInList(cmd, clopt->classMixins); if (del) { /* fprintf(stderr, "Removing class %s from mixins of object %s\n", ClassName(cl), ObjStr(NsfGetObjectFromCmdPtr(cmdlist->cmdPtr)->cmdName)); */ del = CmdListRemoveFromList(&clopt->classMixins, del); CmdListDeleteCmdListEntry(del, GuardDel); if (cl->object.mixinOrder) MixinResetOrder(&cl->object); } } } } /* *---------------------------------------------------------------------- * RemoveFromObjectMixins -- * * Remove the class (provided as a cmd) from all object mixin lists * from the provided classes (provided as cmdlist). * * Results: * void * * Side effects: * Deletes potentially some entries in the object mixins lists. * *---------------------------------------------------------------------- */ static void RemoveFromObjectMixins(Tcl_Command cmd, NsfCmdList *cmdlist) nonnull(1) nonnull(2); static void RemoveFromObjectMixins(Tcl_Command cmd, NsfCmdList *cmdlist) { assert(cmd); assert(cmdlist); for ( ; likely(cmdlist != NULL); cmdlist = cmdlist->nextPtr) { NsfObject *nobj = NsfGetObjectFromCmdPtr(cmdlist->cmdPtr); NsfObjectOpt *objopt = nobj ? nobj->opt : NULL; if (objopt) { NsfCmdList *del = CmdListFindCmdInList(cmd, objopt->objMixins); if (del) { /* fprintf(stderr, "Removing class %s from mixins of object %s\n", ClassName(cl), ObjStr(NsfGetObjectFromCmdPtr(cmdlist->cmdPtr)->cmdName)); */ del = CmdListRemoveFromList(&objopt->objMixins, del); CmdListDeleteCmdListEntry(del, GuardDel); if (nobj->mixinOrder) MixinResetOrder(nobj); } } } } /* *---------------------------------------------------------------------- * ResetOrderOfObjectsUsingThisClassAsObjectMixin -- * * Reset the per-object mixin order for all objects having this class as * per-object mixin. * * Results: * void * * Side effects: * Deletes potentially the mixin list for the objects. * *---------------------------------------------------------------------- */ static void ResetOrderOfObjectsUsingThisClassAsObjectMixin(NsfClass *cl) nonnull(1); static void ResetOrderOfObjectsUsingThisClassAsObjectMixin(NsfClass *cl) { /*fprintf(stderr, "ResetOrderOfObjectsUsingThisClassAsObjectMixin %s - %p\n", ClassName(cl), cl->opt);*/ assert(cl); if (cl->opt) { NsfCmdList *ml; for (ml = cl->opt->isObjectMixinOf; ml; ml = ml->nextPtr) { NsfObject *object = NsfGetObjectFromCmdPtr(ml->cmdPtr); if (object) { if (object->mixinOrder) { MixinResetOrder(object); } object->flags &= ~NSF_MIXIN_ORDER_VALID; } } } } /* *---------------------------------------------------------------------- * MixinInvalidateObjOrders -- * * Reset mixin order for all instances of the class and the instances of * its dependent subclasses. This function is typically called, when the * the class hierarchy or the class mixins have changed and invalidate * mixin entries in all dependent instances. * * Results: * void * * Side effects: * Deletes potentially the mixin list for the objects and classes. * *---------------------------------------------------------------------- */ static void MixinInvalidateObjOrders(Tcl_Interp *interp, NsfClass *cl, NsfClasses *subClasses) nonnull(1) nonnull(2) nonnull(3); static void MixinInvalidateObjOrders(Tcl_Interp *interp, NsfClass *cl, NsfClasses *subClasses) { assert(interp); assert(cl); assert(subClasses); /* * Iterate over the subclass hierarchy. */ for (; likely(subClasses != NULL); subClasses = subClasses->nextPtr) { Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; Tcl_HashTable *instanceTablePtr; /* * Reset mixin order for all objects having this class as per object mixin */ ResetOrderOfObjectsUsingThisClassAsObjectMixin(subClasses->cl); if (subClasses->cl->parsedParamPtr) { ParsedParamFree(subClasses->cl->parsedParamPtr); subClasses->cl->parsedParamPtr = NULL; } instanceTablePtr = &subClasses->cl->instances; for (hPtr = Tcl_FirstHashEntry(instanceTablePtr, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { NsfObject *object = (NsfObject *)Tcl_GetHashKey(instanceTablePtr, hPtr); assert(object); if (!(object->flags & NSF_DURING_DELETE) && (object->flags & NSF_MIXIN_ORDER_DEFINED_AND_VALID)) { MixinResetOrder(object); object->flags &= ~NSF_MIXIN_ORDER_VALID; } } } } /* *---------------------------------------------------------------------- * MixinComputeDefined -- * * This function computes the mixin order for the provided object and * adjusts the mixin flags accordingly. The mixin order is either * * DEFINED (there are mixins on the instance), * NONE (there are no mixins for the instance), * or INVALID (a class restructuring has occurred, * thus it is not clear whether mixins are defined or not). * * If the mixin order is INVALID, MixinComputeDefined can be used to * compute the order and set the instance to DEFINED or NONE * * Results: * void * * Side effects: * Might alter the mixin order. * *---------------------------------------------------------------------- */ static void MixinComputeDefined(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); MixinComputeOrder(interp, object); object->flags |= NSF_MIXIN_ORDER_VALID; if (object->mixinOrder) { object->flags |= NSF_MIXIN_ORDER_DEFINED; } else { object->flags &= ~NSF_MIXIN_ORDER_DEFINED; } } /* *---------------------------------------------------------------------- * ComputePrecedenceList -- * * Returns the precedence list for the provided object. The precedence * list can optionally include the mixins and the root class. If pattern is * provided, this is used as well for filtering. The caller has to free the * resulting list via NsfClassListFree(); * * Results: * Precedence list in form of a class list, potentially NULL due to filtering. * * Side effects: * Allocated class list. * *---------------------------------------------------------------------- */ static NsfClasses *ComputePrecedenceList(Tcl_Interp *interp, NsfObject *object, CONST char *pattern, int withMixins, int withRootClass) nonnull(1) nonnull(2); static NsfClasses * ComputePrecedenceList(Tcl_Interp *interp, NsfObject *object, CONST char *pattern, int withMixins, int withRootClass) { NsfClasses *precedenceList = NULL, *pcl, **npl = &precedenceList; assert(interp); assert(object); if (withMixins) { if (!(object->flags & NSF_MIXIN_ORDER_VALID)) { MixinComputeDefined(interp, object); } if (object->flags & NSF_MIXIN_ORDER_DEFINED_AND_VALID) { NsfCmdList *ml; for (ml = object->mixinOrder; ml; ml = ml->nextPtr) { NsfClass *mixin = NsfGetClassFromCmdPtr(ml->cmdPtr); if (pattern && !Tcl_StringMatch(ClassName(mixin), pattern)) { continue; } npl = NsfClassListAdd(npl, mixin, NULL); } } } pcl = PrecedenceOrder(object->cl); for (; pcl; pcl = pcl->nextPtr) { if (withRootClass == 0 && IsRootClass(pcl->cl)) { continue; } if (pattern && !Tcl_StringMatch(ClassName(pcl->cl), pattern)) { continue; } npl = NsfClassListAdd(npl, pcl->cl, NULL); } return precedenceList; } /* *---------------------------------------------------------------------- * SeekCurrent -- * * Walk through the command list until the provided command is reached. * return the next entry. If the provided cmd is NULL, then return the * first entry. * * Results: * Command list pointer or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfCmdList *SeekCurrent(Tcl_Command cmd, register NsfCmdList *cmdListPtr) nonnull(2); static NsfCmdList * SeekCurrent(Tcl_Command cmd, register NsfCmdList *cmdListPtr) { assert(cmdListPtr); if (cmd) { for (; likely(cmdListPtr != NULL); cmdListPtr = cmdListPtr->nextPtr) { if (cmdListPtr->cmdPtr == cmd) { return cmdListPtr->nextPtr; } } return NULL; } return cmdListPtr; } /* *---------------------------------------------------------------------- * CanInvokeMixinMethod -- * * Check, whether the provided cmd is allowed to be dispatch in a mixin. * * Results: * Tcl result code or NSF_CHECK_FAILED in case, search should continue * * Side effects: * None. * *---------------------------------------------------------------------- */ static int CanInvokeMixinMethod(Tcl_Interp *interp, NsfObject *object, Tcl_Command cmd, NsfCmdList *cmdList) nonnull(1) nonnull(2) nonnull(4); static int CanInvokeMixinMethod(Tcl_Interp *interp, NsfObject *object, Tcl_Command cmd, NsfCmdList *cmdList) { int result = TCL_OK; int cmdFlags = Tcl_Command_flags(cmd); assert(interp); assert(object); assert(cmdList); if ((cmdFlags & NSF_CMD_CALL_PRIVATE_METHOD) || ((cmdFlags & NSF_CMD_CLASS_ONLY_METHOD) && !NsfObjectIsClass(object))) { /* * The command is not applicable for objects (i.e. might crash, * since it expects a class record); therefore skip it */ return NSF_CHECK_FAILED; } if (cmdList->clientData && !RUNTIME_STATE(interp)->guardCount) { /*fprintf(stderr, "guard call\n");*/ result = GuardCall(object, interp, (Tcl_Obj *)cmdList->clientData, NULL); } return result; } /* *---------------------------------------------------------------------- * MixinSearchProc -- * * Search for a method name in the mixin list of the provided * object. Depending on the state of the mixin stack, the search starts * at the beginning or at the last dispatched, shadowed method on * the mixin path. * * Results: * Tcl result code. * Returns as well always cmd (maybe NULL) in cmdPtr. * Returns on success as well the class and the currentCmdPointer * for continuation in next. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int MixinSearchProc(Tcl_Interp *interp, NsfObject *object, CONST char *methodName, Tcl_Obj *methodObj, NsfClass **clPtr, Tcl_Command *currentCmdPtr, Tcl_Command *cmdPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(5) nonnull(6) nonnull(7); static int MixinSearchProc(Tcl_Interp *interp, NsfObject *object, CONST char *methodName, Tcl_Obj *methodObj, NsfClass **clPtr, Tcl_Command *currentCmdPtr, Tcl_Command *cmdPtr) { Tcl_Command cmd = NULL; NsfCmdList *cmdList; NsfClass *cl = NULL; int result = TCL_OK; assert(interp); assert(object); assert(object->mixinStack); assert(methodName); assert(clPtr); assert(currentCmdPtr); assert(cmdPtr); /* ensure that the mixin order is valid */ assert(object->flags & NSF_MIXIN_ORDER_VALID); if (object->mixinOrder == NULL) { return TCL_OK; } cmdList = SeekCurrent(object->mixinStack->currentCmdPtr, object->mixinOrder); RUNTIME_STATE(interp)->currentMixinCmdPtr = cmdList ? cmdList->cmdPtr : NULL; /*fprintf(stderr,"searching for '%s' in %p\n", methodName, cmdList); CmdListPrint(interp, "MixinSearch CL = \n", cmdList);*/ if (unlikely(*clPtr && *cmdPtr)) { Tcl_Command lastCmdPtr = NULL; /*fprintf(stderr, "... new branch\n");*/ for (; cmdList; cmdList = cmdList->nextPtr) { NsfClass *cl1; /* * Ignore deleted commands */ if (Tcl_Command_flags(cmdList->cmdPtr) & CMD_IS_DELETED) { continue; } cl1 = NsfGetClassFromCmdPtr(cmdList->cmdPtr); assert(cl1); lastCmdPtr = cmdList->cmdPtr; if (cl1 == *clPtr) { /* * The wanted class was found. Check guards and permissions to * determine whether we can invoke this method. */ result = CanInvokeMixinMethod(interp, object, *cmdPtr, cmdList); if (likely(result == TCL_OK)) { cl = cl1; } else if (result == NSF_CHECK_FAILED) { result = TCL_OK; } /* * No matter, what the result is, stop the search through the mixin * classes here. */ break; } } if (cl) { assert(cmdList); /* * On success: return class and cmdList->cmdPtr; */ *currentCmdPtr = cmdList->cmdPtr; /*fprintf(stderr, "... mixinsearch success returns %p (cl %s)\n", cmd, ClassName(cl));*/ } else { /* * We did not find the absolute entry in the mixins. Set the * currentCmdPtr (on the mixin stack) to the last entry to flag, that * the mixin list should not started again on a next. */ *cmdPtr = NULL; *currentCmdPtr = lastCmdPtr; /*fprintf(stderr, "... mixinsearch success failure %p (cl %s)\n", cmd, ClassName(cl));*/ } return result; } else { for (; cmdList; cmdList = cmdList->nextPtr) { /* * Ignore deleted commands */ if (Tcl_Command_flags(cmdList->cmdPtr) & CMD_IS_DELETED) { continue; } cl = NsfGetClassFromCmdPtr(cmdList->cmdPtr); assert(cl); /* fprintf(stderr, "+++ MixinSearch %s->%s in %p cmdPtr %p clientData %p\n", ObjectName(object), methodName, cmdList, cmdList->cmdPtr, cmdList->clientData); */ cmd = FindMethod(cl->nsPtr, methodName); if (cmd == NULL) { continue; } result = CanInvokeMixinMethod(interp, object, cmd, cmdList); if (unlikely(result == TCL_ERROR)) { return result; } else if (result == NSF_CHECK_FAILED) { result = TCL_OK; cmd = NULL; continue; } /* * cmd was found and is applicable. We return class and cmdPtr. */ *clPtr = cl; *currentCmdPtr = cmdList->cmdPtr; /*fprintf(stderr, "mixinsearch returns %p (cl %s)\n", cmd, ClassName(cl));*/ break; } } *cmdPtr = cmd; return result; } /* * info option for mixins and class mixins */ static int MixinInfo(Tcl_Interp *interp, NsfCmdList *m, CONST char *pattern, int withGuards, NsfObject *matchObject) nonnull(1); static int MixinInfo(Tcl_Interp *interp, NsfCmdList *m, CONST char *pattern, int withGuards, NsfObject *matchObject) { Tcl_Obj *list = Tcl_NewListObj(0, NULL); assert(interp); /*fprintf(stderr, " mixin info m=%p, pattern %s, matchObject %p\n", m, pattern, matchObject);*/ while (m) { NsfClass *mixinClass = NsfGetClassFromCmdPtr(m->cmdPtr); /* fprintf(stderr, " mixin info m=%p, next=%p, pattern %s, matchObject %p\n", m, m->next, pattern, matchObject);*/ if (mixinClass && (pattern == NULL || (matchObject != NULL && &(mixinClass->object) == matchObject) || (matchObject == NULL && Tcl_StringMatch(ObjStr(mixinClass->object.cmdName), pattern)))) { if (withGuards && m->clientData) { Tcl_Obj *l = Tcl_NewListObj(0, NULL); Tcl_Obj *g = (Tcl_Obj *) m->clientData; Tcl_ListObjAppendElement(interp, l, mixinClass->object.cmdName); Tcl_ListObjAppendElement(interp, l, NsfGlobalObjs[NSF_GUARD_OPTION]); Tcl_ListObjAppendElement(interp, l, g); Tcl_ListObjAppendElement(interp, list, l); } else { Tcl_ListObjAppendElement(interp, list, mixinClass->object.cmdName); } if (matchObject) break; } m = m->nextPtr; } Tcl_SetObjResult(interp, list); return TCL_OK; } /* * info option for mixinofs and isClassMixinOf */ static Tcl_Command MixinSearchMethodByName(NsfCmdList *mixinList, CONST char *name, NsfClass **clPtr) nonnull(1) nonnull(2) nonnull(3); static Tcl_Command MixinSearchMethodByName(NsfCmdList *mixinList, CONST char *name, NsfClass **clPtr) { Tcl_Command cmd; assert(mixinList); assert(name); assert(clPtr); for (; likely(mixinList != NULL); mixinList = mixinList->nextPtr) { NsfClass *foundCl = NsfGetClassFromCmdPtr(mixinList->cmdPtr); if (foundCl && SearchCMethod(foundCl, name, &cmd)) { *clPtr = foundCl; return cmd; } } return NULL; } /* * Filter-Commands */ /* * The search method implements filter search order for object and * class filter: first a given name is interpreted as fully qualified * method name. If no method is found, a proc is searched with fully * name. Otherwise the simple name is searched on the heritage order: * object (only for per-object filters), class, meta-class */ static Tcl_Command FilterSearch(CONST char *name, NsfObject *startingObject, NsfClass *startingClass, NsfClass **clPtr) nonnull(1) nonnull(4); static Tcl_Command FilterSearch(CONST char *name, NsfObject *startingObject, NsfClass *startingClass, NsfClass **clPtr) { Tcl_Command cmd = NULL; assert(name); assert(clPtr); if (startingObject) { NsfObjectOpt *opt = startingObject->opt; /* * the object-specific filter can also be defined on the object's * class, its hierarchy, or the respective class mixins; thus use the * object's class as start point for the class-specific search then ... */ startingClass = startingObject->cl; /* * search for filters on object mixins */ if (opt && opt->objMixins) { if ((cmd = MixinSearchMethodByName(opt->objMixins, name, clPtr))) { return cmd; } } } /* * search for class filters on class mixins */ if (startingClass) { NsfClassOpt *opt = startingClass->opt; if (opt && opt->classMixins) { if ((cmd = MixinSearchMethodByName(opt->classMixins, name, clPtr))) { return cmd; } } } /* * search for object procs that are used as filters */ if (startingObject && startingObject->nsPtr) { /*fprintf(stderr, "search filter %s as proc \n", name);*/ if ((cmd = FindMethod(startingObject->nsPtr, name))) { *clPtr = (NsfClass *)startingObject; return cmd; } } /* * ok, no filter on obj or mixins -> search class */ if (startingClass) { *clPtr = SearchCMethod(startingClass, name, &cmd); if (*clPtr == NULL) { /* * If no filter is found yet -> search the meta-class */ *clPtr = SearchCMethod(startingClass->object.cl, name, &cmd); } } return cmd; } /* * Filter Guards */ /* check a filter guard, return 1 if ok */ static int GuardCheck(Tcl_Interp *interp, Tcl_Obj *guardObj) nonnull(1) nonnull(2); static int GuardCheck(Tcl_Interp *interp, Tcl_Obj *guardObj) { NsfRuntimeState *rst = RUNTIME_STATE(interp); int result; assert(interp); assert(guardObj); /* * if there are more than one filter guard for this filter * (i.e. they are inherited), then they are OR combined * -> if one check succeeds => return 1 */ /*fprintf(stderr, "checking guard **%s**\n", ObjStr(guardObj));*/ rst->guardCount++; result = CheckConditionInScope(interp, guardObj); rst->guardCount--; /*fprintf(stderr, "checking guard **%s** returned rc=%d\n", ObjStr(guardObj), rc);*/ if (likely(result == TCL_OK)) { /* fprintf(stderr, " +++ OK\n"); */ return TCL_OK; } else if (unlikely(result == TCL_ERROR)) { Tcl_Obj *sr = Tcl_GetObjResult(interp); INCR_REF_COUNT(sr); NsfPrintError(interp, "Guard error: '%s'\n%s", ObjStr(guardObj), ObjStr(sr)); DECR_REF_COUNT(sr); return TCL_ERROR; } /* fprintf(stderr, " +++ FAILED\n"); */ return NSF_CHECK_FAILED; } /* static void GuardPrint(Tcl_Interp *interp, ClientData clientData) { Tcl_Obj *guardObj = (TclObj *) clientData; fprintf(stderr, " +++ \n"); if (guardObj) { fprintf(stderr, " * %s \n", ObjStr(guardObj)); } fprintf(stderr, " +++ \n"); } */ static void GuardDel(NsfCmdList *guardList) { assert(guardList); /*fprintf(stderr, "GuardDel %p clientData = %p\n", guardList, guardList? guardList->clientData : NULL);*/ if (guardList->clientData) { DECR_REF_COUNT2("guardObj", (Tcl_Obj *)guardList->clientData); guardList->clientData = NULL; } } NSF_INLINE static void GuardAdd(NsfCmdList *guardList, Tcl_Obj *guardObj) { assert(guardList); assert(guardObj); GuardDel(guardList); if (strlen(ObjStr(guardObj)) != 0) { INCR_REF_COUNT2("guardObj", guardObj); guardList->clientData = guardObj; /*fprintf(stderr, "guard added to %p cmdPtr=%p, clientData= %p\n", guardList, guardList->cmdPtr, guardList->clientData); */ } } static int GuardCall(NsfObject *object, Tcl_Interp *interp, Tcl_Obj *guardObj, NsfCallStackContent *cscPtr) { int result = TCL_OK; Tcl_Obj *res = Tcl_GetObjResult(interp); /* save the result */ CallFrame frame, *framePtr = &frame; assert(object); assert(interp); assert(guardObj); INCR_REF_COUNT(res); /* * For the guard push a fake call-frame on the Tcl stack so that * e.g. a "self calledproc" and other methods in the guard behave * like in the proc. */ if (cscPtr) { Nsf_PushFrameCsc(interp, cscPtr, framePtr); } else { Nsf_PushFrameObj(interp, object, framePtr); } result = GuardCheck(interp, guardObj); if (cscPtr) { Nsf_PopFrameCsc(interp, framePtr); } else { Nsf_PopFrameObj(interp, framePtr); } if (result != TCL_ERROR) { Tcl_SetObjResult(interp, res); /* restore the result */ } DECR_REF_COUNT(res); return result; } /* *---------------------------------------------------------------------- * GuardAddFromDefinitionList -- * * Add a guard to the specified destination list (first arg) from a list of * definitions (last arg). If the provided cmd is found in the list of * definitions, it is added to the destination list if it has non-null * client data. * * Results: * Returns 0 or 1 depending on wether the cmd is part of the * definition list. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int GuardAddFromDefinitionList(NsfCmdList *dest, Tcl_Command interceptorCmd, NsfCmdList *interceptorDefList) nonnull(1) nonnull(2) nonnull(3); static int GuardAddFromDefinitionList(NsfCmdList *dest, Tcl_Command interceptorCmd, NsfCmdList *interceptorDefList) { NsfCmdList *h; assert(interceptorCmd); assert(dest); assert(interceptorDefList); h = CmdListFindCmdInList(interceptorCmd, interceptorDefList); if (h) { if (h->clientData) GuardAdd(dest, (Tcl_Obj *) h->clientData); return 1; } return 0; } /* *---------------------------------------------------------------------- * GuardAddInheritedGuards -- * * Add a inherited guards to the provided destination list. * * Results: * None. * * Side effects: * Updates potentially destination list * *---------------------------------------------------------------------- */ static void GuardAddInheritedGuards(Tcl_Interp *interp, NsfCmdList *dest, NsfObject *object, Tcl_Command filterCmd) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static void GuardAddInheritedGuards(Tcl_Interp *interp, NsfCmdList *dest, NsfObject *object, Tcl_Command filterCmd) { NsfClasses *pl; int guardAdded = 0; NsfObjectOpt *opt; assert(filterCmd); assert(interp); assert(dest); assert(object); /* search guards for class filters registered on mixins */ if (!(object->flags & NSF_MIXIN_ORDER_VALID)) { MixinComputeDefined(interp, object); } if (object->flags & NSF_MIXIN_ORDER_DEFINED_AND_VALID) { NsfCmdList *ml; for (ml = object->mixinOrder; ml != NULL && !guardAdded; ml = ml->nextPtr) { NsfClass *mixin = NsfGetClassFromCmdPtr(ml->cmdPtr); if (mixin && mixin->opt && mixin->opt->classFilters) { guardAdded = GuardAddFromDefinitionList(dest, filterCmd, mixin->opt->classFilters); } } } /* search per-object filters */ opt = object->opt; if (!guardAdded && opt && opt->objFilters) { guardAdded = GuardAddFromDefinitionList(dest, filterCmd, opt->objFilters); } if (!guardAdded) { /* search per-class filters */ for (pl = PrecedenceOrder(object->cl); !guardAdded && pl; pl = pl->nextPtr) { NsfClassOpt *clopt = pl->cl->opt; if (clopt && clopt->classFilters) { guardAdded = GuardAddFromDefinitionList(dest, filterCmd, clopt->classFilters); } } /* * if this is not a registered filter, it is an inherited filter, like: * Class create A * A method f ... * Class create B -superclass A * B method {{f {}}} * B filter f * -> get the guard from the filter that inherits it (here B->f) */ if (!guardAdded) { NsfCmdList *registeredFilter = CmdListFindNameInList(interp, (char *) Tcl_GetCommandName(interp, filterCmd), object->filterOrder); if (registeredFilter && registeredFilter->clientData) { GuardAdd(dest, (Tcl_Obj *) registeredFilter->clientData); } } } } /* *---------------------------------------------------------------------- * GuardList -- * * Set interp result to a named guard in the provided guardList. guardList * might be NULL. * * Results: * interp result * * Side effects: * None. * *---------------------------------------------------------------------- */ static int GuardList(Tcl_Interp *interp, NsfCmdList *guardList, CONST char *interceptorName) nonnull(1) nonnull(3); static int GuardList(Tcl_Interp *interp, NsfCmdList *guardList, CONST char *interceptorName) { assert(interp); assert(interceptorName); if (guardList) { /* try to find simple name first */ NsfCmdList *h = CmdListFindNameInList(interp, interceptorName, guardList); if (h == NULL) { /* maybe it is a qualified name */ Tcl_Command cmd = NSFindCommand(interp, interceptorName); if (cmd) { h = CmdListFindCmdInList(cmd, guardList); } } if (h) { Tcl_ResetResult(interp); if (h->clientData) { Tcl_Obj *g = (Tcl_Obj *) h->clientData; Tcl_SetObjResult(interp, g); } return TCL_OK; } } return NsfPrintError(interp, "info guard: can't find filter/mixin %s", interceptorName); } /* *---------------------------------------------------------------------- * FilterAddActive -- * * Add a method name to the set of methods, which were used as filters in * the current interp. * * TODO: let the set shrink, when filters are removed. * * Results: * None. * * Side effects: * Adding or updating of a hash entry * *---------------------------------------------------------------------- */ static void FilterAddActive(Tcl_Interp *interp, CONST char *methodName) nonnull(1) nonnull(2); static void FilterAddActive(Tcl_Interp *interp, CONST char *methodName) { NsfRuntimeState *rst = RUNTIME_STATE(interp); Tcl_HashEntry *hPtr; int newItem; assert(interp); assert(methodName); hPtr = Tcl_CreateHashEntry(&rst->activeFilterTablePtr, methodName, &newItem); if (newItem) { Tcl_SetHashValue(hPtr, INT2PTR(1)); } else { int count = PTR2INT(Tcl_GetHashValue(hPtr)); Tcl_SetHashValue(hPtr, INT2PTR(count+1)); } } /* *---------------------------------------------------------------------- * FilterIsActive -- * * Check, wether a method name is in the set of methods, which were used as * filters in the current interp. * * Results: * Boolean * * Side effects: * none * *---------------------------------------------------------------------- */ static int FilterIsActive(Tcl_Interp *interp, CONST char *methodName) nonnull(1) nonnull(2); static int FilterIsActive(Tcl_Interp *interp, CONST char *methodName) { NsfRuntimeState *rst = RUNTIME_STATE(interp); Tcl_HashEntry *hPtr; assert(interp); assert(methodName); hPtr = Tcl_CreateHashEntry(&rst->activeFilterTablePtr, methodName, NULL); return (hPtr != NULL); } /* *---------------------------------------------------------------------- * FiltersDefined -- * * Return the number of defined distinct names of filters. * * Results: * Positive number. * * Side effects: * none. * *---------------------------------------------------------------------- */ static int FiltersDefined(Tcl_Interp *interp) nonnull(1); static int FiltersDefined(Tcl_Interp *interp) { NsfRuntimeState *rst = RUNTIME_STATE(interp); assert(interp); return Tcl_HashSize(&rst->activeFilterTablePtr); } /* *---------------------------------------------------------------------- * FilterAdd -- * * Append a filter command to the 'filterList' of an obj/class * * Results: * Standard Tcl result * * Side effects: * Modifies interp result in error situations. * *---------------------------------------------------------------------- */ static int FilterAdd(Tcl_Interp *interp, NsfCmdList **filterList, Tcl_Obj *filterregObj, NsfObject *startingObject, NsfClass *startingClass) nonnull(1) nonnull(2) nonnull(3); static int FilterAdd(Tcl_Interp *interp, NsfCmdList **filterList, Tcl_Obj *filterregObj, NsfObject *startingObject, NsfClass *startingClass) { Tcl_Obj *filterObj = NULL; Tcl_Obj *guardObj = NULL; Tcl_Command cmd; NsfCmdList *new; NsfClass *cl; assert(interp); assert(filterList); assert(filterregObj); /* * When the provided nameObj is of type NsfFilterregObjType, the nsf specific * converter was called already; otherwise call the converter here. */ if (filterregObj->typePtr != &NsfFilterregObjType) { /*fprintf(stderr, "FilterAdd: convert %s in FilterAdd\n", ObjStr(filterregObj));*/ if (Tcl_ConvertToType(interp, filterregObj, &NsfFilterregObjType) != TCL_OK) { return TCL_ERROR; } } else { /*fprintf(stderr, "FilterAdd: %s already converted\n", ObjStr(filterregObj));*/ } NsfFilterregGet(interp, filterregObj, &filterObj, &guardObj); if (!(cmd = FilterSearch(ObjStr(filterObj), startingObject, startingClass, &cl))) { if (startingObject) { return NsfPrintError(interp, "object filter: can't find filterproc '%s' on %s ", ObjStr(filterObj), ObjectName(startingObject)); } else { return NsfPrintError(interp, "class filter: can't find filterproc '%s' on %s ", ObjStr(filterObj), ClassName(startingClass)); } } /*fprintf(stderr, " +++ adding filter %s cl %p\n", ObjStr(nameObj), cl);*/ new = CmdListAdd(filterList, cmd, cl, /*noDuplicates*/ 1, 1); FilterAddActive(interp, ObjStr(filterObj)); if (guardObj) { GuardAdd(new, guardObj); } else if (new->clientData) { GuardDel(new); } return TCL_OK; } /* *---------------------------------------------------------------------- * FilterResetOrder -- * * Reset the filter order cached in obj->filterOrder * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ static void FilterResetOrder(NsfObject *object) nonnull(1); static void FilterResetOrder(NsfObject *object) { assert(object); CmdListFree(&object->filterOrder, GuardDel); object->filterOrder = NULL; } /* *---------------------------------------------------------------------- * FilterSearchAgain -- * * Search the filter in the hierarchy again with FilterSearch, e.g. upon * changes in the class hierarchy or mixins that carry the filter command, * so that we can be sure it is still reachable. * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ static void FilterSearchAgain(Tcl_Interp *interp, NsfCmdList **filters, NsfObject *startingObject, NsfClass *startingClass) nonnull(1) nonnull(2); static void FilterSearchAgain(Tcl_Interp *interp, NsfCmdList **filters, NsfObject *startingObject, NsfClass *startingClass) { NsfCmdList *cmdList, *del; assert(interp); assert(filters); CmdListRemoveDeleted(filters, GuardDel); for (cmdList = *filters; cmdList; cmdList = cmdList->nextPtr) { NsfClass *cl = NULL; char *simpleName = (char *) Tcl_GetCommandName(interp, cmdList->cmdPtr); Tcl_Command cmd = FilterSearch(simpleName, startingObject, startingClass, &cl); if (cmd == NULL) { del = CmdListRemoveFromList(filters, cmdList); CmdListDeleteCmdListEntry(del, GuardDel); } else if (cmd != cmdList->cmdPtr) { CmdListReplaceCmd(cmdList, cmd, cl); } } /* * some entries might be NULL now, if they are not found anymore * -> delete those * CmdListRemoveNulledEntries(filters, GuardDel); */ } /* *---------------------------------------------------------------------- * FilterInvalidateObjOrders -- * * Invalidate filter entries in all dependent instances. This will be * e.g. necessary, when the class hierarchy or the class filters have * changed. * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ static void FilterInvalidateObjOrders(Tcl_Interp *interp, NsfClasses *subClasses) nonnull(1) nonnull(2); static void FilterInvalidateObjOrders(Tcl_Interp *interp, NsfClasses *subClasses) { assert(interp); assert(subClasses); for (; likely(subClasses != NULL); subClasses = subClasses->nextPtr) { Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; assert(subClasses->cl); hPtr = &subClasses->cl->instances ? Tcl_FirstHashEntry(&subClasses->cl->instances, &hSrch) : NULL; /* recalculate the commands of all class-filter registrations */ if (subClasses->cl->opt) { FilterSearchAgain(interp, &subClasses->cl->opt->classFilters, NULL, subClasses->cl); } for (; hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { NsfObject *object = (NsfObject *)Tcl_GetHashKey(&subClasses->cl->instances, hPtr); FilterResetOrder(object); object->flags &= ~NSF_FILTER_ORDER_VALID; /* recalculate the commands of all object filter registrations */ if (object->opt) { FilterSearchAgain(interp, &object->opt->objFilters, object, NULL); } } } } /* *---------------------------------------------------------------------- * FilterRemoveDependentFilterCmds -- * * * Remove all filters from all subclasses that refer to "removeClass". This * function is e.g. used to remove filters defined in superclass list from * a dependent class. * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ /* */ static void FilterRemoveDependentFilterCmds(NsfClass *removeClass, NsfClasses *subClasses) nonnull(1) nonnull(2); static void FilterRemoveDependentFilterCmds(NsfClass *removeClass, NsfClasses *subClasses) { assert(removeClass); assert(subClasses); /*fprintf(stderr, "FilterRemoveDependentFilterCmds removeClass %p %s\n", removeClass, ObjStr(removeClass->object.cmdName));*/ for (; likely(subClasses != NULL); subClasses = subClasses->nextPtr) { Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; NsfClassOpt *opt; assert(subClasses->cl); hPtr = &subClasses->cl->instances ? Tcl_FirstHashEntry(&subClasses->cl->instances, &hSrch) : NULL; opt = subClasses->cl->opt; if (opt) { CmdListRemoveContextClassFromList(&opt->classFilters, removeClass, GuardDel); } for (; hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { NsfObject *object = (NsfObject *) Tcl_GetHashKey(&subClasses->cl->instances, hPtr); if (object->opt) { CmdListRemoveContextClassFromList(&object->opt->objFilters, removeClass, GuardDel); } } } } /* *---------------------------------------------------------------------- * MethodHandleObj -- * * Builds a methodHandle from a method name. We assume, the methodName is * not fully qualified (i.e. it must not start with a colon). * * Results: * fresh Tcl_Obj * * Side effects: * none * *---------------------------------------------------------------------- */ static Tcl_Obj * MethodHandleObj(NsfObject *object, int withPer_object, CONST char *methodName) nonnull(1) nonnull(3) returns_nonnull; static Tcl_Obj * MethodHandleObj(NsfObject *object, int withPer_object, CONST char *methodName) { Tcl_Obj *resultObj; assert(object); assert(methodName); assert(*methodName != ':'); resultObj = Tcl_NewStringObj(withPer_object ? "" : "::nsf::classes", -1); Tcl_AppendObjToObj(resultObj, object->cmdName); Tcl_AppendStringsToObj(resultObj, "::", methodName, (char *) NULL); return resultObj; } /* *---------------------------------------------------------------------- * FilterInfo -- * * Set the interp results with a tcl list containing the content of the * filter list. The options withGuards and withMethodHandles can be used * for different output structures * * Results: * Standard Tcl results * * Side effects: * Updating interp result * *---------------------------------------------------------------------- */ static int FilterInfo(Tcl_Interp *interp, NsfCmdList *f, CONST char *pattern, int withGuards, int withMethodHandles) nonnull(1); static int FilterInfo(Tcl_Interp *interp, NsfCmdList *f, CONST char *pattern, int withGuards, int withMethodHandles) { Tcl_Obj *list = Tcl_NewListObj(0, NULL); assert(interp); /* * Guard lists should only have unqualified filter lists when withGuards is * activated. withMethodHandles has no effect when withGuards is specified. */ if (withGuards) { withMethodHandles = 0; } while (f) { CONST char *simpleName = Tcl_GetCommandName(interp, f->cmdPtr); if (pattern == NULL || Tcl_StringMatch(simpleName, pattern)) { if (withGuards && f->clientData) { Tcl_Obj *innerList = Tcl_NewListObj(0, NULL); Tcl_Obj *g = (Tcl_Obj *) f->clientData; Tcl_ListObjAppendElement(interp, innerList, Tcl_NewStringObj(simpleName, -1)); Tcl_ListObjAppendElement(interp, innerList, NsfGlobalObjs[NSF_GUARD_OPTION]); Tcl_ListObjAppendElement(interp, innerList, g); Tcl_ListObjAppendElement(interp, list, innerList); } else { if (withMethodHandles) { NsfClass *filterClass = f->clorobj; Tcl_ListObjAppendElement(interp, list, MethodHandleObj((NsfObject *)filterClass, !NsfObjectIsClass(&filterClass->object), simpleName)); } else { Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj(simpleName, -1)); } } } f = f->nextPtr; } Tcl_SetObjResult(interp, list); return TCL_OK; } /* *---------------------------------------------------------------------- * FilterComputeOrderFullList -- * * Compute a fresh list of filters and append it to the filterList. * * Results: * None * * Side effects: * Updating filterList * *---------------------------------------------------------------------- */ static void FilterComputeOrderFullList(Tcl_Interp *interp, NsfCmdList **filters, NsfCmdList **filterList) nonnull(1) nonnull(2) nonnull(3); static void FilterComputeOrderFullList(Tcl_Interp *interp, NsfCmdList **filters, NsfCmdList **filterList) { NsfCmdList *f ; NsfClass *fcl; NsfClasses *pl; assert(interp); assert(filters); assert(filterList); /* * Ensure that no epoched command is in the filters list. */ CmdListRemoveDeleted(filters, GuardDel); for (f = *filters; f; f = f->nextPtr) { char *simpleName = (char *) Tcl_GetCommandName(interp, f->cmdPtr); fcl = f->clorobj; CmdListAdd(filterList, f->cmdPtr, fcl, /*noDuplicates*/ 0, 1); if (fcl && !NsfObjectIsClass(&fcl->object)) { /* get the class from the object for per-object filter */ fcl = ((NsfObject *)fcl)->cl; } /* if we have a filter class -> search up the inheritance hierarchy*/ if (fcl) { pl = PrecedenceOrder(fcl); if (pl && pl->nextPtr) { /* don't search on the start class again */ pl = pl->nextPtr; /* now go up the hierarchy */ for(; pl; pl = pl->nextPtr) { Tcl_Command pi = FindMethod(pl->cl->nsPtr, simpleName); if (pi) { CmdListAdd(filterList, pi, pl->cl, /*noDuplicates*/ 0, 1); /* fprintf(stderr, " %s::%s, ", ClassName(pl->cl), simpleName); */ } } } } } /*CmdListPrint(interp, "FilterComputeOrderFullList....\n", *filterList);*/ } /* *---------------------------------------------------------------------- * FilterComputeOrder -- * * Computes a linearized order of object and class filter. Then duplicates * in the full list and with the class inheritance list of 'obj' are * eliminated. The precedence rule is that the last occurrence makes it * into the final list (object->filterOrder). * * Results: * None * * Side effects: * Updating interp result * *---------------------------------------------------------------------- */ static void FilterComputeOrder(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static void FilterComputeOrder(Tcl_Interp *interp, NsfObject *object) { NsfCmdList *filterList = NULL, *next, *checker, *newList; NsfClasses *pl; assert(interp); assert(object); if (object->filterOrder) { FilterResetOrder(object); } /* fprintf(stderr, " List: ", ObjectName(object)); */ /* * Append class filters registered for mixins. */ if (!(object->flags & NSF_MIXIN_ORDER_VALID)) { MixinComputeDefined(interp, object); } if (object->flags & NSF_MIXIN_ORDER_DEFINED_AND_VALID) { NsfCmdList *ml; for (ml = object->mixinOrder; ml; ml = ml->nextPtr) { NsfClass *mixin = NsfGetClassFromCmdPtr(ml->cmdPtr); if (mixin && mixin->opt && mixin->opt->classFilters) { FilterComputeOrderFullList(interp, &mixin->opt->classFilters, &filterList); } } } /* * Append per-obj filters. */ if (object->opt) { FilterComputeOrderFullList(interp, &object->opt->objFilters, &filterList); } /* * Append per-class filters. */ for (pl = PrecedenceOrder(object->cl); pl; pl = pl->nextPtr) { NsfClassOpt *clopt = pl->cl->opt; if (clopt && clopt->classFilters) { FilterComputeOrderFullList(interp, &clopt->classFilters, &filterList); } } /* * Use no duplicates & no classes of the precedence order * on the resulting list. */ while (filterList) { /* * Search for filterList->cmdPtr */ for (checker = next = filterList->nextPtr; checker; checker = checker->nextPtr) { if (checker->cmdPtr == filterList->cmdPtr) { break; } } if (checker == NULL) { /* * filterList->cmdPtr was found */ newList = CmdListAdd(&object->filterOrder, filterList->cmdPtr, filterList->clorobj, /*noDuplicates*/ 0, 1); GuardAddInheritedGuards(interp, newList, object, filterList->cmdPtr); /* GuardPrint(interp, newList->clientData); */ } CmdListDeleteCmdListEntry(filterList, GuardDel); filterList = next; } } /* *---------------------------------------------------------------------- * FilterComputeDefined -- * * Compute the state of the filter order. The filter order is either * * DEFINED (there are filter on the instance), * NONE (there are no filter for the instance), * or INVALID (a class restructuring has occurred, thus it is not clear * whether filters are defined or not). * * If it is INVALID FilterComputeDefined can be used to compute the order * and set the instance to DEFINE or NONE. * * Results: * None * * Side effects: * Updating object-flags * *---------------------------------------------------------------------- */ static void FilterComputeDefined(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); FilterComputeOrder(interp, object); object->flags |= NSF_FILTER_ORDER_VALID; if (object->filterOrder) { object->flags |= NSF_FILTER_ORDER_DEFINED; } else { object->flags &= ~NSF_FILTER_ORDER_DEFINED; } } /* *---------------------------------------------------------------------- * FilterStackPush -- * * Push a filter stack information on this object and initialize it with * calledProc. * * Results: * None * * Side effects: * Updating object->filterStack * *---------------------------------------------------------------------- */ static void FilterStackPush(NsfObject *object, Tcl_Obj *calledProc) nonnull(1) nonnull(2); static void FilterStackPush(NsfObject *object, Tcl_Obj *calledProc) { register NsfFilterStack *h = NEW(NsfFilterStack); assert(object); assert(calledProc); h->currentCmdPtr = NULL; h->calledProc = calledProc; INCR_REF_COUNT(h->calledProc); h->nextPtr = object->filterStack; object->filterStack = h; } /* *---------------------------------------------------------------------- * FilterStackPush -- * * Pop filter stack information from the specified object * * Results: * None * * Side effects: * Free filter stack info * *---------------------------------------------------------------------- */ static void FilterStackPop(NsfObject *object) nonnull(1); static void FilterStackPop(NsfObject *object) { register NsfFilterStack *h; assert(object); h = object->filterStack; object->filterStack = h->nextPtr; /* free stack entry */ DECR_REF_COUNT(h->calledProc); FREE(NsfFilterStack, h); } /* *---------------------------------------------------------------------- * FilterFindReg -- * * Search through the filter list on obj and class hierarchy for * registration of a cmdPtr as filter * * Results: * Returns a tcl list with the filter registration, like: * " filter , " filter , * or an empty list, if not registered * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_Obj * FilterFindReg(Tcl_Interp *interp, NsfObject *object, Tcl_Command cmd) nonnull(1) nonnull(2) nonnull(3) returns_nonnull; static Tcl_Obj * FilterFindReg(Tcl_Interp *interp, NsfObject *object, Tcl_Command cmd) { Tcl_Obj *list = Tcl_NewListObj(0, NULL); NsfClasses *pl; assert(interp); assert(object); assert(cmd); /* search per-object filters */ if (object->opt && object->opt->objFilters && CmdListFindCmdInList(cmd, object->opt->objFilters)) { Tcl_ListObjAppendElement(interp, list, object->cmdName); Tcl_ListObjAppendElement(interp, list, NsfGlobalObjs[NSF_OBJECT]); Tcl_ListObjAppendElement(interp, list, NsfGlobalObjs[NSF_FILTER]); Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj(Tcl_GetCommandName(interp, cmd), -1)); return list; } /* search per-class filters */ for (pl = PrecedenceOrder(object->cl); pl; pl = pl->nextPtr) { NsfClassOpt *opt = pl->cl->opt; if (opt && opt->classFilters) { if (CmdListFindCmdInList(cmd, opt->classFilters)) { Tcl_ListObjAppendElement(interp, list, pl->cl->object.cmdName); Tcl_ListObjAppendElement(interp, list, NsfGlobalObjs[NSF_FILTER]); Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj(Tcl_GetCommandName(interp, cmd), -1)); return list; } } } return list; } /* *---------------------------------------------------------------------- * FilterSearchProc -- * * FilterSearchProc seeks the current filter and the relevant calling * information (class and currentCmd). The function assumes to be called * with an existing filterStack. * * Results: * Tcl_Command or NULL * * Side effects: * Updates *currentCmd and **cl * *---------------------------------------------------------------------- */ /* */ static Tcl_Command FilterSearchProc(Tcl_Interp *interp, NsfObject *object, Tcl_Command *currentCmd, NsfClass **clPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static Tcl_Command FilterSearchProc(Tcl_Interp *interp, NsfObject *object, Tcl_Command *currentCmd, NsfClass **clPtr) { assert(interp); assert(object); assert(object->filterStack); assert(currentCmd); assert(clPtr); /* * Ensure that the filter order is not invalid, otherwise compute order * FilterComputeDefined(interp, object); */ assert(object->flags & NSF_FILTER_ORDER_VALID); if (object->filterOrder) { NsfCmdList *cmdList; *currentCmd = NULL; cmdList = SeekCurrent(object->filterStack->currentCmdPtr, object->filterOrder); while (cmdList) { /*fprintf(stderr, "FilterSearchProc found %s\n", Tcl_GetCommandName(interp, (Tcl_Command)cmdList->cmdPtr));*/ if (Tcl_Command_cmdEpoch(cmdList->cmdPtr)) { cmdList = cmdList->nextPtr; } else if (FilterActiveOnObj(interp, object, cmdList->cmdPtr)) { /* fprintf(stderr, "Filter <%s> -- Active on: %s\n", Tcl_GetCommandName(interp, (Tcl_Command)cmdList->cmdPtr), ObjectName(object)); */ object->filterStack->currentCmdPtr = cmdList->cmdPtr; cmdList = SeekCurrent(object->filterStack->currentCmdPtr, object->filterOrder); } else { /* ok. we found it */ if (cmdList->clorobj && !NsfObjectIsClass(&cmdList->clorobj->object)) { *clPtr = NULL; } else { *clPtr = cmdList->clorobj; } *currentCmd = cmdList->cmdPtr; /* fprintf(stderr, "FilterSearchProc - found: %s, %p\n", Tcl_GetCommandName(interp, (Tcl_Command)cmdList->cmdPtr), cmdList->cmdPtr); */ return cmdList->cmdPtr; } } } return NULL; } /* *---------------------------------------------------------------------- * SuperclassAdd -- * * Add a list of superClasses (specified in the argument vector) to * the specified class. On the first call, the class has no previous * superClasses. * * Results: * Tcl result code. * * Side effects: * Rearranging the class relations, flushing previous precedence orders. * *---------------------------------------------------------------------- */ static int SuperclassAdd(Tcl_Interp *interp, NsfClass *cl, int oc, Tcl_Obj **ov, Tcl_Obj *arg) nonnull(1) nonnull(2) nonnull(4) nonnull(5); static int SuperclassAdd(Tcl_Interp *interp, NsfClass *cl, int oc, Tcl_Obj **ov, Tcl_Obj *arg) { NsfClasses *superClasses, *subClasses, *osl = NULL; NsfObjectSystem *osPtr; NsfClass **scl; int i, j; assert(interp); assert(cl); assert(ov); assert(arg); superClasses = PrecedenceOrder(cl); subClasses = DependentSubClasses(cl); /* * We have to remove all dependent superclass filter referenced * by class or one of its subclasses. * * Do not check the class "cl" itself (first entry in * filterCheck class list). */ if (superClasses != NULL) { superClasses = superClasses->nextPtr; } for (; superClasses; superClasses = superClasses->nextPtr) { FilterRemoveDependentFilterCmds(superClasses->cl, subClasses); } /* * Invalidate all interceptors orders of instances of this and of all * depended classes. */ MixinInvalidateObjOrders(interp, cl, subClasses); if (FiltersDefined(interp) > 0) { FilterInvalidateObjOrders(interp, subClasses); } /* * Build an array of superClasses from the argument vector. */ scl = NEW_ARRAY(NsfClass*, oc); for (i = 0; i < oc; i++) { if (GetClassFromObj(interp, ov[i], &scl[i], 1) != TCL_OK) { FREE(NsfClass**, scl); NsfClassListFree(subClasses); return NsfObjErrType(interp, "superclass", arg, "a list of classes", NULL); } } /* * Check that superClasses don't precede their classes. */ for (i = 0; i < oc; i++) { for (j = i+1; j < oc; j++) { NsfClasses *dl = PrecedenceOrder(scl[j]); dl = NsfClassListFind(dl, scl[i]); if (dl) { FREE(NsfClass**, scl); NsfClassListFree(subClasses); return NsfObjErrType(interp, "superclass", arg, "classes in dependence order", NULL); } } } /* * Ensure that the current class and new superClasses are from the * same object system. */ osPtr = GetObjectSystem(&cl->object); for (i = 0; i < oc; i++) { if (osPtr != GetObjectSystem(&scl[i]->object)) { NsfPrintError(interp, "class \"%s\" has a different object system as class \"%s\"", ClassName(cl), ClassName(scl[i])); NsfClassListFree(subClasses); FREE(NsfClass**, scl); return TCL_ERROR; } } while (cl->super) { /* * Build a backup of the old superclass list in case we need to revert. */ NsfClass *sc = cl->super->cl; NsfClasses *l = osl; osl = NEW(NsfClasses); osl->cl = sc; osl->nextPtr = l; (void)RemoveSuper(cl, cl->super->cl); } for (i = 0; i < oc; i++) { AddSuper(cl, scl[i]); } FlushPrecedences(subClasses); NsfClassListFree(subClasses); FREE(NsfClass**, scl); if (unlikely(!PrecedenceOrder(cl))) { NsfClasses *l; /* * There is a cycle in the superclass graph, we have to revert and return * an error. */ while (cl->super) { (void)RemoveSuper(cl, cl->super->cl); } for (l = osl; l; l = l->nextPtr) { AddSuper(cl, l->cl); } if (osl) NsfClassListFree(osl); return NsfObjErrType(interp, "superclass", arg, "a cycle-free graph", NULL); } if (osl) NsfClassListFree(osl); assert(cl->super); Tcl_ResetResult(interp); return TCL_OK; } /* *---------------------------------------------------------------------- * CheckVarName -- * * Check, whether the provided name is free of namespace markup. * * Results: * Tcl result code. * * Side effects: * none * *---------------------------------------------------------------------- */ static int CheckVarName(Tcl_Interp *interp, const char *varNameString) nonnull(1) nonnull(2); static int CheckVarName(Tcl_Interp *interp, const char *varNameString) { assert(interp); assert(varNameString); /* * We want to have a plain variable name, since we do not want to * get interferences with namespace resolver and such. In an first * attempt, we disallowed occurrences of "::", but we have to deal as * well with e.g. arrayName(::x::y) * * TODO: more general and efficient solution to disallow e.g. a::b * (check for :: until parens) */ /*if (strstr(varNameString, "::") || *varNameString == ':') {*/ if (*varNameString == ':') { return NsfPrintError(interp, "variable name \"%s\" must not contain " "namespace separator or colon prefix", varNameString); } return TCL_OK; } /* *---------------------------------------------------------------------- * VarExists -- * * Check, whether the named variable exists on the specified object. * * Results: * Tcl result code. * * Side effects: * none * *---------------------------------------------------------------------- */ static int VarExists(Tcl_Interp *interp, NsfObject *object, CONST char *name1, CONST char *name2, unsigned int flags) nonnull(1) nonnull(2) nonnull(3); static int VarExists(Tcl_Interp *interp, NsfObject *object, CONST char *name1, CONST char *name2, unsigned int flags) { CallFrame frame, *framePtr = &frame; Var *varPtr, *arrayPtr; int result; assert(interp); assert(object); assert(name1); Nsf_PushFrameObj(interp, object, framePtr); if (flags & NSF_VAR_TRIGGER_TRACE) { varPtr = TclVarTraceExists(interp, name1); } else { unsigned int flags = (name2 == NULL) ? TCL_PARSE_PART1 : 0; varPtr = TclLookupVar(interp, name1, name2, flags, "access", /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); } /* fprintf(stderr, "VarExists %s varPtr %p flags %.4x isundef %d\n", name1, varPtr, flags, varPtr ? TclIsVarUndefined(varPtr) : NULL); */ result = (varPtr && ((flags & NSF_VAR_REQUIRE_DEFINED) == 0 || !TclIsVarUndefined(varPtr))); if (result && (flags & NSF_VAR_ISARRAY) && !TclIsVarArray(varPtr)) { result = 0; } Nsf_PopFrameObj(interp, framePtr); return result; } #if defined(WITH_TCL_COMPILE) # include #endif /* *---------------------------------------------------------------------- * MakeProcError -- * * Function called internally from Tcl in case the definition of the proc * failed. * * Results: * None * * Side effects: * none * *---------------------------------------------------------------------- */ static void MakeProcError( Tcl_Interp *interp, /* The interpreter in which the procedure was called. */ Tcl_Obj *procNameObj) /* Name of the procedure. Used for error * messages and trace information. */ { int overflow, limit = 60, nameLen; const char *procName; /*fprintf(stderr, "MakeProcError %p type %p refCount %d\n", procNameObj, procNameObj->typePtr, procNameObj->refCount);*/ procName = Tcl_GetString(procNameObj); nameLen = procNameObj->length; overflow = (nameLen > limit); Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (procedure \"%.*s%s\" line %d)", (overflow ? limit : nameLen), procName, (overflow ? "..." : ""), Tcl_GetErrorLine(interp))); } /* *---------------------------------------------------------------------- * ByteCompiled -- * * Function to determine wether a proc is already byted compiled or not. * * Results: * 0 or 1 based on success * * Side effects: * none * *---------------------------------------------------------------------- */ static int ByteCompiled(Tcl_Interp *interp, unsigned int *flagsPtr, Proc *procPtr, CONST char *procName) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int ByteCompiled(Tcl_Interp *interp, unsigned int *flagsPtr, Proc *procPtr, CONST char *procName) { Namespace *nsPtr; Tcl_Obj *bodyObj; assert(interp); assert(flagsPtr); assert(procPtr); assert(procName); nsPtr = procPtr->cmdPtr->nsPtr; bodyObj = procPtr->bodyPtr; if (likely(bodyObj->typePtr == Nsf_OT_byteCodeType)) { #if defined(HAVE_TCL_COMPILE_H) ByteCode *codePtr; Interp *iPtr = (Interp *) interp; /* * When we've got bytecode, this is the check for validity. That is, * the bytecode must be for the right interpreter (no cross-leaks!), * the code must be from the current epoch (so subcommand compilation * is up-to-date), the namespace must match (so variable handling * is right) and the resolverEpoch must match (so that new shadowed * commands and/or resolver changes are considered). */ codePtr = bodyObj->internalRep.otherValuePtr; if (unlikely(((Interp *) *codePtr->interpHandle != iPtr) || (codePtr->compileEpoch != iPtr->compileEpoch) || (codePtr->nsPtr != nsPtr) || (codePtr->nsEpoch != nsPtr->resolverEpoch))) { # if defined(VAR_RESOLVER_TRACE) fprintf(stderr, "ByteCompiled bytecode not valid proc %p cmd %p method %s\n", procPtr, procPtr->cmdPtr, Tcl_GetCommandName(interp, (Tcl_Command)procPtr->cmdPtr)); fprintf(stderr, " %d %d %d %d\n", ((Interp *) *codePtr->interpHandle != iPtr), (codePtr->compileEpoch != iPtr->compileEpoch), (codePtr->nsPtr != nsPtr), (codePtr->nsEpoch != nsPtr->resolverEpoch)); { CompiledLocal *localPtr = procPtr->firstLocalPtr; for (; localPtr != NULL; localPtr = localPtr->nextPtr) { fprintf(stderr, "... local %p '%s' resolveInfo %p deleteProc %p\n", localPtr, localPtr->name, localPtr->resolveInfo, localPtr->resolveInfo ? localPtr->resolveInfo->deleteProc : NULL); } } # endif /* dummy statement for coverage analysis */ assert(1); goto doCompilation; } #endif return TCL_OK; } else { int result; #if defined(HAVE_TCL_COMPILE_H) doCompilation: #endif *flagsPtr |= NSF_CSC_CALL_IS_COMPILE; result = TclProcCompileProc(interp, procPtr, bodyObj, (Namespace *) nsPtr, "body of proc", procName); *flagsPtr &= ~NSF_CSC_CALL_IS_COMPILE; return result; } } /* *---------------------------------------------------------------------- * PushProcCallFrame -- * * Set up and push a new call frame for the procedure invocation. * call-frame. The proc is passed via clientData. * * Results: * Tcl result code * * Side effects: * compiles body conditionally * *---------------------------------------------------------------------- */ static int PushProcCallFrame(Proc *procPtr, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], NsfCallStackContent *cscPtr) nonnull(1) nonnull(2) nonnull(4) nonnull(5); static int PushProcCallFrame(Proc *procPtr, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], NsfCallStackContent *cscPtr) { Tcl_CallFrame *framePtr; int result; assert(procPtr); assert(interp); assert(objv); assert(cscPtr); /* * Set up and push a new call frame for the new procedure invocation. * This call frame will execute in the proc's namespace, which might be * different than the current namespace. The proc's namespace is that of * its command, which can change if the command is renamed from one * namespace to another. */ /* TODO: we could use Tcl_PushCallFrame(), if we would allocate the tcl stack frame earlier */ result = TclPushStackFrame(interp, (Tcl_CallFrame **)&framePtr, (Tcl_Namespace *) procPtr->cmdPtr->nsPtr, (FRAME_IS_PROC|FRAME_IS_NSF_METHOD)); if (unlikely(result != TCL_OK)) { return result; } Tcl_CallFrame_objc(framePtr) = objc; Tcl_CallFrame_objv(framePtr) = objv; Tcl_CallFrame_procPtr(framePtr) = procPtr; Tcl_CallFrame_clientData(framePtr) = cscPtr; /*fprintf(stderr, "Stack Frame %p procPtr %p compiledLocals %p firstLocal %p\n", framePtr, procPtr, Tcl_CallFrame_compiledLocals(framePtr), procPtr->firstLocalPtr);*/ return ByteCompiled(interp, &cscPtr->flags, procPtr, ObjStr(objv[0])); } #include "nsfAPI.h" /* *---------------------------------------------------------------------- * ObjectSystemsCheckSystemMethod -- * * Mark in all object systems the specified method as * (potentially) overloaded and mark it in the specified * object system as defined. * * Results: * Tcl result code. * * Side effects: * Updating the object system structure(s). * *---------------------------------------------------------------------- */ static int ObjectSystemsCheckSystemMethod(Tcl_Interp *interp, CONST char *methodName, NsfObject *object, unsigned int flags) { NsfObjectSystem *osPtr, *defOsPtr = GetObjectSystem(object); char firstChar; assert(interp); assert(object); assert(methodName); firstChar = *methodName; for (osPtr = RUNTIME_STATE(interp)->objectSystems; osPtr; osPtr = osPtr->nextPtr) { int i, rootClassMethod, flag = 0; NsfObject *defObject; for (i = 0; i <= NSF_s_set_idx; i++) { Tcl_Obj *methodObj = osPtr->methods[i]; CONST char *methodString = methodObj ? ObjStr(methodObj) : NULL; if (methodString && *methodString == firstChar && !strcmp(methodName, methodString)) { flag = 1<rootClass->object : &osPtr->rootMetaClass->object; if (osPtr->handles[i] && osPtr->protected[i]) { if (defObject == object && (flags & NSF_CMD_REDEFINE_PROTECTED_METHOD) == 0) { return NsfPrintError(interp, "refuse to overwrite protected method %s on %s", methodName, ObjectName(defObject)); } } if (osPtr->definedMethods & flag) { /* * If for some reason (e.g. reload) we redefine the base * methods, these never count as overloads. */ if ((rootClassMethod && object == &defOsPtr->rootClass->object) || (!rootClassMethod && object == &defOsPtr->rootMetaClass->object) ) { /*fprintf(stderr, "+++ %s %.6x NOT overloading %s.%s %s (is root %d, is meta %d)\n", ClassName(defOsPtr->rootClass), osPtr->overloadedMethods, ObjectName(object), methodName, Nsf_SystemMethodOpts[i], object == &defOsPtr->rootClass->object, object == &defOsPtr->rootMetaClass->object);*/ } else { osPtr->overloadedMethods |= flag; /*fprintf(stderr, "+++ %s %.6x overloading %s.%s %s (is root %d, is meta %d)\n", ClassName(defOsPtr->rootClass), osPtr->overloadedMethods, ObjectName(object), methodName, Nsf_SystemMethodOpts[i], object == &defOsPtr->rootClass->object, object == &defOsPtr->rootMetaClass->object);*/ } } if (osPtr == defOsPtr && ((osPtr->definedMethods & flag) == 0)) { /* * Mark the method das defined */ osPtr->definedMethods |= flag; /*fprintf(stderr, "+++ %s %.6x defining %s.%s %s osPtr %p defined %.8x flag %.8x handle %p\n", ClassName(defOsPtr->rootClass), osPtr->definedMethods, ObjectName(object), methodName, Nsf_SystemMethodOpts[i], osPtr, osPtr->definedMethods, flag, osPtr->handles[i]);*/ /* * If there is a method-handle provided for this system method, * register it as a fallback unless the method being defined is * already at the root class. */ if (osPtr->handles[i]) { if (defObject != object) { int result = NsfMethodAliasCmd(interp, defObject, 0, methodName, 0, ProtectionRedefine_protectedIdx, osPtr->handles[i]); if (result != TCL_OK) { return TCL_ERROR; } /* * Since the defObject is not equals the overloaded method, the * definition above is effectively an overload of the alias. */ osPtr->overloadedMethods |= flag; NsfLog(interp, NSF_LOG_NOTICE, "Define automatically alias %s for %s", ObjStr(osPtr->handles[i]), Nsf_SystemMethodOpts[i]); /* * If the definition was ok, make the method protected. */ if (likely(result == TCL_OK)) { Tcl_Obj *methodObj = Tcl_GetObjResult(interp); Tcl_Command cmd = Tcl_GetCommandFromObj(interp, methodObj); if (cmd) { Tcl_Command_flags(cmd) |= NSF_CMD_CALL_PROTECTED_METHOD; if (osPtr->protected[i]) { Tcl_Command_flags(cmd) |= NSF_CMD_REDEFINE_PROTECTED_METHOD; } } Tcl_ResetResult(interp); } else { NsfLog(interp, NSF_LOG_WARN, "Could not define alias %s for %s", ObjStr(osPtr->handles[i]), Nsf_SystemMethodOpts[i]); } } } } } return TCL_OK; } /*---------------------------------------------------------------------- * ParamsNew -- * * Allocate an array of Nsf_Param structures * * Results: * Pointer to allocated memory * * Side effects: * Allocation of memory. * *---------------------------------------------------------------------- */ static Nsf_Param * ParamsNew(int nr) { Nsf_Param *paramsPtr = NEW_ARRAY(Nsf_Param, nr+1); memset(paramsPtr, 0, sizeof(Nsf_Param)*(nr+1)); return paramsPtr; } /*---------------------------------------------------------------------- * ParamFree -- * * Deallocate the contents of a single Nsf_Param* * * Results: * None. * * Side effects: * Free the parameter definition. * *---------------------------------------------------------------------- */ static void ParamFree(Nsf_Param *paramPtr) nonnull(1); static void ParamFree(Nsf_Param *paramPtr) { assert(paramPtr); /*fprintf(stderr, "ParamFree %p\n", paramPtr);*/ if (paramPtr->name) {STRING_FREE("paramPtr->name", paramPtr->name);} if (paramPtr->nameObj) {DECR_REF_COUNT(paramPtr->nameObj);} if (paramPtr->defaultValue) {DECR_REF_COUNT(paramPtr->defaultValue);} if (paramPtr->converterName) {DECR_REF_COUNT2("converterNameObj", paramPtr->converterName);} if (paramPtr->converterArg) {DECR_REF_COUNT(paramPtr->converterArg);} if (paramPtr->paramObj) {DECR_REF_COUNT(paramPtr->paramObj);} if (paramPtr->slotObj) {DECR_REF_COUNT(paramPtr->slotObj);} if (paramPtr->method) {DECR_REF_COUNT(paramPtr->method);} } /*---------------------------------------------------------------------- * ParamsFree -- * * Deallocate a block of multiple Nsf_Param* * * Results: * None. * * Side effects: * Free the parameter definition. * *---------------------------------------------------------------------- */ static void ParamsFree(Nsf_Param *paramsPtr) nonnull(1); static void ParamsFree(Nsf_Param *paramsPtr) { Nsf_Param *paramPtr; assert(paramsPtr); /*fprintf(stderr, "ParamsFree %p\n", paramsPtr);*/ for (paramPtr = paramsPtr; paramPtr->name; paramPtr++) { ParamFree(paramPtr); } FREE(Nsf_Param*, paramsPtr); } /*---------------------------------------------------------------------- * ParamDefsGet -- * * Obtain parameter definitions for a cmdPtr; Optionally, this command * returns as well a flag for ProcessMethodArguments to indicate if the * parameter have to checked always. * * Results: * Parameter definitions or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ NSF_INLINE static NsfParamDefs *ParamDefsGet(Tcl_Command cmdPtr, int *checkAlwaysFlagPtr) nonnull(1); NSF_INLINE static NsfParamDefs * ParamDefsGet(Tcl_Command cmdPtr, int *checkAlwaysFlagPtr) { assert(cmdPtr); if (likely(Tcl_Command_deleteProc(cmdPtr) == NsfProcDeleteProc)) { NsfProcContext *ctx = (NsfProcContext *)Tcl_Command_deleteData(cmdPtr); if (checkAlwaysFlagPtr) { *checkAlwaysFlagPtr = ctx->checkAlwaysFlag;} return ctx->paramDefs; } return NULL; } /*---------------------------------------------------------------------- * NsfParamDefsFilter -- * * Process a list of ParamDefs and return a subset of it matching the * provided pattern. If no parameter name matches the pattern, NULL is * returned. The client is supposed to FREE the returned parameter list * (entries are shared, a free of the returned pointer is sufficient). * * Results: * Parameter definitions or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ static Nsf_Param *NsfParamDefsFilter(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, CONST char *pattern) nonnull(1) nonnull(2) nonnull(3); static Nsf_Param * NsfParamDefsFilter(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, CONST char *pattern) { static Nsf_Param *paramList = NULL; Nsf_Param CONST *pPtr; int maxParams, nrMatchingParams; /* * If a single parameter or pattern name is given, we construct a filtered * parameter list on the fly and we return it */ /* * Count the parameters */ for (pPtr = paramsPtr, maxParams = 0; pPtr->name; pPtr++, maxParams++); /* * Allocate the number of potentional matches */ paramList = ParamsNew(maxParams); for (pPtr = paramsPtr, nrMatchingParams = 0; pPtr->name; pPtr++) { if (Tcl_StringMatch( ObjStr(pPtr->nameObj), pattern)) { paramList[nrMatchingParams] = *pPtr; nrMatchingParams++; } } if (nrMatchingParams == 0) { /* * The named parameter were NOT found, so return NULL */ FREE(Nsf_Param*, paramList); paramList = NULL; } return paramList; } /*---------------------------------------------------------------------- * NsfParamDefsNonposLookup -- * * Process a list of ParamDefs look for a nonpos args. If there is no exact * match, look for an abbreviated match having at least * NSF_ABBREV_MIN_CHARS leading chars which are identical. * * Results: * Parameter definition or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ static int NsfParamDefsNonposLookup(Tcl_Interp *interp, CONST char *nameString, Nsf_Param CONST *paramsPtr, Nsf_Param CONST **paramPtrPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int NsfParamDefsNonposLookup(Tcl_Interp *interp, CONST char *nameString, Nsf_Param CONST *paramsPtr, Nsf_Param CONST **paramPtrPtr) { Nsf_Param CONST *paramPtr; char ch1 = nameString[2]; int length; assert(interp); assert(nameString); assert(paramsPtr); assert(paramPtrPtr); /* * The provided paramsPtr must point to a block starting with a nonpos arg. */ assert(paramsPtr->name); assert(*paramsPtr->name == '-'); /* * The provided nameString starts as well with a leading dash. */ assert(*nameString == '-'); for (paramPtr = paramsPtr; likely(paramPtr->name != NULL) && *paramPtr->name == '-'; paramPtr++) { if (unlikely(paramPtr->flags & NSF_ARG_NOCONFIG)) continue; if (ch1 == paramPtr->name[2] && strcmp(nameString, paramPtr->name) == 0) { *paramPtrPtr = paramPtr; return TCL_OK; } } length = strlen(nameString); if (length >= NSF_ABBREV_MIN_CHARS) { for (paramPtr = paramsPtr; likely(paramPtr->name != NULL) && *paramPtr->name == '-'; paramPtr++) { if (unlikely(paramPtr->flags & NSF_ARG_NOCONFIG)) continue; if (ch1 == paramPtr->name[2] && strncmp(nameString, paramPtr->name, length) == 0) { Nsf_Param CONST *pPtr; /* fprintf(stderr, "... <%s> is an abbrev of <%s>\n", nameString, paramPtr->name); */ /* * Check, if the abbreviation is unique */ for (pPtr = paramPtr + 1; likely(pPtr->name != NULL) && *pPtr->name == '-'; pPtr++) { if (unlikely(pPtr->flags & NSF_ARG_NOCONFIG)) continue; if (ch1 == pPtr->name[2] && strncmp(nameString, pPtr->name, length) == 0) { /* * The abbreviation is not unique */ *paramPtrPtr = NULL; return NsfPrintError(interp, "the provided argument %s is an abbreviation for %s and %s", nameString, paramPtr->name, pPtr->name); } } /* * The abbreviation is unique */ *paramPtrPtr = paramPtr; return TCL_OK; } } } *paramPtrPtr = NULL; return TCL_OK; } /* *---------------------------------------------------------------------- * NsfProcDeleteProc -- * * FreeProc for procs with associated parameter definitions. * * Results: * None. * * Side effects: * Freeing memory. * *---------------------------------------------------------------------- */ static void NsfProcDeleteProc(ClientData clientData) { NsfProcContext *ctxPtr = (NsfProcContext *)clientData; assert(clientData); if (ctxPtr->oldDeleteProc) { (*ctxPtr->oldDeleteProc)(ctxPtr->oldDeleteData); } if (ctxPtr->paramDefs) { /*fprintf(stderr, "free ParamDefs %p\n", ctxPtr->paramDefs);*/ ParamDefsRefCountDecr(ctxPtr->paramDefs); } /*fprintf(stderr, "free %p\n", ctxPtr);*/ FREE(NsfProcContext, ctxPtr); } /* *---------------------------------------------------------------------- * ParamDefsStore -- * * Store the provided parameter definitions in the provided * command. It stores a new deleteProc which will call the original * delete proc automatically. * * Results: * Tcl result code. * * Side effects: * None * *---------------------------------------------------------------------- */ static int ParamDefsStore(Tcl_Interp *interp, Tcl_Command cmd, NsfParamDefs *paramDefs, int checkAlwaysFlag) nonnull(1) nonnull(2); static int ParamDefsStore(Tcl_Interp *interp, Tcl_Command cmd, NsfParamDefs *paramDefs, int checkAlwaysFlag) { Command *cmdPtr = (Command *)cmd; assert(interp); assert(cmd); /* * TODO This function might store empty paramDefs. needed? */ if (cmdPtr->deleteProc != NsfProcDeleteProc) { NsfProcContext *ctxPtr = NEW(NsfProcContext); /*fprintf(stderr, "ParamDefsStore %p (%s) replace deleteProc %p by %p\n", paramDefs, Tcl_GetCommandName(interp, cmd), cmdPtr->deleteProc, NsfProcDeleteProc);*/ ctxPtr->oldDeleteData = (Proc *)cmdPtr->deleteData; ctxPtr->oldDeleteProc = cmdPtr->deleteProc; cmdPtr->deleteProc = NsfProcDeleteProc; ctxPtr->paramDefs = paramDefs; ctxPtr->checkAlwaysFlag = checkAlwaysFlag; cmdPtr->deleteData = ctxPtr; return TCL_OK; } else { /*fprintf(stderr, "ParamDefsStore cmd %p has already NsfProcDeleteProc deleteData %p\n", cmd, cmdPtr->deleteData);*/ if (cmdPtr->deleteData) { NsfProcContext *ctxPtr = cmdPtr->deleteData; assert(ctxPtr->paramDefs == NULL); ctxPtr->paramDefs = paramDefs; } } return TCL_ERROR; } /* *---------------------------------------------------------------------- * ParamDefsNew -- * * Allocate a new paramDefs structure and initialize it with zeros. The * allocated structure should be freed with ParamDefsFree(). * * Results: * pointer to paramDefs structure * * Side effects: * Allocating memory * *---------------------------------------------------------------------- */ static NsfParamDefs * ParamDefsNew() { NsfParamDefs *paramDefs; static NsfMutex serialMutex = 0; static int serial = 0; paramDefs = NEW(NsfParamDefs); memset(paramDefs, 0, sizeof(NsfParamDefs)); /* We could keep the serial as well in thread local storage */ NsfMutexLock(&serialMutex); paramDefs->serial = serial++; NsfMutexUnlock(&serialMutex); /*fprintf(stderr, "ParamDefsNew %p\n", paramDefs);*/ return paramDefs; } /* *---------------------------------------------------------------------- * ParamDefsFree -- * * Actually free the parameter definitions. Since the parameter definitions * are ref-counted, this function should be just called via * ParamDefsRefCountDecr. * * Results: * None. * * Side effects: * Free the parameter definitions. * *---------------------------------------------------------------------- */ static void ParamDefsFree(NsfParamDefs *paramDefs) nonnull(1); static void ParamDefsFree(NsfParamDefs *paramDefs) { /*fprintf(stderr, "ParamDefsFree %p slotObj %p returns %p\n", paramDefs, paramDefs->slotObj, paramDefs->returns);*/ assert(paramDefs); if (paramDefs->paramsPtr) { ParamsFree(paramDefs->paramsPtr); } if (paramDefs->slotObj) {DECR_REF_COUNT2("paramDefsObj", paramDefs->slotObj);} if (paramDefs->returns) {DECR_REF_COUNT2("paramDefsObj", paramDefs->returns);} FREE(NsfParamDefs, paramDefs); } /* *---------------------------------------------------------------------- * ParamDefsRefCountIncr -- * ParamDefsRefCountDecr -- * * Perform book keeping on the parameter definitions. RefCounting is * necessary, since it might be possible that during the processing of the * e.g. object parameters, these might be redefined (when an object * parameter calls a method, redefining the * structures). ParamDefsRefCountDecr() is responsible for actually freeing * the structure. * * Results: * None. * * Side effects: * No direct. * *---------------------------------------------------------------------- */ static void ParamDefsRefCountIncr(NsfParamDefs *paramDefs) { assert(paramDefs); paramDefs->refCount ++; } static void ParamDefsRefCountDecr(NsfParamDefs *paramDefs) { assert(paramDefs); paramDefs->refCount --; if (paramDefs->refCount < 1) { ParamDefsFree(paramDefs); } } /* *---------------------------------------------------------------------- * ParamDefsFormatOption -- * * Append a parameter option to the nameStringObj representing the * syntax of the parameter definition. * * Results: * None. * * Side effects: * none * *---------------------------------------------------------------------- */ static void ParamDefsFormatOption(Tcl_Obj *nameStringObj, CONST char *option, int *colonWritten, int *firstOption) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static void ParamDefsFormatOption(Tcl_Obj *nameStringObj, CONST char *option, int *colonWritten, int *firstOption) { assert(nameStringObj); assert(option); assert(colonWritten); assert(firstOption); if (!*colonWritten) { Tcl_AppendLimitedToObj(nameStringObj, ":", 1, INT_MAX, NULL); *colonWritten = 1; } if (*firstOption) { *firstOption = 0; } else { Tcl_AppendLimitedToObj(nameStringObj, ",", 1, INT_MAX, NULL); } Tcl_AppendLimitedToObj(nameStringObj, option, -1, INT_MAX, NULL); } /* *---------------------------------------------------------------------- * ParamDefsFormat -- * * Produce a Tcl_Obj representing a single parameter in the syntax * of the parameter definition. * * Results: * Tcl_Obj * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_Obj *ParamDefsFormat(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern) nonnull(1) nonnull(2) returns_nonnull; static int ParamsDefMatchPattern(Nsf_Param CONST *paramsPtr, CONST char *pattern) { if (paramsPtr->nameObj != NULL) { return Tcl_StringMatch(ObjStr(paramsPtr->nameObj), pattern); } return Tcl_StringMatch(paramsPtr->name, pattern); } static Tcl_Obj * ParamDefsFormat(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern) { int first, colonWritten; Tcl_Obj *listObj = Tcl_NewListObj(0, NULL), *innerListObj, *nameStringObj; assert(interp); assert(paramsPtr); INCR_REF_COUNT2("paramDefsObj", listObj); for (; likely(paramsPtr->name != NULL); paramsPtr++) { if (paramsPtr->flags & NSF_ARG_NOCONFIG) { continue; } if (paramsPtr->paramObj) { if (pattern && !ParamsDefMatchPattern(paramsPtr, pattern)) continue; innerListObj = paramsPtr->paramObj; } else { /* * We need this part only for C-defined parameter definitions, defined * via genTclAPI. * * TODO: we could streamline this by defining as well C-API via the same * syntax as for accepted for tcl obj types "nsfParam" */ int isNonpos = *paramsPtr->name == '-'; int outputRequired = (isNonpos && (paramsPtr->flags & NSF_ARG_REQUIRED)); int outputOptional = (!isNonpos && !(paramsPtr->flags & NSF_ARG_REQUIRED) && !paramsPtr->defaultValue && paramsPtr->converter != ConvertToNothing); first = 1; colonWritten = 0; if (NsfParamDefsAppendVirtual(interp, listObj, paramsPtr, contextObject, pattern, ParamDefsFormat)) { continue; } if (pattern && !ParamsDefMatchPattern(paramsPtr, pattern)) continue; nameStringObj = Tcl_NewStringObj(paramsPtr->name, -1); if (paramsPtr->type) { ParamDefsFormatOption(nameStringObj, paramsPtr->type, &colonWritten, &first); } else if (isNonpos && paramsPtr->nrArgs == 0) { ParamDefsFormatOption(nameStringObj, "switch", &colonWritten, &first); } if (outputRequired) { ParamDefsFormatOption(nameStringObj, "required", &colonWritten, &first); } else if (outputOptional) { ParamDefsFormatOption(nameStringObj, "optional", &colonWritten, &first); } if ((paramsPtr->flags & NSF_ARG_SUBST_DEFAULT)) { ParamDefsFormatOption(nameStringObj, "substdefault", &colonWritten, &first); } if ((paramsPtr->flags & NSF_ARG_ALLOW_EMPTY) || (paramsPtr->flags & NSF_ARG_MULTIVALUED)) { char option[10] = "...."; option[0] = (paramsPtr->flags & NSF_ARG_ALLOW_EMPTY) ? '0' : '1'; option[3] = (paramsPtr->flags & NSF_ARG_MULTIVALUED) ? '*' : '1'; ParamDefsFormatOption(nameStringObj, option, &colonWritten, &first); } if ((paramsPtr->flags & NSF_ARG_IS_CONVERTER)) { ParamDefsFormatOption(nameStringObj, "convert", &colonWritten, &first); } if ((paramsPtr->flags & NSF_ARG_INITCMD)) { ParamDefsFormatOption(nameStringObj, "initcmd", &colonWritten, &first); } else if ((paramsPtr->flags & NSF_ARG_CMD)) { ParamDefsFormatOption(nameStringObj, "cmd", &colonWritten, &first); } else if ((paramsPtr->flags & NSF_ARG_ALIAS)) { ParamDefsFormatOption(nameStringObj, "alias", &colonWritten, &first); } else if ((paramsPtr->flags & NSF_ARG_FORWARD)) { ParamDefsFormatOption(nameStringObj, "forward", &colonWritten, &first); } else if ((paramsPtr->flags & NSF_ARG_NOARG)) { ParamDefsFormatOption(nameStringObj, "noarg", &colonWritten, &first); } else if ((paramsPtr->flags & NSF_ARG_NOCONFIG)) { ParamDefsFormatOption(nameStringObj, "noconfig", &colonWritten, &first); } innerListObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, innerListObj, nameStringObj); if (paramsPtr->defaultValue) { Tcl_ListObjAppendElement(interp, innerListObj, paramsPtr->defaultValue); } } Tcl_ListObjAppendElement(interp, listObj, innerListObj); } return listObj; } /* *---------------------------------------------------------------------- * ParamDefsList -- * * Produce a Tcl_ListObj containing the list of the parameters * based on a parameter structure. * * Results: * Tcl_Obj * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_Obj *ParamDefsList(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern) nonnull(1) nonnull(2) returns_nonnull; static Tcl_Obj * ParamDefsList(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern) { Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); assert(interp); assert(paramsPtr); INCR_REF_COUNT2("paramDefsObj", listObj); for (; likely(paramsPtr->name != NULL); paramsPtr++) { if ((paramsPtr->flags & NSF_ARG_NOCONFIG) != 0) continue; if (NsfParamDefsAppendVirtual(interp, listObj, paramsPtr, contextObject, pattern, ParamDefsList)) continue; Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(paramsPtr->name, -1)); } return listObj; } /* *---------------------------------------------------------------------- * ParamDefsNames -- * * Produce a Tcl_ListObj containing the names of the parameters * based on a parameter structure. * * Results: * Tcl_Obj * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_Obj * ParamDefsNames(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern) nonnull(1) nonnull(2) returns_nonnull; static Tcl_Obj * ParamDefsNames(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern) { Tcl_Obj *listObj = Tcl_NewListObj(0, NULL), *obj; assert(interp); assert(paramsPtr); INCR_REF_COUNT2("paramDefsObj", listObj); for (; likely(paramsPtr->name != NULL); paramsPtr++) { if ((paramsPtr->flags & NSF_ARG_NOCONFIG) != 0) continue; if (NsfParamDefsAppendVirtual(interp, listObj, paramsPtr, contextObject, pattern, ParamDefsNames)) continue; obj = paramsPtr->nameObj ? paramsPtr->nameObj : Tcl_NewStringObj(paramsPtr->name,-1); if (pattern && !Tcl_StringMatch(ObjStr(obj), pattern)) continue; Tcl_ListObjAppendElement(interp, listObj, obj); } return listObj; } /* *---------------------------------------------------------------------- * ParamGetType -- * * Obtain the type of a single parameter and return it as a string. * * Results: * Type of the parameter in form of a string * * Side effects: * None. * *---------------------------------------------------------------------- */ static CONST char *ParamGetType(Nsf_Param CONST *paramPtr) nonnull(1) returns_nonnull; static CONST char * ParamGetType(Nsf_Param CONST *paramPtr) { CONST char *result = "value"; assert(paramPtr); if (paramPtr->type) { if (paramPtr->converter == ConvertViaCmd) { result = paramPtr->type + 5; } else if (paramPtr->converter == Nsf_ConvertToClass && (paramPtr->flags & (NSF_ARG_BASECLASS|NSF_ARG_METACLASS)) ) { if (paramPtr->flags & NSF_ARG_BASECLASS) { result = "baseclass"; } else { result = "metaclass"; } } else if (strcmp(paramPtr->type, "stringtype") == 0) { if (paramPtr->converterArg) { result = ObjStr(paramPtr->converterArg); } } else { result = paramPtr->type; } } return result; } /* *---------------------------------------------------------------------- * ParamGetDomain -- * * Obtain the domain of a single parameter and return it as a * string. The domain is an approximate type used in the parameter * syntax. * * Results: * Domain of the parameter in form of a string * * Side effects: * None. * *---------------------------------------------------------------------- */ static CONST char * ParamGetDomain(Nsf_Param CONST *paramPtr) nonnull(1) returns_nonnull; static CONST char * ParamGetDomain(Nsf_Param CONST *paramPtr) { CONST char *result = "value"; assert(paramPtr); if ((paramPtr->flags & NSF_ARG_IS_ENUMERATION)) { return Nsf_EnumerationTypeGetDomain(paramPtr->converter); } else { result = ParamGetType(paramPtr); } return result; } /* *---------------------------------------------------------------------- * NsfParamDefsSyntaxOne -- * * Appends the formatted parameter (provided as 2nd argument) to the * content of the first argument. * * Results: * None * * Side effects: * Appending to first argument. * *---------------------------------------------------------------------- */ static void NsfParamDefsSyntaxOne(Tcl_Obj *argStringObj, Nsf_Param CONST *pPtr) nonnull(1) nonnull(2); static void NsfParamDefsSyntaxOne(Tcl_Obj *argStringObj, Nsf_Param CONST *pPtr) { assert(argStringObj); assert(pPtr); if (pPtr->nrArgs > 0 && *pPtr->name == '-') { Tcl_AppendLimitedToObj(argStringObj, pPtr->name, -1, INT_MAX, NULL); Tcl_AppendLimitedToObj(argStringObj, " ", 1, INT_MAX, NULL); if ((pPtr->flags & NSF_ARG_IS_ENUMERATION)) { Tcl_AppendLimitedToObj(argStringObj, ParamGetDomain(pPtr), -1, INT_MAX, NULL); if (pPtr->flags & NSF_ARG_MULTIVALUED) { Tcl_AppendLimitedToObj(argStringObj, " ...", 4, INT_MAX, NULL); } } else { Tcl_AppendLimitedToObj(argStringObj, "/", 1, INT_MAX, NULL); Tcl_AppendLimitedToObj(argStringObj, ParamGetDomain(pPtr), -1, INT_MAX, NULL); if (pPtr->flags & NSF_ARG_MULTIVALUED) { Tcl_AppendLimitedToObj(argStringObj, " ...", 4, INT_MAX, NULL); } Tcl_AppendLimitedToObj(argStringObj, "/", 1, INT_MAX, NULL); } } else if (*pPtr->name != '-') { Tcl_AppendLimitedToObj(argStringObj, "/", 1, INT_MAX, NULL); Tcl_AppendLimitedToObj(argStringObj, pPtr->name, -1, INT_MAX, NULL); Tcl_AppendLimitedToObj(argStringObj, "/", 1, INT_MAX, NULL); } else { Tcl_AppendLimitedToObj(argStringObj, pPtr->name, -1, INT_MAX, NULL); } } /* * NsfParamDefsVirtualFormat -- * * This function is called, when we know we can resolve a virtual argument * against the context object. In such cases, obtain the resolved parsed * params and call the formatter. * * Results: * Standard Tcl result code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_Obj * NsfParamDefsVirtualFormat(Tcl_Interp *interp, Nsf_Param CONST *pPtr, NsfObject *contextObject, CONST char *pattern, NsfFormatFunction formatFunction) { NsfParsedParam parsedParam; int result; assert(interp); assert(pPtr); assert(pPtr->type); assert(formatFunction); assert(contextObject); if (strcmp(pPtr->type, "virtualobjectargs") == 0) { result = GetObjectParameterDefinition(interp, NsfGlobalObjs[NSF_EMPTY], contextObject, NULL, &parsedParam); } else if (NsfObjectIsClass(contextObject)) { result = GetObjectParameterDefinition(interp, NsfGlobalObjs[NSF_EMPTY], NULL, (NsfClass *)contextObject, &parsedParam); } else { NsfLog(interp, NSF_LOG_WARN, "virtual args: provided context is not a class <%s>", ObjectName(contextObject)); result = TCL_ERROR; } if (result == TCL_OK && parsedParam.paramDefs != NULL) { return (*formatFunction)(interp, parsedParam.paramDefs->paramsPtr, contextObject, pattern); } return NULL; } /* *---------------------------------------------------------------------- * NsfParamDefsAppendVirtual -- * * Check for the given paramsPtr wether this is a virtual parameter and if * possible, resolve it and append the formatted content to the Tcl_Obj. * * Results: * Boolean value for success * * Side effects: * None. * *---------------------------------------------------------------------- */ static int NsfParamDefsAppendVirtual(Tcl_Interp *interp, Tcl_Obj *listObj, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern, NsfFormatFunction formatFunction) { assert(interp); assert(listObj); assert(paramsPtr); assert(paramsPtr->name); assert(formatFunction); if (paramsPtr->converter == ConvertToNothing && strcmp(paramsPtr->name, "args") == 0) { if (contextObject != NULL && paramsPtr->type && strncmp(paramsPtr->type, "virtual", 7) == 0) { Tcl_Obj *formattedObj = NsfParamDefsVirtualFormat(interp, paramsPtr, contextObject, pattern, formatFunction); if (formattedObj != NULL) { Tcl_ListObjAppendList(interp, listObj, formattedObj); DECR_REF_COUNT2("paramDefsObj", formattedObj); return 1; } } } return 0; } /* *---------------------------------------------------------------------- * NsfParamDefsSyntax -- * * Return the parameter definitions of a sequence of parameters in * the form of the "parametersyntax", inspired by the Tcl manual * pages. * * Results: * Tcl_Obj containing the parameter syntax * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj *NsfParamDefsSyntax(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern) nonnull(1) nonnull(2) returns_nonnull; Tcl_Obj * NsfParamDefsSyntax(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern) { Tcl_Obj *argStringObj = Tcl_NewObj(); Nsf_Param CONST *pPtr; int needSpace = 0; assert(interp); assert(paramsPtr); INCR_REF_COUNT2("paramDefsObj", argStringObj); for (pPtr = paramsPtr; pPtr->name; pPtr++) { if ((pPtr->flags & NSF_ARG_NOCONFIG)) { /* * Don't output non-configurable parameters */ continue; } if (pPtr != paramsPtr) { /* * Don't output non-consuming parameters (i.e. positional, and no args) */ if (*pPtr->name != '-' && pPtr->nrArgs == 0) { continue; } } if (pPtr->converter == ConvertToNothing && strcmp(pPtr->name, "args") == 0) { int argsResolved = 0; if (contextObject != NULL && pPtr->type && strncmp(pPtr->type, "virtual", 7) == 0) { Tcl_Obj *formattedObj = NsfParamDefsVirtualFormat(interp, pPtr, contextObject, pattern, NsfParamDefsSyntax); if (formattedObj != NULL) { argsResolved = 1; if (needSpace) Tcl_AppendLimitedToObj(argStringObj, " ", 1, INT_MAX, NULL); Tcl_AppendObjToObj(argStringObj, formattedObj); DECR_REF_COUNT2("paramDefsObj", formattedObj); } } if (argsResolved == 0) { if (pattern && !ParamsDefMatchPattern(pPtr, pattern)) continue; if (needSpace) Tcl_AppendLimitedToObj(argStringObj, " ", 1, INT_MAX, NULL); Tcl_AppendLimitedToObj(argStringObj, "?/arg .../?", 11, INT_MAX, NULL); } } else if (pPtr->flags & NSF_ARG_REQUIRED) { if (pattern && !ParamsDefMatchPattern(pPtr, pattern)) continue; if (needSpace) Tcl_AppendLimitedToObj(argStringObj, " ", 1, INT_MAX, NULL); if ((pPtr->flags & NSF_ARG_IS_ENUMERATION)) { Tcl_AppendLimitedToObj(argStringObj, Nsf_EnumerationTypeGetDomain(pPtr->converter), -1, INT_MAX, NULL); } else { NsfParamDefsSyntaxOne(argStringObj, pPtr); } } else { if (pattern && !ParamsDefMatchPattern(pPtr, pattern)) continue; if (needSpace) Tcl_AppendLimitedToObj(argStringObj, " ", 1, INT_MAX, NULL); Tcl_AppendLimitedToObj(argStringObj, "?", 1, INT_MAX, NULL); NsfParamDefsSyntaxOne(argStringObj, pPtr); Tcl_AppendLimitedToObj(argStringObj, "?", 1, INT_MAX, NULL); } needSpace = 1; } /* caller has to decr */ return argStringObj; } /* *---------------------------------------------------------------------- * ParsedParamFree -- * * Free the provided information of the parsed parameters. * * Results: * None. * * Side effects: * Freed Memory. * *---------------------------------------------------------------------- */ static void ParsedParamFree(NsfParsedParam *parsedParamPtr) { assert(parsedParamPtr); /*fprintf(stderr, "ParsedParamFree %p, npargs %p\n", parsedParamPtr, parsedParamPtr->paramDefs);*/ if (parsedParamPtr->paramDefs) { ParamDefsRefCountDecr(parsedParamPtr->paramDefs); } FREE(NsfParsedParam, parsedParamPtr); } /* * method dispatch */ /* *---------------------------------------------------------------------- * ProcMethodDispatchFinalize -- * * Finalization function for ProcMethodDispatch which executes * scripted methods. Essentially it handles post-assertions and * frees per-invocation memory. The function was developed for NRE * enabled Tcl versions but is used in the same way for non-NRE * enabled versions. * * Results: * Tcl result code. * * Side effects: * indirect effects by calling Tcl code * *---------------------------------------------------------------------- */ static int ProcMethodDispatchFinalize(ClientData data[], Tcl_Interp *interp, int result) nonnull(1) nonnull(2); static int ProcMethodDispatchFinalize(ClientData data[], Tcl_Interp *interp, int result) { ParseContext *pcPtr = data[0]; /*CONST char *methodName = data[2];*/ #if defined(NSF_WITH_ASSERTIONS) || defined(NRE) NsfCallStackContent *cscPtr = data[1]; #endif #if defined(NSF_WITH_ASSERTIONS) NsfObject *object = cscPtr->self; NsfObjectOpt *opt = object->opt; #endif assert(data); assert(interp); /*fprintf(stderr, "ProcMethodDispatchFinalize %s %s flags %.6x isNRE %d pcPtr %p result %d\n", ObjectName(object), methodName, cscPtr->flags, (cscPtr->flags & NSF_CSC_CALL_IS_NRE), pcPtr, result);*/ #if defined(NSF_WITH_ASSERTIONS) if (unlikely(opt && object->teardown && (opt->checkoptions & CHECK_POST)) && likely(result == TCL_OK)) { int rc = AssertionCheck(interp, object, cscPtr->cl, data[2], CHECK_POST); if (rc != TCL_OK) { result = rc; } } #endif #if defined(NRE) if (likely(cscPtr->flags & NSF_CSC_CALL_IS_NRE)) { if (likely(pcPtr != NULL)) { ParseContextRelease(pcPtr); NsfTclStackFree(interp, pcPtr, "release parse context"); } result = ObjectDispatchFinalize(interp, cscPtr, result /*, "NRE" , methodName*/); CscFinish(interp, cscPtr, result, "scripted finalize"); } #else if (unlikely(pcPtr != NULL)) { ParseContextRelease(pcPtr); } #endif return result; } /* *---------------------------------------------------------------------- * ProcDispatchFinalize -- * * Finalization function for nsf::proc. Simplified version of * ProcMethodDispatchFinalize(). * * Results: * Tcl result code. * * Side effects: * indirect effects by calling Tcl code * *---------------------------------------------------------------------- */ static int ProcDispatchFinalize(ClientData data[], Tcl_Interp *interp, int result) nonnull(1) nonnull(2); static int ProcDispatchFinalize(ClientData data[], Tcl_Interp *interp, int result) { ParseContext *pcPtr = data[1]; /*CONST char *methodName = data[0]; fprintf(stderr, "ProcDispatchFinalize of method %s\n", methodName);*/ assert(data); assert(interp); # if defined(NSF_PROFILE) long int startUsec = (long int)data[2]; long int startSec = (long int)data[3]; char *methodName = data[0]; NsfRuntimeState *rst = RUNTIME_STATE(interp); if (rst->doProfile) { NsfProfileRecordProcData(interp, methodName, startSec, startUsec); } # endif ParseContextRelease(pcPtr); NsfTclStackFree(interp, pcPtr, "nsf::proc dispatch finalize release parse context"); return result; } /* *---------------------------------------------------------------------- * ProcMethodDispatch -- * * Invoke a scripted method (with assertion checking and filters). * * Results: * Tcl result code. * * Side effects: * Indirect effects by calling Tcl code * *---------------------------------------------------------------------- */ static int ProcMethodDispatch(ClientData cp, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], CONST char *methodName, NsfObject *object, NsfClass *cl, Tcl_Command cmdPtr, NsfCallStackContent *cscPtr) nonnull(1) nonnull(2) nonnull(4) nonnull(5) nonnull(6) nonnull(8) nonnull(9); static int ProcMethodDispatch(ClientData cp, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], CONST char *methodName, NsfObject *object, NsfClass *cl, Tcl_Command cmdPtr, NsfCallStackContent *cscPtr) { int result, releasePc = 0, checkAlwaysFlag = 0; NsfParamDefs *paramDefs; #if defined(NSF_WITH_ASSERTIONS) NsfObjectOpt *opt = object->opt; #endif #if defined(NRE) ParseContext *pcPtr = NULL; #else ParseContext pc, *pcPtr = &pc; #endif assert(cp); assert(interp); assert(objv); assert(methodName); assert(cmdPtr); assert(cscPtr); assert(object); assert(object->teardown); #if defined(NRE) /*fprintf(stderr, "ProcMethodDispatch cmd %s\n", Tcl_GetCommandName(interp, cmdPtr));*/ assert(cscPtr->flags & NSF_CSC_CALL_IS_NRE); #endif /* * If this is a filter, check whether its guard applies, * if not: just step forward to the next filter */ if (unlikely(cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_FILTER)) { NsfCmdList *cmdList; /* * seek cmd in obj's filterOrder */ assert(object->flags & NSF_FILTER_ORDER_VALID); /* otherwise: FilterComputeDefined(interp, object);*/ for (cmdList = object->filterOrder; cmdList && cmdList->cmdPtr != cmdPtr; cmdList = cmdList->nextPtr); if (cmdList) { /* * A filter was found, check whether it has a guard. */ if (cmdList->clientData) { result = GuardCall(object, interp, cmdList->clientData, cscPtr); } else { result = TCL_OK; } if (unlikely(result != TCL_OK)) { /*fprintf(stderr, "Filter GuardCall in invokeProc returned %d\n", result);*/ if (result != TCL_ERROR) { /* * The guard failed (but no error), and we call "next". * Since we may not be in a method with already provided * arguments, we call next with the actual arguments and * perform no argument substitution. * * The call stack content is not jet pushed to the Tcl * stack, we pass it already to search-and-invoke. */ /*fprintf(stderr, "... calling nextmethod cscPtr %p\n", cscPtr);*/ result = NextSearchAndInvoke(interp, methodName, objc, objv, cscPtr, 0); /*fprintf(stderr, "... after nextmethod result %d\n", result);*/ } /* * Next might have succeeded or not, but we are done. In the * NRE-case, we need a CscFinish for all return codes. */ #if defined(NRE) CscFinish(interp, cscPtr, result, "guard failed"); #endif return result; } } } #if defined(NSF_WITH_ASSERTIONS) if (unlikely(opt && (opt->checkoptions & CHECK_PRE)) && (result = AssertionCheck(interp, object, cl, methodName, CHECK_PRE)) == TCL_ERROR) { goto prep_done; } #endif /* * If the method to be invoked has paramDefs, we have to call the * argument parser with the argument definitions obtained from the * proc context from the cmdPtr. */ paramDefs = ParamDefsGet(cmdPtr, &checkAlwaysFlag); if (paramDefs && paramDefs->paramsPtr) { #if defined(NRE) pcPtr = (ParseContext *) NsfTclStackAlloc(interp, sizeof(ParseContext), "parse context"); #endif result = ProcessMethodArguments(pcPtr, interp, object, checkAlwaysFlag|NSF_ARGPARSE_METHOD_PUSH|NSF_ARGPARSE_FORCE_REQUIRED, paramDefs, objv[0], objc, objv); cscPtr->objc = objc; cscPtr->objv = (Tcl_Obj **)objv; if (likely(result == TCL_OK)) { releasePc = 1; result = PushProcCallFrame(cp, interp, pcPtr->objc+1, pcPtr->full_objv, cscPtr); } else { /* * some error occurred */ #if defined(NRE) ParseContextRelease(pcPtr); NsfTclStackFree(interp, pcPtr, "parse context (proc prep failed)"); pcPtr = NULL; #else ParseContextRelease(pcPtr); #endif } } else { result = PushProcCallFrame(cp, interp, objc, objv, cscPtr); } /* * The stack frame is pushed, we could do something here before * running the byte code of the body. */ /* we could consider to run here ARG_METHOD or ARG_INITCMD if (likely(result == TCL_OK)) { } */ #if defined(NSF_WITH_ASSERTIONS) prep_done: #endif if (likely(result == TCL_OK)) { #if defined(NRE) /*fprintf(stderr, "CALL TclNRInterpProcCore %s method '%s'\n", ObjectName(object), ObjStr(objv[0]));*/ Tcl_NRAddCallback(interp, ProcMethodDispatchFinalize, releasePc ? pcPtr : NULL, cscPtr, (ClientData)methodName, NULL); cscPtr->flags |= NSF_CSC_CALL_IS_NRE; result = TclNRInterpProcCore(interp, objv[0], 1, &MakeProcError); #else ClientData data[3] = { releasePc ? pcPtr : NULL, cscPtr, (ClientData)methodName }; result = TclObjInterpProcCore(interp, objv[0], 1, &MakeProcError); result = ProcMethodDispatchFinalize(data, interp, result); #endif } else /* result != OK */ { #if defined(NRE) CscFinish(interp, cscPtr, result, "nre, prep failed"); #endif } return result; } /* *---------------------------------------------------------------------- * CmdMethodDispatch -- * * Invoke a method implemented as a cmd. Essentially it stacks * optionally a frame, calls the method, pops the frame and runs * invariants. * * Results: * Tcl result code. * * Side effects: * Indirect effects by calling cmd * *---------------------------------------------------------------------- */ static int CmdMethodDispatch(ClientData cp, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], NsfObject *object, Tcl_Command cmd, NsfCallStackContent *cscPtr) nonnull(2) nonnull(4) nonnull(5) nonnull(6); static int CmdMethodDispatch(ClientData cp, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], NsfObject *object, Tcl_Command cmd, NsfCallStackContent *cscPtr) { CallFrame frame, *framePtr = &frame; int result; assert(interp); assert(objv); assert(cmd); assert(object); assert(object->teardown); #if defined(NRE) assert(!cscPtr || (cscPtr->flags & NSF_CSC_CALL_IS_NRE) == 0); #endif if (cscPtr) { /* * We have a call-stack content, but the requested dispatch will not store * the call-stack content in a corresponding call-frame on its own. To get, * for example, self introspection working for the requested dispatch, we * introduce a CMETHOD frame. */ /*fprintf(stderr, "Nsf_PushFrameCsc %s %s\n", ObjectName(object), Tcl_GetCommandName(cmd));*/ Nsf_PushFrameCsc(interp, cscPtr, framePtr); result = Tcl_NRCallObjProc(interp, Tcl_Command_objProc(cmd), cp, objc, objv); Nsf_PopFrameCsc(interp, framePtr); } else { result = Tcl_NRCallObjProc(interp, Tcl_Command_objProc(cmd), cp, objc, objv); } #if defined(NSF_WITH_ASSERTIONS) if (unlikely(object->opt != NULL) && likely(result == TCL_OK)) { CheckOptions co = object->opt->checkoptions; if ((co & CHECK_INVAR)) { int rc = AssertionCheckInvars(interp, object, Tcl_GetCommandName(interp, cmd), co); if (rc != TCL_OK) { result = rc; } } } #endif /* * Reference counting in the calling ObjectDispatch() makes sure * that obj->opt is still accessible even after "dealloc" */ return result; } /* *---------------------------------------------------------------------- * ObjectCmdMethodDispatch -- * * Invoke a method implemented as an object. The referenced object is used * as a source for methods to be executed. Essentially this is currently * primarily used to implement the dispatch of ensemble objects. * * Results: * Tcl result code. * * Side effects: * Indirect effects by calling cmd * *---------------------------------------------------------------------- */ static int ObjectCmdMethodDispatch(NsfObject *invokedObject, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], CONST char *methodName, NsfObject *callerSelf, NsfCallStackContent *cscPtr) nonnull(1) nonnull(2) nonnull(4) nonnull(5) nonnull(6) nonnull(7); static int ObjectCmdMethodDispatch(NsfObject *invokedObject, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], CONST char *methodName, NsfObject *callerSelf, NsfCallStackContent *cscPtr) { CallFrame frame, *framePtr = &frame; Tcl_Command cmd = cscPtr->cmdPtr, subMethodCmd; CONST char *subMethodName; NsfObject *actualSelf; NsfClass *actualClass; int result; assert(invokedObject); assert(interp); assert(objv); assert(methodName); assert(callerSelf); assert(cscPtr); /*fprintf(stderr, "ObjectCmdMethodDispatch %p %s\n", cmd, Tcl_GetCommandName(interp, cmd));*/ /*fprintf(stderr, "ObjectCmdMethodDispatch method %s invokedObject %p %s callerSelf %p %s\n", methodName, invokedObject, ObjectName(invokedObject), callerSelf, ObjectName(callerSelf));*/ if (unlikely(invokedObject->flags & NSF_DELETED)) { /* * When we try to invoke a deleted object, the cmd (alias) is * automatically removed. Note that the cmd might be still referenced * in various entries in the call-stack. The reference counting on * these elements takes care that the cmdPtr is deleted on a pop * operation (although we do a Tcl_DeleteCommandFromToken() below. */ /*fprintf(stderr, "methodName %s found DELETED object with cmd %p my cscPtr %p\n", methodName, cmd, cscPtr);*/ Tcl_DeleteCommandFromToken(interp, cmd); if (cscPtr->cl) { NsfInstanceMethodEpochIncr("DeleteObjectAlias"); } else { NsfObjectMethodEpochIncr("DeleteObjectAlias"); } NsfCleanupObject(invokedObject, "alias-delete1"); return NsfPrintError(interp, "trying to dispatch deleted object via method '%s'", methodName); } /* * Check, if the object cmd was called without a reference to a method. If * so, perform the standard dispatch of default methods. */ if (unlikely(objc < 2)) { if ((invokedObject->flags & NSF_PER_OBJECT_DISPATCH) != 0) { cscPtr->flags |= NSF_CSC_CALL_IS_ENSEMBLE; } Nsf_PushFrameCsc(interp, cscPtr, framePtr); result = DispatchDefaultMethod(interp, invokedObject, objv[0], NSF_CSC_IMMEDIATE); Nsf_PopFrameCsc(interp, framePtr); return result; } /* * Check, if we want NSF_KEEP_CALLER_SELF. The setting of this flag * determines the values of actualSelf and actualClass. */ if (invokedObject->flags & NSF_KEEP_CALLER_SELF) { actualSelf = callerSelf; actualClass = cscPtr->cl; } else { actualSelf = invokedObject; actualClass = NULL; } subMethodName = ObjStr(objv[1]); if ((invokedObject->flags & NSF_PER_OBJECT_DISPATCH) == 0) { /*fprintf(stderr, "invokedObject %p %s methodName %s: no perobjectdispatch\n", invokedObject, ObjectName(invokedObject), methodName);*/ #if 0 /* * We should have either an approach * - to obtain from an object to methodname the cmd, and * call e.g. MethodDispatch(), or pass a fully qualified * method name, or * - to pass an the actualSelf and invokedObject both * to MethodDispatch/MethodDispatch * TODO: maybe remove NSF_CM_KEEP_CALLER_SELF when done. */ result = MethodDispatch(object, interp, nobjc+1, nobjv-1, cmd, object, NULL /*NsfClass *cl*/, Tcl_GetCommandName(interp, cmd), NSF_CSC_TYPE_PLAIN, flags); #endif #if 1 /* simple and brutal */ if (likely(invokedObject->nsPtr != NULL)) { subMethodCmd = FindMethod(invokedObject->nsPtr, subMethodName); } else { subMethodCmd = NULL; } if (subMethodCmd == NULL) { /* no -system handling */ actualClass = SearchPLMethod(invokedObject->cl->order, subMethodName, &subMethodCmd, NSF_CMD_CALL_PRIVATE_METHOD); } if (likely(subMethodCmd != NULL)) { cscPtr->objc = objc; cscPtr->objv = objv; Nsf_PushFrameCsc(interp, cscPtr, framePtr); result = MethodDispatch(actualSelf, interp, objc-1, objv+1, subMethodCmd, actualSelf, actualClass, subMethodName, cscPtr->frameType|NSF_CSC_TYPE_ENSEMBLE, (cscPtr->flags & 0xFF)|NSF_CSC_IMMEDIATE); Nsf_PopFrameCsc(interp, framePtr); return result; } /*fprintf(stderr, "... objv[0] %s cmd %p %s csc %p\n", ObjStr(objv[0]), subMethodCmd, subMethodName, cscPtr); */ if (0) { fprintf(stderr, "new unknown\n"); return DispatchUnknownMethod(interp, invokedObject, /* objc-1, objv+1*/ objc, objv, actualSelf->cmdName, objv[1], NSF_CM_NO_OBJECT_METHOD|NSF_CSC_IMMEDIATE); } #endif return ObjectDispatch(actualSelf, interp, objc, objv, NSF_CM_KEEP_CALLER_SELF); } /* * NSF_PER_OBJECT_DISPATCH is set */ if (likely(invokedObject->nsPtr != NULL)) { subMethodCmd = FindMethod(invokedObject->nsPtr, subMethodName); } else { subMethodCmd = NULL; } /* * Make sure, that the current call is marked as an ensemble call, both * for dispatching to the default-method and for dispatching the method * interface of the given object. Otherwise, current introspection * specific to sub-methods fails (e.g., a [current method-path] in the * default-method). */ cscPtr->flags |= NSF_CSC_CALL_IS_ENSEMBLE; /* fprintf(stderr, "ensemble dispatch cp %s %s objc %d\n", ObjectName((NsfObject*)cp), methodName, objc);*/ cscPtr->objc = objc; cscPtr->objv = objv; Nsf_PushFrameCsc(interp, cscPtr, framePtr); /*fprintf(stderr, "... objv[0] %s cmd %p %s csc %p\n", ObjStr(objv[0]), subMethodCmd, subMethodName, cscPtr); */ if (likely(subMethodCmd != NULL)) { /* * In order to allow [next] to be called in an ensemble method, * an extra call-frame is needed. This CSC frame is typed as * NSF_CSC_TYPE_ENSEMBLE. Note that the associated call is flagged * additionally (NSF_CSC_CALL_IS_ENSEMBLE; see above) to be able * to identify ensemble-specific frames during [next] execution. * * The dispatch requires NSF_CSC_IMMEDIATE to be set, ensuring * that scripted methods are executed before the ensemble ends. If * they were executed later, they would find their parent frame * (CMETHOD) being popped from the stack already. */ /*fprintf(stderr, ".... ensemble dispatch object %s self %s pass %s\n", ObjectName(object), ObjectName(self), (self->flags & NSF_KEEP_CALLER_SELF) ? "callerSelf" : "invokedObject"); fprintf(stderr, ".... ensemble dispatch on %s.%s objflags %.8x cscPtr %p base flags %.6x flags %.6x cl %s\n", ObjectName(actualSelf), subMethodName, self->flags, cscPtr, (0xFF & cscPtr->flags), (cscPtr->flags & 0xFF)|NSF_CSC_IMMEDIATE, actualClass ? ClassName(actualClass) : "NONE");*/ result = MethodDispatch(actualSelf, interp, objc-1, objv+1, subMethodCmd, actualSelf, actualClass, subMethodName, cscPtr->frameType|NSF_CSC_TYPE_ENSEMBLE, (cscPtr->flags & 0xFF)|NSF_CSC_IMMEDIATE); /*if (unlikely(result != TCL_OK)) { fprintf(stderr, "ERROR: cmd %p %s subMethodName %s -- %s -- %s\n", subMethodCmd, Tcl_GetCommandName(interp, subMethodCmd), subMethodName, Tcl_GetCommandName(interp, cscPtr->cmdPtr), ObjStr(Tcl_GetObjResult(interp))); }*/ } else { /* * The method to be called was not part of this ensemble. Call * next to try to call such methods along the next path. */ Tcl_CallFrame *framePtr1; NsfCallStackContent *cscPtr1 = CallStackGetTopFrame(interp, &framePtr1); /*fprintf(stderr, "call next instead of unknown %s.%s \n", ObjectName(cscPtr->self), methodName);*/ assert(cscPtr1); if ((cscPtr1->frameType & NSF_CSC_TYPE_ENSEMBLE)) { /* * We are in an ensemble method. The next works here not on the * actual methodName + frame, but on the ensemble above it. We * locate the appropriate call-stack content and continue next on * that. */ cscPtr1 = CallStackFindEnsembleCsc(framePtr1, &framePtr1); assert(cscPtr1); } /* * We mark in the flags that we are in an ensemble but failed so far to * resolve the cmd. Now we try to resolve the unknown subcmd via next and * we record this in the flags. The method name for next might be * colon-prefixed. In these cases, we have to skip the single colon with * the MethodName() function. */ cscPtr1-> flags |= NSF_CM_ENSEMBLE_UNKNOWN; /* fprintf(stderr, "==> trying to find <%s> in ensemble <%s> via next\n", subMethodName, MethodName(cscPtr1->objv[0]));*/ result = NextSearchAndInvoke(interp, MethodName(cscPtr1->objv[0]), cscPtr1->objc, cscPtr1->objv, cscPtr1, 0); /*fprintf(stderr, "==> next %s.%s subMethodName %s (obj %s) cscPtr %p (flags %.8x)) cscPtr1 %p (flags %.8x) result %d unknown %d\n", ObjectName(callerSelf), methodName, subMethodName, ObjectName(invokedObject), cscPtr, cscPtr->flags, cscPtr1, cscPtr1 ? cscPtr1->flags : 0, result, RUNTIME_STATE(interp)->unknown);*/ if (RUNTIME_STATE(interp)->unknown) { Tcl_Obj *callInfoObj = Tcl_NewListObj(1, &callerSelf->cmdName); Tcl_CallFrame *varFramePtr, *tclFramePtr = CallStackGetTclFrame(interp,(Tcl_CallFrame *)framePtr, 1); int pathLength, pathLength0 = 0, getPath = 1, unknownIndex; Tcl_Obj *pathObj = NsfMethodNamePath(interp, tclFramePtr, MethodName(objv[0])); /* * The "next" call could not resolve the unknown subcommand. At this * point, potentially serval different ensembles were tried, which can * be found on the stack. * * Example1: call: foo a b d * mixin: foo a b c * object: foo a x * * We want to return the longest, most precise prefix (here "foo a b") * and flag "d" as unknown (here the mixin frame). Another (inferior) * solution would be to report "foo a" as know prefix and "b d" as * unknown (when the error is generated from the point of view of the * object method frame). * * In the general case, we traverse the stack for all ensembles and pick * the longest known ensemble for reporting. This path is passed to the * unknown-handler of the ensemble. */ Tcl_ListObjLength(interp, pathObj, &pathLength0); pathLength = pathLength0; for (varFramePtr = (Tcl_CallFrame *)framePtr; likely(varFramePtr != NULL); varFramePtr = Tcl_CallFrame_callerVarPtr(varFramePtr)) { NsfCallStackContent *cscPtr; /* * If we reach a non-nsf frame, or it is not an ensemble, we are done. */ cscPtr = (Tcl_CallFrame_isProcCallFrame(varFramePtr) & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) ? (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr) : NULL; if (cscPtr == NULL || (cscPtr->flags & NSF_CSC_CALL_IS_ENSEMBLE) == 0) { break; } /* * Every ensemble block starts with a frame of * NSF_CSC_TYPE_ENSEMBLE. If we find one, then we compute a new path * in the next iteration. */ if ((cscPtr->frameType & (NSF_CSC_TYPE_ENSEMBLE)) == 0) { /* get method path the next round */ getPath = 1; } else if (getPath) { Tcl_Obj *pathObj1 = CallStackMethodPath(interp, varFramePtr); int pathLength1; getPath = 0; Tcl_ListObjLength(interp, pathObj1, &pathLength1); if (pathLength1 > pathLength) { if (pathObj != NULL) { DECR_REF_COUNT(pathObj); } pathObj = pathObj1; pathLength = pathLength1; } } } unknownIndex = pathLength <= pathLength0 ? 1 : 1 + pathLength - pathLength0; assert(objc > unknownIndex); INCR_REF_COUNT(callInfoObj); Tcl_ListObjAppendList(interp, callInfoObj, pathObj); Tcl_ListObjAppendElement(interp, callInfoObj, objv[unknownIndex]); /* fprintf(stderr, "DispatchUnknownMethod is called with callinfo <%s> (callerSelf <%s>, methodName '%s', methodPath '%s')\n", ObjStr(callInfoObj), ObjStr(callerSelf->cmdName), MethodName(objv[0]), ObjStr(callInfoObj)); */ result = DispatchUnknownMethod(interp, invokedObject, objc-1, objv+1, callInfoObj, objv[1], NSF_CM_NO_OBJECT_METHOD|NSF_CSC_IMMEDIATE); DECR_REF_COUNT(callInfoObj); } } Nsf_PopFrameCsc(interp, framePtr); return result; } #if !defined(NSF_ASSEMBLE) static int NsfAsmProc(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { return TCL_OK; } #endif /* *---------------------------------------------------------------------- * CheckCStack -- * * Monitor the growth of the C Stack when complied with * NSF_STACKCHECK. * * Results: * none * * Side effects: * update of rst->bottomOfStack * *---------------------------------------------------------------------- */ #if defined(NSF_STACKCHECK) NSF_INLINE static void CheckCStack(Tcl_Interp *interp, CONST char *prefix, CONST char *fullMethodName) nonnull(1) nonnull(2) nonnull(3); NSF_INLINE static void CheckCStack(Tcl_Interp *interp, CONST char *prefix, CONST char *fullMethodName) { int somevar; NsfRuntimeState *rst = RUNTIME_STATE(interp); assert(interp); assert(prefix); assert(fullMethodName); if (rst->exitHandlerDestroyRound == NSF_EXITHANDLER_OFF) { # if TCL_STACK_GROWS_UP if ((void *)&somevar < rst->bottomOfStack) { NsfLog(interp, NSF_LOG_WARN, "Stack adjust bottom %ld - %s %s", (void *)&somevar - rst->bottomOfStack, prefix, fullMethodName); rst->bottomOfStack = (void *)&somevar; } else if ((void *)&somevar > rst->maxStack) { NsfLog(interp, NSF_LOG_WARN, "Stack adjust top %ld - %s %s", (void *)&somevar - rst->bottomOfStack, prefix, fullMethodName); rst->maxStack = (void *)&somevar; } # else if ((void *)&somevar > rst->bottomOfStack) { NsfLog(interp, NSF_LOG_WARN, "Stack adjust bottom %ld - %s %s", rst->bottomOfStack - (void *)&somevar, prefix, fullMethodName); rst->bottomOfStack = (void *)&somevar; } else if ((void *)&somevar < rst->maxStack) { NsfLog(interp, NSF_LOG_WARN, "Stack adjust top %ld - %s %s", rst->bottomOfStack - (void *)&somevar, prefix, fullMethodName); rst->maxStack = (void *)&somevar; } # endif } } #else # define CheckCStack(interp, prefix, methodName) #endif /* *---------------------------------------------------------------------- * MethodDispatchCsc -- * * Dispatch a method (scripted or cmd) with an already allocated * call stack content. The method calls either ProcMethodDispatch() * (for scripted methods) or CmdMethodDispatch() (otherwise). * * Results: * Tcl result code. * * Side effects: * Indirect effects by calling methods * *---------------------------------------------------------------------- */ static int MethodDispatchCsc(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Command cmd, NsfCallStackContent *cscPtr, CONST char *methodName, int *validCscPtr) nonnull(1) nonnull(2) nonnull(4) nonnull(5) nonnull(6) nonnull(7) nonnull(8); static int MethodDispatchCsc(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Command cmd, NsfCallStackContent *cscPtr, CONST char *methodName, int *validCscPtr) { NsfObject *object = cscPtr->self; ClientData cp = Tcl_Command_objClientData(cmd); Tcl_ObjCmdProc *proc = Tcl_Command_objProc(cmd); NsfCallStackContent *cscPtr1; assert(clientData); assert(interp); assert(objv); assert(cmd); assert(cscPtr); assert(methodName); assert(validCscPtr); /* * Privide DTrace with calling info */ if (NSF_DTRACE_METHOD_ENTRY_ENABLED()) { NSF_DTRACE_METHOD_ENTRY(ObjectName(object), cscPtr->cl ? ClassName(cscPtr->cl) : ObjectName(object), (char *)methodName, objc-1, (Tcl_Obj **)objv+1); } /*fprintf(stderr, "MethodDispatch method '%s' cmd %p %s clientData %p cp=%p objc=%d cscPtr %p csc->flags %.6x \n", methodName, cmd, Tcl_GetCommandName(interp, cmd), clientData, cp, objc, cscPtr, cscPtr->flags);*/ /*fprintf(stderr, "MethodDispatch method '%s' cmd %p cp=%p objc=%d cscPtr %p csc->flags %.6x " "obj->flags %.6x teardown %p\n", methodName, cmd, cp, objc, cscPtr, cscPtr->flags, object->flags, object->teardown);*/ assert(object->teardown); /* * The default assumption is that the CscPtr is valid after this function * finishes. */ if (likely(proc == TclObjInterpProc)) { int result; #if defined(NRE) NRE_callback *rootPtr = TOP_CB(interp); int isImmediate = (cscPtr->flags & NSF_CSC_IMMEDIATE); # if defined(NRE_CALLBACK_TRACE) NsfClass *cl = cscPtr->cl; # endif #endif /* * The cmd is a scripted method */ result = ProcMethodDispatch(cp, interp, objc, objv, methodName, object, cscPtr->cl, cmd, cscPtr); #if defined(NRE) /* * In the NRE case, there is no trust in the cscPtr anymore, it might be already gone. */ *validCscPtr = 0; if (unlikely(isImmediate)) { # if defined(NRE_CALLBACK_TRACE) fprintf(stderr, ".... manual run callbacks rootPtr = %p, result %d methodName %s.%s\n", rootPtr, result, cl?ClassName(cl):"NULL", methodName); # endif result = NsfNRRunCallbacks(interp, result, rootPtr); } else { # if defined(NRE_CALLBACK_TRACE) fprintf(stderr, ".... don't run callbacks rootPtr = %p, result %d methodName %s.%s\n", rootPtr, result, cl?ClassName(cl):"NULL", methodName); # endif } #endif /* * scripted method done */ return result; } else if (proc == NsfObjDispatch) { assert(cp); return ObjectCmdMethodDispatch((NsfObject *)cp, interp, objc, objv, methodName, object, cscPtr); } else if (cp) { cscPtr1 = cscPtr; /*fprintf(stderr, "cscPtr %p cmd %p %s wanna stack cmd %p %s cp %p no-leaf %d force frame %d\n", cscPtr, cmd, Tcl_GetCommandName(interp, cmd), cmd, Tcl_GetCommandName(interp, cmd), cp, (Tcl_Command_flags(cmd) & NSF_CMD_NONLEAF_METHOD), (cscPtr->flags & NSF_CSC_FORCE_FRAME));*/ /* * The cmd has client data, we check for required updates in this * structure. */ if (proc == NsfForwardMethod || proc == NsfObjscopedMethod || proc == NsfSetterMethod || proc == NsfAsmProc ) { TclCmdClientData *tcd = (TclCmdClientData *)cp; assert(tcd); tcd->object = object; assert((CmdIsProc(cmd) == 0)); } else if (cp == (ClientData)NSF_CMD_NONLEAF_METHOD) { cp = clientData; assert((CmdIsProc(cmd) == 0)); } #if !defined(NDEBUG) else if (proc == NsfProcAliasMethod) { /* This should never happen */ Tcl_Panic("Alias invoked in unexpected way"); } #endif } else if ((Tcl_Command_flags(cmd) & NSF_CMD_NONLEAF_METHOD) || (cscPtr->flags & NSF_CSC_FORCE_FRAME)) { /* * Technically, we would not need a frame to execute the cmd, but maybe, * the user want's it (to be able to call next, or the keep proc-level * variables. The clientData cp is in such cases typically NULL. */ /*fprintf(stderr, "FORCE_FRAME\n");*/ cscPtr1 = cscPtr; } else { /* * There is no need to pass a frame. Use the original clientData. */ cscPtr1 = NULL; } if (cscPtr1) { /* * Call with a stack frame. */ /*fprintf(stderr, "cmdMethodDispatch %s.%s, cscPtr %p objflags %.6x\n", ObjectName(object), methodName, cscPtr, object->flags); */ return CmdMethodDispatch(cp, interp, objc, objv, object, cmd, cscPtr1); } else { /* * Call without a stack frame. */ CscListAdd(interp, cscPtr); /*fprintf(stderr, "cmdMethodDispatch %p %s.%s, nothing stacked, objflags %.6x\n", cmd, ObjectName(object), methodName, object->flags); */ return CmdMethodDispatch(clientData, interp, objc, objv, object, cmd, NULL); } } /* *---------------------------------------------------------------------- * MethodDispatch -- * * Convenience wrapper for MethodDispatchCsc(). It allocates a call * stack content and invokes MethodDispatchCsc. * * Results: * Tcl result code. * * Side effects: * Indirect effects by calling methods * *---------------------------------------------------------------------- */ static int MethodDispatch(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Command cmd, NsfObject *object, NsfClass *cl, CONST char *methodName, int frameType, unsigned int flags) { NsfCallStackContent csc, *cscPtr; int result, validCscPtr = 1; Tcl_Command resolvedCmd; assert(clientData); assert(interp); assert(objv); assert(cmd); assert(object); assert(methodName); assert(object->teardown); CheckCStack(interp, "method", methodName); /*fprintf(stderr, "MethodDispatch method '%s.%s' objc %d flags %.6x\n", ObjectName(object), methodName, objc, flags); */ resolvedCmd = AliasDereference(interp, object, methodName, cmd); if (unlikely(resolvedCmd == NULL)) { return TCL_ERROR; } /* * cscAlloc uses for resolvedCmd for allocating the call stack content and * sets the IS_NRE flag based on it. We use the original cmd in the * callstack content structure for introspection. */ cscPtr = CscAlloc(interp, &csc, resolvedCmd); /* * We would not need CscInit when cp (clientData) == NULL && * !(Tcl_Command_flags(cmd) & NSF_CMD_NONLEAF_METHOD) TODO: We could pass * cmd==NULL, but is this worth it? */ CscInit(cscPtr, object, cl, cmd, frameType, flags, methodName); result = MethodDispatchCsc(object, interp, objc, objv, resolvedCmd, cscPtr, methodName, &validCscPtr); #if defined(NRE) if (validCscPtr) { CscListRemove(interp, cscPtr, NULL); CscFinish(interp, cscPtr, result, "csc cleanup"); } #else CscListRemove(interp, cscPtr, NULL); CscFinish(interp, cscPtr, result, "csc cleanup"); #endif return result; } /* *---------------------------------------------------------------------- * ObjectDispatchFinalize -- * * Finalization function for ObjectDispatch() which performs method * lookup and call all kind of methods. The function runs after * ObjectDispatch() and calls the unknown handler if necessary and * resets the filter and mixin stacks. * * Results: * Tcl result code. * * Side effects: * Maybe side effects by the cmd called by ParameterCheck() * or DispatchUnknownMethod() * *---------------------------------------------------------------------- */ NSF_INLINE static int ObjectDispatchFinalize(Tcl_Interp *interp, NsfCallStackContent *cscPtr, int result /*, char *msg, CONST char *methodName*/) nonnull(1) nonnull(2); NSF_INLINE static int ObjectDispatchFinalize(Tcl_Interp *interp, NsfCallStackContent *cscPtr, int result /*, char *msg, CONST char *methodName*/) { NsfRuntimeState *rst = RUNTIME_STATE(interp); NsfObject *object; unsigned int flags; assert(interp); assert(cscPtr); object = cscPtr->self; assert(object); assert(object->id); flags = cscPtr->flags; /*fprintf(stderr, "ObjectDispatchFinalize %p %s flags %.6x (%d) frame %.6x unk %d m %s\n", cscPtr, ObjectName(object), flags, result, cscPtr->frameType, RUNTIME_STATE(interp)->unknown, cscPtr->cmdPtr ? Tcl_GetCommandName(interp, cscPtr->cmdPtr) : "");*/ /* * Check the return value if wanted */ if (likely(result == TCL_OK && cscPtr->cmdPtr && Tcl_Command_cmdEpoch(cscPtr->cmdPtr) == 0)) { NsfParamDefs *paramDefs = ParamDefsGet(cscPtr->cmdPtr, NULL); if (paramDefs && paramDefs->returns) { Tcl_Obj *valueObj = Tcl_GetObjResult(interp); result = ParameterCheck(interp, paramDefs->returns, valueObj, "return-value:", rst->doCheckResults, 0, 0, NULL); } } else { /*fprintf(stderr, "We have no cmdPtr in cscPtr %p %s", cscPtr, ObjectName(object)); fprintf(stderr, "... cannot check return values!\n");*/ } /* * On success (no error occurred) check for unknown cases. */ if (likely(result == TCL_OK)) { if (unlikely((flags & NSF_CSC_METHOD_IS_UNKNOWN) || ((cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_FILTER) && rst->unknown) )) { result = DispatchUnknownMethod(interp, object, cscPtr->objc, cscPtr->objv, NULL, cscPtr->objv[0], (cscPtr->flags & NSF_CSC_CALL_NO_UNKNOWN)|NSF_CSC_IMMEDIATE); } } /* * Resetting mixin and filter stacks */ if (unlikely(flags & NSF_CSC_MIXIN_STACK_PUSHED) && object->mixinStack) { /* fprintf(stderr, "MixinStackPop %s.%s %p %s\n", ObjectName(object), methodName, object->mixinStack, msg);*/ MixinStackPop(object); } if (unlikely(flags & NSF_CSC_FILTER_STACK_PUSHED) && object->filterStack) { /* fprintf(stderr, "FilterStackPop %s.%s %p %s\n", ObjectName(object), methodName, object->filterStack, msg);*/ FilterStackPop(object); } return result; } /*#define INHERIT_CLASS_METHODS 1*/ #if defined(INHERIT_CLASS_METHODS) static Tcl_Command NsfFindClassMethod(Tcl_Interp *interp, NsfClass *cl, CONST char *methodName) nonnull(1) nonnull(2) nonnull(3); static Tcl_Command NsfFindClassMethod(Tcl_Interp *interp, NsfClass *cl, CONST char *methodName) { Tcl_Command cmd; NsfClasses *p; assert(interp); assert(cl); assert(methodName); /*fprintf(stderr, "NsfFindClassMethod %s %s\n", ClassName(cl), methodName);*/ for(p = PrecedenceOrder(cl); p; p = p->nextPtr) { NsfClass *currentClass = p->cl; Tcl_Namespace *nsPtr = currentClass->object.nsPtr; /*fprintf(stderr, "1 check for obj ns in class %s => %p\n", ClassName(currentClass), nsPtr);*/ if (nsPtr) { cmd = FindMethod(nsPtr, methodName); /*fprintf(stderr, "1 lookup for method %s in class %s => %p\n", methodName, ClassName(currentClass), cmd);*/ if (cmd) {return cmd;} } } return NULL; } #endif /* *---------------------------------------------------------------------- * ObjectDispatch -- * * This function performs the method lookup and call all kind of * methods. It checks, whether a filter or mixin has to be * applied. in these cases, the effective method lookup is * performed by next. * * Results: * Tcl result code. * * Side effects: * Maybe side effects by the cmd called by ParameterCheck() * or DispatchUnknownMethod() * *---------------------------------------------------------------------- */ NSF_INLINE static int ObjectDispatch(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], unsigned int flags) nonnull(2) nonnull(4); NSF_INLINE static int ObjectDispatch(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], unsigned int flags) { register NsfObject *object = (NsfObject *)clientData; int result = TCL_OK, objflags, shift, frameType = NSF_CSC_TYPE_PLAIN; CONST char *methodName; NsfObject *calledObject; NsfClass *cl = NULL; Tcl_Command cmd = NULL; Tcl_Obj *cmdName = object->cmdName, *methodObj; NsfCallStackContent csc, *cscPtr = NULL; int validCscPtr = 1; NsfRuntimeState *rst = RUNTIME_STATE(interp); assert(interp); assert(objv); /* none of the higher copy-flags must be passed */ assert((flags & (NSF_CSC_COPY_FLAGS & 0x000FFF000U)) == 0); if (unlikely(flags & NSF_CM_NO_SHIFT)) { shift = 0; methodObj = objv[0]; methodName = MethodName(methodObj); } else { assert(objc > 1); shift = 1; methodObj = objv[1]; methodName = ObjStr(methodObj); if (unlikely(FOR_COLON_RESOLVER(methodName))) { return NsfPrintError(interp, "%s: method name '%s' must not start with a colon", ObjectName(object), methodName); } } assert(object->teardown); #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "method %p/%d '%s' type %p <%s>\n", methodObj, methodObj->refCount, methodName, methodObj->typePtr, methodObj->typePtr ? methodObj->typePtr->name : ""); #endif /*fprintf(stderr, "ObjectDispatch obj = %s objc = %d 0=%s methodName=%s shift %d\n", object ? ObjectName(object) : NULL, objc, objv[0] ? ObjStr(objv[0]) : NULL, methodName, shift);*/ objflags = object->flags; /* avoid stalling */ /* * Make sure, cmdName and obj survive this method until the end of * this function. */ INCR_REF_COUNT(cmdName); NsfObjectRefCountIncr(object); /*fprintf(stderr, "obj refCount of %p after incr %d (ObjectDispatch) %s\n", object, object->refCount, methodName);*/ if (unlikely((objflags & NSF_FILTER_ORDER_VALID) == 0)) { FilterComputeDefined(interp, object); objflags = object->flags; } if (unlikely((objflags & NSF_MIXIN_ORDER_VALID) == 0)) { MixinComputeDefined(interp, object); objflags = object->flags; } /* * Only start new filter chain, if * (a) filters are defined and * (b) the toplevel csc entry is not an filter on self */ /*fprintf(stderr, "call %s, objflags %.6x, defined and valid %.6x doFilters %d guard count %d\n", methodName, objflags, NSF_FILTER_ORDER_DEFINED_AND_VALID, rst->doFilters, rst->guardCount);*/ assert((flags & (NSF_CSC_MIXIN_STACK_PUSHED|NSF_CSC_FILTER_STACK_PUSHED)) == 0); if (unlikely((objflags & NSF_FILTER_ORDER_DEFINED_AND_VALID) == NSF_FILTER_ORDER_DEFINED_AND_VALID)) { if (rst->doFilters && !rst->guardCount) { NsfCallStackContent *cscPtr1 = CallStackGetTopFrame0(interp); if (cscPtr1 == NULL || (object != cscPtr1->self || (cscPtr1->frameType != NSF_CSC_TYPE_ACTIVE_FILTER))) { FilterStackPush(object, methodObj); flags |= NSF_CSC_FILTER_STACK_PUSHED; cmd = FilterSearchProc(interp, object, &object->filterStack->currentCmdPtr, &cl); if (cmd) { /*fprintf(stderr, "*** filterSearchProc returned cmd %p\n", cmd);*/ frameType = NSF_CSC_TYPE_ACTIVE_FILTER; methodName = (char *)Tcl_GetCommandName(interp, cmd); flags |= NSF_CM_IGNORE_PERMISSIONS; } } } } if (unlikely(cmd == NULL && (flags & NSF_CM_LOCAL_METHOD))) { /* * We require a local method. If the local method is found, we set always * the cmd and sometimes the class (if it is a class specific method). */ NsfCallStackContent *cscPtr1 = CallStackGetTopFrame0(interp); if (unlikely(cscPtr1 == NULL)) { return NsfPrintError(interp, "flag '-local' only allowed when called from a method body"); } if (cscPtr1->cl) { cmd = FindMethod(cscPtr1->cl->nsPtr, methodName); if (cmd) { cl = cscPtr1->cl; } } else if (object->nsPtr) { cmd = FindMethod(object->nsPtr, methodName); } /*fprintf(stderr, "ObjectDispatch NSF_CM_LOCAL_METHOD obj %s methodName %s => cl %p %s cmd %p \n", object ? ObjectName(object) : NULL, methodName, cl, cl ? ClassName(cl) : "NONE", cmd);*/ } else if (unlikely(*methodName == ':')) { NsfObject *regObject; int fromClassNS = 0; /* * We have fully qualified name provided. Determine the class and/or * object on which the method was registered. */ INCR_REF_COUNT(methodObj); cmd = ResolveMethodName(interp, NULL, methodObj, NULL, ®Object, NULL, NULL, &fromClassNS); DECR_REF_COUNT(methodObj); if (likely(cmd != NULL)) { if (CmdIsNsfObject(cmd)) { /* * Don't allow for calling objects as methods via fully qualified * names. Otherwise, in line [2] below, ::State (or any children of * it, e.g., ::Slot::child) is interpreted as a method candidate. As a * result, dispatch chaining occurs with ::State or ::State::child * being the receiver (instead of Class) of the method call * "-parameter". In such a dispatch chaining, the method "unknown" * won't be called on Class (in the XOTcl tradition), effectively * bypassing any unknown-based indirection mechanism (e.g., XOTcl's short-cutting * of object/class creations). * * [1] Class ::State; Class ::State::child * [2] Class ::State -parameter x; Class ::State::child -parameter x */ NsfLog(interp, NSF_LOG_NOTICE, "Don't invoke object %s this way. Register object via alias ...", methodName); cmd = NULL; } else { if (regObject) { if (NsfObjectIsClass(regObject)) { cl = (NsfClass *)regObject; } } /* fprintf(stderr, "fully qualified lookup of %s returned %p\n", ObjStr(methodObj), cmd); */ /* ignore permissions for fully qualified method names */ flags |= NSF_CM_IGNORE_PERMISSIONS; } /*fprintf(stderr, "ObjectDispatch fully qualified obj %s methodName %s => cl %p cmd %p \n", object ? ObjectName(object) : NULL, methodName, cl, cmd);*/ } } /*fprintf(stderr, "MixinStackPush check for %p %s.%s objflags %.6x == %d\n", object, ObjectName(object), methodName, objflags & NSF_MIXIN_ORDER_DEFINED_AND_VALID, (objflags & NSF_MIXIN_ORDER_DEFINED_AND_VALID) == NSF_MIXIN_ORDER_DEFINED_AND_VALID);*/ /* * Check if a mixed in method has to be called. */ if (unlikely((objflags & NSF_MIXIN_ORDER_DEFINED_AND_VALID) == NSF_MIXIN_ORDER_DEFINED_AND_VALID && (flags & (NSF_CM_SYSTEM_METHOD|NSF_CM_INTRINSIC_METHOD)) == 0 && ((flags & NSF_CM_LOCAL_METHOD) == 0 || cl))) { /* * The current logic allocates first an entry on the per-object * stack and searches then for a mixin. This could be improved by * allocating a stack entry just when an mixin is found. The same * holds for the filters above, but there, the hit-rate is much * larger. */ MixinStackPush(object); flags |= NSF_CSC_MIXIN_STACK_PUSHED; if (frameType != NSF_CSC_TYPE_ACTIVE_FILTER) { Tcl_Command cmd1 = cmd; /* * The entry is just searched and pushed on the stack when we * have no filter; in the filter case, the search happens in * next. */ result = MixinSearchProc(interp, object, methodName, methodObj, &cl, &object->mixinStack->currentCmdPtr, &cmd1); if (unlikely(result != TCL_OK)) { /*fprintf(stderr, "mixinsearch returned an error for %p %s.%s\n", object, ObjectName(object), methodName);*/ validCscPtr = 0; goto exit_object_dispatch; } if (cmd1) { frameType = NSF_CSC_TYPE_ACTIVE_MIXIN; cmd = cmd1; } } } /* * If no fully qualified method name/filter/mixin was found then perform * ordinary method lookup. First, try to resolve the method name as a * per-object method. */ if (likely(cmd == NULL)) { NsfMethodContext *mcPtr = methodObj->internalRep.twoPtrValue.ptr1; int nsfObjectMethodEpoch = rst->objectMethodEpoch; if (methodObj->typePtr == &NsfObjectMethodObjType && mcPtr->context == object && mcPtr->methodEpoch == nsfObjectMethodEpoch && mcPtr->flags == flags ) { cmd = mcPtr->cmd; #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "... use internal rep method %p %s cmd %p (objProc %p) cl %p %s\n", methodObj, ObjStr(methodObj), cmd, cmd ? ((Command *)cmd)->objProc : 0, cl, cl ? ClassName(cl) : ObjectName(object)); #endif assert(cmd ? ((Command *)cmd)->objProc != NULL : 1); } else { /* * Do we have an object-specific cmd? */ if (unlikely(object->nsPtr && (flags & (NSF_CM_NO_OBJECT_METHOD|NSF_CM_SYSTEM_METHOD)) == 0)) { cmd = FindMethod(object->nsPtr, methodName); /*fprintf(stderr, "lookup for per-object method in obj %p method %s nsPtr %p" " => %p objProc %p\n", object, methodName, object->nsPtr, cmd, cmd ? ((Command *)cmd)->objProc : NULL);*/ if (cmd) { /* * Reject call when * a) trying to call a private method without the local flag or ignore permssions, or * b) trying to call an object with no method interface */ if (((flags & (NSF_CM_LOCAL_METHOD|NSF_CM_IGNORE_PERMISSIONS)) == 0 && (Tcl_Command_flags(cmd) & NSF_CMD_CALL_PRIVATE_METHOD)) ) { cmd = NULL; } else { NsfMethodObjSet(interp, methodObj, &NsfObjectMethodObjType, object, nsfObjectMethodEpoch, cmd, NULL, flags); } } } } #if defined(INHERIT_CLASS_METHODS) /* this is not optimized yet, since current class might be checked twice, but easier to maintain */ if ((flags & NSF_CM_NO_OBJECT_METHOD) == 0 && cmd == NULL && NsfObjectIsClass(object)) { cmd = NsfFindClassMethod(interp, (NsfClass *)object, methodName); } #endif if (likely(cmd == NULL)) { /* check for an instance method */ NsfClass *currentClass = object->cl; NsfMethodContext *mcPtr = methodObj->internalRep.twoPtrValue.ptr1; int nsfInstanceMethodEpoch = rst->instanceMethodEpoch; #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "... method %p/%d '%s' type? %d context? %d nsfMethodEpoch %d => %d\n", methodObj, methodObj->refCount, ObjStr(methodObj), methodObj->typePtr == &NsfInstanceMethodObjType, methodObj->typePtr == &NsfInstanceMethodObjType ? mcPtr->context == currentClass : 0, methodObj->typePtr == &NsfInstanceMethodObjType ? mcPtr->methodEpoch : 0, nsfInstanceMethodEpoch ); #endif if (methodObj->typePtr == &NsfInstanceMethodObjType && mcPtr->context == currentClass && mcPtr->methodEpoch == nsfInstanceMethodEpoch && mcPtr->flags == flags ) { cmd = mcPtr->cmd; cl = mcPtr->cl; #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "... use internal rep method %p %s cmd %p (objProc %p) cl %p %s\n", methodObj, ObjStr(methodObj), cmd, cmd?((Command *)cmd)->objProc : NULL, cl, cl ? ClassName(cl) : ObjectName(object)); #endif assert(cmd ? ((Command *)cmd)->objProc != NULL : 1); } else { /* * We could call PrecedenceOrder(currentClass) to recompute * currentClass->order on demand, but by construction this is already * set here. */ assert(currentClass->order); if (unlikely(flags & NSF_CM_SYSTEM_METHOD)) { NsfClasses *classList = currentClass->order; /* * Skip entries until the first base class. */ for (; classList; classList = classList->nextPtr) { if (IsBaseClass(&classList->cl->object)) {break;} } cl = SearchPLMethod(classList, methodName, &cmd, NSF_CMD_CALL_PRIVATE_METHOD); } else { cl = SearchPLMethod(currentClass->order, methodName, &cmd, NSF_CMD_CALL_PRIVATE_METHOD); } if (methodObj->typePtr != Nsf_OT_tclCmdNameType && methodObj->typePtr != Nsf_OT_parsedVarNameType ) { NsfMethodObjSet(interp, methodObj, &NsfInstanceMethodObjType, currentClass, nsfInstanceMethodEpoch, cmd, cl, flags); } } } } calledObject = object; /* * If we have a command, check the permissions, unless * NSF_CM_IGNORE_PERMISSIONS is set. Note, that NSF_CM_IGNORE_PERMISSIONS is * set currently for fully qualified cmd names and in nsf::object::dispatch. */ if (likely(cmd && (flags & NSF_CM_IGNORE_PERMISSIONS) == 0)) { int cmdFlags = Tcl_Command_flags(cmd); #if !defined(NDEBUG) if (unlikely((cmdFlags & NSF_CMD_CALL_PRIVATE_METHOD) && ((flags & NSF_CM_LOCAL_METHOD) == 0)) ) { /* * Private methods can be only called with the "-local" flag. All cases * handling private methods should be covered above (e.g. by setting * NSF_CM_IGNORE_PERMISSIONS, or by filtering private methods in method * search. So, this branch should never by executed. */ Tcl_Panic("Unexpected handling of private method; most likely a caching bug"); cmd = NULL; } else #endif if (unlikely(cmdFlags & NSF_CMD_CALL_PROTECTED_METHOD)) { NsfObject *lastSelf = GetSelfObj(interp); /* * Protected methods can be called, when calling object == called object. */ if (unlikely(object != lastSelf)) { NsfLog(interp, NSF_LOG_WARN, "'%s %s' fails since method %s.%s is protected", ObjectName(object), methodName, cl ? ClassName(cl) : ObjectName(object), methodName); /* reset cmd, since it is still unknown */ cmd = NULL; } } } assert(result == TCL_OK); if (likely(cmd != NULL)) { /* * We found the method to dispatch. */ Tcl_Command resolvedCmd = AliasDereference(interp, object, methodName, cmd); if (unlikely(resolvedCmd == NULL)) { validCscPtr = 0; goto exit_object_dispatch; } /* * cscAlloc uses for resolvedCmd for allocating the call stack content and * sets the IS_NRE flag based on it. We use the original cmd in the * callstack content structure for introspection. */ cscPtr = CscAlloc(interp, &csc, resolvedCmd); CscInit(cscPtr, calledObject, cl, cmd, frameType, flags, methodName); if (unlikely(cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_FILTER)) { /* run filters is not NRE enabled */ cscPtr->flags |= NSF_CSC_IMMEDIATE; /* * Setting cscPtr->objc and cscPtr->objv is needed for invoking UNKNOWN * from ProcMethodDispatchFinalize() */ cscPtr->objc = objc-shift; cscPtr->objv = objv+shift; } /*fprintf(stderr, "MethodDispatchCsc %s.%s %p flags %.6x cscPtr %p\n", ObjectName(object), methodName, object->mixinStack, cscPtr->flags, cscPtr);*/ result = MethodDispatchCsc(clientData, interp, objc-shift, objv+shift, resolvedCmd, cscPtr, methodName, &validCscPtr); if (unlikely(result == TCL_ERROR)) { /*fprintf(stderr, "Call ErrInProc cl = %p, cmd %p, methodName %s flags %.6x\n", cl, cl ? cl->object.id : NULL, methodName, cl ? cl->object.flags : 0);*/ result = NsfErrInProc(interp, cmdName, cl && cl->object.teardown ? cl->object.cmdName : NULL, methodName); } } else { /* * The method to be dispatched is unknown */ cscPtr = CscAlloc(interp, &csc, cmd); CscInit(cscPtr, object, cl, cmd, frameType, flags, methodName); cscPtr->flags |= NSF_CSC_METHOD_IS_UNKNOWN; if ((flags & NSF_CM_NO_UNKNOWN)) { cscPtr->flags |= NSF_CSC_CALL_NO_UNKNOWN; } cscPtr->objc = objc-shift; cscPtr->objv = objv+shift; } exit_object_dispatch: if (likely(validCscPtr)) { /* * In every situation, we have a cscPtr containing all context information */ assert(cscPtr); result = ObjectDispatchFinalize(interp, cscPtr, result /*, "immediate" , methodName*/); CscListRemove(interp, cscPtr, NULL); CscFinish(interp, cscPtr, result, "non-scripted finalize"); } /*fprintf(stderr, "ObjectDispatch %s.%s returns %d\n", ObjectName(object), methodName, result);*/ NsfCleanupObject(object, "ObjectDispatch"); /*fprintf(stderr, "ObjectDispatch call NsfCleanupObject %p DONE\n", object);*/ DECR_REF_COUNT(cmdName); /* must be after last dereferencing of obj */ return result; } /* *---------------------------------------------------------------------- * DispatchDefaultMethod -- * * Dispatch the default method (when object is called without arguments) * in case the object system has it defined. * * Results: * result code. * * Side effects: * indirect effects by calling Tcl code * *---------------------------------------------------------------------- */ static int DispatchDefaultMethod(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *obj, unsigned int flags) { int result; Tcl_Obj *methodObj; assert(interp); assert(object); assert(obj); if (CallDirectly(interp, object, NSF_o_defaultmethod_idx, &methodObj)) { Tcl_SetObjResult(interp, object->cmdName); result = TCL_OK; } else { Tcl_Obj *tov[2]; tov[0] = obj; tov[1] = methodObj; result = ObjectDispatch(object, interp, 2, tov, flags|NSF_CM_NO_UNKNOWN|NSF_CM_IGNORE_PERMISSIONS); } return result; } /* *---------------------------------------------------------------------- * DispatchDestroyMethod -- * * Dispatch the method "destroy" in case the object system has it * defined. During the final cleanup of the object system, the * destroy is called separately from deallocation. Normally, * Object.destroy() calls dealloc, which is responsible for the * physical deallocation. * * Results: * result code * * Side effects: * indirect effects by calling Tcl code * *---------------------------------------------------------------------- */ static int DispatchDestroyMethod(Tcl_Interp *interp, NsfObject *object, unsigned int flags) { int result; Tcl_Obj *methodObj; NsfRuntimeState *rst = RUNTIME_STATE(interp); assert(interp); assert(object); /* * Don't call destroy after exit handler started physical * destruction, or when it was called already before */ if (rst->exitHandlerDestroyRound == NSF_EXITHANDLER_ON_PHYSICAL_DESTROY || (object->flags & NSF_DESTROY_CALLED) ) { return TCL_OK; } /*fprintf(stderr, " DispatchDestroyMethod obj %p flags %.6x active %d\n", object, object->flags, object->activationCount); */ PRINTOBJ("DispatchDestroyMethod", object); /* flag, that destroy was called and invoke the method */ object->flags |= NSF_DESTROY_CALLED; if (CallDirectly(interp, object, NSF_o_destroy_idx, &methodObj)) { result = NsfODestroyMethod(interp, object); } else { result = CallMethod(object, interp, methodObj, 2, NULL, NSF_CM_IGNORE_PERMISSIONS|NSF_CSC_IMMEDIATE|flags); } if (unlikely(result != TCL_OK)) { /* * The object might be already gone here, since we have no stack frame. * Therefore, we can't even use nsf::current object safely. */ NsfErrorContext(interp, "method destroy"); if (++rst->errorCount > 20) { Tcl_Panic("too many destroy errors occurred. Endless loop?"); } } else if (rst->errorCount > 0) { rst->errorCount--; } #ifdef OBJDELETION_TRACE fprintf(stderr, "DispatchDestroyMethod for %p exit\n", object); #endif return result; } /* *---------------------------------------------------------------------- * DispatchInitMethod -- * in case the object system has it * defined and it was not already called on the object, * * Results: * Result code. * * Side effects: * Indirect effects by calling Tcl code * *---------------------------------------------------------------------- */ static int DispatchInitMethod(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[], unsigned int flags) nonnull(1) nonnull(2); static int DispatchInitMethod(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[], unsigned int flags) { int result; Tcl_Obj *methodObj; assert(interp); assert(object); /* * check, whether init was called already */ if (!(object->flags & (NSF_INIT_CALLED|NSF_DESTROY_CALLED))) { /* * Flag the call to "init" before the dispatch, such that a call to * "configure" within init does not clear the already set instance * variables. */ object->flags |= NSF_INIT_CALLED; if (CallDirectly(interp, object, NSF_o_init_idx, &methodObj)) { /*fprintf(stderr, "%s init directly\n", ObjectName(object));*/ /* * Actually, nothing to do. */ result = TCL_OK; } else { result = CallMethod(object, interp, methodObj, objc+2, objv, flags|NSF_CM_IGNORE_PERMISSIONS|NSF_CSC_IMMEDIATE); } } else { result = TCL_OK; } return result; } /* *---------------------------------------------------------------------- * DispatchUnknownMethod -- * * Dispatch the method "unknown" in case the object system has it * defined and the application program contains an unknown handler. * * Results: * result code * * Side effects: * There might be indirect effects by calling Tcl code; also, * the interp's unknown-state is reset. * *---------------------------------------------------------------------- */ static int DispatchUnknownMethod(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *callInfoObj, Tcl_Obj *methodObj, unsigned int flags) { int result; Tcl_Obj *unknownObj = NsfMethodObj(object, NSF_o_unknown_idx); CONST char *methodName = MethodName(methodObj); NsfRuntimeState *rst = RUNTIME_STATE(interp); assert(interp); assert(object); assert(objv); assert(methodObj); /*fprintf(stderr, "compare unknownObj %p with methodObj %p '%s' %p %p %s -- %s\n", unknownObj, methodObj, ObjStr(methodObj), callInfoObj, callInfoObj?objv[1]:NULL, callInfoObj?ObjStr(objv[1]) : NULL, methodName);*/ if (unknownObj && methodObj != unknownObj && (flags & NSF_CSC_CALL_NO_UNKNOWN) == 0) { /* * back off and try unknown; */ int mustCopy = *(ObjStr(methodObj)) == ':'; ALLOC_ON_STACK(Tcl_Obj*, objc+3, tov); if (callInfoObj == NULL) { callInfoObj = mustCopy ? Tcl_NewStringObj(methodName, -1) : methodObj; } INCR_REF_COUNT(callInfoObj); /*fprintf(stderr, "calling unknown for %s %s, flags=%.6x,%.6x/%.6x isClass=%d %p %s objc %d\n", ObjectName(object), ObjStr(methodObj), flags, NSF_CM_NO_UNKNOWN, NSF_CSC_CALL_NO_UNKNOWN, NsfObjectIsClass(object), object, ObjectName(object), objc);*/ tov[0] = object->cmdName; tov[1] = unknownObj; tov[2] = callInfoObj; if (objc > 1) { memcpy(tov + 3, objv + 1, sizeof(Tcl_Obj *) * (objc - 1)); } flags &= ~NSF_CM_NO_SHIFT; /*fprintf(stderr, "call unknown via dispatch mustCopy %d delegator %p method %s (%s)\n", mustCopy, delegator, ObjStr(tov[offset]), ObjStr(methodObj));*/ result = ObjectDispatch(object, interp, objc+2, tov, flags|NSF_CM_NO_UNKNOWN|NSF_CM_IGNORE_PERMISSIONS); DECR_REF_COUNT(callInfoObj); FREE_ON_STACK(Tcl_Obj*, tov); } else { /* no unknown called, this is the built-in unknown handler */ Tcl_Obj *tailMethodObj = NULL; if (objc > 1 && ((*methodName) == '-' || (unknownObj && objv[0] == unknownObj))) { int length; if (Tcl_ListObjLength(interp, objv[1], &length) == TCL_OK && length > 0) { Tcl_ListObjIndex(interp, objv[1], length - 1, &tailMethodObj); } } result = NsfPrintError(interp, "%s: unable to dispatch method '%s'", ObjectName(object), tailMethodObj ? MethodName(tailMethodObj) : methodName); } /* * Reset interp state, unknown has been fired. */ rst->unknown = 0; return result; } /* *---------------------------------------------------------------------- * NsfObjDispatch -- * * This function is called on every object dispatch (when an object * is invoked). It calls either the passed method, or dispatches * some default method. * * Results: * Tcl result code. * * Side effects: * Maybe side effects by the cmd called by ParameterCheck() * or DispatchUnknownMethod() * *---------------------------------------------------------------------- */ #if defined(NRE) Tcl_ObjCmdProc NsfObjDispatchNRE; int NsfObjDispatch(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { assert(clientData); assert(interp); assert(objv); return Tcl_NRCallObjProc(interp, NsfObjDispatchNRE, clientData, objc, objv); } int NsfObjDispatchNRE(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(4); int NsfObjDispatchNRE(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { #else EXTERN int NsfObjDispatch(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { #endif int result; #ifdef STACK_TRACE NsfStackDump(interp); #endif assert(clientData); assert(interp); assert(objv); if (likely(objc > 1)) { /* * Normal dispatch; we must not use NSF_CSC_IMMEDIATE here, * otherwise coroutines won't work. */ result = ObjectDispatch(clientData, interp, objc, objv, 0); } else { result = DispatchDefaultMethod(interp, (NsfObject *)clientData, objv[0], NSF_CSC_IMMEDIATE); } return result; } /* * Proc-Creation */ /* *---------------------------------------------------------------------- * AddPrefixToBody -- * * Create a fresh TclObj* containing the body with an potential prefix. * The caller has to decrement the ref-count on this Tcl_Obj*. * * Results: * Tcl_Obj * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_Obj * AddPrefixToBody(Tcl_Obj *body, int paramDefs, NsfParsedParam *paramPtr) nonnull(1) nonnull(3); static Tcl_Obj * AddPrefixToBody(Tcl_Obj *body, int paramDefs, NsfParsedParam *paramPtr) { Tcl_Obj *resultBody = Tcl_NewObj(); assert(body); assert(paramPtr); INCR_REF_COUNT2("resultBody", resultBody); if (paramDefs && paramPtr->possibleUnknowns > 0) { Tcl_AppendStringsToObj(resultBody, "::nsf::__unset_unknown_args\n", (char *) NULL); } Tcl_AppendStringsToObj(resultBody, ObjStr(body), (char *) NULL); return resultBody; } NSF_INLINE static int NoMetaChars(CONST char *pattern) nonnull(1); NSF_INLINE static int NoMetaChars(CONST char *pattern) { register char c; assert(pattern); for (c = *pattern; c; c = *++pattern) { if (c == '*' || c == '?' || c == '[') { return 0; } } return 1; } /*********************************************************************** * Nsf_TypeConverter ***********************************************************************/ /* *---------------------------------------------------------------------- * Nsf_ConvertToString -- * * Minimal Nsf_TypeConverter setting the client data (passed to C * functions) to the ObjStr of the object. * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_ConvertToString(Tcl_Interp *UNUSED(interp), Tcl_Obj *objPtr, Nsf_Param CONST *UNUSED(pPtr), ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(2) nonnull(4) nonnull(5); int Nsf_ConvertToString(Tcl_Interp *UNUSED(interp), Tcl_Obj *objPtr, Nsf_Param CONST *UNUSED(pPtr), ClientData *clientData, Tcl_Obj **outObjPtr) { assert(objPtr); assert(clientData); assert(outObjPtr); *clientData = (char *)ObjStr(objPtr); assert(*outObjPtr == objPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * ConvertToNothing -- * * Minimalistic Nsf_TypeConverter, even setting the client data (passed to * C functions). * * Results: * Tcl result code, **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ConvertToNothing(Tcl_Interp *UNUSED(interp), Tcl_Obj *objPtr, Nsf_Param CONST *UNUSED(pPtr), ClientData *UNUSED(clientData), Tcl_Obj **outObjPtr) nonnull(2) nonnull(5); static int ConvertToNothing(Tcl_Interp *UNUSED(interp), Tcl_Obj *objPtr, Nsf_Param CONST *UNUSED(pPtr), ClientData *UNUSED(clientData), Tcl_Obj **outObjPtr) { assert(objPtr); assert(outObjPtr); assert(*outObjPtr == objPtr); *outObjPtr = objPtr; return TCL_OK; } /* *---------------------------------------------------------------------- * Nsf_ConvertToTclobj -- * * Nsf_TypeConverter setting the client data (passed to C functions) to the * passed Tcl_Obj. Optionally this converter checks if the Tcl_Obj has * permissible content via the Tcl "string is" checkers. * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ enum stringTypeIdx {StringTypeAlnum, StringTypeAlpha, StringTypeAscii, StringTypeBoolean, StringTypeControl, StringTypeDigit, StringTypeDouble, StringTypeFalse, StringTypeGraph, StringTypeInteger, StringTypeLower, StringTypePrint, StringTypePunct, StringTypeSpace, StringTypeTrue, StringTypeUpper, StringTypeWideinteger, StringTypeWordchar, StringTypeXdigit }; static CONST char *stringTypeOpts[] = {"alnum", "alpha", "ascii", "boolean", "control", "digit", "double", "false", "graph", "integer", "lower", "print", "punct", "space", "true", "upper", "wideinteger", "wordchar", "xdigit", NULL}; int Nsf_ConvertToTclobj(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); int Nsf_ConvertToTclobj(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int result; assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); if (unlikely(pPtr->converterArg != NULL)) { Tcl_Obj *objv[4]; /*fprintf(stderr, "ConvertToTclobj %s (must be %s)\n", ObjStr(objPtr), ObjStr(pPtr->converterArg));*/ objv[1] = pPtr->converterArg; objv[2] = NsfGlobalObjs[NSF_OPTION_STRICT]; objv[3] = objPtr; result = NsfCallCommand(interp, NSF_STRING_IS, 4, objv); if (likely(result == TCL_OK)) { int success; Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &success); if (success == 1) { *clientData = objPtr; } else { Tcl_ResetResult(interp); result = NsfObjErrType(interp, NULL, objPtr, ObjStr(pPtr->converterArg), (Nsf_Param *)pPtr); } } } else { result = TCL_OK; #if defined(NSF_WITH_VALUE_WARNINGS) if (RUNTIME_STATE(interp)->debugLevel > 0) { char *value = ObjStr(objPtr); if (unlikely(*value == '-' && (pPtr->flags & NSF_ARG_CHECK_NONPOS) && isalpha(*(value+1)) && strchr(value+1, ' ') == NULL) ) { /* * In order to flag a warning, we set the error message and * return TCL_CONTINUE */ (void)NsfPrintError(interp, "value '%s' of parameter '%s' could be a non-positional argument", value, pPtr->name); result = TCL_CONTINUE; } } #endif *clientData = objPtr; } assert(*outObjPtr == objPtr); return result; } /* *---------------------------------------------------------------------- * Nsf_ConvertToBoolean -- * * Nsf_TypeConverter setting the client data (passed to C functions) to the * internal representation of a boolean. This converter checks the passed * value via Tcl_GetBooleanFromObj(). * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_ConvertToBoolean(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); int Nsf_ConvertToBoolean(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int result, bool; result = Tcl_GetBooleanFromObj(interp, objPtr, &bool); assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); if (likely(result == TCL_OK)) { *clientData = (ClientData)INT2PTR(bool); } else { Tcl_ResetResult(interp); NsfObjErrType(interp, NULL, objPtr, "boolean", pPtr); } assert(*outObjPtr == objPtr); return result; } /* *---------------------------------------------------------------------- * Nsf_ConvertToInt32 -- * * Nsf_TypeConverter setting the client data (passed to C functions) to the * internal representation of an integer. This converter checks the passed * value via Tcl_GetIntFromObj(). * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_ConvertToInt32(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); int Nsf_ConvertToInt32(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int result; int i; assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); result = Tcl_GetIntFromObj(interp, objPtr, &i); if (likely(result == TCL_OK)) { *clientData = (ClientData)INT2PTR(i); assert(*outObjPtr == objPtr); } else { Tcl_ResetResult(interp); NsfObjErrType(interp, NULL, objPtr, "int32", (Nsf_Param *)pPtr); } return result; } /* *---------------------------------------------------------------------- * Nsf_ConvertToInteger -- * * Nsf_TypeConverter setting the client data (passed to C functions) to the * TclObj containing the bignum value. This converter checks the passed * value via Tcl_GetBignumFromObj(). * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ #include int Nsf_ConvertToInteger(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); int Nsf_ConvertToInteger(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int result; assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); /* * Try to short_cut common cases to avoid conversion to bignums, since * Tcl_GetBignumFromObj returns a value, which has to be freed. */ if (objPtr->typePtr == Nsf_OT_intType) { /* * We know already, that the value is an int */ result = TCL_OK; } else if (objPtr->typePtr == Nsf_OT_doubleType) { /* * We know already, that the value is not an int */ result = TCL_ERROR; } else { mp_int bignumValue; /* * We have to figure out, whether the value is an int. We perform this * test via Tcl_GetBignumFromObj(), which tries to keep the type small if * possible (e.g. it might return type "int" or "float" when appropriate. */ /*if (objPtr->typePtr) { fprintf(stderr, "type is on call %p %s value %s \n", objPtr->typePtr, objPtr->typePtr? objPtr->typePtr->name:"NULL", ObjStr(objPtr)); }*/ if ((result = Tcl_GetBignumFromObj(interp, objPtr, &bignumValue)) == TCL_OK) { mp_clear(&bignumValue); } } if (likely(result == TCL_OK)) { *clientData = (ClientData)objPtr; assert(*outObjPtr == objPtr); } else { Tcl_ResetResult(interp); NsfObjErrType(interp, NULL, objPtr, "integer", (Nsf_Param *)pPtr); } return result; } /* *---------------------------------------------------------------------- * Nsf_ConvertToSwitch -- * * Nsf_TypeConverter setting the client data (passed to C functions) to the * internal representation of an Boolean. This converter simply calls * Tcl_ConvertToBoolean(). The distinction between "switch" and boolean is * made on the semantics of which arguments/defaults are passed to the real * converter. * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_ConvertToSwitch(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); int Nsf_ConvertToSwitch(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); return Nsf_ConvertToBoolean(interp, objPtr, pPtr, clientData, outObjPtr); } /* *---------------------------------------------------------------------- * Nsf_ConvertToObject -- * * Nsf_TypeConverter setting the client data (passed to C functions) to the * internal representation of an object. This converter checks the passed * value via IsObjectOfType(). * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_ConvertToObject(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); int Nsf_ConvertToObject(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); assert(*outObjPtr == objPtr); if (likely(GetObjectFromObj(interp, objPtr, (NsfObject **)clientData) == TCL_OK)) { return IsObjectOfType(interp, (NsfObject *)*clientData, "object", objPtr, pPtr); } return NsfObjErrType(interp, NULL, objPtr, "object", (Nsf_Param *)pPtr); } /* *---------------------------------------------------------------------- * Nsf_ConvertToClass -- * * Nsf_TypeConverter setting the client data (passed to C functions) to the * internal representation of a class. This converter checks the passed * value via IsObjectOfType(). * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_ConvertToClass(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); int Nsf_ConvertToClass(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); assert(*outObjPtr == objPtr); if (likely(GetClassFromObj(interp, objPtr, (NsfClass **)clientData, 0) == TCL_OK)) { return IsObjectOfType(interp, (NsfObject *)*clientData, "class", objPtr, pPtr); } return NsfObjErrType(interp, NULL, objPtr, "class", (Nsf_Param *)pPtr); } /* *---------------------------------------------------------------------- * Nsf_ConvertToFilterreg -- * * Nsf_TypeConverter setting the client data (passed to C functions) to the * Tcl_Obj. This nsf type converter checks the passed value via the * NsfFilterregObjType tcl_obj converter, which provides an internal * representation for the client function. * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_ConvertToFilterreg(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); int Nsf_ConvertToFilterreg(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int result; assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); assert(*outObjPtr == objPtr); result = Tcl_ConvertToType(interp, objPtr, &NsfFilterregObjType); if (likely(result == TCL_OK)) { *clientData = objPtr; return result; } return NsfObjErrType(interp, NULL, objPtr, "filterreg", (Nsf_Param *)pPtr); } /* *---------------------------------------------------------------------- * Nsf_ConvertToMixinreg -- * * Nsf_TypeConverter setting the client data (passed to C functions) to the * Tcl_Obj. This nsf type converter checks the passed value via the * NsfMixinregObjType tcl_obj converter, which provides an internal * representation for the client function. * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_ConvertToMixinreg(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); int Nsf_ConvertToMixinreg(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int result; assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); assert(*outObjPtr == objPtr); result = Tcl_ConvertToType(interp, objPtr, &NsfMixinregObjType); if (likely(result == TCL_OK)) { *clientData = objPtr; return result; } return NsfObjErrType(interp, NULL, objPtr, "mixinreg", (Nsf_Param *)pPtr); } /* *---------------------------------------------------------------------- * Nsf_ConvertToParameter -- * * Nsf_TypeConverter setting the client data (passed to C functions) to the * Tcl_Obj. This nsf type converter checks if the provided value could be a * valid parameter spec (i.e. start with no ":", is not an unnamed spec * "-:int"). This converter performs just a rough syntactic check. * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_ConvertToParameter(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); int Nsf_ConvertToParameter(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { CONST char *value = ObjStr(objPtr); assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); assert(*outObjPtr == objPtr); /*fprintf(stderr, "convert to parameter '%s' t '%s'\n", value, pPtr->type);*/ if (*value == ':' || (*value == '-' && *(value + 1) == ':')) { return NsfPrintError(interp, "leading colon in '%s' not allowed in parameter specification '%s'", ObjStr(objPtr), pPtr->name); } *clientData = (char *)ObjStr(objPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * ConvertViaCmd -- * * Nsf_TypeConverter calling a used-defined checking/conversion * function. It sets the client data (passed to C functions) to the * Tcl_Obj. * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ConvertViaCmd(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); static int ConvertViaCmd(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { Tcl_Obj *ov[5], *savedResult; NsfObject *object; int result, oc; assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); /* * In general, when the converter is used e.g. for result checking, * we do not want to alter the result just when the converter sets a * result. So, for non-converter, we save the old result and restore * it before the return in case of success. Strictly speaking, * result-overwriting just harms for result-converters, but saving is * always semantically correct. */ if (unlikely((pPtr->flags & NSF_ARG_IS_CONVERTER) == 0)) { savedResult = Tcl_GetObjResult(interp); /* save the result */ INCR_REF_COUNT(savedResult); } else { savedResult = NULL; } ov[0] = pPtr->slotObj ? pPtr->slotObj : NsfGlobalObjs[NSF_METHOD_PARAMETER_SLOT_OBJ]; ov[1] = pPtr->converterName; ov[2] = pPtr->nameObj; ov[3] = objPtr; oc = 4; if (pPtr->converterArg) { ov[4] = pPtr->converterArg; oc++; } /*fprintf(stderr, "ConvertViaCmd call converter %s (refCount %d) on %s paramPtr %p arg %p oc %d\n", ObjStr(pPtr->converterName), pPtr->converterName->refCount, ObjStr(ov[0]), pPtr, pPtr->converterArg, oc);*/ INCR_REF_COUNT(ov[1]); INCR_REF_COUNT(ov[2]); /* result = Tcl_EvalObjv(interp, oc, ov, 0); */ GetObjectFromObj(interp, ov[0], &object); result = ObjectDispatch(object, interp, oc, ov, NSF_CSC_IMMEDIATE|NSF_CM_IGNORE_PERMISSIONS); DECR_REF_COUNT(ov[1]); DECR_REF_COUNT(ov[2]); /* per default, the input arg is the output arg */ assert(*outObjPtr == objPtr); if (likely(result == TCL_OK)) { /*fprintf(stderr, "ConvertViaCmd could convert %s to '%s' paramPtr %p, is_converter %d\n", ObjStr(objPtr), ObjStr(Tcl_GetObjResult(interp)), pPtr, pPtr->flags & NSF_ARG_IS_CONVERTER);*/ if (pPtr->flags & NSF_ARG_IS_CONVERTER) { Tcl_Obj *resultObj; /* * If we want to convert, the resulting obj is the result of the * converter. incr refCount is necessary e.g. for e.g. * * return [expr {$value + 1}] * * The conversion is just needed, when resultObj differs from the actual * value in the output vector. Otherwise the conversion and the value * increment happened already before (and is already recorded in the parse context). */ resultObj = Tcl_GetObjResult(interp); if (*outObjPtr != resultObj) { INCR_REF_COUNT2("valueObj", resultObj); *outObjPtr = resultObj; } /*fprintf(stderr, "**** NSF_ARG_IS_CONVERTER %p\n", *outObjPtr);*/ } *clientData = (ClientData) *outObjPtr; if (savedResult) { /*fprintf(stderr, "restore savedResult %p\n", savedResult);*/ Tcl_SetObjResult(interp, savedResult); /* restore the result */ } } if (savedResult) { DECR_REF_COUNT(savedResult); } return result; } /* *---------------------------------------------------------------------- * ConvertToObjpattern -- * * This function obtains a Tcl_Obj *, which contains the pattern if an Next * Scripting Object. When this pattern contains no meta characters, we * check if the object exists. If it exists, the Tcl_Obj is converted to * the cmd-type. If it does not exit, the function using this pattern will * fail. If the pattern contains meta characters, we prepend to the pattern * "::" if necessary to avoid errors, if one specifies a pattern object * without the prefix. In this case, the patternObj is is of plain type. * The resulting patternObj has always the refCount incremented, which has * to be decremented by the caller.x * * Results: * Tcl result code. * * Side effects: * Incremented refCount for the patternObj. * *---------------------------------------------------------------------- */ static int ConvertToObjpattern(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *UNUSED(pPtr), ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(4) nonnull(5); static int ConvertToObjpattern(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *UNUSED(pPtr), ClientData *clientData, Tcl_Obj **outObjPtr) { Tcl_Obj *patternObj = objPtr; CONST char *pattern = ObjStr(objPtr); assert(interp); assert(objPtr); assert(clientData); assert(outObjPtr); if (NoMetaChars(pattern)) { /* * We have no meta characters, we try to check for an existing object */ NsfObject *object = NULL; GetObjectFromObj(interp, objPtr, &object); if (object) { patternObj = object->cmdName; } } else { /* * We have a pattern and meta characters, we might have * to prefix it to ovoid obvious errors: since all object * names are prefixed with ::, we add this prefix automatically * to the match pattern, if it does not exist. */ if (*pattern != ':' && *pattern+1 != ':') { patternObj = Tcl_NewStringObj("::", 2); Tcl_AppendLimitedToObj(patternObj, pattern, -1, INT_MAX, NULL); } } if (patternObj) { INCR_REF_COUNT2("patternObj", patternObj); } *clientData = (ClientData)patternObj; /* The following assert does not hold here, since we have a direct call to the converter assert(*outObjPtr == objPtr); */ *outObjPtr = objPtr; return TCL_OK; } /* *---------------------------------------------------------------------- * ParamCheckObj -- * * Ths function returns a fresh Tcl_Obj in the form of a method name for a * checker method. * * Results: * Tcl_Obj * * Side effects: * None * *---------------------------------------------------------------------- */ static Tcl_Obj *ParamCheckObj(CONST char *start, size_t len) nonnull(1) returns_nonnull; static Tcl_Obj * ParamCheckObj(CONST char *start, size_t len) { Tcl_Obj *checker = Tcl_NewStringObj("type=", 5); assert(start); Tcl_AppendLimitedToObj(checker, start, len, INT_MAX, NULL); return checker; } /* *---------------------------------------------------------------------- * ParamOptionSetConverter -- * * Fill in the fields int to the specifed paramPtr structure * checker method and perform sanity checking. * * Results: * Standard result code * * Side effects: * None * *---------------------------------------------------------------------- */ static int ParamOptionSetConverter(Tcl_Interp *interp, Nsf_Param *paramPtr, CONST char *typeName, Nsf_TypeConverter *converter) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int ParamOptionSetConverter(Tcl_Interp *interp, Nsf_Param *paramPtr, CONST char *typeName, Nsf_TypeConverter *converter) { assert(interp); assert(paramPtr); assert(typeName); assert(converter); if (paramPtr->converter) { return NsfPrintError(interp, "refuse to redefine parameter type of '%s' from type '%s' to type '%s'", paramPtr->name, paramPtr->type, typeName); } paramPtr->converter = converter; paramPtr->nrArgs = 1; paramPtr->type = typeName; return TCL_OK; } /* *---------------------------------------------------------------------- * Unescape -- * * Unescape double commas in the provided Tcl_Obj. * * Results: * None * * Side effects: * Potentially shortend string content * *---------------------------------------------------------------------- */ static void Unescape(Tcl_Obj *objPtr) nonnull(1); static void Unescape(Tcl_Obj *objPtr) { int i, j, l = Tcl_GetCharLength(objPtr); char *string = ObjStr(objPtr); assert(objPtr); for (i = 0; i < l; i++) { if (string[i] == ',' && string[i+1] == ',') { for (j = i+1; j < l; j++) { string[j] = string[j+1]; } l--; i++; } } Tcl_SetObjLength(objPtr, l); } /* *---------------------------------------------------------------------- * ParamOptionParse -- * * Parse a single parameter option of a parameter. The parameter option * string is passed in as second argument, the sizes start and remainder * flag the offsets in the string. As a result, the fields of the parameter * structure are updated. * * Results: * Tcl result code, updated fields in the Nsf_Param structure. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ParamOptionParse(Tcl_Interp *interp, CONST char *argString, size_t start, size_t optionLength, int disallowedOptions, Nsf_Param *paramPtr, int unescape) nonnull(1) nonnull(2) nonnull(6); static int ParamOptionParse(Tcl_Interp *interp, CONST char *argString, size_t start, size_t optionLength, int disallowedOptions, Nsf_Param *paramPtr, int unescape) { CONST char *dotdot, *option = argString + start; int result = TCL_OK; assert(interp); assert(argString); assert(paramPtr); /*fprintf(stderr, "ParamOptionParse name %s, option '%s' (%ld) disallowed %.6x\n", paramPtr->name, option, start, disallowedOptions);*/ if (strncmp(option, "required", MAX(3, optionLength)) == 0) { paramPtr->flags |= NSF_ARG_REQUIRED; } else if (strncmp(option, "optional", MAX(3, optionLength)) == 0) { paramPtr->flags &= ~NSF_ARG_REQUIRED; } else if (strncmp(option, "substdefault", 12) == 0) { paramPtr->flags |= NSF_ARG_SUBST_DEFAULT; } else if (strncmp(option, "convert", 7) == 0) { paramPtr->flags |= NSF_ARG_IS_CONVERTER; } else if (strncmp(option, "initcmd", 7) == 0) { if (unlikely(paramPtr->flags & (NSF_ARG_CMD|NSF_ARG_ALIAS|NSF_ARG_FORWARD))) { return NsfPrintError(interp, "parameter option 'initcmd' not valid in this option combination"); } paramPtr->flags |= NSF_ARG_INITCMD; } else if (strncmp(option, "cmd", 3) == 0) { if (unlikely(paramPtr->flags & (NSF_ARG_INITCMD|NSF_ARG_ALIAS|NSF_ARG_FORWARD))) { return NsfPrintError(interp, "parameter option 'cmd' not valid in this option combination"); } paramPtr->flags |= NSF_ARG_CMD; } else if (strncmp(option, "alias", 5) == 0) { if (unlikely(paramPtr->flags & (NSF_ARG_INITCMD|NSF_ARG_CMD|NSF_ARG_FORWARD))) { return NsfPrintError(interp, "parameter option 'alias' not valid in this option combination"); } paramPtr->flags |= NSF_ARG_ALIAS; } else if (strncmp(option, "forward", 7) == 0) { if (unlikely(paramPtr->flags & (NSF_ARG_INITCMD|NSF_ARG_CMD|NSF_ARG_ALIAS))) { return NsfPrintError(interp, "parameter option 'forward' not valid in this option combination"); } paramPtr->flags |= NSF_ARG_FORWARD; } else if (strncmp(option, "slotset", 7) == 0) { if (unlikely(paramPtr->slotObj == NULL)) { return NsfPrintError(interp, "parameter option 'slotset' must follow 'slot='"); } paramPtr->flags |= NSF_ARG_SLOTSET; } else if (strncmp(option, "slotinitialize", 14) == 0) { if (unlikely(paramPtr->slotObj == NULL)) { return NsfPrintError(interp, "parameter option 'slotinit' must follow 'slot='"); } paramPtr->flags |= NSF_ARG_SLOTINITIALIZE; } else if ((dotdot = strnstr(option, "..", optionLength-1))) { /* check lower bound */ if (*option == '0') { paramPtr->flags |= NSF_ARG_ALLOW_EMPTY; } else if (unlikely(*option != '1')) { return NsfPrintError(interp, "lower bound of multiplicity in %s not supported", argString); } /* check upper bound */ option = dotdot + 2; if (*option == '*' || *option == 'n') { if (unlikely((paramPtr->flags & (NSF_ARG_SWITCH)) != 0)) { return NsfPrintError(interp, "upper bound of multiplicity of '%c' not allowed for \"switch\"\n", *option); } paramPtr->flags |= NSF_ARG_MULTIVALUED; } else if (*option != '1') { return NsfPrintError(interp, "upper bound of multiplicity in %s not supported", argString); } } else if (strncmp(option, "noarg", 5) == 0) { if ((paramPtr->flags & NSF_ARG_ALIAS) == 0) { return NsfPrintError(interp, "parameter option \"noarg\" only allowed for parameter type \"alias\""); } paramPtr->flags |= NSF_ARG_NOARG; paramPtr->nrArgs = 0; } else if (strncmp(option, "nodashalnum", 11) == 0) { if (*paramPtr->name == '-') { return NsfPrintError(interp, "parameter option 'nodashalnum' only allowed for positional parameters"); } paramPtr->flags |= NSF_ARG_NODASHALNUM; } else if (strncmp(option, "noconfig", 8) == 0) { if (disallowedOptions != NSF_DISALLOWED_ARG_OBJECT_PARAMETER) { return NsfPrintError(interp, "parameter option 'noconfig' only allowed for object parameters"); } paramPtr->flags |= NSF_ARG_NOCONFIG; } else if (strncmp(option, "args", 4) == 0) { if ((paramPtr->flags & NSF_ARG_ALIAS) == 0) { return NsfPrintError(interp, "parameter option \"args\" only allowed for parameter type \"alias\""); } result = ParamOptionSetConverter(interp, paramPtr, "args", ConvertToNothing); } else if (optionLength >= 4 && strncmp(option, "arg=", 4) == 0) { if (paramPtr->converter != ConvertViaCmd) { return NsfPrintError(interp, "parameter option 'arg=' only allowed for user-defined converter"); } if (paramPtr->converterArg) {DECR_REF_COUNT(paramPtr->converterArg);} paramPtr->converterArg = Tcl_NewStringObj(option + 4, optionLength - 4); /* * In case, we know that we have to unescape double commas, do it here... */ if (unlikely(unescape)) { Unescape(paramPtr->converterArg); } INCR_REF_COUNT(paramPtr->converterArg); } else if (strncmp(option, "switch", 6) == 0) { if (*paramPtr->name != '-') { return NsfPrintError(interp, "invalid parameter type \"switch\" for argument \"%s\"; " "type \"switch\" only allowed for non-positional arguments", paramPtr->name); } else if (paramPtr->flags & NSF_ARG_METHOD_INVOCATION) { return NsfPrintError(interp, "parameter invocation types cannot be used with option 'switch'"); } result = ParamOptionSetConverter(interp, paramPtr, "switch", Nsf_ConvertToSwitch); paramPtr->flags |= NSF_ARG_SWITCH; paramPtr->nrArgs = 0; assert(paramPtr->defaultValue == NULL); paramPtr->defaultValue = Tcl_NewBooleanObj(0); INCR_REF_COUNT(paramPtr->defaultValue); } else if (strncmp(option, "integer", MAX(3, optionLength)) == 0) { result = ParamOptionSetConverter(interp, paramPtr, "integer", Nsf_ConvertToInteger); } else if (strncmp(option, "int32", 5) == 0) { result = ParamOptionSetConverter(interp, paramPtr, "int32", Nsf_ConvertToInt32); } else if (strncmp(option, "boolean", 7) == 0) { result = ParamOptionSetConverter(interp, paramPtr, "boolean", Nsf_ConvertToBoolean); } else if (strncmp(option, "object", 6) == 0) { result = ParamOptionSetConverter(interp, paramPtr, "object", Nsf_ConvertToObject); } else if (strncmp(option, "class", 5) == 0) { result = ParamOptionSetConverter(interp, paramPtr, "class", Nsf_ConvertToClass); } else if (strncmp(option, "metaclass", 9) == 0) { result = ParamOptionSetConverter(interp, paramPtr, "class", Nsf_ConvertToClass); paramPtr->flags |= NSF_ARG_METACLASS; } else if (strncmp(option, "baseclass", 9) == 0) { result = ParamOptionSetConverter(interp, paramPtr, "class", Nsf_ConvertToClass); paramPtr->flags |= NSF_ARG_BASECLASS; } else if (strncmp(option, "mixinreg", 8) == 0) { result = ParamOptionSetConverter(interp, paramPtr, "mixinreg", Nsf_ConvertToMixinreg); } else if (strncmp(option, "filterreg", 9) == 0) { result = ParamOptionSetConverter(interp, paramPtr, "filterreg", Nsf_ConvertToFilterreg); } else if (strncmp(option, "parameter", 9) == 0) { result = ParamOptionSetConverter(interp, paramPtr, "parameter", Nsf_ConvertToParameter); } else if (optionLength >= 6 && strncmp(option, "type=", 5) == 0) { if (paramPtr->converter != Nsf_ConvertToObject && paramPtr->converter != Nsf_ConvertToClass) return NsfPrintError(interp, "parameter option 'type=' only allowed for parameter types 'object' and 'class'"); if (paramPtr->converterArg) {DECR_REF_COUNT(paramPtr->converterArg);} paramPtr->converterArg = Tcl_NewStringObj(option + 5, optionLength - 5); if (unlikely(unescape)) { Unescape(paramPtr->converterArg); } INCR_REF_COUNT(paramPtr->converterArg); } else if (optionLength >= 6 && strncmp(option, "slot=", 5) == 0) { if (paramPtr->slotObj) {DECR_REF_COUNT(paramPtr->slotObj);} paramPtr->slotObj = Tcl_NewStringObj(option + 5, optionLength - 5); if (unlikely(unescape)) { Unescape(paramPtr->slotObj); } INCR_REF_COUNT(paramPtr->slotObj); } else if (optionLength >= 6 && strncmp(option, "method=", 7) == 0) { if ((paramPtr->flags & (NSF_ARG_ALIAS|NSF_ARG_FORWARD|NSF_ARG_SLOTSET)) == 0) { return NsfPrintError(interp, "parameter option 'method=' only allowed for parameter " "types 'alias', 'forward' and 'slotset'"); } if (paramPtr->method) {DECR_REF_COUNT(paramPtr->method);} paramPtr->method = Tcl_NewStringObj(option + 7, optionLength - 7); if (unlikely(unescape)) { Unescape(paramPtr->method); } INCR_REF_COUNT(paramPtr->method); } else { Tcl_DString ds, *dsPtr = &ds; if (option[0] == '\0') { NsfLog(interp, NSF_LOG_WARN, "empty parameter option ignored"); return TCL_OK; } Tcl_DStringInit(dsPtr); Tcl_DStringAppend(dsPtr, option, optionLength); if (paramPtr->converter) { NsfPrintError(interp, "parameter option '%s' unknown for parameter type '%s'", Tcl_DStringValue(dsPtr), paramPtr->type); Tcl_DStringFree(dsPtr); return TCL_ERROR; } if (Nsf_PointerTypeLookup(interp, Tcl_DStringValue(dsPtr))) { /* * Check, if the option refers to a pointer converter */ ParamOptionSetConverter(interp, paramPtr, Tcl_DStringValue(dsPtr), Nsf_ConvertToPointer); Tcl_DStringFree(dsPtr); } else { int i, found = -1; /* * The option is still unknown, check the Tcl string-is checkers */ Tcl_DStringFree(dsPtr); for (i = 0; stringTypeOpts[i]; i++) { /* * Do not allow abbreviations, so the additional strlen checks * for a full match */ if (strncmp(option, stringTypeOpts[i], optionLength) == 0 && strlen(stringTypeOpts[i]) == optionLength) { found = i; break; } } if (found > -1) { /* converter is stringType */ result = ParamOptionSetConverter(interp, paramPtr, "stringtype", Nsf_ConvertToTclobj); if (paramPtr->converterArg) {DECR_REF_COUNT(paramPtr->converterArg);} paramPtr->converterArg = Tcl_NewStringObj(stringTypeOpts[i], -1); INCR_REF_COUNT(paramPtr->converterArg); } else { /* * The parameter option is still unknown. We assume that the parameter * option identifies a user-defined argument checker, implemented as a * method. */ if (paramPtr->converterName) {DECR_REF_COUNT2("converterNameObj",paramPtr->converterName);} paramPtr->converterName = ParamCheckObj(option, optionLength); INCR_REF_COUNT2("converterNameObj", paramPtr->converterName); result = ParamOptionSetConverter(interp, paramPtr, ObjStr(paramPtr->converterName), ConvertViaCmd); } } } if ((paramPtr->flags & disallowedOptions)) { return NsfPrintError(interp, "parameter option '%s' not allowed", option); } if (unlikely((paramPtr->flags & NSF_ARG_METHOD_INVOCATION) && (paramPtr->flags & NSF_ARG_NOCONFIG))) { return NsfPrintError(interp, "parameter option 'noconfig' cannot used together with this type of object parameter"); } return result; } /* *---------------------------------------------------------------------- * ParamParse -- * * Parse a a single parameter with a possible default provided in the form * of an Tcl_Obj. * * Results: * Tcl result code * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ParamParse(Tcl_Interp *interp, Tcl_Obj *procNameObj, Tcl_Obj *arg, int disallowedFlags, Nsf_Param *paramPtr, int *possibleUnknowns, int *plainParams, int *nrNonposArgs) nonnull(1) nonnull(3) nonnull(5) nonnull(6) nonnull(7) nonnull(8); static int ParamParse(Tcl_Interp *interp, Tcl_Obj *procNameObj, Tcl_Obj *arg, int disallowedFlags, Nsf_Param *paramPtr, int *possibleUnknowns, int *plainParams, int *nrNonposArgs) { int result, npac, isNonposArgument, parensCount; size_t length, j; CONST char *argString, *argName; Tcl_Obj **npav; assert(interp); assert(arg); assert(paramPtr); assert(possibleUnknowns); assert(plainParams); assert(nrNonposArgs); paramPtr->paramObj = arg; INCR_REF_COUNT(paramPtr->paramObj); result = Tcl_ListObjGetElements(interp, arg, &npac, &npav); if (unlikely(result != TCL_OK || npac < 1 || npac > 2)) { DECR_REF_COUNT(paramPtr->paramObj); return NsfPrintError(interp, "wrong # of elements in parameter definition for method '%s'" " (should be 1 or 2 list elements): %s", ObjStr(procNameObj), ObjStr(arg)); } argString = ObjStr(npav[0]); length = strlen(argString); /* * Per default parameter have exactly one argument; types without arguments * (like "switch") have to set their nrArgs explicitly. */ paramPtr->nrArgs = 1; isNonposArgument = *argString == '-'; if (isNonposArgument) { argName = argString+1; (*nrNonposArgs) ++; } else { argName = argString; paramPtr->flags |= NSF_ARG_REQUIRED; /* positional arguments are required unless we have a default */ } /*fprintf(stderr, "... parsing '%s', name '%s' argString '%s' \n", ObjStr(arg), argName, argString);*/ /* * Find the first ':' outside of parens; the name of the parameter might be * in array syntax, the array index might contain ":", "," etc. */ parensCount = 0; for (j = 0; j < length; j++) { if (parensCount > 0 && argString[j] == ')') { parensCount --; continue; } if (argString[j] == '(') { parensCount ++; continue; } if (parensCount == 0 && argString[j] == ':') { break; } } if (argString[j] == ':') { /* we found a ':' */ size_t l, start, end; int unescape = 0; /* get parameter name */ STRING_NEW(paramPtr->name, argString, j); paramPtr->nameObj = Tcl_NewStringObj(argName, isNonposArgument ? j-1 : j); INCR_REF_COUNT(paramPtr->nameObj); /* skip space at begin */ for (start = j+1; start 0 && isspace((int)argString[end-1]); end--); result = ParamOptionParse(interp, argString, start, end-start, disallowedFlags, paramPtr, unescape); unescape = 0; if (unlikely(result != TCL_OK)) { goto param_error; } l++; /* skip space from begin */ for (start = l; start 0 && isspace((int)argString[end-1]); end--); /* process last option */ if (end-start > 0) { result = ParamOptionParse(interp, argString, start, end-start, disallowedFlags, paramPtr, unescape); if (unlikely(result != TCL_OK)) { goto param_error; } } } else { /* no ':', the whole arg is the name, we have no options */ STRING_NEW(paramPtr->name, argString, length); if (isNonposArgument) { paramPtr->nameObj = Tcl_NewStringObj(argName, length-1); } else { (*plainParams) ++; paramPtr->nameObj = Tcl_NewStringObj(argName, length); } INCR_REF_COUNT(paramPtr->nameObj); } /* if we have two arguments in the list, the second one is a default value */ if (npac == 2) { if (disallowedFlags & NSF_ARG_HAS_DEFAULT) { NsfPrintError(interp, "parameter \"%s\" is not allowed to have default \"%s\"", argString, ObjStr(npav[1])); goto param_error; } /* if we have for some reason already a default value, free it */ if (paramPtr->defaultValue) { DECR_REF_COUNT(paramPtr->defaultValue); } paramPtr->defaultValue = Tcl_DuplicateObj(npav[1]); INCR_REF_COUNT(paramPtr->defaultValue); /* * The argument will be not required for an invocation, since we * have a default. */ paramPtr->flags &= ~NSF_ARG_REQUIRED; } else if (paramPtr->flags & NSF_ARG_SUBST_DEFAULT) { NsfPrintError(interp, "parameter option substdefault specified for parameter \"%s\"" " without default value", paramPtr->name); goto param_error; } /* postprocessing the parameter options */ if (paramPtr->converter == NULL) { /* * If no converter is set, use the default converter */ paramPtr->converter = Nsf_ConvertToTclobj; } else if (paramPtr->converter == ConvertToNothing) { if (paramPtr->flags & (NSF_ARG_ALLOW_EMPTY|NSF_ARG_MULTIVALUED)) { NsfPrintError(interp, "multiplicity settings for variable argument parameter \"%s\" not allowed", paramPtr->name); goto param_error; } } /* * Check for application specific value checkers and converters */ /*fprintf(stderr, "parm %s: slotObj %p viaCmd? %d\n", paramPtr->name, paramPtr->slotObj, paramPtr->converter == ConvertViaCmd);*/ if ((paramPtr->slotObj || paramPtr->converter == ConvertViaCmd) && paramPtr->type) { CONST char *converterNameString; Tcl_Obj *converterNameObj, *slotObj; NsfObject *paramObject; Tcl_Command cmd; NsfClass *pcl = NULL; slotObj = paramPtr->slotObj ? paramPtr->slotObj : NsfGlobalObjs[NSF_METHOD_PARAMETER_SLOT_OBJ]; result = GetObjectFromObj(interp, slotObj, ¶mObject); if (unlikely(result != TCL_OK)) { NsfPrintError(interp, "non-existing slot object \"%s\"", ObjStr(slotObj)); goto param_error; } if (paramPtr->converterName == NULL) { converterNameObj = ParamCheckObj(paramPtr->type, strlen(paramPtr->type)); INCR_REF_COUNT2("converterNameObj",converterNameObj); } else { converterNameObj = paramPtr->converterName; } converterNameString = ObjStr(converterNameObj); cmd = ObjectFindMethod(interp, paramObject, converterNameObj, &pcl); /*fprintf(stderr, "locating %s on %s returns %p (%s)\n", ObjStr(converterNameObj), ObjectName(paramObject), cmd, ClassName(pcl));*/ if (cmd == NULL) { if (paramPtr->converter == ConvertViaCmd) { NsfLog(interp, NSF_LOG_WARN, "Could not find value checker %s defined on %s", converterNameString, ObjectName(paramObject)); paramPtr->flags |= NSF_ARG_CURRENTLY_UNKNOWN; /* TODO: for the time being, we do not return an error here */ } } else if (paramPtr->converter != ConvertViaCmd && paramPtr->slotObj && strcmp(ObjStr(paramPtr->slotObj), NsfGlobalStrings[NSF_METHOD_PARAMETER_SLOT_OBJ]) != 0) { NsfLog(interp, NSF_LOG_WARN, "Checker method %s defined on %s shadows built-in converter", converterNameString, ObjectName(paramObject)); if (paramPtr->converterName == NULL) { paramPtr->converterName = converterNameObj; paramPtr->converter = NULL; result = ParamOptionSetConverter(interp, paramPtr, converterNameString, ConvertViaCmd); if (unlikely(result != TCL_OK)) { if (converterNameObj != paramPtr->converterName) { DECR_REF_COUNT2("converterNameObj", converterNameObj); } goto param_error; } } } if ((paramPtr->flags & NSF_ARG_IS_CONVERTER) && paramPtr->converter != ConvertViaCmd) { NsfPrintError(interp, "option 'convert' only allowed for application-defined converters"); if (converterNameObj != paramPtr->converterName) { DECR_REF_COUNT2("converterNameObj",converterNameObj); } goto param_error; } if (converterNameObj != paramPtr->converterName) { DECR_REF_COUNT2("converterNameObj", converterNameObj); } } /* * If the argument has no arguments and it is positional, it can't be * required. */ if (paramPtr->nrArgs == 0 && *paramPtr->name != '-' && paramPtr->flags & NSF_ARG_REQUIRED) { paramPtr->flags &= ~NSF_ARG_REQUIRED; } /* * If the argument is not required and no default value is * specified, we have to handle in the client code (eg. in the * canonical arg handlers for scripted methods) the unknown value * (e.g. don't set/unset a variable) */ if (!(paramPtr->flags & NSF_ARG_REQUIRED) && paramPtr->defaultValue == NULL) { (*possibleUnknowns)++; } return TCL_OK; param_error: ParamFree(paramPtr); paramPtr->name = NULL; #if !defined(NDEBUG) /* * Whenever we return a TCL_ERROR, we expect that the interp result contains * an error message. */ { char *errStr = ObjStr(Tcl_GetObjResult(interp)); assert(*errStr != '\0'); } #endif return TCL_ERROR; } /* *---------------------------------------------------------------------- * ParamDefsParse -- * * Parse a list of parameters in the form of Tcl_Objs into a parsedParamPtr * structure (last argument). The argument allowedOptions is used to flag, * what parameter options are generally allowed (typically different for * method and object parameters). Unless forceParamdefs is set, the parsed * parameter structure is only returned when needed (i.e. when not all * parameters are plain parameters). * * Results: * Tcl result code, parsedParameter structure in last argument (allocated * by the caller). * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ParamDefsParse(Tcl_Interp *interp, Tcl_Obj *procNameObj, Tcl_Obj *paramSpecObjs, int allowedOptinons, int forceParamdefs, NsfParsedParam *parsedParamPtr) nonnull(1) nonnull(3) nonnull(6); static int ParamDefsParse(Tcl_Interp *interp, Tcl_Obj *procNameObj, Tcl_Obj *paramSpecObjs, int allowedOptinons, int forceParamdefs, NsfParsedParam *parsedParamPtr) { Tcl_Obj **argsv; int result, argsc; assert(interp); assert(paramSpecObjs); assert(parsedParamPtr); parsedParamPtr->paramDefs = NULL; parsedParamPtr->possibleUnknowns = 0; result = Tcl_ListObjGetElements(interp, paramSpecObjs, &argsc, &argsv); if (unlikely(result != TCL_OK)) { return NsfPrintError(interp, "cannot break down non-positional args: %s", ObjStr(paramSpecObjs)); } if (argsc > 0) { Nsf_Param *paramsPtr, *paramPtr, *lastParamPtr; int i, possibleUnknowns = 0, plainParams = 0, nrNonposArgs = 0; NsfParamDefs *paramDefs; paramPtr = paramsPtr = ParamsNew(argsc); for (i = 0; i < argsc; i++, paramPtr++) { result = ParamParse(interp, procNameObj, argsv[i], allowedOptinons, paramPtr, &possibleUnknowns, &plainParams, &nrNonposArgs); if (result == TCL_OK && paramPtr->converter == ConvertToNothing && i < argsc-1) { result = NsfPrintError(interp, "parameter option \"args\" invalid for parameter \"%s\"; only allowed for last parameter", paramPtr->name); } if (unlikely(result != TCL_OK)) { ParamsFree(paramsPtr); return result; } /* every parameter must have at least a name set */ assert(paramPtr->name); } #if defined(NSF_WITH_VALUE_WARNINGS) if (nrNonposArgs > 0 && argsc > 1) { for (i = 0; i < argsc; i++) { (paramsPtr + i)->flags |= NSF_ARG_CHECK_NONPOS; } } #endif /* * If all arguments are good old Tcl arguments, there is no need * to use the parameter definition structure. */ if (plainParams == argsc && !forceParamdefs) { ParamsFree(paramsPtr); return TCL_OK; } /* fprintf(stderr, "we need param definition structure for {%s}, argsc %d plain %d\n", ObjStr(args), argsc, plainParams); */ /* * Check the last argument. If the last argument is named 'args', * force converter and make it non-required. */ lastParamPtr = paramPtr - 1; if (isArgsString(lastParamPtr->name)) { lastParamPtr->converter = ConvertToNothing; lastParamPtr->flags &= ~NSF_ARG_REQUIRED; } paramDefs = ParamDefsNew(); paramDefs->paramsPtr = paramsPtr; paramDefs->nrParams = paramPtr-paramsPtr; /*fprintf(stderr, "method %s serial %d paramDefs %p ifsize %ld, possible unknowns = %d,\n", ObjStr(procNameObj), paramDefs->serial, paramDefs, paramPtr-paramsPtr, possibleUnknowns);*/ parsedParamPtr->paramDefs = paramDefs; parsedParamPtr->possibleUnknowns = possibleUnknowns; } return TCL_OK; } /* *---------------------------------------------------------------------- * ParameterMethodForwardDispatch -- * * Dispatch a forwarding method provided via parameter definition. * * The current implementation performs for every object * parameter forward the full cycle of * * (a) splitting the spec, * (b) convert it to a the client data structure, * (c) invoke forward, * (d) free client data structure * * In the future, it should convert to the client data * structure just once and free it with the disposal of the * parameter. This could be achieved * * Results: * Tcl result code * * Side effects: * The called function might side-effect. * *---------------------------------------------------------------------- */ static int ParameterMethodForwardDispatch(Tcl_Interp *interp, NsfObject *object, Nsf_Param CONST *paramPtr, Tcl_Obj *newValue, NsfCallStackContent *cscPtr) nonnull(1) nonnull(2) nonnull(3); static int ParameterMethodForwardDispatch(Tcl_Interp *interp, NsfObject *object, Nsf_Param CONST *paramPtr, Tcl_Obj *newValue, NsfCallStackContent *cscPtr) { Tcl_Obj **nobjv, *ov[3], *methodObj, *forwardSpec; ForwardCmdClientData *tcd = NULL; int result, oc, nobjc; assert(interp); assert(object); assert(paramPtr); assert(paramPtr->flags & NSF_ARG_FORWARD); forwardSpec = paramPtr->method ? paramPtr->method : NULL; /* different default? */ if (forwardSpec == NULL) { return NsfPrintError(interp, "forward: no spec available\n"); } result = Tcl_ListObjGetElements(interp, forwardSpec, &nobjc, &nobjv); if (unlikely(result != TCL_OK)) { return result; } methodObj = paramPtr->nameObj; result = ForwardProcessOptions(interp, methodObj, NULL /*withDefault*/, 0 /*withEarlybinding*/, NULL /*withOnerror*/, NULL /*withMethodprefix*/, 0 /*withFrame*/, 0 /*withVerbose*/, nobjv[0], nobjc-1, nobjv+1, &tcd); if (unlikely(result != TCL_OK)) { if (tcd) ForwardCmdDeleteProc(tcd); return result; } /*fprintf(stderr, "parameter %s forward spec <%s> After Options obj %s method %s\n", ObjStr(paramPtr->nameObj), ObjStr(forwardSpec), ObjectName(object), ObjStr(methodObj));*/ tcd->object = object; oc = 1; ov[0] = methodObj; if (paramPtr->nrArgs == 1 && newValue) { ov[oc] = newValue; oc ++; } /* * Mark the intermittent CSC frame as INACTIVE, so that, e.g., * call-stack traversals seeking active frames ignore it. */ if (cscPtr) { cscPtr->frameType = NSF_CSC_TYPE_INACTIVE; } result = NsfForwardMethod(tcd, interp, oc, ov); ForwardCmdDeleteProc(tcd); return result; } /* *---------------------------------------------------------------------- * ParameterMethodDispatch -- * * Dispatch a method provided via parameter definition. The function checks * the parameter definition, builds a argument list for the function call * and invokes finally the configured cmd. This function is typically * called from configure. * * Results: * Tcl result code * * Side effects: * The called function might side-effect. * *---------------------------------------------------------------------- */ static int ParameterMethodDispatch(Tcl_Interp *interp, NsfObject *object, Nsf_Param *paramPtr, Tcl_Obj *newValue, CallFrame *uplevelVarFramePtr, CONST char *initString, Tcl_Obj **nextObjPtr, int nrRemainingArgs) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(6) nonnull(7); static int ParameterMethodDispatch(Tcl_Interp *interp, NsfObject *object, Nsf_Param *paramPtr, Tcl_Obj *newValue, CallFrame *uplevelVarFramePtr, CONST char *initString, Tcl_Obj **nextObjPtr, int nrRemainingArgs) { CallFrame *varFramePtr = Tcl_Interp_varFramePtr(interp); NsfCallStackContent csc, *cscPtr = &csc; CallFrame frame2, *framePtr2 = &frame2; int result = TCL_OK; assert(interp); assert(object); assert(paramPtr); assert(newValue); assert(initString); assert(nextObjPtr); #if 0 {int i; fprintf(stderr, "ParameterMethodDispatch %s flags %06x nrRemainingArgs %d ", paramPtr->name, paramPtr->flags, nrRemainingArgs); for(i = 0; i < nrRemainingArgs; i++) {fprintf(stderr, " [%d]=%p %s,", i, &nextObjPtr[i], ObjStr(nextObjPtr[i]));} fprintf(stderr, "\n"); } #endif /* * The current call-frame of configure uses an obj-frame, such * that setvar etc. are able to access variables like "a" as a * local variable. However, in the init block, we do not like * that behavior, since this should look like like a proc body. * So we push yet another call-frame without providing the * var-frame. * * The new frame will have the namespace of the caller to avoid * the current obj-frame. Nsf_PushFrameCsc() will establish a * CMETHOD frame. */ Tcl_Interp_varFramePtr(interp) = varFramePtr->callerVarPtr; cscPtr->flags = 0; CscInit(cscPtr, object, object->cl /*cl*/, NULL /*cmd*/, NSF_CSC_TYPE_PLAIN, 0, NsfGlobalStrings[NSF_CONFIGURE]); Nsf_PushFrameCsc(interp, cscPtr, framePtr2); if (paramPtr->flags & (NSF_ARG_INITCMD|NSF_ARG_CMD)) { /* cscPtr->cmdPtr = NSFindCommand(interp, "::eval"); */ result = Tcl_EvalObjEx(interp, newValue, TCL_EVAL_DIRECT); } else if (paramPtr->flags & NSF_ARG_ALIAS) { Tcl_Obj *methodObj, **ovPtr, *ov0; CONST char *methodString; int oc = 0; /* * Restore the variable frame context as found at the original call * site of configure(). Note that we do not have to revert this * context change when leaving this configure() context because a * surrounding [uplevel] will correct the call-stack context for us ... */ if (uplevelVarFramePtr) { Tcl_Interp_varFramePtr(interp) = uplevelVarFramePtr; } /* * Mark the intermittent CSC frame as INACTIVE, so that, e.g., * call-stack traversals seeking active frames ignore it. */ cscPtr->frameType = NSF_CSC_TYPE_INACTIVE; /* * If parameter option "method=" was given, use it as method name */ methodObj = paramPtr->method ? paramPtr->method : paramPtr->nameObj; methodString = ObjStr(methodObj); /*fprintf(stderr, "ALIAS %s, nrargs %d converter %p ConvertToNothing %d oc %d\n", paramPtr->name, paramPtr->nrArgs, paramPtr->converter, paramPtr->converter == ConvertToNothing, oc);*/ if (paramPtr->converter == ConvertToNothing) { /* * We are using the varargs interface; pass all remaining args into * the called method. */ if (newValue == paramPtr->defaultValue) { /* * Use the default. */ if (Tcl_ListObjGetElements(interp, paramPtr->defaultValue, &oc, &ovPtr) != TCL_OK) { goto method_arg_done; } ov0 = *ovPtr; ovPtr ++; } else { /* * Use actual args. */ ov0 = *nextObjPtr; /*fprintf(stderr, "ALIAS use actual args oc %d ov0 <%s> nextObjPtr %p %p\n", nrRemainingArgs, ObjStr(ov0), nextObjPtr, nextObjPtr+1);*/ ovPtr = nextObjPtr+1; oc = nrRemainingArgs; } } else { /* * A simple alias, receives no arg (when noarg was specified) or a * single argument (which might be the default value). */ int moc = 1; Tcl_Obj **movPtr = NULL; ov0 = NULL; ovPtr = NULL; if (Tcl_ListObjGetElements(interp, methodObj, &moc, &movPtr) == TCL_OK) { if (moc != 2) { oc = 0; if (unlikely(moc > 2)) { NsfLog(interp, NSF_LOG_WARN, "max 2 words are currently allowed in methodName <%s>", methodString); } } else { oc = 1; methodObj = movPtr[0]; ov0 = movPtr[1]; } } if (paramPtr->nrArgs == 1) { oc++; if (oc == 1) { ov0 = newValue; } else { ovPtr = &newValue; } } } /* * Check, if we have an object parameter alias for the constructor. * Since we require the object system for the current object to * determine its object system configuration, we can't do this at * parameter compile time. */ if (*initString == *methodString && strcmp(initString, methodString) == 0) { result = DispatchInitMethod(interp, object, oc, &ov0, 0); } else { /*fprintf(stderr, "... call alias %s with methodObj %s.%s oc %d, nrArgs %d '%s'\n", paramPtr->name, ObjectName(object), ObjStr(methodObj), oc, paramPtr->nrArgs, ObjStr(newValue));*/ #if !defined(NDEBUG) if (oc>2) { assert(ISOBJ(ovPtr[oc-2])); } #endif Tcl_ResetResult(interp); result = NsfCallMethodWithArgs(interp, (Nsf_Object*)object, methodObj, ov0, oc, ovPtr, NSF_CSC_IMMEDIATE|NSF_CM_IGNORE_PERMISSIONS); } } else { /* must be NSF_ARG_FORWARD */ assert(paramPtr->flags & NSF_ARG_FORWARD); result = ParameterMethodForwardDispatch(interp, object, paramPtr, newValue, cscPtr); } method_arg_done: /* * Pop previously stacked frame for eval context and set the * varFramePtr to the previous value. */ Nsf_PopFrameCsc(interp, framePtr2); CscListRemove(interp, cscPtr, NULL); CscFinish(interp, cscPtr, result, "converter object frame"); Tcl_Interp_varFramePtr(interp) = varFramePtr; /* fprintf(stderr, "NsfOConfigureMethod_ attribute %s evaluated %s => (%d)\n", ObjStr(paramPtr->nameObj), ObjStr(newValue), result);*/ if (likely(result == TCL_OK)) { if (paramPtr->flags & NSF_ARG_CMD && RUNTIME_STATE(interp)->doKeepcmds) { Tcl_ObjSetVar2(interp, NsfGlobalObjs[NSF_ARRAY_CMD], paramPtr->nameObj, newValue, 0); } } return result; } /* *---------------------------------------------------------------------- * MakeProc -- * * Define a scripted function via the ObjCmd "proc". * * Results: * Tcl result code * * Side effects: * Defined function or exception. * *---------------------------------------------------------------------- */ static int MakeProc(Tcl_Namespace *nsPtr, NsfAssertionStore *aStore, Tcl_Interp *interp, Tcl_Obj *nameObj, Tcl_Obj *args, Tcl_Obj *body, Tcl_Obj *precondition, Tcl_Obj *postcondition, NsfObject *defObject, NsfObject *regObject, int withPer_object, int withInner_namespace, int checkAlwaysFlag) nonnull(1) nonnull(3) nonnull(4) nonnull(5) nonnull(6) nonnull(9); static int MakeProc(Tcl_Namespace *nsPtr, NsfAssertionStore *aStore, Tcl_Interp *interp, Tcl_Obj *nameObj, Tcl_Obj *args, Tcl_Obj *body, Tcl_Obj *precondition, Tcl_Obj *postcondition, NsfObject *defObject, NsfObject *regObject, int withPer_object, int withInner_namespace, int checkAlwaysFlag) { Tcl_CallFrame frame, *framePtr = &frame; CONST char *methodName = ObjStr(nameObj); NsfParsedParam parsedParam; Tcl_Obj *ov[4]; int result; assert(nsPtr); assert(interp); assert(nameObj); assert(args); assert(body); assert(defObject); assert(*methodName != ':'); if (regObject == NULL) {regObject = defObject;} /* Check, if we are allowed to redefine the method */ result = CanRedefineCmd(interp, nsPtr, defObject, methodName, 0); if (likely(result == TCL_OK)) { /* Yes, we can! ...so obtain an method parameter definitions */ result = ParamDefsParse(interp, nameObj, args, NSF_DISALLOWED_ARG_METHOD_PARAMETER, 0, &parsedParam); } if (unlikely(result != TCL_OK)) { return result; } ov[0] = NULL; /*objv[0];*/ ov[1] = nameObj; if (parsedParam.paramDefs) { Nsf_Param *pPtr; Tcl_Obj *argList = Tcl_NewListObj(0, NULL); for (pPtr = parsedParam.paramDefs->paramsPtr; pPtr->name; pPtr++) { if (*pPtr->name == '-') { Tcl_ListObjAppendElement(interp, argList, Tcl_NewStringObj(pPtr->name+1, -1)); } else { Tcl_ListObjAppendElement(interp, argList, Tcl_NewStringObj(pPtr->name, -1)); } } ov[2] = argList; INCR_REF_COUNT(ov[2]); /*fprintf(stderr, "final arglist = <%s>\n", ObjStr(argList)); */ ov[3] = AddPrefixToBody(body, 1, &parsedParam); } else { /* no parameter handling needed */ ov[2] = args; ov[3] = AddPrefixToBody(body, 0, &parsedParam); } Tcl_PushCallFrame(interp, (Tcl_CallFrame *)framePtr, nsPtr, 0); /* * Create the method in the provided namespace. */ result = Tcl_ProcObjCmd(NULL, interp, 4, ov); if (likely(result == TCL_OK)) { /* retrieve the defined proc */ Proc *procPtr = FindProcMethod(nsPtr, methodName); if (procPtr) { /* modify the cmd of the proc to set the current namespace for the body */ if (withInner_namespace) { /* * Set the namespace of the method as inside of the class. */ if (regObject->nsPtr == NULL) { MakeObjNamespace(interp, regObject); } /*fprintf(stderr, "obj %s\n", ObjectName(defObject)); fprintf(stderr, "ns %p defObject->ns %p\n", nsPtr, defObject->nsPtr); fprintf(stderr, "ns %s defObject->ns %s\n", nsPtr->fullName, defObject->nsPtr->fullName); fprintf(stderr, "old %s\n", procPtr->cmdPtr->nsPtr->fullName);*/ procPtr->cmdPtr->nsPtr = (Namespace *)regObject->nsPtr; } else { /* * Set the namespace of the method to the same namespace the cmd of * the defObject has. */ procPtr->cmdPtr->nsPtr = ((Command *)regObject->id)->nsPtr; } ParamDefsStore(interp, (Tcl_Command)procPtr->cmdPtr, parsedParam.paramDefs, checkAlwaysFlag); Tcl_SetObjResult(interp, MethodHandleObj(defObject, withPer_object, methodName)); result = TCL_OK; } } Tcl_PopCallFrame(interp); #if defined(NSF_WITH_ASSERTIONS) if (result == TCL_OK && (precondition || postcondition)) { AssertionAddProc(interp, methodName, aStore, precondition, postcondition); } #endif if (parsedParam.paramDefs) { DECR_REF_COUNT(ov[2]); } DECR_REF_COUNT2("resultBody", ov[3]); return result; } /* *---------------------------------------------------------------------- * MakeMethod -- * * Define a scripted method to be defined on defObject and registered on * regObject (if specified). This function handles as well assertions. * * Results: * Tcl result code * * Side effects: * Defined method or exception. * *---------------------------------------------------------------------- */ static int MakeMethod(Tcl_Interp *interp, NsfObject *defObject, NsfObject *regObject, NsfClass *cl, Tcl_Obj *nameObj, Tcl_Obj *args, Tcl_Obj *body, Tcl_Obj *precondition, Tcl_Obj *postcondition, int withInner_namespace, int checkAlwaysFlag) nonnull(1) nonnull(2) nonnull(5) nonnull(6) nonnull(7); static int MakeMethod(Tcl_Interp *interp, NsfObject *defObject, NsfObject *regObject, NsfClass *cl, Tcl_Obj *nameObj, Tcl_Obj *args, Tcl_Obj *body, Tcl_Obj *precondition, Tcl_Obj *postcondition, int withInner_namespace, int checkAlwaysFlag) { CONST char *argsStr = ObjStr(args), *bodyStr = ObjStr(body), *nameStr = ObjStr(nameObj); int result; assert(interp); assert(defObject); assert(nameObj); assert(args); assert(body); if (precondition != NULL && postcondition == NULL) { return NsfPrintError(interp, "%s method '%s'; when specifying a precondition (%s)" " a postcondition must be specified as well", ClassName(cl), nameStr, ObjStr(precondition)); } if (*argsStr == 0 && *bodyStr == 0) { /* * Both, args and body are empty strings. This means we should delete the * method. */ if (RUNTIME_STATE(interp)->exitHandlerDestroyRound == NSF_EXITHANDLER_OFF) { /* * Don't delete methods via scripting during shutdown */ result = cl ? NsfRemoveClassMethod(interp, (Nsf_Class *)cl, nameStr) : NsfRemoveObjectMethod(interp, (Nsf_Object *)defObject, nameStr); } else { /* fprintf(stderr, "don't delete method %s during shutdown\n", nameStr); */ result = TCL_OK; } } else { #if defined(NSF_WITH_ASSERTIONS) NsfAssertionStore *aStore = NULL; if (precondition || postcondition) { if (cl) { NsfClassOpt *opt = NsfRequireClassOpt(cl); if (opt->assertions == NULL) { opt->assertions = AssertionCreateStore(); } aStore = opt->assertions; } else { NsfObjectOpt *opt = NsfRequireObjectOpt(defObject); if (opt->assertions == NULL) { opt->assertions = AssertionCreateStore(); } aStore = opt->assertions; } } result = MakeProc(cl ? cl->nsPtr : defObject->nsPtr, aStore, interp, nameObj, args, body, precondition, postcondition, defObject, regObject, cl == NULL, withInner_namespace, checkAlwaysFlag); #else if (precondition) { NsfLog(interp, NSF_LOG_WARN, "Precondition %s provided, but not compiled with assertion enabled", ObjStr(precondition)); } else if (postcondition) { NsfLog(interp, NSF_LOG_WARN, "Postcondition %s provided, but not compiled with assertion enabled", ObjStr(postcondition)); } result = MakeProc(cl ? cl->nsPtr : defObject->nsPtr, NULL, interp, nameObj, args, body, NULL, NULL, defObject, regObject, cl == NULL, withInner_namespace, checkAlwaysFlag); #endif } if (cl) { NsfInstanceMethodEpochIncr("MakeMethod"); /* could be a filter or filter inheritance ... update filter orders */ if (FilterIsActive(interp, nameStr)) { NsfClasses *subClasses = TransitiveSubClasses(cl); if (subClasses) { FilterInvalidateObjOrders(interp, subClasses); NsfClassListFree(subClasses); } } } else { NsfObjectMethodEpochIncr("MakeMethod"); /* could be a filter => recompute filter order */ FilterComputeDefined(interp, defObject); } return result; } /************************************************************************** * Begin Definition of nsf::proc (Tcl Procs with Parameter handling) **************************************************************************/ /* *---------------------------------------------------------------------- * NsfProcStubDeleteProc -- * * Tcl_CmdDeleteProc for NsfProcStubs. Is called, whenever a * NsfProcStub is deleted and frees the associated client data. * * Results: * None. * * Side effects: * Frees client-data * *---------------------------------------------------------------------- */ static void NsfProcStubDeleteProc(ClientData clientData) { NsfProcClientData *tcd = clientData; /*fprintf(stderr, "NsfProcStubDeleteProc received %p\n", clientData); fprintf(stderr, "... procName %s paramDefs %p\n", ObjStr(tcd->procName), tcd->paramDefs);*/ DECR_REF_COUNT2("procNameObj",tcd->procName); if (tcd->cmd) { NsfCommandRelease(tcd->cmd); } /* tcd->paramDefs is freed by NsfProcDeleteProc() */ FREE(NsfProcClientData, tcd); } /* *---------------------------------------------------------------------- * InvokeShadowedProc -- * * Call the proc specified in objc/objv; procNameObj should be used * for error messages. * * Results: * Tcl result code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int InvokeShadowedProc(Tcl_Interp *interp, Tcl_Obj *procNameObj, Tcl_Command cmd, ParseContext *pcPtr) nonnull(1) nonnull(2) nonnull(4) nonnull(3); static int InvokeShadowedProc(Tcl_Interp *interp, Tcl_Obj *procNameObj, Tcl_Command cmd, ParseContext *pcPtr) { Tcl_Obj *CONST *objv = pcPtr->full_objv; int objc = pcPtr->objc+1; int result; CONST char *fullMethodName = ObjStr(procNameObj); Tcl_CallFrame *framePtr; Proc *procPtr; #if defined(NSF_PROFILE) struct timeval trt; NsfRuntimeState *rst = RUNTIME_STATE(interp); if (rst->doProfile) { gettimeofday(&trt, NULL); } #endif assert(interp); assert(procNameObj); assert(cmd); assert(pcPtr); CheckCStack(interp, "nsfProc", fullMethodName); /* * The code below is derived from the scripted method dispatch and just * slightly adapted to remove object dependencies. */ /*fprintf(stderr, "fullMethodName %s epoch %d\n", fullMethodName, Tcl_Command_cmdEpoch(cmd));*/ if (Tcl_Command_cmdEpoch(cmd)) { #if 1 /* * It seems, as someone messed around with the shadowed proc. For now, we * give up. */ return NsfPrintError(interp, "command '%s' is epoched", fullMethodName); #else /* * We could refetch the command ... */ cmd = Tcl_GetCommandFromObj(interp, procNameObj); if (unlikely(cmd == NULL)) { return NsfPrintError(interp, "cannot lookup command '%s'", fullMethodName); } if (unlikely(!CmdIsProc(cmd))) { return NsfPrintError(interp, "command '%s' is not a proc", fullMethodName); } /* * ... and update the refCounts */ NsfCommandRelease(tcd->cmd); tcd->cmd = cmd; NsfCommandPreserve(tcd->cmd); #endif } procPtr = (Proc *)Tcl_Command_objClientData(cmd); result = TclPushStackFrame(interp, &framePtr, (Tcl_Namespace *) procPtr->cmdPtr->nsPtr, (FRAME_IS_PROC)); if (likely(result == TCL_OK)) { unsigned int dummy = 0; result = ByteCompiled(interp, &dummy, procPtr, fullMethodName); } if (unlikely(result != TCL_OK)) { /* todo: really? error msg? */ return result; } Tcl_CallFrame_objc(framePtr) = objc; Tcl_CallFrame_objv(framePtr) = objv; Tcl_CallFrame_procPtr(framePtr) = procPtr; #if defined(NRE) /*fprintf(stderr, "CALL TclNRInterpProcCore proc '%s' %s nameObj %p %s\n", ObjStr(objv[0]), fullMethodName, procNameObj, ObjStr(procNameObj));*/ Tcl_NRAddCallback(interp, ProcDispatchFinalize, (ClientData)fullMethodName, pcPtr, # if defined(NSF_PROFILE) (ClientData)(unsigned long)trt.tv_usec, (ClientData)(unsigned long)trt.tv_sec # else NULL, NULL # endif ); result = TclNRInterpProcCore(interp, procNameObj, 1, &MakeProcError); #else { ClientData data[4] = { (ClientData)fullMethodName, pcPtr, # if defined(NSF_PROFILE) (ClientData)(unsigned long)trt.tv_usec, (ClientData)(unsigned long)trt.tv_sec # else NULL, NULL # endif }; result = TclObjInterpProcCore(interp, procNameObj, 1, &MakeProcError); result = ProcDispatchFinalize(data, interp, result); } #endif return result; } /* *---------------------------------------------------------------------- * NsfProcStub -- * * Tcl_ObjCmdProc implementing Proc Stubs. This function processes * the argument list in accordance with the parameter definitions * and calls in case of success the shadowed proc. * * Results: * Tcl return code. * * Side effects: * None. * *---------------------------------------------------------------------- */ int NsfProcStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) nonnull(2) nonnull(4) nonnull(1); int NsfProcStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfProcClientData *tcd = clientData; int result; assert(clientData); assert(interp); assert(objv); /*fprintf(stderr, "NsfProcStub %s is called, tcd %p\n", ObjStr(objv[0]), tcd);*/ if (likely(tcd->paramDefs && tcd->paramDefs->paramsPtr)) { ParseContext *pcPtr = (ParseContext *) NsfTclStackAlloc(interp, sizeof(ParseContext), "parse context"); #if 0 /* i see no difference from tcl */ ALLOC_ON_STACK(Tcl_Obj*, objc, tov); /* * We have to substitute the first element of objv with the name * of the function to be called. Since objv is immutable, we have * to copy the full argument vector and replace the element on * position [0] */ memcpy(tov+1, objv+1, sizeof(Tcl_Obj *)*(objc-1)); tov[0] = tcd->procName; #endif /* If the argument parsing is ok, the shadowed proc will be called */ result = ProcessMethodArguments(pcPtr, interp, NULL, tcd->checkAlwaysFlag|NSF_ARGPARSE_FORCE_REQUIRED, tcd->paramDefs, objv[0], objc, objv); if (likely(result == TCL_OK)) { result = InvokeShadowedProc(interp, tcd->procName, tcd->cmd, pcPtr); } else { /*Tcl_Obj *resultObj = Tcl_GetObjResult(interp); fprintf(stderr, "NsfProcStub: incorrect arguments (%s)\n", ObjStr(resultObj));*/ ParseContextRelease(pcPtr); NsfTclStackFree(interp, pcPtr, "release parse context"); } /*FREE_ON_STACK(Tcl_Obj *, tov);*/ } else { fprintf(stderr, "no parameters\n"); assert(0); /* should never happen */ result = TCL_ERROR; } return result; } /* *---------------------------------------------------------------------- * NsfProcAdd -- * * Add a command for implementing a Tcl proc with next scripting * parameter handling. * * For the time being, this function adds two things, (a) a Tcl cmd * functioning as a stub for the argument processing (in accordance * with the parameter definitions) and (b) the shadowed Tcl proc * with a mutated name. * * TODO: the current 1 cmd + 1 proc implementation is not robust * against renaming and partial deletions (deletion of the * stub). * * Results: * Tcl return code. * * Side effects: * Adding one Tcl command and one Tcl proc * *---------------------------------------------------------------------- */ static int NsfProcAdd(Tcl_Interp *interp, NsfParsedParam *parsedParamPtr, CONST char *procName, Tcl_Obj *body, int with_ad, int with_checkAlways) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int NsfProcAdd(Tcl_Interp *interp, NsfParsedParam *parsedParamPtr, CONST char *procName, Tcl_Obj *body, int with_ad, int with_checkAlways) { NsfParamDefs *paramDefs = parsedParamPtr->paramDefs; Nsf_Param *paramPtr; NsfProcClientData *tcd; Tcl_Namespace *cmdNsPtr; Tcl_Obj *argList, *procNameObj, *ov[4]; Tcl_DString ds, *dsPtr = &ds; int result, checkAlwaysFlag; Tcl_Command cmd; assert(interp); assert(parsedParamPtr); assert(procName); assert(body); Tcl_DStringInit(dsPtr); /* * Create a fully qualified procName */ if (*procName != ':') { DStringAppendQualName(dsPtr, Tcl_GetCurrentNamespace(interp), procName); procName = Tcl_DStringValue(dsPtr); } /* * Create first the ProcStub to obtain later its namespace, which is * needed as the inner namespace of the shadowed proc. */ tcd = NEW(NsfProcClientData); cmd = Tcl_CreateObjCommand(interp, procName, NsfProcStub, tcd, NsfProcStubDeleteProc); if (unlikely(cmd == NULL)) { /* * For some reason, the command could not be created. Let us hope, * we have a useful error message. */ Tcl_DStringFree(dsPtr); FREE(NsfProcClientData, tcd); return TCL_ERROR; } checkAlwaysFlag = with_checkAlways ? NSF_ARGPARSE_CHECK : 0; cmdNsPtr = Tcl_Command_nsPtr(cmd); /* * Storing param defs is actually not needed to be stored, since the stub * receives paramters + flag via client data... but it is needed for * introspection. */ ParamDefsStore(interp, cmd, paramDefs, checkAlwaysFlag); /*fprintf(stderr, "NsfProcAdd procName '%s' define cmd '%s' %p in namespace %s\n", procName, Tcl_GetCommandName(interp, cmd), cmd, cmdNsPtr->fullName);*/ /* * Let us create the shadowed Tcl proc, which is stored under * ::nsf::procs::*. First build the fully qualified name * procNameObj. */ Tcl_DStringSetLength(dsPtr, 0); Tcl_DStringAppend(dsPtr, "::nsf::procs", -1); DStringAppendQualName(dsPtr, cmdNsPtr, Tcl_GetCommandName(interp, cmd)); procNameObj = Tcl_NewStringObj(Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr)); INCR_REF_COUNT2("procNameObj", procNameObj); /* will be freed, when NsfProcStub is deleted */ /* * Make sure to create the target namespace under "::nsf::procs::", if * it does not exist. */ { Namespace *nsPtr, *dummy1Ptr, *dummy2Ptr; const char *dummy; /* create the target namespace, if it does not exist */ TclGetNamespaceForQualName(interp, ObjStr(procNameObj), NULL, TCL_CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1Ptr, &dummy2Ptr, &dummy); } /* * Create the client data, which links the stub cmd with the proc. */ tcd->procName = procNameObj; tcd->paramDefs = paramDefs; tcd->with_ad = with_ad; tcd->checkAlwaysFlag = checkAlwaysFlag; tcd->cmd = NULL; /*fprintf(stderr, "NsfProcAdd %s tcd %p paramdefs %p\n", ObjStr(procNameObj), tcd, tcd->paramDefs);*/ /* * Build an argument list for the shadowed proc. */ argList = Tcl_NewListObj(0, NULL); INCR_REF_COUNT(argList); for (paramPtr = paramDefs->paramsPtr; paramPtr->name; paramPtr++) { if (*paramPtr->name == '-') { Tcl_Obj *varNameObj = Tcl_NewStringObj(paramPtr->name+1, -1); /* * If we have the -ad (for ars digita) flag set, we provide the * OpenACS semantics. This is (a) to use the name "boolean" for * a switch and (b) to name the automatic variable with the * prefix "_p". */ if (with_ad && paramPtr->converter == Nsf_ConvertToBoolean && paramPtr->nrArgs == 1) { /*fprintf(stderr, "... ad handling: proc %s param %s type %s nrargs %d default %p\n", procName, paramPtr->name, paramPtr->type, paramPtr->nrArgs, paramPtr->defaultValue);*/ paramPtr->nrArgs = 0; /*paramPtr->converter = Nsf_ConvertToSwitch;*/ Tcl_AppendToObj(varNameObj, "_p", 2); if (paramPtr->defaultValue == NULL) { paramPtr->defaultValue = Tcl_NewBooleanObj(0); INCR_REF_COUNT(paramPtr->defaultValue); } } Tcl_ListObjAppendElement(interp, argList, varNameObj); } else { Tcl_ListObjAppendElement(interp, argList, Tcl_NewStringObj(paramPtr->name, -1)); } } ov[0] = NULL; ov[1] = procNameObj; ov[2] = argList; ov[3] = AddPrefixToBody(body, 1, parsedParamPtr); /*fprintf(stderr, "NsfProcAdd define proc %s arglist '%s'\n", ObjStr(ov[1]), ObjStr(ov[2])); */ result = Tcl_ProcObjCmd(0, interp, 4, ov); DECR_REF_COUNT(argList); DECR_REF_COUNT2("resultBody", ov[3]); if (likely(result == TCL_OK)) { /* * The shadowed proc was created successfully. Retrieve the * defined proc and set its namespace to the namespace of the stub * cmd */ Tcl_Command procCmd = Tcl_GetCommandFromObj(interp, procNameObj); assert(procCmd); ((Command *)procCmd)->nsPtr = (Namespace *)cmdNsPtr; tcd->cmd = procCmd; NsfCommandPreserve(tcd->cmd); } else { /* * We could not define the shadowed proc. In this case, cleanup by * removing the stub cmd. */ Tcl_DeleteCommandFromToken(interp, cmd); } Tcl_DStringFree(dsPtr); return result; } /* *---------------------------------------------------------------------- * ProcessMethodArguments -- * * Process the arguments provided to a method call. It parses the argument * vector objv, disallows certain parameter types and updates the parse * context. * * Results: * Tcl return code. * * Side effects: * Updates parameter context * *---------------------------------------------------------------------- */ static int ProcessMethodArguments(ParseContext *pcPtr, Tcl_Interp *interp, NsfObject *object, int processFlags, NsfParamDefs *paramDefs, Tcl_Obj *methodNameObj, int objc, Tcl_Obj *CONST objv[]) { int result; CallFrame frame, *framePtr = &frame; assert(pcPtr); assert(interp); assert(paramDefs); assert(methodNameObj); assert(objv); if (object && (processFlags & NSF_ARGPARSE_METHOD_PUSH)) { Nsf_PushFrameObj(interp, object, framePtr); } #if 0 {int i; fprintf(stderr, "ProcessMethodArguments before ArgumentParse %s (flags %.6x objc %d): ", ObjStr(methodNameObj), processFlags, objc); for(i = 0; i < objc; i++) {fprintf(stderr, " [%d]=%s,", i, ObjStr(objv[i]));} fprintf(stderr, "\n"); } #endif result = ArgumentParse(interp, objc, objv, object, methodNameObj, paramDefs->paramsPtr, paramDefs->nrParams, paramDefs->serial, processFlags|RUNTIME_STATE(interp)->doCheckArguments, pcPtr); #if 0 { int i, fromArg, toArg; fprintf(stderr, "ProcessMethodArguments after ArgumentParse %s pcPtr->objc %d result %d\n", ObjStr(methodNameObj), pcPtr->objc, result); if (result == TCL_OK) { if (processFlags & NSF_ARGPARSE_START_ZERO) { fromArg = 0; toArg = pcPtr->objc; } else { fromArg = 1; toArg = pcPtr->objc; } for (i = fromArg; i < toArg; i++) { fprintf(stderr, "... pcPtr %p [%d] obj %p refCount %d (%s) flags %.6x & %p\n", pcPtr, i, pcPtr->objv[i] ? pcPtr->objv[i] : NULL, pcPtr->objv[i] ? pcPtr->objv[i]->refCount : -1, pcPtr->objv[i] ? ObjStr(pcPtr->objv[i]) : "(null)", pcPtr->flags[i], &(pcPtr->flags[i])); } } } #endif if (object && (processFlags & NSF_ARGPARSE_METHOD_PUSH)) { Nsf_PopFrameObj(interp, framePtr); } /* * Set objc of the parse context to the number of defined parameters. * pcPtr->objc and paramDefs->nrParams will be equivalent in cases * where argument values are passed to the call in absence of var * args ('args'). Treating "args is more involved (see below). */ if (unlikely(result != TCL_OK)) { return result; } if (pcPtr->varArgs) { /* * The last argument was "args". */ int elts = objc - pcPtr->lastObjc; if (elts == 0) { /* * No arguments were passed to "args". We simply decrement objc. */ pcPtr->objc--; } else if (elts > 1) { /* * Multiple arguments were passed to "args". pcPtr->objv is * pointing to the first of the var args. We have to copy the * remaining actual argument vector objv to the parse context. */ /*NsfPrintObjv("actual: ", objc, objv);*/ ParseContextExtendObjv(pcPtr, paramDefs->nrParams, elts-1, objv + 1 + pcPtr->lastObjc); } else { /* * A single argument was passed to "args". There is no need to * mutate the pcPtr->objv, because this has been achieved in * ArgumentParse (i.e., pcPtr->objv[i] contains this element). */ } } return TCL_OK; } /************************************************************************** * End Definition of nsf::proc (Tcl Procs with Parameter handling) **************************************************************************/ /* *---------------------------------------------------------------------- * ForwardCmdDeleteProc -- * * This Tcl_CmdDeleteProc is called, when a forward method is deleted * * Results: * None. * * Side effects: * Frees client data of the setter command. * *---------------------------------------------------------------------- */ static void ForwardCmdDeleteProc(ClientData clientData) { ForwardCmdClientData *tcd = (ForwardCmdClientData *)clientData; assert(clientData); if (tcd->cmdName) {DECR_REF_COUNT(tcd->cmdName);} if (tcd->subcommands) {DECR_REF_COUNT(tcd->subcommands);} #if defined(NSF_FORWARD_WITH_ONERROR) if (tcd->onerror) {DECR_REF_COUNT(tcd->onerror);} #endif if (tcd->prefix) {DECR_REF_COUNT(tcd->prefix);} if (tcd->args) {DECR_REF_COUNT(tcd->args);} FREE(ForwardCmdClientData, tcd); } /* *---------------------------------------------------------------------- * SetterCmdDeleteProc -- * * This Tcl_CmdDeleteProc is called, when a setter method is deleted * * Results: * None. * * Side effects: * Frees client data of the setter command. * *---------------------------------------------------------------------- */ static void SetterCmdDeleteProc(ClientData clientData) nonnull(1); static void SetterCmdDeleteProc(ClientData clientData) { SetterCmdClientData *setterClientData = (SetterCmdClientData *)clientData; assert(clientData); if (setterClientData->paramsPtr) { ParamsFree(setterClientData->paramsPtr); } FREE(SetterCmdClientData, setterClientData); } /* *---------------------------------------------------------------------- * AliasCmdDeleteProc -- * * This Tcl_CmdDeleteProc is called, when an alias method is deleted * * Results: * None. * * Side effects: * Frees client data of the setter command. * *---------------------------------------------------------------------- */ static void AliasCmdDeleteProc(ClientData clientData) nonnull(1); static void AliasCmdDeleteProc(ClientData clientData) { AliasCmdClientData *tcd = (AliasCmdClientData *)clientData; assert(clientData); /* * Since we just get the clientData, we have to obtain interp, * object, methodName and per-object from tcd; the obj might be * deleted already. We need as well at least still the global * namespace. */ if (tcd->interp && ((Interp *)(tcd->interp))->globalNsPtr && RUNTIME_STATE(tcd->interp)->exitHandlerDestroyRound != NSF_EXITHANDLER_ON_PHYSICAL_DESTROY) { CONST char *methodName = Tcl_GetCommandName(tcd->interp, tcd->aliasCmd); AliasDelete(tcd->interp, tcd->cmdName, methodName, tcd->class == NULL); } /*fprintf(stderr, "AliasCmdDeleteProc aliasedCmd %p\n", tcd->aliasedCmd);*/ if (tcd->cmdName) {DECR_REF_COUNT(tcd->cmdName);} if (tcd->aliasedCmd) { #if defined(WITH_IMPORT_REFS) ImportRef *refPtr, *prevPtr = NULL; Command *aliasedCmd = (Command *)(tcd->aliasedCmd); /*fprintf(stderr, "AliasCmdDeleteProc aliasedCmd %p epoch %d refCount %d\n", aliasedCmd, Tcl_Command_cmdEpoch(tcd->aliasedCmd), aliasedCmd->refCount);*/ /* * Clear the aliasCmd from the imported-ref chain of the aliased * (or real) cmd. This widely resembles what happens in the * DeleteImportedCmd() (see tclNamesp.c), however, as we do not * provide for ImportedCmdData client data etc., we cannot * directly use it. */ for (refPtr = aliasedCmd->importRefPtr; refPtr; refPtr = refPtr->nextPtr) { if (refPtr->importedCmdPtr == (Command *) tcd->aliasCmd) { if (prevPtr == NULL) { aliasedCmd->importRefPtr = refPtr->nextPtr; } else { prevPtr->nextPtr = refPtr->nextPtr; } ckfree((char *) refPtr); break; } prevPtr = refPtr; } #endif NsfCommandRelease(tcd->aliasedCmd); } FREE(AliasCmdClientData, tcd); } /* *---------------------------------------------------------------------- * GetMatchObject -- * * Helper method used by nsfAPI.h and the info methods to check if the the * Tcl_Obj patternObj was provided and can be looked up. If this is the * case, wild card matching etc. does not have to be performed, but just * the properties of the object have to be tested. * * Results: * 0 or 1 or -1, potentially the matchObject (when 0 is returned) * 0: we have wild-card characters, iterate to get matches * 1: we have an existing object * -1: we no wild-card characters and a non-existing object * * Side effects: * None. * *---------------------------------------------------------------------- */ static int GetMatchObject(Tcl_Interp *interp, Tcl_Obj *patternObj, Tcl_Obj *origObj, NsfObject **matchObjectPtr, CONST char **patternPtr) { assert(interp); assert(matchObjectPtr); assert(patternPtr); if (patternObj) { *patternPtr = ObjStr(patternObj); if (TclObjIsNsfObject(interp, patternObj, matchObjectPtr)) { return 1; } if (patternObj == origObj && **patternPtr != ':') { return -1; } } return 0; } /* *---------------------------------------------------------------------- * ForwardProcessOptions -- * * Process the options provided by the forward method and turn these into * the ForwardCmdClientData structure. * * Results: * Tcl result code. * * Side effects: * Allocated and initialized ForwardCmdClientData * *---------------------------------------------------------------------- */ static int ForwardProcessOptions(Tcl_Interp *interp, Tcl_Obj *nameObj, Tcl_Obj *withDefault, int withEarlybinding, Tcl_Obj *withOnerror, Tcl_Obj *withMethodprefix, int withFrame, int withVerbose, Tcl_Obj *target, int objc, Tcl_Obj * CONST objv[], ForwardCmdClientData **tcdPtr) { ForwardCmdClientData *tcd; int i, result = 0; assert(interp); assert(nameObj); assert(objv); tcd = NEW(ForwardCmdClientData); memset(tcd, 0, sizeof(ForwardCmdClientData)); if (withDefault) { Tcl_DString ds, *dsPtr = &ds; DSTRING_INIT(dsPtr); Tcl_DStringAppend(dsPtr, "%1 {", 4); Tcl_DStringAppend(dsPtr, ObjStr(withDefault), -1); Tcl_DStringAppend(dsPtr, "}", 1); NsfDeprecatedCmd(interp, "forward option","-default ...", Tcl_DStringValue(dsPtr)); DSTRING_FREE(dsPtr); tcd->subcommands = withDefault; result = Tcl_ListObjLength(interp, withDefault, &tcd->nr_subcommands); INCR_REF_COUNT(tcd->subcommands); } if (withMethodprefix) { tcd->prefix = withMethodprefix; INCR_REF_COUNT(tcd->prefix); } #if defined(NSF_FORWARD_WITH_ONERROR) if (withOnerror) { tcd->onerror = withOnerror; INCR_REF_COUNT(tcd->onerror); } #endif tcd->frame = withFrame; tcd->verbose = withVerbose; tcd->needobjmap = 0; tcd->cmdName = target; /*fprintf(stderr, "...forwardprocess objc %d, cmdName %p %s\n", objc, target, ObjStr(target));*/ for (i = 0; i < objc; i++) { CONST char *element = ObjStr(objv[i]); /*fprintf(stderr, "... [%d] forwardprocess element '%s'\n", i, element);*/ tcd->needobjmap |= (*element == '%' && *(element+1) == '@'); tcd->hasNonposArgs |= (*element == '%' && *(element+1) == '-'); if (tcd->args == NULL) { tcd->args = Tcl_NewListObj(1, &objv[i]); tcd->nr_args++; INCR_REF_COUNT(tcd->args); } else { Tcl_ListObjAppendElement(interp, tcd->args, objv[i]); tcd->nr_args++; } } if (tcd->cmdName == NULL) { tcd->cmdName = nameObj; } /*fprintf(stderr, "+++ cmdName = %s, args = %s, # = %d\n", ObjStr(tcd->cmdName), tcd->args?ObjStr(tcd->args):"NULL", tcd->nr_args);*/ if (tcd->frame == FrameObjectIdx) { /* * When we evaluating objscope, and define ... * o forward append -frame object append * a call to * o append ... * would lead to a recursive call; so we add the appropriate namespace. */ CONST char *nameString = ObjStr(tcd->cmdName); if (!isAbsolutePath(nameString)) { tcd->cmdName = NameInNamespaceObj(nameString, CallingNameSpace(interp)); /*fprintf(stderr, "+++ name %s not absolute, therefore qualifying %s\n", nameString, ObjStr(tcd->cmdName));*/ } } INCR_REF_COUNT(tcd->cmdName); if (withEarlybinding) { Tcl_Command cmd = Tcl_GetCommandFromObj(interp, tcd->cmdName); if (cmd == NULL) { result = NsfPrintError(interp, "cannot lookup command '%s'", ObjStr(tcd->cmdName)); goto forward_process_options_exit; } if (CmdIsNsfObject(cmd) /* don't do direct invoke on nsf objects */ || Tcl_Command_objProc(cmd) == TclObjInterpProc /* don't do direct invoke on tcl procs */ ) { /* silently ignore earlybinding flag */ tcd->objProc = NULL; } else { tcd->objProc = Tcl_Command_objProc(cmd); tcd->clientData = Tcl_Command_objClientData(cmd); } } tcd->passthrough = tcd->args == NULL && *(ObjStr(tcd->cmdName)) != '%' && tcd->objProc; forward_process_options_exit: /*fprintf(stderr, "forward args = %p, name = '%s'\n", tcd->args, ObjStr(tcd->cmdName));*/ if (likely(result == TCL_OK)) { *tcdPtr = tcd; } else { ForwardCmdDeleteProc(tcd); } return result; } /* *---------------------------------------------------------------------- * StripBodyPrefix -- * * Strip the prefix of the body, which might have been added by nsf. * * Results: * The string of the body without the prefix. * * Side effects: * None. * *---------------------------------------------------------------------- */ static CONST char * StripBodyPrefix(CONST char *body) nonnull(1); static CONST char * StripBodyPrefix(CONST char *body) { assert(body); if (strncmp(body, "::nsf::__unset_unknown_args\n", 28) == 0) { body += 28; } return body; } /* *---------------------------------------------------------------------- * AddSlotObjects -- * * Compute the slot objects (children of the slot container) for a provided * object. The objects can be filtered via a pattern. * * Results: * The function appends results to the provide listObj * * Side effects: * Might add as well to the hash-table to avoid duplicates. * *---------------------------------------------------------------------- */ static void AddSlotObjects(Tcl_Interp *interp, NsfObject *parent, CONST char *prefix, Tcl_HashTable *slotTablePtr, int withSource, NsfClass *type, CONST char *pattern, Tcl_Obj *listObj) nonnull(1) nonnull(2) nonnull(3) nonnull(8); static void AddSlotObjects(Tcl_Interp *interp, NsfObject *parent, CONST char *prefix, Tcl_HashTable *slotTablePtr, int withSource, NsfClass *type, CONST char *pattern, Tcl_Obj *listObj) { NsfObject *slotContainerObject; Tcl_DString ds, *dsPtr = &ds; int fullQualPattern = (pattern && *pattern == ':' && *(pattern+1) == ':'); assert(interp); assert(parent); assert(prefix); assert(listObj); /* fprintf(stderr, "AddSlotObjects parent %s prefix %s type %p %s\n", ObjectName(parent), prefix, type, type ? ClassName(type) : "");*/ DSTRING_INIT(dsPtr); Tcl_DStringAppend(dsPtr, ObjectName(parent), -1); Tcl_DStringAppend(dsPtr, prefix, -1); slotContainerObject = GetObjectFromString(interp, Tcl_DStringValue(dsPtr)); if (slotContainerObject && slotContainerObject->nsPtr && (slotContainerObject->flags & NSF_IS_SLOT_CONTAINER)) { Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; Tcl_HashTable *cmdTablePtr = Tcl_Namespace_cmdTablePtr(slotContainerObject->nsPtr); Tcl_Command cmd; hPtr = Tcl_FirstHashEntry(cmdTablePtr, &hSrch); for (; hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { char *key = Tcl_GetHashKey(cmdTablePtr, hPtr); NsfObject *childObject; if (slotTablePtr) { int new; /* * Check, if we have and entry with this key already processed. We * never want to report shadowed entries. */ Tcl_CreateHashEntry(slotTablePtr, key, &new); if (!new) continue; } /* * Obtain the childObject */ cmd = (Tcl_Command) Tcl_GetHashValue(hPtr); childObject = NsfGetObjectFromCmdPtr(cmd); /* * Report just the already fully initialized slot objects, not the one * being right now created. */ if (childObject == NULL || (childObject->flags & NSF_INIT_CALLED) == 0) { /* fprintf(stderr, "....... key %s unfinished\n", key);*/ continue; } /* * Check the pattern. */ if (pattern) { int match; /* * If the pattern looks like fully qualified, we match against the * fully qualified name. */ if (fullQualPattern) { match = Tcl_StringMatch(ObjectName(childObject), pattern); } else { /* * do we have a mangled name of a private property/variable? */ if (*key == '_' && *(key+1) == '_' && *(key+2) == '_' && *(key+3) == '_') { Tcl_Obj *value = Nsf_ObjGetVar2((Nsf_Object *)childObject, interp, NsfGlobalObjs[NSF_SETTERNAME], NULL, 0); match = value ? Tcl_StringMatch(ObjStr(value), pattern) : 0; /*fprintf(stderr, "pattern <%s> fullQualPattern %d child %s key %s %p <%s> match %d\n", pattern, fullQualPattern, ObjectName(childObject), key, value, value ? ObjStr(value) : "", match);*/ } else { match = Tcl_StringMatch(key, pattern); } } if (!match) { continue; } } /* * Check, if the entry is from the right type */ if (type && !IsSubType(childObject->cl, type)) { continue; } /* * Add finally the entry to the returned list. */ Tcl_ListObjAppendElement(interp, listObj, childObject->cmdName); } } DSTRING_FREE(dsPtr); } /* *---------------------------------------------------------------------- * FindCalledClass -- * * Find the called class of the called proc on the callstack. * * Results: * NsfClass * or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfClass *FindCalledClass(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static NsfClass * FindCalledClass(Tcl_Interp *interp, NsfObject *object) { NsfCallStackContent *cscPtr = CallStackGetTopFrame0(interp); CONST char *methodName; Tcl_Command cmd; assert(interp); assert(object); if (cscPtr->frameType == NSF_CSC_TYPE_PLAIN) { return cscPtr->cl; } if (cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_FILTER) { methodName = MethodName(cscPtr->filterStackEntry->calledProc); } else if (cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_MIXIN && object->mixinStack) { methodName = (char *)Tcl_GetCommandName(interp, cscPtr->cmdPtr); } else { return NULL; } if (object->nsPtr) { cmd = FindMethod(object->nsPtr, methodName); if (cmd) { /* we called an object specific method */ return NULL; } } return SearchCMethod(object->cl, methodName, &cmd); } /* * Next Primitive Handling */ /* *---------------------------------------------------------------------- * NextSearchMethod -- * * Determine the method to be called via "next". The function returns on * success the found cmd and information like method name, was it from a mixin, filter, * or was the end of the filter chain reached. * * Results: * Tcl result code * * Side effects: * None. * *---------------------------------------------------------------------- */ NSF_INLINE static int NextSearchMethod(NsfObject *object, Tcl_Interp *interp, NsfCallStackContent *cscPtr, NsfClass **clPtr, CONST char **methodNamePtr, Tcl_Command *cmdPtr, int *isMixinEntry, int *isFilterEntry, int *endOfFilterChain, Tcl_Command *currentCmdPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5) nonnull(6) nonnull(7) nonnull(8) nonnull(9) nonnull(10); NSF_INLINE static int NextSearchMethod(NsfObject *object, Tcl_Interp *interp, NsfCallStackContent *cscPtr, NsfClass **clPtr, CONST char **methodNamePtr, Tcl_Command *cmdPtr, int *isMixinEntry, int *isFilterEntry, int *endOfFilterChain, Tcl_Command *currentCmdPtr) { int endOfChain = 0, objflags; assert(object); assert(interp); assert(cscPtr); assert(clPtr); assert(methodNamePtr); assert(cmdPtr); assert(isMixinEntry); assert(isFilterEntry); assert(endOfFilterChain); assert(currentCmdPtr); /*fprintf(stderr, "NextSearchMethod for %s called with cl %p\n", *methodNamePtr, *clPtr);*/ /* * Next in filters */ objflags = object->flags; /* avoid stalling */ if (!(objflags & NSF_MIXIN_ORDER_VALID)) { MixinComputeDefined(interp, object); objflags = object->flags; /* avoid stalling */ } if ((objflags & NSF_FILTER_ORDER_VALID) && object->filterStack && object->filterStack->currentCmdPtr) { *cmdPtr = FilterSearchProc(interp, object, currentCmdPtr, clPtr); /*fprintf(stderr, "FilterSearchProc returned cmd %p\n", *cmdPtr); NsfShowStack(interp);*/ if (*cmdPtr == NULL) { if (cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_FILTER) { /* * Reset the information to the values of method, clPtr * to the values they had before calling the filters. */ *methodNamePtr = MethodName(object->filterStack->calledProc); endOfChain = 1; *endOfFilterChain = 1; *clPtr = NULL; /*fprintf(stderr, "EndOfChain resetting cl\n");*/ } } else { *methodNamePtr = (char *) Tcl_GetCommandName(interp, *cmdPtr); *endOfFilterChain = 0; *isFilterEntry = 1; return TCL_OK; } } /* * Next in Mixins requires that we have already a mixinStack, and the * current frame is not a plain frame. */ assert(objflags & NSF_MIXIN_ORDER_VALID); if (object->mixinStack && cscPtr->frameType) { int result = MixinSearchProc(interp, object, *methodNamePtr, NULL, clPtr, currentCmdPtr, cmdPtr); /* fprintf(stderr, "next in mixins %s frameType %.6x\n", *methodNamePtr, cscPtr->frameType); */ if (unlikely(result != TCL_OK)) { return result; } if (*cmdPtr == NULL) { if (cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_MIXIN) { endOfChain = 1; *clPtr = NULL; } } else { *isMixinEntry = 1; return TCL_OK; } } /*fprintf(stderr, "nextsearch: object %s nsPtr %p endOfChain %d\n", ObjectName(object), object->nsPtr, endOfChain);*/ /* * Otherwise: normal method dispatch * * If we are already in the precedence ordering, then advance * past our last point; otherwise (if clPtr==NULL) begin from the start. * * When a mixin or filter chain reached its end, we have to check for * fully qualified method names and search the obj-specific methods as well. */ if (endOfChain) { if (**methodNamePtr == ':') { *cmdPtr = Tcl_FindCommand(interp, *methodNamePtr, NULL, TCL_GLOBAL_ONLY); /* fprintf(stderr, "NEXT found absolute cmd %s => %p\n", *methodNamePtr, *cmdPtr); */ } else if (object->nsPtr) { *cmdPtr = FindMethod(object->nsPtr, *methodNamePtr); if (*cmdPtr && (Tcl_Command_flags(*cmdPtr) & NSF_CMD_CALL_PRIVATE_METHOD)) { /*fprintf(stderr, "NEXT found private cmd %s => %p\n", *methodNamePtr, *cmdPtr);*/ *cmdPtr = NULL; } } else { *cmdPtr = NULL; } } else { *cmdPtr = NULL; } /*fprintf(stderr, "NEXT methodName %s *clPtr %p %s *cmd %p cscPtr->flags %.6x\n", *methodNamePtr, *clPtr, ClassName((*clPtr)), *cmdPtr, cscPtr->flags); */ if (!*cmdPtr) { NsfClasses *pl = PrecedenceOrder(object->cl); NsfClass *cl = *clPtr; if (cl) { /* * Skip until actual class */ for ( ; pl; pl = pl->nextPtr) { if (pl->cl == cl) { pl = pl->nextPtr; break; } } } if (pl) { /* * Search for a further class method. When we are called from an active * filter and the call had the -local flag set, then allow to call private methods. */ *clPtr = SearchPLMethod(pl, *methodNamePtr, cmdPtr, ((cscPtr->flags & NSF_CM_LOCAL_METHOD) && cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_FILTER) ? 0 : NSF_CMD_CALL_PRIVATE_METHOD); } else { *clPtr = NULL; } } else { *clPtr = NULL; } return TCL_OK; } /* *---------------------------------------------------------------------- * NextGetArguments -- * * Obtain arguments for a method invoked via next either from the * argument vector or from the stack (call stack content or Tcl * stack). In case of ensemble calls the stack entries of the * ensemble invocation are used. The function returns the arguments * 4 to 8. * * Results: * Tcl return code * * Side effects: * none * *---------------------------------------------------------------------- */ static int NextGetArguments(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], NsfCallStackContent **cscPtrPtr, CONST char **methodNamePtr, int *outObjc, Tcl_Obj ***outObjv, int *freeArgumentVector) nonnull(1) nonnull(4) nonnull(5) nonnull(6) nonnull(7) nonnull(8); static int NextGetArguments(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], NsfCallStackContent **cscPtrPtr, CONST char **methodNamePtr, int *outObjc, Tcl_Obj ***outObjv, int *freeArgumentVector) { Tcl_Obj **nobjv; int nobjc, oc, inEnsemble; Tcl_CallFrame *framePtr; NsfCallStackContent *cscPtr = CallStackGetTopFrame(interp, &framePtr); assert(interp); assert(cscPtrPtr); assert(methodNamePtr); assert(outObjc); assert(outObjv); assert(freeArgumentVector); /* always make sure, we only decrement when necessary */ *freeArgumentVector = 0; if (cscPtr == NULL) { return NsfPrintError(interp, "next: can't find self"); } if (cscPtr->cmdPtr == NULL) { return NsfPrintError(interp, "next: no executing proc"); } oc = Tcl_CallFrame_objc(framePtr); if ((cscPtr->frameType & NSF_CSC_TYPE_ENSEMBLE)) { /* * We are in an ensemble method. The next works here not on the * actual methodName + frame, but on the ensemble above it. We * locate the appropriate call-stack content and continue next on * that. */ cscPtr = CallStackFindEnsembleCsc(framePtr, &framePtr); assert(cscPtr); inEnsemble = 1; *methodNamePtr = ObjStr(cscPtr->objv[0]); } else { inEnsemble = 0; *methodNamePtr = Tcl_GetCommandName(interp, cscPtr->cmdPtr); } /*fprintf(stderr, "NextGetArguments oc %d objc %d inEnsemble %d objv %p\n", oc, objc, inEnsemble, cscPtr->objv); */ if (objc > -1) { int methodNameLength; /* * Arguments were provided. We have to construct an argument * vector with the first argument(s) as the method name. In an * ensemble, we have to insert the objs of the full ensemble name. */ if (inEnsemble) { methodNameLength = 1 + cscPtr->objc - oc; nobjc = objc + methodNameLength; nobjv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj *) * nobjc); MEM_COUNT_ALLOC("nextArgumentVector", nobjv); /* * copy the ensemble path name */ memcpy((char *)nobjv, cscPtr->objv, sizeof(Tcl_Obj *) * methodNameLength); } else { methodNameLength = 1; nobjc = objc + methodNameLength; nobjv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj *) * nobjc); MEM_COUNT_ALLOC("nextArgumentVector", nobjv); /* * copy the method name */ if (cscPtr->objv) { nobjv[0] = cscPtr->objv[0]; } else if (Tcl_CallFrame_objv(framePtr)) { nobjv[0] = Tcl_CallFrame_objv(framePtr)[0]; } } /* * copy the remaining argument vector */ memcpy(nobjv + methodNameLength, objv, sizeof(Tcl_Obj *) * objc); INCR_REF_COUNT(nobjv[0]); /* we seem to need this here */ *freeArgumentVector = 1; } else { /* * no arguments were provided */ if (cscPtr->objv) { nobjv = (Tcl_Obj **)cscPtr->objv; nobjc = cscPtr->objc; } else { nobjc = Tcl_CallFrame_objc(framePtr); nobjv = (Tcl_Obj **)Tcl_CallFrame_objv(framePtr); } } *cscPtrPtr = cscPtr; *outObjc = nobjc; *outObjv = nobjv; return TCL_OK; } /* *---------------------------------------------------------------------- * NextInvokeFinalize -- * * This finalize function is either called via NRE callback or * directly (from NextSearchAndInvoke). It resets after a successul * lookup and invocation the continuation context (filter flags etc) * and cleans up optionally the argument vector (inverse operation * of NextGetArguments). * * Results: * Tcl return code * * Side effects: * freeing memory * *---------------------------------------------------------------------- */ NSF_INLINE static int NextInvokeFinalize(ClientData data[], Tcl_Interp *interp, int result) nonnull(1) nonnull(2); NSF_INLINE static int NextInvokeFinalize(ClientData data[], Tcl_Interp *interp, int result) { Tcl_Obj **nobjv = data[0]; NsfCallStackContent *cscPtr = data[1]; assert(data); assert(interp); /*fprintf(stderr, "***** NextInvokeFinalize cscPtr %p flags %.6x is next %d result %d unk %d\n", cscPtr, cscPtr->flags, cscPtr->flags & NSF_CSC_CALL_IS_NEXT, result, RUNTIME_STATE(interp)->unknown);*/ if (cscPtr->flags & NSF_CSC_CALL_IS_NEXT) { /* fprintf(stderr, "..... it was a successful next\n"); */ cscPtr->flags &= ~NSF_CSC_CALL_IS_NEXT; if (cscPtr->frameType == NSF_CSC_TYPE_INACTIVE_FILTER) { cscPtr->frameType = NSF_CSC_TYPE_ACTIVE_FILTER; } else if (cscPtr->frameType == NSF_CSC_TYPE_INACTIVE_MIXIN) { cscPtr->frameType = NSF_CSC_TYPE_ACTIVE_MIXIN; } } if (nobjv) { DECR_REF_COUNT(nobjv[0]); MEM_COUNT_FREE("nextArgumentVector", nobjv); ckfree((char *)nobjv); } if (result == TCL_ERROR && RUNTIME_STATE(interp)->unknown) { /* fprintf(stderr, "don't report unknown error\n"); */ /* * Don't report "unknown" errors via next. */ result = TCL_OK; } return result; } /* *---------------------------------------------------------------------- * NextSearchAndInvoke -- * * The function is called with a final argument vector and searches for a * possibly shadowed method. If a target method is found, this dispatcher * function updates the continuation context (filter flags etc.), invokes * upon the target method, and performs a cleanup. * * Results: * Tcl return code * * Side effects: * The invoked method might produce side effects. Also, the interp's unknown * state may be modified. * *---------------------------------------------------------------------- */ static int NextSearchAndInvoke(Tcl_Interp *interp, CONST char *methodName, int objc, Tcl_Obj *CONST objv[], NsfCallStackContent *cscPtr, int freeArgumentVector) { Tcl_Command cmd = NULL, currentCmd = NULL; int result, isMixinEntry = 0, isFilterEntry = 0, endOfFilterChain = 0; NsfRuntimeState *rst = RUNTIME_STATE(interp); NsfObject *object = cscPtr->self; NsfClass *cl; assert(interp); assert(methodName); assert(objv); assert(cscPtr); /* * Search the next method & compute its method data */ cl = cscPtr->cl; result = NextSearchMethod(object, interp, cscPtr, &cl, &methodName, &cmd, &isMixinEntry, &isFilterEntry, &endOfFilterChain, ¤tCmd); /*fprintf(stderr, "NEXT search on %s.%s cl %p cmd %p endOfFilterChain %d result %d IS OK %d\n", ObjectName(object), methodName, cl, cmd, endOfFilterChain, result, (result == TCL_OK));*/ if (unlikely(result != TCL_OK)) { goto next_search_and_invoke_cleanup; } #if 0 Tcl_ResetResult(interp); /* needed for bytecode support */ #endif if (cmd) { int frameType = NSF_CSC_TYPE_PLAIN; /* * change mixin state */ if (object->mixinStack) { if (cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_MIXIN) { cscPtr->frameType = NSF_CSC_TYPE_INACTIVE_MIXIN; } /* otherwise move the command pointer forward */ if (isMixinEntry) { frameType = NSF_CSC_TYPE_ACTIVE_MIXIN; object->mixinStack->currentCmdPtr = currentCmd; } } /* * change filter state */ if (object->filterStack) { if (cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_FILTER) { /*fprintf(stderr, "next changes filter state\n");*/ cscPtr->frameType = NSF_CSC_TYPE_INACTIVE_FILTER; } /* otherwise move the command pointer forward */ if (isFilterEntry) { /*fprintf(stderr, "next moves filter forward\n");*/ frameType = NSF_CSC_TYPE_ACTIVE_FILTER; object->filterStack->currentCmdPtr = currentCmd; } } /* * now actually call the "next" method */ cscPtr->flags |= NSF_CSC_CALL_IS_NEXT; rst->unknown = 0; #if defined(NRE) { unsigned int flags; /* * Allow call only without immediate flag, when caller has NRE without immediate */ flags = NsfImmediateFromCallerFlags(cscPtr->flags); /*fprintf(stderr, "MethodDispatch in next flags %.6x NRE %d immediate %d next-flags %.6x\n", cscPtr->flags, (cscPtr->flags & NSF_CSC_CALL_IS_NRE) != 0, (cscPtr->flags & NSF_CSC_IMMEDIATE) != 0, flags);*/ if (flags == 0) { /* * The call is NRE-enabled. We register the callback and return * here immediately. All other exists form this functions have * to call NextInvokeFinalize manually on return. */ Tcl_NRAddCallback(interp, NextInvokeFinalize, freeArgumentVector ? (ClientData)objv : NULL, cscPtr, NULL, NULL); return MethodDispatch(object, interp, objc, objv, cmd, object, cl, methodName, frameType, flags); } else { result = MethodDispatch(object, interp, objc, objv, cmd, object, cl, methodName, frameType, flags); } } #else /*fprintf(stderr, "NextSearchAndWinvoke calls cmd %p methodName %s cscPtr->flags %.8x\n", cmd, methodName, cscPtr->flags);*/ result = MethodDispatch(object, interp, objc, objv, cmd, object, cl, methodName, frameType, cscPtr->flags); #endif } else if (likely(result == TCL_OK)) { NsfCallStackContent *topCscPtr; Tcl_CallFrame *varFramePtr; int isLeafNext; /* * We could not find a cmd, yet the dispatch attempt did not result * in an error. This means that we find ourselves in either of three * situations at this point: * * 1) An explicit "next" cmd (NsfNextCmd()) at the end of a filter chain: * Dispatch to unknown as there is no implementation for the requested * call available. * * 2) An explicit "next" cmd from within a leaf sub-method (a "leaf * next"): Remain silent, do not dispatch to unknown. * 3) An implicit "next" triggered for unresolved submethods that might be * resolved along the next path: Dispatch to unknown, the requested * sub-cmd is not resolvable to a cmd. * * For the cases 1) and 3), set the interp's unknown flag signaling to * higher levels (e.g., in MethodDispatchCsc(), in NsfNextCmd()) the need * for dispatching to unknown. */ /* NsfShowStack(interp);*/ topCscPtr = CallStackGetTopFrame(interp, &varFramePtr); assert(topCscPtr); /* * Find the appropriate frame pointing to the start of the ensemble, in * case we are in the middle of an ensemble. */ /*fprintf(stderr, "######## cscPtr %p topCscPtr %p\n", cscPtr, topCscPtr);*/ if ( cscPtr != topCscPtr && (cscPtr->flags & NSF_CSC_CALL_IS_ENSEMBLE) != 0 && (topCscPtr->flags & NSF_CSC_CALL_IS_ENSEMBLE) != 0) { for (; varFramePtr != NULL; varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr)) { topCscPtr = (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr); assert(topCscPtr); /*fprintf(stderr, "######## cscPtr %p topCscPtr %p topCscPtr->flags %8x\n", cscPtr, topCscPtr, topCscPtr ? topCscPtr->flags : 0);*/ if ((topCscPtr->flags & NSF_CM_ENSEMBLE_UNKNOWN)) break; } varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr); if (Tcl_CallFrame_isProcCallFrame(varFramePtr) & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { topCscPtr = (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr); assert(topCscPtr); } } /* case 2 */ isLeafNext = (cscPtr != topCscPtr) && (topCscPtr->frameType & NSF_CSC_TYPE_ENSEMBLE) && (topCscPtr->flags & NSF_CSC_CALL_IS_ENSEMBLE) == 0; rst->unknown = /* case 1 */ endOfFilterChain || /* case 3 */ (!isLeafNext && (cscPtr->flags & NSF_CSC_CALL_IS_ENSEMBLE)); /*fprintf(stderr, "******** setting unknown to %d isLeafNext %d topCscPtr %p endOfFilterChain %d\n", rst->unknown, isLeafNext,topCscPtr,endOfFilterChain);*/ } next_search_and_invoke_cleanup: /* * We come here, whenever the NRE callback is NOT registered */ {ClientData data[2] = { freeArgumentVector ? (ClientData)objv : NULL, cscPtr }; return NextInvokeFinalize(data, interp, result); } } /* *---------------------------------------------------------------------- * NsfNextObjCmd -- * * nsf::xotclnext is for backwards compatibility to the next * implementation in XOTcl. It receives an argument vector which * is used for the invocation. if no argument vector is provided, * the argument vector of the last invocation is used. If the * argument vector starts with "--noArgs", then no arguments are * passed to the shadowed method. * * TODO: On the longer range, this function should go into an external * library (e.g. XOTcl compatibility library) * * Results: * Tcl return code * * Side effects: * The invoked method might produce side effects * *---------------------------------------------------------------------- */ static int NsfNextObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) nonnull(2) nonnull(4); static int NsfNextObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { int freeArgumentVector, result, nobjc = 0; CONST char *methodName = NULL; NsfCallStackContent *cscPtr; Tcl_Obj **nobjv; assert(interp); assert(objv); if (likely(objc < 2)) { /* No arguments were provided */ objc = 0; } else { /* in case --noArgs is used, remove the flag and provide an empty argument list */ CONST char *arg1String = ObjStr(objv[1]); if (*arg1String == '-' && !strcmp(arg1String, "--noArgs")) { objc = 1; } } result = NextGetArguments(interp, objc-1, &objv[1], &cscPtr, &methodName, &nobjc, &nobjv, &freeArgumentVector); if (likely(result == TCL_OK)) { assert(nobjc > 0); result = NextSearchAndInvoke(interp, methodName, nobjc, nobjv, cscPtr, freeArgumentVector); } return result; } /* *---------------------------------------------------------------------- * FindSelfNext -- * * This function is called via [current nextmethod] to set the result of the * interp to the method which would be called by [next]. If there are more * shadowed methods along the precedence path, it sets the result of the * next method in form of a method handle. If there are no more shadowed * procs, the result is set to empty. * * Results: * Tcl return code * * Side effects: * Set Tcl result. * *---------------------------------------------------------------------- */ static int FindSelfNext(Tcl_Interp *interp) nonnull(1); static int FindSelfNext(Tcl_Interp *interp) { NsfCallStackContent *cscPtr = CallStackGetTopFrame0(interp); Tcl_Command cmd = NULL, currentCmd = NULL; int result, isMixinEntry = 0, isFilterEntry = 0, endOfFilterChain = 0; NsfClass *cl = cscPtr->cl; NsfObject *object = cscPtr->self; CONST char *methodName; assert(interp); Tcl_ResetResult(interp); methodName = (char *)Tcl_GetCommandName(interp, cscPtr->cmdPtr); if (methodName == NULL) { return TCL_OK; } result = NextSearchMethod(object, interp, cscPtr, &cl, &methodName, &cmd, &isMixinEntry, &isFilterEntry, &endOfFilterChain, ¤tCmd); if (cmd) { Tcl_SetObjResult(interp, MethodHandleObj(cl ? (NsfObject *)cl : object, cl == NULL, methodName)); } return result; } /* *---------------------------------------------------------------------- * ComputeLevelObj -- * * This function comptes a fresh Tcl_Obj referring to the interp level. The * caller has to care about freeing the returned Tcl_Obj. * * Results: * Tcl_Obj * * * Side effects: * Allocates a new Tcl_Obj * *---------------------------------------------------------------------- */ static Tcl_Obj * ComputeLevelObj(Tcl_Interp *interp, CallStackLevel level) nonnull(1) returns_nonnull; static Tcl_Obj * ComputeLevelObj(Tcl_Interp *interp, CallStackLevel level) { Tcl_CallFrame *framePtr; Tcl_Obj *resultObj; assert(interp); switch (level) { case CALLING_LEVEL: NsfCallStackFindLastInvocation(interp, 1, &framePtr); break; case ACTIVE_LEVEL: NsfCallStackFindActiveFrame(interp, 1, &framePtr); break; default: framePtr = NULL; /* silence compiler */ } if (framePtr) { /* the call was from an nsf frame, return absolute frame number */ char buffer[LONG_AS_STRING]; int l; buffer[0] = '#'; Nsf_ltoa(buffer+1, (long)Tcl_CallFrame_level(framePtr), &l); resultObj = Tcl_NewStringObj(buffer, l+1); } else { /* If not called from an nsf frame, return 1 as default */ resultObj = Tcl_NewIntObj(1); } return resultObj; } /* int NsfKObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { if (objc < 2) return NsfPrintError(interp, "wrong # of args for K"); Tcl_SetObjResult(interp, objv[1]); return TCL_OK; } */ /* * object creation & destruction */ /* *---------------------------------------------------------------------- * UnsetInAllNamespaces -- * * Try to unset a variable, searching for the variable in all * name-spaces. This function is used by volatile to unset the automatic * variable used for the destroy trace. * * Results: * Tcl return code * * Side effects: * Might unset variable * *---------------------------------------------------------------------- */ static int UnsetInAllNamespaces(Tcl_Interp *interp, Tcl_Namespace *nsPtr, CONST char *name) nonnull(1) nonnull(2) nonnull(3); static int UnsetInAllNamespaces(Tcl_Interp *interp, Tcl_Namespace *nsPtr, CONST char *name) { int rc = 0; Tcl_HashSearch search; Tcl_HashEntry *entryPtr; Tcl_Var *varPtr; assert(interp); assert(nsPtr); assert(name); /*fprintf(stderr, "### UnsetInAllNamespaces variable '%s', current namespace '%s'\n", name, nsPtr ? nsPtr->fullName : "NULL");*/ entryPtr = Tcl_FirstHashEntry(Tcl_Namespace_childTablePtr(nsPtr), &search); varPtr = (Tcl_Var *) Tcl_FindNamespaceVar(interp, name, nsPtr, 0); /*fprintf(stderr, "found %s in %s -> %p\n", name, nsPtr->fullName, varPtr);*/ if (varPtr) { Tcl_DString dFullname, *dsPtr = &dFullname; int result; Tcl_DStringInit(dsPtr); Tcl_DStringAppend(dsPtr, "unset ", -1); DStringAppendQualName(dsPtr, nsPtr, name); /*rc = Tcl_UnsetVar2(interp, Tcl_DStringValue(dsPtr), NULL, TCL_LEAVE_ERR_MSG);*/ result = Tcl_Eval(interp, Tcl_DStringValue(dsPtr)); /* fprintf(stderr, "fqName = '%s' unset => %d %d\n", Tcl_DStringValue(dsPtr), rc, TCL_OK);*/ if (likely(result == TCL_OK)) { rc = 1; } else { Tcl_Obj *resultObj = Tcl_GetObjResult(interp); fprintf(stderr, " err = '%s'\n", ObjStr(resultObj)); } Tcl_DStringFree(dsPtr); } while (rc == 0 && entryPtr) { Tcl_Namespace *childNsPtr = (Tcl_Namespace *) Tcl_GetHashValue(entryPtr); /*fprintf(stderr, "child = %s\n", childNsPtr->fullName);*/ entryPtr = Tcl_NextHashEntry(&search); rc |= UnsetInAllNamespaces(interp, childNsPtr, name); } return rc; } /* *---------------------------------------------------------------------- * FreeUnsetTraceVariable -- * * Unset trace variable. * * Results: * Tcl return code * * Side effects: * Might unset variable * *---------------------------------------------------------------------- */ static int FreeUnsetTraceVariable(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static int FreeUnsetTraceVariable(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); if (object->opt && object->opt->volatileVarName) { int result = Tcl_UnsetVar2(interp, object->opt->volatileVarName, NULL, 0); /* * Somebody destroys a volatile object manually while the vartrace is * still active. Destroying the object will be a problem in case the * variable is deleted later and fires the trace. So, we unset the * variable here which will cause a destroy via var trace, which in turn * clears the volatileVarName flag. */ /* fprintf(stderr, "### FreeUnsetTraceVariable %s\n", object->opt->volatileVarName);*/ if (unlikely(result != TCL_OK)) { int result = Tcl_UnsetVar2(interp, object->opt->volatileVarName, NULL, TCL_GLOBAL_ONLY); if (unlikely(result != TCL_OK)) { Tcl_Namespace *nsPtr = Tcl_GetCurrentNamespace(interp); if (UnsetInAllNamespaces(interp, nsPtr, object->opt->volatileVarName) == 0) { fprintf(stderr, "### don't know how to delete variable '%s' of volatile object\n", object->opt->volatileVarName); /* * Return always success, since an error during destroy does not * help at all */ } } } /*fprintf(stderr, "### FreeUnsetTraceVariable returns %d OK %d\n", result, TCL_OK);*/ } return TCL_OK; } /* *---------------------------------------------------------------------- * NsfUnsetTrace -- * * Function to be triggered whenever the trigger variable is * deleted. Typically this function deletes the associated object. * * Results: * Result msg or null * * Side effects: * Might delete associated object * *---------------------------------------------------------------------- */ static char *NsfUnsetTrace(ClientData clientData, Tcl_Interp *interp, CONST char *UNUSED(name), CONST char *UNUSED(name2), unsigned int flags) nonnull(1) nonnull(2); static char * NsfUnsetTrace(ClientData clientData, Tcl_Interp *interp, CONST char *UNUSED(name), CONST char *UNUSED(name2), unsigned int flags) { Tcl_Obj *objPtr = (Tcl_Obj *)clientData; NsfObject *object; char *resultMsg = NULL; assert(clientData); assert(interp); /*fprintf(stderr, "NsfUnsetTrace %s flags %.4x %.4x\n", name, flags, flags & TCL_INTERP_DESTROYED);*/ if ((flags & TCL_INTERP_DESTROYED) == 0) { if (GetObjectFromObj(interp, objPtr, &object) == TCL_OK) { Tcl_Obj *savedResultObj = Tcl_GetObjResult(interp); /* save the result */ INCR_REF_COUNT(savedResultObj); /* clear variable, destroy is called from trace */ if (object->opt && object->opt->volatileVarName) { object->opt->volatileVarName = NULL; } if (DispatchDestroyMethod(interp, object, 0) != TCL_OK) { resultMsg = "Destroy for volatile object failed"; } else { resultMsg = "No nsf Object passed"; } Tcl_SetObjResult(interp, savedResultObj); /* restore the result */ DECR_REF_COUNT(savedResultObj); } DECR_REF_COUNT(objPtr); } else { /*fprintf(stderr, "omitting destroy on %s %p\n", name);*/ } return resultMsg; } /* *---------------------------------------------------------------------- * CleanupDestroyObject -- * * Perform cleanup of object; after the function is executed, the object is * in the same fresh state as after initialization. * * Results: * None. * * Side effects: * Possibly freeing memory. * *---------------------------------------------------------------------- */ static void CleanupDestroyObject(Tcl_Interp *interp, NsfObject *object, int softrecreate) nonnull(1) nonnull(2); static void CleanupDestroyObject(Tcl_Interp *interp, NsfObject *object, int softrecreate) { assert(interp); assert(object); /*fprintf(stderr, "CleanupDestroyObject obj %p softrecreate %d nsPtr %p\n", object, softrecreate, object->nsPtr);*/ /* * The object pointer is guaranteed to point to the same object, so it is * not sufficient for methodObj validation. Therefore, for objects * containing per-object methods, we increment the objectMethodEpoch. */ if (object->nsPtr) { NsfObjectMethodEpochIncr("CleanupDestroyObject"); } /* * Remove the instance, but not for ::Class/::Object */ if (IsBaseClass(object) == 0) { if (!softrecreate) { RemoveInstance(object, object->cl); } } if (object->nsPtr) { NSCleanupNamespace(interp, object->nsPtr); NSDeleteChildren(interp, object->nsPtr); } if (object->varTablePtr) { TclDeleteVars(((Interp *)interp), object->varTablePtr); ckfree((char *)object->varTablePtr); /*FREE(obj->varTablePtr, obj->varTablePtr);*/ object->varTablePtr = 0; } if (object->opt) { NsfObjectOpt *opt = object->opt; #if defined(NSF_WITH_ASSERTIONS) if (opt->assertions) { AssertionRemoveStore(opt->assertions); opt->assertions = NULL; } #endif #if defined(PER_OBJECT_PARAMETER_CACHING) if (object->opt->parsedParamPtr) { NsfParameterCacheObjectInvalidateCmd(interp, object); } #endif if (!softrecreate) { /* * Remove this object from all per object mixin lists and clear the * mixin list. */ if (opt->objMixins) RemoveFromObjectMixinsOf(object->id, opt->objMixins); CmdListFree(&opt->objMixins, GuardDel); CmdListFree(&opt->objFilters, GuardDel); FREE(NsfObjectOpt, opt); object->opt = 0; } } object->flags &= ~NSF_MIXIN_ORDER_VALID; if (object->mixinOrder) MixinResetOrder(object); object->flags &= ~NSF_FILTER_ORDER_VALID; if (object->filterOrder) FilterResetOrder(object); } /* * obj initialization & namespace creation */ /* *---------------------------------------------------------------------- * CleanupInitObject -- * * Perform the initialization of an object in a virgin state. * During bootstrap, cl might be NULL. * * Results: * None. * * Side effects: * Updateing the object structure * *---------------------------------------------------------------------- */ static void CleanupInitObject(Tcl_Interp *interp, NsfObject *object, NsfClass *cl, Tcl_Namespace *nsPtr, int softrecreate) nonnull(1) nonnull(2); static void CleanupInitObject(Tcl_Interp *interp, NsfObject *object, NsfClass *cl, Tcl_Namespace *nsPtr, int softrecreate) { assert(interp); assert(object); #ifdef OBJDELETION_TRACE fprintf(stderr, "+++ CleanupInitObject\n"); #endif object->teardown = interp; object->nsPtr = nsPtr; if (!softrecreate && cl != NULL) { AddInstance(object, cl); } if (object->flags & NSF_RECREATE) { object->opt = NULL; object->varTablePtr = NULL; object->mixinOrder = NULL; object->filterOrder = NULL; object->flags = 0; } /* fprintf(stderr, "cleanupInitObject %s: %p cl = %p\n", obj->cmdName ? ObjectName(object) : "", object, object->cl);*/ } /* *---------------------------------------------------------------------- * PrimitiveDestroy -- * * Dispatch either PrimitiveCDestroy or PrimitiveODestroy * depending on whether the object is a class * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void PrimitiveDestroy(ClientData clientData) { assert(clientData); if (NsfObjectIsClass((NsfObject *)clientData)) { PrimitiveCDestroy(clientData); } else { PrimitiveODestroy(clientData); } } /* *---------------------------------------------------------------------- * TclDeletesObject -- * * Function to be called, when Tcl deletes the command which has an * object/class associated. This happens, when e.g. a namespace is deleted. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void TclDeletesObject(ClientData clientData) nonnull(1); static void TclDeletesObject(ClientData clientData) { NsfObject *object = (NsfObject *)clientData; Tcl_Interp *interp; assert(clientData); /* * TODO: Actually, it seems like a good idea to flag a deletion from Tcl by * setting object->id to NULL. However, we seem to have some dependencies * avoiding this currently, so we use the flag. */ object->flags |= NSF_TCL_DELETE; /*fprintf(stderr, "cmd dealloc %p TclDeletesObject (%d)\n", object->id, Tcl_Command_refCount(object->id));*/ #ifdef OBJDELETION_TRACE fprintf(stderr, "TclDeletesObject %p obj->id %p flags %.6x\n", object, object->id, object->flags); #endif if ((object->flags & NSF_DURING_DELETE) || !object->teardown) return; interp = object->teardown; # ifdef OBJDELETION_TRACE fprintf(stderr, "... %p %s\n", object, ObjectName(object)); # endif CallStackDestroyObject(interp, object); } /* *---------------------------------------------------------------------- * PrimitiveODestroy -- * * Delete an object with its namespace and associated data structures * (mixin stack, filter stack). The physical deallocation is handled by * NsfCleanupObject() which performs reference counting. * * Results: * None. * * Side effects: * Free obejct contents. * *---------------------------------------------------------------------- */ static void PrimitiveODestroy(ClientData clientData) { NsfObject *object = (NsfObject *)clientData; Tcl_Interp *interp; assert(clientData); assert(object->teardown); /*fprintf(stderr, "****** PrimitiveODestroy %p cmd %p flags %.6x\n", object, object->id, object->flags);*/ /* * We assume, the object was not yet deleted, but destroy was called * already. */ assert(!(object->flags & NSF_DELETED)); assert(object->flags & NSF_DESTROY_CALLED); /* * Check and latch against recurrent calls with object->teardown. */ PRINTOBJ("PrimitiveODestroy", object); interp = object->teardown; /* * Don't destroy, if the interpreter is destroyed already * e.g. TK calls Tcl_DeleteInterp directly, if the window is killed */ if (Tcl_InterpDeleted(interp)) return; #ifdef OBJDELETION_TRACE {Command *cmdPtr = object->id; fprintf(stderr, " physical delete of %p id=%p (cmd->refCount %d) destroyCalled=%d '%s'\n", object, object->id, cmdPtr->refCount, (object->flags & NSF_DESTROY_CALLED), ObjectName(object)); } #endif CleanupDestroyObject(interp, object, 0); while (object->mixinStack) { MixinStackPop(object); } while (object->filterStack) { FilterStackPop(object); } /* * Object is now mostly dead, but still allocated. However, since * Nsf_DeleteNamespace might delegate to the parent (e.g. slots) we clear * teardown after the deletion of the children. */ if (object->nsPtr) { /*fprintf(stderr, "PrimitiveODestroy calls deleteNamespace for object %p nsPtr %p\n", object, object->nsPtr);*/ Nsf_DeleteNamespace(interp, object->nsPtr); object->nsPtr = NULL; } object->teardown = NULL; /*fprintf(stderr, " +++ OBJ/CLS free: %p %s\n", object, ObjectName(object));*/ object->flags |= NSF_DELETED; ObjTrace("ODestroy", object); DECR_REF_COUNT(object->cmdName); NsfCleanupObject(object, "PrimitiveODestroy"); } /* *---------------------------------------------------------------------- * DoDealloc -- * * Perform deallocation of an object/class. This function is called * from the dealloc method and internally to get rid of an * abject. It cares about volatile and frees/triggers free * operation depending on the stack references. * * Results: * Tcl return code * * Side effects: * freed object or object is marked to be freed. * *---------------------------------------------------------------------- */ static int DoDealloc(Tcl_Interp *interp, NsfObject *object) nonnull(1) nonnull(2); static int DoDealloc(Tcl_Interp *interp, NsfObject *object) { int result; assert(interp); assert(object); /*fprintf(stderr, "DoDealloc obj= %s %p flags %.6x activation %d cmd %p opt=%p\n", ObjectName(object), object, object->flags, object->activationCount, object->id, object->opt);*/ result = FreeUnsetTraceVariable(interp, object); if (unlikely(result != TCL_OK)) { return result; } /* * Latch, and call delete command if not already in progress. */ if (RUNTIME_STATE(interp)->exitHandlerDestroyRound != NSF_EXITHANDLER_ON_SOFT_DESTROY) { CallStackDestroyObject(interp, object); } return TCL_OK; } /* *---------------------------------------------------------------------- * MarkUndestroyed -- * * Mark an object as if destroy was not called. This function is e.g. used * from recreate. * * Results: * None * * Side effects: * Setting object flag. * *---------------------------------------------------------------------- */ static void MarkUndestroyed(NsfObject *object) nonnull(1); static void MarkUndestroyed(NsfObject *object) { assert(object); object->flags &= ~NSF_DESTROY_CALLED; } /* *---------------------------------------------------------------------- * PrimitiveOInit -- * * Set/reset the object to a fresh, un-destroyed state * * Results: * Tcl return code * * Side effects: * initializing object structure * *---------------------------------------------------------------------- */ static void PrimitiveOInit(NsfObject *object, Tcl_Interp *interp, CONST char *name, Tcl_Namespace *nsPtr, NsfClass *cl) nonnull(1) nonnull(2) nonnull(3); static void PrimitiveOInit(NsfObject *object, Tcl_Interp *interp, CONST char *name, Tcl_Namespace *nsPtr, NsfClass *cl) { assert(object); assert(interp); assert(name); #ifdef OBJDELETION_TRACE fprintf(stderr, "+++ PrimitiveOInit\n"); #endif #ifdef NSFOBJ_TRACE fprintf(stderr, "OINIT %s = %p\n", name, object); #endif NsfObjectRefCountIncr(object); MarkUndestroyed(object); /* * There might be already a namespace with name name; if this is the * case, use this namespace as object namespace. The preexisting * namespace might contain Next Scripting objects. If we would not use the * namespace as child namespace, we would not recognize the objects * as child objects, deletions of the object might lead to a crash. * * We can use here the provided nsPtr, except in cases, where this * namespaces is being destroyed (e.g. recreate a new object from a * different object system). */ if (nsPtr && (((Namespace *)nsPtr)->flags & NS_DYING)) { Namespace *dummy1Ptr, *dummy2Ptr, *nsPtr1 = (Namespace *)nsPtr; const char *dummy; TclGetNamespaceForQualName(interp, name, NULL, TCL_GLOBAL_ONLY|TCL_FIND_ONLY_NS, &nsPtr1, &dummy1Ptr, &dummy2Ptr, &dummy); nsPtr = (Tcl_Namespace *)nsPtr1; /*fprintf(stderr, "PrimitiveOInit %p calls TclGetNamespaceForQualName with %s => %p given %p object->nsPtr %p\n", object, name, nsPtr, nsPtr, object->nsPtr);*/ } if (nsPtr) { NsfNamespaceInit(nsPtr); } /* fprintf(stderr, "PrimitiveOInit %p %s, ns %p\n", object, name, nsPtr); */ CleanupInitObject(interp, object, cl, nsPtr, 0); /* TODO: would be nice, if we could init object flags */ /* object->flags = NSF_MIXIN_ORDER_VALID | NSF_FILTER_ORDER_VALID;*/ object->mixinStack = NULL; object->filterStack = NULL; } /* *---------------------------------------------------------------------- * PrimitiveOCreate -- * * Allocate memory for an object, create the object name and the associated * Tcl command and call the initialization functions. * * Results: * NsfObject* * * Side effects: * Allocating memory * *---------------------------------------------------------------------- */ static NsfObject * PrimitiveOCreate(Tcl_Interp *interp, Tcl_Obj *nameObj, Tcl_Namespace *parentNsPtr, NsfClass *cl) nonnull(1) nonnull(2) nonnull(4) returns_nonnull; static NsfObject * PrimitiveOCreate(Tcl_Interp *interp, Tcl_Obj *nameObj, Tcl_Namespace *parentNsPtr, NsfClass *cl) { CONST char *nameString; Tcl_Namespace *nsPtr; NsfObject *object; assert(interp); assert(nameObj); assert(cl); object = (NsfObject *)ckalloc(sizeof(NsfObject)); memset(object, 0, sizeof(NsfObject)); MEM_COUNT_ALLOC("NsfObject/NsfClass", object); assert(object); /* ckalloc panics, if malloc fails */ nameString = ObjStr(nameObj); assert(isAbsolutePath(nameString)); #if defined(NSFOBJ_TRACE) fprintf(stderr, "CKALLOC Object %p %s\n", object, nameString); #endif #ifdef OBJDELETION_TRACE fprintf(stderr, "+++ PrimitiveOCreate\n"); #endif nsPtr = NSCheckNamespace(interp, nameString, parentNsPtr); if (nsPtr) { NSNamespacePreserve(nsPtr); } #if defined(NRE) object->id = Tcl_NRCreateCommand(interp, nameString, NsfObjDispatch, NsfObjDispatchNRE, object, TclDeletesObject); #else object->id = Tcl_CreateObjCommand(interp, nameString, NsfObjDispatch, object, TclDeletesObject); #endif /*fprintf(stderr, "cmd alloc %p %d (%s)\n", object->id, Tcl_Command_refCount(object->id), nameString);*/ PrimitiveOInit(object, interp, nameString, nsPtr, cl); if (nsPtr) { NSNamespaceRelease(nsPtr); } object->cmdName = nameObj; INCR_REF_COUNT(object->cmdName); ObjTrace("PrimitiveOCreate", object); return object; } /* *---------------------------------------------------------------------- * DefaultSuperClass -- * * Determine the default Superclass of the class (specified as * second argument) and meta class (third argument). The function * searches for the variable NSF_DEFAULTMETACLASS or * NSF_DEFAULTSUPERCLASS and uses it if present. * * Results: * Default superclass or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfClass * DefaultSuperClass(Tcl_Interp *interp, NsfClass *cl, NsfClass *mcl, int isMeta) { NsfClass *resultClass = NULL; Tcl_Obj *resultObj; assert(interp); assert(cl); assert(mcl); /*fprintf(stderr, "DefaultSuperClass cl %s, mcl %s, isMeta %d\n", ClassName(cl), ClassName(mcl), isMeta );*/ resultObj = Nsf_ObjGetVar2((Nsf_Object *)mcl, interp, isMeta ? NsfGlobalObjs[NSF_DEFAULTMETACLASS] : NsfGlobalObjs[NSF_DEFAULTSUPERCLASS], NULL, 0); if (resultObj) { if (unlikely(GetClassFromObj(interp, resultObj, &resultClass, 0) != TCL_OK)) { NsfPrintError(interp, "default superclass is not a class"); } /* fprintf(stderr, "DefaultSuperClass for %s got from var %s\n", ClassName(cl), ObjStr(nameObj)); */ } else { NsfClasses *sc; /* fprintf(stderr, "DefaultSuperClass for %s: search in superClasses starting with %p meta %d\n", ClassName(cl), cl->super, isMeta); */ if (isMeta) { /* * Is this already the root metaclass ? */ if (IsRootMetaClass(mcl->object.cl)) { return mcl->object.cl; } } /* * check superClasses of metaclass */ for (sc = mcl->super; sc && sc->cl != cl; sc = sc->nextPtr) { /* fprintf(stderr, " ... check ismeta %d %s root mcl %d root cl %d\n", isMeta, ClassName(sc->cl), sc->cl->object.flags & NSF_IS_ROOT_META_CLASS, sc->cl->object.flags & NSF_IS_ROOT_CLASS); */ if (isMeta) { if (IsRootMetaClass(sc->cl)) { return sc->cl; } } else { if (IsRootClass(sc->cl)) { /* fprintf(stderr, "found root class %p %s\n", sc->cl, ClassName(sc->cl)); */ return sc->cl; } } resultClass = DefaultSuperClass(interp, cl, sc->cl, isMeta); if (resultClass) { break; } } } return resultClass; } /* *---------------------------------------------------------------------- * CleanupDestroyClass -- * * Cleanup class in a destroy call. Remove filters, mixins, assertions, * instances and remove finally class from class hierarchy. In the recreate * case, it preserves the pointers from other class structures. * * Results: * None. * * Side effects: * Updated class structures. * *---------------------------------------------------------------------- */ static void CleanupDestroyClass(Tcl_Interp *interp, NsfClass *cl, int softrecreate, int recreate) nonnull(1) nonnull(2); static void CleanupDestroyClass(Tcl_Interp *interp, NsfClass *cl, int softrecreate, int recreate) { NsfClassOpt *clopt = cl->opt; NsfClass *baseClass = NULL; NsfClasses *subClasses; assert(interp); assert(cl); PRINTOBJ("CleanupDestroyClass", (NsfObject *)cl); assert(softrecreate ? recreate == 1 : 1); /*fprintf(stderr, "CleanupDestroyClass %p %s (ismeta=%d) softrecreate=%d, recreate=%d, %p\n", cl, ClassName(cl), IsMetaClass(interp, cl, 1), softrecreate, recreate, clopt);*/ subClasses = DependentSubClasses(cl); if (subClasses) { /* * Perform the next steps even with clopt == NULL, since the class * might be used as a superclass of a per object mixin, so it might * have no clopt... */ MixinInvalidateObjOrders(interp, cl, subClasses); if (FiltersDefined(interp) > 0) { FilterInvalidateObjOrders(interp, subClasses); } } if (clopt != NULL) { /* * Remove this class from all isClassMixinOf lists and clear the * class mixin list */ if (clopt->classMixins) { RemoveFromClassMixinsOf(clopt->id, clopt->classMixins); } CmdListFree(&clopt->classMixins, GuardDel); CmdListFree(&clopt->classFilters, GuardDel); if (clopt->mixinRegObjs != NULL) { NsfMixinregInvalidate(interp, clopt->mixinRegObjs); DECR_REF_COUNT2("mixinRegObjs", clopt->mixinRegObjs); clopt->mixinRegObjs = NULL; } if (!recreate) { /* * Remove this class from all mixin lists and clear the isObjectMixinOf list */ if (clopt->isObjectMixinOf) RemoveFromObjectMixins(clopt->id, clopt->isObjectMixinOf); CmdListFree(&clopt->isObjectMixinOf, GuardDel); /* * Remove this class from all class mixin lists and clear the * isClassMixinOf list */ if (clopt->isClassMixinOf) RemoveFromClassmixins(clopt->id, clopt->isClassMixinOf); CmdListFree(&clopt->isClassMixinOf, GuardDel); } /* * Remove dependent filters of this class from all subclasses */ FilterRemoveDependentFilterCmds(cl, subClasses); #if defined(NSF_WITH_ASSERTIONS) if (clopt->assertions) { AssertionRemoveStore(clopt->assertions); clopt->assertions = NULL; } #endif #ifdef NSF_OBJECTDATA NsfFreeObjectData(cl); #endif } NSCleanupNamespace(interp, cl->nsPtr); NSDeleteChildren(interp, cl->nsPtr); if (!softrecreate) { /* * Reclass all instances of the current class the the appropriate * most general class ("baseClass"). The most general class of a * metaclass is the root meta class, the most general class of an * object is the root class. Instances of meta-classes can be only * reset to the root meta class (and not to to the root base * class). */ baseClass = DefaultSuperClass(interp, cl, cl->object.cl, IsMetaClass(interp, cl, 1)); /* * We do not have to reclassing in case, cl is a root class */ if (IsRootClass(cl) == 0) { Tcl_HashTable *instanceTablePtr = &cl->instances; Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; for (hPtr = Tcl_FirstHashEntry(instanceTablePtr, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { NsfObject *inst = (NsfObject *)Tcl_GetHashKey(instanceTablePtr, hPtr); /*fprintf(stderr, " inst %p %s flags %.6x id %p baseClass %p %s\n", inst, ObjectName(inst), inst->flags, inst->id, baseClass, ClassName(baseClass));*/ if (inst && inst != (NsfObject *)cl && !(inst->flags & NSF_DURING_DELETE) /*inst->id*/) { if (inst != &(baseClass->object)) { AddInstance(inst, baseClass); } } } } Tcl_DeleteHashTable(&cl->instances); MEM_COUNT_FREE("Tcl_InitHashTable", &cl->instances); } if (clopt != NULL && recreate == 0) { FREE(NsfClassOpt, clopt); cl->opt = NULL; } if (subClasses) { /* * On a recreate, it might be possible that the newly created class * has a different superclass. So we have to flush the precedence * list on a recreate as well. */ FlushPrecedences(subClasses); NsfClassListFree(subClasses); } while (cl->super) { (void)RemoveSuper(cl, cl->super->cl); } if (!softrecreate) { /* * flush all caches, unlink superClasses */ while (cl->sub) { NsfClass *subClass = cl->sub->cl; (void)RemoveSuper(subClass, cl); /* * If there are no more super classes add the Object * class as superClasses * -> don't do that for Object itself! */ if (subClass->super == 0 && IsRootClass(cl) == 0) { /* fprintf(stderr,"subClass %p %s baseClass %p %s\n", cl, ClassName(cl), baseClass, ClassName(baseClass)); */ AddSuper(subClass, baseClass); } } /*(void)RemoveSuper(cl, cl->super->cl);*/ } } /* *---------------------------------------------------------------------- * CleanupInitClass -- * * Basic initialization of a class, setting namespace, super- and * sub-classes, and setup optionally instances table. * * Results: * None. * * Side effects: * Makes a class structure useable. * *---------------------------------------------------------------------- */ static void CleanupInitClass(Tcl_Interp *interp, NsfClass *cl, Tcl_Namespace *nsPtr, int softrecreate, int recreate) nonnull(1) nonnull(2) nonnull(3); static void CleanupInitClass(Tcl_Interp *interp, NsfClass *cl, Tcl_Namespace *nsPtr, int softrecreate, int recreate) { NsfClass *defaultSuperclass; assert(interp); assert(cl); assert(nsPtr); assert(softrecreate ? recreate == 1 : 1); #ifdef OBJDELETION_TRACE fprintf(stderr, "+++ CleanupInitClass\n"); #endif /* * Record, that cl is a class and set its namespace */ NsfObjectSetClass((NsfObject *)cl); cl->nsPtr = nsPtr; if (!softrecreate) { /* * Subclasses are preserved during recreate, superClasses not (since the * creation statement defined the superclass, might be different the * second time) */ cl->sub = NULL; } cl->super = NULL; /* * We can can the default superclass from the metaclass, if this exists. */ if (cl->object.cl != NULL) { /* Look for a configured default superclass */ defaultSuperclass = DefaultSuperClass(interp, cl, cl->object.cl, 0); } else { defaultSuperclass = NULL; } if (cl != defaultSuperclass) { AddSuper(cl, defaultSuperclass); } cl->color = WHITE; cl->order = NULL; if (!softrecreate) { Tcl_InitHashTable(&cl->instances, TCL_ONE_WORD_KEYS); MEM_COUNT_ALLOC("Tcl_InitHashTable", &cl->instances); } if (!recreate) { cl->opt = NULL; } } /* *---------------------------------------------------------------------- * PrimitiveCDestroy -- * * Delete a class with its namespace and associated data structures. The * physical deallocation is handled by PrimitiveODestroy() * * Results: * None. * * Side effects: * Free obejct contents. * *---------------------------------------------------------------------- */ static void PrimitiveCDestroy(ClientData clientData) { NsfClass *cl = (NsfClass *)clientData; NsfObject *object = (NsfObject *)clientData; Tcl_Interp *interp; Tcl_Namespace *saved; assert(clientData); PRINTOBJ("PrimitiveCDestroy", object); /* * check and latch against recurrent calls with obj->teardown */ if (object == NULL || object->teardown == NULL) return; interp = object->teardown; /* * Don't destroy, if the interpreted is destroyed already * e.g. TK calls Tcl_DeleteInterp directly, if Window is killed */ if (Tcl_InterpDeleted(interp)) return; /* * call and latch user destroy with object->id if we haven't */ /*fprintf(stderr, "PrimitiveCDestroy %s flags %.6x\n", ObjectName(object), object->flags);*/ object->teardown = NULL; CleanupDestroyClass(interp, cl, 0, 0); /* * handoff the primitive teardown */ saved = cl->nsPtr; object->teardown = interp; /* * class object destroy + physical destroy */ PrimitiveODestroy(clientData); /*fprintf(stderr, "primitive cdestroy calls delete namespace for obj %p, nsPtr %p flags %.6x\n", cl, saved, ((Namespace *)saved)->flags);*/ Nsf_DeleteNamespace(interp, saved); /*fprintf(stderr, "primitive cdestroy %p DONE\n", cl);*/ return; } /* *---------------------------------------------------------------------- * PrimitiveCInit -- * * Set/reset a class to a fresh, un-destroyed state * * Results: * Tcl return code * * Side effects: * initializing object structure * *---------------------------------------------------------------------- */ static void PrimitiveCInit(NsfClass *cl, Tcl_Interp *interp, CONST char *name) nonnull(1) nonnull(2) nonnull(3); static void PrimitiveCInit(NsfClass *cl, Tcl_Interp *interp, CONST char *name) { Tcl_CallFrame frame, *framePtr = &frame; Tcl_Namespace *nsPtr; assert(cl); assert(interp); assert(name); /* * Ensure that namespace is newly created during CleanupInitClass. Kill it, * if it exists already */ if (Tcl_PushCallFrame(interp, (Tcl_CallFrame *)framePtr, RUNTIME_STATE(interp)->NsfClassesNS, 0) != TCL_OK) { return; } nsPtr = NSGetFreshNamespace(interp, &cl->object, name); Tcl_PopCallFrame(interp); CleanupInitClass(interp, cl, nsPtr, 0, 0); return; } /* *---------------------------------------------------------------------- * PrimitiveCCreate -- * * Allocate memory for a class, initialize the class specific data * structure (eg. class namespace) and call PrimitiveOCreate() for the * object specific initialization. * * Results: * NsfClass* * * Side effects: * Allocating memory * *---------------------------------------------------------------------- */ static NsfClass *PrimitiveCCreate(Tcl_Interp *interp, Tcl_Obj *nameObj, Tcl_Namespace *parentNsPtr, NsfClass *metaClass) nonnull(1) nonnull(2) returns_nonnull; static NsfClass * PrimitiveCCreate(Tcl_Interp *interp, Tcl_Obj *nameObj, Tcl_Namespace *parentNsPtr, NsfClass *metaClass) { Tcl_Namespace *nsPtr; CONST char *nameString; NsfObject *object; NsfClass *cl; assert(interp); assert(nameObj); cl = (NsfClass *)ckalloc(sizeof(NsfClass)); nameString = ObjStr(nameObj); object = (NsfObject *)cl; #if defined(NSFOBJ_TRACE) fprintf(stderr, "CKALLOC Class %p %s\n", cl, nameString); #endif memset(cl, 0, sizeof(NsfClass)); MEM_COUNT_ALLOC("NsfObject/NsfClass", cl); /* pass object system from meta class */ if (metaClass) { cl->osPtr = metaClass->osPtr; } assert(isAbsolutePath(nameString)); /* fprintf(stderr, "Class alloc %p '%s'\n", cl, nameString); */ nsPtr = NSCheckNamespace(interp, nameString, parentNsPtr); if (nsPtr) { NSNamespacePreserve(nsPtr); } #if defined(NRE) object->id = Tcl_NRCreateCommand(interp, nameString, NsfObjDispatch, NsfObjDispatchNRE, cl, TclDeletesObject); #else object->id = Tcl_CreateObjCommand(interp, nameString, NsfObjDispatch, cl, TclDeletesObject); #endif PrimitiveOInit(object, interp, nameString, nsPtr, metaClass); if (nsPtr) { NSNamespaceRelease(nsPtr); } object->cmdName = nameObj; /* convert cmdName to Tcl Obj of type cmdName */ /* Tcl_GetCommandFromObj(interp, obj->cmdName);*/ INCR_REF_COUNT(object->cmdName); PrimitiveCInit(cl, interp, nameString+2); ObjTrace("PrimitiveCCreate", object); return cl; } /* *---------------------------------------------------------------------- * ChangeClass -- * * Change class of a Next Scripting object. This function takes * care that one tries not to change an object into a class or vice * versa. Changing meta-class to meta-class, or class to class, or * object to object is fine, but upgrading/downgrading is not * allowed * * Results: * Tcl return code * * Side effects: * changes class of object if possible and updates instance relation. * *---------------------------------------------------------------------- */ NSF_INLINE static int ChangeClass(Tcl_Interp *interp, NsfObject *object, NsfClass *cl) nonnull(1) nonnull(2) nonnull(3); NSF_INLINE static int ChangeClass(Tcl_Interp *interp, NsfObject *object, NsfClass *cl) { assert(interp); assert(object); assert(cl); NsfInstanceMethodEpochIncr("ChangeClass"); /*fprintf(stderr, "changing %s to class %s ismeta %d\n", ObjectName(object), ClassName(cl), IsMetaClass(interp, cl, 1));*/ if (cl != object->cl) { if (IsMetaClass(interp, cl, 1)) { /* Do not allow upgrading from a class to a meta-class (in other words, don't make an object to a class). To allow this, it would be necessary to reallocate the base structures. */ if (!IsMetaClass(interp, object->cl, 1)) { return NsfPrintError(interp, "cannot turn object into a class"); } } else { /* The target class is not a meta class. */ /*fprintf(stderr, "target class %s not a meta class, am i a class %d\n", ClassName(cl), NsfObjectIsClass(object) );*/ if (NsfObjectIsClass(object)) { return NsfPrintError(interp, "cannot turn class into an object "); } } RemoveInstance(object, object->cl); AddInstance(object, cl); MixinComputeDefined(interp, object); FilterComputeDefined(interp, object); } return TCL_OK; } /* *---------------------------------------------------------------------- * DoObjInitialization -- * * Perform the object initialization: first call "configure" and the * constructor "init", if not called already from configure. The function * will make sure that the called methods do not change the result passed * into this function. * * Results: * Tcl return code * * Side effects: * Indirect effects by calling Tcl code * *---------------------------------------------------------------------- */ static int DoObjInitialization(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(4); static int DoObjInitialization(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[]) { Tcl_Obj *methodObj, *savedObjResult; int result; assert(interp); assert(object); assert(objv); assert(objc >= 0); #if 0 { int i; fprintf(stderr, "DoObjInitialization objc %d: ",objc); for(i = 0; i < objc; i++) {fprintf(stderr, " [%d]=%s,", i, ObjStr(objv[i]));} fprintf(stderr, "\n"); } #endif /* * Save the result we have so far to return it in case of success */ savedObjResult = Tcl_GetObjResult(interp); INCR_REF_COUNT(savedObjResult); /* * clear INIT_CALLED flag */ object->flags &= ~NSF_INIT_CALLED; /* * Make sure, the object survives initialization; the cmd/initcmd might * destroy it. */ NsfObjectRefCountIncr(object); /* * Call configure method */ if (CallDirectly(interp, object, NSF_o_configure_idx, &methodObj)) { if (methodObj == NULL) { methodObj = NsfGlobalObjs[NSF_CONFIGURE]; } assert(methodObj); /* methodObjd is just for error reporting */ result = NsfOConfigureMethod(interp, object, objc, objv, methodObj); } else { result = CallMethod(object, interp, methodObj, objc+2, objv, NSF_CSC_IMMEDIATE); } if (likely(result == TCL_OK)) { /* * Call constructor when needed */ if (!(object->flags & (NSF_INIT_CALLED|NSF_DESTROY_CALLED))) { result = DispatchInitMethod(interp, object, 0, NULL, 0); } if (likely(result == TCL_OK)) { Tcl_SetObjResult(interp, savedObjResult); } } else { /* * Configure failed and might have left the object in a bogus state. To * avoid strange errors, we delete the half-baked object. */ Tcl_Obj *errObj; /* * Preserve the outer error message, calls triggered by * DispatchDestroyMethod() can cause the interp result to be reset */ errObj = Tcl_GetObjResult(interp); INCR_REF_COUNT(errObj); DispatchDestroyMethod(interp, (NsfObject *)object, 0); Tcl_SetObjResult(interp, errObj); DECR_REF_COUNT(errObj); } NsfCleanupObject(object, "obj init"); DECR_REF_COUNT(savedObjResult); return result; } /* *---------------------------------------------------------------------- * IsRootMetaClass -- * * Check, of the class has the Root meta class flag set. * * Results: * Boolean * * Side effects: * None * *---------------------------------------------------------------------- */ static int IsRootMetaClass(NsfClass *cl) { assert(cl); return cl->object.flags & NSF_IS_ROOT_META_CLASS; } /* *---------------------------------------------------------------------- * IsBaseClass -- * * Check, whether the object is a base class. * * Results: * Boolean * * Side effects: * none * *---------------------------------------------------------------------- */ static int IsBaseClass(NsfObject *object) { assert(object); return object->flags & (NSF_IS_ROOT_CLASS|NSF_IS_ROOT_META_CLASS); } /* *---------------------------------------------------------------------- * IsRootClass -- * * Check, whether the object is a root class. * * Results: * Boolean * * Side effects: * none * *---------------------------------------------------------------------- */ static int IsRootClass(NsfClass *cls) { assert(cls); return cls->object.flags & (NSF_IS_ROOT_CLASS); } /* *---------------------------------------------------------------------- * IsMetaClass -- * * Check, whether the object is a meta class. Optionally, mixins are * checked as well. * * Results: * Boolean * * Side effects: * none * *---------------------------------------------------------------------- */ static int IsMetaClass(Tcl_Interp *interp, NsfClass *cl, int withMixins) { NsfClasses *pl; assert(interp); assert(cl); /* is the class the most general meta-class? */ if (IsRootMetaClass(cl)) { return 1; } /* is the class a subclass of a meta-class? */ for (pl = PrecedenceOrder(cl); pl; pl = pl->nextPtr) { if (IsRootMetaClass(pl->cl)) { return 1; } } if (withMixins) { NsfClasses *checkList = NULL, *mixinClasses = NULL, *mc; int hasMCM = 0; /* has the class metaclass mixed in? */ NsfClassListAddPerClassMixins(interp, cl, &mixinClasses, &checkList); for (mc = mixinClasses; mc; mc = mc->nextPtr) { if (IsMetaClass(interp, mc->cl, 0)) { hasMCM = 1; break; } } if (mixinClasses) NsfClassListFree(mixinClasses); if (checkList) NsfClassListFree(checkList); /*fprintf(stderr, "has MC returns %d, mixinClasses = %p\n", hasMCM, mixinClasses);*/ return hasMCM; } else { return 0; } } /* *---------------------------------------------------------------------- * IsSubType -- * * Check, whether a class is a subclass of another class * * Results: * Boolean * * Side effects: * none * *---------------------------------------------------------------------- */ static int IsSubType(NsfClass *subcl, NsfClass *cl) { assert(subcl); assert(cl); if (cl != subcl) { return NsfClassListFind(PrecedenceOrder(subcl), cl) != NULL; } return 1; } /* *---------------------------------------------------------------------- * HasMixin -- * * Check, whether the specified object the the specified class as mixin. * * Results: * Boolean * * Side effects: * none * *---------------------------------------------------------------------- */ static int HasMixin(Tcl_Interp *interp, NsfObject *object, NsfClass *cl) nonnull(1) nonnull(2) nonnull(3); static int HasMixin(Tcl_Interp *interp, NsfObject *object, NsfClass *cl) { assert(interp); assert(object); assert(cl); if (!(object->flags & NSF_MIXIN_ORDER_VALID)) { MixinComputeDefined(interp, object); } if ((object->flags & NSF_MIXIN_ORDER_DEFINED_AND_VALID)) { NsfCmdList *ml; for (ml = object->mixinOrder; ml; ml = ml->nextPtr) { NsfClass *mixin = NsfGetClassFromCmdPtr(ml->cmdPtr); if (mixin == cl) { return 1; } } } return 0; } /* *---------------------------------------------------------------------- * ImportInstVarIntoCurrentScope -- * * Import an instance variable into the corrent variable scope * (e.g. function scope). * * Results: * Boolean * * Side effects: * none * *---------------------------------------------------------------------- */ static int ImportInstVarIntoCurrentScope(Tcl_Interp *interp, const char *cmdName, NsfObject *object, Tcl_Obj *varName, Tcl_Obj *newName) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int ImportInstVarIntoCurrentScope(Tcl_Interp *interp, const char *cmdName, NsfObject *object, Tcl_Obj *varName, Tcl_Obj *newName) { Var *otherPtr = NULL, *arrayPtr; unsigned int flogs = TCL_LEAVE_ERR_MSG; Tcl_CallFrame *varFramePtr; CallFrame frame, *framePtr = &frame; char *varNameString; assert(interp); assert(cmdName); assert(object); assert(varName); if (CheckVarName(interp, ObjStr(varName)) != TCL_OK) { return TCL_ERROR; } Nsf_PushFrameObj(interp, object, framePtr); if (object->nsPtr) { flogs = flogs|TCL_NAMESPACE_ONLY; } otherPtr = TclObjLookupVar(interp, varName, NULL, flogs, "define", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr); Nsf_PopFrameObj(interp, framePtr); if (unlikely(otherPtr == NULL)) { return NsfPrintError(interp, "can't import variable %s into method scope: " "can't find variable on %s", ObjStr(varName), ObjectName(object)); } /* * if newName == NULL -> there is no alias, use varName * as target link name */ if (newName == NULL) { /* * Variable link into namespace cannot be an element in an array. * see Tcl_VariableObjCmd ... */ if (arrayPtr) { return NsfPrintError(interp, "can't make instance variable %s on %s: " "Variable cannot be an element in an array; use e.g. an alias.", ObjStr(varName), ObjectName(object)); } newName = varName; } varNameString = ObjStr(newName); varFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); /* * If we are executing inside a Tcl procedure, create a local * variable linked to the new namespace variable "varName". */ if (varFramePtr && (Tcl_CallFrame_isProcCallFrame(varFramePtr) & FRAME_IS_PROC)) { Var *varPtr = (Var *)CompiledLocalsLookup((CallFrame *)varFramePtr, varNameString); int new = 0; if (varPtr == NULL) { /* * Look in frame's local var hash-table. */ TclVarHashTable *varTablePtr = Tcl_CallFrame_varTablePtr(varFramePtr); if (varTablePtr == NULL) { /* * The variable table does not exist. This seems to be is the * first access to a variable on this frame. We create the and * initialize the variable hash-table and update the object */ /*fprintf(stderr, "+++ create varTable in ImportInstVarIntoCurrentScope\n");*/ Tcl_CallFrame_varTablePtr(varFramePtr) = varTablePtr = VarHashTableCreate(); } varPtr = VarHashCreateVar(varTablePtr, newName, &new); } /* * If we define an alias (newName != varName), be sure that * the target does not exist already. */ if (!new) { /*fprintf(stderr, "GetIntoScope create alias\n");*/ if (unlikely(varPtr == otherPtr)) { return NsfPrintError(interp, "can't instvar to variable itself"); } if (TclIsVarLink(varPtr)) { /* * We try to make the same instvar again ... this is ok */ Var *linkPtr = TclVarValue(Var, varPtr, linkPtr); if (linkPtr == otherPtr) { return TCL_OK; } /*fprintf(stderr, "linkvar flags=%x\n", linkPtr->flags); Tcl_Panic("new linkvar %s... When does this happen?", ObjStr(newName), NULL);*/ /* * We have already a variable with the same name imported * from a different object. Get rid of this old variable. */ VarHashRefCount(linkPtr)--; if (TclIsVarUndefined(linkPtr)) { TclCleanupVar(linkPtr, (Var *) NULL); } } else if (unlikely(TclIsVarUndefined(varPtr) == 0)) { return NsfPrintError(interp, "varName '%s' exists already", varNameString); } else if (unlikely(TclIsVarTraced(varPtr) != 0)) { return NsfPrintError(interp, "varName '%s' has traces: can't use for instvar", varNameString); } } TclSetVarLink(varPtr); TclClearVarUndefined(varPtr); varPtr->value.linkPtr = otherPtr; VarHashRefCount(otherPtr)++; /* fprintf(stderr, "defining an alias var='%s' in obj %s fwd %d flags %x isLink %d isTraced %d isUndefined %d\n", ObjStr(newName), ObjectName(object), 0, varPtr->flags, TclIsVarLink(varPtr), TclIsVarTraced(varPtr), TclIsVarUndefined(varPtr)); */ } else { return NsfPrintError(interp, "%s cannot import variable '%s' into method scope; " "not called from a method frame", cmdName, varNameString); } return TCL_OK; } /* *---------------------------------------------------------------------- * SetInstVar -- * * Set an instance variable of the specified object to the given value. * * Results: * Tcl result code. * * Side effects: * Set instance variable. * *---------------------------------------------------------------------- */ static int SetInstVar(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *nameObj, Tcl_Obj *valueObj) { CallFrame frame, *framePtr = &frame; Tcl_Obj *resultObj; unsigned int flags; assert(interp); assert(object); assert(nameObj); flags = (object->nsPtr) ? TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY : TCL_LEAVE_ERR_MSG; Nsf_PushFrameObj(interp, object, framePtr); if (likely(valueObj == NULL)) { resultObj = Tcl_ObjGetVar2(interp, nameObj, NULL, flags); } else { /*fprintf(stderr, "setvar in obj %s: name %s = %s\n", ObjectName(object), ObjStr(nameObj), ObjStr(value));*/ resultObj = Tcl_ObjSetVar2(interp, nameObj, NULL, valueObj, flags); } Nsf_PopFrameObj(interp, framePtr); if (resultObj) { Tcl_SetObjResult(interp, resultObj); return TCL_OK; } return TCL_ERROR; } /* *---------------------------------------------------------------------- * SetInstArray -- * * Set an instance variable array of the specified object to the given * value. This function performs essentially an "array set" or "array get" * operation. * * Results: * Tcl result code. * * Side effects: * Set instance variable. * *---------------------------------------------------------------------- */ static int SetInstArray(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *arrayNameObj, Tcl_Obj *valueObj) nonnull(1) nonnull(2) nonnull(3); static int SetInstArray(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *arrayNameObj, Tcl_Obj *valueObj) { CallFrame frame, *framePtr = &frame; int result; Tcl_Obj *ov[4]; assert(interp); assert(object); assert(arrayNameObj); Nsf_PushFrameObj(interp, object, framePtr); ov[0] = NsfGlobalObjs[NSF_ARRAY]; ov[2] = arrayNameObj; INCR_REF_COUNT(arrayNameObj); if (valueObj == NULL) { /* * Perform an array get */ ov[1] = NsfGlobalObjs[NSF_GET]; result = Tcl_EvalObjv(interp, 3, ov, 0); } else { /* * Perform an array get */ ov[1] = NsfGlobalObjs[NSF_SET]; ov[3] = valueObj; INCR_REF_COUNT(valueObj); result = Tcl_EvalObjv(interp, 4, ov, 0); DECR_REF_COUNT(valueObj); } DECR_REF_COUNT(arrayNameObj); Nsf_PopFrameObj(interp, framePtr); return result; } /* *---------------------------------------------------------------------- * UnsetInstVar -- * * Unset an instance variable of the specified object. * * Results: * Tcl result code. * * Side effects: * Variable unset. * *---------------------------------------------------------------------- */ static int UnsetInstVar(Tcl_Interp *interp, int withNocomplain, NsfObject *object, CONST char *name) nonnull(1) nonnull(3) nonnull(4); static int UnsetInstVar(Tcl_Interp *interp, int withNocomplain, NsfObject *object, CONST char *name) { CallFrame frame, *framePtr = &frame; unsigned int flags; int result; assert(interp); assert(object); assert(name); flags = withNocomplain ? 0 : TCL_LEAVE_ERR_MSG; if (object->nsPtr) {flags |= TCL_NAMESPACE_ONLY;} Nsf_PushFrameObj(interp, object, framePtr); result = Tcl_UnsetVar2(interp, name, NULL, flags); Nsf_PopFrameObj(interp, framePtr); return withNocomplain ? TCL_OK : result; } /* *---------------------------------------------------------------------- * NsfSetterMethod -- * * This Tcl_ObjCmdProc is called, when a setter method is invoked. A setter * is a method that accesses/modifies a same-named instance variable. If * the setter is called without arguments, it returns the values, if it is * called with one argument, the argument is used as new value. * * Results: * Tcl result code. * * Side effects: * Can set an instance variable. * *---------------------------------------------------------------------- */ static int NsfSetterMethod(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(4); static int NsfSetterMethod(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { SetterCmdClientData *cd = (SetterCmdClientData *)clientData; NsfObject *object = cd->object; assert(clientData); assert(interp); assert(objv); if (objc > 2) { return NsfObjWrongArgs(interp, "wrong # args", object->cmdName, NsfMethodNamePath(interp, CallStackGetTclFrame(interp, NULL, 1), NsfMethodName(objv[0])), "?value?"); } if (object == NULL) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (cd->paramsPtr && objc == 2) { Tcl_Obj *outObjPtr; int result; unsigned flags = 0; ClientData checkedData; result = ArgumentCheck(interp, objv[1], cd->paramsPtr, RUNTIME_STATE(interp)->doCheckArguments, &flags, &checkedData, &outObjPtr); if (likely(result == TCL_OK)) { result = SetInstVar(interp, object, objv[0], outObjPtr); } if (flags & NSF_PC_MUST_DECR) { DECR_REF_COUNT2("valueObj", outObjPtr); } return result; } else { return SetInstVar(interp, object, objv[0], objc == 2 ? objv[1] : NULL); } } /* *---------------------------------------------------------------------- * NsfForwardPrintError -- * * Helper function to print either an error message directly to call the * forwarder specific callback method specified in * tcd->onerror. Background: ForwardArg() is called at runtime to * substitute the argument list. Catching such errors is not conveniently * doable via catch, since it would be necessary to wrap every possible * usage of a forwarder in a catch. Therefore the callback function can be * used to give a sensible error message appropriate for each context. * * Results: * Tcl result code. * * Side effects: * Potential side effects through the script. * *---------------------------------------------------------------------- */ static int NsfForwardPrintError(Tcl_Interp *interp, ForwardCmdClientData *tcd, int objc, Tcl_Obj *CONST objv[], CONST char *fmt, ...) nonnull(1) nonnull(2) nonnull(5) NSF_attribute_format((printf,5,6)); static int NsfForwardPrintError(Tcl_Interp *interp, ForwardCmdClientData *tcd, int objc, Tcl_Obj *CONST objv[], CONST char *fmt, ...) { Tcl_DString ds; va_list ap; int result; assert(interp); assert(tcd); assert(fmt); Tcl_DStringInit(&ds); va_start(ap, fmt); NsfDStringPrintf(&ds, fmt, ap); va_end(ap); if (tcd->onerror) { Tcl_Obj *script = Tcl_DuplicateObj(tcd->onerror); Tcl_Obj *cmd; if (tcd->object) { cmd = Tcl_DuplicateObj(tcd->object->cmdName); if (objc > 0) { Tcl_ListObjAppendList(interp, cmd, NsfMethodNamePath(interp, CallStackGetTclFrame(interp, NULL, 1), MethodName(objv[0]))); if (objc > 1) { Tcl_ListObjAppendElement(interp, cmd, Tcl_NewListObj(objc-1,objv+1)); } } } else { cmd = Tcl_NewListObj(objc, objv); } Tcl_ListObjAppendElement(interp, script, cmd); Tcl_ListObjAppendElement(interp, script, Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds))); INCR_REF_COUNT(script); result = Tcl_EvalObjEx(interp, script, TCL_EVAL_DIRECT); DECR_REF_COUNT(script); } else { result = NsfPrintError(interp, "%s", Tcl_DStringValue(&ds)); } Tcl_DStringFree(&ds); return result; } /* *---------------------------------------------------------------------- * ForwardArg -- * * This function is a helper function of NsfForwardMethod() and processes a * single entry (ForwardArgObj) of the forward spec. Essentially, it * performs the percent substitution of the forward spec. * * Results: * Tcl result code. * * Side effects: * Updates the provided output arguments. * *---------------------------------------------------------------------- */ static int ForwardArg(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *ForwardArgObj, ForwardCmdClientData *tcd, Tcl_Obj **out, Tcl_Obj **freeList, int *inputArg, int *mapvalue, int firstPosArg, int *outputincr) nonnull(1) nonnull(3) nonnull(4) nonnull(5) nonnull(6) nonnull(7) nonnull(8) nonnull(9) nonnull(11); static int ForwardArg(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *forwardArgObj, ForwardCmdClientData *tcd, Tcl_Obj **out, Tcl_Obj **freeList, int *inputArg, int *mapvalue, int firstPosArg, int *outputincr) { CONST char *ForwardArgString = ObjStr(forwardArgObj), *p; int totalargs = objc + tcd->nr_args - 1; char c = *ForwardArgString; assert(interp); assert(objv); assert(forwardArgObj); assert(tcd); assert(out); assert(freeList); assert(inputArg); assert(mapvalue); assert(outputincr); /* * Per default every ForwardArgString from the processed list corresponds to * exactly one ForwardArgString in the computed final list. */ *outputincr = 1; p = ForwardArgString; /* fprintf(stderr, "ForwardArg: processing '%s'\n", ForwardArgString);*/ if (c == '%' && *(ForwardArgString+1) == '@') { char *remainder = NULL; long pos; ForwardArgString += 2; pos = strtol(ForwardArgString, &remainder, 0); /*fprintf(stderr, "strtol('%s) returned %ld '%s'\n", ForwardArgString, pos, remainder);*/ if (ForwardArgString == remainder && *ForwardArgString == 'e' && !strncmp(ForwardArgString, "end", 3)) { pos = -1; remainder += 3; } else if (pos < 0) { pos --; } if (ForwardArgString == remainder || abs(pos) > totalargs) { return NsfForwardPrintError(interp, tcd, objc, objv, "forward: invalid index specified in argument %s", ObjStr(forwardArgObj)); } if (!remainder || *remainder != ' ') { return NsfForwardPrintError(interp, tcd, objc, objv, "forward: invalid syntax in '%s'; use: %%@ ", ObjStr(forwardArgObj)); } ForwardArgString = ++remainder; /* * In case we address from the end, we reduct further to distinguish from * -1 (void) */ if (pos < 0) pos--; /*fprintf(stderr, "remainder = '%s' pos = %ld\n", remainder, pos);*/ *mapvalue = pos; c = *ForwardArgString; } if (c == '%') { Tcl_Obj *list = NULL, **listElements; int nrArgs = objc-1, nrPosArgs = objc - firstPosArg, nrElements = 0; char c1, *firstActualArgument = nrArgs > 0 ? ObjStr(objv[1]) : NULL; c = *++ForwardArgString; c1 = *(ForwardArgString+1); if (c == 's' && !strcmp(ForwardArgString, "self")) { *out = tcd->object->cmdName; } else if ((c == 'p' && !strcmp(ForwardArgString, "proc")) || (c == 'm' && !strcmp(ForwardArgString, "method")) ) { CONST char *methodName = ObjStr(objv[0]); /* * If we dispatch a method via ".", we do not want to see the "." in the * %proc, e.g. for the interceptor slots (such as mixin, ...) */ if (FOR_COLON_RESOLVER(methodName)) { *out = Tcl_NewStringObj(methodName + 1, -1); } else { *out = objv[0]; } } else if (c == '1' && (c1 == '\0' || c1 == ' ')) { if (c1 != '\0') { if (unlikely(Tcl_ListObjIndex(interp, forwardArgObj, 1, &list) != TCL_OK)) { return NsfForwardPrintError(interp, tcd, objc, objv, "forward: %%1 must be followed by a valid list, given: '%s'", ObjStr(forwardArgObj)); } if (unlikely(Tcl_ListObjGetElements(interp, list, &nrElements, &listElements) != TCL_OK)) { return NsfForwardPrintError(interp, tcd, objc, objv, "forward: %%1 contains invalid list '%s'", ObjStr(list)); } } else if (unlikely(tcd->subcommands != NULL)) { /* deprecated part */ if (Tcl_ListObjGetElements(interp, tcd->subcommands, &nrElements, &listElements) != TCL_OK) { return NsfForwardPrintError(interp, tcd, objc, objv, "forward: %%1 contains invalid list '%s'", ObjStr(tcd->subcommands)); } } else { assert(nrElements <= nrPosArgs); } /*fprintf(stderr, "nrElements=%d, nra=%d firstPos %d objc %d\n", nrElements, nrArgs, firstPosArg, objc);*/ if (nrElements > nrPosArgs) { /* * Insert default subcommand depending on number of arguments. */ /*fprintf(stderr, "inserting listElements[%d] '%s'\n", nrPosArgs, ObjStr(listElements[nrPosArgs]));*/ *out = listElements[nrPosArgs]; } else if (objc <= 1) { return NsfForwardPrintError(interp, tcd, objc, objv, "%%1 requires argument; should be \"%s arg ...\"", ObjStr(objv[0])); } else { /*fprintf(stderr, "copying %%1: '%s'\n", ObjStr(objv[firstPosArg]));*/ *out = objv[firstPosArg]; *inputArg = firstPosArg+1; } } else if (c == '-') { CONST char *firstElementString; int insertRequired, done = 0; /*fprintf(stderr, "process flag '%s'\n", firstActualArgument);*/ if (Tcl_ListObjGetElements(interp, forwardArgObj, &nrElements, &listElements) != TCL_OK) { return NsfForwardPrintError(interp, tcd, objc, objv, "forward: '%s' is not a valid list", ForwardArgString); } if (nrElements < 1 || nrElements > 2) { return NsfForwardPrintError(interp, tcd, objc, objv, "forward: '%s': must contain 1 or 2 arguments", ForwardArgString); } firstElementString = ObjStr(listElements[0]); firstElementString++; /* we skip the dash */ if (firstActualArgument && *firstActualArgument == '-') { int i; /*fprintf(stderr, "we have a flag in first argument '%s'\n", firstActualArgument);*/ for (i = 1; i < firstPosArg; i++) { if (strcmp(firstElementString, ObjStr(objv[i])) == 0) { /*fprintf(stderr, "We have a MATCH for '%s' oldInputArg %d\n", ForwardArgString, *inputArg);*/ *out = objv[i]; /* %1 will start at a different place. Proceed if necessary to firstPosArg */ if (*inputArg < firstPosArg) { *inputArg = firstPosArg; } done = 1; break; } } } if (!done) { /* * We have a flag in the actual arguments that does not match. We * proceed to the actual arguments without dashes. */ if (*inputArg < firstPosArg) { *inputArg = firstPosArg; } /* * If the user requested we output the argument also when not * given in the argument list. */ if (nrElements == 2 && Tcl_GetIntFromObj(interp, listElements[1], &insertRequired) == TCL_OK && insertRequired) { /* * No match, but insert of flag is required. */ /*fprintf(stderr, "no match, but insert of %s required\n", firstElementString);*/ *out = Tcl_NewStringObj(firstElementString, -1); *outputincr = 1; goto add_to_freelist; } else { /* * No match, no insert of flag required, we skip the forwarder * option and output nothing. */ /*fprintf(stderr, "no match, nrElements %d insert req %d\n", nrElements, insertRequired);*/ *outputincr = 0; } } } else if (c == 'a' && !strncmp(ForwardArgString, "argcl", 4)) { if (Tcl_ListObjIndex(interp, forwardArgObj, 1, &list) != TCL_OK) { return NsfForwardPrintError(interp, tcd, objc, objv, "forward: %%argclindex must by a valid list, given: '%s'", ForwardArgString); } if (Tcl_ListObjGetElements(interp, list, &nrElements, &listElements) != TCL_OK) { return NsfForwardPrintError(interp, tcd, objc, objv, "forward: %%argclindex contains invalid list '%s'", ObjStr(list)); } if (nrArgs >= nrElements) { return NsfForwardPrintError(interp, tcd, objc, objv, "forward: not enough elements in specified list of ARGC argument %s", ForwardArgString); } *out = listElements[nrArgs]; } else if (c == '%') { Tcl_Obj *newarg = Tcl_NewStringObj(ForwardArgString, -1); *out = newarg; goto add_to_freelist; } else { /* * Evaluate the given command */ int result; /*fprintf(stderr, "evaluating '%s'\n", ForwardArgString);*/ if ((result = Tcl_EvalEx(interp, ForwardArgString, -1, 0)) != TCL_OK) { return result; } *out = Tcl_DuplicateObj(Tcl_GetObjResult(interp)); /*fprintf(stderr, "result = '%s'\n", ObjStr(*out));*/ goto add_to_freelist; } } else { if (likely(p == ForwardArgString)) { *out = forwardArgObj; } else { Tcl_Obj *newarg = Tcl_NewStringObj(ForwardArgString, -1); *out = newarg; goto add_to_freelist; } } return TCL_OK; add_to_freelist: if (!*freeList) { *freeList = Tcl_NewListObj(1, out); INCR_REF_COUNT2("freeList", *freeList); } else { Tcl_ListObjAppendElement(interp, *freeList, *out); } return TCL_OK; } /* *---------------------------------------------------------------------- * CallForwarder -- * * Invoke the method to which the forwarder points. This function receives * the already transformed argument vector, calls the method and performs * error handling. * * Results: * Tcl result code. * * Side effects: * Maybe through the invoked command. * *---------------------------------------------------------------------- */ static int CallForwarder(ForwardCmdClientData *tcd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(4); static int CallForwarder(ForwardCmdClientData *tcd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { int result; NsfObject *object = tcd->object; CallFrame frame, *framePtr = &frame; assert(tcd); assert(interp); assert(objv); tcd->object = NULL; if (unlikely(tcd->verbose)) { Tcl_Obj *cmd = Tcl_NewListObj(objc, objv); NsfLog(interp, NSF_LOG_INFO, "forwarder calls '%s'", ObjStr(cmd)); DECR_REF_COUNT(cmd); } if (tcd->frame == FrameObjectIdx) { Nsf_PushFrameObj(interp, object, framePtr); } if (tcd->objProc) { /*fprintf(stderr, "CallForwarder Tcl_NRCallObjProc %p\n", tcd->clientData);*/ result = Tcl_NRCallObjProc(interp, tcd->objProc, tcd->clientData, objc, objv); } else if (TclObjIsNsfObject(interp, tcd->cmdName, &object)) { /*fprintf(stderr, "CallForwarder NsfObjDispatch object %s, objc=%d\n", ObjStr(tcd->cmdName), objc);*/ if (likely(objc > 1)) { result = ObjectDispatch(object, interp, objc, objv, NSF_CSC_IMMEDIATE); } else { result = DispatchDefaultMethod(interp, object, objv[0], NSF_CSC_IMMEDIATE); } } else { /*fprintf(stderr, "CallForwarder: no nsf object %s [0] %s\n", ObjStr(tcd->cmdName), ObjStr(objv[0]));*/ result = Tcl_EvalObjv(interp, objc, objv, 0); } if (tcd->frame == FrameObjectIdx) { Nsf_PopFrameObj(interp, framePtr); } #if defined(NSF_FORWARD_WITH_ONERROR) if (unlikely(result == TCL_ERROR && tcd->onerror)) { result = NsfForwardPrintError(interp, tcd, objc, objv, "%s", ObjStr(Tcl_GetObjResult(interp))); } #endif return result; } /* *---------------------------------------------------------------------- * NsfForwardMethod -- * * This Tcl_ObjCmdProc is called, when a forwarder is invoked. It performs * argument substitution through ForwardArg() and calls finally the method, * to which the call was forwarded via CallForwarder(). * * Results: * Tcl result code. * * Side effects: * Maybe through the invoked command. * *---------------------------------------------------------------------- */ static int NsfForwardMethod(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(4); static int NsfForwardMethod(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ForwardCmdClientData *tcd = (ForwardCmdClientData *)clientData; int result, inputArg = 1; assert(clientData); assert(interp); assert(objv); if (unlikely(!tcd || !tcd->object)) { return NsfDispatchClientDataError(interp, tcd, "object", objc > 0 ? ObjStr(objv[0]) : "forwarder"); } /* * First, we handle two short cuts for simple cases. */ if (tcd->passthrough) { /* * This is set for early binding. This means, that the cmd is already * resolved, we have to care only for objscope. */ return CallForwarder(tcd, interp, objc, objv); } else if (tcd->args == NULL && *(ObjStr(tcd->cmdName)) != '%') { /* * We have have no args, therefore we have only to replace the method name * with the given cmd name. */ ALLOC_ON_STACK(Tcl_Obj*, objc, ov); /*fprintf(stderr, "+++ forwardMethod must subst oc=%d <%s>\n", objc, ObjStr(tcd->cmdName));*/ memcpy(ov, objv, sizeof(Tcl_Obj *)*objc); ov[0] = tcd->cmdName; result = CallForwarder(tcd, interp, objc, ov); FREE_ON_STACK(Tcl_Obj *, ov); return result; } else { Tcl_Obj **ov, *freeList = NULL; int j, outputincr, outputArg = 0, firstPosArg=1, totalargs = objc + tcd->nr_args + 3; ALLOC_ON_STACK(Tcl_Obj*, totalargs, OV); ALLOC_ON_STACK(int, totalargs, objvmap); /*fprintf(stderr, "+++ forwardMethod standard case, allocated %d args, tcd->args %s\n", totalargs, ObjStr(tcd->args));*/ ov = &OV[1]; if (tcd->needobjmap) { memset(objvmap, -1, sizeof(int)*totalargs); } /* * The first argument is always the command, to which we forward. */ if ((result = ForwardArg(interp, objc, objv, tcd->cmdName, tcd, &ov[outputArg], &freeList, &inputArg, &objvmap[outputArg], firstPosArg, &outputincr)) != TCL_OK) { goto exitforwardmethod; } outputArg += outputincr; /* * If we have nonpos args, determine the first pos arg position for %1 */ if (tcd->hasNonposArgs) { firstPosArg = objc; for (j = outputArg; j < objc; j++) { CONST char *arg = ObjStr(objv[j]); if (*arg != '-') { firstPosArg = j; break; } } } if (tcd->args) { Tcl_Obj **listElements; int nrElements; /* * Copy argument list from the definitions. */ Tcl_ListObjGetElements(interp, tcd->args, &nrElements, &listElements); for (j = 0; j < nrElements; j++, outputArg += outputincr) { if ((result = ForwardArg(interp, objc, objv, listElements[j], tcd, &ov[outputArg], &freeList, &inputArg, &objvmap[outputArg], firstPosArg, &outputincr)) != TCL_OK) { goto exitforwardmethod; } } } /*fprintf(stderr, "objc=%d, tcd->nr_subcommands=%d size=%d\n", objc, tcd->nr_subcommands, objc+ 2 );*/ if (objc-inputArg > 0) { /*fprintf(stderr, " copying remaining %d args starting at [%d]\n", objc-inputArg, outputArg);*/ memcpy(ov+outputArg, objv+inputArg, sizeof(Tcl_Obj *)*(objc-inputArg)); } else { /*fprintf(stderr, " nothing to copy, objc=%d, inputArg=%d\n", objc, inputArg);*/ } if (tcd->needobjmap) { /* * The objmap can shuffle the argument list. We have to set the * addressing relative from the end; -2 means last, -3 element before * last, etc. */ int max = objc + tcd->nr_args - inputArg; for (j = 0; j < totalargs; j++) { if (objvmap[j] < -1) { /*fprintf(stderr, "must reduct, v=%d\n", objvmap[j]);*/ objvmap[j] = max + objvmap[j] + 2; /*fprintf(stderr, "... new value=%d, max = %d\n", objvmap[j], max);*/ } } } objc += outputArg - inputArg; #if 0 for(j = 0; j < objc; j++) { /*fprintf(stderr, " ov[%d]=%p, objc=%d\n", j, ov[j], objc);*/ fprintf(stderr, " o[%d]=%p %s (%d),", j, ov[j], ov[j] ? ObjStr(ov[j]) : "NADA", objvmap[j]); } fprintf(stderr, "\n"); #endif if (tcd->needobjmap) { for (j = 0; j < totalargs; j++) { Tcl_Obj *tmp; int pos = objvmap[j], i; if (pos == -1 || pos == j) { continue; } tmp = ov[j]; if (j > pos) { for(i = j; i > pos; i--) { /*fprintf(stderr, "...moving right %d to %d\n", i-1, i);*/ ov[i] = ov[i-1]; objvmap[i] = objvmap[i-1]; } } else { for(i = j; i < pos; i++) { /*fprintf(stderr, "...moving left %d to %d\n", i+1, i);*/ ov[i] = ov[i+1]; objvmap[i] = objvmap[i+1]; } } /*fprintf(stderr, "...setting at %d -> %s\n", pos, ObjStr(tmp));*/ ov[pos] = tmp; objvmap[pos] = -1; } } /* If a prefix is provided, it will be prepended to the 2nd argument. This allows for avoiding name clashes if the 2nd argument denotes a subcommand, for example. Make sure that the prefix is only prepended, if a second arg is actually available! Otherwise, the requested prefix has no effect. */ if (tcd->prefix && objc > 1) { Tcl_Obj *methodName = Tcl_DuplicateObj(tcd->prefix); Tcl_AppendObjToObj(methodName, ov[1]); ov[1] = methodName; INCR_REF_COUNT(ov[1]); } #if 0 for(j = 0; j < objc; j++) { /*fprintf(stderr, " ov[%d]=%p, objc=%d\n", j, ov[j], objc);*/ fprintf(stderr, " ov[%d]=%p '%s' map=%d\n", j, ov[j], ov[j] ? ObjStr(ov[j]) : "NADA", objvmap[j]); } #endif OV[0] = tcd->cmdName; result = CallForwarder(tcd, interp, objc, ov); if (tcd->prefix && objc > 1) { DECR_REF_COUNT(ov[1]); } exitforwardmethod: if (freeList) {DECR_REF_COUNT2("freeList", freeList);} FREE_ON_STACK(int, objvmap); FREE_ON_STACK(Tcl_Obj*, OV); } return result; } /* *---------------------------------------------------------------------- * NsfProcAliasMethod -- * * Since alias-resolving happens in dispatch, this Tcl_ObjCmdProc should * never be called during normal operations. The only way to invoke this * could happen via directly calling the handle. * * Results: * TCL_ERROR * * Side effects: * None. * *---------------------------------------------------------------------- */ static int NsfProcAliasMethod(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(4); static int NsfProcAliasMethod(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { AliasCmdClientData *tcd = (AliasCmdClientData *)clientData; assert(clientData); assert(interp); assert(objv); return NsfDispatchClientDataError(interp, NULL, "object", Tcl_GetCommandName(interp, tcd->aliasCmd)); } /* *---------------------------------------------------------------------- * NsfObjscopedMethod -- * * This Tcl_ObjCmdProc is called, when an objscoped alias is invoked. * * Results: * Tcl result code. * * Side effects: * Maybe through the invoked command. * *---------------------------------------------------------------------- */ static int NsfObjscopedMethod(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(4); static int NsfObjscopedMethod(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { AliasCmdClientData *tcd = (AliasCmdClientData *)clientData; NsfObject *object = tcd->object; CallFrame frame, *framePtr = &frame; int result; assert(clientData); assert(interp); assert(objv); /*fprintf(stderr, "objscopedMethod obj=%p %s, ptr=%p\n", object, ObjectName(object), tcd->objProc);*/ tcd->object = NULL; Nsf_PushFrameObj(interp, object, framePtr); result = Tcl_NRCallObjProc(interp, tcd->objProc, tcd->clientData, objc, objv); Nsf_PopFrameObj(interp, framePtr); return result; } /* *---------------------------------------------------------------------- * IsDashArg -- * * Check, whether the provided argument (pointed to be the index firstArg) * starts with a "-", or is a list starting with a "-". The method returns * via **methodName the name of the dashed argument (without the dash). * * Results: * Enum value dashArgType. * * Side effects: * None. * *---------------------------------------------------------------------- */ typedef enum {NO_DASH, SKALAR_DASH, LIST_DASH} dashArgType; static dashArgType IsDashArg(Tcl_Interp *interp, Tcl_Obj *obj, int firstArg, CONST char **methodName, int *objc, Tcl_Obj **objv[]) nonnull(1) nonnull(2) nonnull(4) nonnull(5) nonnull(6); static dashArgType IsDashArg(Tcl_Interp *interp, Tcl_Obj *obj, int firstArg, CONST char **methodName, int *objcPtr, Tcl_Obj **objvPtr[]) { CONST char *flag; assert(interp); assert(obj); assert(methodName); assert(objcPtr); assert(objvPtr); if (obj->typePtr == Nsf_OT_listType) { if (Tcl_ListObjGetElements(interp, obj, objcPtr, objvPtr) == TCL_OK && *objcPtr > 1) { flag = ObjStr(*objvPtr[0]); /*fprintf(stderr, "we have a list starting with '%s'\n", flag);*/ if (*flag == '-') { *methodName = flag+1; return LIST_DASH; } } } flag = ObjStr(obj); /*fprintf(stderr, "we have a scalar '%s' firstArg %d\n", flag, firstArg);*/ if ((*flag == '-') && isalpha(*((flag)+1))) { if (firstArg) { /* if the argument contains a space, try to split */ CONST char *p= flag+1; while (*p && *p != ' ') p++; if (*p == ' ') { if (Tcl_ListObjGetElements(interp, obj, objcPtr, objvPtr) == TCL_OK) { *methodName = ObjStr(*objvPtr[0]); if (**methodName == '-') {(*methodName)++ ;} return LIST_DASH; } } } *methodName = flag+1; *objcPtr = 1; return SKALAR_DASH; } return NO_DASH; } /* *---------------------------------------------------------------------- * CallConfigureMethod -- * * Call a method identified by a string selector; or provide an error * message. This dispatcher function records as well constructor (init) * calls via this interface. The dispatcher is used in XOTcl's * configure(), interpreting arguments with a leading dash as method dispatches. * This behavior is now implemented in NsfOResidualargsMethod(). * * Results: * Tcl result code. * * Side effects: * Maybe side effects from the called methods. * *---------------------------------------------------------------------- */ static int CallConfigureMethod(Tcl_Interp *interp, NsfObject *object, CONST char *initString, CONST char *methodName, int argc, Tcl_Obj *CONST argv[]) nonnull(1) nonnull(2) nonnull(3) nonnull(4); static int CallConfigureMethod(Tcl_Interp *interp, NsfObject *object, CONST char *initString, CONST char *methodName, int argc, Tcl_Obj *CONST argv[]) { int result; Tcl_Obj *methodObj = Tcl_NewStringObj(methodName, -1); assert(interp); assert(object); assert(initString); assert(methodName); /* * When configure gets "-init" passed, we call "init" and notice the fact it * in the object's flags. */ if (*initString == *methodName && strcmp(methodName, initString) == 0) { object->flags |= NSF_INIT_CALLED; } Tcl_ResetResult(interp); INCR_REF_COUNT(methodObj); result = CallMethod(object, interp, methodObj, argc, argv, NSF_CM_NO_UNKNOWN|NSF_CSC_IMMEDIATE|NSF_CM_IGNORE_PERMISSIONS); DECR_REF_COUNT(methodObj); /*fprintf(stderr, "method '%s' called args: %d o=%p, result=%d %d\n", methodName, argc+1, object, result, TCL_ERROR);*/ if (unlikely(result != TCL_OK)) { Tcl_Obj *res = Tcl_DuplicateObj(Tcl_GetObjResult(interp)); /* save the result */ INCR_REF_COUNT(res); NsfPrintError(interp, "%s during '%s.%s'", ObjStr(res), ObjectName(object), methodName); DECR_REF_COUNT(res); } return result; } /* * class method implementations */ /* *---------------------------------------------------------------------- * IsRootNamespace -- * * Check, if the provided namespace is the namespace of the base * class of an object system. * * Results: * Boolean value. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int IsRootNamespace(Tcl_Interp *interp, Tcl_Namespace *nsPtr) nonnull(1) nonnull(2); static int IsRootNamespace(Tcl_Interp *interp, Tcl_Namespace *nsPtr) { NsfObjectSystem *osPtr; assert(interp); assert(nsPtr); for (osPtr = RUNTIME_STATE(interp)->objectSystems; osPtr; osPtr = osPtr->nextPtr) { Tcl_Command cmd = osPtr->rootClass->object.id; if ((Tcl_Namespace *)((Command *)cmd)->nsPtr == nsPtr) { return 1; } } return 0; } /* *---------------------------------------------------------------------- * CallingNameSpace -- * * Find the last invocation outside the Next Scripting system * namespaces. This function return the namespace of the caller but * skips system-specific namespaces (e.g. the namespaces of the * pre-defined slot handlers for mixin and class * registration. etc.) If we would use such namespaces, we would * resolve non-fully-qualified names against the root namespace). * * Results: * Tcl_Namespace or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_Namespace * CallingNameSpace(Tcl_Interp *interp) { Tcl_CallFrame *framePtr; Tcl_Namespace *nsPtr; assert(interp); /*NsfShowStack(interp);*/ framePtr = CallStackGetActiveProcFrame((Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp)); /* framePtr = BeginOfCallChain(interp, GetSelfObj(interp));*/ for (; likely(framePtr != NULL); framePtr = Tcl_CallFrame_callerVarPtr(framePtr)) { nsPtr = Tcl_CallFrame_nsPtr(framePtr); if (IsRootNamespace(interp, nsPtr)) { /*fprintf(stderr, "... %p skip %s\n", framePtr, nsPtr->fullName);*/ continue; } /*fprintf(stderr, "... %p take %s\n", framePtr, nsPtr->fullName); */ break; } if (framePtr == NULL) { nsPtr = Tcl_GetGlobalNamespace(interp); } /*fprintf(stderr, " **** CallingNameSpace: returns %p %s framePtr %p\n", nsPtr, nsPtr ? nsPtr->fullName:"(null)", framePtr);*/ return nsPtr; } /*********************************** * argument handling ***********************************/ static void ArgumentResetRefCounts(struct Nsf_Param CONST *pPtr, Tcl_Obj *valueObj) nonnull(1) nonnull(2); static void ArgumentResetRefCounts(struct Nsf_Param CONST *pPtr, Tcl_Obj *valueObj) { assert(pPtr); assert(valueObj); if ((pPtr->flags & NSF_ARG_IS_CONVERTER)) { DECR_REF_COUNT2("valueObj", valueObj); } } /* *---------------------------------------------------------------------- * ArgumentCheckHelper -- * * Helper function for ArgumentCheck() called when argument checking leads * to a different output element (non-pure checking). * * Results: * Tcl result code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ArgumentCheckHelper(Tcl_Interp *interp, Tcl_Obj *objPtr, struct Nsf_Param CONST *pPtr, unsigned int *flags, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5) nonnull(6); static int ArgumentCheckHelper(Tcl_Interp *interp, Tcl_Obj *objPtr, struct Nsf_Param CONST *pPtr, unsigned int *flags, ClientData *clientData, Tcl_Obj **outObjPtr) { int objc, i, result; Tcl_Obj **ov; assert(interp); assert(objPtr); assert(pPtr); assert(flags); assert(clientData); assert(outObjPtr); assert(pPtr->flags & NSF_ARG_MULTIVALUED); assert(*flags & NSF_PC_MUST_DECR); result = Tcl_ListObjGetElements(interp, objPtr, &objc, &ov); if (unlikely(result != TCL_OK)) { return result; } *outObjPtr = Tcl_NewListObj(0, NULL); INCR_REF_COUNT2("valueObj", *outObjPtr); for (i = 0; i < objc; i++) { Tcl_Obj *elementObjPtr = ov[i]; const char *valueString = ObjStr(elementObjPtr); if (pPtr->flags & NSF_ARG_ALLOW_EMPTY && *valueString == '\0') { result = Nsf_ConvertToString(interp, elementObjPtr, pPtr, clientData, &elementObjPtr); } else { result = (*pPtr->converter)(interp, elementObjPtr, pPtr, clientData, &elementObjPtr); } /*fprintf(stderr, "ArgumentCheckHelper convert %s result %d (%s)\n", valueString, result, ObjStr(elementObjPtr));*/ if (result == TCL_OK || result == TCL_CONTINUE) { Tcl_ListObjAppendElement(interp, *outObjPtr, elementObjPtr); /* * If the refCount of the valueObj was already incremented, we have to * decrement it here, since we want the valuObj reclaimed when the list * containing the valueObj is freed. */ ArgumentResetRefCounts(pPtr, elementObjPtr); } else { Tcl_Obj *resultObj = Tcl_GetObjResult(interp); INCR_REF_COUNT(resultObj); NsfPrintError(interp, "invalid value in \"%s\": %s", ObjStr(objPtr), ObjStr(resultObj)); *flags &= ~NSF_PC_MUST_DECR; *outObjPtr = objPtr; DECR_REF_COUNT2("valueObj", *outObjPtr); DECR_REF_COUNT(resultObj); break; } } return result; } /* *---------------------------------------------------------------------- * ArgumentCheck -- * * Check a single argument (2nd argument) against the parameter structure * when argument checking is turned on (default). * * Results: * Standard Tcl result * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ArgumentCheck(Tcl_Interp *interp, Tcl_Obj *objPtr, struct Nsf_Param CONST *pPtr, int doCheckArguments, unsigned int *flags, ClientData *clientData, Tcl_Obj **outObjPtr) { int result; assert(interp); assert(objPtr); assert(pPtr); assert(flags); assert(clientData); assert(outObjPtr); /* * Default assumption: outObjPtr is not modified. */ *outObjPtr = objPtr; /* * Omit argument checking, provided that ... * ... argument checking is turned off *and* no converter is specified, or * ... the ruling parameter option is 'initcmd' */ if ((unlikely((doCheckArguments & NSF_ARGPARSE_CHECK) == 0) && (pPtr->flags & (NSF_ARG_IS_CONVERTER)) == 0 ) || (pPtr->flags & (NSF_ARG_CMD))) { /* fprintf(stderr, "*** omit argument check for arg %s flags %.6x\n", pPtr->name, pPtr->flags); */ *clientData = ObjStr(objPtr); return TCL_OK; } /* * If the argument is multivalued, perform the check for every element of * the list (pure checker), or we have to build a new list of values (in * case, the converter alters the values). */ if (unlikely(pPtr->flags & NSF_ARG_MULTIVALUED)) { int objc, i; Tcl_Obj **ov; result = Tcl_ListObjGetElements(interp, objPtr, &objc, &ov); if (unlikely(result != TCL_OK)) { return result; } if (objc == 0 && ((pPtr->flags & NSF_ARG_ALLOW_EMPTY) == 0)) { return NsfPrintError(interp, "invalid value for parameter '%s': list is not allowed to be empty", pPtr->name); } /* * In cases where necessary (the output element changed), switch to the * helper function */ for (i = 0; i < objc; i++) { Tcl_Obj *elementObjPtr = ov[i]; result = (*pPtr->converter)(interp, elementObjPtr, pPtr, clientData, &elementObjPtr); if (likely(result == TCL_OK || result == TCL_CONTINUE)) { if (ov[i] != elementObjPtr) { /*fprintf(stderr, "ArgumentCheck: switch to output list construction for value %s\n", ObjStr(elementObjPtr));*/ /* * The elementObjPtr differs from the input Tcl_Obj, we switch to * the version of this handler building an output list. But first, * we have to reset the ref-counts from the first conversion. */ ArgumentResetRefCounts(pPtr, elementObjPtr); *flags |= NSF_PC_MUST_DECR; result = ArgumentCheckHelper(interp, objPtr, pPtr, flags, clientData, outObjPtr); break; } } else { Tcl_Obj *resultObj = Tcl_GetObjResult(interp); INCR_REF_COUNT(resultObj); NsfPrintError(interp, "invalid value in \"%s\": %s", ObjStr(objPtr), ObjStr(resultObj)); DECR_REF_COUNT(resultObj); break; } } } else { assert(objPtr == *outObjPtr); if (pPtr->flags & NSF_ARG_ALLOW_EMPTY && *(ObjStr(objPtr)) == '\0') { result = Nsf_ConvertToString(interp, objPtr, pPtr, clientData, outObjPtr); } else { result = (*pPtr->converter)(interp, objPtr, pPtr, clientData, outObjPtr); } /*fprintf(stderr, "ArgumentCheck param %s type %s is converter %d flags %.6x " "outObj changed %d (%p %p) isok %d\n", pPtr->name, pPtr->type, pPtr->flags & NSF_ARG_IS_CONVERTER, pPtr->flags, objPtr != *outObjPtr, objPtr, *outObjPtr, result == TCL_OK);*/ if (unlikely(pPtr->flags & NSF_ARG_IS_CONVERTER) && objPtr != *outObjPtr) { *flags |= NSF_PC_MUST_DECR; } else { /* * If the output obj differs from the input obj, ensure we have * MUST_DECR set. */ assert( *flags & NSF_PC_MUST_DECR || objPtr == *outObjPtr ); } } if (unlikely(result == TCL_CONTINUE)) { *flags |= NSF_ARG_WARN; result = TCL_OK; } return result; } /* *---------------------------------------------------------------------- * ArgumentDefaults -- * * Process the argument vector and set defaults in parse context if * provided and necessary. * * Results: * Standard Tcl result * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ArgumentDefaults(ParseContext *pcPtr, Tcl_Interp *interp, Nsf_Param CONST *ifd, int nrParams, int processFlags) nonnull(1) nonnull(2) nonnull(3); static int ArgumentDefaults(ParseContext *pcPtr, Tcl_Interp *interp, Nsf_Param CONST *ifd, int nrParams, int processFlags) { Nsf_Param CONST *pPtr; int i; assert(pcPtr); assert(interp); assert(ifd); for (pPtr = ifd, i = 0; i < nrParams; pPtr++, i++) { /*fprintf(stderr, "ArgumentDefaults got for arg %s (req %d, nrArgs %d) %p => %p %p, default '%s' \n", pPtr->name, pPtr->flags & NSF_ARG_REQUIRED, pPtr->nrArgs, pPtr, pcPtr->clientData[i], pcPtr->objv[i], pPtr->defaultValue ? ObjStr(pPtr->defaultValue) : "NONE");*/ if (pcPtr->objv[i]) { /* * We got an actual value, which was already checked by ArgumentParse(). * In case the value is a switch and NSF_PC_INVERT_DEFAULT is set, we * take the default and invert the value in place. */ if (unlikely(pcPtr->flags[i] & NSF_PC_INVERT_DEFAULT)) { int bool; Tcl_GetBooleanFromObj(interp, pPtr->defaultValue, &bool); pcPtr->objv[i] = Tcl_NewBooleanObj(!bool); /* * Perform bookkeeping to avoid that someone releases the new obj * before we are done. The according DECR is performed by * ParseContextRelease() */ INCR_REF_COUNT2("valueObj", pcPtr->objv[i]); pcPtr->flags[i] |= NSF_PC_MUST_DECR; pcPtr->status |= NSF_PC_STATUS_MUST_DECR; } } else { /* * No valued was passed, check if a default is available. */ if (pPtr->defaultValue) { int mustDecrNewValue; Tcl_Obj *newValue = pPtr->defaultValue; ClientData checkedData; /* * We have a default value for the argument. Mark that this argument * gets the default value. */ pcPtr->flags[i] |= NSF_PC_IS_DEFAULT; /* Is it necessary to substitute the default value? */ if (unlikely(pPtr->flags & NSF_ARG_SUBST_DEFAULT)) { Tcl_Obj *obj = Tcl_SubstObj(interp, newValue, TCL_SUBST_ALL); if (likely(obj != NULL)) { newValue = obj; } else { pcPtr->flags[i] = 0; return TCL_ERROR; } /* The matching DECR is performed by ParseContextRelease() */ INCR_REF_COUNT2("valueObj", newValue); /*fprintf(stderr, "SUBST_DEFAULT increments %p refCount %d\n", newValue,newValue->refCount);*/ mustDecrNewValue = 1; pcPtr->flags[i] |= NSF_PC_MUST_DECR; pcPtr->status |= NSF_PC_STATUS_MUST_DECR; } else { mustDecrNewValue = 0; } pcPtr->objv[i] = newValue; /*fprintf(stderr, "==> setting default value '%s' for var '%s' flag %d type %s conv %p\n", ObjStr(newValue), pPtr->name, pPtr->flags & NSF_ARG_INITCMD, pPtr->type, pPtr->converter);*/ /* * Check the default value if necessary */ if (pPtr->type || unlikely(pPtr->flags & NSF_ARG_MULTIVALUED)) { unsigned int mustDecrList = 0; if (unlikely((pPtr->flags & NSF_ARG_INITCMD) == 0 && ArgumentCheck(interp, newValue, pPtr, RUNTIME_STATE(interp)->doCheckArguments, &mustDecrList, &checkedData, &pcPtr->objv[i]) != TCL_OK)) { if (mustDecrNewValue) { DECR_REF_COUNT2("valueObj", newValue); pcPtr->flags[i] &= ~NSF_PC_MUST_DECR; } return TCL_ERROR; } if (unlikely(pcPtr->objv[i] != newValue)) { /* * The output Tcl_Obj differs from the input, so the * Tcl_Obj was converted; in case we have set previously * must_decr on newValue, we decr the refCount on newValue * here and clear the flag. */ if (mustDecrNewValue) { DECR_REF_COUNT2("valueObj", newValue); pcPtr->flags[i] &= ~NSF_PC_MUST_DECR; } /* * The new output value itself might require a decr, so * set the flag here if required; this is just necessary * for multivalued converted output. */ if (mustDecrList) { pcPtr->flags[i] |= NSF_PC_MUST_DECR; pcPtr->status |= NSF_PC_STATUS_MUST_DECR; } } } else { /*fprintf(stderr, "Param %s default %s type %s\n", pPtr->name, ObjStr(pPtr->defaultValue), pPtr->type);*/ assert(pPtr->type ? pPtr->defaultValue == NULL : 1); } } else if (unlikely(pPtr->flags & NSF_ARG_REQUIRED) && (processFlags & NSF_ARGPARSE_FORCE_REQUIRED)) { Tcl_Obj *paramDefsObj = NsfParamDefsSyntax(interp, ifd, pcPtr->object, NULL); Tcl_Obj *methodPathObj = NsfMethodNamePath(interp, CallStackGetTclFrame(interp, NULL, 1), MethodName(pcPtr->full_objv[0])); INCR_REF_COUNT2("methodPathObj", methodPathObj); NsfPrintError(interp, "required argument '%s' is missing, should be:\n\t%s%s%s %s", pPtr->nameObj ? ObjStr(pPtr->nameObj) : pPtr->name, pcPtr->object ? ObjectName(pcPtr->object) : "", pcPtr->object ? " " : "", ObjStr(methodPathObj), ObjStr(paramDefsObj)); DECR_REF_COUNT2("paramDefsObj", paramDefsObj); DECR_REF_COUNT2("methodPathObj", methodPathObj); return TCL_ERROR; } else { /* * Use as dummy default value an arbitrary symbol, which must * not be returned to the Tcl level level; this value is unset * later typically by NsfUnsetUnknownArgsCmd(). */ pcPtr->objv[i] = NsfGlobalObjs[NSF___UNKNOWN__]; } } } return TCL_OK; } /* *---------------------------------------------------------------------- * ArgumentParse -- * * Parse the argument vector based on the parameter definitions. * The parsed argument vector is returned in a normalized order * in the parse context. * * Results: * Tcl return code. * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_ArgumentParse(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Nsf_Object *object, Tcl_Obj *procNameObj, Nsf_Param CONST *paramPtr, int nrParams, int serial, unsigned int processFlags, Nsf_ParseContext *pcPtr) { assert(interp); assert(objv); assert(procNameObj); assert(pcPtr); return ArgumentParse(interp, objc, objv, (NsfObject *)object, procNameObj, paramPtr, nrParams, serial, processFlags, (ParseContext *)pcPtr); } /* *---------------------------------------------------------------------- * NextParam -- * * Advance in the parameter definitions and return the next parameter. * * Results: * Next parameter. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Nsf_Param CONST * NextParam(Nsf_Param CONST *paramPtr, Nsf_Param CONST *lastParamPtr) nonnull(1) nonnull(2) returns_nonnull; static Nsf_Param CONST * NextParam(Nsf_Param CONST *paramPtr, Nsf_Param CONST *lastParamPtr) { assert(paramPtr); assert(lastParamPtr); for (; ++paramPtr <= lastParamPtr && *paramPtr->name == '-'; ); return paramPtr; } /* *---------------------------------------------------------------------- * ArgumentParse -- * * Parse the provided ist of argument against the given definition. The * result is returned in the parameter context structure. * * Results: * Standard Tcl result. * * Side effects: * None. * *---------------------------------------------------------------------- */ #define SkipNonposParamDefs(cPtr) \ for (; ++(cPtr) <= lastParamPtr && *(cPtr)->name == '-';) static int ArgumentParse(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], NsfObject *object, Tcl_Obj *procNameObj, Nsf_Param CONST *paramPtr, int nrParams, int serial, unsigned int processFlags, ParseContext *pcPtr) { int o, dashdash = 0, j, fromArg; Nsf_Param CONST *currentParamPtr = paramPtr; Nsf_Param CONST *lastParamPtr = paramPtr + nrParams - 1; assert(interp); assert(objv); assert(procNameObj); assert(paramPtr); assert(pcPtr); if (processFlags & NSF_ARGPARSE_START_ZERO) { fromArg = 0; } else { fromArg = 1; } ParseContextInit(pcPtr, nrParams, object, procNameObj); #if defined(PARSE_TRACE) { Nsf_Param CONST *pPtr; fprintf(stderr, "PARAMETER "); for (o = 0, pPtr = paramPtr; pPtr->name; o++, pPtr++) { fprintf(stderr, "[%d]%s (nrargs %d %s) ", o, pPtr->name, pPtr->nrArgs, pPtr->flags & NSF_ARG_REQUIRED ? "req" : "opt"); } fprintf(stderr, "\n"); fprintf(stderr, "BEGIN (%d) [0]%s ", objc, ObjStr(procNameObj)); for (o = fromArg; o < objc; o++) {fprintf(stderr, "[%d]%s ", o, ObjStr(objv[o]));} fprintf(stderr, "\n"); } #endif for (o = fromArg; o < objc; o++) { Nsf_Param CONST *pPtr = currentParamPtr; Tcl_Obj *argumentObj = objv[o]; Tcl_Obj *valueObj = NULL; char *valueInArgument = NULL; #if defined(PARSE_TRACE_FULL) fprintf(stderr, "arg [%d]: %s (param %ld, last %d)\n", o, ObjStr(argumentObj), currentParamPtr - paramPtr, currentParamPtr == lastParamPtr); #endif if (unlikely(currentParamPtr > lastParamPtr)) { Tcl_Obj *methodPathObj = NsfMethodNamePath(interp, CallStackGetTclFrame(interp, NULL, 0), NsfMethodName(procNameObj)); /*fprintf(stderr, "call NsfUnexpectedArgumentError 1\n");*/ return NsfUnexpectedArgumentError(interp, ObjStr(argumentObj), (Nsf_Object*)object, paramPtr, methodPathObj); } if (*currentParamPtr->name == '-') { /* * We expect a nonpos arg. Check, if we a Tcl_Obj already converted to * NsfFlagObjType. */ NsfFlag *flagPtr = argumentObj->internalRep.twoPtrValue.ptr1; #if defined(PARSE_TRACE_FULL) fprintf(stderr, "... arg %p %s expect nonpos arg in block %s isFlag %d sig %d serial %d (%d => %d)\n", argumentObj, ObjStr(argumentObj), currentParamPtr->name, argumentObj->typePtr == &NsfFlagObjType, argumentObj->typePtr == &NsfFlagObjType ? flagPtr->signature == paramPtr : 0, argumentObj->typePtr == &NsfFlagObjType ? flagPtr->serial == serial : 0, argumentObj->typePtr == &NsfFlagObjType ? flagPtr->serial : 0, serial ); #endif if (argumentObj->typePtr == &NsfFlagObjType && flagPtr->signature == paramPtr && flagPtr->serial == serial ) { /* * The argument was processed before and the Tcl_Obj is still valid. */ if (flagPtr->flags & NSF_FLAG_DASHDAH) { /* * We got a dashDash, skip nonpos param definitions and continue with next * element from objv. */ SkipNonposParamDefs(currentParamPtr); assert(dashdash == 0); continue; } else if (flagPtr->flags & NSF_FLAG_CONTAINS_VALUE) { /* * We got a flag with an embedded value (e.g -flag=1). */ valueInArgument = "flag"; } pPtr = flagPtr->paramPtr; valueObj = flagPtr->payload; } else { CONST char *argumentString = ObjStr(argumentObj); /* * We are in a state, where we expect a non-positional argument, and * the lookup from the Tcl_Obj has failed. If this non-pos args are * optional, the current argument might contain also a value for a * positional argument maybe the argument is for a posarg * later). First check, if the argument looks like a flag. */ if (argumentString[0] != '-') { /* * The actual argument is not a flag, so proceed in the parameter * vector to the next block (positional parameter) */ SkipNonposParamDefs(currentParamPtr); pPtr = currentParamPtr; /* * currentParamPtr is either NULL or points to a positional parameter */ assert(currentParamPtr == NULL || currentParamPtr->name == NULL || *currentParamPtr->name != '-'); } else { /* * The actual argument starts with a dash, so search for the flag in * the current block of nonpos parameter definitions */ char ch1 = *(argumentString+1); /* Is there a "--" ? */ if (ch1 == '-' && *(argumentString+2) == '\0' && dashdash == 0) { dashdash = 1; NsfFlagObjSet(interp, argumentObj, paramPtr, serial, NULL, NULL, NSF_FLAG_DASHDAH); SkipNonposParamDefs(currentParamPtr); continue; } valueInArgument = strchr(argumentString, '='); if (valueInArgument) { int found = 0, equalOffset = valueInArgument - argumentString; /* * Handle parameter like -flag=1 */ for (; pPtr <= lastParamPtr && *pPtr->name == '-'; pPtr ++) { if (pPtr->nrArgs > 0) { /* parameter expects no arg, can't be this */ continue; } if ((pPtr->flags & NSF_ARG_NOCONFIG) == 0 && ch1 == pPtr->name[1] && strncmp(argumentString, pPtr->name, equalOffset) == 0 && *(pPtr->name+equalOffset) == '\0') { valueObj = Tcl_NewStringObj(valueInArgument+1,-1); /*fprintf(stderr, "... value from argument = %s\n", ObjStr(valueObj));*/ NsfFlagObjSet(interp, argumentObj, paramPtr, serial, pPtr, valueObj, NSF_FLAG_CONTAINS_VALUE); found = 1; break; } } if (!found) { Nsf_Param CONST *nextParamPtr = NextParam(currentParamPtr, lastParamPtr); if (nextParamPtr > lastParamPtr || (nextParamPtr->flags & NSF_ARG_NODASHALNUM)) { Tcl_Obj *methodPathObj = NsfMethodNamePath(interp, CallStackGetTclFrame(interp, NULL, 0), NsfMethodName(procNameObj)); return NsfUnexpectedNonposArgumentError(interp, argumentString, (Nsf_Object *)object, currentParamPtr, paramPtr, methodPathObj); } pPtr = currentParamPtr = nextParamPtr; } } else { /* * Must be a classical nonpos arg; check for a matching parameter * definition. */ int found = 0; assert(pPtr == currentParamPtr); if (ch1 != '\0') { if (unlikely(NsfParamDefsNonposLookup(interp, argumentString, currentParamPtr, &pPtr) != TCL_OK)) { return TCL_ERROR; } else { if (pPtr != NULL) { found = 1; NsfFlagObjSet(interp, argumentObj, paramPtr, serial, pPtr, NULL, 0); } } } /* * We might have found the argument starting with the dash in the * parameter definitions or not. If it was not found, then we can * advance to the next positional parameter and stuff the value in * there, if the parameter definition allows this. */ if (!found) { int nonposArgError = 0; Nsf_Param CONST *nextParamPtr = NextParam(currentParamPtr, lastParamPtr); /*fprintf(stderr, "non-pos-arg '%s' not found, current %p %s last %p %s next %p %s\n", argumentString, currentParamPtr, currentParamPtr->name, lastParamPtr, lastParamPtr->name, nextParamPtr, nextParamPtr->name);*/ if (nextParamPtr > lastParamPtr) { nonposArgError = 1; } else if (nextParamPtr->flags & NSF_ARG_NODASHALNUM) { /* * Check if argment is numeric, since we want to allow it as * value even when NSF_ARG_NODASHALNUM was specified. */ nonposArgError = 1; if (argumentString[1] >= '0' && argumentString[1] <= '9') { char *p; (void)strtod(&argumentString[1], &p); if (*p == '\0') { /* argument is numeric */ nonposArgError = 0; } } } if (nonposArgError) { Tcl_Obj *methodPathObj = NsfMethodNamePath(interp, CallStackGetTclFrame(interp, NULL, 0), NsfMethodName(procNameObj)); return NsfUnexpectedNonposArgumentError(interp, argumentString, (Nsf_Object *)object, currentParamPtr, paramPtr, methodPathObj); } pPtr = currentParamPtr = nextParamPtr; } } } /* end of lookup loop */ } } assert(pPtr); /* * pPtr points to the actual parameter (part of the currentParamPtr block) * or might point to a place past the last parameter, in which case an * unexpected argument was provided. o is the index of the actual * parameter, valueObj might be already provided for valueInArgument. */ if (unlikely(pPtr > lastParamPtr)) { Tcl_Obj *methodPathObj = NsfMethodNamePath(interp, CallStackGetTclFrame(interp, NULL, 0), NsfMethodName(procNameObj)); /*fprintf(stderr, "call NsfUnexpectedArgumentError 2\n");*/ return NsfUnexpectedArgumentError(interp, ObjStr(argumentObj), (Nsf_Object *)object, paramPtr, methodPathObj); } /* * Set the position in the downstream argv (normalized order) */ j = pPtr - paramPtr; #if defined(PARSE_TRACE_FULL) fprintf(stderr, "... pPtr->name %s/%d o %d objc %d\n", pPtr->name, pPtr->nrArgs, o, objc); #endif if (*pPtr->name == '-') { /* process the nonpos arg */ if (pPtr->nrArgs == 1) { /* The nonpos arg expects an argument */ o++; if (unlikely(o >= objc)) { /* we expect an argument, but we are already at the end of the argument list */ return NsfPrintError(interp, "value for parameter '%s' expected", pPtr->name); } assert(valueObj == NULL); valueObj = objv[o]; } else { /* The nonpos arg expects no argument */ if (valueObj == NULL) { valueObj = NsfGlobalObjs[NSF_ONE]; } } } else if (unlikely(pPtr == lastParamPtr && pPtr->converter == ConvertToNothing)) { /* * "args" was given, use the varargs interface. Store the actual * argument into pcPtr->objv. No checking is performed on "args". */ pcPtr->varArgs = 1; pcPtr->objv[j] = argumentObj; #if defined(PARSE_TRACE_FULL) fprintf(stderr, "... args found o %d objc %d is dashdash %d [%d] <%s>\n", o, objc, dashdash, j, ObjStr(argumentObj)); #endif break; } else { /* * Process an ordinary positional argument. */ currentParamPtr ++; #if defined(PARSE_TRACE_FULL) fprintf(stderr, "... positional arg o %d objc %d, nrArgs %d next paramPtr %s\n", o, objc, pPtr->nrArgs, currentParamPtr->name); #endif if (unlikely(pPtr->nrArgs == 0)) { /* * Allow positional arguments with 0 args for object parameter * aliases, which are always fired. Such parameter are non-consuming, * therefore the processing of the current argument is not finished, we * have to decrement o. We have to check here if we are already at the * end if the parameter vector. */ o--; continue; } if (unlikely(dashdash)) { /* reset dashdash */ dashdash = 0; } valueObj = argumentObj; } #if defined(PARSE_TRACE_FULL) fprintf(stderr, "... setting parameter %s pos %d valueObj '%s'\n", pPtr->name, j, valueObj == argumentObj ? "=" : ObjStr(valueObj)); #endif /* * The value for the flag is now in the valueObj. We * check, whether it is value is permissible. */ assert(valueObj); if (unlikely(ArgumentCheck(interp, valueObj, pPtr, processFlags, &pcPtr->flags[j], &pcPtr->clientData[j], &pcPtr->objv[j]) != TCL_OK)) { if (pcPtr->flags[j] & NSF_PC_MUST_DECR) {pcPtr->status |= NSF_PC_STATUS_MUST_DECR;} return TCL_ERROR; } /* * Switches are more tricky: if the flag is provided without * valueInArgument, we take the default and invert it. If valueInArgument * was used, the default inversion must not happen. */ if (likely(valueInArgument == NULL)) { if (unlikely(pPtr->converter == Nsf_ConvertToSwitch)) { /*fprintf(stderr,"... set INVERT_DEFAULT for '%s' flags %.6x\n", pPtr->name, pPtr->flags);*/ pcPtr->flags[j] |= NSF_PC_INVERT_DEFAULT; } } /*fprintf(stderr, "... non-positional pcPtr %p check [%d] obj %p flags %.6x & %p\n", pcPtr, j, pcPtr->objv[j], pcPtr->flags[j], &(pcPtr->flags[j])); */ /* * Provide warnings for double-settings. */ if (unlikely(pcPtr->flags[j] & NSF_ARG_SET)) { Tcl_Obj *cmdLineObj = Tcl_NewListObj(objc-1, objv+1); INCR_REF_COUNT(cmdLineObj); NsfLog(interp, NSF_LOG_WARN, "Non-positional parameter %s was passed more than once (%s%s%s %s)", pPtr->name, object ? ObjectName(object) : "", object ? " method " : "", ObjStr(procNameObj), ObjStr(cmdLineObj)); DECR_REF_COUNT(cmdLineObj); } pcPtr->flags[j] |= NSF_ARG_SET; /* * Embed error message of converter in current context. */ if (unlikely(pcPtr->flags[j] & NSF_ARG_WARN)) { Tcl_Obj *resultObj = Tcl_GetObjResult(interp); Tcl_DString ds, *dsPtr = &ds; Tcl_DStringInit(dsPtr); INCR_REF_COUNT(resultObj); NsfDStringArgv(dsPtr, objc, objv); NsfLog(interp, NSF_LOG_WARN, "%s during:\n%s %s", ObjStr(resultObj), object ? ObjectName(object) : "nsf::proc", Tcl_DStringValue(dsPtr)); DECR_REF_COUNT(resultObj); Tcl_DStringFree(dsPtr); } if (unlikely(pcPtr->flags[j] & NSF_PC_MUST_DECR)) { pcPtr->status |= NSF_PC_STATUS_MUST_DECR; } assert(pcPtr->varArgs == 0); #if defined(PARSE_TRACE_FULL) fprintf(stderr, "... iterate on o %d objc %d, currentParamPtr %s\n", o, objc, currentParamPtr->name); #endif } if (currentParamPtr <= lastParamPtr && pcPtr->varArgs == 0) { /* not all parameter processed, make sure varags is set */ /*fprintf(stderr, ".... not all parms processed, pPtr '%s' j %ld nrParams %d last '%s' varArgs %d dashdash %d\n", currentParamPtr->name, currentParamPtr - paramPtr, nrParams, lastParamPtr->name, pcPtr->varArgs, dashdash);*/ if (lastParamPtr->converter == ConvertToNothing) { pcPtr->varArgs = 1; } } /* * Set lastObjc as index of the first "unprocessed" parameter. */ pcPtr->lastObjc = o; pcPtr->objc = nrParams; assert(ISOBJ(objv[pcPtr->lastObjc-1])); #if defined(PARSE_TRACE_FULL) fprintf(stderr, "..... argv processed o %d lastObjc %d nrParams %d olastObjc, nrParams, ovarArgs); #endif return ArgumentDefaults(pcPtr, interp, paramPtr, nrParams, processFlags); } /*********************************** * Begin result setting commands * (essentially List*() and support ***********************************/ /* *---------------------------------------------------------------------- * ListVarKeys -- * * Return variable names of the provided hash table in the interp * result. Optionally "pattern" might be used to filter the result list. * * Results: * Standard Tcl result * * Side effects: * Modifies interp result * *---------------------------------------------------------------------- */ static int ListVarKeys(Tcl_Interp *interp, Tcl_HashTable *tablePtr, CONST char *pattern) nonnull(1); static int ListVarKeys(Tcl_Interp *interp, Tcl_HashTable *tablePtr, CONST char *pattern) { Tcl_HashEntry *hPtr; assert(interp); if (pattern && NoMetaChars(pattern)) { Tcl_Obj *patternObj = Tcl_NewStringObj(pattern, -1); INCR_REF_COUNT(patternObj); hPtr = tablePtr ? Tcl_CreateHashEntry(tablePtr, (char *)patternObj, NULL) : NULL; if (hPtr) { Var *val = TclVarHashGetValue(hPtr); Tcl_SetObjResult(interp, TclVarHashGetKey(val)); } else { Tcl_SetObjResult(interp, NsfGlobalObjs[NSF_EMPTY]); } DECR_REF_COUNT(patternObj); } else { Tcl_Obj *list = Tcl_NewListObj(0, NULL); Tcl_HashSearch hSrch; hPtr = tablePtr ? Tcl_FirstHashEntry(tablePtr, &hSrch) : NULL; for (; hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { Var *val = TclVarHashGetValue(hPtr); Tcl_Obj *key = TclVarHashGetKey(val); if (pattern == NULL || Tcl_StringMatch(ObjStr(key), pattern)) { Tcl_ListObjAppendElement(interp, list, key); } } Tcl_SetObjResult(interp, list); } return TCL_OK; } /* *---------------------------------------------------------------------- * GetOriginalCommand -- * * Obtain for an imported/aliased cmd the original definition. * * Results: * Tcl command * * Side effects: * none * *---------------------------------------------------------------------- */ static Tcl_Command GetOriginalCommand(Tcl_Command cmd) /* The imported command for which the original * command should be returned. */ { Tcl_Command importedCmd; assert(cmd); while (1) { /* dereference the namespace import reference chain */ if ((importedCmd = TclGetOriginalCommand(cmd))) { cmd = importedCmd; } /* dereference the Next Scripting alias chain */ if (Tcl_Command_deleteProc(cmd) == AliasCmdDeleteProc) { AliasCmdClientData *tcd = (AliasCmdClientData *)Tcl_Command_objClientData(cmd); cmd = tcd->aliasedCmd; continue; } break; } return cmd; } /* *---------------------------------------------------------------------- * ListProcBody -- * * Return the body of a scripted proc as tcl interp result. * * Results: * Standard Tcl result * * Side effects: * Modifies interp result * *---------------------------------------------------------------------- */ static int ListProcBody(Tcl_Interp *interp, Proc *procPtr, CONST char *methodName) nonnull(1) nonnull(2) nonnull(3); static int ListProcBody(Tcl_Interp *interp, Proc *procPtr, CONST char *methodName) { CONST char *body; assert(interp); assert(procPtr); assert(methodName); body = ObjStr(procPtr->bodyPtr); Tcl_SetObjResult(interp, Tcl_NewStringObj(StripBodyPrefix(body), -1)); return TCL_OK; } /* *---------------------------------------------------------------------- * ListParamDefs -- * * Compute the parameter definition in one of four different forms. * * Results: * Standard Tcl result * * Side effects: * Modifies interp result * *---------------------------------------------------------------------- */ static Tcl_Obj *ListParamDefs(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern, NsfParamsPrintStyle style) nonnull(1) nonnull(2) returns_nonnull; static Tcl_Obj * ListParamDefs(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern, NsfParamsPrintStyle style) { Tcl_Obj *listObj; assert(interp); assert(paramsPtr); switch (style) { case NSF_PARAMS_PARAMETER: listObj = ParamDefsFormat(interp, paramsPtr, contextObject, pattern); break; case NSF_PARAMS_LIST: listObj = ParamDefsList(interp, paramsPtr, contextObject, pattern); break; case NSF_PARAMS_NAMES: listObj = ParamDefsNames(interp, paramsPtr, contextObject, pattern); break; default: /* NSF_PARAMS_SYNTAX:*/ listObj = NsfParamDefsSyntax(interp, paramsPtr, contextObject, pattern); break; } return listObj; } /* *---------------------------------------------------------------------- * ListCmdParams -- * * Obtains a cmd and a method name and sets as side effect the Tcl result * to either the list. The print-style NSF_PARAMS_NAMES, NSF_PARAMS_LIST, * NSF_PARAMS_PARAMETER, NSF_PARAMS_SYNTAX controls the elements of the * list. * * Results: * Tcl result code. * * Side effects: * Sets interp result * *---------------------------------------------------------------------- */ static int ListCmdParams(Tcl_Interp *interp, Tcl_Command cmd, NsfObject *contextObject, CONST char *pattern, CONST char *methodName, NsfParamsPrintStyle printStyle) nonnull(1) nonnull(2) nonnull(5); static int ListCmdParams(Tcl_Interp *interp, Tcl_Command cmd, NsfObject *contextObject, CONST char *pattern, CONST char *methodName, NsfParamsPrintStyle printStyle) { NsfParamDefs *paramDefs; Tcl_Obj *listObj; Proc *procPtr; assert(interp); assert(methodName); assert(cmd); paramDefs = ParamDefsGet(cmd, NULL); if (paramDefs && paramDefs->paramsPtr) { /* * Obtain parameter info from paramDefs. */ listObj = ListParamDefs(interp, paramDefs->paramsPtr, contextObject, pattern, printStyle); Tcl_SetObjResult(interp, listObj); DECR_REF_COUNT2("paramDefsObj", listObj); return TCL_OK; } procPtr = GetTclProcFromCommand(cmd); if (procPtr) { /* * Obtain parameter info from compiled locals. */ CompiledLocal *args = procPtr->firstLocalPtr; listObj = Tcl_NewListObj(0, NULL); for ( ; args; args = args->nextPtr) { if (!TclIsCompiledLocalArgument(args)) { continue; } if (pattern && !Tcl_StringMatch(args->name, pattern)) continue; if (printStyle == NSF_PARAMS_SYNTAX && strcmp(args->name, "args") == 0) { if (args != procPtr->firstLocalPtr) { Tcl_AppendToObj(listObj, " ", 1); } Tcl_AppendToObj(listObj, "?/arg .../?", 11); } else { if (printStyle == NSF_PARAMS_SYNTAX) { /* * A default means that the argument is optional. */ if (args->defValuePtr) { Tcl_AppendToObj(listObj, "?", 1); Tcl_AppendToObj(listObj, args->name, -1); Tcl_AppendToObj(listObj, "?", 1); } else { Tcl_AppendToObj(listObj, "/", 1); Tcl_AppendToObj(listObj, args->name, -1); Tcl_AppendToObj(listObj, "/", 1); } if (args->nextPtr != NULL) { Tcl_AppendToObj(listObj, " ", 1); } } else { Tcl_Obj *innerListObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, innerListObj, Tcl_NewStringObj(args->name, -1)); /* * Return default just for NSF_PARAMS_PARAMETER. */ if (args->defValuePtr && printStyle == NSF_PARAMS_PARAMETER) { Tcl_ListObjAppendElement(interp, innerListObj, args->defValuePtr); } Tcl_ListObjAppendElement(interp, listObj, innerListObj); } } } Tcl_SetObjResult(interp, listObj); return TCL_OK; } { /* * If a command is not found for the object|class, check whether we * find the parameter definitions for the C-defined method. */ Nsf_methodDefinition *mdPtr = Nsf_CmdDefinitionGet(((Command *)cmd)->objProc); if (mdPtr != NULL) { NsfParamDefs paramDefs = {mdPtr->paramDefs, mdPtr->nrParameters, 1, 0, NULL, NULL}; Tcl_Obj *list = ListParamDefs(interp, paramDefs.paramsPtr, contextObject, pattern, printStyle); Tcl_SetObjResult(interp, list); DECR_REF_COUNT2("paramDefsObj", list); return TCL_OK; } } if (((Command *)cmd)->objProc == NsfSetterMethod) { SetterCmdClientData *cd = (SetterCmdClientData *)Tcl_Command_objClientData(cmd); if (cd && cd->paramsPtr) { Tcl_Obj *list; NsfParamDefs paramDefs; paramDefs.paramsPtr = cd->paramsPtr; paramDefs.nrParams = 1; paramDefs.slotObj = NULL; list = ListParamDefs(interp, paramDefs.paramsPtr, contextObject, pattern, printStyle); Tcl_SetObjResult(interp, list); DECR_REF_COUNT2("paramDefsObj", list); return TCL_OK; } else { Tcl_SetObjResult(interp, Tcl_NewStringObj(methodName, -1)); return TCL_OK; } } /* * In case, we failed so far to obtain a result, try to use the * object-system implementors definitions in the global array * ::nsf::parametersyntax. Note that we can only obtain the * parameter syntax this way. */ if (printStyle == NSF_PARAMS_SYNTAX) { Tcl_DString ds, *dsPtr = &ds; Tcl_Obj *parameterSyntaxObj; Tcl_DStringInit(dsPtr); DStringAppendQualName(dsPtr, Tcl_Command_nsPtr(cmd), methodName); /*fprintf(stderr,"Looking up ::nsf::parametersyntax(%s) ...\n", Tcl_DStringValue(dsPtr));*/ parameterSyntaxObj = Tcl_GetVar2Ex(interp, NsfGlobalStrings[NSF_ARRAY_PARAMETERSYNTAX], Tcl_DStringValue(dsPtr), TCL_GLOBAL_ONLY); /*fprintf(stderr, "No parametersyntax so far methodName %s cmd name %s ns %s\n", methodName, Tcl_GetCommandName(interp, cmd), Tcl_DStringValue(dsPtr));*/ Tcl_DStringFree(dsPtr); if (parameterSyntaxObj) { Tcl_SetObjResult(interp, parameterSyntaxObj); return TCL_OK; } } if (Tcl_Command_objProc(cmd) == NsfForwardMethod) { return NsfPrintError(interp, "could not obtain parameter definition for forwarder '%s'", methodName); } else if (!CmdIsNsfObject(cmd)) { return NsfPrintError(interp, "could not obtain parameter definition for method '%s'", methodName); } else { /* procPtr == NsfObjDispatch, be quiet */ return TCL_OK; } { Tcl_Obj *methodObj = Tcl_NewStringObj(methodName, -1); INCR_REF_COUNT(methodObj); NsfObjErrType(interp, "parameter get", methodObj, "a method name", NULL); DECR_REF_COUNT(methodObj); } return TCL_ERROR; } /* *---------------------------------------------------------------------- * AppendForwardDefinition -- * * Append the parameters of a forward definition to the specified listObj. * * Results: * None. * * Side effects: * Appending to listObj * *---------------------------------------------------------------------- */ static void AppendForwardDefinition(Tcl_Interp *interp, Tcl_Obj *listObj, ForwardCmdClientData *tcd) nonnull(1) nonnull(2) nonnull(3); static void AppendForwardDefinition(Tcl_Interp *interp, Tcl_Obj *listObj, ForwardCmdClientData *tcd) { assert(interp); assert(listObj); assert(tcd); if (tcd->prefix) { Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-prefix", -1)); Tcl_ListObjAppendElement(interp, listObj, tcd->prefix); } if (tcd->subcommands) { Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-default", -1)); Tcl_ListObjAppendElement(interp, listObj, tcd->subcommands); } if (tcd->objProc) { Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-earlybinding", -1)); } if (tcd->frame == FrameObjectIdx) { Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-frame", 6)); Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("object", 6)); } Tcl_ListObjAppendElement(interp, listObj, tcd->cmdName); if (tcd->args) { Tcl_Obj **args; int nrArgs, i; Tcl_ListObjGetElements(interp, tcd->args, &nrArgs, &args); for (i = 0; i < nrArgs; i++) { Tcl_ListObjAppendElement(interp, listObj, args[i]); } } } /* *---------------------------------------------------------------------- * AppendMethodRegistration -- * * Append to the listObj the command words needed for defintion / * registration. * * Results: * None. * * Side effects: * Appending to listObj * *---------------------------------------------------------------------- */ static void AppendMethodRegistration(Tcl_Interp *interp, Tcl_Obj *listObj, CONST char *registerCmdName, NsfObject *object, CONST char *methodName, Tcl_Command cmd, int withObjFrame, int withPer_object, int withProtection) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5) nonnull(6); static void AppendMethodRegistration(Tcl_Interp *interp, Tcl_Obj *listObj, CONST char *registerCmdName, NsfObject *object, CONST char *methodName, Tcl_Command cmd, int withObjFrame, int withPer_object, int withProtection) { assert(interp); assert(listObj); assert(registerCmdName); assert(object); assert(methodName); assert(cmd); Tcl_ListObjAppendElement(interp, listObj, object->cmdName); if (withProtection) { Tcl_ListObjAppendElement(interp, listObj, Tcl_Command_flags(cmd) & NSF_CMD_CALL_PRIVATE_METHOD ? Tcl_NewStringObj("private", 7) : Tcl_Command_flags(cmd) & NSF_CMD_CALL_PROTECTED_METHOD ? Tcl_NewStringObj("protected", 9) : Tcl_NewStringObj("public", 6)); } if (!NsfObjectIsClass(object) || withPer_object) { Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("object", 6)); } Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(registerCmdName, -1)); Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(methodName, -1)); if (withObjFrame) { Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-frame", 6)); Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("object", 6)); } if (Tcl_Command_flags(cmd) & NSF_CMD_NONLEAF_METHOD) { Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-frame", 6)); Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("method", 6)); } } /* *---------------------------------------------------------------------- * AppendReturnsClause -- * * Append to the listObj a returns clause, if it was spefified for the * current cmd. * * Results: * None. * * Side effects: * Appending to listObj * *---------------------------------------------------------------------- */ static void AppendReturnsClause(Tcl_Interp *interp, Tcl_Obj *listObj, Tcl_Command cmd) nonnull(1) nonnull(2) nonnull(3); static void AppendReturnsClause(Tcl_Interp *interp, Tcl_Obj *listObj, Tcl_Command cmd) { NsfParamDefs *paramDefs; assert(interp); assert(listObj); assert(cmd); paramDefs = ParamDefsGet(cmd, NULL); if (paramDefs && paramDefs->returns) { /* TODO: avoid hard-coding the script-level/NX-specific keyword "returns" */ Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-returns", -1)); Tcl_ListObjAppendElement(interp, listObj, paramDefs->returns); } } /* *---------------------------------------------------------------------- * ListMethod -- * * Constuct a command to regenerate the specified method. The method might * be scripted or not (alias, forwarder, ...). The command is returned in * the interp result. * * Results: * Tcl result code. * * Side effects: * Sets interp result * *---------------------------------------------------------------------- */ static int ListMethod(Tcl_Interp *interp, NsfObject *regObject, NsfObject *defObject, CONST char *methodName, Tcl_Command cmd, int subcmd, NsfObject *contextObject, CONST char *pattern, int withPer_object) nonnull(1) nonnull(4) nonnull(5); static int ListMethod(Tcl_Interp *interp, NsfObject *regObject, NsfObject *defObject, CONST char *methodName, Tcl_Command cmd, int subcmd, NsfObject *contextObject, CONST char *pattern, int withPer_object) { Tcl_ObjCmdProc *procPtr; int outputPerObject; Tcl_Obj *resultObj; assert(interp); assert(methodName); assert(*methodName != ':'); assert(cmd); Tcl_ResetResult(interp); if (regObject && !NsfObjectIsClass(regObject)) { withPer_object = 1; /* don't output "object" modifier, if regObject is not a class */ outputPerObject = 0; } else { outputPerObject = withPer_object; } procPtr = Tcl_Command_objProc(cmd); switch (subcmd) { case InfomethodsubcmdRegistrationhandleIdx: { if (regObject) { Tcl_SetObjResult(interp, MethodHandleObj(regObject, withPer_object, methodName)); } return TCL_OK; } case InfomethodsubcmdDefinitionhandleIdx: { if (defObject) { Tcl_SetObjResult(interp, MethodHandleObj(defObject, NsfObjectIsClass(defObject) ? withPer_object : 1, Tcl_GetCommandName(interp, cmd))); } return TCL_OK; } case InfomethodsubcmdExistsIdx: { Tcl_SetObjResult(interp, Tcl_NewIntObj(1)); return TCL_OK; } case InfomethodsubcmdArgsIdx: { Tcl_Command importedCmd = GetOriginalCommand(cmd); return ListCmdParams(interp, importedCmd, contextObject, pattern, methodName, NSF_PARAMS_NAMES); } case InfomethodsubcmdParameterIdx: { Tcl_Command importedCmd = GetOriginalCommand(cmd); return ListCmdParams(interp, importedCmd, contextObject, pattern, methodName, NSF_PARAMS_PARAMETER); } case InfomethodsubcmdReturnsIdx: { Tcl_Command importedCmd; NsfParamDefs *paramDefs; importedCmd = GetOriginalCommand(cmd); paramDefs = ParamDefsGet(importedCmd, NULL); if (paramDefs && paramDefs->returns) { Tcl_SetObjResult(interp, paramDefs->returns); } return TCL_OK; } case InfomethodsubcmdSyntaxIdx: { Tcl_Command importedCmd = GetOriginalCommand(cmd); return ListCmdParams(interp, importedCmd, contextObject, pattern, methodName, NSF_PARAMS_SYNTAX); } case InfomethodsubcmdPreconditionIdx: #if defined(NSF_WITH_ASSERTIONS) if (regObject) { NsfProcAssertion *procs = NULL; if (withPer_object) { if (regObject->opt && regObject->opt->assertions) { procs = AssertionFindProcs(regObject->opt->assertions, methodName); } } else { NsfClass *class = (NsfClass *)regObject; if (class->opt && class->opt->assertions) { procs = AssertionFindProcs(class->opt->assertions, methodName); } } if (procs) Tcl_SetObjResult(interp, AssertionList(interp, procs->pre)); } #endif return TCL_OK; case InfomethodsubcmdPostconditionIdx: #if defined(NSF_WITH_ASSERTIONS) if (regObject) { NsfProcAssertion *procs = NULL; if (withPer_object) { if (regObject->opt && regObject->opt->assertions) { procs = AssertionFindProcs(regObject->opt->assertions, methodName); } } else { NsfClass *class = (NsfClass *)regObject; if (class->opt && class->opt->assertions) { procs = AssertionFindProcs(class->opt->assertions, methodName); } } if (procs) Tcl_SetObjResult(interp, AssertionList(interp, procs->post)); } #endif return TCL_OK; case InfomethodsubcmdSubmethodsIdx: { Tcl_Command origCmd = GetOriginalCommand(cmd); if (CmdIsNsfObject(origCmd)) { NsfObject *subObject = NsfGetObjectFromCmdPtr(origCmd); if (subObject) { return ListDefinedMethods(interp, subObject, NULL, 1 /* per-object */, NSF_METHODTYPE_ALL, CallprotectionAllIdx, 0); } } /* all other cases return empty */ Tcl_SetObjResult(interp, NsfGlobalObjs[NSF_EMPTY]); return TCL_OK; } } /* * The subcommands differ per type of method. The converter in * InfoMethods defines the types: * * all|scripted|builtin|alias|forwarder|object|setter|nsfproc */ if (GetTclProcFromCommand(cmd)) { /* a scripted method */ switch (subcmd) { case InfomethodsubcmdTypeIdx: if (regObject) { Tcl_SetObjResult(interp, Tcl_NewStringObj("scripted", -1)); } else { Tcl_SetObjResult(interp, Tcl_NewStringObj("proc", -1)); } break; case InfomethodsubcmdBodyIdx: ListProcBody(interp, GetTclProcFromCommand(cmd), methodName); break; case InfomethodsubcmdDefinitionIdx: { resultObj = Tcl_NewListObj(0, NULL); /* todo: don't hard-code registering command name "method" / NSF_METHOD */ if (regObject) { AppendMethodRegistration(interp, resultObj, NsfGlobalStrings[NSF_METHOD], regObject, methodName, cmd, 0, outputPerObject, 1); } else { Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("::proc", -1)); Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(methodName,-1)); } ListCmdParams(interp, cmd, contextObject, NULL, methodName, NSF_PARAMS_PARAMETER); Tcl_ListObjAppendElement(interp, resultObj, Tcl_GetObjResult(interp)); AppendReturnsClause(interp, resultObj, cmd); ListProcBody(interp, GetTclProcFromCommand(cmd), methodName); Tcl_ListObjAppendElement(interp, resultObj, Tcl_GetObjResult(interp)); #if defined(NSF_WITH_ASSERTIONS) if (regObject) { NsfAssertionStore *assertions; if (withPer_object) { assertions = regObject->opt ? regObject->opt->assertions : NULL; } else { NsfClass *class = (NsfClass *)regObject; assertions = class->opt ? class->opt->assertions : NULL; } if (assertions) { NsfProcAssertion *procs = AssertionFindProcs(assertions, methodName); if (procs) { Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("-precondition", -1)); Tcl_ListObjAppendElement(interp, resultObj, AssertionList(interp, procs->pre)); Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("-postcondition", -1)); Tcl_ListObjAppendElement(interp, resultObj, AssertionList(interp, procs->post)); } } } #endif Tcl_SetObjResult(interp, resultObj); break; } } } else if (procPtr == NsfForwardMethod) { /* forwarder */ switch (subcmd) { case InfomethodsubcmdTypeIdx: Tcl_SetObjResult(interp, NsfGlobalObjs[NSF_FORWARD]); break; case InfomethodsubcmdDefinitionIdx: { ClientData clientData = cmd ? Tcl_Command_objClientData(cmd) : NULL; if (clientData) { resultObj = Tcl_NewListObj(0, NULL); /* todo: don't hard-code registering command name "forward" / NSF_FORWARD*/ AppendMethodRegistration(interp, resultObj, NsfGlobalStrings[NSF_FORWARD], regObject, methodName, cmd, 0, outputPerObject, 1); AppendReturnsClause(interp, resultObj, cmd); AppendForwardDefinition(interp, resultObj, clientData); Tcl_SetObjResult(interp, resultObj); break; } } } } else if (procPtr == NsfSetterMethod) { /* setter methods */ switch (subcmd) { case InfomethodsubcmdTypeIdx: Tcl_SetObjResult(interp, NsfGlobalObjs[NSF_SETTER]); break; case InfomethodsubcmdDefinitionIdx: { SetterCmdClientData *cd = (SetterCmdClientData *)Tcl_Command_objClientData(cmd); resultObj = Tcl_NewListObj(0, NULL); /* todo: don't hard-code registering command name "setter" / NSF_SETTER */ AppendMethodRegistration(interp, resultObj, NsfGlobalStrings[NSF_SETTER], regObject, (cd && cd->paramsPtr) ? ObjStr(cd->paramsPtr->paramObj) : methodName, cmd, 0, outputPerObject, 1); Tcl_SetObjResult(interp, resultObj); break; } } } else if (procPtr == NsfProcStub) { /* * Special nsfproc handling: */ NsfProcClientData *tcd = Tcl_Command_objClientData(cmd); if (tcd && tcd->procName) { Tcl_Command procCmd = Tcl_GetCommandFromObj(interp, tcd->procName); Tcl_DString ds, *dsPtr = &ds; Tcl_Obj *resultObj; switch (subcmd) { case InfomethodsubcmdTypeIdx: Tcl_SetObjResult(interp, Tcl_NewStringObj("nsfproc", -1)); break; case InfomethodsubcmdBodyIdx: ListProcBody(interp, GetTclProcFromCommand(procCmd), methodName); break; case InfomethodsubcmdDefinitionIdx: resultObj = Tcl_NewListObj(0, NULL); Tcl_DStringInit(dsPtr); DStringAppendQualName(dsPtr, Tcl_Command_nsPtr(cmd), methodName); /* don't hardcode names */ Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("::nsf::proc", -1)); if (tcd->with_ad) { Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("-ad", 3)); } Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr))); ListCmdParams(interp, cmd, NULL, NULL, Tcl_DStringValue(dsPtr), NSF_PARAMS_PARAMETER); Tcl_ListObjAppendElement(interp, resultObj, Tcl_GetObjResult(interp)); ListProcBody(interp, GetTclProcFromCommand(procCmd), methodName); Tcl_ListObjAppendElement(interp, resultObj, Tcl_GetObjResult(interp)); Tcl_SetObjResult(interp, resultObj); Tcl_DStringFree(dsPtr); break; } } } else if (defObject != NULL) { /* * The cmd must be an alias or object. * * Note that some aliases come with procPtr == NsfObjDispatch. * In order to distinguish between "object" and alias, we have * to do the lookup for the entryObj to determine wether it is * really an alias. */ Tcl_Obj *entryObj; entryObj = AliasGet(interp, defObject->cmdName, Tcl_GetCommandName(interp, cmd), regObject != defObject ? 1 : withPer_object, 0); /* fprintf(stderr, "aliasGet %s -> %s/%s (%d) returned %p\n", ObjectName(defObject), methodName, Tcl_GetCommandName(interp, cmd), withPer_object, entryObj); fprintf(stderr, "... regObject %p %s\n", regObject, ObjectName(regObject)); fprintf(stderr, "... defObject %p %s\n", defObject, ObjectName(defObject)); */ if (entryObj) { /* is an alias */ switch (subcmd) { case InfomethodsubcmdTypeIdx: Tcl_SetObjResult(interp, NsfGlobalObjs[NSF_ALIAS]); break; case InfomethodsubcmdDefinitionIdx: { int nrElements; Tcl_Obj **listElements; resultObj = Tcl_NewListObj(0, NULL); Tcl_ListObjGetElements(interp, entryObj, &nrElements, &listElements); /* todo: don't hard-code registering command name "alias" / NSF_ALIAS */ AppendMethodRegistration(interp, resultObj, NsfGlobalStrings[NSF_ALIAS], regObject, methodName, cmd, procPtr == NsfObjscopedMethod, outputPerObject, 1); AppendReturnsClause(interp, resultObj, cmd); Tcl_ListObjAppendElement(interp, resultObj, listElements[nrElements-1]); Tcl_SetObjResult(interp, resultObj); break; } case InfomethodsubcmdOriginIdx: { int nrElements; Tcl_Obj **listElements; Tcl_ListObjGetElements(interp, entryObj, &nrElements, &listElements); Tcl_SetObjResult(interp, listElements[nrElements-1]); break; } } } else { /* check, to be on the safe side */ if (CmdIsNsfObject(cmd)) { /* the command is an object */ switch (subcmd) { case InfomethodsubcmdTypeIdx: Tcl_SetObjResult(interp, Tcl_NewStringObj("object", -1)); break; case InfomethodsubcmdDefinitionIdx: { NsfObject *subObject = NsfGetObjectFromCmdPtr(cmd); assert(subObject); resultObj = Tcl_NewListObj(0, NULL); AppendMethodRegistration(interp, resultObj, "create", &(subObject->cl)->object, ObjStr(subObject->cmdName), cmd, 0, 0, 0); Tcl_SetObjResult(interp, resultObj); break; } } } else { /* * Should never happen. * * The warning is just a guess, so we don't raise an error here. */ NsfLog(interp, NSF_LOG_WARN, "Could not obtain alias definition for %s. " "Maybe someone deleted the alias %s for object %s?", methodName, methodName, ObjectName(regObject)); Tcl_ResetResult(interp); } } } else { /* * The cmd must be a plain unregisted cmd */ switch (subcmd) { case InfomethodsubcmdTypeIdx: Tcl_SetObjResult(interp, NsfGlobalObjs[NSF_CMD]); break; case InfomethodsubcmdDefinitionIdx: break; case InfomethodsubcmdOriginIdx: break; } } return TCL_OK; } /* *---------------------------------------------------------------------- * ListMethodResolve -- * * Call essentially ListMethod(), but try to resolve the method name/handle * first. * * Results: * Standard Tcl result * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ListMethodResolve(Tcl_Interp *interp, int subcmd, NsfObject *contextObject, CONST char *pattern, Tcl_Namespace *nsPtr, NsfObject *object, Tcl_Obj *methodNameObj, int fromClassNS) nonnull(1) nonnull(7); static int ListMethodResolve(Tcl_Interp *interp, int subcmd, NsfObject *contextObject, CONST char *pattern, Tcl_Namespace *nsPtr, NsfObject *object, Tcl_Obj *methodNameObj, int fromClassNS) { NsfObject *regObject, *defObject; CONST char *methodName1 = NULL; int result = TCL_OK; Tcl_DString ds, *dsPtr = &ds; Tcl_Command cmd; assert(interp); assert(methodNameObj); Tcl_DStringInit(dsPtr); cmd = ResolveMethodName(interp, nsPtr, methodNameObj, dsPtr, ®Object, &defObject, &methodName1, &fromClassNS); /* * If the cmd is not found, we return for every sub-command but "exists" * empty. */ if (likely(cmd != NULL)) { result = ListMethod(interp, regObject ? regObject : object, defObject ? defObject : object, methodName1, cmd, subcmd, contextObject, pattern, fromClassNS ? 0 : 1); } else if (subcmd == InfomethodsubcmdExistsIdx) { Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); } Tcl_DStringFree(dsPtr); return result; } /* *---------------------------------------------------------------------- * MethodSourceMatches -- * * Check, whether the provided class or object (mutually exclusive) matches * with the required method source (typically all|application|system). * * Results: * Returns true or false * * Side effects: * None. * *---------------------------------------------------------------------- */ static int MethodSourceMatches(int withSource, NsfClass *cl, NsfObject *object) { int isBaseClass; if (withSource == SourceAllIdx) { return 1; } if (cl == NULL) { /* * If the method is object specific, it can't be from a baseclass and must * be application specific. */ assert(object); return (withSource == SourceApplicationIdx && !IsBaseClass(object)); } assert(cl); isBaseClass = IsBaseClass(&cl->object); if (withSource == SourceSystemIdx && isBaseClass) { return 1; } else if (withSource == SourceApplicationIdx && !isBaseClass) { return 1; } return 0; } /* *---------------------------------------------------------------------- * MethodTypeMatches -- * * Check, whether the provided method (specified as a cmd) matches with the * required method type (typically * all|scripted|builtin|alias|forwarder|object|setter). * * Results: * Returns true or false * * Side effects: * None. * *---------------------------------------------------------------------- */ static int MethodTypeMatches(Tcl_Interp *interp, int methodType, Tcl_Command cmd, NsfObject *object, CONST char *methodName, int withPer_object, int *isObject) nonnull(1) nonnull(3) nonnull(5) nonnull(7); static int MethodTypeMatches(Tcl_Interp *interp, int methodType, Tcl_Command cmd, NsfObject *object, CONST char *methodName, int withPer_object, int *isObject) { Tcl_ObjCmdProc *proc, *resolvedProc; Tcl_Command importedCmd; assert(interp); assert(cmd); assert(methodName); assert(isObject); proc = Tcl_Command_objProc(cmd); importedCmd = GetOriginalCommand(cmd); resolvedProc = Tcl_Command_objProc(importedCmd); /* * Return always state isObject, since the cmd might be an ensemble, * where we have to search further */ *isObject = CmdIsNsfObject(importedCmd); if (methodType == NSF_METHODTYPE_ALIAS) { if (!(proc == NsfProcAliasMethod || AliasGet(interp, object->cmdName, methodName, withPer_object, 0))) { return 0; } } else { if (proc == NsfProcAliasMethod) { if ((methodType & NSF_METHODTYPE_ALIAS) == 0) return 0; } /* the following cases are disjoint */ if (CmdIsProc(importedCmd)) { /*fprintf(stderr,"%s scripted %d\n", methodName, methodType & NSF_METHODTYPE_SCRIPTED);*/ if ((methodType & NSF_METHODTYPE_SCRIPTED) == 0) return 0; } else if (resolvedProc == NsfForwardMethod) { if ((methodType & NSF_METHODTYPE_FORWARDER) == 0) return 0; } else if (resolvedProc == NsfSetterMethod) { if ((methodType & NSF_METHODTYPE_SETTER) == 0) return 0; } else if (*isObject) { if ((methodType & NSF_METHODTYPE_OBJECT) == 0) return 0; } else if (resolvedProc == NsfProcStub) { if ((methodType & NSF_METHODTYPE_NSFPROC) == 0) return 0; } else if ((methodType & NSF_METHODTYPE_OTHER) == 0) { /* fprintf(stderr,"OTHER %s not wanted %.4x\n", methodName, methodType);*/ return 0; } /* NsfObjscopedMethod ??? */ } return 1; } /* *---------------------------------------------------------------------- * ProtectionMatches -- * * Check, whether the provided method (specified as a cmd) matches with the * required call-protection (typically all|public|protected|private). * * Results: * Returns true or false * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ProtectionMatches(int withCallprotection, Tcl_Command cmd) nonnull(2); static int ProtectionMatches(int withCallprotection, Tcl_Command cmd) { int result, isProtected, isPrivate, cmdFlags; assert(cmd); cmdFlags = Tcl_Command_flags(cmd); isProtected = (cmdFlags & NSF_CMD_CALL_PROTECTED_METHOD) != 0; isPrivate = (cmdFlags & NSF_CMD_CALL_PRIVATE_METHOD) != 0; if (withCallprotection == CallprotectionNULL) { withCallprotection = CallprotectionPublicIdx; } switch (withCallprotection) { case CallprotectionAllIdx: result = 1; break; case CallprotectionPublicIdx: result = (isProtected == 0); break; case CallprotectionProtectedIdx: result = isProtected && !isPrivate; break; case CallprotectionPrivateIdx: result = isPrivate; break; default: result = 1; } return result; } /* *---------------------------------------------------------------------- * * ListMethodKeys -- * * List the method names contained in the specified hash-table * according to the filtering options (types, pattern, * protection, etc.). Optionally, a name prefix can be provided * in form of a Tcl_DString. The result is placed into the interp * result. * * Results: * Tcl result code. * * Side effects: * Setting interp result. * *---------------------------------------------------------------------- */ static int ListMethodKeys(Tcl_Interp *interp, Tcl_HashTable *tablePtr, Tcl_DString *prefix, CONST char *pattern, int methodType, int withCallprotection, int withPath, Tcl_HashTable *dups, NsfObject *object, int withPer_object) nonnull(1) nonnull(2); static int ListMethodKeys(Tcl_Interp *interp, Tcl_HashTable *tablePtr, Tcl_DString *prefix, CONST char *pattern, int methodType, int withCallprotection, int withPath, Tcl_HashTable *dups, NsfObject *object, int withPer_object) { Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; Tcl_Command cmd; char *key; int isObject, methodTypeMatch; int prefixLength = prefix ? Tcl_DStringLength(prefix) : 0; Tcl_Obj *resultObj = Tcl_GetObjResult(interp); assert(interp); assert(tablePtr); if (pattern && NoMetaChars(pattern) && strchr(pattern, ' ') == NULL) { /* * We have a pattern that can be used for direct lookup; no need * to iterate. */ hPtr = Tcl_CreateHashEntry(tablePtr, pattern, NULL); if (hPtr) { NsfObject *childObject; Tcl_Command origCmd; key = Tcl_GetHashKey(tablePtr, hPtr); cmd = (Tcl_Command)Tcl_GetHashValue(hPtr); methodTypeMatch = MethodTypeMatches(interp, methodType, cmd, object, key, withPer_object, &isObject); if (Tcl_Command_flags(cmd) & NSF_CMD_CLASS_ONLY_METHOD && !NsfObjectIsClass(object)) { return TCL_OK; } /* * Aliased objects methods return 1 but lookup from cmd returns * NULL. Below, we are just interested on true subobjects. */ origCmd = GetOriginalCommand(cmd); childObject = isObject ? NsfGetObjectFromCmdPtr(origCmd) : NULL; if (childObject) { if (withPath) { return TCL_OK; } /* * Treat aliased object dispatch different from direct object * dispatches. */ #if 0 if (cmd == origCmd && (childObject->flags & NSF_ALLOW_METHOD_DISPATCH ) == 0) { /*fprintf(stderr, "no method dispatch allowed on child %s\n", ObjectName(childObject));*/ return TCL_OK; } #endif } if (ProtectionMatches(withCallprotection, cmd) && methodTypeMatch) { if (prefixLength) { Tcl_DStringAppend(prefix, key, -1); key = Tcl_DStringValue(prefix); } if (dups) { int new; Tcl_CreateHashEntry(dups, key, &new); if (new) { Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(key, -1)); } } else { Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(key, -1)); } } } return TCL_OK; } else { /* * We have to iterate over the elements */ for (hPtr = Tcl_FirstHashEntry(tablePtr, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { NsfObject *childObject; Tcl_Command origCmd; key = Tcl_GetHashKey(tablePtr, hPtr); cmd = (Tcl_Command)Tcl_GetHashValue(hPtr); if (prefixLength) {Tcl_DStringTrunc(prefix, prefixLength);} methodTypeMatch = MethodTypeMatches(interp, methodType, cmd, object, key, withPer_object, &isObject); /* * Aliased objects methods return 1 but lookup from cmd returns * NULL. Below, we are just interested on true subobjects. */ origCmd = GetOriginalCommand(cmd); childObject = isObject ? NsfGetObjectFromCmdPtr(origCmd) : NULL; if (childObject) { if (withPath) { Tcl_DString ds, *dsPtr = &ds; Tcl_HashTable *cmdTablePtr = childObject->nsPtr ? Tcl_Namespace_cmdTablePtr(childObject->nsPtr) : NULL; if (cmdTablePtr == NULL) { /* nothing to do */ continue; } if (childObject->flags & NSF_IS_SLOT_CONTAINER) { /* Don't report slot container */ continue; } if ((childObject->flags & NSF_KEEP_CALLER_SELF) == 0) { /* Do only report sub-objects with keep caller self */ continue; } /*fprintf(stderr, "ListMethodKeys key %s append key space flags %.6x\n", key, childObject->flags);*/ if (prefix == NULL) { DSTRING_INIT(dsPtr); Tcl_DStringAppend(dsPtr, key, -1); Tcl_DStringAppend(dsPtr, " ", 1); ListMethodKeys(interp, cmdTablePtr, dsPtr, pattern, methodType, withCallprotection, 1, dups, object, withPer_object); DSTRING_FREE(dsPtr); } else { Tcl_DStringAppend(prefix, key, -1); Tcl_DStringAppend(prefix, " ", 1); ListMethodKeys(interp, cmdTablePtr, prefix, pattern, methodType, withCallprotection, 1, dups, object, withPer_object); } /* don't list ensembles by themselves */ continue; } /* * Treat aliased object dispatch different from direct object * dispatches. */ #if 0 if (cmd == origCmd && (childObject->flags & NSF_ALLOW_METHOD_DISPATCH ) == 0) { /*fprintf(stderr, "no method dispatch allowed on child %s\n", ObjectName(childObject));*/ continue; } #endif } if (Tcl_Command_flags(cmd) & NSF_CMD_CLASS_ONLY_METHOD && !NsfObjectIsClass(object)) continue; if (!ProtectionMatches(withCallprotection, cmd) || !methodTypeMatch) continue; if (prefixLength) { Tcl_DStringAppend(prefix, key, -1); key = Tcl_DStringValue(prefix); } if (pattern && !Tcl_StringMatch(key, pattern)) continue; if (dups) { int new; Tcl_CreateHashEntry(dups, key, &new); if (!new) continue; } Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(key, -1)); } } /*fprintf(stderr, "listkeys returns '%s'\n", ObjStr(Tcl_GetObjResult(interp)));*/ return TCL_OK; } /* *---------------------------------------------------------------------- * * ListChildren -- * * List the children of the specified object. The result can be filtered * via a pattern or a type. * * Results: * Tcl result code. * * Side effects: * Setting interp result. * *---------------------------------------------------------------------- */ static int ListChildren(Tcl_Interp *interp, NsfObject *object, CONST char *pattern, int classesOnly, NsfClass *type) nonnull(1) nonnull(2); static int ListChildren(Tcl_Interp *interp, NsfObject *object, CONST char *pattern, int classesOnly, NsfClass *type) { NsfObject *childObject; assert(interp); assert(object); if (object->nsPtr == NULL) { return TCL_OK; } if (pattern != NULL && NoMetaChars(pattern)) { Tcl_DString ds, *dsPtr = &ds; Tcl_DStringInit(dsPtr); if (*pattern != ':') { /* build a fully qualified name */ DStringAppendQualName(dsPtr, object->nsPtr, pattern); pattern = Tcl_DStringValue(dsPtr); } if ((childObject = GetObjectFromString(interp, pattern)) && (!classesOnly || NsfObjectIsClass(childObject)) && (!type || IsSubType(childObject->cl, type)) && (Tcl_Command_nsPtr(childObject->id) == object->nsPtr) /* true children */ ) { Tcl_SetObjResult(interp, childObject->cmdName); } else { Tcl_SetObjResult(interp, NsfGlobalObjs[NSF_EMPTY]); } Tcl_DStringFree(dsPtr); } else { Tcl_Obj *list = Tcl_NewListObj(0, NULL); Tcl_HashSearch hSrch; Tcl_HashTable *cmdTablePtr = Tcl_Namespace_cmdTablePtr(object->nsPtr); Tcl_HashEntry *hPtr; for (hPtr = Tcl_FirstHashEntry(cmdTablePtr, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { char *key = Tcl_GetHashKey(cmdTablePtr, hPtr); if (pattern == NULL || Tcl_StringMatch(key, pattern)) { Tcl_Command cmd = (Tcl_Command)Tcl_GetHashValue(hPtr); /*fprintf(stderr, "... check %s child key %s child object %p %p\n", ObjectName(object), key, GetObjectFromString(interp, key), NsfGetObjectFromCmdPtr(cmd));*/ if ((childObject = NsfGetObjectFromCmdPtr(cmd)) && (!classesOnly || NsfObjectIsClass(childObject)) && (!type || IsSubType(childObject->cl, type)) && (Tcl_Command_nsPtr(childObject->id) == object->nsPtr) /* true children */ ) { Tcl_ListObjAppendElement(interp, list, childObject->cmdName); } } } Tcl_SetObjResult(interp, list); } return TCL_OK; } /* *---------------------------------------------------------------------- * * ListForward -- * * List registered forwareder defined in the hash table. The result can be filtered * via a pattern, optionally the forward definition is returned. * * Results: * Tcl result code. * * Side effects: * Setting interp result. * *---------------------------------------------------------------------- */ static int ListForward(Tcl_Interp *interp, Tcl_HashTable *tablePtr, CONST char *pattern, int withDefinition) nonnull(1) nonnull(2); static int ListForward(Tcl_Interp *interp, Tcl_HashTable *tablePtr, CONST char *pattern, int withDefinition) { assert(interp); assert(tablePtr); if (withDefinition) { Tcl_HashEntry *hPtr = pattern ? Tcl_CreateHashEntry(tablePtr, pattern, NULL) : NULL; /* * Notice: we don't use pattern for wildcard matching here; pattern can * only contain wildcards when used without "-definition". */ if (hPtr) { Tcl_Command cmd = (Tcl_Command)Tcl_GetHashValue(hPtr); ClientData clientData = cmd ? Tcl_Command_objClientData(cmd) : NULL; ForwardCmdClientData *tcd = (ForwardCmdClientData *)clientData; if (tcd && Tcl_Command_objProc(cmd) == NsfForwardMethod) { Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); AppendForwardDefinition(interp, listObj, tcd); Tcl_SetObjResult(interp, listObj); return TCL_OK; } } return NsfPrintError(interp, "'%s' is not a forwarder", pattern); } return ListMethodKeys(interp, tablePtr, NULL, pattern, NSF_METHODTYPE_FORWARDER, CallprotectionAllIdx, 0, NULL, NULL, 0); } /* *---------------------------------------------------------------------- * * ListDefinedMethods -- * * List the methods defined by the specified object/class * according to the filtering options (types, pattern, * protection, etc.). The result is placed into the interp * result. * * Results: * Tcl result code. * * Side effects: * Setting interp result. * *---------------------------------------------------------------------- */ static int ListDefinedMethods(Tcl_Interp *interp, NsfObject *object, CONST char *pattern, int withPer_object, int methodType, int withCallproctection, int withPath) { Tcl_HashTable *cmdTablePtr; Tcl_DString ds, *dsPtr = NULL; assert(interp); assert(object); if (pattern && *pattern == ':' && *(pattern + 1) == ':') { Namespace *nsPtr, *dummy1Ptr, *dummy2Ptr; CONST char *remainder; /*fprintf(stderr, "we have a colon pattern '%s' methodtype %.6x\n", pattern, methodType);*/ TclGetNamespaceForQualName(interp, pattern, NULL, 0, &nsPtr, &dummy1Ptr, &dummy2Ptr, &remainder); /*fprintf(stderr, "TclGetNamespaceForQualName with %s => (%p %s) (%p %s) (%p %s) (%p %s)\n", pattern, nsPtr, nsPtr ? nsPtr->fullName : "", dummy1Ptr, dummy1Ptr ? dummy1Ptr->fullName : "", dummy2Ptr, dummy2Ptr ? dummy2Ptr->fullName : "", remainder, remainder ? remainder : "");*/ if (nsPtr) { cmdTablePtr = Tcl_Namespace_cmdTablePtr(nsPtr); dsPtr = &ds; Tcl_DStringInit(dsPtr); Tcl_DStringAppend(dsPtr, nsPtr->fullName, -1); if (Tcl_DStringLength(dsPtr) > 2) { Tcl_DStringAppend(dsPtr, "::", 2); } pattern = remainder; } else { cmdTablePtr = NULL; } } else if (NsfObjectIsClass(object) && !withPer_object) { cmdTablePtr = Tcl_Namespace_cmdTablePtr(((NsfClass *)object)->nsPtr); } else { cmdTablePtr = object->nsPtr ? Tcl_Namespace_cmdTablePtr(object->nsPtr) : NULL; } if (cmdTablePtr) { ListMethodKeys(interp, cmdTablePtr, dsPtr, pattern, methodType, withCallproctection, withPath, NULL, object, withPer_object); if (dsPtr) { Tcl_DStringFree(dsPtr); } } return TCL_OK; } /* *---------------------------------------------------------------------- * * ListSuperClasses -- * * List the superclasses of a class. Optionally the transitive closure is * computed and the result can be filtered via a pattern. * * Results: * Tcl result code. * * Side effects: * Setting interp result. * *---------------------------------------------------------------------- */ static int ListSuperClasses(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *pattern, int withClosure) nonnull(1) nonnull(2); static int ListSuperClasses(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *pattern, int withClosure) { NsfObject *matchObject = NULL; Tcl_Obj *patternObj = NULL, *outObjPtr; CONST char *patternString = NULL; ClientData clientData; int rc; assert(interp); assert(cl); if (cl->super == NULL) { return TCL_OK; } if (pattern != NULL && ConvertToObjpattern(interp, pattern, NULL, &clientData, &outObjPtr) == TCL_OK) { patternObj = (Tcl_Obj *)clientData; if (GetMatchObject(interp, patternObj, pattern, &matchObject, &patternString) == -1) { /* * The pattern has no meta chars and does not correspond to an existing * object. Therefore, it can't be a superclass. */ if (patternObj) { DECR_REF_COUNT2("patternObj", patternObj); } return TCL_OK; } } if (withClosure) { NsfClasses *pl = PrecedenceOrder(cl); if (pl) pl = pl->nextPtr; rc = AppendMatchingElementsFromClasses(interp, pl, patternString, matchObject); } else { NsfClasses *clSuper = NsfReverseClasses(cl->super); rc = AppendMatchingElementsFromClasses(interp, clSuper, patternString, matchObject); NsfClassListFree(clSuper); } if (matchObject) { Tcl_SetObjResult(interp, rc ? matchObject->cmdName : NsfGlobalObjs[NSF_EMPTY]); } if (patternObj) { DECR_REF_COUNT2("patternObj", patternObj); } return TCL_OK; } /******************************** * End result setting commands ********************************/ /* *---------------------------------------------------------------------- * * AliasIndex -- * * The alias index is an internal data structure to keep track how * aliases are constructed. This function computes the key of the index. * * Results: * string value of the index * * Side effects: * updating DString * *---------------------------------------------------------------------- */ static CONST char *AliasIndex(Tcl_DString *dsPtr, Tcl_Obj *cmdName, CONST char *methodName, int withPer_object) nonnull(1) nonnull(2) nonnull(3) returns_nonnull; static CONST char * AliasIndex(Tcl_DString *dsPtr, Tcl_Obj *cmdName, CONST char *methodName, int withPer_object) { assert(dsPtr); assert(cmdName); assert(methodName); Tcl_DStringInit(dsPtr); Tcl_DStringAppend(dsPtr, ObjStr(cmdName), -1); Tcl_DStringAppend(dsPtr, ",", 1); Tcl_DStringAppend(dsPtr, methodName, -11); if (withPer_object) { Tcl_DStringAppend(dsPtr, ",1", 2); } else { Tcl_DStringAppend(dsPtr, ",0", 2); } /*fprintf(stderr, "AI %s\n", Tcl_DStringValue(dsPtr));*/ return Tcl_DStringValue(dsPtr); } /* *---------------------------------------------------------------------- * * AliasAdd -- * * Add an alias to the alias index * * Results: * Standard Tcl result * * Side effects: * Adding value to the hidden associated array. * *---------------------------------------------------------------------- */ static int AliasAdd(Tcl_Interp *interp, Tcl_Obj *cmdName, CONST char *methodName, int withPer_object, CONST char *cmd) nonnull(1) nonnull(2) nonnull(3) nonnull(5); static int AliasAdd(Tcl_Interp *interp, Tcl_Obj *cmdName, CONST char *methodName, int withPer_object, CONST char *cmd) { Tcl_DString ds, *dsPtr = &ds; assert(interp); assert(cmdName); assert(methodName); assert(cmd); Tcl_SetVar2Ex(interp, NsfGlobalStrings[NSF_ARRAY_ALIAS], AliasIndex(dsPtr, cmdName, methodName, withPer_object), Tcl_NewStringObj(cmd, -1), TCL_GLOBAL_ONLY); /*fprintf(stderr, "aliasAdd ::nsf::alias(%s) '%s' returned %p\n", AliasIndex(dsPtr, cmdName, methodName, withPer_object), cmd, 1);*/ Tcl_DStringFree(dsPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * AliasDelete -- * * Delete an alias from the index * * Results: * Standard Tcl result * * Side effects: * delete an entry from the hidden associative array * *---------------------------------------------------------------------- */ static int AliasDelete(Tcl_Interp *interp, Tcl_Obj *cmdName, CONST char *methodName, int withPer_object) { Tcl_DString ds, *dsPtr = &ds; int result; assert(interp); assert(cmdName); assert(methodName); result = Tcl_UnsetVar2(interp, NsfGlobalStrings[NSF_ARRAY_ALIAS], AliasIndex(dsPtr, cmdName, methodName, withPer_object), TCL_GLOBAL_ONLY); /*fprintf(stderr, "aliasDelete ::nsf::alias(%s) returned %d (%d)\n", AliasIndex(dsPtr, cmdName, methodName, withPer_object), result);*/ Tcl_DStringFree(dsPtr); return result; } /* *---------------------------------------------------------------------- * * AliasGet -- * * Get an entry from the alias index * * Results: * Tcl obj * * Side effects: * delete an entry from the hidden associative array * *---------------------------------------------------------------------- */ static Tcl_Obj * AliasGet(Tcl_Interp *interp, Tcl_Obj *cmdName, CONST char *methodName, int withPer_object, int leaveError) { Tcl_DString ds, *dsPtr = &ds; Tcl_Obj *obj; assert(interp); assert(cmdName); assert(methodName); obj = Tcl_GetVar2Ex(interp, NsfGlobalStrings[NSF_ARRAY_ALIAS], AliasIndex(dsPtr, cmdName, methodName, withPer_object), TCL_GLOBAL_ONLY); /*fprintf(stderr, "aliasGet methodName '%s' returns %p\n", methodName, obj);*/ Tcl_DStringFree(dsPtr); if (obj == NULL && leaveError) { NsfPrintError(interp, "could not obtain alias definition for %s %s.", ObjStr(cmdName), methodName); } return obj; } /* *---------------------------------------------------------------------- * AliasDeleteObjectReference -- * * Delete an alias to an referenced object. Such aliases are * created by registering an alias to an object. This function * distinguishes between a sub-object and an alias to an object, * deletes the alias but never the referenced object. * * Results: * 1 when alias is deleted. * * Side effects: * Deletes cmd sometimes * *---------------------------------------------------------------------- */ static int AliasDeleteObjectReference(Tcl_Interp *interp, Tcl_Command cmd) { NsfObject *referencedObject = NsfGetObjectFromCmdPtr(cmd); assert(interp); assert(cmd); assert(referencedObject); /*fprintf(stderr, "AliasDeleteObjectReference on %p obj %p\n", cmd, referencedObject);*/ if (referencedObject->refCount > 0 && cmd != referencedObject->id) { /* * The cmd is an aliased object, reduce the refCount of the * object, delete the cmd. */ /*fprintf(stderr, "remove alias %s to %s\n", Tcl_GetCommandName(interp, cmd), ObjectName(referencedObject));*/ NsfCleanupObject(referencedObject, "AliasDeleteObjectReference"); Tcl_DeleteCommandFromToken(interp, cmd); return 1; } return 0; } /* *---------------------------------------------------------------------- * AliasRefetch -- * * Perform a refetch of an epoched aliased cmd and update the * AliasCmdClientData structure with fresh values. * * Results: * Tcl result code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int AliasRefetch(Tcl_Interp *interp, NsfObject *object, CONST char *methodName, AliasCmdClientData *tcd) { Tcl_Obj **listElements, *entryObj, *targetObj; int nrElements, withPer_object; NsfObject *defObject; Tcl_Command cmd; assert(interp); assert(object); assert(methodName); assert(tcd); defObject = tcd->class ? &(tcd->class->object) : object; /* * Get the targetObject. Currently, we can get it just via the * alias array. */ withPer_object = tcd->class ? 0 : 1; entryObj = AliasGet(interp, defObject->cmdName, methodName, withPer_object, 1); if (entryObj == NULL) { return TCL_ERROR; } INCR_REF_COUNT(entryObj); Tcl_ListObjGetElements(interp, entryObj, &nrElements, &listElements); targetObj = listElements[nrElements-1]; NsfLog(interp, NSF_LOG_NOTICE, "trying to refetch an epoched cmd %p as %s -- cmdName %s\n", tcd->aliasedCmd, methodName, ObjStr(targetObj)); /* * Replace cmd and its objProc and clientData with a newly fetched * version. */ cmd = Tcl_GetCommandFromObj(interp, targetObj); if (cmd) { cmd = GetOriginalCommand(cmd); /*fprintf(stderr, "cmd %p epoch %d deleted %.6x\n", cmd, Tcl_Command_cmdEpoch(cmd), Tcl_Command_flags(cmd) & CMD_IS_DELETED);*/ if (Tcl_Command_flags(cmd) & CMD_IS_DELETED) { cmd = NULL; } } if (cmd == NULL) { int result = NsfPrintError(interp, "target \"%s\" of alias %s apparently disappeared", ObjStr(targetObj), methodName); DECR_REF_COUNT(entryObj); return result; } assert(Tcl_Command_objProc(cmd)); NsfCommandRelease(tcd->aliasedCmd); tcd->objProc = Tcl_Command_objProc(cmd); tcd->aliasedCmd = cmd; tcd->clientData = Tcl_Command_objClientData(cmd); NsfCommandPreserve(tcd->aliasedCmd); DECR_REF_COUNT(entryObj); /* * Now, we should be able to proceed as planned, we have an * non-epoched aliasCmd. */ return TCL_OK; } /* *---------------------------------------------------------------------- * AliasDereference -- * * Dereference a cmd in respect of the the alias structure. If necessary, * this command refetches the aliased command. * * Results: * NULL, in case refetching fails, * the aliased cmd if it was an alias, or * the original cmd * * Side effects: * None. * *---------------------------------------------------------------------- */ NSF_INLINE static Tcl_Command AliasDereference(Tcl_Interp *interp, NsfObject *object, CONST char *methodName, Tcl_Command cmd) nonnull(1) nonnull(2) nonnull(3) nonnull(4); NSF_INLINE static Tcl_Command AliasDereference(Tcl_Interp *interp, NsfObject *object, CONST char *methodName, Tcl_Command cmd) { assert(interp); assert(object); assert(methodName); assert(cmd); if (unlikely(Tcl_Command_objProc(cmd) == NsfProcAliasMethod)) { AliasCmdClientData *tcd = (AliasCmdClientData *)Tcl_Command_objClientData(cmd); assert(tcd); if (unlikely(Tcl_Command_cmdEpoch(tcd->aliasedCmd))) { /*fprintf(stderr, "NsfProcAliasMethod aliasedCmd %p epoch %p\n", tcd->aliasedCmd, Tcl_Command_cmdEpoch(tcd->aliasedCmd));*/ if (AliasRefetch(interp, object, methodName, tcd) != TCL_OK) { return NULL; } } return tcd->aliasedCmd; } return cmd; } #if defined(NSF_ASSEMBLE) # include "asm/nsfAssemble.c" #else static int NsfAsmMethodCreateCmd(Tcl_Interp *interp, NsfObject *defObject, int with_checkAlways, int withInner_namespace, int withPer_object, NsfObject *regObject, Tcl_Obj *nameObj, Tcl_Obj *argumentsObj, Tcl_Obj *bodyObj) { assert(interp); assert(defObject); assert(nameObj); assert(argumentsObj); assert(bodyObj); /* * Dummy stub; used, when compiled without NSF_ASSEMBLE */ return TCL_OK; } #endif /* *---------------------------------------------------------------------- * SetBooleanFlag -- * * Set an unsigned int flag based on valueObj * * Results: * Tcl result code * * Side effects: * update passed flags * *---------------------------------------------------------------------- */ static int SetBooleanFlag(Tcl_Interp *interp, unsigned int *flagsPtr, unsigned int flag, Tcl_Obj *valueObj, int *flagValue) nonnull(1) nonnull(2) nonnull(4) nonnull(5); static int SetBooleanFlag(Tcl_Interp *interp, unsigned int *flagsPtr, unsigned int flag, Tcl_Obj *valueObj, int *flagValue) { int result; assert(interp); assert(flagsPtr); assert(valueObj); assert(flagValue); result = Tcl_GetBooleanFromObj(interp, valueObj, flagValue); if (unlikely(result != TCL_OK)) { return result; } if (*flagValue) { *flagsPtr |= flag; } else { *flagsPtr &= ~flag; } return result; } /*********************************************************************** * Begin generated Next Scripting commands ***********************************************************************/ /* cmd __db_compile_epoch NsfDebugCompileEpoch {} */ static int NsfDebugCompileEpoch(Tcl_Interp *interp) nonnull(1); static int NsfDebugCompileEpoch(Tcl_Interp *interp) { Interp *iPtr = (Interp *) interp; assert(interp); Tcl_SetObjResult(interp, Tcl_NewIntObj(iPtr->compileEpoch)); return TCL_OK; } /* cmd __db_show_obj NsfDebugShowObj { {-argName "obj" -required 1 -type tclobj} } */ static int NsfDebugShowObj(Tcl_Interp *interp, Tcl_Obj *objPtr) nonnull(1) nonnull(2); static int NsfDebugShowObj(Tcl_Interp *interp, Tcl_Obj *objPtr) { assert(interp); assert(objPtr); fprintf(stderr, "*** obj %p refCount %d type <%s>\n", objPtr, objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : ""); if (objPtr->typePtr == &NsfObjectMethodObjType || objPtr->typePtr == &NsfInstanceMethodObjType ) { NsfMethodContext *mcPtr = objPtr->internalRep.twoPtrValue.ptr1; int currentMethodEpoch = objPtr->typePtr == &NsfObjectMethodObjType ? RUNTIME_STATE(interp)->objectMethodEpoch : RUNTIME_STATE(interp)->instanceMethodEpoch; Tcl_Command cmd = mcPtr->cmd; fprintf(stderr, " method epoch %d max %d cmd %p objProc %p flags %.6x\n", mcPtr->methodEpoch, currentMethodEpoch, cmd, cmd ? ((Command *)cmd)->objProc : 0, mcPtr->flags); if (cmd) { fprintf(stderr, "... cmd %p flags %.6x\n", cmd, Tcl_Command_flags(cmd)); assert(((Command *)cmd)->objProc != NULL); } assert( currentMethodEpoch >= mcPtr->methodEpoch); } return TCL_OK; } /* cmd __db_show_stack NsfShowStackCmd {} */ static int NsfShowStackCmd(Tcl_Interp *interp) { assert(interp); NsfShowStack(interp); return TCL_OK; } /* cmd __db_run_assertions NsfDebugRunAssertionsCmd {} */ static int NsfDebugRunAssertionsCmd(Tcl_Interp *interp) { NsfObjectSystem *osPtr; NsfCmdList *instances = NULL, *entry; assert(interp); /* * Collect all instances from all object systems. */ for (osPtr = RUNTIME_STATE(interp)->objectSystems; osPtr; osPtr = osPtr->nextPtr) { GetAllInstances(interp, &instances, osPtr->rootClass); } for (entry = instances; entry; entry = entry->nextPtr) { #if !defined(NDEBUG) NsfObject *object = (NsfObject *)entry->clorobj; #endif assert(object); assert(object->refCount > 0); assert(object->cmdName->refCount > 0); assert(object->activationCount >= 0); #if defined(CHECK_ACTIVATION_COUNTS) if (object->activationCount > 0) { Tcl_CallFrame *framePtr; int count = 0; NsfClasses *unstackedEntries = RUNTIME_STATE(interp)->cscList; /*fprintf(stderr, "DEBUG obj %p %s activationcount %d\n", object, ObjectName(object), object->activationCount);*/ framePtr = (Tcl_CallFrame *)Tcl_Interp_framePtr(interp); for (; framePtr; framePtr = Tcl_CallFrame_callerPtr(framePtr)) { int frameFlags = Tcl_CallFrame_isProcCallFrame(framePtr); NsfCallStackContent *cscPtr = (frameFlags & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) ? ((NsfCallStackContent *)Tcl_CallFrame_clientData(framePtr)) : NULL; if (cscPtr && cscPtr->self == object) count ++; if (cscPtr && (NsfObject *)cscPtr->cl == object) count ++; } for (; unstackedEntries; unstackedEntries = unstackedEntries->nextPtr) { NsfCallStackContent *cscPtr = (NsfCallStackContent *)unstackedEntries->cl; if (cscPtr && cscPtr->self == object) count ++; if (cscPtr && (NsfObject *)cscPtr->cl == object) count ++; } if (count != object->activationCount) { fprintf(stderr, "DEBUG obj %p %s activationcount %d on stack %d; " "might be from non-stacked but active call-stack content\n", object, ObjectName(object), object->activationCount, count); fprintf(stderr, "fixed count %d\n", count); /*NsfShowStack(interp);*/ /*return NsfPrintError(interp, "wrong activation count for object %s", ObjectName(object));*/ } } #endif } CmdListFree(&instances, NULL); /*fprintf(stderr, "all assertions passed\n");*/ return TCL_OK; } /* cmd __profile_clear_data NsfProfileClearDataStub {} */ static int NsfProfileClearDataStub(Tcl_Interp *interp) nonnull(1); static int NsfProfileClearDataStub(Tcl_Interp *interp) { assert(interp); #if defined(NSF_PROFILE) NsfProfileClearData(interp); #endif return TCL_OK; } /* cmd __profile_get_data NsfProfileGetDataStub {} */ static int NsfProfileGetDataStub(Tcl_Interp *interp) nonnull(1); static int NsfProfileGetDataStub(Tcl_Interp *interp) { assert(interp); #if defined(NSF_PROFILE) NsfProfileGetData(interp); #endif return TCL_OK; } /* *---------------------------------------------------------------------- * NsfUnsetUnknownArgsCmd -- * * Unset variables set from arguments with the default dummy * default value. The dummy default values are set by * ArgumentDefaults() * * Results: * Tcl result code. * * Side effects: * unsets some variables * *---------------------------------------------------------------------- */ /* cmd __unset_unknown_args NsfUnsetUnknownArgsCmd {} */ static int NsfUnsetUnknownArgsCmd(Tcl_Interp *interp) { CallFrame *varFramePtr = Tcl_Interp_varFramePtr(interp); Proc *proc = Tcl_CallFrame_procPtr(varFramePtr); assert(interp); if (likely(proc != NULL)) { CompiledLocal *ap; Var *varPtr; int i; for (ap = proc->firstLocalPtr, i = 0; ap; ap = ap->nextPtr, i++) { if (!TclIsCompiledLocalArgument(ap)) continue; varPtr = &Tcl_CallFrame_compiledLocals(varFramePtr)[i]; /*fprintf(stderr, "NsfUnsetUnknownArgsCmd var '%s' i %d fi %d var %p flags %.8x obj %p unk %p\n", ap->name, i, ap->frameIndex, varPtr, varPtr->flags, varPtr->value.objPtr, NsfGlobalObjs[NSF___UNKNOWN__]);*/ if (varPtr->value.objPtr != NsfGlobalObjs[NSF___UNKNOWN__]) continue; /*fprintf(stderr, "NsfUnsetUnknownArgsCmd must unset %s\n", ap->name);*/ Tcl_UnsetVar2(interp, ap->name, NULL, 0); } } return TCL_OK; } /* cmd asmproc NsfAsmProcCmd { {-argName "-ad" -required 0 -nrargs 0 -type switch} {-argName "-checkalways" -required 0 -nrargs 0 -type switch} {-argName "procName" -required 1 -type tclobj} {-argName "arguments" -required 1 -type tclobj} {-argName "body" -required 1 -type tclobj} } */ #if !defined(NSF_ASSEMBLE) static int NsfAsmProcCmd(Tcl_Interp *interp, int with_ad, int with_checkAlways, Tcl_Obj *nameObj, Tcl_Obj *arguments, Tcl_Obj *body) { assert(interp); assert(nameObj); assert(arguments); assert(body); return TCL_OK; } #else static int NsfAsmProcCmd(Tcl_Interp *interp, int with_ad, int with_checkAlways, Tcl_Obj *nameObj, Tcl_Obj *arguments, Tcl_Obj *body) { NsfParsedParam parsedParam; int result; assert(interp); assert(nameObj); assert(arguments); assert(body); /* * Parse argument list "arguments" to determine if we should provide * nsf parameter handling. */ result = ParamDefsParse(interp, nameObj, arguments, NSF_DISALLOWED_ARG_METHOD_PARAMETER, 0, &parsedParam); if (unlikely(result != TCL_OK)) { return result; } if (parsedParam.paramDefs) { /* * We need parameter handling. */ result = NsfAsmProcAddParam(interp, &parsedParam, nameObj, body, with_ad, with_checkAlways); } else { /* * No parameter handling needed. */ result = NsfAsmProcAddArgs(interp, arguments, nameObj, body, with_ad, with_checkAlways); } return result; } #endif /* cmd "cmd::info" NsfCmdInfoCmd { {-argName "subcmd" -required 1 -typeName "methodgetcmd" -type "args|body|definition|exists|registrationhandle|definitionhandle|origin|parameter|syntax|type|precondition|postcondition|submethods|returns"} {-argName "-context" -required 0 -type object} {-argName "methodName" -required 1 -type tclobj} {-argName "pattern" -required 0} } {-nxdoc 1} */ static int NsfCmdInfoCmd(Tcl_Interp *interp, int subcmd, NsfObject *context, Tcl_Obj *methodNameObj, CONST char *pattern) { assert(interp); assert(methodNameObj); return ListMethodResolve(interp, subcmd, context, pattern, NULL, NULL, methodNameObj, 0); } /* cmd configure NsfConfigureCmd { {-argName "configureoption" -required 1 -type "debug|dtrace|filter|profile|softrecreate|objectsystems|keepcmds|checkresults|checkarguments"} {-argName "value" -required 0 -type tclobj} } */ static int NsfConfigureCmd(Tcl_Interp *interp, int configureoption, Tcl_Obj *valueObj) { int bool; assert(interp); #if defined(NSF_DTRACE) if (NSF_DTRACE_CONFIGURE_PROBE_ENABLED()) { /* TODO: opts copied from tclAPI.h; maybe make global value? */ static CONST char *opts[] = { "debug", "dtrace", "filter", "profile", "softrecreate", "objectsystems", "keepcmds", "checkresults", "checkarguments", NULL}; NSF_DTRACE_CONFIGURE_PROBE((char *)opts[configureoption-1], valueObj ? ObjStr(valueObj) : NULL); } #endif if (configureoption == ConfigureoptionObjectsystemsIdx) { NsfObjectSystem *osPtr; Tcl_Obj *list = Tcl_NewListObj(0, NULL); for (osPtr = RUNTIME_STATE(interp)->objectSystems; osPtr; osPtr = osPtr->nextPtr) { Tcl_Obj *osObj = Tcl_NewListObj(0, NULL); Tcl_Obj *systemMethods = Tcl_NewListObj(0, NULL); int idx; Tcl_ListObjAppendElement(interp, osObj, osPtr->rootClass->object.cmdName); Tcl_ListObjAppendElement(interp, osObj, osPtr->rootMetaClass->object.cmdName); for (idx = 0; Nsf_SystemMethodOpts[idx]; idx++) { /*fprintf(stderr, "opt %s %s\n", Nsf_SystemMethodOpts[idx], osPtr->methods[idx] ? ObjStr(osPtr->methods[idx]) : "NULL");*/ if (osPtr->methods[idx] == NULL) { continue; } Tcl_ListObjAppendElement(interp, systemMethods, Tcl_NewStringObj(Nsf_SystemMethodOpts[idx], -1)); if (osPtr->handles[idx] || osPtr->protected[idx]) { Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, listObj, osPtr->methods[idx]); Tcl_ListObjAppendElement(interp, listObj, osPtr->handles[idx]); if (osPtr->protected[idx]) { Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj(1)); } Tcl_ListObjAppendElement(interp, systemMethods, listObj); } else { Tcl_ListObjAppendElement(interp, systemMethods, osPtr->methods[idx]); } } Tcl_ListObjAppendElement(interp, osObj, systemMethods); Tcl_ListObjAppendElement(interp, list, osObj); } Tcl_SetObjResult(interp, list); return TCL_OK; } if (configureoption == ConfigureoptionDebugIdx) { int level; if (valueObj) { int result = Tcl_GetIntFromObj(interp, valueObj, &level); if (unlikely(result != TCL_OK)) { return result; } RUNTIME_STATE(interp)->debugLevel = level; } Tcl_SetIntObj(Tcl_GetObjResult(interp), RUNTIME_STATE(interp)->debugLevel); return TCL_OK; } /* * All other configure options are boolean. */ if (valueObj) { int result = Tcl_GetBooleanFromObj(interp, valueObj, &bool); if (unlikely(result != TCL_OK)) { return result; } } switch (configureoption) { case ConfigureoptionFilterIdx: Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (RUNTIME_STATE(interp)->doFilters)); if (valueObj) { RUNTIME_STATE(interp)->doFilters = bool; } break; case ConfigureoptionProfileIdx: Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (RUNTIME_STATE(interp)->doProfile)); if (valueObj) { #if defined(NSF_PROFILE) RUNTIME_STATE(interp)->doProfile = bool; #else NsfLog(interp, NSF_LOG_WARN, "No profile support compiled in"); #endif } break; case ConfigureoptionSoftrecreateIdx: Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (RUNTIME_STATE(interp)->doSoftrecreate)); if (valueObj) { RUNTIME_STATE(interp)->doSoftrecreate = bool; } break; case ConfigureoptionKeepcmdsIdx: Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (RUNTIME_STATE(interp)->doKeepcmds)); if (valueObj) { RUNTIME_STATE(interp)->doKeepcmds = bool; } break; case ConfigureoptionCheckresultsIdx: Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (RUNTIME_STATE(interp)->doCheckResults)); if (valueObj) { RUNTIME_STATE(interp)->doCheckResults = bool; } break; case ConfigureoptionCheckargumentsIdx: Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (RUNTIME_STATE(interp)->doCheckArguments) != 0); if (valueObj) { RUNTIME_STATE(interp)->doCheckArguments = bool ? NSF_ARGPARSE_CHECK : 0; } break; } return TCL_OK; } /* cmd colon NsfColonCmd { {-argName "args" -type allargs} } */ static int NsfColonCmd(Tcl_Interp *interp, int nobjc, Tcl_Obj *CONST nobjv[]) { CONST char *methodName = ObjStr(nobjv[0]); NsfObject *self = GetSelfObj(interp); assert(interp); if (unlikely(self == NULL)) { return NsfNoCurrentObjectError(interp, methodName); } /*fprintf(stderr, "Colon dispatch %s.%s (%d)\n", ObjectName(self), ObjStr(nobjv[0]), nobjc);*/ if (likely(!(*methodName == ':' && *(methodName + 1) == '\0'))) { return ObjectDispatch(self, interp, nobjc, nobjv, NSF_CM_NO_SHIFT); } /* * First arg is a single colon and no other args are given. */ if (nobjc <= 1) { Tcl_SetObjResult(interp, self->cmdName); return TCL_OK; } /* * Multiple arguments were given. */ methodName = ObjStr(nobjv[1]); if (*methodName != '-') { /* * No need to parse arguments (local, intrinsic, ...). */ return ObjectDispatch(self, interp, nobjc, nobjv, 0); } else { ParseContext pc; int withIntrinsic, withLocal, withSystem, flags; Tcl_Obj *methodObj; /* * Parse arguments, use definitions from nsf::my */ if (ArgumentParse(interp, nobjc, nobjv, NULL, nobjv[0], method_definitions[NsfMyCmdIdx].paramDefs, method_definitions[NsfMyCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) != TCL_OK) { return TCL_ERROR; } withIntrinsic = (int)PTR2INT(pc.clientData[0]); withLocal = (int)PTR2INT(pc.clientData[1]); withSystem = (int)PTR2INT(pc.clientData[2]); methodObj = (Tcl_Obj *)pc.clientData[3]; assert(pc.status == 0); if ((withIntrinsic && withLocal) || (withIntrinsic && withSystem) || (withLocal && withSystem)) { return NsfPrintError(interp, "flags '-intrinsic', '-local' and '-system' are mutual exclusive"); } flags = NSF_CSC_IMMEDIATE; if (withIntrinsic) {flags |= NSF_CM_INTRINSIC_METHOD;} if (withLocal) {flags |= NSF_CM_LOCAL_METHOD;} if (withSystem) {flags |= NSF_CM_SYSTEM_METHOD;} return CallMethod(self, interp, methodObj, (nobjc-pc.lastObjc)+2, nobjv+pc.lastObjc, flags); } } /* cmd "directdispatch" NsfDirectDispatchCmd { {-argName "object" -required 1 -type object} {-argName "-frame" -required 0 -nrargs 1 -type "method|object|default" -default "default"} {-argName "command" -required 1 -type tclobj} {-argName "args" -type args} } */ static int NsfDirectDispatchCmd(Tcl_Interp *interp, NsfObject *object, int withFrame, Tcl_Obj *commandObj, int nobjc, Tcl_Obj *CONST nobjv[]) { int result; CONST char *methodName = ObjStr(commandObj); Tcl_Command cmd, importedCmd; CallFrame frame, *framePtr = &frame; Tcl_ObjCmdProc *proc; unsigned int flags = 0; int useCmdDispatch = 1; assert(interp); assert(object); assert(commandObj); /*fprintf(stderr, "NsfDirectDispatchCmd obj=%s, cmd m='%s' oc %d\n", ObjectName(object), methodName, nobjc);*/ if (unlikely(*methodName != ':')) { return NsfPrintError(interp, "method name '%s' must be fully qualified", methodName); } /* * We have a fully qualified name of a Tcl command that will be dispatched. */ cmd = Tcl_GetCommandFromObj(interp, commandObj); if (likely(cmd != NULL)) { importedCmd = TclGetOriginalCommand(cmd); if (unlikely(importedCmd != NULL)) { cmd = importedCmd; } } if (unlikely(cmd == NULL)) { return NsfPrintError(interp, "cannot lookup command '%s'", methodName); } proc = Tcl_Command_objProc(cmd); if (proc == TclObjInterpProc || proc == NsfForwardMethod || proc == NsfObjscopedMethod || proc == NsfSetterMethod || CmdIsNsfObject(cmd)) { if (withFrame && withFrame != FrameDefaultIdx) { return NsfPrintError(interp, "cannot use -frame object|method in dispatch for command '%s'", methodName); } useCmdDispatch = 0; } else { if (unlikely(withFrame == FrameMethodIdx)) { useCmdDispatch = 0; } else { useCmdDispatch = 1; } } /* * If "withFrame == FrameObjectIdx" is specified, a call-stack frame is * pushed to make instance variables accessible for the command. */ if (unlikely(withFrame == FrameObjectIdx)) { Nsf_PushFrameObj(interp, object, framePtr); flags = NSF_CSC_IMMEDIATE; } /* * Since we know, that we are always called with a full argument * vector, we can include the cmd name in the objv by using * nobjv-1; this way, we avoid a memcpy(). */ if (useCmdDispatch) { if (NSF_DTRACE_METHOD_ENTRY_ENABLED()) { NSF_DTRACE_METHOD_ENTRY(ObjectName(object), "", (char *)methodName, nobjc, (Tcl_Obj **)nobjv); } result = CmdMethodDispatch(object, interp, nobjc+1, nobjv-1, object, cmd, NULL); } else { /* * If "withFrame == FrameMethodIdx" is specified, a call-stack frame is * pushed to make instance variables accessible for the command. */ if (unlikely(withFrame == FrameMethodIdx)) { flags = NSF_CSC_FORCE_FRAME|NSF_CSC_IMMEDIATE; } result = MethodDispatch(object, interp, nobjc+1, nobjv-1, cmd, object, NULL /*NsfClass *cl*/, Tcl_GetCommandName(interp, cmd), NSF_CSC_TYPE_PLAIN, flags); } if (unlikely(withFrame == FrameObjectIdx)) { Nsf_PopFrameObj(interp, framePtr); } return result; } /* cmd "dispatch" NsfDispatchCmd { {-argName "object" -required 1 -type object} {-argName "-intrinsic" -required 0 -nrargs 0} {-argName "-system" -required 0 -nrargs 0} {-argName "command" -required 1 -type tclobj} {-argName "args" -type args} } */ static int NsfDispatchCmd(Tcl_Interp *interp, NsfObject *object, int withIntrinsic, int withSystem, Tcl_Obj *commandObj, int nobjc, Tcl_Obj *CONST nobjv[]) { unsigned int flags = NSF_CM_NO_UNKNOWN|NSF_CSC_IMMEDIATE|NSF_CM_IGNORE_PERMISSIONS|NSF_CM_NO_SHIFT; Tcl_Obj *CONST*objv = nobjv-1; assert(interp); assert(object); assert(commandObj); /* * We use the construct tclObj + args in the spec to enforce that at least a * commandName is specified (this way we allow empty "args", and can provide * a nice error message, if cmdName is not specified). Since the we know * that the commandObj has to be right before "args" in the objv, we can * decrement the nobjv to obtain objv (and increment nobjc), be we make sure * that this assumption is correct. */ assert(objv[0] == commandObj); assert(ISOBJ(commandObj)); nobjc++; #if 0 {int i; fprintf(stderr, "NsfDispatchCmd %s method %s oc %2d", ObjectName(object), ObjStr(commandObj), nobjc); for(i = 0; i < nobjc; i++) {fprintf(stderr, " [%d]=%s,", i, ObjStr(objv[i]));} fprintf(stderr, "\n"); } #endif if (unlikely(withIntrinsic && withSystem)) { return NsfPrintError(interp, "flags '-intrinsic' and '-system' are mutual exclusive"); } /* * Dispatch the command the method from the precedence order, with filters * etc. -- strictly speaking unnecessary, but this function can be used to * call protected methods and provide the flags '-intrinsics' and '-system'. */ if (withIntrinsic) {flags |= NSF_CM_INTRINSIC_METHOD;} if (withSystem) {flags |= NSF_CM_SYSTEM_METHOD;} /* * Since we know, that we are always called with a full argument * vector, we can include the cmd name in the objv by using * nobjv-1; this way, we avoid a memcpy(). */ return ObjectDispatch(object, interp, nobjc, objv, flags); } /* cmd finalize NsfFinalizeCmd { {-argName "-keepvars" -required 0 -nrargs 0} } */ static int NsfFinalizeCmd(Tcl_Interp *interp, int withKeepvars) { int result; assert(interp); #if defined(NSF_STACKCHECK) {NsfRuntimeState *rst = RUNTIME_STATE(interp); NsfLog(interp, NSF_LOG_WARN, "Stack max usage %ld", labs(rst->maxStack - rst->bottomOfStack)); } #endif /*fprintf(stderr, "+++ call tcl-defined exit handler\n");*/ /* * Evaluate user-defined exit handler. */ result = Tcl_Eval(interp, "::nsf::__exithandler"); if (unlikely(result != TCL_OK)) { fprintf(stderr, "User defined exit handler contains errors!\n" "Error in line %d: %s\nExecution interrupted.\n", Tcl_GetErrorLine(interp), ObjStr(Tcl_GetObjResult(interp))); } ObjectSystemsCleanup(interp, withKeepvars); #ifdef DO_CLEANUP { NsfRuntimeState *rst = RUNTIME_STATE(interp); # if defined(CHECK_ACTIVATION_COUNTS) assert(rst->cscList == NULL); # endif /*fprintf(stderr, "CLEANUP TOP NS\n");*/ Tcl_Export(interp, rst->NsfNS, "", 1); if (rst->NsfClassesNS) { MEM_COUNT_FREE("TclNamespace",rst->NsfClassesNS); Tcl_DeleteNamespace(rst->NsfClassesNS); } if (rst->NsfNS) { MEM_COUNT_FREE("TclNamespace",rst->NsfNS); Tcl_DeleteNamespace(rst->NsfNS); } } #endif return TCL_OK; } /* cmd interp NsfInterpObjCmd { {-argName "name"} {-argName "args" -type allargs} } */ /* create a slave interp that calls Next Scripting Init */ static int NsfInterpObjCmd(Tcl_Interp *interp, CONST char *name, int objc, Tcl_Obj *CONST objv[]) { assert(interp); assert(name); /* create a fresh Tcl interpreter, or pass command to an existing one */ if (NsfCallCommand(interp, NSF_INTERP, objc, objv) != TCL_OK) { return TCL_ERROR; } /* * Upon [interp create], set up NSF for the new child interp by running * Nsf_Init() */ if (isCreateString(name)) { Tcl_Obj *slaveCmdObj; Tcl_Interp *slavePtr; /* * Tcl_InterpObjCmd() stores the newly created child interp's command name * in the interp result store. */ slaveCmdObj = Tcl_GetObjResult(interp); slavePtr = Tcl_GetSlave(interp, ObjStr(slaveCmdObj)); if (slavePtr == NULL) { return NsfPrintError(interp, "creation of slave interpreter failed"); } if (Nsf_Init(slavePtr) == TCL_ERROR) { return TCL_ERROR; } } return TCL_OK; } /* cmd is NsfIsCmd { {-argName "-complain" -nrargs 0} {-argName "-configure" -nrargs 0} {-argName "-name" -required 0} {-argName "constraint" -required 1 -type tclobj} {-argName "value" -required 1 -type tclobj} } {-nxdoc 1} */ static int NsfIsCmd(Tcl_Interp *interp, int withComplain, int doConfigureParameter, CONST char *name, Tcl_Obj *constraintObj, Tcl_Obj *valueObj) { Nsf_Param *paramPtr = NULL; int result; assert(interp); assert(constraintObj); assert(valueObj); result = ParameterCheck(interp, constraintObj, valueObj, name ? name : "value:", 1, (name != NULL), doConfigureParameter, ¶mPtr); if (paramPtr == NULL) { /* * We could not convert the arguments. Even with noComplain, we * report the invalid converter spec as exception. */ return TCL_ERROR; } if (paramPtr->converter == ConvertViaCmd && (withComplain == 0 || result == TCL_OK)) { Tcl_ResetResult(interp); } if (withComplain == 0) { Tcl_SetIntObj(Tcl_GetObjResult(interp), (result == TCL_OK)); result = TCL_OK; } else if (likely(result == TCL_OK)) { Tcl_SetIntObj(Tcl_GetObjResult(interp), 1); } return result; } /* cmd method::alias NsfMethodAliasCmd { {-argName "object" -type object} {-argName "-per-object"} {-argName "methodName"} {-argName "-frame" -required 0 -nrargs 1 -type "method|object|default" -default "default"} {-argName "-protection" -required 0 -type "call-protected|redefine-protected|none" -default "none"} {-argName "cmdName" -required 1 -type tclobj} } */ static int NsfMethodAliasCmd(Tcl_Interp *interp, NsfObject *object, int withPer_object, CONST char *methodName, int withFrame, int withProtection, Tcl_Obj *cmdName) { Tcl_ObjCmdProc *objProc, *newObjProc = NULL; Tcl_CmdDeleteProc *deleteProc = NULL; AliasCmdClientData *tcd = NULL; /* make compiler happy */ Tcl_Command cmd, oldCmd, newCmd = NULL; Tcl_Namespace *nsPtr; int result; unsigned int flags; NsfClass *cl = (withPer_object || ! NsfObjectIsClass(object)) ? NULL : (NsfClass *)object; NsfObject *oldTargetObject, *newTargetObject; assert(interp); assert(object); assert(methodName); assert(methodName && *methodName != ':'); assert(cmdName); cmd = Tcl_GetCommandFromObj(interp, cmdName); if (cmd == NULL) { return NsfPrintError(interp, "cannot lookup command '%s'", ObjStr(cmdName)); } cmd = GetOriginalCommand(cmd); objProc = Tcl_Command_objProc(cmd); assert(objProc); /* objProc is either ... * * 1. NsfObjDispatch: a command representing an Next Scripting object * * 2. TclObjInterpProc: a cmd standing for a Tcl proc (including * Next Scripting methods), verified through CmdIsProc() -> to be * wrapped by NsfProcAliasMethod() * * 3. NsfForwardMethod: an Next Scripting forwarder * * 4. NsfSetterMethod: an Next Scripting setter * * 5. arbitrary Tcl commands (e.g. set, ..., ::nsf::relation, ...) * */ if (withFrame == FrameObjectIdx) { newObjProc = NsfObjscopedMethod; } /* * We need to perform a defensive lookup of a previously defined * object-alias under the given methodName. */ nsPtr = cl ? cl->nsPtr : object->nsPtr; oldCmd = nsPtr ? FindMethod(nsPtr, methodName) : NULL; newTargetObject = NsfGetObjectFromCmdPtr(cmd); if (oldCmd != NULL) { oldTargetObject = NsfGetObjectFromCmdPtr(oldCmd); /* fprintf(stderr, "oldTargetObject %p flags %.6x newTargetObject %p\n", oldTargetObject, oldTargetObject ? oldTargetObject->flags : 0, newTargetObject);*/ /* * We might have to decrement the reference counter on an previously * aliased object. Decrement the reference count to the old aliased object * only, when it is different to the new target Object. */ if (oldTargetObject != NULL && oldTargetObject != newTargetObject) { /*fprintf(stderr, "--- releasing old target object %p refCount %d\n", oldTargetObject, oldTargetObject->refCount);*/ assert(oldTargetObject->refCount > 0); AliasDeleteObjectReference(interp, oldCmd); } } else { oldTargetObject = NULL; } if (newTargetObject) { /* * We set now for every alias to an object a stub proc, such we can * distinguish between cases, where the user wants to create a method, and * between cases, where object-invocation via method interface might * happen. */ newObjProc = NsfProcAliasMethod; } else if (CmdIsProc(cmd)) { /* * When we have a Tcl proc|nsf-method as alias, then use the * wrapper, which will be deleted automatically when the original * proc/method is deleted. */ newObjProc = NsfProcAliasMethod; if (objProc == TclObjInterpProc) { /* * We have an alias to a tcl proc; */ Proc *procPtr = (Proc *)Tcl_Command_objClientData(cmd); Tcl_Obj *bodyObj = procPtr ? procPtr->bodyPtr : NULL; if (bodyObj && bodyObj->typePtr == Nsf_OT_byteCodeType) { /* * Flush old byte code */ /*fprintf(stderr, "flush byte code\n");*/ bodyObj->typePtr->freeIntRepProc(bodyObj); } } if (withFrame && withFrame != FrameDefaultIdx) { return NsfPrintError(interp, "cannot use -frame object|method in alias for scripted command '%s'", ObjStr(cmdName)); } } if (newObjProc) { /* add a wrapper */ /*fprintf(stderr, "NsfMethodAliasCmd add wrapper cmd %p\n", cmd);*/ NsfCommandPreserve(cmd); tcd = NEW(AliasCmdClientData); tcd->cmdName = object->cmdName; tcd->interp = interp; /* just for deleting the alias */ tcd->object = NULL; tcd->class = cl ? (NsfClass *) object : NULL; tcd->objProc = objProc; tcd->aliasedCmd = cmd; tcd->clientData = Tcl_Command_objClientData(cmd); objProc = newObjProc; deleteProc = AliasCmdDeleteProc; if (tcd->cmdName) {INCR_REF_COUNT(tcd->cmdName);} } else { /* * Call the command directly (must be a c-implemented command not * depending on a volatile client data) */ tcd = Tcl_Command_objClientData(cmd); /*fprintf(stderr, "NsfMethodAliasCmd no wrapper cmd %p\n", cmd);*/ } switch (withProtection) { case ProtectionCall_protectedIdx: flags = NSF_CMD_CALL_PROTECTED_METHOD; break; case ProtectionRedefine_protectedIdx: flags = NSF_CMD_REDEFINE_PROTECTED_METHOD; break; default: flags = 0; } if (cl) { result = NsfAddClassMethod(interp, (Nsf_Class *)cl, methodName, objProc, tcd, deleteProc, flags); nsPtr = cl->nsPtr; } else { result = NsfAddObjectMethod(interp, (Nsf_Object *)object, methodName, objProc, tcd, deleteProc, flags); nsPtr = object->nsPtr; } if (likely(result == TCL_OK)) { newCmd = FindMethod(nsPtr, methodName); } #if defined(WITH_IMPORT_REFS) if (newObjProc) { /* * Define the reference chain like for 'namespace import' to * obtain automatic deletes when the original command is deleted. */ ImportRef *refPtr = (ImportRef *) ckalloc(sizeof(ImportRef)); refPtr->importedCmdPtr = (Command *) newCmd; refPtr->nextPtr = ((Command *) tcd->aliasedCmd)->importRefPtr; ((Command *) tcd->aliasedCmd)->importRefPtr = refPtr; tcd->aliasCmd = newCmd; } #else if (newObjProc) { tcd->aliasCmd = newCmd; } #endif if (newCmd) { AliasAdd(interp, object->cmdName, methodName, cl == NULL, ObjStr(cmdName)); if (withFrame == FrameMethodIdx) { Tcl_Command_flags(newCmd) |= NSF_CMD_NONLEAF_METHOD; /*fprintf(stderr, "setting aliased for cmd %p %s flags %.6x, tcd = %p\n", newCmd, methodName, Tcl_Command_flags(newCmd), tcd);*/ } Tcl_SetObjResult(interp, MethodHandleObj(object, cl == NULL, methodName)); result = TCL_OK; } return result; } /* cmd method::assertion NsfMethodAssertionCmd { {-argName "object" -type object} {-argName "assertionsubcmd" -required 1 -type "check|object-invar|class-invar"} {-argName "arg" -required 0 -type tclobj} } Make "::nsf::assertion" a cmd rather than a method, otherwise we cannot define e.g. a "method check options {...}" to reset the check options in case of a failed option, since assertion checking would be applied on the sketched method already. */ static int NsfMethodAssertionCmd(Tcl_Interp *interp, NsfObject *object, int subcmd, Tcl_Obj *arg) { #if defined(NSF_WITH_ASSERTIONS) NsfClass *class; assert(interp); assert(object); switch (subcmd) { case AssertionsubcmdCheckIdx: if (arg) { return AssertionSetCheckOptions(interp, object, arg); } else { return AssertionListCheckOption(interp, object); } break; case AssertionsubcmdObject_invarIdx: if (arg) { NsfObjectOpt *opt = NsfRequireObjectOpt(object); AssertionSetInvariants(interp, &opt->assertions, arg); } else { if (object->opt && object->opt->assertions) { Tcl_SetObjResult(interp, AssertionList(interp, object->opt->assertions->invariants)); } } break; case AssertionsubcmdClass_invarIdx: if (!NsfObjectIsClass(object)) { return NsfPrintError(interp, "object is not a class"); } class = (NsfClass *)object; if (arg) { NsfClassOpt *opt = NsfRequireClassOpt(class); AssertionSetInvariants(interp, &opt->assertions, arg); } else { if (class->opt && class->opt->assertions) { Tcl_SetObjResult(interp, AssertionList(interp, class->opt->assertions->invariants)); } } } #endif return TCL_OK; } /* cmd method::create NsfMethodCreateCmd { {-argName "object" -required 1 -type object} {-argName "-checkalways" -required 0 -nrargs 0 -type switch} {-argName "-inner-namespace"} {-argName "-per-object"} {-argName "-reg-object" -required 0 -nrargs 1 -type object} {-argName "name" -required 1 -type tclobj} {-argName "arguments" -required 1 -type tclobj} {-argName "body" -required 1 -type tclobj} {-argName "-precondition" -nrargs 1 -type tclobj} {-argName "-postcondition" -nrargs 1 -type tclobj} } */ static int NsfMethodCreateCmd(Tcl_Interp *interp, NsfObject *defObject, int withCheckAlways, int withInner_namespace, int withPer_object, NsfObject *regObject, Tcl_Obj *nameObj, Tcl_Obj *arguments, Tcl_Obj *body, Tcl_Obj *withPrecondition, Tcl_Obj *withPostcondition) { NsfClass *cl = (withPer_object || ! NsfObjectIsClass(defObject)) ? NULL : (NsfClass *)defObject; assert(interp); assert(defObject); assert(nameObj); assert(arguments); assert(body); if (cl == NULL) { RequireObjNamespace(interp, defObject); } return MakeMethod(interp, defObject, regObject, cl, nameObj, arguments, body, withPrecondition, withPostcondition, withInner_namespace, withCheckAlways ? NSF_ARGPARSE_CHECK : 0); } /* cmd "method::delete" NsfMethodDeleteCmd { {-argName "object" -required 1 -type object} {-argName "-per-object"} {-argName "methodName" -required 1 -type tclobj} } */ static int NsfMethodDeleteCmd(Tcl_Interp *interp, NsfObject *object, int withPer_object, Tcl_Obj *methodNameObj) { NsfObject *regObject, *defObject; CONST char *methodName1 = NULL; NsfClass *cl = withPer_object == 0 && NsfObjectIsClass(object) ? (NsfClass *)object : NULL; int fromClassNS = cl != NULL, result; Tcl_DString ds, *dsPtr = &ds; Tcl_Command cmd; assert(interp); assert(object); assert(methodNameObj); Tcl_DStringInit(dsPtr); cmd = ResolveMethodName(interp, cl ? cl->nsPtr : object->nsPtr, methodNameObj, dsPtr, ®Object, &defObject, &methodName1, &fromClassNS); /*fprintf(stderr, "NsfMethodDeleteCmd method %s '%s' object %p regObject %p defObject %p cl %p fromClass %d cmd %p\n", ObjStr(methodNameObj), methodName1, object, regObject, defObject, cl, fromClassNS, cmd);*/ if (cmd) { methodName1 = Tcl_GetCommandName(interp, cmd); if (defObject) { cl = withPer_object == 0 && NsfObjectIsClass(defObject) ? (NsfClass *)defObject : NULL; } else { defObject = object; } if (RUNTIME_STATE(interp)->exitHandlerDestroyRound == NSF_EXITHANDLER_OFF) { result = cl ? NsfRemoveClassMethod(interp, (Nsf_Class *)defObject, methodName1) : NsfRemoveObjectMethod(interp, (Nsf_Object *)defObject, methodName1); } else { result = TCL_OK; } } else { result = NsfPrintError(interp, "%s: %s method '%s' does not exist", ObjectName(object), withPer_object ? "object specific" : "instance", ObjStr(methodNameObj)); } Tcl_DStringFree(dsPtr); return result; } /* cmd method::forward NsfMethodForwardCmd { {-argName "object" -required 1 -type object} {-argName "-per-object" -required 0 -nrargs 0 -type switch} {-argName "method" -required 1 -type tclobj} {-argName "-default" -type tclobj} {-argName "-earlybinding" -nrargs 0} {-argName "-prefix" -type tclobj} {-argName "-frame" -nrargs 1 -type "object|method|default" -default default} {-argName "-verbose" -nrargs 0} {-argName "target" -type tclobj} {-argName "args" -type args} } */ static int NsfMethodForwardCmd(Tcl_Interp *interp, NsfObject *object, int withPer_object, Tcl_Obj *methodObj, Tcl_Obj *withDefault, int withEarlybinding, Tcl_Obj *withOnerror, Tcl_Obj *withMethodprefix, int withFrame, int withVerbose, Tcl_Obj *target, int nobjc, Tcl_Obj *CONST nobjv[]) { ForwardCmdClientData *tcd = NULL; int result; assert(interp); assert(object); assert(methodObj); result = ForwardProcessOptions(interp, methodObj, withDefault, withEarlybinding, withOnerror, withMethodprefix, withFrame, withVerbose, target, nobjc, nobjv, &tcd); if (likely(result == TCL_OK)) { CONST char *methodName = NSTail(ObjStr(methodObj)); NsfClass *cl = (withPer_object || ! NsfObjectIsClass(object)) ? NULL : (NsfClass *)object; tcd->object = object; if (cl == NULL) { result = NsfAddObjectMethod(interp, (Nsf_Object *)object, methodName, (Tcl_ObjCmdProc *)NsfForwardMethod, tcd, ForwardCmdDeleteProc, 0); } else { result = NsfAddClassMethod(interp, (Nsf_Class *)cl, methodName, (Tcl_ObjCmdProc *)NsfForwardMethod, tcd, ForwardCmdDeleteProc, 0); } if (likely(result == TCL_OK)) { Tcl_SetObjResult(interp, MethodHandleObj(object, withPer_object, methodName)); } } if (result != TCL_OK && tcd) { ForwardCmdDeleteProc(tcd); } return result; } /* cmd ::method::property NsfMethodPropertyCmd { {-argName "object" -required 1 -type object} {-argName "-per-object"} {-argName "methodName" -required 1 -type tclobj} {-argName "methodproperty" -required 1 -type "class-only|call-private|call-protected|redefine-protected|returns|slotobj"} {-argName "value" -type tclobj} } */ static int NsfMethodPropertyCmd(Tcl_Interp *interp, NsfObject *object, int withPer_object, Tcl_Obj *methodObj, int methodproperty, Tcl_Obj *valueObj) { CONST char *methodName = ObjStr(methodObj); NsfObject *defObject; Tcl_Command cmd; NsfClass *cl = withPer_object == 0 && NsfObjectIsClass(object) ? (NsfClass *)object : NULL; int fromClassNS = cl != NULL; unsigned int flag; assert(interp); assert(object); assert(methodObj); cmd = ResolveMethodName(interp, cl ? cl->nsPtr : object->nsPtr, methodObj, NULL, NULL, &defObject, NULL, &fromClassNS); /*fprintf(stderr, "methodProperty for method '%s' prop %d value %s => cl %p cmd %p\n", methodName, methodproperty, valueObj ? ObjStr(valueObj) : "NULL", cl, cmd);*/ if (unlikely(cmd == NULL)) { return NsfPrintError(interp, "cannot lookup %s method '%s' for %s", cl == NULL ? "object " : "", methodName, ObjectName(object)); } switch (methodproperty) { case MethodpropertyClass_onlyIdx: /* fall through */ case MethodpropertyCall_privateIdx: /* fall through */ case MethodpropertyCall_protectedIdx: /* fall through */ case MethodpropertyRedefine_protectedIdx: /* fall through */ { int impliedSetFlag = 0, impliedClearFlag = 0; switch (methodproperty) { case MethodpropertyClass_onlyIdx: flag = NSF_CMD_CLASS_ONLY_METHOD; break; case MethodpropertyCall_privateIdx: flag = NSF_CMD_CALL_PRIVATE_METHOD; impliedSetFlag = NSF_CMD_CALL_PROTECTED_METHOD; break; case MethodpropertyCall_protectedIdx: impliedClearFlag = NSF_CMD_CALL_PRIVATE_METHOD; flag = NSF_CMD_CALL_PROTECTED_METHOD; break; case MethodpropertyRedefine_protectedIdx: flag = NSF_CMD_REDEFINE_PROTECTED_METHOD; break; default: flag = 0; } if (valueObj) { int bool, result; result = Tcl_GetBooleanFromObj(interp, valueObj, &bool); if (unlikely(result != TCL_OK)) { return result; } if (bool) { /* * set flag */ Tcl_Command_flags(cmd) |= flag; if (impliedSetFlag) { Tcl_Command_flags(cmd) |= impliedSetFlag; } } else { /* * clear flag */ Tcl_Command_flags(cmd) &= ~flag; if (impliedClearFlag) { Tcl_Command_flags(cmd) &= ~impliedClearFlag; } } if (cl) { NsfInstanceMethodEpochIncr("Permissions"); } else { NsfObjectMethodEpochIncr("Permissions"); } } Tcl_SetIntObj(Tcl_GetObjResult(interp), (Tcl_Command_flags(cmd) & flag) != 0); break; } case MethodpropertySlotobjIdx: case MethodpropertyReturnsIdx: { NsfParamDefs *paramDefs; Tcl_Obj **objPtr; if (valueObj == NULL && methodproperty == MethodpropertySlotobjIdx) { return NsfPrintError(interp, "option 'slotobj' of method '%s' requires argument", methodName); } paramDefs = ParamDefsGet(cmd, NULL); /*fprintf(stderr, "MethodProperty, ParamDefsGet cmd %p paramDefs %p returns %p\n", cmd, paramDefs, paramDefs?paramDefs->returns:NULL);*/ if (valueObj == NULL) { /* a query for "returns" or "slotobj" */ Tcl_Obj *resultObj; if (paramDefs == NULL) { resultObj = NsfGlobalObjs[NSF_EMPTY]; } else { objPtr = methodproperty == MethodpropertySlotobjIdx ? ¶mDefs->slotObj : ¶mDefs->returns; resultObj = *objPtr ? *objPtr : NsfGlobalObjs[NSF_EMPTY]; } Tcl_SetObjResult(interp, resultObj); } else { /* setting "returns" or "slotobj" */ const char *valueString = ObjStr(valueObj); if (paramDefs == NULL) { /* acquire new paramDefs */ paramDefs = ParamDefsNew(); ParamDefsStore(interp, cmd, paramDefs, 0); /*fprintf(stderr, "new param definitions %p for cmd %p %s\n", paramDefs, cmd, methodName);*/ } objPtr = methodproperty == MethodpropertySlotobjIdx ? ¶mDefs->slotObj : ¶mDefs->returns; /* Set a new value; if there is already a value, free it */ if (*objPtr) { DECR_REF_COUNT2("paramDefsObj", *objPtr); } if (*valueString == '\0') { /* set the value to NULL */ *objPtr = NULL; } else { *objPtr = valueObj; INCR_REF_COUNT2("paramDefsObj", *objPtr); } } break; } } return TCL_OK; } /* cmd "method::registered" NsfMethodRegisteredCmd { {-argName "handle" -required 1 -type tclobj} } */ static int NsfMethodRegisteredCmd(Tcl_Interp *interp, Tcl_Obj *methodNameObj) { NsfObject *regObject; int fromClassNS = 0; Tcl_Command cmd; assert(interp); assert(methodNameObj); cmd = ResolveMethodName(interp, NULL, methodNameObj, NULL, ®Object, NULL, NULL, &fromClassNS); /* * In case the provided cmd is fully qualified and refers to a registered * method, the function returns the object, on which the method was * resisted. */ Tcl_SetObjResult(interp, (cmd && regObject) ? regObject->cmdName : NsfGlobalObjs[NSF_EMPTY]); return TCL_OK; } /* cmd method::setter NsfMethodSetterCmd { {-argName "object" -required 1 -type object} {-argName "-per-object"} {-argName "parameter" -type tclobj} } */ static int NsfMethodSetterCmd(Tcl_Interp *interp, NsfObject *object, int withPer_object, Tcl_Obj *parameter) { NsfClass *cl = (withPer_object || ! NsfObjectIsClass(object)) ? NULL : (NsfClass *)object; CONST char *methodName = ObjStr(parameter); SetterCmdClientData *setterClientData; size_t j, length; int result; assert(interp); assert(object); assert(parameter); if (unlikely(*methodName == '-' || *methodName == ':')) { return NsfPrintError(interp, "invalid setter name \"%s\" (must not start with a dash or colon)", methodName); } setterClientData = NEW(SetterCmdClientData); setterClientData->object = NULL; setterClientData->paramsPtr = NULL; length = strlen(methodName); for (j = 0; j < length; j++) { if (methodName[j] == ':' || methodName[j] == ' ') break; } if (j < length) { /* looks as if we have a parameter specification */ int result, possibleUnknowns = 0, plainParams = 0, nrNonposArgs = 0; setterClientData->paramsPtr = ParamsNew(1); result = ParamParse(interp, NsfGlobalObjs[NSF_SETTER], parameter, NSF_DISALLOWED_ARG_SETTER|NSF_ARG_HAS_DEFAULT, setterClientData->paramsPtr, &possibleUnknowns, &plainParams, &nrNonposArgs); if (unlikely(result != TCL_OK)) { SetterCmdDeleteProc(setterClientData); return result; } methodName = setterClientData->paramsPtr->name; } else { setterClientData->paramsPtr = NULL; } if (cl) { result = NsfAddClassMethod(interp, (Nsf_Class *)cl, methodName, (Tcl_ObjCmdProc *)NsfSetterMethod, setterClientData, SetterCmdDeleteProc, 0); } else { result = NsfAddObjectMethod(interp, (Nsf_Object *)object, methodName, (Tcl_ObjCmdProc *)NsfSetterMethod, setterClientData, SetterCmdDeleteProc, 0); } if (likely(result == TCL_OK)) { Tcl_SetObjResult(interp, MethodHandleObj(object, cl == NULL, methodName)); } else { SetterCmdDeleteProc(setterClientData); } return result; } /* cmd "object::alloc" NsfObjectAllocCmd { {-argName "class" -required 1 -type class} {-argName "name" -required 1 -type tclobj} {-argName "initcmd" -required 0 -type tclobj} } */ static int NsfObjectAllocCmd(Tcl_Interp *interp, NsfClass *class, Tcl_Obj *nameObj, Tcl_Obj *initcmdObj) { Tcl_Obj *newNameObj = NULL; int result; assert(interp); assert(class); assert(nameObj); /* * If the provided name is empty, make a new symbol */ if (strlen(ObjStr(nameObj)) == 0) { Tcl_DString ds, *dsPtr = &ds; Tcl_DStringInit(dsPtr); Tcl_DStringAppend(dsPtr, "::nsf::__#", 10); NewTclCommand(interp, dsPtr); newNameObj = Tcl_NewStringObj(Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr)); INCR_REF_COUNT(newNameObj); Tcl_DStringFree(dsPtr); nameObj = newNameObj; } /*fprintf(stderr, "trying to alloc <%s>\n", ObjStr(nameObj));*/ result = NsfCAllocMethod(interp, class, nameObj); if (result == TCL_OK && initcmdObj) { NsfObject *object; Tcl_Obj *nameObj = Tcl_GetObjResult(interp); INCR_REF_COUNT(nameObj); if (unlikely(GetObjectFromObj(interp, nameObj, &object) != TCL_OK)) { return NsfPrintError(interp, "couldn't find result of alloc"); } result = NsfDirectDispatchCmd(interp, object, 1, NsfGlobalObjs[NSF_EVAL], 1, &initcmdObj); if (likely(result == TCL_OK)) { Tcl_SetObjResult(interp,nameObj); } DECR_REF_COUNT(nameObj); } if (newNameObj) { DECR_REF_COUNT(newNameObj); } return result; } /* cmd "object::exists" NsfObjectExistsCmd { {-argName "value" -required 1 -type tclobj} } */ static int NsfObjectExistsCmd(Tcl_Interp *interp, Tcl_Obj *valueObj) { NsfObject *object; assert(interp); assert(valueObj); /* * Pass the object as Tcl_Obj, since we do not want to raise an error in * case the object does not exist. */ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), GetObjectFromObj(interp, valueObj, &object) == TCL_OK); return TCL_OK; } /* cmd "object::property" NsfObjectPropertyCmd { {-argName "objectName" -required 1 -type object} {-argName "objectproperty" -type "initialized|class|rootmetaclass|rootclass|volatile|slotcontainer|hasperobjectslots|keepcallerself|perobjectdispatch" -required 1} {-argName "value" -required 0 -type tclobj} } */ static int NsfObjectPropertyCmd(Tcl_Interp *interp, NsfObject *object, int objectproperty, Tcl_Obj *valueObj) { unsigned int flags = 0, allowSet = 0; assert(interp); assert(object); switch (objectproperty) { case ObjectpropertyInitializedIdx: flags = NSF_INIT_CALLED; allowSet = 1; break; case ObjectpropertyClassIdx: flags = NSF_IS_CLASS; break; case ObjectpropertyRootmetaclassIdx: flags = NSF_IS_ROOT_META_CLASS; break; case ObjectpropertyVolatileIdx: if (valueObj == NULL) { Tcl_SetObjResult(interp, NsfGlobalObjs[object->opt && object->opt->volatileVarName ? NSF_ONE : NSF_ZERO]); return TCL_OK; }; /* if value is provided, return the error below */ break; case ObjectpropertyRootclassIdx: flags = NSF_IS_ROOT_CLASS; break; case ObjectpropertySlotcontainerIdx: flags = NSF_IS_SLOT_CONTAINER; allowSet = 1; break; case ObjectpropertyKeepcallerselfIdx: flags = NSF_KEEP_CALLER_SELF; allowSet = 1; break; case ObjectpropertyPerobjectdispatchIdx: flags = NSF_PER_OBJECT_DISPATCH; allowSet = 1; break; case ObjectpropertyHasperobjectslotsIdx: flags = NSF_HAS_PER_OBJECT_SLOTS; allowSet = 1; break; } if (valueObj) { if (likely(allowSet)) { int flagValue, result; result = SetBooleanFlag(interp, &object->flags, flags, valueObj, &flagValue); if (unlikely(result != TCL_OK)) { return result; } if (objectproperty == ObjectpropertySlotcontainerIdx) { assert(object->nsPtr); if (flagValue) { /* turn on SlotContainerCmdResolver */ Tcl_SetNamespaceResolvers(object->nsPtr, (Tcl_ResolveCmdProc *)SlotContainerCmdResolver, NsColonVarResolver, (Tcl_ResolveCompiledVarProc *)NULL); } else { /* turn off SlotContainerCmdResolver */ Tcl_SetNamespaceResolvers(object->nsPtr, (Tcl_ResolveCmdProc *)NULL, NsColonVarResolver, (Tcl_ResolveCompiledVarProc *)NULL); } } } else { return NsfPrintError(interp, "object property is read only"); } } Tcl_SetObjResult(interp, NsfGlobalObjs[(object->flags & flags) ? NSF_ONE : NSF_ZERO]); return TCL_OK; } /* cmd "object::qualify" NsfObjectQualifyCmd { {-argName "objectName" -required 1 -type tclobj} } */ static int NsfObjectQualifyCmd(Tcl_Interp *interp, Tcl_Obj *nameObj) { CONST char *nameString = ObjStr(nameObj); assert(interp); assert(nameObj); if (isAbsolutePath(nameString)) { Tcl_SetObjResult(interp, nameObj); } else { Tcl_SetObjResult(interp, NameInNamespaceObj(nameString, CallingNameSpace(interp))); } return TCL_OK; } /* cmd "objectsystem::create" NsfObjectSystemCreateCmd { {-argName "rootClass" -required 1 -type tclobj} {-argName "rootMetaClass" -required 1 -type tclobj} {-argName "systemMethods" -required 0 -type tclobj} } */ static int NsfObjectSystemCreateCmd(Tcl_Interp *interp, Tcl_Obj *Object, Tcl_Obj *Class, Tcl_Obj *systemMethodsObj) { NsfClass *theobj = NULL, *thecls = NULL; Tcl_Obj *object, *class; char *objectName = ObjStr(Object); char *className = ObjStr(Class); NsfObjectSystem *osPtr; assert(interp); assert(Object); assert(Class); osPtr = NEW(NsfObjectSystem); memset(osPtr, 0, sizeof(NsfObjectSystem)); object = isAbsolutePath(objectName) ? Object : NameInNamespaceObj(objectName, CallingNameSpace(interp)); class = isAbsolutePath(className) ? Class : NameInNamespaceObj(className, CallingNameSpace(interp)); GetClassFromObj(interp, object, &theobj, 0); GetClassFromObj(interp, class, &thecls, 0); if (theobj || thecls) { ObjectSystemFree(interp, osPtr); NsfLog(interp, NSF_LOG_WARN, "Base class '%s' exists already; ignoring definition", theobj ? objectName : className); return TCL_OK; } if (systemMethodsObj) { int oc, idx, result; Tcl_Obj **ov; if ((result = Tcl_ListObjGetElements(interp, systemMethodsObj, &oc, &ov)) == TCL_OK) { int i; if (oc % 2) { ObjectSystemFree(interp, osPtr); return NsfPrintError(interp, "system methods must be provided as pairs"); } for (i = 0; i < oc; i += 2) { Tcl_Obj *arg, **arg_ov; int arg_oc = -1; arg = ov[i+1]; result = Tcl_GetIndexFromObj(interp, ov[i], Nsf_SystemMethodOpts, "system method", 0, &idx); if (likely(result == TCL_OK)) { result = Tcl_ListObjGetElements(interp, arg, &arg_oc, &arg_ov); } if (unlikely(result != TCL_OK)) { ObjectSystemFree(interp, osPtr); return NsfPrintError(interp, "invalid system method '%s'", ObjStr(ov[i])); } else if (arg_oc < 1 || arg_oc > 3) { ObjectSystemFree(interp, osPtr); return NsfPrintError(interp, "invalid system method argument '%s'", ObjStr(ov[i]), ObjStr(arg)); } /*fprintf(stderr, "NsfCreateObjectSystemCmd [%d] = %p %s (max %d, given %d)\n", idx, ov[i+1], ObjStr(ov[i+1]), NSF_s_set_idx, oc);*/ if (arg_oc == 1) { osPtr->methods[idx] = arg; } else { /* (arg_oc == 2) */ osPtr->methods[idx] = arg_ov[0]; osPtr->handles[idx] = arg_ov[1]; if (arg_oc == 3) { int bool = 0; Tcl_GetBooleanFromObj(interp, arg_ov[2], &bool); osPtr->protected[idx] = bool; } INCR_REF_COUNT(osPtr->handles[idx]); } INCR_REF_COUNT(osPtr->methods[idx]); } } else { ObjectSystemFree(interp, osPtr); return NsfPrintError(interp, "provided system methods are not a proper list"); } } /* * Create a basic object system with the basic root class Object and the * basic metaclass Class, and store them in the RUNTIME STATE if successful. */ theobj = PrimitiveCCreate(interp, object, NULL, NULL); thecls = PrimitiveCCreate(interp, class, NULL, NULL); /* fprintf(stderr, "CreateObjectSystem created base classes \n"); */ /* check whether Object and Class creation was successful */ if (theobj == NULL || thecls == NULL) { if (thecls) PrimitiveCDestroy(thecls); if (theobj) PrimitiveCDestroy(theobj); ObjectSystemFree(interp, osPtr); return NsfPrintError(interp, "creation of object system failed"); } theobj->osPtr = osPtr; thecls->osPtr = osPtr; osPtr->rootClass = theobj; osPtr->rootMetaClass = thecls; theobj->object.flags |= NSF_IS_ROOT_CLASS|NSF_INIT_CALLED; thecls->object.flags |= NSF_IS_ROOT_META_CLASS|NSF_INIT_CALLED; ObjectSystemAdd(interp, osPtr); AddInstance((NsfObject *)theobj, thecls); AddInstance((NsfObject *)thecls, thecls); AddSuper(thecls, theobj); if (NSF_DTRACE_OBJECT_ALLOC_ENABLED()) { NSF_DTRACE_OBJECT_ALLOC(ObjectName((NsfObject *)theobj), ClassName(((NsfObject *)theobj)->cl)); NSF_DTRACE_OBJECT_ALLOC(ObjectName((NsfObject *)thecls), ClassName(((NsfObject *)thecls)->cl)); } return TCL_OK; } /* cmd my NsfMyCmd { {-argName "-intrinsic" -nrargs 0} {-argName "-local" -nrargs 0} {-argName "-system" -nrargs 0} {-argName "method" -required 1 -type tclobj} {-argName "args" -type args} } */ static int NsfMyCmd(Tcl_Interp *interp, int withIntrinsic, int withLocal, int withSystem, Tcl_Obj *methodObj, int nobjc, Tcl_Obj *CONST nobjv[]) { NsfObject *self = GetSelfObj(interp); unsigned int flags; int result; assert(interp); assert(methodObj); if (unlikely(self == NULL)) { return NsfNoCurrentObjectError(interp, method_definitions[NsfMyCmdIdx].methodName); } if ((withIntrinsic && withLocal) || (withIntrinsic && withSystem) || (withLocal && withSystem)) { return NsfPrintError(interp, "flags '-intrinsic', '-local' and '-system' are mutual exclusive"); } #if 0 /* TODO attempt to make "my" NRE-enabled, failed so far (crash in mixinInheritanceTest) */ NsfCallStackContent *cscPtr = CallStackGetTopFrame0(interp); if (cscPtr == NULL || self != cscPtr->self) { flags = NSF_CSC_IMMEDIATE; } else { flags = NsfImmediateFromCallerFlags(cscPtr->flags); fprintf(stderr, "XXX MY %s.%s frame has flags %.6x -> next-flags %.6x\n", ObjectName(self), ObjStr(methodObj), cscPtr->flags, flags); } if (withIntrinsic) {flags |= NSF_CM_INTRINSIC_METHOD;} if (withLocal) {flags |= NSF_CM_LOCAL_METHOD;} if (withSystem) {flags |= NSF_CM_SYSTEM_METHOD;} result = CallMethod(self, interp, methodObj, nobjc+2, nobjv, flags); #else flags = NSF_CSC_IMMEDIATE; if (withIntrinsic) {flags |= NSF_CM_INTRINSIC_METHOD;} if (withLocal) {flags |= NSF_CM_LOCAL_METHOD;} if (withSystem) {flags |= NSF_CM_SYSTEM_METHOD;} result = CallMethod(self, interp, methodObj, nobjc+2, nobjv, flags); #endif return result; } /* *---------------------------------------------------------------------- * NsfNextCmd -- * * nsf::next calls the next shadowed method. It might get a single * argument which is used as argument vector for that method. If no * argument is provided, the argument vector of the last invocation * is used. * * Results: * Tcl return code * * Side effects: * The invoked method might produce side effects * *---------------------------------------------------------------------- */ /* cmd next NsfNextCmd { {-argName "arguments" -required 0 -type tclobj} } */ static int NsfNextCmd(Tcl_Interp *interp, Tcl_Obj *arguments) { int freeArgumentVector, oc, nobjc = 0, result; NsfCallStackContent *cscPtr = NULL; CONST char *methodName = NULL; Tcl_Obj **nobjv = NULL, **ov; assert(interp); if (arguments) { /* Arguments were provided. */ int result = Tcl_ListObjGetElements(interp, arguments, &oc, &ov); if (unlikely(result != TCL_OK)) {return result;} } else { /* No arguments were provided. */ oc = -1; ov = NULL; } result = NextGetArguments(interp, oc, ov, &cscPtr, &methodName, &nobjc, &nobjv, &freeArgumentVector); if (likely(result == TCL_OK)) { assert(cscPtr); result = NextSearchAndInvoke(interp, methodName, nobjc, nobjv, cscPtr, freeArgumentVector); } return result; } /* cmd nscopyvars NsfNSCopyVars { {-argName "fromNs" -required 1 -type tclobj} {-argName "toNs" -required 1 -type tclobj} } */ static int NsfNSCopyVarsCmd(Tcl_Interp *interp, Tcl_Obj *fromNs, Tcl_Obj *toNs) { Tcl_Namespace *fromNsPtr = NULL, *toNsPtr; Var *varPtr = NULL; Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; TclVarHashTable *varTablePtr; NsfObject *object, *destObject; CONST char *destFullName; Tcl_Obj *destFullNameObj; Tcl_CallFrame frame, *framePtr = &frame; Tcl_Obj *varNameObj = NULL; assert(interp); assert(fromNs); assert(toNs); TclGetNamespaceFromObj(interp, fromNs, &fromNsPtr); if (fromNsPtr) { if (TclGetNamespaceFromObj(interp, toNs, &toNsPtr) != TCL_OK) { return NsfPrintError(interp, "CopyVars: Destination namespace %s does not exist", ObjStr(toNs)); } object = GetObjectFromString(interp, ObjStr(fromNs)); destFullName = toNsPtr->fullName; destFullNameObj = Tcl_NewStringObj(destFullName, -1); INCR_REF_COUNT(destFullNameObj); varTablePtr = Tcl_Namespace_varTablePtr(fromNsPtr); Tcl_PushCallFrame(interp, (Tcl_CallFrame *)framePtr, toNsPtr, 0); } else { NsfObject *newObject; if (GetObjectFromObj(interp, fromNs, &object) != TCL_OK) { return NsfPrintError(interp, "CopyVars: Origin object/namespace %s does not exist", ObjStr(fromNs)); } if (GetObjectFromObj(interp, toNs, &newObject) != TCL_OK) { return NsfPrintError(interp, "CopyVars: Destination object/namespace %s does not exist", ObjStr(toNs)); } varTablePtr = object->varTablePtr; destFullNameObj = newObject->cmdName; destFullName = ObjStr(destFullNameObj); } destObject = GetObjectFromString(interp, destFullName); /* copy all vars in the ns */ hPtr = varTablePtr ? Tcl_FirstHashEntry(TclVarHashTablePtr(varTablePtr), &hSrch) : NULL; while (hPtr) { GetVarAndNameFromHash(hPtr, &varPtr, &varNameObj); INCR_REF_COUNT(varNameObj); if (!TclIsVarUndefined(varPtr) && !TclIsVarLink(varPtr)) { if (TclIsVarScalar(varPtr)) { /* * Copy scalar variables from the namespace, which might be * either object or namespace variables. */ if (object) { /* fprintf(stderr, "copy in obj %s var %s val '%s'\n", ObjectName(object), ObjStr(varNameObj), ObjStr(TclVarValue(Tcl_Obj, varPtr, objPtr)));*/ Nsf_ObjSetVar2((Nsf_Object *)destObject, interp, varNameObj, NULL, TclVarValue(Tcl_Obj, varPtr, objPtr), 0); } else { Tcl_ObjSetVar2(interp, varNameObj, NULL, TclVarValue(Tcl_Obj, varPtr, objPtr), TCL_NAMESPACE_ONLY); } } else { if (TclIsVarArray(varPtr)) { /* HERE!! PRE85 Why not [array get/set] based? Let the core iterate */ TclVarHashTable *aTable = TclVarValue(TclVarHashTable, varPtr, tablePtr); Tcl_HashSearch ahSrch; Tcl_HashEntry *ahPtr = aTable ? Tcl_FirstHashEntry(TclVarHashTablePtr(aTable), &ahSrch) : 0; for (; ahPtr; ahPtr = Tcl_NextHashEntry(&ahSrch)) { Tcl_Obj *eltNameObj; Var *eltVar; GetVarAndNameFromHash(ahPtr, &eltVar, &eltNameObj); INCR_REF_COUNT(eltNameObj); if (TclIsVarScalar(eltVar)) { if (object) { Nsf_ObjSetVar2((Nsf_Object *)destObject, interp, varNameObj, eltNameObj, TclVarValue(Tcl_Obj, eltVar, objPtr), 0); } else { Tcl_ObjSetVar2(interp, varNameObj, eltNameObj, TclVarValue(Tcl_Obj, eltVar, objPtr), TCL_NAMESPACE_ONLY); } } DECR_REF_COUNT(eltNameObj); } } } } DECR_REF_COUNT(varNameObj); hPtr = Tcl_NextHashEntry(&hSrch); } if (fromNsPtr) { DECR_REF_COUNT(destFullNameObj); Tcl_PopCallFrame(interp); } return TCL_OK; } /* cmd parameter::info NsfParameterInfoCmd { {-argName "parametersubcmd" -type "default|list|name|syntax|type" -required 1} {-argName "parameterspec" -required 1 -type tclobj} {-argName "varname" -required 0 -type tclobj} } */ static int NsfParameterInfoCmd(Tcl_Interp *interp, int parametersubcmd, Tcl_Obj *parameterspec, Tcl_Obj *varname) { NsfParsedParam parsedParam; Tcl_Obj *paramsObj, *listObj = NULL; Nsf_Param *paramsPtr; int result; assert(interp); assert(parameterspec); if (parametersubcmd != ParametersubcmdDefaultIdx && varname != NULL) { return NsfPrintError(interp, "parameter::info: provided third argument is only valid for querying defaults"); } paramsObj = Tcl_NewListObj(1, ¶meterspec); INCR_REF_COUNT(paramsObj); result = ParamDefsParse(interp, NULL, paramsObj, NSF_DISALLOWED_ARG_OBJECT_PARAMETER, 1, &parsedParam); DECR_REF_COUNT(paramsObj); if (unlikely(result != TCL_OK)) { return result; } assert(parsedParam.paramDefs); paramsPtr = parsedParam.paramDefs->paramsPtr; assert(paramsPtr); /* * Since we are passing in a parameter definition in Tcl syntax, and we want * to extract information from that syntax, it make limited sense to provide * a context object for virutal parameter expansion. At least, we do not * allow this so far. */ switch (parametersubcmd) { case ParametersubcmdDefaultIdx: if (paramsPtr->defaultValue) { if (varname) { Tcl_Obj *resultObj = Tcl_ObjSetVar2(interp, varname, NULL, paramsPtr->defaultValue, TCL_LEAVE_ERR_MSG|TCL_PARSE_PART1); if (resultObj == NULL) { ParamDefsRefCountDecr(parsedParam.paramDefs); return TCL_ERROR; } } Tcl_SetObjResult(interp, NsfGlobalObjs[NSF_ONE]); } else { Tcl_SetObjResult(interp, NsfGlobalObjs[NSF_ZERO]); } break; case ParametersubcmdListIdx: listObj = ParamDefsList(interp, paramsPtr, NULL, NULL); Tcl_SetObjResult(interp, listObj); DECR_REF_COUNT2("paramDefsObj", listObj); break; case ParametersubcmdNameIdx: listObj = ParamDefsNames(interp, paramsPtr, NULL, NULL); Tcl_SetObjResult(interp, listObj); DECR_REF_COUNT2("paramDefsObj", listObj); break; case ParametersubcmdSyntaxIdx: listObj = NsfParamDefsSyntax(interp, paramsPtr, NULL, NULL); Tcl_SetObjResult(interp, listObj); DECR_REF_COUNT2("paramDefsObj", listObj); break; case ParametersubcmdTypeIdx: if (paramsPtr->type) { if (paramsPtr->converter == Nsf_ConvertToTclobj && paramsPtr->converterArg) { Tcl_SetObjResult(interp, paramsPtr->converterArg); } else { if (paramsPtr->converter == Nsf_ConvertToObject || paramsPtr->converter == Nsf_ConvertToClass) { CONST char *what = paramsPtr->type; /* * baseclass and metaclass are communicated via flags */ if (unlikely(paramsPtr->flags & NSF_ARG_BASECLASS)) { what = "baseclass"; } else if (unlikely(paramsPtr->flags & NSF_ARG_METACLASS)) { what = "metaclass"; } /* * The converterArg might contain a class for type checking */ if (paramsPtr->converterArg == NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj(what, -1)); } else { Tcl_SetObjResult(interp, paramsPtr->converterArg); } } else { Tcl_SetObjResult(interp, Tcl_NewStringObj(paramsPtr->type, -1)); } } } else { Tcl_SetObjResult(interp, NsfGlobalObjs[NSF_EMPTY]); } break; } ParamDefsRefCountDecr(parsedParam.paramDefs); return TCL_OK; } /* cmd parameter::cache::classinvalidate NsfParameterCacheClassInvalidateCmd { {-argName "class" -required 1 -type class} } */ static int NsfParameterCacheClassInvalidateCmd(Tcl_Interp *interp, NsfClass *cl) { assert(interp); assert(cl); /* * First, increment the epoch in case we have a parsedParam. The * classParamPtrEpoch is just used for PER_OBJECT_PARAMETER_CACHING */ #if defined(PER_OBJECT_PARAMETER_CACHING) if (unlikely(cl->parsedParamPtr != NULL)) { NsfClassParamPtrEpochIncr("NsfParameterCacheClassInvalidateCmd"); } #endif /* * During shutdown, no new objects are created, therefore we do not need to * to invalidate the cached parsedParamPtr of the classes. */ if (unlikely(RUNTIME_STATE(interp)->exitHandlerDestroyRound == NSF_EXITHANDLER_OFF)) { NsfClasses *dependentSubClasses; NsfClasses *clPtr; /* * Clear the cached parsedParam of the class and all its subclasses (the * result of DependentSubClasses() contains the starting class). Furthermore, * make a quick check, if any of the subclasses is a class mixin of some * other class. */ dependentSubClasses = DependentSubClasses(cl); if (dependentSubClasses) { for (clPtr = dependentSubClasses; clPtr; clPtr = clPtr->nextPtr) { NsfClass *subClass = clPtr->cl; if (subClass->parsedParamPtr) { ParsedParamFree(subClass->parsedParamPtr); subClass->parsedParamPtr = NULL; } } NsfClassListFree(dependentSubClasses); } } return TCL_OK; } /* cmd parameter::cache::objectinvalidate NsfParameterCacheObjectInvalidateCmd { {-argName "object" -required 1 -type object} } */ static int NsfParameterCacheObjectInvalidateCmd(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); #if defined(PER_OBJECT_PARAMETER_CACHING) if (object->opt && object->opt->parsedParamPtr) { /*fprintf(stderr, " %p %s invalidate %p\n", object, ObjectName(object), object->opt->parsedParamPtr);*/ ParsedParamFree(object->opt->parsedParamPtr); object->opt->parsedParamPtr = NULL; } #endif return TCL_OK; } /* cmd parameter::specs NsfParameterSpecsCmd { {-argName "-configure" -nrargs 0 -required 0} {-argName "-nonposargs" -nrargs 0 -required 0} {-argName "slotobjs" -required 1 -type tclobj} } */ static int NsfParameterSpecsCmd(Tcl_Interp *interp, int withConfigure, int withNonposargs, Tcl_Obj *slotListObj) { NsfTclObjList *objList = NULL, *elt; Tcl_Obj **objv, *resultObj; int result = TCL_OK, i, objc; assert(interp); assert(slotListObj); if (Tcl_ListObjGetElements(interp, slotListObj, &objc, &objv) != TCL_OK) { return NsfPrintError(interp, "NsfParameterSpecsCmd: invalid slot object list"); } /* * Iterate over the slot objects and obtain the position and the * parameterSpec. */ for (i = 0; i < objc; i++) { NsfObject *slotObject; Tcl_Obj *positionObj, *specObj = NULL; if (GetObjectFromObj(interp, objv[i], &slotObject) != TCL_OK) { return NsfPrintError(interp, "objectparameter: slot element is not a next scripting object"); } /* * When withConfigure is provided, skip this parameter ... * - when configure is not set * - or configure == 0 */ if (withConfigure) { int configure = 0; Tcl_Obj *configureObj = Nsf_ObjGetVar2((Nsf_Object *)slotObject, interp, NsfGlobalObjs[NSF_CONFIGURABLE], NULL, 0); if (configureObj == NULL) continue; Tcl_GetBooleanFromObj(interp, configureObj, &configure); if (!configure) continue; } /* * When withNonposargs is provided, skip this parameter ... * - when positional == 1 */ if (withNonposargs) { Tcl_Obj *positionalObj = Nsf_ObjGetVar2((Nsf_Object *)slotObject, interp, NsfGlobalObjs[NSF_POSITIONAL], NULL, 0); if (positionalObj) { int positional = 0; Tcl_GetBooleanFromObj(interp, positionalObj, &positional); if (positional) continue; } } positionObj = Nsf_ObjGetVar2((Nsf_Object *)slotObject, interp, NsfGlobalObjs[NSF_POSITION], NULL, 0); specObj = Nsf_ObjGetVar2((Nsf_Object *)slotObject, interp, NsfGlobalObjs[NSF_PARAMETERSPEC], NULL, 0); if (specObj == NULL) { result = CallMethod(slotObject, interp, NsfGlobalObjs[NSF_GET_PARAMETER_SPEC], 2, NULL, NSF_CM_IGNORE_PERMISSIONS|NSF_CSC_IMMEDIATE); if (unlikely(result != TCL_OK)) { return NsfPrintError(interp, "objectparameter: %s %s returned error", ObjectName(slotObject), NsfGlobalStrings[NSF_GET_PARAMETER_SPEC]); } specObj = Tcl_GetObjResult(interp); } /*fprintf(stderr, "NsfParameterSpecsCmd slot obj = %s pos %s spec %s\n", ObjStr(objv[i]), positionObj ? ObjStr(positionObj) : "NONE", ObjStr(specObj) );*/ /* * Add the spec to the list indicated by the position */ TclObjListAdd(interp, &objList, positionObj, specObj); } /* * Fold the per-position lists into a flat result list */ resultObj = Tcl_NewListObj(0, NULL); for (elt = objList; elt; elt = elt->nextPtr) { Tcl_ListObjGetElements(interp, elt->payload, &objc, &objv); for (i = 0; i < objc; i++) { Tcl_ListObjAppendElement(interp, resultObj, objv[i]); } } Tcl_SetObjResult(interp, resultObj); if (objList) { TclObjListFreeList(objList); } return result; } /* cmd proc NsfProcCmd { {-argName "-ad" -required 0 -nrargs 0 -type switch} {-argName "-checkalways" -required 0 -nrargs 0 -type switch} {-argName "procName" -required 1 -type tclobj} {-argName "arguments" -required 1 -type tclobj} {-argName "body" -required 1 -type tclobj} } */ static int NsfProcCmd(Tcl_Interp *interp, int with_ad, int with_checkAlways, Tcl_Obj *nameObj, Tcl_Obj *arguments, Tcl_Obj *body) { NsfParsedParam parsedParam; int result; assert(interp); assert(nameObj); assert(arguments); assert(body); /* * Parse argument list "arguments" to determine if we should provide * nsf parameter handling. */ result = ParamDefsParse(interp, nameObj, arguments, NSF_DISALLOWED_ARG_METHOD_PARAMETER, 0, &parsedParam); if (unlikely(result != TCL_OK)) { return result; } if (parsedParam.paramDefs) { /* * We need parameter handling. In such cases, a thin C-based layer * is added which handles the parameter passing and calls the proc * later. */ result = NsfProcAdd(interp, &parsedParam, ObjStr(nameObj), body, with_ad, with_checkAlways); } else { /* * No parameter handling needed. A plain Tcl proc is added. */ Tcl_Obj *ov[4]; ov[0] = NULL; ov[1] = nameObj; ov[2] = arguments; ov[3] = body; result = Tcl_ProcObjCmd(0, interp, 4, ov); } return result; } /* cmd relation::get NsfRelationGetCmd { {-argName "object" -type object} {-argName "relationtype" -required 1 -type "object-mixin|class-mixin|object-filter|class-filter|class|superclass|rootclass"} } */ static int NsfRelationGetCmd(Tcl_Interp *interp, NsfObject *object, int relationtype) { return NsfRelationSetCmd(interp, object, relationtype, NULL); } /* *---------------------------------------------------------------------- * NsfRelationClassMixinsSet -- * * Set class mixins; the main reason for the factored out semantics is that * it can allow to undo/redo the operations in case of a failure. * * Results: * Tcl result code. * * Side effects: * class mixins are set, various kinds of invlidations. * *---------------------------------------------------------------------- */ static int NsfRelationClassMixinsSet(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *valueObj, int oc, Tcl_Obj **ov) nonnull(1) nonnull(2) nonnull(3); static int NsfRelationClassMixinsSet(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *valueObj, int oc, Tcl_Obj **ov) { NsfCmdList *newMixinCmdList = NULL, *cmds; NsfClasses *subClasses; NsfClassOpt *clopt = cl->opt; int i; assert(interp); assert(cl); assert(clopt); assert(valueObj); for (i = 0; i < oc; i++) { if (MixinAdd(interp, &newMixinCmdList, ov[i], cl->object.cl) != TCL_OK) { CmdListFree(&newMixinCmdList, GuardDel); return TCL_ERROR; } } if (clopt->classMixins) { if (clopt->classMixins) RemoveFromClassMixinsOf(cl->object.id, clopt->classMixins); CmdListFree(&clopt->classMixins, GuardDel); } subClasses = DependentSubClasses(cl); MixinInvalidateObjOrders(interp, cl, subClasses); /* * Since methods of mixed in classes may be used as filters, we have to * invalidate the filters as well. */ if (FiltersDefined(interp) > 0) { FilterInvalidateObjOrders(interp, subClasses); } NsfClassListFree(subClasses); /* * Now register the specified mixins. */ clopt->classMixins = newMixinCmdList; /* * Finally, update classMixinOfs */ for (cmds = newMixinCmdList; cmds; cmds = cmds->nextPtr) { NsfObject *nObject = NsfGetObjectFromCmdPtr(cmds->cmdPtr); if (nObject) { NsfClassOpt *nclopt = NsfRequireClassOpt((NsfClass *) nObject); CmdListAddSorted(&nclopt->isClassMixinOf, cl->object.id, NULL); } else { NsfLog(interp, NSF_LOG_WARN, "Problem registering %s as a class mixin of %s\n", ObjStr(valueObj), ClassName(cl)); } } return TCL_OK; } /* cmd relation::set NsfRelationSetCmd { {-argName "object" -type object} {-argName "relationtype" -required 1 -type "object-mixin|class-mixin|object-filter|class-filter|class|superclass|rootclass"} {-argName "value" -required 0 -type tclobj} } */ static int NsfRelationSetCmd(Tcl_Interp *interp, NsfObject *object, int relationtype, Tcl_Obj *valueObj) { int oc; Tcl_Obj **ov; NsfClass *cl = NULL; NsfObjectOpt *objopt = NULL; NsfClassOpt *clopt = NULL, *nclopt = NULL; int i; assert(interp); assert(object); /*fprintf(stderr, "NsfRelationSetCmd %s rel=%d val='%s'\n", ObjectName(object), relationtype, valueObj ? ObjStr(valueObj) : "NULL");*/ if (relationtype == RelationtypeClass_mixinIdx || relationtype == RelationtypeClass_filterIdx) { if (NsfObjectIsClass(object)) { cl = (NsfClass *)object; } else { /* fall back to per-object case */ relationtype = (relationtype == RelationtypeClass_mixinIdx) ? RelationtypeObject_mixinIdx : RelationtypeObject_filterIdx ; } } switch (relationtype) { case RelationtypeObject_filterIdx: case RelationtypeObject_mixinIdx: if (valueObj == NULL) { objopt = object->opt; switch (relationtype) { case RelationtypeObject_mixinIdx: return objopt ? MixinInfo(interp, objopt->objMixins, NULL, 1, NULL) : TCL_OK; case RelationtypeObject_filterIdx: return objopt ? FilterInfo(interp, objopt->objFilters, NULL, 1, 0) : TCL_OK; } } if (Tcl_ListObjGetElements(interp, valueObj, &oc, &ov) != TCL_OK) { return TCL_ERROR; } objopt = NsfRequireObjectOpt(object); break; case RelationtypeClass_mixinIdx: case RelationtypeClass_filterIdx: if (valueObj == NULL) { clopt = cl->opt; switch (relationtype) { case RelationtypeClass_mixinIdx: return clopt ? MixinInfo(interp, clopt->classMixins, NULL, 1, NULL) : TCL_OK; case RelationtypeClass_filterIdx: return clopt ? FilterInfo(interp, clopt->classFilters, NULL, 1, 0) : TCL_OK; } } if (Tcl_ListObjGetElements(interp, valueObj, &oc, &ov) != TCL_OK) { return TCL_ERROR; } clopt = NsfRequireClassOpt(cl); break; case RelationtypeSuperclassIdx: if (!NsfObjectIsClass(object)) { return NsfObjErrType(interp, "superclass", object->cmdName, "class", NULL); } cl = (NsfClass *)object; if (valueObj == NULL) { return ListSuperClasses(interp, cl, NULL, 0); } if (Tcl_ListObjGetElements(interp, valueObj, &oc, &ov) != TCL_OK) { return TCL_ERROR; } return SuperclassAdd(interp, cl, oc, ov, valueObj); case RelationtypeClassIdx: if (valueObj == NULL) { Tcl_SetObjResult(interp, object->cl->object.cmdName); return TCL_OK; } GetClassFromObj(interp, valueObj, &cl, 1); if (cl == NULL) return NsfObjErrType(interp, "class", valueObj, "a class", NULL); i = ChangeClass(interp, object, cl); if (i == TCL_OK) { Tcl_SetObjResult(interp, object->cl->object.cmdName); } return i; case RelationtypeRootclassIdx: { NsfClass *metaClass = NULL; if (!NsfObjectIsClass(object)) { return NsfObjErrType(interp, "rootclass", object->cmdName, "class", NULL); } cl = (NsfClass *)object; if (valueObj == NULL) { return NsfPrintError(interp, "metaclass must be specified as third argument"); } GetClassFromObj(interp, valueObj, &metaClass, 0); if (metaClass == NULL) return NsfObjErrType(interp, "rootclass", valueObj, "class", NULL); cl->object.flags |= NSF_IS_ROOT_CLASS; metaClass->object.flags |= NSF_IS_ROOT_META_CLASS; return TCL_OK; /* todo: need to remove these properties? allow to delete a class system at runtime? */ } } switch (relationtype) { case RelationtypeObject_mixinIdx: { NsfCmdList *newMixinCmdList = NULL, *cmds; /* * Add every mixin class */ for (i = 0; i < oc; i++) { if (MixinAdd(interp, &newMixinCmdList, ov[i], object->cl->object.cl) != TCL_OK) { CmdListFree(&newMixinCmdList, GuardDel); return TCL_ERROR; } } if (objopt->objMixins) { NsfCmdList *cmdlist, *del; /* * Delete from old isObjectMixinOf lists */ for (cmdlist = objopt->objMixins; cmdlist; cmdlist = cmdlist->nextPtr) { cl = NsfGetClassFromCmdPtr(cmdlist->cmdPtr); clopt = cl ? cl->opt : NULL; if (clopt) { del = CmdListFindCmdInList(object->id, clopt->isObjectMixinOf); if (del) { /* fprintf(stderr, "Removing object %s from isObjectMixinOf of class %s\n", ObjectName(object), ObjStr(NsfGetClassFromCmdPtr(cmdlist->cmdPtr)->object.cmdName)); */ del = CmdListRemoveFromList(&clopt->isObjectMixinOf, del); CmdListDeleteCmdListEntry(del, GuardDel); } } } CmdListFree(&objopt->objMixins, GuardDel); } /* * Invalidate per-object infos */ NsfParameterCacheObjectInvalidateCmd(interp, object); object->flags &= ~NSF_MIXIN_ORDER_VALID; /* * Since mixin procs may be used as filters -> we have to invalidate * filters as well. */ object->flags &= ~NSF_FILTER_ORDER_VALID; /* * Now register the specified mixins. */ objopt->objMixins = newMixinCmdList; for (cmds = newMixinCmdList; cmds; cmds = cmds->nextPtr) { NsfObject *nObject = NsfGetObjectFromCmdPtr(cmds->cmdPtr); if (nObject) { nclopt = NsfRequireClassOpt((NsfClass *) nObject); CmdListAddSorted(&nclopt->isObjectMixinOf, object->id, NULL); } else { NsfLog(interp, NSF_LOG_WARN, "Problem registering %s as a object mixin of %s\n", ObjStr(valueObj), ObjectName(object)); } } MixinComputeDefined(interp, object); FilterComputeDefined(interp, object); break; } case RelationtypeObject_filterIdx: { NsfCmdList *newFilterCmdList = NULL; for (i = 0; i < oc; i ++) { if (FilterAdd(interp, &newFilterCmdList, ov[i], object, NULL) != TCL_OK) { CmdListFree(&newFilterCmdList, GuardDel); return TCL_ERROR; } } if (objopt->objFilters) { CmdListFree(&objopt->objFilters, GuardDel); } object->flags &= ~NSF_FILTER_ORDER_VALID; objopt->objFilters = newFilterCmdList; /*FilterComputeDefined(interp, object);*/ break; } case RelationtypeClass_mixinIdx: if (NsfRelationClassMixinsSet(interp, cl, valueObj, oc, ov) != TCL_OK) { return TCL_ERROR; } break; case RelationtypeClass_filterIdx: { NsfCmdList *newFilterCmdList = NULL; for (i = 0; i < oc; i ++) { if (FilterAdd(interp, &newFilterCmdList, ov[i], NULL, cl) != TCL_OK) { CmdListFree(&newFilterCmdList, GuardDel); return TCL_ERROR; } } if (clopt->classFilters) { CmdListFree(&clopt->classFilters, GuardDel); } if (FiltersDefined(interp) > 0) { NsfClasses *subClasses = DependentSubClasses(cl); if (subClasses) { FilterInvalidateObjOrders(interp, subClasses); NsfClassListFree(subClasses); } } clopt->classFilters = newFilterCmdList; break; } } /* * Return on success the final setting */ NsfRelationSetCmd(interp, object, relationtype, NULL); return TCL_OK; } /* cmd current NsfCurrentCmd { {-argName "currentoption" -required 0 -type "proc|method|methodpath|object|class|activelevel|args|activemixin|calledproc|calledmethod|calledclass|callingproc|callingmethod|callingclass|callinglevel|callingobject|filterreg|isnextcall|next"} } */ static int NsfCurrentCmd(Tcl_Interp *interp, int selfoption) { NsfObject *object = GetSelfObj(interp); NsfCallStackContent *cscPtr; Tcl_CallFrame *framePtr; int result = TCL_OK; assert(interp); if (selfoption == 0 || selfoption == CurrentoptionObjectIdx) { if (likely(object != NULL)) { Tcl_SetObjResult(interp, object->cmdName); return TCL_OK; } else { return NsfNoCurrentObjectError(interp, NULL); } } if (unlikely(object == NULL && selfoption != CurrentoptionCallinglevelIdx)) { return NsfNoCurrentObjectError(interp, NULL); } switch (selfoption) { case CurrentoptionMethodIdx: /* fall through */ case CurrentoptionProcIdx: cscPtr = CallStackGetTopFrame0(interp); if (cscPtr) { CONST char *procName = Tcl_GetCommandName(interp, cscPtr->cmdPtr); Tcl_SetResult(interp, (char *)procName, TCL_VOLATILE); } else { return NsfPrintError(interp, "can't find proc"); } break; case CurrentoptionMethodpathIdx: cscPtr = CallStackGetTopFrame0(interp); Tcl_SetObjResult(interp, NsfMethodNamePath(interp, CallStackGetTclFrame(interp, NULL, 1), Tcl_GetCommandName(interp, cscPtr->cmdPtr))); break; case CurrentoptionClassIdx: /* class subcommand */ cscPtr = CallStackGetTopFrame0(interp); Tcl_SetObjResult(interp, cscPtr && cscPtr->cl ? cscPtr->cl->object.cmdName : NsfGlobalObjs[NSF_EMPTY]); break; case CurrentoptionActivelevelIdx: Tcl_SetObjResult(interp, ComputeLevelObj(interp, ACTIVE_LEVEL)); break; case CurrentoptionArgsIdx: { cscPtr = CallStackGetTopFrame(interp, &framePtr); if (cscPtr) { int nobjc; Tcl_Obj **nobjv; if (cscPtr->objv) { nobjc = cscPtr->objc; nobjv = (Tcl_Obj **)cscPtr->objv; } else { nobjc = Tcl_CallFrame_objc(framePtr); nobjv = (Tcl_Obj **)Tcl_CallFrame_objv(framePtr); } Tcl_SetObjResult(interp, Tcl_NewListObj(nobjc-1, nobjv+1)); } else { return NsfPrintError(interp, "can't find proc"); } break; } case CurrentoptionActivemixinIdx: { NsfObject *object = NULL; if (RUNTIME_STATE(interp)->currentMixinCmdPtr) { object = NsfGetObjectFromCmdPtr(RUNTIME_STATE(interp)->currentMixinCmdPtr); } Tcl_SetObjResult(interp, object ? object->cmdName : NsfGlobalObjs[NSF_EMPTY]); break; } case CurrentoptionCalledprocIdx: case CurrentoptionCalledmethodIdx: cscPtr = CallStackFindActiveFilter(interp); if (cscPtr) { Tcl_SetObjResult(interp, Tcl_NewStringObj(MethodName(cscPtr->filterStackEntry->calledProc), -1)); } else { result = NsfPrintError(interp, "called from outside of a filter"); } break; case CurrentoptionCalledclassIdx: { NsfClass *cl = FindCalledClass(interp, object); Tcl_SetObjResult(interp, cl ? cl->object.cmdName : NsfGlobalObjs[NSF_EMPTY]); break; } case CurrentoptionCallingmethodIdx: case CurrentoptionCallingprocIdx: { Tcl_Obj *resultObj; cscPtr = NsfCallStackFindLastInvocation(interp, 1, &framePtr); if (cscPtr && cscPtr->cmdPtr) { resultObj = NsfMethodNamePath(interp, CallStackGetTclFrame(interp, framePtr, 1), Tcl_GetCommandName(interp, cscPtr->cmdPtr)); } else { resultObj = NsfGlobalObjs[NSF_EMPTY]; } Tcl_SetObjResult(interp, resultObj); break; } case CurrentoptionCallingclassIdx: cscPtr = NsfCallStackFindLastInvocation(interp, 1, NULL); Tcl_SetObjResult(interp, cscPtr && cscPtr->cl ? cscPtr->cl->object.cmdName : NsfGlobalObjs[NSF_EMPTY]); break; case CurrentoptionCallinglevelIdx: if (object == NULL) { Tcl_SetIntObj(Tcl_GetObjResult(interp), 1); } else { Tcl_SetObjResult(interp, ComputeLevelObj(interp, CALLING_LEVEL)); } break; case CurrentoptionCallingobjectIdx: cscPtr = NsfCallStackFindLastInvocation(interp, 1, NULL); Tcl_SetObjResult(interp, cscPtr ? cscPtr->self->cmdName : NsfGlobalObjs[NSF_EMPTY]); break; case CurrentoptionFilterregIdx: cscPtr = CallStackFindActiveFilter(interp); if (cscPtr) { Tcl_SetObjResult(interp, FilterFindReg(interp, object, cscPtr->cmdPtr)); } else { result = NsfPrintError(interp, "called from outside of a filter"); } break; case CurrentoptionIsnextcallIdx: { (void)CallStackGetTopFrame(interp, &framePtr); framePtr = CallStackNextFrameOfType(Tcl_CallFrame_callerPtr(framePtr), FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD); cscPtr = framePtr ? Tcl_CallFrame_clientData(framePtr) : NULL; Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (cscPtr && (cscPtr->flags & NSF_CSC_CALL_IS_NEXT))); break; } case CurrentoptionNextmethodIdx: result = FindSelfNext(interp); break; } return result; } /* cmd self NsfSelfCmd { } */ static int NsfSelfCmd(Tcl_Interp *interp) { NsfObject *object = GetSelfObj(interp); assert(interp); if (likely(object != NULL)) { Tcl_SetObjResult(interp, object->cmdName); return TCL_OK; } else { return NsfNoCurrentObjectError(interp, NULL); } } /* cmd var::exists NsfVarExistsCmd { {-argName "-array" -required 0 -nrargs 0} {-argName "object" -required 1 -type object} {-argName "varName" -required 1} } */ static int NsfVarExistsCmd(Tcl_Interp *interp, int withArray, NsfObject *object, CONST char *varName) { unsigned int flags = NSF_VAR_TRIGGER_TRACE|NSF_VAR_REQUIRE_DEFINED| (withArray ? NSF_VAR_ISARRAY : 0); assert(interp); assert(object); assert(varName); if (CheckVarName(interp, varName) != TCL_OK) { return TCL_ERROR; } Tcl_SetIntObj(Tcl_GetObjResult(interp), VarExists(interp, object, varName, NULL, flags)); return TCL_OK; } /* cmd var::get NsfVarGetCmd { {-argName "-array" -required 0 -nrargs 0} {-argName "object" -required 1 -type object} {-argName "varName" -required 1 -type tclobj} } */ static int NsfVarGetCmd(Tcl_Interp *interp, int withArray, NsfObject *object, Tcl_Obj *varName) { return NsfVarSetCmd(interp, withArray, object, varName, NULL); } /* cmd var::import NsfVarImportCmd { {-argName "object" -type object} {-argName "args" -type args} } */ static int NsfVarImport(Tcl_Interp *interp, NsfObject *object, const char *cmdName, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(3) nonnull(5); static int NsfVarImport(Tcl_Interp *interp, NsfObject *object, const char *cmdName, int objc, Tcl_Obj *CONST objv[]) { int i, result = TCL_OK; assert(interp); assert(object); assert(cmdName); assert(objv); for (i = 0; i < objc && result == TCL_OK; i++) { Tcl_Obj **ov; int oc; /*fprintf(stderr, "ListGetElements %p %s\n", objv[i], ObjStr(objv[i]));*/ if ((result = Tcl_ListObjGetElements(interp, objv[i], &oc, &ov)) == TCL_OK) { Tcl_Obj *varName = NULL, *alias = NULL; switch (oc) { case 0: {varName = objv[i]; break;} case 1: {varName = ov[0]; break;} case 2: {varName = ov[0]; alias = ov[1]; break;} } if (likely(varName != NULL)) { result = ImportInstVarIntoCurrentScope(interp, cmdName, object, varName, alias); } else { assert(objv[i]); result = NsfPrintError(interp, "invalid variable specification '%s'", ObjStr(objv[i])); } } } return result; } static int NsfVarImportCmd(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[]) { assert(interp); assert(object); return NsfVarImport(interp, object, "importvar", objc, objv); } /* cmd var::set NsfVarSetCmd { {-argName "-array" -required 0 -nrargs 0} {-argName "object" -required 1 -type object} {-argName "varName" -required 1 -type tclobj} {-argName "value" -required 0 -type tclobj} } */ static int NsfVarSetCmd(Tcl_Interp *interp, int withArray, NsfObject *object, Tcl_Obj *varName, Tcl_Obj *valueObj) { assert(interp); assert(object); assert(varName); if (CheckVarName(interp, ObjStr(varName)) != TCL_OK) { return TCL_ERROR; } if (withArray) { return SetInstArray(interp, object, varName, valueObj); } else { return SetInstVar(interp, object, varName, valueObj); } } /* cmd var::unset NsfVarUnsetCmd { {-argName "-nocomplain" -required 0 -nrargs 0} {-argName "object" -required 1 -type object} {-argName "varName" -required 1 -type tclobj} } */ static int NsfVarUnsetCmd(Tcl_Interp *interp, int withNocomplain, NsfObject *object, Tcl_Obj *varNameObj) { char *varName = ObjStr(varNameObj); assert(interp); assert(object); assert(varNameObj); if (CheckVarName(interp, varName) != TCL_OK) { return TCL_ERROR; } return UnsetInstVar(interp, withNocomplain, object, varName); } /*********************************************************************** * End generated Next Scripting commands ***********************************************************************/ /* * Parameter support functions */ typedef struct NsfParamWrapper { Nsf_Param *paramPtr; int refCount; int canFree; } NsfParamWrapper; static Tcl_DupInternalRepProc ParamDupInteralRep; static Tcl_FreeInternalRepProc ParamFreeInternalRep; static Tcl_UpdateStringProc ParamUpdateString; static void ParamUpdateString(Tcl_Obj *objPtr) nonnull(1); static void ParamDupInteralRep(Tcl_Obj *srcPtr, Tcl_Obj *UNUSED(dupPtr)) nonnull(1); static void ParamFreeInternalRep(register Tcl_Obj *objPtr) nonnull(1); static int ParamSetFromAny(Tcl_Interp *interp, register Tcl_Obj *objPtr) nonnull(1) nonnull(2); static int ParamSetFromAny2(Tcl_Interp *interp, const char *varNamePrefix, int configureParameter, register Tcl_Obj *objPtr) nonnull(1) nonnull(2) nonnull(4); static void ParamUpdateString(Tcl_Obj *objPtr) { assert(objPtr); Tcl_Panic("%s of type %s should not be called", "updateStringProc", objPtr->typePtr->name); } static void ParamDupInteralRep(Tcl_Obj *srcPtr, Tcl_Obj *UNUSED(dupPtr)) { assert(srcPtr); Tcl_Panic("%s of type %s should not be called", "dupStringProc", srcPtr->typePtr->name); } static Tcl_ObjType paramObjType = { "nsfParam", /* name */ ParamFreeInternalRep, /* freeIntRepProc */ ParamDupInteralRep, /* dupIntRepProc */ ParamUpdateString, /* updateStringProc */ ParamSetFromAny /* setFromAnyProc */ }; static void ParamFreeInternalRep( register Tcl_Obj *objPtr) /* Param structure object with internal * representation to free. */ { NsfParamWrapper *paramWrapperPtr = (NsfParamWrapper *)objPtr->internalRep.twoPtrValue.ptr1; assert(objPtr); if (paramWrapperPtr != NULL) { /* fprintf(stderr, "ParamFreeInternalRep freeing wrapper %p paramPtr %p refCount %dcanFree %d\n", paramWrapperPtr, paramWrapperPtr->paramPtr, paramWrapperPtr->refCount, paramWrapperPtr->canFree);*/ if (paramWrapperPtr->canFree) { ParamsFree(paramWrapperPtr->paramPtr); FREE(NsfParamWrapper, paramWrapperPtr); } else { paramWrapperPtr->refCount--; } } } /* *---------------------------------------------------------------------- * ParamSetFromAny2 -- * * Convert the second argument argument (e.g. "x:integer") into the * internal representation of a Tcl_Obj of the type parameter. The * conversion is performed by the usual ParamParse() function, used * e.g. for the parameter passing for arguments. * * Results: * Result code. * * Side effects: * Converted internal rep of Tcl_Obj * *---------------------------------------------------------------------- */ static int ParamSetFromAny2( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ const char *varNamePrefix, /* shows up as varName in error message */ int configureParameter, /* allow object parameters */ register Tcl_Obj *objPtr) /* The object to convert. */ { Tcl_Obj *fullParamObj = Tcl_NewStringObj(varNamePrefix, -1); int result, possibleUnknowns = 0, plainParams = 0, nrNonposArgs = 0; NsfParamWrapper *paramWrapperPtr = NEW(NsfParamWrapper); assert(interp); assert(varNamePrefix); assert(objPtr); paramWrapperPtr->paramPtr = ParamsNew(1); paramWrapperPtr->refCount = 1; paramWrapperPtr->canFree = 0; Tcl_AppendLimitedToObj(fullParamObj, ObjStr(objPtr), -1, INT_MAX, NULL); INCR_REF_COUNT(fullParamObj); result = ParamParse(interp, NsfGlobalObjs[NSF_VALUECHECK], fullParamObj, configureParameter ? NSF_DISALLOWED_ARG_OBJECT_PARAMETER : NSF_DISALLOWED_ARG_VALUECHECK, paramWrapperPtr->paramPtr, &possibleUnknowns, &plainParams, &nrNonposArgs); /* * We treat currently unknown user level converters as error. */ if (paramWrapperPtr->paramPtr->flags & NSF_ARG_CURRENTLY_UNKNOWN) { result = TCL_ERROR; } if (likely(result == TCL_OK)) { /* * In success cases, the memory allocated by this function is freed via * the tcl_obj type. */ paramWrapperPtr->paramPtr->flags |= NSF_ARG_UNNAMED; if (*(paramWrapperPtr->paramPtr->name) == 'r') { paramWrapperPtr->paramPtr->flags |= NSF_ARG_IS_RETURNVALUE; } TclFreeIntRep(objPtr); objPtr->internalRep.twoPtrValue.ptr1 = (void *)paramWrapperPtr; objPtr->internalRep.twoPtrValue.ptr2 = NULL; objPtr->typePtr = ¶mObjType; } else { /* * In error cases, free manually memory allocated by this function. */ ParamsFree(paramWrapperPtr->paramPtr); FREE(NsfParamWrapper, paramWrapperPtr); } DECR_REF_COUNT(fullParamObj); return result; } static int ParamSetFromAny( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ register Tcl_Obj *objPtr) /* The object to convert. */ { assert(interp); assert(objPtr); return ParamSetFromAny2(interp, "value:", 0, objPtr); } /* *---------------------------------------------------------------------- * GetObjectParameterDefinition -- * * Obtain the parameter definitions for an object by calling the method * "__objectparameter" if the value is not cached already. Caching is * performed on the class, the cached values are used in case there are no * object-specific slots. * * Results: * Tcl return code, parsed structure in last argument * * Side effects: * Updates potentially cl->parsedParamPtr * *---------------------------------------------------------------------- */ static int ComputeParameterDefinition(Tcl_Interp *interp, Tcl_Obj *procNameObj, NsfObject *object, NsfClass *class, NsfParsedParam *parsedParamPtr) { int result = TCL_OK; Tcl_Obj *methodObj; NsfObject *self; if (object) { methodObj = NsfMethodObj(object, NSF_o_configureparameter_idx); self = object; } else { assert(class); self = &class->object; methodObj = NsfMethodObj(self, NSF_c_configureparameter_idx); } if (methodObj) { /*fprintf(stderr, "calling %s %s\n", ObjectName(self), ObjStr(methodObj));*/ result = CallMethod(self, interp, methodObj, 2, NULL, NSF_CM_IGNORE_PERMISSIONS|NSF_CSC_IMMEDIATE); if (likely(result == TCL_OK)) { Tcl_Obj *rawConfArgs = Tcl_GetObjResult(interp); /*fprintf(stderr, ".... rawConfArgs for %s => '%s'\n", ObjectName(self), ObjStr(rawConfArgs));*/ INCR_REF_COUNT(rawConfArgs); /* * Parse the string representation to obtain the internal * representation. */ result = ParamDefsParse(interp, procNameObj, rawConfArgs, NSF_DISALLOWED_ARG_OBJECT_PARAMETER, 1, parsedParamPtr); if (likely(result == TCL_OK)) { NsfParsedParam *ppDefPtr = NEW(NsfParsedParam); ppDefPtr->paramDefs = parsedParamPtr->paramDefs; ppDefPtr->possibleUnknowns = parsedParamPtr->possibleUnknowns; if (class) { assert(class->parsedParamPtr == NULL); class->parsedParamPtr = ppDefPtr; #if defined(PER_OBJECT_PARAMETER_CACHING) } else if (object) { NsfObjectOpt *opt = NsfRequireObjectOpt(object); if (object->opt->parsedParamPtr) { NsfParameterCacheObjectInvalidateCmd(interp, object); } opt->parsedParamPtr = ppDefPtr; opt->classParamPtrEpoch = RUNTIME_STATE(interp)->classParamPtrEpoch; /*fprintf(stderr, "set obj param for obj %p %s epoch %d ppDefPtr %p\n", object, ObjectName(object), opt->classParamPtrEpoch, ppDefPtr);*/ #endif } if (ppDefPtr->paramDefs) { ParamDefsRefCountIncr(ppDefPtr->paramDefs); } } DECR_REF_COUNT(rawConfArgs); } } return result; } static int GetObjectParameterDefinition(Tcl_Interp *interp, Tcl_Obj *procNameObj, NsfObject *object, NsfClass *class, NsfParsedParam *parsedParamPtr) { int result = TCL_OK; assert(interp); assert(procNameObj); assert(parsedParamPtr); parsedParamPtr->paramDefs = NULL; parsedParamPtr->possibleUnknowns = 0; if (class == NULL) { assert(object != NULL); if ((object->flags & NSF_HAS_PER_OBJECT_SLOTS) || (object->opt && object->opt->objMixins) ) { /* * We have object-specific parameters. Do not use the per-class cache, * and do not save the results in the per-class cache */ /*fprintf(stderr, "per-object configure obj %s flags %.6x\n", ObjectName(object), object->flags);*/ } else { class = object->cl; } } /* * Parameter definitions are cached in the class, for which * instances are created. The parameter definitions are flushed in * the following situations: * * a) on class cleanup: ParsedParamFree(cl->parsedParamPtr) * b) on class structure changes, * c) when class-mixins are added, * d) when new slots are defined, * e) when slots are removed * * When slot defaults or types are changed, the slots have to * perform a manual "::nsf::invalidateobjectparameter $domain". */ /* * Check, if there is already a parameter definition available for * creating objects of this class. */ if (likely(class && class->parsedParamPtr)) { NsfParsedParam *clParsedParamPtr = class->parsedParamPtr; parsedParamPtr->paramDefs = clParsedParamPtr->paramDefs; parsedParamPtr->possibleUnknowns = clParsedParamPtr->possibleUnknowns; result = TCL_OK; #if defined(PER_OBJECT_PARAMETER_CACHING) } else if (object && object->opt && object->opt->parsedParamPtr && object->opt->classParamPtrEpoch == RUNTIME_STATE(interp)->classParamPtrEpoch) { NsfParsedParam *objParsedParamPtr = object->opt->parsedParamPtr; /*fprintf(stderr, "reuse obj param for obj %p %s paramPtr %p\n", object, ObjectName(object), objParsedParamPtr);*/ parsedParamPtr->paramDefs = objParsedParamPtr->paramDefs; parsedParamPtr->possibleUnknowns = objParsedParamPtr->possibleUnknowns; result = TCL_OK; #endif } else { /* * There is no parameter definition available, get a new one in * the the string representation. */ result = ComputeParameterDefinition(interp, procNameObj, object, class, parsedParamPtr); } return result; } /* *---------------------------------------------------------------------- * ParameterCheck -- * * Check the provided valueObj against the parameter specification provided * in the second argument (paramObjPtr), when doCheckArguments is true. This * function is used e.g. by nsf::is, where only the right hand side of a * parameter specification (after the colon) is specified. The argument * Name (before the colon in a parameter spec) is provided via * argNamePrefix. The converted parameter structure is returned optionally * via the last argument. * * Results: * Tcl return code, parsed structure in last argument * * Side effects: * Converts potentially tcl_obj type of paramObjPtr * *---------------------------------------------------------------------- */ static int ParameterCheck(Tcl_Interp *interp, Tcl_Obj *paramObjPtr, Tcl_Obj *valueObj, const char *argNamePrefix, int doCheckArguments, int isNamed, int doConfigureParameter, Nsf_Param **paramPtrPtr) { Nsf_Param *paramPtr; NsfParamWrapper *paramWrapperPtr; Tcl_Obj *outObjPtr = NULL; ClientData checkedData; int result; unsigned int flags = 0; assert(interp); assert(paramObjPtr); assert(valueObj); /*fprintf(stderr, "ParameterCheck %s value %p %s\n", ObjStr(paramObjPtr), valueObj, ObjStr(valueObj));*/ if (paramObjPtr->typePtr == ¶mObjType) { paramWrapperPtr = (NsfParamWrapper *) paramObjPtr->internalRep.twoPtrValue.ptr1; } else { /* * We could use in principle Tcl_ConvertToType(..., ¶mObjType) instead * of checking the type manually, but we want to pass the argNamePrefix * explicitly. */ result = ParamSetFromAny2(interp, argNamePrefix, doConfigureParameter, paramObjPtr); if (likely(result == TCL_OK)) { paramWrapperPtr = (NsfParamWrapper *) paramObjPtr->internalRep.twoPtrValue.ptr1; } else { return NsfPrintError(interp, "invalid value constraints \"%s\"", ObjStr(paramObjPtr)); } } paramPtr = paramWrapperPtr->paramPtr; if (paramPtrPtr) *paramPtrPtr = paramPtr; if (isNamed) { paramPtr->flags &= ~NSF_ARG_UNNAMED; } result = ArgumentCheck(interp, valueObj, paramPtr, doCheckArguments, &flags, &checkedData, &outObjPtr); /*fprintf(stderr, "ParameterCheck paramPtr %p final refCount of wrapper %d can free %d flags %.6x\n", paramPtr, paramWrapperPtr->refCount, paramWrapperPtr->canFree, flags);*/ assert(paramWrapperPtr->refCount > 0); paramWrapperPtr->canFree = 1; if (flags & NSF_PC_MUST_DECR) { DECR_REF_COUNT2("valueObj", outObjPtr); } return result; } /*********************************************************************** * Begin Object Methods ***********************************************************************/ /* objectMethod autoname NsfOAutonameMethod { {-argName "-instance"} {-argName "-reset"} {-argName "name" -required 1 -type tclobj} } */ static int NsfOAutonameMethod(Tcl_Interp *interp, NsfObject *object, int withInstance, int withReset, Tcl_Obj *nameObj) { Tcl_Obj *autonamedObj; assert(interp); assert(object); assert(nameObj); autonamedObj = AutonameIncr(interp, nameObj, object, withInstance, withReset); if (autonamedObj) { Tcl_SetObjResult(interp, autonamedObj); DECR_REF_COUNT2("autoname", autonamedObj); return TCL_OK; } return NsfPrintError(interp, "autoname failed. Probably format string (with %%) was not well-formed"); } /* objectMethod class NsfOClassMethod { {-argName "class" -required 0 -type tclobj} } */ static int NsfOClassMethod(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *classObj) { assert(interp); assert(object); return NsfRelationSetCmd(interp, object, RelationtypeClassIdx, classObj); } /* objectMethod cleanup NsfOCleanupMethod { } */ static int NsfOCleanupMethod(Tcl_Interp *interp, NsfObject *object) { NsfClass *cl = NsfObjectToClass(object); Tcl_Obj *savedNameObj; int softrecreate; assert(interp); assert(object); #if defined(OBJDELETION_TRACE) fprintf(stderr, "+++ NsfOCleanupMethod\n"); #endif PRINTOBJ("NsfOCleanupMethod", object); savedNameObj = object->cmdName; INCR_REF_COUNT(savedNameObj); /* save and pass around softrecreate*/ softrecreate = object->flags & NSF_RECREATE && RUNTIME_STATE(interp)->doSoftrecreate; CleanupDestroyObject(interp, object, softrecreate); CleanupInitObject(interp, object, object->cl, object->nsPtr, softrecreate); if (cl) { CleanupDestroyClass(interp, cl, softrecreate, 1); CleanupInitClass(interp, cl, cl->nsPtr, softrecreate, 1); } DECR_REF_COUNT(savedNameObj); return TCL_OK; } /* objectMethod configure NsfOConfigureMethod { {-argName "args" -type allargs} } */ static NsfObject* GetSlotObject(Tcl_Interp *interp, Tcl_Obj *slotObj) nonnull(1) nonnull(2); static NsfObject* GetSlotObject(Tcl_Interp *interp, Tcl_Obj *slotObj) { NsfObject *slotObject = NULL; assert(interp); assert(slotObj); GetObjectFromObj(interp, slotObj, &slotObject); if (unlikely(slotObject == NULL)) { NsfPrintError(interp, "couldn't resolve slot object %s", ObjStr(slotObj)); } return slotObject; } static int NsfOConfigureMethod(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *objv0) { int result, i; NsfParsedParam parsedParam; Nsf_Param *paramPtr; NsfParamDefs *paramDefs; Tcl_Obj *newValue, *initMethodObj; CONST char *initString; ParseContext pc; CallFrame frame, *framePtr = &frame, *uplevelVarFramePtr; assert(interp); assert(object); assert(objv); #if 0 fprintf(stderr, "NsfOConfigureMethod %s.%s flags %.6x oc %2d", ObjectName(object), ObjStr(objv0), object->flags, objc); for(i = 0; i < objc; i++) {fprintf(stderr, " [%d]=%s,", i, ObjStr(objv[i]));} fprintf(stderr, "\n"); #endif /* Get the object parameter definition */ assert(objv0); result = GetObjectParameterDefinition(interp, objv0, object, NULL, &parsedParam); if (result != TCL_OK || parsedParam.paramDefs == NULL) { /*fprintf(stderr, "... nothing to do for method %s\n", ObjStr(objv0));*/ return result; } /* * Get the initMethodObj/initString outside the loop iterating over the * arguments. */ if (CallDirectly(interp, object, NSF_o_init_idx, &initMethodObj)) { initString = NULL; } else { initString = ObjStr(initMethodObj); } /* * The effective call site of the configure() method (e.g., a proc or a * method) can result from up-leveling the object creation procedure; * therefore, the *effective* call site can deviate from the *declaring* * call site (e.g. as in XOTcl2's unknown method). In such a scenario, the * configure() dispatch finds itself in a particular call-stack * configuration: The top-most frame reflects the declaring call site * (interp->framePtr), while the effective call site (interp->varFramePtr) * is identified by a lower call-stack level. * * Since configure pushes an object frame (for accessing the instance * variables) and sometimes a CMETHOD frame (for method invocations) we * record a) whether there was a preceding uplevel (identifiable through * deviating interp->framePtr and interp->varFramePtr) and, in case, b) the * ruling variable frame context. The preserved call-frame reference can * later be used to restore the uplevel'ed call frame context. */ uplevelVarFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp) != Tcl_Interp_framePtr(interp) ? Tcl_Interp_varFramePtr(interp) : NULL; /* * Push frame to allow for [self] and make instance variables of obj * accessible as locals. */ Nsf_PushFrameObj(interp, object, framePtr); /* Process the actual arguments based on the parameter definitions */ paramDefs = parsedParam.paramDefs; ParamDefsRefCountIncr(paramDefs); result = ProcessMethodArguments(&pc, interp, object, NSF_ARGPARSE_START_ZERO, paramDefs, NsfGlobalObjs[NSF_CONFIGURE], objc, objv); if (unlikely(result != TCL_OK)) { Nsf_PopFrameObj(interp, framePtr); goto configure_exit; } /* * At this point, the arguments are tested to be valid (according to the * parameter definitions) and the defaults are set. Now we have to apply the * arguments (mostly setting instance variables). */ #if defined(CONFIGURE_ARGS_TRACE) fprintf(stderr, "*** POPULATE OBJ '%s': nr of parsed args %d\n", ObjectName(object), pc.objc); #endif for (i = 1, paramPtr = paramDefs->paramsPtr; paramPtr->name; paramPtr++, i++) { /* * Set the new value always when the new value was specified (was not * taken from the default). When we take the default, we do not overwrite * already existing values (which might have been set via parameter * alias). */ /*fprintf(stderr, "[%d] param %s, object init called %d is default %d value = '%s' nrArgs %d\n", i, paramPtr->name, (object->flags & NSF_INIT_CALLED), (pc.flags[i-1] & NSF_PC_IS_DEFAULT), ObjStr(pc.full_objv[i]), paramPtr->nrArgs);*/ if ((pc.flags[i-1] & NSF_PC_IS_DEFAULT)) { /* * Object parameter method calls (when the flag * NSF_ARG_METHOD_INVOCATION is set) do not set instance variables, so * we do not have to check for existing variables. */ if ((paramPtr->flags & NSF_ARG_METHOD_INVOCATION) == 0) { Tcl_Obj *varObj = Tcl_ObjGetVar2(interp, paramPtr->nameObj, NULL, TCL_PARSE_PART1); if (varObj) { /* * The value exists already, ignore this parameter. */ /*fprintf(stderr, "a variable for %s exists already, " "ignore param flags %.6x valueObj %p\n", paramPtr->name, paramPtr->flags, pc.full_objv[i]);*/ continue; } } else if (object->flags & NSF_INIT_CALLED) { /* * The object is already initialized. Don't use the default, since it * might change part of the state back to the original default. This * might happen, when e.g. configure is called on a class manually, * where "superclass" has a default. */ /*fprintf(stderr, "%s skip default %s in configure\n", ObjectName(object), ObjStr(pc.full_objv[i]));*/ continue; } } else if (unlikely(paramPtr->flags & NSF_ARG_REQUIRED && pc.full_objv[i] == NsfGlobalObjs[NSF___UNKNOWN__])) { /* Previous versions contained a test for * (object->flags & NSF_INIT_CALLED) * * to perform required testing just for in the non-initialized state. We * switched in 2.0b5 to checking for the existance of the associated * instance variable, which works under the assumption that the instance * variable has the same name and that e.g. an required alias parameter * sets this variable either. Similar assumption is in the default * handling. Future versions might use a more general handling of the * parameter states. */ Tcl_Obj *varObj = Tcl_ObjGetVar2(interp, paramPtr->nameObj, NULL, TCL_PARSE_PART1); if (varObj == NULL) { Tcl_Obj *paramDefsObj = NsfParamDefsSyntax(interp, paramDefs->paramsPtr, object, NULL); NsfPrintError(interp, "required argument '%s' is missing, should be:\n\t%s%s%s %s", paramPtr->nameObj ? ObjStr(paramPtr->nameObj) : paramPtr->name, pc.object ? ObjectName(pc.object) : "", pc.object ? " " : "", ObjStr(pc.full_objv[0]), ObjStr(paramDefsObj)); DECR_REF_COUNT2("paramDefsObj", paramDefsObj); Nsf_PopFrameObj(interp, framePtr); result = TCL_ERROR; goto configure_exit; } } newValue = pc.full_objv[i]; /*fprintf(stderr, " new Value of %s = [%d] %p '%s', type %s addr %p\n", ObjStr(paramPtr->nameObj), i, newValue, newValue ? ObjStr(newValue) : "(null)", paramPtr->type, &(pc.full_objv[i]));*/ /* * Handling slot initialize */ if (paramPtr->flags & NSF_ARG_SLOTINITIALIZE) { NsfObject *slotObject = GetSlotObject(interp, paramPtr->slotObj); if (likely(slotObject != NULL)) { Tcl_Obj *ov[1]; ov[0] = paramPtr->nameObj; result = NsfCallMethodWithArgs(interp, (Nsf_Object *)slotObject, NsfGlobalObjs[NSF_INITIALIZE], object->cmdName, 2, ov, NSF_CSC_IMMEDIATE|NSF_CM_IGNORE_PERMISSIONS); } if (unlikely(result != TCL_OK)) { /* * The error message was set either by GetSlotObject or by ...CallMethod... */ Nsf_PopFrameObj(interp, framePtr); goto configure_exit; } } /* * Special setter methods for invoking methods calls; handles types * "cmd", "initcmd", "alias" and "forward". */ if ((paramPtr->flags & NSF_ARG_METHOD_INVOCATION) ) { int consuming = (*paramPtr->name == '-' || paramPtr->nrArgs > 0); if (consuming && newValue == NsfGlobalObjs[NSF___UNKNOWN__]) { /* * In the case we have a consuming parameter, but we have no value * provided and not default, there is no reason to call the invocation * parameter. */ /*fprintf(stderr, "%s consuming nrargs %d no value\n", paramPtr->name, paramPtr->nrArgs);*/ continue; } if ((paramPtr->flags & NSF_ARG_INITCMD)) { if (paramPtr->defaultValue) { /* * The "defaultValue" holds the initcmd to be executed */ Tcl_Obj *varObj = Tcl_ObjGetVar2(interp, NsfGlobalObjs[NSF_ARRAY_INITCMD], paramPtr->nameObj, 0); if (varObj == NULL) { /* * The variable is not set. Therefore, we assume, we have to * execute the initcmd. On success, we note the execution in the NSF_ARRAY_INITCMD * variable (usually __initcmd(name)) */ result = ParameterMethodDispatch(interp, object, paramPtr, paramPtr->defaultValue, uplevelVarFramePtr, initString, (Tcl_Obj **)&objv[pc.lastObjc], objc - pc.lastObjc); if (unlikely(result != TCL_OK)) { Nsf_PopFrameObj(interp, framePtr); goto configure_exit; } Tcl_ObjSetVar2(interp, NsfGlobalObjs[NSF_ARRAY_INITCMD], paramPtr->nameObj, Tcl_NewIntObj(1), 0); } } else { /* * we could consider to require a default */ } /* * if we have a new actual value, proceed to setvars */ if ((pc.flags[i-1] & NSF_PC_IS_DEFAULT) == 0) { goto setvars; } continue; } /* * lastObjc points to the first "unprocessed" argument, the argument before should be valid, when lastObjc > 1 */ if (pc.lastObjc > 1) { assert(ISOBJ(objv[pc.lastObjc-1])); } result = ParameterMethodDispatch(interp, object, paramPtr, newValue, uplevelVarFramePtr, initString, (Tcl_Obj **)&objv[pc.lastObjc], objc - pc.lastObjc); if (unlikely(result != TCL_OK)) { Nsf_PopFrameObj(interp, framePtr); goto configure_exit; } continue; } setvars: if (newValue == NsfGlobalObjs[NSF___UNKNOWN__]) { /* * Nothing to do, we have a value setter, but no value is specified and * no default was provided. */ continue; } /* * Set the instance variable unless the last argument of the * definition is varArgs. */ if (i < paramDefs->nrParams || pc.varArgs == 0) { #if defined(CONFIGURE_ARGS_TRACE) fprintf(stderr, "*** %s SET %s '%s' // %p\n", ObjectName(object), ObjStr(paramPtr->nameObj), ObjStr(newValue), paramPtr->slotObj); #endif /* * Actually set instance variable with the provided value or default * value. In case, explicit invocation of the setter is needed, we call the method, which * is typically a forwarder to the slot object. */ if (paramPtr->flags & NSF_ARG_SLOTSET) { NsfObject *slotObject = GetSlotObject(interp, paramPtr->slotObj); if (likely(slotObject != NULL)) { Tcl_Obj *ov[2]; Tcl_Obj *methodObj = NsfMethodObj(object, NSF_s_set_idx); ov[0] = paramPtr->method ? paramPtr->method : paramPtr->nameObj; ov[1] = newValue; /*fprintf(stderr, "SLOTSET %s %s %s %s %s idx %d %p\n", ObjectName(slotObject), ObjStr(NsfGlobalObjs[NSF_SET]), ObjStr(object->cmdName), ObjStr(paramPtr->nameObj), ObjStr(newValue), NSF_s_set_idx, methodObj);*/ result = NsfCallMethodWithArgs(interp, (Nsf_Object *)slotObject, methodObj ? methodObj : NsfGlobalObjs[NSF_SLOT_SET], object->cmdName, 3, ov, NSF_CSC_IMMEDIATE); } if (unlikely(result != TCL_OK)) { /* * The error message was set either by GetSlotObject or by ...CallMethod... */ Nsf_PopFrameObj(interp, framePtr); goto configure_exit; } } else { Tcl_ObjSetVar2(interp, paramPtr->nameObj, NULL, newValue, TCL_LEAVE_ERR_MSG|TCL_PARSE_PART1); } } } Nsf_PopFrameObj(interp, framePtr); configure_exit: ParamDefsRefCountDecr(paramDefs); ParseContextRelease(&pc); if (likely(result == TCL_OK)) { Tcl_ResetResult(interp); } return result; } /* objectMethod cget NsfOCgetMethod { {-argName "name" -type tclobj -required 1} } */ static int NsfOCgetMethod(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *nameObj) { int result, found = 0; NsfParsedParam parsedParam; Nsf_Param CONST *paramPtr; NsfParamDefs *paramDefs; CallFrame frame, *framePtr = &frame, *uplevelVarFramePtr; char *nameString = ObjStr(nameObj); assert(interp); assert(object); assert(nameObj); /* * Get the object parameter definition */ result = GetObjectParameterDefinition(interp, NsfGlobalObjs[NSF_EMPTY], object, NULL, &parsedParam); if (unlikely(result != TCL_OK)) { return result; } /* * GetObjectParameterDefinition() was returning TCL_OK, the paramdefs have * to be set. */ assert(parsedParam.paramDefs); /* * We do not stack a plain stack fraom NSF_CSC_TYPE_PLAIN here, as we do in * NsfOConfigureMethod (but maybe we have to for full compatibility TODO: * check and compar with configure stack setup). Therefore we pass NULL as * cscPtr to ParameterMethodForwardDispatch). */ /* * The uplevel handling is exactly the same as in NsfOConfigureMethod() and * is needed, when methods are called, which perform an upvar. */ uplevelVarFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp) != Tcl_Interp_framePtr(interp) ? Tcl_Interp_varFramePtr(interp) : NULL; /* * Push frame to allow invocations of [self] and make instance variables of * obj accessible as locals. */ Nsf_PushFrameObj(interp, object, framePtr); paramDefs = parsedParam.paramDefs; ParamDefsRefCountIncr(paramDefs); /* * Does provided value start with a dash? */ if (*nameString == '-') { /* * Skip leading parameters from the definition, which are no nonpos args * (very unlikely). */ for (paramPtr = paramDefs->paramsPtr; paramPtr->name && *paramPtr->name != '-'; paramPtr++); /* * Perform the lookup from the next group. */ if (unlikely(NsfParamDefsNonposLookup(interp, nameString, paramPtr, ¶mPtr) != TCL_OK)) { result = TCL_ERROR; goto cget_exit; } else { found = (paramPtr != NULL); } } if (!found) { result = NsfPrintError(interp, "cget: unknown configure parameter %s", nameString); goto cget_exit; } /*fprintf(stderr, "arg %s found, flags %.8x\n", nameString, paramPtr->flags);*/ /* * Check for slot invocation */ if (paramPtr->slotObj) { NsfObject *slotObject = GetSlotObject(interp, paramPtr->slotObj); Tcl_Obj *methodObj = NsfMethodObj(object, NSF_s_get_idx); Tcl_Obj *ov[1]; /* * Get instance variable via slot. */ if (uplevelVarFramePtr) { Tcl_Interp_varFramePtr(interp) = uplevelVarFramePtr; } ov[0] = paramPtr->method ? paramPtr->method : paramPtr->nameObj; /*fprintf(stderr, "SLOTGET %s idx %d %p\n", ObjectName(slotObject), NSF_s_get_idx, methodObj);*/ result = NsfCallMethodWithArgs(interp, (Nsf_Object *)slotObject, methodObj ? methodObj : NsfGlobalObjs[NSF_SLOT_GET], object->cmdName, 2, ov, NSF_CSC_IMMEDIATE); goto cget_exit; } /* * We do NOT have a slot */ if (found && paramPtr->flags & NSF_ARG_METHOD_CALL) { if (paramPtr->flags & NSF_ARG_ALIAS) { /* * It is a parameter associated with an aliased method. Invoke the * method without an argument. */ Tcl_Obj *methodObj = paramPtr->method ? paramPtr->method : paramPtr->nameObj; if (uplevelVarFramePtr) { Tcl_Interp_varFramePtr(interp) = uplevelVarFramePtr; } result = CallMethod(object, interp, methodObj, 2, NULL, NSF_CSC_IMMEDIATE); } else { /* * Must be NSF_ARG_FORWARD */ assert(paramPtr->flags & NSF_ARG_FORWARD); /* since we have no cscPtr, we provide NULL */ result = ParameterMethodForwardDispatch(interp, object, paramPtr, NULL, NULL /* cscPtr */); } } else { /* * Must be a parameter associated with a variable */ unsigned int flags = (object->nsPtr) ? TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY : TCL_LEAVE_ERR_MSG; Tcl_Obj *resultObj = Tcl_ObjGetVar2(interp, paramPtr->nameObj, NULL, flags); if (resultObj) { /* * The value exists */ Tcl_SetObjResult(interp, resultObj); } } cget_exit: Nsf_PopFrameObj(interp, framePtr); ParamDefsRefCountDecr(paramDefs); return result; } /* objectMethod destroy NsfODestroyMethod { } */ static int NsfODestroyMethod(Tcl_Interp *interp, NsfObject *object) { PRINTOBJ("NsfODestroyMethod", object); assert(interp); assert(object); /* * Provide protection against destroy on base classes. */ if (unlikely(IsBaseClass(object))) { if (RUNTIME_STATE(interp)->exitHandlerDestroyRound != NSF_EXITHANDLER_ON_SOFT_DESTROY) { return NsfPrintError(interp, "cannot destroy base class %s", ObjectName(object)); } } /*fprintf(stderr,"NsfODestroyMethod %p %s flags %.6x activation %d cmd %p cmd->flags %.6x\n", object, ((Command *)object->id)->flags == 0 ? ObjectName(object) : "(deleted)", object->flags, object->activationCount, object->id, ((Command *)object->id)->flags);*/ /* * NSF_DESTROY_CALLED might be set already be DispatchDestroyMethod(), * the implicit destroy calls. It is necessary to set it here for * the explicit destroy calls in the script, which reach the * Object->destroy. */ if ((object->flags & NSF_DESTROY_CALLED) == 0) { object->flags |= NSF_DESTROY_CALLED; /*fprintf(stderr, "NsfODestroyMethod %p sets DESTROY_CALLED %.6x\n", object, object->flags);*/ } object->flags |= NSF_DESTROY_CALLED_SUCCESS; if (likely((object->flags & NSF_DURING_DELETE) == 0)) { int result; Tcl_Obj *methodObj; /*fprintf(stderr, " call dealloc on %p %s\n", object, ((Command *)object->id)->flags == 0 ? ObjectName(object) : "(deleted)");*/ if (CallDirectly(interp, &object->cl->object, NSF_c_dealloc_idx, &methodObj)) { result = DoDealloc(interp, object); } else { result = NsfCallMethodWithArgs(interp, (Nsf_Object *)object->cl, methodObj, object->cmdName, 1, NULL, NSF_CSC_IMMEDIATE|NSF_CM_IGNORE_PERMISSIONS); if (unlikely(result != TCL_OK)) { /* * In case, the call of the dealloc method has failed above (e.g. NS_DYING), * we have to call dealloc manually, otherwise we have a memory leak */ /*fprintf(stderr, "*** dealloc failed for %p %s flags %.6x, retry\n", object, ObjectName(object), object->flags);*/ result = DoDealloc(interp, object); } } return result; } else { #if defined(OBJDELETION_TRACE) fprintf(stderr, " Object->destroy already during delete, don't call dealloc %p\n", object); #endif } return TCL_OK; } /* objectMethod exists NsfOExistsMethod { {-argName "varName" -required 1} } */ static int NsfOExistsMethod(Tcl_Interp *interp, NsfObject *object, CONST char *var) { assert(interp); assert(object); assert(var); Tcl_SetIntObj(Tcl_GetObjResult(interp), VarExists(interp, object, var, NULL, NSF_VAR_TRIGGER_TRACE|NSF_VAR_REQUIRE_DEFINED)); return TCL_OK; } /* objectMethod filterguard NsfOFilterGuardMethod { {-argName "filter" -required 1} {-argName "guard" -required 1 -type tclobj} } */ static int NsfOFilterGuardMethod(Tcl_Interp *interp, NsfObject *object, CONST char *filter, Tcl_Obj *guardObj) { NsfObjectOpt *opt = object->opt; assert(interp); assert(object); assert(filter); assert(guardObj); if (opt && opt->objFilters) { NsfCmdList *h = CmdListFindNameInList(interp, filter, opt->objFilters); if (h) { if (h->clientData) { GuardDel((NsfCmdList *) h); } GuardAdd(h, guardObj); object->flags &= ~NSF_FILTER_ORDER_VALID; return TCL_OK; } } return NsfPrintError(interp, "filterguard: can't find filter %s on %s", filter, ObjectName(object)); } /* objectMethod instvar NsfOInstvarMethod { {-argName "args" -type allargs} } */ static int NsfOInstvarMethod(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[]) { callFrameContext ctx = {0, NULL, NULL}; int result; assert(interp); assert(object); if (object->filterStack || object->mixinStack) { CallStackUseActiveFrame(interp, &ctx); } if (unlikely(Tcl_Interp_varFramePtr(interp) == NULL)) { CallStackRestoreSavedFrames(interp, &ctx); return NsfPrintError(interp, "instvar used on %s, but call-stack is not in procedure scope", ObjectName(object)); } result = NsfVarImport(interp, object, ObjStr(objv[0]), objc-1, objv+1); CallStackRestoreSavedFrames(interp, &ctx); return result; } /* objectMethod mixinguard NsfOMixinGuardMethod { {-argName "mixin" -required 1 -type tclobj} {-argName "guard" -required 1 -type tclobj} } */ static int NsfOMixinGuardMethod(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *mixin, Tcl_Obj *guardObj) { NsfObjectOpt *opt = object->opt; assert(interp); assert(object); assert(mixin); assert(guardObj); if (opt && opt->objMixins) { Tcl_Command mixinCmd = Tcl_GetCommandFromObj(interp, mixin); if (mixinCmd) { NsfClass *mixinCl = NsfGetClassFromCmdPtr(mixinCmd); if (mixinCl) { NsfCmdList *h = CmdListFindCmdInList(mixinCmd, opt->objMixins); if (h) { if (h->clientData) { GuardDel((NsfCmdList *) h); } GuardAdd(h, guardObj); object->flags &= ~NSF_MIXIN_ORDER_VALID; return TCL_OK; } } } } return NsfPrintError(interp, "mixinguard: can't find mixin %s on %s", ObjStr(mixin), ObjectName(object)); } /* objectMethod noinit NsfONoinitMethod { } */ static int NsfONoinitMethod(Tcl_Interp *UNUSED(interp), NsfObject *object) { assert(object); object->flags |= NSF_INIT_CALLED; return TCL_OK; } /* objectMethod requirenamespace NsfORequireNamespaceMethod { } */ static int NsfORequireNamespaceMethod(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); RequireObjNamespace(interp, object); return TCL_OK; } /* objectMethod residualargs NsfOResidualargsMethod { {-argName "args" -type allargs} } */ static int NsfOResidualargsMethod(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[]) { int i, start = 1, argc, nextArgc, normalArgs, result = TCL_OK, isdasharg = NO_DASH; CONST char *methodName, *nextMethodName, *initString = NULL; Tcl_Obj **argv, **nextArgv; assert(interp); assert(object); #if 0 fprintf(stderr, "NsfOResidualargsMethod %s %2d ", ObjectName(object), objc); for(i = 0; i < objc; i++) {fprintf(stderr, " [%d]=%p %s,", i, &objv[i], ObjStr(objv[i]));} fprintf(stderr, "\n"); #endif /* skip arguments without leading dash */ for (i = start; i < objc; i++) { if ((isdasharg = IsDashArg(interp, objv[i], 1, &methodName, &argc, &argv))) { break; } } normalArgs = i-1; /* * Get the init string; do it once, outside the loop. */ if (i < objc) { NsfObjectSystem *osPtr = GetObjectSystem(object); Tcl_Obj *initObj = osPtr->methods[NSF_o_init_idx]; if (initObj) { initString = ObjStr(initObj); } } for( ; i < objc; argc = nextArgc, argv = nextArgv, methodName = nextMethodName) { Tcl_ResetResult(interp); switch (isdasharg) { case SKALAR_DASH: /* Argument is a skalar with a leading dash */ { int j; nextMethodName = NULL; nextArgv = NULL; nextArgc = 0; for (j = i+1; j < objc; j++, argc++) { if ((isdasharg = IsDashArg(interp, objv[j], 1, &nextMethodName, &nextArgc, &nextArgv))) { break; } } result = CallConfigureMethod(interp, object, initString, methodName, argc+1, objv+i+1); if (unlikely(result != TCL_OK)) { return result; } i += argc; break; } case LIST_DASH: /* Argument is a list with a leading dash, grouping determined by list */ { i++; nextMethodName = NULL; if (i < objc) { isdasharg = IsDashArg(interp, objv[i], 1, &nextMethodName, &nextArgc, &nextArgv); } else { nextMethodName = NULL; nextArgv = NULL; nextArgc = 0; } result = CallConfigureMethod(interp, object, initString, methodName, argc+1, argv+1); if (unlikely(result != TCL_OK)) { return result; } break; } default: { return NsfPrintError(interp, "%s configure: unexpected argument '%s' between parameters", ObjectName(object), ObjStr(objv[i])); } } } /* * Call init with residual args in case it was not called yet. */ result = DispatchInitMethod(interp, object, normalArgs, objv+1, 0); if (likely(result == TCL_OK)) { /* * Return the non-processed leading arguments unless there was an error * (XOTcl convention) */ Tcl_SetObjResult(interp, Tcl_NewListObj(normalArgs, objv+1)); } return result; } /* objectMethod uplevel NsfOUplevelMethod { {-argName "args" -type allargs} } */ static int NsfOUplevelMethod(Tcl_Interp *interp, NsfObject *UNUSED(object), int objc, Tcl_Obj *CONST objv[]) { int i, result = TCL_ERROR; Tcl_CallFrame *framePtr = NULL, *savedVarFramePtr; assert(interp); assert(objv); /* * Find the level to use for executing the command. */ if (objc>2) { CallFrame *cf; CONST char *frameInfo = ObjStr(objv[1]); result = TclGetFrame(interp, frameInfo, &cf); if (result == -1) { return TCL_ERROR; } framePtr = (Tcl_CallFrame *)cf; i = result+1; } else { i = 1; } objc -= i; objv += i; if (framePtr == NULL) { NsfCallStackFindLastInvocation(interp, 1, &framePtr); if (framePtr == NULL) { framePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp)->callerVarPtr; if (framePtr == NULL) { framePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); } } } savedVarFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); Tcl_Interp_varFramePtr(interp) = (CallFrame *)framePtr; /* * Execute the residual arguments as a command. */ if (objc == 1) { result = Tcl_EvalObjEx(interp, objv[0], TCL_EVAL_DIRECT); } else { /* * More than one argument: concatenate them together with spaces * between, then evaluate the result. Tcl_EvalObjEx will delete * the object when it decrements its refCount after eval'ing it. */ Tcl_Obj *objPtr = Tcl_ConcatObj(objc, objv); result = Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_DIRECT); } if (unlikely(result == TCL_ERROR)) { Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf("\n (\"uplevel\" body line %d)", Tcl_GetErrorLine(interp))); } /* * Restore the variable frame, and return. */ Tcl_Interp_varFramePtr(interp) = (CallFrame *)savedVarFramePtr; return result; } /* objectMethod upvar NsfOUpvarMethod { {-argName "args" -type allargs} } */ static int NsfOUpvarMethod(Tcl_Interp *interp, NsfObject *object, int objc, Tcl_Obj *CONST objv[]) { Tcl_Obj *frameInfoObj = NULL; int i, result = TCL_ERROR; CONST char *frameInfo; callFrameContext ctx = {0, NULL, NULL}; assert(interp); assert(object); if (objc % 2 == 0) { frameInfo = ObjStr(objv[1]); i = 2; } else { frameInfoObj = ComputeLevelObj(interp, CALLING_LEVEL); INCR_REF_COUNT(frameInfoObj); frameInfo = ObjStr(frameInfoObj); i = 1; } if (object && (object->filterStack || object->mixinStack)) { CallStackUseActiveFrame(interp, &ctx); } for ( ; i < objc; i += 2) { result = Tcl_UpVar2(interp, frameInfo, ObjStr(objv[i]), NULL, ObjStr(objv[i+1]), 0 /*flags*/); if (unlikely(result != TCL_OK)) { break; } } if (frameInfoObj) { DECR_REF_COUNT(frameInfoObj); } CallStackRestoreSavedFrames(interp, &ctx); return result; } /* objectMethod volatile NsfOVolatileMethod { } */ static int NsfOVolatileMethod(Tcl_Interp *interp, NsfObject *object) { Tcl_Obj *objPtr = object->cmdName; int result = TCL_ERROR; CONST char *fullName = ObjStr(objPtr); CONST char *vn; callFrameContext ctx = {0, NULL, NULL}; assert(interp); assert(object); if (unlikely(RUNTIME_STATE(interp)->exitHandlerDestroyRound != NSF_EXITHANDLER_OFF)) { return NsfPrintError(interp, "can't make objects volatile during shutdown"); } CallStackUseActiveFrame(interp, &ctx); vn = NSTail(fullName); if (Tcl_SetVar2(interp, vn, NULL, fullName, 0)) { NsfObjectOpt *opt = NsfRequireObjectOpt(object); /*fprintf(stderr, "### setting trace for %s on frame %p\n", fullName, Tcl_Interp_varFramePtr(interp)); NsfShowStack(interp);*/ result = Tcl_TraceVar(interp, vn, TCL_TRACE_UNSETS, (Tcl_VarTraceProc *)NsfUnsetTrace, objPtr); opt->volatileVarName = vn; } CallStackRestoreSavedFrames(interp, &ctx); if (likely(result == TCL_OK)) { INCR_REF_COUNT(objPtr); } return result; } /*********************************************************************** * End Object Methods ***********************************************************************/ /*********************************************************************** * Begin Class Methods ***********************************************************************/ static int NsfCAllocMethod_(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *nameObj, Tcl_Namespace *parentNsPtr) { CONST char *nameString = ObjStr(nameObj); NsfObject *newObj; assert(interp); assert(cl); assert(nameObj); assert(isAbsolutePath(nameString)); assert(NSValidObjectName(nameString, 0)); /* * Create a new object from scratch. */ if (IsMetaClass(interp, cl, 1) == 0) { /* * If the base class is an ordinary class, we create an object. */ newObj = PrimitiveOCreate(interp, nameObj, parentNsPtr, cl); } else { /* * If the base class is a meta-class, we create a class. */ newObj = (NsfObject *)PrimitiveCCreate(interp, nameObj, parentNsPtr, cl); } if (unlikely(newObj == NULL)) { return NsfPrintError(interp, "alloc failed to create '%s' " "(possibly parent namespace does not exist)", nameString); } if (NSF_DTRACE_OBJECT_ALLOC_ENABLED()) { NSF_DTRACE_OBJECT_ALLOC(ObjectName(newObj), ClassName(cl)); } Tcl_SetObjResult(interp, nameObj); return TCL_OK; } /* classMethod alloc NsfCAllocMethod { {-argName "name" -required 1 -type tclobj} } */ static int NsfCAllocMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *nameObj) { CONST char *nameString = ObjStr(nameObj); Tcl_Namespace *parentNsPtr; Tcl_Obj *tmpName; int result; assert(interp); assert(cl); assert(nameObj); /* * Create a new object from scratch. */ /* * Check for illegal names. */ if (unlikely(NSValidObjectName(nameString, 0) == 0)) { return NsfPrintError(interp, "cannot allocate object - illegal name '%s'", nameString); } /* * If the path is not absolute, we add the appropriate namespace. */ if (isAbsolutePath(nameString)) { tmpName = NULL; parentNsPtr = NULL; } else { parentNsPtr = CallingNameSpace(interp); nameObj = tmpName = NameInNamespaceObj(nameString, parentNsPtr); if (strchr(nameString, ':')) { parentNsPtr = NULL; } INCR_REF_COUNT(tmpName); /*fprintf(stderr, " **** NoAbsoluteName for '%s' -> determined = '%s' parentNs %s\n", nameString, ObjStr(tmpName), parentNsPtr->fullName);*/ } result = NsfCAllocMethod_(interp, cl, nameObj, parentNsPtr); if (tmpName) { DECR_REF_COUNT(tmpName); } return result; } /* classMethod create NsfCCreateMethod { {-argName "name" -required 1} {-argName "args" -type allargs} } */ static int NsfCCreateMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *specifiedNameObj, int objc, Tcl_Obj *CONST objv[]) { NsfObject *newObject = NULL; Tcl_Obj *nameObj, *methodObj, *tmpObj = NULL; int result; CONST char *nameString = ObjStr(specifiedNameObj); Tcl_Namespace *parentNsPtr; assert(interp); assert(cl); assert(specifiedNameObj); assert(objv); #if 0 { int i; fprintf(stderr, "NsfCCreateMethod %s create <%s> oc %d ", ClassName(cl), ObjStr(specifiedNameObj), objc); for(i = 0; i < objc; i++) {fprintf(stderr, " [%d]=%s,", i, ObjStr(objv[i]));} fprintf(stderr, "\n"); } #endif if (unlikely(RUNTIME_STATE(interp)->exitHandlerDestroyRound != NSF_EXITHANDLER_OFF)) { fprintf(stderr, "### Can't create object %s during shutdown\n", ObjStr(objv[1])); return TCL_OK; /* don't fail, if this happens during destroy, it might be canceled */ } /* * Check for illegal names. */ if (unlikely(NSValidObjectName(nameString, 0) == 0)) { result = NsfPrintError(interp, "cannot allocate object - illegal name '%s'", nameString); goto create_method_exit; } /*fprintf(stderr, "NsfCCreateMethod specifiedName %s\n", nameString);*/ /* * Complete the name if it is not absolute. */ if (!isAbsolutePath(nameString)) { parentNsPtr = CallingNameSpace(interp); tmpObj = NameInNamespaceObj(nameString, parentNsPtr); /* * If the name contains colons, the parentNsPtr is not appropriate * for determining the parent. */ if (strchr(nameString, ':')) { parentNsPtr = NULL; } nameString = ObjStr(tmpObj); /* fprintf(stderr, " **** fixed name is '%s'\n", nameString); */ INCR_REF_COUNT(tmpObj); nameObj = tmpObj; } else { parentNsPtr = NULL; nameObj = specifiedNameObj; /* fprintf(stderr, " **** used specified name is '%s'\n", nameString); */ } /* * Check whether we have to call recreate (i.e. when the object exists * already). First check, if was have such a command, then check, if the * command is an object. */ { Tcl_Command cmd = NSFindCommand(interp, nameString); if (cmd) { newObject = NsfGetObjectFromCmdPtr(cmd); if (newObject == NULL) { /* * We have a cmd, but no object. Don't allow to overwrite an ordinary * cmd by an nsf object. */ result = NsfPrintError(interp, "refuse to overwrite cmd %s; delete/rename it before overwriting", nameString); goto create_method_exit; } } } /*fprintf(stderr, "+++ createspecifiedName '%s', nameString '%s', newObject=%p ismeta(%s) %d, ismeta(%s) %d\n", ObjStr(specifiedNameObj), nameString, newObject, ClassName(cl), IsMetaClass(interp, cl, 1), newObject ? ClassName(newObject->cl) : "NULL", newObject ? IsMetaClass(interp, newObject->cl, 1) : 0 );*/ /* * Provide protection against recreation if base classes. */ if (unlikely(newObject && unlikely(IsBaseClass(newObject)))) { result = NsfPrintError(interp, "cannot recreate base class %s", ObjectName(newObject)); goto create_method_exit; } /* * Don't allow to * - recreate an object as a class, * - recreate a class as an object, and to * - recreate an object in a different object system * * In these clases, we use destroy followed by create instead of recreate. */ if (newObject && (IsMetaClass(interp, cl, 1) == IsMetaClass(interp, newObject->cl, 1)) && GetObjectSystem(newObject) == cl->osPtr) { /*fprintf(stderr, "%%%% recreate, call recreate method ... %s, objc=%d oldOs %p != newOs %p EQ %d\n", ObjStr(nameObj), objc+1, GetObjectSystem(newObject), cl->osPtr, GetObjectSystem(newObject) != cl->osPtr );*/ /* call recreate --> initialization */ if (CallDirectly(interp, &cl->object, NSF_c_recreate_idx, &methodObj)) { result = RecreateObject(interp, cl, newObject, objc, objv); } else { ALLOC_ON_STACK(Tcl_Obj*, objc+3, xov); xov[0] = NULL; /* just a placeholder for passing conventions in ObjectDispatch() */ xov[1] = methodObj; xov[2] = nameObj; if (objc >= 1) { memcpy(xov+3, objv, sizeof(Tcl_Obj *)*objc); } result = ObjectDispatch(cl, interp, objc+3, xov, NSF_CM_IGNORE_PERMISSIONS|NSF_CSC_IMMEDIATE); FREE_ON_STACK(Tcl_Obj *, xov); } if (unlikely(result != TCL_OK)) { goto create_method_exit; } Tcl_SetObjResult(interp, newObject->cmdName); nameObj = newObject->cmdName; ObjTrace("RECREATE", newObject); } else { /* * newObject might exist here, but will be automatically destroyed by * alloc. */ if (CallDirectly(interp, &cl->object, NSF_c_alloc_idx, &methodObj)) { result = NsfCAllocMethod_(interp, cl, nameObj, parentNsPtr); } else { result = CallMethod(cl, interp, methodObj, 3, &nameObj, NSF_CSC_IMMEDIATE); } if (unlikely(result != TCL_OK)) { goto create_method_exit; } nameObj = Tcl_GetObjResult(interp); if (unlikely(GetObjectFromObj(interp, nameObj, &newObject) != TCL_OK)) { result = NsfPrintError(interp, "couldn't find result of alloc"); goto create_method_exit; } ObjTrace("CREATE", newObject); /* * In case, the object is destroyed during initialization, we incr * refCount. */ INCR_REF_COUNT(nameObj); result = DoObjInitialization(interp, newObject, objc, objv); DECR_REF_COUNT(nameObj); } create_method_exit: if (tmpObj) {DECR_REF_COUNT(tmpObj);} return result; } /* classMethod dealloc NsfCDeallocMethod { {-argName "object" -required 1 -type tclobj} } */ static int NsfCDeallocMethod(Tcl_Interp *interp, NsfClass *UNUSED(cl), Tcl_Obj *obj) { NsfObject *object; assert(interp); if (GetObjectFromObj(interp, obj, &object) != TCL_OK) { return NsfPrintError(interp, "can't destroy object %s that does not exist", ObjStr(obj)); } return DoDealloc(interp, object); } /* classMethod filterguard NsfCFilterGuardMethod { {-argName "filter" -required 1} {-argName "guard" -required 1 -type tclobj} } */ static int NsfCFilterGuardMethod(Tcl_Interp *interp, NsfClass *cl, CONST char *filter, Tcl_Obj *guardObj) { NsfClassOpt *opt = cl->opt; assert(interp); assert(cl); assert(filter); assert(guardObj); if (opt && opt->classFilters) { NsfCmdList *h = CmdListFindNameInList(interp, filter, opt->classFilters); if (h) { NsfClasses *subClasses = DependentSubClasses(cl); if (h->clientData) { GuardDel(h); } GuardAdd(h, guardObj); if (subClasses) { FilterInvalidateObjOrders(interp, subClasses); NsfClassListFree(subClasses); } return TCL_OK; } } return NsfPrintError(interp, "filterguard: can't find filter %s on %s", filter, ClassName(cl)); } /* classMethod getCachedParameters NsfCGetCachendParametersMethod { } */ static int NsfCGetCachendParametersMethod(Tcl_Interp *interp, NsfClass *class) { assert(interp); assert(class); if (likely(class->parsedParamPtr && class->parsedParamPtr->paramDefs)) { Tcl_Obj *listObj; listObj = ListParamDefs(interp, class->parsedParamPtr->paramDefs->paramsPtr, NULL, NULL, NSF_PARAMS_PARAMETER); Tcl_SetObjResult(interp, listObj); DECR_REF_COUNT2("paramDefsObj", listObj); } return TCL_OK; } /* classMethod mixinguard NsfCMixinGuardMethod { {-argName "mixin" -required 1 -type tclobj} {-argName "guard" -required 1 -type tclobj} } */ static int NsfCMixinGuardMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *mixin, Tcl_Obj *guardObj) { NsfClassOpt *opt = cl->opt; assert(interp); assert(cl); assert(mixin); assert(guardObj); if (opt && opt->classMixins) { Tcl_Command mixinCmd = Tcl_GetCommandFromObj(interp, mixin); if (mixinCmd) { NsfClass *mixinCl = NsfGetClassFromCmdPtr(mixinCmd); if (mixinCl) { NsfCmdList *h = CmdListFindCmdInList(mixinCmd, opt->classMixins); if (h) { NsfClasses *subClasses; if (h->clientData) { GuardDel((NsfCmdList *) h); } GuardAdd(h, guardObj); subClasses = DependentSubClasses(cl); MixinInvalidateObjOrders(interp, cl, subClasses); NsfClassListFree(subClasses); return TCL_OK; } } } } return NsfPrintError(interp, "mixinguard: can't find mixin %s on %s", ObjStr(mixin), ClassName(cl)); } /* classMethod new NsfCNewMethod { {-argName "-childof" -required 0 -type tclobj} {-argName "args" -required 0 -type args} } */ static int NsfCNewMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *withChildof, int objc, Tcl_Obj *CONST objv[]) { Tcl_Obj *fullnameObj; Tcl_DString dFullname, *dsPtr = &dFullname; int result; assert(interp); assert(cl); #if 0 { int i; fprintf(stderr, "NsfCNewMethod %s withChildof %p oc %d ", ClassName(cl), withChildof, objc); for(i = 0; i < objc; i++) {fprintf(stderr, " [%d]=%s,", i, ObjStr(objv[i]));} fprintf(stderr, "\n"); } #endif Tcl_DStringInit(dsPtr); if (withChildof) { CONST char *parentName = ObjStr(withChildof); /* * If parentName is fully qualified, use it as prefix, else prepend the * CallingNameSpace() to be compatible with the object name completion. */ if (*parentName == ':' && *(parentName + 1) == ':') { /* * Prepend parentName only if it is not "::" */ if (*(parentName + 2) != '\0') { Tcl_DStringAppend(dsPtr, parentName, -1); } } else { Tcl_Obj *tmpName = NameInNamespaceObj(parentName, CallingNameSpace(interp)); CONST char *completedParentName; INCR_REF_COUNT(tmpName); completedParentName = ObjStr(tmpName); if (strcmp(completedParentName, "::")) { Tcl_DStringAppend(dsPtr, ObjStr(tmpName), -1); } DECR_REF_COUNT(tmpName); } Tcl_DStringAppend(dsPtr, "::__#", 5); } else { Tcl_DStringAppend(dsPtr, "::nsf::__#", 10); } NewTclCommand(interp, dsPtr); fullnameObj = Tcl_NewStringObj(Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr)); INCR_REF_COUNT(fullnameObj); { Tcl_Obj *methodObj; int callDirectly; callDirectly = CallDirectly(interp, &cl->object, NSF_c_create_idx, &methodObj); if (callDirectly) { result = NsfCCreateMethod(interp, cl, fullnameObj, objc, objv); } else { ALLOC_ON_STACK(Tcl_Obj*, objc+3, ov); ov[0] = NULL; /* just a placeholder for passing conventions in ObjectDispatch() */ ov[1] = methodObj; ov[2] = fullnameObj; if (objc >= 1) { memcpy(ov+3, objv, sizeof(Tcl_Obj *)*objc); } result = ObjectDispatch(cl, interp, objc+3, ov, NSF_CSC_IMMEDIATE); FREE_ON_STACK(Tcl_Obj *, ov); } } DECR_REF_COUNT(fullnameObj); Tcl_DStringFree(dsPtr); return result; } /* classMethod recreate NsfCRecreateMethod { {-argName "name" -required 1 -type tclobj} {-argName "args" -type allargs} } */ static int RecreateObject(Tcl_Interp *interp, NsfClass *class, NsfObject *object, int objc, Tcl_Obj *CONST objv[]) { int result; assert(interp); assert(class); assert(object); assert(objv); object->flags |= NSF_RECREATE; /* * First, cleanup the data from the object. * * Check whether we have a pending destroy on the object; if yes, * clear it, such that the recreated object and won't be destroyed * on a POP. */ MarkUndestroyed(object); /* * Ensure correct class for object. */ result = ChangeClass(interp, object, class); if (likely(result == TCL_OK)) { Tcl_Obj *methodObj; /* * Dispatch "cleanup" method. */ if (CallDirectly(interp, object, NSF_o_cleanup_idx, &methodObj)) { /*fprintf(stderr, "RECREATE calls cleanup directly for object %s\n", ObjectName(object));*/ result = NsfOCleanupMethod(interp, object); } else { /*NsfObjectSystem *osPtr = GetObjectSystem(object); fprintf(stderr, "RECREATE calls method cleanup for object %p %s OS %s\n", object, ObjectName(object), ObjectName(&osPtr->rootClass->object));*/ result = CallMethod(object, interp, methodObj, 2, NULL, NSF_CM_IGNORE_PERMISSIONS|NSF_CSC_IMMEDIATE); } } /* * Second: if cleanup was successful, initialize the object as usual. */ if (likely(result == TCL_OK)) { result = DoObjInitialization(interp, object, objc, objv); if (likely(result == TCL_OK)) { Tcl_SetObjResult(interp, object->cmdName); } else { /* fprintf(stderr, "recreate DoObjInitialization returned %d\n", result);*/ } } return result; } static int NsfCRecreateMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *nameObj, int objc, Tcl_Obj *CONST objv[]) { NsfObject *object; assert(interp); assert(cl); assert(nameObj); if (GetObjectFromObj(interp, nameObj, &object) != TCL_OK) { return NsfPrintError(interp, "can't recreate non existing object %s", ObjStr(nameObj)); } return RecreateObject(interp, cl, object, objc, objv); } /* classMethod superclass NsfCSuperclassMethod { {-argName "superclasses" -required 0 -type tclobj} } */ static int NsfCSuperclassMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *superClassesObj) { assert(interp); assert(cl); return NsfRelationSetCmd(interp, &cl->object, RelationtypeSuperclassIdx, superClassesObj); } /*********************************************************************** * End Class Methods ***********************************************************************/ static int AggregatedMethodType(int methodType) { switch (methodType) { case MethodtypeNULL: /* default */ case MethodtypeAllIdx: methodType = NSF_METHODTYPE_ALL; break; case MethodtypeScriptedIdx: /*methodType = NSF_METHODTYPE_SCRIPTED|NSF_METHODTYPE_ALIAS;*/ methodType = NSF_METHODTYPE_SCRIPTED; break; case MethodtypeBuiltinIdx: methodType = NSF_METHODTYPE_BUILTIN|NSF_METHODTYPE_OBJECT; break; case MethodtypeForwarderIdx: methodType = NSF_METHODTYPE_FORWARDER; break; case MethodtypeAliasIdx: methodType = NSF_METHODTYPE_ALIAS; break; case MethodtypeSetterIdx: methodType = NSF_METHODTYPE_SETTER; break; case MethodtypeObjectIdx: methodType = NSF_METHODTYPE_OBJECT; break; case MethodtypeNsfprocIdx: methodType = NSF_METHODTYPE_NSFPROC; break; default: methodType = 0; } return methodType; } /*********************************************************************** * Begin Object Info Methods ***********************************************************************/ /* objectInfoMethod children NsfObjInfoChildrenMethod { {-argName "-type" -required 0 -nrargs 1 -type class} {-argName "pattern" -required 0} } */ static int NsfObjInfoChildrenMethod(Tcl_Interp *interp, NsfObject *object, NsfClass *type, CONST char *pattern) { assert(interp); assert(object); return ListChildren(interp, object, pattern, 0, type); } /* objectInfoMethod class NsfObjInfoClassMethod { } */ static int NsfObjInfoClassMethod(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); Tcl_SetObjResult(interp, object->cl->object.cmdName); return TCL_OK; } /* objectInfoMethod filterguard NsfObjInfoFilterguardMethod { {-argName "filter" -required 1} } */ static int NsfObjInfoFilterguardMethod(Tcl_Interp *interp, NsfObject *object, CONST char *filter) { assert(interp); assert(object); assert(filter); return object->opt ? GuardList(interp, object->opt->objFilters, filter) : TCL_OK; } /* objectInfoMethod filters NsfObjInfoFiltersMethod { {-argName "-guards" -nrargs 0 -type switch} {-argName "pattern"} } */ static int NsfObjInfoFiltersMethod(Tcl_Interp *interp, NsfObject *object, int withGuards, CONST char *pattern) { NsfObjectOpt *opt = object->opt; assert(interp); assert(object); return opt ? FilterInfo(interp, opt->objFilters, pattern, withGuards, 0) : TCL_OK; } /* objectInfoMethod forward NsfObjInfoForwardMethod { {-argName "-definition"} {-argName "name"} } */ static int NsfObjInfoForwardMethod(Tcl_Interp *interp, NsfObject *object, int withDefinition, CONST char *pattern) { assert(interp); assert(object); return object->nsPtr ? ListForward(interp, Tcl_Namespace_cmdTablePtr(object->nsPtr), pattern, withDefinition) : TCL_OK; } /* objectInfoMethod hasmixin NsfObjInfoHasMixinMethod { {-argName "class" -required 1 -type class} } */ static int NsfObjInfoHasMixinMethod(Tcl_Interp *interp, NsfObject *object, NsfClass *mixinClass) { assert(interp); assert(object); assert(mixinClass); Tcl_SetBooleanObj(Tcl_GetObjResult(interp), HasMixin(interp, object, mixinClass)); return TCL_OK; } /* objectInfoMethod hasnamespace NsfObjInfoHasnamespaceMethod { } */ static int NsfObjInfoHasnamespaceMethod(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); Tcl_SetBooleanObj(Tcl_GetObjResult(interp), object->nsPtr != NULL); return TCL_OK; } /* objectInfoMethod hastype NsfObjInfoHasTypeMethod { {-argName "class" -required 1 -type class} } */ static int NsfObjInfoHasTypeMethod(Tcl_Interp *interp, NsfObject *object, NsfClass *typeClass) { assert(interp); assert(object); assert(typeClass); Tcl_SetBooleanObj(Tcl_GetObjResult(interp), IsSubType(object->cl, typeClass)); return TCL_OK; } /* objectInfoMethod lookupfilter NsfObjInfoLookupFilterMethod { {-argName "filter" -required 1} } */ static int NsfObjInfoLookupFilterMethod(Tcl_Interp *interp, NsfObject *object, CONST char *filter) { CONST char *filterName; NsfCmdList *cmdList; NsfClass *fcl; assert(interp); assert(object); assert(filter); /* * Searches for filter on [self] and returns fully qualified name if it is * not found it returns an empty string. */ Tcl_ResetResult(interp); if (!(object->flags & NSF_FILTER_ORDER_VALID)) { FilterComputeDefined(interp, object); } if (!(object->flags & NSF_FILTER_ORDER_DEFINED)) { return TCL_OK; } for (cmdList = object->filterOrder; cmdList; cmdList = cmdList->nextPtr) { filterName = Tcl_GetCommandName(interp, cmdList->cmdPtr); if (filterName[0] == filter[0] && !strcmp(filterName, filter)) { break; } } if (cmdList == NULL) { return TCL_OK; } fcl = cmdList->clorobj; Tcl_SetObjResult(interp, MethodHandleObj((NsfObject *)fcl, !NsfObjectIsClass(&fcl->object), filterName)); return TCL_OK; } /* objectInfoMethod lookupfilters NsfObjInfoLookupFiltersMethod { {-argName "-guards" -nrargs 0 -type switch} {-argName "pattern"} } */ static int NsfObjInfoLookupFiltersMethod(Tcl_Interp *interp, NsfObject *object, int withGuards, CONST char *pattern) { assert(interp); assert(object); if (!(object->flags & NSF_FILTER_ORDER_VALID)) { FilterComputeDefined(interp, object); } return FilterInfo(interp, object->filterOrder, pattern, withGuards, 1); } /* objectInfoMethod lookupmethod NsfObjInfoLookupMethodMethod { {-argName "name" -required 1 -type tclobj} } */ static int NsfObjInfoLookupMethodMethod(Tcl_Interp *interp, NsfObject *object, Tcl_Obj *methodObj) { NsfClass *pcl = NULL; Tcl_Command cmd; assert(interp); assert(object); assert(methodObj); cmd = ObjectFindMethod(interp, object, methodObj, &pcl); if (likely(cmd != NULL)) { NsfObject *pobj = pcl ? &pcl->object : object; int perObject = (pcl == NULL); ListMethod(interp, pobj, pobj, ObjStr(methodObj), cmd, InfomethodsubcmdRegistrationhandleIdx, NULL, NULL, perObject); } return TCL_OK; } /* objectInfoMethod lookupmethods NsfObjInfoLookupMethodsMethod { {-argName "-callprotection" -nrargs 1 -type "all|public|protected|private" -default all} {-argName "-incontext"} {-argName "-type" -nrargs 1 -typeName "methodtype" -type "all|scripted|builtin|alias|forwarder|object|setter"} {-argName "-nomixins"} {-argName "-path" -nrargs 0} {-argName "-source" -nrargs 1 -type "all|application|system"} {-argName "pattern" -required 0} } */ static int ListMethodKeysClassList(Tcl_Interp *interp, NsfClasses *classList, int withSource, CONST char *pattern, int methodType, int withCallprotection, int withPath, Tcl_HashTable *dups, NsfObject *object, int withPer_object) nonnull(1) nonnull(8) nonnull(9); static int ListMethodKeysClassList(Tcl_Interp *interp, NsfClasses *classList, int withSource, CONST char *pattern, int methodType, int withCallprotection, int withPath, Tcl_HashTable *dups, NsfObject *object, int withPer_object) { assert(interp); assert(dups); assert(object); /* append method keys from inheritance order */ for (; classList; classList = classList->nextPtr) { Tcl_HashTable *cmdTablePtr = Tcl_Namespace_cmdTablePtr(classList->cl->nsPtr); if (!MethodSourceMatches(withSource, classList->cl, NULL)) { continue; } ListMethodKeys(interp, cmdTablePtr, NULL, pattern, methodType, withCallprotection, withPath, dups, object, withPer_object); } return TCL_OK; } /* objectInfoMethod lookupmethods NsfObjInfoLookupMethodsMethod { {-argName "-callprotection" -type "all|public|protected|private" -default all} {-argName "-incontext" -nrargs 0} {-argName "-type" -typeName "methodtype" -type "all|scripted|builtin|alias|forwarder|object|setter|nsfproc"} {-argName "-nomixins" -nrargs 0} {-argName "-path" -nrargs 0} {-argName "-source" -type "all|application|system" -default all} {-argName "pattern" -required 0} } */ static int NsfObjInfoLookupMethodsMethod(Tcl_Interp *interp, NsfObject *object, int withCallprotection, int withIncontext, int withMethodtype, int withNomixins, int withPath, int withSource, CONST char *pattern) { int withPer_object = 1; Tcl_HashTable dupsTable, *dups = &dupsTable; int result, methodType = AggregatedMethodType(withMethodtype); assert(interp); assert(object); /* * TODO: we could make this faster for patterns without meta-chars * by letting ListMethodKeys() to signal us when an entry was found. * we wait, until the we decided about "info methods defined" * vs. "info method search" vs. "info defined" etc. */ if (withCallprotection == CallprotectionNULL) { withCallprotection = CallprotectionPublicIdx; } if (withSource == SourceNULL) { withSource = SourceAllIdx; } Tcl_InitHashTable(dups, TCL_STRING_KEYS); if (object->nsPtr) { Tcl_HashTable *cmdTablePtr = Tcl_Namespace_cmdTablePtr(object->nsPtr); if (MethodSourceMatches(withSource, NULL, object)) { ListMethodKeys(interp, cmdTablePtr, NULL, pattern, methodType, withCallprotection, withPath, dups, object, withPer_object); } } if (!withNomixins) { if (!(object->flags & NSF_MIXIN_ORDER_VALID)) { MixinComputeDefined(interp, object); } if (object->flags & NSF_MIXIN_ORDER_DEFINED_AND_VALID) { NsfCmdList *ml; for (ml = object->mixinOrder; ml; ml = ml->nextPtr) { int guardOk = TCL_OK; NsfClass *mixin = NsfGetClassFromCmdPtr(ml->cmdPtr); assert(mixin); if (withIncontext) { if (!RUNTIME_STATE(interp)->guardCount && ml->clientData) { guardOk = GuardCall(object, interp, ml->clientData, NULL); } } if (mixin && guardOk == TCL_OK) { Tcl_HashTable *cmdTablePtr = Tcl_Namespace_cmdTablePtr(mixin->nsPtr); if (!MethodSourceMatches(withSource, mixin, NULL)) continue; ListMethodKeys(interp, cmdTablePtr, NULL, pattern, methodType, withCallprotection, withPath, dups, object, withPer_object); } } } } result = ListMethodKeysClassList(interp, PrecedenceOrder(object->cl), withSource, pattern, methodType, withCallprotection, withPath, dups, object, withPer_object); Tcl_DeleteHashTable(dups); return result; } /* objectInfoMethod lookupmixins NsfObjInfoLookupMixinsMethod { {-argName "-guards" -nrargs 0 -type switch} {-argName "pattern" -type objpattern} } */ static int NsfObjInfoLookupMixinsMethod(Tcl_Interp *interp, NsfObject *object, int withGuards, CONST char *patternString, NsfObject *patternObj) { assert(interp); assert(object); if (!(object->flags & NSF_MIXIN_ORDER_VALID)) { MixinComputeDefined(interp, object); } return MixinInfo(interp, object->mixinOrder, patternString, withGuards, patternObj); } /* objectInfoMethod lookupslots NsfObjInfoLookupSlotsMethod { {-argName "-source" -nrargs 1 -type "all|application|system" -default all} {-argName "-type" -required 0 -nrargs 1 -type class} {-argName "pattern" -required 0} } */ static int NsfObjInfoLookupSlotsMethod(Tcl_Interp *interp, NsfObject *object, int withSource, NsfClass *type, CONST char *pattern) { Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); NsfClasses *precendenceList, *clPtr; Tcl_HashTable slotTable; assert(interp); assert(object); precendenceList = ComputePrecedenceList(interp, object, NULL /* pattern*/, 1, 1); assert(precendenceList); if (withSource == 0) {withSource = 1;} Tcl_InitHashTable(&slotTable, TCL_STRING_KEYS); MEM_COUNT_ALLOC("Tcl_InitHashTable", &slotTable); /* * First add the per-object slot objects. */ if (MethodSourceMatches(withSource, NULL, object)) { AddSlotObjects(interp, object, "::per-object-slot", &slotTable, withSource, type, pattern, listObj); } /* * Then add the class provided slot objects. */ for (clPtr = precendenceList; likely(clPtr != NULL); clPtr = clPtr->nextPtr) { if (MethodSourceMatches(withSource, clPtr->cl, NULL)) { AddSlotObjects(interp, &clPtr->cl->object, "::slot", &slotTable, withSource, type, pattern, listObj); } } Tcl_DeleteHashTable(&slotTable); MEM_COUNT_FREE("Tcl_InitHashTable", &slotTable); NsfClassListFree(precendenceList); Tcl_SetObjResult(interp, listObj); return TCL_OK; } /* objectInfoMethod method NsfObjInfoMethodMethod { {-argName "infomethodsubcmd" -type "args|body|definition|exists|registrationhandle|definitionhandle|origin|parameter|syntax|type|precondition|postcondition|submethods"} {-argName "name" -required 1 -type tclobj} } */ static int NsfObjInfoMethodMethod(Tcl_Interp *interp, NsfObject *object, int subcmd, Tcl_Obj *methodNameObj) { return ListMethodResolve(interp, subcmd, NULL, NULL, object->nsPtr, object, methodNameObj, 0); } /* objectInfoMethod methods NsfObjInfoMethodsMethod { {-argName "-callprotection" -type "all|public|protected|private" -default all} {-argName "-type" -nrargs 1 -typeName "methodtype" -type "all|scripted|builtin|alias|forwarder|object|setter"} {-argName "-path" -nrargs 0} {-argName "pattern"} } */ static int NsfObjInfoMethodsMethod(Tcl_Interp *interp, NsfObject *object, int withCallproctection, int withMethodtype, int withPath, CONST char *pattern) { assert(interp); assert(object); return ListDefinedMethods(interp, object, pattern, 1 /* per-object */, AggregatedMethodType(withMethodtype), withCallproctection, withPath); } /* objectInfoMethod mixins NsfObjInfoMixinsMethod { {-argName "-guards" -nrargs 0 -type switch} {-argName "pattern" -type objpattern} } */ static int NsfObjInfoMixinsMethod(Tcl_Interp *interp, NsfObject *object, int withGuards, CONST char *patternString, NsfObject *patternObj) { assert(interp); assert(object); return object->opt ? MixinInfo(interp, object->opt->objMixins, patternString, withGuards, patternObj) : TCL_OK; } /* objectInfoMethod mixinguard NsfObjInfoMixinguardMethod { {-argName "mixin" -required 1} } */ static int NsfObjInfoMixinguardMethod(Tcl_Interp *interp, NsfObject *object, CONST char *mixin) { assert(interp); assert(object); assert(mixin); return object->opt ? GuardList(interp, object->opt->objMixins, mixin) : TCL_OK; } /* objectInfoMethod name NsfObjInfoNameMethod { } */ static int NsfObjInfoNameMethod(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_GetCommandName(interp, object->id), -1)); return TCL_OK; } /* objectInfoMethod objectparameter NsfObjInfoObjectparameterMethod { {-argName "infoobjectparametersubcmd" -type "definitions|list|names|syntax" -required 1} {-argName "pattern" -required 0} } */ /* * Actually, this method (object::info::objectparameter) is not used anymore. */ static int NsfObjInfoObjectparameterMethod(Tcl_Interp *interp, NsfObject *object, int subcmd, CONST char *pattern) { NsfParsedParam parsedParam; Tcl_Obj *listObj = NULL; Nsf_Param CONST *paramsPtr; Nsf_Param *paramList = NULL; int result; assert(interp); assert(object); result = GetObjectParameterDefinition(interp, NsfGlobalObjs[NSF_EMPTY], object, NULL, &parsedParam); if (result != TCL_OK || parsedParam.paramDefs == NULL) { return result; } paramsPtr = parsedParam.paramDefs->paramsPtr; if (pattern) { paramList = NsfParamDefsFilter(interp, paramsPtr, pattern); if (unlikely(paramList == NULL)) { /* * The named parameter were NOT found, so return "". */ Tcl_SetObjResult(interp, NsfGlobalObjs[NSF_EMPTY]); return TCL_OK; } /* Iterate below over the computed selection */ paramsPtr = paramList; } switch (subcmd) { case InfoobjectparametersubcmdDefinitionsIdx: listObj = ParamDefsFormat(interp, paramsPtr, NULL, NULL); break; case InfoobjectparametersubcmdListIdx: listObj = ParamDefsList(interp, paramsPtr, NULL, NULL); break; case InfoobjectparametersubcmdNamesIdx: listObj = ParamDefsNames(interp, paramsPtr, NULL, NULL); break; case InfoobjectparametersubcmdSyntaxIdx: listObj = NsfParamDefsSyntax(interp, paramsPtr, NULL, NULL); break; } assert(listObj); Tcl_SetObjResult(interp, listObj); DECR_REF_COUNT2("paramDefsObj", listObj); if (paramList) { FREE(Nsf_Param*, paramList); } return TCL_OK; } /* objectInfoMethod parent NsfObjInfoParentMethod { } */ static int NsfObjInfoParentMethod(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); if (object->id) { Tcl_Namespace *nsPtr = Tcl_Command_nsPtr(object->id); Tcl_SetResult(interp, nsPtr ? nsPtr->fullName : "", TCL_VOLATILE); } return TCL_OK; } /* objectInfoMethod precedence NsfObjInfoPrecedenceMethod { {-argName "-intrinsic"} {-argName "pattern" -required 0} } */ static int NsfObjInfoPrecedenceMethod(Tcl_Interp *interp, NsfObject *object, int withIntrinsicOnly, CONST char *pattern) { NsfClasses *precedenceList, *pl; Tcl_Obj *resultObj = Tcl_NewObj(); assert(interp); assert(object); precedenceList = ComputePrecedenceList(interp, object, pattern, !withIntrinsicOnly, 1); for (pl = precedenceList; pl; pl = pl->nextPtr) { assert(pl->cl); Tcl_ListObjAppendElement(interp, resultObj, pl->cl->object.cmdName); } if (precedenceList) NsfClassListFree(precedenceList); Tcl_SetObjResult(interp, resultObj); return TCL_OK; } /* objectInfoMethod slotobjects NsfObjInfoSlotobjectsMethod { {-argName "-type" -required 0 -nrargs 1 -type class} {-argName "pattern" -required 0} } */ static int NsfObjInfoSlotobjectsMethod(Tcl_Interp *interp, NsfObject *object, NsfClass *type, CONST char *pattern) { Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); assert(interp); assert(object); AddSlotObjects(interp, object, "::per-object-slot", NULL, SourceAllIdx, type, pattern, listObj); Tcl_SetObjResult(interp, listObj); return TCL_OK; } /* objectInfoMethod vars NsfObjInfoVarsMethod { {-argName "pattern" -required 0} } */ static int NsfObjInfoVarsMethod(Tcl_Interp *interp, NsfObject *object, CONST char *pattern) { Tcl_Obj *varList, *okList, *element; int i, length; TclVarHashTable *varTablePtr = object->nsPtr ? Tcl_Namespace_varTablePtr(object->nsPtr) : object->varTablePtr; assert(interp); assert(object); ListVarKeys(interp, TclVarHashTablePtr(varTablePtr), pattern); varList = Tcl_GetObjResult(interp); Tcl_ListObjLength(interp, varList, &length); okList = Tcl_NewListObj(0, NULL); for (i = 0; i < length; i++) { Tcl_ListObjIndex(interp, varList, i, &element); if (VarExists(interp, object, ObjStr(element), NULL, NSF_VAR_REQUIRE_DEFINED)) { Tcl_ListObjAppendElement(interp, okList, element); } else { /*fprintf(stderr, "must ignore '%s' %d\n", ObjStr(element), i);*/ /*Tcl_ListObjReplace(interp, varList, i, 1, 0, NULL);*/ } } Tcl_SetObjResult(interp, okList); return TCL_OK; } /*********************************************************************** * End Object Info Methods ***********************************************************************/ /*********************************************************************** * Begin Class Info methods ***********************************************************************/ /* classInfoMethod filterguard NsfClassInfoFilterguardMethod { {-argName "filter" -required 1} } */ static int NsfClassInfoFilterguardMethod(Tcl_Interp *interp, NsfClass *class, CONST char *filter) { assert(interp); assert(class); assert(filter); return class->opt ? GuardList(interp, class->opt->classFilters, filter) : TCL_OK; } /* classInfoMethod filters NsfClassInfoFiltersMethod { {-argName "-guards" -nrargs 0 -type switch} {-argName "pattern"} } */ static int NsfClassInfoFiltersMethod(Tcl_Interp *interp, NsfClass *class, int withGuards, CONST char *pattern) { assert(interp); assert(class); return class->opt ? FilterInfo(interp, class->opt->classFilters, pattern, withGuards, 0) : TCL_OK; } /* classInfoMethod forward NsfClassInfoForwardMethod { {-argName "-definition"} {-argName "name"} } */ static int NsfClassInfoForwardMethod(Tcl_Interp *interp, NsfClass *class, int withDefinition, CONST char *pattern) { assert(interp); assert(class); return ListForward(interp, Tcl_Namespace_cmdTablePtr(class->nsPtr), pattern, withDefinition); } /* classInfoMethod heritage NsfClassInfoHeritageMethod { {-argName "pattern"} } */ static int NsfClassInfoHeritageMethod(Tcl_Interp *interp, NsfClass *cl, CONST char *pattern) { NsfClasses *pl, *intrinsic, *checkList = NULL, *mixinClasses = NULL; Tcl_Obj *resultObj; assert(interp); assert(cl); resultObj = Tcl_NewObj(); intrinsic = PrecedenceOrder(cl); NsfClassListAddPerClassMixins(interp, cl, &mixinClasses, &checkList); for (pl = mixinClasses; pl; pl = pl->nextPtr) { if (NsfClassListFind(pl->nextPtr, pl->cl) == NULL && NsfClassListFind(intrinsic, pl->cl) == NULL) { AppendMatchingElement(interp, resultObj, pl->cl->object.cmdName, pattern); } } if (intrinsic) { for (pl = intrinsic->nextPtr; pl; pl = pl->nextPtr) { AppendMatchingElement(interp, resultObj, pl->cl->object.cmdName, pattern); } } if (mixinClasses) NsfClassListFree(mixinClasses); if (checkList) NsfClassListFree(checkList); Tcl_SetObjResult(interp, resultObj); return TCL_OK; } /* *---------------------------------------------------------------------- * * InstancesFromClassList -- * * Collect all instances of the classes of the provided class list in the * returned result object. * * Results: * Tcl_Obj containing a list of instances or a single instance * * Side effects: * Updated resultObj. * *---------------------------------------------------------------------- */ static Tcl_Obj *InstancesFromClassList(Tcl_Interp *interp, NsfClasses *subClasses, CONST char *pattern, NsfObject *matchObject) nonnull(1) nonnull(2) returns_nonnull; static Tcl_Obj * InstancesFromClassList(Tcl_Interp *interp, NsfClasses *subClasses, CONST char *pattern, NsfObject *matchObject) { Tcl_Obj *resultObj = Tcl_NewObj(); assert(interp); assert(subClasses); for (; subClasses; subClasses = subClasses->nextPtr) { Tcl_HashTable *tablePtr = &subClasses->cl->instances; Tcl_HashEntry *hPtr; Tcl_HashSearch search; for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr; hPtr = Tcl_NextHashEntry(&search)) { NsfObject *inst = (NsfObject *) Tcl_GetHashKey(tablePtr, hPtr); if (matchObject && inst == matchObject) { Tcl_SetStringObj(resultObj, ObjStr(matchObject->cmdName), -1); return resultObj; } AppendMatchingElement(interp, resultObj, inst->cmdName, pattern); } } return resultObj; } /* classInfoMethod instances NsfClassInfoInstancesMethod { {-argName "-closure" -nrargs 0} {-argName "pattern" -type objpattern} } */ static int NsfClassInfoInstancesMethod(Tcl_Interp *interp, NsfClass *startCl, int withClosure, CONST char *pattern, NsfObject *matchObject) { NsfClasses clElement, *subClasses; assert(interp); assert(startCl); if (withClosure) { subClasses = TransitiveSubClasses(startCl); } else { subClasses = &clElement; clElement.cl = startCl; clElement.nextPtr = NULL; } Tcl_SetObjResult(interp, InstancesFromClassList(interp, subClasses, pattern, matchObject)); if (withClosure) { NsfClassListFree(subClasses); } return TCL_OK; } /* classInfoMethod method NsfClassInfoMethodMethod { {-argName "infomethodsubcmd" -type "args|body|definition|exists|registrationhandle|definitionhandle|origin|parameter|syntax|type|precondition|postcondition|submethods|returns"} {-argName "name" -required 1 -type tclobj} } */ static int NsfClassInfoMethodMethod(Tcl_Interp *interp, NsfClass *class, int subcmd, Tcl_Obj *methodNameObj) { return ListMethodResolve(interp, subcmd, NULL, NULL, class->nsPtr, &class->object, methodNameObj, 1); } /* classInfoMethod methods NsfClassInfoMethodsMethod { {-argName "-callprotection" -type "all|public|protected|private" -default all} {-argName "-closure" -nrargs 0} {-argName "-type" -typeName "methodtype" -nrargs 1 -type "all|scripted|builtin|alias|forwarder|object|setter"} {-argName "-path" -nrargs 0} {-argName "-source" -nrargs 1 -type "all|application|system"} {-argName "pattern"} } */ static int NsfClassInfoMethodsMethod(Tcl_Interp *interp, NsfClass *class, int withCallprotection, int withClosure, int withMethodtype, int withPath, int withSource, CONST char *pattern) { assert(interp); assert(class); if (withClosure) { NsfClasses *checkList = NULL, *mixinClasses = NULL; Tcl_HashTable dupsTable, *dups = &dupsTable; int result; #if 0 if (withCallprotection == CallprotectionNULL) { withCallprotection = CallprotectionPublicIdx; } #endif if (withSource == SourceNULL) { withSource = SourceAllIdx; } Tcl_InitHashTable(dups, TCL_STRING_KEYS); /* guards are ignored */ NsfClassListAddPerClassMixins(interp, class, &mixinClasses, &checkList); (void) ListMethodKeysClassList(interp, mixinClasses, withSource, pattern, AggregatedMethodType(withMethodtype), withCallprotection, withPath, dups, &class->object, 0); if (checkList) NsfClassListFree(checkList); if (mixinClasses) NsfClassListFree(mixinClasses); result = ListMethodKeysClassList(interp, PrecedenceOrder(class), withSource, pattern, AggregatedMethodType(withMethodtype), withCallprotection, withPath, dups, &class->object, 0); Tcl_DeleteHashTable(dups); return result; } else { if (withSource) { return NsfPrintError(interp, "-source cannot be used without -closure\n"); } return ListDefinedMethods(interp, &class->object, pattern, 0 /* per-object */, AggregatedMethodType(withMethodtype), withCallprotection, withPath); } } /* classInfoMethod mixins NsfClassInfoMixinsMethod { {-argName "-closure" -nrargs 0 -type switch} {-argName "-guards" -nrargs 0 -type switch} {-argName "-heritage" -nrargs 0 -type switch} {-argName "pattern" -type objpattern} } */ static int NsfClassInfoMixinsMethod(Tcl_Interp *interp, NsfClass *class, int withClosure, int withGuards, int withHeritage, CONST char *patternString, NsfObject *patternObj) { NsfClassOpt *opt = class->opt; Tcl_Obj *resultObj; int result = TCL_OK; assert(interp); assert(class); Tcl_ResetResult(interp); resultObj = Tcl_GetObjResult(interp); if (withHeritage) { NsfClasses *checkList = NULL, *mixinClasses = NULL, *clPtr; if (withGuards) { return NsfPrintError(interp, "-guards cannot be used together with -heritage\n"); } NsfClassListAddPerClassMixins(interp, class, &mixinClasses, &checkList); for (clPtr = mixinClasses; clPtr; clPtr = clPtr->nextPtr) { if (NsfClassListFind(clPtr->nextPtr, clPtr->cl)) continue; AppendMatchingElement(interp, resultObj, clPtr->cl->object.cmdName, patternString); } if (checkList) NsfClassListFree(checkList); if (mixinClasses) NsfClassListFree(mixinClasses); } else if (withClosure) { Tcl_HashTable objTable, *commandTable = &objTable; int rc; MEM_COUNT_ALLOC("Tcl_InitHashTable", commandTable); Tcl_InitHashTable(commandTable, TCL_ONE_WORD_KEYS); rc = GetAllClassMixins(interp, commandTable, resultObj, class, withGuards, patternString, patternObj); if (patternObj && rc && !withGuards) { Tcl_SetObjResult(interp, rc ? patternObj->cmdName : NsfGlobalObjs[NSF_EMPTY]); } Tcl_DeleteHashTable(commandTable); MEM_COUNT_FREE("Tcl_InitHashTable", commandTable); } else { result = opt ? MixinInfo(interp, opt->classMixins, patternString, withGuards, patternObj) : TCL_OK; } return result; } /* classInfoMethod mixinguard NsfClassInfoMixinguardMethod { {-argName "mixin" -required 1} } */ static int NsfClassInfoMixinguardMethod(Tcl_Interp *interp, NsfClass *class, CONST char *mixin) { assert(interp); assert(class); assert(mixin); return class->opt ? GuardList(interp, class->opt->classMixins, mixin) : TCL_OK; } /* classInfoMethod mixinof NsfClassInfoMixinOfMethod { {-argName "-closure" -nrargs 0} {-argName "-scope" -required 0 -nrargs 1 -type "all|class|object"} {-argName "pattern" -type objpattern} } */ static int NsfClassInfoMixinOfMethod(Tcl_Interp *interp, NsfClass *class, int withClosure, int withScope, CONST char *patternString, NsfObject *patternObj) { NsfClassOpt *opt = class->opt; int perClass, perObject, rc = TCL_OK; Tcl_Obj *resultObj; assert(interp); assert(class); Tcl_ResetResult(interp); resultObj = Tcl_GetObjResult(interp); if (withScope == ScopeNULL || withScope == ScopeAllIdx) { perClass = 1; perObject = 1; } else if (withScope == ScopeClassIdx) { perClass = 1; perObject = 0; } else { perClass = 0; perObject = 1; } if (opt && !withClosure) { if (perClass && opt->isClassMixinOf) { rc = AppendMatchingElementsFromCmdList(interp, opt->isClassMixinOf, resultObj, patternString, patternObj); if (rc && patternObj) {goto finished;} } if (perObject && opt->isObjectMixinOf) { rc = AppendMatchingElementsFromCmdList(interp, opt->isObjectMixinOf, resultObj, patternString, patternObj); } } else if (withClosure) { Tcl_HashTable objTable, *commandTable = &objTable; MEM_COUNT_ALLOC("Tcl_InitHashTable", commandTable); Tcl_InitHashTable(commandTable, TCL_ONE_WORD_KEYS); if (perClass) { rc = GetAllClassMixinsOf(interp, commandTable, resultObj, class, 0, 1, patternString, patternObj); if (rc && patternObj) {goto finished;} } if (perObject) { rc = GetAllObjectMixinsOf(interp, commandTable, resultObj, class, 0, 1, patternString, patternObj); } Tcl_DeleteHashTable(commandTable); MEM_COUNT_FREE("Tcl_InitHashTable", commandTable); } finished: if (patternObj) { Tcl_SetObjResult(interp, rc ? patternObj->cmdName : NsfGlobalObjs[NSF_EMPTY]); } else { Tcl_SetObjResult(interp, resultObj); } return TCL_OK; } /* classInfoMethod slots NsfClassInfoSlotobjectsMethod { {-argName "-closure" -nrargs 0} {-argName "-source" -nrargs 1 -type "all|application|system"} {-argName "-type" -required 0 -nrargs 1 -type class} {-argName "pattern" -required 0} } */ static int NsfClassInfoSlotobjectsMethod(Tcl_Interp *interp, NsfClass *class, int withClosure, int withSource, NsfClass *type, CONST char *pattern) { NsfClasses *clPtr, *intrinsicClasses, *precedenceList = NULL; Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); Tcl_HashTable slotTable; assert(interp); assert(class); Tcl_ResetResult(interp); intrinsicClasses = PrecedenceOrder(class); if (withClosure) { NsfClasses *checkList = NULL, *mixinClasses = NULL; /* * Compute the closure: first the transitive mixin-classes... */ NsfClassListAddPerClassMixins(interp, class, &mixinClasses, &checkList); for (clPtr = mixinClasses; clPtr; clPtr = clPtr->nextPtr) { if (NsfClassListFind(clPtr->nextPtr, clPtr->cl) == NULL && NsfClassListFind(intrinsicClasses, clPtr->cl) == NULL) { NsfClassListAdd(&precedenceList, clPtr->cl, NULL); } } /* * ... followed by the intrinsic classes. */ NsfClassListAdd(&precedenceList, class, NULL); for (clPtr = intrinsicClasses->nextPtr; clPtr; clPtr = clPtr->nextPtr) { NsfClassListAdd(&precedenceList, clPtr->cl, NULL); } if (checkList) NsfClassListFree(checkList); if (mixinClasses) NsfClassListFree(mixinClasses); } else { NsfClassListAdd(&precedenceList, class, NULL); } /* NsfClassListPrint("precedence", precedenceList); */ if (withSource == 0) {withSource = 1;} /* * Use a hash-table to eliminate potential duplicates. */ Tcl_InitHashTable(&slotTable, TCL_STRING_KEYS); MEM_COUNT_ALLOC("Tcl_InitHashTable", &slotTable); for (clPtr = precedenceList; clPtr; clPtr = clPtr->nextPtr) { if (MethodSourceMatches(withSource, clPtr->cl, NULL)) { AddSlotObjects(interp, &clPtr->cl->object, "::slot", &slotTable, withSource, type, pattern, listObj); } } Tcl_DeleteHashTable(&slotTable); MEM_COUNT_FREE("Tcl_InitHashTable", &slotTable); if (precedenceList) NsfClassListFree(precedenceList); Tcl_SetObjResult(interp, listObj); return TCL_OK; } /* classInfoMethod subclass NsfClassInfoSubclassMethod { {-argName "-closure" -nrargs 0 -type switch} {-argName "-dependent" -nrargs 0 -type switch} {-argName "pattern" -type objpattern} } */ static int NsfClassInfoSubclassMethod(Tcl_Interp *interp, NsfClass *class, int withClosure, int withDependent, CONST char *patternString, NsfObject *patternObj) { int rc = 0; assert(interp); assert(class); if (withClosure && withDependent) { return NsfPrintError(interp, "only -closure or -dependent can be specified, not both"); } if (withClosure || withDependent) { NsfClasses *subClasses = withClosure ? TransitiveSubClasses(class) : DependentSubClasses(class); if (subClasses) { rc = AppendMatchingElementsFromClasses(interp, subClasses, patternString, patternObj); NsfClassListFree(subClasses); } } else if (class->sub) { rc = AppendMatchingElementsFromClasses(interp, class->sub, patternString, patternObj); } if (patternObj) { Tcl_SetObjResult(interp, rc ? patternObj->cmdName : NsfGlobalObjs[NSF_EMPTY]); } return TCL_OK; } /* classInfoMethod superclass NsfClassInfoSuperclassMethod { {-argName "-closure" -nrargs 0} {-argName "pattern" -type tclobj} } */ static int NsfClassInfoSuperclassMethod(Tcl_Interp *interp, NsfClass *class, int withClosure, Tcl_Obj *pattern) { assert(interp); assert(class); return ListSuperClasses(interp, class, pattern, withClosure); } /*********************************************************************** * End Class Info methods ***********************************************************************/ /* * Initialization and Exit handlers */ #ifdef DO_FULL_CLEANUP /* delete global variables and procs */ static void DeleteProcsAndVars(Tcl_Interp *interp, Tcl_Namespace *nsPtr, int withKeepvars) nonnull(1) nonnull(2); static void DeleteProcsAndVars(Tcl_Interp *interp, Tcl_Namespace *nsPtr, int withKeepvars) { Tcl_HashTable *varTablePtr, *cmdTablePtr, *childTablePtr; Tcl_HashSearch search; Tcl_Command cmd; Var *varPtr; register Tcl_HashEntry *entryPtr; assert(interp); assert(nsPtr); /* fprintf(stderr, "DeleteProcsAndVars in %s\n", nsPtr->fullName); */ varTablePtr = (Tcl_HashTable *)Tcl_Namespace_varTablePtr(nsPtr); cmdTablePtr = Tcl_Namespace_cmdTablePtr(nsPtr); childTablePtr = Tcl_Namespace_childTablePtr(nsPtr); /* * Deleting the procs and vars in the child namespaces does not seem to be * necessary, but we do it anyway. */ for (entryPtr = Tcl_FirstHashEntry(childTablePtr, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { Tcl_Namespace *childNsPtr = (Tcl_Namespace *) Tcl_GetHashValue(entryPtr); DeleteProcsAndVars(interp, childNsPtr, withKeepvars); } if (!withKeepvars) { for (entryPtr = Tcl_FirstHashEntry(varTablePtr, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { Tcl_Obj *nameObj; GetVarAndNameFromHash(entryPtr, &varPtr, &nameObj); if (!TclIsVarUndefined(varPtr) || TclIsVarNamespaceVar(varPtr)) { /* fprintf(stderr, "unsetting var %s\n", ObjStr(nameObj));*/ Tcl_UnsetVar2(interp, ObjStr(nameObj), (char *)NULL, TCL_GLOBAL_ONLY); } } } for (entryPtr = Tcl_FirstHashEntry(cmdTablePtr, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { cmd = (Tcl_Command)Tcl_GetHashValue(entryPtr); if (Tcl_Command_objProc(cmd) == RUNTIME_STATE(interp)->objInterpProc) { /*fprintf(stderr, "cmdname = %s cmd %p proc %p objProc %p %d\n", Tcl_GetHashKey(cmdTablePtr, entryPtr), cmd, Tcl_Command_proc(cmd), Tcl_Command_objProc(cmd), Tcl_Command_proc(cmd)==RUNTIME_STATE(interp)->objInterpProc);*/ Tcl_DeleteCommandFromToken(interp, cmd); } } } #endif /* *---------------------------------------------------------------------- * * FinalObjectDeletion -- * * The method is to be called, when an object is finally deleted, * typically during the final cleanup. It tests as well the actication * count of the object. * * Results: * None. * * Side effects: * Deletion of the objects. * *---------------------------------------------------------------------- */ static void FinalObjectDeletion(Tcl_Interp *interp, NsfObject *object) { assert(interp); assert(object); /* * If a call to exit happens from a higher stack frame, the object * refCount might not be decremented correctly. If we are in the * physical destroy round, we can set the counter to an appropriate * value to ensure deletion. */ #if defined(NSF_DEVELOPMENT) if (unlikely(object->refCount != 1)) { if (object->refCount > 1) { NsfLog(interp, NSF_LOG_WARN, "RefCount for obj %p %d (name %s) > 1", object, object->refCount, ObjectName(object)); } else { NsfLog(interp, NSF_LOG_WARN, "Refcount for obj %p %d > 1", object, object->refCount); } /*object->refCount = 1;*/ } #endif #if !defined(NDEBUG) if (unlikely(object->activationCount != 0)) { fprintf(stderr, "FinalObjectDeletion obj %p activationcount %d\n", object, object->activationCount); } #endif assert(object->activationCount == 0); if (likely(object->id != NULL)) { /*fprintf(stderr, " ... cmd dealloc %p final delete refCount %d\n", object->id, Tcl_Command_refCount(object->id));*/ if (NSF_DTRACE_OBJECT_FREE_ENABLED()) { NSF_DTRACE_OBJECT_FREE(ObjectName(object), ClassName(object->cl)); } Tcl_DeleteCommandFromToken(interp, object->id); } } #ifdef DO_CLEANUP /* *---------------------------------------------------------------------- * * DeleteNsfProcs -- * * Delete all nsfprocs in the namespaces rooted by the second * argument. If the provided nsPtr is NULL, the global namespace is used * as root of the namespace tree. The function is necessary to trigger * the freeing of the parameter definitions. * * Results: * None. * * Side effects: * Deletion of nsfprocs. * *---------------------------------------------------------------------- */ static void DeleteNsfProcs(Tcl_Interp *interp, Tcl_Namespace *nsPtr) nonnull(1); static void DeleteNsfProcs(Tcl_Interp *interp, Tcl_Namespace *nsPtr) { Tcl_HashTable *cmdTablePtr, *childTablePtr; register Tcl_HashEntry *entryPtr; Tcl_HashSearch search; assert(interp); if (nsPtr == NULL) { nsPtr = Tcl_GetGlobalNamespace(interp); } assert(nsPtr); /*fprintf(stderr, "### DeleteNsfProcs current namespace '%s'\n", nsPtr ? nsPtr->fullName : "NULL");*/ cmdTablePtr = Tcl_Namespace_cmdTablePtr(nsPtr); childTablePtr = Tcl_Namespace_childTablePtr(nsPtr); for (entryPtr = Tcl_FirstHashEntry(cmdTablePtr, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { Tcl_Command cmd = (Tcl_Command)Tcl_GetHashValue(entryPtr); if (Tcl_Command_objProc(cmd) == NsfProcStub) { /*fprintf(stderr, "cmdname = %s cmd %p\n", Tcl_GetHashKey(cmdTablePtr, entryPtr), cmd);*/ Tcl_DeleteCommandFromToken(interp, cmd); } } for (entryPtr = Tcl_FirstHashEntry(childTablePtr, &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) { Tcl_Namespace *childNsPtr = (Tcl_Namespace *) Tcl_GetHashValue(entryPtr); DeleteNsfProcs(interp, childNsPtr); } } /* *---------------------------------------------------------------------- * * ClassHasSubclasses -- * * Check, whether the given class has subclasses. * * Results: * boolean * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ClassHasSubclasses(NsfClass *cl) nonnull(1); static int ClassHasSubclasses(NsfClass *cl) { assert(cl); return (cl->sub != NULL); } /* *---------------------------------------------------------------------- * * ClassHasInstances -- * * Check, whether the given class has instances. * * Results: * boolean * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ClassHasInstances(NsfClass *cl) nonnull(1); static int ClassHasInstances(NsfClass *cl) { Tcl_HashSearch hSrch; assert(cl); return (Tcl_FirstHashEntry(&cl->instances, &hSrch) != NULL); } /* *---------------------------------------------------------------------- * * ObjectHasChildren -- * * Check, whether the given object has children * * Results: * boolean * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ObjectHasChildren(NsfObject *object) nonnull(1); static int ObjectHasChildren(NsfObject *object) { Tcl_Namespace *ns = object->nsPtr; int result = 0; assert(object); if (ns) { Tcl_HashEntry *hPtr; Tcl_HashSearch hSrch; Tcl_HashTable *cmdTablePtr = Tcl_Namespace_cmdTablePtr(ns); for (hPtr = Tcl_FirstHashEntry(cmdTablePtr, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { Tcl_Command cmd = Tcl_GetHashValue(hPtr); NsfObject *childObject = NsfGetObjectFromCmdPtr(cmd); if (childObject) { result = 1; break; } } } return result; } /* *---------------------------------------------------------------------- * * FreeAllNsfObjectsAndClasses -- * * Destroy and free all objects and classes defined int the interp. * * Results: * None. * * Side effects: * Freeing memory. * *---------------------------------------------------------------------- */ static void FreeAllNsfObjectsAndClasses(Tcl_Interp *interp, NsfCmdList **instances) nonnull(1) nonnull(2); static void FreeAllNsfObjectsAndClasses(Tcl_Interp *interp, NsfCmdList **instances) { NsfCmdList *entry, *lastEntry; int deleted = 0; assert(interp); assert(instances); /*fprintf(stderr, "FreeAllNsfObjectsAndClasses in %p\n", interp);*/ RUNTIME_STATE(interp)->exitHandlerDestroyRound = NSF_EXITHANDLER_ON_PHYSICAL_DESTROY; /* * First delete all child commands of all objects, which are not * objects themselves. This will for example delete namespace * imported commands and objects and will resolve potential loops in * the dependency graph. The result is a plain object/class tree. */ for (entry = *instances; entry; entry = entry->nextPtr) { NsfObject *object = (NsfObject *)entry->clorobj; /* delete per-object methods */ if (object && object->nsPtr) { Tcl_HashEntry *hPtr; Tcl_HashSearch hSrch; for (hPtr = Tcl_FirstHashEntry(Tcl_Namespace_cmdTablePtr(object->nsPtr), &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { Tcl_Command cmd = Tcl_GetHashValue(hPtr); if (cmd) { if (CmdIsNsfObject(cmd)) { AliasDeleteObjectReference(interp, cmd); continue; } Tcl_DeleteCommandFromToken(interp, cmd); deleted ++; } } } /* * Delete class methods; these methods might have aliases (dependencies) to * objects, which will resolved this way. */ if (object && NsfObjectIsClass(object)) { Tcl_HashEntry *hPtr; Tcl_HashSearch hSrch; for (hPtr = Tcl_FirstHashEntry(Tcl_Namespace_cmdTablePtr(((NsfClass *)object)->nsPtr), &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { Tcl_Command cmd = Tcl_GetHashValue(hPtr); if (cmd && CmdIsNsfObject(cmd)) { AliasDeleteObjectReference(interp, cmd); continue; } } } } /*fprintf(stderr, "deleted %d cmds\n", deleted);*/ /* * Finally delete the object/class tree in a bottom up manner, * deleting all objects without dependencies first. Finally, only * the root classes of the object system will remain, which are * deleted separately. */ while (1) { /* * Delete all plain objects without dependencies. */ deleted = 0; for (entry = *instances, lastEntry = NULL; entry; lastEntry = entry, entry = entry->nextPtr) { NsfObject *object = (NsfObject *)entry->clorobj; /* * The list of the instances should contain only alive objects, without * duplicates. We would recognize duplicates since a deletion of one * object would trigger the CMD_IS_DELETED flag of the cmdPtr of the * duplicate. */ assert((Tcl_Command_flags(entry->cmdPtr) & CMD_IS_DELETED) == 0); if (object && !NsfObjectIsClass(object) && !ObjectHasChildren(object)) { /*fprintf(stderr, "check %p obj->flags %.6x cmd %p deleted %d\n", object, object->flags, entry->cmdPtr, Tcl_Command_flags(entry->cmdPtr) & CMD_IS_DELETED); */ assert(object->id); /*fprintf(stderr, " ... delete object %s %p, class=%s id %p ns %p\n", ObjectName(object), object, ClassName(object->cl), object->id, object->nsPtr);*/ FreeUnsetTraceVariable(interp, object); FinalObjectDeletion(interp, object); if (entry == *instances) { *instances = entry->nextPtr; CmdListDeleteCmdListEntry(entry, NULL); entry = *instances; } else { lastEntry->nextPtr = entry->nextPtr; CmdListDeleteCmdListEntry(entry, NULL); entry = lastEntry; } assert(entry); deleted++; } } /*fprintf(stderr, "deleted %d Objects without dependencies\n", deleted);*/ if (deleted > 0) { continue; } /* * Delete all classes without dependencies. */ for (entry = *instances, lastEntry = NULL; entry; lastEntry = entry, entry = entry->nextPtr) { NsfClass *cl = entry->clorobj; assert(cl); if (!NsfObjectIsClass(&cl->object)) { continue; } /*fprintf(stderr, "cl key = %s %p\n", ClassName(cl), cl); */ if (cl && !ObjectHasChildren((NsfObject *)cl) && !ClassHasInstances(cl) && !ClassHasSubclasses(cl) && !IsBaseClass(&cl->object) ) { /*fprintf(stderr, " ... delete class %s %p\n", ClassName(cl), cl); */ assert(cl->object.id); FreeUnsetTraceVariable(interp, &cl->object); FinalObjectDeletion(interp, &cl->object); if (entry == *instances) { *instances = entry->nextPtr; /*fprintf(stderr, "... delete first entry %p\n", entry);*/ CmdListDeleteCmdListEntry(entry, NULL); entry = *instances; } else { /*fprintf(stderr, "... delete entry %p\n", entry);*/ lastEntry->nextPtr = entry->nextPtr; CmdListDeleteCmdListEntry(entry, NULL); entry = lastEntry; } deleted++; } } /*fprintf(stderr, "deleted %d Classes\n", deleted);*/ if (deleted == 0) { int reclassed = 0; /* * Final check. If there are no cyclical dependencies, we should have * now just the the base classes left. If this is not the case, reclass * the remaining objects to their base classes, and set the superClasses * to the most general superclass. */ for (entry = *instances; entry; entry = entry->nextPtr) { NsfObject *object = (NsfObject *)entry->clorobj; NsfClass *baseClass; NsfObjectSystem *osPtr; if (NsfObjectIsClass(object) && IsBaseClass(object)) { continue; } osPtr = GetObjectSystem(object); /* * For classes, check the superclass hierarchy. */ if (NsfObjectIsClass(object)) { NsfClass *cl = (NsfClass *)object; NsfClasses *sc; for (sc = cl->super; sc; sc = sc->nextPtr) { if (sc->cl != osPtr->rootClass) { Tcl_Obj *objectName = osPtr->rootClass->object.cmdName; SuperclassAdd(interp, cl, 1, &objectName, objectName); reclassed ++; break; } } } /* * In all cases, straigthen the class to the base case. */ baseClass = NsfObjectIsClass(object) ? osPtr->rootMetaClass : osPtr->rootClass; if (object->cl != baseClass) { ChangeClass(interp, object, baseClass); reclassed ++; } } /*fprintf(stderr, "We have reclassed %d objects\n", reclassed);*/ if (reclassed == 0) { break; } } } } #endif /* DO_CLEANUP */ /* * Exit Handler */ static void ExitHandler(ClientData clientData) nonnull(1); static void ExitHandler(ClientData clientData) { Tcl_Interp *interp = (Tcl_Interp *)clientData; int i, flags; NsfRuntimeState *rst = RUNTIME_STATE(interp); assert(clientData); /*fprintf(stderr, "ExitHandler\n");*/ /* * Don't use exit handler, if the interpreter is already destroyed. * Call to exit handler comes after freeing namespaces, commands, etc. * e.g. TK calls Tcl_DeleteInterp directly, if Window is killed. */ /* * Ahem ... * * Since we *must* be sure that our destroy methods will run * we must *cheat* (I mean CHEAT) here: we flip the interp * flag, saying, "hey boy, you're not deleted any more". * After our handlers are done, we restore the old state... * All this is needed so we can do an eval in the interp which * is potentially marked for delete when we start working here. * * I know, I know, this is not really elegant. But... I'd need a * standard way of invoking some code at interpreter delete time * but JUST BEFORE the actual deletion process starts. Sadly, * there is no such hook in Tcl as of Tcl8.4.*, that I know of. * * So, for the rest of procedure, assume the interp is alive ! */ flags = Tcl_Interp_flags(interp); Tcl_Interp_flags(interp) &= ~DELETED; CallStackPopAll(interp); if (rst->exitHandlerDestroyRound == NSF_EXITHANDLER_OFF) { NsfFinalizeCmd(interp, 0); } /* Must be before freeing of NsfGlobalObjs */ NsfShadowTclCommands(interp, SHADOW_UNLOAD); MEM_COUNT_FREE("Tcl_InitHashTable", &rst->activeFilterTablePtr); Tcl_DeleteHashTable(&rst->activeFilterTablePtr); /* free global objects */ for (i = 0; i < nr_elements(NsfGlobalStrings); i++) { DECR_REF_COUNT(NsfGlobalObjs[i]); } NsfStringIncrFree(&rst->iss); /* * Free all data in the pointer converter. */ Nsf_PointerExit(interp); #if defined(NSF_PROFILE) NsfProfileFree(interp); #endif FREE(Tcl_Obj**, NsfGlobalObjs); #if defined(TCL_MEM_DEBUG) TclDumpMemoryInfo((ClientData) stderr, 0); Tcl_DumpActiveMemory("./nsfActiveMem"); /* Tcl_Eval(interp, "puts {checkmem to checkmemFile}; checkmem checkmemFile"); */ #endif /* * Free runtime state. */ ckfree((char *) RUNTIME_STATE(interp)); #if USE_ASSOC_DATA Tcl_DeleteAssocData(interp, "NsfRuntimeState"); #else Tcl_Interp_globalNsPtr(interp)->clientData = NULL; #endif #if defined(NSF_MEM_COUNT) && !defined(PRE86) /* * When raising an error, the Tcl_Objs on the error stack and in the * inner context are refCount-incremented. When Tcl exits, it does normally * not perform the according decrementing. We perform here a manual * decrementing and reset these lists. */ { Interp *iPtr = (Interp *) interp; if (iPtr->innerContext) { Tcl_DecrRefCount(iPtr->errorStack); iPtr->errorStack = Tcl_NewListObj(0, NULL); Tcl_IncrRefCount(iPtr->errorStack); Tcl_DecrRefCount(iPtr->innerContext); iPtr->innerContext = Tcl_NewListObj(0, NULL); Tcl_IncrRefCount(iPtr->innerContext); } } #endif Tcl_Interp_flags(interp) = flags; Tcl_Release(interp); MEM_COUNT_RELEASE(); } #if defined(TCL_THREADS) /* * Gets activated at thread-exit */ static void Nsf_ThreadExitProc(ClientData clientData) nonnull(1); static void Nsf_ThreadExitProc(ClientData clientData) { /*fprintf(stderr, "+++ Nsf_ThreadExitProc\n");*/ void Nsf_ExitProc(ClientData clientData); assert(clientData); Tcl_DeleteExitHandler(Nsf_ExitProc, clientData); ExitHandler(clientData); } #endif /* * Gets activated at application-exit */ void Nsf_ExitProc(ClientData clientData) nonnull(1); void Nsf_ExitProc(ClientData clientData) { assert(clientData); /*fprintf(stderr, "+++ Nsf_ExitProc\n");*/ #if defined(TCL_THREADS) Tcl_DeleteThreadExitHandler(Nsf_ThreadExitProc, clientData); #endif ExitHandler(clientData); } /* * Registers thread/application exit handlers. */ static void RegisterExitHandlers(ClientData clientData) nonnull(1); static void RegisterExitHandlers(ClientData clientData) { assert(clientData); Tcl_Preserve(clientData); #if defined(TCL_THREADS) Tcl_CreateThreadExitHandler(Nsf_ThreadExitProc, clientData); #endif Tcl_CreateExitHandler(Nsf_ExitProc, clientData); } /* * Tcl extension initialization routine */ #if 0 #include #endif int Nsf_Init(Tcl_Interp *interp) nonnull(1); int Nsf_Init(Tcl_Interp *interp) { static NsfMutex initMutex = 0; ClientData runtimeState; NsfRuntimeState *rst; int result, i; #ifdef NSF_BYTECODE /*NsfCompEnv *interpstructions = NsfGetCompEnv();*/ #endif #ifdef USE_TCL_STUBS static int stubsInitialized = 0; #endif assert(interp); #if 0 ProfilerStart("profiler"); #endif #ifdef USE_TCL_STUBS /* * Since the stub-tables are initialized globally (not per interp), we want * to initialize these only once. The read operation on "stubsInitialized" * is a potentially dirty read. However, we can't use a mutex lock around * this, since Tcl_MutexLock() requires (at least on some platforms) * initialized stub-tables. The dirty read of stubsInitialized is not so * invasive as the dirty reads caused by overwriting the stub tables. * * NsfMutexLock(&stubFlagMutex); * ... * NsfMutexUnlock(&stubFlagMutex); */ if (stubsInitialized == 0) { if (Tcl_InitStubs(interp, "8.5", 0) == NULL) { return TCL_ERROR; } if (Tcl_TomMath_InitStubs(interp, "8.5") == NULL) { return TCL_ERROR; } stubsInitialized = 1; } #endif #if defined(TCL_MEM_DEBUG) TclDumpMemoryInfo((ClientData) stderr, 0); #endif /* * Runtime State stored in the client data of the Interp's global namespace * in order to avoid global state information. All fields are per default * set to zero. */ runtimeState = ckalloc(sizeof(NsfRuntimeState)); memset(runtimeState, 0, sizeof(NsfRuntimeState)); #if USE_ASSOC_DATA Tcl_SetAssocData(interp, "NsfRuntimeState", NULL, runtimeState); #else Tcl_Interp_globalNsPtr(interp)->clientData = runtimeState; #endif /* * If MEM_COUNT is activated, the tables have to be initialized before the * first call to the MEM_COUNT macros (including e.g. INCR_REF_COUNT), but * it requires that the runtimeState is already associated with the interp. */ MEM_COUNT_INIT(); /* * Init global variables for Tcl_Obj types. */ NsfMutexLock(&initMutex); Nsf_OT_byteCodeType = Tcl_GetObjType("bytecode"); assert(Nsf_OT_byteCodeType); Nsf_OT_tclCmdNameType = Tcl_GetObjType("cmdName"); assert(Nsf_OT_tclCmdNameType); Nsf_OT_listType = Tcl_GetObjType("list"); assert(Nsf_OT_listType); Nsf_OT_intType = Tcl_GetObjType("int"); assert(Nsf_OT_intType); Nsf_OT_doubleType = Tcl_GetObjType("double"); assert(Nsf_OT_doubleType); NsfMutexUnlock(&initMutex); /* * Initialize the pointer converter, the enumeration types and cmd * definitions tables and load it with the generated information for * introspection. */ Nsf_PointerInit(interp); Nsf_EnumerationTypeInit(interp); result = Nsf_EnumerationTypeRegister(interp, enumeratorConverterEntries); if (unlikely(result != TCL_OK)) { return result; } Nsf_CmdDefinitionInit(interp); Nsf_CmdDefinitionRegister(interp, method_definitions); /* fprintf(stderr, "SIZES: obj=%d, tcl_obj=%d, DString=%d, class=%d, namespace=%d, command=%d, HashTable=%d\n", sizeof(NsfObject), sizeof(Tcl_Obj), sizeof(Tcl_DString), sizeof(NsfClass), sizeof(Namespace), sizeof(Command), sizeof(Tcl_HashTable)); */ #if defined(NSF_PROFILE) NsfProfileInit(interp); #endif rst = RUNTIME_STATE(interp); rst->doFilters = 1; rst->doCheckResults = 1; rst->doCheckArguments = NSF_ARGPARSE_CHECK; #if defined(NSF_STACKCHECK) { int someVar; /* * Note that Nsf_Init() is called typically via a package require, which * is therefore not really the bottom of the stack, but just a first * approximization. */ rst->bottomOfStack = &someVar; rst->maxStack = rst->bottomOfStack; } #endif /* * Check, if the namespace exists, otherwise create it. */ rst->NsfNS = Tcl_FindNamespace(interp, "::nsf", NULL, TCL_GLOBAL_ONLY); if (rst->NsfNS == NULL) { rst->NsfNS = Tcl_CreateNamespace(interp, "::nsf", NULL, (Tcl_NamespaceDeleteProc *)NULL); } MEM_COUNT_ALLOC("TclNamespace", rst->NsfNS); /* * Init an empty, faked proc structure in the RUNTIME state. */ rst->fakeProc.iPtr = (Interp *)interp; rst->fakeProc.refCount = 1; rst->fakeProc.cmdPtr = NULL; rst->fakeProc.bodyPtr = NULL; rst->fakeProc.numArgs = 0; rst->fakeProc.numCompiledLocals = 0; rst->fakeProc.firstLocalPtr = NULL; rst->fakeProc.lastLocalPtr = NULL; /* * NsfClasses in separate Namespace / Objects */ rst->NsfClassesNS = Tcl_CreateNamespace(interp, "::nsf::classes", NULL, (Tcl_NamespaceDeleteProc *)NULL); MEM_COUNT_ALLOC("TclNamespace", rst->NsfClassesNS); /* * Cache interpreters proc interpretation functions */ rst->objInterpProc = TclGetObjInterpProc(); rst->exitHandlerDestroyRound = NSF_EXITHANDLER_OFF; RegisterExitHandlers(interp); NsfStringIncrInit(&RUNTIME_STATE(interp)->iss); /* * initialize global Tcl_Obj */ NsfGlobalObjs = NEW_ARRAY(Tcl_Obj*, nr_elements(NsfGlobalStrings)); for (i = 0; i < nr_elements(NsfGlobalStrings); i++) { NsfGlobalObjs[i] = Tcl_NewStringObj(NsfGlobalStrings[i], -1); INCR_REF_COUNT(NsfGlobalObjs[i]); } Tcl_InitHashTable(&rst->activeFilterTablePtr, TCL_STRING_KEYS); MEM_COUNT_ALLOC("Tcl_InitHashTable", &rst->activeFilterTablePtr); /* * Create namespaces for the different command types. */ Tcl_CreateNamespace(interp, "::nsf::cmd", 0, (Tcl_NamespaceDeleteProc *)NULL); for (i = 0; i < nr_elements(method_command_namespace_names); i++) { Tcl_CreateNamespace(interp, method_command_namespace_names[i], 0, (Tcl_NamespaceDeleteProc *)NULL); } /* * Create all method commands (will use the namespaces above). */ for (i = 0; i < nr_elements(method_definitions)-1; i++) { Tcl_CreateObjCommand(interp, method_definitions[i].methodName, method_definitions[i].proc, 0, 0); } /* * Create Shadowed Tcl cmds: */ result = NsfShadowTclCommands(interp, SHADOW_LOAD); if (unlikely(result != TCL_OK)) { return result; } /* * Create new Tcl cmds: */ #ifdef NSF_BYTECODE instructions[INST_NEXT].cmdPtr = (Command *) #endif Tcl_CreateObjCommand(interp, "::nsf::xotclnext", NsfNextObjCmd, 0, 0); #ifdef NSF_BYTECODE instructions[INST_SELF].cmdPtr = (Command *)Tcl_FindCommand(interp, "::nsf::current", NULL, TCL_GLOBAL_ONLY); #endif /*Tcl_CreateObjCommand(interp, "::nsf::K", NsfKObjCmd, 0, 0);*/ #ifdef NSF_BYTECODE NsfBytecodeInit(); #endif NsfReportVars(interp); Tcl_AddInterpResolvers(interp,"nsf", (Tcl_ResolveCmdProc *)InterpColonCmdResolver, InterpColonVarResolver, (Tcl_ResolveCompiledVarProc *)InterpCompiledColonVarResolver); rst->colonCmd = Tcl_FindCommand(interp, "::nsf::colon", NULL, TCL_GLOBAL_ONLY); /* * Tcl occasionally resolves a proc's cmd structure (e.g., in * [info frame /number/] or TclInfoFrame()) without * verification. However, NSF non-proc frames, in particular * initcmd blocks, point to the fakeProc structure which does not * contain a initialized Command pointer. For now, we default to * an internal command. However, we might have to revisit this decision * as non-proc frames (e.g., initcmds) report a "proc" entry * for c-based functions with a proc scope, such as "::nsf::colon"), * which can lead to confusions. "proc" does not mean "tcp proc", * but an entry with a proc frame for local vars. */ rst->fakeProc.cmdPtr = (Command *)RUNTIME_STATE(interp)->colonCmd; { /* * The file "predefined.h" contains some methods and library procs * implemented in Tcl - they could go in a nsf.tcl file, but * they're embedded here with Tcl_Eval to avoid the need to * carry around a separate file at runtime. */ #include "predefined.h" /* fprintf(stderr, "predefined=<<%s>>\n", cmd);*/ if (Tcl_Eval(interp, cmd) != TCL_OK) { static char cmd[] = "puts stderr \"Error in predefined code\n\ $::errorInfo\""; Tcl_EvalEx(interp, cmd, -1, 0); return TCL_ERROR; } } #ifndef AOL_SERVER /* the AOL server uses a different package loading mechanism */ # ifdef COMPILE_NSF_STUBS Tcl_PkgProvideEx(interp, "nsf", PACKAGE_VERSION, &nsfStubs); # else Tcl_PkgProvide(interp, "nsf", PACKAGE_VERSION); # endif #endif /* * Obtain type for parsed var name. */ if (Nsf_OT_parsedVarNameType == NULL) { Tcl_Obj *varNameObj = Tcl_NewStringObj("::nsf::version",-1); Var *arrayPtr; INCR_REF_COUNT(varNameObj); TclObjLookupVar(interp, varNameObj, NULL, 0, "access", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr); Nsf_OT_parsedVarNameType = varNameObj->typePtr; assert(Nsf_OT_parsedVarNameType); DECR_REF_COUNT(varNameObj); } #if !defined(TCL_THREADS) if ((Tcl_GetVar2(interp, "tcl_platform", "threaded", TCL_GLOBAL_ONLY) != NULL)) { /* a non threaded version of nsf is loaded into a threaded environment */ fprintf(stderr, "\n A non threaded version of the Next Scripting Framework " "is loaded into threaded environment.\n" "Please reconfigure nsf with --enable-threads!\n\n\n"); } #endif Tcl_ResetResult(interp); Tcl_SetIntObj(Tcl_GetObjResult(interp), 1); return TCL_OK; } EXTERN int Nsf_SafeInit(Tcl_Interp *interp) nonnull(1); EXTERN int Nsf_SafeInit(Tcl_Interp *interp) { assert(interp); /*** dummy for now **/ return Nsf_Init(interp); } /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsf.decls000066400000000000000000000160351242365656200143140ustar00rootroot00000000000000# -*- Tcl -*- # # nx.decls -- # # This file contains the declarations for all supported public # functions that are exported by the Next Scripting Framework # (NSF) library via stubs tables. This file is used to # generate nsfDecls.h. # # Copyright (C) 1999-2014 Gustaf Neumann (a, b) # Copyright (C) 1999-2007 Uwe Zdun (a, b) # # (a) University of Essen # Specification of Software Systems # Altendorferstrasse 97-101 # D-45143 Essen, Germany # # (b) Vienna University of Economics and Business # Institute of Information Systems and New Media # A-1020, Welthandelsplatz 1 # Vienna, Austria # # This work is licensed under the MIT License http://www.opensource.org/licenses/MIT # # Copyright: # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # library nsf interface nsf hooks {nsfInt} # Declare each of the functions in the public Tcl interface. Note that # the an index should never be reused for a different function in order # to preserve backwards compatibility. declare 0 generic { int Nsf_Init(Tcl_Interp *interp) } # 1 is reserved for safe init #declare 1 generic { # int Nsf_SafeInit(Tcl_Interp *interp) #} declare 2 generic { struct Nsf_Class *NsfIsClass(Tcl_Interp *interp, ClientData cd) } declare 3 generic { struct Nsf_Object *NsfGetObject(Tcl_Interp *interp, CONST char *name) } declare 4 generic { struct Nsf_Class *NsfGetClass(Tcl_Interp *interp, CONST char *name) } declare 5 generic { int NsfDeleteObject(Tcl_Interp *interp, struct Nsf_Object *object) } declare 6 generic { int NsfRemoveObjectMethod(Tcl_Interp *interp, struct Nsf_Object *object, CONST char *nm) } declare 7 generic { int NsfRemoveClassMethod(Tcl_Interp *interp, struct Nsf_Class *cl, CONST char *nm) } declare 8 generic { Tcl_Obj *Nsf_ObjSetVar2(struct Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, Tcl_Obj *value, unsigned int flags) } declare 9 generic { Tcl_Obj *Nsf_ObjGetVar2(struct Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, unsigned int flags) } declare 10 generic { int Nsf_UnsetVar2(struct Nsf_Object *object, Tcl_Interp *interp, CONST char *name1, CONST char *name2, unsigned int flags) } declare 11 generic { void NsfDStringPrintf(Tcl_DString *dsPtr, CONST char *fmt, va_list apSrc) } declare 12 generic { int NsfPrintError(Tcl_Interp *interp, CONST char *fmt, ...) } declare 13 generic { int NsfErrInProc (Tcl_Interp *interp, Tcl_Obj *objName, Tcl_Obj *clName, CONST char *procName) } declare 14 generic { int NsfObjErrType(Tcl_Interp *interp, CONST char *context, Tcl_Obj *value, CONST char *type, Nsf_Param CONST *pPtr) } declare 15 generic { void NsfStackDump (Tcl_Interp *interp) } declare 16 generic { void NsfSetObjClientData(Tcl_Interp *interp, Nsf_Object *object, ClientData data) } declare 17 generic { ClientData NsfGetObjClientData(Tcl_Interp *interp, Nsf_Object *object) } declare 18 generic { void NsfSetClassClientData(Tcl_Interp *interp, Nsf_Class *cl, ClientData data) } declare 19 generic { ClientData NsfGetClassClientData(Tcl_Interp *interp, Nsf_Class *cl) } declare 20 generic { void NsfRequireObjNamespace(Tcl_Interp *interp, Nsf_Object *object) } declare 21 generic { int NsfCallMethodWithArgs(Tcl_Interp *interp, Nsf_Object *object, Tcl_Obj *method, Tcl_Obj *arg, int objc, Tcl_Obj *CONST objv[], unsigned int flags) } declare 22 generic { int NsfAddObjectMethod(Tcl_Interp *interp, struct Nsf_Object *object, CONST char *nm, Tcl_ObjCmdProc *proc, ClientData cd, Tcl_CmdDeleteProc *dp, unsigned int flags) } declare 23 generic { int NsfAddClassMethod(Tcl_Interp *interp, struct Nsf_Class *cl, CONST char *nm, Tcl_ObjCmdProc *proc, ClientData cd, Tcl_CmdDeleteProc *dp, unsigned int flags) } declare 24 generic { int NsfCreate(Tcl_Interp *in, Nsf_Class *class, Tcl_Obj *name, int objc, Tcl_Obj *CONST objv[]) } declare 25 generic { int Nsf_ArgumentParse(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Nsf_Object *object, Tcl_Obj *procNameObj, Nsf_Param CONST *paramPtr, int nrParams, int serial, unsigned int processFlags, Nsf_ParseContext *pcPtr) } declare 26 generic { void NsfLog(Tcl_Interp *interp, int requiredLevel, CONST char *fmt, ...) } declare 27 generic { int Nsf_PointerAdd(Tcl_Interp *interp, char *buffer, CONST char *typeName, void *valuePtr) } declare 28 generic { int Nsf_PointerDelete(CONST char *key, void *valuePtr, int free) } declare 29 generic { int Nsf_PointerTypeRegister(Tcl_Interp *interp, CONST char* typeName, int *counterPtr) } declare 30 generic { int Nsf_ConvertToBoolean(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) } declare 31 generic { int Nsf_ConvertToClass(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) } declare 32 generic { int Nsf_ConvertToInt32(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) } declare 33 generic { int Nsf_ConvertToInteger(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) } declare 34 generic { int Nsf_ConvertToObject(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) } declare 35 generic { int Nsf_ConvertToPointer(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) } declare 36 generic { int Nsf_ConvertToString(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) } declare 37 generic { int Nsf_ConvertToTclobj(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) } declare 38 generic { int Nsf_EnumerationTypeRegister(Tcl_Interp *interp, Nsf_EnumeratorConverterEntry *typeRecords) } declare 39 generic { int Nsf_CmdDefinitionRegister(Tcl_Interp *interp, Nsf_methodDefinition *definitionRecords) } generic/nsf.h000066400000000000000000000324621242365656200134530ustar00rootroot00000000000000/* * Next Scripting Framework * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #ifndef _nsf_h_ #define _nsf_h_ #include "tcl.h" #undef TCL_STORAGE_CLASS #ifdef BUILD_nsf # define TCL_STORAGE_CLASS DLLEXPORT #else # ifdef USE_NSF_STUBS # define TCL_STORAGE_CLASS # else # define TCL_STORAGE_CLASS DLLIMPORT # endif #endif /* * prevent old TCL-versions */ #if TCL_MAJOR_VERSION < 8 # error Tcl distribution is TOO OLD, we require at least tcl8.5 #endif #if TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION<5 # error Tcl distribution is TOO OLD, we require at least tcl8.5 #endif #if TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION<6 # define PRE86 #endif #if defined(PRE86) # define CONST86 # define Tcl_GetErrorLine(interp) (interp)->errorLine #else # define NRE #endif /* * Feature activation/deactivation */ /* * The following features are controlled via * configure flags * * --with-dtrace * --enable-development * --enable-profile * --enable-memcount=yes|trace * --enable-assertions * * Are we developing? * #define NSF_DEVELOPMENT 1 * * Activate/deactivate profiling information * #define NSF_PROFILE 1 * * Compile with dtrace support * #define NSF_DTRACE 1 * * Scripting level assertions * #define NSF_WITH_ASSERTIONS 1 * * Activate/deactivate memory tracing * #define NSF_MEM_TRACE 1 #define NSF_MEM_COUNT 1 */ /* Activate bytecode support #define NSF_BYTECODE */ /* Activate/deactivate C-level assert() Activated automatically when NSF_DEVELOPMENT is set #define NDEBUG 1 */ /* Experimental language feature #define NSF_WITH_INHERIT_NAMESPACES 1 */ #define NSF_WITH_OS_RESOLVER 1 #define NSF_WITH_VALUE_WARNINGS 1 /* turn tracing output on/off #define NSFOBJ_TRACE 1 #define NAMESPACE_TRACE 1 #define OBJDELETION_TRACE 1 #define STACK_TRACE 1 #define PARSE_TRACE 1 #define PARSE_TRACE_FULL 1 #define CONFIGURE_ARGS_TRACE 1 #define TCL_STACK_ALLOC_TRACE 1 #define VAR_RESOLVER_TRACE 1 #define CMD_RESOLVER_TRACE 1 #define NRE_CALLBACK_TRACE 1 #define METHOD_OBJECT_TRACE 1 #define NSF_LINEARIZER_TRACE 1 #define NSF_STACKCHECK 1 #define NSF_CLASSLIST_PRINT 1 */ #define PER_OBJECT_PARAMETER_CACHING 1 /* * Sanity checks and dependencies for optional compile flags */ #if defined(PARSE_TRACE_FULL) # define PARSE_TRACE 1 #endif #ifdef NSF_MEM_COUNT # define DO_FULL_CLEANUP 1 #endif #ifdef AOL_SERVER # ifndef TCL_THREADS # define TCL_THREADS # endif #endif #ifdef TCL_THREADS # define DO_CLEANUP #endif #ifdef DO_FULL_CLEANUP # define DO_CLEANUP #endif #ifdef NSF_LINEARIZER_TRACE # if !defined(NSF_CLASSLIST_PRINT) # define NSF_CLASSLIST_PRINT 1 # endif #endif #ifdef NSF_DTRACE # define NSF_DTRACE_METHOD_RETURN_PROBE(cscPtr,retCode) \ if (cscPtr->cmdPtr && NSF_DTRACE_METHOD_RETURN_ENABLED()) { \ NSF_DTRACE_METHOD_RETURN(ObjectName(cscPtr->self), \ cscPtr->cl ? ClassName(cscPtr->cl) : ObjectName(cscPtr->self), \ (char *)cscPtr->methodName, \ retCode); \ } #else # define NSF_DTRACE_METHOD_RETURN_PROBE(cscPtr,retCode) {} #endif #ifdef NSF_DEVELOPMENT /* * The activation counts checking is best performed via the MEM_COUNT * macros. In case, the MEM_COUNT macros indicate a problem, setting * CHECK_ACTIVATION_COUNTS might help to locate the problem more * precisely. The CHECK_ACTIVATION_COUNTS tester might however still * report false positives. */ /*# define CHECK_ACTIVATION_COUNTS 1*/ # define NsfCleanupObject(object,string) \ /*fprintf(stderr, "NsfCleanupObject %p %s\n",object,string);*/ \ NsfCleanupObject_(object) # define CscFinish(interp,cscPtr,retCode,string) \ /*fprintf(stderr, "CscFinish %p %s\n",cscPtr,string); */ \ NSF_DTRACE_METHOD_RETURN_PROBE(cscPtr,retCode); \ CscFinish_(interp, cscPtr) #else # define NDEBUG 1 # define NsfCleanupObject(object,string) \ NsfCleanupObject_(object) # define CscFinish(interp,cscPtr,retCode,string) \ NSF_DTRACE_METHOD_RETURN_PROBE(cscPtr,retCode); \ CscFinish_(interp, cscPtr) #endif #if defined(NSF_MEM_TRACE) && !defined(NSF_MEM_COUNT) # define NSF_MEM_COUNT 1 #endif #if defined(NSF_PROFILE) || defined(NSF_DTRACE) # define CscInit(cscPtr, object, cl, cmd, frametype, flags, method) \ CscInit_(cscPtr, object, cl, cmd, frametype, flags); cscPtr->methodName = (method); #else # define CscInit(cscPtr, object, cl, cmd, frametype, flags, methodName) \ CscInit_(cscPtr, object, cl, cmd, frametype, flags) #endif #if !defined(CHECK_ACTIVATION_COUNTS) # define CscListAdd(interp, cscPtr) # define CscListRemove(interp, cscPtr, cscListPtr) #endif #if defined(TCL_THREADS) # define NsfMutex Tcl_Mutex # define NsfMutexLock(a) Tcl_MutexLock(a) # define NsfMutexUnlock(a) Tcl_MutexUnlock(a) #else # define NsfMutex int # define NsfMutexLock(a) (*(a))++ # define NsfMutexUnlock(a) (*(a))-- #endif /* * A special definition used to allow this header file to be included * in resource files so that they can get obtain version information from * this file. Resource compilers don't like all the C stuff, like typedefs * and procedure declarations, that occur below. */ #ifndef RC_INVOKED /* * The structures Nsf_Object and Nsf_Class define mostly opaque * data structures for the internal use structures NsfObject and * NsfClass (both defined in NsfInt.h). Modification of elements * visible elements must be mirrored in both incarnations. * * Warning: These structures are just containing a few public * fields. These structures must not be used for querying the size or * allocating the data structures. */ typedef struct Nsf_Object { Tcl_Obj *cmdName; } Nsf_Object; typedef struct Nsf_Class { struct Nsf_Object object; } Nsf_Class; typedef struct Nsf_ParseContext { ClientData *clientData; int status; } Nsf_ParseContext; struct Nsf_Param; typedef int (Nsf_TypeConverter)(Tcl_Interp *interp, Tcl_Obj *obj, struct Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); typedef struct { Nsf_TypeConverter *converter; CONST char *domain; } Nsf_EnumeratorConverterEntry; EXTERN Nsf_TypeConverter Nsf_ConvertToBoolean, Nsf_ConvertToClass, Nsf_ConvertToInteger, Nsf_ConvertToInt32, Nsf_ConvertToObject, Nsf_ConvertToParameter, Nsf_ConvertToString, Nsf_ConvertToSwitch, Nsf_ConvertToTclobj, Nsf_ConvertToPointer; typedef struct Nsf_Param { char *name; unsigned int flags; int nrArgs; Nsf_TypeConverter *converter; Tcl_Obj *converterArg; Tcl_Obj *defaultValue; CONST char *type; Tcl_Obj *nameObj; Tcl_Obj *converterName; Tcl_Obj *paramObj; Tcl_Obj *slotObj; Tcl_Obj *method; } Nsf_Param; /* Argument parse processing flags */ #define NSF_ARGPARSE_CHECK 0x0001 #define NSF_ARGPARSE_FORCE_REQUIRED 0x0002 #define NSF_ARGPARSE_BUILTIN (NSF_ARGPARSE_CHECK|NSF_ARGPARSE_FORCE_REQUIRED) #define NSF_ARGPARSE_START_ZERO 0x0010 /* Special flags for process method arguments */ #define NSF_ARGPARSE_METHOD_PUSH 0x0100 /* flags for NsfParams */ #define NSF_ARG_REQUIRED 0x00000001 #define NSF_ARG_MULTIVALUED 0x00000002 #define NSF_ARG_NOARG 0x00000004 #define NSF_ARG_NOCONFIG 0x00000008 #define NSF_ARG_CURRENTLY_UNKNOWN 0x00000010 #define NSF_ARG_SUBST_DEFAULT 0x00000020 #define NSF_ARG_ALLOW_EMPTY 0x00000040 #define NSF_ARG_INITCMD 0x00000080 #define NSF_ARG_CMD 0x00000100 #define NSF_ARG_ALIAS 0x00000200 #define NSF_ARG_FORWARD 0x00000400 #define NSF_ARG_SWITCH 0x00000800 #define NSF_ARG_BASECLASS 0x00001000 #define NSF_ARG_METACLASS 0x00002000 #define NSF_ARG_HAS_DEFAULT 0x00004000 #define NSF_ARG_IS_CONVERTER 0x00008000 #define NSF_ARG_IS_ENUMERATION 0x00010000 #define NSF_ARG_CHECK_NONPOS 0x00020000 #define NSF_ARG_SET 0x00040000 #define NSF_ARG_WARN 0x00080000 #define NSF_ARG_UNNAMED 0x00100000 #define NSF_ARG_IS_RETURNVALUE 0x00200000 #define NSF_ARG_NODASHALNUM 0x00400000 #define NSF_ARG_SLOTSET 0x00800000 #define NSF_ARG_SLOTINITIALIZE 0x01000000 #undef __GNUC_PREREQ #if defined __GNUC__ && defined __GNUC_MINOR__ # define __GNUC_PREREQ(maj, min) \ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) #else # define __GNUC_PREREQ(maj, min) (0) #endif #if __GNUC_PREREQ(3, 3) # define NSF_nonnull(ARGS) __attribute__((__nonnull__(ARGS))) #else # define NSF_nonnull(ARGS) #endif /* unforunately, we can't combine NSF_attribute_format() with functions called via stubs */ #if __GNUC_PREREQ(3, 4) # define NSF_attribute_format(ARGS) __attribute__((format ARGS)) #else # define NSF_attribute_format(ARGS) #endif EXTERN int Nsf_ArgumentParse(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Nsf_Object *object, Tcl_Obj *procNameObj, Nsf_Param CONST *paramPtr, int nrParams, int serial, unsigned int processFlags, Nsf_ParseContext *pcPtr) NSF_nonnull(1) NSF_nonnull(3) NSF_nonnull(5) NSF_nonnull(10); EXTERN int NsfArgumentError(Tcl_Interp *interp, CONST char *errorMsg, Nsf_Param CONST *paramPtr, Tcl_Obj *cmdNameObj, Tcl_Obj *methodObj) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); EXTERN int NsfDispatchClientDataError(Tcl_Interp *interp, ClientData clientData, CONST char *what, CONST char *methodName) NSF_nonnull(1) NSF_nonnull(3) NSF_nonnull(4); EXTERN int NsfNoCurrentObjectError(Tcl_Interp *interp, CONST char *what) NSF_nonnull(1); EXTERN int NsfUnexpectedArgumentError(Tcl_Interp *interp, CONST char *argumentString, Nsf_Object *object, Nsf_Param CONST *paramPtr, Tcl_Obj *procNameObj) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4) NSF_nonnull(5); EXTERN int NsfUnexpectedNonposArgumentError(Tcl_Interp *interp, CONST char *argumentString, Nsf_Object *object, Nsf_Param CONST *currentParamPtr, Nsf_Param CONST *paramPtr, Tcl_Obj *procNameObj) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4) NSF_nonnull(5) NSF_nonnull(6); /* * logging */ #define NSF_LOG_NOTICE 2 #define NSF_LOG_WARN 1 #define NSF_LOG_INFO 0 EXTERN void NsfLog(Tcl_Interp *interp, int requiredLevel, CONST char *fmt, ...) NSF_nonnull(1) NSF_nonnull(3) NSF_attribute_format((printf,3,4)); /* * Nsf Pointer converter interface */ EXTERN int Nsf_PointerAdd(Tcl_Interp *interp, char *buffer, CONST char *typeName, void *valuePtr) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3) NSF_nonnull(4); EXTERN int Nsf_PointerDelete(CONST char *key, void *valuePtr, int free) NSF_nonnull(2); EXTERN void Nsf_PointerInit(Tcl_Interp *interp) NSF_nonnull(1); EXTERN void Nsf_PointerExit(Tcl_Interp *interp) NSF_nonnull(1); EXTERN void *Nsf_PointerTypeLookup(Tcl_Interp *interp, CONST char* typeName) NSF_nonnull(1) NSF_nonnull(2); EXTERN int Nsf_PointerTypeRegister(Tcl_Interp *interp, CONST char* typeName, int *counterPtr) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); /* * methodDefinition */ typedef struct Nsf_methodDefinition { CONST char *methodName; Tcl_ObjCmdProc *proc; int nrParameters; Nsf_Param paramDefs[12]; } Nsf_methodDefinition; /* * Nsf Enumeration type interface */ EXTERN int Nsf_EnumerationTypeRegister(Tcl_Interp *interp, Nsf_EnumeratorConverterEntry *typeRecords) NSF_nonnull(1) NSF_nonnull(2); /* * Nsf Cmd definition interface */ EXTERN int Nsf_CmdDefinitionRegister(Tcl_Interp *interp, Nsf_methodDefinition *definitionRecords) NSF_nonnull(1) NSF_nonnull(2); /* * Include the public function declarations that are accessible via * the stubs table. */ #if defined(NRE) # include "stubs8.6/nsfDecls.h" #else # include "stubs8.5/nsfDecls.h" #endif /* * Nsf_InitStubs is used by extensions that can be linked * against the nsf stubs library. If we are not using stubs * then this reduces to package require. */ #ifdef USE_NSF_STUBS # ifdef __cplusplus EXTERN "C" # endif CONST char * Nsf_InitStubs _ANSI_ARGS_((Tcl_Interp *interp, CONST char *version, int exact)); #else # define Nsf_InitStubs(interp, version, exact) \ Tcl_PkgRequire(interp, "nx", version, exact) #endif #endif /* RC_INVOKED */ /* #undef TCL_STORAGE_CLASS #define TCL_STORAGE_CLASS DLLIMPORT */ #endif /* _nsf_h_ */ generic/nsf.tcl000066400000000000000000000142211242365656200137770ustar00rootroot00000000000000# -*- Tcl -*- # # Define a basic set of predefined Tcl commands and definitions for # the Next Scripting Framework. This file will be transformed by # mk_predefined.tcl into "predefined.h", which in included in nsf.c. # # Copyright (C) 2009-2014 Gustaf Neumann # Copyright (C) 2010 Stefan Sobernig # namespace eval ::nsf { # # get frequenly used primitiva into the ::nsf namespace # # Symbols reused in the next scripting language namespace export \ next current self configure finalize interp is my relation dispatch namespace eval ::nsf::method::create {namespace export alias} # # support for method provide and method require # proc ::nsf::method::provide {require_name definition {script ""}} { set ::nsf::methodIndex($require_name) [list definition $definition script $script] } proc ::nsf::method::require {object name {per_object 0}} { # # On a method require, the optional script is evaluated and the # "definition" gets inserted # - on posiiton 1 the actual object # - on posiiton 2 optionally "-per-object" # # The definition cmd must return the method handle. # set key ::nsf::methodIndex($name) if {[info exists $key]} { array set "" [set $key] if {$(script) ne ""} { eval $(script) } if {$per_object} { set cmd [linsert $(definition) 1 -per-object] return [eval [linsert $cmd 1 $object]] } else { return [eval [linsert $(definition) 1 $object]] } } else { error "cannot require method $name for $object, method unknown" } } # # The following helper proc is used e.g. in OpenACS to pair # introspection with nsf::procs. # ::proc strip_proc_name {name} { if {[string match ::nsf::procs::* $name]} { return [string range $name 12 end] } elseif {[string match nsf::procs::* $name]} { return [string range $name 12 end] } else { return $name } } # # ::nsf::mixin # # Provide a similar interface as for ::nsf::method::create, ::nsf::method::alias, # etc.. Semantically, ::nsf::mixin behaves like a "mixin add", but # can be used as well for deleting the mixin list (empty last # argument). # ::nsf::proc ::nsf::mixin {object -per-object:switch classes} { set rel [expr {${per-object} ? "object-mixin" : "class-mixin"}] if {[lindex $classes 0] ne ""} { set oldSetting [::nsf::relation::get $object $rel] # use uplevel to avoid namespace surprises uplevel [list ::nsf::relation::set $object $rel [linsert $oldSetting 0 $classes]] } else { uplevel [list ::nsf::relation::set $object $rel ""] } } # # provide some popular methods for "method require" # ::nsf::method::provide autoname {::nsf::method::alias autoname ::nsf::methods::object::autoname} ::nsf::method::provide exists {::nsf::method::alias exists ::nsf::methods::object::exists} ::nsf::method::provide volatile {::nsf::method::alias volatile ::nsf::methods::object::volatile} ###################################################################### # unknown handler for objects and classes # proc ::nsf::object::unknown {name} { foreach {key handler} [array get ::nsf::object::unknown] { set result [uplevel [list {*}$handler $name]] if {$result ne ""} { return $result } } return "" } namespace eval ::nsf::object::unknown { proc add {key handler} {set ::nsf::object::unknown($key) $handler} proc get {key} {return $::nsf::object::unknown($key)} proc delete {key} {array unset ::nsf::object::unknown($key)} proc keys {} {array names ::nsf::object::unknown} } # Example unknown handler: # ::nsf::object::unknown::add xotcl {::xotcl::Class __unknown} namespace eval ::nsf::argument {} proc ::nsf::argument::unknown {args} { #puts stderr "??? ::nsf::argument::unknown <$args> [info frame -1]" return "" } ###################################################################### # exit handlers # proc ::nsf::exithandler {args} { lassign $args op value switch $op { set {::proc ::nsf::__exithandler {} $value} get {::info body ::nsf::__exithandler} unset {proc ::nsf::__exithandler args {;}} default {error "syntax: ::nsf::exithandler $::nsf::parameter::syntax(::nsf::exithandler)"} } } # initialize exit handler ::nsf::exithandler unset # # logger # if {[info command ::ns_log] ne ""} { proc ::nsf::log {level msg} { # The function might be called in situations in # aolserver/naviserver, where ns_log is not available. if {[info command ::ns_log] ne ""} { ::ns_log $level "nsf: $msg" } else { puts stderr "$level: $msg" } } } else { proc ::nsf::log {level msg} { puts stderr "$level: $msg" } } # # deprecated command # proc ::nsf::deprecated {what oldCmd newCmd} { set msg "**\n** The $what $oldCmd is deprecated." if {$newCmd ne ""} {append msg " use $newCmd instead."} append msg "\n**\n" nsf::log Warning $msg } # # determine platform aware temp directory # proc tmpdir {} { foreach e [list TMPDIR TEMP TMP] { if {[info exists ::env($e)] \ && [file isdirectory $::env($e)] \ && [file writable $::env($e)]} { return $::env($e) } } if {$::tcl_platform(platform) eq "windows"} { foreach d [list "C:\\TEMP" "C:\\TMP" "\\TEMP" "\\TMP"] { if {[file isdirectory $d] && [file writable $d]} { return $d } } } return /tmp } namespace export tmpdir # if HOME is not set, and ~ is resolved, Tcl chokes on that if {![info exists ::env(HOME)]} {set ::env(HOME) /root} # # parameter support # namespace eval ::nsf::parameter {} proc ::nsf::parameter::filter {defs pattern} { set result {} foreach def $defs { if {[string match $pattern [::nsf::parameter::info name $def]]} { lappend result $def } } return $result } set ::nsf::parameter::syntax(::nsf::xotclnext) "?--noArgs? ?/arg .../?" set ::nsf::parameter::syntax(::nsf::__unset_unknown_args) "" set ::nsf::parameter::syntax(::nsf::exithandler) "?get?|?set /cmds/?|?unset?" } generic/nsfAPI.decls000066400000000000000000000504231242365656200146450ustar00rootroot00000000000000# -*- Tcl -*- # # nsfAPI.decls -- # # Public functions and Tcl commands offered by the Next # Scripting Framework (NSF) library. This script is sourced by # the C-code generator gentclAPI.tcl in the same directory. # # Copyright (C) 2009-2014 Gustaf Neumann # # Vienna University of Economics and Business # Institute of Information Systems and New Media # A-1020, Welthandelsplatz 1 # Vienna, Austria # # This work is licensed under the MIT License http://www.opensource.org/licenses/MIT # # Copyright: # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # namespaces for types of methods array set ns { cmd "::nsf" objectMethod "::nsf::methods::object" objectInfoMethod "::nsf::methods::object::info" classMethod "::nsf::methods::class" classInfoMethod "::nsf::methods::class::info" } # # Next Scripting commands # cmd __db_compile_epoch NsfDebugCompileEpoch {} cmd __db_run_assertions NsfDebugRunAssertionsCmd {} cmd __db_show_stack NsfShowStackCmd {} cmd __db_show_obj NsfDebugShowObj { {-argName "obj" -required 1 -type tclobj} } cmd __profile_clear NsfProfileClearDataStub {} cmd __profile_get NsfProfileGetDataStub {} cmd __unset_unknown_args NsfUnsetUnknownArgsCmd {} cmd "asm::proc" NsfAsmProcCmd { {-argName "-ad" -required 0 -nrargs 0 -type switch} {-argName "-checkalways" -required 0 -nrargs 0 -type switch} {-argName "procName" -required 1 -type tclobj} {-argName "arguments" -required 1 -type tclobj} {-argName "body" -required 1 -type tclobj} } cmd configure NsfConfigureCmd { {-argName "option" -required 1 -typeName "configureoption" -type "debug|dtrace|filter|profile|softrecreate|objectsystems|keepcmds|checkresults|checkarguments"} {-argName "value" -required 0 -type tclobj} } {-nxdoc 1} cmd colon NsfColonCmd { {-argName "args" -type allargs} } cmd "directdispatch" NsfDirectDispatchCmd { {-argName "object" -required 1 -type object} {-argName "-frame" -required 0 -type "method|object|default" -default "default"} {-argName "command" -required 1 -type tclobj} {-argName "args" -type args} } cmd "dispatch" NsfDispatchCmd { {-argName "object" -required 1 -type object} {-argName "-intrinsic" -required 0 -nrargs 0 -type switch} {-argName "-system" -required 0 -nrargs 0 -type switch} {-argName "command" -required 1 -type tclobj} {-argName "args" -type args} } {-nxdoc 1} cmd finalize NsfFinalizeCmd { {-argName "-keepvars" -required 0 -nrargs 0 -type switch} } {-nxdoc 1} cmd interp NsfInterpObjCmd { {-argName "name" -required 1} {-argName "args" -type allargs} } {-nxdoc 1} cmd is NsfIsCmd { {-argName "-complain" -nrargs 0 -type switch} {-argName "-configure" -nrargs 0 -type switch} {-argName "-name" -required 0} {-argName "constraint" -required 1 -type tclobj} {-argName "value" -required 1 -type tclobj} } {-nxdoc 1} cmd parameter::info NsfParameterInfoCmd { {-argName "subcmd" -typeName "parametersubcmd" -type "default|list|name|syntax|type" -required 1} {-argName "spec" -required 1 -type tclobj} {-argName "varname" -required 0 -type tclobj} } cmd parameter::cache::classinvalidate NsfParameterCacheClassInvalidateCmd { {-argName "class" -required 1 -type class} } cmd parameter::cache::objectinvalidate NsfParameterCacheObjectInvalidateCmd { {-argName "object" -required 1 -type object} } cmd parameter::specs NsfParameterSpecsCmd { {-argName "-configure" -nrargs 0 -required 0 -type switch} {-argName "-nonposargs" -nrargs 0 -required 0 -type switch} {-argName "slotobjs" -required 1 -type tclobj} } # # cmd cmds (maybe more later) # cmd "cmd::info" NsfCmdInfoCmd { {-argName "subcmd" -required 1 -typeName "methodgetcmd" -type "args|body|definition|exists|registrationhandle|definitionhandle|origin|parameter|syntax|type|precondition|postcondition|submethods|returns"} {-argName "-context" -required 0 -type object} {-argName "methodName" -required 1 -type tclobj} {-argName "pattern" -required 0} } {-nxdoc 1} # # method cmds # cmd "method::alias" NsfMethodAliasCmd { {-argName "object" -required 1 -type object} {-argName "-per-object" -required 0 -nrargs 0 -type switch} {-argName "methodName" -required 1} {-argName "-frame" -required 0 -type "method|object|default" -default "default"} {-argName "-protection" -required 0 -type "call-protected|redefine-protected|none" -default "none"} {-argName "cmdName" -required 1 -type tclobj} } {-nxdoc 1} cmd "method::assertion" NsfMethodAssertionCmd { {-argName "object" -required 1 -type object} {-argName "subcmd" -required 1 -typeName "assertionsubcmd" -type "check|object-invar|class-invar"} {-argName "arg" -required 0 -type tclobj} } {-nxdoc 1} cmd "method::asmcreate" NsfAsmMethodCreateCmd { {-argName "object" -required 1 -type object} {-argName "-checkalways" -required 0 -nrargs 0 -type switch} {-argName "-inner-namespace" -nrargs 0 -type switch} {-argName "-per-object" -required 0 -nrargs 0 -type switch} {-argName "-reg-object" -required 0 -nrargs 1 -type object} {-argName "methodName" -required 1 -type tclobj} {-argName "arguments" -required 1 -type tclobj} {-argName "body" -required 1 -type tclobj} } cmd "method::create" NsfMethodCreateCmd { {-argName "object" -required 1 -type object} {-argName "-checkalways" -required 0 -nrargs 0 -type switch} {-argName "-inner-namespace" -nrargs 0 -type switch} {-argName "-per-object" -required 0 -nrargs 0 -type switch} {-argName "-reg-object" -required 0 -type object} {-argName "methodName" -required 1 -type tclobj} {-argName "arguments" -required 1 -type tclobj} {-argName "body" -required 1 -type tclobj} {-argName "-precondition" -type tclobj} {-argName "-postcondition" -type tclobj} } {-nxdoc 1} cmd "method::delete" NsfMethodDeleteCmd { {-argName "object" -required 1 -type object} {-argName "-per-object" -required 0 -nrargs 0 -type switch} {-argName "methodName" -required 1 -type tclobj} } {-nxdoc 1} cmd "method::forward" NsfMethodForwardCmd { {-argName "object" -required 1 -type object} {-argName "-per-object" -required 0 -nrargs 0 -type switch} {-argName "method" -required 1 -type tclobj} {-argName "-default" -type tclobj} {-argName "-earlybinding" -nrargs 0 -type switch} {-argName "-onerror" -type tclobj} {-argName "-prefix" -type tclobj} {-argName "-frame" -nrargs 1 -type "object|method|default" -default default} {-argName "-verbose" -nrargs 0 -type switch} {-argName "target" -type tclobj} {-argName "args" -type args} } {-nxdoc 1} cmd "method::property" NsfMethodPropertyCmd { {-argName "object" -required 1 -type object} {-argName "-per-object" -required 0 -nrargs 0 -type switch} {-argName "methodName" -required 1 -type tclobj} {-argName "methodProperty" -required 1 -type "class-only|call-private|call-protected|redefine-protected|returns|slotobj"} {-argName "value" -type tclobj} } {-nxdoc 1} cmd "method::registered" NsfMethodRegisteredCmd { {-argName "handle" -required 1 -type tclobj} } {-nxdoc 1} cmd "method::setter" NsfMethodSetterCmd { {-argName "object" -required 1 -type object} {-argName "-per-object" -required 0 -nrargs 0 -type switch} {-argName "parameter" -required 1 -type tclobj} } {-nxdoc 1} # # object cmds # cmd "object::alloc" NsfObjectAllocCmd { {-argName "class" -required 1 -type class} {-argName "name" -required 1 -type tclobj} {-argName "initcmd" -required 0 -type tclobj} } cmd "object::exists" NsfObjectExistsCmd { {-argName "value" -required 1 -type tclobj} } {-nxdoc 1} cmd "object::property" NsfObjectPropertyCmd { {-argName "objectName" -required 1 -type object} {-argName "objectProperty" -type "initialized|class|rootmetaclass|rootclass|volatile|slotcontainer|hasperobjectslots|keepcallerself|perobjectdispatch" -required 1} {-argName "value" -required 0 -type tclobj} } {-nxdoc 1} cmd "object::qualify" NsfObjectQualifyCmd { {-argName "objectName" -required 1 -type tclobj} } {-nxdoc 1} # # objectsystem cmds # cmd "objectsystem::create" NsfObjectSystemCreateCmd { {-argName "rootClass" -required 1 -type tclobj} {-argName "rootMetaClass" -required 1 -type tclobj} {-argName "systemMethods" -required 0 -type tclobj} } {-nxdoc 1} cmd my NsfMyCmd { {-argName "-intrinsic" -nrargs 0 -type switch} {-argName "-local" -nrargs 0 -type switch} {-argName "-system" -nrargs 0 -type switch} {-argName "methodName" -required 1 -type tclobj} {-argName "args" -type args} } {-nxdoc 1} cmd next NsfNextCmd { {-argName "arguments" -required 0 -type tclobj} } {-nxdoc 1} cmd nscopyvars NsfNSCopyVarsCmd { {-argName "fromNs" -required 1 -type tclobj} {-argName "toNs" -required 1 -type tclobj} } cmd proc NsfProcCmd { {-argName "-ad" -required 0 -nrargs 0 -type switch} {-argName "-checkalways" -required 0 -nrargs 0 -type switch} {-argName "procName" -required 1 -type tclobj} {-argName "arguments" -required 1 -type tclobj} {-argName "body" -required 1 -type tclobj} } {-nxdoc 1} cmd relation::get NsfRelationGetCmd { {-argName "object" -required 1 -type object} {-argName "type" -required 1 -typeName "relationtype" -type "object-mixin|class-mixin|object-filter|class-filter|class|superclass|rootclass"} } {-nxdoc 1} cmd relation::set NsfRelationSetCmd { {-argName "object" -required 1 -type object} {-argName "type" -required 1 -typeName "relationtype" -type "object-mixin|class-mixin|object-filter|class-filter|class|superclass|rootclass"} {-argName "value" -required 0 -type tclobj} } {-nxdoc 1} cmd current NsfCurrentCmd { {-argName "option" -required 0 -typeName "currentoption" -type "proc|method|methodpath|object|class|activelevel|args|activemixin|calledproc|calledmethod|calledclass|callingproc|callingmethod|callingclass|callinglevel|callingobject|filterreg|isnextcall|nextmethod"} } {-nxdoc 1} cmd self NsfSelfCmd { } {-nxdoc 1} # # var cmds # cmd "var::exists" NsfVarExistsCmd { {-argName "-array" -required 0 -nrargs 0 -type switch} {-argName "object" -required 1 -type object} {-argName "varName" -required 1} } {-nxdoc 1} cmd "var::get" NsfVarGetCmd { {-argName "-array" -required 0 -nrargs 0 -type switch} {-argName "object" -required 1 -type object} {-argName "varName" -required 1 -type tclobj} } {-nxdoc 1} cmd "var::import" NsfVarImportCmd { {-argName "object" -required 1 -type object} {-argName "args" -type args} } {-nxdoc 1} cmd "var::set" NsfVarSetCmd { {-argName "-array" -required 0 -nrargs 0 -type switch} {-argName "object" -required 1 -type object} {-argName "varName" -required 1 -type tclobj} {-argName "value" -required 0 -type tclobj} } {-nxdoc 1} cmd "var::unset" NsfVarUnsetCmd { {-argName "-nocomplain" -required 0 -nrargs 0 -type switch} {-argName "object" -required 1 -type object} {-argName "varName" -required 1 -type tclobj} } {-nxdoc 1} # # object methods # objectMethod autoname NsfOAutonameMethod { {-argName "-instance" -nrargs 0 -type switch} {-argName "-reset" -nrargs 0 -type switch} {-argName "name" -required 1 -type tclobj} } objectMethod class NsfOClassMethod { {-argName "class" -required 0 -type tclobj} } objectMethod cleanup NsfOCleanupMethod { } objectMethod cget NsfOCgetMethod { {-argName "name" -type tclobj -required 1} } objectMethod configure NsfOConfigureMethod { {-argName "args" -type virtualobjectargs} } {-objv0 1} objectMethod destroy NsfODestroyMethod { } objectMethod exists NsfOExistsMethod { {-argName "varName" -required 1} } objectMethod filterguard NsfOFilterGuardMethod { {-argName "filter" -required 1} {-argName "guard" -required 1 -type tclobj} } objectMethod instvar NsfOInstvarMethod { {-argName "args" -type allargs} } objectMethod mixinguard NsfOMixinGuardMethod { {-argName "mixin" -required 1 -type tclobj} {-argName "guard" -required 1 -type tclobj} } objectMethod noinit NsfONoinitMethod { } objectMethod requirenamespace NsfORequireNamespaceMethod { } objectMethod residualargs NsfOResidualargsMethod { {-argName "args" -type allargs} } objectMethod uplevel NsfOUplevelMethod { {-argName "args" -type allargs} } objectMethod upvar NsfOUpvarMethod { {-argName "args" -type allargs} } objectMethod volatile NsfOVolatileMethod { } # # class methods # classMethod alloc NsfCAllocMethod { {-argName "objectName" -required 1 -type tclobj} } classMethod create NsfCCreateMethod { {-argName "objectName" -required 1 -type tclobj} {-argName "args" -type virtualclassargs} } classMethod dealloc NsfCDeallocMethod { {-argName "object" -required 1 -type tclobj} } classMethod filterguard NsfCFilterGuardMethod { {-argName "filter" -required 1} {-argName "guard" -required 1 -type tclobj} } classMethod getCachedParameters NsfCGetCachendParametersMethod { } classMethod mixinguard NsfCMixinGuardMethod { {-argName "mixin" -required 1 -type tclobj} {-argName "guard" -required 1 -type tclobj} } classMethod new NsfCNewMethod { {-argName "-childof" -required 0 -type tclobj} {-argName "args" -required 0 -type virtualclassargs} } classMethod recreate NsfCRecreateMethod { {-argName "objectName" -required 1 -type tclobj} {-argName "args" -type virtualclassargs} } classMethod superclass NsfCSuperclassMethod { {-argName "superclasses" -required 0 -type tclobj} } # # info object methods # objectInfoMethod children NsfObjInfoChildrenMethod { {-argName "-type" -required 0 -type class} {-argName "pattern" -required 0} } objectInfoMethod class NsfObjInfoClassMethod { } objectInfoMethod filterguard NsfObjInfoFilterguardMethod { {-argName "filter" -required 1} } objectInfoMethod filters NsfObjInfoFiltersMethod { {-argName "-guards" -nrargs 0 -type switch} {-argName "pattern"} } objectInfoMethod forward NsfObjInfoForwardMethod { {-argName "-definition" -nrargs 0 -type switch} {-argName "name"} } objectInfoMethod hasmixin NsfObjInfoHasMixinMethod { {-argName "class" -required 1 -type class} } objectInfoMethod hasnamespace NsfObjInfoHasnamespaceMethod { } objectInfoMethod hastype NsfObjInfoHasTypeMethod { {-argName "class" -required 1 -type class} } objectInfoMethod lookupfilter NsfObjInfoLookupFilterMethod { {-argName "filter" -required 1} } objectInfoMethod lookupfilters NsfObjInfoLookupFiltersMethod { {-argName "-guards" -nrargs 0 -type switch} {-argName "pattern"} } objectInfoMethod lookupmethod NsfObjInfoLookupMethodMethod { {-argName "name" -required 1 -type tclobj} } objectInfoMethod lookupmethods NsfObjInfoLookupMethodsMethod { {-argName "-callprotection" -type "all|public|protected|private" -default all} {-argName "-incontext" -nrargs 0 -type switch} {-argName "-type" -typeName "methodtype" -type "all|scripted|builtin|alias|forwarder|object|setter|nsfproc"} {-argName "-nomixins" -nrargs 0 -type switch} {-argName "-path" -nrargs 0 -type switch} {-argName "-source" -type "all|application|system" -default all} {-argName "pattern" -required 0} } objectInfoMethod lookupmixins NsfObjInfoLookupMixinsMethod { {-argName "-guards" -nrargs 0 -type switch} {-argName "pattern" -type objpattern} } objectInfoMethod lookupslots NsfObjInfoLookupSlotsMethod { {-argName "-source" -type "all|application|system" -default all} {-argName "-type" -required 0 -type class} {-argName "pattern" -required 0} } objectInfoMethod method NsfObjInfoMethodMethod { {-argName "subcmd" -required 1 -typeName "infomethodsubcmd" -type "args|body|definition|exists|registrationhandle|definitionhandle|origin|parameter|syntax|type|precondition|postcondition|submethods|returns"} {-argName "name" -required 1 -type tclobj} } objectInfoMethod methods NsfObjInfoMethodsMethod { {-argName "-callprotection" -type "all|public|protected|private" -default all} {-argName "-type" -typeName "methodtype" -type "all|scripted|builtin|alias|forwarder|object|setter|nsfproc"} {-argName "-path" -nrargs 0 -type switch} {-argName "pattern" -required 0} } objectInfoMethod mixins NsfObjInfoMixinsMethod { {-argName "-guards" -nrargs 0 -type switch} {-argName "pattern" -type objpattern} } objectInfoMethod mixinguard NsfObjInfoMixinguardMethod { {-argName "mixin" -required 1} } objectInfoMethod name NsfObjInfoNameMethod { } objectInfoMethod parent NsfObjInfoParentMethod { } objectInfoMethod objectparameter NsfObjInfoObjectparameterMethod { {-argName "subcmd" -typeName "infoobjectparametersubcmd" -type "definitions|list|names|syntax" -required 1} {-argName "pattern" -required 0} } objectInfoMethod precedence NsfObjInfoPrecedenceMethod { {-argName "-intrinsic" -nrargs 0 -type switch} {-argName "pattern" -required 0} } objectInfoMethod slotobjects NsfObjInfoSlotobjectsMethod { {-argName "-type" -required 0 -type class} {-argName "pattern" -required 0} } objectInfoMethod vars NsfObjInfoVarsMethod { {-argName "pattern" -required 0} } # # info class methods # classInfoMethod filterguard NsfClassInfoFilterguardMethod { {-argName "filter" -required 1} } classInfoMethod filters NsfClassInfoFiltersMethod { {-argName "-guards" -nrargs 0 -type switch} {-argName "pattern"} } classInfoMethod forward NsfClassInfoForwardMethod { {-argName "-definition" -nrargs 0 -type switch} {-argName "name"} } classInfoMethod heritage NsfClassInfoHeritageMethod { {-argName "pattern"} } classInfoMethod instances NsfClassInfoInstancesMethod { {-argName "-closure" -nrargs 0 -type switch} {-argName "pattern" -type objpattern} } classInfoMethod method NsfClassInfoMethodMethod { {-argName "subcmd" -required 1 -typeName "infomethodsubcmd" -type "args|body|definition|exists|registrationhandle|definitionhandle|origin|parameter|syntax|type|precondition|postcondition|submethods|returns"} {-argName "name" -required 1 -type tclobj} } classInfoMethod methods NsfClassInfoMethodsMethod { {-argName "-callprotection" -type "all|public|protected|private" -default all} {-argName "-closure" -nrargs 0 -type switch} {-argName "-type" -nrargs 1 -typeName "methodtype" -type "all|scripted|builtin|alias|forwarder|object|setter|nsfproc"} {-argName "-path" -nrargs 0 -type switch} {-argName "-source" -nrargs 1 -type "all|application|system" -default all} {-argName "pattern"} } classInfoMethod mixins NsfClassInfoMixinsMethod { {-argName "-closure" -nrargs 0 -type switch} {-argName "-guards" -nrargs 0 -type switch} {-argName "-heritage" -nrargs 0 -type switch} {-argName "pattern" -type objpattern} } classInfoMethod mixinguard NsfClassInfoMixinguardMethod { {-argName "mixin" -required 1} } classInfoMethod mixinof NsfClassInfoMixinOfMethod { {-argName "-closure" -nrargs 0 -type switch} {-argName "-scope" -required 0 -type "all|class|object"} {-argName "pattern" -type objpattern} } classInfoMethod slotobjects NsfClassInfoSlotobjectsMethod { {-argName "-closure" -nrargs 0 -type switch} {-argName "-source" -type "all|application|system" -default all} {-argName "-type" -required 0 -type class} {-argName "pattern" -required 0} } classInfoMethod subclass NsfClassInfoSubclassMethod { {-argName "-closure" -nrargs 0 -type switch} {-argName "-dependent" -nrargs 0 -type switch} {-argName "pattern" -type objpattern -flags NSF_ARG_NODASHALNUM} } classInfoMethod superclass NsfClassInfoSuperclassMethod { {-argName "-closure" -nrargs 0 -type switch} {-argName "pattern" -type tclobj} } # # check methods # # checkMethod required NsfCheckRequiredArgs { # {-argName "name" -required 1} # {-argName "value" -required 0 -type tclobj} # } # checkMethod boolean NsfCheckBooleanArgs { # {-argName "name" -required 1} # {-argName "value" -required 0 -type tclobj} # } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: generic/nsfAPI.h000066400000000000000000005001551242365656200140040ustar00rootroot00000000000000 /* * This source code file was generated by the C-code generator gentclAPI.tcl, * part of the Next Scripting Framework. */ #if defined(USE_NSF_STUBS) int Nsf_ConvertTo_Boolean(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToBoolean(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Class(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToClass(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Int32(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToInt32(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Integer(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToInteger(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Object(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToObject(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Pointer(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToPointer(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_String(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToString(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Tclobj(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToTclobj(interp, objPtr, pPtr, clientData, outObjPtr); } #else # define Nsf_ConvertTo_Boolean Nsf_ConvertToBoolean # define Nsf_ConvertTo_Class Nsf_ConvertToClass # define Nsf_ConvertTo_Int32 Nsf_ConvertToInt32 # define Nsf_ConvertTo_Integer Nsf_ConvertToInteger # define Nsf_ConvertTo_Object Nsf_ConvertToObject # define Nsf_ConvertTo_Pointer Nsf_ConvertToPointer # define Nsf_ConvertTo_String Nsf_ConvertToString # define Nsf_ConvertTo_Tclobj Nsf_ConvertToTclobj #endif #if !defined(likely) # if defined(__GNUC__) && __GNUC__ > 2 /* Use gcc branch prediction hint to minimize cost of e.g. DTrace * ENABLED checks. */ # define unlikely(x) (__builtin_expect((x), 0)) # define likely(x) (__builtin_expect((x), 1)) # else # define unlikely(x) (x) # define likely(x) (x) # endif #endif enum InfomethodsubcmdIdx {InfomethodsubcmdNULL, InfomethodsubcmdArgsIdx, InfomethodsubcmdBodyIdx, InfomethodsubcmdDefinitionIdx, InfomethodsubcmdExistsIdx, InfomethodsubcmdRegistrationhandleIdx, InfomethodsubcmdDefinitionhandleIdx, InfomethodsubcmdOriginIdx, InfomethodsubcmdParameterIdx, InfomethodsubcmdSyntaxIdx, InfomethodsubcmdTypeIdx, InfomethodsubcmdPreconditionIdx, InfomethodsubcmdPostconditionIdx, InfomethodsubcmdSubmethodsIdx, InfomethodsubcmdReturnsIdx}; static int ConvertToInfomethodsubcmd(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"args", "body", "definition", "exists", "registrationhandle", "definitionhandle", "origin", "parameter", "syntax", "type", "precondition", "postcondition", "submethods", "returns", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "infomethodsubcmd", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum CallprotectionIdx {CallprotectionNULL, CallprotectionAllIdx, CallprotectionPublicIdx, CallprotectionProtectedIdx, CallprotectionPrivateIdx}; static int ConvertToCallprotection(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"all", "public", "protected", "private", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "-callprotection", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum MethodtypeIdx {MethodtypeNULL, MethodtypeAllIdx, MethodtypeScriptedIdx, MethodtypeBuiltinIdx, MethodtypeAliasIdx, MethodtypeForwarderIdx, MethodtypeObjectIdx, MethodtypeSetterIdx, MethodtypeNsfprocIdx}; static int ConvertToMethodtype(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"all", "scripted", "builtin", "alias", "forwarder", "object", "setter", "nsfproc", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "methodtype", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum SourceIdx {SourceNULL, SourceAllIdx, SourceApplicationIdx, SourceSystemIdx}; static int ConvertToSource(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"all", "application", "system", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "-source", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum ScopeIdx {ScopeNULL, ScopeAllIdx, ScopeClassIdx, ScopeObjectIdx}; static int ConvertToScope(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"all", "class", "object", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "-scope", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum ConfigureoptionIdx {ConfigureoptionNULL, ConfigureoptionDebugIdx, ConfigureoptionDtraceIdx, ConfigureoptionFilterIdx, ConfigureoptionProfileIdx, ConfigureoptionSoftrecreateIdx, ConfigureoptionObjectsystemsIdx, ConfigureoptionKeepcmdsIdx, ConfigureoptionCheckresultsIdx, ConfigureoptionCheckargumentsIdx}; static int ConvertToConfigureoption(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"debug", "dtrace", "filter", "profile", "softrecreate", "objectsystems", "keepcmds", "checkresults", "checkarguments", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "configureoption", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum CurrentoptionIdx {CurrentoptionNULL, CurrentoptionProcIdx, CurrentoptionMethodIdx, CurrentoptionMethodpathIdx, CurrentoptionObjectIdx, CurrentoptionClassIdx, CurrentoptionActivelevelIdx, CurrentoptionArgsIdx, CurrentoptionActivemixinIdx, CurrentoptionCalledprocIdx, CurrentoptionCalledmethodIdx, CurrentoptionCalledclassIdx, CurrentoptionCallingprocIdx, CurrentoptionCallingmethodIdx, CurrentoptionCallingclassIdx, CurrentoptionCallinglevelIdx, CurrentoptionCallingobjectIdx, CurrentoptionFilterregIdx, CurrentoptionIsnextcallIdx, CurrentoptionNextmethodIdx}; static int ConvertToCurrentoption(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"proc", "method", "methodpath", "object", "class", "activelevel", "args", "activemixin", "calledproc", "calledmethod", "calledclass", "callingproc", "callingmethod", "callingclass", "callinglevel", "callingobject", "filterreg", "isnextcall", "nextmethod", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "currentoption", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum FrameIdx {FrameNULL, FrameMethodIdx, FrameObjectIdx, FrameDefaultIdx}; static int ConvertToFrame(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"method", "object", "default", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "-frame", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum ProtectionIdx {ProtectionNULL, ProtectionCall_protectedIdx, ProtectionRedefine_protectedIdx, ProtectionNoneIdx}; static int ConvertToProtection(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"call-protected", "redefine-protected", "none", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "-protection", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum AssertionsubcmdIdx {AssertionsubcmdNULL, AssertionsubcmdCheckIdx, AssertionsubcmdObject_invarIdx, AssertionsubcmdClass_invarIdx}; static int ConvertToAssertionsubcmd(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"check", "object-invar", "class-invar", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "assertionsubcmd", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum MethodpropertyIdx {MethodpropertyNULL, MethodpropertyClass_onlyIdx, MethodpropertyCall_privateIdx, MethodpropertyCall_protectedIdx, MethodpropertyRedefine_protectedIdx, MethodpropertyReturnsIdx, MethodpropertySlotobjIdx}; static int ConvertToMethodproperty(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"class-only", "call-private", "call-protected", "redefine-protected", "returns", "slotobj", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "methodProperty", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum ObjectpropertyIdx {ObjectpropertyNULL, ObjectpropertyInitializedIdx, ObjectpropertyClassIdx, ObjectpropertyRootmetaclassIdx, ObjectpropertyRootclassIdx, ObjectpropertyVolatileIdx, ObjectpropertySlotcontainerIdx, ObjectpropertyHasperobjectslotsIdx, ObjectpropertyKeepcallerselfIdx, ObjectpropertyPerobjectdispatchIdx}; static int ConvertToObjectproperty(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"initialized", "class", "rootmetaclass", "rootclass", "volatile", "slotcontainer", "hasperobjectslots", "keepcallerself", "perobjectdispatch", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "objectProperty", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum ParametersubcmdIdx {ParametersubcmdNULL, ParametersubcmdDefaultIdx, ParametersubcmdListIdx, ParametersubcmdNameIdx, ParametersubcmdSyntaxIdx, ParametersubcmdTypeIdx}; static int ConvertToParametersubcmd(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"default", "list", "name", "syntax", "type", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "parametersubcmd", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum RelationtypeIdx {RelationtypeNULL, RelationtypeObject_mixinIdx, RelationtypeClass_mixinIdx, RelationtypeObject_filterIdx, RelationtypeClass_filterIdx, RelationtypeClassIdx, RelationtypeSuperclassIdx, RelationtypeRootclassIdx}; static int ConvertToRelationtype(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"object-mixin", "class-mixin", "object-filter", "class-filter", "class", "superclass", "rootclass", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "relationtype", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } enum InfoobjectparametersubcmdIdx {InfoobjectparametersubcmdNULL, InfoobjectparametersubcmdDefinitionsIdx, InfoobjectparametersubcmdListIdx, InfoobjectparametersubcmdNamesIdx, InfoobjectparametersubcmdSyntaxIdx}; static int ConvertToInfoobjectparametersubcmd(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"definitions", "list", "names", "syntax", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "infoobjectparametersubcmd", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } static Nsf_EnumeratorConverterEntry enumeratorConverterEntries[] = { {ConvertToInfoobjectparametersubcmd, "definitions|list|names|syntax"}, {ConvertToScope, "all|class|object"}, {ConvertToInfomethodsubcmd, "args|body|definition|exists|registrationhandle|definitionhandle|origin|parameter|syntax|type|precondition|postcondition|submethods|returns"}, {ConvertToCallprotection, "all|public|protected|private"}, {ConvertToMethodtype, "all|scripted|builtin|alias|forwarder|object|setter|nsfproc"}, {ConvertToFrame, "method|object|default"}, {ConvertToCurrentoption, "proc|method|methodpath|object|class|activelevel|args|activemixin|calledproc|calledmethod|calledclass|callingproc|callingmethod|callingclass|callinglevel|callingobject|filterreg|isnextcall|nextmethod"}, {ConvertToMethodproperty, "class-only|call-private|call-protected|redefine-protected|returns|slotobj"}, {ConvertToRelationtype, "object-mixin|class-mixin|object-filter|class-filter|class|superclass|rootclass"}, {ConvertToSource, "all|application|system"}, {ConvertToConfigureoption, "debug|dtrace|filter|profile|softrecreate|objectsystems|keepcmds|checkresults|checkarguments"}, {ConvertToObjectproperty, "initialized|class|rootmetaclass|rootclass|volatile|slotcontainer|hasperobjectslots|keepcallerself|perobjectdispatch"}, {ConvertToAssertionsubcmd, "check|object-invar|class-invar"}, {ConvertToParametersubcmd, "default|list|name|syntax|type"}, {ConvertToProtection, "call-protected|redefine-protected|none"}, {NULL, NULL} }; /* just to define the symbol */ static Nsf_methodDefinition method_definitions[110]; static CONST char *method_command_namespace_names[] = { "::nsf::methods::object::info", "::nsf::methods::object", "::nsf::methods::class::info", "::nsf::methods::class" }; static int NsfCAllocMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfCCreateMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfCDeallocMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfCFilterGuardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfCGetCachendParametersMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfCMixinGuardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfCNewMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfCRecreateMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfCSuperclassMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoFilterguardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoFiltersMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoForwardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoHeritageMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoInstancesMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoMethodMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoMethodsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoMixinOfMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoMixinguardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoMixinsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoSlotobjectsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoSubclassMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoSuperclassMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfAsmMethodCreateCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfAsmProcCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfCmdInfoCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfColonCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfConfigureCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfCurrentCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfDebugCompileEpochStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfDebugRunAssertionsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfDebugShowObjStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfDirectDispatchCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfDispatchCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfFinalizeCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfInterpObjCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfIsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMethodAliasCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMethodAssertionCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMethodCreateCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMethodDeleteCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMethodForwardCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMethodPropertyCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMethodRegisteredCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMethodSetterCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMyCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfNSCopyVarsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfNextCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjectAllocCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjectExistsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjectPropertyCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjectQualifyCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjectSystemCreateCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfParameterCacheClassInvalidateCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfParameterCacheObjectInvalidateCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfParameterInfoCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfParameterSpecsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfProcCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfProfileClearDataStubStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfProfileGetDataStubStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfRelationGetCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfRelationSetCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfSelfCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfShowStackCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfUnsetUnknownArgsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfVarExistsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfVarGetCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfVarImportCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfVarSetCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfVarUnsetCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfOAutonameMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOCgetMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOClassMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOCleanupMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOConfigureMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfODestroyMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOExistsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOFilterGuardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOInstvarMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOMixinGuardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfONoinitMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfORequireNamespaceMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOResidualargsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOUplevelMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOUpvarMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfOVolatileMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoChildrenMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoClassMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoFilterguardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoFiltersMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoForwardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoHasMixinMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoHasTypeMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoHasnamespaceMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoLookupFilterMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoLookupFiltersMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoLookupMethodMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoLookupMethodsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoLookupMixinsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoLookupSlotsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoMethodMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoMethodsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoMixinguardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoMixinsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoNameMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoObjectparameterMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoParentMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoPrecedenceMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoSlotobjectsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoVarsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfCAllocMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *objectName) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfCCreateMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *objectName, int nobjc, Tcl_Obj *CONST nobjv[]) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfCDeallocMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *object) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfCFilterGuardMethod(Tcl_Interp *interp, NsfClass *cl, CONST char *filter, Tcl_Obj *guard) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3) NSF_nonnull(4); static int NsfCGetCachendParametersMethod(Tcl_Interp *interp, NsfClass *cl) NSF_nonnull(1) NSF_nonnull(2); static int NsfCMixinGuardMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *mixin, Tcl_Obj *guard) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3) NSF_nonnull(4); static int NsfCNewMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *withChildof, int nobjc, Tcl_Obj *CONST nobjv[]) NSF_nonnull(1) NSF_nonnull(2); static int NsfCRecreateMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *objectName, int nobjc, Tcl_Obj *CONST nobjv[]) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfCSuperclassMethod(Tcl_Interp *interp, NsfClass *cl, Tcl_Obj *superclasses) NSF_nonnull(1) NSF_nonnull(2); static int NsfClassInfoFilterguardMethod(Tcl_Interp *interp, NsfClass *cl, CONST char *filter) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfClassInfoFiltersMethod(Tcl_Interp *interp, NsfClass *cl, int withGuards, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfClassInfoForwardMethod(Tcl_Interp *interp, NsfClass *cl, int withDefinition, CONST char *name) NSF_nonnull(1) NSF_nonnull(2); static int NsfClassInfoHeritageMethod(Tcl_Interp *interp, NsfClass *cl, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfClassInfoInstancesMethod(Tcl_Interp *interp, NsfClass *cl, int withClosure, CONST char *patternString, NsfObject *patternObject) NSF_nonnull(1) NSF_nonnull(2); static int NsfClassInfoMethodMethod(Tcl_Interp *interp, NsfClass *cl, int subcmd, Tcl_Obj *name) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfClassInfoMethodsMethod(Tcl_Interp *interp, NsfClass *cl, int withCallprotection, int withClosure, int withType, int withPath, int withSource, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfClassInfoMixinOfMethod(Tcl_Interp *interp, NsfClass *cl, int withClosure, int withScope, CONST char *patternString, NsfObject *patternObject) NSF_nonnull(1) NSF_nonnull(2); static int NsfClassInfoMixinguardMethod(Tcl_Interp *interp, NsfClass *cl, CONST char *mixin) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfClassInfoMixinsMethod(Tcl_Interp *interp, NsfClass *cl, int withClosure, int withGuards, int withHeritage, CONST char *patternString, NsfObject *patternObject) NSF_nonnull(1) NSF_nonnull(2); static int NsfClassInfoSlotobjectsMethod(Tcl_Interp *interp, NsfClass *cl, int withClosure, int withSource, NsfClass *withType, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfClassInfoSubclassMethod(Tcl_Interp *interp, NsfClass *cl, int withClosure, int withDependent, CONST char *patternString, NsfObject *patternObject) NSF_nonnull(1) NSF_nonnull(2); static int NsfClassInfoSuperclassMethod(Tcl_Interp *interp, NsfClass *cl, int withClosure, Tcl_Obj *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfAsmMethodCreateCmd(Tcl_Interp *interp, NsfObject *object, int withCheckalways, int withInner_namespace, int withPer_object, NsfObject *withReg_object, Tcl_Obj *methodName, Tcl_Obj *arguments, Tcl_Obj *body) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(7) NSF_nonnull(8) NSF_nonnull(9); static int NsfAsmProcCmd(Tcl_Interp *interp, int withAd, int withCheckalways, Tcl_Obj *procName, Tcl_Obj *arguments, Tcl_Obj *body) NSF_nonnull(1) NSF_nonnull(4) NSF_nonnull(5) NSF_nonnull(6); static int NsfCmdInfoCmd(Tcl_Interp *interp, int subcmd, NsfObject *withContext, Tcl_Obj *methodName, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(4); static int NsfColonCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) NSF_nonnull(1); static int NsfConfigureCmd(Tcl_Interp *interp, int option, Tcl_Obj *value) NSF_nonnull(1); static int NsfCurrentCmd(Tcl_Interp *interp, int option) NSF_nonnull(1); static int NsfDebugCompileEpoch(Tcl_Interp *interp) NSF_nonnull(1); static int NsfDebugRunAssertionsCmd(Tcl_Interp *interp) NSF_nonnull(1); static int NsfDebugShowObj(Tcl_Interp *interp, Tcl_Obj *obj) NSF_nonnull(1) NSF_nonnull(2); static int NsfDirectDispatchCmd(Tcl_Interp *interp, NsfObject *object, int withFrame, Tcl_Obj *command, int nobjc, Tcl_Obj *CONST nobjv[]) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfDispatchCmd(Tcl_Interp *interp, NsfObject *object, int withIntrinsic, int withSystem, Tcl_Obj *command, int nobjc, Tcl_Obj *CONST nobjv[]) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(5); static int NsfFinalizeCmd(Tcl_Interp *interp, int withKeepvars) NSF_nonnull(1); static int NsfInterpObjCmd(Tcl_Interp *interp, CONST char *name, int objc, Tcl_Obj *CONST objv[]) NSF_nonnull(1) NSF_nonnull(2); static int NsfIsCmd(Tcl_Interp *interp, int withComplain, int withConfigure, CONST char *withName, Tcl_Obj *constraint, Tcl_Obj *value) NSF_nonnull(1) NSF_nonnull(5) NSF_nonnull(6); static int NsfMethodAliasCmd(Tcl_Interp *interp, NsfObject *object, int withPer_object, CONST char *methodName, int withFrame, int withProtection, Tcl_Obj *cmdName) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4) NSF_nonnull(7); static int NsfMethodAssertionCmd(Tcl_Interp *interp, NsfObject *object, int subcmd, Tcl_Obj *arg) NSF_nonnull(1) NSF_nonnull(2); static int NsfMethodCreateCmd(Tcl_Interp *interp, NsfObject *object, int withCheckalways, int withInner_namespace, int withPer_object, NsfObject *withReg_object, Tcl_Obj *methodName, Tcl_Obj *arguments, Tcl_Obj *body, Tcl_Obj *withPrecondition, Tcl_Obj *withPostcondition) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(7) NSF_nonnull(8) NSF_nonnull(9); static int NsfMethodDeleteCmd(Tcl_Interp *interp, NsfObject *object, int withPer_object, Tcl_Obj *methodName) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfMethodForwardCmd(Tcl_Interp *interp, NsfObject *object, int withPer_object, Tcl_Obj *method, Tcl_Obj *withDefault, int withEarlybinding, Tcl_Obj *withOnerror, Tcl_Obj *withPrefix, int withFrame, int withVerbose, Tcl_Obj *target, int nobjc, Tcl_Obj *CONST nobjv[]) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfMethodPropertyCmd(Tcl_Interp *interp, NsfObject *object, int withPer_object, Tcl_Obj *methodName, int methodProperty, Tcl_Obj *value) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfMethodRegisteredCmd(Tcl_Interp *interp, Tcl_Obj *handle) NSF_nonnull(1) NSF_nonnull(2); static int NsfMethodSetterCmd(Tcl_Interp *interp, NsfObject *object, int withPer_object, Tcl_Obj *parameter) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfMyCmd(Tcl_Interp *interp, int withIntrinsic, int withLocal, int withSystem, Tcl_Obj *methodName, int nobjc, Tcl_Obj *CONST nobjv[]) NSF_nonnull(1) NSF_nonnull(5); static int NsfNSCopyVarsCmd(Tcl_Interp *interp, Tcl_Obj *fromNs, Tcl_Obj *toNs) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfNextCmd(Tcl_Interp *interp, Tcl_Obj *arguments) NSF_nonnull(1); static int NsfObjectAllocCmd(Tcl_Interp *interp, NsfClass *class, Tcl_Obj *name, Tcl_Obj *initcmd) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfObjectExistsCmd(Tcl_Interp *interp, Tcl_Obj *value) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjectPropertyCmd(Tcl_Interp *interp, NsfObject *objectName, int objectProperty, Tcl_Obj *value) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjectQualifyCmd(Tcl_Interp *interp, Tcl_Obj *objectName) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjectSystemCreateCmd(Tcl_Interp *interp, Tcl_Obj *rootClass, Tcl_Obj *rootMetaClass, Tcl_Obj *systemMethods) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfParameterCacheClassInvalidateCmd(Tcl_Interp *interp, NsfClass *class) NSF_nonnull(1) NSF_nonnull(2); static int NsfParameterCacheObjectInvalidateCmd(Tcl_Interp *interp, NsfObject *object) NSF_nonnull(1) NSF_nonnull(2); static int NsfParameterInfoCmd(Tcl_Interp *interp, int subcmd, Tcl_Obj *spec, Tcl_Obj *varname) NSF_nonnull(1) NSF_nonnull(3); static int NsfParameterSpecsCmd(Tcl_Interp *interp, int withConfigure, int withNonposargs, Tcl_Obj *slotobjs) NSF_nonnull(1) NSF_nonnull(4); static int NsfProcCmd(Tcl_Interp *interp, int withAd, int withCheckalways, Tcl_Obj *procName, Tcl_Obj *arguments, Tcl_Obj *body) NSF_nonnull(1) NSF_nonnull(4) NSF_nonnull(5) NSF_nonnull(6); static int NsfProfileClearDataStub(Tcl_Interp *interp) NSF_nonnull(1); static int NsfProfileGetDataStub(Tcl_Interp *interp) NSF_nonnull(1); static int NsfRelationGetCmd(Tcl_Interp *interp, NsfObject *object, int type) NSF_nonnull(1) NSF_nonnull(2); static int NsfRelationSetCmd(Tcl_Interp *interp, NsfObject *object, int type, Tcl_Obj *value) NSF_nonnull(1) NSF_nonnull(2); static int NsfSelfCmd(Tcl_Interp *interp) NSF_nonnull(1); static int NsfShowStackCmd(Tcl_Interp *interp) NSF_nonnull(1); static int NsfUnsetUnknownArgsCmd(Tcl_Interp *interp) NSF_nonnull(1); static int NsfVarExistsCmd(Tcl_Interp *interp, int withArray, NsfObject *object, CONST char *varName) NSF_nonnull(1) NSF_nonnull(3) NSF_nonnull(4); static int NsfVarGetCmd(Tcl_Interp *interp, int withArray, NsfObject *object, Tcl_Obj *varName) NSF_nonnull(1) NSF_nonnull(3) NSF_nonnull(4); static int NsfVarImportCmd(Tcl_Interp *interp, NsfObject *object, int nobjc, Tcl_Obj *CONST nobjv[]) NSF_nonnull(1) NSF_nonnull(2); static int NsfVarSetCmd(Tcl_Interp *interp, int withArray, NsfObject *object, Tcl_Obj *varName, Tcl_Obj *value) NSF_nonnull(1) NSF_nonnull(3) NSF_nonnull(4); static int NsfVarUnsetCmd(Tcl_Interp *interp, int withNocomplain, NsfObject *object, Tcl_Obj *varName) NSF_nonnull(1) NSF_nonnull(3) NSF_nonnull(4); static int NsfOAutonameMethod(Tcl_Interp *interp, NsfObject *obj, int withInstance, int withReset, Tcl_Obj *name) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(5); static int NsfOCgetMethod(Tcl_Interp *interp, NsfObject *obj, Tcl_Obj *name) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfOClassMethod(Tcl_Interp *interp, NsfObject *obj, Tcl_Obj *class) NSF_nonnull(1) NSF_nonnull(2); static int NsfOCleanupMethod(Tcl_Interp *interp, NsfObject *obj) NSF_nonnull(1) NSF_nonnull(2); static int NsfOConfigureMethod(Tcl_Interp *interp, NsfObject *obj, int nobjc, Tcl_Obj *CONST nobjv[], Tcl_Obj *objv0) NSF_nonnull(1) NSF_nonnull(2); static int NsfODestroyMethod(Tcl_Interp *interp, NsfObject *obj) NSF_nonnull(1) NSF_nonnull(2); static int NsfOExistsMethod(Tcl_Interp *interp, NsfObject *obj, CONST char *varName) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfOFilterGuardMethod(Tcl_Interp *interp, NsfObject *obj, CONST char *filter, Tcl_Obj *guard) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3) NSF_nonnull(4); static int NsfOInstvarMethod(Tcl_Interp *interp, NsfObject *obj, int objc, Tcl_Obj *CONST objv[]) NSF_nonnull(1) NSF_nonnull(2); static int NsfOMixinGuardMethod(Tcl_Interp *interp, NsfObject *obj, Tcl_Obj *mixin, Tcl_Obj *guard) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3) NSF_nonnull(4); static int NsfONoinitMethod(Tcl_Interp *interp, NsfObject *obj) NSF_nonnull(1) NSF_nonnull(2); static int NsfORequireNamespaceMethod(Tcl_Interp *interp, NsfObject *obj) NSF_nonnull(1) NSF_nonnull(2); static int NsfOResidualargsMethod(Tcl_Interp *interp, NsfObject *obj, int objc, Tcl_Obj *CONST objv[]) NSF_nonnull(1) NSF_nonnull(2); static int NsfOUplevelMethod(Tcl_Interp *interp, NsfObject *obj, int objc, Tcl_Obj *CONST objv[]) NSF_nonnull(1) NSF_nonnull(2); static int NsfOUpvarMethod(Tcl_Interp *interp, NsfObject *obj, int objc, Tcl_Obj *CONST objv[]) NSF_nonnull(1) NSF_nonnull(2); static int NsfOVolatileMethod(Tcl_Interp *interp, NsfObject *obj) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoChildrenMethod(Tcl_Interp *interp, NsfObject *obj, NsfClass *withType, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoClassMethod(Tcl_Interp *interp, NsfObject *obj) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoFilterguardMethod(Tcl_Interp *interp, NsfObject *obj, CONST char *filter) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfObjInfoFiltersMethod(Tcl_Interp *interp, NsfObject *obj, int withGuards, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoForwardMethod(Tcl_Interp *interp, NsfObject *obj, int withDefinition, CONST char *name) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoHasMixinMethod(Tcl_Interp *interp, NsfObject *obj, NsfClass *class) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfObjInfoHasTypeMethod(Tcl_Interp *interp, NsfObject *obj, NsfClass *class) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfObjInfoHasnamespaceMethod(Tcl_Interp *interp, NsfObject *obj) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoLookupFilterMethod(Tcl_Interp *interp, NsfObject *obj, CONST char *filter) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfObjInfoLookupFiltersMethod(Tcl_Interp *interp, NsfObject *obj, int withGuards, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoLookupMethodMethod(Tcl_Interp *interp, NsfObject *obj, Tcl_Obj *name) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfObjInfoLookupMethodsMethod(Tcl_Interp *interp, NsfObject *obj, int withCallprotection, int withIncontext, int withType, int withNomixins, int withPath, int withSource, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoLookupMixinsMethod(Tcl_Interp *interp, NsfObject *obj, int withGuards, CONST char *patternString, NsfObject *patternObject) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoLookupSlotsMethod(Tcl_Interp *interp, NsfObject *obj, int withSource, NsfClass *withType, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoMethodMethod(Tcl_Interp *interp, NsfObject *obj, int subcmd, Tcl_Obj *name) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(4); static int NsfObjInfoMethodsMethod(Tcl_Interp *interp, NsfObject *obj, int withCallprotection, int withType, int withPath, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoMixinguardMethod(Tcl_Interp *interp, NsfObject *obj, CONST char *mixin) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfObjInfoMixinsMethod(Tcl_Interp *interp, NsfObject *obj, int withGuards, CONST char *patternString, NsfObject *patternObject) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoNameMethod(Tcl_Interp *interp, NsfObject *obj) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoObjectparameterMethod(Tcl_Interp *interp, NsfObject *obj, int subcmd, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoParentMethod(Tcl_Interp *interp, NsfObject *obj) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoPrecedenceMethod(Tcl_Interp *interp, NsfObject *obj, int withIntrinsic, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoSlotobjectsMethod(Tcl_Interp *interp, NsfObject *obj, NsfClass *withType, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); static int NsfObjInfoVarsMethod(Tcl_Interp *interp, NsfObject *obj, CONST char *pattern) NSF_nonnull(1) NSF_nonnull(2); enum { NsfCAllocMethodIdx, NsfCCreateMethodIdx, NsfCDeallocMethodIdx, NsfCFilterGuardMethodIdx, NsfCGetCachendParametersMethodIdx, NsfCMixinGuardMethodIdx, NsfCNewMethodIdx, NsfCRecreateMethodIdx, NsfCSuperclassMethodIdx, NsfClassInfoFilterguardMethodIdx, NsfClassInfoFiltersMethodIdx, NsfClassInfoForwardMethodIdx, NsfClassInfoHeritageMethodIdx, NsfClassInfoInstancesMethodIdx, NsfClassInfoMethodMethodIdx, NsfClassInfoMethodsMethodIdx, NsfClassInfoMixinOfMethodIdx, NsfClassInfoMixinguardMethodIdx, NsfClassInfoMixinsMethodIdx, NsfClassInfoSlotobjectsMethodIdx, NsfClassInfoSubclassMethodIdx, NsfClassInfoSuperclassMethodIdx, NsfAsmMethodCreateCmdIdx, NsfAsmProcCmdIdx, NsfCmdInfoCmdIdx, NsfColonCmdIdx, NsfConfigureCmdIdx, NsfCurrentCmdIdx, NsfDebugCompileEpochIdx, NsfDebugRunAssertionsCmdIdx, NsfDebugShowObjIdx, NsfDirectDispatchCmdIdx, NsfDispatchCmdIdx, NsfFinalizeCmdIdx, NsfInterpObjCmdIdx, NsfIsCmdIdx, NsfMethodAliasCmdIdx, NsfMethodAssertionCmdIdx, NsfMethodCreateCmdIdx, NsfMethodDeleteCmdIdx, NsfMethodForwardCmdIdx, NsfMethodPropertyCmdIdx, NsfMethodRegisteredCmdIdx, NsfMethodSetterCmdIdx, NsfMyCmdIdx, NsfNSCopyVarsCmdIdx, NsfNextCmdIdx, NsfObjectAllocCmdIdx, NsfObjectExistsCmdIdx, NsfObjectPropertyCmdIdx, NsfObjectQualifyCmdIdx, NsfObjectSystemCreateCmdIdx, NsfParameterCacheClassInvalidateCmdIdx, NsfParameterCacheObjectInvalidateCmdIdx, NsfParameterInfoCmdIdx, NsfParameterSpecsCmdIdx, NsfProcCmdIdx, NsfProfileClearDataStubIdx, NsfProfileGetDataStubIdx, NsfRelationGetCmdIdx, NsfRelationSetCmdIdx, NsfSelfCmdIdx, NsfShowStackCmdIdx, NsfUnsetUnknownArgsCmdIdx, NsfVarExistsCmdIdx, NsfVarGetCmdIdx, NsfVarImportCmdIdx, NsfVarSetCmdIdx, NsfVarUnsetCmdIdx, NsfOAutonameMethodIdx, NsfOCgetMethodIdx, NsfOClassMethodIdx, NsfOCleanupMethodIdx, NsfOConfigureMethodIdx, NsfODestroyMethodIdx, NsfOExistsMethodIdx, NsfOFilterGuardMethodIdx, NsfOInstvarMethodIdx, NsfOMixinGuardMethodIdx, NsfONoinitMethodIdx, NsfORequireNamespaceMethodIdx, NsfOResidualargsMethodIdx, NsfOUplevelMethodIdx, NsfOUpvarMethodIdx, NsfOVolatileMethodIdx, NsfObjInfoChildrenMethodIdx, NsfObjInfoClassMethodIdx, NsfObjInfoFilterguardMethodIdx, NsfObjInfoFiltersMethodIdx, NsfObjInfoForwardMethodIdx, NsfObjInfoHasMixinMethodIdx, NsfObjInfoHasTypeMethodIdx, NsfObjInfoHasnamespaceMethodIdx, NsfObjInfoLookupFilterMethodIdx, NsfObjInfoLookupFiltersMethodIdx, NsfObjInfoLookupMethodMethodIdx, NsfObjInfoLookupMethodsMethodIdx, NsfObjInfoLookupMixinsMethodIdx, NsfObjInfoLookupSlotsMethodIdx, NsfObjInfoMethodMethodIdx, NsfObjInfoMethodsMethodIdx, NsfObjInfoMixinguardMethodIdx, NsfObjInfoMixinsMethodIdx, NsfObjInfoNameMethodIdx, NsfObjInfoObjectparameterMethodIdx, NsfObjInfoParentMethodIdx, NsfObjInfoPrecedenceMethodIdx, NsfObjInfoSlotobjectsMethodIdx, NsfObjInfoVarsMethodIdx } NsfMethods; static int NsfCAllocMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (objc != 2) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[NsfCAllocMethodIdx].paramDefs, NULL, objv[0]); } return NsfCAllocMethod(interp, cl, objv[1]); } static int NsfCCreateMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfCCreateMethodIdx].paramDefs, method_definitions[NsfCCreateMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { Tcl_Obj *objectName = (Tcl_Obj *)pc.clientData[0]; assert(pc.status == 0); return NsfCCreateMethod(interp, cl, objectName, objc-pc.lastObjc, objv+pc.lastObjc); } else { return TCL_ERROR; } } static int NsfCDeallocMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (objc != 2) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[NsfCDeallocMethodIdx].paramDefs, NULL, objv[0]); } return NsfCDeallocMethod(interp, cl, objv[1]); } static int NsfCFilterGuardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfCFilterGuardMethodIdx].paramDefs, method_definitions[NsfCFilterGuardMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *filter = (CONST char *)pc.clientData[0]; Tcl_Obj *guard = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfCFilterGuardMethod(interp, cl, filter, guard); } else { return TCL_ERROR; } } static int NsfCGetCachendParametersMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfCGetCachendParametersMethodIdx].paramDefs, NULL, objv[0]); } return NsfCGetCachendParametersMethod(interp, cl); } static int NsfCMixinGuardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfCMixinGuardMethodIdx].paramDefs, method_definitions[NsfCMixinGuardMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { Tcl_Obj *mixin = (Tcl_Obj *)pc.clientData[0]; Tcl_Obj *guard = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfCMixinGuardMethod(interp, cl, mixin, guard); } else { return TCL_ERROR; } } static int NsfCNewMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfCNewMethodIdx].paramDefs, method_definitions[NsfCNewMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { Tcl_Obj *withChildof = (Tcl_Obj *)pc.clientData[0]; assert(pc.status == 0); return NsfCNewMethod(interp, cl, withChildof, objc-pc.lastObjc, objv+pc.lastObjc); } else { return TCL_ERROR; } } static int NsfCRecreateMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfCRecreateMethodIdx].paramDefs, method_definitions[NsfCRecreateMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { Tcl_Obj *objectName = (Tcl_Obj *)pc.clientData[0]; assert(pc.status == 0); return NsfCRecreateMethod(interp, cl, objectName, objc-pc.lastObjc, objv+pc.lastObjc); } else { return TCL_ERROR; } } static int NsfCSuperclassMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (objc < 1 || objc > 2) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[NsfCSuperclassMethodIdx].paramDefs, NULL, objv[0]); } return NsfCSuperclassMethod(interp, cl, objc == 2 ? objv[1] : NULL); } static int NsfClassInfoFilterguardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoFilterguardMethodIdx].paramDefs, method_definitions[NsfClassInfoFilterguardMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *filter = (CONST char *)pc.clientData[0]; assert(pc.status == 0); return NsfClassInfoFilterguardMethod(interp, cl, filter); } else { return TCL_ERROR; } } static int NsfClassInfoFiltersMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoFiltersMethodIdx].paramDefs, method_definitions[NsfClassInfoFiltersMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withGuards = (int )PTR2INT(pc.clientData[0]); CONST char *pattern = (CONST char *)pc.clientData[1]; assert(pc.status == 0); return NsfClassInfoFiltersMethod(interp, cl, withGuards, pattern); } else { return TCL_ERROR; } } static int NsfClassInfoForwardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoForwardMethodIdx].paramDefs, method_definitions[NsfClassInfoForwardMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withDefinition = (int )PTR2INT(pc.clientData[0]); CONST char *name = (CONST char *)pc.clientData[1]; assert(pc.status == 0); return NsfClassInfoForwardMethod(interp, cl, withDefinition, name); } else { return TCL_ERROR; } } static int NsfClassInfoHeritageMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoHeritageMethodIdx].paramDefs, method_definitions[NsfClassInfoHeritageMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *pattern = (CONST char *)pc.clientData[0]; assert(pc.status == 0); return NsfClassInfoHeritageMethod(interp, cl, pattern); } else { return TCL_ERROR; } } static int NsfClassInfoInstancesMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoInstancesMethodIdx].paramDefs, method_definitions[NsfClassInfoInstancesMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withClosure = (int )PTR2INT(pc.clientData[0]); CONST char *patternString = NULL; NsfObject *patternObject = NULL; Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[1]; int returnCode; if (GetMatchObject(interp, pattern, objc>1 ? objv[1] : NULL, &patternObject, &patternString) == -1) { if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_OK; } assert(pc.status == 0); returnCode = NsfClassInfoInstancesMethod(interp, cl, withClosure, patternString, patternObject); if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return returnCode; } else { Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[1]; if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_ERROR; } } static int NsfClassInfoMethodMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoMethodMethodIdx].paramDefs, method_definitions[NsfClassInfoMethodMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int subcmd = (int )PTR2INT(pc.clientData[0]); Tcl_Obj *name = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfClassInfoMethodMethod(interp, cl, subcmd, name); } else { return TCL_ERROR; } } static int NsfClassInfoMethodsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoMethodsMethodIdx].paramDefs, method_definitions[NsfClassInfoMethodsMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withCallprotection = (int )PTR2INT(pc.clientData[0]); int withClosure = (int )PTR2INT(pc.clientData[1]); int withType = (int )PTR2INT(pc.clientData[2]); int withPath = (int )PTR2INT(pc.clientData[3]); int withSource = (int )PTR2INT(pc.clientData[4]); CONST char *pattern = (CONST char *)pc.clientData[5]; assert(pc.status == 0); return NsfClassInfoMethodsMethod(interp, cl, withCallprotection, withClosure, withType, withPath, withSource, pattern); } else { return TCL_ERROR; } } static int NsfClassInfoMixinOfMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoMixinOfMethodIdx].paramDefs, method_definitions[NsfClassInfoMixinOfMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withClosure = (int )PTR2INT(pc.clientData[0]); int withScope = (int )PTR2INT(pc.clientData[1]); CONST char *patternString = NULL; NsfObject *patternObject = NULL; Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[2]; int returnCode; if (GetMatchObject(interp, pattern, objc>2 ? objv[2] : NULL, &patternObject, &patternString) == -1) { if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_OK; } assert(pc.status == 0); returnCode = NsfClassInfoMixinOfMethod(interp, cl, withClosure, withScope, patternString, patternObject); if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return returnCode; } else { Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[2]; if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_ERROR; } } static int NsfClassInfoMixinguardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoMixinguardMethodIdx].paramDefs, method_definitions[NsfClassInfoMixinguardMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *mixin = (CONST char *)pc.clientData[0]; assert(pc.status == 0); return NsfClassInfoMixinguardMethod(interp, cl, mixin); } else { return TCL_ERROR; } } static int NsfClassInfoMixinsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoMixinsMethodIdx].paramDefs, method_definitions[NsfClassInfoMixinsMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withClosure = (int )PTR2INT(pc.clientData[0]); int withGuards = (int )PTR2INT(pc.clientData[1]); int withHeritage = (int )PTR2INT(pc.clientData[2]); CONST char *patternString = NULL; NsfObject *patternObject = NULL; Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[3]; int returnCode; if (GetMatchObject(interp, pattern, objc>3 ? objv[3] : NULL, &patternObject, &patternString) == -1) { if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_OK; } assert(pc.status == 0); returnCode = NsfClassInfoMixinsMethod(interp, cl, withClosure, withGuards, withHeritage, patternString, patternObject); if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return returnCode; } else { Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[3]; if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_ERROR; } } static int NsfClassInfoSlotobjectsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoSlotobjectsMethodIdx].paramDefs, method_definitions[NsfClassInfoSlotobjectsMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withClosure = (int )PTR2INT(pc.clientData[0]); int withSource = (int )PTR2INT(pc.clientData[1]); NsfClass *withType = (NsfClass *)pc.clientData[2]; CONST char *pattern = (CONST char *)pc.clientData[3]; assert(pc.status == 0); return NsfClassInfoSlotobjectsMethod(interp, cl, withClosure, withSource, withType, pattern); } else { return TCL_ERROR; } } static int NsfClassInfoSubclassMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoSubclassMethodIdx].paramDefs, method_definitions[NsfClassInfoSubclassMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withClosure = (int )PTR2INT(pc.clientData[0]); int withDependent = (int )PTR2INT(pc.clientData[1]); CONST char *patternString = NULL; NsfObject *patternObject = NULL; Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[2]; int returnCode; if (GetMatchObject(interp, pattern, objc>2 ? objv[2] : NULL, &patternObject, &patternString) == -1) { if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_OK; } assert(pc.status == 0); returnCode = NsfClassInfoSubclassMethod(interp, cl, withClosure, withDependent, patternString, patternObject); if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return returnCode; } else { Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[2]; if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_ERROR; } } static int NsfClassInfoSuperclassMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfClass *cl = NsfObjectToClass(clientData); assert(clientData); assert(objc > 0); if (unlikely(cl == NULL)) return NsfDispatchClientDataError(interp, clientData, "class", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, (NsfObject *) cl, objv[0], method_definitions[NsfClassInfoSuperclassMethodIdx].paramDefs, method_definitions[NsfClassInfoSuperclassMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withClosure = (int )PTR2INT(pc.clientData[0]); Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfClassInfoSuperclassMethod(interp, cl, withClosure, pattern); } else { return TCL_ERROR; } } static int NsfAsmMethodCreateCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfAsmMethodCreateCmdIdx].paramDefs, method_definitions[NsfAsmMethodCreateCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int withCheckalways = (int )PTR2INT(pc.clientData[1]); int withInner_namespace = (int )PTR2INT(pc.clientData[2]); int withPer_object = (int )PTR2INT(pc.clientData[3]); NsfObject *withReg_object = (NsfObject *)pc.clientData[4]; Tcl_Obj *methodName = (Tcl_Obj *)pc.clientData[5]; Tcl_Obj *arguments = (Tcl_Obj *)pc.clientData[6]; Tcl_Obj *body = (Tcl_Obj *)pc.clientData[7]; assert(pc.status == 0); return NsfAsmMethodCreateCmd(interp, object, withCheckalways, withInner_namespace, withPer_object, withReg_object, methodName, arguments, body); } else { return TCL_ERROR; } } static int NsfAsmProcCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfAsmProcCmdIdx].paramDefs, method_definitions[NsfAsmProcCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withAd = (int )PTR2INT(pc.clientData[0]); int withCheckalways = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *procName = (Tcl_Obj *)pc.clientData[2]; Tcl_Obj *arguments = (Tcl_Obj *)pc.clientData[3]; Tcl_Obj *body = (Tcl_Obj *)pc.clientData[4]; assert(pc.status == 0); return NsfAsmProcCmd(interp, withAd, withCheckalways, procName, arguments, body); } else { return TCL_ERROR; } } static int NsfCmdInfoCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfCmdInfoCmdIdx].paramDefs, method_definitions[NsfCmdInfoCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int subcmd = (int )PTR2INT(pc.clientData[0]); NsfObject *withContext = (NsfObject *)pc.clientData[1]; Tcl_Obj *methodName = (Tcl_Obj *)pc.clientData[2]; CONST char *pattern = (CONST char *)pc.clientData[3]; assert(pc.status == 0); return NsfCmdInfoCmd(interp, subcmd, withContext, methodName, pattern); } else { return TCL_ERROR; } } static int NsfColonCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; return NsfColonCmd(interp, objc, objv); } static int NsfConfigureCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfConfigureCmdIdx].paramDefs, method_definitions[NsfConfigureCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int option = (int )PTR2INT(pc.clientData[0]); Tcl_Obj *value = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfConfigureCmd(interp, option, value); } else { return TCL_ERROR; } } static int NsfCurrentCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfCurrentCmdIdx].paramDefs, method_definitions[NsfCurrentCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int option = (int )PTR2INT(pc.clientData[0]); assert(pc.status == 0); return NsfCurrentCmd(interp, option); } else { return TCL_ERROR; } } static int NsfDebugCompileEpochStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfDebugCompileEpochIdx].paramDefs, NULL, objv[0]); } return NsfDebugCompileEpoch(interp); } static int NsfDebugRunAssertionsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfDebugRunAssertionsCmdIdx].paramDefs, NULL, objv[0]); } return NsfDebugRunAssertionsCmd(interp); } static int NsfDebugShowObjStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (objc != 2) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[NsfDebugShowObjIdx].paramDefs, NULL, objv[0]); } return NsfDebugShowObj(interp, objv[1]); } static int NsfDirectDispatchCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfDirectDispatchCmdIdx].paramDefs, method_definitions[NsfDirectDispatchCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int withFrame = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *command = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfDirectDispatchCmd(interp, object, withFrame, command, objc-pc.lastObjc, objv+pc.lastObjc); } else { return TCL_ERROR; } } static int NsfDispatchCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfDispatchCmdIdx].paramDefs, method_definitions[NsfDispatchCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int withIntrinsic = (int )PTR2INT(pc.clientData[1]); int withSystem = (int )PTR2INT(pc.clientData[2]); Tcl_Obj *command = (Tcl_Obj *)pc.clientData[3]; assert(pc.status == 0); return NsfDispatchCmd(interp, object, withIntrinsic, withSystem, command, objc-pc.lastObjc, objv+pc.lastObjc); } else { return TCL_ERROR; } } static int NsfFinalizeCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfFinalizeCmdIdx].paramDefs, method_definitions[NsfFinalizeCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withKeepvars = (int )PTR2INT(pc.clientData[0]); assert(pc.status == 0); return NsfFinalizeCmd(interp, withKeepvars); } else { return TCL_ERROR; } } static int NsfInterpObjCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfInterpObjCmdIdx].paramDefs, method_definitions[NsfInterpObjCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *name = (CONST char *)pc.clientData[0]; assert(pc.status == 0); return NsfInterpObjCmd(interp, name, objc, objv); } else { return TCL_ERROR; } } static int NsfIsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfIsCmdIdx].paramDefs, method_definitions[NsfIsCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withComplain = (int )PTR2INT(pc.clientData[0]); int withConfigure = (int )PTR2INT(pc.clientData[1]); CONST char *withName = (CONST char *)pc.clientData[2]; Tcl_Obj *constraint = (Tcl_Obj *)pc.clientData[3]; Tcl_Obj *value = (Tcl_Obj *)pc.clientData[4]; assert(pc.status == 0); return NsfIsCmd(interp, withComplain, withConfigure, withName, constraint, value); } else { return TCL_ERROR; } } static int NsfMethodAliasCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMethodAliasCmdIdx].paramDefs, method_definitions[NsfMethodAliasCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int withPer_object = (int )PTR2INT(pc.clientData[1]); CONST char *methodName = (CONST char *)pc.clientData[2]; int withFrame = (int )PTR2INT(pc.clientData[3]); int withProtection = (int )PTR2INT(pc.clientData[4]); Tcl_Obj *cmdName = (Tcl_Obj *)pc.clientData[5]; assert(pc.status == 0); return NsfMethodAliasCmd(interp, object, withPer_object, methodName, withFrame, withProtection, cmdName); } else { return TCL_ERROR; } } static int NsfMethodAssertionCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMethodAssertionCmdIdx].paramDefs, method_definitions[NsfMethodAssertionCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int subcmd = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *arg = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfMethodAssertionCmd(interp, object, subcmd, arg); } else { return TCL_ERROR; } } static int NsfMethodCreateCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMethodCreateCmdIdx].paramDefs, method_definitions[NsfMethodCreateCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int withCheckalways = (int )PTR2INT(pc.clientData[1]); int withInner_namespace = (int )PTR2INT(pc.clientData[2]); int withPer_object = (int )PTR2INT(pc.clientData[3]); NsfObject *withReg_object = (NsfObject *)pc.clientData[4]; Tcl_Obj *methodName = (Tcl_Obj *)pc.clientData[5]; Tcl_Obj *arguments = (Tcl_Obj *)pc.clientData[6]; Tcl_Obj *body = (Tcl_Obj *)pc.clientData[7]; Tcl_Obj *withPrecondition = (Tcl_Obj *)pc.clientData[8]; Tcl_Obj *withPostcondition = (Tcl_Obj *)pc.clientData[9]; assert(pc.status == 0); return NsfMethodCreateCmd(interp, object, withCheckalways, withInner_namespace, withPer_object, withReg_object, methodName, arguments, body, withPrecondition, withPostcondition); } else { return TCL_ERROR; } } static int NsfMethodDeleteCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMethodDeleteCmdIdx].paramDefs, method_definitions[NsfMethodDeleteCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int withPer_object = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *methodName = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfMethodDeleteCmd(interp, object, withPer_object, methodName); } else { return TCL_ERROR; } } static int NsfMethodForwardCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMethodForwardCmdIdx].paramDefs, method_definitions[NsfMethodForwardCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int withPer_object = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *method = (Tcl_Obj *)pc.clientData[2]; Tcl_Obj *withDefault = (Tcl_Obj *)pc.clientData[3]; int withEarlybinding = (int )PTR2INT(pc.clientData[4]); Tcl_Obj *withOnerror = (Tcl_Obj *)pc.clientData[5]; Tcl_Obj *withPrefix = (Tcl_Obj *)pc.clientData[6]; int withFrame = (int )PTR2INT(pc.clientData[7]); int withVerbose = (int )PTR2INT(pc.clientData[8]); Tcl_Obj *target = (Tcl_Obj *)pc.clientData[9]; assert(pc.status == 0); return NsfMethodForwardCmd(interp, object, withPer_object, method, withDefault, withEarlybinding, withOnerror, withPrefix, withFrame, withVerbose, target, objc-pc.lastObjc, objv+pc.lastObjc); } else { return TCL_ERROR; } } static int NsfMethodPropertyCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMethodPropertyCmdIdx].paramDefs, method_definitions[NsfMethodPropertyCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int withPer_object = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *methodName = (Tcl_Obj *)pc.clientData[2]; int methodProperty = (int )PTR2INT(pc.clientData[3]); Tcl_Obj *value = (Tcl_Obj *)pc.clientData[4]; assert(pc.status == 0); return NsfMethodPropertyCmd(interp, object, withPer_object, methodName, methodProperty, value); } else { return TCL_ERROR; } } static int NsfMethodRegisteredCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (objc != 2) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[NsfMethodRegisteredCmdIdx].paramDefs, NULL, objv[0]); } return NsfMethodRegisteredCmd(interp, objv[1]); } static int NsfMethodSetterCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMethodSetterCmdIdx].paramDefs, method_definitions[NsfMethodSetterCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int withPer_object = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *parameter = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfMethodSetterCmd(interp, object, withPer_object, parameter); } else { return TCL_ERROR; } } static int NsfMyCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMyCmdIdx].paramDefs, method_definitions[NsfMyCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withIntrinsic = (int )PTR2INT(pc.clientData[0]); int withLocal = (int )PTR2INT(pc.clientData[1]); int withSystem = (int )PTR2INT(pc.clientData[2]); Tcl_Obj *methodName = (Tcl_Obj *)pc.clientData[3]; assert(pc.status == 0); return NsfMyCmd(interp, withIntrinsic, withLocal, withSystem, methodName, objc-pc.lastObjc, objv+pc.lastObjc); } else { return TCL_ERROR; } } static int NsfNSCopyVarsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfNSCopyVarsCmdIdx].paramDefs, method_definitions[NsfNSCopyVarsCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { Tcl_Obj *fromNs = (Tcl_Obj *)pc.clientData[0]; Tcl_Obj *toNs = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfNSCopyVarsCmd(interp, fromNs, toNs); } else { return TCL_ERROR; } } static int NsfNextCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (objc < 1 || objc > 2) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[NsfNextCmdIdx].paramDefs, NULL, objv[0]); } return NsfNextCmd(interp, objc == 2 ? objv[1] : NULL); } static int NsfObjectAllocCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfObjectAllocCmdIdx].paramDefs, method_definitions[NsfObjectAllocCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfClass *class = (NsfClass *)pc.clientData[0]; Tcl_Obj *name = (Tcl_Obj *)pc.clientData[1]; Tcl_Obj *initcmd = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfObjectAllocCmd(interp, class, name, initcmd); } else { return TCL_ERROR; } } static int NsfObjectExistsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (objc != 2) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[NsfObjectExistsCmdIdx].paramDefs, NULL, objv[0]); } return NsfObjectExistsCmd(interp, objv[1]); } static int NsfObjectPropertyCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfObjectPropertyCmdIdx].paramDefs, method_definitions[NsfObjectPropertyCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *objectName = (NsfObject *)pc.clientData[0]; int objectProperty = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *value = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfObjectPropertyCmd(interp, objectName, objectProperty, value); } else { return TCL_ERROR; } } static int NsfObjectQualifyCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (objc != 2) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[NsfObjectQualifyCmdIdx].paramDefs, NULL, objv[0]); } return NsfObjectQualifyCmd(interp, objv[1]); } static int NsfObjectSystemCreateCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfObjectSystemCreateCmdIdx].paramDefs, method_definitions[NsfObjectSystemCreateCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { Tcl_Obj *rootClass = (Tcl_Obj *)pc.clientData[0]; Tcl_Obj *rootMetaClass = (Tcl_Obj *)pc.clientData[1]; Tcl_Obj *systemMethods = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfObjectSystemCreateCmd(interp, rootClass, rootMetaClass, systemMethods); } else { return TCL_ERROR; } } static int NsfParameterCacheClassInvalidateCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfParameterCacheClassInvalidateCmdIdx].paramDefs, method_definitions[NsfParameterCacheClassInvalidateCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfClass *class = (NsfClass *)pc.clientData[0]; assert(pc.status == 0); return NsfParameterCacheClassInvalidateCmd(interp, class); } else { return TCL_ERROR; } } static int NsfParameterCacheObjectInvalidateCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfParameterCacheObjectInvalidateCmdIdx].paramDefs, method_definitions[NsfParameterCacheObjectInvalidateCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; assert(pc.status == 0); return NsfParameterCacheObjectInvalidateCmd(interp, object); } else { return TCL_ERROR; } } static int NsfParameterInfoCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfParameterInfoCmdIdx].paramDefs, method_definitions[NsfParameterInfoCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int subcmd = (int )PTR2INT(pc.clientData[0]); Tcl_Obj *spec = (Tcl_Obj *)pc.clientData[1]; Tcl_Obj *varname = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfParameterInfoCmd(interp, subcmd, spec, varname); } else { return TCL_ERROR; } } static int NsfParameterSpecsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfParameterSpecsCmdIdx].paramDefs, method_definitions[NsfParameterSpecsCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withConfigure = (int )PTR2INT(pc.clientData[0]); int withNonposargs = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *slotobjs = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfParameterSpecsCmd(interp, withConfigure, withNonposargs, slotobjs); } else { return TCL_ERROR; } } static int NsfProcCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfProcCmdIdx].paramDefs, method_definitions[NsfProcCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withAd = (int )PTR2INT(pc.clientData[0]); int withCheckalways = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *procName = (Tcl_Obj *)pc.clientData[2]; Tcl_Obj *arguments = (Tcl_Obj *)pc.clientData[3]; Tcl_Obj *body = (Tcl_Obj *)pc.clientData[4]; assert(pc.status == 0); return NsfProcCmd(interp, withAd, withCheckalways, procName, arguments, body); } else { return TCL_ERROR; } } static int NsfProfileClearDataStubStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfProfileClearDataStubIdx].paramDefs, NULL, objv[0]); } return NsfProfileClearDataStub(interp); } static int NsfProfileGetDataStubStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfProfileGetDataStubIdx].paramDefs, NULL, objv[0]); } return NsfProfileGetDataStub(interp); } static int NsfRelationGetCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfRelationGetCmdIdx].paramDefs, method_definitions[NsfRelationGetCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int type = (int )PTR2INT(pc.clientData[1]); assert(pc.status == 0); return NsfRelationGetCmd(interp, object, type); } else { return TCL_ERROR; } } static int NsfRelationSetCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfRelationSetCmdIdx].paramDefs, method_definitions[NsfRelationSetCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; int type = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *value = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfRelationSetCmd(interp, object, type, value); } else { return TCL_ERROR; } } static int NsfSelfCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfSelfCmdIdx].paramDefs, NULL, objv[0]); } return NsfSelfCmd(interp); } static int NsfShowStackCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfShowStackCmdIdx].paramDefs, NULL, objv[0]); } return NsfShowStackCmd(interp); } static int NsfUnsetUnknownArgsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { (void)clientData; if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfUnsetUnknownArgsCmdIdx].paramDefs, NULL, objv[0]); } return NsfUnsetUnknownArgsCmd(interp); } static int NsfVarExistsCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfVarExistsCmdIdx].paramDefs, method_definitions[NsfVarExistsCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withArray = (int )PTR2INT(pc.clientData[0]); NsfObject *object = (NsfObject *)pc.clientData[1]; CONST char *varName = (CONST char *)pc.clientData[2]; assert(pc.status == 0); return NsfVarExistsCmd(interp, withArray, object, varName); } else { return TCL_ERROR; } } static int NsfVarGetCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfVarGetCmdIdx].paramDefs, method_definitions[NsfVarGetCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withArray = (int )PTR2INT(pc.clientData[0]); NsfObject *object = (NsfObject *)pc.clientData[1]; Tcl_Obj *varName = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfVarGetCmd(interp, withArray, object, varName); } else { return TCL_ERROR; } } static int NsfVarImportCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfVarImportCmdIdx].paramDefs, method_definitions[NsfVarImportCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfObject *object = (NsfObject *)pc.clientData[0]; assert(pc.status == 0); return NsfVarImportCmd(interp, object, objc-pc.lastObjc, objv+pc.lastObjc); } else { return TCL_ERROR; } } static int NsfVarSetCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfVarSetCmdIdx].paramDefs, method_definitions[NsfVarSetCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withArray = (int )PTR2INT(pc.clientData[0]); NsfObject *object = (NsfObject *)pc.clientData[1]; Tcl_Obj *varName = (Tcl_Obj *)pc.clientData[2]; Tcl_Obj *value = (Tcl_Obj *)pc.clientData[3]; assert(pc.status == 0); return NsfVarSetCmd(interp, withArray, object, varName, value); } else { return TCL_ERROR; } } static int NsfVarUnsetCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfVarUnsetCmdIdx].paramDefs, method_definitions[NsfVarUnsetCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withNocomplain = (int )PTR2INT(pc.clientData[0]); NsfObject *object = (NsfObject *)pc.clientData[1]; Tcl_Obj *varName = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfVarUnsetCmd(interp, withNocomplain, object, varName); } else { return TCL_ERROR; } } static int NsfOAutonameMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfOAutonameMethodIdx].paramDefs, method_definitions[NsfOAutonameMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withInstance = (int )PTR2INT(pc.clientData[0]); int withReset = (int )PTR2INT(pc.clientData[1]); Tcl_Obj *name = (Tcl_Obj *)pc.clientData[2]; assert(pc.status == 0); return NsfOAutonameMethod(interp, obj, withInstance, withReset, name); } else { return TCL_ERROR; } } static int NsfOCgetMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (objc != 2) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[NsfOCgetMethodIdx].paramDefs, NULL, objv[0]); } return NsfOCgetMethod(interp, obj, objv[1]); } static int NsfOClassMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (objc < 1 || objc > 2) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[NsfOClassMethodIdx].paramDefs, NULL, objv[0]); } return NsfOClassMethod(interp, obj, objc == 2 ? objv[1] : NULL); } static int NsfOCleanupMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfOCleanupMethodIdx].paramDefs, NULL, objv[0]); } return NsfOCleanupMethod(interp, obj); } static int NsfOConfigureMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfOConfigureMethodIdx].paramDefs, method_definitions[NsfOConfigureMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { return NsfOConfigureMethod(interp, obj, objc-pc.lastObjc, objv+pc.lastObjc, objv[0]); } else { return TCL_ERROR; } } static int NsfODestroyMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfODestroyMethodIdx].paramDefs, NULL, objv[0]); } return NsfODestroyMethod(interp, obj); } static int NsfOExistsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfOExistsMethodIdx].paramDefs, method_definitions[NsfOExistsMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *varName = (CONST char *)pc.clientData[0]; assert(pc.status == 0); return NsfOExistsMethod(interp, obj, varName); } else { return TCL_ERROR; } } static int NsfOFilterGuardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfOFilterGuardMethodIdx].paramDefs, method_definitions[NsfOFilterGuardMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *filter = (CONST char *)pc.clientData[0]; Tcl_Obj *guard = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfOFilterGuardMethod(interp, obj, filter, guard); } else { return TCL_ERROR; } } static int NsfOInstvarMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); return NsfOInstvarMethod(interp, obj, objc, objv); } static int NsfOMixinGuardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfOMixinGuardMethodIdx].paramDefs, method_definitions[NsfOMixinGuardMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { Tcl_Obj *mixin = (Tcl_Obj *)pc.clientData[0]; Tcl_Obj *guard = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfOMixinGuardMethod(interp, obj, mixin, guard); } else { return TCL_ERROR; } } static int NsfONoinitMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfONoinitMethodIdx].paramDefs, NULL, objv[0]); } return NsfONoinitMethod(interp, obj); } static int NsfORequireNamespaceMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfORequireNamespaceMethodIdx].paramDefs, NULL, objv[0]); } return NsfORequireNamespaceMethod(interp, obj); } static int NsfOResidualargsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); return NsfOResidualargsMethod(interp, obj, objc, objv); } static int NsfOUplevelMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); return NsfOUplevelMethod(interp, obj, objc, objv); } static int NsfOUpvarMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); return NsfOUpvarMethod(interp, obj, objc, objv); } static int NsfOVolatileMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfOVolatileMethodIdx].paramDefs, NULL, objv[0]); } return NsfOVolatileMethod(interp, obj); } static int NsfObjInfoChildrenMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoChildrenMethodIdx].paramDefs, method_definitions[NsfObjInfoChildrenMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfClass *withType = (NsfClass *)pc.clientData[0]; CONST char *pattern = (CONST char *)pc.clientData[1]; assert(pc.status == 0); return NsfObjInfoChildrenMethod(interp, obj, withType, pattern); } else { return TCL_ERROR; } } static int NsfObjInfoClassMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfObjInfoClassMethodIdx].paramDefs, NULL, objv[0]); } return NsfObjInfoClassMethod(interp, obj); } static int NsfObjInfoFilterguardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoFilterguardMethodIdx].paramDefs, method_definitions[NsfObjInfoFilterguardMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *filter = (CONST char *)pc.clientData[0]; assert(pc.status == 0); return NsfObjInfoFilterguardMethod(interp, obj, filter); } else { return TCL_ERROR; } } static int NsfObjInfoFiltersMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoFiltersMethodIdx].paramDefs, method_definitions[NsfObjInfoFiltersMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withGuards = (int )PTR2INT(pc.clientData[0]); CONST char *pattern = (CONST char *)pc.clientData[1]; assert(pc.status == 0); return NsfObjInfoFiltersMethod(interp, obj, withGuards, pattern); } else { return TCL_ERROR; } } static int NsfObjInfoForwardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoForwardMethodIdx].paramDefs, method_definitions[NsfObjInfoForwardMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withDefinition = (int )PTR2INT(pc.clientData[0]); CONST char *name = (CONST char *)pc.clientData[1]; assert(pc.status == 0); return NsfObjInfoForwardMethod(interp, obj, withDefinition, name); } else { return TCL_ERROR; } } static int NsfObjInfoHasMixinMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoHasMixinMethodIdx].paramDefs, method_definitions[NsfObjInfoHasMixinMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfClass *class = (NsfClass *)pc.clientData[0]; assert(pc.status == 0); return NsfObjInfoHasMixinMethod(interp, obj, class); } else { return TCL_ERROR; } } static int NsfObjInfoHasTypeMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoHasTypeMethodIdx].paramDefs, method_definitions[NsfObjInfoHasTypeMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfClass *class = (NsfClass *)pc.clientData[0]; assert(pc.status == 0); return NsfObjInfoHasTypeMethod(interp, obj, class); } else { return TCL_ERROR; } } static int NsfObjInfoHasnamespaceMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfObjInfoHasnamespaceMethodIdx].paramDefs, NULL, objv[0]); } return NsfObjInfoHasnamespaceMethod(interp, obj); } static int NsfObjInfoLookupFilterMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoLookupFilterMethodIdx].paramDefs, method_definitions[NsfObjInfoLookupFilterMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *filter = (CONST char *)pc.clientData[0]; assert(pc.status == 0); return NsfObjInfoLookupFilterMethod(interp, obj, filter); } else { return TCL_ERROR; } } static int NsfObjInfoLookupFiltersMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoLookupFiltersMethodIdx].paramDefs, method_definitions[NsfObjInfoLookupFiltersMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withGuards = (int )PTR2INT(pc.clientData[0]); CONST char *pattern = (CONST char *)pc.clientData[1]; assert(pc.status == 0); return NsfObjInfoLookupFiltersMethod(interp, obj, withGuards, pattern); } else { return TCL_ERROR; } } static int NsfObjInfoLookupMethodMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (objc != 2) { return NsfArgumentError(interp, "wrong # of arguments:", method_definitions[NsfObjInfoLookupMethodMethodIdx].paramDefs, NULL, objv[0]); } return NsfObjInfoLookupMethodMethod(interp, obj, objv[1]); } static int NsfObjInfoLookupMethodsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoLookupMethodsMethodIdx].paramDefs, method_definitions[NsfObjInfoLookupMethodsMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withCallprotection = (int )PTR2INT(pc.clientData[0]); int withIncontext = (int )PTR2INT(pc.clientData[1]); int withType = (int )PTR2INT(pc.clientData[2]); int withNomixins = (int )PTR2INT(pc.clientData[3]); int withPath = (int )PTR2INT(pc.clientData[4]); int withSource = (int )PTR2INT(pc.clientData[5]); CONST char *pattern = (CONST char *)pc.clientData[6]; assert(pc.status == 0); return NsfObjInfoLookupMethodsMethod(interp, obj, withCallprotection, withIncontext, withType, withNomixins, withPath, withSource, pattern); } else { return TCL_ERROR; } } static int NsfObjInfoLookupMixinsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoLookupMixinsMethodIdx].paramDefs, method_definitions[NsfObjInfoLookupMixinsMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withGuards = (int )PTR2INT(pc.clientData[0]); CONST char *patternString = NULL; NsfObject *patternObject = NULL; Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[1]; int returnCode; if (GetMatchObject(interp, pattern, objc>1 ? objv[1] : NULL, &patternObject, &patternString) == -1) { if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_OK; } assert(pc.status == 0); returnCode = NsfObjInfoLookupMixinsMethod(interp, obj, withGuards, patternString, patternObject); if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return returnCode; } else { Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[1]; if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_ERROR; } } static int NsfObjInfoLookupSlotsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoLookupSlotsMethodIdx].paramDefs, method_definitions[NsfObjInfoLookupSlotsMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withSource = (int )PTR2INT(pc.clientData[0]); NsfClass *withType = (NsfClass *)pc.clientData[1]; CONST char *pattern = (CONST char *)pc.clientData[2]; assert(pc.status == 0); return NsfObjInfoLookupSlotsMethod(interp, obj, withSource, withType, pattern); } else { return TCL_ERROR; } } static int NsfObjInfoMethodMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoMethodMethodIdx].paramDefs, method_definitions[NsfObjInfoMethodMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int subcmd = (int )PTR2INT(pc.clientData[0]); Tcl_Obj *name = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfObjInfoMethodMethod(interp, obj, subcmd, name); } else { return TCL_ERROR; } } static int NsfObjInfoMethodsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoMethodsMethodIdx].paramDefs, method_definitions[NsfObjInfoMethodsMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withCallprotection = (int )PTR2INT(pc.clientData[0]); int withType = (int )PTR2INT(pc.clientData[1]); int withPath = (int )PTR2INT(pc.clientData[2]); CONST char *pattern = (CONST char *)pc.clientData[3]; assert(pc.status == 0); return NsfObjInfoMethodsMethod(interp, obj, withCallprotection, withType, withPath, pattern); } else { return TCL_ERROR; } } static int NsfObjInfoMixinguardMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoMixinguardMethodIdx].paramDefs, method_definitions[NsfObjInfoMixinguardMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *mixin = (CONST char *)pc.clientData[0]; assert(pc.status == 0); return NsfObjInfoMixinguardMethod(interp, obj, mixin); } else { return TCL_ERROR; } } static int NsfObjInfoMixinsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoMixinsMethodIdx].paramDefs, method_definitions[NsfObjInfoMixinsMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withGuards = (int )PTR2INT(pc.clientData[0]); CONST char *patternString = NULL; NsfObject *patternObject = NULL; Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[1]; int returnCode; if (GetMatchObject(interp, pattern, objc>1 ? objv[1] : NULL, &patternObject, &patternString) == -1) { if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_OK; } assert(pc.status == 0); returnCode = NsfObjInfoMixinsMethod(interp, obj, withGuards, patternString, patternObject); if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return returnCode; } else { Tcl_Obj *pattern = (Tcl_Obj *)pc.clientData[1]; if (pattern) { DECR_REF_COUNT2("patternObj", pattern); } return TCL_ERROR; } } static int NsfObjInfoNameMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfObjInfoNameMethodIdx].paramDefs, NULL, objv[0]); } return NsfObjInfoNameMethod(interp, obj); } static int NsfObjInfoObjectparameterMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoObjectparameterMethodIdx].paramDefs, method_definitions[NsfObjInfoObjectparameterMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int subcmd = (int )PTR2INT(pc.clientData[0]); CONST char *pattern = (CONST char *)pc.clientData[1]; assert(pc.status == 0); return NsfObjInfoObjectparameterMethod(interp, obj, subcmd, pattern); } else { return TCL_ERROR; } } static int NsfObjInfoParentMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (unlikely(objc != 1)) { return NsfArgumentError(interp, "too many arguments:", method_definitions[NsfObjInfoParentMethodIdx].paramDefs, NULL, objv[0]); } return NsfObjInfoParentMethod(interp, obj); } static int NsfObjInfoPrecedenceMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoPrecedenceMethodIdx].paramDefs, method_definitions[NsfObjInfoPrecedenceMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withIntrinsic = (int )PTR2INT(pc.clientData[0]); CONST char *pattern = (CONST char *)pc.clientData[1]; assert(pc.status == 0); return NsfObjInfoPrecedenceMethod(interp, obj, withIntrinsic, pattern); } else { return TCL_ERROR; } } static int NsfObjInfoSlotobjectsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoSlotobjectsMethodIdx].paramDefs, method_definitions[NsfObjInfoSlotobjectsMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { NsfClass *withType = (NsfClass *)pc.clientData[0]; CONST char *pattern = (CONST char *)pc.clientData[1]; assert(pc.status == 0); return NsfObjInfoSlotobjectsMethod(interp, obj, withType, pattern); } else { return TCL_ERROR; } } static int NsfObjInfoVarsMethodStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; NsfObject *obj = (NsfObject *)clientData; assert(clientData); assert(objc > 0); if (unlikely(obj == NULL)) return NsfDispatchClientDataError(interp, clientData, "object", ObjStr(objv[0])); if (likely(ArgumentParse(interp, objc, objv, obj, objv[0], method_definitions[NsfObjInfoVarsMethodIdx].paramDefs, method_definitions[NsfObjInfoVarsMethodIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *pattern = (CONST char *)pc.clientData[0]; assert(pc.status == 0); return NsfObjInfoVarsMethod(interp, obj, pattern); } else { return TCL_ERROR; } } static Nsf_methodDefinition method_definitions[110] = { {"::nsf::methods::class::alloc", NsfCAllocMethodStub, 1, { {"objectName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::create", NsfCCreateMethodStub, 2, { {"objectName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"args", 0, 1, ConvertToNothing, NULL,NULL,"virtualclassargs",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::dealloc", NsfCDeallocMethodStub, 1, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::filterguard", NsfCFilterGuardMethodStub, 2, { {"filter", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"guard", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::getCachedParameters", NsfCGetCachendParametersMethodStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::mixinguard", NsfCMixinGuardMethodStub, 2, { {"mixin", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"guard", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::new", NsfCNewMethodStub, 2, { {"-childof", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"args", 0, 1, ConvertToNothing, NULL,NULL,"virtualclassargs",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::recreate", NsfCRecreateMethodStub, 2, { {"objectName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"args", 0, 1, ConvertToNothing, NULL,NULL,"virtualclassargs",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::superclass", NsfCSuperclassMethodStub, 1, { {"superclasses", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::filterguard", NsfClassInfoFilterguardMethodStub, 1, { {"filter", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::filters", NsfClassInfoFiltersMethodStub, 2, { {"-guards", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::forward", NsfClassInfoForwardMethodStub, 2, { {"-definition", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"name", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::heritage", NsfClassInfoHeritageMethodStub, 1, { {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::instances", NsfClassInfoInstancesMethodStub, 2, { {"-closure", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, ConvertToObjpattern, NULL,NULL,"objpattern",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::method", NsfClassInfoMethodMethodStub, 2, { {"subcmd", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToInfomethodsubcmd, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"name", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::methods", NsfClassInfoMethodsMethodStub, 6, { {"-callprotection", NSF_ARG_IS_ENUMERATION, 1, ConvertToCallprotection, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-closure", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-type", NSF_ARG_IS_ENUMERATION, 1, ConvertToMethodtype, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-path", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-source", NSF_ARG_IS_ENUMERATION, 1, ConvertToSource, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::mixinof", NsfClassInfoMixinOfMethodStub, 3, { {"-closure", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-scope", NSF_ARG_IS_ENUMERATION, 1, ConvertToScope, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, ConvertToObjpattern, NULL,NULL,"objpattern",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::mixinguard", NsfClassInfoMixinguardMethodStub, 1, { {"mixin", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::mixins", NsfClassInfoMixinsMethodStub, 4, { {"-closure", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-guards", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-heritage", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, ConvertToObjpattern, NULL,NULL,"objpattern",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::slotobjects", NsfClassInfoSlotobjectsMethodStub, 4, { {"-closure", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-source", NSF_ARG_IS_ENUMERATION, 1, ConvertToSource, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-type", 0, 1, Nsf_ConvertTo_Class, NULL,NULL,"class",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::subclass", NsfClassInfoSubclassMethodStub, 3, { {"-closure", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-dependent", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"pattern", NSF_ARG_NODASHALNUM, 1, ConvertToObjpattern, NULL,NULL,"objpattern",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::class::info::superclass", NsfClassInfoSuperclassMethodStub, 2, { {"-closure", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::method::asmcreate", NsfAsmMethodCreateCmdStub, 8, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"-checkalways", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-inner-namespace", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-per-object", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-reg-object", 0, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"methodName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"arguments", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"body", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::asm::proc", NsfAsmProcCmdStub, 5, { {"-ad", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-checkalways", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"procName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"arguments", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"body", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::cmd::info", NsfCmdInfoCmdStub, 4, { {"subcmd", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToInfomethodsubcmd, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-context", 0, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"methodName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::colon", NsfColonCmdStub, 1, { {"args", 0, 1, ConvertToNothing, NULL,NULL,"allargs",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::configure", NsfConfigureCmdStub, 2, { {"option", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToConfigureoption, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"value", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::current", NsfCurrentCmdStub, 1, { {"option", NSF_ARG_IS_ENUMERATION, 1, ConvertToCurrentoption, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::__db_compile_epoch", NsfDebugCompileEpochStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::__db_run_assertions", NsfDebugRunAssertionsCmdStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::__db_show_obj", NsfDebugShowObjStub, 1, { {"obj", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::directdispatch", NsfDirectDispatchCmdStub, 4, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"-frame", NSF_ARG_IS_ENUMERATION, 1, ConvertToFrame, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"command", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"args", 0, 1, ConvertToNothing, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::dispatch", NsfDispatchCmdStub, 5, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"-intrinsic", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-system", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"command", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"args", 0, 1, ConvertToNothing, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::finalize", NsfFinalizeCmdStub, 1, { {"-keepvars", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::interp", NsfInterpObjCmdStub, 2, { {"name", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"args", 0, 1, ConvertToNothing, NULL,NULL,"allargs",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::is", NsfIsCmdStub, 5, { {"-complain", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-configure", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-name", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"constraint", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"value", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::method::alias", NsfMethodAliasCmdStub, 6, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"-per-object", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"methodName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-frame", NSF_ARG_IS_ENUMERATION, 1, ConvertToFrame, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-protection", NSF_ARG_IS_ENUMERATION, 1, ConvertToProtection, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"cmdName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::method::assertion", NsfMethodAssertionCmdStub, 3, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"subcmd", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToAssertionsubcmd, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"arg", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::method::create", NsfMethodCreateCmdStub, 10, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"-checkalways", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-inner-namespace", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-per-object", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-reg-object", 0, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"methodName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"arguments", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"body", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-precondition", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-postcondition", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::method::delete", NsfMethodDeleteCmdStub, 3, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"-per-object", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"methodName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::method::forward", NsfMethodForwardCmdStub, 11, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"-per-object", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"method", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-default", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-earlybinding", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-onerror", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-prefix", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-frame", NSF_ARG_IS_ENUMERATION, 1, ConvertToFrame, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-verbose", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"target", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"args", 0, 1, ConvertToNothing, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::method::property", NsfMethodPropertyCmdStub, 5, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"-per-object", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"methodName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"methodProperty", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToMethodproperty, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"value", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::method::registered", NsfMethodRegisteredCmdStub, 1, { {"handle", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::method::setter", NsfMethodSetterCmdStub, 3, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"-per-object", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"parameter", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::my", NsfMyCmdStub, 5, { {"-intrinsic", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-local", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-system", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"methodName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"args", 0, 1, ConvertToNothing, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::nscopyvars", NsfNSCopyVarsCmdStub, 2, { {"fromNs", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"toNs", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::next", NsfNextCmdStub, 1, { {"arguments", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::object::alloc", NsfObjectAllocCmdStub, 3, { {"class", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Class, NULL,NULL,"class",NULL,NULL,NULL,NULL,NULL}, {"name", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"initcmd", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::object::exists", NsfObjectExistsCmdStub, 1, { {"value", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::object::property", NsfObjectPropertyCmdStub, 3, { {"objectName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"objectProperty", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToObjectproperty, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"value", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::object::qualify", NsfObjectQualifyCmdStub, 1, { {"objectName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::objectsystem::create", NsfObjectSystemCreateCmdStub, 3, { {"rootClass", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"rootMetaClass", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"systemMethods", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::parameter::cache::classinvalidate", NsfParameterCacheClassInvalidateCmdStub, 1, { {"class", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Class, NULL,NULL,"class",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::parameter::cache::objectinvalidate", NsfParameterCacheObjectInvalidateCmdStub, 1, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::parameter::info", NsfParameterInfoCmdStub, 3, { {"subcmd", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToParametersubcmd, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"spec", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"varname", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::parameter::specs", NsfParameterSpecsCmdStub, 3, { {"-configure", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-nonposargs", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"slotobjs", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::proc", NsfProcCmdStub, 5, { {"-ad", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-checkalways", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"procName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"arguments", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"body", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::__profile_clear", NsfProfileClearDataStubStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::__profile_get", NsfProfileGetDataStubStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::relation::get", NsfRelationGetCmdStub, 2, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"type", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToRelationtype, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::relation::set", NsfRelationSetCmdStub, 3, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"type", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToRelationtype, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"value", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::self", NsfSelfCmdStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::__db_show_stack", NsfShowStackCmdStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::__unset_unknown_args", NsfUnsetUnknownArgsCmdStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::var::exists", NsfVarExistsCmdStub, 3, { {"-array", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"varName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::var::get", NsfVarGetCmdStub, 3, { {"-array", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"varName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::var::import", NsfVarImportCmdStub, 2, { {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"args", 0, 1, ConvertToNothing, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::var::set", NsfVarSetCmdStub, 4, { {"-array", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"varName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"value", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::var::unset", NsfVarUnsetCmdStub, 3, { {"-nocomplain", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"object", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Object, NULL,NULL,"object",NULL,NULL,NULL,NULL,NULL}, {"varName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::autoname", NsfOAutonameMethodStub, 3, { {"-instance", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-reset", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"name", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::cget", NsfOCgetMethodStub, 1, { {"name", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::class", NsfOClassMethodStub, 1, { {"class", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::cleanup", NsfOCleanupMethodStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::configure", NsfOConfigureMethodStub, 1, { {"args", 0, 1, ConvertToNothing, NULL,NULL,"virtualobjectargs",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::destroy", NsfODestroyMethodStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::exists", NsfOExistsMethodStub, 1, { {"varName", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::filterguard", NsfOFilterGuardMethodStub, 2, { {"filter", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"guard", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::instvar", NsfOInstvarMethodStub, 1, { {"args", 0, 1, ConvertToNothing, NULL,NULL,"allargs",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::mixinguard", NsfOMixinGuardMethodStub, 2, { {"mixin", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"guard", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::noinit", NsfONoinitMethodStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::requirenamespace", NsfORequireNamespaceMethodStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::residualargs", NsfOResidualargsMethodStub, 1, { {"args", 0, 1, ConvertToNothing, NULL,NULL,"allargs",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::uplevel", NsfOUplevelMethodStub, 1, { {"args", 0, 1, ConvertToNothing, NULL,NULL,"allargs",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::upvar", NsfOUpvarMethodStub, 1, { {"args", 0, 1, ConvertToNothing, NULL,NULL,"allargs",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::volatile", NsfOVolatileMethodStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::children", NsfObjInfoChildrenMethodStub, 2, { {"-type", 0, 1, Nsf_ConvertTo_Class, NULL,NULL,"class",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::class", NsfObjInfoClassMethodStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::filterguard", NsfObjInfoFilterguardMethodStub, 1, { {"filter", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::filters", NsfObjInfoFiltersMethodStub, 2, { {"-guards", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::forward", NsfObjInfoForwardMethodStub, 2, { {"-definition", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"name", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::hasmixin", NsfObjInfoHasMixinMethodStub, 1, { {"class", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Class, NULL,NULL,"class",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::hastype", NsfObjInfoHasTypeMethodStub, 1, { {"class", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Class, NULL,NULL,"class",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::hasnamespace", NsfObjInfoHasnamespaceMethodStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::lookupfilter", NsfObjInfoLookupFilterMethodStub, 1, { {"filter", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::lookupfilters", NsfObjInfoLookupFiltersMethodStub, 2, { {"-guards", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::lookupmethod", NsfObjInfoLookupMethodMethodStub, 1, { {"name", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::lookupmethods", NsfObjInfoLookupMethodsMethodStub, 7, { {"-callprotection", NSF_ARG_IS_ENUMERATION, 1, ConvertToCallprotection, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-incontext", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-type", NSF_ARG_IS_ENUMERATION, 1, ConvertToMethodtype, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-nomixins", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-path", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"-source", NSF_ARG_IS_ENUMERATION, 1, ConvertToSource, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::lookupmixins", NsfObjInfoLookupMixinsMethodStub, 2, { {"-guards", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, ConvertToObjpattern, NULL,NULL,"objpattern",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::lookupslots", NsfObjInfoLookupSlotsMethodStub, 3, { {"-source", NSF_ARG_IS_ENUMERATION, 1, ConvertToSource, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-type", 0, 1, Nsf_ConvertTo_Class, NULL,NULL,"class",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::method", NsfObjInfoMethodMethodStub, 2, { {"subcmd", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToInfomethodsubcmd, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"name", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::methods", NsfObjInfoMethodsMethodStub, 4, { {"-callprotection", NSF_ARG_IS_ENUMERATION, 1, ConvertToCallprotection, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-type", NSF_ARG_IS_ENUMERATION, 1, ConvertToMethodtype, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-path", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::mixinguard", NsfObjInfoMixinguardMethodStub, 1, { {"mixin", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::mixins", NsfObjInfoMixinsMethodStub, 2, { {"-guards", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, ConvertToObjpattern, NULL,NULL,"objpattern",NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::name", NsfObjInfoNameMethodStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::objectparameter", NsfObjInfoObjectparameterMethodStub, 2, { {"subcmd", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToInfoobjectparametersubcmd, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::parent", NsfObjInfoParentMethodStub, 0, { {NULL, 0, 0, NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::precedence", NsfObjInfoPrecedenceMethodStub, 2, { {"-intrinsic", 0, 0, Nsf_ConvertTo_Boolean, NULL,NULL,"switch",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::slotobjects", NsfObjInfoSlotobjectsMethodStub, 2, { {"-type", 0, 1, Nsf_ConvertTo_Class, NULL,NULL,"class",NULL,NULL,NULL,NULL,NULL}, {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::nsf::methods::object::info::vars", NsfObjInfoVarsMethodStub, 1, { {"pattern", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} },{NULL} }; generic/nsfAPI.nxdocindex000066400000000000000000000133631242365656200157200ustar00rootroot00000000000000set ::nxdoc::include(::nsf::__db_compile_epoch) 0 set ::nxdoc::include(::nsf::__db_run_assertions) 0 set ::nxdoc::include(::nsf::__db_show_stack) 0 set ::nxdoc::include(::nsf::__db_show_obj) 0 set ::nxdoc::include(::nsf::__profile_clear) 0 set ::nxdoc::include(::nsf::__profile_get) 0 set ::nxdoc::include(::nsf::__unset_unknown_args) 0 set ::nxdoc::include(::nsf::asm::proc) 0 set ::nxdoc::include(::nsf::configure) 1 set ::nxdoc::include(::nsf::colon) 0 set ::nxdoc::include(::nsf::directdispatch) 0 set ::nxdoc::include(::nsf::dispatch) 1 set ::nxdoc::include(::nsf::finalize) 1 set ::nxdoc::include(::nsf::interp) 1 set ::nxdoc::include(::nsf::is) 1 set ::nxdoc::include(::nsf::parameter::info) 0 set ::nxdoc::include(::nsf::parameter::cache::classinvalidate) 0 set ::nxdoc::include(::nsf::parameter::cache::objectinvalidate) 0 set ::nxdoc::include(::nsf::parameter::specs) 0 set ::nxdoc::include(::nsf::cmd::info) 1 set ::nxdoc::include(::nsf::method::alias) 1 set ::nxdoc::include(::nsf::method::assertion) 1 set ::nxdoc::include(::nsf::method::asmcreate) 0 set ::nxdoc::include(::nsf::method::create) 1 set ::nxdoc::include(::nsf::method::delete) 1 set ::nxdoc::include(::nsf::method::forward) 1 set ::nxdoc::include(::nsf::method::property) 1 set ::nxdoc::include(::nsf::method::registered) 1 set ::nxdoc::include(::nsf::method::setter) 1 set ::nxdoc::include(::nsf::object::alloc) 0 set ::nxdoc::include(::nsf::object::exists) 1 set ::nxdoc::include(::nsf::object::property) 1 set ::nxdoc::include(::nsf::object::qualify) 1 set ::nxdoc::include(::nsf::objectsystem::create) 1 set ::nxdoc::include(::nsf::my) 1 set ::nxdoc::include(::nsf::next) 1 set ::nxdoc::include(::nsf::nscopyvars) 0 set ::nxdoc::include(::nsf::proc) 1 set ::nxdoc::include(::nsf::relation::get) 1 set ::nxdoc::include(::nsf::relation::set) 1 set ::nxdoc::include(::nsf::current) 1 set ::nxdoc::include(::nsf::self) 1 set ::nxdoc::include(::nsf::var::exists) 1 set ::nxdoc::include(::nsf::var::get) 1 set ::nxdoc::include(::nsf::var::import) 1 set ::nxdoc::include(::nsf::var::set) 1 set ::nxdoc::include(::nsf::var::unset) 1 set ::nxdoc::include(::nsf::methods::object::autoname) 0 set ::nxdoc::include(::nsf::methods::object::class) 0 set ::nxdoc::include(::nsf::methods::object::cleanup) 0 set ::nxdoc::include(::nsf::methods::object::cget) 0 set ::nxdoc::include(::nsf::methods::object::configure) 0 set ::nxdoc::include(::nsf::methods::object::destroy) 0 set ::nxdoc::include(::nsf::methods::object::exists) 0 set ::nxdoc::include(::nsf::methods::object::filterguard) 0 set ::nxdoc::include(::nsf::methods::object::instvar) 0 set ::nxdoc::include(::nsf::methods::object::mixinguard) 0 set ::nxdoc::include(::nsf::methods::object::noinit) 0 set ::nxdoc::include(::nsf::methods::object::requirenamespace) 0 set ::nxdoc::include(::nsf::methods::object::residualargs) 0 set ::nxdoc::include(::nsf::methods::object::uplevel) 0 set ::nxdoc::include(::nsf::methods::object::upvar) 0 set ::nxdoc::include(::nsf::methods::object::volatile) 0 set ::nxdoc::include(::nsf::methods::class::alloc) 0 set ::nxdoc::include(::nsf::methods::class::create) 0 set ::nxdoc::include(::nsf::methods::class::dealloc) 0 set ::nxdoc::include(::nsf::methods::class::filterguard) 0 set ::nxdoc::include(::nsf::methods::class::getCachedParameters) 0 set ::nxdoc::include(::nsf::methods::class::mixinguard) 0 set ::nxdoc::include(::nsf::methods::class::new) 0 set ::nxdoc::include(::nsf::methods::class::recreate) 0 set ::nxdoc::include(::nsf::methods::class::superclass) 0 set ::nxdoc::include(::nsf::methods::object::info::children) 0 set ::nxdoc::include(::nsf::methods::object::info::class) 0 set ::nxdoc::include(::nsf::methods::object::info::filterguard) 0 set ::nxdoc::include(::nsf::methods::object::info::filters) 0 set ::nxdoc::include(::nsf::methods::object::info::forward) 0 set ::nxdoc::include(::nsf::methods::object::info::hasmixin) 0 set ::nxdoc::include(::nsf::methods::object::info::hasnamespace) 0 set ::nxdoc::include(::nsf::methods::object::info::hastype) 0 set ::nxdoc::include(::nsf::methods::object::info::lookupfilter) 0 set ::nxdoc::include(::nsf::methods::object::info::lookupfilters) 0 set ::nxdoc::include(::nsf::methods::object::info::lookupmethod) 0 set ::nxdoc::include(::nsf::methods::object::info::lookupmethods) 0 set ::nxdoc::include(::nsf::methods::object::info::lookupmixins) 0 set ::nxdoc::include(::nsf::methods::object::info::lookupslots) 0 set ::nxdoc::include(::nsf::methods::object::info::method) 0 set ::nxdoc::include(::nsf::methods::object::info::methods) 0 set ::nxdoc::include(::nsf::methods::object::info::mixins) 0 set ::nxdoc::include(::nsf::methods::object::info::mixinguard) 0 set ::nxdoc::include(::nsf::methods::object::info::name) 0 set ::nxdoc::include(::nsf::methods::object::info::parent) 0 set ::nxdoc::include(::nsf::methods::object::info::objectparameter) 0 set ::nxdoc::include(::nsf::methods::object::info::precedence) 0 set ::nxdoc::include(::nsf::methods::object::info::slotobjects) 0 set ::nxdoc::include(::nsf::methods::object::info::vars) 0 set ::nxdoc::include(::nsf::methods::class::info::filterguard) 0 set ::nxdoc::include(::nsf::methods::class::info::filters) 0 set ::nxdoc::include(::nsf::methods::class::info::forward) 0 set ::nxdoc::include(::nsf::methods::class::info::heritage) 0 set ::nxdoc::include(::nsf::methods::class::info::instances) 0 set ::nxdoc::include(::nsf::methods::class::info::method) 0 set ::nxdoc::include(::nsf::methods::class::info::methods) 0 set ::nxdoc::include(::nsf::methods::class::info::mixins) 0 set ::nxdoc::include(::nsf::methods::class::info::mixinguard) 0 set ::nxdoc::include(::nsf::methods::class::info::mixinof) 0 set ::nxdoc::include(::nsf::methods::class::info::slotobjects) 0 set ::nxdoc::include(::nsf::methods::class::info::subclass) 0 set ::nxdoc::include(::nsf::methods::class::info::superclass) 0 generic/nsfAccessInt.h000066400000000000000000000124371242365656200152500ustar00rootroot00000000000000/* * Macros to abstract access to Tcl internals where possible. * This file is part of the Next Scripting Framework * * Copyright (C) 2010-2014 Gustaf Neumann * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #define Tcl_Interp_numLevels(interp) ((Interp *)(interp))->numLevels #define Tcl_Interp_framePtr(interp) ((Tcl_CallFrame *)((Interp *)(interp))->framePtr) #define Tcl_Interp_varFramePtr(interp) (((Interp *)(interp))->varFramePtr) #define Tcl_Interp_cmdFramePtr(interp) (((Interp *)(interp))->cmdFramePtr) #define Tcl_Interp_globalNsPtr(interp) ((Tcl_Namespace *)((Interp *)(interp))->globalNsPtr) #define Tcl_Interp_flags(interp) ((Interp *)(interp))->flags #define Tcl_Interp_threadId(interp) ((Interp *)(interp))->threadId #define Tcl_CallFrame_callerPtr(cf) ((Tcl_CallFrame*)((CallFrame *)(cf))->callerPtr) #define Tcl_CallFrame_procPtr(cf) ((CallFrame *)(cf))->procPtr #define Tcl_CallFrame_varTablePtr(cf) ((CallFrame *)(cf))->varTablePtr #define Tcl_CallFrame_level(cf) ((CallFrame *)(cf))->level #define Tcl_CallFrame_isProcCallFrame(cf) ((CallFrame *)(cf))->isProcCallFrame #define Tcl_CallFrame_compiledLocals(cf) ((CallFrame *)(cf))->compiledLocals #define Tcl_CallFrame_numCompiledLocals(cf) ((CallFrame *)(cf))->numCompiledLocals #define Tcl_CallFrame_callerVarPtr(cf) ((Tcl_CallFrame*)((CallFrame *)(cf))->callerVarPtr) #define Tcl_CallFrame_objc(cf) ((CallFrame *)(cf))->objc #define Tcl_CallFrame_objv(cf) ((CallFrame *)(cf))->objv #define Tcl_CallFrame_clientData(cf) ((CallFrame *)(cf))->clientData #define Tcl_CallFrame_nsPtr(cf) ((Tcl_Namespace *)((CallFrame *)(cf))->nsPtr) #define Tcl_Namespace_cmdTablePtr(nsPtr) &((Namespace *)(nsPtr))->cmdTable #define Tcl_Namespace_varTablePtr(nsPtr) &((Namespace *)(nsPtr))->varTable #define Tcl_Namespace_childTablePtr(nsPtr) &((Namespace *)(nsPtr))->childTable #define Tcl_Namespace_activationCount(nsPtr) ((Namespace *)(nsPtr))->activationCount #define Tcl_Namespace_deleteProc(nsPtr) ((Namespace *)(nsPtr))->deleteProc #define Tcl_Namespace_parentPtr(nsPtr) ((Namespace *)(nsPtr))->parentPtr #define Tcl_Namespace_commandPathLength(nsPtr) ((Namespace *)(nsPtr))->commandPathLength #define Tcl_Namespace_commandPathArray(nsPtr) ((Namespace *)(nsPtr))->commandPathArray #define Tcl_Namespace_refCount(nsPtr) ((Namespace *)(nsPtr))->refCount #define Tcl_Namespace_flags(nsPtr) ((Namespace *)(nsPtr))->flags #define Tcl_Command_refCount(cmd) ((Command *)(cmd))->refCount #define Tcl_Command_cmdEpoch(cmd) ((Command *)(cmd))->cmdEpoch #define Tcl_Command_flags(cmd) ((Command *)(cmd))->flags /* the following items could be obtained from Tcl_GetCommandInfoFromToken(cmd, infoPtr) */ #define Tcl_Command_nsPtr(cmd) ((Tcl_Namespace*)(((Command *)(cmd))->nsPtr)) #define Tcl_Command_objProc(cmd) ((Command *)(cmd))->objProc #if defined(NRE) # define Tcl_Command_nreProc(cmd) ((Command *)(cmd))->nreProc #endif #define Tcl_Command_objClientData(cmd) ((Command *)(cmd))->objClientData #define Tcl_Command_proc(cmd) ((Command *)(cmd))->proc #define Tcl_Command_clientData(cmd) ((Command *)(cmd))->clientData #define Tcl_Command_deleteProc(cmd) ((Command *)(cmd))->deleteProc #define Tcl_Command_deleteData(cmd) ((Command *)(cmd))->deleteData /* * Var Reform Compatibility support. * * Definitions for accessing Tcl variable structures after varreform * in Tcl 8.5. */ #define TclIsCompiledLocalArgument(compiledLocalPtr) ((compiledLocalPtr)->flags & VAR_ARGUMENT) #define TclIsCompiledLocalTemporary(compiledLocalPtr) ((compiledLocalPtr)->flags & VAR_TEMPORARY) #define TclVarHashGetValue(hPtr) ((Var *) ((char *)(hPtr) - TclOffset(VarInHash, entry))) #define TclVarHashGetKey(varPtr) (((VarInHash *)(varPtr))->entry.key.objPtr) #define TclVarHashTablePtr(varTablePtr) &(varTablePtr)->table #define TclVarValue(type, varPtr, field) (type *)(varPtr)->value.field #if !defined(Tcl_HashSize) # define Tcl_HashSize(tablePtr) ((tablePtr)->numEntries) #endif generic/nsfCmdDefinitions.c000066400000000000000000000110051242365656200162540ustar00rootroot00000000000000/* * nsfCmdDefinitions.c -- * * Provide API for registering method definitions * and obtaining this data for introspection * * Copyright (C) 2014 Gustaf Neumann * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include "nsfInt.h" static Tcl_HashTable cmdDefinitonHashTable, *cmdDefinitonHashTablePtr = &cmdDefinitonHashTable; static int cmdDefinitonRefCount = 0; static NsfMutex cmdDefinitonMutex = 0; static int Register(Tcl_Interp *interp, Nsf_methodDefinition *methodDefinition); /* *---------------------------------------------------------------------- * Nsf_CmdDefinitionInit -- * * Initialize cmd definition structures * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void Nsf_CmdDefinitionInit(Tcl_Interp *interp) { assert(interp); NsfMutexLock(&cmdDefinitonMutex); if (cmdDefinitonRefCount == 0) { Tcl_InitHashTable(cmdDefinitonHashTablePtr, TCL_ONE_WORD_KEYS); } cmdDefinitonRefCount++; NsfMutexUnlock(&cmdDefinitonMutex); } /* *---------------------------------------------------------------------- * Nsf_CmdDefinitionRegister -- * * Register an array of cmd definitons * * Results: * TCL_OK * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_CmdDefinitionRegister(Tcl_Interp *interp, Nsf_methodDefinition *definitionRecords) { Nsf_methodDefinition *ePtr; assert(interp); assert(definitionRecords); for (ePtr = definitionRecords; ePtr->methodName; ePtr++) { Register(interp, ePtr); } return TCL_OK; } /* *---------------------------------------------------------------------- * Nsf_CmdDefinitionGet -- * * Obtain the definiton for a registered proc * * Results: * method definition or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ Nsf_methodDefinition * Nsf_CmdDefinitionGet(Tcl_ObjCmdProc *proc) { Tcl_HashEntry *hPtr; assert(proc); NsfMutexLock(&cmdDefinitonMutex); hPtr = Tcl_FindHashEntry(cmdDefinitonHashTablePtr, (char *)proc); NsfMutexUnlock(&cmdDefinitonMutex); if (hPtr != NULL) { return Tcl_GetHashValue(hPtr); } return NULL; } /* *---------------------------------------------------------------------- * Register -- * * Register a method Definition * * Results: * Tcl result code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int Register(Tcl_Interp *interp, Nsf_methodDefinition *methodDefinition) nonnull(1) nonnull(2); static int Register(Tcl_Interp *interp, Nsf_methodDefinition *methodDefinition) { Tcl_HashEntry *hPtr; int isNew; assert(interp); assert(methodDefinition); NsfMutexLock(&cmdDefinitonMutex); hPtr = Tcl_CreateHashEntry(cmdDefinitonHashTablePtr, (char *)methodDefinition->proc, &isNew); NsfMutexUnlock(&cmdDefinitonMutex); if (isNew) { Tcl_SetHashValue(hPtr, methodDefinition); return TCL_OK; } else { return NsfPrintError(interp, "proc %s is already registered", methodDefinition->methodName); } } /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsfCmdPtr.c000066400000000000000000000023461242365656200145560ustar00rootroot00000000000000/* * Conversion from CmdPtr to Class / Object */ static NSF_INLINE NsfObject*NsfGetObjectFromCmdPtr(Tcl_Command cmd) nonnull(1); static NSF_INLINE NsfClass*NsfGetClassFromCmdPtr(Tcl_Command cmd) nonnull(1); static NSF_INLINE ClientData NsfGetClientDataFromCmdPtr(Tcl_Command cmd) nonnull(1); static NSF_INLINE ClientData NsfGetClientDataFromCmdPtr(Tcl_Command cmd) { assert(cmd); /*fprintf(stderr, "objProc=%p %p\n", Tcl_Command_objProc(cmd),NsfObjDispatch);*/ if (likely(Tcl_Command_objProc(cmd) == NsfObjDispatch)) return Tcl_Command_objClientData(cmd); else { cmd = TclGetOriginalCommand(cmd); if (likely(cmd != NULL) && unlikely(Tcl_Command_objProc(cmd) == NsfObjDispatch)) { /*fprintf(stderr, "???? got cmd right in 2nd round\n");*/ return Tcl_Command_objClientData(cmd); } return NULL; } } static NSF_INLINE NsfClass* NsfGetClassFromCmdPtr(Tcl_Command cmd) { ClientData cd = NsfGetClientDataFromCmdPtr(cmd); assert(cmd); /*fprintf(stderr, "cd=%p\n",cd);*/ if (likely(cd != NULL)) { return NsfObjectToClass(cd); } else { return NULL; } } static NSF_INLINE NsfObject* NsfGetObjectFromCmdPtr(Tcl_Command cmd) { assert(cmd); return (NsfObject*) NsfGetClientDataFromCmdPtr(cmd); } generic/nsfCompile.c000066400000000000000000000140431242365656200147520ustar00rootroot00000000000000/* * nsfCompile.c -- * * Support for Bytecode in XOTcl/Nsf. This code was written for Tcl 8.4 * and required a small change for Tcl 8.4 (available from www.xotcl.org, * but not part of the Tcl Toolkit). This file was adopted only slightly * for naming changes etc., but requires more work to be revived. The * code is currently deactivated. * * Copyright (C) 2005-2014 Gustaf Neumann * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "nsfInt.h" #ifdef NSF_BYTECODE #include static CompileProc initProcNsCompile, nextCompile, selfCompile, selfDispatchCompile; static InstructionDesc instructionTable[] = { {"initProc", 1, 0, {OPERAND_NONE}}, {"next", 1, 0, {OPERAND_NONE}}, {"self", 1, 0, {OPERAND_NONE}}, {"dispatch", 2, 1, {OPERAND_UINT1}}, }; static NsfCompEnv instructions[] = { {0, 0, initProcNsCompile, NsfInitProcNSCmd}, {0, 0, nextCompile, NsfNextObjCmd}, {0, 0, selfCompile, NsfGetSelfObjCmd}, {0, 0, selfDispatchCompile, /*NsfSelfDispatchCmd*/NsfDirectSelfDispatch}, 0 }; NsfCompEnv * NsfGetCompEnv(void) { return &instructions[0]; } static int initProcNsCompile(Tcl_Interp *interp, Tcl_Parse *parsePtr, CompileEnv *envPtr) nonnull(1) nonnull(2) nonnull(3); static int initProcNsCompile(Tcl_Interp *interp, Tcl_Parse *parsePtr, CompileEnv *envPtr) { assert(interp); assert(parsePtr); assert(envPtr); if (parsePtr->numWords != 1) { Tcl_ResetResult(interp); Tcl_AppendToObj(Tcl_GetObjResult(interp), "wrong # args: should be '::nsf::initProcNS'", -1); envPtr->maxStackDepth = 0; return TCL_ERROR; } TclEmitOpcode(instructions[INST_INITPROC].bytecode, envPtr); envPtr->maxStackDepth = 0; return TCL_OK; } static int nextCompile(Tcl_Interp *interp, Tcl_Parse *parsePtr, CompileEnv *envPtr) nonnull(1) nonnull(2) nonnull(3); static int nextCompile(Tcl_Interp *interp, Tcl_Parse *parsePtr, CompileEnv *envPtr) { assert(interp); assert(parsePtr); assert(envPtr); if (parsePtr->numWords != 1) return TCL_OUT_LINE_COMPILE; TclEmitOpcode(instructions[INST_NEXT].bytecode, envPtr); envPtr->maxStackDepth = 0; return TCL_OK; } static int selfCompile(Tcl_Interp *interp, Tcl_Parse *parsePtr, CompileEnv *envPtr) nonnull(1) nonnull(2) nonnull(3); static int selfCompile(Tcl_Interp *interp, Tcl_Parse *parsePtr, CompileEnv *envPtr) { assert(interp); assert(parsePtr); assert(envPtr); if (parsePtr->numWords != 1) return TCL_OUT_LINE_COMPILE; TclEmitOpcode(instructions[INST_SELF].bytecode, envPtr); envPtr->maxStackDepth = 0; return TCL_OK; } static int selfDispatchCompile(Tcl_Interp *interp, Tcl_Parse *parsePtr, CompileEnv *envPtr) nonnull(1) nonnull(2) nonnull(3); static int selfDispatchCompile(Tcl_Interp *interp, Tcl_Parse *parsePtr, CompileEnv *envPtr) { Tcl_Token *tokenPtr; int code, wordIdx; assert(interp); assert(parsePtr); assert(envPtr); /* fprintf(stderr, "****** selfDispatchCompile words=%d tokens=%d, avail=%d\n", parsePtr->numWords, parsePtr->numTokens, parsePtr->tokensAvailable); */ if (parsePtr->numWords > 255) return TCL_OUT_LINE_COMPILE; /*TclEmitOpcode(instructions[INST_SELF].bytecode, envPtr);*/ for (wordIdx=0, tokenPtr = parsePtr->tokenPtr + 0; wordIdx < parsePtr->numWords; wordIdx++, tokenPtr += (tokenPtr->numComponents + 1)) { /* fprintf(stderr," %d: %p token type=%d size=%d\n", wordIdx, tokenPtr, tokenPtr->type, tokenPtr->size ); */ if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { TclEmitPush(TclRegisterLiteral(envPtr, tokenPtr->start, tokenPtr->size, 0), envPtr); envPtr->maxStackDepth = 1; /* fprintf(stderr," %d: simple '%s' components=%d\n", wordIdx, tokenPtr->start, tokenPtr->numComponents); */ } else { /* fprintf(stderr," %d NOT simple '%s' components=%d\n", wordIdx, tokenPtr->start, tokenPtr->numComponents); */ code = TclCompileTokens(interp, tokenPtr+1, tokenPtr->numComponents, envPtr); if (code != TCL_OK) { return code; } } } /*fprintf(stderr, "maxdepth=%d, onStack=%d\n", envPtr->maxStackDepth, wordIdx); */ TclEmitInstInt1(instructions[INST_SELF_DISPATCH].bytecode, wordIdx, envPtr); envPtr->maxStackDepth = 0; return TCL_OK; } void NsfBytecodeInit(void) { int i; for(i=0; iobjClientData))) { instructions[i].cmdPtr->compileProc = instructions[i].compileProc; } } /*tclTraceCompile = 2;*/ } #endif /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsfDTrace.d000066400000000000000000000107631242365656200145320ustar00rootroot00000000000000/* * nsfDTrace.d -- * * Next Scripting Framework DTrace provider. * * Copyright (c) 2011-2014 Gustaf Neumann * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ typedef struct Tcl_Obj Tcl_Obj; /* * Next Scripting DTrace probes * * Modeled in alignment with the Tcl DTrace probes */ provider nsf { /***************************** proc probes *****************************/ /* * nsf*:::method-entry probe * triggered immediately before method bytecode execution * arg0: object name (string) * arg1: class/object name (string) * arg2: method name (string) * arg3: number of arguments (int) * arg4: array of proc argument objects (Tcl_Obj**) */ probe method__entry(char* object, char *class, char* method, int objc, Tcl_Obj **objv); /* * nsf*:::proc-return probe * triggered immediately after proc bytecode execution * arg0: object name (string) * arg1: class/object name (string) * arg2: method name (string) * arg3: return code (int) */ probe method__return(char *object, char *class, char* name, int code); /* * tcl*:::proc-result probe * triggered after proc-return probe and result processing * arg0: proc name (string) * arg1: return code (int) * arg2: proc result (string) * arg3: proc result object (Tcl_Obj*) */ /***************************** Object probes ******************************/ /* * nsf*:::object-alloc probe * triggered when an NSF object is allocated * arg0: object (string) * arg1: class (string) */ probe object__alloc(char *object, char *class); /* * nsf*:::object-free probe * triggered whean an NSF object is freeed * arg0: object (string) * arg1: class (string) */ probe object__free(char *object, char *class); /***************************** nsf configure probe ******************************/ /* * nsf*:::configure-probe probe * triggered when the ::nsf::configure is called * arg0-arg1: command arguments (strings) */ probe configure__probe(char *arg0, char *arg1); }; /* * Tcl types and constants for use in DTrace scripts */ typedef struct Tcl_ObjType { char *name; void *freeIntRepProc; void *dupIntRepProc; void *updateStringProc; void *setFromAnyProc; } Tcl_ObjType; struct Tcl_Obj { int refCount; char *bytes; int length; Tcl_ObjType *typePtr; union { long longValue; double doubleValue; void *otherValuePtr; int64_t wideValue; struct { void *ptr1; void *ptr2; } twoPtrValue; struct { void *ptr; unsigned long value; } ptrAndLongRep; } internalRep; }; enum return_codes { TCL_OK = 0, TCL_ERROR, TCL_RETURN, TCL_BREAK, TCL_CONTINUE }; #pragma D attributes Evolving/Evolving/Common provider nsf provider #pragma D attributes Private/Private/Common provider nsf module #pragma D attributes Private/Private/Common provider nsf function #pragma D attributes Evolving/Evolving/Common provider nsf name #pragma D attributes Evolving/Evolving/Common provider nsf args /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */ generic/nsfDTrace.h000066400000000000000000000103161242365656200145300ustar00rootroot00000000000000/* * nsfDTrace.h -- * * Generated by dtrace(1M). * * Copyright (c) 2011-2014 Gustaf Neumann * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #ifndef _NSFDTRACE_H #define _NSFDTRACE_H #include #ifdef __cplusplus extern "C" { #endif #define NSF_STABILITY "___dtrace_stability$nsf$v1$5_5_5_1_1_5_1_1_5_5_5_5_5_5_5" #define NSF_TYPEDEFS "___dtrace_typedefs$nsf$v2$54636c5f4f626a" #define NSF_CONFIGURE_PROBE(arg0, arg1) \ do { \ __asm__ volatile(".reference " NSF_TYPEDEFS); \ __dtrace_probe$nsf$configure__probe$v1$63686172202a$63686172202a(arg0, arg1); \ __asm__ volatile(".reference " NSF_STABILITY); \ } while (0) #define NSF_CONFIGURE_PROBE_ENABLED() \ __dtrace_isenabled$nsf$configure__probe$v1() #define NSF_METHOD_ENTRY(arg0, arg1, arg2, arg3, arg4) \ do { \ __asm__ volatile(".reference " NSF_TYPEDEFS); \ __dtrace_probe$nsf$method__entry$v1$63686172202a$63686172202a$63686172202a$696e74$54636c5f4f626a202a2a(arg0, arg1, arg2, arg3, arg4); \ __asm__ volatile(".reference " NSF_STABILITY); \ } while (0) #define NSF_METHOD_ENTRY_ENABLED() \ __dtrace_isenabled$nsf$method__entry$v1() #define NSF_METHOD_RETURN(arg0, arg1, arg2, arg3) \ do { \ __asm__ volatile(".reference " NSF_TYPEDEFS); \ __dtrace_probe$nsf$method__return$v1$63686172202a$63686172202a$63686172202a$696e74(arg0, arg1, arg2, arg3); \ __asm__ volatile(".reference " NSF_STABILITY); \ } while (0) #define NSF_METHOD_RETURN_ENABLED() \ __dtrace_isenabled$nsf$method__return$v1() #define NSF_OBJECT_ALLOC(arg0, arg1) \ do { \ __asm__ volatile(".reference " NSF_TYPEDEFS); \ __dtrace_probe$nsf$object__alloc$v1$63686172202a$63686172202a(arg0, arg1); \ __asm__ volatile(".reference " NSF_STABILITY); \ } while (0) #define NSF_OBJECT_ALLOC_ENABLED() \ __dtrace_isenabled$nsf$object__alloc$v1() #define NSF_OBJECT_FREE(arg0, arg1) \ do { \ __asm__ volatile(".reference " NSF_TYPEDEFS); \ __dtrace_probe$nsf$object__free$v1$63686172202a$63686172202a(arg0, arg1); \ __asm__ volatile(".reference " NSF_STABILITY); \ } while (0) #define NSF_OBJECT_FREE_ENABLED() \ __dtrace_isenabled$nsf$object__free$v1() extern void __dtrace_probe$nsf$configure__probe$v1$63686172202a$63686172202a(char *, char *); extern int __dtrace_isenabled$nsf$configure__probe$v1(void); extern void __dtrace_probe$nsf$method__entry$v1$63686172202a$63686172202a$63686172202a$696e74$54636c5f4f626a202a2a(char *, char *, char *, int, Tcl_Obj **); extern int __dtrace_isenabled$nsf$method__entry$v1(void); extern void __dtrace_probe$nsf$method__return$v1$63686172202a$63686172202a$63686172202a$696e74(char *, char *, char *, int); extern int __dtrace_isenabled$nsf$method__return$v1(void); extern void __dtrace_probe$nsf$object__alloc$v1$63686172202a$63686172202a(char *, char *); extern int __dtrace_isenabled$nsf$object__alloc$v1(void); extern void __dtrace_probe$nsf$object__free$v1$63686172202a$63686172202a(char *, char *); extern int __dtrace_isenabled$nsf$object__free$v1(void); #ifdef __cplusplus } #endif #endif /* _NSFDTRACE_H */ generic/nsfDebug.c000066400000000000000000000262311242365656200144120ustar00rootroot00000000000000/* * nsfDebug.c -- * * Debugging facilities for the Next Scripting Framework. * * Copyright (C) 1999-2014 Gustaf Neumann (a, b) * Copyright (C) 1999-2007 Uwe Zdun (a, b) * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "nsfInt.h" #include "nsfAccessInt.h" /* *---------------------------------------------------------------------- * NsfReportVars -- * * Report version numbers and configure options as tcl variables. * * Results: * None. * * Side effects: * Setting Tcl variables * *---------------------------------------------------------------------- */ #define NSF_XSTR(x) NSF_STR(x) #define NSF_STR(x) #x void NsfReportVars(Tcl_Interp *interp) { assert(interp); Tcl_SetVar(interp, "::nsf::version", NSF_VERSION, TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "::nsf::patchLevel", NSF_PATCHLEVEL, TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "::nsf::config(development)", NSF_XSTR(NsfConfigEnabled(NSF_DEVELOPMENT)), TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "::nsf::config(memcount)", NSF_XSTR(NsfConfigEnabled(NSF_MEM_COUNT)), TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "::nsf::config(memtrace)", NSF_XSTR(NsfConfigEnabled(NSF_MEM_TRACE)), TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "::nsf::config(profile)", NSF_XSTR(NsfConfigEnabled(NSF_PROFILE)), TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "::nsf::config(dtrace)", NSF_XSTR(NsfConfigEnabled(NSF_DTRACE)), TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "::nsf::config(assertions)", NSF_XSTR(NsfConfigEnabled(NSF_WITH_ASSERTIONS)), TCL_GLOBAL_ONLY); } /* *---------------------------------------------------------------------- * NsfStackDump -- * * Write the current callstack with various debugging infos to stderr. This * function is primarily for debugging proposes of the C implmenentation of * nsf. * * Results: * None. * * Side effects: * Debugging output * *---------------------------------------------------------------------- */ void NsfStackDump(Tcl_Interp *interp) nonnull(1); void NsfStackDump(Tcl_Interp *interp) { Interp *iPtr = (Interp *)interp; CallFrame *f = iPtr->framePtr, *v = iPtr->varFramePtr; Tcl_Obj *varCmdObj; assert(interp); varCmdObj = Tcl_NewObj(); fprintf (stderr, " TCL STACK:\n"); if (f == 0) fprintf(stderr, "- "); while (f) { Tcl_Obj *cmdObj = Tcl_NewObj(); fprintf(stderr, "\tFrame=%p ", f); if (f && f->isProcCallFrame && f->procPtr && f->procPtr->cmdPtr) { fprintf(stderr,"caller %p ", Tcl_CallFrame_callerPtr(f)); fprintf(stderr,"callerV %p ", Tcl_CallFrame_callerVarPtr(f)); Tcl_GetCommandFullName(interp, (Tcl_Command)f->procPtr->cmdPtr, cmdObj); fprintf(stderr, "%s (%p) lvl=%d\n", ObjStr(cmdObj), f->procPtr->cmdPtr, f->level); } else { if (f && f->varTablePtr) { fprintf(stderr, "var_table = %p ", f->varTablePtr); } fprintf(stderr, "- \n"); } DECR_REF_COUNT(cmdObj); f = f->callerPtr; } fprintf (stderr, " VARFRAME:\n"); fprintf(stderr, "\tFrame=%p ", v); if (v) { fprintf(stderr, "caller %p var_table %p ", v->callerPtr, v->varTablePtr); /* if (v->varTablePtr) panic(0, "testing");*/ } if (v && v->isProcCallFrame && v->procPtr && v->procPtr->cmdPtr) { Tcl_GetCommandFullName(interp, (Tcl_Command) v->procPtr->cmdPtr, varCmdObj); fprintf(stderr, " %s (%d)\n", ObjStr(varCmdObj), v->level); } else { fprintf(stderr, "- \n"); } DECR_REF_COUNT(varCmdObj); } /* *---------------------------------------------------------------------- * NsfPrintObjv -- * * Print the provided argument vector to stderr. This function is primarily * for debugging proposes of the C implmenentation of nsf. * * Results: * None. * * Side effects: * Debugging output * *---------------------------------------------------------------------- */ void NsfPrintObjv(char *string, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(3); void NsfPrintObjv(char *string, int objc, Tcl_Obj *CONST objv[]) { int j; assert(string); assert(objv); fprintf(stderr, "%s", string); for (j = 0; j < objc; j++) { /*fprintf(stderr, " objv[%d]=%s, ", j, objv[j] ? ObjStr(objv[j]) : "NULL");*/ fprintf(stderr, " objv[%d]=%s %p, ", j, objv[j] ? ObjStr(objv[j]) : "NULL", objv[j]); } fprintf(stderr, "\n"); } #ifdef NSF_MEM_COUNT /* *---------------------------------------------------------------------- * NsfMemCountGetTable -- * * Obtain the hash table structure * * Results: * None. * * Side effects: * Updateing Hash table * *---------------------------------------------------------------------- */ static Tcl_HashTable * NsfMemCountGetTable(int **initialized) nonnull(1); static Tcl_HashTable * NsfMemCountGetTable(int **initialized) { static Tcl_ThreadDataKey memCountTableKey; static Tcl_ThreadDataKey memCountFlagKey; Tcl_HashTable *tablePtr; assert(initialized); tablePtr = (Tcl_HashTable *)Tcl_GetThreadData(&memCountTableKey, sizeof(Tcl_HashTable)); *initialized = (int *)Tcl_GetThreadData(&memCountFlagKey, sizeof(int)); return tablePtr; } /* *---------------------------------------------------------------------- * NsfMemCountAlloc -- * * Bookkeeping function for memory und refcount debugging. This function * records the allocation of memory resources. The accompanying function is * NsfMemCountFree(). * * Results: * None. * * Side effects: * Updateing Hash table * *---------------------------------------------------------------------- */ void NsfMemCountAlloc(char *id, void *p) nonnull(1); void NsfMemCountAlloc(char *id, void *p) { int new, *tableInitialized; NsfMemCounter *entry; Tcl_HashTable *tablePtr = NsfMemCountGetTable(&tableInitialized); Tcl_HashEntry *hPtr; assert(id); if (!*tableInitialized) { fprintf(stderr, "+++ alloc %s %p\n", id, p); return; } hPtr = Tcl_CreateHashEntry(tablePtr, id, &new); #ifdef NSF_MEM_TRACE fprintf(stderr, "+++ alloc %s %p\n", id, p); #endif if (new) { entry = (NsfMemCounter*)ckalloc(sizeof(NsfMemCounter)); entry->count = 1; entry->peak = 1; Tcl_SetHashValue(hPtr, entry); } else { entry = (NsfMemCounter*) Tcl_GetHashValue(hPtr); entry->count++; if (entry->count > entry->peak) { entry->peak = entry->count; } } } /* *---------------------------------------------------------------------- * NsfMemCountFree -- * * Bookkeeping function for memory und refcount debugging. This function * records the deallocation of memory resources. The accompanying function * is NsfMemCountAlloc(). * * Results: * None. * * Side effects: * Updateing Hash table * *---------------------------------------------------------------------- */ void NsfMemCountFree(char *id, void *p) nonnull(1); void NsfMemCountFree(char *id, void *p) { NsfMemCounter *entry; int *tableInitialized; Tcl_HashTable *tablePtr = NsfMemCountGetTable(&tableInitialized); Tcl_HashEntry *hPtr; assert(id); if (!*tableInitialized) { fprintf(stderr, "+++ free %s %p !tableInitialized !\n", id, p); return; } #ifdef NSF_MEM_TRACE fprintf(stderr, "+++ free %s %p\n", id, p); #endif hPtr = Tcl_FindHashEntry(tablePtr, id); if (!hPtr) { fprintf(stderr, "******** MEM COUNT ALERT: Trying to free %p <%s>, " "but was not allocated\n", p, id); return; } entry = (NsfMemCounter *)Tcl_GetHashValue(hPtr); entry->count--; } /* *---------------------------------------------------------------------- * NsfMemCountInit -- * * Initialize book-keeping for memory und refcount debugging. The * bookkeeping is realized via a per-interp hash table. * * Results: * None. * * Side effects: * Initializes a hash table * *---------------------------------------------------------------------- */ void NsfMemCountInit(void) { int *tableInitialized; Tcl_HashTable *tablePtr = NsfMemCountGetTable(&tableInitialized); if (!*tableInitialized) { Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS); } (*tableInitialized) ++; } /* *---------------------------------------------------------------------- * NsfMemCountRelease -- * * Terminate book-keeping for memory und refcount debugging. This function * prints the resulting book-information to stderr, in case of paired * allocs/frees and incr-ref-counts and dec-ref-counts, the Overall count * should be 0. * * Results: * None. * * Side effects: * Deletes the book-keeping hash table, outputs to stderr * *---------------------------------------------------------------------- */ void NsfMemCountRelease(void) { int *tableInitialized; Tcl_HashTable *tablePtr = NsfMemCountGetTable(&tableInitialized); #ifdef NSF_MEM_TRACE fprintf(stderr, "+++ release count %d\n", *tableInitialized); #endif if (!*tableInitialized) { fprintf(stderr, "+++ release called on uninitialized/free hash table\n"); return; } if (*tableInitialized == 1) { Tcl_HashSearch search; Tcl_HashEntry *hPtr; int count = 0; fprintf(stderr, "******** NSF MEM Count *********\n* count peak\n"); for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { char *id = Tcl_GetHashKey(tablePtr, hPtr); NsfMemCounter *entry = (NsfMemCounter*) Tcl_GetHashValue(hPtr); count += entry->count; fprintf(stderr, "* %4d %6d %s\n", entry->count, entry->peak, id); ckfree ((char*) entry); } Tcl_DeleteHashTable(tablePtr); fprintf(stderr, "******** Count Overall = %d\n", count); } (*tableInitialized) --; } #endif /* * Local Variables: * mode: c * c-basic-offset: 2 * indent-tabs-mode: nil * fill-column: 78 * End: */ generic/nsfEnumerationType.c000066400000000000000000000121011242365656200165030ustar00rootroot00000000000000/* * nsfEnumerationType.c -- * * Provide API for registering enumeration types * and obtaining their domain. * * Copyright (C) 2014 Gustaf Neumann * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include "nsfInt.h" static Tcl_HashTable enumerationHashTable, *enumerationHashTablePtr = &enumerationHashTable; static int enumerationTypeRefCount = 0; static NsfMutex enumerationMutex = 0; static int Register(Tcl_Interp *interp, CONST char* domain, Nsf_TypeConverter *converter) nonnull(1) nonnull(3); /* *---------------------------------------------------------------------- * Nsf_EnumerationTypeInit -- * * Initialize enumeration type converters * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void Nsf_EnumerationTypeInit(Tcl_Interp *interp) { assert(interp); NsfMutexLock(&enumerationMutex); if (enumerationTypeRefCount == 0) { Tcl_InitHashTable(enumerationHashTablePtr, TCL_STRING_KEYS); } enumerationTypeRefCount++; NsfMutexUnlock(&enumerationMutex); } /* *---------------------------------------------------------------------- * Nsf_EnumerationTypeRegister -- * * Register an array of enumeration types * * Results: * TCL_OK * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_EnumerationTypeRegister(Tcl_Interp *interp, Nsf_EnumeratorConverterEntry *typeRecords) { Nsf_EnumeratorConverterEntry *ePtr; assert(interp); assert(typeRecords); for (ePtr = typeRecords; ePtr->converter; ePtr++) { int result = Register(interp, ePtr->domain, ePtr->converter); if (unlikely(result != TCL_OK)) { return result; } } return TCL_OK; } /* *---------------------------------------------------------------------- * Nsf_EnumerationTypeGetDomain -- * * Obtain the domain from an enumeration type converter * * Results: * domain as a string or NULL, if not successful * * Side effects: * None. * *---------------------------------------------------------------------- */ CONST char * Nsf_EnumerationTypeGetDomain(Nsf_TypeConverter *converter) { Tcl_HashEntry *hPtr; Tcl_HashSearch hSrch; CONST char* domain = NULL; assert(converter); NsfMutexLock(&enumerationMutex); for (hPtr = Tcl_FirstHashEntry(enumerationHashTablePtr, &hSrch); hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSrch)) { void *ptr = Tcl_GetHashValue(hPtr); if (ptr == converter) { domain = Tcl_GetHashKey(enumerationHashTablePtr, hPtr); break; } } NsfMutexUnlock(&enumerationMutex); return domain; } /* *---------------------------------------------------------------------- * Register -- * * Register a enumeration type converter and its domain. * * Results: * Tcl result code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int Register(Tcl_Interp *interp, CONST char* domain, Nsf_TypeConverter *converter) { Tcl_HashEntry *hPtr; int isNew; assert(interp); assert(converter); NsfMutexLock(&enumerationMutex); hPtr = Tcl_CreateHashEntry(enumerationHashTablePtr, domain, &isNew); NsfMutexUnlock(&enumerationMutex); if (isNew) { Tcl_SetHashValue(hPtr, converter); } else { /* * In general, it would make sense to return an error here, but for * multiple interps (e.g. slave interps) the register happens per * interp. So, not even a warning seems here appropriate */ /*return NsfPrintError(interp, "type converter %s is already registered", domain); NsfLog(interp, NSF_LOG_WARN, "type converter %s is already registered", domain); */ } return TCL_OK; } /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsfError.c000066400000000000000000000330751242365656200144610ustar00rootroot00000000000000/* * nsfError.c -- * * Error reporting functions for the Next Scripting Framework. * * Copyright (C) 1999-2014 Gustaf Neumann (a, b) * Copyright (C) 1999-2007 Uwe Zdun (a, b) * Copyright (C) 2011 Stefan Sobernig (b) * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include "nsfInt.h" /* function prototypes */ Tcl_Obj *NsfParamDefsSyntax(Tcl_Interp *interp, Nsf_Param CONST *paramsPtr, NsfObject *contextObject, CONST char *pattern) nonnull(1) nonnull(2) returns_nonnull; /* *---------------------------------------------------------------------- * * NsfDStringPrintf -- * * Appends to a Tcl_DString a formatted value. This function * iterates until it has sufficiently memory allocated. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void NsfDStringPrintf(Tcl_DString *dsPtr, CONST char *fmt, va_list vargs) { int result, failure, offset = dsPtr->length, avail = dsPtr->spaceAvl; va_list vargsCopy; /* * Work on a copy of the va_list so that the caller's copy is untouched */ va_copy(vargsCopy, vargs); result = vsnprintf(dsPtr->string + offset, avail, fmt, vargsCopy); va_end(vargsCopy); /* * Trap C99+ incompatabilities of certain vsnprintf() implementations * w.r.t. the result value: For example, old *nix implementations of * vsnprintf() as well as C89 implementations (as current MS Visual Compiler * runtimes) return -1 (or another negative number) upon overflowing the * buffer (rather than the number of required bytes as required by C99) and * upon other error conditions. This should not happen for the above size * estimation, however. Also, for MS VC runtimes, we use the vendor-specific * _vscprintf() * * Note: For MinGW and MinGW-w64, we assume that their ANSI-compliant * version of vsnprintf() is used. See __USE_MINGW_ANSI_STDIO in nsfInt.h */ #if defined(_MSC_VER) failure = (result == -1 && errno == ERANGE) || (result == avail) /* VC 12 */; #else assert(result > -1); failure = (result >= avail); #endif if (likely(failure == 0)) { /* * vsnprintf() has already copied all content, * we have just to adjust the length. */ Tcl_DStringSetLength(dsPtr, offset + result); } else { int addedStringLength; /* * vsnprintf() has already not copied all content, * we have to determine the required length (MS), * adjust the DString size and copy again. */ #if defined(_MSC_VER) va_copy(vargsCopy, vargs); addedStringLength = _vscprintf(fmt, vargsCopy); va_end(vargsCopy); #else addedStringLength = result; #endif Tcl_DStringSetLength(dsPtr, offset + addedStringLength); va_copy(vargsCopy, vargs); result = vsnprintf(dsPtr->string + offset, dsPtr->spaceAvl, fmt, vargsCopy); assert(result > -1); va_end(vargsCopy); } } /* *---------------------------------------------------------------------- * * NsfDStringArgv -- * * Appends argument vector to an initialized Tcl_DString. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void NsfDStringArgv(Tcl_DString *dsPtr, int objc, Tcl_Obj *CONST objv[]) { assert(dsPtr); assert(objv); if (objc > 0) { int i; Tcl_DStringAppendElement(dsPtr, NsfMethodName(objv[0])); for (i = 1; i < objc; i++) { Tcl_DStringAppendElement(dsPtr, ObjStr(objv[i])); } } } /* *---------------------------------------------------------------------- * * NsfPrintError -- * * Produce a formatted error message with a printf like semantics * * Results: * TCL_ERROR * * Side effects: * Sets the result message. * *---------------------------------------------------------------------- */ int NsfPrintError(Tcl_Interp *interp, CONST char *fmt, ...) { va_list ap; Tcl_DString ds; Tcl_DStringInit(&ds); va_start(ap, fmt); NsfDStringPrintf(&ds, fmt, ap); va_end(ap); Tcl_SetResult(interp, Tcl_DStringValue(&ds), TCL_VOLATILE); Tcl_DStringFree(&ds); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * NsfErrInProc -- * * Produce a general error message when an error occurs in a scripted nsf * method. * * Results: * TCL_ERROR * * Side effects: * Sets the result message. * *---------------------------------------------------------------------- */ int NsfErrInProc(Tcl_Interp *interp, Tcl_Obj *objName, Tcl_Obj *clName, CONST char *procName) { Tcl_DString errMsg; char *cName, *space; Tcl_DStringInit(&errMsg); Tcl_DStringAppend(&errMsg, "\n ", -1); if (clName) { cName = ObjStr(clName); space = " "; } else { cName = ""; space =""; } Tcl_DStringAppend(&errMsg, ObjStr(objName),-1); Tcl_DStringAppend(&errMsg, space, -1); Tcl_DStringAppend(&errMsg, cName, -1); Tcl_DStringAppend(&errMsg, "->", 2); Tcl_DStringAppend(&errMsg, procName, -1); Tcl_AddErrorInfo (interp, Tcl_DStringValue(&errMsg)); Tcl_DStringFree(&errMsg); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * NsfObjWrongArgs -- * * Produce a general error message when a nsf method is called with an * invalid argument list (wrong number of arguments). * * Results: * TCL_ERROR * * Side effects: * Sets the result message. * *---------------------------------------------------------------------- */ int NsfObjWrongArgs(Tcl_Interp *interp, CONST char *msg, Tcl_Obj *cmdName, Tcl_Obj *methodPathObj, char *arglist) { int need_space = 0; assert(interp); assert(msg); Tcl_ResetResult(interp); Tcl_AppendResult(interp, msg, " should be \"", (char *) NULL); if (cmdName) { Tcl_AppendResult(interp, ObjStr(cmdName), (char *) NULL); need_space = 1; } if (methodPathObj) { if (need_space) Tcl_AppendResult(interp, " ", (char *) NULL); INCR_REF_COUNT(methodPathObj); Tcl_AppendResult(interp, ObjStr(methodPathObj), (char *) NULL); DECR_REF_COUNT(methodPathObj); need_space = 1; } if (arglist != NULL) { if (need_space) Tcl_AppendResult(interp, " ", (char *) NULL); Tcl_AppendResult(interp, arglist, (char *) NULL); } Tcl_AppendResult(interp, "\"", (char *) NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * NsfArgumentError -- * * Produce a wrong number of argument error based on a parameter definition * * Results: * TCL_ERROR * * Side effects: * Sets the result message. * *---------------------------------------------------------------------- */ int NsfArgumentError(Tcl_Interp *interp, CONST char *errorMsg, Nsf_Param CONST *paramPtr, Tcl_Obj *cmdNameObj, Tcl_Obj *methodPathObj) { Tcl_Obj *argStringObj = NsfParamDefsSyntax(interp, paramPtr, NULL, NULL); assert(interp); assert(errorMsg); assert(paramPtr); NsfObjWrongArgs(interp, errorMsg, cmdNameObj, methodPathObj, ObjStr(argStringObj)); DECR_REF_COUNT2("paramDefsObj", argStringObj); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * NsfUnexpectedArgumentError -- * * Produce an error message about an unexpected argument (most likely, * too many arguments) * * Results: * TCL_ERROR * * Side effects: * Sets the result message. * *---------------------------------------------------------------------- */ int NsfUnexpectedArgumentError(Tcl_Interp *interp, CONST char *argumentString, Nsf_Object *object, Nsf_Param CONST *paramPtr, Tcl_Obj *methodPathObj) { Tcl_DString ds, *dsPtr = &ds; assert(interp); assert(argumentString); assert(paramPtr); assert(methodPathObj); DSTRING_INIT(dsPtr); Tcl_DStringAppend(dsPtr, "invalid argument '", -1); Tcl_DStringAppend(dsPtr, argumentString, -1); Tcl_DStringAppend(dsPtr, "', maybe too many arguments;", -1); NsfArgumentError(interp, Tcl_DStringValue(dsPtr), paramPtr, object ? object->cmdName : NULL, methodPathObj); DSTRING_FREE(dsPtr); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * NsfUnexpectedNonposArgumentError -- * * Produce an error message about an invalid nonposistional argument. * * Results: * TCL_ERROR * * Side effects: * Sets the result message. * *---------------------------------------------------------------------- */ int NsfUnexpectedNonposArgumentError(Tcl_Interp *interp, CONST char *argumentString, Nsf_Object *object, Nsf_Param CONST *currentParamPtr, Nsf_Param CONST *paramPtr, Tcl_Obj *methodPathObj) { Tcl_DString ds, *dsPtr = &ds; Nsf_Param CONST *pPtr; assert(interp); assert(argumentString); assert(currentParamPtr); assert(paramPtr); assert(methodPathObj); DSTRING_INIT(dsPtr); Tcl_DStringAppend(dsPtr, "invalid non-positional argument '", -1); Tcl_DStringAppend(dsPtr, argumentString, -1); Tcl_DStringAppend(dsPtr, "', valid are : ", -1); for (pPtr = currentParamPtr; pPtr->name && *pPtr->name == '-'; pPtr ++) { if (pPtr->flags & NSF_ARG_NOCONFIG) { continue; } Tcl_DStringAppend(dsPtr, pPtr->name, -1); Tcl_DStringAppend(dsPtr, ", ", -1); } Tcl_DStringTrunc(dsPtr, Tcl_DStringLength(dsPtr) - 2); Tcl_DStringAppend(dsPtr, ";\n", 2); NsfArgumentError(interp, Tcl_DStringValue(dsPtr), paramPtr, object ? object->cmdName : NULL, methodPathObj); DSTRING_FREE(dsPtr); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * NsfDispatchClientDataError -- * * Produce a error message when method was not dispatched on an object * * Results: * TCL_ERROR * * Side effects: * Sets the result message. * *---------------------------------------------------------------------- */ int NsfDispatchClientDataError(Tcl_Interp *interp, ClientData clientData, CONST char *what, CONST char *methodName) { assert(interp); assert(what); assert(methodName); if (clientData) { return NsfPrintError(interp, "method %s not dispatched on valid %s", methodName, what); } else { return NsfNoCurrentObjectError(interp, methodName); } } /* *---------------------------------------------------------------------- * * NsfNoCurrentObjectError -- * * Produce a error message when method was called outside the context of * a method * * Results: * TCL_ERROR * * Side effects: * Sets the result message. * *---------------------------------------------------------------------- */ int NsfNoCurrentObjectError(Tcl_Interp *interp, CONST char *what) { assert(interp); return NsfPrintError(interp, "no current object; %s called outside the context of a Next Scripting method", what ? what : "command"); } /* *---------------------------------------------------------------------- * * NsfObjErrType -- * * Produce a general error message when a nsf method is called with an * invalid value for some argument. * * Results: * TCL_ERROR * * Side effects: * Sets the result message. * *---------------------------------------------------------------------- */ int NsfObjErrType(Tcl_Interp *interp, CONST char *context, Tcl_Obj *value, CONST char *type, Nsf_Param CONST *paramPtr) { int named = (paramPtr && (paramPtr->flags & NSF_ARG_UNNAMED) == 0); int returnValue = !named && paramPtr && (paramPtr->flags & NSF_ARG_IS_RETURNVALUE); char *prevErrMsg = ObjStr(Tcl_GetObjResult(interp)); if (*prevErrMsg != '\0') { Tcl_AppendResult(interp, " 2nd error: ", (char *) NULL); } /*Tcl_ResetResult(interp);*/ if (context) { Tcl_AppendResult(interp, context, ": ", (char *) NULL); } Tcl_AppendResult(interp,"expected ", type, " but got \"", ObjStr(value), "\"", (char *) NULL); if (named) { Tcl_AppendResult(interp," for parameter \"", paramPtr->name, "\"", (char *) NULL); } else if (returnValue) { Tcl_AppendResult(interp," as return value", (char *) NULL); } return TCL_ERROR; } /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsfInt.decls000066400000000000000000000037021242365656200147640ustar00rootroot00000000000000# -*- Tcl -*- # # nsfInt.decls -- # # This file contains the declarations for all internal # functions that are exported by the Next Scripting Framework # (NSF) library via stubs tables. This file is used to # generate nsfIntDecls.h. # # Copyright (C) 1999-2014 Gustaf Neumann (a, b) # Copyright (C) 1999-2007 Uwe Zdun (a, b) # # (a) University of Essen # Specification of Software Systems # Altendorferstrasse 97-101 # D-45143 Essen, Germany # # (b) Vienna University of Economics and Business # Institute of Information Systems and New Media # A-1020, Welthandelsplatz 1 # Vienna, Austria # # This work is licensed under the MIT License http://www.opensource.org/licenses/MIT # # Copyright: # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # library nsf # Define the unsupported generic interfaces. interface nsfInt # # Functions used within the package, but not considered "public" # #declare 0 generic { #} #declare 1 generic { #} generic/nsfInt.h000066400000000000000000001073631242365656200141310ustar00rootroot00000000000000/* * nsfInt.h -- * * Declarations of the internally used API Functions of the Next * Scripting Framework. * * Copyright (C) 1999-2014 Gustaf Neumann (a, b) * Copyright (C) 1999-2007 Uwe Zdun (a, b) * Copyright (C) 2011 Stefan Sobernig (b) * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #ifndef _nsf_int_h_ #define _nsf_int_h_ #if defined(HAVE_STDINT_H) # define HAVE_INTPTR_T # define HAVE_UINTPTR_T #endif /* * MinGW and MinGW-w64 provide both MSCRT-compliant and ANSI-compliant * implementations of certain I/O operations (e.g., *printf()). By * setting __USE_MINGW_ANSI_STDIO to 1 explicitly, we can assume the * ANSI versions. * * Note: It is sufficient to test for __MINGW32__ to trap all MinGW * tool chains, including 64bit versions. See * http://sourceforge.net/p/predef/wiki/Compilers/#mingw-and-mingw-w64 */ #if defined(__MINGW32__) && !defined(__USE_MINGW_ANSI_STDIO) #define __USE_MINGW_ANSI_STDIO 1 #endif #include #include "nsf.h" #include #include #include #if defined(HAVE_TCL_COMPILE_H) # include #endif #if defined(NSF_PROFILE) # include #endif #if __GNUC_PREREQ(2, 95) /* Use gcc branch prediction hint to minimize cost of e.g. DTrace * ENABLED checks. */ # define unlikely(x) (__builtin_expect((x), 0)) # define likely(x) (__builtin_expect((x), 1)) #else # define unlikely(x) (x) # define likely(x) (x) #endif #if __GNUC_PREREQ(2, 96) # define pure __attribute__((pure)) #else # define pure #endif #if __GNUC_PREREQ(3, 3) # define nonnull(ARGS) __attribute__((__nonnull__(ARGS))) #else # define nonnull(ARGS) #endif #if __GNUC_PREREQ(4, 9) # define returns_nonnull __attribute__((returns_nonnull)) #else # define returns_nonnull #endif /* * Tries to use gcc __attribute__ unused and mangles the name, so the * attribute could not be used, if declared as unused. */ #ifdef UNUSED #elif __GNUC_PREREQ(2, 7) # define UNUSED(x) UNUSED_ ## x __attribute__((unused)) #elif defined(__LCLINT__) # define UNUSED(x) /*@unused@*/ (x) #else # define UNUSED(x) (x) #endif #if defined(NSF_DTRACE) # include "nsfDTrace.h" # define NSF_DTRACE_METHOD_ENTRY_ENABLED() unlikely(NSF_METHOD_ENTRY_ENABLED()) # define NSF_DTRACE_METHOD_RETURN_ENABLED() unlikely(NSF_METHOD_RETURN_ENABLED()) # define NSF_DTRACE_OBJECT_ALLOC_ENABLED() unlikely(NSF_OBJECT_ALLOC_ENABLED()) # define NSF_DTRACE_OBJECT_FREE_ENABLED() unlikely(NSF_OBJECT_FREE_ENABLED()) # define NSF_DTRACE_CONFIGURE_PROBE_ENABLED() unlikely(NSF_CONFIGURE_PROBE_ENABLED()) # define NSF_DTRACE_METHOD_ENTRY(a0, a1, a2, a3, a4) NSF_METHOD_ENTRY((a0), (a1), (a2), (a3), (a4)) # define NSF_DTRACE_METHOD_RETURN(a0, a1, a2, a3) NSF_METHOD_RETURN((a0), (a1), (a2), (a3)) # define NSF_DTRACE_OBJECT_ALLOC(a0, a1) NSF_OBJECT_ALLOC((a0), (a1)) # define NSF_DTRACE_OBJECT_FREE(a0, a1) NSF_OBJECT_FREE((a0), (a1)) # define NSF_DTRACE_CONFIGURE_PROBE(a0, a1) NSF_CONFIGURE_PROBE((a0), (a1)) #else # define NSF_DTRACE_METHOD_ENTRY_ENABLED() 0 # define NSF_DTRACE_METHOD_RETURN_ENABLED() 0 # define NSF_DTRACE_OBJECT_ALLOC_ENABLED() 0 # define NSF_DTRACE_OBJECT_FREE_ENABLED() 0 # define NSF_DTRACE_CONFIGURE_PROBE_ENABLED() 0 # define NSF_DTRACE_METHOD_ENTRY(a0, a1, a2, a3, a4) {} # define NSF_DTRACE_METHOD_RETURN(a0, a1, a2, a3) {} # define NSF_DTRACE_OBJECT_ALLOC(a0, a1) {} # define NSF_DTRACE_OBJECT_FREE(a0, a1) {} # define NSF_DTRACE_CONFIGURE_PROBE(a0, a1) {} #endif #ifdef DMALLOC # include "dmalloc.h" #endif /* * Makros */ #if defined(PRE86) # define Tcl_NRCallObjProc(interp, proc, cd, objc, objv) \ (*(proc))((cd), (interp), (objc), (objv)) #endif #ifdef NSF_MEM_COUNT EXTERN int nsfMemCountInterpCounter; typedef struct NsfMemCounter { int peak; int count; } NsfMemCounter; # define MEM_COUNT_ALLOC(id,p) NsfMemCountAlloc((id), (p)) # define MEM_COUNT_FREE(id,p) NsfMemCountFree((id), (p)) # define MEM_COUNT_INIT() NsfMemCountInit() # define MEM_COUNT_RELEASE() NsfMemCountRelease() #else # define MEM_COUNT_ALLOC(id,p) # define MEM_COUNT_FREE(id,p) # define MEM_COUNT_INIT() # define MEM_COUNT_RELEASE() #endif # define STRING_NEW(target, p, l) (target) = ckalloc((l)+1); strncpy((target), (p), (l)); *((target)+(l)) = '\0'; \ MEM_COUNT_ALLOC(#target, (target)) # define STRING_FREE(key, p) MEM_COUNT_FREE((key), (p)); ckfree((p)) #define DSTRING_INIT(dsPtr) Tcl_DStringInit(dsPtr); MEM_COUNT_ALLOC("DString",(dsPtr)) #define DSTRING_FREE(dsPtr) \ if ((dsPtr)->string != (dsPtr)->staticSpace) {Tcl_DStringFree(dsPtr);} MEM_COUNT_FREE("DString",(dsPtr)) #if USE_ASSOC_DATA # define RUNTIME_STATE(interp) ((NsfRuntimeState*)Tcl_GetAssocData((interp), "NsfRuntimeState", NULL)) #else # define RUNTIME_STATE(interp) ((NsfRuntimeState*)((Interp*)(interp))->globalNsPtr->clientData) #endif #define nr_elements(arr) ((int) (sizeof(arr) / sizeof((arr)[0]))) # define NEW(type) \ (type *)ckalloc(sizeof(type)); MEM_COUNT_ALLOC(#type, NULL) # define NEW_ARRAY(type,n) \ (type *)ckalloc(sizeof(type)*(n)); MEM_COUNT_ALLOC(#type "*", NULL) # define FREE(type, var) \ ckfree((char*) (var)); MEM_COUNT_FREE(#type,(var)) #define isAbsolutePath(m) (*(m) == ':' && (m)[1] == ':') #define isArgsString(m) (\ *(m) == 'a' && (m)[1] == 'r' && (m)[2] == 'g' && (m)[3] == 's' && \ (m)[4] == '\0') #define isBodyString(m) (\ *(m) == 'b' && (m)[1] == 'o' && (m)[2] == 'd' && (m)[3] == 'y' && \ (m)[4] == '\0') #define isCheckString(m) (\ *(m) == 'c' && (m)[1] == 'h' && (m)[2] == 'e' && (m)[3] == 'c' && \ (m)[4] == 'k' && (m)[5] == '\0') #define isCheckObjString(m) (\ *(m) == 'c' && (m)[1] == 'h' && (m)[2] == 'e' && (m)[3] == 'c' && \ (m)[4] == 'k' && (m)[5] == 'o' && (m)[6] == 'b' && (m)[7] == 'j' && \ (m)[8] == '\0') #define isCreateString(m) (\ *(m) == 'c' && (m)[1] == 'r' && (m)[2] == 'e' && (m)[3] == 'a' && \ (m)[4] == 't' && (m)[5] == 'e' && (m)[6] == '\0') #define isTypeString(m) (\ *(m) == 't' && (m)[1] == 'y' && (m)[2] == 'p' && (m)[3] == 'e' && \ (m)[4] == '\0') #define isObjectString(m) (\ *(m) == 'o' && (m)[1] == 'b' && (m)[2] == 'j' && (m)[3] == 'e' && \ (m)[4] == 'c' && (m)[5] == 't' && (m)[6] == '\0') #define isClassString(m) (\ *(m) == 'c' && (m)[1] == 'l' && (m)[2] == 'a' && (m)[3] == 's' && \ (m)[4] == 's' && (m)[5] == '\0') #if (defined(sun) || defined(__hpux)) && !defined(__GNUC__) # define USE_ALLOCA #endif #if defined(__IBMC__) && !defined(__GNUC__) # if __IBMC__ >= 0x0306 # define USE_ALLOCA # else # define USE_MALLOC # endif #endif #if defined(VISUAL_CC) # define USE_MALLOC #endif #if defined(__GNUC__) && !defined(USE_ALLOCA) && !defined(USE_MALLOC) # if !defined(NDEBUG) # define ALLOC_ON_STACK(type,n,var) \ int __##var##_count = (n); type __##var[(n)+2]; \ type *(var) = __##var + 1; (var)[-1] = var[__##var##_count] = (type)0xdeadbeaf # define FREE_ON_STACK(type,var) \ assert((var)[-1] == (var)[__##var##_count] && (var)[-1] == (type)0xdeadbeaf) # else # define ALLOC_ON_STACK(type,n,var) type (var)[(n)] # define FREE_ON_STACK(type,var) # endif #elif defined(USE_ALLOCA) # define ALLOC_ON_STACK(type,n,var) type *(var) = (type *)alloca((n)*sizeof(type)) # define FREE_ON_STACK(type,var) #else # define ALLOC_ON_STACK(type,n,var) type *(var) = (type *)ckalloc((n)*sizeof(type)) # define FREE_ON_STACK(type,var) ckfree((char*)(var)) #endif #ifdef USE_ALLOCA # include #endif #if !defined(NDEBUG) # if defined(PRE86) # define ISOBJ(o) ((o) != NULL && (o) != (void*)0xdeadbeaf && ((o)->typePtr ? (o)->typePtr->name != NULL : 1) && (o)->length >= -1 && ((o)->length > 0 ? (o)->bytes!= NULL : 1) && (o)->refCount >= 0) # else # define ISOBJ(o) ((o) != NULL && (o) != (void*)0xdeadbeaf && ((o)->typePtr ? (o)->typePtr->name != NULL : 1) && ((o)->bytes != NULL ? (o)->length >= 0 : 1) && (o)->refCount >= 0) # endif #else # define ISOBJ(o) ((o) != NULL) #endif #define NSF_ABBREV_MIN_CHARS 4 /* * This was defined to be inline for anything !sun or __IBMC__ >= 0x0306, * but __hpux should also be checked - switched to only allow in gcc - JH */ #if defined(__GNUC__) && !defined(__STRICT_ANSI__) # define NSF_INLINE inline #else # define NSF_INLINE #endif #ifdef USE_TCL_STUBS # define DECR_REF_COUNT(A) \ MEM_COUNT_FREE("INCR_REF_COUNT" #A,(A)); assert((A)->refCount > -1); \ Tcl_DecrRefCount(A) # define DECR_REF_COUNT2(name,A) \ MEM_COUNT_FREE("INCR_REF_COUNT-" name,(A)); assert((A)->refCount > -1); \ Tcl_DecrRefCount(A) #else # define DECR_REF_COUNT(A) \ MEM_COUNT_FREE("INCR_REF_COUNT" #A,(A)); TclDecrRefCount(A) # define DECR_REF_COUNT2(name,A) \ MEM_COUNT_FREE("INCR_REF_COUNT-" name,(A)); TclDecrRefCount(A) #endif #define INCR_REF_COUNT(A) MEM_COUNT_ALLOC("INCR_REF_COUNT" #A,(A)); Tcl_IncrRefCount((A)) #define INCR_REF_COUNT2(name,A) \ /*fprintf(stderr, "c '%s'\n", ObjStr(A));*/ \ MEM_COUNT_ALLOC("INCR_REF_COUNT-" name,(A)); Tcl_IncrRefCount((A)) #define ObjStr(obj) ((obj)->bytes) ? ((obj)->bytes) : Tcl_GetString(obj) #define ClassName(cl) (((cl) ? ObjStr((cl)->object.cmdName) : "NULL")) #define ObjectName(obj) (((obj) ? ObjStr((obj)->cmdName) : "NULL")) #ifdef OBJDELETION_TRACE # define PRINTOBJ(ctx,obj) \ fprintf(stderr, " %s %p %s oid=%p teardown=%p destroyCalled=%d\n", \ (ctx),(obj),(obj)->teardown?ObjStr((obj)->cmdName):"(deleted)", \ (obj)->id, (obj)->teardown, \ ((obj)->flags & NSF_DESTROY_CALLED)) #else # define PRINTOBJ(ctx,obj) #endif /* * When an integer is printed, it might take so many digits */ #define LONG_AS_STRING 32 /* TCL_CONTINUE is defined as 4, from 5 on we can use app-specific return codes */ #define NSF_CHECK_FAILED 6 /* The NsfConfigEnabled() macro allows for querying whether a configuration macro (see above) is actually defined (and whether it expands to 1). This macro can be used both in CPP expressions (e.g., "#if NsfConfigEnabled(...)") and in C expressions (e.g., "if(NsfConfigEnabled(...))") Adapted from https://plus.google.com/+LinusTorvalds/posts/9gntjh57dXt */ #define NsfConfigEnabled(macro) NsfConfigEnabled_(macro) #define NsfMacroTest_1 , #define NsfConfigEnabled_(value) NsfConfigEnabled__(NsfMacroTest_##value) #define NsfConfigEnabled__(comma) NsfConfigEnabled___(comma 1, 0) #define NsfConfigEnabled___(_, v, ...) v /* * * Next Scripting Structures * */ /* * Filter structures */ typedef struct NsfFilterStack { Tcl_Command currentCmdPtr; Tcl_Obj *calledProc; struct NsfFilterStack *nextPtr; } NsfFilterStack; /* * Assertion structures */ typedef struct NsfTclObjList { Tcl_Obj *content; Tcl_Obj *payload; struct NsfTclObjList *nextPtr; } NsfTclObjList; typedef struct NsfProcAssertion { NsfTclObjList *pre; NsfTclObjList *post; } NsfProcAssertion; typedef struct NsfAssertionStore { NsfTclObjList *invariants; Tcl_HashTable procs; } NsfAssertionStore; typedef enum { /* powers of 2; add to ALL, if default; */ CHECK_NONE = 0, CHECK_CLINVAR = 1, CHECK_OBJINVAR = 2, CHECK_PRE = 4, CHECK_POST = 8, CHECK_INVAR = CHECK_CLINVAR + CHECK_OBJINVAR, CHECK_ALL = CHECK_INVAR + CHECK_PRE + CHECK_POST } CheckOptions; void NsfAssertionRename(Tcl_Interp *interp, Tcl_Command cmd, NsfAssertionStore *as, char *oldSimpleCmdName, char *newName); /* * mixins */ typedef struct NsfMixinStack { Tcl_Command currentCmdPtr; struct NsfMixinStack *nextPtr; } NsfMixinStack; /* * Generic command pointer list */ typedef struct NsfCmdList { Tcl_Command cmdPtr; ClientData clientData; struct NsfClass *clorobj; struct NsfCmdList *nextPtr; } NsfCmdList; typedef void (NsfFreeCmdListClientData) _ANSI_ARGS_((NsfCmdList*)); /* for incr string */ typedef struct NsfStringIncrStruct { char *buffer; char *start; size_t bufSize; int length; } NsfStringIncrStruct; /* * cmd flags */ #define NSF_CMD_CALL_PROTECTED_METHOD 0x00010000 #define NSF_CMD_CALL_PRIVATE_METHOD 0x00020000 #define NSF_CMD_REDEFINE_PROTECTED_METHOD 0x00040000 /* NSF_CMD_NONLEAF_METHOD is used to flag, if a Method implemented via cmd calls "next" */ #define NSF_CMD_NONLEAF_METHOD 0x00080000 #define NSF_CMD_CLASS_ONLY_METHOD 0x00100000 /* * object flags ... */ /* DESTROY_CALLED indicates that destroy was called on obj */ #define NSF_DESTROY_CALLED 0x0001 /* INIT_CALLED indicates that init was called on obj */ #define NSF_INIT_CALLED 0x0002 /* MIXIN_ORDER_VALID set when mixin order is valid */ #define NSF_MIXIN_ORDER_VALID 0x0004 /* MIXIN_ORDER_DEFINED set, when mixins are defined for obj */ #define NSF_MIXIN_ORDER_DEFINED 0x0008 #define NSF_MIXIN_ORDER_DEFINED_AND_VALID 0x000c /* FILTER_ORDER_VALID set, when filter order is valid */ #define NSF_FILTER_ORDER_VALID 0x0010 /* FILTER_ORDER_DEFINED set, when filters are defined for obj */ #define NSF_FILTER_ORDER_DEFINED 0x0020 #define NSF_FILTER_ORDER_DEFINED_AND_VALID 0x0030 /* class and object properties for objects */ #define NSF_IS_CLASS 0x0040 #define NSF_IS_ROOT_META_CLASS 0x0080 #define NSF_IS_ROOT_CLASS 0x0100 #define NSF_IS_SLOT_CONTAINER 0x0200 #define NSF_KEEP_CALLER_SELF 0x0400 #define NSF_PER_OBJECT_DISPATCH 0x0800 #define NSF_HAS_PER_OBJECT_SLOTS 0x1000 /* deletion states */ #define NSF_DESTROY_CALLED_SUCCESS 0x010000 /* requires flags to be int, not short */ #define NSF_DURING_DELETE 0x020000 #define NSF_DELETED 0x040000 #define NSF_RECREATE 0x080000 #define NSF_TCL_DELETE 0x100000 /* method invocations */ #define NSF_ARG_METHOD_INVOCATION (NSF_ARG_ALIAS|NSF_ARG_FORWARD|NSF_ARG_INITCMD|NSF_ARG_CMD) #define NSF_ARG_METHOD_CALL (NSF_ARG_ALIAS|NSF_ARG_FORWARD) /* Disallowed parameter options */ #define NSF_DISALLOWED_ARG_METHOD_PARAMETER (NSF_ARG_METHOD_INVOCATION|NSF_ARG_NOCONFIG|NSF_ARG_SLOTSET|NSF_ARG_SLOTINITIALIZE) #define NSF_DISALLOWED_ARG_SETTER (NSF_ARG_SWITCH|NSF_ARG_SUBST_DEFAULT|NSF_DISALLOWED_ARG_METHOD_PARAMETER) /*#define NSF_DISALLOWED_ARG_OBJECT_PARAMETER (NSF_ARG_SWITCH)*/ #define NSF_DISALLOWED_ARG_OBJECT_PARAMETER 0 #define NSF_DISALLOWED_ARG_VALUECHECK (NSF_ARG_SUBST_DEFAULT|NSF_ARG_METHOD_INVOCATION|NSF_ARG_SWITCH|NSF_ARG_CURRENTLY_UNKNOWN|NSF_ARG_SLOTSET|NSF_ARG_SLOTINITIALIZE) /* flags for ParseContext */ #define NSF_PC_MUST_DECR 0x0001 #define NSF_PC_IS_DEFAULT 0x0002 #define NSF_PC_INVERT_DEFAULT 0x0010 #define NSF_PC_STATUS_MUST_DECR 0x0001 #define NSF_PC_STATUS_FREE_OBJV 0x0002 #define NSF_PC_STATUS_FREE_CD 0x0004 /* method types */ #define NSF_METHODTYPE_ALIAS 0x0001 #define NSF_METHODTYPE_SCRIPTED 0x0002 #define NSF_METHODTYPE_SETTER 0x0004 #define NSF_METHODTYPE_FORWARDER 0x0008 #define NSF_METHODTYPE_OBJECT 0x0010 #define NSF_METHODTYPE_NSFPROC 0x0020 #define NSF_METHODTYPE_OTHER 0x0100 #define NSF_METHODTYPE_BUILTIN NSF_METHODTYPE_ALIAS|NSF_METHODTYPE_SETTER|NSF_METHODTYPE_FORWARDER|NSF_METHODTYPE_OTHER #define NSF_METHODTYPE_ALL NSF_METHODTYPE_SCRIPTED|NSF_METHODTYPE_BUILTIN|NSF_METHODTYPE_OBJECT #define NsfObjectSetClass(obj) \ (obj)->flags |= NSF_IS_CLASS #define NsfObjectClearClass(obj) \ (obj)->flags &= ~NSF_IS_CLASS #define NsfObjectIsClass(obj) \ ((obj)->flags & NSF_IS_CLASS) #define NsfObjectToClass(obj) \ (NsfClass*)((((NsfObject*)obj)->flags & NSF_IS_CLASS)?obj:NULL) /* * object and class internals */ typedef struct NsfParamDefs { Nsf_Param *paramsPtr; int nrParams; int refCount; int serial; Tcl_Obj *slotObj; Tcl_Obj *returns; } NsfParamDefs; typedef struct NsfParsedParam { NsfParamDefs *paramDefs; int possibleUnknowns; } NsfParsedParam; typedef struct NsfObjectOpt { NsfAssertionStore *assertions; NsfCmdList *objFilters; NsfCmdList *objMixins; ClientData clientData; #if defined(PER_OBJECT_PARAMETER_CACHING) NsfParsedParam *parsedParamPtr; int classParamPtrEpoch; #endif CONST char *volatileVarName; short checkoptions; } NsfObjectOpt; typedef struct NsfObject { Tcl_Obj *cmdName; Tcl_Command id; Tcl_Interp *teardown; struct NsfClass *cl; TclVarHashTable *varTablePtr; Tcl_Namespace *nsPtr; NsfObjectOpt *opt; struct NsfCmdList *filterOrder; struct NsfCmdList *mixinOrder; NsfFilterStack *filterStack; NsfMixinStack *mixinStack; int refCount; unsigned int flags; short activationCount; } NsfObject; typedef struct NsfClassOpt { NsfCmdList *classFilters; NsfCmdList *classMixins; NsfCmdList *isObjectMixinOf; NsfCmdList *isClassMixinOf; NsfAssertionStore *assertions; Tcl_Obj *mixinRegObjs; #ifdef NSF_OBJECTDATA Tcl_HashTable *objectdata; #endif Tcl_Command id; ClientData clientData; } NsfClassOpt; typedef struct NsfClass { struct NsfObject object; struct NsfClasses *super; struct NsfClasses *sub; struct NsfObjectSystem *osPtr; struct NsfClasses *order; Tcl_HashTable instances; Tcl_Namespace *nsPtr; NsfParsedParam *parsedParamPtr; NsfClassOpt *opt; short color; } NsfClass; typedef struct NsfClasses { struct NsfClass *cl; ClientData clientData; struct NsfClasses *nextPtr; } NsfClasses; /* * needed in nsf.c and in nsfShadow */ typedef struct NsfProcClientData { Tcl_Obj *procName; Tcl_Command cmd; NsfParamDefs *paramDefs; int with_ad; int checkAlwaysFlag; } NsfProcClientData; typedef enum SystemMethodsIdx { NSF_c_alloc_idx, NSF_c_create_idx, NSF_c_dealloc_idx, NSF_c_configureparameter_idx, NSF_c_recreate_idx, NSF_o_cleanup_idx, NSF_o_configure_idx, NSF_o_configureparameter_idx, NSF_o_defaultmethod_idx, NSF_o_destroy_idx, NSF_o_init_idx, NSF_o_move_idx, NSF_o_unknown_idx, NSF_s_get_idx, NSF_s_set_idx } SystemMethodsIdx; #if !defined(NSF_C) EXTERN CONST char *Nsf_SystemMethodOpts[]; #else CONST char *Nsf_SystemMethodOpts[] = { "-class.alloc", "-class.create", "-class.dealloc", "-class.configureparameter", "-class.recreate", "-object.cleanup", "-object.configure", "-object.configureparameter", "-object.defaultmethod", "-object.destroy", "-object.init", "-object.move", "-object.unknown", "-slot.get", "-slot.set", NULL }; #endif typedef struct NsfObjectSystem { NsfClass *rootClass; NsfClass *rootMetaClass; int overloadedMethods; int definedMethods; Tcl_Obj *methods[NSF_s_set_idx+1]; Tcl_Obj *handles[NSF_s_set_idx+1]; struct NsfObjectSystem *nextPtr; char protected[NSF_s_set_idx+1]; } NsfObjectSystem; /* * Next Scripting global names and strings * * We provide enums for efficient lookup for corresponding string * names and Tcl_Objs via global arrays. The "constant" Tcl_Objs are * built at start-up-time via Nsf_Init(). */ typedef enum { NSF_EMPTY, NSF_ZERO, NSF_ONE, /* methods called internally */ NSF_CONFIGURE, NSF_INITIALIZE, NSF_GET_PARAMETER_SPEC, NSF_SLOT_GET, NSF_SLOT_SET, /* var names */ NSF_AUTONAMES, NSF_DEFAULTMETACLASS, NSF_DEFAULTSUPERCLASS, NSF_ARRAY_INITCMD, NSF_ARRAY_CMD, NSF_ARRAY_ALIAS, NSF_ARRAY_PARAMETERSYNTAX, NSF_POSITION, NSF_POSITIONAL, NSF_CONFIGURABLE, NSF_PARAMETERSPEC, /* object/class names */ NSF_METHOD_PARAMETER_SLOT_OBJ, /* constants */ NSF_ALIAS, NSF_ARGS, NSF_CMD, NSF_FILTER, NSF_FORWARD, NSF_METHOD, NSF_OBJECT, NSF_SETTER, NSF_SETTERNAME, NSF_VALUECHECK, NSF_GUARD_OPTION, NSF___UNKNOWN__, NSF_ARRAY, NSF_GET, NSF_SET, NSF_OPTION_STRICT, NSF_OBJECT_UNKNOWN_HANDLER, NSF_ARGUMENT_UNKNOWN_HANDLER, /* Partly redefined Tcl commands; leave them together at the end */ NSF_EXPR, NSF_FORMAT, NSF_INFO_BODY, NSF_INFO_FRAME, NSF_INTERP, NSF_STRING_IS, NSF_EVAL, NSF_RENAME } NsfGlobalNames; #if !defined(NSF_C) EXTERN char *NsfGlobalStrings[]; #else char *NsfGlobalStrings[] = { "", "0", "1", /* methods called internally */ "configure", "initialize", "getParameterSpec", "value=get", "value=set", /* var names */ "__autonames", "__default_metaclass", "__default_superclass", "__initcmd", "__cmd", "::nsf::alias", "::nsf::parameter::syntax", "position", "positional", "configurable", "parameterSpec", /* object/class names */ "::nx::methodParameterSlot", /* constants */ "alias", "args", "cmd", "filter", "forward", "method", "object", "setter", "settername", "valuecheck", "-guard", "__unknown__", "::array", "get", "set", "-strict", /* nsf tcl commands */ "::nsf::object::unknown", "::nsf::argument::unknown", /* tcl commands */ "expr", "format", "::tcl::info::body", "::tcl::info::frame", "interp", "::tcl::string::is", "::eval", "rename" }; #endif #define NsfGlobalObjs RUNTIME_STATE(interp)->methodObjNames /* obj types */ EXTERN Tcl_ObjType NsfMixinregObjType; EXTERN int NsfMixinregGet(Tcl_Interp *interp, Tcl_Obj *obj, NsfClass **clPtr, Tcl_Obj **guardObj) nonnull(1) nonnull(2) nonnull(3) nonnull(4); EXTERN int NsfMixinregInvalidate(Tcl_Interp *interp, Tcl_Obj *obj) nonnull(1) nonnull(2); EXTERN Tcl_ObjType NsfFilterregObjType; EXTERN int NsfFilterregGet(Tcl_Interp *interp, Tcl_Obj *obj, Tcl_Obj **filterObj, Tcl_Obj **guardObj) nonnull(1) nonnull(2) nonnull(3) nonnull(4); EXTERN NsfClassOpt *NsfRequireClassOpt(/*@notnull@*/ NsfClass *cl) nonnull(1) returns_nonnull; /* Next Scripting ShadowTclCommands */ typedef struct NsfShadowTclCommandInfo { TclObjCmdProcType proc; ClientData clientData; } NsfShadowTclCommandInfo; typedef enum {SHADOW_LOAD=1, SHADOW_UNLOAD=0, SHADOW_REFETCH=2} NsfShadowOperations; typedef enum {NSF_PARAMS_NAMES, NSF_PARAMS_LIST, NSF_PARAMS_PARAMETER, NSF_PARAMS_SYNTAX} NsfParamsPrintStyle; int NsfCallCommand(Tcl_Interp *interp, NsfGlobalNames name, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(4); int NsfShadowTclCommands(Tcl_Interp *interp, NsfShadowOperations load) nonnull(1); Tcl_Obj *NsfMethodObj(NsfObject *object, int methodIdx) nonnull(1); /* * Next Scripting CallStack */ typedef struct NsfCallStackContent { NsfObject *self; NsfClass *cl; Tcl_Command cmdPtr; NsfFilterStack *filterStackEntry; Tcl_Obj *CONST* objv; int objc; unsigned int flags; unsigned short frameType; #if defined(NSF_PROFILE) || defined(NSF_DTRACE) long int startUsec; long int startSec; CONST char *methodName; #endif } NsfCallStackContent; #define NSF_CSC_TYPE_PLAIN 0U #define NSF_CSC_TYPE_ACTIVE_MIXIN 1U #define NSF_CSC_TYPE_ACTIVE_FILTER 2U #define NSF_CSC_TYPE_INACTIVE 4U #define NSF_CSC_TYPE_INACTIVE_MIXIN 5U #define NSF_CSC_TYPE_INACTIVE_FILTER 6U #define NSF_CSC_TYPE_GUARD 0x10U #define NSF_CSC_TYPE_ENSEMBLE 0x20U #define NSF_CSC_CALL_IS_NEXT 1U #define NSF_CSC_CALL_IS_GUARD 2U #define NSF_CSC_CALL_IS_ENSEMBLE 4U #define NSF_CSC_CALL_IS_COMPILE 8U #define NSF_CSC_IMMEDIATE 0x000000100U #define NSF_CSC_FORCE_FRAME 0x000000200U #define NSF_CSC_CALL_NO_UNKNOWN 0x000000400U #define NSF_CSC_CALL_IS_NRE 0x000002000U #define NSF_CSC_MIXIN_STACK_PUSHED 0x000004000U #define NSF_CSC_FILTER_STACK_PUSHED 0x000008000U #define NSF_CSC_METHOD_IS_UNKNOWN 0x000010000U /* flags for call method */ #define NSF_CM_NO_UNKNOWN 0x000000001U #define NSF_CM_NO_SHIFT 0x000000002U #define NSF_CM_IGNORE_PERMISSIONS 0x000000004U #define NSF_CM_NO_OBJECT_METHOD 0x000000008U #define NSF_CM_SYSTEM_METHOD 0x000000010U #define NSF_CM_LOCAL_METHOD 0x000000020U #define NSF_CM_INTRINSIC_METHOD 0x000000040U #define NSF_CM_KEEP_CALLER_SELF 0x000000080U #define NSF_CM_ENSEMBLE_UNKNOWN 0x008000000U #define NSF_CSC_COPY_FLAGS (NSF_CSC_MIXIN_STACK_PUSHED|NSF_CSC_FILTER_STACK_PUSHED|NSF_CSC_IMMEDIATE|NSF_CSC_FORCE_FRAME|NSF_CM_LOCAL_METHOD) #define NSF_VAR_TRIGGER_TRACE 1 #define NSF_VAR_REQUIRE_DEFINED 2 #define NSF_VAR_ISARRAY 4 /* * Tcl uses 01 and 02, TclOO uses 04 and 08, so leave some space free * for further extensions of tcl and tcloo... */ #define FRAME_IS_NSF_OBJECT 0x10000U #define FRAME_IS_NSF_METHOD 0x20000U #define FRAME_IS_NSF_CMETHOD 0x40000U #if defined(NRE) # define NRE_SANE_PATCH 1 # define NsfImmediateFromCallerFlags(flags) \ (((flags) & (NSF_CSC_CALL_IS_NRE|NSF_CSC_IMMEDIATE)) == NSF_CSC_CALL_IS_NRE ? 0 : NSF_CSC_IMMEDIATE) # if defined(NRE_SANE_PATCH) # define NsfNRRunCallbacks(interp, result, rootPtr) TclNRRunCallbacks(interp, result, rootPtr) # if !defined(TclStackFree) # define TclStackFree(interp, ptr) ckfree(ptr) # define TclStackAlloc(interp, size) ckalloc(size) # endif # else # define NsfNRRunCallbacks(interp, result, rootPtr) TclNRRunCallbacks(interp, result, rootPtr, 0) # define TEOV_callback NRE_callback # endif #endif #if defined(NSF_PROFILE) typedef struct NsfProfile { long int overallTime; long int startSec; long int startUSec; Tcl_HashTable objectData; Tcl_HashTable methodData; Tcl_HashTable procData; } NsfProfile; #endif typedef struct NsfRuntimeState { /* * The defined object systems */ struct NsfObjectSystem *objectSystems; /* * namespaces and cmds */ Tcl_Namespace *NsfNS; /* the ::nsf namespace */ Tcl_Namespace *NsfClassesNS; /* the ::nsf::classes namespace, where classes are created physically */ Tcl_ObjCmdProc *objInterpProc; /* cached result of TclGetObjInterpProc() */ Tcl_Command colonCmd; /* cmdPtr of cmd ":" to dispatch via cmdResolver */ Proc fakeProc; /* dummy proc strucure, used for C-implemented methods with local scope */ Tcl_Command currentMixinCmdPtr; /* cmdPtr of currently active mixin, used for "info activemixin" */ int objectMethodEpoch; int instanceMethodEpoch; #if defined(PER_OBJECT_PARAMETER_CACHING) int classParamPtrEpoch; #endif Tcl_Obj **methodObjNames; /* global objects of nsf */ struct NsfShadowTclCommandInfo *tclCommands; /* shadowed Tcl commands */ #if defined(CHECK_ACTIVATION_COUNTS) NsfClasses *cscList; #endif int errorCount; /* keep track of number of errors to avoid potential error loops */ int unknown; /* keep track whether an unknown method is currently called */ int overloadedMethods; /* bitarray for tracking overloaded methods */ /* * Configure options. The following do*-flags could be moved into a * bitarray, but we have only one state per interp, so the win on * memory is very little. */ int debugLevel; int doCheckArguments; int doCheckResults; int doFilters; int doKeepcmds; int doProfile; int doSoftrecreate; /* keep track of defined filters */ Tcl_HashTable activeFilterTablePtr; /* * shutdown handling */ int exitHandlerDestroyRound; #if defined(NSF_PROFILE) NsfProfile profile; #endif #if defined(NSF_STACKCHECK) void *bottomOfStack; void *maxStack; #endif NsfStringIncrStruct iss; /* used for new to create new symbols */ short guardCount; /* keep track of guard invocations */ ClientData clientData; } NsfRuntimeState; #define NSF_EXITHANDLER_OFF 0 #define NSF_EXITHANDLER_ON_SOFT_DESTROY 1 #define NSF_EXITHANDLER_ON_PHYSICAL_DESTROY 2 #ifdef NSF_OBJECTDATA EXTERN void NsfSetObjectData(struct NsfObject *obj, struct NsfClass *cl, ClientData data) nonnull(1) nonnull(2) nonnull(3); EXTERN int NsfGetObjectData(struct NsfObject *obj, struct NsfClass *cl, ClientData *data) nonnull(1) nonnull(2) nonnull(3); EXTERN int NsfUnsetObjectData(struct NsfObject *obj, struct NsfClass *cl) nonnull(1) nonnull(2); EXTERN void NsfFreeObjectData(NsfClass *cl) nonnull(1); #endif /* * NsfObject Reference Accounting */ EXTERN void NsfCleanupObject_(NsfObject *object) nonnull(1); #if defined(NSFOBJ_TRACE) # define NsfObjectRefCountIncr(obj) \ ((NsfObject *)obj)->refCount++; \ fprintf(stderr, "RefCountIncr %p count=%d %s\n", obj, ((NsfObject *)obj)->refCount, \ ((NsfObject *)obj)->cmdName?ObjStr(((NsfObject *)obj)->cmdName):"no name"); \ MEM_COUNT_ALLOC("NsfObject.refCount", obj) # define NsfObjectRefCountDecr(obj) \ (obj)->refCount--; \ fprintf(stderr, "RefCountDecr %p count=%d\n", obj, obj->refCount); \ MEM_COUNT_FREE("NsfObject.refCount", obj) #else # define NsfObjectRefCountIncr(obj) \ (obj)->refCount++; \ MEM_COUNT_ALLOC("NsfObject.refCount", obj) # define NsfObjectRefCountDecr(obj) \ (obj)->refCount--; \ MEM_COUNT_FREE("NsfObject.refCount", obj) #endif /* * * Internally used API functions * */ #if defined(NRE) # include "stubs8.6/nsfIntDecls.h" #else # include "stubs8.5/nsfIntDecls.h" #endif /* * Profiling functions */ #if defined(NSF_PROFILE) EXTERN void NsfProfileRecordMethodData(Tcl_Interp* interp, NsfCallStackContent *cscPtr) nonnull(1) nonnull(2); EXTERN void NsfProfileRecordProcData(Tcl_Interp *interp, char *methodName, long startSec, long startUsec) nonnull(1) nonnull(2); EXTERN void NsfProfileInit(Tcl_Interp *interp) nonnull(1); EXTERN void NsfProfileFree(Tcl_Interp *interp) nonnull(1); EXTERN void NsfProfileClearData(Tcl_Interp *interp) nonnull(1); EXTERN void NsfProfileGetData(Tcl_Interp *interp) nonnull(1); EXTERN NsfCallStackContent *NsfCallStackGetTopFrame(Tcl_Interp *interp, Tcl_CallFrame **framePtrPtr) nonnull(1); #endif /* * MEM Counting */ #ifdef NSF_MEM_COUNT void NsfMemCountAlloc(char *id, void *); void NsfMemCountFree(char *id, void *); void NsfMemCountInit(void); void NsfMemCountRelease(void); #endif /* NSF_MEM_COUNT */ /* * TCL_STACK_ALLOC_TRACE */ #if defined(TCL_STACK_ALLOC_TRACE) # define NsfTclStackFree(interp,ptr,msg) \ fprintf(stderr, "---- TclStackFree %p %s\n", ptr, msg);\ TclStackFree(interp,ptr) static char * NsfTclStackAlloc(Tcl_Interp *interp, size_t size, char *msg) { char *ptr = TclStackAlloc(interp, size); fprintf(stderr, "---- TclStackAlloc %p %s\n", ptr, msg); return ptr; } #else # define NsfTclStackFree(interp,ptr,msg) TclStackFree(interp,ptr) # define NsfTclStackAlloc(interp,size,msg) TclStackAlloc(interp,size) #endif /* * bytecode support */ #ifdef NSF_BYTECODE typedef struct NsfCompEnv { int bytecode; Command *cmdPtr; CompileProc *compileProc; Tcl_ObjCmdProc *callProc; } NsfCompEnv; typedef enum {INST_INITPROC, INST_NEXT, INST_SELF, INST_SELF_DISPATCH, LAST_INSTRUCTION} NsfByteCodeInstructions; Tcl_ObjCmdProc NsfInitProcNSCmd, NsfSelfDispatchCmd, NsfNextObjCmd, NsfGetSelfObjCmd; EXTERN NsfCompEnv *NsfGetCompEnv(void); int NsfDirectSelfDispatch(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2); #endif EXTERN int NsfGetClassFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, NsfClass **clPtr, int withUnknown) nonnull(1) nonnull(2) nonnull(3); EXTERN int NsfObjDispatch(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(4); EXTERN int NsfObjWrongArgs(Tcl_Interp *interp, CONST char *msg, Tcl_Obj *cmdName, Tcl_Obj *methodName, char *arglist) nonnull(1) nonnull(2); EXTERN CONST char *NsfMethodName(Tcl_Obj *methodObj) nonnull(1) returns_nonnull; EXTERN void NsfReportVars(Tcl_Interp *interp) nonnull(1); EXTERN void NsfDStringArgv(Tcl_DString *dsPtr, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(3); EXTERN Tcl_Obj *NsfMethodNamePath(Tcl_Interp *interp, Tcl_CallFrame *framePtr, CONST char *methodName) nonnull(1) nonnull(3) returns_nonnull; /* * Definition of methodEpoch macros */ #if defined(METHOD_OBJECT_TRACE) # define NsfInstanceMethodEpochIncr(msg) \ RUNTIME_STATE(interp)->instanceMethodEpoch++; \ fprintf(stderr, "+++ instanceMethodEpoch %d %s\n", RUNTIME_STATE(interp)->instanceMethodEpoch, msg) # define NsfObjectMethodEpochIncr(msg) \ RUNTIME_STATE(interp)->objectMethodEpoch++; \ fprintf(stderr, "+++ objectMethodEpoch %d %s\n", RUNTIME_STATE(interp)->objectMethodEpoch, msg) #else # define NsfInstanceMethodEpochIncr(msg) RUNTIME_STATE(interp)->instanceMethodEpoch++ # define NsfObjectMethodEpochIncr(msg) RUNTIME_STATE(interp)->objectMethodEpoch++ #endif #if defined(PER_OBJECT_PARAMETER_CACHING) # define NsfClassParamPtrEpochIncr(msg) RUNTIME_STATE(interp)->classParamPtrEpoch++ #else # define NsfClassParamPtrEpochIncr(msg) #endif /* * NsfFlag type */ EXTERN Tcl_ObjType NsfFlagObjType; EXTERN int NsfFlagObjSet(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *baseParamPtr, int serial, Nsf_Param CONST *paramPtr, Tcl_Obj *payload, int flags); typedef struct { CONST Nsf_Param *signature; int serial; Nsf_Param CONST *paramPtr; Tcl_Obj *payload; unsigned int flags; } NsfFlag; #define NSF_FLAG_DASHDAH 0x01 #define NSF_FLAG_CONTAINS_VALUE 0x02 /* * NsfMethodContext type */ EXTERN Tcl_ObjType NsfInstanceMethodObjType; EXTERN Tcl_ObjType NsfObjectMethodObjType; EXTERN int NsfMethodObjSet(Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_ObjType *objectType, void *context, int methodEpoch, Tcl_Command cmd, NsfClass *cl, unsigned int flags) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(6); typedef struct { void *context; int methodEpoch; Tcl_Command cmd; NsfClass *cl; unsigned int flags; } NsfMethodContext; /* functions from nsfUtil.c */ char *Nsf_ltoa(char *buf, long i, int *len) nonnull(1) nonnull(3); char *NsfStringIncr(NsfStringIncrStruct *iss) nonnull(1); void NsfStringIncrInit(NsfStringIncrStruct *iss) nonnull(1); void NsfStringIncrFree(NsfStringIncrStruct *iss) nonnull(1); /* * Nsf Enumeration type interface */ EXTERN void Nsf_EnumerationTypeInit(Tcl_Interp *interp) nonnull(1); EXTERN CONST char *Nsf_EnumerationTypeGetDomain(Nsf_TypeConverter *converter) nonnull(1); /* * Nsf Cmd definition interface */ EXTERN void Nsf_CmdDefinitionInit(Tcl_Interp *interp) nonnull(1); EXTERN Nsf_methodDefinition *Nsf_CmdDefinitionGet(Tcl_ObjCmdProc *proc) nonnull(1); #ifndef HAVE_STRNSTR char *strnstr(const char *buffer, const char *needle, size_t buffer_len); #endif /* In ANSI mode (ISO C89/90) compilers such as gcc and clang do not define the va_copy macro. However, they *do* in reasonably recent versions provide a prefixed (__*) one. The by-feature test below falls back to the prefixed version, if available, and provides a more general fallback to a simple assignment; this is primarily for MSVC; admittedly, this simplifcation is not generally portable to platform/compiler combos other then x86, but the best I can think of right now. One might constrain the assignment-fallback to a platform and leave va_copy undefined in uncaught platform cases (?). */ #ifndef va_copy #ifdef __va_copy #define va_copy(dest,src) __va_copy((dest),(src)) #else #define va_copy(dest,src) ((dest) = (src)) #endif #endif #endif /* _nsf_int_h_ */ generic/nsfObj.c000066400000000000000000000532311242365656200140760ustar00rootroot00000000000000/* * nsfError.c -- * * Tcl_Obj types provided by the Next Scripting Framework. * * Copyright (C) 1999-2014 Gustaf Neumann * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include "nsfInt.h" #include "nsfAccessInt.h" /* *---------------------------------------------------------------------- * * NsfMethodObjType Tcl_Obj type -- * * The NsfMethodObjType is an Tcl_Obj type carrying the result of * a method lookup. We define two types (NsfInstanceMethodObjType * and NsfObjectMethodObjType) sharing their implementation. The * type setting function NsfMethodObjSet() receives the intended * type. * *---------------------------------------------------------------------- */ static Tcl_FreeInternalRepProc MethodFreeInternalRep; static Tcl_DupInternalRepProc MethodDupInternalRep; Tcl_ObjType NsfInstanceMethodObjType = { "nsfInstanceMethod", /* name */ MethodFreeInternalRep, /* freeIntRepProc */ MethodDupInternalRep, /* dupIntRepProc */ NULL, /* updateStringProc */ NULL /* setFromAnyProc */ }; Tcl_ObjType NsfObjectMethodObjType = { "nsfObjectMethod", /* name */ MethodFreeInternalRep, /* freeIntRepProc */ MethodDupInternalRep, /* dupIntRepProc */ NULL, /* updateStringProc */ NULL /* setFromAnyProc */ }; /* * freeIntRepProc */ static void MethodFreeInternalRep( register Tcl_Obj *objPtr) /* Tcl_Obj structure object with internal * representation to free. */ { NsfMethodContext *mcPtr = (NsfMethodContext *)objPtr->internalRep.twoPtrValue.ptr1; if (mcPtr != NULL) { #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "MethodFreeInternalRep %p methodContext %p methodEpoch %d type <%s>\n", objPtr, mcPtr, mcPtr->methodEpoch, objPtr->typePtr ? objPtr->typePtr->name : "none"); #endif /* * ... and free structure */ FREE(NsfMethodContext, mcPtr); objPtr->internalRep.twoPtrValue.ptr1 = NULL; objPtr->typePtr = NULL; } } /* * dupIntRepProc */ static void MethodDupInternalRep( Tcl_Obj *srcObjPtr, Tcl_Obj *dstObjPtr) { register NsfMethodContext *srcMcPtr = srcObjPtr->internalRep.twoPtrValue.ptr1, *dstMcPtr; #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "MethodDupInternalRep src %p dst %p\n", srcObjPtr, dstObjPtr); #endif dstMcPtr = NEW(NsfMethodContext); /*fprintf(stderr, "MethodDupInternalRep allocated NsfMethodContext %p for %s\n", dstMcPtr, ObjStr(srcObjPtr));*/ memcpy(dstMcPtr, srcMcPtr, sizeof(NsfMethodContext)); dstObjPtr->typePtr = srcObjPtr->typePtr; dstObjPtr->internalRep.twoPtrValue.ptr1 = dstMcPtr; } /* *---------------------------------------------------------------------- * * NsfMethodObjSet -- * * Convert the provided Tcl_Obj into the type of NsfMethodContext. * *---------------------------------------------------------------------- */ int NsfMethodObjSet( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ register Tcl_Obj *objPtr, /* The object to convert. */ Tcl_ObjType *objectType, void *context, /* context (to avoid over-eager sharing) */ int methodEpoch, /* methodEpoch */ Tcl_Command cmd, /* the tclCommand behind the method */ NsfClass *cl, /* the object/class where the method was defined */ unsigned int flags /* flags */ ) { NsfMethodContext *mcPtr; #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "... NsfMethodObjSet %p %s context %p methodEpoch %d " "cmd %p cl %p %s old obj type <%s> flags %.6x\n", objPtr, ObjStr(objPtr), context, methodEpoch, cmd, cl, cl ? ClassName(cl) : "obj", objPtr->typePtr ? objPtr->typePtr->name : "none", flags); #endif /* * Free or reuse the old interal representation and store own * structure as internal representation. */ if (objPtr->typePtr != objectType) { #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "... NsfMethodObjSet frees old int rep %s\n", objPtr->typePtr ? objPtr->typePtr->name : "none"); #endif TclFreeIntRep(objPtr); mcPtr = NEW(NsfMethodContext); /*fprintf(stderr, "NsfMethodObjSet allocated NsfMethodContext %p for %s\n", mcPtr, ObjStr(objPtr));*/ objPtr->internalRep.twoPtrValue.ptr1 = (void *)mcPtr; objPtr->internalRep.twoPtrValue.ptr2 = NULL; objPtr->typePtr = objectType; #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "alloc %p methodContext %p methodEpoch %d type <%s> %s refCount %d\n", objPtr, mcPtr, methodEpoch, objectType->name, ObjStr(objPtr), objPtr->refCount); #endif } else { mcPtr = (NsfMethodContext *)objPtr->internalRep.twoPtrValue.ptr1; #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "... NsfMethodObjSet %p reuses interal rep, serial (%d/%d) refCount %d\n", objPtr, mcPtr->methodEpoch, methodEpoch, objPtr->refCount); #endif } assert(mcPtr); /* * add values to the structure */ mcPtr->context = context; mcPtr->methodEpoch = methodEpoch; mcPtr->cmd = cmd; mcPtr->cl = cl; mcPtr->flags = flags; return TCL_OK; } /* *---------------------------------------------------------------------- * * NsfFlagObjType -- * * The NsfFlagObjType is an Tcl_Obj type carrying the result of a * flag lookup. * *---------------------------------------------------------------------- */ static Tcl_FreeInternalRepProc FlagFreeInternalRep; static Tcl_DupInternalRepProc FlagDupInternalRep; Tcl_ObjType NsfFlagObjType = { "nsfFlag", /* name */ FlagFreeInternalRep, /* freeIntRepProc */ FlagDupInternalRep, /* dupIntRepProc */ NULL, /* updateStringProc */ NULL /* setFromAnyProc */ }; /* * freeIntRepProc */ static void FlagFreeInternalRep( Tcl_Obj *objPtr) /* Tcl_Obj structure object with internal * representation to free. */ { register NsfFlag *flagPtr = (NsfFlag *)objPtr->internalRep.twoPtrValue.ptr1; if (flagPtr != NULL) { /*fprintf(stderr, "FlagFreeInternalRep %p flagPtr %p serial (%d) payload %p\n", objPtr, flagPtr, flagPtr->serial, flagPtr->payload);*/ /* * Decrement refCounts; same as in NsfFlagSet() in the reuse branch */ if (flagPtr->payload) {DECR_REF_COUNT2("flagPtr->payload", flagPtr->payload);} /* * ... and free structure */ FREE(NsfFlag, flagPtr); objPtr->internalRep.twoPtrValue.ptr1 = NULL; } } /* * dupIntRepProc */ static void FlagDupInternalRep( Tcl_Obj *srcObjPtr, Tcl_Obj *dstObjPtr) { register NsfFlag *srcPtr = (NsfFlag *)srcObjPtr->internalRep.twoPtrValue.ptr1, *dstPtr; #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "FlagDupInternalRepx src %p dst %p\n", srcObjPtr, dstObjPtr); #endif dstPtr = NEW(NsfFlag); /*fprintf(stderr, "FlagDupInternalRep allocated NsfFlag %p for %s\n", dstPtr, ObjStr(srcObjPtr));*/ memcpy(dstPtr, srcPtr, sizeof(NsfFlag)); dstObjPtr->typePtr = srcObjPtr->typePtr; dstObjPtr->internalRep.twoPtrValue.ptr1 = dstPtr; } /* *---------------------------------------------------------------------- * * NsfFlagObjSet -- * * Convert the provided Tcl_Obj into the type of an nsf flag. * *---------------------------------------------------------------------- */ int NsfFlagObjSet( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ register Tcl_Obj *objPtr, /* The object to convert. */ Nsf_Param CONST *baseParamPtr, /* the full parameter block */ int serial, /* interface serial */ Nsf_Param CONST *paramPtr, /* a single parameter */ Tcl_Obj *payload, /* payload */ int flags /* detail infos */ ) { NsfFlag *flagPtr; /*fprintf(stderr, "NsfFlagObjSet %p %s signature %p (%d) param %p payload %p flags %.4x\n", objPtr, ObjStr(objPtr), baseParamPtr, serial, paramPtr, payload, flags);*/ /* * Free or reuse the old interal representation and store own * structure as internal representation. */ if (objPtr->typePtr != &NsfFlagObjType) { TclFreeIntRep(objPtr); flagPtr = NEW(NsfFlag); /*fprintf(stderr, "NsfFlagObjSet allocated NsfFlag %p for %s\n", flagPtr, ObjStr(objPtr));*/ objPtr->internalRep.twoPtrValue.ptr1 = (void *)flagPtr; objPtr->internalRep.twoPtrValue.ptr2 = NULL; objPtr->typePtr = &NsfFlagObjType; } else { flagPtr = (NsfFlag *)objPtr->internalRep.twoPtrValue.ptr1; /*fprintf(stderr, "NsfFlagObjSet %p reuses interal rep, serial (%d/%d)\n", objPtr, flagPtr->serial, serial);*/ if (flagPtr->payload) {DECR_REF_COUNT2("flagPtr->payload", flagPtr->payload);} } assert(flagPtr); /* * add values to the structure */ flagPtr->signature = baseParamPtr; flagPtr->serial = serial; flagPtr->paramPtr = paramPtr; flagPtr->payload = payload; if (payload) {INCR_REF_COUNT2("flagPtr->payload", flagPtr->payload);} flagPtr->flags = flags; return TCL_OK; } /* *---------------------------------------------------------------------- * * Mixinreg Tcl_Obj type -- * * The mixin registration type is an Tcl_Obj type carrying a * class and a guard object. The string representation might have * the form "/cls/" or "/cls/ -guard /expr/". When no guard * expression is provided (first form), the guard entry is NULL. * *---------------------------------------------------------------------- */ typedef struct { NsfClass *mixin; Tcl_Obj *guardObj; } Mixinreg; static Tcl_FreeInternalRepProc MixinregFreeInternalRep; static Tcl_SetFromAnyProc MixinregSetFromAny; static Tcl_DupInternalRepProc MixinregDupInternalRep; Tcl_ObjType NsfMixinregObjType = { "nsfMixinreg", /* name */ MixinregFreeInternalRep, /* freeIntRepProc */ MixinregDupInternalRep, /* dupIntRepProc */ NULL, /* updateStringProc */ MixinregSetFromAny /* setFromAnyProc */ }; /* * freeIntRepProc */ static void MixinregFreeInternalRep( register Tcl_Obj *objPtr) /* Mixinreg structure object with internal * representation to free. */ { Mixinreg *mixinRegPtr = (Mixinreg *)objPtr->internalRep.twoPtrValue.ptr1; assert(mixinRegPtr != NULL); /*fprintf(stderr, "MixinregFreeInternalRep freeing mixinReg %p class %p guard %p refcount before decr %d\n", mixinRegPtr, mixinRegPtr->mixin, mixinRegPtr->guardObj, (&(mixinRegPtr->mixin)->object)->refCount);*/ /* * Decrement refCounts */ NsfCleanupObject(&(mixinRegPtr->mixin)->object, "MixinregFreeInternalRep"); if (mixinRegPtr->guardObj) {DECR_REF_COUNT2("mixinRegPtr->guardObj", mixinRegPtr->guardObj);} /* * ... and free structure */ FREE(Mixinreg, mixinRegPtr); objPtr->internalRep.twoPtrValue.ptr1 = NULL; objPtr->typePtr = NULL; } /* * dupIntRepProc */ static void MixinregDupInternalRep( Tcl_Obj *srcObjPtr, Tcl_Obj *dstObjPtr) { register Mixinreg *srcPtr = (Mixinreg *)srcObjPtr->internalRep.twoPtrValue.ptr1, *dstPtr; assert(srcPtr); #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "MixinregDupInternalRep src %p dst %p\n", srcObjPtr, dstObjPtr); #endif dstPtr = NEW(Mixinreg); memcpy(dstPtr, srcPtr, sizeof(Mixinreg)); /* * increment refcounts */ NsfObjectRefCountIncr(&(srcPtr->mixin)->object); if (srcPtr->guardObj) {INCR_REF_COUNT2("mixinRegPtr->guardObj", srcPtr->guardObj);} /* * update destination obj */ dstObjPtr->typePtr = srcObjPtr->typePtr; dstObjPtr->internalRep.twoPtrValue.ptr1 = dstPtr; } /* * setFromAnyProc */ static int MixinregSetFromAny( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ Tcl_Obj *objPtr) /* The object to convert. */ { NsfClass *mixin = NULL; Tcl_Obj *guardObj = NULL, *nameObj = NULL; Mixinreg *mixinRegPtr; int oc; Tcl_Obj **ov; if (Tcl_ListObjGetElements(interp, objPtr, &oc, &ov) == TCL_OK) { if (oc == 1) { nameObj = ov[0]; /*} else if (oc == 2) { nameObj = ov[0]; guardObj = ov[1];*/ } else if (oc == 3 && !strcmp(ObjStr(ov[1]), NsfGlobalStrings[NSF_GUARD_OPTION])) { nameObj = ov[0]; guardObj = ov[2]; } else { nameObj = objPtr; } } else { return NsfObjErrType(interp, "mixin", nameObj, "a class as mixin", NULL); } /* * Syntax was ok. Try to lookup mixin classes: */ if (NsfGetClassFromObj(interp, nameObj, &mixin, 1) != TCL_OK) { return NsfObjErrType(interp, "mixin", nameObj, "a class as mixin", NULL); } /* * Conversion was ok. * Allocate structure ... */ mixinRegPtr = NEW(Mixinreg); mixinRegPtr->mixin = mixin; mixinRegPtr->guardObj = guardObj; /* * ... and increment refCounts */ NsfObjectRefCountIncr((&mixin->object)); if (guardObj) {INCR_REF_COUNT2("mixinRegPtr->guardObj", guardObj);} /* * Build list of tcl-objs per mixin class for invalidation. */ { NsfClassOpt *clOpt = NsfRequireClassOpt(mixin); if (clOpt->mixinRegObjs == NULL) { clOpt->mixinRegObjs = Tcl_NewListObj(1, &objPtr); INCR_REF_COUNT2("mixinRegObjs", clOpt->mixinRegObjs); } else { Tcl_ListObjAppendElement(interp, clOpt->mixinRegObjs, objPtr); } } /*fprintf(stderr, "MixinregSetFromAny alloc mixinReg %p class %p guard %p object->refCount %d\n", mixinRegPtr, mixinRegPtr->mixin, mixinRegPtr->guardObj, ((&mixin->object)->refCount));*/ /* * Free the old interal representation and store own structure as internal * representation. */ TclFreeIntRep(objPtr); objPtr->internalRep.twoPtrValue.ptr1 = (void *)mixinRegPtr; objPtr->internalRep.twoPtrValue.ptr2 = NULL; objPtr->typePtr = &NsfMixinregObjType; return TCL_OK; } /* *---------------------------------------------------------------------- * * NsfMixinregGet -- * * Return the internal representation of a mixinreg obj type to * keep internal rep local to this file. * * Results: * Tcl result code, arg two and three on success. * * Side effects: * None * *---------------------------------------------------------------------- */ int NsfMixinregGet(Tcl_Interp *interp, Tcl_Obj *obj, NsfClass **clPtr, Tcl_Obj **guardObj) { assert(interp); assert(obj); assert(clPtr); assert(guardObj); if (obj->typePtr == &NsfMixinregObjType) { Mixinreg *mixinRegPtr = obj->internalRep.twoPtrValue.ptr1; /* * We got a mixin with an included cmd, but both might be already deleted. */ if ((mixinRegPtr->mixin->object.flags & NSF_DELETED) != 0U || (Tcl_Command_flags(mixinRegPtr->mixin->object.id) & CMD_IS_DELETED) != 0U) { /* * The cmd is deleted. retry to refetch it. */ /*fprintf(stderr, "### we have to refetch internal rep of obj %p refCount %d\n", obj, obj->refCount);*/ if (MixinregSetFromAny(interp, obj) == TCL_OK) { mixinRegPtr = obj->internalRep.twoPtrValue.ptr1; } else { return TCL_ERROR; } } *guardObj = mixinRegPtr->guardObj; *clPtr = mixinRegPtr->mixin; return TCL_OK; } return TCL_ERROR; } /* *---------------------------------------------------------------------- * * NsfMixinregInvalidate -- * * MixinClasses keep a list of Tcl_Objs of type mixinReg. * When a class is deleted, this call makes sure, that * non-of these have th chance to point to a stale entry. * * Results: * Tcl result code * * Side effects: * Freeing internal rep of Tcl_Objs. * *---------------------------------------------------------------------- */ int NsfMixinregInvalidate(Tcl_Interp *interp, Tcl_Obj *obj) { int i, result, oc = 0; Tcl_Obj **objv; result = Tcl_ListObjGetElements(interp, obj, &oc, &objv); for (i= 0; i < oc; i++) { if (objv[i]->typePtr == &NsfMixinregObjType) { MixinregFreeInternalRep((objv[i])); } } return result; } /* *---------------------------------------------------------------------- * * Filterreg Tcl_Obj type -- * * The filter registration type is an Tcl_Obj type carrying a the * name of a filter and a guard object. The string representation * might have the form "/filter/" or "/filter/ -guard * /expr/". When no guard expression is provided (first form), * the guard entry is NULL. The primary purpose of this converter * is to provide symmetry to mixinregs and to provide meaningful * type names for introspection. * *---------------------------------------------------------------------- */ typedef struct { Tcl_Obj *filterObj; Tcl_Obj *guardObj; } Filterreg; static Tcl_FreeInternalRepProc FilterregFreeInternalRep; static Tcl_DupInternalRepProc FilterregDupInternalRep; static Tcl_SetFromAnyProc FilterregSetFromAny; Tcl_ObjType NsfFilterregObjType = { "nsfFilterreg", /* name */ FilterregFreeInternalRep, /* freeIntRepProc */ FilterregDupInternalRep, /* dupIntRepProc */ NULL, /* updateStringProc */ FilterregSetFromAny /* setFromAnyProc */ }; /* * freeIntRepProc */ static void FilterregFreeInternalRep( register Tcl_Obj *objPtr) /* Filterreg structure object with internal * representation to free. */ { Filterreg *filterregPtr = (Filterreg *)objPtr->internalRep.twoPtrValue.ptr1; assert(filterregPtr); /*fprintf(stderr, "FilterregFreeInternalRep freeing filterreg %p class %p guard %p\n", filterregPtr, filterregPtr->class, filterregPtr->guardObj);*/ /* * Decrement refCounts */ DECR_REF_COUNT2("filterregPtr->filterObj", filterregPtr->filterObj); if (filterregPtr->guardObj) {DECR_REF_COUNT2("filterregPtr->guardObj", filterregPtr->guardObj);} /* * ... and free structure */ FREE(Filterreg, filterregPtr); objPtr->internalRep.twoPtrValue.ptr1 = NULL; objPtr->typePtr = NULL; } /* * dupIntRepProc */ static void FilterregDupInternalRep( Tcl_Obj *srcObjPtr, Tcl_Obj *dstObjPtr) { register Filterreg *srcPtr = (Filterreg *)srcObjPtr->internalRep.twoPtrValue.ptr1, *dstPtr; assert(srcObjPtr); assert(dstObjPtr); assert(srcPtr); #if defined(METHOD_OBJECT_TRACE) fprintf(stderr, "FilterregDupInternalRep src %p dst %p\n", srcObjPtr, dstObjPtr); #endif dstPtr = NEW(Filterreg); memcpy(dstPtr, srcPtr, sizeof(Filterreg)); /* * increment refcounts */ INCR_REF_COUNT2("filterregPtr->filterObj", srcPtr->filterObj); if (srcPtr->guardObj) {INCR_REF_COUNT2("FilterregPtr->guardObj", srcPtr->guardObj);} /* * update destination obj */ dstObjPtr->typePtr = srcObjPtr->typePtr; dstObjPtr->internalRep.twoPtrValue.ptr1 = dstPtr; } /* * setFromAnyProc */ static int FilterregSetFromAny( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ register Tcl_Obj *objPtr) /* The object to convert. */ { Tcl_Obj *guardObj = NULL, *filterObj; Filterreg *filterregPtr; int oc; Tcl_Obj **ov; if (Tcl_ListObjGetElements(interp, objPtr, &oc, &ov) == TCL_OK) { if (oc == 1) { filterObj = ov[0]; /* } else if (oc == 2) { filterObj = ov[0]; guardObj = ov[1];*/ } else if (oc == 3 && !strcmp(ObjStr(ov[1]), NsfGlobalStrings[NSF_GUARD_OPTION])) { filterObj = ov[0]; guardObj = ov[2]; } else { return TCL_ERROR; } } else { return TCL_ERROR; } /* * Conversion was ok. * Allocate structure ... */ filterregPtr = NEW(Filterreg); filterregPtr->filterObj = filterObj; filterregPtr->guardObj = guardObj; /* * ... and increment refCounts */ INCR_REF_COUNT2("filterregPtr->filterObj", filterObj); if (guardObj) {INCR_REF_COUNT2("filterregPtr->guardObj", guardObj);} /*fprintf(stderr, "FilterregSetFromAny alloc filterreg %p class %p guard %p\n", filterregPtr, filterregPtr->filterObj, filterregPtr->guardObj);*/ /* * Free the old interal representation and store own structure as internal * representation. */ TclFreeIntRep(objPtr); objPtr->internalRep.twoPtrValue.ptr1 = (void *)filterregPtr; objPtr->internalRep.twoPtrValue.ptr2 = NULL; objPtr->typePtr = &NsfFilterregObjType; return TCL_OK; } /* *---------------------------------------------------------------------- * * NsfFilterregGet -- * * Return the internal representation of a filterreg obj type to * keep internal rep local to this file. * * Results: * Tcl result code, arg two and three on success. * * Side effects: * None * *---------------------------------------------------------------------- */ int NsfFilterregGet(Tcl_Interp *UNUSED(interp), Tcl_Obj *obj, Tcl_Obj **filterObj, Tcl_Obj **guardObj) { assert(obj); assert(filterObj); assert(guardObj); if (obj->typePtr == &NsfFilterregObjType) { Filterreg *filterregPtr = obj->internalRep.twoPtrValue.ptr1; *filterObj = filterregPtr->filterObj; *guardObj = filterregPtr->guardObj; return TCL_OK; } return TCL_ERROR; } /* * Filterreg type end */ /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsfObjectData.c000066400000000000000000000060611242365656200153630ustar00rootroot00000000000000/* * nsfObjectData.c -- * * NSF object data assumes NSF_OBJECTDATA to be compiled in. When * specified, it can be used to equip every object with an * additional payload from C. * * Copyright (C) 1999-2014 Gustaf Neumann (a, b) * Copyright (C) 1999-2007 Uwe Zdun (a, b) * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include "nsfInt.h" #ifdef NSF_OBJECTDATA EXTERN void NsfFreeObjectData(NsfClass* cl) { if (cl->opt && cl->opt->objectdata) { Tcl_DeleteHashTable(cl->opt->objectdata); ckfree((char*)cl->opt->objectdata); cl->opt->objectdata = 0; } } EXTERN void NsfSetObjectData(NsfObject* obj, NsfClass* cl, ClientData data) { Tcl_HashEntry *hPtr; int nw; NsfRequireClassOpt(cl); if (!cl->opt->objectdata) { cl->opt->objectdata = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(cl->opt->objectdata, TCL_ONE_WORD_KEYS); } hPtr = Tcl_CreateHashEntry(cl->opt->objectdata, (char*)obj, &nw); Tcl_SetHashValue(hPtr, data); } EXTERN int NsfGetObjectData(NsfObject* obj, NsfClass* cl, ClientData* data) { Tcl_HashEntry *hPtr; if (!cl->opt || !cl->opt->objectdata) return 0; hPtr = Tcl_FindHashEntry(cl->opt->objectdata, (char*)obj); if (data) *data = hPtr ? Tcl_GetHashValue(hPtr) : 0; return hPtr != 0; } EXTERN int NsfUnsetObjectData(NsfObject* obj, NsfClass* cl) { Tcl_HashEntry *hPtr; if (!cl->opt || !cl->opt->objectdata) return 0; hPtr = Tcl_FindHashEntry(cl->opt->objectdata, (char*)obj); if (hPtr) Tcl_DeleteHashEntry(hPtr); return hPtr != 0; } #endif /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsfPointer.c000066400000000000000000000252141242365656200150040ustar00rootroot00000000000000/* * nsfPointer.c -- * * Provide API for accessing mallocated data via Tcl. * This is used e.g. via the MongoDB interface. * * Copyright (C) 2011-2014 Gustaf Neumann * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "nsfInt.h" static Tcl_HashTable pointerHashTable, *pointerHashTablePtr = &pointerHashTable; static int pointerTableRefCount = 0; static NsfMutex pointerMutex = 0; /* *---------------------------------------------------------------------- * * Nsf_PointerAdd -- * * Add an entry to our locally maintained hash table and set its * value to the provided valuePtr. The keys are generated based on * the passed type and the counter obtained from the type * registration. * * Results: * Tcl result code * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_PointerAdd(Tcl_Interp *interp, char *buffer, CONST char *typeName, void *valuePtr) { int *counterPtr; assert(interp); assert(buffer); assert(typeName); assert(valuePtr); counterPtr = Nsf_PointerTypeLookup(interp, typeName); if (counterPtr) { Tcl_DString ds, *dsPtr = &ds; Tcl_HashEntry *hPtr; int isNew; Tcl_DStringInit(dsPtr); Tcl_DStringAppend(dsPtr, typeName, -1); Tcl_DStringAppend(dsPtr, ":%d", 3); NsfMutexLock(&pointerMutex); sprintf(buffer, Tcl_DStringValue(dsPtr), (*counterPtr)++); hPtr = Tcl_CreateHashEntry(pointerHashTablePtr, buffer, &isNew); NsfMutexUnlock(&pointerMutex); Tcl_SetHashValue(hPtr, valuePtr); /*fprintf(stderr, "Nsf_PointerAdd key '%s' prefix '%s' => %p value %p\n", buffer, typeName, hPtr, valuePtr);*/ Tcl_DStringFree(dsPtr); } else { return NsfPrintError(interp, "no type converter for %s registered", typeName); } return TCL_OK; } /* *---------------------------------------------------------------------- * * Nsf_PointerGet -- * * Get an entry to our locally maintained hash table and make sure * that the prefix matches (this ensures that the right type of * entry is obtained). If the prefix does not match, or there is no * such entry in the table, the function returns NULL. * * Results: * valuePtr or NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void * Nsf_PointerGet(char *key, CONST char *prefix) nonnull(1) nonnull(2); static void * Nsf_PointerGet(char *key, CONST char *prefix) { void *valuePtr = NULL; assert(key); assert(prefix); /* make sure to return the right type of hash entry */ if (strncmp(prefix, key, strlen(prefix)) == 0) { Tcl_HashEntry *hPtr; NsfMutexLock(&pointerMutex); hPtr = Tcl_CreateHashEntry(pointerHashTablePtr, key, NULL); if (hPtr) { valuePtr = Tcl_GetHashValue(hPtr); } NsfMutexUnlock(&pointerMutex); } return valuePtr; } /* *---------------------------------------------------------------------- * * Nsf_PointerGetHptr -- * * Find for a pointer the associated key for a valuePtr (reverse * lookup). The current implementation is quite slow in case there * are a high number of pointer values registered (which should not * be the case for the current usage patterns). It could certainly * be improved by a second hash table. The function should be run * under a callers mutex. * * Results: * key or NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_HashEntry * Nsf_PointerGetHptr(void *valuePtr) nonnull(1); static Tcl_HashEntry * Nsf_PointerGetHptr(void *valuePtr) { Tcl_HashEntry *hPtr; Tcl_HashSearch hSrch; assert(valuePtr); for (hPtr = Tcl_FirstHashEntry(pointerHashTablePtr, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { void *ptr = Tcl_GetHashValue(hPtr); if (ptr == valuePtr) { return hPtr; } } return NULL; } /* *---------------------------------------------------------------------- * * Nsf_PointerDelete -- * * Delete an hash entry from the locally maintained hash table and * free the associated memory, if the hash entry is * found. Normally, the key should be provided. If the key is not * available, we perform a reverse lookup from the hash table. * * Results: * valuePtr or NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_PointerDelete(CONST char *key, void *valuePtr, int free) { Tcl_HashEntry *hPtr; int result; assert(valuePtr); NsfMutexLock(&pointerMutex); hPtr = key ? Tcl_CreateHashEntry(pointerHashTablePtr, key, NULL) : Nsf_PointerGetHptr(valuePtr); if (hPtr) { if (free) {ckfree((char *)valuePtr);} Tcl_DeleteHashEntry(hPtr); result = TCL_OK; } else { result = TCL_ERROR; } NsfMutexUnlock(&pointerMutex); return result; } /* *---------------------------------------------------------------------- * Nsf_ConvertToPointer -- * * Nsf_TypeConverter setting the client data (passed to C functions) * to the valuePtr of the opaque structure. This nsf type converter * checks the passed value via the internally maintained pointer hash * table. * * Results: * Tcl result code, *clientData and **outObjPtr * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_ConvertToPointer(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) nonnull(1) nonnull(2) nonnull(3) nonnull(4) nonnull(5); int Nsf_ConvertToPointer(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { void *valuePtr; assert(interp); assert(objPtr); assert(pPtr); assert(clientData); assert(outObjPtr); *outObjPtr = objPtr; valuePtr = Nsf_PointerGet(ObjStr(objPtr), pPtr->type); if (valuePtr) { *clientData = valuePtr; return TCL_OK; } return NsfObjErrType(interp, NULL, objPtr, pPtr->type, (Nsf_Param *)pPtr); } /* *---------------------------------------------------------------------- * Nsf_PointerTypeRegister -- * * Register a pointer type which is identified by the type string * * Results: * Tcl result code. * * Side effects: * None. * *---------------------------------------------------------------------- */ int Nsf_PointerTypeRegister(Tcl_Interp *interp, CONST char* typeName, int *counterPtr) { Tcl_HashEntry *hPtr; int isNew; assert(interp); assert(typeName); assert(counterPtr); NsfMutexLock(&pointerMutex); hPtr = Tcl_CreateHashEntry(pointerHashTablePtr, typeName, &isNew); NsfMutexUnlock(&pointerMutex); if (isNew) { Tcl_SetHashValue(hPtr, counterPtr); return TCL_OK; } else { return NsfPrintError(interp, "type converter %s is already registered", typeName); } } /* *---------------------------------------------------------------------- * Nsf_PointerTypeLookup -- * * Lookup of type name. If the type name is registed, return the * converter or NULL otherwise. * * Results: * TypeConverter on success or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ void * Nsf_PointerTypeLookup(Tcl_Interp *interp, CONST char* typeName) { Tcl_HashEntry *hPtr; assert(interp); assert(typeName); NsfMutexLock(&pointerMutex); hPtr = Tcl_CreateHashEntry(pointerHashTablePtr, typeName, NULL); NsfMutexUnlock(&pointerMutex); if (hPtr) { return Tcl_GetHashValue(hPtr); } return NULL; } /* *---------------------------------------------------------------------- * Nsf_PointerInit -- * * Initialize the Pointer converter * * Results: * void * * Side effects: * None. * *---------------------------------------------------------------------- */ void Nsf_PointerInit(Tcl_Interp *interp) { assert(interp); NsfMutexLock(&pointerMutex); if (pointerTableRefCount == 0) { Tcl_InitHashTable(pointerHashTablePtr, TCL_STRING_KEYS); } pointerTableRefCount++; /* fprintf(stderr, "Nsf_PointerInit pointerTableRefCount == %d\n", pointerTableRefCount);*/ NsfMutexUnlock(&pointerMutex); } /* *---------------------------------------------------------------------- * Nsf_PointerExit -- * * Exit handler for the Pointer converter * * Results: * void * * Side effects: * None. * *---------------------------------------------------------------------- */ void Nsf_PointerExit(Tcl_Interp *interp) { assert(interp); NsfMutexLock(&pointerMutex); if (--pointerTableRefCount == 0) { if (RUNTIME_STATE(interp)->debugLevel >= 2) { Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; for (hPtr = Tcl_FirstHashEntry(pointerHashTablePtr, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { char *key = Tcl_GetHashKey(pointerHashTablePtr, hPtr); void *valuePtr = Tcl_GetHashValue(hPtr); /* * We can't use NsfLog here any more, since the Tcl procs are * already deleted. */ fprintf(stderr, "Nsf_PointerExit: we have still an entry %s with value %p\n", key, valuePtr); } } Tcl_DeleteHashTable(pointerHashTablePtr); } /*fprintf(stderr, "Nsf_PointerExit pointerTableRefCount == %d\n", pointerTableRefCount);*/ NsfMutexUnlock(&pointerMutex); } /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsfProfile.c000066400000000000000000000273451242365656200147730ustar00rootroot00000000000000/* * nsfProfile.c -- * * Provides profiling information about Next Scripting Framework internals. * For turning on profiling, NSF_PROFILE must be configured. * * Copyright (C) 2010-2014 Gustaf Neumann * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include "nsfInt.h" #if defined(NSF_PROFILE) typedef struct NsfProfileData { long microSec; long count; } NsfProfileData; /* *---------------------------------------------------------------------- * NsfProfileFillTable -- * * Insert or Update a keyed entry with provided microseconds and * update the counts for this entry. * * Results: * None * * Side effects: * Updated or created profile data entry * *---------------------------------------------------------------------- */ static void NsfProfileFillTable(Tcl_HashTable *table, char *keyStr, double totalMicroSec) nonnull(1) nonnull(2); static void NsfProfileFillTable(Tcl_HashTable *table, char *keyStr, double totalMicroSec) { NsfProfileData *value; Tcl_HashEntry *hPtr; int isNew; assert(table); assert(keyStr); hPtr = Tcl_CreateHashEntry(table, keyStr, &isNew); if (isNew) { value = (NsfProfileData *)ckalloc(sizeof(NsfProfileData)); value->microSec = 0; value->count = 0; Tcl_SetHashValue(hPtr, (ClientData) value); } else { value = (NsfProfileData *)Tcl_GetHashValue (hPtr); } value->microSec += totalMicroSec; value->count ++; } /* *---------------------------------------------------------------------- * NsfProfileRecordMethodData -- * * This function is invoked, when a call of a method ends. It * records profiling information based on the provided call stack * content and the caller. In particular, it records the time spent * in an object (identified with an objectKey) and the time spent * in the method (using methodKey). * * Results: * None * * Side effects: * Updated or created profile data entries * *---------------------------------------------------------------------- */ void NsfProfileRecordMethodData(Tcl_Interp *interp, NsfCallStackContent *cscPtr) { double totalMicroSec; NsfObject *obj = cscPtr->self; NsfClass *cl = cscPtr->cl; Tcl_DString methodKey, objectKey; NsfProfile *profile = &RUNTIME_STATE(interp)->profile; struct timeval trt; assert(interp); assert(cscPtr); gettimeofday(&trt, NULL); totalMicroSec = (trt.tv_sec - cscPtr->startSec) * 1000000 + (trt.tv_usec - cscPtr->startUsec); profile->overallTime += totalMicroSec; if (obj->teardown == 0 || !obj->id) { return; } Tcl_DStringInit(&objectKey); Tcl_DStringAppend(&objectKey, ObjectName(obj), -1); Tcl_DStringAppend(&objectKey, " ", 1); Tcl_DStringAppend(&objectKey, ClassName(obj->cl), -1); Tcl_DStringAppend(&objectKey, " ", 1); Tcl_DStringAppend(&objectKey, cscPtr->methodName, -1); Tcl_DStringInit(&methodKey); Tcl_DStringAppend(&methodKey, cl ? ObjStr(cl->object.cmdName) : ObjStr(obj->cmdName), -1); Tcl_DStringAppend(&methodKey, " ", 1); Tcl_DStringAppend(&methodKey, cscPtr->methodName, -1); if (cl) { Tcl_DStringAppend(&methodKey, " method", 7); } else { Tcl_DStringAppend(&methodKey, " object-method", 14); } { NsfCallStackContent *cscPtrTop = NsfCallStackGetTopFrame(interp, NULL); if (cscPtrTop) { NsfClass *cl = cscPtrTop->cl; NsfObject *obj = cscPtrTop->self; Tcl_DStringAppend(&methodKey, " ", 1); Tcl_DStringAppend(&methodKey, cl ? ObjStr(cl->object.cmdName) : ObjStr(obj->cmdName), -1); Tcl_DStringAppend(&methodKey, " ", 1); Tcl_DStringAppend(&methodKey, cscPtrTop->methodName, -1); if (cl) { Tcl_DStringAppend(&methodKey, " method", 7); } else { Tcl_DStringAppend(&methodKey, " object-method", 14); } } else { Tcl_DStringAppend(&methodKey, " - - -", 6); } } NsfProfileFillTable(&profile->objectData, Tcl_DStringValue(&objectKey), totalMicroSec); NsfProfileFillTable(&profile->methodData, Tcl_DStringValue(&methodKey), totalMicroSec); Tcl_DStringFree(&objectKey); Tcl_DStringFree(&methodKey); } /* *---------------------------------------------------------------------- * NsfProfileRecordMethodData -- * * This function is invoked, when a call of a nsf::proc. It records * time spent and count per nsf::proc. * * Results: * None * * Side effects: * Updated or created profile data entries * *---------------------------------------------------------------------- */ void NsfProfileRecordProcData(Tcl_Interp *interp, char *methodName, long startSec, long startUsec) { NsfProfile *profile = &RUNTIME_STATE(interp)->profile; double totalMicroSec; struct timeval trt; assert(interp); assert(methodName); gettimeofday(&trt, NULL); totalMicroSec = (trt.tv_sec - startSec) * 1000000 + (trt.tv_usec - startUsec); profile->overallTime += totalMicroSec; NsfProfileFillTable(&profile->procData, methodName, totalMicroSec); } /* *---------------------------------------------------------------------- * NsfProfileClearTable -- * * Clear all data in a profile table. * * Results: * None * * Side effects: * freed profile information. * *---------------------------------------------------------------------- */ static void NsfProfileClearTable(Tcl_HashTable *table) nonnull(1); static void NsfProfileClearTable(Tcl_HashTable *table) { Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; assert(table); for (hPtr = Tcl_FirstHashEntry(table, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { NsfProfileData *value = (NsfProfileData *) Tcl_GetHashValue(hPtr); ckfree((char *) value); Tcl_DeleteHashEntry(hPtr); } } /* *---------------------------------------------------------------------- * NsfProfileClearData -- * * Flush all data in all profile tables and reset the time * counters. * * Results: * None * * Side effects: * freed profile information. * *---------------------------------------------------------------------- */ void NsfProfileClearData(Tcl_Interp *interp) { NsfProfile *profilePtr = &RUNTIME_STATE(interp)->profile; struct timeval trt; assert(interp); NsfProfileClearTable(&profilePtr->objectData); NsfProfileClearTable(&profilePtr->methodData); NsfProfileClearTable(&profilePtr->procData); gettimeofday(&trt, NULL); profilePtr->startSec = trt.tv_sec; profilePtr->startUSec = trt.tv_usec; profilePtr->overallTime = 0; } /* *---------------------------------------------------------------------- * NsfProfileGetTable -- * * Return the profiling information for the specified profile table * in form of a Tcl list. * * Results: * Tcl List * * Side effects: * Nne. * *---------------------------------------------------------------------- */ static Tcl_Obj* NsfProfileGetTable(Tcl_Interp *interp, Tcl_HashTable *table) nonnull(1) nonnull(2); static Tcl_Obj* NsfProfileGetTable(Tcl_Interp *interp, Tcl_HashTable *table) { Tcl_Obj *list = Tcl_NewListObj(0, NULL); Tcl_HashSearch hSrch; Tcl_HashEntry *hPtr; assert(interp); assert(table); for (hPtr = Tcl_FirstHashEntry(table, &hSrch); hPtr; hPtr = Tcl_NextHashEntry(&hSrch)) { NsfProfileData *value = (NsfProfileData *) Tcl_GetHashValue(hPtr); char *key = Tcl_GetHashKey(table, hPtr); Tcl_Obj *subList = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, subList, Tcl_NewStringObj(key, -1)); Tcl_ListObjAppendElement(interp, subList, Tcl_NewIntObj(value->microSec)); Tcl_ListObjAppendElement(interp, subList, Tcl_NewIntObj(value->count)); Tcl_ListObjAppendElement(interp, list, subList); } return list; } /* *---------------------------------------------------------------------- * NsfProfileGetData -- * * Return recorded profiling information. This function returns a * list containing (a) the elapsed time since the last clear (or * init), (b) the cumulative time, (c) the list with the per-object * data and (d) the list with the method invocation data. * * Results: * Tcl List * * Side effects: * None. * *---------------------------------------------------------------------- */ void NsfProfileGetData(Tcl_Interp *interp) { Tcl_Obj *list = Tcl_NewListObj(0, NULL); NsfProfile *profilePtr = &RUNTIME_STATE(interp)->profile; long totalMicroSec; struct timeval trt; assert(interp); gettimeofday(&trt, NULL); totalMicroSec = (trt.tv_sec - profilePtr->startSec) * 1000000 + (trt.tv_usec - profilePtr->startUSec); Tcl_ListObjAppendElement(interp, list, Tcl_NewIntObj(totalMicroSec)); Tcl_ListObjAppendElement(interp, list, Tcl_NewIntObj(profilePtr->overallTime)); Tcl_ListObjAppendElement(interp, list, NsfProfileGetTable(interp, &profilePtr->objectData)); Tcl_ListObjAppendElement(interp, list, NsfProfileGetTable(interp, &profilePtr->methodData)); Tcl_ListObjAppendElement(interp, list, NsfProfileGetTable(interp, &profilePtr->procData)); Tcl_SetObjResult(interp, list); } /* *---------------------------------------------------------------------- * NsfProfileInit -- * * Initialize the profiling information. This is a one-time only * operation and initializes the hash table and the timing * results. The inverse operation is NsfProfileFree() * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void NsfProfileInit(Tcl_Interp *interp) { NsfProfile *profilePtr = &RUNTIME_STATE(interp)->profile; struct timeval trt; assert(interp); Tcl_InitHashTable(&profilePtr->objectData, TCL_STRING_KEYS); Tcl_InitHashTable(&profilePtr->methodData, TCL_STRING_KEYS); Tcl_InitHashTable(&profilePtr->procData, TCL_STRING_KEYS); gettimeofday(&trt, NULL); profilePtr->startSec = trt.tv_sec; profilePtr->startUSec = trt.tv_usec; profilePtr->overallTime = 0; } /* *---------------------------------------------------------------------- * NsfProfileFree -- * * Free all profiling information. This is a one-time only * operation only. The inverse operation is NsfProfileInit(). * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void NsfProfileFree(Tcl_Interp *interp) { NsfProfile *profilePtr = &RUNTIME_STATE(interp)->profile; assert(interp); NsfProfileClearData(interp); Tcl_DeleteHashTable(&profilePtr->objectData); Tcl_DeleteHashTable(&profilePtr->methodData); Tcl_DeleteHashTable(&profilePtr->procData); } #endif /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsfShadow.c000066400000000000000000000374511242365656200146170ustar00rootroot00000000000000/* * nsfShadow.c -- * * API support for shadowing (overloading) and accessing C-implemented * Tcl obj-commands. * * Copyright (C) 1999-2014 Gustaf Neumann * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include "nsfInt.h" #include "nsfAccessInt.h" #include "nsfCmdPtr.c" /* *---------------------------------------------------------------------- * NsfReplaceCommandCleanup -- * * Undo the effects of NsfReplaceCommand() for the Tcl command * referred by name. * * Results: * Tcl return code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int NsfReplaceCommandCleanup(Tcl_Interp *interp, NsfGlobalNames name) nonnull(1); static int NsfReplaceCommandCleanup(Tcl_Interp *interp, NsfGlobalNames name) { Tcl_Command cmd; int result = TCL_OK; NsfShadowTclCommandInfo *ti = &RUNTIME_STATE(interp)->tclCommands[name-NSF_EXPR]; assert(interp); /*fprintf(stderr," cleanup for %s ti=%p in %p\n", NsfGlobalStrings[name], ti, interp);*/ cmd = Tcl_GetCommandFromObj(interp, NsfGlobalObjs[name]); if (cmd != NULL) { Tcl_Command_objProc(cmd) = ti->proc; ti->proc = NULL; } else { result = TCL_ERROR; } return result; } /* *---------------------------------------------------------------------- * NsfReplaceCommandCheck -- * * Test, whether shadowing is still in effect, and refresh the * replacement if necessary. * * Results: * Tcl return code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void NsfReplaceCommandCheck(Tcl_Interp *interp, NsfGlobalNames name, Tcl_ObjCmdProc *proc) nonnull(1) nonnull(3); static void NsfReplaceCommandCheck(Tcl_Interp *interp, NsfGlobalNames name, Tcl_ObjCmdProc *proc) { NsfShadowTclCommandInfo *ti = &RUNTIME_STATE(interp)->tclCommands[name-NSF_EXPR]; Tcl_Command cmd; assert(interp); assert(proc); cmd = Tcl_GetCommandFromObj(interp, NsfGlobalObjs[name]); if (cmd != NULL && ti->proc && Tcl_Command_objProc(cmd) != proc) { /* fprintf(stderr, "we have to do something about %s %p %p\n", NsfGlobalStrings[name], Tcl_Command_objProc(cmd), proc); */ ti->proc = Tcl_Command_objProc(cmd); ti->clientData = Tcl_Command_objClientData(cmd); Tcl_Command_objProc(cmd) = proc; } } /* *---------------------------------------------------------------------- * NsfReplaceCommandCheck -- * * Lookup the objProc of a Tcl command and keep it around for * efficient calling. Replace the objProc optionally with a newly * specified one. * * Results: * Tcl return code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int NsfReplaceCommand(Tcl_Interp *interp, NsfGlobalNames name, Tcl_ObjCmdProc *nsfReplacementProc, int pass) nonnull(1); static int NsfReplaceCommand(Tcl_Interp *interp, NsfGlobalNames name, Tcl_ObjCmdProc *nsfReplacementProc, int pass) { Tcl_Command cmd; NsfShadowTclCommandInfo *ti = &RUNTIME_STATE(interp)->tclCommands[name-NSF_EXPR]; int result = TCL_OK; assert(interp); /*fprintf(stderr,"NsfReplaceCommand %d\n", name);*/ cmd = Tcl_GetCommandFromObj(interp, NsfGlobalObjs[name]); if (cmd == NULL) { result = TCL_ERROR; } else { Tcl_ObjCmdProc *objProc = Tcl_Command_objProc(cmd); if (nsfReplacementProc != objProc) { if (pass == 0) { /* setting values on first pass (must be locked here) */ ti->proc = objProc; ti->clientData = Tcl_Command_objClientData(cmd); } else if (ti->proc != objProc) { /*fprintf(stderr, "we have to refetch command for %s\n", NsfGlobalStrings[name]);*/ ti->proc = objProc; ti->clientData = Tcl_Command_objClientData(cmd); } if (nsfReplacementProc) { Tcl_Command_objProc(cmd) = nsfReplacementProc; } } } return result; } /* *---------------------------------------------------------------------- * Nsf_InfoBodyObjCmd -- * * TclObjCmd for shadowing "::tcl::info::body. In case the function * is called with an nsf::proc (which is technically a command, not * a proc), the original command fails ("not a proc"). We catch this * call here and test, whether the body is from an nsf::proc. If * so, we call tcl::info::body with the shadowed body. * * Example: * nsf::proc foo {-a} {puts $a}; info body foo * * Results: * Tcl return code. * * Side effects: * None. * *---------------------------------------------------------------------- */ EXTERN int NsfProcStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) nonnull(1) nonnull(2) nonnull(4); static int Nsf_InfoBodyObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Tcl_Command cmd; assert(interp); assert(objv); if (objc != 2) { /* wrong # args, let Tcl generate the error */ return NsfCallCommand(interp, NSF_INFO_BODY, objc, objv); } cmd = Tcl_FindCommand(interp, ObjStr(objv[1]), (Tcl_Namespace *)NULL, 0); if (cmd) { Tcl_ObjCmdProc *proc = Tcl_Command_objProc(cmd); ClientData clientData = Tcl_Command_objClientData(cmd); if (proc == NsfProcStub && clientData) { NsfProcClientData *tcd = clientData; Tcl_Obj *ov[2]; /* * The command is from an nsf::proc */ ov[0] = objv[0]; ov[1] = tcd->procName; return NsfCallCommand(interp, NSF_INFO_BODY, objc, ov); } } /* Actually call the cmd using Tcl's info body */ return NsfCallCommand(interp, NSF_INFO_BODY, objc, objv); } /* *---------------------------------------------------------------------- * Nsf_RenameObjCmd -- * * TclObjCmd for shadowing "::rename". We check whether the cmd * refers to an NsfObject. If so we have to destroy and/or "move" * it. Otherwise proceed by calling the shadowed function. * * Results: * Tcl return code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int Nsf_RenameObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Tcl_Command cmd; if (objc != 3) { /* wrong # args, let Tcl generate the error */ return NsfCallCommand(interp, NSF_RENAME, objc, objv); } /* if an obj/cl should be renamed => call the Nsf move method */ cmd = Tcl_FindCommand(interp, ObjStr(objv[1]), (Tcl_Namespace *)NULL, 0); if (cmd) { NsfObject *object = NsfGetObjectFromCmdPtr(cmd); Tcl_Obj *methodObj = object ? NsfMethodObj(object, NSF_o_move_idx) : NULL; Tcl_Command parentCmd; if (object && methodObj) { return NsfCallMethodWithArgs(interp, (Nsf_Object *)object, methodObj, objv[2], 1, 0, NSF_CSC_IMMEDIATE); } parentCmd = Tcl_FindCommand(interp, Tcl_Command_nsPtr(cmd)->fullName, (Tcl_Namespace *)NULL, 0); if (parentCmd) { NsfObjectMethodEpochIncr("::rename"); } } /* Actually rename the cmd using Tcl's rename*/ return NsfCallCommand(interp, NSF_RENAME, objc, objv); } /* *---------------------------------------------------------------------- * Nsf_InfoFrameObjCmd -- * * TclObjCmd for shadowing "::tcl::info::frame". First we call the * shadowed method. If it returns OK we check, whether the frame is * an NSF frame. If so, we remove from the result the misleading * "proc" and add "method", "class", "object" and "frametype". * * Results: * Tcl return code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int Nsf_InfoFrameObjCmd(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { int result; result = NsfCallCommand(interp, NSF_INFO_FRAME, objc, objv); if (result == TCL_OK && objc == 2) { int level, topLevel, frameFlags; CmdFrame *framePtr = Tcl_Interp_cmdFramePtr(interp); CallFrame *varFramePtr = Tcl_Interp_varFramePtr(interp); Tcl_Obj *resultObj = Tcl_GetObjResult(interp); /* level must be ok, otherwise we would not have a TCL_OK */ Tcl_GetIntFromObj(interp, objv[1], &level); /* todo: coroutine level messing is missing */ topLevel = (framePtr == NULL) ? 0 : framePtr->level; if (level > 0) { level -= topLevel; } while (++level <= 0 && varFramePtr && framePtr) { framePtr = framePtr->nextPtr; varFramePtr = varFramePtr->callerPtr; } frameFlags = varFramePtr ? Tcl_CallFrame_isProcCallFrame(varFramePtr) : 0; /*fprintf(stderr, " ... frame %p varFramePtr %p frameFlags %.6x\n", framePtr, varFramePtr, frameFlags); Tcl85showStack(interp);*/ if (frameFlags & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { NsfCallStackContent *cscPtr = ((NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr)); CONST char *frameType; Tcl_Obj *listObj, **ov; int oc, i; listObj = Tcl_NewListObj(0, NULL); /* remove "proc" element from list, if provided */ Tcl_ListObjGetElements(interp, resultObj, &oc, &ov); for (i=0; iself->cmdName); Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("class",5)); Tcl_ListObjAppendElement(interp, listObj, cscPtr->cl ? cscPtr->cl->object.cmdName : NsfGlobalObjs[NSF_EMPTY]); Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("method",6)); Tcl_ListObjAppendElement(interp, listObj, cscPtr->cmdPtr ? Tcl_NewStringObj(Tcl_GetCommandName(interp, cscPtr->cmdPtr), -1) : NsfGlobalObjs[NSF_EMPTY]); Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("frametype",9)); if (cscPtr->frameType == NSF_CSC_TYPE_PLAIN) { frameType = "intrinsic"; } else if (cscPtr->frameType & NSF_CSC_TYPE_ACTIVE_MIXIN) { frameType = "mixin"; } else if (cscPtr->frameType & NSF_CSC_TYPE_ACTIVE_FILTER) { frameType = "filter"; } else if (cscPtr->frameType & NSF_CSC_TYPE_GUARD) { frameType = "guard"; } else { frameType = "unknown"; } Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(frameType,-1)); Tcl_SetObjResult(interp, listObj); } else if (frameFlags & (FRAME_IS_NSF_OBJECT)) { NsfObject *object = (NsfObject *)Tcl_CallFrame_clientData(varFramePtr); /* Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); */ Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("object",6)); Tcl_ListObjAppendElement(interp, resultObj, object->cmdName); Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("frameType",9)); Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("object",6)); Tcl_SetObjResult(interp, resultObj); } } return result; } /* *---------------------------------------------------------------------- * NsfShadowTclCommands -- * * Load/refresh/unload shadowed Tcl commands. Essentially, the * shadowing function serve two things: * (a) lookup some Tcl ObjProcs, which are not available via global * symbols and make these available via NsfCallCommand(). * (b) some Tcl commands are actually shadowed; we perform some * pre- and/or postprocessing on these calls. * * Results: * Tcl return code. * * Side effects: * None. * *---------------------------------------------------------------------- */ int NsfShadowTclCommands(Tcl_Interp *interp, NsfShadowOperations load) { int rc = TCL_OK; assert(interp); if (load == SHADOW_LOAD) { int initialized = (RUNTIME_STATE(interp)->tclCommands != NULL); assert(initialized == 0); RUNTIME_STATE(interp)->tclCommands = NEW_ARRAY(NsfShadowTclCommandInfo, NSF_RENAME - NSF_EXPR + 1); /*fprintf(stderr, "+++ load tcl commands %d %d\n", load, initialized);*/ #ifdef USE_TCL_STUBS /* no commands are overloaded, these are only used for calling e.g. Tcl_ExprObjCmd(), Tcl_IncrObjCmd() and Tcl_SubstObjCmd(), which are not available in though the stub table */ rc |= NsfReplaceCommand(interp, NSF_EXPR, NULL, initialized); #endif rc |= NsfReplaceCommand(interp, NSF_FORMAT, NULL, initialized); rc |= NsfReplaceCommand(interp, NSF_INTERP, NULL, initialized); rc |= NsfReplaceCommand(interp, NSF_STRING_IS, NULL, initialized); /* for the following commands, we have to add our own semantics */ rc |= NsfReplaceCommand(interp, NSF_INFO_BODY, Nsf_InfoBodyObjCmd, initialized); rc |= NsfReplaceCommand(interp, NSF_INFO_FRAME, Nsf_InfoFrameObjCmd, initialized); rc |= NsfReplaceCommand(interp, NSF_RENAME, Nsf_RenameObjCmd, initialized); } else if (load == SHADOW_REFETCH) { NsfReplaceCommandCheck(interp, NSF_INFO_BODY, Nsf_InfoFrameObjCmd); NsfReplaceCommandCheck(interp, NSF_INFO_FRAME, Nsf_InfoFrameObjCmd); NsfReplaceCommandCheck(interp, NSF_RENAME, Nsf_RenameObjCmd); } else { NsfReplaceCommandCleanup(interp, NSF_INFO_BODY); NsfReplaceCommandCleanup(interp, NSF_INFO_FRAME); NsfReplaceCommandCleanup(interp, NSF_RENAME); FREE(NsfShadowTclCommandInfo*, RUNTIME_STATE(interp)->tclCommands); RUNTIME_STATE(interp)->tclCommands = NULL; } return rc; } /* *---------------------------------------------------------------------- * NsfCallCommand -- * * Calls Tcl Commands as direct as possible. The commands have to * be looked up previously via NsfShadowTclCommands(). objv[0] is * replaced with the predefined command name. * * Results: * Tcl return code. * * Side effects: * None. * *---------------------------------------------------------------------- */ int NsfCallCommand(Tcl_Interp *interp, NsfGlobalNames name, int objc, Tcl_Obj *CONST objv[]) { int result; NsfShadowTclCommandInfo *ti = &RUNTIME_STATE(interp)->tclCommands[name-NSF_EXPR]; ALLOC_ON_STACK(Tcl_Obj*, objc, ov); /* {int i; fprintf(stderr,"calling %s (%p %p) in %p, objc=%d ", NsfGlobalStrings[name], ti, ti->proc, interp, objc); for(i=0;i 1) { memcpy(ov+1, objv+1, sizeof(Tcl_Obj *)*(objc-1)); } result = Tcl_NRCallObjProc(interp, ti->proc, ti->clientData, objc, objv); FREE_ON_STACK(Tcl_Obj *, ov); return result; } /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsfStack.c000066400000000000000000001167421242365656200144400ustar00rootroot00000000000000/* * nsfStack.c -- * * Stack handling functions of the Next Scripting Framework. * * Copyright (C) 2010-2014 Gustaf Neumann * Copyright (C) 2011 Stefan Sobernig * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifdef CHECK_ACTIVATION_COUNTS static NsfClasses * NsfClassListUnlink(NsfClasses **firstPtrPtr, void *key); /* *---------------------------------------------------------------------- * CscListAdd -- * * Add an entry to the list of unstacked CSC entries. * * Results: * none * * Side effects: * List element added * *---------------------------------------------------------------------- */ static int CscListRemove(Tcl_Interp *interp, NsfCallStackContent *cscPtr, NsfClasses **cscListPtr) nonnull(1) nonnull(2) nonnull(3); static void CscListAdd(Tcl_Interp *interp, NsfCallStackContent *cscPtr) nonnull(1) nonnull(2); static void CscListAdd(Tcl_Interp *interp, NsfCallStackContent *cscPtr) { assert(interp); assert(cscPtr); NsfClassListAdd(&RUNTIME_STATE(interp)->cscList, (NsfClass *)cscPtr, NULL); } /* *---------------------------------------------------------------------- * CscListRemove -- * * Removes an entry from the list of unstacked CSC entries. * * Results: * true on success or 0 * * Side effects: * * List element potentially removed and freed. If a list turns * empty, the interp's state is updated. * *---------------------------------------------------------------------- */ static int CscListRemove(Tcl_Interp *interp, NsfCallStackContent *cscPtr, NsfClasses **cscListPtr) { NsfClasses *entryPtr, **cscList = &RUNTIME_STATE(interp)->cscList; assert(interp); assert(cscPtr); assert(cscListPtr); entryPtr = NsfClassListUnlink(cscList, cscPtr); if (entryPtr) { FREE(NsfClasses, entryPtr); } if (cscListPtr != NULL) { *cscListPtr = *cscList; } return (entryPtr != NULL); } #endif /* *---------------------------------------------------------------------- * NsfShowStack -- * * Print the contents of the call-stack to stderr. This function is * for debugging purposes only. * * Results: * None. * * Side effects: * Output on stderr. * *---------------------------------------------------------------------- */ void NsfShowStack(Tcl_Interp *interp) { Tcl_CallFrame *framePtr; fprintf(stderr, "NsfShowStack framePtr %p varFramePtr %p\n", Tcl_Interp_framePtr(interp), Tcl_Interp_varFramePtr(interp)); /* framePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); for (; framePtr; framePtr = Tcl_CallFrame_callerPtr(framePtr)) { fprintf(stderr, "... frame %p flags %.6x cd %p objv[0] %s\n", framePtr, Tcl_CallFrame_isProcCallFrame(framePtr), Tcl_CallFrame_clientData(framePtr), Tcl_CallFrame_objc(framePtr) ? ObjStr(Tcl_CallFrame_objv(framePtr)[0]) : "(null)"); }*/ framePtr = (Tcl_CallFrame *)Tcl_Interp_framePtr(interp); fprintf(stderr, "... varFrame flags clientData lvl ns\n"); for (; framePtr; framePtr = Tcl_CallFrame_callerPtr(framePtr)) { int frameFlags = Tcl_CallFrame_isProcCallFrame(framePtr); NsfCallStackContent *cscPtr = (frameFlags & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) ? ((NsfCallStackContent *)Tcl_CallFrame_clientData(framePtr)) : NULL; fprintf(stderr, "... %16p %.6x %16p %3d %16p %s ov %s %d", framePtr, frameFlags, Tcl_CallFrame_clientData(framePtr), Tcl_CallFrame_level(framePtr), Tcl_CallFrame_nsPtr(framePtr), Tcl_CallFrame_nsPtr(framePtr)->fullName, Tcl_CallFrame_objc(framePtr) && 0 ? ObjStr(Tcl_CallFrame_objv(framePtr)[0]) : "(null)", Tcl_CallFrame_objc(framePtr) ? Tcl_CallFrame_objc(framePtr) : -1); if (cscPtr) { fprintf(stderr, " csc %p frameType %.4x flags %.6x (%s.%p %s)\n", cscPtr, cscPtr ? cscPtr->frameType : -1, cscPtr ? cscPtr->flags : -1, cscPtr ? ObjectName(cscPtr->self) : "", cscPtr ? cscPtr->cmdPtr : NULL, cscPtr ? Tcl_GetCommandName(interp, cscPtr->cmdPtr) : "" ); } else { fprintf(stderr, " no csc"); if (frameFlags & FRAME_IS_NSF_OBJECT) { NsfObject *object = (NsfObject *)Tcl_CallFrame_clientData(framePtr); fprintf(stderr, " obj %p %s", object, ObjectName(object)); } fprintf(stderr, "\n"); } } } /* * Push and pop operations. * * Note that it is possible that between push and pop * a object->nsPtr can be created (e.g. during a read trace) */ /* *---------------------------------------------------------------------- * Nsf_PushFrameObj, Nsf_PopFrameObj -- * * Push or pop a frame with a call-stack content as an OBJECT * frame. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void Nsf_PushFrameObj(Tcl_Interp *interp, NsfObject *object, CallFrame *framePtr) nonnull(1) nonnull(2) nonnull(3); static void Nsf_PopFrameObj(Tcl_Interp *interp, CallFrame *framePtr) nonnull(1) nonnull(2); static void Nsf_PushFrameObj(Tcl_Interp *interp, NsfObject *object, CallFrame *framePtr) { assert(interp); assert(object); assert(framePtr); /*fprintf(stderr,"PUSH OBJECT_FRAME (Nsf_PushFrameObj) frame %p\n", framePtr);*/ if (object->nsPtr) { Tcl_PushCallFrame(interp, (Tcl_CallFrame *)framePtr, object->nsPtr, 0|FRAME_IS_NSF_OBJECT); } else { /* The object has no nsPtr, so we disguise as a proc, using fakeProc */ Tcl_PushCallFrame(interp, (Tcl_CallFrame *)framePtr, Tcl_CallFrame_nsPtr(Tcl_Interp_varFramePtr(interp)), FRAME_IS_PROC|FRAME_IS_NSF_OBJECT); Tcl_CallFrame_procPtr(framePtr) = &RUNTIME_STATE(interp)->fakeProc; if (unlikely(object->varTablePtr == NULL)) { object->varTablePtr = VarHashTableCreate(); } Tcl_CallFrame_varTablePtr(framePtr) = object->varTablePtr; } Tcl_CallFrame_clientData(framePtr) = (ClientData)object; } static void Nsf_PopFrameObj(Tcl_Interp *interp, CallFrame *framePtr) { assert(interp); assert(framePtr); /*fprintf(stderr,"POP OBJECT_FRAME (Nsf_PopFrameObj) frame %p, varTable %p set to NULL, already %d\n", framePtr, Tcl_CallFrame_varTablePtr(framePtr), Tcl_CallFrame_varTablePtr(framePtr) == NULL);*/ Tcl_CallFrame_varTablePtr(framePtr) = NULL; Tcl_PopCallFrame(interp); } /* *---------------------------------------------------------------------- * Nsf_PushFrameCsc, Nsf_PopFrameCsc -- * * Push or pop a frame with a call-stack content as a CMETHOD * frame. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ NSF_INLINE static void Nsf_PushFrameCsc(Tcl_Interp *interp, NsfCallStackContent *cscPtr, CallFrame *framePtr) nonnull(1) nonnull(2) nonnull(3); static void Nsf_PopFrameCsc(Tcl_Interp *interp, CallFrame *UNUSED(framePtr)) nonnull(1); NSF_INLINE static void Nsf_PushFrameCsc(Tcl_Interp *interp, NsfCallStackContent *cscPtr, CallFrame *framePtr) { CallFrame *varFramePtr = Tcl_Interp_varFramePtr(interp); assert(interp); assert(cscPtr); assert(framePtr); /*fprintf(stderr,"PUSH CMETHOD_FRAME (Nsf_PushFrameCsc) frame %p cscPtr %p methodName %s\n", framePtr, cscPtr, Tcl_GetCommandName(interp, cscPtr->cmdPtr));*/ Tcl_PushCallFrame(interp, (Tcl_CallFrame *)framePtr, Tcl_CallFrame_nsPtr(varFramePtr), FRAME_IS_PROC|FRAME_IS_NSF_CMETHOD); Tcl_CallFrame_clientData(framePtr) = (ClientData)cscPtr; Tcl_CallFrame_procPtr(framePtr) = &RUNTIME_STATE(interp)->fakeProc; } NSF_INLINE static void Nsf_PopFrameCsc(Tcl_Interp *interp, CallFrame *UNUSED(framePtr)) { assert(interp); /*fprintf(stderr,"POP CMETHOD_FRAME (Nsf_PopFrameCsc) frame %p, varTablePtr = %p\n", framePtr, Tcl_CallFrame_varTablePtr(framePtr));*/ Tcl_PopCallFrame(interp); } /* * stack query operations */ /* *---------------------------------------------------------------------- * CallStackGetActiveProcFrame -- * * Return the Tcl call frame of the last scripted method. * * Results: * Tcl_CallFrame * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_CallFrame * CallStackGetActiveProcFrame(Tcl_CallFrame *framePtr) nonnull(1); static Tcl_CallFrame * CallStackGetActiveProcFrame(Tcl_CallFrame *framePtr) { assert(framePtr); for (; framePtr; framePtr = Tcl_CallFrame_callerPtr(framePtr)) { register int flag = Tcl_CallFrame_isProcCallFrame(framePtr); if (flag & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { /* never return an inactive method frame */ if (likely(!(((NsfCallStackContent *)Tcl_CallFrame_clientData(framePtr))->frameType & NSF_CSC_TYPE_INACTIVE))) break; } else { if (unlikely(flag & (FRAME_IS_NSF_OBJECT))) continue; if (flag == 0 || (flag & FRAME_IS_PROC)) break; } } return framePtr; } /* *---------------------------------------------------------------------- * CallStackNextFrameOfType -- * * Return the next frame with a specified type from the call stack. * The type is specified by a bit mask passed as flags. * * Results: * Tcl_CallFrame * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_CallFrame * CallStackNextFrameOfType(Tcl_CallFrame *framePtr, unsigned int flags) nonnull(1); static Tcl_CallFrame * CallStackNextFrameOfType(Tcl_CallFrame *framePtr, unsigned int flags) { assert(framePtr); for (; framePtr; framePtr = Tcl_CallFrame_callerPtr(framePtr)) { if (Tcl_CallFrame_isProcCallFrame(framePtr) & flags) return framePtr; } return framePtr; } /* *---------------------------------------------------------------------- * GetSelfObj -- * * Return the currently active object from a method or object frame. * * Results: * NsfObject * or NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ #define SKIP_LEVELS 1 #define SKIP_LAMBDA 1 #if defined(SKIP_LAMBDA) # if !defined(SKIP_LEVELS) # define SKIP_LEVELS 1 # endif #endif NSF_INLINE static NsfObject* GetSelfObj(Tcl_Interp *interp) nonnull(1); NSF_INLINE static NsfObject* GetSelfObj(Tcl_Interp *interp) { register Tcl_CallFrame *varFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); assert(interp); /*fprintf(stderr, "GetSelfObj interp has frame %p and var-frame %p\n", Tcl_Interp_framePtr(interp), Tcl_Interp_varFramePtr(interp));*/ for (; varFramePtr; varFramePtr = #if defined(SKIP_LEVELS) Tcl_CallFrame_callerPtr(varFramePtr) #else NULL #endif ) { register int flags = Tcl_CallFrame_isProcCallFrame(varFramePtr); if (likely(flags & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD))) { NsfCallStackContent *cscPtr = (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr); return cscPtr->self; } else if (flags & FRAME_IS_NSF_OBJECT) { return (NsfObject *)Tcl_CallFrame_clientData(varFramePtr); } #if defined(SKIP_LAMBDA) if (flags & FRAME_IS_LAMBDA) { continue; } break; #endif } return NULL; } /* *---------------------------------------------------------------------- * CallStackGetTclFrame -- * * Return the Tcl_Callframe a (scripted or nonleaf) method starting with * the specified or topmost frame; if skip is a positive number the * specified number of Tcl frames are skipped. * * Results: * Tcl_CallFrame or NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_CallFrame* CallStackGetTclFrame(Tcl_Interp *interp, Tcl_CallFrame *startFramePtr, int skip) nonnull(1); static Tcl_CallFrame* CallStackGetTclFrame(Tcl_Interp *interp, Tcl_CallFrame *varFramePtr, int skip) { assert(interp); assert(skip >= 0); /* NsfShowStack(interp); */ if (varFramePtr == NULL) { varFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); } while(skip-- && varFramePtr != NULL) { varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr); } for (; varFramePtr; varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr)) { if (Tcl_CallFrame_isProcCallFrame(varFramePtr) & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { return varFramePtr; } } return NULL; } /* *---------------------------------------------------------------------- * CallStackGetTopFrame, CallStackGetTopFrame0, NsfCallStackGetTopFrame -- * * Return the NsfCallStackContent* of the topmost invocation of a (scripted * or nonleaf) method. If framePtrPtr is provided, it is used to return the * Tcl frame as well. * * Results: * Call stack content or NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ NSF_INLINE static NsfCallStackContent *CallStackGetTopFrame0(Tcl_Interp *interp) nonnull(1); static NsfCallStackContent* CallStackGetTopFrame(Tcl_Interp *interp, Tcl_CallFrame **framePtrPtr) { register Tcl_CallFrame *varFramePtr; assert(interp); for (varFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); varFramePtr; varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr)) { if (Tcl_CallFrame_isProcCallFrame(varFramePtr) & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { if (framePtrPtr) *framePtrPtr = varFramePtr; return (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr); } } if (framePtrPtr) *framePtrPtr = NULL; return NULL; } NSF_INLINE static NsfCallStackContent* CallStackGetTopFrame0(Tcl_Interp *interp) nonnull(1); NSF_INLINE static NsfCallStackContent* CallStackGetTopFrame0(Tcl_Interp *interp) { register Tcl_CallFrame *varFramePtr; assert(interp); for (varFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); varFramePtr; varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr)) { if (likely(Tcl_CallFrame_isProcCallFrame(varFramePtr) & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD))) { return (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr); } } return NULL; } #if defined(NSF_PROFILE) NsfCallStackContent* NsfCallStackGetTopFrame(Tcl_Interp *interp, Tcl_CallFrame **framePtrPtr) nonnull(1); NsfCallStackContent* NsfCallStackGetTopFrame(Tcl_Interp *interp, Tcl_CallFrame **framePtrPtr) { return CallStackGetTopFrame(interp, framePtrPtr); } #endif /* *---------------------------------------------------------------------- * NsfCallStackFindLastInvocation -- * * Find last invocation of a (scripted or nonleaf) method with a * specified offset. * * Results: * Call stack content or NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfCallStackContent *NsfCallStackFindLastInvocation(Tcl_Interp *interp, int offset, Tcl_CallFrame **framePtrPtr) nonnull(1); static NsfCallStackContent * NsfCallStackFindLastInvocation(Tcl_Interp *interp, int offset, Tcl_CallFrame **framePtrPtr) { register Tcl_CallFrame *varFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); int lvl = Tcl_CallFrame_level(varFramePtr); assert(interp); for (; likely(varFramePtr != NULL); varFramePtr = Tcl_CallFrame_callerVarPtr(varFramePtr)) { if (Tcl_CallFrame_isProcCallFrame(varFramePtr) & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { NsfCallStackContent *cscPtr = (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr); /* * A NSF method frame. */ if ((cscPtr->flags & (NSF_CSC_CALL_IS_NEXT|NSF_CSC_CALL_IS_ENSEMBLE)) || (cscPtr->frameType & NSF_CSC_TYPE_INACTIVE)) { continue; } if (offset) { offset--; } else if (Tcl_CallFrame_level(varFramePtr) < lvl) { if (framePtrPtr) *framePtrPtr = varFramePtr; return cscPtr; } } else if (Tcl_CallFrame_isProcCallFrame(varFramePtr)) { /* * A Tcl proc frame. */ if (offset) { offset--; } else if (Tcl_CallFrame_level(varFramePtr) < lvl) { if (framePtrPtr) *framePtrPtr = varFramePtr; return NULL; } } } if (framePtrPtr) *framePtrPtr = NULL; return NULL; } /* *---------------------------------------------------------------------- * NsfCallStackFindActiveFrame -- * * Search for the first active frame on the call-stack. * * Results: * Call stack content or NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfCallStackContent *NsfCallStackFindActiveFrame(Tcl_Interp *interp, int offset, Tcl_CallFrame **framePtrPtr) nonnull(1); static NsfCallStackContent * NsfCallStackFindActiveFrame(Tcl_Interp *interp, int offset, Tcl_CallFrame **framePtrPtr) { register Tcl_CallFrame *varFramePtr; assert(interp); /* skip #offset frames */ for (varFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); offset>0 && varFramePtr; varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr), offset--); /* search for first active frame and set tcl frame pointers */ for (; varFramePtr; varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr)) { if (Tcl_CallFrame_isProcCallFrame(varFramePtr) & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { NsfCallStackContent *cscPtr = (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr); if (!(cscPtr->frameType & NSF_CSC_TYPE_INACTIVE)) { /* we found the highest active frame */ if (framePtrPtr) *framePtrPtr = varFramePtr; return cscPtr; } } } /* we could not find an active frame; called from toplevel? */ if (framePtrPtr) *framePtrPtr = NULL; return NULL; } /* *---------------------------------------------------------------------- * CallStackUseActiveFrame -- * * Activate the varFrame of the first active non-object frame and * save the previously active frames in the call frame context. * These stored frames are typically reactivated by * CallStackRestoreSavedFrames(). * * Results: * None. * * Side effects: * The varFramePtr of the interp is potentially updated * *---------------------------------------------------------------------- */ static void CallStackUseActiveFrame(Tcl_Interp *interp, callFrameContext *ctx) nonnull(1) nonnull(2); static void CallStackUseActiveFrame(Tcl_Interp *interp, callFrameContext *ctx) { Tcl_CallFrame *framePtr, *inFramePtr; assert(interp); assert(ctx); inFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); /* Get the first active non object frame */ framePtr = CallStackGetActiveProcFrame(inFramePtr); /*fprintf(stderr,"... use frameptr %p \n", framePtr);*/ if (inFramePtr == framePtr) { /* call frame pointers are fine */ ctx->frameSaved = 0; } else { ctx->varFramePtr = inFramePtr; /*fprintf(stderr, "CallStackUseActiveFrame stores %p\n", framePtr);*/ Tcl_Interp_varFramePtr(interp) = (CallFrame *)framePtr; ctx->frameSaved = 1; } } /* *---------------------------------------------------------------------- * CallStackRestoreSavedFrames -- * * Restore the previously saved frames from the specified call * frame context. These frames are typically saved by * CallStackUseActiveFrame(). * * Results: * None. * * Side effects: * The varFramePtr of the interp is potentially updated * *---------------------------------------------------------------------- */ static void CallStackRestoreSavedFrames(Tcl_Interp *interp, callFrameContext *ctx) nonnull(1) nonnull(2); static void CallStackRestoreSavedFrames(Tcl_Interp *interp, callFrameContext *ctx) { assert(interp); assert(ctx); if (ctx->frameSaved) { /*fprintf(stderr, "CallStackRestoreSavedFrames drops %p restores %p\n", Tcl_Interp_varFramePtr(interp), ctx->varFramePtr);*/ Tcl_Interp_varFramePtr(interp) = (CallFrame *)ctx->varFramePtr; } } /* *---------------------------------------------------------------------- * CallStackFindActiveFilter -- * * Return the call-stack content of the currently active filter * * Results: * Call-stack content or NULL, if no filter is active * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfCallStackContent * CallStackFindActiveFilter(Tcl_Interp *interp) nonnull(1); static NsfCallStackContent * CallStackFindActiveFilter(Tcl_Interp *interp) { register Tcl_CallFrame *varFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); assert(interp); for (; varFramePtr; varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr)) { if (Tcl_CallFrame_isProcCallFrame(varFramePtr) & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { NsfCallStackContent *cscPtr = (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr); if (cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_FILTER) { return cscPtr; } } } /* for some reasons, we could not find invocation (topLevel, destroy) */ return NULL; } /* *---------------------------------------------------------------------- * CallStackFindEnsembleCsc -- * * Return the call-stack content and the optionally the stack frame * of the last ensemble invocation. * * Results: * call-stack content * * Side effects: * None. * *---------------------------------------------------------------------- */ static NsfCallStackContent * CallStackFindEnsembleCsc(Tcl_CallFrame *framePtr, Tcl_CallFrame **framePtrPtr) nonnull(1) nonnull(2); static NsfCallStackContent * CallStackFindEnsembleCsc(Tcl_CallFrame *framePtr, Tcl_CallFrame **framePtrPtr) { register Tcl_CallFrame *varFramePtr; NsfCallStackContent *cscPtr = NULL; assert(framePtr); assert(framePtrPtr); for (/* Skipping the starting frame, assuming a "leaf" frame in an ensemble dispatch */ varFramePtr = Tcl_CallFrame_callerPtr(framePtr); Tcl_CallFrame_isProcCallFrame(varFramePtr) & FRAME_IS_NSF_CMETHOD; varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr)) { cscPtr = (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr); assert(cscPtr); /*fprintf(stderr," --- frame %p cmdPtr %p NSF_CSC_TYPE_ENSEMBLE %d NSF_CSC_CALL_IS_ENSEMBLE %d \ NSF_CSC_TYPE_INACTIVE %d\n", varFramePtr, cscPtr->cmdPtr, (cscPtr->frameType & NSF_CSC_TYPE_ENSEMBLE) != 0, (cscPtr->flags & NSF_CSC_CALL_IS_ENSEMBLE) != 0, (cscPtr->frameType & NSF_CSC_TYPE_INACTIVE) != 0);*/ /* * The "root" frame in a call-stack branch resulting from an ensemble * dispatch is not typed as an NSF_CSC_TYPE_ENSEMBLE frame, the call type * /is/ NSF_CSC_CALL_IS_ENSEMBLE. */ if ((cscPtr->frameType & NSF_CSC_TYPE_ENSEMBLE) == 0 && (cscPtr->flags & NSF_CSC_CALL_IS_ENSEMBLE)) break; } *framePtrPtr = varFramePtr; return cscPtr; } /* *---------------------------------------------------------------------- * CallStackMethodPath -- * * Return the method path of the current ensemble in a Tcl_Obj with * refCount 0. * * Results: * Tcl_Obj containing the method path * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_Obj* CallStackMethodPath(Tcl_Interp *interp, Tcl_CallFrame *framePtr) nonnull(1) nonnull(2); static Tcl_Obj* CallStackMethodPath(Tcl_Interp *interp, Tcl_CallFrame *framePtr) { int elements; Tcl_Obj *resultObj; Tcl_Obj *methodPathObj = Tcl_NewListObj(0, NULL); assert(interp); assert(framePtr); /* * Append all ensemble names to the specified list obj */ for (elements = 0; Tcl_CallFrame_isProcCallFrame(framePtr) & (FRAME_IS_NSF_CMETHOD|FRAME_IS_NSF_METHOD); framePtr = Tcl_CallFrame_callerPtr(framePtr)) { NsfCallStackContent *cscPtr = (NsfCallStackContent *)Tcl_CallFrame_clientData(framePtr); assert(cscPtr); /*fprintf(stderr, "--- frame %p cmdPtr %p cmd %s NSF_CSC_TYPE_ENSEMBLE %d \ NSF_CSC_CALL_IS_ENSEMBLE %d NSF_CSC_TYPE_INACTIVE %d\n", framePtr, cscPtr->cmdPtr, Tcl_GetCommandName(interp, cscPtr->cmdPtr), (cscPtr->frameType & NSF_CSC_TYPE_ENSEMBLE) != 0, (cscPtr->flags & NSF_CSC_CALL_IS_ENSEMBLE) != 0, (cscPtr->frameType & NSF_CSC_TYPE_INACTIVE) != 0);*/ /* * The "ensemble" call type, we find applied to all intermediate and leaf * ensemble frames. By filtering according to the ensemble call type, we * effectively omit leaf ensemble and non-ensemble frames from being * reported. */ if ((cscPtr->flags & NSF_CSC_CALL_IS_ENSEMBLE) == 0) break; /* * The callstack might contain consecutive calls of ensemble entry calls * chained via next. We can detect consecutive calls via the elements * count. */ if (elements == 0 && (cscPtr->flags & NSF_CM_ENSEMBLE_UNKNOWN) && (cscPtr->flags & NSF_CSC_CALL_IS_NEXT)) break; Tcl_ListObjAppendElement(interp, methodPathObj, Tcl_NewStringObj(Tcl_GetCommandName(interp, cscPtr->cmdPtr), -1)); elements++; /* * The "root" frame in a call-stack branch resulting from an ensemble * dispatch is not typed as an NSF_CSC_TYPE_ENSEMBLE frame, the call type * /is/ NSF_CSC_CALL_IS_ENSEMBLE (as checked above). */ if ((cscPtr->frameType & NSF_CSC_TYPE_ENSEMBLE) == 0) break; } /* * The resulting list has reversed order. If there are multiple * arguments, reverse the list to obtain the right order. */ if (elements > 1) { int oc, i; Tcl_Obj **ov; INCR_REF_COUNT(methodPathObj); Tcl_ListObjGetElements(interp, methodPathObj, &oc, &ov); resultObj = Tcl_NewListObj(0, NULL); for (i = elements-1; i >= 0; i--) { Tcl_ListObjAppendElement(interp, resultObj, ov[i]); } DECR_REF_COUNT(methodPathObj); } else { resultObj = methodPathObj; } /*fprintf(stderr, "--- CallStackMethodPath returns %s\n", ObjStr(resultObj));*/ return resultObj; } /* *---------------------------------------------------------------------- * FilterActiveOnObj -- * * Check, if there is an active filter on "obj" using the specified * cmd. * * Results: * 0 or 1 * * Side effects: * None. * *---------------------------------------------------------------------- */ NSF_INLINE static int FilterActiveOnObj(Tcl_Interp *interp, NsfObject *object, Tcl_Command cmd) nonnull(1) nonnull(2); NSF_INLINE static int FilterActiveOnObj(Tcl_Interp *interp, NsfObject *object, Tcl_Command cmd) { register Tcl_CallFrame *varFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp); assert(interp); assert(object); for (; varFramePtr; varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr)) { if (Tcl_CallFrame_isProcCallFrame(varFramePtr) & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { NsfCallStackContent *cscPtr = (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr); if (cmd == cscPtr->cmdPtr && object == cscPtr->self && cscPtr->frameType == NSF_CSC_TYPE_ACTIVE_FILTER) { return 1; } } } return 0; } /* *---------------------------------------------------------------------- * CallStackReplaceVarTableReferences -- * * Replace all references to the old var table (arg 1) by * references to a new var table (arg 2) on the call-stack. * This function is e.g. used by require namespace. * * Results: * None. * * Side effects: * Updated stack. * *---------------------------------------------------------------------- */ static void CallStackReplaceVarTableReferences(Tcl_Interp *interp, TclVarHashTable *oldVarTablePtr, TclVarHashTable *newVarTablePtr) nonnull(1) nonnull(2) nonnull(3); static void CallStackReplaceVarTableReferences(Tcl_Interp *interp, TclVarHashTable *oldVarTablePtr, TclVarHashTable *newVarTablePtr) { Tcl_CallFrame *framePtr; assert(interp); assert(oldVarTablePtr); assert(newVarTablePtr); for (framePtr = (Tcl_CallFrame *)Tcl_Interp_framePtr(interp); framePtr; framePtr = Tcl_CallFrame_callerPtr(framePtr)) { int frameFlags = Tcl_CallFrame_isProcCallFrame(framePtr); if (!(frameFlags & FRAME_IS_NSF_OBJECT)) continue; if (!(Tcl_CallFrame_varTablePtr(framePtr) == oldVarTablePtr)) continue; /*fprintf(stderr, "+++ makeObjNamespace replacing varTable %p with %p in frame %p\n", oldVarTablePtr, newVarTablePtr, framePtr);*/ Tcl_CallFrame_varTablePtr(framePtr) = newVarTablePtr; } } /* *---------------------------------------------------------------------- * CallStackPopAll -- * * Unwind the stack and pop all call-stack entries that are still * alive (e.g. if "exit" is called and we were jumping out of the * call-frame). * * Results: * None. * * Side effects: * Updated stack. * *---------------------------------------------------------------------- */ static void CallStackPopAll(Tcl_Interp *interp) { assert(interp); if (RUNTIME_STATE(interp)->debugLevel > 2) { NsfShowStack(interp); } while (1) { Tcl_CallFrame *framePtr = Tcl_Interp_framePtr(interp); int frameFlags; if (!framePtr) break; if (Tcl_CallFrame_level(framePtr) == 0) break; frameFlags = Tcl_CallFrame_isProcCallFrame(framePtr); /*fprintf(stderr, "--- popping %p frame-flags %.6x\n", framePtr, frameFlags);*/ if (frameFlags & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { /* free the call stack content; we need this just for decr activation count */ NsfCallStackContent *cscPtr = ((NsfCallStackContent *)Tcl_CallFrame_clientData(framePtr)); #if defined(NRE) /* Mask out IS_NRE, since Tcl_PopCallFrame takes care about TclStackFree */ cscPtr->flags &= ~NSF_CSC_CALL_IS_NRE; #endif CscFinish(interp, cscPtr, TCL_OK, "popall"); } else if (frameFlags & FRAME_IS_NSF_OBJECT) { Tcl_CallFrame_varTablePtr(framePtr) = NULL; } /* pop the Tcl frame */ Tcl_PopCallFrame(interp); } #if defined(CHECK_ACTIVATION_COUNTS) { int count = 0; NsfClasses *unstackedEntries = RUNTIME_STATE(interp)->cscList, *nextCscPtr = unstackedEntries; while (nextCscPtr) { NsfCallStackContent *cscPtr = (NsfCallStackContent *)nextCscPtr->cl; CscListRemove(interp, cscPtr, &unstackedEntries); CscFinish(interp, cscPtr, TCL_OK, "unwind"); count ++; nextCscPtr = unstackedEntries ? unstackedEntries->nextPtr : NULL; } if (count>0 && RUNTIME_STATE(interp)->debugLevel > 0) { fprintf(stderr, "+++ unwind removed %d unstacked csc entries\n", count); } } #endif } /* *---------------------------------------------------------------------- * CscAlloc -- * * Allocate the csc structure either from the stack or via * StackAlloc (the latter is recorded in the callType). The Alloc * operation requires a CscFinish operation later. * * Results: * A valid, semi-initialized cscPtr. * * Side effects: * Memory allocation * *---------------------------------------------------------------------- */ #if defined(NRE) static NsfCallStackContent * CscAlloc(Tcl_Interp *interp, NsfCallStackContent *cscPtr, Tcl_Command cmd) nonnull(1); #else static NsfCallStackContent * CscAlloc(Tcl_Interp *interp, NsfCallStackContent *cscPtr, Tcl_Command cmd) nonnull(2); #endif static NsfCallStackContent * CscAlloc(Tcl_Interp *interp, NsfCallStackContent *cscPtr, Tcl_Command cmd) { #if defined(NRE) Tcl_ObjCmdProc *proc = cmd ? Tcl_Command_objProc(cmd) : NULL; if (proc == TclObjInterpProc) { cscPtr = (NsfCallStackContent *) NsfTclStackAlloc(interp, sizeof(NsfCallStackContent), "csc"); cscPtr->flags = NSF_CSC_CALL_IS_NRE; } else { cscPtr->flags = 0; } #else assert(cscPtr); (void)interp; (void)cmd; cscPtr->flags = 0; #endif /*fprintf(stderr, "CscAlloc allocated %p\n", cscPtr);*/ return cscPtr; } /* *---------------------------------------------------------------------- * CscInit -- * * Initialize call stack content and track activation counts * of involved objects and classes * * Results: * None. * * Side effects: * Initialized Csc, updated activation counts * *---------------------------------------------------------------------- */ NSF_INLINE static void CscInit_(/*@notnull@*/ NsfCallStackContent *cscPtr, NsfObject *object, NsfClass *cl, Tcl_Command cmd, int frameType, unsigned int flags) nonnull(1) nonnull(2); NSF_INLINE static void CscInit_(/*@notnull@*/ NsfCallStackContent *cscPtr, NsfObject *object, NsfClass *cl, Tcl_Command cmd, int frameType, unsigned int flags) { #if defined(NSF_PROFILE) struct timeval trt; #endif assert(cscPtr); assert(object); #if defined(NSF_PROFILE) gettimeofday(&trt, NULL); cscPtr->startUsec = trt.tv_usec; cscPtr->startSec = trt.tv_sec; #endif /* * When cmd is provided, the call is not unknown, the method * will be executed and the object will be stacked. In these * cases, we maintain an activation count. */ if (likely(cmd != NULL)) { /* * Track object activations */ object->activationCount ++; MEM_COUNT_ALLOC("object.activationCount",object); /*fprintf(stderr, "CscInit %p method %s activationCount ++ (%s) --> %d (cl %p)\n", cscPtr, cmd ? Tcl_GetCommandName(object->teardown, cmd) : "UNK", ObjectName(object), object->activationCount, cl);*/ /* * Track class activations */ if (cl) { /* * handle class activation count */ cl->object.activationCount ++; MEM_COUNT_ALLOC("class.activationCount", cl); /* * Increment the namespace ptr in case Tcl tries to delete * this namespace during the invocation */ NSNamespacePreserve(Tcl_Command_nsPtr(cmd)); /*fprintf(stderr, "NSNamespacePreserve %p\n", nsPtr);*/ } NsfCommandPreserve(cmd); } cscPtr->flags |= flags & NSF_CSC_COPY_FLAGS; cscPtr->self = object; cscPtr->cl = cl; cscPtr->cmdPtr = cmd; cscPtr->objv = NULL; cscPtr->filterStackEntry = object->filterStack; cscPtr->frameType = frameType; /*fprintf(stderr, "CscInit %p (%s) object %p %s flags %.6x cmdPtr %p\n", cscPtr, msg, object, ObjectName(object), cscPtr->flags, cscPtr->cmdPtr);*/ } /* *---------------------------------------------------------------------- * CscFinish -- * * Counterpart of CscInit(). Decrement activation counts * and delete objects/classes if necessary. * * Results: * None. * * Side effects: * potentially deletes objects, classes or namespaces. * *---------------------------------------------------------------------- */ NSF_INLINE static void CscFinish_(Tcl_Interp *interp, NsfCallStackContent *cscPtr) nonnull(1) nonnull(2); NSF_INLINE static void CscFinish_(Tcl_Interp *interp, NsfCallStackContent *cscPtr) { NsfObject *object; assert(interp); assert(cscPtr); assert(cscPtr->self); #if defined(NSF_PROFILE) if (RUNTIME_STATE(interp)->doProfile) { NsfProfileRecordMethodData(interp, cscPtr); } #endif object = cscPtr->self; /*fprintf(stderr, "CscFinish %p object %p %s flags %.6x cmdPtr %p\n", cscPtr, object, ObjectName(object), cscPtr->flags, cscPtr->cmdPtr); */ /* * In the cases, where an cmd was provided, we tracked in init the * activations. Release these activations now. Notem, that * cscPtr->cmdPtr might have been epoched, but it is still * available, since we used NsfCommandPreserve() in CscInit(). */ if (likely(cscPtr->cmdPtr != NULL)) { int allowDestroy = RUNTIME_STATE(interp)->exitHandlerDestroyRound == NSF_EXITHANDLER_OFF; /* * Track object activations */ object->activationCount --; MEM_COUNT_FREE("object.activationCount", object); /*fprintf(stderr, "CscFinish decr activationCount for %s to %d object->flags %.6x dc %.6x succ %.6x\n", ObjectName(cscPtr->self), cscPtr->self->activationCount, object->flags, object->flags & NSF_DESTROY_CALLED, object->flags & NSF_DESTROY_CALLED_SUCCESS );*/ assert(object->activationCount > -1); if (object->activationCount < 1 && (object->flags & NSF_DESTROY_CALLED) && allowDestroy) { /*fprintf(stderr, "CscFinish calls destroy object %p\n", object);*/ CallStackDoDestroy(interp, object); } /* * Track class activations */ if (unlikely(cscPtr->cl != NULL)) { NsfObject *clObject = &cscPtr->cl->object; clObject->activationCount --; MEM_COUNT_FREE("class.activationCount", clObject); /*fprintf(stderr, "CscFinish class %p %s check ac %d flags destroy %.6x success %.6x\n", clObject, ObjectName(clObject), clObject->activationCount, clObject->flags & NSF_DESTROY_CALLED, clObject->flags & NSF_DESTROY_CALLED_SUCCESS);*/ if (clObject->activationCount < 1 && clObject->flags & NSF_DESTROY_CALLED && allowDestroy) { /* fprintf(stderr, "CscFinish calls destroy class %p\n", clObject);*/ CallStackDoDestroy(interp, clObject); } /* * Release the Namespace */ NSNamespaceRelease(Tcl_Command_nsPtr(cscPtr->cmdPtr)); } /* * Release the Command */ NsfCommandRelease(cscPtr->cmdPtr); } #if defined(NRE) if ((cscPtr->flags & NSF_CSC_CALL_IS_NRE)) { NsfTclStackFree(interp, cscPtr, "CscFinish"); } #endif /*fprintf(stderr, "CscFinish done\n");*/ } /* *---------------------------------------------------------------------- * BeginOfCallChain -- * * Experimental function to track the begin of a call chain. * Currently not used. * * Results: * Call-frame ptr * * Side effects: * None. * *---------------------------------------------------------------------- */ #if 0 static Tcl_CallFrame * BeginOfCallChain(Tcl_Interp *interp, NsfObject *object) nonnull(1); static Tcl_CallFrame * BeginOfCallChain(Tcl_Interp *interp, NsfObject *object) { Tcl_CallFrame *varFramePtr = (Tcl_CallFrame *)Tcl_Interp_varFramePtr(interp), *prevFramePtr = varFramePtr; assert(interp); if (object) { fprintf(stderr, "BeginOfCallChain obj %s\n", ObjectName(object)); for (; varFramePtr; varFramePtr = Tcl_CallFrame_callerPtr(varFramePtr)) { register unsigned int flags = Tcl_CallFrame_isProcCallFrame(varFramePtr); if (flags & (FRAME_IS_NSF_METHOD|FRAME_IS_NSF_CMETHOD)) { NsfCallStackContent *cscPtr = (NsfCallStackContent *)Tcl_CallFrame_clientData(varFramePtr); if (cscPtr->self == object) { prevFramePtr = varFramePtr; continue; } } else if (flags & (FRAME_IS_NSF_OBJECT|FRAME_IS_LAMBDA)) { continue; } break; } } fprintf(stderr, "BeginOfCallChain returns %p\n", prevFramePtr); return prevFramePtr; } #endif /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsfStubLib.c000066400000000000000000000107551242365656200147340ustar00rootroot00000000000000/* * nsfStubLib.c -- * * Stub object that will be statically linked into extensions of the Next * Scripting Framework. * * Copyright (C) 1998 Paul Duffin * Copyright (C) 2001-2014 Gustaf Neumann (a) * Copyright (C) 2001-2007 Uwe Zdun (a) * * (a) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * The original work by Paul Duffin was licensed as: * * "See the file "tcl-license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES." * * See also http://www.tcl.tk/software/tcltk/license.html. * */ /* * We need to ensure that we use the stub macros so that this file contains * no references to any of the stub functions. This will make it possible * to build an extension that references Tcl_InitStubs but doesn't end up * including the rest of the stub functions. */ #ifndef USE_TCL_STUBS # define USE_TCL_STUBS #endif #undef USE_TCL_STUB_PROCS /* * This ensures that the Nsf_InitStubs has a prototype in * nsf.h and is not the macro that turns it into Tcl_PkgRequire */ #ifndef USE_NSF_STUBS # define USE_NSF_STUBS #endif #include "nsfInt.h" #if defined(PRE86) extern NsfStubs *nsfStubsPtr; #else MODULE_SCOPE const NsfStubs *nsfStubsPtr; MODULE_SCOPE const NsfIntStubs *nsfIntStubsPtr; #endif CONST86 NsfStubs *nsfStubsPtr = NULL; CONST86 NsfIntStubs *nsfIntStubsPtr = NULL; /* *---------------------------------------------------------------------- * * Nsf_InitStubs -- * * Tries to initialise the stub table pointers and ensures that * the correct version of nsf is loaded. * * Results: * The actual version of nsf that satisfies the request, or * NULL to indicate that an error occurred. * * Side effects: * Sets the stub table pointers. * *---------------------------------------------------------------------- */ CONST char * Nsf_InitStubs(Tcl_Interp *interp, CONST char *version, int exact) { CONST char *actualVersion; const char *packageName = "nsf"; ClientData clientData = NULL; actualVersion = Tcl_PkgRequireEx(interp, "nsf", version, exact, &clientData); /*fprintf(stderr, "Nsf_InitStubs required nsf version %s exact %d -> %s %p\n", version, exact, actualVersion, clientData);*/ if (clientData == NULL) { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "Error loading package ", packageName, ": package not present or incomplete", NULL); return NULL; } else { CONST86 NsfStubs * const stubsPtr = clientData; CONST86 NsfIntStubs * const intStubsPtr = stubsPtr->hooks ? stubsPtr->hooks->nsfIntStubs : NULL; if (actualVersion == NULL) { return NULL; } if (!intStubsPtr) { static char *errMsg = "missing stubInt table pointer"; Tcl_ResetResult(interp); Tcl_AppendResult(interp, "Error loading package", packageName, ": (requested version '", version, "', loaded version '", actualVersion, "'): ", errMsg, NULL); return NULL; } nsfStubsPtr = stubsPtr; nsfIntStubsPtr = intStubsPtr; return actualVersion; } } /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ generic/nsfUtil.c000066400000000000000000000143501242365656200143000ustar00rootroot00000000000000/* * nsfUtil.c -- * * Utility functions of the Next Scripting Framework. * * Copyright (C) 2001-2014 Gustaf Neumann * Copyright (C) 2001-2007 Uwe Zdun * * Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include "nsfInt.h" /* *---------------------------------------------------------------------- * strnstr -- * * Implementation of strnstr() for platforms not providing it via their C * library. The function strnstr locates the first occurance of a substring * in a null-terminated string * * Results: * Strbstring or NULL * * Side effects: * None. * *---------------------------------------------------------------------- */ #ifndef HAVE_STRNSTR char *strnstr(const char *buffer, const char *needle, size_t buffer_len) { char *p; size_t remainder, needle_len; if (*needle == '\0') { return (char *)buffer; } needle_len = strlen(needle); for (p = (char *)buffer, remainder = buffer_len; p != NULL; p = memchr(p + 1, *needle, remainder-1)) { remainder = buffer_len - (p - buffer); if (remainder < needle_len) break; if (strncmp(p, needle, needle_len) == 0) { return p; } } return NULL; } #endif /* *---------------------------------------------------------------------- * Nsf_ltoa -- * * Convert a long value into a string; this function is a fast * version of sprintf(buf, "%ld", l); * * Results: * String containing decimal value of the provided parameter. * * Side effects: * None. * *---------------------------------------------------------------------- */ char * Nsf_ltoa(char *buf, long i, int *len) { int nr_written, negative; char tmp[LONG_AS_STRING], *pointer = &tmp[1], *string, *p; *tmp = 0; assert(buf); if (i<0) { i = -i; negative = nr_written = 1; } else nr_written = negative = 0; do { nr_written++; *pointer++ = i%10 + '0'; i/=10; } while (i); p = string = buf; if (negative) *p++ = '-'; while ((*p++ = *--pointer)); /* copy number (reversed) from tmp to buf */ if (len) *len = nr_written; return string; } /* *---------------------------------------------------------------------- * NsfStringIncr -- * * Increment a value on a number system with the provided alphabet. The * intention of the function is to generate compact new symbols. * * Results: * New symbol in form of a string. * * Side effects: * None. * *---------------------------------------------------------------------- */ static char *alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; static int blockIncrement = 8; /* static char *alphabet = "ab"; static int blockIncrement = 2; */ static unsigned char chartable[255] = {0}; char * NsfStringIncr(NsfStringIncrStruct *iss) { char newch, *currentChar; assert(iss); currentChar = iss->buffer + iss->bufSize - 2; newch = *(alphabet + chartable[(unsigned)*currentChar]); while (1) { if (newch) { /* no overflow */ *currentChar = newch; break; } else { /* overflow */ *currentChar = *alphabet; /* use first char from alphabet */ currentChar--; assert(currentChar >= iss->buffer); newch = *(alphabet + chartable[(unsigned)*currentChar]); if (currentChar < iss->start) { iss->length++; if (currentChar == iss->buffer) { size_t newBufSize = iss->bufSize + blockIncrement; char *newBuffer = ckalloc(newBufSize); currentChar = newBuffer+blockIncrement; /*memset(newBuffer, 0, blockIncrement);*/ memcpy(currentChar, iss->buffer, iss->bufSize); *currentChar = newch; iss->start = currentChar; ckfree(iss->buffer); iss->buffer = newBuffer; iss->bufSize = newBufSize; } else { iss->start = currentChar; } } } } assert(iss->buffer[iss->bufSize-1] == 0); assert(iss->buffer[iss->bufSize-2] != 0); assert(iss->length < iss->bufSize); assert(iss->start + iss->length + 1 == iss->buffer + iss->bufSize); return iss->start; } /* *---------------------------------------------------------------------- * NsfStringIncrInit, NsfStringIncrFree -- * * Support function for NsfStringIncr(). NsfStringIncrInit() function is * called before NsfStringIncr() can be used on this buffer, * NsfStringIncrFree() terminates usage. * * Results: * void * * Side effects: * Initializes the . * *---------------------------------------------------------------------- */ void NsfStringIncrInit(NsfStringIncrStruct *iss) { char *p; int i = 0; const size_t bufSize = blockIncrement>2 ? blockIncrement : 2; assert(iss); for (p=alphabet; *p; p++) { chartable[(int)*p] = ++i; } iss->buffer = ckalloc(bufSize); memset(iss->buffer, 0, bufSize); iss->start = iss->buffer + bufSize-2; iss->bufSize = bufSize; iss->length = 1; /* for (i=1; i<50; i++) { NsfStringIncr(iss); fprintf(stderr, "string '%s' (%d)\n", iss->start, iss->length); } */ } void NsfStringIncrFree(NsfStringIncrStruct *iss) { assert(iss); ckfree(iss->buffer); } generic/nxdocIndex.tcl000066400000000000000000000026121242365656200153150ustar00rootroot00000000000000# NXDoc index file, version 1.0 # This file was generated by the "::nx::doc::mkIndex" command # and is optionally sourced by nxdoc to filter the command population # to be documented. Typically each line is a command that # sets an element in the ::nxdoc::include array, where the # element name is the name of a command and the value indicates whether # the command is to be documented (1) or not (0). # # To regenerate this index file, run nxsh in the root dir of the # source distribution and have the following two liner evaluated: # # package req nx::doc # nx::doc::mkIndex -documentAll -indexfiles nsfAPI.nxdocindex -outdir generic/ generic/nsf.tcl # # Source external (e.g., auto-generated) index files source [file join [file dirname [info script]] nsfAPI.nxdocindex] set ::nxdoc::include(::nsf::method::provide) 1 set ::nxdoc::include(::nsf::method::require) 1 set ::nxdoc::include(::nsf::strip_proc_name) 0 set ::nxdoc::include(::nsf::mixin) 1 set ::nxdoc::include(::nsf::object::unknown) 0 set ::nxdoc::include(::nsf::object::unknown::add) 0 set ::nxdoc::include(::nsf::object::unknown::get) 0 set ::nxdoc::include(::nsf::object::unknown::delete) 0 set ::nxdoc::include(::nsf::object::unknown::keys) 0 set ::nxdoc::include(::nsf::exithandler) 1 set ::nxdoc::include(::nsf::__exithandler) 0 set ::nxdoc::include(::nsf::log) 1 set ::nxdoc::include(::nsf::deprecated) 1 set ::nxdoc::include(::nsf::tmpdir) 1generic/predefined.h000066400000000000000000000102751242365656200147700ustar00rootroot00000000000000/* Generated by mk_predefined.tcl */ static char cmd[] = "# -*- Tcl -*-\n" "namespace eval ::nsf {\n" "namespace export \\\n" "next current self configure finalize interp is my relation dispatch\n" "namespace eval ::nsf::method::create {namespace export alias}\n" "proc ::nsf::method::provide {require_name definition {script \"\"}} {\n" "set ::nsf::methodIndex($require_name) [list definition $definition script $script]}\n" "proc ::nsf::method::require {object name {per_object 0}} {\n" "set key ::nsf::methodIndex($name)\n" "if {[info exists $key]} {\n" "array set \"\" [set $key]\n" "if {$(script) ne \"\"} {\n" "eval $(script)}\n" "if {$per_object} {\n" "set cmd [linsert $(definition) 1 -per-object]\n" "return [eval [linsert $cmd 1 $object]]} else {\n" "return [eval [linsert $(definition) 1 $object]]}} else {\n" "error \"cannot require method $name for $object, method unknown\"}}\n" "::proc strip_proc_name {name} {\n" "if {[string match ::nsf::procs::* $name]} {\n" "return [string range $name 12 end]} elseif {[string match nsf::procs::* $name]} {\n" "return [string range $name 12 end]} else {\n" "return $name}}\n" "::nsf::proc ::nsf::mixin {object -per-object:switch classes} {\n" "set rel [expr {${per-object} ? \"object-mixin\" : \"class-mixin\"}]\n" "if {[lindex $classes 0] ne \"\"} {\n" "set oldSetting [::nsf::relation::get $object $rel]\n" "uplevel [list ::nsf::relation::set $object $rel [linsert $oldSetting 0 $classes]]} else {\n" "uplevel [list ::nsf::relation::set $object $rel \"\"]}}\n" "::nsf::method::provide autoname {::nsf::method::alias autoname ::nsf::methods::object::autoname}\n" "::nsf::method::provide exists {::nsf::method::alias exists ::nsf::methods::object::exists}\n" "::nsf::method::provide volatile {::nsf::method::alias volatile ::nsf::methods::object::volatile}\n" "proc ::nsf::object::unknown {name} {\n" "foreach {key handler} [array get ::nsf::object::unknown] {\n" "set result [uplevel [list {*}$handler $name]]\n" "if {$result ne \"\"} {\n" "return $result}}\n" "return \"\"}\n" "namespace eval ::nsf::object::unknown {\n" "proc add {key handler} {set ::nsf::object::unknown($key) $handler}\n" "proc get {key} {return $::nsf::object::unknown($key)}\n" "proc delete {key} {array unset ::nsf::object::unknown($key)}\n" "proc keys {} {array names ::nsf::object::unknown}}\n" "namespace eval ::nsf::argument {}\n" "proc ::nsf::argument::unknown {args} {\n" "return \"\"}\n" "proc ::nsf::exithandler {args} {\n" "lassign $args op value\n" "switch $op {\n" "set {::proc ::nsf::__exithandler {} $value}\n" "get {::info body ::nsf::__exithandler}\n" "unset {proc ::nsf::__exithandler args {;}}\n" "default {error \"syntax: ::nsf::exithandler $::nsf::parameter::syntax(::nsf::exithandler)\"}}}\n" "::nsf::exithandler unset\n" "if {[info command ::ns_log] ne \"\"} {\n" "proc ::nsf::log {level msg} {\n" "if {[info command ::ns_log] ne \"\"} {\n" "::ns_log $level \"nsf: $msg\"} else {\n" "puts stderr \"$level: $msg\"}}} else {\n" "proc ::nsf::log {level msg} {\n" "puts stderr \"$level: $msg\"}}\n" "proc ::nsf::deprecated {what oldCmd newCmd} {\n" "set msg \"**\\n** The $what $oldCmd is deprecated.\"\n" "if {$newCmd ne \"\"} {append msg \" use $newCmd instead.\"}\n" "append msg \"\\n**\\n\"\n" "nsf::log Warning $msg}\n" "proc tmpdir {} {\n" "foreach e [list TMPDIR TEMP TMP] {\n" "if {[info exists ::env($e)] \\\n" "&& [file isdirectory $::env($e)] \\\n" "&& [file writable $::env($e)]} {\n" "return $::env($e)}}\n" "if {$::tcl_platform(platform) eq \"windows\"} {\n" "foreach d [list \"C:\\\\TEMP\" \"C:\\\\TMP\" \"\\\\TEMP\" \"\\\\TMP\"] {\n" "if {[file isdirectory $d] && [file writable $d]} {\n" "return $d}}}\n" "return /tmp}\n" "namespace export tmpdir\n" "if {![info exists ::env(HOME)]} {set ::env(HOME) /root}\n" "namespace eval ::nsf::parameter {}\n" "proc ::nsf::parameter::filter {defs pattern} {\n" "set result {}\n" "foreach def $defs {\n" "if {[string match $pattern [::nsf::parameter::info name $def]]} {\n" "lappend result $def}}\n" "return $result}\n" "set ::nsf::parameter::syntax(::nsf::xotclnext) \"?--noArgs? ?/arg .../?\"\n" "set ::nsf::parameter::syntax(::nsf::__unset_unknown_args) \"\"\n" "set ::nsf::parameter::syntax(::nsf::exithandler) \"?get?|?set /cmds/?|?unset?\"}\n" ""; generic/stubs8.5/000077500000000000000000000000001242365656200141005ustar00rootroot00000000000000generic/stubs8.5/nsfDecls.h000066400000000000000000000457601242365656200160260ustar00rootroot00000000000000/* * nsfDecls.h -- * * Declarations of functions in the platform independent public Nsf API. * * This file is part of the Next Scripting Framework. * * Copyright (C) 1999-2014 Gustaf Neumann (a, b) * Copyright (C) 1999-2007 Uwe Zdun (a, b) * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #ifndef _NSFDECLS #define _NSFDECLS /* * WARNING: This file is automatically generated by the tools/genStubs.tcl * script. Any modifications to the function declarations below should be made * in the nsf.decls script. */ /* !BEGIN!: Do not edit below this line. */ #ifdef __cplusplus extern "C" { #endif /* * Exported function declarations: */ #ifndef Nsf_Init_TCL_DECLARED #define Nsf_Init_TCL_DECLARED /* 0 */ EXTERN int Nsf_Init(Tcl_Interp *interp); #endif /* Slot 1 is reserved */ #ifndef NsfIsClass_TCL_DECLARED #define NsfIsClass_TCL_DECLARED /* 2 */ EXTERN struct Nsf_Class * NsfIsClass(Tcl_Interp *interp, ClientData cd); #endif #ifndef NsfGetObject_TCL_DECLARED #define NsfGetObject_TCL_DECLARED /* 3 */ EXTERN struct Nsf_Object * NsfGetObject(Tcl_Interp *interp, CONST char *name); #endif #ifndef NsfGetClass_TCL_DECLARED #define NsfGetClass_TCL_DECLARED /* 4 */ EXTERN struct Nsf_Class * NsfGetClass(Tcl_Interp *interp, CONST char *name); #endif #ifndef NsfDeleteObject_TCL_DECLARED #define NsfDeleteObject_TCL_DECLARED /* 5 */ EXTERN int NsfDeleteObject(Tcl_Interp *interp, struct Nsf_Object *object); #endif #ifndef NsfRemoveObjectMethod_TCL_DECLARED #define NsfRemoveObjectMethod_TCL_DECLARED /* 6 */ EXTERN int NsfRemoveObjectMethod(Tcl_Interp *interp, struct Nsf_Object *object, CONST char *nm); #endif #ifndef NsfRemoveClassMethod_TCL_DECLARED #define NsfRemoveClassMethod_TCL_DECLARED /* 7 */ EXTERN int NsfRemoveClassMethod(Tcl_Interp *interp, struct Nsf_Class *cl, CONST char *nm); #endif #ifndef Nsf_ObjSetVar2_TCL_DECLARED #define Nsf_ObjSetVar2_TCL_DECLARED /* 8 */ EXTERN Tcl_Obj * Nsf_ObjSetVar2(struct Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, Tcl_Obj *value, unsigned int flags); #endif #ifndef Nsf_ObjGetVar2_TCL_DECLARED #define Nsf_ObjGetVar2_TCL_DECLARED /* 9 */ EXTERN Tcl_Obj * Nsf_ObjGetVar2(struct Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, unsigned int flags); #endif #ifndef Nsf_UnsetVar2_TCL_DECLARED #define Nsf_UnsetVar2_TCL_DECLARED /* 10 */ EXTERN int Nsf_UnsetVar2(struct Nsf_Object *object, Tcl_Interp *interp, CONST char *name1, CONST char *name2, unsigned int flags); #endif #ifndef NsfDStringPrintf_TCL_DECLARED #define NsfDStringPrintf_TCL_DECLARED /* 11 */ EXTERN void NsfDStringPrintf(Tcl_DString *dsPtr, CONST char *fmt, va_list apSrc); #endif #ifndef NsfPrintError_TCL_DECLARED #define NsfPrintError_TCL_DECLARED /* 12 */ EXTERN int NsfPrintError(Tcl_Interp *interp, CONST char *fmt, ...); #endif #ifndef NsfErrInProc_TCL_DECLARED #define NsfErrInProc_TCL_DECLARED /* 13 */ EXTERN int NsfErrInProc(Tcl_Interp *interp, Tcl_Obj *objName, Tcl_Obj *clName, CONST char *procName); #endif #ifndef NsfObjErrType_TCL_DECLARED #define NsfObjErrType_TCL_DECLARED /* 14 */ EXTERN int NsfObjErrType(Tcl_Interp *interp, CONST char *context, Tcl_Obj *value, CONST char *type, Nsf_Param CONST *pPtr); #endif #ifndef NsfStackDump_TCL_DECLARED #define NsfStackDump_TCL_DECLARED /* 15 */ EXTERN void NsfStackDump(Tcl_Interp *interp); #endif #ifndef NsfSetObjClientData_TCL_DECLARED #define NsfSetObjClientData_TCL_DECLARED /* 16 */ EXTERN void NsfSetObjClientData(Tcl_Interp *interp, Nsf_Object *object, ClientData data); #endif #ifndef NsfGetObjClientData_TCL_DECLARED #define NsfGetObjClientData_TCL_DECLARED /* 17 */ EXTERN ClientData NsfGetObjClientData(Tcl_Interp *interp, Nsf_Object *object); #endif #ifndef NsfSetClassClientData_TCL_DECLARED #define NsfSetClassClientData_TCL_DECLARED /* 18 */ EXTERN void NsfSetClassClientData(Tcl_Interp *interp, Nsf_Class *cl, ClientData data); #endif #ifndef NsfGetClassClientData_TCL_DECLARED #define NsfGetClassClientData_TCL_DECLARED /* 19 */ EXTERN ClientData NsfGetClassClientData(Tcl_Interp *interp, Nsf_Class *cl); #endif #ifndef NsfRequireObjNamespace_TCL_DECLARED #define NsfRequireObjNamespace_TCL_DECLARED /* 20 */ EXTERN void NsfRequireObjNamespace(Tcl_Interp *interp, Nsf_Object *object); #endif #ifndef NsfCallMethodWithArgs_TCL_DECLARED #define NsfCallMethodWithArgs_TCL_DECLARED /* 21 */ EXTERN int NsfCallMethodWithArgs(Tcl_Interp *interp, Nsf_Object *object, Tcl_Obj *method, Tcl_Obj *arg, int objc, Tcl_Obj *CONST objv[], unsigned int flags); #endif #ifndef NsfAddObjectMethod_TCL_DECLARED #define NsfAddObjectMethod_TCL_DECLARED /* 22 */ EXTERN int NsfAddObjectMethod(Tcl_Interp *interp, struct Nsf_Object *object, CONST char *nm, Tcl_ObjCmdProc *proc, ClientData cd, Tcl_CmdDeleteProc *dp, unsigned int flags); #endif #ifndef NsfAddClassMethod_TCL_DECLARED #define NsfAddClassMethod_TCL_DECLARED /* 23 */ EXTERN int NsfAddClassMethod(Tcl_Interp *interp, struct Nsf_Class *cl, CONST char *nm, Tcl_ObjCmdProc *proc, ClientData cd, Tcl_CmdDeleteProc *dp, unsigned int flags); #endif #ifndef NsfCreate_TCL_DECLARED #define NsfCreate_TCL_DECLARED /* 24 */ EXTERN int NsfCreate(Tcl_Interp *in, Nsf_Class *class, Tcl_Obj *name, int objc, Tcl_Obj *CONST objv[]); #endif #ifndef Nsf_ArgumentParse_TCL_DECLARED #define Nsf_ArgumentParse_TCL_DECLARED /* 25 */ EXTERN int Nsf_ArgumentParse(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Nsf_Object *object, Tcl_Obj *procNameObj, Nsf_Param CONST *paramPtr, int nrParams, int serial, unsigned int processFlags, Nsf_ParseContext *pcPtr); #endif #ifndef NsfLog_TCL_DECLARED #define NsfLog_TCL_DECLARED /* 26 */ EXTERN void NsfLog(Tcl_Interp *interp, int requiredLevel, CONST char *fmt, ...); #endif #ifndef Nsf_PointerAdd_TCL_DECLARED #define Nsf_PointerAdd_TCL_DECLARED /* 27 */ EXTERN int Nsf_PointerAdd(Tcl_Interp *interp, char *buffer, CONST char *typeName, VOID *valuePtr); #endif #ifndef Nsf_PointerDelete_TCL_DECLARED #define Nsf_PointerDelete_TCL_DECLARED /* 28 */ EXTERN int Nsf_PointerDelete(CONST char *key, VOID *valuePtr, int free); #endif #ifndef Nsf_PointerTypeRegister_TCL_DECLARED #define Nsf_PointerTypeRegister_TCL_DECLARED /* 29 */ EXTERN int Nsf_PointerTypeRegister(Tcl_Interp *interp, CONST char*typeName, int *counterPtr); #endif #ifndef Nsf_ConvertToBoolean_TCL_DECLARED #define Nsf_ConvertToBoolean_TCL_DECLARED /* 30 */ EXTERN int Nsf_ConvertToBoolean(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); #endif #ifndef Nsf_ConvertToClass_TCL_DECLARED #define Nsf_ConvertToClass_TCL_DECLARED /* 31 */ EXTERN int Nsf_ConvertToClass(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); #endif #ifndef Nsf_ConvertToInt32_TCL_DECLARED #define Nsf_ConvertToInt32_TCL_DECLARED /* 32 */ EXTERN int Nsf_ConvertToInt32(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); #endif #ifndef Nsf_ConvertToInteger_TCL_DECLARED #define Nsf_ConvertToInteger_TCL_DECLARED /* 33 */ EXTERN int Nsf_ConvertToInteger(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); #endif #ifndef Nsf_ConvertToObject_TCL_DECLARED #define Nsf_ConvertToObject_TCL_DECLARED /* 34 */ EXTERN int Nsf_ConvertToObject(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); #endif #ifndef Nsf_ConvertToPointer_TCL_DECLARED #define Nsf_ConvertToPointer_TCL_DECLARED /* 35 */ EXTERN int Nsf_ConvertToPointer(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); #endif #ifndef Nsf_ConvertToString_TCL_DECLARED #define Nsf_ConvertToString_TCL_DECLARED /* 36 */ EXTERN int Nsf_ConvertToString(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); #endif #ifndef Nsf_ConvertToTclobj_TCL_DECLARED #define Nsf_ConvertToTclobj_TCL_DECLARED /* 37 */ EXTERN int Nsf_ConvertToTclobj(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); #endif #ifndef Nsf_EnumerationTypeRegister_TCL_DECLARED #define Nsf_EnumerationTypeRegister_TCL_DECLARED /* 38 */ EXTERN int Nsf_EnumerationTypeRegister(Tcl_Interp *interp, Nsf_EnumeratorConverterEntry *typeRecords); #endif #ifndef Nsf_CmdDefinitionRegister_TCL_DECLARED #define Nsf_CmdDefinitionRegister_TCL_DECLARED /* 39 */ EXTERN int Nsf_CmdDefinitionRegister(Tcl_Interp *interp, Nsf_methodDefinition *definitionRecords); #endif typedef struct NsfStubHooks { struct NsfIntStubs *nsfIntStubs; } NsfStubHooks; typedef struct NsfStubs { int magic; struct NsfStubHooks *hooks; int (*nsf_Init) (Tcl_Interp *interp); /* 0 */ VOID *reserved1; struct Nsf_Class * (*nsfIsClass) (Tcl_Interp *interp, ClientData cd); /* 2 */ struct Nsf_Object * (*nsfGetObject) (Tcl_Interp *interp, CONST char *name); /* 3 */ struct Nsf_Class * (*nsfGetClass) (Tcl_Interp *interp, CONST char *name); /* 4 */ int (*nsfDeleteObject) (Tcl_Interp *interp, struct Nsf_Object *object); /* 5 */ int (*nsfRemoveObjectMethod) (Tcl_Interp *interp, struct Nsf_Object *object, CONST char *nm); /* 6 */ int (*nsfRemoveClassMethod) (Tcl_Interp *interp, struct Nsf_Class *cl, CONST char *nm); /* 7 */ Tcl_Obj * (*nsf_ObjSetVar2) (struct Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, Tcl_Obj *value, unsigned int flags); /* 8 */ Tcl_Obj * (*nsf_ObjGetVar2) (struct Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, unsigned int flags); /* 9 */ int (*nsf_UnsetVar2) (struct Nsf_Object *object, Tcl_Interp *interp, CONST char *name1, CONST char *name2, unsigned int flags); /* 10 */ void (*nsfDStringPrintf) (Tcl_DString *dsPtr, CONST char *fmt, va_list apSrc); /* 11 */ int (*nsfPrintError) (Tcl_Interp *interp, CONST char *fmt, ...); /* 12 */ int (*nsfErrInProc) (Tcl_Interp *interp, Tcl_Obj *objName, Tcl_Obj *clName, CONST char *procName); /* 13 */ int (*nsfObjErrType) (Tcl_Interp *interp, CONST char *context, Tcl_Obj *value, CONST char *type, Nsf_Param CONST *pPtr); /* 14 */ void (*nsfStackDump) (Tcl_Interp *interp); /* 15 */ void (*nsfSetObjClientData) (Tcl_Interp *interp, Nsf_Object *object, ClientData data); /* 16 */ ClientData (*nsfGetObjClientData) (Tcl_Interp *interp, Nsf_Object *object); /* 17 */ void (*nsfSetClassClientData) (Tcl_Interp *interp, Nsf_Class *cl, ClientData data); /* 18 */ ClientData (*nsfGetClassClientData) (Tcl_Interp *interp, Nsf_Class *cl); /* 19 */ void (*nsfRequireObjNamespace) (Tcl_Interp *interp, Nsf_Object *object); /* 20 */ int (*nsfCallMethodWithArgs) (Tcl_Interp *interp, Nsf_Object *object, Tcl_Obj *method, Tcl_Obj *arg, int objc, Tcl_Obj *CONST objv[], unsigned int flags); /* 21 */ int (*nsfAddObjectMethod) (Tcl_Interp *interp, struct Nsf_Object *object, CONST char *nm, Tcl_ObjCmdProc *proc, ClientData cd, Tcl_CmdDeleteProc *dp, unsigned int flags); /* 22 */ int (*nsfAddClassMethod) (Tcl_Interp *interp, struct Nsf_Class *cl, CONST char *nm, Tcl_ObjCmdProc *proc, ClientData cd, Tcl_CmdDeleteProc *dp, unsigned int flags); /* 23 */ int (*nsfCreate) (Tcl_Interp *in, Nsf_Class *class, Tcl_Obj *name, int objc, Tcl_Obj *CONST objv[]); /* 24 */ int (*nsf_ArgumentParse) (Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Nsf_Object *object, Tcl_Obj *procNameObj, Nsf_Param CONST *paramPtr, int nrParams, int serial, unsigned int processFlags, Nsf_ParseContext *pcPtr); /* 25 */ void (*nsfLog) (Tcl_Interp *interp, int requiredLevel, CONST char *fmt, ...); /* 26 */ int (*nsf_PointerAdd) (Tcl_Interp *interp, char *buffer, CONST char *typeName, VOID *valuePtr); /* 27 */ int (*nsf_PointerDelete) (CONST char *key, VOID *valuePtr, int free); /* 28 */ int (*nsf_PointerTypeRegister) (Tcl_Interp *interp, CONST char*typeName, int *counterPtr); /* 29 */ int (*nsf_ConvertToBoolean) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 30 */ int (*nsf_ConvertToClass) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 31 */ int (*nsf_ConvertToInt32) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 32 */ int (*nsf_ConvertToInteger) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 33 */ int (*nsf_ConvertToObject) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 34 */ int (*nsf_ConvertToPointer) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 35 */ int (*nsf_ConvertToString) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 36 */ int (*nsf_ConvertToTclobj) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 37 */ int (*nsf_EnumerationTypeRegister) (Tcl_Interp *interp, Nsf_EnumeratorConverterEntry *typeRecords); /* 38 */ int (*nsf_CmdDefinitionRegister) (Tcl_Interp *interp, Nsf_methodDefinition *definitionRecords); /* 39 */ } NsfStubs; extern NsfStubs *nsfStubsPtr; #ifdef __cplusplus } #endif #if defined(USE_NSF_STUBS) && !defined(USE_NSF_STUB_PROCS) /* * Inline function declarations: */ #ifndef Nsf_Init #define Nsf_Init \ (nsfStubsPtr->nsf_Init) /* 0 */ #endif /* Slot 1 is reserved */ #ifndef NsfIsClass #define NsfIsClass \ (nsfStubsPtr->nsfIsClass) /* 2 */ #endif #ifndef NsfGetObject #define NsfGetObject \ (nsfStubsPtr->nsfGetObject) /* 3 */ #endif #ifndef NsfGetClass #define NsfGetClass \ (nsfStubsPtr->nsfGetClass) /* 4 */ #endif #ifndef NsfDeleteObject #define NsfDeleteObject \ (nsfStubsPtr->nsfDeleteObject) /* 5 */ #endif #ifndef NsfRemoveObjectMethod #define NsfRemoveObjectMethod \ (nsfStubsPtr->nsfRemoveObjectMethod) /* 6 */ #endif #ifndef NsfRemoveClassMethod #define NsfRemoveClassMethod \ (nsfStubsPtr->nsfRemoveClassMethod) /* 7 */ #endif #ifndef Nsf_ObjSetVar2 #define Nsf_ObjSetVar2 \ (nsfStubsPtr->nsf_ObjSetVar2) /* 8 */ #endif #ifndef Nsf_ObjGetVar2 #define Nsf_ObjGetVar2 \ (nsfStubsPtr->nsf_ObjGetVar2) /* 9 */ #endif #ifndef Nsf_UnsetVar2 #define Nsf_UnsetVar2 \ (nsfStubsPtr->nsf_UnsetVar2) /* 10 */ #endif #ifndef NsfDStringPrintf #define NsfDStringPrintf \ (nsfStubsPtr->nsfDStringPrintf) /* 11 */ #endif #ifndef NsfPrintError #define NsfPrintError \ (nsfStubsPtr->nsfPrintError) /* 12 */ #endif #ifndef NsfErrInProc #define NsfErrInProc \ (nsfStubsPtr->nsfErrInProc) /* 13 */ #endif #ifndef NsfObjErrType #define NsfObjErrType \ (nsfStubsPtr->nsfObjErrType) /* 14 */ #endif #ifndef NsfStackDump #define NsfStackDump \ (nsfStubsPtr->nsfStackDump) /* 15 */ #endif #ifndef NsfSetObjClientData #define NsfSetObjClientData \ (nsfStubsPtr->nsfSetObjClientData) /* 16 */ #endif #ifndef NsfGetObjClientData #define NsfGetObjClientData \ (nsfStubsPtr->nsfGetObjClientData) /* 17 */ #endif #ifndef NsfSetClassClientData #define NsfSetClassClientData \ (nsfStubsPtr->nsfSetClassClientData) /* 18 */ #endif #ifndef NsfGetClassClientData #define NsfGetClassClientData \ (nsfStubsPtr->nsfGetClassClientData) /* 19 */ #endif #ifndef NsfRequireObjNamespace #define NsfRequireObjNamespace \ (nsfStubsPtr->nsfRequireObjNamespace) /* 20 */ #endif #ifndef NsfCallMethodWithArgs #define NsfCallMethodWithArgs \ (nsfStubsPtr->nsfCallMethodWithArgs) /* 21 */ #endif #ifndef NsfAddObjectMethod #define NsfAddObjectMethod \ (nsfStubsPtr->nsfAddObjectMethod) /* 22 */ #endif #ifndef NsfAddClassMethod #define NsfAddClassMethod \ (nsfStubsPtr->nsfAddClassMethod) /* 23 */ #endif #ifndef NsfCreate #define NsfCreate \ (nsfStubsPtr->nsfCreate) /* 24 */ #endif #ifndef Nsf_ArgumentParse #define Nsf_ArgumentParse \ (nsfStubsPtr->nsf_ArgumentParse) /* 25 */ #endif #ifndef NsfLog #define NsfLog \ (nsfStubsPtr->nsfLog) /* 26 */ #endif #ifndef Nsf_PointerAdd #define Nsf_PointerAdd \ (nsfStubsPtr->nsf_PointerAdd) /* 27 */ #endif #ifndef Nsf_PointerDelete #define Nsf_PointerDelete \ (nsfStubsPtr->nsf_PointerDelete) /* 28 */ #endif #ifndef Nsf_PointerTypeRegister #define Nsf_PointerTypeRegister \ (nsfStubsPtr->nsf_PointerTypeRegister) /* 29 */ #endif #ifndef Nsf_ConvertToBoolean #define Nsf_ConvertToBoolean \ (nsfStubsPtr->nsf_ConvertToBoolean) /* 30 */ #endif #ifndef Nsf_ConvertToClass #define Nsf_ConvertToClass \ (nsfStubsPtr->nsf_ConvertToClass) /* 31 */ #endif #ifndef Nsf_ConvertToInt32 #define Nsf_ConvertToInt32 \ (nsfStubsPtr->nsf_ConvertToInt32) /* 32 */ #endif #ifndef Nsf_ConvertToInteger #define Nsf_ConvertToInteger \ (nsfStubsPtr->nsf_ConvertToInteger) /* 33 */ #endif #ifndef Nsf_ConvertToObject #define Nsf_ConvertToObject \ (nsfStubsPtr->nsf_ConvertToObject) /* 34 */ #endif #ifndef Nsf_ConvertToPointer #define Nsf_ConvertToPointer \ (nsfStubsPtr->nsf_ConvertToPointer) /* 35 */ #endif #ifndef Nsf_ConvertToString #define Nsf_ConvertToString \ (nsfStubsPtr->nsf_ConvertToString) /* 36 */ #endif #ifndef Nsf_ConvertToTclobj #define Nsf_ConvertToTclobj \ (nsfStubsPtr->nsf_ConvertToTclobj) /* 37 */ #endif #ifndef Nsf_EnumerationTypeRegister #define Nsf_EnumerationTypeRegister \ (nsfStubsPtr->nsf_EnumerationTypeRegister) /* 38 */ #endif #ifndef Nsf_CmdDefinitionRegister #define Nsf_CmdDefinitionRegister \ (nsfStubsPtr->nsf_CmdDefinitionRegister) /* 39 */ #endif #endif /* defined(USE_NSF_STUBS) && !defined(USE_NSF_STUB_PROCS) */ /* !END!: Do not edit above this line. */ #endif /* _NSFDECLS */ generic/stubs8.5/nsfIntDecls.h000066400000000000000000000050201242365656200164620ustar00rootroot00000000000000/* * nsfIntDecls.h -- * * This file contains the declarations for all unsupported * functions that are exported by the Tcl library. These * interfaces are not guaranteed to remain the same between * versions. Use at your own risk. * * Copyright (C) 1998-2008 Uwe Zdun * Copyright (C) 1998-2014 Gustaf Neumann * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #ifndef _NSFINTDECLS #define _NSFINTDECLS /* * WARNING: This file is automatically generated by the tools/genStubs.tcl * script. Any modifications to the function declarations below should be made * in the nsfInt.decls script. */ /* !BEGIN!: Do not edit below this line. */ #ifdef __cplusplus extern "C" { #endif /* * Exported function declarations: */ typedef struct NsfIntStubs { int magic; struct NsfIntStubHooks *hooks; } NsfIntStubs; extern NsfIntStubs *nsfIntStubsPtr; #ifdef __cplusplus } #endif #if defined(USE_NSF_STUBS) && !defined(USE_NSF_STUB_PROCS) /* * Inline function declarations: */ #endif /* defined(USE_NSF_STUBS) && !defined(USE_NSF_STUB_PROCS) */ /* !END!: Do not edit above this line. */ #endif /* _NSFINTDECLS */ generic/stubs8.5/nsfStubInit.c000066400000000000000000000071361242365656200165230ustar00rootroot00000000000000/* * nsfStubInit.c -- * * This file contains the initializers for the stub vectors of the Next * Scripting Framework. * * Copyright (C) 1999-2014 Gustaf Neumann (a, b) * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include "nsfInt.h" /* * Remove macros that will interfere with the definitions below. */ /* * WARNING: The contents of this file is automatically generated by the * tools/genStubs.tcl script. Any modifications to the function declarations * below should be made in the generic/tcl.decls script. */ #if defined(PRE86) EXTERN NsfStubs nsfStubs; # else MODULE_SCOPE const NsfStubs nsfStubs; #endif /* !BEGIN!: Do not edit below this line. */ NsfIntStubs nsfIntStubs = { TCL_STUB_MAGIC, NULL, }; static NsfStubHooks nsfStubHooks = { &nsfIntStubs }; NsfStubs nsfStubs = { TCL_STUB_MAGIC, &nsfStubHooks, Nsf_Init, /* 0 */ NULL, /* 1 */ NsfIsClass, /* 2 */ NsfGetObject, /* 3 */ NsfGetClass, /* 4 */ NsfDeleteObject, /* 5 */ NsfRemoveObjectMethod, /* 6 */ NsfRemoveClassMethod, /* 7 */ Nsf_ObjSetVar2, /* 8 */ Nsf_ObjGetVar2, /* 9 */ Nsf_UnsetVar2, /* 10 */ NsfDStringPrintf, /* 11 */ NsfPrintError, /* 12 */ NsfErrInProc, /* 13 */ NsfObjErrType, /* 14 */ NsfStackDump, /* 15 */ NsfSetObjClientData, /* 16 */ NsfGetObjClientData, /* 17 */ NsfSetClassClientData, /* 18 */ NsfGetClassClientData, /* 19 */ NsfRequireObjNamespace, /* 20 */ NsfCallMethodWithArgs, /* 21 */ NsfAddObjectMethod, /* 22 */ NsfAddClassMethod, /* 23 */ NsfCreate, /* 24 */ Nsf_ArgumentParse, /* 25 */ NsfLog, /* 26 */ Nsf_PointerAdd, /* 27 */ Nsf_PointerDelete, /* 28 */ Nsf_PointerTypeRegister, /* 29 */ Nsf_ConvertToBoolean, /* 30 */ Nsf_ConvertToClass, /* 31 */ Nsf_ConvertToInt32, /* 32 */ Nsf_ConvertToInteger, /* 33 */ Nsf_ConvertToObject, /* 34 */ Nsf_ConvertToPointer, /* 35 */ Nsf_ConvertToString, /* 36 */ Nsf_ConvertToTclobj, /* 37 */ Nsf_EnumerationTypeRegister, /* 38 */ Nsf_CmdDefinitionRegister, /* 39 */ }; /* !END!: Do not edit above this line. */ /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * End: */ generic/stubs8.6/000077500000000000000000000000001242365656200141015ustar00rootroot00000000000000generic/stubs8.6/nsfDecls.h000066400000000000000000000347171242365656200160270ustar00rootroot00000000000000/* * nsfDecls.h -- * * Declarations of functions in the platform independent public Nsf API. * * This file is part of the Next Scripting Framework. * * Copyright (C) 1999-2014 Gustaf Neumann (a, b) * Copyright (C) 1999-2007 Uwe Zdun (a, b) * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #ifndef _NSFDECLS #define _NSFDECLS /* * WARNING: This file is automatically generated by the tools/genStubs.tcl * script. Any modifications to the function declarations below should be made * in the nsf.decls script. */ /* !BEGIN!: Do not edit below this line. */ #ifdef __cplusplus extern "C" { #endif /* * Exported function declarations: */ /* 0 */ EXTERN int Nsf_Init(Tcl_Interp *interp); /* Slot 1 is reserved */ /* 2 */ EXTERN struct Nsf_Class * NsfIsClass(Tcl_Interp *interp, ClientData cd); /* 3 */ EXTERN struct Nsf_Object * NsfGetObject(Tcl_Interp *interp, CONST char *name); /* 4 */ EXTERN struct Nsf_Class * NsfGetClass(Tcl_Interp *interp, CONST char *name); /* 5 */ EXTERN int NsfDeleteObject(Tcl_Interp *interp, struct Nsf_Object *object); /* 6 */ EXTERN int NsfRemoveObjectMethod(Tcl_Interp *interp, struct Nsf_Object *object, CONST char *nm); /* 7 */ EXTERN int NsfRemoveClassMethod(Tcl_Interp *interp, struct Nsf_Class *cl, CONST char *nm); /* 8 */ EXTERN Tcl_Obj * Nsf_ObjSetVar2(struct Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, Tcl_Obj *value, unsigned int flags); /* 9 */ EXTERN Tcl_Obj * Nsf_ObjGetVar2(struct Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, unsigned int flags); /* 10 */ EXTERN int Nsf_UnsetVar2(struct Nsf_Object *object, Tcl_Interp *interp, CONST char *name1, CONST char *name2, unsigned int flags); /* 11 */ EXTERN void NsfDStringPrintf(Tcl_DString *dsPtr, CONST char *fmt, va_list apSrc); /* 12 */ EXTERN int NsfPrintError(Tcl_Interp *interp, CONST char *fmt, ...); /* 13 */ EXTERN int NsfErrInProc(Tcl_Interp *interp, Tcl_Obj *objName, Tcl_Obj *clName, CONST char *procName); /* 14 */ EXTERN int NsfObjErrType(Tcl_Interp *interp, CONST char *context, Tcl_Obj *value, CONST char *type, Nsf_Param CONST *pPtr); /* 15 */ EXTERN void NsfStackDump(Tcl_Interp *interp); /* 16 */ EXTERN void NsfSetObjClientData(Tcl_Interp *interp, Nsf_Object *object, ClientData data); /* 17 */ EXTERN ClientData NsfGetObjClientData(Tcl_Interp *interp, Nsf_Object *object); /* 18 */ EXTERN void NsfSetClassClientData(Tcl_Interp *interp, Nsf_Class *cl, ClientData data); /* 19 */ EXTERN ClientData NsfGetClassClientData(Tcl_Interp *interp, Nsf_Class *cl); /* 20 */ EXTERN void NsfRequireObjNamespace(Tcl_Interp *interp, Nsf_Object *object); /* 21 */ EXTERN int NsfCallMethodWithArgs(Tcl_Interp *interp, Nsf_Object *object, Tcl_Obj *method, Tcl_Obj *arg, int objc, Tcl_Obj *CONST objv[], unsigned int flags); /* 22 */ EXTERN int NsfAddObjectMethod(Tcl_Interp *interp, struct Nsf_Object *object, CONST char *nm, Tcl_ObjCmdProc *proc, ClientData cd, Tcl_CmdDeleteProc *dp, unsigned int flags); /* 23 */ EXTERN int NsfAddClassMethod(Tcl_Interp *interp, struct Nsf_Class *cl, CONST char *nm, Tcl_ObjCmdProc *proc, ClientData cd, Tcl_CmdDeleteProc *dp, unsigned int flags); /* 24 */ EXTERN int NsfCreate(Tcl_Interp *in, Nsf_Class *class, Tcl_Obj *name, int objc, Tcl_Obj *CONST objv[]); /* 25 */ EXTERN int Nsf_ArgumentParse(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Nsf_Object *object, Tcl_Obj *procNameObj, Nsf_Param CONST *paramPtr, int nrParams, int serial, unsigned int processFlags, Nsf_ParseContext *pcPtr); /* 26 */ EXTERN void NsfLog(Tcl_Interp *interp, int requiredLevel, CONST char *fmt, ...); /* 27 */ EXTERN int Nsf_PointerAdd(Tcl_Interp *interp, char *buffer, CONST char *typeName, void *valuePtr); /* 28 */ EXTERN int Nsf_PointerDelete(CONST char *key, void *valuePtr, int free); /* 29 */ EXTERN int Nsf_PointerTypeRegister(Tcl_Interp *interp, CONST char*typeName, int *counterPtr); /* 30 */ EXTERN int Nsf_ConvertToBoolean(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 31 */ EXTERN int Nsf_ConvertToClass(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 32 */ EXTERN int Nsf_ConvertToInt32(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 33 */ EXTERN int Nsf_ConvertToInteger(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 34 */ EXTERN int Nsf_ConvertToObject(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 35 */ EXTERN int Nsf_ConvertToPointer(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 36 */ EXTERN int Nsf_ConvertToString(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 37 */ EXTERN int Nsf_ConvertToTclobj(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 38 */ EXTERN int Nsf_EnumerationTypeRegister(Tcl_Interp *interp, Nsf_EnumeratorConverterEntry *typeRecords); /* 39 */ EXTERN int Nsf_CmdDefinitionRegister(Tcl_Interp *interp, Nsf_methodDefinition *definitionRecords); typedef struct { const struct NsfIntStubs *nsfIntStubs; } NsfStubHooks; typedef struct NsfStubs { int magic; const NsfStubHooks *hooks; int (*nsf_Init) (Tcl_Interp *interp); /* 0 */ void (*reserved1)(void); struct Nsf_Class * (*nsfIsClass) (Tcl_Interp *interp, ClientData cd); /* 2 */ struct Nsf_Object * (*nsfGetObject) (Tcl_Interp *interp, CONST char *name); /* 3 */ struct Nsf_Class * (*nsfGetClass) (Tcl_Interp *interp, CONST char *name); /* 4 */ int (*nsfDeleteObject) (Tcl_Interp *interp, struct Nsf_Object *object); /* 5 */ int (*nsfRemoveObjectMethod) (Tcl_Interp *interp, struct Nsf_Object *object, CONST char *nm); /* 6 */ int (*nsfRemoveClassMethod) (Tcl_Interp *interp, struct Nsf_Class *cl, CONST char *nm); /* 7 */ Tcl_Obj * (*nsf_ObjSetVar2) (struct Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, Tcl_Obj *value, unsigned int flags); /* 8 */ Tcl_Obj * (*nsf_ObjGetVar2) (struct Nsf_Object *object, Tcl_Interp *interp, Tcl_Obj *name1, Tcl_Obj *name2, unsigned int flags); /* 9 */ int (*nsf_UnsetVar2) (struct Nsf_Object *object, Tcl_Interp *interp, CONST char *name1, CONST char *name2, unsigned int flags); /* 10 */ void (*nsfDStringPrintf) (Tcl_DString *dsPtr, CONST char *fmt, va_list apSrc); /* 11 */ int (*nsfPrintError) (Tcl_Interp *interp, CONST char *fmt, ...); /* 12 */ int (*nsfErrInProc) (Tcl_Interp *interp, Tcl_Obj *objName, Tcl_Obj *clName, CONST char *procName); /* 13 */ int (*nsfObjErrType) (Tcl_Interp *interp, CONST char *context, Tcl_Obj *value, CONST char *type, Nsf_Param CONST *pPtr); /* 14 */ void (*nsfStackDump) (Tcl_Interp *interp); /* 15 */ void (*nsfSetObjClientData) (Tcl_Interp *interp, Nsf_Object *object, ClientData data); /* 16 */ ClientData (*nsfGetObjClientData) (Tcl_Interp *interp, Nsf_Object *object); /* 17 */ void (*nsfSetClassClientData) (Tcl_Interp *interp, Nsf_Class *cl, ClientData data); /* 18 */ ClientData (*nsfGetClassClientData) (Tcl_Interp *interp, Nsf_Class *cl); /* 19 */ void (*nsfRequireObjNamespace) (Tcl_Interp *interp, Nsf_Object *object); /* 20 */ int (*nsfCallMethodWithArgs) (Tcl_Interp *interp, Nsf_Object *object, Tcl_Obj *method, Tcl_Obj *arg, int objc, Tcl_Obj *CONST objv[], unsigned int flags); /* 21 */ int (*nsfAddObjectMethod) (Tcl_Interp *interp, struct Nsf_Object *object, CONST char *nm, Tcl_ObjCmdProc *proc, ClientData cd, Tcl_CmdDeleteProc *dp, unsigned int flags); /* 22 */ int (*nsfAddClassMethod) (Tcl_Interp *interp, struct Nsf_Class *cl, CONST char *nm, Tcl_ObjCmdProc *proc, ClientData cd, Tcl_CmdDeleteProc *dp, unsigned int flags); /* 23 */ int (*nsfCreate) (Tcl_Interp *in, Nsf_Class *class, Tcl_Obj *name, int objc, Tcl_Obj *CONST objv[]); /* 24 */ int (*nsf_ArgumentParse) (Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Nsf_Object *object, Tcl_Obj *procNameObj, Nsf_Param CONST *paramPtr, int nrParams, int serial, unsigned int processFlags, Nsf_ParseContext *pcPtr); /* 25 */ void (*nsfLog) (Tcl_Interp *interp, int requiredLevel, CONST char *fmt, ...); /* 26 */ int (*nsf_PointerAdd) (Tcl_Interp *interp, char *buffer, CONST char *typeName, void *valuePtr); /* 27 */ int (*nsf_PointerDelete) (CONST char *key, void *valuePtr, int free); /* 28 */ int (*nsf_PointerTypeRegister) (Tcl_Interp *interp, CONST char*typeName, int *counterPtr); /* 29 */ int (*nsf_ConvertToBoolean) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 30 */ int (*nsf_ConvertToClass) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 31 */ int (*nsf_ConvertToInt32) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 32 */ int (*nsf_ConvertToInteger) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 33 */ int (*nsf_ConvertToObject) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 34 */ int (*nsf_ConvertToPointer) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 35 */ int (*nsf_ConvertToString) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 36 */ int (*nsf_ConvertToTclobj) (Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr); /* 37 */ int (*nsf_EnumerationTypeRegister) (Tcl_Interp *interp, Nsf_EnumeratorConverterEntry *typeRecords); /* 38 */ int (*nsf_CmdDefinitionRegister) (Tcl_Interp *interp, Nsf_methodDefinition *definitionRecords); /* 39 */ } NsfStubs; extern const NsfStubs *nsfStubsPtr; #ifdef __cplusplus } #endif #if defined(USE_NSF_STUBS) /* * Inline function declarations: */ #define Nsf_Init \ (nsfStubsPtr->nsf_Init) /* 0 */ /* Slot 1 is reserved */ #define NsfIsClass \ (nsfStubsPtr->nsfIsClass) /* 2 */ #define NsfGetObject \ (nsfStubsPtr->nsfGetObject) /* 3 */ #define NsfGetClass \ (nsfStubsPtr->nsfGetClass) /* 4 */ #define NsfDeleteObject \ (nsfStubsPtr->nsfDeleteObject) /* 5 */ #define NsfRemoveObjectMethod \ (nsfStubsPtr->nsfRemoveObjectMethod) /* 6 */ #define NsfRemoveClassMethod \ (nsfStubsPtr->nsfRemoveClassMethod) /* 7 */ #define Nsf_ObjSetVar2 \ (nsfStubsPtr->nsf_ObjSetVar2) /* 8 */ #define Nsf_ObjGetVar2 \ (nsfStubsPtr->nsf_ObjGetVar2) /* 9 */ #define Nsf_UnsetVar2 \ (nsfStubsPtr->nsf_UnsetVar2) /* 10 */ #define NsfDStringPrintf \ (nsfStubsPtr->nsfDStringPrintf) /* 11 */ #define NsfPrintError \ (nsfStubsPtr->nsfPrintError) /* 12 */ #define NsfErrInProc \ (nsfStubsPtr->nsfErrInProc) /* 13 */ #define NsfObjErrType \ (nsfStubsPtr->nsfObjErrType) /* 14 */ #define NsfStackDump \ (nsfStubsPtr->nsfStackDump) /* 15 */ #define NsfSetObjClientData \ (nsfStubsPtr->nsfSetObjClientData) /* 16 */ #define NsfGetObjClientData \ (nsfStubsPtr->nsfGetObjClientData) /* 17 */ #define NsfSetClassClientData \ (nsfStubsPtr->nsfSetClassClientData) /* 18 */ #define NsfGetClassClientData \ (nsfStubsPtr->nsfGetClassClientData) /* 19 */ #define NsfRequireObjNamespace \ (nsfStubsPtr->nsfRequireObjNamespace) /* 20 */ #define NsfCallMethodWithArgs \ (nsfStubsPtr->nsfCallMethodWithArgs) /* 21 */ #define NsfAddObjectMethod \ (nsfStubsPtr->nsfAddObjectMethod) /* 22 */ #define NsfAddClassMethod \ (nsfStubsPtr->nsfAddClassMethod) /* 23 */ #define NsfCreate \ (nsfStubsPtr->nsfCreate) /* 24 */ #define Nsf_ArgumentParse \ (nsfStubsPtr->nsf_ArgumentParse) /* 25 */ #define NsfLog \ (nsfStubsPtr->nsfLog) /* 26 */ #define Nsf_PointerAdd \ (nsfStubsPtr->nsf_PointerAdd) /* 27 */ #define Nsf_PointerDelete \ (nsfStubsPtr->nsf_PointerDelete) /* 28 */ #define Nsf_PointerTypeRegister \ (nsfStubsPtr->nsf_PointerTypeRegister) /* 29 */ #define Nsf_ConvertToBoolean \ (nsfStubsPtr->nsf_ConvertToBoolean) /* 30 */ #define Nsf_ConvertToClass \ (nsfStubsPtr->nsf_ConvertToClass) /* 31 */ #define Nsf_ConvertToInt32 \ (nsfStubsPtr->nsf_ConvertToInt32) /* 32 */ #define Nsf_ConvertToInteger \ (nsfStubsPtr->nsf_ConvertToInteger) /* 33 */ #define Nsf_ConvertToObject \ (nsfStubsPtr->nsf_ConvertToObject) /* 34 */ #define Nsf_ConvertToPointer \ (nsfStubsPtr->nsf_ConvertToPointer) /* 35 */ #define Nsf_ConvertToString \ (nsfStubsPtr->nsf_ConvertToString) /* 36 */ #define Nsf_ConvertToTclobj \ (nsfStubsPtr->nsf_ConvertToTclobj) /* 37 */ #define Nsf_EnumerationTypeRegister \ (nsfStubsPtr->nsf_EnumerationTypeRegister) /* 38 */ #define Nsf_CmdDefinitionRegister \ (nsfStubsPtr->nsf_CmdDefinitionRegister) /* 39 */ #endif /* defined(USE_NSF_STUBS) */ /* !END!: Do not edit above this line. */ #endif /* _NSFDECLS */ generic/stubs8.6/nsfIntDecls.h000066400000000000000000000047221242365656200164730ustar00rootroot00000000000000/* * nsfIntDecls.h -- * * This file contains the declarations for all unsupported * functions that are exported by the Tcl library. These * interfaces are not guaranteed to remain the same between * versions. Use at your own risk. * * Copyright (C) 1998-2008 Uwe Zdun (a, b) * Copyright (C) 1998-2014 Gustaf Neumann (a, b) * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #ifndef _NSFINTDECLS #define _NSFINTDECLS /* * WARNING: This file is automatically generated by the tools/genStubs.tcl * script. Any modifications to the function declarations below should be made * in the nsfInt.decls script. */ /* !BEGIN!: Do not edit below this line. */ #ifdef __cplusplus extern "C" { #endif /* * Exported function declarations: */ typedef struct NsfIntStubs { int magic; void *hooks; } NsfIntStubs; extern const NsfIntStubs *nsfIntStubsPtr; #ifdef __cplusplus } #endif #if defined(USE_NSF_STUBS) /* * Inline function declarations: */ #endif /* defined(USE_NSF_STUBS) */ /* !END!: Do not edit above this line. */ #endif /* _NSFINTDECLS */ generic/stubs8.6/nsfStubInit.c000066400000000000000000000071611242365656200165220ustar00rootroot00000000000000/* * nsfStubInit.c -- * * This file contains the initializers for the stub vectors of the Next * Scripting Framework. * * Copyright (C) 1999-2014 Gustaf Neumann (a, b) * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1020, Welthandelsplatz 1 * Vienna, Austria * * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include "nsfInt.h" /* * Remove macros that will interfere with the definitions below. */ /* * WARNING: The contents of this file is automatically generated by the * tools/genStubs.tcl script. Any modifications to the function declarations * below should be made in the generic/tcl.decls script. */ #if defined(PRE86) EXTERN NsfStubs nsfStubs; # else MODULE_SCOPE const NsfStubs nsfStubs; #endif /* !BEGIN!: Do not edit below this line. */ static const NsfIntStubs nsfIntStubs = { TCL_STUB_MAGIC, 0, }; static const NsfStubHooks nsfStubHooks = { &nsfIntStubs }; const NsfStubs nsfStubs = { TCL_STUB_MAGIC, &nsfStubHooks, Nsf_Init, /* 0 */ 0, /* 1 */ NsfIsClass, /* 2 */ NsfGetObject, /* 3 */ NsfGetClass, /* 4 */ NsfDeleteObject, /* 5 */ NsfRemoveObjectMethod, /* 6 */ NsfRemoveClassMethod, /* 7 */ Nsf_ObjSetVar2, /* 8 */ Nsf_ObjGetVar2, /* 9 */ Nsf_UnsetVar2, /* 10 */ NsfDStringPrintf, /* 11 */ NsfPrintError, /* 12 */ NsfErrInProc, /* 13 */ NsfObjErrType, /* 14 */ NsfStackDump, /* 15 */ NsfSetObjClientData, /* 16 */ NsfGetObjClientData, /* 17 */ NsfSetClassClientData, /* 18 */ NsfGetClassClientData, /* 19 */ NsfRequireObjNamespace, /* 20 */ NsfCallMethodWithArgs, /* 21 */ NsfAddObjectMethod, /* 22 */ NsfAddClassMethod, /* 23 */ NsfCreate, /* 24 */ Nsf_ArgumentParse, /* 25 */ NsfLog, /* 26 */ Nsf_PointerAdd, /* 27 */ Nsf_PointerDelete, /* 28 */ Nsf_PointerTypeRegister, /* 29 */ Nsf_ConvertToBoolean, /* 30 */ Nsf_ConvertToClass, /* 31 */ Nsf_ConvertToInt32, /* 32 */ Nsf_ConvertToInteger, /* 33 */ Nsf_ConvertToObject, /* 34 */ Nsf_ConvertToPointer, /* 35 */ Nsf_ConvertToString, /* 36 */ Nsf_ConvertToTclobj, /* 37 */ Nsf_EnumerationTypeRegister, /* 38 */ Nsf_CmdDefinitionRegister, /* 39 */ }; /* !END!: Do not edit above this line. */ /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * End: */ library/000077500000000000000000000000001242365656200125355ustar00rootroot00000000000000library/COPYRIGHT000066400000000000000000000050371242365656200140350ustar00rootroot00000000000000/* * Next Scripting Framework * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * This software is based upon MIT Object Tcl by David Wetherall and * Christopher J. Lindblad, that contains the following copyright * message: * * "Copyright 1993 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty." */ library/lib/000077500000000000000000000000001242365656200133035ustar00rootroot00000000000000library/lib/COPYRIGHT000066400000000000000000000050371242365656200146030ustar00rootroot00000000000000/* * Next Scripting Framework * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * This software is based upon MIT Object Tcl by David Wetherall and * Christopher J. Lindblad, that contains the following copyright * message: * * "Copyright 1993 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty." */ library/lib/make.tcl000066400000000000000000000135031242365656200147260ustar00rootroot00000000000000### ### Utility for the build process. Main purpose currently: ### ### - Build the pkgIndex in each directory ### # adjust the paths; # - auto_path is needed, when nx is loaded via good old pkgIndex.tcl # - tcl::tm::roots is needed when nx is provided as a tcl module (.tm) lappend auto_path .. ::tcl::tm::roots [pwd] #puts stderr TM-LIST=[ ::tcl::tm::path list ] set verbose 0 package require nx namespace eval ::nx {}; # make pkg_mkIndex happy ### nx::Object create make { # # shared lib add files for pkgIndex.tcl # :object method mkIndex {name} { if {$::verbose} {puts stderr "+++ mkIndex in [pwd]"} set fls {} foreach f [glob -nocomplain *tcl] { if {![file isdirectory $f]} { set F [file open $f]; set c [read $F]; close $F if {[string match "*package provide*" $c]} { lappend fls $f } } } set so [glob -nocomplain *[info sharedlibextension]] set version $::nsf::version # loading libnext into nextsh might cause problems on some systems foreach lib [list libnext$version[info sharedlibextension] \ next$version.dll] { set p [lsearch -exact $so $lib] if {$p != -1} { set so [lreplace $so $p $p] puts stderr "new so=<$so>" } } #puts stderr "[pwd]: call so=<$so>" set fls [concat $fls $so] if {$fls ne ""} { if {[file exists pkgIndex.tcl]} { file delete -force pkgIndex.tcl } #puts stderr "callinglevel <[current callinglevel]> $fls" # # redefine the logging behavior to show just error or warnings, # preceded by the current directory # #set ::current [pwd] proc ::tclLog msg { if {[regexp {^(error|warning)} $msg]} { if {[regexp -nocase error $msg]} { error $msg } puts stderr "$msg ([pwd])" } } set flags "-verbose -direct -load nsf" # the following test is just an approximization, loading nsf + # nx does not seem to work for binary extensions (e.g. mongodb) if {$fls ne "nx.tcl" && ![string match "*[info sharedlibextension]" $fls]} { append flags " -load nx" } #package prefer latest if {$::verbose} {puts stderr "[pwd]:\n\tcall pkg_mkIndex $flags . $fls"} pkg_mkIndex {*}$flags . {*}$fls if {$::verbose} {puts stderr "[pwd] done"} } foreach addFile [glob -nocomplain *.add] { if {[file exists $addFile]} { puts stderr "Appending $addFile to pkgIndex.tcl in [pwd]" set OUT [file open pkgIndex.tcl a] set IN [file open $addFile] puts -nonewline $OUT [read $IN] close $IN; close $OUT } } #puts stderr "+++ mkIndex name=$name, pwd=[pwd] DONE" } :public object method inEachDir {path cmd} { if {$::verbose} {puts stderr "[pwd] inEachDir $path (dir [file isdirectory $path]) $cmd"} if { [file isdirectory $path] && ![string match *CVS $path] && ![string match *SCCS $path] && ![string match *Attic $path] && ![string match *dbm* $path] } { set olddir [pwd] cd $path if {[catch {make {*}$cmd $path} errMsg]} { error "$errMsg (in directory [pwd])" } set files [glob -nocomplain *] cd $olddir foreach p $files { :inEachDir $path/$p $cmd } if {$::verbose} {puts stderr "+++ change back to $olddir"} } } :object method in {path cmd} { if {[file isdirectory $path] && ![string match *CVS $path]} { set olddir [pwd] cd $path make {*}$cmd $path cd $olddir } } } ### tcl file-command rename file tcl_file nx::Object create file { :require namespace array set :destructive { atime 0 attributes 0 copy 1 delete 1 dirname 0 executable 0 exists 0 extension 0 isdirectory 0 isfile 0 join 0 lstat 0 mkdir 1 mtime 0 nativename 0 owned 0 pathtype 0 readable 0 readlink 0 rename 1 rootname 0 size 0 split 0 stat 0 tail 0 type 0 volumes 0 writable 0 } foreach subcmd [array names :destructive] { :public object method $subcmd args { #puts stderr " [pwd] call: '::tcl_file [current method] $args'" ::tcl_file [current method] {*}$args } } } rename open file::open proc open {f {mode r}} { file open $f $mode } ### minus n option nx::Class create make::-n foreach f [file info object methods] { if {$f eq "unknown" || $f eq "next" || $f eq "self"} continue if {![file exists destructive($f)] || [file eval [list set :destructive($f)]]} { #puts stderr destruct=$f make::-n method $f args { puts "--- [pwd]:\t[current method] $args" } } else { #puts stderr nondestruct=$f make::-n method $f args { set r [next] #puts "??? [current method] $args -> {$r}" return $r } } } ### command line parameters if {![info exists argv] || $argv eq ""} {set argv -all} if {$argv eq "-n"} {set argv "-n -all"} nx::Class create Script { :public object method create args { lappend args {*}$::argv set s [next] set method [list] foreach arg [lrange $args 1 end] { switch -glob -- $arg { "-all" {$s all} "-n" {$s n} "-*" {set method [string range $arg 1 end]} default { puts "$s $method $arg" $s $method $arg } } } } :object method unknown args { puts stderr "$::argv0: Unknown option ´-$args´ provided" } :public method n {} {file mixin make::-n} :public method all {} {make inEachDir . mkIndex} :public method dir {dirName} {cd $dirName} :public method target {path} {make eval [list set :target $path]} if {[catch {:create main} errorMsg]} { puts stderr "*** $errorMsg" # Exit sliently, alltough we are leaving from an active stack # frame. ::nsf::configure debug 0 exit -1 } } #puts stderr "+++ make.tcl finished." #exit $::result library/lib/mkIndex.tcl000066400000000000000000000042041242365656200154060ustar00rootroot00000000000000### ### Utility for the build process. Main purpose currently: ### ### - Build the pkgIndex in each directory ### # adjust the paths; # - auto_path is needed, when nx is loaded via good old pkgIndex.tcl # - tcl::tm::roots is needed when nx is provided as a tcl module (.tm) lappend auto_path .. ::tcl::tm::roots [pwd] set verbose 0 proc mkIndex {} { if {$::verbose} {puts stderr "+++ mkIndex in [pwd]"} set fls {} foreach f [glob -nocomplain *tcl] { if {![file isdirectory $f]} { set F [open $f]; set c [read $F]; close $F if {[string match "*package provide*" $c]} { lappend fls $f #puts "provide in $f" foreach l [split $c \n] { #puts stderr "check $l" if {[regexp {^\s*package\s+provide\s+(\S+)\s+([0-9]\S+)\s*$} $l _ pkg version]} { #puts stderr "found package $pkg $version in $f" set pkg_file($pkg) $f set pkg_version($pkg) $version break } } } } } set pkgIndex "" foreach pkg [lsort [array names pkg_file]] { append pkgIndex "package ifneeded $pkg $pkg_version($pkg) \[list source \[file join \$dir $pkg_file($pkg)\]\]\n" } foreach addFile [glob -nocomplain *.add] { if {[file exists $addFile]} { puts stderr "Appending $addFile to pkgIndex.tcl in [pwd]" set IN [open $addFile] append pkgIndex [read $IN]\n close $IN } } if {$pkgIndex ne ""} { if {$::verbose} {puts stderr "Write [pwd]/pkgIndex.tcl"} set OUT [open pkgIndex.tcl w] puts -nonewline $OUT $pkgIndex close $OUT } #puts stderr "+++ mkIndex pwd=[pwd] DONE" } proc inEachDir {path cmd} { if {$::verbose} {puts stderr "[pwd] inEachDir $path (dir [file isdirectory $path]) $cmd"} if { [file isdirectory $path] && ![string match *CVS $path] && ![string match *SCCS $path] && ![string match *Attic $path] && ![string match *dbm* $path] } { set olddir [pwd] cd $path if {[catch $cmd errMsg]} { error "$errMsg (in directory [pwd])" } set files [glob -nocomplain *] cd $olddir foreach p $files { inEachDir $path/$p $cmd } if {$::verbose} {puts stderr "+++ change back to $olddir"} } } inEachDir . mkIndex library/lib/nx-callback.add000066400000000000000000000001771242365656200161410ustar00rootroot00000000000000# -*- Tcl -*- namespace eval ::nsf { set traitIndex(nx::traits::callback) {script {package require nx::traits::callback}} } library/lib/nx-callback.tcl000066400000000000000000000006501242365656200161670ustar00rootroot00000000000000package require nx package require nx::trait package provide nx::traits::callback 1.0 nx::Trait create nx::traits::callback { # # A small support trait to ease syntactically the reference to # instance variables and the registration of callbacks. # :method bindvar {name} { :require namespace return [nx::self]::$name } :method callback {name args} { return [list [nx::self] $name {*}$args] } } library/lib/nx-help.tcl000066400000000000000000000027431242365656200153700ustar00rootroot00000000000000package provide nx::help 1.0 package require nx namespace eval ::nx { proc help {args} { set l [llength $args] if {$l == 0} { nsf::log info "Usage: help /nsf-tcl command/" return } if {[nsf::is object [lindex $args 0]]} { set obj [nsf::dispatch [lindex $args 0] eval self] if {$l == 1} { nsf::log info "$obj /method/ ..." return } set w [lrange $args 1 end] set h [$obj ::nsf::methods::object::info::lookupmethod $w] if {$h eq ""} { nsf::log warn "$obj has no method \"$w\"" return } set origin [nsf::cmd::info origin $h] if {$origin eq ""} { # # Since "info object ..." is not a true ensemble, we # have to use the definition handle. # set origin [nsf::cmd::info definitionhandle $h] } if {[nsf::is object $origin]} { nsf::log info "$obj $w [join [lsort [nsf::cmd::info submethods $origin]] |] ..." } else { nsf::log info "$obj $w [nsf::cmd::info syntax -context $obj $h]" } return } # # catch-all # set cmd [namespace origin [lindex $args 0]] nsf::log info "[lindex $args 0] [nsf::cmd::info syntax $cmd]" return } } return nx::help nx::Object nx::help nx::Object configure nx::help nx::Object create nx::help nx::Object new nx::help nx::Object newx nx::help nx::Object info nx::help nx::Object info precedence nx::help nx::Object info vars nx::help nx::Object info object nx::help nx::Object info object mixin nx::help nx::Object info object methods nx::help nsf::cmd::info library/lib/nx-pp.tcl000066400000000000000000000202071242365656200150520ustar00rootroot00000000000000package require nx package provide nx::pp 1.0 # @package nx::pp # # A simple pretty printer for Tcl/XOTcl/NX # that converts a script into HTML output. # # Usage: # package require nx::pp # set html [nx::pp render { your script }] # # Desinged to be usable from asciidoc like gnu source-highligt, # ignores options. # # Gustaf Neumann, Dez 2010 namespace eval ::nx::pp { # # The pretty printer is implemented via several States objects that # represent different context for input processing. Such states are # for example "comment" or "quoted strings". Every state contains # the parsed content, and a CSS class for HTML rendering. # nx::Class create State { :property {text ""} :property {cssClass "[namespace tail [nx::self]]"} :property {prevState "[default]"} :public method start {char} { # Start output in a state by initializing the text buffer. set :text $char return [nx::self] } :public method cssWrap {html} { if {${:cssClass} ne ""} { return "$html" } else { return $html } } :public method flush {} { # Flush the current text in the buffer using the css class set html [string map [list & {&} < {<} > {>}] ${:text}] ::nx::pp puts -nonewline [:cssWrap $html] set :text "" } :method new_state {new lastChar firstChar} { # Switch from one state to another state if {[$new eval {info exists :escape}]} { $new configure -prevState [nx::self] append :text $lastChar return [$new] } else { $new configure -prevState [default] append :text $lastChar :flush return [$new start $firstChar] } } :public method process {char} { # Process a single character in the current state append :text $char return [nx::self] } } # # Below, we define the state objects for processing the input # State create default -cssClass "" { # # The State "default" is processing bare Tcl words. In this state, # we perform substitutions of keywords and placeholders. # :public object method process {char} { switch $char { "\#" { return [:new_state comment "" $char]} "\"" { return [:new_state quoted "" $char]} "\$" { return [:new_state variable "" $char]} default {return [nx::next]} } } set keywords { after append apply array binary break catch cd chan clock close concat continue dict else encoding eof error eval exec exit expr fblocked fconfigure fcopy file fileevent flush for foreach format gets glob global if incr info interp join lappend lassign lindex linsert list llength load lrange lrepeat lreplace lreverse lsearch lset lsort namespace open pid proc puts read regexp regsub rename return scan seek set socket source split stdin stderr stdout string subst switch tell trace unset uplevel update upvar variable vwait while package public protected private method alias property forward delete require my next new self current dispatch objectparameter defaultmethod create init new destroy alloc dealloc recreate unknown move cget configure class object superclass mixin filter guard metaclass methods lookup ::nx::Class nx::Class ::xotcl::Class xotcl::Class Class ::nx::Object nx::Object ::xotcl::Object xotcl::Object Object ::nx::VariableSlot nx::VariableSlot Attribute } set :re(keyword1) (\[^:.-\])(\\m[join $keywords \\M|\\m]\\M) set :re(keyword2) (\[^:\])(\\m:[join $keywords \\M|:\\m]\\M) set :re(placeholder1) {([/][a-zA-Z0-9:]+?[/])} set :re(placeholder2) {([?][^ ][-a-zA-Z0-9: .]+?[?])} :public object method flush {} { set html [string map [list & {&} < {<} > {>}] ${:text}] regsub -all [set :re(keyword1)] " $html" {\1\2} html regsub -all [set :re(keyword2)] $html {\1\2} html set html [string range $html 1 end] regsub -all [set :re(placeholder1)] $html {\1} html regsub -all [set :re(placeholder2)] $html {\1} html nx::pp puts -nonewline [:cssWrap $html] set :text "" } } State create quoted -cssClass "string" { # # The State "quoted" is for content between double quotes. # :public object method process {char} { switch $char { "\"" {return [:new_state ${:prevState} $char ""]} "\\" {return [:new_state escape $char ""]} default {return [nx::next]} } } } State create comment { # # The State "comment" is for Tcl comments (currently, only up to # end of line) # :public object method process {char} { switch $char { "\n" {return [:new_state default $char ""]} default {return [nx::next]} } } } State create variable { # # The State "variable" is for simple Tcl variables (without curley # braces) # :public object method process {char} { switch -glob -- $char { {\{} {return [:new_state quoted_variable $char ""] } {[a-zA-Z0-9_:]} {return [nx::next]} default {return [:new_state default "" $char]} } } } State create quoted_variable -cssClass "variable" { # # The State "quoted_variable" is for Tcl variables, where the # names are quoted with curley braces. # :public object method process {char} { switch -glob -- $char { {\}} {return [:new_state default $char ""] } default {return [nx::next]} } } } State create escape -cssClass "" { # # The State "escape" is for simple backslash handling. # # Set an instance variable to ease identification of the state set :escape 1 # When a character is processed in the escape state, it is suffed # into the previous state and returns immediately to it. # :public object method process {char} { ${:prevState} eval [list append :text $char] return ${:prevState} } } } # # Finally, we create a simple pretty-printer as an object. The # method render receives a Tcl script as input and writes the HTML # output to stdout # nx::Object create nx::pp { :public object method toHTML {block} { set state [self]::default set l [string length $block] for {set i 0} {$i < $l} {incr i} { set state [$state process [string index $block $i]] } $state flush } :public object method numbers {block} { set nrlines [regsub -all \n $block \n block] incr nrlines set HTML "" for {set i 1} {$i<=$nrlines} {incr i} { append HTML [format %3d $i]\n } return $HTML } :public object method render {{-linenumbers false} -noCSSClasses:switch block} { set :output "" :toHTML $block set HTML ${:output} set :output "" :puts "" if {$linenumbers} { :puts -nonewline "" :puts -nonewline "
[:numbers $block]
$HTML
" } else { :puts -nonewline "
$HTML
" } return ${:output} } :public object method puts {{-nonewline:switch} string} { append :output $string if {!$nonewline} {append :output \n} } } # pp render { # set x "hello\ngoodbye" # # a comment line # set b $c($a).b # foo a ${:text} b "hello \"$x" world # } # exit library/lib/nx-test.tcl000066400000000000000000000152301242365656200154120ustar00rootroot00000000000000package provide nx::test 1.0 package require nx namespace eval ::nx { # @file Simple regression test support for XOTcl / NX nx::Class create nx::test { # # Class Test is used to configure test instances, which can # be configured by the following parameters: # # @param cmd the command to be executed # @param expected the expected result # @param count number of executions of cmd # @param pre a command to be executed at the begin of the test (before cmd) # @param post a command to be executed after the test (after all cmds) # @param namespace in which pre, post and cmd are evaluated; default "::" # # The defined tests can be executed by [:cmd "Test run"] :property {name ""} :property cmd :property {namespace ::} :property {verbose:boolean 0} :property {expected:any 1} :property {count:integer 1} :property msg :property setResult :property errorReport :property pre :property post :object property {count:integer 1} :object property {verbose:boolean 0} :object variable success 0 :object variable failure 0 :object variable testfile "" :object variable ms 0 :object variable case "test" :public object method success {} { incr :success } :public object method failure {} { incr :failure } :public object method ms {ms:double} { set :ms [expr {${:ms} + $ms}] } :public object method destroy {} { lappend msg \ Test-set [file rootname [file tail ${:testfile}]] \ tests [expr {${:success} + ${:failure}}] \ success ${:success} \ failure ${:failure} \ ms ${:ms} puts "Summary: $msg\n" array set "" $::argv if {[info exists (-testlog)]} { set f [open $(-testlog) a]; puts $f $msg; close $f } next } :public object method case {name arg:optional} { # # Experimental version of Test case, which (1) accepts test case as argument # and (2) destroys all created objects on exit (auto cleanup) # # General limitation: namespace resolving differs in nested evals # from global evals. So, this approach is not suitable for all tests # (but for most). # # Current limitations: # - cleanup for for nx::Objects, # - no method/mixin cleanup # - no var cleanup # set :case $name nsf::log notice "Running test case: [info script] $name" if {[info exists arg]} { foreach o [Object info instances -closure] {set pre_exist($o) 1} namespace eval :: [list [current] eval $arg] foreach o [Object info instances -closure] { if {[info exists pre_exist($o)]} continue if {$o in {::xotcl::Attribute}} continue if {[::nsf::object::exists $o]} {$o destroy} } } } :public object method new args { set testfile [file rootname [file tail [info script]]] set :testfile $testfile if {![info exists :ccount(${:case})]} {set :ccount(${:case}) 0} set :name $testfile/${:case}.[format %.3d [incr :ccount(${:case})]] :create ${:name} -name ${:name} -count ${:count} -verbose ${:verbose} {*}$args } :public object method run {} { set startTime [clock clicks -milliseconds] foreach example [lsort [:info instances -closure]] { $example run } set ms [expr {[clock clicks -milliseconds]-$startTime}] puts stderr "Total Time: $ms ms" } :public method call {msg cmd} { if {${:verbose}} {puts stderr "$msg: $cmd"} return [::namespace eval ${:namespace} $cmd] } :public method run args { set startTime [clock clicks -milliseconds] :exitOn if {[info exists :pre]} {:call "pre" ${:pre}} if {![info exists :msg]} {set :msg ${:cmd}} set gotError [catch {:call "run" ${:cmd}} r] #puts stderr "gotError = $gotError // $r == ${:expected} // [info exists :setResult]" if {[info exists :setResult]} {set r [eval [set :setResult]]} if {$r eq ${:expected}} { if {$gotError} { set c 1 } else { if {[info exists :count]} {set c ${:count}} {set c 1000} } #puts stderr "running test $c times" if {${:verbose}} {puts stderr "running test $c times"} if {$c > 1} { # # The following line was used to calculate calling-overhead. # deactivated for now, since sometimes the reported calling # overhead was larger than the call. # #set r0 [time {time {::namespace eval ${:namespace} ";"} $c}] #regexp {^(-?[0-9]+) +} $r0 _ mS0 set r1 [time {time {::namespace eval ${:namespace} ${:cmd}} $c}] #puts stderr "running {time {::namespace eval ${:namespace} ${:cmd}} $c} => $r1" regexp {^(-?[0-9]+) +} $r1 _ mS1 #set ms [expr {($mS1 - $mS0) * 1.0 / $c}] set ms [expr {$mS1 * 1.0 / $c}] # if for some reason the run of the test is faster than the # body-less eval, don't report negative values. #if {$ms < 0} {set ms 0.0} #puts stderr "[set :name]:\t[format %6.2f $ms]\tmms, ${:msg} (overhead [format %.2f [expr {$mS0*1.0/$c}]])" puts stderr "[set :name]:\t[format %6.2f $ms]\tmms, ${:msg}" } else { puts stderr "[set :name]: ${:msg} ok" } ::nx::test success } else { puts stderr "[set :name]:\tincorrect result for '${:msg}', expected:" puts stderr "'${:expected}', got\n\"$r\"" puts stderr "\tin test file [info script]" if {[info exists :errorReport]} {eval [set :errorReport]} ::nx::test failure # # Make sure that the script exits with an error code, but # unwind the callstack via return with an error code. Using # [exit -1] would leave us with a partially unwinded callstack # with garbage complicating debugging (e.g. MEM_COUNT # statistics would indicate unbalanced refCounts, etc.). :exit -1 } if {[info exists :post]} {:call "post" ${:post}} ::nx::test ms [expr {[clock clicks -milliseconds]-$startTime}] :exitOff } :public method exit {{statuscode "1"}} { array set map {1 ok -1 error} set errorcode $map($statuscode) :exitOff return -code $errorcode -level [expr {[info level]-1}] "Test was exited with code $statuscode" } :public method exitOn {} { interp hide {} exit; interp alias {} ::exit {} [current] exit } :public method exitOff {} { interp alias {} ::exit {} interp expose {} exit; } } ::namespace export Test } proc ? {cmd expected {msg ""}} { set namespace [uplevel {::namespace current}] #puts stderr "eval in namespace $namespace" if {$msg ne ""} { set t [nx::test new -cmd $cmd -msg $msg -namespace $namespace] } else { set t [nx::test new -cmd $cmd -namespace $namespace] } $t configure -expected $expected $t run nsf::__db_run_assertions } library/lib/nx-traits.tcl000066400000000000000000000137421242365656200157470ustar00rootroot00000000000000package require nx package provide nx::trait 0.4 # @package nx::trait # # Minimal trait framework with checking in NX, based on # # S. Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: # Traits: A Mechanism for Fine-grained Reuse, # ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 # # Gustaf Neumann (Aug 2011) # # Traits are a mechanism for the reuse of methods. In contrary to # other forms of reuse (e.g. inheritance of methods in a class # hierarchy or via mixin classes), the methods defined in traits are # materialized in the target objects and classes. This gives more # fine-grained control over the reuse of methods and overcomes the # "total composition ordering" limitation of mixins. # # The current implementation does not handle overwrites (conflicting # definition from several traits), be we handle renames (aliases) and # we check required methods. "requiredVariables" (not part of the # ducasse paper) are not checked yet. # # In essence, the package provides a class "nx::Trait" to define # Traits and a method "useTrait" to reuse a trait in trait consumer # (e.g. a class or another trait). # # Usage: # package require nx::trait # nx::Trait create .... { # ... # } # nx::Class create .... { # ... # :useTrait ... # } # # # Define a method to allow configuration for verbosity of the # trait operations: # # nx::configure trait-verbosity on|off # # This might be useful for debugging of complex trait compositions. # nx::configure public object method trait-verbosity {onoff:boolean,optional} { if {[info exists onoff]} { set :trait-verbosity $onoff } else { set :trait-verbosity } } nx::configure trait-verbosity off namespace eval ::nx::trait { # # nx::trait::provide and nx::trait::require implement a basic # auto-loading mechanism for traits # nsf::proc provide {traitName script} { set ::nsf::traitIndex($traitName) [list script $script] } nsf::proc require {traitName} { if {[::nsf::object::exists $traitName]} {return} set key ::nsf::traitIndex($traitName) if {[info exists $key]} { array set "" [set $key] if {$(script) ne ""} { eval $(script) } } if {[::nsf::object::exists $traitName]} {return} error "cannot require trait $traitName, trait unknown" } # # The function nx::trait::add adds the methods defined in the # specified trait to the obj/class provided as first argument. # nsf::proc add {obj -per-object:switch traitName {nameMap ""}} { array set map $nameMap if {${per-object} || ![::nsf::is class $obj]} { error "per-object traits are currently not supported" } foreach m [$traitName info methods -callprotection all -path] { if {[info exists map($m)]} {set newName $map($m)} else {set newName $m} # do not add entries with $newName empty if {$newName eq ""} continue set traitMethodHandle [$traitName info method definitionhandle $m] $obj public alias $newName $traitMethodHandle if {[nx::configure trait-verbosity]} { puts "...trait: $obj public alias $newName" } } foreach slot [$traitName info variables] { #puts "$obj - wanna define: [$traitName info variable definition $slot]" $obj {*}[lrange [$traitName info variable definition $slot] 1 end] if {[nx::configure trait-verbosity]} { puts "...trait: $obj [lrange [$traitName info variable definition $slot] 1 end]" } } } # # The function nx::trait::checkObject checks, whether the target # object has the method defined that the trait requires. # nsf::proc checkObject {obj traitName} { foreach m [$traitName cget -requiredMethods] { #puts "$m ok? [$obj info methods -closure $m]" if {[$obj info lookup method $m] eq ""} { error "trait $traitName requires $m, which is not defined for $obj" } } } # # The function nx::trait::checkClass checks, whether the target # class has the method defined that the trait requires. # nsf::proc checkClass {obj traitName} { foreach m [$traitName cget -requiredMethods] { #puts "$m ok? [$obj info methods -closure $m]" if {[$obj info methods -closure $m] eq ""} { error "trait $traitName requires $m, which is not defined for $obj" } } } } # # The require methods for traits extend the predefined ensemble with # trait-specific subcommands. # nx::Class public method "require trait" {traitName {nameMap ""}} { # adding a trait to a class if {[nx::configure trait-verbosity]} { puts "trait: [self] requires $traitName" } nx::trait::require $traitName nx::trait::checkClass [self] $traitName nx::trait::add [self] $traitName $nameMap } #nx::Object public method "require object trait" {traitName {nameMap ""}} { # puts "[self] require object trait $traitName -- MAYBE OBSOLETE" # # adding a trait to an object # nx::trait::require $traitName # nx::trait::checkObject [self] $traitName # nx::trait::add [self] -per-object $traitName $nameMap #} # # The class "nx::Trait" provides the basic properties and methods needed for # the trait management. # nx::Class create nx::Trait -superclass nx::Class { :property {package} :property {requiredMethods:0..n ""} :property {requiredVariables:0..n ""} ::nsf::method::setter [self] requiredMethods:0..n ::nsf::method::setter [self] requiredVariables:0..n :public method "require trait" {traitName {nameMap ""}} { # adding a trait to a trait nx::trait::require $traitName nx::trait::add [self] $traitName $nameMap set finalReqMethods {} # remove the methods from the set of required methods, which became available foreach m [lsort -unique [concat ${:requiredMethods} [$traitName cget -requiredMethods]]] { if {[:info methods $m] eq ""} {lappend finalReqMethods $m} } #puts "final reqMethods of [self]: $finalReqMethods // defined=[:info methods]" set :requiredMethods $finalReqMethods } } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/lib/nx-volatile.tcl000066400000000000000000000010651242365656200162530ustar00rootroot00000000000000# # Package to add configure-parameter "-volatile" # package require nx package provide nx::volatile 1.0 namespace eval ::nx { ::nx::ObjectParameterSlot create ::nx::Object::slot::volatile -noarg true ::nsf::method::create ::nx::Object::slot::volatile value=set {object var value} { $object ::nsf::methods::object::volatile } ::nsf::method::create ::nx::Object::slot::volatile value=get {object var} { ::nsf::object::property $object volatile } } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/lib/nx-zip.tcl000066400000000000000000000217161242365656200152430ustar00rootroot00000000000000# # Zip file generator - Create a Zip-file from a list of input file names # # This implementation is based on the zip file builder of Artur # Trzewik (http://wiki.tcl.tk/15158), but was simplified, refactored, # commented and extended, based on Trf and translated to NX; for # details about the format, see # http://www.pkware.com/documents/casestudies/APPNOTE.TXT # # by Gustaf Neumann (June 2011) # package require nx package require Trf package provide nx::zip 1.1 namespace eval ::nx::zip { nx::Class create Archive { # # The public interface of the class archive contains the methods # # - addFile (add a file from the filesystem to the archive) # - addString (add the file-content from a string to the archive) # # - writeToZipFile (produce a Zip file) # - ns_returnZipFile (return a zip file via aolserver ns_return) # # - writeToStream (for already opened and configured # output streams # # # Add a file from the file system to the zip archive # :public method addFile {inputFileName outputFileName:optional} { # inputFileName - source file to archive # outputFileName - name of the file in the archive if {![file readable $inputFileName] || [file isdirectory $inputFileName]} { error "filename $inputFileName does not belong to a readable file" } if {![info exists outputFileName]} {set outputFileName $inputFileName} lappend :files file $inputFileName $outputFileName } # # Add a filecontent provided as string to the zip archive # :public method addString {string outputFileName} { # string - content to be added # outputFileName - name of the file in the archive lappend :files string $string $outputFileName } # # Write the added files to a zip file # :public method writeToZipFile {zipFileName} { set fout [open $zipFileName w] fconfigure $fout -translation binary -encoding binary :writeToStream $fout close $fout } # # return the added files via aolserver/naviserver to the client # :public method ns_returnZipFile {zipFileName} { ns_write "HTTP/1.0 200 OK\r\nContent-type: application/zip\r\n" ns_write "Content-Disposition: attachment;filename=\"$zipFileName\"\r\n" ns_write "\r\n" set channel [ns_conn channel] fconfigure $channel -translation binary :writeToStream $channel # aolserver/naviserver closes the channel automatically } # # Write the added files to an already open stream # :public method writeToStream {outputStream} { set :outputStream $outputStream # # Write all files to the outout stream # set descriptionList [list] foreach {type in fnOut} ${:files} { lappend descriptionList [:addSingleFile $type $in $fnOut] } # # we have no # - archive descryption header # - archive extra data record # # Add the central directory # set :cdOffset ${:written} foreach {type in fnOut} ${:files} desc $descriptionList { array set "" $desc # For every file, it contains again part of the information of # the local file headers, but with some additional information # such as a the "version made by", comment, ... set comment "" set platform 0 ;# dos/windows #if {$::tcl_platform(platform) ne "windows"} { # set platform 3 ;# unix #} # central file header signature binary scan \x02\x01\x4B\x50 I CFH_SIG :writeLong $CFH_SIG # version made by (os + zip version) :writeShort [expr { (($platform << 8) | 20) }] :writeFileHeaderBlock $desc # file comment length :writeShort [string length $comment] # disk number start :writeShort 0 # internal file attributes :writeShort 0 # external file attributes :writeLong 0 # relative offset of local header :writeLong $(offset) # file name :writeString $(fileNameInternal) :writeExtraFieldUPATH $(fileName) $(fileNameInternal) # file comment :writeString $comment } set :cdLength [expr {${:written} - ${:cdOffset}}] # # End of Central Directory record # binary scan \x06\x05\x4B\x50 I EOCD :writeLong $EOCD # disk numbers :writeShort 0 :writeShort 0 # number of entries set filenum [expr {[llength ${:files}] / 3}] :writeShort $filenum :writeShort $filenum # length and location of CD :writeLong ${:cdLength} :writeLong ${:cdOffset} # zip file comment set comment "" # comment length :writeShort [string bytelength $comment] :writeString $comment } # # Constructor # :method init {} { set :files [list] set :cdLength 0 set :cdOffset 0 set :written 0 } # # Output content file to the output stream # :method addSingleFile {type in fnOut} { set (offset) ${:written} if {$type eq "file"} { set fdata [open $in r] fconfigure $fdata -encoding binary -translation binary set data [read $fdata] close $fdata set mtime [file mtime $in] } else { set data [encoding convertto utf-8 $in] set mtime [clock seconds] } # # local file header # binary scan \x04\x03\x4B\x50 I LFH_SIG :writeLong $LFH_SIG set datacompressed [string range [::zip -mode compress -- $data] 2 end-4] set (dosTime) [:toDosTime $mtime] set (crc) [::crc-zlib -- $data] set (csize) [string length $datacompressed] set (size) [string length $data] set (fileName) [encoding convertto utf-8 $fnOut] set (fileNameInternal) $(fileName) #set (fileNameInternal) [encoding convertto cp850 $fnOut] set (extraFieldLength) [expr {9+[string length $(fileName)]}] :writeFileHeaderBlock [array get ""] # file name :writeString $(fileNameInternal) :writeExtraFieldUPATH $(fileName) $(fileNameInternal) # # file data # :writeString $datacompressed return [array get ""] } :method writeFileHeaderBlock {pairs} { array set "" $pairs # version needed to extract :writeShort 20 # general pupose bit flag :writeShort [expr {1<<11}] #:writeShort 0 # compression method :writeShort 8 # last mod. time and date :writeLong $(dosTime) :writeString $(crc) :writeLong $(csize) :writeLong $(size) # file name length :writeShort [string length $(fileNameInternal)] # extra field length :writeShort $(extraFieldLength) } # # Convert the provided time stamp to dos time # :method toDosTime {time} { foreach {year month day hour minute second} \ [clock format $time -format "%Y %m %e %k %M %S"] {} set RE {^0([0-9]+)$} regexp $RE $year . year regexp $RE $month . month regexp $RE $day . day regexp $RE $hour . hour regexp $RE $minute . minute regexp $RE $second . second set value [expr {(($year - 1980) << 25) | ($month << 21) | ($day << 16) | ($hour << 11) | ($minute << 5) | ($second >> 1)}] return $value } # # Extra field UPath: Info-ZIP Unicode Path Extra Field # :method writeExtraFieldUPATH {fileName fileNameInternal} { # extra field UPATH binary scan \x70\x75 S EPEF :writeShort $EPEF :writeShort [expr {5+[string length $fileName]}] :writeByte 1 :writeString [::crc-zlib $fileNameInternal] :writeString $fileName } # # Write the provided integer in binary form as a long value (32 bit) # :method writeLong {long:integer} { puts -nonewline ${:outputStream} [binary format i $long] incr :written 4 } # # Write the provided integer in binary form as a short value (16 bit) # :method writeShort {short:integer} { puts -nonewline ${:outputStream} [binary format s $short] incr :written 2 } # # Write the provided integer in binary form as a single byte (8 bit) # :method writeByte {byte:integer} { puts -nonewline ${:outputStream} [binary format c $byte] incr :written 1 } # # Write the provided string to the output stream and increment # byte counter. # :method writeString {string} { puts -nonewline ${:outputStream} $string incr :written [string length $string] } :method writeStringBytes {string} { puts -nonewline ${:outputStream} $string incr :written [string bytelength $string] } } } if {0} { set z [::nx::zip::Archive new] $z addFile README.aol $z addFile COPYRIGHT $z addFile nsfUtil.o $z addFile doc/nx.css $z addString "This is a file\nthat может be from a string\n" README $z addString "-Avec 3,2% des parts de marché, la France est le sixième plus grand pays fournisseur de l’Autriche. " franz.txt $z writeToZipFile /tmp/test.zip $z destroy } library/mongodb/000077500000000000000000000000001242365656200141625ustar00rootroot00000000000000library/mongodb/COPYRIGHT000066400000000000000000000050371242365656200154620ustar00rootroot00000000000000/* * Next Scripting Framework * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * This software is based upon MIT Object Tcl by David Wetherall and * Christopher J. Lindblad, that contains the following copyright * message: * * "Copyright 1993 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty." */ library/mongodb/Makefile.in000066400000000000000000000403071242365656200162330ustar00rootroot00000000000000# Makefile.in -- # # This file is a Makefile for Sample TEA Extension. If it has the name # "Makefile.in" then it is a template for a Makefile; to generate the # actual Makefile, run "./configure", which is a configuration script # generated by the "autoconf" program (constructs like "@foo@" will get # replaced in the actual Makefile. # # Copyright (c) 1999 Scriptics Corporation. # Copyright (c) 2002-2003 ActiveState Corporation. # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # #======================================================================== # Add additional lines to handle any additional AC_SUBST cases that # have been added in a customized configure script. #======================================================================== #SAMPLE_NEW_VAR = @SAMPLE_NEW_VAR@ #======================================================================== # Nothing of the variables below this line should need to be changed. # Please check the TARGETS section below to make sure the make targets # are correct. #======================================================================== #======================================================================== # The names of the source files is defined in the configure script. # The object files are used for linking into the final library. # This will be used when a dist target is added to the Makefile. # It is not important to specify the directory, as long as it is the # $(srcdir) or in the generic, win or unix subdirectory. #======================================================================== PKG_SOURCES = @PKG_SOURCES@ PKG_OBJECTS = @PKG_OBJECTS@ PKG_STUB_SOURCES = @PKG_STUB_SOURCES@ PKG_STUB_OBJECTS = @PKG_STUB_OBJECTS@ #======================================================================== # PKG_TCL_SOURCES identifies Tcl runtime files that are associated with # this package that need to be installed, if any. #======================================================================== PKG_TCL_SOURCES = @PKG_TCL_SOURCES@ #======================================================================== # This is a list of public header files to be installed, if any. #======================================================================== PKG_HEADERS = @PKG_HEADERS@ #======================================================================== # "PKG_LIB_FILE" refers to the library (dynamic or static as per # configuration options) composed of the named objects. #======================================================================== PKG_LIB_FILE = @PKG_LIB_FILE@ PKG_STUB_LIB_FILE = @PKG_STUB_LIB_FILE@ lib_BINARIES = $(PKG_LIB_FILE) BINARIES = $(lib_BINARIES) SHELL = @SHELL@ srcdir = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ libdir = @libdir@ datarootdir = @datarootdir@ datadir = @datadir@ mandir = @mandir@ includedir = @includedir@ DESTDIR = PKG_DIR = $(PACKAGE_NAME)$(PACKAGE_VERSION) pkgdatadir = $(datadir)/$(PKG_DIR) pkglibdir = $(libdir)/$(PKG_DIR) pkgincludedir = $(includedir)/$(PKG_DIR) top_builddir = . INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ CC = @CC@ CFLAGS_DEFAULT = @CFLAGS_DEFAULT@ CFLAGS_WARNING = @CFLAGS_WARNING@ CLEANFILES = @CLEANFILES@ EXEEXT = @EXEEXT@ LDFLAGS_DEFAULT = @LDFLAGS_DEFAULT@ MAKE_LIB = @MAKE_LIB@ MAKE_SHARED_LIB = @MAKE_SHARED_LIB@ MAKE_STATIC_LIB = @MAKE_STATIC_LIB@ MAKE_STUB_LIB = @MAKE_STUB_LIB@ OBJEXT = @OBJEXT@ RANLIB = @RANLIB@ RANLIB_STUB = @RANLIB_STUB@ SHLIB_CFLAGS = @SHLIB_CFLAGS@ SHLIB_LD = @SHLIB_LD@ SHLIB_LD_LIBS = @SHLIB_LD_LIBS@ STLIB_LD = @STLIB_LD@ TCL_DEFS = @TCL_DEFS@ TCL_BIN_DIR = @TCL_BIN_DIR@ TCL_SRC_DIR = @TCL_SRC_DIR@ # Not used, but retained for reference of what libs Tcl required TCL_LIBS = @TCL_LIBS@ #======================================================================== # TCLLIBPATH seeds the auto_path in Tcl's init.tcl so we can test our # package without installing. The other environment variables allow us # to test against an uninstalled Tcl. Add special env vars that you # require for testing here (like TCLX_LIBRARY). #======================================================================== EXTRA_PATH = $(top_builddir):$(TCL_BIN_DIR) TCLSH_ENV = TCL_LIBRARY=`@CYGPATH@ $(TCL_SRC_DIR)/library` \ @LD_LIBRARY_PATH_VAR@="$(EXTRA_PATH):$(@LD_LIBRARY_PATH_VAR@)" \ PATH="$(EXTRA_PATH):$(PATH)" \ TCLLIBPATH="../../ $(top_builddir)" TCLSH_PROG = @TCLSH_PROG@ TCLSH = $(TCLSH_ENV) $(TCLSH_PROG) SHARED_BUILD = @SHARED_BUILD@ INCLUDES = @PKG_INCLUDES@ @TCL_INCLUDES@ EXTRA_CFLAGS = @PKG_CFLAGS@ EXTRA_CFLAGS = @PKG_CFLAGS@ -DMONGO_HAVE_STDINT -DUSE_NSF_STUBS=1 # TCL_DEFS is not strictly need here, but if you remove it, then you # must make sure that configure.in checks for the necessary components # that your library may use. TCL_DEFS can actually be a problem if # you do not compile with a similar machine setup as the Tcl core was # compiled with. #DEFS = $(TCL_DEFS) @DEFS@ $(EXTRA_CFLAGS) DEFS = @DEFS@ $(EXTRA_CFLAGS) CONFIG_CLEAN_FILES = Makefile CPPFLAGS = @CPPFLAGS@ LIBS = @PKG_LIBS@ @LIBS@ AR = ar CFLAGS = @CFLAGS@ COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) #======================================================================== # Start of user-definable TARGETS section #======================================================================== #======================================================================== # TEA TARGETS. Please note that the "libraries:" target refers to platform # independent files, and the "binaries:" target inclues executable programs and # platform-dependent libraries. Modify these targets so that they install # the various pieces of your package. The make and install rules # for the BINARIES that you specified above have already been done. #======================================================================== all: binaries libraries doc #======================================================================== # The binaries target builds executable programs, Windows .dll's, unix # shared/static libraries, and any other platform-dependent files. # The list of targets to build for "binaries:" is specified at the top # of the Makefile, in the "BINARIES" variable. #======================================================================== binaries: $(BINARIES) pkgIndex.tcl libraries: doc: @echo "If you have documentation to create, place the commands to" @echo "build the docs in the 'doc:' target. For example:" @echo " xml2nroff sample.xml > sample.n" @echo " xml2html sample.xml > sample.html" install: all install-binaries install-libraries install-doc install-binaries: binaries install-lib-binaries install-bin-binaries if test "x$(SHARED_BUILD)" = "x1"; then \ $(INSTALL_DATA) pkgIndex.tcl $(DESTDIR)$(pkglibdir); \ fi #======================================================================== # This rule installs platform-independent files, such as header files. #======================================================================== install-libraries: libraries @mkdir -p $(DESTDIR)$(includedir) @echo "Installing header files in $(DESTDIR)$(includedir)" @if test -n "$(PKG_HEADERS)" ; then \ for i in "$(PKG_HEADERS)" ; do \ echo "Installing $(srcdir)/$$i" ; \ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir) ; \ done; \ fi $(INSTALL_DATA) $(srcdir)/nx-mongo.tcl $(DESTDIR)$(pkglibdir) #======================================================================== # Install documentation. Unix manpages should go in the $(mandir) # directory. #======================================================================== install-doc: doc #install-doc: doc # @mkdir -p $(DESTDIR)$(mandir)/mann # @echo "Installing documentation in $(DESTDIR)$(mandir)" # @for i in $(srcdir)/doc/*.n; do \ # echo "Installing $$i"; \ # rm -f $(DESTDIR)$(mandir)/mann/`basename $$i`; \ # $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/mann ; \ # done TESTLOG = ./__test.log TESTFLAGS = -testlog $(TESTLOG) test: binaries libraries rm -f $(TESTLOG) $(TCLSH) $(srcdir)/tests/nsf-mongo.test -libdir . $(TESTFLAGS) $(TCLSH) $(srcdir)/tests/nsf-gridfs.test -libdir . $(TESTFLAGS) $(TCLSH) $(srcdir)/tests/nx-bi.test -libdir . $(TESTFLAGS) $(TCLSH) $(srcdir)/tests/nx-mongo.test -libdir . $(TESTFLAGS) $(TCLSH) $(srcdir)/tests/nx-reference-one.test -libdir . $(TESTFLAGS) $(TCLSH) $(srcdir)/tests/nx-reference-many.test -libdir . $(TESTFLAGS) $(TCLSH) $(srcdir)/tests/nx-serialize.test -libdir . $(TESTFLAGS) $(TCLSH) $(srcdir)/tests/nx-rep.test -libdir . $(TESTFLAGS) $(TCLSH) $(srcdir)/../../tests/summary.tcl -title MongoDB -libdir . $(TESTFLAGS) shell: binaries libraries @$(TCLSH) $(SCRIPT) gdb: $(TCLSH_ENV) gdb $(TCLSH_PROG) $(SCRIPT) depend: #======================================================================== # $(PKG_LIB_FILE) should be listed as part of the BINARIES variable # mentioned above. That will ensure that this target is built when you # run "make binaries". # # The $(PKG_OBJECTS) objects are created and linked into the final # library. In most cases these object files will correspond to the # source files above. #======================================================================== $(PKG_LIB_FILE): $(PKG_OBJECTS) -rm -f $(PKG_LIB_FILE) ${MAKE_LIB} $(RANLIB) $(PKG_LIB_FILE) $(PKG_STUB_LIB_FILE): $(PKG_STUB_OBJECTS) -rm -f $(PKG_STUB_LIB_FILE) ${MAKE_STUB_LIB} $(RANLIB_STUB) $(PKG_STUB_LIB_FILE) #======================================================================== # We need to enumerate the list of .c to .o lines here. # # In the following lines, $(srcdir) refers to the toplevel directory # containing your extension. If your sources are in a subdirectory, # you will have to modify the paths to reflect this: # # sample.$(OBJEXT): $(srcdir)/generic/sample.c # $(COMPILE) -c `@CYGPATH@ $(srcdir)/generic/sample.c` -o $@ # # Setting the VPATH variable to a list of paths will cause the makefile # to look into these paths when resolving .c to .obj dependencies. # As necessary, add $(srcdir):$(srcdir)/compat:.... #======================================================================== VPATH = $(srcdir):$(srcdir)/generic:$(srcdir)/unix:$(srcdir)/win .c.@OBJEXT@: $(COMPILE) -c `@CYGPATH@ $<` -o $@ nsfmongo.@OBJEXT@: nsfmongo.c mongoAPI.h mongoAPI.h: ../../generic/gentclAPI.tcl mongoAPI.decls $(TCLSH) ../../generic/gentclAPI.tcl mongoAPI.decls > mongoAPI.h #======================================================================== # Create the pkgIndex.tcl file. # It is usually easiest to let Tcl do this for you with pkg_mkIndex, but # you may find that you need to customize the package. If so, either # modify the -hand version, or create a pkgIndex.tcl.in file and have # the configure script output the pkgIndex.tcl by editing configure.in. #======================================================================== #pkgIndex.tcl: # ( echo pkg_mkIndex . $(PKG_LIB_FILE) \; exit; ) | $(TCLSH) pkgIndex.tcl: (echo 'package ifneeded nsf::mongo $(PACKAGE_VERSION) \ [list load [file join $$dir $(PKG_LIB_FILE)]]'\ ) > pkgIndex.tcl (echo 'package ifneeded nx::mongo 0.2 \ [list source [file join $$dir nx-mongo.tcl]]'\ ) >> pkgIndex.tcl #======================================================================== # Distribution creation # You may need to tweak this target to make it work correctly. #======================================================================== #COMPRESS = tar cvf $(PKG_DIR).tar $(PKG_DIR); compress $(PKG_DIR).tar COMPRESS = gtar zcvf $(PKG_DIR).tar.gz $(PKG_DIR) DIST_ROOT = /tmp/dist DIST_DIR = $(DIST_ROOT)/$(PKG_DIR) dist-clean: rm -rf $(DIST_DIR) $(DIST_ROOT)/$(PKG_DIR).tar.* dist: dist-clean mkdir -p $(DIST_DIR) cp -p $(srcdir)/ChangeLog $(srcdir)/README* $(srcdir)/license* \ $(srcdir)/aclocal.m4 $(srcdir)/configure $(srcdir)/*.in \ $(DIST_DIR)/ chmod 664 $(DIST_DIR)/Makefile.in $(DIST_DIR)/aclocal.m4 chmod 775 $(DIST_DIR)/configure $(DIST_DIR)/configure.in cp -p $(srcdir)/*.[ch] $(DIST_DIR)/ mkdir $(DIST_DIR)/tclconfig cp $(srcdir)/tclconfig/install-sh $(srcdir)/tclconfig/tcl.m4 \ $(DIST_DIR)/tclconfig/ chmod 664 $(DIST_DIR)/tclconfig/tcl.m4 chmod +x $(DIST_DIR)/tclconfig/install-sh list='demos doc generic library mac tests unix win'; \ for p in $$list; do \ if test -d $(srcdir)/$$p ; then \ mkdir $(DIST_DIR)/$$p; \ cp -p $(srcdir)/$$p/*.* $(DIST_DIR)/$$p/; \ fi; \ done (cd $(DIST_ROOT); $(COMPRESS);) #======================================================================== # End of user-definable section #======================================================================== #======================================================================== # Don't modify the file to clean here. Instead, set the "CLEANFILES" # variable in configure.in #======================================================================== clean: -test -z "$(BINARIES)" || rm -f $(BINARIES) -rm -f *.$(OBJEXT) core *.core -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean: clean -rm -f *.tab.c -rm -f $(CONFIG_CLEAN_FILES) -rm -f config.cache config.log config.status #======================================================================== # Install binary object libraries. On Windows this includes both .dll and # .lib files. Because the .lib files are not explicitly listed anywhere, # we need to deduce their existence from the .dll file of the same name. # Library files go into the lib directory. # In addition, this will generate the pkgIndex.tcl # file in the install location (assuming it can find a usable tclsh shell) # # You should not have to modify this target. #======================================================================== install-lib-binaries: @mkdir -p $(DESTDIR)$(pkglibdir) @list='$(lib_BINARIES)'; for p in $$list; do \ if test -f $$p; then \ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p"; \ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p; \ stub=`echo $$p|sed -e "s/.*\(stub\).*/\1/"`; \ if test "x$$stub" = "xstub"; then \ echo " $(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p"; \ $(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p; \ else \ echo " $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p"; \ $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p; \ fi; \ ext=`echo $$p|sed -e "s/.*\.//"`; \ if test "x$$ext" = "xdll"; then \ lib=`basename $$p|sed -e 's/.[^.]*$$//'`.lib; \ if test -f $$lib; then \ echo " $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib"; \ $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib; \ fi; \ fi; \ fi; \ done @list='$(PKG_TCL_SOURCES)'; for p in $$list; do \ if test -f $(srcdir)/$$p; then \ destp=`basename $$p`; \ echo " Install $$destp $(DESTDIR)$(pkglibdir)/$$destp"; \ $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkglibdir)/$$destp; \ fi; \ done #======================================================================== # Install binary executables (e.g. .exe files and dependent .dll files) # This is for files that must go in the bin directory (located next to # wish and tclsh), like dependent .dll files on Windows. # # You should not have to modify this target, except to define bin_BINARIES # above if necessary. #======================================================================== install-bin-binaries: @mkdir -p $(DESTDIR)$(bindir) @list='$(bin_BINARIES)'; for p in $$list; do \ if test -f $$p; then \ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p"; \ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p; \ fi; \ done .SUFFIXES: .c .$(OBJEXT) Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status cd $(top_builddir) \ && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status uninstall-binaries: list='$(lib_BINARIES)'; for p in $$list; do \ rm -f $(DESTDIR)$(pkglibdir)/$$p; \ done list='$(PKG_TCL_SOURCES)'; for p in $$list; do \ p=`basename $$p`; \ rm -f $(DESTDIR)$(pkglibdir)/$$p; \ done list='$(bin_BINARIES)'; for p in $$list; do \ rm -f $(DESTDIR)$(bindir)/$$p; \ done .PHONY: all binaries clean depend distclean doc install libraries test # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: library/mongodb/README000066400000000000000000000066231242365656200150510ustar00rootroot00000000000000 Interface between mongoDB and the Next Scripting Framework Ingredients: https://github.com/mongodb/mongo https://github.com/mongodb/mongo-c-driver The current version is tested with - Tcl 8.5 and Tcl 8.6 - MongoDB v2.6.5 (released Aug 10, 2014) - mongodb-c-driver 1.0.2 (released Oct 9, 2014) Follow the following steps to get MongoDB up and running and to compile the MongoDB driver for nx: - Compile or obtain mongodb (the database). - Compile or obtain the mongo-c-driver (client interface) cd /usr/local/src git clone https://github.com/mongodb/mongo-c-driver cd mongo-c-driver sh autogen.sh make sudo make install If you experience errors during autogen on debian, you might have to apt-get install libtool If configure complains about not finding bson, you might have to do export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig Assume the following installation directories - Tcl: /usr/local/ns/lib/ - mongo-c-driver: /usr/local/src/mongo-c-driver/ configure the mongodb nsf interface via the following command in the directory nsf*/library/mongodb/ You will probably have to adjust the paths. ./configure --with-tcl=/usr/local/ns/lib/ --prefix=/usr/local/ns --with-nsf=../../ \ --with-mongoc=/usr/local/src/mongo-c-driver/src/mongoc/,/usr/local/src/mongo-c-driver/.libs \ --with-bson=/usr/local/src/mongo-c-driver/src/libbson/src/bson \ --enable-threads --enable-symbols In order to run the sample script, * first start the mongdb (eg. mongod) * go to your nsf source directory * make sure, the c-driver libraries are on the library path (assuming the the c-driver was installed in /usr/local/lib) export DYLD_LIBRARY_PATH=/usr/local/lib:`pwd` * run ./nxsh library/mongodb/tests/nsf-mongo.test The script is using the low level interface (nsf::mongo) and has a few insert, query and delete statements, some of these are commented out. * run ./nxsh library/mongodb/example-nx-mongo.tcl This example script is using the higher level object oriented interface for nx (nx::mongo). After running this script, you should could check the content in MongoDB: % mongo MongoDB shell version: 2.6.5 connecting to: test > use tutorial switched to db tutorial > db.persons.find(); { "_id" : ObjectId("530c6e4649686ad16e261f81"), "name" : "Gustaf", "projects" : "nsf", "age" : 53 } { "_id" : ObjectId("530c6e4649686ad16e261f82"), "name" : "Stefan", "projects" : "nsf" } { "_id" : ObjectId("530c6e4649686ad16e261f83"), "name" : "Victor", "a" : [ "x", "y" ], "age" : 31 } { "_id" : ObjectId("530c6e4649686ad16e261f84"), "name" : "Joe", "projects" : "abc", "age" : 23, "classes" : [ DBRef("courses", ObjectId("100000000000000000000000")) ] } { "_id" : ObjectId("530c6e4649686ad16e261f85"), "name" : "Franz", "info" : { "x" : 203, "y" : 102 }, "age" : 29, "projects" : "gtat" } { "_id" : ObjectId("530c6e4649686ad16e261f86"), "name" : "Selim", "ts" : Timestamp(1302945037, 1), "d" : ISODate("2011-04-16T09:53:39.279Z") } > * Further sample-scripts: ./nxsh library/mongodb/tests/nx-bi.test ./nxsh library/mongodb/tests/nx-reference-one.test ./nxsh library/mongodb/tests/nx-reference-many.test ./nxsh library/mongodb/tests/nx-rep.test ./nxsh library/mongodb/tests/nx-serialize.test ./nxsh library/mongodb/tests/nsf-gridfs.test -gustaf neumann library/mongodb/aclocal.m4000066400000000000000000000012621242365656200160230ustar00rootroot00000000000000# generated automatically by aclocal 1.14.1 -*- Autoconf -*- # Copyright (C) 1996-2013 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_include([m4/tcl.m4]) library/mongodb/configure000077500000000000000000010374761242365656200161130ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for nsfmongo 0.3. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='nsfmongo' PACKAGE_TARNAME='nsfmongo' PACKAGE_VERSION='0.3' PACKAGE_STRING='nsfmongo 0.3' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS TCLSH_PROG VC_MANIFEST_EMBED_EXE VC_MANIFEST_EMBED_DLL RANLIB_STUB MAKE_STUB_LIB MAKE_STATIC_LIB MAKE_SHARED_LIB MAKE_LIB TCL_DBGX LDFLAGS_DEFAULT CFLAGS_DEFAULT LD_LIBRARY_PATH_VAR SHLIB_CFLAGS SHLIB_LD_LIBS SHLIB_LD STLIB_LD CFLAGS_WARNING CFLAGS_OPTIMIZE CFLAGS_DEBUG RC CELIB_DIR AR SHARED_BUILD TCL_THREADS TCL_INCLUDES PKG_OBJECTS PKG_SOURCES MATH_LIBS EGREP GREP RANLIB SET_MAKE INSTALL_SCRIPT INSTALL_PROGRAM INSTALL_DATA INSTALL CPP TCL_SHLIB_LD_LIBS TCL_LD_FLAGS TCL_EXTRA_CFLAGS TCL_DEFS TCL_LIBS CLEANFILES OBJEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC TCL_STUB_LIB_SPEC TCL_STUB_LIB_FLAG TCL_STUB_LIB_FILE TCL_LIB_SPEC TCL_LIB_FLAG TCL_LIB_FILE TCL_SRC_DIR TCL_BIN_DIR TCL_PATCH_LEVEL TCL_VERSION PKG_CFLAGS PKG_LIBS PKG_INCLUDES PKG_HEADERS PKG_TCL_SOURCES PKG_STUB_OBJECTS PKG_STUB_SOURCES PKG_STUB_LIB_FILE PKG_LIB_FILE EXEEXT CYGPATH target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_mongoc with_bson with_nsf enable_development with_tcl with_tclinclude enable_threads enable_shared enable_64bit enable_64bit_vis enable_rpath enable_wince with_celib enable_symbols ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures nsfmongo 0.3 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/nsfmongo] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of nsfmongo 0.3:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-development build nsf with development support (intensive runtime checking, etc.; default: disabled) --enable-threads build with threads --enable-shared build and link with shared libraries (default: on) --enable-64bit enable 64bit support (default: off) --enable-64bit-vis enable 64bit Sparc VIS support (default: off) --disable-rpath disable rpath support (default: on) --enable-wince enable Win/CE support (where applicable) --enable-symbols build with debugging symbols (default: off) Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-mongoc=MONGOC_INCLUDE_DIR,MONGOC_LIB_DIR absolute path to mongo.h and optionally the path to the library, --without-mongoc disables build of the mongo interface --with-bson=BSON_INCLUDE_DIR,BSON_LIB_DIR absolute path to bson.h and optionally the path to the library, --without-bson disables build of the mongo interface --with-nsf=DIR_CONTAINING_NSFCONFIG_SH absolute path to nsfConfig.sh, --without-nsf disables, but this is pointless --with-tcl directory containing tcl configuration (tclConfig.sh) --with-tclinclude directory containing the public Tcl header files --with-celib=DIR use Windows/CE support library from DIR Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF nsfmongo configure 0.3 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by nsfmongo $as_me 0.3, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. # This will define a ${TEA_PLATFORM} variable = "unix" or "windows" # as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE. #-------------------------------------------------------------------- # TEA extensions pass this us the version of TEA they think they # are compatible with. TEA_VERSION="3.9" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for correct TEA configuration" >&5 $as_echo_n "checking for correct TEA configuration... " >&6; } if test x"${PACKAGE_NAME}" = x ; then as_fn_error $? " The PACKAGE_NAME variable must be defined by your TEA configure.in" "$LINENO" 5 fi if test x"3.9" = x ; then as_fn_error $? " TEA version not specified." "$LINENO" 5 elif test "3.9" != "${TEA_VERSION}" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: warning: requested TEA version \"3.9\", have \"${TEA_VERSION}\"" >&5 $as_echo "warning: requested TEA version \"3.9\", have \"${TEA_VERSION}\"" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok (TEA ${TEA_VERSION})" >&5 $as_echo "ok (TEA ${TEA_VERSION})" >&6; } fi # If the user did not set CFLAGS, set it now to keep macros # like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2". if test "${CFLAGS+set}" != "set" ; then CFLAGS="" fi case "`uname -s`" in *win32*|*WIN32*|*MINGW32_*) # Extract the first word of "cygpath", so it can be a program name with args. set dummy cygpath; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CYGPATH+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CYGPATH"; then ac_cv_prog_CYGPATH="$CYGPATH" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CYGPATH="cygpath -w" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_CYGPATH" && ac_cv_prog_CYGPATH="echo" fi fi CYGPATH=$ac_cv_prog_CYGPATH if test -n "$CYGPATH"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CYGPATH" >&5 $as_echo "$CYGPATH" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi EXEEXT=".exe" TEA_PLATFORM="windows" ;; *CYGWIN_*) CYGPATH=echo EXEEXT=".exe" # TEA_PLATFORM is determined later in LOAD_TCLCONFIG ;; *) CYGPATH=echo # Maybe we are cross-compiling.... case ${host_alias} in *mingw32*) EXEEXT=".exe" TEA_PLATFORM="windows" ;; *) EXEEXT="" TEA_PLATFORM="unix" ;; esac ;; esac # Check if exec_prefix is set. If not use fall back to prefix. # Note when adjusted, so that TEA_PREFIX can correct for this. # This is needed for recursive configures, since autoconf propagates # $prefix, but not $exec_prefix (doh!). if test x$exec_prefix = xNONE ; then exec_prefix_default=yes exec_prefix=$prefix fi { $as_echo "$as_me:${as_lineno-$LINENO}: configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}" >&5 $as_echo "$as_me: configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}" >&6;} # This package name must be replaced statically for AC_SUBST to work # Substitute STUB_LIB_FILE in case package creates a stub library too. # We AC_SUBST these here to ensure they are subst'ed, # in case the user doesn't call TEA_ADD_... ac_aux_dir= for ac_dir in ../../tclconfig "$srcdir"/../../tclconfig; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in ../../tclconfig \"$srcdir\"/../../tclconfig" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. #-------------------------------------------------------------------- # specify some extra flags #-------------------------------------------------------------------- # Check whether --with-mongoc was given. if test "${with_mongoc+set}" = set; then : withval=$with_mongoc; with_mongoc=$withval else with_mongoc=no fi # Check whether --with-bson was given. if test "${with_bson+set}" = set; then : withval=$with_bson; with_bson=$withval else with_bson=no fi # Check whether --with-nsf was given. if test "${with_nsf+set}" = set; then : withval=$with_nsf; with_nsf=$withval else as_fn_error $? "--with-nsf is required" "$LINENO" 5 fi # Check whether --enable-development was given. if test "${enable_development+set}" = set; then : enableval=$enable_development; enable_development=$enableval else enable_development=no fi #-------------------------------------------------------------------- # Load the tclConfig.sh file #-------------------------------------------------------------------- # # Ok, lets find the tcl configuration # First, look for one uninstalled. # the alternative search directory is invoked by --with-tcl # if test x"${no_tcl}" = x ; then # we reset no_tcl in case something fails here no_tcl=true # Check whether --with-tcl was given. if test "${with_tcl+set}" = set; then : withval=$with_tcl; with_tclconfig="${withval}" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl configuration" >&5 $as_echo_n "checking for Tcl configuration... " >&6; } if ${ac_cv_c_tclconfig+:} false; then : $as_echo_n "(cached) " >&6 else # First check to see if --with-tcl was specified. if test x"${with_tclconfig}" != x ; then case "${with_tclconfig}" in */tclConfig.sh ) if test -f "${with_tclconfig}"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself" >&5 $as_echo "$as_me: WARNING: --with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself" >&2;} with_tclconfig="`echo "${with_tclconfig}" | sed 's!/tclConfig\.sh$!!'`" fi ;; esac if test -f "${with_tclconfig}/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd "${with_tclconfig}"; pwd)`" else as_fn_error $? "${with_tclconfig} directory doesn't contain tclConfig.sh" "$LINENO" 5 fi fi # then check for a private Tcl installation if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ ../tcl \ `ls -dr ../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ../tcl[8-9].[0-9]* 2>/dev/null` \ ../../tcl \ `ls -dr ../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ../../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ../../tcl[8-9].[0-9]* 2>/dev/null` \ ../../../tcl \ `ls -dr ../../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ../../../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ../../../tcl[8-9].[0-9]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/unix; pwd)`" break fi done fi # on Darwin, check in Framework installation locations if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d ~/Library/Frameworks 2>/dev/null` \ `ls -d /Library/Frameworks 2>/dev/null` \ `ls -d /Network/Library/Frameworks 2>/dev/null` \ `ls -d /System/Library/Frameworks 2>/dev/null` \ ; do if test -f "$i/Tcl.framework/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/Tcl.framework; pwd)`" break fi done fi # TEA specific: on Windows, check in common installation locations if test "${TEA_PLATFORM}" = "windows" \ -a x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d C:/Tcl/lib 2>/dev/null` \ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \ ; do if test -f "$i/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i; pwd)`" break fi done fi # check in a few common install locations if test x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d ${libdir} 2>/dev/null` \ `ls -d ${exec_prefix}/lib 2>/dev/null` \ `ls -d ${prefix}/lib 2>/dev/null` \ `ls -d /usr/local/lib 2>/dev/null` \ `ls -d /usr/contrib/lib 2>/dev/null` \ `ls -d /usr/lib 2>/dev/null` \ `ls -d /usr/lib64 2>/dev/null` \ `ls -d /usr/lib/tcl8.6 2>/dev/null` \ `ls -d /usr/lib/tcl8.5 2>/dev/null` \ ; do if test -f "$i/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i; pwd)`" break fi done fi # check in a few other private locations if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ ${srcdir}/../tcl \ `ls -dr ${srcdir}/../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ${srcdir}/../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ${srcdir}/../tcl[8-9].[0-9]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/unix; pwd)`" break fi done fi fi if test x"${ac_cv_c_tclconfig}" = x ; then TCL_BIN_DIR="# no Tcl configs found" as_fn_error $? "Can't find Tcl configuration definitions. Use --with-tcl to specify a directory containing tclConfig.sh" "$LINENO" 5 else no_tcl= TCL_BIN_DIR="${ac_cv_c_tclconfig}" { $as_echo "$as_me:${as_lineno-$LINENO}: result: found ${TCL_BIN_DIR}/tclConfig.sh" >&5 $as_echo "found ${TCL_BIN_DIR}/tclConfig.sh" >&6; } fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for existence of ${TCL_BIN_DIR}/tclConfig.sh" >&5 $as_echo_n "checking for existence of ${TCL_BIN_DIR}/tclConfig.sh... " >&6; } if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: loading" >&5 $as_echo "loading" >&6; } . "${TCL_BIN_DIR}/tclConfig.sh" else { $as_echo "$as_me:${as_lineno-$LINENO}: result: could not find ${TCL_BIN_DIR}/tclConfig.sh" >&5 $as_echo "could not find ${TCL_BIN_DIR}/tclConfig.sh" >&6; } fi # eval is required to do the TCL_DBGX substitution eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\"" eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\"" # If the TCL_BIN_DIR is the build directory (not the install directory), # then set the common variable name to the value of the build variables. # For example, the variable TCL_LIB_SPEC will be set to the value # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC # instead of TCL_BUILD_LIB_SPEC since it will work with both an # installed and uninstalled version of Tcl. if test -f "${TCL_BIN_DIR}/Makefile" ; then TCL_LIB_SPEC="${TCL_BUILD_LIB_SPEC}" TCL_STUB_LIB_SPEC="${TCL_BUILD_STUB_LIB_SPEC}" TCL_STUB_LIB_PATH="${TCL_BUILD_STUB_LIB_PATH}" elif test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use the libraries # from the framework at the given location so that linking works # against Tcl.framework installed in an arbitrary location. case ${TCL_DEFS} in *TCL_FRAMEWORK*) if test -f "${TCL_BIN_DIR}/${TCL_LIB_FILE}"; then for i in "`cd "${TCL_BIN_DIR}"; pwd`" \ "`cd "${TCL_BIN_DIR}"/../..; pwd`"; do if test "`basename "$i"`" = "${TCL_LIB_FILE}.framework"; then TCL_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TCL_LIB_FILE}" break fi done fi if test -f "${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"; then TCL_STUB_LIB_SPEC="-L`echo "${TCL_BIN_DIR}" | sed -e 's/ /\\\\ /g'` ${TCL_STUB_LIB_FLAG}" TCL_STUB_LIB_PATH="${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}" fi ;; esac fi # eval is required to do the TCL_DBGX substitution eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\"" eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\"" eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\"" eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\"" { $as_echo "$as_me:${as_lineno-$LINENO}: checking platform" >&5 $as_echo_n "checking platform... " >&6; } hold_cc=$CC; CC="$TCL_CC" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifdef _WIN32 #error win32 #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : TEA_PLATFORM="unix" else TEA_PLATFORM="windows" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CC=$hold_cc { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEA_PLATFORM" >&5 $as_echo "$TEA_PLATFORM" >&6; } # The BUILD_$pkg is to define the correct extern storage class # handling when making this package cat >>confdefs.h <<_ACEOF #define BUILD_${PACKAGE_NAME} /**/ _ACEOF # Do this here as we have fully defined TEA_PLATFORM now if test "${TEA_PLATFORM}" = "windows" ; then EXEEXT=".exe" CLEANFILES="$CLEANFILES *.lib *.dll *.pdb *.exp" fi # TEA specific: #-------------------------------------------------------------------- # Load the tkConfig.sh file if necessary (Tk extension) #-------------------------------------------------------------------- #TEA_PATH_TKCONFIG #TEA_LOAD_TKCONFIG #----------------------------------------------------------------------- # Handle the --prefix=... option by defaulting to what Tcl gave. # Must be called after TEA_LOAD_TCLCONFIG and before TEA_SETUP_COMPILER. #----------------------------------------------------------------------- if test "${prefix}" = "NONE"; then prefix_default=yes if test x"${TCL_PREFIX}" != x; then { $as_echo "$as_me:${as_lineno-$LINENO}: --prefix defaulting to TCL_PREFIX ${TCL_PREFIX}" >&5 $as_echo "$as_me: --prefix defaulting to TCL_PREFIX ${TCL_PREFIX}" >&6;} prefix=${TCL_PREFIX} else { $as_echo "$as_me:${as_lineno-$LINENO}: --prefix defaulting to /usr/local" >&5 $as_echo "$as_me: --prefix defaulting to /usr/local" >&6;} prefix=/usr/local fi fi if test "${exec_prefix}" = "NONE" -a x"${prefix_default}" = x"yes" \ -o x"${exec_prefix_default}" = x"yes" ; then if test x"${TCL_EXEC_PREFIX}" != x; then { $as_echo "$as_me:${as_lineno-$LINENO}: --exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}" >&5 $as_echo "$as_me: --exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}" >&6;} exec_prefix=${TCL_EXEC_PREFIX} else { $as_echo "$as_me:${as_lineno-$LINENO}: --exec-prefix defaulting to ${prefix}" >&5 $as_echo "$as_me: --exec-prefix defaulting to ${prefix}" >&6;} exec_prefix=$prefix fi fi #----------------------------------------------------------------------- # Standard compiler checks. # This sets up CC by using the CC env var, or looks for gcc otherwise. # This also calls AC_PROG_CC, AC_PROG_INSTALL and a few others to create # the basic setup necessary to compile executables. #----------------------------------------------------------------------- # Don't put any macros that use the compiler (e.g. AC_TRY_COMPILE) # in this macro, they need to go into TEA_SETUP_COMPILER instead. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu INSTALL="\$(SHELL) \$(srcdir)/tclconfig/install-sh -c" INSTALL_DATA="\${INSTALL} -m 644" INSTALL_PROGRAM="\${INSTALL}" INSTALL_SCRIPT="\${INSTALL}" #-------------------------------------------------------------------- # Checks to see if the make program sets the $MAKE variable. #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi #-------------------------------------------------------------------- # Find ranlib #-------------------------------------------------------------------- if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi #-------------------------------------------------------------------- # Determines the correct binary file extension (.o, .obj, .exe etc.) #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # Any macros that use the compiler (e.g. AC_TRY_COMPILE) have to go here. #------------------------------------------------------------------------ # If we're using GCC, see if the compiler understands -pipe. If so, use it. # It makes compiling go faster. (This is only a performance feature.) #------------------------------------------------------------------------ if test -z "$no_pipe" -a -n "$GCC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the compiler understands -pipe" >&5 $as_echo_n "checking if the compiler understands -pipe... " >&6; } if ${tcl_cv_cc_pipe+:} false; then : $as_echo_n "(cached) " >&6 else hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -pipe" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_cc_pipe=yes else tcl_cv_cc_pipe=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$hold_cflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_pipe" >&5 $as_echo "$tcl_cv_cc_pipe" >&6; } if test $tcl_cv_cc_pipe = yes; then CFLAGS="$CFLAGS -pipe" fi fi #-------------------------------------------------------------------- # Common compiler flag setup #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac if test "${TEA_PLATFORM}" = "unix" ; then #-------------------------------------------------------------------- # On a few very rare systems, all of the libm.a stuff is # already in libc.a. Set compiler flags accordingly. # Also, Linux requires the "ieee" library for math to work # right (and it must appear before "-lm"). #-------------------------------------------------------------------- ac_fn_c_check_func "$LINENO" "sin" "ac_cv_func_sin" if test "x$ac_cv_func_sin" = xyes; then : MATH_LIBS="" else MATH_LIBS="-lm" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lieee" >&5 $as_echo_n "checking for main in -lieee... " >&6; } if ${ac_cv_lib_ieee_main+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lieee $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ieee_main=yes else ac_cv_lib_ieee_main=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ieee_main" >&5 $as_echo "$ac_cv_lib_ieee_main" >&6; } if test "x$ac_cv_lib_ieee_main" = xyes; then : MATH_LIBS="-lieee $MATH_LIBS" fi #-------------------------------------------------------------------- # Interactive UNIX requires -linet instead of -lsocket, plus it # needs net/errno.h to define the socket-related error codes. #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -linet" >&5 $as_echo_n "checking for main in -linet... " >&6; } if ${ac_cv_lib_inet_main+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-linet $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_inet_main=yes else ac_cv_lib_inet_main=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_inet_main" >&5 $as_echo "$ac_cv_lib_inet_main" >&6; } if test "x$ac_cv_lib_inet_main" = xyes; then : LIBS="$LIBS -linet" fi ac_fn_c_check_header_mongrel "$LINENO" "net/errno.h" "ac_cv_header_net_errno_h" "$ac_includes_default" if test "x$ac_cv_header_net_errno_h" = xyes; then : $as_echo "#define HAVE_NET_ERRNO_H 1" >>confdefs.h fi #-------------------------------------------------------------------- # Check for the existence of the -lsocket and -lnsl libraries. # The order here is important, so that they end up in the right # order in the command line generated by make. Here are some # special considerations: # 1. Use "connect" and "accept" to check for -lsocket, and # "gethostbyname" to check for -lnsl. # 2. Use each function name only once: can't redo a check because # autoconf caches the results of the last check and won't redo it. # 3. Use -lnsl and -lsocket only if they supply procedures that # aren't already present in the normal libraries. This is because # IRIX 5.2 has libraries, but they aren't needed and they're # bogus: they goof up name resolution if used. # 4. On some SVR4 systems, can't use -lsocket without -lnsl too. # To get around this problem, check for both libraries together # if -lsocket doesn't work by itself. #-------------------------------------------------------------------- tcl_checkBoth=0 ac_fn_c_check_func "$LINENO" "connect" "ac_cv_func_connect" if test "x$ac_cv_func_connect" = xyes; then : tcl_checkSocket=0 else tcl_checkSocket=1 fi if test "$tcl_checkSocket" = 1; then ac_fn_c_check_func "$LINENO" "setsockopt" "ac_cv_func_setsockopt" if test "x$ac_cv_func_setsockopt" = xyes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setsockopt in -lsocket" >&5 $as_echo_n "checking for setsockopt in -lsocket... " >&6; } if ${ac_cv_lib_socket_setsockopt+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char setsockopt (); int main () { return setsockopt (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_socket_setsockopt=yes else ac_cv_lib_socket_setsockopt=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_setsockopt" >&5 $as_echo "$ac_cv_lib_socket_setsockopt" >&6; } if test "x$ac_cv_lib_socket_setsockopt" = xyes; then : LIBS="$LIBS -lsocket" else tcl_checkBoth=1 fi fi fi if test "$tcl_checkBoth" = 1; then tk_oldLibs=$LIBS LIBS="$LIBS -lsocket -lnsl" ac_fn_c_check_func "$LINENO" "accept" "ac_cv_func_accept" if test "x$ac_cv_func_accept" = xyes; then : tcl_checkNsl=0 else LIBS=$tk_oldLibs fi fi ac_fn_c_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname" if test "x$ac_cv_func_gethostbyname" = xyes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5 $as_echo_n "checking for gethostbyname in -lnsl... " >&6; } if ${ac_cv_lib_nsl_gethostbyname+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lnsl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char gethostbyname (); int main () { return gethostbyname (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_nsl_gethostbyname=yes else ac_cv_lib_nsl_gethostbyname=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_gethostbyname" >&5 $as_echo "$ac_cv_lib_nsl_gethostbyname" >&6; } if test "x$ac_cv_lib_nsl_gethostbyname" = xyes; then : LIBS="$LIBS -lnsl" fi fi # TEA specific: Don't perform the eval of the libraries here because # DL_LIBS won't be set until we call TEA_CONFIG_CFLAGS TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}' { $as_echo "$as_me:${as_lineno-$LINENO}: checking dirent.h" >&5 $as_echo_n "checking dirent.h... " >&6; } if ${tcl_cv_dirent_h+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #ifndef _POSIX_SOURCE # ifdef __Lynx__ /* * Generate compilation error to make the test fail: Lynx headers * are only valid if really in the POSIX environment. */ missing_procedure(); # endif #endif DIR *d; struct dirent *entryPtr; char *p; d = opendir("foobar"); entryPtr = readdir(d); p = entryPtr->d_name; closedir(d); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_dirent_h=yes else tcl_cv_dirent_h=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_dirent_h" >&5 $as_echo "$tcl_cv_dirent_h" >&6; } if test $tcl_cv_dirent_h = no; then $as_echo "#define NO_DIRENT_H 1" >>confdefs.h fi # TEA specific: ac_fn_c_check_header_mongrel "$LINENO" "errno.h" "ac_cv_header_errno_h" "$ac_includes_default" if test "x$ac_cv_header_errno_h" = xyes; then : else $as_echo "#define NO_ERRNO_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "float.h" "ac_cv_header_float_h" "$ac_includes_default" if test "x$ac_cv_header_float_h" = xyes; then : else $as_echo "#define NO_FLOAT_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "values.h" "ac_cv_header_values_h" "$ac_includes_default" if test "x$ac_cv_header_values_h" = xyes; then : else $as_echo "#define NO_VALUES_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default" if test "x$ac_cv_header_limits_h" = xyes; then : $as_echo "#define HAVE_LIMITS_H 1" >>confdefs.h else $as_echo "#define NO_LIMITS_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" if test "x$ac_cv_header_stdlib_h" = xyes; then : tcl_ok=1 else tcl_ok=0 fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "strtol" >/dev/null 2>&1; then : else tcl_ok=0 fi rm -f conftest* cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "strtoul" >/dev/null 2>&1; then : else tcl_ok=0 fi rm -f conftest* cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "strtod" >/dev/null 2>&1; then : else tcl_ok=0 fi rm -f conftest* if test $tcl_ok = 0; then $as_echo "#define NO_STDLIB_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default" if test "x$ac_cv_header_string_h" = xyes; then : tcl_ok=1 else tcl_ok=0 fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "strstr" >/dev/null 2>&1; then : else tcl_ok=0 fi rm -f conftest* cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "strerror" >/dev/null 2>&1; then : else tcl_ok=0 fi rm -f conftest* # See also memmove check below for a place where NO_STRING_H can be # set and why. if test $tcl_ok = 0; then $as_echo "#define NO_STRING_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "sys/wait.h" "ac_cv_header_sys_wait_h" "$ac_includes_default" if test "x$ac_cv_header_sys_wait_h" = xyes; then : else $as_echo "#define NO_SYS_WAIT_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default" if test "x$ac_cv_header_dlfcn_h" = xyes; then : else $as_echo "#define NO_DLFCN_H 1" >>confdefs.h fi # OS/390 lacks sys/param.h (and doesn't need it, by chance). for ac_header in sys/param.h do : ac_fn_c_check_header_mongrel "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default" if test "x$ac_cv_header_sys_param_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SYS_PARAM_H 1 _ACEOF fi done # Let the user call this, because if it triggers, they will # need a compat/strtod.c that is correct. Users can also # use Tcl_GetDouble(FromObj) instead. #TEA_BUGGY_STRTOD fi #----------------------------------------------------------------------- # __CHANGE__ # Specify the C source files to compile in TEA_ADD_SOURCES, # public headers that need to be installed in TEA_ADD_HEADERS, # stub library C source files to compile in TEA_ADD_STUB_SOURCES, # and runtime Tcl library files in TEA_ADD_TCL_SOURCES. # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS # and PKG_TCL_SOURCES. #----------------------------------------------------------------------- if test ! "${with_mongoc}" = no; then MONGOC_INC_DIR="`echo $with_mongoc |cut -f1 -d,`" MONGOC_LIB_DIR="`echo $with_mongoc |cut -f2 -d, -s`" fi if test ! "${with_bson}" = no; then BSON_INC_DIR="`echo $with_bson |cut -f1 -d,`" BSON_LIB_DIR="`echo $with_bson |cut -f2 -d, -s`" fi mongo_h_ok=1 if test -z "$MONGOC_INC_DIR" ; then mongo_h_ok=0 MONGO_INC_SPEC="" fi if test -z "$BSON_INC_DIR" ; then mongo_h_ok=0 MONGO_INC_SPEC="" fi if test "${mongo_h_ok}" = "1" ; then MONGO_INC_SPEC="-I${MONGOC_INC_DIR} -I${BSON_INC_DIR}" echo "Checking ${MONGOC_INC_DIR}/mongoc.h" if test ! -f "${MONGOC_INC_DIR}/mongoc.h" ; then mongo_h_ok=0 fi echo "Checking ${BSON_INC_DIR}/bson.h" if test ! -f "${BSON_INC_DIR}/bson.h" ; then mongo_h_ok=0 fi fi if test "${mongo_h_ok}" = "0" ; then as_fn_error $? " Could not locate bson.h and mongo.h on your machine to build the nsf mongo interface. " "$LINENO" 5 fi if test -z "${MONGOC_LIB_DIR}" ; then MONGO_LIB_SPEC="" else MONGO_LIB_SPEC="-L${MONGOC_LIB_DIR}" fi if test ! -z "${BSON_LIB_DIR}" ; then MONGO_LIB_SPEC="${MONGO_LIB_SPEC} -L${BSON_LIB_DIR}" fi #echo "MONGO include spec = '${MONGO_INC_SPEC}'" #echo "MONGO lib spec = '${MONGO_LIB_SPEC}'" #-------------------------------------------------------------------- # Load the nsfConfig.sh file #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: Reading file ${with_nsf}/nsfConfig.sh" >&5 $as_echo "$as_me: Reading file ${with_nsf}/nsfConfig.sh" >&6;} source ${with_nsf}/nsfConfig.sh vars="nsfmongo.c" for i in $vars; do case $i in \$*) # allow $-var names PKG_SOURCES="$PKG_SOURCES $i" PKG_OBJECTS="$PKG_OBJECTS $i" ;; *) # check for existence - allows for generic/win/unix VPATH # To add more dirs here (like 'src'), you have to update VPATH # in Makefile.in as well if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \ -a ! -f "${srcdir}/macosx/$i" \ ; then as_fn_error $? "could not find source file '$i'" "$LINENO" 5 fi PKG_SOURCES="$PKG_SOURCES $i" # this assumes it is in a VPATH dir i=`basename $i` # handle user calling this before or after TEA_SETUP_COMPILER if test x"${OBJEXT}" != x ; then j="`echo $i | sed -e 's/\.[^.]*$//'`.${OBJEXT}" else j="`echo $i | sed -e 's/\.[^.]*$//'`.\${OBJEXT}" fi PKG_OBJECTS="$PKG_OBJECTS $j" ;; esac done vars="" for i in $vars; do # check for existence, be strict because it is installed if test ! -f "${srcdir}/$i" ; then as_fn_error $? "could not find header file '${srcdir}/$i'" "$LINENO" 5 fi PKG_HEADERS="$PKG_HEADERS $i" done vars="-I${with_nsf}/generic ${NSF_BUILD_INCLUDE_SPEC} ${MONGO_INC_SPEC}" for i in $vars; do PKG_INCLUDES="$PKG_INCLUDES $i" done #TEA_ADD_LIBS([$NSF_BUILD_STUB_LIB_SPEC $MONGO_LIB_SPEC -Wl,-rpath,${MONGO_LIB_DIR} -L${MONGO_LIB_DIR} -lmongoc -lbson]) #TEA_ADD_LIBS([$NSF_BUILD_STUB_LIB_SPEC $MONGO_LIB_SPEC -L${MONGO_LIB_DIR} -lmongoc -lbson]) vars="$NSF_BUILD_STUB_LIB_SPEC $MONGO_LIB_SPEC -lmongoc-1.0 -lbson-1.0" for i in $vars; do if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then # Convert foo.lib to -lfoo for GCC. No-op if not *.lib i=`echo "$i" | sed -e 's/^\([^-].*\)\.lib$/-l\1/i'` fi PKG_LIBS="$PKG_LIBS $i" done PKG_CFLAGS="$PKG_CFLAGS " vars="" for i in $vars; do # check for existence - allows for generic/win/unix VPATH if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \ -a ! -f "${srcdir}/macosx/$i" \ ; then as_fn_error $? "could not find stub source file '$i'" "$LINENO" 5 fi PKG_STUB_SOURCES="$PKG_STUB_SOURCES $i" # this assumes it is in a VPATH dir i=`basename $i` # handle user calling this before or after TEA_SETUP_COMPILER if test x"${OBJEXT}" != x ; then j="`echo $i | sed -e 's/\.[^.]*$//'`.${OBJEXT}" else j="`echo $i | sed -e 's/\.[^.]*$//'`.\${OBJEXT}" fi PKG_STUB_OBJECTS="$PKG_STUB_OBJECTS $j" done vars="" for i in $vars; do # check for existence, be strict because it is installed if test ! -f "${srcdir}/$i" ; then as_fn_error $? "could not find tcl source file '${srcdir}/$i'" "$LINENO" 5 fi PKG_TCL_SOURCES="$PKG_TCL_SOURCES $i" done #-------------------------------------------------------------------- # __CHANGE__ # A few miscellaneous platform-specific items: # # Define a special symbol for Windows (BUILD_sample in this case) so # that we create the export library with the dll. # # Windows creates a few extra files that need to be cleaned up. # You can add more files to clean if your extension creates any extra # files. # # TEA_ADD_* any platform specific compiler/build info here. #-------------------------------------------------------------------- if test "${TEA_PLATFORM}" = "windows" ; then CLEANFILES="pkgIndex.tcl *.lib *.dll *.exp *.ilk *.pdb vc*.pch" #TEA_ADD_SOURCES([win/winFile.c]) #TEA_ADD_INCLUDES([-I\"$(${CYGPATH} ${srcdir}/win)\"]) else CLEANFILES="pkgIndex.tcl" #TEA_ADD_SOURCES([unix/unixFile.c]) #TEA_ADD_LIBS([-lsuperfly]) fi #-------------------------------------------------------------------- # __CHANGE__ # Choose which headers you need. Extension authors should try very # hard to only rely on the Tcl public header files. Internal headers # contain private data structures and are subject to change without # notice. # This MUST be called after TEA_LOAD_TCLCONFIG / TEA_LOAD_TKCONFIG #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl public headers" >&5 $as_echo_n "checking for Tcl public headers... " >&6; } # Check whether --with-tclinclude was given. if test "${with_tclinclude+set}" = set; then : withval=$with_tclinclude; with_tclinclude=${withval} fi if ${ac_cv_c_tclh+:} false; then : $as_echo_n "(cached) " >&6 else # Use the value from --with-tclinclude, if it was given if test x"${with_tclinclude}" != x ; then if test -f "${with_tclinclude}/tcl.h" ; then ac_cv_c_tclh=${with_tclinclude} else as_fn_error $? "${with_tclinclude} directory does not contain tcl.h" "$LINENO" 5 fi else list="" if test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use # the framework's Headers directory case ${TCL_DEFS} in *TCL_FRAMEWORK*) list="`ls -d ${TCL_BIN_DIR}/Headers 2>/dev/null`" ;; esac fi # Look in the source dir only if Tcl is not installed, # and in that situation, look there before installed locations. if test -f "${TCL_BIN_DIR}/Makefile" ; then list="$list `ls -d ${TCL_SRC_DIR}/generic 2>/dev/null`" fi # Check order: pkg --prefix location, Tcl's --prefix location, # relative to directory of tclConfig.sh. eval "temp_includedir=${includedir}" list="$list \ `ls -d ${temp_includedir} 2>/dev/null` \ `ls -d ${TCL_PREFIX}/include 2>/dev/null` \ `ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`" if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then list="$list /usr/local/include /usr/include" if test x"${TCL_INCLUDE_SPEC}" != x ; then d=`echo "${TCL_INCLUDE_SPEC}" | sed -e 's/^-I//'` list="$list `ls -d ${d} 2>/dev/null`" fi fi for i in $list ; do if test -f "$i/tcl.h" ; then ac_cv_c_tclh=$i break fi done fi fi # Print a message based on how we determined the include path if test x"${ac_cv_c_tclh}" = x ; then as_fn_error $? "tcl.h not found. Please specify its location with --with-tclinclude" "$LINENO" 5 else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${ac_cv_c_tclh}" >&5 $as_echo "${ac_cv_c_tclh}" >&6; } fi # Convert to a native path and substitute into the output files. INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tclh}` TCL_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\" #TEA_PRIVATE_TCL_HEADERS #TEA_PUBLIC_TK_HEADERS #TEA_PRIVATE_TK_HEADERS #TEA_PATH_X #-------------------------------------------------------------------- # Check whether --enable-threads or --disable-threads was given. #-------------------------------------------------------------------- # Check whether --enable-threads was given. if test "${enable_threads+set}" = set; then : enableval=$enable_threads; tcl_ok=$enableval else tcl_ok=yes fi if test "${enable_threads+set}" = set; then enableval="$enable_threads" tcl_ok=$enableval else tcl_ok=yes fi if test "$tcl_ok" = "yes" -o "${TCL_THREADS}" = 1; then TCL_THREADS=1 if test "${TEA_PLATFORM}" != "windows" ; then # We are always OK on Windows, so check what this platform wants: # USE_THREAD_ALLOC tells us to try the special thread-based # allocator that significantly reduces lock contention $as_echo "#define USE_THREAD_ALLOC 1" >>confdefs.h $as_echo "#define _REENTRANT 1" >>confdefs.h if test "`uname -s`" = "SunOS" ; then $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h fi $as_echo "#define _THREAD_SAFE 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lpthread" >&5 $as_echo_n "checking for pthread_mutex_init in -lpthread... " >&6; } if ${ac_cv_lib_pthread_pthread_mutex_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_mutex_init (); int main () { return pthread_mutex_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pthread_pthread_mutex_init=yes else ac_cv_lib_pthread_pthread_mutex_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_mutex_init" >&5 $as_echo "$ac_cv_lib_pthread_pthread_mutex_init" >&6; } if test "x$ac_cv_lib_pthread_pthread_mutex_init" = xyes; then : tcl_ok=yes else tcl_ok=no fi if test "$tcl_ok" = "no"; then # Check a little harder for __pthread_mutex_init in the same # library, as some systems hide it there until pthread.h is # defined. We could alternatively do an AC_TRY_COMPILE with # pthread.h, but that will work with libpthread really doesn't # exist, like AIX 4.2. [Bug: 4359] { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __pthread_mutex_init in -lpthread" >&5 $as_echo_n "checking for __pthread_mutex_init in -lpthread... " >&6; } if ${ac_cv_lib_pthread___pthread_mutex_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char __pthread_mutex_init (); int main () { return __pthread_mutex_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pthread___pthread_mutex_init=yes else ac_cv_lib_pthread___pthread_mutex_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread___pthread_mutex_init" >&5 $as_echo "$ac_cv_lib_pthread___pthread_mutex_init" >&6; } if test "x$ac_cv_lib_pthread___pthread_mutex_init" = xyes; then : tcl_ok=yes else tcl_ok=no fi fi if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -lpthread" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lpthreads" >&5 $as_echo_n "checking for pthread_mutex_init in -lpthreads... " >&6; } if ${ac_cv_lib_pthreads_pthread_mutex_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthreads $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_mutex_init (); int main () { return pthread_mutex_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pthreads_pthread_mutex_init=yes else ac_cv_lib_pthreads_pthread_mutex_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthreads_pthread_mutex_init" >&5 $as_echo "$ac_cv_lib_pthreads_pthread_mutex_init" >&6; } if test "x$ac_cv_lib_pthreads_pthread_mutex_init" = xyes; then : tcl_ok=yes else tcl_ok=no fi if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -lpthreads" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lc" >&5 $as_echo_n "checking for pthread_mutex_init in -lc... " >&6; } if ${ac_cv_lib_c_pthread_mutex_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lc $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_mutex_init (); int main () { return pthread_mutex_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_c_pthread_mutex_init=yes else ac_cv_lib_c_pthread_mutex_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_pthread_mutex_init" >&5 $as_echo "$ac_cv_lib_c_pthread_mutex_init" >&6; } if test "x$ac_cv_lib_c_pthread_mutex_init" = xyes; then : tcl_ok=yes else tcl_ok=no fi if test "$tcl_ok" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lc_r" >&5 $as_echo_n "checking for pthread_mutex_init in -lc_r... " >&6; } if ${ac_cv_lib_c_r_pthread_mutex_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lc_r $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_mutex_init (); int main () { return pthread_mutex_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_c_r_pthread_mutex_init=yes else ac_cv_lib_c_r_pthread_mutex_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_r_pthread_mutex_init" >&5 $as_echo "$ac_cv_lib_c_r_pthread_mutex_init" >&6; } if test "x$ac_cv_lib_c_r_pthread_mutex_init" = xyes; then : tcl_ok=yes else tcl_ok=no fi if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -pthread" else TCL_THREADS=0 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Do not know how to find pthread lib on your system - thread support disabled" >&5 $as_echo "$as_me: WARNING: Do not know how to find pthread lib on your system - thread support disabled" >&2;} fi fi fi fi fi else TCL_THREADS=0 fi # Do checking message here to not mess up interleaved configure output { $as_echo "$as_me:${as_lineno-$LINENO}: checking for building with threads" >&5 $as_echo_n "checking for building with threads... " >&6; } if test "${TCL_THREADS}" = 1; then $as_echo "#define TCL_THREADS 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (default)" >&5 $as_echo "yes (default)" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # TCL_THREADS sanity checking. See if our request for building with # threads is the same as the way Tcl was built. If not, warn the user. case ${TCL_DEFS} in *THREADS=1*) if test "${TCL_THREADS}" = "0"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Building ${PACKAGE_NAME} without threads enabled, but building against Tcl that IS thread-enabled. It is recommended to use --enable-threads." >&5 $as_echo "$as_me: WARNING: Building ${PACKAGE_NAME} without threads enabled, but building against Tcl that IS thread-enabled. It is recommended to use --enable-threads." >&2;} fi ;; *) if test "${TCL_THREADS}" = "1"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --enable-threads requested, but building against a Tcl that is NOT thread-enabled. This is an OK configuration that will also run in a thread-enabled core." >&5 $as_echo "$as_me: WARNING: --enable-threads requested, but building against a Tcl that is NOT thread-enabled. This is an OK configuration that will also run in a thread-enabled core." >&2;} fi ;; esac #-------------------------------------------------------------------- # The statement below defines a collection of symbols related to # building as a shared library instead of a static library. #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to build libraries" >&5 $as_echo_n "checking how to build libraries... " >&6; } # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; tcl_ok=$enableval else tcl_ok=yes fi if test "${enable_shared+set}" = set; then enableval="$enable_shared" tcl_ok=$enableval else tcl_ok=yes fi if test "$tcl_ok" = "yes" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: shared" >&5 $as_echo "shared" >&6; } SHARED_BUILD=1 else { $as_echo "$as_me:${as_lineno-$LINENO}: result: static" >&5 $as_echo "static" >&6; } SHARED_BUILD=0 $as_echo "#define STATIC_BUILD 1" >>confdefs.h fi #-------------------------------------------------------------------- # This macro figures out what flags to use with the compiler/linker # when building shared/static debug/optimized objects. This information # can be taken from the tclConfig.sh file, but this figures it all out. #-------------------------------------------------------------------- if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi # Step 0.a: Enable 64 bit support? { $as_echo "$as_me:${as_lineno-$LINENO}: checking if 64bit support is requested" >&5 $as_echo_n "checking if 64bit support is requested... " >&6; } # Check whether --enable-64bit was given. if test "${enable_64bit+set}" = set; then : enableval=$enable_64bit; do64bit=$enableval else do64bit=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $do64bit" >&5 $as_echo "$do64bit" >&6; } # Step 0.b: Enable Solaris 64 bit VIS support? { $as_echo "$as_me:${as_lineno-$LINENO}: checking if 64bit Sparc VIS support is requested" >&5 $as_echo_n "checking if 64bit Sparc VIS support is requested... " >&6; } # Check whether --enable-64bit-vis was given. if test "${enable_64bit_vis+set}" = set; then : enableval=$enable_64bit_vis; do64bitVIS=$enableval else do64bitVIS=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $do64bitVIS" >&5 $as_echo "$do64bitVIS" >&6; } # Force 64bit on with VIS if test "$do64bitVIS" = "yes"; then : do64bit=yes fi # Step 0.c: Check if visibility support is available. Do this here so # that platform specific alternatives can be used below if this fails. { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler supports visibility \"hidden\"" >&5 $as_echo_n "checking if compiler supports visibility \"hidden\"... " >&6; } if ${tcl_cv_cc_visibility_hidden+:} false; then : $as_echo_n "(cached) " >&6 else hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern __attribute__((__visibility__("hidden"))) void f(void); void f(void) {} int main () { f(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_cc_visibility_hidden=yes else tcl_cv_cc_visibility_hidden=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CFLAGS=$hold_cflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_visibility_hidden" >&5 $as_echo "$tcl_cv_cc_visibility_hidden" >&6; } if test $tcl_cv_cc_visibility_hidden = yes; then : $as_echo "#define MODULE_SCOPE extern __attribute__((__visibility__(\"hidden\")))" >>confdefs.h $as_echo "#define HAVE_HIDDEN 1" >>confdefs.h fi # Step 0.d: Disable -rpath support? { $as_echo "$as_me:${as_lineno-$LINENO}: checking if rpath support is requested" >&5 $as_echo_n "checking if rpath support is requested... " >&6; } # Check whether --enable-rpath was given. if test "${enable_rpath+set}" = set; then : enableval=$enable_rpath; doRpath=$enableval else doRpath=yes fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $doRpath" >&5 $as_echo "$doRpath" >&6; } # TEA specific: Cross-compiling options for Windows/CE builds? if test "${TEA_PLATFORM}" = windows; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking if Windows/CE build is requested" >&5 $as_echo_n "checking if Windows/CE build is requested... " >&6; } # Check whether --enable-wince was given. if test "${enable_wince+set}" = set; then : enableval=$enable_wince; doWince=$enableval else doWince=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $doWince" >&5 $as_echo "$doWince" >&6; } fi # Set the variable "system" to hold the name and version number # for the system. { $as_echo "$as_me:${as_lineno-$LINENO}: checking system version" >&5 $as_echo_n "checking system version... " >&6; } if ${tcl_cv_sys_version+:} false; then : $as_echo_n "(cached) " >&6 else # TEA specific: if test "${TEA_PLATFORM}" = "windows" ; then tcl_cv_sys_version=windows else tcl_cv_sys_version=`uname -s`-`uname -r` if test "$?" -ne 0 ; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: can't find uname command" >&5 $as_echo "$as_me: WARNING: can't find uname command" >&2;} tcl_cv_sys_version=unknown else if test "`uname -s`" = "AIX" ; then tcl_cv_sys_version=AIX-`uname -v`.`uname -r` fi fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_sys_version" >&5 $as_echo "$tcl_cv_sys_version" >&6; } system=$tcl_cv_sys_version # Require ranlib early so we can override it in special cases below. # Set configuration options based on system name and version. # This is similar to Tcl's unix/tcl.m4 except that we've added a # "windows" case and removed some core-only vars. do64bit_ok=no # default to '{$LIBS}' and set to "" on per-platform necessary basis SHLIB_LD_LIBS='${LIBS}' # When ld needs options to work in 64-bit mode, put them in # LDFLAGS_ARCH so they eventually end up in LDFLAGS even if [load] # is disabled by the user. [Bug 1016796] LDFLAGS_ARCH="" UNSHARED_LIB_SUFFIX="" # TEA specific: use PACKAGE_VERSION instead of VERSION TCL_TRIM_DOTS='`echo ${PACKAGE_VERSION} | tr -d .`' ECHO_VERSION='`echo ${PACKAGE_VERSION}`' TCL_LIB_VERSIONS_OK=ok CFLAGS_DEBUG=-g if test "$GCC" = yes; then : CFLAGS_OPTIMIZE=-O2 CFLAGS_WARNING="-Wall" else CFLAGS_OPTIMIZE=-O CFLAGS_WARNING="" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. set dummy ${ac_tool_prefix}ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="${ac_tool_prefix}ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_AR"; then ac_ct_AR=$AR # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_AR" = x; then AR="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi else AR="$ac_cv_prog_AR" fi STLIB_LD='${AR} cr' LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH" if test "x$SHLIB_VERSION" = x; then : SHLIB_VERSION="1.0" fi case $system in # TEA specific: windows) # This is a 2-stage check to make sure we have the 64-bit SDK # We have to know where the SDK is installed. # This magic is based on MS Platform SDK for Win2003 SP1 - hobbs # MACHINE is IX86 for LINK, but this is used by the manifest, # which requires x86|amd64|ia64. MACHINE="X86" if test "$do64bit" != "no" ; then if test "x${MSSDK}x" = "xx" ; then MSSDK="C:/Progra~1/Microsoft Platform SDK" fi MSSDK=`echo "$MSSDK" | sed -e 's!\\\!/!g'` PATH64="" case "$do64bit" in amd64|x64|yes) MACHINE="AMD64" ; # default to AMD64 64-bit build PATH64="${MSSDK}/Bin/Win64/x86/AMD64" ;; ia64) MACHINE="IA64" PATH64="${MSSDK}/Bin/Win64" ;; esac if test "$GCC" != "yes" -a ! -d "${PATH64}" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not find 64-bit $MACHINE SDK to enable 64bit mode" >&5 $as_echo "$as_me: WARNING: Could not find 64-bit $MACHINE SDK to enable 64bit mode" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Ensure latest Platform SDK is installed" >&5 $as_echo "$as_me: WARNING: Ensure latest Platform SDK is installed" >&2;} do64bit="no" else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using 64-bit $MACHINE mode" >&5 $as_echo " Using 64-bit $MACHINE mode" >&6; } do64bit_ok="yes" fi fi if test "$doWince" != "no" ; then if test "$do64bit" != "no" ; then as_fn_error $? "Windows/CE and 64-bit builds incompatible" "$LINENO" 5 fi if test "$GCC" = "yes" ; then as_fn_error $? "Windows/CE and GCC builds incompatible" "$LINENO" 5 fi # First, look for one uninstalled. # the alternative search directory is invoked by --with-celib if test x"${no_celib}" = x ; then # we reset no_celib in case something fails here no_celib=true # Check whether --with-celib was given. if test "${with_celib+set}" = set; then : withval=$with_celib; with_celibconfig=${withval} fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Windows/CE celib directory" >&5 $as_echo_n "checking for Windows/CE celib directory... " >&6; } if ${ac_cv_c_celibconfig+:} false; then : $as_echo_n "(cached) " >&6 else # First check to see if --with-celibconfig was specified. if test x"${with_celibconfig}" != x ; then if test -d "${with_celibconfig}/inc" ; then ac_cv_c_celibconfig=`(cd ${with_celibconfig}; pwd)` else as_fn_error $? "${with_celibconfig} directory doesn't contain inc directory" "$LINENO" 5 fi fi # then check for a celib library if test x"${ac_cv_c_celibconfig}" = x ; then for i in \ ../celib-palm-3.0 \ ../celib \ ../../celib-palm-3.0 \ ../../celib \ `ls -dr ../celib-*3.[0-9]* 2>/dev/null` \ ${srcdir}/../celib-palm-3.0 \ ${srcdir}/../celib \ `ls -dr ${srcdir}/../celib-*3.[0-9]* 2>/dev/null` \ ; do if test -d "$i/inc" ; then ac_cv_c_celibconfig=`(cd $i; pwd)` break fi done fi fi if test x"${ac_cv_c_celibconfig}" = x ; then as_fn_error $? "Cannot find celib support library directory" "$LINENO" 5 else no_celib= CELIB_DIR=${ac_cv_c_celibconfig} CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'` { $as_echo "$as_me:${as_lineno-$LINENO}: result: found $CELIB_DIR" >&5 $as_echo "found $CELIB_DIR" >&6; } fi fi # Set defaults for common evc4/PPC2003 setup # Currently Tcl requires 300+, possibly 420+ for sockets CEVERSION=420; # could be 211 300 301 400 420 ... TARGETCPU=ARMV4; # could be ARMV4 ARM MIPS SH3 X86 ... ARCH=ARM; # could be ARM MIPS X86EM ... PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002" if test "$doWince" != "yes"; then # If !yes then the user specified something # Reset ARCH to allow user to skip specifying it ARCH= eval `echo $doWince | awk -F, '{ \ if (length($1)) { printf "CEVERSION=\"%s\"\n", $1; \ if ($1 < 400) { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \ if (length($2)) { printf "TARGETCPU=\"%s\"\n", toupper($2) }; \ if (length($3)) { printf "ARCH=\"%s\"\n", toupper($3) }; \ if (length($4)) { printf "PLATFORM=\"%s\"\n", $4 }; \ }'` if test "x${ARCH}" = "x" ; then ARCH=$TARGETCPU; fi fi OSVERSION=WCE$CEVERSION; if test "x${WCEROOT}" = "x" ; then WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0" if test ! -d "${WCEROOT}" ; then WCEROOT="C:/Program Files/Microsoft eMbedded Tools" fi fi if test "x${SDKROOT}" = "x" ; then SDKROOT="C:/Program Files/Windows CE Tools" if test ! -d "${SDKROOT}" ; then SDKROOT="C:/Windows CE Tools" fi fi WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'` SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'` if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" \ -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then as_fn_error $? "could not find PocketPC SDK or target compiler to enable WinCE mode $CEVERSION,$TARGETCPU,$ARCH,$PLATFORM" "$LINENO" 5 doWince="no" else # We could PATH_NOSPACE these, but that's not important, # as long as we quote them when used. CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include" if test -d "${CEINCLUDE}/${TARGETCPU}" ; then CEINCLUDE="${CEINCLUDE}/${TARGETCPU}" fi CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" fi fi if test "$GCC" != "yes" ; then if test "${SHARED_BUILD}" = "0" ; then runtime=-MT else runtime=-MD fi if test "$do64bit" != "no" ; then # All this magic is necessary for the Win64 SDK RC1 - hobbs CC="\"${PATH64}/cl.exe\"" CFLAGS="${CFLAGS} -I\"${MSSDK}/Include\" -I\"${MSSDK}/Include/crt\" -I\"${MSSDK}/Include/crt/sys\"" RC="\"${MSSDK}/bin/rc.exe\"" lflags="-nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\"" LINKBIN="\"${PATH64}/link.exe\"" CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d" CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}" # Avoid 'unresolved external symbol __security_cookie' # errors, c.f. http://support.microsoft.com/?id=894573 vars="bufferoverflowU.lib" for i in $vars; do if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then # Convert foo.lib to -lfoo for GCC. No-op if not *.lib i=`echo "$i" | sed -e 's/^\([^-].*\)\.lib$/-l\1/i'` fi PKG_LIBS="$PKG_LIBS $i" done elif test "$doWince" != "no" ; then CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin" if test "${TARGETCPU}" = "X86"; then CC="\"${CEBINROOT}/cl.exe\"" else CC="\"${CEBINROOT}/cl${ARCH}.exe\"" fi CFLAGS="$CFLAGS -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\"" RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\"" arch=`echo ${ARCH} | awk '{print tolower($0)}'` defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _WINDOWS" if test "${SHARED_BUILD}" = "1" ; then # Static CE builds require static celib as well defs="${defs} _DLL" fi for i in $defs ; do cat >>confdefs.h <<_ACEOF #define $i 1 _ACEOF done cat >>confdefs.h <<_ACEOF #define _WIN32_WCE $CEVERSION _ACEOF cat >>confdefs.h <<_ACEOF #define UNDER_CE $CEVERSION _ACEOF CFLAGS_DEBUG="-nologo -Zi -Od" CFLAGS_OPTIMIZE="-nologo -Ox" lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'` lflags="-MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo" LINKBIN="\"${CEBINROOT}/link.exe\"" else RC="rc" lflags="-nologo" LINKBIN="link" CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d" CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}" fi fi if test "$GCC" = "yes"; then # mingw gcc mode if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args. set dummy ${ac_tool_prefix}windres; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RC"; then ac_cv_prog_RC="$RC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RC="${ac_tool_prefix}windres" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RC=$ac_cv_prog_RC if test -n "$RC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RC" >&5 $as_echo "$RC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RC"; then ac_ct_RC=$RC # Extract the first word of "windres", so it can be a program name with args. set dummy windres; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RC"; then ac_cv_prog_ac_ct_RC="$ac_ct_RC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RC="windres" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RC=$ac_cv_prog_ac_ct_RC if test -n "$ac_ct_RC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RC" >&5 $as_echo "$ac_ct_RC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RC" = x; then RC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RC=$ac_ct_RC fi else RC="$ac_cv_prog_RC" fi CFLAGS_DEBUG="-g" CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer" SHLIB_LD='${CC} -shared' UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}" LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cross-compile version of gcc" >&5 $as_echo_n "checking for cross-compile version of gcc... " >&6; } if ${ac_cv_cross+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef _WIN32 #error cross-compiler #endif int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_cross=yes else ac_cv_cross=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cross" >&5 $as_echo "$ac_cv_cross" >&6; } if test "$ac_cv_cross" = "yes"; then case "$do64bit" in amd64|x64|yes) CC="x86_64-w64-mingw32-gcc" LD="x86_64-w64-mingw32-ld" AR="x86_64-w64-mingw32-ar" RANLIB="x86_64-w64-mingw32-ranlib" RC="x86_64-w64-mingw32-windres" ;; *) CC="i686-w64-mingw32-gcc" LD="i686-w64-mingw32-ld" AR="i686-w64-mingw32-ar" RANLIB="i686-w64-mingw32-ranlib" RC="i686-w64-mingw32-windres" ;; esac fi else SHLIB_LD="${LINKBIN} -dll ${lflags}" # link -lib only works when -lib is the first arg STLIB_LD="${LINKBIN} -lib ${lflags}" UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.lib' PATHTYPE=-w # For information on what debugtype is most useful, see: # http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp # and also # http://msdn2.microsoft.com/en-us/library/y0zzbyt4%28VS.80%29.aspx # This essentially turns it all on. LDFLAGS_DEBUG="-debug -debugtype:cv" LDFLAGS_OPTIMIZE="-release" if test "$doWince" != "no" ; then LDFLAGS_CONSOLE="-link ${lflags}" LDFLAGS_WINDOW=${LDFLAGS_CONSOLE} else LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}" LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}" fi fi SHLIB_SUFFIX=".dll" SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.dll' TCL_LIB_VERSIONS_OK=nodots ;; AIX-*) if test "${TCL_THREADS}" = "1" -a "$GCC" != "yes"; then : # AIX requires the _r compiler when gcc isn't being used case "${CC}" in *_r|*_r\ *) # ok ... ;; *) # Make sure only first arg gets _r CC=`echo "$CC" | sed -e 's/^\([^ ]*\)/\1_r/'` ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using $CC for compiling with threads" >&5 $as_echo "Using $CC for compiling with threads" >&6; } fi LIBS="$LIBS -lc" SHLIB_CFLAGS="" SHLIB_SUFFIX=".so" LD_LIBRARY_PATH_VAR="LIBPATH" # Check to enable 64-bit flags for compiler/linker if test "$do64bit" = yes; then : if test "$GCC" = yes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC on $system" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported with GCC on $system" >&2;} else do64bit_ok=yes CFLAGS="$CFLAGS -q64" LDFLAGS_ARCH="-q64" RANLIB="${RANLIB} -X64" AR="${AR} -X64" SHLIB_LD_FLAGS="-b64" fi fi if test "`uname -m`" = ia64; then : # AIX-5 uses ELF style dynamic libraries on IA-64, but not PPC SHLIB_LD="/usr/ccs/bin/ld -G -z text" if test "$GCC" = yes; then : CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' else CC_SEARCH_FLAGS='-R${LIB_RUNTIME_DIR}' fi LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' else if test "$GCC" = yes; then : SHLIB_LD='${CC} -shared -Wl,-bexpall' else SHLIB_LD="/bin/ld -bhalt:4 -bM:SRE -bexpall -H512 -T512 -bnoentry" LDFLAGS="$LDFLAGS -brtl" fi SHLIB_LD="${SHLIB_LD} ${SHLIB_LD_FLAGS}" CC_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} fi ;; BeOS*) SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -nostart' SHLIB_SUFFIX=".so" #----------------------------------------------------------- # Check for inet_ntoa in -lbind, for BeOS (which also needs # -lsocket, even if the network functions are in -lnet which # is always linked to, for compatibility. #----------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_ntoa in -lbind" >&5 $as_echo_n "checking for inet_ntoa in -lbind... " >&6; } if ${ac_cv_lib_bind_inet_ntoa+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbind $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inet_ntoa (); int main () { return inet_ntoa (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_bind_inet_ntoa=yes else ac_cv_lib_bind_inet_ntoa=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bind_inet_ntoa" >&5 $as_echo "$ac_cv_lib_bind_inet_ntoa" >&6; } if test "x$ac_cv_lib_bind_inet_ntoa" = xyes; then : LIBS="$LIBS -lbind -lsocket" fi ;; BSD/OS-4.*) SHLIB_CFLAGS="-export-dynamic -fPIC" SHLIB_LD='${CC} -shared' SHLIB_SUFFIX=".so" LDFLAGS="$LDFLAGS -export-dynamic" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; CYGWIN_*) SHLIB_CFLAGS="" SHLIB_LD='${CC} -shared' SHLIB_SUFFIX=".dll" EXEEXT=".exe" do64bit_ok=yes CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; Haiku*) LDFLAGS="$LDFLAGS -Wl,--export-dynamic" SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS}' { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_ntoa in -lnetwork" >&5 $as_echo_n "checking for inet_ntoa in -lnetwork... " >&6; } if ${ac_cv_lib_network_inet_ntoa+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lnetwork $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inet_ntoa (); int main () { return inet_ntoa (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_network_inet_ntoa=yes else ac_cv_lib_network_inet_ntoa=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_network_inet_ntoa" >&5 $as_echo "$ac_cv_lib_network_inet_ntoa" >&6; } if test "x$ac_cv_lib_network_inet_ntoa" = xyes; then : LIBS="$LIBS -lnetwork" fi ;; HP-UX-*.11.*) # Use updated header definitions where possible $as_echo "#define _XOPEN_SOURCE_EXTENDED 1" >>confdefs.h # TEA specific: Needed by Tcl, but not most extensions #AC_DEFINE(_XOPEN_SOURCE, 1, [Do we want to use the XOPEN network library?]) #LIBS="$LIBS -lxnet" # Use the XOPEN network library if test "`uname -m`" = ia64; then : SHLIB_SUFFIX=".so" # Use newer C++ library for C++ extensions #if test "$GCC" != "yes" ; then # CPPFLAGS="-AA" #fi else SHLIB_SUFFIX=".sl" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if ${ac_cv_lib_dld_shl_load+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes; then : tcl_ok=yes else tcl_ok=no fi if test "$tcl_ok" = yes; then : LDFLAGS="$LDFLAGS -Wl,-E" CC_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.' LD_SEARCH_FLAGS='+s +b ${LIB_RUNTIME_DIR}:.' LD_LIBRARY_PATH_VAR="SHLIB_PATH" fi if test "$GCC" = yes; then : SHLIB_LD='${CC} -shared' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} else CFLAGS="$CFLAGS -z" # Users may want PA-RISC 1.1/2.0 portable code - needs HP cc #CFLAGS="$CFLAGS +DAportable" SHLIB_CFLAGS="+z" SHLIB_LD="ld -b" fi # Check to enable 64-bit flags for compiler/linker if test "$do64bit" = "yes"; then : if test "$GCC" = yes; then : case `${CC} -dumpmachine` in hppa64*) # 64-bit gcc in use. Fix flags for GNU ld. do64bit_ok=yes SHLIB_LD='${CC} -shared' if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC on $system" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported with GCC on $system" >&2;} ;; esac else do64bit_ok=yes CFLAGS="$CFLAGS +DD64" LDFLAGS_ARCH="+DD64" fi fi ;; IRIX-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -n32 -shared -rdata_shared" SHLIB_SUFFIX=".so" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}' fi if test "$GCC" = yes; then : CFLAGS="$CFLAGS -mabi=n32" LDFLAGS="$LDFLAGS -mabi=n32" else case $system in IRIX-6.3) # Use to build 6.2 compatible binaries on 6.3. CFLAGS="$CFLAGS -n32 -D_OLD_TERMIOS" ;; *) CFLAGS="$CFLAGS -n32" ;; esac LDFLAGS="$LDFLAGS -n32" fi ;; IRIX64-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -n32 -shared -rdata_shared" SHLIB_SUFFIX=".so" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}' fi # Check to enable 64-bit flags for compiler/linker if test "$do64bit" = yes; then : if test "$GCC" = yes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported by gcc" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported by gcc" >&2;} else do64bit_ok=yes SHLIB_LD="ld -64 -shared -rdata_shared" CFLAGS="$CFLAGS -64" LDFLAGS_ARCH="-64" fi fi ;; Linux*|GNU*|NetBSD-Debian) SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" # TEA specific: CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer" # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS_DEFAULT}' LDFLAGS="$LDFLAGS -Wl,--export-dynamic" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} if test "`uname -m`" = "alpha"; then : CFLAGS="$CFLAGS -mieee" fi if test $do64bit = yes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler accepts -m64 flag" >&5 $as_echo_n "checking if compiler accepts -m64 flag... " >&6; } if ${tcl_cv_cc_m64+:} false; then : $as_echo_n "(cached) " >&6 else hold_cflags=$CFLAGS CFLAGS="$CFLAGS -m64" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_cc_m64=yes else tcl_cv_cc_m64=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CFLAGS=$hold_cflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_m64" >&5 $as_echo "$tcl_cv_cc_m64" >&6; } if test $tcl_cv_cc_m64 = yes; then : CFLAGS="$CFLAGS -m64" do64bit_ok=yes fi fi # The combo of gcc + glibc has a bug related to inlining of # functions like strtod(). The -fno-builtin flag should address # this problem but it does not work. The -fno-inline flag is kind # of overkill but it works. Disable inlining only when one of the # files in compat/*.c is being linked in. if test x"${USE_COMPAT}" != x; then : CFLAGS="$CFLAGS -fno-inline" fi ;; Lynx*) SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" CFLAGS_OPTIMIZE=-02 SHLIB_LD='${CC} -shared' LD_FLAGS="-Wl,--export-dynamic" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi ;; OpenBSD-*) arch=`arch -s` case "$arch" in vax) SHLIB_SUFFIX="" SHARED_LIB_SUFFIX="" LDFLAGS="" ;; *) SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}' SHLIB_SUFFIX=".so" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.${SHLIB_VERSION}' LDFLAGS="-Wl,-export-dynamic" ;; esac case "$arch" in vax) CFLAGS_OPTIMIZE="-O1" ;; *) CFLAGS_OPTIMIZE="-O2" ;; esac if test "${TCL_THREADS}" = "1"; then : # On OpenBSD: Compile with -pthread # Don't link with -lpthread LIBS=`echo $LIBS | sed s/-lpthread//` CFLAGS="$CFLAGS -pthread" fi # OpenBSD doesn't do version numbers with dots. UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' TCL_LIB_VERSIONS_OK=nodots ;; NetBSD-*) # NetBSD has ELF and can use 'cc -shared' to build shared libs SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}' SHLIB_SUFFIX=".so" LDFLAGS="$LDFLAGS -export-dynamic" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} if test "${TCL_THREADS}" = "1"; then : # The -pthread needs to go in the CFLAGS, not LIBS LIBS=`echo $LIBS | sed s/-pthread//` CFLAGS="$CFLAGS -pthread" LDFLAGS="$LDFLAGS -pthread" fi ;; FreeBSD-*) # This configuration from FreeBSD Ports. SHLIB_CFLAGS="-fPIC" SHLIB_LD="${CC} -shared" TCL_SHLIB_LD_EXTRAS="-Wl,-soname=\$@" TK_SHLIB_LD_EXTRAS="-Wl,-soname,\$@" SHLIB_SUFFIX=".so" LDFLAGS="" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' fi if test "${TCL_THREADS}" = "1"; then : # The -pthread needs to go in the LDFLAGS, not LIBS LIBS=`echo $LIBS | sed s/-pthread//` CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LDFLAGS="$LDFLAGS $PTHREAD_LIBS" fi case $system in FreeBSD-3.*) # Version numbers are dot-stripped by system policy. TCL_TRIM_DOTS=`echo ${VERSION} | tr -d .` UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so' TCL_LIB_VERSIONS_OK=nodots ;; esac ;; Darwin-*) CFLAGS_OPTIMIZE="-Os" SHLIB_CFLAGS="-fno-common" # To avoid discrepancies between what headers configure sees during # preprocessing tests and compiling tests, move any -isysroot and # -mmacosx-version-min flags from CFLAGS to CPPFLAGS: CPPFLAGS="${CPPFLAGS} `echo " ${CFLAGS}" | \ awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \ if ($i~/^(isysroot|mmacosx-version-min)/) print "-"$i}'`" CFLAGS="`echo " ${CFLAGS}" | \ awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \ if (!($i~/^(isysroot|mmacosx-version-min)/)) print "-"$i}'`" if test $do64bit = yes; then : case `arch` in ppc) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler accepts -arch ppc64 flag" >&5 $as_echo_n "checking if compiler accepts -arch ppc64 flag... " >&6; } if ${tcl_cv_cc_arch_ppc64+:} false; then : $as_echo_n "(cached) " >&6 else hold_cflags=$CFLAGS CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_cc_arch_ppc64=yes else tcl_cv_cc_arch_ppc64=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CFLAGS=$hold_cflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_arch_ppc64" >&5 $as_echo "$tcl_cv_cc_arch_ppc64" >&6; } if test $tcl_cv_cc_arch_ppc64 = yes; then : CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5" do64bit_ok=yes fi;; i386) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler accepts -arch x86_64 flag" >&5 $as_echo_n "checking if compiler accepts -arch x86_64 flag... " >&6; } if ${tcl_cv_cc_arch_x86_64+:} false; then : $as_echo_n "(cached) " >&6 else hold_cflags=$CFLAGS CFLAGS="$CFLAGS -arch x86_64" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_cc_arch_x86_64=yes else tcl_cv_cc_arch_x86_64=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CFLAGS=$hold_cflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_arch_x86_64" >&5 $as_echo "$tcl_cv_cc_arch_x86_64" >&6; } if test $tcl_cv_cc_arch_x86_64 = yes; then : CFLAGS="$CFLAGS -arch x86_64" do64bit_ok=yes fi;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Don't know how enable 64-bit on architecture \`arch\`" >&5 $as_echo "$as_me: WARNING: Don't know how enable 64-bit on architecture \`arch\`" >&2;};; esac else # Check for combined 32-bit and 64-bit fat build if echo "$CFLAGS " |grep -E -q -- '-arch (ppc64|x86_64) ' \ && echo "$CFLAGS " |grep -E -q -- '-arch (ppc|i386) '; then : fat_32_64=yes fi fi # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -dynamiclib ${CFLAGS} ${LDFLAGS_DEFAULT}' { $as_echo "$as_me:${as_lineno-$LINENO}: checking if ld accepts -single_module flag" >&5 $as_echo_n "checking if ld accepts -single_module flag... " >&6; } if ${tcl_cv_ld_single_module+:} false; then : $as_echo_n "(cached) " >&6 else hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -dynamiclib -Wl,-single_module" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { int i; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_ld_single_module=yes else tcl_cv_ld_single_module=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$hold_ldflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_ld_single_module" >&5 $as_echo "$tcl_cv_ld_single_module" >&6; } if test $tcl_cv_ld_single_module = yes; then : SHLIB_LD="${SHLIB_LD} -Wl,-single_module" fi # TEA specific: link shlib with current and compatibility version flags vers=`echo ${PACKAGE_VERSION} | sed -e 's/^\([0-9]\{1,5\}\)\(\(\.[0-9]\{1,3\}\)\{0,2\}\).*$/\1\2/p' -e d` SHLIB_LD="${SHLIB_LD} -current_version ${vers:-0} -compatibility_version ${vers:-0}" SHLIB_SUFFIX=".dylib" # Don't use -prebind when building for Mac OS X 10.4 or later only: if test "`echo "${MACOSX_DEPLOYMENT_TARGET}" | awk -F '10\\.' '{print int($2)}'`" -lt 4 -a \ "`echo "${CPPFLAGS}" | awk -F '-mmacosx-version-min=10\\.' '{print int($2)}'`" -lt 4; then : LDFLAGS="$LDFLAGS -prebind" fi LDFLAGS="$LDFLAGS -headerpad_max_install_names" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if ld accepts -search_paths_first flag" >&5 $as_echo_n "checking if ld accepts -search_paths_first flag... " >&6; } if ${tcl_cv_ld_search_paths_first+:} false; then : $as_echo_n "(cached) " >&6 else hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-search_paths_first" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { int i; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_ld_search_paths_first=yes else tcl_cv_ld_search_paths_first=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$hold_ldflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_ld_search_paths_first" >&5 $as_echo "$tcl_cv_ld_search_paths_first" >&6; } if test $tcl_cv_ld_search_paths_first = yes; then : LDFLAGS="$LDFLAGS -Wl,-search_paths_first" fi if test "$tcl_cv_cc_visibility_hidden" != yes; then : $as_echo "#define MODULE_SCOPE __private_extern__" >>confdefs.h tcl_cv_cc_visibility_hidden=yes fi CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" LD_LIBRARY_PATH_VAR="DYLD_LIBRARY_PATH" # TEA specific: for combined 32 & 64 bit fat builds of Tk # extensions, verify that 64-bit build is possible. if test "$fat_32_64" = yes && test -n "${TK_BIN_DIR}"; then : if test "${TEA_WINDOWINGSYSTEM}" = x11; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit X11" >&5 $as_echo_n "checking for 64-bit X11... " >&6; } if ${tcl_cv_lib_x11_64+:} false; then : $as_echo_n "(cached) " >&6 else for v in CFLAGS CPPFLAGS LDFLAGS; do eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"' done CPPFLAGS="$CPPFLAGS -I/usr/X11R6/include" LDFLAGS="$LDFLAGS -L/usr/X11R6/lib -lX11" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { XrmInitialize(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_lib_x11_64=yes else tcl_cv_lib_x11_64=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="$hold_'$v'"' done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_lib_x11_64" >&5 $as_echo "$tcl_cv_lib_x11_64" >&6; } fi if test "${TEA_WINDOWINGSYSTEM}" = aqua; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit Tk" >&5 $as_echo_n "checking for 64-bit Tk... " >&6; } if ${tcl_cv_lib_tk_64+:} false; then : $as_echo_n "(cached) " >&6 else for v in CFLAGS CPPFLAGS LDFLAGS; do eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"' done CPPFLAGS="$CPPFLAGS -DUSE_TCL_STUBS=1 -DUSE_TK_STUBS=1 ${TCL_INCLUDES} ${TK_INCLUDES}" LDFLAGS="$LDFLAGS ${TCL_STUB_LIB_SPEC} ${TK_STUB_LIB_SPEC}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { Tk_InitStubs(NULL, "", 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_lib_tk_64=yes else tcl_cv_lib_tk_64=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="$hold_'$v'"' done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_lib_tk_64" >&5 $as_echo "$tcl_cv_lib_tk_64" >&6; } fi # remove 64-bit arch flags from CFLAGS et al. if configuration # does not support 64-bit. if test "$tcl_cv_lib_tk_64" = no -o "$tcl_cv_lib_x11_64" = no; then : { $as_echo "$as_me:${as_lineno-$LINENO}: Removing 64-bit architectures from compiler & linker flags" >&5 $as_echo "$as_me: Removing 64-bit architectures from compiler & linker flags" >&6;} for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="`echo "$'$v' "|sed -e "s/-arch ppc64 / /g" -e "s/-arch x86_64 / /g"`"' done fi fi ;; OS/390-*) CFLAGS_OPTIMIZE="" # Optimizer is buggy $as_echo "#define _OE_SOCKETS 1" >>confdefs.h ;; OSF1-V*) # Digital OSF/1 SHLIB_CFLAGS="" if test "$SHARED_BUILD" = 1; then : SHLIB_LD='ld -shared -expect_unresolved "*"' else SHLIB_LD='ld -non_shared -expect_unresolved "*"' fi SHLIB_SUFFIX=".so" if test $doRpath = yes; then : CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}' fi if test "$GCC" = yes; then : CFLAGS="$CFLAGS -mieee" else CFLAGS="$CFLAGS -DHAVE_TZSET -std1 -ieee" fi # see pthread_intro(3) for pthread support on osf1, k.furukawa if test "${TCL_THREADS}" = 1; then : CFLAGS="$CFLAGS -DHAVE_PTHREAD_ATTR_SETSTACKSIZE" CFLAGS="$CFLAGS -DTCL_THREAD_STACK_MIN=PTHREAD_STACK_MIN*64" LIBS=`echo $LIBS | sed s/-lpthreads//` if test "$GCC" = yes; then : LIBS="$LIBS -lpthread -lmach -lexc" else CFLAGS="$CFLAGS -pthread" LDFLAGS="$LDFLAGS -pthread" fi fi ;; QNX-6*) # QNX RTP # This may work for all QNX, but it was only reported for v6. SHLIB_CFLAGS="-fPIC" SHLIB_LD="ld -Bshareable -x" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; SCO_SV-3.2*) if test "$GCC" = yes; then : SHLIB_CFLAGS="-fPIC -melf" LDFLAGS="$LDFLAGS -melf -Wl,-Bexport" else SHLIB_CFLAGS="-Kpic -belf" LDFLAGS="$LDFLAGS -belf -Wl,-Bexport" fi SHLIB_LD="ld -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; SunOS-5.[0-6]) # Careful to not let 5.10+ fall into this case # Note: If _REENTRANT isn't defined, then Solaris # won't define thread-safe library routines. $as_echo "#define _REENTRANT 1" >>confdefs.h $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h SHLIB_CFLAGS="-KPIC" SHLIB_SUFFIX=".so" if test "$GCC" = yes; then : SHLIB_LD='${CC} -shared' CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} else SHLIB_LD="/usr/ccs/bin/ld -G -z text" CC_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} fi ;; SunOS-5*) # Note: If _REENTRANT isn't defined, then Solaris # won't define thread-safe library routines. $as_echo "#define _REENTRANT 1" >>confdefs.h $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h SHLIB_CFLAGS="-KPIC" # Check to enable 64-bit flags for compiler/linker if test "$do64bit" = yes; then : arch=`isainfo` if test "$arch" = "sparcv9 sparc"; then : if test "$GCC" = yes; then : if test "`${CC} -dumpversion | awk -F. '{print $1}'`" -lt 3; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC < 3.2 on $system" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported with GCC < 3.2 on $system" >&2;} else do64bit_ok=yes CFLAGS="$CFLAGS -m64 -mcpu=v9" LDFLAGS="$LDFLAGS -m64 -mcpu=v9" SHLIB_CFLAGS="-fPIC" fi else do64bit_ok=yes if test "$do64bitVIS" = yes; then : CFLAGS="$CFLAGS -xarch=v9a" LDFLAGS_ARCH="-xarch=v9a" else CFLAGS="$CFLAGS -xarch=v9" LDFLAGS_ARCH="-xarch=v9" fi # Solaris 64 uses this as well #LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH_64" fi else if test "$arch" = "amd64 i386"; then : if test "$GCC" = yes; then : case $system in SunOS-5.1[1-9]*|SunOS-5.[2-9][0-9]*) do64bit_ok=yes CFLAGS="$CFLAGS -m64" LDFLAGS="$LDFLAGS -m64";; *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC on $system" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported with GCC on $system" >&2;};; esac else do64bit_ok=yes case $system in SunOS-5.1[1-9]*|SunOS-5.[2-9][0-9]*) CFLAGS="$CFLAGS -m64" LDFLAGS="$LDFLAGS -m64";; *) CFLAGS="$CFLAGS -xarch=amd64" LDFLAGS="$LDFLAGS -xarch=amd64";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported for $arch" >&5 $as_echo "$as_me: WARNING: 64bit mode not supported for $arch" >&2;} fi fi fi SHLIB_SUFFIX=".so" if test "$GCC" = yes; then : SHLIB_LD='${CC} -shared' CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} if test "$do64bit_ok" = yes; then : if test "$arch" = "sparcv9 sparc"; then : # We need to specify -static-libgcc or we need to # add the path to the sparv9 libgcc. # JH: static-libgcc is necessary for core Tcl, but may # not be necessary for extensions. SHLIB_LD="$SHLIB_LD -m64 -mcpu=v9 -static-libgcc" # for finding sparcv9 libgcc, get the regular libgcc # path, remove so name and append 'sparcv9' #v9gcclibdir="`gcc -print-file-name=libgcc_s.so` | ..." #CC_SEARCH_FLAGS="${CC_SEARCH_FLAGS},-R,$v9gcclibdir" else if test "$arch" = "amd64 i386"; then : # JH: static-libgcc is necessary for core Tcl, but may # not be necessary for extensions. SHLIB_LD="$SHLIB_LD -m64 -static-libgcc" fi fi fi else case $system in SunOS-5.[1-9][0-9]*) # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -G -z text ${LDFLAGS_DEFAULT}';; *) SHLIB_LD='/usr/ccs/bin/ld -G -z text';; esac CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' fi ;; UNIX_SV* | UnixWare-5*) SHLIB_CFLAGS="-KPIC" SHLIB_LD='${CC} -G' SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" # Some UNIX_SV* systems (unixware 1.1.2 for example) have linkers # that don't grok the -Bexport option. Test that it does. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld accepts -Bexport flag" >&5 $as_echo_n "checking for ld accepts -Bexport flag... " >&6; } if ${tcl_cv_ld_Bexport+:} false; then : $as_echo_n "(cached) " >&6 else hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-Bexport" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { int i; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tcl_cv_ld_Bexport=yes else tcl_cv_ld_Bexport=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$hold_ldflags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_ld_Bexport" >&5 $as_echo "$tcl_cv_ld_Bexport" >&6; } if test $tcl_cv_ld_Bexport = yes; then : LDFLAGS="$LDFLAGS -Wl,-Bexport" fi CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; esac if test "$do64bit" = yes -a "$do64bit_ok" = no; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit support being disabled -- don't know magic for this platform" >&5 $as_echo "$as_me: WARNING: 64bit support being disabled -- don't know magic for this platform" >&2;} fi # Add in the arch flags late to ensure it wasn't removed. # Not necessary in TEA, but this is aligned with core LDFLAGS="$LDFLAGS $LDFLAGS_ARCH" # If we're running gcc, then change the C flags for compiling shared # libraries to the right flags for gcc, instead of those for the # standard manufacturer compiler. if test "$GCC" = yes; then : case $system in AIX-*) ;; BSD/OS*) ;; CYGWIN_*|MINGW32_*) ;; IRIX*) ;; NetBSD-*|FreeBSD-*|OpenBSD-*) ;; Darwin-*) ;; SCO_SV-3.2*) ;; windows) ;; *) SHLIB_CFLAGS="-fPIC" ;; esac fi if test "$tcl_cv_cc_visibility_hidden" != yes; then : $as_echo "#define MODULE_SCOPE extern" >>confdefs.h fi if test "$SHARED_LIB_SUFFIX" = ""; then : # TEA specific: use PACKAGE_VERSION instead of VERSION SHARED_LIB_SUFFIX='${PACKAGE_VERSION}${SHLIB_SUFFIX}' fi if test "$UNSHARED_LIB_SUFFIX" = ""; then : # TEA specific: use PACKAGE_VERSION instead of VERSION UNSHARED_LIB_SUFFIX='${PACKAGE_VERSION}.a' fi if test "${GCC}" = "yes" -a ${SHLIB_SUFFIX} = ".dll"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SEH support in compiler" >&5 $as_echo_n "checking for SEH support in compiler... " >&6; } if ${tcl_cv_seh+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : tcl_cv_seh=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN int main(int argc, char** argv) { int a, b = 0; __try { a = 666 / b; } __except (EXCEPTION_EXECUTE_HANDLER) { return 0; } return 1; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : tcl_cv_seh=yes else tcl_cv_seh=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_seh" >&5 $as_echo "$tcl_cv_seh" >&6; } if test "$tcl_cv_seh" = "no" ; then $as_echo "#define HAVE_NO_SEH 1" >>confdefs.h fi # # Check to see if the excpt.h include file provided contains the # definition for EXCEPTION_DISPOSITION; if not, which is the case # with Cygwin's version as of 2002-04-10, define it to be int, # sufficient for getting the current code to work. # { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EXCEPTION_DISPOSITION support in include files" >&5 $as_echo_n "checking for EXCEPTION_DISPOSITION support in include files... " >&6; } if ${tcl_cv_eh_disposition+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # define WIN32_LEAN_AND_MEAN # include # undef WIN32_LEAN_AND_MEAN int main () { EXCEPTION_DISPOSITION x; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_eh_disposition=yes else tcl_cv_eh_disposition=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_eh_disposition" >&5 $as_echo "$tcl_cv_eh_disposition" >&6; } if test "$tcl_cv_eh_disposition" = "no" ; then $as_echo "#define EXCEPTION_DISPOSITION int" >>confdefs.h fi # Check to see if winnt.h defines CHAR, SHORT, and LONG # even if VOID has already been #defined. The win32api # used by mingw and cygwin is known to do this. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for winnt.h that ignores VOID define" >&5 $as_echo_n "checking for winnt.h that ignores VOID define... " >&6; } if ${tcl_cv_winnt_ignore_void+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define VOID void #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN int main () { CHAR c; SHORT s; LONG l; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_winnt_ignore_void=yes else tcl_cv_winnt_ignore_void=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_winnt_ignore_void" >&5 $as_echo "$tcl_cv_winnt_ignore_void" >&6; } if test "$tcl_cv_winnt_ignore_void" = "yes" ; then $as_echo "#define HAVE_WINNT_IGNORE_VOID 1" >>confdefs.h fi fi # See if the compiler supports casting to a union type. # This is used to stop gcc from printing a compiler # warning when initializing a union member. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cast to union support" >&5 $as_echo_n "checking for cast to union support... " >&6; } if ${tcl_cv_cast_to_union+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { union foo { int i; double d; }; union foo f = (union foo) (int) 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_cast_to_union=yes else tcl_cv_cast_to_union=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cast_to_union" >&5 $as_echo "$tcl_cv_cast_to_union" >&6; } if test "$tcl_cv_cast_to_union" = "yes"; then $as_echo "#define HAVE_CAST_TO_UNION 1" >>confdefs.h fi # These must be called after we do the basic CFLAGS checks and # verify any possible 64-bit or similar switches are necessary { $as_echo "$as_me:${as_lineno-$LINENO}: checking for required early compiler flags" >&5 $as_echo_n "checking for required early compiler flags... " >&6; } tcl_flags="" if ${tcl_cv_flag__isoc99_source+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { char *p = (char *)strtoll; char *q = (char *)strtoull; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__isoc99_source=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _ISOC99_SOURCE 1 #include int main () { char *p = (char *)strtoll; char *q = (char *)strtoull; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__isoc99_source=yes else tcl_cv_flag__isoc99_source=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test "x${tcl_cv_flag__isoc99_source}" = "xyes" ; then $as_echo "#define _ISOC99_SOURCE 1" >>confdefs.h tcl_flags="$tcl_flags _ISOC99_SOURCE" fi if ${tcl_cv_flag__largefile64_source+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { struct stat64 buf; int i = stat64("/", &buf); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__largefile64_source=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGEFILE64_SOURCE 1 #include int main () { struct stat64 buf; int i = stat64("/", &buf); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__largefile64_source=yes else tcl_cv_flag__largefile64_source=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test "x${tcl_cv_flag__largefile64_source}" = "xyes" ; then $as_echo "#define _LARGEFILE64_SOURCE 1" >>confdefs.h tcl_flags="$tcl_flags _LARGEFILE64_SOURCE" fi if ${tcl_cv_flag__largefile_source64+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { char *p = (char *)open64; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__largefile_source64=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGEFILE_SOURCE64 1 #include int main () { char *p = (char *)open64; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_flag__largefile_source64=yes else tcl_cv_flag__largefile_source64=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test "x${tcl_cv_flag__largefile_source64}" = "xyes" ; then $as_echo "#define _LARGEFILE_SOURCE64 1" >>confdefs.h tcl_flags="$tcl_flags _LARGEFILE_SOURCE64" fi if test "x${tcl_flags}" = "x" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${tcl_flags}" >&5 $as_echo "${tcl_flags}" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit integer type" >&5 $as_echo_n "checking for 64-bit integer type... " >&6; } if ${tcl_cv_type_64bit+:} false; then : $as_echo_n "(cached) " >&6 else tcl_cv_type_64bit=none # See if the compiler knows natively about __int64 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { __int64 value = (__int64) 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_type_64bit=__int64 else tcl_type_64bit="long long" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # See if we should use long anyway Note that we substitute in the # type that is our current guess for a 64-bit type inside this check # program, so it should be modified only carefully... cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { switch (0) { case 1: case (sizeof(${tcl_type_64bit})==sizeof(long)): ; } ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_type_64bit=${tcl_type_64bit} fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test "${tcl_cv_type_64bit}" = none ; then $as_echo "#define TCL_WIDE_INT_IS_LONG 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: using long" >&5 $as_echo "using long" >&6; } elif test "${tcl_cv_type_64bit}" = "__int64" \ -a "${TEA_PLATFORM}" = "windows" ; then # TEA specific: We actually want to use the default tcl.h checks in # this case to handle both TCL_WIDE_INT_TYPE and TCL_LL_MODIFIER* { $as_echo "$as_me:${as_lineno-$LINENO}: result: using Tcl header defaults" >&5 $as_echo "using Tcl header defaults" >&6; } else cat >>confdefs.h <<_ACEOF #define TCL_WIDE_INT_TYPE ${tcl_cv_type_64bit} _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${tcl_cv_type_64bit}" >&5 $as_echo "${tcl_cv_type_64bit}" >&6; } # Now check for auxiliary declarations { $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct dirent64" >&5 $as_echo_n "checking for struct dirent64... " >&6; } if ${tcl_cv_struct_dirent64+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { struct dirent64 p; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_struct_dirent64=yes else tcl_cv_struct_dirent64=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_struct_dirent64" >&5 $as_echo "$tcl_cv_struct_dirent64" >&6; } if test "x${tcl_cv_struct_dirent64}" = "xyes" ; then $as_echo "#define HAVE_STRUCT_DIRENT64 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct stat64" >&5 $as_echo_n "checking for struct stat64... " >&6; } if ${tcl_cv_struct_stat64+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { struct stat64 p; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_struct_stat64=yes else tcl_cv_struct_stat64=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_struct_stat64" >&5 $as_echo "$tcl_cv_struct_stat64" >&6; } if test "x${tcl_cv_struct_stat64}" = "xyes" ; then $as_echo "#define HAVE_STRUCT_STAT64 1" >>confdefs.h fi for ac_func in open64 lseek64 do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for off64_t" >&5 $as_echo_n "checking for off64_t... " >&6; } if ${tcl_cv_type_off64_t+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { off64_t offset; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : tcl_cv_type_off64_t=yes else tcl_cv_type_off64_t=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test "x${tcl_cv_type_off64_t}" = "xyes" && \ test "x${ac_cv_func_lseek64}" = "xyes" && \ test "x${ac_cv_func_open64}" = "xyes" ; then $as_echo "#define HAVE_TYPE_OFF64_T 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi #-------------------------------------------------------------------- # Set the default compiler switches based on the --enable-symbols option. #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for build with symbols" >&5 $as_echo_n "checking for build with symbols... " >&6; } # Check whether --enable-symbols was given. if test "${enable_symbols+set}" = set; then : enableval=$enable_symbols; tcl_ok=$enableval else tcl_ok=no fi DBGX="" if test "$tcl_ok" = "no"; then CFLAGS_DEFAULT="${CFLAGS_OPTIMIZE} -DNDEBUG" LDFLAGS_DEFAULT="${LDFLAGS_OPTIMIZE}" { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } else CFLAGS_DEFAULT="${CFLAGS_DEBUG}" LDFLAGS_DEFAULT="${LDFLAGS_DEBUG}" if test "$tcl_ok" = "yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (standard debugging)" >&5 $as_echo "yes (standard debugging)" >&6; } fi fi # TEA specific: if test "${TEA_PLATFORM}" != "windows" ; then LDFLAGS_DEFAULT="${LDFLAGS}" fi if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then $as_echo "#define TCL_MEM_DEBUG 1" >>confdefs.h fi if test "$tcl_ok" != "yes" -a "$tcl_ok" != "no"; then if test "$tcl_ok" = "all"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled symbols mem debugging" >&5 $as_echo "enabled symbols mem debugging" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled $tcl_ok debugging" >&5 $as_echo "enabled $tcl_ok debugging" >&6; } fi fi #-------------------------------------------------------------------- # Everyone should be linking against the Tcl stub library. If you # can't for some reason, remove this definition. If you aren't using # stubs, you also need to modify the SHLIB_LD_LIBS setting below to # link against the non-stubbed Tcl library. Add Tk too if necessary. #-------------------------------------------------------------------- $as_echo "#define USE_TCL_STUBS 1" >>confdefs.h #AC_DEFINE(USE_TK_STUBS) if test "$enable_development" = yes; then $as_echo "#define NSF_DEVELOPMENT 1" >>confdefs.h fi #-------------------------------------------------------------------- # This macro generates a line to use when building a library. It # depends on values set by the TEA_ENABLE_SHARED, TEA_ENABLE_SYMBOLS, # and TEA_LOAD_TCLCONFIG macros above. #-------------------------------------------------------------------- if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes"; then MAKE_STATIC_LIB="\${STLIB_LD} -out:\$@ \$(PKG_OBJECTS)" MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LD_LIBS} \${LDFLAGS_DEFAULT} -out:\$@ \$(PKG_OBJECTS)" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if defined(_MSC_VER) && _MSC_VER >= 1400 print("manifest needed") #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "manifest needed" >/dev/null 2>&1; then : # Could do a CHECK_PROG for mt, but should always be with MSVC8+ VC_MANIFEST_EMBED_DLL="if test -f \$@.manifest ; then mt.exe -nologo -manifest \$@.manifest -outputresource:\$@\;2 ; fi" VC_MANIFEST_EMBED_EXE="if test -f \$@.manifest ; then mt.exe -nologo -manifest \$@.manifest -outputresource:\$@\;1 ; fi" MAKE_SHARED_LIB="${MAKE_SHARED_LIB} ; ${VC_MANIFEST_EMBED_DLL}" CLEANFILES="$CLEANFILES *.manifest" fi rm -f conftest* MAKE_STUB_LIB="\${STLIB_LD} -nodefaultlib -out:\$@ \$(PKG_STUB_OBJECTS)" else MAKE_STATIC_LIB="\${STLIB_LD} \$@ \$(PKG_OBJECTS)" MAKE_SHARED_LIB="\${SHLIB_LD} -o \$@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}" MAKE_STUB_LIB="\${STLIB_LD} \$@ \$(PKG_STUB_OBJECTS)" fi if test "${SHARED_BUILD}" = "1" ; then MAKE_LIB="${MAKE_SHARED_LIB} " else MAKE_LIB="${MAKE_STATIC_LIB} " fi #-------------------------------------------------------------------- # Shared libraries and static libraries have different names. # Use the double eval to make sure any variables in the suffix is # substituted. (@@@ Might not be necessary anymore) #-------------------------------------------------------------------- if test "${TEA_PLATFORM}" = "windows" ; then if test "${SHARED_BUILD}" = "1" ; then # We force the unresolved linking of symbols that are really in # the private libraries of Tcl and Tk. if test x"${TK_BIN_DIR}" != x ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TK_BIN_DIR}/${TK_STUB_LIB_FILE}`\"" fi SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}`\"" if test "$GCC" = "yes"; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -static-libgcc" fi eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}" else eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}" if test "$GCC" = "yes"; then PKG_LIB_FILE=lib${PKG_LIB_FILE} fi fi # Some packages build their own stubs libraries eval eval "PKG_STUB_LIB_FILE=${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}" if test "$GCC" = "yes"; then PKG_STUB_LIB_FILE=lib${PKG_STUB_LIB_FILE} fi # These aren't needed on Windows (either MSVC or gcc) RANLIB=: RANLIB_STUB=: else RANLIB_STUB="${RANLIB}" if test "${SHARED_BUILD}" = "1" ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TCL_STUB_LIB_SPEC}" if test x"${TK_BIN_DIR}" != x ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TK_STUB_LIB_SPEC}" fi eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${SHARED_LIB_SUFFIX}" RANLIB=: else eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}" fi # Some packages build their own stubs libraries eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}" fi # These are escaped so that only CFLAGS is picked up at configure time. # The other values will be substituted at make time. CFLAGS="${CFLAGS} \${CFLAGS_DEFAULT} \${CFLAGS_WARNING}" if test "${SHARED_BUILD}" = "1" ; then CFLAGS="${CFLAGS} \${SHLIB_CFLAGS}" fi #-------------------------------------------------------------------- # Find tclsh so that we can run pkg_mkIndex to generate the pkgIndex.tcl # file during the install process. Don't run the TCLSH_PROG through # ${CYGPATH} because it's being used directly by make. # Require that we use a tclsh shell version 8.2 or later since earlier # versions have bugs in the pkg_mkIndex routine. # Add WISH as well if this is a Tk extension. #-------------------------------------------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tclsh" >&5 $as_echo_n "checking for tclsh... " >&6; } if test -f "${TCL_BIN_DIR}/Makefile" ; then # tclConfig.sh is in Tcl build directory if test "${TEA_PLATFORM}" = "windows"; then TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}" else TCLSH_PROG="${TCL_BIN_DIR}/tclsh" fi else # tclConfig.sh is in install location if test "${TEA_PLATFORM}" = "windows"; then TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}" else TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_DBGX}" fi list="`ls -d ${TCL_BIN_DIR}/../bin 2>/dev/null` \ `ls -d ${TCL_BIN_DIR}/.. 2>/dev/null` \ `ls -d ${TCL_PREFIX}/bin 2>/dev/null`" for i in $list ; do if test -f "$i/${TCLSH_PROG}" ; then REAL_TCL_BIN_DIR="`cd "$i"; pwd`/" break fi done TCLSH_PROG="${REAL_TCL_BIN_DIR}${TCLSH_PROG}" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${TCLSH_PROG}" >&5 $as_echo "${TCLSH_PROG}" >&6; } #TEA_PROG_WISH #-------------------------------------------------------------------- # Finally, substitute all of the various values into the Makefile. # You may alternatively have a special pkgIndex.tcl.in or other files # which require substituting th AC variables in. Include these here. #-------------------------------------------------------------------- ac_config_files="$ac_config_files Makefile pkgIndex.add" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs CFLAGS="${CFLAGS} ${CPPFLAGS}"; CPPFLAGS="" : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by nsfmongo $as_me 0.3, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ nsfmongo config.status 0.3 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "pkgIndex.add") CONFIG_FILES="$CONFIG_FILES pkgIndex.add" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi library/mongodb/configure.ac000066400000000000000000000252061242365656200164550ustar00rootroot00000000000000#!/bin/bash -norc dnl This file is an input file used by the GNU "autoconf" program to dnl generate the file "configure", which is run during Tcl installation dnl to configure the system for the local environment. # #----------------------------------------------------------------------- # Sample configure.in for Tcl Extensions. The only places you should # need to modify this file are marked by the string __CHANGE__ #----------------------------------------------------------------------- configdir=$(srcdir)/../../tclconfig #----------------------------------------------------------------------- # __CHANGE__ # Set your package name and version numbers here. # # This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION # set as provided. These will also be added as -D defs in your Makefile # so you can encode the package version directly into the source files. #----------------------------------------------------------------------- AC_INIT([nsfmongo], [0.3]) AC_CONFIG_MACRO_DIR([m4]) #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. # This will define a ${TEA_PLATFORM} variable = "unix" or "windows" # as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE. #-------------------------------------------------------------------- TEA_INIT([3.9]) AC_CONFIG_AUX_DIR([../../tclconfig]) #-------------------------------------------------------------------- # specify some extra flags #-------------------------------------------------------------------- AC_ARG_WITH([mongoc], [ --with-mongoc=MONGOC_INCLUDE_DIR[,MONGOC_LIB_DIR] absolute path to mongo.h and optionally the path to the library, --without-mongoc disables build of the mongo interface], [with_mongoc=$withval], [with_mongoc=no]) AC_ARG_WITH([bson], [ --with-bson=BSON_INCLUDE_DIR[,BSON_LIB_DIR] absolute path to bson.h and optionally the path to the library, --without-bson disables build of the mongo interface], [with_bson=$withval], [with_bson=no]) AC_ARG_WITH([nsf], [ --with-nsf=DIR_CONTAINING_NSFCONFIG_SH absolute path to nsfConfig.sh, --without-nsf disables, but this is pointless], [with_nsf=$withval], [AC_MSG_ERROR([--with-nsf is required])]) AC_ARG_ENABLE([development], AS_HELP_STRING([--enable-development], [build nsf with development support (intensive runtime checking, etc.; default: disabled)]), [enable_development=$enableval], [enable_development=no]) #-------------------------------------------------------------------- # Load the tclConfig.sh file #-------------------------------------------------------------------- TEA_PATH_TCLCONFIG TEA_LOAD_TCLCONFIG #-------------------------------------------------------------------- # Load the tkConfig.sh file if necessary (Tk extension) #-------------------------------------------------------------------- #TEA_PATH_TKCONFIG #TEA_LOAD_TKCONFIG #----------------------------------------------------------------------- # Handle the --prefix=... option by defaulting to what Tcl gave. # Must be called after TEA_LOAD_TCLCONFIG and before TEA_SETUP_COMPILER. #----------------------------------------------------------------------- TEA_PREFIX #----------------------------------------------------------------------- # Standard compiler checks. # This sets up CC by using the CC env var, or looks for gcc otherwise. # This also calls AC_PROG_CC, AC_PROG_INSTALL and a few others to create # the basic setup necessary to compile executables. #----------------------------------------------------------------------- TEA_SETUP_COMPILER #----------------------------------------------------------------------- # __CHANGE__ # Specify the C source files to compile in TEA_ADD_SOURCES, # public headers that need to be installed in TEA_ADD_HEADERS, # stub library C source files to compile in TEA_ADD_STUB_SOURCES, # and runtime Tcl library files in TEA_ADD_TCL_SOURCES. # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS # and PKG_TCL_SOURCES. #----------------------------------------------------------------------- if test ! "${with_mongoc}" = no; then MONGOC_INC_DIR="`echo $with_mongoc |cut -f1 -d,`" MONGOC_LIB_DIR="`echo $with_mongoc |cut -f2 -d, -s`" fi if test ! "${with_bson}" = no; then BSON_INC_DIR="`echo $with_bson |cut -f1 -d,`" BSON_LIB_DIR="`echo $with_bson |cut -f2 -d, -s`" fi mongo_h_ok=1 if test -z "$MONGOC_INC_DIR" ; then mongo_h_ok=0 MONGO_INC_SPEC="" fi if test -z "$BSON_INC_DIR" ; then mongo_h_ok=0 MONGO_INC_SPEC="" fi if test "${mongo_h_ok}" = "1" ; then MONGO_INC_SPEC="-I${MONGOC_INC_DIR} -I${BSON_INC_DIR}" echo "Checking ${MONGOC_INC_DIR}/mongoc.h" if test ! -f "${MONGOC_INC_DIR}/mongoc.h" ; then mongo_h_ok=0 fi echo "Checking ${BSON_INC_DIR}/bson.h" if test ! -f "${BSON_INC_DIR}/bson.h" ; then mongo_h_ok=0 fi fi if test "${mongo_h_ok}" = "0" ; then AC_MSG_ERROR([ Could not locate bson.h and mongoc.h on your machine to build the nsf mongo interface. ]) fi if test -z "${MONGOC_LIB_DIR}" ; then MONGO_LIB_SPEC="" else MONGO_LIB_SPEC="-L${MONGOC_LIB_DIR}" fi if test ! -z "${BSON_LIB_DIR}" ; then MONGO_LIB_SPEC="${MONGO_LIB_SPEC} -L${BSON_LIB_DIR}" fi #echo "MONGO include spec = '${MONGO_INC_SPEC}'" #echo "MONGO lib spec = '${MONGO_LIB_SPEC}'" #-------------------------------------------------------------------- # Load the nsfConfig.sh file #-------------------------------------------------------------------- AC_MSG_NOTICE([Reading file ${with_nsf}/nsfConfig.sh]) source ${with_nsf}/nsfConfig.sh TEA_ADD_SOURCES([nsfmongo.c]) TEA_ADD_HEADERS([]) TEA_ADD_INCLUDES([-I${with_nsf}/generic ${NSF_BUILD_INCLUDE_SPEC} ${MONGO_INC_SPEC}]) #TEA_ADD_LIBS([$NSF_BUILD_STUB_LIB_SPEC $MONGO_LIB_SPEC -Wl,-rpath,${MONGO_LIB_DIR} -L${MONGO_LIB_DIR} -lmongoc -lbson]) #TEA_ADD_LIBS([$NSF_BUILD_STUB_LIB_SPEC $MONGO_LIB_SPEC -L${MONGO_LIB_DIR} -lmongoc -lbson]) TEA_ADD_LIBS([$NSF_BUILD_STUB_LIB_SPEC $MONGO_LIB_SPEC -lmongoc-1.0 -lbson-1.0]) TEA_ADD_CFLAGS([]) TEA_ADD_STUB_SOURCES([]) TEA_ADD_TCL_SOURCES([]) #-------------------------------------------------------------------- # __CHANGE__ # A few miscellaneous platform-specific items: # # Define a special symbol for Windows (BUILD_sample in this case) so # that we create the export library with the dll. # # Windows creates a few extra files that need to be cleaned up. # You can add more files to clean if your extension creates any extra # files. # # TEA_ADD_* any platform specific compiler/build info here. #-------------------------------------------------------------------- if test "${TEA_PLATFORM}" = "windows" ; then CLEANFILES="pkgIndex.tcl *.lib *.dll *.exp *.ilk *.pdb vc*.pch" #TEA_ADD_SOURCES([win/winFile.c]) #TEA_ADD_INCLUDES([-I\"$(${CYGPATH} ${srcdir}/win)\"]) else CLEANFILES="pkgIndex.tcl" #TEA_ADD_SOURCES([unix/unixFile.c]) #TEA_ADD_LIBS([-lsuperfly]) fi AC_SUBST([CLEANFILES]) #-------------------------------------------------------------------- # __CHANGE__ # Choose which headers you need. Extension authors should try very # hard to only rely on the Tcl public header files. Internal headers # contain private data structures and are subject to change without # notice. # This MUST be called after TEA_LOAD_TCLCONFIG / TEA_LOAD_TKCONFIG #-------------------------------------------------------------------- TEA_PUBLIC_TCL_HEADERS #TEA_PRIVATE_TCL_HEADERS #TEA_PUBLIC_TK_HEADERS #TEA_PRIVATE_TK_HEADERS #TEA_PATH_X #-------------------------------------------------------------------- # Check whether --enable-threads or --disable-threads was given. #-------------------------------------------------------------------- TEA_ENABLE_THREADS #-------------------------------------------------------------------- # The statement below defines a collection of symbols related to # building as a shared library instead of a static library. #-------------------------------------------------------------------- TEA_ENABLE_SHARED #-------------------------------------------------------------------- # This macro figures out what flags to use with the compiler/linker # when building shared/static debug/optimized objects. This information # can be taken from the tclConfig.sh file, but this figures it all out. #-------------------------------------------------------------------- TEA_CONFIG_CFLAGS #-------------------------------------------------------------------- # Set the default compiler switches based on the --enable-symbols option. #-------------------------------------------------------------------- TEA_ENABLE_SYMBOLS #-------------------------------------------------------------------- # Everyone should be linking against the Tcl stub library. If you # can't for some reason, remove this definition. If you aren't using # stubs, you also need to modify the SHLIB_LD_LIBS setting below to # link against the non-stubbed Tcl library. Add Tk too if necessary. #-------------------------------------------------------------------- AC_DEFINE([USE_TCL_STUBS]) #AC_DEFINE([USE_TK_STUBS]) if test "$enable_development" = yes; then AC_DEFINE([NSF_DEVELOPMENT], [1], [Are we building with development support?]) fi #-------------------------------------------------------------------- # This macro generates a line to use when building a library. It # depends on values set by the TEA_ENABLE_SHARED, TEA_ENABLE_SYMBOLS, # and TEA_LOAD_TCLCONFIG macros above. #-------------------------------------------------------------------- TEA_MAKE_LIB #-------------------------------------------------------------------- # Find tclsh so that we can run pkg_mkIndex to generate the pkgIndex.tcl # file during the install process. Don't run the TCLSH_PROG through # ${CYGPATH} because it's being used directly by make. # Require that we use a tclsh shell version 8.2 or later since earlier # versions have bugs in the pkg_mkIndex routine. # Add WISH as well if this is a Tk extension. #-------------------------------------------------------------------- TEA_PROG_TCLSH #TEA_PROG_WISH #-------------------------------------------------------------------- # Finally, substitute all of the various values into the Makefile. # You may alternatively have a special pkgIndex.tcl.in or other files # which require substituting th AC variables in. Include these here. #-------------------------------------------------------------------- AC_CONFIG_FILES([Makefile pkgIndex.add]) AC_OUTPUT library/mongodb/m4/000077500000000000000000000000001242365656200145025ustar00rootroot00000000000000library/mongodb/m4/nsf.m4000066400000000000000000000073731242365656200155440ustar00rootroot00000000000000# nsf.m4 -- # # This file provides a set of autoconf macros to help TEA-enable # a Tcl extension. # # Copyright (c) 1999 Scriptics Corporation. # Copyright (c) 1999-2008 Uwe Zdun # Copyright (c) 1999-2014 Gustaf Neumann # # See the file "tcl-license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. #------------------------------------------------------------------------ # SC_PATH_NSFCONFIG -- # # Locate the nsfConfig.sh file and perform a sanity check on # the Tcl compile flags # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --with-nsf=... # # Defines the following vars: # NX_BIN_DIR Full path to the directory containing # the nsfConfig.sh file #------------------------------------------------------------------------ AC_DEFUN(SC_PATH_NSFCONFIG, [ # # Ok, lets find the tcl configuration # First, look for one uninstalled. # the alternative search directory is invoked by --with-tcl # if test x"${no_nsf}" = x ; then # we reset no_nsf in case something fails here no_nsf=true AC_ARG_WITH(nsf, [ --with-nsf directory containing nsf configuration (nsfConfig.sh)], with_nsfconfig=${withval}) AC_MSG_CHECKING([for nsf configuration]) AC_CACHE_VAL(ac_cv_c_nsfconfig,[ # First check to see if --with-nsf was specified. if test x"${with_nsfconfig}" != x ; then if test -f "${with_nsfconfig}/nsfConfig.sh" ; then ac_cv_c_nsfconfig=`(cd ${with_nsfconfig}; pwd)` else AC_MSG_ERROR([${with_nsfconfig} directory doesn't contain nsfConfig.sh]) fi fi # then check for a private Tcl installation if test x"${ac_cv_c_nsfconfig}" = x ; then for i in \ ${srcdir}/../nsf \ `ls -dr ${srcdir}/../nsf-* 2>/dev/null` \ ${srcdir}/../../nsf \ `ls -dr ${srcdir}/../../nsf-* 2>/dev/null` \ ${srcdir}/../../../nsf \ `ls -dr ${srcdir}/../../../nsf-* 2>/dev/null` \ ${srcdir}/../../../../nsf \ `ls -dr ${srcdir}/../../../../nsf-* 2>/dev/null` \ ${srcdir}/../../../../../nsf \ `ls -dr ${srcdir}/../../../../../nsf-* 2>/dev/null` ; do if test -f "$i/nsfConfig.sh" ; then ac_cv_c_nsfconfig=`(cd $i; pwd)` break fi done fi # check in a few common install locations if test x"${ac_cv_c_nsfconfig}" = x ; then for i in `ls -d ${prefix}/lib 2>/dev/null` \ `ls -d /usr/local/lib 2>/dev/null` ; do if test -f "$i/nsfConfig.sh" ; then ac_cv_c_nsfconfig=`(cd $i; pwd)` break fi done fi ]) if test x"${ac_cv_c_nsfconfig}" = x ; then NX_BIN_DIR="# no nsf configs found" AC_MSG_WARN(Can't find nsf configuration definitions) exit 0 else no_nsf= NX_BIN_DIR=${ac_cv_c_nsfconfig} AC_MSG_RESULT(found $NX_BIN_DIR/nsfConfig.sh) fi fi ]) #------------------------------------------------------------------------ # SC_LOAD_NSFCONFIG -- # # Load the tclConfig.sh file # # Arguments: # # Requires the following vars to be set: # NX_BIN_DIR # # Results: # # Subst the vars: # #------------------------------------------------------------------------ AC_DEFUN(SC_LOAD_NSFCONFIG, [ AC_MSG_CHECKING([for existence of $NX_BIN_DIR/nsfConfig.sh]) if test -f "$NX_BIN_DIR/nsfConfig.sh" ; then AC_MSG_RESULT([loading]) . $NX_BIN_DIR/nsfConfig.sh else AC_MSG_RESULT([file not found]) fi # # The eval is required to do the TCL_DBGX substitution in the # TCL_LIB_FILE variable # AC_SUBST(NX_VERSION) AC_SUBST(NX_MAJOR_VERSION) AC_SUBST(NX_MINOR_VERSION) AC_SUBST(NX_RELEASE_LEVEL) AC_SUBST(NX_LIB_FILE) AC_SUBST(NX_BUILD_LIB_SPEC) AC_SUBST(NX_LIB_SPEC) AC_SUBST(NX_STUB_LIB_FILE) AC_SUBST(NX_BUILD_STUB_LIB_SPEC) AC_SUBST(NX_STUB_LIB_SPEC) AC_SUBST(NX_SRC_DIR) ]) library/mongodb/m4/tcl.m4000066400000000000000000004056471242365656200155460ustar00rootroot00000000000000# tcl.m4 -- # # This file provides a set of autoconf macros to help TEA-enable # a Tcl extension. # # Copyright (c) 1999-2000 Ajuba Solutions. # Copyright (c) 2002-2005 ActiveState Corporation. # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. AC_PREREQ(2.57) dnl TEA extensions pass us the version of TEA they think they dnl are compatible with (must be set in TEA_INIT below) dnl TEA_VERSION="3.9" # Possible values for key variables defined: # # TEA_WINDOWINGSYSTEM - win32 aqua x11 (mirrors 'tk windowingsystem') # TEA_PLATFORM - windows unix # #------------------------------------------------------------------------ # TEA_PATH_TCLCONFIG -- # # Locate the tclConfig.sh file and perform a sanity check on # the Tcl compile flags # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --with-tcl=... # # Defines the following vars: # TCL_BIN_DIR Full path to the directory containing # the tclConfig.sh file #------------------------------------------------------------------------ AC_DEFUN([TEA_PATH_TCLCONFIG], [ dnl TEA specific: Make sure we are initialized AC_REQUIRE([TEA_INIT]) # # Ok, lets find the tcl configuration # First, look for one uninstalled. # the alternative search directory is invoked by --with-tcl # if test x"${no_tcl}" = x ; then # we reset no_tcl in case something fails here no_tcl=true AC_ARG_WITH(tcl, AC_HELP_STRING([--with-tcl], [directory containing tcl configuration (tclConfig.sh)]), with_tclconfig="${withval}") AC_MSG_CHECKING([for Tcl configuration]) AC_CACHE_VAL(ac_cv_c_tclconfig,[ # First check to see if --with-tcl was specified. if test x"${with_tclconfig}" != x ; then case "${with_tclconfig}" in */tclConfig.sh ) if test -f "${with_tclconfig}"; then AC_MSG_WARN([--with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself]) with_tclconfig="`echo "${with_tclconfig}" | sed 's!/tclConfig\.sh$!!'`" fi ;; esac if test -f "${with_tclconfig}/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd "${with_tclconfig}"; pwd)`" else AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh]) fi fi # then check for a private Tcl installation if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ ../tcl \ `ls -dr ../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../tcl[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../tcl[[8-9]].[[0-9]]* 2>/dev/null` \ ../../tcl \ `ls -dr ../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../../tcl[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \ ../../../tcl \ `ls -dr ../../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../../../tcl[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/unix; pwd)`" break fi done fi # on Darwin, check in Framework installation locations if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d ~/Library/Frameworks 2>/dev/null` \ `ls -d /Library/Frameworks 2>/dev/null` \ `ls -d /Network/Library/Frameworks 2>/dev/null` \ `ls -d /System/Library/Frameworks 2>/dev/null` \ ; do if test -f "$i/Tcl.framework/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/Tcl.framework; pwd)`" break fi done fi # TEA specific: on Windows, check in common installation locations if test "${TEA_PLATFORM}" = "windows" \ -a x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d C:/Tcl/lib 2>/dev/null` \ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \ ; do if test -f "$i/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i; pwd)`" break fi done fi # check in a few common install locations if test x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d ${libdir} 2>/dev/null` \ `ls -d ${exec_prefix}/lib 2>/dev/null` \ `ls -d ${prefix}/lib 2>/dev/null` \ `ls -d /usr/local/lib 2>/dev/null` \ `ls -d /usr/contrib/lib 2>/dev/null` \ `ls -d /usr/lib 2>/dev/null` \ `ls -d /usr/lib64 2>/dev/null` \ `ls -d /usr/lib/tcl8.6 2>/dev/null` \ `ls -d /usr/lib/tcl8.5 2>/dev/null` \ ; do if test -f "$i/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i; pwd)`" break fi done fi # check in a few other private locations if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ ${srcdir}/../tcl \ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/unix; pwd)`" break fi done fi ]) if test x"${ac_cv_c_tclconfig}" = x ; then TCL_BIN_DIR="# no Tcl configs found" AC_MSG_ERROR([Can't find Tcl configuration definitions. Use --with-tcl to specify a directory containing tclConfig.sh]) else no_tcl= TCL_BIN_DIR="${ac_cv_c_tclconfig}" AC_MSG_RESULT([found ${TCL_BIN_DIR}/tclConfig.sh]) fi fi ]) #------------------------------------------------------------------------ # TEA_PATH_TKCONFIG -- # # Locate the tkConfig.sh file # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --with-tk=... # # Defines the following vars: # TK_BIN_DIR Full path to the directory containing # the tkConfig.sh file #------------------------------------------------------------------------ AC_DEFUN([TEA_PATH_TKCONFIG], [ # # Ok, lets find the tk configuration # First, look for one uninstalled. # the alternative search directory is invoked by --with-tk # if test x"${no_tk}" = x ; then # we reset no_tk in case something fails here no_tk=true AC_ARG_WITH(tk, AC_HELP_STRING([--with-tk], [directory containing tk configuration (tkConfig.sh)]), with_tkconfig="${withval}") AC_MSG_CHECKING([for Tk configuration]) AC_CACHE_VAL(ac_cv_c_tkconfig,[ # First check to see if --with-tkconfig was specified. if test x"${with_tkconfig}" != x ; then case "${with_tkconfig}" in */tkConfig.sh ) if test -f "${with_tkconfig}"; then AC_MSG_WARN([--with-tk argument should refer to directory containing tkConfig.sh, not to tkConfig.sh itself]) with_tkconfig="`echo "${with_tkconfig}" | sed 's!/tkConfig\.sh$!!'`" fi ;; esac if test -f "${with_tkconfig}/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd "${with_tkconfig}"; pwd)`" else AC_MSG_ERROR([${with_tkconfig} directory doesn't contain tkConfig.sh]) fi fi # then check for a private Tk library if test x"${ac_cv_c_tkconfig}" = x ; then for i in \ ../tk \ `ls -dr ../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../tk[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../tk[[8-9]].[[0-9]]* 2>/dev/null` \ ../../tk \ `ls -dr ../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../../tk[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../tk[[8-9]].[[0-9]]* 2>/dev/null` \ ../../../tk \ `ls -dr ../../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../../../tk[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i/unix; pwd)`" break fi done fi # on Darwin, check in Framework installation locations if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tkconfig}" = x ; then for i in `ls -d ~/Library/Frameworks 2>/dev/null` \ `ls -d /Library/Frameworks 2>/dev/null` \ `ls -d /Network/Library/Frameworks 2>/dev/null` \ `ls -d /System/Library/Frameworks 2>/dev/null` \ ; do if test -f "$i/Tk.framework/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i/Tk.framework; pwd)`" break fi done fi # check in a few common install locations if test x"${ac_cv_c_tkconfig}" = x ; then for i in `ls -d ${libdir} 2>/dev/null` \ `ls -d ${exec_prefix}/lib 2>/dev/null` \ `ls -d ${prefix}/lib 2>/dev/null` \ `ls -d /usr/local/lib 2>/dev/null` \ `ls -d /usr/contrib/lib 2>/dev/null` \ `ls -d /usr/lib 2>/dev/null` \ `ls -d /usr/lib64 2>/dev/null` \ ; do if test -f "$i/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i; pwd)`" break fi done fi # TEA specific: on Windows, check in common installation locations if test "${TEA_PLATFORM}" = "windows" \ -a x"${ac_cv_c_tkconfig}" = x ; then for i in `ls -d C:/Tcl/lib 2>/dev/null` \ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \ ; do if test -f "$i/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i; pwd)`" break fi done fi # check in a few other private locations if test x"${ac_cv_c_tkconfig}" = x ; then for i in \ ${srcdir}/../tk \ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i/unix; pwd)`" break fi done fi ]) if test x"${ac_cv_c_tkconfig}" = x ; then TK_BIN_DIR="# no Tk configs found" AC_MSG_ERROR([Can't find Tk configuration definitions. Use --with-tk to specify a directory containing tkConfig.sh]) else no_tk= TK_BIN_DIR="${ac_cv_c_tkconfig}" AC_MSG_RESULT([found ${TK_BIN_DIR}/tkConfig.sh]) fi fi ]) #------------------------------------------------------------------------ # TEA_LOAD_TCLCONFIG -- # # Load the tclConfig.sh file # # Arguments: # # Requires the following vars to be set: # TCL_BIN_DIR # # Results: # # Substitutes the following vars: # TCL_BIN_DIR # TCL_SRC_DIR # TCL_LIB_FILE #------------------------------------------------------------------------ AC_DEFUN([TEA_LOAD_TCLCONFIG], [ AC_MSG_CHECKING([for existence of ${TCL_BIN_DIR}/tclConfig.sh]) if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then AC_MSG_RESULT([loading]) . "${TCL_BIN_DIR}/tclConfig.sh" else AC_MSG_RESULT([could not find ${TCL_BIN_DIR}/tclConfig.sh]) fi # eval is required to do the TCL_DBGX substitution eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\"" eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\"" # If the TCL_BIN_DIR is the build directory (not the install directory), # then set the common variable name to the value of the build variables. # For example, the variable TCL_LIB_SPEC will be set to the value # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC # instead of TCL_BUILD_LIB_SPEC since it will work with both an # installed and uninstalled version of Tcl. if test -f "${TCL_BIN_DIR}/Makefile" ; then TCL_LIB_SPEC="${TCL_BUILD_LIB_SPEC}" TCL_STUB_LIB_SPEC="${TCL_BUILD_STUB_LIB_SPEC}" TCL_STUB_LIB_PATH="${TCL_BUILD_STUB_LIB_PATH}" elif test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use the libraries # from the framework at the given location so that linking works # against Tcl.framework installed in an arbitrary location. case ${TCL_DEFS} in *TCL_FRAMEWORK*) if test -f "${TCL_BIN_DIR}/${TCL_LIB_FILE}"; then for i in "`cd "${TCL_BIN_DIR}"; pwd`" \ "`cd "${TCL_BIN_DIR}"/../..; pwd`"; do if test "`basename "$i"`" = "${TCL_LIB_FILE}.framework"; then TCL_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TCL_LIB_FILE}" break fi done fi if test -f "${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"; then TCL_STUB_LIB_SPEC="-L`echo "${TCL_BIN_DIR}" | sed -e 's/ /\\\\ /g'` ${TCL_STUB_LIB_FLAG}" TCL_STUB_LIB_PATH="${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}" fi ;; esac fi # eval is required to do the TCL_DBGX substitution eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\"" eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\"" eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\"" eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\"" AC_SUBST(TCL_VERSION) AC_SUBST(TCL_PATCH_LEVEL) AC_SUBST(TCL_BIN_DIR) AC_SUBST(TCL_SRC_DIR) AC_SUBST(TCL_LIB_FILE) AC_SUBST(TCL_LIB_FLAG) AC_SUBST(TCL_LIB_SPEC) AC_SUBST(TCL_STUB_LIB_FILE) AC_SUBST(TCL_STUB_LIB_FLAG) AC_SUBST(TCL_STUB_LIB_SPEC) AC_MSG_CHECKING([platform]) hold_cc=$CC; CC="$TCL_CC" AC_TRY_COMPILE(,[ #ifdef _WIN32 #error win32 #endif ], TEA_PLATFORM="unix", TEA_PLATFORM="windows" ) CC=$hold_cc AC_MSG_RESULT($TEA_PLATFORM) # The BUILD_$pkg is to define the correct extern storage class # handling when making this package AC_DEFINE_UNQUOTED(BUILD_${PACKAGE_NAME}, [], [Building extension source?]) # Do this here as we have fully defined TEA_PLATFORM now if test "${TEA_PLATFORM}" = "windows" ; then EXEEXT=".exe" CLEANFILES="$CLEANFILES *.lib *.dll *.pdb *.exp" fi # TEA specific: AC_SUBST(CLEANFILES) AC_SUBST(TCL_LIBS) AC_SUBST(TCL_DEFS) AC_SUBST(TCL_EXTRA_CFLAGS) AC_SUBST(TCL_LD_FLAGS) AC_SUBST(TCL_SHLIB_LD_LIBS) ]) #------------------------------------------------------------------------ # TEA_LOAD_TKCONFIG -- # # Load the tkConfig.sh file # # Arguments: # # Requires the following vars to be set: # TK_BIN_DIR # # Results: # # Sets the following vars that should be in tkConfig.sh: # TK_BIN_DIR #------------------------------------------------------------------------ AC_DEFUN([TEA_LOAD_TKCONFIG], [ AC_MSG_CHECKING([for existence of ${TK_BIN_DIR}/tkConfig.sh]) if test -f "${TK_BIN_DIR}/tkConfig.sh" ; then AC_MSG_RESULT([loading]) . "${TK_BIN_DIR}/tkConfig.sh" else AC_MSG_RESULT([could not find ${TK_BIN_DIR}/tkConfig.sh]) fi # eval is required to do the TK_DBGX substitution eval "TK_LIB_FILE=\"${TK_LIB_FILE}\"" eval "TK_STUB_LIB_FILE=\"${TK_STUB_LIB_FILE}\"" # If the TK_BIN_DIR is the build directory (not the install directory), # then set the common variable name to the value of the build variables. # For example, the variable TK_LIB_SPEC will be set to the value # of TK_BUILD_LIB_SPEC. An extension should make use of TK_LIB_SPEC # instead of TK_BUILD_LIB_SPEC since it will work with both an # installed and uninstalled version of Tcl. if test -f "${TK_BIN_DIR}/Makefile" ; then TK_LIB_SPEC="${TK_BUILD_LIB_SPEC}" TK_STUB_LIB_SPEC="${TK_BUILD_STUB_LIB_SPEC}" TK_STUB_LIB_PATH="${TK_BUILD_STUB_LIB_PATH}" elif test "`uname -s`" = "Darwin"; then # If Tk was built as a framework, attempt to use the libraries # from the framework at the given location so that linking works # against Tk.framework installed in an arbitrary location. case ${TK_DEFS} in *TK_FRAMEWORK*) if test -f "${TK_BIN_DIR}/${TK_LIB_FILE}"; then for i in "`cd "${TK_BIN_DIR}"; pwd`" \ "`cd "${TK_BIN_DIR}"/../..; pwd`"; do if test "`basename "$i"`" = "${TK_LIB_FILE}.framework"; then TK_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TK_LIB_FILE}" break fi done fi if test -f "${TK_BIN_DIR}/${TK_STUB_LIB_FILE}"; then TK_STUB_LIB_SPEC="-L` echo "${TK_BIN_DIR}" | sed -e 's/ /\\\\ /g'` ${TK_STUB_LIB_FLAG}" TK_STUB_LIB_PATH="${TK_BIN_DIR}/${TK_STUB_LIB_FILE}" fi ;; esac fi # eval is required to do the TK_DBGX substitution eval "TK_LIB_FLAG=\"${TK_LIB_FLAG}\"" eval "TK_LIB_SPEC=\"${TK_LIB_SPEC}\"" eval "TK_STUB_LIB_FLAG=\"${TK_STUB_LIB_FLAG}\"" eval "TK_STUB_LIB_SPEC=\"${TK_STUB_LIB_SPEC}\"" # TEA specific: Ensure windowingsystem is defined if test "${TEA_PLATFORM}" = "unix" ; then case ${TK_DEFS} in *MAC_OSX_TK*) AC_DEFINE(MAC_OSX_TK, 1, [Are we building against Mac OS X TkAqua?]) TEA_WINDOWINGSYSTEM="aqua" ;; *) TEA_WINDOWINGSYSTEM="x11" ;; esac elif test "${TEA_PLATFORM}" = "windows" ; then TEA_WINDOWINGSYSTEM="win32" fi AC_SUBST(TK_VERSION) AC_SUBST(TK_BIN_DIR) AC_SUBST(TK_SRC_DIR) AC_SUBST(TK_LIB_FILE) AC_SUBST(TK_LIB_FLAG) AC_SUBST(TK_LIB_SPEC) AC_SUBST(TK_STUB_LIB_FILE) AC_SUBST(TK_STUB_LIB_FLAG) AC_SUBST(TK_STUB_LIB_SPEC) # TEA specific: AC_SUBST(TK_LIBS) AC_SUBST(TK_XINCLUDES) ]) #------------------------------------------------------------------------ # TEA_PROG_TCLSH # Determine the fully qualified path name of the tclsh executable # in the Tcl build directory or the tclsh installed in a bin # directory. This macro will correctly determine the name # of the tclsh executable even if tclsh has not yet been # built in the build directory. The tclsh found is always # associated with a tclConfig.sh file. This tclsh should be used # only for running extension test cases. It should never be # or generation of files (like pkgIndex.tcl) at build time. # # Arguments: # none # # Results: # Substitutes the following vars: # TCLSH_PROG #------------------------------------------------------------------------ AC_DEFUN([TEA_PROG_TCLSH], [ AC_MSG_CHECKING([for tclsh]) if test -f "${TCL_BIN_DIR}/Makefile" ; then # tclConfig.sh is in Tcl build directory if test "${TEA_PLATFORM}" = "windows"; then TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}" else TCLSH_PROG="${TCL_BIN_DIR}/tclsh" fi else # tclConfig.sh is in install location if test "${TEA_PLATFORM}" = "windows"; then TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}" else TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_DBGX}" fi list="`ls -d ${TCL_BIN_DIR}/../bin 2>/dev/null` \ `ls -d ${TCL_BIN_DIR}/.. 2>/dev/null` \ `ls -d ${TCL_PREFIX}/bin 2>/dev/null`" for i in $list ; do if test -f "$i/${TCLSH_PROG}" ; then REAL_TCL_BIN_DIR="`cd "$i"; pwd`/" break fi done TCLSH_PROG="${REAL_TCL_BIN_DIR}${TCLSH_PROG}" fi AC_MSG_RESULT([${TCLSH_PROG}]) AC_SUBST(TCLSH_PROG) ]) #------------------------------------------------------------------------ # TEA_PROG_WISH # Determine the fully qualified path name of the wish executable # in the Tk build directory or the wish installed in a bin # directory. This macro will correctly determine the name # of the wish executable even if wish has not yet been # built in the build directory. The wish found is always # associated with a tkConfig.sh file. This wish should be used # only for running extension test cases. It should never be # or generation of files (like pkgIndex.tcl) at build time. # # Arguments: # none # # Results: # Substitutes the following vars: # WISH_PROG #------------------------------------------------------------------------ AC_DEFUN([TEA_PROG_WISH], [ AC_MSG_CHECKING([for wish]) if test -f "${TK_BIN_DIR}/Makefile" ; then # tkConfig.sh is in Tk build directory if test "${TEA_PLATFORM}" = "windows"; then WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}" else WISH_PROG="${TK_BIN_DIR}/wish" fi else # tkConfig.sh is in install location if test "${TEA_PLATFORM}" = "windows"; then WISH_PROG="wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}" else WISH_PROG="wish${TK_MAJOR_VERSION}.${TK_MINOR_VERSION}${TK_DBGX}" fi list="`ls -d ${TK_BIN_DIR}/../bin 2>/dev/null` \ `ls -d ${TK_BIN_DIR}/.. 2>/dev/null` \ `ls -d ${TK_PREFIX}/bin 2>/dev/null`" for i in $list ; do if test -f "$i/${WISH_PROG}" ; then REAL_TK_BIN_DIR="`cd "$i"; pwd`/" break fi done WISH_PROG="${REAL_TK_BIN_DIR}${WISH_PROG}" fi AC_MSG_RESULT([${WISH_PROG}]) AC_SUBST(WISH_PROG) ]) #------------------------------------------------------------------------ # TEA_ENABLE_SHARED -- # # Allows the building of shared libraries # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --enable-shared=yes|no # # Defines the following vars: # STATIC_BUILD Used for building import/export libraries # on Windows. # # Sets the following vars: # SHARED_BUILD Value of 1 or 0 #------------------------------------------------------------------------ AC_DEFUN([TEA_ENABLE_SHARED], [ AC_MSG_CHECKING([how to build libraries]) AC_ARG_ENABLE(shared, AC_HELP_STRING([--enable-shared], [build and link with shared libraries (default: on)]), [tcl_ok=$enableval], [tcl_ok=yes]) if test "${enable_shared+set}" = set; then enableval="$enable_shared" tcl_ok=$enableval else tcl_ok=yes fi if test "$tcl_ok" = "yes" ; then AC_MSG_RESULT([shared]) SHARED_BUILD=1 else AC_MSG_RESULT([static]) SHARED_BUILD=0 AC_DEFINE(STATIC_BUILD, 1, [Is this a static build?]) fi AC_SUBST(SHARED_BUILD) ]) #------------------------------------------------------------------------ # TEA_ENABLE_THREADS -- # # Specify if thread support should be enabled. If "yes" is specified # as an arg (optional), threads are enabled by default, "no" means # threads are disabled. "yes" is the default. # # TCL_THREADS is checked so that if you are compiling an extension # against a threaded core, your extension must be compiled threaded # as well. # # Note that it is legal to have a thread enabled extension run in a # threaded or non-threaded Tcl core, but a non-threaded extension may # only run in a non-threaded Tcl core. # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --enable-threads # # Sets the following vars: # THREADS_LIBS Thread library(s) # # Defines the following vars: # TCL_THREADS # _REENTRANT # _THREAD_SAFE #------------------------------------------------------------------------ AC_DEFUN([TEA_ENABLE_THREADS], [ AC_ARG_ENABLE(threads, AC_HELP_STRING([--enable-threads], [build with threads]), [tcl_ok=$enableval], [tcl_ok=yes]) if test "${enable_threads+set}" = set; then enableval="$enable_threads" tcl_ok=$enableval else tcl_ok=yes fi if test "$tcl_ok" = "yes" -o "${TCL_THREADS}" = 1; then TCL_THREADS=1 if test "${TEA_PLATFORM}" != "windows" ; then # We are always OK on Windows, so check what this platform wants: # USE_THREAD_ALLOC tells us to try the special thread-based # allocator that significantly reduces lock contention AC_DEFINE(USE_THREAD_ALLOC, 1, [Do we want to use the threaded memory allocator?]) AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?]) if test "`uname -s`" = "SunOS" ; then AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Do we really want to follow the standard? Yes we do!]) fi AC_DEFINE(_THREAD_SAFE, 1, [Do we want the thread-safe OS API?]) AC_CHECK_LIB(pthread,pthread_mutex_init,tcl_ok=yes,tcl_ok=no) if test "$tcl_ok" = "no"; then # Check a little harder for __pthread_mutex_init in the same # library, as some systems hide it there until pthread.h is # defined. We could alternatively do an AC_TRY_COMPILE with # pthread.h, but that will work with libpthread really doesn't # exist, like AIX 4.2. [Bug: 4359] AC_CHECK_LIB(pthread, __pthread_mutex_init, tcl_ok=yes, tcl_ok=no) fi if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -lpthread" else AC_CHECK_LIB(pthreads, pthread_mutex_init, tcl_ok=yes, tcl_ok=no) if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -lpthreads" else AC_CHECK_LIB(c, pthread_mutex_init, tcl_ok=yes, tcl_ok=no) if test "$tcl_ok" = "no"; then AC_CHECK_LIB(c_r, pthread_mutex_init, tcl_ok=yes, tcl_ok=no) if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -pthread" else TCL_THREADS=0 AC_MSG_WARN([Do not know how to find pthread lib on your system - thread support disabled]) fi fi fi fi fi else TCL_THREADS=0 fi # Do checking message here to not mess up interleaved configure output AC_MSG_CHECKING([for building with threads]) if test "${TCL_THREADS}" = 1; then AC_DEFINE(TCL_THREADS, 1, [Are we building with threads enabled?]) AC_MSG_RESULT([yes (default)]) else AC_MSG_RESULT([no]) fi # TCL_THREADS sanity checking. See if our request for building with # threads is the same as the way Tcl was built. If not, warn the user. case ${TCL_DEFS} in *THREADS=1*) if test "${TCL_THREADS}" = "0"; then AC_MSG_WARN([ Building ${PACKAGE_NAME} without threads enabled, but building against Tcl that IS thread-enabled. It is recommended to use --enable-threads.]) fi ;; *) if test "${TCL_THREADS}" = "1"; then AC_MSG_WARN([ --enable-threads requested, but building against a Tcl that is NOT thread-enabled. This is an OK configuration that will also run in a thread-enabled core.]) fi ;; esac AC_SUBST(TCL_THREADS) ]) #------------------------------------------------------------------------ # TEA_ENABLE_SYMBOLS -- # # Specify if debugging symbols should be used. # Memory (TCL_MEM_DEBUG) debugging can also be enabled. # # Arguments: # none # # TEA varies from core Tcl in that C|LDFLAGS_DEFAULT receives # the value of C|LDFLAGS_OPTIMIZE|DEBUG already substituted. # Requires the following vars to be set in the Makefile: # CFLAGS_DEFAULT # LDFLAGS_DEFAULT # # Results: # # Adds the following arguments to configure: # --enable-symbols # # Defines the following vars: # CFLAGS_DEFAULT Sets to $(CFLAGS_DEBUG) if true # Sets to "$(CFLAGS_OPTIMIZE) -DNDEBUG" if false # LDFLAGS_DEFAULT Sets to $(LDFLAGS_DEBUG) if true # Sets to $(LDFLAGS_OPTIMIZE) if false # DBGX Formerly used as debug library extension; # always blank now. #------------------------------------------------------------------------ AC_DEFUN([TEA_ENABLE_SYMBOLS], [ dnl TEA specific: Make sure we are initialized AC_REQUIRE([TEA_CONFIG_CFLAGS]) AC_MSG_CHECKING([for build with symbols]) AC_ARG_ENABLE(symbols, AC_HELP_STRING([--enable-symbols], [build with debugging symbols (default: off)]), [tcl_ok=$enableval], [tcl_ok=no]) DBGX="" if test "$tcl_ok" = "no"; then CFLAGS_DEFAULT="${CFLAGS_OPTIMIZE} -DNDEBUG" LDFLAGS_DEFAULT="${LDFLAGS_OPTIMIZE}" AC_MSG_RESULT([no]) else CFLAGS_DEFAULT="${CFLAGS_DEBUG}" LDFLAGS_DEFAULT="${LDFLAGS_DEBUG}" if test "$tcl_ok" = "yes"; then AC_MSG_RESULT([yes (standard debugging)]) fi fi # TEA specific: if test "${TEA_PLATFORM}" != "windows" ; then LDFLAGS_DEFAULT="${LDFLAGS}" fi AC_SUBST(CFLAGS_DEFAULT) AC_SUBST(LDFLAGS_DEFAULT) AC_SUBST(TCL_DBGX) if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then AC_DEFINE(TCL_MEM_DEBUG, 1, [Is memory debugging enabled?]) fi if test "$tcl_ok" != "yes" -a "$tcl_ok" != "no"; then if test "$tcl_ok" = "all"; then AC_MSG_RESULT([enabled symbols mem debugging]) else AC_MSG_RESULT([enabled $tcl_ok debugging]) fi fi ]) #------------------------------------------------------------------------ # TEA_ENABLE_LANGINFO -- # # Allows use of modern nl_langinfo check for better l10n. # This is only relevant for Unix. # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --enable-langinfo=yes|no (default is yes) # # Defines the following vars: # HAVE_LANGINFO Triggers use of nl_langinfo if defined. #------------------------------------------------------------------------ AC_DEFUN([TEA_ENABLE_LANGINFO], [ AC_ARG_ENABLE(langinfo, AC_HELP_STRING([--enable-langinfo], [use nl_langinfo if possible to determine encoding at startup, otherwise use old heuristic (default: on)]), [langinfo_ok=$enableval], [langinfo_ok=yes]) HAVE_LANGINFO=0 if test "$langinfo_ok" = "yes"; then AC_CHECK_HEADER(langinfo.h,[langinfo_ok=yes],[langinfo_ok=no]) fi AC_MSG_CHECKING([whether to use nl_langinfo]) if test "$langinfo_ok" = "yes"; then AC_CACHE_VAL(tcl_cv_langinfo_h, [ AC_TRY_COMPILE([#include ], [nl_langinfo(CODESET);], [tcl_cv_langinfo_h=yes],[tcl_cv_langinfo_h=no])]) AC_MSG_RESULT([$tcl_cv_langinfo_h]) if test $tcl_cv_langinfo_h = yes; then AC_DEFINE(HAVE_LANGINFO, 1, [Do we have nl_langinfo()?]) fi else AC_MSG_RESULT([$langinfo_ok]) fi ]) #-------------------------------------------------------------------- # TEA_CONFIG_SYSTEM # # Determine what the system is (some things cannot be easily checked # on a feature-driven basis, alas). This can usually be done via the # "uname" command. # # Arguments: # none # # Results: # Defines the following var: # # system - System/platform/version identification code. #-------------------------------------------------------------------- AC_DEFUN([TEA_CONFIG_SYSTEM], [ AC_CACHE_CHECK([system version], tcl_cv_sys_version, [ # TEA specific: if test "${TEA_PLATFORM}" = "windows" ; then tcl_cv_sys_version=windows else tcl_cv_sys_version=`uname -s`-`uname -r` if test "$?" -ne 0 ; then AC_MSG_WARN([can't find uname command]) tcl_cv_sys_version=unknown else if test "`uname -s`" = "AIX" ; then tcl_cv_sys_version=AIX-`uname -v`.`uname -r` fi fi fi ]) system=$tcl_cv_sys_version ]) #-------------------------------------------------------------------- # TEA_CONFIG_CFLAGS # # Try to determine the proper flags to pass to the compiler # for building shared libraries and other such nonsense. # # Arguments: # none # # Results: # # Defines and substitutes the following vars: # # DL_OBJS, DL_LIBS - removed for TEA, only needed by core. # LDFLAGS - Flags to pass to the compiler when linking object # files into an executable application binary such # as tclsh. # LD_SEARCH_FLAGS-Flags to pass to ld, such as "-R /usr/local/tcl/lib", # that tell the run-time dynamic linker where to look # for shared libraries such as libtcl.so. Depends on # the variable LIB_RUNTIME_DIR in the Makefile. Could # be the same as CC_SEARCH_FLAGS if ${CC} is used to link. # CC_SEARCH_FLAGS-Flags to pass to ${CC}, such as "-Wl,-rpath,/usr/local/tcl/lib", # that tell the run-time dynamic linker where to look # for shared libraries such as libtcl.so. Depends on # the variable LIB_RUNTIME_DIR in the Makefile. # SHLIB_CFLAGS - Flags to pass to cc when compiling the components # of a shared library (may request position-independent # code, among other things). # SHLIB_LD - Base command to use for combining object files # into a shared library. # SHLIB_LD_LIBS - Dependent libraries for the linker to scan when # creating shared libraries. This symbol typically # goes at the end of the "ld" commands that build # shared libraries. The value of the symbol defaults to # "${LIBS}" if all of the dependent libraries should # be specified when creating a shared library. If # dependent libraries should not be specified (as on # SunOS 4.x, where they cause the link to fail, or in # general if Tcl and Tk aren't themselves shared # libraries), then this symbol has an empty string # as its value. # SHLIB_SUFFIX - Suffix to use for the names of dynamically loadable # extensions. An empty string means we don't know how # to use shared libraries on this platform. # LIB_SUFFIX - Specifies everything that comes after the "libfoo" # in a static or shared library name, using the $PACKAGE_VERSION variable # to put the version in the right place. This is used # by platforms that need non-standard library names. # Examples: ${PACKAGE_VERSION}.so.1.1 on NetBSD, since it needs # to have a version after the .so, and ${PACKAGE_VERSION}.a # on AIX, since a shared library needs to have # a .a extension whereas shared objects for loadable # extensions have a .so extension. Defaults to # ${PACKAGE_VERSION}${SHLIB_SUFFIX}. # CFLAGS_DEBUG - # Flags used when running the compiler in debug mode # CFLAGS_OPTIMIZE - # Flags used when running the compiler in optimize mode # CFLAGS - Additional CFLAGS added as necessary (usually 64-bit) #-------------------------------------------------------------------- AC_DEFUN([TEA_CONFIG_CFLAGS], [ dnl TEA specific: Make sure we are initialized AC_REQUIRE([TEA_INIT]) # Step 0.a: Enable 64 bit support? AC_MSG_CHECKING([if 64bit support is requested]) AC_ARG_ENABLE(64bit, AC_HELP_STRING([--enable-64bit], [enable 64bit support (default: off)]), [do64bit=$enableval], [do64bit=no]) AC_MSG_RESULT([$do64bit]) # Step 0.b: Enable Solaris 64 bit VIS support? AC_MSG_CHECKING([if 64bit Sparc VIS support is requested]) AC_ARG_ENABLE(64bit-vis, AC_HELP_STRING([--enable-64bit-vis], [enable 64bit Sparc VIS support (default: off)]), [do64bitVIS=$enableval], [do64bitVIS=no]) AC_MSG_RESULT([$do64bitVIS]) # Force 64bit on with VIS AS_IF([test "$do64bitVIS" = "yes"], [do64bit=yes]) # Step 0.c: Check if visibility support is available. Do this here so # that platform specific alternatives can be used below if this fails. AC_CACHE_CHECK([if compiler supports visibility "hidden"], tcl_cv_cc_visibility_hidden, [ hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -Werror" AC_TRY_LINK([ extern __attribute__((__visibility__("hidden"))) void f(void); void f(void) {}], [f();], tcl_cv_cc_visibility_hidden=yes, tcl_cv_cc_visibility_hidden=no) CFLAGS=$hold_cflags]) AS_IF([test $tcl_cv_cc_visibility_hidden = yes], [ AC_DEFINE(MODULE_SCOPE, [extern __attribute__((__visibility__("hidden")))], [Compiler support for module scope symbols]) AC_DEFINE(HAVE_HIDDEN, [1], [Compiler support for module scope symbols]) ]) # Step 0.d: Disable -rpath support? AC_MSG_CHECKING([if rpath support is requested]) AC_ARG_ENABLE(rpath, AC_HELP_STRING([--disable-rpath], [disable rpath support (default: on)]), [doRpath=$enableval], [doRpath=yes]) AC_MSG_RESULT([$doRpath]) # TEA specific: Cross-compiling options for Windows/CE builds? AS_IF([test "${TEA_PLATFORM}" = windows], [ AC_MSG_CHECKING([if Windows/CE build is requested]) AC_ARG_ENABLE(wince, AC_HELP_STRING([--enable-wince], [enable Win/CE support (where applicable)]), [doWince=$enableval], [doWince=no]) AC_MSG_RESULT([$doWince]) ]) # Set the variable "system" to hold the name and version number # for the system. TEA_CONFIG_SYSTEM # Require ranlib early so we can override it in special cases below. AC_REQUIRE([AC_PROG_RANLIB]) # Set configuration options based on system name and version. # This is similar to Tcl's unix/tcl.m4 except that we've added a # "windows" case and removed some core-only vars. do64bit_ok=no # default to '{$LIBS}' and set to "" on per-platform necessary basis SHLIB_LD_LIBS='${LIBS}' # When ld needs options to work in 64-bit mode, put them in # LDFLAGS_ARCH so they eventually end up in LDFLAGS even if [load] # is disabled by the user. [Bug 1016796] LDFLAGS_ARCH="" UNSHARED_LIB_SUFFIX="" # TEA specific: use PACKAGE_VERSION instead of VERSION TCL_TRIM_DOTS='`echo ${PACKAGE_VERSION} | tr -d .`' ECHO_VERSION='`echo ${PACKAGE_VERSION}`' TCL_LIB_VERSIONS_OK=ok CFLAGS_DEBUG=-g AS_IF([test "$GCC" = yes], [ CFLAGS_OPTIMIZE=-O2 CFLAGS_WARNING="-Wall" ], [ CFLAGS_OPTIMIZE=-O CFLAGS_WARNING="" ]) AC_CHECK_TOOL(AR, ar) STLIB_LD='${AR} cr' LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH" AS_IF([test "x$SHLIB_VERSION" = x],[SHLIB_VERSION="1.0"]) case $system in # TEA specific: windows) # This is a 2-stage check to make sure we have the 64-bit SDK # We have to know where the SDK is installed. # This magic is based on MS Platform SDK for Win2003 SP1 - hobbs # MACHINE is IX86 for LINK, but this is used by the manifest, # which requires x86|amd64|ia64. MACHINE="X86" if test "$do64bit" != "no" ; then if test "x${MSSDK}x" = "xx" ; then MSSDK="C:/Progra~1/Microsoft Platform SDK" fi MSSDK=`echo "$MSSDK" | sed -e 's!\\\!/!g'` PATH64="" case "$do64bit" in amd64|x64|yes) MACHINE="AMD64" ; # default to AMD64 64-bit build PATH64="${MSSDK}/Bin/Win64/x86/AMD64" ;; ia64) MACHINE="IA64" PATH64="${MSSDK}/Bin/Win64" ;; esac if test "$GCC" != "yes" -a ! -d "${PATH64}" ; then AC_MSG_WARN([Could not find 64-bit $MACHINE SDK to enable 64bit mode]) AC_MSG_WARN([Ensure latest Platform SDK is installed]) do64bit="no" else AC_MSG_RESULT([ Using 64-bit $MACHINE mode]) do64bit_ok="yes" fi fi if test "$doWince" != "no" ; then if test "$do64bit" != "no" ; then AC_MSG_ERROR([Windows/CE and 64-bit builds incompatible]) fi if test "$GCC" = "yes" ; then AC_MSG_ERROR([Windows/CE and GCC builds incompatible]) fi TEA_PATH_CELIB # Set defaults for common evc4/PPC2003 setup # Currently Tcl requires 300+, possibly 420+ for sockets CEVERSION=420; # could be 211 300 301 400 420 ... TARGETCPU=ARMV4; # could be ARMV4 ARM MIPS SH3 X86 ... ARCH=ARM; # could be ARM MIPS X86EM ... PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002" if test "$doWince" != "yes"; then # If !yes then the user specified something # Reset ARCH to allow user to skip specifying it ARCH= eval `echo $doWince | awk -F, '{ \ if (length([$]1)) { printf "CEVERSION=\"%s\"\n", [$]1; \ if ([$]1 < 400) { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \ if (length([$]2)) { printf "TARGETCPU=\"%s\"\n", toupper([$]2) }; \ if (length([$]3)) { printf "ARCH=\"%s\"\n", toupper([$]3) }; \ if (length([$]4)) { printf "PLATFORM=\"%s\"\n", [$]4 }; \ }'` if test "x${ARCH}" = "x" ; then ARCH=$TARGETCPU; fi fi OSVERSION=WCE$CEVERSION; if test "x${WCEROOT}" = "x" ; then WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0" if test ! -d "${WCEROOT}" ; then WCEROOT="C:/Program Files/Microsoft eMbedded Tools" fi fi if test "x${SDKROOT}" = "x" ; then SDKROOT="C:/Program Files/Windows CE Tools" if test ! -d "${SDKROOT}" ; then SDKROOT="C:/Windows CE Tools" fi fi WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'` SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'` if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" \ -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then AC_MSG_ERROR([could not find PocketPC SDK or target compiler to enable WinCE mode [$CEVERSION,$TARGETCPU,$ARCH,$PLATFORM]]) doWince="no" else # We could PATH_NOSPACE these, but that's not important, # as long as we quote them when used. CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include" if test -d "${CEINCLUDE}/${TARGETCPU}" ; then CEINCLUDE="${CEINCLUDE}/${TARGETCPU}" fi CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" fi fi if test "$GCC" != "yes" ; then if test "${SHARED_BUILD}" = "0" ; then runtime=-MT else runtime=-MD fi if test "$do64bit" != "no" ; then # All this magic is necessary for the Win64 SDK RC1 - hobbs CC="\"${PATH64}/cl.exe\"" CFLAGS="${CFLAGS} -I\"${MSSDK}/Include\" -I\"${MSSDK}/Include/crt\" -I\"${MSSDK}/Include/crt/sys\"" RC="\"${MSSDK}/bin/rc.exe\"" lflags="-nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\"" LINKBIN="\"${PATH64}/link.exe\"" CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d" CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}" # Avoid 'unresolved external symbol __security_cookie' # errors, c.f. http://support.microsoft.com/?id=894573 TEA_ADD_LIBS([bufferoverflowU.lib]) elif test "$doWince" != "no" ; then CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin" if test "${TARGETCPU}" = "X86"; then CC="\"${CEBINROOT}/cl.exe\"" else CC="\"${CEBINROOT}/cl${ARCH}.exe\"" fi CFLAGS="$CFLAGS -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\"" RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\"" arch=`echo ${ARCH} | awk '{print tolower([$]0)}'` defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _WINDOWS" if test "${SHARED_BUILD}" = "1" ; then # Static CE builds require static celib as well defs="${defs} _DLL" fi for i in $defs ; do AC_DEFINE_UNQUOTED($i, 1, [WinCE def ]$i) done AC_DEFINE_UNQUOTED(_WIN32_WCE, $CEVERSION, [_WIN32_WCE version]) AC_DEFINE_UNQUOTED(UNDER_CE, $CEVERSION, [UNDER_CE version]) CFLAGS_DEBUG="-nologo -Zi -Od" CFLAGS_OPTIMIZE="-nologo -Ox" lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'` lflags="-MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo" LINKBIN="\"${CEBINROOT}/link.exe\"" AC_SUBST(CELIB_DIR) else RC="rc" lflags="-nologo" LINKBIN="link" CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d" CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}" fi fi if test "$GCC" = "yes"; then # mingw gcc mode AC_CHECK_TOOL(RC, windres) CFLAGS_DEBUG="-g" CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer" SHLIB_LD='${CC} -shared' UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}" LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}" AC_CACHE_CHECK(for cross-compile version of gcc, ac_cv_cross, AC_TRY_COMPILE([ #ifdef _WIN32 #error cross-compiler #endif ], [], ac_cv_cross=yes, ac_cv_cross=no) ) if test "$ac_cv_cross" = "yes"; then case "$do64bit" in amd64|x64|yes) CC="x86_64-w64-mingw32-gcc" LD="x86_64-w64-mingw32-ld" AR="x86_64-w64-mingw32-ar" RANLIB="x86_64-w64-mingw32-ranlib" RC="x86_64-w64-mingw32-windres" ;; *) CC="i686-w64-mingw32-gcc" LD="i686-w64-mingw32-ld" AR="i686-w64-mingw32-ar" RANLIB="i686-w64-mingw32-ranlib" RC="i686-w64-mingw32-windres" ;; esac fi else SHLIB_LD="${LINKBIN} -dll ${lflags}" # link -lib only works when -lib is the first arg STLIB_LD="${LINKBIN} -lib ${lflags}" UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.lib' PATHTYPE=-w # For information on what debugtype is most useful, see: # http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp # and also # http://msdn2.microsoft.com/en-us/library/y0zzbyt4%28VS.80%29.aspx # This essentially turns it all on. LDFLAGS_DEBUG="-debug -debugtype:cv" LDFLAGS_OPTIMIZE="-release" if test "$doWince" != "no" ; then LDFLAGS_CONSOLE="-link ${lflags}" LDFLAGS_WINDOW=${LDFLAGS_CONSOLE} else LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}" LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}" fi fi SHLIB_SUFFIX=".dll" SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.dll' TCL_LIB_VERSIONS_OK=nodots ;; AIX-*) AS_IF([test "${TCL_THREADS}" = "1" -a "$GCC" != "yes"], [ # AIX requires the _r compiler when gcc isn't being used case "${CC}" in *_r|*_r\ *) # ok ... ;; *) # Make sure only first arg gets _r CC=`echo "$CC" | sed -e 's/^\([[^ ]]*\)/\1_r/'` ;; esac AC_MSG_RESULT([Using $CC for compiling with threads]) ]) LIBS="$LIBS -lc" SHLIB_CFLAGS="" SHLIB_SUFFIX=".so" LD_LIBRARY_PATH_VAR="LIBPATH" # Check to enable 64-bit flags for compiler/linker AS_IF([test "$do64bit" = yes], [ AS_IF([test "$GCC" = yes], [ AC_MSG_WARN([64bit mode not supported with GCC on $system]) ], [ do64bit_ok=yes CFLAGS="$CFLAGS -q64" LDFLAGS_ARCH="-q64" RANLIB="${RANLIB} -X64" AR="${AR} -X64" SHLIB_LD_FLAGS="-b64" ]) ]) AS_IF([test "`uname -m`" = ia64], [ # AIX-5 uses ELF style dynamic libraries on IA-64, but not PPC SHLIB_LD="/usr/ccs/bin/ld -G -z text" AS_IF([test "$GCC" = yes], [ CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' ], [ CC_SEARCH_FLAGS='-R${LIB_RUNTIME_DIR}' ]) LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' ], [ AS_IF([test "$GCC" = yes], [ SHLIB_LD='${CC} -shared -Wl,-bexpall' ], [ SHLIB_LD="/bin/ld -bhalt:4 -bM:SRE -bexpall -H512 -T512 -bnoentry" LDFLAGS="$LDFLAGS -brtl" ]) SHLIB_LD="${SHLIB_LD} ${SHLIB_LD_FLAGS}" CC_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ]) ;; BeOS*) SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -nostart' SHLIB_SUFFIX=".so" #----------------------------------------------------------- # Check for inet_ntoa in -lbind, for BeOS (which also needs # -lsocket, even if the network functions are in -lnet which # is always linked to, for compatibility. #----------------------------------------------------------- AC_CHECK_LIB(bind, inet_ntoa, [LIBS="$LIBS -lbind -lsocket"]) ;; BSD/OS-4.*) SHLIB_CFLAGS="-export-dynamic -fPIC" SHLIB_LD='${CC} -shared' SHLIB_SUFFIX=".so" LDFLAGS="$LDFLAGS -export-dynamic" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; CYGWIN_*) SHLIB_CFLAGS="" SHLIB_LD='${CC} -shared' SHLIB_SUFFIX=".dll" EXEEXT=".exe" do64bit_ok=yes CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; Haiku*) LDFLAGS="$LDFLAGS -Wl,--export-dynamic" SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS}' AC_CHECK_LIB(network, inet_ntoa, [LIBS="$LIBS -lnetwork"]) ;; HP-UX-*.11.*) # Use updated header definitions where possible AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, [Do we want to use the XOPEN network library?]) # TEA specific: Needed by Tcl, but not most extensions #AC_DEFINE(_XOPEN_SOURCE, 1, [Do we want to use the XOPEN network library?]) #LIBS="$LIBS -lxnet" # Use the XOPEN network library AS_IF([test "`uname -m`" = ia64], [ SHLIB_SUFFIX=".so" # Use newer C++ library for C++ extensions #if test "$GCC" != "yes" ; then # CPPFLAGS="-AA" #fi ], [ SHLIB_SUFFIX=".sl" ]) AC_CHECK_LIB(dld, shl_load, tcl_ok=yes, tcl_ok=no) AS_IF([test "$tcl_ok" = yes], [ LDFLAGS="$LDFLAGS -Wl,-E" CC_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.' LD_SEARCH_FLAGS='+s +b ${LIB_RUNTIME_DIR}:.' LD_LIBRARY_PATH_VAR="SHLIB_PATH" ]) AS_IF([test "$GCC" = yes], [ SHLIB_LD='${CC} -shared' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ], [ CFLAGS="$CFLAGS -z" # Users may want PA-RISC 1.1/2.0 portable code - needs HP cc #CFLAGS="$CFLAGS +DAportable" SHLIB_CFLAGS="+z" SHLIB_LD="ld -b" ]) # Check to enable 64-bit flags for compiler/linker AS_IF([test "$do64bit" = "yes"], [ AS_IF([test "$GCC" = yes], [ case `${CC} -dumpmachine` in hppa64*) # 64-bit gcc in use. Fix flags for GNU ld. do64bit_ok=yes SHLIB_LD='${CC} -shared' AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ;; *) AC_MSG_WARN([64bit mode not supported with GCC on $system]) ;; esac ], [ do64bit_ok=yes CFLAGS="$CFLAGS +DD64" LDFLAGS_ARCH="+DD64" ]) ]) ;; IRIX-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -n32 -shared -rdata_shared" SHLIB_SUFFIX=".so" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}']) AS_IF([test "$GCC" = yes], [ CFLAGS="$CFLAGS -mabi=n32" LDFLAGS="$LDFLAGS -mabi=n32" ], [ case $system in IRIX-6.3) # Use to build 6.2 compatible binaries on 6.3. CFLAGS="$CFLAGS -n32 -D_OLD_TERMIOS" ;; *) CFLAGS="$CFLAGS -n32" ;; esac LDFLAGS="$LDFLAGS -n32" ]) ;; IRIX64-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -n32 -shared -rdata_shared" SHLIB_SUFFIX=".so" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}']) # Check to enable 64-bit flags for compiler/linker AS_IF([test "$do64bit" = yes], [ AS_IF([test "$GCC" = yes], [ AC_MSG_WARN([64bit mode not supported by gcc]) ], [ do64bit_ok=yes SHLIB_LD="ld -64 -shared -rdata_shared" CFLAGS="$CFLAGS -64" LDFLAGS_ARCH="-64" ]) ]) ;; Linux*|GNU*|NetBSD-Debian) SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" # TEA specific: CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer" # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS_DEFAULT}' LDFLAGS="$LDFLAGS -Wl,--export-dynamic" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} AS_IF([test "`uname -m`" = "alpha"], [CFLAGS="$CFLAGS -mieee"]) AS_IF([test $do64bit = yes], [ AC_CACHE_CHECK([if compiler accepts -m64 flag], tcl_cv_cc_m64, [ hold_cflags=$CFLAGS CFLAGS="$CFLAGS -m64" AC_TRY_LINK(,, tcl_cv_cc_m64=yes, tcl_cv_cc_m64=no) CFLAGS=$hold_cflags]) AS_IF([test $tcl_cv_cc_m64 = yes], [ CFLAGS="$CFLAGS -m64" do64bit_ok=yes ]) ]) # The combo of gcc + glibc has a bug related to inlining of # functions like strtod(). The -fno-builtin flag should address # this problem but it does not work. The -fno-inline flag is kind # of overkill but it works. Disable inlining only when one of the # files in compat/*.c is being linked in. AS_IF([test x"${USE_COMPAT}" != x],[CFLAGS="$CFLAGS -fno-inline"]) ;; Lynx*) SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" CFLAGS_OPTIMIZE=-02 SHLIB_LD='${CC} -shared' LD_FLAGS="-Wl,--export-dynamic" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) ;; OpenBSD-*) arch=`arch -s` case "$arch" in vax) SHLIB_SUFFIX="" SHARED_LIB_SUFFIX="" LDFLAGS="" ;; *) SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}' SHLIB_SUFFIX=".so" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.${SHLIB_VERSION}' LDFLAGS="-Wl,-export-dynamic" ;; esac case "$arch" in vax) CFLAGS_OPTIMIZE="-O1" ;; *) CFLAGS_OPTIMIZE="-O2" ;; esac AS_IF([test "${TCL_THREADS}" = "1"], [ # On OpenBSD: Compile with -pthread # Don't link with -lpthread LIBS=`echo $LIBS | sed s/-lpthread//` CFLAGS="$CFLAGS -pthread" ]) # OpenBSD doesn't do version numbers with dots. UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' TCL_LIB_VERSIONS_OK=nodots ;; NetBSD-*) # NetBSD has ELF and can use 'cc -shared' to build shared libs SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}' SHLIB_SUFFIX=".so" LDFLAGS="$LDFLAGS -export-dynamic" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} AS_IF([test "${TCL_THREADS}" = "1"], [ # The -pthread needs to go in the CFLAGS, not LIBS LIBS=`echo $LIBS | sed s/-pthread//` CFLAGS="$CFLAGS -pthread" LDFLAGS="$LDFLAGS -pthread" ]) ;; FreeBSD-*) # This configuration from FreeBSD Ports. SHLIB_CFLAGS="-fPIC" SHLIB_LD="${CC} -shared" TCL_SHLIB_LD_EXTRAS="-Wl,-soname=\$[@]" TK_SHLIB_LD_EXTRAS="-Wl,-soname,\$[@]" SHLIB_SUFFIX=".so" LDFLAGS="" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) AS_IF([test "${TCL_THREADS}" = "1"], [ # The -pthread needs to go in the LDFLAGS, not LIBS LIBS=`echo $LIBS | sed s/-pthread//` CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LDFLAGS="$LDFLAGS $PTHREAD_LIBS"]) case $system in FreeBSD-3.*) # Version numbers are dot-stripped by system policy. TCL_TRIM_DOTS=`echo ${VERSION} | tr -d .` UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so' TCL_LIB_VERSIONS_OK=nodots ;; esac ;; Darwin-*) CFLAGS_OPTIMIZE="-Os" SHLIB_CFLAGS="-fno-common" # To avoid discrepancies between what headers configure sees during # preprocessing tests and compiling tests, move any -isysroot and # -mmacosx-version-min flags from CFLAGS to CPPFLAGS: CPPFLAGS="${CPPFLAGS} `echo " ${CFLAGS}" | \ awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \ if ([$]i~/^(isysroot|mmacosx-version-min)/) print "-"[$]i}'`" CFLAGS="`echo " ${CFLAGS}" | \ awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \ if (!([$]i~/^(isysroot|mmacosx-version-min)/)) print "-"[$]i}'`" AS_IF([test $do64bit = yes], [ case `arch` in ppc) AC_CACHE_CHECK([if compiler accepts -arch ppc64 flag], tcl_cv_cc_arch_ppc64, [ hold_cflags=$CFLAGS CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5" AC_TRY_LINK(,, tcl_cv_cc_arch_ppc64=yes, tcl_cv_cc_arch_ppc64=no) CFLAGS=$hold_cflags]) AS_IF([test $tcl_cv_cc_arch_ppc64 = yes], [ CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5" do64bit_ok=yes ]);; i386) AC_CACHE_CHECK([if compiler accepts -arch x86_64 flag], tcl_cv_cc_arch_x86_64, [ hold_cflags=$CFLAGS CFLAGS="$CFLAGS -arch x86_64" AC_TRY_LINK(,, tcl_cv_cc_arch_x86_64=yes, tcl_cv_cc_arch_x86_64=no) CFLAGS=$hold_cflags]) AS_IF([test $tcl_cv_cc_arch_x86_64 = yes], [ CFLAGS="$CFLAGS -arch x86_64" do64bit_ok=yes ]);; *) AC_MSG_WARN([Don't know how enable 64-bit on architecture `arch`]);; esac ], [ # Check for combined 32-bit and 64-bit fat build AS_IF([echo "$CFLAGS " |grep -E -q -- '-arch (ppc64|x86_64) ' \ && echo "$CFLAGS " |grep -E -q -- '-arch (ppc|i386) '], [ fat_32_64=yes]) ]) # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -dynamiclib ${CFLAGS} ${LDFLAGS_DEFAULT}' AC_CACHE_CHECK([if ld accepts -single_module flag], tcl_cv_ld_single_module, [ hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -dynamiclib -Wl,-single_module" AC_TRY_LINK(, [int i;], tcl_cv_ld_single_module=yes, tcl_cv_ld_single_module=no) LDFLAGS=$hold_ldflags]) AS_IF([test $tcl_cv_ld_single_module = yes], [ SHLIB_LD="${SHLIB_LD} -Wl,-single_module" ]) # TEA specific: link shlib with current and compatibility version flags vers=`echo ${PACKAGE_VERSION} | sed -e 's/^\([[0-9]]\{1,5\}\)\(\(\.[[0-9]]\{1,3\}\)\{0,2\}\).*$/\1\2/p' -e d` SHLIB_LD="${SHLIB_LD} -current_version ${vers:-0} -compatibility_version ${vers:-0}" SHLIB_SUFFIX=".dylib" # Don't use -prebind when building for Mac OS X 10.4 or later only: AS_IF([test "`echo "${MACOSX_DEPLOYMENT_TARGET}" | awk -F '10\\.' '{print int([$]2)}'`" -lt 4 -a \ "`echo "${CPPFLAGS}" | awk -F '-mmacosx-version-min=10\\.' '{print int([$]2)}'`" -lt 4], [ LDFLAGS="$LDFLAGS -prebind"]) LDFLAGS="$LDFLAGS -headerpad_max_install_names" AC_CACHE_CHECK([if ld accepts -search_paths_first flag], tcl_cv_ld_search_paths_first, [ hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-search_paths_first" AC_TRY_LINK(, [int i;], tcl_cv_ld_search_paths_first=yes, tcl_cv_ld_search_paths_first=no) LDFLAGS=$hold_ldflags]) AS_IF([test $tcl_cv_ld_search_paths_first = yes], [ LDFLAGS="$LDFLAGS -Wl,-search_paths_first" ]) AS_IF([test "$tcl_cv_cc_visibility_hidden" != yes], [ AC_DEFINE(MODULE_SCOPE, [__private_extern__], [Compiler support for module scope symbols]) tcl_cv_cc_visibility_hidden=yes ]) CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" LD_LIBRARY_PATH_VAR="DYLD_LIBRARY_PATH" # TEA specific: for combined 32 & 64 bit fat builds of Tk # extensions, verify that 64-bit build is possible. AS_IF([test "$fat_32_64" = yes && test -n "${TK_BIN_DIR}"], [ AS_IF([test "${TEA_WINDOWINGSYSTEM}" = x11], [ AC_CACHE_CHECK([for 64-bit X11], tcl_cv_lib_x11_64, [ for v in CFLAGS CPPFLAGS LDFLAGS; do eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"' done CPPFLAGS="$CPPFLAGS -I/usr/X11R6/include" LDFLAGS="$LDFLAGS -L/usr/X11R6/lib -lX11" AC_TRY_LINK([#include ], [XrmInitialize();], tcl_cv_lib_x11_64=yes, tcl_cv_lib_x11_64=no) for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="$hold_'$v'"' done]) ]) AS_IF([test "${TEA_WINDOWINGSYSTEM}" = aqua], [ AC_CACHE_CHECK([for 64-bit Tk], tcl_cv_lib_tk_64, [ for v in CFLAGS CPPFLAGS LDFLAGS; do eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"' done CPPFLAGS="$CPPFLAGS -DUSE_TCL_STUBS=1 -DUSE_TK_STUBS=1 ${TCL_INCLUDES} ${TK_INCLUDES}" LDFLAGS="$LDFLAGS ${TCL_STUB_LIB_SPEC} ${TK_STUB_LIB_SPEC}" AC_TRY_LINK([#include ], [Tk_InitStubs(NULL, "", 0);], tcl_cv_lib_tk_64=yes, tcl_cv_lib_tk_64=no) for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="$hold_'$v'"' done]) ]) # remove 64-bit arch flags from CFLAGS et al. if configuration # does not support 64-bit. AS_IF([test "$tcl_cv_lib_tk_64" = no -o "$tcl_cv_lib_x11_64" = no], [ AC_MSG_NOTICE([Removing 64-bit architectures from compiler & linker flags]) for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="`echo "$'$v' "|sed -e "s/-arch ppc64 / /g" -e "s/-arch x86_64 / /g"`"' done]) ]) ;; OS/390-*) CFLAGS_OPTIMIZE="" # Optimizer is buggy AC_DEFINE(_OE_SOCKETS, 1, # needed in sys/socket.h [Should OS/390 do the right thing with sockets?]) ;; OSF1-V*) # Digital OSF/1 SHLIB_CFLAGS="" AS_IF([test "$SHARED_BUILD" = 1], [ SHLIB_LD='ld -shared -expect_unresolved "*"' ], [ SHLIB_LD='ld -non_shared -expect_unresolved "*"' ]) SHLIB_SUFFIX=".so" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}']) AS_IF([test "$GCC" = yes], [CFLAGS="$CFLAGS -mieee"], [ CFLAGS="$CFLAGS -DHAVE_TZSET -std1 -ieee"]) # see pthread_intro(3) for pthread support on osf1, k.furukawa AS_IF([test "${TCL_THREADS}" = 1], [ CFLAGS="$CFLAGS -DHAVE_PTHREAD_ATTR_SETSTACKSIZE" CFLAGS="$CFLAGS -DTCL_THREAD_STACK_MIN=PTHREAD_STACK_MIN*64" LIBS=`echo $LIBS | sed s/-lpthreads//` AS_IF([test "$GCC" = yes], [ LIBS="$LIBS -lpthread -lmach -lexc" ], [ CFLAGS="$CFLAGS -pthread" LDFLAGS="$LDFLAGS -pthread" ]) ]) ;; QNX-6*) # QNX RTP # This may work for all QNX, but it was only reported for v6. SHLIB_CFLAGS="-fPIC" SHLIB_LD="ld -Bshareable -x" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; SCO_SV-3.2*) AS_IF([test "$GCC" = yes], [ SHLIB_CFLAGS="-fPIC -melf" LDFLAGS="$LDFLAGS -melf -Wl,-Bexport" ], [ SHLIB_CFLAGS="-Kpic -belf" LDFLAGS="$LDFLAGS -belf -Wl,-Bexport" ]) SHLIB_LD="ld -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; SunOS-5.[[0-6]]) # Careful to not let 5.10+ fall into this case # Note: If _REENTRANT isn't defined, then Solaris # won't define thread-safe library routines. AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?]) AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Do we really want to follow the standard? Yes we do!]) SHLIB_CFLAGS="-KPIC" SHLIB_SUFFIX=".so" AS_IF([test "$GCC" = yes], [ SHLIB_LD='${CC} -shared' CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ], [ SHLIB_LD="/usr/ccs/bin/ld -G -z text" CC_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ]) ;; SunOS-5*) # Note: If _REENTRANT isn't defined, then Solaris # won't define thread-safe library routines. AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?]) AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Do we really want to follow the standard? Yes we do!]) SHLIB_CFLAGS="-KPIC" # Check to enable 64-bit flags for compiler/linker AS_IF([test "$do64bit" = yes], [ arch=`isainfo` AS_IF([test "$arch" = "sparcv9 sparc"], [ AS_IF([test "$GCC" = yes], [ AS_IF([test "`${CC} -dumpversion | awk -F. '{print [$]1}'`" -lt 3], [ AC_MSG_WARN([64bit mode not supported with GCC < 3.2 on $system]) ], [ do64bit_ok=yes CFLAGS="$CFLAGS -m64 -mcpu=v9" LDFLAGS="$LDFLAGS -m64 -mcpu=v9" SHLIB_CFLAGS="-fPIC" ]) ], [ do64bit_ok=yes AS_IF([test "$do64bitVIS" = yes], [ CFLAGS="$CFLAGS -xarch=v9a" LDFLAGS_ARCH="-xarch=v9a" ], [ CFLAGS="$CFLAGS -xarch=v9" LDFLAGS_ARCH="-xarch=v9" ]) # Solaris 64 uses this as well #LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH_64" ]) ], [AS_IF([test "$arch" = "amd64 i386"], [ AS_IF([test "$GCC" = yes], [ case $system in SunOS-5.1[[1-9]]*|SunOS-5.[[2-9]][[0-9]]*) do64bit_ok=yes CFLAGS="$CFLAGS -m64" LDFLAGS="$LDFLAGS -m64";; *) AC_MSG_WARN([64bit mode not supported with GCC on $system]);; esac ], [ do64bit_ok=yes case $system in SunOS-5.1[[1-9]]*|SunOS-5.[[2-9]][[0-9]]*) CFLAGS="$CFLAGS -m64" LDFLAGS="$LDFLAGS -m64";; *) CFLAGS="$CFLAGS -xarch=amd64" LDFLAGS="$LDFLAGS -xarch=amd64";; esac ]) ], [AC_MSG_WARN([64bit mode not supported for $arch])])]) ]) SHLIB_SUFFIX=".so" AS_IF([test "$GCC" = yes], [ SHLIB_LD='${CC} -shared' CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} AS_IF([test "$do64bit_ok" = yes], [ AS_IF([test "$arch" = "sparcv9 sparc"], [ # We need to specify -static-libgcc or we need to # add the path to the sparv9 libgcc. # JH: static-libgcc is necessary for core Tcl, but may # not be necessary for extensions. SHLIB_LD="$SHLIB_LD -m64 -mcpu=v9 -static-libgcc" # for finding sparcv9 libgcc, get the regular libgcc # path, remove so name and append 'sparcv9' #v9gcclibdir="`gcc -print-file-name=libgcc_s.so` | ..." #CC_SEARCH_FLAGS="${CC_SEARCH_FLAGS},-R,$v9gcclibdir" ], [AS_IF([test "$arch" = "amd64 i386"], [ # JH: static-libgcc is necessary for core Tcl, but may # not be necessary for extensions. SHLIB_LD="$SHLIB_LD -m64 -static-libgcc" ])]) ]) ], [ case $system in SunOS-5.[[1-9]][[0-9]]*) # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -G -z text ${LDFLAGS_DEFAULT}';; *) SHLIB_LD='/usr/ccs/bin/ld -G -z text';; esac CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' ]) ;; UNIX_SV* | UnixWare-5*) SHLIB_CFLAGS="-KPIC" SHLIB_LD='${CC} -G' SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" # Some UNIX_SV* systems (unixware 1.1.2 for example) have linkers # that don't grok the -Bexport option. Test that it does. AC_CACHE_CHECK([for ld accepts -Bexport flag], tcl_cv_ld_Bexport, [ hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-Bexport" AC_TRY_LINK(, [int i;], tcl_cv_ld_Bexport=yes, tcl_cv_ld_Bexport=no) LDFLAGS=$hold_ldflags]) AS_IF([test $tcl_cv_ld_Bexport = yes], [ LDFLAGS="$LDFLAGS -Wl,-Bexport" ]) CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; esac AS_IF([test "$do64bit" = yes -a "$do64bit_ok" = no], [ AC_MSG_WARN([64bit support being disabled -- don't know magic for this platform]) ]) dnl # Add any CPPFLAGS set in the environment to our CFLAGS, but delay doing so dnl # until the end of configure, as configure's compile and link tests use dnl # both CPPFLAGS and CFLAGS (unlike our compile and link) but configure's dnl # preprocessing tests use only CPPFLAGS. AC_CONFIG_COMMANDS_PRE([CFLAGS="${CFLAGS} ${CPPFLAGS}"; CPPFLAGS=""]) # Add in the arch flags late to ensure it wasn't removed. # Not necessary in TEA, but this is aligned with core LDFLAGS="$LDFLAGS $LDFLAGS_ARCH" # If we're running gcc, then change the C flags for compiling shared # libraries to the right flags for gcc, instead of those for the # standard manufacturer compiler. AS_IF([test "$GCC" = yes], [ case $system in AIX-*) ;; BSD/OS*) ;; CYGWIN_*|MINGW32_*) ;; IRIX*) ;; NetBSD-*|FreeBSD-*|OpenBSD-*) ;; Darwin-*) ;; SCO_SV-3.2*) ;; windows) ;; *) SHLIB_CFLAGS="-fPIC" ;; esac]) AS_IF([test "$tcl_cv_cc_visibility_hidden" != yes], [ AC_DEFINE(MODULE_SCOPE, [extern], [No Compiler support for module scope symbols]) ]) AS_IF([test "$SHARED_LIB_SUFFIX" = ""], [ # TEA specific: use PACKAGE_VERSION instead of VERSION SHARED_LIB_SUFFIX='${PACKAGE_VERSION}${SHLIB_SUFFIX}']) AS_IF([test "$UNSHARED_LIB_SUFFIX" = ""], [ # TEA specific: use PACKAGE_VERSION instead of VERSION UNSHARED_LIB_SUFFIX='${PACKAGE_VERSION}.a']) if test "${GCC}" = "yes" -a ${SHLIB_SUFFIX} = ".dll"; then AC_CACHE_CHECK(for SEH support in compiler, tcl_cv_seh, AC_TRY_RUN([ #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN int main(int argc, char** argv) { int a, b = 0; __try { a = 666 / b; } __except (EXCEPTION_EXECUTE_HANDLER) { return 0; } return 1; } ], tcl_cv_seh=yes, tcl_cv_seh=no, tcl_cv_seh=no) ) if test "$tcl_cv_seh" = "no" ; then AC_DEFINE(HAVE_NO_SEH, 1, [Defined when mingw does not support SEH]) fi # # Check to see if the excpt.h include file provided contains the # definition for EXCEPTION_DISPOSITION; if not, which is the case # with Cygwin's version as of 2002-04-10, define it to be int, # sufficient for getting the current code to work. # AC_CACHE_CHECK(for EXCEPTION_DISPOSITION support in include files, tcl_cv_eh_disposition, AC_TRY_COMPILE([ # define WIN32_LEAN_AND_MEAN # include # undef WIN32_LEAN_AND_MEAN ],[ EXCEPTION_DISPOSITION x; ], tcl_cv_eh_disposition=yes, tcl_cv_eh_disposition=no) ) if test "$tcl_cv_eh_disposition" = "no" ; then AC_DEFINE(EXCEPTION_DISPOSITION, int, [Defined when cygwin/mingw does not support EXCEPTION DISPOSITION]) fi # Check to see if winnt.h defines CHAR, SHORT, and LONG # even if VOID has already been #defined. The win32api # used by mingw and cygwin is known to do this. AC_CACHE_CHECK(for winnt.h that ignores VOID define, tcl_cv_winnt_ignore_void, AC_TRY_COMPILE([ #define VOID void #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN ], [ CHAR c; SHORT s; LONG l; ], tcl_cv_winnt_ignore_void=yes, tcl_cv_winnt_ignore_void=no) ) if test "$tcl_cv_winnt_ignore_void" = "yes" ; then AC_DEFINE(HAVE_WINNT_IGNORE_VOID, 1, [Defined when cygwin/mingw ignores VOID define in winnt.h]) fi fi # See if the compiler supports casting to a union type. # This is used to stop gcc from printing a compiler # warning when initializing a union member. AC_CACHE_CHECK(for cast to union support, tcl_cv_cast_to_union, AC_TRY_COMPILE([], [ union foo { int i; double d; }; union foo f = (union foo) (int) 0; ], tcl_cv_cast_to_union=yes, tcl_cv_cast_to_union=no) ) if test "$tcl_cv_cast_to_union" = "yes"; then AC_DEFINE(HAVE_CAST_TO_UNION, 1, [Defined when compiler supports casting to union type.]) fi AC_SUBST(CFLAGS_DEBUG) AC_SUBST(CFLAGS_OPTIMIZE) AC_SUBST(CFLAGS_WARNING) AC_SUBST(STLIB_LD) AC_SUBST(SHLIB_LD) AC_SUBST(SHLIB_LD_LIBS) AC_SUBST(SHLIB_CFLAGS) AC_SUBST(LD_LIBRARY_PATH_VAR) # These must be called after we do the basic CFLAGS checks and # verify any possible 64-bit or similar switches are necessary TEA_TCL_EARLY_FLAGS TEA_TCL_64BIT_FLAGS ]) #-------------------------------------------------------------------- # TEA_SERIAL_PORT # # Determine which interface to use to talk to the serial port. # Note that #include lines must begin in leftmost column for # some compilers to recognize them as preprocessor directives, # and some build environments have stdin not pointing at a # pseudo-terminal (usually /dev/null instead.) # # Arguments: # none # # Results: # # Defines only one of the following vars: # HAVE_SYS_MODEM_H # USE_TERMIOS # USE_TERMIO # USE_SGTTY #-------------------------------------------------------------------- AC_DEFUN([TEA_SERIAL_PORT], [ AC_CHECK_HEADERS(sys/modem.h) AC_CACHE_CHECK([termios vs. termio vs. sgtty], tcl_cv_api_serial, [ AC_TRY_RUN([ #include int main() { struct termios t; if (tcgetattr(0, &t) == 0) { cfsetospeed(&t, 0); t.c_cflag |= PARENB | PARODD | CSIZE | CSTOPB; return 0; } return 1; }], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no) if test $tcl_cv_api_serial = no ; then AC_TRY_RUN([ #include int main() { struct termio t; if (ioctl(0, TCGETA, &t) == 0) { t.c_cflag |= CBAUD | PARENB | PARODD | CSIZE | CSTOPB; return 0; } return 1; }], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no) fi if test $tcl_cv_api_serial = no ; then AC_TRY_RUN([ #include int main() { struct sgttyb t; if (ioctl(0, TIOCGETP, &t) == 0) { t.sg_ospeed = 0; t.sg_flags |= ODDP | EVENP | RAW; return 0; } return 1; }], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=no, tcl_cv_api_serial=no) fi if test $tcl_cv_api_serial = no ; then AC_TRY_RUN([ #include #include int main() { struct termios t; if (tcgetattr(0, &t) == 0 || errno == ENOTTY || errno == ENXIO || errno == EINVAL) { cfsetospeed(&t, 0); t.c_cflag |= PARENB | PARODD | CSIZE | CSTOPB; return 0; } return 1; }], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no) fi if test $tcl_cv_api_serial = no; then AC_TRY_RUN([ #include #include int main() { struct termio t; if (ioctl(0, TCGETA, &t) == 0 || errno == ENOTTY || errno == ENXIO || errno == EINVAL) { t.c_cflag |= CBAUD | PARENB | PARODD | CSIZE | CSTOPB; return 0; } return 1; }], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no) fi if test $tcl_cv_api_serial = no; then AC_TRY_RUN([ #include #include int main() { struct sgttyb t; if (ioctl(0, TIOCGETP, &t) == 0 || errno == ENOTTY || errno == ENXIO || errno == EINVAL) { t.sg_ospeed = 0; t.sg_flags |= ODDP | EVENP | RAW; return 0; } return 1; }], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=none, tcl_cv_api_serial=none) fi]) case $tcl_cv_api_serial in termios) AC_DEFINE(USE_TERMIOS, 1, [Use the termios API for serial lines]);; termio) AC_DEFINE(USE_TERMIO, 1, [Use the termio API for serial lines]);; sgtty) AC_DEFINE(USE_SGTTY, 1, [Use the sgtty API for serial lines]);; esac ]) #-------------------------------------------------------------------- # TEA_MISSING_POSIX_HEADERS # # Supply substitutes for missing POSIX header files. Special # notes: # - stdlib.h doesn't define strtol, strtoul, or # strtod in some versions of SunOS # - some versions of string.h don't declare procedures such # as strstr # # Arguments: # none # # Results: # # Defines some of the following vars: # NO_DIRENT_H # NO_ERRNO_H # NO_VALUES_H # HAVE_LIMITS_H or NO_LIMITS_H # NO_STDLIB_H # NO_STRING_H # NO_SYS_WAIT_H # NO_DLFCN_H # HAVE_SYS_PARAM_H # # HAVE_STRING_H ? # # tkUnixPort.h checks for HAVE_LIMITS_H, so do both HAVE and # CHECK on limits.h #-------------------------------------------------------------------- AC_DEFUN([TEA_MISSING_POSIX_HEADERS], [ AC_CACHE_CHECK([dirent.h], tcl_cv_dirent_h, [ AC_TRY_LINK([#include #include ], [ #ifndef _POSIX_SOURCE # ifdef __Lynx__ /* * Generate compilation error to make the test fail: Lynx headers * are only valid if really in the POSIX environment. */ missing_procedure(); # endif #endif DIR *d; struct dirent *entryPtr; char *p; d = opendir("foobar"); entryPtr = readdir(d); p = entryPtr->d_name; closedir(d); ], tcl_cv_dirent_h=yes, tcl_cv_dirent_h=no)]) if test $tcl_cv_dirent_h = no; then AC_DEFINE(NO_DIRENT_H, 1, [Do we have ?]) fi # TEA specific: AC_CHECK_HEADER(errno.h, , [AC_DEFINE(NO_ERRNO_H, 1, [Do we have ?])]) AC_CHECK_HEADER(float.h, , [AC_DEFINE(NO_FLOAT_H, 1, [Do we have ?])]) AC_CHECK_HEADER(values.h, , [AC_DEFINE(NO_VALUES_H, 1, [Do we have ?])]) AC_CHECK_HEADER(limits.h, [AC_DEFINE(HAVE_LIMITS_H, 1, [Do we have ?])], [AC_DEFINE(NO_LIMITS_H, 1, [Do we have ?])]) AC_CHECK_HEADER(stdlib.h, tcl_ok=1, tcl_ok=0) AC_EGREP_HEADER(strtol, stdlib.h, , tcl_ok=0) AC_EGREP_HEADER(strtoul, stdlib.h, , tcl_ok=0) AC_EGREP_HEADER(strtod, stdlib.h, , tcl_ok=0) if test $tcl_ok = 0; then AC_DEFINE(NO_STDLIB_H, 1, [Do we have ?]) fi AC_CHECK_HEADER(string.h, tcl_ok=1, tcl_ok=0) AC_EGREP_HEADER(strstr, string.h, , tcl_ok=0) AC_EGREP_HEADER(strerror, string.h, , tcl_ok=0) # See also memmove check below for a place where NO_STRING_H can be # set and why. if test $tcl_ok = 0; then AC_DEFINE(NO_STRING_H, 1, [Do we have ?]) fi AC_CHECK_HEADER(sys/wait.h, , [AC_DEFINE(NO_SYS_WAIT_H, 1, [Do we have ?])]) AC_CHECK_HEADER(dlfcn.h, , [AC_DEFINE(NO_DLFCN_H, 1, [Do we have ?])]) # OS/390 lacks sys/param.h (and doesn't need it, by chance). AC_HAVE_HEADERS(sys/param.h) ]) #-------------------------------------------------------------------- # TEA_PATH_X # # Locate the X11 header files and the X11 library archive. Try # the ac_path_x macro first, but if it doesn't find the X stuff # (e.g. because there's no xmkmf program) then check through # a list of possible directories. Under some conditions the # autoconf macro will return an include directory that contains # no include files, so double-check its result just to be safe. # # This should be called after TEA_CONFIG_CFLAGS as setting the # LIBS line can confuse some configure macro magic. # # Arguments: # none # # Results: # # Sets the following vars: # XINCLUDES # XLIBSW # PKG_LIBS (appends to) #-------------------------------------------------------------------- AC_DEFUN([TEA_PATH_X], [ if test "${TEA_WINDOWINGSYSTEM}" = "x11" ; then TEA_PATH_UNIX_X fi ]) AC_DEFUN([TEA_PATH_UNIX_X], [ AC_PATH_X not_really_there="" if test "$no_x" = ""; then if test "$x_includes" = ""; then AC_TRY_CPP([#include ], , not_really_there="yes") else if test ! -r $x_includes/X11/Xlib.h; then not_really_there="yes" fi fi fi if test "$no_x" = "yes" -o "$not_really_there" = "yes"; then AC_MSG_CHECKING([for X11 header files]) found_xincludes="no" AC_TRY_CPP([#include ], found_xincludes="yes", found_xincludes="no") if test "$found_xincludes" = "no"; then dirs="/usr/unsupported/include /usr/local/include /usr/X386/include /usr/X11R6/include /usr/X11R5/include /usr/include/X11R5 /usr/include/X11R4 /usr/openwin/include /usr/X11/include /usr/sww/include" for i in $dirs ; do if test -r $i/X11/Xlib.h; then AC_MSG_RESULT([$i]) XINCLUDES=" -I$i" found_xincludes="yes" break fi done fi else if test "$x_includes" != ""; then XINCLUDES="-I$x_includes" found_xincludes="yes" fi fi if test "$found_xincludes" = "no"; then AC_MSG_RESULT([couldn't find any!]) fi if test "$no_x" = yes; then AC_MSG_CHECKING([for X11 libraries]) XLIBSW=nope dirs="/usr/unsupported/lib /usr/local/lib /usr/X386/lib /usr/X11R6/lib /usr/X11R5/lib /usr/lib/X11R5 /usr/lib/X11R4 /usr/openwin/lib /usr/X11/lib /usr/sww/X11/lib" for i in $dirs ; do if test -r $i/libX11.a -o -r $i/libX11.so -o -r $i/libX11.sl -o -r $i/libX11.dylib; then AC_MSG_RESULT([$i]) XLIBSW="-L$i -lX11" x_libraries="$i" break fi done else if test "$x_libraries" = ""; then XLIBSW=-lX11 else XLIBSW="-L$x_libraries -lX11" fi fi if test "$XLIBSW" = nope ; then AC_CHECK_LIB(Xwindow, XCreateWindow, XLIBSW=-lXwindow) fi if test "$XLIBSW" = nope ; then AC_MSG_RESULT([could not find any! Using -lX11.]) XLIBSW=-lX11 fi # TEA specific: if test x"${XLIBSW}" != x ; then PKG_LIBS="${PKG_LIBS} ${XLIBSW}" fi ]) #-------------------------------------------------------------------- # TEA_BLOCKING_STYLE # # The statements below check for systems where POSIX-style # non-blocking I/O (O_NONBLOCK) doesn't work or is unimplemented. # On these systems (mostly older ones), use the old BSD-style # FIONBIO approach instead. # # Arguments: # none # # Results: # # Defines some of the following vars: # HAVE_SYS_IOCTL_H # HAVE_SYS_FILIO_H # USE_FIONBIO # O_NONBLOCK #-------------------------------------------------------------------- AC_DEFUN([TEA_BLOCKING_STYLE], [ AC_CHECK_HEADERS(sys/ioctl.h) AC_CHECK_HEADERS(sys/filio.h) TEA_CONFIG_SYSTEM AC_MSG_CHECKING([FIONBIO vs. O_NONBLOCK for nonblocking I/O]) case $system in OSF*) AC_DEFINE(USE_FIONBIO, 1, [Should we use FIONBIO?]) AC_MSG_RESULT([FIONBIO]) ;; *) AC_MSG_RESULT([O_NONBLOCK]) ;; esac ]) #-------------------------------------------------------------------- # TEA_TIME_HANDLER # # Checks how the system deals with time.h, what time structures # are used on the system, and what fields the structures have. # # Arguments: # none # # Results: # # Defines some of the following vars: # USE_DELTA_FOR_TZ # HAVE_TM_GMTOFF # HAVE_TM_TZADJ # HAVE_TIMEZONE_VAR #-------------------------------------------------------------------- AC_DEFUN([TEA_TIME_HANDLER], [ AC_CHECK_HEADERS(sys/time.h) AC_HEADER_TIME AC_STRUCT_TIMEZONE AC_CHECK_FUNCS(gmtime_r localtime_r) AC_CACHE_CHECK([tm_tzadj in struct tm], tcl_cv_member_tm_tzadj, [ AC_TRY_COMPILE([#include ], [struct tm tm; tm.tm_tzadj;], tcl_cv_member_tm_tzadj=yes, tcl_cv_member_tm_tzadj=no)]) if test $tcl_cv_member_tm_tzadj = yes ; then AC_DEFINE(HAVE_TM_TZADJ, 1, [Should we use the tm_tzadj field of struct tm?]) fi AC_CACHE_CHECK([tm_gmtoff in struct tm], tcl_cv_member_tm_gmtoff, [ AC_TRY_COMPILE([#include ], [struct tm tm; tm.tm_gmtoff;], tcl_cv_member_tm_gmtoff=yes, tcl_cv_member_tm_gmtoff=no)]) if test $tcl_cv_member_tm_gmtoff = yes ; then AC_DEFINE(HAVE_TM_GMTOFF, 1, [Should we use the tm_gmtoff field of struct tm?]) fi # # Its important to include time.h in this check, as some systems # (like convex) have timezone functions, etc. # AC_CACHE_CHECK([long timezone variable], tcl_cv_timezone_long, [ AC_TRY_COMPILE([#include ], [extern long timezone; timezone += 1; exit (0);], tcl_cv_timezone_long=yes, tcl_cv_timezone_long=no)]) if test $tcl_cv_timezone_long = yes ; then AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?]) else # # On some systems (eg IRIX 6.2), timezone is a time_t and not a long. # AC_CACHE_CHECK([time_t timezone variable], tcl_cv_timezone_time, [ AC_TRY_COMPILE([#include ], [extern time_t timezone; timezone += 1; exit (0);], tcl_cv_timezone_time=yes, tcl_cv_timezone_time=no)]) if test $tcl_cv_timezone_time = yes ; then AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?]) fi fi ]) #-------------------------------------------------------------------- # TEA_BUGGY_STRTOD # # Under Solaris 2.4, strtod returns the wrong value for the # terminating character under some conditions. Check for this # and if the problem exists use a substitute procedure # "fixstrtod" (provided by Tcl) that corrects the error. # Also, on Compaq's Tru64 Unix 5.0, # strtod(" ") returns 0.0 instead of a failure to convert. # # Arguments: # none # # Results: # # Might defines some of the following vars: # strtod (=fixstrtod) #-------------------------------------------------------------------- AC_DEFUN([TEA_BUGGY_STRTOD], [ AC_CHECK_FUNC(strtod, tcl_strtod=1, tcl_strtod=0) if test "$tcl_strtod" = 1; then AC_CACHE_CHECK([for Solaris2.4/Tru64 strtod bugs], tcl_cv_strtod_buggy,[ AC_TRY_RUN([ extern double strtod(); int main() { char *infString="Inf", *nanString="NaN", *spaceString=" "; char *term; double value; value = strtod(infString, &term); if ((term != infString) && (term[-1] == 0)) { exit(1); } value = strtod(nanString, &term); if ((term != nanString) && (term[-1] == 0)) { exit(1); } value = strtod(spaceString, &term); if (term == (spaceString+1)) { exit(1); } exit(0); }], tcl_cv_strtod_buggy=ok, tcl_cv_strtod_buggy=buggy, tcl_cv_strtod_buggy=buggy)]) if test "$tcl_cv_strtod_buggy" = buggy; then AC_LIBOBJ([fixstrtod]) USE_COMPAT=1 AC_DEFINE(strtod, fixstrtod, [Do we want to use the strtod() in compat?]) fi fi ]) #-------------------------------------------------------------------- # TEA_TCL_LINK_LIBS # # Search for the libraries needed to link the Tcl shell. # Things like the math library (-lm) and socket stuff (-lsocket vs. # -lnsl) are dealt with here. # # Arguments: # Requires the following vars to be set in the Makefile: # DL_LIBS (not in TEA, only needed in core) # LIBS # MATH_LIBS # # Results: # # Substitutes the following vars: # TCL_LIBS # MATH_LIBS # # Might append to the following vars: # LIBS # # Might define the following vars: # HAVE_NET_ERRNO_H #-------------------------------------------------------------------- AC_DEFUN([TEA_TCL_LINK_LIBS], [ #-------------------------------------------------------------------- # On a few very rare systems, all of the libm.a stuff is # already in libc.a. Set compiler flags accordingly. # Also, Linux requires the "ieee" library for math to work # right (and it must appear before "-lm"). #-------------------------------------------------------------------- AC_CHECK_FUNC(sin, MATH_LIBS="", MATH_LIBS="-lm") AC_CHECK_LIB(ieee, main, [MATH_LIBS="-lieee $MATH_LIBS"]) #-------------------------------------------------------------------- # Interactive UNIX requires -linet instead of -lsocket, plus it # needs net/errno.h to define the socket-related error codes. #-------------------------------------------------------------------- AC_CHECK_LIB(inet, main, [LIBS="$LIBS -linet"]) AC_CHECK_HEADER(net/errno.h, [ AC_DEFINE(HAVE_NET_ERRNO_H, 1, [Do we have ?])]) #-------------------------------------------------------------------- # Check for the existence of the -lsocket and -lnsl libraries. # The order here is important, so that they end up in the right # order in the command line generated by make. Here are some # special considerations: # 1. Use "connect" and "accept" to check for -lsocket, and # "gethostbyname" to check for -lnsl. # 2. Use each function name only once: can't redo a check because # autoconf caches the results of the last check and won't redo it. # 3. Use -lnsl and -lsocket only if they supply procedures that # aren't already present in the normal libraries. This is because # IRIX 5.2 has libraries, but they aren't needed and they're # bogus: they goof up name resolution if used. # 4. On some SVR4 systems, can't use -lsocket without -lnsl too. # To get around this problem, check for both libraries together # if -lsocket doesn't work by itself. #-------------------------------------------------------------------- tcl_checkBoth=0 AC_CHECK_FUNC(connect, tcl_checkSocket=0, tcl_checkSocket=1) if test "$tcl_checkSocket" = 1; then AC_CHECK_FUNC(setsockopt, , [AC_CHECK_LIB(socket, setsockopt, LIBS="$LIBS -lsocket", tcl_checkBoth=1)]) fi if test "$tcl_checkBoth" = 1; then tk_oldLibs=$LIBS LIBS="$LIBS -lsocket -lnsl" AC_CHECK_FUNC(accept, tcl_checkNsl=0, [LIBS=$tk_oldLibs]) fi AC_CHECK_FUNC(gethostbyname, , [AC_CHECK_LIB(nsl, gethostbyname, [LIBS="$LIBS -lnsl"])]) # TEA specific: Don't perform the eval of the libraries here because # DL_LIBS won't be set until we call TEA_CONFIG_CFLAGS TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}' AC_SUBST(TCL_LIBS) AC_SUBST(MATH_LIBS) ]) #-------------------------------------------------------------------- # TEA_TCL_EARLY_FLAGS # # Check for what flags are needed to be passed so the correct OS # features are available. # # Arguments: # None # # Results: # # Might define the following vars: # _ISOC99_SOURCE # _LARGEFILE64_SOURCE # _LARGEFILE_SOURCE64 #-------------------------------------------------------------------- AC_DEFUN([TEA_TCL_EARLY_FLAG],[ AC_CACHE_VAL([tcl_cv_flag_]translit($1,[A-Z],[a-z]), AC_TRY_COMPILE([$2], $3, [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no, AC_TRY_COMPILE([[#define ]$1[ 1 ]$2], $3, [tcl_cv_flag_]translit($1,[A-Z],[a-z])=yes, [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no))) if test ["x${tcl_cv_flag_]translit($1,[A-Z],[a-z])[}" = "xyes"] ; then AC_DEFINE($1, 1, [Add the ]$1[ flag when building]) tcl_flags="$tcl_flags $1" fi ]) AC_DEFUN([TEA_TCL_EARLY_FLAGS],[ AC_MSG_CHECKING([for required early compiler flags]) tcl_flags="" TEA_TCL_EARLY_FLAG(_ISOC99_SOURCE,[#include ], [char *p = (char *)strtoll; char *q = (char *)strtoull;]) TEA_TCL_EARLY_FLAG(_LARGEFILE64_SOURCE,[#include ], [struct stat64 buf; int i = stat64("/", &buf);]) TEA_TCL_EARLY_FLAG(_LARGEFILE_SOURCE64,[#include ], [char *p = (char *)open64;]) if test "x${tcl_flags}" = "x" ; then AC_MSG_RESULT([none]) else AC_MSG_RESULT([${tcl_flags}]) fi ]) #-------------------------------------------------------------------- # TEA_TCL_64BIT_FLAGS # # Check for what is defined in the way of 64-bit features. # # Arguments: # None # # Results: # # Might define the following vars: # TCL_WIDE_INT_IS_LONG # TCL_WIDE_INT_TYPE # HAVE_STRUCT_DIRENT64 # HAVE_STRUCT_STAT64 # HAVE_TYPE_OFF64_T #-------------------------------------------------------------------- AC_DEFUN([TEA_TCL_64BIT_FLAGS], [ AC_MSG_CHECKING([for 64-bit integer type]) AC_CACHE_VAL(tcl_cv_type_64bit,[ tcl_cv_type_64bit=none # See if the compiler knows natively about __int64 AC_TRY_COMPILE(,[__int64 value = (__int64) 0;], tcl_type_64bit=__int64, tcl_type_64bit="long long") # See if we should use long anyway Note that we substitute in the # type that is our current guess for a 64-bit type inside this check # program, so it should be modified only carefully... AC_TRY_COMPILE(,[switch (0) { case 1: case (sizeof(]${tcl_type_64bit}[)==sizeof(long)): ; }],tcl_cv_type_64bit=${tcl_type_64bit})]) if test "${tcl_cv_type_64bit}" = none ; then AC_DEFINE(TCL_WIDE_INT_IS_LONG, 1, [Are wide integers to be implemented with C 'long's?]) AC_MSG_RESULT([using long]) elif test "${tcl_cv_type_64bit}" = "__int64" \ -a "${TEA_PLATFORM}" = "windows" ; then # TEA specific: We actually want to use the default tcl.h checks in # this case to handle both TCL_WIDE_INT_TYPE and TCL_LL_MODIFIER* AC_MSG_RESULT([using Tcl header defaults]) else AC_DEFINE_UNQUOTED(TCL_WIDE_INT_TYPE,${tcl_cv_type_64bit}, [What type should be used to define wide integers?]) AC_MSG_RESULT([${tcl_cv_type_64bit}]) # Now check for auxiliary declarations AC_CACHE_CHECK([for struct dirent64], tcl_cv_struct_dirent64,[ AC_TRY_COMPILE([#include #include ],[struct dirent64 p;], tcl_cv_struct_dirent64=yes,tcl_cv_struct_dirent64=no)]) if test "x${tcl_cv_struct_dirent64}" = "xyes" ; then AC_DEFINE(HAVE_STRUCT_DIRENT64, 1, [Is 'struct dirent64' in ?]) fi AC_CACHE_CHECK([for struct stat64], tcl_cv_struct_stat64,[ AC_TRY_COMPILE([#include ],[struct stat64 p; ], tcl_cv_struct_stat64=yes,tcl_cv_struct_stat64=no)]) if test "x${tcl_cv_struct_stat64}" = "xyes" ; then AC_DEFINE(HAVE_STRUCT_STAT64, 1, [Is 'struct stat64' in ?]) fi AC_CHECK_FUNCS(open64 lseek64) AC_MSG_CHECKING([for off64_t]) AC_CACHE_VAL(tcl_cv_type_off64_t,[ AC_TRY_COMPILE([#include ],[off64_t offset; ], tcl_cv_type_off64_t=yes,tcl_cv_type_off64_t=no)]) dnl Define HAVE_TYPE_OFF64_T only when the off64_t type and the dnl functions lseek64 and open64 are defined. if test "x${tcl_cv_type_off64_t}" = "xyes" && \ test "x${ac_cv_func_lseek64}" = "xyes" && \ test "x${ac_cv_func_open64}" = "xyes" ; then AC_DEFINE(HAVE_TYPE_OFF64_T, 1, [Is off64_t in ?]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi fi ]) ## ## Here ends the standard Tcl configuration bits and starts the ## TEA specific functions ## #------------------------------------------------------------------------ # TEA_INIT -- # # Init various Tcl Extension Architecture (TEA) variables. # This should be the first called TEA_* macro. # # Arguments: # none # # Results: # # Defines and substs the following vars: # CYGPATH # EXEEXT # Defines only: # TEA_VERSION # TEA_INITED # TEA_PLATFORM (windows or unix) # # "cygpath" is used on windows to generate native path names for include # files. These variables should only be used with the compiler and linker # since they generate native path names. # # EXEEXT # Select the executable extension based on the host type. This # is a lightweight replacement for AC_EXEEXT that doesn't require # a compiler. #------------------------------------------------------------------------ AC_DEFUN([TEA_INIT], [ # TEA extensions pass this us the version of TEA they think they # are compatible with. TEA_VERSION="3.9" AC_MSG_CHECKING([for correct TEA configuration]) if test x"${PACKAGE_NAME}" = x ; then AC_MSG_ERROR([ The PACKAGE_NAME variable must be defined by your TEA configure.in]) fi if test x"$1" = x ; then AC_MSG_ERROR([ TEA version not specified.]) elif test "$1" != "${TEA_VERSION}" ; then AC_MSG_RESULT([warning: requested TEA version "$1", have "${TEA_VERSION}"]) else AC_MSG_RESULT([ok (TEA ${TEA_VERSION})]) fi # If the user did not set CFLAGS, set it now to keep macros # like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2". if test "${CFLAGS+set}" != "set" ; then CFLAGS="" fi case "`uname -s`" in *win32*|*WIN32*|*MINGW32_*) AC_CHECK_PROG(CYGPATH, cygpath, cygpath -w, echo) EXEEXT=".exe" TEA_PLATFORM="windows" ;; *CYGWIN_*) CYGPATH=echo EXEEXT=".exe" # TEA_PLATFORM is determined later in LOAD_TCLCONFIG ;; *) CYGPATH=echo # Maybe we are cross-compiling.... case ${host_alias} in *mingw32*) EXEEXT=".exe" TEA_PLATFORM="windows" ;; *) EXEEXT="" TEA_PLATFORM="unix" ;; esac ;; esac # Check if exec_prefix is set. If not use fall back to prefix. # Note when adjusted, so that TEA_PREFIX can correct for this. # This is needed for recursive configures, since autoconf propagates # $prefix, but not $exec_prefix (doh!). if test x$exec_prefix = xNONE ; then exec_prefix_default=yes exec_prefix=$prefix fi AC_MSG_NOTICE([configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}]) AC_SUBST(EXEEXT) AC_SUBST(CYGPATH) # This package name must be replaced statically for AC_SUBST to work AC_SUBST(PKG_LIB_FILE) # Substitute STUB_LIB_FILE in case package creates a stub library too. AC_SUBST(PKG_STUB_LIB_FILE) # We AC_SUBST these here to ensure they are subst'ed, # in case the user doesn't call TEA_ADD_... AC_SUBST(PKG_STUB_SOURCES) AC_SUBST(PKG_STUB_OBJECTS) AC_SUBST(PKG_TCL_SOURCES) AC_SUBST(PKG_HEADERS) AC_SUBST(PKG_INCLUDES) AC_SUBST(PKG_LIBS) AC_SUBST(PKG_CFLAGS) ]) #------------------------------------------------------------------------ # TEA_ADD_SOURCES -- # # Specify one or more source files. Users should check for # the right platform before adding to their list. # It is not important to specify the directory, as long as it is # in the generic, win or unix subdirectory of $(srcdir). # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_SOURCES # PKG_OBJECTS #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_SOURCES], [ vars="$@" for i in $vars; do case $i in [\$]*) # allow $-var names PKG_SOURCES="$PKG_SOURCES $i" PKG_OBJECTS="$PKG_OBJECTS $i" ;; *) # check for existence - allows for generic/win/unix VPATH # To add more dirs here (like 'src'), you have to update VPATH # in Makefile.in as well if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \ -a ! -f "${srcdir}/macosx/$i" \ ; then AC_MSG_ERROR([could not find source file '$i']) fi PKG_SOURCES="$PKG_SOURCES $i" # this assumes it is in a VPATH dir i=`basename $i` # handle user calling this before or after TEA_SETUP_COMPILER if test x"${OBJEXT}" != x ; then j="`echo $i | sed -e 's/\.[[^.]]*$//'`.${OBJEXT}" else j="`echo $i | sed -e 's/\.[[^.]]*$//'`.\${OBJEXT}" fi PKG_OBJECTS="$PKG_OBJECTS $j" ;; esac done AC_SUBST(PKG_SOURCES) AC_SUBST(PKG_OBJECTS) ]) #------------------------------------------------------------------------ # TEA_ADD_STUB_SOURCES -- # # Specify one or more source files. Users should check for # the right platform before adding to their list. # It is not important to specify the directory, as long as it is # in the generic, win or unix subdirectory of $(srcdir). # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_STUB_SOURCES # PKG_STUB_OBJECTS #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_STUB_SOURCES], [ vars="$@" for i in $vars; do # check for existence - allows for generic/win/unix VPATH if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \ -a ! -f "${srcdir}/macosx/$i" \ ; then AC_MSG_ERROR([could not find stub source file '$i']) fi PKG_STUB_SOURCES="$PKG_STUB_SOURCES $i" # this assumes it is in a VPATH dir i=`basename $i` # handle user calling this before or after TEA_SETUP_COMPILER if test x"${OBJEXT}" != x ; then j="`echo $i | sed -e 's/\.[[^.]]*$//'`.${OBJEXT}" else j="`echo $i | sed -e 's/\.[[^.]]*$//'`.\${OBJEXT}" fi PKG_STUB_OBJECTS="$PKG_STUB_OBJECTS $j" done AC_SUBST(PKG_STUB_SOURCES) AC_SUBST(PKG_STUB_OBJECTS) ]) #------------------------------------------------------------------------ # TEA_ADD_TCL_SOURCES -- # # Specify one or more Tcl source files. These should be platform # independent runtime files. # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_TCL_SOURCES #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_TCL_SOURCES], [ vars="$@" for i in $vars; do # check for existence, be strict because it is installed if test ! -f "${srcdir}/$i" ; then AC_MSG_ERROR([could not find tcl source file '${srcdir}/$i']) fi PKG_TCL_SOURCES="$PKG_TCL_SOURCES $i" done AC_SUBST(PKG_TCL_SOURCES) ]) #------------------------------------------------------------------------ # TEA_ADD_HEADERS -- # # Specify one or more source headers. Users should check for # the right platform before adding to their list. # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_HEADERS #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_HEADERS], [ vars="$@" for i in $vars; do # check for existence, be strict because it is installed if test ! -f "${srcdir}/$i" ; then AC_MSG_ERROR([could not find header file '${srcdir}/$i']) fi PKG_HEADERS="$PKG_HEADERS $i" done AC_SUBST(PKG_HEADERS) ]) #------------------------------------------------------------------------ # TEA_ADD_INCLUDES -- # # Specify one or more include dirs. Users should check for # the right platform before adding to their list. # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_INCLUDES #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_INCLUDES], [ vars="$@" for i in $vars; do PKG_INCLUDES="$PKG_INCLUDES $i" done AC_SUBST(PKG_INCLUDES) ]) #------------------------------------------------------------------------ # TEA_ADD_LIBS -- # # Specify one or more libraries. Users should check for # the right platform before adding to their list. For Windows, # libraries provided in "foo.lib" format will be converted to # "-lfoo" when using GCC (mingw). # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_LIBS #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_LIBS], [ vars="$@" for i in $vars; do if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then # Convert foo.lib to -lfoo for GCC. No-op if not *.lib i=`echo "$i" | sed -e 's/^\([[^-]].*\)\.lib[$]/-l\1/i'` fi PKG_LIBS="$PKG_LIBS $i" done AC_SUBST(PKG_LIBS) ]) #------------------------------------------------------------------------ # TEA_ADD_CFLAGS -- # # Specify one or more CFLAGS. Users should check for # the right platform before adding to their list. # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_CFLAGS #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_CFLAGS], [ PKG_CFLAGS="$PKG_CFLAGS $@" AC_SUBST(PKG_CFLAGS) ]) #------------------------------------------------------------------------ # TEA_ADD_CLEANFILES -- # # Specify one or more CLEANFILES. # # Arguments: # one or more file names to clean target # # Results: # # Appends to CLEANFILES, already defined for subst in LOAD_TCLCONFIG #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_CLEANFILES], [ CLEANFILES="$CLEANFILES $@" ]) #------------------------------------------------------------------------ # TEA_PREFIX -- # # Handle the --prefix=... option by defaulting to what Tcl gave # # Arguments: # none # # Results: # # If --prefix or --exec-prefix was not specified, $prefix and # $exec_prefix will be set to the values given to Tcl when it was # configured. #------------------------------------------------------------------------ AC_DEFUN([TEA_PREFIX], [ if test "${prefix}" = "NONE"; then prefix_default=yes if test x"${TCL_PREFIX}" != x; then AC_MSG_NOTICE([--prefix defaulting to TCL_PREFIX ${TCL_PREFIX}]) prefix=${TCL_PREFIX} else AC_MSG_NOTICE([--prefix defaulting to /usr/local]) prefix=/usr/local fi fi if test "${exec_prefix}" = "NONE" -a x"${prefix_default}" = x"yes" \ -o x"${exec_prefix_default}" = x"yes" ; then if test x"${TCL_EXEC_PREFIX}" != x; then AC_MSG_NOTICE([--exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}]) exec_prefix=${TCL_EXEC_PREFIX} else AC_MSG_NOTICE([--exec-prefix defaulting to ${prefix}]) exec_prefix=$prefix fi fi ]) #------------------------------------------------------------------------ # TEA_SETUP_COMPILER_CC -- # # Do compiler checks the way we want. This is just a replacement # for AC_PROG_CC in TEA configure.in files to make them cleaner. # # Arguments: # none # # Results: # # Sets up CC var and other standard bits we need to make executables. #------------------------------------------------------------------------ AC_DEFUN([TEA_SETUP_COMPILER_CC], [ # Don't put any macros that use the compiler (e.g. AC_TRY_COMPILE) # in this macro, they need to go into TEA_SETUP_COMPILER instead. AC_PROG_CC AC_PROG_CPP INSTALL="\$(SHELL) \$(srcdir)/tclconfig/install-sh -c" AC_SUBST(INSTALL) INSTALL_DATA="\${INSTALL} -m 644" AC_SUBST(INSTALL_DATA) INSTALL_PROGRAM="\${INSTALL}" AC_SUBST(INSTALL_PROGRAM) INSTALL_SCRIPT="\${INSTALL}" AC_SUBST(INSTALL_SCRIPT) #-------------------------------------------------------------------- # Checks to see if the make program sets the $MAKE variable. #-------------------------------------------------------------------- AC_PROG_MAKE_SET #-------------------------------------------------------------------- # Find ranlib #-------------------------------------------------------------------- AC_CHECK_TOOL(RANLIB, ranlib) #-------------------------------------------------------------------- # Determines the correct binary file extension (.o, .obj, .exe etc.) #-------------------------------------------------------------------- AC_OBJEXT AC_EXEEXT ]) #------------------------------------------------------------------------ # TEA_SETUP_COMPILER -- # # Do compiler checks that use the compiler. This must go after # TEA_SETUP_COMPILER_CC, which does the actual compiler check. # # Arguments: # none # # Results: # # Sets up CC var and other standard bits we need to make executables. #------------------------------------------------------------------------ AC_DEFUN([TEA_SETUP_COMPILER], [ # Any macros that use the compiler (e.g. AC_TRY_COMPILE) have to go here. AC_REQUIRE([TEA_SETUP_COMPILER_CC]) #------------------------------------------------------------------------ # If we're using GCC, see if the compiler understands -pipe. If so, use it. # It makes compiling go faster. (This is only a performance feature.) #------------------------------------------------------------------------ if test -z "$no_pipe" -a -n "$GCC"; then AC_CACHE_CHECK([if the compiler understands -pipe], tcl_cv_cc_pipe, [ hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -pipe" AC_TRY_COMPILE(,, tcl_cv_cc_pipe=yes, tcl_cv_cc_pipe=no) CFLAGS=$hold_cflags]) if test $tcl_cv_cc_pipe = yes; then CFLAGS="$CFLAGS -pipe" fi fi #-------------------------------------------------------------------- # Common compiler flag setup #-------------------------------------------------------------------- AC_C_BIGENDIAN if test "${TEA_PLATFORM}" = "unix" ; then TEA_TCL_LINK_LIBS TEA_MISSING_POSIX_HEADERS # Let the user call this, because if it triggers, they will # need a compat/strtod.c that is correct. Users can also # use Tcl_GetDouble(FromObj) instead. #TEA_BUGGY_STRTOD fi ]) #------------------------------------------------------------------------ # TEA_MAKE_LIB -- # # Generate a line that can be used to build a shared/unshared library # in a platform independent manner. # # Arguments: # none # # Requires: # # Results: # # Defines the following vars: # CFLAGS - Done late here to note disturb other AC macros # MAKE_LIB - Command to execute to build the Tcl library; # differs depending on whether or not Tcl is being # compiled as a shared library. # MAKE_SHARED_LIB Makefile rule for building a shared library # MAKE_STATIC_LIB Makefile rule for building a static library # MAKE_STUB_LIB Makefile rule for building a stub library # VC_MANIFEST_EMBED_DLL Makefile rule for embedded VC manifest in DLL # VC_MANIFEST_EMBED_EXE Makefile rule for embedded VC manifest in EXE #------------------------------------------------------------------------ AC_DEFUN([TEA_MAKE_LIB], [ if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes"; then MAKE_STATIC_LIB="\${STLIB_LD} -out:\[$]@ \$(PKG_OBJECTS)" MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LD_LIBS} \${LDFLAGS_DEFAULT} -out:\[$]@ \$(PKG_OBJECTS)" AC_EGREP_CPP([manifest needed], [ #if defined(_MSC_VER) && _MSC_VER >= 1400 print("manifest needed") #endif ], [ # Could do a CHECK_PROG for mt, but should always be with MSVC8+ VC_MANIFEST_EMBED_DLL="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest -outputresource:\[$]@\;2 ; fi" VC_MANIFEST_EMBED_EXE="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest -outputresource:\[$]@\;1 ; fi" MAKE_SHARED_LIB="${MAKE_SHARED_LIB} ; ${VC_MANIFEST_EMBED_DLL}" TEA_ADD_CLEANFILES([*.manifest]) ]) MAKE_STUB_LIB="\${STLIB_LD} -nodefaultlib -out:\[$]@ \$(PKG_STUB_OBJECTS)" else MAKE_STATIC_LIB="\${STLIB_LD} \[$]@ \$(PKG_OBJECTS)" MAKE_SHARED_LIB="\${SHLIB_LD} -o \[$]@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}" MAKE_STUB_LIB="\${STLIB_LD} \[$]@ \$(PKG_STUB_OBJECTS)" fi if test "${SHARED_BUILD}" = "1" ; then MAKE_LIB="${MAKE_SHARED_LIB} " else MAKE_LIB="${MAKE_STATIC_LIB} " fi #-------------------------------------------------------------------- # Shared libraries and static libraries have different names. # Use the double eval to make sure any variables in the suffix is # substituted. (@@@ Might not be necessary anymore) #-------------------------------------------------------------------- if test "${TEA_PLATFORM}" = "windows" ; then if test "${SHARED_BUILD}" = "1" ; then # We force the unresolved linking of symbols that are really in # the private libraries of Tcl and Tk. if test x"${TK_BIN_DIR}" != x ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TK_BIN_DIR}/${TK_STUB_LIB_FILE}`\"" fi SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}`\"" if test "$GCC" = "yes"; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -static-libgcc" fi eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}" else eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}" if test "$GCC" = "yes"; then PKG_LIB_FILE=lib${PKG_LIB_FILE} fi fi # Some packages build their own stubs libraries eval eval "PKG_STUB_LIB_FILE=${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}" if test "$GCC" = "yes"; then PKG_STUB_LIB_FILE=lib${PKG_STUB_LIB_FILE} fi # These aren't needed on Windows (either MSVC or gcc) RANLIB=: RANLIB_STUB=: else RANLIB_STUB="${RANLIB}" if test "${SHARED_BUILD}" = "1" ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TCL_STUB_LIB_SPEC}" if test x"${TK_BIN_DIR}" != x ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TK_STUB_LIB_SPEC}" fi eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${SHARED_LIB_SUFFIX}" RANLIB=: else eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}" fi # Some packages build their own stubs libraries eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}" fi # These are escaped so that only CFLAGS is picked up at configure time. # The other values will be substituted at make time. CFLAGS="${CFLAGS} \${CFLAGS_DEFAULT} \${CFLAGS_WARNING}" if test "${SHARED_BUILD}" = "1" ; then CFLAGS="${CFLAGS} \${SHLIB_CFLAGS}" fi AC_SUBST(MAKE_LIB) AC_SUBST(MAKE_SHARED_LIB) AC_SUBST(MAKE_STATIC_LIB) AC_SUBST(MAKE_STUB_LIB) AC_SUBST(RANLIB_STUB) AC_SUBST(VC_MANIFEST_EMBED_DLL) AC_SUBST(VC_MANIFEST_EMBED_EXE) ]) #------------------------------------------------------------------------ # TEA_LIB_SPEC -- # # Compute the name of an existing object library located in libdir # from the given base name and produce the appropriate linker flags. # # Arguments: # basename The base name of the library without version # numbers, extensions, or "lib" prefixes. # extra_dir Extra directory in which to search for the # library. This location is used first, then # $prefix/$exec-prefix, then some defaults. # # Requires: # TEA_INIT and TEA_PREFIX must be called first. # # Results: # # Defines the following vars: # ${basename}_LIB_NAME The computed library name. # ${basename}_LIB_SPEC The computed linker flags. #------------------------------------------------------------------------ AC_DEFUN([TEA_LIB_SPEC], [ AC_MSG_CHECKING([for $1 library]) # Look in exec-prefix for the library (defined by TEA_PREFIX). tea_lib_name_dir="${exec_prefix}/lib" # Or in a user-specified location. if test x"$2" != x ; then tea_extra_lib_dir=$2 else tea_extra_lib_dir=NONE fi for i in \ `ls -dr ${tea_extra_lib_dir}/$1[[0-9]]*.lib 2>/dev/null ` \ `ls -dr ${tea_extra_lib_dir}/lib$1[[0-9]]* 2>/dev/null ` \ `ls -dr ${tea_lib_name_dir}/$1[[0-9]]*.lib 2>/dev/null ` \ `ls -dr ${tea_lib_name_dir}/lib$1[[0-9]]* 2>/dev/null ` \ `ls -dr /usr/lib/$1[[0-9]]*.lib 2>/dev/null ` \ `ls -dr /usr/lib/lib$1[[0-9]]* 2>/dev/null ` \ `ls -dr /usr/lib64/$1[[0-9]]*.lib 2>/dev/null ` \ `ls -dr /usr/lib64/lib$1[[0-9]]* 2>/dev/null ` \ `ls -dr /usr/local/lib/$1[[0-9]]*.lib 2>/dev/null ` \ `ls -dr /usr/local/lib/lib$1[[0-9]]* 2>/dev/null ` ; do if test -f "$i" ; then tea_lib_name_dir=`dirname $i` $1_LIB_NAME=`basename $i` $1_LIB_PATH_NAME=$i break fi done if test "${TEA_PLATFORM}" = "windows"; then $1_LIB_SPEC=\"`${CYGPATH} ${$1_LIB_PATH_NAME} 2>/dev/null`\" else # Strip off the leading "lib" and trailing ".a" or ".so" tea_lib_name_lib=`echo ${$1_LIB_NAME}|sed -e 's/^lib//' -e 's/\.[[^.]]*$//' -e 's/\.so.*//'` $1_LIB_SPEC="-L${tea_lib_name_dir} -l${tea_lib_name_lib}" fi if test "x${$1_LIB_NAME}" = x ; then AC_MSG_ERROR([not found]) else AC_MSG_RESULT([${$1_LIB_SPEC}]) fi ]) #------------------------------------------------------------------------ # TEA_PRIVATE_TCL_HEADERS -- # # Locate the private Tcl include files # # Arguments: # # Requires: # TCL_SRC_DIR Assumes that TEA_LOAD_TCLCONFIG has # already been called. # # Results: # # Substitutes the following vars: # TCL_TOP_DIR_NATIVE # TCL_INCLUDES #------------------------------------------------------------------------ AC_DEFUN([TEA_PRIVATE_TCL_HEADERS], [ # Allow for --with-tclinclude to take effect and define ${ac_cv_c_tclh} AC_REQUIRE([TEA_PUBLIC_TCL_HEADERS]) AC_MSG_CHECKING([for Tcl private include files]) TCL_SRC_DIR_NATIVE=`${CYGPATH} ${TCL_SRC_DIR}` TCL_TOP_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}\" # Check to see if tclPort.h isn't already with the public headers # Don't look for tclInt.h because that resides with tcl.h in the core # sources, but the Port headers are in a different directory if test "${TEA_PLATFORM}" = "windows" -a \ -f "${ac_cv_c_tclh}/tclWinPort.h"; then result="private headers found with public headers" elif test "${TEA_PLATFORM}" = "unix" -a \ -f "${ac_cv_c_tclh}/tclUnixPort.h"; then result="private headers found with public headers" else TCL_GENERIC_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/generic\" if test "${TEA_PLATFORM}" = "windows"; then TCL_PLATFORM_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/win\" else TCL_PLATFORM_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/unix\" fi # Overwrite the previous TCL_INCLUDES as this should capture both # public and private headers in the same set. # We want to ensure these are substituted so as not to require # any *_NATIVE vars be defined in the Makefile TCL_INCLUDES="-I${TCL_GENERIC_DIR_NATIVE} -I${TCL_PLATFORM_DIR_NATIVE}" if test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use # the framework's Headers and PrivateHeaders directories case ${TCL_DEFS} in *TCL_FRAMEWORK*) if test -d "${TCL_BIN_DIR}/Headers" -a \ -d "${TCL_BIN_DIR}/PrivateHeaders"; then TCL_INCLUDES="-I\"${TCL_BIN_DIR}/Headers\" -I\"${TCL_BIN_DIR}/PrivateHeaders\" ${TCL_INCLUDES}" else TCL_INCLUDES="${TCL_INCLUDES} ${TCL_INCLUDE_SPEC} `echo "${TCL_INCLUDE_SPEC}" | sed -e 's/Headers/PrivateHeaders/'`" fi ;; esac result="Using ${TCL_INCLUDES}" else if test ! -f "${TCL_SRC_DIR}/generic/tclInt.h" ; then AC_MSG_ERROR([Cannot find private header tclInt.h in ${TCL_SRC_DIR}]) fi result="Using srcdir found in tclConfig.sh: ${TCL_SRC_DIR}" fi fi AC_SUBST(TCL_TOP_DIR_NATIVE) AC_SUBST(TCL_INCLUDES) AC_MSG_RESULT([${result}]) ]) #------------------------------------------------------------------------ # TEA_PUBLIC_TCL_HEADERS -- # # Locate the installed public Tcl header files # # Arguments: # None. # # Requires: # CYGPATH must be set # # Results: # # Adds a --with-tclinclude switch to configure. # Result is cached. # # Substitutes the following vars: # TCL_INCLUDES #------------------------------------------------------------------------ AC_DEFUN([TEA_PUBLIC_TCL_HEADERS], [ AC_MSG_CHECKING([for Tcl public headers]) AC_ARG_WITH(tclinclude, [ --with-tclinclude directory containing the public Tcl header files], with_tclinclude=${withval}) AC_CACHE_VAL(ac_cv_c_tclh, [ # Use the value from --with-tclinclude, if it was given if test x"${with_tclinclude}" != x ; then if test -f "${with_tclinclude}/tcl.h" ; then ac_cv_c_tclh=${with_tclinclude} else AC_MSG_ERROR([${with_tclinclude} directory does not contain tcl.h]) fi else list="" if test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use # the framework's Headers directory case ${TCL_DEFS} in *TCL_FRAMEWORK*) list="`ls -d ${TCL_BIN_DIR}/Headers 2>/dev/null`" ;; esac fi # Look in the source dir only if Tcl is not installed, # and in that situation, look there before installed locations. if test -f "${TCL_BIN_DIR}/Makefile" ; then list="$list `ls -d ${TCL_SRC_DIR}/generic 2>/dev/null`" fi # Check order: pkg --prefix location, Tcl's --prefix location, # relative to directory of tclConfig.sh. eval "temp_includedir=${includedir}" list="$list \ `ls -d ${temp_includedir} 2>/dev/null` \ `ls -d ${TCL_PREFIX}/include 2>/dev/null` \ `ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`" if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then list="$list /usr/local/include /usr/include" if test x"${TCL_INCLUDE_SPEC}" != x ; then d=`echo "${TCL_INCLUDE_SPEC}" | sed -e 's/^-I//'` list="$list `ls -d ${d} 2>/dev/null`" fi fi for i in $list ; do if test -f "$i/tcl.h" ; then ac_cv_c_tclh=$i break fi done fi ]) # Print a message based on how we determined the include path if test x"${ac_cv_c_tclh}" = x ; then AC_MSG_ERROR([tcl.h not found. Please specify its location with --with-tclinclude]) else AC_MSG_RESULT([${ac_cv_c_tclh}]) fi # Convert to a native path and substitute into the output files. INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tclh}` TCL_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\" AC_SUBST(TCL_INCLUDES) ]) #------------------------------------------------------------------------ # TEA_PRIVATE_TK_HEADERS -- # # Locate the private Tk include files # # Arguments: # # Requires: # TK_SRC_DIR Assumes that TEA_LOAD_TKCONFIG has # already been called. # # Results: # # Substitutes the following vars: # TK_INCLUDES #------------------------------------------------------------------------ AC_DEFUN([TEA_PRIVATE_TK_HEADERS], [ # Allow for --with-tkinclude to take effect and define ${ac_cv_c_tkh} AC_REQUIRE([TEA_PUBLIC_TK_HEADERS]) AC_MSG_CHECKING([for Tk private include files]) TK_SRC_DIR_NATIVE=`${CYGPATH} ${TK_SRC_DIR}` TK_TOP_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}\" # Check to see if tkPort.h isn't already with the public headers # Don't look for tkInt.h because that resides with tk.h in the core # sources, but the Port headers are in a different directory if test "${TEA_PLATFORM}" = "windows" -a \ -f "${ac_cv_c_tkh}/tkWinPort.h"; then result="private headers found with public headers" elif test "${TEA_PLATFORM}" = "unix" -a \ -f "${ac_cv_c_tkh}/tkUnixPort.h"; then result="private headers found with public headers" else TK_GENERIC_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/generic\" TK_XLIB_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/xlib\" if test "${TEA_PLATFORM}" = "windows"; then TK_PLATFORM_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/win\" else TK_PLATFORM_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/unix\" fi # Overwrite the previous TK_INCLUDES as this should capture both # public and private headers in the same set. # We want to ensure these are substituted so as not to require # any *_NATIVE vars be defined in the Makefile TK_INCLUDES="-I${TK_GENERIC_DIR_NATIVE} -I${TK_PLATFORM_DIR_NATIVE}" # Detect and add ttk subdir if test -d "${TK_SRC_DIR}/generic/ttk"; then TK_INCLUDES="${TK_INCLUDES} -I\"${TK_SRC_DIR_NATIVE}/generic/ttk\"" fi if test "${TEA_WINDOWINGSYSTEM}" != "x11"; then TK_INCLUDES="${TK_INCLUDES} -I\"${TK_XLIB_DIR_NATIVE}\"" fi if test "${TEA_WINDOWINGSYSTEM}" = "aqua"; then TK_INCLUDES="${TK_INCLUDES} -I\"${TK_SRC_DIR_NATIVE}/macosx\"" fi if test "`uname -s`" = "Darwin"; then # If Tk was built as a framework, attempt to use # the framework's Headers and PrivateHeaders directories case ${TK_DEFS} in *TK_FRAMEWORK*) if test -d "${TK_BIN_DIR}/Headers" -a \ -d "${TK_BIN_DIR}/PrivateHeaders"; then TK_INCLUDES="-I\"${TK_BIN_DIR}/Headers\" -I\"${TK_BIN_DIR}/PrivateHeaders\" ${TK_INCLUDES}" else TK_INCLUDES="${TK_INCLUDES} ${TK_INCLUDE_SPEC} `echo "${TK_INCLUDE_SPEC}" | sed -e 's/Headers/PrivateHeaders/'`" fi ;; esac result="Using ${TK_INCLUDES}" else if test ! -f "${TK_SRC_DIR}/generic/tkInt.h" ; then AC_MSG_ERROR([Cannot find private header tkInt.h in ${TK_SRC_DIR}]) fi result="Using srcdir found in tkConfig.sh: ${TK_SRC_DIR}" fi fi AC_SUBST(TK_TOP_DIR_NATIVE) AC_SUBST(TK_XLIB_DIR_NATIVE) AC_SUBST(TK_INCLUDES) AC_MSG_RESULT([${result}]) ]) #------------------------------------------------------------------------ # TEA_PUBLIC_TK_HEADERS -- # # Locate the installed public Tk header files # # Arguments: # None. # # Requires: # CYGPATH must be set # # Results: # # Adds a --with-tkinclude switch to configure. # Result is cached. # # Substitutes the following vars: # TK_INCLUDES #------------------------------------------------------------------------ AC_DEFUN([TEA_PUBLIC_TK_HEADERS], [ AC_MSG_CHECKING([for Tk public headers]) AC_ARG_WITH(tkinclude, [ --with-tkinclude directory containing the public Tk header files], with_tkinclude=${withval}) AC_CACHE_VAL(ac_cv_c_tkh, [ # Use the value from --with-tkinclude, if it was given if test x"${with_tkinclude}" != x ; then if test -f "${with_tkinclude}/tk.h" ; then ac_cv_c_tkh=${with_tkinclude} else AC_MSG_ERROR([${with_tkinclude} directory does not contain tk.h]) fi else list="" if test "`uname -s`" = "Darwin"; then # If Tk was built as a framework, attempt to use # the framework's Headers directory. case ${TK_DEFS} in *TK_FRAMEWORK*) list="`ls -d ${TK_BIN_DIR}/Headers 2>/dev/null`" ;; esac fi # Look in the source dir only if Tk is not installed, # and in that situation, look there before installed locations. if test -f "${TK_BIN_DIR}/Makefile" ; then list="$list `ls -d ${TK_SRC_DIR}/generic 2>/dev/null`" fi # Check order: pkg --prefix location, Tk's --prefix location, # relative to directory of tkConfig.sh, Tcl's --prefix location, # relative to directory of tclConfig.sh. eval "temp_includedir=${includedir}" list="$list \ `ls -d ${temp_includedir} 2>/dev/null` \ `ls -d ${TK_PREFIX}/include 2>/dev/null` \ `ls -d ${TK_BIN_DIR}/../include 2>/dev/null` \ `ls -d ${TCL_PREFIX}/include 2>/dev/null` \ `ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`" if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then list="$list /usr/local/include /usr/include" if test x"${TK_INCLUDE_SPEC}" != x ; then d=`echo "${TK_INCLUDE_SPEC}" | sed -e 's/^-I//'` list="$list `ls -d ${d} 2>/dev/null`" fi fi for i in $list ; do if test -f "$i/tk.h" ; then ac_cv_c_tkh=$i break fi done fi ]) # Print a message based on how we determined the include path if test x"${ac_cv_c_tkh}" = x ; then AC_MSG_ERROR([tk.h not found. Please specify its location with --with-tkinclude]) else AC_MSG_RESULT([${ac_cv_c_tkh}]) fi # Convert to a native path and substitute into the output files. INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tkh}` TK_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\" AC_SUBST(TK_INCLUDES) if test "${TEA_WINDOWINGSYSTEM}" != "x11"; then # On Windows and Aqua, we need the X compat headers AC_MSG_CHECKING([for X11 header files]) if test ! -r "${INCLUDE_DIR_NATIVE}/X11/Xlib.h"; then INCLUDE_DIR_NATIVE="`${CYGPATH} ${TK_SRC_DIR}/xlib`" TK_XINCLUDES=-I\"${INCLUDE_DIR_NATIVE}\" AC_SUBST(TK_XINCLUDES) fi AC_MSG_RESULT([${INCLUDE_DIR_NATIVE}]) fi ]) #------------------------------------------------------------------------ # TEA_PATH_CONFIG -- # # Locate the ${1}Config.sh file and perform a sanity check on # the ${1} compile flags. These are used by packages like # [incr Tk] that load *Config.sh files from more than Tcl and Tk. # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --with-$1=... # # Defines the following vars: # $1_BIN_DIR Full path to the directory containing # the $1Config.sh file #------------------------------------------------------------------------ AC_DEFUN([TEA_PATH_CONFIG], [ # # Ok, lets find the $1 configuration # First, look for one uninstalled. # the alternative search directory is invoked by --with-$1 # if test x"${no_$1}" = x ; then # we reset no_$1 in case something fails here no_$1=true AC_ARG_WITH($1, [ --with-$1 directory containing $1 configuration ($1Config.sh)], with_$1config=${withval}) AC_MSG_CHECKING([for $1 configuration]) AC_CACHE_VAL(ac_cv_c_$1config,[ # First check to see if --with-$1 was specified. if test x"${with_$1config}" != x ; then case ${with_$1config} in */$1Config.sh ) if test -f ${with_$1config}; then AC_MSG_WARN([--with-$1 argument should refer to directory containing $1Config.sh, not to $1Config.sh itself]) with_$1config=`echo ${with_$1config} | sed 's!/$1Config\.sh$!!'` fi;; esac if test -f "${with_$1config}/$1Config.sh" ; then ac_cv_c_$1config=`(cd ${with_$1config}; pwd)` else AC_MSG_ERROR([${with_$1config} directory doesn't contain $1Config.sh]) fi fi # then check for a private $1 installation if test x"${ac_cv_c_$1config}" = x ; then for i in \ ../$1 \ `ls -dr ../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \ `ls -dr ../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \ `ls -dr ../$1*[[0-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../$1*[[0-9]].[[0-9]]* 2>/dev/null` \ ../../$1 \ `ls -dr ../../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \ `ls -dr ../../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \ `ls -dr ../../$1*[[0-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../$1*[[0-9]].[[0-9]]* 2>/dev/null` \ ../../../$1 \ `ls -dr ../../../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \ `ls -dr ../../../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \ `ls -dr ../../../$1*[[0-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../../$1*[[0-9]].[[0-9]]* 2>/dev/null` \ ${srcdir}/../$1 \ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]] 2>/dev/null` \ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]]* 2>/dev/null` \ ; do if test -f "$i/$1Config.sh" ; then ac_cv_c_$1config=`(cd $i; pwd)` break fi if test -f "$i/unix/$1Config.sh" ; then ac_cv_c_$1config=`(cd $i/unix; pwd)` break fi done fi # check in a few common install locations if test x"${ac_cv_c_$1config}" = x ; then for i in `ls -d ${libdir} 2>/dev/null` \ `ls -d ${exec_prefix}/lib 2>/dev/null` \ `ls -d ${prefix}/lib 2>/dev/null` \ `ls -d /usr/local/lib 2>/dev/null` \ `ls -d /usr/contrib/lib 2>/dev/null` \ `ls -d /usr/lib 2>/dev/null` \ `ls -d /usr/lib64 2>/dev/null` \ ; do if test -f "$i/$1Config.sh" ; then ac_cv_c_$1config=`(cd $i; pwd)` break fi done fi ]) if test x"${ac_cv_c_$1config}" = x ; then $1_BIN_DIR="# no $1 configs found" AC_MSG_WARN([Cannot find $1 configuration definitions]) exit 0 else no_$1= $1_BIN_DIR=${ac_cv_c_$1config} AC_MSG_RESULT([found $$1_BIN_DIR/$1Config.sh]) fi fi ]) #------------------------------------------------------------------------ # TEA_LOAD_CONFIG -- # # Load the $1Config.sh file # # Arguments: # # Requires the following vars to be set: # $1_BIN_DIR # # Results: # # Substitutes the following vars: # $1_SRC_DIR # $1_LIB_FILE # $1_LIB_SPEC #------------------------------------------------------------------------ AC_DEFUN([TEA_LOAD_CONFIG], [ AC_MSG_CHECKING([for existence of ${$1_BIN_DIR}/$1Config.sh]) if test -f "${$1_BIN_DIR}/$1Config.sh" ; then AC_MSG_RESULT([loading]) . "${$1_BIN_DIR}/$1Config.sh" else AC_MSG_RESULT([file not found]) fi # # If the $1_BIN_DIR is the build directory (not the install directory), # then set the common variable name to the value of the build variables. # For example, the variable $1_LIB_SPEC will be set to the value # of $1_BUILD_LIB_SPEC. An extension should make use of $1_LIB_SPEC # instead of $1_BUILD_LIB_SPEC since it will work with both an # installed and uninstalled version of Tcl. # if test -f "${$1_BIN_DIR}/Makefile" ; then AC_MSG_WARN([Found Makefile - using build library specs for $1]) $1_LIB_SPEC=${$1_BUILD_LIB_SPEC} $1_STUB_LIB_SPEC=${$1_BUILD_STUB_LIB_SPEC} $1_STUB_LIB_PATH=${$1_BUILD_STUB_LIB_PATH} $1_INCLUDE_SPEC=${$1_BUILD_INCLUDE_SPEC} $1_LIBRARY_PATH=${$1_LIBRARY_PATH} fi AC_SUBST($1_VERSION) AC_SUBST($1_BIN_DIR) AC_SUBST($1_SRC_DIR) AC_SUBST($1_LIB_FILE) AC_SUBST($1_LIB_SPEC) AC_SUBST($1_STUB_LIB_FILE) AC_SUBST($1_STUB_LIB_SPEC) AC_SUBST($1_STUB_LIB_PATH) # Allow the caller to prevent this auto-check by specifying any 2nd arg AS_IF([test "x$2" = x], [ # Check both upper and lower-case variants # If a dev wanted non-stubs libs, this function could take an option # to not use _STUB in the paths below AS_IF([test "x${$1_STUB_LIB_SPEC}" = x], [TEA_LOAD_CONFIG_LIB(translit($1,[a-z],[A-Z])_STUB)], [TEA_LOAD_CONFIG_LIB($1_STUB)]) ]) ]) #------------------------------------------------------------------------ # TEA_LOAD_CONFIG_LIB -- # # Helper function to load correct library from another extension's # ${PACKAGE}Config.sh. # # Results: # Adds to LIBS the appropriate extension library #------------------------------------------------------------------------ AC_DEFUN([TEA_LOAD_CONFIG_LIB], [ AC_MSG_CHECKING([For $1 library for LIBS]) # This simplifies the use of stub libraries by automatically adding # the stub lib to your path. Normally this would add to SHLIB_LD_LIBS, # but this is called before CONFIG_CFLAGS. More importantly, this adds # to PKG_LIBS, which becomes LIBS, and that is only used by SHLIB_LD. if test "x${$1_LIB_SPEC}" != "x" ; then if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes" ; then TEA_ADD_LIBS([\"`${CYGPATH} ${$1_LIB_PATH}`\"]) AC_MSG_RESULT([using $1_LIB_PATH ${$1_LIB_PATH}]) else TEA_ADD_LIBS([${$1_LIB_SPEC}]) AC_MSG_RESULT([using $1_LIB_SPEC ${$1_LIB_SPEC}]) fi else AC_MSG_RESULT([file not found]) fi ]) #------------------------------------------------------------------------ # TEA_EXPORT_CONFIG -- # # Define the data to insert into the ${PACKAGE}Config.sh file # # Arguments: # # Requires the following vars to be set: # $1 # # Results: # Substitutes the following vars: #------------------------------------------------------------------------ AC_DEFUN([TEA_EXPORT_CONFIG], [ #-------------------------------------------------------------------- # These are for $1Config.sh #-------------------------------------------------------------------- # pkglibdir must be a fully qualified path and (not ${exec_prefix}/lib) eval pkglibdir="[$]{libdir}/$1${PACKAGE_VERSION}" if test "${TCL_LIB_VERSIONS_OK}" = "ok"; then eval $1_LIB_FLAG="-l$1${PACKAGE_VERSION}${DBGX}" eval $1_STUB_LIB_FLAG="-l$1stub${PACKAGE_VERSION}${DBGX}" else eval $1_LIB_FLAG="-l$1`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}" eval $1_STUB_LIB_FLAG="-l$1stub`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}" fi $1_BUILD_LIB_SPEC="-L`pwd` ${$1_LIB_FLAG}" $1_LIB_SPEC="-L${pkglibdir} ${$1_LIB_FLAG}" $1_BUILD_STUB_LIB_SPEC="-L`pwd` [$]{$1_STUB_LIB_FLAG}" $1_STUB_LIB_SPEC="-L${pkglibdir} [$]{$1_STUB_LIB_FLAG}" $1_BUILD_STUB_LIB_PATH="`pwd`/[$]{PKG_STUB_LIB_FILE}" $1_STUB_LIB_PATH="${pkglibdir}/[$]{PKG_STUB_LIB_FILE}" AC_SUBST($1_BUILD_LIB_SPEC) AC_SUBST($1_LIB_SPEC) AC_SUBST($1_BUILD_STUB_LIB_SPEC) AC_SUBST($1_STUB_LIB_SPEC) AC_SUBST($1_BUILD_STUB_LIB_PATH) AC_SUBST($1_STUB_LIB_PATH) AC_SUBST(MAJOR_VERSION) AC_SUBST(MINOR_VERSION) AC_SUBST(PATCHLEVEL) ]) #------------------------------------------------------------------------ # TEA_PATH_CELIB -- # # Locate Keuchel's celib emulation layer for targeting Win/CE # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --with-celib=... # # Defines the following vars: # CELIB_DIR Full path to the directory containing # the include and platform lib files #------------------------------------------------------------------------ AC_DEFUN([TEA_PATH_CELIB], [ # First, look for one uninstalled. # the alternative search directory is invoked by --with-celib if test x"${no_celib}" = x ; then # we reset no_celib in case something fails here no_celib=true AC_ARG_WITH(celib,[ --with-celib=DIR use Windows/CE support library from DIR], with_celibconfig=${withval}) AC_MSG_CHECKING([for Windows/CE celib directory]) AC_CACHE_VAL(ac_cv_c_celibconfig,[ # First check to see if --with-celibconfig was specified. if test x"${with_celibconfig}" != x ; then if test -d "${with_celibconfig}/inc" ; then ac_cv_c_celibconfig=`(cd ${with_celibconfig}; pwd)` else AC_MSG_ERROR([${with_celibconfig} directory doesn't contain inc directory]) fi fi # then check for a celib library if test x"${ac_cv_c_celibconfig}" = x ; then for i in \ ../celib-palm-3.0 \ ../celib \ ../../celib-palm-3.0 \ ../../celib \ `ls -dr ../celib-*3.[[0-9]]* 2>/dev/null` \ ${srcdir}/../celib-palm-3.0 \ ${srcdir}/../celib \ `ls -dr ${srcdir}/../celib-*3.[[0-9]]* 2>/dev/null` \ ; do if test -d "$i/inc" ; then ac_cv_c_celibconfig=`(cd $i; pwd)` break fi done fi ]) if test x"${ac_cv_c_celibconfig}" = x ; then AC_MSG_ERROR([Cannot find celib support library directory]) else no_celib= CELIB_DIR=${ac_cv_c_celibconfig} CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'` AC_MSG_RESULT([found $CELIB_DIR]) fi fi ]) # Local Variables: # mode: autoconf # End: library/mongodb/mongoAPI.decls000066400000000000000000000126771242365656200166640ustar00rootroot00000000000000# -*- Tcl -*- # # API declarations for the nsf mongo interface # # namespaces for types of methods array set ns { cmd "::mongo" } array set ptrConverter { mongoc_client_t 1 mongoc_collection_t 1 mongoc_cursor_t 1 mongoc_gridfs_file_t 1 mongoc_gridfs_t 1 } cmd close NsfMongoClose { {-argName "conn" -required 1 -type mongoc_client_t -withObj 1} } cmd connect NsfMongoConnect { {-argName "-uri" -required 0 -nrargs 1} } cmd run NsfMongoRunCmd { {-argName "-nocomplain" -required 0 -nrargs 0} {-argName "conn" -required 1 -type mongoc_client_t} {-argName "db" -required 1} {-argName "cmd" -required 1 -type tclobj} } cmd status NsfMongoStatus { {-argName "conn" -required 1 -type mongoc_client_t -withObj 1} } # # collection # cmd "collection::close" NsfCollectionClose { {-argName "collection" -required 1 -type mongoc_collection_t -withObj 1} } cmd "collection::count" NsfMongoCollectionCount { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "query" -required 1 -type tclobj} } cmd "collection::delete" NsfMongoCollectionDelete { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "condition" -required 1 -type tclobj} } cmd "collection::index" NsfMongoCollectionIndex { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "attributes" -required 1 -type tclobj} {-argName "-name" -required 0 -nrargs 1} {-argName "-background" -required 0 -nrargs 0} {-argName "-dropdups" -required 0 -nrargs 0} {-argName "-sparse" -required 0 -nrargs 0} {-argName "-ttl" -required 0 -nrargs 1 -type int32} {-argName "-unique" -required 0 -nrargs 0} } cmd "collection::insert" NsfMongoCollectionInsert { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "values" -required 1 -type tclobj} } cmd collection::open NsfCollectionOpen { {-argName "conn" -required 1 -type mongoc_client_t} {-argName "dbname" -required 1} {-argName "collectionname" -required 1} } cmd "collection::query" NsfMongoCollectionQuery { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "query" -required 1 -type tclobj} {-argName "-atts" -required 0 -nrargs 1 -type tclobj} {-argName "-limit" -required 0 -type int32} {-argName "-skip" -required 0 -type int32} } cmd "collection::stats" NsfMongoCollectionStats { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "-options" -required 0 -type tclobj} } cmd "collection::update" NsfMongoCollectionUpdate { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "cond" -required 1 -type tclobj} {-argName "values" -required 1 -type tclobj} {-argName "-upsert" -required 0 -nrargs 0} {-argName "-all" -required 0 -nrargs 0} } # # Cursor # cmd cursor::aggregate NsfMongoCursorAggregate { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "pipeline" -required 1 -type tclobj} {-argName "options" -required 1 -type tclobj} {-argName "-tailable" -required 0 -nrargs 0} {-argName "-awaitdata" -required 0 -nrargs 0} } cmd cursor::find NsfMongoCursorFind { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "query" -required 1 -type tclobj} {-argName "-atts" -required 0 -nrargs 1 -type tclobj} {-argName "-limit" -required 0 -type int32} {-argName "-skip" -required 0 -type int32} {-argName "-tailable" -required 0 -nrargs 0} {-argName "-awaitdata" -required 0 -nrargs 0} } cmd cursor::next NsfMongoCursorNext { {-argName "cursor" -required 1 -type mongoc_cursor_t} } cmd cursor::close NsfMongoCursorClose { {-argName "cursor" -required 1 -type mongoc_cursor_t -withObj 1} } # # GridFS # cmd gridfs::close NsfMongoGridFSClose { {-argName "gfs" -required 1 -type mongoc_gridfs_t -withObj 1} } cmd gridfs::open NsfMongoGridFSOpen { {-argName "conn" -required 1 -type mongoc_client_t} {-argName "dbname" -required 1} {-argName "prefix" -required 1} } # # GridFile commands operating on GridFS # cmd gridfile::create NsfMongoGridFileCreate { {-argName "-source" -required 1 -typeName "gridfilesource" -type "file|string"} {-argName "gfs" -required 1 -type mongoc_gridfs_t} {-argName "value" -required 1} {-argName "name" -required 1} {-argName "contenttype" -required 1} {-argName "-metadata" -type tclobj} } cmd "gridfile::delete" NsfMongoGridFileDelete { {-argName "gfs" -required 1 -type mongoc_gridfs_t} {-argName "query" -required 1 -type tclobj} } cmd "gridfile::open" NsfMongoGridFileOpen { {-argName "gfs" -required 1 -type mongoc_gridfs_t} {-argName "query" -required 1 -type tclobj} } # # GridFile # cmd "gridfile::close" NsfMongoGridFileClose { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t -withObj 1} } cmd "gridfile::get_contentlength" NsfMongoGridFileGetContentlength { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t} } cmd "gridfile::get_contenttype" NsfMongoGridFileGetContentType { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t} } cmd "gridfile::get_metadata" NsfMongoGridFileGetMetaData { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t} } cmd "gridfile::read" NsfMongoGridFileRead { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t} {-argName "size" -required 1 -type int32} } cmd "gridfile::seek" NsfMongoGridFileSeek { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t} {-argName "offset" -required 1 -type int32} } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/mongodb/mongoAPI.h000066400000000000000000001237531242365656200160170ustar00rootroot00000000000000 /* * This source code file was generated by the C-code generator gentclAPI.tcl, * part of the Next Scripting Framework. */ #if defined(USE_NSF_STUBS) int Nsf_ConvertTo_Boolean(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToBoolean(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Class(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToClass(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Int32(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToInt32(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Integer(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToInteger(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Object(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToObject(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Pointer(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToPointer(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_String(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToString(interp, objPtr, pPtr, clientData, outObjPtr); } int Nsf_ConvertTo_Tclobj(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { return Nsf_ConvertToTclobj(interp, objPtr, pPtr, clientData, outObjPtr); } #else # define Nsf_ConvertTo_Boolean Nsf_ConvertToBoolean # define Nsf_ConvertTo_Class Nsf_ConvertToClass # define Nsf_ConvertTo_Int32 Nsf_ConvertToInt32 # define Nsf_ConvertTo_Integer Nsf_ConvertToInteger # define Nsf_ConvertTo_Object Nsf_ConvertToObject # define Nsf_ConvertTo_Pointer Nsf_ConvertToPointer # define Nsf_ConvertTo_String Nsf_ConvertToString # define Nsf_ConvertTo_Tclobj Nsf_ConvertToTclobj #endif #if !defined(likely) # if defined(__GNUC__) && __GNUC__ > 2 /* Use gcc branch prediction hint to minimize cost of e.g. DTrace * ENABLED checks. */ # define unlikely(x) (__builtin_expect((x), 0)) # define likely(x) (__builtin_expect((x), 1)) # else # define unlikely(x) (x) # define likely(x) (x) # endif #endif enum GridfilesourceIdx {GridfilesourceNULL, GridfilesourceFileIdx, GridfilesourceStringIdx}; static int ConvertToGridfilesource(Tcl_Interp *interp, Tcl_Obj *objPtr, Nsf_Param CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { int index, result; static CONST char *opts[] = {"file", "string", NULL}; (void)pPtr; result = Tcl_GetIndexFromObj(interp, objPtr, opts, "gridfilesource", 0, &index); *clientData = (ClientData) INT2PTR(index + 1); *outObjPtr = objPtr; return result; } static Nsf_EnumeratorConverterEntry enumeratorConverterEntries[] = { {ConvertToGridfilesource, "file|string"}, {NULL, NULL} }; /* just to define the symbol */ static Nsf_methodDefinition method_definitions[29]; static CONST char *method_command_namespace_names[] = { "::mongo" }; static int NsfCollectionCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfCollectionOpenStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCollectionCountStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCollectionDeleteStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCollectionIndexStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCollectionInsertStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCollectionQueryStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCollectionStatsStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCollectionUpdateStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoConnectStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCursorAggregateStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCursorCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCursorFindStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoCursorNextStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoGridFSCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoGridFSOpenStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoGridFileCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoGridFileCreateStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoGridFileDeleteStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoGridFileGetContentTypeStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoGridFileGetContentlengthStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoGridFileGetMetaDataStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoGridFileOpenStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoGridFileReadStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoGridFileSeekStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoRunCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfMongoStatusStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []) NSF_nonnull(2) NSF_nonnull(4); static int NsfCollectionClose(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *collectionObj) NSF_nonnull(1) NSF_nonnull(2); static int NsfCollectionOpen(Tcl_Interp *interp, mongoc_client_t *connPtr, CONST char *dbname, CONST char *collectionname) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3) NSF_nonnull(4); static int NsfMongoClose(Tcl_Interp *interp, mongoc_client_t *connPtr, Tcl_Obj *connObj) NSF_nonnull(1) NSF_nonnull(2); static int NsfMongoCollectionCount(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *query) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfMongoCollectionDelete(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *condition) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfMongoCollectionIndex(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *attributes, CONST char *withName, int withBackground, int withDropdups, int withSparse, int withTtl, int withUnique) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfMongoCollectionInsert(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *values) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfMongoCollectionQuery(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *query, Tcl_Obj *withAtts, int withLimit, int withSkip) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfMongoCollectionStats(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *withOptions) NSF_nonnull(1) NSF_nonnull(2); static int NsfMongoCollectionUpdate(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *cond, Tcl_Obj *values, int withUpsert, int withAll) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3) NSF_nonnull(4); static int NsfMongoConnect(Tcl_Interp *interp, CONST char *withUri) NSF_nonnull(1); static int NsfMongoCursorAggregate(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *pipeline, Tcl_Obj *options, int withTailable, int withAwaitdata) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3) NSF_nonnull(4); static int NsfMongoCursorClose(Tcl_Interp *interp, mongoc_cursor_t *cursorPtr, Tcl_Obj *cursorObj) NSF_nonnull(1) NSF_nonnull(2); static int NsfMongoCursorFind(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *query, Tcl_Obj *withAtts, int withLimit, int withSkip, int withTailable, int withAwaitdata) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfMongoCursorNext(Tcl_Interp *interp, mongoc_cursor_t *cursorPtr) NSF_nonnull(1) NSF_nonnull(2); static int NsfMongoGridFSClose(Tcl_Interp *interp, mongoc_gridfs_t *gfsPtr, Tcl_Obj *gfsObj) NSF_nonnull(1) NSF_nonnull(2); static int NsfMongoGridFSOpen(Tcl_Interp *interp, mongoc_client_t *connPtr, CONST char *dbname, CONST char *prefix) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3) NSF_nonnull(4); static int NsfMongoGridFileClose(Tcl_Interp *interp, mongoc_gridfs_file_t *gridfilePtr, Tcl_Obj *gridfileObj) NSF_nonnull(1) NSF_nonnull(2); static int NsfMongoGridFileCreate(Tcl_Interp *interp, int withSource, mongoc_gridfs_t *gfsPtr, CONST char *value, CONST char *name, CONST char *contenttype, Tcl_Obj *withMetadata) NSF_nonnull(1) NSF_nonnull(3) NSF_nonnull(4) NSF_nonnull(5) NSF_nonnull(6); static int NsfMongoGridFileDelete(Tcl_Interp *interp, mongoc_gridfs_t *gfsPtr, Tcl_Obj *query) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfMongoGridFileGetContentType(Tcl_Interp *interp, mongoc_gridfs_file_t *gridfilePtr) NSF_nonnull(1) NSF_nonnull(2); static int NsfMongoGridFileGetContentlength(Tcl_Interp *interp, mongoc_gridfs_file_t *gridfilePtr) NSF_nonnull(1) NSF_nonnull(2); static int NsfMongoGridFileGetMetaData(Tcl_Interp *interp, mongoc_gridfs_file_t *gridfilePtr) NSF_nonnull(1) NSF_nonnull(2); static int NsfMongoGridFileOpen(Tcl_Interp *interp, mongoc_gridfs_t *gfsPtr, Tcl_Obj *query) NSF_nonnull(1) NSF_nonnull(2) NSF_nonnull(3); static int NsfMongoGridFileRead(Tcl_Interp *interp, mongoc_gridfs_file_t *gridfilePtr, int size) NSF_nonnull(1) NSF_nonnull(2); static int NsfMongoGridFileSeek(Tcl_Interp *interp, mongoc_gridfs_file_t *gridfilePtr, int offset) NSF_nonnull(1) NSF_nonnull(2); static int NsfMongoRunCmd(Tcl_Interp *interp, int withNocomplain, mongoc_client_t *connPtr, CONST char *db, Tcl_Obj *cmd) NSF_nonnull(1) NSF_nonnull(3) NSF_nonnull(4) NSF_nonnull(5); static int NsfMongoStatus(Tcl_Interp *interp, mongoc_client_t *connPtr, Tcl_Obj *connObj) NSF_nonnull(1) NSF_nonnull(2); enum { NsfCollectionCloseIdx, NsfCollectionOpenIdx, NsfMongoCloseIdx, NsfMongoCollectionCountIdx, NsfMongoCollectionDeleteIdx, NsfMongoCollectionIndexIdx, NsfMongoCollectionInsertIdx, NsfMongoCollectionQueryIdx, NsfMongoCollectionStatsIdx, NsfMongoCollectionUpdateIdx, NsfMongoConnectIdx, NsfMongoCursorAggregateIdx, NsfMongoCursorCloseIdx, NsfMongoCursorFindIdx, NsfMongoCursorNextIdx, NsfMongoGridFSCloseIdx, NsfMongoGridFSOpenIdx, NsfMongoGridFileCloseIdx, NsfMongoGridFileCreateIdx, NsfMongoGridFileDeleteIdx, NsfMongoGridFileGetContentTypeIdx, NsfMongoGridFileGetContentlengthIdx, NsfMongoGridFileGetMetaDataIdx, NsfMongoGridFileOpenIdx, NsfMongoGridFileReadIdx, NsfMongoGridFileSeekIdx, NsfMongoRunCmdIdx, NsfMongoStatusIdx } NsfMethods; static int NsfCollectionCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfCollectionCloseIdx].paramDefs, method_definitions[NsfCollectionCloseIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_collection_t *collectionPtr = (mongoc_collection_t *)pc.clientData[0]; assert(pc.status == 0); return NsfCollectionClose(interp, collectionPtr,pc.objv[0]); } else { return TCL_ERROR; } } static int NsfCollectionOpenStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfCollectionOpenIdx].paramDefs, method_definitions[NsfCollectionOpenIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_client_t *connPtr = (mongoc_client_t *)pc.clientData[0]; CONST char *dbname = (CONST char *)pc.clientData[1]; CONST char *collectionname = (CONST char *)pc.clientData[2]; assert(pc.status == 0); return NsfCollectionOpen(interp, connPtr, dbname, collectionname); } else { return TCL_ERROR; } } static int NsfMongoCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCloseIdx].paramDefs, method_definitions[NsfMongoCloseIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_client_t *connPtr = (mongoc_client_t *)pc.clientData[0]; assert(pc.status == 0); return NsfMongoClose(interp, connPtr,pc.objv[0]); } else { return TCL_ERROR; } } static int NsfMongoCollectionCountStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCollectionCountIdx].paramDefs, method_definitions[NsfMongoCollectionCountIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_collection_t *collectionPtr = (mongoc_collection_t *)pc.clientData[0]; Tcl_Obj *query = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfMongoCollectionCount(interp, collectionPtr, query); } else { return TCL_ERROR; } } static int NsfMongoCollectionDeleteStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCollectionDeleteIdx].paramDefs, method_definitions[NsfMongoCollectionDeleteIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_collection_t *collectionPtr = (mongoc_collection_t *)pc.clientData[0]; Tcl_Obj *condition = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfMongoCollectionDelete(interp, collectionPtr, condition); } else { return TCL_ERROR; } } static int NsfMongoCollectionIndexStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCollectionIndexIdx].paramDefs, method_definitions[NsfMongoCollectionIndexIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_collection_t *collectionPtr = (mongoc_collection_t *)pc.clientData[0]; Tcl_Obj *attributes = (Tcl_Obj *)pc.clientData[1]; CONST char *withName = (CONST char *)pc.clientData[2]; int withBackground = (int )PTR2INT(pc.clientData[3]); int withDropdups = (int )PTR2INT(pc.clientData[4]); int withSparse = (int )PTR2INT(pc.clientData[5]); int withTtl = (int )PTR2INT(pc.clientData[6]); int withUnique = (int )PTR2INT(pc.clientData[7]); assert(pc.status == 0); return NsfMongoCollectionIndex(interp, collectionPtr, attributes, withName, withBackground, withDropdups, withSparse, withTtl, withUnique); } else { return TCL_ERROR; } } static int NsfMongoCollectionInsertStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCollectionInsertIdx].paramDefs, method_definitions[NsfMongoCollectionInsertIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_collection_t *collectionPtr = (mongoc_collection_t *)pc.clientData[0]; Tcl_Obj *values = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfMongoCollectionInsert(interp, collectionPtr, values); } else { return TCL_ERROR; } } static int NsfMongoCollectionQueryStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCollectionQueryIdx].paramDefs, method_definitions[NsfMongoCollectionQueryIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_collection_t *collectionPtr = (mongoc_collection_t *)pc.clientData[0]; Tcl_Obj *query = (Tcl_Obj *)pc.clientData[1]; Tcl_Obj *withAtts = (Tcl_Obj *)pc.clientData[2]; int withLimit = (int )PTR2INT(pc.clientData[3]); int withSkip = (int )PTR2INT(pc.clientData[4]); assert(pc.status == 0); return NsfMongoCollectionQuery(interp, collectionPtr, query, withAtts, withLimit, withSkip); } else { return TCL_ERROR; } } static int NsfMongoCollectionStatsStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCollectionStatsIdx].paramDefs, method_definitions[NsfMongoCollectionStatsIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_collection_t *collectionPtr = (mongoc_collection_t *)pc.clientData[0]; Tcl_Obj *withOptions = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfMongoCollectionStats(interp, collectionPtr, withOptions); } else { return TCL_ERROR; } } static int NsfMongoCollectionUpdateStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCollectionUpdateIdx].paramDefs, method_definitions[NsfMongoCollectionUpdateIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_collection_t *collectionPtr = (mongoc_collection_t *)pc.clientData[0]; Tcl_Obj *cond = (Tcl_Obj *)pc.clientData[1]; Tcl_Obj *values = (Tcl_Obj *)pc.clientData[2]; int withUpsert = (int )PTR2INT(pc.clientData[3]); int withAll = (int )PTR2INT(pc.clientData[4]); assert(pc.status == 0); return NsfMongoCollectionUpdate(interp, collectionPtr, cond, values, withUpsert, withAll); } else { return TCL_ERROR; } } static int NsfMongoConnectStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoConnectIdx].paramDefs, method_definitions[NsfMongoConnectIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *withUri = (CONST char *)pc.clientData[0]; assert(pc.status == 0); return NsfMongoConnect(interp, withUri); } else { return TCL_ERROR; } } static int NsfMongoCursorAggregateStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCursorAggregateIdx].paramDefs, method_definitions[NsfMongoCursorAggregateIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_collection_t *collectionPtr = (mongoc_collection_t *)pc.clientData[0]; Tcl_Obj *pipeline = (Tcl_Obj *)pc.clientData[1]; Tcl_Obj *options = (Tcl_Obj *)pc.clientData[2]; int withTailable = (int )PTR2INT(pc.clientData[3]); int withAwaitdata = (int )PTR2INT(pc.clientData[4]); assert(pc.status == 0); return NsfMongoCursorAggregate(interp, collectionPtr, pipeline, options, withTailable, withAwaitdata); } else { return TCL_ERROR; } } static int NsfMongoCursorCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCursorCloseIdx].paramDefs, method_definitions[NsfMongoCursorCloseIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_cursor_t *cursorPtr = (mongoc_cursor_t *)pc.clientData[0]; assert(pc.status == 0); return NsfMongoCursorClose(interp, cursorPtr,pc.objv[0]); } else { return TCL_ERROR; } } static int NsfMongoCursorFindStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCursorFindIdx].paramDefs, method_definitions[NsfMongoCursorFindIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_collection_t *collectionPtr = (mongoc_collection_t *)pc.clientData[0]; Tcl_Obj *query = (Tcl_Obj *)pc.clientData[1]; Tcl_Obj *withAtts = (Tcl_Obj *)pc.clientData[2]; int withLimit = (int )PTR2INT(pc.clientData[3]); int withSkip = (int )PTR2INT(pc.clientData[4]); int withTailable = (int )PTR2INT(pc.clientData[5]); int withAwaitdata = (int )PTR2INT(pc.clientData[6]); assert(pc.status == 0); return NsfMongoCursorFind(interp, collectionPtr, query, withAtts, withLimit, withSkip, withTailable, withAwaitdata); } else { return TCL_ERROR; } } static int NsfMongoCursorNextStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCursorNextIdx].paramDefs, method_definitions[NsfMongoCursorNextIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_cursor_t *cursorPtr = (mongoc_cursor_t *)pc.clientData[0]; assert(pc.status == 0); return NsfMongoCursorNext(interp, cursorPtr); } else { return TCL_ERROR; } } static int NsfMongoGridFSCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFSCloseIdx].paramDefs, method_definitions[NsfMongoGridFSCloseIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_gridfs_t *gfsPtr = (mongoc_gridfs_t *)pc.clientData[0]; assert(pc.status == 0); return NsfMongoGridFSClose(interp, gfsPtr,pc.objv[0]); } else { return TCL_ERROR; } } static int NsfMongoGridFSOpenStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFSOpenIdx].paramDefs, method_definitions[NsfMongoGridFSOpenIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_client_t *connPtr = (mongoc_client_t *)pc.clientData[0]; CONST char *dbname = (CONST char *)pc.clientData[1]; CONST char *prefix = (CONST char *)pc.clientData[2]; assert(pc.status == 0); return NsfMongoGridFSOpen(interp, connPtr, dbname, prefix); } else { return TCL_ERROR; } } static int NsfMongoGridFileCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileCloseIdx].paramDefs, method_definitions[NsfMongoGridFileCloseIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_gridfs_file_t *gridfilePtr = (mongoc_gridfs_file_t *)pc.clientData[0]; assert(pc.status == 0); return NsfMongoGridFileClose(interp, gridfilePtr,pc.objv[0]); } else { return TCL_ERROR; } } static int NsfMongoGridFileCreateStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileCreateIdx].paramDefs, method_definitions[NsfMongoGridFileCreateIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withSource = (int )PTR2INT(pc.clientData[0]); mongoc_gridfs_t *gfsPtr = (mongoc_gridfs_t *)pc.clientData[1]; CONST char *value = (CONST char *)pc.clientData[2]; CONST char *name = (CONST char *)pc.clientData[3]; CONST char *contenttype = (CONST char *)pc.clientData[4]; Tcl_Obj *withMetadata = (Tcl_Obj *)pc.clientData[5]; assert(pc.status == 0); return NsfMongoGridFileCreate(interp, withSource, gfsPtr, value, name, contenttype, withMetadata); } else { return TCL_ERROR; } } static int NsfMongoGridFileDeleteStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileDeleteIdx].paramDefs, method_definitions[NsfMongoGridFileDeleteIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_gridfs_t *gfsPtr = (mongoc_gridfs_t *)pc.clientData[0]; Tcl_Obj *query = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfMongoGridFileDelete(interp, gfsPtr, query); } else { return TCL_ERROR; } } static int NsfMongoGridFileGetContentTypeStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileGetContentTypeIdx].paramDefs, method_definitions[NsfMongoGridFileGetContentTypeIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_gridfs_file_t *gridfilePtr = (mongoc_gridfs_file_t *)pc.clientData[0]; assert(pc.status == 0); return NsfMongoGridFileGetContentType(interp, gridfilePtr); } else { return TCL_ERROR; } } static int NsfMongoGridFileGetContentlengthStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileGetContentlengthIdx].paramDefs, method_definitions[NsfMongoGridFileGetContentlengthIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_gridfs_file_t *gridfilePtr = (mongoc_gridfs_file_t *)pc.clientData[0]; assert(pc.status == 0); return NsfMongoGridFileGetContentlength(interp, gridfilePtr); } else { return TCL_ERROR; } } static int NsfMongoGridFileGetMetaDataStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileGetMetaDataIdx].paramDefs, method_definitions[NsfMongoGridFileGetMetaDataIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_gridfs_file_t *gridfilePtr = (mongoc_gridfs_file_t *)pc.clientData[0]; assert(pc.status == 0); return NsfMongoGridFileGetMetaData(interp, gridfilePtr); } else { return TCL_ERROR; } } static int NsfMongoGridFileOpenStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileOpenIdx].paramDefs, method_definitions[NsfMongoGridFileOpenIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_gridfs_t *gfsPtr = (mongoc_gridfs_t *)pc.clientData[0]; Tcl_Obj *query = (Tcl_Obj *)pc.clientData[1]; assert(pc.status == 0); return NsfMongoGridFileOpen(interp, gfsPtr, query); } else { return TCL_ERROR; } } static int NsfMongoGridFileReadStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileReadIdx].paramDefs, method_definitions[NsfMongoGridFileReadIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_gridfs_file_t *gridfilePtr = (mongoc_gridfs_file_t *)pc.clientData[0]; int size = (int )PTR2INT(pc.clientData[1]); assert(pc.status == 0); return NsfMongoGridFileRead(interp, gridfilePtr, size); } else { return TCL_ERROR; } } static int NsfMongoGridFileSeekStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileSeekIdx].paramDefs, method_definitions[NsfMongoGridFileSeekIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_gridfs_file_t *gridfilePtr = (mongoc_gridfs_file_t *)pc.clientData[0]; int offset = (int )PTR2INT(pc.clientData[1]); assert(pc.status == 0); return NsfMongoGridFileSeek(interp, gridfilePtr, offset); } else { return TCL_ERROR; } } static int NsfMongoRunCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoRunCmdIdx].paramDefs, method_definitions[NsfMongoRunCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { int withNocomplain = (int )PTR2INT(pc.clientData[0]); mongoc_client_t *connPtr = (mongoc_client_t *)pc.clientData[1]; CONST char *db = (CONST char *)pc.clientData[2]; Tcl_Obj *cmd = (Tcl_Obj *)pc.clientData[3]; assert(pc.status == 0); return NsfMongoRunCmd(interp, withNocomplain, connPtr, db, cmd); } else { return TCL_ERROR; } } static int NsfMongoStatusStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoStatusIdx].paramDefs, method_definitions[NsfMongoStatusIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongoc_client_t *connPtr = (mongoc_client_t *)pc.clientData[0]; assert(pc.status == 0); return NsfMongoStatus(interp, connPtr,pc.objv[0]); } else { return TCL_ERROR; } } static Nsf_methodDefinition method_definitions[29] = { {"::mongo::collection::close", NsfCollectionCloseStub, 1, { {"collection", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_collection_t",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::collection::open", NsfCollectionOpenStub, 3, { {"conn", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_client_t",NULL,NULL,NULL,NULL,NULL}, {"dbname", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"collectionname", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::close", NsfMongoCloseStub, 1, { {"conn", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_client_t",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::collection::count", NsfMongoCollectionCountStub, 2, { {"collection", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_collection_t",NULL,NULL,NULL,NULL,NULL}, {"query", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::collection::delete", NsfMongoCollectionDeleteStub, 2, { {"collection", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_collection_t",NULL,NULL,NULL,NULL,NULL}, {"condition", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::collection::index", NsfMongoCollectionIndexStub, 8, { {"collection", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_collection_t",NULL,NULL,NULL,NULL,NULL}, {"attributes", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-name", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-background", 0, 0, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-dropdups", 0, 0, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-sparse", 0, 0, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-ttl", 0, 1, Nsf_ConvertTo_Int32, NULL,NULL,"int32",NULL,NULL,NULL,NULL,NULL}, {"-unique", 0, 0, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::collection::insert", NsfMongoCollectionInsertStub, 2, { {"collection", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_collection_t",NULL,NULL,NULL,NULL,NULL}, {"values", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::collection::query", NsfMongoCollectionQueryStub, 5, { {"collection", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_collection_t",NULL,NULL,NULL,NULL,NULL}, {"query", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-atts", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-limit", 0, 1, Nsf_ConvertTo_Int32, NULL,NULL,"int32",NULL,NULL,NULL,NULL,NULL}, {"-skip", 0, 1, Nsf_ConvertTo_Int32, NULL,NULL,"int32",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::collection::stats", NsfMongoCollectionStatsStub, 2, { {"collection", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_collection_t",NULL,NULL,NULL,NULL,NULL}, {"-options", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::collection::update", NsfMongoCollectionUpdateStub, 5, { {"collection", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_collection_t",NULL,NULL,NULL,NULL,NULL}, {"cond", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"values", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-upsert", 0, 0, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-all", 0, 0, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::connect", NsfMongoConnectStub, 1, { {"-uri", 0, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::cursor::aggregate", NsfMongoCursorAggregateStub, 5, { {"collection", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_collection_t",NULL,NULL,NULL,NULL,NULL}, {"pipeline", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"options", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-tailable", 0, 0, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-awaitdata", 0, 0, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::cursor::close", NsfMongoCursorCloseStub, 1, { {"cursor", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_cursor_t",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::cursor::find", NsfMongoCursorFindStub, 7, { {"collection", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_collection_t",NULL,NULL,NULL,NULL,NULL}, {"query", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-atts", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-limit", 0, 1, Nsf_ConvertTo_Int32, NULL,NULL,"int32",NULL,NULL,NULL,NULL,NULL}, {"-skip", 0, 1, Nsf_ConvertTo_Int32, NULL,NULL,"int32",NULL,NULL,NULL,NULL,NULL}, {"-tailable", 0, 0, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-awaitdata", 0, 0, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::cursor::next", NsfMongoCursorNextStub, 1, { {"cursor", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_cursor_t",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::gridfs::close", NsfMongoGridFSCloseStub, 1, { {"gfs", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_gridfs_t",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::gridfs::open", NsfMongoGridFSOpenStub, 3, { {"conn", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_client_t",NULL,NULL,NULL,NULL,NULL}, {"dbname", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"prefix", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::gridfile::close", NsfMongoGridFileCloseStub, 1, { {"gridfile", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_gridfs_file_t",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::gridfile::create", NsfMongoGridFileCreateStub, 6, { {"-source", NSF_ARG_REQUIRED|NSF_ARG_IS_ENUMERATION, 1, ConvertToGridfilesource, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"gfs", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_gridfs_t",NULL,NULL,NULL,NULL,NULL}, {"value", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"name", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"contenttype", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-metadata", 0, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::gridfile::delete", NsfMongoGridFileDeleteStub, 2, { {"gfs", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_gridfs_t",NULL,NULL,NULL,NULL,NULL}, {"query", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::gridfile::get_contenttype", NsfMongoGridFileGetContentTypeStub, 1, { {"gridfile", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_gridfs_file_t",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::gridfile::get_contentlength", NsfMongoGridFileGetContentlengthStub, 1, { {"gridfile", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_gridfs_file_t",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::gridfile::get_metadata", NsfMongoGridFileGetMetaDataStub, 1, { {"gridfile", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_gridfs_file_t",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::gridfile::open", NsfMongoGridFileOpenStub, 2, { {"gfs", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_gridfs_t",NULL,NULL,NULL,NULL,NULL}, {"query", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::gridfile::read", NsfMongoGridFileReadStub, 2, { {"gridfile", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_gridfs_file_t",NULL,NULL,NULL,NULL,NULL}, {"size", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Int32, NULL,NULL,"int32",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::gridfile::seek", NsfMongoGridFileSeekStub, 2, { {"gridfile", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_gridfs_file_t",NULL,NULL,NULL,NULL,NULL}, {"offset", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Int32, NULL,NULL,"int32",NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::run", NsfMongoRunCmdStub, 4, { {"-nocomplain", 0, 0, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"conn", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_client_t",NULL,NULL,NULL,NULL,NULL}, {"db", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_String, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"cmd", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Tclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, {"::mongo::status", NsfMongoStatusStub, 1, { {"conn", NSF_ARG_REQUIRED, 1, Nsf_ConvertTo_Pointer, NULL,NULL,"mongoc_client_t",NULL,NULL,NULL,NULL,NULL}} },{NULL} }; library/mongodb/nsfmongo.c000066400000000000000000001442311242365656200161610ustar00rootroot00000000000000/* * Experimental Interface to MongoDB based on nsf (Next Scripting * Framework) * * The example shows how to use the source code generator from nsf to * generate a c interface. * This implementation provides a low level interface based on tagged * elements to force / preserve the datatypes of mongodb. * * This code serves as well as an example how to use the source code * generator of nsf to provide a C interface for optional nsf * packages. * * -gustaf neumann March 27, 2011 */ #include #include #include #include "bson.h" #include "mongoc.h" #include #include #include #define USE_CLIENT_POOL 1 /* * Define the counters to generate nice symbols for pointer converter */ static int gridfileCount = 0; static int gridfsCount = 0; static int mongoClientCount = 0; static int mongoCollectionCount = 0; static int mongoCursorCount = 0; #if defined(USE_CLIENT_POOL) static NsfMutex poolMutex = 0; static mongoc_client_pool_t *mongoClientPool = NULL; static int mongoClientPoolRefCount = 0; static mongoc_uri_t *mongoUri = NULL; #endif typedef enum { NSF_BSON_ARRAY, NSF_BSON_BOOL, NSF_BSON_INT32, NSF_BSON_INT64, NSF_BSON_DATE_TIME, NSF_BSON_DOCUMENT, NSF_BSON_DOUBLE, NSF_BSON_MINKEY, NSF_BSON_MAXKEY, NSF_BSON_NULL, NSF_BSON_OID, NSF_BSON_REGEX, NSF_BSON_STRING, NSF_BSON_TIMESTAMP, NSF_BSON_UNKNOWN } nsfMongoTypes; static char * NsfMongoGlobalStrings[] = { "array", "boolean", "int32", "int64", "datetime", "document", "double", "minkey", "maxkey", "null", "oid", "regex", "string", "timestamp", "unknown", NULL }; static Tcl_Obj **NsfMongoGlobalObjs = NULL; /*********************************************************************** * The following definitions should not be here, but they are included * to get compilation going for the time being. ***********************************************************************/ typedef void *NsfObject; #define PARSE_CONTEXT_PREALLOC 20 typedef struct { int status; ClientData *clientData; /* 4 members pointer to the actual parse context data */ Tcl_Obj **objv; Tcl_Obj **full_objv; /* contains method as well */ int *flags; ClientData clientData_static[PARSE_CONTEXT_PREALLOC]; /* 3 members preallocated parse context data */ Tcl_Obj *objv_static[PARSE_CONTEXT_PREALLOC+1]; int flags_static[PARSE_CONTEXT_PREALLOC+1]; int lastObjc; /* points to the first "unprocessed" argument */ int objc; int varArgs; /* does the parameter end with some kind of "args" */ NsfObject *object; } ParseContext; #define nr_elements(arr) ((int) (sizeof(arr) / sizeof(arr[0]))) #define ObjStr(obj) (obj)->bytes ? (obj)->bytes : Tcl_GetString(obj) #ifdef UNUSED #elif defined(__GNUC__) # define UNUSED(x) UNUSED_ ## x __attribute__((unused)) #elif defined(__LCLINT__) # define UNUSED(x) /*@unused@*/ x #else # define UNUSED(x) x #endif #if defined(HAVE_STDINT_H) # define HAVE_INTPTR_T # define HAVE_UINTPTR_T #endif #if !defined(INT2PTR) && !defined(PTR2INT) # if defined(HAVE_INTPTR_T) || defined(intptr_t) # define INT2PTR(p) ((void *)(intptr_t)(p)) # define PTR2INT(p) ((int)(intptr_t)(p)) # else # define INT2PTR(p) ((void *)(p)) # define PTR2INT(p) ((int)(p)) # endif #endif #if !defined(UINT2PTR) && !defined(PTR2UINT) # if defined(HAVE_UINTPTR_T) || defined(uintptr_t) # define UINT2PTR(p) ((void *)(uintptr_t)(p)) # define PTR2UINT(p) ((unsigned int)(uintptr_t)(p)) # else # define UINT2PTR(p) ((void *)(p)) # define PTR2UINT(p) ((unsigned int)(p)) # endif #endif static int ArgumentParse(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], NsfObject *obj, Tcl_Obj *procName, Nsf_Param CONST *paramPtr, int nrParameters, int serial, int doCheck, ParseContext *pc) { return Nsf_ArgumentParse(interp, objc, objv, (Nsf_Object *)obj, procName, paramPtr, nrParameters, serial, doCheck, (Nsf_ParseContext *)pc); } /*********************************************************************** * Include the generated mongo db api. ***********************************************************************/ #include "mongoAPI.h" /*********************************************************************** * Helper functions ***********************************************************************/ /* *---------------------------------------------------------------------- * * BsonToList -- * * Convert a bson_t structure to a tagged list. Each value field is * preceded by a tag denoting its bson type. * * Results: * Tagged list. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj * BsonToList(Tcl_Interp *interp, const bson_t *data , int depth) { bson_iter_t i; char oidhex[25]; Tcl_Obj *resultObj, *elemObj; bson_iter_init( &i , data ); resultObj = Tcl_NewListObj(0, NULL); while ( bson_iter_next( &i ) ){ bson_type_t t = bson_iter_type( &i ); nsfMongoTypes tag; const char *key; if ( t == 0 ) break; key = bson_iter_key( &i ); /*fprintf(stderr, "BsonToList: key %s t %d string %d\n", key, t, bson_string);*/ switch ( t ){ case BSON_TYPE_INT32: tag = NSF_BSON_INT32; elemObj = Tcl_NewIntObj(bson_iter_int32( &i )); break; case BSON_TYPE_INT64: tag = NSF_BSON_INT64; elemObj = Tcl_NewLongObj(bson_iter_int64( &i )); break; case BSON_TYPE_DATE_TIME: tag = NSF_BSON_DATE_TIME; elemObj = Tcl_NewLongObj(bson_iter_date_time( &i )); break; case BSON_TYPE_DOUBLE: tag = NSF_BSON_DOUBLE; elemObj = Tcl_NewDoubleObj(bson_iter_double( &i )); break; case BSON_TYPE_BOOL: tag = NSF_BSON_BOOL; elemObj = Tcl_NewBooleanObj(bson_iter_bool( &i )); break; case BSON_TYPE_REGEX: { const char *options = NULL, *regex = NULL; tag = NSF_BSON_REGEX; regex = bson_iter_regex( &i, &options ); elemObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, elemObj, Tcl_NewStringObj(regex, -1)); Tcl_ListObjAppendElement(interp, elemObj, Tcl_NewStringObj(options, -1)); break; } case BSON_TYPE_UTF8: { uint32_t utf8_len; const char *string = bson_iter_utf8( &i, &utf8_len); /*fprintf(stderr, "append UTF8: <%s> %d\n", string, utf8_len);*/ tag = NSF_BSON_STRING; elemObj = Tcl_NewStringObj(string, utf8_len); break; } case BSON_TYPE_MINKEY: tag = NSF_BSON_MINKEY; elemObj = Tcl_NewStringObj("null", 4); break; case BSON_TYPE_MAXKEY: tag = NSF_BSON_MAXKEY; elemObj = Tcl_NewStringObj("null", 4); break; case BSON_TYPE_NULL: tag = NSF_BSON_NULL; elemObj = Tcl_NewStringObj("null", 4); break; case BSON_TYPE_OID: { tag = NSF_BSON_OID; bson_oid_to_string(bson_iter_oid(&i), oidhex); elemObj = Tcl_NewStringObj(oidhex, -1); break; } case BSON_TYPE_TIMESTAMP: { uint32_t timestamp, increment; tag = NSF_BSON_TIMESTAMP; bson_iter_timestamp( &i, ×tamp, &increment ); elemObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, elemObj, Tcl_NewIntObj(timestamp)); Tcl_ListObjAppendElement(interp, elemObj, Tcl_NewIntObj(increment)); break; } case BSON_TYPE_DOCUMENT: { const uint8_t *docbuf = NULL; uint32_t doclen = 0; bson_t b; tag = NSF_BSON_DOCUMENT; bson_iter_document (&i, &doclen, &docbuf); bson_init_static(&b, docbuf, doclen); elemObj = BsonToList(interp, &b , depth + 1 ); break; } case BSON_TYPE_ARRAY: { const uint8_t *docbuf = NULL; uint32_t doclen = 0; bson_t b; tag = NSF_BSON_ARRAY; bson_iter_array(&i, &doclen, &docbuf); bson_init_static (&b, docbuf, doclen); elemObj = BsonToList(interp, &b , depth + 1 ); break; } default: tag = NSF_BSON_UNKNOWN; elemObj = Tcl_NewStringObj("", 0); NsfLog(interp, NSF_LOG_WARN, "BsonToList: unknown type %d", t); } Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(key, -1)); Tcl_ListObjAppendElement(interp, resultObj, NsfMongoGlobalObjs[tag]); Tcl_ListObjAppendElement(interp, resultObj, elemObj); } return resultObj; } /* *---------------------------------------------------------------------- * * BsonTagToType -- * * Convert a bson tag string to a bson_type. For the time being * we compare as little as possible characters. In the future we * might want to cache the bson tag in the Tcl_obj, maybe we can * use Tcl_GetIndexFromObj(); * * Results: * bson_type. * * Side effects: * None. * *---------------------------------------------------------------------- */ bson_type_t BsonTagToType(Tcl_Interp *interp, CONST char *tag) { char firstChar = *tag; switch (firstChar) { case 'a': /* array */ return BSON_TYPE_ARRAY; case 'b': /* bool */ return BSON_TYPE_BOOL; case 'd': if (*(tag + 1) == 'a') /* date */ return BSON_TYPE_DATE_TIME; if (*(tag + 1) == 'o' && *(tag + 2) == 'c') /* document */ return BSON_TYPE_DOCUMENT; if (*(tag + 1) == 'o' && *(tag + 2) == 'u') /* double */ return BSON_TYPE_DOUBLE; break; case 'i': /* int32|64 */ if (*(tag + 1) == 'n' && *(tag + 2) == 't' && *(tag + 3) == '3') return BSON_TYPE_INT32; if (*(tag + 1) == 'n' && *(tag + 2) == 't' && *(tag + 3) == '6') return BSON_TYPE_INT64; if (*(tag + 1) == 'n' && *(tag + 2) == 't') return BSON_TYPE_INT32; break; case 'm': if (*(tag + 1) == 'i') /* minkey */ return BSON_TYPE_MINKEY; if (*(tag + 1) == 'a') /* maxkey */ return BSON_TYPE_MAXKEY; break; case 'n': /* null */ return BSON_TYPE_NULL; case 'o': if (*(tag + 1) == 'i') /* oid */ return BSON_TYPE_OID; break; case 'r': /* regex */ return BSON_TYPE_REGEX; case 's': /* string */ return BSON_TYPE_UTF8; case 't': /* timestamp */ return BSON_TYPE_TIMESTAMP; } NsfLog(interp, NSF_LOG_WARN, "BsonTagToType: Treat unknown tag '%s' as string", tag); return BSON_TYPE_UTF8; } /* *---------------------------------------------------------------------- * * BsonAppend -- * * append a tagged element to a bson buffer. * * Results: * Tcl result code. * * Side effects: * Value appended to bson buffer. * *---------------------------------------------------------------------- */ static int BsonAppend(Tcl_Interp *interp, bson_t *bbPtr, CONST char *name, CONST char *tag, Tcl_Obj *value) { int result = TCL_OK; bson_type_t t = BsonTagToType(interp, tag); int keyLength = strlen(name); /*fprintf(stderr, "BsonAppend: add name %s tag %s value '%s'\n", name, tag, ObjStr(value));*/ switch ( t ){ case BSON_TYPE_UTF8: { const char* string = ObjStr(value); bson_append_utf8(bbPtr, name, keyLength, string, strlen(string)); break; } case BSON_TYPE_INT32: { int32_t v; result = Tcl_GetIntFromObj(interp, value, &v); if (result != TCL_OK) break; bson_append_int32(bbPtr, name, keyLength, v); break; } case BSON_TYPE_DOUBLE: { double v; result = Tcl_GetDoubleFromObj(interp, value, &v); if (result != TCL_OK) break; bson_append_double(bbPtr, name, keyLength, v); break; } case BSON_TYPE_BOOL: { int v; result = Tcl_GetBooleanFromObj(interp, value, &v); if (result != TCL_OK) break; bson_append_bool(bbPtr, name, keyLength, v); break; } case BSON_TYPE_INT64: { long v; result = Tcl_GetLongFromObj(interp, value, &v); if (result != TCL_OK) break; bson_append_int64(bbPtr, name, keyLength, v); break; } case BSON_TYPE_MAXKEY: bson_append_maxkey(bbPtr, name, keyLength); break; case BSON_TYPE_MINKEY: bson_append_minkey(bbPtr, name, keyLength); break; case BSON_TYPE_NULL: { bson_append_null(bbPtr, name, keyLength); break; } case BSON_TYPE_OID: { bson_oid_t v; bson_oid_init_from_string(&v, ObjStr(value)); bson_append_oid(bbPtr, name, keyLength, &v); break; } case BSON_TYPE_REGEX: { int objc = 0; Tcl_Obj **objv; result = Tcl_ListObjGetElements(interp, value, &objc, &objv); if (result != TCL_OK || objc != 2) { return NsfPrintError(interp, "invalid regexp representation: %s", ObjStr(value)); } bson_append_regex(bbPtr, name, keyLength, ObjStr(objv[0]), ObjStr(objv[1])); break; } case BSON_TYPE_DATE_TIME: { long v; result = Tcl_GetLongFromObj(interp, value, &v); if (result != TCL_OK) break; bson_append_date_time(bbPtr, name, keyLength, v); break; } case BSON_TYPE_TIMESTAMP: { int timestamp, increment, objc = 0; Tcl_Obj **objv; result = Tcl_ListObjGetElements(interp, value, &objc, &objv); if (result != TCL_OK || objc != 2) { return NsfPrintError(interp, "invalid timestamp: %s", ObjStr(value)); } result = Tcl_GetIntFromObj(interp, objv[0], ×tamp); if (result == TCL_OK) { result = Tcl_GetIntFromObj(interp, objv[1], &increment); } if (result != TCL_OK) break; bson_append_timestamp(bbPtr, name, keyLength, timestamp, increment); break; } case BSON_TYPE_DOCUMENT: case BSON_TYPE_ARRAY: { int i, objc; Tcl_Obj **objv; bson_t child, *childPtr = &child; result = Tcl_ListObjGetElements(interp, value, &objc, &objv); if (result != TCL_OK || objc % 3 != 0) { return NsfPrintError(interp, "invalid %s value contain multiple of 3 elements %s", tag, ObjStr(value)); } if (t == BSON_TYPE_DOCUMENT) { bson_append_document_begin(bbPtr, name, keyLength, childPtr); } else { bson_append_array_begin(bbPtr, name, keyLength, childPtr); } for (i = 0; i< objc; i += 3) { /*fprintf(stderr, "value %s, i %d, [0]: %s, [1]: %s, [2]: %s\n", ObjStr(value), i, ObjStr(objv[i]), ObjStr(objv[i+1]), ObjStr(objv[i+2]));*/ result = BsonAppend(interp, childPtr, ObjStr(objv[i]), ObjStr(objv[i+1]), objv[i+2]); if (result != TCL_OK) break; } if (t == BSON_TYPE_DOCUMENT) { bson_append_document_end(bbPtr, childPtr); } else { bson_append_array_end(bbPtr, childPtr); } break; } case BSON_TYPE_BINARY: case BSON_TYPE_DBPOINTER: case BSON_TYPE_CODE: case BSON_TYPE_SYMBOL: case BSON_TYPE_CODEWSCOPE: return NsfPrintError(interp, "tag %s not handled yet", tag); break; case BSON_TYPE_UNDEFINED: case BSON_TYPE_EOD: break; /* no default here, to get the warning to the compilation log for the time being */ } return result; } /* *---------------------------------------------------------------------- * * BsonAppendObjv -- * * Append all elements of objv to an uninitialized bson buffer. * * Results: * Tcl result code. * * Side effects: * Value appended to bson buffer. * *---------------------------------------------------------------------- */ static int BsonAppendObjv(Tcl_Interp *interp, bson_t *bPtr, int objc, Tcl_Obj **objv) { int i; bson_init(bPtr); for (i = 0; i < objc; i += 3) { char *name = ObjStr(objv[i]); char *tag = ObjStr(objv[i+1]); Tcl_Obj *value = objv[i+2]; /*fprintf(stderr, "adding pair '%s' (%s) '%s'\n", name, tag, ObjStr(value));*/ BsonAppend(interp, bPtr, name, tag, value); } return TCL_OK; } /*********************************************************************** * Define the api functions ***********************************************************************/ /* cmd close NsfMongoClose { {-argName "conn" -required 1 -type mongoc_client_t -withObj 1} } */ static int NsfMongoClose(Tcl_Interp *interp, mongoc_client_t *clientPtr, Tcl_Obj *clientObj) { if (clientPtr) { #if defined(USE_CLIENT_POOL) mongoc_client_pool_push(mongoClientPool, clientPtr); #else mongoc_client_destroy(clientPtr); #endif Nsf_PointerDelete(ObjStr(clientObj), clientPtr, 0); } return TCL_OK; } /* cmd connect NsfMongoConnect { {-argName "-uri" -required 1 -nrargs 1} } */ static int NsfMongoConnect(Tcl_Interp *interp, CONST char *uri) { char channelName[80]; mongoc_client_t *clientPtr; if (uri == NULL) { uri = "mongodb://127.0.0.1:27017/"; } #if defined(USE_CLIENT_POOL) NsfMutexLock(&poolMutex); if (mongoClientPool == NULL) { mongoUri = mongoc_uri_new(uri); NsfLog(interp, NSF_LOG_NOTICE, "nsf::mongo::connect: creating pool with uri %s", uri); mongoClientPool = mongoc_client_pool_new(mongoUri); } NsfMutexUnlock(&poolMutex); clientPtr = mongoc_client_pool_pop(mongoClientPool); #else clientPtr = mongoc_client_new(uri); #endif if (clientPtr == NULL) { return NsfPrintError(interp, "failed to parse Mongo URI"); } /* * Make an entry in the symbol table and return entry name it as * result. */ if (Nsf_PointerAdd(interp, channelName, "mongoc_client_t", clientPtr) != TCL_OK) { mongoc_client_destroy(clientPtr); return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewStringObj(channelName, -1)); return TCL_OK; } /* cmd run NsfMongoRunCmd { {-argName "-nocomplain" -required 0 -nrargs 0} {-argName "conn" -required 1 -type mongoc_client_t} {-argName "db" -required 1} {-argName "cmd" -required 1 -type tclobj} } */ static int NsfMongoRunCmd(Tcl_Interp *interp, int withNocomplain, mongoc_client_t *clientPtr, CONST char *db, Tcl_Obj *cmdObj) { bson_t cmd, *cmdPtr = &cmd, out, *outPtr = &out; mongoc_read_prefs_t *readPrefsPtr = NULL; /* TODO: not used */ bson_error_t bsonError; int result, objc; Tcl_Obj **objv; result = Tcl_ListObjGetElements(interp, cmdObj, &objc, &objv); if (result != TCL_OK || (objc % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(cmdObj)); } BsonAppendObjv(interp, cmdPtr, objc, objv); /*mongo_clear_errors( connPtr );*/ result = mongoc_client_command_simple( clientPtr, db, cmdPtr, readPrefsPtr, outPtr, &bsonError); bson_destroy( cmdPtr ); if (withNocomplain == 0 && result == 0) { return NsfPrintError(interp, "mongo::run: command '%s' returned error: %s", ObjStr(cmdObj), bsonError.message); } Tcl_SetObjResult(interp, Tcl_NewIntObj(result)); return TCL_OK; } /* cmd status NsfMongoStatus { {-argName "conn" -required 1 -type mongoc_client_t -withObj 1} } */ static int NsfMongoStatus(Tcl_Interp *interp, mongoc_client_t *clientPtr, Tcl_Obj *clientObj) { mongoc_read_prefs_t *readPrefs = NULL; /* TODO: not handled */ bson_t reply, *replyPtr = &reply; bson_error_t bsonError; int result = TCL_OK; if (likely(mongoc_client_get_server_status(clientPtr, readPrefs, replyPtr, &bsonError)) !=0) { Tcl_SetObjResult(interp, BsonToList(interp, replyPtr, 0)); } else { result = NsfPrintError(interp, "mongo::status: error: %s", bsonError.message); } bson_destroy(replyPtr); return result; } /* cmd collection::open NsfCollectionOpen { {-argName "conn" -required 1 -type mongoc_client_t} {-argName "dbname" -required 1} {-argName "collectionname" -required 1} } */ int NsfCollectionOpen(Tcl_Interp *interp, mongoc_client_t *clientPtr, const char *dbName, const char *collectionName) { int result = TCL_ERROR; mongoc_collection_t *collectionPtr; collectionPtr = mongoc_client_get_collection(clientPtr, dbName, collectionName); if (collectionPtr != NULL) { char buffer[80]; if (Nsf_PointerAdd(interp, buffer, "mongoc_collection_t", collectionPtr) == TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewStringObj(buffer, -1)); result = TCL_OK; } else { mongoc_collection_destroy(collectionPtr); result = TCL_ERROR; } } if (collectionPtr == NULL) { result = NsfPrintError(interp, "collection::open: could not open collection: %s.%s", dbName, collectionName); } return result; } /* cmd collection::close NsfCollectionClose { {-argName "collection" -required 1 -type mongoc_collection_t -withObj 1} } */ static int NsfCollectionClose(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *clientObj) { if (collectionPtr) { mongoc_collection_destroy(collectionPtr); Nsf_PointerDelete(ObjStr(clientObj), collectionPtr, 0); } return TCL_OK; } /* cmd collection::count NsfMongoCollectionCount { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "query" -required 1 -type tclobj} } */ static int NsfMongoCollectionCount(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *queryObj) { int objc, result; Tcl_Obj **objv; int count; bson_t query, *queryPtr = &query; bson_error_t bsonError; result = Tcl_ListObjGetElements(interp, queryObj, &objc, &objv); if (result != TCL_OK || (objc % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(queryObj)); } BsonAppendObjv(interp, queryPtr, objc, objv); if (collectionPtr != NULL) { count = mongoc_collection_count(collectionPtr, 0 /* query flags */, queryPtr, 0 /*skip */, 0 /*limit */, NULL /* read preferences */, &bsonError); if (count == -1) { bson_destroy( queryPtr ); return NsfPrintError(interp, "mongo::collection::count: error: %s", bsonError.message); } } else { count = 0; } bson_destroy( queryPtr ); Tcl_SetObjResult(interp, Tcl_NewIntObj(count)); return TCL_OK; } /* cmd "collection::delete" NsfMongoCollectionDelete { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "condition" -required 1 -type tclobj} } */ static int NsfMongoCollectionDelete(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *conditionObj) { int objc, result, success; Tcl_Obj **objv; bson_t query, *queryPtr = &query; bson_error_t bsonError; mongoc_delete_flags_t deleteFlags = 0; /* TODO: not handled */ /* MONGOC_DELETE_SINGLE_REMOVE = 1 << 0,**/ const mongoc_write_concern_t *writeConcern = NULL; /* TODO: not handled yet */ result = Tcl_ListObjGetElements(interp, conditionObj, &objc, &objv); if (result != TCL_OK || (objc % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(conditionObj)); } BsonAppendObjv(interp, queryPtr, objc, objv); success = mongoc_collection_remove(collectionPtr, deleteFlags, queryPtr, writeConcern, &bsonError); if (success == 0) { result = NsfPrintError(interp, "mongo::collection::delete: error: %s", bsonError.message); } bson_destroy(queryPtr); return result; } /* cmd "collection::index" NsfMongoCollectionIndex { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "attributes" -required 1 -type tclobj} {-argName "-name" -required 0 -nrargs 1} {-argName "-background" -required 0 -nrargs 0} {-argName "-dropdups" -required 0 -nrargs 0} {-argName "-sparse" -required 0 -nrargs 0} {-argName "-ttl" -required 0 -nrargs 1 -type int32} {-argName "-unique" -required 0 -nrargs 0} } */ static int NsfMongoCollectionIndex(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *attributesObj, CONST char *withName, int withBackground, int withDropdups, int withSparse, int withTtl, int withUnique) { int success = 0; int objc, result; Tcl_Obj **objv; bson_t keys, *keysPtr = &keys; bson_error_t bsonError; mongoc_index_opt_t options; result = Tcl_ListObjGetElements(interp, attributesObj, &objc, &objv); if (result != TCL_OK || (objc % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(attributesObj)); } BsonAppendObjv(interp, keysPtr, objc, objv); mongoc_index_opt_init(&options); if (withBackground) {options.background = 1;} if (withDropdups) {options.drop_dups = 1;} if (withSparse) {options.sparse = 1;} if (withUnique) {options.unique = 1;} if (withTtl) {options.expire_after_seconds = withTtl;} if (withName) {options.name = withName;} /* TODO: not handled: is_initialized, v, weights, default_language, laguage_override, padding */ success = mongoc_collection_create_index(collectionPtr, keysPtr, &options, &bsonError); bson_destroy(keysPtr); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(success)); return TCL_OK; } /* cmd "collection::insert" NsfMongoCollectionInsert { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "values" -required 1 -type tclobj} } */ static int NsfMongoCollectionInsert(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *valuesObj) { int i, objc, result, success; Tcl_Obj **objv; bson_t bson, *bsonPtr = &bson; bson_oid_t oid; bson_error_t bsonError; mongoc_insert_flags_t insertFlags = MONGOC_INSERT_NO_VALIDATE; /* otherwise, we can't insert a DBRef */ /* TODO: insertFlags not handled: MONGOC_INSERT_NONE = 0, MONGOC_INSERT_CONTINUE_ON_ERROR = 1 << 0, MONGOC_INSERT_NO_VALIDATE = 1 << 31, */ const mongoc_write_concern_t *writeConcern = NULL; /* TODO: not handled yet */ result = Tcl_ListObjGetElements(interp, valuesObj, &objc, &objv); if (result != TCL_OK || (objc % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(valuesObj)); } bson_init(bsonPtr); bson_oid_init(&oid, NULL); bson_append_oid(bsonPtr, "_id", 3, &oid); for (i = 0; i < objc; i += 3) { CONST char *name = ObjStr(objv[i]); CONST char *tag = ObjStr(objv[i+1]); Tcl_Obj *value = objv[i+2]; /*fprintf(stderr, "adding pair '%s' (%s) '%s'\n", name, tag, ObjStr(value));*/ BsonAppend(interp, bsonPtr, name, tag, value); } success = mongoc_collection_insert(collectionPtr, insertFlags, bsonPtr, writeConcern, &bsonError); if (success == 0) { result = NsfPrintError(interp, "mongo::collection::insert: error: %s", bsonError.message); } else { Tcl_SetObjResult(interp, BsonToList(interp, bsonPtr, 0)); } bson_destroy(bsonPtr); return result; } /* cmd collection::query NsfMongoCollectionQuery { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "query" -required 1 -type tclobj} {-argName "-atts" -required 0 -nrargs 1 -type tclobj} {-argName "-limit" -required 0 -type int32} {-argName "-skip" -required 0 -type int32} } */ static int NsfMongoCollectionQuery(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *queryObj, Tcl_Obj *withAttsObj, int withLimit, int withSkip) { int objc1, objc2 = 0, result; Tcl_Obj **objv1, **objv2 = NULL, *resultObj; mongoc_cursor_t *cursor; bson_t query, *queryPtr = &query; bson_t atts, *attsPtr = &atts; const bson_t *nextPtr; mongoc_query_flags_t queryFlags = 0; /* TODO: not handled */ mongoc_read_prefs_t *readPrefs = NULL; /* TODO: not handled */ /*fprintf(stderr, "NsfMongoQuery: namespace %s withLimit %d withSkip %d\n", namespace, withLimit, withSkip);*/ result = Tcl_ListObjGetElements(interp, queryObj, &objc1, &objv1); if (result != TCL_OK || (objc1 % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(queryObj)); } if (withAttsObj) { result = Tcl_ListObjGetElements(interp, withAttsObj, &objc2, &objv2); if (result != TCL_OK || (objc2 % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(withAttsObj)); } } else { objc2 = 0; } /* fprintf(stderr, "query # %d, atts # %d\n", objc1, objc2); */ BsonAppendObjv(interp, queryPtr, objc1, objv1); BsonAppendObjv(interp, attsPtr, objc2, objv2); resultObj = Tcl_NewListObj(0, NULL); /* * The last field of mongo_find is options, semantics are described here * http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-OPQUERY */ cursor = mongoc_collection_find( collectionPtr, queryFlags, withSkip, withLimit, 0 /* batch_size */, queryPtr, attsPtr, readPrefs); while( mongoc_cursor_next( cursor, &nextPtr ) == 1 ) { Tcl_ListObjAppendElement(interp, resultObj, BsonToList(interp, nextPtr, 0)); } mongoc_cursor_destroy( cursor ); bson_destroy( queryPtr ); bson_destroy( attsPtr ); Tcl_SetObjResult(interp, resultObj); return TCL_OK; } /* cmd "collection::stats" NsfMongoCollectionStats { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "-options" -required 0 -type tclobj} } */ static int NsfMongoCollectionStats(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *optionsObj) { int objc = 0, success; Tcl_Obj **objv = NULL; bson_t options, *optionsPtr = NULL; bson_t stats, *statsPtr = &stats; bson_error_t bsonError; if (optionsObj) { int result = Tcl_ListObjGetElements(interp, optionsObj, &objc, &objv); if (result != TCL_OK || (objc % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(optionsObj)); } optionsPtr = &options; BsonAppendObjv(interp, optionsPtr, objc, objv); } success = mongoc_collection_stats(collectionPtr, optionsPtr, statsPtr, &bsonError); if (optionsPtr) { bson_destroy (optionsPtr); } if (success) { Tcl_SetObjResult(interp, BsonToList(interp, statsPtr, 0)); bson_destroy (statsPtr); return TCL_OK; } else { return NsfPrintError(interp, "mongo::collection::stats: error: %s", bsonError.message); } } /* cmd "collection::update" NsfMongoCollectionUpdate { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "cond" -required 1 -type tclobj} {-argName "values" -required 1 -type tclobj} {-argName "-upsert" -required 0 -nrargs 0} {-argName "-all" -required 0 -nrargs 0} } */ static int NsfMongoCollectionUpdate(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *conditionObj, Tcl_Obj *valuesObj, int withUpsert, int withAll) { const mongoc_write_concern_t *writeConcern = NULL; /* TODO: not handled yet */ mongoc_update_flags_t updateFlags = MONGOC_UPDATE_NO_VALIDATE; /* for dbrefs */ bson_error_t bsonError; bson_t cond, *condPtr = &cond, values, *valuesPtr = &values; int objc, result, success; Tcl_Obj **objv; result = Tcl_ListObjGetElements(interp, conditionObj, &objc, &objv); if (result != TCL_OK || (objc % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(conditionObj)); } BsonAppendObjv(interp, condPtr, objc, objv); result = Tcl_ListObjGetElements(interp, valuesObj, &objc, &objv); if (result != TCL_OK || (objc % 3 != 0)) { bson_destroy(condPtr); return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(valuesObj)); } BsonAppendObjv(interp, valuesPtr, objc, objv); if (withUpsert) {updateFlags |= MONGOC_UPDATE_UPSERT;} if (withAll) {updateFlags |= MONGOC_UPDATE_MULTI_UPDATE;} success = mongoc_collection_update(collectionPtr, updateFlags, condPtr, valuesPtr, writeConcern, &bsonError); if (success == 0) { result = NsfPrintError(interp, "mongo::collection::delete: error: %s", bsonError.message); } return result; } /*********************************************************************** * Cursor interface ***********************************************************************/ /* cmd cursor::aggregate NsfMongoCursorAggregate { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "pipeline" -required 1 -type tclobj} {-argName "options" -required 1 -type tclobj} {-argName "-tailable" -required 0 -nrargs 0} {-argName "-awaitdata" -required 0 -nrargs 0} } */ static int NsfMongoCursorAggregate(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *pipelineObj, Tcl_Obj *optionsObj, int withTailable, int withAwaitdata) { int objc1, objc2, result; mongoc_query_flags_t queryFlags = 0; Tcl_Obj **objv1, **objv2 = NULL; mongoc_cursor_t *cursor; bson_t pipeline, *pipelinePtr = &pipeline; bson_t options, *optionsPtr = &options; mongoc_read_prefs_t *readPrefsPtr = NULL; /* TODO: not used */ result = Tcl_ListObjGetElements(interp, pipelineObj, &objc1, &objv1); if (result != TCL_OK || (objc1 % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(pipelineObj)); } result = Tcl_ListObjGetElements(interp, optionsObj, &objc2, &objv2); if (result != TCL_OK || (objc2 % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(optionsObj)); } BsonAppendObjv(interp, pipelinePtr, objc1, objv1); BsonAppendObjv(interp, optionsPtr, objc2, objv2); /* * The last field of mongo_find is options, semantics are described here * http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-OPQUERY */ if (withTailable) { queryFlags |= MONGOC_QUERY_TAILABLE_CURSOR; } if (withAwaitdata) { queryFlags |= MONGOC_QUERY_AWAIT_DATA; } /* TODO: query flags: MONGOC_QUERY_SLAVE_OK = 1 << 2, MONGOC_QUERY_OPLOG_REPLAY = 1 << 3, MONGOC_QUERY_NO_CURSOR_TIMEOUT = 1 << 4, MONGOC_QUERY_EXHAUST = 1 << 6, MONGOC_QUERY_PARTIAL = 1 << 7, */ cursor = mongoc_collection_aggregate(collectionPtr, queryFlags, pipelinePtr, optionsPtr, readPrefsPtr); if (cursor) { char buffer[80]; if (Nsf_PointerAdd(interp, buffer, "mongoc_cursor_t", cursor) == TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewStringObj(buffer, -1)); } else { mongoc_cursor_destroy( cursor ); result = TCL_ERROR; } } else { Tcl_ResetResult(interp); } bson_destroy( pipelinePtr ); bson_destroy( optionsPtr ); return result; } /* cmd cursor::find NsfMongoCursorFind { {-argName "collection" -required 1 -type mongoc_collection_t} {-argName "query" -required 1 -type tclobj} {-argName "-atts" -required 0 -nrargs 1 -type tclobj} {-argName "-limit" -required 0 -type int32} {-argName "-skip" -required 0 -type int32} {-argName "-tailable" -required 0 -nrargs 0} {-argName "-awaitdata" -required 0 -nrargs 0} } */ static int NsfMongoCursorFind(Tcl_Interp *interp, mongoc_collection_t *collectionPtr, Tcl_Obj *queryObj, Tcl_Obj *withAttsObj, int withLimit, int withSkip, int withTailable, int withAwaitdata) { int objc1, objc2 = 0, result; mongoc_query_flags_t queryFlags = 0; Tcl_Obj **objv1, **objv2 = NULL; mongoc_cursor_t *cursor; bson_t query, *queryPtr = &query; bson_t atts, *attsPtr = &atts; mongoc_read_prefs_t *readPrefsPtr = NULL; /* TODO: not used */ /*fprintf(stderr, "NsfMongoQuery: namespace %s withLimit %d withSkip %d\n", namespace, withLimit, withSkip);*/ result = Tcl_ListObjGetElements(interp, queryObj, &objc1, &objv1); if (result != TCL_OK || (objc1 % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(queryObj)); } if (withAttsObj) { result = Tcl_ListObjGetElements(interp, withAttsObj, &objc2, &objv2); if (result != TCL_OK || (objc2 % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(withAttsObj)); } } BsonAppendObjv(interp, queryPtr, objc1, objv1); BsonAppendObjv(interp, attsPtr, objc2, objv2); /* * The last field of mongo_find is options, semantics are described here * http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-OPQUERY */ if (withTailable) { queryFlags |= MONGOC_QUERY_TAILABLE_CURSOR; } if (withAwaitdata) { queryFlags |= MONGOC_QUERY_AWAIT_DATA; } /* TODO: query flags: MONGOC_QUERY_SLAVE_OK = 1 << 2, MONGOC_QUERY_OPLOG_REPLAY = 1 << 3, MONGOC_QUERY_NO_CURSOR_TIMEOUT = 1 << 4, MONGOC_QUERY_EXHAUST = 1 << 6, MONGOC_QUERY_PARTIAL = 1 << 7, */ cursor = mongoc_collection_find(collectionPtr, queryFlags, withSkip, withLimit, 0 /*TODO missing batch_size*/, queryPtr, attsPtr, readPrefsPtr); if (cursor) { char buffer[80]; if (Nsf_PointerAdd(interp, buffer, "mongoc_cursor_t", cursor) == TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewStringObj(buffer, -1)); } else { mongoc_cursor_destroy( cursor ); result = TCL_ERROR; } } else { Tcl_ResetResult(interp); } bson_destroy( queryPtr ); bson_destroy( attsPtr ); return result; } /* cmd cursor::next NsfMongoCursorNext { {-argName "cursor" -required 1 -type mongoc_cursor_t} } */ static int NsfMongoCursorNext(Tcl_Interp *interp, mongoc_cursor_t *cursor) { int result; const bson_t *nextPtr; result = mongoc_cursor_next( cursor, &nextPtr ); if (result == 1) { Tcl_SetObjResult(interp, BsonToList(interp, nextPtr, 0)); } return TCL_OK; } /* cmd cursor::close NsfMongoCursorClose { {-argName "cursor" -required 1 -type mongoc_cursor_t -withObj 1} } */ static int NsfMongoCursorClose(Tcl_Interp *interp, mongoc_cursor_t *cursor, Tcl_Obj *cursorObj) { mongoc_cursor_destroy( cursor ); Nsf_PointerDelete(ObjStr(cursorObj), cursor, 0); return TCL_OK; } /*********************************************************************** * GridFS interface ***********************************************************************/ /* cmd gridfs::close NsfMongoGridFSClose { {-argName "gfs" -required 1 -type mongoc_gridfs_t -withObj 1} } */ static int NsfMongoGridFSClose(Tcl_Interp *interp, mongoc_gridfs_t *gridfsPtr, Tcl_Obj *gridfsObj) { mongoc_gridfs_destroy(gridfsPtr); Nsf_PointerDelete(ObjStr(gridfsObj), gridfsPtr, 0); return TCL_OK; } /* cmd gridfs::open NsfMongoGridFSOpen { {-argName "conn" -required 1 -type mongoc_client_t} {-argName "dbname" -required 1} {-argName "prefix" -required 1} } */ static int NsfMongoGridFSOpen(Tcl_Interp *interp, mongoc_client_t *clientPtr, CONST char *dbname, CONST char *prefix) { char buffer[80]; int result = TCL_OK; bson_error_t bsonError; mongoc_gridfs_t *gfsPtr; gfsPtr = mongoc_client_get_gridfs(clientPtr, dbname, prefix, &bsonError); if (gfsPtr == NULL) { result = NsfPrintError(interp, "mongo::gridfs::open: error: %s", bsonError.message); } if (Nsf_PointerAdd(interp, buffer, "mongoc_gridfs_t", gfsPtr) == TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewStringObj(buffer, -1)); } else { mongoc_gridfs_destroy(gfsPtr); result = TCL_ERROR; } return result; } /*********************************************************************** * GridFile interface operating on GridFS ***********************************************************************/ #define MONGOC_GRIDFS_READ_CHUNK 4096*4 /* cmd gridfile::create NsfMongoGridFileCreate { {-argName "-source" -required 1 -typeName "gridfilesource" -type "file|string"} {-argName "gfs" -required 1 -type mongoc_gridfs_t} {-argName "value" -required 1} {-argName "name" -required 1} {-argName "contenttype" -required 1} {-argName "-metadata" -required 0 -nrags 1 -type tclobj} } */ static int NsfMongoGridFileCreate(Tcl_Interp *interp, int withSource, mongoc_gridfs_t *gridfsPtr, CONST char *value, CONST char *name, CONST char *contenttype, Tcl_Obj *withMetadata ) { int result = TCL_OK; mongoc_gridfs_file_opt_t fileOpts = {NULL}; mongoc_gridfs_file_t *gridFile; bson_t bsonMetaData, *bsonMetaDataPtr = &bsonMetaData; if (withSource == GridfilesourceNULL) { withSource = GridfilesourceFileIdx; } if (withMetadata != NULL) { Tcl_Obj **objv; int objc; result = Tcl_ListObjGetElements(interp, withMetadata, &objc, &objv); if (result != TCL_OK || (objc % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(withMetadata)); } BsonAppendObjv(interp, bsonMetaDataPtr, objc, objv); fileOpts.metadata = bsonMetaDataPtr; } fileOpts.filename = name; fileOpts.content_type = contenttype; /* const char *md5; const bson_t *aliases; uint32_t chunk_size; */ gridFile = mongoc_gridfs_create_file(gridfsPtr, &fileOpts); if (withSource == GridfilesourceFileIdx) { uint8_t buf[MONGOC_GRIDFS_READ_CHUNK]; struct iovec iov = { buf, 0 }; int fd = open(value, O_RDONLY); if (fd < 1) { mongoc_gridfs_file_destroy(gridFile); return NsfPrintError(interp, "nsf::gridfile::create: cannot open file '%s' for reading", value); } for (;; ) { int n = read(fd, iov.iov_base, MONGOC_GRIDFS_READ_CHUNK); if (n > 0) { iov.iov_len = n; n = mongoc_gridfs_file_writev(gridFile, &iov, 1, 0); } else if (n == 0) { break; } else { result = TCL_ERROR; break; } } close(fd); } else { struct iovec iov = { (char *)value, strlen(value) }; mongoc_gridfs_file_writev(gridFile, &iov, 1, 0); } if (result == TCL_OK) { mongoc_gridfs_file_save(gridFile); } mongoc_gridfs_file_destroy(gridFile); Tcl_SetObjResult(interp, Tcl_NewIntObj(result == TCL_OK)); return result; } /* cmd "gridfile::delete" NsfMongoGridFileDelete { {-argName "gfs" -required 1 -type mongoc_gridfs_t} {-argName "query" -required 1 -type tclobj} } */ static int NsfMongoGridFileDelete(Tcl_Interp *interp, mongoc_gridfs_t *gridfsPtr, Tcl_Obj *queryObj) { bson_t query, *queryPtr = &query; mongoc_cursor_t *files; const bson_t *nextPtr; bson_iter_t it; Tcl_Obj **objv; int objc, result; result = Tcl_ListObjGetElements(interp, queryObj, &objc, &objv); if (result != TCL_OK || (objc % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(queryObj)); } BsonAppendObjv(interp, queryPtr, objc, objv); files = mongoc_collection_find( mongoc_gridfs_get_files(gridfsPtr), 0, 0, 0, 0 /* batch_size */, queryPtr, NULL, NULL); bson_destroy(queryPtr); /* files should be a valid cursor even if the file doesn't exist */ if ( files == NULL ) { return NsfPrintError(interp, "gridfs::remove_file: invalid cursor for files"); } /* Remove each file and it's chunks from files named filename */ while (mongoc_cursor_next(files, &nextPtr)) { bson_t bson, *bsonPtr = &bson; bson_error_t bsonError; bson_oid_t id; bson_iter_init_find(&it, nextPtr, "_id"); id = *bson_iter_oid(&it); /* Remove the file with the specified id */ bson_init(bsonPtr); bson_append_oid(bsonPtr, "_id", 3, &id); mongoc_collection_remove(mongoc_gridfs_get_files(gridfsPtr), 0, bsonPtr, NULL, &bsonError); bson_destroy(bsonPtr); /* Remove all chunks from the file with the specified id */ bson_init(bsonPtr); bson_append_oid(bsonPtr, "files_id", 8, &id); mongoc_collection_remove(mongoc_gridfs_get_chunks(gridfsPtr), 0, bsonPtr, NULL, &bsonError); bson_destroy(bsonPtr); } mongoc_cursor_destroy(files); return TCL_OK; } /* cmd gridfile::open NsfMongoGridFileOpen { {-argName "gfs" -required 1 -type mongoc_gridfs_t} {-argName "query" -required 1 -type tclobj} } */ static int NsfMongoGridFileOpen(Tcl_Interp *interp, mongoc_gridfs_t *gridfsPtr, Tcl_Obj *queryObj) { mongoc_gridfs_file_t* gridFilePtr; bson_error_t bsonError; int result, objc; bson_t query, *queryPtr = &query; Tcl_Obj **objv; /*fprintf(stderr, "NsfMongoQuery: namespace %s withLimit %d withSkip %d\n", namespace, withLimit, withSkip);*/ result = Tcl_ListObjGetElements(interp, queryObj, &objc, &objv); if (result != TCL_OK || (objc % 3 != 0)) { return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(queryObj)); } BsonAppendObjv(interp, queryPtr, objc, objv); gridFilePtr = mongoc_gridfs_find_one(gridfsPtr, queryPtr, &bsonError); if (gridFilePtr != NULL) { char buffer[80]; if (Nsf_PointerAdd(interp, buffer, "mongoc_gridfs_file_t", gridFilePtr) == TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewStringObj(buffer, -1)); } else { mongoc_gridfs_file_destroy(gridFilePtr); result = TCL_ERROR; } } else { Tcl_ResetResult(interp); } bson_destroy(queryPtr); return result; } /*********************************************************************** * GridFile interface * * Currently offsets and sizes are limited to 32bit integers, we should * relax this later. ***********************************************************************/ /* cmd gridfile::close NsfMongoGridFileClose { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t -withObj 1} } */ static int NsfMongoGridFileClose(Tcl_Interp *interp, mongoc_gridfs_file_t* gridFilePtr, Tcl_Obj *gridFileObj) { mongoc_gridfs_file_destroy(gridFilePtr); Nsf_PointerDelete(ObjStr(gridFileObj), gridFilePtr, 0); return TCL_OK; } /* cmd gridfile::get_contentlength NsfMongoGridFileGetContentlength { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t} } */ static int NsfMongoGridFileGetContentlength(Tcl_Interp *interp, mongoc_gridfs_file_t* gridFilePtr) { int64_t len; len = mongoc_gridfs_file_get_length(gridFilePtr); Tcl_SetObjResult(interp, Tcl_NewLongObj(len)); return TCL_OK; } /* cmd gridfile::get_contenttype NsfMongoGridFileGetContentType { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t} } */ static int NsfMongoGridFileGetContentType(Tcl_Interp *interp, mongoc_gridfs_file_t* gridFilePtr) { Tcl_SetObjResult(interp, Tcl_NewStringObj(mongoc_gridfs_file_get_content_type(gridFilePtr), -1)); return TCL_OK; } /* cmd gridfile::get_metadata NsfMongoGridFileGetMetaData { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t} } */ static int NsfMongoGridFileGetMetaData(Tcl_Interp *interp, mongoc_gridfs_file_t* gridFilePtr) { const bson_t *metaDataPtr = mongoc_gridfs_file_get_metadata(gridFilePtr); if (metaDataPtr != NULL) { Tcl_SetObjResult(interp, BsonToList(interp, metaDataPtr, 0)); } return TCL_OK; } /* cmd gridfile::read NsfMongoGridFileRead { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t} {-argName "size" -required 1 -type int} } */ static int NsfMongoGridFileRead(Tcl_Interp *interp, mongoc_gridfs_file_t *gridFilePtr, int size) { int readSize; Tcl_Obj *resultObj = Tcl_NewByteArrayObj(NULL, size); struct iovec iov = { NULL, size }; iov.iov_base = Tcl_SetByteArrayLength(resultObj, size); readSize = mongoc_gridfs_file_readv(gridFilePtr, &iov, 1, 0 /* min_bytes */, 0 /* timeout_msec */); /*fprintf(stderr, "NsfMongoGridFileRead want %d got %d\n", size, readSize);*/ Tcl_SetByteArrayLength(resultObj, readSize); Tcl_SetObjResult(interp, resultObj); return TCL_OK; } /* cmd "gridfile::seek" NsfMongoGridFileSeek { {-argName "gridfile" -required 1 -type mongoc_gridfs_file_t} {-argName "offset" -required 1 -type int32} } */ static int NsfMongoGridFileSeek(Tcl_Interp *interp, mongoc_gridfs_file_t *gridFilePtr, int offset) { int result; /* TODO: whence SEEK_SET, SEEK_CUR or SEEK_END; implementation of SEEK_END looks incorrect */ result = mongoc_gridfs_file_seek(gridFilePtr, offset, SEEK_SET); return result < 0 ? TCL_ERROR : TCL_OK; } /*********************************************************************** * Finally, provide the necessary Tcl package interface. ***********************************************************************/ void Nsfmongo_ThreadExit(ClientData clientData) { /* * The exit might happen at a time, when tcl is already shut down. * We can't reliably call NsfLog. */ fprintf(stderr, "+++ Nsfmongo_ThreadExit\n"); #if defined(USE_CLIENT_POOL) NsfMutexLock(&poolMutex); mongoClientPoolRefCount --; if (mongoClientPool != NULL) { fprintf(stderr, "========= Nsfmongo_ThreadExit mongoClientPoolRefCount %d\n", mongoClientPoolRefCount); if (mongoClientPoolRefCount < 1) { mongoc_client_pool_destroy(mongoClientPool); mongoClientPool = NULL; mongoc_uri_destroy(mongoUri); mongoUri = NULL; } } NsfMutexUnlock(&poolMutex); #endif } void Nsfmongo_Exit(ClientData clientData) { /* * The exit might happen at a time, when tcl is already shut down. * We can't reliably call NsfLog. * * Tcl_Interp *interp = (Tcl_Interp *)clientData; * NsfLog(interp,NSF_LOG_NOTICE, "Nsfmongo Exit"); */ fprintf(stderr, "+++ Nsfmongo_Exit\n"); #if defined(TCL_THREADS) Tcl_DeleteThreadExitHandler(Nsfmongo_ThreadExit, clientData); #endif Tcl_Release(clientData); } extern int Nsfmongo_Init(Tcl_Interp * interp) { int i; static NsfMutex initMutex = 0; #ifdef USE_TCL_STUBS if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) { return TCL_ERROR; } # ifdef USE_NSF_STUBS if (Nsf_InitStubs(interp, "2.0", 0) == NULL) { return TCL_ERROR; } # endif #else if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 0) == NULL) { return TCL_ERROR; } #endif Tcl_PkgProvide(interp, "nsf::mongo", PACKAGE_VERSION); #ifdef PACKAGE_REQUIRE_FROM_SLAVE_INTERP_WORKS_NOW if (Tcl_PkgRequire(interp, "nsf", PACKAGE_VERSION, 0) == NULL) { return TCL_ERROR; } #endif Tcl_Preserve(interp); #if defined(TCL_THREADS) Tcl_CreateThreadExitHandler(Nsfmongo_ThreadExit, interp); #endif Tcl_CreateExitHandler(Nsfmongo_Exit, interp); #if defined(USE_CLIENT_POOL) NsfMutexLock(&poolMutex); mongoClientPoolRefCount ++; NsfMutexUnlock(&poolMutex); #endif /* * Register global mongo tcl_objs */ NsfMutexLock(&initMutex); if (NsfMongoGlobalObjs == NULL) { NsfMongoGlobalObjs = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj*)*nr_elements(NsfMongoGlobalStrings)); for (i = 0; i < nr_elements(NsfMongoGlobalStrings); i++) { NsfMongoGlobalObjs[i] = Tcl_NewStringObj(NsfMongoGlobalStrings[i], -1); Tcl_IncrRefCount(NsfMongoGlobalObjs[i]); } } NsfMutexUnlock(&initMutex); Nsf_EnumerationTypeRegister(interp, enumeratorConverterEntries); Nsf_CmdDefinitionRegister(interp, method_definitions); /* * register the pointer converter */ Nsf_PointerTypeRegister(interp, "mongoc_client_t", &mongoClientCount); Nsf_PointerTypeRegister(interp, "mongoc_collection_t", &mongoCollectionCount); Nsf_PointerTypeRegister(interp, "mongoc_cursor_t", &mongoCursorCount); Nsf_PointerTypeRegister(interp, "mongoc_gridfs_file_t", &gridfileCount); Nsf_PointerTypeRegister(interp, "mongoc_gridfs_t", &gridfsCount); for (i=0; i < nr_elements(method_command_namespace_names); i++) { Tcl_CreateNamespace(interp, method_command_namespace_names[i], 0, (Tcl_NamespaceDeleteProc *)NULL); } /* create all method commands (will use the namespaces above) */ for (i=0; i < nr_elements(method_definitions)-1; i++) { Tcl_CreateObjCommand(interp, method_definitions[i].methodName, method_definitions[i].proc, 0, 0); } Tcl_SetIntObj(Tcl_GetObjResult(interp), 1); return TCL_OK; } extern int Nsfmongo_SafeInit(interp) Tcl_Interp *interp; { return Nsfmongo_Init(interp); } /* * Local Variables: * mode: c * c-basic-offset: 2 * fill-column: 78 * indent-tabs-mode: nil * End: */ library/mongodb/nx-mongo.tcl000066400000000000000000000751401242365656200164370ustar00rootroot00000000000000# # Object orientend mapping between MongoDB and nx. # # Gustaf Neumann fecit, April 2011 # package require nx package require nsf::mongo package provide nx::mongo 0.5 # todo: how to handle multiple connections; currently we have a single, global connection # todo: all references are currently auto-fetched. make this optional # todo: If "embeds" or "references" are used, the object must be of # the specified classes, no subclasses allowed # todo: extend the query language syntax, e.g. regexp, ... # todo: handle remove for non-multivalued embedded objects # idea: handle names of nx objects (e.g. property like __name) # idea: handle classes von nx objects (e.g. property like __class) # idea: combine incremental slot operations with e.g. add -> $push, remove -> $pull # todo: make "embedded", "reference" spec even nicer? namespace eval ::nx::mongo { set ::nx::mongo::log 0 ::nx::Object create ::nx::mongo::db { :object property db :object property mongoConn :object property gridFsName :public object method connect {{-db test} args} { if {[info exists :db]} { if {${:db} eq $db} { # reuse existing connection return ${:mongoConn} } ::mongo::close ${:mongoConn} } set :db $db set :mongoConn [::mongo::connect {*}$args] } :public object method close {} { if {[info exists :gridFs]} { ::nsf::log notice "nx::mongo: auto close gridfs" :gridfs close } foreach {ns coll} [array get :collection] { ::nsf::log notice "nx::mongo: auto close collection $ns $coll" ::mongo::collection::close $coll } ::mongo::close ${:mongoConn} unset :db :mongoConn } :public object method destroy {} { if {[info exists :db]} { ::nsf::log notice "nx::mongo: auto close connection to database '${:db}'" ::mongo::close ${:mongoConn} } } :public object method collection {ns} { set key :collection($ns) if {[info exists $key]} {return [set $key]} if {[regexp {^([^.]+)[.](.+)$} $ns _ db coll]} { return [set $key [mongo::collection::open ${:mongoConn} $db $coll]] } return -code error "invalid mongo namespace '$ns'" } :public object method count {ns args} {::mongo::collection::count [:collection $ns] {*}$args} :public object method index {ns args} {::mongo::collection::index [:collection $ns] {*}$args} :public object method insert {ns args} {::mongo::collection::insert [:collection $ns] {*}$args} :public object method delete {ns args} {::mongo::collection::delete [:collection $ns] {*}$args} :public object method query {ns args} {::mongo::collection::query [:collection $ns] {*}$args} :public object method update {ns args} {::mongo::collection::update [:collection $ns] {*}$args} :public object method "drop collection" {name} { ::mongo::run -nocomplain ${:mongoConn} ${:db} [list drop string $name] } :public object method "drop database" {} { ::mongo::run -nocomplain ${:mongoConn} ${:db} [list dropDatabase integer 1] } :public object method "reset error" {} { ::mongo::run -nocomplain ${:mongoConn} ${:db} [list reseterror integer 1] } :public object method is_oid {string} {expr {[string length $string] == 24}} # # GridFS # :object property gridFs :public object method "gridfs open" {{name fs}} { if {[info exists :gridFsName]} { if {${:gridFsName} eq $name} {return ${:gridFs}} :gridfs close } set :gridFsName $name set :gridFs [::mongo::gridfs::open ${:mongoConn} ${:db} $name] } :public object method "gridfs close" {} { ::mongo::gridfs::close ${:gridFs} unset :gridFs :gridFsName } :public object method "gridfs create" {{-source file} value name {mime text/plain} {-metadata}} { ::mongo::gridfile::create -source $source ${:gridFs} $value $name $mime \ {*}[expr {[info exists metadata] ? [list -metadata $metadata] : {}}] } :public object method "gridfs list" {{-all:switch false} query} { set coll [:collection ${:db}.${:gridFsName}.files] if {!$all} { set info [::mongo::collection::query $coll $query -limit 1] return [lindex $info 0] } else { set info [::mongo::collection::query $coll {}] return $info } } :public object method "gridfs update" {id bson} { ::mongo::collection::update [:collection ${:db}.${:gridFsName}.files] \ [list _id oid $id] $bson } :public object method "file content" {query} { set f [mongo::gridfile::open ${:gridFs} $query] set content "" while {1} { append content [set chunk [mongo::gridfile::read $f 4096]] if {[string length $chunk] < 4096} { break } } mongo::gridfile::close $f return $content } :public object method "gridfs set attribute" {query attribute value} { set info [::nx::mongo::db gridfs list $query] if {$info eq ""} {return -code error "no such file <$query> stored in gridfs"} foreach {att type v} $info { dict set d $att $v } if {[dict exists $d $attribute] && [dict get $d $attribute] eq $value} { # right value, nothing to do return } elseif {[dict exists $d $attribute]} { # wrong value replace it set bson {} foreach {att type v} $info { if {$att eq $attribute} { lappend bson $att $type $value } else { lappend bson $att $type $v } } } else { #no such value, add it lappend bson {*}$info $attribute string $value } nx::mongo::db gridfs update [dict get $d _id] $bson } :public object method "gridfs unset attribute" {query attribute} { set info [::nx::mongo::db gridfs list $query] if {$info eq ""} {return -code error "no such file <$query> stored in gridfs"} foreach {att type v} $info { dict set d $att $v } if {[dict exists $d $attribute]} { # delete the attribute nx::mongo::db gridfs update [dict get $d _id] [list \$unset document [list $attribute string ""]] } else { # does not exist, nothing to do } } :public object method "gridfs map" {query url} { # map always the newest entry set fullQuery [list \$query document $query \$orderby document {uploadDate integer -1}] ::nx::mongo::db gridfs set attribute $fullQuery url $url } :public object method "gridfs mapped" {url} { set info [::mongo::collection::query [:collection ${:db}.${:gridFsName}.files] \ [list \$query document [list url string $url]] \ -limit 1] return [lindex $info 0] } } ####################################################################### # nx::mongo::Attribute is a specialized property slot # ::nx::MetaSlot create ::nx::mongo::Attribute -superclass ::nx::VariableSlot { :property mongotype :property rep # # manage logging of mongo concerns # :public method log {msg} { if {$::nx::mongo::log} { nsf::log notice "mongo-attribute: $msg" } } :protected method init {} { # # If the mongotype was not provided, set it to a value derived # from "type". Not all types are mappable easily to mongo types. # if {![info exists :mongotype]} { set :mongotype string if {[info exists :type]} { switch -glob ${:type} { "boolean" - "integer" {set :mongotype ${:type}} "embedded" {set :mongotype embedded_object} "reference" {set :mongotype referenced_object} } #"::*" {set :mongotype object} } } #puts stderr "mongo type of ${:name} is ${:mongotype} [info exists :type]" next } # # The methods "bson encode|decode" perform the low level type # mapping. For now, this handles just the array notation. # :method "bson decode" {bsontype value} { #puts stderr "bson decode of ${:name} /$bsontype/ '$value'" if {$bsontype eq "array"} { if {![:isMultivalued]} { # We got an array, but the slot is not multivalued. Maybe # generating an error is too harsh, but for the mapping back, # we check for multivalued as well. return -code error "Attribute ${:name} should be multivalued, but it is not" } set result [list] foreach {pos type v} $value {lappend result [:bson decode $type $v]} return $result } elseif {$bsontype eq "document"} { #puts stderr "*** we have an document '$value', [:serialize]" if {${:type} eq "embedded" && [info exists :arg]} { #puts stderr "*** we have an embed class = ${:arg}" set value [${:arg} bson create $value] #puts stderr "*** ${:arg} bson create ==> $value" } elseif {${:type} eq "reference" && [info exists :arg]} { #puts stderr "*** we have a reference, class = ${:arg}" # TODO we assume auto_deref set value [:bson deref ${:arg} $value] #puts stderr "*** bson deref ${:arg} ==> $value" } else { return -code error "don't know how to decode document with value '$value'; [:serialize]" } } return $value } :method "bson deref" {class value} { #puts stderr "*** bson deref $class '$value'" foreach {name type v} $value { if {[string match {$*} $name]} {set ([string range $name 1 end]) $v} } if {![info exists (id)]} { return -code error "value to be dereferenced does not contain dbref id: $value" } if {[info exists (db)]} { if {$(db) ne [$class cget -mongo_db]} { return -code error "$(db) is different to [$class cget -mongo_db]" } } if {[info exists (ref)]} { if {$(ref) ne [$class cget -mongo_collection]} { return -code error "$(ref) is different to [$class cget -mongo_collection]" } } return [$class find first -cond [list _id = $(id)]] } :method "bson encodeValue" {value} { if {${:mongotype} eq "embedded_object"} { #puts "embedded_object <$value>" return [list document [$value bson encode]] } elseif {${:mongotype} eq "referenced_object"} { if {![::nsf::var::exists $value _id]} { :log "autosave $value to obtain an object_id" $value save } set _id [$value cget -_id] set cls [$value info class] return [list document [list \ {$ref} string [$cls cget -mongo_collection] \ {$id} oid $_id \ {$db} string [$cls cget -mongo_db]]] } else { return [list ${:mongotype} $value] } } :method "bson encodeArray" {value} { set c -1 set array [list] foreach v $value {lappend array [incr c] {*}[:bson encodeValue $v]} return [list array $array] } :public method "bson encode" {-array:switch value} { if {[:isMultivalued] || $array} { return [:bson encodeArray $value] } else { return [:bson encodeValue $value] } } :public method remove {object value} { if {[:isMultivalued]} { set values [::nsf::var::set $object ${:name}] set p [lsearch $values $value] if {$p < 0} { return -code error "$value not included in $object.$value ($values)" } set newValues [lreplace $values $p $p] ::nsf::var::set $object ${:name} $newValues } else { return -code error "remove just implemented for multivalued slots" } } # # Type converter for handling embedded objects. Makes sure to # track "embedded in" relationship # :public method type=embedded {name value arg} { # Determine the calling object of the type converter, which # might be object itself or a variable slot managing the object. set s [:uplevel self] #puts stderr "XXXX check $name '$value' arg='$arg' s=$s // [:uplevel 1 self]" # The calling objects have the the mongo::Object mixed in. if {![$s info has mixin ::nx::mongo::Object]} { # If this is not the case, we might be in a variable slot, # where we cannot trust the incoming name and we have to # obtain the object from one level higher. if {[$s info has type ::nx::VariableSlot]} { set name [$s cget -name] set s [:uplevel 2 self] if {![$s info has mixin ::nx::mongo::Object]} {set s ""} } else { # no slot object, some strange constellation set s "" } if {$s eq ""} { return -code error "$name '$value' is not embedded in object of type ::nx::mongo::Object" } } if {[::nsf::object::exists $value] && [::nsf::is class $arg] && [$value info has type $arg]} { ::nsf::var::set $value __embedded_in [list $s $name] ::nsf::var::set $s __contains($value) 1 } else { return -code error "value '$value' for property $name is not of type $arg" } } # # Type converter for handling embedded objects. Makes sure to # track "embedded in" relationship # :public method type=reference {name value arg} { set s [:uplevel self] #puts stderr "check $name '$value' arg='$arg' s=$s" if {[::nsf::object::exists $value] && [::nsf::is class $arg] && [$value info has type $arg]} { set ref [list $s $name] if {[::nsf::var::exists $value __referenced_in]} { set refs [::nsf::var::set $value __referenced_in] if {[lsearch $refs $ref] == -1} {lappend refs $ref} } else { set refs [list $ref] } ::nsf::var::set $value __referenced_in $refs } else { return -code error "value '$value' for property $name is not of type $arg" } } # # Type converter for datetime handling (scan date strings into # input values into integers in the form mongo expects it) # :public method type=datetime {name value} { # puts stderr "... [clock format [clock scan $value -format {%B %d, %Y}] -format {%B %d, %Y}]" # MongoDB stores time in ms if {[info exists :scanformat]} {return [expr {[clock scan $value -format ${:scanformat}] * 1000}]} return [expr {[clock scan $value] * 1000}] } } ####################################################################### # The class mongo::Class provides methods for mongo classes (such as # "find", "insert", ...) # ::nx::Class create ::nx::mongo::Class -superclass nx::Class { # # Every mongo class can be configured with a mongo_ns, from which # its instance data is queried. # :property mongo_ns :property mongo_db :property mongo_collection # # Provide helper methods to access from an external specifier # (property name or operator name) internal representations # (eg. mongo type, or mongo operator). # :method "get slot" {att} { set classes [concat [self] [:info mixins] [:info heritage]] foreach cls $classes { set slot [$cls info slots $att] if {$slot ne ""} { return $slot } } } :public method "get relop" {op} { array set "" {< $lt > $gt <= $lte >= $gte != $ne in $in all $all} return $($op) } # # For interaction with bson structures, we provide on the class # level "bson cond" (a small dsl for a more convenient syntax in # bson queries), "bson query" (combining conditions with # ordering), "bson atts (a simplifed property selection) and # "bson parameter" which translates from a bson structure (tuple) # into a dashed parameter list used in object creation. # :method "bson cond" {cond} { #puts "bson cond $cond" set bson [list] foreach {att op value} $cond { set slot [:get slot $att] if {$slot eq ""} {return -code error "could not obtain slot for <$att $op $value>"} switch $op { "=" {lappend bson $att [$slot cget -mongotype] $value} ">" - "<" - "<=" - ">=" - "!=" { lappend bson $att document [list [:get relop $op] [$slot cget -mongotype] $value] } "in" - "all" { lappend bson $att document [list [:get relop $op] {*}[$slot bson encode -array $value]] } default {return -code error "unknown operator $op"} } } #puts "bson cond <$cond> => $bson" return $bson } :method "bson query" {{-cond ""} {-orderby ""}} { #puts "bson query -cond <$cond> -orderby <$orderby>" set bson [:bson cond $cond] set result [list \$query document $bson] if {[llength $orderby] > 0} { set bson [list] foreach attspec $orderby { lassign $attspec att direction lappend bson $att int [expr {$direction eq "desc" ? -1 : 1}] } lappend result \$orderby document $bson } #puts "bson query -cond <$cond> -orderby <$orderby> => $result" return $result } :method "bson atts" {atts} { set result {} foreach {att value} $atts { if {![string is integer -strict $value]} { return -code error "$atts: $value should be integer" } lappend result $att int $value } return $result } :method "bson parameter" {tuple} { # # Translate bson tuple into a parameter values pairs suited as # configure parameters # #puts "bson parameter: <$tuple>" set objParams [list] foreach {att type value} $tuple { set slot [:get slot $att] if {$slot eq ""} {return -code error "could not obtain slot for <$att $type $value>"} lappend objParams -$att [$slot bson decode $type $value] } #puts "bson parameter <$tuple> => $objParams" return $objParams } :method "bson setvalues" {tuple} { # # Translate bson tuple into a cmd to set instance values, which # can be evaluated in the context of an object. # #puts "bson setvalues: <$tuple>" set cmd "" foreach {att type value} $tuple { set slot [:get slot $att] if {$slot eq ""} {return -code error "could not obtain slot for <$att $type $value>"} if {[nx::var exists $slot rep] && [nx::var set $slot rep] ne ""} { set script [:bson rep decode [nx::var set $slot rep] $slot $att $type $value] append cmd $script\n } else { append cmd "set [list :$att] [list [$slot bson decode $type $value]]\n" } } #puts "bson parameter <$tuple> => $objParams" return $cmd } :public method "bson create" {{-name ""} tuple} { ::nsf::object::alloc [self] $name [:bson setvalues $tuple] } :method "bson pp_array" {{-indent 0} list} { set result [list] foreach {name type value} $list { switch $type { document { lappend result "\{ [:bson pp -indent $indent $value] \}" } array { lappend result "\[ [:bson pp_array -indent $indent $value] \]" } default { lappend result [list $value]} } } return [join $result ", "] } :public method "bson pp" {{-indent 0} list} { set result [list] set nextIndent [expr {$indent + 2}] foreach {name type value} $list { set prefix "\n[string repeat { } $indent]$name: " switch $type { document { lappend result "$prefix\{ [:bson pp -indent $nextIndent $value] \}" } array { lappend result "$prefix\[ [:bson pp_array -indent $nextIndent $value] \]" } default { lappend result $prefix[list $value]} } } return [join $result ", "] } # # Overload method property to provide "::nx::mongo::Attribute" as a # default slot class # :public method property { {-accessor ""} {-class ::nx::mongo::Attribute} {-configurable:boolean true} {-incremental:switch} {-rep ""} spec:parameter {initblock ""} } { regsub -all {,type=::} $spec {,arg=::} spec set result [next [list -accessor $accessor -class $class \ -configurable $configurable -incremental=$incremental \ $spec $initblock]] lassign [::nx::MetaSlot parseParameterSpec $spec] name [:info slots $name] configure -rep $rep return $result } :public method variable { {-accessor "none"} {-class ::nx::mongo::Attribute} {-configurable:boolean false} {-incremental:switch} {-initblock ""} {-rep ""} spec:parameter defaultValue:optional } { regsub -all {,type=::} $spec {,arg=::} spec set result [next [list -accessor $accessor -class $class \ -configurable $configurable -incremental=$incremental \ -initblock $initblock $spec \ {*}[expr {[info exists defaultValue] ? [list $defaultValue] : ""}]]] lassign [::nx::MetaSlot parseParameterSpec $spec] name [:info slots $name] configure -rep $rep return $result } :public method pretty_variables {} { set vars {} foreach p [lmap handle [lsort [:info variables]] {::nx::Object info variable parameter $handle}] { if {[regexp {^([^:]+):(.*)$} $p _ name options]} { set resultOptions {} set opts [split $options ,] if {[lindex $opts 0] eq "embedded"} { set resultOpts {} foreach opt $opts { switch -glob $opt { slot=* {continue} arg=* {lappend resultOpts type=[string range $opt 4 end]} default {lappend resultOpts $opt} } } lappend vars $name:[join $resultOpts ,] continue } } lappend vars $p } return $vars } # # index method # :public method index {att {-type 1} args} { if {![info exists :mongo_ns]} {:mongo_setup} # todo: 2nd index will need a different type # todo: multi-property indices db index ${:mongo_ns} [list $att int $type] {*}$args } # # A convenience method for inserting a fresh tuple # :public method insert {args} { set p [:new {*}$args] $p save set _id [$p cget -_id] $p destroy return $_id } # # The method "count" is similar to find, but returns just the # number of tuples for the query. # :public method count {{-cond ""}} { return [::nx::mongo::db count ${:mongo_ns} [:bson cond $cond]] } # # The query interface consists currently of "find first" (returning # a single instance) and "find all" (returning a list of instances). # :public method "find first" { {-instance ""} {-atts ""} {-cond ""} {-orderby ""} } { set tuple [lindex [::nx::mongo::db query ${:mongo_ns} \ [:bson query -cond $cond -orderby $orderby] \ -atts [:bson atts $atts] \ -limit 1] 0] if {$tuple eq ""} { return "" } if {$instance ne ""} {set instance [:uplevel [list ::nsf::object::qualify $instance]]} return [:bson create -name $instance $tuple] } :public method "find all" { {-atts ""} {-cond ""} {-orderby ""} {-limit} {-skip} } { set result [list] set opts [list] if {[info exists limit]} {lappend opts -limit $limit} if {[info exists skip]} {lappend opts -skip $skip} set fetched [::nx::mongo::db query ${:mongo_ns} \ [:bson query -cond $cond -orderby $orderby] \ -atts [:bson atts $atts] \ {*}$opts] foreach tuple $fetched { lappend result [:bson create $tuple] } return $result } :public method show { {-atts ""} {-cond ""} {-orderby ""} {-limit} {-skip} {-puts:boolean 1} } { set opts [list] if {[info exists limit]} {lappend opts -limit $limit} if {[info exists skip]} {lappend opts -skip $skip} set fetched [::nx::mongo::db query ${:mongo_ns} \ [:bson query -cond $cond -orderby $orderby] \ -atts [:bson atts $atts] \ {*}$opts] set tuples [list] foreach tuple $fetched { lappend tuples "\{[:bson pp -indent 4 $tuple]\n\}" } if {$puts} {puts [join $tuples ", "]} return $tuples } :method mongo_setup {} { # # setup mongo_collection, mongo_db and mongo_ns # if {[info exists :mongo_ns]} { #puts stderr "given mongo_ns ${:mongo_ns}" if {![regexp {^([^.]+)[.](.*)$} ${:mongo_ns} :mongo_db :mongo_collection]} { return -code error "${:mongo_ns} does not contain a dot." } } else { if {![info exists :mongo_collection]} { set :mongo_collection [string tolower [namespace tail [self]]]s } if {![info exists :mongo_db]} { set :mongo_db [::nx::mongo::db cget -db] } set :mongo_ns ${:mongo_db}.${:mongo_collection} #puts stderr "mongo_ns is set to ${:mongo_ns}" } } # # When a mongo::Class is created, mixin the mongo::Object to make # "save" etc. available # :method init {} { :mixins add ::nx::mongo::Object :mongo_setup } # :public method "bson rep encode array" {slot obj name} { return [$slot bson encode -array [$obj eval [list array get :$name]]] } :public method "bson rep decode array" {slot name bsontype value} { set result [list] foreach {pos type v} $value {lappend result [$slot bson decode $type $v]} return [list array set :$name $result] } } # # Allow special representations in MongoDB for instance variables. # The methods # # bson rep encode .... # bson rep decode .... # # allow for creating tailored methods to obtain + encode instance # variables and for decode an setting these. The codecs # (coder/decoder) are extensible on the application level by # defining ensemble methods with the name of the codec as last part. ::nx::mongo::Class eval { # # rep codec "array" # :public method "bson rep encode array" {slot obj name} { set body {} set c 0 foreach {k v} [$obj eval [list array get :$name]] { lappend body [incr c] document [list k string $k v string $v] } return [list array $body] } :public method "bson rep decode array" {slot name bsontype value} { set av "" foreach {pos type entry} $value { lappend av [lindex $entry 2] [lindex $entry 5] } return "array set :$name [list $av]" } # # rep codec "dict" # :public method "bson rep encode dict" {slot obj name} { set body {} dict for {k v} [$obj eval [list set :$name]] { lappend body $k string $v } return [list document $body] } :public method "bson rep decode dict" {slot name bsontype value} { set result "" foreach {k type v} $value { lappend result $k $v } return "set :$name \[dict create $result\]" } } ####################################################################### # The class mongo::Object provides methods for mongo objects (such as # "save") # ::nx::Class create ::nx::mongo::Object { # # manage logging of mongo concerns # :public method log {msg} { if {$::nx::mongo::log} { nsf::log notice "mongo: $msg" } } # # _id is the special property maintained by mongoDB # :property -accessor public -class ::nx::mongo::Attribute _id { set :mongotype oid } # # Encode all object data in bson notation # :method "bson encode" {{-ignore ""}} { set bson [list] set cls [:info class] foreach var [:info vars] { if {$var in $ignore} continue set slot [$cls get slot $var] if {$slot ne ""} { if {[nx::var exists $slot rep] && [nx::var set $slot rep] ne ""} { lappend bson $var {*}[$cls bson rep encode [nx::var set $slot rep] $slot [self] $var] } else { lappend bson $var {*}[$slot bson encode [set :$var]] } } } return $bson } # # destroy a mapped object from memory # :public method destroy {} { if {[array exists :__contains]} { # destroy embedded object foreach o [array names :__contains] { :log "[self] contains $o -> destroy" $o destroy } } if {[info exists :__embedded_in]} { lassign ${:__embedded_in} parent att ::nsf::var::unset $parent __contains([self]) } next } # # delete the current object from the db # :public method delete {} { if {[info exists :__embedded_in]} { # # When an embedded object is deleted, it is removed for the # reference list. The containing object is not automatically # saved for the time being. We could consider an automatic # save or mongo-$pull update operation. # #puts "[self] is embedded in ${:__embedded_in}" lassign ${:__embedded_in} parent att set slot [[$parent info class] get slot $att] if {$slot eq ""} {return -code error "could not obtain slot for <[$parent info class] $att>"} $slot remove $parent [self] #puts stderr [:serialize] :log "[self] must save parent $parent in db" :destroy } elseif {[info exists :__referenced_in]} { # # When a referenced is deleted, we do for now essentially the # same as for embedded objects. However, the same object might # be referenced by several objects. # #puts "[self] is referenced in ${:__referenced_in}" foreach reference ${:__referenced_in} { lassign $reference parent att set slot [[$parent info class] get slot $att] if {$slot eq ""} {return -code error "could not obtain slot for <[$parent info class] $att>"} $slot remove $parent [self] :log "[self] must save parent $parent in db" } :destroy } else { #puts "delete a non-embedded entry" if {[info exists :_id]} { set mongo_ns [[:info class] cget -mongo_ns] ::nx::mongo::db delete $mongo_ns [list _id oid ${:_id}] } else { return -code error "[self]: object does not contain an _id; it can't be delete from the mongo db." } } } # # Save the current object. When we have an _id, perform an update, # otherwise perform an insert # :public method save {} { set mongo_ns [[:info class] cget -mongo_ns] if {$mongo_ns eq ""} { # We could perform the delegation probably automatically, but # for now we provide an error return -code error "No mongo_ns specified for [:info class]. In case this is an embedded object, save the embedding one." } else { set bson [:bson encode] if {[info exists :_id]} { :log "we have to update [[:info class] bson pp -indent 4 $bson]" ::nx::mongo::db update $mongo_ns [list _id oid ${:_id}] $bson set :_id } else { :log "we have to insert [[:info class] bson pp -indent 4 $bson]" set r [::nx::mongo::db insert $mongo_ns $bson] set :_id [lindex $r 2] } } } } } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/mongodb/pkgIndex.add000066400000000000000000000001221242365656200164000ustar00rootroot00000000000000package ifneeded nsf::mongo 0.3 [list load [file join $dir libnsfmongo0.3.dylib]] library/mongodb/pkgIndex.add.in000066400000000000000000000001321242365656200170060ustar00rootroot00000000000000package ifneeded nsf::mongo @PACKAGE_VERSION@ [list load [file join $dir @PKG_LIB_FILE@]] library/mongodb/tclconfig/000077500000000000000000000000001242365656200161325ustar00rootroot00000000000000library/mongodb/tclconfig/install-sh000077500000000000000000000330541242365656200201430ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2011-04-20.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -S $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -S) stripcmd="$stripprog $2" shift;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: library/mongodb/tests/000077500000000000000000000000001242365656200153245ustar00rootroot00000000000000library/mongodb/tests/nsf-gridfs.test000066400000000000000000000073301242365656200202720ustar00rootroot00000000000000# -*- tcl -*- # # This test suite tests some basic interactions from the nsf mongo # interface with gridFS. It connects to mongoDB, opens a GridFS named # "myfs" and inserts a file into the file systems. Run the script # with the current directory of nsfmongo, such it can find the README # file. # # After running the script, one can use the following command to # inspect the content in the GridFS via the mongo shell # # $ mongo # > use myfs # > show collections # > db.fs.files.find() # # or via the mongofiles interface: # # $ mongofiles -d myfs list # package require nx::test package require nsf::mongo # # First, as usual, open the connection to the mongo db # ? {set mongoConn [::mongo::connect]} mongoc_client_t:0 # # Open a GridFS in the mongo datbase "myfs" and use the usual prefix # "fs", such GridFS names the collections "fs.chunks" and "fs.files". # ? {set gridFS [::mongo::gridfs::open $mongoConn myfs fs]} mongoc_gridfs_t:0 set dir [file dirname [file dirname [info script]]] set fn README # gridfs::remove_file removes all files with the specified name # multiple store operations create "revisions" with different uploadDates ::mongo::gridfile::delete $gridFS [list filename string $fn] # get the fs.files collection set mongoColl [mongo::collection::open $mongoConn myfs fs.files] # # The current version of gridfs_store_file() is quite unfriendly, # since it assumes that the file exists, and aborts otherwise. So, we # perform the existence test here. # # Store a known file: # ? {::mongo::gridfile::create -source file $gridFS $dir/$fn $fn text/plain} 1 # # Open grid file, get some of its properties, and read it in chunks # of 1000 bytes, and close it finally. ? {set f [mongo::gridfile::open $gridFS {filename string README}]} mongoc_gridfs_file_t:0 ? {mongo::gridfile::get_metadata $f} "" ? {mongo::gridfile::get_contentlength $f} [file size $dir/README] ? {mongo::gridfile::get_contenttype $f} text/plain ? { set chunks 0 while {1} { set chunk [mongo::gridfile::read $f 1000] puts "... read chunk length [string length $chunk]" if {[string length $chunk] > 0} { incr chunks } if {[string length $chunk] < 1000} { break } } set chunks } 4 ? {mongo::gridfile::close $f} "" # # Access the files stored in the gridfs via plain query interface. # (should be just one) puts "\nAll Files:" ? {llength [::mongo::collection::query $mongoColl {}]} 1 # store one more copy ? {::mongo::gridfile::create -source file $gridFS $dir/$fn $fn text/plain} 1 # we should have now two entries: ? {llength [::mongo::collection::query $mongoColl {}]} 2 puts [join [::mongo::collection::query $mongoColl {}] \n] # # Get the file named README from the gridfs via plain query interface # ? {set files [::mongo::collection::query $mongoColl \ [list \$query document {filename string README}] \ -limit 1] llength [lindex $files 0] } 18 # # Extract the oid from the bson attributes # ? { foreach {name type value} [lindex $files 0] { if {$name eq "_id"} {set oid $value; break} } expr {$oid ne ""} } 1 # # Add a dc:creator to the bson attributes # and update the entry in the gridfs # ? {::mongo::collection::update $mongoColl [list _id oid $oid] \ [concat [lindex $files 0] [list metadata document {dc:creator string "Gustaf Neumann"}]] } "" # # Now we can use the gridfs interface to obtain the additional # metadata as well # set f [mongo::gridfile::open $gridFS [list _id oid $oid]] ? {mongo::gridfile::get_metadata $f} "dc:creator string {Gustaf Neumann}" mongo::gridfile::close $f # # close everything # ::mongo::gridfs::close $gridFS ::mongo::collection::close $mongoColl ::mongo::close $mongoConn # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/mongodb/tests/nsf-mongo.test000066400000000000000000000133051242365656200201320ustar00rootroot00000000000000# -*- tcl -*- # # This is a sample test set using the low-level (pure tcl) interface # for inserting and querying tuples into MongoDB. # package require nsf puts stderr "PWD [pwd]" puts stderr "auto_path $auto_path" foreach p [lsort [package names]] { if {![catch {package present $p}]} { set l [format %-15s $p] puts stderr "loaded $l [package ifneeded $p [package require $p]]" } } package require nx::test package require nsf::mongo #nsf::configure debug 2 # # One might query the resulting tuples from the mongo shell via: # # mongo # > use tutorial # > db.persons.find(); # #set mongoConn [::mongo::connect -uri mongodb://127.0.0.1:27017/] ? {set mongoConn [::mongo::connect]} "mongoc_client_t:0" puts "Connection: $mongoConn" if {1} { #::mongo::collection::delete $mongoConn tutorial.persons {} # Drop old potenially old collection and # recreate it as a capped collection ::mongo::run -nocomplain $mongoConn tutorial {drop string persons} puts "\nCreate a capped collection:" ? {::mongo::run $mongoConn tutorial { create string persons capped bool 1 size int32 100000 }} 1 ? {set mongoColl [::mongo::collection::open $mongoConn tutorial persons]} "mongoc_collection_t:0" puts "Collection: $mongoColl" ? {mongo::collection::count $mongoColl {}} 0 puts "\nInserting a few tuples" ? {llength [::mongo::collection::insert $mongoColl \ [list name string Gustaf projects string nsf age int32 53]]} "12" ? {mongo::collection::count $mongoColl {}} 1 ::mongo::collection::insert $mongoColl \ [list name string Stefan projects string nsf] ::mongo::collection::insert $mongoColl \ [list name string Victor a array {0 string "x" 1 string "y"} age int 31] ? { set r [::mongo::collection::insert $mongoColl \ [list name string Joe \ projects string abc \ age int32 23 \ classes array {0 document {$ref string courses $id oid 1}}]] string match "_id oid *" $r } 1 ::mongo::collection::insert $mongoColl \ [list name string Franz info document {x int 203 y int 102} age int 29 projects string gtat] ::mongo::collection::insert $mongoColl \ [list name string Selim ts timestamp {1302945037 1} d date 1302947619279] ? {mongo::collection::count $mongoColl {}} 6 puts stderr "\nCreate an index on name (ascending)" ? {::mongo::collection::index $mongoColl [list name int 1]} 1 } puts stderr "\nFull content" ? {llength [::mongo::collection::query $mongoColl {}]} 6 puts stderr "\nProject members" ? { llength [::mongo::collection::query $mongoColl \ [list \$query document {projects string nsf} \$orderby document {name int 1}]] } 2 puts stderr "\nProject members of nsf sorted by name" ? { set r [lindex [::mongo::collection::query $mongoColl \ [list \$query document {projects string nsf} \$orderby document {name int 1}]] 0] string match *Gustaf* $r } 1 puts stderr "\nAge > 30 (all atts)" ? { set r [::mongo::collection::query $mongoColl \ [list \$query document {age document {$gt int 30}}]] set _ [llength $r]-[llength [lindex $r 0]] } 2-12 puts stderr "\nAge > 30 (only atts name and age, aside of _id)" ? { set r [::mongo::collection::query $mongoColl \ [list \$query document {age document {$gt int 30}}] \ -atts {name int 1 age int 1}] set _ [llength $r]-[llength [lindex $r 0]] } 2-9 puts stderr "\nCount Age > 30" ? {::mongo::collection::count $mongoColl {age document {$gt int 30}}} 2 puts stderr "\nAge > 30 (all atts, via cursor interface)" ? { set cursor [::mongo::cursor::find $mongoColl \ [list \$query document {age document {$gt int 30}}]] puts "Cursor: $cursor" set r0 [::mongo::cursor::next $cursor] set r1 [::mongo::cursor::next $cursor] set r2 [::mongo::cursor::next $cursor] ::mongo::cursor::close $cursor set _ [llength $r0]-[llength $r1]-[llength $r2] } 12-12-0 puts stderr "\nAge > 30 (all atts, via cursor interface, tailable)" ? { set cursor [::mongo::cursor::find $mongoColl \ [list \$query document {age document {$gt int 30}}] -tailable] if {$cursor ne ""} { set r "" while {1} { lappend r [::mongo::cursor::next $cursor] if {[lindex $r end] eq ""} break } ::mongo::cursor::close $cursor join [lmap x $r {llength $x}] - } } 12-12-0 puts stderr "\nEmpty result (via cursor interface)" ? { set cursor [::mongo::cursor::find $mongoColl \ [list \$query document {age document {$gt int 300}}]] if {$cursor ne ""} { set r {} while {1} { lappend r [::mongo::cursor::next $cursor] if {[lindex $r end] eq ""} break } ::mongo::cursor::close $cursor join [lmap x $r {llength $x}] - } } 0 puts stderr "\nArray 'a' contains 'x'" ? {llength [::mongo::collection::query $mongoColl \ [list \$query document {a string "x"}]]} 1 puts stderr "\nEmbedded document has some value (info.y > 100)" ? {llength [::mongo::collection::query $mongoColl \ [list \$query document {info.y document {$gt int 100}}]]} 1 puts stderr "\nProjects in {nsf gtat}" ? { llength [::mongo::collection::query $mongoColl \ [list \$query document {projects document {$in array {0 string nsf 1 string gtat}}}]]} 3 puts stderr "\nStatistics of $mongoColl" set stats [::mongo::collection::stats $mongoColl] ? {expr [llength $stats] % 3 == 0} 1 ? {expr [llength $stats] > 0} 1 if {[llength $stats] % 3 == 0} { package req nx::mongo nx::mongo::Class create C puts [C bson pp $stats] } puts stderr "\nStatus" set status [::mongo::status $mongoConn] puts [C bson pp $status] ? {expr [llength $status] % 3 == 0} 1 ? {expr [llength $status] > 0} 1 puts stderr "\nClose connection $mongoConn" ::mongo::close $mongoConn # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/mongodb/tests/nx-bi.test000066400000000000000000000134031242365656200172430ustar00rootroot00000000000000# -*- tcl -*- # # An example for the usage of the nx mongo object mapping based on the # the real world # # "Business Insider" Data Model # # (see e.g. # http://www.slideshare.net/mongodb/nosql-the-shift-to-a-nonrelational-world). # # { title: 'Too Big to Fail', # author: 'John S.', # ts: Date('05-Nov-09 10:33'), # [ comments: [ {author: 'Ian White', # comment: 'Great Article!' }, # {author: 'Joe Smith', # comment: 'But how fast is it?', # replies: [ {author: Jane Smith', # comment: 'scalable?' }] # } # ], # tags: ['finance', 'economy'] # } # # # # Gustaf Neumann fecit, May 2011 # package require nx::mongo package require nx::serializer package require nx::test #nsf::configure debug 2 # Establish connection to the database ::nx::mongo::db connect -db "tutorial" # Make sure, we start always from scratch, so remove everything from # the collection. nx::mongo::db drop collection postings ###################################################################### # Create the application classes based on the "Business Insider" data # model. Note that instances of the class "Comment" can be embedded in # a posting (property "comments") as well as in an comment itself # (property "replies"). All comments are in this example multivalued # and incremental (i.e. one can use slot methods "... add ..." and # "... delete ..." to add values to the attributes). # nx::mongo::Class create Comment { :property author:required :property comment:required :property -incremental replies:embedded,type=::Comment,0..n } nx::mongo::Class create Posting { :index tags :property title:required :property author:required :property ts:required :property -incremental comments:embedded,type=::Comment,0..n :property -incremental tags:0..n } #puts stderr "OP [join [Posting info configure parameters] \n\t]" ###################################################################### # Build a composite Posting instance based on the example above. # set p [Posting new -title "Too Big to Fail" -author "John S." \ -ts "05-Nov-09 10:33" -tags {finance economy} \ -comments [list \ [Comment new -author "Ian White" -comment "Great Article!"] \ [Comment new -author "Joe Smith" -comment "But how fast is it?" \ -replies [list [Comment new -author "Jane Smith" -comment "scalable?"]]] \ ]] # # When we save the item, the embedded objects (the comments and # replies) are saved together with the posting in a compound document. # $p save # After saving the item, the main object contains an _id, such that a # subsequent save operations do not create an additional entries in # the database. For our little experiment here, we like to save # multiple copies to see the results of our changes. Therefore we # remove the _id manually: $p eval {unset :_id} # We have two comments for the posting $p ? [list llength [$p cget -comments]] 2 # Now we want to remove e.g. the second comment (with the embedded # replies). First get this comment object $c ... set c [lindex [$p cget -comments] 1] # ... and delete it $c delete # The delete operation destroy the embedded object and removes the # reference to it in the comments property. ? [list llength [$p cget -comments]] 1 # The delete operation does not automatically persist the change, # since there might be multiple changes in a complex # document. Therefore we have to perform an save operation of the # containing document. $p save # Now, we have two postings in the database, the first with the two # comments, the second one with just a single comment. ? {Posting count} 2 # Again, we want to continue with our test and remove the fresh _id as # well. $p eval {unset :_id} # We add an additional comment at the end of the list of the comments # with the incremental operations (the slot is incremental) ... $p comments add [Comment new -author "Gustaf N" -comment "This sounds pretty cool"] end # ... and we add another tag ... $p tags add nx # ... and save everything $p save # We have now three entries in the database collection. ? {Posting count} 3 # Now fetch the first entry with the tag "nx" set q [Posting find first -cond {tags = nx}] # The fetched entry should have the two comments: ? [list llength [$q cget -comments]] 2 # We add jet another tag and save it $q tags add nsf $q save # We still have three entries in the database ? {Posting count} 3 Posting show nx::mongo::db close ###################################################################### # Output ###################################################################### # { # _id: 4daaeb04727b2b1000000000, # title: {Too Big to Fail}, # comments: [ { # author: {Ian White}, # comment: {Great Article!} }, { # replies: [ { # author: {Jane Smith}, # comment: scalable? } ], # author: {Joe Smith}, # comment: {But how fast is it?} } ], # author: {John S.}, # ts: {05-Nov-09 10:33}, # tags: [ finance, economy ] # }, { # _id: 4daaeb04727b2b1000000001, # title: {Too Big to Fail}, # comments: [ { # author: {Ian White}, # comment: {Great Article!} } ], # author: {John S.}, # ts: {05-Nov-09 10:33}, # tags: [ finance, economy ] # }, { # _id: 4daaeb04727b2b1000000002, # title: {Too Big to Fail}, # comments: [ { # author: {Ian White}, # comment: {Great Article!} }, { # author: {Gustaf N}, # comment: {This sounds pretty cool} } ], # author: {John S.}, # ts: {05-Nov-09 10:33}, # tags: [ nsf, nx, finance, economy ] # } ###################################################################### # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/mongodb/tests/nx-mongo.test000066400000000000000000000101011242365656200177600ustar00rootroot00000000000000# -*- tcl -*- # # This is an example how to use the nx mongo mapping. We show here # single class mapped to the mongo db with sing and multivalued # scalars together with some querying options. # # Gustaf Neumann fecit, April 2011 # package require nx::mongo package require nx::test #nsf::configure debug 2 # Establish connection to the database ? {::nx::mongo::db connect -db "tutorial"} mongoc_client_t:0 # # Create or lookup collection handle; the first operation is a create, # the second a lookup. # ? {::nx::mongo::db collection tutorial.persons} "mongoc_collection_t:0" ? {::nx::mongo::db collection tutorial.persons} "mongoc_collection_t:0" # # When we create a capped colletion, we cannot use # # nx::mongo::db delete tutorial.persons {} # # but have to use "drop collection" to get rid of it (this is enforced # by MongoDB 2.6.3 or newer). nx::mongo::db drop collection persons # # Create the application class "Person" # ? { nx::mongo::Class create Person { :index name :property name:required :property projects:0..n {set :incremental 1} :property age:integer } } ::Person # # Insert a tuple to the database via creating an object, saving and # destroying it: # ? { nsf::is object [set p [Person new -name Gustaf -projects {nsf nx nxmongo} -age 53]]} 1 ? { nx::mongo::db is_oid [$p save]} 1 ? { $p destroy; nsf::is object $p} 0 # # The insert operation of above can be achieved with less typing via # the conveniance method "insert": # ? { nx::mongo::db is_oid [Person insert -name Stefan -projects {nsf nx}]} 1 ? { nx::mongo::db is_oid [Person insert -name Joe -projects abc -age 23]} 1 ? { nx::mongo::db is_oid [Person insert -name Franz -projects {gtat annobackend abc} -age 29]} 1 # # Quick check of the results: count all persons and count the persons # named Gustaf # ? {Person count} 4 ? {Person count -cond {name = Gustaf}} 1 # # Lookup a single Person, create an instance of the object ... # ? {nsf::is object [set p [Person find first -cond {name = Gustaf}]]} 1 #puts [$p serialize] # # ... change the age, add an project, and save it. # ? {$p configure -age 55} "" ? {$p projects add xowiki} "xowiki nsf nx nxmongo" ? {nx::mongo::db is_oid [$p save]} 1 ? {$p destroy; nsf::is object $p} 0 # # Lookup a single Person and create a named object # ? {Person find first -instance p2 -cond {name = Gustaf}} ::p2 ? {lsort [p2 info vars]} "_id age name projects" ? {p2 destroy; nsf::is object p2} 0 # # Test a few queries based on the user-friendly query language defined # for the class objects. # puts "\nProject members of nsf:" ? {llength [set persons [Person find all -cond {projects = nsf}]]} 2 ? {lsort [lmap p $persons {$p cget -name}]} "Gustaf Stefan" puts "\nProject members of nsf or gtat:" ? {llength [set persons [Person find all -cond {projects in {nsf gtat}}]]} 3 ? {lsort [lmap p $persons {$p cget -name}]} "Franz Gustaf Stefan" puts "\nProject members working on both nsf and nxmongo:" ? {llength [set persons [Person find all -cond {projects all {nsf nxmongo}}]]} 1 ? {lsort [lmap p $persons {$p cget -name}]} "Gustaf" puts "\nAll Persons sorted by name (ascending):" ? {llength [set persons [Person find all -orderby name]]} 4 ? {lmap p $persons {$p cget -name}} "Franz Gustaf Joe Stefan" puts "\nMembers of Projects != 'abc' nsf sorted by name desc and age:" ? {llength [set persons [Person find all -cond {projects != "abc"} -orderby {{name desc} age}]]} 2 ? {lmap p $persons {$p cget -name}} "Stefan Gustaf" puts "\nFind persons age > 30:" ? {llength [set persons [Person find all -cond {age > 30}]]} 1 ? {lsort [lmap p $persons {$p cget -name}]} "Gustaf" # # Define a special find method for "Person" named "oldies" by # extending the query interface (add sub-method to ensemble). # Person public object method "find oldies" {} { return [:find all -cond {age > 30}] } # # Use the special find method # puts "\nFind oldies:" ? {llength [set persons [Person find oldies]]} 1 ? {lsort [lmap p $persons {$p cget -name}]} "Gustaf" # check autoclosing nx::mongo::db close # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/mongodb/tests/nx-reference-many.test000066400000000000000000000124221242365656200215510ustar00rootroot00000000000000# -*- tcl -*- # # This is an introductory example how to use the nx mongo mapping for # referencing some objects. We use here an example of a Group having # a (possible compound) users as members. # # Gustaf Neumann fecit, May 2011 # package require nx::mongo package require nx::test # Establish connection to the database ? {::nx::mongo::db connect -db "tutorial"} mongoc_client_t:0 # Make sure, we start always from scratch nx::mongo::db drop collection groups nx::mongo::db drop collection members ###################################################################### # The first approach to implement references simply as multivalued # attributes. This is just feasible in cases, where the user has just # a name and not more attributes. # ? {nx::mongo::Class create Group { :property name :property members:0..n }} ::Group # Insert entry with the schema of ::Group into the database: ? { nx::mongo::db is_oid [Group insert -name "grp1" -members {gustaf stefan}]} 1 # Retrieve the entry from the database: ? {nsf::is object [set g [Group find first -cond {name = "grp1"}]]} 1 ? {$g cget -members} "gustaf stefan" ###################################################################### # The second approach to implement references to other objects via an # property pointing to the object ids of other objects. This is # similar to the classical datbase approach. When the object is # fetched, the application developer has to care about # fetching/dereferencing the referenced objects. # ? {nx::mongo::Class create Member { :property name }} ::Member ? {nx::mongo::Class create Group { :property name :property members:0..n }} ::Group ? {nx::mongo::db is_oid [set id1 [Member insert -name gustaf]]} 1 ? {nx::mongo::db is_oid [set id2 [Member insert -name stefan]]} 1 ? {nx::mongo::db is_oid [Group insert -name "grp2" -members [list $id1 $id2]]} 1 # Retrieve the entry from the database: ? {nsf::is object [set g [Group find first -cond {name = "grp2"}]]} 1 # Obtain the objects with the oids contained in the group ? {llength [set members [Member find all -cond [list _id in [$g cget -members]]]]} 2 ? {lsort [lmap m $members {$m cget -name}]} "gustaf stefan" ###################################################################### # The third approach is to embed the objects in the referencing # document. This might be feasible when the values of the embedded # objects seldomly change, When the containing object (the posting) is # loaded, the appropriate object structure is created automatically. # ? {nx::mongo::Class create Member { :property name }} ::Member ? {nx::mongo::Class create Group { :property name :property members:embedded,type=::Member,0..n }} ::Group ? {nx::mongo::db is_oid [Group insert -name "grp3" \ -members [list \ [Member new -name gustaf] \ [Member new -name stefan]]]} 1 # Retrieve the entry from the database: ? {nsf::is object [set g [Group find first -cond {name = "grp3"}]]} 1 ? {lsort [lmap m [$g cget -members] {$m cget -name}]} "gustaf stefan" ###################################################################### # The fourth approach is to use mongo db-refs for referencing. This # is similar to approach two, but provides support for dereferencing # and maintaining the reference lists. # ? {nx::mongo::Class create Member { :property name }} ::Member ? {nx::mongo::Class create Group { :property name :property members:reference,type=::Member,0..n }} ::Group # # Currently, the mongo c-driver does not allow to add DBRefs, since # it refuses to accept field names with leading '$'. So we skip this # version for the time being. # ? {nx::mongo::db is_oid [Group insert -name "grp4" \ -members [list \ [Member new -name gustaf1] \ [Member new -name stefan2]]]} 1 # Retrieve the entry from the database: ? {nsf::is object [set g [Group find first -cond {name = "grp4"}]]} 1 ? {lsort [lmap m [$g cget -members] {$m cget -name}]} "gustaf1 stefan2" puts stderr "\nContent of collection groups:" Group show puts stderr "\nContent of collection members:" Member show ###################################################################### # Output ###################################################################### # Content of collection groups: # { # _id: 51fa2ea113760b0000000000, # name: grp1, # members: [ gustaf, stefan ] # }, { # _id: 51fa2ea113760b0000000003, # name: grp2, # members: [ 51fa2ea113760b0000000001, 51fa2ea113760b0000000002 ] # }, { # _id: 51fa2ea113760b0000000004, # name: grp3, # members: [ { # name: gustaf }, { # name: stefan } ] # }, { # _id: 51fa2ea113760b0000000007, # name: grp4, # members: [ { # $ref: members, # $id: 51fa2ea113760b0000000005, # $db: tutorial }, { # $ref: members, # $id: 51fa2ea113760b0000000006, # $db: tutorial } ] # } # # Content of collection members: # { # _id: 51fa2ea113760b0000000001, # name: gustaf # }, { # _id: 51fa2ea113760b0000000002, # name: stefan # }, { # _id: 51fa2ea113760b0000000005, # name: gustaf1 # }, { # _id: 51fa2ea113760b0000000006, # name: stefan2 # } ###################################################################### # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/mongodb/tests/nx-reference-one.test000066400000000000000000000115041242365656200213660ustar00rootroot00000000000000# -*- tcl -*- # # This is an introductory example how to use the nx mongo mapping for # referencing some object. We use here an example of an Posting having # a single (possible compound) user as originator. All example work # the same way as well with with multivalued attributes. # # Gustaf Neumann fecit, May 2011 # package require nx::mongo package require nx::test # Establish connection to the database ? {::nx::mongo::db connect -db "tutorial"} mongoc_client_t:0 # Make sure, we start always from scratch nx::mongo::db drop collection users nx::mongo::db drop collection posts ###################################################################### # The first approach to implement references simply as an property. # This is just feasible in cases, where the user has just a name and # not more attributes. # ? {nx::mongo::Class create Post { :property title :property user }} ::Post # Insert entry with the schema of ::Post into the database: ? {nx::mongo::db is_oid [Post insert -title "Hello trivial World" -user smith] } 1 # Retrieve the entry from the database: ? {nsf::is object [set p [Post find first -cond {title = "Hello trivial World"}]]} 1 ? {$p cget -user} smith ? {$p destroy; nsf::is object $p} 0 ###################################################################### # The second approach to implement references to other objects via an # property pointing to the object id of an other object. This is the # classical datbase approach. When the object is fetched, the # application developer has to care about fetching/dereferencing the # referenced object. # ? {nx::mongo::Class create User { :property name }} ::User ? {nx::mongo::Class create Post { :property title :property user_id }} ::Post # The method "insert" returns the object id of the newly created # object. We can use this value as a reference in the Post. ? {nx::mongo::db is_oid [set oid [User insert -name Smith]]} 1 ? {nx::mongo::db is_oid [Post insert -title "Hello simple World" -user_id $oid]} 1 # Retrieve the entry from the database: ? {nsf::is object [set p [Post find first -cond {title = "Hello simple World"}]]} 1 ? {nsf::is object [set u [User find first -cond [list _id = [$p cget -user_id]]]]} 1 ? {$u cget -name} "Smith" ###################################################################### # The third approach is to embed the object in the referencing # document. This might be feasible when the values of the embedded # objects seldomly change, When the containing object (the Post # instance) is loaded, the appropriate object structure is created # automatically. # ? {nx::mongo::Class create User { :property name }} ::User ? {nx::mongo::Class create Post { :property title :property user:embedded,type=::User }} ::Post ? {nx::mongo::db is_oid [Post insert -title "Hello embedded World" -user [User new -name Smith]]} 1 # Retrieve the entry from the database: ? {nsf::is object [set p [Post find first -cond {title = "Hello embedded World"}]]} 1 ? {[$p cget -user] cget -name} "Smith" ###################################################################### # The fourth approach is to use mongo db-refs for referencing. This # is similar to approach two, but provides support for dereferencing # and maintaining the reference lists. # ? {nx::mongo::Class create User { :property name }} ::User ? {nx::mongo::Class create Post { :property title :property user:reference,type=::User }} ::Post ? {nx::mongo::db is_oid [Post insert -title "Hello referenced World" -user [User new -name SmithR]]} 1 # Retrieve the entry from the database: ? {nsf::is object [set p [Post find first -cond {title = "Hello referenced World"}]]} 1 ? {[$p cget -user] cget -name} SmithR puts stderr "\nContent of the collection posts:" Post show puts stderr "\nContent of the collection users:" User show ###################################################################### # Output ###################################################################### # Content of the collection posts: # Content of the collection posts: # { # _id: 51fa2f29cb562e0000000000, # title: {Hello trivial World}, # user: smith # }, { # _id: 51fa2f29cb562e0000000002, # title: {Hello simple World}, # user_id: 51fa2f29cb562e0000000001 # }, { # _id: 51fa2f29cb562e0000000003, # title: {Hello embedded World}, # user: { # name: Smith } # }, { # _id: 51fa2f29cb562e0000000005, # title: {Hello referenced World}, # user: { # $ref: users, # $id: 51fa2f29cb562e0000000004, # $db: tutorial } # } # # Content of the collection users: # { # _id: 51fa2f29cb562e0000000001, # name: Smith # }, { # _id: 51fa2f29cb562e0000000004, # name: SmithR # } # ###################################################################### # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/mongodb/tests/nx-rep.test000066400000000000000000000104631242365656200174420ustar00rootroot00000000000000# -*- tcl -*- # # An example for the usage of the nx mongo object mapping with # input/export functions for variables not following the standard # nx-mapping of properties. # # The MongoDB nx package supports special codec (encoding/decoding) # functions which perform the mapping from instance variables to bson # and vice versa. These special mappings can be defined via the # attribute "rep" for :variable and :property. The predefined # representations for "array" and "dict" are included here. The rep # mechanism is extensible, users can define on the application layer # their own representations. # package require nx::mongo package require nx::serializer package require nx::test #nsf::configure debug 2 # Establish connection to the database ::nx::mongo::db connect -db "tutorial" # Make sure, we start always from scratch, so remove everything from # the collection. nx::mongo::db drop collection foos ###################################################################### # nx-mongo has a built-in special representation for Tcl arrays which # can be used via e.g. # # :variable -rep array a # # The "array" representation differs from a string representation by # requiring a different method in tcl to read its values, and by # saving the tcl array in mongoDB in a different notation. The chosen # representation of the array in mongoDB is an array of key/value # pairs. # # Similarly we provide here a "dict" mapping, which transforms a dict # to a a form similar to a nested object. Just the first level of the # dict is transformed. ###################################################################### # # Extend ::nx::mongo::Class to handle rep codecs "array" and "dict" # ::nx::mongo::Class eval { # # rep codec "array" # :public method "bson rep encode array" {slot obj name} { set body {} set c 0 foreach {k v} [$obj eval [list array get :$name]] { lappend body [incr c] document [list k string $k v string $v] } return [list array $body] } :public method "bson rep decode array" {slot name bsontype value} { set av "" foreach {pos type entry} $value { lappend av [lindex $entry 2] [lindex $entry 5] } return "array set :$name [list $av]" } # # rep codec "dict" # :public method "bson rep encode dict" {slot obj name} { set body {} dict for {k v} [$obj eval [list set :$name]] { lappend body $k string $v } return [list document $body] } :public method "bson rep decode dict" {slot name bsontype value} { set result "" foreach {k type v} $value { lappend result $k $v } return "set :$name \[dict create $result\]" } } ###################################################################### # # Define an application class Foo, using the rep type "array" for # instance variable "a" and the rep type "dict" for instance variable # "d". # nx::mongo::Class create Foo { :index tags :property title :property -incremental tags:0..n :variable -rep array a :variable -rep dict d :public method bar {} {return [lsort [array names :a]]} :public method baz {key} {dict get ${:d} $key} } ###################################################################### # Build a composite Posting instance based on the example above. # set p [Foo new -title "Hello World" { :tags add a set :a(1) a set :a(2) b set :d [dict create first_name Walter second_name White] }] ? {$p bar} "1 2" ? {$p baz first_name} "Walter" # # When we save the item, the instances variables are transformed into # a mongoDB representation, using the rep types defined above. # ? {nx::mongo::db is_oid [$p save]} 1 $p destroy ? {nsf::is object $p} 0 # Now fetch the first entry set q [Foo find first] ? {$q bar} "1 2" ? {$q baz first_name} "Walter" Foo show puts stderr "====EXIT [info script]" ###################################################################### # Output ###################################################################### # # { # _id: 5301d307249bc51c00000000, # d: { # first_name: Walter, # second_name: White }, # a: [ { # k: 1, # v: a }, { # k: 2, # v: b } ], # title: {Hello World}, # tags: [ a ] # } ###################################################################### # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/mongodb/tests/nx-serialize.test000066400000000000000000000103111242365656200206330ustar00rootroot00000000000000# -*- tcl -*- # # An example for using MongoDB as a persistence store. # # Gustaf Neumann fecit, Feb 2014 # package require nx::mongo package require nx::serializer package require nx::test #nsf::configure debug 2 ###################################################################### # Define sample methods to use MongoDB as a simple persistence store # for nx. The provided methods are # # nx::Class "mongo persist" ?-db db? ?-collection name? ?-closure? # nx::Class "mongo fetch" ?-db db? ?-collection name? # # where "mongo persist" stores/updates the direct or indirect # instances in the specified collection and "mongo fetch" loads # all instances from this collection # nx::Class public method "mongo persist" { {-db "tutorial"} {-collection "nx"} {-closure:switch} } { set mongo_ns $db.$collection set count(update) 0 set count(insert) 0 foreach i [:info instances -closure=$closure] { set isNew [string match "::nsf::__#*" $i] set bson [list name string $i class string [$i info class] isNew string $isNew] lappend bson definition string [$i serialize] if {[::nx::var exists $i _id]} { #puts "we have to update " ::nx::mongo::db update $mongo_ns [list _id oid [::nx::var set $i _id]] $bson incr count(update) } else { #puts "we have to insert to $mongo_ns" set r [::nx::mongo::db insert $mongo_ns $bson] ::nx::var set $i _id [lindex $r 2] incr count(insert) } } puts "$count(insert) instances inserted, $count(update) instances updated in $mongo_ns" } nx::Class public method "mongo fetch" { {-db "tutorial"} {-collection "nx"} } { set mongo_ns $db.$collection set result {} set bson [::nx::mongo::db query $mongo_ns {}] foreach obj $bson { foreach {att type value} $obj { switch $att { _id {set _id $value} class {set class $value} definition {set definition $value} name {set name $value} } } eval $definition $name eval [list set _id $_id] ::nx::var set $name _id $_id lappend result $name } return $result } # ###################################################################### # # Sample usage of the two methods # # Establish connection to the database ::nx::mongo::db connect -db "tutorial" # Make sure, we start always from scratch, so remove everything from # the collection. nx::mongo::db drop collection nx ###################################################################### # # Define an arbitrary class # nx::Class create Foo { :property title :property {count 1} :public method ++ {} {incr :count} :public method hasArray {} {array exists :a} :public object method counts {} { foreach i [:info instances] {incr c [$i cget -count]} return $c } :public object method countArrays {} { foreach i [:info instances] {incr c [$i hasArray]} return $c } } ###################################################################### # # Create an instance of Foo containing e.g. arrays or dicts as # instance variables # Foo new -title t1 { set :a(1) a set :a(2) b set :d [dict create x 100 y 101] set :count 100 } Foo new -title t2 ? {llength [Foo info instances]} 2 "Foo instances before persist" ? {Foo counts} 101 ? {Foo countArrays} 1 foreach i [Foo info instances] {$i ++} ? {Foo counts} 103 # # Save all instances of Foo (inserts) # Foo mongo persist # # Destroy all instances of Foo in memory # foreach i [Foo info instances] {$i destroy} ? {llength [Foo info instances]} 0 "Foo instances after destroy" # # Load instances from MongoDB # ::nx::Class mongo fetch ? {llength [Foo info instances]} 2 "Foo instances after fetch" ? {Foo counts} 103 ? {Foo countArrays} 1 foreach i [Foo info instances] {$i ++} ? {Foo counts} 105 # # create one more instance, also with an array # Foo new {set :a(x) foo} ? {Foo counts} 106 ? {Foo countArrays} 2 # # Save all instances of Foo (updates) # Foo mongo persist foreach i [Foo info instances] {$i destroy} ? {llength [Foo info instances]} 0 "Foo instances after destroy" ::nx::Class mongo fetch ? {Foo counts} 106 ? {Foo countArrays} 2 ###################################################################### # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/nx/000077500000000000000000000000001242365656200131625ustar00rootroot00000000000000library/nx/COPYRIGHT000066400000000000000000000050371242365656200144620ustar00rootroot00000000000000/* * Next Scripting Framework * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * This software is based upon MIT Object Tcl by David Wetherall and * Christopher J. Lindblad, that contains the following copyright * message: * * "Copyright 1993 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty." */ library/nx/class-method.tcl000066400000000000000000000075251242365656200162620ustar00rootroot00000000000000package provide nx::class-method 1.0 # # Provide a convenience layer to class methods/variables by using # "class method" rather than "object method". This reflects the naming # conventions of NX 2.0b4 and earlier. By using this package, one can # use instead of # # nx::Class create C { # :public object method foo args {....} # :object property p:integer # :object mixins add M # #... # puts [:info object methods] # } # # a terminology closer to text book vocabulary # # package require nx::class-method # # nx::Object create o { # :public class method foo args {....} # :class property p:integer # :class mixins add M # #... # puts [:class info methods] # } # # Note that for object specific methods of object, have still to be # defined via "object method" etc. (see also package # nx::plain-object-method). # # # make "class" an accepted method defining method # namespace eval ::nsf { array set ::nsf::methodDefiningMethod { class 1 } } namespace eval ::nx { # # Define a method to allow configuration for tracing of the # convenience methods. Use # # nx::configure class-method-warning on|off # # for activation/deactivation of tracing. This might be # useful for porting legacy NX programs or for testing # default-configuration compliance. # nx::configure public object method class-method-warning {onoff:boolean,optional} { if {[info exists onoff]} { set :class-method-warning $onoff } else { if {[info exists :class-method-warning]} { if {${:class-method-warning}} { uplevel {::nsf::log warn "class method: [self] [current method] [current args]"} } } } } nx::Class eval { # # Definitions redirected to "object" # foreach m { alias filters forward method mixins property variable } { :public method "class $m" {args} { nx::configure class-method-warning :object [current method] {*}[current args] } } # # info subcommands # foreach m { method methods slots variables filters mixins } { :public method "class info $m" {args} [subst -nocommands { nx::configure class-method-warning :info object $m {*}[current args] }] } } # # Deletions # foreach m { property variable method } { nx::Class public method "class delete $m" {args} { nx::configure class-method-warning :delete object [current method] {*}[current args] } } ###################################################################### # Provide method "require" ###################################################################### Object eval { # # method require, base cases # :method "require class method" {methodName} { nx::configure class-method-warning ::nsf::method::require [::nsf::self] $methodName 1 return [:info lookup method $methodName] } # # method require, public explicitly # :method "require public class method" {methodName} { nx::configure class-method-warning set result [:require class method $methodName] ::nsf::method::property [self] $result call-protected false return $result } # # method require, protected explicitly # :method "require protected class method" {methodName} { nx::configure class-method-warning set result [:require class method $methodName] ::nsf::method::property [self] $result call-protected true return $result } # # method require, private explicitly # :method "require private class method" {methodName} { nx::configure class-method-warning set result [:require class method $methodName] ::nsf::method::property [self] $result call-private true return $result } } } library/nx/nx.nxd000066400000000000000000002173051242365656200143320ustar00rootroot00000000000000# -*- Tcl -*- # @package nx # # The <<@glossary nx>> is an object oriented scripting language based # on the Next Scripting Framework (TODO: passende Referenz <<@package # nsf>>). # The Next Scripting Language (NX) is a successor of XOTcl 1 and is # based on 10 years of experience with XOTcl in projects containing # several hundert thousand lines of code. While XOTcl was the first # language designed to provide language support for design patterns, the # focus of the Next Scripting Framework and NX are on combining this # with Language Oriented Programming. In many respects, NX was designed # to ease the learning of the language by novices (by using a more # mainstream terminology, higher orthogonality of the methods, less # predefined methods), to improve maintainability (remove sources of # common errors) and to encourage developer to write better structured # programs (to provide interfaces) especially for large projects, where # many developers are involved. # # NX is implemented as an object-oriented language extension for # <<@Gls tcl>>. The object system model is highly influenced by # <<@glossary clos>>, where classes are certain kind of objects and # objects might have their own properties (variables and methods) not # induced by the classes. The object system of NX is fully dynamic, # all object-class (e.g. instance-of relationship) and class-class # relationships (e.g. superclass relationship) can change at any time # at runtime. This way, object can change classes at runtime, or # objects might obtain different properties, etc. # # The language and object system of NX is implemeted by the package # nx. It defines the basic language entities <<@class ::nx::Object>> # and <<@class ::nx::Class>>, as well as essential language primitives # (in particular, <<@command ::nx::next>> and <<@command # ::nx::current>>). # # @require nsf # @version 1.0.0a # @namespace ::nx ######################################################################## # ::nx::Object ######################################################################## # @class Object # # ::nx::Object is the <<@gls baseclass>> of the <<@glossary nx>> # object system. All objects defined in NX are (direct or indirect) # instances of this base class. The methods provided by the Object # base class are available to all objects and to all classes defined # in NX. ######################################################################## # Attributes of ::nx::Object ######################################################################## # @class.property {Object class} # # Specifies the class of the object under construction as an object # parameter. This is equivalent to creating the object from this # class directly. # @class.property {Object filter} # # Register the specified methods as per-object <<@glspl # filter>>. Every name in the <<@gls filterspec>> must resolve to an # existing method in the scope of the object. # @class.property {Object mixin} # # Register the specified classes as per-object <<@glspl mixin_class>>. # Every name in the <<@gls mixinspec>> must correspond an existing # class. # # @spec object,type=::nx::Class # @class.property {Object noinit} # # Omit object initialization. When this flag is provided, the # constructor method '''init''' will not be called during object # construction. Consider, for example, reifying a previously # serialized object with its persisted state. Running the constructor # methods would risk altering the serialized state, e.g., by resetting # the object variables. # @class.property {Object properties} # # A shortcut notation for specifying a bulk of object properties, # including their parameter type specifications and default # values. For example: # ''' # nx::Object create o -properties { # {a:integer 0} # {b:boolean false} # c:object,type=::C # } # ''' # @class.property {Object volatile} # # Render the object created volatile. A volatile object is destroyed # automatically upon leaving the variable scope for which the object # construction was triggered. Examples of such variable scopes are # methods, Tcl procs, and <<@glspl initcmd>>. ######################################################################## # Methods of ::nx::Object ######################################################################## # @class.method {Object alias} # # Define an <<@gls alias>> as per-object method. This method is used # for defining a method with the specified name by binding a # pre-existing implementation. This alias target is provided as the # last argument. # # @parameter methodName Name of the new method # @parameter -returns Parameter specification to check the result of # the alias method (e.g. '''integer'''). # @parameter -frame Optional scoping information. If the provided # value is '''object''' then variable references of the # command are created as instance variables. If the value # is '''method''', then a method frame is pushed (the # method can resolve '''self'' or can call '''next'''). # @parameter cmd A reference to an implementation. If the # implementation is a method, the value should be a # <<@gls methodhandle>>, if it is a Tcl command, it should # be a fully qualified name. # @class.method {Object contains} # # A builder for nested object structures. Object and class # construction statements passed to this method as its last argument # are evaluated in a way so that the receiver object becomes the # parent of the newly constructed objects and classes. This is # realized by setting explicitly the namespace for constructing # relatively named objects. Fully qualified object names evade the # nesting. # ''' # nx::Class create Window { # :contains { # \# # \# Become children of Window, implicitly # \# # nx::Class create Header # nx::Object create Panel # } # \# # \# Explicitly declared a child of Window # \# # nx::Class create [self]::Slider # } # ''' # # @parameter -withnew:boolean Provides for re-scoping # automatically named objects so that they become # nested. If turned off, objects constructed using # <<@class.method {Class new}>> do not becomes children # of the receiver object. # @parameter -object An optional, fully-qualified object # name. If provided, this object will become the parent # object instead of the receiver object. # @parameter {-class:class ::nx::Object} An optional, # fully-qualified class name. If set jointly with the # '''object''' parameter, and the specified object does # not exist, the parent object is created from this # class. # @parameter cmds The construction statements for the objects # and classes to nest, as a script block. # @class.method {Object copy} # # Creates a full and deep copy of a given object. The object's copy # features all structural and behavioral properties of the source # object, including nested objects, the slots, namespaces, filters, # and mixins. # # @parameter newName The name of the copy target to be # created and populated with the strucural and # behavioral features of the source object. # @class.method {Object delete} # # Removes various structural and behavioral features of an object by # specifying their name: properties, variables, and methods. # @class.method {Object "delete property"} # # @parameter name The name of the property to delete # @class.method {Object "delete variable"} # # @parameter name The name of the variable to delete # @class.method {Object "delete method"} # # @parameter name The name of the method to delete # @class.method {Object property} # # Defines a per-object <<@gls property>>. For every property, a <<@gls # slotobject>> is created. A property also provides for a pair of # getter and setter methods, automatically. # # @parameter -class:class Allows for specifying a class for the # managing <<@gls slotobject>> other than the default # slot class, i.e., <<@class VariableSlot>> # @parameter -nocomplain:switch If this flag is provided, an # existing object variable by the property name will not # be overwritten. Instead, an error exception is thrown. # @parameter spec The propery specification can be a list of, at # least, one or two elements, maximum. The first element # specifies the property name, optionally followed by # parameter types after a colon delimiter. If provided, # the second element sets the default value for this # property. # @parameter initblock:optional A Tcl script which is # evaluated for the scope of the property's <<@gls # slotobject>> during its initialization. # @class.method {Object variable} # # Defines a per-object variable. Per default, no getter and setter # methods for this object variable are created, unless requested # explicitly. A defaul value can be specified. # # @parameter -accessor:switch If provided, a pair of getter # and setter methods, named after the variable, are # created on the fly. # @parameter -class Allows for specifying a class for the # managing <<@gls slotobject>> other than the default # slot class, i.e., <<@class VariableSlot>>. Note that a # slot object is not necessarily created by # '''variable''', only if needed (i.e., accessors are # requested, an init script is provided) # @parameter -initblock An optional Tcl script which is evaluated # for the scope of the variable-managing <<@gls # slotobject>> during its initialization. # @parameter -nocomplain:switch If this flag is provided, an # existing object variable by the property name will not # be overwritten. Instead, an error exception is thrown. # @parameter spec The variable specification is a single element # which specifies the variable name, optionally followed by # parameter types after a colon delimiter. # @parameter defaultValue If provided, sets the default value for this # object variable. # @class.method {Object "filter guard"} # # Attaches a guard expression to a previously defined per-object # filter; or returns the currently defined guards, if the last # argument is not provided. # # @parameter filter Name of the per-object filter to be guarded or queried for guards # @parameter guard:optional A guard expression applying to the per-object filter # @class.method {Object "mixin guard"} # # Attaches a guard expression to a previously defined per-object mixin; or # returns the currently defined guards, if the last argument is not # provided. # # @parameter mixin Name of the per-object mixin to be guarded or queried for guards # @parameter guard:optional A guard expression to shield the per-object mixin ######################################################################## # ::nx::Class ######################################################################## # @class Class # # ::nx::Class is the <<@gls basemetaclass>> of the <<@glossary nx>>. # All application classes are created as (direct or indirect) # instances from this class using, e.g., its <<@class.method "Class # create">> method: # # ''' # nx::Class create Window { # \# properties # :property size:object,type=::Area # :property {visibility:boolean true} # \# methods # :public method display {} {;} # :public method hide {} {;} # } # ''' ######################################################################## # Attributes of ::nx::Class ######################################################################## # @class.property {Class filter} # # Register the specified methods as per-class <<@glspl # filter>>. Every filter must be an existing method for the # scope of the class. # @class.property {Class mixin} # # Register the specified classes as per-class <<@glspl mixin_class>>. # Every mixin must be an existing class at the time of setting the # mixin relation. # @class.property {Class object-filter} # # Register the specified methods as per-object <<@glspl filter>> for # the class. Every filter must be an existing method for the scope of # the class object. # @class.property {Class object-mixin} # # Register the specified classes as per-object <<@glspl mixin_class>> # for the class object. Every mixin must be an existing class at the # time of setting the mixin relation. # @class.property {Class superclass} # # Set the superclasses for this class. The argument might contain a # list of classes to specify multiple inheritance. The order of the # superclasses is significant. At the time of specifying the class, # these superclasses must exist in the interpreter. ######################################################################## # Methods of ::nx::Class ######################################################################## # @class.hook {Class alloc} # # Creates an uninitialized object. The method '''alloc''' is used by # <<@class.method "::nx::Class create">> to allocate an object and to # invoke <<@class.method "::nx::Object configure">> and '''init''' in # subsequent steps to fully initialize the object. Only in rare # situations, an application developer may consider bypassing the # overall '''create''' mechanism to create uninitialized objects. # # @parameter objectName The designated object identifier assigned to the # object storage to be allocated. # @return The fully qualified name of the allocated, uninitialized object # @class.method {Class alias} # # Define an <<@gls alias>> as class-wide method. This helper method is # used for defining a method with the specified name by binding a # pre-existing implementation. This alias target is provided as the # last argument. # # @parameter methodName Name of the new method # @parameter -returns Parameter specification to check the result of # the alias method (e.g. '''integer'''). # @parameter -frame Optional scoping information. If the provided # value is '''object''', then variable references of the # command are created as instance variables. If the value # is '''method''', then a method frame is pushed. Thus, the # method can resolve <<@command current>> and can call # <<@command next>>. # @parameter cmd A reference to an implementation. If the # implementation is a method, the value should be a # <<@gls methodhandle>>. If it is a Tcl command, it should # be a fully qualified command name. # @class.method {Class class} # # The '''class''' modifier can be applied to a variety of methods to # perform their operations for the scope of a class object, rather # than for their instances. # @class.method {Class "class delete"} # @class.method {Class "class delete property"} # # @parameter name The name of the property to delete # @class.method {Class "class delete variable"} # # @parameter name The name of the variable to delete # @class.method {Class "class delete method"} # # @parameter name The name of the method to delete # @class.method {Class "class alias"} # # @use class.method {Object alias} # @class.method {Class "class forward"} # # @use class.method {Object forward} # @class.method {Class "class info"} # # Provides introspection on class objects. A variety of introspection # options exists. '''info''' is implemented as an <<@gls # ensemble>>. Hence, the introspection options turn into proper # submethods. See <<@class.method "Object info">> for details. # @class.method {Class "class method"} # # Defines a method for a class object, similarly to creating <<@Gls tcl>> # '''procs'''. Optionally, pre- and post-conditions may be specified # by passing two additional arguments. Therefore, to specify only # post-assertions an empty pre-assertion list must be given. All # assertions are a list of ordinary <<@acr tcl>> '''expr''' # statements. When '''method''' is called with an empty argument list # and an empty body, the specified method is deleted. # ''' # Class create C { # :public class method foo args {;} # :public class method bar args {;} # } # # C foo; # invokes "foo" # # C class method foo {} {}; # deletes "foo" # C class delete method bar; # deletes "bar" # ''' # # @parameter name The method name # @parameter arguments:parameter,0..* A list specifying # non-positional and positional method parameters, plus # their parameter types/options. # @parameter -returns Provide an out parameter # specification, used to check the # return value of the method dispatch. # @parameter body The script which forms the method body # @parameter -precondition Optional assertions that must hold before # the proc executes # @parameter -postcondition Optional assertions that must hold after # the proc executes # @class.method {Class "class filterguard"} # # Attaches a guard expression to a previously defined filter on the # class object; or returns the currently defined guards, if the last # argument is not provided. # # @parameter filter Name of the per-object filter to be guarded or queried for guards # @parameter guard:optional A guard expression applying to the per-object filter # @class.method {Class "class mixinguard"} # # Attaches a guard expression to a previously defined mixin on the # class object; or returns the currently defined guards, if the last # argument is not provided. # # @parameter mixin Name of the per-object mixin to be guarded or queried for guards # @parameter guard:optional A guard expression to shield the per-object mixin # @class.method {Class "class variable"} # # @use class.method {Object variable} # @class.method {Class "class filter"} # # Register the specified methods as <<@glspl filter>> with the class # object. Every name in the <<@gls filterspec>> must resolve to an # existing method in the scope of the class object. # # @parameter args One or more <<@glspl filterspec>> # @class.method {Class "class mixin"} # # Register the specified classes as <<@glspl mixin_class>> with the # class object. Every mixin must be an existing class at the time of # setting the mixin relation. # # @parameter args One or more <<@glspl mixin_class>> specifications # @class.method {Class "class property"} # # @use class.method {Object property} # @class.method {Class delete} # # Removes various structural and behavioral features (i.e., # properties, variables, and methods) of a class based on the feature # names. # @class.method {Class "delete property"} # # @parameter name The name of the property to delete # @class.method {Class "delete variable"} # # @parameter name The name of the variable to delete # @class.method {Class "delete method"} # # @parameter name The name of the method to delete # @class.method {Class "filter guard"} # # Attaches a guard expression to a previously defined class-wide # filter; or returns the currently defined guards, if the last # argument is not provided. # # @parameter filter Name of the per-class filter to be guarded or queried for guards # @parameter guard:optional A guard expression applying to the class-wide filter # @class.method {Class "mixin guard"} # # Attaches a guard expression to a previously defined per-class mixin; or # returns the currently defined guards, if the last argument is not # provided. # # @parameter mixin Name of the per-class mixin to be guarded or queried for guards # @parameter guard:optional A guard expression to shield the per-class mixin # @class.method {Class info} # # Provides introspection on classes. A variety of introspection # options exists. '''info''' is implemented as an <<@gls # ensemble>>. Hence, the introspection options appear as proper # submethods. Introspective features, if not documented below, are # inherited from <<@class.method "Object info">>. # # @sub-method filter # @sub-method mixin # @sub-method slot # @sub-method parameter # @class.method {Class "info filter guard"} # # Returns the guards for filter identified by the name of a <<@gls filter>> # # @parameter filter Registration name of the filter # @class.method {Class "info filter methods"} # # Returns a list of methods registered as <<@glspl filter>>. # # @parameter -guards The filter guards are also returned # @parameter pattern Return only method for filter names # matching the given pattern (like # '''string match''') # @class.method {Class "info properties"} # # Report all properties defined for a class. The properties are # printed in their property-defining notation: '''name:paramtype # ?default?'''. # # @parameter {-type:class ::nx::Slot} Select the kind of # slot objects to be # returned by specifying # a class (e.g., # <<@class # VariableSlot>>). # @parameter -closure:switch Include the slots managing this # class over the transitive # relationships of the class, with # its superclasses and transitive # mixin classes. # @parameter -source Restrict the search to either # '''application''' classes or # '''baseclasses'''. If omitted, the # search includes '''all''' classes. # @parameter pattern:optional Restrict the lookup to slot objects # matching the given pattern string # in the sense of '''string match''' # @class.method {Class "info methods"} # # Allows you to query the methods defined on the object. Note the # difference to <<@class.method "Object {info lookup methods}">> which # covers all methods actually callable upon the class object itself. # # @parameter -callprotection Restrict to the methods of a specific # call protection level: '''public''', '''protected''', # '''private''', or '''all''' (the latter is the default). # @parameter -closure:switch Include the methods defined over # the transitive relationships of the # class, i.e., by its superclasses # and transitive mixin classes. # @parameter -type Restrict the search by certain method # kinds: '''scripted''', '''builtin''', '''alias''', # '''forwarder''', '''object''', and '''setter'''. If # omitted, '''all''' method sorts are considered. # @parameter -path The pattern is expressed over the method paths, # rather than the method names. This allows for matching # <<@glspl ensemble>>. # @parameter -source Restrict the search to either # '''application''' classes or # '''baseclasses'''. If omitted, the # search includes '''all''' classes. # @parameter pattern The pattern string in the style of '''string match''' # @class.method {Class "info heritage"} # # Returns the ordered list of superclasses of a class. # # @parameter pattern A pattern string in the sense of # '''string match''' to be matched with # the candidate class names. # @class.method {Class "info instances"} # # Gives the list of instances, i.e., the member objects of the class. # # @parameter -closure:switch Include the instances defined over # the transitive subclass relationships of the # class. # @parameter pattern A pattern string in the sense of # '''string match''' to be matched with # the candidate class names. # @class.method {Class "info mixin guard"} # # Retrieves the <<@glspl guard>> applied to the mixin class # idenitified by the mixin class name # # @parameter mixin The name of the mixin whose guards are requested # @class.method {Class "info mixin classes"} # # The list of per-object <<@glspl mixin_class>> currently registered for the # class is returned. # # @parameter -closure Extend the pattern search to the # indirect mixin classes, by traversing # all direct and indirect per-class # mixin relations. # @parameter -guards Add the guard expressions of the mixin # classes to the output # @parameter -heritage Ascertain that the mixin context of # the object is valid # @parameter pattern Restrict the lookup to class names # matching the given pattern string, in # the sense of '''string match''' # @class.method {Class "info mixinof"} # # Identifies the classes for which the receiver class is registered as # a mixin. # # @parameter -closure:switch Include the instances defined over # the transitive mixin relationships # of the class. If set, all # per-object and per-class mixin axes # are traversed. # @parameter -scope Restrict the registration scope to # either per-class ('''class''') or # per-object mixins ('''object'''). By # default, '''all''' mixin registrations # are reported. # @parameter pattern A pattern string in the sense of # '''string match''' to be matched with # the candidate class names. # @class.method {Class "info subclass"} # # The list of subclasses derived from the class is returned. # # @parameter -closure:switch Include the classes down # the transitive subclass # relationships of the class. If set, # all consecutive subclass # relations are traversed. If # omitted, only direct subclasses # are reported. # @parameter pattern A pattern string in the sense of # '''string match''' to be matched with # the candidate subclass names. # @class.method {Class "info superclass"} # # Returns the list of superclasses specified for the given class. # # @parameter -closure:switch Include the classes along # the transitive superclass # relationships of the class. If set, # all consecutive superclass # relations are traversed. If # omitted, only direct superclasses # are reported. # @parameter pattern A pattern string in the sense of # '''string match''' to be matched with # the candidate superclass names. # @class.method {Class "info slot definition"} # # Report all <<@glspl slot>> defined for a class. The slots owned by # the given slot are reported in the property-defining notation: # '''name:paramtype ?default?'''. Do not confuse this introspector # with <<@class.method "Object {info lookup slots}">> which gives all # slots managing the current class object. # # @parameter {-type:class ::nx::Slot} Select the kind of # slot objects to be # returned by specifying # a class (e.g., # <<@class # VariableSlot>>). # @parameter -closure:switch Include the classes along the # transitive mixin and superclass # relationships of the class. If set, # all indirect superclass and # transitive mixins are covered. If # omitted, only direct superclasses # are reported. # @parameter -source Restrict the search to either # '''application''' classes or # '''baseclasses'''. If omitted, the # search includes '''all''' classes. # @parameter pattern:optional Restrict the lookup to slot objects # matching the given pattern string in # the sense of '''string match''' # @class.method {Class "info slot names"} # # Report the names of all <<@glspl slot>> defined for the class. Do # not confuse this introspector with <<@class.method "Object {info # lookup slots}">> which gives all slots managing the instances of the # current class. # # @parameter {-type:class ::nx::Slot} Select the kind of # slot objects to be # returned by specifying # a class (e.g., # <<@class # VariableSlot>>). # @parameter -closure:switch Include the classes along the # transitive mixin and superclass # relationships of the class. If set, # all indirect superclass and # transitive mixins are covered. If # omitted, only direct superclasses # are reported. # @parameter -source Restrict the search to either # '''application''' classes or # '''baseclasses'''. If omitted, the # search includes '''all''' classes. # @parameter pattern:optional Restrict the lookup to slot objects # matching the given pattern string in # the sense of '''string match''' # @class.method {Class "info slot objects"} # # Report the fully qualified command names of all <<@glspl slot>> # defined for a class. Do not confuse this introspector with # <<@class.method "Object {info lookup slots}">> which gives all slots # managing the receiver class. # # @parameter {-type:class ::nx::Slot} Select the kind of # slot objects to be # returned by specifying # a class (e.g., # <<@class # VariableSlot>>). # @parameter -closure:switch Include the classes along the # transitive mixin and superclass # relationships of the class. If set, # all indirect superclass and # transitive mixins are covered. If # omitted, only direct superclasses # are reported. # @parameter -source Restrict the search to either # '''application''' classes or # '''baseclasses'''. If omitted, the # search includes '''all''' classes. # @parameter pattern:optional Restrict the lookup to slot # objects matching the given # pattern string in the sense of # '''string match''' # @class.method {Class "info parameter definitions"} # # Returns all parameters, or the selected one, as object parameter # specifications # # @parameter name:optional If provided, the result is limited to # the parameter identified by this name # value # @class.method {Class "info parameter list"} # # Returns all parameters, or the selected one, as a list of dashed # parameter name(s) # # @parameter name:optional If provided, the result is limited to # the parameter identified by this name # value # @class.method {Class "info parameter names"} # # Returns the bare names of all parameters, or the selected one # # @parameter name:optional If provided, the result is limited to # the parameter identified by this name # value # @class.method {Class "info parameter syntax"} # # Returns an annotated representation of all object parameters, or the # selected one. The representation resembles the Tcl command call # listings (e.g., as featured in the man pages). # # @parameter name:optional If provided, the result is limited to # the parameter identified by this name # value # @class.method {Class create} # # Provides for creating application-level classes and objects. If # the method receiver is a <<@gls metaclass>>, a <<@gls class>> will be # created. Otherwise, '''create''' yields an object. '''create''' # is responsible a multi-phase object creation scheme. This # creation scheme involves three major steps: # ''' # [Object create anObject] (1) # ---------------. .--------------. # -------------->|Class->create()|-->|Class->alloc()| # `---------------' `--------------' # | | (2) .-------------------. # | .----->|Object->configure()| # | `-------------------' # | (3) .------. # .........>|init()| # `------' # ''' # (1) A call to <<@class.method "::nx::Class alloc">> to create a raw, # uninitalized object. # # (2) The newly allocated object receives a method call upon # <<@class.method "::nx::Object configure">>. This will establish the # object's initial state, by applying <<@gls objparam>> values # provided at object creation time and default values defined at # object definition time. # # (3) Finally, '''create''' emits a call to the initialization method # '''init''', if available. An '''init''' method can be defined by a # class on behalf of its objects, to lay out class-specific # initialisation behaviour. Alternatively, each single object may # define an '''init''' method on its own. # # By overloading the method in a <<@gls metaclass>>, you can refine or # replace this default object creation scheme (e.g., for applying # application-specific naming schemes). # # For creating an object or a class, you must name '''create''' # explicitly, i.e.: # ''' # ::nx::Object create anObject # ::nx::Class create AClass # \# This fails: "Method 'AnotherClass' unknown for ::nx::Class." # ::nx::Class AnotherClass # ''' # Note that this method is also invoked internally when executing # <<@class.method {Class new}>>. # # @parameter name The designated identifier on the class or the object to be created. # @parameter args arguments to be passed down to the object creation # procedure used to initialize the object. # @return The fully qualified name of the created object. # @class.hook {Class dealloc} # # Marks objects for physical deletion in memory. Beware the fact # that calling '''dealloc''' does not necessarily cause the object # to be deleted immediately. Depending on the lifecycle of the # object's environment (e.g., the '''interp''', the containing # namespace) and on call references down the callstack, the actual # memory freeing operation may occur time-shifted (that is, # later). While '''dealloc''' itself cannot be redefined for # '''::nx::Class''', you may consider refining it in a subclass or # <<@gls mixin_class>> for customizing the destruction process. # # @parameter object The name of the object to be scheduled for deletion. # @class.hook {Class recreate} # # This method is called upon recreating an object. Recreation is the # scheme for resolving object naming conflicts in the dynamic and # scripted programming environment of <<@Gls nx>>: An object or class is # created while an object or class with an identical object identifier # already exists. The method '''recreate''' performs standard object # initialization, per default, after re-setting the state and # relationships of the object under recreation. # ''' # Object create Bar # \# ... # Object create Bar; # calls Object->recreate(::Bar, ...) # ''' # By refining '''recreate''' in an application-level subclass or mixin # class, you can intercept the recreation process. In the pre-part the # refined '''recreate''' method, the recreated object has its old # state, after calling <<@command ::nx::next>> it has been cleaned up. # # If the name conflict occurs between an existing class and a newly # created object (or vice versa), '''recreate''' is not # performed. Rather, a sequence of <<@class.method "::nx::Object # destroy">> and <<@class.method "::nx::Class create">> is triggered: # ''' # Object create Bar # \# ... # \# calls Bar->destroy() -> Class->create(::Bar, ...) # Class create Bar; # ''' # # @parameter name The name (identifier) of the object under recreation # @parameter args Arbitrary vector of arguments # @return The name of the recreated object # @class.hook {Object unknown} # # A hook implementation of the abstracted '''unknown''' hook, called # from within the '''interp''' when the method argument could not # be resolved to a callable method receiver on the given object. # # @parameter m Indicates the unresolvable method name # @parameter args Contains the remainder of the original # argument vector of the indirected method # invocation # @command next # # @use ::nsf::next # @command current # # @use ::nsf::current # @class.hook {Object configure} # # This method participates in the object construction process. It is # automatically invoked after having produced a new object in # <<@class.method "::nx::Class create">>. Upon its invocation, the # variable argument vector '''args''' contains a list of parameters # and parameter values passed in from the call site of the object # construction. They are matched against an <<@gls objparam>> # definition. This definition, and so the actual method parameter # definition of this method, is assembled from configuration values of # the classes along the precedence order (see also <<@class.method # "::nx::Object objectparameter">>). The method '''configure''' # can be called at arbitrary times to "re-set" an object. # # @parameter args The variable argument vector delivers the # object parameters and their values, used to # initialize the object under construction # @class.hook {Object defaultmethod} # # An abstracted hook method which is invoked upon calls to an object # without providing a method name to identify the receiver. In <<@Gls # nx>>, the defaultmethod hook implementation returns the object name, # i.e., the result of <<@command current>>. # ''' # Object create ::foo # ::foo defaultmethod; # returns '::foo' # ::foo; # returns '::foo' # ''' # This hook is a versatile extension mechanism to alter the standalone # use of Tcl commands representing objects. # @class.hook {Object init} # # An abstracted hook method which participates in the object # construction process controlled by <<@class.method {Class # create}>>. It is invoked as the last step during object construction # upon the newly created object to provide an intial state. It # resembles the notion of an application-level constructor. The hook # is meant to be provided by application developers for initialising # their application classes and objects. Note that a custom # implemented '''init''' constructor must not contain a parameter # specification. Your init must not expect or does not receive any # arguments from the NX object construction machinery. # # @parameter args Note: In NX, this will be an empty list (i.e., # '''[list]''' or {}) # @class.method {Object destroy} # # The standard shutdown handler ("destructor") for all # objects. Destruction, finally, commands the physical deletion of the # object. The method '''destroy''' can be refined by subclasses or # <<@glspl mixin_class>> to add additional, class-specific shutdown # behaviour. Note that, in most cases, the class-specific # '''destroy''' methods are expected to call <<@command ::nx::next>> # to request the physical destruction, ultimately. # ''' # nx::Class create Foo { # :method destroy {} { # puts "destroying [self]" # next # } # } # Foo create f1 # f1 destroy # ''' # Some background details: The method <<@class.method "::nx::Object destroy">> # delegates the actual destruction to <<@class.method "::nx::Class # dealloc">> which clears the memory object storage. Essentially, this # behaviour could be scripted as: # ''' # Object method destroy {} { # [:info class] dealloc [self] # } # ''' # Note, however, that '''destroy''' is protected against # application-level redefinition. You must refine it in a subclass # or a <<@gls mixin_class>>. # @class.method {Object uplevel} # # This helper allows you to evaluate a script in the context of # another callstack level (i.e., callstack frame). # # @parameter level:optional The starting callstack level (defaults to the # value of '''[current callinglevel]''') # @parameter script:list The script to be evaluated in the targeted # callstack level # @class.method {Object eval} # # Evaluate a special Tcl script for the scope of the receiver # object. In this script, you may use the colon-prefix notation to # dispatch to methods and access variables of the eval-receiving # object. Also, you can maintain script-local variables to store # intermediate results. # ''' # nx::Object create a { # :property {bar 1} # :public method foo {x} { return $x } # } # # set script { # set y [:foo ${:bar}] # } # # a eval $script # ''' # # @parameter args An argument vector concatenated to a string # and then evaluated as a script # @class.method {Object upvar} # # This helper allows you to bind a local variable to a variable # residing at a different callstack level (frame). # # @parameter level:optional The starting callstack level (defaults to the # value of '''[current callinglevel]''') # # @parameter sourceVar A variable which should be linked to a ... # @parameter targetVar ... which is a local variable in a method scope # @see ... # @class.method {Object volatile} # # By calling on this method, the object is bound in its lifetime to # the one of the active call site (e.g., the given <<@acr tcl>> proc or method # scope): # ''' # proc foo {} { # info vars; # shows "" # set x [Object create Bar -volatile] # info vars; # shows "x Bar" # } # ''' # Behind the scenes, '''volatile''' registers a C-level variable trace # ('''Tcl_TraceVar()''') on the hiddenly created local variable (e.g., # '''Bar'''), firing upon unset events and deleting the referenced # object ('''Bar'''). That is, once the callframe context of '''foo''' # is left, the local variable '''Bar''' is unset and so the bound # object destruction is triggered. # @class.method {Class new} # # A convenience method to create auto-named objects and <<@glspl # class>>. It is a frontend to <<@class.method "::nx::Class # create">>. For instance: # ''' # set obj [nx::Object new] # set cls [nx::Class new] # set w [Window new] # ''' # This will provide object identifiers of the form # e.g. '''::nsf::__#0'''. The uniqueness of auto-generated identifiers # is guaranteed for the scope of the current '''interp'''. # # @parameter -childof If provided, the new object is created as a child of # the specified object. # @parameter args The variable arguments passed down to <<@class.method # "::nx::Class create">>. # @class.method {Class property} # # Defines a <<@gls property>> for an entire class. For every property, # a <<@gls slotobject>> is created. A property also provides for a # pair of getter and setter methods, automatically. The getter and # setter are available for instances of the class. # # @parameter -class:class Allows for specifying a class for the # managing <<@gls slotobject>> other than the default # slot class, i.e., <<@class VariableSlot>> # @parameter spec The propery specification can be a list of, at # least, one or two elements, maximum. The first element # specifies the property name, optionally followed by # parameter types after a colon delimiter. If provided, # the second element sets the default value for this # property. # @parameter initblock:optional A Tcl script which is # evaluated for the scope of the property's <<@gls # slotobject>> during its initialization. # @class.hook {Class unknown} # # A hook implementation of the abstracted '''unknown''' hook, called # from within the '''interp''' when the method argument could not # be resolved to a callable method on the receiving object. # # @parameter methodName Indicates the unresolvable method name # @parameter args Contains the remainder of the original # argument vector of the indirected method # invocation # @class.method {Class variable} # # Defines a variable for the scope of a class. Per default, no getter # and setter methods for this object variable are created, unless # requested explicitly. Also, a variable does not become an object # parameter automatically. A defaul value can be specified. # # @parameter -accessor:switch If provided, a pair of getter # and setter methods, named after the variable, are # created on the fly. # @parameter -class Allows for specifying a class for the # managing <<@gls slotobject>> other than the default # slot class, i.e., <<@class VariableSlot>>. Note that a # slot object is not necessarily created by # '''variable''', only if needed (i.e., accessors are # requested, an init script is provided) # @parameter -config:switch Make the variable an object parameter, # rendering the variable accessible during instance # construction # @parameter -initblock An optional Tcl script which is evaluated # for the scope of the variable-managing <<@gls # slotobject>> during its initialization. # @parameter spec The variable specification is a single element # which specifies the variable name, optionally followed by # parameter types after a colon delimiter. # @parameter defaultValue If provided, sets the default value for this # object variable. # @class.method {Class method} # # Defines a class-wide method available to all instances of the class, # similarly to Tcl specifying '''procs'''. Optionally, pre- and # post-conditions may be specified by two additional # arguments. Therefore, to specify only post-assertions an empty # pre-assertion list must be given. All assertions are a list of # ordinary <<@Gls tcl>> '''expr''' statements. When '''method''' is # called with an empty argument list and an empty body, the specified # method, if existing, is deleted. # ''' # Class create AClass { # :public method foo args {;} # :public method bar args {;} # } # # AClass create anInstance # anInstance foo; # invokes "foo" # # AClass method foo {} {}; # deletes "foo" # AClass delete method bar; # deletes "bar" # ''' # # @parameter name The method name # @parameter arguments:parameter,0..* A list specifying # non-positional and positional method parameters, plus # their parameter types/options. # @parameter -returns Provide an out parameter # specification, used to check the # return value of the method dispatch. # @parameter body The script which forms the method body # @parameter -precondition Optional assertions that must hold before # the proc executes # @parameter -postcondition Optional assertions that must hold after # the proc executes # @class.method {Object method} # # Defines a per-object method, similarly to creating <<@gls tcl>> # '''procs'''. Optionally, pre- and post-conditions may be specified # by passing two additional arguments. Therefore, to specify only # post-assertions an empty pre-assertion list must be given. All # assertions are a list of ordinary <<@acr tcl>> '''expr''' # statements. When '''method''' is called with an empty argument list # and an empty body, the specified method is deleted. # ''' # Object create anObject { # :public method foo args {;} # :public method bar args {;} # } # # anObject foo; # invokes "foo" # # anObject method foo {} {}; # deletes "foo" # anObject delete method bar; # deletes "bar" # ''' # # @parameter name The method name # @parameter arguments:parameter,0..* A list specifying # non-positional and positional method parameters, plus # their parameter types/options. # @parameter -returns Provide an out parameter # specification, used to check the # return value of the method dispatch. # @parameter body The script which forms the method body # @parameter -precondition Optional assertions that must hold before # the proc executes # @parameter -postcondition Optional assertions that must hold after # the proc executes # @class.method {Object forward} # # Register a special-purpose per-object method (similar to a # '''proc'''), referred to as a <<@gls forwarder>>, for # forward-delegating calls to a callee (a <<@acr tcl>> command, a method owned # by another object). When the <<@gls forwarder>> method is called, # the actual arguments of the invocation are appended to the specified # arguments. Certain manipulations can be applied on the # forward-passed argument vector: # # '''%proc''' substituted by name of the forwarder method # # '''%self''' substitute by name of the object # # '''%1''' substitute by first argument of the invocation # # ''' {%@POS value} ''' substitute the specified value in the # argument list on position POS, where POS can be a positive or # negative integer or end. Positive integers specify the position # from the begin of the list, while negative integer specify the # position from the end. # # ''' {%argclindex LIST} ''' take the nth argument of the specified # list as substitution value, where n is the number of arguments # from the invocation. # # '''%%''' a single percent. # # '''%Tcl-command''' The command to be executed; the result of the # command evaluation is substituted for the placeholding statement. # # Additionally, each argument can be prefixed by the positional prefix # %@POS (note the delimiting space at the end) that can be used to # specify an explicit position. POS can be a positive or negative # integer or the word end. The positional arguments are evaluated from # left to right and should be used in ascending order. # # @parameter method The name of the delegating or forwarder method # @parameter -default Is used for default method names (only # in connection with %1) # @parameter -methodprefix Prepends the specified prefix to the second # argument of the invocation. # @parameter -objframe Causes the target to be evaluated in # the scope of the object. # @parameter -onerror Register an error handler # @parameter -returns Provide an out parameter # specification, used to check the # return value of the forward dispatch. # @parameter -verbose Print the substituted command string to stderr # before performing the command # execution. For debugging purposes. # @parameter target Possibly the name of the delegatee (a # Tcl proc, method, object, ...), or an # argument mutator/accessor (e.g., # '''%self''', '''%proc''') # @parameter args The specified argument vector, # consisting of literal values and/or # argument accessors/mutators # @class.method {Object move} # # Effectively renames an object. First, the source object is copied # using <<@class.method {Object copy}>>. Second, the source object is # destroyed by invoking <<@class.method {Object destroy}>> on it. This # method is also called internally when '''rename''' is performed for # a Tcl command representing an object. # # @parameter newName The name of the target object # @class.method {Object private} # # The modifier method sets the '''private''' property of the method # under definition. # ''' # nx::Class create A { # :private method foo {} {;} # :private property bar # :private alias baz -frame object ::incr # :private class method baf {} {;} # } # ''' # # @parameter args Argument vector which details a # method-defining operation (e.g., # <<@class.method {Object method}>>, # <<@class.method {Object alias}>>) # @class.method {Object protected} # # The modifier method sets the '''protected''' property of the method # under definition. # ''' # nx::Class create A { # :protected method foo {} {;} # :protected property bar # :protected alias baz -frame object ::incr # :protected class method baf {} {;} # } # ''' # # @parameter args Argument vector which details a # method-defining operation (e.g., # <<@class.method {Object method}>>, # <<@class.method {Object alias}>>) # @class.method {Object public} # # The modifier method sets the '''public''' property of the method # under definition. # ''' # nx::Class create A { # :public method foo {} {;} # :public property bar # :public alias baz -frame object ::incr # :public class method baf {} {;} # } # ''' # # @parameter args Argument vector which details a # method-defining operation (e.g., # <<@class.method {Object method}>>, # <<@class.method {Object alias}>>) # @class.method {Class forward} # # @use class.method {Object forward} # @class.method {Object info} # # Provides introspection on objects. A variety of introspection # options exists. '''info''' is implemented as an <<@gls # ensemble>>. Hence, the introspection options turn into proper # submethods. # # @sub-method lookup # @sub-method has # @sub-method filter # @sub-method mixin # @sub-method slot # @class.method {Object "info is"} # # Verifies whether a given object acts as an application class (i.e., # an instance of <<@class Class>>), as a metaclass (i.e., an instance of # <<@class Class>> which also subclasses <<@class Class>>), or an # object-system class. # # @parameter objectkind Specifies the class test: '''class''', # '''baseclass''', '''metaclass''' # @class.method {Object "info lookup filter"} # # Search for a method which is currently registered as a filter (in # the invocation scope of the given object). If found, the # corresponding <<@gls methodhandle>> is returned. # @class.method {Object "info lookup method"} # # Verifies whether there is a method under a given name available for # invocation upon the object. In case, the introspective call returns # the corresponding <<@gls methodhandle>>. If there is no so-named # method available, an empty string is returned. # @class.method {Object "info lookup methods"} # # Retrieves the methods which are available for dispatch (which are # therefore callable) on the given object. The result set can be # constraint by a match pattern. If the answer set is empty, an empty # string is returned. The answer set can be restricted not only via # match pattern, but also via the following parameters: # # @parameter -callprotection Restrict to the methods of a specific # call protection level: '''public''', '''protected''', # '''private''', or '''all''' (the latter is the default). # @parameter -incontext The search will consider the full # dispatch context, i.e., mixin guards # are incorporated when selecting the # method population. This filter assumes # that mixin-provided methods should be # covered (see also '''-nomixins'''). # @parameter -type Restrict the search by certain method # kinds: '''scripted''', '''builtin''', '''alias''', # '''forwarder''', '''object''', and '''setter'''. If # omitted, '''all''' method sorts are considered. # @parameter -nomixins If set, exclude methods provided by # <<@glspl mixin_class>> # @parameter -path The pattern is expressed over the method paths, # rather than the method names. This allows for matching # <<@glspl ensemble>>. # @parameter -source Restrict the search to either # '''application''' classes or '''baseclasses'''. If # omitted, the search includes '''all''' classes. # @parameter pattern The pattern string in the style of '''string match''' # @class.method {Object "info lookup slots"} # # Returns the list of <<@glspl slot>> which manage the state of the # given object. They are resolved by following the class <<@gls # precedence>> upward and coercing the lists of slots provided by # all classes. # # @parameter {-type:class ::nx::Slot} Select the kind of # slot objects to be # returned by specifying # their class (e.g., # <<@class # VariableSlot>>). # @parameter -source Find either slot objects responsible # for '''application''' classes or '''baseclasses'''. If # omitted, '''all''' classes are considered. # @parameter pattern:optional Restrict the lookup to slot objects # matching the given pattern string in # the sense of '''string match''' # @class.method {Object "info children"} # # Computes the list of aggregated (or nested) objects. The resulting # list reports the fully qualified object names. If a <<@gls # namepattern>> was specified, all child objects, whose names are # matched by the pattern, are returned. Otherwise, all children are # reported. # # @parameter -type Filter children by their class membership. # @parameter pattern A filter string applying to the command names # of the children (like a '''string match''' # pattern). # @class.method {Object "info class"} # # Returns the name of the object's class. # @class.method {Object "info filter guard"} # # Returns the guards for filter identified by the name of a <<@gls filter>> # # @parameter name Registration name of the filter # @class.method {Object "info filter methods"} # # Returns a list of methods registered as <<@glspl filter>>. # # @parameter -guards The filter guards are also returned # @parameter -order Request that the filters are reported # in valid order # @parameter patterns Return only method for filter names # matching the given pattern (like # '''string match''') # @class.method {Object "info has mixin"} # # Verifies in a boolean test whether the object has a given <<@gls # mixin_class>>. # # @parameter class The class which is tested for its property as mixin # of the given object # @class.method {Object "info has namespace"} # # Tells you whether the object has a companion, per-object <<@acr # tcl>> namespace. Note that the results do not necessarily correspond # to those yielded by '''[namespace exists /obj/]'''. See also # <<@class.method "Object {require namespace}">>. # @class.method {Object "info has type"} # # Tests whether the class passed as the argument is a type of the # object, i.e., whether the object is an instance of the given class # or of one of the class's superclasses. # # @parameter class The class name to be tested against the object. # @class.method {Object "info methods"} # # Allows you to query the methods defined on the object. Note the # difference to <<@class.method "Object {info lookup methods}">> which # covers all methods actually callable upon the object. # # @parameter -callprotection Restrict to the methods of a specific # call protection level: '''public''', '''protected''', # '''private''', or '''all'' (the latter is the default). # @parameter -type Restrict the search by certain method # kinds: '''scripted''', '''builtin''', '''alias''', # '''forwarder''', '''object''', and '''setter'''. If # omitted, '''all''' method sorts are considered. # @parameter -path The pattern is expressed over the method paths, # rather than the method names. This allows for matching # <<@glspl ensemble>>. # @parameter pattern The pattern string in the style of '''string match''' # @class.method {Object "info name"} # # Returns the name of an object without the namespace qualifiers. # @class.method {Object "info slot definition"} # # Report all <<@glspl slot>> defined for an object. The slots owned by # the given object are reported in the property-defining notation: # '''name:paramtype ?default?'''. Do not confuse this introspector # with <<@class.method "Object {info lookup slots}">> which gives all # slots managing the current object. # # @parameter {-type:class ::nx::Slot} Select the kind of # slot objects to be # returned by specifying # a class (e.g., # <<@class # VariableSlot>>). # @parameter pattern:optional Restrict the lookup to slot objects # matching the given pattern string in # the sense of '''string match''' # @class.method {Object "info slot names"} # # Report the names of all <<@glspl slot>> defined for an object. Do not # confuse this introspector with <<@class.method "Object {info lookup # slots}">> which gives all slots managing the current object. # # @parameter {-type:class ::nx::Slot} Select the kind of # slot objects to be # returned by specifying # a class (e.g., # <<@class # VariableSlot>>). # @parameter pattern:optional Restrict the lookup to slot objects # matching the given pattern string in # the sense of '''string match''' # @class.method {Object "info slot objects"} # # Report the fully qualified command names of all <<@glspl slot>> # defined for an object. Do not confuse this introspector with # <<@class.method "Object {info lookup slots}">> which gives all slots # managing the current object. # # @parameter {-type:class ::nx::Slot} Select the kind of # slot objects to be # returned by specifying # a class (e.g., # <<@class # VariableSlot>>). # @parameter pattern:optional Restrict the lookup to slot # objects matching the given # pattern string in the sense of # '''string match''' # @class.method {Object "info properties"} # # Report all properties defined for an object. The properties are # printed in their property-defining notation: '''name:paramtype # ?default?'''. # # @parameter {-type:class ::nx::Slot} Select the kind of # slot objects to be # returned by specifying # a class (e.g., # <<@class # VariableSlot>>). # @parameter pattern:optional Restrict the lookup to slot objects # matching the given pattern string in # the sense of '''string match''' # Should be: # \@use class.method {Object "info slot definition"} # @class.method {Object "info mixin guard"} # # Retrieves the <<@glspl guard>> applied to the mixin class # idenitified by the mixin class name # # @parameter mixin The name of the mixin whose guards are requested # @class.method {Object "info mixin classes"} # # The list of per-object <<@glspl mixin_class>> currently registered for the # object is returned. # # @parameter -guards Add the guard expressions of the mixin # classes to the output # @parameter -heritage Ascertain that the mixin context of # the object is valid # @parameter pattern:optional Restrict the lookup to class names # matching the given pattern string, in # the sense of '''string match''' # @class.method {Object "info parent"} # # Returns the fully qualified name of the parent object; or the name # of the global Tcl namespace "::" if there is no parent object. # @class.method {Object "info precedence"} # # Presents to you the list of classes the object is inheriting # properties and methods from, ordered according to their <<@gls # precedence>>. # # @parameter -intrinsic Limit the reported linearization list # to the intrinsic class # hierarchy. Excludes <<@glspl # mixin_class>> explicitly. # @parameter pattern A pattern string in the sense of # '''string match''' to be matched with # the candidate class names. # @class.method {Object "info unknown"} # # The unknown handler for the '''info''' method ensemble. It is called # upon messages to undefined info sub-methods. It provides for # generating a list of valid sub-methods to recover from the error # condition (see <<@class.method "Object {info info}">>). Unlike other # unknown handlers, the self-context of the unknown method is the # definition object and not the receiver. Therefore, the receiver # object is provided as the second argument. # # @parameter method The name of the undefined '''info''' sub-method # @parameter obj:object The receiver of the '''info''' call # which is the registration object # @parameter args The remainder of the original argument vector # @class.method {Object "info info"} # # Lists the valid sub-methods selectors of the '''info''' method ensemble. # @class.method {Object "info method"} # # Returns varios definitional details of a method implementation, # identified by its method name. Examples include the method parameter # specification and the method body. # # @parameter infomethodsubcmd Specify the implementation # detail to retrieve (or the query to perform). One of # '''body''', '''definition''', # '''registrationhandle''', '''definitionhandle''', # '''handle''', '''origin''', '''parameter''', # '''parametersyntax''', '''type''', '''precondition''', # '''postcondition''', '''submethods''' # @parameter name The method name for which to look up # the selected detail # @class.method {Object "info vars"} # # Yields a list of variable names created and defined on the object. # # @parameter pattern A pattern string in the sense of # '''string match''' the resulting # variable names must match. # @class.method {Object "info methods"} # # Allows you to query the methods defined on the object. Note the # difference to <<@class.method "Object {info lookup methods}">> which # covers all methods actually callable upon the object. # # @parameter -callprotection Restrict to the methods of a specific # call protection level: '''public''', '''protected''', # '''private''', or '''all''' (the latter is the default). # @parameter -type Restrict the search by certain method # kinds: '''scripted''', '''builtin''', '''alias''', # '''forwarder''', '''object''', and '''setter'''. If # omitted, '''all''' method sorts are considered. # @parameter -path The pattern is expressed over the method paths, # rather than the method names. This allows for matching # <<@glspl ensemble>>. # @parameter pattern The pattern string in the style of '''string match''' # @class.method {Object require} # # An object can register additional structural and behavioral features # lazily, upon demand. To make features available for lazy # registration, use '''::nsf::method::provide'''. # # @sub-method namespace Attach a Tcl namespace to the object, # with the namespace becoming named # after the object. All object variables # become so available as # namespace-scoped variables. # @sub-method class # @sub-method public # @sub-method protected # @sub-method private # @class.method {Object "require method"} # # Attempts to register a method as identified by a name. The # registered method is subjected to default method call protection (in # NX, '''protected'''). # # @parameter methodName The name of the method requested. # @class.method {Object "require class method"} # # For a class object, attempts to register a method, as identified by # a name, for the scope of the object only; and not for the class. The # registered method is subjected to default method call protection (in # NX, '''protected'''). # # @parameter methodName The name of the class method requested. # @class.method {Object "require public method"} # # Attempts to register a method as identified by a name. The # registered method becomes '''public'''. # # @parameter methodName The name of the method requested. # @class.method {Object "require public class"} # @class.method {Object "require public class method"} # # Attempts to register a class method as identified by a name. The # registered class method becomes '''public'''. # # @parameter methodName The name of the class method requested. # @class.method {Object "require protected method"} # # Attempts to register a method as identified by a name. The # registered method becomes '''protected'''. # # @parameter methodName The name of the method requested. # @class.method {Object "require protected class"} # @class.method {Object "require protected class method"} # # Attempts to register a class method as identified by a name. The # registered class method becomes '''protected'''. # # @parameter methodName The name of the class method requested. # @class.method {Object "require private method"} # # Attempts to register a method as identified by a name. The # registered method becomes '''private'''. # # @parameter methodName The name of the method requested. # @class.method {Object "require private class"} # @class.method {Object "require private class method"} # # Attempts to register a class method as identified by a name. The # registered class method becomes '''private'''. # # @parameter methodName The name of the class method requested. # @class Slot # # A <<@gls slot>> is a meta-object that manages property changes of # objects. A property is either an attribute or a role taken by an # object in an inter-object relation (e.g., in system slots). The # predefined system slots are '''class''', '''superclass''', # '''mixin''', and '''filter'''. These slots appear as methods of # <<@class ::nx::Object>> or <<@class ::nx::Class>>. The slots # provide a common getter and setter interface. Every multivalued # slot provides e.g. a method '''add''' to append a value to the # list of values, and a method '''delete''' which removes it. # # @superclass ::nx::doc::entities::class::nx::Object # @class ObjectParameterSlot # # ... # # @superclass ::nx::doc::entities::class::nx::Slot # @class.property {Slot name:string} # # Name of the <<@gls slot>> which can be used to access the <<@gls # slot>> data from an object # @class.property {Slot multivalued} # # Boolean value for specifying single or multiple values (lists) # @class.property {Slot required} # # Denotes whether a value must be provided # @class.property {Slot default} # # Allows you to define a default value (to be set upon object creation) # @class.property {Slot type} # # You may specify a type constraint on the value range to managed by # the <<@gls slot>> # @class.property {ObjectParameterSlot name} # # Name of the <<@gls slot>> which can be used to access the <<@gls # slot>> data of an object. It defaults to unqualified name of an # instance. # @class.property {ObjectParameterSlot methodname} # # The name of the accessor methods to be registed on behalf of the # <<@gls slot>> object with its domains can vary from the slot name. # @class.property {ObjectParameterSlot domain} # # The domain (object or class) of a <<@gls slot>> on which it can be used # @class.property {ObjectParameterSlot defaultmethods} # # A list of two elements for specifying which methods are called per # default, when no slot method is explicitly specified in a call. # @class.property {ObjectParameterSlot manager} # # The manager object of the <<@gls slot>> (per default, the slot object takes # this role, i.e. '''[self]''') # @class.property {ObjectParameterSlot per-object} # # If set to '''true''', the accessor methods are registered with the # domain object scope only. It defaults to '''false'''. # @class.hook {Class objectparameter} # # An abstracted hook method which is primarily used from within the # object creation process, that is, the object configuration step # performed by <<@class.method {Object configure}>>. The actual # implementation of this hook is called to generate the object # parameter definition and so specifies the object parameter # processing for a given object under construction. For instance, the # <<@gls initcmd>> feature in <<@Gls nx>> is implemented this # way. By providing custom hook implementations (by overloading), # developers can define their own object parametrisation scheme. # @class VariableSlot # # VariableSlot <<@glspl slot>> are used to manage the access, mutation, # and querying of instance variables. One defines variable slots for # objects and classes usually via the helper method <<@class.method # "::nx::Object property">> The following example defines a class # with three variable slots. The property '''salary''' has a default # of '''0''', the property '''projects''' has the empty list as # default and is defined as multivalued and incremental. # ''' # Class create Person { # :property name # :property {salary:integer 0} # :property {projects:multivalued,incremental ""} # } # ''' # # @parameter incremental A boolean value, only useful for multivalued # slots. When set, one can add/delete incrementally values to the # multivalued set (e.g., through an incremental '''add''') # @parameter valuecmd A <<@acr tcl>> command to be executed whenever the managed # object variable is read # @parameter valuechangedcmd A <<@acr tcl>> command to be executed whenever the # value of the managed object variable changes # @parameter arg # @superclass ::nx::doc::entities::class::nx::ObjectParameterSlot # @glossary tcl # # @acronym Tcl # @pretty_name Tool Command Language # @glossary clos # # @pretty_name Common Lisp Object System # @acronym CLOS # @glossary flavors # # @pretty_name Flavors # @glossary nx # # The Next Scripting Language (NX) is one of the languages hosted by # and implemented on top of the Next Scripting Framework (NSF). # # @pretty_name Next Scripting Language # @acronym NX # @glossary objtype # # The type of an object is given by its signature interface. While <<@acr tcl>> # and so NSF object systems are mono-typed (i.e., everything has a # string representation to the interpreter), object-types are lately # checked and verified (e.g., upon method lookup). # # @pretty_name Object-type # @pretty_plural Object-types # @glossary objparam # # @pretty_name Object parameter # @pretty_plural Object parameters # @glossary ensemble # # An ensemble is an auxiliary object which acts as a single method of # another object. The ensemble's per-object methods (the so-called # ensemble methods), however, so appear as submethods of this # top-level method. This ressembles the ideas of subcommands and # namespace ensembles in <<@acr tcl>>. When sending a message to an # object, methods are identified as message receivers by name. This # name can be simple (i.e., a single <<@acr tcl>> word) or composite # (i.e., multiple <<@acr tcl>> words) in the case of # submethods. Submethods are identified by their composite method # names. # # @pretty_name Ensemble # @pretty_plural Ensembles # @glossary namepattern # # @pretty_name Name pattern # @pretty_plural Name patterns # @glossary guard # # A guard acts as ... # # @pretty_name Guard # @pretty_plural Guards # @glossary precedence # # ... # # @pretty_name Precedence order # @pretty_plural Precedence orders # @glossary slot # # ... # # @pretty_name Slot # @pretty_plural Slots # @glossary cim # # In Tcl/XOTcl/NSF, methods can either be scripted or implemented as a # C language extension ... # # @acronym CIM # @pretty_name C-implemented method # @pretty_plural C-implemented methods # @glossary initcmd # # ... # # @acronym initcmd # @pretty_name object initialization script # @pretty_plural object initialization scripts # @glossary filterspec # # ... # # @acronym filterspec # @pretty_name filter specification # @pretty_plural filter specification # @glossary mixinspec # # ... # # @acronym mixinspec # @pretty_name mixin specification # @pretty_plural mixin specification # @glossary property # # ... # # @acronym property # @pretty_name property # @pretty_plural properties # @object configurelibrary/nx/nx.tcl000066400000000000000000003140461242365656200143230ustar00rootroot00000000000000# -*- tcl -*- ############################################################ # nx.tcl -- # # Implementation of the Next Scripting Language (NX) object # system, based on the Next Scripting Framework (NSF). # # Copyright (C) 2010-2014 Gustaf Neumann # Copyright (C) 2010-2014 Stefan Sobernig # # Vienna University of Economics and Business # Institute of Information Systems and New Media # A-1020, Welthandelsplatz 1 # Vienna, Austria # # This work is licensed under the MIT License http://www.opensource.org/licenses/MIT # # Copyright: # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # package require nsf package provide nx 2.0.0 namespace eval ::nx { namespace eval ::nsf {} ;# make pkg-indexer happy namespace eval ::nsf::object {} ;# make pkg-indexer happy namespace eval ::nsf::parameter {} ;# make pkg-indexer happy namespace eval ::nx::internal {} ;# make pkg-indexer happy namespace eval ::nx::traits {} ;# make pkg-indexer happy # # By setting the variable bootstrap, we can check later, whether we # are in bootstrapping mode # set ::nsf::bootstrap ::nx # # First create the ::nx object system. The internally called methods, # which are not defined by in this script, must have method handles # included. The methods "create", "configure", "destroy", "move" and # "__objectcoonfigureparameter" are defined in this script (either scripted, or # via alias). # ::nsf::objectsystem::create ::nx::Object ::nx::Class { -class.alloc {__alloc ::nsf::methods::class::alloc 1} -class.create create -class.dealloc {__dealloc ::nsf::methods::class::dealloc 1} -class.configureparameter __class_configureparameter -class.recreate {__recreate ::nsf::methods::class::recreate 1} -object.configure __configure -object.configureparameter __object_configureparameter -object.defaultmethod {defaultmethod ::nsf::methods::object::defaultmethod} -object.destroy destroy -object.init {init ::nsf::methods::object::init} -object.move move -object.unknown unknown } # # get frequently used primitiva from the next scripting framework # namespace export next current self configure namespace import ::nsf::next ::nsf::current ::nsf::self ::nsf::dispatch # # provide the standard command set for ::nx::Object # ::nsf::method::alias Object upvar ::nsf::methods::object::upvar ::nsf::method::alias Object destroy ::nsf::methods::object::destroy ::nsf::method::alias Object uplevel ::nsf::methods::object::uplevel # # provide ::eval as method for ::nx::Object # ::nsf::method::alias Object eval -frame method ::eval ###################################################################### # Default Methods (referenced via createobjectsystem) ###################################################################### namespace eval ::nsf::methods {} ;# make pkg-indexer happy namespace eval ::nsf::methods::object {} ;# make pkg-indexer happy # Actually, we do not need an unknown handler, but if someone # defines his own unknown handler we define it automatically proc ::nsf::methods::object::unknown {m args} { return -code error "[::nsf::self]: unable to dispatch method '$m'" } # The default constructor proc ::nsf::methods::object::init args {} # This method can be called on invocations of the object without a # specified method. proc ::nsf::methods::object::defaultmethod {} {::nsf::self} ###################################################################### # Class methods ###################################################################### # provide the standard command set for Class ::nsf::method::alias Class create ::nsf::methods::class::create ::nsf::method::alias Class new ::nsf::methods::class::new # set a few aliases as protected # "__next", if defined, should be added as well foreach cmd {uplevel upvar} { ::nsf::method::property Object $cmd call-protected 1 } unset cmd # protect some methods against redefinition ::nsf::method::property Object destroy redefine-protected true ::nsf::method::property Class create redefine-protected true # # Use method::provide for base methods in case they are overloaded # with scripted counterparts ::nsf::method::provide __alloc {::nsf::method::alias __alloc ::nsf::methods::class::alloc} ::nsf::method::provide __dealloc {::nsf::method::alias __dealloc ::nsf::methods::class::dealloc} ::nsf::method::provide __recreate {::nsf::method::alias __recreate ::nsf::methods::class::recreate} ::nsf::method::provide __configure {::nsf::method::alias __configure ::nsf::methods::object::configure} ::nsf::method::provide unknown {::nsf::method::alias unknown ::nsf::methods::object::unknown} # # The method __resolve_method_path resolves a space separated path # of a method name and creates from the path the necessary ensemble # objects when needed. # ::nsf::method::create Object __resolve_method_path { -per-object:switch -verbose:switch path } { set object [::nsf::self] set methodName $path set regObject "" if {[string first " " $path] > -1} { set methodName [lindex $path end] set regObject $object foreach w [lrange $path 0 end-1] { set scope [expr {[::nsf::is class $object] && !${per-object} ? "class" : "object"}] if {[::nsf::is class $object] && !${per-object}} { set scope class set ensembleName [::nx::slotObj ${object} __$w] if {[: ::nsf::methods::class::info::method exists $w] && [: ::nsf::methods::class::info::method type $w] ne "alias"} { return -code error "refuse to overwrite method $w; delete/rename method first." } } else { set scope object if {[: ::nsf::methods::object::info::method exists $w] && [: ::nsf::methods::object::info::method type $w] ne "object"} { return -code error "refuse to overwrite object method $w; delete/rename object method first." } set ensembleName ${object}::$w } #puts stderr "NX check $scope $object info methods $path @ <$w> cmd=[info command $w] obj?[nsf::object::exists $ensembleName] " if {![nsf::object::exists $ensembleName]} { # # Create dispatch/ensemble object and accessor method (if wanted) # set o [nx::EnsembleObject create $ensembleName] if {$scope eq "class"} { if {$verbose} {puts stderr "... create object $o"} # We are on a class, and have to create an alias to be # accessible for objects ::nsf::method::alias $object $w $o if {$verbose} {puts stderr "... create alias $object $w $o"} } else { if {$verbose} {puts stderr "... create object $o"} } set object $o } else { # # The accessor method exists already, check, if it is # appropriate for extending. # set type [::nsf::directdispatch $object ::nsf::methods::${scope}::info::method type $w] set definition [::nsf::directdispatch $object ::nsf::methods::${scope}::info::method definition $w] if {$scope eq "class"} { if {$type eq ""} { # In case of a copy operation, the ensemble object might # exist, but the alias might be missing. ::nsf::method::alias $object $w $ensembleName set object $ensembleName } else { if {$type ne "alias"} {error "can't append to $type"} if {$definition eq ""} {error "definition must not be empty"} set object [lindex $definition end] } } else { if {$type ne "object"} {error "can't append to $type"} if {[llength $definition] != 3} {error "unexpected definition '$definition'"} append object ::$w } } } #puts stderr "... final object $object method $methodName" } return [list object $object methodName $methodName regObject $regObject] } ::nsf::method::property Object __resolve_method_path call-protected true ###################################################################### # Define default method and property protection ###################################################################### ::nsf::method::create Object __default_method_call_protection args {return false} ::nsf::method::create Object __default_accessor args {return public} ::nsf::method::property Object __default_method_call_protection call-protected true ::nsf::method::property Object __default_accessor call-protected true ###################################################################### # Define method "method" for Class ###################################################################### ::nsf::method::create Class method { name arguments:parameter,0..* -checkalways:switch -returns body } { set p [:__resolve_method_path $name] set p [dict filter $p script {k v} {expr {$k in {object regObject methodName}}}] dict with p { #puts "class method $object.$methodName [list $arguments] {...}" set r [::nsf::method::create $object \ -checkalways=$checkalways \ {*}[expr {$regObject ne "" ? "-reg-object [list $regObject]" : ""}] \ $methodName $arguments $body] if {$r ne ""} { # the method was not deleted ::nsf::method::property $object $r call-protected \ [::nsf::dispatch $object __default_method_call_protection] if {[info exists returns]} {::nsf::method::property $object $r returns $returns} } return $r } } ###################################################################### # Define method "unknown" ###################################################################### Class eval { # define unknown handler for class :method unknown {methodName args} { return -code error "method '$methodName' unknown for [::nsf::self];\ consider '[::nsf::self] create $methodName $args' instead of '[::nsf::self] $methodName $args'" } # protected is not yet defined ::nsf::method::property [::nsf::self] unknown call-protected true } ###################################################################### # Remember names of method defining methods ###################################################################### # Well, class is not a method defining method either, but a modifier array set ::nsf::methodDefiningMethod { method 1 alias 1 forward 1 object 1 ::nsf::classes::nx::Class::method 1 ::nsf::classes::nx::Object::method 1 ::nsf::classes::nx::Class::alias 1 ::nsf::classes::nx::Object::alias 1 ::nsf::classes::nx::Class::forward 1 ::nsf::classes::nx::Object::forward 1 } ###################################################################### # Provide method modifiers for ::nx::Object ###################################################################### Object eval { # method modifier "public" :method public {args} { if {![info exists ::nsf::methodDefiningMethod([lindex $args 0])]} { return -code error "'[lindex $args 0]' is not a method defining method" } elseif {[lindex $args 0] eq "object" && ![info exists ::nsf::methodDefiningMethod([lindex $args 1])]} { return -code error "'[lindex $args 1]' is not a method defining method" } set r [: -system {*}$args] if {$r ne ""} {::nsf::method::property [self] $r call-protected false} return $r } # method modifier "protected" :method protected {args} { if {![info exists ::nsf::methodDefiningMethod([lindex $args 0])]} { return -code error "'[lindex $args 0]' is not a method defining method" } elseif {[lindex $args 0] eq "object" && ![info exists ::nsf::methodDefiningMethod([lindex $args 1])]} { return -code error "'[lindex $args 1]' is not a method defining method" } set r [: -system {*}$args] if {$r ne ""} {::nsf::method::property [self] $r call-protected true} return $r } # method modifier "private" :method private {args} { if {![info exists ::nsf::methodDefiningMethod([lindex $args 0])]} { return -code error "'[lindex $args 0]' is not a method defining method" } elseif {[lindex $args 0] eq "object" && ![info exists ::nsf::methodDefiningMethod([lindex $args 1])]} { return -code error "'[lindex $args 1]' is not a method defining method" } set r [: -system {*}$args] if {$r ne ""} {::nsf::method::property [self] $r call-private true} return $r } } # Provide a placeholder for objectparameter during the bootup # process. The real definition is based on slots, which are not # available at this point. Object protected method __object_configureparameter {} {;} ###################################################################### # Define forward methods ###################################################################### # # We could do this simply as # # ::nsf::method::forward Object forward ::nsf::method::forward %self -per-object # ::nsf::method::forward Class forward ::nsf::method::forward %self # # but then, we would loose the option to use compound names # Class public method forward { methodName -default -prefix -frame -onerror -returns -verbose:switch target:optional args } { set pathData [:__resolve_method_path $methodName] set arguments [lrange [::nsf::current args] 1 end] set object [dict get $pathData object] if {[info exists target] && [string range $target 0 0] eq "-"} { error "target '$target' must not start with a dash" } if {[info exists frame] && $frame ni {object default}} { error "value of parameter -frame must be 'object' or 'default'" } if {[info exists returns]} { set nrPreArgs [expr {[llength $arguments]-[llength $args]}] # search for "-returns" in the arguments before $args and remove it set p [lsearch -exact [lrange $arguments 0 $nrPreArgs] -returns] if {$p > -1} {set arguments [lreplace $arguments $p $p+1]} } set r [::nsf::method::forward $object [dict get $pathData methodName] {*}$arguments] ::nsf::method::property $object $r call-protected \ [::nsf::dispatch $object __default_method_call_protection] if {[info exists returns]} {::nsf::method::property $object $r returns $returns} return $r } ###################################################################### # Provide method "alias" # # -frame object|method make only sense for c-defined cmds, ###################################################################### Class public method alias {methodName -returns {-frame default} cmd} { set pathData [:__resolve_method_path $methodName] set object [dict get $pathData object] #puts "class alias $object.[dict get $pathData methodName] $cmd" set r [::nsf::method::alias $object [dict get $pathData methodName] -frame $frame $cmd] ::nsf::method::property $object $r call-protected \ [::nsf::dispatch $object __default_method_call_protection] if {[info exists returns]} {::nsf::method::property $object $r returns $returns} return $r } ###################################################################### # Basic definitions for slots ###################################################################### # # The function isSlotContainer tests, whether the provided object is # a slot container based on the method property slotcontainer, used # internally by the serializer. # proc ::nx::isSlotContainer {object} { return [::nsf::object::property $object slotcontainer] } # # The function nx::internal::setSlotContainerProperties set the method # properties for slot containers # proc ::nx::internal::setSlotContainerProperties {baseObject containerName} { set slotContainer ${baseObject}::$containerName $slotContainer ::nsf::methods::object::requirenamespace ::nsf::method::property $baseObject -per-object $containerName call-protected true ::nsf::method::property $baseObject -per-object $containerName redefine-protected true #puts stderr "::nsf::method::property $baseObject -per-object $containerName slotcontainer true" #::nsf::method::property $baseObject -per-object $containerName slotcontainer true ::nsf::object::property $slotContainer slotcontainer true } # # The function nx::slotObj ensures that the slot container for the # provided baseObject exists. It returns either the name of the # slotContainer (when no slot name was provided) or the fully # qualified name of the slot object. # ::nsf::proc ::nx::slotObj {{-container slot} baseObject name:optional} { # Create slot container object if needed set slotContainer ${baseObject}::$container if {![::nsf::object::exists $slotContainer]} { ::nx::Object ::nsf::methods::class::alloc $slotContainer ::nx::internal::setSlotContainerProperties $baseObject $container if {$container eq "per-object-slot"} { ::nsf::object::property $baseObject hasperobjectslots true } } if {[info exists name]} { return ${slotContainer}::$name } return ${slotContainer} } ###################################################################### # Allocate system slot containers ###################################################################### ::nx::slotObj ::nx::Class ::nx::slotObj ::nx::Object ###################################################################### # Define the EnsembleObject with its base methods ###################################################################### Class create ::nx::EnsembleObject ::nx::EnsembleObject eval { # # The EnsembleObjects are called typically with a "self" bound to # the object, on which they are registered as methods. This way, # only method registered on the object are resolved (ensemble # methods). Only for the methods "unknown" and "defaultmethod", # self is actually the ensemble object. These methods are # maintenance methods. We have to be careful here ... # # a) not to interfere between "maintenance methods" and "ensemble # methods" within the maintenance methods. This is achieved # via explicit dispatch commands in the maintenance methods. # # b) not to overload "maintenance methods" with "ensemble # methods". This is achieved via the object-method-only policy # (we cannot call "subcmd " when "subcmdName" is a # method on EnsembleObject) and via a skip object-methods flag # in nsf when calling e.g. "unknown" (such that a subcmd # "unknown" does not interfere with the method "unknown"). # :protected method init {} { ::nsf::object::property [self] keepcallerself true ::nsf::object::property [self] perobjectdispatch true } :protected method unknown {callInfo args} { set path [lrange $callInfo 1 end-1] set m [lindex $callInfo end] set obj [lindex $callInfo 0] #::nsf::__db_show_stack #puts stderr "CI=<$callInfo> args <$args>" #puts stderr "### [list $obj ::nsf::methods::object::info::lookupmethods -path \"$path *\"]" if {[catch {set valid [$obj ::nsf::methods::object::info::lookupmethods -path "$path *"]} errorMsg]} { set valid "" puts stderr "+++ UNKNOWN raises error $errorMsg" } set ref "\"$m\" of $obj $path" return -code error "unable to dispatch sub-method $ref; valid are: [join [lsort $valid] {, }]" } :protected method defaultmethod {} { if {[catch {set obj [uplevel ::nsf::current]}]} { error "ensemble dispatch called outside of method context" } set path [uplevel {::nsf::current methodpath}] set l [string length $path] set submethods [$obj ::nsf::methods::object::info::lookupmethods -path "$path *"] foreach sm $submethods {set results([lindex [string range $sm $l+1 end] 0]) 1} return -code error "valid submethods of $obj $path: [lsort [array names results]]" } # end of EnsembleObject } ###################################################################### # Now we are able to use ensemble methods in the definition of NX ###################################################################### Object eval { # # Define method defining methods for Object. # # These are: # - "method" # - "alias" # - "forward" :public method "object method" { methodName arguments:parameter,0..* -checkalways:switch -returns body } { set pathData [:__resolve_method_path -per-object $methodName] set object [dict get $pathData object] set regObject [dict get $pathData regObject] # puts "object method $object.[dict get $pathData methodName] [list $arguments] {...}" set r [::nsf::method::create $object \ -checkalways=$checkalways \ {*}[expr {$regObject ne "" ? "-reg-object [list $regObject]" : ""}] \ -per-object \ [dict get $pathData methodName] $arguments $body] if {$r ne ""} { # the method was not deleted ::nsf::method::property $object $r call-protected \ [::nsf::dispatch $object __default_method_call_protection] if {[info exists returns]} {::nsf::method::property $object $r returns $returns} } return $r } :public method "object alias" {methodName -returns {-frame default} cmd} { set pathData [:__resolve_method_path -per-object $methodName] set object [dict get $pathData object] #puts "object alias $object.[dict get $pathData methodName] $cmd" set r [::nsf::method::alias $object -per-object [dict get $pathData methodName] \ -frame $frame $cmd] ::nsf::method::property $object -per-object $r call-protected \ [::nsf::dispatch $object __default_method_call_protection] if {[info exists returns]} {::nsf::method::property $object $r returns $returns} return $r } :public method "object forward" { methodName -default -prefix -frame -onerror -returns -verbose:switch target:optional args } { set arguments [lrange [::nsf::current args] 1 end] set pathData [:__resolve_method_path -per-object $methodName] set object [dict get $pathData object] if {[info exists target] && [string range $target 0 0] eq "-"} { error "target '$target' must not start with a dash" } if {[info exists frame] && $frame ni {object default}} { error "value of parameter '-frame' must be 'object' or 'default'" } if {[info exists returns]} { set nrPreArgs [expr {[llength $arguments]-[llength $args]}] # search for "-returns" in the arguments before $args ... set p [lsearch -exact [lrange $arguments 0 $nrPreArgs] -returns] # ... and remove it if found if {$p > -1} {set arguments [lreplace $arguments $p $p+1]} } set r [::nsf::method::forward $object -per-object \ [dict get $pathData methodName] {*}$arguments] ::nsf::method::property $object -per-object $r call-protected \ [::nsf::dispatch $object __default_method_call_protection] if {[info exists returns]} {::nsf::method::property $object $r returns $returns} return $r } } # # Method for deletion of properties, variables and plain methods # Object eval { :public method "delete object method" {name} { ::nsf::method::delete [self] -per-object $name } :public method "delete object property" {name} { # call explicitly the per-object variant of "info::slotobjects" set slot [: ::nsf::methods::object::info::slotobjects -type ::nx::Slot $name] if {$slot eq ""} { return -code error \ "[self]: cannot delete object-specific property '$name'" } $slot destroy nsf::var::unset -nocomplain [self] $name } :public method "delete object variable" {name} { # First remove the instance variable and complain, if it does # not exist. if {[nsf::var::exists [self] $name]} { nsf::var::unset [self] $name } else { return -code error \ "[self]: object does not have an instance variable '$name'" } # call explicitly the per-object variant of "info::slotobejcts" set slot [: ::nsf::methods::object::info::slotobjects -type ::nx::Slot $name] if {$slot ne ""} { # it is not a slot-less variable $slot destroy } } } Class eval { :public method "delete method" {name} { ::nsf::method::delete [self] $name } :public method "delete property" {name} { set slot [:info slots $name] if {$slot eq ""} { return -code error "[self]: cannot delete property '$name'" } $slot destroy } :public method "delete variable" {name} { set slot [:info slots $name] if {$slot eq ""} { return -code error "[self]: cannot delete variable '$name'" } $slot destroy } } ###################################################################### # Provide method "require" ###################################################################### Object eval { :method "require namespace" {} { ::nsf::directdispatch [::nsf::self] ::nsf::methods::object::requirenamespace } # # method require, base cases # :method "require object method" {methodName} { ::nsf::method::require [::nsf::self] $methodName 1 return [:info lookup method $methodName] } # # method require, public explicitly # :method "require public object method" {methodName} { set result [:require object method $methodName] ::nsf::method::property [self] $result call-protected false return $result } # # method require, protected explicitly # :method "require protected object method" {methodName} { set result [:require object method $methodName] ::nsf::method::property [self] $result call-protected true return $result } # # method require, private explicitly # :method "require private object method" {methodName} { set result [:require object method $methodName] ::nsf::method::property [self] $result call-private true return $result } } nx::Class eval { :method "require method" {methodName} { return [::nsf::method::require [::nsf::self] $methodName 0] } :method "require public method" {methodName} { set result [:require method $methodName] ::nsf::method::property [self] $result call-protected false return $result } :method "require protected method" {methodName} { set result [:require method $methodName] ::nsf::method::property [self] $result call-protected true return $result } :method "require private method" {methodName} { set result [:require method $methodName] ::nsf::method::property [self] $result call-private true return $result } } ###################################################################### # Info definition ###################################################################### # we have to use "eval", since objectParameters are not defined yet Object eval { :alias "info lookup filter" ::nsf::methods::object::info::lookupfilter :alias "info lookup filters" ::nsf::methods::object::info::lookupfilters :alias "info lookup method" ::nsf::methods::object::info::lookupmethod :alias "info lookup methods" ::nsf::methods::object::info::lookupmethods :alias "info lookup mixins" ::nsf::methods::object::info::lookupmixins :method "info lookup slots" {{-type:class ::nx::Slot} -source pattern:optional} { set cmd [list ::nsf::methods::object::info::lookupslots -type $type] if {[info exists source]} {lappend cmd -source $source} if {[info exists pattern]} {lappend cmd $pattern} return [: {*}$cmd] } :method "info lookup parameters" {methodName pattern:optional} { return [::nsf::cmd::info \ parameter \ -context [self] \ [:info lookup method $methodName] \ {*}[expr {[info exists pattern] ? $pattern : ""}] ] } :method "info lookup syntax" {methodName pattern:optional} { return [::nsf::cmd::info \ syntax \ -context [self] \ [:info lookup method $methodName] \ {*}[expr {[info exists pattern] ? $pattern : ""}] ] } :method "info lookup variables" {pattern:optional} { return [: info lookup slots -type ::nx::VariableSlot {*}[current args]] } :alias "info children" ::nsf::methods::object::info::children :alias "info class" ::nsf::methods::object::info::class :alias "info has mixin" ::nsf::methods::object::info::hasmixin :alias "info has namespace" ::nsf::methods::object::info::hasnamespace :alias "info has type" ::nsf::methods::object::info::hastype :alias "info name" ::nsf::methods::object::info::name :alias "info object filters" ::nsf::methods::object::info::filters :alias "info object methods" ::nsf::methods::object::info::methods :alias "info object mixins" ::nsf::methods::object::info::mixins :method "info object slots" {{-type:class ::nx::Slot} pattern:optional} { set method [list ::nsf::methods::object::info::slotobjects -type $type] if {[info exists pattern]} {lappend method $pattern} return [: {*}$method] } :method "info object variables" {pattern:optional} { return [: info object slots -type ::nx::VariableSlot {*}[current args]] } # # Parameter extractors # # :method "info parameter default" {p:parameter varName:optional} { # if {[info exists varName]} { # uplevel [list ::nsf::parameter::info default $p $varName] # } else { # ::nsf::parameter::info default $p # } # } # :method "info parameter name" {p:parameter} {::nsf::parameter::info name $p} # :method "info parameter syntax" {p:parameter} {::nsf::parameter::info syntax $p} # :method "info parameter type" {p:parameter} {::nsf::parameter::info type $p} :alias "info parent" ::nsf::methods::object::info::parent :alias "info precedence" ::nsf::methods::object::info::precedence :alias "info vars" ::nsf::methods::object::info::vars :method "info variable definition" {handle:object,type=::nx::VariableSlot} { return [$handle definition] } :method "info variable name" {handle:object,type=::nx::VariableSlot} { return [$handle cget -name] } :method "info variable parameter" {handle:object,type=::nx::VariableSlot} { return [$handle parameter] } } ###################################################################### # Create the ensemble object for "info" here manually to prevent the # replicated definitions from Object.info in Class.info. # Potentially, some names are overwritten later by Class.info. Note, # that the automatically created name of the ensemble object has to # be the same as defined above. ###################################################################### # # The following test is just for the redefinition case, after a # "package forget". We clear "info method" for ::nx::Object to avoid # confusions in the copy loop below, which uses method "method". # if {[::nsf::directdispatch ::nx::Object::slot::__info ::nsf::methods::object::info::methods "method"] ne ""} { Object method "info method" {} {} } # # There is not need to copy the "info object" ensemble to # ::nx::Class since this is reached via ensemble "next" in # nx::Object. # Class eval { :alias "info lookup" ::nx::Object::slot::__info::lookup :alias "info filters" ::nsf::methods::class::info::filters :alias "info has" ::nx::Object::slot::__info::has :alias "info heritage" ::nsf::methods::class::info::heritage :alias "info instances" ::nsf::methods::class::info::instances :alias "info methods" ::nsf::methods::class::info::methods :alias "info mixins" ::nsf::methods::class::info::mixins :alias "info mixinof" ::nsf::methods::class::info::mixinof :method "info slots" {{-type ::nx::Slot} -closure:switch -source:optional pattern:optional} { set cmd [list ::nsf::methods::class::info::slotobjects -type $type] if {[info exists source]} {lappend cmd -source $source} if {$closure} {lappend cmd -closure} if {[info exists pattern]} {lappend cmd $pattern} return [: {*}$cmd] } :alias "info subclasses" ::nsf::methods::class::info::subclass :alias "info superclasses" ::nsf::methods::class::info::superclass :method "info variables" {pattern:optional} { set cmd {info slots -type ::nx::VariableSlot} if {[info exists pattern]} {lappend cmd $pattern} return [: {*}$cmd] } } ###################################################################### # Define "info info" and "info unknown" ###################################################################### ::nsf::proc ::nx::internal::infoOptions {-asList:switch obj {methods ""}} { # puts stderr "INFO INFO $obj -> '[::nsf::directdispatch $obj ::nsf::methods::object::info::methods -type all]'" foreach name [::nsf::directdispatch $obj ::nsf::methods::object::info::methods] { if {$name in $methods || $name eq "unknown"} continue lappend methods $name } if {$asList} { return $methods } else { return "valid options are: [join [lsort $methods] {, }]" } } Object method "info info" {-asList:switch} { ::nx::internal::infoOptions -asList=$asList ::nx::Object::slot::__info } Class method "info info" {-asList:switch} { ::nx::internal::infoOptions -asList=$asList ::nx::Class::slot::__info [next {info -asList}] } # finally register method for "info method" (otherwise, we cannot use "method" above) Class eval { #:alias "info method" ::nsf::methods::class::info::method :method "info method args" {name} {: ::nsf::methods::class::info::method args $name} :method "info method body" {name} {: ::nsf::methods::class::info::method body $name} :method "info method definition" {name} {: ::nsf::methods::class::info::method definition $name} :method "info method exists" {name} {: ::nsf::methods::class::info::method exists $name} :method "info method handle" {name} {: ::nsf::methods::class::info::method definitionhandle $name} :method "info method registrationhandle" {name} {: ::nsf::methods::class::info::method registrationhandle $name} :method "info method definitionhandle" {name} {: ::nsf::methods::class::info::method definitionhandle $name} :method "info method origin" {name} {: ::nsf::methods::class::info::method origin $name} :method "info method parameters" {name pattern:optional} { set defs [: ::nsf::methods::class::info::method parameter $name] if {[info exists pattern]} {return [::nsf::parameter::filter $defs $pattern]} return $defs } :method "info method syntax" {name} { return [string trimright "/cls/ [namespace tail $name] [: ::nsf::methods::class::info::method syntax $name]" { }] } :method "info method type" {name} {: ::nsf::methods::class::info::method type $name} :method "info method submethods" {name} {: ::nsf::methods::class::info::method submethods $name} :method "info method returns" {name} {: ::nsf::methods::class::info::method returns $name} } Object eval { #:alias "info object method" ::nsf::methods::object::info::method :method "info object method args" {name} {: ::nsf::methods::object::info::method args $name} :method "info object method body" {name} {: ::nsf::methods::object::info::method body $name} :method "info object method definition" {name} {: ::nsf::methods::object::info::method definition $name} :method "info object method exists" {name} {: ::nsf::methods::object::info::method exists $name} :method "info object method handle" {name} {: ::nsf::methods::object::info::method definitionhandle $name} :method "info object method registrationhandle" {name} {: ::nsf::methods::object::info::method registrationhandle $name} :method "info object method definitionhandle" {name} {: ::nsf::methods::object::info::method definitionhandle $name} :method "info object method origin" {name} {: ::nsf::methods::object::info::method origin $name} :method "info object method parameters" {name pattern:optional} { set defs [: ::nsf::methods::object::info::method parameter $name] if {[info exists pattern]} {return [::nsf::parameter::filter $defs $pattern]} return $defs } :method "info object method syntax" {name} { return [string trimright "/obj/ [namespace tail $name] [: ::nsf::methods::object::info::method syntax $name]" { }] } :method "info object method type" {name} {: ::nsf::methods::object::info::method type $name} :method "info object method submethods" {name} {: ::nsf::methods::object::info::method submethods $name} :method "info object method returns" {name} {: ::nsf::methods::object::info::method returns $name} } ###################################################################### # Provide Tk-style methods for configure and cget ###################################################################### Object eval { :public alias cget ::nsf::methods::object::cget :public alias configure ::nsf::methods::object::configure #:public method "info configure" {} {: ::nsf::methods::object::info::objectparameter syntax} } #nsf::method::create ::nx::Class::slot::__info::configure defaultmethod {} { # uplevel {: ::nsf::methods::object::info::objectparameter syntax} #} ###################################################################### # Definition of "abstract method foo ...." # # Deactivated for now. If we like to revive this method, it should # be integrated with the method modifiers and the method "class" # # Object method abstract {methtype -per-object:switch methName arglist} { # if {$methtype ne "method"} { # error "invalid method type '$methtype', must be 'method'" # } # set body " # if {!\[::nsf::current isnextcall\]} { # error \"abstract method $methName $arglist called\" # } else {::nsf::next} # " # if {${per-object}} { # :method -per-object $methName $arglist $body # } else { # :method $methName $arglist $body # } # } ###################################################################### # MetaSlot definitions # # The MetaSlots are used later to create SlotClasses ###################################################################### # # We are in bootstrap code; we cannot use slots/parameter to define # slots, so the code is a little low level. After the definition of # the slots, we can use slot-based code such as "-parameter" or # "objectparameter". # Class create ::nx::MetaSlot ::nsf::relation::set MetaSlot superclass Class MetaSlot object method requireClass {required:class old:class,0..1} { # # Combine two classes and return the more specialized one # if {$old eq "" || $old eq $required} {return $required} if {[$required info superclasses -closure $old] ne ""} { #puts stderr "required $required has $old as superclass => specializing" return $required } elseif {[$required info subclasses -closure $old] ne ""} { #puts stderr "required $required is more general than $old => keep $old" return $old } else { return -code error "required class $required not compatible with $old" } } MetaSlot public object method parseParameterSpec { {-class ""} {-defaultopts ""} spec default:optional } { array set opt $defaultopts set opts "" set colonPos [string first : $spec] if {$colonPos == -1} { set name $spec set parameterOptions "" } else { set parameterOptions [string range $spec [expr {$colonPos+1}] end] set name [string range $spec 0 [expr {$colonPos -1}]] foreach property [split $parameterOptions ,] { if {$property in [list "required" "convert" "substdefault" "noarg" "nodashalnum"]} { if {$property eq "convert" } {set class [:requireClass ::nx::VariableSlot $class]} lappend opts -$property 1 } elseif {$property eq "noconfig"} { set opt(-configurable) 0 ;# TODO } elseif {$property eq "incremental"} { return -code error "parameter option incremental must not be used; use non-positional argument -incremental instead" } elseif {[string match type=* $property]} { set class [:requireClass ::nx::VariableSlot $class] set type [string range $property 5 end] if {![string match ::* $type]} {set type ::$type} } elseif {[string match arg=* $property]} { set argument [string range $property 4 end] lappend opts -arg $argument } elseif {[string match method=* $property]} { lappend opts -methodname [string range $property 7 end] } elseif {$property eq "optional"} { lappend opts -required 0 } elseif {$property in [list "alias" "forward" "cmd" "initcmd"]} { lappend opts -disposition $property set class [:requireClass ::nx::ObjectParameterSlot $class] } elseif {[regexp {([01])[.][.]([1n*])} $property _ minOccurance maxOccurance]} { lappend opts -multiplicity $property } else { set type $property } } } if {[info exists type]} { #if {$type eq "switch"} {error "switch is not allowed as type for object parameter $name"} lappend opts -type $type } lappend opts {*}[array get opt] #puts stderr "[self] *** parseParameterSpec [list $name $parameterOptions $class $opts]" return [list $name $parameterOptions $class $opts] } MetaSlot public object method createFromParameterSpec { target -per-object:switch {-class ""} {-initblock ""} {-private:switch} {-incremental:switch} {-defaultopts ""} spec default:optional } { lassign [:parseParameterSpec -class $class -defaultopts $defaultopts $spec] \ name parameterOptions class opts lappend opts -incremental $incremental if {[info exists default]} { lappend opts -default $default } if {${per-object}} { lappend opts -per-object true set scope object set container per-object-slot } else { set scope class set container slot } if {$private} { regsub -all : __$target _ prefix lappend opts -settername $name -name __private($target,$name) set slotname ${prefix}.$name } else { set slotname $name } if {$class eq ""} { set class ::nx::VariableSlot } else { #puts stderr "*** Class for '$target $name' is $class // [$class info heritage]" } set slotObj [::nx::slotObj -container $container $target $slotname] #puts stderr "*** [list $class create $slotObj] {*}$opts $initblock]" set r [$class create $slotObj {*}$opts $initblock] #puts stderr "*** returned $r" return $r } } namespace eval ::nx { ###################################################################### # Slot definitions ###################################################################### MetaSlot create ::nx::Slot MetaSlot create ::nx::ObjectParameterSlot ::nsf::relation::set ObjectParameterSlot superclass Slot MetaSlot create ::nx::MethodParameterSlot ::nsf::relation::set MethodParameterSlot superclass Slot # Create a slot instance for dispatching method parameter specific # value checkers MethodParameterSlot create ::nx::methodParameterSlot # Define a temporary, low level interface for defining slot # values. Normally, this is done via slot objects, which are defined # later. The proc is removed later in this script. proc createBootstrapVariableSlots {class definitions} { foreach att $definitions { if {[llength $att]>1} {lassign $att att default} set slotObj [::nx::slotObj $class $att] #puts stderr "::nx::BootStrapVariableSlot create $slotObj" ::nx::BootStrapVariableSlot create $slotObj if {[info exists default]} { #puts stderr "::nsf::var::set $slotObj default $default" ::nsf::var::set $slotObj default $default unset default } # # register the standard setter # #::nsf::method::setter $class $att # # make setter protected # #regexp {^([^:]+):} $att . att #::nsf::method::property $class $att call-protected true # # set for every bootstrap property slot the position 0 # ::nsf::var::set $slotObj position 0 ::nsf::var::set $slotObj configurable 1 } #puts stderr "Bootstrap-slot for $class calls parameter::cache::classinvalidate" ::nsf::parameter::cache::classinvalidate $class } ObjectParameterSlot protected method namedParameterSpec {-map-private:switch prefix name options} { # # Build a pos/nonpos parameter specification from name and option list # if {${map-private} && [info exists :accessor] && ${:accessor} eq "private"} { set pName ${:settername} } else { set pName $name } if {[llength $options]>0} { return $prefix${pName}:[join $options ,] } else { return $prefix${pName} } } ###################################################################### # Define slots for slots ###################################################################### # # We would like to have property slots during bootstrap to # configure the slots itself (e.g. a relation slot object). This is # however a chicken/egg problem, so we use a very simple class for # defining slots for slots, called BootStrapVariableSlot. # MetaSlot create ::nx::BootStrapVariableSlot ::nsf::relation::set BootStrapVariableSlot superclass ObjectParameterSlot BootStrapVariableSlot public method getParameterSpec {} { # # Bootstrap version of getParameter spec. Just bare essentials. # if {[info exists :parameterSpec]} { return ${:parameterSpec} } set name [namespace tail [self]] set prefix [expr {[info exists :positional] && ${:positional} ? "" : "-"}] set options [list] if {[info exists :default]} { if {[string match {*\[*\]*} ${:default}]} { append options substdefault } set :parameterSpec [list [list [:namedParameterSpec $prefix $name $options]] ${:default}] } else { set :parameterSpec [list [:namedParameterSpec $prefix $name $options]] } return ${:parameterSpec} } BootStrapVariableSlot protected method init {args} { # # Empty constructor; do nothing, intentionally without "next" # } ###################################################################### # configure nx::Slot ###################################################################### createBootstrapVariableSlots ::nx::Slot { } ###################################################################### # configure nx::ObjectParameterSlot ###################################################################### createBootstrapVariableSlots ::nx::ObjectParameterSlot { {name "[namespace tail [::nsf::self]]"} {domain "[lindex [regexp -inline {^(.*)::(per-object-slot|slot)::[^:]+$} [::nsf::self]] 1]"} {manager "[::nsf::self]"} {per-object false} {methodname} {forwardername} {defaultmethods {}} {accessor public} {incremental:boolean false} {configurable true} {noarg} {nodashalnum} {disposition alias} {required false} {default} {initblock} {substdefault false} {position 0} {positional} {elementtype} {multiplicity 1..1} } # TODO: check, if substdefault/default could work with e.g. alias; otherwise, move substdefault down # # Default unknown handler for all slots # ObjectParameterSlot protected method unknown {method args} { # # Report just application specific methods not starting with "__" # set methods [list] foreach m [::nsf::directdispatch [::nsf::self] \ ::nsf::methods::object::info::lookupmethods -source application] { if {[string match __* $m]} continue lappend methods $m } return -code error "method '$method' unknown for slot [::nsf::self]; valid are: {[lsort $methods]}" } ObjectParameterSlot protected method init {args} { # # Provide a default depending on :name for :methodname. When slot # objects are created, invalidate the object parameters to reflect # the changes # if {${:incremental} && [:info class] eq [current class]} { return -code error "flag incremental must not be used for this slot type" } if {![info exists :methodname]} { set :methodname ${:name} } if {${:per-object}} { ::nsf::parameter::cache::objectinvalidate ${:domain} } else { ::nsf::parameter::cache::classinvalidate ${:domain} } nsf::object::property [self] initialized 1 # # plain object parameter have currently no setter/forwarder # } ObjectParameterSlot public method destroy {} { if {[info exists :domain] && ${:domain} ne ""} { # # When slot objects are destroyed, flush the parameter cache and # delete the accessors # #puts stderr "*** slot destroy of [self], domain ${:domain} per-object ${:per-object}" if {${:per-object}} { ::nsf::parameter::cache::objectinvalidate ${:domain} if {[${:domain} ::nsf::methods::object::info::method exists ${:name}]} { ::nsf::method::delete ${:domain} -per-object ${:name} } } else { ::nsf::parameter::cache::classinvalidate ${:domain} if {[${:domain} ::nsf::methods::class::info::method exists ${:name}]} { ::nsf::method::delete ${:domain} ${:name} } } } ::nsf::next } # # Define a forwarder directing accessor calls to the slot # ObjectParameterSlot protected method createForwarder {name domain} { set dm [${:manager} cget -defaultmethods] ::nsf::method::forward $domain \ -per-object=${:per-object} \ $name \ -prefix "value=" \ -onerror [list ${:manager} onError] \ ${:manager} \ [expr {$dm ne "" ? [list %1 $dm] : "%1"}] %self \ ${:forwardername} } ObjectParameterSlot public method onError {cmd msg} { if {[string match "%1 requires argument*" $msg]} { set template {wrong # args: use \"$cmd [join $methods |]\"} } elseif {[string match "*unknown for slot*" $msg]} { lassign $cmd slot calledMethod obj . regexp {=(.*)$} $calledMethod . calledMethod regexp {::([^:]+)$} $slot . slot set template {submethod $calledMethod undefined for $slot: use \"$obj $slot [join $methods |]\"} } if {[info exists template]} { set methods "" foreach m [lsort [:info lookup methods -callprotection public value=*]] { lappend methods [lindex [split $m =] end] } return -code error [subst $template] } return -code error $msg } ObjectParameterSlot protected method makeForwarder {} { # # Build forwarder from the source object class ($domain) to the slot # to delegate read and update operations # # intended to be called on RelationSlot or VariableSlot # if {![info exists :forwardername]} { set :forwardername ${:methodname} } #puts stderr "makeforwarder --> '${:forwardername}'" if {[info exists :settername]} { set d [nsf::directdispatch ${:domain} \ ::nsf::classes::nx::Object::__resolve_method_path \ {*}[expr {${:per-object} ? "-per-object" : ""}] ${:settername}] :createForwarder [dict get $d methodName] [dict get $d object] } else { :createForwarder ${:name} ${:domain} } } ObjectParameterSlot protected method getParameterOptions { {-withMultiplicity 0} {-forObjectParameter 0} } { # # Obtain a list of parameter options from slot object # set options [list] if {[info exists :elementtype] && ${:elementtype} ne {}} { lappend options ${:elementtype} #puts stderr "+++ [self] added elementtype ${:elementtype}" } if {${:disposition} eq "slotset"} { lappend options slot=[::nsf::self] ${:disposition} if {${:forwardername} ne ${:name}} { lappend options method=${:forwardername} } } else { lappend options ${:disposition} } if {${:name} ne ${:methodname}} { lappend options method=${:methodname} } if {${:required}} { lappend options required } elseif {[info exists :positional] && ${:positional}} { lappend options optional } if {$forObjectParameter} { # # Check, if get or set methods were overloaded # if {[:info lookup method value=set] ni {"" "::nsf::classes::nx::RelationSlot::value=set"}} { # In case the "set" method was provided on the slot, ask nsf to call it directly lappend options slot=[::nsf::self] slotset } elseif {[:info lookup method value=get] ni {"" "::nsf::classes::nx::RelationSlot::value=get"}} { # In case the "get" method was provided on the slot, ask nsf to call it directly lappend options slot=[::nsf::self] } if {[info exists :substdefault] && ${:substdefault}} { lappend options substdefault } } if {[info exists :noarg] && ${:noarg}} {lappend options noarg} if {[info exists :nodashalnum] && ${:nodashalnum}} {lappend options nodashalnum} if {$withMultiplicity && [info exists :multiplicity] && ${:multiplicity} ne "1..1"} { #puts stderr "### [self] added multiplicity ${:multiplicity}" lappend options ${:multiplicity} } return $options } ObjectParameterSlot public method getParameterSpec {} { # # Get a full object parmeter specification from slot object # if {[info exists :parameterSpec]} { } else { set prefix [expr {[info exists :positional] && ${:positional} ? "" : "-"}] set options [:getParameterOptions -withMultiplicity true -forObjectParameter true] if {[info exists :initblock]} { if {[info exists :default]} { if {[llength $options] > 0} { ::nsf::is -complain [join $options ,] ${:default} #puts stderr "::nsf::is -complain [join $options ,] ${:default} ==> OK" } append initblock "\n::nsf::var::set \[::nsf::self\] ${:name} [list ${:default}]\n" #puts stderr ================append-default-to-initblock-old=<${:initblock}> } lappend options initcmd append initblock ${:initblock} set :parameterSpec [list [:namedParameterSpec $prefix ${:name} $options] $initblock] } elseif {[info exists :default]} { # deactivated for now: || [string first {$} ${:default}] > -1 if {[string match {*\[*\]*} ${:default}]} { lappend options substdefault } set :parameterSpec [list [:namedParameterSpec $prefix ${:name} $options] ${:default}] } else { set :parameterSpec [list [:namedParameterSpec $prefix ${:name} $options]] } } #puts stderr ================${:parameterSpec} return ${:parameterSpec} } ObjectParameterSlot public method getPropertyDefinitionOptions {parameterSpec} { #puts "accessor <${:accessor}> configurable ${:configurable} per-object ${:per-object}" set mod [expr {${:per-object} ? "object" : ""}] set opts "" if {${:configurable}} { lappend opts -accessor ${:accessor} if {${:incremental}} {lappend opts -incremental} if {[info exists :default]} { return [list ${:domain} {*}$mod property {*}$opts [list $parameterSpec ${:default}]] } set methodName property } else { lappend opts -accessor ${:accessor} if {${:configurable}} {lappend opts -configurable true} if {[info exists :default]} { return [list ${:domain} {*}$mod variable {*}$opts $parameterSpec ${:default}] } set methodName variable } return [list ${:domain} {*}$mod $methodName {*}$opts $parameterSpec] } ObjectParameterSlot public method definition {} { set options [:getParameterOptions -withMultiplicity true] if {[info exists :positional]} {lappend options positional} #if {!${:configurable}} {lappend options noconfig} return [:getPropertyDefinitionOptions [:namedParameterSpec -map-private "" ${:name} $options]] } ###################################################################### # We have no working objectparameter yet, since it requires a # minimal slot infrastructure to build object parameters from # slots. The above definitions should be sufficient as a basis for # object parameters. We provide the definition here before we refine # the slot definitions. # # Invalidate previously defined object parameter (built with the # empty objectparameter definition. # ::nsf::parameter::cache::classinvalidate MetaSlot ###################################################################### # Define objectparameter method ###################################################################### Object protected method __object_configureparameter {} { set slotObjects [nsf::directdispatch [self] ::nsf::methods::object::info::lookupslots -type ::nx::Slot] return [::nsf::parameter::specs $slotObjects] } Class protected method __class_configureparameter {} { set slotObjects [nsf::directdispatch [self] ::nsf::methods::class::info::slotobjects -closure -type ::nx::Slot] return [::nsf::parameter::specs $slotObjects] } } namespace eval ::nx { ###################################################################### # class nx::RelationSlot ###################################################################### MetaSlot create ::nx::RelationSlot ::nsf::relation::set RelationSlot superclass ObjectParameterSlot createBootstrapVariableSlots ::nx::RelationSlot { {accessor public} {multiplicity 0..n} {settername} } RelationSlot protected method init {} { ::nsf::next if {${:accessor} ne ""} { :makeForwarder } } # # create methods for slot operations set/get/add/clear/delete # ::nsf::method::alias RelationSlot value=set ::nsf::relation::set ::nsf::method::alias RelationSlot value=get ::nsf::relation::get RelationSlot public method value=clear {obj prop} { set result [::nsf::relation::set $obj $prop] ::nsf::relation::set $obj $prop {} return $result } RelationSlot protected method delete_value {obj prop old value} { # # Helper method for the delete operation, deleting a value from a # relation slot list. # if {[string first * $value] > -1 || [string first \[ $value] > -1} { # # Value contains globbing meta characters. # if {[info exists :elementtype] && ${:elementtype} eq "mixinreg" && ![string match ::* $value]} { # # Prefix glob pattern with ::, since all object names have # leading "::" # set value ::$value } return [lsearch -all -not -glob -inline $old $value] } elseif {[info exists :elementtype] && ${:elementtype} eq "mixinreg"} { # # Value contains no globbing meta characters, and elementtype could be # fully qualified # if {[string first :: $value] == -1} { # # Obtain a fully qualified name. # if {![::nsf::object::exists $value]} { return -code error "$value does not appear to be an object" } set value [::nsf::directdispatch $value -frame method ::nsf::self] } } set p [lsearch -exact $old $value] if {$p > -1} { return [lreplace $old $p $p] } else { # # In the resulting list might be guards. If so, do another round # of checking to test the first list element. # set new [list] set found 0 foreach v $old { if {[llength $v]>1 && $value eq [lindex $v 0]} { set found 1 continue } lappend new $v } if {!$found} { return -code error "$value is not a $prop of $obj (valid are: $old)" } return $new } } RelationSlot public method value=add {obj prop value {pos 0}} { set oldSetting [::nsf::relation::get $obj $prop] #puts stderr [list ::nsf::relation::set $obj $prop [linsert $oldSetting $pos $value]] # # Use uplevel to avoid namespace surprises # uplevel [list ::nsf::relation::set $obj $prop [linsert $oldSetting $pos $value]] } RelationSlot public method value=delete {-nocomplain:switch obj prop value} { uplevel [list ::nsf::relation::set $obj $prop \ [:delete_value $obj $prop [::nsf::relation::get $obj $prop] $value]] } ###################################################################### # Register system slots ###################################################################### # # Create relation slots # # on nx::Object for # # object-mixin # object-filter # # and on nx::Class for # # mixin # filter ::nx::RelationSlot create ::nx::Object::slot::object-mixins \ -multiplicity 0..n \ -defaultmethods {} \ -disposition slotset \ -forwardername object-mixin \ -settername "object mixins" \ -elementtype mixinreg ::nx::RelationSlot create ::nx::Object::slot::object-filters \ -multiplicity 0..n \ -defaultmethods {} \ -disposition slotset \ -forwardername object-filter \ -settername "object filters" \ -elementtype filterreg ::nx::RelationSlot create ::nx::Class::slot::mixins \ -multiplicity 0..n \ -defaultmethods {} \ -disposition slotset \ -forwardername "class-mixin" \ -elementtype mixinreg ::nx::RelationSlot create ::nx::Class::slot::filters \ -multiplicity 0..n \ -defaultmethods {} \ -disposition slotset \ -forwardername class-filter \ -elementtype filterreg # # Define "class" as a ObjectParameterSlot defined as alias # ::nx::ObjectParameterSlot create ::nx::Object::slot::class \ -methodname "::nsf::methods::object::class" -elementtype class # # Define "superclass" as a ObjectParameterSlot defined as alias # ::nx::ObjectParameterSlot create ::nx::Class::slot::superclasses \ -methodname "::nsf::methods::class::superclass" \ -elementtype class \ -multiplicity 1..n \ -default ::nx::Object # # Define the initblock as a positional ObjectParameterSlot # ::nx::ObjectParameterSlot create ::nx::Object::slot::__initblock \ -disposition cmd \ -nodashalnum true \ -positional true \ -position 2 # # Make sure the invalidate all ObjectParameterSlots # ::nsf::parameter::cache::classinvalidate ::nx::ObjectParameterSlot # # Define method "guard" and "methods" for object filters # ::nx::Object::slot::object-filters object method value=guard {obj prop filter guard:optional} { if {[info exists guard]} { ::nsf::directdispatch $obj ::nsf::methods::object::filterguard $filter $guard } else { lindex [$obj info object filters -guards $filter] 0 2 } } ::nx::Object::slot::object-filters object method value=methods {obj prop pattern:optional} { if {[info exists pattern]} { $obj info object filters $pattern } else { $obj info object filters } } # # Define method "guard" and "methods" for filters # ::nx::Class::slot::filters object method value=guard {obj prop filter guard:optional} { if {[info exists guard]} { ::nsf::directdispatch $obj ::nsf::methods::class::filterguard $filter $guard } else { lindex [$obj info filters -guards $filter] 0 2 } } ::nx::Class::slot::filters object method value=methods {obj prop pattern:optional} { if {[info exists pattern]} { $obj info filters $pattern } else { $obj info filters } } # # object mixins # ::nx::Object::slot::object-mixins object method value=classes {obj prop pattern:optional} { if {[info exists pattern]} { $obj info object mixins $pattern } else { $obj info object mixins } } ::nx::Object::slot::object-mixins object method value=guard {obj prop mixin guard:optional} { if {[info exists guard]} { ::nsf::directdispatch $obj ::nsf::methods::object::mixinguard $mixin $guard } else { lindex [$obj info object mixins -guards $mixin] 0 2 } } # # mixins # ::nx::Class::slot::mixins object method value=classes {obj prop pattern:optional} { if {[info exists pattern]} { $obj info mixins $pattern } else { $obj info mixins } } ::nx::Class::slot::mixins object method value=guard {obj prop filter guard:optional} { if {[info exists guard]} { ::nsf::directdispatch $obj ::nsf::methods::class::mixinguard $filter $guard } else { lindex [$obj info mixins -guards $mixin] 0 2 } } #::nsf::method::alias ::nx::Class::slot::object-filters guard ::nx::Object::slot::object-filters::guard # # With a special purpose eval, we could avoid the need for # reconfigure for slot changes via eval (two cases in the regression # test). However, most of the eval uses are from various reading # purposes, so maybe this is an overkill. # #::nx::ObjectParameterSlot public method eval {cmd} { # set r [next] # #puts stderr "eval on slot [self] $cmd -> $r" # :reconfigure # return $r #} ###################################################################### # Variable slots ###################################################################### ::nsf::parameter::cache::classinvalidate MetaSlot MetaSlot create ::nx::VariableSlot -superclass ::nx::ObjectParameterSlot createBootstrapVariableSlots ::nx::VariableSlot { {arg} {convert false} {incremental:boolean false} {multiplicity 1..1} {accessor public} {type} {settername} valuecmd defaultcmd valuechangedcmd } ::nx::VariableSlot public method setCheckedInstVar {-nocomplain:switch object value} { if {[::nsf::var::exists $object ${:name}] && !$nocomplain} { return -code error \ "object $object has already an instance variable named '${:name}'" } set options [:getParameterOptions -withMultiplicity true] if {[llength $options]} { ::nsf::is -configure -complain -name ${:name}: [join $options ,] $value } set restore [:removeTraces $object *] ::nsf::var::set $object ${:name} ${:default} if {[info exists restore]} { {*}$restore } } ::nx::VariableSlot protected method setterRedefinedOptions {} { if {[:info lookup method value=set] ne "::nsf::classes::nx::VariableSlot::value=set"} { # In case the "set" method was provided on the slot, ask nsf to call it directly return [list slot=[::nsf::self] slotset] } if {[:info lookup method value=get] ne "::nsf::classes::nx::VariableSlot::value=get"} { # In case the "get" method was provided on the slot, ask nsf to call it directly return [list slot=[::nsf::self]] } } ::nx::VariableSlot protected method getParameterOptions { {-withMultiplicity 0} {-forObjectParameter 0} } { set options "" set slotObject "" if {[info exists :type]} { set type ${:type} if {$type eq "switch" && !$forObjectParameter} {set type boolean} if {$type in {cmd initcmd}} { lappend options $type } elseif {[string match ::* $type]} { lappend options [expr {[::nsf::is metaclass $type] ? "class" : "object"}] type=$type } else { lappend options $type if {$type ni [list "" \ "boolean" "integer" "object" "class" \ "metaclass" "baseclass" "parameter" \ "alnum" "alpha" "ascii" "control" "digit" "double" \ "false" "graph" "lower" "print" "punct" "space" "true" \ "wideinteger" "wordchar" "xdigit" ]} { lappend options slot=[::nsf::self] } } } if {$forObjectParameter} { foreach o [:setterRedefinedOptions] { if {$o ni $options} {lappend options $o} } } if {[:info lookup method initialize] ne "" && $forObjectParameter} { if {"slot=[::nsf::self]" ni $options} {lappend options slot=[::nsf::self]} lappend options slotinitialize } if {[info exists :arg]} {lappend options arg=${:arg}} if {${:required}} { lappend options required } elseif {[info exists :positional] && ${:positional}} { lappend options optional } if {${:convert}} {lappend options convert} if {$withMultiplicity && [info exists :multiplicity] && ${:multiplicity} ne "1..1"} { lappend options ${:multiplicity} } if {$forObjectParameter} { if {[info exists :substdefault] && ${:substdefault}} { lappend options substdefault } if {[info exists :configurable] && !${:configurable}} { lappend options noconfig } } #puts stderr "*** getParameterOptions $withMultiplicity $forObjectParameter [self] returns '$options'" return $options } ::nx::VariableSlot protected method isMultivalued {} { return [string match {*..[n*]} ${:multiplicity}] } # # When there are accessors defined, we use always the forwarders in # NX. XOTcl2 has a detailed optimization. # ::nx::VariableSlot protected method needsForwarder {} { return 1 } ::nx::VariableSlot protected method makeAccessor {} { if {${:accessor} eq "none"} { #puts stderr "*** Do not register forwarder ${:domain} ${:name}" return 0 } if {[:needsForwarder]} { set handle [:makeForwarder] :makeIncrementalOperations } else { set handle [:makeSetter] } if {${:accessor} eq "protected"} { ::nsf::method::property ${:domain} {*}[expr {${:per-object} ? "-per-object" : ""}] \ $handle call-protected true set :configurable 0 } elseif {${:accessor} eq "private"} { ::nsf::method::property ${:domain} {*}[expr {${:per-object} ? "-per-object" : ""}] \ $handle call-private true set :configurable 0 } elseif {${:accessor} ne "public"} { set msg "accessor value '${:accessor}' invalid; might be one of public|protected|private or none" :destroy return -code error $msg } return 1 } ::nx::VariableSlot public method reconfigure {} { #puts stderr "*** Should we reconfigure [self]???" unset -nocomplain :parameterSpec if {${:incremental}} { if {${:accessor} eq "none"} { set :accessor "public" } if {![:isMultivalued]} { set :multiplicity [string range ${:multiplicity} 0 0]..n } } :makeAccessor if {${:per-object} && [info exists :default]} { :setCheckedInstVar -nocomplain=[info exists :nocomplain] ${:domain} ${:default} } if {[::nsf::is class ${:domain}]} { ::nsf::parameter::cache::classinvalidate ${:domain} } } ::nx::VariableSlot public method parameter {} { # This is a shortend "lightweight" version of "getParameterSpec" # returning less (implicit) details. used e.g. by "info variable parameter" set options [:getParameterOptions -withMultiplicity true] set spec [:namedParameterSpec -map-private "" ${:name} $options] if {[info exists :default]} {lappend spec ${:default}} return $spec } ::nx::VariableSlot protected method checkDefault {} { if {![info exists :default] || [string match {*\[*\]*} ${:default}]} {return} set options [:getParameterOptions -withMultiplicity true] if {[llength $options] > 0} { if {[catch {::nsf::is -complain -configure -name ${:name}: [join $options ,] ${:default}} errorMsg]} { #puts stderr "**** destroy [self] - $errorMsg" :destroy return -code error $errorMsg } } } ::nx::VariableSlot protected method init {} { #puts "VariableSlot [self] ${:incremental} && ${:accessor} && ${:multiplicity} incremental ${:incremental}" if {${:incremental}} { if {${:accessor} eq "none"} { set :accessor "public" } if {![:isMultivalued]} { set :multiplicity [string range ${:multiplicity} 0 0]..n } } next :makeAccessor :checkDefault :handleTraces } ::nx::VariableSlot protected method makeSetter {} { set options [:getParameterOptions -withMultiplicity true] set setterParam ${:name} if {[llength $options]>0} {append setterParam :[join $options ,]} ::nsf::method::setter ${:domain} {*}[expr {${:per-object} ? "-per-object" : ""}] $setterParam } ::nx::VariableSlot protected method defineIncrementalOperations {options_single options} { # # Just define these setter methods, when these are not defined # jet. We need the methods as well for e.g. private properties, # where the setting of the property is handled via slot. # if {[:info lookup method value=set] eq "::nsf::classes::nx::VariableSlot::value=set"} { set args [list obj var [:namedParameterSpec {} value $options]] :public object method value=set $args {::nsf::var::set $obj $var $value} } if {[:isMultivalued] && [:info lookup method value=add] eq "::nsf::classes::nx::VariableSlot::value=add"} { lappend options_single slot=[::nsf::self] set args [list obj prop [:namedParameterSpec {} value $options_single] {pos 0}] :public object method value=add $args {::nsf::next} } else { # TODO should we deactivate add/delete? } } ::nx::VariableSlot protected method makeIncrementalOperations {} { set options_single [:getParameterOptions] #if {[llength $options_single] == 0} {} if {![info exists :type]} { # No need to make per-slot methods; the general rules on # nx::VariableSlot are sufficient return } #puts "makeIncrementalOperations -- single $options_single type ${:type}" #if {[info exists :type]} {puts ".... type ${:type}"} set options [:getParameterOptions -withMultiplicity true] lappend options slot=[::nsf::self] :defineIncrementalOperations $options_single $options } ###################################################################### # Handle variable traces ###################################################################### ::nx::VariableSlot protected method removeTraces {object matchOps} { #puts stderr "====removeTraces ${:name} $matchOps" set restore "" set traces [::nsf::directdispatch $object -frame object ::trace info variable ${:name}] foreach trace $traces { lassign $trace ops cmdPrefix if {![string match $matchOps $ops]} continue #puts stderr "====remove trace variable ${:name} $ops $cmdPrefix" ::nsf::directdispatch $object -frame object ::trace remove variable ${:name} $ops $cmdPrefix append restore "[list ::nsf::directdispatch $object -frame object ::trace add variable ${:name} $ops $cmdPrefix]\n" } return $restore } ::nx::VariableSlot protected method handleTraces {} { # essentially like before set __initblock "" set trace {::nsf::directdispatch [::nsf::self] -frame object ::trace} # There be already default values registered on the # class. If so, defaultcmd is ignored. if {[info exists :default]} { if {[info exists :defaultcmd]} { return -code error \ "defaultcmd can't be used together with default value" } if {[info exists :valuecmd]} { return -code error \ "valuecmd can't be used together with default value"} } elseif [info exists :defaultcmd] { if {[info exists :valuecmd]} { return -code error \ "valuecmd can't be used together with defaultcmd" } append __initblock "::nsf::directdispatch [::nsf::self] -frame object :removeTraces \[::nsf::self\] read\n" append __initblock "$trace add variable [list ${:name}] read \ \[list [::nsf::self] __default_from_cmd \[::nsf::self\] [list [set :defaultcmd]]\]\n" } elseif [info exists :valuecmd] { append __initblock "::nsf::directdispatch [::nsf::self] -frame object :removeTraces \[::nsf::self\] read\n" append __initblock "$trace add variable [list ${:name}] read \ \[list [::nsf::self] __value_from_cmd \[::nsf::self\] [list [set :valuecmd]]\]" } if {[info exists :valuechangedcmd]} { append __initblock "::nsf::directdispatch [::nsf::self] -frame object :removeTraces \[::nsf::self\] write\n" append __initblock "$trace add variable [list ${:name}] write \ \[list [::nsf::self] __value_changed_cmd \[::nsf::self\] [list [set :valuechangedcmd]]\]" } if {$__initblock ne ""} { if {${:per-object}} { ${:domain} eval $__initblock } #puts stderr initblock=$__initblock set :initblock $__initblock } } # # Implementation of methods called by the traces # ::nx::VariableSlot method __default_from_cmd {obj cmd var sub op} { #puts "GETVAR [::nsf::current method] obj=$obj cmd=$cmd, var=$var, op=$op" ::nsf::directdispatch $obj -frame object \ ::trace remove variable $var $op [list [::nsf::self] [::nsf::current method] $obj $cmd] ::nsf::var::set $obj $var [$obj eval $cmd] } ::nx::VariableSlot method __value_from_cmd {obj cmd var sub op} { #puts "GETVAR [::nsf::current method] obj=$obj cmd=$cmd, var=$var, op=$op" ::nsf::var::set $obj $var [$obj eval $cmd] } ::nx::VariableSlot method __value_changed_cmd {obj cmd var sub op} { # puts "valuechanged obj=$obj cmd=$cmd, var=$var, op=$op, ...\n$obj exists $var -> [::nsf::var::set $obj $var]" eval $cmd } ###################################################################### # Implementation of (incremental) forwarder operations for # VariableSlots: # - set # - get # - add # - delete ###################################################################### ::nsf::method::alias ::nx::VariableSlot value=get ::nsf::var::get ::nsf::method::alias ::nx::VariableSlot value=set ::nsf::var::set ::nx::VariableSlot public method value=unset {obj prop -nocomplain:switch} { ::nsf::var::unset -nocomplain=$nocomplain $obj $prop } ::nx::VariableSlot public method value=add {obj prop value {pos 0}} { if {![:isMultivalued]} { #puts stderr "... vars [[self] info vars] // [[self] eval {set :multiplicity}]" return -code error "property $prop of [set :domain] ist not multivalued" } if {[::nsf::var::exists $obj $prop]} { ::nsf::var::set $obj $prop [linsert [::nsf::var::set $obj $prop] $pos $value] } else { ::nsf::var::set $obj $prop [list $value] } } ::nx::VariableSlot public method value=delete {-nocomplain:switch obj prop value} { set old [::nsf::var::set $obj $prop] set p [lsearch -glob $old $value] if {$p>-1} {::nsf::var::set $obj $prop [lreplace $old $p $p]} else { return -code error "$value is not a $prop of $obj (valid are: $old)" } } ###################################################################### # Define methods "property" and "variable" ###################################################################### nx::Object method "object variable" { {-accessor "none"} {-incremental:switch} {-class ""} {-configurable:boolean false} {-initblock ""} {-nocomplain:switch} spec:parameter defaultValue:optional } { # # This method creates sometimes a slot, sometimes not # (optimization). We need a slot currently in the following # situations: # - when accessors are needed # (serializer uses slot object to create accessors) # - when initblock is non empty # #puts stderr "Object variable $spec accessor $accessor nocomplain $nocomplain incremental $incremental" # get name and list of parameter options lassign [::nx::MetaSlot parseParameterSpec -class $class $spec] \ name parameterOptions class options array set opts $options if {[info exists opts(-configurable)]} { set configurable $opts(-configurable) } #if {$initblock eq "" && $accessor eq "none" && !$incremental} if {$initblock eq "" && !$configurable && $accessor eq "none" && !$incremental} { # # we can build a slot-less variable # #puts "... slotless variable $spec" set isSwitch [regsub {\mswitch\M} $parameterOptions boolean parameterOptions] if {[info exists defaultValue]} { if {[info exists :$name] && !$nocomplain} { return -code error \ "object [self] has already an instance variable named '$name'" } if {$parameterOptions ne ""} { #puts stderr "*** ::nsf::is $parameterOptions $defaultValue // opts=$options" # we rely here that the nsf::is error message expresses the implementation limits set noptions {} foreach o [split $parameterOptions ,] { if {$o ne "noconfig"} {lappend noptions $o} } set parameterOptions [join $noptions ,] ::nsf::is -complain $parameterOptions $defaultValue } else { set name $spec } set :$name $defaultValue } elseif {$isSwitch} { set :$name 0 } else { return -code error \ "variable definition for '$name' (without value and accessor) is useless" } return } #puts "... slot variable $spec" # # create variable via a slot object # set slot [::nx::MetaSlot createFromParameterSpec [self] \ -per-object \ -class $class \ -initblock $initblock \ -incremental=$incremental \ -private=[expr {$accessor eq "private"}] \ -defaultopts [list -accessor $accessor] \ $spec \ {*}[expr {[info exists defaultValue] ? [list $defaultValue] : ""}]] if {$nocomplain} {$slot eval {set :nocomplain 1}} if {!$configurable} {$slot eval {set :configurable false}} if {[info exists defaultValue]} { # We could consider calling "configure" instead, but that would # not work for true "variable" handlers $slot setCheckedInstVar -nocomplain=$nocomplain [self] $defaultValue #set :__initblock($name) 1 } if {[$slot eval {info exists :settername}]} { set name [$slot cget -settername] } else { set name [$slot cget -name] } return [::nsf::directdispatch [self] ::nsf::methods::object::info::method registrationhandle $name] } Object method "object property" { {-accessor ""} {-configurable:boolean true} {-incremental:switch} {-class ""} {-nocomplain:switch} spec:parameter {initblock ""} } { if {${accessor} eq ""} { set accessor [::nsf::dispatch [self] __default_accessor] #puts stderr "OBJECT [self] got default accessor ${accessor}" } set r [[self] object variable \ -accessor $accessor \ -incremental=$incremental \ -class $class \ -initblock $initblock \ -configurable $configurable \ -nocomplain=$nocomplain \ {*}$spec] return $r } nx::Class method variable { {-accessor "none"} {-incremental:switch} {-class ""} {-configurable:boolean false} {-initblock ""} spec:parameter defaultValue:optional } { set slot [::nx::MetaSlot createFromParameterSpec [::nsf::self] \ -class $class \ -initblock $initblock \ -incremental=$incremental \ -private=[expr {$accessor eq "private"}] \ -defaultopts [list -accessor $accessor -configurable $configurable] \ $spec \ {*}[expr {[info exists defaultValue] ? [list $defaultValue] : ""}]] if {[$slot eval {info exists :settername}]} { set name [$slot cget -settername] } else { set name [$slot cget -name] } #puts stderr handle=[::nsf::directdispatch [self] ::nsf::methods::class::info::method registrationhandle $name] return [::nsf::directdispatch [self] ::nsf::methods::class::info::method registrationhandle $name] } nx::Class method property { {-accessor ""} {-configurable:boolean true} {-incremental:switch} {-class ""} spec:parameter {initblock ""} } { if {${accessor} eq ""} { set accessor [::nsf::dispatch [self] __default_accessor] #puts stderr "CLASS [self] got default accessor ${accessor}" } set r [[self] ::nsf::classes::nx::Class::variable \ -accessor $accessor \ -incremental=$incremental \ -class $class \ -configurable $configurable \ -initblock $initblock \ {*}$spec] return $r } ###################################################################### # Define method "properties" for convenience to define multiple # properties based on a list of parameter specifications. ###################################################################### # #proc ::nx::internal::addProperties {arglist} { # foreach arg $arglist {:property $arg} #} #::nx::ObjectParameterSlot create ::nx::Object::slot::properties \ # -methodname "::nx::internal::addProperties" ###################################################################### # Minimal definition of a value checker that permits every value # without warnings. The primary purpose of this value checker is to # provide a means to specify that the value can have every possible # content and not to produce a warning when it might look like a # non-positional parameter. ###################################################################### ::nx::Slot method type=any {name value} { } ::nsf::method::property ::nx::Slot type=any call-protected true ###################################################################### # Now the slots are defined; now we can defines the Objects or # classes with parameters more easily than above. ###################################################################### # remove helper proc rename ::nx::createBootstrapVariableSlots "" ###################################################################### # Define a scoped "new" method, which is similar to plain new, but # uses the current namespace by default as root of the object name. ###################################################################### Class create ::nx::NsScopedNew { :public method new {-childof args} { if {![info exists childof]} { # # Obtain the namespace from plain uplevel to honor the # namespace provided by apply # set childof [uplevel {namespace current}] } # # Use the uplevel method to assure that e.g. "... new -volatile ..." # has the right scope # :uplevel [list [self] ::nsf::methods::class::new -childof $childof {*}$args] } } ###################################################################### # The method 'contains' changes the namespace in which objects with # relative names are created. Therefore, 'contains' provides a # friendly notation for creating nested object # structures. Optionally, creating new objects in the specified # scope can be turned off. ###################################################################### Object public method contains { {-withnew:boolean true} -object {-class:class ::nx::Object} cmds } { if {![info exists object]} {set object [::nsf::self]} if {![::nsf::object::exists $object]} {$class create $object} # This method is reused in XOTcl which has e.g. no "require"; # therefore use nsf primitiva. ::nsf::directdispatch $object ::nsf::methods::object::requirenamespace if {$withnew} { # # When $withnew is requested we replace the default new method # with a version using the current namespace as root. Earlier # implementations used a mixin on nx::Class and xotcl::Class, # but frequent mixin operations on the most general meta-class # are expensive when there are many classes defined # (e.g. several ten thousands), since the mixin operation # invalidates the mixins for all instances of the meta-class # (i.e. for all classes) # set infoMethod "::nsf::methods::class::info::method" set plainNew "::nsf::methods::class::new" set mappedNew [::nx::NsScopedNew $infoMethod definitionhandle new] set nxMapNew [expr {[::nx::Class $infoMethod origin new] eq $plainNew}] if {$nxMapNew} {::nsf::method::alias ::nx::Class new $mappedNew} if {[::nsf::is class ::xotcl::Class]} { set xotclMapNew [expr {[::xotcl::Class $infoMethod origin new] eq $plainNew}] if {$xotclMapNew} {::nsf::method::alias ::xotcl::Class new $mappedNew } } # # Evaluate the command under catch to ensure reverse mapping # of "new" # set errorOccured [catch [list ::apply [list {} $cmds $object]] errorMsg] # # Remove the mapped "new" method, if it was added above # if {$nxMapNew} {::nsf::method::alias ::nx::Class new $plainNew} if {[::nsf::is class ::xotcl::Class]} { if {$xotclMapNew} {::nsf::method::alias ::xotcl::Class new $plainNew} } if {$errorOccured} {return -code error $errorMsg} } else { ::apply [list {} $cmds $object] } } ###################################################################### # copy/move implementation ###################################################################### Class create ::nx::CopyHandler { :property {targetList ""} :property {dest ""} :property objLength :method makeTargetList {t} { if {[::nsf::is object,type=::nx::EnsembleObject $t]} { # # we do not copy ensemble objects, since method # introspection/recreation will care about these # return } lappend :targetList $t #puts stderr "COPY makeTargetList $t targetList '${:targetList}'" # if it is an object without namespace, it is a leaf if {[::nsf::object::exists $t]} { if {[::nsf::directdispatch $t ::nsf::methods::object::info::hasnamespace]} { # make target list from all children set children [$t info children] } else { # ok, no namespace -> no more children return } } # now append all namespaces that are in the obj, but that # are not objects foreach c [namespace children $t] { if {![::nsf::object::exists $c]} { lappend children [namespace children $t] } } # a namespace or an obj with namespace may have children # itself foreach c $children { :makeTargetList $c } } # construct destination obj name from old qualified ns name :method getDest {origin} { if {${:dest} eq ""} { return "" } else { set tail [string range $origin [set :objLength] end] return ::[string trimleft [set :dest]$tail :] } } :method copyTargets {} { #puts stderr "COPY will copy targetList = [set :targetList]" set objs {} array set cmdMap {alias alias forward forward method create setter setter} foreach origin [set :targetList] { set dest [:getDest $origin] if {[::nsf::object::exists $origin]} { if {$dest eq ""} { #set obj [[$origin info class] new -noinit] set obj [::nsf::object::alloc [$origin info class] ""] #nsf::object::property $obj initialized 1 set dest [set :dest $obj] } else { # # Slot container are handled separately, since # ::nx::slotObj does already the right thing. We have just # to copy the variables (XOTcl keeps the parameter # definitions there). # if {[::nsf::object::property $origin slotcontainer]} { ::nx::slotObj -container [namespace tail $origin] \ [namespace qualifiers $dest] ::nsf::nscopyvars $origin $dest continue } else { # # create an object without calling init # #set obj [[$origin info class] create $dest -noinit] set obj [::nsf::object::alloc [$origin info class] $dest] #nsf::object::property $obj initialized 1 #puts stderr "COPY obj=<$obj>" } } # copy class information if {[::nsf::is class $origin]} { # obj is a class, copy class specific information ::nsf::relation::set $obj superclass [$origin ::nsf::methods::class::info::superclass] ::nsf::method::assertion $obj class-invar [::nsf::method::assertion $origin class-invar] ::nsf::relation::set $obj class-filter [::nsf::relation::get $origin class-filter] ::nsf::relation::set $obj class-mixin [::nsf::relation::get $origin class-mixin] ::nsf::nscopyvars ::nsf::classes$origin ::nsf::classes$dest foreach m [$origin ::nsf::methods::class::info::methods -path -callprotection all] { set rest [lassign [$origin ::nsf::methods::class::info::method definition $m] . protection what .] # remove -returns from reported definitions set p [lsearch -exact $rest -returns]; if {$p > -1} {set rest [lreplace $rest $p $p+1]} set pathData [$obj eval [list :__resolve_method_path $m]] set object [dict get $pathData object] set r [::nsf::method::$cmdMap($what) $object [dict get $pathData methodName] {*}$rest] ::nsf::method::property $object $r returns [$origin ::nsf::methods::class::info::method returns $m] ::nsf::method::property $object $r call-protected [::nsf::method::property $origin $m call-protected] ::nsf::method::property $object $r call-private [::nsf::method::property $origin $m call-private] } } # copy object -> might be a class obj ::nsf::object::property $obj keepcallerself [::nsf::object::property $origin keepcallerself] ::nsf::object::property $obj perobjectdispatch [::nsf::object::property $origin perobjectdispatch] ::nsf::object::property $obj hasperobjectslots [::nsf::object::property $origin hasperobjectslots] ::nsf::method::assertion $obj check [::nsf::method::assertion $origin check] ::nsf::method::assertion $obj object-invar [::nsf::method::assertion $origin object-invar] ::nsf::relation::set $obj object-filter [::nsf::relation::get $origin object-filter] ::nsf::relation::set $obj object-mixin [::nsf::relation::get $origin object-mixin] # reused in XOTcl, no "require namespace" there, so use nsf primitiva if {[::nsf::directdispatch $origin ::nsf::methods::object::info::hasnamespace]} { ::nsf::directdispatch $obj ::nsf::methods::object::requirenamespace } } else { namespace eval $dest {} } lappend objs $obj ::nsf::nscopyvars $origin $dest foreach m [$origin ::nsf::methods::object::info::methods -path -callprotection all] { set rest [lassign [$origin ::nsf::methods::object::info::method definition $m] . protection . what .] # remove -returns from reported definitions set p [lsearch -exact $rest -returns]; if {$p > -1} {set rest [lreplace $rest $p $p+1]} set pathData [$obj eval [list :__resolve_method_path -per-object $m]] set object [dict get $pathData object] set r [::nsf::method::$cmdMap($what) $object -per-object \ [dict get $pathData methodName] {*}$rest] ::nsf::method::property $object -per-object $r \ returns \ [$origin ::nsf::methods::object::info::method returns $m] ::nsf::method::property $object -per-object $r \ call-protected \ [::nsf::method::property $origin -per-object $m call-protected] ::nsf::method::property $object -per-object $r \ call-private \ [::nsf::method::property $origin -per-object $m call-private] } # # transfer the traces # foreach var [$origin info vars] { set cmds [::nsf::directdispatch $origin -frame object ::trace info variable $var] #puts stderr "COPY $var <$cmds>" if {$cmds ne ""} { foreach cmd $cmds { lassign $cmd op def #$origin trace remove variable $var $op $def set domain [lindex $def 0] if {$domain eq $origin} { set def [concat $dest [lrange $def 1 end]] } #puts stderr "COPY $var domain $domain [::nsf::object::exists $domain] && [$domain info has type ::nx::Slot]" #if {[::nsf::object::exists $domain] && [$domain info has type ::nx::Slot]} { # slot traces are handled already by the slot mechanism #continue #} # # handle the most common cases to replace $origin by $dest in trace command # if {[lindex $def 2] eq $origin} { set def [lreplace $def 2 2 $dest] } elseif {[lindex $def 0] eq $origin} { set def [lreplace $def 0 0 $dest] } ::nsf::directdispatch $dest -frame object ::trace add variable $var $op $def } } } } # # alter 'domain' and 'manager' in slot objects # foreach origin [set :targetList] { set dest [:getDest $origin] set slots [list] # # get class specific slots # if {[::nsf::is class $origin]} { set slots [$origin ::nsf::methods::class::info::slotobjects -type ::nx::Slot] } # # append object specific slots # foreach slot [$origin ::nsf::methods::object::info::slotobjects -type ::nx::Slot] { lappend slots $slot } #puts stderr "replacing domain and manager from <$origin> to <$dest> in slots <$slots>" foreach oldslot $slots { set container [expr {[$oldslot cget -per-object] ? "per-object-slot" : "slot"}] set newslot [::nx::slotObj -container $container $dest [namespace tail $oldslot]] if {[$oldslot cget -domain] eq $origin} {$newslot configure -domain $dest} if {[$oldslot cget -manager] eq $oldslot} {$newslot configure -manager $newslot} $newslot eval :init } } return [lindex $objs 0] } :public method copy {obj {dest ""}} { #puts stderr "[::nsf::self] copy <$obj> <$dest>" set :objLength [string length $obj] set :dest $dest :makeTargetList $obj :copyTargets } } Object public method copy {{newName ""}} { if {[string trimleft $newName :] ne [string trimleft [::nsf::self] :]} { set h [CopyHandler new] set r [$h copy [::nsf::self] $newName] $h destroy return $r } } Object public method move {newName} { if {[string trimleft $newName :] ne [string trimleft [::nsf::self] :]} { if {$newName ne ""} { :copy $newName } ### let all subclasses get the copied class as superclass if {[::nsf::is class [::nsf::self]] && $newName ne ""} { foreach subclass [: ::nsf::methods::class::info::subclass] { set scl [$subclass ::nsf::methods::class::info::superclass] if {[set index [lsearch -exact $scl [::nsf::self]]] != -1} { set scl [lreplace $scl $index $index $newName] ::nsf::relation::set $subclass superclass $scl } } } :destroy } } ###################################################################### # Methods of meta-classes are methods intended for classes. Make # sure, these methods are only applied on classes. ###################################################################### foreach m [Class info methods] { ::nsf::method::property Class $m class-only true } if {[info exists m]} {unset m} ###################################################################### # some utilities ###################################################################### # # Provide mechanisms to configure nx # ::nx::Object create ::nx::configure { # # Set the default method protection for nx methods. This # protection level is used per default for all method definitions # of scripted methods, aliases and forwarders without explicit # protection specified. # :object method defaultMethodCallProtection {value:boolean,optional} { if {[info exists value]} { ::nsf::method::create Object __default_method_call_protection args [list return $value] ::nsf::method::property Object __default_method_call_protection call-protected true } return [::nsf::dispatch [::nx::self] __default_method_call_protection] } # # Set the default method accessor handling nx properties. The configured # value is used for creating accessors for properties in nx. # :object method defaultAccessor {value:optional} { if {[info exists value]} { if {$value ni {"public" "protected" "private" "none"}} { return -code error {defaultAccessor must be "public", "protected", "private" or "none"} } ::nsf::method::create Object __default_accessor args [list return $value] ::nsf::method::property Object __default_accessor call-protected true } return [::nsf::dispatch [::nx::self] __default_accessor] } } # # Make the default protected methods # ::nx::configure defaultMethodCallProtection true ::nx::configure defaultAccessor none # # Provide an ensemble-like interface to the ::nsf primitiva to # access variables. Note that aliasing in the next scripting # framework is faster than namespace-ensembles. # Object create ::nx::var { :public object alias exists ::nsf::var::exists :public object alias get ::nsf::var::get :public object alias import ::nsf::var::import :public object alias set ::nsf::var::set } #interp alias {} ::nx::self {} ::nsf::self set value "add /class/|classes ?/pattern/?|clear|delete /class/|get|guard /class/ /?expr?/|set /class .../" set "::nsf::parameter::syntax(::nx::Object::slot::__object::object mixins)" $value set "::nsf::parameter::syntax(::nsf::classes::nx::Class::mixins)" $value # set "::nsf::parameter::syntax(::nsf::classes::nx::Class::superclasses)" $value set "::nsf::parameter::syntax(::nsf::classes::nx::Object::class)" "?/className/?" set value "add /filter/|clear|delete /filter/|get|guard /filter/ ?/expr/?|methods ?/pattern/?|set /filter .../" set "::nsf::parameter::syntax(::nx::Object::slot::__object::object filters)" $value set "::nsf::parameter::syntax(::nsf::classes::nx::Class::filters)" $value set "::nsf::parameter::syntax(::nsf::classes::nx::Object::eval)" "/arg/ ?/arg/ ...?" unset value ::nsf::configure debug 1 } namespace eval ::nx { ###################################################################### # Define exported Tcl commands ###################################################################### # export the main commands of ::nx namespace export Object Class next self current set ::nx::confdir ~/.nx set ::nx::logdir $::nx::confdir/log unset ::nsf::bootstrap } if {[info command ::lmap] eq ""} { # provide a simple forward compatible version of Tcl 8.6's lmap proc lmap {_var list body} { upvar 1 $_var var set res {} foreach var $list {lappend res [uplevel 1 $body]} return $res } } # # When debug is not deactivated, tell the developer, what happened # if {[::nsf::configure debug] > 1} { foreach ns {::nsf ::nx} { puts "vars of $ns: [info vars ${ns}::*]" puts stderr "$ns exports: [namespace eval $ns {lsort [namespace export]}]" } puts stderr "======= nx loaded" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/nx/pkgIndex.tcl000066400000000000000000000003721242365656200154410ustar00rootroot00000000000000package ifneeded nx 2.0.0 [list source [file join $dir nx.tcl]] package ifneeded nx::class-method 1.0 [list source [file join $dir class-method.tcl]] package ifneeded nx::plain-object-method 1.0 [list source [file join $dir plain-object-method.tcl]] library/nx/plain-object-method.tcl000066400000000000000000000067111242365656200175200ustar00rootroot00000000000000package provide nx::plain-object-method 1.0 # # Provide a convenience layer to define/introspect object specific # methods without having to use the "object" modifier. By using this # package, one can use instead of # # nx::Object create o { # :public object method foo args {....} # :object property p:integer # :object mixins add M # #... # puts [:info object methods] # } # # simply # # package require nx::plain-object-method # # nx::Object create o { # :public method foo args {....} # :property p:integer # :mixins add M # #... # puts [:info methods] # } # # Note that for object specific methods of classes, one has still to # use "object method" etc. (see also package nx::plass-method). # namespace eval ::nx { # # Define a method to allow configuration for tracing of the # convenience methods. Use # # nx::configure plain-object-method-warning on|off # # for activation/deactivation of tracing. This might be # useful for porting legacy NX programs or for testing # default-configuration compliance. # nx::configure public object method plain-object-method-warning {onoff:boolean,optional} { if {[info exists onoff]} { set :plain-object-method-warning $onoff } else { if {[info exists :plain-object-method-warning]} { if {${:plain-object-method-warning}} { uplevel {::nsf::log warn "plain object method: [self] [current method] [current args]"} } } } } nx::Object eval { # # Definitions redirected to "object" # foreach m { alias filters forward method mixins property variable } { :public method $m {args} { nx::configure plain-object-method-warning :object [current method] {*}[current args] } } # # info subcommands # foreach m { method methods slots variables filters mixins } { :public method "info $m" {args} [subst -nocommands { nx::configure plain-object-method-warning :info object $m {*}[current args] }] } # # deletions for object # foreach m { "property" "variable" "method" } { nx::Object public method "delete $m" {args} { nx::configure plain-object-method-warning :delete object [current method] {*}[current args] } } } Object eval { # # method require, base cases # :method "require method" {methodName} { nx::configure plain-object-method-warning ::nsf::method::require [::nsf::self] $methodName 1 return [:info lookup method $methodName] } # # method require, public explicitly # :method "require public method" {methodName} { nx::configure plain-object-method-warning set result [:require object method $methodName] ::nsf::method::property [self] $result call-protected false return $result } # # method require, protected explicitly # :method "require protected method" {methodName} { nx::configure plain-object-method-warning set result [:require object method $methodName] ::nsf::method::property [self] $result call-protected true return $result } # # method require, private explicitly # :method "require private method" {methodName} { set result [:require object method $methodName] ::nsf::method::property [self] $result call-private true return $result } } } library/pkgIndex.tcl000066400000000000000000000003671242365656200150200ustar00rootroot00000000000000set __dir__ $dir foreach index [concat \ [glob -nocomplain [file join $dir * pkgIndex.tcl]] \ [glob -nocomplain [file join $dir * * pkgIndex.tcl]]] { set dir [file dirname $index] source $index } set dir $__dir__ unset __dir__ library/serialize/000077500000000000000000000000001242365656200145245ustar00rootroot00000000000000library/serialize/COPYRIGHT000066400000000000000000000050371242365656200160240ustar00rootroot00000000000000/* * Next Scripting Framework * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * This software is based upon MIT Object Tcl by David Wetherall and * Christopher J. Lindblad, that contains the following copyright * message: * * "Copyright 1993 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty." */ library/serialize/pkgIndex.tcl000066400000000000000000000001221242365656200167740ustar00rootroot00000000000000package ifneeded nx::serializer 2.0 [list source [file join $dir serializer.tcl]] library/serialize/serializer.tcl000066400000000000000000001046671242365656200174170ustar00rootroot00000000000000package require nx # TODO: should go away #package require nx::plain-object-method package require XOTcl 2.0 package provide nx::serializer 2.0 # For the time being, we require classical XOTcl. # TODO: separate into two packages (i.e. make one XOTcl specific # serializer package, and (a) load this package on a load of this # package (when ::xotcl::Object is defined), and (b) load it from # "xotcl*.tcl", when the serializer is alreaded loaded (e.g. via nx). namespace eval ::nx::serializer { namespace eval ::xotcl {} ;# just to make mk_pkgIndex happy namespace import -force ::xotcl::* ;# just needed for the time being for @ namespace import -force ::nx::* @ @File { description { This package provides the class Serializer, which can be used to generate a snapshot of the current state of the workspace in the form of XOTcl source code. } authors { Gustaf Neumann, Gustaf.Neumann@wu-wien.ac.at } } @ Serializer proc all { ?-ignoreVarsRE RE? "provide regular expression; matching vars are ignored" ?-ignore obj1 obj2 ...? "provide a list of objects to be omitted"} { Description { Serialize all objects and classes that are currently defined (except the specified omissions and the current Serializer object).

Examples:<@br> <@pre class='code'>Serializer all -ignoreVarsRE {::b$} Do not serialize any instance variable named b (of any object).

<@pre class='code'>Serializer all -ignoreVarsRE {^::o1::.*text.*$|^::o2::x$} Do not serialize any variable of c1 whose name contains the string "text" and do not serialze the variable x of o2.

<@pre class='code'>Serializer all -ignore obj1 obj2 ... do not serizalze the specified objects } return "script" } @ Serializer proc deepSerialize { ?-ignoreVarsRE RE? "provide regular expression; matching vars are ignored" ?-ignore obj1 obj2 ...? "provide a list of objects to be omitted" ?-map list? "translate object names in serialized code" objs "Objects to be serialized" } { Description { Serialize object with all child objects (deep operation) except the specified omissions. For the description of <@tt>ignore and <@tt>ignoreVarsRE see <@tt>Serizalizer all. <@tt>map can be used in addition to provide pairs of old-string and new-string (like in the tcl command <@tt>string map). This option can be used to regenerate the serialized object under a different object or under an different name, or to translate relative object names in the serialized code.

Examples: <@pre class='code'>Serializer deepSerialize -map {::a::b ::x::y} ::a::b::c Serialize the object <@tt>c which is a child of <@tt>a::b; the object will be reinitialized as object <@tt>::x::y::c, all references <@tt>::a::b will be replaced by <@tt>::x::y.

<@pre class='code'>Serializer deepSerialize -map {::a::b [self]} ::a::b::c The serizalized object can be reinstantiated under some current object, under which the script is evaluated.

<@pre class='code'>Serializer deepSerialize -map {::a::b::c ${var} ::a::b::c} The serizalized object will be reinstantiated under a name specified by the variable <@tt>var<@tt> in the recreation context. } return "script" } @ Serializer proc methodSerialize { object "object or class" method "name of method" prefix "either empty or 'inst' (latter for instprocs)" } { Description { Serialize the specified method. In order to serialize an instproc, <@tt>prefix should be 'inst'; to serialze procs, it should be empty.

Examples: <@pre class='code'>Serializer methodSerialize Serializer deepSerialize "" This command serializes the proc <@tt>deepSerialize of the Class <@tt>Serializer.

<@pre class='code'>Serializer methodSerialize Serializer serialize inst This command serializes the instproc <@tt>serialize of the Class <@tt>Serializer.

} return {Script, which can be used to recreate the specified method} } @ Serializer proc exportMethods { list "list of methods of the form 'object proc|instproc methodname'" } { Description { This method can be used to specify methods that should be exported in every <@tt>Serializer all<@/tt>. The rationale behind this is that the serializer does not serialize objects from the namespaces of the basic object systems, which are used for the object system internals and volatile objects. TODO It is however often useful to define methods on ::xotcl::Class or ::xotcl::Objects, which should be exported. One can export procs, instprocs, forward and instforward

Example: <@pre class='code'> Serializer exportMethods { ::xotcl::Object instproc __split_arguments ::xotcl::Object instproc __make_doc ::xotcl::Object instproc ad_proc ::xotcl::Class instproc ad_instproc ::xotcl::Object forward expr }<@/pre> } } @ Serializer instproc serialize {entity "Object or Class"} { Description { Serialize the specified object or class. } return {Object or Class with all currently defined methods, variables, invariants, filters and mixins} } ########################################################################### # Serializer Class, independent from Object System ########################################################################### Class create Serializer { :property -accessor public ignoreVarsRE :public method ignore args { # Ignore the objects passed via args. # :skip is used for filtering only in the topological sort. foreach element $args { foreach o [Serializer allChildren $element] { set :skip($o) 1 } } } :public method objmap {map} { array set :objmap $map } :method init {} { # Never serialize the (volatile) serializer object :ignore [::nsf::current object] } :method warn msg { if {[info command ns_log] ne ""} { ns_log Warning "serializer: $msg" } else { puts stderr "Warning: serializer: $msg" } } :public method addPostCmd {cmd} { if {$cmd ne ""} {append :post_cmds $cmd "\n"} } :public method setObjectSystemSerializer {o serializer} { #puts stderr "set :serializer($o) $serializer" set :serializer($o) $serializer } :public method isExportedObject {o} { # Check, whether o is exported. For exported objects. # we export the object tree. set oo $o while {1} { if {[::nsf::var::exists [::nsf::current class] exportObjects($o)]} { return 1 } # we do this for object trees without object-less namespaces if {![::nsf::object::exists $o]} { return 0 } set o [::nsf::dispatch $o ::nsf::methods::object::info::parent] } } :public method getTargetName {sourceName} { # TODO: make more efficent; set targetName $sourceName if {[array exists :objmap]} { foreach {source target} [array get :objmap] { #puts "[list regsub ^$source $targetName $target targetName]" regsub ^$source $targetName $target targetName } } if {![string match ::* $targetName]} { set targetName ::$targetName } #puts stderr "targetName of <$sourceName> = <$targetName>" return $targetName } :method topoSort {set all} { if {[array exists :s]} {array unset :s} if {[array exists :level]} {array unset :level} # TODO generalize? set ns_excluded(::ns) 1 foreach c $set { set ns [namespace qualifiers $c] if {!$all && [info exists ns_excluded($ns)] && ![:isExportedObject $c]} continue if {[info exists :skip($c)]} continue set :s($c) 1 } set stratum 0 while {1} { set set [array names :s] if {[llength $set] == 0} break incr stratum # :warn "$stratum set=$set" set :level($stratum) {} foreach c $set { set oss [set :serializer($c)] if {[$oss needsNothing $c [::nsf::current object]]} { lappend :level($stratum) $c } else { #puts stderr "$c needs something from $set" } } if {[set :level($stratum)] eq ""} { set :level($stratum) $set :warn "Cyclic dependency in $set" } foreach i [set :level($stratum)] {unset :s($i)} } } :public method needsOneOf list { foreach e $list {if {[info exists :s($e)]} {return 1}} return 0 } :public method serialize-objects {list all} { set :post_cmds "" :topoSort $list $all #foreach i [lsort [array names :level]] { :warn "$i: [set :level($i)]"} set result "" foreach l [lsort -integer [array names :level]] { foreach i [set :level($l)] { #:warn "serialize $i" #append result "# Stratum $l\n" set oss [set :serializer($i)] append result [$oss serialize $i [::nsf::current object]] \n } } foreach e $list { set namespace($e) 1 set namespace([namespace qualifiers $e]) 1 } # Handling of variable traces: traces might require a # different topological sort, which is hard to handle. # Similar as with filters, we deactivate the variable # traces during initialization. This happens by # (1) replacing the next's trace method by a no-op # (2) collecting variable traces through collect-var-traces # (3) re-activating the traces after variable initialization set exports "" set pre_cmds "" # delete ::xotcl from the namespace list, if it exists... #catch {unset namespace(::xotcl)} catch {unset namespace(::ns)} foreach ns [array name namespace] { if {![namespace exists $ns]} continue if {![::nsf::object::exists $ns]} { append pre_cmds "namespace eval $ns {}\n" } elseif {$ns ne [namespace origin $ns] } { append pre_cmds "namespace eval $ns {}\n" } set exp [namespace eval $ns {namespace export}] if {$exp ne ""} { append exports "namespace eval $ns {namespace export $exp}" \n } } return $pre_cmds$result${:post_cmds}$exports } :public method deepSerialize {o} { # assumes $o to be fully qualified set instances [Serializer allChildren $o] foreach oss [ObjectSystemSerializer info instances] { $oss registerSerializer [::nsf::current object] $instances } :serialize-objects $instances 1 } ############################### # class object specfic methods ############################### :public object method allChildren o { # return o and all its children fully qualified set set [::nsf::directdispatch $o -frame method ::nsf::current] foreach c [$o info children] { lappend set {*}[:allChildren $c] } return $set } :public object method exportMethods list { foreach {o p m} $list {set :exportMethods([list $o $p $m]) 1} } :public object method exportObjects list { foreach o $list {set :exportObjects($o) 1} } :public object method exportedMethods {} {array names :exportMethods} :public object method exportedObjects {} {array names :exportObjects} :public object method resetPattern {} {array unset :ignorePattern} :public object method addPattern {p} {set :ignorePattern($p) 1} :object method checkExportedMethods {} { foreach k [array names :exportMethods] { lassign $k o p m set ok 0 foreach p [array names :ignorePattern] { if {[string match $p $o]} { set ok 1; break } } if {!$ok} { error "method export is only for classes in\ [join [array names :ignorePattern] {, }] not for $o" } } } :object method checkExportedObject {} { foreach o [array names :exportObjects] { if {![::nsf::object::exists $o]} { :warn "Serializer exportObject: ignore non-existing object $o" unset :exportObjects($o) } else { # add all child objects foreach o [:allChildren $element] { set :exportObjects($o) 1 } } } } :public object method all {-ignoreVarsRE -ignore} { # # Remove objects which should not be included in the # blueprint. TODO: this is not the best place to do this, since # this the function is defined by OpenACS catch ::xo::at_cleanup # don't filter anything during serialization set filterstate [::nsf::configure filter off] set s [:new -childof [::nsf::current object]] if {[info exists ignoreVarsRE]} {$s ignoreVarsRE set $ignoreVarsRE} if {[info exists ignore]} {$s ignore $ignore} set r [subst { set ::nsf::__filterstate \[::nsf::configure filter off\] #::nx::Slot mixin add ::nx::Slot::Nocheck ::nsf::exithandler set [list [::nsf::exithandler get]] }] foreach option {debug softrecreate keepcmds checkresults checkarguments} { append r \t [list ::nsf::configure $option [::nsf::configure $option]] \n } :resetPattern # # export all nsf_procs # append r [:export_nsfprocs ::] # # export objects and classes # set instances [list] foreach oss [ObjectSystemSerializer info instances] { append r [$oss serialize-all-start $s] lappend instances {*}[$oss instances $s] } # provide error messages for invalid exports :checkExportedMethods # export the objects and classes #$s warn "export objects = [array names :exportObjects]" #$s warn "export objects = [array names :exportMethods]" append r [$s serialize-objects $instances 0] foreach oss [ObjectSystemSerializer info instances] { append r [$oss serialize-all-end $s] } $s destroy append r { #::nx::Slot mixin delete ::nx::Slot::Nocheck ::nsf::configure filter $::nsf::__filterstate unset ::nsf::__filterstate } ::nsf::configure filter $filterstate return $r } :object method add_child_namespaces {ns} { if {$ns eq "::nsf"} return lappend :namespaces $ns foreach n [namespace children $ns] { :add_child_namespaces $n } } :public object method application_namespaces {ns} { set :namespaces "" :add_child_namespaces $ns return ${:namespaces} } :public object method export_nsfprocs {ns} { set result "" foreach n [:application_namespaces $ns] { foreach p [:info methods -type nsfproc ${n}::*] { append result [:info method definition $p] \n } } return $result } :public object method methodSerialize {object method prefix} { foreach oss [ObjectSystemSerializer info instances] { if {[$oss responsibleSerializer $object]} { set result [$oss serializeExportedMethod $object $prefix $method] break } } return $result } :public object method deepSerialize {-ignoreVarsRE -ignore -map -objmap args} { :resetPattern set s [:new -childof [::nsf::current object]] if {[info exists ignoreVarsRE]} {$s ignoreVarsRE set $ignoreVarsRE} if {[info exists ignore]} {$s ignore $ignore} if {[info exists objmap]} {$s objmap $objmap} foreach o $args { append r [$s deepSerialize [::nsf::directdispatch $o -frame method ::nsf::current]] } $s destroy if {[info exists map]} {return [string map $map $r]} return $r } # include Serializer in the serialized code :exportObjects [::nsf::current object] } ########################################################################### # Object System specific serializer ########################################################################### Class create ObjectSystemSerializer { :method init {} { # Include object system serializers and the meta-class in "Serializer all" Serializer exportObjects [::nsf::current class] Serializer exportObjects [::nsf::current object] } # reuse warn here as well :alias warn [Serializer info method registrationhandle warn] # # Methods to be executed at the begin and end of serialize all # :public method serialize-all-start {s} { :getExported return [:serializeExportedMethods $s] } :public method serialize-all-end {s} { set cmd "" foreach o [list ${:rootClass} ${:rootMetaClass}] { append cmd \ [:frameWorkCmd ::nsf::relation::get $o object-mixin] \ [:frameWorkCmd ::nsf::relation::get $o class-mixin] \ [:frameWorkCmd ::nsf::method::assertion $o object-invar] \ [:frameWorkCmd ::nsf::method::assertion $o class-invar] } #puts stderr "*** array unset [nsf::current object] alias_dependency // size [array size :alias_dependency]" array unset :alias_dependency return $cmd } # # Handle association between objects and responsible serializers # :public method responsibleSerializer {object} { return [::nsf::dispatch $object ::nsf::methods::object::info::hastype ${:rootClass}] } :public method registerSerializer {s instances} { # Communicate responsibility to serializer object $s foreach i $instances { if {![::nsf::dispatch $i ::nsf::methods::object::info::hastype ${:rootClass}]} continue $s setObjectSystemSerializer $i [::nsf::current object] } } :public method instances {s} { # Compute all instances, for which we are responsible and # notify serializer object $s set instances [list] foreach i [${:rootClass} info instances -closure] { if {[:matchesIgnorePattern $i] && ![$s isExportedObject $i]} { continue } $s setObjectSystemSerializer $i [::nsf::current object] lappend instances $i } #:warn "[::nsf::current object] handles instances: $instances" return $instances } :public method getExported {} { # # get exported objects and methods from main Serializer for # which this object specific serializer is responsible # foreach k [Serializer exportedMethods] { lassign $k o p m if {![::nsf::object::exists $o]} { :warn "$o is not an object" } elseif {[::nsf::dispatch $o ::nsf::methods::object::info::hastype ${:rootClass}]} { set :exportMethods($k) 1 } } foreach o [Serializer exportedObjects] { if {![::nsf::object::exists $o]} { :warn "$o is not an object" } elseif {[nsf::dispatch $o ::nsf::methods::object::info::hastype ${:rootClass}]} { set :exportObjects($o) 1 } } foreach p [array names :ignorePattern] {Serializer addPattern $p} } ############################### # general method serialization ############################### :method classify {o} { if {[::nsf::dispatch $o ::nsf::methods::object::info::hastype ${:rootMetaClass}]} \ {return Class} {return Object} } :method collectVars {o s} { set setcmd [list] foreach v [lsort [$o info vars]] { if {![::nsf::var::exists $s ignoreVarsRE] || [::nsf::var::set $s ignoreVarsRE] eq "" || ![regexp [::nsf::var::set $s ignoreVarsRE] ${o}::$v]} { if {[::nsf::var::exists $o $v] == 0} { puts stderr "strange, [list $o info vars] returned $v, but it does not seem to exist" continue } if {[::nsf::var::exists -array $o $v]} { lappend setcmd [list array set :$v [::nsf::var::set -array $o $v]] } else { lappend setcmd [list set :$v [::nsf::var::set $o $v]] } } } return $setcmd } :method frameWorkCmd {cmd o relation -unless} { set v [$cmd $o $relation] if {$v eq ""} {return ""} if {[info exists unless] && $v eq $unless} {return ""} regsub -all {::get$} $cmd ::set cmd return [list $cmd ${:targetName} $relation $v]\n } :method serializeExportedMethods {s} { set r "" foreach k [array names :exportMethods] { lassign $k o p m if {![:methodExists $o $p $m]} { :warn "Method does not exist: $o $p $m" continue } set :targetName [$s getTargetName $o] append methods($o) [:serializeExportedMethod $o $p $m]\n } foreach o [array names methods] {set ($o) 1} foreach o [list ${:rootClass} ${:rootMetaClass}] { if {[info exists ($o)]} {unset ($o)} } foreach o [concat ${:rootClass} ${:rootMetaClass} [array names ""]] { if {![info exists methods($o)]} continue append r \n $methods($o) } #puts stderr "[::nsf::current object] ... exportedMethods <$r\n>" return "$r\n" } ############################### # general object serialization ############################### :public method serialize {objectOrClass s} { set :targetName [$s getTargetName $objectOrClass] :[:classify $objectOrClass]-serialize $objectOrClass $s } :method matchesIgnorePattern {o} { foreach p [array names :ignorePattern] { if {[string match $p $o]} {return 1} } return 0 } :method collect-var-traces {o s} { set traces {} foreach v [$o info vars] { # Use directdispatch to query existing traces without the need # of an extra method. set t [::nsf::directdispatch $o -frame object ::trace info variable $v] if {$t ne ""} { foreach ops $t { lassign $ops op cmd # save traces in post_cmds set traceCmd [list ::nsf::directdispatch $o -frame object ::trace add variable $v $op $cmd] $s addPostCmd $traceCmd append traces $traceCmd \n # remove trace from object ::nsf::directdispatch $o -frame object ::trace remove variable $v $op $cmd } } } return $traces } ############################### # general dependency handling ############################### :public method needsNothing {x s} { return [:[:classify $x]-needsNothing $x $s] } :method alias-dependency {x where} { set handle :alias_dependency($x,$where) if {[info exists $handle]} { return [set $handle] } set needed [list] foreach alias [$x ::nsf::methods::${where}::info::methods -type alias -callprotection all -path] { set definition [$x ::nsf::methods::${where}::info::method definition $alias] set aliasedCmd [lindex $definition end] # # The aliasedCmd is fully qualified and could be a method # handle or a primitive cmd. For a primitive cmd, we have no # alias dependency. If the cmd is registed on an object, we # report the dependency. # set regObj [::nsf::method::registered $aliasedCmd] if {$regObj ne ""} { if {$regObj eq $x} { :warn "Dependency for alias $alias from $x to $x not handled (no guarantee on method order)" } else { lappend needed $regObj } } } # if {[llength $needed]>0} { # puts stderr "aliases: $x needs $needed" # puts stderr "set alias-deps for $x - $handle - $needed" # } set $handle $needed return $needed } :method Class-needsNothing {x s} { if {![:Object-needsNothing $x $s]} {return 0} set scs [$x ::nsf::methods::class::info::superclass] if {[$s needsOneOf $scs]} {return 0} if {[$s needsOneOf [::nsf::relation::get $x class-mixin]]} {return 0} foreach sc $scs {if {[$s needsOneOf [$sc ::nsf::methods::class::info::slotobjects]]} {return 0}} if {[$s needsOneOf [:alias-dependency $x class]]} {return 0} return 1 } :method Object-needsNothing {x s} { set p [$x info parent] set cl [$x info class] if {$p ne "::" && [$s needsOneOf $p]} {return 0} if {[$s needsOneOf $cl]} {return 0} if {[$s needsOneOf [$cl ::nsf::methods::class::info::slotobjects -closure -source application]]} {return 0} if {[$s needsOneOf [:alias-dependency $x object]]} {return 0} return 1 } } ########################################################################### # nx specific serializer ########################################################################### ObjectSystemSerializer create nx { set :rootClass ::nx::Object set :rootMetaClass ::nx::Class array set :ignorePattern [list "::nsf::*" 1 "::nx::*" 1 "::xotcl::*" 1] :public object method serialize-all-start {s} { set intro [subst { package require nx ::nx::configure defaultMethodCallProtection [::nx::configure defaultMethodCallProtection] ::nx::configure defaultAccessor [::nx::configure defaultAccessor] }] foreach pkg {nx::mongo} { if {![catch {package present $pkg}]} { append intro "package require $pkg\n" } } if {[info command ::Object] ne "" && [namespace origin ::Object] eq "::nx::Object"} { append intro "\n" "namespace import -force ::nx::*" } return "$intro\n[next]" } ############################### # nx method serialization ############################### :object method methodExists {object kind name} { expr {[$object info method type $name] ne ""} } :public object method serializeExportedMethod {object kind name} { # todo: object modifier is missing set :targetName $object return [:method-serialize $object $name ""] } :object method method-serialize {o m modifier} { if {![::nsf::is class $o]} {set modifier "object"} if {[$o info {*}$modifier method type $m] eq "object"} { # object serialization is fully handled by the serializer return "# [$o info {*}$modifier method definition $m]" } if {[$o info {*}$modifier method type $m] eq "setter"} { set def "" } else { set def [$o info {*}$modifier method definition $m] if {${:targetName} ne $o} { set def [lreplace $def 0 0 ${:targetName}] } } return $def } ############################### # nx object serialization ############################### :object method Object-serialize {o s} { if {[$o ::nsf::methods::object::info::hastype ::nx::EnsembleObject]} { return "" } set traces [:collect-var-traces $o $s] set evalList [:collectVars $o $s] if {[$o info has type ::nx::Slot]} { # Slots need to be explicitely initialized to ensure # __invalidateobjectparameter to be called lappend evalList :init } set objectName [::nsf::directdispatch $o -frame method ::nsf::current object] set isSlotContainer [::nx::isSlotContainer $objectName] if {$isSlotContainer} { append cmd [list ::nx::slotObj -container [namespace tail $objectName] \ [$s getTargetName [$objectName ::nsf::methods::object::info::parent]]]\n if {[llength $evalList] > 0} { append cmd [list ${:targetName} eval [join $evalList "\n "]]\n } } else { #puts stderr "CREATE targetName '${:targetName}'" append cmd [list ::nsf::object::alloc [$o info class] ${:targetName} [join $evalList "\n "]]\n foreach i [lsort [$o ::nsf::methods::object::info::methods -callprotection all -path]] { append cmd [:method-serialize $o $i "object"] "\n" } } append cmd \ [:frameWorkCmd ::nsf::relation::get $o object-mixin] \ [:frameWorkCmd ::nsf::method::assertion $o object-invar] \ [:frameWorkCmd ::nsf::object::property $o keepcallerself -unless 0] \ [:frameWorkCmd ::nsf::object::property $o perobjectdispatch -unless 0] eval $traces $s addPostCmd [:frameWorkCmd ::nsf::relation::get $o object-filter] return $cmd } ############################### # nx class serialization ############################### :object method Class-serialize {o s} { set cmd [:Object-serialize $o $s] foreach i [lsort [$o ::nsf::methods::class::info::methods -callprotection all -path]] { append cmd [:method-serialize $o $i ""] "\n" } append cmd \ [:frameWorkCmd ::nsf::relation::get $o superclass -unless ${:rootClass}] \ [:frameWorkCmd ::nsf::relation::get $o class-mixin] \ [:frameWorkCmd ::nsf::method::assertion $o class-invar] $s addPostCmd [:frameWorkCmd ::nsf::relation::get $o class-filter] return $cmd\n } # register serialize a global method ::nx::Object public method serialize {} { ::Serializer deepSerialize [::nsf::current object] } } ########################################################################### # XOTcl specific serializer ########################################################################### ObjectSystemSerializer create xotcl { set :rootClass ::xotcl::Object set :rootMetaClass ::xotcl::Class #array set :ignorePattern [list "::xotcl::*" 1] array set :ignorePattern [list "::nsf::*" 1 "::nx::*" 1 "::xotcl::*" 1] :public object method serialize-all-start {s} { set intro "package require XOTcl 2.0" if {[info command ::Object] ne "" && [namespace origin ::Object] eq "::xotcl::Object"} { append intro "\nnamespace import -force ::xotcl::*" } return "$intro\n::xotcl::Object instproc trace args {}\n[next]" } :public object method serialize-all-end {s} { return "[next]\n::nsf::method::alias ::xotcl::Object trace -frame object ::trace\n" } ############################### # XOTcl method serialization ############################### :object method methodExists {object kind name} { switch $kind { proc - instproc { return [expr {[$object info ${kind}s $name] ne ""}] } forward - instforward { return [expr {[$object info ${kind} $name] ne ""}] } } } :public object method serializeExportedMethod {object kind name} { set :targetName $object set code "" switch $kind { "" - inst { # legacy; kind is prefix set code [:method-serialize $object $name $kind]\n } proc - instproc { if {[$object info ${kind}s $name] ne ""} { set prefix [expr {$kind eq "proc" ? "" : "inst"}] set code [:method-serialize $object $name $prefix]\n } } forward - instforward { if {[$object info $kind $name] ne ""} { set code [concat [list $object] $kind $name [$object info $kind -definition $name]]\n } } } return $code } :object method method-serialize {o m prefix} { if {![nsf::is class $o] || $prefix eq ""} { set scope object } else { set scope class } set arglist [$o ::nsf::methods::${scope}::info::method parameter $m] # set arglist0 [list] # foreach v [$o info ${prefix}args $m] { # if {[$o info ${prefix}default $m $v x]} { # #puts "... [list $o info ${prefix}default $m $v x] returned 1, x?[info exists x] level=[info level]" # lappend arglist0 [list $v $x] } {lappend arglist0 $v} # } # set arglist0 [concat [$o info ${prefix}nonposargs $m] $arglist0] # puts stderr "====== [list $o $m $prefix] scope $scope => NEW $arglist OLD $arglist0" lappend r ${:targetName} ${prefix}proc $m \ $arglist \ [$o info ${prefix}body $m] foreach p {pre post} { if {[$o info ${prefix}$p $m] ne ""} {lappend r [$o info ${prefix}$p $m]} } return $r } ############################### # XOTcl object serialization ############################### :object method Object-serialize {o s} { set traces [:collect-var-traces $o $s] append cmd [list ::nsf::object::alloc [$o info class] ${:targetName} [join [:collectVars $o $s] "\n "]]\n foreach i [$o ::nsf::methods::object::info::methods -type scripted -callprotection all] { append cmd [:method-serialize $o $i ""] "\n" } foreach i [$o ::nsf::methods::object::info::methods -type forward -callprotection all] { append cmd [concat [list ${:targetName}] forward $i [$o info forward -definition $i]] "\n" } foreach i [$o ::nsf::methods::object::info::methods -type setter -callprotection all] { append cmd [list ${:targetName} parametercmd $i] "\n" } append cmd \ [:frameWorkCmd ::nsf::relation::get $o object-mixin] \ [:frameWorkCmd ::nsf::method::assertion $o object-invar] $s addPostCmd [:frameWorkCmd ::nsf::relation::get $o object-filter] eval $traces return $cmd } ############################### # XOTcl class serialization ############################### :object method Class-serialize {o s} { set cmd [:Object-serialize $o $s] foreach i [$o info instprocs] { append cmd [:method-serialize $o $i inst] "\n" } foreach i [$o info instforward] { append cmd [concat [list ${:targetName}] instforward $i [$o info instforward -definition $i]] "\n" } foreach i [$o info instparametercmd] { append cmd [list ${:targetName} instparametercmd $i] "\n" } # provide limited support for exporting aliases for XOTcl objects foreach i [$o ::nsf::methods::class::info::methods -type alias -callprotection all] { set nxDef [$o ::nsf::methods::class::info::method definition $i] append cmd [list ::nsf::method::alias ${:targetName} {*}[lrange $nxDef 3 end]]\n } append cmd \ [:frameWorkCmd ::nsf::relation::get $o superclass -unless ${:rootClass}] \ [:frameWorkCmd ::nsf::relation::get $o class-mixin] \ [:frameWorkCmd ::nsf::method::assertion $o class-invar] $s addPostCmd [:frameWorkCmd ::nsf::relation::get $o class-filter] return $cmd } # register serialize a global method for XOTcl ::xotcl::Object instproc serialize {} { ::Serializer deepSerialize [::nsf::current object] } # include this method in the serialized code #Serializer exportMethods { # ::xotcl::Object instproc contains #} } namespace export Serializer namespace eval :: "namespace import -force [namespace current]::*" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/tcl-cool/000077500000000000000000000000001242365656200142515ustar00rootroot00000000000000library/tcl-cool/tcl-cool.tcl000066400000000000000000000215251242365656200164760ustar00rootroot00000000000000set auto_path [concat . $auto_path] ######################################################################### # # Implementation of the Tcl Core Object Oriented Language (TclCOOL) # based on the Next Scripting Framework # ######################################################################### # # This script consists of three major parts: # # 1. The definition of TclCOOL (Tcl Core Object Oriented Language) # based on the Next Scripting Framework (::nsf). TclCOOL is # simple but powerful object language. # # 2. Sample TclCOOL program # # In a first step, we load nsf package require nsf # Now we have the following commands and methods defined in the ::nsf # namespace (among more functionality not needed here): # # Two un-exported commands for OO-language designer # ::nsf::alias # ::nsf::objectsystem::create # ::nsf::forward # ::nsf::method # ::nsf::relation::set # # Three exported commands to be used by in the languages # ::nsf::my # ::nsf::current # ::nsf::next # # An unregistered (unattached) set of methods that can be used for # objects. We use here just: # instvar # lookupmethods # # An unattached method can be attached to an object or class by the # ::nsf::alias # # ::nsf::method::alias class|obj ?-per-object? methodName cmdName # # The command registers a command ("cmdName") under a certain name # ("methodName") to an object or class (1st argument) to make the # command available as a method. ###################################################################### # # 1. TclCOOL language definition based on the Next Scripting Framework # ###################################################################### namespace eval tcl-cool { # In a first step, we create two base classes of TclCOOL, # namely "object" and "class" in the current namespace: ::nsf::objectsystem::create object class # We have now the two base classes defined as Tcl commands. Now we # can define methods for these newly defined classes # (tcl-cool::object and tcl-cool::class). # # We define as well [self] as a synonym of "nsf::current object" # interp alias {} ::tcl-cool::self {} ::nsf::current object # We define 2 methods for "class" (actually "::tcl-cool::class) # based on the method set for classes # # - "method" is a means to define the methods, which are provided # by the class to the instances of the class # - "forward" is a forwarder for instances of the class # # These methods are defined by the means of ::nsf::forward ::nsf::method::forward class method ::nsf::method::create %self ::nsf::method::forward class forward ::nsf::method::forward %self # We could have defined the methods "method" and "forward" as well # by the means of ::nsf::method, such as # # ::nsf::method::create class method {methodName arguments body} { # return [::nsf::method::create [self] $methodName $arguments $body] # } # ::nsf::method::create class forward {methodName args} { # return [::nsf::method::forward [self] $methodName {*}$args] # } # # Sometimes using method is better to be selective on the arguments # and the provided switches. # Next, we define 3 methods for "object" (actually "::tcl-cool::object) # based on the method set for objects # # - "variable" is a means to import instance variables into # the current scope ("instvar" in XOTcl) # - "forward" is a method for delegating calls to different objects # - "methods" is an introspection method for showing the methods of an object # ::nsf::method::alias object variable ::nsf::methods::object::instvar ::nsf::method::forward object forward ::nsf::method::forward %self -per-object ::nsf::method::alias object methods ::nsf::methods::object::info::lookupmethods # # The method "create" defines, what happens, when a class or object # is created. First the object is allocated, then the constructor is called. # class method create {name args} { set obj [::nsf::dispatch [self] ::nsf::methods::class::alloc $name] $obj init {*}$args return $obj } # provide primitive commands; we use these from nsf namespace import ::nsf::next ::nsf::my # a small helper proc for processing the body of the constructor proc pop vn {upvar $vn v; set r [lindex $v 0]; set v [lreplace $v 0 0]; return $r} # When we create classes without specifying a superclass, we have to # choose a default class. This is achieved by setting an instance # variable in the meta-class "class" with the name # "__default_superclass" to the newly defined object. In XOTcl, this # is integrated with the slot objects that provide a uniform # interface. Slot objects would need more support from other # commands in TclCOOL, so they are left out for the time being.... # # The following method is the constructor for classes. It sets the # default superclass and provides easy means for specifying methods # and superclasses during initialization class method init {spec} { my variable __default_superclass set __default_superclass [namespace current]::object while {[llength $spec]} { set m [pop spec] switch $m { method {my method [pop spec] [pop spec] [pop spec]} superclass {my superclass [pop spec]} default {error "unknown argument '$m'"} } } } # Call the constructor on "class" to set the default superclass as # well for this class, and define a convenience routine for defining # superclasses class init { method superclass {sc} { ::nsf::relation::set [self] superclass $sc } } # Finally, we provide a few methods for all objects in TclCOOL: # - "unknown": provide an error message, when unknown methods are called # - "filter": convenience routine to set filters though object method unknown {m args} {error "[self]: unknown method '$m' called"} # Provide users a convenient way to register/deregister per-object # filters and mixins object forward filter ::nsf::relation::set %self object-filter object forward mixin ::nsf::relation::set %self object-mixin # finally, export a few commands namespace export object class my self next } ###################################################################### # # 3. Sample TclCOOL program # ###################################################################### namespace import tcl-cool::* class create dog { method init {} { tail create [self]::tail my forward wag [self]::tail wag my forward rise [self]::tail rise } method bark {} { puts "[self] Bark, bark, bark." } } # we can extend the class incrementally dog method chase {thing} { puts "Chase $thing!" } class create tail { method init {} { my variable length set length 5 } method wag {} { return Joy } method rise {} { return Attention } } dog create fido puts "wag means [fido wag]" fido chase tweedy! # The output is: # wag means Joy # Chase tweedy!! puts "\n============ filter ================" # # define a filter method.... # object method tracer args { puts "* call [self] [::nsf::current calledmethod] [::nsf::current args]" set r [next] puts "* exit [self] [::nsf::current calledmethod], returns '$r'" return $r } # # ... and register the filter on the object: # fido filter tracer # # invoke the methods again # puts "wag means [fido wag]" fido chase tweedy! # The output is: # > ============ filter ================ # > * call ::fido wag # > * exit ::fido wag, returns 'Joy' # > wag means Joy # > * call ::fido chase tweedy! # > Chase tweedy!! # > * exit ::fido chase, returns '' # > * call ::fido filter {} # > * exit ::fido filter, returns '' # # remove the filter # fido filter "" puts "\n============ mixin class ================" # # define a class, which should be mixed in into instances of dogs # class create lazydog { method wag {} { puts "... well, if i have to...." next } method chase thing { puts "... [self] does not like to chase $thing" } } # # ... and register the filter on the object: # fido mixin lazydog # # invoke the methods again # puts "wag means [fido wag]" fido chase tweedy! # The output is: # > ============ mixin class ================ # > ... well, if i have to.... # > wag means Joy # > ... ::fido does not like to chase tweedy! # # remove the mixin class again # fido mixin "" puts "\n============ subclass ================" class create terrier { superclass dog method chase thing { puts "[self]: Yippy, I'll get that wicked $thing!" } } terrier create frido frido chase tweedy # The output is: # > ============ subclass ================ # >Yippy, I'll get that wicked tweedy!! puts "\nApplication specific methods of fido: [fido methods -source application]" puts "System specific methods of fido: [fido methods -source baseclasses]" puts "All methods of fido: [fido methods]\n" foreach cmd {{fido wag} {fido rise}} { puts "$cmd [time {eval $cmd} 10000]" } library/xotcl/000077500000000000000000000000001242365656200136665ustar00rootroot00000000000000library/xotcl/COPYRIGHT000066400000000000000000000050371242365656200151660ustar00rootroot00000000000000/* * Next Scripting Framework * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * This software is based upon MIT Object Tcl by David Wetherall and * Christopher J. Lindblad, that contains the following copyright * message: * * "Copyright 1993 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty." */ library/xotcl/apps/000077500000000000000000000000001242365656200146315ustar00rootroot00000000000000library/xotcl/apps/COPYRIGHT000066400000000000000000000050371242365656200161310ustar00rootroot00000000000000/* * Next Scripting Framework * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * This software is based upon MIT Object Tcl by David Wetherall and * Christopher J. Lindblad, that contains the following copyright * message: * * "Copyright 1993 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty." */ library/xotcl/apps/comm/000077500000000000000000000000001242365656200155645ustar00rootroot00000000000000library/xotcl/apps/comm/client.pem000066400000000000000000000063401242365656200175500ustar00rootroot00000000000000issuer :/C=DE/ST=NRW/L=Essen/O=University of Essen/OU=SWT/CN=Fredj Dridi/Email=Fredj.Dridi@uni-essen.de subject:/CN=Fredj Dridi/Email=fredj_dridi@yahoo.com serial :02 Certificate: Data: Version: 3 (0x2) Serial Number: 2 (0x2) Signature Algorithm: md5WithRSAEncryption Issuer: C=DE, ST=NRW, L=Essen, O=University of Essen, OU=SWT, CN=Fredj Dridi/Email=Fredj.Dridi@uni-essen.de Validity Not Before: Apr 28 08:24:43 2000 GMT Not After : Apr 28 08:24:43 2001 GMT Subject: CN=Fredj Dridi/Email=fredj_dridi@yahoo.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:e4:df:a7:f3:ed:61:df:30:ad:d9:6f:63:f2:d1: 85:9b:72:b2:c6:e4:fd:50:11:c5:0a:29:59:02:60: 29:f6:2c:6a:35:08:89:49:ad:d4:44:1d:7f:14:18: 61:4d:e8:66:87:30:01:52:cd:7d:16:72:0e:24:38: 19:a5:a7:dc:cf:7a:5d:79:ea:48:c6:c4:ae:52:a6: 94:36:7f:f3:24:43:b0:21:5a:f2:d5:6d:66:38:4c: b7:7a:0e:ce:12:01:b0:46:4b:ea:08:b4:e0:aa:b8: 96:dc:3e:15:e0:24:92:84:1f:77:d0:8d:73:d2:f3: ac:82:b0:61:60:1a:6a:fc:b9 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Alternative Name: email:fredj_dridi@yahoo.com X509v3 Basic Constraints: critical CA:FALSE X509v3 Authority Key Identifier: keyid:D2:B8:E3:2D:A2:5A:22:78:3A:38:F1:1F:F5:7C:AD:8D:A6:C4:41:0F X509v3 Extended Key Usage: TLS Web Client Authentication, E-mail Protection Signature Algorithm: md5WithRSAEncryption 01:88:26:35:b9:c7:c9:5a:90:ff:b8:9c:37:fa:48:0e:03:82: b4:18:df:49:e7:5d:42:3a:48:e3:e4:84:c7:b6:ef:cc:91:4c: 47:9d:34:f9:51:0b:63:d0:35:f9:ad:a5:a9:3d:ef:75:23:0c: 14:77:59:79:59:58:c8:74:df:01:b8:de:f2:78:47:64:1a:7e: 4b:09:31:5e:f5:34:e2:ad:5e:e8:81:00:f1:af:fe:48:31:0b: a9:b1:4d:53:51:9a:15:1f:55:ba:30:e4:0e:02:05:20:4b:de: 9c:d9:86:f1:e4:d9:18:c4:93:03:19:06:a5:f3:cb:2a:28:a0: fd:de -----BEGIN CERTIFICATE----- MIICuzCCAiSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADCBljELMAkGA1UEBhMCREUx DDAKBgNVBAgTA05SVzEOMAwGA1UEBxMFRXNzZW4xHDAaBgNVBAoTE1VuaXZlcnNp dHkgb2YgRXNzZW4xDDAKBgNVBAsTA1NXVDEUMBIGA1UEAxMLRnJlZGogRHJpZGkx JzAlBgkqhkiG9w0BCQEWGEZyZWRqLkRyaWRpQHVuaS1lc3Nlbi5kZTAeFw0wMDA0 MjgwODI0NDNaFw0wMTA0MjgwODI0NDNaMDwxFDASBgNVBAMTC0ZyZWRqIERyaWRp MSQwIgYJKoZIhvcNAQkBFhVmcmVkal9kcmlkaUB5YWhvby5jb20wgZ8wDQYJKoZI hvcNAQEBBQADgY0AMIGJAoGBAOTfp/PtYd8wrdlvY/LRhZtyssbk/VARxQopWQJg KfYsajUIiUmt1EQdfxQYYU3oZocwAVLNfRZyDiQ4GaWn3M96XXnqSMbErlKmlDZ/ 8yRDsCFa8tVtZjhMt3oOzhIBsEZL6gi04Kq4ltw+FeAkkoQfd9CNc9LzrIKwYWAa avy5AgMBAAGjcjBwMCAGA1UdEQQZMBeBFWZyZWRqX2RyaWRpQHlhaG9vLmNvbTAM BgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFNK44y2iWiJ4OjjxH/V8rY2mxEEPMB0G A1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDANBgkqhkiG9w0BAQQFAAOBgQAB iCY1ucfJWpD/uJw3+kgOA4K0GN9J511COkjj5ITHtu/MkUxHnTT5UQtj0DX5raWp Pe91IwwUd1l5WVjIdN8BuN7yeEdkGn5LCTFe9TTirV7ogQDxr/5IMQupsU1TUZoV H1W6MOQOAgUgS96c2Ybx5NkYxJMDGQal88sqKKD93g== -----END CERTIFICATE----- library/xotcl/apps/comm/filename.crt000066400000000000000000000024321242365656200200570ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDmDCCAwGgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBlTELMAkGA1UEBhMCYXQx EDAOBgNVBAgTB0F1c3RyaWExDzANBgNVBAcTBlZpZW5uYTEQMA4GA1UEChMHV1Ut V2llbjELMAkGA1UECxMCV0kxHjAcBgNVBAMTFW1vaGVnYW4ud3Utd2llbi5hYy5h dDEkMCIGCSqGSIb3DQEJARYVbmV1bWFubkB3dS13aWVuLmFjLmF0MB4XDTAyMDQw MjEwNDUwN1oXDTAyMDUwMjEwNDUwN1owgZUxCzAJBgNVBAYTAmF0MRAwDgYDVQQI EwdBdXN0cmlhMQ8wDQYDVQQHEwZWaWVubmExEDAOBgNVBAoTB1dVLVdpZW4xCzAJ BgNVBAsTAldJMR4wHAYDVQQDExVtb2hlZ2FuLnd1LXdpZW4uYWMuYXQxJDAiBgkq hkiG9w0BCQEWFW5ldW1hbm5Ad3Utd2llbi5hYy5hdDCBnzANBgkqhkiG9w0BAQEF AAOBjQAwgYkCgYEA5iT1aT1zU5mfqabGQWcdLyGVFlJBuiKnD2wCVDBIJFVYk6EJ FrFadKgUXNa0Sxrav/5BJyG2ObOrS4BH6yAl8f90QbAokFp9HeW5wkkAhjkSe1Rw vcts9F+R6OhcBxO+tQ6maR9wIGfWoK+vWDyfO7wnHjiL2YZFW73mDBUuHO8CAwEA AaOB9TCB8jAdBgNVHQ4EFgQUbnmEzLNHBNqySdNmPzLSf+yjEc8wgcIGA1UdIwSB ujCBt4AUbnmEzLNHBNqySdNmPzLSf+yjEc+hgZukgZgwgZUxCzAJBgNVBAYTAmF0 MRAwDgYDVQQIEwdBdXN0cmlhMQ8wDQYDVQQHEwZWaWVubmExEDAOBgNVBAoTB1dV LVdpZW4xCzAJBgNVBAsTAldJMR4wHAYDVQQDExVtb2hlZ2FuLnd1LXdpZW4uYWMu YXQxJDAiBgkqhkiG9w0BCQEWFW5ldW1hbm5Ad3Utd2llbi5hYy5hdIIBADAMBgNV HRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4GBANj6u54TmwEJg/Ldvfx+Qrax+66n zh1EYzxrlYp6eNQVPEyOsF1DJh150Lci1Mm/i71D87yJRVjagTv5dAUdupy9Zf6c AMMv6KvK5G7q6LC9ArwNiBObsmQUlN7+PzRmG9CerRJ6W8eEYnYB0EHhYVKc8cED F4mixcF1HGQJI/qN -----END CERTIFICATE----- library/xotcl/apps/comm/filename.key000066400000000000000000000017031242365656200200570ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,075071A5AF5C1D8A qmihZRxmNNlGCx9lVC4QQPQS25e4aAg3oHjxDaZ0yyDSKsWz4J04lo2x63QT/88i z9YUQqyC8ry53D5y3aQKiFuXlDDvX6PtPS+cQANA8g5qOIHsMF3yINfrVEmWkPjx xOYF2qIGI0ISPTT5AkTN3KVmYE7rbFvqgQMklj/SlNBqeUmKvn1rhUCFAd0jYXa9 imaKVMj08q4GItLAqPoJNY4K73IHZeaW8GEWAJ5E/KEzYFBnYUiPUN8UPCCQsC+b ViRF226hCFSu8yDBah8R/u6W9rEa+Vs/T7Cyklnot2FDyCU0EW/lhKBKfEKTmXAe sBzN7F1hrLFifUD7VzJ2K7zKh7pMy3X+D+0PUjVaIyZWDIH3nOiTzfK6kxksjoE5 P+fp5IL4okEJgD6MoLJHGWTwfhH9K6QBX01My3s22q5zuiJ3SajRCFlLDn7jTTPV 2OQ9+2FksxJYa5Z5/n63Vslz0+yKv9AVTbrtfrbbRJtmpbElCPrvhzVsuWksRS3A biZxD5PkWiYDvNm34eOAOARb7n0awTOZ1bqdccT+rhcvo7c+MypIY0/AfZKZK4hN bWxHR3wZUv+wffx1CBxvsBBomV8XqcqJliVHgdvwDdzl3Z2HbYK3w92RXNY36FYw C1iqALuxE9nhzyEv0ARfjcXTu0lUmFZOfi4oizzMVj7u2q4mGt0+ZuKBs/fTEEm1 3L1nLnalGb5ko5OTZ4DMQlP1HCU3UZslImAcc3FFa/WNzhG07/YAlcTS18YUbJYF 19Yx6qrYfrZA2UZq0+8DFa8XgOHCFO0KqQHRfRB/7tGNGxX5md8+9w== -----END RSA PRIVATE KEY----- library/xotcl/apps/comm/ftp.xotcl000077500000000000000000000004201242365656200174270ustar00rootroot00000000000000#!/usr/bin/env tclsh package require XOTcl 2.0; namespace import -force xotcl::* @ @File {description { A tiny FTP client }} package require xotcl::comm::ftp SimpleRequest r1 -url ftp://prep.ai.mit.edu/README if {[r1 success]} { puts "Got:" puts [r1 getContent] } library/xotcl/apps/comm/get-regression-nb.xotcl000077500000000000000000000306111242365656200221750ustar00rootroot00000000000000#!/usr/bin/env tclsh package require XOTcl 2.0; namespace import -force xotcl::* # ./get-regression-nb.xotcl -host swt -parallel 0 # ./get-regression-nb.xotcl -host swt -sequential 0 # # mit ~/wafe/src/cineast/webserver.xotcl (benotigt ~/wafe/test/*) # ./get-regression-nb.xotcl -port 8086 # # Vergleich webserver.xotcl mit Apache: # 1) installation von Apache auf port 80 # # 2) installation vom webserver.xotcl auf port 8086 # # 3) von beiden server sollend die files in wafe/test/* unter # http://SERVER+PORT/test/* # erreichbar sein. # # 4) test der installation # apache: # cd wafe/src/cineast # get-regression-nb.xotcl -sequential 0 # die ausgabe sollte mit totalbytes=6536120 # abgeschlossen werden # # webserver.xotcl: # cd wafe/src/cineast # get-regression-nb.xotcl -port 8086 -sequential 0 # die ausgabe sollte mit totalbytes=6536120 # abgeschlossen werden # # 5) grosser testlauf: # rsh muss funktionieren: z.B.: rsh localhost date # # apache: # cd wafe/src/cineast # time get-regression-nb.xotcl -sequential 0 -clients 1 # # webserver.xotcl: # cd wafe/src/cineast # time get-regression-nb.xotcl -port 8086 -sequential 0 -clients 1 # # Ergebnisse auf meinem Rechner: # # der xotcl-webserver ist etwa 20% langsamer als apache. # das logging (instproc log) aktivieren kostet ein paar weitere prozent.... # -gn # # mohegan:~/wafe/src/cineast> time ./get-regression-nb.xotcl -port 8086 -sequential 0 -clients 1 # Loading source file ~/wafe/src/cineast/Access.xotcl # Loading source file ~/wafe/src/cineast/PCache.xotcl # Loading source file ~/wafe/src/cineast/Connection.xotcl # Loading source file ~/wafe/src/cineast/trace.xotcl # 1 clients: 3.07 seconds (per client 3.07 seconds, 2127.31 KB/sec) server: 2127.31 KB/sec # 2 clients: 6.36 seconds (per client 3.18 seconds, 1028.10 KB/sec) server: 2056.20 KB/sec # 3 clients: 7.71 seconds (per client 2.57 seconds, 847.74 KB/sec) server: 2543.22 KB/sec # 4 clients: 11.21 seconds (per client 2.80 seconds, 582.92 KB/sec) server: 2331.66 KB/sec # 5 clients: 10.57 seconds (per client 2.11 seconds, 618.49 KB/sec) server: 3092.45 KB/sec # 10 clients: 25.07 seconds (per client 2.51 seconds, 260.68 KB/sec) server: 2606.79 KB/sec # 20 clients: 45.48 seconds (per client 2.27 seconds, 143.73 KB/sec) server: 2874.58 KB/sec #0.420u 0.450s 1:49.65 0.7% 0+0k 0+0io 113263310pf+0w # # # # mohegan:~/wafe/src/cineast> time ./get-regression-nb.xotcl -port 80 -sequential 0 -clients 1 # Loading source file ~/wafe/src/cineast/Access.xotcl # Loading source file ~/wafe/src/cineast/PCache.xotcl # Loading source file ~/wafe/src/cineast/Connection.xotcl # Loading source file ~/wafe/src/cineast/trace.xotcl # 1 clients: 1.85 seconds (per client 1.85 seconds, 3542.58 KB/sec) server: 3542.58 KB/sec # 2 clients: 4.71 seconds (per client 2.36 seconds, 1387.02 KB/sec) server: 2774.03 KB/sec # 3 clients: 4.09 seconds (per client 1.36 seconds, 1596.58 KB/sec) server: 4789.74 KB/sec # 4 clients: 7.74 seconds (per client 1.94 seconds, 844.43 KB/sec) server: 3377.71 KB/sec # 5 clients: 9.46 seconds (per client 1.89 seconds, 690.67 KB/sec) server: 3453.33 KB/sec # 10 clients: 20.91 seconds (per client 2.09 seconds, 312.52 KB/sec) server: 3125.24 KB/sec # 20 clients: 39.01 seconds (per client 1.95 seconds, 167.55 KB/sec) server: 3351.08 KB/sec #0.410u 0.360s 1:27.95 0.8% 0+0k 0+0io 112251994pf+0w # # set CACHE_DIR [::xotcl::tmpdir] package require xotcl::comm::httpAccess package require xotcl::trace set port "" set host localhost set cachingopts {0 1 2 2} set parallel 1 set sequential 0 set clients 0 set local 1 foreach {att val} $argv { switch -exact -- $att { -port {set port $val} -host {set host $val} -memory {set cachingopts 0} -parallel {set parallel $val} -sequential {set sequential $val} -clients {set clients $val} -local {set local $val} } } set hosts { R2H2-11 R2H2-12 R2H2-13 R2H2-21 R2H2-22 R2H2-23 R2H2-31 R2H2-32 R2H2-33 R2H2-41 R2H2-42 R2H2-43 R2H2-51 R2H2-52 R2H2-53 R2H2-61 R2H2-62 R2H2-63 R2H2-73 matush nashawag sagumumpsketuck wawog willimantic wonkituck mashipaug watuppa } #set hosts { # matush nashawag sagumumpsketuck wawog willimantic wonkituck mashipaug # R2H2-11 R2H2-12 R2H2-13 R2H2-21 R2H2-22 R2H2-23 R2H2-31 R2H2-32 # R2H2-33 R2H2-41 R2H2-42 R2H2-43 R2H2-51 R2H2-52 R2H2-53 R2H2-61 # R2H2-62 R2H2-63 R2H2-73 # watuppa #} set totalbytes 6536120 set totalbytes 1293240;# ohne 5m request if {$clients} { proc readable {handle rhost} { if {[eof $handle]} { incr ::running -1 if {[catch {close $handle} output]} { if {![string match *$::totalbytes $output]} { puts stderr "invalid output on client on host $rhost" puts stderr "***********************************" puts stderr $output puts stderr "***********************************" } } #puts stderr clients=$::running if {$::running == 0} { set ::xxx 1 } } else { gets $handle } } proc clients {clients} { append cmd "[pwd]/$::argv0 -host $::host " \ "-parallel $::parallel -sequential $::sequential" if {$::port ne ""} {append cmd " -port $::port"} set starttime [clock clicks] set ::running $clients for {set s 0} {$s < $clients} {incr s} { if {$::local} { set rhost localhost } else { set rhost [lindex $::hosts $s] } #puts stderr "rsh $rhost $cmd" puts -nonewline stderr "$rhost " set f($s) [open "| rsh $rhost $cmd"] fconfigure $f($s) -blocking 0 fileevent $f($s) readable "readable $f($s) $rhost" } puts stderr "" vwait ::xxx set secs [expr {([clock clicks] -$starttime)/1000000.0}] puts stderr "[format %3d $clients] clients: [format %6.2f $secs] seconds \ (per client [format %6.2f [expr {$secs/$clients}]] seconds,\ [format %7.2f [expr {$::totalbytes/($secs*1000.0)}]] KB/sec)\ server: [format %7.2f [expr {$::totalbytes*$clients/($secs*1000.0)}]] KB/sec" } clients 1 clients 2 clients 3 clients 4 clients 5 clients 10 clients 20 exit } persistentCache clear proc assert {f r} { set got [eval $f] if {$got ne $r } { puts stderr "assertion failed: \[$f\] == $r (got $got)" quit } else { puts stderr "OK $r = $f" } } proc head msg { puts stderr "" puts stderr "---------------------------- $msg" } proc test {msg cmd} { set r [Object autoname r] head $msg if {[catch {eval $cmd} msg]} { puts stderr "ERROR: $::errorInfo" quit } $r destroy } Object userPwd userPwd proc user {u} { my set user $u } userPwd proc show {realm userVar pwVar} { upvar $userVar u $pwVar pw set u [my set user] set pw jogl return 1 } # test "simple request" { # SimpleRequest $r -caching 0 \ # -url http://localhost/index.html # assert "$r getContentLength" 81 # } # test "simple request" { # SimpleRequest $r -caching 1 \ # -url http://localhost/index.html # assert "$r getContentLength" 81 # } # test "simple request" { # SimpleRequest $r -caching 1 \ # -url http://localhost/index.html # assert "$r getContentLength" 81 # } # test "simple request" { # persistentCache invalidate \ # http://localhost/index.html # SimpleRequest $r -caching 1 \ # -url http://localhost/index.html # assert "$r getContentLength" 81 # } # test "simple request" { # SimpleRequest $r -caching 0 \ # -url http://localhost/muster-d1klausur.ps # assert "$r getContentLength" 163840 # } set total 0 proc parallelRequests-1.0 {urls} { ParallelSink psink -httpVersion 1.0 -init -requests $urls incr ::total [psink set totalbytes] psink destroy } proc parallelRequests-1.1 {urls} { ParallelSink psink -init -requests $urls incr ::total [psink set totalbytes] psink destroy } if {$port ne ""} {set port :$port} if {$parallel} { parallelRequests-1.0 [list \ http://$host$port/test/file500.html \ http://$host$port/test/file5k.html \ http://$host$port/test/file50k.html \ http://$host$port/test/file500k.html \ http://$host$port/test/file5m.html \ http://$host$port/test/file500.html \ http://$host$port/test/file5k.html \ http://$host$port/test/file5k.html \ http://$host$port/test/file500.html \ http://$host$port/test/file500.html \ http://$host$port/test/file5k.html \ http://$host$port/test/file5k.html \ http://$host$port/test/file500.html \ http://$host$port/test/file5k.html \ http://$host$port/test/file5k.html \ http://$host$port/test/file500.html \ ] for {set i 1} {$i<10} {incr i} { parallelRequests-1.1 [list \ http://$host$port/test/file50k.html \ http://$host$port/test/file5k1.html \ http://$host$port/test/file5k2.html \ http://$host$port/test/file5k3.html \ http://$host$port/test/file5k4.html \ http://$host$port/test/file5k5.html ] } puts stderr totalbytes=$::total } if {$sequential} { set doc http://$host$port/test/suexec.html set size 20680 foreach c $cachingopts { test "caching $c $doc" { SimpleRequest $r -caching $::c -url $::doc assert "$r getContentLength" $::size #puts stderr c=<[$r getContent]> } } set doc http://$host$port/test/xvdocs.ps set size 3678303 foreach c $cachingopts { test "caching $c" { SimpleRequest $r -caching $::c -url $::doc assert "$r getContentLength" $::size } } } exit test "simple request" { SimpleRequest $r -caching 0 \ -url http://nestroy.wi-inf.uni-essen.de/Raumplan.html assert "$r getContentLength" 662 } test "simple request, larger file" { SimpleRequest $r -caching 0 \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 } test "use cache" { SimpleRequest $r -caching 1 \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 } test "specify filename, use cache and validate request" { persistentCache invalidate \ http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps exec rm -f test.ps SimpleRequest $r -caching 1 -fileName test.ps \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 assert "file size test.ps" 349970 assert {lindex [exec md5sum test.ps] 0} c6029c987e841430f3ca9bab157da12f } test "specify filename, and use cache and a validated file" { exec rm -f test.ps SimpleRequest $r -caching 1 -fileName test.ps \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 assert "file size test.ps" 349970 assert {lindex [exec md5sum test.ps] 0} c6029c987e841430f3ca9bab157da12f } test "specify filename, and do not use cache" { exec rm -f test.ps SimpleRequest $r -fileName test.ps -caching 0 \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 assert "file size test.ps" 349970 assert {lindex [exec md5sum test.ps] 0} c6029c987e841430f3ca9bab157da12f } test "specify filesink and use cache; no copying neccesary" { persistentCache invalidate \ http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps SimpleRequest $r -useFileSink 1 -caching 1 \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 assert "file size test.ps" 349970 } test "load a large file to the cache" { persistentCache clearEntry http://swt.wi-inf.uni-essen.de/lx2.1.55 SimpleRequest $r -caching 1 \ -url http://swt.wi-inf.uni-essen.de/lx2.1.55 assert "$r getContentLength" 522411 } test "load a validated large file" { SimpleRequest $r -caching 1 \ -url http://swt.wi-inf.uni-essen.de/lx2.1.55 assert "$r getContentLength" 522411 } test "pure loading test without cache" { SimpleRequest $r -caching 0 \ -url http://swt.wi-inf.uni-essen.de/lx2.1.55 assert "$r getContentLength" 522411 } test "redirect" { SimpleRequest $r -caching 1 \ -url http://mohegan.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 } test "authentication" { SimpleRequest $r -caching 1 \ -url http://nestroy.wi-inf.uni-essen.de/cgi-bin/w3-msql/Forschung/Publikationen/protected/index.html assert "$r getContentLength" 1164 } puts stderr after quit ### request joining ### load to file depending on content type library/xotcl/apps/comm/get-regression.xotcl000066400000000000000000000121701242365656200215750ustar00rootroot00000000000000#!/usr/bin/env tclsh package require XOTcl 2.0; namespace import -force xotcl::* package require xotcl::comm::httpAccess package require xotcl::trace persistentCache clear proc assert {f r} { set got [eval $f] if {$got ne $r } { puts stderr "assertion failed: \[$f\] == $r (got $got)" quit } else { puts stderr "OK $r = $f" } } proc head msg { puts stderr "" puts stderr "---------------------------- $msg" } proc test {msg cmd} { set r [Object autoname r] head $msg if {[catch {eval $cmd} msg]} { puts stderr "ERROR: $::errorInfo" quit } $r destroy } Object userPwd userPwd proc user {u} { my set user $u } userPwd proc show {realm userVar pwVar} { upvar $userVar u $pwVar pw set u [my set user] set pw jogl return 1 } # test "simple request" { # SimpleRequest $r -caching 0 \ # -url http://localhost/index.html # assert "$r getContentLength" 81 # } # test "simple request" { # SimpleRequest $r -caching 1 \ # -url http://localhost/index.html # assert "$r getContentLength" 81 # } # test "simple request" { # SimpleRequest $r -caching 1 \ # -url http://localhost/index.html # assert "$r getContentLength" 81 # } # test "simple request" { # persistentCache invalidate \ # http://localhost/index.html # SimpleRequest $r -caching 1 \ # -url http://localhost/index.html # assert "$r getContentLength" 81 # } # test "simple request" { # SimpleRequest $r -caching 0 \ # -url http://localhost/muster-d1klausur.ps # assert "$r getContentLength" 163840 # } proc parallelRequests {urls} { JoinSink joinsink -requests [llength $urls] set i 0 foreach url $urls { TimedMemorySink sink$i set t$i [Access [Access autoname a] -url $url \ -informObject [list joinsink sink$i] \ -caching 0] incr i } set i 0 foreach url $urls { sink$i reportTimes;incr i} joinsink destroy } # parallelRequests { # http://localhost/muster-d1klausur.ps # http://localhost/muster-d1klausur2.ps # } # quit foreach c {0 1 2 2} { test "caching $c" { SimpleRequest $r -caching $::c \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 } } test "simple request" { SimpleRequest $r -caching 0 \ -url http://nestroy.wi-inf.uni-essen.de/Raumplan.html assert "$r getContentLength" 662 } test "simple request, larger file" { SimpleRequest $r -caching 0 \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 } test "use cache" { SimpleRequest $r -caching 1 \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 } test "specify filename, use cache and validate request" { persistentCache invalidate \ http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps exec rm -f test.ps SimpleRequest $r -caching 1 -fileName test.ps \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 assert "file size test.ps" 349970 assert {lindex [exec md5sum test.ps] 0} c6029c987e841430f3ca9bab157da12f } test "specify filename, and use cache and a validated file" { exec rm -f test.ps SimpleRequest $r -caching 1 -fileName test.ps \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 assert "file size test.ps" 349970 assert {lindex [exec md5sum test.ps] 0} c6029c987e841430f3ca9bab157da12f } test "specify filename, and do not use cache" { exec rm -f test.ps SimpleRequest $r -fileName test.ps -caching 0 \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 assert "file size test.ps" 349970 assert {lindex [exec md5sum test.ps] 0} c6029c987e841430f3ca9bab157da12f } test "specify filesink and use cache; no copying neccesary" { persistentCache invalidate \ http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps SimpleRequest $r -useFileSink 1 -caching 1 \ -url http://nestroy.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 assert "file size test.ps" 349970 } test "load a large file to the cache" { persistentCache clearEntry http://swt.wi-inf.uni-essen.de/lx2.1.55 SimpleRequest $r -caching 1 \ -url http://swt.wi-inf.uni-essen.de/lx2.1.55 assert "$r getContentLength" 522411 } test "load a validated large file" { SimpleRequest $r -caching 1 \ -url http://swt.wi-inf.uni-essen.de/lx2.1.55 assert "$r getContentLength" 522411 } test "pure loading test without cache" { SimpleRequest $r -caching 0 \ -url http://swt.wi-inf.uni-essen.de/lx2.1.55 assert "$r getContentLength" 522411 } test "redirect" { SimpleRequest $r -caching 1 \ -url http://mohegan.wi-inf.uni-essen.de/Lv/muster-d1klausur.ps assert "$r getContentLength" 349970 } test "authentication" { SimpleRequest $r -caching 1 \ -url http://nestroy.wi-inf.uni-essen.de/cgi-bin/w3-msql/Forschung/Publikationen/protected/index.html assert "$r getContentLength" 1164 } puts stderr after quit ### request joining ### load to file depending on content type library/xotcl/apps/comm/link-checker.xotcl000077500000000000000000000110351242365656200212010ustar00rootroot00000000000000#!/usr/bin/env tclsh # -gn july 2000 package require XOTcl 2.0; namespace import -force xotcl::* @ @File { description { A simple link checking program that checks in parallel pages of a site.

Options:

-url Start-URL
-foreign 0 or 1, specifies, whether foreign links of local pages should be checked (default 1)
-local A string match pattern to decide which url should be treated as local e.g. -local *wu-wien.ac.at/* Per default the locality filter ist set to the name of the host followed by '/*'
-restrict 0 or 1, sets the locality filter to the subtree implied by the URL
-verbose 0 or 1 or 2, verbosity level (default 0)
} } if {$tcl_version<8.2} { puts stderr "This script requires Tcl 8.2 or newer" exit -1 } set opt(-url) http://localhost:8000/ set opt(-url) http://nm.wu-wien.ac.at/Lehre/ set opt(-verbose) 0; # 0, 1 (show check), or 2 (show ignore) set opt(-foreign) 1; # 0, 1 (check foreign links on local pages) set opt(-restrict) 0; # 0, 1 ## per default, lc checks the array set opt $argv if {$opt(-restrict)} { regexp {://(.*)$} $opt(-url) _ opt(-local) set opt(-local) [string trimright $opt(-local) /]* puts stderr "locality filter set to '$opt(-local)'" } if {![info exists opt(-local)]} { regexp {http://([^/:]+)} $opt(-url) _ opt(-local) append opt(-local) /* puts stderr "locality filter set to '$opt(-local)'" } #package require xotcl::package; package verbose 1 package require xotcl::comm::httpAccess package require xotcl::trace proc printError {m} {} Class Checker -superclass ParallelSink \ -parameter {verbose foreign local} Checker array set ref {A HREF IMG SRC} Checker set ref_re {[[:space:]]*=[[:space:]]*([[:graph:]]+)} Checker instproc report {msg {level 1}} { my instvar verbose if {$verbose>$level} {puts stderr $msg} return 0 } Checker instproc isLocal {url} { my instvar local string match *://$local $url } Checker instproc isToCheck {url request methodvar} { my instvar foreign upvar $methodvar method if {![regexp -nocase {http://([^/:]+)} $url _ host]} { return [my report "ignored, no http: $url"] } set method GET if {![my isLocal $url]} { if {$foreign} { #puts stderr "parenturl: [$request set parentUrl] -> [my isLocal [$request set parentUrl]]" if {[$request info vars parentUrl] ne "" && ![my isLocal [$request set parentUrl]]} { return [my report "ignored, nor local: $url"] } else { set method HEAD } } else { return [my report "ignored, nor local: $url"] } } if {[regexp -nocase {[.](gif|jpg|ps|pdf|gz)$} $url]} { set method HEAD #return [my report "ignored due to extension: $url"] } return 1 } Checker instproc checkLink {request link} { set resolved [resolve $link [$request set url]] if {[my isToCheck $resolved $request method]} { my instvar checked if {![info exists checked($resolved)]} { my report "checking .......... $resolved" 0 set checked($resolved) 1 my scheduleRequest $method $resolved [$request set url] } else { #puts stderr "already checked $resolved" } } } Checker instproc checkText {request} { if {![my isLocal [$request set url]]} return [self class] instvar ref ref_re set content [$request getContent] set start 0 while {[regexp -nocase -indices -start $start -- \ {<(A|IMG)([^>]*?)} $content a b c]} { set elem [string toupper \ [string range $content [lindex $b 0] [lindex $b 1]]] set attribs [string range $content [lindex $c 0] [lindex $c 1]] #regsub -all {[\n ]+} $attribs " " attribs if {[regexp -nocase $ref($elem)$ref_re $attribs _ i]} { my checkLink $request [string trim $i '\"] } set start [lindex $c 1] } } Checker instproc endCb r { #showObj $r switch [$r set contentType] { text/html {my checkText $r} } next } Checker instproc cancelCb r { #$r showVars puts stderr "ERROR in page [$r set parentUrl]" puts stderr " Link: [$r set url]" puts stderr " cause [$r set errormsg]\n" next } Checker csink \ -verbose $opt(-verbose) -foreign $opt(-foreign) -local $opt(-local) \ -sinkClass MemorySink -httpVersion 1.0 -maxsimultaneous 30 csink requests $opt(-url) puts stderr "sumbytes: [csink set sumbytes] requests: [csink set numrequests]" csink destroy library/xotcl/apps/comm/secure-webclient.xotcl000077500000000000000000000027131242365656200221050ustar00rootroot00000000000000#!/usr/bin/env tclsh package require XOTcl 2.0; namespace import -force xotcl::* @ @File { description { A sample secure web client that queries an secure web-server. It needs to be adopted with different https URLs for testing... } } # package require xotcl::comm::httpAccess package require xotcl::comm::ftp package require xotcl::trace #set version 1.1 set hostport localhost:8086 set http_server http://localhost:8086/ set https_server https://localhost:8443/ set slowURL "http://quote.yahoo.com/q?s=^DJI&d=1d" set ftpURL "ftp://mohegan.wi-inf.uni-essen.de/welcome.msg" set secureURL "https://wawog.wi-inf.uni-essen.de/" set secureURL "https://test17.wu-wien.ac.at:1234/" proc printError msg {puts stderr !!!$msg!!!} puts "\nTrying to get a secure page ..... <$https_server>" SimpleRequest r0 -url $https_server puts stderr "\n content = {[r0 getContent]}" puts -nonewline "\nTrying to load image logo-100.jpg ... (not secure)" SimpleRequest r2 -url $http_server/logo-100.jpg if {[r2::sink set contentLength] == 1706} { puts "suceeded! Loaded 1706 bytes!" } else { puts "failed! Loaded [r2::sink set contentLength] (!= 1706) bytes" exit } puts -nonewline "\nTrying to load image logo-100.jpg secure ... " SimpleRequest r1 -url $https_server/logo-100.jpg if {[r1::sink set contentLength] == 1706} { puts "suceeded! Loaded 1706 bytes!" } else { puts "failed! Loaded [r1::sink set contentLength] (!= 1706) bytes" exit } exit library/xotcl/apps/comm/secure-webserver.xotcl000077500000000000000000000037161242365656200221410ustar00rootroot00000000000000#!/usr/bin/env tclsh package require XOTcl 2.0; namespace import -force xotcl::* @ @File { description { This small secure web server that provides its documents via SSL (https, port 8443) and plain http (port 8086).
This file requires TLS. If you experice problems with versions obtained from the Web, contact gustaf.neumann@wu-wien.ac.at for a patch. } } # # We load the following packages: # package require xotcl::trace package require xotcl::comm::httpd # # we set the default for document root to ../../src/doc and port to 8443 # set root ../../doc set port 8443 set class Httpsd set cb callback ;# use this for triggering the callbacks #set cb "" foreach {att value} $argv { switch -- $att { -root {set root $value} -port {set port $value} -class {set class $value} -cb {set cb $value} } } # # now we can start the web-server instance with these settings # Httpd h0 -port 8086 -root $root $class h1 -port $port -root $root -infoCb $cb \ -requestCert 1 -requireValidCert 0 # Start des HTTP-Servers mit port 8086 und dem angegebenen Verzeichnis #Httpd h2 -port 9086 -root $root \ -mixin {Responder BasicAccessControl} \ -addRealmEntry test {test test} -protectDir test "" {} Object callback callback proc error {chan msg} { puts stderr "+++TLS/$chan: error: $msg" } callback proc verify {chan depth cert rc err} { array set c $cert if {$rc != "1"} { puts stderr "+++TLS/$chan: verify/$depth: Bad Cert: $err (rc = $rc)" } else { puts stderr "+++TLS/$chan: verify/$depth: $c(subject)" } return $rc } callback proc info {chan state minor msg} { # For tracing #upvar #0 tls::$chan cb #set cb($major) $minor #puts stderr "+++TLS/$chan: $major/$minor: $state" puts stderr "+++TLS/$chan $state $minor: $msg" } callback proc unknown {option args} { return -code error "bad option \"$option\": must be one of error, info, or verify" } # # and finally call the event loop... # vwait forever library/xotcl/apps/comm/server.key000066400000000000000000000015671242365656200176150ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDVg+HdA4RAKyYRaxKRSdAw7mQpephZslIf/ZR7Oe3RKZjxu7Sj PYG56GSc8y6hiaKuGnDmJcEGXZCXWNtUuACVsNlFrlVbGSEfAbXbz6H4eaaX2d2P KzrjOc//8+5emaFZRelVTYY4sWNLcCuWV2FIhwMJMblYieXM6iEQg4XzdQIDAQAB AoGAVdY2OC8QvOdb34bHKSeejf1YwSArHWxF/dxpE/0e8YaimRQYM8QnYgDeagaN yZ1WjF3O44dsCU4WMfIkAvQSL19RLSgT+jPb3Uu9Aotwmg4x+55BKctRphXKiIfu +a0IfAFDIt8FMRS08AoSB6eBgOBlhTZM2Y0IuC6QTqaBcwUCQQD5XSiEzh44uMOl N4FbO0MI2NC3pbPg5u1fEJtIawoYOYxbeGHrlOYO2ZN3ZP6+2g5V0kX5tMMeObhE qQLlmNETAkEA2zJ/Tk3IE5ByEBcGjG26nJ1zLlY13NaiIg+AoSP+7rHg5TPhoQp1 VVwcqd4ZWCyqgYDFl2TsiiZwKcEIFJBCVwJBANoLpZSLD04V8a2UXV5C8ZjYzZjo IeP0yXco9D9cqZUJLTwGhckTiB9QDWyHOWH1FjfhCCMS9tKFMiWHi+rrt1UCQQDC rlvxURX1gmI8NicjzEVk2la1fe5C4QKJW9lzxUOj/qpvB6BK5r4FfVUb7d32uV0K vjNAXmvT24XdH8usb9/rAkBR1sqUkqwwm0CSe0Rz9IhSlkwWlmvEzSmEguqrd7Zb rbYJ9NTnV1uFSAX9vcU7lDRp69il6XmyhOL06FYLRaNa -----END RSA PRIVATE KEY----- library/xotcl/apps/comm/server.pem000066400000000000000000000067641242365656200176120ustar00rootroot00000000000000issuer :/C=DE/ST=NRW/L=Essen/O=University of Essen/OU=SWT/CN=Fredj Dridi/Email=Fredj.Dridi@uni-essen.de subject:/C=DE/ST=NRW/L=Essen/O=University of Essen/OU=SWT/CN=tp600e.wi-inf.uni-essen.de/Email=dridi@tp600e.wi-inf.uni-essen.de serial :01 Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: md5WithRSAEncryption Issuer: C=DE, ST=NRW, L=Essen, O=University of Essen, OU=SWT, CN=Fredj Dridi/Email=Fredj.Dridi@uni-essen.de Validity Not Before: Apr 28 08:21:19 2000 GMT Not After : Apr 28 08:21:19 2001 GMT Subject: C=DE, ST=NRW, L=Essen, O=University of Essen, OU=SWT, CN=tp600e.wi-inf.uni-essen.de/Email=dridi@tp600e.wi-inf.uni-essen.de Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:d5:83:e1:dd:03:84:40:2b:26:11:6b:12:91:49: d0:30:ee:64:29:7a:98:59:b2:52:1f:fd:94:7b:39: ed:d1:29:98:f1:bb:b4:a3:3d:81:b9:e8:64:9c:f3: 2e:a1:89:a2:ae:1a:70:e6:25:c1:06:5d:90:97:58: db:54:b8:00:95:b0:d9:45:ae:55:5b:19:21:1f:01: b5:db:cf:a1:f8:79:a6:97:d9:dd:8f:2b:3a:e3:39: cf:ff:f3:ee:5e:99:a1:59:45:e9:55:4d:86:38:b1: 63:4b:70:2b:96:57:61:48:87:03:09:31:b9:58:89: e5:cc:ea:21:10:83:85:f3:75 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:D2:B8:E3:2D:A2:5A:22:78:3A:38:F1:1F:F5:7C:AD:8D:A6:C4:41:0F X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication, Microsoft Server Gated Crypto, Netscape Server Gated Crypto X509v3 Basic Constraints: critical CA:FALSE Signature Algorithm: md5WithRSAEncryption 38:b5:a1:1c:93:c4:aa:6e:3f:d6:ea:11:7f:7b:2b:11:db:7b: 22:b3:d2:a8:b3:f3:20:64:6a:25:b4:fe:0d:ac:12:49:a0:6e: 6d:ef:fb:99:a2:7c:bc:50:b0:eb:42:ef:0a:32:fa:a9:69:e0: 11:10:9b:00:05:15:97:59:ac:dc:5f:f2:cd:81:28:3e:e6:96: 86:f4:d7:99:71:52:c3:ca:0f:4a:48:d8:66:b0:da:e8:d1:45: 84:c4:12:b2:43:ec:63:b6:25:e8:0a:5a:4c:fb:e2:ec:03:36: 6f:cd:f9:2a:5c:52:ba:02:29:92:f5:bf:c4:96:ff:9e:ed:a3: cf:02 -----BEGIN CERTIFICATE----- MIIDIjCCAougAwIBAgIBATANBgkqhkiG9w0BAQQFADCBljELMAkGA1UEBhMCREUx DDAKBgNVBAgTA05SVzEOMAwGA1UEBxMFRXNzZW4xHDAaBgNVBAoTE1VuaXZlcnNp dHkgb2YgRXNzZW4xDDAKBgNVBAsTA1NXVDEUMBIGA1UEAxMLRnJlZGogRHJpZGkx JzAlBgkqhkiG9w0BCQEWGEZyZWRqLkRyaWRpQHVuaS1lc3Nlbi5kZTAeFw0wMDA0 MjgwODIxMTlaFw0wMTA0MjgwODIxMTlaMIGtMQswCQYDVQQGEwJERTEMMAoGA1UE CBMDTlJXMQ4wDAYDVQQHEwVFc3NlbjEcMBoGA1UEChMTVW5pdmVyc2l0eSBvZiBF c3NlbjEMMAoGA1UECxMDU1dUMSMwIQYDVQQDExp0cDYwMGUud2ktaW5mLnVuaS1l c3Nlbi5kZTEvMC0GCSqGSIb3DQEJARYgZHJpZGlAdHA2MDBlLndpLWluZi51bmkt ZXNzZW4uZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANWD4d0DhEArJhFr EpFJ0DDuZCl6mFmyUh/9lHs57dEpmPG7tKM9gbnoZJzzLqGJoq4acOYlwQZdkJdY 21S4AJWw2UWuVVsZIR8BtdvPofh5ppfZ3Y8rOuM5z//z7l6ZoVlF6VVNhjixY0tw K5ZXYUiHAwkxuViJ5czqIRCDhfN1AgMBAAGjZzBlMB8GA1UdIwQYMBaAFNK44y2i WiJ4OjjxH/V8rY2mxEEPMDQGA1UdJQQtMCsGCCsGAQUFBwMBBggrBgEFBQcDAgYK KwYBBAGCNwoDAwYJYIZIAYb4QgQBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEE BQADgYEAOLWhHJPEqm4/1uoRf3srEdt7IrPSqLPzIGRqJbT+DawSSaBube/7maJ8 vFCw60LvCjL6qWngERCbAAUVl1ms3F/yzYEoPuaWhvTXmXFSw8oPSkjYZrDa6NFF hMQSskPsY7Yl6ApaTPvi7AM2b835KlxSugIpkvW/xJb/nu2jzwI= -----END CERTIFICATE----- library/xotcl/apps/comm/test-tls-client.xotcl000077500000000000000000000042301242365656200216740ustar00rootroot00000000000000#!/usr/bin/env tclsh # -*- Tcl -*- package require XOTcl 2.0; namespace import ::xotcl::* package require tls proc bgerror {err} { global errorInfo puts stderr "BG Error: $errorInfo" } array set opts { -port 8443 -host localhost } array set opts $argv proc readCB {CHAN} { #puts stderr "*** CALL: readCB $CHAN" if {![eof $CHAN]} { set rData [gets $CHAN] puts stderr "\nREADING ..." puts stderr "------------------------------------------------------" puts stderr <$rData> puts stderr "------------------------------------------------------" #fileevent $CHAN writable [list writeCB $CHAN] } else { catch {close $CHAN} puts stderr "\nSocket ($CHAN) is closed." exit } } proc writeCB {CHAN} { #puts stderr "*** CALL: writeCB $CHAN" #puts stderr "fileevent $CHAN writable {}" fileevent $CHAN writable {} set wData "GET / HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n\r\n" #set wData "GET\n" puts -nonewline $CHAN $wData flush $CHAN #puts stderr "\nfileevent $CHAN readable [list readCB $CHAN]" fileevent $CHAN readable [list readCB $CHAN] } puts stderr "\n\n\n~~~~~~~~~~~~ Trying $opts(-host):$opts(-port)" # # Create socket # set chan [socket -async $opts(-host) $opts(-port)] tls::import $chan -command callback -cafile cacert.pem -certfile client.pem -server 0 -keyfile client.key -request 1 -require 1 puts stderr "setting channel to auto binary" fconfigure $chan -translation {auto binary} puts stderr "fileevent $chan writable [list writeCB $chan]" fileevent $chan writable [list writeCB $chan] Object callback callback proc error {chan msg} { puts stderr "+++TLS/$chan: error: $msg" } callback proc verify {chan depth cert rc err} { array set c $cert if {$rc != "1"} { puts stderr "+++TLS/$chan: verify/$depth: Bad Cert: $err (rc = $rc)" } else { puts stderr "+++TLS/$chan: verify/$depth: $c(subject)" } return $rc } callback proc info {chan state minor msg} { puts stderr "+++TLS/$chan $state $minor: $msg" } callback proc unknown {option args} { my showCall return -code error "bad option \"$option\": must be one of error, info, or verify" } vwait forever library/xotcl/apps/comm/test-tls-server.xotcl000077500000000000000000000042041242365656200217250ustar00rootroot00000000000000#!/usr/bin/env tclsh # -*- Tcl -*- # # sample secure server that reflect all incoming data to the client # It uses tls1.3 package of Matt Newman # Fredj Dridi package require XOTcl 2.0; namespace import ::xotcl::* package require tls proc bgerror {err} { global errorInfo puts stderr "BG Error: $errorInfo" } # # Sample callback - just reflect data back to client # proc reflectCB {chan {verbose 0}} { puts stderr "\n*** reflectCB $chan $verbose" fconfigure $chan -translation {auto crlf} set data {} if {[catch {set n [::gets $chan data]} msg]} { set error $msg puts stderr "\nEOF ($data)" catch {close $chan} return 0 } puts stderr n=<$n> if {$verbose && $data ne ""} { puts stderr "data=<$data>" } if {[eof $chan]} { ;# client gone or finished puts stderr "\nEOF" close $chan ;# release the servers client channel return } #puts -nonewline $chan $data #flush $chan } proc acceptCB { chan ip port } { puts stderr "\n*** acceptCB $chan $ip $port" tls::import $chan -cafile cacert.pem -certfile server.pem \ -server 1 -request 1 -require 1 -keyfile server.key -command callback if {![tls::handshake $chan]} { puts stderr "\nHandshake pending" return } array set cert [tls::status $chan] puts stderr "\n" parray cert fileevent $chan readable [list reflectCB $chan 1] } set chan [socket -server acceptCB 8443] puts stderr "Server waiting connection on $chan (8443)" ## Sample Callback that gives SSL information Object callback callback proc error {chan msg} { puts stderr "+++TLS/$chan: error: $msg" } callback proc verify {chan depth cert rc err} { array set c $cert if {$rc != "1"} { puts stderr "+++TLS/$chan: verify/$depth: ** Bad Cert **: $err (rc = $rc)" } else { puts stderr "+++TLS/$chan: verify/$depth: $c(subject)" } return $rc } callback proc info {chan state minor msg} { puts stderr "+++TLS/$chan $state $minor: $msg" } callback proc unknown {option args} { my showCall return -code error "bad option \"$option\": must be one of error, info, or verify" } # Go into the eventloop vwait forever library/xotcl/apps/comm/webclient.xotcl000077500000000000000000000004421242365656200206160ustar00rootroot00000000000000#!/usr/bin/env tclsh # -*- Tcl -*- package require XOTcl 2.0; namespace import ::xotcl::* @ @File {description {For a sample webclient, see packages/comm/xocomm.test}} package require xotcl::comm::httpAccess set hostport localhost:8086 SimpleRequest r0 -url http://$hostport/logo-100.jpg library/xotcl/apps/comm/webserver.xotcl000077500000000000000000000073251242365656200206550ustar00rootroot00000000000000#!/usr/bin/env tclsh array set opts {-root ../../doc -port 8086 -protected-port 9096 -pkgdir .} array set opts $argv lappend auto_path $opts(-pkgdir) #if {$::tcl_platform(platform) eq "windows"} {lappend auto_path .} package require XOTcl 2.0; namespace import -force ::xotcl::* proc ! string { set f [open [::xotcl::tmpdir]/log w+]; puts $f "[clock format [clock seconds]] $string" close $f } @ @File { description { This small demo program starts two different webservers:

  • Firstly, it provides a sample web server that povides the documents in ../../src/doc (or the files specified with -root) at port 8086 (or at the port specified via the -port option) as unprotected resources.

  • Secondly, it starts a second webserver with basic access control (it accepts test/test as user/password) on port 9096 (or on the port specified via -protected-port). If it receives an request for an resource named "exit", it terminates. For all other requests it returns actual information about the user and the issued request.
To see, how it works, contact it e.g. from netscape. } } ! "#### webserver starting" # We load the following packages: # #::xotcl::package import ::xotcl::comm::httpd package require xotcl::comm::httpd ! "#### httpd required" # now we can start the web-server instance with these settings # Httpd h1 -port $opts(-port) -root $opts(-root) @ Httpd h1 {description "unprotected web server"} ! "#### h1 started" # specialized worker, which executes tcl commands in web pages @ Class SpecializedWorker { description { Specialized worker that can be passed to any webserver }} Class SpecializedWorker -superclass Httpd::Wrk @ SpecializedWorker instproc respond {} { description { This method handles all responses from the webserver to the client. We implent here "exit", and we return the information about the actual request and user in HTML format for all other requests.

This method is an example, how to access on the server side request specific infomation. }} SpecializedWorker instproc respond {} { if {[my set resourceName] eq "exit"} { set ::forever 1 #my showVars #my set version 1.0;### ???? #puts stderr HERE } # return for all other requests the same response foreach {a v} [my array get meta] { append m $a$v\n } set content {

Request Info

method:[my set method]
resource:[my set resourceName]
user:[my set user]
version:HTTP/[my set version]
response port:[my set port]
request comes from:[my set ipaddr]

Request Header Fields

$m
} set c [subst $content] my replyCode 200 [self]::connection puts "Content-Type: text/html" [self]::connection puts "Content-Length: [string length $c]\n" [self]::connection puts-nonewline $c my close } @ Httpd h2 { description "Web server with basic authentication using the specialied worker"} if {[info exists env(USER)]} { set USER "$env(USER)" } elseif {[info exists env(USERNAME)]} { set USER "$env(USERNAME)" } else { set USER unknown } if {$::tcl_platform(platform) eq "windows"} { set USER unknown } Httpd h2 -port $opts(-protected-port) -root $opts(-root) \ -httpdWrk SpecializedWorker \ -mixin Httpd::BasicAccessControl \ -addRealmEntry test "u1 test $USER test" -protectDir test "" {} ! "#### h2 started" # # and finally call the event loop... # vwait forever library/xotcl/apps/utils/000077500000000000000000000000001242365656200157715ustar00rootroot00000000000000library/xotcl/apps/utils/xo-daemon000077500000000000000000000122071242365656200176100ustar00rootroot00000000000000#!/usr/bin/env tclsh package require XOTcl 2.0; namespace import -force xotcl::* @ @File { description { This script can be used to start/stop/restart xotcl daemons and maintains the process IDs, log files and means for easy restart. <@p> It receives as first parameter the name of the xotcl script to be executed followed by the desired action and optional parameters. The specified action can be <@UL> <@LI> <@EM>start: the specified script is started in the background, an entry to restart is generated in the run-directory as well as the process id of the started script. In addition a logfile is created in the log directory. If the start of the script fails, the error messages are shown. <@LI> <@EM>startall: all scripts that were started before via this script, are started <@LI> <@EM>stop terminates te specified script. <@LI> <@EM>stopall terminates all scripts started via this command <@LI> <@EM>restart tries to restart the specified script. The optional parameters are: <@UL> <@LI> <@EM>-logir specifies the directory for logging. The default is ~/.xotcl/log. <@LI> <@EM>-rundir specifies the directory where the information about the running processes is kept. The default is ~/.xotcl/run. } authors { Gustaf Neumann, Gustaf.Neumann@wu-wien.ac.at } date "[::xotcl::rcs date {$Date: 2006/02/18 22:17:32 $}]" } array set opt [list \ -rundir $::xotcl::confdir/run \ -logdir $::xotcl::confdir/log \ ] if {$argc < 2} { puts "Usage:\n\t$argv0 \ ?options?\nOptions:\n\ \t-logdir dirname (default: $::opt(-logdir))\n\ \t-rundir dirname (default: $::opt(-rundir))\n" exit -1 } lassign $argv daemon action array set opt [lreplace $argv 0 1] # The daemon scripts should be # - location independent (if the script assumes to be started from # a certain directory, it should cd to it) # - parameter less (required for restart, startall, etc.) # # configuration set ::kill /bin/kill set ::ps /bin/ps set ::ln /bin/ln set ::sleep /bin/sleep set ::xotclsh /usr/bin/xotclsh Class Daemon -parameter progname Daemon instproc readfile {n} { set f [open $n r] set c [read $f] close $f return [string trim $c \n] } Daemon instproc init {} { if {![file isdirectory $::opt(-rundir)]} {file mkdir $::opt(-rundir)} if {![file isdirectory $::opt(-logdir)]} {file mkdir $::opt(-logdir)} } Daemon instproc progname name { set n [file tail $name] set n [file rootname $n] [self] set pidfile $::opt(-rundir)/$n.pid [self] set logfile $::opt(-logdir)/$n.log [self] set shortname $n [self] set progname $name } Daemon instproc report string { puts stderr "[[self] set shortname]: $string" } Daemon instproc running pid { set r [catch {exec $::ps -h $pid} msg] #if {$r} { [self] report "msg=$msg" } #[self] report "running returns $r" return [expr {!$r}] } Daemon instproc kill {sig pid} { #[self] report "kill $sig $pid" exec $::kill $sig $pid } Daemon instproc start {} { [self] instvar pidfile logfile progname if {[file exists $pidfile]} { set pid [[self] readfile $pidfile] [self] report "seems already running $pid" if {[[self] running $pid]} { [self] report "... no need to restart" return } else { [self] report "... but disappeared $pid" } } set linkname $::opt(-rundir)/[file tail $progname] if {[string match *~* $linkname]} { # file dirname ~ -> requires env(HOME) regsub ^~ $linkname [file dirname ~]/$::env(USER) linkname } if {$progname != $linkname} { #puts stderr "exec $::ln -sf $progname $linkname" exec $::ln -sf $progname $linkname } set pid [exec $::xotclsh $progname >>& $logfile &] set F [open $pidfile w] puts $F $pid close $F exec sleep 1 if {![[self] running $pid]} { [self] report "start of $pid failed" set size [file size $logfile] set F [open $logfile] if {$size > 500} {seek $F [expr {$size-500}]} puts [read $F] close $F } else { [self] report "started $pid" } } Daemon instproc stop {} { [self] instvar pidfile logfile if {[file exists $pidfile]} { set pid [[self] readfile $pidfile] #[self] report "Got PID $pid" if {[[self] running $pid]} { [self] report "stopping $pid" [self] kill -KILL $pid #if {[running $pid]} {kill -KILL $pid} } else { [self] report "not running $pid" } file delete $pidfile } else { [self] report "was not started before" } } Daemon instproc restart {} { [self] stop [self] start } Daemon instproc stopall {} { foreach pidfile [glob -nocomplain $::opt(-rundir)/*.PID] { set n [file rootname [file tail $pidfile]] [self] configure -progname $n -stop } } Daemon instproc startall {} { foreach file [glob -nocomplain $::opt(-rundir)/*] { if {[string match *.PID $file]} continue [self] configure -progname $file -start } } Daemon d -init -progname $daemon -$action library/xotcl/apps/utils/xo-whichPkg000077500000000000000000000010721242365656200201070ustar00rootroot00000000000000#!/usr/bin/env tclsh package require XOTcl 2.0; namespace import -force xotcl::* @ @File { description { A small sample script to show which package is loaded from where when a target package is loaded. <@p> Usage: "xo-whichPkg -pkg PACKAGENAME" } authors { Fredj Dridi dridi@nestroy.wi-inf.uni-essen.de } date "[::xotcl::rcs date {$Date: 2006/02/18 22:17:32 $}]" } array set opts { -pkg xotcl::comm::httpd } array set opts $argv package require xotcl::package package verbose 1 puts stderr [package require $opts(-pkg)] library/xotcl/doc/000077500000000000000000000000001242365656200144335ustar00rootroot00000000000000library/xotcl/doc/Announce-0.9.3000066400000000000000000000037021242365656200165730ustar00rootroot00000000000000Announcing XOTcl 0.9.3 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. RECENT CHANGES relative to 0.9 are: - A "more or less" TEA compliant build process and its directory structure (aligend it as far as possible to Tcl, sample extension and other extensions) * We started building an XOTcl stub lib. * We separated xotcl from its library/applications (there is an xotcl- directory in the full distribution that just builds, installs, etc. the XOTcl libraries ... this can for instance be used in other distributions using and shipping XOTcl, but not using the shells). * The full distribution still builds shells, but they are using "package require" to load the XOTcl libraries now. * For building xowish (the XOTcl Tk shell) you do only require an installed Tk version on Unix now. - On Windows we still use nmake due to problems with VCC 6.0 in all TEA compliant extensions we have checked, but we use a completely new build process (more or less one-to-one the Unix build process). - A few minor bugfixes. - We have tested this release on platforms we have access to (Linux, Win NT, Solaris). Please inform us, if there are some problems on your platform so that we can eliminate them in future releases. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-0.9.4000066400000000000000000000065651242365656200166060ustar00rootroot00000000000000Announcing XOTcl 0.9.4 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. CHANGES relative to 0.9.3 are: - fixes: * separate reference counting for XOTcl objects and classes to avoid references from tcl_obj's internal representation to freed memory * fixes for AOL-Server (many thanks to Zoran Vasiljevic for that and for his great help during fixing the problems above) * improved portability: horrible macro substitution bug, when USE_ALLOCA or USE_MALLOC was specified; these are used for some platforms such as AIX * fixing path search for XOTcl library (bug was introduced in 0.9.3 in connection with TEA compliance) * fixed a bug in copy/move (many thanks for Artur Trzewik for reporting this) in connection with constructors (constructors are not called automatically in copy/move operations anymore) * various small fixes in libraries (e.g. HTTP support, generation of pkgIndex-files, using "my", ...) - enhancements: * new global command "my": one can use now: Class Counter -parameter {{n 0}} Counter instproc ++ {} { my incr n } Counter instproc print {} { puts "actual value: [my n]"} Counter c1 c1 ++ c1 print In earlier versions it was neccessary to use "[self]" instead of "my". This change reduces the number of special characters in XOTcl programs. "[self]" will continue to be supported by XOTcl. * extended semantics of "new" a) Object new ... b) Object new -childof ... c) Object new -volatile ... d) Object new -volatile -childof ... where (a) creates now "global" objects not in the global namespace anymore, but in the ::xotcl namespace, (b) can be used to create objects as child of other objects (e.g. as children of the actual object [self]; the objects are deleted, when the specified object is deleted). (c) creates are "global" objects as in (a), but they are deleted, when the scope of the current tcl-proc/object-proc/instproc is left and (d) is a combination of (b) and (c). - optimizations: * XOTcl is trying to keep Tcl_Objs of type tclCmdNameType instead of converting it to XOTclObjectType agressively. * A patch for Tcl 8.4a3 is available from the XOTcl web site for performance hungry applications to provide special byte-code-support for xotcl, that yields for classical benchmark programs up to 40%. This patch is not likely to be integrated into the general Tcl core in the new or distant future. For more details, please consult the ChangeLog MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.0000066400000000000000000000025731242365656200164270ustar00rootroot00000000000000Announcing XOTcl 1.0 ******************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. CHANGES relative to 0.9.4 are: - fixes: * various small fixes for compilation with tcl8.4.* * fixed some more memory-leaks that showed up in connection with multi-threading (many thanks to Zoran Vasiljevic for his great help) * removed a few panics() by tcl errors (important for servers) * various small fixes in Httpd-code * fixes in filter-search mechanism * the general cleanup method __exitHandler (called on interpreter exits and on thread exits) defined now as a proc of Object - optimizations: * speedup during recreation of objects For more details, please consult the ChangeLog MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.0.1000066400000000000000000000027331242365656200165640ustar00rootroot00000000000000Announcing XOTcl 1.0.1 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. CHANGES relative to 1.0 are: - fixes: * fixed installation bug on windows (when installed on drives different to c:) * fixes for aolserver 3.5.* * fix for "object info default arg var" in connection with filters * Serializer handles filter guards now - new functionality and improvements: * improved recreate semantics, makes it easier to overload recreation logic. * new method "noinit" to create objects without calling the constructor - optimizations: * general speedup (10 to 15%) by using client data of namespace instead of Tcl_AssocData for interpreter state * faster and more flexible Serializer For more details, please consult the ChangeLog MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.0.2000066400000000000000000000040561242365656200165650ustar00rootroot00000000000000Announcing XOTcl 1.0.2 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. CHANGES relative to 1.0.1 are: - fixes: * fixes for copying/moving objects to same name * works out of the box with AOL-server 4.0 (no patch for AOL-server needed) * various small fixes and code cleanup * new directory structure (no nested version numbers) * improved object and class serializer - new functionality and improvements: * new method "ismixin": Test whether the argument is a mixin or instmixin of the object. * new method "hasclass": Test whether the argument is either a mixin or instmixin of the object or if it is on the class hierarchy of the object. This method combines the functionalities of istype and ismixin. * arguments of configure methods can start now with a leading "-" without ambiguity. In previous versions Class C -parameter {name counter} C c1 -name -x -counter 123 the configuration of c1 was interpreted as follows method argc name 0 x 0 counter 1 Starting with XOTcl 1.0.2 arguments can be protected by putting it into a list: C c1 [list -name -x] -counter 123 which is interpreted as follows: method argc name 1 counter 1 For more details, please consult the ChangeLog MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.1.0000066400000000000000000000056311242365656200165640ustar00rootroot00000000000000Announcing XOTcl 1.1.0 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. CHANGES relative to 1.0.2 are: - New functionality and improvements: * Transitive instmixins (thanks to a suggestion of Neophytos Demetriou): The new version supports a tree of mixins (mixins of mixins); when a mixin-class X has instmixins Y registered, and X is registered as instmixin for a class C, the mixin classes Y are applied to C as well. * Conditional mixins (thanks to a suggestion of Neophytos Demetriou): mixins and instmixins can have now guards similar to filters that define when they should be applied * New composition strategy for (filter- and mixin-)guards: when a guard is defined more than once for the same interceptor the most specific guard is used, the others are overridden. * Creation of sub-objects via parameter: XOTcl's parameter method supports now a switch named "-Class" which can be used to create and configure sub-objects (see tutorial for details) * New predefined method: __unknown This method is called, whenever XOTcl references a class, which is not defined yet (e.g.: Class C -superclass UNKNOWNCLASS) This method is used for Zoran's wonderful ttrace package (for aolserver and tcl-Threads) * Improved documentation: - extended tutorial (Meta-Classes, Tk, ...) - beautified tutorial (style files, distinction between literals and placeholders, ...) - fixed hundreds of typos * some speed improvements - fixes: * New build system: - per default no shells are built - new configure switches eg. --with-xotclsh, -with-actiweb --with-all * Fixes for https in connection with some versions of IE * Fixed a few issues with purify and mt (many thanks to Zoran!) * Fixes for proc-scoped variable in connections with mixins * Fixed passing of error codes from constructors * various small fixes and code cleanup - with newer versions of autoconf - various fixes for stubs - improved build under windows - starkit compliance (many thanks to MichaelL@frogware.com) For more details, please consult the ChangeLog MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.1.1000066400000000000000000000032251242365656200165620ustar00rootroot00000000000000Announcing XOTcl 1.1.1 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. CHANGES relative to 1.1.0 are: - Improved portability * for Mac OS X (many thanks to Daniel Steffen) * freebsd 4.9 (many thanks to Marc Spitzer) - configure can run outside of xotcl-tree and can install to a different directory. Example: % mkdir -p /tmp/xotcl/unix % cd /tmp/xotcl/unix % ~/xotcl-1.1.1/unix/configure --with-all % make % make test % make install DESTDIR=/tmp - several fixes and improvements such as * added option --with-tkinclude for configure (only needed, when built with optional --with-xowish) * made gdbm work when compiled with threads enabled * fix for initialization of stubtables in gdbm * fixes for HTTP support (return format of values in HTTP OPTION command made windows explorer hang) * easy provision for redirects in HTTP server For more details, please consult the ChangeLog MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.2.0000066400000000000000000000143011242365656200165570ustar00rootroot00000000000000Announcing XOTcl 1.2.0 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. CHANGES relative to 1.1.1 are: - Qualitative Improvements * improved performance (up to more than 30% faster) * reduced dependency on Tcl internals (stack rewritings removed) * less C-code (code reduction of about 500 lines c code) * defined macros to access Tcl's internal structures Due to the speedup, an emulation of an itcl like language in XOTcl can be faster than itcl itself (see below). - Functional Improvements * new subcommands of self: self callinglevel: returns the scope of the proc/instproc calling the current method (ignoring filters and next) self activelevel: returns the scope of the proc/instproc preceding the current method (might be a next call, ignoring filters) the returned level can be used in uplevel/upvar as first argument * new methods upvar/uplevel "my uplevel set x 1" is a short form of "uplevel [self callinglevel] set x 1" * sub-objects can be called like methods (potential incompatibility) If an object named o has a sub-object q (i.e. o::q) it is now possible to invoke the sub-object via "o q ?method args?". This change makes it possible to - to redefine tcl sub-commands via procs/instprocs by defining the command with subcommands as an object (instead of defining a command as a proc and its subcommands via a large and hard-to-refine switch statement) - to use the same approach for defining subcommands of subcommands (and so on) in the same way; it would be possible to define e.g. the xotcl info methods via an object), - to use interceptors (filters/mixins) for some or all subcommands (maybe you are only interested in "file delete") - to extend a tcl command on the fly by defining new subcommands (by defining new procs/instprocs) - to use the unknown mechanism to produce dynamic error messages about valid subcommands (via introspection commands). As a simple example, one define the a specialized version of Tcl's file-command (say ns::file) like the following: namespace eval ns { Object file file proc tail name { set tail [file tail $name] regexp {[^/\\]+$} $tail tail return $tail } file proc unknown {subcmd args} { return eval ::file $subcmd $args } } For more details, please consult the ChangeLog MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun #======================================================================= # extract from the logfile of oobench (in oo-bench.tar.gz, # see http://media.wu-wien.ac.at/download.html) XOTcl methcall: 0.470u 0.000s 0:00.41 114.6% 0+0k 0+0io 347pf+0w ITcl emulation in XOTcl methcall: 0.590u 0.000s 0:00.62 95.1% 0+0k 0+0io 351pf+0w itcl methcall: 0.870u 0.020s 0:00.92 96.7% 0+0k 0+0io 347pf+0w #======================================================================= package require XOTcl; namespace import -force xotcl::* ########################################################### ## Small example to emulate a itcl-like language in XOTcl ## -gustaf neumann Jan. 2004 ########################################################### namespace eval itcl { Class create class -superclass Class class instproc instvars {} { set vars [list]; set c [self] for {} {[string compare ::xotcl::Object $c]} {set c [$c info superclass]} { eval lappend vars [$c set __autovars] } return "\n\tmy instvar [lsort -unique $vars]" } class proc constructor {args} { if {[llength $args]==2} { foreach {arglist body} $args break } else { foreach {arglist construct body} $args break set body $construct\n$body } my parameter [list {this [self]}] my proc constructor args {uplevel next $args} my instproc init $arglist [my instvars]\n$body } class proc method {name arglist body} { my proc $name args {uplevel next $args} my instproc $name $arglist [my instvars]\n$body } class proc inherit {class} { my superclass $class } class proc variable {arglist} { foreach v $arglist {my lappend __autovars $v} } class instproc init {classdef} { my set __autovars this namespace eval [self class] $classdef my class Class } } ########################################################### # Two Demo classes from oo-bench ########################################################### itcl::class Toggle { variable state constructor {start_state} { set state $start_state } method value {} { return $state } method activate {} { set state [expr {!$state}] return $this } } itcl::class NthToggle { inherit Toggle variable count_max variable counter constructor {start_state max_counter} { Toggle::constructor $start_state } { set count_max $max_counter set counter 0 } method activate {} { if {[incr counter] >= $count_max} { Toggle::activate set counter 0 } return $this } } proc main {} { set n [lindex $::argv 0] set val 1 set toggle [Toggle t1 $val] for {set i 0} {$i < $n} {incr i} { set val [[$toggle activate] value] } if {$val} {puts "true"} else {puts "false"} set val 1 set ntoggle [NthToggle t2 1 3] for {set i 0} {$i < $n} {incr i} { set val [[$ntoggle activate] value] } if {$val} {puts "true"} else {puts "false"} } main library/xotcl/doc/Announce-1.3.0000066400000000000000000000060401242365656200165610ustar00rootroot00000000000000Announcing XOTcl 1.3.0 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. Major changes relative to 1.2.0 are: - Qualitative Improvements * Improved portability: + TEA3 compatible build + Much more polished configure+makefile (many thanks to Jim Lynch for his help) * Improved code quality: + fixed 2 possible segmentation violations (error handling) + fixed free memory reads (many thanks to Zoran Vasiljevic for his help with purify) * Less intrusive usage of XOTcl components: + XOTcl procs and instprocs use now the namespace in which they were defined. One can use XOTcl packages without having the need to import xotcl globally or to use the xotcl-prefix in all xotcl procs/instprocs. - Functional Improvements * New forwarding mechanisms for fast delegation: new methods 'forward' and 'instforward' (many thanks to Bryan Schofield for fruitful discussions and suggestions). This method is a superset of tclcmd/insttclcmd, which has been marked as deprecated. See the tutorial for more details. * Nonpositional arguments for xotcl procs/instprocs; this is a flexible way to check arguments, to provide defaults etc for every xotcl proc/instproc. It uses a similar syntax as ad_proc and ad_page_contract in OACS. See the tutorial for more details. * Extended methods filter, mixin, instfilter, instmixin as discussed in the XOTcl mailing list. These commands can be used more flexibly as follows obj mixin same as: obj info mixin obj mixin {C1 C2} same as: obj mixin set {C1 C2} obj mixin set {C1 C2} sets the mixins for obj obj mixin get same as: obj info mixin obj mixin add C3 adds a mixin on front of the mixin list obj mixin add C3 end adds a mixin at the end the mixin list obj mixin add C3 3 adds a mixin at the 3rd position obj mixin delete ::C3 removes the mixin from the mixin list The same semantics are available as well for filter, instmixin and instfilter. This change is fully backward compatible. {filter,instfilter,mixin,instmixin}append are deprecated now. For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.3.1000066400000000000000000000005401242365656200165610ustar00rootroot00000000000000Announcing XOTcl 1.3.1 ********************** This is a minor fix release relative to 1.3.0. This fixes a few problems introduced in 1.3.0 (invocation of checking procs in nonpositional argument) and has a better build support for building XOTcl outside of the source tree, the tar files are now dist-cleaned. Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.3.3000066400000000000000000000032531242365656200165670ustar00rootroot00000000000000Announcing XOTcl 1.3.3 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. Major changes relative to 1.3.1 are: - Qualitative Improvements * Improved code quality: + fixed possible segmentation violations in free memory reads (many thanks to Zoran Vasiljevic for his help with purify) + fixed "self callinglevel" when uplevel was called from uplevel + fixed nonposargs + fixed configure in connection with enable-symbols and for aolserver configuration + extended regression test * Improved Functionality + Producing error message when a non-existing proc/instproc is tried to be deleted + fixed exists method for objects with namespaces by using a namespace resolver + fixed return code for unknown handling in next + reduced memory consumption for objects and classes with procs/instprocs of about 14% * Improved Documentation For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.3.4000066400000000000000000000033501242365656200165660ustar00rootroot00000000000000Announcing XOTcl 1.3.4 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. Major changes relative to 1.3.3 are: - Qualitative Improvements * Improved code quality: + fixed possible segmentation violations in free memory reads (many thanks to Zoran Vasiljevic for his help with purify) + fixed "self callinglevel" when uplevel was called from uplevel + Improved portability (many thanks to Jeffrey Hobbs) + fixed nonposargs + fixed configure in connection with enable-symbols and for aolserver configuration + extended regression test * Improved Functionality + Producing error message when a non-existing proc/instproc is tried to be deleted + fixed exists method for objects with namespaces by using a namespace resolver + fixed return code for unknown handling in next + reduced memory consumption for objects and classes with procs/instprocs of about 14% * Improved Documentation For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.3.5000066400000000000000000000040111242365656200165620ustar00rootroot00000000000000Announcing XOTcl 1.3.5 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. Major changes relative to 1.3.4 are: - Qualitative Improvements * Improved code quality: + fixed possible segmentation violations in non positional argument handling + moved the Serializer into a namespace such that % package req XOTcl % package req xotcl::serializer Serialize .... works without namespace imports (the Serializer is still auto-exported, but this will change in the future) + * Improved Functionality + Non-positional arguments can be used in the same argument list as positional ones (more compliant with OpenACS). One can write now % Object o % o proc foo {-x:boolean -y a b} { ...} instead of % ... % o proc foo {-x:boolean -y} {a b} { ...} All introspection commands will work like before. The old syntax (with the additional argument) is deprecated and will be dropped in the future. + Serializer: support for objects with parent namespaces, which are not xotcl objects + additional instproc for ::xotcl::Class allinstances to return all instances for the actual class For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.3.6000066400000000000000000000042651242365656200165760ustar00rootroot00000000000000Announcing XOTcl 1.3.6 ********************** WHAT IS XOTCL? XOTcl is an object-oriented extension of Tcl that was derived from OTcl. In short, XOTcl tries to provide a highly flexible, reflective, component-based, and object-oriented environment. It integrates language support for high level concepts which are not found in other languages, with reasonable performance. It prevails the Tcl programming style and the dynamic/introspective nature of the language, rather than introducing other language's styles and rigidness (such as C++) into Tcl. Major changes relative to 1.3.5 are: * Improved Functionality + new info subcommand: info precedence + simplified meta class definitions: every class can be changed into a metaclass by adding an instmixin of Class (or one of its subclasses) to it. In order to define that every class should be a metaclass, one can use now Object instmixin Class instead of the rather complicated solution i posted on the xotcl mailing list not long ago. * Improved code quality: + fixed possible crashes when - objects are called with class methods (e.g. due to instmixins) - when instmixins are defined recursively (e.g. Class instmixin Class) - objects are turned into classes by changing the class relationship + improved namespace resolution when mixin classes are defined in namespaces (::xotcl:: namespace is skipped, since a helper method that calles the primitive setting command is defined there) + fixed passing of error code from init methods (thanks to Fabrice Pardo for noting it) + returning PACKAGE_VERSION after a package require (e.g. 'package req XOTcl' returns now 1.3.6) + some code refactoring, fixed erroneous documentation of c code + improved documentation For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.3.7000066400000000000000000000043121242365656200165700ustar00rootroot00000000000000Announcing XOTcl 1.3.7 ************************* I am pleased to annouce XOTcl 1.3.7. Major changes relative to 1.3.6 are: * Improved Functionality + new option "switch" for non positional arguments to be used to toggle the default value. A switch is a non positional argument without extra argument. + new option "isnextcall" for self + new command "::xotcl::configure filter on|off" to turn off filters. needed for serializing objects/classes with active filters + several improvements for serializer to handle e.g. application methods on Object/Class (needed for ad_instproc in oacs) + improving namespace resolving in for object/class references + moving all library packages into namespaces + preventing "new" from overwriting objects + allow xotcl to be used in slave interpreters * Improved code quality: + fixed namespace confusion in forward to expression calling xotcl methods + fixed possible crash in instvar when empty variable names are used + some code cleanup + improved documentation * new method "method" for defining methods (experimental): Instead of using e.g. Object o1 Class C o proc m1 {} {....} C instproc m2 {} {....} C proc m3 {} {....} one can use now o method m1 {} {....} C method m2 {} {....} C method -per-object m3 {} {....} in general, we can support options for method definitions, which can be used as well for mixins, filters or info (instead of the "inst" prefix). This way the problem of finding appropriate names for distinguishing between object or class matters can be solved, in rather rare cases (when referring the to class object) "-per-object" can be used. The according name changes will happen in xotcl 2.0. For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.3.8000066400000000000000000000044751242365656200166030ustar00rootroot00000000000000Announcing XOTcl 1.3.8 ************************* I am pleased to annouce XOTcl 1.3.8 Major changes relative to 1.3.6 are: * Improved Functionality + new option "switch" for non positional arguments to be used to toggle the default value. A switch is a non positional argument without extra argument. + new option "isnextcall" for self + new command "::xotcl::configure filter on|off" to turn off filters. needed for serializing objects/classes with active filters + several improvements for serializer to handle e.g. application methods on Object/Class (needed for ad_instproc in oacs) + improving namespace resolving in for object/class references + moving all library packages into namespaces + preventing "new" from overwriting objects + allow xotcl to be used in slave interpreters * Improved code quality: + fixed namespace confusion in forward to expression calling xotcl methods + fixed possible memory leak with instmixins + fixed possible crash in instvar when empty variable names are used + fixed namespace related bug in __unknown resolver hook + some code cleanup + improved documentation * new method "method" for defining methods (experimental): Instead of using e.g. Object o1 Class C o proc m1 {} {....} C instproc m2 {} {....} C proc m3 {} {....} one can use now o method m1 {} {....} C method m2 {} {....} C method -per-object m3 {} {....} in general, we can support options for method definitions, which can be used as well for mixins, filters or info (instead of the "inst" prefix). This way the problem of finding appropriate names for distinguishing between object or class matters can be solved, in rather rare cases (when referring the to class object) "-per-object" can be used. The according name changes will happen in xotcl 2.0. For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.3.9000066400000000000000000000027471242365656200166040ustar00rootroot00000000000000Announcing XOTcl 1.3.9 ************************* Hi everybody. I am pleased to annouce the availability of XOTcl 1.3.9. Major changes relative to 1.3.8 are: * Improved Functionality + new subcommand for self: [self args] return the full argument list of the current call + " info vars" does not trigger read traces on each variable anymore + Serializer: exportMethods accepts forwards/instforwards now as well + new switch -nocomplain for "mixin|instmixin|filter|instfilter delete" to avoid error messages when deleting entries not there (similar unset) + require automatically a namespace when a childobject is added + new command ::xotcl::__qualify: convert an relative name into an command name (exactly as create does it, ignoring namespaces from transparent interceptors) * Improved code quality: + fixing version numbers for package require + Upgraded TEA to 3.4 + updated rpm build (many thanks to Ildiko Schmidt and Alexander Bergolth) + fixed bug with error propagation when getter/setter methods are used for parameters (many thanks to Manfred Stelzhammer for pointing this out) For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.4.0000066400000000000000000000031141242365656200165610ustar00rootroot00000000000000Announcing XOTcl 1.4.0 ************************* Hi everybody. I am pleased to annouce the availability of XOTcl 1.4.0. Major changes relative to 1.3.9 are: * Improved Functionality * new option: ::xotcl::configure softrecreate on|off if softrecreate is set, class relations (superclass, subclass, class, instances) are not affected by a recreate; this allows files with class definitions more easily to reload + easier syntax for providing commands for initializing default values + setting and querying options for ::xotcl::configure simiar to set * Improved code quality: + fixed a bug in connection with nonpositional arguments and "args" + fixed a bug with adding on the fly transitive mixin classes + fixed a bug with filter states after next + fixed omitted error message after calling self with unknown subcommands + fixed a bug with variable traces on thread exit (triggered by volatile) + fixed incorrect rpm dependencies + cleaner compile for xotcl for gcc 4.* under linux + don't destroy namespace imported child objects + info children returns true children, not namespace imported objects/classes + unsetting the global variable "cmd" from predefined.xotcl + Upgraded TEA to 3.5 + more regression tests addeed For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.4.1000066400000000000000000000173331242365656200165720ustar00rootroot00000000000000Announcing XOTcl 1.5.0 ************************* Hi everybody. I am pleased to announce the availability of XOTcl 1.5.0. Major changes relative to 1.4.0 are: * Improved Functionality + The C-level implementation of XOTcl create now the basic classes ::xotcl::Object and ::xotcl::Class completely without any methods. All predefined methods are now registered from the initialization script-code (predefined.xotcl) via the new command ::xotcl::alias | \ ?-objscope? ?-per-object? which is used for registering predefined Tcl commands as methods. These aliases are like zero cost forwarders, since they lookup the function pointer from the commands and used these in the methods. This change makes it possible to register the same command on different classes (with maybe different names), such that for example the predefined set method of ::xotcl::Object can be replaced with a different method and the set method can be registered on some other classes (maybe some application classes). This change makes it as well quite easy to use the XOTcl framework to develop some other object oriented frameworks. + slots TODO documentation of slots TODO remove TODO optimize via parametercmd TODO parametercmd with 3+ args TODO info parameter deprecated A slot is a meta-object that manages property-changes of objects. A property is either an attribute or a role of an relation. In a nutshell, a slot has among other attributes - a name (which it used to access it), - a domain (object or class on which it can be used) , and - can be multivalued or not We distinguish between system slots (predefined slots like class, superclass, mixin, instmixin, filter, instfilter) and application slots (e.g. attributes of classes). System Slots ======== System slots are predefined slots defining e.g. some relations between classes, or between objects and classes. The predefined system slots are: - superclass: every class in XOTcl has one or more superclasses. The name of this slot is "superclass", the domain is "::xotcl::Class", the slot is multivalued. One object might have multiple superclasses. - class: every object has a class; therefore, the domain of the slot is "::xotcl::Class", the property is not multivalued. - mixin: every object in XOTcl can have one or more mixin classes. The name of this slot is "mixin", the domain is "::xotcl::Object", the slot is multivalued. - instmixin: same as above, but the domain is "::xotcl::Class" - filter, instfilter: similar to "mixin" and "instmixin" Every slot can be used set and query the property from its domain. The syntax for setting values is newValue replace newValue and for getting its values is set x [ ] TODO "mixin set" -> "mixin assign" TODO "mixin set" -> "mixin replace" TODO "mixin set" -> "mixin reset" TODO "mixin delete" -> "mixin remove" where the first form is in both cases the short form of the second one. Every multivalued slot has as well a method "add" and "remove" Examples for using the system slot "mixin" Object o; Class M; class N o mixin ::M ;# replacing the per-object mixins of o with M o mixin reset ::M ;# same as before o mixin add ::N ;# add N to the front of the mixin list o mixin delete ::M ;# delete M from the mixin list puts [o mixin] ;# query the current mixin list Every system slot (e.g. superclass) has the exact same interface. Attribute Slots ========= Attribute slots are used to manage the setting and querying of instance variables. We define now a person with three attributes,"name", "salary" and "projects". Class Person -slots { Attribute name Attribute salary -default 0 Attribute projects -default {} -multivalued true } These attributes might have a default value or they might be multivalued. When an instance of class Person is created, the slot names can be used for specifying values for the slots. Person p1 -name "Joe" Object p1 has three instance variables, namely "name", "salary" and "projects". Since slot "projects" is multivalued, we can add values the "add" subcommand. Project project1 -name XOTcl \ -description "A highly flexible OO scripting language" p1 projects add ::project1 p1 projects add some-other-value The value of the instance variable "project" of Person p1 is now the list "some-other-value ::project1" Type Checking ========= Attribute slots can have types assigned which are tested whenever the instance variable is altered. The slot "salary" is defined as integer whereas "projects" is defined to be a list of instances of the class Project (a list, since "projects" is defined as multivalued). Class Person -slots { Attribute name Attribute salary -default 0 -type integer Attribute projects -default {} -multivalued true -type ::Project } Person p2 -name "Sue" -salary 1000 The slot types are checked via Tcl variable traces. This means that the values are enforced now matter how the variables are accessed. The checks are performed in the following two commands, and they will throw an error in the second command, since "1100x" is not an integer. p2 incr salary 100 p2 append salary x similarly the second command below will through an error, since some-other-value is not an instance of ::Project. p2 projects add ::project1 p2 projects add some-other-value When a check throws an error, the instance variables are reset to the previous value. To restore the original value, an associative array "__oldvalue()" is kept as instance variable in the object. In general, checking of variables can be turned off globally by "::xotcl::Slot instmixin add ::xotcl::Slot::Nocheck". It can be turned off selectively for each slot via a per-object-mixin; if attributes are subclassed, it is possible to register the "Nocheck" mixin on a subclass of Attribute. procsearch returns for forwarders now "forward" or "instforward", for parametercmds now "parametercmd" or "instparametercmd" and for other commands "cmd" and "instcmd" info slots TODO slow -superclass TODO king of the bongo + improved forwarding ... expr earlybinding %argclindex + improved serializer - handling of slot dependencies - - * Improved code quality: + fixed a bug with nonpositional arguments, some positional arguments and "args" + fixed a bug in nonpositional arguments when called without arguments + improved error messages in connection with nonpositional arguments + more regression tests added For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.5.0000066400000000000000000000241101242365656200165610ustar00rootroot00000000000000Announcing XOTcl 1.5.0 ************************* Hi everybody. We are pleased to announce the availability of XOTcl 1.5.0. Major changes relative to 1.4.0 are: * Improved Functionality + The C-level implementation of XOTcl creates now the basic classes ::xotcl::Object and ::xotcl::Class completely without any methods. All predefined methods are now registered from the initialization script-code (predefined.xotcl) via the new command ::xotcl::alias | \ ?-objscope? ?-per-object? which is used for registering predefined Tcl commands as methods. These aliases are like zero cost forwarders, since they lookup the function pointer from the commands and used these in the methods. This change makes it possible to register the same command on different classes (with maybe different names), such that for example the predefined set method of ::xotcl::Object can be replaced with a different method and the set method can be registered on some other classes (maybe some application classes). This change makes it as well quite easy to develop some other object oriented languages based on the XOTcl framework, since all methods can be rearrange from the Tcl layer . + Slots A slot is a meta-object that manages property-changes of objects. A property is either an attribute or a role of an relation. In a nutshell, a slot has among other attributes - a name (which it used to access it), - a domain (object or class on which it can be used) , and - can be multivalued or not Every slot defines a uniform interface to access its content. So has for example, every multivalued slot a method "add" to add a value to the list of values and a method "remove" to remove it again. We distinguish between system slots (predefined slots like class, superclass, mixin, instmixin, filter, instfilter) and application slots (e.g. attributes of classes). System Slots ======== System slots are predefined slots defining e.g. some relations between classes, or between objects and classes. The predefined system slots are: - superclass: every class in XOTcl has one or more superclasses. The name of this slot is "superclass", the domain is "::xotcl::Class", the slot is multivalued. One object might have multiple superclasses. - class: every object has a class; therefore, the domain of the slot is "::xotcl::Class", the property is not multivalued. - mixin: every object in XOTcl can have one or more mixin classes. The name of this slot is "mixin", the domain is "::xotcl::Object", the slot is multivalued. - instmixin: same as above, but the domain is "::xotcl::Class" - filter, instfilter: similar to "mixin" and "instmixin" Every slot can be used set and query the property from its domain. The syntax for setting values is newValue and for getting its values is set x [ ] Every multivalued slot has as well a method "add" and "remove" Examples for using the system slot "mixin" Object o; Class M; class N o mixin ::M ;# replacing the per-object mixins of o with M o mixin add ::N ;# add N to the front of the mixin list o mixin delete ::M ;# delete M from the mixin list puts [o mixin] ;# query the current mixin list Attribute Slots ========= Attribute slots are used to manage the setting and querying of instance variables. We define now a person with three attributes,"name", "salary" and "projects". Class Person -slots { Attribute name Attribute salary -default 0 Attribute projects -default {} -multivalued true } Examples for using the slots are Person p1 -name "Joe" p1 projects add project1 Some additional features of the slots are: - Support for value checking for * primitive types (all types from "string is", like integer, boolean, ...) * instances of Classes (e.g. value must be an instance of Person) * custom value checkers * uniform interface for single and multi-valued slots - Support for lazy initialization (e.g. when costly commands (like SQL) are used to initialize instance variables, not all variables are used for each object) - more experimental low level functionality, like * initcmd (executed, whenever the variable is read the first time) * valuecmd (executed, whenever the variable is read) * valuechangedcmd (executed, whenever the variable is altered) For more details, see http://media.wu-wien.ac.at/doc/tutorial.html#slots + Re-implementation of the method "parameter" based on slots. All forms except ... -parameter {name1 {name2 default2}} ... (I.e. pure accessor parameters and parameters with defaults) are deprecated, since slots are the much more powerful and orthogonal construct. The old c-based parameter support based on "parameterclass" is deprecated. It is still in the C-code (the method "parameter" is redefined in predefined.xotcl). If someone urgently needs it, please remove the parameter instproc from predefined for the time being, and write an email to me, in case you really need it). The C code for parameter will be removed in the next release. + Improved introspection though the procsearch method. This method reports now in its second argument not only [inst]proc but as well [inst]forward, [inst]parametercmd and [inst]cmd (the latter for methods implemented in C. * Improved introspection through "info" - new subcommand "info slots" - implemented backwards bug-compatible "info parameter", deprecated + Improved serializer - better handling of cyclical dependencies, when mixins are involved - fix for namespace handling to make the XOTcl communication library classes working with the aolserver (thanks for Stefan Sobernig for pointing this out) - Now other ::xotcl::* objects can be included in the aolserver blueprint (e.g. non positional argument handlers) - handling of slot dependencies - more convenient calling of the serializer: Method "serialize" for Object is defined when the package xotcl::serializer is loaded. + Improved forwarding commands - New option for the argument substitution. When the argument list of the forward command contains "%argclindex {a b c}", then depending of the number of arguments at invocation "a", "b" or "c" is substituted. If more arguments are used at the invocation of the forwarder than values are contained in the list after %argclindex, an error is generated. - New options for forwarder: * The option -earlybinding can be used to look up the function pointer of the called Tcl command at definition time of the forwarder instead of invocation time. This option should only be used for calling C-implemented Tcl commands) * The option -verbose prints the substituted command in the forwarder prior to invocation. + New snit-like utility functions: - ::xotcl::myvar varName: return the fully qualified variable name of the specified variable. - ::xotcl::myproc methodName ?args?: call an xotcl method without the need of using "[list [self] methodName ...]" Both commands are namespace exported + added "subst" to the set of Tcl imported methods (like e.g. incr, append, ...) On can do now: % Object o ::o % o subst {I am [self]} I am ::o + added new method for ::xotcl::Object named "contains": This method is essentially a value added back-port of the OpenACS version. It allows to create a nested object structure with little syntactic overhead. See tutorial or language reference for more details. * Improved code quality: + fixed a bug with nonpositional arguments, some positional arguments and "args" + fixed a bug in nonpositional arguments when called without arguments + tested with Tcl 8.4.13 and 8.5a4 + improved error messages in connection with nonpositional arguments + fixed a bug in the xotcl trace module (many thanks to jima for reporting) + fixed a namespace bug in ::xotcl::package require in connection with xotclide (many thanks to Bill Paulsen and Artur Trzewik for identifying the problem and suggesting a fix) + improved documentation (e.g. new sections added, some sections deleted, spelling improved, in total 8 pages longer) + fixed documentation bugs (many thanks for Kristoffer for reporting) + more regression tests added For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.5.1000066400000000000000000000047641242365656200165770ustar00rootroot00000000000000Announcing XOTcl 1.5.1 ************************* Hi everybody. We are pleased to announce the availability of XOTcl 1.5.1. Major changes relative to 1.5.0 are: * Improved Functionality + Improved interface to develop XOTcl methods in C (Many thanks to Scott Gargash for the constructive discussions and suggestions). New functions imported through the XOTcl's stub interface: - XOTclNextCmd() - XOTclCallMethodWithArgs New constant XOTCL_NONLEAF_METHOD to allow C-implemented methods to call next. This constant can be passed as 5th argument to XOTclAddIMethod (or ...AddPMethod). The following is a short example for defining and registering a C-implemented constructor "init" to a class. static int MyClassInit( ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ) { int rc; /*** before next ***/ rc = XOTclNextObjCmd(cdata, interp, objc,objv); /*** after next ***/ return rc; } ... MyClass = XOTclGetClass(interp, "MyClass" ); XOTclAddIMethod(interp, MyClass, "init", MyClassInit, XOTCL_NONLEAF_METHOD, 0 ); * Improved code quality: + fixed a bug with the backward-compatible and deprecated "setter" interface (many thanks to Manfred Stelzhammer for reporting the bug) + fixed a bug in parsing nonpositional arguments, when e.g. square brackets are used in type declarations for parameters (Many thanks to Stefan Sobernig for reporting) + fixed autoname crash under Windows NT (Many thanks to Mark Janssen for reporting and providing a patch) + fixed serializer to handle deeper stratification layers + simplification and speedup of dispatcher + Makefile.in improvements for genstubs + Changed "test -e" to "test -f" in Makefile.in, since the standard test command in Solaris does not understand "test -e" (Many thanks to Andreas Kupries for reporting) + improved 64-bit compatibility on POWER5+ For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.5.2000066400000000000000000000022371242365656200165710ustar00rootroot00000000000000Announcing XOTcl 1.5.2 ************************* Hi everybody. We are pleased to announce the availability of XOTcl 1.5.2. Major changes relative to 1.5.2 are: * Improved Functionality + allowing to trace objects as well as classes via the package xotcl::trace (many thanks to jima for suggesting this) * Improved code quality: + terminating all vararg lists in C by ..., (char *) NULL There was a problem with this on 64bit AMD on FreeBSD, as some lists were terminated previously by an integer typed zero-value. + better handling of Tcl result objects. Make sure that no changes happen on shared objects. Removed all occurrences of Tcl_AppendResult() from xotcl.c + Fixed memory corruption (accessing feed memory) in the invalidation of transitive mixins (many thanks to Don Porter for reporting) For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.5.3000066400000000000000000000020271242365656200165670ustar00rootroot00000000000000Announcing XOTcl 1.5.3 ************************* Hi everybody. We are pleased to announce the availability of XOTcl 1.5.3. Major changes relative to 1.5.2 are: * Improved code quality: + provided compatibility with Tcl 8.5 + provided compatibility with Tcl 8.4.14 (fixed a nasty bug caused by a change in tcl 8.4.14, could led to a crash due to freed Tcl_Objs in OpenACS, allthough the xotcl regression test passed correctly) + reduced calling overhead of methods with nonpositional arguments by about 10 percent by avoiding shimmering. + fixed handling of "return -code break" from xotcl methods (thanks to Artur Trzewik for the bug report) + library/comm/Access.xotcl: fixed handling of chunked encoding For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.5.4000066400000000000000000000026421242365656200165730ustar00rootroot00000000000000Announcing XOTcl 1.5.4 ************************* Hi everybody. We are pleased to announce the availability of XOTcl 1.5.4. Originally, this release was planned as a small bug fix release. However, the total size of the (unified) diff between 1.5.3 and 1.5.4 is more than 5000 lines, mostly due to the varRefom changes (see first item below). Major changes relative to 1.5.4 are: * Improved code quality: + provided compatibility with Tcl 8.5 (currently, this requires the version of Tcl 8.5 from CVS head, including the changes for VarReform (For details, see http://groups.google.at/group/comp.lang.tcl/browse_frm/thread/bff391a7f1bd8f6c/3f214d088a28ed13?hl=de#3f214d088a28ed13) + improved serializer (handling var traces for instances variables) + several small bug-fixes (e.g. fixing empty variable names, error message propagation for configure, fixing potential crashes, when namespaces are added to objects during eval, etc.) + improved portablility for more platforms (more portable shell tests for e.g. FreeBSD) + extended regression test For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org Best regards, Gustaf Neumann Uwe Zdun library/xotcl/doc/Announce-1.5.5000066400000000000000000000026621242365656200165760ustar00rootroot00000000000000Dear XOTcl Community, XOTcl 1.5.5 is available. The main change is to achieve binary compatibility between Tcl 8.5 and XOTcl compiled with Tcl 8.4. This is essential achieved through a small emulation layer based on C-structures that will be available in Tcl 8.5. best regards -gustaf neumann Announcing XOTcl 1.5.5 ************************* We are pleased to announce the availability of XOTcl 1.5.5. Major changes relative to 1.5.4 are: * Improved binary compatibility: It is now possible to load XOTcl compiled for Tcl 8.4 into a tclsh8.5 (again, substantial change). One can now test now 4 versions: a) a native version for Tcl 8.4 (without compatibility layer) b) a native version for Tcl 8.5 c) a version compiled for Tcl 8.4 with compatibility layer in tclsh8.4 d) a version compiled for Tcl 8.4 with compatibility layer in tclsh8.5 Tests showed that the overhead is for the compatibility layer is between 1.1% and 2.3%, the difference between tcl8.5 and tcl8.4 is much larger. The forward compatibility behavior behavior can be turned off by setting FORWARD_COMPATIBLE to 0 during compilation (xotcl.h) to get a small performance improvement. For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org library/xotcl/doc/Announce-1.5.6000066400000000000000000000034371242365656200166000ustar00rootroot00000000000000Dear XOTcl Community, XOTcl 1.5.6 is available. This version is mostly a fix release for some problems that were apparently quite long in the code and could cause crashes for threaded applications. best regards -gustaf neumann Announcing XOTcl 1.5.6 ************************* We are pleased to announce the availability of XOTcl 1.5.6. Major changes relative to 1.5.4 are: * Fixes for treating global volatile objects (many thanks to Zoran for his concise reports). The problem was that XOTcl uses C-level var traces containing object pointers to objects. If for some reason, these objects are destroyed earlier, this results in dangling references. This problem caused especially problems in the automatic cleanup of variables at the end of a thread. * Fix for Tcl 8.4.* for situations, where Tcl variables contained references to deleted XOTcl objects. The problem was introduced by the VarReform changes between 1.5.3 and 1.5.4 by a bad side effect of a Tcl_Obj based command to look up Tcl command structurs. The problem did not exist for xotcl compiled against Tcl 8.5 and is as well fixed on the Tcl side in CVS (might become available, when one more Tcl 8.4 release is eventually released) * Serializer: - Added dependency rule in serializer to ensure slots of super-classes are listed before subclasses. (Thanks to Stefan Sobernig for reporting the problem) - Moved deactivation of traces into "Serializer all" to get simpler results on "o serialize". * Extended regression tests For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org library/xotcl/doc/Announce-1.6.0000066400000000000000000000074751242365656200166010ustar00rootroot00000000000000% \section Dear XOTcl Community, XOTcl 1.6.0 is available. This release provides in short: - orthogonality improvements for introspection, - more introspection methods (one can now e.g. query, into which classes a mixin class is mixed into) and - performance improvements (some methods are significantly faster. e.g. by a factor of 1000 for larger systems). In addition, the XOTcl source code follows now closer the Tcl source code guidelines (e.g. variable naming), a few potential crashes have been fixed. See below for more details. Best regards -gustaf neumann Announcing XOTcl 1.6.0 ************************* We are pleased to announce the availability of XOTcl 1.6.0. Major changes relative to 1.5.6 are: * Provide a uniform interface to the following info subcommands info superclass ?-closure? ?pattern? info subclass ?-closure? ?pattern? info instances ?-closure? ?pattern? info instmixin ?-closure? ?pattern? The new option "-closure" returns the transitive set of the relation (e.g. .. info subclass -closure) returns the subclasses and the subclasses of the subclasses. For "info instances -closure" the instances of the subclasses are returned as well. For more details, please see the language reference manual. In cases, where a pattern is specified, and the pattern contains meta-characters, a list of results is returned matching the pattern (like "string match"). When no matching value is found, an empty list is returned. In cases, where a pattern is specified, and the pattern contains no meta-characters, a single value is returned corresponding to the specified value. The pattern is used to lookup an object or class, such it is not necessary to provide fully qualified names). if there is no match, empty is returned. Previously, "info superclass" and "info subclass" returned a boolean value and performed always a transitive search. Returning "" is more consistent and more in line with Tcl. Note that " info superclass -closure" is a replacement for " info heritage", and " info instances -closure" is a replacement for " allinstances". The old commands will be marked as deprecated in the near future. Please note, that the behavior of the match pattern has changed and is therefor not completely compatible with prior versions. * New info subcommands: info instmixinof ?-closure? ?pattern? info mixinof ?pattern? These info subcommands are used to determine, into which classes a mixin class was mixed into. These inverse functions of mixin and instmixin are used as well internally. These functions help to speed certain operations (e.g. superclass, or registering a mixin class) up by a factor of 1000 (!) or more, when a large number of objects exist. This functionality was primarily implemented by Martin Matuska. Many thanks! * Made the behavior "pattern" in the following calls identical concerning wild cards and object lookups mixin delete pattern instmixin delete pattern superclass delete pattern info mixin ?pattern? * Fix to preserve var traces when copying objects (Many thanks to Florian Murr for reporting) * Fix problem in Tcl 8.5.* when setting variables from C (problem with Tcl_ObjSetVar2()?); many thanks to Florian Murr and Neophytos Demetriou for reporting) * Improved documentation * Extended regression tests For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org library/xotcl/doc/Announce-1.6.1000066400000000000000000000022601242365656200165650ustar00rootroot00000000000000Dear XOTcl Community, XOTcl 1.6.1 is available. This release provides in short: - improved orthogonality for introspection, - improvements in the serializer - some minor fixes See below for more details. Best regards -gustaf neumann Announcing XOTcl 1.6.1 ************************* We are pleased to announce the availability of XOTcl 1.6.1 Major changes relative to 1.6.0 are: * Extended and generalized "info" method * Generalized serializing package: when deserializing code, now consistenly no constructors are called. In previous versions, constuctors were called on slots; due to the point above, this is not needed; background: the previous solution had problems in aolserver ns_eval, when a slot-constructor called db-functions, since during ns_eval, the database handles are non-functional) * Fixes for potential crashes when methods were called with invalid arguments * Improved documentation * Extended regression tests For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org library/xotcl/doc/Announce-1.6.2000066400000000000000000000025631242365656200165740ustar00rootroot00000000000000Dear XOTcl Community, XOTcl 1.6.2 is available. See below for more details. Best regards -gustaf neumann Announcing XOTcl 1.6.2 ************************* We are pleased to announce the availability of XOTcl 1.6.2 Major changes relative to 1.6.1 are: * Functional extensions: - handle nonposargs in method "copy" properly - new command ::xotcl::finalize (for forcing cleanup and destuctor execution at a time in a multi-threaded environment, where it is still safe to execute all Tcl commands) * Extended and generalized "info" method - Added " mixinof -closure ?pattern?" Query the objects for which is used as a per-object-mixin (directly or indirectly) * Fixes for potential crashes - Implemented proper downgrading of Classes to Objects (handle cases, where something was created first as an object and is reclassed/recreated later as a class) and vice versa - Reset mixin order for per-object mixins, when the superclass of a class is deleted, which is used as per-object mixin * Improved documentation * Extended regression tests * Some code cleanup For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org library/xotcl/doc/Announce-1.6.3000066400000000000000000000026031242365656200165700ustar00rootroot00000000000000Dear XOTcl Community, XOTcl 1.6.3 is available. See below for more details. Best regards Gustaf Neumann Announcing XOTcl 1.6.3 ************************* We are pleased to announce the availability of XOTcl 1.6.3 Major changes relative to 1.6.2 are: * Functional improvements: - simplified and generalized handling of namespaced instance variables, where namespaced associative arrays could conflict with global variables due to Tcl's default namespace resolution. Many thanks to Stefan Sobernig for this contribution! * Fixes: - Corrected deletion of meta-classes. Before, it was possible to create undestroyable objects via complex meta-class structures * Speed improvements: - Use of new interfaces in Tcl 8.5 (when compiled with Tcl 8.5) - simplified interfaces for various C functions - Speed improvement for method invocation: * With Tcl 8.5: 10-15 % * With Tcl 8.4: 5-10 % * Improve code quality: - some more code cleanup - factoring out common code - using const in more cases * Extended regression tests * Improved documentation For more details about the changes, please consult the ChangeLog and documentation. MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org library/xotcl/doc/Announce-1.6.4000066400000000000000000000010331242365656200165650ustar00rootroot00000000000000 Announcing XOTcl 1.6.4 ************************* Dear XOTcl Community, We are pleased to announce the availability of XOTcl 1.6.4 Major changes relative to 1.6.3 are: * 2 fixes of potential crashes introduced in 1.6.3 * Improved documentation (added "stack" as introductory example) For more details about the changes, please consult the ChangeLog and documentation. The planned next release will be 2.0.0 MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org library/xotcl/doc/Announce-1.6.5000066400000000000000000000011721242365656200165720ustar00rootroot00000000000000 Announcing XOTcl 1.6.5 ************************* Dear XOTcl Community, We are pleased to announce the availability of XOTcl 1.6.5 Major changes relative to 1.6.4 are: * fixed namespace visibilty problem reported by Mykhaylo Sorochan * fixed var resolver/memory leak problem reported by Victor Mayevski (when XOTcl is compiled against Tcl 8.4.*, but loaded into 8.5.*) For more details about the changes, please consult the ChangeLog and documentation. The planned next release will be 2.0.0 MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org library/xotcl/doc/Announce-1.6.6000066400000000000000000000013461242365656200165760ustar00rootroot00000000000000 Announcing XOTcl 1.6.6 ************************* Dear XOTcl Community, We are pleased to announce the availability of XOTcl 1.6.6 Major changes relative to 1.6.5 are: * improved 64-bit compatibility (expat) * fixed minor memory leaks (info methods, forward error case) * fixed potential cyclic dependencies via namespace imports during cleanup * fixed potential crash with var-traces being fired twice * compatibility with Tcl 8.6b1 * fix for debian packaging For more details about the changes, please consult the ChangeLog and documentation. The planned next release will be 2.0.0 MORE INFO General and more detailed information about XOTcl and its components can be found at http://www.xotcl.org library/xotcl/doc/cascaded-message-filter.gif000066400000000000000000000135711242365656200215650ustar00rootroot00000000000000GIF87aÚ÷˜˜˜ÐÐÐØØØøøø‹ot@sX/Fcam°eTr-z骥³N5Ø­,!’²'{$Ÿû¨"³wÊh«¯âDmCÇÎGlœ~b).‰Å> ãgØ6©­f49;™ˆö ®_Ç‚(m޶õ÷â™×~ËŠì¶ë/[&¨/Áµ6â]±éV0•. i­N}f{SjÛfì•»u (bþšE`~‹×qÅ×% $Qsu_š±ª2¬Dž¹Þ‘èßÁ‚L$Á€N¼îP-§´ï„y†,m¹[¢¬îA|â{ET5IW›tô”9FËŸÌYshãŽ×¼3ù¦¼ò©¾,Ý•.ª4ÓVöFwKæ”þ®Ö‰ù.x£ÒýàÀ|Y TÞ+ñÝmÜ >(âAþ£Ýž²ú«ã˜ã|8ż×0¯…g޹äxs.:Ó¤›k«é¥ËðéT¥¾(Û·e;ì¬Éðæ¬ƒw;î¹ÿ»zíÜþ¼gº‡Î{Rô%<휢+,ѳ?|LÎG8”Æ[ÿ&ö?uÛ¶÷Zf?ýÅé’¯~³.™?ìöëǯ‘óî»^™ò£žìáð˜¿ã²+ÚêEÀve÷YÛÿèF:fÕkŒ 'HÁ ÖK d ÿã– zðƒ ” èêIoDžÖ$ÒÁºð…¡K>WU/…ßK•CH(CúЇþ=¬`룺Ýá3y ÈD qnD¼á‘X7M5ñŠ!<`aØ?öIqŠÉZp¶ˆÅ2ΰ„X< ðŠÆëÕ¨ $4£=x,3v…ll£ ß(8ÎñÌÕø’ûé‘)-‹ÉH ’‘„<¤$36Â6ò’Ää1bHIž…Z‹¼$`@ÄÐ’¢Œ`$'çÉÌ!+”©,¥fIKZ0•§4a[9:ÑTR“n©¥0‡ILa`–uÄ%çÅKýId€Žd0‹IÍjÖò˜Ål!&uiÄfö"‹$ã§iÍrV›åôŠ)!Ù<¬y³5ÐúUÅùBR¢Óœøþ¤æ=Í©NHrL6|gîRM 2Ñžû̧BI‰Ì…^³+ë”ã*5¶KæìuyâêÐ|’³£¶D¥ÿy‘\YôMâHAcXOˆ‚ô¥ƒyé0!*Qj™ô¤HôÕJY:J—ʤÇú©1)Q’²°¢8UØz>HOÚS¨2 *TÑ+n²s_LêE£“E>uª@ý(XCZU&.óDÝÓªcÐÈÔ ~u¬+\ÉZÆ«ªUtZ!}:×bÀ€] @_³ÙT½Bó®®Te[g8Xüµ€ˆ¬d% €ÈP°sé ehWÄ–k‹ôì!C3ë–Éšö´¨M­e½‚Y°u³"4þªg§Qžòª¬U­nw«[¹Â´°ŠálIZGrv£…êc+ËÛæ:7µêÄmY *Äá:J¯hŒîO—ûÜîz÷´¬ý©f¹h];íTˆ%¼m\sûÝöº—»Q® Ðòšå¼”êoßËßþ.¦òÍby²j_­—‡Ú]¯Üßʆµ‰­"p¯r`ã&X¡e°†ùX ˜©“ð„cçC„ª×šÝ°ŠßÛa‡¾ˆhMëˆI ÃÑ^¸š)^±ŽÝ{ât~X£šË㌭Ra§î“£ÃÌñŽSK€%÷ÉÙdè8M˜´!_¿^…ò9™»ãŸ/NîñL:e+?Ëèu1þ—ìeHÅ=†è=ÇkX3Í1th†5ŒQ¼}eÕk‘igÀYË~]3‡ÏZRö6¸µÈŒé5ÇYè¹ZÍýõSûþÌaHë×–”®ô'¹èbþjz&`æ1:%=S'Šº,x–`rñ©èîN‡baa±‰¡üb:ªòÕd©ð¬Ë¹çïÞ=µ~®oùãy ØG6¢…Yl[3{Én®˜™ík vÚD‰µ‘Øï^[`æv0?å»WÙ~ ÜC7r§}Íöž}é¦um½}9x¥Âôžeµ{o\›[ß±¥ã€ý}ªÍæsà¼=ßRrÝ]u[sžmUÃmåÂaSâ»-xøþš\ñu“ŸÃiíÝNb-Ûª¥÷ ]žòg8ऴv°INp~ž¼æÿž¯GuŽžóÖâçü9Ð9B'ZÙ47ÌÁ é™Òt¾î^:Š„nNC—^Ê®úCÙªW­ŽëÄžúd%®±ÿøWFï­>3Yv³óDÜN'¦Ú%ËvN^É—a£øÑ ÛÏùÚJh¿¸sûÎÉgiz=«‚{Ï­.gòFÝîx—9ÁWØ«!õìØQ«›¶ÅÌ^ƒøòf¿y×÷ÆS4Ä ŠW]_Ò¸C·êÚ=4êµ\z{Ý´´o¼>ÿxâ¤Ïj÷ô‘7{ø‹5]ó—\°|ä1%Ù k“o(·»þÚ|Îk&‹üܼԩ¯£²A-ø%ýÅç»{37(7ÏO»¶ÄzD›>—þ¾ Ôðå5X½Èíª~é4ôg{¨eÌopÓ#Ÿ3)R3-%€=%!7#‚s÷cѤ€RB6²Q8's~£!½~»…~ke€¦…€†n:ÃáÂ""R+”"±–wµô{‘…‚attbwM8@h9×— ÖA%Oã3b!€ŠWå3xIׂÀ66es6»âÏr(K¨Wè„壂”õƒ &…ÝwT“S‚]¨Z<ˆ½¨=[gŠMsÅRŒIWX‹øI…t±6mS·†ôržbäã>.ŒöW‹ÄÕ[{xŽÑƒj‚x‡jXM!n(`Ös?D§¸‚Ø6Šþøeà#wõXwÇ“3‘ÏˆŠ‰óŠ€·Œ,$ˆh &·‚„¸“8ÞeÜ<Û{X¥C|ñŽŽR74þ‘zG†§3x¡þ”£)2¸gã‘¡86u $ Ä’«H°ƒ*1Ù53©( 2ñ$’ ©$É=ÍÒ‘-)‘Ci7æwoôU”r2"Éy’P9;Uf8î”@h•/‰•$s~f¨<2ø7"4[HG(Æ“?R–fÙ!h9ijéJXÉ'ó–¡2—ÆÇ•E–Î(–cù2â7ß&•©B•A ŽW˜æw”(ç;vc’z§‰ÅÁ0 sSUÔ::Ô—|ø—ßtB Ç™Èá™3šGÙo¸’HS‰)”xu1Ø›9ˆ— 7$¤9?0PZƒš¤ä’€¹F©2Æ•g‘›åÆ€#˜E2{Aþæw²âÌôFº¹› ‡;4'}°’ˆ=´j VYƒ1YE!ïi”È™œÐõ@v™¬©5‹ƒžZR1ÅžõòWV³8‘³+nùzôYŸâ9ž/ùú a¡:¡Z¡þX Ýùž¹© UÉ›«Ù.²ŸfIê9MÚžZ#J‘;c5z€i™½éœ/g|‰(BþiLË¥hñi|ñ¢à£ :£ù•ð‡e«ö™Õéž.bЃ}áY™CÚœEš,ÀYo(i¤OÊ—QjLÌ @¾YHŒ9טp÷o@º‚BZ]ø ¢bj—;™¥Z:<“Ù¥^ê¡`J£‰³˜´(§5j¡€*¡b§þ~)£Tê¦íçŸé§¥I=i†kÊok©§¨¨±É¨gÊ2ƒJ¨©i¨Šjo §Œ ^¤©JY‘‡m ª‰j©:Š©Ëò£§Ú‰ ÚŠ“Z¥\q¥¶$›sº½7÷\Ñ€DÅ;œ‹çÑ’‘}ÊOÍÏq,pM®p1®M¬ÊCÕÎöÛÕK¼9açc°EÖMÊ2}‚T]{kNJçÖ!o8×Õ×l-çsN×PÕäK€Æ×L- -ØeÝÏ«×rsm;ÍÖÍØZ½Õ\í]I 0“m×mmÙo ׎…Ø`­ÍYŒÓ ýÒ+wÔH¤½mˆ<Ö«½CÓeos}Ú>HkAx¹µÚŽOÑi=&<¦Ð cÁ½Éf‰ÆvÜ*þªk¾ÍÜÍò6o EÚºÜ~¼mÕ}××-Ï­íQÜÝÑÒí-@ uäæjã½ÈåMÜçÝÝæãhÉÝÞÖýÞÎ-Ú´TÜÚ–ÞnÃ`+]ÎZ¬ßØ}i åßÇÂg8'Ô"mà‘Ý$„iÖfêݲ€¦ÜUfÞŽÛVçd^&ŠNöXÛýÛ»øá5RbžÛ'ã îb*Ü,^Â5öâýýÝ2ã²í‘î}ãïšã/¥d=.ã-–âf%ä¸ââ2¥àGÎbNÙùÍäCÎE:ãQgS~qƒ¦°V>nQ5ß[¾‚]Þ¡æ—èäâeßgþßi®æUÎæËbVY^É<çþðUæVeç9ûC"¾z{Žä?^Ýèay>wf®Û‡Nk_þ†ŠNµnW(ºb¥…é5[•Î%ŒÞX;z,GY}…Œýé*1æŒÕXg X>U¨žêª¾ê“ndîêº~uV…ѵÞäWôVº>ìétÓ›Ù«=ë=•ëÄþæÎR¿.¬>AÂÞìÃÎëiäëÑèXTíÖNZÏ~JÛηž_ß>Vº˜íãŽåNí7vîæmìãT‰ë.0„Pð.éònVÚ^ï-Ke„ïùNyáŽ]þîÓÎ~‘Žîé>Hý~ðØK¬æZt§I/Lõ²e„KtñE‘ñ߇4ròØõð Ïîínò,¯³)¯"ßò2^(ÿòw§ì3Ïònó,ó9¯L;Ïó…„ó?_S5/ôÑFôE¿QHk ¿ôXŽìMŸõYDCS¬hõ7šõõô@?Q^¯%?G;öL2¢zôhßön¿q;library/xotcl/doc/features.gif000066400000000000000000000153651242365656200167520ustar00rootroot00000000000000GIF87aÃz÷ÀÀÀðððøøøßX‹F@X€Fòÿ¿°¥Tˆ@< 0MOM›,ÿòñÿÿ@¿¿L «ñÿ@¿Ü¤ßv&‹ @@tXXóFFÿ¿ X°óFT”Ä4 TM@tbócMÿ¿Ü°vTÛX~vF• @°Hòÿ¿L «@¤XE&F ä¼xÏòMÿ@¿¥hˆñÿ@¿€fLl\X~F•@¤H& HX óFÿ¿ÔEÛòÿ@¿ø¥€KˆM@€HhLMñÿ¿\T˜òlÿ¿¤X&F @€¤ƒL& @@tX°6XH FøÄKT€FLc°óÿ¿UXfEFüt]óñ ÿÿ¿¿¥Zˆs@$||ôMuÿ„°ò'ÿäLj¤„ó&ñÿ ÿ¿@¿[XaFg X0ôFs$ÄÐôTöÿÿ¿¿„°ô'ÿX›F|;°f„T°O'¤Ð&ö ÿNИöRôóÿ¿)n„O°T#àÐòöÿÿ¿¿|Õüó¸@@z¤HÄ&óMÿ °@TäHÏóÀËìßX‹F,ÃzþH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sê܀ϟ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X‡Žü «×¯`ÊK¶¬Ù³hÓª]˶­Û·pãÊK·®Ý»b}‚€·¯ß¿€ L¸°áÃ_õr䋸±ãÇ#KžLY-€Œ+kÞ̹³çÏ•/cÌ º´éÓ¨S«î*º"éÕ°cËžM[._×µsëÞÍ{ö퉯{ N¼xaÆÀ+_μ¹[äƒ;ŸN½:qè¥[ßν;êÌѽþ‹O~3øìåÓ«_?˜4zöðãËþõ}Y£f¹ÎßÏ¿·{‡Ú%VT~>õgà²ý×P€yˆ–~F(ág 2Ä X ¡ƒvèád.t¡†a ÅÚˆ¦¨"^!*„≯éÇU†+Öhã]-&ôbnX 7)d[9"´#‡06¸ãL2YäA/Òˆ¤€MVi%YOå”2þ8å•`6™eA[Æh"a¦y㘕YbPIª)gl´äœxæY§@wæégš{Ð矄Vè …&Jg}*ꨞŒ.øè¤jJé¥WZŠé¦CjÊé§5z ꨊJ꩚Šêªû©Êê«þì¹ ë¬äÉJë­ÜÙŠë®ÓéÊë¯Êù ì°Â Kì±¹‹ì²±+pBK”W@Q[­´> cf×b‹(³…:Ëm‰'Rk.´åòxn¶Á1Æ.ºðŽ .«Î&†¡»ö²¯¾ûòëoºfæûå¼§Ök®¶¤kâ¶ÿò…ð½ÑbKðªë›pÃ3˜­½«Ë0–ßNŒgÅêr‹ï¹¿Ë¡»,{oË*Ë+ò¨$;ülÇ.wíÎû¸r·3ª,b!½ëЇmô­H¦ôÒ³6 õÔÇEj!ÕXk&uÖ\³hµˆšöt×#íâ\cI6Ófëˆöci¯]¶WöÝ·­—6³ûsþ—=úœ·¿:G,÷Ÿ¯ÌïϯÛòËŒËûî¾qÞaác¬8†_Ü£,9¤t7zÖɇ[¸Œ™Ã¹yéœ{þùœ”·Ž³Ë‰gLn¹ŽwþïërÆÎzÂ0;¾x̸_o¾¼ÃÞ¶‘aÓ3Ä&ódàG/3íɺ<”’Ežý¥[ãø=¸áÛåýø–~öê¯Ï{ûîüƒÏï5ö¿Ó?©ýøÛf7òú#Üö´d¾û%F ×É”–jÎoÖºÖÎî&;iAî| „¸pg³Ó,w¿^þ—Á0 kzqJYÇVˆ@çÐdý*¡˜ئÑõLxD™-w2ÒyI†þ™¢¡l¨$ãaÎusaÊXg1 1t’""º~˜3Ú.„µë!‰œh(!ò©3‚áÞ®Ç2Ôy«‚䢕øg ª±k„/Ìá{øÃöͰW7 â›øÄ(&ðzIœâ»øÅñTY ãÛøÆüýliŒãûøÇã•1Ry ä"ùÈÓÕqi‰Œä&;ÙÆØ-“ŸLå*—8Êœ²•·Ìå ŸGÊ]³˜-üå,ùÌhNpþ™3«å4»ùÍæ]3eÛ ç:Û™»rŽ,¥û“$C—ÏA4Q²Ûç;ºÃyf잣 FÿYEqô 1üèC[ÚËP43~¡[hHwºÓ‚Æs¥/Mj':±‹–4¨AíéôŽÚÓŸNu©gßS8È–tuGÍê¡8šÖÀ°­+kXG˜Õ­öó±ÿÜgdûÙõ¶^‹ë_ïzñ>p³_ ín×:Ólo }ml/›ÅÎö¶ºÏ+moºÚÉV¶¼Ém]n¯ûÞäm7\]èlÏ;Ôßv±ñMð$ƒ{ÎßÍõª}íês:Ýøvõ½Åœs·Ùs¶Îí^ây½þ®\Ñ\<¿¸ÈWc’+šå0¯1Å5óš·xædU¹Íw`œ˜ç@÷°Ï»ªó }¾C×jÑÎt÷&}ÆMº©]Žj©[]ÀO—êÒ¯Îuš¦T‰aAÚWïh›Áø¦`™•²É•r*–%jŸ9œ¤¨£Sªšly¨ùœþÔ8Ÿfi¥ø'Ÿ¹·ißI¥Šú©™?x¦R*©ùù£Ë'¡7uŽ 9›ˆy§ÎǺY§ˆÙ¤â¶¦šøˆ©*tn‰¨·ƒ¡IŸ… Ÿ:Št‰¢ö)ln§¹y¡Fº¡*–æ ¬cI–§ ƒ•ú€'Šc¨šf-ÙXùÖ¬”¦  Ç@&­gö¬E•“Ì™y©tÜzuÚºPØ®Îú­Pçexf®>6® 5‘·®¡·„׊®Zw_ñúžGdåJ_îjZzºqWØ€;šý†šmY°€Ø¯NƯGö¯5ŒÚIƒgªƒ°z“I©Ö žEFšk¨­J¢§ùªý b Uòš£ŠÚ Jþ¨$ö²×F‹Óh©›ŽJ‹XZŸ8:£Ä8…„Êa'ëOô k¢~Š¢Z«Š —튦išŠm;[›¢˜ÚršŠpúÚ§”v³|ú³g‰ ©H‚ưðU£=Z²²ªp<ûq:¢a ´öªv]»™‰º¥>Ë–“z«*{³f‹¶II¦:«ke*œ/*²¸ú WV·tw·Þ˜±ú„¦)—Eë·qÙ±•I«†»±{k¥…›Ÿ=˸&븃ǭ Y¢¸º ;¨J{‹Ê¡¥»µzÆ®N7’¦‹x¶•¸K»/·»ëølAXÀ{tÃkO‚[¼Tv¼[¥¼AǼò„¶Éë¼Ué»Uþgj™Ghâ&¼¹ÛxØ+®rGkÐ+Y ÍX¨<ú–ýI¹Â¾³6¾½E”Þ9¿Û9±eˆµÓKf®k€iÙ§J»¿ kdð«YºW¥‡èƒmK¶œ¿@û˜y[¿? ¶W*™íÚ½–W¾˜Ûh¾–À¤;µÒ)f²»ÀòYµ‘º hÈÀqfÁ›—².ë‰v8œ!̧f1L´·£p¥±[­/6Àµ§¯Ûƒ{Ã9Û¹(¬®¼² ê£Pk‹ڱ* zÞ7€ôÀß—ÁŠÊÃΪÀœÛ;Z©^kˆ3Ü¸Ö v+YîªÀ¯ÛÁ+ÀQLzÔ‹e\vq\süvu\s>\NGœÇÌúÆþ¨çÇ1·Çä4½},ÈÞEÈnå¯ù†aˆ|bм©7pã÷È&É:1Œ“Öƒ‚v9—ìkÉ@zÇy7ÞÙ¥E™f|¢¡Û$yÛ¹Q{±T‹¸ªÜÊ †ÉÅÂuy°ì²l»°‡L½ºŒC‹¨©LºakÂL<·¸¬«¤¼o¼L´*Êœ–¹˜ û̹ È­7ÍTj™ûéÉMh«w¨Í™úÊ`fÎÉÍ'5Ìê,tì,‘ï¼nÅ|î¼z6J`,œÏ ÷ÇѬ_Å›«ú¼A&sñ|‘µK\Ь}Í‘ªg\ìËüš›–—U ¬Åm÷Ï4ç`ÉmŽÜÃ[Ã\ºþ¹Öi¿ܨ³ÜÒ×iÀcÒ9§f¼ëf¬¸¡Kœ-»³ÚÓ\Ú ÏRùÐ iÓìöf9ͳ‡;μÆÅ6ì¶¡›¾;Î9fÔ%9ÅÔê‚Ãg¾iÌ´ºÁB ¨¬cD<ÔAM£;Ô ¢¬ÙÄ_]~X’šH™i¸‡*m×^³þi±O:¿Èú§ØœÒVkÀhl©+úÖ`œ<Óè¬izIÍ:<Ò̦Pû£ ¼–ÚÖú+Ø9œœ­¾M ÃPŠØGKÖ¯sí’+Ö7ÂjÃΜÙØš9Æ)vϼÚÐÚÚ0;Ðr«ÖÉŒŸ2<Ù¥-«w}­°ÇÛÛê}úµbl´ÏþÍÏœé׸š·M¦Û—ÌÜäZ¾±«Ð­º[-Ó´}͋ʭ\Ï6qÌ96Ï»MÓ?×µøêÞÛ ßD·ÏѦÝê¬Þ5¡ßø,¿‘-ß íbüMþqW›Èø ÂÜý® ­Ð%àhVàMY×n¼‘fš­¾åüÝëûÙ î± Lá2ÁÞUmx']ĉº/k±b ÓÑh³@hßà*à½xD Ôç ÛHûâH ÚL]±tKãéê¤&ššpùÄ—ÙTÀ<ž´ÔÌhÃCþØáÄS-ボã„ëä“ å“Ü…«ÓY^å~—²G^ ¼r9­ÒŒËâ|MÜNœæcÞŸ}ÕD~¯Xþjëygä!¼š]ýÓ~Jæ·íÑX×à»çÒÍÏô]ÀfËNç¨üèx±á™çv;é\Ú–°ˆ¦èûéÁFâ=Aê,)ê(‹êï«êBûè>ÊVε»Ë{Ü«é[ëî›ê¸~ºáèû¿Özè^Í¿­Úc¦N“¯‡Ý6ËÊRËÌKØ—ë#×ëºÑ\NÂf:·L­]\¯Öî½¥Çä²}š>^ÎkÕJÞîN¼Ø>¥bJ¶¤­¦·ü|ì펼Ë~Ö•¾¬C|œ+.íA\Áá~Áï·{jÕ‘k¹™kìKí»–ïÍË꥖ìÂHñ/ñÑ‹ñ¤fñÁÅñ—æñæò–&þò,ñ$ïØgžòwfò•Ç .á nÖßVð+\Ç2=óA)Ò*/éÄ|áÀ½ëXbO¾¿ÎÕ|°v­ÁH¾¨½\ÅÞ¢­Á‰-è|7ëµûë±ø›à¬¥?ºó.æ]¯Ø1=ݦý˜Wôñ{ð=Ý}T[ÝÍÕm·ø[Ü8[Û³œÖ;=}jOÀl¹Ð>çTÍçnÛNl÷³*Ì+*Ø>m{}ÿÃãÞ­^nÜʈÖkýæ‹ûãï{MïÊ\–ÏÇû‹g©ÙYšù`¿ÙuŸ‡ïõ ¸²›ö6/Å‘ïáûÙ´÷;µ©mø—õmÑú‡Úòú³Ç,/l‘žÎÇ?ôæò*þò‚<â¡_ÈË_gÎ?‡Õ¯ÔÓ¿ÈÙÓÛ/Éi“M¦7NôÅȩ݄wù@ÿÑXÿ»©þi«½Îì¯ßŸÉË>€cš°Íô¢¹@À@ `ð A… LÈP!Bˆ:„xáFŽ=r@ä’%MžD™RåJ–-]¾„ 3ãGš5mÞÄ™SçNž=}n\¨1"A†=V̘tè E…b$ꔩR¥K‘Fµ8TëÌŸGEˆVìX²eevE›VíZ¶mµb-˜5*P¹[—Òlš4èU£o゜{W°^¡n~5›XñbÆ&¹†Yòd¼€52*q¢_£uA>þÔ[t]¤˜9ž rÈ‘]¿†­ò1eÚµmwåJµ/Ö̧#ÊÍ;û¢èÙ3g5Î[0QájYƒ]:ãæ·­_Çî´ðæÓ„}”š›¯jï¦%ö\ùùÈϧ¿‡/¶zvúõÛ?¤˜=èï[¹Ó*¿í¸K?«Tn¯ùÐr/>LiAû&¤Ð°¦*ݦ ÝjB”0CGÄiCG4‘­?dqºO„Æc¤pƵVl1Ç×^¤±G’'u$R1ƒD2I%i²H'Ç:rI)§¤òº&ŸÄò¥(«ä²K/Óº2K1eû²L3ϼ±1×liK4ß„“Ë0Ù¤ñÓÍ8ïÄóÇ9é\ÓÎ<ÿÃç ã³Ð’ü 4QE­ÔPGIBtQI'mkPB-4RJ7åt'K1}TÓNG%¨¯ZÕPKeµÕ,½4ÕL]¥µÖŽ :UY]ÕV_Å5×Xwåu4cE6Ye—e¶YgŸ…6Zi§¥¶Zk¯MX]‰ÝU[o¿7\qÇ%·\sÏE7]u×e·]wßý–[yI‚·^{ïÅ7_}÷å·_açØ_&¸`ƒî`…OB¸a‡†8âw¦¸b‹/Æ8c7æ¸c?9d‘G&¹d“OF9e•Wf¹e—_†9f™I ;library/xotcl/doc/filter-inheritance.gif000066400000000000000000000154301242365656200207010ustar00rootroot00000000000000GIF87aü^÷PPPÈÈÈàààøøø‹oFt@sX/XFcFam°eTra-‚Ñ dB<¸75íOdéIcüŽ#Ɇ¸ñ²CÈg§Bæh²yœ ¥'!‰N„Ž«pP¨ÇT¾î!ŸŒ¤*_9Ên)Š#m\²ò ®å1IvÊÝÓ˜÷[H2’KØ4Ór™æ$—¹Á]*3še æy†™Mo*¤šÈ4'/ÁIqÒŽœÝ¼¥CÐYNyž“›_þqg´jYÏbΟÑfAè }rž(5êDu”¡\1¨«P‡.”­´è@šÏkr¦hÏü'FÓiÏmŽ”™´¤ Hû MHS£á¨m<úPq¯r6Ô’~Y3–ŠÔ¥ ,é7ÚNšºÅJƒj’—’ú&JÙ$¤«‹"E,_ý–øÅéM°‰É] gôÁ1°Vԛà ³e‡4æo \aâV…ÈDã1tkcïÕÈÆ‰ Oëµb&·ØÇW>ñ“ç’º$o¢ …ðþb%,æoU*#[“Z÷9eÌVÌÍ1F 'WˆÄY¶rÑœ™ÕÜß4yÍCÆ2ñNX)ÎiÉ@PF˜eëìÞ%ÇÞó£[d†¥È¯ªíò¥IªaBÏÆÎÕÔce‰=Àþ'w¡î1‹\êC÷—Í4©Ê. {•Û"¬Å#êyÖaÝ‘öŒ'® V×[}õ6h[[ÚØ˜~œœáüæÅÁõÛ“uõD"H°[ÉwFvž3ãy•[Ö^¦õ“­çv»»%Õæ-šwËa\»êÏätcçsî3/ÖÐúFtV­Ÿ€×lÜÊ7‡Z®ü…ö³Z4·jÿ\ä6›M%ç&Îþb—šs…ÐŒIÀXÀ+ä÷#_·¯“_`ªÙµÇMI˜aÑ556gõÞô›lëy«X®§žóŠ˜"sWÑÕ5t©ãÏèÉ£XˉÆq?ë¸/Ù[\Õá5õt]eÇúí‚æ«® ÛÞ.ÿˆ {wv«OÑ]uWWÚÕNÁ,óëLj ãµ®¼£ýêÐS ÑÏw¬·“Í…»ÑàÄÀ^ñ5g|» /‹_üƒ½³ƒ1½ãƒÉà,OuÌ“]óB‡Ýñ¬ Öœ·Žç³žõx£-ˆq8¿<Í;x÷Ó ¼ßÄ’y×è†Ô¡þ«ˆÁîýEGë+áÝñ…þüi‚Å­cÓï×îñýùò›‡‡‰\ç€&ü¶kqç7÷KßlidB};ºÛ•ó|³à jí7C@†}RBză€~f#âG}áç>¾Õ°!886è&ñFjóVDã·> ø5ë@q‚X$b‚^!zèR Øz¯s6Xkø{ÉçÓ÷€$Ø€²Ñу*˜.(ƒ¦–n˜#µ²­61LcBRþáƒ+È‚†”97„Ç–H¨r Ò9R~§Ç<ÏñƒS1b#UH{WxixÄ)À—3]xCçè÷„ì2$ìò†Jx&ÛGIèbkx͇‡cDs˜+×ç(þ!x9P@”¶Y $o ¶t""ìs{R·1…«8ðfp‰ç5‰:…F $ÿÃt(((gÓ‰žH]î%S{HTâTëc1l·‰YÇsÕñx+òаx‡èˆU‹Ð7n¥¨*ˆ+‚2,·ŠÙµ‚?‰Ãx‡³Q…¡˜†ØiJ³…ñŒÐè‹Ô‘6ÒgEÕuüR$gøˆÜˆn¬¦‹¸ÈlÆ#ŽúGŽ¥R†pRCÖø†>„[v…Œ×óT¤ˆrøè5¢3-~À·yÓó1¸ñ˜H[¸ˆ’Ã=Š–ê·l§0ås.X„\—… á„äxÛ±%I!ý’@Çþ4•3Þg’%i“c¢"jgA*©,)n!’x $~£Á@7{¿‡”ÝÒ|‡;@édìõ‚{0é~ãARˆ’¦”€¨Ž¡§<ÑVoåØq‘s.U1/”’^‚ß÷•¯‡=ù‹Mg;0)1C$W“ò©=nY‚qø”Ï5–)Ó"iC>3DÔèŠÂH5K95}É|™Œ•¥7ó<ƒ˜'+sINó˜KÈ„qr¿ó1šÉˆÁš¬yC¢©(¤‰êJ^I|GÀÆ­¹›%èvq)˜‘L•·@Ô/k³1¼™œ‹™JD+ ¿““L©w Ô8çÈ6ʹ›zþùv;'›Ð 1wérwrÓ'\“¡‡>çœ>œÀsŠÓ96W–èyD~7+±Ùƒî žáyY„›ßØ‘õ¹=Z©rù)ßñEý)r|)BRFÊ2HW+:^¡ |¥Å&¥¡}ÕÂÄžrÙÓ³  ·m¼Vr­™r“)–”©_ÿ'º3Msw~¡ÕaÝ©ŸßY€VÖ©0ú„¦¹[?ÊAÚ¿ù¢$j¤x†¤òw¢¾'¢Ày¡NªnPZ€3:¥#‰£^ Z=W¤>ú¤‘¤Œñt½‡Ab*„dªfºhúa‚1›W*’e¥·¦w”¦YÕ£lŠ¥wª¥4wþ{*§\j¥c ¨nЧ÷ÒS†J¨ûù§vº¨‚*¥j +kz[*oªq*yéœ:¨›z¤‰ªc–z§ZÚªŠš~Ž*ª Fªñª~*‘©:««:§°š¡¦š«¨}¼še¬*¬®¬¶Ú¦²Z¨´zoËj¢yZŸº«ÎÚ«‡Ú¤‰:©Q­Äz­Æê«È*­"á­Íe™xÑ ¬–ú«£§Åz‰Çº®«ôŠmg¯CZ«ˆ*©ïš¥2h¯§sϪ¦ãÊ®ÓJæjªêfŽÒ˜Þ8¯ýZªíz°ÿj­èÚ-Õ2\a9nzz¯!°Ú°Ê:°šv²ûˆˆ‘µ–Ëþ—:²›°±°…Ѱ‹&$€rœôG’ÒÈ{+£% ³Ãz®˜Ú}ªÆT±WÒ³øù³!´ A³+Y¨k½ökIXÒ&® Ke !µK0}ö5m‚‘eËi*´´S+´ÉZ±]k…JJµžæ°9Û%*+}<»¶Q[§K©$ë¶&Ë&È´&ã´^û`˵¦Ø² vî ´€Ë¶±Ê°úJ¥/»·ÛÚ·ÝʨÏR¹¸+Ä·ø ¤œ+¤à*¯Š+¹[®¥·§»¯Ð:±· µ×®k´‡ºÙйþ:º««ßšj(Š­—[»‡ë‰»» ˜tÄ«©†kg_Ûºùz³Ì®ÊþK„õJ»Øë»Ûœ‰’®Žû¼•É[¼§Â§s&¾À»¹•*³æŽèKfû´‘Ë»ëK¹¯k¹Îû¶ã½íÛ¨ùû¹ie¿³[¿Æ ·Ó;š¸k½ï+ŠäJ¾Ò ¯ ±_ZÁS£·Ü¿ˆÁ š“Œ´¸²Û¶î;¿ À[÷Á(Âì¿;ÂêŠz(ì±ê;´H7ÚyøcÁVÜ.ÃëÉ¿÷Û7 $÷€{‡r¬ÁÃRæÃTKÂÐûF«w®wxäYŸIÂÚ,‹™£ºXîàM>‰wþ*'׫ÞN±ØUÙýëÑ ÛOÒ•½üåEÎl¦í¼8‚˜»>}:ëëu8CþBß{²Õ>âH~HË‚s„­½Ýì0½íØHß?厰Yèˆ-ì2vã%9Ï®5²2䨇ïŠR!h­±' "ž“) ,ô¾/%аFiêò¸ï ßð_Bhƒ{•(Å0)ÂÛþÝnÞï_G¼}" „UéÖ$oÀÙÞ˜ýîïD©Fùò† n-eßûS“&Ÿï¾|µT}:š)î„ô¤];á”Û©¸ìËC6:¯Ö³ô•m 6ô>3ó`2”’3Òpý+[mCxÝ<ióÌЙòHìÃäƒ8¾9 tì`Wá;é”1ìHrE ´ÐNŸ;äÜx^Ÿ)ÿC{Ý3 ”’H’ŽÌ<ƒÿ˜+ýɽ³L“–x™òýÎõÁš¿¢…+•’ “–Þ¾3V¦Å¾Ã› øŸÜO¡}Úh¹ø°ŽŽFç™ö×úÐúñÃ÷i›t>#¤ûÄoË+ŸÅ>„áPT›a|›ïþ'YZüÒ¿ÌÇÆX½BFž3Åùïf”ÓOüÿú™¥üõø÷_ ïO©öß͈$þžÙ1FÊmÐ<ï<럭ìþïÿ0±]|ZA@ D˜PáB† >„QâDŠÀ˜cAŽ=~RäH’%MžD™RåJ–-]Ž Sf€—5; À™SI ú$4¨E¢EEZ±§F›M>…UêTªgʬªRgÎ’L…fäˆ1éX²eË.›UíZ¶mݾy+Ü[wž\’à´hÍþ,±oÏt FœXqH¹4´›’iÐBIòL8ðæ„™ó>þZôèªEG–œ6è稘=¿†;vaÒµmßÆí²±cШ%ÿÌ\øpâÅßšm÷îKËÆ?‡:òÞ¾™KÇž]ûöÇWO[¯ÙœûxòåͯŒ9ZyTñçÝ¿‡?1x§íåßÇŸ_?KúOíï0@Aï?½~H5—úƒÊÀ„ð>ËÄ ¯Jð%Œ°Có›lµÊ.\m# }jŽD ¬kCö>„1ÆòP\±²ŽjL‘ÄÃp¥õÜbQF!‡¬­Æo\±'Ê<Ò1,—$ 'È\œ*H"¯Ä1#¡d’ & loË ¹ú.+³D3ͬh|ÒÉ%qdRÄ8{lþ$å¸ÒRM=÷RA0/œLG@mü©N;ïTìL>eÔ½;—k4RI'5 QJ/Å4ÓG!Í´SOÕÜ”ÓOG%õÃPE-5UU÷{”8E\5VUËtµ©WeÅT<µ ±×´(,P?ƒÍµXMAVÎA•1C7™56Zi¥úŠK6ŸÅvÂ'o¶[o³µ¶P#u³×6¿E7]‘ª­6, ÅPÌsÕ¥·Þdï·Yå=²^ÕU2AU«3à`ßuö_…½åÖ…†[›ޏbY)†×b7æ¸c?9d‘G&¹d“OF9e•Wf¹e—_†9f™g¦¹f›oÆ9gwæ¹gŸ:h¡‡&ºh£F:i¥—fºiã;library/xotcl/doc/langRef.xotcl000066400000000000000000001661621242365656200171000ustar00rootroot00000000000000# -*- tcl -*- package provide XOTcl-langRef 2.0 package require XOTcl 2.0 namespace eval ::xotcl {} namespace import -force ::xotcl::@ # catch { # @ XXX x x # } msg # puts stderr $::errorInfo if {$::tcl_version >= 8.6} { # # Without this [namespace which] (or any other [namespace *] # command, the previously ::nxdoc::@ is called, rather than the # imported ::xotcl::@?!?! # namespace which @ } @ @File { description { XOTcl language reference. Describes predefined objects and classes. } "predefined primitives" { XOTcl contains the following predefined primitives (Tcl commands):
<@tt>self<@/tt>
computes callstack related information. It can be used in the following ways: <@UL> <@LI><@TT>self - returns the name of the object, which is currently in execution. If it is called from outside of a proc, it returns the error message ``<@TT>Can't find self''. <@LI><@TT>self class - the self command with a given argument <@TT>class returns the name of the class, which holds the currently executing instproc. Note, that this may be different to the class of the current object. If it is called from a proc it returns an empty string. <@LI><@TT>self proc - the self command with a given argument <@TT>proc returns the name of the currently executing proc or instproc. <@li><@TT>self callingclass: Returns class name of the class that has called the executing method. <@li><@TT>self callingobject: Returns object name of the object that has called the executing method. <@li><@TT>self callingproc: Returns proc name of the method that has called the executing method. <@li><@TT>self calledclass: Returns class name of the class that holds the target proc (in mixins and filters). <@li><@TT>self calledproc: Returns method name of the target proc (only applicable in a filter). <@li><@TT>self isnextcall: Return 1 if this method was invoked via next, otherwise 0 <@li><@TT>self next: Return the "next" method on the precedence path as a string. <@li><@TT>self filterreg: In a filter: returns the name of the object/class on which the filter is registered. Returns either 'objName filter filterName' or 'className instfilter filterName'. <@li><@TT>self callinglevel: Returns the calling level, from where the actual proc was called from. Intermediary next calls are ignored in this computation. The level is returned in a form it can be used as first argument in <@TT>uplevel or <@TT>upvar. <@li><@TT>self activelevel: Returns the level, from where the actual proc was invoked from. This might be the calling level or a next call, whatever is higher in the stack. The level is returned in a form it can be used as first argument in <@TT>uplevel or <@TT>upvar. <@/UL> <@/p>
<@tt>my methodName<@/tt>
is a short form for <@tt>[self] methodName and can only be called in a context of an instproc or an method specific proc. It allows certain optimizations and shorter to write. <@/p><@p> <@tt>next <@/tt>
invokes the next shadowed (same-named) method on the precedence path and returns its result. If <@tt>next is called without arguments, the arguments of the current method are passed through the called method. If <@tt>next is invoked with the flag <@tt>--noArgs, the shadowed method is called without arguments. If other arguments are specified for next, these will be used for the call. <@/dd>
<@tt>myvar varName
returns the fully qualified variable name of the specified variable.

<@tt>myproc methodName ?args?
calls the specified XOTcl method without the need of using "<@tt>[list [self] methodName ...]".

<@tt>::xotcl::alias class|obj methodName ?-objscope? ?-per-object? cmdName
can be used to register a predefined C-implemented Tcl command as method <@tt>methodName. The option <@tt>-objscope has the same meaning as for forwarder (instance variables of the calling object appear in the local scope of the Tcl command), <@tt>-per-object has the same meaning as for the method <@tt>method (when used on a class, the method is registered for the class object only, but not for the instances). This command can be used to bootstrap xotcl (when e.g. no methods are available).

::xotcl::configure filter ?on|off?<@/tt>
allows to turn on or off filters globally for the current interpreter. By default, the filter state is turned off. This function returns the old filter state. This function is needed for the serializer that is intended to serialize the objects classes independent of filter settings.

<@tt>::xotcl::configure softrecreate ?on|off?<@/tt>
allows to control what should happen, when an object / a class is recreated. Per default it is set off, which means that the object/class is destroyed and all relations (e.g. subclass/superclass) to other objects/classes are destroyed as well. If <@tt>softrecreate is set, the object is reseted, but not destroyed, the relations are kept. This is important, when e.g. reloading a file with class definitions (e.g. when used in OpenACS with file watching and reloading). With <@tt>softrecreate set, it is not necessary to recreate dependent subclasses etc.

Example: e.g. there is a class hierarchy A <- B <- C Without <@tt>softrecreate set, a reload of B means first a destroy of B, leading to A <- C, and instances of B are re-classed to ::xotcl::Object. When <@tt>softrecreate is set, the structure remains unchanged.

<@tt>::xotcl::finalize<@/tt>
Delete all XOTcl objects and classes and free all associated memory.

This command has the only purpose to delete all objects and classes of an interpreter in a multi-threaded environment at a safe time.

Background: when XOTcl is used in a threaded environment such as for example in AOLserver, one has to take care that the deletion of objects and classes happens in a safe environment, where the XOTcl destructors (destroy methods) are still able to run. Without ::xotcl::finalize the deletion happens in Tcl_FinalizeThread(), after thread cleanup (where e.g. the thread local storage is freed). This can lead to memory leaks in AOLserver, which allocates e.g. some structures on demand, but since this happens after cleanup, it will leak. A simple ns_log in a destructor might lead to this problem. The solution is to call ::xotcl::finalize in the "delete trace" in AOLserver (as it happens in OpenACS).

Note, that ::xotcl::finalize is not intended for application programs.

} } ## ## Object methods ## @ Class Object { description { This class holds the pre-defined methods available for all XOTcl objects. All these methods are also available on classes. } } @ Object instproc abstract { methtype "instproc or proc" methodName "name of abstract method" arglist "arguments" } { Description { Specify an abstract method for class/object with arguments. An abstract method specifies an interface and returns an error, if it is invoked directly. Sub-classes or mixins have to override it. } return "error" } @ Object instproc append { varName "name of variable" args "arguments to append" } { Description { Append all of the value arguments to the current value of variable varName. Wrapper to the same named Tcl command (see documentation of Tcl command with the same name for details). } return "empty string" } @ Object instproc array { opt "array option" array "array name" ?args? "args of the option" } { Description { This method performs one of several operations on the variable given by arrayName. It is a wrapper to the same named Tcl command (see documentation of Tcl command with the same name for details). } return "diverse results" } @ Object instproc autoname { ?<-instance>|<-reset>? "Optional modifiers: <@br> '-instance' makes the autoname start with a small letter.<@br> '-reset' resets the autoname index to 0." name "base name of the autoname"} { Description { autoname creates an automatically assigned name. It is constructed from the base name plus an index, that is incremented for each usage. E.g.: <@pre class='code'> $obj autoname a produces a0, a1, a2, ... Autonames may have format strings as in the Tcl 'format' command. E.g.: <@pre class='code'> $obj autoname a%06d produces a000000, a000001, a000002, ... } return "newly constructed autoname value" } @ Object instproc check { options "none, one or more of: (?all? ?pre? ?post? ?invar? ?instinvar?)" } { Description { Turn on/off assertion checking. Options argument is the list of assertions, that should be checked on the object automatically. Per default assertion checking is turned off.
Examples:
<@pre CLASS="code"> o check {}; <@i># turn off assertion checking on object o o check all; <@i># turn on all assertion checks on object o o check {pre post}; <@i># only check pre/post assertions } return "empty string" } @ Object instproc class { newClass "?new class?" } { Description { Changes the class of an object dynamically to <@tt>newClass. The method returns the current value of class, when it is called without arguments. } return "if <@tt>newClass is not specified return class, otherwise empty" } @ Object instproc cleanup { ?args? "Arbitrary arguments passed to cleanup" } { Description { Resets an object or class into an initial state, as after construction. Called during recreation process by the method 'recreate' } return "empty string" } @ Object instproc configure { ?args? "'-' method calls" } { Description { Calls the '-' (dash) methods. I.e. evaluates arguments and calls everything starting with '-' (and not having a digit a second char) as a method. Every list element until the next '-' is interpreted as a method argument. configure is called before the constructor init during initialization and recreation. In the following example, the variable set is called via configure before init: <@pre class='code'> Object o -set x 4 The method configure can be called with the dash-notation at arbitrary times: <@pre class='code'> o configure -set x 4 Note, that if '-' is followed by a numerical, the arument is interpreted as a negative number (and not as a method). If a value of a method called this way starts with a "-", the call can be placed safely into a list (e.g. "Class c [list -strangearg -a-] -simplearg 2").

See also create. } return "number of the skipped first arguments" } @ Object instproc contains { "?-withnew?" "Option to overload new to create new objects within the specified object. Per default, this option is turned on." "?-object?" "object, in which the new objects should be created. The default is the object, for which contains>/tt> was called." "?-class?" "In combination with option -object: If the specified object does not exist, create it from the specified class. The default is ::xotcl::Object" cmd "Tcl command to create multiple objects" } { Description { This method can be used to create nested object structures with little syntactic overhead. The method changes the namespace to the specified object and creates objects there. Optionally, a different object scope can be specified and creating new objects in the specified scope can be turned off. The following command creates a three rectangles, containing some points.

  Class Point -parameter {{x 100} {y 300}}
  Class Rectangle -parameter {color}

  Rectangle r0 -color pink -contains {
    Rectangle r1 -color red -contains {
      Point x1 -x 1 -y 2
      Point x2 -x 1 -y 2
    }
    Rectangle r2 -color green -contains {
      Point x1
      Point x2
    }
  }
The resulting object structure looks like in the folloing example (simplified).
   ::r0
   ::r0::r1
   ::r0::r1::x1
   ::r0::r1::x2
   ::r0::r2
   ::r0::r2::x1
   ::r0::r2::x2
} return "number of the skipped first arguments" } @ Object instproc copy { newName "destination of copy operation" } { Description { Perform a deep copy of the object/class (with all information, like class, parameter, filter, ...) to "newName". } return "empty string" } @ Object instproc destroy { ?args? "Arbitrary arguments passed to the destructor" } { Description { Standard destructor. Can be overloaded for customized destruction process. Actual destruction is done by instdestroy. "destroy" in principal does: <@pre class='code'> Object instproc destroy args { [my info class] instdestroy [self] } } return "empty string" } @ Object instproc eval { args "cmds to eval" } { Description { Eval args in the scope of the object. That is local variables are directly accessible as Tcl vars. } return "result of cmds evaled" } @ Object instproc extractConfigureArg { al "Argument List Name" name "Name of the configure argument to be extracted (should start with '-')" ?cutTheArg? "if cutTheArg not 0, it cut from upvar argsList, default is 0" } { Description { Check an argument list separated with '-' args, as for instance configure arguments, and extract the argument's values. Optionally, cut the whole argument. } return "value list of the argument" } @ Object instproc exists { var "variable name" } { Description { Check for existence of the named instance variable on the object. } return "1 if variable exists, 0 if not" } @ Object instproc filter { ?args? "filter specification" } { Description { If <@tt>$args is one argument, it specifies a list of filters to be set. Every filter must be an XOTcl proc/instproc within the object scope. If <@tt>$args it has more argument, the first one specifies the action. Possible values are <@tt>assign, <@tt>get, <@tt>add or <@tt>delete, it modifies the current settings as indicated. For more details, check the tutorial. } return "if <@tt>$args return empty current filters, otherwise empty" } @ Object instproc filterguard { filterName "filter name of a registered filter" guard "set of conditions to execute the filter" } { description { Add conditions to guard a filter registration point. The filter is only executed, if the guards are true. Otherwise we ignore the filter. If no guards are given, we always execute the filter. } return "an empty string" } @ Object instproc filtersearch { methodName "filter method name" } { description { Search a full qualified method name that is currently registered as a filter. Return a list of the proc qualifier format: 'objName|className proc|instproc methodName'. } return "full qualified name, if filter is found, otherwise an empty string" } @ Object instproc forward { methodName "name of forwarder method" ?options? "-objscope, -methodprefix string, -default names, -earlybinding, -verbose" ?callee? "named of the called command or object" ?args? "arguments" } { Description { Register an object specific method (similar to a proc) for forwarding calls to a callee (target Tcl command, other object). When the forwarder method is called, the actual arguments of the invocation are appended to the specified arguments. In callee an arguments certain substitutions can take place:
  • %proc: substituted by name of the forwarder method
  • %self: substitute by name of the object
  • %1: substitute by first argument of the invocation
  • {%@POS value}: substitute the specified value in the argument list on position POS, where POS can be a positive or negative integer or end. Positive integers specify the position from the begin of the list, while negative integer specify the position from the end.
  • {%argclindex LIST}: take the nth argument of the specified list as substitution value, where n is the number of arguments from the invocation.
  • %%: a single percent.
  • %Tcl-command: command to be executed; substituted by result.
Additionally each argument can be prefixed by the positional prefix %@POS (note the delimiting space at the end) that can be used to specify an explicit position. POS can be a positive or negative integer or the word end. The positional arguments are evaluated from left to right and should be used in ascending order. valid Options are:
  • -objscope causes the target to be evaluated in the scope of the object,
  • -methodprefix string inserts the specified prefix in front of the second argument of the invocation,
  • -default is used for default method names (only in connection with %1)
  • -earlybinding: look up the function pointer of the called Tcl command at definition time of the forwarder instead of invocation time. This option should only be used for calling C-implemented Tcl commands, no procs etc.);
  • -verbose
  • : print the substituted command to stderr before executing
See tutorial for detailed examples. } return "empty" } @ Object instproc hasclass { ?className? "name of a class to be tested" } { Description { Test whether the argument is either a mixin or instmixin of the object or if it is on the class hierarchy of the object. This method combines the functionalities of istype and ismixin. } return "1 or 0" } @ Object instproc incr { varName "variable name" ?increment? "value to increment" } { Description { Increments the value stored in the variable whose name is varName. The new value is stored as a decimal string in variable varName and also returned as result. Wrapper to the same named Tcl command (see documentation of Tcl command with the same name for details). } return "new value of varName" } @ Object instproc info { args "info options" } { Description { Introspection of objects. The following options can be specified: <@l> <@li><@TT>objName info args method: Returns the arguments of the specified proc (object specific method). <@li><@TT>objName info body method: Returns the body of the specified proc (object specific method). <@li><@TT>objName info class: Returns the class of objName. <@li><@TT>objName info children ?pattern?: Returns the list of aggregated objects with fully qualified names if <@TT>pattern was not specified, otherwise it returns all children where the object name matches the pattern. <@li><@TT>objName info commands ?pattern: Returns all commands defined for the object if <@TT>pattern was not specified, otherwise it returns all commands that match the pattern. <@li><@TT>objName info default method arg var: Returns 1 if the argument <@TT>arg of the proc (object specific method) <@TT>method has a default value, otherwise 0. If it exists the default value is stored in <@TT>var. <@li><@TT>objName info filter: Returns a list of filters. With -guard modifier all filterguards are integrated (<@TT> objName info filter -guards). With <@TT>-order modifier the order of filters (whole hierarchy) is printed. <@li><@TT>objName info filterguard name: Returns the guards for filter identified by name. <@li><@TT>objName info forward ?-definition name? ?pattern?: Returns the list of forwarders. One can call this method either without the optional arguments, or with the <@TT>pattern or with <@TT>-definition name. When the <@TT>pattern is specified only the matching forwarders are returned. When the <@TT>definition option is used together with a name of a forwarder, the definition of the forwarder with all flags is returned in a way that can be used e.g. for registering the forwarder on another object. <@li><@TT>objName info hasnamespace: From XOTcl version 0.9 on, namespaces of objects are allocated on demand. hasnamespace returns 1, if the object currently has a namespace, otherwise 0. The method <@TT>requireNamespace can be used to ensure that the object has a namespace. <@li><@TT>objName info info: Returns a list of all available info options on the object. <@li><@TT>objName info invar: Returns object invariants. <@li><@TT>objName info methods: Returns the list of all methods currently reachable for objName. Includes procs, instprocs, cmds, instcommands on object, class hierarchy and mixins. Modifier <@TT>-noprocs only returns instcommands, <@TT>-nocmds only returns procs. Modifier <@TT>-nomixins excludes search on mixins. <@li><@TT>objName info mixin ?-order? ?-guard? ?pattern?: Returns the list of mixins of the object. With <@TT>-order modifier the order of mixins (whole hierarchy) is printed. If <@TT>-guard is specified, the mixin guards are returned. If <@TT>pattern is specified and it contains wildcards, all matching mixins are returned. If <@TT>pattern does not contain wildcards, either the fully qualified name is returned, or empty, if no match exists. <@li><@TT>objName info nonposargs methodName: Returns non-positional arg list of methodName <@li><@TT>objName info parametercmd ?pattern?: Returns a list of registered parametercmds the object (or empty if there are none). If <@TT>pattern is specified, only the matching parametercmds are returned. <@li><@TT>objName info parent: Returns parent object name (or "::" for no parent), in fully qualified form. <@li><@TT>objName info post methodName: Returns post assertions of methodName. <@li><@TT>objName info pre methodName: Returns pre assertions of methodName. <@li><@TT>objName info procs ?pattern?: Returns all procs defined for the object if <@TT>pattern was not specified, otherwise it returns all procs that match the pattern. <@li><@TT>objName info precedence ?-intrinsic? ?pattern?: Returns all classes in the precedence order from which the specified object inherits methods. If the flag <@TT>-intrinsic is specified only the intrinsic classes (from the class hierarchy) are specified. If the flag is not specified, the returned list of classes contains the mixin and instmixin classes as well as the classes of the superclass chain in linearized order (i.e., duplicate classes are removed). If the pattern is specified, only matching classes are returned. <@li><@TT>objName info vars ?pattern?: Returns all variables defined for the object if <@TT>pattern was not specified, otherwise it returns all variables that match the pattern. <@/ul> } return "Value of introspected option as a string." } @ Object instproc instvar { v1 "name of instance variable" "?v2...vn?" "optional other names for instance variables" } { Description { Binds an variable of the object to the current method's scope. Example: <@pre class='code'> kitchen proc enter {name} { my instvar persons set persons($name) [clock seconds] } Now persons can be accessed as a local variable of the method.<@br> A special syntax is: <@tt> {varName aliasName} . This gives the variable with the name <@TT>varName the alias <@TT>aliasName. This way the variables can be linked to the methods scope, even if a variable with that name already exists in the scope. } return "empty string" } @ Object instproc invar { invariantList "Body of invariants for the object" } { Description { Specify invariants for the objects. All assertions are a list of ordinary Tcl conditions. } return "empty string" } @ Object instproc isclass { ?className? "name of a class to be tested" } { Description { Test whether the argument (or the Object, if no argument is specified) is an existing class or not. } return "1 or 0" } @ Object instproc ismetaclass { ?metaClassName? "name of a metaclass to be tested" } { Description { Test whether the argument (or the Object, if no argument is specified) is an existing metaclass or not. } return "1 or 0" } @ Object instproc ismixin { ?className? "name of a class to be tested" } { Description { Test whether the argument is a mixin or instmixin of the object. } return "1 or 0" } @ Object instproc isobject { objName "string that should be tested, whether it is a name of an object or not" } { Description { Test whether the argument is an existing object or not. Every XOTcl object has the capability to check the object system. } return "1 or 0" } @ Object instproc istype { className "type name" } { Description { Test whether the argument is a type of the object. I.e., 1 is returned if className is either the class of the object or one of its superclasses. } return "1 or 0" } @ Object instproc lappend { varName "name of variable" args "elements to append" } { Description { Append all the specified arguments to the list specified by varName as separated elements (typically separated by blanks). If varName doesn't exist, it creates a list with the specified values (see documentation of Tcl command with the same name for details). } return "empty string" } @ Object instproc mixin { ?args? "mixin specification" } { Description { If <@tt>$args is one argument, it specifies a list of mixins to be set. Every mixin must be a defined class. If <@tt>$args has more argument, the first one specifies the action. Possible values are <@tt>assign, <@tt>get, <@tt>add or <@tt>delete, it modifies the current settings as indicated. For more details, check the tutorial. } return "if <@tt>$args empty return current mixins, otherwise empty" } @ Object instproc move { newName "destination of move operation" } { Description { Perform a deep move of the object/class (with all information, like class, parameter, filter, ...) to "newName". Note that move is currently implemented as a copy plus subsequent destroy operation. } return "empty string" } @ Object instproc parametercmd { name "variable to be provided with getter/setter method" } { description { Add a getter/setter for an instance variable with the specified name as a command for the obj. Example: <@pre class='code'> Object o o parametercmd x o x 100 puts [o x] } return "empty string" } @ Object instproc noinit { } { description { flag that constructor (method <@tt>init
) should not be called. Example: <@pre class='code'> Class C C instproc init {} {puts hu} C c1 -noinit The object <@tt>c1 will be created without calling the constructor. This can be used to draw a snapshot of an existing object (using the serializer) and to recreate it in some other context in its last state. } return "empty string" } @ Object instproc proc { name "method name" ?non-pos-args? "optional non-positional arguments" args "method arguments" body "method body" "?preAssertion?" "optional assertions that must hold before the proc executes" "?postAssertion?" "optional assertions that must hold after the proc executes" } { Description { Specify a method in the same style as Tcl specifies procs. <@br> Optionally assertions may be specified by two additional arguments. Therefore, to specify only post-assertions an empty pre-assertion list must be given. All assertions are a list of ordinary Tcl conditions. <@br> When instproc is called with an empty argument list and an empty body, the specified instproc is deleted. } return "empty string" } @ Object instproc procsearch { procName "simple proc name" } { Description { Search which method should be invoked for an object and return the fully qualified name of the method as a list in proc qualifier format: 'objName|className proc|instproc|forward|instforward|parametercmd|instparametercmd|cmd|instcmd methodName'. The proc qualifier format reports the command used to create the method. The only exception is instcmd and cmd, which refer to commands implemented in C. E.g., <@pre class='code'> o procsearch set returns <@pre>::xotcl::Object instcmd set. } return "fully qualified name of the searched method or empty string if not found" } @ Object instproc requireNamespace { } { Description { The method <@TT>requireNamespace can be used to ensure that the object has a namespace. Namespaces are created automatically by XOTcl, when e.g. an object has child objects (aggregated objects) or procs. The namespace will be used to keep instance variables, procs and child objects. To check, whether an object currently has a namespace, <@TT>info hasnamespace can be used. Hint: In versions prior to XOTcl 0.9 all XOTcl objects had their own namespaces; it was made on demand to save memory when e.g. huge numbers of objects are created. <@TT>requireNamespace is often needed when e.g. using Tk widgets when variables are to be referenced via the namespace (with <@TT>... -variable [self]::varName ...). } return "empty string" } @ Object instproc set { varName "name of the instance variable" ?value? "optional new value" } { Description { Set an instance variable in the same style as Tcl sets a variable. With one argument, we retrieve the current value, with two arguments, we set the instance variable to the new value. } return "Value of the instance variable" } @ Object instproc subst { options "?-nobackslashes? ?-nocommands? ?-novariables?" string "string to be substituted" } { Description { Perform backslash, command, and variable substitutions in the scope of the given object (see documentation of Tcl command with the same name for details). } return "substituted string" } @ Object instproc trace { varName "name of variable" } { Description { Trace an object variable (see documentation of Tcl command with the same name for details). } return "empty string" } @ Object instproc unset { "?-nocomplain?" "possible error messages are suppressed" v1 "Variable to unset" "?v2...vn?" "Optional more vars to unset" } { Description { The unset operation deletes one or optionally a set of variables from an object. } return "empty string" } @ Object instproc uplevel { ?level? "Level" command ?args? "command and arguments to be called" } { Description { When this method is used without the optional level, it is a short form of the Tcl command <@pre class='code'> uplevel [self callinglevel] command ?args?<@/pre> When it is called with the level, it is compatible with the original Tcl command. } return "result of the command" } @ Object instproc upvar { ?level? "Level" otherVar localVar "referenced variable and variable in the local scope" ?otherVar localVar? "optional pairs of referenced and local variable names" } { Description { When this method is used without the optional level, it is a short form of the Tcl command <@pre class='code'> upvar [self callinglevel] otherVar localVar ?...?<@/pre>. When it is called with the level, it is compatible with the original Tcl command. } return "result of the command" } @ Object instproc vwait { varName "name of variable" } { Description { Enter event loop until the specified variable is set (see documentation of Tcl command with the same name for details). } return "empty string" } # procs of Object @ Object proc getExitHandler {} { Description "Retrieve the current exit handler procedure body as a string." return "exit handler proc body" } @ Object proc setExitHandler {body "procedure body"} { Description { Set body for the exit handler procedure. The exit handler is executed when XOTcl is existed or aborted. Can be used to call cleanups that are not associated with objects (otherwise use destructor). On exit the object destructors are called after the user-defined exit-handler. } return "exit handler proc body" } # class @ Class Class -superclass Object { description { This meta-class holds the pre-defined methods available for all XOTcl classes. } } @ Class instproc alloc { obj "new obj/class name" ?args? "arguments passed to the new class after creation" } { description { Allocate an uninitialized XOTcl object or class. Alloc is used by the method <@tt>create<@/tt> to allocate the object. Note that <@tt>create<@/tt> also calls as well configure and init to initialized the object. Only in seldom cases the programmer may want to suppress the <@tt>create<@/tt> mechanism and just allocate uninitiaized objects via <@tt>alloc<@/tt>. } return "new class name" } @ Class instproc allinstances { } { description { Compute all immediate and indirect instances of a class } return "fully qualified list of instances" } @ Class instproc create { objName "name of a new class or object" ?args? "arguments passed to the constructor" } { description { Create user-defined classes or objects. If the class is a meta-class, a class is created, otherwise an object. The method <@tt>create<@/tt> is responsible for allocating and initializing objects. The method can be overloaded e.g. in a metaclass if other initialization behavior is wanted.

The standard behavior of <@tt>create<@/tt> is as follows:

  1. Call the method <@tt>alloc<@/tt> to create an uninitialized object.
  2. Call the method <@tt>searchDefaults<@/tt> to set default values for instance attributes-
  3. Call the method <@tt>configure<@/tt> to configure the object with the values provided at object creation time. The method <@tt>configure<@/tt> interprets the arguments with leading dashes as method calls.
  4. Call the method <@tt>init<@/tt> to allow initialization by the class. The argument passed to init are the values from the passed argument list containing the arguments up to the first '-'.<@p>

Create firstly calls <@tt>alloc in order to allocate memory for the new object. Then default values for parameters are searched on superclasses (an set if found). Finally the constructor <@tt>init is called on the object with all arguments up to the first '-' arg.<@p> The <@tt>create method is often called implicitly through the <@tt>unknown mechanism when a class (meta-class) is called with an unknown method. E.g. the following two commands are equivalent <@pre class='code'> Car herby -color red Car create herby -color red <@/pre> When a users may want to call the constructor <@tt>init before other '-' methods, one can specify '-init' explicitly in the left to right order of the '-' method. Init is called always only once. e.g.: <@pre class='code'> Class Car -init -superclass Vehicle <@/pre> } return "name of the created instance (result of alloc)" } @ Class instproc info { args "info options" } { Description { Introspection of classes. All options available for objects (see <@a href="#Object-info">info object) is also available for classes. The following options can be specified: <@ul> <@li><@TT>ClassName info classchildren ?pattern?: Returns the list of nested classes with fully qualified names if <@TT>pattern was not specified, otherwise it returns all class children where the class name matches the pattern. <@li><@TT>ClassName info classparent: Returns the class ClassName is nesting to. <@li><@TT>ClassName info heritage ?pattern?: Returns a list of all classes in the precedence order of the class hierarchy. If pattern is specified, only matching values are returned. <@li><@TT>ClassName info instances ?-closure? ?pattern?: Returns a list of the instances of the class. If <@TT>-closure is specified, the resultet contains as well the instances of subclasses. If <@TT>pattern is specified and it contains wildcards, all matching instances are returned. If <@TT>pattern does not contain wildcards, either the fully qualified name is returned, or empty, if no match exists. <@li><@TT>ClassName info instargs method: Returns the arguments of the specified instproc (instance method). <@li><@TT>ClassName info instbody method: Returns the body of the specified instproc (instance method). <@li><@TT>ClassName info instcommands ?pattern?: Returns all commands defined for the class. If pattern is specified it returns all commands that match the pattern. <@li><@TT>ClassName info instdefault method arg var: Returns 1 if the argument <@TT>arg of the instproc (instance method) <@TT>method has a default value, otherwise 0. If it exists the default value is stored in <@TT>var. <@li><@TT>ClassName info instfilter: Returns the list of registered filters. With -guard modifier all instfilterguards are integrated (<@TT> ClassName info instfilter -guards). <@li><@TT>objName info instfilterguard name: Returns the guards for instfilter identified by name. <@li><@TT>objName info instforward ?-definition name? ?pattern?: Returns the list of instforwarders. One can call this method either without the optional arguments, or with the <@TT>pattern or with <@TT>-definition name. When the <@TT>pattern is specified only the matching instforwarders are returned. When the <@TT>definition option is used together with a name of a isntforwarder, the definition of the instforwarder with all flags is returned in a way that can be used e.g. for registering the instforwarder on another class. <@li><@TT>ClassName info instinvar: Returns class invariants. <@li><@TT>ClassName info instmixin ?pattern?: Returns the list of instmixins of this class. If <@TT>pattern is specified and it contains wildcards, all matching mixin classes are returned. If <@TT>pattern does not contain wildcards, either the fully qualified name is returned, or empty, if no match exists. <@li><@TT>ClassName info instmixinof ?-closure? ?pattern?: Returns the list of classes, into which this class was mixed in via instmixin. This is the inverse function of <@TT>ClassName info instmixin. If <@TT>-closure is specified, also the classes are returned, for which the class is indirectly mixed in via instmixin. If <@TT>pattern is specified and it contains wildcards, all matching mixin classes are returned. If <@TT>pattern does not contain wildcards, either the fully qualified name is returned, or empty, if no match exists. <@li><@TT>ClassName info instnonposargs methodName: returns list of non-positional args of methodName <@li><@TT>objName info instparametercmd ?pattern?: Returns a list of registered instparametercmds the class (or empty if there are none). If <@TT>pattern is specified, only the matching instparametercmds are returned. <@li><@TT>ClassName info instpost methodName: Returns post assertions of methodName. <@li><@TT>ClassName info instpre methodName: Returns pre assertions of methodName. <@li><@TT>ClassName info instprocs ?pattern?: Returns all instprocs defined for the class. If pattern is specified it returns all instprocs that match the pattern. <@li><@TT>ClassName info mixinof ?-closure? ?pattern?: Returns the list of classes, into which this class was mixed in via per object mixin. This is the inverse function of <@TT>Object info mixin. If <@TT>-closure is specified, also the classes are returned, for which the class is indirectly mixed in as a per-object mixin. If <@TT>pattern is specified and it contains wildcards, all matching mixin classes are returned. If <@TT>pattern does not contain wildcards, either the fully qualified name is returned, or empty, if no match exists. <@li><@TT>ClassName info parameter: Returns parameter list. <@li><@TT>ClassName info subclass ?-closure? ?pattern?: Returns a list of all subclasses of the class. If <@TT>-closure is specified, the result contains as well the subclasses of the subclasses. If <@TT>pattern is specified and it contains wildcards, all matching subclasses are returned. If <@TT>pattern does not contain wildcards, either the fully qualified name is returned, or empty, if no match exists. <@li><@TT> ClassName info superclass ?-closure? ?superclassname?: Returns a list of all super-classes of the class. If <@TT>-closure is specified, the result contains as well the superclasses of the superclasses. If <@TT>pattern is specified and it contains wildcards, all matching superclasses are returned. If <@TT>pattern does not contain wildcards, either the fully qualified name is returned, or empty, if no match exists. <@/ul> } return "Value of introspected option as a string." } @ Class instproc instdestroy { obj "obj/class name" ?args? "arguments passed to the destructor" } { Description { Standard destructor. Destroys XOTcl object physically from the memory. Can be overloaded for customized destruction process. <@p> In XOTcl objects are not directly destroyed, when a destroy is encountered in a method. Beforehand, the interpreter looks up whether the object is still referenced on the method callstack or not. If not, the object is directly destroyed. Otherwise every occurrence of the object on the callstack is marked as destroyed. During popping of the callstack, for each object marked as destroyed, the reference count is decremented by one. When no more references to the object are on the callstack the object is physically destroyed. This way we can assure that objects are not accessed with [self] in running methods after they are physically destroyed. } return "empty string" } @ Class instproc instfilter { ?args? "instfilter specification" } { Description { If <@tt>$args is one argument, it specifies a list of instfilters to be set. Every filter must be an XOTcl proc/instproc within the object scope. If <@tt>$args it has more argument, the first one specifies the action. Possible values are <@tt>assign, <@tt>get, <@tt>add or <@tt>delete, it modifies the current settings as indicated. For more details, check the tutorial. } return "if <@tt>$args return empty current instfilters, otherwise empty" } @ Class instproc instfilterguard { filterName "filter name of a registered filter" guard "set of conditions to execute the filter" } { description { Add conditions to guard a filter registration point. The filter is only executed, if the guards are true. Otherwise we ignore the filter. If no guards are given, we always execute the filter. } return "empty string" } @ Class instproc instforward { methodName "name of forwarder method" ?options? "-objscope, -methodprefix string, -default names, -earlybinding, -verbose" ?callee? "named of the called command or object" ?args? "arguments" } { Description { Register a method for the instances of a class (similar to an instproc) for forwarding calls to a callee (target Tcl command, other object). When the forwarder method is called, the actual arguments of the invocation are appended to the specified arguments. In callee an arguments certain substitutions can take place:

  • %proc: substituted by name of the forwarder method
  • %self: substitute by name of the object
  • %1: substitute by first argument of the invocation
  • {%@POS value}: substitute the specified value in the argument list on position POS, where POS can be a positive or negative integer or end. Positive integers specify the position from the begin of the list, while negative integer specify the position from the end.
  • {%argclindex LIST}: take the nth argument of the specified list as substitution value, where n is the number of arguments from the invocation.
  • %%: a single percent.
  • %Tcl-command: command to be executed; substituted by result.
Additionally each argument can be prefixed by the positional prefix %@POS (note the delimiting space at the end) that can be used to specify an explicit position. POS can be a positive or negative integer or the word end. The positional arguments are evaluated from left to right and should be used in ascending order. valid Options are:
  • -objscope causes the target to be evaluated in the scope of the object,
  • -methodprefix string inserts the specified prefix in front of the second argument of the invocation,
  • -default is used for default method names (only in connection with %1)
  • -earlybinding: look up the function pointer of the called Tcl command at definition time of the forwarder instead of invocation time. This option should only be used for calling C-implemented Tcl commands, no procs etc.);
  • -verbose
  • : print the substituted command to stderr before executing
See tutorial for detailed examples. } return "empty" } @ Class instproc instinvar { invariantList "Body of invariants for the class" } { Description { Specify invariants for the class. These are inherited by sub-classes. The invariants must hold for all instances. All assertions are a list of ordinary Tcl conditions. } return "empty string" } @ Class instproc instmixin { ?args? "instmixin specification" } { Description { If <@tt>$args is one argument, it specifies a list of instmixins to be set. Every instmixin must be a defined class. If <@tt>$args has more argument, the first one specifies the action. Possible values are <@tt>assign, <@tt>get, <@tt>add or <@tt>delete, it modifies the current settings as indicated. For more details, check the tutorial. } return "if <@tt>$args empty return current instmixins, otherwise empty" } @ Class instproc instparametercmd { name "variable to be provided with getter/setter method" } { description { Add a getter/setter command for an instance variable with the specified name. This method is used for example by the <@A href="#Class-parameter">parameter method. Example: <@br> <@pre class='code'> Class C C instparametercmd x C c1 -x 100 puts [c1 x] } return "empty string" } @ Class instproc instproc { name "instance method name" ?non-pos-args?" "optional non-positional arguments" args "instance method arguments" body "instance method body" "?preAssertion?" "optional assertions that must hold before the proc executes" "?postAssertion?" "optional assertions that must hold after the proc executes" } { Description { Specify an instance method in the same style as Tcl specifies procs. <@br> Optionally assertions may be given by two additional arguments. Therefore, to specify only post-assertions an empty pre-assertion list must be given. All assertions are a list of ordinary Tcl conditions. <@br> When instproc is called with an empty argument list and an empty body, the specified instproc is deleted. } return "empty string" } @ Class instproc new { "?-childof obj? ?args?" "args passed to create" } { description { Convenience method to create an autonamed object. E.g.: <@pre class='code'> HTTP new creates ::xotcl::__#0, a subsequent call creates ::xotcl::__#1, ...<@br> If <@tt>-childof obj is specified, the new object is created as a child of the specified object. } return "new object name" } @ Class instproc parameter { parameterList "list of parameter definitions" } { description { Specify parameters automatically created for each instance. Parameters denote instance variables which are available on each class instance and that have a getter/setter method with their own name. Parameters are specified in a parameter list of the form {p1 p2 ... pn}. p1 ... pn may either be parameter names or definitions of the form {parameterName defaultValue}. If a default value is given, that parameter is created during creation process of the instance object, otherwise only the getter/setter method is created (and the parameter does not exist). The getter/setter method has the same name as the parameter. It gets and returns the parameter, if no argument is specified. With one argument, the parameter is set to the argument value. <@br> Example: <@pre class='code'> Class Car -parameter {{doors 4} color} Car herby -doors 2 -color green <@/pre> } return "empty string" } @ Class instproc parameterclass { class "parameter class name" } { description { Set the parameter class. The parameter class specifies how parameters are stored and maintained internally. Per default, a method "default" is called, to set the parameter with a default value. I.e., <@pre class='code'> Class Car -parameter { {doors 4} }<@/pre> is a short form for <@pre class='code'> Class Car -parameter { {doors -default 4} }<@/pre> For specialized parameter classes other methods can be called, e.g.<@br> <@pre class='code'> {doors -default 3 -updateWidget car}<@/pre> } return "empty string" } @ Class instproc recreate { obj "obj to be recreated" ?args? "arbitrary arguments" } { description { Methods called upon recreation of an object. Recreate is called, when an object/class is created, but a same-named object/class exists already. "recreate" is not called, when an object is trying to be recreated as a class or vice versa. In these cases, recreating is realized via destroy+create. The Methods "recreate" performs standard object initialization, per default. May be overloaded/-written. It calls another method cleanup which handles actual cleanup of the object during next. That means, if you overload recreate, in the pre-part the object still contains its old state, after next it is cleaned up. } return "obj name" } @ Class instproc superclass { classList "?list of classes?" } { description { Specify super-classes for a class. "superclass" changes the list of superclasses dynamically to <@tt>classList. The method returns the current value of superclass, when it is called without arguments. } return "if <@tt>classList is not specified return superclass(es), otherwise empty" } @ Class instproc unknown { ?args? "arbitrary arguments" } { description { Standard unknown mechanism. This mechanism is always triggered when XOTcl does not know a method called on an object. Supposed that there is no method with the called name, XOTcl looks up the method "unknown" (which is found on the Class Object) and executes it. The standard unknown-mechanism of XOTcl calls create with all arguments stepping one step to the right; in the general case: <@pre class='code'> ClassName create ClassName ?args?<@/pre> Unknown can be overloaded in user-defined subclasses of class. } return "Standard unknown mechanism returns result of create" } @ Object instproc volatile { "" "" } { description { This method is used to specify that the object should be deleted automatically, when the current Tcl-proc/object-proc/instproc is left. Example: <@pre class='code'> set x [Object new -volatile] } return "empty string" } @ Class proc __unknown { "name" "name of class to be created" } { description { This method is called, whenever XOTcl references a class, which is not defined yet. In the following example: <@tt>Class C -superclass D D is not defined. Therefore <@tt>Class __unknown D is called. This callback can be used to perform auto-loading of classes. After this call, XOTcl tries again to resolve D. If it succeeds, XOTcl will continue; otherwise, an error is generated. <@p> This method is called on mixin/instmixin definition calls, istype, ismixin, class, superclass and parameterclass } return "empty string" } @ Class ::xotcl::Slot -superclass Object { description { A slot is a meta-object that manages changes of properties of objects. A property is either an attribute or a relation (defined in the system slots). The predefined system slots are class, superclass, mixin, instmixin, filter, instfilter. These slots appear as methods of Object or Class.

The slots provide a common query and setting interface. Every multivalued slot provides e.g. a method add to add a value to the list of values, and a method delete which removes it. See for example the documentation of the slot mixin.

Parameters:

-name Name of the slot to access from an object the slot
-domain domain (object or class) of a slot on which it can be used
-multivalued boolean value for specifying single or multiple values (lists)
-defaultmethods list of two elements for specifying which methods are called per default, when no slot method is explicitly specified
-manager the manager object of the slot (per default [self])
-per-object specify whether a slot should be used per class or per object; note that there is a restricted usage if applied per class, since defaults etc, work per initialization

For more details, consult the tutorial.

} } @ Class Attribute -superclass ::xotcl::Slot { description { Attribute slots are used to manage the setting and querying of instance variables. Parameters:

-default specify a default value
-type specify the type of a slot
-initcmd specify a Tcl command to be executed when the value of the associated variable is read the first time; allows lazy initialization
-valuecmd specify a Tcl command to be executed whenever the variable is read
-valuechangedcmd specify a Tcl command to be executed whenever the variable is changed

Example of a class definition with three property slots:

<@pre CLASS="code"> <@tt>Class Person -slots { Attribute name Attribute salary -default 0 Attribute projects -default {} -multivalued true } Person p1 -name "John Doe"

The slot parameters default, initcmd and valuecmd have to be used mutually exclusively. For more details, consult the tutorial.

} } #Class::Parameter instproc values {param args} #proc xotcl_mkindex #proc xotcl_load library/xotcl/doc/logo-100.jpg000066400000000000000000000032521242365656200163750ustar00rootroot00000000000000ÿØÿàJFIFÿÛC    $.' ",#(7),01444'9=82<.342ÿÛC  2!!22222222222222222222222222222222222222222222222222ÿÀ*f"ÿÄÿÄ7!1A"Qqa#2b¡²Ñ$B‘ÁDrt±ðÿÄÿÄ#!1Qa2A¡ÿÚ ?ÇŒž\dõ©Hˆ»&m§ ŒÓ…—|%¬wú¬»@!Ì#¨QͳŸ—j‡jšV®R-6Ïc À” nôÍ%W-±‘ “´¨éacopÒ,Šø«Œû{•¾ƒssmñB1m'~z{Õ¾¸[ŒÚÞ[‹¸cÆÄs…^g÷?Þ´+›ë ]!D6ÙGBžHÅQ*­òÄàBÌWi’ªµ³Ha‚«zV™>¢eH·1T.p:zUéL×òmÎÝØ¡Ål:m…¬t f¤C°'©Ïsó¬ÓÐ-b ØDîTLr{yàw!½NÃÇyÖ(÷nÁÀ­Zó…`ÔÎÑ㸱á¦ðµ´ñܫڥIe “ìk[Háºaîí3õK±¦4›D˜8'ð«6ZŽ=·E˜çÈ䜊Ò[GµXeˆF»dmÃËÒ„êü3–,Öè¤ƒË ÍEci-MÄ=Àv2Η*Ín¡X¸ÛÇ[ÇËûPžµšÞV‰‰* €Ç¿|Q÷#5ëi.-PÌ“ sÔ noøíú–º½¸&"º´Ä©çýK]\š¶Í³¢¯¬Â-înõI‚Þ^LêÎ>Û“Ìú yƒ!»D{­ð]ªs?xþÔG°kxÕŒGzô t>ôfââþ[ibø‰†ô+͈¨i¨îï 6ûD©t-OKøBЗ¶‚M¾2òܧ¿ø4Å«k‹' Ú_Áoõ’«DÌ<«úëS‚Ñ­4ùíLïðÒƒ½ŽÑJ6‘DuhìÚv{?1œrcš-S&˜ ¯ Éo˪Ü#µÄîTö^|Ï¿*xáOZAá©‘£;¶Ð0O~ô2ÿQ–ÒÒ5”bRÄù*G¥v³rú»Ba³¶±Ž4¤ ·¯°¢VÊìe¨d:êÓ÷0UlàŸ^†øüqÞÚ%ÀŠkØÙÜ4Œ±ÿ¥:ª]x¶š”8o¼0}é$ÙÉàíITMÔ’¹_Ƨa’Ù£¼…‰,—8Á#üPU½ˆoÓ ­hJçˆûµl²ˆ.]‡"èF3úùÔ¯µ>ÇXÒ¬ž \ê&Ù## ´Ï×9¥hôÈ-PÉzÞ$¯öcÒ=‡,Òî¯Ä×wØÃd¦$Hš7È Y\ŒžŸdtªÕ}„dñæA•3´Ö|K˜øq.Næ5æd€çž3ò¤µžéÆ|g«é–ï#¾} z=²8ÌÔ¸Q⤈½D·ÞZê[ú>7-Ä7 <¬ßÊ·S÷’º¸o<½X¯­bŠ#Y†G^ÕêÚÆž¬ÊgŒàdó ºÔ1%Âí>Ê(}Ê&%ò¯OJŸÌ³ˆ;+%ÄÜH—(¶6l_”’c·¥V¼_ðlᘈIu˜wnôTM í\ûUQÔûÔšçbIŒ =è¼M§ÜiqÛj8øˆ—ä@À‘Ò‰C©ØMâYd%p+fiטÏáU4mAdâ[˽ÉoQ-'¤ÛÕŽJä|†{â–¶+H7(8é‘QñÖmÖîÑ6áÎ3´þT;Ò; Š 6aûî-‡FÔ †§¥\Z\*‚ÈY[h##˜ùb¾/é¯.Óo"·,+ Iú۵Ƥ$™ŒŽcPYÎIÇÌÑD&âxdœ™\B3ùˆ É«U¾`4¤Õ~Žuë{þ"¸Š8]mŽÜŸ½u ú-q5Æú'ýi]A¬g91*€0'ÿÙlibrary/xotcl/doc/next-path-mixin-movement.gif000066400000000000000000000054111242365656200220050ustar00rootroot00000000000000GIF87a3»÷ˆˆˆðððøøøßX‹F@X€Fòÿ¿°¥Tˆ@<Ð0MNM›,ÿòñÿÿ@¿¿L «ñÿ@¿Ü¤ßv&‹ @@tXXóFFÿ¿ X°óFT”Ä4 TM@t^ócMÿ¿Ü°vTÛX~vF• @°"òÿ¿L «@¤X&F ä¼tÏòMÿ@¿¥hˆñÿ@¿€fLl\X~F•@¤"& HX óFÿ¿ÔÛòÿ@¿ø¥|KˆM@€HhLMñÿ¿\T˜òlÿ¿¤X&F @€¤ƒL& @@tX°6X" FøÄKT€ Lc°óÿ¿UX4EFüt]óñ ÿÿ¿¿¥Zˆs@$||ôMuÿ„€òÿàLj¤„ó&ñÿ ÿ¿@¿[XaFg X0ôFs$ÄÐôTöÿÿ¿¿„€ôÿX›F|;°4„TÄ€N¤Ð&ö ÿNИöRôóÿ¿)nHO°T#àÐòöÿÿ¿¿|Õüó¸@@>¤H&óOÿ °@TäHÏóÀËìßX‹F,3»þH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H XÊ´©Ó§P£JJõiÒ«XÀµ«×¯`ÊK¶¬WYÓªUj¶­Û·pÑ®‹u+Ü»xñÊ¥Ëרݼ€‹ÝÛ·pп‚ &l¸1OÄ™rE YñWÆŽ3߬¬wòV΃cÖLZ&è·”HV­z©çÕ‘K˦yÚmêϯ]§^<»7ÌÚm]·fmWøäãÀÍŽöͼdrå«£·.®[tóë(Ÿ/¶}úë½;«-‰•úÐÃ嶺(½žŠÌ-²*÷E2T7<‘Ì ½lòÉ-jLqˬںàÌ4KtB3^lfÅ+ûü³ÐæY h0˜?†\OEßû ƒsœíu‡‚gáúX .&>#Ì ä>(£g:Ã!T^ÃCáüe‡þä‘a³£BVG*<4"nº 1†L4ŽdšXÄäp@áŒn–XœéHG‡]ì" wXÂï¡0Š9,!}Hœ/"‡‰odá`ÎØ™êx1ŒeäMàhÂQK#q*(F1¢Qޤ£™F5ò,‚üaks?½LÑ‚4¤K‡«ï=²‰¹™$ E "ò‰¡á¢©3Ê$rÑ‹8Œå]˜6*§œà¦hE(^0‰¤¥b4A—ñr“Cµò¹|š­(ÿDhAsP¢$Ô¡ ÍbCýÑŸ<”¢S¨=5ŠO†f¢iG%úQŒŽÔ¢õÉ@íÅÑ~4¤&ËJrÑ—žT¥µ©K=zÓžÌÔa jKÚÒŸ-¥>Í)OwJÒž>ÆeðT§ô¢ ½©R•yj¼PŠÐ­‚ŽNC§_Ü%;°’F5ˆW¿Ú¨µrè¬:UÚîüéÖÉnX*=ïzŹƎC|½J^Z¡Ç¤<|ÒaÏ“X˜²”S­šcX½Ís²Á™Z#;2ÌR‰Y•õÚeGf2¨U¯d«YÇöN«ä ´¡EþÜhY¯lÅK[^eÎ<±9K³Zl¢8ÚÔ– µ«íí}Š [Ôìµ6®ÏbôÙpŶR­€[SÌrKº¦¢Ow1¢]¤^×%¥éTqËZ. Œ¹¬eiE£k«ôv6b½5î>†Ûþu³¡o«¸ù3üìÀÿB˜‚™j/Þìµ·Eî€í«Ú`9GTý…n†;&1#ŠÀ`KR{‘‹_}­ÖgVÑy^C­ø*-~ñ‰ÔÔ\gN$îµ±l³ƒcÄIÊlŒäžù¸7E6²Öà3ä§xHÖ=2Kž e)oé»WÅ•EÊ·›¦É[pƒÀ,!*S²8 sõØÌ 1§YÍLrE¤Kæ ÖÂ!9”—á\ºÙ’wÏÑZTíÜ£ðà+Ï|nj û¯D“ÏÏÁm4£1æhѲ™³‹žô>+½f9ÿ5®±-®›ÉÇc½†ÖÄ C3©ÅåëB÷f7³^’c<èSµ¶¶ÕŸà˜œå罸Ä>.\«E¼;¶Õœ–ÔÞ ·k»þúÌ)³½×SWŦ®ìˆo.Ú9 5;}+=rS¶ÇÈ÷m][=M *ÛÉqLãíyÓ{l ¾7¾¹ªïÂ*ºß›ùé·N›jܰ½ÖÏÁÎð†;üá¸Ä'NñŠ[üâÏ8;library/xotcl/doc/next-path.gif000066400000000000000000000066631242365656200170450ustar00rootroot00000000000000GIF87aUB÷°´°øüø -@pažth.ögÿi¿fã'È?@¿d„DõÂóÿÿ¿@¿Â@Ä]¤ó ÿ¿4töôÿÿ¿¿dPõóÿÿ¿¿¸¸ÐÌó ÿ@¿ÿàþÿtÿ!ÿ@@¸Œ”Ìô¥ÿ¿d_Põóÿÿ@¤Œ¸„! @@lùôžÿÿ¿¿¸öžÿ¿¸tPôó ÿÿ@¿¿dô¶ô¶ÿ|óÿ¿Èü¤óÿ¿\„ôÿ¿¸ˆõó€õô$þÂôtÿ@¿@@À¤õ¥ÿÈþˆ¤tóÿ\¥¸¸ô ÿ Ȥž¬¸õžÿ ¿@:ˆ žóÿ@¿@¤žȤôÿ¿èõÿ¿É¼¸_ôÿ ¿@\¬ü5òÀÄKøüoó娕k÷m÷lwî“£þOϰúçÍÁ}§¯»»ûëŒÅ“ŸÐ|ßëë¿ggΞ5üÅòÑ' @öñ…_ú­çÝÿÝ6àƒ ¸×ï!ƒNá†e¨W{ËYˆà~úeg‡( àá‡àåWasÞýöZŠ®ž3Ò!ŽòžŽ;ÚÆ¢Ÿù D– ‘²!™dgÄ ¶$“r9ùdŽ•¥¢–Tªfå•?n)¥–Sv\h¦©æšh‰éf\ešÙUœ?9'™xÒ)§UzÒEÐcJY™{jÕ'O°Á©â–ƒ¾yh¡B=ªS”\.(¤|>%^Ÿ¸Å…©R’¶Z ‹~ZT¨8¡êЦ¥ªMþ¯Bk«+Í:“­áJkGºÂÔ+EŠîZÓ¯.K©Æ +«¦°2ª¬LÉÖšê³·2K-fÑŽéµ'e›’·ÊíHàvi¥ãÎf-P¤¦ûQ¹&Á®³îr$/I÷Î[/¯ëî{f¿þ~•¯H›'¬0ºëXpÃ5Ç0Ä‘é&.FS\ÑÃDyŠ1ǘ‚¼-hxfé)—yм§Êì¢Ë*Êch'ËrÒܲÉ2; s¥k<‘Í>¹<³˜ í3Æ¥Ž3½&ó ´‘—=}3£K/MõÑ?w¨ôÐDçLoÏX+ÔiØûF'5ÙcÉw6Ú7´6Û[•ùvL‚ÂM0u¢yl·þGœfvñÞönì÷ܦ.¬÷\`~·ÅL+¾šÄ‰;þ®t„»[¹ämcžîåšsÅyçá.ú褗~8èÇíè«ÁŸ#êkë^Á>i±‘?+»¨š±®:êÝ>-ïHI¶éÄ‹®YñÈ—.à—`¨YóÐæ{dÑW_0óÕŸHöÙYl÷àw·|øW^O~÷Óßz~’æ¯}úк/dûòƒ ?Ýõ×Fþì?ZõÛßod àd(-îQ’ÿœ##÷ °BÉAŒÀã™ûù J’Ùε¢VðyxÁÐù“ ÉD!âÏH ‚%Œ!‰ ¥–¨.ÊO{Ô#"~'‡þ#â‡hBp‡>$ ƒ¾BÊPA ¢Èü¸‡þ!âJ„Ä'úp86L §£ QŒ/º"¤ÅøDñˆ$‘#À ÅñŽpôâ õhGïÍG‚o좿ˆ%–øG…x¤"´ÇE&’Ûà WHÃ9&'ŒT̤&W(Ç)6ñ‡û¡¤ÛÈ¿BÖj}–l ™XʲÒz„|Ò*[½bze´)[Ÿ[òR‘ü%_)ÌÓ처Ðñ%2µ÷Çe†‰–Î4æö’GÍ4¯šØdÓò¦­Lm›ßºÊ, N”ŒÓ çìfS®'Îé°Ê)ìôfÞ³UƳ*éþä²PVNsÉ35—²S?ãÕÎØì3K}Ü?ó–§?%_e(² ÏÜ-2 e˜=-ŠO/E-Q%WD§µ’®“£Tɧ9Cº¸Ž~³¢Å©ÒX•qµCÈFczÑØÕM";¥¥Lc禬ÁT¨=õ\N#DSu%5+ÁVS'÷TC…*¨ä¬*T?–Í®¦åš^ +[fÓòx¹ËéR‡V¶*We­ØQ’µTs–ì¬TZkÈõÓ×µ ¯Pëë¶øÊ±€ò-¯ìÝôú­†ò‹IŒÝ+¾b–»¨î²‚Ù¨ {×Çv®U(-{ضÕ¤Q¹k]—eZÖ¢*‘ÕþWkÝÆT©d+®”³”:—‚[‰Ñ¶[.ìmnäf×û w·A¡“·þ ZÅö&}‹e?†T­¶ZR•®ª,»Rç^ntÝÍÚ;¥•¢¿!en‘ílVË{ݼ´è„å–”YW¹n¼¿ƒœçZ¶¶ãþiºgñZÊâ¶;ŸÖshVk/¨ <'s-À^3ƒ•š³¯F8±´kX‡jÕ 'ªh‡ÒU-åWVÿâŒjp}Å–´ÝDÓyZÅçƒOÆ5 [ʼ¹zñ]j&ünLÇtä0˜{ _2Æåmq‘,=!“•ÉM~Dm·ZÞœ±Aj´¢õG7ºRë b›Ð•[8"17Ð5±ê,þ„«öÜ´R™Èõù"CùȽ1…,ìN)(¾ÐØÃ*ž2·êË39sð=yó ë\Iàì&Å:± Éú'+Žq†‹¥–c$HE‡ðц0ÓŒ7Š‘,„Xd7˜ê<ÎùB F1:%-Ò°¡Žá¾(ÄNÇ2ÍV-‚[z4+ß¹“$B6F`«8Á­5ÖŒÝê=WÏ7Ô`• k$×kÄt2ë lq ܨ4·¿Ð}¾Ü~Lé»ám u—Þõö¶å4¼a}‹Ôîµ÷¿Û¬XySúdSµï¹¥äû;dûèQ þíŒÞtâS1ñ©‹ÕŒëNo·-ëþÔœðS­¾!OmÙ~J²’wìÛ6nxRÜ]qc\ä¶v9Y†§ó‚û,å°åùÍUžó¡½è'}8fãôž[wÅ:u:»”5ÝUI]²»"8E“ÎãVá¸<üöªÀóz6;u¶¥Ù}+<´§}›nw-Üãnõ¶Óý·j¿ûªÂÎw±ÒjíTÕ»Nî7Á3ð¥5ü¬ïYÅŸXuŒ á1ZÇÇ<·[wüä 36ËÛ=¾žimCïN¼“žqu?}“Þ®zÉ•î‘?mvã¾ùŽö±¬çkÿ˜Ücê¥?½ÖW/|™“Ç÷”ØlyùÖ2}Ǧw¿FãPçžÝäCþ£ÿþ,ëH÷ßb/ #í´×§üC?ŠEm´c¥ý£T¿zeV~ >Vðÿ´ü)+ì ãíþÔ“}ötÏ6c'+UÇ[8€l3|ìâF™†FŸT>Š3K+‚Bg6$ØzøÓ# E›TfœVf;Ö9¾sÄmýaBš–Jég‚ ø€=B!4”j¾ÖK¨s)ÉtiEgT‚ Ã]t¹Öl‰TmAÖVØÂ±lŒvƒ0¦wèb˜gc¤‚Ê&„´÷&(€Ú'0ðuA †j6DH0 (ˆ3iø.kh†ƒƒ7Å|Ø>rP2†ëWq؆XTƒg‹â‚F–‡¢ÓÔ(i}bˆ36U¸:ŽX]LˆYwu“8{•˜‰ëµ‰œˆ\÷‰á剢èO\WŠ¢EЍ8i¼µŠet®Øx­‹Ò¦Š´x¶x‹²Šº˜x¹Ø‹²õ‹À<§8ŒÁ8‹ÆxY°˜Œæ‡ŒÌHŒÂøŒ²WŒÒ,ŒHMÕ˜Ú¸ÜØÞøàŽâ8ŽäXŽæxŽè˜Žê¸ŽìØŽîøŽð@;library/xotcl/doc/obj_class_system.gif000066400000000000000000000053501242365656200204700ustar00rootroot00000000000000GIF87aÓ÷ˆˆˆðððøøøßX‹F@X€Fòÿ¿°¥Tˆ@<È0MNM›,ÿòñÿÿ@¿¿L «ñÿ@¿Ü¤ßv&‹ @@tXXóFFÿ¿ X°óFT”Ä4 TM@tjócMÿ¿Ü°vTÛX~vF• @°nòÿ¿L «@¤Xk&F ä¼€ÏòMÿ@¿¥hˆñÿ@¿€fLl\X~F•@¤n& HX óFÿ¿ÔkÛòÿ@¿ø¥ˆKˆM@€HhLMñÿ¿\T˜òlÿ¿¤X&F @€¤ƒL& @@tX°6Xn FøÄKT€lLc°óÿ¿UXmEFüt]óñ ÿÿ¿¿¥Zˆs@$||ôMuÿ„€ò/ÿìLj¤„ó&ñÿ ÿ¿@¿[XaFg X0ôFs$ÄÐôTöÿÿ¿¿„€ô/ÿX›F|;°m„T¼€N/¤Ð&ö ÿNИöRôóÿ¿)n@O°T#àÐòöÿÿ¿¿|Õüó¸@@6¤H &óOÿ °@TäHÏóÀËìßX‹F,ÓþH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊK¶¬Ù³hÓª]˶­Û·pãÊK·®Ý» àŨw¯ß¦}ÿN ,¸pQ†"N ˜%áÅ7!;¶ø¨dž/'Öœ ñ¾Ä7®»8rÝÊŸìrºðãËOCw®ýzôì-}þ«œþ}»yëäáVœ=ûöÊãƒ?=¾vú×át=~÷ݹ‡œ|óѧž€÷qg]ùgŸyÆýç ÛÉ!€õi5á}Úg¡…™íÇa[!>ç቙1¸UsØa÷^r·‹vx^zYÑØ"xäõÈ!tå©xUÍx#Œ…–$CB¾Õä’PF)å”TViåW.˜å–ãq‰¡—`†ô$]c†iæ—[–y¦™jÆÕæš^¾é$œtò¦œuæ©çž|öé矀*è „j衈&:莌âI‘£b6Úbd¼IêbØ  é¦œvÚé¥Õù”©§¤z*#Lòu\©¬n j•þĵ*ë¬À˜Rªƒ G뮦ڊ®ÅÊë°šúºYlÄ&ËjbÖ¤«²ÐrŠ#IÀ2 \´ØÖ:­]Ïfëm±ÛvT-BÝ~›-³âÆt­¹ß†ëÖºìÆ n³Õ ï½Hz4.söÞË®»h!ëïÀùj´ïwû 0n+ œ°¼÷§o¿qEã:\±ÂcV ïÆ_,Ô“ƒlñP%›ŒïÁ|©\1Ë ™²Ë E1Í ‹ÙÍ8¯Œr‘Ò» Ï=ŸŒÑEãÛÓÌIÇ«³M>ÈdÓ&?]/Õ[]ÖYÿu}"#ÍuÍP 2Ì¥‰mö¿hç–zBŸ¶öÙå5w×uë{7þÞT©½÷¿µé÷ßíæ3á/OÅ4â€Vã·VæàŸ«“¥Vžxn¥=ªyÎÁ’KÑçç$å¤GËZç§N°ãÙ†ºëÄjÝ0Æ´sì™è­ç1ç±æ;ÄPêùðNÏ$¿Ýé¸íìÈï €òË¿H £ûEŸ<ÔÇko®Õ!^(õ‰ìyÏvCáøŸúÆ™ßnm‹Ø»ûÞN;†)v(7ýõS¾ø2Qùøw.ÿ©KxÄÖô ¸#½ IÐK`©¦‡çao€TÖw3¿³­+‚.£`jh¯›0gð MÑL¸6΄6'T«Šõ· ŽH\!œa­xChQPyþ¶ë yˆ3Pˆ9#U³ø¡«eUûÔiµ8|51nYbý¤§E6ñ…HLb¯®Õ¢žaRÏì¥+6 LXUC£W¥Æ7’Q[ý‚£¯(É©Q‰:¤XèÄùÈìÒš¢ïhÆB>/‹ˆÜañˆ¬Að‹G VM¥Cm«‘_œÖ!7ùÉIjŒ…Uk¢#µųÍД¨¤! ù˜ÉÏÔ‘Œ±*ciÕÔh–fc4?º`*î™Ó<â+§ëg… XOTcl - Tutorial

XOTcl - Tutorial - Index

Version: 1.6.4

Introduction

 
Language Overview

XOTcl [Neumann and Zdun 2000a] is an extension to the object-oriented scripting language OTcl [Wetherall and Lindblad 1995] which itself extends Tcl [Ousterhout 1990] (Tool Command Language) with object-orientation. XOTcl is a value-added replacement for OTcl and does not require OTcl to compile.

XOTcl runs in the tclsh and provides a few extension commands. These are offered in a Tcl namespace ::xotcl, and can be imported into the current namespace to reduce typing and improve readability. All Tcl commands remain available (and are also applicable on the extension constructs).

A central property of Tcl is, that it uses strings solely for the representation of data. Internally it uses an dynamic type system with automatic conversion (which enables efficient type handling). For that reason all components (e.g. written in C) once integrated in Tcl automatically fit together and the components can be reused in unpredicted situations without change. The evolving component frameworks provide a high degree of code reuse, rapid application development, and ease of use. The application developer may concentrate on the application task solely, rather than investing efforts in fitting components together. Therefore, in certain applications scripting languages like Tcl are very useful for a fast and high-quality development of software (see [Ousterhout 1998] for more details).

Tcl is equipped with appropriate functionalities for the easy gluing of components, like dynamic typing, dynamic extensibility, and read/write introspection. OTcl is an object-oriented extension to Tcl, which encourages a Tcl-like programming style and is composed of language constructs with properties similar to Tcl. It offers an object-orientation with encapsulation of data and operation without protection mechanisms and single and multiple inheritance. Furthermore it enables to change the relationships dynamically, offers read/write introspection, has a three level class system based on meta-classes and offers method chaining. These abilities are integrated in XOTcl with only slight changes to OTcl visible to the programmer.

Extended Object Tcl aims at complexity and adaptability issues that may occur in context of large (object-oriented) software structures and in the context of component glueing. In particular we added the following support:

  • Filters as a means of abstractions over method invocations to implement large program structures, like design patterns.

  • Mixin Classes, as a means to give an object or a classes' instances access to several different supplemental classes, which may be changed dynamically.

  • Dynamic Object Aggregations, to provide dynamic aggregations through nested namespaces.

  • Nested Classes, to reduce the interference of independently developed program structures.

  • Assertions, to reduce the interface and the reliability problems caused by dynamic typing and, therefore, to ease the combination of components.

  • Forwarders, to delegate calls efficiently to other objects or classes.

  • Slots, to manage values of instance variables with a common interface.

  • Meta-data and Automatic Documentation, to enhance self-documentation of objects and classes.


  

Figure 1: Language Extensions of XOTcl

 
Introductory Overview Example: Stack

To give you an impression of the language before we go into the details of the extended language constructs, we present in this section a simple, classical example, familiar to many from introductory programming courses: the Stack example. In the later section, we will present the soccer club example, which focuses more on the dynamic features of the Extended Object Tcl.

In a first step, we define a class Stack. A new class is defined in XOTcl via the command Class create yourclass. The stack will have a constructor (in XOTcl, the method init) and the methods push and pop. Methods which are inherited to objects are defined via instproc. In the following example, all predefined commands (some from Tcl, some from XOTcl) are emphasized.

#
# Create a stack class 
#
Class create Stack 

Stack instproc init {} {
  # Constructor
  my instvar things
  set things ""
}

Stack instproc push {thing} {
  my instvar things
  set things [concat [list $thing] $things]
  return $thing
}
  
Stack instproc pop {} {
  my instvar things
  set top [lindex $things 0]
  set things [lrange $things 1 end]
  return $top
}

The definition of the class Stack is typically saved in a file (say stack.xotcl) and can be used e.g. in an interactive Tcl shell (tclsh) as follows. The percent sign indicates the prompt of the Tcl shell, the reminder of the line is typed in, the result of the command is shown in the line below. Comments are lines starting with a hash symbol #.

% package require XOTcl
% namespace import xotcl::*
% source stack.xotcl
  
# Create Object s1 of class Stack
% Stack create s1
::s1
% s1 push a
a
% s1 push b
b
% s1 push c
c
% s1 pop
c
% s1 pop
b
# Delete object s1
s1 destroy

In the session above, we load XOTcl into the current shell, import the names from the xotcl namespace and we load the file stack.xotcl. At this time, the class Stack is available in the scripting session. In the next step, we create an stack object named s1 and push into the stack the values a, b and c via separate push calls. Then we pop values from the stack and we destroy finally the stack s1.

 
Object specific methods

This definition is pretty similar to stack definitions in many other object oriented languages. The example below shows how to define purely object specific behavior. This way we can define an object stack without the need of a class Stack. Notice that the methods of the object stack are defined via proc and not via instproc in the example of the class Stack.

#
# Create an object named stack
#
Object create stack

stack set things ""

stack proc push {thing} {
  my instvar things
  set things [concat [list $thing] $things]
  return $thing
}
  
stack proc pop {} {
  my instvar things
  set top [lindex $things 0]
  set things [lrange $things 1 end]
  return $top
}

The object stack can be used in exactly the same way as s1 (the instance of class Stack) before.

 
Refining the behavior of objects and classes

So far, the definition of stacks were pretty minimal. Suppose, we want to define safe stacks, that check e.g. for stack underruns (more pop the push operations are issued). Checking safety can be done mostly independent from the implementation details of the stack (usage of internal data structures). With XOTcl, one can define stack-safety as a separate class using methods with the same names as the implementations before, and "mix" this behavior later into classes or objects. The implementation of Safety uses a counter to check for stack underruns.

#
# Create a safety class 
#
Class create Safety
Safety instproc init {} {
  my set count 0
  next
}
Safety instproc push {thing} {
  my incr count
  next
}
  
Safety instproc pop {} {
  if {[my set count] == 0} then { error "Stack empty!" }
  my incr count -1
  next
}

When we load the classes Stack and Safety into the same script, we can define e.g. a certain stack s2 as a safe stack, while all other stacks might be still "unsafe". This can be achieved via the option -mixin during object creation.

% Stack create s2 -mixin Safety
::s2
% s2 push a
a
% s2 pop
a
% s2 pop
Stack empty!
Note that the definition of Saftey can be used not only for instances of the class Stack, but for arbitrary objects supporting the same interface. Therefore we could as well make the stack object stack (that we created before) safe at any time by adding the safety at arbitrary times with the method mixin, and we can remove the safety as well at any time.
# Add Safety to the object stack
% stack mixin Safety
...
% stack push a
...
# remove Safety
% stack mixin {}
We can as well use Saftey to create a new class SafeStack. In this case, all instances of SafeStack have the safety property defined above.
#
# Create a safe stack class by using Stack and mixin 
# Safety 
#
Class create SafeStack -superclass Stack -instmixin Safety

SafeStack create s3

 
Stack of integers

The definition of Stack is generic and allows all kind of elements to be stacked. Suppose, we want to use the generic stack definition, but a certain stack (say, s4) should allow only stacking of integers. This behavior can be achieved by defining an object specific method for the stack s4 that checks the values to be pushed. In case the pushed value is ok, the push definition of Stack is called via next.

# 
# Create a stack with a object-specific method 
# to check the type of entries 
#
# s4 is a stack of integer 
 
Stack create s4
s4 proc  push {value} {
   if  {![ string is integer $value]} {
     error "value $value is not an integer"
  }
   next
}

 
Class specifc methods

In extended object Tcl, classes are objects as well (objects with different properties). We will come to this later in more detail. However, we can define as well methods of classes, which are not inherited to the instances, by defining class-specific methods using proc. This happens exactly like defining objects specific methods as shown before. In the following example, we will define a class-specific method available_stacks that returns the number of the currently existing stack instances.

Class create Stack
# ...
Stack proc available_stacks {} {
   return [llength [my info instances]]
}

Stack create s1
Stack create s2

puts [Stack available_stacks]

 
Introductory Overview Example: Soccer Club

In our second example, we will focus on an application example where one can benefit substantially from the dynamic language constructs of XOTcl, the soccer club example (the full code can be found in the xotcl/src/scripts/soccerClub.xotcl file. All the persons and characters in this example are fictitious, and any resemblance to actual persons, living or deceased, is coincidental.

Before we start, we introduce an instrument for making the documentation of programs more easy. In order to document source code files, we can use the @ object, which is used generally to provide any kind of information, meta-data, and documentation on a running program. Here, we just give a file description. Then the makeDoc.xotcl tool can automatically document the program file later for us.

  @ @File {
    description {
      This is a simple introductory example for the language XOTcl. 
      It demonstrates the basic language constructs on the example of
      a soccer club.
    }
  }

All things and entities in XOTcl are objects. A special kind of objects are classes. Classes define common properties for other objects. For a soccer club, we firstly require a common class for all kinds of members.

Common to all members is that they have a name. Common properties defined across all instances of a class are called 'parameter' in XOTcl. In this example the instance variable name will be initialized by default with an empty string.

  Class ClubMember -parameter {{name ""}}

A special club member is a Player. Derived classes can be build with inheritance (specified through superclass). Players may have a playerRole (defaults to NONE).

  Class Player -superclass ClubMember -parameter {{playerRole NONE}}

Other club member types are trainers, player-trainers, and presidents:

  Class Trainer -superclass ClubMember
  Class President -superclass ClubMember

The PlayerTrainer uses multiple inheritances by being both a player and a trainer:

  Class PlayerTrainer -superclass {Player Trainer}

Now we define the SoccerTeam class:

  Class SoccerTeam -parameter {name location type}

We may add a player. This is done by a method. Instance methods are in XOTcl defined with instproc. All club members are aggregated in the team (denoted by :: namespace syntax).

  SoccerTeam instproc newPlayer args {
    # we create a new player who is part of the soccer team
    # "eval" is needed to pass the provided arguments to the call of new
    eval Player new -childof [self] $args
  }

A player can be transfered to another team. The player object does not change internally (e.g. the playerRole stays the same). Therefore we move it to the destination team.

  SoccerTeam instproc transferPlayer {playername destinationTeam} {
    # We use the aggregation introspection option children in order
    # to get all club members
    foreach player [my info children] {
      # But we only remove matching playernames of type "Player". We do
      # not want to remove another club member type who has the same
      # name.
      if {[$player istype Player] && [$player name] == $playername} {
        # We simply 'move' the player object to the destination team.
        # Again we use a unique autoname in the new scope
        $player move [set destinationTeam]::[$destinationTeam autoname player%02d]
      }
    }
  }

Finally we define two convenience to print the members/players to the stdout with puts.

  SoccerTeam instproc printMembers {} {
    puts "Members of [my name]:"
    foreach m [my info children] {puts "  [$m name]"}
  }
  SoccerTeam instproc printPlayers {} {
    puts "Players of [my name]:"
    foreach m [my info children] {
      if {[$m istype Player]} {puts "  [$m name]"}
    }
  }

Now let us build to example soccer team objects.

  SoccerTeam chelsea -name "Chelsea FC" -location "Chelsea"
  SoccerTeam bayernMunich -name "F.C. Bayern München" -location "Munich"

With addPlayer we can create new aggregated player objects

Let us start some years in the past, when "Franz Beckenbauer" was still a player.

  set fb [bayernMunich newPlayer -name "Franz Beckenbauer" \
    -playerRole PLAYER]

playerRole may not take any value. It may either be NONE, PLAYER, or GOALY ... such rules may be given as assertions (here: an instinvar gives an invariant covering all instances of a class). In XOTcl the rules are syntactically identical to if statements:

  Player instinvar {
      {[my playerRole] == "NONE" ||  
       [my playerRole] == "PLAYER" || 
       [my playerRole] == "GOALY"}
  }

If we break the invariant and turn assertions checking on, we should get an error message:

  $fb check all
  if {[catch {$fb set playerRole SINGER} errMsg]} {
    puts "CAUGHT EXCEPTION: playerRole has either to be NONE, PLAYER, or TRAINER"
    # turn assertion checking off again and reset to PLAYER
    $fb check {}
    $fb set playerRole PLAYER
  }

But soccer players may play quite different, orthogonal roles. E.g. Franz Beckenbauer was also a singer (a remarkably bad one). However, we can not simply add such orthogonal, extrinsic extensions with multiple inheritance or delegation. Otherwise we would have either to build a lot of unnecessary helper classes, like PlayerSinger, PlayerTrainerSinger, etc., or we would have to build such helper objects. This either leads to an unwanted combinatorial explosion of class or object number

Here we can use a per-object mixin, which is a language construct that expresses that a class is used as a role or as an extrinsic extension to an object.

First we just define the Singer class.

  Class Singer
  Singer instproc sing text {
    puts "[my name] sings: $text, lala."
  }

Now we register this class as a per-object mixin on the player object:

  $fb mixin Singer

And now Franz Beckenbauer is able to sing:

  $fb sing "lali"

But Franz Beckenbauer has already retired. When a player retires, we have an intrinsic change of the classification. He *is* not a player anymore. But still he has the same name, is club member, and is a singer (brrrrrr).

Before we perform the class change, we extend the Player class to support it. I.e. the playerRole is not valid after class change anymore (we unset the instance variable).

  Player instproc class args {
    my unset playerRole
    next
  }

Now we can re-class the player object to its new class (now Franz Beckenbauer is President of Bayern Munich.

  $fb class President
  # Check that the playerRole isn't there anymore.
  if {[catch {$fb set playerRole} errMsg]} {
    puts "CAUGHT EXCEPTION: The player role doesn't exist anymore \
         (as it should be after the class change)"
  }

But still Franz Beckenbauer can entertain us with what he believes is singing:

  $fb sing "lali"

Now we define some new players for Bayern Munich:

  bayernMunich newPlayer -name "Oliver Kahn" -playerRole GOALY
  bayernMunich newPlayer -name "Giovanne Elber" -playerRole PLAYER

If we enlist the players of Munich Franz Beckenbauer is not enlisted anymore:

  bayernMunich printPlayers

But as a president he still appears in the list of members:

  bayernMunich printMembers

Now consider an orthonogal extension of a transfer list. Every transfer in the system should be notified. But since the transfer list is orthogonal to SoccerTeams we do not want to interfere with the existing implementation at all. Moreover, the targeted kind of extension has also to work on all subclasses of SoccerTeam. Firstly, we just create the extension as an ordinary class:

  Class TransferObserver
  TransferObserver instproc transferPlayer {pname destinationTeam} {
    puts "Player '$pname' is transfered to Team '[$destinationTeam name]'"
    next
  }

Now we can apply the class as a per-class mixin, which functions exactly like a per-object mixin, but on all instances of a class and its subclasses. The next primitive ensures that the original method on SoccerTeam is called after notifying the transfer (with puts to stdout):

  SoccerTeam instmixin TransferObserver

If we perform a transfer of one of the players, he is moved to the new club and the transfer is reported to the stdout:

  bayernMunich transferPlayer "Giovanne Elber" chelsea

Finally we verify the transfer by printing the players:

  chelsea printPlayers
  bayernMunich printPlayers



Object and Class System

In XOTcl every object is associated with a class over the class relationship. Classes are special objects with the purpose of managing other objects. ``Managing'' means that a class controls the creation and destruction of its instances and that it contains a repository of methods (``instprocs'') accessible for the instances. Object-specific methods are called ``procs'', instance methods are called ``instprocs''.

The instance methods common to all objects are defined in the root class Object (predefined or user-defined). Since a class is a special (managing) kind of object it is managed itself by a special class called ``meta-class'' (which manages itself). Meta-Classes are used to define classes and to provides methods for these. Most classes are defined by the predefined meta-class Class. One interesting aspect of meta-classes is that by providing a constructor pre-configured classes can be derived. Meta-classes can be used to instantiate large program structures, like some design patterns (see [Neumann and Zdun 1999a] for more details), where the meta-class may holds the generic parts of the structures. Since a meta-class is an entity of the program, it is possible to collect these entities in pattern libraries for later reuse easily (more details about meta-classes are given in a later section).

XOTcl supports single and multiple inheritance. Classes are ordered by the relationship superclass in a directed acyclic graph. The root of the class hierarchy is the class Object. A single object can be instantiated directly from this class. An inherent problem of multiple inheritance is the problem of name resolution, when for example two super-classes contain an instance method with the same name. XOTcl provides an intuitive and unambiguous approach for name resolution by defining the precedence order along a linear ``next-path'' incorporating the class and mixin hierarchies, which is modeled after CLOS. A method can invoke explicitly the shadowed methods by the predefined command next. When this command is executed a shadowed method is ``mixed into'' the execution of the current method. Method chaining without explicit naming of the targeted method is very important for languages supporting a dynamic class system, because one cannot always predict which classes are currently participating in the inheritance hierarchy at design time (often necessary in inheritance models, like C++).

An important feature of all XOTcl objects is the read/write introspection. The reading introspection abilities of XOTcl are packed compactly into the info instance method which is available for objects and classes. All obtained information can be changed at run-time dynamically with immediate effect. Unlike languages with a static class concept, XOTcl supports dynamic class/superclass relationships. At any time the class graph may be changed entirely using the superclass method, or an object may change its class through the class method. This feature can be used for an implementation of a life-cycle or other intrinsic changes of object properties (in contrast to extrinsic properties e.g. modeled through roles and implemented through per-object and per-class mixins [Neumann and Zdun 1999c] ) . These changes can be achieved without loosing the object's identity, its inner state, and its per-object behavior (procs and per-object mixins).

  

Figure 2: Object and Class System

Basic Functionalities

 
Objects

Initially XOTcl offers two new commands: Object and Class. They represent hooks to the features of the language. This section discusses both of them in detail and shows how they function in the context of XOTcl. Note, that even if most of this is compatible to OTcl, a few changes occur. For this reason, this section is no introduction to plain OTcl. The Object command provides access to the Object class, which holds the common features of all objects, and allows us to define new objects. Objects are always instances of classes, therefore, objects defined with the Object command are (initially) instances of the Object class. But since they have no user-defined type, they may be referred to as singular objects. As all other objects they may be specialized by object-operations and -data.

The object command has the following syntax:

  Object objName ?args?

A command of this form is a short-cut for a message to the create instance method (forwarded automatically by the unknown mechanism, which is invoked every time the message dispatch system discovers an unknown message):

  Object create objName ?args?

It creates a new object of type Object with the name objName (in fact it invokes a create call on the Object class). objName becomes a new command, which allows us to access the created object. Similar to the Object command it may be used like a normal Tcl-command (using sub-commands to access the object's methods). Therefore, this form of access is called object-command approach. A simple example is an object which holds the information of a kitchen. It is created by:

  Object kitchen

An object creation calls the constructor init of the object's class. The destruction of an object is handled by the destroy instance method. The general syntax of destroy is:

  objName destroy

E.g. the kitchen object is destroyed by:

  kitchen destroy

To invoke a user-defined destruction process, it is possible to overload this instance method in every class derived from object.

Note that the destruction of an object is performed by the method destroy of Object (since every object is an instance of Object, every object can call destroy). When an application class overloads destroy, this method should contain a next in order to reach the base class and to actually destroy the object.

Data on Objects

The Object class provides a range of operations to manage objects, including those to manipulate data-structures on the objects. They are similar to the same-named Tcl-commands:

  objName set varname ?value?
  objName unset v1 ?v2 ... vn?

The set instance method with given value option allows us to manipulate an object-variable's value or to create a new one, if the variable varname does not exist on the object so far. Without value option the set operation queries the variable and returns it's value, if the variable exists, otherwise it produces an error message. The unset operation deletes one or optionally a set of variables from an object. For example the kitchen object can store information on the color of the wall-paper by:

  kitchen set wallPaperColor white

Similar to Tcl-variables the object variables are dynamical; they may be set at run-time when they are needed and unset when they become obsolete. E.g. the persons in the kitchen may be stored in an array. If there are no persons in the kitchen the array is deleted:

  # Peter enters the kitchen to cook
  kitchen set persons(cook) Peter
  ...
  # Marion enters the kitchen to take one of the seats
  kitchen set persons(seat1) Marion 
  ...
  # Both Peter and Marion leave the kitchen
  # the array is deleted by unset
  kitchen unset persons

Since XOTcl variables are internally realized through Tcl-variables they may be treated like all Tcl-variables. For that reason they have all Tcl-variable abilities, including the possibility to handle them as lists or arrays (as seen in the last example). The array command of Tcl is mapped to an XOTcl-command directly. An object-oriented call to an object of the form

  objName array option arrayName args

forwards its arguments to an array Tcl-command for the object´s instance variable arrayName. It could be used like the same-named Tcl-command, e.g. the command

  kitchen array names persons

returns all indexes currently stored in the persons array.

Similarly Tcl´s incr command is mapped to the object system. A call with the syntax:

  objName incr varName ?value?

increments varName with the given value (or without given value with 1).

Methods for Objects

Methods in XOTcl resemble Tcl-procedures. On objects one can define object-specific methods, called procs. Instance methods which are defined on classes are called instprocs. A new proc is defined using the proc instance method of the class Object:

  objName proc name args body

The arguments of the proc instance method specify the name, the arguments as a Tcl-list, and the body of the new proc. All of them must be given, only one of args and body may be empty. An example proc would be a method to let persons enter the kitchen:

  kitchen proc enter {name} {
    [self] set persons($name) [clock seconds]
  }

Here the predefined self command is used in one of three possible ways, which allow us to access useful information when working with XOTcl-methods, these are in particular:

  • self: returns the name of the object, which is currently in execution. This command is similar to this in C++. It is automatically generated on each object. If it is called from outside of an XOTcl method, it produces the error message "Can't find self".

  • self class: the self command with the argument class returns the name of the class, which holds the currently executing instproc. Note, that this may be different to the class of the current object. If it is called from a proc it returns an empty string.

  • self proc: the self command with the argument proc returns the name of the currently executing method (proc or instproc).

The method enter can be written in XOTcl as well with less syntactic overhead by using the predefined primitive my instead of [self]:

  kitchen proc enter {name} {
    my set persons($name) [clock seconds]
  }

Note, that there is a difference to the realization of these object informations to OTcl. XOTcl uses commands in order to make XOTcl-methods compatible to Tcl-procedures and accessible via namespace-paths. OTcl uses the three variables self, class and proc, which are filled automatically with proper values by the interpreter each time a method is called. To gain backwards compatibility XOTcl can be compiled with -DAUTOVARS to provide these variables additionally. By default this option is turned off.

Each XOTcl-method has its own scope for definition of local variables for the executing method. In most cases when a method uses object-variables, it is likely that the programmer wants to make one or more of these variables part of the method's scope. Then the Tcl-command for variable handling, like set, lindex, array, ... work also on these variables. The instvar instance method links a variable to the scope of an executing method. It has the syntax:

  objName instvar v1 ?v2 ... vn?

It makes the variables v1 ... vn, which must be variables of the object, part of the current method's scope. A special syntax is:

  objName instvar {varName aliasName} ...

for one of the variables. This gives the variable with the name varName the alias aliasName. This way the variables can be linked to the methods scope, even if a variable with that name already exists in the scope. Now the enter method can be adapted slightly and a leave method can be added, which uses Tcl's info command to check whether the named person is in the object's persons array. To demonstrate the alias-syntax this is done with the persons array and the alias p.

  kitchen proc enter {name} {
    my instvar persons
    set persons($name) [clock seconds]
  }

  kitchen proc leave {name} {
    my instvar {persons p}
    if {[info exists p($name)]} {
      puts "$name leaves after [expr {[clock seconds]-$p($name)}] seconds" 
      unset p($name) 
    } else {
      puts "$name is not in the room"
    }
  }
A method defined via proc can be deleted by proc using an empty argument list and an empty body. The following example deletes the method enter:
  Room proc enter {} {}

Information about Objects

XOTcl offers reading and writing introspection. The reading introspection abilities are packed compactly into the info instance method which is available for objects and classes (there are special info options for object aggregations, nested classes, mixins, filters, meta-data and assertions, which are explained separately in the following sections).

Options for the info method on objects

objName info args methodName

Returns the arguments of the specified proc (object specific method).

objName info body methodName

Returns the body of the specified proc.

objName info class ?className?

Returns the name of the class of the current object, if className was not specified. Otherwise it returns 1 if className matches the object's class and 0 if not.

objName info commands ?pattern?

Returns all commands defined on the object if pattern was not specified. Otherwise it returns all commands that match the pattern.

objName info default methodName arg var

Returns 1 if the argument arg of the specified proc has a default value, otherwise 0. If the default value exists it is stored in var.

objName info precedence ?pattern?

Returns all classes in the precedence order from which the specified object inherits methods. The returned list of classes contains the mixin and instmixin classes as well as the classes of the superclass chain in linearized order (i.e., duplicate classes are removed). If the pattern is specified, only matching classes are returned.

objName info vars ?pattern?

Returns all variables defined on the object if pattern was not specified, otherwise it returns all variables that match the pattern.


For example on the kitchen object

  kitchen info procs

returns enter and leave as a Tcl-list since these are the procs defined on the object.

Classes

Creating Classes and deriving Instances

There are different ways to create a class in XOTcl. They have in common that they derive the new class from a meta-class. Initially the Class command provides access to the meta-class Class, which holds the features common to all classes. It also allows one to derive new meta-classes. The common way to create a new class is:

  Class className ?args?

Similar to the object short form, this is a short form of a call to the create instance method of the meta-class Class, which is also executed by the standard unknown mechanism. This mechanism is always triggered when XOTcl does not know a method called on an object. Supposed that there is no method with the name className, defined on the class-object of Class, XOTcl looks up the method unknown (which is found on the Class Object) and executes it. The standard unknown-mechanism of XOTcl calls create with all arguments stepping one step to the right; in the general case:

  Class create className ?args?

This may also be called directly. Besides the indirection when using unknown, in most cases there is no difference in the action performed: Firstly the memory is allocated, using the alloc instance method; as the next step the constructor init is called on the creating object, which is in this case the class-object of the meta-class Class. In seldom cases the programmer may want to suppress the init call. To do so the alloc instance method may also be called directly:

  Class alloc className ?args?

As seen in the preceding section objects are created in the same way. The difference was, that the command Object, which accesses a class, instead of the command Class, which accesses a meta-class, was used. The user-defined classes may also be used in the same way to create new objects:

  className objName ?args?

Resembling the creation of classes this creates an object objName of type className using the unknown mechanism. That means the create instance method of the class is called. If there is no other instance method defined on the class-path so far (which would mean, an user defined creation process is invoked), the create instance method of the class Object is invoked. This method is similar to the create method of the meta-class Class. It firstly calls the alloc instance method on its (of the Class class) which allocates memory for the object, and makes it an instance of it's class. Afterwards a call to the constructor init is invoked.

Now we can specify the object for the kitchen by the class to which it belongs. In this case a kitchen is an instance of a room.

  Class Room
  Room kitchen

A set call on a class creates an instance variable on the class-object. This variable is unique for all instances, therefore, it may be referred to as a class variable.

Methods Defined in Classes

Methods which are defined in classes and which are provided to the instances of these classes are called "instprocs". The syntax for defining an instproc is:

  className instproc procname args body

It is similar to the definition of procs on objects, but uses the keyword instproc to distinguish between the methods defined on the class-object and those defined on the class. Since all rooms (in the modeled world) have ceilings, we may want to define a simple convenience instproc, which is able to set the color:

  Room instproc setCeilingColor color {
    my set ceilingColor $color
  }

A special instproc, the constructor init, was mentioned already. Now we are able to define such an instproc. Defined on a class it is responsible for all initialization tasks, which needed to be performed, when constructing a new instance object of the class. The constructor of the Room can initialize a variable for the color, in which the ceiling is painted, to white as default, since this is the color of ceilings without painting.

  Room instproc init args {
    my setCeilingColor white
    next
  }

After this definition, all instances derived from the Room class have an instance variable ceilingColor with the value white. The args argument used here is a special argument in Tcl which allows us to use a list of arguments which may change its length from call to call.

An instproc can be deleted by the method instproc as well. If instproc is called with an empty argument list and an empty body, the specified method is deleted, as the following example shows:

  Room instproc setCeilingColor {} {}

Information about Classes

Resembling to objects, information on classes may be gained through the info instance method of the meta-class Class. Note that this instance method does not only support the class info options, but also the class-object info options, since the accessing command refers to the class-object, which itself is an object and, therefore, offers its informations. The following table summarizes the additional info options available on classes.

Options for the info method on classes

className info heritage ?pattern?

Returns a list of all classes in the precedence order of the class hierarchy matching pattern or a list of all classes, if pattern was not specified.

className info instances ?pattern?

Returns a list of the instances of the class matching pattern or of all instances, if pattern was not specified.

className info instargs methodName

Returns the arguments of the specified instproc (method provided to objects).

className info instbody methodName

Returns the body of the specified instproc.

className info instcommands ?pattern?

Returns all commands defined on the class, if pattern was not specified, otherwise it returns all commands provided to objects that match the pattern.

className info instdefault methodName arg var

Returns 1 if the argument arg of the specified instproc has a default value, otherwise 0. If the default value exists it is stored in var.

className info subclass ?className2?

Returns a list of all subclasses of the class, if className2 was not specified, otherwise it returns 1 if className2 is a subclass and 0 if not.

className info superclass ?className2?

Returns a list of all super-classes of the class, if className2 was not specified, otherwise it returns 1 if className2 is a superclass and 0 if not.

The full list of info options is provided in the language reference.

Inheritance

Besides encapsulation of operations and state in objects, a second central ability of object-orientation is inheritance. XOTcl supports single and multiple inheritance with a directed acyclic class graph. Automatically each new class created by the instance methods create and alloc of Class inherits from Object. Therefore, it is ensured that all instances of the new class have access to the common features of objects stored in the class Object.

To specify further inheritance relationships the instance methods superclass of Class is used:

  className -superclass classList

E.g. in the example a kitchen may be seen as a special room:

  Class Room
  Class Kitchen -superclass Room

Now all instances of Kitchen are able to access the operations stored in the Room and in the Kitchen class. Note the transition the kitchen was going through: firstly it was a singular object, then it was an object with a user-defined class, and now it is a class. This is possible (and not senseless) because of the per-object specialization ability and the dual shape of a class, which is at the same time object and class. Both lead to a seamless connection of the run-time properties (the object features) and their descriptive properties (the class features). It is possible to avoid the strict distinction between them, known from static typed languages, like C++, Java, etc.

Moreover, since the syntaxes of constructs expressing the same concern are nearly identical, we can re-factor a solution with very few changes to the alternative. We will see similar "ease of refactoring" throughout the XOTcl language. E.g., we can also easily re-factor the class hierarchies or exchange class hierarchies against mixin solutions with only slight changes in the code.

Besides single inheritance, as seen, XOTcl provides also multiple inheritance. This is syntactically solved by giving the superclass instance method a list of classes instead of a single class as argument.

  Class Room
  Class 4WallsRoom -superclass Room
  Class CookingPlace
  Class Kitchen -superclass {4WallsRoom CookingPlace}

Now the kitchen class is specialized a bit more. It is a special room which has four walls and it is a cooking place. Multiple inheritance, as seen here, is as simple to apply as single inheritance.

Most often when the disadvantages of multiple inheritance are discussed, the name resolution along the class graph is considered as the biggest problem. The question is, which method is to be chosen and which path through class graph is to be taken, if more then one method of the specified name exist on the class graph.

In the example such questions would arise for an object of the Kitchen class, if two same-named methods are defined on CookingPlace and 4WallsRoom or if a method of the class Object is called, which is reachable through two paths (along CookingPlace or Room).

Often - e.g. in the inheritance model of C++ - the path through the graph is not clearly determined and/or the rules are too complicated to be understood on the first glance. The programmer often can only determine by trial which method is found firstly. Than an explicit naming of the class is necessary, which means storage of non-local information in sub-classes. Often different compilers of one language behave differently. All these issues make code reuse difficult. Moreover understandability and portability are reduced.


Figure 3: The example classes and the following next-path


XOTcl goes an intuitive and unambiguous way to solve this problem. It resolutes the precedence order along a ``next-path''. Firstly the class of the object is searched, which is Kitchen in example. Then the super-classes are searched in definition order, which means at first 4WallsRoom, then CookingPlace. Each branch is searched completely, before changing to the next branch. That means, Room is searched, before the CookingPlace branch is visited. At last the top of the hierarchy, the class Object, is searched.

The usage of next in XOTcl is different to OTcl: In OTcl, next is defined as a method, in XOTcl it is a primitive command. Furthermore, in OTcl, it is always necessary to provide the full argument list for every invocation explicitly. In XOTcl, a call of next without arguments can be used to call the shadowed methods with the same arguments (which is the most common case). When arguments should be changed for the shadowed methods, they must be provided explicitly in XOTcl as well. In the rare case that the shadowed method should receive no argument, the flag --noArgs must be used.

Destruction of Classes

Classes are destroyed by the destruction of the class-object using the destroy method of the Object class. The destruction of super-classes does not destroy the sub-classes. The super-class is simply removed from the sub-classes' super-class lists. All classes have the super-class Object, if no super-class is specified. Therefore, if all super-classes are destroyed or removed, the new super-class is Object, not: no super-class. The destruction of the class of an object does neither delete the object nor leave it without class. In XOTcl a deleted class leaves it's instances with the class Object.

So all empty class- and superclass-relationships are automatically reseted to Object. Note, that this are differences to OTcl, where the destruction of an class destroys all instances and an empty super-class list remains empty.

Method Chaining

A special feature of XOTcl is the method chaining without explicit naming of the ``mix-in''-method. It allows one to mix the same-named superclass methods into the current method (modeled after CLOS). The previously described next-path is the basis for this functionality. At the point marked by a call to the next primitive of XOTcl the next shadowed method on the next path is searched and, when it is found, it is mixed into the execution of the current method. When no method is found, the call of next returns an empty string, otherwise it returns the result of the called method. The syntax is:

  next ?arguments|--noArgs?

As stated earlier the usage of next in XOTcl differs from OTcl, since the next call without arguments in OTcl means per default that no arguments are passed. But most often all arguments are passed through to the shadowed methods (since these will most likely have the same signatures). When all variables should be passed through, in OTcl it is necessary for correct variable substitution to use:

  eval $self next $args

To avoid such difficulties, we made the passing of all arguments the default case; a simple

  next

performs the task of passing all arguments to the shadowed methods. These arguments are called the standard arguments. If the standard argument feature should not be used, optionally arguments can be given or the flag --noArgs could be set as sole argument, which means that the shadowed method is called with no arguments.

E.g. the following next call ignores the standard arguments and sends the arguments 1 and 2 instead:

  next 1 2

As an example all classes involved in the previous example should get a constructor instance method, which simply sets an instance variable on the object:

  Room instproc init args {
    my set roomNumber 0
    next
  }    
  4WallsRoom instproc init args {
    my set doorPosition 0
    next
  }
  CookingPlace instproc init args {
    my set stoveType electric
    next
  }
  Kitchen instproc init args {
    my set cookName -
    next
  }

After creation an object of class Kitchen gets automatically four instance variables cookName, roomNumber, doorPosition and stoveType set up with default values in this order (since this is the order of the classes in the next-path). Note, that the order is important, because one missing next call, in one of the init methods, means that succeeding init methods will not be executed. This mechanism functions equally on all kinds of instprocs, not only on constructors.

The constructors use the args argument, which allows us to give a list of variable length as arguments. To ensure reusability of our classes the constructors should use args in most cases, since they may pass through arguments for constructors further up the class hierarchy.

If a proc with the searched name exists on the object it shadows all instprocs. A next call in a proc leads to the normal next-paths search, starting with the object's class.

By the way, an observant reader might notice that the example above can be rewritten without explicit constructors, just by using parameters with default values.

  Class Room -parameter {{roomNumber 0}}
  Class 4WallsRoom -superclass Room -parameter {{doorPosition 0}}
  Class CookingPlace -parameter {{stoveType electric}}
  Class Kitchen -superclass {4WallsRoom CookingPlace} -parameter {{cookName -}}

If an instance of a Kitchen is created it will contain instance variables for doorPosition, cookName, roomNumber, and stoveType, as the following statements will show.

  Kitchen k
  puts [k info vars]

Dynamic Class and Superclass Relationships

Another property of XOTcl that distinguishes it from statically typed languages are dynamics of class relationships. The realization of the definition of super-classes as seen above with the superclass method suggests already, that it is not only available at the class definition time. In the above example its appended to the class definition with "-superclass" as a short syntax for method invocation at definition time (all other available methods can also be called with a preceding dash ("-") appended to definitions).

At any time the class graph may be changed entirely using the superclass method. Suppose the rooms and kitchens created in modeling of a house should be displayed to a screen, but it is not determined, whether the user of the system has the possibilities for graphical outputs. Two classes TextOutput and GraphicalOutput may be defined, which handle the output. Both have an instproc paint which does the painting of the virtual world on the chosen display type. The common output requirements are handled by a derived class VirtualWorldOutput which calls the paint method of the superclass using next. In statically typed languages it would need more sophisticated constructs to change the output class at run-time. E.g. a delegation to another object handling the intrinsic task of the output object would be introduced solely for the purpose of configuring the output form. With a dynamic class system we can use the superclass method to do so easily:

  Class TextOutput
  TextOutput instproc paint args {
    # do the painting ...
  }
  Class GraphicalOutput
  GraphicalOutput instproc paint args {
    # do the painting ...
  }

  # initially we use textual output
  Class VirtualWorldOutput -superclass TextOutput
  VirtualWorldOutput instproc paint args {
    # do the common computations for painting ...
    next; # and call the actual output
  }

  # user decides to change to graphical output
  VirtualWorldOutput superclass GraphicalOutput

Sometimes, such a change to new intrinsic properties should not happen for all instances of a class (or the class hierarchy), but only for one specific object. Then the usage of a dynamic super-class relationship is a too coarse-grained means. A second form of such dynamics is the changing of the relationship between object and class. This means, objects can also change their class dynamically at run-time. This feature may be used to model a life-cycle of an object, without loosing the object's identity, inner state or per-object-specializations through procs. The class instance method enables this functionality.

An example would be an agent for the virtual world. Agents may be placeholders for persons, who interactively travel the world, or programs, which act automatically. When a person decides at run-time to give a task it has performed formerly by hand to an automatic agent, the agents nature changes from interactive agent to automatic agent, but the identity and the local state (that means the parts of the task, that are already fulfilled by the person) stay the same. This is a scenario for changing class relationships, e.g.:

  Class Agent
  Class AutomaticAgent -superclass Agent
  Class InteractiveAgent -superclass Agent

  # create a new agent for a person
  InteractiveAgent agent1

  # the person does something ...
  # and decides the change to an automatic agent
  agent1 class AutomaticAgent

Meta-Classes

Meta-classes are a special kind of classes. Similar as classes are managing objects (where managing means: control the creation and destruction of instances, know what instances exist, provide methods), meta-classes are managing classes. So, meta-classes are used to define classes. In other words, every Class in XOTcl is created by a meta-class, in most cases by the meta-class named Class. New user-defined meta-classes can be defined as subclasses of the predefined meta-class Class, or by adding an instmixin class (see below) containing Class to the precedence chain of the class. By defining Object instmixin Class one can even change the object system of XOTcl in in a way such that every created Object is a meta-class.

Since the concept of a meta-class are sometimes confusing to people of a background of some other programming languages, we explain meta-classes slowly with the analogy of classes and objects.

When a class Foo is created via the command

   Class Foo
it has no private variables and no special methods. This is somewhat similar as creating an object via Object:
   Object foo
This plain object foo can be configured directly, or one can create a class that configures the object. Instead of writing
   Object foo 
   foo set x 1
   foo proc hi {} {puts "hello"}
one can use
   Class C -superclass Object
   C instproc init {} {my set x 1}
   C instproc hi {} {puts "hello"}
and create an instance and call the method.
   C c1
   c1 hi
The same holds for meta-classes and classes as well: Instead of writing
   Class Foo
   Foo set x 1
   Foo proc hi {} {puts "hello"}
the following can be used:
   Class MC -superclass Class
   MC instproc init {} {my set x 1}
   MC instproc hi {} {puts "hello"}
The instances of meta-classes are classes which can be defined the usual way:
   MC Bar
   Bar hi
   Bar b1
Now we have a class names Bar which has a class-scoped variable named x with the value of 1 (set via the constructor); the class Bar has as well a class-method named hi which prints, when called, the string "hello". The class Bar can be used to create instances of the class like b1, b2 and so on.

Note that the command Class is a predefined definition of the most general meta-class in XOTcl. Each time we are creating a class, we use this meta-class. In order to define a specialized meta-class, we can do this the traditional object-oriented way: we subclass. Therefore, in to define a specialized meta-class, we can use:

  Class myMetaClass -superclass Class

This defines a new meta-class myMetaClass, which has all the abilities of meta-classes. That means that the programmer is able to specify new class features or override old ones. Later she/he may instantiate these into new classes.

This is a very powerful language feature, since it allows one to give some classes further abilities than the others (or to restrict classes). This way large program structures, like certain design pattern parts, may be instantiated. Meta-classes hold the common abstract parts of the structures. They allow one to form libraries of such structures very easily.

Example 1: Overloading the info method of classes

As a simple example we can derive a new meta-class NoClassInfo from Class. Later we override the info method of Class. Thus the classes created with NoClassInfo, have an info option that only produces an error message. All classes created with NoClassInfo, like Agent in the example below, are not capable of accessing the class info method anymore:

  Class NoClassInfo -superclass Class
  # redefine info ability
  NoClassInfo instproc info args {
    error "No class info lookup"
  }
  # derive agent class from meta-class, which
  # can not access class info
  NoClassInfo Agent
Now a call like:
  Agent info superclass

triggers the error message.

Example 2: Defining Classes that Count Their Instances

Meta-classes are frequently used to define some bookkeeping about the number of instances on the class level. In the following example we define a meta-class named CountedClass which defines classes that count their instances:

  Class CountedClass -superclass Class -parameter {{counter 0}}
  CountedClass instproc create args {
    my incr counter
    next
  }
  CountedClass instproc instdestroy args {
    my incr counter -1
    next
  }
  CountedClass Dog

  Dog piffie
  Dog idefix
  puts "nr of dogs: [Dog counter]"

  piffie destroy
  puts "nr of dogs: [Dog counter]"
Note that the behavior introduced by meta-classes can be orthogonal to the behavior of the classes. One can define Dog as a specialization of Animal or defines a special kind of dog such as Poodle using the method superclass as usual.

Example 3: The Singleton Meta-Class

Finally, a small example, which is more practical. Some applications have the requirement that only one instance of a class might be defined at a certain time. Such a behavior is frequently called a "Singleton". In XOTcl we can define a class singleton by overloading the create method of Class: when create is called and there exists already an instance of the singleton it is returned instead of a new instance.

  Class Singleton -superclass Class
  Singleton instproc create args {
    expr {[my exists instance] ? [my set instance] : [my set instance [next]]}
  }
If someone wants to have a class e.g. Manager to be a singleton, you can create it by e.g.
  Singleton Manager -superclass FOO

Create, Destroy, and Recreate Methods

XOTcl allows since version 0.84 for a flexible destroy and recreate scheme. create and alloc are both Class instprocs handling creation for their instances. I.e.:

 className alloc [self]
and
 className create [self]

are used for creating an instance. A similar method instdestroy exists on Class that handles physical destruction of an object. The method destroy on Object which lets an object destroy itself in fact has the following behavior:

  Object instproc destroy args {
   [my info class] instdestroy [self]
  }

However, this behavior is not implemented in XOTcl, but in C. create distinguishes between the following situations:

  • Create a new object: By default create calls alloc and then doInitializations.
  • Recreate an existing object: When the specified object exists, it is recreated through the recreate method:
      givenClass recreate [self]
    

    The method recreate can be customized like all other XOTcl methods (e.g. by overloading or interception). By default recreate calls cleanup followed by doInitializations.

    Note, that recreate is not called, when a someone tries to recreate a class as an object or an object as a class. In these cases, destroy + create are used.

        Class c
        Object c ;# here, "c" is downgraded to an object, no "recreate" is called
    

For create and recreate, the method doInitializations is called automatically from C and has the following default behavior:

  • Search for parameter default values,
  • Call parameter initialization methods,
  • Call the constructor init.

Each step has a method call that can be changed, intercepted, etc. Of course, cleanup, recreate, instdestroy, etc. can also be overloaded or intercepted.

Consider a typical case for overloading recreate: a structure preserving recreate that cleans up the class but preserves the existing class hierarchy (subclass and instance relationships):

  Class StructurePreservingRecreate
  StructurePreservingRecreate instproc recreate {cl args} {
    if {[my isclass $cl]} {
      set subclass [$cl info subclass]
      set instances [$cl info instances]
    }
    next
    if {[my isclass $cl]} {
      foreach sc $subclass {
        $sc superclass $cl
      }
      foreach i $instances {
        $i class $cl
      }
    }
  }
  Object instmixin add StructurePreservingRecreate

Now the following code does not change the superclass or instance relationships of C:

  Class A
  Class B
  Class C -superclass {A B}
  Class D
  Class E -superclass {C D}
  C c1
  C c2

  # recreate -> is structure preserving
  Class C -superclass {A B}
  C c2

  # test
  puts superclass=[C info superclass]
  puts subclass=[C info subclass]
  puts instances=[C info instances]
  puts class=[c1 info class]
  puts class=[c2 info class]
Starting with XOTcl 1.4.0, xotcl provides also a user-friendly way for a structure-prevering recreate implemented in C. Since this version, one can configure "softrecreate" as follow.
::xotcl::configure softrecreate true
This command causes that recreates are structure-conservative.

Methods with Non-Positional Arguments

So far we have introduced methods only with positional arguments: that is, the position of an argument in the argument list determines to which local variable the argument is bound, when the method is invoked. Sometimes non-positional arguments -- arguments that carry both a name and a value when the method is invoked -- are useful. Before a non-positional argument can be used, it must be defined in the method definition using the following syntax:

 className instproc methodName ?non-pos-args? args body ?assertions
 objName proc methodName ?non-pos-args? args body ?assertions

The non-positional arguments are defined with the following syntax:

 {-name?:checkoption1, checkoption2, ...? default value} \
     {-name?:checkoption1, checkoption2, ...? ?default value?} ...

Only the name of the non-positional argument is really required, all other parts of this syntax are optional.

Let's consider a simple example, where a method with two non-positional args is defined; one has only a name ("a"), and one has a name and a default value (b):

 Object o
 o proc someproc {-a {-b {1 2 3}} x y} {
     puts "$a $b $x $y"
 }

We can invoke this method as follows:

 o someproc -b {4 5} -a 1 3 4

Here, the order of a and b can be changed; hence the name non-positional arguments. As b has a default value, we do not need to provide a value for it. In the following invocation b has the value "1 2 3":

 o someproc -a 1 3 4

The ordinary arguments start after the last non-positional argument (here: "3 4"). We can explicitly end the non-positional arguments by using "--". This is useful if we want to provide arguments that contain dashes ("-"), e.g.:

 o someproc -a 1 -- -b -c

Sometimes we want to check or control the non-positional arguments. For instance, in the above invocation, we might want to check that a is not forgotten, because otherwise the method cannot execute properly. This is the role of the checkoptions. There are three predefined checkoptions: required, boolean and switch. required checks whether a non-positional argument is given, boolean checks that a non-positional argument is of boolean type. For instance:

 Class P
 P instproc someproc {-a:required {-b:boolean true}} {
     puts "$a $b"
 }
 P p

This method requires a, and b needs to be of type boolean (is has the default value true). This invocation is valid:

 p someproc -a 1 -b 0

This invocation is invalid, because a is missing, and b is not a Tcl boolean type:

 p someproc -b " a b v"

The checkoption switch is similar to boolean except it does not require an additional argument. If the default value is false, the switch can be turned on, if the default is true it can be switched off.

The checkoptions are extensible. In fact, they are defined in an object ::xotcl::nonposArgs. We can extend this object with new methods. A check option method has the following syntax:

 someobject|someclass proc|instproc methodName {?optional nonpositional arguments? argName arg} {
  ...
 }

argName is here used to denote the name of the argument, and arg is the provided value.

Of course, the non-positional arguments can also be introspected. The following info options return the non-positional arguments of a method:

 objName info nonposargs methodName
 className info instnonposargs methodName

Message Interception Techniques

Even though object-orientation orders program structures around data, objects are characterized primarily by their behavior. Object-oriented programming style encourages the access of encapsulated data only through the methods of an object, since this enables data abstractions. A method invocation can be interpreted as a message exchange between the calling and the called object. Therefore, objects are at runtime only traceable through their message exchanges. At this point the message interceptors can be applied to catch and manipulate all incoming and outgoing messages of an object.

Generally interceptors can be applied to attach additional or extrinsic concerns to an object or a class or a class hierarchy. For instance roles or aspects can be implemented this way on various levels of scale.

We have already discussed some interception techniques implicitly. E.g., the unknown mechanism intercepts messages that have not be found on the object. It can be used as a very useful programming technique, e.g., the define a default behavior for an object. The interceptors presented in this section have a different character: They are applied before/after the original method even if the method is defined for the target object. Thus these interception techniques may be applied

We will discuss the message interceptors in this section in detail. The table below gives an impression, when which interceptor may be applied.

Message Interceptors Overview

Applied When

Primary Target Structure

Coverage

Per-Object Filter

before/after a call

object hierarchies

all methods

Per-Class Filter

before/after a call

class and class hierarchies

all methods

Per-Object Mixin

before/after a call

object

specific methods

Per-Class Mixin

before/after a call

class and class hierarchies

specific methods

Unknown Mechanism

after method was not found

object

all unknown calls


Filter

The filter (see [Neumann and Zdun 1999a] for more details) is a language construct to implement broader extensional concerns either for a single object or for several classes or class hierarchies. This way large program structures at the scale of several classes or class hierarchies can be managed. It is a very general interception mechanism which can be used in various application areas. E.g. a very powerful programming language support for certain design patterns is easily achievable, but there are also several other domains which are covered, like tracing of program structures, self-documentation at run-time, re-interpretation of the running program, etc.

A per-class filter is a special instance method that is registered for a class C. A per-object filter is a special instance method that is registered for a object o. Every time an object of class, C or the object o respectively, receives a message, the filter method is invoked automatically.

Usage of Filters

All messages to a filtered object must go through the filter before they reach their destination object. A simple example would be a sole filter on the class of the object. To define such a filter two steps are necessary. Firstly an filter method has to be defined, then the filter has to be registered. The filter method consists of three parts which are all optional. A filter method has the following form:

  className instproc FilterName args {
    pre-part
    next
    post-part
  }

When a filter comes to execution at first the actions in the pre-part are processed. The filter is free in what it does with the message. Especially it can (a) pass the message, which was perhaps modified in the pre-part, to other filters and finally to the object. It can (b) redirect it to another destination. Or it can (c) decide to handle the message on its own. The forward passing of messages is implemented through the next primitive of XOTcl. After the filter has passed its pre-part, the actual called method is invoked through next.

After the call of next is processed, the execution returns to the point in the filter, where the next call is located and resumes execution with the actions of the post-part. These may contain arbitrary statements, but especially may take the result of the actual called method (which is returned by the next-call) and modify it. The caller then receives the result of the filter, instead of the result of the actual called method.

The pre- and post-part may be filled with any ordinary XOTcl-statements. The distinction between the three parts is just a naming convention for explanation purposes.

The filter uses the args argument which lets us use a list of variable length as arguments, since it must filter a lot of different calls, which may have different argument lists. Furthermore, it may pass through arguments to other filters and the preceding filters may change the argument list.

Since any proc/instproc may be a filter, a registration of the filter is necessary, in order to tell XOTcl, which instprocs are filters on which classes. The filter and instfilter instance methods are able to handle this task for per-object filters and per-class filters respectively. Similar to the XOTcl language introduced so far, the filter registration is dynamic at run-time. By supplying a new list of filters to filter/instfilter, the programmer can change the filters registered on a class at arbitrary times. The filter instance method has the syntax:

  className instfilter filterList
for per-class filters and:
  objName filter filterList
for per-object filters.

Now a simple example should show the filter's usage. In the preceding examples we have defined several rooms. Every time a room action occurs it is likely that the graphical sub-system has to change something on the output of that particular room. Therefore, at first we need a facility to be informed every time an action on a room happens. This is quite easily done using filters:

  Class Room
  Room r1; Room r2;       # just two test objects

  Room instproc roomObservationFilter args {
    puts "now a room action begins"
    set result [next]
    puts "now a room action ends - Result: $result"
    return $result
  }

  Room instfilter roomObservationFilter

Now every action performed on room objects is notified with a pre- and a post-message to the standard output stream. We return the result of the actual called method, since we don't want to change the program behavior at all. E.g. we can set an instance variable on both of the two room objects:

  r1 set name "room 1"
  r2 set name "room 2"

The output would be:

  now a room action begins
  now a room action ends - Result: room 1
  now a room action begins
  now a room action ends - Result: room 2


  

Figure 4: Cascaded Message Filtering



All classes may have more than one filter. In fact they may have a whole filter chain, where the filters are cascaded through next. The next method is responsible for the forwarding of messages to the remaining filters in the chain one by one till all pre-parts are executed. Then the actual method is executed and then the post-parts come to turn. If one next-call is omitted the chain ends in this filter method. As an example for an additional filter we may register a filter that just counts the calls to rooms.

  Room set callCounter 0;  # set class variable
  Room instproc counterFilter args {
    [self class] instvar callCounter
    incr callCounter
    puts "the call number callCounter to a room object"
    next
  }
  Room instfilter {roomObservationFilter counterFilter}

Filters are invoked in registration order. The order may be changed by removing them and adding them in new order. Filters are inherited by sub-classes. E.g. in the preceding example for the next path, an OvalOffice was derived from the Room class. Without a change to the program each OvalOffice object automatically produces the same filter output as rooms.


  

Figure 5: Filter Inheritance


Filter chains can also be combined through (multiple) inheritance using the next method. When the filter chain of the object's class is passed, the filter chains of the superclasses are invoked using the same precedence order as for inheritance. Since on the subclass there may also be a another filter chain, without sophisticated computing in the pre- and post-parts one can produce easily a powerful tracing facility. E.g. if we want to distinguish an OvalOffice from other rooms we may want to add a filter solely for rooms of the type OvalOffice:

  Class OvalOffice -superclass Room
  OvalOffice o1;  # test object
  OvalOffice instproc ovalOfficeObservationFilter args {
    puts "actions in an oval office"
    next
  }
  OvalOffice instfilter ovalOfficeObservationFilter

A simple call to the o1 object, like:

  o1 set location "Washington"

produces the following output:

  actions in an oval office
  now a room action begins
  the call number 3 to a room object
  now a room action ends - Result: Washington

As seen already, filter registrations can be added dynamically at runtime. But they may also be removed. Perhaps the counting on rooms should stop after a while, then a simple call of the instfilter method is sufficient:

  Room instfilter roomObservationFilter

Filters can be removed completely by giving an empty list to the registration method:

  Room instfilter {}

Per-object filters operate on a single object. E.g. if we only want to observe a single Room object room1, we can use the filter method to register the roomObservationFilter only for this particular instance:

  room1 filter roomObservationFilter

As a filter we can register any method in the precedence order of the class or object. Thus we can also register procs as per-object filters. Additionally, meta-class methods may be registered as per-class filters. Filters are linearized so that each filter is only executed once, even if it is registered multiple times.



Introspection on Filters

In order to gain information about the currently registered filters on a certain object/class, the class-object info option filters and the class info option instfilters may be queried. It returns a list of the currently registered filters:

  className info instfilter
  objName info filter

A special call-stack info option for filters is self filterreg. It returns the name of the object or class on which the filter is registered. Since the filter may be registered on other objects/classes than the one on which it is defined, this may vary from self class in the filter. The command returns a list of the form:

  objName filter filterName
or:
  className instfilter filterName
respectively.



Example: A Simple Trace Filter

The trace example primarily demonstrates the inheritance of filter chains. Since all classes inherit from Object, a filter on this class is applied on all messages to objects. The Trace object encapsulates methods for managing the tracing:

  Object Trace
  Trace set traceStream stdout

  Trace proc openTraceFile name {
    my set traceStream [open $name w]
  }

  Trace proc closeTraceFile {} {
    close $Trace::traceStream
    my set traceStream stdout
  }

  Trace proc puts line {
    puts $Trace::traceStream $line
  }

  Trace proc add className {
    $className instfilter [concat [$className info filter] traceFilter]
  }

First we define the object and set a variable for the stream to which we send the trace outputs (here: stdout). With a method for opening and a method for closing a file we can redirect the trace stream to a file. puts is helper method for the filter to print an output to the selected output stream. In add the traceFilter is appended to the existing filters of a specified class. The actual filter method (see below) displays the calls and exits of methods with an according message. The calls are supplied with the arguments, the exit traces contain the result values. We have to avoid the tracing of the trace methods explicitly.

  Object instproc traceFilter args {
    # don't trace the Trace object
    if {[string equal [self] ::Trace]} {return [next]}
    set context "[self class]->[self callingproc]"
    set method [self calledproc]
    switch -- $method {
      proc -
      instproc {::set dargs [list [lindex $args 0] [lindex $args 1] ...] }
      default  {::set dargs $args }
    }
    Trace::puts "CALL $context>  [self]->$method $dargs"
    set result [next]
    Trace::puts "EXIT $context>  [self]->$method ($result)"
    return $result
  }

As trace message we write the callee´s context (class and proc), the invoked method (using calledproc), and the given arguments. In the switch statement we avoid to print whole method bodies.

With

  Trace add Room

messages to all rooms, including all instances of Room´s sub-classes, are surrounded with a CALL and an EXIT output. With

  Trace add Object

messages to all objects in an XOTcl environment are surrounded with a CALL and an EXIT output. In general, it is possible to restrict the trace to instances of certain classes, or to produce trace output for only certain methods. This requires registration methods and a more sophisticated implementation of the filter method.



Mixin Classes

Per-object and per-class mixins (see [Neumann and Zdun 1999c] for more details) are another interception technique of XOTcl to handle complex data-structures dynamically. Here, we use mixin as a short form for mixin class. All methods which are mixed into the execution of the current method, by method chaining or through a mixin class, are called mixin methods. Mixin classes resembles the filter presented in the preceding section. While the filters work on all calls to all methods of an object/class hierarchy, the mixin classes are applied on specific methods. The filter is defined in a single method, while the mixin is composes several method in a class.

Supplemental Classes

Mixin classes cover a problem which is not solvable elegantly just by the method chaining, introduced so far. To bring in an addition to a class, the normal XOTcl way is to define a mixin method and chain the methods through next, e.g.:

  Class Basic
  Basic instproc someProc  {
    # do the basic computations
  }
  Class Addition
  Addition instproc someProc {
    # do the additional computations
    next
  }

In order to mix-in the additional functionality of the supplemental class Addition a new helper class (sometimes called intersection class) has to be defined, like:

  Basic+Addition -superclass {Addition Basic}

This is even applicable in a dynamical manner, every object of the class Basic may be changed to class Basic+Addition at arbitrary times, e.g.:

  Basic basicObj
  ...
  basicObj class Basic+Addition

Now consider a situation with two addition classes. Then following set of classes has to be defined to cover all possible combinations:

  Class Basic
  Class Addition1
  Class Addition2
  Class Basic+Addition1 -superclass {Addition1 Basic}
  Class Basic+Addition2 -superclass {Addition2 Basic}
  Class Basic+Addition1+Addition2 -superclass {Addition2 Addition1 Basic}

The number of necessary helper classes rises exponential. For n additions, 2n-1 (or their permutations if order matters) artificially constructed helper-classes are needed to provide all combinations of additional mix-in functionality. Furthermore it is possible that the number of additions is unlimited, since the additions may produce other additions as side-effects. This demonstrates clearly that the sub-class mechanism provides only a poor mechanism for mix-in of orthogonal functionality. Therefore we provide an extension in the form of class-object mixin classes, which are added in front of the search precedence of classes.

Per-Object Mixins

The mix-ins methods extend the next-path of shadowed methods. Therefore, per-object mix-in methods use the next primitive to access the next shadowed method. Consider the following example:

  Class Agent
  Agent instproc move {x y} { 
    # do the movement
  }
  Class InteractiveAgent -superclass Agent
  # Addition-Classes
  Class MovementLog
  MovementLog instproc move {x y} { 
    # movement logging
    next
  }
  Class MovementTest
  MovementTest instproc move {x y} {
    # movement testing
    next
  }

An agent class is defined, which allows agents to move around. Some of the agents may need logging of the movements, some need a testing of the movements, and some both (perhaps only for a while). These functionalities are achieved through the additional classes, which we will apply through per-object mixins.

Before we can use the per-object mix-ins on a particular object, we must register the mixins on it with the mixin instance method. It has the syntax:

  objName mixin mixinList

For example we may create two interactive agents, where one is logged and one is tested:

  InteractiveAgent i1; InteractiveAgent i2
  i1 mixin MovementLog
  i2 mixin MovementTest

At arbitrary times the mixins can be changed dynamically. For example i2's movements can also be logged:

  i2 mixin MovementTest MovementLog


  

Figure 6: Per-Object Mix-ins: Next-Path for the Example



The mixin option of the info instance method allows us to introspect the per-object mixins. It has the syntax:

  objName info mixin ?pattern?

It returns the list of all mix-ins of the object, if pattern is not specified, otherwise it returns the matching per class-object mixin classes.

The inverse operation of info mixin is mixinof finds out, into which objects an per-object mixin class is mixed into.
  clsName info mixinof ?pattern?

Note, that the constructors (init methods) of per-object mixins (and per-class mixins) are only called, if the mixin is registered already during object initialization (when init is called). For per-object mixins, one can achieve the initialization of a mixin via an idiom like

  Object o -mixin M -init
that registers the mixin before init is called. When a mixin is registered after object creation and it needs initializations, it is necessary to define special methods for this. Note, that the behavior described here is introduced in version 0.84 to ensure consistent behavior of intrinsic classes, per-object and per-class mixins, and to achieve predictable behavior for dynamic registration for all kind of mixins, and as well during recreations of objects having mixins registered. Older versions used heuristics for the initialization of per-object mixins.

Per-Class Mixins

Per-class mixins are exactly identical in their behavior to per-object mixins, but they operate on classes. Thus they are the class-specific variant of the per-object mixins, like instprocs are a class-specific variant of procs. Therefore, in the language the per-class mixins are called instmixins.

In general a per-class mixin is a class which is mixed into the precedence order of all instances of the class and all its subclasses it is registered for. It is also searched before the object's class itself is searched, but after per-object mixins.

Per-class mixins are linearized according to the precedence order like classes on the superclass hierarchy. I.e. from the full list of per-object mixins, per-class mixins, and intrinsic classes (and all the superclasses of all these classes) always the last occurrence is used.

From the point of view of language expressibility instmixins are not required, because they cannot express anything that per-object mixins cannot express already (like procs can express any instproc feature). As alternative to instmixins, we could simply register the per-object mixins in the constructor of the class.

But there at least the following reasons for instmixins as an additional language construct:

  1. we can at runtime determine with info mixin and info instmixin whether it is a class- or object-specific mixin. Thus we get a better structuring at runtime.
  2. We have not to 'pollute' the constructors with per-class mixin registrations. Therefore, the constructors get more understandable.
  3. If it is required to add (and remove) dynamically interceptors to a set of objects, which are instances of a certain type, per-class mixins are much easier to handle (e.g. add an instmixin to Object to intercept e.g. all calls to certain predefined methods).
  4. The language is more 'symmetrical', since any object-specific feature in XOTcl has a class-specific variant.

The mix-ins methods of per-class mixins extend the next-path of shadowed methods in the same way as per-object mixin methods. Before we can use a per-class mix-in on a particular class, we must register the mixin on it with the instmixin instance method. It has the syntax:

  className instmixin mixinList
The inverse operation of info inmixin is instmixinof finds out, into which objects an per-object mixin class is mixed into.
  className info instmixinof ?-closure? ?pattern?

Now consider that in the given per-object mixin example all interactive agents should be tested. We could either build a subclass TestedInteractiveAgent or register the per-object mixin in the constructor of the interactive agent class. The subclass solution leads to the same combinatorial explosion of intersection classes as discussed in the previous section, if more supplemental classes are added. The per-object mixin solution pollutes the constructor and does not prevail the structural semantics that the 'tested' property belongs to the interactive agent class at runtime

Here, we can use a per-class mixin:

  Class Agent
  Agent instproc move {x y} {# do the movement}
  Class InteractiveAgent -superclass Agent
  Class MovementTest
  MovementTest instproc move {x y} {
    # movement testing
    next
  }

  # now register the instmixin
  InteractiveAgent instmixin MovementTest

The per-class mixin now operates on all interactive agent including the instances of subclasses. E.g. for interactive agents i1 and i2 we automatically have movement testing. i2 is also logged, since it has the logging class as object-specific mixin:

  InteractiveAgent i1
  InteractiveAgent i2 -mixin MovementLog

  i1 move 3 4
  i2 move 1 2 

At arbitrary times the instmixins can be changed dynamically.

The instmixin option of the class info instance method allows us to introspect the per-class mixins. It has the syntax:

  className info instmixin ?className2?

It returns the list of all instmixins of the the class, if className2 is not specified, otherwise it returns 1, if className2 is a mixin of the object, or 0 if not.

Per-class mixins are applied transitively. That means the per-class mixin A of a per-class mixin B is also applied for an object in in B's scope. This is exactly the same as how superclasses are applied for instances. Consider the following example

  Class X11 \
     -instproc test args {
	puts [self class]
	next
     }
  Class X12 \
    -instproc test args {
	puts [self class]
	next
    }
  Class X \
    -instmixin {X11 X12} \
    -instproc test args {
	puts [self class]
	next
    }

  Class Y \
    -instmixin X

  Y create y -test
  X create x -test

Here the application as a superclass (for x) yields the same result as the application as an instmixin (for y):

  ::X11 
  ::X12 
  ::X

Precedence Order

The precedence order is composed by the precedence order of the superclass hierarchy (as explained earlier) and the message interceptors. In general, filters precede mixins and the superclass hierarchy. They are applied in the order of the next path of the object. Thus per-object filters are ordered before per-class filters.

Mixins are processed after the filters. Again, they are applied in the order of the next path of the object. Thus per-object mixins are ordered before per-class mixins.

Finally, the object's own heritage order comes in the order: object, class, superclasses.

The three precedence order lists (filters, mixins, and classes) are pre-calculated and cached.

Filters as well as classes (mixins and ordinary classes) are linearized. That means, each filter and each class can be only once on a precedence order list. If a filter or class can be reached more than once, than the last occurrence is used.

For instance, consider a class A is superclass, per-class mixin, and per-object mixin. On the precedence order lists only the last occurrence as a superclass is used after linearization.

Guards for Filters and Mixins

Message interceptors, such as filters and mixins, are applied for potentially huge number of messages. In many cases it is possible to reduce the effective number of cases in which interceptors are applied. Interceptor guards offer this functionality: they are boolean conditions with which you can specify in which cases a registered interceptor should be applied.

Filter Guards

A filter guard is a set of conditions that determine whether a filter is to be executed upon a certain invocation or not. Syntactically we can append a filter guard to the filter registration, or it can be registered using the methods filterguard for filters and instfilterguard for instfilters.

Each filter guard is an ordinary condition. A filter guard is executed in the call frame of the filter to be executed, if the filter guard returns 1. Thus, the call-stack information are already set to the values of the targeted filter - and these values can be used in the filter guard.

Let us consider a simple program:

Class Room
Room instproc enter {name} {puts [self proc]}
Room instproc leave {name} {puts [self proc]}
Room instproc loggingFilter args {
    puts [self calledproc]
    next
}
Room instfilter loggingFilter

Now consider we only want to apply the logging filter for enter and leave, not for any other message sent to Room instances. In the following example, for instance, we do not want to log the set message:

Room r 
r enter Uwe
r leave Uwe
r set roomName "Office"

In this example a filterguard can be applied to restrict the application of the filter to those two methods:

Room instfilterguard loggingFilter {
  [self calledproc] == "enter" || 
  [self calledproc] == "leave"}

Here we limit the filter application of the logging filter on rooms to calls to enter and leave. All other calls are not filtered at all. Note that the same syntax can also be applied for filterguard. Also, there is a short form to register filter guards directly during filter registration. The following code has the same semantics as the filter and filter guard definitions above:

Room instfilter {{loggingFilter -guard {
    [self calledproc] == "enter" || 
    [self calledproc] == "leave"}}}

The filter guard language construct is registration centric. It only applies for the class or object on which a filter is registered, not for all applications of the filter method. That is, if we use loggingFilter on another class we may give no or completely different filter guards.

If no filter guard is given for a filter, we assume that it is to be applied on all methods (equivalent to the filter guard '1' which is always true).

There are introspection options for filter guards. In particular, we can use info filterguard and info instfilterguard for getting the filter guards for a particular filter or instfilter respectively. For instance:

puts [Room info instfilterguard loggingFilter]

This prints out the content of the above guard definition. We can also append -guard to info filter or info instfilter to obtain a filter definition with guards:

puts [Room info instfilter -guards]

If we call a method from within a filter guard, as for instance callsMethod, we might require some parameters from the guard's context, such as calledproc. These parameters can be passed as references, as in the following example:

  Room instfilter loggingFilter
  Room instfilterguard loggingFilter {[my callsMethod openURL [self calledproc]]}

This example works because the filterguard is already set to the scope of the guard. Now we can use this dynamic calledproc context in the called method:

  Room instproc callsMethod {method calledproc} {
    return[string match $calledproc $method]
  }

We simply check whether the called method matches the given method name or not.

Mixin Guards

Similar to filters, there are mixin guards, defined with mixinguard and instmixinguard, or with -guard during mixin registration. Consider a simple example: there are a number of birds who have two mixins: Fly and Sing. For Fly there are limitations: a bird can only fly if it is at least two years old and is not a Penguin. Such problems are be solved with mixin guards:

  Class Fly
  Fly instproc fly {} {puts "[my signature]: yippee, fly like an eagle!"}

  Class Sing
  Sing instproc sing {} {puts "[my signature]: what a difference a day makes"}

  Class Animal -parameter age
  Animal instproc unknown args { puts "[my signature]: how should I $args?"}
  Animal instproc signature {} {
    return "[self] [my info class] ([my age] years)"
  }

  Class Bird -superclass Animal
  Class Penguine -superclass Bird
  Class Parrot -superclass Bird
  Class Duck -superclass Bird

  Parrot tweedy -age 1
  Penguine pingo -age 5
  Duck donald -age 4
  Parrot lora -age 6

  Bird instmixin {{Fly -guard {[my age] > 2 && ![my istype Penguine]}} Sing}

An invocation like:

foreach bird {tweedy pingo donald lora} { $bird fly }

yields the following result:

::tweedy ::Parrot (1 years): how should I fly?
::pingo ::Penguine (5 years): how should I fly?
::donald ::Duck (4 years): yippee, fly like an eagle!
::lora ::Parrot (6 years): yippee, fly like an eagle!

There are similar introspection options for mixin guards as those for filter guards. In particular, we can use info mixinguard and info instmixinguard for getting the mixin guards for a particular mixin or instmixin respectively.

Querying, Setting, Altering Filter and Mixin Lists

The methods mixin, instmixin, filter and instfilter are system slots having the same query and update interface.
  • If one of those methods is called without argument, it returns the current setting.
  • If it is called with one argument, the argument is used to set the specified list as indicated in the above examples.
  • If these methods are called with more than one argument, the first argument is used to specify the action. Possible values for the action are set, get, add and delete. See below for commonly used examples.

obj mixin same as: obj info mixin
obj mixin {C1 C2} same as: obj mixin assign {C1 C2}
obj mixin assign {C1 C2}sets the mixins for obj
obj mixin add C3 adds the mixin C3 on front of the mixin list
obj mixin add C3 end adds the mixin C3 at the end the mixin list
obj mixin add C3 3 adds the mixin C3 at the 3rd position
obj mixin delete ::C3removes the mixin C3 from the mixin list. Use absolute class names. delete supports an optional flag -nocomplain that does not produce an error, when the specified class is not in the list.

Note, that the list of possible actions can be extended by extending the class ::xotcl::Relations.

Querying Call-stack Information

Since the presented interceptors are normal XOTcl instprocs they can access all XOTcl introspection abilities introduced so far. In instprocs all recent information is accessible within their scope. But the interceptors are mechanisms, which cover more then their sole scope. The meaningful usage of the meta-programming abilities often requires to go further and to get information from the caller's and the callee's scope (e.g for delegation decisions). Therefore, we introduced rich call-stack informations for the interceptors. Note, that these are also available for ordinary methods, but the "called..." info options return empty strings.

All call-stack information are packed compactly into the self primitive as additional options. Note, before XOTcl version 0.84 these were implemented as a part of the info method. They are part of the self command for conceptual integrity: introspection options in info can be expected to produce the same result, when they are not explicitly changed. In contrast, all information provided by self are call-stack dependent.

Querying Call-stack Information via self

self activelevel

Returns the stack level from where the current command was invoked from, or where the last next was called (whatever is closer to the invocation). If the current command was invoked from an XOTcl method the absolute level is returned (e.g. #4) which can be used in the uplevel or upvar Tcl command or XOTcl method. If the current command was not invoked from an XOTcl method, the value 1 is returned.

self calledproc

Returns the name of the method which was invoked in the original call.

self calledclass

Returns the name of the class which presumably (if no dynamic class change occurs afterwards) is invoked in the original call.

self callingclass

Returns the name of the class from which the call was invoked (if one exists, otherwise an empty string).

self callinglevel

Returns the stack level from where the current command was invoked from. In contrary to activelevel next-calls are ignored in the computation. If the current command was invoked from an XOTcl method the absolute level is returned (e.g. #4) which can be used in the uplevel or upvar Tcl command or XOTcl method. If the current command was not invoked from an XOTcl method, the value 1 is returned.

self callingproc

Returns the name of the method from which the call was invoked (if one exists, otherwise an empty string).

self callingobject

Returns the name of the object from which the call was invoked (if one exists, otherwise an empty string).

self filterreg

In a filter: returns the name of the object/class on which the filter is registered. Returns either 'objName filter filterName' or 'className instfilter filterName'.

self isnextcall

Return 1 if this method was invoked via next, otherwise 0

self next

Return the "next" method on the path as a string, i.e. the method which will be called by [next].


Note, that three options with the prefix calling represent the values of self, self proc, and self class in the scope where the original call was invoked. In the following section we will show a simple program in which all of the info options have different values.


Filter Call-stack Information Example

Now we discuss a simple example that shows that all filter introspection options may have different values:

  Class InfoTrace
  InfoTrace instproc infoTraceFilter args { 
    puts "SELF:                [self]"
    puts "SELF PROC:           [self proc]"
    puts "SELF CLASS:          [self class]"
    puts "INFO CLASS:          [my info class]"
    puts "CALLED PROC:         [self calledproc]"
    puts "CALLING PROC:        [self callingproc]"
    puts "CALLING OBJECT:      [self callingobject]"
    puts "CALLING CLASS:       [self callingclass]"
    puts "REGISTRATION CLASS:  [self filterreg]"
    puts "CALLING LEVEL:       [self callinglevel]"
    puts "ACTIVE LEVEL:        [self activelevel]"
    next
  }

  Class CallingObjectsClass
  CallingObjectsClass callingObject

  Class FilterRegClass -superclass InfoTrace
  Class FilteredObjectsClass -superclass FilterRegClass 
  FilteredObjectsClass  filteredObject 

  CallingObjectsClass instproc callingProc args {
     filteredObject set someVar 0
  }    
  FilterRegClass instfilter infoTraceFilter

The invocation of callingObject callingProc produces the following output:

  SELF:                ::filteredObject
  SELF PROC:           infoTraceFilter
  SELF CLASS:          ::InfoTrace
  INFO CLASS:          ::FilteredObjectsClass
  CALLED PROC:         set
  CALLING PROC:        callingProc
  CALLING OBJECT:      ::callingObject
  CALLING CLASS:       ::CallingObjectsClass
  REGISTRATION CLASS:  ::FilterRegClass instfilter infoTraceFilter
  CALLING LEVEL:       #1
  ACTIVE LEVEL:        #1

The filter reports for self the value filteredObject, since this is the object on which the set call is invoked; infoTraceFilter is the method of the filter, and therefore, the actual proc, while the actual class is InfoTrace, the filter's class. The class of the actual object is FilteredObjectsClass.

The called procedure is set. While the program stays in a XOTcl-instproc all calling-info-options are set, the calling procedure is callingProc, the calling class is the class, where the method is defined (namely CallingObjectsClass), and the object from which the call invoked is callingObject.

In this example, the calling level is equal to the active level, both are #1.

Slots

A slot is a meta-object that manages property-changes of objects. A property is either an attribute or a role in an relation. In a nutshell, a slot has among other attributes:

  • a name (which it used to access it),
  • a domain (object or class on which it can be used) , and
  • can be multivalued or not.

We distinguish between system slots (predefined slots like class, superclass, mixin, instmixin, filter, instfilter) and attribute slots (e.g. attributes of classes).

System Slots

System slots are predefined slots defining e.g. some relations between classes, or between objects and classes. The predefined system slots are:

  • superclass: every class in XOTcl has one or more superclasses. The name of this slot is superclass, the domain is ::xotcl::Class, the slot is multivalued, since one object might have multiple superclasses.

  • class: every object has a class; therefore, the domain of the slot is ::xotcl::Class, the property is not multivalued.

  • mixin: every object in XOTcl can have one or more mixin classes. The name of this slot is mixin, the domain is ::xotcl::Object , the slot is multivalued.

  • instmixin: same as above, but the domain is ::xotcl::Class.

  • filter, instfilter: similar to mixin and instmixin.

The system slots were introduced earlier with their semantics. Here we just point out, that they have all the same interfaces for querying, setting, adding and removing of slot values.

Every slot can be used set and query the property from its domain. The syntax for setting values is

  object property newValue
and for getting its values is
   set x [object property]
where property denotes the slot name. Every multivalued slot provides the methods add and delete. Here are a few examples for using the system slot mixin which we have introduced already in the section of the mixins
  Object o; Class M; Class N
  o mixin ::M      ;# replacing the per-object mixins of o with M
  o mixin reset ::M  ;# same as before
  o mixin add ::N   ;# add N to the front of the mixin list
  o mixin delete ::M ;# delete M from the mixin list
  puts [o mixin]   ;# query the current mixin list
Every system slot (e.g. superclass) has the exact same interface.

Attribute Slots

Attribute slots are used to manage the setting and querying of instance variables. We define now a person with three attributes name, salary and projects.

  Class Person -slots {
    Attribute name
    Attribute salary -default 0
    Attribute projects -default {} -multivalued true
  }

These attributes might have a default value or they might be multivalued. When an instance of class Person is created, the slot names can be used for specifying values for the slots.

  Person p1 -name "Joe"	

Object p1 has three instance variables, namely name, salary and projects. Since slot projects is multivalued, we can add a value to the list of values the add subcommand.

  Project project1 -name XOTcl \
     -description "A highly flexible OO scripting language"

  p1 projects add ::project1
  p1 projects add some-other-value

The value of the instance variable project of Person p1 is now the list {some-other-value ::project1}.

Attribute slots are implemented via dynamic object aggregations (see below), where the Class objects contain the slot objects with the information like default etc. In order to prevent name clashes between the slot objects and the methods of a class (like e.g. create), an intermediary object named slot is used as a container of the slot objects. In the example above we create an object structure of the following form:

  Person
  Person slot name
  Person slot salary
  Person slot projects

This object structure can be used to to query and modify the slot properties or to add additional methods to the slot objects. One application is for example to used slot-specific methods for checking slot values, as shown in the next section.

  Person info vars  ;# results in the list of variables of ::Person
  Person slot name info vars ;# list of variables of the slot object ::Person::slot::name
Since slot objects are ordinary XOTcl objects, they can have their own slots as well (such as default, name etc.). The following example sets and queries the default of the slot name of Person:

  Person slot name default "gustaf"
  ? {Person slot name default} gustaf

However, due to the current implementation, it is necessary to re-init the slot object when the slot properties (such as e.g. default) are changed. This can be achieved by calling the method init of the slot object.

Note that a statement for creating a slot object like

  ... {
    Attribute name
    ...
  }

is a short hand notation for

  ... {
    Attribute create name
    ...
  }

This is exactly the same situation like every where else in XOTcl, when an object/class is created. One has to use create explicitly, when a name of a slot object conflicts with a method of the class (e.g. one has to use "Attribute create class" if a slot named class is created).

One cannot define on a meta-class an attribute named slot or slots and use then "... MetaClass Foo -slots { ::xotcl::Attribute x}... to create the slot objects. To handle this naming conflict, one has to create the slot objects outside of the aggregation and to provide the values for the properties of Attribute (domain, manager, .... ) by hand.

Setter and Getter Methods for Slots

When a slot is called via its name, the call is delegated to the slot object. Per default, the slot value is read via the get method of the slot and it is set the assign method. By redefining these methods, it is possible to provide custom setter and getter methods. The following example redefines the setter methods assign to check, whether an attribute value is within the range between 1 and 99.

  Class create A -slots {
    Attribute foo -default 1 -proc assign {domain var value} {
      if {$value < 0 || $value > 99} {
        error "$value is not in the range of 0 .. 99"
      }  
      $domain set $var $value
    }
  }

  A create a1
  ? {a1 foo 10} 10
  ? {a1 foo} 10
  ? {catch {a1 foo -1}} 1

For the most common simple cases with single valued attributes, where neither setter or getter are redefined, XOTcl optimizes the slot access function and replaces the delegation to the slot object by the the C-level implementation of instparametercmd.

Note that it is possible to subclass Attribute (e.g. in order to store more properties for attributes, like when attributes are stored in a relational database) or to register mixin-classes or filters.

Backward-compatible Short-Hand Notation for Attribute Slots

XOTcl provides a short-hand notation for creating attribute slots, which is backward compatible for the most important options of XOTcl verison prior to 1.5.0. Instead of writing

  Class Car -slots {
    Attribute owner
    Attribute doors -default 4
  }

one can use as well

  Class Car -parameter {
    owner
    {doors 4}
  }

The various features of the prior implementation of parameter are deprecated and will be removed in future versions.

Experimental Slot Features

Value Checking

Attribute slots can have types assigned which are tested whenever the instance variable is altered. The slot salary is defined as integer whereas projects is defined to be a list of instances of the class ::Project (a list of instances, since projects is defined as multivalued).

  Class Person -slots {
    Attribute name
    Attribute salary -default 0 -type integer
    Attribute projects -default {} -multivalued true -type ::Project
    ...
  }

  Person p2 -name "Sue"	-salary 1000

It is as well possible to define custom value checkers and to normalize the input values. We extend the previous example and define "my sex" as value for type. If the value checker consists of multiple words, the type check compiler assumes that the value is a Tcl command, to which the actual value is appended as additional argument before invocation. my refers to the slot object. In the example below, we define for the slot object an object specific method that returns 1 or 0 depending on the success of the check. This method (a) checks the values via switch and (b) normalizes and resets the value via uplevel.

  Class Person -slots {
    ...
    Attribute sex -type "my sex" -proc sex {value} {
      switch -glob $value {
        m* {my uplevel {$obj set $var m}; return 1}
        f* {my uplevel {$obj set $var f}; return 1}
        default {return 0}
      }
    }
  }

The slot values are actually checked via Tcl variable traces whenever the associated variable gets a new value assigned. This means that the values are enforced now matter how the variables are set. Therefore, the checks are performed in the following two commands as well, although the slot values are not accessed via the slot names. The checks will throw an error in the second command, since 1100x is not an integer.

  p2 incr salary 100
  p2 append salary x

Similarly the second command below will throw an error, since some-other-value is not an instance of ::Project.

  p2 projects add ::project1
  p2 projects add some-other-value     

When a check throws an error, the instance variables are reset to the previous value. To restore the original value, an associative array __oldvalue() is kept as instance variable in the object.

In general, checking of variables can be turned off globally by

  ::xotcl::Slot instmixin add ::xotcl::Slot::Nocheck

This mixin replaces the methods check and checkall as well as mk_type_checker by no-ops. When the mixin is active and the Attribute definitions are loaded, the specified type has no effect.

Value checking can be turned off also selectively for each slot via using ::xotcl::Slot::Nocheck as per-object-mixin; if attributes are subclassed, it is possible to register the Nocheck mixin on a subclass of Attribute.

Init Commands and Value Commands for Slot Values

An init command (initcmd) of a slot is similar to a default and is a command to be executed when the value of the associated variable is read the first time. That means that when an object is created the associated variable has no value. On the contrary, when a default is used, the variable is set to the default value, when the object is created.

The primary advantage of slot init commands is Lacy initialization: When an object has many slots and the initialization of all slots is costly (e.g. the value of each slot is fetched from a relational database), and not all of the values are needed for each instance, only the relevant variables of the object are initialized on demand.
  Class C -slots {
    Attribute x -initcmd {puts {init}; set _ 101}
  }

  C c1
  c1 info vars  ;# ==> returns ""
  c1 set  x     ;# ==> puts init, returns 101
  c1 info vars  ;# ==> returns "x"

The initcmd is executed only once, when the variable is read the first time. For later reads of the variable contents, the values are returned.

A value command (valuecmd) of a slot is similar to a a init command, except that it is executed whenever the value of the variable is read. A value command can be used e.g. to implement live updates for variables or for abstracting from sql sequences or the like.

Finally the value changed command (valuechangedcmd) can be used to specify the behavior, whenever the value of the variable is altered. This option is used to implement the value checking described in the last section.

The slot parameters default, initcmd and valuecmd have to be used mutually exclusively.

Nested Classes and Dynamic Object Aggregations

Most object-oriented analysis and design methods are based on the concepts of generalization and aggregation. Generalization is achieved through class hierarchies and inheritance, while static aggregation is provided through embedding. Since version 8.0 Tcl offers a namespace concept which can be used as a mechanism to provide dynamic aggregations.

A namespace provides an encapsulation of variable and procedure names in order to prevent unwanted name collisions with other system components. Each namespace has a unique identifier which becomes part of the fully qualified variable and procedure names. Namespaces are therefore already object-based in the terminology of Wegner. OTcl is object-oriented since it offers classes and class inheritance. Its objects are also namespaces, but an object is more than only a namespace. Therefore, two incompatible namespace concepts have existed in OTcl in parallel.

In XOTcl every object and every class is logically implemented as a separate Tcl namespace. The biggest benefit of this design decision aside from performance advantages is the ability to aggregate objects and nest classes. Contrary in OTcl every object has a global identifier. Through the introspection abilities of namespaces nested classes are also traceable at runtime and can be changed dynamically. In XOTcl objects are allowed to contain nested objects, which are dynamically changeable aggregates of the containing object.

Nested Classes

The notation for nested classes follows the syntax of Tcl namespaces by using ``::'' as a delimiter. For example the description of a oval carpet and a desk can nest inside of the OvalOffice class:

  Class OvalOffice
  # general carpet
  Class Carpet
  Class OvalOffice::Desk
  # special oval carpet - no name collision
  Class OvalOffice::Carpet -superclass ::Carpet

Nested classes can be used exactly like ordinary classes, a user can sub-class it, derive instances, etc. The information about the nesting structure of classes is available through the info instance method:

  className info classchildren ?pattern?
  className info classparent

The classchildren option returns a list of children, if one or more exist, otherwise it returns an empty string. classparent results in the name of the parent class, if the class is nested. Since nested classes are realized through namespaces, all functionality offered by Tcl's namespace command is usable from XOTcl as well.

Dynamic Object Aggregations

The nested classes only provide an aggregation of the descriptive not of the runtime properties of an object. We have pointed out the difference of object and class in XOTcl. Because of the splitting of a class into class and class-object it is possible to give each object its own namespace. The internal implementation of objects enable them to contain nested objects, which are aggregates of the containing object. In XOTcl these can be changed dynamically and introspected through the language support of dynamic object aggregations [Neumann and Zdun 2000b]. Suppose an object of the class Agent should aggregate some property objects of an agent, such as head and body:

  ClassAgent
  Agent myAgent

  Class Agent::Head
  Class Agent::Body

  Agent::Head ::myAgent::myHead
  Agent::Body ::myAgent::myBody

Now the objects myHead and myBody are part of the myAgent object and they are accessible through a qualification using ``::'' (or through Tcl's namespace command). But in the common case they will be accessed, as introduced so far: the explicit full qualification is not necessary when such variables are being accessed from within XOTcl methods, since the object changes to its namespace.

The information about the part-of relationship of objects can be obtained exactly the same way as for classes through the info interface:

  objName info children ?pattern?
  objName info parent

Relationship between Class Nesting and Object Aggregation

The classes Head and Body are children of the Agent class. It is likely that all agents, interactive or not, have properties for head and body. This implies a static or predetermined relationship between class nesting and object aggregation. Such predetermination do not exist in XOTcl, but are simply build, when specifying the relationship in the constructor, e.g.:

  Agent instproc init args {
    ::Agent::Head [self]::myHead
    ::Agent::Body [self]::myBody
  }

Now all agents derived from the class have the two property objects aggregated after creation. But still they are changeable in a dynamical manner, e.g. with:

  Agent myAgent
  myAgent::myHead destroy

The agent turns into a headless agent. In companion of the introspection mechanisms such constructions could be very useful. Suppose, that in the virtual world the agents heads may be slashed from their bodies. The graphical system simply needs to ask with info children on the agent's object, whether it has a head or not and can choose the appropriate graphical representation.

Simplified Syntax for Creating Nested Object Structures

To ease the generation of nested structures, one can use the predefined method contains. In essence, contains changes the namespace, where objects are created to the object, on which it is executed. In the example below, we create three nested rectangles, where two of these contain two more points. The outer rectangle is r0 containing rectangle r1 and r2.
  Class Point -parameter {{x 100} {y 300}}
  Class Rectangle -parameter {color}

  Rectangle r0 -color pink -contains {
    Rectangle r1 -color red -contains {
      Point x1 -x 1 -y 2
      Point x2 -x 1 -y 2
    }
    Rectangle r2 -color green -contains {
      Point x1
      Point x2
    }
  }

  ? {r0 color} pink
  ? {r0 r1 color} red
  ? {r0 r1 x1 x} 1
  ? {r0 r1 x2 y} 2
  ? {r0 r2 color} green

Every object in XOTcl is realized as a Tcl command. If nested objects are created, these commands are available as object specific methods. So, instead of calling the contained rectangle r1 via the fully qualfied name ::r0::r1, one can use r0 r1. This is exactly the same situation as it arises, when e.g. a global Tcl proc proc o1 {} {...} and an XOTcl object o1 (created via Object o1) is created. Both commands cannot coexist in the same namespace.

Copy/Move

Often an object has to be copied/moved. This is a very useful functionality when XOTcl should be used as a prototyping language. The XOTcl method move provides this functionality. Another common behavior is implemented by the copy method which clones the actual object to a destination object via a deep copy operation. The two methods have the syntax:
  objName move destination
  objName copy destination

Copy and move operations work with all object/class information, i.e., information on filters, mixins, parameters, etc. are automatically copied. Copy and move are integrated with class nesting and object aggregations. All copy/move operations are deep copy operations: all nested objects/classes are automatically copied/moved, too. E.g. if we want to reuse an imperial march object of star wars for star wars 2, we can just copy the object:

  starWars::imperialMarch copy starWars2::imperialMarch
Note that move is implemented in current versions of xotcl as a copy plus subsequent destroy operation.

Method Forwarding

As you have seen from many previous examples, XOTcl's primary command for method forwarding is the next primitive. next calls the same-named method of the current object, usually with the same argument list. However, frequently method forwarding is required between different objects as well, commonly referred to as delegation.

In general, delegation can be achieved in XOTcl without any special construct using simple methods containing a few lines. However, In several situations, forwarding is as well needed to plain Tcl commands, for example, if object oriented stubs are implemented on base of non-oo function calls. These functions might access instance variables of the objects. XOTcl uses this functionality in various situations, such as for instance in the implementation of the set, unset, append, array methods among others.

The fowarding functionality is suppored by XOTcl be the methods forward and instforward that address these requirements and provide an efficient implementation for these tasks.

The forwarding command specifies that whenever methodName is called, this invocation is delegated to callee, where the actual argument list (from the invocation) is appended to the argument list specified in the forwarding command. Like for procs and instprocs, we can distinguish between forward and instforward, depending on we want to the method available for a single object of for the instances of a class.

The general form of the forwarding commands is:

  obj  forward methodName ?options? callee ?arglist? 
  cls  instforward methodName ?options? callee ?arglist? 
where valid options are -objscope, -methodprefix, -earlybinding and -default. The option -objscope is used to specify that the command should be executed in the scope of the calling object (i.e. instance variables apprear as local varibales), -methodprefix means that the called method should be prefixed with the specified string (to avoid name clashes), -earlybinding means that the function pointer of the specified command (callee) is take at invocation time (should only be done for (builtin) commands inplemented in C), and -default provides a means for providing default methods when none are specifed.

Each of the arguments after the method name (including callee) can be be substituted an invocation time, or they are taken literally. The arguments to be substituted are starting always with a percent sign. These arguemnts can be %self, %proc, %1, %argclindex, or % followed by a Tcl command, and it can be prefixed with a positional prefix %@. We will introduce the usage of these options and argument substitutions based on examples.

In our first example we define an object dog and an object tail. If the dog receives the call wag it delegates this call to the tail and returns its result. In this introductory example, the method tail simply returns its arguments.

In this example, forwarding is achieved through the method forward that creates a forwarder command. This method receives as first argument the name, under which the forwarder is registered, followed by the object that receives the delegation (the "callee"), followed my the (optional) method name and optional arguments. More about this later. Here we register the forwarder under the name wag, the callee is tail, and the method is defined to have the name of the forwarder. We could have written here dog forward wag tail wag as well, be we use %proc which refers to the name of the forwarder. Using %proc is slightly more general in cases the forwarder is renamed.

  ###########################################
  # trivial object delegation
  ###########################################
  Object dog
  Object tail
  tail proc wag args { return $args }
  dog forward wag tail %proc

With these definitions a call to "dog wag 100" calls actually "tail wag 100" which returns the result of 100.

The following command shows the delegation to a Tcl command (instead of delegation to an object). We define a simple forwarder that forwards a call to the Tcl command expr with some arguments.

  ###########################################
  # adding 
  ###########################################
  Object obj
  obj forward addOne expr 1 +
The invocation obj addOne 5 returns 6 as value.

In our next example we want additionally that the Tcl command should to be evaluated in the context of the current object. This means that the method can easily access instance variables of the delegating object. We define a forwarder for the class X with the name Incr (to avoid confusion with the already defined method incr), we use the -objscope option and specify incr as the callee. Since the forwarder is defined via instforward the forwarder is available to all instances of the class.

  ###########################################
  # evaluating in scope 
  ###########################################
  Class X -parameter {{x 1}}
  X instforward Incr -objscope incr
  
  X x1 -x 100
  x1 Incr x
  x1 Incr x
  x1 Incr x
After the three calls to Incr the call x1 x returns the value 103.

In our next example, we show the usage of the %-substitution more advanced argument handling. This example sketches the implementation of the mixin add, mixin set methods as shown above. In order to obtain extensible subcommands (such as mixin add, mixin delete, etc.), we define an object for which the subcommands are defined as methods. We will use this object as callee for the appropriate methods. So, we define an object named mixin and define a forwarder with the name Mixin (again we capitalize Mixin to avoid name clashes with the already defined methodmixin ).

  ###########################################
  # mixin example
  ###########################################
  Object create mixin
  mixin proc unknown {m args} {return [concat [self] $m $args]}
  obj forward Mixin mixin %1 %self
We define here the method unknown to see what arguments are passed. The following invocation will lead to the call in noted in the comment.
  obj Mixin add M1       ;# calls ::mixin add ::obj M1
You see that %1 was substituted by the first argument of the invocation (here add) and %self was substituted by the name of the current object (here ::obj). The second argument of the invocation (here M1) was appended as usual. However, in calls like
  obj Mixin
we have to deal with cases, where the used argument (%1) is not given at the invocation. In this case we get either an error message, or we can specify a default argument via the option -default:
  obj forward Mixin -default {getter setter} mixin %1 %self
This definition means that if no argument is specified in the invocation we call the method getter, if one argument is given the method setter, in other cases we use the specified arguments. Therefore the following three invocations are delegated as indicated in the comments.
  obj Mixin              ;# calls ::mixin getter ::obj
  obj Mixin M1           ;# calls ::mixin setter ::obj M1
  obj Mixin add M1       ;# calls ::mixin add ::obj M1

When we implement subcommands by delegating to other commands (as shown in the last example), there can be situations where naming conflicts might arise. For example, if we want to implement a subcommand method class we might not want to implement a new method class on the callee, since this would overwrite the standard definition of class. To overcome such difficulties, we provide the option -methodprefix. The following example shows how to prefix every called method with the prefix @.

  ###########################################
  # sketching extensible info
  ###########################################
  Object Info
  Info proc @mixin {o} {
    $o info mixin
  }
  Info proc @class {o} { ;# without prefix, doing here a [Info class] would be wrong
    $o info class
  }
  Info proc @help {o} { ;# define a new subcommand for info
    foreach c [my info procs] {lappend result [string range $c 1 end]}
    return $result
  }
  Object instforward Info -methodprefix @ Info %1 %self 
With this definitions, the following call is rewritten as indicated in the comment.
  x1 Info class          ;# ::Info @class ::x1

When a forwarder is defined, the callee (the target command) can be omitted. When the callee is not specified, the method-name is used instead. When the method-name has a namespace prefix, the method name is the tail and the callee is the fully qualified name.

  ###########################################
  # optional callee
  ###########################################
  obj set x 2
  obj forward append -objscope
  Object n; Object n::x
  obj forward ::n::x
With this definitions of the forwarder append and x, the following calls are rewritten as indicated in the comment.
  obj append x y z        ;# ::append x y z ... returning  2yz
  obj x self              ;# ::n::x self    ... returning  ::n::x

The forwarder append forwards the call to the Tcl command append, which accesses the instance variable x and appends the specified values.

The list of tokens executed by the forwarder might contain Tcl commands executed during every invocations. This makes it for instance possible to pass instances variables to the callee. In the next example the object has the instvar named x which is multiplied by a factor of 10 when the method x* is invoked.

  ###########################################
  # command substitution
  ###########################################
  obj set x 10
  obj forward x* expr {%my set x} *
With this definitions, the following call is rewritten as indicated in the comment.
  obj x* 10               ;# expr 10 * 10 ... returning  100

In certain situations it is necessary to insert arguments always at the same position (e.g. at the second to last position). The positional addressing can be achieved by prefixing the arguments of the forward specification by %@POS , where POS is either a positive (argument positing from the beginning) or negative integer (argument counting from the end) or the constant end (denoting the last position). After POS a single space is used as a delimiter for the rest of the argument, which might be some other %-substitution or a constant. The positional arguments are evaluated from left to right and should be used in ascending order.

The following examples show a few usages of the positional arguments in the forwarder. The forwarders f1 to f5 are created, followed by one or more usages. The first argument of the usage is the call to to forewarder, the second argument is the result.

  ###########################################
  # forwarding with positional arguments
  ###########################################
  Object obj
  obj forward f1 list {%@end 13}
  ? {obj f1 1 2 3 } [list 1 2 3 13]

  obj forward f2 list {%@-1 13}
  ? {obj f2 1 2 3 } [list 1 2 13 3]

  obj forward f3 list {%@1 13}
  ? {obj f3 1 2 3 } [list 13 1 2 3]
  ? {obj f3} [list 13]

  obj forward f4 list {%@2 13}
  ? {obj f4 1 2 3 } [list 1 13 2 3]

  obj forward f5 {%@end 99} {%@0 list} 10
  ? {obj f5} [list 10 99]
  ? {obj f5 a b c} [list 10 a b c 99]

The construct %argclindex LIST can be used to substitute an argument depending on the number of arguments when the forwarder is invoked. For example, it is possible to call forward to a different method depending on how many arguments are specified. The number of arguments is used as an index in the specified list. When the number of arguments is larger than the number of elements in the specified list, an error is generated.

  ###############################################
  # substitution depending on number of arguments
  ###############################################
  obj forward f %self [list %argclindex [list a b c]]
  obj proc a args {return [list [self proc] $args]}
  obj proc b args {return [list [self proc] $args]}
  obj proc c args {return [list [self proc] $args]}
  ? {obj f} [list a {}]
  ? {obj f 1 } [list b 1]
  ? {obj f 1 2} [list c {1 2}]
  ? {catch {obj f 1 2 3}} 1

Finally, the concluding example defines a class chan to use the I/O-commands in an OO-manner. The proc open is used to create a chan instance. For the channel object we provide the method close (to close a channel and to destroy the channel object), puts (to write on a stream), blocked (to check whether last command exhausted all input), and fconfigure (to configure the stream). Note that for puts we specified that the actual stream should be inserted as the second to last argument.

  Class chan -parameter stream
  # create stream and object
  chan proc open args { 
    set stream [eval open $args]
    my create $stream -stream $stream  ;# make an object
  }
  # close stream and destroy object
  chan instproc close {} {
    close [my stream]
    [self] destroy
  }
  # handle other subcommands (methods) via unknown
  chan instproc unknown {m args} {
    set valid [lsort [chan info instcommands]]
    stderr puts "unknown chan method '$m' $args called; 
      	defined methods: $valid"
  }
  chan create stdout -stream stdout   ;# define standard stream
  chan create stderr -stream stderr   ;# define standard stream

  chan instforward puts puts {%@-1 %my stream}
  chan instforward blocked fblocked {%my stream}
  chan instforward fconfigure fconfigure {%my stream} 

  set c [chan open /tmp/junk w]
  $c puts -nonewline "hello"
  $c puts -nonewline " world"
  $c puts ""
  $c xxx                                       ;# trigger unknown
  # The stream instances denote the currently open streams
  stderr puts "currently open streams: [chan info instances]" 
  $c close
  stderr puts "currently open streams: [chan info instances]"

Assertions

In order to improve reliability and self documentation we added assertions to XOTcl. The implemented assertions are modeled after the ``design by contract'' concept of Bertrand Meyer. In XOTcl assertions can be specified in form of formal and informal pre- and post-conditions for each method. The conditions are defined as a list of and-combined constraints. The formal conditions have the form of normal Tcl conditions, while the informal conditions are defined as comments (specified with a starting ``#''). The lists containing the pre- and post-conditions are appended to the method definition (see example below).

Since XOTcl offers per-object specialization it is desirable to specify conditions within objects as well (this is different to the concept of Meyer). Furthermore there may be conditions which must be valid for the whole class or object at any visible state (that means in every pre- and post-condition). These are called invariants and may be defined with following syntax for class invariants:

  className instinvar invariantList

or for objects invariants:

  objName invar invariantList

Logically all invariants are appended to the pre- and post-conditions with a logical ``and''. All assertions can be introspected.

Since assertions are contracts they need not to be tested if one can be sure that the contracts are fulfilled by the partners. But for example when a component has changed or a new one is developed the assertions could be checked on demand. For this purpose the check method can be used either to test the pre- or the post-conditions. The syntax is:

  objName check ?all? ?instinvar? ?invar? ?pre? ?post?

Per default all options are turned off. check all turns all assertion options for an object on, an arbitrary list (maybe empty) can be used for the selection of certain options. Assertion options are introspected by the info check option. The following class is equipped with assertions:

  Class Sensor -parameter {{value 1}}
  Sensor instinvar {
    {[regexp {^[0-9]$} [my value]] == 1}
  }
  Sensor instproc incrValue {} {
    my incr value
  } {
    {# pre-condition:} 
    {[my value] > 0}
  } {
    {# post-condition:} 
    {[my value] > 1}
  }

The parameter instance method defines an instance variable value with value 1. The invariant expresses the condition (using the Tcl command regexp), that the value must be a single decimal digit. The method definition expresses the formal contract between the class and its clients that the method incrValue only gets input-states in which the value of the variable value is positive. If this contract is fulfilled by the client, the class commits itself to supply a post-condition where the variable's value is larger than 1. The formal conditions are ordinary Tcl conditions. If checking is turned on for sensor s:

  s check all

the pre-conditions and invariants are tested at the beginning and the post-condition and invariants are tested at the end of the method execution automatically. A broken assertion, like calling incrValue 9 times (would break the invariant of being a single digit) results in an error message.

In assertions we do not check methods that modify or introspect assertions. These are check,info,proc,instproc,invar, and instinvar. The reason for this is that we want to be able to recover a malicious action in a catch error handler, like:

  ...
  if {[catch {my assertionBreakingAction} errMsg]} {
    puts "CAUGHT ERROR: $errMsg"
    # remember checking options, for turning them on later again
    set check [my info check]
    my check {}
    # recover from broken assertion
    ...
    # turning checking on again 
    $fb check $check
  }

Meta-Data and Automatic Documentation

To enhance the understandability and the consistency between documentation and program it is useful to have a facility to make the documentation a part of the program. There are several kinds of meta-data which are interesting for a class, e.g. the author, a description, the version, etc.

Older versions of XOTcl have contained a special meta-data command metadata. This command is now (from version 0.83) deprecated and replaced by an integrated solution with XOTcl's API documentation functionality. The object @ is used for documentation and metadata issues. Per default it is not evaluated at all. Everything that is send to @ is simply ignored. That way we do not waste memory/performance at runtime, if we do not require to parse the metadata/documentation.

If we have to know the meta-data/documentation, as for instance in the xoDoc component and the makeDoc tool, that handle XOTcl's internal documentation, we have to re-define the documentation object. Alternatively, we can partially parse the source code for @ commands.

With @ the meta-data/documentation is handled by first class XOTcl objects. By defining alternate @ implementations - as in xoDoc/makeDoc - we can evaluate the meta-data/documentation arbitrarily. xoDoc/makeDoc are only an HTML back-end, but the basic idea is to provide support for several other usages as well (e.g. XML, RDF, on-line help, documentation of dynamic structures, etc).

The object@ handles comments via its unknown method. xoDoc adds the appropriate instprocs to t@ to produce HTML output. The appropriate command is:

  tclsh src/lib/makeDoc.xotcl DOCDIR DOCFILES

The source of a documentation is structurally very similar to the XOTcl constructs being commented. E.g. one can copy an instproc and add comments at the right places, like:

    Class C
    C instproc m {a1 a2} {
       return [expr {$a1+$a2}]
    }

can be commented as follows

    @ Class C { description { "my sample class"} }
    @ C instproc m {a1 "first number" a2 "second number"} {
       description "add two numbers"
       return "sum of a1 and a2"
    }

One can do essentially a copy+paste of the source and add the comments via attribute value pairs. Every basic language construct can have a "description". If you want to include other properties to the description, you can add them like:

    @ C instproc m {a1 "first number" a2 "second number"} {
       author "GN+UZ"
       date "Feb 31"
       description "add two numbers"
       return "sum of a1 and a2"
    }

This way, author and date are added automatically to the generated HTML file. In addition, there is a @File hook for a per file description, like:

@ @File {
  description {
    This is a file which provides a regression test
    for the features of the XOTcl - Language. 
  }
}

Additional Functionalities

Abstract Classes

In XOTcl a class is defined abstract if at least one method of this class is abstract. The instance method abstract defines an abstract method and specifies its interface. Direct calls to abstract methods produce an error message. E.g. a Storage class provides an abstract interface for access to different storage forms:

  Class Storage
  Storage abstract instproc open  {name}       
  Storage abstract instproc store {key value}
  Storage abstract instproc list  {}         
  Storage abstract instproc fetch key        
  Storage abstract instproc close {}         
  Storage abstract instproc delete {k} 

All kinds of storage have to implement every method from the interface. E.g. a GNU Database Access, a relational database access, and several other storage forms may be derived by sub-classing (therefore, all conform to the same storage access interface).

Checking Commands for being Objects, Classes, or Meta-Classes

Since XOTcl is a hybrid language containing several Tcl commands, sometimes its necessary for applications to distinguish between Tcl commands and object commands for XOTcl. method of the Object class looks up an objName and returns 1 if it is an object and 0 if not:

  objName1 isobject objName2

If one can be sure that a command represents an object, it might be unsure if the command is only an object or also class or even meta-class. The two instance methods isclass and ismetaclass check in the same manner, whether a class or meta-class is given (since ever XOTcl class is an object, they also return 0, when objName is not an XOTcl object).

  objName1 isclass objName2
  objName1 ismetaclass objName2

Exit Handler

A task for a programming language, sometimes of similar importance as object creation, is the object destruction. XOTcl ensures that all objects are destroyed and their destructors are invoked when XOTcl applications terminate. For that reason objects and classes are destroyed in the order objects, classes, meta-classes. Sometimes further destruction order is of importance. For these cases, the XOTcl language provides an exit handler, which is a user-defined proc, which invokes user-defined exit handling just before the destruction of objects, classes, meta-classes is invoked. For instance, the exit handler lets the user specify objects which have to be destroyed before all other objects.

The exit handler is defined as a proc of Object, which is per default empty:

  ::xotcl::Object proc __exitHandler {} {
    # clients should append exit handlers to this proc body
    ;
  }

There are some procs of the Object class pre-defined, which let us specify an exit handler conveniently:

   Object setExitHandler body
   Object getExitHandler
   Object unsetExitHandler

setExitHandler lets us specify a proc body that actually contains the user-defined exit handling:

   Object setExitHandler {
     aObj destroy
     puts "exiting"
   }

destroys the object aObj before all other objects and prints the message existing to the screen. With getExitHandler the exit handler can be introspected. E.g. if we just want to append the destruction of object bObj to an existing exit handler, we use getExitHandler:

   Object setExitHandler "[Object getExitHandler]; bObj destroy"

unsetExitHandler deletes the exit handler.


Automatic Name Creation

The XOTcl autoname instance method provides an simple way to take the task of automatically creating names out of the responsibility of the programmer. The example below shows how to create on each invocation of method new an agent with a fresh name (prefixed with agent):

  Agent proc new args {
    eval my [my autoname agent] $args
  }

Autonames may have format strings as in the Tcl 'format' command. E.g.:

  objName autoname a%06d

produces

  a000000, a000001, a000002, ...

Integrating XOTcl Programs with C Extensions (such as TK)

Because all XOTcl commands are in the ::xotcl namespace, it is usually no problem to integrate XOTcl with other Tcl extensions. Most often it works to import the XOTcl commands (like Object, Class) into the current namespace because there are no name-clashes with the commands defined by other extensions.

Consider you want to perform a deeper integration of an other extension and XOTcl because you want to benefit from XOTcl's object system. For instance, you might want to introduce composite TK widgets (sometimes called mega-widgets) as classes and inherit from these classes. Here, you have two options: you can change or extend the C code of that other extension to provide XOTcl classes or objects, or you can write an XOTcl wrapper in Tcl. For the first alternative, there are some examples provided in the XOTcl distribution. XOTclGdbm provides an OO Tcl interface to the GDBM database, for instance. XOTclSdbm does the same for SDBM, and the TclExpat wrapper provides a class-based interface to the TclExpat XML parser.

Consider you do not want to change the C code of a Tcl extension. Then you can write an OO wrapper in XOTcl for the commands of the other extension. For stateless commands, you can simply write forwarder methods. If the extension maintains some state, you typically associate the state handle with an XOTcl parameter, acquire the state in the XOTcl constructor, and align the XOTcl destructor with the stateful instance.

Consider you want to wrap the Tk button widget. You can acquire the widget in the constructor, and maintain the widget ID in a parameter. You now can forward invocations to this widget ID (e.g. when using "pack"), or register command callbacks (like buttonPressed). Note that we let the "self" command be replaced in the scope of the current method so that TK receives the correct object ID for the callback. In the destructor we destroy the widget as well (we use "catch" because sometimes widgets can destroyed by other means as well (e.g. by their parent widget, when a widget/object hierarchy is destroyed at once).

  Class MyButton -parameter {button}
  MyButton instproc buttonPressed args {
    puts "pressed [my button]"
  }
  MyButton instproc init args {
    set ID [namespace tail [self]]
    my instvar button
    set button [button .$ID \
      -text "My Button $ID" \
      -command [list [self] buttonPressed]] 
    pack $button
    next
  }
  MyButton instproc destroy args {
     catch {destroy [my button]}
     next
  }

  # a test -> 3 buttons, destroy one of them
  foreach b {a b c} {
    MyButton $b
  }
  b destroy

The "trick" to substitute "self" within the current method scope works for all kinds of command callbacks. Extensions such as TK, however, often work with bindings to (global) variables as well. Using global variables is frowned upon in the OO community. Instead you should use instance variables of objects. As Tcl can only bind to existing namespace variables (and XOTcl acquires the namespace of an object on demand), you have to make sure that the namespace of an object exists before binding a variable. That can be done with "requireNamespace":

  GUIClass instproc buildEntry win {
    my requireNamespace
    entry $win -textvariable [self]::entryValue
    my set entryValue {Init Value}
  }

Note that in the above example we have used to tail of the object ID as ID for the widget. Usually, it is a good idea to the object name, if possible, for TK (and other extensions) IDs as well. Another option is to use a autoname to get a unique name for the ID.

Sometimes you want to simply send all invocations, not implemented by XOTcl, to the wrapped command. Here, it is tedious to write a wrapper for each of these methods. Instead you can use "unknown" to handle automatic forwarding. Consider you want to wrap TK commands like pack and replace XOTcl object names with their TK widget ID, so that you can use both IDs synonymously. You can rename the respective TK commands in the following way:

  foreach tkCommand {bell bind bindtags clipboard event 
    focus font grid image lower option pack place raise 
    selection send tk tkwait winfo wm} { 
    rename ::$tkCommand __tk_$tkCommand
    TkCommand ::$tkCommand
    ::$tkCommand set wrapped __tk_$tkCommand
  }

The XOTcl class handling the ID substitution for the TK command might look as follows:

  Class TkCommand -parameter wrapped
  TkCommand instproc unknown args {
      my instvar wrapped
      set args [Widget replaceWithWidgetIDs $args]
      # now call the command
      eval $wrapped $args
  }

References

[Zdun, Strembeck, Neumann 2007] U. Zdun, M. Strembeck, G. Neumann: Object-Based and Class-Based Composition of Transitive Mixins, Information and Software Technology, 49(8) 2007 .

[Neumann and Zdun 1999a] G. Neumann and U. Zdun. Filters as a language support for design patterns in object-oriented scripting languages. In Proceedings of COOTS'99, 5th Conference on Object-Oriented Technologies and Systems, San Diego, May 1999.

[Neumann and Zdun 1999b] G. Neumann and U. Zdun. Implementing object-specific design patterns using per-object mixins. In Proc. of NOSA`99, Second Nordic Workshop on Software Architecture, Ronneby, Sweden, August 1999.

[Neumann and Zdun 1999c] G. Neumann and U. Zdun. Enhancing object-based system composition through per-object mixins. In Proceedings of Asia-Pacific Software Engineering Conference (APSEC), Takamatsu, Japan, December 1999.

[Neumann and Zdun 2000a] G. Neumann and U. Zdun. XOTCL, an object-oriented scripting language. In Proceedings of Tcl2k: The 7th USENIX Tcl/Tk Conference, Austin, Texas, February 2000.

[Neumann and Zdun 2000b] G. Neumann and U. Zdun. Towards the Usage of Dynamic Object Aggregations as a Form of Composition In: Proceedings of Symposium of Applied Computing (SAC'00), Como, Italy, Mar 19-21, 2000.

[Ousterhout 1990] J. K. Ousterhout. Tcl: An embeddable command language. In Proc. of the 1990 Winter USENIX Conference, January 1990.

[Ousterhout 1998] J. K. Ousterhout. Scripting: Higher Level Programming for the 21st Century, IEEE Computer 31(3), March 1998.

[Wetherall and Lindblad 1995] D. Wetherall and C. J. Lindblad. Extending Tcl for Dynamic Object-Oriented Programming. Proc. of the Tcl/Tk Workshop '95, July 1995. library/xotcl/doc/xotcl-doc.css000066400000000000000000000014631242365656200170450ustar00rootroot00000000000000BODY { font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: normal; background-color : white; color: black; } tt { font-family: courier, monospace; } A { font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: 100; background-color : white; } A em { font-weight: 900; } pre.code { font-size: 90%; font-family: courier, monospace; PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-BOTTOM: 10px; PADDING-TOP: 10px; BORDER: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee; MARGIN-BOTTOM: 15px; } pre em { font-family: cursive; color: #888888; } pre tt { font-family: helvetica; font-weight: 900; } pre it { font-style: italic; color: green; } tt em { font-family: cursive; color: #888888; } table { font-size: 80%; } library/xotcl/library/000077500000000000000000000000001242365656200153325ustar00rootroot00000000000000library/xotcl/library/COPYRIGHT000066400000000000000000000050371242365656200166320ustar00rootroot00000000000000/* * Next Scripting Framework * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * This software is based upon MIT Object Tcl by David Wetherall and * Christopher J. Lindblad, that contains the following copyright * message: * * "Copyright 1993 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty." */ library/xotcl/library/comm/000077500000000000000000000000001242365656200162655ustar00rootroot00000000000000library/xotcl/library/comm/Access.xotcl000066400000000000000000001336041242365656200205500ustar00rootroot00000000000000# -*- tcl -*- $ package provide xotcl::comm::httpAccess 2.0 set httpAccessVersion [package present xotcl::comm::httpAccess] package require xotcl::comm::pcache package require xotcl::comm::mime package require xotcl::comm::connection package require xotcl::trace package require XOTcl 2.0 namespace eval ::xotcl::comm::httpAccess { namespace import ::xotcl::* variable os variable VERSION if {[catch {set os [exec uname -sr]}]} { if {[catch {set os [exec uname -a]}]} { set os unknownOS } } if {![info exists VERSION]} { set VERSION 1.0 } # assert is used only for invariants in the source proc assert {f r} { set got [eval $f] if {$got != $r} { puts stderr "assertion failed: \[$f\] == $r (got $got)" } } # resolve and checkUrl implement URL handling (primarily completion) proc checkUrl {url} { #puts stderr "checkUrl: <$url>" if {![regexp {^([^:]+:/)/([^/]+)(/.*)?$} $url _ scheme network path]} { regexp {^([^:]+:)(/.*)?$} $url _ scheme path } if {![info exists path]} { # no access scheme provided, try some heuristics... if {[regexp {^[./]} $url]} { # expand leading "." to pwd if {[regexp {^\.(.*)$} $url _ url]} { set url [pwd]$url } return file:$url } else { set url http://$url regsub {///$} $url // url return $url } } if {$path eq ""} {set path /} return [expr {[info exists network] ? "$scheme/$network$path" : "$scheme$path"}] } # resolving a relative url with respect to a base url (based on rfc 1808) proc resolve {rel base {childTagName ""}} { if {$base eq ""} { return [checkUrl $rel] } if {$rel eq ""} { return $base } if {[regexp {^[^:]+:/} $rel _]} { return [checkUrl $rel] } if {![regexp {^([^:]+:/)/([^/]+)(/.*)$} $base _ baseScheme baseNetwork basePath]} { regexp {^([^:]+:)(/.*)$} $base _ baseScheme basePath } elseif {[regexp {^[^:/]+:} $rel]} { return $rel } regexp {^(.*)\#.*$} $basePath _ basePath regexp {^(.*)[?].*$} $basePath _ basePath if {[regexp {^//([^/]+)/(.*)$} $rel _ relNetwork relPath]} { return $baseScheme/$relNetwork/$relPath } if {[info exists baseNetwork]} { append baseScheme /$baseNetwork } #puts stderr rel=<$rel> if {![string match "/*" $rel]} { #puts stderr rel<$rel>base<$basePath> if {[string match \#* $rel]} { set r $basePath$rel } elseif {![regsub {/([^/]*)$} $basePath /$rel r]} { set r /$rel } while {[regsub -all {/\./} $r / r]} {} regsub {/\.$} $r / r while {[regsub -all {/[^/.]+/\.\./} $r / r]} {} # remove leading /../ (netscapes navigator does this) while {[regsub {^/\.\./} $r / r]} {} set rel $r #puts stderr finalrel<$r> } #if {$childTagName ne ""} if {1} { upvar 1 $childTagName childTag catch {unset childTag} if {[regexp {^(.+)\#(.+)$} $rel _ rel childTag]} { #puts stderr childTag=$childTag,url=$baseScheme$rel. } } return $baseScheme$rel } assert {resolve "" http://mohegan/} http://mohegan/ assert {resolve http://mohegan/ ""} http://mohegan/ assert {resolve http://mohegan ""} http://mohegan/ assert {resolve http://mohegan/ http://nestroy} http://mohegan/ assert {resolve test.html http://127.0.0.1/} http://127.0.0.1/test.html assert {resolve test http://nestroy/} http://nestroy/test assert {resolve test file:/u/neumann/old} file:/u/neumann/test assert {resolve /test http://nestroy/} http://nestroy/test assert {resolve //mohegan/index.html http://nestroy/} http://mohegan/index.html assert {resolve //mohegan/index.html http://nestroy/} http://mohegan/index.html assert {resolve index.html http://nestroy/dir/} http://nestroy/dir/index.html assert {resolve ./index.html http://nestroy/dir/} http://nestroy/dir/index.html assert {resolve ././index.html http://nestroy/dir/} http://nestroy/dir/index.html assert {resolve ../index.html http://nestroy/dir/} http://nestroy/index.html assert {resolve ../../index.html http://nestroy/dir/} http://nestroy/index.html assert {resolve ../../../test file:/u/neumann/old} file:/test assert {resolve newdir/ http://nestroy/dir/} http://nestroy/dir/newdir/ assert {resolve /newdir/ http://nestroy/dir/} http://nestroy/newdir/ assert {resolve file:/u/neumann/ice.html ""} file:/u/neumann/ice.html assert {resolve \#label http://nestroy/h.html} http://nestroy/h.html assert {resolve mailto:user@host http://nestroy/h.html} mailto:user@host assert {resolve ../../../../mis2001/folien/081101.ppt http://wwwi.wu-wien.ac.at/Studium/Abschnitt_2/LVA_ws01/IT/index.html} http://wwwi.wu-wien.ac.at/mis2001/folien/081101.ppt ############################################################## # Every object of class Access informs the (possibly empty) list of # informObjects during its lifecycle with the following messages # # startCb: when the request for the object is created # and was classified and initialized # # notifyCb: when the type of the incming data is dertermined # (typically after parsing the http header) # # incCb: when new data is available # # endCb: when the request is finished sucessfully and the object # is going to be destroyed # # cancelCb: when the request is finished nonsucessfully and the object # is going to be destroyed # # All these messages receive the name of the Access object # as first parameter, incCb has two more arguments (totalsize # and currentsize) # caching: # 0 no caching # 1 volatile cache (does not check cache, keeps spool file for back button) # 2 persistent cache Class Access -parameter { {method GET} {blocking 0} {httpVersion 1.1} {headers {}} url query data contentType path caching sinkClass parentUrl } Access instproc informObject x { foreach i $x { my lappend informObjects $i } } Access instproc method x { my set method [string toupper $x] } Access instproc unknown {m args} { error "Method '$m' with args '$args' is unknown for [self class]" } Access proc checkRunning {} { foreach u [my array names running] { puts stderr "... running: $u" } puts stderr "... -----------" } Access proc createRequest args { #my showCall set informobject {} set allowJoin 1 set nargs {} foreach {a v} $args { switch -exact -- $a { -url {set url $v; lappend nargs $a $v} -informObject {set informobject $v; lappend nargs $a $v} -parentRequest {set parentRequest $v} -allowJoin {set allowJoin $v} default {lappend nargs $a $v} } } #if {[my array exists running]} {parray [self]::running} if {[my exists running($url)] && $allowJoin} { my showMsg "we can join running($url)=[my set running($url)]" # we can join the request. # requests are joined by adding the informobjects to # the existing request set token [my set running($url)] # delegation to a different request works only so easy # when loading to a file... $token informObject $informobject return $token } else { set token [my autoname ::req] if {[info exists parentRequest]} { set token ${parentRequest}$token } #my showMsg "TOKEN = $token $url" return [eval my create $token $nargs] } } Access instproc running {} { #my showCall [self class] set running([my url]) [self] } Access instproc stop {} { #showCall my instvar url if {[[self class] exists running($url)]} { #puts stderr "my unset [[self class] set running($url)] [self class] unset running($url) #if {[my array exists running]} { parray [self class]::running } } else { #puts stderr "not running($url)" #if {[my array exists running]} { parray [self class]::running } } } #Access instproc destroy args { # my showCall # next #} Access instproc init args { #my showCall my instvar method url if {![my exists informObjects]} { my set informObjects {} } next if {![my exists caching]} { if {$method eq "GET"} { set defaultCaching 2 } else { set defaultCaching 1 } my caching $defaultCaching } #my showVars set url [string trim $url] my initialize if {[my classify $url]} { #my showVars # now inform all interested objects that we have the object my doCallbacks startCb #my showVars blocking # trigger the transfer... (might be blocking or not) my $method if {![my exists finished]} { # the request is not finished if {[my blocking]} { #my showMsg "waiting" my vwait finished #my showMsg "waiting DONE" } } } } Access instproc getContent {} { [my set sink] content } ######################################### Access instproc timeout t { my set timeout [::after $t [self] timeoutOccured] } Access instproc timeoutOccured {} { #my showCall my unset timeout my abort "timeout exceeded" } Access instproc timeoutFinish {} { if {[my exists timeout]} { after cancel [my set timeout] my unset timeout } } ######################################### Access instproc initialize {} { #my showCall my set state 0 my set meta {} my set currentsize 0 my set totalsize 0 } Access instproc classify {url} { my instvar host path port method #my showVars caching if {[my caching] > 1 && [persistentCache isValidated $url]} { #puts stderr "*** cacheable && validated" #showVars #persistentCache dump set access CacheAccess } elseif {[regexp -nocase {^http://([^/:]+)(:([0-9]+))?(/.*)?$} $url \ _ host y givenPort path]} { if {$givenPort ne ""} {set port $givenPort } {set port 80} switch -exact $method { PROPFIND - PROPPATCH - COPY - MKCOL - MOVE - LOCK - UNLOCK { package require xotcl::comm::dav set access Dav } default {set access Http} } } elseif {[regexp -nocase {^https://([^/:]+)(:([0-9]+))?(/.*)$} $url \ _ host y givenPort path]} { if {$givenPort ne ""} {set port $givenPort } {set port 443} set access Https } elseif {[regexp -nocase {^file:(.*)$} $url _ path]} { set access File } elseif {[regexp -nocase {^ftp://([^/]+)/(.*)$} $url _ host path]} { package require xotcl::comm::ftp set access Ftp } elseif {[regexp -nocase {^imap://([^/]+)/(.*)$} $url _ host path]} { package require xotcl::comm::imap set access Imap } elseif {[regexp -nocase {^cmd:/(.*)$} $url _ path]} { set access xotcl::Cmd } elseif {[regexp -nocase {^ldap://([^/:]+)?(:([0-9]+))?(/.*)$} \ $url _ host y givenPort path]} { if {$givenPort ne ""} { set port $givenPort } my showMsg "*** ldap://<$host>:<$port>/<$path>" package require xotcl::comm::ldap set access Ldap } else { #my set state 0 my abort "Unsupported URL: '$url'" return 0 } my class $access #my showMsg "class of request = $access" return 1 } Access instproc revisit {} { my class ReAccess my initialize my [my set method] } ### dummy stubs for 'close' and 'GET' for error cases Access instproc close {} { } Access instproc GET {} { if {[my exists errormsg]} { ;# the error was already reportet my finish } else { my abort "unknown error" } } Access instproc headerDone {} { my instvar caching sink if {[my exists ignoreBody]} return if {[my exists sinkClass] && $caching>0 } { error "can´t set both sinkclass and caching" } switch $caching { 2 { set sink [CacheFileSink create [self]::cfs] #my showMsg "[self class] result goes to cache" $sink notifyCb [self] } 1 { set sink [CacheFileSink create [self]::cfs -persistent 0] #my showMsg "result goes to volatile cache" $sink notifyCb [self] } 0 { if {[my exists sinkClass]} { set sink [[my sinkClass] create [self]::s] } } } my doCallbacks notifyCb } Access instproc mkSpoolFile {{name ""}} { if {![my exists fileName]} { my set fileName [persistentCache newEntry [my url] [self] [my caching] $name] } } Access instproc block {} { my set block } Access instproc kill {} { #my showCall my set state -1; #interrupted my finish } Access instproc abort {msg} { #my showCall #my showVars my instvar state errormsg if {[catch {::printError "[self] ($state): $msg"} err]} { puts stderr "\n$err\nERROR [self] ($state): $msg\n" } #my set error [list $msg $::errorInfo $::errorCode] my caching 0 if {[my exists ignoreBody]} { my unset ignoreBody } set state -2 ;# error set errormsg $msg my finish } Access instproc finish {} { #my showCall my timeoutFinish my close #my showVars state ignoreBody # state is "interrupted" or "error" if {[my set state] < 0} { set action cancelCb set success 0 } else { set action endCb #set state ok set success 1 } if {[my exists ignoreBody]} { my stop #my set finished $success set cmd [my set ignoreBody] my unset ignoreBody #my showMsg "executing... <$cmd>" eval my $cmd } else { if {[my exists sink]} { [my set sink] $action [self] } #catch {after cancel $after} ;# ???? my doCallbacks $action my stop my set finished $success } } Access instproc eof {} { #my showCall #my showMsg "c [my set currentsize]== t [[self set totalsize]]" #my set state eof my finish } Access instproc doCallbacks {cb} { #my showCall if {[my exists ignoreBody]} { my showMsg "ignoring callback" } else { foreach obj [my set informObjects] { #my showMsg "*** $obj $cb [self]" #puts stderr "------------ calling: $obj $cb [self]" if {[catch {$obj $cb [self]} errormsg]} { puts stderr "--------------$cb:errormsg=$errormsg, \ errorInfo=$::errorInfo, \ errorCode=$::errorCode." } #puts stderr "------------ calling DONE: $obj $cb [self]" } } } Access instproc shutdown {} { #my showMsg "state=[my set state] > 3" if {[my set state] > 3} { my set mustDestroy 1 } else { #my showVars #my showStack #my showMsg "DESTROY !!!" if {[my set state] > -2} { my destroy } } } Class FileAccess -superclass Access FileAccess instproc initialize args { my caching 0 next } FileAccess instproc close {} { } FileAccess instproc block {} { my showTimeStart set S [open [my set fileName] r] fconfigure $S -translation binary set block [::read $S] ::close $S my showTimeEnd return $block } FileAccess instproc GET {} { my instvar path response totalsize currentsize \ fileName informObjects set fileName $path set totalsize [file size $path] set response "file 200 OK" my headerDone my set state 4 set currentsize $totalsize #my showVars informObjects foreach obj $informObjects { $obj incCb [self] $totalsize $currentsize } my eof } Class File -superclass FileAccess File instproc GET {} { my instvar path #puts stderr path=$path,exists=[file exists $path] if {![file exists $path]} { my abort "No such file '$path'" return } if {![my exists contentType]} { my contentType [Mime guessContentType $path] } my set sink [FileSink create [self]::fas -fileName $path] #puts stderr ****sink=$sink,[$sink info class] #puts stderr "*** before next ([self class])" next #puts stderr "*** after next ([self class])" } Class CacheAccess -superclass File CacheAccess instproc GET {} { my instvar url my path [persistentCache cacheFileName $url] my contentType [persistentCache contentType $url] my set meta [persistentCache meta $url] next } Class ReAccess -superclass File ReAccess instproc GET {} { my instvar fileName sink my set block "" my set currentsize 0 my caching 0 if {![info exists fileName]} { set fileName [$sink set fileName] } my set path $fileName next } Class Cmd -superclass Access Cmd instproc init args { if {![my exists caching]} { my caching 0 } next } Cmd instproc GET {} { my instvar path block totalsize currentsize \ response informObjects state if {[catch {set block [eval $path]} error]} { my contentType text/plain set block $error } else { my contentType text/html } set totalsize [string length $block] set response "cmd 200 OK" my headerDone my set state 4 set currentsize $totalsize foreach obj $informObjects { $obj incCb [self] $totalsize $currentsize #$obj endCb [self] } #set state eof my finish } Cmd instproc block args { my instvar block return $block } #Class NetAccess -superclass Access -parameter {host port} Class NetAccess -superclass Access NetAccess instproc initialize args { if {![my exists blocksize]} {my set blocksize 512} my set readMethod read next } NetAccess instproc read {} { #my instvar S blocksize block #set block [::read $S $blocksize] my instvar S block blocksize set block [::read $S $blocksize] } NetAccess instproc gzread {} { my instvar S Z blocksize block puts -nonewline $Z [::read $S $blocksize] set block [::read $Z] #puts stderr len=[string length $block],block=<$block> } NetAccess instproc gzopen {} { my instvar Z S readMethod requireModules {zipchan libzipchan.so} fconfigure $S -translation binary set Z [zipchan] set readMethod gzread } NetAccess instproc close {} { #my showMsg "**** close persistent=[my exists persistent]" if {![my exists persistent]} { foreach stream {S Z} { if {[my exists $stream]} { ::close [my set $stream] my unset $stream } } } my stop } NetAccess instproc hold {} { my instvar S $S hold } NetAccess instproc resume {} { my instvar S $S resume } NetAccess instproc readData {} { #my showCall if {[catch { #puts stderr "??????????????readMethod=[my set readMethod]" my [my set readMethod] my pushBlock } err]} { puts stderr ERR=$err my set block "" my abort $err } } NetAccess instproc pushBlock {} { #my showCall my instvar block if {[set n [string length $block]]} { my instvar currentsize totalsize informObjects sink #my showVars n currentsize totalsize incr currentsize $n if {$currentsize > $totalsize} { set totalsize $currentsize } #my showMsg "total=$totalsize current=$currentsize" if {[my exists ignoreBody]} { #puts stderr "ignoring: <$block>" } else { if {[info exists sink]} { $sink incCb [self] $totalsize $currentsize } foreach obj $informObjects { #my showMsg "call incbCb $totalsize $currentsize $obj [$obj info class]" $obj incCb [self] $totalsize $currentsize #my showMsg "done incbCb $totalsize $currentsize" } } } else { #my showVars n return [my eof] } } Class Http -superclass NetAccess ;### -parameter {query {httpVersion 1.0}} Http set proxyfilter httpProxyRequired Http set proxyhost {} Http set proxyport {} Http set accept */* if {[info exists argv0]} { Http set useragent "[file tail $argv0] httpAccess/$httpAccessVersion xotcl/$::xotcl::version ($os)" } Http proc proxyfilter {host phostv pportv} { my instvar proxyfilter proxyhost proxyport upvar \#1 $phostv phost $pportv pport switch -- $proxyfilter { httpProxyRequired { if {[string length $proxyhost]} { if {![string length $proxyport]} { set proxyport 8080 } set phost $proxyhost set pport $proxyport } } } } Http instproc initialize args { if {![my exists port]} {my set port 80} if {![my exists httpVersion]} {my set httpVersion 1.1} next #my showMsg "result queried from net" } Http instproc GET {} { #my showCall if {[my exists query]} { my instvar query caching my append url ?$query my append path ?$query if {$caching == 2} {set caching 1} my showVars caching $query } my open } Http instproc HEAD {} { my open } Http instproc POST {} { # we have query and data only for a uniform interface for # forms that cann pass the attribute value pairs always via # query. if {[my exists query]} { my data [my query] } my open } Http instproc PUT {} { my open } # Http1.1 Http instproc OPTIONS {} { my showCall my open } # Http1.1 Http instproc TRACE {} { my showCall } # Http1.1 Http instproc DELETE {} { #my showCall my open } Http instproc openConnection {host port reuse} { return [Connection make [self] $host $port $reuse _] } Http instproc open {} { #my showCall my instvar url S state host port path if {$state > 0} { puts stderr "... [self]:$proc ignoring request in state $state" return } Http proxyfilter $host phost pport if {[info exists phost] && [string length $phost]} { set usePort $pport set useHost $phost set path $url } else { set usePort $port set useHost $host } set S [my openConnection $useHost $usePort [expr {[my httpVersion]>1.0}]] if {[$S exists error]} { my abort [$S set error] return } set state 2 my running $S event writable [self] ask } Http instproc ask {} { my instvar url S state host port path \ method headers data caching $S event writable [self] {} if {[pwdManager checkAvailableCredentials $url credentials]} { eval lappend headers $credentials #my showMsg "*** new headers = <$headers>" } if {$caching==2 && [persistentCache ifModifiedHeader $url ifModified]} { eval lappend headers $ifModified #puts stderr "new headers = <$headers>" } # Send data in cr-lf format, but accept any line terminators $S translation {auto crlf} #my showMsg "'$method $path HTTP/[my httpVersion]' headers {$headers}" $S puts "$method $path HTTP/[my httpVersion]" if {[$S exists error]} { my abort "Connection refused by host '$host' port '$port'\n\ [$S set error]" return } $S puts "Accept: [Http set accept]" $S puts "Host: $host" $S puts "User-Agent: [Http set useragent]" foreach {tag value} $headers { regsub -all \[\n\r\] $value {} value set tag [string trim $tag] if {[string length $tag]} { #my showMsg "+++ <$tag: $value>" $S puts "$tag: $value" } } if {[my exists data]} { $S puts "Content-Length: [string length $data]" $S puts "Content-Type: [my contentType]" $S puts "" $S translation {auto binary} $S puts-nonewline $data } else { $S puts "" } $S flush if {[$S exists error]} { my instvar host port my abort "Connection refused by host '$host' port '$port'\n\ [$S set error]" } else { set state 3 $S event readable [self] headerStart } } Http instproc headerStart {} { #my showCall my instvar S response set n [$S gets response] #my showVars n response if {[$S exists error]} { my instvar host port my abort "Error on connection to host '$host' port '$port'\n\ [$S set error]" return } #my showMsg "n=$n, eof=[$S eof]" if {$n == -1 && ![$S eof]} { #my showMsg "******************************input pending, no full line" return } my instvar responseCode responseHttpVersion if {[regexp {^HTTP/([0-9.]+) +([0-9]+) *} $response _ \ responseHttpVersion responseCode]} { #my showMsg "response valid: '$response'" $S event readable [self] header } else { my instvar host port my abort "Unexpected response from $host:$port\n $n: '$response'" } } Http instproc header {} { my instvar S meta totalsize if {[$S gets line]} { #my showMsg "line=$line" if {[regexp -nocase {^content-type:(.+)$} $line _ type]} { my contentType [string trim $type] } elseif {[regexp -nocase {^content-length:(.+)$} $line _ length]} { set totalsize [string trim $length] } if {[regexp -nocase {^([^:]+): *(.+)$} $line _ key value]} { lappend meta [string tolower $key] $value } } else { my headerDone } } Http array set expectsBody \ {GET 1 HEAD 0 POST 1 PUT 0 DELETE 1 OPTIONS 0 TRACE 1} Http instproc headerDone {} { #my showVars meta my instvar S meta method responseCode responseHttpVersion [self class] instvar expectsBody set expectBody $expectsBody($method) array set v [my set meta] if {([info exists v(connection)] && $v(connection) eq "close") || \ $responseHttpVersion < 1.1} { $S makePersistent 0 } else { $S makePersistent 1 } switch $responseCode { 200 {} 204 { #set state empty my finish set block "" set expectBody 0 return } 301 - 302 { # the request is redirected to another server my set ignoreBody [list redirect $v(location)] # RFC2068 Note: When automatically redirecting a POST request after # receiving a 302 status code, some existing HTTP/1.0 user agents # will erroneously change it into a GET request. #my method GET my showMsg "redirect '[my url]' --> '$v(location)'" } 304 { ;# Not Modified, use cached version my set ignoreBody cacheAccess set expectBody 1 #my showMsg "result comes from cache" } 401 { my set ignoreBody \ [list resubmitAuthenticated $v(www-authenticate)] #my showMsg "resubmitAuthenticated $v(www-authenticate)" if {[my exists resubmitAuthenticated]} { if {[my set resubmitAuthenticated] > 5} { my abort "Too many wrong passwords given" } else { my incr resubmitAuthenticated } } else { my set resubmitAuthenticated 1 } set expectBody 1 } 404 { my instvar url #my showVars my set ignoreBody [list abort "File Not Found on Server '$url'"] set expectBody 1 } default { #my showVars responseCode } } next if {![my exists S]} {;# this request was already canceled return } if {[info exists v(transfer-encoding)]} { if {$v(transfer-encoding) == "chunked"} { $S translation {auto binary} my set state 4 $S event readable [self] readChunkedLength } else { my abort "Unexpected Transfer encoding '$v(transfer-encoding)'" } } else { # yahoo does not send a content length for a get request #if {$totalsize == 0 && ($responseCode > 300 || !$expectsBody($method) )} #my showVars method totalsize expectsBody($method) expectBody # the following is used currently for Actiweb-Agents: # the reponse of a PUTS contains a BODY! if {!$expectBody && [::info exists v(content-length)] && $v(content-length) > 0} { set expectBody 1 #my showMsg "setting expectBody 1" } if {!$expectBody} { #$S event readable [self] "" #set state eof my finish set block "" } else { ### To avoid CRLF problmes we set the translation for ### the body entity to binary $S translation binary my set state 4 $S event readable [self] readData } } } Http instproc cacheAccess {} { # there is an actual version of the document in the cache #showCall persistentCache validated [my url] #my close my class CacheAccess #my showMsg "result comes from cache [persistentCache cacheFileName $url]" my caching 0 ;# should be really: toCache 0 my GET } Http instproc redirect location { # the request is redirected to another server if {$location ne [my url] } { #my showVars my url $location my initialize my classify $location my [my set method] } } Http instproc resubmitAuthenticated headerField { #my showCall # the server requires authentification my instvar path method if {[pwdManager checkRequestedAuthentification $method $path $headerField \ type realm]} { my instvar url caching method headers array set v $headers #my showVars catch {unset v(Authorization)} set headers [array get v] pwdManager removePasswd $realm my close if {[pwdManager requireCredentials $realm $url]} { my initialize if {$caching == 2} {set caching 1} my $method } } else { my abort "unknown authentication method '$headerField'" } } Http instproc readChunkedLength {} { #my showCall my instvar S chunkLength totalsize set length [$S gets lengthString] if {$length > 0} { set chunkLength [expr 0x$lengthString] #my showVars lengthString chunkLength if {$chunkLength == 0} { $S event readable [self] readChunkedTrailer } else { incr totalsize $chunkLength $S translation {binary} $S event readable [self] readChunkedData } } } Http instproc readChunkedTrailer {} { #my showCall my instvar S state block set size [$S gets line] if {$size != 0} { showObj [self] my abort "expected trailer size 0, got size $size" } else { #set state eof my finish set block "" #showObj [self] } } Http instproc readChunkedData {} { #my showCall my instvar S block totalsize currentsize chunkLength set block [$S readSize $chunkLength] set received [string length $block] #my showVars block #my showVars currentsize totalsize chunkLength received if {$chunkLength == $received} { $S translation {auto binary} $S event readable [self] readChunkedLength } else { incr chunkLength -$received } my pushBlock } Http instproc readData {} { #my showCall my instvar S block totalsize currentsize set block [$S read] #puts stderr "????? bl=[string length $block]" if {[$S exists error]} { set block "" my abort [$S set error] return } my pushBlock #my showMsg "c [my set currentsize]== t [[self set totalsize]]" if {$currentsize == $totalsize && [my exists S] && [$S exists persistent]} { #my showMsg "PERSITENT, end of entity reached" #my set state eof my finish set block "" #showObj [self] } if {[my exists mustDestroy]} { #my showMsg "mustDestroy was set" my destroy } } Http instproc close {} { #my showCall if {[my exists S]} { [my set S] close #unset S } #next } Http instproc freeConnection {} { #showCall my instvar S #::puts stderr "[self] freeing $S !!!!!!!!!!!!!!!!!!!!!!!" unset S } #Access instproc makeZombie {} { # my showMsg "procs= [my info procs], i [Object info instcommands]" # my class Zombie #} #Class Zombie #Zombie instproc unknown {m args} { # my showMsg "+++ zombie method '$m' called" #} Class Https -superclass Http Https instproc initialize args { #my showCall package require tls if {![my exists port]} { my set port 443} next ### temporary solution, FIXME: ### zur zeit muss man den secure-webserver.xotcl und ### secure-webclient.xotcl jedenfalls aus dem xotcl/apps/xocomm-apps ### verzeichnis starten, da haben wir mal die cfg files in unserem ### baum.... source .ssl/ssl-configuration.xotcl ### } Https instproc openConnection {host port reuse} { set S [Connection make [self] $host $port $reuse reused] if {$reused} { #my showMsg "reusing $S" } else { my showMsg "make the socket ([$S socket]) secure ..." set cmd [list $S importSSL] foreach attr {cafile cadir certfile cipher command keyfile \ model request require ssl2 ssl3 tls1} { if {[sslClientConfig exists $attr]} { lappend cmd -$attr [sslClientConfig set $attr] } } my showMsg "the assembled command is... ´$cmd´" eval $cmd puts stderr "CHANNELS= [file channels]" } return $S } ####################################################################### Object pwdManager pwdManager proc requirePasswd {realm username password} { # used by ftp and imap my instvar user area upvar [self callinglevel] $password passwd if {[my exists pwd($realm)]} { #my showMsg "*** reusing password for $realm" set passwd [my set pwd($realm)] return 1 } else { userPwd user $username if {[userPwd show $realm user($realm) passwd]} { set area($realm/$username) $realm return 1 } } return 0 } pwdManager proc storePasswd {realm username password} { # used by ftp and imap my instvar pwd user set pwd($realm) $password set user($realm) $username } pwdManager proc removePasswd {realm} { if {[my exists pwd($realm)]} { my unset pwd($realm) my unset user($realm) } } pwdManager proc requireCredentials {realm url} { regexp {^(.*/)[^/]*$} $url _ path if {[my exists pwd($realm)]} { #my showMsg "*** register url=$url for ther realm=$realm" my set area($path) $realm return 1 } else { my instvar pwd user if {[info exists ::env(USER)]} { set USER $::env(USER) } elseif {[info exists ::env(USERNAME)]} { set USER $::env(USERNAME) } else { set USER unknown } userPwd user $USER if {[userPwd show $realm user($realm) pwd($realm)]} { #my showMsg "*** setting password for realm '$realm' url=$path" my set area($path) $realm return 1 } } return 0 } pwdManager proc encodeCredentials {authMethod realm} { #my showCall switch $authMethod { basic {set credential [my encodeCredentialsBasic $realm]} digest {set credential [my encodeCredentialsDigest $realm]} } return $credential } pwdManager proc encodeCredentialsBasic {realm} { my instvar pwd user return [list Authorization \ "Basic [base64 encode $user($realm):$pwd($realm)]"] } pwdManager proc encodeCredentialsDigest {realm} { #my showCall package require tcu; #FIXME: noch nicht in distribution my instvar digestResponseData my mkDigestResponseData $realm set digestResponse {} foreach {t v} [array get digestResponseData] { append digestResponse " $t = \"$v\"," } return [list Authorization "Digest [string trimright $digestResponse ,]"] } pwdManager proc mkDigestResponseData {realm} { #my showCall my instvar pwd user digestResponseData requestUri # RFC 2617 # credentials = "Digest" digest-response # digest-response = 1#( username | realm | nonce | digest-uri # | response | [ algorithm ] | [cnonce] | # [opaque] | [message-qop] | # [nonce-count] | [auth-param] ) # username = "username" "=" username-value # username-value = quoted-string # digest-uri = "uri" "=" digest-uri-value # digest-uri-value = request-uri ; As specified by HTTP/1.1 # message-qop = "qop" "=" qop-value # cnonce = "cnonce" "=" cnonce-value # cnonce-value = nonce-value # nonce-count = "nc" "=" nc-value # nc-value = 8LHEX # response = "response" "=" request-digest # request-digest = <"> 32LHEX <"> # LHEX = "0" | "1"| ...| "e" | "f" set digestResponseData(username) $user($realm) set digestResponseData(uri) $requestUri set digestResponseData(cnonce) "TEST" set digestResponseData(nc) 00000001 set digestResponseData(response) [my mkRequestDigest] #parray digestResponseData } pwdManager proc mkRequestDigest {} { # returns a string of 32 hex digits, which proves that the user # knows a password #A1 = unq(username-value) ":" unq(realm-value) ":" passwd my instvar digestResponseData pwd requestMethod requestUri append A1 $digestResponseData(username)\ : $digestResponseData(realm) : $pwd($digestResponseData(realm)) if {![my exists digestResponseData(qop)] || $digestResponseData(qop) eq "auth"} { append A2 $requestMethod : $requestUri } elseif {$digestResponseData(qop) eq "auth-int"} { #A2 = Method ":" digest-uri-value ":" H(entity-body) append A2 $requestMethod : $requestUri: "" } if {[my exists digestResponseData(qop)]} { append reqDigest $digestResponseData(nonce) : \ $digestResponseData(nc) : \ $digestResponseData(cnonce): \ $digestResponseData(qop) set reqDigest [md5 [md5 $A1]:$reqDigest:[md5 $A2]] } else { set reqDigest [md5 [md5 $A1]:$digestResponseData(nonce):[md5 $A2]] } return $reqDigest } pwdManager proc checkAvailableCredentials {url returnCredentials} { # we start a fresh request and check, whether we have sent for this url # (directory) already some credentials in an earlier reqhest. my instvar authMethod regexp {^(.*/)[^/]*$} $url _ path if {[my exists area($path)]} { set realm [my set area($path)] upvar [self callinglevel] $returnCredentials credentials #my showMsg "*** adding credentials for realm=$realm and $path" set credentials [my encodeCredentials $authMethod $realm] return 1 } return 0 } pwdManager proc checkRequestedAuthentification {reqMethod path headerField typeVar realmVar} { # check for which realm with which authentification method the # server wants an authentification #my showCall upvar [self callinglevel] $typeVar type $realmVar realm my instvar authMethod digestResponseData requestUri requestMethod set requestUri $path set requestMethod $reqMethod if {[regexp {^Basic realm="(.*)"$} $headerField _ realm]} { set type basic;# FD: musste da lassen wg. call by reference set authMethod $type return 1 } elseif {[regsub {^Digest } $headerField _ headerField]} { set type digest ;# FD: musste da lassen wg. call by reference set authMethod $type foreach pair [split $headerField ,] { if {[regexp {^(.*) *= *(".*")$} $pair _ n v]} { set digestResponseData([string trim $n]) [string trim [string trim $v] \"] } } set realm $digestResponseData(realm) return 1 } return 0 } ####################################################################### # test classes Class Sink Sink instproc startCb {r} { my set contentLength 0 next } Sink instproc notifyCb {r} { next } Sink instproc incCb {r t c} { my set contentLength $t next } Sink instproc endCb {r} { next #showCall } Sink instproc cancelCb {r} { next #showCall } Sink instproc content {} { next #showCall } Sink instproc contentLength {} { my set contentLength #showCall } Class TimeSink -superclass Sink TimeSink instproc startCb {r} { my set startTime [clock clicks] next } TimeSink instproc notifyCb {r} { my set notifyTime [clock clicks] next } TimeSink instproc endCb {r} { my set endTime [clock clicks] next my reportTimes } TimeSink instproc cancelCb {r} { my set endTime [clock clicks] next } TimeSink instproc reportTimes {} { my instvar startTime endTime notifyTime set bytes [my contentLength] set grossSecs [expr {($endTime-$startTime)/1000000.0}] set grossKbps [expr {($bytes/1000.0)/$grossSecs}] set netSecs [expr {($endTime-$notifyTime)/1000000.0}] set netKbps [expr {($bytes/1000.0)/$netSecs}] #if {[catch {set netKbps [expr {($bytes/1000.0)/$netSecs}]}]} { # set netKbps 0 #} set headSecs [expr {$grossSecs-$netSecs}] foreach v {grossSecs grossKbps netSecs netKbps headSecs} { set $v [format %.2f [set $v]] } my showMsg "got $bytes bytes in $grossSecs ($headSecs+$netSecs) seconds,\ $grossKbps ($netKbps) KB/S" } Class ParallelSink -superclass Sink -parameter { {httpVersion 1.1} {sinkClass TimeSink} {maxsimultaneous 20} } ParallelSink instproc init args { next my set running 1 my set numrequests 0 my set sumbytes 0 my set blocked {} } ParallelSink instproc startCb r { #my showCall my incr running my incr numrequests #puts stderr "... running++ [my set running]" } ParallelSink instproc endCb r { #my showCall my incr sumbytes [$r set currentsize] my done $r } ParallelSink instproc cancelCb r { #my showCall my done $r } ParallelSink instproc done r { #my showCall my instvar blocked $r shutdown my incr running -1 #puts stderr "... running-- [my set running] [llength [Http info instances]]" #puts stderr [Http info instances] #foreach i [Http info instances] {puts stderr "\t$i: [$i set method] [$i set url]"} #puts stderr RUNNING=[my set running] if {[llength $blocked] > 0} { set newreq [lindex $blocked 0] set blocked [lrange $blocked 1 end] my scheduleRequest [lindex $newreq 0] [lindex $newreq 1] [lindex $newreq 2] } elseif {[my set running] < 1} { my set done 1 } } ParallelSink instproc scheduleRequest {method url {parentUrl ""}} { my instvar requests blocked running maxsimultaneous if {$running > $maxsimultaneous} { lappend blocked [list $method $url $parentUrl] } else { set cmd [list Access createRequest -url $url \ -sinkClass [my sinkClass] \ -informObject [self] \ -method $method \ -timeout 25000 \ -caching 0 -allowJoin 0 -httpVersion [my httpVersion]] if {$parentUrl ne ""} { lappend cmd -parentUrl $parentUrl } set r [eval $cmd] } } ParallelSink instproc requests {urls} { my showTimeStart foreach url $urls { my scheduleRequest GET $url } my wait my showTimeEnd } ParallelSink instproc wait {} { my instvar running if {$running > 0} { set savedValue $running #my showMsg ".......... waiting for initially $running requests" if {[catch {my vwait done} err]} { my showMsg "vwait returned: $err " } #my showMsg "$savedValue requests FINISHED " } } Class MemorySink -superclass Sink MemorySink instproc incCb {r t c} { my append d [$r block] next } MemorySink instproc content {} { return [my set d] } MemorySink instproc contentLength {} { if {[my exists d]} { return [string length [my set d]] } else { return 0 } } Class FileSink -superclass Sink -parameter fileName ### write methods #FileSink instproc startCb {r} { # next #} FileSink instproc notifyCb {r} { #my showVars next my instvar file fileName if {[info exists fileName]} { set file [::open $fileName w] fconfigure $file -translation binary } else { # we have no filename; we assume the sink must be a dummy sink # that deletgates its work to some other FileSink my class ShadowFileSink my notifyCb $r } } FileSink instproc incCb {r t c} { next if {[my exists file]} { if {$r == "req0"} { puts stderr "*******************************************************" puts stderr [$r block] puts stderr "*******************************************************" } puts -nonewline [my set file] [$r block] } } FileSink instproc endCb {r} { #my showCall next my close } FileSink instproc cancelCb {r} { next my close } FileSink instproc close {} { if {[my exists file]} { ::close [my set file] my unset file } } ### read methods FileSink instproc content {} { next my instvar file fileName set file [::open $fileName r] fconfigure $file -translation binary set d [read [my set file]] my close return $d } FileSink instproc contentLength {} { next if {[my exists fileName]} { return [file size [my set fileName]] } else { return 0 } } Class ShadowFileSink -superclass Sink ShadowFileSink instproc notifyCb {r} { next my set token $r } ShadowFileSink instproc content {} { my instvar token next return [[$token set sink] content] } ShadowFileSink instproc contentLength {} { my instvar token next return [[$token set sink] contentLength] } Class CacheFileSink -superclass FileSink -parameter {{persistent 1}} CacheFileSink instproc notifyCb req { #my showCall if {![my exists fileName]} { my instvar persistent set url [$req set url] my set fileName [persistentCache newEntry $url $req $persistent ""] } # it is important to execute next after setting the fileName... next } CacheFileSink instproc endCb req { #my showCall my instvar persistent next if {$persistent} { persistentCache entryDone [$req set url] } } CacheFileSink instproc cancelCb req { next if {[my exists fileName]} { file delete [my set fileName] my unset fileName } } CacheFileSink instproc destroy {} { #my showCall if {[my exists fileName] && ![my set persistent]} { #my showMsg "file delete $fileName" file delete [my set fileName] my unset fileName } next } #=========================================================== Class SimpleRequest -parameter { {caching 0} {useFileSink 0} {blocking 1} {timing 0} url fileName timeout httpVersion method headers data query contentType informObject } SimpleRequest instproc fileName x { my set fileName $x my set useFileSink 1 } SimpleRequest instproc init args { my instvar useFileSink fileName sink caching token #my showMsg "Starting Request" next if {[info exists fileName]} { set sink [FileSink create [self]::sink -fileName $fileName] } elseif {$useFileSink || $caching > 0} { set sink [FileSink create [self]::sink] } else { set sink [MemorySink create [self]::sink] } #my showMsg "class of sink = [$sink info class]" if {[my set timing]} { $sink mixin TimeSink } set cmd [list Access createRequest \ -url [my url] \ -informObject $sink \ -blocking [my blocking] \ -caching $caching] foreach optionalParameter { timeout httpVersion method headers data query contentType informObject } { if {[my exists $optionalParameter]} { lappend cmd -$optionalParameter [my set $optionalParameter] } } #my showMsg "cmd=$cmd" set token [eval $cmd] #if {[my success]} { # $sink reportTimes # #puts stderr <[$sink content]> #} } SimpleRequest instproc success {} { if {[my exists token]} { return [expr {[[my set token] set finished] == 1}] } return 0 } SimpleRequest instproc destroy {} { if {[my exists token]} { [my set token] destroy } next } SimpleRequest instproc getContent {} { [my set sink] content } SimpleRequest instproc getContentLength {} { [my set sink] contentLength } #SimpleRequest instproc destroy args { next } ####################################################################### namespace export \ Access FileAccess File CacheAccess ReAccess \ Cmd NetAccess Http Https Sink TimeSink \ ParallelSink MemorySink FileSink \ ShadowFileSink CacheFileSink SimpleRequest } namespace import ::xotcl::comm::httpAccess::* library/xotcl/library/comm/COPYRIGHT000066400000000000000000000050371242365656200175650ustar00rootroot00000000000000/* * Next Scripting Framework * * Copyright (C) 1999-2014 Gustaf Neumann (a) (b) * Copyright (C) 1999-2007 Uwe Zdun (a) (b) * Copyright (C) 2007-2008 Martin Matuska (b) * Copyright (C) 2010-2014 Stefan Sobernig (b) * * * (a) University of Essen * Specification of Software Systems * Altendorferstrasse 97-101 * D-45143 Essen, Germany * * (b) Vienna University of Economics and Business * Institute of Information Systems and New Media * A-1090, Augasse 2-6 * Vienna, Austria * * This work is licensed under the MIT License * http://www.opensource.org/licenses/MIT * * Copyright: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * This software is based upon MIT Object Tcl by David Wetherall and * Christopher J. Lindblad, that contains the following copyright * message: * * "Copyright 1993 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this * software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notice appear in * all copies and that both that copyright notice and this * permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty." */ library/xotcl/library/comm/Connection.xotcl000066400000000000000000000166601242365656200214500ustar00rootroot00000000000000# -*- tcl -*- $ package provide xotcl::comm::connection 2.0 package require XOTcl 2.0 namespace eval ::xotcl::comm::connection { namespace import ::xotcl::* Class Connection -parameter {host port req socket handle} Connection proc make {r host port reuse reusedVar} { #my showCall my instvar openConnections upvar [self callinglevel] $reusedVar reused if {$reuse} { set handle $host:$port-[$r set blocking] #if {[array exists openConnections]} {parray openConnections} if {![info exists openConnections($handle)]} { # there is no persistent connection, we create a new one set reused 0 set openConnections($handle) \ [Connection new -host $host -port $port -req $r -handle $handle] #my showMsg "$openConnections($handle) CONNECTION add for $handle added" } else { # there is a persistent connection set reused 1 set c $openConnections($handle) $c instvar req #::puts stderr "$c CONNECTION reuse for $handle ($c) new req=$r" if {[info exists req]} { # the persistent connection is active with some request $req #::puts stderr "$c CONNECTION req $req already active" } else { # the persistent connection is currently not active $c set req $r } } return $openConnections($handle) } else { set reused 0 return [Connection new -host $host -port $port -req $r] } } Connection proc removeHandle handle { #my showVars #puts stderr "***************** unsetting $handle ***************" if {[my exists openConnections($handle)]} { my unset openConnections($handle) } } Connection instproc init args { ;# the constructor creates the socket my set blocked {} next if {[my exists socket]} { my set keepOpen 1 } else { my set keepOpen 0 if {[catch {my socket [socket -async [my host] [my port]]} msg]} { my set error $msg return } } ::fconfigure [my socket] -blocking false -buffersize 16384 } #Connection instproc STATUS {ctx} { # my instvar socket # ::puts stderr "*** $ctx: $socket blocking=[::fconfigure $socket -blocking]" #} Connection instproc destroy {} { ;# the destructor closes the socket #my showCall if {[my exists handle]} { #my showVars handle # the connection was created via make [self class] removeHandle [my set handle] #::puts stderr "my CONNECTION close and destroy [my set handle]" } else { #::puts stderr "my CONNECTION close and destroy" } # in cases of errors we might not have a socket yet if {[my exists socket]} { close [my set socket] } next } Connection instproc translation {translation} { #showCall ::fconfigure [my socket] -translation $translation } Connection instproc importSSL args { #my showCall package require tls eval tls::import [my socket] $args } Connection instproc fconfigure args { #my showCall eval ::fconfigure [my socket] $args } Connection instproc event {type r method} { #my showCall my instvar req blocked # is the request in the argument list the currently active request? if {[info exists req] && $r == $req} { # a request can overwrite its active request if {$method eq ""} { ::fileevent [my socket] $type "" #my showMsg "CONNECTION clear for [my socket]" } else { #my showMsg "CONNECTION register for [my socket]" ::fileevent [my socket] $type [list $r $method] } } else { #my showMsg "event BLOCKING current request=$req, new=$r $method" #my showMsg "event BLOCKING rd=[::fileevent [my socket] readable]" #my showMsg "event BLOCKING wr=[::fileevent [my socket] writable]" #my showMsg "event BLOCKING bl=$blocked" ::lappend blocked $r $type $method } } Connection instproc hold {} { my set continueCmd [list ::fileevent [my socket] readable \ [::fileevent [my socket] readable]] ::fileevent $socket readable {} #my showVars continueCmd } Connection instproc resume {} { #my showCall if {[my exists continueCmd]} { eval [my set continueCmd] my unset continueCmd } } Connection instproc puts {string} { #my showCall if {[catch {::puts [my socket] $string} msg]} { ::puts stderr message=$msg } } Connection instproc puts-nonewline {string} { #my showCall if {[catch {::puts -nonewline [my socket] $string} msg]} { ::puts stderr message=$msg } } Connection instproc gets {var} { #my showCall upvar [self callinglevel] $var result if {[catch {set n [::gets [my socket] result]} msg]} { my set error $msg #my showMsg "CONNECTION error: $msg" return 0 } #my showMsg "n=$n, result=<$result>" return $n } Connection instproc read {} { # my showCall my instvar socket # Prior to the fix for Tcl bug #3401422, there was a regression # in Tcl 8.6 beta versions, causing a previously non-blocking # socket created using [socket -async] to return to a blocking # state. This required a manual overwrite (see below). Starting # with the 8.6 release candidates, this is obsolete. # # ::fconfigure $socket -blocking 0 if {[catch {set result [::read $socket [::fconfigure $socket -buffersize]]} msg]} { my set error $msg return "" } return $result } Connection instproc readSize {length} { if {[catch {set result [::read [my socket] $length]} msg]} { my set error $msg return 0 } return $result } Connection instproc flush {} { #my showCall if {[catch {::flush [my socket]} msg]} { my set error $msg } } Connection instproc eof {} { #my showCall if {[my exists error]} { return 1 } else { return [::eof [my socket]] } } Connection instproc close {} { #my showCall my instvar req socket blocked if {![info exists socket]} return ;# error during connection open ::fileevent $socket readable "" ::fileevent $socket writable "" $req freeConnection if {[my exists persistent]} { my flush #::puts stderr "[self] PERSISTENT CONNECTION wanna close" if {$blocked eq ""} { ::fileevent $socket readable [list [self] destroy] unset req } else { #my showVars blocked set req [lindex $blocked 0] set type [lindex $blocked 1] set method [lindex $blocked 2] set blocked [lrange $blocked 3 end] #my showMsg "in persistent connection unblock $type [list $req $method]" ::fileevent $socket $type [list $req $method] } } else { #my showMsg "in nonpersistent connection blocked=$blocked" if {$blocked ne ""} { set req [lindex $blocked 0] set type [lindex $blocked 1] set method [lindex $blocked 2] set nblocked [lrange $blocked 3 end] close $socket unset socket if {[my exists handle]} { [self class] removeHandle [my set handle] } if {[my exists error]} { #my showMsg "UNSETTING ERROR -----------" my unset error } my init set blocked $nblocked ::fileevent $socket $type [list $req $method] #my showMsg "REANIMATE $socket $type [list $req $method]" #my showVars } else { #my showMsg "Nothing blocked: readable=[::fileevent $socket readable]" my destroy } } } Connection instproc makePersistent {p} { if {$p} { my set persistent 1 } else { if {[my exists persistent]} { my unset persistent #my showMsg "no longer persistent" } } } namespace export Connection } namespace import ::xotcl::comm::connection::* if {[info command bgerror] eq ""} { proc bgerror {msg} { puts stderr "******* bgerror $msg $::errorInfo*****"} } library/xotcl/library/comm/Dav.xotcl000066400000000000000000000105561242365656200200610ustar00rootroot00000000000000# -*- Tcl -*- $ package provide xotcl::comm::dav 2.0 package require XOTcl 2.0 namespace eval ::xotcl::comm::dav { package require xotcl::comm::httpAccess namespace import ::xotcl::* Class Dav -superclass Http Dav instproc initialize args { my instvar contentType #showCall set contentType text/xml next } Dav instproc PROPFIND {} { #showCall # extra dav headers # Depth: ("0" | "1" | "infinity") [infinity is the default] # body is a propfind XML-Element # # # # # this should be set by the clients # # # # my open } Dav instproc PROPPATCH {} { #showCall # body is a propertyupdate XML-Element # # # # set xmlReqBody($method) " # # # # # # # " my open } Dav instproc MKCOL {} { #showCall # invoked without a request body (may contain a message body?) my open } Dav instproc GET {} { #showCall # invoked without a request body and without extra header # back to HTTP class next } Dav instproc HEAD {} { #showCall # invoked without a request bodyand without extra header # back to HTTP class next } Dav instproc POST {} { #showCall # the same as in RFC2068 # back to HTTP class next } Dav instproc DELETE {} { #showCall # extra dav headers # Depth: ("0" | "1" | "infinity") # invoked without a request body my open } Dav instproc PUT {} { #showCall # PUT for Non-Collection Resources --> RFC2068 # PUT for Collections --> MKCOL # next } Dav instproc COPY {} { #showCall # extra dav headers # If: [see 9.4 WebDAV] # Destination: [see RFC2396 for the definition of absolutURI] # Depth: ("0" | "1" | "infinity") # Overwrite: ("T" | "F") # body is a propertybehavior XML-Element # # # # my open } Dav instproc MOVE {} { #showCall # extra dav headers # If: [see 9.4 WebDAV] # Destination: [see RFC2396 for the definition of absolutURI] # Depth: "infinity" [see 8.9.2] # Overwrite: ("T" | "F") # body is a propertybehavior XML-Element # see COPY my open } Dav instproc LOCK {} { #showCall # extra dav headers # If: [see 9.4 WebDAV] # Destination: [see RFC2396 for the definition of absolutURI] # Depth: ("0" | "1" | "infinity") # Timeout: [see 9.8 WebDAV] # Authorization: (defined in HTTP1.1 in 14.8) # body is a lockinfo XML-Element # # # # # # # my open } # The Lock-Token request header is used with the UNLOCK method to # identify the lock to be removed. Dav instproc UNLOCK {} { my instvar headers #showCall # extra dav headers # Lock-Token: [see 8.11 in WebDAV] # invoked without a request body my open } #--------------------- # Utility # #--------------------- #? Object xmlReqBodyManager xmlReqBodyManager proc requireXmlReqBody {request} { } #? Object davHeaderManager davHeaderManager proc requireDavHeader {request} { } #LOCK /DAV/welcome.html HTTP/1.1 #Host: wawog #Connection: close namespace export Dav \ xmlReqBodyManager davHeaderManager } namespace import ::xotcl::comm::dav::* library/xotcl/library/comm/Ftp.xotcl000066400000000000000000000110551242365656200200730ustar00rootroot00000000000000# -*- Tcl -*- $ package provide xotcl::comm::ftp 2.0 package require xotcl::comm::httpAccess package require XOTcl 2.0 namespace eval ::xotcl::comm::ftp { namespace import ::xotcl::* Class Ftp -superclass NetAccess -parameter {user passwd} Ftp instproc initialize args { #my showCall my instvar port caching user passwd loginMsg resp blocksize set port 21 set blocksize 1024 set caching 0 set user ftp set passwd cineast@ set loginMsg {} set resp(connect) {220 provideUser} set resp(provideUser) {331 providePasswd} set resp(providePasswd) {230 loginFinished} set resp(loginFinished) {227 pasv} set resp(pasv) {200 type} set resp(type-list) {150 list} set resp(type-retr) {150 retr 550 retry-retrieve} set resp(transfer) {226 transferDone} next } Ftp instproc err {state reply} { my abort "Error in $state: $reply" } Ftp instproc queryServer {query state} { my instvar S puts $S $query flush $S fileevent $S readable [::list [self] response $state] } Ftp instproc response {state} { #my showCall my instvar S code msg set reply [gets $S] #my showVars reply if {[regexp {^([0-9]+)[-](.*)$} $reply _ code msg]} { fileevent $S readable [::list [self] responseMulti $state] } else { regexp {^([0-9]+) (.*)$} $reply _ code msg my responseEnd $state } } Ftp instproc responseMulti {state} { # multi line response my instvar S code msg set m [gets $S] if {[regexp "^$code " $m]} { my responseEnd $state } else { # try to strip code and dash regexp "^$code-(.*)\$" $m _ m append msg \n$m } } Ftp instproc responseEnd {state} { my instvar S code msg resp fileevent $S readable {} #puts stderr "code=$code, msg=<$msg>" foreach {c newState} $resp($state) { if {$c == $code} { return [my $newState] } } my err $state "expected=$resp($state), got $code $msg" } Ftp instproc GET {} { my instvar S host port url regexp {^(.*):([0-9]+)$} $host _ host port my running # rb running my $url ;# ??? # proxy ? set S [socket -async $host $port] fconfigure $S -blocking false -translation {auto crlf} fileevent $S readable [::list [self] response connect] } Ftp instproc provideUser {} { my instvar user msg loginMsg set loginMsg $msg my queryServer "USER $user" provideUser } Ftp instproc providePasswd {} { my instvar passwd # if {[pwdManager requirePasswd "Ftp $user\@$host" $user password]} { # my queryServer "PASS $password" providePasswd # } my queryServer "PASS $passwd" providePasswd } Ftp instproc loginFinished {} { my instvar msg loginMsg append loginMsg \n$msg my queryServer "PASV" loginFinished } Ftp instproc pasv {} { my instvar S D msg set d {([0-9]+)} if {[regexp "\[(]$d,$d,$d,$d,$d,$d" $msg _ 1 2 3 4 p1 p2]} { if {[catch {set D [socket -async $1.$2.$3.$4 [expr {$p1*256 + $p2}]]} err ]} { return [my err $proc $err] } fconfigure $D -blocking no -translation binary } else { return [my err $proc $msg] } my queryServer "TYPE I" pasv } Ftp instproc type {} { my instvar path if {$path=={}} { my queryServer "LIST" type-list } elseif {[regexp /$ $path]} { my queryServer "LIST $path" type-list } else { my queryServer "RETR $path" type-retr } } Ftp instproc retry-retrieve {} { my instvar path url append url / my queryServer "LIST $path/" type-list } Ftp instproc list {} { my instvar S D contentType set contentType text/dirlist my headerDone fileevent $S readable [::list [self] response transfer] fileevent $D readable [::list [self] readData] } Ftp instproc read {} { # the method read is called by the more general method readData my instvar D block blocksize if {[::eof $D]} { set block "" close $D unset D } else { #puts stderr blocksize=$blocksize set block [::read $D $blocksize] #puts stderr read:[string length $block]bytes } } Ftp instproc transferDone {} { my instvar D S if {[info exists D]} { fileevent $S readable {} set block "" close $D unset D } my finish } Ftp instproc retr {} { my instvar S D msg totalsize contentType path regexp {[(]([0-9]+)[ ]+[Bb]ytes} $msg _ totalsize set contentType [Mime guessContentType $path] my headerDone if {[info exists S]} { # file dialog was not canceled fileevent $S readable [::list [self] response transfer] fileevent $D readable [::list [self] readData] fconfigure $D -translation binary } } namespace export Ftp } namespace import ::xotcl::comm::ftp::* library/xotcl/library/comm/Httpd.xotcl000066400000000000000000000672351242365656200204400ustar00rootroot00000000000000# -*- Tcl -*- $ # # The XOTcl class Httpd implements an HTTP/1.0 and HTTP/1.1 server with # basic functionality. # # Gustaf Neumann (neumann@wu-wien.ac.at) package provide xotcl::comm::httpd 2.0 set VERSION [package present xotcl::comm::httpd] package require XOTcl 2.0 #package require xotcl::comm::httpAccess package require xotcl::comm::connection package require xotcl::trace package require xotcl::comm::mime namespace eval ::xotcl::comm::httpd { namespace import -force ::xotcl::* Class Httpd -parameter { {port 80} ipaddr {root ./} {logdir "[set ::xotcl::logdir]"} {httpdWrk Httpd::Wrk} {redirects [list]} {workerTimeout 10000} } Httpd proc Date seconds {clock format $seconds -format {%a, %d %b %Y %T GMT} -gmt true} Httpd instproc checkRoot {} { my instvar root set root [string trimright $root /] if {![file isdir $root]} { puts stderr "Warning: create root directory '$root'" file mkdir $root } # make directory absolute set currentdir [pwd] cd $root set root [pwd] #puts stderr "[self] root=$root" cd $currentdir } proc ! string { set f [open [::xotcl::tmpdir]log w+]; puts $f "[clock format [clock seconds]] $string" close $f} Httpd instproc init args { my instvar port logdir logfile redirects if {![my exists workerMixins]} { my set workerMixins {} #puts stderr "resetting workermixins of [self]" } next set proto [string trim [namespace tail [my info class]] :d] puts stderr "Starting XOTcl [string toupper $proto] server $::VERSION\ [string tolower $proto]://[info hostname]:$port/" # Start a server by listening on the port if {[my exists ipaddr]} {set ip "-myaddr [my set ipaddr]"} {set ip ""} my set listen [eval [list socket -server [list [self] accept]] $ip $port] #my set listen [socket -server [list [self] accept] $port] my checkRoot if {![file isdir $logdir]} {file mkdir $logdir} set logfile [open $logdir/serverlog-$port a+] my array set requiresBody \ {GET 0 HEAD 0 POST 1 PUT 1 DELETE 0 OPTIONS 0 TRACE 0} } Httpd instproc destroy {} { # destructor catch {close [my set listen]} catch {close [my set logfile]} next } Httpd instproc accept {socket ipaddr port} { # Accept a new connection and set up a handler #puts stderr "using workermixins of [self] {[my set workerMixins]}" [my set httpdWrk] new -childof [self] -socket $socket -ipaddr $ipaddr \ -port $port -mixin [my set workerMixins] } Httpd instproc redirect list { foreach {pattern hostport} $list { my lappend redirects $pattern $hostport } } Class Httpd::Wrk -parameter {socket port ipaddr} Httpd::Wrk array set codes { 200 {Data follows} 201 {Created} 204 {No Content} 302 {Moved Temporarily} 304 {Not Modified} 400 {Bad Request} 401 {Unauthorized} 402 {Payment Required} 403 {Forbidden} 404 {Not Found} 405 {Method Not Allowed} 406 {Not Acceptable} 408 {Request Timeout} 411 {Length Required} 500 {Internal Server Error} 503 {Service Unavailable} 504 {Service Temporarily Unavailable} } Httpd::Wrk instproc formData {} {my set formData} Httpd::Wrk instproc init args { # Constructor my instvar socket port ipaddr my set formData [list] my set replyHeaderFields [list] next my makeConnection $socket my log Connect "$ipaddr $port" [self]::connection translation {auto crlf} [self]::connection event readable [self] firstLine } Httpd::Wrk instproc makeConnection {socket} { Connection create [self]::connection -socket $socket -req [self] } Httpd::Wrk instproc close {} { # logical close of a single request #my showCall my instvar version timeout meta set eof [[self]::connection eof] if {$version > 1.0 && !$eof} { #my showMsg "!EOF in http/$version" [self]::connection flush set timeout [after [[my info parent] workerTimeout] [self] destroy] ### reset parameters, worker will be potentially reused if {[array exists meta]} { unset meta array set meta {} } unset version if {[my exists user]} { my unset user my unset realm } foreach c [my set formData] { $c destroy } my set replyHeaderFields [list] my set formData {} #my showVars [self]::connection translation {auto crlf} [self]::connection event readable [self] firstLine } elseif {$eof} { #my showMsg "Destroy in http/$version" # the client side has closed the connection my destroy } else { #my showMsg "!EOF in http/$version ???" # we close the conneciton actively (e.g. forced by an error) [self]::connection flush #puts stderr "DESTROY----this line should never show up" my destroy } } Httpd::Wrk instproc destroy {} { #my showCall if {[my isobject [self]::connection]} { [self]::connection close } next } Httpd::Wrk instproc freeConnection {} { } Httpd::Wrk instproc firstLine {} { # Read the first line of the request #my showCall my instvar method resourceName hasFormData query fileName \ version timeout if {[info exists timeout]} { after cancel $timeout unset timeout } my lappend replyHeaderFields Date [Httpd Date [clock seconds]] set n [[self]::connection gets firstLine] if {$n > 0} { #::puts stderr "[self] firstline=<$firstLine>" # parse request line, ignore HTTP version for now if {[regexp {^(POST|GET|PUT|HEAD|OPTIONS) ([^?]+)(\??)([^ ]*) *HTTP/(.*)$} \ $firstLine _ method resourceName hasFormData query version]} { set resourceName [string trimright [string trimleft $resourceName ./] " "] # construct filename [my info parent] instvar root set fileName $root/[url decodeName $resourceName] #puts stderr ---[encoding convertfrom utf-8 $fileName]---- set fileName [encoding convertfrom utf-8 $fileName] # my decode-formData $query my log Query $firstLine if {[my exists forceVersion1.0]} { set version 1.0 } [self]::connection makePersistent [expr {$version > 1.0}] [self]::connection event readable [self] header } else { set version 1.0 set resourceName ??? set method ??? my log Error "bad first line:$firstLine" my replyCode 400 my replyErrorMsg } } elseif {![[self]::connection eof]} { #my showMsg "+++ not completed EOF=[[self]::connection eof]" } else { set version 1.0 #my showMsg "+++ n=negative ($n) EOF=[[self]::connection eof] version set to 1.0" my close } } Httpd::Wrk instproc header {} { # Read the header #my showCall my instvar method data if {[[self]::connection gets line] > 0} { #puts stderr line=$line if {[regexp -nocase {^([^:]+): *(.+)$} $line _ key value]} { my set meta([string tolower $key]) $value } } else { #puts stderr line-EMPTY if {[my exists meta(content-length)] && [my set meta(content-length)]>0} { #puts stderr "we have content-length [my set meta(content-length)]" set data "" [self]::connection translation binary [self]::connection event readable [self] receive-body } elseif {[my exists meta(content-type)] && [regexp -nocase {multipart/form-data; *boundary=} \ [my set meta(content-type)]]} { #puts stderr "formdata" set data "" [self]::connection event readable [self] receive-body } else { #puts stderr "no-content-length, triggering respond" [self]::connection event readable [self] "" [my info parent] instvar requiresBody if {$requiresBody($method)} { my replyCode 411 my replyErrorMsg } else { my check-redirect } } } } Httpd::Wrk instproc receive-body {} { ;# ... now we have to read the body #my showCall my instvar method data meta set d [[self]::connection read] if {$d ne ""} { append data $d #my showMsg "datal=[string length $data], cl=$meta(content-length)" if {[string length $data] >= $meta(content-length)} { [self]::connection event readable [self] "" if {$method eq "POST"} { my decode-POST-query } my check-redirect } } else { ;# 0 byte, must be eof... my showMsg "received 0 bytes" [self]::connection event readable [self] "" if {[string length $data] < $meta(content-length)} { my replyCode 404 my replyErrorMsg } else { my check-redirect } } } Httpd::Wrk instproc unmodified mtime { my instvar meta if {[info exists meta(if-modified-since)]} { set ms $meta(if-modified-since) regexp {^([^;]+);(.*)$} $ms _ ms options if {[catch {set mss [clock scan $ms]}]} { regsub -all -- {-} $ms " " ms if {[catch {set mss [clock scan $ms]}]} { set ms [lreplace $ms end end] set mss [clock scan $ms] } } return [expr {$mtime <= $mss}] } return 0 } Httpd::Wrk instproc check-redirect {} { [my info parent] instvar redirects my instvar resourceName hasFormData query set resource $resourceName$hasFormData$query foreach {pattern hostport} $redirects { #puts stderr "match <$pattern> <$resource> [regexp $pattern $resource]" if {[regexp $pattern $resource]} { #puts stderr "do redirect to $hostport/$resource" my replyCode 302 location $hostport/$resource my replyErrorMsg return } } my respond } Httpd::Wrk instproc respond {} { # Respond to the query # the request was read completely... This method is wellsuited for mixins! my respond-[my set method] } Httpd::Wrk instproc respond-GET {} { #my showCall my instvar fileName my sendFile $fileName } Httpd::Wrk instproc respond-HEAD {} { # Respond to the query my instvar fileName if {[file readable $fileName]} { my replyCode 200 \ Last-Modified [Httpd Date [file mtime $fileName]] \ Content-Type [Mime guessContentType $fileName] \ Content-Length [file size $fileName] [self]::connection puts "" #my log Done "$fileName [Mime guessContentType $fileName]" my close } else { my replyCode 404 my replyErrorMsg } } Httpd::Wrk instproc respond-OPTIONS {} { # Respond to the query my replyCode 200 \ Allow "OPTIONS, GET, HEAD, POST" \ Public "OPTIONS, GET, HEAD, POST" [self]::connection puts "" my close } Httpd::Wrk instproc respond-PUT {} { my instvar data method fileName my replyCode [expr {[file writable $fileName] ? 200 : 201}] [self]::connection puts "" set out [open $fileName w] fconfigure $out -translation binary puts -nonewline $out $data my log Done "$fileName [Mime guessContentType $fileName]" close $out my close } Httpd::Wrk instproc respond-CGI {} { my instvar fileName if {[file executable $fileName]} { my replyCode 200 [self]::connection puts [exec $fileName] ;# no parameter handling yet my close } else { my replyCode 403 my replyErrorMsg } } Httpd::Wrk instproc new-formData {} { set arg [Object create [self]::[my autoname formData]] my lappend formData $arg return $arg } Httpd::Wrk instproc decode-formData {query} { #my showCall foreach pair [split [string trimleft $query \n] &] { set arg [my new-formData] if {[regexp {^(.+)=(.*)$} $pair _ name content]} { $arg set name [url decodeItem $name] $arg set content [url decodeItem $content] } else { $arg set content [url decodeItem $pair] } } } Httpd::Wrk instproc decode-POST-query {} { if {[my exists meta(content-type)]} { set ct [my set meta(content-type)] if {[regexp -nocase {application/x-www-form-urlencoded} $ct]} { #my showMsg "ordinary FORM" my decode-formData [my set data] return } elseif {[regexp -nocase {multipart/form-data; *boundary=(.*)$} $ct \ _ boundary]} { #my showMsg "multipart FORM" set parts [my set data] set bl [expr {[string length $boundary]+2}] while {[set endIDX [string first --$boundary $parts]] > -1} { set part [string range $parts $bl [expr {$endIDX-1}]] if {[set endHD [string first \r\n\r\n $part]] > -1} { set arg [my new-formData] if {[catch {Mime multipart-decode-header \ [string range $part 0 [expr {$endHD-1}]] \ $arg} msg]} { my replyCode 406 my replyErrorMsg $msg return 0 } $arg set content [string range $part \ [expr {$endHD + 4}] \ [expr {[string length $part] -3}]] #$arg showVars } set parts [string range $parts [expr {$endIDX+2}] end] } } } } Httpd::Wrk instproc respond-POST {} { my replyCode 405 my replyErrorMsg #my respond-CGI } Httpd::Wrk instproc replyErrorMsg {{msg ""} args} { my instvar replyCode [self class] instvar codes foreach {tag value} $args {[self]::connection puts "$tag: $value"} my sendText "\nStatus Code: $replyCode\n\ $msg

\n\ Status Code $replyCode: $codes($replyCode)
\n\ Resource Name: [my set resourceName]\n" my close ;# close must be last call } Httpd::Wrk instproc replyCode {code args} { #my showCall my instvar version [self class] instvar codes my set replyCode $code [self]::connection puts "HTTP/$version $code $codes($code)" foreach {tag value} [my set replyHeaderFields] {[self]::connection puts "$tag: $value"} foreach {tag value} $args {[self]::connection puts "$tag: $value"} if {$code >= 400} { my log Error "$code $codes($code)\tmeta: [my array get meta]" } else { my log Done "$code $codes($code)" } } Httpd::Wrk instproc sendText {response {type text/html}} { #my showCall [self]::connection puts "Content-Type: $type" # bei einer leeren Responses blockieren Klienten und melden Fehler if {$response eq ""} { set response " " } [self]::connection puts "Content-Length: [string length $response]\n" if {[my set method] ne "HEAD"} { [self]::connection fconfigure -translation {auto binary} [self]::connection puts-nonewline $response } else { my showMsg HEAD! } } Httpd::Wrk instproc sendMsg {response {type text/html}} { # my showCall my replyCode 200 my sendText $response $type my close } Httpd::Wrk instproc sendDir {dirName} { [my info parent] instvar root set title "Directory listing" set reply "$title

$title

\n\n" set oldpwd [pwd] cd $root set dirs ""; set files "" foreach f [lsort -dictionary [glob -nocomplain ./$dirName/*]] { set full [file join $root $f] set pname [string trimleft $f ./] if {[file isdir $full]} { append pname / } if {![catch {set size [file size $full]}]} { # it is not a broken link set entry "" append entry \ \ " \ " \ \n if {[string match */ $pname]} {append dirs $entry} else {append files $entry} } } append reply $dirs $files "
"$pname" " $size " [clock format [file mtime $full]]
\n" cd $oldpwd my sendMsg $reply return } Httpd::Wrk instproc sendFile {fn {type ""}} { #my showCall if {[file isdirectory $fn]} { set full [file join $fn index.html] if {[file readable $full]} { set fn $full } else { my sendDir [my set resourceName] return } } #puts stderr "readable '$fn' [file readable $fn]" if {[file readable $fn]} { set mtime [file mtime $fn] if {[my unmodified $mtime]} { my replyCode 304 my replyErrorMsg return } if {$type eq ""} {set type [Mime guessContentType $fn]} my replyCode 200 \ Last-Modified [Httpd Date $mtime] \ Content-Type $type \ Content-Length [file size $fn] [self]::connection puts "" [self]::connection fconfigure -translation binary ;#-buffersize 65536 set localFile [open $fn] fconfigure $localFile -translation binary -buffersize 65536 fcopy $localFile [[self]::connection set socket] \ -command [list [self] fcopy-end $localFile] } else { my replyCode 404 my replyErrorMsg } } Httpd::Wrk instproc fcopy-end {localFile args} { # End of fcopy close $localFile [self]::connection fconfigure -blocking false ;# fconfigure changes blocking in 8.3.2! my close } Httpd::Wrk instproc log {reason arg} { # trivial logging my instvar port ipaddr if {[my exists user]} { set user [my set user]/[my set realm] } {set user -} [my info parent] instvar logfile puts $logfile "[clock format [clock seconds]] $user $ipaddr:$port\t$reason\t$arg" flush $logfile } ######################################################################### Class Httpsd -superclass Httpd -parameter { {port 443} {httpdWrk Httpsd::Wrk} {requestCert 0} {requireValidCert 0} {certfile filename.crt} {keyfile filename.key} {cafile cacert.pem} {infoCb {}} } Httpsd instproc init args { package require tls proc tls::password {} { puts stderr "getting passwd" return pemp } next } Class Httpsd::Wrk -superclass Httpd::Wrk Httpsd::Wrk instproc firstLine {} { my set forceVersion1.0 1 my lappend replyHeaderFields Connection close next } Httpsd::Wrk instproc makeConnection {socket} { Connection create [self]::connection -socket $socket -req [self] [my info parent] instvar \ keyfile certfile cafile infoCb requestCert requireValidCert # SSL-enable a regular Tcl channel - it need not be a socket, but # must provide bi-directional flow. Also setting session parameters # for SSL handshake. www.sensus.org/tcl/tls.htm # -request bool --> Request a certificate from peer during SSL # handshake. (default: true) # -require bool --> Require a valid certificate from peer during SSL # handshake. If this is set to true then -request must also be set # to true. (default: false) # -server bool --> Handshake as server if true, else handshake as # client.(default: false) [self]::connection importSSL -server 1 \ -certfile $certfile \ -keyfile $keyfile \ -cafile $cafile \ -request $requestCert \ -require $requireValidCert \ -command $infoCb } ######################################################################### ### ### Mixin-Classes for respond patterns ### mixes into Http and Httpd::Wrk ### Class Httpd::Responder Httpd::Responder instproc init args { next my lappend workerMixins Httpd::Responder::Wrk my set respondpatterns {} # Example how to register new methods: regexp is matched with the triple # (HTTP-METHOD URL HASFORMDATA) where HASFORMDATA is empty when no # parameters are given. The parsed components of the url etc. are # available as instvars my actions {^GET cgi[-]bin [?]} respond-CGI } Httpd::Responder instproc actions {regexp method} { my lappend respondpatterns $regexp $method } Class Httpd::Responder::Wrk Httpd::Responder::Wrk instproc respond {} { my instvar fileName method resourceName hasFormData [my info parent] instvar respondpatterns ### auch das ist ein kandidat fuer eine chain of responsibility foreach {pattern action} $respondpatterns { if {[regexp $pattern "$method $resourceName $hasFormData"]} { my $action return } } next } ### ### Mixin-Classes for Access Control ### mixes into Http and Httpd::Wrk ### Class Httpd::AccessControl Httpd::AccessControl abstract instproc protectedResource {fn method varAuthMethod varRealm} Httpd::AccessControl abstract instproc credentialsNotOk {wrk credentials authMethod realm} Httpd::AccessControl abstract instproc addRealmFile {realm authFile} Httpd::AccessControl abstract instproc addRealmEntry {realm passwds} Httpd::AccessControl abstract instproc protectDir {realm path methods} Class Httpd::AccessControl::Wrk Httpd::AccessControl::Wrk instproc respond {} { my instvar fileName method digestChallengeData set controller [my info parent] if {[$controller protectedResource $fileName $method authMethod realm]} { #my showMsg "*** Protected resource: $fileName $method" if {![my exists meta(authorization)] || [$controller credentialsNotOk [self] \ [my set meta(authorization)] $authMethod $realm]} { my unauthorizedAccess $realm return } } next } ########################################################################### ## Basic Access Control ########################################################################### Class Httpd::BasicAccessControl -superclass Httpd::AccessControl Httpd::BasicAccessControl instproc initWorkerMixins {} { my lappend workerMixins [self class]::Wrk } Httpd::BasicAccessControl instproc init args { next my initWorkerMixins } Httpd::BasicAccessControl instproc protectedResource {fn method varAuthMethod varRealm} { #my showCall # check whether access to $fn via $method is protected upvar [self callinglevel] $varAuthMethod authMethod $varRealm realm # we check only the current directory, not the parent directories if {[string match */ $fn]} { set path $fn } else { set path [file dirname $fn]/ } foreach i [list $path $path:$method] { if {[my exists protected($i)]} { set realm [my set protected($i)] set authMethod Basic return 1 } } return 0 } Httpd::BasicAccessControl instproc credentialsNotOk {wrk credentials authMethod realm} { # check whether $credentials are sufficient for $realm regexp {^(.*):(.*)$} [base64 decode [lindex $credentials 1]] _ user pwd #puts stderr "passwd($realm:$user)=[my exists passwd($realm:$user)]" $wrk set user $user $wrk set realm $realm if {[my exists passwd($realm:$user)]} { return [expr {[my set passwd($realm:$user)] != $pwd}] } return 1 } Httpd::BasicAccessControl instproc addRealmEntry {realm passwds} { if {[llength $passwds] == 1} { my addRealmFile [lindex $passwds 0] } else { foreach {name pwd} $passwds { #puts stderr "realm='$realm' adding user: $name pw: $pwd" my set passwd($realm:$name) $pwd } } } Httpd::BasicAccessControl instproc addRealmFile {realm authFile} { set FILE [open $authFile r] while {![eof $FILE]} { foreach {name pwd} [split [gets $FILE] :] { my addRealmEntry $realm [list $name $pwd] } } close $FILE } Httpd::BasicAccessControl instproc protectDir {realm path methods} { my instvar root my checkRoot set resource $root/$path ;# resources are currently directories if {$methods == {}} { my set protected($resource) $realm ;#for every method } else { foreach m $methods { my set protected($resource:$m) $realm ;#for selected methods } } } Class Httpd::BasicAccessControl::Wrk -superclass Httpd::AccessControl::Wrk Httpd::BasicAccessControl::Wrk instproc unauthorizedAccess {realm} { my set digestChallengeData(realm) $realm my replyCode 401 www-authenticate "Basic realm=\"$realm\"" my replyErrorMsg "Unauthorized request for realm '$realm'" } ########################################################################### ## Digest Access Control ########################################################################### Class Httpd::DigestAccessControl -superclass Httpd::BasicAccessControl Httpd::DigestAccessControl instproc init args { package require tcu next my lappend workerMixins [self class]::Wrk } Httpd::DigestAccessControl instproc credentialsNotOk {wrk credentials authMethod realm} { # check whether $credentials are sufficient for $realm my showMsg "Digest Authentication ..." # HELP FD: hier muss ich noch überprüfen, ob die digest-header # (credentials) ok sind. Hier habe ich probleme auf die sachen, # die der worker gesendet (bspw. nonce) hat zu kommen. Ich # weiß, man kann mit [my info children] daran kommen. Aber, # was ist, wenn man mehrere Worker hat? ## Fredj, das sollte kein Problem sein: das credentialsNotOk wird ## vom aktuellen worker (respond) aufgerufen. man kann dem *NotOk ## den worker mitgeben, oder die beiden Methoden etwas umorganisieren. return } Class Httpd::DigestAccessControl::Wrk -superclass Httpd::BasicAccessControl::Wrk Httpd::DigestAccessControl::Wrk instproc unauthorizedAccess {realm} { my set digestChallengeData(realm) $realm my replyCode 401 www-authenticate "Digest [my digestChallenge]" my replyErrorMsg "Unauthorized request for realm '$realm'" } Httpd::DigestAccessControl::Wrk instproc digestChallenge {} { my showCall my instvar digestChallengeData my mkDigestChallengeData set digestResponse {} foreach {t v} [array get digestChallengeData] { append digestResponse "$t = \"$v\", " } regsub {, $} $digestResponse {} digestResponse return $digestResponse } Httpd::DigestAccessControl::Wrk instproc mkDigestChallengeData {} { my showCall my instvar digestChallengeData # RFC 2617 # challenge = "Digest" digest-challenge # digest-challenge = 1#( realm | [ domain ] | nonce | # [ opaque ] |[ stale ] | [ algorithm ] | # [ qop-options ] | [auth-param] ) # domain = "domain" "=" <"> URI ( 1*SP URI ) <"> # URI = absoluteURI | abs_path # nonce = "nonce" "=" nonce-value # nonce-value = quoted-string # opaque = "opaque" "=" quoted-string # stale = "stale" "=" ( "true" | "false" ) # algorithm = "algorithm" "=" ( "MD5" | "MD5-sess" | token ) # qop-options = "qop" "=" <"> 1#qop-value <"> # qop-value = "auth" | "auth-int" | token # FD: hier würde man die nötigen parametern (nonce,domain,opaque, # etc.) berechnen und in dem asso. Array speichern. # FD: minimale Anforderung set digestChallengeData(nonce) [my genNonce] set digestChallengeData(opaque) [base64 encode [self]:my-self-spcified-string] set digestChallengeData(algorithm) "MD5" ;#default set digestChallengeData(qop) "auth" set digestChallengeData(domain) [array names [my info parent]::protected] } Httpd::DigestAccessControl::Wrk instproc genNonce {} { my showCall my instvar digestChallengeData set timeStamp [clock seconds] set nonce [base64 encode [md5 $timeStamp:[self]]] return $nonce } # # example usage: #Httpd h1 -port 8081 -root [glob ~/wafe] #Httpd h2 -port 9086 -root $root \ -mixin {Httpd::Responder Httdp::BasicAccessControl} \ -addRealmEntry test {test test} -protectDir test "" {} \ -redirect {^(mailman|pipermail|cgi-bin) http://alice.wu-wien.ac.at:80} namespace export Httpd Httpsd namespace eval Httpd { namespace export Wrk \ AccessControl BasicAccessControl DigestAccessControl \ Responder } namespace eval Httpsd { namespace export Wrk } #namespace eval Responder {namespace export Wrk} #namespace eval AccessControl {namespace export Wrk} #namespace eval BasicAccessControl {namespace export Wrk} #namespace eval DigestAccessControl {namespace export Wrk} } namespace import ::xotcl::comm::httpd::* namespace eval Httpd {namespace import ::xotcl::comm::httpd::Httpd::*} namespace eval Httpsd {namespace import ::xotcl::comm::httpd::Httpsd::*} #namespace eval Responder {namespace import ::xotcl::comm::httpd::Responder::*} #namespace eval AccessControl {namespace import ::xotcl::comm::httpd::AccessControl::*} #namespace eval BasicAccessControl {namespace import ::xotcl::comm::httpd::BasicAccessControl::*} #namespace eval DigestAccessControl {namespace import ::xotcl::comm::httpd::DigestAccessControl::*} library/xotcl/library/comm/Imap.xotcl000066400000000000000000000127711242365656200202360ustar00rootroot00000000000000# -*- Tcl -*- $ package provide xotcl::comm::imap 2.0 package require XOTcl 2.0 namespace eval ::xotcl::comm::imap { package require xotcl::comm::httpAccess namespace import ::xotcl::* Class Imap -superclass NetAccess -parameter {user} Imap instproc initialize args { my instvar port caching tokenCounter resp token set port 143 set caching 1 set resp(connect) {"[*] OK" login} set resp(login) {"A[0-9]+ OK" loginFinished "A[0-9]+ NO" login} set resp(loginFinished) {"[*] [0-9]+" inboxSize "[*] OK" inboxSelected} set resp(mailSelected) {"[*] [0-9]+ FETCH" fetchBody "A[0-9]+ OK " ignoreLine "[*] " ignoreLine} set resp(heads) {"[*] [0-9]+ FETCH" fetchHeaders "A[0-9]+ OK " ignoreLine "[*] " ignoreLine} set tokenCounter 0 next set token NONE } Imap instproc err {state reply} { my abort "Error in $state: $reply" } Imap instproc token {} { my instvar tokenCounter return [format {A%.4d} [incr tokenCounter]] } Imap instproc imapString {input} { regsub -all {(["\])} $input {\\\1} output ;#" return \"$output\" } Imap instproc queryServer {query state} { #my showCall my instvar S token set token [my token] puts $S "$token $query" #puts stderr "$token $query" flush $S fileevent $S readable [list [self] response $state] } Imap instproc response {state} { my instvar S resp msg token set msg [gets $S] #my showVars msg token foreach {c newState} $resp($state) { if {![regexp {^[*]} $msg] && ![regexp ^$token $msg]} { my showMsg "$state: token=$token IGNORING $msg" return } if {[regexp ^$c $msg]} { #my showMsg "$state NEWSTATE $newState" return [my $newState] } } my err $state "expected=$resp($state), got $msg" } Imap instproc GET {} { my instvar state S path host port user inbox mailNr # number at end of path is the message number in the mailbox if {[regexp {^([^/]+)/([^/]+)/([0-9]+)$} $path _ user inbox mailNr]} { } elseif {[regexp {^([^/]+)/([^/]+)/?$} $path _ user inbox]} { } else { my abort "invalid imap path $path" } regexp {^(.*):([0-9]+)$} $host _ host port # proxy ? if {[catch {set S [socket -async $host $port]} err]} { my abort "Could not open connection to host '$host:$port'\n $err" } else { fconfigure $S -blocking false fileevent $S readable [list [self] response connect] } } Imap instproc login {} { my instvar user host password if {[pwdManager requirePasswd "Imap $user\@$host" $user password]} { my queryServer "login $user [my imapString $password]" login } else { what now? } } Imap instproc loginFinished {} { my instvar user host password inbox pwdManager storePasswd "Imap $user\@$host" $user $password my queryServer "select $inbox" loginFinished } Imap instproc inboxSize {} { my instvar msg nrMails regexp {^[*] ([0-9]+) EXISTS} $msg _ nrMails } Imap instproc inboxSelected {} { my instvar msg contentType nrMails mailNr if {[info exists mailNr]} { set contentType text/plain my body-state my queryServer "fetch $mailNr rfc822" mailSelected } else { my instvar header inbox block host user block set contentType text/html my body-state set what "Mailbox $inbox of $user@$host" set block "$what\n" append block "

$what

\n" \ "The following $nrMails messages are in this mailbox:" \ "

\n

\n" my pushBlock my set state 4 my finish } } } namespace export Imap } namespace import ::xotcl::comm::imap::* library/xotcl/library/comm/Ldap.xotcl000066400000000000000000000077571242365656200202400ustar00rootroot00000000000000# -*- Tcl -*- package provide xotcl::comm::ldap 2.0 package require xotcl::wafecompat ; # Get 'requireModules'. package require XOTcl 2.0 namespace eval ::xotcl::comm::ldap { namespace import ::xotcl::* requireModules { ldapOpen ldaplibGen.so } Class Ldap -superclass NetAccess -parameter {host port dn attributes scope filter} Ldap instproc initialize args { my instvar port mapToC useCache my set port 389 my set useCache 0 set mapToC(one) onelevel set mapToC(sub) subtree set mapToC(base) base next } Ldap proc urlDecode string { set toParse $string set parsed "" while {1} { if {[regexp {^([^%]*)%(..)(.*)$} $toParse _ front hex toParse]} { append parsed $front [binary format c 0x$hex] } else { append parsed $toParse break } } return $parsed } Ldap instproc getUrlcomponents {} { showCall my instvar path dn attributes scope filter url set path [Ldap urlDecode $path] puts stderr "___ path=<$path>" if {[regexp -nocase {^/([^?]*)(\?([^?]*)(\?([^?]*)(\?([^?]*))?)?)?$} \ $path _ dn a attributes s scope f filter]} { if {$scope eq ""} { set scope "base" } if {$filter eq ""} { set filter "(objectClass=*)" } } else { set errmsg "*** Ldap Url trail=<$path> does not match!\n" append errmsg "___ RFC 1959 says:\n" append errmsg " ldap://:/\[?\[??\]\]\n" append errmsg "___ Cineast and Netscape uses:\n" append errmsg " ldap://:/\[?\[?\[?\]\]\]" my abort "Unsupported URL: '$url' \n $errmsg" } } Ldap instproc GET {} { my instvar contentType totalsize state currentsize informObjects block showCall set contentType text/html my getUrlcomponents if {"start" ne $state } { puts stderr "... [self]:$proc ignoring request in state $state" return } my open my search my body-state set totalsize [string length $block] set currentsize $totalsize foreach obj $informObjects { $obj incCb [self] $totalsize $currentsize } my eof } Ldap instproc open {} { showCall my instvar port host ldapHandle set ldapHandle [ldapOpen $host $port] } Ldap instproc bind {} { my instvar ldapHandle showCall } Ldap instproc search {} { showVars my instvar url ldapHandle searchHandle dn attributes scope filter results mapToC path set searchHandle [ldapSearch $ldapHandle $dn \ $mapToC($scope) $filter [split $attributes ,] false results] set nentries [ldapCountEntries $ldapHandle $searchHandle] puts stderr "*** nentries = $nentries" if {!$nentries} {set results ""} my response } Ldap instproc getAttrs {dn} { } Ldap instproc makeUrl {dn} { showCall my instvar port host scope filter attributes set tmpUrl ldap://$host:$port/$dn?$attributes?$scope?$filter return "$dn" } Ldap instproc response {} { showCall my instvar block results attrsVals ldapHandle searchHandle set block " LDAP searching result!!

Result

\n
    \n" foreach {resDN} $results { append block "
  • [my makeUrl $resDN]

    \n

      \n" ldapAttributes $ldapHandle $searchHandle $resDN attrsVals foreach {a v} [array get attrsVals] { append block "
    • $a = $v

      \n" } append block "

    \n" } append block "
\n \n" } # destructor: Close Connection to LDAP-Server and unbind Ldap instproc destroy {} { showCall my instvar ldapHandle if {[catch {ldapUnbind $ldapHandle} error]} { return $error } my freeSearchHandle } Ldap instproc close {} { showCall my destroy next } Ldap instproc freeSearchHandle {} { showCall my instvar searchHandle if {[info exists searchHandle]} { ldapFreeSearch $searchHandle } } namespace export Ldap } namespace import ::xotcl::comm::ldap::* library/xotcl/library/comm/Mime.xotcl000066400000000000000000000162331242365656200202340ustar00rootroot00000000000000# -*- Tcl -*- package provide xotcl::comm::mime 2.0 package require XOTcl 2.0 namespace eval ::xotcl::comm::mime { namespace import ::xotcl::* ####################################################################### Class MimeTypeLoader MimeTypeLoader instproc loadMimeTypes {file} { if {![file exists $file]} return puts stderr "Loading Mime types from $file" set f [open $file r] set content [read $f] close $f regsub -all "\\\\ *\n" $content " " content foreach line [split $content \n] { set line [string trim $line] if {[regexp ^\# $line]} continue if {$line eq ""} continue regsub -all " +" $line " " line #puts stderr <$line> while {$line ne ""} { if {[regexp {^ *([^ ]+)=\"([^\"]+)\" *(.*)$} $line _ key value line]} { set v([string tolower $key]) $value } elseif {[regexp {^ *([^ ]+)=([^ ]+) *(.*)$} $line _ key value line]} { set v([string tolower $key]) $value } else { set tokens [split $line] if {![regexp / [lindex $line 0]]} { puts stderr "Mime: cannot parse line '$line' in $file" } else { set v(exts) [join [lrange $tokens 1 end] ,] set v(type) [lindex $tokens 0] } break } } if {[info exists v(exts)] && [info exists v(type)]} { set v(exts) [string tolower $v(exts)] set v(type) [string tolower $v(type)] foreach ext [split $v(exts) ,] { set ext [string trimleft $ext .] #puts stderr "ext '$ext', contentType = '$v(type)'" my set extTable($ext) $v(type) } unset v(exts) v(type) } else { puts stderr "invalid mime entry in $file" } } } MimeTypeLoader instproc guessContentType {name} { my loadMimeTypes ~/.mime.types my mixin {} return [next] } Class MIME MIME instproc guessContentType {name} { my instvar extTable nameTable if {[regexp {\.([a-zA-Z0-9]+)$} $name _ ext]} { catch {set contentType $extTable([string tolower $ext])} } if {![info exists contentType]} { foreach namePattern [array names nameTable] { if {[regexp $namePattern $name]} { set contentType text/plain break } } } if {![info exists contentType]} { set contentType unknown/unknown } return $contentType } MIME instproc multipart-decode-header {header obj} { $obj instvar name filename contentType foreach line [split $header \r] { set line [string trim $line \n] #puts stderr line=$line if {[regexp -nocase {^Content-Disposition: *([^;]+);(.*)$} $line _ \ dispo detail]} { if {$dispo ne "form-data"} { error "Unknown Content Disposition '$line'" } if {![regexp -nocase { name *= *"([^\"]+)"} $line _ name]} { error "can't parse form-data name '$line'" } regexp -nocase {filename *= *"([^\"]+)"} $line _ filename } elseif {[regexp -nocase {^Content-Type: *([^; ]+)} $line _ contentType]} { } else { my showMsg "ignoring '$line'" } } } MIME create Mime -mixin MimeTypeLoader Mime array set nameTable { README text/plain } Mime array set extTable { gif image/gif xpm image/x-xpixmap xbm image/x-xbitmap jpg image/jpeg png image/x-png html text/html htm text/html xml text/xml css text/css ps application/postscript pdf application/pdf doc application/msword xls application/msexel } ################################################################## Class FormData FormData instproc encode list {;#RFC 1867 my showCall } FormData formData ################################################################## Class Base64 Base64 instproc init args { my instvar base64 base64_en # Emit base64 encoding for a string set i 0 foreach char {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \ a b c d e f g h i j k l m n o p q r s t u v w x y z \ 0 1 2 3 4 5 6 7 8 9 + /} { set base64($char) $i set base64_en($i) $char incr i } next } Base64 instproc encode string { my instvar base64_en set result {} set length 0 foreach {a b c} [split $string {}] { scan $a %c x if {$c ne ""} { scan $b %c y scan $c %c z append result \ $base64_en([expr {($x>>2) & 0x3F}]) \ $base64_en([expr {(($x<<4) & 0x30) | (($y>>4) & 0xF)}]) \ $base64_en([expr {(($y<<2) & 0x3C) | (($z>>6) & 0x3)}]) \ $base64_en([expr {$z & 0x3F}]) } elseif {$b ne ""} { scan $b %c y append result \ $base64_en([expr {($x>>2) & 0x3F}]) \ $base64_en([expr {(($x<<4) & 0x30) | (($y>>4) & 0xF)}]) \ $base64_en([expr {($y<<2) & 0x3C}]) \ = } else { append result \ $base64_en([expr {($x>>2) & 0x3F}]) \ $base64_en([expr {($x<<4) & 0x30}]) \ == } if {[incr length 4] >= 72} { append result \n set length 0 } } return $result } Base64 instproc decode string { my instvar base64 set output {} set group 0 set j 18 foreach char [split $string {}] { if {$char != "="} { set group [expr {$group | ($base64($char) << $j)}] if {[incr j -6] < 0} { scan [format %06x $group] %2x%2x%2x a b c append output [format %c%c%c $a $b $c] set group 0 set j 18 } } else { scan [format %04x $group] %2x%2x a b if {$j==6} { append output [format %c $a] } else { append output [format %c%c $a $b] } break } } return $output } Base64 base64 ################################################################## Class Url Url instproc encode list { set result "" set sep "" foreach i $list { append result $sep [my encodeItem $i] if {$sep != "="} { set sep = } else { set sep & } } return $result } Url instproc encodeItem string { my instvar httpFormMap set alphanumeric a-zA-Z0-9. if {![info exists httpFormMap]} { for {set i 1} {$i <= 256} {incr i} { set c [format %c $i] if {![string match \[$alphanumeric\] $c]} { set httpFormMap($c) %[format %.2x $i] } } # these are handled specially array set httpFormMap { " " + \n %0d%0a } } regsub -all \[^$alphanumeric\] $string {$httpFormMap(&)} string regsub -all \n $string {\\n} string regsub -all \t $string {\\t} string regsub -all {[][{})\\]\)} $string {\\&} string return [subst $string] } Url instproc hexToChar hex { ::scan $hex %x h #my showMsg "::scan $hex %x h -> $h" format %c $h } Url instproc decodeItem string { #my showCall set result "" regsub -all {\+} $string " " string regsub -all {%0d%0a} $string "\n" string regsub -all {%([a-fA-F0-9][a-fA-F0-9])} $string {[my hexToChar \1]} string return [subst -novariables -nobackslashes $string] } Url instproc decodeName string { #my showCall set result "" regsub -all {%0d%0a} $string "\n" string regsub -all {%([a-fA-F0-9][a-fA-F0-9])} $string {[my hexToChar \1]} string return [subst -novariables -nobackslashes $string] } Url instproc decode string { #my showCall set result "" foreach i [split $string &=] { lappend result [decodeItem $i] } #my showVars result return $result } Url url namespace export Mime url base64 } namespace import ::xotcl::comm::mime::* #puts stderr "importing ::xotcl::comm::mime::* to [namespace current]" library/xotcl/library/comm/PCache.xotcl000066400000000000000000000157301242365656200204710ustar00rootroot00000000000000# -*- Tcl -*- $ # Persistent Cache object, using gdbm # Configuration: # The persistent cache is kept in a directory which is determined by # the following three rules. # # 1) the global variable "CACHE_DIR", which has to be set, # before this file is loaded # 2) If "CACHE_DIR" is not set, the global variable "homedir" # is checked, which is assumed to be the home directory # of the Cineast browser # 3) As a last resource the tmp directory is used as the cache directory # # Additionally, the cache directory can be specified after loading of this # file (before the first open) through the instance variable "dir" # in the object persistentCache. package provide xotcl::comm::pcache 2.0 #package require xotcl::package package require XOTcl 2.0 namespace eval ::xotcl::comm::pcache { namespace import ::xotcl::* variable CACHE_DIR variable homeDir if {![info exists CACHE_DIR]} { if {![info exists homeDir]} { set homeDir [::xotcl::tmpdir] } set CACHE_DIR $homeDir/cache2 } Object persistentCache persistentCache set dir $CACHE_DIR persistentCache proc flush { {cmd {}} } { my instvar DBID if {[info exists DBID]} { $DBID close } if {{} ne $cmd } { if {[catch {eval $cmd} err]} {puts stderr err=$err} } my open ;# UZ: wenn hier das self weggenommen wird, crashed das lintFilter #open ;# UZ: wenn hier das self weggenommen wird, crashed das lintFilter } # the open method for the first invocation persistentCache proc open {} { my instvar dir DBID package require xotcl::store set DBID [Storage someNewChildStore] if {![file isdirectory $dir]} { # if the cache directory does not exist, create it.. file mkdir $dir } # the open method for later invocations, doing the real work my proc open {} { my instvar dir DBID $DBID open $dir/index } # invoke the method open } persistentCache proc clear {} { my instvar cacheFileName contentType meta entry validated dir my flush [list eval file delete -force $dir/index \ [glob -nocomplain $dir/\[0-9\]*::*]] foreach var {cacheFileName contentType meta entry validated} { catch {unset $var} } } persistentCache proc clearEntry {url} { my instvar DBID cacheFileName contentType meta entry validated my inCache $url if {[info exists cacheFileName($url)]} { my flush [list eval file delete -force $cacheFileName($url)] foreach var {cacheFileName contentType meta entry validated} { my showMsg "unset ${var}($url)" catch {unset ${var}($url)} } catch {$DBID unset $url} } } persistentCache proc lazyFlush {} { my instvar flushPending if {[info exists flushPending]} { after cancel $flushPending } set flushPending [after 100 [self] flush] } persistentCache proc newEntry {url access doCache name} { my instvar cacheFileName contentType meta dir if {$name ne ""} { #$access set caching 0 return $name } elseif {$doCache} { set cacheFileName($url) $dir/[pid]-$access set contentType($url) [$access set contentType] set meta($url) [$access set meta] return $cacheFileName($url) } else { # we use the Memory cache only for non-persistent cache entries # which are deleted when the program terminates set fileName $dir/v[pid]-$access MemoryCache + $url $fileName return $fileName } } persistentCache proc entryDone {url} { my instvar entry cacheFileName contentType DBID meta if {![info exists DBID]} { open } $DBID set $url [list \ cacheFileName $cacheFileName($url) \ contentType $contentType($url) \ meta $meta($url) ] my lazyFlush #my showMsg "size=[file size $cacheFileName($url)]" set entry($url) 1 my set validated($url) 1 } persistentCache proc inCache {url} { my instvar entry if {[info exists entry($url)]} { set result 1 } else { my instvar cacheFileName contentType meta DBID if {![info exists DBID]} { open } set result [$DBID set $url] my lazyFlush if {$result ne ""} { set entry($url) 1 array set r $result set cacheFileName($url) $r(cacheFileName) set contentType($url) $r(contentType) set meta($url) $r(meta) set result 1 } else { set result 0 } } return $result } persistentCache proc validated {url} { my set validated($url) 1 } persistentCache proc invalidate {url} { if {[my exists validated($url)]} { my unset validated($url) } } persistentCache proc isValidated {url} { if {[my exists validated($url)]} { return 1 } return 0 } persistentCache proc ifModifiedHeader {url ifModVar} { set result 0 if {[my inCache $url]} { #puts stderr inCache:$url upvar [self callinglevel] $ifModVar ifModifiedHeader my instvar meta array set m $meta($url) if {[info exists m(last-modified)]} { set ifModifiedHeader [list If-Modified-Since $m(last-modified)] set result 1 } } else { #puts stderr "url=$url is not in cache" } return $result } persistentCache proc dump {} { my instvar DBID puts stderr DUMP: foreach k [$DBID names] { puts stderr $k puts stderr " [$DBID set $k]" } } persistentCache proc cacheFileName {url} { my instvar cacheFileName return $cacheFileName($url) } persistentCache proc contentType {url} { my instvar contentType return $contentType($url) } persistentCache proc meta {url} { my instvar meta return $meta($url) } persistentCache proc destroy {} { #my showCall next } #persistentCache flush ########################################################### Cache Object MemoryCache MemoryCache proc query {url entry} { my instvar cache if {[info exists cache($url)]} { upvar [self callinglevel] $entry e #puts stderr "-->[self] [self proc] finds: $url" set e $cache($url) return 1 } return 0 } MemoryCache proc + {url entry} { #puts stderr "-->[self class]:[self] [self proc] $url" my set cache($url) $entry } MemoryCache proc - {url} { #puts stderr "-->[self class]:[self] [self proc] $url" catch {my unset cache($url)} } MemoryCache proc destroy {} { my instvar cache foreach url [array names cache] { set f $cache($url) if {[regexp ^/ $f]} { #my showMsg "trying to remove $f [file exists $f]" file delete -force $f } } next } Object instproc allInstances {} { # Diese Methode ermittelt rekursiv alle direkten und indirekten # Instanzen einer Klasse ::set inst [my info instances] foreach s [my info subclass] { foreach i [$s allInstances] { ::lappend inst $i } } return $inst } # onExit is automatically called when wafe terminates proc onExit {} { #puts stderr "allinstances of Access: [Access allInstances]" #foreach i [Access allInstances] { # if {[info command $i] eq ""} continue # $i destroy #} #MemoryCache clear persistentCache flush #Trace statReport } namespace export persistentCache MemoryCache } namespace import ::xotcl::comm::pcache::* library/xotcl/library/comm/pkgIndex.tcl000066400000000000000000000013571242365656200205500ustar00rootroot00000000000000package ifneeded xotcl::comm::connection 2.0 [list source [file join $dir Connection.xotcl]] package ifneeded xotcl::comm::dav 2.0 [list source [file join $dir Dav.xotcl]] package ifneeded xotcl::comm::ftp 2.0 [list source [file join $dir Ftp.xotcl]] package ifneeded xotcl::comm::httpAccess 2.0 [list source [file join $dir Access.xotcl]] package ifneeded xotcl::comm::httpd 2.0 [list source [file join $dir Httpd.xotcl]] package ifneeded xotcl::comm::imap 2.0 [list source [file join $dir Imap.xotcl]] package ifneeded xotcl::comm::ldap 2.0 [list source [file join $dir Ldap.xotcl]] package ifneeded xotcl::comm::mime 2.0 [list source [file join $dir Mime.xotcl]] package ifneeded xotcl::comm::pcache 2.0 [list source [file join $dir PCache.xotcl]] library/xotcl/library/lib/000077500000000000000000000000001242365656200161005ustar00rootroot00000000000000library/xotcl/library/lib/Script.xotcl000066400000000000000000000015461242365656200204250ustar00rootroot00000000000000# -*- Tcl -*- package provide xotcl::script 2.0 package require XOTcl 2.0 namespace eval ::xotcl::script { namespace import ::xotcl::* @ @File {description { A small package to instantiate an object, that represents a script. } } @ Class Script { description { An object of type Script becomes automatically the command line arguments evaluated as "-" method calls during creation, e.g. <@pre> Script s -set r 5 and a call with cmd-line "-set v 6" of the script, results in an object s with two vars set: r to 5, and v to 6. } } Class Script Script proc create args { eval lappend args $::argv eval next $args } Script instproc unknown args { puts stderr "$::argv0: Unknown option ´-$args´ provided" } namespace export Script } namespace import ::xotcl::script::* library/xotcl/library/lib/htmllib.xotcl000066400000000000000000000433411242365656200206130ustar00rootroot00000000000000## -*- Tcl -*- $ # # htmllib.xotcl # # Author: Antti Salonen, as@fishpool.fi # # Copyright: # # This software is copyrighted by Fishpool Creations Oy Ltd. 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. # package provide xotcl::htmllib 2.0 package require XOTcl 2.0 namespace eval ::xotcl::htmllib { namespace import ::xotcl::* @ @File { description { This package provides the class HtmlBuilder, which can be used to generate HTML documents, or a part of a document. } authors { Antti Salonen, as@fishpool.fi } date { $Date: 2006/09/27 08:12:40 $ } } # # the compressed parameter means that minimal HTML page are created # i.e. that space indentation is turned off # Class create HtmlBuilder -parameter { {compressed 0} } ## The constructor. ## ## The HtmlBuilder object has two instance variables. The document Tcl list ## contains the document as a list of strings. The document is stored as a list ## rather than a single string to allow further indentation of the whole ## document when necessary. ## The indentLevel variable is the level of indentation, which is generally ## increased for the contents of any HTML element that may contain block-level ## elements. Typical examples would be
    ,
  • , and so forth. HtmlBuilder instproc init {} { my instvar document indentLevel set document [list] set indentLevel 0 return } HtmlBuilder instproc clear {} { my instvar document indentLevel set document [list] set indentLevel 0 return } HtmlBuilder instproc getDocument {} { my instvar document return $document } HtmlBuilder instproc toString {} { my instvar document compressed set rvalue "" foreach line $document { if {$compressed == "0"} { append rvalue "$line\n" } else { ## only new line for closing tags at the beginnig ## of a document element if {[string equal -length 2 ", for ## example, is not required by the HTML specification, using the closing method ## is necessary to have the document properly indented. # Add a string to the document within ... HtmlBuilder instproc addStringStrong {str} { my addString "$str" return } # Add a string to the document within ... HtmlBuilder instproc addStringEmphasized {str} { my addString "$str" return } # Add a comment to the document HtmlBuilder instproc addComment {str} { my addString "" return } HtmlBuilder instproc addLineBreak {} { my addString "
    " return } ## startDocument - Start an HTML document. Currently all documents are HTML 4.0 ## Transitional. HTML, BODY, HEAD and TITLE elements are added/started here. ## Optional arguments: ## -title documentTitle (empty if not given) ## -stylesheet externalStyleSheet ## -bgcolor backgroundColour (deprecated in HTML 4.0) HtmlBuilder instproc startDocument {args} { set title "" foreach {name value} $args { switch -- $name { -title { set title $value } -stylesheet { set stylesheet $value } -bgcolor { set bgcolor $value } } } my addString {} my addWhiteSpace my addString {} my addStringIncr {} my addString "$title" if {[info exists stylesheet]} { my addString "" } my addStringDecr {} my addWhiteSpace if {[info exists bgcolor]} { my addStringIncr "" } else { my addStringIncr {} } return } ## endDocument - end an HTML document HtmlBuilder instproc endDocument {} { my addStringDecr {} my addString {} return } ## startParagraph - start a P element ## Optional arguments: ## Common HTML arguments HtmlBuilder instproc startParagraph {args} { set attributes [HtmlBuilder parseArguments $args [list] [list]] my addStringIncr "" return } ## endParagraph - end a P element HtmlBuilder instproc endParagraph {} { my addStringDecr {

    } return } ## startAnchor - start an A element ## Optional arguments: ## -href URI ## -name cdata ## -target frameTarget ## Common HTML arguments HtmlBuilder instproc startAnchor {args} { set attributes [HtmlBuilder parseArguments $args \ [list "HREF" "NAME" "TARGET"] [list]] my addStringIncr "" return } ## endAnchor - end an A element HtmlBuilder instproc endAnchor {args} { my addStringDecr {} return } ## addAnchor - add an A element, using content as the visible link. ## Optional arguments: ## -href URI ## -name cdata ## -target frameTarget ## Common HTML arguments HtmlBuilder instproc addAnchor {content args} { eval my startAnchor $args my addString $content my endAnchor return } ## startUnorderedList - start a UL element ## Optional arguments: ## Commmon HTML arguments HtmlBuilder instproc startUnorderedList {args} { set attributes [HtmlBuilder parseArguments $args [list] [list]] my addStringIncr "" return } ## endUnorderedList - end a UL element HtmlBuilder instproc endUnorderedList {} { my addStringDecr {
} return } ## startListItem - start an LI element ## Optional arguments: ## Common HTML arguments HtmlBuilder instproc startListItem {args} { set attributes [HtmlBuilder parseArguments $args [list] [list]] my addStringIncr "" return } ## endListItem - end an LI element HtmlBuilder instproc endListItem {} { my addStringDecr {} return } ## add a simple list item HtmlBuilder instproc addListItem {content} { my startListItem my addString $content my endListItem } ## startTable - start a TABLE element. Note that if the -border argument isn't ## used, by default the table are created with borders (). ## Optional arguments: ## -border pixels ## -cellpadding length ## -cellspacing length ## -summary text ## -width length ## -bgcolor color spec ## Common HTML arguments HtmlBuilder instproc startTable {args} { set attributes [HtmlBuilder parseArguments $args \ [list "BORDER" "CELLPADDING" "CELLSPACING" "SUMMARY" \ "WIDTH" "BGCOLOR"] [list]] if {[lsearch $args "-border"] == -1} { append attributes " BORDER" } my addStringIncr "" return } ## endTable - end a TABLE element HtmlBuilder instproc endTable {} { my addStringDecr {
} return } ## startTableRow - start a TR element ## Optional arguments: ## Common HTML arguments HtmlBuilder instproc startTableRow {args} { set attributes [HtmlBuilder parseArguments $args [list "VALIGN"] [list]] my addStringIncr "" return } ## endTableRow - end a TR element HtmlBuilder instproc endTableRow {} { my addStringDecr {} return } ## startTableCell - start a TD element ## Optional arguments: ## -colspan number ## -rowspan number ## -align left|center|right|justify|char ## -valign top|middle|bottom|baseline ## -bgcolor ## -width ## Common HTML arguments HtmlBuilder instproc startTableCell {args} { set attributes [HtmlBuilder parseArguments $args \ [list "COLSPAN" "ROWSPAN" "ALIGN" "VALIGN" \ "BGCOLOR" "WIDTH"] [list]] my addStringIncr "" return } ## endTableCell - end a TD element HtmlBuilder instproc endTableCell {} { my addStringDecr {} return } # # add a simple table cell which just contains a string # HtmlBuilder instproc addTableCell {{string ""} args} { eval my startTableCell $args my addString $string my endTableCell } ## startTableHeaderCell - start a TH element ## Optional arguments: ## -colspan number ## -rowspan number ## -align left|center|right|justify|char ## -valign top|middle|bottom|baseline ## Common HTML arguments HtmlBuilder instproc startTableHeaderCell {args} { set attributes [HtmlBuilder parseArguments $args \ [list "COLSPAN" "ROWSPAN" "ALIGN" "VALIGN"] [list]] my addStringIncr "" return } ## endTableHeaderCell - end a TH element HtmlBuilder instproc endTableHeaderCell {} { my addStringDecr {} return } ## startForm - start a FORM element ## Required arguments: ## -action URI ## Optional arguments: ## -method get|post ## Common HTML arguments HtmlBuilder instproc startForm {args} { set attributes [HtmlBuilder parseArguments $args \ [list "ACTION" "METHOD" "ENCTYPE"] [list]] my addStringIncr "" return } ## endForm - end a FORM element HtmlBuilder instproc endForm {} { my addStringDecr {} return } ## addInput - add in INPUT element ## Required arguments: ## -type ## -name ## Optional arguments: ## -value ## -size ## -maxlength ## -checked ## Common HTML arguments HtmlBuilder instproc addInput {args} { set attributes [HtmlBuilder parseArguments $args \ [list "TYPE" "NAME" "VALUE" "SIZE" "MAXLENGTH"] \ [list "CHECKED"]] my addString "" return } ## addTextArea - start a TEXTAREA element ## First parameter: value - Default value of the text area ## Required arguments: ## -rows ## -cols ## Optional arguments: ## -name ## Common HTML Arguments HtmlBuilder instproc addTextArea {value args} { set attributes [HtmlBuilder parseArguments $args \ [list "ROWS" "COLS" "NAME"] [list]] my addString "$value" return } ## startOptionSelector - start a SELECT element ## Optional arguments: ## -name ## -size ## -multiple ## Common HTML arguments HtmlBuilder instproc startOptionSelector {args} { set attributes [HtmlBuilder parseArguments $args \ [list "NAME" "SIZE"] [list "MULTIPLE"]] my addStringIncr "" return } ## endOptionSelector - end a SELECT element HtmlBuilder instproc endOptionSelector {} { my addStringDecr "" return } ## startOption - start an OPTION element ## Optional arguments: ## -value ## -selected ## Common HTML arguments HtmlBuilder instproc startOption {args} { set attributes [HtmlBuilder parseArguments $args \ [list "VALUE"] [list "SELECTED"]] my addStringIncr "" return } ## endOption - end an OPTION element HtmlBuilder instproc endOption {} { my addStringDecr "" return } ## addImage - add an IMG element ## Required arguments: ## -src ## -alt ## -align (deprecated in HTML 4.0) ## Optional arguments: ## Common HTML arguments HtmlBuilder instproc addImage {args} { set attributes [HtmlBuilder parseArguments $args \ [list "SRC" "ALT" "ALIGN"] [list]] my addString "" return } ## startBlock - start a DIV element (a generic block-level container) ## Optional arguments: ## Common HTML attributes HtmlBuilder instproc startBlock {args} { set attributes [HtmlBuilder parseArguments $args [list] [list]] my addStringIncr "" return } ## endBlock - end a DIV element HtmlBuilder instproc endBlock {} { my addStringDecr "" return } ## addHorizontalRule - add an HR element ## Optional arguments: ## Common HTML arguments HtmlBuilder instproc addHorizontalRule {args} { set attributes [HtmlBuilder parseArguments $args [list] [list]] my addString "" return } namespace export HtmlBuilder } namespace import ::xotcl::htmllib::* library/xotcl/library/lib/makeDoc.xotcl000066400000000000000000000053651242365656200205270ustar00rootroot00000000000000# -*- Tcl -*- # # XOTcl style documentation tools # set auto_path [concat [file dirname [info script]] $auto_path] package require XOTcl 2.0 namespace import ::xotcl::* @ @File { description { Documentation tool for the XOTcl distribution.
Usage: 'makeDoc docdir filename ?filename ...?'
Called by Makefile. } } package require xotcl::package package verbose 1 package require xotcl::xodoc set fileList "" puts "XOTcl Documentation Tool" puts "------------------------" if {$argc > 1} { set DOCDIR [lindex $argv 0] puts "Documenting to directory $DOCDIR:" if {![file isdirectory $DOCDIR]} { file mkdir $DOCDIR } set files [lrange $argv 1 end] foreach file $files { puts "...$file" if {[catch {XODoc documentFileAsHTML $file $DOCDIR} fb]} { puts stderr "\terror processing $file:\n[string replace $::errorInfo 400 end ...]" } else { lappend fileList $file $fb } } } else { error "usage: xodoc docdir filename ?filename ...?" } set filesHtml "" set filesDir "" ## write index page foreach {f fb} $fileList { set dir . regexp {^(.*)/[^/]*$} $f _ dir if {$fb ne "langRef-xotcl"} { set tail ", " if {$dir != $filesDir} { append filesHtml "
  • Directory '$dir':
    " set filesDir $dir set tail "" } append filesHtml "$tail[file tail $f]" } } # # # XOTcl - Documentation # # #

    Lanuage Reference - Index

    set content { The Extended Object Tcl (XOTcl) Documentation contains the following parts:

    XOTcl Language Documentation

    Package and Script Documentation

    This section of the documentation is under work...
      $filesHtml

    Tcl Online Information

    } set content [subst -nobackslashes -nocommands $content] set f [open $DOCDIR/index.html w] puts $f $content close $f puts "Documentation finished" library/xotcl/library/lib/metadataAnalyzer.xotcl000066400000000000000000000305061242365656200224450ustar00rootroot00000000000000# -*- Tcl -*- package provide xotcl::metadataAnalyzer 2.0 package require XOTcl 2.0 namespace eval ::xotcl::metadataAnalyzer { namespace import ::xotcl::* @ @File { description { XOTcl file analyzer for @ metadata. E.g.\ used for doumentation with xoDoc (but in the static variant StaticMetadataAnalyzer which uses the dynamic variant in this file). <@p> Sample sample usage: <@pre> package require xotcl::metadataAnalyzer # instantiate metadata analyzer object MetadataAnalyzer @::m # make this object be known to @ and turn @ metadata processing on @ analyzerObj @::m @ onOff 1 # read in some metadata tags (in sample file) & execute the file source lib/testx.xotcl # turn @ metadata processing off again @ onOff 0 # print out all collected metadata puts [@::m print] } } @ Class MetadataToken { description { Each collected metadata element is stored in a token object. MetadataToken is superclass of token object classes. Each metadata token has two interesting parameters: <@p> "properties" contains list of all described metadata properties. E.g. can be printed with <@pre> foreach p [my set properties] { if {[my exists $p]} { append c " $p=[my set $p]\n" } } "name" contains the method, object, ... name of the metadata element. <@p> All metadata token are aggregated by @. Therefore, <@pre> foreach mdt [@ info children] { if {[$mdt istype MetadataToken]} {$mdt print} } prints all token. } } Class create MetadataToken -parameter { {name ""} {properties ""} } @ MetadataToken proc sortTokenList {l "token list"} { description {Sort a token list with names. Since names are autonames, this means order of appearance in the program.} } MetadataToken proc sortTokenList l { foreach t $l { set names([$t set name]) $t } set sortedNames [lsort [array names names]] set sortedList "" foreach n $sortedNames { lappend sortedList $names($n) } return $sortedList } MetadataToken instproc evaluateMetadata md { my instvar properties foreach {p v} $md { # only append property, if its not already there # otherwise just overwrite the value if {[lsearch $properties $p] == -1} { my lappend properties $p } my set $p $v } } @ MetadataToken instproc printProperties {} { description {Print metadata properties to stdout.} } MetadataToken instproc printProperties {} { set c "" foreach p [my set properties] { if {[my exists $p]} { append c " [my capitalize $p]=[my set $p]\n" } } return $c } MetadataToken instproc capitalize string { if {$::tcl_version >= 8.3} { string toupper $string 0 0 } else { return "[string toupper [string range $string 0 0]][string range $string 1 end]" } } @ MetadataToken abstract instproc print {} { description { Abstract method for printing a token to stdout. } } MetadataToken abstract instproc print {} @ Class FileToken -superclass MetadataToken { description { Token for @File Metadata. } } Class create FileToken -superclass MetadataToken FileToken instproc print {} { set c "FILE=[my set name]\n" append c [my printProperties] return $c } @ Class ConstraintToken -superclass MetadataToken { description { Token for @Constraint Metadata. } } Class create ConstraintToken -superclass MetadataToken ConstraintToken instproc print {} { set c "CONSTRAINT=[my set name]\n" append c [my printProperties] return $c } @ Class PackageToken -superclass MetadataToken { description { Token for Package metadata. Contains additional parameters: "version" of the package and "type"= either "require" or "provide". } } Class create PackageToken -superclass MetadataToken -parameter { {version ""} {type ""} } @ Class ObjToken -superclass MetadataToken { description { Token for Object metadata. Contains additional parameters: "procList" = list of all proc token and "cl"= class name. } } Class create ObjToken -superclass MetadataToken -parameter { {procList ""} cl } ObjToken instproc printProcs {} { set c " PROCS:\n" set pl [MetadataToken sortTokenList [my procList]] if {[my istype ClassToken]} { set pl [concat [MetadataToken sortTokenList [my instprocList]] $pl] } foreach p $pl { append c " [$p set name]\n" } return $c } ObjToken instproc print {} { set c "OBJECT=[my set name]\n" if {[my exists cl]} {append c " CLASS=[my set cl]\n"} if {[my exists heritage]} {append c " HERITAGE=[my set heritage]\n"} append c [my printProperties] set pl [MetadataToken sortTokenList [my procList]] if {[my istype ClassToken]} { set pl [concat [MetadataToken sortTokenList [my instprocList]] $pl] } foreach p $pl { append c [$p print] } return $c } @ Class ClassToken -superclass ObjToken { description { Token for Class metadata. Contains additional parameters: "instprocList" = list of all instproc token. } } Class create ClassToken -superclass ObjToken -parameter { {instprocList ""} } ClassToken instproc print {} { regsub "^OBJECT=" [next] "CLASS=" r return $r } @ Class MetaClassToken -superclass ClassToken { description { Token for Meta-Class metadata. } } Class create MetaClassToken -superclass ClassToken MetaClassToken instproc print {} { regsub "^CLASS=" [next] "META-CLASS=" r return $r } @ Class MethodToken -superclass MetadataToken { description { Token for Method metadata. Contains additional parameters: "arguments" of the method, "returnValue" of the method, "obj" name, "abstract" = 0 or 1 (whether its an abstract method or not). } } Class create MethodToken -superclass MetadataToken -parameter { arguments returnValue obj {abstract 0} } # Prints out method information MethodToken instproc print {} { set c " METHOD=[my set name], ARGUMENTS= " if {[my exists arguments]} { foreach {arg argDescription} [my set arguments] { # ignore argDescription and default values if {[llength $arg] > 1} {set arg [lindex $arg 0]} append c $arg " " } } append c "\n [my printProperties]" return $c } @ Class ProcToken -superclass MethodToken { description { Token for Proc metadata } } Class create ProcToken -superclass MethodToken ProcToken instproc print {} { regsub "^ METHOD=" [next] " PROC=" r return $r } @ Class InstprocToken -superclass MethodToken { description { Token for Instproc metadata. } } Class create InstprocToken -superclass MethodToken InstprocToken instproc print {} { regsub "^ METHOD=" [next] " INSTPROC=" r return $r } @ Class MetadataAnalyzer { description "Handler class for building a metadata runtime structure" } Class create MetadataAnalyzer -parameter { {objList ""} {packageList ""} {knownMetaclasses "Class"} {ns ""} fileToken {constraintList ""} } MetadataAnalyzer instproc init args { next } MetadataAnalyzer instproc handleMethod {obj type name {argList ""} {doc ""}} { #puts stderr "+++Method $type $name $argList $doc" set procClass ProcToken set objCl ObjToken if {$type eq "instproc"} { set procCl InstprocToken set objCl ClassToken } set t [$procClass create [my autoname ::xotcl::@::t]] set n [$t set name [string trimleft $name :]] $t set obj $obj set objFound 0 foreach o [my set objList] { if {[$o set name] == $obj} { set objFound 1 if {$type eq "instproc" && ![$o istype ClassToken]} { $o class ClassToken } break } } if {$objFound == 0} { set o [$objCl create [my autoname ::xotcl::@::t]] $o set name $obj my lappend objList $o } $o lappend ${type}List $t $t set arguments $argList $t evaluateMetadata $doc return $t } MetadataAnalyzer instproc handleObj {class name args} { my instvar knownMetaclasses objList extensions set objCl ObjToken if {[lsearch $class $knownMetaclasses] != -1} { set objCl ClassToken } # if an instproc/proc has created an entry for this obj/class # -> use it and overwrite it with new info if {[set idx [lsearch $name $objList]] != -1} { set t [lindex $objList $idx] $t class $objCl } else { set t [$objCl create [my autoname ::xotcl::@::t]] my lappend objList $t } $t set name $name set la [llength $args] # evaluate -superclass argument if {($la == 3 || $la == 2) && [lindex $args 0] eq "-superclass"} { set heritage [$t set heritage [lindex $args 1]] foreach h $heritage { if {[lsearch $h $knownMetaclasses] != -1} { # A new metaclass was defined lappend knownMetaclasses $name $t class MetaClassToken } } } # evaluate documentation set doc "" if {$la == 1} { set doc [lindex $args 0] } elseif {$la == 3} { set doc [lindex $args 2] } $t evaluateMetadata $doc $t set cl $class #puts stderr "+++Obj $name $args" } MetadataAnalyzer instproc handleFile doc { if {[my exists fileToken]} { [my set fileToken] evaluateMetadata $doc } } MetadataAnalyzer instproc handleConstraint {constraint name args} { set t [ConstraintToken create [my autoname ::xotcl::@::t]] my lappend constraintList $t $t set name $name set doc [lindex $args 0] $t evaluateMetadata $doc } MetadataAnalyzer instproc handlePackage args { #puts "$args" if {[llength $args] > 2} { set type [lindex $args 1] if {$type eq "provide" || $type eq "require"} { set t [PackageToken create [my autoname ::xotcl::@::t]] my lappend packageList $t $t set name [lindex $args 2] $t set type $type if {[llength $args] > 3} { $t set version [lindex $args 3] } } } } @ MetadataAnalyzer instproc print {} { description "Print all collected token information to stdout. This method is also an exmaple how the tokens can be used." } MetadataAnalyzer instproc print {} { my instvar extensions packageList set c "" if {[llength $packageList] > 0} { append c "PACKAGES:" foreach t $packageList { if {[$t type] eq "provide"} { append c " Package provided: [$t name] [$t version]\n" } elseif {[$t type] eq "require"} { append c " Package required: [$t name] [$t version]\n" } } } if {[my exists fileToken]} { append c [[my set fileToken] print] } if {[my exists constraintToken]} { append c [[my set constraintToken] print] } if {[info exists extensions]} { # Add list of extensions. foreach extension $extensions { append c "\nExtensions: [$extension name], " \ "Description: [$extension description]" } } set objList [MetadataToken sortTokenList [my objList]] if {[llength $objList]>0} { foreach obj $objList {append c [$obj print]} } return $c } @ Class AnalyzerCmd { description {Class that overload the unknown mechanism of @ to provide metadata analysis.} } Class create AnalyzerCmd -parameter { {analyzerObj ""} {onOff 0} } AnalyzerCmd instproc unknown args { my instvar analyzerObj onOff # puts stderr "AnalyzerCmd: [self args]" if {!$onOff} {return [next]} if {[llength $args] > 1} { set abstract 0 if {[lindex $args 1] eq "abstract"} { if {[llength $args] > 2} { set p [lindex $args 2] if {$p eq "proc" || $p eq "instproc"} { set args [lreplace $args 1 1] set abstract 1 } } } switch [lindex $args 1] { proc - instproc { set r [eval $analyzerObj handleMethod $args] if {$abstract} {$r abstract 1} return $r } default { switch [lindex $args 0] { @File { return [$analyzerObj handleFile [lindex $args 1]] } @Constraint { return [eval $analyzerObj handleConstraint $args] } default { return [eval $analyzerObj handleObj $args] } } } } } puts stderr "Unknown @ metadata: '$args'" } @ AnalyzerCmd @ { description {Recreate @ with metadata analyis funtionality.} } AnalyzerCmd create ::xotcl::@ namespace export \ MetadataToken FileToken ConstraintToken PackageToken ObjToken \ ClassToken MetaClassToken MethodToken ProcToken InstprocToken \ MetadataAnalyzer AnalyzerCmd } namespace import ::xotcl::metadataAnalyzer::* library/xotcl/library/lib/mixinStrategy.xotcl000066400000000000000000000056651242365656200220360ustar00rootroot00000000000000# -*- Tcl -*- package provide xotcl::mixinStrategy 2.0 package require XOTcl 2.0 namespace eval ::xotcl::mixinStrategy { namespace import ::xotcl::* @ @File { description { These methods provide support for managing "strategies", i.e. mixin-classes, where only one kind of a family of conformant mixins should be registered. <@p> Naming convertions for strategies: All strategies must follow the naming convention 'kind=implementation'. Examples are the persistency strategy 'eager' specfied as 'persistent=eager' or the persistency strategy 'lazy' (specified as 'persistent=lazy') }} @ Object instproc mixinStrategy {strategy "Strategy to be added" } { description { This method adds or replaces a new strategy from the mixin list. Strategies are named following the convention mentioned above. } return "old strategy" } Object instproc mixinStrategy {strategy} { regexp {:?([^:=]+)=} $strategy _ kind set mixins "" set oldStrategy "" foreach mixin [my info mixin] { if {[string match *${kind}=* $mixin]} { lappend mixins $strategy set oldStrategy $mixin } else { lappend mixins $mixin } } if {$oldStrategy eq ""} { lappend mixins $strategy } my mixin $mixins return $oldStrategy } @ Object instproc mixinQueryStrategy {kind "strategy kind"} { description { This method searches the mixin list for a mixin of this kind (starting with $kind=) } return "returns the maching strategy" } Object instproc mixinQueryStrategy {kind} { set m [my info mixin] return [::lindex $m [::lsearch -glob $m $kind=*]] } @ Object instproc add {construct "(inst) 'filter' or 'mixin'" args "to be added"} { description "add the specified (inst) 'filters' or 'mixins'" return "empty" } Object instproc add {kind args} { if {$kind != {instfilter} && $kind != {instmixin} && $kind != {filter} && $kind != {mixin}} { error "Usage: [self proc] ..." } ::set classes [my info $kind] eval ::lappend classes $args my $kind $classes #puts stderr "$kind of [self] are now: ´[my info $kind]´" } @ Object instproc remove {construct "(inst) 'filter' or 'mixin'" args "to be removed"} { description "remove the specified (inst) 'filters' or 'mixins'" return "empty" } Object instproc remove {kind args} { if {$kind != {instfilter} && $kind != {instmixin} && $kind != {filter} && $kind != {mixin}} { error "Usage: [self proc] ..." } ::set classes [my info $kind] foreach c $args { ::set pos [::lsearch $classes $c] if {$pos == -1} { error "$kind ´$c´ could not be removed" } else { set $classes [::lreplace $classes $pos $pos] } } my $kind $classes # puts stderr "$kind of [self] are now: ´[my info $kind]´" } } library/xotcl/library/lib/package.xotcl000066400000000000000000000100371242365656200205470ustar00rootroot00000000000000# -*- Tcl -*- package provide xotcl::package 2.0 package require XOTcl 2.0 package require xotcl::mixinStrategy # if pgk_mkIndex is running, there is a conflict with the # new command package provided here. We can detect when # pkg_mkIndex is running by checking for the namespace variable # ::tcl::file. if {![info exists ::tcl::file]} { rename package tcl_package } namespace eval ::xotcl::package { namespace import ::xotcl::* @ @File {description { Represent Tcl package loading command by an XOTcl object. Enables tracking, tracing, and verbose output of package loading } } @ Object package { description { Supports all Tcl package options plus present and verbose. } } @ package proc present {args "packageName or -exact packageName"} { description { Check whether a package is present or not. Similar to Tcl's package present, but works with Tcl < 8.3 } } @ package proc verbose {v "1 or 0"} { description { Toggle verbose output on/off. If on, package prints the locations from where packages are loaded to the screen. Default is off. } } set package_obj [Object create package] $package_obj set component . $package_obj set verbose 0 $package_obj proc unknown args { #puts stderr "unknown: package $args" namespace eval :: tcl_package $args } $package_obj proc verbose value { my set verbose $value } $package_obj proc present args { namespace eval :: tcl_package present $args } $package_obj proc require args { my instvar component verbose uses loaded set prevComponent $component if {[catch {set v [eval package present $args]} msg]} { #puts stderr "we have to load $msg" switch -exact -- [lindex $args 0] { -exact {set pkg [lindex $args 1]} default {set pkg [lindex $args 0]} } set component $pkg lappend uses($prevComponent) $component set v [namespace eval :: tcl_package require $args] if {$v ne "" && $verbose} { set path [lindex [tcl_package ifneeded $pkg $v] 1] puts "... $pkg $v loaded from '$path'" set loaded($pkg) $v ;# loaded stuff needed for Tcl 8.0 } } set component $prevComponent return $v } Object create package::tracker package::tracker set verbose 0 package::tracker proc storeEntry {table index} { my instvar verbose $table set ${table}($index) "[package set component] [info script]" if {$verbose} { puts "... $table $index loaded from [info script]" } } package::tracker proc dump {} { my instvar class object instproc proc if {[info exist class]} { parray class } if {[info exist object]} { parray object } if {[info exist instproc]} { parray instproc } if {[info exist proc]} { parray proc } } package::tracker proc start {} { ::Class add mixin [self]::M ::Object add mixin [self]::M } Class create package::tracker::M package::tracker::M instproc create {cls args} { set table [string tolower [string trimleft [self] :]] package::tracker storeEntry $table [lindex $args 0] next $cls add mixin [self class] } package::tracker::M instproc instproc args { package::tracker storeEntry instproc [self]->[lindex $args 0] next } package::tracker::M instproc proc args { package::tracker storeEntry proc [self]->[lindex $args 0] next } #package::tracker set verbose 1 #package::tracker start # #Class A #A instproc p args { # puts A #} #A proc pp args { # a call #} #Object o #o proc ppp args { # another call #} #puts stderr ==================================================== #package::tracker dump #puts stderr AUTO_PATH=$auto_path. namespace export package namespace eval package { namespace export tracker namespace eval tracker { namespace export M } } } namespace import ::xotcl::package::* namespace eval package { namespace import ::xotcl::package::package::* namespace eval tracker { namespace import ::xotcl::package::package::tracker::* } } library/xotcl/library/lib/pkgIndex-package.add000066400000000000000000000001211242365656200217060ustar00rootroot00000000000000package ifneeded xotcl::package 2.0 [list source [file join $dir package.xotcl]] library/xotcl/library/lib/staticMetadata.xotcl000066400000000000000000000047051242365656200221110ustar00rootroot00000000000000# -*- Tcl -*- package require XOTcl 2.0 package require xotcl::metadataAnalyzer package provide xotcl::staticMetadataAnalyzer 2.0 namespace eval ::xotcl::staticMetadataAnalyzer { namespace import ::xotcl::* @ @File { description { XOTcl file static analyzer for @ metadata. E.g. used for doumentation with xoDoc. I.e. allows for reading in a file and evaluating the metadata-related info only. } } @ Class StaticMetadataAnalyzer -superclass MetadataAnalyzer { description { Metadata analyzer class that allows for reading in files and evaluation of the metadata content in the file. } } Class create StaticMetadataAnalyzer -superclass MetadataAnalyzer \ -parameter {{namespace ::}} StaticMetadataAnalyzer instproc cmdsplit {cmd} { # from Jeffrey's tkcon set inc {} set cmds {} foreach cmd [split [string trimleft $cmd] \n] { if {{} ne $inc } { append inc \n$cmd } else { append inc [string trimleft $cmd] } if {[info complete $inc] && ![regexp {[^\\]\\$} $inc]} { if {[regexp "^\[^#\]" $inc]} {lappend cmds $inc} set inc {} } } if {[regexp "^\[^#\]" $inc]} {lappend cmds $inc} return $cmds } StaticMetadataAnalyzer instproc evaluateCommands {c} { my instvar namespace foreach command [my cmdsplit $c] { #puts stderr "$command===========================" if {[regexp "^ *:*@ " $command]} { #puts stderr "$command===========================" namespace eval $namespace $command } elseif {[regexp "^ *package " $command]} { #puts stderr "$command===========================" namespace eval $namespace [list [self] handlePackage $command] } elseif {[regexp "^ *namespace *eval *(\[^\{\]*) *\{(.*)\}\[^\}\]*$" $command _ namespace nsc]} { #puts stderr "$command===========================" namespace eval $namespace [list [self] evaluateCommands $nsc] } } } @ StaticMetadataAnalyzer instproc analyzeFile {name "File name"} { description "Analyze a file and build up a token structure for each metadata token in the file." } StaticMetadataAnalyzer instproc analyzeFile name { my set cmd "" set t [FileToken create [my autoname t]] $t set name $name my set fileToken $t set f [open $name r] set c [read $f] close $f ::@ onOff 1 my evaluateCommands $c ::@ onOff 0 } namespace export StaticMetadataAnalyzer } namespace import ::xotcl::staticMetadataAnalyzer::* library/xotcl/library/lib/trace.xotcl000066400000000000000000000215141242365656200202540ustar00rootroot00000000000000# -*- Tcl -*- $ package provide xotcl::trace 2.0 package require XOTcl 2.0 namespace eval ::xotcl::trace { namespace import ::xotcl::* @ @File {description { Various tracing tools for the XOTcl language. } } @ Object instproc traceFilter { args "arbitrary args" } { Description { Filter to trace every method call on an object or class hierarchy. Outputs a message befora and after each call of the traced object. } return "empty string" } @ Object Trace { Description { Write trace outputs and produce statistics. Variable traceStream defines where to write trace output (default: stderr). } } @ Trace proc puts {line "output line"} { Description { Define how traceFilter writes to the output stream. Default: write to trace stream. } } @ Trace proc openTraceFile {name "file name"} { Description { Redirect trace output to file. } } @ Trace proc closeTraceFile {name "file name"} { Description { Close trace file and redirect output to stderr. } } @ Object instproc lintFilter {} { Description {Experimental lint filter} } @ Object instproc statFilter {} { Description {Experimental statistics filter} } @ Object instproc showVars {args "ist of variables"} { Description {Show the values of the specified variables (or of all variables) of an object on stderr.} } @ Object instproc showMsg {msg "optional output"} { Description {Show a message msg with the form "[self] $cls->$method $msg" on stderr.} } @ Object instproc showClass {} { Description {Show classes and mixins of the object}} @ Object instproc showStack {maxDepth "max stack depth, default=100"} { Description {Show callstack up to the specified calldepth.}} @ Object instproc showCall {} { Description {Show the current call with the form "[self] $cls->$method $args" on stderr.}} @ Object instproc showTimeStart {"?handle?" "Handle object name, optional"} {Description {start a timer}} @ Object instproc showTimeEnd {"?handle?" "Handle object name, optional"} {Description {end a timer and show result}} ########################################################################## proc showCall {} { Trace deprecated-function showCall} proc showVars {} { Trace deprecated-function showVars} proc showObj {o {printObjectName 1}} { Trace deprecated-function showObj} proc showStack {{m 100}} { Trace deprecated-function showStack} Object Trace Trace set traceStream stderr Trace proc openTraceFile name { my set traceStream [open $name w] } Trace proc closeTraceFile {} { close $Trace::traceStream my set traceStream stderr } Trace proc puts line { puts $Trace::traceStream $line } Trace proc add {type obj} { if {[my isclass $obj]} { $obj instfilter add ${type}Filter } else { $obj filter add ${type}Filter } } Trace proc delete {type obj} { if {[my isclass $obj]} { $obj instfilter delete ${type}Filter } else { $obj filter delete ${type}Filter } } Trace proc statReset {} { catch {my unset stat} } Trace proc statReportClass c { if {[my exists stat($c)]} { puts "\nClass $c: [my set stat($c)] references" foreach method [$c info instprocs] { set key $c->$method if {[info exists stat($key)]} { puts "\t$key: [my set stat($key)] references" } else { puts "\t$key: not used" } } } else { puts "\nClass $c: not used" } foreach subclass [lsort [$c info subclass]] { my [self proc] $subclass } } Trace proc statReport {} { my statReportClass Object } Trace proc statCount {key} { puts stderr "[self] [self proc] '$key'" if {[my exists stat($key)]} { my incr stat($key) } else { my set stat($key) 1 } puts stderr "[self] [self proc] '$key' => [my set stat($key)]" } Trace proc deprecated-function {name} { puts stderr "Function <$name> is deprecated. Use method with same name instead." } Object instproc traceFilter args { # don't trace the Trace object if {[self] eq "::xotcl::trace::Trace"} {return [next]} set method [self calledproc] if {$method eq [self proc]} {return [next]} set context "[self callingclass]->[self callingproc]" switch -- $method { proc - instproc {set dargs [list [lindex $args 0] [lindex $args 1] ...] } default {set dargs $args } } #my showStack Trace puts "CALL $context> [self]->$method $dargs (next=[self next])" set result [next] Trace puts "EXIT $context> [self]->$method ($result)" return $result } Object instproc lintFilter args { #puts stderr c=[self class],ic[my info class],p=[self calledproc] #puts stderr " =====================METHOD='[self calledproc]'" my instvar __reported switch -exact -- [self calledproc] { instvar { set ccls [self callingclass] set method [self callingproc] #puts stderr ccls=$ccls. if {$ccls eq ""} { ;## instvar in proc set bod [my info body $method] set context "proc [self]->$method" } else { ;## instvar in instproc set bod [$ccls info instbody $method] set context "instproc $ccls->$method" } foreach v $args { set vpattern "$v\[^a-zA-Z0-9\]" if {[regexp "\[\$\]$vpattern" $bod]} continue if {[regexp " *$vpattern" $bod]} continue #if {[regexp "info *exists *$vpattern" $bod]} continue #if {[regexp "append *$vpattern" $bod]} continue #if {[regexp "array.*$vpattern" $bod]} continue if {[info exists __reported($v,$context)]} continue set __reported($v,$context) 1 puts stderr "'$v' of 'instvar $args' is NOT used in\n\ $context ... {$bod}" } } } next } Object instproc statFilter args { # don't return statistics from the Trace object #puts stderr "self=[self]" if {[self] eq "::xotcl::trace::Trace"} {return [next]} set ccls [self callingclass] set cmet [self callingproc] set met [self calledproc] #::puts stderr "cls=$ccls->$cmet, [self]->$met" Trace statCount $ccls Trace statCount $ccls->$cmet next } ###################################################################### # show**** methods # Object instproc showVars args { set msg {} if {$args == {}} { foreach var [lsort [my info vars]] { if {[my array exists $var]} { append msg "\n\t$var: " #puts stderr "ARRAY $var" #puts stderr "ARRAY names <[[self]array names $var]>" foreach i [lsort [my array names $var]] { append msg $i=[my set ${var}($i)] ", " } } elseif {[my exists $var]} { append msg "\n\t$var: " [list [my set $var]] } else { append msg "\n\t$var: " UNKNOWN } } } else { foreach var $args { if {[my array exists $var]} { lappend msg $var: ARRAY } elseif {[my exists $var]} { lappend msg $var: [my set $var] } else { lappend msg $var: UNKNOWN } } } set method [self callingproc] set cls [self callingclass] puts stderr "[self] $cls->$method $msg" #puts stderr " MIXINS: [my info mixin]" } Object instproc showMsg msg { set method [self callingproc] set cls [self callingclass] puts stderr "[self] $cls->$method $msg" } Object instproc showClass {} { set method [self callingproc] set cls [self callingclass] puts stderr "[self] $cls->$method class [my info class]\ mixins {[my info mixin]}" } Object instproc showStack {{m 100}} { set max [info level] if {$m<$max} {set max $m} puts stderr "Call Stack (level: command)" for {set i 0} {$i < $max} {incr i} { if {[catch {set s [uplevel $i self]} msg]} { set s "" } puts stderr "[format %5d -$i]:\t$s [info level [expr {-$i}]]" } } Object instproc showCall {} { set method [self callingproc] set cls [self callingclass] set args [lreplace [info level -1] 0 0] puts stderr "[self] $cls->$method $args" } Object instproc showTimeStart {{handle __h}} { upvar [self callinglevel] $handle obj set obj [Object [self]::[my autoname __time]] $obj set clicks [clock clicks] return } Object instproc showTimeEnd {{handle __h}} { upvar [self callinglevel] $handle obj set method [self callingproc] set cls [self callingclass] set elapsed [expr {([clock clicks]-[$obj set clicks])/1000000.0}] puts stderr "[self] $cls->$method: elapsed [format %.2f $elapsed]secs" $obj destroy } ###################################################################### namespace export showCall showVars showObj showStack Trace } namespace import ::xotcl::trace::* library/xotcl/library/lib/upvarcompat.xotcl000066400000000000000000000031041242365656200215120ustar00rootroot00000000000000# -*- Tcl -*- package provide xotcl::upvar-compat 2.0 package require XOTcl 2.0 namespace eval ::xotcl::upvar-compat { namespace import ::xotcl::* @ @File {description { Provide a version of upvar and uplevel that provide backward compatibility such that these commands ignore inactive filter and mixin frames (upvar behaves the same whether or not a filter is installed). Newer scripts should use <@TT>upvar/uplevel [self callinglevel] var/command instead. } } } # Define upvar and uplevel; use the level, if given explizitely: # otherwise point to the callinglevel from XOTcl rename ::uplevel ::xotcl::tcl_uplevel proc ::uplevel {lvl args} { set cl [::xotcl::tcl_uplevel 1 ::xotcl::self callinglevel] if {[string match #* $cl]} { # we were called from XOTcl, use the XOTcl method set cmd [concat [list my uplevel $lvl] $args] } else { # no XOTcl in sight, use tcl variant set cmd [concat [list ::xotcl::tcl_uplevel $lvl] $args] } #puts stderr cmd=$cmd set code [catch [list ::xotcl::tcl_uplevel 1 $cmd] msg] return -code $code $msg } rename ::upvar ::xotcl::tcl_upvar proc ::upvar {lvl args} { set cl [::xotcl::tcl_uplevel 1 ::xotcl::self callinglevel] if {[string match #* $cl]} { # we were called from XOTcl, use the XOTcl method set cmd [concat [list my upvar $lvl] $args] #set code [catch {my uplevel $lvl $args} msg] } else { # no XOTcl in sight, use tcl variant set cmd [concat [list ::xotcl::tcl_upvar $lvl] $args] } set code [catch [list ::xotcl::tcl_uplevel 1 $cmd] msg] return -code $code $msg } library/xotcl/library/lib/wafecompat.tcl000066400000000000000000000026021242365656200207320ustar00rootroot00000000000000# -*- Tcl -*- package provide xotcl::wafecompat 2.0 set WAFELIB /usr/lib/X11/wafe/ set MODULE_PATH "$WAFELIB $auto_path" set COMPONENT_PATH $WAFELIB/otcl-classes proc MOTIFPREFIX {} {return {}} proc requireModules modules { global MODULE_PATH foreach {cmd module} $modules { if {{} ne [info command $cmd] } continue if {[regexp {([A-Za-z1-9]+)Gen} $module _ n] || [regexp {lib([a-z]+)} $module _ n] || [regexp {^(.+)[.]so} $module _ n] } { set name [string toupper $n] } foreach path $MODULE_PATH { set f $path/tcllib/bin/$module if {[set found [file exists $f]]} { puts stderr "Loading module $name from $f" load $f $name break } } if {!$found} { error "Could not find module $module in {$MODULE_PATH}"} }} proc requireTclComponents {files} { global COMPONENT_PATH _componentLoaded foreach component $files { if {[info exists _componentLoaded($component)]} continue foreach path $COMPONENT_PATH { set f $path/$component if {[file exists $f]} { puts stderr "Loading source file $f" uplevel \#0 source $f set _componentLoaded($component) $f break } } if {![info exists _componentLoaded($component)]} { error "Could not find component $component in {$COMPONENT_PATH}" } }} proc addTimeOut {n cmd} { after $n $cmd } proc removeTimeOut {n} { after cancel $n } proc quit {} { exit } library/xotcl/library/lib/xodoc.xotcl000066400000000000000000000265241242365656200203000ustar00rootroot00000000000000# -*- Tcl -*- package require XOTcl 2.0 package provide xotcl::xodoc 2.0 package require xotcl::staticMetadataAnalyzer package require xotcl::htmllib namespace eval ::xotcl::xodoc { namespace import ::xotcl::* @ @File { description { XOTcl documentation tool. Overloads the command @, which is used as a documentation token. } } @ Class MetadataTokenHTML { description {Instmixin to provide HTML printing. Such instmixins are registered for all token types. } } Class create MetadataTokenHTML @ MetadataTokenHTML abstract instproc printHTML {} { description {Print token to HTML document object} } MetadataTokenHTML abstract instproc printHTML {} @ MetadataTokenHTML instproc getDocPropertiesHTML {} { description { Returns list of properties as HTML. } } MetadataTokenHTML instproc getDocPropertiesHTML {htmlDoc} { foreach p [my set properties] { $htmlDoc startTableRow -valign top if {[my exists $p]} { $htmlDoc startTableCell -valign top $htmlDoc addString " [my capitalize $p]:" $htmlDoc endTableCell $htmlDoc startTableCell -valign top if {$p eq "errorCodes"} { # Build table cell with list of error codes. foreach {code desc} [my set $p] { set code [string map [list < <\; > >\;] $code] set desc [string map [list < <\; > >\;] $desc] $htmlDoc addString "$code: $desc\n

    " } } else { $htmlDoc addString [my set $p] } $htmlDoc endTableCell } $htmlDoc endTableRow } } MetadataTokenHTML instproc reflowHTML {left paragraph} { #set result "" #foreach line [split $paragraph \n] { # if {![regexp {^ *$} $line]} { # append result "$left$line
    \n" # } #} #return $result return $paragraph } MetadataToken instmixin [concat [MetadataToken info instmixin] MetadataTokenHTML] @ Class FileTokenHTML -superclass MetadataTokenHTML Class create FileTokenHTML -superclass MetadataTokenHTML FileTokenHTML instproc printHTML {htmlDoc} { $htmlDoc addLineBreak $htmlDoc addString " Filename: " $htmlDoc addAnchor [my set name] -href [my set name] $htmlDoc addLineBreak $htmlDoc addLineBreak $htmlDoc startTable -border 0 my getDocPropertiesHTML $htmlDoc $htmlDoc endTable } FileToken instmixin [concat [FileToken info instmixin] FileTokenHTML] @ Class ConstraintTokenHTML -superclass MetadataTokenHTML Class create ConstraintTokenHTML -superclass MetadataTokenHTML ConstraintTokenHTML instproc printHTML {htmlDoc} { $htmlDoc addAnchor "" -name [my set name] $htmlDoc addString "

    Constraint: [my set name]

    " $htmlDoc addLineBreak $htmlDoc startTable -border 0 my getDocPropertiesHTML $htmlDoc $htmlDoc endTable } ConstraintToken instmixin [concat [ConstraintToken info instmixin] ConstraintTokenHTML] @ Class ObjTokenHTML -superclass MetadataTokenHTML Class create ObjTokenHTML -superclass MetadataTokenHTML ObjTokenHTML instproc getProcsHTML {htmlDoc} { set c "" set pl [MetadataToken sortTokenList [my procList]] if {[my istype ClassToken]} { set pl [concat [MetadataToken sortTokenList [my instprocList]] $pl] } foreach p $pl { set pn [$p set name] set label($pn) "$pn" } foreach l [lsort [array names label]] { if {$c ne ""} {append c ", "} append c $label($l) } if {$c ne ""} {append c "."} $htmlDoc addString "$c" } ObjTokenHTML instproc printHTML {htmlDoc} { $htmlDoc addAnchor "" -name [my set name] if {[my istype MetaClassToken]} { set start "

    MetaClass:" } elseif {[my istype ClassToken]} { set start "

    Class:" } else { set start "

    Object:" } $htmlDoc addString "$start [my set name]

    " if {[my exists cl]} { $htmlDoc addString "Class: [my set cl]" $htmlDoc addLineBreak } if {[my exists heritage]} { $htmlDoc addString "Heritage: [my set heritage]" $htmlDoc addLineBreak } set head "" if {[my procList] ne ""} {set head " Procs "} if {[my istype ClassToken]} { if {[my instprocList] ne ""} {set head " Procs/Instprocs: "} } $htmlDoc addString $head my getProcsHTML $htmlDoc $htmlDoc startTable -border 0 my getDocPropertiesHTML $htmlDoc $htmlDoc endTable } ObjToken instmixin [concat [ObjToken info instmixin] ObjTokenHTML] @ Class MethodTokenHTML -superclass MetadataTokenHTML Class create MethodTokenHTML -superclass MetadataTokenHTML # Prints out method information as HTML. MethodTokenHTML instproc printHTML {htmlDoc} { #my showVars set argText "\n" HtmlBuilder create args set a "Arguments:" set anchor [my set obj]-[my set name] $htmlDoc addAnchor "" -name $anchor if {[my abstract]} {$htmlDoc addString "abstract"} $htmlDoc addString "[my set name] " args set indentLevel [$htmlDoc set indentLevel] if {[my exists arguments]} { #set argText "\n" foreach {arg argDescription} [my set arguments] { if {[llength $arg] > 1} { # A default value was given to the argument. $htmlDoc addString "?[lindex $arg 0]?" set at "?[lindex $arg 0]?:$argDescription Default: \"[lindex $arg 1]\"." } else { $htmlDoc addString "$arg" set at "$arg: $argDescription" } args startTableRow -valign top args startTableCell -valign top args addString $a set a "" args endTableCell args startTableCell -valign top args addString $at args endTableCell args endTableRow } } $htmlDoc startTable -border 0 $htmlDoc addString [args toString] args destroy my getDocPropertiesHTML $htmlDoc $htmlDoc endTable #$htmlDoc endListItem } MethodToken instmixin [concat [MethodToken info instmixin] MethodTokenHTML] @ Class XODoc { description "Handler class for building a documentation database" } Class create XODoc -superclass StaticMetadataAnalyzer @ XODoc proc documentFileAsHTML { file "filename of the xotcl file to be documented" docdir "directory to which the html file is written" } { description "Uses the xoDoc package to produce an HTML documentation of a specified file ***.xotcl. The file is written to ***.html in docdir" return "file basename without suffix" } XODoc proc documentFileAsHTML {file docdir} { set docdb [XODoc create [XODoc autoname docdb]] ::@ set analyzerObj $docdb $docdb analyzeFile $file set ext [file extension $file] if {$ext ne ""} {set ext -[string trimleft $ext .]} set docfilename [file rootname [file tail $file]]$ext $docdb writeFile ${docdir}/$docfilename.html $file $docdb destroy return $docfilename } XODoc instproc printPackages {htmlDoc} { my instvar packageList $htmlDoc addString "

    Package/File Information

    " if {[llength $packageList] > 0} { foreach t $packageList { if {[$t type] eq "provide"} { $htmlDoc addString " Package provided: [$t name] [$t version]" } elseif {[$t type] eq "require"} { $htmlDoc addString " Package required: [$t name] [$t version]" } $htmlDoc addLineBreak } } else { $htmlDoc addString " No package provided/required " $htmlDoc addLineBreak } } XODoc instproc printExtensions {htmlDoc} { my instvar extensions if {[info exists extensions]} { # Add list of extensions. foreach extension $extensions { $htmlDoc addLineBreak $htmlDoc addString "

    Document extension: [$extension name]" $htmlDoc addString "Description: [$extension description]" $htmlDoc addLineBreak } } } XODoc instproc printObjList {htmlDoc} { set objList [MetadataToken sortTokenList [my objList]] if {[llength $objList]>0} { $htmlDoc addLineBreak $htmlDoc addString "Defined Objects/Classes: " $htmlDoc startUnorderedList foreach obj $objList { set on [$obj set name] $htmlDoc startListItem $htmlDoc addAnchor "$on:" -href "#$on" $obj getProcsHTML $htmlDoc $htmlDoc addLineBreak $htmlDoc endListItem } $htmlDoc endUnorderedList } } XODoc instproc printFileToken {htmlDoc} { if {[my exists fileToken]} { [my set fileToken] printHTML $htmlDoc } else { $htmlDoc addString " No file information. \n" } $htmlDoc addLineBreak } XODoc instproc printConstraintsList {htmlDoc} { set constraintList [MetadataToken sortTokenList [my constraintList]] if {[llength $constraintList]>0} { $htmlDoc addLineBreak $htmlDoc addString "Defined Constraints: " $htmlDoc startUnorderedList foreach c $constraintList { set cn [$c set name] $htmlDoc startListItem $htmlDoc addAnchor "$cn:" -href "#$cn" $htmlDoc addLineBreak $htmlDoc endListItem } $htmlDoc endUnorderedList } } XODoc instproc printConstraints {htmlDoc} { foreach c [my set constraintList] { $htmlDoc addHorizontalRule $htmlDoc startParagraph $c printHTML $htmlDoc $htmlDoc endParagraph } $htmlDoc addLineBreak } XODoc instproc printProcsList {htmlDoc list string} { if {[llength $list] > 0} { $htmlDoc addString "

    $string

    " $htmlDoc startUnorderedList foreach s $list { $htmlDoc startListItem $s printHTML $htmlDoc $htmlDoc endListItem } $htmlDoc endUnorderedList } } XODoc instproc printObjs {htmlDoc} { set objList [MetadataToken sortTokenList [my objList]] foreach t $objList { $htmlDoc addHorizontalRule $htmlDoc startParagraph $t printHTML $htmlDoc if {[$t istype ClassToken]} { my printProcsList $htmlDoc [$t set instprocList] Instprocs } my printProcsList $htmlDoc [$t set procList] Procs $htmlDoc endParagraph } } XODoc instproc replaceFormatTags {fc} { regsub -all <@ $fc < fc regsub -all " htmlDoc addImage -src "./logo-100.jpg" -alt "$name" -align MIDDLE htmlDoc addStringDecr "$name" htmlDoc addHorizontalRule htmlDoc startParagraph my printPackages htmlDoc my printExtensions htmlDoc my printObjList htmlDoc my printConstraintsList htmlDoc my printFileToken htmlDoc my printObjs htmlDoc my printConstraints htmlDoc htmlDoc endParagraph htmlDoc addHorizontalRule htmlDoc startParagraph htmlDoc endParagraph htmlDoc addAnchor "Back to index page." -href "./index.html" htmlDoc addLineBreak htmlDoc addHorizontalRule htmlDoc startParagraph htmlDoc endParagraph htmlDoc endDocument set r [my replaceFormatTags [htmlDoc toString]] htmlDoc destroy return $r } @ XODoc instproc writeFile { filename "file name destination" name "name of the html document" } { description "Create HTML docuemntation from metadata token and write to file " } XODoc instproc writeFile {filename name} { set content [my printHTML $name] set f [open $filename w] puts $f $content close $f } namespace export \ MetadataTokenHTML FileTokenHTML ConstraintTokenHTML ObjTokenHTML \ MethodTokenHTML XODoc } namespace import ::xotcl::xodoc::* library/xotcl/library/pkgIndex.tcl000066400000000000000000000001071242365656200176050ustar00rootroot00000000000000package ifneeded XOTcl 2.0.0 [list source [file join $dir xotcl2.tcl]] library/xotcl/library/serialize/000077500000000000000000000000001242365656200173215ustar00rootroot00000000000000library/xotcl/library/serialize/RecoveryPoint.xotcl000066400000000000000000000241601242365656200232070ustar00rootroot00000000000000# -*- Tcl -*- package provide xotcl::scriptCreation::recoveryPoint 2.0 package require XOTcl 2.0 namespace eval ::xotcl::scriptCreation::recoveryPoint { namespace import ::xotcl::* ## fehlt noch: filter, mixins, metadata, ass, assoption, etc ## beim recover Class's,Object's proc instproc vars nicht ueberschreiben ## filter dann anhaengen etc ... ## der Recovery Filter darf durch Object filter "" nicht gelöscht werden # # filter to ensure that recovering doesn't overwrite # existing objs/classes # Object instproc recoveryFilter args { ::set method [self calledproc] switch -- $method { create { # don't overwrite objects if {![::Object isobject [lindex $args 0]]} { next } else { # puts stderr "Recovery Filter: omitting [lindex $args 0]" } } proc { if {[lsearch [my info procs] [lindex $args 0]] == -1} { next } else { # puts stderr "Recovery Filter: omitting proc [self]::[lindex $args 0]" } } instproc { if {[lsearch [my info instprocs] [lindex $args 0]] == -1} { next } else { # puts stderr "Recovery Filter: omitting instproc [self]::[lindex $args 0]" } } set { if {[lsearch [my info vars] [lindex $args 0]] == -1} { next } else { # puts stderr "Recovery Filter: omitting var [self]::[lindex $args 0]" } } default {next} } } # # remove filter from object # Object instproc filterremove f { ::set fl [my info filter] puts stderr "filterremove on [self] with $f; fullName: [my filtersearch $f]" while {[::set index [lsearch $fl [my filtersearch $f]]] != -1} { ::set fl [lreplace $fl $index $index] } my filter $fl } # # remove mixin from object # Object instproc mixinremove m { puts stderr "mixinremove on [self] with $m" ::set ml [my info mixins] while {[::set index [lsearch $ml $m]] != -1} { ::set ml [lreplace $ml $index $index] } my mixin $ml } Class RecoveryPoint \ -parameter { {appendedObjs ""} {appendedCls ""} {appendedNamespaces ""} {withState 0} {appendToFile 0} {definedObjs [list Object \ Class \ Class::Parameter]} {excludeNames ""} } # # queries the definedObjs variable whether a given object # is already defined/predefined or not # -> a way to exclude classes/objs from saving # RecoveryPoint instproc isDefined {n} { my instvar definedObjs puts stderr "Checking Defined: $n in $definedObjs" if {[lsearch $definedObjs [string trimleft $n :]] == -1} { return 0 } else { return 1 } } RecoveryPoint instproc appendDefined {n} { my instvar definedObjs lappend definedObjs [string trimleft $n :] } # # check whether an obj/cls/namespace is appended already # append obj/cls/namespace # foreach method {Obj Cl Namespace} { set r { my instvar {appended${method}s name}} set r [subst -nocommands -nobackslash $r] set s $r append s { if {[lsearch $name [string trimleft $n :]] == -1} { return 0 } else { return 1 } } RecoveryPoint instproc isAppended$method {n} $s append r { lappend name [string trimleft $n :] } RecoveryPoint instproc append$method {n} $r } # # compare command for lsort # RecoveryPoint instproc namespaceDepth {a b} { set aCount 0 set bCount 0 for {set i 0} {$i < [string length $a]} {incr i} { if {[string index $a $i] eq ":"} { incr aCount } } for {set i 0} {$i < [string length $b]} {incr i} { if {[string index $b $i] eq ":"} { incr bCount } } if {$aCount == $bCount} { return 0 } elseif {$aCount > $bCount} { return 1 } return -1 } # # produces a script containing the current state of # the given obj # RecoveryPoint instproc stateScript {obj} { set script "" foreach v [$obj info vars] { if {[lsearch [my set excludeNames] $v] == -1} { $obj instvar $v if {[array exists $v]} { foreach name [array names $v] { set arr ${v}($name) set value [$obj set $arr] append script "$obj set $arr \"$value\"\n" } } else { set value [set $v] append script "$obj set $v \"$value\"\n" } } } return $script } # # produces a script containing the procs of the given obj # RecoveryPoint instproc procScript {obj} { set script "" foreach p [$obj info procs] { if {[lsearch [my set excludeNames] $v] == -1} { append script \ "$obj proc $p \{[$obj info args $p]\} \{[$obj info body $p]\}\n" } } return $script } # # produces a script containing the instprocs of the given class # RecoveryPoint instproc instprocScript {cl} { set script "" foreach p [$cl info instprocs] { if {[lsearch [my set excludeNames] $v] == -1} { append script \ "$cl instproc $p \{[$cl info instargs $p]\} \{[$cl info instbody $p]\}\n" } } return $script } # # append parent obj/classes/namespaces of an object completly # RecoveryPoint instproc appendParents {name} { # puts stderr "Recovery -- appendParents $name " set p "" set script "" set n $name while {[set np [namespace parent ::$n]] != "::"} { lappend p $np set n $np } set p [lsort -command {[self] namespaceDepth} $p] foreach n $p { if {[Object isobject $n]} { if {[$n isclass]} { append script [my classScript $n] } else { append script [my objectScript $n] } } else { if {![my isAppendedNamespace $n]} { append script "namespace eval $n \{\}\n" # puts stderr "Recovery -- Appending Namespace: $n" my appendedNamespace $n } } } return $script } # # produces a script recovering the given obj with all children # without state # RecoveryPoint instproc objectScript {obj} { # puts stderr "Recovery -- Object Script $obj" my instvar withState set script "" if {![my isDefined $obj] && ![my isAppendedObj $obj]} { # if the object's class is not yet appended => do it now set objClass [$obj info class] append script [my classScript $objClass] # append all parent namespaces append script [my appendParents $obj] # append the obj append script "$objClass $obj\n" append script [my procScript $obj] if {$withState == 1} { append script [my stateScript $obj] } # puts stderr "Recovery -- Appending Object: $obj" my appendObj $obj # append its children foreach o [$obj info children] { append script [my objectScript $o] } } return $script } # # produces a script recovering the given class with all children # without state # RecoveryPoint instproc classScript {cl} { # puts stderr "Recovery -- Class Script $cl" my instvar withState set script "" if {![my isDefined $cl] && ![my isAppendedCl $cl]} { # if the class's meta-class is not yet appended => do it now set metaClass [$cl info class] append script [my classScript $metaClass] # append all parent namespaces append script [my appendParents $cl] # append the class append script "$metaClass $cl" set sl [$cl info superclass] if {$sl ne ""} { append script " -superclass \{$sl\}\n" } else { append script "\n" } append script [my instprocScript $cl] append script [my procScript $cl] if {$withState == 1} { append script [my stateScript $cl] } # puts stderr "Recovery -- Appending Class: $cl \n $script" my appendCl $cl # append children set children [$cl info children] set classChildren [$cl info classchildren] foreach c $children { if {[lsearch $classChildren $c] != -1} { append script [my classScript $c] } else { append script [my objectScript $c] } } } return $script } # # produces a script recovering the given class and all subclasses # with all their children and all instances # # RecoveryPoint instproc hierarchyScript {cl} { set script [my classScript $cl] set sortedInstances \ [lsort -command {[self] namespaceDepth} [$cl info instances]] foreach o $sortedInstances { append script [my objectScript $o] } foreach c [$cl info subclass] { append script [my hierarchyScript $c] } return $script } # # saves a script to a file # RecoveryPoint instproc saveScript {filename script} { my instvar appendToFile if {$appendToFile} { set mode a } else { set mode w } set f [open $filename $mode] puts $f $script close $f } # # load a script from a file # RecoveryPoint instproc loadScript {filename} { set f [open $filename r] set r [read $f] close $f return $r } # # produce methods to save/recover an object script to/from a file # with/without state/only state # foreach method { Object ObjectState ObjectWithState Class ClassWithState \ Hierarchy HierarchyWithState } { set s { my set withState } if {[regexp {(.*)WithState} $method _ m]} { set call $m append s "1" } else { set call $method append s "0" } scan $call %c l set ::low "[format %c [expr {$l + 32}]][string range $call 1 end]" append s { my appendedObjs "" my appendedCls "" my appendedNamespaces "" } append s " foreach a \$args \{" set r { set script [my ${low}Script } set r [subst -nocommands -nobackslash $r] append s $r append s {$a] my saveScript $filename $script} append s " \} " RecoveryPoint instproc save$method {filename args} $s } RecoveryPoint instproc recover {filename} { set r [my loadScript $filename] Object filterappend recoveryFilter # puts stderr "RecoveryFilter appended for $filename" eval $r Object filterremove recoveryFilter # puts stderr "RecoveryFilter removed for $filename" return } namespace export RecoveryPoint } namespace import ::xotcl::scriptCreation::recoveryPoint::* library/xotcl/library/serialize/ScriptCreator.xotcl000066400000000000000000000120731242365656200231630ustar00rootroot00000000000000# -*- Tcl -*- package provide xotcl::scriptCreation::scriptCreator 2.0 package require XOTcl 2.0 namespace eval ::xotcl::scriptCreation::scriptCreator { namespace import ::xotcl::* Class ScriptCreator \ -parameter { {excludedObjs {Object Class Class::Parameter}} {excludeNames ""} {dependencyChecking 1} } # # queries the excludedObjs variable whether a given object # is already defined/predefined or not # -> a way to exclude classes/objs from saving # ScriptCreator instproc isExcluded {n} { my instvar excludedObjs #puts stderr "Checking Excluded: $n in $excludedObjs" if {[lsearch $excludedObjs [string trimleft $n :]] == -1} { return 0 } else { return 1 } } ScriptCreator instproc appendExcluded {n} { my instvar excludedObjs lappend excludedObjs [string trimleft $n :] } # # compare command for lsort # ScriptCreator instproc namespaceDepth {a b} { set aCount 0 set bCount 0 for {set i 0} {$i < [string length $a]} {incr i} { if {[string index $a $i] eq ":"} { incr aCount } } for {set i 0} {$i < [string length $b]} {incr i} { if {[string index $b $i] eq ":"} { incr bCount } } if {$aCount == $bCount} { return 0 } elseif {$aCount > $bCount} { return 1 } return -1 } # # produces a script containing the current state of # the given obj # ScriptCreator instproc stateScript {obj} { set script "" foreach v [$obj info vars] { if {[lsearch [my set excludeNames] $v] == -1} { if {[$obj array exists $v]} { foreach name [$obj array names $v] { set arr ${v}($name) set value [$obj set $arr] append script "$obj set $arr \"$value\"\n" } } else { set value [$obj set $v] append script "$obj set $v \"$value\"\n" } } } return $script } # # produces a script containing the procs of the given obj # ScriptCreator instproc procScript {obj} { set script "" foreach p [$obj info procs] { if {[lsearch [my set excludeNames] $p] == -1} { append script \ "$obj proc $p \{[$obj info args $p]\} \{[$obj info body $p]\}\n" } } return $script } # # produces a script containing the instprocs of the given class # ScriptCreator instproc instprocScript {cl} { set script "" foreach p [$cl info instprocs] { if {[lsearch [my set excludeNames] $p] == -1} { append script \ "$cl instproc $p \{[$cl info instargs $p]\} \{[$cl info instbody $p]\}\n" } } return $script } # # saves a script to a file # ScriptCreator instproc saveScript {filename script} { set f [open $filename w] puts $f $script close $f } # # load a script from a file # ScriptCreator instproc loadScript {filename} { set f [open $filename r] set r [read $f] close $f return $r } # # check parent obj/classes/namespaces of an object completly # ScriptCreator instproc checkParents {name} { set p "" set n $name while {[set np [namespace parent ::$n]] != "::"} { lappend p $np set n $np } set p [lsort -command {my namespaceDepth} $p] foreach n $p { if {![my isExcluded $n] && ![my isAppended $n]} { error "ScriptCreator: $name needs parent $n, neither appended nor excluded yet." } } } ScriptCreator instproc checkClass {obj class} { if {![my isExcluded $class] && ![my isAppended $class]} { error "ScriptCreator: $obj depends on $class, neither appended nor excluded yet." } } ScriptCreator instproc isAppended name { set n [string trimleft $name :] if {[lsearch [my set appendedNames] $n]!=-1} { return 1 } else { return 0 } } ScriptCreator instproc appendName name { set n [string trimleft $name :] my lappend appendedNames $n } ScriptCreator instproc makeScript args { my instvar dependencyChecking my set appendedNames "" set script "" foreach name $args { #puts stderr "Script Creator -- $name" if {![my isExcluded $name] && ![my isAppended $name]} { if {$dependencyChecking} { my checkParents $name } if {[Object isobject $name]} { set class [$name info class] if {$dependencyChecking} { my checkClass $name $class } if {[Object isclass $name]} { # append the class #puts stderr "Appending Class: $name" append script "[$name info class] $name" set sl [$name info superclass] if {$dependencyChecking} { foreach c $sl { my checkClass $name $c } } if {$sl ne ""} { append script " -superclass \{$sl\}\n" } else { append script "\n" } append script [my instprocScript $name] } else { # append the obj #puts stderr "Appending Object: $name" append script "[$name info class] $name\n" } append script [my procScript $name] } else { append script "namespace eval $name \{\}\n" #puts stderr "Appending Namespace: $name" } my appendName $name } } return $script } namespace export ScriptCreator } namespace import ::xotcl::scriptCreation::scriptCreator::* library/xotcl/library/serialize/Serializer.xotcl000066400000000000000000000002161242365656200225040ustar00rootroot00000000000000# -*- Tcl -*- # offer old package name for backward minimal compatibility package provide xotcl::serializer 2.0 package require nx::serializerlibrary/xotcl/library/xotcl2.tcl000066400000000000000000001522321242365656200172560ustar00rootroot00000000000000# -*- tcl -*- ############################################################ # xotcl2.tcl -- # # Implementation of the XOTcl 2 object system, based # on the Next Scripting Framework (NSF). # # Copyright (C) 2010-2014 Gustaf Neumann # Copyright (C) 2010-2014 Stefan Sobernig # # Vienna University of Economics and Business # Institute of Information Systems and New Media # A-1020, Welthandelsplatz 1 # Vienna, Austria # # This work is licensed under the MIT License http://www.opensource.org/licenses/MIT # # Copyright: # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # package provide XOTcl 2.0.0 package require nx ####################################################### # Classical ::xotcl* ####################################################### namespace eval ::xotcl { # # Set XOTcl version variables # set ::xotcl::version 2.0 set ::xotcl::patchlevel .0 namespace eval ::nsf {} ;# make pkg-indexer happy namespace eval ::nsf::parameter {} ;# make pkg-indexer happy set ::nsf::bootstrap ::xotcl # # Perform the basic setup of XOTcl. First, let us allocate the # basic classes of XOTcl. This call creates the classes # ::xotcl::Object and ::xotcl::Class and defines these as root class # of the object system and as root meta class. # ::nsf::objectsystem::create ::xotcl::Object ::xotcl::Class { -class.alloc alloc -class.create create -class.dealloc dealloc -class.configureparameter __class_configureparameter -class.recreate recreate -object.configure configure -object.configureparameter __object_configureparameter -object.cleanup cleanup -object.defaultmethod defaultmethod -object.destroy destroy -object.init init -object.move move -object.unknown unknown -slot.set value=set -slot.get value=get } # # create ::nx and ::nsf namespaces, otherwise mk_pkgindex will fail # namespace eval ::nx {} namespace eval ::nsf {} namespace eval ::nsf::method::create {} # # get frequently used primitiva into the ::xotcl namespace # namespace import ::nsf::configure ::nsf::my ::nsf::finalize ::nsf::interp namespace import ::nsf::method::alias ::nsf::is interp alias {} ::xotcl::next {} ::nsf::xotclnext interp alias {} ::xotcl::relation {} ::nsf::relation::set proc ::xotcl::self {{arg ""}} { switch $arg { "" {uplevel ::nsf::self} next { set handle [uplevel ::nsf::current nextmethod] method_handle_to_xotcl $handle } default {uplevel ::nsf::current $arg} } } # @object ::xotcl::Object # # XOTcl programs are constructed out of objects. This class # describes common structural and behavioral features for all XOTcl # objects. It is the root object-class in the XOTcl 2 object system. # provide the standard command set for ::xotcl::Object ::nsf::method::alias Object autoname ::nsf::methods::object::autoname ::nsf::method::alias Object class ::nsf::methods::object::class ::nsf::method::alias Object cleanup ::nsf::methods::object::cleanup ::nsf::method::alias Object configure ::nsf::methods::object::configure ::nsf::method::alias Object defaultmethod ::nsf::methods::object::defaultmethod ::nsf::method::alias Object destroy ::nsf::methods::object::destroy ::nsf::method::alias Object exists ::nsf::methods::object::exists # ::nsf::method::alias Object init ::nsf::methods::object::init ::nsf::method::alias Object instvar ::nsf::methods::object::instvar ::nsf::method::alias Object noinit ::nsf::methods::object::noinit ::nsf::method::alias Object residualargs ::nsf::methods::object::residualargs # ::nsf::method::alias Object unknown ::nsf::methods::object::unknown ::nsf::method::alias Object uplevel ::nsf::methods::object::uplevel ::nsf::method::alias Object upvar ::nsf::methods::object::upvar ::nsf::method::alias Object volatile ::nsf::methods::object::volatile # # object methods # # @method ::xotcl::Object#autoname # # Provides a facility for auto-generating object identifiers. It is # constructed from a seeding string which is appended a numeric # index. This numeric index is incremented upon each call to # {{{autoname}}}. # {{{ # set obj [Object new] # $obj autoname a; # yields "a1" # $obj autoname -instance B; # yields "b1" # $obj autoname a; # yields "a2" # $obj autoname b; # yields "b1" # $obj autoname -reset a; # "" # $obj autoname -reset -instance B; # "" # $obj autoname -instance a; # yields "a1", and NOT "a3"! # $obj autoname -instance B; # yields "b1" # $obj autoname b; # yields "b2" # }}} # The seeding string may also contain {{{[format]}}} expressions (see ...): # {{{ # $obj autoname a%06d; # gives you "a000001", ... # }}} # # @param -instance Have the generated name start with a lower letter (though the seed string has a major first letter) # @param -reset Reset the object-internal counter for a given seed string # @param name The seeding string which is used as a base for name generation # @return The generated name string # @method ::xotcl::Object#cleanup # # TODO: this is a method not used in the Next Scripting Langauge. This # method is just called via recreate, so everything necessary can be # performed there as well. However, it is available for backward # compatibility available in XOTcl 2.0 # # Resets an object or class to its initial state, as after object # allocation (see {{@method ::xotcl::Class class alloc}}). This method # participates in recreating objects, i.e, it is called during the # recreation process by {{@method ::xotcl::Class class recreate}}. # Depending on the recreation scheme applied (see {{@command # ::nsf::configure}}, object variables are deleted, per-object # namespaces are cleared, and the object's relationships (e.g., mixin # relations) are reset. # # @properties interally-called # @method ::xotcl::Object#destroy # # @use ::xotcl::Object#destroy # @method ::xotcl::Object#exists # # A helper method for checking whether the variable {{{var}}} is # defined on the object and assigned a value. You may use a variable # name with or without prefix, both will resolve to the object scope: # {{{ # $obj eval { # set :foo 1 # set bar 2 # } # # $obj exists foo; # returns 1 # $obj exists :foo; # returns 1 # $obj exists bar; # returns 0 # $obj exists :bar; # returns 0 # }}} # # @param var The name of the variable to verify # @return :boolean 1 if the variable exists, 0 otherwise # @method ::xotcl::Object#instvar # # @param args # @method ::xotcl::Object#noinit # # Calling upon this method during object construction allows you to # bypass the constructor method: # {{{ # Class create C # C instproc init {} {puts stderr "A class-specific constructor shouts out ..."} # C c1 -noinit # }}} # This bypassing feature comes handy when streaming an object into a # scripted form (e.g., by using the bundled Serializer). Upon # deserializing the object, using the {{{noinit}}} flag helps you to # preserve the serialized object state (rather then having the # object re-initialized). # @method ::xotcl::Object#requireNamespace # # This method allows you to request the creation of a namespace for # the given object, a per-object namespace. The namespace is then used # to store instance variables, methods and nested objects. Per-object # namespaces are needed for using and binding object variables to # non-object scopes in Tcl and Tk. For instance, you may use an # per-object namespace to have object variables accessible Tk widgets # and Tk callbacks. To verify whether a per-object namespace is # available for an object, see ... # # Beware that there is a difference between per-object namespaces and # Tcl namespaces which shadow an existing object (i.e., carry the same # name): # {{{ # Object create Foo # Foo requireNamespace # namespace exists Foo; # returns 1 # Foo info hasnamespace; # returns 1 # # Object create Bar # namespace eval ::Bar {} # namespace exists Bar; # returns 1 # Bar info hasnamespace; # returns 0 # }}} # provide some Tcl-commands as methods for ::xotcl::Object ::nsf::method::alias Object append -frame object ::append ::nsf::method::alias Object array -frame object ::array ::nsf::method::alias Object eval -frame object ::eval ::nsf::method::alias Object incr -frame object ::incr ::nsf::method::alias Object lappend -frame object ::lappend ::nsf::method::alias Object set -frame object ::set ::nsf::method::alias Object subst -frame object ::subst ::nsf::method::alias Object trace -frame object ::trace ::nsf::method::alias Object unset -frame object ::unset # @method ::xotcl::Object#vwait # # A method variant of the Tcl {{{vwait}}} command. You can use it to # have the {{{interp}}} enter an event loop until the specified # variable {{{varname}}} is set on the object. # # @param varName The name of the signaling object variable. ::nsf::method::create Object vwait {varName} { if {[regexp {:[^:]*} $varName]} { error "invalid varName '$varName'; only plain or fully qualified variable names allowed" } if {[string match ::* $varName]} { ::vwait $varName } else { ::vwait :$varName } } # provide the standard command set for ::xotcl::Class ::nsf::method::alias Class alloc ::nsf::methods::class::alloc ::nsf::method::alias Class create ::nsf::methods::class::create ::nsf::method::alias Class dealloc ::nsf::methods::class::dealloc ::nsf::method::alias Class new ::nsf::methods::class::new ::nsf::method::alias Class recreate ::nsf::methods::class::recreate ::nsf::method::alias Class superclass ::nsf::methods::class::superclass # protect some methods against redefinition ::nsf::method::property Object destroy redefine-protected true ::nsf::method::property Class alloc redefine-protected true ::nsf::method::property Class dealloc redefine-protected true ::nsf::method::property Class create redefine-protected true # # define parametercmd and instparametercmd in terms of ::nsf::setter # define filterguard and instfilterguard in terms of filterguard # define mixinguard and instmixinguard in terms of mixinguard # ::nsf::method::forward Object parametercmd ::nsf::method::setter %self -per-object ::nsf::method::forward Class instparametercmd ::nsf::method::setter %self ::nsf::method::alias Object filterguard ::nsf::methods::object::filterguard ::nsf::method::alias Class instfilterguard ::nsf::methods::class::filterguard ::nsf::method::alias Object mixinguard ::nsf::methods::object::mixinguard ::nsf::method::alias Class instmixinguard ::nsf::methods::class::mixinguard ::nsf::method::alias Object requireNamespace ::nsf::methods::object::requirenamespace # define instproc and proc ::nsf::method::create Class instproc { name arguments:parameter,0..* body precondition:optional postcondition:optional } { set conditions [list] if {[info exists precondition]} {lappend conditions -precondition $precondition} if {[info exists postcondition]} {lappend conditions -postcondition $postcondition} ::nsf::method::create [self] $name $arguments $body {*}$conditions } ::nsf::method::create Object proc { name arguments body precondition:optional postcondition:optional } { set conditions [list] if {[info exists precondition]} {lappend conditions -precondition $precondition} if {[info exists postcondition]} {lappend conditions -postcondition $postcondition} ::nsf::method::create [self] -per-object $name $arguments $body {*}$conditions } # define a minimal implementation of "method" Object instproc method {name arguments:parameter,0..* body} { :proc $name $arguments $body } Class instproc method {-per-object:switch name arguments:parameter,0..* body} { if {${per-object}} { :proc $name $arguments $body } else { :instproc $name $arguments $body } } # define forward methods # # We could nearly define forward via forwarder # # ::nsf::method::forward Object forward ::nsf::method::forward %self -per-object # ::nsf::method::forward Class instforward ::nsf::method::forward %self # # but since we changed the name of -objscope in nsf to -objframe, we # have to provide the definition the hard way via methods. Object instproc forward { method -default -earlybinding:switch -methodprefix -objscope:switch -onerror -verbose:switch target:optional args } { set arglist [list] if {[info exists target] && [string range $target 0 0] eq "-"} { error "target '$target' must not start with a dash" } if {[info exists default]} {lappend arglist -default $default} if {$earlybinding} {lappend arglist -earlybinding} if {[info exists methodprefix]} {lappend arglist -prefix $methodprefix} if {$objscope} {lappend arglist -frame object} if {[info exists onerror]} {lappend arglist -onerror $onerror} if {$verbose} {lappend arglist -verbose} if {[info exists target]} {lappend arglist $target} if {[llength $args] > 0} {lappend arglist {*}$args} set r [::nsf::method::forward [self] -per-object $method {*}$arglist] return $r } Class instproc instforward { method -default -earlybinding:switch -methodprefix -objscope:switch -onerror -verbose:switch target:optional args } { set arglist [list] if {[info exists target] && [string range $target 0 0] eq "-"} { error "target '$target' must not start with a dash" } if {[info exists default]} {lappend arglist -default $default} if {$earlybinding} {lappend arglist -earlybinding} if {[info exists methodprefix]} {lappend arglist -prefix $methodprefix} if {$objscope} {lappend arglist -frame object} if {[info exists onerror]} {lappend arglist -onerror $onerror} if {$verbose} {lappend arglist -verbose} if {[info exists target]} {lappend arglist $target} if {[llength $args] > 0} {lappend arglist {*}$args} set r [::nsf::method::forward [self] $method {*}$arglist] return $r } Class instproc unknown {args} { #puts stderr "use '[self] create $args', not '[self] $args'" uplevel [list [self] create {*}$args] } Object instproc unknown {m args} { if {![self isnext]} { error "[self]: unable to dispatch method '$m'" } } # "init" must exist on Object. per default it is empty. Object instproc init args { if {![::nsf::current isnextcall] && [llength $args] > 0 && [::nsf::configure debug] > 0} { ::nsf::log Warning "Arguments '$args' to constructor of object [self] are most likely not processed" } } Object instproc self {} {::xotcl::self} # # Method objectparameter, backwards upward compatible. We use # here the definition of parametersfromslots from nx.tcl # ::xotcl::Object instproc __object_configureparameter {} { set slotObjects [nsf::directdispatch [self] ::nsf::methods::object::info::lookupslots -type ::nx::Slot] set parameterDefinitions [::nsf::parameter::specs $slotObjects] lappend parameterDefinitions args:alias,method=residualargs,args } ::xotcl::Class instproc __class_configureparameter {} { set slotObjects [nsf::directdispatch [self] ::nsf::methods::class::info::slotobjects -closure -type ::nx::Slot] set parameterDefinitions [::nsf::parameter::specs $slotObjects] lappend parameterDefinitions args:alias,method=residualargs,args } ###################################################################### # Define default accessors for all parameters ###################################################################### ::nsf::method::create Object __default_accessor args {return public} ::nsf::method::property Object __default_accessor call-protected true # # Use parameter definition from nx # (same with classInfo parameter, see below) #::nsf::method::alias ::xotcl::Class parameter ::nsf::classes::nx::Class::attributes ::xotcl::Class instproc parameter {arglist} { set slotContainer [::nx::slotObj [::nsf::self]] foreach arg $arglist { #puts stderr "PARAMETER: [self] ::nsf::classes::nx::Class::property -accessor public $arg" [self] ::nsf::classes::nx::Class::property -class ::xotcl::Attribute -accessor public $arg ::nsf::method:::setter [self] [lindex $arg 0] } ::nsf::var::set $slotContainer __parameter $arglist } # We provide a default value for superclass (when no superclass is # specified explicitly) and metaclass, in case they should differ # from the root classes of the object system. proc createBootstrapVariableSlots {class definitions} { foreach att $definitions { if {[llength $att]>1} {lassign $att att default} set slotObj [::nx::slotObj $class $att] #puts stderr "::nx::BootStrapVariableSlot create $slotObj" ::nx::BootStrapVariableSlot create $slotObj if {[info exists default]} { #puts stderr "::nsf::var::set $slotObj default $default" ::nsf::var::set $slotObj default $default unset default } # # register the standard setter # ::nsf::method::setter $class $att # # make setter protected # #regexp {^([^:]+):} $att . att #::nsf::method::property $class $att call-protected true # # set for every bootstrap property slot the position 0 # ::nsf::var::set $slotObj position 0 ::nsf::var::set $slotObj configurable 1 } } createBootstrapVariableSlots ::xotcl::Class { {__default_superclass ::xotcl::Object} {__default_metaclass ::xotcl::Class} } ############################################ # Register system slots ############################################ # We need fully qualified "::xotcl" prefixes, since prefix # completion would skip the object system root namespace nx::MetaSlot create ::xotcl::RelationSlot -superclass ::nx::RelationSlot ::nsf::method::alias ::xotcl::RelationSlot value=assign ::nsf::relation::set set cSlotContainer [::nx::slotObj ::xotcl::Class] set oSlotContainer [::nx::slotObj ::xotcl::Object] ::xotcl::RelationSlot create ${cSlotContainer}::superclass \ -defaultmethods {get set} #::nsf::method::alias ${cSlotContainer}::superclass value=set ::nsf::relation::set ::xotcl::RelationSlot create ${oSlotContainer}::class -elementtype class -multiplicity 1..1 \ -defaultmethods {get set} #::nsf::method::alias ${oSlotContainer}::class value=set ::nsf::relation::set ::xotcl::RelationSlot create ${oSlotContainer}::mixin -forwardername object-mixin \ -defaultmethods {get set} \ -elementtype mixinreg -multiplicity 0..n ::xotcl::RelationSlot create ${oSlotContainer}::filter -forwardername object-filter \ -defaultmethods {get set} \ -elementtype filterreg -multiplicity 0..n ::xotcl::RelationSlot create ${cSlotContainer}::instmixin -forwardername class-mixin \ -defaultmethods {get set} \ -elementtype mixinreg -multiplicity 0..n ::xotcl::RelationSlot create ${cSlotContainer}::instfilter -forwardername class-filter \ -defaultmethods {get set} \ -elementtype filterreg -multiplicity 0..n ######################## # Info definition ######################## Object create ::xotcl::objectInfo Object create ::xotcl::classInfo ::nsf::object::property ::xotcl::objectInfo keepcallerself true ::nsf::object::property ::xotcl::classInfo keepcallerself true ::nsf::object::property ::xotcl::objectInfo perobjectdispatch true ::nsf::object::property ::xotcl::classInfo perobjectdispatch true # note, we are using ::xotcl::infoError, defined below #Object instforward info -onerror ::nsf::infoError ::xotcl::objectInfo %1 {%@2 %self} #Class instforward info -onerror ::nsf::infoError ::xotcl::classInfo %1 {%@2 %self} # # error handler for info # #proc ::nsf::infoerror msg { # #puts stderr "INFO ERROR: <$msg>\n$::errorInfo" # regsub -all " " $msg "" msg # regsub -all " " $msg "" msg # regsub {\"} $msg "\"info " msg # error $msg "" #} ::nsf::method::alias Object info ::xotcl::objectInfo ::nsf::method::alias Class info ::xotcl::classInfo # # Backward compatibility info subcommands; # # TODO: should go finally into a library. # # Obsolete methods # # already emulated: # # => info -per-object method parameter .... replaces # info instargs # info instnonposargs # info instdefault # # => info method .... replaces # info body # info instbody # # => info methods .... replaces # info commands # info instcommands # info procs # info instprocs # info parametercmd # info instparametercmd # # => info is (resp. ::xotcl::is) replaces # info isobject # info isclass # info ismetaclass # info ismixin # info istype # # => info method .... replaces # proc # instproc # info args # info nonposargs # info default # # TODO mark all obsolete calls at least as deprecated in library # proc ::xotcl::info_args {scope o method} { set result [list] foreach \ argName [$o ::nsf::methods::${scope}::info::method args $method] \ flag [$o ::nsf::methods::${scope}::info::method parameter $method] { if {[string match -* $flag]} continue lappend result $argName } #puts stderr "+++ get ${inst}args for $o $method => $result" return $result } proc ::xotcl::info_nonposargs {scope o method} { set result [list] foreach flag [$o ::nsf::methods::${scope}::info::method parameter $method] { if {![string match -* $flag]} continue lappend result $flag } #puts stderr "+++ get ${inst}nonposargs for $o $method => $result" return $result } proc ::xotcl::info_default {scope o method arg varName} { foreach \ argName [$o ::nsf::methods::${scope}::info::method args $method] \ flag [$o ::nsf::methods::${scope}::info::method parameter $method] { if {$argName eq $arg} { # we are in a proc, so using builtin "upvar" is safe upvar $varName default if {[llength $flag] == 2} { set default [lindex $flag 1] #puts "--- get $scope default for $o $method $arg => setting default to '$default'" return 1 } #puts "--- get $scope default for $o $method $arg fails" set default "" return 0 } } error "procedure \"$method\" doesn't have an argument \"$arg\"" } proc ::xotcl::info_forward_options {list} { set result [list] set i 0 for {set i 0} {$i < [llength $list]} {incr i} { switch -glob -- [lindex $list $i] { -frame { lappend result -objscope incr i } -prefix { lappend result -methodprefix incr i lappend result [lindex $list $i] } -* {lappend result [lindex $list $i]} default { lappend result {*}[lrange $list $i end] break } } } return $result } # define temporary method "alias" Object instproc alias {name cmd} {::nsf::method::alias [self] $name $cmd} objectInfo eval { :proc args {method} {::xotcl::info_args object [self] $method} :proc body {methodName} {my ::nsf::methods::object::info::method body $methodName} :proc check {} {::xotcl::checkoption_internal_to_xotcl1 [::nsf::method::assertion [self] check]} :alias class ::nsf::methods::object::info::class :alias children ::nsf::methods::object::info::children :proc commands {pattern:optional} { set cmd [list ::nsf::methods::object::info::methods -type all] if {[info exists pattern]} {lappend cmd $pattern} return [my {*}$cmd] } :proc default {method arg varName} { # # We are called from object "objectInfo". The true caller is 2 # levels up. # :upvar 2 $varName defaultVar # pass varName to be able produce the right error message set r [::xotcl::info_default object [self] $method $arg defaultVar] #if {$r == 1 && ![info exists defaultVar]} {error inconsistency} #puts "--- objectInfo default: var '$varName' level=[info level]" return $r } :proc filter {-order:switch -guards:switch pattern:optional} { set guardsFlag [expr {$guards ? "-guards" : ""}] set patternArg [expr {[info exists pattern] ? [list $pattern] : ""}] if {$order && !$guards} { set def [::nsf::directdispatch [::nsf::current object] \ ::nsf::methods::object::info::lookupfilters \ {*}$guardsFlag \ {*}$patternArg] set def [method_handles_to_xotcl $def] } else { set def [::nsf::directdispatch [::nsf::current object] \ ::nsf::methods::object::info::filters \ {*}$guardsFlag \ {*}$patternArg] } #puts stderr " => $def" return $def } :alias filterguard ::nsf::methods::object::info::filterguard :proc forward {-definition:switch name:optional} { if {$definition} { if {![info exists name]} {error "option -definition requires name of forwarding method to be specified" } set def [my ::nsf::methods::object::info::forward -definition $name] return [::xotcl::info_forward_options $def] } else { return [my ::nsf::methods::object::info::forward {*}[self args]] } } :alias hasnamespace ::nsf::methods::object::info::hasnamespace :proc invar {} {::nsf::method::assertion [self] object-invar} :proc methods { -nocmds:switch -noprocs:switch -nomixins:switch -incontext:switch pattern:optional } { set type all if {$nocmds} {set type scripted} if {$noprocs} {if {$nocmds} {return ""}; set type builtin} set cmd [list ::nsf::methods::object::info::lookupmethods -type $type] if {$nomixins} {lappend cmd -nomixins} if {$incontext} {lappend cmd -incontext} if {[info exists pattern]} {lappend cmd $pattern} my {*}$cmd } :proc mixin {-order:switch -guards:switch pattern:optional} { if {$order} { set cmd ::nsf::methods::object::info::lookupmixins } else { set cmd ::nsf::methods::object::info::mixins } if {$guards} {lappend cmd "-guards"} if {[info exists pattern]} {lappend cmd $pattern} my {*}$cmd } :alias mixinguard ::nsf::methods::object::info::mixinguard :proc nonposargs {method} {::xotcl::info_nonposargs object [self] $method} :proc parametercmd {name} {::nsf::classes::nx::Object::setter [self] $name} :alias parent ::nsf::methods::object::info::parent :proc post {methodName} {my ::nsf::methods::object::info::method post $methodName} :proc pre {methodName} {my ::nsf::methods::object::info::method pre $methodName} :proc procs {pattern:optional} { set cmd [list ::nsf::methods::object::info::methods -type scripted] if {[info exists pattern]} {lappend cmd $pattern} return [my {*}$cmd] } :alias slots ::nsf::methods::object::info::slotobjects :alias precedence ::nsf::methods::object::info::precedence :alias vars ::nsf::methods::object::info::vars } # # copy all methods from Object.info to Class.info # foreach m [objectInfo ::nsf::methods::object::info::methods] { ::nsf::method::alias classInfo $m [objectInfo ::nsf::methods::object::info::method registrationhandle $m] } classInfo eval { :alias classchildren ::nsf::methods::object::info::children :alias classparent ::nsf::methods::object::info::parent :proc default {method arg varName} { # # We are called from object "classInfo". The true caller is 2 # levels up. # :upvar 2 $varName defaultVar set r [::xotcl::info_default object [self] $method $arg defaultVar] #puts "--- classInfo default: var '$varName' level=[info level] result $r exists [info exists defaultVar]" #if {$r == 1 && ![info exists defaultVar]} {error inconsistency} return $r } :proc heritage {pattern:optional} { set cmd [list ::nsf::methods::class::info::superclass -closure] if {[info exists pattern]} {lappend cmd $pattern} return [my {*}$cmd] } :alias instances ::nsf::methods::class::info::instances :proc instargs {method} {::xotcl::info_args class [self] $method} :proc instbody {methodName} {my ::nsf::methods::class::info::method body $methodName} :proc instcommands {pattern:optional} { set cmd [list ::nsf::methods::class::info::methods] if {[info exists pattern]} {lappend cmd $pattern} return [my {*}$cmd] } :proc instdefault {method arg varName} { # # We are called from object "classInfo". The true caller is 2 # levels up. # :upvar 2 $varName defaultVar set r [::xotcl::info_default class [self] $method $arg defaultVar] #puts "--- classInfo instdefault: var '$varName' level=[info level]" #if {$r == 1 && ![info exists defaultVar]} {error inconsistency} return $r } :alias instfilter ::nsf::methods::class::info::filters :alias instfilterguard ::nsf::methods::class::info::filterguard #:alias instforward ::nsf::methods::class::info::forward :proc instforward {-definition:switch name:optional} { if {$definition} { if {![info exists name]} {error "option -definition requires name of forwarding method to be specified" } set def [my ::nsf::methods::class::info::forward -definition $name] return [::xotcl::info_forward_options $def] } else { return [my ::nsf::methods::class::info::forward {*}[self args]] } } :proc instinvar {} {::nsf::method::assertion [self] class-invar} :proc instmixin {-order:switch -guards:switch pattern:optional} { set cmd ::nsf::methods::class::info::mixins if {$order} {lappend cmd "-heritage"} if {$guards} {lappend cmd "-guards"} if {[info exists pattern]} {lappend cmd $pattern} my {*}$cmd } :alias instmixinguard ::nsf::methods::class::info::mixinguard :proc instmixinof {-closure:switch pattern:optional} { set cmd [list ::nsf::methods::class::info::mixinof -scope class] if {$closure} {lappend cmd -closure} if {[info exists pattern]} {lappend cmd $pattern} return [my {*}$cmd] } :proc instparametercmd {pattern:optional} { set cmd [list ::nsf::methods::class::info::methods -type setter] if {[info exists pattern]} {lappend cmd $pattern} return [my {*}$cmd] } :proc instnonposargs {method} {::xotcl::info_nonposargs class [self] $method} :proc instpost {methodName} {my ::nsf::methods::class::info::method postcondition $methodName} :proc instpre {methodName} {my ::nsf::methods::class::info::method precondition $methodName} :proc instprocs {pattern:optional} { set cmd [list ::nsf::methods::class::info::methods -type scripted] if {[info exists pattern]} {lappend cmd $pattern} return [my {*}$cmd] } :proc mixinof {-closure:switch pattern:optional} { set cmd [list ::nsf::methods::class::info::mixinof -scope object] if {$closure} {lappend cmd -closure} if {[info exists pattern]} {lappend cmd $pattern} return [my {*}$cmd] } #:alias parameter ::nx::Class::slot::__info::attributes :proc parameter {} { set slotContainer [::nx::slotObj [::nsf::self]] if {[::nsf::var::exists $slotContainer __parameter]} { return [::nsf::var::set $slotContainer __parameter] } return "" } :alias slots ::nsf::methods::class::info::slotobjects :alias subclass ::nsf::methods::class::info::subclass :alias superclass ::nsf::methods::class::info::superclass } # define "info info" objectInfo method info {} {::nx::internal::infoOptions ::xotcl::objectInfo} classInfo method info {} {::nx::internal::infoOptions ::xotcl::classInfo} # define "info unknown" objectInfo proc unknown {method args} { error "[::xotcl::self] unknown info option \"$method\"; [:info info]" } classInfo proc unknown {method args} { error "[::xotcl::self] unknown info option \"$method\"; [:info info]" } # # end if info # # remove temporary method "alias" Object instproc alias {} {} # emulation of object::exists, isclass ... Object instproc isobject {{object:substdefault "[self]"}} {::nsf::object::exists $object} Object instproc isclass {{class:substdefault "[self]"}} {::nsf::is class $class} Object instproc ismetaclass {{class:substdefault "[self]"}} {::nsf::is metaclass $class} Object instproc ismixin {class} { return [expr {[::nsf::is class $class] && [my ::nsf::methods::object::info::hasmixin $class]}] } Object instproc istype {class} { return [expr {[::nsf::is class $class] && [::nsf::directdispatch [self] ::nsf::methods::object::info::hastype $class]}] } # definition of "xotcl::Object contains", based on nx ::nsf::method::alias Object contains ::nsf::classes::nx::Object::contains # definition of "xotcl::Class slots", based on contains ::xotcl::Class instproc slots {cmd} { set slotContainer [::nx::slotObj [self]] uplevel [list [self] contains -object $slotContainer $cmd] } # # provide a stub function to allow reuse of copy of nx # ::nsf::method::create Object __resolve_method_path { -per-object:switch -verbose:switch path } { set object [::nsf::self] return [list object $object methodName $path regObject $object] } ::nsf::method::property Object __resolve_method_path call-protected true # assertion handling proc checkoption_xotcl1_to_internal checkoptions { set options [list] foreach option $checkoptions { if {$option eq "invar"} { lappend options "object-invar" } elseif {$option eq "instinvar"} { lappend options "class-invar" } else { lappend options $option } } return $options } proc checkoption_internal_to_xotcl1 checkoptions { set options [list] foreach option $checkoptions { if {$option eq "object-invar"} { lappend options "invar" } elseif {$option eq "class-invar"} { lappend options "instinvar" } else { lappend options $option } } return $options } proc method_handles_to_xotcl definitions { set defs [list] foreach def $definitions {lappend defs [method_handle_to_xotcl $def]} return $defs } proc method_handle_to_xotcl methodHandle { set definition [::nx::Object info method definition $methodHandle] #puts "method_handle_to_xotcl raw definition '$methodHandle' // $definition" if {$definition ne ""} { set obj [lindex $definition 0] set modifier [lindex $definition 2] if {$modifier eq "object"} { set prefix "" set kind [lindex $definition 3] set name [lindex $definition 4] } else { set prefix [expr {[::nsf::is class $obj] ? "inst" : ""}] set kind $modifier set name [lindex $definition 3] } if {$kind eq "method"} { set kind proc } elseif {$kind eq "setter"} { set kind parametercmd } elseif {$kind eq "alias"} { set kind "cmd" set name [lindex $definition 3] } set definition [list [lindex $definition 0] ${prefix}$kind $name] } #puts "method_handle_to_xotcl gets handle '$methodHandle' // $definition" return $definition } Object instproc check {checkoptions} { ::nsf::method::assertion [self] check [::xotcl::checkoption_xotcl1_to_internal $checkoptions] } Object instforward invar ::nsf::method::assertion %self object-invar Class instforward instinvar ::nsf::method::assertion %self class-invar Object instproc abstract {methtype methname arglist} { if {$methtype ne "proc" && $methtype ne "instproc" && $methtype ne "method"} { error "invalid method type '$methtype', \ must be either 'proc', 'instproc' or 'method'." } set arglist2 $arglist regsub -all {\"} $arglist {\\"} arglist2 :$methtype $methname $arglist " if {!\[::xotcl::self isnextcall\]} { error \"Abstract method $methname $arglist2 called\" } else {::xotcl::next} " # balance " for emacs syntax highlighter } # support for XOTcl specific convenience routines Object instproc hasclass cl { if {![::nsf::is class $cl]} {return 0} if {[::nsf::directdispatch [self] ::nsf::methods::object::info::hasmixin $cl]} {return 1} ::nsf::directdispatch [self] ::nsf::methods::object::info::hastype $cl } Object instproc filtersearch {filter} { set handle [::nsf::directdispatch [::nsf::current object] \ ::nsf::methods::object::info::lookupfilter $filter] return [method_handle_to_xotcl $handle] } Object instproc procsearch {name} { set handle [::nsf::directdispatch [::nsf::current object] \ ::nsf::methods::object::info::lookupmethod $name] return [method_handle_to_xotcl $handle] } Class instproc allinstances {} { # TODO: mark it deprecated return [:info instances -closure] } # keep old object interface for XOTcl Object proc unsetExitHandler {} {::nsf::exithandler unset} Object proc setExitHandler {newbody} {::nsf::exithandler set $newbody} Object proc getExitHandler {} {::nsf::exithandler get} # reuse some definitions from next scripting ::nsf::method::alias ::xotcl::Object copy ::nsf::classes::nx::Object::copy ::nsf::method::alias ::xotcl::Object move ::nsf::classes::nx::Object::move #::nsf::method::alias ::xotcl::Object defaultmethod ::nsf::classes::nx::Object::defaultmethod #::nsf::method::alias ::xotcl::Class -per-object __unknown ::nx::Class::__unknown ::nsf::method::create ::xotcl::Class -per-object __unknown {name} {} ::nsf::object::unknown::add xotcl {::xotcl::Class __unknown} proc myproc {args} {linsert $args 0 [uplevel ::nsf::self]} proc myvar {var} {[uplevel ::nsf::self] requireNamespace; return [uplevel ::nsf::self]::$var} # # create ::xotcl::MetaSlot for better compatibility with XOTcl 1 # ::nx::Class create ::xotcl::MetaSlot -superclass ::nx::MetaSlot { :property parameter :method init {} { if {[info exists :parameter]} {my ::nsf::classes::xotcl::Class::parameter ${:parameter}} next } :public forward instproc %self public method :public forward proc %self public object method # # As NX/XOTcl hybrids, all slot kinds would not inherit the # unknown behaviour of ::xotcl::Class. Therefore, we provide it # explicitly to slots for backward compatibility ... # :public alias unknown ::nsf::classes::xotcl::Class::unknown } # # Create ::xotcl::Attribute for compatibility # ::xotcl::MetaSlot create ::xotcl::Attribute -superclass ::nx::VariableSlot { :public alias value=set ::nsf::var::set #:property defaultmethods {get set} :property -accessor public multivalued { # # The slot object is an nx object, therefore we need the nx # "set" variant no matter what xotcl2 uses. # :public object method value=set {object property value} { set mClass [expr {$value ? "0..n" : "1..1"}] $object configure -incremental $value -multiplicity $mClass } :public object method value=get {object property} { return [$object eval [list :isMultivalued]] } } :protected method setterRedefinedOptions {} { if {[:info lookup method value=set] ne "::nsf::classes::xotcl::Attribute::value=set"} { # In case the "set" method was provided on the slot, ask nsf to call it directly return [list slot=[::nsf::self] slotset] } if {[:info lookup method value=get] ne "::nsf::classes::nx::VariableSlot::value=get"} { # In case the "get" method was provided on the slot, ask nsf to call it directly return [list slot=[::nsf::self]] } } :protected method defineIncrementalOperations {options_single options} { # # Just define these setter methods, when these are not defined # jet. We need the methods as well for e.g. private properties, # where the setting of the property is handled via slot. # if {[:info lookup method value=set] eq "::nsf::classes::xotcl::Attribute::value=set"} { set args [list obj var [:namedParameterSpec {} value $options]] :public object method value=set $args {::nsf::var::set $obj $var $value} } if {[:isMultivalued] && [:info lookup method value=add] eq "::nsf::classes::nx::VariableSlot::value=add"} { lappend options_single slot=[::nsf::self] set args [list obj prop [:namedParameterSpec {} value $options_single] {pos 0}] :public object method value=add $args {::nsf::next} } else { # TODO should we deactivate add/delete? } } :protected method needsForwarder {} { # # We just forward, when # * "set", "get" and "add" are still untouched, or # * or incremental is specified # if {[:info lookup method value=set] ne "::nsf::classes::xotcl::Attribute::value=set"} {return 1} if {[:info lookup method value=add] ne "::nsf::classes::nx::VariableSlot::value=add"} {return 1} if {[:info lookup method value=get] ne "::nsf::classes::nx::VariableSlot::value=get"} {return 1} if {[info exists :settername]} {return 1} if {!${:incremental}} {return 0} #if {![:isMultivalued]} {return 0} #puts stderr "--------------- [self] ismultivalued" return 1 } :public method createForwarder {name domain} { ::nsf::method::forward $domain \ -per-object=${:per-object} \ $name \ -prefix value= \ ${:manager} \ "%1 {get set}" %self \ ${:forwardername} } :public method __object_configureparameter {} { set slotObjects [nsf::directdispatch [self] ::nsf::methods::object::info::lookupslots -type ::nx::Slot] set parameterDefinitions [::nsf::parameter::specs -nonposargs $slotObjects] lappend parameterDefinitions args:alias,method=residualargs,args return $parameterDefinitions } :method init args { # # Via XOTcl calling convention, init gets the residual arguments # passed. Since nx does not allow this, we simply ignore the passed # arguments $args. # nsf::next "" } # provide minimal compatibility :public alias proc ::nsf::classes::xotcl::Object::proc :public method exists {var} {::nsf::var::exists [self] $var} :public method istype {class} [::nx::Object info method body ::nsf::classes::xotcl::Object::istype] :public alias set -frame object ::set :public alias residualargs ::nsf::methods::object::residualargs :public alias instvar ::nsf::methods::object::instvar ::nsf::method::setter [self] name ::nsf::method::setter [self] domain ::nsf::method::setter [self] default } # # Provide a backward compatible version of ::xotcl::alias # ::nsf::proc ::xotcl::alias { obj:object methodName -per-object:switch -objscope:switch target } { ::nsf::method::alias \ $obj \ {*}[expr {${per-object} ? "-per-object" : ""}] \ $methodName \ {*}[expr {${objscope} ? "-frame object" : ""}] \ $target } Object create ::xotcl::config config proc load {obj file} { source $file foreach i [array names ::auto_index [list $obj *proc *]] { set type [lindex $i 1] set meth [lindex $i 2] if {[$obj info ${type}s $meth] == {}} { $obj $type $meth auto $::auto_index($i) } } } config proc mkindex {meta dir args} { set sp {[ ]+} set st {^[ ]*} set wd {([^ ;]+)} foreach creator $meta { ::lappend cp $st$creator${sp}create$sp$wd ::lappend ap $st$creator$sp$wd } foreach methodkind {proc instproc} { ::lappend mp $st$wd${sp}($methodkind)$sp$wd } foreach cl [concat ::xotcl::Class [::xotcl::Class info heritage]] { ::lappend meths {*}[$cl info instcommands] } set old [pwd] cd $dir ::append idx "# Tcl autoload index file, version 2.0\n" ::append idx "# xotcl additions generated with " ::append idx "\"::xotcl::config::mkindex [list $meta] [list $dir] $args\"\n" set oc 0 set mc 0 foreach file [glob -nocomplain -- {*}$args] { if {[catch {set f [open $file]} msg]} then { catch {close $f} cd $old error $msg } while {[gets $f line] >= 0} { foreach c $cp { if {[regexp $c $line x obj]==1 && [string index $obj 0]!={$}} then { ::incr oc ::append idx "set auto_index($obj) " ::append idx "\"::xotcl::config::load $obj \$dir/$file\"\n" } } foreach a $ap { if {[regexp $a $line x obj]==1 && [string index $obj 0]!={$} && [lsearch -exact $meths $obj]==-1} { ::incr oc ::append idx "set auto_index($obj) " ::append idx "\"::xotcl::config::load $obj \$dir/$file\"\n" } } foreach m $mp { if {[regexp $m $line x obj ty pr]==1 && [string index $obj 0]!={$} && [string index $pr 0]!={$}} then { ::incr mc ::append idx "set \{auto_index($obj " ::append idx "$ty $pr)\} \"source \$dir/$file\"\n" } } } close $f } set t [open tclIndex a+] puts $t $idx nonewline close $t cd $old return "$oc objects, $mc methods" } # # if cutTheArg not 0, it cut from upvar argsList # Object instproc extractConfigureArg {al name {cutTheArg 0}} { set value "" upvar $al argList set largs [llength $argList] for {set i 0} {$i < $largs} {incr i} { if {[lindex $argList $i] == $name && $i + 1 < $largs} { set startIndex $i set endIndex [expr {$i + 1}] while {$endIndex < $largs && [string first - [lindex $argList $endIndex]] != 0} { lappend value [lindex $argList $endIndex] incr endIndex } } } if {[info exists startIndex] && $cutTheArg != 0} { set argList [lreplace $argList $startIndex [expr {$endIndex - 1}]] } return $value } Object create ::xotcl::rcs rcs proc date string { lreplace [lreplace $string 0 0] end end } rcs proc version string { lindex $string 2 } # # package support # # puts this for the time being into XOTcl # ::xotcl::Class instproc uses list { foreach package $list { ::xotcl::package import -into [::xotcl::self] $package puts stderr "*** using ${package}::* in [::xotcl::self]" } } ::nx::Class create ::xotcl::package -superclass ::nx::Class { :property provide :property {version 1.0} :property {autoexport {}} :property {export {}} :public object method create {name args} { set nq [namespace qualifiers $name] if {$nq ne "" && ![namespace exists $nq]} {Object create $nq} next } :public object method extend {name args} { :require $name $name configure {*}$args } :public object method contains script { if {[info exists :provide]} { package provide [set :provide] [set :version] } else { package provide [::xotcl::self] [set :version] } namespace eval [::xotcl::self] {namespace import ::xotcl::*} #namespace eval [::xotcl::self] $script #::nsf::directdispatch [::xotcl::self] -frame method ::apply [list {} $script [::xotcl::self]] ::apply [list {} $script [::xotcl::self]] foreach e [set :export] { set nq [namespace qualifiers $e] if {$nq ne ""} { namespace eval [::xotcl::self]::$nq [list namespace export [namespace tail $e]] } else { namespace eval [::xotcl::self] [list namespace export $e] } } foreach e [set :autoexport] { namespace eval :: [list namespace import [::xotcl::self]::$e] } } :public object method unknown args { #puts stderr "unknown: package $args" [set :packagecmd] {*}$args } :public object method verbose value { set :verbose $value } :public object method present args { if {$::tcl_version<8.3} { switch -exact -- [lindex $args 0] { -exact {set pkg [lindex $args 1]} default {set pkg [lindex $args 0]} } if {[info exists :loaded($pkg)]} { return ${:loaded}($pkg) } else { error "not found" } } else { [set :packagecmd] present {*}$args } } :public object method import {{-into ::} pkg} { :require $pkg namespace eval $into [subst -nocommands { #puts stderr "*** package import ${pkg}::* into [namespace current]" namespace import ${pkg}::* }] # import subclasses if any foreach e [$pkg export] { set nq [namespace qualifiers $e] if {$nq ne ""} { namespace eval $into$nq [list namespace import ${pkg}::$e] } } } :public object method require args { #puts "XOTCL package require $args, current=[namespace current]" set prevComponent ${:component} if {[catch {set v [package present {*}$args]} msg]} { #puts stderr "we have to load $msg" switch -exact -- [lindex $args 0] { -exact {set pkg [lindex $args 1]} default {set pkg [lindex $args 0]} } set :component $pkg lappend :uses($prevComponent) ${:component} set v [uplevel \#1 [set :packagecmd] require $args] if {$v ne "" && ${:verbose}} { set path [lindex [::package ifneeded $pkg $v] 1] puts "... $pkg $v loaded from '$path'" set :loaded($pkg) $v ;# loaded stuff needed for Tcl 8.0 } } set :component $prevComponent return $v } set :component . set :verbose 0 set :packagecmd ::package } unset -nocomplain cmd unset ::nsf::bootstrap # Documentation stub object -> just ignore per default. # if xoDoc is loaded, documentation will be activated ::xotcl::Object create ::xotcl::@ ::xotcl::@ proc unknown args {} set ::xotcl::confdir ~/.xotcl set ::xotcl::logdir $::xotcl::confdir/log namespace import ::nsf::tmpdir # finally, export contents defined for XOTcl namespace export Object Class Attribute myproc myvar my self next @ # # Provide parametersyntax for methods, which do not have a spec # # Tcl commands set ::nsf::parameter::syntax(::append) "/varName/ ?/value/ ...?" set ::nsf::parameter::syntax(::array) "/option/ /arrayName/ ?/arg/ ...?" set ::nsf::parameter::syntax(::eval) "/arg/ ?/arg/ ...?" set ::nsf::parameter::syntax(::incr) "/varName/ ?/increment/?" set ::nsf::parameter::syntax(::lappend) "/varName/ ?/value/ ...?" set ::nsf::parameter::syntax(::set) "/varName/ ?/value/?" set ::nsf::parameter::syntax(::set) "/varName/ ?/value/?" set ::nsf::parameter::syntax(::subst) "?-nobackslashes? ?-nocommands? ?-novariables? /string/" set ::nsf::parameter::syntax(::trace) "/option/ ?/arg/ ...?" set ::nsf::parameter::syntax(::unset) "?-nocomplain? ?--? ?/name/ ...?" set ::nsf::parameter::syntax(::nsf::classes::xotcl::Object::invar) "?/expr/?" set ::nsf::parameter::syntax(::nsf::classes::xotcl::Class::instinvar) "?/expr/?" set ::nsf::parameter::syntax(::nsf::classes::xotcl::Object::parametercmd) "/name/" set ::nsf::parameter::syntax(::nsf::classes::xotcl::Class::instparametercmd) "/name/" set ::nsf::parameter::syntax(::nsf::classes::xotcl::Class::slots) "/cmds/" # slots set ::nsf::parameter::syntax(::nsf::classes::xotcl::Object::class) "?/class/?" set value "?/class .../?|?add /class/?|?delete /class/?" set ::nsf::parameter::syntax(::nsf::classes::xotcl::Object::mixin) $value set ::nsf::parameter::syntax(::nsf::classes::xotcl::Class::instmixin) $value set ::nsf::parameter::syntax(::nsf::classes::xotcl::Class::superclass) $value set value "?/filters/?|?add /filter/?|?delete /filter/?" set ::nsf::parameter::syntax(::nsf::classes::xotcl::Object::filter) $value set ::nsf::parameter::syntax(::nsf::classes::xotcl::Class::instfilter) $value unset value } if {[::nsf::configure debug] > 1} { foreach ns {::xotcl} { puts "vars of $ns: [info vars ${ns}::*]" puts stderr "$ns exports: [namespace eval $ns {lsort [namespace export]}]" } puts stderr "======= XOTcl $::xotcl::version$::xotcl::patchlevel loaded" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/xotcl/tests/000077500000000000000000000000001242365656200150305ustar00rootroot00000000000000library/xotcl/tests/slottest.xotcl000066400000000000000000000475261242365656200200020ustar00rootroot00000000000000# -*- Tcl -*- package prefer latest package require XOTcl 2.0; namespace import -force ::xotcl::* package require nx::test nx::test configure -count 1000 # what's new: # - slots instances are manager objects for slot values # - generalization of slots to have different kind of domains and managers # - slots for objects and classes (slot parameter 'per-object' true|false, # when to used on a class object) # - works for mixins/filters/class/superclass (e.g ... superclass add ::M) # - defaultcmd and valuecmd # defaultcmd: is executed when the instance variable is read the first time # valuecmd: is executed whenever the instance variable is read # (implemented via trace; alternate approach for similar behavior # is to define per-object procs for get/assign, see e.g. slots for # class and superclass; slots require methods to be invoked, # not var references; # otoh, trace are somewhat more fragile and harder to debug) # default, defaultcmd and valuecmd are to be used mutually exclusively # - valuechangedcmd: executed after the change of an instance variable, # can be used e.g. for validation # # -gustaf neumann 21.Jan. 2006 package require nx::serializer ####################################################### set ::hu 0 proc T1 {var sub op} {c1 set $var t1} proc T2 {var sub op} {c1 set $var t2} Class C -slots { Attribute create x -defaultcmd {set x 1} Attribute create y -defaultcmd {incr ::hu} Attribute create z -defaultcmd {my trace add variable z read T1} } C create c1 ? {c1 info vars x} "" ? {c1 x} "1" ? {c1 info vars x} "x" ? {c1 info vars y} "" ? {c1 y} 1 ? {c1 set x} 1 ? {set ::hu} 1 proc ?? {cmd expected {msg ""}} { #puts "??? $cmd" set r [uplevel $cmd] if {$msg eq ""} {set msg $cmd} if {$r ne $expected} { puts stderr "ERROR $msg returned '$r' ne '$expected'" error "FAILED $msg returned '$r' ne '$expected'" } else { puts stderr "OK $msg" } } Class D -slots { Attribute create x -defaultcmd {set x 2} Attribute create z -defaultcmd {my trace add variable z read T2} ?? self ::D ?? {namespace current} ::D::slot } -superclass C D create c1 ? {c1 set x} 2 ? {c1 z} "" ? {c1 z} t2 ? {c1 y} 2 ? {set ::hu} 2 ####################################################### # # a small helper Object instproc slots cmds { if {![my isobject [self]::slot]} {Object create [self]::slot} namespace eval [self]::slot $cmds } ###################### # system slots ###################### Class M Class O -mixin M ? {O mixin} ::M ? {catch {Object o -mixin check1 M}} 1 ? {O mixin} ::M Class M2 O mixin add M2 ? {O mixin} {::M2 ::M} O mixin M2 ? {O mixin} ::M2 O mixin "" ? {O mixin} "" #O mixin set M ;# not sure, whether we should keep set here, or use assign or some better term O mixin assign M ;# new name ? {O mixin} ::M ? {O mixin ""} "" # with slots like class etc. we have to option to # a) rename the original command like in the following # b) provide a no-op value, such that we define only meta-data in the slot # c) define a low-level tcl command like setrelation (or extend it) to handle the setter # "class" is not multivalued, therefore we should not add (or remove) add/delete # from the set of subcommands... ? {::nx::RelationSlot info class} "::nx::MetaSlot" O o1 ? {o1 class} "::O" o1 class Object ? {o1 class} "::xotcl::Object" ? {o1 __object_configureparameter} "-mixin:mixinreg,alias,0..n -filter:filterreg,alias,0..n -class:class,alias args:alias,method=residualargs,args" ? {Object __object_configureparameter} "-instfilter:filterreg,alias,0..n -superclass:alias,0..n -instmixin:mixinreg,alias,0..n {-__default_metaclass ::xotcl::Class} {-__default_superclass ::xotcl::Object} -mixin:mixinreg,alias,0..n -filter:filterreg,alias,0..n -class:class,alias args:alias,method=residualargs,args" ? {o1 class add M} {class: expected a class but got "M ::xotcl::Object"} ? {O superclass} "::xotcl::Object" Class O2 -superclass O #? {O2 superclass O} "superclass 1" ? {O superclass} "::xotcl::Object" ::nx::ObjectParameterSlot public method slot {object name property} { switch $property { self {return [self]} domain {return [my domain]} } } #? {O superclass slot self} "::xotcl::Class::slot::superclass" ? {O ::nsf::methods::object::info::lookupslots superclass} "::xotcl::Class::slot::superclass" ? {::xotcl::Class::slot::superclass cget -domain} "::xotcl::Class" ? {O2 superclass} "::O" O2 superclass add M ? {O2 superclass} "::M ::O" O2 superclass delete ::O ? {O2 superclass} "::M" # # test "... info default ..." and "... info instdefault ..." # ::nx::test case info-default { ::xotcl::Class create ::Test ::Test proc m0 {-id:required {-flag:boolean true} -switch:switch x {y 1}} {return 0} ::Test instproc m1 {-id:required {-flag:boolean true} -switch:switch x {y 1}} {return 0} ? {::Test info default m0 y default0} 1 ? {info exists default0} 1 ? {::Test info default m0 x default1} 0 unset -nocomplain default0 default1 ? {::Test info instdefault m1 y default0} 1 ? {info exists default0} 1 ? {::Test info instdefault m1 x default1} 0 } # # The main difference between an Attribute and a Role is that it # references some other objects # ::xotcl::MetaSlot create Role -superclass Attribute -parameter {references} ::nx::test case info-slots-heritage { ::xotcl::Class create C -parameter {c1 c2} ::xotcl::Class create D -superclass C -parameter {c2 c3} ? {C info heritage} "::xotcl::Object" ? {D info heritage} "::C ::xotcl::Object" # xotcl info heritage should not see the mixins C instmixin [::xotcl::Class create M] ? {C info superclass -closure} "::xotcl::Object" ? {D info superclass -closure} "::C ::xotcl::Object" ? {D info heritage} "::C ::xotcl::Object" ? {C info slots} "::C::slot::c1 ::C::slot::c2" ? {D info slots} "::D::slot::c2 ::D::slot::c3" ? {D info slots -closure -source application} "::D::slot::c2 ::D::slot::c3 ::C::slot::c1" } ###################### # application classes ###################### Class Person -slots { Attribute create name Attribute create age -default 0 } Class Article -slots { Attribute create title Attribute create date } Class publishes -slots { Role create written_by -references Person -multiplicity 0..n Role create has_published -references Paper -multiplicity 0..n } Class Project -slots { Attribute create name Role create manager -references Person Role create member -references Person -multiplicity 0..n } puts [Person serialize] Person::slot::name configure -default "gustaf" ? {Person::slot::name cget -default} gustaf Person p1 -name neophytos ? {p1 name} neophytos ? {p1 age} 0 p1 age 123 ? {p1 age} 123 Object o1 o1 set i 0 ::nsf::method::alias o1 Incr -frame object ::incr ? {o1 incr i} 1 "method incr" ? {o1 Incr i} 1002 "aliased tcl incr" ? {o1 incr i} 2003 "method incr" ? {o1 Incr i} 3004 "aliased tcl incr" ::nsf::method::alias ::xotcl::Object Set -frame object ::set ? {o1 set i 1} 1 "method set" ? {o1 set i} 1 "method set" ? {o1 Set i 1} 1 "aliased tcl set" ? {o1 Set i} 1 "aliased tcl set" ::nsf::method::alias o1 Set -frame object ::set ? {o1 Set i 1} 1 "aliased object tcl set" ? {o1 Set i} 1 "aliased object tcl set" ::xotcl::Object instforward SSet -earlybinding -objscope ::set ? {o1 SSet i 1} 1 "forward earlybinding tcl set" ? {o1 SSet i} 1 "forward earlybinding tcl set" ? {::xotcl::Object info instforward -definition SSet} "-earlybinding -objscope ::set" o1 set z 100 #o1 forward z o1 [list %argclindex [list set set]] %proc #o1 proc get name {my set $name} o1 forward get -earlybinding ::nsf::var::set %self %1 ? {o1 info forward} get ? {o1 get z 101} 101 ? {o1 get z} "101" ? {o1 get z} 101 "get value via new parametercmd get" ? {o1 get z 124} 124 "set value via new parametercmd get" o1 forward zz -earlybinding ::nsf::var::set %self %proc ? {o1 zz 123} 123 ? {o1 zz} 123 ? {o1 zz} 123 "parametercmd forward earlybinding setinstvar" ? {o1 zz 124} 124 "parametercmd forward earlybinding setinstvar" o1 forward z2 -earlybinding -objscope ::set %proc ? {o1 z2 111} 111 "parametercmd forward earlybinding tcl set" ? {o1 z2} 111 "parametercmd forward earlybinding tcl set" o1 forward z3 -objscope ::set %proc ? {o1 z3 111} 111 "parametercmd forward tcl set" ? {o1 z3} 111 "parametercmd forward tcl set" o1 set y 11 o1 parametercmd y ? {o1 y} 11 "parametercmd" ? {o1 y 1} 1 "parametercmd" #Class C -parameter {a {b 10} {c "Hello World"}} #C copy V #puts [C serialize] #puts [V serialize] #C destroy #V v1 #puts [v1 b] # ::xotcl::Object instproc param arglist { # foreach arg $arglist { # puts "arg=$arg" # set l [llength $arg] # set name [lindex $arg 0] # if {![my isobject [self]::slot]} {::xotcl::Object create [self]::slot} # if {$l == 1} { # Attribute create [self]::slot::$name # } elseif {$l == 2} { # Attribute create [self]::slot::$name -default [lindex $arg 1] # } else { # set paramstring [string range $arg [expr {[string length $name]+1}] end] # #puts stderr "remaining arg = '$paramstring'" # if {[string match {[$\[]*} $paramstring]} { # #puts stderr "match, $cl set __defaults($name) $paramstring" # Attribute create [self]::slot::$name -default $paramstring # continue # } # } # } # } # maybe work directly on ::xotcl::Attribute would be nicer, when # ::xotcl::Attribute would be true alias for ::nx::VariableSlot ... #::nx::VariableSlot mixin delete ::nx::VariableSlot::Optimizer Class C1 -parameter {a {b 10} {c "Hello World"}} C1 c1 -a 1 ? {c1 a} 1 ? {c1 b} 10 ? {c1 c} "Hello World" ##### is short form of Class C2 -slots { Attribute create a Attribute create b -default 10 Attribute create c -default "Hello World" } C2 c2 -a 1 ? {c2 procsearch a} "::C2 instparametercmd a" ? {c2 a} 1 ? {c2 b} 10 ? {c2 c} "Hello World" ? {c2 a} 1 "new indirect parametercmd" ? {c2 a 1} 1 "new indirect parametercmd" #::nx::VariableSlot mixin add ::nx::VariableSlot::Optimizer Class C3 -slots { Attribute create a Attribute create b -default 10 Attribute create c -default "Hello World" } C3 c3 -a 1 ? {c3 procsearch a} "::C3 instparametercmd a" ? {c3 a} 1 ? {c3 b} 10 ? {c3 c} "Hello World" ? {c3 a} 1 "new indirect parametercmd optimized" ? {c3 a 1} 1 "new indirect parametercmd optimized" ####### nasty names Class create D -slots { Attribute create create -default 1 } D d1 ####### gargash 2 Class create A -parameter {{foo 1}} # or Class create A -slots { Attribute create foo -default 1 } A create a1 -foo 234 ;# calls default foo setter A instproc f1 {} {puts hu} A instforward f2 puts hu A create a0 #a0 f1 a0 proc f3 {} {puts hu} a0 forward f4 puts hu ? {a0 procsearch f1} "::A instproc f1" ? {a0 procsearch f2} "::A instforward f2" ? {a0 procsearch f3} "::a0 proc f3" ? {a0 procsearch f4} "::a0 forward f4" ? {a0 procsearch set} "::xotcl::Object instcmd set" ? {A::slot::foo info lookup method value=set} "::nsf::classes::xotcl::Attribute::value=set" # redefine setter for foo of class A #A slot foo method assign {domain var val} ... A::slot::foo public object method assign {domain var val} { # Do something with [self] that isn't valid before init #puts setter-[self proc] $domain set $var $val } a1 foo ;# calls default foo getter a1 foo 123 ;# calls overridden foosetter ? {a1 foo} 123 #puts [A serialize] ################### nx::test case req-param { ::xotcl::Class create C -parameter {y:required x:required} C instproc init args {set ::_ $args} set ::_ "" ? {C create c2 -y 1 -x} {value for parameter '-x' expected} ? {set ::_} "" ? {::nsf::is object c2} 0 ? {C create c3 -y 1 -x 0} "::c3" ? {set ::_} "" ? {c3 x} "0" } ################### # Application Slots # nx::test case app-slots Class Person -slots { Attribute create name Attribute create age -default 0 Attribute create projects -default {} -multiplicity 0..n -incremental true } Person p1 -name "Gustaf" ? {p1 name} Gustaf ? {p1 age} 0 ? {p1 projects} {} Class Project -slots { Attribute create name Attribute create description } Project project1 -name XOTcl -description "A highly flexible OO scripting language" p1 projects add ::project1 ? {p1 projects} ::project1 #p1 projects add some-other-value #? {p1 projects} "some-other-value ::project1" ::nx::ObjectParameterSlot method check { {-keep_old_value:boolean true} value predicate type obj var } { puts "+++ checking $value with $predicate ==> [expr $predicate]" if {![expr $predicate]} { if {[$obj exists __oldvalue($var)]} { $obj set $var [$obj set __oldvalue($var)] } else { $obj unset $var } error "$value is not of type $type" } if {$keep_old_value} {$obj set __oldvalue($var) $value} } ::nx::ObjectParameterSlot method checkall {values predicate type obj var} { foreach value $values { my check -keep_old_value false $value $predicate $type $obj $var } $obj set __oldvalue($var) $value } Person slots { Attribute create projects -default "" -multiplicity 0..n -incremental true -type ::Project Attribute create salary -type integer } Person p2 -name "Gustaf" p2 projects add ::project1 ? {p2 projects add ::o1} {expected object of type ::Project but got "::o1" for parameter "value"} p2 salary 100 ? {catch {p2 salary 100.9}} 1 ? {p2 salary} 100 p2 append salary 9 ? {p2 salary} 1009 # todo currently not checked #? {catch {p2 append salary b}} 1 ? {p2 salary} 1009 Person slots { Attribute create sex -type "sex" -convert true -proc type=sex {name value} { #puts stderr "[self] slot specific converter" switch -glob $value { m* {return m} f* {return f} default {error "expected sex but got $value"} } } } Person p3 -sex male ? {p3 sex} m Person method foo {s:sex,slot=::Person::slot::sex,convert} {return $s} ? {p3 foo male} "m" ? {p3 sex male} m ####################################################### # defaultcmd via slots ####################################################### nx::test case defaultcmd set ::hu 0 Class C -slots { Attribute create x -defaultcmd {incr ::hu; set x 101} } C c1 ? {c1 info vars} "__initcmd" ? {c1 set x} 101 ? {c1 info vars} "x __initcmd" ? {set ::hu 1} 1 ####################################################### # nested contains ####################################################### nx::test case nested-contains Class Point -parameter {{x 100} {y 300}} Class Rectangle -parameter {color} Rectangle r0 -color pink -contains { Rectangle r1 -color red -contains { Point x1 -x 1 -y 2 Point x2 -x 1 -y 2 } Rectangle r2 -color green -contains { Point x1 Point x2 } } #? {r0 color} pink #? {r0 r1 color} red #? {r0 r1 x1 x} 1 #? {r0 r1 x2 y} 2 #? {r0 r2 color} green ? {r0 color} pink ? {r0::r1 color} red ? {r0::r1::x1 x} 1 ? {r0::r1::x2 y} 2 ? {r0::r2 color} green #puts [r0 serialize] ####################################################### # assign via slots ####################################################### nx::test case assign-via-slots Class create A -slots { Attribute create foo -default 1 -proc value=set {domain var value} { if {$value < 0 || $value > 99} { error "$value is not in the range of 0 .. 99" } $domain set $var $value } } A create a1 ? {a1 foo 10} 10 ? {a1 foo 20} 20 ? {a1 foo} 20 ? {a1 foo -1} "-1 is not in the range of 0 .. 99" ? {catch {a1 foo -1}} 1 ? {a1 foo 100} "100 is not in the range of 0 .. 99" ? {a1 foo 99} 99 set x [Object new -set x 1 -contains { Object new -set x 1.1 Object new -set x 1.2 -contains { Object new -set x 1.2.1 Object new -set x 1.2.2 -contains { Object new -set x 1.2.2.1 } Object new -set x 1.2.3 } Object new -set x 1.3 }] ? {llength [$x info children]} 3 ? {llength [[lindex [lsort [$x info children]] 0] info children]} 0 ? {llength [[lindex [lsort [$x info children]] 1] info children]} 3 ? {llength [[lindex [lsort [$x info children]] 2] info children]} 0 # # test case (bug) posted by Neil Hampton # Class Fred -slots { Attribute create a -defaultcmd { set _ 4 } } ? {Fred x} ::x ? {x a 4} 4 x move y ? {y a} 4 ::nx::test case slots-compat # # Some tests covering the backward compatibility of NX/XOTcl2 hybrid # slots to the XOTcl1 slot API (as extracted from the XOTcl language # reference) # # # 1) old-style Attribute creation # Class Window -slots { Attribute scrollbar; # old style Attribute create title; # new style } ? {lsort [Window info slots]} "::Window::slot::scrollbar ::Window::slot::title" # # 2) Dropped/missing slot attributes: multivalued # Class Person -slots { Attribute name Attribute salary -default 0 Attribute projects -default {} -multivalued true } ? {lsort [Person info slots]} "::Person::slot::name ::Person::slot::projects ::Person::slot::salary" ? {Person::slot::name multivalued get} 0 ? {Person::slot::salary multivalued get} 0 ? {Person::slot::projects multivalued get} 1 Person p2 -name "John Doe" ? {p2 name} "John Doe" ? {p2 salary} "0" ? {p2 projects} [list] Project compatPrj -name XOTclCompat p2 projects add ::compatPrj p2 projects add some-other-value ? {lsort [p2 projects]} "::compatPrj some-other-value" p2 projects delete some-other-value ? {lsort [p2 projects]} "::compatPrj" ? {catch {p2 name add BOOM!}} 1 ? {p2 name} "John Doe" # # 3) -proc inline statements upon Attribute creation # (as found in the tutorial) # Class create AA -slots { Attribute foo -default 1 -proc value=set {domain var value} { if {$value < 0 || $value > 99} { error "$value is not in the range of 0 .. 99" } $domain set $var $value } } AA create aa1 ? {aa1 foo 10} 10 ? {aa1 foo} 10 ? {catch {aa1 foo -1}} 1 exit #puts [Person array get __defaults] #puts [Person serialize] puts [Serializer all] eval [Serializer all] ? {p2 salary} 1009 ? {catch {p2 append salary b}} 1 ? {p2 salary} 1009 #p2 projects add ::o1 exit p1 set x 0 t {p1 set x} "get instvar value via set" t {p1 set x 1} "set instvar value via set" Object o1 proc f {x} {return $x} o1 forward myf -earlybinding f ? {o1 myf abc} abc rename f "" proc f {x} {return 11} ? {o1 myf abc} 11 Object o2 o2 proc f {x} {expr {$x*2}} o1 forward myf -earlybinding o2 f ? {o1 myf 100} 200 o1 set x 42 o1 forward x -earlybinding ::nsf::var::set %self %proc ? [list o1 x] 42 ? [list o1 x 41] 41 ? {o1 x} "get parametercmd via forward (earlybinding)" ? {o1 x 41} "set parametercmd via forward (earlybinding)" #obj forward Mixin -default {getter setter} mixin %1 %self o1 forward z -default {getter setter} %self o1 forward myfset -objscope set o1 myfset y 102 ? {o1 myfset y} 102 ? {o1 myfset y} "get instvar value via forward" ? {o1 myfset y 122} "set instvar value via forward" o1 forward myfdset -earlybinding -objscope set o1 myfdset y 103 ? {o1 myfdset y} 103 ? {o1 myfdset y} "get instvar value via forward -earlybinding" ? {o1 myfdset y 123} "set instvar value via forward -earlybinding" ::nsf::method::alias o1 myset -frame object ::set o1 myset x 101 ? {o1 myset x} 101 ? {o1 myset x} "get instvar value via set alias" ? {o1 myset x 123} "set instvar value via set alias" ? {p1 age} "slot read" Class P -parameter {age {s -setter sets}} P instproc sets {var value} { my set $var $value } P create p2 -age 345 -s 567 ? {p2 age} "parametercmd read" ? {::nsf::var::set p2 age} "via setinstvar" ? {p2 s} "parameter read with setter" Slot create Project::fullbudget \ -defaultcmd {$obj set __x 100} \ -valuechangedcmd { puts "budget is now [$obj set fullbudget]" $obj set __x [$obj set fullbudget] } Slot create Project::currentbudget -valuecmd {$obj incr __x -1} Person p2 -name gustaf Person p3 -name frido Article a1 -title "My life as a saint" -date "1.1.2006" publishes new -written_by p1 -has_published a1 set p [Project new -name icamp -manager p2 -member add p1 -member add p3] $p member add X end puts [$p member] ? [list $p fullbudget] 100 ? [list $p fullbudget] 100 ? [list $p currentbudget] 99 ? [list $p currentbudget] 98 ? [list $p fullbudget 200] 200 ? [list $p currentbudget] 199 # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/xotcl/tests/speedtest.xotcl000066400000000000000000000422701242365656200201100ustar00rootroot00000000000000# -*- Tcl -*- #memory trace on package prefer latest package require XOTcl 2.0; namespace import ::xotcl::* package require nx::test nx::test new -msg {test multiple dashed args o0} \ -cmd {Object create o0 [list -set a -a1] [list -set b "-b 1 -y 2"]} \ -expected ::o0 \ -post {o0 destroy} nx::test new -msg {test multiple dashed args o1} \ -cmd {Object create o1 -proc foo args {return 1} [list -set a -a1] [list -set b "-b 1 -y 2"]} \ -expected ::o1 \ -post {o1} nx::test new -msg {test multiple dashed args o2} \ -cmd {Object create o2 {-proc foo args {return 1}} {-set -a -t1} {-set b "-b 1 -y 2"}} \ -expected ::o2 \ -post {o2 destroy} nx::test new -msg {test multiple dashed args o3} \ -cmd {Object create o3 -proc foo args {return 1} {-set -a -t1} {-set b "-b 1 -y 2"}} \ -expected ::o3 \ -post {o3 destroy} nx::test configure -count 1000 @ @File {description { Regression and speed test for various ways to achieve a similar behaviour. } } set ccount 20 #set ocount 1014 #set ocount [expr {$ccount + 206}] #set ocount [expr {$ccount + 15}] set ocount [expr {$ccount + 6}] Class M1; Class M2 Class C -parameter {{p 99} {q 98} r} C instproc f args {next} C instproc init args { my instvar n v #for {set i 1} {$i<1000} {incr i} {set n($i) 1} #for {set i 1} {$i<1000} {incr i} {Object [self]::$i} for {set i 0} {$i<$::ccount} {incr i} {set n($i) 1} for {set i 0} {$i<$::ccount} {incr i} {Object [self]::$i} set v 1 } C instproc mixinappend m { my mixin [concat [my info mixin] $m] my info mixin } C instproc ma m { set mix [my info mixin] my mixin [lappend mix $m] my info mixin } C instproc existsViaInstvar {} { my instvar v info exists v } C instproc existsViaMyInstvar {} { my instvar v info exists v } C instproc existsViaExistsMethod {} { [self] exists v } C instproc existsViaMyExistsMethod {} { my exists v } C instproc existsViaDotExistsMethod {} { :exists v } C instproc existsViaResolver {} { info exists :v } C instproc notExistsViaInstvar {} { my instvar xxx info exists xxx } C instproc notExistsViaExistsMethod {} { my exists xxx } C instproc existsAndReturnValue1 {} { if {[my exists v]} { my set v } } C instproc existsAndReturnValue3 {} { if {[my exists v]} { set x [my set v] } } C instproc setViaInstvar x { my instvar v set v $x } C instproc setViaSetMethod x { my set v $x } C instproc setViaParameter x { my r $x } C instproc testAndSetViaInstvar x { my instvar v if {[info exists v]} {set v $x} } C instproc testAndSetViaSetMethod x { if {[my info vars v] ne ""} {my set v $x} } C instproc readViaInstvar {} { my instvar p set p } C instproc readViaSetMethod {} { my set p } C instproc readViaSetMethodNoSelf {} { ::c set p } C instproc readViaParameter {} { my p } C instproc readTwiceViaInstvar {} { my instvar p set p set p } C instproc readTwiceViaSetMethod {} { my set p my set p } C instproc readTwiceViaSetMethodNoSelf {} { ::c set p ::c set p } C instproc readTwiceViaParameter {} { my p my p } C instproc readTwovarsViaInstvar {} { my instvar p q set p set q } C instproc readTwovarsViaSetMethod {} { my set p my set q } C instproc readTwovarsViaSetMethodNoSelf {} { ::c set p ::c set q } C instproc readTwovarsViaParameter {} { my p my q } C instproc instvarAlias {} { my instvar {a b} set b 1 my exists a } C instproc explicitReturn {} { return [set i 1] } C instproc implicitReturn {} { set i 1 } C instproc explicitReturnFromVar {} { set i 1 return $i } C instproc implicitReturnFromVar {} { set i 1 set i } C instproc childNodeNamespace {} { Object [self]::13 } C instproc childNodeNamespaceCreate {} { Object create [self]::13 } C instproc createVolatileRc {} { Object new -volatile return 2 } C c Class D -superclass C D instproc init args {} D d #nx::test new -cmd {llength [c info children]} -count 1 -expected 999 #nx::test new -cmd {set x [llength [c info children]]} -count 1 -expected 999 nx::test new -cmd {llength [c info children]} -count 1 -expected $ccount nx::test new -cmd {set x [llength [c info children]]} -count 1 -expected $ccount nx::test new -cmd {set x [llength [Object info instances]]} -count 1 -expected $ocount nx::test new -cmd {llength [Object info instances]} -count 1 -expected $ocount nx::test new -cmd {d istype D} -expected 1 nx::test new -cmd {c setViaInstvar 100} -expected 100 nx::test new -cmd {c setViaSetMethod 100} -expected 100 nx::test new -cmd {c setViaParameter 100} -expected 100 nx::test new -cmd {c existsViaInstvar} nx::test new -cmd {c existsViaMyInstvar} nx::test new -cmd {c existsViaExistsMethod} nx::test new -cmd {c existsViaMyExistsMethod} nx::test new -cmd {c existsViaDotExistsMethod} nx::test new -cmd {c existsViaResolver} nx::test new -cmd {c exists v} nx::test new -cmd {c notExistsViaInstvar} -expected 0 nx::test new -cmd {c notExistsViaExistsMethod} -expected 0 nx::test new -cmd {c exists xxx} -expected 0 nx::test new -cmd {c existsAndReturnValue1} -expected 100 nx::test new -cmd {c existsAndReturnValue3} -expected 100 nx::test new -cmd {c testAndSetViaInstvar 100} -expected 100 nx::test new -cmd {c testAndSetViaSetMethod 100} -expected 100 nx::test new -cmd {c readViaInstvar} -expected 99 nx::test new -cmd {c readViaSetMethod} -expected 99 nx::test new -cmd {c readViaParameter} -expected 99 nx::test new -cmd {c readViaSetMethodNoSelf} -expected 99 nx::test new -cmd {c readTwiceViaInstvar} -expected 99 nx::test new -cmd {c readTwiceViaSetMethod} -expected 99 nx::test new -cmd {c readTwiceViaParameter} -expected 99 nx::test new -cmd {c readTwiceViaSetMethodNoSelf} -expected 99 nx::test new -cmd {c readTwovarsViaInstvar} -expected 98 nx::test new -cmd {c readTwovarsViaSetMethod} -expected 98 nx::test new -cmd {c readTwovarsViaParameter} -expected 98 nx::test new -cmd {c readTwovarsViaSetMethodNoSelf} -expected 98 nx::test new -cmd {c instvarAlias} nx::test new -cmd {c incr v} -post {c set v 1} -expected 101 nx::test new -cmd {c unset v; set r [c exists v]; c set v 1; set r} -expected 0 nx::test new -cmd {llength [Object info instances]} -count 1 -expected $ocount nx::test new -cmd {set x [llength [Object info instances]]} -count 1 -expected $ocount nx::test new -cmd {c explicitReturn} nx::test new -cmd {c implicitReturn} nx::test new -cmd {c explicitReturnFromVar} nx::test new -cmd {c implicitReturnFromVar} nx::test new -cmd {llength [Object info instances]} -count 1 -expected $ocount nx::test new -cmd {set x [llength [Object info instances]]} -count 1 -expected $ocount nx::test new -cmd {c childNodeNamespace} -expected ::c::13 nx::test new -cmd {llength [Object info instances]} -count 1 -expected $ocount nx::test new -cmd {c childNodeNamespaceCreate} -expected ::c::13 nx::test new -cmd {llength [Object info instances]} -expected $ocount nx::test new -cmd {c createVolatileRc} -expected 2 # should be still the same number as above nx::test new -count 1 -cmd {llength [Object info instances]} -expected $ocount nx::test new -cmd {Object new -volatile} -expected ::nsf::__\#F9 -count 2000 \ -post {foreach o [Object info instances ::nsf::__*] {$o destroy}} # should be still the same number as above nx::test new -count 1 -cmd {llength [Object info instances]} -expected $ocount nx::test new -cmd {Object new} -expected ::nsf::__\#lQ -count 2000 \ -post {foreach o [Object info instances ::nsf::__*] {$o destroy}} # should be still the same number as above nx::test new -count 1 -cmd {llength [Object info instances]} -expected $ocount nx::test new -cmd {Object new -childof o} -expected ::o::__\#0Hh \ -pre {Object o} -post {o destroy} # should be still the same number as above nx::test new -count 1 -cmd {llength [Object info instances]} -expected $ocount nx::test new -count 1000 -pre {::set ::count 0} \ -cmd {Object create [incr ::count]} \ -expected ::1 \ -post {::unset ::count} nx::test new -count 1000 -pre {::set ::count 0} \ -cmd {[incr ::count] destroy} \ -post {::unset ::count} \ -expected "" # nx::test new -count 1 -cmd {llength [Object info instances]} -expected $ocount # we create another object set ocount [expr {$ocount + 1}] nx::test new -cmd {Object create x} -expected ::x nx::test new -count 1 -cmd {llength [Object info instances]} -expected $ocount nx::test new -cmd {Object create x -set a -1 -set b ,, -set c a--} \ -expected ::x nx::test new -count 1 -cmd {llength [Object info instances]} -expected $ocount nx::test new -cmd {expr {[c array names n 5] ne ""}} nx::test new -count 1 -cmd {llength [Object info instances]} -expected $ocount nx::test new -cmd {info exists c::n(5)} nx::test new -count 1 -cmd {llength [Object info instances]} -expected $ocount nx::test new -cmd {c exists n(5)} nx::test new -cmd {llength [c info children]} -expected $ccount nx::test new -cmd {c info children ::c::5} -expected ::c::5 nx::test new -cmd {c info children 5} -expected ::c::5 nx::test new -cmd {c info children 5*} -expected ::c::5 nx::test new -count 1 -cmd {llength [Object info instances]} -expected $ocount nx::test new -cmd {Object info instances ::c::5*} -expected ::c::5 nx::test new -cmd {Object info instances ::c::5} -expected ::c::5 nx::test new -cmd {Object info instances ::c::5000} -expected "" nx::test new -count 100 -pre {set ::c::l ""} \ -cmd {lappend ::c::l 1} \ -post {c unset l} nx::test new \ -count 100 \ -cmd {c mixinappend M1} \ -expected ::M1 \ -post {c mixin ""} nx::test new \ -count 100 \ -cmd {c ma M1} \ -expected ::M1 \ -post {c mixin ""} nx::test new \ -count 100 \ -cmd {c mixin add M1} \ -expected "::M1" \ -post {c mixin ""} nx::test new \ -count 100 \ -cmd {c mixinappend M1; c mixinappend M2} \ -expected {::M1 ::M2} \ -post {c mixin ""} nx::test new \ -count 100 \ -cmd {c ma M1; c ma M2} \ -expected {::M1 ::M2} \ -post {c mixin ""} nx::test new \ -count 100 \ -pre {Class D; Class E; Object o -mixin {D E}} \ -cmd {o info mixin D} \ -expected {::D} \ -post {foreach o {D E o} {$o destroy}} nx::test new \ -count 100 \ -pre {Class D; Class E; Object o -mixin {D E}} \ -cmd {o info mixin E} \ -expected {::E} \ -post {foreach o {D E o} {$o destroy}} nx::test new \ -count 100 \ -pre {Class D; Class E; Object o -mixin {D E}} \ -cmd {o info mixin ::E*} \ -expected {::E} \ -post {foreach o {D E o} {$o destroy}} nx::test new \ -count 100 \ -pre {Class D; Class E; Class E1; Object o -mixin {D E E1}} \ -cmd {o info mixin ::E*} \ -expected {::E ::E1} \ -post {foreach o {D E E1 o} {$o destroy}} nx::test new \ -count 100 \ -pre {Class D; Class E; Class X1 -instmixin {D E}} \ -cmd {X1 info instmixin D} \ -expected {::D} \ -post {foreach o {D E X1} {$o destroy}} nx::test new \ -count 100 \ -pre {Class D; Class E; Class X2 -instmixin {D E}} \ -cmd {X2 info instmixin E} \ -expected {::E} \ -post {foreach o {D E X2} {$o destroy}} nx::test new \ -count 100 \ -pre {Class D; Class E; Class E1; Class X -instmixin {D E E1}} \ -cmd {X info instmixin ::E*} \ -expected {::E ::E1} \ -post {foreach o {D E E1 X} {$o destroy}} nx::test new \ -count 100 \ -pre {Class D; Class E; Class X3 -instmixin {D E}} \ -cmd {X3 info instmixin ::E*} \ -expected {::E} \ -post {foreach o {D E X3} {$o destroy}} nx::test new \ -count 100 \ -pre {Class D; Class E; Class X} \ -cmd {X instmixin {D E}; X instmixin delete ::E; X info instmixin} \ -expected {::D} \ -post {foreach o {D E X} {$o destroy}} nx::test new \ -count 100 \ -pre {Class D; Class E; Class X} \ -cmd {X instmixin {D E}; X instmixin delete E; X info instmixin} \ -expected {::D} \ -post {foreach o {D E X} {$o destroy}} nx::test new \ -count 100 \ -pre {Class D; Class E; Class E1; Class X} \ -cmd {X instmixin {D E E1}; catch {X instmixin delete ::E*}; X info instmixin} \ -expected {::D} \ -post {foreach o {D E E1 X} {$o destroy}} nx::test new \ -count 100 \ -pre {Class D; Class E; Class E1; Class X} \ -cmd {X instmixin {D E E1}; catch {X instmixin delete E*}; X info instmixin} \ -expected {::D} \ -post {foreach o {D E E1 X} {$o destroy}} nx::test new \ -cmd {C instfilter f; C info instfilter} \ -expected f \ -post {C instfilter ""} nx::test new -pre {set s \#hallo} -cmd {string match "\#*" $s} nx::test new -pre {set s \#hallo} -cmd {regexp {^\#} $s} nx::test new -pre {set s \#hallo} -cmd {expr {[string first "\#" $s] == 0}} nx::test new -pre {set s \#hallo} -cmd {expr {[string range $s 0 0] == "\#"}} nx::test new -pre {set s \#hallo} -cmd {regexp {^\#.*a} $s} nx::test new -pre {set s \#hallo} -cmd {regexp {^\#.*a.*o} $s} nx::test new -pre {set s \#hallo} -cmd {regexp {^\#.*a(.*)o} $s} nx::test new -pre {set s \#hallo} -cmd {regexp {^\#.*a(.*)o} $s _} nx::test new -pre {set s \#hallo} -cmd {regexp {^\#.*a(.*)o} $s _ out} nx::test new -msg {call proc of subobject directly} \ -pre {C c2; C c2::o; c2::o proc f a {incr a}} \ -cmd {c2::o::f 10} -expected 11 -count 5000 \ -post {c2 destroy} nx::test new -msg {call proc of subobject via dispatch} \ -pre {C c2; C c2::o; c2::o proc f a {incr a}} \ -cmd {c2::o f 10} -expected 11 -count 5000 \ -post {c2 destroy} #nx::test new -msg {call proc of object and subobject via dispatch} \ # -pre {C c2; C c2::o; c2::o proc f a {incr a}} \ # -cmd {c2 o f 10} -expected 11 -count 5000 \ # -post {c2 destroy} nx::test new -msg {dispatch subobject directy via [self]} \ -pre {C c2; C c2::o; c2::o proc f a {incr a}; c2 proc t a {[self]::o f $a}} \ -cmd {c2 t 12} -expected 13 -count 5000 \ -post {c2 destroy} #nx::test new -msg {dispatch subobject via my} \ # -pre {C c2; C c2::o; c2::o proc f a {incr a}; c2 proc t a {my o f $a}} \ # -cmd {c2 t 12} -expected 13 -count 5000 \ # -post {c2 destroy} ###### insttclcmd tests set cnt 10000 #nx::test new -msg {call insttclcmd (append) and check created variable} \ -pre {Object o} \ -cmd {o append X 1; o exists X} -expected 1 \ -post {o destroy} #nx::test new -msg {call tclcmd (regexep) and check created variable} \ -pre {Object o; o tclcmd regexp} \ -cmd {o regexp (a) a _ x; o exists x} -expected 1 -count $cnt \ -post {o destroy} nx::test new -msg {call forwarder for (append) and check created variable} \ -pre {Object o; o forward append -objscope} \ -cmd {o append X 1; o exists X} -expected 1 \ -post {o destroy} nx::test new -msg {call forwarder (regexep) and check created variable} \ -pre {Object o; o forward regexp -objscope} \ -cmd {o regexp (a) a _ x; o exists x} -expected 1 -count $cnt \ -post {o destroy} nx::test new -msg {call forwarder to another obj} \ -pre {Object o; Object t; o forward set t set; t set x 100} \ -cmd {o set x} -expected 100 -count $cnt \ -post {o destroy} set cnt 100000 nx::test new -msg {call handcoded incr} \ -pre {Class C; C create o; o set x 1} \ -cmd {o incr x 77} -expected 78 -count $cnt \ -post {o destroy} nx::test new -msg {call incr via instforward} \ -pre {Class C; C instforward ::incr -objscope; C create o; o set x 1} \ -cmd {o incr x 77} -expected 78 -count $cnt \ -post {o destroy} nx::test new -msg {call incr via forward} \ -pre {Class C; C create o; o forward ::incr -objscope; o set x 1} \ -cmd {o incr x 77} -expected 78 -count $cnt \ -post {o destroy} set cnt 10000 nx::test new -msg {call obj with namespace via forward} \ -pre {Object n; Object n::x; Object o -forward ::n::x} \ -cmd {o x self} -expected ::n::x -count $cnt \ -post {o destroy} nx::test new -msg {call obj with namespace via instforward} \ -pre {Object n; Object n::x; Class C; C create o; C instforward ::n::x} \ -cmd {o x self} -expected ::n::x -count $cnt \ -post {o destroy} nx::test new -msg {call obj with namespace via instforward and mixinclass} \ -pre {Object n; Object n::x; Class M -instforward ::n::x; Class C -instmixin M; C create o } \ -cmd {o x self} -expected ::n::x -count $cnt \ -post {o destroy} nx::test new -msg {call obj with namespace via instforward and next from proc} \ -pre { Object n; Object n::x; Class C -instforward ::n::x; C create o -proc x args {next} } \ -cmd {o x self} -expected ::n::x -count $cnt \ -post {o destroy} nx::test new -msg {call obj with namespace via instforward and next from instproc} \ -pre { Object n; Object n::x; Class C -instforward ::n::x; Class D -superclass C -instproc x args {next}; D create o } \ -cmd {o x self} -expected ::n::x -count $cnt \ -post {o destroy} nx::test new -msg {call obj with namespace via mixin and instforward and next} \ -pre {Object n; Object n::x; Class M -instforward ::n::x; Class N -superclass M -instproc x args {next}; Class C -instmixin N; C create o} \ -cmd {o x self} -expected ::n::x -count $cnt \ -post {o destroy} nx::test new -msg {return -code break} \ -pre {Class A -instproc br {} {return -code break}; A create a1} \ -cmd {catch {a1 br}} -expected 3 -count 2 \ -post {A destroy; a1 destroy} nx::test run # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/xotcl/tests/testo.xotcl000066400000000000000000000272271242365656200172530ustar00rootroot00000000000000# # Copyright 1993 Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # package prefer latest package require XOTcl 2.0; namespace import ::xotcl::* @ @File {description { This is a class which provides regression test objects for the OTcl derived features of the XOTcl - Language. This script is based upon the test.tcl script of the OTcl distribution and adopted for XOTcl. } } # # a meta-class for test objects, and a class for test suites # Class TestSuite @ Class TestSuite Class TestClass -superclass Class @ Class TestClass { description { A meta-class for test objects. } } # # check basic argument dispatch and unknown # TestSuite objectdispatch objectdispatch proc run {{n 50}} { Object adispatch adispatch proc unknown {m args} {eval list [list $m] $args} adispatch proc cycle {l n args} { if {$l>=$n} then {return ok} set i [llength $args] foreach a $args { if {$a != $i} then { error "wrong order in arguments: $l $n $args" } incr i -1 } incr l set ukn [eval [list [self]] $args] if {$ukn != $args} then { error "wrong order in unknown: $ukns" } eval [list [self]] [list [self proc]] [list $l] [list $n] [list $l] $args } if {[catch {adispatch cycle 1 $n 1} msg]} then { error "FAILED [self]: cycle: $msg" } return "PASSED [self]" } # # examples from the workshop paper # TestSuite paperexamples paperexamples proc example1 {} { Object astack astack set things {} astack proc put {thing} { my instvar things set things [concat [list $thing] $things] return $thing } astack proc get {} { my instvar things set top [lindex $things 0] set things [lrange $things 1 end] return $top } astack put bagel astack get astack destroy } paperexamples proc example2 {} { Class Safety Safety instproc init {} { next my set count 0 } Safety instproc put {thing} { my instvar count incr count next } Safety instproc get {} { my instvar count if {$count == 0} then { return {empty!} } incr count -1 next } Class Stack Stack instproc init {} { next my set things {} } Stack instproc put {thing} { my instvar things set things [concat [list $thing] $things] return $thing } Stack instproc get {} { my instvar things set top [lindex $things 0] set things [lrange $things 1 end] return $top } Class SafeStack -superclass {Safety Stack} SafeStack s s put bagel s get s get s destroy SafeStack destroy Stack destroy Safety destroy } paperexamples proc run {} { set msg {} if {[catch {my example1; my example2} msg] == "0"} then { return "PASSED [self]" } else { error "FAILED [self]: $msg" } } # # create a graph of classes # TestSuite classcreate classcreate proc factorgraph {{n 3600}} { TestClass $n for {set i [expr {$n/2}]} {$i>1} {incr i -1} { if {($n % $i) == 0} then { # # factors become subclasses, direct or indirect # if {[TestClass info instances $i] eq ""} then { my factorgraph $i $i superclass $n } elseif {[$i info superclass $n] == 0} then { $i superclass [concat [$i info superclass] $n] } } } } classcreate proc run {} { set msg {} if {[catch {my factorgraph} msg] == "0"} then { return "PASSED [self]" } else { error "FAILED [self]: $msg" } } # # lookup superclasses and combine inherited methods # TestSuite inheritance inheritance proc meshes {s l} { set p -1 foreach j $s { set n [lsearch -exact $l $j] if {$n == -1} then { error "FAILED [self] - missing superclass" } if {$n <= $p} then { error "FAILED [self] - misordered heritage: $s : $l" } set p $n } } inheritance proc superclass {} { foreach i [TestClass info instances] { set s [$i info superclass] set h [$i info heritage] # # superclasses should mesh with heritage # my meshes $s $h } } inheritance proc combination {} { foreach i [lsort [TestClass info instances]] { # # combination should mesh with heritage # $i anumber set obj [lrange [anumber combineforobj] 1 end] set h [$i info heritage] my meshes $obj $h anumber destroy if {[$i info procs combineforclass] ne ""} then { set cls [lrange [$i combineforclass] 1 end] my meshes $cls $h } } } inheritance proc run {} { # # add combine methods to "random" half of the graph # set t [TestClass info instances] for {set i 0} {$i < [llength $t]} {incr i 2} { set o [lindex $t $i] $o instproc combineforobj {} { return [concat [list [self class]] [next]] } $o proc combineforclass {} { return [concat [list [self class]] [next]] } } # # and to Object as a fallback # Object instproc combineforobj {} { return [concat [list [self class]] [next]] } Object proc combineforclass {} { return [concat [list [self class]] [next]] } my superclass my combination return "PASSED [self]" } # # destroy graph of classes # TestSuite classdestroy classdestroy proc run {} { # # remove half of the graph at a time # TestClass instproc destroy {} { global TCdestroy set TCdestroy [self] next } while {[TestClass info instances] ne ""} { set t [TestClass info instances] for {set i 0} {$i < [llength $t]} {incr i} { set o [lindex $t $i] # # quarter dies directly, quarter indirectly, quarter renamed # if {($i % 2) == 0} then { global TCdestroy set sb [$o info subclass] if {[info tclversion] >= 7.4 && ($i % 4) == 0} then { $o move "" } else { $o destroy } if {[catch {set TCdestroy}] || $TCdestroy != $o} then { error "FAILED [self] - destroy instproc not run for $o" } if {[info commands $o] ne ""} then { error "FAILED [self] - $o not removed from interpreter" } unset TCdestroy # # but everyone must still have a superclass # foreach j $sb { if {[$j info superclass] eq ""} then { $j superclass Object } } } elseif {[info tclversion] >= 7.4 && ($i % 3) == 0} then { $o move $o.$i } } inheritance superclass inheritance combination } return "PASSED [self]" } TestSuite objectinits objectinits proc prepare {n} { # # head of a chain of classes that do add inits # TestClass 0 0 instproc init {args} { next my set order {} } # # and the rest # for {set i 1} {$i < $n} {incr i} { TestClass $i -superclass [expr {$i-1}] # # record the reverse order of inits # $i instproc init {args} { eval next $args my instvar order lappend order [namespace tail [self class]] } # # add instproc for init options # $i instproc m$i.set {val} { my instvar [namespace tail [self class]] set [namespace tail [self class]] [self proc].$val } } } objectinits proc run {{n 15}} { my prepare $n set il {} for {set i 1} {$i < $n} {incr i} { lappend il $i set al {} set args {} for {set j $i} {$j > 0} {incr j -1} { lappend al $j lappend args -m$j.set $j # # create obj of increasing class with increasing options # if {[catch {eval $i m$i.$j $args} msg] != 0} then { error "FAILED [self] - $msg" } if {[m$i.$j set order] != $il} then { error "FAILED [self] - inited order was wrong" } set vl [lsort -decreasing -integer [m$i.$j info vars {[0-9]*}]] if {$vl != $al} then { error "FAILED [self] - wrong instvar names: $vl : $al" } foreach k $vl { if {[m$i.$j set $k] != "m$k.set.$k"} then { error "FAILED [self] - wrong instvar values" } } } } return "PASSED [self]" } TestSuite objectvariables objectvariables proc run {{n 100}} { TestClass Variables Variables avar foreach obj {avar Variables TestClass xotcl::Class xotcl::Object} { # # set up some variables # $obj set scalar 0 $obj set array() {} $obj unset array() $obj set unset.$n {} # # mess with them recursively # $obj proc recurse {n} { my instvar scalar array incr scalar set array($n) $n my instvar unset.$n unset unset.$n incr n -1 my instvar unset.$n set unset.$n [array names array] if {$n > 0} then { my recurse $n } } $obj recurse $n # # check the result and clean up # if {[$obj set scalar] != $n} then { error "FAILED [self] - scalar [$obj set scalar] != $n" } $obj unset scalar for {set i $n} {$i > 0} {incr i -1} { if {[$obj set array($i)] != $i} then { error "FAILED [self] - array" } } $obj unset array if {[$obj info vars "unset.0"] eq ""} then { error "FAILED [self] - unset: [$obj info vars]" } } # # trace variables # Variables avar2 proc ::traceproc {maj min op} { set majTmp [namespace tail "$maj"] #puts stderr ...TRACE global trail; lappend trail [list $majTmp $min $op] } avar2 proc trace {var ops} { my instvar $var ::trace variable $var $ops "avar2 traceproc" #puts stderr "::trace variable $var $ops avar2 traceproc" } avar2 proc traceproc {maj min op} { set majTmp [namespace tail $maj] #puts stderr ...TRACE global trail; lappend trail [list $majTmp $min $op] } avar2 proc killSelf {} { my destroy } global guide trail set trail {} set guide {} avar2 trace array wu for {set i 0} {$i < $n} {incr i} { avar2 trace scalar$i wu avar2 set scalar$i $i lappend guide [list scalar$i {} w] avar2 set array($i) [avar2 set scalar$i] lappend guide [list array $i w] } if {$guide != $trail} then { error "FAILED [self] - trace: expected $guide, got $trail" } # # destroy must trigger unset traces # set trail {} set guide {} lappend guide [list array {} u] for {set i 0} {$i < $n} {incr i} { lappend guide [list scalar$i {} u] } avar2 killSelf if {[lsort $guide] != [lsort $trail]} then { error "FAILED [self] - trace: expected $guide, got $trail" } Variables destroy return "PASSED [self]" } # # c api, if compiled with -DTESTCAPI # TestSuite capi capi proc run {{n 50}} { set start [dawnoftime read] for {set i 0} {$i < $n} {incr i} { Timer atime$i if {$i % 3} {atime$i stop} if {$i % 7} {atime$i read} if {$i % 2} {atime$i start} if {$i % 5} {atime$i stop} } set end [dawnoftime read] if {$end < $start} { error "FAILED [self]: timer doesn't work" } foreach i [Timer info instances] {$i destroy} Timer destroy return "PASSED [self]" } TestSuite proc run {} { # # run individual tests in needed order # puts [objectdispatch run] puts [paperexamples run] puts [classcreate run] puts [inheritance run] puts [classdestroy run] puts [objectinits run] puts [objectvariables run] if {[info commands Timer] ne ""} then { puts [capi run] } # puts [autoload run] } puts [time {TestSuite run} 1] # Local Variables: # mode: tcl # tcl-indent-level: 2 # End: library/xotcl/tests/testx.xotcl000066400000000000000000004301501242365656200172550ustar00rootroot00000000000000# -*- Tcl -*- package prefer latest package require XOTcl 2.0; namespace import -force ::xotcl::* proc ::errorCheck {got expected msg} { nsf::__db_run_assertions if {$got != $expected} { if {[catch {uplevel self} self]} { set self "NO CURRENT OBJECT" } puts stderr "$self FAILED: $msg\nGot: $got\nExpected: $expected" foreach g $got e $expected { set result [expr {$g == $e}] if {[string length $g]>60} { puts "$result g='$g'\n e='$e'" } else { puts "$result g='$g' e='$e'" } } exit -1 } } proc ::cutSpaces {string} { regsub -all " " $string "" result regsub -all "\n" $result " " result return $result } Class TestX \ -instmixin [Class TestXM -instproc run args {next; puts "[self] PASSED"}] @ @File {description { This is a file which provides a regression test for the features of the XOTcl - Language. } } @ Class TestX @ TestX nestingClasses { description {Regression test object testing the class nesting feature.} } TestX nestingClasses -proc run {{n 20}} { for {set i 0} {$i < $n} {incr i} { Class x($i) Class x($i)::y ::errorCheck [x($i) info commands y] "y" " -- creating Nested Class " Class x($i)::z Class x($i)::z::t Class x($i)::t ::errorCheck [x($i) info classchildren] "::x($i)::t ::x($i)::y ::x($i)::z" \ "info classchildren" ::errorCheck [x($i) info children] "::x($i)::t ::x($i)::y ::x($i)::z" \ "info children" ::errorCheck [x($i)::z info classparent] "::x($i)" \ "info classparent" ::errorCheck [x($i)::z info parent] "::x($i)" \ "info parent" ::errorCheck [x($i) info commands t] "t" "-- MakeClass " x($i) a x($i)::z a x($i)::z::t a x($i)::z::t move x($i)::z::rt x($i)::z::rt a ::errorCheck [x($i)::z info commands rt] "rt" \ "renaming leaf " x($i)::z move x($i)::rz ::errorCheck [x($i) info commands rz] "rz" \ "renaming node (node itself)" ::errorCheck [x($i)::rz info commands rt] "rt" \ "renaming node (leaf in node)" ::errorCheck [x($i)::rz info classchildren] "::x($i)::rz::rt" \ "info classchildren (2)" ::errorCheck [x($i)::rz info children] "::x($i)::rz::rt" \ "info children (2)" ::errorCheck [x($i)::rz::rt info classparent] "::x($i)::rz" \ "info classparent (2)" ::errorCheck [x($i)::rz::rt info parent] "::x($i)::rz" \ "info parent (2)" x($i) move rx ::errorCheck [rx info commands rz] "rz" \ "renaming root " ::errorCheck [info commands rx] "rx" \ "renaming root " rx destroy } } @ TestX nestingObjects { description {Regression test object testing the object nesting feature.} } TestX nestingObjects -proc run {{n 20}} { for {set i 0} {$i < $n} {incr i} { Class C($i) C($i) instproc testinstproc {} { return } C($i) o o proc testproc {} { return } o testproc; o testinstproc C($i) o::y ::errorCheck [o info commands y] y "creating Nested Object " C($i) o::z C($i) o::z::t C($i) o::t ::errorCheck [o info children] "::o::t ::o::y ::o::z" "info children" ::errorCheck [o::t info parent] "::o" "info parent" ::errorCheck [o info commands t] t "MakeObject" o::z::t move o::z::rt ::errorCheck [o::z info commands rt] rt "renaming leaf" o::z move o::rz ::errorCheck [o::rz info commands rt] rt "renaming node" ::errorCheck [o info commands rz] rz "renaming node" o move rx ::errorCheck [rx info commands rz] rz "renaming root " ::errorCheck [info commands rx] rx "renaming root" rx destroy C($i) destroy Class A A instproc x {a1 args} { my set var $a1 } A a A a::n -x "1 2 3" ::errorCheck [::a::n set var] "1 2 3" "arg passing - init dash" } } @ TestX assertions { description {Regression test object testing the assertions.} } TestX assertions -proc run {{n 20}} { if {!$::nsf::config(assertions)} { return } for {set i 0} {$i < $n} {incr i} { Class C($i) set r [C($i) invar { {$a > 2} {$c < 3} {$d > 5} {#a } {#b } }] C($i) instinvar { {$a > 2} {$c < 3} {$d > 5} {#a } {#b } } ::errorCheck [C($i) info invar] {{$a > 2} {$c < 3} {$d > 5} {#a } {#b }} \ "Class invar " ::errorCheck [C($i) info invar] {{$a > 2} {$c < 3} {$d > 5} {#a } {#b }} \ "Class instinvar " Object b($i) b($i) invar { {$a > 2} {$c < 3} {$d > 5} {#a} {#b} } ::errorCheck [C($i) info invar] {{$a > 2} {$c < 3} {$d > 5} {#a } {#b }} \ "Object invar " b($i) proc p {a b c} { return p } {pre1 pre2 pre3} {post1 post2 post3} ::errorCheck [b($i) info pre p] {pre1 pre2 pre3} \ "Obj proc pre assertion " ::errorCheck [b($i) info post p] {post1 post2 post3} \ "Obj proc post assertion " C($i) instproc p {a b c} { return p } {} {post1 post2 post3} ::errorCheck [C($i) info instpre p] "" \ "CL proc pre assertion " ::errorCheck [C($i) info instpost p] {post1 post2 post3} \ "CL proc post assertion " C(0) set a 3; C(0) set c 2; C(0) set d 7; C(0) set f 50; C(0) check all C(0) proc checkit {} { C(0) instvar a c d f ::errorCheck [my info check] {invar instinvar pre post} \ "check options != all" # turn obj-invar off C(0) check {pre post instinvar} C(0) set c 10 ::errorCheck [my info check] {instinvar pre post} \ "check options != instinvar pre post" } {{$f > 10}} {{$f < 100}} C(0) checkit } for {set i 0} {$i < $n} {incr i} { b($i) destroy C($i) destroy } Object b b proc p {a b c} { return p } {pre1 pre2 pre3} {post1 post2 post3} ::rename b a ::errorCheck [a info pre p] {pre1 pre2 pre3} \ "renamed Obj proc pre assertion " ::errorCheck [a info post p] {post1 post2 post3} \ "renamed Obj proc post assertion " Class Sensor -parameter {{value 1}} Sensor instinvar { {[regexp {^[0-9]$} [my value]] == 1} } Sensor s s check all Sensor instproc x {} { s value } { {[regexp {^[0-9]$} [my value]] == 1} } {} s x s value # inheritance Class A -parameter {{x 1} {y 1}} A instinvar {{$x == 1}} A instproc xTo2 args { my set x 2 } A instproc yTo2 args { my set y 2 } {} {{$y == 1}} A a -check all if {![catch {a xTo2} err]} { set err "ok" } else { a check {} a set x 1 a check all } ::errorCheck $err {assertion failed check: {$x == 1} in proc 'set'} \ "inheritance a xTo2" if {![catch {a yTo2} err]} { set err "ok" } ::errorCheck $err {assertion failed check: {$y == 1} in proc 'yTo2'} \ "inheritance a yTo2" Class B -superclass A B b -check all if {![catch {b xTo2} err]} { set err "ok" } else { b check {} b set x 1 b check all } ::errorCheck $err {assertion failed check: {$x == 1} in proc 'set'} \ "inheritance b xTo2" if {![catch {b yTo2} err]} { set err "ok" } ::errorCheck $err {assertion failed check: {$y == 1} in proc 'yTo2'} \ "inheritance b yTo2" a destroy } @ TestX filterAddRemove { description {Regression test object testing adding/removing of filters.} } TestX filterAddRemove -proc run {{n 20}} { set ::filterCount 0 for {set i 0} {$i < $n} {incr i} { Class SA($i) Class SB($i) Class SC($i) -superclass [list SB($i) SA($i)] SA($i) instproc fa args { incr ::filterCount my set x 150 set r [next] lappend ::result "$r-[self class]::[self proc]" return $r } SA($i) instproc f2 args { incr ::filterCount my set x 150 set r [next] lappend ::result "$r-[self class]::[self proc]" return $r } SB($i) instproc f2 args { incr ::filterCount my set x 150 set r [next] lappend ::result "$r-[self class]::[self proc]" return $r } SB($i) instproc fb args { incr ::filterCount my set x 150 set r [next] lappend ::result "$r-[self class]::[self proc]" return $r } SC($i) instproc fc args { incr ::filterCount my set x 150 set r [next] lappend ::result "$r-[self class]::[self proc]" return $r } SC($i) instfilter fc SB($i) instfilter {fb f2} SA($i) instfilter {fa f2} Class T T proc s {} { return } Class Filtered${i} -superclass SC($i) Filtered${i} instproc testfilter args { incr ::filterCount T s set r [next] lappend ::result "$r-[self class]::[self proc]" return $r } Filtered${i} instfilter testfilter Filtered${i} instproc a args { return "in a" } Filtered${i} f${i} set ::result "" set erg [f${i} a] ::errorCheck $::result \ "{in a-::SA($i)::f2} {in a-::SA($i)::fa} {in a-::SB($i)::f2} {in a-::SB($i)::fb} {in a-::SC($i)::fc} {in a-::Filtered${i}::testfilter}" \ "Filter Test - add" SC($i) instfilter {} SB($i) instfilter fb SA($i) instfilter {} set ::result "" set erg [f${i} a] ::errorCheck $::result "{in a-::SB($i)::fb} {in a-::Filtered${i}::testfilter}" \ "Filter Test - remove" f${i} proc procFilter args { return "[next]-[self class]::[self proc]" } f${i} filter {fa f2 procFilter} set ::result "" set erg [f${i} a] ::errorCheck $::result "{in a-::SB($i)::fb} {in a-::Filtered${i}::testfilter} {in a-::procFilter-::SB($i)::f2} {in a-::procFilter-::SA($i)::fa}" \ "Obj Filter Test call three filter + instfilter" ::errorCheck [f${i} info filter]-[SB($i) info instfilter]-[SC($i) info instfilter] \ "fa f2 procFilter-::procFilter-fb-" \ "filter infos" ::errorCheck [f${i} filtersearch fa]-[f${i} filtersearch fb]-[f${i} filtersearch procFilter] \ "::SA($i) instproc fa-::procFilter-::SB($i) instproc fb-::procFilter-::f${i} proc procFilter-::procFilter" \ "filtersearch" Filtered${i} instfilter {} SB($i) instfilter {} set ::result "" set erg [f${i} a] ::errorCheck $::result \ "{in a-::procFilter-::SB($i)::f2} {in a-::procFilter-::SA($i)::fa}" \ "only obj filter" f${i} filter {} set ::result "" set erg [f${i} a] ::errorCheck $erg "in a" \ "obj filter remove" } for {set i 0} {$i < $n} {incr i} { SA($i) destroy SB($i) destroy SC($i) destroy } ::errorCheck $::filterCount 1080 \ "Filter Test - Filter Count -- Got: $::filterCount" # # instvar test # Object o o set x 1 Object o1 o1 set x 11 Object o2 o2 proc t {} { # multiple imports for existing (x) and not existing vars (y) o instvar x y append result "x: $x " append result "y: ie [info exists y] me [my exists y] " \ "iv '[info vars y]' oe [o exists y] oiv '[o info vars y]' // " set y 100 append result "y: ie [info exists y] me [my exists y] " \ "iv '[info vars y]' oe [o exists y] oiv '[o info vars y]'" ::errorCheck $result \ "x: 1 y: ie 0 me 0 iv 'y' oe 0 oiv '' // y: ie 1 me 0 iv 'y' oe 1 oiv 'y'" \ "instvar test 1 failed" set result "" o1 instvar x y append result "x: $x " append result "y: ie [info exists y] me [my exists y] " \ "iv '[info vars y]' oe [o1 exists y] oiv '[o1 info vars y]' // " set y 101 append result "y: ie [info exists y] me [my exists y] " \ "iv '[info vars y]' oe [o1 exists y] oiv '[o1 info vars y]'" ::errorCheck $result \ "x: 11 y: ie 0 me 0 iv 'y' oe 0 oiv '' // y: ie 1 me 0 iv 'y' oe 1 oiv 'y'" \ "instvar test 2 failed" } o2 t o destroy o1 destroy o2 destroy global filterResult set filterResult "" Object a -requireNamespace a set o 12 a set p 13 Class A A set m 14 Object instproc f args { global filterResult a instvar o p A instvar m ::append filterResult " [self] [self calledproc] [self callingproc]" ::append filterResult " $o $p $m" next } proc x {} { set ::a::e xxx } Object instfilter f x rename x "" ::errorCheck $::a::e xxx \ "filterAddRemove: instvar test -- proc set failed" a set e yyy ::errorCheck $::a::e yyy \ "filterAddRemove: instvar test -- obj set failed" ::errorCheck $filterResult " ::A instvar f 12 13 14 ::a set run 12 13 14" \ "filterAddRemove: instvar test -- instvar filter failed" Object instfilter "" Object instproc f args { next } Object instfilter f ::errorCheck [Object o] "::o" \ "filterAddRemove: Object creation with filter" # This produces a bug, if not # RUNTIME_STATE(in)->returnCode = TCL_OK; # in ObjDispatch -> UNKNOWN handling */ # abrupt stop of program because result is set to XOTCL_UNKNOWN # instead of TCL_ERROR, as it should be catch {puts ${ZZZZZZZZZZZZZZZ::ZZZZZ}} o set r 43 ::errorCheck [o set r] "43" \ "filterAddRemove: Object creation with filter: setting var" Object instfilter "" # test for CmdListReplaceCmd set ::r "" Class A A instproc f2 args {lappend ::r [self class]-[self proc]; next} Class C -superclass A Class D -superclass C D instfilter {f2} D d d filter {f2} C instproc f2 args {lappend ::r [self class]-[self proc]; next} set ::r "" d set r 1 ::errorCheck $::r "::C-f2 ::A-f2" \ "filter method addition" o proc m {} { } o proc f args { my incr count next } o set count 0 o filter f o m ::errorCheck [o set count] 2 "filter count" o filter "" set filterstate [::nsf::configure filter off] o set count 0 o m ::errorCheck [o set count]-$filterstate 0-1 "filter off + old state" o filter "" ::nsf::configure filter on set ::r "" Object instproc f args { set r [next] lappend ::r [self]-[self calledproc] return $r } Class D D filter f D d1 ::errorCheck $::r "::D-d1 ::D-alloc ::D-create ::D-unknown" \ "filter state after next" Object instproc f {} {} D destroy } @ TestX filterClassChange { description {Regression test object testing class changes of filters.} } TestX filterClassChange -proc run {{n 20}} { for {set i 0} {$i < $n} {incr i} { Class A($i) Class B A($i) instproc f args { set result pre*[self]*[self proc]*$args lappend result [next] post*[self]*[self proc] return $result } A($i) o($i) o($i) proc change {} { my class B } o($i) proc call {} { return in-call } A($i) instfilter f set erg [o($i) call] ::errorCheck $erg "pre*::o($i)*f* in-call post*::o($i)*f" \ "Filter Class Change -- Call before change" o($i) change set erg [o($i) call] ::errorCheck $erg "in-call" \ "Filter Class Change -- Call after change" # testing deleting a filter proc Class F F instproc testf args {return filtered} F instfilter testf F f1 ::errorCheck [f1 set r 45] "filtered" "Deleting a filter proc ... before" F instproc testf {} {} ::errorCheck [f1 set r 45] "45" "Deleting a filter proc ... after" # testing remove a superclass Class F1 Class F2 -superclass F1 Class F3 -superclass F2 F1 instproc testf args { set r [next] lappend ::result $r-filtered return $r } F2 instproc testf2 args { set r [next] lappend ::result $r-filtered return $r } F3 instfilter {testf testf2} F3 f2 set ::result "" ::errorCheck [f2 filtersearch testf2] "::F2 instproc testf2" "filtersearch 2" set ::result "" f2 set r 45 ::errorCheck $::result "45-filtered 45-filtered" \ "Removing a superclass ... before" F3 superclass [F1 info superclass] set ::result "" ::errorCheck [f2 filtersearch testf2] "" "filtersearch 2 after" set ::result "" ::errorCheck [f2 set r 45] "45" "Class F2 removed from classtree ... after" } B destroy for {set i 0} {$i < $n} {incr i} {A($i) destroy} } @ TestX filterGuards { description {Regression test object testing filter guards.} } TestX filterGuards -proc run {{n 20}} { global filterResult for {set i 0} {$i < $n} {incr i} { set ::filterResult "" Class A A instproc f2 args { global filterResult append filterResult "-[self]-[self proc]-[self class]-[self calledproc]" next } Class B -superclass A B instproc f1 args { global filterResult append filterResult "-[self]-[self proc]-[self class]-[self calledproc]" next } B instproc f3 args { global filterResult append filterResult "-[self]-[self proc]-[self class]-[self calledproc]" next } B instproc f01 args { append ::filterResult 1; next } B instproc f02 args { append ::filterResult 2; next } B instfilter {{f1 -guard "1 2 3"}} ;# guard with error set r [catch {B b}] ::errorCheck $r-$filterResult "1-" "Filter guard: Filter guard with error" #b destroy set ::filterResult "" B instfilter {f01 {f02 -guard "a b"}} set r [catch {B b}] ::errorCheck $r-$filterResult "1-1" "Filter guard: Filter guard with error via next" set ::filterResult "" B instfilter {{f1 -guard "1<0"}} ;# failing guard B b ::errorCheck $filterResult "" "Filter guard: Filter never to be applied" b destroy A instproc f1 args { global filterResult append filterResult "-[self]-[self proc]-[self class]-[self calledproc]" next } set filterResult "" B b ::errorCheck $filterResult "" \ "Filter guard: Filter never to be applied + filter inheritance on this filter" # filter w/o guard -> has to be applied A instfilter f1 b destroy set filterResult "" B b set r1 "-::b-f1-::A-configure-::b-f1-::A-residualargs-::b-f1-::A-init" set r2 "-::b-f1-::A-configure-::b-f1-::A-init" ::errorCheck $filterResult $r2 \ "Filter guard: two different filters, same name + different class, one guarded, one not" # two filter w/o guard -> both have to be applied B instfilter f1 b destroy set filterResult "" B b set r1 "-::b-f1-::B-configure-::b-f1-::A-configure-::b-f1-::B-residualargs-::b-f1-::A-residualargs-::b-f1-::B-init-::b-f1-::A-init" set r2 "-::b-f1-::B-configure-::b-f1-::A-configure-::b-f1-::B-init-::b-f1-::A-init" ::errorCheck $filterResult $r2 \ "Filter guard: two different filters, both not guarded anymore" # three filters with guards, not to be applied, in one chain b destroy A instfilter {} B instfilter {{f1 -guard {0}} {f3 -guard {0}} {f2 -guard {0}}} set filterResult "" B b ::errorCheck $filterResult "" "Filter guard: three filters in one chain" # three times the same filter --> guards are and-combined set filterResult "" B instfilter {{f2 -guard {[self calledproc] eq "set" || [self] == "::b2"}}} A instfilter {{f2 -guard {[self] == "::b2"}}} B b1 B b2 if {$i == 0} { set r1 "-::b2-f2-::A-configure-::b2-f2-::A-residualargs-::b2-f2-::A-init" set r2 "-::b2-f2-::A-configure-::b2-f2-::A-init" ::errorCheck $filterResult $r2 \ "Filter guard: creation with less restrictive guards" } else { set r1 "-::b2-f2-::A-cleanup-::b2-f2-::A-configure-::b2-f2-::A-residualargs-::b2-f2-::A-init" set r2 "-::b2-f2-::A-cleanup-::b2-f2-::A-configure-::b2-f2-::A-init" ::errorCheck $filterResult $r2 \ "Filter guard: creation with less restrictive guards (b)" } set filterResult "" b1 set x 45 ::errorCheck $filterResult "-::b1-f2-::A-set" \ "Filter guard: setting restricted object" set filterResult "" b1 info class ::errorCheck $filterResult "" \ "Filter guard: info restricted object (no guard applies)" set filterResult "" b2 info class ::errorCheck $filterResult "-::b2-f2-::A-info" \ "Filter guard: setting restricted object (2nd guard applies)" b1 filter {{f2 -guard {[self calledproc] eq "info"}}} set filterResult "" b1 proc a {} { # } ::errorCheck $filterResult "" \ "Filter guard: proc on restricted object (no guard applies)" set filterResult "" b1 info class ::errorCheck $filterResult "-::b1-f2-::A-info" \ "Filter guard: info filtered by object filter guard" # checking infos ::errorCheck [b1 info filterguard f2]-[B info instfilterguard f2]-[A info instfilterguard f2] \ {[self calledproc] eq "info"-[self calledproc] eq "set" || [self] == "::b2"-[self] == "::b2"} \ "Filter guard: info filtered by object filter guard" # checking info -guards option Class A A instproc f1 args {next} A instproc fx args {next} Class B -superclass A B instproc f1 args {next} B instproc f2 args {next} B b B instfilter {{f1 -guard {[self] eq "::b"}} {f2 -guard 0} f1} b filter {{f1 -guard {[self] eq "::b"}} {f2 -guard 0}} ::errorCheck [B info instfilter] {f1 f2} "info filter order a" ::errorCheck [B info instfilter -guards] {f1 {f2 -guard 0}} "info filter order b" ::errorCheck [b info filter] {f1 f2} "info filter order c" ::errorCheck [b info filter -guards] {{f1 -guard {[self] eq "::b"}} {f2 -guard 0}} "info filter order d" A instfilter {f1 fx} A a a proc x args {next} a filter x ::errorCheck [b info filter -order] \ "{::B instproc f1} {::B instproc f2} {::A instproc f1} {::A instproc fx}" "info filter-order- 2a" ::errorCheck [a info filter -order] \ "{::a proc x} {::A instproc f1} {::A instproc fx}" "info filter-order- 2b" ::errorCheck [b info filter -order]-[a info filter -order] "{::B instproc f1} {::B instproc f2} {::A instproc f1} {::A instproc fx}-{::a proc x} {::A instproc f1} {::A instproc fx}" \ {[self] -- Filter guard: -order option} ::errorCheck [b info filter -order -guards] {{f1 -guard {[self] eq "::b"}} {f2 -guard 0}} \ "filter order guards 1" ::errorCheck [a info filter -order -guards] {x} \ "filter order guards 2" Class Foo Foo instproc init {args} {my set bar hello} Foo instproc baz {args} { my instvar bar return $bar } Foo instproc myFilter {args} { lappend ::r myFilter->[self calledproc] my set r 4 next } Foo instfilter myFilter Foo instfilterguard myFilter { ([self calledproc] eq "baz") } Foo instfilterguard myFilter { ([self calledproc] eq "baz") } set f [Foo new] $f baz ::errorCheck [$f baz] "hello" {Filter guard from method call} Foo instfilterguard myFilter {} set ::r "" Foo create f f filter myFilter f filterguard myFilter { ([self calledproc] eq "baz") } lappend ::r [f baz] [f set r 1] f filterguard myFilter {} lappend ::r [f baz] [f set r 1] set r1 [list myFilter->configure myFilter->residualargs myFilter->init myFilter->set myFilter->filter myFilter->filterguard myFilter->baz hello 1 myFilter->baz myFilter->instvar myFilter->set hello 1] set r2 [list myFilter->configure myFilter->init myFilter->set myFilter->filter myFilter->filterguard myFilter->baz hello 1 myFilter->baz myFilter->instvar myFilter->set hello 1] ::errorCheck $::r $r2 "Filter guard from method call" f destroy Class Room Room instproc open {} {lappend ::r [self proc]} Room instproc x {} {lappend ::r [self proc]} Room instproc loggingFilter args { lappend ::r [self proc]-[self calledproc] next } Room instproc callsMethod {method calledproc} { return [string match $calledproc $method] } Room instproc callsLevel2 {} { set level [self guardedlevel] lappend ::r $level set calledproc [uplevel $level self calledproc] lappend ::r $calledproc } Room instfilter loggingFilter Room instfilterguard loggingFilter {[my callsMethod open [self calledproc]]} Room r set ::r "" r open r x ::errorCheck $::r "loggingFilter-open open x" {info guarded scope} } } @ TestX mixinGuards { description {Regression test object testing mixin guards.} } TestX mixinGuards -proc run {{n 20}} { set ::r "" Class Fly Fly instproc fly {} {lappend ::r "[my signature]: yippee, fly like an eagle!"} Class Sing Sing instproc sing {} {lappend ::r "[my signature]: what a difference a day make"} Class Animal -parameter age Animal instproc unknown args { lappend ::r "[my signature]: how should i $args?"} Animal instproc signature {} { return "[self] [my info class] ([my age] years)" } Class Bird -superclass Animal Class Penguine -superclass Bird Class Parrot -superclass Bird Class Duck -superclass Bird Parrot tweedy -age 1 Penguine pingo -age 5 Duck donald -age 4 Parrot lora -age 6 Bird instmixin {{Fly -guard {[my age]>2 && ![my istype Penguine]}} Sing} foreach bird {tweedy pingo donald lora} { $bird fly } ::errorCheck [set ::r] [list \ {::tweedy ::Parrot (1 years): how should i fly?} \ {::pingo ::Penguine (5 years): how should i fly?} \ {::donald ::Duck (4 years): yippee, fly like an eagle!} \ {::lora ::Parrot (6 years): yippee, fly like an eagle!}] \ {Simple Instmixin Guard} set ::r "" tweedy age 3 pingo class Duck lora class Penguine foreach bird {tweedy pingo donald lora} { $bird fly } ::errorCheck [set ::r] [list \ {::tweedy ::Parrot (3 years): yippee, fly like an eagle!} \ {::pingo ::Duck (5 years): yippee, fly like an eagle!} \ {::donald ::Duck (4 years): yippee, fly like an eagle!} \ {::lora ::Penguine (6 years): how should i fly?}] \ {Simple Instmixin Guard ... Class Change} set ::r "" pingo mixin {{Fly -guard {[my age]>2}} Sing} foreach i { {Bird info instmixin -guards} {pingo info mixin -guards} {pingo info mixin -order -guards}} { lappend ::r "$i [eval $i]" } ::errorCheck [set ::r] [list \ {Bird info instmixin -guards {::Fly -guard {[my age]>2 && ![my istype Penguine]}} ::Sing} \ {pingo info mixin -guards {::Fly -guard {[my age]>2}} ::Sing} \ {pingo info mixin -order -guards {::Fly -guard {[my age]>2}} ::Sing}] \ {Simple Instmixin Guard ... Info} set ::r "" Class POM-start Class POM-end Class PCM-start Class PCM-end pingo mixin {POM-start {Fly -guard {[my age]>2}} Sing POM-end} Bird instmixin {PCM-start {Fly -guard {[my age]>2 && ![my istype Penguine]}} Sing PCM-end} pingo class Penguine foreach i { {Bird info instmixin -guards} {pingo info mixin -guards} {pingo info mixin -order -guards}} { lappend ::r "$i [eval $i]" } ::errorCheck [Bird info instmixin -guards] \ {::PCM-start {::Fly -guard {[my age]>2 && ![my istype Penguine]}} ::Sing ::PCM-end} pingo1 ::errorCheck [pingo info mixin -guards] \ {::POM-start {::Fly -guard {[my age]>2}} ::Sing ::POM-end} pingo2 ::errorCheck [pingo info mixin -order -guards] \ {::POM-start {::Fly -guard {[my age]>2}} ::Sing ::POM-end ::PCM-start ::PCM-end} pingo3 set ::r "" pingo fly ::errorCheck [set ::r] [list \ {::pingo ::Penguine (5 years): yippee, fly like an eagle!}] \ {Same Mixin Guard ... most specific counts} set ::r "" Animal a -set age 20 a mixin Fly a mixinguard ::Fly {[my age] > 3} a fly lappend ::r [a info mixin -guards] lappend ::r [a info mixin -order -guards] a set age 2 a fly a mixinguard ::Fly {[my age] > 4} a fly set info "" lappend info [a info mixinguard Fly] lappend ::r [a info mixin -guards] lappend ::r [a info mixin -order -guards] a mixinguard ::Fly {} a fly lappend ::r [a info mixin -guards] lappend info [a info mixinguard Fly] lappend ::r [a info mixin -order -guards] ::errorCheck [set ::r] [list \ {::a ::Animal (20 years): yippee, fly like an eagle!} \ {{::Fly -guard {[my age] > 3}}} {{::Fly -guard {[my age] > 3}}} \ {::a ::Animal (2 years): how should i fly?} \ {::a ::Animal (2 years): how should i fly?} \ {{::Fly -guard {[my age] > 4}}} {{::Fly -guard {[my age] > 4}}} \ {::a ::Animal (2 years): yippee, fly like an eagle!} \ ::Fly ::Fly] \ {mixinguard method} set ::r "" Class A -superclass Animal A a -set age 20 A instmixin Fly A instmixinguard ::Fly {[my age] > 3} lappend info [A info instmixinguard ::Fly] a fly lappend ::r [A info instmixin -guards] lappend ::r [a info mixin -order -guards] a set age 2 a fly A instmixinguard ::Fly {[my age] > 4} lappend info [A info instmixinguard ::Fly] a fly lappend ::r [A info instmixin -guards] lappend ::r [a info mixin -order -guards] A instmixinguard ::Fly {} lappend info [A info instmixinguard ::Fly] a fly lappend ::r [A info instmixin -guards] lappend ::r [a info mixin -order -guards] ::errorCheck [set ::r] [list \ {::a ::A (20 years): yippee, fly like an eagle!} \ {{::Fly -guard {[my age] > 3}}} {{::Fly -guard {[my age] > 3}}} \ {::a ::A (2 years): how should i fly?} \ {::a ::A (2 years): how should i fly?} \ {{::Fly -guard {[my age] > 4}}} {{::Fly -guard {[my age] > 4}}} \ {::a ::A (2 years): yippee, fly like an eagle!} \ ::Fly ::Fly] \ {instmixinguard method} ::errorCheck [set info] [list {[my age] > 4} {} {[my age] > 3} \ {[my age] > 4} {} ] {info (inst)mixinguard} Class create C C method foo {} {return 0} Class create M M method foo {} {return 1} Class create M1 M1 method foo {} {return 2} C c1 -mixin {M} ::errorCheck [c1 foo] 1 "no mixin guard" C c1 -mixin {{M -guard {0>1}}} ::errorCheck [c1 foo] 0 "mixin guard prevents call" C c1 -mixin {{M -guard {0 1 2}}} ::errorCheck [catch {c1 foo}] 1 "mixin guard with error" M method foo {} {next} C c1 -mixin {M {M1 -guard {a b c}}} ::errorCheck [catch {c1 foo}] 1 "mixin guard with error in next" c1 destroy C destroy M destroy M1 destroy } @ TestX filterSimpleObserver { description {Regression test object testing a simple observer using filters. } } TestX filterSimpleObserver -proc run {{n 20}} { set ::filterCount 0 for {set i 0} {$i < $n} {incr i} { set ::filterResult [list] Class NetAccess$i Class Http$i -superclass NetAccess$i Class TransferDialog$i TransferDialog$i proc addObserver cl { $cl instproc observerFilter args { set calledMethod [self calledproc] set callingClass [my info class] incr ::filterCount set result [next] my set r 34 foreach var {args calledMethod callingClass result} { if {[info vars $var] != $var} { puts stderr "[self] -- Simple Observer - info vars in filter" exit } } lappend ::filterResult [self]-[self class]-[my info class]-$args-[self calledproc]-[self callingproc]-$result return $result } $cl instfilter observerFilter } TransferDialog$i instproc show {i} { next TransferDialog${i} addObserver NetAccess$i [self class] instvar observingObjects lappend observingObjects(::NetAccess$i) [self] } Http$i parameter {a be bu} Http$i instproc path x { my set path $x } Http$i instproc query x { my set [self proc] $x } Http$i instproc init {args} { my set url abc next my instvar query path bu if {![info exists query] || ![info exists path] || ![info exists bu] || $query ne "q"} { puts stderr "FAILED - [self] -- Simple Observer - Variable Init"; exit } } Http$i instproc GET {x} { my instvar query url path if {[info exists query]} { append url ?$query append path ?$query } set ::baseLevel [info level] if {0 != [info level] - $::baseLevel} { puts stderr "FAILED - [self] -- Simple Observer - info level in filtered proc\n\ expected 0, got [expr {[info level] - $::baseLevel}]" exit } foreach var {x path query url} { if {[info vars $var] != $var} { puts stderr "FAILED - [self] -- Simple Observer - info vars in filtered proc"; exit } } return $url } TransferDialog$i t($i) t($i) show $i Http$i h($i) -query q -path p -bu b set ::filterResult [list] set erg [h($i) GET 1] ::errorCheck $erg "abc?q" \ "Simple Observer - Filter Return" ::errorCheck $::filterResult "{::h($i)-::NetAccess$i-::Http$i-query url path-instvar-GET-} ::h($i)-::NetAccess$i-::Http$i-1-GET-run-abc?q" \ "Simple Observer - Filter Return" } for {set i 0} {$i < $n} {incr i} { NetAccess$i instfilter {} h($i) destroy t($i) destroy Http$i destroy NetAccess$i destroy TransferDialog$i destroy } ::errorCheck $::filterCount 260 \ "Simple Observer - Filter Count" } @ TestX stdargs { description { Regression test object testing the ability of the next primitive to pass arguments without naming them. } } TestX stdargs -proc run {{n 20}} { for {set i 0} {$i < $n} {incr i} { Class C Class D Class A -superclass {C D} Class B -superclass A C instproc t {} { next return } D instproc t args { ::errorCheck $args "" --noArgs next return } A instproc t {a b args} { if {$a != 1 || $b != 2 || $args != {3 4 5 6 7 8 9}} { puts stderr "FAILED - [self] -- StdArgs not computed"; exit } next --noArgs return } B instproc t {a b args} { if {$a != 1 || $b != 2 || $args != {3 4 5 6 7 8 9}} { puts stderr "FAILED -[self] -- StdArgs not computed"; exit } next return } B x x t 1 2 3 4 5 6 7 8 9 } foreach o {x A B C D} {$o destroy} } @ TestX filterInfo { description{ Regression test object testing introspection of filters. } } TestX filterInfo # Helper Procs proc ::showStack {{m 100}} { set r "" set max [info level] if {$m<$max} {set max $m} for {set i 0} {$i < $max} {incr i} { set r ${r}-$i=[info level [expr -$i]] } return $r } proc ::showCall {} { set n "" for {set level -1} {1} {incr level -1} { set p [info level $level] if {[lindex $p 0] eq "next"} {set n "next:"} break } return [showStack] } filterInfo proc run {{n 20}} { # TODO for now, deactivated, since different configure-semantics leads to very different traces" return for {set i 0} {$i < $n} {incr i} { global FInfo set FInfo "" Class FI FI proc addFilter {classname} { $classname instproc infoFilter args { global FInfo lappend FInfo \ [list callingclass [self callingclass] \ filterreg [self filterreg] \ callingobject [self callingobject] \ callingproc [self callingproc] \ calledproc [self calledproc]] set r [next] lappend FInfo \ [list self [self] proc [self proc] class [self class] \ infoclass [my info class] r $r] return $r } $classname instfilter infoFilter } Class C0 FI addFilter C0 C0 instproc m1 {} { my instvar aa bb cc set cc 1 } Class C1 -superclass C0 C1 instproc init args { my set a 1 my set c 22 next } C1 instproc m1 args { set r [next] my instvar a b cc return $r--${a}--[set cc] } set safedObjFilters [Object info filter] Object instfilter "" C1 c1 Object instfilter $safedObjFilters if {$i == 0} { ::errorCheck "$FInfo" \ "{callingclass {} filterreg {::C0 instfilter infoFilter} callingobject ::filterInfo callingproc run calledproc configure} {callingclass {} filterreg {::C0 instfilter infoFilter} callingobject ::filterInfo callingproc run calledproc residualargs} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r {}} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r {}} {callingclass {} filterreg {::C0 instfilter infoFilter} callingobject ::filterInfo callingproc run calledproc init} {callingclass ::C1 filterreg {::C0 instfilter infoFilter} callingobject ::c1 callingproc init calledproc set} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r 1} {callingclass ::C1 filterreg {::C0 instfilter infoFilter} callingobject ::c1 callingproc init calledproc set} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r 22} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r {}}" \ "Wrong filtering of instproc creation C/C1" } else { ::errorCheck "$FInfo" \ "{callingclass {} filterreg {::C0 instfilter infoFilter} callingobject ::filterInfo callingproc run calledproc cleanup} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r {}} {callingclass {} filterreg {::C0 instfilter infoFilter} callingobject ::filterInfo callingproc run calledproc configure} {callingclass {} filterreg {::C0 instfilter infoFilter} callingobject ::filterInfo callingproc run calledproc residualargs} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r {}} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r {}} {callingclass {} filterreg {::C0 instfilter infoFilter} callingobject ::filterInfo callingproc run calledproc init} {callingclass ::C1 filterreg {::C0 instfilter infoFilter} callingobject ::c1 callingproc init calledproc set} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r 1} {callingclass ::C1 filterreg {::C0 instfilter infoFilter} callingobject ::c1 callingproc init calledproc set} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r 22} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r {}}" \ "Wrong filtering of instproc creation C/C1 (b)" } set FInfo "" set result [c1 m1] ::errorCheck $FInfo \ "{callingclass {} filterreg {::C0 instfilter infoFilter} callingobject ::filterInfo callingproc run calledproc m1} {callingclass ::C0 filterreg {::C0 instfilter infoFilter} callingobject ::c1 callingproc m1 calledproc instvar} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r {}} {callingclass ::C1 filterreg {::C0 instfilter infoFilter} callingobject ::c1 callingproc m1 calledproc instvar} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r {}} {self ::c1 proc infoFilter class ::C0 infoclass ::C1 r 1--1--1}" \ "Wrong filtering of c1 m1" set FInfo "" ::errorCheck $result \ "1--1--1" "Wrong return result of Filter Example 2 'c1 m1' " Class T0 FI addFilter T0 T0 instproc m {} { set e -0=showStack-1=showCall-2=m-3=m-4=m-5=run-6=run if {[string first $e [showCall]] == -1} { puts stderr "FAILED - Wrong calling stack in T0 m: [showCall]" puts stderr "expected = '$e'" puts stderr "got = '[showCall]'" exit } return [self]-[self proc]-[self class]-[my info class] } Class T1 -superclass T0 T1 instproc m {} { set e 0=showStack-1=showCall-2=m-3=m-4=run-5=run if {[string first $e [showCall]] == -1} { puts stderr "FAILED - Wrong calling stack in T1 m: [showCall]" puts stderr "expected = '$e'" puts stderr "got = '[showCall]'" exit } set r1 before-[self]-[self proc]-[self class]-[my info class] set r2 [next] set r after-[self]-[self proc]-[self class]-[my info class]-${r1}-$r2 } T1 t set FInfo "" set result [t m] ::errorCheck $FInfo \ "{callingclass {} filterreg {::T0 instfilter infoFilter} callingobject ::filterInfo callingproc run calledproc m} {callingclass ::T1 filterreg {::T0 instfilter infoFilter} callingobject ::t callingproc m calledproc info} {self ::t proc infoFilter class ::T0 infoclass ::T1 r ::T1} {callingclass ::T0 filterreg {::T0 instfilter infoFilter} callingobject ::t callingproc m calledproc info} {self ::t proc infoFilter class ::T0 infoclass ::T1 r ::T1} {callingclass ::T1 filterreg {::T0 instfilter infoFilter} callingobject ::t callingproc m calledproc info} {self ::t proc infoFilter class ::T0 infoclass ::T1 r ::T1} {self ::t proc infoFilter class ::T0 infoclass ::T1 r after-::t-m-::T1-::T1-before-::t-m-::T1-::T1-::t-m-::T0-::T1}" \ "Wrong filtering of t m" set FInfo "" ::errorCheck $result \ "after-::t-m-::T1-::T1-before-::t-m-::T1-::T1-::t-m-::T0-::T1" \ "Wrong return result of Filter Example 2 \"t m\" " } c1 destroy for {set i 0} {$i < $n} {incr i} { global InfoTraceResult Object instfilter "" Object InfoTrace InfoTrace proc createInfoTrace cl { $cl instproc infoTraceFilter args { global InfoTraceResult ::set r [next] ::lappend InfoTraceResult [list \ $r-[self]-[self proc]-[self class] \ [my info class]-[self calledproc] \ [self callingproc]-[self callingobject] \ [self callingclass]-[self filterreg]] return $r } $cl instfilter infoTraceFilter } Class ObjectsClass ObjectsClass anObject Class aClass ObjectsClass instproc aProc {} {aClass create anotherObject} InfoTrace createInfoTrace Object set InfoTraceResult "" set r [anObject aProc] if {$i > 0} { ::errorCheck $InfoTraceResult \ "{::aClass-::xotcl::objectInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, commands, default, filter, filterguard, forward, hasnamespace, info, invar, is, method, methods, mixin, mixinguard, nonposargs, parametercmd, parent, post, pre, precedence, procs, slotobjects, vars-class} info-::anotherObject {::xotcl::Object-::xotcl::Object instfilter infoTraceFilter}} {-::anotherObject-infoTraceFilter-::xotcl::Object ::aClass-cleanup aProc-::anObject {::ObjectsClass-::xotcl::Object instfilter infoTraceFilter}} {::aClass-::xotcl::objectInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, commands, default, filter, filterguard, forward, hasnamespace, info, invar, is, method, methods, mixin, mixinguard, nonposargs, parametercmd, parent, post, pre, precedence, procs, slotobjects, vars-class} info-::anotherObject {::xotcl::Object-::xotcl::Object instfilter infoTraceFilter}} {-::anotherObject-infoTraceFilter-::xotcl::Object ::aClass-residualargs aProc-::anObject {::ObjectsClass-::xotcl::Object instfilter infoTraceFilter}} {::aClass-::xotcl::objectInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, commands, default, filter, filterguard, forward, hasnamespace, info, invar, is, method, methods, mixin, mixinguard, nonposargs, parametercmd, parent, post, pre, precedence, procs, slotobjects, vars-class} info-::anotherObject {::xotcl::Object-::xotcl::Object instfilter infoTraceFilter}} {-::anotherObject-infoTraceFilter-::xotcl::Object ::aClass-configure aProc-::anObject {::ObjectsClass-::xotcl::Object instfilter infoTraceFilter}} {::aClass-::xotcl::objectInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, commands, default, filter, filterguard, forward, hasnamespace, info, invar, is, method, methods, mixin, mixinguard, nonposargs, parametercmd, parent, post, pre, precedence, procs, slotobjects, vars-class} info-::anotherObject {::xotcl::Object-::xotcl::Object instfilter infoTraceFilter}} {-::anotherObject-infoTraceFilter-::xotcl::Object ::aClass-init aProc-::anObject {::ObjectsClass-::xotcl::Object instfilter infoTraceFilter}} {::xotcl::Class-::xotcl::classInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, classchildren, classparent, commands, default, filter, filterguard, forward, hasnamespace, heritage, info, instances, instargs, instbody, instcommands, instdefault, instfilter, instfilterguard, instforward, instinvar, instmixin, instmixinguard, instmixinof, instnonposargs, instparametercmd, instpost, instpre, instprocs, invar, is, method, methods, mixin, mixinguard, mixinof, nonposargs, parameter, parametercmd, parent, post, pre, precedence, procs, slotobjects, slots, subclass, superclass, vars-class} info-::aClass {::xotcl::Class-::xotcl::Object instfilter infoTraceFilter}} {::anotherObject-::aClass-infoTraceFilter-::xotcl::Object ::xotcl::Class-recreate aProc-::anObject {::ObjectsClass-::xotcl::Object instfilter infoTraceFilter}} {::xotcl::Class-::xotcl::classInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, classchildren, classparent, commands, default, filter, filterguard, forward, hasnamespace, heritage, info, instances, instargs, instbody, instcommands, instdefault, instfilter, instfilterguard, instforward, instinvar, instmixin, instmixinguard, instmixinof, instnonposargs, instparametercmd, instpost, instpre, instprocs, invar, is, method, methods, mixin, mixinguard, mixinof, nonposargs, parameter, parametercmd, parent, post, pre, precedence, procs, slotobjects, slots, subclass, superclass, vars-class} info-::aClass {::xotcl::Class-::xotcl::Object instfilter infoTraceFilter}} {::anotherObject-::aClass-infoTraceFilter-::xotcl::Object ::xotcl::Class-create aProc-::anObject {::ObjectsClass-::xotcl::Object instfilter infoTraceFilter}} {::ObjectsClass-::xotcl::objectInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, commands, default, filter, filterguard, forward, hasnamespace, info, invar, is, method, methods, mixin, mixinguard, nonposargs, parametercmd, parent, post, pre, precedence, procs, slotobjects, vars-class} info-::anObject {::xotcl::Object-::xotcl::Object instfilter infoTraceFilter}} {::anotherObject-::anObject-infoTraceFilter-::xotcl::Object ::ObjectsClass-aProc run-::filterInfo {-::xotcl::Object instfilter infoTraceFilter}}" \ "FilterInfo InfoTrace: Filter information wrong (b)" } else { ::errorCheck $InfoTraceResult \ "{::xotcl::Class-::xotcl::classInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, classchildren, classparent, commands, default, filter, filterguard, forward, hasnamespace, heritage, info, instances, instargs, instbody, instcommands, instdefault, instfilter, instfilterguard, instforward, instinvar, instmixin, instmixinguard, instmixinof, instnonposargs, instparametercmd, instpost, instpre, instprocs, invar, is, method, methods, mixin, mixinguard, mixinof, nonposargs, parameter, parametercmd, parent, post, pre, precedence, procs, slotobjects, slots, subclass, superclass, vars-class} info-::aClass {::xotcl::Class-::xotcl::Object instfilter infoTraceFilter}} {::anotherObject-::aClass-infoTraceFilter-::xotcl::Object ::xotcl::Class-alloc aProc-::anObject {::ObjectsClass-::xotcl::Object instfilter infoTraceFilter}} {::aClass-::xotcl::objectInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, commands, default, filter, filterguard, forward, hasnamespace, info, invar, is, method, methods, mixin, mixinguard, nonposargs, parametercmd, parent, post, pre, precedence, procs, slotobjects, vars-class} info-::anotherObject {::xotcl::Object-::xotcl::Object instfilter infoTraceFilter}} {-::anotherObject-infoTraceFilter-::xotcl::Object ::aClass-residualargs aProc-::anObject {::ObjectsClass-::xotcl::Object instfilter infoTraceFilter}} {::aClass-::xotcl::objectInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, commands, default, filter, filterguard, forward, hasnamespace, info, invar, is, method, methods, mixin, mixinguard, nonposargs, parametercmd, parent, post, pre, precedence, procs, slotobjects, vars-class} info-::anotherObject {::xotcl::Object-::xotcl::Object instfilter infoTraceFilter}} {-::anotherObject-infoTraceFilter-::xotcl::Object ::aClass-configure aProc-::anObject {::ObjectsClass-::xotcl::Object instfilter infoTraceFilter}} {::aClass-::xotcl::objectInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, commands, default, filter, filterguard, forward, hasnamespace, info, invar, is, method, methods, mixin, mixinguard, nonposargs, parametercmd, parent, post, pre, precedence, procs, slotobjects, vars-class} info-::anotherObject {::xotcl::Object-::xotcl::Object instfilter infoTraceFilter}} {-::anotherObject-infoTraceFilter-::xotcl::Object ::aClass-init aProc-::anObject {::ObjectsClass-::xotcl::Object instfilter infoTraceFilter}} {::xotcl::Class-::xotcl::classInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, classchildren, classparent, commands, default, filter, filterguard, forward, hasnamespace, heritage, info, instances, instargs, instbody, instcommands, instdefault, instfilter, instfilterguard, instforward, instinvar, instmixin, instmixinguard, instmixinof, instnonposargs, instparametercmd, instpost, instpre, instprocs, invar, is, method, methods, mixin, mixinguard, mixinof, nonposargs, parameter, parametercmd, parent, post, pre, precedence, procs, slotobjects, slots, subclass, superclass, vars-class} info-::aClass {::xotcl::Class-::xotcl::Object instfilter infoTraceFilter}} {::anotherObject-::aClass-infoTraceFilter-::xotcl::Object ::xotcl::Class-create aProc-::anObject {::ObjectsClass-::xotcl::Object instfilter infoTraceFilter}} {::ObjectsClass-::xotcl::objectInfo-infoTraceFilter-::xotcl::Object {valid options are: args, body, callable, check, children, class, commands, default, filter, filterguard, forward, hasnamespace, info, invar, is, method, methods, mixin, mixinguard, nonposargs, parametercmd, parent, post, pre, precedence, procs, slotobjects, vars-class} info-::anObject {::xotcl::Object-::xotcl::Object instfilter infoTraceFilter}} {::anotherObject-::anObject-infoTraceFilter-::xotcl::Object ::ObjectsClass-aProc run-::filterInfo {-::xotcl::Object instfilter infoTraceFilter}}" \ "FilterInfo InfoTrace: Filter information wrong" } } Object instfilter {} global fUplevelResult set fUplevelResult "" Class FilterMix FilterMix instproc calls args { global fUplevelResult set calledproc [uplevel 1 {self calledproc}] set calledclass [uplevel 1 {self calledclass}] append fUplevelResult "-[self class]-[self proc]-$calledproc-$calledclass" } Class FilterCL -instmixin FilterMix FilterCL instproc filterA args { global fUplevelResult append fUplevelResult -[self class]-[self proc]-[self calledproc]-[self calledclass] my calls next } FilterCL instproc fclproc args {} FilterCL instfilter filterA FilterCL fcl fcl fclproc # ::errorCheck $fUplevelResult "-::FilterCL-filterA-configure-::xotcl::Object-::FilterMix-calls-configure-::xotcl::Object-::FilterCL-filterA-init-::xotcl::Object-::FilterMix-calls-init-::xotcl::Object-::FilterCL-filterA-fclproc-::FilterCL-::FilterMix-calls-fclproc-::FilterCL" "Filter/Mixin Info Uplevel Test" } @ TestX nextTest { description {Regression test object testing the next primitive.} } TestX nextTest -proc run {{n 20}} { for {set i 0} {$i < $n} {incr i} { global result infoNext set result "" set infoNext "" Class X X instproc n {} { append ::infoNext " [self]+[self class]->[self proc]*<[self next]>" next } Class Y -superclass X Y instproc m {} { append ::infoNext " [self]+[self class]->[self proc]*<[self next]>" next } Y instproc n {} { append ::infoNext " [self]+[self class]->[self proc]*<[self next]>" next } Y y y m y n y proc n {} { append ::infoNext " [self]+[self class]->[self proc]*<[self next]>" next } y n ::errorCheck $infoNext " ::y+::Y->m*<> ::y+::Y->n*<::X instproc n> ::y+::X->n*<> ::y+->n*<::Y instproc n> ::y+::Y->n*<::X instproc n> ::y+::X->n*<>" \ "simple self next test" set infoNext "" set result "" Class A A instproc m arg { global result infoNext set result ${result}-[self]-$arg } Class B -superclass A B instproc m arg { global result infoNext set result ${result}-[self]-$arg append infoNext " 2[self]+[self class]->[self proc]*<[self next]>" next } B b0 -m 1 B b -m "" ::errorCheck $result "-::b0-1-::b0-1-::b--::b-" \ "Next Test A/B -- Wrong result" set result "" Class X X instproc init args { global result infoNext set result ${result}-[self]-$args append infoNext " 1[self]+[self class]->[self proc]*<[self next]>" next } X instproc test {} { global result set result ${result}-[self] } X x -test ::errorCheck $result "-::x-::x-" \ "Next Test X -- Wrong result" ::errorCheck $infoNext " 2::b0+::B->m*<::A instproc m> 2::b+::B->m*<::A instproc m> 1::x+::X->init*<::xotcl::Object instproc init>" \ "self next test 2" X destroy x destroy A destroy B destroy b0 destroy b destroy Class MIX MIX instproc mProc args { global result append result "[self]-[self class]-[self next]" next } Object o -mixin MIX o proc mProc args { global result append result "[self]-[self class]-[self next]" } set result "" o mProc ::errorCheck $result "::o-::MIX-::o proc mProc::o--" \ "Next Test Proc & Mixin" o destroy; MIX destroy } } @ TestX init_params { description { Regression test object testing the parameter instance method, the init dash '-' and constructor calling. } } TestX init_params -proc run {{n 20}} { for {set i 0} {$i < $n} {incr i} { global dashResult set dashResult "" set dashResultEnd "" Class A A instproc t0 {} { global dashResult set dashResult ${dashResult}*[self proc] } A instproc t1 {a} { global dashResult set dashResult ${dashResult}*[self proc]-$a } A instproc t2 {a b} { global dashResult set dashResult ${dashResult}*[self proc]-${a}-$b } A instproc t3 {a b c} { global dashResult set dashResult ${dashResult}*[self proc]-${a}-${b}-$c } A a set dashResultEnd "[A a -t0] $dashResultEnd" A a set dashResultEnd "[A a -t1 1] $dashResultEnd" A a set dashResultEnd "[A a -t2 1 2] $dashResultEnd" A a set dashResultEnd "[A a -t3 1 2 3] $dashResultEnd" A a set dashResultEnd "[A a -t0 -t0 -t3 1 2 3 -t0 -t1 1 -t1 1 -t0] $dashResultEnd" catch {A a t} ::errorCheck $dashResult \ "*t0*t1-1*t2-1-2*t3-1-2-3*t0*t0*t3-1-2-3*t0*t1-1*t1-1*t0" \ "Init Dash Test fails" ::errorCheck $dashResultEnd \ "::a ::a ::a ::a ::a " \ "Init Dash Test fails -- result" Class Foo -parameter {{match -exact}} Foo ff ::errorCheck [ff match] "-exact" "default with dash" } # parameter/defaults test proc ::cmd {a b} { return in-cmd-${a}-${b} } global parameterResult global initResult for {set i 0} {$i < $n} {incr i} { Class O -parameter { {a 0} {b {[cmd 3 4]}} c d {e 3} {Self [self]} } O instproc init args { global initResult set initResult ${initResult}-[self]-[self class]-[self proc]--$args next } O instproc show {} { global parameterResult set parameterResult [self] foreach v [lsort [my info vars]] { set parameterResult ${parameterResult}-${v}=<[my set ${v}]> } } Class Meta -superclass Class Meta instproc create args {next; return Meta-create} Meta C -superclass O -parameter {a {b ""} {c 1}} Class D -parameter {a {c 1}} -superclass O # create on class should not be called D instproc create args {next; return D-create} D instproc init args { global initResult append initResult -[self]-[self class]-[self proc]--$args next } D instproc test i { ::errorCheck [my set c]-[my set a] "2-0" "Wrong order of init call" } set parameterResult "" set initResult "" C c0 -show ::errorCheck $parameterResult "::c0-Self=<::c0>-b=<>-c=<1>-e=<3>" \ "C c0 parameter Test failed" if {$i == 0} { ::errorCheck $initResult "-::c0-::O-init--" \ "C c0 parameter init Test failed" } else { ::errorCheck $initResult "-::c0-::O-init--" \ "C c0 parameter init Test failed (b)" } set parameterResult "" set initResult "" set r [C c1 -c 2 -init a b c -a 1 -show] ::errorCheck $parameterResult "::c1-Self=<::c1>-a=<1>-b=<>-c=<2>-e=<3>" \ "C c1 parameter Test failed (b)" ::errorCheck $initResult "-::c1-::O-init--a b c" \ "C c1 parameter init Test failed" set parameterResult "" set initResult "" set r $r-[D d1 -c 2 -a 0 -init a b c -test $i -a 1 -show] ::errorCheck $parameterResult "::d1-Self=<::d1>-a=<1>-b=-c=<2>-e=<3>" \ "D d1 parameter Test failed" if {$i == 0} { ::errorCheck $initResult "-::d1-::D-init--a b c-::d1-::O-init--a b c" \ "D d1 parameter init Test failed" } else { ::errorCheck $initResult "-::d1-::D-init--a b c-::d1-::O-init--a b c" \ "D d1 parameter init Test failed (b)" } ::errorCheck $r "Meta-create-::d1" "User defined object creation failed" } } @ TestX mixinTest { description { Regression test object testing per-object mixins. } } TestX mixinTest -proc run {{n 10}} { global mixinResult set mixinResult "" Class Agent Agent instproc moveAgent {x y} { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] next } Agent instproc otherProc {} { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] next } Class InteractiveAgent -superclass Agent InteractiveAgent instproc moveAgent {x y} { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] next } Class InteractiveAgent2 -superclass Agent InteractiveAgent2 instproc moveAgent {x y} { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] next } Class InteractiveAgent3 -superclass Agent InteractiveAgent3 instproc moveAgent {x y} { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] next } # Addition-Classes Class MovementLog MovementLog instproc moveAgent {x y} { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] my otherProc next } MovementLog instproc otherProc {} { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] next } Class MovementTest MovementTest instproc moveAgent {x y} { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] next } InteractiveAgent i1; InteractiveAgent i2 i1 mixin MovementLog i2 mixin MovementTest InteractiveAgent2 instmixin {MovementLog MovementTest} InteractiveAgent3 instmixin MovementTest InteractiveAgent2 i3; InteractiveAgent3 i4; ::errorCheck [InteractiveAgent2 info instmixin] "::MovementLog ::MovementTest" "Mixin: info instmixin" i2 moveAgent 1 2 ::errorCheck $mixinResult \ "-::i2-moveAgent-::MovementTest-::i2-moveAgent-::InteractiveAgent-::i2-moveAgent-::Agent" \ "Mixin: 'i2 moveAgent 1 2' failed" set mixinResult "" i1 moveAgent 3 4 ::errorCheck $mixinResult \ "-::i1-moveAgent-::MovementLog-::i1-otherProc-::MovementLog-::i1-otherProc-::Agent-::i1-moveAgent-::InteractiveAgent-::i1-moveAgent-::Agent" \ "Mixin: 'i1 moveAgent 3 4' failed" set mixinResult "" i3 moveAgent 3 4 ::errorCheck $mixinResult \ "-::i3-moveAgent-::MovementLog-::i3-otherProc-::MovementLog-::i3-otherProc-::Agent-::i3-moveAgent-::MovementTest-::i3-moveAgent-::InteractiveAgent2-::i3-moveAgent-::Agent" \ "Instmixin: 'i3 moveAgent 3 4' failed" set mixinResult "" i4 moveAgent 3 4 ::errorCheck $mixinResult \ "-::i4-moveAgent-::MovementTest-::i4-moveAgent-::InteractiveAgent3-::i4-moveAgent-::Agent" \ "Instmixin: 'i4 moveAgent 3 4' failed" i4 mixin {MovementTest MovementLog} i4 proc aaa args {puts TEST} ::errorCheck [i4 info precedence] "::MovementTest ::MovementLog ::InteractiveAgent3 ::Agent ::xotcl::Object" "precedence i4" ::errorCheck [i4 procsearch moveAgent] "::MovementTest instproc moveAgent" "procsearch1" ::errorCheck [i4 procsearch aaa] "::i4 proc aaa" "procsearch2" ::errorCheck [i4 procsearch set] "::xotcl::Object instcmd set" "procsearch3" Class create A A instproc f1 {} {puts hu} A instforward f2 puts hu A instparametercmd f5 A create a0 a0 proc f3 {} {puts hu} a0 forward f4 puts hu a0 parametercmd f6 ::errorCheck [a0 procsearch f1] "::A instproc f1" procsearch-1 ::errorCheck [a0 procsearch f2] "::A instforward f2" procsearch-2 ::errorCheck [a0 procsearch f3] "::a0 proc f3" procsearch-3 ::errorCheck [a0 procsearch f4] "::a0 forward f4" procsearch-4 ::errorCheck [a0 procsearch f5] "::A instparametercmd f5" procsearch-4 ::errorCheck [a0 procsearch f6] "::a0 parametercmd f6" procsearch-6 ::errorCheck [a0 procsearch set] "::xotcl::Object instcmd set" procsearch-6 ::errorCheck [catch {a0 parametercmd f6 puts}] 1 "parametercmd with wrong args returns error" set mixinResult "" i4 moveAgent 5 6 ::errorCheck $mixinResult \ -::i4-moveAgent-::MovementTest-::i4-moveAgent-::MovementLog-::i4-otherProc-::MovementLog-::i4-otherProc-::Agent-::i4-moveAgent-::InteractiveAgent3-::i4-moveAgent-::Agent \ "Instmixin: 'i4 moveAgent 5 6' failed" Class A A instproc test {} { global mixinResult set mixinResult "test" i1 moveAgent 3 4 } A a a test ::errorCheck $mixinResult \ "test-::i1-moveAgent-::MovementLog-::i1-otherProc-::MovementLog-::i1-otherProc-::Agent-::i1-moveAgent-::InteractiveAgent-::i1-moveAgent-::Agent" \ "Mixin: 'a test' failed" i2 mixin {MovementLog MovementTest} set mixinResult "" i2 moveAgent a b ::errorCheck $mixinResult \ "-::i2-moveAgent-::MovementLog-::i2-otherProc-::MovementLog-::i2-otherProc-::Agent-::i2-moveAgent-::MovementTest-::i2-moveAgent-::InteractiveAgent-::i2-moveAgent-::Agent" \ "Mixin: 'i2 moveAgent a b' failed" ::errorCheck "[i2 info mixin]-[i1 info mixin]-[a info mixin]" \ "::MovementLog ::MovementTest-::MovementLog-" \ "Mixin: Info failed" ::errorCheck "[i2 ismixin MovementTest]-[i4 ismixin MovementTest]-[a ismixin MovementTest]-[i3 ismixin MovementTest]-[i4 ismixin MovementTest]-[i4 ismixin MovementLog]-[i3 ismixin YXZ]-[i3 ismixin InteractiveAgent]" \ "1-1-0-1-1-1-0-0" \ "'ismixin test' failed" ::errorCheck "[i2 hasclass MovementTest]-[i4 hasclass MovementTest]-[a hasclass MovementTest]-[i3 hasclass MovementTest]-[i4 hasclass MovementTest]-[i4 hasclass MovementLog]-[i3 hasclass YXZ]-[i3 hasclass InteractiveAgent]-[a hasclass A]-[i3 hasclass Agent]" \ "1-1-0-1-1-1-0-0-1-1" \ "'hasclass test' failed" set mixinResult "" i2 mixin "" i2 moveAgent a b ::errorCheck $mixinResult \ "-::i2-moveAgent-::InteractiveAgent-::i2-moveAgent-::Agent" \ "Mixin: remove failed" set mixinResult "" Class A A instproc destroy args { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] next } A instproc y args { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] next } Class B B instproc destroy args { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] next } B instproc y args { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] my mixin "" next } B instproc x args { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] my destroy } A a -mixin B a destroy A a -mixin B a x A a -mixin B a y ::errorCheck $mixinResult \ "-::a-destroy-::B-::a-destroy-::A-::a-x-::B-::a-destroy-::B-::a-destroy-::A-::a-y-::B-::a-y-::A" \ "Mixin: destroy failed" A instmixin B set mixinResult "" A a2 a2 destroy A a2 a2 x A a2 a2 y ::errorCheck $mixinResult \ "-::a2-destroy-::B-::a2-destroy-::A-::a2-x-::B-::a2-destroy-::B-::a2-destroy-::A-::a2-y-::B-::a2-y-::A" \ "Instmixin: destroy failed" # mixin Test: calls the mixins and a proc of the object set ::mixinResult "" Class A Class B A instproc a {} {set ::mixinResult ${::mixinResult}-[self]-[self class]-[self proc];next} B instproc a {} {set ::mixinResult ${::mixinResult}-[self]-[self class]-[self proc]; next} A d -mixin B d proc a {} {set ::mixinResult ${::mixinResult}-[self]-[self class]-[self proc]; next} d a ::errorCheck $::mixinResult \ "-::d-::B-a-::d--a-::d-::A-a" \ "Mixin: calling of object's proc" set mixinResult "" d mixin {} A instmixin B d a ::errorCheck $::mixinResult \ "-::d-::B-a-::d--a-::d-::A-a" \ "Instmixin: calling of object's proc" # # combining filters with mixins # set ::traceResults "" Class M1 M1 instproc test args { global traceResults lappend traceResults "[self] [self proc] [self class]" next } Class M2 M2 instproc test args { global traceResults lappend traceResults "[self] [self proc] [self class]" next } Class A A instproc test args { global traceResults lappend traceResults "[self] [self proc] [self class]" next } A instproc f1 args { global traceResults lappend traceResults "[self] [self proc] [self class]" next } A instproc f2 args { global traceResults lappend traceResults "[self] [self proc] [self class]" next } A a A instmixin {M1 M2} A instfilter {f1 f2} a test ::errorCheck $::traceResults \ "{::a f1 ::A} {::a f2 ::A} {::a test ::M1} {::a test ::M2} {::a test ::A}" \ "Combining mixins and filters" # mixin recursion test set mixinResult "" Class Computation Computation instproc compute args { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] # abstract interface for computations } Class ComputationOutput -superclass Computation Computation instproc compute args { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] return $args } Class RecFacultyMixin RecFacultyMixin instproc compute args { global mixinResult set mixinResult ${mixinResult}-[self]-[self proc]-[self class] set n [lindex $args 0] set callingClass - #puts stderr [self class]=[uplevel 1 self class]-[self callingclass] #catch {set callingClass [uplevel 1 self class]} set callingClass [self callingclass] if {$n == 0} { set result 1 } else { set f [my compute [expr {$n - 1}] x] set result [expr {$n * $f}] } if {$callingClass != [self class]} { next $result return $result } else { return $result } } ComputationOutput faculty faculty mixin RecFacultyMixin ::errorCheck [faculty compute 3] 6 \ "Mixin: faculty wrong result" ::errorCheck $mixinResult \ "-::faculty-compute-::RecFacultyMixin-::faculty-compute-::RecFacultyMixin-::faculty-compute-::RecFacultyMixin-::faculty-compute-::RecFacultyMixin-::faculty-compute-::Computation" \ "Mixin: faculty failed" set mixinResult "" ComputationOutput faculty ComputationOutput instmixin RecFacultyMixin ::errorCheck [faculty compute 3] 6 "Mixin: faculty wrong result" ::errorCheck $mixinResult \ "-::faculty-compute-::RecFacultyMixin-::faculty-compute-::RecFacultyMixin-::faculty-compute-::RecFacultyMixin-::faculty-compute-::RecFacultyMixin-::faculty-compute-::Computation" \ "Mixin: faculty failed" set ::mixinResult "" set ::calling "" Class GrObject GrObject instproc draw args { lappend ::mixinResult [list grObject [self] [self proc] [self class]] lappend ::calling [list grObject [self proc]: [self callingobject] [self callingclass] [self callingproc] [self next]] } Class Image -superclass GrObject Image instproc draw args { lappend ::mixinResult [list image [self] [self proc] [self class]] lappend ::calling [list image [self proc]: [self callingobject] [self callingclass] [self callingproc] [self next]] next } Class MenuDecorator MenuDecorator instproc draw args { lappend ::mixinResult [list m1 [self] [self proc] [self class]] lappend ::calling [list m1 [self proc]: [self callingobject] [self callingclass] [self callingproc] [self next]] next } Class ScrollBarDecorator ScrollBarDecorator instproc draw args { lappend ::mixinResult [list m2 [self] [self proc] [self class]] lappend ::calling [list m2 [self proc]: [self callingobject] [self callingclass] [self callingproc] [self next]] next } Image mainImage -mixin {MenuDecorator ScrollBarDecorator} Image zoom -mixin {ScrollBarDecorator} Object instproc f args { if {[self calledproc] ne "filter"} { lappend ::mixinResult [list filter [self] [self proc] [self class]] lappend ::calling [list filter [self proc]: [self callingobject] [self callingclass] [self callingproc] [self calledproc] [self next]] } return [next] } Object instfilter f mainImage draw zoom draw Object instfilter "" ::errorCheck $::calling \ "{filter f: ::mixinTest {} run draw {::MenuDecorator instproc draw}} {m1 draw: ::mixinTest {} run {::ScrollBarDecorator instproc draw}} {m2 draw: ::mixinTest {} run {::Image instproc draw}} {image draw: ::mixinTest {} run {::GrObject instproc draw}} {grObject draw: ::mixinTest {} run {}} {filter f: ::mixinTest {} run draw {::ScrollBarDecorator instproc draw}} {m2 draw: ::mixinTest {} run {::Image instproc draw}} {image draw: ::mixinTest {} run {::GrObject instproc draw}} {grObject draw: ::mixinTest {} run {}} {filter f: ::mixinTest {} run instfilter {::xotcl::Class instforward instfilter}}" \ "Mixin: Calling-Obj/Cl/Proc failed" # ::errorCheck $::calling \ # "{filter f: ::mixinTest {} run draw {::MenuDecorator method draw}} {m1 draw: ::mixinTest {} run {::ScrollBarDecorator method draw}} {m2 draw: ::mixinTest {} run {::Image method draw}} {image draw: ::mixinTest {} run {::GrObject method draw}} {grObject draw: ::mixinTest {} run {}} {filter f: ::mixinTest {} run draw {::ScrollBarDecorator method draw}} {m2 draw: ::mixinTest {} run {::Image method draw}} {image draw: ::mixinTest {} run {::GrObject method draw}} {grObject draw: ::mixinTest {} run {}} {filter f: ::mixinTest {} run instfilter {::xotcl::Class forward instfilter}}" \ # "Mixin: Calling-Obj/Cl/Proc failed" ::errorCheck $::mixinResult \ "{filter ::mainImage f ::xotcl::Object} {m1 ::mainImage draw ::MenuDecorator} {m2 ::mainImage draw ::ScrollBarDecorator} {image ::mainImage draw ::Image} {grObject ::mainImage draw ::GrObject} {filter ::zoom f ::xotcl::Object} {m2 ::zoom draw ::ScrollBarDecorator} {image ::zoom draw ::Image} {grObject ::zoom draw ::GrObject} {filter ::xotcl::Object f ::xotcl::Object}" \ "Mixin: Filter failed" set ::mixinResult "" set ::calling "" Class InfoTrace2 InfoTrace2 instproc infoTraceFilter2 args { lappend ::calling \ self [self] \ "self proc" [self proc] \ "self class" [self class] \ "self calledproc" [self calledproc] \ "self callingproc" [self callingproc] \ "self callingobject" [self callingobject] \ "self callingclass" [self callingclass] \ "self filterreg" [self filterreg] \ "self next" [self next] next } Class CallingObjectsClass CallingObjectsClass create callingObject Class FilterRegClass -superclass InfoTrace2 Class FilteredObjectsClass -superclass FilterRegClass FilteredObjectsClass filteredObject CallingObjectsClass instproc callingProc args { filteredObject set someVar 0 } FilterRegClass instfilter infoTraceFilter2 callingObject callingProc # ::errorCheck $::calling \ # {self ::filteredObject {self proc} infoTraceFilter2 {self class} ::InfoTrace2 {self calledproc} set {self callingproc} callingProc {self callingobject} ::callingObject {self callingclass} ::CallingObjectsClass {self filterreg} {::FilterRegClass instfilter infoTraceFilter2} {self next} {::xotcl::Object instcmd set}} \ # "call stack info" ::errorCheck $::calling \ {self ::filteredObject {self proc} infoTraceFilter2 {self class} ::InfoTrace2 {self calledproc} set {self callingproc} callingProc {self callingobject} ::callingObject {self callingclass} ::CallingObjectsClass {self filterreg} {::FilterRegClass filter infoTraceFilter2} {self next} {::xotcl::Object instcmd set}} \ "call stack info" Class M1; Class M2; Class M3; Class M4 Class A; Class B -superclass A; B b A instmixin {M1 M2} B instmixin {M3 M1 M1 M4} b mixin {M1 M1 M4} ::errorCheck [b info mixin] "::M1 ::M4" "Mixin Info: -no dups1" ::errorCheck [b info precedence] "::M1 ::M4 ::M3 ::M2 ::B ::A ::xotcl::Object" "Mixin Info: -no dups2" ::errorCheck [b info mixin -order] "::M1 ::M4 ::M3 ::M2" "Mixin Info: -order option1" ::errorCheck [B info instmixin]-[b info mixin] "::M3 ::M1 ::M4-::M1 ::M4" "Mixin Info: no duplicates" B instmixin {} ::errorCheck [b info mixin -order] "::M1 ::M4 ::M2" "Mixin Info: -order option2" set ::r "" Class X11 -instproc test {args} { lappend ::r [self class] next } Class X12 -instproc test {args} { lappend ::r [self class] next } Class X -instmixin {X11 X12} -instproc test {args} { lappend ::r [self class] next } Class Y -instmixin X Y create y -test X create x -test ::errorCheck $::r [list ::X11 ::X12 ::X ::X11 ::X12 ::X] \ {transitive mixin} unset ::r # test for MixinRemoveFromMixinStack, MixinRemoveFromCmdPtr, # MixinRemoveOnObjFromCmdPtr Class A A instproc x {} {B destroy; next} Class B B instproc x {} {next} Class C C instproc x {} {next} Object o -mixin {A B C} o proc x {} {return x} ::errorCheck [o x] {x} {mixin destroy on stack} o destroy # testing transitive mixins; should be in both cases the same Class IM Class M Object o -mixin M M instmixin IM ::errorCheck [o info precedence] {::IM ::M ::xotcl::Object} \ {trans. mixin precedence 1} Object o -mixin M ::errorCheck [o info precedence] {::IM ::M ::xotcl::Object} \ {trans. mixin precedence 2} o destroy } @ TestX procsearchTest { description { Regression test for procsearch } } TestX procsearchTest -proc run {{n 10}} { Class M -instproc foo args {puts m;next} Object o -mixin M -proc foo args {puts o;next} ::errorCheck [o procsearch foo] "::M instproc foo" "mixin before proc in procsearch" M destroy o destroy Class CC -instproc foo args {puts CC;next} CC create c -proc foo args {puts c;next} ::errorCheck [c procsearch foo] "::c proc foo" "proc before instproc in procsearch" CC destroy c destroy } @ TestX mixinInheritanceTest { description { Regression test object testing per-object mixin inheritance. } } TestX mixinInheritanceTest -proc run {{n 10}} { for {set i 0} {$i < $n} {incr i} { global mixinResult set mixinResult "" Class A Class B Class C -superclass {A B} Class GeneralMixin Class RefinedMixin1 -superclass GeneralMixin Class RefinedMixin2 -superclass GeneralMixin Class AppMixin1 -superclass {RefinedMixin1 RefinedMixin2} Class AppMixin2 -superclass {RefinedMixin2 RefinedMixin1} Class AppMixin3 -superclass {RefinedMixin1} A instproc aProc args { global mixinResult; ::set mixinResult "$mixinResult [self class]" return $args } B instproc aProc args { global mixinResult; ::set mixinResult "$mixinResult [self class]" return $args } C instproc aProc args { global mixinResult; ::set mixinResult "$mixinResult [self class]" return [next "$args [self class]"] } GeneralMixin instproc aProc args { global mixinResult; ::set mixinResult "$mixinResult [self class]" return [next "$args [self class]"] } RefinedMixin1 instproc aProc args { global mixinResult; ::set mixinResult "$mixinResult [self class]" return [next "$args [self class]"] } RefinedMixin2 instproc aProc args { global mixinResult; ::set mixinResult "$mixinResult [self class]" return [next "$args [self class]"] } AppMixin1 instproc aProc args { global mixinResult; ::set mixinResult "$mixinResult [self class]" return [next "$args [self class]"] } AppMixin1 mixinInstance set r [mixinInstance aProc ARGS1 ARGS2] ::errorCheck $mixinResult \ " ::AppMixin1 ::RefinedMixin1 ::RefinedMixin2 ::GeneralMixin" \ "Mixin inheritance: mixinInstance aProc" set mixinResult "" AppMixin3 mixinInstance2 set r [mixinInstance2 aProc ARGS1 ARGS2] ::errorCheck $mixinResult \ " ::RefinedMixin1 ::GeneralMixin" \ "Mixin inheritance: mixinInstance2 aProc" set mixinResult "" A a a mixin AppMixin1 set r [a aProc ARGS1 ARGS2] ::errorCheck $mixinResult \ " ::AppMixin1 ::RefinedMixin1 ::RefinedMixin2 ::GeneralMixin ::A" \ "Mixin inheritance: a aProc" ::errorCheck $r \ "{{{{ARGS1 ARGS2 ::AppMixin1} ::RefinedMixin1} ::RefinedMixin2} ::GeneralMixin}" \ "Mixin inheritance result: a aProc" A a A instmixin AppMixin1 set mixinResult "" set r [a aProc ARGS1 ARGS2] ::errorCheck $mixinResult \ " ::AppMixin1 ::RefinedMixin1 ::RefinedMixin2 ::GeneralMixin ::A" \ "Instmixin inheritance: a aProc" ::errorCheck $r \ "{{{{ARGS1 ARGS2 ::AppMixin1} ::RefinedMixin1} ::RefinedMixin2} ::GeneralMixin}" \ "Instmixin inheritance: a aProc" set mixinResult "" C c c mixin {AppMixin3 AppMixin2} ::errorCheck [c info precedence] \ "::AppMixin3 ::AppMixin2 ::AppMixin1 ::RefinedMixin1 ::RefinedMixin2 ::GeneralMixin ::C ::A ::B ::xotcl::Object" \ "mixin precedence" set r [c aProc ARGS1 ARGS2] ::errorCheck $mixinResult \ " ::AppMixin1 ::RefinedMixin1 ::RefinedMixin2 ::GeneralMixin ::C ::A" \ "Mixin/Instmixin inheritance: c aProc" set mixinResult "" A instmixin {} set r [c aProc ARGS1 ARGS2] ::errorCheck $mixinResult \ " ::RefinedMixin2 ::RefinedMixin1 ::GeneralMixin ::C ::A" \ "Mixin/Instmixin inheritance: c aProc" GeneralMixin instproc set args { global mixinResult; ::set mixinResult "$mixinResult [self class]" return [next] } RefinedMixin1 instproc set args { global mixinResult; ::set mixinResult "$mixinResult [self class]" return [next] } AppMixin1 instproc set args { global mixinResult; ::set mixinResult "$mixinResult [self class]" return [next] } global setFilterResult set setFilterResult "" Object instproc setFilter args { global setFilterResult ::append setFilterResult \ -[self]-[self calledproc]-[self calledclass] next } Object instfilter setFilter set mixinResult "" set r [c set setVar 111] ::errorCheck $mixinResult \ " ::RefinedMixin1 ::GeneralMixin" \ "Mixin inheritance: c set" # UNKNOWN PROBLEM 2 # ::errorCheck [c setsetVar] 111 "Mixin inheritance: c set - value" ::errorCheck [c set setVar] 111 "Mixin inheritance: c set - value" set mixinResult "" mixinInstance set setVar 222 ::errorCheck $mixinResult \ " ::AppMixin1 ::RefinedMixin1 ::GeneralMixin" \ "Mixin inheritance: mixinInstance set" ::errorCheck [mixinInstance set setVar] 222\ "Mixin inheritance: mixinInstance set - value" ::errorCheck $setFilterResult \ "-::c-set-::xotcl::Object-::c-set-::xotcl::Object-::mixinInstance-set-::AppMixin1-::mixinInstance-set-::AppMixin1" \ "Mixin inheritance: Wrong classes in mixin set test" Object instfilter "" } # Mixin init test global initResult set initResult "" Class A A instproc init args { my mixin B global initResult append initResult [self class]- next } Class C C instproc init args { global initResult append initResult [self class]- next } Class B -superclass C B instproc init args { global initResult append initResult [self class]- next } Class D D instproc init args { global initResult append initResult [self class]- next } A a ::errorCheck $initResult "::A-" "Mixin init 1 failed" set initResult "" # in A mixin changes to B - before D's constructor must # be called A b -mixin D ::errorCheck $initResult "::D-::A-" "Mixin init 2 failed" Class Mix Mix instproc init args { global initResult append initResult [self class]- next } Class Mix1 Mix1 instproc init args { global initResult append initResult [self class]- next } Class Mix2 Mix2 instproc init args { global initResult append initResult [self class]- next } Class A A instproc init args { global initResult append initResult [self class]- next } Class B B instproc init args { my mixin {Mix Mix1} global initResult append initResult [self class]- next } set initResult "" A a a mixin {Mix Mix1} ::errorCheck $initResult ::A- "Mixin init 3 failed" set initResult "" B b ::errorCheck $initResult ::B- "Mixin init 4 failed" set initResult "" B mixin add Mix2 ::errorCheck $initResult "" "Mixin init 5 failed" set initResult "" A mixin {}; A mixin {Mix Mix1} ::errorCheck $initResult "" "Mixin init 6 failed" set initResult "" A a -mixin {Mix} ::errorCheck $initResult "::Mix-::A-" "Mixin init 7 failed" Class Strategy Strategy instproc init args { global initResult append initResult [self class]- next } Class A A instproc strategy {n} { set a [my info mixin] my mixin [concat $n $a] } A instproc init args { global initResult append initResult [self class]- next } Class Mix1 Mix1 instproc init args { global initResult append initResult [self class]- my strategy Strategy next } set initResult "" A a -mixin Mix1 ::errorCheck $initResult ::Mix1-::A- "Mixin init 8 failed" set initResult "" Class X X instproc init args { append ::initResult " [self]: [self class]->[self proc]" next } Class Y -superclass X Y instproc init args { append ::initResult " [self]: [self class]->[self proc]" next } Class U -superclass X U instproc init args { append ::initResult " [self]: [self class]->[self proc]" next } Class V V instproc init args { append ::initResult " [self]: [self class]->[self proc]" next } Class A A instproc init args { append ::initResult " [self]: [self class]->[self proc]" next } Class B -superclass A B instproc init args { append ::initResult " [self]: [self class]->[self proc]" next } A a a mixin X B b b mixin Y A a2 -mixin Y B b2 -mixin X A a3 -mixin {U V} B b3 b3 mixin {U V} A a3 A instmixin X A instmixin {} B instmixin Y B b3 b3 mixin Y ::errorCheck $initResult \ " ::a: ::A->init ::b: ::B->init ::b: ::A->init ::a2: ::Y->init ::a2: ::X->init ::a2: ::A->init ::b2: ::X->init ::b2: ::B->init ::b2: ::A->init ::a3: ::U->init ::a3: ::X->init ::a3: ::V->init ::a3: ::A->init ::b3: ::B->init ::b3: ::A->init ::a3: ::A->init ::b3: ::Y->init ::b3: ::X->init ::b3: ::B->init ::b3: ::A->init" \ "Mixin init 9 failed" } @ TestX copymove { description {Regression test for copy/move methods} } TestX copymove -proc run {{n 10}} { # Composite Class Composite -superclass Class Composite instproc addop {op} { my instvar ops set ops($op) $op } Composite instproc compositeFilter args { set m [self calledproc] set c [lindex [self filterreg] 0] set r [next] if {[$c exists ops($m)]} { foreach child [my info children] { eval [self]::$child $m $args } } return $r } Composite AbstractNode AbstractNode abstract instproc iterate v AbstractNode addop iterate for {set i 0} {$i < $n} {incr i} { # # class copy # Class X Class X::Y Class X::Y::Z -parameter { {param1 1} {param2 2} } #X::Y::Z metadata add {Version Author Nothing} #X::Y::Z metadata Version {0.0.9} #X::Y::Z metadata Author {Uwe} X::Y::Z instproc defaultValueIP {{a defA} {b defB} v} { return } X::Y::Z proc defaultValueP {{c defC} {d defD} v} { return } X::Y::Z instinvar {{7 > 6} { #a comment } } X::Y::Z instproc assProc {} {puts x} {{5 > 4} { #pre }} {{5 > 4} { #post } } X::Y::Z check {pre post instinvar} foreach C {X X::Y X::Y::Z} { $C instproc q {a b c} { return [self]--[self class]--[self proc]--[next]-- } } X::Y::Z instforward a b X::Y::Z forward c d ::errorCheck [X::Y::Z info instforward -definition a] "b" "define instforward" ::errorCheck [X::Y::Z info forward -definition c] "d" "define forward" X::Y::Z z X::Y::Z copy V V v ::errorCheck "[z q 1 2 3]--[X::Y::Z info class]--[X::Y::Z info classparent]" \ "::z--::X::Y::Z--q------::xotcl::Class--::X::Y"\ "classparent class copy z" ::errorCheck "[z q 1 2 3]--[X::Y::Z info class]--[X::Y::Z info parent]" \ "::z--::X::Y::Z--q------::xotcl::Class--::X::Y"\ "parent class copy z" ::errorCheck "[v q 1 2 3]--[V info class]--[V info classparent]" "::v--::V--q------::xotcl::Class--::"\ "classparent class copy v" ::errorCheck "[v q 1 2 3]--[V info class]--[V info parent]" "::v--::V--q------::xotcl::Class--::"\ "parent class copy v" ::errorCheck "[::cutSpaces [V info parameter]--[v set param1]--[v set param2]]" \ " {param1 1} {param2 2} --1--2" \ "parameter test" if {$::nsf::config(assertions)} { ::errorCheck "[::cutSpaces [V info instinvar]--[V info instpre assProc]--[V info instpost assProc]]"\ "{7 > 6} { #a comment }--{5 > 4} { #pre }--{5 > 4} { #post }"\ "Copy Class Assertions" } ::errorCheck [V info instforward -definition a] "b" "copied instforward" ::errorCheck [V info forward -definition c] "d" "copied forward" #::errorCheck "[V info metadata]--[V metadata Author]--[V metadata Version]--[V metadata Nothing]"\ "Version Author Nothing--Uwe--0.0.9--"\ "Copy Metadata" set df1 [V info default defaultValueP v dfv1] set df2 [V info default defaultValueP c dfv2] set df3 [V info instdefault defaultValueIP v dfv3] set df4 [V info instdefault defaultValueIP a dfv4] ::errorCheck "$df1 $dfv1 $df2 $dfv2 $df3 $dfv3 $df4 $dfv4"\ "0 1 defC 0 1 defA"\ "Copy Default Values" # class hierarchy copy Class O X copy O::X ::errorCheck "[::xotcl::is object O::X]" 1 "O::X is an object" ::errorCheck "[::xotcl::is object O::X::Y]" 1 "O::X::Y is an object" ::errorCheck "[::xotcl::is object O::X::Y::Z]" 1 "O::X::Y::Z is an object" O::X x1; O::X::Y y1; O::X::Y::Z z1 ::errorCheck "[x1 q 1 2 3]--[y1 q 1 2 3]--[z1 q 1 2 3]" \ "::x1--::O::X--q------::y1--::O::X::Y--q------::z1--::O::X::Y::Z--q----"\ "class hierarchy copy" # # object copy # X x -set var1 12 -requireNamespace proc ::x::tclProc args {return tclProc} x proc q {a b c} {return [self]--[self class]--[self proc]--[next]--} x copy y ::errorCheck "[::y::tclProc]--[x q 1 2 3]--[y q 1 2 3]" \ "tclProc--::x----q--::x--::X--q--------::y----q--::y--::X--q------"\ "object copy" # object hierarchy copy x copy x::a x copy x::a::z ::errorCheck "[::x::a::tclProc]--[::x::a::z::a::tclProc]" \ "tclProc--tclProc"\ "object hierarchy copy" Class O O x x invar {{7 > 5} { #a comment }} x proc assProc {} {return} {{5 > 3} { #pre }} {{5 > 4} {#post }} x set var1 12 x proc p1 {} {return [self]-p1} x copy y ::errorCheck "[x p1]--[x set var1]--[::x info class]" "::x-p1--12--::O"\ "Simple Copy - Origin" ::errorCheck "[y p1]--[y set var1]--[::y info class]" "::y-p1--12--::O"\ "Simple Copy - Duplicate" if {$::nsf::config(assertions)} { ::errorCheck "[::cutSpaces [y info invar]--[y info pre assProc]--[y info post assProc]]"\ "{7 > 5} { #a comment }--{5 > 3} { #pre }--{5 > 4} {#post }"\ "Copy Obj Assertions" } # # move test # V destroy X::Y::Z move V V v ::errorCheck "[v q 1 2 3]--[V info class]--[V info classparent]" "::v--::V--q------::xotcl::Class--::"\ "classparent class move v" ::errorCheck "[v q 1 2 3]--[V info class]--[V info parent]" "::v--::V--q------::xotcl::Class--::"\ "parent class move v" ::errorCheck "[::cutSpaces [V info parameter]--[v set param1]--[v set param2]]" \ " {param1 1} {param2 2} --1--2" \ "parameter move test" if {$::nsf::config(assertions)} { ::errorCheck "[::cutSpaces [V info instinvar]--[V info instpre assProc]--[V info instpost assProc]]"\ "{7 > 6} { #a comment }--{5 > 4} { #pre }--{5 > 4} { #post }"\ "Move Class Assertions" } #::errorCheck "[V info metadata]--[V metadata Author]--[V metadata Version]--[V metadata Nothing]"\ "Version Author Nothing--Uwe--0.0.9--"\ "Move Metadata" set df1 [V info default defaultValueP v dfv1] set df2 [V info default defaultValueP c dfv2] set df3 [V info instdefault defaultValueIP v dfv3] set df4 [V info instdefault defaultValueIP a dfv4] ::errorCheck "$df1 $dfv1 $df2 $dfv2 $df3 $dfv3 $df4 $dfv4"\ "0 1 defC 0 1 defA"\ "Move Default Values" ::errorCheck [::info commands X::Y::Z] "" "Moved command still exists" # # copy with filters test # foreach filters {{} compositeFilter} { Composite instfilter $filters AbstractNode instfilter $filters Object commands Class Commands -superclass AbstractNode Class Command -superclass Commands Command instproc init args { my instvar label set label [self] next } Command instproc setlabel {{arg ""}} { my instvar label if {$arg eq ""} { set label } else { set label $arg } } Command instproc setproc {value} { my instvar src set src $value } # prototypes Command commands::cellcmd commands::cellcmd setlabel cell commands::cellcmd setproc {return "coucou" } commands::cellcmd proc x args {return xxx} commands::cellcmd copy toto ::errorCheck [::toto info class] ::Command "Copy with Filter: info class" ::errorCheck [toto set label] cell "Copy with Filter: set var" ::errorCheck [toto x] xxx "Copy with Filter: call proc" ::errorCheck [commands::cellcmd set label] cell \ "Copy with Filter: set var" } Class A Class V Class Z Class B -superclass A Class B1 -superclass {V A Z} A move X ::errorCheck [B info superclass]-[B1 info superclass]-[X info subclass] \ "::X-::V ::X ::Z-::B ::B1" \ "Move of subclass relationship" # # test nonpos args # Class X X proc do0 {arg1 arg2} {puts "$arg1 $arg2"} X proc do1 {-arg1 -arg2} {puts "$arg1 $arg2"} X proc do2 {-arg1 arg2} {puts "$arg1 $arg2"} X proc do3 {arg1 {arg2 d1}} {puts "$arg1 $arg2"} X proc do4 {-arg1 {-arg2 d2}} {puts "$arg1 $arg2"} X proc do5 {{-arg1 d3} {arg2 d4}} {puts "$arg1 $arg2"} X instproc do6 {{-arg1 d3} {arg2 d4}} {puts "$arg1 $arg2"} X copy Y ::errorCheck [lsort [X info procs]] "do0 do1 do2 do3 do4 do5" "check procs to be copied" ::errorCheck [lsort [Y info procs]] "do0 do1 do2 do3 do4 do5" "check copied procs" ::errorCheck [lsort [X info instprocs]] "do6" "check instprocs to be copied" ::errorCheck [lsort [Y info instprocs]] "do6" "check copied instprocs" foreach m [lsort [X info procs]] { foreach info {args nonposargs} { set x [X info $info $m] set y [Y info $info $m] ::errorCheck $x $y "copy nonposargs: $x ne $y" } foreach a [X info args $m] { set vx ""; set vy "" set dx [X info default $m $a vx] set dy [Y info default $m $a vy] ::errorCheck $dx $dy "copy nonposargs: hasdefault $m $a: (source) $dx ne (copy) $dy" if {[info exists dx] && [info exists dy]} { ::errorCheck $vx $vy "copy nonposargs: hasdefault value $vx ne $vy" } } } foreach m [lsort [X info instprocs]] { foreach info {instargs instnonposargs} { set x [X info $info $m] set y [Y info $info $m] ::errorCheck $x $y "copy inst nonposargs: $x ne $y" } foreach a [X info instargs $m] { set vx ""; set vy "" set dx [X info instdefault $m $a vx] set dy [Y info instdefault $m $a vy] ::errorCheck $dx $dy "copy inst nonposargs: hasdefault $dx ne $dy" if {[info exists dx] && [info exists dy]} { ::errorCheck $vx $vy "copy inst nonposargs: hasdefault value $vx ne $vy" } } } Object X X proc do0 {arg1 arg2} {puts "$arg1 $arg2"} X proc do1 {-arg1 -arg2} {puts "$arg1 $arg2"} X proc do2 {-arg1 arg2} {puts "$arg1 $arg2"} X proc do3 {arg1 {arg2 d1}} {puts "$arg1 $arg2"} X proc do4 {-arg1 {-arg2 d2}} {puts "$arg1 $arg2"} X proc do5 {{-arg1 d3} {arg2 d4}} {puts "$arg1 $arg2"} X copy Y foreach m [lsort [X info procs]] { foreach info {args nonposargs} { set x [X info $info $m] set y [Y info $info $m] ::errorCheck $x $y "copy nonposargs: $x ne $y" } foreach a [X info args $m] { set vx ""; set vy "" set dx [X info default $m $a vx] set dy [Y info default $m $a vy] ::errorCheck $dx $dy "copy nonposargs: hasdefault $dx ne $dy" if {[info exists dx] && [info exists dy]} { ::errorCheck $vx $vy "copy nonposargs: hasdefault value $vx ne $vy" } } } } } @ TestX recreation { description { Regression test for object recreation/cleanup. } } TestX recreation -proc run {{n 10}} { for {set i 0} {$i < $n} {incr i} { set ::recreateResult "" Class R R instproc recreate args { global recreateResult append recreateResult "*recreate [self] $args* " set r [next] append recreateResult "*recreate [self] <[lindex $args 0]> $r * " return $r } Object instmixin R catch { C destroy c1 destroy } Class C -parameter {a b} C instproc cProc {} {return cProc} C set r 4 C set v 5 C c1 -a 1 c1 proc x {} {return p} c1 set x 3 C c1 -b 2 append ::recreateResult "+[c1 info vars]," append ::recreateResult "[c1 info procs] +" Class C C set w 3 append ::recreateResult "+[C info vars]," append ::recreateResult "[C info instprocs] +" if {$i > 0} { errorCheck [set ::recreateResult] \ "*recreate ::xotcl::Class ::R* *recreate ::xotcl::Class <::R> ::R * *recreate ::C ::c1 -b 2* *recreate ::C <::c1> ::c1 * +b, +*recreate ::xotcl::Class ::C* *recreate ::xotcl::Class <::C> ::C * +w __default_superclass __default_metaclass, +" \ "Var/proc recreate delete failed (n)" } else { errorCheck [set ::recreateResult] \ "*recreate ::C ::c1 -b 2* *recreate ::C <::c1> ::c1 * +b, +*recreate ::xotcl::Class ::C* *recreate ::xotcl::Class <::C> ::C * +w __default_superclass __default_metaclass, +" \ "Var/proc recreate delete failed (0)" } global recreateMixinResult global recreateFilterResult set recreateMixinResult "" set recreateFilterResult "" Class RecreateObserve foreach ip {create destroy dealloc init configure recreate cleanup alloc class} { RecreateObserve instproc $ip args { append ::recreateMixinResult " [self]+[self class]->[self proc]" next } } Class Recreated Recreated instproc recreationFilter args { append ::recreateFilterResult " [self]+[self calledclass]->[self calledproc]" next } Recreated instfilter recreationFilter Recreated mixin RecreateObserve Recreated instmixin RecreateObserve Recreated recreateObj Recreated recreateObj recreateObj destroy errorCheck [set ::recreateFilterResult] \ " ::recreateObj+::xotcl::Object->configure ::recreateObj+::xotcl::Object->__object_configureparameter ::recreateObj+::xotcl::Object->init ::recreateObj+::xotcl::Object->cleanup ::recreateObj+::xotcl::Object->configure ::recreateObj+::xotcl::Object->init ::recreateObj+::xotcl::Object->destroy" \ "recreateObj - recreateFilterResult" if {$i == 0} { errorCheck [set ::recreateMixinResult] \ " ::Recreated+::RecreateObserve->create ::Recreated+::RecreateObserve->alloc ::recreateObj+::RecreateObserve->configure ::recreateObj+::RecreateObserve->init ::Recreated+::RecreateObserve->create ::Recreated+::RecreateObserve->recreate ::recreateObj+::RecreateObserve->cleanup ::recreateObj+::RecreateObserve->configure ::recreateObj+::RecreateObserve->init ::recreateObj+::RecreateObserve->destroy ::Recreated+::RecreateObserve->dealloc" \ "recreateObj - recreateMixinResult (0)" } else { errorCheck [set ::recreateMixinResult] \ " ::Recreated+::RecreateObserve->cleanup ::Recreated+::RecreateObserve->create ::Recreated+::RecreateObserve->alloc ::recreateObj+::RecreateObserve->configure ::recreateObj+::RecreateObserve->init ::Recreated+::RecreateObserve->create ::Recreated+::RecreateObserve->recreate ::recreateObj+::RecreateObserve->cleanup ::recreateObj+::RecreateObserve->configure ::recreateObj+::RecreateObserve->init ::recreateObj+::RecreateObserve->destroy ::Recreated+::RecreateObserve->dealloc" \ "recreateObj - recreateMixinResult (n)" } } set ::cleanupResult "" catch {a destroy} catch {A destroy} catch {X destroy} catch {META destroy} nsf::__db_run_assertions Class A A proc dealloc args {append ::cleanupResult " [self]+[self class]->[self proc]"; next} A proc recreate args {append ::cleanupResult " [self]+[self class]->[self proc]"; next} A instproc destroy args {append ::cleanupResult " [self]+[self class]->[self proc]"; next} A instproc cleanup args {append ::cleanupResult " [self]+[self class]->[self proc]"; next} A a A a::b errorCheck [set ::cleanupResult] "" "Cleanup Create Failed" A a errorCheck [a info children] "" "Cleanup Object Children Destroy Failed" A a::b errorCheck [set ::cleanupResult] \ " ::A+->recreate ::a+::A->cleanup ::a::b+::A->destroy ::A+->dealloc" \ "Cleanup a/a::b Failed (n)" a destroy set ::cleanupResult "" A instproc cleanup args {append ::cleanupResult " [self]+[self class]->[self proc]"} A a A a::b errorCheck [set ::cleanupResult] "" "Cleanup Redefine Create Failed" A a errorCheck [a info children] ::a::b \ "Cleanup Redefine Object Children Survive Failed" A a::b errorCheck [set ::cleanupResult] \ " ::A+->recreate ::a+::A->cleanup ::A+->recreate ::a::b+::A->cleanup" \ "Cleanup Redefine a/a::b Failed" a destroy set ::cleanupResult "" nsf::__db_run_assertions Class META -superclass Class META proc dealloc args {append ::cleanupResult " [self]+[self class]->[self proc]"; next} META proc recreate args {append ::cleanupResult " [self]+[self class]->[self proc]"; next} META instproc destroy args {append ::cleanupResult " [self]+[self class]->[self proc]"; next} META instproc cleanup args {append ::cleanupResult " [self]+[self class]->[self proc]"; next} META X META X::Y errorCheck [set ::cleanupResult] "" "Class Cleanup Create Failed" META X errorCheck [X info classchildren] "" "classchildren Class Cleanup Class Children Destroy Failed" errorCheck [X info children] "" "children Class Cleanup Class Children Destroy Failed" META X::Y errorCheck [set ::cleanupResult] \ " ::META+->recreate ::X+::META->cleanup ::X::Y+::META->destroy ::META+->dealloc" \ "Class Cleanup X/X::Y Failed" X destroy set ::cleanupResult "" META instproc cleanup args {append ::cleanupResult " [self]+[self class]->[self proc]"} META X META X::Y errorCheck [set ::cleanupResult] "" "Class Cleanup Redefine Create Failed" META X errorCheck [X info classchildren] ::X::Y \ "classchildren Class Cleanup Redefine Class Children Survive Failed" errorCheck [X info children] ::X::Y \ "children Class Cleanup Redefine Class Children Survive Failed" META X::Y errorCheck [set ::cleanupResult] \ " ::META+->recreate ::X+::META->cleanup ::META+->recreate ::X::Y+::META->cleanup" \ "Class Cleanup Redefine X/X::Y Failed" X destroy A destroy META destroy unset ::cleanupResult Object instmixin "" # upgrading/downgrading Class B Class C -superclass B C c1 Object o1 -mixin B Object o2 -mixin C ::errorCheck [B info class] "::xotcl::Class" "up/down before 0" ::errorCheck [c1 istype B] 1 "up/down before 1" ::errorCheck [C info superclass] ::B "up/down before 2" ::errorCheck [B info subclass] ::C "up/down before 3" ::errorCheck [o1 info mixin] ::B "up/down before 4" ::errorCheck [o2 info mixin] ::C "up/down before 5" ::errorCheck [B info mixinof] ::o1 "up/down before 6" ::errorCheck [C info mixinof] ::o2 "up/down before 7" ::errorCheck [c1 info precedence] "::C ::B ::xotcl::Object" "up/down before 8" ::errorCheck [o1 info precedence] "::B ::xotcl::Object" "up/down before 9" ::errorCheck [o2 info precedence] "::C ::B ::xotcl::Object" "up/down before 10" ::errorCheck [catch {B class Object}] 1 "don't allow downgrading" Object B ::errorCheck [B info class] "::xotcl::Object" "up/down after 0" ::errorCheck [c1 istype B] 0 "up/down after 1" ::errorCheck [C info superclass] ::xotcl::Object "up/down after 2" ::errorCheck [catch {B info subclass}] 1 "up/down after 3" ::errorCheck [o1 info mixin] "" "up/down after 4" ::errorCheck [o2 info mixin] ::C "up/down after 5" ::errorCheck [catch {B info mixinof}] 1 "up/down after 6" ::errorCheck [C info mixinof] ::o2 "up/down after 7" ::errorCheck [c1 info precedence] "::C ::xotcl::Object" "up/down after 8" ::errorCheck [o1 info precedence] "::xotcl::Object" "up/down after 9" ::errorCheck [o2 info precedence] "::C ::xotcl::Object" "up/down after 10" ::errorCheck [B info class] "::xotcl::Object" "up/down after 0x" B class Object ::errorCheck [catch {B class Object}] 0 "don't complain when same level" ::errorCheck [catch {B class Class}] 1 "don't allow upgrading" } @ TestX smallScripts { description { Regression test object testing arbitrary features. } } TestX smallScripts proc ::up1 {} { return [uplevel 1 self] } proc ::up3 {} { return [uplevel 3 self] } proc ::up2 {} { return [up3] } smallScripts proc run {{n 20}} { catch {Object o; o r} errMsg ::errorCheck $errMsg "::o: unable to dispatch method 'r'" "Unknown Test" # uplevel test for {set i 0} {$i < $n} {incr i} { Object o o proc u2 {} {return [::up2]} o proc u1 {} {return [::up1]} Class SM SM instproc init args { ::errorCheck [o u1] "::o" "FAILED - UpLevel Test 1" ::errorCheck [o u2] "::s" "FAILED - UpLevel Test 2" } SM s } for {set i 1} {$i < $n} {incr i} { Class A A a set oname1 [Object autoname ooo] set oname2 [Object autoname -instance OOO] A autoname -reset AAA set names [A autoname AAA] a autoname -reset aaa lappend names [a autoname aaa] lappend names [a autoname aaa] ::errorCheck $names "AAA1 aaa1 aaa2" "Autoname creation" ::errorCheck $oname1 "ooo$i" "Autoname Object 1" ::errorCheck $oname2 "oOO$i" "Autoname Object 2" ::errorCheck [xotcl::Object set __autonames(ooo)] $i \ "Autoname Object Count" } Class P; P p P instproc x {} { my instvar "x(1) t" return $t } p set x(1) rrr ::errorCheck [p x] rrr "Array member alias, no ns" Object o o proc x {} { my instvar "x(1) t" return $t } o set x(1) rrr ::errorCheck [o x] rrr "Array member alias, with ns" Object o o proc x args {puts r} ::errorCheck [o info body x] "puts r" "Info Body" #::errorCheck [info body o::x] "puts r" "Info Body"; #don't do this Object o o proc a {} { my lappend table(i) xxx } ::errorCheck [o a]-[o set table(i)] "xxx-xxx" "Array instvar create" Class A A instproc myProc args {} Class Mix1 Mix1 instproc myProcMix1 args {} Class Mix2 Mix2 instproc myProcMix2 args {} Class B -superclass A -instmixin Mix1 B instproc myProc2 args {} B b -mixin Mix2 b proc objproc args {} ::errorCheck [b info procs] objproc "info procs" ::errorCheck [B info instprocs] myProc2 "info instprocs" ::errorCheck [lsort [b info methods]] "__object_configureparameter abstract append array autoname check class cleanup configure contains copy defaultmethod destroy eval exists extractConfigureArg f filter filterguard filtersearch forward hasclass incr info init instvar invar isclass ismetaclass ismixin isobject istype lappend method mixin mixinguard move myProc myProc2 myProcMix1 myProcMix2 noinit objproc parametercmd proc procsearch requireNamespace residualargs self set setFilter signature subst trace unknown unset uplevel upvar volatile vwait" "b info methods" ::errorCheck [lsort [b info methods -nocmds]] "__object_configureparameter abstract check extractConfigureArg f filtersearch forward hasclass init isclass ismetaclass ismixin isobject istype method myProc myProc2 myProcMix1 myProcMix2 objproc proc procsearch self setFilter signature unknown vwait" "b info methods -nocmds" ::errorCheck [lsort [b info methods -noprocs]] "append array autoname class cleanup configure destroy eval exists filter filterguard incr info instvar invar lappend mixin mixinguard noinit parametercmd requireNamespace residualargs set subst trace unset uplevel upvar volatile" "b info methods -noprocs" ::errorCheck [lsort [b info methods -nocmds -nomixins]] "__object_configureparameter abstract check extractConfigureArg f filtersearch forward hasclass init isclass ismetaclass ismixin isobject istype method myProc myProc2 objproc proc procsearch self setFilter signature unknown vwait" "b info methods -nocmds -nomixins" ::errorCheck [b info methods -nocmds -noprocs] "" "b info methods -nocmds -noprocs" ::errorCheck [lsort [B info methods -nocmds]] "__class_configureparameter __object_configureparameter abstract allinstances check extractConfigureArg f filtersearch forward hasclass init instforward instproc isclass ismetaclass ismixin isobject istype method parameter proc procsearch self setFilter signature slots unknown uses vwait" "B info methods -nocmds" namespace eval a { proc o args {return o} } namespace eval a::b { proc b args {return b} } Object a a requireNamespace set r [a::b::b] Object a::b a::b proc x args { return x } set r "$r-[a::b x]-[a o]" ::errorCheck $r b-x-o "Tcl Namespace should survive requireNamespace" xotcl::interp create in set ::r [in eval { package prefer latest package req XOTcl 2.0; namespace import ::xotcl::* Object o }] xotcl::interp delete in ::errorCheck $::r ::o "XOTcl slave interpreter " Object o -requireNamespace o set r 1 after 100 {o set r 3} o vwait r ::errorCheck [o set r] 3 "Vwait test" Class NS Class NS::Main NS::Main proc m1 {} { my m2 } NS::Main proc m2 {} { namespace eval :: Object toplevelObj } NS::Main m1 ::errorCheck [::toplevelObj set a 1] 1 "toplevel object allocated in ns" namespace eval foo { Class Foo Foo instproc blah {} {puts jou} Foo proc bar {} {puts bar} } namespace delete foo ::errorCheck [Object isobject ::foo::Foo] 0 "Namespace delete under object" # destroy test set x [Object create x] x destroy ::errorCheck [catch {$x set a 1}] 1 "Reference to destroyed object still valid" Object create x -volatile unset x ::errorCheck [catch {x destroy}] 1 "Object should not exist anymore" Object create x -volatile x destroy ::errorCheck [catch {unset x}] 1 "Variable should not exist anymore" } @ TestX objectReferences { description { Regression test for object and class references in tcl_objs } } TestX objectReferences -proc run {{n 20}} { my proc ok01 {} { Class AAA AAA destroy Class AAA } my proc ok02 {} { Class ::AAA AAA destroy Class AAA } my proc ok03 {} { Class ::AAA ::AAA destroy Class AAA } my proc ok04 {} { Class ::AAA ::AAA destroy Class ::AAA } my proc ok05 {} { set c [Class AAA] $c destroy Class AAA } my proc ok06 {} { set c [Class ::AAA] $c destroy Class AAA } my proc ok07 {} { set c [Class ::AAA] $c destroy Class ::AAA } my proc ok08 {} { set c [Class ::AAA] $c destroy Class $c } my proc ok09 {} { [Class AAA] destroy Class AAA } my proc ok10 {} { [Class ::AAA] destroy Class AAA } my proc ok11 {} { [Class ::AAA] destroy Class ::AAA } for {set i 1} {$i < 20} {incr i} { # "reference to xotcl object in instvar" Class LexxTreeMounter Class LexxTree LexxTreeMounter proc new {args} { if {[LexxTree exists LexxTreeMounter]} { set o [LexxTree set LexxTreeMounter] } else { set o [my create [my autoname [self]]] } $o incr C(refcnt) return $o } LexxTreeMounter instproc init {args} { my instvar C set C(refcnt) 0 if {[LexxTree exists LexxTreeMounter] == 0} { LexxTree set LexxTreeMounter [self] } next } set x [LexxTreeMounter new] set x [LexxTreeMounter new] ::errorCheck [llength [LexxTreeMounter info instances]] 1 singleton # "Global reference to xotcl object" set ::v [Object ::a] set ::w [Object ::b] set ::z(1) [Object ::c] unset ::v # "Class creation and Class destroys, after 2nd round procs contain xotcl-object references" foreach m [lsort [my info procs ok*]] {my $m} ::errorCheck [my isobject AAA] 1 classdestroys } catch {UnknownClass destroy} set ::utest "" Class proc __unknown args { #puts stderr ===UNK-$args lappend ::utest $args set x [Class $args] set r [$x] #puts r=$r return $r } Class O -superclass UnknownClass ::errorCheck $::utest ::UnknownClass "__unknown 1" Object o ::errorCheck [o mixin XX1] ::XX1 "__unknown XX1" namespace eval "" { Object o ::errorCheck [o mixin XX2] ::XX2 "__unknown XX2" } namespace eval "::" { Object o ::errorCheck [o mixin XX3] ::XX3 "__unknown XX3" } # this version of unknown creates gobal objects Class proc __unknown {name} { #puts "unknown called with $name" set name ::[namespace tail $name] set x [Class $name] set r [$x] #puts "... created $r" return $r } Object o ::errorCheck [o mixin XY1] ::XY1 " __unknown XY1" namespace eval "" { Object o ::errorCheck [o mixin XY2] ::XY2 " __unknown XY2" } namespace eval :: { Object o ::errorCheck [o mixin XY3] ::XY3 " __unknown XY3" } Class C namespace eval ::tmp { Object o -mixin C ::errorCheck [o mixin XY4] ::XY4 " __unknown XY4" } ::errorCheck [UnknownClass info info] {valid options are: args, body, check, children, class, classchildren, classparent, commands, default, filter, filterguard, forward, hasnamespace, heritage, info, instances, instargs, instbody, instcommands, instdefault, instfilter, instfilterguard, instforward, instinvar, instmixin, instmixinguard, instmixinof, instnonposargs, instparametercmd, instpost, instpre, instprocs, invar, methods, mixin, mixinguard, mixinof, nonposargs, parameter, parametercmd, parent, post, pre, precedence, procs, slots, subclass, superclass, vars} "UnknownClass info info" # clear unknown handler to avoid strange results later Class proc __unknown "" "" ::errorCheck [Class info instances *Unk*] ::UnknownClass "match in info instances" ::errorCheck [Class info instances Unk*] "::UnknownClass" "no match in info instances" ::errorCheck [Class info instances Unk] "" "no match in info instances (no metachars)" ::errorCheck [Class info class] ::xotcl::Class "info class of Class" ::errorCheck [Class info precedence ::xotcl::Object] ::xotcl::Object "info class of Class Object" Class C Class D -superclass C Class E -superclass D -parameter {{x 1}} E instproc t {a b {c 1}} {return ok} E proc p {a b {c 1}} {return ok} E instproc q {} {return [self proc]} ::errorCheck [C info subclass D] ::D "transitive subclass 0" ::errorCheck [C info subclass E] "" "transitive subclass 0a" ::errorCheck [C info subclass -closure E] ::E "transitive subclass 1" ::errorCheck [Object info subclass -closure E] ::E "transitive subclass 2" ::errorCheck [D info subclass -closure C] "" "transitive subclass 3" ::errorCheck [E info heritage] "::D ::C ::xotcl::Object" "heritage" ::errorCheck [E info instargs t] "a b c" "instargs" ::errorCheck [E info instdefault t c x] 1 "instdefault" ::errorCheck [E info args p] "a b c" "args" ::errorCheck [E info default p c x] 1 "default" ::errorCheck [E configure [list -p -x -y]] {} "list params 1" #::errorCheck [E e1 [list -t -1 -e -3]] ::e1 "list params 2"; # TODO worked in 1.6 ::errorCheck [E create e1 [list -t -1 -e -3]] ::e1 "list params 2" ::errorCheck [e1 x] 1 "instparameter cmd 1" ::errorCheck [e1 x 2] 2 "instparameter cmd 2" ::errorCheck [e1 x] 2 "instparameter cmd 3" ::errorCheck [e1 parametercmd y] "::e1::y" "parametercmd 1" ::errorCheck [e1 y 3] 3 "parametercmd 2" ::errorCheck [e1 y] 3 "parametercmd 3" ::errorCheck [e1 forward regexp -objscope] "::e1::regexp" "forward 1" ::errorCheck [e1 regexp (y) xyz _ X] "1" "forward 2" ::errorCheck [e1 exists X] "1" "forward 3" ::errorCheck [e1 q] q "self proc" ::errorCheck [lsort [E info commands]] {p} "class commands" ::errorCheck [lsort [E info instcommands]] "q t x" "class instcommands" ::errorCheck [E info instbody t] "return ok" "class info instbody" Object o Object o::abc Object o::bcd Object o::cde namespace eval ns1 {Class C; namespace export C} o eval {namespace import ::ns1::*} ::errorCheck [lsort [o info children]] "::o::abc ::o::bcd ::o::cde" "info children 1" ::errorCheck [lsort [o info children *cd*]] "::o::bcd ::o::cde" "info children 2" ::errorCheck [o info children ::o::cde] ::o::cde "info children 3" ::errorCheck [o info children ::o::def] "" "info children 4" Object new -childof o ::errorCheck [llength [o info children]] 4 "info children 5" ::errorCheck [Object isobject o] 1 "is object 1" ::errorCheck [Object isobject ox] 0 "is object 2" Class M -superclass Class ::errorCheck [Object ismetaclass M] 1 "is metaclass 1" ::errorCheck [Object ismetaclass C] 0 "is metaclass 0" Class X ::errorCheck [Object ismetaclass X] 0 "is metaclass 0" ::errorCheck [X isclass] 1 "is isclass 1" ::errorCheck [Class info instances X] ::X "is an instance of Class" ::errorCheck [catch {X class Object}] 1 "turn class into an object (error)" Class Y -superclass X Object o1 -mixin Y ::errorCheck [o1 info precedence] "::Y ::X ::xotcl::Object" "normal mixin precedence" Object X ;# turn class X into Object X (via destroy/create) ::errorCheck [o1 info precedence] "::Y ::xotcl::Object" "reduced mixin precedence" X destroy Y destroy o1 destroy Class M -superclass Class M create m1 ::errorCheck [Object ismetaclass M] 1 "is metaclass 1" ::errorCheck [M isclass] 1 "is isclass 1" ::errorCheck [Class info instances M] ::M "is an instance of Class" ::errorCheck [m1 info class] ::M "m1 is an instance of the meta-class" ::errorCheck [m1 isclass] 1 "m1 is isclass 1" ::errorCheck [m1 info class] ::M "m1 is of class ::M" Object M ;# make object from metaclass ::errorCheck [Object ismetaclass M] 0 "is metaclass 0" ::errorCheck [M isclass] 0 "is isclass 0" ::errorCheck [Class info instances M] "" "is not an instance of Class" ::errorCheck [Object isclass m1] 1 "m1 is still a class" ::errorCheck [::xotcl::is object m1] 1 "m1 is still an object" ::errorCheck [::xotcl::is class m1] 1 "m1 is still a class" ::errorCheck [::xotcl::relation m1 class] ::xotcl::Class "m1 now a baseclass" ::errorCheck [m1 info class] ::xotcl::Class "m1 is now an instance of Class" ::errorCheck [m1 isclass] 1 "m1 is isclass 1" ::errorCheck [m1 info class] ::xotcl::Class "m1 is of class ::xotcl::Class" M destroy # to be completed XXX Class C -parameter {number name} C instproc test {} { my instvar {number x} name return [list $name $x] } C c -name koen -number 25 ::errorCheck [c test] "koen 25" "instvar with alias" # Class C Class D -superclass C Class D1 D instmixin D1 D d1 ::errorCheck [d1 info precedence] "::D1 ::D ::C ::xotcl::Object" "d1 info precedence" ::errorCheck [d1 info precedence *] "::D1 ::D ::C ::xotcl::Object" "d1 info precedence *" ::errorCheck [d1 info precedence ::D*] "::D1 ::D" "d1 info precedence pattern" ::errorCheck [d1 info precedence -intrinsic] "::D ::C ::xotcl::Object" "d1 info precedence -intrinsic" ::errorCheck [d1 info precedence -intrinsic *] "::D ::C ::xotcl::Object" "d1 info precedence -intrinsic *" ::errorCheck [d1 info precedence -intrinsic ::D*] "::D" "d1 info precedence -intrinsic pattern" d1 destroy D destroy D1 destroy } @ TestX condMixins { description { Regression test for conditional mixins } } TestX create condMixins -proc show {c obj} { set ::context $c set r [list] foreach x [list \ [list $obj info methods salary] \ [list $obj info methods -incontext salary] \ [list $obj info methods driv*] \ [list $obj info methods -incontext driv*] \ ] { lappend r "$::context: $x => [lsort [eval $x]]" } return $r } condMixins proc run {{n 20}} { Object instproc signature {} {return "[self] [my info class] ([my age] years)"} Class Person -parameter {id name age} Class Payroll-aspect -parameter salary Payroll-aspect instproc print {} {puts "[my signature]: [my salary]"} Class Driver-aspect -parameter driving-license Payroll-aspect instproc print {} {puts "[my signature]: [my driving-license]"} Person instmixin {{Payroll-aspect -guard {[string equal $::context "payrollApp"]}}} Person jim -mixin {{Driver-aspect -guard {[string equal $::context "shipmentApp"]}}} set ::context payrollApp ::errorCheck [lsort [jim info methods]] "__object_configureparameter abstract age append array autoname check class cleanup configure contains copy defaultmethod destroy driving-license eval exists extractConfigureArg filter filterguard filtersearch forward hasclass id incr info init instvar invar isclass ismetaclass ismixin isobject istype lappend method mixin mixinguard move name noinit parametercmd print proc procsearch requireNamespace residualargs salary self set signature subst trace unknown unset uplevel upvar volatile vwait" "condmixin all methods" ::errorCheck "[lsort [jim info methods -incontext]]" "__object_configureparameter abstract age append array autoname check class cleanup configure contains copy defaultmethod destroy eval exists extractConfigureArg filter filterguard filtersearch forward hasclass id incr info init instvar invar isclass ismetaclass ismixin isobject istype lappend method mixin mixinguard move name noinit parametercmd print proc procsearch requireNamespace residualargs salary self set signature subst trace unknown unset uplevel upvar volatile vwait" "all methods in context" ::errorCheck [my show payrollApp jim] "{payrollApp: jim info methods salary => salary} {payrollApp: jim info methods -incontext salary => salary} {payrollApp: jim info methods driv* => driving-license} {payrollApp: jim info methods -incontext driv* => }" "payrollApp jim" ::errorCheck [my show shipmentApp jim] "{shipmentApp: jim info methods salary => salary} {shipmentApp: jim info methods -incontext salary => } {shipmentApp: jim info methods driv* => driving-license} {shipmentApp: jim info methods -incontext driv* => driving-license}" "shipmentApp jim" } @ TestX volatileObjects { description { Regression test for volatile objects } } TestX create volatileObjects volatileObjects proc inscope {} { set r 0 set y 0 set z 0 set c [C new -volatile] catch {incr r [$c test]} catch {set y [$c y]} catch {set z [$c z]} if {[catch {set u [$c u]} err]} {puts stderr $err} return $r-[llength [C info instances]]-$y-$z-$u } volatileObjects proc run {{n 20}} { Class create ::xotcl::_creator -instproc create {args} { set result [next] return $result } Class instproc f args { #puts stderr "*****F [self calledproc]" return [next] } Class C -parameter {{x 0}} C instproc f args { #puts stderr "*****C [self calledproc]" return [next] } C instproc test {} { my incr x } C instproc y {} { my instvar x; incr x } C instproc z {} { my set x 10 } C instproc u {} { upvar [self callinglevel] z b; info exists b } Class create ::xotcl::I -instproc instvar args { #puts [self proc] next } -instproc set args { #puts [self proc] next } -instproc u args { #puts [self proc] next } ::errorCheck [llength [C info instances]] 0 "foreign instances" ::errorCheck [my inscope] 1-1-2-10-1 "volatile objects in scope" ::errorCheck [llength [C info instances]] 0 "instances survived scope" Class instmixin ::xotcl::_creator ::errorCheck [my inscope] 1-1-2-10-1 "volatile objects in scope through mixin" ::errorCheck [llength [C info instances]] 0 \ "instances survived scope through mixin" Class instfilter f ::errorCheck [my inscope] 1-1-2-10-1 \ "volatile objects in scope through mixin + filter" ::errorCheck [llength [C info instances]] 0 \ "instances survived scope through mixin + filter" Class instmixin {} Class instfilter f ::errorCheck [my inscope] 1-1-2-10-1 \ "volatile objects in scope through filter" ::errorCheck [llength [C info instances]] 0 \ "instances survived scope through filter" Class instfilter {} C instmixin ::xotcl::I ::errorCheck [my inscope] 1-1-2-10-1 \ "instvar overload in scope through mixin" C instfilter f ::errorCheck [my inscope] 1-1-2-10-1 \ "instvar overload in scope through mixin and filter" C instfilter {} Class instproc f {} {} } TestX create uplevelCmds uplevelCmds proc upproc {} { lappend ::result [list \ self=[self] \ up1=[uplevel 1 self] \ up2=[uplevel 2 self] \ up3=[uplevel 3 self] ] } uplevelCmds proc run {{n 20}} { Object o1 -proc m {} { set ::result [list] lappend ::result [list \ self=[self] \ up1=[uplevel 1 self] \ up2=[uplevel 2 self] \ up3=[uplevel 3 self] ] #uplevelCmds::upproc uplevelCmds upproc return $::result } Object o2 -proc m {} { o1 m } Object o3 -proc m {} { o2 m } Object o4 -proc m {} { o3 m } ::errorCheck [o4 m] \ "{self=::o1 up1=::o2 up2=::o3 up3=::o4} {self=::uplevelCmds up1=::o1 up2=::o2 up3=::o3}" \ "uplevel self" o4 m proc showstack {} { set l [info level] for {set i $l} {$i>0} {incr i -1} { set vars [uplevel \#$i info vars] upvar \#$i what w if {![info exists w]} {set w ""} puts "$i: $w[info level $i] vars=$vars" } } Class C C instproc u0 {} { upvar [self callinglevel] x y; incr y return [uplevel [self callinglevel] {incr x 1}] } C instproc u1 {} { upvar [self callinglevel] x y; incr y set r [uplevel [self callinglevel] {incr x 1}] set z [uplevel [self activelevel] incr z] return $z-$r } C instproc p0 {y} { set x $y set r [my u0] return $r-$x } C instproc p1 {y} { set z 0 set x $y set r [my u1] return $r-$x } Class D -superclass C D instproc u0 {} { upvar [self callinglevel] x y; incr y return [uplevel [self callinglevel] {incr x 1}] } D instproc u1 {} { set z [uplevel [self activelevel] incr z] set r [next] return $z-$r } Class M M instproc u1 {} { set z [uplevel [self activelevel] incr z] set r [next] return $z-$r } Object instproc f args { next } D create d1 errorCheck [d1 p0 1] 3-3 "simple uplevel" errorCheck [d1 p1 1] 2-2-3-3 "uplevel through next in class hierarchy + activelevel" D instmixin M errorCheck [d1 p1 1] 1-3-3-3-3 "uplevel through mixin and class hierarchy + activelevel" Object instfilter f errorCheck [d1 p1 1] 1-3-3-3-3 "uplevel through filter, mixin and class hierarchy + activelevel" Object instfilter "" D instmixin {} # now again the same tests with upvar and uplevel methods C instproc u0 {} { my upvar [self callinglevel] x y; incr y return [my uplevel {incr x 1}] } C instproc u1 {} { my upvar [self callinglevel] x y; incr y set r [my uplevel {incr x 1}] set z [my uplevel [self activelevel] incr z] return $z-$r } D instproc u0 {} { my upvar [self callinglevel] x y; incr y return [my uplevel {incr x 1}] } Class M M instproc u1 {} { set z [my uplevel [self activelevel] incr z] set r [next] return $z-$r } errorCheck [d1 p0 1] 3-3 "upvar method: simple uplevel" errorCheck [d1 p1 1] 2-2-3-3 \ "upvar method: uplevel through next in class hierarchy + activelevel" D instmixin M errorCheck [d1 p1 1] 1-3-3-3-3 \ "upvar method: uplevel through mixin and class hierarchy + activelevel" Object instfilter f errorCheck [d1 p1 1] 1-3-3-3-3 \ "upvar method: uplevel through filter, mixin and class hierarchy + activelevel" Object instfilter "" D instmixin {} # now again the same tests with upvar and uplevel methods with default levels C instproc u0 {} { my upvar x y; incr y return [my uplevel {incr x 1}] } C instproc u1 {} { my upvar x y; incr y set r [my uplevel {incr x 1}] set z [my uplevel [self activelevel] incr z] return $z-$r } D instproc u0 {} { my upvar x y; incr y return [my uplevel {incr x 1}] } Class M M instproc u1 {} { set z [my uplevel [self activelevel] incr z] set r [next] return $z-$r } errorCheck [d1 p0 1] 3-3 "upvar method: simple uplevel (dl)" errorCheck [d1 p1 1] 2-2-3-3 \ "upvar method: uplevel through next in class hierarchy + activelevel (dl)" D instmixin M errorCheck [d1 p1 1] 1-3-3-3-3 \ "upvar method: uplevel through mixin and class hierarchy + activelevel (dl)" Object instfilter f errorCheck [d1 p1 1] 1-3-3-3-3 \ "upvar method: uplevel through filter, mixin and class hierarchy + activelevel (dl)" Object instfilter "" D instmixin {} C instproc selftest args { return [self class]/[self isnextcall]-[next] } D instproc selftest args { return [self class]/[self isnextcall]-[next] } errorCheck [d1 selftest] "::D/0-::C/1-" \ "self isnextcall" Object instproc each {objName body} { #puts " *** level = [info level] self callinglevel = [self callinglevel]" uplevel [self callinglevel] [list foreach $objName [lsort [[self] info children]] $body] } Class TestB Class TestA TestA instproc init {args} { next TestB [self]::b1 TestB [self]::b2 TestB [self]::b3 } Class Test Test instproc init {args} { next TestA [self]::a1 TestA [self]::a2 TestA [self]::a3 } Test instproc loop1 {} { set i 0 [self] each a { incr i #puts "$a" } #puts "Total = $i" return $i } Test instproc loop2 {} { set i 0 [self] each a { incr i #puts "$a" $a each b { incr i #puts " $b" } } #puts "Total = $i" return $i } Object instproc f args {next} Test t errorCheck [t loop1] 3 "uplevel eval loop" errorCheck [t loop2] 12 "nested uplevel eval loop" t filter f errorCheck [t loop1] 3 "uplevel eval loop with filter" errorCheck [t loop2] 12 "nested uplevel eval loop with filter" t destroy } TestX create namespaceCommands -proc run {{n 20}} { errorCheck [catch { namespace eval foo { Class m Object o -mixin m } }] 0 "mixin resolved from namespace" Class create ::xotcl::_creator -instproc create {args} { set result [next] return $result } errorCheck [catch { namespace eval bar { Class A namespace export A } namespace eval foo { Class M -superclass Class namespace import ::bar::* Class B -superclass A -instmixin M Class instmixin ::xotcl::_creator Class C -superclass A -instmixin B Class instmixin "" } } error] 0 "mixin and Class resolve and import into namespace\n$error" } TestX create metaClassAsMixin -proc run {{n 20}} { Class create A -instmixin Class Class create B -superclass A B create b1 errorCheck [A ismetaclass]-[B ismetaclass]-[b1 ismetaclass]-[b1 isclass] \ "1-1-0-1" "metaclass through mixin" } TestX create nonposargs -proc run {{n 20}} { Object o o set result "" o proc test1 {-x:switch y} { my append result "x=$x y=$y, " } o test1 1 o test1 -x 1 o proc test2 {{-x:switch true} y} { my append result "x=$x y=$y, " } o test2 2 o test2 -x 2 o proc test3 {{-x:switch false} y} { my append result "x=$x y=$y, " } o test3 3 o test3 -x 3 errorCheck [o set result] \ "x=0 y=1, x=1 y=1, x=true y=2, x=0 y=2, x=false y=3, x=1 y=3, " \ "nonpos args switch" Object o o proc x {a b} { return "$a $b" } o proc y {-x {-a {1 2 3}} a b} { return "$args" } o proc z1 {-x:required {-a {1 2 3}} a args} { return "$x -- $args" } o proc z2 {-x:required {-a {1 }} {-b {1 2}} args} {return "$x -- $args -- $a -- $b"} o proc z3 {-b:boolean arg} { return "$b $arg" } Object colorchecker colorchecker proc color {var value} { lappend ::r "color <$var> <$value>" } colorchecker proc reddish {var value} { lappend ::r "reddish <$var> <$value>" } # o proc z4 { # {{-b: required, checkobj colorchecker,color, reddish, # checkobj xotcl::nonposArgs,required} red} # {{-c: required }} # arg # } { # lappend ::r "$b $arg" # return "$b $arg" # } o proc z5 {-pos args} { return [list $pos $args] } Class P P instproc x {a b} { return "$a $b" } P instproc z2 {-x:required {-a 1} {-b {1 2}} args} {return "$x -- $args -- $a -- $b"} P instproc z3 {-x:required {-a 1} {-b {1 2}} a b c} { return "$x -- $args -- $a -- $b" } P p errorCheck [o x 1 2] "1 2" "Ordinary Method" errorCheck [p x 3 4] "3 4" "Ordinary Method (2)" catch { o y 4 56 5 } m errorCheck $m \ {invalid argument '5', maybe too many arguments; should be "::o y ?-x /value/? ?-a /value/? /a/ /b/"} \ "wrong \# check 1" catch { o y } m errorCheck $m {required argument 'a' is missing, should be: ::o y ?-x /value/? ?-a /value/? /a/ /b/} "wrong \# check 2" catch { o y -x 1 } m errorCheck $m {required argument 'a' is missing, should be: ::o y ?-x /value/? ?-a /value/? /a/ /b/} "wrong \# check 3" catch { o z1 a 1 2 3 } m errorCheck $m {required argument 'x' is missing, should be: ::o z1 -x /value/ ?-a /value/? /a/ ?/arg .../?} "required missing" errorCheck [o z1 -x 1 a 1 2 3] "1 -- 1 2 3" "invocation 1" errorCheck [o z2 -x 2 a 1 2 3] "2 -- a 1 2 3 -- 1 -- 1 2" "invocation 2" catch { o y -x 1 -a 2 2 3 } m errorCheck $m "can't read \"args\": no such variable" "args unset?" errorCheck [o z2 -a 2 -x 1 -b 3 a b c] \ "1 -- a b c -- 2 -- 3" "invocation 3" errorCheck [p z2 -x 1 -a 2 -b 3 a b c] \ "1 -- a b c -- 2 -- 3" "invocation 4" errorCheck [o z3 -b true -- -b] "true -b" "dash dash" errorCheck [o z5 -pos 1 a b] "1 {a b}" "nonpos with given args" errorCheck [o z5 -pos 1 a] "1 a" "nonpos with given args" errorCheck [o z5 -pos 1] "1 {}" "nonpos without given args" catch { o z3 -b abc -- -b } m errorCheck $m {expected boolean but got "abc" for parameter "-b"} "not boolean" set ::r "" #o z4 -c 1 1 #errorCheck $::r "{color } {reddish } {red 1}" \ "multiple check options + checkobject" errorCheck [o info body z2] {return "$x -- $args -- $a -- $b"} "info body 1" errorCheck [P info instbody z2] {return "$x -- $args -- $a -- $b"} "info instbody z2" # errorCheck [o info args z4] {arg} "info args" # errorCheck [o info nonposargs z4] "{{-b:required,checkobj colorchecker,color,reddish,checkobj xotcl::nonposArgs,required} red} -c:required" "info nonposargs 1" errorCheck [o info nonposargs x] {} "info nonposargs 2" errorCheck [P info instargs z3] {a b c} "info instargs" errorCheck [P info instnonposargs z3] {-x:required {-a 1} {-b {1 2}}} "info instnonposargs 1" errorCheck [P info instnonposargs x] {} "info instnonposargs 2" Object o o proc foo {{-a apple} {b banana}} { return [list [lsort [info locals]] a: $a b: $b] } o proc foo2 {{-a apple} {b banana} {c apple}} { return [list [lsort [info locals]] a: $a b: $b c: $c] } o proc foo3 {{-a apple} x y {b banana} {c apple}} { return [list [lsort [info locals]] x: $x y: $y a: $a b: $b c: $c] } errorCheck [o foo] [list {a b} a: apple b: banana] \ "non pos + default values 1" errorCheck [o foo -a ack] [list {a b} a: ack b: banana] \ "non pos + default values 2" errorCheck [o foo bar] [list {a b} a: apple b: bar] \ "non pos + default values 3" errorCheck [o foo -a ack bar] [list {a b} a: ack b: bar] \ "non pos + default values 4" errorCheck [o foo2 -a ack] [list {a b c} a: ack b: banana c: apple] \ "non pos + default values 5" errorCheck [ o foo3 -a ack 1 2] [list {a b c x y} x: 1 y: 2 \ a: ack b: banana c: apple] \ "non pos + default values 6" Object o o proc foo {{-foo 1}} { #puts "foo: $foo" } o foo o foo -foo 0 catch {o foo -foo} msg errorCheck $msg "value for parameter '-foo' expected" "Empty non-pos arg" Object oa oa proc foo {{-a A} b} { #puts "$a $b" } oa foo "B" oa foo "-" oa foo "---" catch {oa foo "--"} msg errorCheck $msg {required argument 'b' is missing, should be: ::oa foo ?-a /value/? /b/} "Non-pos arg: double dash alone" Class C C create c1 C instproc m2 { {-flag:boolean false} x y {z 15} } { return $flag-$z } c1 proc m14 { {-flag:boolean false} x y {z 15} } { return $flag-$z } errorCheck [list [c1 m14 1 2 3] [c1 info args m14] \ [c1 info default m14 z e] [set e]] \ "false-3 {x y z} 1 15" \ "Defaults proc" errorCheck [list [c1 m2 1 2 3] [C info instargs m2]] \ "false-3 {x y z}" \ "info instargs" errorCheck [list [C info instdefault m2 x d] [C info instdefault m2 z d] [set d]] \ "0 1 15" \ "Defaults for instproc" catch {C info instdefault m2 xxx e} msg errorCheck $msg {procedure "m2" doesn't have an argument "xxx"} \ "Defaults instproc error" C instproc m3 { {-flag:boolean} x y z } { return hu3 } errorCheck [c1 m3 1 2 3] "hu3" "Defaults instproc no flag" Object o o proc f1 {{-x:boolean true} a } { if {![info exists a]} {error "pos arg a does not exist"} if {$x ne "true"} {error "x $x ne true"} if {$a ne "x"} {error "a $a ne x"} if {[info exists args]} {error "args still exists"} } o proc f2 {{-x:boolean true} {a x}} { if {![info exists a]} {error "pos arg a does not exist"} if {$x ne "false"} {error "x $x ne false"} if {$a ne "x"} {error "a $a ne x"} if {[info exists args]} {error "args still exists"} } o proc f3 {{-x:boolean true} } { if {$x ne "true"} {error "x $x ne true"} if {[info exists args]} {error "args still exists"} } o proc p0 {{-x 1} a} { #puts "--- [self proc] x=$x [info exists a]" if {![info exists a]} {error "pos arg a does not exist"} if {$a != 1} {error "a $a != 1"} if {$x != 1} {error "x $x != 1"} if {[info exists args]} {error "args still exists"} } o proc p1 {{-x 1} a args} { #puts "--- [self proc] x=$x [info exists a] args=$args" if {![info exists a]} {error "pos arg a does not exist"} if {$a != 1} {error "a $a != 1"} if {$x != 1} {error "x $x != 1"} if {$args ne ""} {error "args $args ne {}"} } o proc p2 {{-x 1} args} { if {$x != 1} {error "x $x != 1"} if {$args ne ""} {error "args $args ne {}"} } o proc p3 {{-x 1} args} { if {$x != 1} {error "x $x != 1"} if {$args ne "a b c"} {error "args $args ne {}"} } o proc p4 {{-x 1} args} { if {$x != 2} {error "x $x != 2"} if {$args ne "a b c"} {error "args $args ne {a b c}"} } o proc p5 {{-x 1} a args} { #puts "--- [self proc] x=$x [info exists a] args=$args" if {![info exists a]} {error "pos arg a does not exist"} if {$a != 1} {error "a $a != 1"} if {$x != 1} {error "x $x != 1"} if {$args ne "a b c"} {error "args $args ne {a b c}"} } o proc p6 {{-x 1} a args} { #puts "--- [self proc] x=$x [info exists a] args=$args" if {![info exists a]} {error "pos arg a does not exist"} if {$a != 1} {error "a $a != 1"} if {$x != 2} {error "x $x != 2"} if {$args ne "a b c"} {error "args $args ne {a b c}"} } o proc p7 {{-x 1} a args} { #puts "--- [self proc] x=$x [info exists a] args=$args" if {![info exists a]} {error "pos arg a does not exist"} if {$a != 1} {error "a $a != 1"} if {$x != 2} {error "x $x != 2"} if {$args ne ""} {error "args $args ne {}"} } o proc p8 {{-x 1} {a 1} args} { #puts "--- [self proc] x=$x [info exists a] args=$args" if {![info exists a]} {error "pos arg a does not exist"} if {$a != 1} {error "a $a != 1"} if {$x != 2} {error "x $x != 2"} if {$args ne ""} {error "args $args ne {}"} } errorCheck [catch {o f1 x}] 0 nonpos-1 errorCheck [catch {o f1 -y 1}] 1 nonpos-2 errorCheck [catch {o f1 -x false}] 1 nonpos-3 errorCheck [catch {o f2 -x false}] 0 nonpos-4 errorCheck [catch {o f3}] 0 nonpos-5 errorCheck [catch {o f3 -x true -y 1}] 1 nonpos-6 errorCheck [catch {o f3-y 1}] 1 nonpos-7 errorCheck [catch {o p0 1}] 0 nonpos-8 errorCheck [catch {o p1 1}] 0 nonpos-9 errorCheck [catch {o p1 }] 1 nonpos-10 errorCheck [catch {o p2 }] 0 nonpos-11 errorCheck [catch {o p3 a b c}] 0 nonpos-12 errorCheck [catch {o p4 -x 2 a b c}] 0 nonpos-13 errorCheck [catch {o p5 1 a b c}] 0 nonpos-14 errorCheck [catch {o p7 -x 2 1}] 0 nonpos-15 errorCheck [catch {o p7 -x 2 }] 1 nonpos-16 errorCheck [catch {o p8 -x 2 }] 0 nonpos-17 o proc foo {-enable:switch i:integer} { return "enable=$enable, i=$i" } o proc bar {-enable:switch o:object c:class} { return "o=$o c=$c" } errorCheck [catch {o foo 123}] 0 check-pos-args-1 errorCheck [catch {o foo abc}] 1 check-pos-args-2 errorCheck [catch {o bar o Object}] 0 check-pos-args-3 errorCheck [catch {o bar ooo Object}] 1 check-pos-args-4 errorCheck [catch {o bar o Object1}] 1 check-pos-args-5 Class X X instproc ListOfStringsOption {{-default "murr6"} {-cb {}} name} { if {$cb eq {}} { set cb "::set ::$name " } ;# global variable eval $cb \$default } ::X create x1 ::x1 ListOfStringsOption uu errorCheck [set ::uu] murr6 murr6 ::x1 destroy X destroy } TestX copymove2 -proc run {{n 10}} { # Composite Class Composite -superclass Class Composite instproc addop {op} { my instvar ops set ops($op) $op } Composite instproc compositeFilter args { set m [self calledproc] set c [lindex [self filterreg] 0] set r [next] if {[$c exists ops($m)]} { foreach child [my info children] { eval [self]::$child $m $args } } return $r } Composite AbstractNode AbstractNode abstract instproc iterate v AbstractNode addop iterate for {set i 0} {$i < $n} {incr i} { # # class copy # foreach filters {{} compositeFilter} { Composite instfilter $filters AbstractNode instfilter $filters Object commands Class Commands -superclass AbstractNode Class Command -superclass Commands Command instproc init args { my instvar label set label [self] next } Command instproc setlabel {{arg ""}} { my instvar label if {$arg eq ""} { set label } else { set label $arg } } Command instproc setproc {value} { my instvar src set src $value } # prototypes Command commands::cellcmd commands::cellcmd copy toto } } } TestX proc run {} { foreach test [lsort [TestX info instances]] { puts stderr "$test: start" $test run } } puts "XOTcl - Test" puts "Time used: [time {TestX run} 1]" # toplevel tests ################################################# Class instmixin {} C instmixin {} set o [C new -volatile];errorCheck [Object isobject $o] 1 "topLevel, check object 1 - $o" Class instmixin ::xotcl::_creator set o [C new -volatile];errorCheck [Object isobject $o] 1 "topLevel, check object 2 - $o" C instmixin ::xotcl::I set o [C new -volatile];errorCheck [Object isobject $o] 1 "topLevel, check object 3 - $o" foreach i [C info instances] {$i destroy} proc x {} { Class instmixin {} C instmixin {} set c0 [llength [C info instances]] set o1 [C new -volatile]; errorCheck [Object isobject $o1] 1 "x, check object" Class instmixin ::xotcl::_creator set o2 [C new -volatile]; errorCheck [Object isobject $o2] 1 "x, check object" C instmixin ::xotcl::I set o3 [C new -volatile]; errorCheck [Object isobject $o3] 1 "x, check object" set c1 [llength [C info instances]] errorCheck [expr {$c1 - $c0 != 3}] 0 "exit x, three more objects" #puts stderr "WE HAVE $o1 $o2 $o3" } x errorCheck [expr {[llength [C info instances]] > 0}] 0 "top, all volatile object gone" proc x1 {} { set c0 [llength [C info instances]] set o1 [C new -volatile]; errorCheck [Object isobject $o1] 1 "x1, check object $o1" x set o2 [C new -volatile]; errorCheck [Object isobject $o2] 1 "x1, check object $o2" set c1 [llength [C info instances]] errorCheck [expr {$c1 - $c0 != 2}] 0 "exit x1, two more objects - $c1 ($o1,$o2), [C info instances]" } x1 errorCheck [expr {[llength [C info instances]] > 0}] 0 "top, volatile objects gone" Object o o proc test {} { x1; errorCheck [expr {[llength [C info instances]] > 0}] 0 "x1 from o" } o test puts "PASSED ::topLevelCommands" # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/xotcl/tests/xocomm.test000066400000000000000000000067031242365656200172410ustar00rootroot00000000000000# -*- Tcl -*- package prefer latest package require XOTcl 2.0; namespace import ::xotcl::* set auto_path [concat [file dirname [info script]]/../library $auto_path] package require nx::test @ @File { description { This is a webclient used as a regression test. When it is started it launches an xotcl-Web server as partner process. It tests currently the basic functionality of:
    • GET and PUT requests
    • Basic Access Control
    } } array set opt {-startServer 1} array set opt $argv set xotclsh [info nameofexecutable] set dir [file dir [info script]] set serverScript $dir/../apps/comm/webserver.xotcl set startCmd "$xotclsh $serverScript -root $dir/../doc -pkgdir $dir/../library" puts $startCmd if {$opt(-startServer)} { set PIPE [open "| $startCmd"] } else { puts $startCmd } package require xotcl::comm::httpAccess package require xotcl::comm::ftp package require xotcl::trace #::xotcl::package verbose 1 #::xotcl::package require xotcl::comm::httpAccess #::xotcl::package require xotcl::comm::ftp #::xotcl::package require xotcl::trace set hostport localhost:8086 set protectedhostport localhost:9096 set slowURL "http://quote.yahoo.com/q?s=^DJI&d=1d" set ftpURL "ftp://mohegan.wi-inf.uni-essen.de/welcome.msg" proc printError msg {puts stderr !!!$msg!!!} Object userPwd userPwd proc user {u} { my set user $u if {[set ::tcl_platform(platform)] == "windows"} { my set user unknown } } userPwd proc show {realm userVar pwVar} { upvar $userVar u $pwVar pw set u [my set user] set pw test return 1 } #Test parameter {{errorReport { # puts "\tcontent-length: \[r0::sink set contentLength\]\n\ # \tstatus-code: \[\[r0 set token\] set responseCode\]" #}}} nx::test new -msg "Trying to load image logo-100.jpg ... " \ -verbose 1 \ -pre "puts starting..." \ -setResult {expr {[r0::sink set contentLength] == 1706}} \ -cmd [list SimpleRequest r0 -url http://$hostport/logo-100.jpg] \ nx::test new -msg "Trying to PUT a file on web-server ... " \ -setResult {expr [[r0 set token] set responseCode] == 201} \ -pre [list file delete -force $dir/../doc/junk.junk] \ -cmd [list SimpleRequest r0 \ -url http://$hostport/junk.junk \ -method PUT \ -data "this is a test\n" \ -contentType plain/text] nx::test new -msg "And download it again ... " \ -setResult {expr [r0 getContentLength] == 15} \ -post {file delete -force ../doc/junk.junk} \ -cmd [list SimpleRequest r0 -url http://$hostport/junk.junk] nx::test new -msg "Get protected resource ... " \ -setResult {expr [r0 getContentLength] > 500} \ -cmd [list SimpleRequest r0 -url http://$protectedhostport/ ] #nx::test new -msg "Try an FTP request $ftpURL ... " \ -setResult {expr [r0 getContentLength] > 100} \ -cmd [list SimpleRequest r0 -url $ftpURL] #nx::test new -msg "Try timeout with slow URL $slowURL ... " \ -setResult {expr {[[r0 set token] set errormsg] == {timeout exceeded}}} \ -cmd [list SimpleRequest r0 -url $slowURL -timeout 100] nx::test new -msg terminate \ -setResult {set x 1} \ -cmd [list SimpleRequest r0 -url http://$protectedhostport/exit] \ -post {set ::forever 1} #puts stderr "present [package present xotcl::comm::connection]" #puts stderr "versions [package versions xotcl::comm::connection]" after 1000 {nx::test run} catch {vwait forever} # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: library/xotcl/xotclsh.in000066400000000000000000000022371242365656200157060ustar00rootroot00000000000000#! /bin/sh # Lookup a Tcl interpreter \ INTERP="tclsh@TCL_VERSION@"; \ INTERPS="@NSF_COMPATIBLE_TCLSH@ @TCL_EXEC_PREFIX@/bin/$INTERP"; \ for interp in $INTERPS; \ do if [ -x $interp ]; then INTERP=$interp; break; \ fi; done; \ exec $INTERP "$0" ${1+"$@"} # -*- tcl -*- puts [info nameofexecutable] # # Tiny scripted replacement of a binary nxsh. This script can be used # as interactive shell for testing or like a regular shell with the !# # markup in the first line of a script. It is designed to work with # multiple installed shells during development. For installed # versions, it should be sufficient to remove the first line. # package require XOTcl 2.0 namespace import -force ::xotcl::* if {$argc == 0} { set prefix "" set line "" while {1} { if {$line eq ""} { puts -nonewline "% " flush stdout } append line [gets stdin] if {[info complete $line]} { if {[catch $line result]} { puts $::errorInfo } else { puts $result } set line "" continue } append line \n } } else { set argv0 [lindex $argv 0] set argv [lreplace $argv 0 0] incr argc -1 source $argv0 } library/xotcl/xowish.in000066400000000000000000000023211242365656200155350ustar00rootroot00000000000000#! /bin/sh # Lookup a Tcl interpreter \ INTERP="tclsh@TCL_VERSION@"; \ INTERPS="@NSF_COMPATIBLE_TCLSH@ @TCL_EXEC_PREFIX@/bin/$INTERP"; \ for interp in $INTERPS; \ do if [ -x $interp ]; then INTERP=$interp; break; \ fi; done; \ exec $INTERP "$0" ${1+"$@"} # -*- tcl -*- puts [info nameofexecutable] # # Tiny scripted replacement of a binary nxwish (former xowish). This # script can be used as interactive shell for testing or like a # regular shell with the !# markup in the first line of a script. It # is designed to work with multiple installed shells during # development. For installed versions, it should be sufficient to # remove the first line. # package require Tk package require XOTcl 2.0 namespace import -force ::xotcl::* if {$argc == 0} { set prefix "" set line "" while {1} { update if {$line eq ""} { puts -nonewline "% " flush stdout } append line [gets stdin] if {[info complete $line]} { if {[catch $line result]} { puts $::errorInfo } else { puts $result } set line "" continue } append line \n } } else { set argv0 [lindex $argv 0] set argv [lreplace $argv 0 0] incr argc -1 source $argv0 } m4/000077500000000000000000000000001242365656200114115ustar00rootroot00000000000000m4/tcl.m4000066400000000000000000004056471242365656200124550ustar00rootroot00000000000000# tcl.m4 -- # # This file provides a set of autoconf macros to help TEA-enable # a Tcl extension. # # Copyright (c) 1999-2000 Ajuba Solutions. # Copyright (c) 2002-2005 ActiveState Corporation. # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. AC_PREREQ(2.57) dnl TEA extensions pass us the version of TEA they think they dnl are compatible with (must be set in TEA_INIT below) dnl TEA_VERSION="3.9" # Possible values for key variables defined: # # TEA_WINDOWINGSYSTEM - win32 aqua x11 (mirrors 'tk windowingsystem') # TEA_PLATFORM - windows unix # #------------------------------------------------------------------------ # TEA_PATH_TCLCONFIG -- # # Locate the tclConfig.sh file and perform a sanity check on # the Tcl compile flags # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --with-tcl=... # # Defines the following vars: # TCL_BIN_DIR Full path to the directory containing # the tclConfig.sh file #------------------------------------------------------------------------ AC_DEFUN([TEA_PATH_TCLCONFIG], [ dnl TEA specific: Make sure we are initialized AC_REQUIRE([TEA_INIT]) # # Ok, lets find the tcl configuration # First, look for one uninstalled. # the alternative search directory is invoked by --with-tcl # if test x"${no_tcl}" = x ; then # we reset no_tcl in case something fails here no_tcl=true AC_ARG_WITH(tcl, AC_HELP_STRING([--with-tcl], [directory containing tcl configuration (tclConfig.sh)]), with_tclconfig="${withval}") AC_MSG_CHECKING([for Tcl configuration]) AC_CACHE_VAL(ac_cv_c_tclconfig,[ # First check to see if --with-tcl was specified. if test x"${with_tclconfig}" != x ; then case "${with_tclconfig}" in */tclConfig.sh ) if test -f "${with_tclconfig}"; then AC_MSG_WARN([--with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself]) with_tclconfig="`echo "${with_tclconfig}" | sed 's!/tclConfig\.sh$!!'`" fi ;; esac if test -f "${with_tclconfig}/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd "${with_tclconfig}"; pwd)`" else AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh]) fi fi # then check for a private Tcl installation if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ ../tcl \ `ls -dr ../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../tcl[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../tcl[[8-9]].[[0-9]]* 2>/dev/null` \ ../../tcl \ `ls -dr ../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../../tcl[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \ ../../../tcl \ `ls -dr ../../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../../../tcl[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/unix; pwd)`" break fi done fi # on Darwin, check in Framework installation locations if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d ~/Library/Frameworks 2>/dev/null` \ `ls -d /Library/Frameworks 2>/dev/null` \ `ls -d /Network/Library/Frameworks 2>/dev/null` \ `ls -d /System/Library/Frameworks 2>/dev/null` \ ; do if test -f "$i/Tcl.framework/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/Tcl.framework; pwd)`" break fi done fi # TEA specific: on Windows, check in common installation locations if test "${TEA_PLATFORM}" = "windows" \ -a x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d C:/Tcl/lib 2>/dev/null` \ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \ ; do if test -f "$i/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i; pwd)`" break fi done fi # check in a few common install locations if test x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d ${libdir} 2>/dev/null` \ `ls -d ${exec_prefix}/lib 2>/dev/null` \ `ls -d ${prefix}/lib 2>/dev/null` \ `ls -d /usr/local/lib 2>/dev/null` \ `ls -d /usr/contrib/lib 2>/dev/null` \ `ls -d /usr/lib 2>/dev/null` \ `ls -d /usr/lib64 2>/dev/null` \ `ls -d /usr/lib/tcl8.6 2>/dev/null` \ `ls -d /usr/lib/tcl8.5 2>/dev/null` \ ; do if test -f "$i/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i; pwd)`" break fi done fi # check in a few other private locations if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ ${srcdir}/../tcl \ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/unix; pwd)`" break fi done fi ]) if test x"${ac_cv_c_tclconfig}" = x ; then TCL_BIN_DIR="# no Tcl configs found" AC_MSG_ERROR([Can't find Tcl configuration definitions. Use --with-tcl to specify a directory containing tclConfig.sh]) else no_tcl= TCL_BIN_DIR="${ac_cv_c_tclconfig}" AC_MSG_RESULT([found ${TCL_BIN_DIR}/tclConfig.sh]) fi fi ]) #------------------------------------------------------------------------ # TEA_PATH_TKCONFIG -- # # Locate the tkConfig.sh file # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --with-tk=... # # Defines the following vars: # TK_BIN_DIR Full path to the directory containing # the tkConfig.sh file #------------------------------------------------------------------------ AC_DEFUN([TEA_PATH_TKCONFIG], [ # # Ok, lets find the tk configuration # First, look for one uninstalled. # the alternative search directory is invoked by --with-tk # if test x"${no_tk}" = x ; then # we reset no_tk in case something fails here no_tk=true AC_ARG_WITH(tk, AC_HELP_STRING([--with-tk], [directory containing tk configuration (tkConfig.sh)]), with_tkconfig="${withval}") AC_MSG_CHECKING([for Tk configuration]) AC_CACHE_VAL(ac_cv_c_tkconfig,[ # First check to see if --with-tkconfig was specified. if test x"${with_tkconfig}" != x ; then case "${with_tkconfig}" in */tkConfig.sh ) if test -f "${with_tkconfig}"; then AC_MSG_WARN([--with-tk argument should refer to directory containing tkConfig.sh, not to tkConfig.sh itself]) with_tkconfig="`echo "${with_tkconfig}" | sed 's!/tkConfig\.sh$!!'`" fi ;; esac if test -f "${with_tkconfig}/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd "${with_tkconfig}"; pwd)`" else AC_MSG_ERROR([${with_tkconfig} directory doesn't contain tkConfig.sh]) fi fi # then check for a private Tk library if test x"${ac_cv_c_tkconfig}" = x ; then for i in \ ../tk \ `ls -dr ../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../tk[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../tk[[8-9]].[[0-9]]* 2>/dev/null` \ ../../tk \ `ls -dr ../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../../tk[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../tk[[8-9]].[[0-9]]* 2>/dev/null` \ ../../../tk \ `ls -dr ../../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ../../../tk[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i/unix; pwd)`" break fi done fi # on Darwin, check in Framework installation locations if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tkconfig}" = x ; then for i in `ls -d ~/Library/Frameworks 2>/dev/null` \ `ls -d /Library/Frameworks 2>/dev/null` \ `ls -d /Network/Library/Frameworks 2>/dev/null` \ `ls -d /System/Library/Frameworks 2>/dev/null` \ ; do if test -f "$i/Tk.framework/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i/Tk.framework; pwd)`" break fi done fi # check in a few common install locations if test x"${ac_cv_c_tkconfig}" = x ; then for i in `ls -d ${libdir} 2>/dev/null` \ `ls -d ${exec_prefix}/lib 2>/dev/null` \ `ls -d ${prefix}/lib 2>/dev/null` \ `ls -d /usr/local/lib 2>/dev/null` \ `ls -d /usr/contrib/lib 2>/dev/null` \ `ls -d /usr/lib 2>/dev/null` \ `ls -d /usr/lib64 2>/dev/null` \ ; do if test -f "$i/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i; pwd)`" break fi done fi # TEA specific: on Windows, check in common installation locations if test "${TEA_PLATFORM}" = "windows" \ -a x"${ac_cv_c_tkconfig}" = x ; then for i in `ls -d C:/Tcl/lib 2>/dev/null` \ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \ ; do if test -f "$i/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i; pwd)`" break fi done fi # check in a few other private locations if test x"${ac_cv_c_tkconfig}" = x ; then for i in \ ${srcdir}/../tk \ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]] 2>/dev/null` \ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tkConfig.sh" ; then ac_cv_c_tkconfig="`(cd $i/unix; pwd)`" break fi done fi ]) if test x"${ac_cv_c_tkconfig}" = x ; then TK_BIN_DIR="# no Tk configs found" AC_MSG_ERROR([Can't find Tk configuration definitions. Use --with-tk to specify a directory containing tkConfig.sh]) else no_tk= TK_BIN_DIR="${ac_cv_c_tkconfig}" AC_MSG_RESULT([found ${TK_BIN_DIR}/tkConfig.sh]) fi fi ]) #------------------------------------------------------------------------ # TEA_LOAD_TCLCONFIG -- # # Load the tclConfig.sh file # # Arguments: # # Requires the following vars to be set: # TCL_BIN_DIR # # Results: # # Substitutes the following vars: # TCL_BIN_DIR # TCL_SRC_DIR # TCL_LIB_FILE #------------------------------------------------------------------------ AC_DEFUN([TEA_LOAD_TCLCONFIG], [ AC_MSG_CHECKING([for existence of ${TCL_BIN_DIR}/tclConfig.sh]) if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then AC_MSG_RESULT([loading]) . "${TCL_BIN_DIR}/tclConfig.sh" else AC_MSG_RESULT([could not find ${TCL_BIN_DIR}/tclConfig.sh]) fi # eval is required to do the TCL_DBGX substitution eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\"" eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\"" # If the TCL_BIN_DIR is the build directory (not the install directory), # then set the common variable name to the value of the build variables. # For example, the variable TCL_LIB_SPEC will be set to the value # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC # instead of TCL_BUILD_LIB_SPEC since it will work with both an # installed and uninstalled version of Tcl. if test -f "${TCL_BIN_DIR}/Makefile" ; then TCL_LIB_SPEC="${TCL_BUILD_LIB_SPEC}" TCL_STUB_LIB_SPEC="${TCL_BUILD_STUB_LIB_SPEC}" TCL_STUB_LIB_PATH="${TCL_BUILD_STUB_LIB_PATH}" elif test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use the libraries # from the framework at the given location so that linking works # against Tcl.framework installed in an arbitrary location. case ${TCL_DEFS} in *TCL_FRAMEWORK*) if test -f "${TCL_BIN_DIR}/${TCL_LIB_FILE}"; then for i in "`cd "${TCL_BIN_DIR}"; pwd`" \ "`cd "${TCL_BIN_DIR}"/../..; pwd`"; do if test "`basename "$i"`" = "${TCL_LIB_FILE}.framework"; then TCL_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TCL_LIB_FILE}" break fi done fi if test -f "${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"; then TCL_STUB_LIB_SPEC="-L`echo "${TCL_BIN_DIR}" | sed -e 's/ /\\\\ /g'` ${TCL_STUB_LIB_FLAG}" TCL_STUB_LIB_PATH="${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}" fi ;; esac fi # eval is required to do the TCL_DBGX substitution eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\"" eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\"" eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\"" eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\"" AC_SUBST(TCL_VERSION) AC_SUBST(TCL_PATCH_LEVEL) AC_SUBST(TCL_BIN_DIR) AC_SUBST(TCL_SRC_DIR) AC_SUBST(TCL_LIB_FILE) AC_SUBST(TCL_LIB_FLAG) AC_SUBST(TCL_LIB_SPEC) AC_SUBST(TCL_STUB_LIB_FILE) AC_SUBST(TCL_STUB_LIB_FLAG) AC_SUBST(TCL_STUB_LIB_SPEC) AC_MSG_CHECKING([platform]) hold_cc=$CC; CC="$TCL_CC" AC_TRY_COMPILE(,[ #ifdef _WIN32 #error win32 #endif ], TEA_PLATFORM="unix", TEA_PLATFORM="windows" ) CC=$hold_cc AC_MSG_RESULT($TEA_PLATFORM) # The BUILD_$pkg is to define the correct extern storage class # handling when making this package AC_DEFINE_UNQUOTED(BUILD_${PACKAGE_NAME}, [], [Building extension source?]) # Do this here as we have fully defined TEA_PLATFORM now if test "${TEA_PLATFORM}" = "windows" ; then EXEEXT=".exe" CLEANFILES="$CLEANFILES *.lib *.dll *.pdb *.exp" fi # TEA specific: AC_SUBST(CLEANFILES) AC_SUBST(TCL_LIBS) AC_SUBST(TCL_DEFS) AC_SUBST(TCL_EXTRA_CFLAGS) AC_SUBST(TCL_LD_FLAGS) AC_SUBST(TCL_SHLIB_LD_LIBS) ]) #------------------------------------------------------------------------ # TEA_LOAD_TKCONFIG -- # # Load the tkConfig.sh file # # Arguments: # # Requires the following vars to be set: # TK_BIN_DIR # # Results: # # Sets the following vars that should be in tkConfig.sh: # TK_BIN_DIR #------------------------------------------------------------------------ AC_DEFUN([TEA_LOAD_TKCONFIG], [ AC_MSG_CHECKING([for existence of ${TK_BIN_DIR}/tkConfig.sh]) if test -f "${TK_BIN_DIR}/tkConfig.sh" ; then AC_MSG_RESULT([loading]) . "${TK_BIN_DIR}/tkConfig.sh" else AC_MSG_RESULT([could not find ${TK_BIN_DIR}/tkConfig.sh]) fi # eval is required to do the TK_DBGX substitution eval "TK_LIB_FILE=\"${TK_LIB_FILE}\"" eval "TK_STUB_LIB_FILE=\"${TK_STUB_LIB_FILE}\"" # If the TK_BIN_DIR is the build directory (not the install directory), # then set the common variable name to the value of the build variables. # For example, the variable TK_LIB_SPEC will be set to the value # of TK_BUILD_LIB_SPEC. An extension should make use of TK_LIB_SPEC # instead of TK_BUILD_LIB_SPEC since it will work with both an # installed and uninstalled version of Tcl. if test -f "${TK_BIN_DIR}/Makefile" ; then TK_LIB_SPEC="${TK_BUILD_LIB_SPEC}" TK_STUB_LIB_SPEC="${TK_BUILD_STUB_LIB_SPEC}" TK_STUB_LIB_PATH="${TK_BUILD_STUB_LIB_PATH}" elif test "`uname -s`" = "Darwin"; then # If Tk was built as a framework, attempt to use the libraries # from the framework at the given location so that linking works # against Tk.framework installed in an arbitrary location. case ${TK_DEFS} in *TK_FRAMEWORK*) if test -f "${TK_BIN_DIR}/${TK_LIB_FILE}"; then for i in "`cd "${TK_BIN_DIR}"; pwd`" \ "`cd "${TK_BIN_DIR}"/../..; pwd`"; do if test "`basename "$i"`" = "${TK_LIB_FILE}.framework"; then TK_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TK_LIB_FILE}" break fi done fi if test -f "${TK_BIN_DIR}/${TK_STUB_LIB_FILE}"; then TK_STUB_LIB_SPEC="-L` echo "${TK_BIN_DIR}" | sed -e 's/ /\\\\ /g'` ${TK_STUB_LIB_FLAG}" TK_STUB_LIB_PATH="${TK_BIN_DIR}/${TK_STUB_LIB_FILE}" fi ;; esac fi # eval is required to do the TK_DBGX substitution eval "TK_LIB_FLAG=\"${TK_LIB_FLAG}\"" eval "TK_LIB_SPEC=\"${TK_LIB_SPEC}\"" eval "TK_STUB_LIB_FLAG=\"${TK_STUB_LIB_FLAG}\"" eval "TK_STUB_LIB_SPEC=\"${TK_STUB_LIB_SPEC}\"" # TEA specific: Ensure windowingsystem is defined if test "${TEA_PLATFORM}" = "unix" ; then case ${TK_DEFS} in *MAC_OSX_TK*) AC_DEFINE(MAC_OSX_TK, 1, [Are we building against Mac OS X TkAqua?]) TEA_WINDOWINGSYSTEM="aqua" ;; *) TEA_WINDOWINGSYSTEM="x11" ;; esac elif test "${TEA_PLATFORM}" = "windows" ; then TEA_WINDOWINGSYSTEM="win32" fi AC_SUBST(TK_VERSION) AC_SUBST(TK_BIN_DIR) AC_SUBST(TK_SRC_DIR) AC_SUBST(TK_LIB_FILE) AC_SUBST(TK_LIB_FLAG) AC_SUBST(TK_LIB_SPEC) AC_SUBST(TK_STUB_LIB_FILE) AC_SUBST(TK_STUB_LIB_FLAG) AC_SUBST(TK_STUB_LIB_SPEC) # TEA specific: AC_SUBST(TK_LIBS) AC_SUBST(TK_XINCLUDES) ]) #------------------------------------------------------------------------ # TEA_PROG_TCLSH # Determine the fully qualified path name of the tclsh executable # in the Tcl build directory or the tclsh installed in a bin # directory. This macro will correctly determine the name # of the tclsh executable even if tclsh has not yet been # built in the build directory. The tclsh found is always # associated with a tclConfig.sh file. This tclsh should be used # only for running extension test cases. It should never be # or generation of files (like pkgIndex.tcl) at build time. # # Arguments: # none # # Results: # Substitutes the following vars: # TCLSH_PROG #------------------------------------------------------------------------ AC_DEFUN([TEA_PROG_TCLSH], [ AC_MSG_CHECKING([for tclsh]) if test -f "${TCL_BIN_DIR}/Makefile" ; then # tclConfig.sh is in Tcl build directory if test "${TEA_PLATFORM}" = "windows"; then TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}" else TCLSH_PROG="${TCL_BIN_DIR}/tclsh" fi else # tclConfig.sh is in install location if test "${TEA_PLATFORM}" = "windows"; then TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}" else TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_DBGX}" fi list="`ls -d ${TCL_BIN_DIR}/../bin 2>/dev/null` \ `ls -d ${TCL_BIN_DIR}/.. 2>/dev/null` \ `ls -d ${TCL_PREFIX}/bin 2>/dev/null`" for i in $list ; do if test -f "$i/${TCLSH_PROG}" ; then REAL_TCL_BIN_DIR="`cd "$i"; pwd`/" break fi done TCLSH_PROG="${REAL_TCL_BIN_DIR}${TCLSH_PROG}" fi AC_MSG_RESULT([${TCLSH_PROG}]) AC_SUBST(TCLSH_PROG) ]) #------------------------------------------------------------------------ # TEA_PROG_WISH # Determine the fully qualified path name of the wish executable # in the Tk build directory or the wish installed in a bin # directory. This macro will correctly determine the name # of the wish executable even if wish has not yet been # built in the build directory. The wish found is always # associated with a tkConfig.sh file. This wish should be used # only for running extension test cases. It should never be # or generation of files (like pkgIndex.tcl) at build time. # # Arguments: # none # # Results: # Substitutes the following vars: # WISH_PROG #------------------------------------------------------------------------ AC_DEFUN([TEA_PROG_WISH], [ AC_MSG_CHECKING([for wish]) if test -f "${TK_BIN_DIR}/Makefile" ; then # tkConfig.sh is in Tk build directory if test "${TEA_PLATFORM}" = "windows"; then WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}" else WISH_PROG="${TK_BIN_DIR}/wish" fi else # tkConfig.sh is in install location if test "${TEA_PLATFORM}" = "windows"; then WISH_PROG="wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}" else WISH_PROG="wish${TK_MAJOR_VERSION}.${TK_MINOR_VERSION}${TK_DBGX}" fi list="`ls -d ${TK_BIN_DIR}/../bin 2>/dev/null` \ `ls -d ${TK_BIN_DIR}/.. 2>/dev/null` \ `ls -d ${TK_PREFIX}/bin 2>/dev/null`" for i in $list ; do if test -f "$i/${WISH_PROG}" ; then REAL_TK_BIN_DIR="`cd "$i"; pwd`/" break fi done WISH_PROG="${REAL_TK_BIN_DIR}${WISH_PROG}" fi AC_MSG_RESULT([${WISH_PROG}]) AC_SUBST(WISH_PROG) ]) #------------------------------------------------------------------------ # TEA_ENABLE_SHARED -- # # Allows the building of shared libraries # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --enable-shared=yes|no # # Defines the following vars: # STATIC_BUILD Used for building import/export libraries # on Windows. # # Sets the following vars: # SHARED_BUILD Value of 1 or 0 #------------------------------------------------------------------------ AC_DEFUN([TEA_ENABLE_SHARED], [ AC_MSG_CHECKING([how to build libraries]) AC_ARG_ENABLE(shared, AC_HELP_STRING([--enable-shared], [build and link with shared libraries (default: on)]), [tcl_ok=$enableval], [tcl_ok=yes]) if test "${enable_shared+set}" = set; then enableval="$enable_shared" tcl_ok=$enableval else tcl_ok=yes fi if test "$tcl_ok" = "yes" ; then AC_MSG_RESULT([shared]) SHARED_BUILD=1 else AC_MSG_RESULT([static]) SHARED_BUILD=0 AC_DEFINE(STATIC_BUILD, 1, [Is this a static build?]) fi AC_SUBST(SHARED_BUILD) ]) #------------------------------------------------------------------------ # TEA_ENABLE_THREADS -- # # Specify if thread support should be enabled. If "yes" is specified # as an arg (optional), threads are enabled by default, "no" means # threads are disabled. "yes" is the default. # # TCL_THREADS is checked so that if you are compiling an extension # against a threaded core, your extension must be compiled threaded # as well. # # Note that it is legal to have a thread enabled extension run in a # threaded or non-threaded Tcl core, but a non-threaded extension may # only run in a non-threaded Tcl core. # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --enable-threads # # Sets the following vars: # THREADS_LIBS Thread library(s) # # Defines the following vars: # TCL_THREADS # _REENTRANT # _THREAD_SAFE #------------------------------------------------------------------------ AC_DEFUN([TEA_ENABLE_THREADS], [ AC_ARG_ENABLE(threads, AC_HELP_STRING([--enable-threads], [build with threads]), [tcl_ok=$enableval], [tcl_ok=yes]) if test "${enable_threads+set}" = set; then enableval="$enable_threads" tcl_ok=$enableval else tcl_ok=yes fi if test "$tcl_ok" = "yes" -o "${TCL_THREADS}" = 1; then TCL_THREADS=1 if test "${TEA_PLATFORM}" != "windows" ; then # We are always OK on Windows, so check what this platform wants: # USE_THREAD_ALLOC tells us to try the special thread-based # allocator that significantly reduces lock contention AC_DEFINE(USE_THREAD_ALLOC, 1, [Do we want to use the threaded memory allocator?]) AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?]) if test "`uname -s`" = "SunOS" ; then AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Do we really want to follow the standard? Yes we do!]) fi AC_DEFINE(_THREAD_SAFE, 1, [Do we want the thread-safe OS API?]) AC_CHECK_LIB(pthread,pthread_mutex_init,tcl_ok=yes,tcl_ok=no) if test "$tcl_ok" = "no"; then # Check a little harder for __pthread_mutex_init in the same # library, as some systems hide it there until pthread.h is # defined. We could alternatively do an AC_TRY_COMPILE with # pthread.h, but that will work with libpthread really doesn't # exist, like AIX 4.2. [Bug: 4359] AC_CHECK_LIB(pthread, __pthread_mutex_init, tcl_ok=yes, tcl_ok=no) fi if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -lpthread" else AC_CHECK_LIB(pthreads, pthread_mutex_init, tcl_ok=yes, tcl_ok=no) if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -lpthreads" else AC_CHECK_LIB(c, pthread_mutex_init, tcl_ok=yes, tcl_ok=no) if test "$tcl_ok" = "no"; then AC_CHECK_LIB(c_r, pthread_mutex_init, tcl_ok=yes, tcl_ok=no) if test "$tcl_ok" = "yes"; then # The space is needed THREADS_LIBS=" -pthread" else TCL_THREADS=0 AC_MSG_WARN([Do not know how to find pthread lib on your system - thread support disabled]) fi fi fi fi fi else TCL_THREADS=0 fi # Do checking message here to not mess up interleaved configure output AC_MSG_CHECKING([for building with threads]) if test "${TCL_THREADS}" = 1; then AC_DEFINE(TCL_THREADS, 1, [Are we building with threads enabled?]) AC_MSG_RESULT([yes (default)]) else AC_MSG_RESULT([no]) fi # TCL_THREADS sanity checking. See if our request for building with # threads is the same as the way Tcl was built. If not, warn the user. case ${TCL_DEFS} in *THREADS=1*) if test "${TCL_THREADS}" = "0"; then AC_MSG_WARN([ Building ${PACKAGE_NAME} without threads enabled, but building against Tcl that IS thread-enabled. It is recommended to use --enable-threads.]) fi ;; *) if test "${TCL_THREADS}" = "1"; then AC_MSG_WARN([ --enable-threads requested, but building against a Tcl that is NOT thread-enabled. This is an OK configuration that will also run in a thread-enabled core.]) fi ;; esac AC_SUBST(TCL_THREADS) ]) #------------------------------------------------------------------------ # TEA_ENABLE_SYMBOLS -- # # Specify if debugging symbols should be used. # Memory (TCL_MEM_DEBUG) debugging can also be enabled. # # Arguments: # none # # TEA varies from core Tcl in that C|LDFLAGS_DEFAULT receives # the value of C|LDFLAGS_OPTIMIZE|DEBUG already substituted. # Requires the following vars to be set in the Makefile: # CFLAGS_DEFAULT # LDFLAGS_DEFAULT # # Results: # # Adds the following arguments to configure: # --enable-symbols # # Defines the following vars: # CFLAGS_DEFAULT Sets to $(CFLAGS_DEBUG) if true # Sets to "$(CFLAGS_OPTIMIZE) -DNDEBUG" if false # LDFLAGS_DEFAULT Sets to $(LDFLAGS_DEBUG) if true # Sets to $(LDFLAGS_OPTIMIZE) if false # DBGX Formerly used as debug library extension; # always blank now. #------------------------------------------------------------------------ AC_DEFUN([TEA_ENABLE_SYMBOLS], [ dnl TEA specific: Make sure we are initialized AC_REQUIRE([TEA_CONFIG_CFLAGS]) AC_MSG_CHECKING([for build with symbols]) AC_ARG_ENABLE(symbols, AC_HELP_STRING([--enable-symbols], [build with debugging symbols (default: off)]), [tcl_ok=$enableval], [tcl_ok=no]) DBGX="" if test "$tcl_ok" = "no"; then CFLAGS_DEFAULT="${CFLAGS_OPTIMIZE} -DNDEBUG" LDFLAGS_DEFAULT="${LDFLAGS_OPTIMIZE}" AC_MSG_RESULT([no]) else CFLAGS_DEFAULT="${CFLAGS_DEBUG}" LDFLAGS_DEFAULT="${LDFLAGS_DEBUG}" if test "$tcl_ok" = "yes"; then AC_MSG_RESULT([yes (standard debugging)]) fi fi # TEA specific: if test "${TEA_PLATFORM}" != "windows" ; then LDFLAGS_DEFAULT="${LDFLAGS}" fi AC_SUBST(CFLAGS_DEFAULT) AC_SUBST(LDFLAGS_DEFAULT) AC_SUBST(TCL_DBGX) if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then AC_DEFINE(TCL_MEM_DEBUG, 1, [Is memory debugging enabled?]) fi if test "$tcl_ok" != "yes" -a "$tcl_ok" != "no"; then if test "$tcl_ok" = "all"; then AC_MSG_RESULT([enabled symbols mem debugging]) else AC_MSG_RESULT([enabled $tcl_ok debugging]) fi fi ]) #------------------------------------------------------------------------ # TEA_ENABLE_LANGINFO -- # # Allows use of modern nl_langinfo check for better l10n. # This is only relevant for Unix. # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --enable-langinfo=yes|no (default is yes) # # Defines the following vars: # HAVE_LANGINFO Triggers use of nl_langinfo if defined. #------------------------------------------------------------------------ AC_DEFUN([TEA_ENABLE_LANGINFO], [ AC_ARG_ENABLE(langinfo, AC_HELP_STRING([--enable-langinfo], [use nl_langinfo if possible to determine encoding at startup, otherwise use old heuristic (default: on)]), [langinfo_ok=$enableval], [langinfo_ok=yes]) HAVE_LANGINFO=0 if test "$langinfo_ok" = "yes"; then AC_CHECK_HEADER(langinfo.h,[langinfo_ok=yes],[langinfo_ok=no]) fi AC_MSG_CHECKING([whether to use nl_langinfo]) if test "$langinfo_ok" = "yes"; then AC_CACHE_VAL(tcl_cv_langinfo_h, [ AC_TRY_COMPILE([#include ], [nl_langinfo(CODESET);], [tcl_cv_langinfo_h=yes],[tcl_cv_langinfo_h=no])]) AC_MSG_RESULT([$tcl_cv_langinfo_h]) if test $tcl_cv_langinfo_h = yes; then AC_DEFINE(HAVE_LANGINFO, 1, [Do we have nl_langinfo()?]) fi else AC_MSG_RESULT([$langinfo_ok]) fi ]) #-------------------------------------------------------------------- # TEA_CONFIG_SYSTEM # # Determine what the system is (some things cannot be easily checked # on a feature-driven basis, alas). This can usually be done via the # "uname" command. # # Arguments: # none # # Results: # Defines the following var: # # system - System/platform/version identification code. #-------------------------------------------------------------------- AC_DEFUN([TEA_CONFIG_SYSTEM], [ AC_CACHE_CHECK([system version], tcl_cv_sys_version, [ # TEA specific: if test "${TEA_PLATFORM}" = "windows" ; then tcl_cv_sys_version=windows else tcl_cv_sys_version=`uname -s`-`uname -r` if test "$?" -ne 0 ; then AC_MSG_WARN([can't find uname command]) tcl_cv_sys_version=unknown else if test "`uname -s`" = "AIX" ; then tcl_cv_sys_version=AIX-`uname -v`.`uname -r` fi fi fi ]) system=$tcl_cv_sys_version ]) #-------------------------------------------------------------------- # TEA_CONFIG_CFLAGS # # Try to determine the proper flags to pass to the compiler # for building shared libraries and other such nonsense. # # Arguments: # none # # Results: # # Defines and substitutes the following vars: # # DL_OBJS, DL_LIBS - removed for TEA, only needed by core. # LDFLAGS - Flags to pass to the compiler when linking object # files into an executable application binary such # as tclsh. # LD_SEARCH_FLAGS-Flags to pass to ld, such as "-R /usr/local/tcl/lib", # that tell the run-time dynamic linker where to look # for shared libraries such as libtcl.so. Depends on # the variable LIB_RUNTIME_DIR in the Makefile. Could # be the same as CC_SEARCH_FLAGS if ${CC} is used to link. # CC_SEARCH_FLAGS-Flags to pass to ${CC}, such as "-Wl,-rpath,/usr/local/tcl/lib", # that tell the run-time dynamic linker where to look # for shared libraries such as libtcl.so. Depends on # the variable LIB_RUNTIME_DIR in the Makefile. # SHLIB_CFLAGS - Flags to pass to cc when compiling the components # of a shared library (may request position-independent # code, among other things). # SHLIB_LD - Base command to use for combining object files # into a shared library. # SHLIB_LD_LIBS - Dependent libraries for the linker to scan when # creating shared libraries. This symbol typically # goes at the end of the "ld" commands that build # shared libraries. The value of the symbol defaults to # "${LIBS}" if all of the dependent libraries should # be specified when creating a shared library. If # dependent libraries should not be specified (as on # SunOS 4.x, where they cause the link to fail, or in # general if Tcl and Tk aren't themselves shared # libraries), then this symbol has an empty string # as its value. # SHLIB_SUFFIX - Suffix to use for the names of dynamically loadable # extensions. An empty string means we don't know how # to use shared libraries on this platform. # LIB_SUFFIX - Specifies everything that comes after the "libfoo" # in a static or shared library name, using the $PACKAGE_VERSION variable # to put the version in the right place. This is used # by platforms that need non-standard library names. # Examples: ${PACKAGE_VERSION}.so.1.1 on NetBSD, since it needs # to have a version after the .so, and ${PACKAGE_VERSION}.a # on AIX, since a shared library needs to have # a .a extension whereas shared objects for loadable # extensions have a .so extension. Defaults to # ${PACKAGE_VERSION}${SHLIB_SUFFIX}. # CFLAGS_DEBUG - # Flags used when running the compiler in debug mode # CFLAGS_OPTIMIZE - # Flags used when running the compiler in optimize mode # CFLAGS - Additional CFLAGS added as necessary (usually 64-bit) #-------------------------------------------------------------------- AC_DEFUN([TEA_CONFIG_CFLAGS], [ dnl TEA specific: Make sure we are initialized AC_REQUIRE([TEA_INIT]) # Step 0.a: Enable 64 bit support? AC_MSG_CHECKING([if 64bit support is requested]) AC_ARG_ENABLE(64bit, AC_HELP_STRING([--enable-64bit], [enable 64bit support (default: off)]), [do64bit=$enableval], [do64bit=no]) AC_MSG_RESULT([$do64bit]) # Step 0.b: Enable Solaris 64 bit VIS support? AC_MSG_CHECKING([if 64bit Sparc VIS support is requested]) AC_ARG_ENABLE(64bit-vis, AC_HELP_STRING([--enable-64bit-vis], [enable 64bit Sparc VIS support (default: off)]), [do64bitVIS=$enableval], [do64bitVIS=no]) AC_MSG_RESULT([$do64bitVIS]) # Force 64bit on with VIS AS_IF([test "$do64bitVIS" = "yes"], [do64bit=yes]) # Step 0.c: Check if visibility support is available. Do this here so # that platform specific alternatives can be used below if this fails. AC_CACHE_CHECK([if compiler supports visibility "hidden"], tcl_cv_cc_visibility_hidden, [ hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -Werror" AC_TRY_LINK([ extern __attribute__((__visibility__("hidden"))) void f(void); void f(void) {}], [f();], tcl_cv_cc_visibility_hidden=yes, tcl_cv_cc_visibility_hidden=no) CFLAGS=$hold_cflags]) AS_IF([test $tcl_cv_cc_visibility_hidden = yes], [ AC_DEFINE(MODULE_SCOPE, [extern __attribute__((__visibility__("hidden")))], [Compiler support for module scope symbols]) AC_DEFINE(HAVE_HIDDEN, [1], [Compiler support for module scope symbols]) ]) # Step 0.d: Disable -rpath support? AC_MSG_CHECKING([if rpath support is requested]) AC_ARG_ENABLE(rpath, AC_HELP_STRING([--disable-rpath], [disable rpath support (default: on)]), [doRpath=$enableval], [doRpath=yes]) AC_MSG_RESULT([$doRpath]) # TEA specific: Cross-compiling options for Windows/CE builds? AS_IF([test "${TEA_PLATFORM}" = windows], [ AC_MSG_CHECKING([if Windows/CE build is requested]) AC_ARG_ENABLE(wince, AC_HELP_STRING([--enable-wince], [enable Win/CE support (where applicable)]), [doWince=$enableval], [doWince=no]) AC_MSG_RESULT([$doWince]) ]) # Set the variable "system" to hold the name and version number # for the system. TEA_CONFIG_SYSTEM # Require ranlib early so we can override it in special cases below. AC_REQUIRE([AC_PROG_RANLIB]) # Set configuration options based on system name and version. # This is similar to Tcl's unix/tcl.m4 except that we've added a # "windows" case and removed some core-only vars. do64bit_ok=no # default to '{$LIBS}' and set to "" on per-platform necessary basis SHLIB_LD_LIBS='${LIBS}' # When ld needs options to work in 64-bit mode, put them in # LDFLAGS_ARCH so they eventually end up in LDFLAGS even if [load] # is disabled by the user. [Bug 1016796] LDFLAGS_ARCH="" UNSHARED_LIB_SUFFIX="" # TEA specific: use PACKAGE_VERSION instead of VERSION TCL_TRIM_DOTS='`echo ${PACKAGE_VERSION} | tr -d .`' ECHO_VERSION='`echo ${PACKAGE_VERSION}`' TCL_LIB_VERSIONS_OK=ok CFLAGS_DEBUG=-g AS_IF([test "$GCC" = yes], [ CFLAGS_OPTIMIZE=-O2 CFLAGS_WARNING="-Wall" ], [ CFLAGS_OPTIMIZE=-O CFLAGS_WARNING="" ]) AC_CHECK_TOOL(AR, ar) STLIB_LD='${AR} cr' LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH" AS_IF([test "x$SHLIB_VERSION" = x],[SHLIB_VERSION="1.0"]) case $system in # TEA specific: windows) # This is a 2-stage check to make sure we have the 64-bit SDK # We have to know where the SDK is installed. # This magic is based on MS Platform SDK for Win2003 SP1 - hobbs # MACHINE is IX86 for LINK, but this is used by the manifest, # which requires x86|amd64|ia64. MACHINE="X86" if test "$do64bit" != "no" ; then if test "x${MSSDK}x" = "xx" ; then MSSDK="C:/Progra~1/Microsoft Platform SDK" fi MSSDK=`echo "$MSSDK" | sed -e 's!\\\!/!g'` PATH64="" case "$do64bit" in amd64|x64|yes) MACHINE="AMD64" ; # default to AMD64 64-bit build PATH64="${MSSDK}/Bin/Win64/x86/AMD64" ;; ia64) MACHINE="IA64" PATH64="${MSSDK}/Bin/Win64" ;; esac if test "$GCC" != "yes" -a ! -d "${PATH64}" ; then AC_MSG_WARN([Could not find 64-bit $MACHINE SDK to enable 64bit mode]) AC_MSG_WARN([Ensure latest Platform SDK is installed]) do64bit="no" else AC_MSG_RESULT([ Using 64-bit $MACHINE mode]) do64bit_ok="yes" fi fi if test "$doWince" != "no" ; then if test "$do64bit" != "no" ; then AC_MSG_ERROR([Windows/CE and 64-bit builds incompatible]) fi if test "$GCC" = "yes" ; then AC_MSG_ERROR([Windows/CE and GCC builds incompatible]) fi TEA_PATH_CELIB # Set defaults for common evc4/PPC2003 setup # Currently Tcl requires 300+, possibly 420+ for sockets CEVERSION=420; # could be 211 300 301 400 420 ... TARGETCPU=ARMV4; # could be ARMV4 ARM MIPS SH3 X86 ... ARCH=ARM; # could be ARM MIPS X86EM ... PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002" if test "$doWince" != "yes"; then # If !yes then the user specified something # Reset ARCH to allow user to skip specifying it ARCH= eval `echo $doWince | awk -F, '{ \ if (length([$]1)) { printf "CEVERSION=\"%s\"\n", [$]1; \ if ([$]1 < 400) { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \ if (length([$]2)) { printf "TARGETCPU=\"%s\"\n", toupper([$]2) }; \ if (length([$]3)) { printf "ARCH=\"%s\"\n", toupper([$]3) }; \ if (length([$]4)) { printf "PLATFORM=\"%s\"\n", [$]4 }; \ }'` if test "x${ARCH}" = "x" ; then ARCH=$TARGETCPU; fi fi OSVERSION=WCE$CEVERSION; if test "x${WCEROOT}" = "x" ; then WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0" if test ! -d "${WCEROOT}" ; then WCEROOT="C:/Program Files/Microsoft eMbedded Tools" fi fi if test "x${SDKROOT}" = "x" ; then SDKROOT="C:/Program Files/Windows CE Tools" if test ! -d "${SDKROOT}" ; then SDKROOT="C:/Windows CE Tools" fi fi WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'` SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'` if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" \ -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then AC_MSG_ERROR([could not find PocketPC SDK or target compiler to enable WinCE mode [$CEVERSION,$TARGETCPU,$ARCH,$PLATFORM]]) doWince="no" else # We could PATH_NOSPACE these, but that's not important, # as long as we quote them when used. CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include" if test -d "${CEINCLUDE}/${TARGETCPU}" ; then CEINCLUDE="${CEINCLUDE}/${TARGETCPU}" fi CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" fi fi if test "$GCC" != "yes" ; then if test "${SHARED_BUILD}" = "0" ; then runtime=-MT else runtime=-MD fi if test "$do64bit" != "no" ; then # All this magic is necessary for the Win64 SDK RC1 - hobbs CC="\"${PATH64}/cl.exe\"" CFLAGS="${CFLAGS} -I\"${MSSDK}/Include\" -I\"${MSSDK}/Include/crt\" -I\"${MSSDK}/Include/crt/sys\"" RC="\"${MSSDK}/bin/rc.exe\"" lflags="-nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\"" LINKBIN="\"${PATH64}/link.exe\"" CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d" CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}" # Avoid 'unresolved external symbol __security_cookie' # errors, c.f. http://support.microsoft.com/?id=894573 TEA_ADD_LIBS([bufferoverflowU.lib]) elif test "$doWince" != "no" ; then CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin" if test "${TARGETCPU}" = "X86"; then CC="\"${CEBINROOT}/cl.exe\"" else CC="\"${CEBINROOT}/cl${ARCH}.exe\"" fi CFLAGS="$CFLAGS -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\"" RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\"" arch=`echo ${ARCH} | awk '{print tolower([$]0)}'` defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _WINDOWS" if test "${SHARED_BUILD}" = "1" ; then # Static CE builds require static celib as well defs="${defs} _DLL" fi for i in $defs ; do AC_DEFINE_UNQUOTED($i, 1, [WinCE def ]$i) done AC_DEFINE_UNQUOTED(_WIN32_WCE, $CEVERSION, [_WIN32_WCE version]) AC_DEFINE_UNQUOTED(UNDER_CE, $CEVERSION, [UNDER_CE version]) CFLAGS_DEBUG="-nologo -Zi -Od" CFLAGS_OPTIMIZE="-nologo -Ox" lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'` lflags="-MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo" LINKBIN="\"${CEBINROOT}/link.exe\"" AC_SUBST(CELIB_DIR) else RC="rc" lflags="-nologo" LINKBIN="link" CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d" CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}" fi fi if test "$GCC" = "yes"; then # mingw gcc mode AC_CHECK_TOOL(RC, windres) CFLAGS_DEBUG="-g" CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer" SHLIB_LD='${CC} -shared' UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}" LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}" AC_CACHE_CHECK(for cross-compile version of gcc, ac_cv_cross, AC_TRY_COMPILE([ #ifdef _WIN32 #error cross-compiler #endif ], [], ac_cv_cross=yes, ac_cv_cross=no) ) if test "$ac_cv_cross" = "yes"; then case "$do64bit" in amd64|x64|yes) CC="x86_64-w64-mingw32-gcc" LD="x86_64-w64-mingw32-ld" AR="x86_64-w64-mingw32-ar" RANLIB="x86_64-w64-mingw32-ranlib" RC="x86_64-w64-mingw32-windres" ;; *) CC="i686-w64-mingw32-gcc" LD="i686-w64-mingw32-ld" AR="i686-w64-mingw32-ar" RANLIB="i686-w64-mingw32-ranlib" RC="i686-w64-mingw32-windres" ;; esac fi else SHLIB_LD="${LINKBIN} -dll ${lflags}" # link -lib only works when -lib is the first arg STLIB_LD="${LINKBIN} -lib ${lflags}" UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.lib' PATHTYPE=-w # For information on what debugtype is most useful, see: # http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp # and also # http://msdn2.microsoft.com/en-us/library/y0zzbyt4%28VS.80%29.aspx # This essentially turns it all on. LDFLAGS_DEBUG="-debug -debugtype:cv" LDFLAGS_OPTIMIZE="-release" if test "$doWince" != "no" ; then LDFLAGS_CONSOLE="-link ${lflags}" LDFLAGS_WINDOW=${LDFLAGS_CONSOLE} else LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}" LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}" fi fi SHLIB_SUFFIX=".dll" SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.dll' TCL_LIB_VERSIONS_OK=nodots ;; AIX-*) AS_IF([test "${TCL_THREADS}" = "1" -a "$GCC" != "yes"], [ # AIX requires the _r compiler when gcc isn't being used case "${CC}" in *_r|*_r\ *) # ok ... ;; *) # Make sure only first arg gets _r CC=`echo "$CC" | sed -e 's/^\([[^ ]]*\)/\1_r/'` ;; esac AC_MSG_RESULT([Using $CC for compiling with threads]) ]) LIBS="$LIBS -lc" SHLIB_CFLAGS="" SHLIB_SUFFIX=".so" LD_LIBRARY_PATH_VAR="LIBPATH" # Check to enable 64-bit flags for compiler/linker AS_IF([test "$do64bit" = yes], [ AS_IF([test "$GCC" = yes], [ AC_MSG_WARN([64bit mode not supported with GCC on $system]) ], [ do64bit_ok=yes CFLAGS="$CFLAGS -q64" LDFLAGS_ARCH="-q64" RANLIB="${RANLIB} -X64" AR="${AR} -X64" SHLIB_LD_FLAGS="-b64" ]) ]) AS_IF([test "`uname -m`" = ia64], [ # AIX-5 uses ELF style dynamic libraries on IA-64, but not PPC SHLIB_LD="/usr/ccs/bin/ld -G -z text" AS_IF([test "$GCC" = yes], [ CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' ], [ CC_SEARCH_FLAGS='-R${LIB_RUNTIME_DIR}' ]) LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' ], [ AS_IF([test "$GCC" = yes], [ SHLIB_LD='${CC} -shared -Wl,-bexpall' ], [ SHLIB_LD="/bin/ld -bhalt:4 -bM:SRE -bexpall -H512 -T512 -bnoentry" LDFLAGS="$LDFLAGS -brtl" ]) SHLIB_LD="${SHLIB_LD} ${SHLIB_LD_FLAGS}" CC_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ]) ;; BeOS*) SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -nostart' SHLIB_SUFFIX=".so" #----------------------------------------------------------- # Check for inet_ntoa in -lbind, for BeOS (which also needs # -lsocket, even if the network functions are in -lnet which # is always linked to, for compatibility. #----------------------------------------------------------- AC_CHECK_LIB(bind, inet_ntoa, [LIBS="$LIBS -lbind -lsocket"]) ;; BSD/OS-4.*) SHLIB_CFLAGS="-export-dynamic -fPIC" SHLIB_LD='${CC} -shared' SHLIB_SUFFIX=".so" LDFLAGS="$LDFLAGS -export-dynamic" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; CYGWIN_*) SHLIB_CFLAGS="" SHLIB_LD='${CC} -shared' SHLIB_SUFFIX=".dll" EXEEXT=".exe" do64bit_ok=yes CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; Haiku*) LDFLAGS="$LDFLAGS -Wl,--export-dynamic" SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS}' AC_CHECK_LIB(network, inet_ntoa, [LIBS="$LIBS -lnetwork"]) ;; HP-UX-*.11.*) # Use updated header definitions where possible AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, [Do we want to use the XOPEN network library?]) # TEA specific: Needed by Tcl, but not most extensions #AC_DEFINE(_XOPEN_SOURCE, 1, [Do we want to use the XOPEN network library?]) #LIBS="$LIBS -lxnet" # Use the XOPEN network library AS_IF([test "`uname -m`" = ia64], [ SHLIB_SUFFIX=".so" # Use newer C++ library for C++ extensions #if test "$GCC" != "yes" ; then # CPPFLAGS="-AA" #fi ], [ SHLIB_SUFFIX=".sl" ]) AC_CHECK_LIB(dld, shl_load, tcl_ok=yes, tcl_ok=no) AS_IF([test "$tcl_ok" = yes], [ LDFLAGS="$LDFLAGS -Wl,-E" CC_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.' LD_SEARCH_FLAGS='+s +b ${LIB_RUNTIME_DIR}:.' LD_LIBRARY_PATH_VAR="SHLIB_PATH" ]) AS_IF([test "$GCC" = yes], [ SHLIB_LD='${CC} -shared' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ], [ CFLAGS="$CFLAGS -z" # Users may want PA-RISC 1.1/2.0 portable code - needs HP cc #CFLAGS="$CFLAGS +DAportable" SHLIB_CFLAGS="+z" SHLIB_LD="ld -b" ]) # Check to enable 64-bit flags for compiler/linker AS_IF([test "$do64bit" = "yes"], [ AS_IF([test "$GCC" = yes], [ case `${CC} -dumpmachine` in hppa64*) # 64-bit gcc in use. Fix flags for GNU ld. do64bit_ok=yes SHLIB_LD='${CC} -shared' AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ;; *) AC_MSG_WARN([64bit mode not supported with GCC on $system]) ;; esac ], [ do64bit_ok=yes CFLAGS="$CFLAGS +DD64" LDFLAGS_ARCH="+DD64" ]) ]) ;; IRIX-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -n32 -shared -rdata_shared" SHLIB_SUFFIX=".so" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}']) AS_IF([test "$GCC" = yes], [ CFLAGS="$CFLAGS -mabi=n32" LDFLAGS="$LDFLAGS -mabi=n32" ], [ case $system in IRIX-6.3) # Use to build 6.2 compatible binaries on 6.3. CFLAGS="$CFLAGS -n32 -D_OLD_TERMIOS" ;; *) CFLAGS="$CFLAGS -n32" ;; esac LDFLAGS="$LDFLAGS -n32" ]) ;; IRIX64-6.*) SHLIB_CFLAGS="" SHLIB_LD="ld -n32 -shared -rdata_shared" SHLIB_SUFFIX=".so" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}']) # Check to enable 64-bit flags for compiler/linker AS_IF([test "$do64bit" = yes], [ AS_IF([test "$GCC" = yes], [ AC_MSG_WARN([64bit mode not supported by gcc]) ], [ do64bit_ok=yes SHLIB_LD="ld -64 -shared -rdata_shared" CFLAGS="$CFLAGS -64" LDFLAGS_ARCH="-64" ]) ]) ;; Linux*|GNU*|NetBSD-Debian) SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" # TEA specific: CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer" # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS_DEFAULT}' LDFLAGS="$LDFLAGS -Wl,--export-dynamic" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} AS_IF([test "`uname -m`" = "alpha"], [CFLAGS="$CFLAGS -mieee"]) AS_IF([test $do64bit = yes], [ AC_CACHE_CHECK([if compiler accepts -m64 flag], tcl_cv_cc_m64, [ hold_cflags=$CFLAGS CFLAGS="$CFLAGS -m64" AC_TRY_LINK(,, tcl_cv_cc_m64=yes, tcl_cv_cc_m64=no) CFLAGS=$hold_cflags]) AS_IF([test $tcl_cv_cc_m64 = yes], [ CFLAGS="$CFLAGS -m64" do64bit_ok=yes ]) ]) # The combo of gcc + glibc has a bug related to inlining of # functions like strtod(). The -fno-builtin flag should address # this problem but it does not work. The -fno-inline flag is kind # of overkill but it works. Disable inlining only when one of the # files in compat/*.c is being linked in. AS_IF([test x"${USE_COMPAT}" != x],[CFLAGS="$CFLAGS -fno-inline"]) ;; Lynx*) SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" CFLAGS_OPTIMIZE=-02 SHLIB_LD='${CC} -shared' LD_FLAGS="-Wl,--export-dynamic" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) ;; OpenBSD-*) arch=`arch -s` case "$arch" in vax) SHLIB_SUFFIX="" SHARED_LIB_SUFFIX="" LDFLAGS="" ;; *) SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}' SHLIB_SUFFIX=".so" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.${SHLIB_VERSION}' LDFLAGS="-Wl,-export-dynamic" ;; esac case "$arch" in vax) CFLAGS_OPTIMIZE="-O1" ;; *) CFLAGS_OPTIMIZE="-O2" ;; esac AS_IF([test "${TCL_THREADS}" = "1"], [ # On OpenBSD: Compile with -pthread # Don't link with -lpthread LIBS=`echo $LIBS | sed s/-lpthread//` CFLAGS="$CFLAGS -pthread" ]) # OpenBSD doesn't do version numbers with dots. UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' TCL_LIB_VERSIONS_OK=nodots ;; NetBSD-*) # NetBSD has ELF and can use 'cc -shared' to build shared libs SHLIB_CFLAGS="-fPIC" SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}' SHLIB_SUFFIX=".so" LDFLAGS="$LDFLAGS -export-dynamic" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} AS_IF([test "${TCL_THREADS}" = "1"], [ # The -pthread needs to go in the CFLAGS, not LIBS LIBS=`echo $LIBS | sed s/-pthread//` CFLAGS="$CFLAGS -pthread" LDFLAGS="$LDFLAGS -pthread" ]) ;; FreeBSD-*) # This configuration from FreeBSD Ports. SHLIB_CFLAGS="-fPIC" SHLIB_LD="${CC} -shared" TCL_SHLIB_LD_EXTRAS="-Wl,-soname=\$[@]" TK_SHLIB_LD_EXTRAS="-Wl,-soname,\$[@]" SHLIB_SUFFIX=".so" LDFLAGS="" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}']) AS_IF([test "${TCL_THREADS}" = "1"], [ # The -pthread needs to go in the LDFLAGS, not LIBS LIBS=`echo $LIBS | sed s/-pthread//` CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LDFLAGS="$LDFLAGS $PTHREAD_LIBS"]) case $system in FreeBSD-3.*) # Version numbers are dot-stripped by system policy. TCL_TRIM_DOTS=`echo ${VERSION} | tr -d .` UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a' SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so' TCL_LIB_VERSIONS_OK=nodots ;; esac ;; Darwin-*) CFLAGS_OPTIMIZE="-Os" SHLIB_CFLAGS="-fno-common" # To avoid discrepancies between what headers configure sees during # preprocessing tests and compiling tests, move any -isysroot and # -mmacosx-version-min flags from CFLAGS to CPPFLAGS: CPPFLAGS="${CPPFLAGS} `echo " ${CFLAGS}" | \ awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \ if ([$]i~/^(isysroot|mmacosx-version-min)/) print "-"[$]i}'`" CFLAGS="`echo " ${CFLAGS}" | \ awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \ if (!([$]i~/^(isysroot|mmacosx-version-min)/)) print "-"[$]i}'`" AS_IF([test $do64bit = yes], [ case `arch` in ppc) AC_CACHE_CHECK([if compiler accepts -arch ppc64 flag], tcl_cv_cc_arch_ppc64, [ hold_cflags=$CFLAGS CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5" AC_TRY_LINK(,, tcl_cv_cc_arch_ppc64=yes, tcl_cv_cc_arch_ppc64=no) CFLAGS=$hold_cflags]) AS_IF([test $tcl_cv_cc_arch_ppc64 = yes], [ CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5" do64bit_ok=yes ]);; i386) AC_CACHE_CHECK([if compiler accepts -arch x86_64 flag], tcl_cv_cc_arch_x86_64, [ hold_cflags=$CFLAGS CFLAGS="$CFLAGS -arch x86_64" AC_TRY_LINK(,, tcl_cv_cc_arch_x86_64=yes, tcl_cv_cc_arch_x86_64=no) CFLAGS=$hold_cflags]) AS_IF([test $tcl_cv_cc_arch_x86_64 = yes], [ CFLAGS="$CFLAGS -arch x86_64" do64bit_ok=yes ]);; *) AC_MSG_WARN([Don't know how enable 64-bit on architecture `arch`]);; esac ], [ # Check for combined 32-bit and 64-bit fat build AS_IF([echo "$CFLAGS " |grep -E -q -- '-arch (ppc64|x86_64) ' \ && echo "$CFLAGS " |grep -E -q -- '-arch (ppc|i386) '], [ fat_32_64=yes]) ]) # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -dynamiclib ${CFLAGS} ${LDFLAGS_DEFAULT}' AC_CACHE_CHECK([if ld accepts -single_module flag], tcl_cv_ld_single_module, [ hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -dynamiclib -Wl,-single_module" AC_TRY_LINK(, [int i;], tcl_cv_ld_single_module=yes, tcl_cv_ld_single_module=no) LDFLAGS=$hold_ldflags]) AS_IF([test $tcl_cv_ld_single_module = yes], [ SHLIB_LD="${SHLIB_LD} -Wl,-single_module" ]) # TEA specific: link shlib with current and compatibility version flags vers=`echo ${PACKAGE_VERSION} | sed -e 's/^\([[0-9]]\{1,5\}\)\(\(\.[[0-9]]\{1,3\}\)\{0,2\}\).*$/\1\2/p' -e d` SHLIB_LD="${SHLIB_LD} -current_version ${vers:-0} -compatibility_version ${vers:-0}" SHLIB_SUFFIX=".dylib" # Don't use -prebind when building for Mac OS X 10.4 or later only: AS_IF([test "`echo "${MACOSX_DEPLOYMENT_TARGET}" | awk -F '10\\.' '{print int([$]2)}'`" -lt 4 -a \ "`echo "${CPPFLAGS}" | awk -F '-mmacosx-version-min=10\\.' '{print int([$]2)}'`" -lt 4], [ LDFLAGS="$LDFLAGS -prebind"]) LDFLAGS="$LDFLAGS -headerpad_max_install_names" AC_CACHE_CHECK([if ld accepts -search_paths_first flag], tcl_cv_ld_search_paths_first, [ hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-search_paths_first" AC_TRY_LINK(, [int i;], tcl_cv_ld_search_paths_first=yes, tcl_cv_ld_search_paths_first=no) LDFLAGS=$hold_ldflags]) AS_IF([test $tcl_cv_ld_search_paths_first = yes], [ LDFLAGS="$LDFLAGS -Wl,-search_paths_first" ]) AS_IF([test "$tcl_cv_cc_visibility_hidden" != yes], [ AC_DEFINE(MODULE_SCOPE, [__private_extern__], [Compiler support for module scope symbols]) tcl_cv_cc_visibility_hidden=yes ]) CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" LD_LIBRARY_PATH_VAR="DYLD_LIBRARY_PATH" # TEA specific: for combined 32 & 64 bit fat builds of Tk # extensions, verify that 64-bit build is possible. AS_IF([test "$fat_32_64" = yes && test -n "${TK_BIN_DIR}"], [ AS_IF([test "${TEA_WINDOWINGSYSTEM}" = x11], [ AC_CACHE_CHECK([for 64-bit X11], tcl_cv_lib_x11_64, [ for v in CFLAGS CPPFLAGS LDFLAGS; do eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"' done CPPFLAGS="$CPPFLAGS -I/usr/X11R6/include" LDFLAGS="$LDFLAGS -L/usr/X11R6/lib -lX11" AC_TRY_LINK([#include ], [XrmInitialize();], tcl_cv_lib_x11_64=yes, tcl_cv_lib_x11_64=no) for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="$hold_'$v'"' done]) ]) AS_IF([test "${TEA_WINDOWINGSYSTEM}" = aqua], [ AC_CACHE_CHECK([for 64-bit Tk], tcl_cv_lib_tk_64, [ for v in CFLAGS CPPFLAGS LDFLAGS; do eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"' done CPPFLAGS="$CPPFLAGS -DUSE_TCL_STUBS=1 -DUSE_TK_STUBS=1 ${TCL_INCLUDES} ${TK_INCLUDES}" LDFLAGS="$LDFLAGS ${TCL_STUB_LIB_SPEC} ${TK_STUB_LIB_SPEC}" AC_TRY_LINK([#include ], [Tk_InitStubs(NULL, "", 0);], tcl_cv_lib_tk_64=yes, tcl_cv_lib_tk_64=no) for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="$hold_'$v'"' done]) ]) # remove 64-bit arch flags from CFLAGS et al. if configuration # does not support 64-bit. AS_IF([test "$tcl_cv_lib_tk_64" = no -o "$tcl_cv_lib_x11_64" = no], [ AC_MSG_NOTICE([Removing 64-bit architectures from compiler & linker flags]) for v in CFLAGS CPPFLAGS LDFLAGS; do eval $v'="`echo "$'$v' "|sed -e "s/-arch ppc64 / /g" -e "s/-arch x86_64 / /g"`"' done]) ]) ;; OS/390-*) CFLAGS_OPTIMIZE="" # Optimizer is buggy AC_DEFINE(_OE_SOCKETS, 1, # needed in sys/socket.h [Should OS/390 do the right thing with sockets?]) ;; OSF1-V*) # Digital OSF/1 SHLIB_CFLAGS="" AS_IF([test "$SHARED_BUILD" = 1], [ SHLIB_LD='ld -shared -expect_unresolved "*"' ], [ SHLIB_LD='ld -non_shared -expect_unresolved "*"' ]) SHLIB_SUFFIX=".so" AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}']) AS_IF([test "$GCC" = yes], [CFLAGS="$CFLAGS -mieee"], [ CFLAGS="$CFLAGS -DHAVE_TZSET -std1 -ieee"]) # see pthread_intro(3) for pthread support on osf1, k.furukawa AS_IF([test "${TCL_THREADS}" = 1], [ CFLAGS="$CFLAGS -DHAVE_PTHREAD_ATTR_SETSTACKSIZE" CFLAGS="$CFLAGS -DTCL_THREAD_STACK_MIN=PTHREAD_STACK_MIN*64" LIBS=`echo $LIBS | sed s/-lpthreads//` AS_IF([test "$GCC" = yes], [ LIBS="$LIBS -lpthread -lmach -lexc" ], [ CFLAGS="$CFLAGS -pthread" LDFLAGS="$LDFLAGS -pthread" ]) ]) ;; QNX-6*) # QNX RTP # This may work for all QNX, but it was only reported for v6. SHLIB_CFLAGS="-fPIC" SHLIB_LD="ld -Bshareable -x" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; SCO_SV-3.2*) AS_IF([test "$GCC" = yes], [ SHLIB_CFLAGS="-fPIC -melf" LDFLAGS="$LDFLAGS -melf -Wl,-Bexport" ], [ SHLIB_CFLAGS="-Kpic -belf" LDFLAGS="$LDFLAGS -belf -Wl,-Bexport" ]) SHLIB_LD="ld -G" SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; SunOS-5.[[0-6]]) # Careful to not let 5.10+ fall into this case # Note: If _REENTRANT isn't defined, then Solaris # won't define thread-safe library routines. AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?]) AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Do we really want to follow the standard? Yes we do!]) SHLIB_CFLAGS="-KPIC" SHLIB_SUFFIX=".so" AS_IF([test "$GCC" = yes], [ SHLIB_LD='${CC} -shared' CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ], [ SHLIB_LD="/usr/ccs/bin/ld -G -z text" CC_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} ]) ;; SunOS-5*) # Note: If _REENTRANT isn't defined, then Solaris # won't define thread-safe library routines. AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?]) AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Do we really want to follow the standard? Yes we do!]) SHLIB_CFLAGS="-KPIC" # Check to enable 64-bit flags for compiler/linker AS_IF([test "$do64bit" = yes], [ arch=`isainfo` AS_IF([test "$arch" = "sparcv9 sparc"], [ AS_IF([test "$GCC" = yes], [ AS_IF([test "`${CC} -dumpversion | awk -F. '{print [$]1}'`" -lt 3], [ AC_MSG_WARN([64bit mode not supported with GCC < 3.2 on $system]) ], [ do64bit_ok=yes CFLAGS="$CFLAGS -m64 -mcpu=v9" LDFLAGS="$LDFLAGS -m64 -mcpu=v9" SHLIB_CFLAGS="-fPIC" ]) ], [ do64bit_ok=yes AS_IF([test "$do64bitVIS" = yes], [ CFLAGS="$CFLAGS -xarch=v9a" LDFLAGS_ARCH="-xarch=v9a" ], [ CFLAGS="$CFLAGS -xarch=v9" LDFLAGS_ARCH="-xarch=v9" ]) # Solaris 64 uses this as well #LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH_64" ]) ], [AS_IF([test "$arch" = "amd64 i386"], [ AS_IF([test "$GCC" = yes], [ case $system in SunOS-5.1[[1-9]]*|SunOS-5.[[2-9]][[0-9]]*) do64bit_ok=yes CFLAGS="$CFLAGS -m64" LDFLAGS="$LDFLAGS -m64";; *) AC_MSG_WARN([64bit mode not supported with GCC on $system]);; esac ], [ do64bit_ok=yes case $system in SunOS-5.1[[1-9]]*|SunOS-5.[[2-9]][[0-9]]*) CFLAGS="$CFLAGS -m64" LDFLAGS="$LDFLAGS -m64";; *) CFLAGS="$CFLAGS -xarch=amd64" LDFLAGS="$LDFLAGS -xarch=amd64";; esac ]) ], [AC_MSG_WARN([64bit mode not supported for $arch])])]) ]) SHLIB_SUFFIX=".so" AS_IF([test "$GCC" = yes], [ SHLIB_LD='${CC} -shared' CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS} AS_IF([test "$do64bit_ok" = yes], [ AS_IF([test "$arch" = "sparcv9 sparc"], [ # We need to specify -static-libgcc or we need to # add the path to the sparv9 libgcc. # JH: static-libgcc is necessary for core Tcl, but may # not be necessary for extensions. SHLIB_LD="$SHLIB_LD -m64 -mcpu=v9 -static-libgcc" # for finding sparcv9 libgcc, get the regular libgcc # path, remove so name and append 'sparcv9' #v9gcclibdir="`gcc -print-file-name=libgcc_s.so` | ..." #CC_SEARCH_FLAGS="${CC_SEARCH_FLAGS},-R,$v9gcclibdir" ], [AS_IF([test "$arch" = "amd64 i386"], [ # JH: static-libgcc is necessary for core Tcl, but may # not be necessary for extensions. SHLIB_LD="$SHLIB_LD -m64 -static-libgcc" ])]) ]) ], [ case $system in SunOS-5.[[1-9]][[0-9]]*) # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS SHLIB_LD='${CC} -G -z text ${LDFLAGS_DEFAULT}';; *) SHLIB_LD='/usr/ccs/bin/ld -G -z text';; esac CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' ]) ;; UNIX_SV* | UnixWare-5*) SHLIB_CFLAGS="-KPIC" SHLIB_LD='${CC} -G' SHLIB_LD_LIBS="" SHLIB_SUFFIX=".so" # Some UNIX_SV* systems (unixware 1.1.2 for example) have linkers # that don't grok the -Bexport option. Test that it does. AC_CACHE_CHECK([for ld accepts -Bexport flag], tcl_cv_ld_Bexport, [ hold_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-Bexport" AC_TRY_LINK(, [int i;], tcl_cv_ld_Bexport=yes, tcl_cv_ld_Bexport=no) LDFLAGS=$hold_ldflags]) AS_IF([test $tcl_cv_ld_Bexport = yes], [ LDFLAGS="$LDFLAGS -Wl,-Bexport" ]) CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" ;; esac AS_IF([test "$do64bit" = yes -a "$do64bit_ok" = no], [ AC_MSG_WARN([64bit support being disabled -- don't know magic for this platform]) ]) dnl # Add any CPPFLAGS set in the environment to our CFLAGS, but delay doing so dnl # until the end of configure, as configure's compile and link tests use dnl # both CPPFLAGS and CFLAGS (unlike our compile and link) but configure's dnl # preprocessing tests use only CPPFLAGS. AC_CONFIG_COMMANDS_PRE([CFLAGS="${CFLAGS} ${CPPFLAGS}"; CPPFLAGS=""]) # Add in the arch flags late to ensure it wasn't removed. # Not necessary in TEA, but this is aligned with core LDFLAGS="$LDFLAGS $LDFLAGS_ARCH" # If we're running gcc, then change the C flags for compiling shared # libraries to the right flags for gcc, instead of those for the # standard manufacturer compiler. AS_IF([test "$GCC" = yes], [ case $system in AIX-*) ;; BSD/OS*) ;; CYGWIN_*|MINGW32_*) ;; IRIX*) ;; NetBSD-*|FreeBSD-*|OpenBSD-*) ;; Darwin-*) ;; SCO_SV-3.2*) ;; windows) ;; *) SHLIB_CFLAGS="-fPIC" ;; esac]) AS_IF([test "$tcl_cv_cc_visibility_hidden" != yes], [ AC_DEFINE(MODULE_SCOPE, [extern], [No Compiler support for module scope symbols]) ]) AS_IF([test "$SHARED_LIB_SUFFIX" = ""], [ # TEA specific: use PACKAGE_VERSION instead of VERSION SHARED_LIB_SUFFIX='${PACKAGE_VERSION}${SHLIB_SUFFIX}']) AS_IF([test "$UNSHARED_LIB_SUFFIX" = ""], [ # TEA specific: use PACKAGE_VERSION instead of VERSION UNSHARED_LIB_SUFFIX='${PACKAGE_VERSION}.a']) if test "${GCC}" = "yes" -a ${SHLIB_SUFFIX} = ".dll"; then AC_CACHE_CHECK(for SEH support in compiler, tcl_cv_seh, AC_TRY_RUN([ #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN int main(int argc, char** argv) { int a, b = 0; __try { a = 666 / b; } __except (EXCEPTION_EXECUTE_HANDLER) { return 0; } return 1; } ], tcl_cv_seh=yes, tcl_cv_seh=no, tcl_cv_seh=no) ) if test "$tcl_cv_seh" = "no" ; then AC_DEFINE(HAVE_NO_SEH, 1, [Defined when mingw does not support SEH]) fi # # Check to see if the excpt.h include file provided contains the # definition for EXCEPTION_DISPOSITION; if not, which is the case # with Cygwin's version as of 2002-04-10, define it to be int, # sufficient for getting the current code to work. # AC_CACHE_CHECK(for EXCEPTION_DISPOSITION support in include files, tcl_cv_eh_disposition, AC_TRY_COMPILE([ # define WIN32_LEAN_AND_MEAN # include # undef WIN32_LEAN_AND_MEAN ],[ EXCEPTION_DISPOSITION x; ], tcl_cv_eh_disposition=yes, tcl_cv_eh_disposition=no) ) if test "$tcl_cv_eh_disposition" = "no" ; then AC_DEFINE(EXCEPTION_DISPOSITION, int, [Defined when cygwin/mingw does not support EXCEPTION DISPOSITION]) fi # Check to see if winnt.h defines CHAR, SHORT, and LONG # even if VOID has already been #defined. The win32api # used by mingw and cygwin is known to do this. AC_CACHE_CHECK(for winnt.h that ignores VOID define, tcl_cv_winnt_ignore_void, AC_TRY_COMPILE([ #define VOID void #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN ], [ CHAR c; SHORT s; LONG l; ], tcl_cv_winnt_ignore_void=yes, tcl_cv_winnt_ignore_void=no) ) if test "$tcl_cv_winnt_ignore_void" = "yes" ; then AC_DEFINE(HAVE_WINNT_IGNORE_VOID, 1, [Defined when cygwin/mingw ignores VOID define in winnt.h]) fi fi # See if the compiler supports casting to a union type. # This is used to stop gcc from printing a compiler # warning when initializing a union member. AC_CACHE_CHECK(for cast to union support, tcl_cv_cast_to_union, AC_TRY_COMPILE([], [ union foo { int i; double d; }; union foo f = (union foo) (int) 0; ], tcl_cv_cast_to_union=yes, tcl_cv_cast_to_union=no) ) if test "$tcl_cv_cast_to_union" = "yes"; then AC_DEFINE(HAVE_CAST_TO_UNION, 1, [Defined when compiler supports casting to union type.]) fi AC_SUBST(CFLAGS_DEBUG) AC_SUBST(CFLAGS_OPTIMIZE) AC_SUBST(CFLAGS_WARNING) AC_SUBST(STLIB_LD) AC_SUBST(SHLIB_LD) AC_SUBST(SHLIB_LD_LIBS) AC_SUBST(SHLIB_CFLAGS) AC_SUBST(LD_LIBRARY_PATH_VAR) # These must be called after we do the basic CFLAGS checks and # verify any possible 64-bit or similar switches are necessary TEA_TCL_EARLY_FLAGS TEA_TCL_64BIT_FLAGS ]) #-------------------------------------------------------------------- # TEA_SERIAL_PORT # # Determine which interface to use to talk to the serial port. # Note that #include lines must begin in leftmost column for # some compilers to recognize them as preprocessor directives, # and some build environments have stdin not pointing at a # pseudo-terminal (usually /dev/null instead.) # # Arguments: # none # # Results: # # Defines only one of the following vars: # HAVE_SYS_MODEM_H # USE_TERMIOS # USE_TERMIO # USE_SGTTY #-------------------------------------------------------------------- AC_DEFUN([TEA_SERIAL_PORT], [ AC_CHECK_HEADERS(sys/modem.h) AC_CACHE_CHECK([termios vs. termio vs. sgtty], tcl_cv_api_serial, [ AC_TRY_RUN([ #include int main() { struct termios t; if (tcgetattr(0, &t) == 0) { cfsetospeed(&t, 0); t.c_cflag |= PARENB | PARODD | CSIZE | CSTOPB; return 0; } return 1; }], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no) if test $tcl_cv_api_serial = no ; then AC_TRY_RUN([ #include int main() { struct termio t; if (ioctl(0, TCGETA, &t) == 0) { t.c_cflag |= CBAUD | PARENB | PARODD | CSIZE | CSTOPB; return 0; } return 1; }], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no) fi if test $tcl_cv_api_serial = no ; then AC_TRY_RUN([ #include int main() { struct sgttyb t; if (ioctl(0, TIOCGETP, &t) == 0) { t.sg_ospeed = 0; t.sg_flags |= ODDP | EVENP | RAW; return 0; } return 1; }], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=no, tcl_cv_api_serial=no) fi if test $tcl_cv_api_serial = no ; then AC_TRY_RUN([ #include #include int main() { struct termios t; if (tcgetattr(0, &t) == 0 || errno == ENOTTY || errno == ENXIO || errno == EINVAL) { cfsetospeed(&t, 0); t.c_cflag |= PARENB | PARODD | CSIZE | CSTOPB; return 0; } return 1; }], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no) fi if test $tcl_cv_api_serial = no; then AC_TRY_RUN([ #include #include int main() { struct termio t; if (ioctl(0, TCGETA, &t) == 0 || errno == ENOTTY || errno == ENXIO || errno == EINVAL) { t.c_cflag |= CBAUD | PARENB | PARODD | CSIZE | CSTOPB; return 0; } return 1; }], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no) fi if test $tcl_cv_api_serial = no; then AC_TRY_RUN([ #include #include int main() { struct sgttyb t; if (ioctl(0, TIOCGETP, &t) == 0 || errno == ENOTTY || errno == ENXIO || errno == EINVAL) { t.sg_ospeed = 0; t.sg_flags |= ODDP | EVENP | RAW; return 0; } return 1; }], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=none, tcl_cv_api_serial=none) fi]) case $tcl_cv_api_serial in termios) AC_DEFINE(USE_TERMIOS, 1, [Use the termios API for serial lines]);; termio) AC_DEFINE(USE_TERMIO, 1, [Use the termio API for serial lines]);; sgtty) AC_DEFINE(USE_SGTTY, 1, [Use the sgtty API for serial lines]);; esac ]) #-------------------------------------------------------------------- # TEA_MISSING_POSIX_HEADERS # # Supply substitutes for missing POSIX header files. Special # notes: # - stdlib.h doesn't define strtol, strtoul, or # strtod in some versions of SunOS # - some versions of string.h don't declare procedures such # as strstr # # Arguments: # none # # Results: # # Defines some of the following vars: # NO_DIRENT_H # NO_ERRNO_H # NO_VALUES_H # HAVE_LIMITS_H or NO_LIMITS_H # NO_STDLIB_H # NO_STRING_H # NO_SYS_WAIT_H # NO_DLFCN_H # HAVE_SYS_PARAM_H # # HAVE_STRING_H ? # # tkUnixPort.h checks for HAVE_LIMITS_H, so do both HAVE and # CHECK on limits.h #-------------------------------------------------------------------- AC_DEFUN([TEA_MISSING_POSIX_HEADERS], [ AC_CACHE_CHECK([dirent.h], tcl_cv_dirent_h, [ AC_TRY_LINK([#include #include ], [ #ifndef _POSIX_SOURCE # ifdef __Lynx__ /* * Generate compilation error to make the test fail: Lynx headers * are only valid if really in the POSIX environment. */ missing_procedure(); # endif #endif DIR *d; struct dirent *entryPtr; char *p; d = opendir("foobar"); entryPtr = readdir(d); p = entryPtr->d_name; closedir(d); ], tcl_cv_dirent_h=yes, tcl_cv_dirent_h=no)]) if test $tcl_cv_dirent_h = no; then AC_DEFINE(NO_DIRENT_H, 1, [Do we have ?]) fi # TEA specific: AC_CHECK_HEADER(errno.h, , [AC_DEFINE(NO_ERRNO_H, 1, [Do we have ?])]) AC_CHECK_HEADER(float.h, , [AC_DEFINE(NO_FLOAT_H, 1, [Do we have ?])]) AC_CHECK_HEADER(values.h, , [AC_DEFINE(NO_VALUES_H, 1, [Do we have ?])]) AC_CHECK_HEADER(limits.h, [AC_DEFINE(HAVE_LIMITS_H, 1, [Do we have ?])], [AC_DEFINE(NO_LIMITS_H, 1, [Do we have ?])]) AC_CHECK_HEADER(stdlib.h, tcl_ok=1, tcl_ok=0) AC_EGREP_HEADER(strtol, stdlib.h, , tcl_ok=0) AC_EGREP_HEADER(strtoul, stdlib.h, , tcl_ok=0) AC_EGREP_HEADER(strtod, stdlib.h, , tcl_ok=0) if test $tcl_ok = 0; then AC_DEFINE(NO_STDLIB_H, 1, [Do we have ?]) fi AC_CHECK_HEADER(string.h, tcl_ok=1, tcl_ok=0) AC_EGREP_HEADER(strstr, string.h, , tcl_ok=0) AC_EGREP_HEADER(strerror, string.h, , tcl_ok=0) # See also memmove check below for a place where NO_STRING_H can be # set and why. if test $tcl_ok = 0; then AC_DEFINE(NO_STRING_H, 1, [Do we have ?]) fi AC_CHECK_HEADER(sys/wait.h, , [AC_DEFINE(NO_SYS_WAIT_H, 1, [Do we have ?])]) AC_CHECK_HEADER(dlfcn.h, , [AC_DEFINE(NO_DLFCN_H, 1, [Do we have ?])]) # OS/390 lacks sys/param.h (and doesn't need it, by chance). AC_HAVE_HEADERS(sys/param.h) ]) #-------------------------------------------------------------------- # TEA_PATH_X # # Locate the X11 header files and the X11 library archive. Try # the ac_path_x macro first, but if it doesn't find the X stuff # (e.g. because there's no xmkmf program) then check through # a list of possible directories. Under some conditions the # autoconf macro will return an include directory that contains # no include files, so double-check its result just to be safe. # # This should be called after TEA_CONFIG_CFLAGS as setting the # LIBS line can confuse some configure macro magic. # # Arguments: # none # # Results: # # Sets the following vars: # XINCLUDES # XLIBSW # PKG_LIBS (appends to) #-------------------------------------------------------------------- AC_DEFUN([TEA_PATH_X], [ if test "${TEA_WINDOWINGSYSTEM}" = "x11" ; then TEA_PATH_UNIX_X fi ]) AC_DEFUN([TEA_PATH_UNIX_X], [ AC_PATH_X not_really_there="" if test "$no_x" = ""; then if test "$x_includes" = ""; then AC_TRY_CPP([#include ], , not_really_there="yes") else if test ! -r $x_includes/X11/Xlib.h; then not_really_there="yes" fi fi fi if test "$no_x" = "yes" -o "$not_really_there" = "yes"; then AC_MSG_CHECKING([for X11 header files]) found_xincludes="no" AC_TRY_CPP([#include ], found_xincludes="yes", found_xincludes="no") if test "$found_xincludes" = "no"; then dirs="/usr/unsupported/include /usr/local/include /usr/X386/include /usr/X11R6/include /usr/X11R5/include /usr/include/X11R5 /usr/include/X11R4 /usr/openwin/include /usr/X11/include /usr/sww/include" for i in $dirs ; do if test -r $i/X11/Xlib.h; then AC_MSG_RESULT([$i]) XINCLUDES=" -I$i" found_xincludes="yes" break fi done fi else if test "$x_includes" != ""; then XINCLUDES="-I$x_includes" found_xincludes="yes" fi fi if test "$found_xincludes" = "no"; then AC_MSG_RESULT([couldn't find any!]) fi if test "$no_x" = yes; then AC_MSG_CHECKING([for X11 libraries]) XLIBSW=nope dirs="/usr/unsupported/lib /usr/local/lib /usr/X386/lib /usr/X11R6/lib /usr/X11R5/lib /usr/lib/X11R5 /usr/lib/X11R4 /usr/openwin/lib /usr/X11/lib /usr/sww/X11/lib" for i in $dirs ; do if test -r $i/libX11.a -o -r $i/libX11.so -o -r $i/libX11.sl -o -r $i/libX11.dylib; then AC_MSG_RESULT([$i]) XLIBSW="-L$i -lX11" x_libraries="$i" break fi done else if test "$x_libraries" = ""; then XLIBSW=-lX11 else XLIBSW="-L$x_libraries -lX11" fi fi if test "$XLIBSW" = nope ; then AC_CHECK_LIB(Xwindow, XCreateWindow, XLIBSW=-lXwindow) fi if test "$XLIBSW" = nope ; then AC_MSG_RESULT([could not find any! Using -lX11.]) XLIBSW=-lX11 fi # TEA specific: if test x"${XLIBSW}" != x ; then PKG_LIBS="${PKG_LIBS} ${XLIBSW}" fi ]) #-------------------------------------------------------------------- # TEA_BLOCKING_STYLE # # The statements below check for systems where POSIX-style # non-blocking I/O (O_NONBLOCK) doesn't work or is unimplemented. # On these systems (mostly older ones), use the old BSD-style # FIONBIO approach instead. # # Arguments: # none # # Results: # # Defines some of the following vars: # HAVE_SYS_IOCTL_H # HAVE_SYS_FILIO_H # USE_FIONBIO # O_NONBLOCK #-------------------------------------------------------------------- AC_DEFUN([TEA_BLOCKING_STYLE], [ AC_CHECK_HEADERS(sys/ioctl.h) AC_CHECK_HEADERS(sys/filio.h) TEA_CONFIG_SYSTEM AC_MSG_CHECKING([FIONBIO vs. O_NONBLOCK for nonblocking I/O]) case $system in OSF*) AC_DEFINE(USE_FIONBIO, 1, [Should we use FIONBIO?]) AC_MSG_RESULT([FIONBIO]) ;; *) AC_MSG_RESULT([O_NONBLOCK]) ;; esac ]) #-------------------------------------------------------------------- # TEA_TIME_HANDLER # # Checks how the system deals with time.h, what time structures # are used on the system, and what fields the structures have. # # Arguments: # none # # Results: # # Defines some of the following vars: # USE_DELTA_FOR_TZ # HAVE_TM_GMTOFF # HAVE_TM_TZADJ # HAVE_TIMEZONE_VAR #-------------------------------------------------------------------- AC_DEFUN([TEA_TIME_HANDLER], [ AC_CHECK_HEADERS(sys/time.h) AC_HEADER_TIME AC_STRUCT_TIMEZONE AC_CHECK_FUNCS(gmtime_r localtime_r) AC_CACHE_CHECK([tm_tzadj in struct tm], tcl_cv_member_tm_tzadj, [ AC_TRY_COMPILE([#include ], [struct tm tm; tm.tm_tzadj;], tcl_cv_member_tm_tzadj=yes, tcl_cv_member_tm_tzadj=no)]) if test $tcl_cv_member_tm_tzadj = yes ; then AC_DEFINE(HAVE_TM_TZADJ, 1, [Should we use the tm_tzadj field of struct tm?]) fi AC_CACHE_CHECK([tm_gmtoff in struct tm], tcl_cv_member_tm_gmtoff, [ AC_TRY_COMPILE([#include ], [struct tm tm; tm.tm_gmtoff;], tcl_cv_member_tm_gmtoff=yes, tcl_cv_member_tm_gmtoff=no)]) if test $tcl_cv_member_tm_gmtoff = yes ; then AC_DEFINE(HAVE_TM_GMTOFF, 1, [Should we use the tm_gmtoff field of struct tm?]) fi # # Its important to include time.h in this check, as some systems # (like convex) have timezone functions, etc. # AC_CACHE_CHECK([long timezone variable], tcl_cv_timezone_long, [ AC_TRY_COMPILE([#include ], [extern long timezone; timezone += 1; exit (0);], tcl_cv_timezone_long=yes, tcl_cv_timezone_long=no)]) if test $tcl_cv_timezone_long = yes ; then AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?]) else # # On some systems (eg IRIX 6.2), timezone is a time_t and not a long. # AC_CACHE_CHECK([time_t timezone variable], tcl_cv_timezone_time, [ AC_TRY_COMPILE([#include ], [extern time_t timezone; timezone += 1; exit (0);], tcl_cv_timezone_time=yes, tcl_cv_timezone_time=no)]) if test $tcl_cv_timezone_time = yes ; then AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?]) fi fi ]) #-------------------------------------------------------------------- # TEA_BUGGY_STRTOD # # Under Solaris 2.4, strtod returns the wrong value for the # terminating character under some conditions. Check for this # and if the problem exists use a substitute procedure # "fixstrtod" (provided by Tcl) that corrects the error. # Also, on Compaq's Tru64 Unix 5.0, # strtod(" ") returns 0.0 instead of a failure to convert. # # Arguments: # none # # Results: # # Might defines some of the following vars: # strtod (=fixstrtod) #-------------------------------------------------------------------- AC_DEFUN([TEA_BUGGY_STRTOD], [ AC_CHECK_FUNC(strtod, tcl_strtod=1, tcl_strtod=0) if test "$tcl_strtod" = 1; then AC_CACHE_CHECK([for Solaris2.4/Tru64 strtod bugs], tcl_cv_strtod_buggy,[ AC_TRY_RUN([ extern double strtod(); int main() { char *infString="Inf", *nanString="NaN", *spaceString=" "; char *term; double value; value = strtod(infString, &term); if ((term != infString) && (term[-1] == 0)) { exit(1); } value = strtod(nanString, &term); if ((term != nanString) && (term[-1] == 0)) { exit(1); } value = strtod(spaceString, &term); if (term == (spaceString+1)) { exit(1); } exit(0); }], tcl_cv_strtod_buggy=ok, tcl_cv_strtod_buggy=buggy, tcl_cv_strtod_buggy=buggy)]) if test "$tcl_cv_strtod_buggy" = buggy; then AC_LIBOBJ([fixstrtod]) USE_COMPAT=1 AC_DEFINE(strtod, fixstrtod, [Do we want to use the strtod() in compat?]) fi fi ]) #-------------------------------------------------------------------- # TEA_TCL_LINK_LIBS # # Search for the libraries needed to link the Tcl shell. # Things like the math library (-lm) and socket stuff (-lsocket vs. # -lnsl) are dealt with here. # # Arguments: # Requires the following vars to be set in the Makefile: # DL_LIBS (not in TEA, only needed in core) # LIBS # MATH_LIBS # # Results: # # Substitutes the following vars: # TCL_LIBS # MATH_LIBS # # Might append to the following vars: # LIBS # # Might define the following vars: # HAVE_NET_ERRNO_H #-------------------------------------------------------------------- AC_DEFUN([TEA_TCL_LINK_LIBS], [ #-------------------------------------------------------------------- # On a few very rare systems, all of the libm.a stuff is # already in libc.a. Set compiler flags accordingly. # Also, Linux requires the "ieee" library for math to work # right (and it must appear before "-lm"). #-------------------------------------------------------------------- AC_CHECK_FUNC(sin, MATH_LIBS="", MATH_LIBS="-lm") AC_CHECK_LIB(ieee, main, [MATH_LIBS="-lieee $MATH_LIBS"]) #-------------------------------------------------------------------- # Interactive UNIX requires -linet instead of -lsocket, plus it # needs net/errno.h to define the socket-related error codes. #-------------------------------------------------------------------- AC_CHECK_LIB(inet, main, [LIBS="$LIBS -linet"]) AC_CHECK_HEADER(net/errno.h, [ AC_DEFINE(HAVE_NET_ERRNO_H, 1, [Do we have ?])]) #-------------------------------------------------------------------- # Check for the existence of the -lsocket and -lnsl libraries. # The order here is important, so that they end up in the right # order in the command line generated by make. Here are some # special considerations: # 1. Use "connect" and "accept" to check for -lsocket, and # "gethostbyname" to check for -lnsl. # 2. Use each function name only once: can't redo a check because # autoconf caches the results of the last check and won't redo it. # 3. Use -lnsl and -lsocket only if they supply procedures that # aren't already present in the normal libraries. This is because # IRIX 5.2 has libraries, but they aren't needed and they're # bogus: they goof up name resolution if used. # 4. On some SVR4 systems, can't use -lsocket without -lnsl too. # To get around this problem, check for both libraries together # if -lsocket doesn't work by itself. #-------------------------------------------------------------------- tcl_checkBoth=0 AC_CHECK_FUNC(connect, tcl_checkSocket=0, tcl_checkSocket=1) if test "$tcl_checkSocket" = 1; then AC_CHECK_FUNC(setsockopt, , [AC_CHECK_LIB(socket, setsockopt, LIBS="$LIBS -lsocket", tcl_checkBoth=1)]) fi if test "$tcl_checkBoth" = 1; then tk_oldLibs=$LIBS LIBS="$LIBS -lsocket -lnsl" AC_CHECK_FUNC(accept, tcl_checkNsl=0, [LIBS=$tk_oldLibs]) fi AC_CHECK_FUNC(gethostbyname, , [AC_CHECK_LIB(nsl, gethostbyname, [LIBS="$LIBS -lnsl"])]) # TEA specific: Don't perform the eval of the libraries here because # DL_LIBS won't be set until we call TEA_CONFIG_CFLAGS TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}' AC_SUBST(TCL_LIBS) AC_SUBST(MATH_LIBS) ]) #-------------------------------------------------------------------- # TEA_TCL_EARLY_FLAGS # # Check for what flags are needed to be passed so the correct OS # features are available. # # Arguments: # None # # Results: # # Might define the following vars: # _ISOC99_SOURCE # _LARGEFILE64_SOURCE # _LARGEFILE_SOURCE64 #-------------------------------------------------------------------- AC_DEFUN([TEA_TCL_EARLY_FLAG],[ AC_CACHE_VAL([tcl_cv_flag_]translit($1,[A-Z],[a-z]), AC_TRY_COMPILE([$2], $3, [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no, AC_TRY_COMPILE([[#define ]$1[ 1 ]$2], $3, [tcl_cv_flag_]translit($1,[A-Z],[a-z])=yes, [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no))) if test ["x${tcl_cv_flag_]translit($1,[A-Z],[a-z])[}" = "xyes"] ; then AC_DEFINE($1, 1, [Add the ]$1[ flag when building]) tcl_flags="$tcl_flags $1" fi ]) AC_DEFUN([TEA_TCL_EARLY_FLAGS],[ AC_MSG_CHECKING([for required early compiler flags]) tcl_flags="" TEA_TCL_EARLY_FLAG(_ISOC99_SOURCE,[#include ], [char *p = (char *)strtoll; char *q = (char *)strtoull;]) TEA_TCL_EARLY_FLAG(_LARGEFILE64_SOURCE,[#include ], [struct stat64 buf; int i = stat64("/", &buf);]) TEA_TCL_EARLY_FLAG(_LARGEFILE_SOURCE64,[#include ], [char *p = (char *)open64;]) if test "x${tcl_flags}" = "x" ; then AC_MSG_RESULT([none]) else AC_MSG_RESULT([${tcl_flags}]) fi ]) #-------------------------------------------------------------------- # TEA_TCL_64BIT_FLAGS # # Check for what is defined in the way of 64-bit features. # # Arguments: # None # # Results: # # Might define the following vars: # TCL_WIDE_INT_IS_LONG # TCL_WIDE_INT_TYPE # HAVE_STRUCT_DIRENT64 # HAVE_STRUCT_STAT64 # HAVE_TYPE_OFF64_T #-------------------------------------------------------------------- AC_DEFUN([TEA_TCL_64BIT_FLAGS], [ AC_MSG_CHECKING([for 64-bit integer type]) AC_CACHE_VAL(tcl_cv_type_64bit,[ tcl_cv_type_64bit=none # See if the compiler knows natively about __int64 AC_TRY_COMPILE(,[__int64 value = (__int64) 0;], tcl_type_64bit=__int64, tcl_type_64bit="long long") # See if we should use long anyway Note that we substitute in the # type that is our current guess for a 64-bit type inside this check # program, so it should be modified only carefully... AC_TRY_COMPILE(,[switch (0) { case 1: case (sizeof(]${tcl_type_64bit}[)==sizeof(long)): ; }],tcl_cv_type_64bit=${tcl_type_64bit})]) if test "${tcl_cv_type_64bit}" = none ; then AC_DEFINE(TCL_WIDE_INT_IS_LONG, 1, [Are wide integers to be implemented with C 'long's?]) AC_MSG_RESULT([using long]) elif test "${tcl_cv_type_64bit}" = "__int64" \ -a "${TEA_PLATFORM}" = "windows" ; then # TEA specific: We actually want to use the default tcl.h checks in # this case to handle both TCL_WIDE_INT_TYPE and TCL_LL_MODIFIER* AC_MSG_RESULT([using Tcl header defaults]) else AC_DEFINE_UNQUOTED(TCL_WIDE_INT_TYPE,${tcl_cv_type_64bit}, [What type should be used to define wide integers?]) AC_MSG_RESULT([${tcl_cv_type_64bit}]) # Now check for auxiliary declarations AC_CACHE_CHECK([for struct dirent64], tcl_cv_struct_dirent64,[ AC_TRY_COMPILE([#include #include ],[struct dirent64 p;], tcl_cv_struct_dirent64=yes,tcl_cv_struct_dirent64=no)]) if test "x${tcl_cv_struct_dirent64}" = "xyes" ; then AC_DEFINE(HAVE_STRUCT_DIRENT64, 1, [Is 'struct dirent64' in ?]) fi AC_CACHE_CHECK([for struct stat64], tcl_cv_struct_stat64,[ AC_TRY_COMPILE([#include ],[struct stat64 p; ], tcl_cv_struct_stat64=yes,tcl_cv_struct_stat64=no)]) if test "x${tcl_cv_struct_stat64}" = "xyes" ; then AC_DEFINE(HAVE_STRUCT_STAT64, 1, [Is 'struct stat64' in ?]) fi AC_CHECK_FUNCS(open64 lseek64) AC_MSG_CHECKING([for off64_t]) AC_CACHE_VAL(tcl_cv_type_off64_t,[ AC_TRY_COMPILE([#include ],[off64_t offset; ], tcl_cv_type_off64_t=yes,tcl_cv_type_off64_t=no)]) dnl Define HAVE_TYPE_OFF64_T only when the off64_t type and the dnl functions lseek64 and open64 are defined. if test "x${tcl_cv_type_off64_t}" = "xyes" && \ test "x${ac_cv_func_lseek64}" = "xyes" && \ test "x${ac_cv_func_open64}" = "xyes" ; then AC_DEFINE(HAVE_TYPE_OFF64_T, 1, [Is off64_t in ?]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi fi ]) ## ## Here ends the standard Tcl configuration bits and starts the ## TEA specific functions ## #------------------------------------------------------------------------ # TEA_INIT -- # # Init various Tcl Extension Architecture (TEA) variables. # This should be the first called TEA_* macro. # # Arguments: # none # # Results: # # Defines and substs the following vars: # CYGPATH # EXEEXT # Defines only: # TEA_VERSION # TEA_INITED # TEA_PLATFORM (windows or unix) # # "cygpath" is used on windows to generate native path names for include # files. These variables should only be used with the compiler and linker # since they generate native path names. # # EXEEXT # Select the executable extension based on the host type. This # is a lightweight replacement for AC_EXEEXT that doesn't require # a compiler. #------------------------------------------------------------------------ AC_DEFUN([TEA_INIT], [ # TEA extensions pass this us the version of TEA they think they # are compatible with. TEA_VERSION="3.9" AC_MSG_CHECKING([for correct TEA configuration]) if test x"${PACKAGE_NAME}" = x ; then AC_MSG_ERROR([ The PACKAGE_NAME variable must be defined by your TEA configure.in]) fi if test x"$1" = x ; then AC_MSG_ERROR([ TEA version not specified.]) elif test "$1" != "${TEA_VERSION}" ; then AC_MSG_RESULT([warning: requested TEA version "$1", have "${TEA_VERSION}"]) else AC_MSG_RESULT([ok (TEA ${TEA_VERSION})]) fi # If the user did not set CFLAGS, set it now to keep macros # like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2". if test "${CFLAGS+set}" != "set" ; then CFLAGS="" fi case "`uname -s`" in *win32*|*WIN32*|*MINGW32_*) AC_CHECK_PROG(CYGPATH, cygpath, cygpath -w, echo) EXEEXT=".exe" TEA_PLATFORM="windows" ;; *CYGWIN_*) CYGPATH=echo EXEEXT=".exe" # TEA_PLATFORM is determined later in LOAD_TCLCONFIG ;; *) CYGPATH=echo # Maybe we are cross-compiling.... case ${host_alias} in *mingw32*) EXEEXT=".exe" TEA_PLATFORM="windows" ;; *) EXEEXT="" TEA_PLATFORM="unix" ;; esac ;; esac # Check if exec_prefix is set. If not use fall back to prefix. # Note when adjusted, so that TEA_PREFIX can correct for this. # This is needed for recursive configures, since autoconf propagates # $prefix, but not $exec_prefix (doh!). if test x$exec_prefix = xNONE ; then exec_prefix_default=yes exec_prefix=$prefix fi AC_MSG_NOTICE([configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}]) AC_SUBST(EXEEXT) AC_SUBST(CYGPATH) # This package name must be replaced statically for AC_SUBST to work AC_SUBST(PKG_LIB_FILE) # Substitute STUB_LIB_FILE in case package creates a stub library too. AC_SUBST(PKG_STUB_LIB_FILE) # We AC_SUBST these here to ensure they are subst'ed, # in case the user doesn't call TEA_ADD_... AC_SUBST(PKG_STUB_SOURCES) AC_SUBST(PKG_STUB_OBJECTS) AC_SUBST(PKG_TCL_SOURCES) AC_SUBST(PKG_HEADERS) AC_SUBST(PKG_INCLUDES) AC_SUBST(PKG_LIBS) AC_SUBST(PKG_CFLAGS) ]) #------------------------------------------------------------------------ # TEA_ADD_SOURCES -- # # Specify one or more source files. Users should check for # the right platform before adding to their list. # It is not important to specify the directory, as long as it is # in the generic, win or unix subdirectory of $(srcdir). # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_SOURCES # PKG_OBJECTS #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_SOURCES], [ vars="$@" for i in $vars; do case $i in [\$]*) # allow $-var names PKG_SOURCES="$PKG_SOURCES $i" PKG_OBJECTS="$PKG_OBJECTS $i" ;; *) # check for existence - allows for generic/win/unix VPATH # To add more dirs here (like 'src'), you have to update VPATH # in Makefile.in as well if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \ -a ! -f "${srcdir}/macosx/$i" \ ; then AC_MSG_ERROR([could not find source file '$i']) fi PKG_SOURCES="$PKG_SOURCES $i" # this assumes it is in a VPATH dir i=`basename $i` # handle user calling this before or after TEA_SETUP_COMPILER if test x"${OBJEXT}" != x ; then j="`echo $i | sed -e 's/\.[[^.]]*$//'`.${OBJEXT}" else j="`echo $i | sed -e 's/\.[[^.]]*$//'`.\${OBJEXT}" fi PKG_OBJECTS="$PKG_OBJECTS $j" ;; esac done AC_SUBST(PKG_SOURCES) AC_SUBST(PKG_OBJECTS) ]) #------------------------------------------------------------------------ # TEA_ADD_STUB_SOURCES -- # # Specify one or more source files. Users should check for # the right platform before adding to their list. # It is not important to specify the directory, as long as it is # in the generic, win or unix subdirectory of $(srcdir). # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_STUB_SOURCES # PKG_STUB_OBJECTS #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_STUB_SOURCES], [ vars="$@" for i in $vars; do # check for existence - allows for generic/win/unix VPATH if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \ -a ! -f "${srcdir}/macosx/$i" \ ; then AC_MSG_ERROR([could not find stub source file '$i']) fi PKG_STUB_SOURCES="$PKG_STUB_SOURCES $i" # this assumes it is in a VPATH dir i=`basename $i` # handle user calling this before or after TEA_SETUP_COMPILER if test x"${OBJEXT}" != x ; then j="`echo $i | sed -e 's/\.[[^.]]*$//'`.${OBJEXT}" else j="`echo $i | sed -e 's/\.[[^.]]*$//'`.\${OBJEXT}" fi PKG_STUB_OBJECTS="$PKG_STUB_OBJECTS $j" done AC_SUBST(PKG_STUB_SOURCES) AC_SUBST(PKG_STUB_OBJECTS) ]) #------------------------------------------------------------------------ # TEA_ADD_TCL_SOURCES -- # # Specify one or more Tcl source files. These should be platform # independent runtime files. # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_TCL_SOURCES #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_TCL_SOURCES], [ vars="$@" for i in $vars; do # check for existence, be strict because it is installed if test ! -f "${srcdir}/$i" ; then AC_MSG_ERROR([could not find tcl source file '${srcdir}/$i']) fi PKG_TCL_SOURCES="$PKG_TCL_SOURCES $i" done AC_SUBST(PKG_TCL_SOURCES) ]) #------------------------------------------------------------------------ # TEA_ADD_HEADERS -- # # Specify one or more source headers. Users should check for # the right platform before adding to their list. # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_HEADERS #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_HEADERS], [ vars="$@" for i in $vars; do # check for existence, be strict because it is installed if test ! -f "${srcdir}/$i" ; then AC_MSG_ERROR([could not find header file '${srcdir}/$i']) fi PKG_HEADERS="$PKG_HEADERS $i" done AC_SUBST(PKG_HEADERS) ]) #------------------------------------------------------------------------ # TEA_ADD_INCLUDES -- # # Specify one or more include dirs. Users should check for # the right platform before adding to their list. # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_INCLUDES #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_INCLUDES], [ vars="$@" for i in $vars; do PKG_INCLUDES="$PKG_INCLUDES $i" done AC_SUBST(PKG_INCLUDES) ]) #------------------------------------------------------------------------ # TEA_ADD_LIBS -- # # Specify one or more libraries. Users should check for # the right platform before adding to their list. For Windows, # libraries provided in "foo.lib" format will be converted to # "-lfoo" when using GCC (mingw). # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_LIBS #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_LIBS], [ vars="$@" for i in $vars; do if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then # Convert foo.lib to -lfoo for GCC. No-op if not *.lib i=`echo "$i" | sed -e 's/^\([[^-]].*\)\.lib[$]/-l\1/i'` fi PKG_LIBS="$PKG_LIBS $i" done AC_SUBST(PKG_LIBS) ]) #------------------------------------------------------------------------ # TEA_ADD_CFLAGS -- # # Specify one or more CFLAGS. Users should check for # the right platform before adding to their list. # # Arguments: # one or more file names # # Results: # # Defines and substs the following vars: # PKG_CFLAGS #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_CFLAGS], [ PKG_CFLAGS="$PKG_CFLAGS $@" AC_SUBST(PKG_CFLAGS) ]) #------------------------------------------------------------------------ # TEA_ADD_CLEANFILES -- # # Specify one or more CLEANFILES. # # Arguments: # one or more file names to clean target # # Results: # # Appends to CLEANFILES, already defined for subst in LOAD_TCLCONFIG #------------------------------------------------------------------------ AC_DEFUN([TEA_ADD_CLEANFILES], [ CLEANFILES="$CLEANFILES $@" ]) #------------------------------------------------------------------------ # TEA_PREFIX -- # # Handle the --prefix=... option by defaulting to what Tcl gave # # Arguments: # none # # Results: # # If --prefix or --exec-prefix was not specified, $prefix and # $exec_prefix will be set to the values given to Tcl when it was # configured. #------------------------------------------------------------------------ AC_DEFUN([TEA_PREFIX], [ if test "${prefix}" = "NONE"; then prefix_default=yes if test x"${TCL_PREFIX}" != x; then AC_MSG_NOTICE([--prefix defaulting to TCL_PREFIX ${TCL_PREFIX}]) prefix=${TCL_PREFIX} else AC_MSG_NOTICE([--prefix defaulting to /usr/local]) prefix=/usr/local fi fi if test "${exec_prefix}" = "NONE" -a x"${prefix_default}" = x"yes" \ -o x"${exec_prefix_default}" = x"yes" ; then if test x"${TCL_EXEC_PREFIX}" != x; then AC_MSG_NOTICE([--exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}]) exec_prefix=${TCL_EXEC_PREFIX} else AC_MSG_NOTICE([--exec-prefix defaulting to ${prefix}]) exec_prefix=$prefix fi fi ]) #------------------------------------------------------------------------ # TEA_SETUP_COMPILER_CC -- # # Do compiler checks the way we want. This is just a replacement # for AC_PROG_CC in TEA configure.in files to make them cleaner. # # Arguments: # none # # Results: # # Sets up CC var and other standard bits we need to make executables. #------------------------------------------------------------------------ AC_DEFUN([TEA_SETUP_COMPILER_CC], [ # Don't put any macros that use the compiler (e.g. AC_TRY_COMPILE) # in this macro, they need to go into TEA_SETUP_COMPILER instead. AC_PROG_CC AC_PROG_CPP INSTALL="\$(SHELL) \$(srcdir)/tclconfig/install-sh -c" AC_SUBST(INSTALL) INSTALL_DATA="\${INSTALL} -m 644" AC_SUBST(INSTALL_DATA) INSTALL_PROGRAM="\${INSTALL}" AC_SUBST(INSTALL_PROGRAM) INSTALL_SCRIPT="\${INSTALL}" AC_SUBST(INSTALL_SCRIPT) #-------------------------------------------------------------------- # Checks to see if the make program sets the $MAKE variable. #-------------------------------------------------------------------- AC_PROG_MAKE_SET #-------------------------------------------------------------------- # Find ranlib #-------------------------------------------------------------------- AC_CHECK_TOOL(RANLIB, ranlib) #-------------------------------------------------------------------- # Determines the correct binary file extension (.o, .obj, .exe etc.) #-------------------------------------------------------------------- AC_OBJEXT AC_EXEEXT ]) #------------------------------------------------------------------------ # TEA_SETUP_COMPILER -- # # Do compiler checks that use the compiler. This must go after # TEA_SETUP_COMPILER_CC, which does the actual compiler check. # # Arguments: # none # # Results: # # Sets up CC var and other standard bits we need to make executables. #------------------------------------------------------------------------ AC_DEFUN([TEA_SETUP_COMPILER], [ # Any macros that use the compiler (e.g. AC_TRY_COMPILE) have to go here. AC_REQUIRE([TEA_SETUP_COMPILER_CC]) #------------------------------------------------------------------------ # If we're using GCC, see if the compiler understands -pipe. If so, use it. # It makes compiling go faster. (This is only a performance feature.) #------------------------------------------------------------------------ if test -z "$no_pipe" -a -n "$GCC"; then AC_CACHE_CHECK([if the compiler understands -pipe], tcl_cv_cc_pipe, [ hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -pipe" AC_TRY_COMPILE(,, tcl_cv_cc_pipe=yes, tcl_cv_cc_pipe=no) CFLAGS=$hold_cflags]) if test $tcl_cv_cc_pipe = yes; then CFLAGS="$CFLAGS -pipe" fi fi #-------------------------------------------------------------------- # Common compiler flag setup #-------------------------------------------------------------------- AC_C_BIGENDIAN if test "${TEA_PLATFORM}" = "unix" ; then TEA_TCL_LINK_LIBS TEA_MISSING_POSIX_HEADERS # Let the user call this, because if it triggers, they will # need a compat/strtod.c that is correct. Users can also # use Tcl_GetDouble(FromObj) instead. #TEA_BUGGY_STRTOD fi ]) #------------------------------------------------------------------------ # TEA_MAKE_LIB -- # # Generate a line that can be used to build a shared/unshared library # in a platform independent manner. # # Arguments: # none # # Requires: # # Results: # # Defines the following vars: # CFLAGS - Done late here to note disturb other AC macros # MAKE_LIB - Command to execute to build the Tcl library; # differs depending on whether or not Tcl is being # compiled as a shared library. # MAKE_SHARED_LIB Makefile rule for building a shared library # MAKE_STATIC_LIB Makefile rule for building a static library # MAKE_STUB_LIB Makefile rule for building a stub library # VC_MANIFEST_EMBED_DLL Makefile rule for embedded VC manifest in DLL # VC_MANIFEST_EMBED_EXE Makefile rule for embedded VC manifest in EXE #------------------------------------------------------------------------ AC_DEFUN([TEA_MAKE_LIB], [ if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes"; then MAKE_STATIC_LIB="\${STLIB_LD} -out:\[$]@ \$(PKG_OBJECTS)" MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LD_LIBS} \${LDFLAGS_DEFAULT} -out:\[$]@ \$(PKG_OBJECTS)" AC_EGREP_CPP([manifest needed], [ #if defined(_MSC_VER) && _MSC_VER >= 1400 print("manifest needed") #endif ], [ # Could do a CHECK_PROG for mt, but should always be with MSVC8+ VC_MANIFEST_EMBED_DLL="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest -outputresource:\[$]@\;2 ; fi" VC_MANIFEST_EMBED_EXE="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest -outputresource:\[$]@\;1 ; fi" MAKE_SHARED_LIB="${MAKE_SHARED_LIB} ; ${VC_MANIFEST_EMBED_DLL}" TEA_ADD_CLEANFILES([*.manifest]) ]) MAKE_STUB_LIB="\${STLIB_LD} -nodefaultlib -out:\[$]@ \$(PKG_STUB_OBJECTS)" else MAKE_STATIC_LIB="\${STLIB_LD} \[$]@ \$(PKG_OBJECTS)" MAKE_SHARED_LIB="\${SHLIB_LD} -o \[$]@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}" MAKE_STUB_LIB="\${STLIB_LD} \[$]@ \$(PKG_STUB_OBJECTS)" fi if test "${SHARED_BUILD}" = "1" ; then MAKE_LIB="${MAKE_SHARED_LIB} " else MAKE_LIB="${MAKE_STATIC_LIB} " fi #-------------------------------------------------------------------- # Shared libraries and static libraries have different names. # Use the double eval to make sure any variables in the suffix is # substituted. (@@@ Might not be necessary anymore) #-------------------------------------------------------------------- if test "${TEA_PLATFORM}" = "windows" ; then if test "${SHARED_BUILD}" = "1" ; then # We force the unresolved linking of symbols that are really in # the private libraries of Tcl and Tk. if test x"${TK_BIN_DIR}" != x ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TK_BIN_DIR}/${TK_STUB_LIB_FILE}`\"" fi SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}`\"" if test "$GCC" = "yes"; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -static-libgcc" fi eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}" else eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}" if test "$GCC" = "yes"; then PKG_LIB_FILE=lib${PKG_LIB_FILE} fi fi # Some packages build their own stubs libraries eval eval "PKG_STUB_LIB_FILE=${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}" if test "$GCC" = "yes"; then PKG_STUB_LIB_FILE=lib${PKG_STUB_LIB_FILE} fi # These aren't needed on Windows (either MSVC or gcc) RANLIB=: RANLIB_STUB=: else RANLIB_STUB="${RANLIB}" if test "${SHARED_BUILD}" = "1" ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TCL_STUB_LIB_SPEC}" if test x"${TK_BIN_DIR}" != x ; then SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TK_STUB_LIB_SPEC}" fi eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${SHARED_LIB_SUFFIX}" RANLIB=: else eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}" fi # Some packages build their own stubs libraries eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}" fi # These are escaped so that only CFLAGS is picked up at configure time. # The other values will be substituted at make time. CFLAGS="${CFLAGS} \${CFLAGS_DEFAULT} \${CFLAGS_WARNING}" if test "${SHARED_BUILD}" = "1" ; then CFLAGS="${CFLAGS} \${SHLIB_CFLAGS}" fi AC_SUBST(MAKE_LIB) AC_SUBST(MAKE_SHARED_LIB) AC_SUBST(MAKE_STATIC_LIB) AC_SUBST(MAKE_STUB_LIB) AC_SUBST(RANLIB_STUB) AC_SUBST(VC_MANIFEST_EMBED_DLL) AC_SUBST(VC_MANIFEST_EMBED_EXE) ]) #------------------------------------------------------------------------ # TEA_LIB_SPEC -- # # Compute the name of an existing object library located in libdir # from the given base name and produce the appropriate linker flags. # # Arguments: # basename The base name of the library without version # numbers, extensions, or "lib" prefixes. # extra_dir Extra directory in which to search for the # library. This location is used first, then # $prefix/$exec-prefix, then some defaults. # # Requires: # TEA_INIT and TEA_PREFIX must be called first. # # Results: # # Defines the following vars: # ${basename}_LIB_NAME The computed library name. # ${basename}_LIB_SPEC The computed linker flags. #------------------------------------------------------------------------ AC_DEFUN([TEA_LIB_SPEC], [ AC_MSG_CHECKING([for $1 library]) # Look in exec-prefix for the library (defined by TEA_PREFIX). tea_lib_name_dir="${exec_prefix}/lib" # Or in a user-specified location. if test x"$2" != x ; then tea_extra_lib_dir=$2 else tea_extra_lib_dir=NONE fi for i in \ `ls -dr ${tea_extra_lib_dir}/$1[[0-9]]*.lib 2>/dev/null ` \ `ls -dr ${tea_extra_lib_dir}/lib$1[[0-9]]* 2>/dev/null ` \ `ls -dr ${tea_lib_name_dir}/$1[[0-9]]*.lib 2>/dev/null ` \ `ls -dr ${tea_lib_name_dir}/lib$1[[0-9]]* 2>/dev/null ` \ `ls -dr /usr/lib/$1[[0-9]]*.lib 2>/dev/null ` \ `ls -dr /usr/lib/lib$1[[0-9]]* 2>/dev/null ` \ `ls -dr /usr/lib64/$1[[0-9]]*.lib 2>/dev/null ` \ `ls -dr /usr/lib64/lib$1[[0-9]]* 2>/dev/null ` \ `ls -dr /usr/local/lib/$1[[0-9]]*.lib 2>/dev/null ` \ `ls -dr /usr/local/lib/lib$1[[0-9]]* 2>/dev/null ` ; do if test -f "$i" ; then tea_lib_name_dir=`dirname $i` $1_LIB_NAME=`basename $i` $1_LIB_PATH_NAME=$i break fi done if test "${TEA_PLATFORM}" = "windows"; then $1_LIB_SPEC=\"`${CYGPATH} ${$1_LIB_PATH_NAME} 2>/dev/null`\" else # Strip off the leading "lib" and trailing ".a" or ".so" tea_lib_name_lib=`echo ${$1_LIB_NAME}|sed -e 's/^lib//' -e 's/\.[[^.]]*$//' -e 's/\.so.*//'` $1_LIB_SPEC="-L${tea_lib_name_dir} -l${tea_lib_name_lib}" fi if test "x${$1_LIB_NAME}" = x ; then AC_MSG_ERROR([not found]) else AC_MSG_RESULT([${$1_LIB_SPEC}]) fi ]) #------------------------------------------------------------------------ # TEA_PRIVATE_TCL_HEADERS -- # # Locate the private Tcl include files # # Arguments: # # Requires: # TCL_SRC_DIR Assumes that TEA_LOAD_TCLCONFIG has # already been called. # # Results: # # Substitutes the following vars: # TCL_TOP_DIR_NATIVE # TCL_INCLUDES #------------------------------------------------------------------------ AC_DEFUN([TEA_PRIVATE_TCL_HEADERS], [ # Allow for --with-tclinclude to take effect and define ${ac_cv_c_tclh} AC_REQUIRE([TEA_PUBLIC_TCL_HEADERS]) AC_MSG_CHECKING([for Tcl private include files]) TCL_SRC_DIR_NATIVE=`${CYGPATH} ${TCL_SRC_DIR}` TCL_TOP_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}\" # Check to see if tclPort.h isn't already with the public headers # Don't look for tclInt.h because that resides with tcl.h in the core # sources, but the Port headers are in a different directory if test "${TEA_PLATFORM}" = "windows" -a \ -f "${ac_cv_c_tclh}/tclWinPort.h"; then result="private headers found with public headers" elif test "${TEA_PLATFORM}" = "unix" -a \ -f "${ac_cv_c_tclh}/tclUnixPort.h"; then result="private headers found with public headers" else TCL_GENERIC_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/generic\" if test "${TEA_PLATFORM}" = "windows"; then TCL_PLATFORM_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/win\" else TCL_PLATFORM_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/unix\" fi # Overwrite the previous TCL_INCLUDES as this should capture both # public and private headers in the same set. # We want to ensure these are substituted so as not to require # any *_NATIVE vars be defined in the Makefile TCL_INCLUDES="-I${TCL_GENERIC_DIR_NATIVE} -I${TCL_PLATFORM_DIR_NATIVE}" if test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use # the framework's Headers and PrivateHeaders directories case ${TCL_DEFS} in *TCL_FRAMEWORK*) if test -d "${TCL_BIN_DIR}/Headers" -a \ -d "${TCL_BIN_DIR}/PrivateHeaders"; then TCL_INCLUDES="-I\"${TCL_BIN_DIR}/Headers\" -I\"${TCL_BIN_DIR}/PrivateHeaders\" ${TCL_INCLUDES}" else TCL_INCLUDES="${TCL_INCLUDES} ${TCL_INCLUDE_SPEC} `echo "${TCL_INCLUDE_SPEC}" | sed -e 's/Headers/PrivateHeaders/'`" fi ;; esac result="Using ${TCL_INCLUDES}" else if test ! -f "${TCL_SRC_DIR}/generic/tclInt.h" ; then AC_MSG_ERROR([Cannot find private header tclInt.h in ${TCL_SRC_DIR}]) fi result="Using srcdir found in tclConfig.sh: ${TCL_SRC_DIR}" fi fi AC_SUBST(TCL_TOP_DIR_NATIVE) AC_SUBST(TCL_INCLUDES) AC_MSG_RESULT([${result}]) ]) #------------------------------------------------------------------------ # TEA_PUBLIC_TCL_HEADERS -- # # Locate the installed public Tcl header files # # Arguments: # None. # # Requires: # CYGPATH must be set # # Results: # # Adds a --with-tclinclude switch to configure. # Result is cached. # # Substitutes the following vars: # TCL_INCLUDES #------------------------------------------------------------------------ AC_DEFUN([TEA_PUBLIC_TCL_HEADERS], [ AC_MSG_CHECKING([for Tcl public headers]) AC_ARG_WITH(tclinclude, [ --with-tclinclude directory containing the public Tcl header files], with_tclinclude=${withval}) AC_CACHE_VAL(ac_cv_c_tclh, [ # Use the value from --with-tclinclude, if it was given if test x"${with_tclinclude}" != x ; then if test -f "${with_tclinclude}/tcl.h" ; then ac_cv_c_tclh=${with_tclinclude} else AC_MSG_ERROR([${with_tclinclude} directory does not contain tcl.h]) fi else list="" if test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use # the framework's Headers directory case ${TCL_DEFS} in *TCL_FRAMEWORK*) list="`ls -d ${TCL_BIN_DIR}/Headers 2>/dev/null`" ;; esac fi # Look in the source dir only if Tcl is not installed, # and in that situation, look there before installed locations. if test -f "${TCL_BIN_DIR}/Makefile" ; then list="$list `ls -d ${TCL_SRC_DIR}/generic 2>/dev/null`" fi # Check order: pkg --prefix location, Tcl's --prefix location, # relative to directory of tclConfig.sh. eval "temp_includedir=${includedir}" list="$list \ `ls -d ${temp_includedir} 2>/dev/null` \ `ls -d ${TCL_PREFIX}/include 2>/dev/null` \ `ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`" if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then list="$list /usr/local/include /usr/include" if test x"${TCL_INCLUDE_SPEC}" != x ; then d=`echo "${TCL_INCLUDE_SPEC}" | sed -e 's/^-I//'` list="$list `ls -d ${d} 2>/dev/null`" fi fi for i in $list ; do if test -f "$i/tcl.h" ; then ac_cv_c_tclh=$i break fi done fi ]) # Print a message based on how we determined the include path if test x"${ac_cv_c_tclh}" = x ; then AC_MSG_ERROR([tcl.h not found. Please specify its location with --with-tclinclude]) else AC_MSG_RESULT([${ac_cv_c_tclh}]) fi # Convert to a native path and substitute into the output files. INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tclh}` TCL_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\" AC_SUBST(TCL_INCLUDES) ]) #------------------------------------------------------------------------ # TEA_PRIVATE_TK_HEADERS -- # # Locate the private Tk include files # # Arguments: # # Requires: # TK_SRC_DIR Assumes that TEA_LOAD_TKCONFIG has # already been called. # # Results: # # Substitutes the following vars: # TK_INCLUDES #------------------------------------------------------------------------ AC_DEFUN([TEA_PRIVATE_TK_HEADERS], [ # Allow for --with-tkinclude to take effect and define ${ac_cv_c_tkh} AC_REQUIRE([TEA_PUBLIC_TK_HEADERS]) AC_MSG_CHECKING([for Tk private include files]) TK_SRC_DIR_NATIVE=`${CYGPATH} ${TK_SRC_DIR}` TK_TOP_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}\" # Check to see if tkPort.h isn't already with the public headers # Don't look for tkInt.h because that resides with tk.h in the core # sources, but the Port headers are in a different directory if test "${TEA_PLATFORM}" = "windows" -a \ -f "${ac_cv_c_tkh}/tkWinPort.h"; then result="private headers found with public headers" elif test "${TEA_PLATFORM}" = "unix" -a \ -f "${ac_cv_c_tkh}/tkUnixPort.h"; then result="private headers found with public headers" else TK_GENERIC_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/generic\" TK_XLIB_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/xlib\" if test "${TEA_PLATFORM}" = "windows"; then TK_PLATFORM_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/win\" else TK_PLATFORM_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/unix\" fi # Overwrite the previous TK_INCLUDES as this should capture both # public and private headers in the same set. # We want to ensure these are substituted so as not to require # any *_NATIVE vars be defined in the Makefile TK_INCLUDES="-I${TK_GENERIC_DIR_NATIVE} -I${TK_PLATFORM_DIR_NATIVE}" # Detect and add ttk subdir if test -d "${TK_SRC_DIR}/generic/ttk"; then TK_INCLUDES="${TK_INCLUDES} -I\"${TK_SRC_DIR_NATIVE}/generic/ttk\"" fi if test "${TEA_WINDOWINGSYSTEM}" != "x11"; then TK_INCLUDES="${TK_INCLUDES} -I\"${TK_XLIB_DIR_NATIVE}\"" fi if test "${TEA_WINDOWINGSYSTEM}" = "aqua"; then TK_INCLUDES="${TK_INCLUDES} -I\"${TK_SRC_DIR_NATIVE}/macosx\"" fi if test "`uname -s`" = "Darwin"; then # If Tk was built as a framework, attempt to use # the framework's Headers and PrivateHeaders directories case ${TK_DEFS} in *TK_FRAMEWORK*) if test -d "${TK_BIN_DIR}/Headers" -a \ -d "${TK_BIN_DIR}/PrivateHeaders"; then TK_INCLUDES="-I\"${TK_BIN_DIR}/Headers\" -I\"${TK_BIN_DIR}/PrivateHeaders\" ${TK_INCLUDES}" else TK_INCLUDES="${TK_INCLUDES} ${TK_INCLUDE_SPEC} `echo "${TK_INCLUDE_SPEC}" | sed -e 's/Headers/PrivateHeaders/'`" fi ;; esac result="Using ${TK_INCLUDES}" else if test ! -f "${TK_SRC_DIR}/generic/tkInt.h" ; then AC_MSG_ERROR([Cannot find private header tkInt.h in ${TK_SRC_DIR}]) fi result="Using srcdir found in tkConfig.sh: ${TK_SRC_DIR}" fi fi AC_SUBST(TK_TOP_DIR_NATIVE) AC_SUBST(TK_XLIB_DIR_NATIVE) AC_SUBST(TK_INCLUDES) AC_MSG_RESULT([${result}]) ]) #------------------------------------------------------------------------ # TEA_PUBLIC_TK_HEADERS -- # # Locate the installed public Tk header files # # Arguments: # None. # # Requires: # CYGPATH must be set # # Results: # # Adds a --with-tkinclude switch to configure. # Result is cached. # # Substitutes the following vars: # TK_INCLUDES #------------------------------------------------------------------------ AC_DEFUN([TEA_PUBLIC_TK_HEADERS], [ AC_MSG_CHECKING([for Tk public headers]) AC_ARG_WITH(tkinclude, [ --with-tkinclude directory containing the public Tk header files], with_tkinclude=${withval}) AC_CACHE_VAL(ac_cv_c_tkh, [ # Use the value from --with-tkinclude, if it was given if test x"${with_tkinclude}" != x ; then if test -f "${with_tkinclude}/tk.h" ; then ac_cv_c_tkh=${with_tkinclude} else AC_MSG_ERROR([${with_tkinclude} directory does not contain tk.h]) fi else list="" if test "`uname -s`" = "Darwin"; then # If Tk was built as a framework, attempt to use # the framework's Headers directory. case ${TK_DEFS} in *TK_FRAMEWORK*) list="`ls -d ${TK_BIN_DIR}/Headers 2>/dev/null`" ;; esac fi # Look in the source dir only if Tk is not installed, # and in that situation, look there before installed locations. if test -f "${TK_BIN_DIR}/Makefile" ; then list="$list `ls -d ${TK_SRC_DIR}/generic 2>/dev/null`" fi # Check order: pkg --prefix location, Tk's --prefix location, # relative to directory of tkConfig.sh, Tcl's --prefix location, # relative to directory of tclConfig.sh. eval "temp_includedir=${includedir}" list="$list \ `ls -d ${temp_includedir} 2>/dev/null` \ `ls -d ${TK_PREFIX}/include 2>/dev/null` \ `ls -d ${TK_BIN_DIR}/../include 2>/dev/null` \ `ls -d ${TCL_PREFIX}/include 2>/dev/null` \ `ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`" if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then list="$list /usr/local/include /usr/include" if test x"${TK_INCLUDE_SPEC}" != x ; then d=`echo "${TK_INCLUDE_SPEC}" | sed -e 's/^-I//'` list="$list `ls -d ${d} 2>/dev/null`" fi fi for i in $list ; do if test -f "$i/tk.h" ; then ac_cv_c_tkh=$i break fi done fi ]) # Print a message based on how we determined the include path if test x"${ac_cv_c_tkh}" = x ; then AC_MSG_ERROR([tk.h not found. Please specify its location with --with-tkinclude]) else AC_MSG_RESULT([${ac_cv_c_tkh}]) fi # Convert to a native path and substitute into the output files. INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tkh}` TK_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\" AC_SUBST(TK_INCLUDES) if test "${TEA_WINDOWINGSYSTEM}" != "x11"; then # On Windows and Aqua, we need the X compat headers AC_MSG_CHECKING([for X11 header files]) if test ! -r "${INCLUDE_DIR_NATIVE}/X11/Xlib.h"; then INCLUDE_DIR_NATIVE="`${CYGPATH} ${TK_SRC_DIR}/xlib`" TK_XINCLUDES=-I\"${INCLUDE_DIR_NATIVE}\" AC_SUBST(TK_XINCLUDES) fi AC_MSG_RESULT([${INCLUDE_DIR_NATIVE}]) fi ]) #------------------------------------------------------------------------ # TEA_PATH_CONFIG -- # # Locate the ${1}Config.sh file and perform a sanity check on # the ${1} compile flags. These are used by packages like # [incr Tk] that load *Config.sh files from more than Tcl and Tk. # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --with-$1=... # # Defines the following vars: # $1_BIN_DIR Full path to the directory containing # the $1Config.sh file #------------------------------------------------------------------------ AC_DEFUN([TEA_PATH_CONFIG], [ # # Ok, lets find the $1 configuration # First, look for one uninstalled. # the alternative search directory is invoked by --with-$1 # if test x"${no_$1}" = x ; then # we reset no_$1 in case something fails here no_$1=true AC_ARG_WITH($1, [ --with-$1 directory containing $1 configuration ($1Config.sh)], with_$1config=${withval}) AC_MSG_CHECKING([for $1 configuration]) AC_CACHE_VAL(ac_cv_c_$1config,[ # First check to see if --with-$1 was specified. if test x"${with_$1config}" != x ; then case ${with_$1config} in */$1Config.sh ) if test -f ${with_$1config}; then AC_MSG_WARN([--with-$1 argument should refer to directory containing $1Config.sh, not to $1Config.sh itself]) with_$1config=`echo ${with_$1config} | sed 's!/$1Config\.sh$!!'` fi;; esac if test -f "${with_$1config}/$1Config.sh" ; then ac_cv_c_$1config=`(cd ${with_$1config}; pwd)` else AC_MSG_ERROR([${with_$1config} directory doesn't contain $1Config.sh]) fi fi # then check for a private $1 installation if test x"${ac_cv_c_$1config}" = x ; then for i in \ ../$1 \ `ls -dr ../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \ `ls -dr ../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \ `ls -dr ../$1*[[0-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../$1*[[0-9]].[[0-9]]* 2>/dev/null` \ ../../$1 \ `ls -dr ../../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \ `ls -dr ../../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \ `ls -dr ../../$1*[[0-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../$1*[[0-9]].[[0-9]]* 2>/dev/null` \ ../../../$1 \ `ls -dr ../../../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \ `ls -dr ../../../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \ `ls -dr ../../../$1*[[0-9]].[[0-9]] 2>/dev/null` \ `ls -dr ../../../$1*[[0-9]].[[0-9]]* 2>/dev/null` \ ${srcdir}/../$1 \ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]] 2>/dev/null` \ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]]* 2>/dev/null` \ ; do if test -f "$i/$1Config.sh" ; then ac_cv_c_$1config=`(cd $i; pwd)` break fi if test -f "$i/unix/$1Config.sh" ; then ac_cv_c_$1config=`(cd $i/unix; pwd)` break fi done fi # check in a few common install locations if test x"${ac_cv_c_$1config}" = x ; then for i in `ls -d ${libdir} 2>/dev/null` \ `ls -d ${exec_prefix}/lib 2>/dev/null` \ `ls -d ${prefix}/lib 2>/dev/null` \ `ls -d /usr/local/lib 2>/dev/null` \ `ls -d /usr/contrib/lib 2>/dev/null` \ `ls -d /usr/lib 2>/dev/null` \ `ls -d /usr/lib64 2>/dev/null` \ ; do if test -f "$i/$1Config.sh" ; then ac_cv_c_$1config=`(cd $i; pwd)` break fi done fi ]) if test x"${ac_cv_c_$1config}" = x ; then $1_BIN_DIR="# no $1 configs found" AC_MSG_WARN([Cannot find $1 configuration definitions]) exit 0 else no_$1= $1_BIN_DIR=${ac_cv_c_$1config} AC_MSG_RESULT([found $$1_BIN_DIR/$1Config.sh]) fi fi ]) #------------------------------------------------------------------------ # TEA_LOAD_CONFIG -- # # Load the $1Config.sh file # # Arguments: # # Requires the following vars to be set: # $1_BIN_DIR # # Results: # # Substitutes the following vars: # $1_SRC_DIR # $1_LIB_FILE # $1_LIB_SPEC #------------------------------------------------------------------------ AC_DEFUN([TEA_LOAD_CONFIG], [ AC_MSG_CHECKING([for existence of ${$1_BIN_DIR}/$1Config.sh]) if test -f "${$1_BIN_DIR}/$1Config.sh" ; then AC_MSG_RESULT([loading]) . "${$1_BIN_DIR}/$1Config.sh" else AC_MSG_RESULT([file not found]) fi # # If the $1_BIN_DIR is the build directory (not the install directory), # then set the common variable name to the value of the build variables. # For example, the variable $1_LIB_SPEC will be set to the value # of $1_BUILD_LIB_SPEC. An extension should make use of $1_LIB_SPEC # instead of $1_BUILD_LIB_SPEC since it will work with both an # installed and uninstalled version of Tcl. # if test -f "${$1_BIN_DIR}/Makefile" ; then AC_MSG_WARN([Found Makefile - using build library specs for $1]) $1_LIB_SPEC=${$1_BUILD_LIB_SPEC} $1_STUB_LIB_SPEC=${$1_BUILD_STUB_LIB_SPEC} $1_STUB_LIB_PATH=${$1_BUILD_STUB_LIB_PATH} $1_INCLUDE_SPEC=${$1_BUILD_INCLUDE_SPEC} $1_LIBRARY_PATH=${$1_LIBRARY_PATH} fi AC_SUBST($1_VERSION) AC_SUBST($1_BIN_DIR) AC_SUBST($1_SRC_DIR) AC_SUBST($1_LIB_FILE) AC_SUBST($1_LIB_SPEC) AC_SUBST($1_STUB_LIB_FILE) AC_SUBST($1_STUB_LIB_SPEC) AC_SUBST($1_STUB_LIB_PATH) # Allow the caller to prevent this auto-check by specifying any 2nd arg AS_IF([test "x$2" = x], [ # Check both upper and lower-case variants # If a dev wanted non-stubs libs, this function could take an option # to not use _STUB in the paths below AS_IF([test "x${$1_STUB_LIB_SPEC}" = x], [TEA_LOAD_CONFIG_LIB(translit($1,[a-z],[A-Z])_STUB)], [TEA_LOAD_CONFIG_LIB($1_STUB)]) ]) ]) #------------------------------------------------------------------------ # TEA_LOAD_CONFIG_LIB -- # # Helper function to load correct library from another extension's # ${PACKAGE}Config.sh. # # Results: # Adds to LIBS the appropriate extension library #------------------------------------------------------------------------ AC_DEFUN([TEA_LOAD_CONFIG_LIB], [ AC_MSG_CHECKING([For $1 library for LIBS]) # This simplifies the use of stub libraries by automatically adding # the stub lib to your path. Normally this would add to SHLIB_LD_LIBS, # but this is called before CONFIG_CFLAGS. More importantly, this adds # to PKG_LIBS, which becomes LIBS, and that is only used by SHLIB_LD. if test "x${$1_LIB_SPEC}" != "x" ; then if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes" ; then TEA_ADD_LIBS([\"`${CYGPATH} ${$1_LIB_PATH}`\"]) AC_MSG_RESULT([using $1_LIB_PATH ${$1_LIB_PATH}]) else TEA_ADD_LIBS([${$1_LIB_SPEC}]) AC_MSG_RESULT([using $1_LIB_SPEC ${$1_LIB_SPEC}]) fi else AC_MSG_RESULT([file not found]) fi ]) #------------------------------------------------------------------------ # TEA_EXPORT_CONFIG -- # # Define the data to insert into the ${PACKAGE}Config.sh file # # Arguments: # # Requires the following vars to be set: # $1 # # Results: # Substitutes the following vars: #------------------------------------------------------------------------ AC_DEFUN([TEA_EXPORT_CONFIG], [ #-------------------------------------------------------------------- # These are for $1Config.sh #-------------------------------------------------------------------- # pkglibdir must be a fully qualified path and (not ${exec_prefix}/lib) eval pkglibdir="[$]{libdir}/$1${PACKAGE_VERSION}" if test "${TCL_LIB_VERSIONS_OK}" = "ok"; then eval $1_LIB_FLAG="-l$1${PACKAGE_VERSION}${DBGX}" eval $1_STUB_LIB_FLAG="-l$1stub${PACKAGE_VERSION}${DBGX}" else eval $1_LIB_FLAG="-l$1`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}" eval $1_STUB_LIB_FLAG="-l$1stub`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}" fi $1_BUILD_LIB_SPEC="-L`pwd` ${$1_LIB_FLAG}" $1_LIB_SPEC="-L${pkglibdir} ${$1_LIB_FLAG}" $1_BUILD_STUB_LIB_SPEC="-L`pwd` [$]{$1_STUB_LIB_FLAG}" $1_STUB_LIB_SPEC="-L${pkglibdir} [$]{$1_STUB_LIB_FLAG}" $1_BUILD_STUB_LIB_PATH="`pwd`/[$]{PKG_STUB_LIB_FILE}" $1_STUB_LIB_PATH="${pkglibdir}/[$]{PKG_STUB_LIB_FILE}" AC_SUBST($1_BUILD_LIB_SPEC) AC_SUBST($1_LIB_SPEC) AC_SUBST($1_BUILD_STUB_LIB_SPEC) AC_SUBST($1_STUB_LIB_SPEC) AC_SUBST($1_BUILD_STUB_LIB_PATH) AC_SUBST($1_STUB_LIB_PATH) AC_SUBST(MAJOR_VERSION) AC_SUBST(MINOR_VERSION) AC_SUBST(PATCHLEVEL) ]) #------------------------------------------------------------------------ # TEA_PATH_CELIB -- # # Locate Keuchel's celib emulation layer for targeting Win/CE # # Arguments: # none # # Results: # # Adds the following arguments to configure: # --with-celib=... # # Defines the following vars: # CELIB_DIR Full path to the directory containing # the include and platform lib files #------------------------------------------------------------------------ AC_DEFUN([TEA_PATH_CELIB], [ # First, look for one uninstalled. # the alternative search directory is invoked by --with-celib if test x"${no_celib}" = x ; then # we reset no_celib in case something fails here no_celib=true AC_ARG_WITH(celib,[ --with-celib=DIR use Windows/CE support library from DIR], with_celibconfig=${withval}) AC_MSG_CHECKING([for Windows/CE celib directory]) AC_CACHE_VAL(ac_cv_c_celibconfig,[ # First check to see if --with-celibconfig was specified. if test x"${with_celibconfig}" != x ; then if test -d "${with_celibconfig}/inc" ; then ac_cv_c_celibconfig=`(cd ${with_celibconfig}; pwd)` else AC_MSG_ERROR([${with_celibconfig} directory doesn't contain inc directory]) fi fi # then check for a celib library if test x"${ac_cv_c_celibconfig}" = x ; then for i in \ ../celib-palm-3.0 \ ../celib \ ../../celib-palm-3.0 \ ../../celib \ `ls -dr ../celib-*3.[[0-9]]* 2>/dev/null` \ ${srcdir}/../celib-palm-3.0 \ ${srcdir}/../celib \ `ls -dr ${srcdir}/../celib-*3.[[0-9]]* 2>/dev/null` \ ; do if test -d "$i/inc" ; then ac_cv_c_celibconfig=`(cd $i; pwd)` break fi done fi ]) if test x"${ac_cv_c_celibconfig}" = x ; then AC_MSG_ERROR([Cannot find celib support library directory]) else no_celib= CELIB_DIR=${ac_cv_c_celibconfig} CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'` AC_MSG_RESULT([found $CELIB_DIR]) fi fi ]) # Local Variables: # mode: autoconf # End: man/000077500000000000000000000000001242365656200116445ustar00rootroot00000000000000man/man.macros000066400000000000000000000114171242365656200136310ustar00rootroot00000000000000'\" The definitions below are for supplemental macros used in Tcl/Tk '\" manual entries. '\" '\" .AP type name in/out ?indent? '\" Start paragraph describing an argument to a library procedure. '\" type is type of argument (int, etc.), in/out is either "in", "out", '\" or "in/out" to describe whether procedure reads or modifies arg, '\" and indent is equivalent to second arg of .IP (shouldn't ever be '\" needed; use .AS below instead) '\" '\" .AS ?type? ?name? '\" Give maximum sizes of arguments for setting tab stops. Type and '\" name are examples of largest possible arguments that will be passed '\" to .AP later. If args are omitted, default tab stops are used. '\" '\" .BS '\" Start box enclosure. From here until next .BE, everything will be '\" enclosed in one large box. '\" '\" .BE '\" End of box enclosure. '\" '\" .CS '\" Begin code excerpt. '\" '\" .CE '\" End code excerpt. '\" '\" .VS ?version? ?br? '\" Begin vertical sidebar, for use in marking newly-changed parts '\" of man pages. The first argument is ignored and used for recording '\" the version when the .VS was added, so that the sidebars can be '\" found and removed when they reach a certain age. If another argument '\" is present, then a line break is forced before starting the sidebar. '\" '\" .VE '\" End of vertical sidebar. '\" '\" .DS '\" Begin an indented unfilled display. '\" '\" .DE '\" End of indented unfilled display. '\" '\" .SO '\" Start of list of standard options for a Tk widget. The '\" options follow on successive lines, in four columns separated '\" by tabs. '\" '\" .SE '\" End of list of standard options for a Tk widget. '\" '\" .OP cmdName dbName dbClass '\" Start of description of a specific option. cmdName gives the '\" option's name as specified in the class command, dbName gives '\" the option's name in the option database, and dbClass gives '\" the option's class in the option database. '\" '\" .UL arg1 arg2 '\" Print arg1 underlined, then print arg2 normally. '\" '\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages. .if t .wh -1.3i ^B .nr ^l \n(.l .ad b '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ta \\n()Au \\n()Bu .ie !"\\$3"" \{\ \&\\$1 \\fI\\$2\\fP (\\$3) .\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n .\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. .AS Tcl_Interp Tcl_CreateInterp in/out '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ .\" Draw four-sided box normally, but don't draw top of .\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .if !"\\$2"" .br .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ .\" Draw three-sided box if this is the box's first page, .\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .nf .sp .. '\" # DE - end display .de DE .fi .RE .sp .. '\" # SO - start of list of standard options .de SO .SH "STANDARD OPTIONS" .LP .nf .ta 4c 8c 12c .ft B .. '\" # SE - end of list of standard options .de SE .fi .ft R .LP See the \\fBoptions\\fR manual entry for details on the standard options. .. '\" # OP - start of full description for a single option .de OP .LP .nf .ta 4c Command-Line Name: \\fB\\$1\\fR Database Name: \\fB\\$2\\fR Database Class: \\fB\\$3\\fR .fi .IP .. '\" # CS - begin code excerpt .de CS .RS .nf .ta .25i .5i .75i 1i .. '\" # CE - end code excerpt .de CE .fi .RE .. .de UL \\$1\l'|0\(ul'\\$2 .. man/xotclsh.1000066400000000000000000000064701242365656200134210ustar00rootroot00000000000000'\" '\" XOTcl - Extended OTcl '\" Copyright (C) 1999-2008 Uwe Zdun '\" Copyright (C) 1999-2014 Gustaf Neumann '\" '\" .so man.macros .TH xotclsh 1 "" XOTcl "XOTcl Applications" .BS .SH NAME xotclsh \- Tcl Shell containing object-oriented scripting language XOTcl .SH SYNOPSIS \fBxotclsh\fR \fI?filename arg arg ...?\fR .BE .SH DESCRIPTION .PP \fBxotclsh\fR is a shell-like application that reads XOTcl commands from its standard input or from a file and evaluates them. Similarly as the relation between \fBtclsh\fR and \fBwish\fR, \fBxowish\fR provides all functionality of xotclsh and provides as well predefined support for TK widgets. .PP XOTcl (XOTcl, pronounced exotickle) is an object-oriented scripting language based on MIT's OTcl. It is intended as a value added replacement of OTcl. .PP Scripting languages, such as Tcl, are designed for glueing components together, provide features such as dynamic extensibility and dynamic typing with automatic conversion, that make them well suited for rapid application development. .PP The object system of XOTcl enables a user to to define objects, classes, and meta-classes. Classes are special objects with the purpose of managing other objects. ``Managing'' means that a class controls the creation and destruction of its instances and that it contains a repository of methods accessible for the instances. Every object may be enhanced with object-specific methods. XOTcl supports single and multiple inheritance. All object-class and class-class relationships in XOTcl are introspectable and can be dynamically changed at arbitrary times. Ambiguities in name resolution of methods are avoided through method chaining through "next", which does not require explicit method naming. .PP XOTcl combines the ideas of scripting and object-orientation in a way that preserves the benefits of both of them. It is equipped with several new language constructs that help building and managing complex systems. We added the following support: .PP \fIDynamic Object Aggregations\fR, to provide dynamic aggregations through nested namespaces (objects). .PP \fINested Classes\fR, to reduce the interference of independently developed program structures. .PP \fIAssertions\fR, to reduce the interface and the reliability problems caused by dynamic typing and, therefore, to ease the combination of many components. .PP \fIMeta-data\fR, to enhance self-documentation of objects and classes. .PP \fIPer-object mixins\fR, as a means to give an object dynamically access to the methods of one or several supplemental classes. .PP \fIPer-class mixins\fR, as a means to give all instances of an class dynamically access to the methods of one or several supplemental classes. .PP \fIFilters\fR as a means of abstractions over method invocations to implement large program structures, like design patterns. XOTcl provides a value-added replacement of Tcl package loading providing integration with object-oriented constructs and tracking/tracing of component loading. .SH VARIABLES .PP \fBxotclsh\fR sets all variables that \fBtclsh\fR sets, and additionally the following variables: .TP 15 \fB::xotcl::version\fR XOTcl version number. .TP 15 \fB::xotcl::confdir\fR Directory for XOTcl configuration. .TP 15 \fB::xotcl::logdir\fR Directory where logfiles are placed. .PP .SH KEYWORDS argument, interpreter, prompt, script file, shell man/xowish.1000066400000000000000000000063521242365656200132550ustar00rootroot00000000000000'\" '\" XOTcl - Extended OTcl '\" Copyright (C) 1999-2008 Uwe Zdun '\" Copyright (C) 1999-2014 Gustaf Neumann '\" '\" .so man.macros .TH xowish 1 "" XOWish "XOTcl Applications" .BS '\" Note: do not modify the .SH NAME line immediately below! .SH NAME xowish \- Graphical shell containing object-oriented scripting language XOTcl .SH SYNOPSIS \fBxowish\fR ?\fIfileName arg arg ...\fR? .BE .SH DESCRIPTION .PP \fBxowish\fR is a shell-like application that reads XOTcl commands from its standard input or from a file and evaluates them. In addition to \fBxotclsh\fR it provides graphical user interface support for TK widgets. .PP XOTcl (XOTcl, pronounced exotickle) is an object-oriented scripting language based on MIT's OTcl. It is intended as a value added replacement for OTcl. .PP Scripting languages, like Tcl, are designed for glueing components together, provide features like dynamic extensibility and dynamic typing with automatic conversion, that make them well suited for rapid application development. .PP The basic object system of XOTcl is adopted from OTcl. The object system enables us to define objects, classes, and meta-classes. Classes are special objects with the purpose of managing other objects. ``Managing'' means that a class controls the creation and destruction of its instances and that it contains a repository of methods accessible for the instances. Every object may be enhanced with object-specific methods. XOTcl supports single and multiple inheritance. All relationships in XOTcl, including class and superclass relationships, are completely dynamic and can be introspected. Through method chaining without explicit naming of the intended method, ambiguities in name resolution of methods are avoided. This way a shadowed method can be ``mixed into'' the execution of the current method. .PP XOTcl combines the ideas of scripting and object-orientation in a way that preserves the benefits of both of them. It is equipped with several new language functionalities that help building and managing complex systems. We added the following support: .PP Dynamic Object Aggregations, to provide dynamic aggregations through nested namespaces (objects). .PP Nested Classes, to reduce the interference of independently developed program structures. .PP Assertions, to reduce the interface and the reliability problems caused by dynamic typing and, therefore, to ease the combination of many components. .PP Meta-data, to enhance self-documentation of objects and classes. .PP Per-object mixins, as a means to improve flexibility of mixin methods by giving an object access to several different supplemental classes, which may be changed dynamically. .PP Per-class mixins, as a means to improve flexibility of mixin methods to a class, all instances of the class have access to the mixed in methods like for multiple inheritance, but without the need of intersection classes. .PP Filters as a means of abstractions over method invocations to implement large program structures, like design patterns. .PP Dynamic Component Loading XOTcl integrates the Tcl package loading with architectrual support for integration with object-oriented constructs. Moreover, it provides tracking/tracing of component loading. .PP .SH KEYWORDS argument, interpreter, prompt, script file, shell nsf.nxd000066400000000000000000001227101242365656200123750ustar00rootroot00000000000000# -*- Tcl -*- # @package nsf # # The package provides a basic set of primitives (Tcl commands) to # support language-oriented programming based on the Next Scripting # Framework. These primitives are used to define one or multiple # object systems within a single system. The NSF primitives are # primarily meant for the designer of an object system. Application # developers should use the functionality provided by the object # systems they are using (eg. XOTcl or NX). # # @require Tcl # @version 1.0.0a # @namespace ::nsf # @glossary metaclass # # A metaclass is a certain kind of <<@gls class>> that manages # classes. The instances of metaclasses are classes. # # @pretty_name Metaclass # @pretty_plural Metaclasses # @glossary class # # A class is a certain kind of <<@gls object>> that is responsible for # the life-cycle management of other objects (creation, # initialization, destruction) and which serves as a method # respository for objects (typically along a class hierarchy). In the # object systems of XOTcl and NX, where objects might have their own # methods, the class objects might have as well methods applicable # only to the class object. We refer to these methods as class-object # specific methods, sometimes these methods are called "static methods". # # @pretty_name Class # @pretty_plural Classes # @glossary baseclass # # A base class is the most general <<@gls class>> (synonym: common # root class) of an object system. All objects of an object system # are direct or indirect instances of the base class. # # @pretty_name Base class # @pretty_plural Base classes # @glossary basemetaclass # # A base metaclass is the most general <<@gls metaclass>> of an object # system. All classes are direct or indirect instances of the base # metaclass. # # @pretty_name Base metaclass # @pretty_plural Base metaclasses # @glossary object # # An object is the basic entity of an object system. Every object is # the instance of a <<@gls class>> and might contain variables (called # instance variables). In the object systems of XOTcl and NX, objects # might have as well their own methods (called object specific # methods). # # @pretty_name Object # @pretty_plural Objects # @glossary alias # # An alias is an alternate name for some artefact. In NSF, the term # "alias" is an abbreviation for a "method alias", which allows to # reuse some command or method unter a certain name. This way, one can # e.g. reuse Tcl commands like <> or <> as # methods for object and classes. # # @pretty_name Alias # @pretty_plural Aliases # @glossary methodhandle # # A method handle is a fully qualified reference to a method. Since # methods with identical names can be defined on object and classes, # method names are subject to resolving. In contrary to method names, # method handles provide unique identifier to methods. # # @pretty_name Method handle # @pretty_plural Method handles # @glossary callframe # # A call frame (sometimes called: activation record) is an entry on the # execution stack, keeping information about the invoked functions and # methods. NSF uses the extension mechansims of Tcl to provide its # own types of callframes, which are different for scripted and C # implemented methods. # # @pretty_name Callframe # @pretty_plural Callframes # @glossary assertion # # An assertion is an assumption that the developer assumes to hold (to # be true). Following a Design-by-Contract approach, a developer may # define different kinds of assertions on object, classes, and # methods. NSF assertions support object and class invariants and # method asstions (pre- and post-conditions). # # @pretty_name Assertion # @pretty_plural Assertions # @glossary forwarder # # A forwarder (shorthand for a "forwarder method") is a delegation # mechanism to realize a variety of programming techniques. A # forwarder is a C implemented method that redirects an invocation # with a potentially different argument list to some implementation # (delegation target). Delegation targets might be methods of the same # or different objects, but these might be as well arbitrary Tcl # commands. Like <<@glspl alias>>, method forwarders are a # means of reuse. On contrary to aliases method forwarders allow the # modification of the argument vector, but involve more calling # overhead. Method forwarder do not extend the expressability of the # language, since everything performed by a forwarder could be # implemented as well in a scripted method. However, a forwarder is # more efficient and expresses the intention of the developer more # clearly. # # @pretty_name Forwarder # @pretty_plural Forwarders # @glossary mixin_class # # A mixin class is a class that provides its method-set to objects and # classes outside of the intrinsic class hierarchy (the class # hierarchy defined by the is-instance-of and superclass # relationship). A mixin class registered on an object is called a # per-object mixin, a mixin class registed on a class is called an # per-class mixin. Technically, a mixin class is realized by an # ordinary class which is registered via a dedicated mixin relation. # # @pretty_name Mixin class # @pretty_plural Mixin classes # @glossary filter # # A filter is a method called before and after every method # invocation. Filters can be registered on the object level # (per-object filters) and on the class level (per-class filters). # # @pretty_name Filter # @pretty_plural Filters # @glossary slotobject # # A slot object is an <<@gls object>> of the class <<@class # ::nx::Slot>> that manages certain properties of objects. The most # prominent properties are instance variables (see <<@glspl # attribute>>). Typical examples for such meta-data are # default-values, value checker, linkage to database table/attributes, # labels for pretty-printing, widget specifications for data entry, # etc. # # @pretty_name Slot object # @pretty_plural Slot objects # @glossary attribute # # An attribute describes properties of instance variables and might be # defined for a class or an object. Attributes are realized via # <<@glspl slotobject>>. When an instance of the class, on which the # attribute is defined, is created, the default value of the slot # object is used as initial value, and same-named accessor functions # are created automatically (unless this is suppressed). # # @pretty_name Attribute # @pretty_plural Attributes # xxxx-bis-hierher ####################################################################### # @command ::nsf::configure # # A top-level configuration facility which allows you modify # properties of the a given object system for the scope of an entire # '''interp'''. # @command.sub-command {configure debug} # # The NSF runtime provides you with the <<@command ::nsf::log>> command to # drop logging statements, filtered by custom debug levels. To # activate a certain debug level, use this configuration facility. The # runtime defaults to debug level '''0'''. A debug level greater than # '''0''' increases the runtime's native verbosity (i.e., selected # warnings and debugging-critical information will be displayed). # # @parameter level:optional If provided, sets the runtime's debug # level. If omitted, you obtain the # debug level currently active. # @command.sub-command {configure filter} # # Allows turning on or off filters globally for the current # interpreter. By default, the filter state is turned off. This # function returns the old filter state. This filterless '''interp''' # state is needed for the serializer which should introspect and stream the # objects and classes without being affected by active filter. # # @parameter toggle Accepts either '''on''' or '''off''' # @return The current filter activation state # @command.sub-command {configure softrecreate} # # Allows controlling the scheme applied when recreating an object or a # class. By default, it is set to '''off'''. This means that the # object/class is destroyed and all relations # (e.g. subclass/superclass) to other objects/classes are revoked as # well. If softrecreate is set to '''on''', the object is re-set, but not # destroyed, the relations are # kept. # # A "soft" recreation is important for e.g. reloading a file with # class definitions (e.g. when used in OpenACS with file watching and # reloading). With softrecreate set, it is not necessary to recreate # dependent subclasses etc. Consider the example of a class hierarchy # '''A <- B <- C'''. Without '''softrecreate''' set, a reload of # '''B''' means first a destroy of B, leading to '''A <- C''', and # instances of '''B''' are re-classed to the object system's root # class. When softrecreate is set, the class hierarchy remains # untouched. # # @parameter toggle Accepts either '''on''' or '''off''' # @return The current toggle value # @command.sub-command {configure objectsystems} # # A mere introspection subcommand. It gives you the top level of the # current object system, i.e., the ruling root class and the root # metaclass. It is the introspective counterpart of <<@command # objectsystem::create>>. # # @return A list of currently specified object systems. Each # sublist gives the pair of root class and # root metaclass and (if available) the mappings of # system hooks to system methods. # @command.sub-command {configure keepcmds} # # Usually, initblocks of objects (actually configure parameter of type cmd) # are not preserved. This is somewhat in contrast to '''proc''' and '''method''' # bodies. If you need these values to be preserved for later introspection and # processing (as in the "Next" documentation system), set this option # to '''true'''. Then, the initcmd scripts are retained as a # particular object variable ('''__cmd(__initblock)''') of classes and # objects. It defaults to '''false'''. # # @parameter value:boolean Either '''true''' or '''false''' # @return The current setting # @command.sub-command {configure checkarguments} # # NSF provides optional type checkers for arguments to method # invocations, based on method parameter specifications. This # configuration options lets you activate or deactive type checking on # method arguments for the scope of a given '''interp'''. # # @parameter value:boolean Either '''true''' or '''false''' # @return The current setting, either '''1''' or '''0''' # @command.sub-command {configure checkresults} # # NSF provides optional type checkers for result values of method # executions. For details, see <<@command ::nsf::method::property>>. This # configuration options lets you activate or deactive this type # checking for the scope of a given '''interp'''. # # @parameter value:boolean Either '''true''' or '''false''' # @return The current setting, either '''1''' or '''0''' ######################################################################## # @command ::nsf::current # # An introspective command which allows you to explore the callstack # from within the scope of a method (or a proc bound to an object via # '''alias'''). If executed without specifying a subcommand, # i.e. '''[current]''', it defaults to <<@command.command "current # object">>. While '''current''' operates on the Tcl callstack, it is # aware of object-specific callstack and frame information. To some # extent, this object introspection protocol can be approximated at # the script level by instrumenting '''[info frame]'''. # # If invoked outside of an object's scope (e.g., an ordinary proc, the # global namespace), it fails and reports '''No current object'''. # # It comes with a variety of sub-commands to query the object-specific # callstack information available. See below. # # @sub-command class Returns the name of the class holding the # currently executing per-class method, if and only if called from # within a per-class method. Note, that this method-owning class may # be different to the class of the current object. If called from # within a per-object method, it returns an empty string. # # @sub-command method Returns the name of the currently executing method. # # @sub-command callingclass Returns the name of the class which is # calling into the executing method. # # @sub-command callingobject Returns the name of the object which is # calling into the executing method. # # @sub-command calledclass Returns the name of the class that holds # the originally (and now shadowed) target method (applicable in # mixin classes and filters). # # @sub-command calledmethod Returns the name of the target method # (applicable in a filter only). # # @sub-command isnextcall Returns 1 if the executing method was # invoked via <<@command ::nsf::next>>, 0 otherwise. # # @sub-command next Returns the name of the method next on the # precedence path as a string. # # @sub-command filterreg In a method serving as active filter, # returns the name of the object (class) on which the method is # registered as a filter. # # @sub-command callinglevel Resolves the callstack level which represents # the originating invocation into the currently executing method. Levels # of indirection (e.g., filters) and method combination along the # class linearisation path ('''next''') are ignored. The callstack is # returned as an absolute level number (# followed by a digit). The # level number returned can be directly used as the first argument to # '''uplevel''' or '''upvar''' calls. See also <<@command.command # "current activelevel">> # # @sub-command activelevel Returns the actual callstack level calling # into the executing method. The active might correspond the # '''callinglevel''', but this is not necessarily the case. The # '''activelevel''' counts <<@command ::nsf::next>> call. The level # is returned in a form so that it can be used as first argument in # '''uplevel''' or '''upvar'''. # @command.command {current object} # # The default sub-command returns the name of the object currently # active on the callstack. ######################################################################## # @command ::nsf::deprecated # # A helper command which prints a notice that a given command is # deprecated, optionally pointing to its successor, if any. # # @parameter what Identifies the command type (proc, method, ...) # @parameter oldCmd Gives the name of the deprecated command # @parameter newCmd:optional Points to the successor command ######################################################################## # @command ::nsf::dispatch # # The command gives script-level access to the NSF infrastructure for # method dispatch. It can be used to bypass standard method # dispatching (as defined by a given object system) and to devise your # own method dispatch scheme. Likewise, it provides means to create # programs which operate over several object systems and their method # dispatching schemes (e.g., a serializer, a program structure # analyzer). # # @parameter object Indicates the name of the receiving object # @parameter -frame Denotes the type of <<@gls callframe>> to # be stacked upon invoking the alias # method. Permissible options are: # '''method''', '''object''', # '''default''' # @parameter command Specifies the receiving command, to be invoked in # the callframe scope of the receiver # object. The receiving command can be # specified as a <<@gls methodhandle>> # @parameter args The actual invocation arguments, to be # passed to the receiving command. # @return The result value as returned by the receiving # command ######################################################################## # @command ::nsf::exithandler # # This command is used to register and manage an application-level # handler which is to be executed upon shutting down a NSF-enabled # '''interp'''. The handler takes the form of a script which is # evaluated early upon calling <<@command ::nsf::finalize>>. # # @parameter args A variable argument vector, identifying a # subcommand ('''set''', '''get''', '''unset''') # and an optional value which carries the # handler script to be set. ######################################################################## # @command ::nsf::finalize # # The command manually triggers the disciplined shutdown and # destruction of an entire object system in a given # '''interp'''. First, the application-level exit handler script is # evaluated. You may provide your own exit handler using <<@command # exithandler>>. Second, the object system itself is cleared and # removed from the '''interp'''. This discrete destruction trigger is # convenient to control an object system's lifespan in environments # with more complex '''interp''' lifecycles (e.g., in threaded Tcl # programs, '''interp''' reuse, etc.). ######################################################################## # @command ::nsf::interp # # A convenience wrapper command around '''[interp]'''. When '''[interp # create]''' is intercepted, the resulting slave '''interp''' is # equipped with the NSF extension by calling the extension's # '''*_Init()''' function on the new '''interp'''. Roughly, it # corresponds to the following scripted version: # # ''' # ::proc ::nsf::interp {subcmd args} { # set r [uplevel [list ::interp $subcmd {*}$args]] # switch -- $subcmd { # create { # $r eval [list package req nsf] # } # } # return $r # } # ''' # # @parameter name The name of the slave '''interp''' # @parameter args A variable argument vector, # carrying subcommand-specific arguments. ######################################################################## # @command ::nsf::is # # The command tests whether a given string is a valid according to a # value constraint. Depending on the nature of the tested string value # (i.e., a Tcl value structure, an NSF object, or an NSF class), you # can express various constraint types. # # 1. Value constraints on Tcl value representations ('''Tcl_Obj'''), # i.e. arbitrary strings: # # You may use any character class provided by '''[string is]''', e.g.: # ''' # ::nsf::is boolean|double|false|integer|list|lower|true|upper|wideinteger /value/ # ''' # A Tcl value may be tested whether it represents an NSF object (see # also <<@command ::nsf::object::exists>>): # ''' # ::nsf::is object /value/ # ''' # 2. Value constraints on NSF objects: # # Objects may be tested whether they object::qualify as a class and whether # they have a particular object-type: # ''' # ::nsf::is object,type=/class/ /object/ # ::nsf::is class /object/ # ''' # 3. Value constraints on NSF classes: # # Classes can be tested for their relationship status, e.g., whether # they serve as metaclass for other classes: # ''' # ::nsf::is metaclass /class/ # ''' # # @parameter -complain:switch If set, a constraint violation will # result in a Tcl error being fired. # @parameter constraint The actual value constraint expression # @parameter value The value to test # @return In the non-complaining mode # ('''-complain''' has been omitted), a # pass is signalled by '''1''', a # violation by '''0'''. In complaining # mode, a Tcl error is raised in case of # a constraint violation along with a # '''0''' return value. ######################################################################## # @command ::nsf::log # # Provides script-level access to NSF's logging facilities. You may # specify logging statements to be filtered by custom-defined debug # levels. By convention, level '''0''' represents the lowest verbosity # level. To set the actual debug level, use <<@command.command # {configure debug}>>. # # @parameter level A numeric debugging level, e.g.: '''0''', '''1''', # ... # @parameter msg The logging statement to display ######################################################################## # @command ::nsf::method::alias # # A factory command which creates an <<@gls alias>> method for an # object or a class. The aliased (or target) command so appears as a # member of the method record of the object or the instances of the # alias-defining class. Beware, method <<@glspl alias>> and this factory # command are not related to Tcl's interpreter aliases and their # '''interp alias''' helper. # # @parameter object The target object to own the alias method # @parameter -per-object If the target object is a <<@gls # class>>, one can specify the owner # scope (i.e., per-object or per-class) # of the alias method # @parameter methodName:optional The name of the alias method. Under # this name, the alias method is listed # in the method signature interface. The # alias method name and the target name # so can differ from each other. # @parameter -frame Denotes the type of <<@gls callframe>> to # be stacked upon invoking the alias # method. Permissible options are: # '''method''', '''object''', # '''default''' # @parameter cmdName The alias source as a <<@gls methodhandle>> # @return The <<@gls methodhandle>> representing the # alias method just created ######################################################################## # @command ::nsf::method::assertion # # A helper command to (a) define assertion expressions and (b) to # selectively activate checking of certain assertion types on a given # object. # # @parameter object Denotes an object or class as the # subject of assertion checking. It also # stores the assertion expressions to be # evaluated. # @parameter assertionsubcmd The subcommand '''check''' allows you to # specify the category of assertions to # be checked, while '''object-invar''' and # '''class-invar''' actually set # assertion expressions in terms of # object or class invariants. # @parameter arg:optional Either: (a) subcommand '''check''': A list # of assertion checks to activate # (permissible options: '''all''', # '''pre''', '''post''', # '''object-invar''', # '''class-invar'''); if omitted, the # currently active assertion type is # returned. # (b) subcommands '''object-invar''' and # '''class-invar''': A list of assertion # expressions (in the sense of Tcl # expression statements). A list of # assertion expressions is evaluated # under a logical AND. ######################################################################## # @command ::nsf::method::create # # A factory command for defining methods on objects and classes. The # command creates (and replaces any existing) method by a given name. # # @parameter object The name of the method-owning # object or class # @parameter -inner-namespace:switch If requested, the owner object # is attached a per-object # namespace which becomes the # current namespace for # evaluating the method body (in # the sense of '''[namespace # current]'''). # @parameter -per-object:switch When defined on a class, the # method is owned by and invoked # upon the class object itself # (rather than its instance # objects). # @parameter -public:switch Sets the call protection # @parameter name Name of the method # @parameter args The formal definition of method parameters # @parameter body The body script to be # evaluated upon invoking on the method # @parameter -precondition A list of <<@glspl assert>> (Tcl # expressions) which must hold # before executing the method # body. See also <<@command # assertion>>. # @parameter -postcondition A list of <<@glspl assert>> # (Tcl expressions) which must # hold after having evaluated # the method, provided that all # preconditions evaluated to # true. Beware that, if # specified, preconditions must # also be provided. See also # <<@command ::nsf::method::assertion>>. # @return The <<@gls methodhandle>> representing the # newly created method ######################################################################## # @command ::nsf::method::forward # # A factory command for defining <<@glspl forwarder>> methods on objects # and classes. The command creates (and replaces any existing) # forwarder method by a given name, delegating the call to a target # command. The argument vector passed to the forward target can be # manipulated by providing a filtering expresion in '''args''', using # the following protocol: # # # '''%proc''' substituted by name of the forwarder method # # '''%self''' substitute by name of the object # # '''%1''' substitute by first argument of the invocation # # ''' {%@POS value} ''' substitute the specified value in the # argument list on position POS, where POS can be a positive or # negative integer or end. Positive integers specify the position # from the begin of the list, while negative integer specify the # position from the end. # # ''' {%argclindex LIST} ''' take the nth argument of the specified # list as substitution value, where n is the number of arguments # from the invocation. # # '''%%''' a single percent. # # '''%Tcl-command''' The command to be executed; the result of the # command evaluation is substituted for the placeholding statement. # # Additionally, each argument can be prefixed by the positional prefix # %@POS (note the delimiting space at the end) that can be used to # specify an explicit position. POS can be a positive or negative # integer or the word end. The positional arguments are evaluated from # left to right and should be used in ascending order. # # @parameter object The name of the forward-owning # object or class. The forwarder # appears as member of the # object's or class' method # record. # @parameter -per-object:switch When defined on a class, the # method is owned by and invoked # upon the class object itself # (rather than its instance # objects). # @parameter method The name of the forwarder method # @parameter -default A two-valued list, specifying # the a conditional selection of # default forward targets (i.e., # methods). Beware that this # option works only in # connection with '''%1'''. If # more than one argument is # provided at the forwarder's # call site, the method at # position 2 becomes the call # target. # @parameter -earlybinding:switch Provided that the forward # specification resolves to a # Tcl command, the forward # method can be bound to its # target at definition time. # @parameter -methodprefix Indicates a string prefix # prepended to the second # argument issued at the # forwarder's call site. # @parameter -objframe:switch If provided, the forward # specification will be # evaluated in the context of # the forward-owning object. # @parameter -onerror Declare a error handler proc # (its fully qualified name) to # be executed upon sensing a Tcl # error during delegation. # @parameter -verbose:switch If requested, # @parameter target:optional The Tcl command or method name # representing the forward target. # @parameter args The remainder of the forward # specification ######################################################################## # @command ::nsf::method::property # # The command to configure certain semantic properties of methods, # previously defined by either <<@command ::nsf::method::create>>, <<@command # ::nsf::method::forward>>, or <<@command ::nsf::method::alias>>. # # The following property classes can be set on methods: # # 1. '''class-only''': Marks a method as a class-only behavioural # feature, that is, the method will only be # executed if called on a class. # # 2. '''call-protected''': A call-protected method is only visible and # accessible for self-calls and calls from # within a generalisation/specialisation # hierarchy. See <<@gls cprotection>>. # # 3. '''redefine-protected''': A redefine-protected method cannot be # replaced on the the owning object or # class. In order to alter or refine # behaviour, the method must be # overwritten or overloaded. # # 4. '''returns''': Specify a value constraint on the return value # of the method. For activating and deactivating return value # validation, see <<@command ::nsf::configure>>. # # 5. '''slotcontainer''': ... # # 6. '''slotobj''': ... # # @parameter object The method-owning object or class # @parameter -per-object:switch When called for a class, a # property of a class object's # method is set (if existant). # @parameter methodName The name of the method to configure # @parameter methodproperty The property class to set or # unset. Accepts one of: # '''class-only''', # '''call-protected''' # '''redefine-protected''', # '''returns''', # '''slotcontainer''', # '''slotobj'''. # @parameter value:optional If provided, '''1''' sets the # propery, '''0''' unsets it. If # omitted, the current property # state is returned. ######################################################################## # @command ::nsf::method::provide # # The command contributes an entry to a '''interp'''-wide method # repository (the method index) to be queried and harvested by # <<@command ::nsf::method::require>>. An entry consists of a unique # identifier, a definition script, and a setup script. Arbitrary # objects can request method definitions from this repository in a # disciplined manner. The idea of this method repository ressembles # Tcl's '''auto_import''' feature, applied to method ownership of # objects. # # @parameter require_name The unique identifier, used to request a # method from the method index. # @parameter definition The definition script, usually stating # a call to <<@command ::nsf::method::alias>>. # @parameter script:optional A setup script which is evaluated # before defining the method on the # requiring object. ######################################################################## # @command ::nsf::method::require # # As the counterpart of <<@command ::nsf::method::provide>>, the command allows # you to require an method definition for the given object from the # '''interp'''-wide method repository. # # @parameter object The object to own the requested method # @parameter name The unique identifier of the requested # method in the repository. # @parameter per_object If the target object is a class, the # method will be owned by the class # object itself. ######################################################################## # @command ::nsf::method::setter # # A factory method which creates a setter method on the object or # class specified. A setter method provides a pair of accessor and # mutator operations for object variables. The following '''setter''' # example ... # ''' # ::nsf::method::setter /obj/ x # ''' # ... can be rewritten as a scripted method using the NSF primitives # <<@command ::nsf::current>> and <<@command ::nsf::var::set>>: # ''' # ::nsf::method::create /obj/ -public x {value:optional} { # if {[info exists value]} { # return [::nsf::var::set [::nsf::current object] [::nsf::current method] $value] # } else { # return [::nsf::var::set [::nsf::current] [::nsf::current method]] # } # } # ''' # While adding a method dispatch overhead, accessing and mutating # object variables through dedicated setter methods provides several # benefits: They facilitate accessing object states from client # objects and render operations on object variables interceptable # by <<@glspl filter>> and <<@glspl mixin_class>>. # # @parameter object The owner object or class of # the setter method # @parameter -per-object:switch If requested and if defined on # a class, the setter method is # owned by the class object # itself. # @parameter parameter The name of the setter method # and, as a consequence, the # object variable managed by the # setter method. ######################################################################## # @command ::nsf::mixin # # A helper command to set and maintain mixin relations between an # object and a set of of <<@gls mixin_class>>. It is a convenience # wrapper around <<@command ::nsf::relation>>. # # @parameter object The target object to refine # @parameter arg ######################################################################## # @command ::nsf::my # # This special command is a dispatcher for self-calls, i.e. method # calls on the self-object (in the sense of '''[::nsf::current # object]''', see <<@command ::nsf::current>>). Using '''my''' incurs less # dispatch overhead than an explicit self-dispatch. For example, # '''[::nsf::current object] /methodname/''' is equivalent to and, # therefore, should be replaced by '''my /methodname/'''. # # In terms of the general dispatcher command <<@command ::nsf::dispatch>>, # '''my''' could be implemented as follows: # ''' # ::nsf::dispatch [::nsf::current object] /methodname/ # ''' # By default, this dispatcher realises a subclassing method resolution # scheme, that is, method resolution along the self reference takes # into account the entire precedence of inheriting classes of the # current object. To ressemble more closely a subtyping scheme, i.e., # method resolution starts at the current class as a fixpoint, use the # '''-local''' option to '''my'''. This will limit the method lookup # range to the class of the calling method, i.e. the # '''[::nsf::current class]''' resolved at the call site of '''my'''. # # # # @parameter -local Dispatch on the interface of the class # enclosing the '''my''' instruction call. # @parameter method The name of the receiver method # @parameter args ######################################################################## # @command ::nsf::next # # Invokes the shadowed (i.e, same-named) method which is next along # the precedence path and returns the results of this invocation. The # command realises method combination in NSF-derived object # systems. If '''next''' is called without arguments, the arguments of # the current method (i.e., the arguments as present at the current # callframe) are passed through to the shadowed method. If '''next''' # is invoked with an empty list, the shadowed method is called without # the active callframe arguments. If explicit arguments are specified # for '''next''' explicitly, these will be passed instead of the ones # active on the callframe. Note that explicit arguments must be passed # boxed into a list. # # @parameter arguments:optional A list boxing explicit # arguments to be passed to # the next shadowed method. ######################################################################## # @command ::nsf::object::exists # # Tests whether a given Tcl command represents an NSF object. The # command incurs the least dispatch overhead to identify an object and # is preferred over its derivates, such as <<@command ::nsf::is>>. # # @parameter value The value to test # @return If the value represents an object, '''1''' is # signalled, '''0''' otherwise. ######################################################################## # @command ::nsf::object::qualify # # The name resolver command resolves unqualified command names (i.e., # no leading '::') according to NSF's internal namespace resolution # rules. This resolution order slightly deviates from Tcl standards: # While in Tcl namespace resolution '''[namespace current]''' of the # currently executing proc takes precedence, followed by the global # namespace, the method interception and delegation techniques require # a namespace resolution which is aware of the call # graph. '''object::qualify''' gives script-level access to this # callstack-driven namespace resolution infrastructure (for testing # purposes, etc.). # # @parameter objectname The name to be expanded into its # fully qualified form. ######################################################################## # @command ::nsf::objectsystem::create # # A factory command for specifying an NSF object system. By providing # the object names of a root class and a root metaclass, you obtain # two barebone objects as a starting point to define the basic class # and object interfaces for your object system. In addition, you may # map system hooks required by the NSF runtime to methods specific to # your object system. For instance, the NX object system is defined as follows: # ''' # ::nsf::objectsystem::create ::nx::Object ::nx::Class { # -class.alloc {alloc ::nsf::methods::class::alloc} # -class.create create # -class.dealloc {dealloc ::nsf::methods::class::dealloc} # -class.objectparameter objectparameter # -class.recreate {recreate ::nsf::methods::class::recreate} # -object.configure configure # -object.defaultmethod {defaultmethod ::nsf::methods::object::defaultmethod} # -object.destroy destroy # -object.init {init ::nsf::methods::object::init} # -object.move move # -object.unknown unknown # } # ''' # # @parameter rootClass The name of the class at the root of # your class hierarchy. # @parameter rootMetaClass The name of the metaclass at the root # of your metaclass hierarchy. # @parameter systemMethods:optional A map which provides bindings # between system hooks (e.g., # '''-class.create''') and # methods defined on the root # class and the root metaclass. ######################################################################## # @command ::nsf::relation # # The core command to establish, maintain and introspect entity # relations, i.e. relations between objects, classes, and methods. The # following relation types can be defined. # # 1. Object - Class, Object - Method # # '''class''': Describes an '''instance-of''' relationship between an # object (instance) and another object (its class). # # '''object-mixin''': Declares a list of <<@glspl mixin_class>> as the # mixins of a given object. # # '''object-filter''': Declares a method as a <<@gls filter>> method # on a given object. # # '''rootclass''': Sets the rootclass of a given object. The rootclass # identifies an object's membership with a certain object system and # acts as the 'default' class to comfort the object system dynamics: # recreation, automatic downcasting upon class removals, etc. # # 2. Class - Class, Class - Method # # '''superclass''': Establishes a generalisation/specialisation # relation between two classes. # # '''class-mixin''': Declares a list of <<@glspl mixin_class>> as the # mixins of a given class, with the method interceptors becoming # active on all instances of this class. # # '''class-filter''': Declares a method as a <<@gls filter>> method on # a given class, with the filter becoming active on all # instances of this class. # # Some convenience wrappers on top of '''relation''' are available: # <<@command ::nsf::mixin>>. # # @parameter object The object or class acting as # the receiving end of a relation # @parameter relationtype The type of relation to be # established between two # entities. Accepts one of: # '''class''', # '''object-mixin''', # '''object-filter''', # '''rootclass''', # '''superclass''', # '''class-mixin''', # '''class-filter'''. # @parameter value:optional If omitted, the current # entities (classes or methods) # participating in the given # relation are return. If # provided, and depending on the # relation type specified, it # takes either a list of classes # or a method name (for # '''object-filter''' and # '''class-filter'''). ######################################################################## # @command ::nsf::tmpdir # # Depending on your platform, identifies the file system path to a # system-wide temporary directory. # # @return The absolute file path to the temporary directory identified ######################################################################## # @command ::nsf::var::exists # # A low-level introspection command to query the existance of an # object variable, given the variable's name and the holder object. By # existance, we mean whether a variable has been 'created' (and not # necessarily 'defined', in terms of Tcl variable semantics). It, # therefore, pairs with Tcl's '''[info exists]'''. # # @parameter object The variable-holding object # @parameter varname The variable name # @return If the variable exists, the test returns # '''1'''; '''0''' otherwise ######################################################################## # @command ::nsf::var::import # # A command used to create a link to an object variable in the current # callframe context (i.e., a proc or eval frame). It arranges for a # one or several local variables in the current callframe to refer to # variables held by an object. The command resembles the variable # binding semantics of '''upvar''', '''uplevel''', and # '''global'''. While the local link variable does not have to exist # at the time of calling this command, as it will be lazily created # upon first use, there must not exist a local variable by the name of # the specified link variable. # # @parameter object The variable-holding object # @parameter args A variable argument vector, defining # the intended mappings between object # and local variables. Each mapping is a # single- or double-valued list. A # single-valued mapping will create a # local link variable with the same name # as the object variable. A # double-valued mapping will give the # link variable a different name. ######################################################################## # @command ::nsf::var::set # # A primitive command to create an variable held by an object and # assign a value to it (i.e., to define an object variable) or # retrieve a variable's value. For related and supporting commands, # see also <<@command ::nsf::var::exists>> and <<@command ::nsf::var::import>>. # # @parameter object The object to hold the newly defined variable. # @parameter varname The name of the object variable # @parameter value:optional If provided, assigns the value to the # object variable. If omitted and if the # variable exists, it will return its # current value. nsfConfig.sh.in000066400000000000000000000047231242365656200137540ustar00rootroot00000000000000# nsfConfig.sh -- # # This shell script (for sh) is generated automatically by the Next # Scripting configure script. It will create shell variables for most # of the configuration options discovered by the configure script. # This script is intended to be included by the configure scripts for # Next Scripting extensions so that they don't have to figure this all # out for themselves. This file does not duplicate information # already provided by tclConfig.sh, so you may need to use that file # in addition to this one. # # The information in this file is specific to a single platform. # Version number. NSF_VERSION='@NSF_VERSION@' NSF_PATCH_LEVEL='@NSF_PATCH_LEVEL@' # String to pass to compiles to pick up includes during build # (i.e., assuming nothing has been installed) NSF_BUILD_INCLUDE_DIR='@NSF_BUILD_INCLUDE_DIR@' NSF_BUILD_INCLUDE_SPEC="-I${NSF_BUILD_INCLUDE_DIR}" # String to pass to compiles to pick up the nsf includes from their # installed directory. NSF_INCLUDE_DIR="@NSF_INCLUDE_DIR@" NSF_INCLUDE_SPEC="-I${NSF_INCLUDE_DIR}" # The name of the nsf library (may be either a .a file or a shared library): NSF_LIB_FILE=@PKG_LIB_FILE@ # String to pass to linker to pick up the nsf library from its # build directory. NSF_BUILD_LIB_SPEC='@NSF_BUILD_LIB_SPEC@' # String to pass to linker to pick up the nsf library from its # installed directory. NSF_LIB_SPEC='@NSF_LIB_SPEC@' # The name of the NSF stub library (a .a file): # NSF_STUB_LIB_FILE=@PKG_STUB_LIB_FILE@ # String to pass to linker to pick up the NSF stub library from its # build directory. NSF_BUILD_STUB_LIB_SPEC='@NSF_BUILD_STUB_LIB_SPEC@' # String to pass to linker to pick up the NSF stub library from its # installed directory. NSF_STUB_LIB_SPEC='@NSF_STUB_LIB_SPEC@' # Name of the NSF stub library with full path in build and install directory NSF_BUILD_STUB_LIB_PATH='@NSF_BUILD_STUB_LIB_PATH@' NSF_STUB_LIB_PATH='@NSF_STUB_LIB_PATH@' # Location of the top-level source directories from which nsf # was built. This is the directory that contains generic, unix, etc. # If nsf was compiled in a different place than the directory # containing the source files, this points to the location of the sources, # not the location where nsf was compiled. NSF_SRC_DIR='@NSF_SRC_DIR@' # shared and unshared library suffix NSF_SHARED_LIB_SUFFIX=@SHARED_LIB_SUFFIX@ NSF_UNSHARED_LIB_SUFFIX=@UNSHARED_LIB_SUFFIX@ # the shell in whose installation dirs the nsf package is installed NSF_COMPATIBLE_TCLSH=@NSF_COMPATIBLE_TCLSH@ nx-mode.el000066400000000000000000000066411242365656200127710ustar00rootroot00000000000000; Simple nx-mode designed as an extension of the tcl-mode in tcl.el ; in popular emacs distribitions ; ; gustaf neumann (May 2013) ; TODO: ; - complete definitions, ; - maybe set indent level ; - finish var highlighting for nx variable syntax ; ; For now, load it e.g. with M-x load-library ; ~/.emacs.d/nx-mode.el (load-library "tcl") (setq nx-typeword-list (append tcl-typeword-list '("property"))) ;; extra commands/methods to define something (setq nx-proc-list (append tcl-proc-list '("method" "alias" "forward"))) ;; Tcl control operators are rendered as keywords (if, while, ... (setq nx-keyword-list tcl-keyword-list) (setq nx-builtin-list (append tcl-builtin-list '("apply" "chan" "dict" "lassign" "lsearch" "lrepeat" "lreverse" "lset" "pkg_mkIndex" "refchan" "unload" "update" "cget" "children" "configure" "create" "copy" "delete" "destroy" "filter" "has" "lookup" "heritage" "instances" "methods" "mixinof" "move" "object" "new" "parameter" "parent" "precedence" "require" "slots" "subclass" "superclass" "volatile" "variables" "vars" ))) (setq nx-typeword-regexp (regexp-opt nx-typeword-list 'words)) (setq nx-proc-regexp (regexp-opt nx-proc-list 'words)) (setq nx-keyword-regexp (regexp-opt nx-keyword-list 'words)) (setq nx-builtin-regexp (regexp-opt nx-builtin-list 'words)) (add-to-list 'myKeywords (cons nx-typeword-regexp 'font-lock-type-face)) (add-to-list 'myKeywords (cons nx-proc-regexp 'font-lock-function-name-face)) (add-to-list 'myKeywords (cons nx-keyword-regexp 'font-lock-keyword-face)) (add-to-list 'myKeywords (cons nx-builtin-regexp 'font-lock-builtin-face)) ;;(message "My keywords is: %S" myKeywords) (define-derived-mode nx-mode tcl-mode (setq font-lock-defaults '(myKeywords)) (setq mode-name "NX Tcl") ) ;;;; the following section contains already an adapted regexp for vars in nx ;;; (to highlight "set :x a" the same way as "set x a"), but this has still ;;; to be mangeled into the nicer style of above... (setq myKeywords0 (append '("nx::Class\\|nx::Object\\|\\bmethod\\b\\|\\balias\\b\\|\\bforward\\b\\|\\bobject\\b\\|\\bproc\\b" . 'font-lock-function-name-face) '("\\bclass\\b\\|\\bcget\\b\\|\\bconfigure\\b\\|\\bcreate\\b\\|\\beval\\b\\|\\bfilter\\b\\|\\binfo\\b\\|\\blookup\\b\\|\\bmixin\\b\\|\\bsuperclass\\b" . 'font-lock-builtin-face) ;; '(nx-typeword-regexp . 'font-lock-type-face) '("\\bproperty\\b\\|\\bprotected\\b\\|\\bprivate\\b\\|\\bpublic\\b\\|\\bvariable\\b\\|\\bupvar\\b" . 'font-lock-type-face) ;; (list (concat "\\(\\s-\\|^\\)" ;; (regexp-opt nx-typeword-list t) ;; "\\(\\s-\\|$\\)") ;; 2 'font-lock-type-face) ;; When variable names are enclosed in {} braces, any ;; character can be used. Otherwise just letters, digits, ;; underscores. Variable names can be prefixed with any ;; number of "namespace::" qualifiers. A leading "::" refers ;; to the global namespace. '("\\${\\([^}]+\\)}" 1 'font-lock-variable-name-face) '("\\$\\(\\(?:::\\)?\\(?:[[:alnum:]_]+::\\)*[[:alnum:]_]+\\)" 1 'font-lock-variable-name-face) '("\\(?:\\s-\\|^\\|\\[\\)set\\s-+{\\([^}]+\\)}" 1 'font-lock-variable-name-face 'keep) '("\\(?:\\s-\\|^\\|\\[\\)set\\s-+\\(\\(?:::\\)?\ \\(?:[[:alnum:]_]+::\\)*:?[[:alnum:]_]+\\)" 1 'font-lock-variable-name-face 'keep) ) )nxsh.in000066400000000000000000000023571242365656200124100ustar00rootroot00000000000000#! /bin/sh # Lookup a Tcl interpreter \ INTERP="tclsh@TCL_VERSION@"; \ INTERPS="@NSF_COMPATIBLE_TCLSH@ @TCL_EXEC_PREFIX@/bin/$INTERP"; \ for interp in $INTERPS; \ do if [ -x $interp ]; then INTERP=$interp; break; \ fi; done; \ exec $INTERP "$0" ${1+"$@"} # -*- tcl -*- puts [info nameofexecutable] # # Tiny scripted replacement of a binary nxsh. This script can be used # as interactive shell for testing or like a regular shell with the !# # markup in the first line of a script. It is designed to work with # multiple installed shells during development. For installed # versions, it should be sufficient to remove the first line. # package require nx namespace import ::nx::* if {$argc == 0} { set prefix "" set line "" while {1} { if {$line eq ""} { puts -nonewline "% " flush stdout } append line [gets stdin] if {[info complete $line]} { if {[catch $line result]} { puts $::errorInfo } else { puts $result } set line "" continue } append line \n } } else { set argv0 [lindex $argv 0] set argv [lreplace $argv 0 0] incr argc -1 source $argv0 #if {[catch [list source $argv0] errorMsg]} { # return -code error -level 1 $errorMsg #} } nxwish.in000066400000000000000000000023001242365656200127340ustar00rootroot00000000000000#! /bin/sh # Lookup a Tcl interpreter \ INTERP="tclsh@TCL_VERSION@"; \ INTERPS="@NSF_COMPATIBLE_TCLSH@ @TCL_EXEC_PREFIX@/bin/$INTERP"; \ for interp in $INTERPS; \ do if [ -x $interp ]; then INTERP=$interp; break; \ fi; done; \ exec $INTERP "$0" ${1+"$@"} # -*- tcl -*- puts [info nameofexecutable] # # Tiny scripted replacement of a binary nxwish (former xowish). This # script can be used as interactive shell for testing or like a # regular shell with the !# markup in the first line of a script. It # is designed to work with multiple installed shells during # development. For installed versions, it should be sufficient to # remove the first line. # package require Tk package require nx namespace import ::nx::* if {$argc == 0} { set prefix "" set line "" while {1} { update if {$line eq ""} { puts -nonewline "% " flush stdout } append line [gets stdin] if {[info complete $line]} { if {[catch $line result]} { puts $::errorInfo } else { puts $result } set line "" continue } append line \n } } else { set argv0 [lindex $argv 0] set argv [lreplace $argv 0 0] incr argc -1 source $argv0 } tcl-license.terms000066400000000000000000000043211242365656200143470ustar00rootroot00000000000000This software is copyrighted by the Regents of the University of California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState 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. tclconfig/000077500000000000000000000000001242365656200130415ustar00rootroot00000000000000tclconfig/install-sh000077500000000000000000000330541242365656200150520ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2011-04-20.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -S $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -S) stripcmd="$stripprog $2" shift;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: tclconfig/mktar.sh000077500000000000000000000011251242365656200145150ustar00rootroot00000000000000#!/bin/sh pwd=`pwd` name=`basename $(pwd)` echo "name=$name" make distclean cd .. tar zcvf ./$name.tar.gz \ `find ./$name -type f -o -type l| fgrep -v .git| fgrep -v .dSYM | fgrep -v .DS_Store| \ fgrep -v "~"| fgrep -v "#" | fgrep -v -- -orig | fgrep -v .junk |\ fgrep -v .gcov |fgrep -v .gcda|fgrep -v .gcno|fgrep -v lcov-result|fgrep -v lcov.info|\ egrep -v "doc/example.*[.]txt"| \ fgrep -v ".dylib"| \ fgrep -v "autom4te"| fgrep -v config. | fgrep -v callgrind.out. | fgrep -v .plist |\ fgrep -v .db | fgrep -v .gdb | fgrep -v .graffle` tests/000077500000000000000000000000001242365656200122335ustar00rootroot00000000000000tests/accessor.test000066400000000000000000000040411242365656200147350ustar00rootroot00000000000000package req nx::test nx::test configure -count 1 nx::test case setter-variants { nx::Class create C { :property {p1a 1} :property {p1b 1} { :public object method value=set {obj prop value} { nx::var::set $obj $prop [incr value] } } :property -accessor public {p2a 2} :property -accessor public {p2b 2} { :public object method value=set {obj prop value} { nx::var::set $obj $prop [incr value] } } :property -incremental {p3a 3} :property -incremental {p3b 3} { :public object method value=set {obj prop value} { nx::var::set $obj $prop [incr value] } } :create c1 } puts [C info method definition p1a] ? {c1 cget -p1a} 1 ? {c1 configure -p1a 1} "" puts [C info method definition p1b] ? {c1 cget -p1b} 2 ? {c1 configure -p1b 3} "" ? {c1 cget -p1b} 4 puts [C info method definition p2a] ? {c1 cget -p2a} 2 ? {c1 p2a get} 2 ? {c1 configure -p2a 2} "" ? {c1 p2a set 2} 2 ? {c1 p2a unset} "" ? {c1 cget -p2a} {can't read "p2a": no such variable} puts stderr ====1 ? {c1 p2a unset} {can't unset "p2a": no such variable} puts stderr ====2 ? {c1 p2a unset -nocomplain} "" puts stderr ====3 puts [C info method definition p2b] ? {c1 cget -p2b} 3 ? {c1 p2b get} 3 ? {c1 configure -p2b 2} "" ? {c1 p2b set 2} 3 ? {c1 p2b unset} "" ? {c1 cget -p2b} {can't read "p2b": no such variable} puts [C info method definition p3a] ? {c1 cget -p3a} 3 ? {c1 p3a get} 3 ? {c1 configure -p3a 3} "" ? {c1 p3a get 3} {invalid argument '3', maybe too many arguments; should be "value=get ?-array? /object/ /varName/"} ? {c1 p3a set 3} 3 ? {c1 p3a unset} "" ? {c1 cget -p3a} {can't read "p3a": no such variable} puts [C info method definition p3b] ? {c1 cget -p3b} 4 ? {c1 p3b get} 4 ? {c1 configure -p3b 4} "" ? {c1 p3b get} 5 ? {c1 p3b set 4} 5 ? {c1 p3b get} 5 ? {c1 p3b set 4} 5 ? {c1 p3b get} 5 ? {c1 p3b unset} "" ? {c1 cget -p3b} {can't read "p3b": no such variable} } tests/alias.test000066400000000000000000000534711242365656200142370ustar00rootroot00000000000000# -*- Tcl -*- package prefer latest package require nx package require nx::test #::nx::configure defaultMethodCallProtection false nx::test configure -count 10 nx::test case alias-preliminaries { # The system methods of nx::VariableSlot are either alias or forwarders ? {lsort [::nx::VariableSlot info methods -type alias]} {value=get value=set} ? {::nx::VariableSlot info method definition value=get} \ "::nx::VariableSlot public alias value=get ::nsf::var::get" # define an alias and retrieve its definition set cmd "::nx::Object public alias set ::set" eval $cmd ? {nx::Object info method definition set} $cmd # define an alias and retrieve its definition set cmd "::nx::Object public alias set -frame method ::set" eval $cmd ? {nx::Object info method definition set} $cmd # define an alias and retrieve its definition set cmd "::nx::Object public alias set -frame object ::set" eval $cmd ? {nx::Object info method definition set} $cmd proc ::foo {} {return foo} ? {nx::Object alias foo -frame object ::foo} \ "cannot use -frame object|method in alias for scripted command '::foo'" ? {nx::Object alias foo -frame method ::foo} \ "cannot use -frame object|method in alias for scripted command '::foo'" ? {nx::Object alias foo -frame default ::foo} "::nsf::classes::nx::Object::foo" } nx::test case alias-simple { # define an alias and retrieve its definition nx::Class create Base { :public method foo {{-x 1}} {return $x} } nx::Class create Foo ? {::nsf::method::alias ::Foo foo ::nsf::classes::Base::foo} "::nsf::classes::Foo::foo" ? {Foo info method definition foo} "::Foo public alias foo ::nsf::classes::Base::foo" Foo create f1 ? {f1 foo} 1 ? {f1 foo -x 2} 2 ? {Foo info methods -type alias} "foo" ? {Base info methods -type scripted} {foo} ? {Foo info methods -type scripted} {} ? {Foo info methods -type alias} {foo} Base public method foo {} {} #WITH_IMPORT_REFS #? {Foo info methods -type alias} "" ? {Base info methods -type scripted} {} ? {Foo info methods -type scripted} {} #WITH_IMPORT_REFS #? {Foo info method definition foo} "" ? {Foo info method definition foo} "::Foo public alias foo ::nsf::classes::Base::foo" Base public method foo {{-x 1}} {return $x} ::nsf::method::alias ::Foo foo ::nsf::classes::Base::foo ? {Base info methods -type scripted} {foo} "defined again" ? {Foo info methods -type alias} {foo} "aliased again" Foo public method foo {} {} ? {Base info methods -type scripted} {foo} "still defined" ? {Foo info methods -type alias} {} "removed" } nx::test case alias-chaining { # # chaining aliases # nx::Class create T nx::Class create S T create t S create s T public method foo args { return [current class]->[current method] } ::nsf::method::alias T FOO ::nsf::classes::T::foo ? {t foo} ::T->foo ? {t FOO} ::T->FOO ? {lsort [T info methods]} {FOO foo} T method foo {} {} #WITH_IMPORT_REFS #? {lsort [T info methods]} {} "alias is deleted" ? {lsort [T info methods]} {FOO} "alias is deleted" # puts stderr "double indirection" T public method foo args { return [current class]->[current method] } ::nsf::method::alias T FOO ::nsf::classes::T::foo ::nsf::method::alias S BAR ::nsf::classes::T::FOO ? {T info methods -type alias} "FOO" ? {T info method definition FOO} "::T public alias FOO ::nsf::classes::T::foo" ? {lsort [T info methods]} {FOO foo} ? {S info methods} {BAR} T method FOO {} {} ? {T info methods} {foo} ? {S info methods} {BAR} ? {s BAR} ::S->BAR ? {t foo} ::T->foo ? {S info method definition BAR} "::S public alias BAR ::nsf::classes::T::FOO" T public method foo {} {} ? {T info methods} {} #WITH_IMPORT_REFS #? {S info methods} {} ? {S info methods} {BAR} T public method foo args { return [current class]->[current method] } ::nsf::method::alias T FOO ::nsf::classes::T::foo ::nsf::method::alias S BAR ::nsf::classes::T::FOO ? {lsort [T info methods]} {FOO foo} ? {S info methods} {BAR} T public method foo {} {} #WITH_IMPORT_REFS #? {S info methods} {} ? {S info methods} {BAR} #WITH_IMPORT_REFS #? {T info methods} {} ? {T info methods} {FOO} T public method foo args { return [current class]->[current method] } T public object method bar args { return [current class]->[current method] } ::nsf::method::alias T -per-object FOO ::nsf::classes::T::foo ::nsf::method::alias T -per-object BAR ::T::FOO ::nsf::method::alias T -per-object ZAP ::T::BAR #WITH_IMPORT_REFS #? {T info methods} {foo} ? {T info methods} {foo FOO} ? {lsort [T info object methods -type alias]} {BAR FOO ZAP} ? {lsort [T info object methods]} {BAR FOO ZAP bar} ? {t foo} ::T->foo ? {T info object method definition ZAP} {::T public object alias ZAP ::T::BAR} ? {T FOO} ->FOO ? {T BAR} ->BAR ? {T ZAP} ->ZAP ? {T bar} ->bar T object method FOO {} {} #WITH_IMPORT_REFS #? {T info methods} {foo} ? {T info methods} {foo FOO} ? {lsort [T info object methods]} {BAR ZAP bar} ? {T BAR} ->BAR ? {T ZAP} ->ZAP rename ::T::BAR "" #WITH_IMPORT_REFS #? {T info methods} {foo} ? {T info methods} {foo FOO} ? {lsort [T info object methods]} {ZAP bar} ? {T ZAP} ->ZAP; # is ok, still pointing to 'foo' #WITH_IMPORT_REFS #? {T info methods} {foo} ? {T info methods} {foo FOO} ? {lsort [T info object methods]} {ZAP bar} ? {T ZAP} ->ZAP T public method foo {} {} #WITH_IMPORT_REFS #? {T info methods} {} ? {T info methods} {FOO} #WITH_IMPORT_REFS #? {lsort [T info object methods]} {bar} ? {lsort [T info object methods]} {ZAP bar} } nx::test case alias-per-object { nx::Class create T { :public object method bar args { return [current class]->[current method] } :create t } proc ::foo args { return [current class]->[current method] } # # per-object methods as per-object aliases # T public object method m1 args { return [current class]->[current method] } ::nsf::method::alias T -per-object M1 ::T::m1 ::nsf::method::alias T -per-object M11 ::T::M1 ? {lsort [T info object methods]} {M1 M11 bar m1} ? {T m1} ->m1 ? {T M1} ->M1 ? {T M11} ->M11 T object method M1 {} {} ? {lsort [T info object methods]} {M11 bar m1} ? {T m1} ->m1 ? {T M11} ->M11 T object method m1 {} {} #WITH_IMPORT_REFS #? {lsort [T info object methods]} {bar} ? {lsort [T info object methods]} {M11 bar} # # a proc as alias # proc foo args { return [current class]->[current method] } ::nsf::method::alias T FOO1 ::foo ::nsf::method::alias T -per-object FOO2 ::foo # # ! per-object alias referenced as per-class alias ! # ::nsf::method::alias T BAR ::T::FOO2 #WITH_IMPORT_REFS #? {lsort [T info object methods]} {FOO2 bar} ? {lsort [T info object methods]} {FOO2 M11 bar} ? {lsort [T info methods]} {BAR FOO1} ? {T FOO2} ->FOO2 ? {t FOO1} ::T->FOO1 ? {t BAR} ::T->BAR # # delete proc # rename ::foo "" #WITH_IMPORT_REFS #? {lsort [T info object methods]} {bar} ? {lsort [T info object methods]} {FOO2 M11 bar} #WITH_IMPORT_REFS #? {lsort [T info methods]} {} ? {lsort [T info methods]} {BAR FOO1} } # namespaced procs + namespace deletion nx::test case alias-namespaced { nx::Class create T { :public object method bar args { return [current class]->[current method] } :create t } namespace eval ::ns1 { proc foo args { return [current class]->[current method] } proc bar args { return [uplevel 1 {set _}] } proc bar2 args { upvar 1 _ __; return $__} } ::nsf::method::alias T FOO ::ns1::foo ::nsf::method::alias T BAR ::ns1::bar ::nsf::method::alias T BAR2 ::ns1::bar2 ? {lsort [T info methods]} {BAR BAR2 FOO} set ::_ GOTYA ? {t FOO} ::T->FOO ? {t BAR} GOTYA ? {t BAR2} GOTYA namespace delete ::ns1 ? {info procs ::ns1::*} {} #WITH_IMPORT_REFS #? {lsort [T info methods]} {} ? {lsort [T info methods]} {BAR BAR2 FOO} # per-object namespaces nx::Class create U U create u ? {namespace exists ::U} 0 U public object method zap args { return [current class]->[current method] } ::nsf::method::alias ::U -per-object ZAP ::U::zap U require namespace ? {namespace exists ::U} 1 U public object method bar args { return [current class]->[current method] } ::nsf::method::alias U -per-object BAR ::U::bar ? {lsort [U info object methods]} {BAR ZAP bar zap} ? {U BAR} ->BAR ? {U ZAP} ->ZAP namespace delete ::U ? {namespace exists ::U} 0 ? {lsort [U info object methods]} {} ? {U info lookup methods BAR} "" ? {U info lookup methods ZAP} "" ::U destroy } # dot-resolver/ dot-dispatcher used in aliased proc nx::test case alias-dot-resolver { nx::Class create V { set :z 1 :public method bar {z} { return $z } :public object method bar {z} { return $z } :create v { set :z 2 } } ? {lsort [V info vars]} {z} ? {lsort [V info vars]} {z} ? {lsort [v info vars]} {z} proc ::foo args { return [:bar ${:z}]-[set :z]-[:bar [set :z]] } ::nsf::method::alias V FOO1 ::foo ::nsf::method::alias V -per-object FOO2 ::foo ? {lsort [V info object methods]} {FOO2 bar} ? {lsort [V info methods]} {FOO1 bar} ? {V FOO2} 1-1-1 ? {v FOO1} 2-2-2 V public method FOO1 {} {} ? {lsort [V info methods]} {bar} rename ::foo "" #WITH_IMPORT_REFS #? {lsort [V info object methods]} {bar} ? {lsort [V info object methods]} {FOO2 bar} } nx::test case alias-store # # Tests for the ::nsf::method::alias store, used for introspection for # aliases. The alias store (an associative variable) is mostly # necessary for for the direct aliases (e.g. aliases to C implemented # tcl commands), for which we have no stubs at the place where the # alias was registered. # # # structure of the ::nsf::method::alias store: # ,, -> # nx::Object create o nx::Class create C o public object method bar args {;} ? {info vars ::nsf::alias} ::nsf::alias ? {array exists ::nsf::alias} 1 proc ::foo args {;} ::nsf::method::alias ::o FOO ::foo ::nsf::method::alias ::C FOO ::foo ? {info exists ::nsf::alias(::o,FOO,1)} 1 ? {info exists ::nsf::alias(::C,FOO,0)} 1 ? {array get ::nsf::alias ::o,FOO,1} "::o,FOO,1 ::foo" ? {array get ::nsf::alias ::C,FOO,0} "::C,FOO,0 ::foo" ? {o info object method definition FOO} "::o public object alias FOO ::foo" ? {C info method definition FOO} "::C public alias FOO ::foo" ::nsf::method::alias o FOO ::o::bar ? {info exists ::nsf::alias(::o,FOO,1)} 1 ? {array get ::nsf::alias ::o,FOO,1} "::o,FOO,1 ::o::bar" ? {o info object method definition FOO} "::o public object alias FOO ::o::bar" # AliasDelete in RemoveObjectMethod o public object method FOO {} {} ? {info exists ::nsf::alias(::o,FOO,1)} 0 ? {array get ::nsf::alias ::o,FOO,1} "" ? {o info object method definition FOO} "" # AliasDelete in RemoveClassMethod C public method FOO {} {} ? {info exists ::nsf::alias(::C,FOO,0)} 0 ? {array get ::nsf::alias ::C,FOO,0} "" ? {C info method definition FOO} "" ::nsf::method::alias ::o BAR ::foo ::nsf::method::alias ::C BAR ::foo # AliasDelete in AddObjectMethod ? {info exists ::nsf::alias(::o,BAR,1)} 1 ::o public object method BAR {} {;} ? {info exists ::nsf::alias(::o,BAR,1)} 0 # AliasDelete in AddInstanceMethod ? {info exists ::nsf::alias(::C,BAR,0)} 1 ::C public method BAR {} {;} ? {info exists ::nsf::alias(::C,BAR,0)} 0 # AliasDelete in aliasCmdDeleteProc ::nsf::method::alias o FOO ::foo ? {info exists ::nsf::alias(::o,FOO,1)} 1 rename ::foo "" #WITH_IMPORT_REFS #? {info exists ::nsf::alias(::o,FOO,1)} 0 ? {info exists ::nsf::alias(::o,FOO,1)} 1 ::nsf::method::alias o FOO ::o::bar ::nsf::method::alias o BAR ::o::FOO ? {info exists ::nsf::alias(::o,FOO,1)} 1 ? {info exists ::nsf::alias(::o,BAR,1)} 1 o public object method bar {} {} #WITH_IMPORT_REFS #? {info exists ::nsf::alias(::o,FOO,1)} 0 ? {info exists ::nsf::alias(::o,FOO,1)} 1 #WITH_IMPORT_REFS #? {info exists ::nsf::alias(::o,BAR,1)} 0 ? {info exists ::nsf::alias(::o,BAR,1)} 1 # # pulling the rug out from the proc-alias deletion mechanism # proc ::foo args {;} ::nsf::method::alias C FOO ::foo ? {info exists ::nsf::alias(::C,FOO,0)} 1 unset ::nsf::alias(::C,FOO,0) ? {info exists ::nsf::alias(::C,FOO,0)} 0 ? {C info method definition FOO} "" ? {C info methods -type alias} FOO rename ::foo "" #WITH_IMPORT_REFS #? {C info methods -type alias} "" ? {C info methods -type alias} "FOO" ? {info exists ::nsf::alias(::C,FOO,0)} 0 ? {C info method definition FOO} "" # # test renaming of Tcl proc (actually sensed by the alias, though not # reflected by the alias definition store) # a) is this acceptable? # b) sync ::nsf::method::alias upon "info method definition" calls? is this feasible, # e.g. through rename traces? # C create c proc ::foo args { return [current]->[current method]} ? {info exists ::nsf::alias(::C,FOO,0)} 0 ::nsf::method::alias C FOO ::foo ::nsf::method::alias C FOO2 ::foo ? {info exists ::nsf::alias(::C,FOO,0)} 1 ? {lsort [C info methods -type alias]} {FOO FOO2} # Rename target, such that alias points to an invalid item # Note that removing the target works differently (makes cleanup) # rename ::foo "" rename ::foo ::foo2 ? {info exists ::nsf::alias(::C,FOO,0)} 1 ? {lsort [C info methods -type alias]} {FOO FOO2} ? {c FOO} {target "::foo" of alias FOO apparently disappeared} ? {C info method definition FOO} "::C public alias FOO ::foo" unset ::nsf::alias(::C,FOO,0) ? {c FOO} {could not obtain alias definition for ::C FOO.} ? {c FOO2} {target "::foo" of alias FOO2 apparently disappeared} rename ::foo2 ::foo ? {c FOO} {could not obtain alias definition for ::C FOO.} ? {c FOO2} {::c->FOO2} # # Check resolving of namespace imported classes # and when a class is aliased via "interp alias" # nx::test case class-resolve { namespace eval ::ns1 { nx::Class create A {:public method foo {} {::nx::current class}} nx::Class create B {:public method foo {} {::nx::current class}} namespace export A } namespace eval ::ns2 { # namespace import Class A from namespace ns1 namespace import ::ns1::A ? {A create a1} ::ns2::a1 ? {nx::Class create C -superclass A} ::ns2::C ? {C create c1} ::ns2::c1 ? {c1 foo} ::ns1::A # "import" Class B from namespace ns1 via interp-alias interp alias {} ::ns2::B {} ::ns1::B ? {B create b1} ::ns2::b1 ? {b1 foo} ::ns1::B ? {nx::Class create D -superclass B} ::ns2::D ? {D create d1} ::ns2::d1 ? {d1 foo} ::ns1::B } } nx::test configure -count 10 nx::test case proc-alias { nx::Class create C { :public method foo {} {upvar x y; info exists y} :public method bar {} {set x 1; :foo} :public alias bar_ [:info method registrationhandle bar] :public alias foo_ [:info method registrationhandle foo] :public method bar2 {} {set x 1; :foo_} :create c1 } nx::Class create D { :public method foo {} {:upvar x y; info exists y} :public method bar {} {set x 1; :foo} :public alias foo_ [:info method registrationhandle foo] :public alias bar_ [:info method registrationhandle bar] :public method bar2 {} {set x 1; :foo_} :create d1 } nx::Class create M { :public method foo args next :public method bar args next :public method foo_ args next :public method bar_ args next :public method bar_ args next } ? {c1 bar} 1 ? {c1 bar_} 1 ? {c1 bar2} 1 ? {d1 bar} 1 ? {d1 bar_} 1 ? {d1 bar2} 1 c1 object mixins add M ? {c1 bar} 0 ;# upvar reaches into to mixin method ? {c1 bar_} 0 ;# upvar reaches into to mixin method ? {c1 bar2} 0 ;# upvar reaches into to mixin method d1 object mixins add M ? {d1 bar} 1 ? {d1 bar_} 1 ? {d1 bar2} 1 } proc foo {:a :b} { set :c 1 return ${:a} } foo 1 2 proc bar {:a :b} { set :b 1 set :x 47 return [info exists :d]-${:a}-${:x} } proc baz {} { set :z 3 return ${:z} } nx::test configure -count 10 nx::test case proc-alias-compile { nx::Object create o { set :a 100 set :d 1001 #:method foo {-:a:integer :b :c:optional} { # puts stderr ${:a},${:b},${:c} #} :public object alias foo ::foo :public object alias bar ::bar :public object alias baz ::baz } # # by calling "foo" outside the object/method context, we get a # byte-code without the compiled-local handler, colon-vars are not # recognized, :a refers to the argument ? {foo 1 2} 1 ? {lsort [o info vars]} "a d" ? {o foo 1 2} 1 ? {lsort [o info vars]} "a d" # # by calling "bar" the first time as a method, we get a byte-code with # the compiled-local handler, colon-vars are recognized, colon vars # from the argument vector have precedence over instance variables. ? {o bar 2 3} 1-2-47 ? {lsort [o info vars]} "a d x" ? {o baz} 3 ? {lsort [o info vars]} "a d x z" # # by calling "bar" outside the proc context, the compiled-var-fetch # has no object to refer to, the variable is unknown. ? {bar 3 4} 0-3-47 # the variable in the test scope does not influence result set :d 200 ? {bar 3 4} 0-3-47 } # # test redefinition of a target proc # nx::test configure -count 1 nx::test case alias-proc-refetch { # # initial definition # proc target {} {return 1} nx::Object create o {:public object alias foo ::target} ? {o foo} 1 # # redefinition # proc ::target {} {return 2} ? {o foo} 2 } # # test registration of a pre-compiled proc # nx::test configure -count 1 nx::test case alias-precompiled-proc { nx::Class create C { :public method vars {} { set result [list] foreach v [lsort [:info vars]] {lappend result $v [set :$v]} return $result } :create c1 } ? {c1 vars} {} proc ::foo {x} {set :a $x} proc ::bar {x} {set :b $x} # # force bytecode compilation of ::foo # ? {::foo 1} 1 # # Register an already used tcl proc. Byte compilation happened # without nsf context. If the byte code is not invalidated, the # compiled var resolver would not kick in, we would not be able to # set an instance variable. ::nsf::method::alias ::C foo ::foo ? {c1 foo 2} 2 ? {c1 vars} {a 2} # # Register an unused tcl proc. Byte compilation happens within nsf # context, compiled var resolver works as expected. ::nsf::method::alias ::C bar ::bar ? {c1 bar 2} 2 ? {c1 vars} {a 2 b 2} # Call proc from outside nx; does not set the variable, and does not # crash; seems ok, but could warn. ? {::bar 3} 3 ? {c1 vars} {a 2 b 2} # call proc from method context; it sets the variable, # maybe questionable, but not horrible c1 public object method baz {} {::bar 4} ? {c1 baz} 4 ? {c1 vars} {a 2 b 4} } # # Testing aliases to objects and reference counting. # Check the effects via MEM_COUNT... # nx::test case refcount-object-alias-recreate1 { # # alias recreate with the same object # nx::Object create ::x # per-object aliases nx::Object create ::o { :object alias X ::x ? {o info object method definition X} "::o protected object alias X ::x" :object alias X ::x ? {o info object method definition X} "::o protected object alias X ::x" } # per-class aliases nx::Class create ::C { :alias A1 ::x ? {C info method definition A1} "::C protected alias A1 ::x" :alias A1 ::x ? {C info method definition A1} "::C protected alias A1 ::x" :object alias A2 ::x ? {C info object method definition A2} "::C protected object alias A2 ::x" :object alias A2 ::x ? {C info object method definition A2} "::C protected object alias A2 ::x" } } nx::test case refcount-object-alias-recreate2 { # # alias recreate with a proc # nx::Object create ::x ::proc ::y {} {} nx::Object create ::o { :object alias X ::x ? {o info object method definition X} "::o protected object alias X ::x" :object alias X ::y ? {o info object method definition X} "::o protected object alias X ::y" } } nx::test case refount-destroy-delete1 { nx::Object create ::x nx::Object create ::o {:object alias X ::x} ? {o info object method definition X} "::o protected object alias X ::x" # destroy the object, make sure it does not exist anymore ? {x destroy} "" ? {nsf::object::exists x} 0 # The alias lookup does still work ? {o info object method definition X} "::o protected object alias X ::x" # Create the referenced object new nx::Object create ::x # Recreation of the alias, must free refcount to the old object ? {::o object alias X ::x} "::o::X" # Recreate the object. On recreation, the object is not freed, # therefore we test the reference counter is aleady set, and must # nor be incremented nx::Object create ::x ? {::o object alias X ::x} "::o::X" } nx::test case refount-destroy-delete2 { nx::Object create ::o nx::Object create ::baff nx::Object create ::baff::child ::o object alias X ::baff::child ? {nsf::object::exists ::baff::child} 1 ? {o info object method definition X} "::o protected object alias X ::baff::child" nx::Object create ::baff ? {nsf::object::exists ::baff::child} 0 # The alias lookup does still work ? {o info object method definition X} "::o protected object alias X ::baff::child" # Create the child new nx::Object create ::baff::child ? {nsf::object::exists ::baff::child} 1 # Recreation of the alias, must free refcount to the old object ? {::o object alias X ::baff::child} "::o::X" } # # Testing cylcic alias # nx::test case cyclic-alias { nx::Object create o { set handle [:public object method foo {} {return 1}] # we can define currently the recursive definition ? [list [:] public object alias foo $handle] "::o::foo" } # at runtime, we get an exception ? {o foo} {target "::o::foo" of alias foo apparently disappeared} # test indirect case set handle1 [o public object method foo {} {return 1}] set handle2 [o public object alias bar $handle1] set handle3 [o public object alias foo $handle2] ? {o foo} {target "::o::bar" of alias foo apparently disappeared} } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/cget.test000066400000000000000000000241531242365656200140630ustar00rootroot00000000000000# -*- Tcl -*- package req nx package require nx::test # # The first test set checks just the basic behavior: # nx::test case cget-simple { nx::Class create Person { :property famnam:required :property {age:integer,required 0} :property {friends:0..n ""} :property sex # Create an instance of the class :create p1 -famnam hugo -age 25 } # # first, check basic provided values and defaults # ? {p1 cget -age} 25 ? {p1 cget -famnam} hugo ? {p1 cget -friends} "" # # a method property ? {p1 cget -class} ::Person # # error handling: # - wrong # args # - wrong parameter # - parameter without a value # ? {p1 cget} {wrong # of arguments: should be "cget /name/"} ? {p1 cget -foo} "cget: unknown configure parameter -foo" ? {p1 cget foo} "cget: unknown configure parameter foo" ? {p1 cget -sex} {can't read "sex": no such variable} # # Reconfigure the object # ? {p1 configure -famnam joe -age 27} "" # # check the new values # ? {p1 cget -age} 27 ? {p1 cget -famnam} joe # # configure without arguments # ? {p1 configure} "" ? {p1 info lookup syntax configure} {?-sex /value/? -famnam /value/ ?-age /integer/? ?-friends /value .../? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} } # # The second test set checks redirection of configure / cget to slot # methods "set" and "get". # nx::test configure -count 1 nx::test case cget-via-slot { nx::Class create C { # Define a property with a "get" method :property bar1 { :public object method value=get { object property} { incr ::count(cget) nsf::var::set $object $property } } # Define a property with a "get" and "set" method :property bar2 { :public object method value=get { object property} { incr ::count(cget) nsf::var::set $object $property } :public object method value=set { object property value } { incr ::count(assign) nsf::var::set $object $property $value } } # Create an instance of the class :create p1 } # # configure without arguments # ? {p1 configure} "" ? {p1 info lookup syntax configure} {?-bar1 /value/? ?-bar2 /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} # # test gettin/setting via slots # # just a getter: # array unset ::count ? {p1 configure -bar1 100} "" ? {array get ::count} "" ? {p1 cget -bar1} 100 ? {array get ::count} "cget 1" # a getter and a setter: # array unset ::count ? {p1 configure -bar2 100} "" ? {array get ::count} "assign 1" ? {p1 cget -bar2} 100 ? {array get ::count} "assign 1 cget 1" } # # The third test set checks method binding to parameter: # All cmds are supposed to return resonable values. # nx::test case cget-parameter-methods { nx::Class create C { :property {foo:alias,method=m0 {1 2 3}} :property {{bar:forward,method=%self m1 a b c %method} bar1} :public method m0 {args} {set :m0 $args; return $args} :public method m1 {args} {set :m1 $args; return $args} :create c1 } package req nx::volatile # # class-level lookup # ? {C info lookup syntax configure} \ "?-mixins /mixinreg .../? ?-superclasses /class .../? ?-filters /filterreg .../? ?-volatile? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" ? {C cget -superclasses} "::nx::Object" ? {C cget -superclass} "::nx::Object" ? {C cget -object-mixin} "" ? {C cget -mixin} "" ? {C cget -filter} "" ? {C cget -volatile} 0 #? {C cget -noinit} "" ? {C cget -class} "::nx::Class" # # object-level lookup # ? {c1 info lookup syntax configure} \ "?-foo /value/? ?-bar /value/? ?-volatile? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" # # query all properties from base classes # ? {c1 cget -volatile} 0 #? {c1 cget -noinit} "" #? {c1 cget -mixin} "" ? {c1 cget -object-mixin} "" ? {c1 cget -class} ::C #? {c1 cget -filter} "" ? {c1 cget -object-filter} "" # # query alias and forward # ? {c1 eval {set :m0}} "{1 2 3}" ? {c1 eval {set :m1}} {a b c bar bar1} ? {c1 cget -foo} "" ? {c1 cget -bar} "a b c bar" } # # The fourth test set checks performance of "cget" and "configure". # nx::test configure -count 10000 nx::test case cget-performance { nx::Class create Person { :property famnam:required :property -accessor public {age:integer,required 0} :property {friends:0..n ""} :property sex # Define a property with a "get" and "set" method :property bar { :public object method value=get { object property } { nsf::var::set $object $property } :public object method value=set { object property value } { nsf::var::set $object $property $value } } # Create an instance of the class :create p1 -famnam hugo -age 25 -bar 101 } # # read properties # - built-in accessor # - cget # - dispatch of cget method with full path # - cget via slot method ? {p1 age get} 25 ? {p1 cget -age} 25 ? {p1 ::nsf::methods::object::cget -age} 25 ? {p1 cget -bar} 101 # # write properties: # - built-in accessor # - configure # - configure via slot method ? {p1 age set 27} 27 ? {p1 configure -age 27} "" ? {p1 configure -bar 102} "" } nx::test configure -count 1 nx::test case configure-trace-class { # # class case with no default # nx::Class create C C property p { set :valuechangedcmd { #puts stderr "C.p valuechangedcmd $obj $var +1" ::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]] } } C create c1 ? {c1 eval {info exists :p}} 0 ? {c1 cget -p} {can't read "p": no such variable} ? {c1 configure -p 1} "" ? {c1 eval {info exists :p}} 1 ? {c1 cget -p} "2" # # class case with default # C property {q 100} { set :valuechangedcmd { #puts stderr "C.q valuechangedcmd $obj $var +1" ::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]] } } C create c2 ? {c2 eval {info exists :q}} 1 ? {c2 cget -q} 100 ? {c2 configure -q 101} "" ? {c2 cget -q} "102" } nx::test case configure-trace-object { # # object case with no default # nx::Object create o ? {o eval {info exists :A}} 0 o object property A { set :valuechangedcmd { #puts stderr "o.A valuechangedcmd $obj $var +1" ::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]] } } # puts [o info object variables A] # puts [o info variable parameter [o info object variables A]] # puts [[o info object slots A] getParameterSpec] ? {o eval {info exists :A}} 0 ? {o cget -A} {can't read "A": no such variable} ? {o configure -A 1} "" ? {o cget -A} "2" # # object case with default # ? {o eval {info exists :B}} 0 o object property {B 1000} { #puts stderr "o.B valuechangedcmd $obj $var +1" set :valuechangedcmd {::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]]} } ? {o eval {info exists :B}} 1 ? {o cget -B} 1000 ? {o configure -B 1001} "" ? {o cget -B} 1002 } nx::test case configure-trace-class-type { # # class case with type and no default # nx::Class create C C property p:integer { set :valuechangedcmd { #puts stderr "C.p valuechangedcmd $obj $var +1" ::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]] } } C create c1 ? {c1 eval {info exists :p}} 0 ? {c1 cget -p} {can't read "p": no such variable} ? {c1 configure -p a} {expected integer but got "a" for parameter "-p"} ? {c1 eval {info exists :p}} 0 ? {c1 configure -p 1} "" ? {c1 eval {info exists :p}} 1 ? {c1 cget -p} "2" # # class case with type and default # ? {C property {q:integer aaa} { set :valuechangedcmd { #puts stderr "C.q valuechangedcmd $obj $var +1" ::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]] } }} {expected integer but got "aaa" for parameter "q"} # slot should no exist ? {C info slots q} "" ? {C property {q:integer 99} { set :valuechangedcmd { #puts stderr "C.q valuechangedcmd $obj $var +1" ::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]] } }} "" # slot should exist ? {C info slots q} "::C::slot::q" ? {C create c2 -q 111} ::c2 ? {c2 eval {info exists :q}} 1 ? {c2 cget -q} 112 ? {c2 configure -q 101} "" ? {c2 cget -q} "102" } nx::test case configure-trace-object-type { # # object case with no default # nx::Object create o ? {o eval {info exists :A}} 0 o object property A:integer { set :valuechangedcmd { #puts stderr "o.A valuechangedcmd $obj $var +1" ::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]] } } # puts [o info object variables A] # puts [o info variable parameter [o info object variables A]] # puts [[o info object slots A] getParameterSpec] ? {o eval {info exists :A}} 0 ? {o cget -A} {can't read "A": no such variable} ? {o configure -A 1} "" ? {o cget -A} "2" ? {o configure -A x} {expected integer but got "x" for parameter "-A"} ? {o cget -A} "2" # # object case with default # ? {o eval {info exists :B}} 0 ? {o object property {B:integer x} { #puts stderr "o.B valuechangedcmd $obj $var +1" set :valuechangedcmd {::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]]} }} {expected integer but got "x" for parameter "B"} ? {o eval {info exists :B}} 0 ? {o info object slots B} "" ? {o object property {B:integer 1000} { #puts stderr "o.B valuechangedcmd $obj $var +1" set :valuechangedcmd {::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]]} }} {} ? {o info object slots B} {::o::per-object-slot::B} ? {o eval {info exists :B}} 1 ? {o cget -B} 1000 ? {o configure -B 1001} "" ? {o cget -B} 1002 ? {o configure -B x} {expected integer but got "x" for parameter "-B"} ? {o cget -B} 1002 } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/class-method.test000066400000000000000000000077321242365656200155300ustar00rootroot00000000000000# -*- Tcl -*- package require nx::test # # The first test series without the conveniance layer # nx::test case class-methods-0 { nx::Class create M1 nx::Class create C { ? {::C public class method foo {} {return foo}} "'class' is not a method defining method" :public object method f args {next} } ? {::C class mixins set M1} \ "method 'class' unknown for ::C; consider '::C create class mixins set M1' instead of '::C class mixins set M1'" ? {::C class filter f} \ "method 'class' unknown for ::C; consider '::C create class filter f' instead of '::C class filter f'" ? {lsort [::C info object methods]} "f" ? {lsort [::C info]} \ "valid submethods of ::C info: children class filters has heritage info instances lookup method methods mixinof mixins name object parent precedence slots subclasses superclasses variable variables vars" } # # require the conveniance layer # and make it verbose # package require nx::class-method nx::configure class-method-warning on nx::test case class-methods-1 { nx::Class create M1 nx::Class create ::C { :public class method foo {} {return [:pm1]} :public class method f args {next} :protected class method pm1 args {return pm1} :public class alias a ::C::pm1 :public class forward fwd %self pm1 :private class method priv args {return priv} :class method pm2 args {return pm2} :class property -accessor public p :class variable v1 1 :class variable -incremental v2:integer 1 # # public, protected, private # alias, forward # } ? {::C info object methods} "v2 p foo fwd a f" ? {lsort [::C info object methods -callprotection protected]} "per-object-slot pm1 pm2" ? {lsort [::C info object methods -callprotection private]} "priv" ? {::C class info methods} "v2 p foo fwd a f" ? {lsort [::C class info methods -callprotection protected]} "per-object-slot pm1 pm2" ? {lsort [::C class info methods -callprotection private]} "priv" ? {::C class info variables} "::C::per-object-slot::v2 ::C::per-object-slot::p" ? {::C info object variables} "::C::per-object-slot::v2 ::C::per-object-slot::p" ? {::C class info slots} "::C::per-object-slot::v2 ::C::per-object-slot::p" ? {::C pm1} \ "method 'pm1' unknown for ::C; consider '::C create pm1 ' instead of '::C pm1 '" ? {::C foo} "pm1" ? {::C a} "pm1" ? {::C fwd} "pm1" ? {::C class mixins set M1} ::M1 ? {::C class info mixins} ::M1 ? {::C class mixins set ""} "" ? {::C class info mixins} "" ? {::C class filters set f} f ? {::C class info filters} f ? {::C class filters set ""} "" ? {::C class info filters} "" ? {lsort [::C info object methods]} "a f foo fwd p v2" ? {lsort [::C info]} \ "valid submethods of ::C info: children class filters has heritage info instances lookup method methods mixinof mixins name object parent precedence slots subclasses superclasses variable variables vars" } # # delete class method, class property, class variable # nx::test case class-methods-2 { nx::Class create ::C { :public class method foo {} {return foo} :class property -accessor public p :class variable -incremental v1:integer 1 } ? {C class info methods} "p foo v1" ? {C class info variables} "::C::per-object-slot::p ::C::per-object-slot::v1" ? {C class delete method foo} "" ? {C class info methods} "p v1" ? {C class info variables} "::C::per-object-slot::p ::C::per-object-slot::v1" ? {C class delete property p} "" ? {C class info methods} "v1" ? {C class info variables} "::C::per-object-slot::v1" ? {C class delete variable v1} "" ? {C class info methods} "" ? {C class info variables} "" } # # require method # nx::test case class-methods-2 { nsf::method::provide set {::nsf::method::alias set -frame object ::set} nx::Class create ::C { :require class method set } ? {C class info methods} "set" ? {C info object methods} "set" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/contains.test000066400000000000000000000072411242365656200147560ustar00rootroot00000000000000# -*- Tcl -*- package prefer latest package require nx # # Intentionally, we do not want to make a "namespace import" in this # test file. Run this file via a pure tclsh! # namespace path nx # Don't use test, since both, package test and contains redefine "new", # so we have a conflict.... proc ? {cmd expected {msg ""}} { #puts "??? $cmd" set r [uplevel $cmd] if {$msg eq ""} {set msg $cmd} if {$r ne $expected} { puts stderr "ERROR $msg returned '$r' ne '$expected'" error "FAILED $msg returned '$r' ne '$expected'" } else { puts stderr "OK $msg" } } # # We define here a few attributes of type method, such we can add # arbitrary "-" calls # Class create Tree { :property label :property contains:alias :property foo:alias :public method foo {arg} {set :x $arg} } set y [Tree new -foo hu] ? [list $y eval {set :x}] hu # # actually, the intention was to define an xotcl-like -contains # set x [Tree create 1 -label 1 -contains { ? {self} ::1 ? {namespace current} ::1 Tree create 1.1 -label 1.1 Tree create 1.2 -label 1.2 -contains { ? {self} ::1::1.2 ? {namespace current} ::1::1.2 Tree create 1.2.1 -label 1.2.1 Tree create 1.2.2 -label 1.2.2 -contains { Tree create 1.2.2.1 -label 1.2.2.1 ? {self} ::1::1.2::1.2.2 } Tree create 1.2.3 -label 1.2.3 } Tree create 1.3 -label 1.3 }] set x [Tree create t -contains { ? {Tree create branch} ::t::branch ? {Tree new} ::t::__#1 }] # # Test resolving next without namespace import/path # namespace path "" # make sure, we have no "::next" defined or globally imported ? {info command ::next} "" nx::Class create C { :public method foo {} {next; return 12} :create c1 } ? {c1 foo} 12 ? {c1 foo} 12 C create c2 { set :s [self] set :c [current] :public object method bar {} {return "[set :s]-[set :c]"} } ? {c2 bar} "::c2-::c2" # # Test potential crash, when methodNamePath is computed without a # stack frame # C public method foo {{-new 0} name value} { return $value} catch {c1 foo -name a b} errMsg ? {set errMsg} \ {invalid argument 'b', maybe too many arguments; should be "::c1 foo ?-new /value/? /name/ /value/"} # Test resolving of implicit namespaces in relationcmds (here # superclass) in the nx namespace. namespace path "" namespace eval ::nx { #puts stderr =====1 set c [Class create C -superclass Class { :object method foo {} {;} }] ? {set c} ::C # recreate set c [Class create C -superclass Class ] ? {set c} ::C #puts stderr =====3 } # # Forget and reload nx # #puts ====NX-[package versions nx]-[set auto_path] package forget nx package req nx #puts ====XOTCL-[package versions XOTcl]-[set auto_path] package require XOTcl 2.0 package forget XOTcl package require XOTcl 2.0 ######################################################################## # # Test that we do not allow to mix object systems within the intrinsic # classes of an object. Otherwise we would have problems with the # recreate "C0 create c0". On a recreate of c0 the object system for # C0 is ::xotcl, therefore we try to call recreate. In the C0 class # hierarchy is from nx and contains no "recreate" method. ? {catch {::xotcl::Class create C0 -superclass ::nx::Object} errorMsg} 1 ? {set ::errorMsg} {class "::C0" has a different object system as class "::nx::Object"} ::nx::Class create C1 -superclass ::nx::Object # trigger the call of "cleanup" to work via method dispatch ::xotcl::Object create o1 o1 proc cleanup {} {puts stderr "CLEANUP"; return} #C0 create c0 #C0 create c0 C1 create c1 C1 create c1 puts stderr "====EXIT [info script]" # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/destroy.test000066400000000000000000001001001242365656200146150ustar00rootroot00000000000000# -*- Tcl -*- package require nx package require nx::test nx::test configure -count 10 ::nx::configure defaultMethodCallProtection false ::nsf::method::alias ::nx::Object set -frame object ::set nx::Class create O -superclass nx::Object { :method init {} { set ::ObjectDestroy 0 set ::firstDestroy 0 } :method destroy {} { incr ::ObjectDestroy #[:info class] dealloc [current] next } } # # classical simple case # set case "simple destroy (1)" nx::test case simple-destroy-1 nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy"; next} C method foo {} { puts stderr "==== $::case [current]" :destroy puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 puts stderr XXXX2 ? "[current] set x" 1 "$::case can still access [current]" puts stderr XXXX3 ? {::nsf::object::exists c1} 1 "$::case object still exists in proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "ObjectDestroy called" } C create c1 c1 foo ? {::nsf::object::exists c1} 0 "$::case object deleted" ? "set ::firstDestroy" 1 "firstDestroy called" # # simple case, destroy does not propagate, c1 survives # set case "simple destroy (2), destroy blocks" nx::test case simple-destroy-2 nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy block"} C method foo {} { puts stderr "==== $::case [current]" :destroy puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 ? "[current] set x" 1 "$::case can still access [current]" ? {::nsf::object::exists c1} 1 "$::case object still exists in proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called" } C create c1 c1 foo ? {::nsf::object::exists c1} 1 "$::case object deleted" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called" # # simple object recreate # set case "recreate" nx::test case recreate nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy"; next} C method foo {} { puts stderr "==== $::case [current]" [:info class] create [current] puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 ? "[current] set x" 1 "$::case can still access [current]" ? {::nsf::object::exists c1} 1 "$::case object still exists in proc" ? "set ::firstDestroy" 0 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called" } C create c1 c1 foo ? {::nsf::object::exists c1} 1 "$::case object deleted" ? "set ::firstDestroy" 0 "firstDestroy called" # # cmd rename to empty, xotcl provides its own rename and calls destroy # .. like simple case above # set case "cmd rename empty (1)" nx::test case rename-empty-1 nx::Object create o nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy"; next} C method foo {} { puts stderr "==== $::case [current]" rename [current] "" puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 ? "[current] set x" 1 "$::case can still access [current]" ? {::nsf::object::exists c1} 1 "$::case object still exists in proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "ObjectDestroy called" } C create c1 c1 foo ? {::nsf::object::exists c1} 0 "$::case object still exists after proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "ObjectDestroy called" # # cmd rename to empty, xotcl provides its own rename and calls # destroy, but destroy does not propagate, c1 survives rename, since # this is the situation like above, as long xotcl's rename is used. # set case "cmd rename empty (2)" nx::test case rename-empty-2 nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy block"} C method foo {} { puts stderr "==== $::case [current]" rename [current] "" puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 ? "[current] set x" 1 "$::case can still access [current]" ? {::nsf::object::exists c1} 1 "$::case object still exists in proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called" } C create c1 c1 foo #puts stderr ======[c1 set x] ? {::nsf::object::exists c1} 1 "$::case object still exists after proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called" # # cmd rename other xotcl object to current, # xotcl's rename invokes a move # set case "cmd rename object to current" nx::test case rename-to-current nx::Object create o nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy"; next} C method foo {} { puts stderr "==== $::case [current]" rename o [current] puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 #? "[current] set x" 1 "$::case can still access [current]" ? "[current] set x" {can't read "x": no such variable} "$::case cannot access [current]" ? {::nsf::object::exists c1} 1 "$::case object still exists in proc" #? "set ::firstDestroy" 0 "firstDestroy called" #? "set ::ObjectDestroy" 0 "ObjectDestroy called" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "ObjectDestroy called" } C create c1 c1 foo ? {::nsf::object::exists c1} 1 "$::case object still exists after proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "ObjectDestroy called" # # cmd rename other proc to current object, # xotcl's rename invokes a move # set case "cmd rename proc to current" nx::test case rename-proc-to-current proc o args {} nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy"; next} C method foo {} { puts stderr "==== $::case [current]" set x [catch {rename o [current]}] ? "set _ $x" 1 "$::case tcl refuses to rename into an existing command" } C create c1 c1 foo ? {::nsf::object::exists c1} 1 "$::case object still exists after proc" ? "set ::firstDestroy" 0 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called" rename o "" # # namespace delete: tcl delays delete until the namespace is not # active anymore. destroy is called after BBBB. Hypothesis: destroy is # called only when we are lucky, since C might be destroyed before c1 # by the namespace delete # set case "delete parent namespace (1)" nx::test case delete-parent-namespace namespace eval ::test { nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy"; next} C method foo {} { puts stderr "==== $::case [current]" namespace delete ::test puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 # # If the following line is commented in, the namespace is deleted # here. Is there a bug with nsPtr->activationCount # #? "[current] set x" 1 "$::case can still access [current]" puts stderr "BBB" puts stderr "???? [current] exists [::nsf::object::exists [current]]" ? "::nsf::object::exists [current]" 0 ;# WHY? puts stderr "???? [current] exists [::nsf::object::exists [current]]" ? "set ::firstDestroy" 0 "firstDestroy called" ? "set ::ObjectDestroy" 0 "$::case destroy not yet called" } } test::C create test::c1 test::c1 foo ? {::nsf::object::exists test::c1} 0 "object still exists after proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "destroy was called when poping stack frame" ? {::nsf::object::exists ::test::C} 0 "class still exists after proc" ? {namespace exists ::test::C} 0 "namespace ::test::C still exists after proc" ? {namespace exists ::test} 1 "parent ::test namespace still exists after proc" ? {namespace exists ::xotcl::classes::test::C} 0 "namespace ::xotcl::classes::test::C still exists after proc" # # namespace delete: tcl delays delete until the namespace is not # active anymore. destroy is called after BBBB, but does not # propagate. # set case "delete parent namespace (2)" nx::test case delete-parent-namespace-2 namespace eval ::test { ? {namespace exists test::C} 0 "exists test::C" nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy block"} C method foo {} { puts stderr "==== $::case [current]" namespace delete ::test puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 # # If the following line is commented in, the namespace is deleted # here. Is there a bug with nsPtr->activationCount # #? "[current] set x" 1 "$::case can still access [current]" puts stderr "BBBB" puts stderr "???? [current] exists [::nsf::object::exists [current]]" ? "::nsf::object::exists [current]" 0 "$::case object still exists in proc";# WHY? puts stderr "???? [current] exists [::nsf::object::exists [current]]" ? "set ::firstDestroy" 0 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called"; # NOT YET CALLED } } test::C create test::c1 test::c1 foo ? {::nsf::object::exists test::c1} 0 "$::case object still exists after proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called" ;# toplevel destroy was blocked # # controlled namespace delete: xotcl has its own namespace cleanup, # topological order should be always ok. however, the object o::c1 is # already deleted, while a method of it is excuted # set case "delete parent object (1)" nx::test case delete-parent-object nx::Object create o nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy"; next} C method foo {} { puts stderr "==== $::case [current]" o destroy puts stderr "AAAA" # the following object::exists call has a problem in Tcl_GetCommandFromObj(), # which tries to access invalid memory puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 #? "[current] set x" 1 "$::case can still access [current]" puts stderr "BBBB" ? {::nsf::object::exists ::o::c1} 0 "$::case object still exists in proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "ObjectDestroy called" } C create o::c1 o::c1 foo ? {::nsf::object::exists ::o::c1} 0 "$::case object o::c1 still exists after proc" ? {::nsf::object::exists o} 0 "$::case object o still exists after proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "ObjectDestroy called" # # controlled namespace delete: xotcl has its own namespace cleanup. # destroy does not delegate, but still o::c1 does not survive, since o # is deleted. # set case "delete parent object (2)" nx::test case delete-parent-object-2 nx::Object create o nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy block"} C method foo {} { puts stderr "==== $::case [current]" o destroy puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 #? "[current] set x" 1 "$::case can still access [current]" puts stderr "BBB" ? {::nsf::object::exists ::o::c1} 0 "$::case object still exists in proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called" } C create o::c1 o::c1 foo ? {::nsf::object::exists ::o::c1} 0 "$::case object still exists after proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called" # # create an other cmd with the current object's name. # xotcl 1.6 crashed on this test # set case "redefine current object as proc" nx::test case redefine-current-object-as-proc nx::Object create o nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy"; next} C method foo {} { puts stderr "==== $::case [current]" proc [current] {args} {puts HELLO} puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 #? "[current] set x" 1 "$::case can still access [current]" puts stderr "BBB" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "ObjectDestroy called" ? {::nsf::object::exists c1} 0 "$::case object still exists in proc" } C create c1 c1 foo ? {::nsf::object::exists c1} 0 "$::case object still exists after proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "ObjectDestroy called" rename c1 "" # # delete the active class # set case "delete active class" nx::test case delete-active-class nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy"; next} C method foo {} { puts stderr "==== $::case [current]" C destroy puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 #? "[current] set x" 1 "$::case can still access [current]" puts stderr "BBB" #? [:info class] ::xotcl::Object "object reclassed" ? [:info class] ::C "object reclassed?" ? "set ::firstDestroy" 0 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called" ? {::nsf::object::exists c1} 1 "object still exists in proc" #? {::nsf::is class ::C} 0 "class still exists in proc" ? {::nsf::is class ::C} 1 "class still exists in proc" } C create c1 c1 foo ? {::nsf::object::exists c1} 1 "object still exists after proc" ? [c1 info class] ::nx::Object "after proc: object reclassed?" ? "set ::firstDestroy" 0 "firstDestroy called" ? "set ::ObjectDestroy" 0 "ObjectDestroy called" # # delete active object nested in class # set case "delete active object nested in class" nx::test case delete-active-object-nested-in-class nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy"; next} C method foo {} { puts stderr "==== $::case [current]" C destroy puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 #? "[current] set x" 1 "$::case can still access [current]" puts stderr "BBB" #? "set ::firstDestroy" 0 "firstDestroy called" ? "set ::firstDestroy" 1 "firstDestroy called" #? "set ::ObjectDestroy" 0 "ObjectDestroy called" ? "set ::ObjectDestroy" 1 "ObjectDestroy called" ? [:info class] ::C "object reclassed" #? [:info class] ::xotcl::Object "object reclassed" ? {::nsf::object::exists ::C::c1} 1 "object still exists in proc" ? {::nsf::is class ::C} 1 "class still exists in proc" } C create ::C::c1 C::c1 foo #puts stderr ======[::nsf::object::exists ::C::c1] ? {::nsf::object::exists ::C::c1} 0 "object still exists after proc" ? {::nsf::is class ::C} 0 "class still exists after proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "ObjectDestroy called" # nx::test case nesting-destroy { nx::Object create x nx::Object create x::y x destroy ? {::nsf::object::exists x} 0 "parent object gone" ? {::nsf::object::exists x::y} 0 "child object gone" } nx::test case deleting-aliased-object1 { nx::Object create o nx::Object create o2 # behave like an ensemble: aliased object has self of the caller ::nsf::object::property o2 perobjectdispatch 1 ::nsf::method::alias o a o2 ? {o a} ::o2 "call object via alias" ? {o info object method type a} alias ## the ensemble-object needs per-object methods o2 object method info args {:info {*}$args} o2 object method set args {:set {*}$args} ::nsf::object::property o2 keepcallerself 1 ? {o a info vars} "" "call info on aliased object" ? {o set x 10} 10 "set variable on object" ? {o info vars} x "query vars" ? {o a info vars} x "query vars via alias" ? {o a set x} 10 "set var via alias" o2 destroy #? {o a info vars} "Trying to dispatch deleted object via method 'a'" "1st call on deleted object" #? {o a info vars} "::o: unable to dispatch method 'a'" "2nd call on deleted object" ? {o a info vars} {target "o2" of alias a apparently disappeared} "1st call on deleted object" ? {o a info vars} {target "o2" of alias a apparently disappeared} "2nd call on deleted object" } nx::test case deleting-aliased-object2 { nx::Object create o nx::Object create o2 # The methods of the aliased object have their own self ::nsf::method::alias o a o2 ? {o a} ::o2 "call object via alias" ? {o info object method type a} alias # In order to avoid recursive calls, we have to provide the # selector for the method definitions in nx::Object o2 object method info args {: ::nsf::classes::nx::Object::info {*}$args} o2 object method set args {: ::nsf::classes::nx::Object::set {*}$args} ? {o a info vars} "" "call info on aliased object" ? {o set x 10} 10 "set variable on object o" ? {o info vars} x "query vars of o" ? {o a info vars} "" "query vars via alias (from o2)" ? {o a set y 1} 1 "set var via alias (on o2)" ? {o a info vars} y "query vars via alias (from o2)" o2 destroy #? {o a info vars} "Trying to dispatch deleted object via method 'a'" "1st call on deleted object" #? {o a info vars} "::o: unable to dispatch method 'a'" "2nd call on deleted object" ? {o a info vars} {target "o2" of alias a apparently disappeared} "1st call on deleted object" ? {o a info vars} {target "o2" of alias a apparently disappeared} "2nd call on deleted object" } set case "deleting object with alias to object" nx::test case deleting-object-with-alias-to-object nx::Object create o nx::Object create o3 ::nsf::method::alias o x o3 o destroy ? {::nsf::object::exists o} 0 "parent object gone" ? {::nsf::object::exists o3} 1 "aliased object still here" o3 destroy ? {::nsf::object::exists o3} 0 "aliased object destroyed" set case "create an alias, and delete cmd via aggregation" nx::test case create-alias-delete-via-aggregation nx::Object create o nx::Object create o3 ::nsf::method::alias o x o3 #o::x destroy o3 destroy ? {o x foo} {target "o3" of alias x apparently disappeared} #? {o x foo} {Trying to dispatch deleted object via method 'x'} ? {::nsf::object::exists o3} 0 "aliased object destroyed" o destroy # # create an alias, and recreate obj # nx::test case create-alias-and-recreate-obj { nx::Object create o nx::Object create o3 o object alias x o3 nx::Object create o3 o3 object method set args {: ::nsf::classes::nx::Object::set {*}$args} o x set a 13 ? {o x set a} 13 "aliased object works after recreate" } # # create an alias on the class level, double aliasing, delete aliased # object # nx::test case create-alias-on-class-delete-aliased-obj { nx::Class create C nx::Object create o nx::Object create o3 ::nsf::object::property o keepcallerself 1 ::nsf::object::property o3 keepcallerself 1 ::nsf::object::property o perobjectdispatch 1 ::nsf::object::property o3 perobjectdispatch 1 o object alias a o3 C alias b o o3 object method set args {: ::nsf::classes::nx::Object::set {*}$args} o object method set args {: ::nsf::classes::nx::Object::set {*}$args} C create c1 ? {c1 b set B 2} 2 "call 1st level" ? {c1 b a set A 3} 3 "call 2nd level" ? {c1 set B} 2 "call 1st level ok" ? {c1 set A} 3 "call 2nd level ok" o destroy #? {c1 b} "Trying to dispatch deleted object via method 'b'" "call via alias to deleted object" ? {c1 b} {target "o" of alias b apparently disappeared} "call via alias to deleted object" } # # create an alias on the class level, double aliasing, destroy class # nx::test case create-alias-on-class-destroy-class { nx::Class create C nx::Object create o nx::Object create o3 o object alias a o3 C alias b o C create c1 C destroy ? {::nsf::object::exists o} 1 "object o still here" ? {::nsf::object::exists o3} 1 "object o3 still here" } # # test cases where preexisting namespaces are re-used # nx::test case module { # create a namespace with an object/class in it namespace eval ::module { nx::Object create foo } # reuse the namespace for a class/object nx::Class create ::module ? {::nsf::is class ::module} 1 # delete the object/class ... and namespace ::module destroy ? {::nsf::is class ::module} 0 } nx::test case namespace-import { namespace eval ::module { nx::Class create Foo { :create foo } namespace export Foo foo } nx::Class create ::module { :create mod1 } ? {::nsf::is class ::module::Foo} 1 ? {::nsf::is class ::module::foo} 0 ? {::nsf::object::exists ::module::foo} 1 ? {::nsf::is class ::module} 1 nx::Object create ::o { :require namespace } namespace eval ::o {namespace import ::module::*} ? {::nsf::is class ::o::Foo} 1 ? {::nsf::object::exists ::o::foo} 1 # do not destroy namespace imported objects/classes ::o destroy ? {::nsf::is class ::o::Foo} 0 ? {::nsf::object::exists ::o::foo} 0 ? {::nsf::is class ::module::Foo} 1 ? {::nsf::object::exists ::module::foo} 1 ::module destroy } # to avoid CallDirectly, we could activate this line ::nx::Class create M {:method dealloc args {next}} nx::test case delete-parent-namespace-dealloc namespace eval ::test { nx::Class create C -superclass O C method destroy {} {incr ::firstDestroy; puts stderr " *** [current] destroy"; next} C method foo {} { puts stderr "==== $::case [current]" namespace delete ::test puts stderr "AAAA [current] exists [::nsf::object::exists [current]]" :set x 1 # # If the following line is commented in, the namespace is deleted # here. Is there a bug with nsPtr->activationCount # #? "[current] set x" 1 "$::case can still access [current]" puts stderr "???? [current] exists [::nsf::object::exists [current]]" ? "::nsf::object::exists [current]" 0 ;# WHY? puts stderr "???? [current] exists [::nsf::object::exists [current]]" ? "set ::firstDestroy" 0 "firstDestroy called" ? "set ::ObjectDestroy" 0 "$::case destroy not yet called" } } test::C create test::c1 test::c1 foo ? {::nsf::object::exists test::c1} 0 "object still exists after proc" ? "set ::firstDestroy" 1 "firstDestroy called" ? "set ::ObjectDestroy" 1 "destroy was called when poping stack frame" ? {::nsf::object::exists ::test::C} 0 "class still exists after proc" ? {namespace exists ::test::C} 0 "namespace ::test::C still exists after proc" ? {namespace exists ::test} 1 "parent ::test namespace still exists after proc" ? {namespace exists ::xotcl::classes::test::C} 0 "namespace ::xotcl::classes::test::C still exists after proc" nx::test case destroy-during-init { # create class nx::Class create Foo { :public method bar {} {return 1} :public method baz {} {return 2} } # create object Foo create f1 { :bar; :baz; :destroy } ? {info command f1} "" "explicit destroy of object" set c [nx::Class new { :public method bar {} {return 1} :public method baz {} {return 2} :new { :bar; :baz; :destroy } :destroy }] ? [list info command $c] "" "explicit destroy of class" package require nx::volatile ::nsf::method::require ::nx::Object volatile # create new class and object and cleanup everything set x [nx::Class new { :volatile :public method bar {} {return 1} :public method baz {} {return 2} :new { :volatile; :bar; :baz } }] ? [list info command $x] "" "destroy via volatile" set x [nx::Class new -volatile { :public method bar {} {return 1} :public method baz {} {return 2} :new { :volatile; :bar; :baz } }] ? [list info command $x] $x "destroy via volatile method" # create new class and object and cleanup everything + 2 filters ::nx::Object public method f1 args {next} ::nx::Object public method f2 args {next} ::nx::Object filters set {f1 f2} set x [nx::Class new { :volatile :public method bar {} {return 1} :public method baz {} {return 2} :new { :volatile; :bar; :baz } }] ? [list info command $x] "" "destroy via volatile + 2 filters" set x [nx::Class new -volatile { :public method bar {} {return 1} :public method baz {} {return 2} :new { :volatile; :bar; :baz } }] ? [list info command $x] $x "destroy via volatile method + 2 filters" ::nx::Object filters set "" } nx::test case nested-ordered-composite { # The following test case an explicit deletion/redefinition of an # toplevel object (o1) will cause the implicit deletion of a nested # object o1::o2. The object o2 has as well several included objects, # containing an "ordered composite". The deletion of the ordered # compostite causes the (explicit) deletion of its siblings (all # children of o1::o2). This is actually a stress test for the deletion # of o2's namespace, since the loop over its children will be # confronted with the deletion of indirectly deleted items (deleted by # the deletion of the ordered composite). nx::Class create C { :property os :public method destroy {} { #puts stderr "[self] destroy ${:os}" foreach o ${:os} { if {[::nsf::object::exists $o]} { #puts stderr "--D $o destroy" $o destroy } next } } } # # 10 siblings of oc1: # deletion order in bucket: 8 4 10 9 5 1 6 2 oc1 7 3 # oc1 deletes 7 and 3, fine # ... loop might run into an epoched cmd -> might crash # set c 0 for {set i 0} {$i < 10} {incr i} { set os [list] for {set j 0} {$j < 10} {incr j} {lappend os ::o1::o2::[incr c]} nx::Object create ::o1 nx::Object create ::o1::o2 foreach o $os {nx::Object create $o} C create ::o1::o2::oc1 -os $os ? {llength [o1 info children]} 1 ? {llength [o1::o2 info children]} 11 } ### 20 siblings of oc1 (has to be >12): # deletion order in bucket: 17 18 1 20 19 2 3 4 5 6 7 8 9 19 11 oc1 12 13 14 15 16 # oc1 deletes 12 13 14 15 16 # after destroy of oc1 # a) NextHashEntry(hSearch) returns valid looking hPtr # b) Tcl_GetHashValue(hPtr) returns garbage (uninitialized memory?) instead of cmd # --> might crash # set c 0 for {set i 0} {$i < 10} {incr i} { set os [list] for {set j 0} {$j < 20} {incr j} {lappend os ::o1::o2::[incr c]} nx::Object create ::o1 nx::Object create ::o1::o2 foreach o $os {nx::Object create $o} C create ::o1::o2::oc1 -os $os ? {llength [o1 info children]} 1 ? {llength [o1::o2 info children]} 21 } # similar to above, but this time partial deletes occur set c 0 for {set i 0} {$i < 10} {incr i} { set os [list] for {set j 0} {$j < 20} {incr j} {lappend os ::o1::o2::[incr c]} nx::Object create ::o1 nx::Object create ::o1::o2 foreach o $os {nx::Object create $o} C create ::o1::o2::ocX -os {} C create ::o1::o2::ocY -os $os ? {llength [o1 info children]} 1 ? {llength [o1::o2 info children]} 22 } } # # The following tests the deletion order triggering implict # deletions. This caused a crash in nsf 2.0b2. # package req nx::serializer nx::test case class-object-property { nx::Class create C { :object property -accessor public x :property a:int } ? {::nsf::object::exists ::C} 1 ? {::nsf::object::exists ::C::slot} 1 set s(C) [C serialize] C destroy ? {::nsf::object::exists ::C} 0 ? {::nsf::object::exists ::C::slot} 0 eval $s(C) ? {::nsf::object::exists ::C} 1 ? {::nsf::object::exists ::C::slot} 1 C::slot destroy ? {::nsf::object::exists ::C} 1 ? {::nsf::object::exists ::C::slot} 0 C destroy ? {::nsf::object::exists ::C} 0 } nx::test case unset-traces-during-cleanup { global i set i [interp create] $i eval { package req nx nx::Object create o { set :x 100 # The following line is tricky: the trailing ";#" # is used to trim the undesirable extra arguments # from the trace command. ::trace add variable :x unset "[list set ::X ${:x}];#" } } ? {$i eval {info exists ::X}} 0 $i eval {::nsf::finalize -keepvars} ? {$i eval {info exists ::X}} 1 ? {$i eval {set ::X}} 100 interp delete $i unset i } nx::test case unset-traces-during-cleanup-with-destroy { # # Make sure that a very-late destroy (in the unset trace) does not # fire ... and does not cause any side effects. # global i set i [interp create] $i eval { package req nx nx::Object create o { :public object method destroy args { incr ::X next } set :x 100 # The following line is tricky: the trailing ";#" # is used to trim the undesirable extra arguments # from the trace command. ::trace add variable :x unset "[list ::incr ::X]; [list [self] destroy];#" } } ? {$i eval {info exists ::X}} 0 $i eval {::nsf::finalize -keepvars} ? {$i eval {info exists ::X}} 1 ? {$i eval {set ::X}} 2 interp delete $i unset i } nx::test case unset-traces-during-cleanup-with-destroy-2 { # # We are safe when trying to delete the base class/metaclass ... # global i set i [interp create] $i eval { package req nx nx::Object create o { set :x _ # The following line is tricky: the trailing ";#" # is used to trim the undesirable extra arguments # from the trace command. ::trace add variable :x unset "[list catch [list ::nx::Object destroy] msg1]; [list catch [list ::nx::Class destroy] msg2]; set ::MSG \[list \$msg1 \$msg2\];#" } } ? {$i eval {info exists ::MSG}} 0 $i eval {::nsf::finalize -keepvars} ? {$i eval {info exists ::MSG}} 1 ? {$i eval {set ::MSG}} [list "cannot destroy base class ::nx::Object" "cannot destroy base class ::nx::Class"] interp delete $i unset i } nx::test case unset-traces-during-cleanup-with-reset { # # Check for leaks ... # global i set i [interp create] $i eval { package req nx nx::Object create o { set :x 100 # The following line is tricky: the trailing ";#" # is used to trim the undesirable extra arguments # from the trace command. ::trace add variable :x unset "[list ::nsf::var::set [self] x ${:x}];#" } } ? {$i eval {::nsf::object::exists ::o}} 1 ? {$i eval {info commands ::o}} ::o $i eval {::nsf::finalize -keepvars} ? {$i eval {info commands ::o}} "" interp delete $i unset i } # # Exercise renaming of cmds which are used as methods # nx::test case rename-cached-method { # Create a class with a namespace nx::Class create A {:public object method foo args {}} # # Add a proc named "new" to the namespace of the class. # This is not recommended, but we can't avoid it. # proc ::A::new {} { return "something from A" } # # Call the proc via the method interface. After the call the cmd is # cached in the Tcl_Obj "new". # ? {A new} "something from A" # # Delete the proc. The rename command has to take care, that the # cached cmd has to be invalidated. # rename ::A::new "" # # We expect that the original method works again. # ? {string match ::nsf::__#* [A new]} 1 # # Now try the same with the internal namespace from nsf. Messing # around there is even less wanted, but still, we can't avoid this. # We make first a backup of the method. # rename ::nsf::classes::nx::Class::new ::nsf::classes::nx::Class::new.orig proc ::nsf::classes::nx::Class::new {} { return "something" } ? {A new} "something" # # Delete the proc and call "new" again # rename ::nsf::classes::nx::Class::new "" ? {A new} "method 'new' unknown for ::A; consider '::A create new ' instead of '::A new '" # # Restore the original state # rename ::nsf::classes::nx::Class::new.orig ::nsf::classes::nx::Class::new # # We expect that the original method works again. # ? {string match ::nsf::__#* [A new]} 1 } # # Create a cyclical class dependency and delete it manually # nx::test case cyclical-dependency { nx::Object create o1 ? {nx::Class create o1::C} ::o1::C ? {nsf::relation::set o1 class o1::C} ::o1::C o1 destroy } # # Create a cyclical class dependency and let it be deleted on # object-system-cleanup # nx::Object create o1 nx::Class create o1::C nsf::relation::set o1 class o1::C # # Create a cyclical superclass dependency and delete it manually # nx::test case cyclical-dependency { nx::Class create C nx::Class create C::* ? {nsf::relation::set C superclass {C::* nx::Object}} "" C destroy } # # Create a cyclical superclass dependency and let it be deleted on # object-system-cleanup # nx::Class create C nx::Class create C::* nsf::relation::set C superclass {C::* nx::Object} puts "==== EXIT [info script]" # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/disposition.test000066400000000000000000001234671242365656200155150ustar00rootroot00000000000000# -*- Tcl -*- package prefer latest package req nx package require nx::test # # test cases for disposition "alias" and "forward" # nx::test case basics { Class create C { :object property {inst "::__%&singleton"} :method foo {x} { #puts stderr [current method] set :[current method] $x } :method bar {} {;} :protected method baz {y} { #puts stderr [current method] set :my[current method] $y } # # some testing helpers # :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [current] } :setObjectParams "" :public object method new args { return [:create ${:inst} {*}$args] } } foreach paramType {forward alias} { # # Restricted to object parameters only? # set msg "parameter option '$paramType' not allowed" ? [list C method m1 -foo:$paramType {;}] $msg ? [list C method m1 foo:$paramType {;}] $msg # # Not applicable in parametercheck # ? [list ::nsf::is $paramType $msg] "invalid value constraints \"$paramType\"" } # # Do aliases and forwarder set instance variables? They should not. # C setObjectParams -baz:alias ? {[C new -baz BAZ] eval {info exists :baz}} 0 C setObjectParams {{{-baz:forward,method=%self %method}}} ? {[C new -baz BAZ] eval {info exists :baz}} 0 # # Note, currently alias/forward disposition operate on public and # protected target methods alike. Is this intended? For example, # providing access through the parameter interface to protected # methods etc. (at the instantiation site only) ? Or, are they # expected to be public ... # ### objectparameter are from the intentions public: the typical ### use-case is that someone wants to configure an object to be ### created, before the object exists.... # # 1) Positional object parameters + alias/forward disposition? # # # Passing a single argument to a positional alias # C setObjectParams foo:alias ? {C new FOO} "::__%&singleton" ? {C new {FOO FAA}} "::__%&singleton" ### ### Whenever a value is provided (default value or actual value) the ### parameter is evaluated. ### C setObjectParams {{foo:alias ""}} ? {C new} "::__%&singleton" C setObjectParams {{-foo:alias "fooDefault"}} ? {[C new] eval {set :foo}} "fooDefault" # # What about multi-argument vectors? # C eval { :method multi-2 {x y} { set :[current method] [current args] } :method multi-escape {x} { set :[current method] $x } :method multi-args {args} { set :[current method] $args } } # # Parameters are limited to a single value by the object parameter. # C setObjectParams {{-multi-2:alias}} ? {[C new -multi-2 {X Y}] eval {set :multi-2}} \ "wrong # args: should be \"multi-2 x y\"" # # Passing multiple arguments as a list # C setObjectParams {{-multi-escape:alias}} ? {[C new -multi-escape [list X Y]] eval {set :multi-escape}} \ [list X Y] # # Passing multiple arguments as a list, passed to a args argument # list. # C setObjectParams {{-multi-args:alias}} ? {[C new -multi-args [list X Y]] eval {set :multi-args}} \ [list [list X Y]] # # As used, all parameters receive currently 0 or 1 # argument. The same is true for disposition "alias" an # "forward". One could consider to unbox a parameter list via a # parameter option "expand" (like {*}) for alias|forward parameter # specs, e.g.: # {-multi-2:alias,expand} # {-multi-2:forward,method=...,expand} # # Without the sketched extension, one could use eval in a forwarder. # puts stderr ===1 C setObjectParams {{{-multi-2:forward,method=eval %self %method}}} puts stderr ===2a set x [C new -multi-2 {X Y}] puts stderr [$x eval {set :multi-2}] puts stderr ===2b ? {[C new -multi-2 {X Y}] eval {set :multi-2}} \ "X Y" puts stderr ===3 # # In the positional case, why is FOO not passed on as arg value to # the target method? # C setObjectParams {{{foo:forward,method=%self %method}}} ? {C new FOO} "::__%&singleton" ? {[C new FOO] eval {set :foo}} "FOO" # # Naming of the parameter spec element "method": It fits the alias # disposition, but is a little irritating in the context of a # forward. One would expect forwardspec or simply "spec" (as this is # used in the docs, the error messages etc.), e.g.: # # {foo:forward,spec=%self %method} # # 'spec' would also work for 'alias' as it is more general (the spec # of an alias is the method name ...) # #### well, "spec" is not nice for alias, and potentially confusing #### with the parameter spec (the full parameter definition). # # Passing non-positional arguments to target methods (at least # forwarder ones)? # C method multi-mix {-x y args} { set :[current method] --x-$x--y-$y--args-$args } C setObjectParams {{{-multi-mix:forward,method=eval %self %method}}} ? {[C new -multi-mix [list -x X Y Z 1 2]] eval {set :multi-mix}} \ "--x-X--y-Y--args-Z 1 2" # # Aliased methods with nonpos arguments are rendered entirely # useless by the single-value limitation (see also above): # C method single-np {-x:required} { set :[current method] --x-$x } C setObjectParams {{-single-np:alias}} ? {[C new -single-np [list -x]] eval {set :single-np}} \ "value for parameter '-x' expected" ? {[C new -single-np [list -x X]] eval {set :single-np}} \ "invalid non-positional argument '-x X', valid are : -x; should be \"::__%&singleton single-np -x /value/\"" # # INTERACTIONS with other parameter types # # There are two validation points: # 1) the object parameter validation on the initial argument set # 2) the target method validation on the (mangled) argument set # # ... they can deviate from each other, to a point of direct # conflict # # # Allowed built-in value types (according to feature matrix in # parameters.test) # set msg {expected $mtype but got \"$testvalue\" for parameter \"x\"} dict set types boolean [list testvalue f mtype object msg $msg] dict set types integer [list testvalue 81 mtype punct msg $msg] dict set types object [list testvalue ::C mtype integer msg $msg ] dict set types class [list testvalue ::C mtype boolean msg $msg] dict set types object,type=::nx::Class \ [list testvalue ::C mtype object,type=::C \ msg "expected object of type ::C but got \"::C\"\ for parameter \"x\""] # for aliases ... dict for {t tdict} $types { dict with tdict { ::C public method foo [list x:$t] { set :[current method] $x } ::C setObjectParams [list [list -foo:alias,$t]] ? "::nsf::is $t \[\[::C new -foo $testvalue\] eval {set :foo}\]" 1 "check: -foo:alias,$t" } } ::C public method baz {x} { return $x } dict for {t tdict} $types { dict with tdict { ::C public method foo [list x:$mtype] { set :[current method] $x } ::C setObjectParams [list [list -foo:alias,$t]] ? "::nsf::is $t \[\[::C new -foo $testvalue\] eval {set :foo}\]" \ [subst $msg] } } # # TODO: update the matrix in parameters.test (feature matrix) # ### ### The question is, what happens with the matrix. The matrix is in ### some respects not complete (no disposition) and contains old ### namings (e.g. allowempty, multiple) and contains types removed ### some time ago (such as e.g. "relation"). ### # # define a user defined parameter type # ::nx::methodParameterSlot object method type=mytype {name value} { if {$value < 1 || $value > 3} { error "Value '$value' of parameter $name is not between 1 and 3" } } array set script {alias "method=baz" forward "method=%self %method"} foreach disposition [list alias forward] { C setObjectParams [list [list -foo:$disposition,switch]] ? {C new} "parameter invocation types cannot be used with option 'switch'" \ "switch not allowed for $disposition" C setObjectParams [list [list -baz:$disposition,mytype,$script($disposition)]] ? {C new -baz 1} "::__%&singleton" \ "disposition $disposition, user defined type, valid value" C setObjectParams [list [list -baz:$disposition,mytype,$script($disposition)]] ? {C new -baz 0} "Value '0' of parameter baz is not between 1 and 3" \ "disposition $disposition, user defined type, invalid value" C setObjectParams [list [list -foo:$disposition,xxx]] ? {C new} "::__%&singleton" \ "disposition $disposition, unknown user defined type - just a warning" C setObjectParams [list [list -foo:$disposition,type=::C]] ? {C new} "parameter option 'type=' only allowed for parameter types 'object' and 'class'" # # The 'arg=...' option should not be used, consider using 'method=...' # C setObjectParams [list [list -foo:$disposition,arg=BOOM]] ? {C new} "parameter option 'arg=' only allowed for user-defined converter" } # # The option 'method=...' applies to disposition types only # C setObjectParams [list [list -foo:initcmd,method=BOOM]] ? {C new} "parameter option 'method=' only allowed for parameter types 'alias', 'forward' and 'slotset'" C setObjectParams [list [list -foo:alias,forward]] ? {C new} "parameter option 'forward' not valid in this option combination" C setObjectParams [list [list -foo:forward,alias]] ? {C new} "parameter option 'alias' not valid in this option combination" C setObjectParams [list [list -foo:alias,initcmd]] ? {C new} "parameter option 'initcmd' not valid in this option combination" C setObjectParams [list [list -foo:forward,initcmd]] ? {C new} "parameter option 'initcmd' not valid in this option combination" } nx::test case dispo-multiplicities { Class create S { :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [current] } #:object method __object_configureparameter {} { # return ${:objectparams} #} :public method foo {args} { set :foo $args return $args } } # # On multiplicity classes ... # # ... implying a Tcl list value: 1..*, 0..* # ... implying a Tcl word value: 1..1, 0..1 # S setObjectParams {-foo:alias,1..*,boolean} S method foo {x:0..1,boolean} { set :foo $x } ? {[S new -foo [list f f]] eval {info exists :foo}} \ "expected boolean but got \"f f\" for parameter \"x\"" S setObjectParams {-foo:alias,1..*,integer} S method foo {x:1..1,integer} { set :foo $x } ? {[S new -foo [list a 1]] eval {info exists :foo}} \ "invalid value in \"a 1\": expected integer but got \"a\" for parameter \"-foo\"" ? {[S new -foo [list 0 1]] eval {info exists :foo}} \ "expected integer but got \"0 1\" for parameter \"x\"" ? {[S new -foo [list]] eval {info exists :foo}} \ "invalid value for parameter '-foo': list is not allowed to be empty" ? {[S new -foo 5] eval {info exists :foo}} 1 ? {[S new -foo f] eval {info exists :foo}} \ "invalid value in \"f\": expected integer but got \"f\" for parameter \"-foo\"" S setObjectParams {-foo:alias,0..*,false} S method foo {x:0..1,false} { set :foo $x } ? {[S new -foo [list a 1]] eval {info exists :foo}} \ "invalid value in \"a 1\": expected false but got \"a\" for parameter \"-foo\"" ? {[S new -foo [list f 0]] eval {info exists :foo}} \ "expected false but got \"f 0\" for parameter \"x\"" ? {[S new -foo [list t]] eval {info exists :foo}} \ "invalid value in \"t\": expected false but got \"t\" for parameter \"-foo\"" ? {[S new -foo [list f]] eval {info exists :foo}} 1 ? {[S new -foo [list]] eval {info exists :foo}} 1 } nx::test case dispo-returns { Class create R { :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [current] } } # # Alias/forward dispositions are unavailable as parameter types of return checkers # set methods(raz) [R public object method raz {} {;}] foreach dispoSpec { alias,noarg alias,method=xxx {forward,method=%self xxx} initcmd } { ::nsf::method::property R $methods(raz) returns $dispoSpec ? {R raz} "invalid value constraints \"$dispoSpec\"" } # # Interactions between disposition types and the return value checkers # ::nsf::configure checkresults true # -- R setObjectParams -foo:alias,true set methods(foo) [R public method foo {x:true} -returns false { set :foo $x }] ? {[R new] foo t} "expected false but got \"t\" as return value" R setObjectParams [list -foo:alias,true bar:alias,false] ::nsf::method::property R $methods(foo) returns boolean set methods(bar) [R public method bar {y:false} -returns true { set :bar $y }] ? {[R new -foo t f] eval {info exists :bar}} "expected true but got \"f\" as return value" R setObjectParams [list -foo:alias,true bar:alias,false \ [list baz:alias,wideinteger,substdefault {[expr {2 ** 63}]}]] ::nsf::method::property R $methods(bar) returns boolean set methods(baz) [R public method baz {z:wideinteger} -returns int32 { set :baz $z }] ? {[R new -foo t f [expr {2 ** 31}]] eval {info exists :foo}} 1 ? {[R new -foo t f] eval {info exists :baz}} "expected int32 but got \"[expr {2 ** 63}]\" as return value" ? {[R new -foo t f] eval {info exists :baz}} "expected int32 but got \"[expr {2 ** 63}]\" as return value" ::nsf::method::property R $methods(baz) returns wideinteger ? {string is wideinteger [[R new -foo t f] eval {set :baz}]} 1 } nx::test case dispo-callstack { Class create Callee { :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [current] } } # # uplevel, upvar (with alias and forward) # Callee public method call {{-level 2} x} { # # The callstack positioning corresponds to the one of # alias/forward target methods in general: # Level -1 -> C-level frame # Level -2 -> Actual caller frame # # Note: Like any aliased methods, target methods of alias # parameters do not have full callstack transparency (e.g., in a # direct call to the target method, level -1 would resolve to the # caller frame) # # ::nsf::__db_show_stack uplevel $level [list set ix $x] upvar $level $x _ incr _ } foreach dispoSpec { {-ah:alias,method=call {call:alias X}} {{{-ah:forward,method=%self call}} {{call:forward,method=%self %method} X}} {{{-ah:forward,method=uplevel %self call -level 1}} {{call:forward,method=uplevel %self %method -level 1} X}} } { Callee setObjectParams $dispoSpec namespace eval __ { ? {info exists X} 0 ? {info exists ix} 0 ? {Callee new; info exists ix} 1 ? {set X} 1 ? {Callee new; info exists X} 1 ? {Callee new; set X} 3 ? {Callee new; set ix} X ? {Callee new -ah X X; set ix} X ? {set X} 6 ? {info exists Y} 0 ? {Callee new -ah X Y; set Y} 1 ? {set X} 7 ? {set ix} Y } namespace delete __ } # # TODO: Test missing elements for method declarations: # /cls/ public class {} {} ... # # / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / # Test the ACTIVE/INACTIVE transparency for the method-variants of # uplevel|upvar # Callee public object method run {} { set self [self] set objparams [:__object_configureparameter] # # The ? helper by default performs a [namespace eval] in the :: # namespace, so the uplevel|upvar would happen in a different, # non-testable callstack branch. Therefore, we have to build the # tests around this limitation (for now) # ? [list set _ [info exists X]] 0 ? [list set _ [info exists ix]] 0 $self new ? [list set _ [info exists ix]] 1 "after 1. uplevel/upvar calls ('$objparams')" ? [list set _ [set X]] 1 "after 1. uplevel/upvar calls ('$objparams')" $self new ? [list set _ [info exists X]] 1 "after 2. uplevel/upvar calls ('$objparams')" $self new ? [list set _ [set X]] 3 "after 3. uplevel/upvar calls ('$objparams')" $self new ? [list set _ [set ix]] X "after 4. uplevel/upvar calls ('$objparams')" $self new -ah X X; ? [list set _ [set ix]] X "after 5. uplevel/upvar calls ('$objparams')" ? [list set _ [set X]] 6 "after 5. uplevel/upvar calls ('$objparams')" ? [list set _ [info exists Y]] 0 $self new -ah X Y; ? [list set _ [set Y]] 1 "after 6. uplevel/upvar calls ('$objparams')" ? [list set _ [set X]] 7 "after 6. uplevel/upvar calls ('$objparams')" ? [list set _ [set ix]] Y } # {{{-ah:forward,method=uplevel %self call -level 1}} {{call:forward,method=uplevel %self %method -level 1} X}} # # a) NSF/Nx methods upvar() and uplevel() # Callee public method call {x} { :uplevel [list set ix $x] :upvar $x _ incr _ } foreach dispoSpec { {-ah:alias,method=call {call:alias X}} {{{-ah:forward,method=%self call}} {{call:forward,method=%self %method} X}} } { Callee setObjectParams $dispoSpec Callee run } # # b) [current callinglevel] # # ... with [uplevel [current callinglevel]] being equivalent to # using NSF/Nx methods upvar() and uplevel() directly. # Callee public method call {x} { # ::nsf::__db_show_stack uplevel [current callinglevel] [list set ix $x] upvar [current callinglevel] $x _ incr _ } foreach dispoSpec { {-ah:alias,method=call {call:alias X}} {{{-ah:forward,method=%self call}} {{call:forward,method=%self %method} X}} } { Callee setObjectParams $dispoSpec Callee run } # # c) [current activelevel] # # ... Currently, in the current testing scenario, there is no # effective difference between #activelevel and #callinglevel, both # skip INACTIVE frames. Callee mixins set [Class new {:public method call args { next }}] foreach dispoSpec { {-ah:alias,method=call {call:alias X}} {{{-ah:forward,method=%self call}} {{call:forward,method=%self %method} X}} } { Callee setObjectParams $dispoSpec Callee run } Callee public method call {x} { uplevel [current activelevel] [list set ix $x] upvar [current activelevel] $x _ incr _ } foreach dispoSpec { {-ah:alias,method=call {call:alias X}} {{{-ah:forward,method=%self call}} {{call:forward,method=%self %method} X}} } { Callee setObjectParams $dispoSpec Callee run } } nx::test case alias-noarg { Class create C { :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [current] } :public method foo {args} { set :foo $args return $args } :public method bar {args} { set :bar $args return $args } } # # nopos arg with noargs, given # C setObjectParams {-bar:alias,noarg} C create c1 -bar ? {c1 eval {info exists :bar}} 1 ? {c1 eval {info exists :x}} 0 # # nopos arg with noargs, not given # C setObjectParams {-bar:alias,noarg} C create c1 ? {c1 eval {info exists :bar}} 0 # # pos arg with noargs # C setObjectParams {foo:alias,noarg} C create c1 ? {c1 eval {info exists :foo}} 1 # # initcmd with default # C setObjectParams {{__init:cmd :foo}} C create c1 ? {c1 eval {info exists :foo}} 1 # # pos arg with noargs and nonposarg with noargs, given # C setObjectParams {foo:alias,noarg -bar:alias,noarg} C create c1 -bar ? {c1 eval {info exists :bar}} 1 ? {c1 eval {info exists :foo}} 1 ? {c1 eval {info exists :x}} 0 # # optional initcmd, like in nx # C setObjectParams {initcmd:cmd,optional} C create c1 {set :x 1} ? {c1 eval {info exists :x}} 1 # # using a default value for initcmd # C setObjectParams {{initcmd:cmd ""}} C create c1 {set :x 1} C create c2 ? {c1 eval {info exists :x}} 1 ? {c2 eval {info exists :x}} 0 # # optional initcmd + non-consuming (nrargs==0) posarg, provided # initcmd # C setObjectParams {foo:alias,noarg initcmd:cmd,optional} C create c1 {set :x 1} ? {c1 eval {info exists :x}} 1 ? {c1 eval {info exists :foo}} 1 ? {c1 eval {info exists :bar}} 0 # # optional initcmd + non-consuming (nrargs==0) posarg, no value for # initcmd # C setObjectParams {foo:alias,noarg initcmd:cmd,optional} C create c1 ? {c1 eval {info exists :x}} 0 ? {c1 eval {info exists :foo}} 1 ? {c1 eval {info exists :bar}} 0 # # initcmd with default + non-consuming (nrargs==0) posarg, no value # for initcmd # C setObjectParams {foo:alias,noarg {initcmd:cmd ""}} C create c1 ? {c1 eval {info exists :x}} 0 ? {c1 eval {info exists :foo}} 1 ? {c1 eval {info exists :bar}} 0 # # non-consuming alias, nonpos alias with noarg, initcmd provided # C setObjectParams {foo:alias,noarg -bar:alias,noarg initcmd:cmd,optional} C create c1 {set :x 1} ? {c1 eval {info exists :foo}} 1 ? {c1 eval {info exists :bar}} 0 ? {c1 eval {info exists :x}} 1 # # non-consuming alias, nonpos alias with noarg, nonpos called, initcmd provided # C setObjectParams {foo:alias,noarg -bar:alias,noarg initcmd:cmd,optional} C create c1 -bar {set :x 1} ? {c1 eval {info exists :foo}} 1 ? {c1 eval {info exists :bar}} 1 ? {c1 eval {info exists :x}} 1 # # non-consuming alias, nonpos alias with noarg, no initcmd provided # C setObjectParams {foo:alias,noarg -bar:alias,noarg initcmd:cmd,optional} C create c1 ? {c1 eval {info exists :foo}} 1 ? {c1 eval {info exists :bar}} 0 ? {c1 eval {info exists :x}} 0 # # non-consuming alias, nonpos alias with noarg, nonpos called, no # initcmd provided # C setObjectParams {foo:alias,noarg -bar:alias,noarg initcmd:cmd,optional} C create c1 -bar ? {c1 eval {info exists :foo}} 1 ? {c1 eval {info exists :bar}} 1 ? {c1 eval {info exists :x}} 0 } # # check inticmd + noarg (should not be allowed) # nx::test case alias-noarg { Class create C { :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [current] } } C setObjectParams {initcmd:cmd,noarg} ? {C create c1} {parameter option "noarg" only allowed for parameter type "alias"} } # # check alias + args # nx::test case alias-args { Class create C { :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [current] } :public method Residualargs args { #puts stderr "aliased RESIDUALARGS <[llength $args]>" #puts stderr "....... <$args>" set :args $args } :public method residualargs args { #puts stderr "residualargs <$args>" } } C copy D # TODO: check the meaning of these C setObjectParams {args} D setObjectParams {-a args} # Configure object parameters to call method Residualargs with # option args when args is used C setObjectParams {args:alias,method=Residualargs,args} D setObjectParams {-a args:alias,method=Residualargs,args} # If no residual args are provided, the method residualargs is not # called. This is the same rule as for all other consuming object # parameter dispatches ? {C create c1} {::c1} ? {c1 eval {info exists :args}} 0 ? {D create c1} {::c1} ? {c1 eval {info exists :args}} 0 # Residual args are provided, the method residualargs is # called. ? {C create c1 1 2 3} {::c1} ? {c1 eval {info exists :args}} 1 ? {c1 eval {set :args}} {1 2 3} ? {D create c1 1 2 3} {::c1} ? {c1 eval {info exists :args}} 1 ? {c1 eval {set :args}} {1 2 3} # # Provide a default for args. # C setObjectParams {{args:alias,method=Residualargs,args {hello world}}} # use the default ? {C create c1} {::c1} ? {c1 eval {info exists :args}} 1 ? {c1 eval {set :args}} {hello world} # override the default ? {C create c1 a b c} {::c1} ? {c1 eval {info exists :args}} 1 ? {c1 eval {set :args}} {a b c} # # don't allow other types for parameter option "args" # C setObjectParams {{args:alias,int,method=Residualargs,args {hello world}}} ? {C create c1} {refuse to redefine parameter type of 'args' from type 'integer' to type 'args'} ? {nsf::is object c1} 0 C setObjectParams {{args:int,alias,method=Residualargs,args {hello world}}} ? {C create c1} {refuse to redefine parameter type of 'args' from type 'integer' to type 'args'} ? {nsf::is object c1} 0 # # don't allow multiplicity settings for parameter option "args" # C setObjectParams {{args:alias,method=Residualargs,0..n,args {hello world}}} ? {C create c1} {multiplicity settings for variable argument parameter "args" not allowed} ? {nsf::is object c1} 0 C setObjectParams {args:alias,method=Residualargs,args,1..n} ? {C create c1} {multiplicity settings for variable argument parameter "args" not allowed} ? {nsf::is object c1} 0 # # make sure, parameter with parameter option "args" is used in last parameter # C setObjectParams {a:alias,method=Residualargs,args -b:integer} ? {C create c1 hello world} {parameter option "args" invalid for parameter "a"; only allowed for last parameter} ? {nsf::is object c1} 0 } nx::test case alias-init { Class create C { :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [current] } :method init {} { incr :y } } # call init between -a and -b C setObjectParams {-a init:alias,noarg -b:integer} ? {C create c1} {::c1} # "init" should be called only once ? {c1 eval {set :y}} 1 } nx::test case submethods-via-aliasparams { # # Could move to submethods.test? # Class create C { :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [current] } } # A depth-1 submethod ... C public method "FOO foo" {} { # next append :msg "[::nsf::current]--[::nsf::current methodpath]--[::nsf::current method]" } # A depth-2 submethod ... C public method "BAR BOO buu" {} { append :msg "[::nsf::current]--[::nsf::current methodpath]--[::nsf::current method]" } # // Ordinary dispatch // # The message send below expands into the following callstack # structure (when viewed at from within foo(), N is the anonymous # call site) # # N+3 |:CscFrame @Type(ENSEMBLE) | <-- foo (leaf) # N+2 |:CscFrame @Call(ENSEMBLE) | <-- FOO (root) # N+1 |:TclFrame| e.g. cmd, [namespace eval], [apply] ? { [C create c1] FOO foo; # N c1 eval {set :msg} } "::c1--FOO foo--foo" # # Submethod levels greater than 1 turn into intermittent frames: # N+4 |:CscFrame @Type(ENSEMBLE) | <-- buu (leaf) # N+3 |:CscFrame @Type(ENSEMBLE) @Call(ENSEMBLE)| <-- BOO (intermittent) # N+2 |:CscFrame @Call(ENSEMBLE) | <-- BAR (root) # N+1 |:TclFrame| # ? { [C create c3] BAR BOO buu; # N c3 eval {set :msg} } "::c3--BAR BOO buu--buu" # // Parameter (alias) dispatch // # # In contrast to an ordinary dispatch, a parameter dispatch results # in a different callstack structure, due to the interferring # configure(): # # N+5 |:CscFrame @Type(ENSEMBLE)| <-- foo (leaf) # N+4 |:CscFrame @Call(ENSEMBLE)| <-- FOO (root) # N+3 |:CscFrame @INACTIVE| <-- (INNER configure() frame) # N+2 |:ObjFrame| <-- ::c2 (OUTER configure() frame) # N+1 |:TclFrame| C setObjectParams [list FOO:alias] ? { [C create c2 foo] eval {set :msg}; # N } "::c2--FOO foo--foo" # # 1) Interleaving @Type(INACTIVE) frames through indirection # # a) Ahead of the ensemble "root" frame (i.e., indirection at the # level the receiver object) # Class create M1 { :public method FOO args { next } } C mixins set M1 # N+4 |:CscFrame @Type(ENSEMBLE) | <-- foo (leaf) # N+3 |:CscFrame @Call(ENSEMBLE) | <-- FOO (root) # N+2 |:CscFrame @INACTIVE| <-- M1.FOO # N+1 |:TclFrame| C setObjectParams [list] ? { [C create c1] FOO foo; # N c1 eval {set :msg} } "::c1--FOO foo--foo" # N+6 |:CscFrame @Type(ENSEMBLE)| <-- foo (leaf) # N+5 |:CscFrame @Call(ENSEMBLE)| <-- FOO (root) # N+4 |:CscFrame @INACTIVE| <-- M1.FOO # N+3 |:CscFrame @INACTIVE| <-- (INNER configure() frame) # N+2 |:ObjFrame| <-- ::c2 (OUTER configure() frame) # N+1 |:TclFrame| C setObjectParams [list FOO:alias] ? { [C create c2 foo] eval {set :msg}; # N } "::c2--FOO foo--foo" # ... the filter variant ... C mixins set {} C public method intercept args { next } C filters set intercept # N+4 |:CscFrame @Type(ENSEMBLE) | <-- foo (leaf) # N+3 |:CscFrame @Call(ENSEMBLE) | <-- FOO (root) # N+2 |:CscFrame @INACTIVE| <-- intercept # N+1 |:TclFrame| C setObjectParams [list] ? { [C create c1] FOO foo; # N c1 eval {set :msg} } "::c1--FOO foo--foo" # N+6 |:CscFrame @Type(ENSEMBLE)| <-- foo (leaf) # N+5 |:CscFrame @Call(ENSEMBLE)| <-- FOO (root) # N+4 |:CscFrame @INACTIVE| <-- intercept # N+3 |:CscFrame @INACTIVE| <-- (INNER configure() frame) # N+2 |:ObjFrame| <-- ::c2 (OUTER configure() frame) # N+1 |:TclFrame| C setObjectParams [list FOO:alias] ? { [C create c2 foo] eval {set :msg}; # N } "::c2--FOO foo--foo" C filters set "" # / / / / / / / / / / / / / / / / / / / / / / / / / / / / / # b) Between root and intermittent or inbetween the set of # intermittent frames (i.e., indirection at the level of # container/ensemble objects) # NOTE: Filters and mixins registered for the container object do # not interleave in ensemble dispatches ... the dispatch lookup # (along the next path) always starts at the top-level # (calling) object. As a result, there are no intermediate frames to # be expected ... Class create M2 { :public method foo args { return "[current class]--[next]" } } C::slot::__FOO object mixins set M2 ? {C::slot::__FOO foo} "::M2--::C::slot::__FOO--foo--foo" C::slot::__FOO eval {unset :msg} C setObjectParams [list] ? { [C create c1] FOO foo; # N c1 eval {set :msg} } "::c1--FOO foo--foo" C::slot::__FOO object mixins set {} C::slot::__FOO public object method intercept {} { return "[current]--[next]" } C::slot::__FOO object filters set intercept ? {C::slot::__FOO foo} "::C::slot::__FOO--::C::slot::__FOO--foo--foo" C setObjectParams [list] ? { [C create c1] FOO foo; # N c1 eval {set :msg} } "::c1--FOO foo--foo" # -- Class create M2 { :public method "FOO foo" args { append :msg "(1)--[current nextmethod]" next #puts stderr ++++++++++++++++++ append :msg "--(3)--[current class]--[current methodpath]--[current]" #puts stderr ++++++++++++++++++ } } C mixins set M2 # N+4 |:CscFrame @Type(ENSEMBLE) | <-- C.FOO.foo (leaf) # N+2 |:CscFrame @Call(ENSEMBLE) | <-- C.FOO (root) # N+3 |:CscFrame @INACTIVE @Type(ENSEMBLE)| <-- M2.FOO.foo # N+2 |:CscFrame @INACTIVE @Call(ENSEMBLE) | <-- M2.FOO # N+1 |:TclFrame| C setObjectParams [list] ? { #puts stderr "/ / / / / / / / / / / " [C create c1] FOO foo; # N #puts stderr "/ / / / / / / / / / / " c1 eval {set :msg} } "(1)--::c1--FOO foo--foo--(3)--::M2--FOO foo--::c1" C mixins set {} } nx::test case dispo-configure-transparency { Class create C { :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [current] } } ::proc foo {} { error [::nsf::current]-[::nsf::current methodpath]-[::nsf::current method] } # ::nsf::method::alias C FOO ::foo ? {[C create c] FOO} "::c-FOO-FOO" C setObjectParams [list [list FOO:alias,noarg ""]] ? {C create c} "::c-FOO-FOO" C public method "show me" {} { set :msg [::nsf::current]-[::nsf::current methodpath] } C setObjectParams [list -show:alias] ? {[C create c -show me] eval {info exists :msg}} 1 ? {[C create c -show me] eval {set :msg}} "::c-show me" # # ... with mixin indirection # # ... at the calling object level / configure() ... Class create M { :public method configure args { next; } :public method foo args { next; } :public method FOO args { error [::nsf::current]-[::nsf::current methodpath] } } C setObjectParams [list [list FOO:alias,noarg ""]] C mixins add M ? {C create c} "::c-FOO" C mixins set {} # ... at the called object level Object create ::callee { ::nsf::object::property [self] perobjectdispatch true :public object method foo {} { error [::nsf::current]-[::nsf::current methodpath] } } ::nsf::method::alias C FOO ::callee C setObjectParams [list [list FOO:alias,noarg ""]] ? {C create c} "::c" "Defaultmethod of callee is invoked ..." C setObjectParams [list [list FOO:alias "foo"]] ? {C create c} "::callee-FOO foo" "foo leaf method is selected ..." ::callee object mixins add M ? {C create c} "::callee-FOO foo" "With mixin ..." # # ... at the calling object level / ensemble path # # This scenario effectively stacks additional call frames to be # traversed by CallStackMethodPath(). However, these frames precede # the first ensemble frame, that's why they are skipped by # CallStackMethodPath(). M eval { :public method FOO args { puts stderr "!!!!! FOO MIXIN ...." next; } } ? {C create c} "::callee-FOO foo" "With mixin ..." # # ... with filter indirection: tbd # } nx::test case dispo-object-targets { Object create obj ::nsf::object::property obj perobjectdispatch true Class create C Class create T { :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [current] } } # # 1. Behavioural baseline: An alias method binding an object # set methods(z) [::nsf::method::alias T z ::obj] ? {[T new] z} ::obj "Aliased dispatch to defaultmethod" ? {[T new] z uff} "::obj: unable to dispatch method 'uff'" \ "Aliased dispatch to unknown method (default unknown handler)" Class create UnknownHandler { :method unknown {callInfo args} { # # callInfo is a Tcl list. For ensemble dispatches, it contains # the complete message: delegator ; for # ordinary dispatches, the list has a single element: # # methodpath [current methodpath] # puts stderr "CALLINFO='$callInfo' args=$args" switch [llength $callInfo] { 1 { error "UNKNOWNMETHOD-$callInfo" } default { set delegator [lindex $callInfo 0] set unknownMethod [lindex $callInfo end] set path [lrange $callInfo 1 end-1] error "CURRENT-[current]-DELEGATOR-$delegator-UNKNOWNMETHOD-$unknownMethod-PATH-$path" } } } } ::obj object mixins set UnknownHandler ? {[T create t] z uff} "CURRENT-::obj-DELEGATOR-::t-UNKNOWNMETHOD-uff-PATH-z" \ "Aliased dispatch to unknown method (custom unknown handler)" set x [UnknownHandler create handledObj] ::nsf::object::property handledObj perobjectdispatch true set methods(ix) [::nsf::method::alias ::obj ix $x] ? {[T create t] z ix baff} "CURRENT-$x-DELEGATOR-::obj-UNKNOWNMETHOD-baff-PATH-z ix" \ "Aliased dispatch to unknown method (custom unknown handler)" # # 2. Obj targets via alias disposition parameters # # # a) direct dispatch (non-aliased) with fully qualified selector (::*) # ::obj object mixins set {} T setObjectParams x:alias,method=::obj ? {T create t XXX} "::t: unable to dispatch method '::obj'" "FQ dispatch with default unknown handler" ::T mixins set UnknownHandler ? {T create t XXX} "UNKNOWNMETHOD-::obj" "FQ dispatch with custom unknown handler" # # b) calls to the defaultmethod of the aliased object # UnknownHandler method defaultmethod {} { set :defaultmethod 1 } ::obj object mixins set UnknownHandler T setObjectParams [list [list z:alias,noarg ""]] ? {T create t; ::obj eval {info exists :defaultmethod}} 1 \ "Calling defaultmethod via alias+noarg combo with empty default" T setObjectParams [list [list z:alias,noarg "XXX"]] ? {T create t; ::obj eval {info exists :defaultmethod}} 1 \ "Calling defaultmethod via alias+noarg non-empty with \ default combo (default is not passed)" # # b) intermediary object aliases, non-fully qualified selector # T setObjectParams [list [list z:alias,noarg ""]] ? {T create tt} ::tt "sending the msg: tt->z()" # # ISSUE: positional objparam + alias + noarg -> what's the point? # noarg & ?z? are irritating, ?z? should not be printed! # ? {T create t XXX} "invalid argument 'XXX', maybe too many arguments; should be \"::t configure ?/z/?\"" ::obj object mixins set {} T setObjectParams [list z:alias] ? {T create tt YYY} "::obj: unable to dispatch method 'YYY'" "sending the msg: tt->z(::obj)->YYY()" ::obj object mixins set UnknownHandler ? {T create tt YYY} "CURRENT-::obj-DELEGATOR-::tt-UNKNOWNMETHOD-YYY-PATH-z" \ "sending the msg: tt->z(::obj)->YYY()" ::obj object mixins set {} T setObjectParams [list -z:alias] ? {T create tt -z YYY} "::obj: unable to dispatch method 'YYY'" "sending the msg: tt->z(::obj)->YYY()" ::obj object mixins set UnknownHandler ? {T create tt -z YYY} "CURRENT-::obj-DELEGATOR-::tt-UNKNOWNMETHOD-YYY-PATH-z" \ "sending the msg: tt->z(::obj)->YYY()" # # [current methodpath] & empty selector strings: # ::obj object mixins set {} T setObjectParams [list z:alias] ? {T create tt ""} "::obj: unable to dispatch method ''" "sending the msg: tt->z->{}()" ::obj object mixins set UnknownHandler ? {T create tt ""} "CURRENT-::obj-DELEGATOR-::tt-UNKNOWNMETHOD--PATH-z" "sending the msg: tt->z->{}()" T setObjectParams [list -z:alias] ? {T create tt -z ""} "CURRENT-::obj-DELEGATOR-::tt-UNKNOWNMETHOD--PATH-z" "sending the msg: tt->z()" # # Dispatch with a method handle # ::T mixins set {} ? [list [T create t] $methods(z) XXX] \ "CURRENT-::obj-DELEGATOR-::t-UNKNOWNMETHOD-XXX-PATH-::nsf::classes::T::z" T setObjectParams x:alias,method=$methods(z) ? {T create t XXX} "CURRENT-::obj-DELEGATOR-::t-UNKNOWNMETHOD-XXX-PATH-::nsf::classes::T::z" \ "Non-object FQ selector with default unknown handler" ::T mixins set UnknownHandler ? {T create t XXX} "CURRENT-::obj-DELEGATOR-::t-UNKNOWNMETHOD-XXX-PATH-::nsf::classes::T::z" \ "Non-object FQ selector with custom unknown handler" # # A Tcl proc is allowed?! # proc ::baz {x} { set :baz $x } T setObjectParams x:alias,method=::baz ? {[T create t XXX] eval {info exists :baz}} 1 ? {[T create t XXX] eval {set :baz}} XXX # # TBD: nested objects # # # TBD: object-system methods # } # # check xotcl with residual args # nx::test case xotcl-residualargs { package prefer latest puts stderr "XOTcl loaded: [package req XOTcl 2.0]" ? {::xotcl::Class create XD -set x 1} "::XD" #? {c1 eval {info exists :args}} 0 ? {XD __object_configureparameter} "-instfilter:filterreg,alias,0..n -superclass:alias,0..n -instmixin:mixinreg,alias,0..n {-__default_metaclass ::xotcl::Class} {-__default_superclass ::xotcl::Object} -mixin:mixinreg,alias,0..n -filter:filterreg,alias,0..n -class:class,alias args:alias,method=residualargs,args" # # test passing arguments to init # ::XD instproc init args { set :args $args } ::XD create x1 1 2 3 -set x 1 ? {x1 exists x} 1 ? {x1 exists args} 1 ? {x1 set args} {1 2 3} } nx::test configure -count 1000 nx::test case xotcl-residualargs2 { ::xotcl::Class create XC -parameter {a b c} ::XC instproc init args {set :x $args; incr :y} ? {XC create xc1 -a 1} ::xc1 ? {XC create xc2 x y -a 1} ::xc2 ::nx::Class create C { :property a :property b :property c :method init args {set :x $args; incr :y} } ? {C create c1 -a 1} ::c1 ? {xc2 eval {info exists :a}} 1 ? {xc2 eval {set :x}} {x y} ? {xc2 eval {set :y}} 1 ? {c1 eval {info exists :a}} 1 ? {c1 eval {set :y}} 1 } nx::test case xotcl-residualargs-upleveling { # # Test callstack resolution for upvar/uplevel in # parameter-dispatched methods under residualargs() ... # package prefer latest package req XOTcl 2.0 xotcl::Class C -proc onTheFly {name args} { ? [list set _ [info exists ix]] 0 ? [list set _ [info exists Y]] 0 set c [[self] $name {*}$args] ? [list set _ [info exists ix]] 1 ? [list set _ [set ix]] Y ? [list set _ [info exists Y]] 1 ? [list set _ [set Y]] 1 return $c } -instproc call {x} { # ::nsf::__db_show_stack my uplevel [list set ix $x] my upvar $x _ incr _ } -instproc call2 {x} { # ::nsf::__db_show_stack uplevel [self callinglevel] [list set ix $x] upvar [self callinglevel] $x _ incr _ } C onTheFly c1 -call Y C onTheFly c1 -call2 Y } # TODO: what todo with object parameter inspection for names with # alias, forward... "names" do not always correspond with vars set. nx::test case class-configure-default { # Background: when class is created, it is created with a "default" # superclass of "::nx::Object". This is defined in the slot for # superclass in nx.tcl nx::Class create P ? {P info superclasses} ::nx::Object # # When we pass the superclass a different value, this is certainly used. # nx::Class create Q -superclass P ? {Q info superclasses} ::P # # When we call configure on the superclass, we do not want the # default to be used to reset it to ::nx::Object. Therefore the # configure uses the default for parameters with METHOD_INVOCATION # only, when the object is not yet initialized. # Q configure ? {Q info superclasses} ::P } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/doc.test000066400000000000000000001040151242365656200137020ustar00rootroot00000000000000# -*- Tcl -*- package require nx package require nx::test package require nx::doc namespace import -force ::nx::* namespace import -force ::nx::doc::* nx::test configure -count 1 # # some helper # proc lcompare {a b} { foreach x $a y $b { if {$a ne $b} {return -1} } return 1 } # -- # # Source the "Document Comment" backend # package require nx::doc::dc Test case scanning { set lines { "# @package o" 1 "#@package o" 1 "bla" 0 "# @object o" 1 "# 1 2 3" 1 "#" 1 "# " 1 " # " 1 "\t#\t \t" 1 "# 345" 1 "# @tag1 part1" 1 "bla; # no comment" 0 "" 0 "\t\t" 0 "### # # # # @object o # ####" 1 "# # # # # 345" 1 "# # # @tag1 part1" 1 "bla; # # # # # no comment" 0 " " 0 } set ::prj [@project new -name _PROJECT_] foreach {::line ::result} $lines { ? {lassign [$::prj analyze_line $::line] is_comment text; set is_comment} $::result "processor analyze_line '$::line'" } set script { # @package o # 1 2 3 bla bla # @object o # 1 2 3 # # 345 # @tag1 part1 # @tag2 part2 bla; # no comment bla bla bla ### # # # # @object o # #### # 1 2 3 # # # # # # 345 # # # @tag1 part1 # @tag2 part2 bla; # # # # # no comment } set blocks {1 {{ @package o} { 1 2 3}} 5 {{ @object o} { 1 2 3} {} { 345} { @tag1 part1} { @tag2 part2}} 17 {{ @object o # ####} { 1 2 3} {} { 345} { @tag1 part1} { @tag2 part2}}} ? [list ::lcompare [$::prj comment_blocks $script] $blocks] 1 } Test case parsing { set ::prj [@project new -name _PROJECT_] namespace import -force ::nx::doc::CommentBlockParser # # TODO: Add tests for doc-parsing state machine. # set block { {@command ::cc} } set ::cbp [CommentBlockParser process $block] ? [list $::cbp status ? COMPLETED] 1 set block { {} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? COMPLETED] 0 ? [list $cbp status ? STYLEVIOLATION] 1 # # For now, a valid comment block must start with a non-space line # (i.e., a tag or text line, depending on the section: context # vs. description) # set block { {} {@command ::cc} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 1 set block { {command ::cc} {} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 1 set block { {@command ::cc} {some description} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 1 set block { {@command ::cc} {} {} {} {@see ::o} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 0 ? [list $cbp status ? COMPLETED] 1 set block { {@command ::cc} {} {some description} {some description2} {} {} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 0 # Note: We do allow description blocks with intermediate space # lines, for now. set block { {@command ::cc} {} {some description} {some description2} {} {an erroreneous description line, for now} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 0 # # TODO: Do not enforce space line between the context and immediate # part block (when description is skipped)? # # OR: For absolutely object::qualifying parts (e.g., outside of an initblock), # do we need sequences of _two_ (or more) tag lines, e.g. # # -- # @object Foo # @param attr1 # -- # # THEN, we can only discriminate between the context and an # immediate part section by requiring a space line! # # Alternatively, we can use the @see like syntax for object::qualifying: # @param ::Foo#attr1 (I have a preference for this option). set block { {@command ::cc} {@see someOtherEntity} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 1 # # TODO: Disallow space lines between parts? Check back with Javadoc spec. # set block { {@command ::cc} {} {@see SomeOtherEntity} {add a line of description} {} {} {@see SomeOtherEntity2} {} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 1 # # TODO: Should we enforce a mandatory space line between description and part block? # set block { {@command ::cc} {} {add a line of description} {a second line of description} {a third line of description} {@see entity3} {@see SomeOtherEntity2} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 1 set block { {@command ::cc} {} {add a line of description} {a second line of description} {a third line of description} {} {@see SomeOtherEntity2} {} {} {an erroreneous description line, for now} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 1 set block { {@command ::cc} {} {add a line of description} {a second line of description} {} {a third line of description} {} {@see SomeOtherEntity2} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 0 set block { {@object ::cc} {} {add a line of description} {a second line of description} {} {@see SomeOtherEntity2} {@xyz SomeOtherEntity2} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? INVALIDTAG] 1 set block { {@class ::cc} {} {add a line of description} {a second line of description} {} {@see SomeOtherEntity2} {@xyz SomeOtherEntity2} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? INVALIDTAG] 1 # # TODO: Where shall we allow the @author tag?! Re-activate # later, if necessary ... # if {0} { # # testing the doc object construction # set block { {@object ::o} {} {some more text} {and another line for the description} {} {@author stefan.sobernig@wu.ac.at} {@author gustaf.neumann@wu-wien.ac.at} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? COMPLETED] 1 set entity [$cbp current_entity] ? [list ::nsf::is object $entity] 1 ? [list $entity info has type ::nx::doc::@object] 1 ? [list $entity @author] "stefan.sobernig@wu.ac.at gustaf.neumann@wu-wien.ac.at"; ? [list $entity as_text] "some more text and another line for the description"; } set block { {@command ::c} {} {some text on the command} {} {@see ::o} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? COMPLETED] 1 set entity [$cbp current_entity] ? [list ::nsf::is object $entity] 1 ? [list $entity info has type ::nx::doc::@command] 1 ? [list $entity as_text] "some text on the command"; ? [list $entity @see] "::o"; set block { {@class ::C} {} {some text on the class entity} {} {@class-property attr1 Here! we check whether we can get a valid description block} {for text spanning multiple lines} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? COMPLETED] 1 set entity [$cbp current_entity] ? [list ::nsf::is object $entity] 1 ? [list $entity info has type ::nx::doc::@class] 1 ? [list $entity as_text] "some text on the class entity"; ? [list llength [$entity @property]] 1 ? [list [$entity @property] info has type ::nx::doc::@param] 1 ? [list [$entity @property] as_text] "Here! we check whether we can get a valid description block for text spanning multiple lines" } if {0} { Test case in-situ-basics { # # basic test for in-situ documentation (initblock) # # set script { package req nx namespace import -force ::nx::Class Class create ::Foo { # The class Foo defines the behaviour for all Foo objects # # @author gustaf.neumann@wu-wien.ac.at # @author ssoberni@wu.ac.at # @.property attr1 # # This property 1 is wonderful # # @see ::nx::VariableSlot # @see ::nx::MetaSlot :property attr1 :property attr2 :property attr3 # @.method foo # # This describes the foo method # # @parameter a Provides a first value # @parameter b Provides a second value :method foo {a b} {;} } } # set prj [processor process -sandboxed -type eval $script] set prj [@project new -name _PROJECT_] set entity [@class id ::Foo] ? [list ::nsf::is object $entity] 1 ? [list $entity info has type ::nx::doc::@class] 1 ? [list $entity as_text] "The class Foo defines the behaviour for all Foo objects"; ? [list $entity @author] "gustaf.neumann@wu-wien.ac.at ssoberni@wu.ac.at" # TODO: Fix the [@param id] programming scheme to allow (a) for # entities to be passed and the (b) documented structures set entity [@property id [@class id ::Foo] class attr1] ? [list ::nsf::is object $entity] 1 ? [list $entity info has type ::nx::doc::@property] 1 ? [list $entity @see] "::nx::VariableSlot ::nx::MetaSlot"; set entity [@method id ::Foo class foo] ? [list [@class id ::Foo] @method] $entity ? [list ::nsf::is object $entity] 1 ? [list $entity info has type ::nx::doc::@method] 1 ? [list $entity as_text] "This describes the foo method"; foreach p [$entity @parameter] expected { "Provides a first value" "Provides a second value" } { ? [list expr [list [$p as_text] eq $expected]] 1; } $prj destroy } # TODO: how to realise scanning and parsing for mixed ex- and # in-situ documentation? That is, how to differentiate between # absolutely and relatively qualified comment blocks in line-based # scanning phase (or later)? Test case mixed-mode-parsing { set script { package req nx namespace import -force ::nx::* # @class ::Bar # # The class Bar defines the behaviour for all Bar objects # # @author gustaf.neumann@wu-wien.ac.at # @author ssoberni@wu.ac.at # @class.property {::Bar attr1} # # This property 1 is wonderful # # @see ::nx::VariableSlot # @see ::nx::MetaSlot # @class.class-method {::Bar foo} # # # This describes the foo method # # @parameter a Provides a first value # @parameter b Provides a second value # @class.object-method {::Bar foo} # # This describes the per-object foo method # # @parameter a Provides a first value # @parameter b Provides a second value namespace eval ::ns1 { ::nx::Object create ooo } Class create Bar { :property attr1 :property attr2 :property attr3 # @.method foo # # This describes the foo method in the initblock # # @parameter a Provides a first value # @parameter b Provides a second value :public method foo {a b} { # This describes the foo method in the method body # # @parameter a Provides a first value (refined) } :public class method foo {a b c} { # This describes the per-object foo method in the method body # # @parameter b Provides a second value (refined) # @parameter c Provides a third value (first time) } } } set prj [processor process -sandboxed -type eval $script] set entity [@class id ::Bar] ? [list ::nsf::is object $entity] 1 ? [list $entity info has type ::nx::doc::@class] 1 ? [list $entity as_text] "The class Bar defines the behaviour for all Bar objects"; ? [list $entity @author] "gustaf.neumann@wu-wien.ac.at ssoberni@wu.ac.at" # TODO: Fix the [@param id] programming scheme to allow (a) for # entities to be passed and the (b) documented structures set entity [@property id [@class id ::Bar] class attr1] ? [list ::nsf::is object $entity] 1 ? [list $entity info has type ::nx::doc::@property] 1 ? [list $entity @see] "::nx::VariableSlot ::nx::MetaSlot"; set entity [@method id ::Bar class foo] ? [list [@class id ::Bar] @method] $entity ? [list ::nsf::is object $entity] 1 ? [list $entity info has type ::nx::doc::@method] 1 ? [list $entity as_text] "This describes the foo method in the method body"; foreach p [$entity @parameter] expected { "Provides a first value (refined)" "Provides a second value" } { ? [list expr [list [$p as_text] eq $expected]] 1; } set entity [@method id ::Bar object foo] ? [list [@class id ::Bar] @class-object-method] $entity ? [list ::nsf::is object $entity] 1 ? [list $entity info has type ::nx::doc::@method] 1 ? [list $entity as_text] "This describes the per-object foo method in the method body"; foreach p [$entity @parameter] expected { "Provides a first value" "Provides a second value (refined)" "Provides a third value (first time)" } { ? [list expr [list [$p as_text] eq $expected]] 1; } $prj destroy } Test case tag-notations-basics { # # Some tests on structured/navigatable tag notations # # adding support for parsing levels # -- @class.object.object {::D o1 o2} set block { {@..object o2 We have a tag notation sensitive to the parsing level} } set entity [[@ @class ::D] @object o1] set cbp [CommentBlockParser process -parsing_level 1 -partof_entity $entity $block] ? [list $cbp status ? LEVELMISMATCH] 1 set cbp [CommentBlockParser process -parsing_level 2 -partof_entity $entity $block] ? [list $cbp status ? COMPLETED] 1 set entity [$cbp current_entity] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@object] 1 ? [list $entity as_text] "We have a tag notation sensitive to the parsing level" set block { {@..object {o2 o3} We still look for balanced specs} } set entity [[@ @class ::D] @object o1] set cbp [CommentBlockParser process -parsing_level 2 -partof_entity $entity $block] ? [list $cbp status ? STYLEVIOLATION] 1 # This fails because we do not allow uninitialised/non-existing # entity objects (@object o) along the resolution path ... set block { {@class.object.property {::C o attr1} We have an invalid specification} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? INVALIDTAG] 1 # ? [list $cbp message] "The tag 'object' is not supported for the entity type '@class'" set block { {@class.method.property attr1 We have an imbalanced specification (the names are underspecified!)} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 1 ? [list $cbp message] "Imbalanced tag line spec: 'class method property' vs. 'attr1'" # For now, we do not verify and use a fixed scope of permissive tag # names. So, punctuation errors or typos are most probably reported # as imbalanced specs. In the mid-term run, this should rather # become an INVALIDTAG condition. set block { {@cla.ss.method.parameter {::C foo p1} We mistyped a tag fragment} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? STYLEVIOLATION] 1 ? [list $cbp message] "Imbalanced tag line spec: 'cla ss method parameter' vs. '::C foo p1'" set block { {@cla,ss.method.parameter {::C foo p1} We mistyped a tag fragment} } set cbp [CommentBlockParser process $block] ? [list $cbp status ? INVALIDTAG] 1 ? [list $cbp message] "The entity type '@cla,ss' is not available." } Test case tag-notations-extended { set script { # @class ::C # # The global description of ::C # # @property attr1 Here we can only provide a description block for object parameters # @class.object-method {::C sub} # # For now, we have to declare a family of sub methods explicitly # (allows for providing some overview, shared description) # @class.property {::C attr1} Here, we could also write '@class.class-property \{::C attr1\}', @property is a mere forwarder! In the context section, only one-liners are allowed! # @class.object.property {::C foo p1} A short description is ... # # .. is overruled by a long one ... # If addressing to a nested object, one strategy would be to use # @object and provide the object identifier (which reflects the # nesting, e.g. ::C::foo). However, we cannot distinguish between # namespace qualifiers denoting an object, class or owning # namespace! # # ISSUE: If specifying an axis ".object", we would have to define # a part property @object on @class and @object. However, @object # would be ambiguous now: It could be called in a freestanding # (absolute) manner AND in a contextualised manner (in an init # block). In the latter case, it would fail because we would have # to provide a FQ'ed name (which defeats the purpose of a nested = # contextualised notation). # # SO: for now, we introduce a part property child-object (and # child-class?) to discrimate between the two situations ... # # TODO: How to register this so created @object entity as nested # object with the doc entity represented the parent object? Class create C { # This is the initblock description of ::C which overwrites the # global description (see above) # @.property attr1 # # This is equivalent to writing "@class-property attr1" :property attr1 { # This description does not apply to the object parameter # "attr1" owned by the ::C class, rather it is a description # of the property slot object! How should we deal with this # situation? Should this level overwrite the top-level and # initblock descriptions? } # @.class-object-property attr2 Carries a short desc only :class property attr2 # @.method foo # # @parameter p1 set fooHandle [:public method foo {p1} { # Here goes some method-body-level description # # @parameter p1 The most specific level! return [current method]-$p1-[current] }] # @.object-method bar # # Before referring to its parts, an entity must exist; so # declare eagerly ... # @.class-object-method.parameter {bar p1} # # This extended form allows to describe a method parameter with all # its structural features! set barHandle [:public class method bar {p1} { return [current method]-$p1-[current] }] # @.object foo 'foo' needs to be defined before referencing any of its parts! # @.object.property {foo p1} # # The first element in the name list is resolved into a fully # qualified (absolute) entity, based on the object owning the # initblock! Object create [current]::foo { # Adding a line for the first time (not processed in the initblock phase!) # @..property p1 # # This is equivalent to stating "@class-object-property p1" :property p1 } # @.class Foo X # # By providing a fully-qualified identifier ("::Foo") you leave the # context of the initblock-owning object, i.e. you would NOT refer to # a nested class object named "Foo" anymore! # @.class.property {Foo p1} # # This is equivalent to stating "@child-class.class-property {Foo p1}" # @.class.class-object-property {Foo p2} Y Class create [current]::Foo { # @..property p1 # # # This is equivalent to stating "@class-property p1"; or # '@class.object.property {::C Foo p1}' from the top-level. :property p1 # @..class-object-property p2 :class property p2 } # @.class-object-method.sub-method {sub foo} # # ISSUE: Should submethods be navigatable through "method" (i.e., # "@method.method.method ...") or "submethod" (i.e., # "@method.submethod.submethod ...")? ISSUE: Should it be sub* with # "-" (to correspond to "@class-object-method", "@class-method")? Also, we # could allow both (@sub-method is the property name, @method is a # forwarder in the context of an owning @method object!) # # @parameter p1 Some words on p1 :class alias "sub foo" $fooHandle # @.method sub # # The desc of the ensemble object 'sub' # # @sub-method bar Only description available here ... # ISSUE: Should the helper object "sub" be documentable in its own # right? This would be feasible with the dotted notation from # within and outside the init block, e.g. "@object sub" or # "@class.object {::C sub}" # # ISSUE: Is it correct to say the sub appears as per-object method # and so do its submethods? Or is it misleading to document it that # way? Having an "@class-object-submethod" would not make much sense to # me?! :alias "sub bar" $barHandle # @.class-object-method sub A brief desc # @.class-object-method {"sub foo2"} # # could allow both (@sub-method is the property name, @method is a # forwarder in the context of an owning @method object!) # # @parameter p1 Some words on p1 # @see anotherentity # @author ss@thinkersfoot.net :class alias "sub foo2" $fooHandle } } # # 1) process the top-level comments (PARSING LEVEL 0) # processor readin $script # --testing-- "@class ::C" set entity [@class id ::C] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@class] 1 ? [list $entity as_text] "The global description of ::C"; # --testing-- "@class.property {::C attr1}" set entity [@property id $entity class attr1] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@property] 1 ? [list $entity as_text] "Here, we could also write '@class.class-property {::C attr1}', @property is a mere forwarder! In the context section, only one-liners are allowed!" # --testing-- "@class.object.property {::C foo p1} A short description is ..." # set entity [@property id $entity class attr1] # set entity [@object id -partof_name ::C -scope child foo] # ? [list ::nsf::object::exists $entity] 1 # ? [list $entity info has type ::nx::doc::@object] 1 # ? [list $entity as_text] "" # set entity [@property id $entity object p1] # ? [list ::nsf::object::exists $entity] 1 # ? [list $entity info has type ::nx::doc::@property] 1 # ? [list $entity as_text] ".. is overruled by a long one ..." set entity [@object id ::C::foo] ? [list ::nsf::object::exists $entity] 0 set entity [@property id $entity class p1] ? [list ::nsf::object::exists $entity] 0 # ? [list $entity info has type ::nx::doc::@property] 1 # ? [list $entity as_text] ".. is overruled by a long one ..." # --testing-- @class-object-property attr2 (its non-existance) set entity [@property id [@class id ::C] object attr2] ? [list ::nsf::object::exists $entity] 0 # --testing-- @child-class Foo (its non-existance) set entity [@class id ::C::Foo] ? [list ::nsf::object::exists $entity] 0 # --testing -- @method foo (its non-existance) set entity [@method id ::C class foo] ? [list ::nsf::object::exists $entity] 0 # --testing-- @class-object-method.parameter {bar p1} (its non-existance) set entity [@parameter id [@method id ::C class bar] "" p1] ? [list ::nsf::object::exists $entity] 0 # --testing-- @child-object.property {foo p1} (its non-existance) set cl [@class id ::C::Foo] ? [list ::nsf::object::exists $entity] 0 set entity [@property id $cl class p1] ? [list ::nsf::object::exists $entity] 0 set entity [@property id $cl object p2] ? [list ::nsf::object::exists $entity] 0 # # 2) process the initblock comments (PARSING LEVEL 1) # puts stderr -----CMD------ ::nsf::configure keepcmds true eval $script ::nsf::configure keepcmds false lassign [processor readin \ -parsing_level 1 \ -docstring \ -tag @class \ -name ::C \ [::C eval {set :__cmd(__initblock)}]] _ processed_entities # a) existing, but modified ... set entity [@class id ::C] ? $_ $entity ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@class] 1 ? [list $entity as_text] "This is the initblock description of ::C which overwrites the global description (see above)" set entity [@property id $entity class attr1] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@property] 1 ? [list $entity as_text] {This is equivalent to writing "@class-property attr1"} set entity [@object id ::C::foo] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@object] 1 ? [list $entity as_text] "'foo' needs to be defined before referencing any of its parts!"; # still empty! set entity [@property id $entity object p1] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@property] 1 ? [list $entity as_text] "The first element in the name list is resolved into a fully qualified (absolute) entity, based on the object owning the initblock!" # b) newly added ... # --testing-- @class-object-property attr2 set entity [@property id [@class id ::C] object attr2] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@property] 1 ? [list $entity as_text] "Carries a short desc only"; # --testing-- @child-class Foo # TODO: provide a check against fully-qualified names in part specifications set entity [@class id ::C::Foo] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@class] 1 ? [list $entity as_text] {By providing a fully-qualified identifier ("::Foo") you leave the context of the initblock owning object, i.e. you would NOT refer to a nested class object named "Foo" anymore!} set entity [@property id [@class id ::C] class p1] ? [list ::nsf::object::exists $entity] 0; # should be 0 at this stage! # --testing -- @method foo set entity [@method id ::C class foo] ? [list ::nsf::object::exists $entity] 1 ? [list $entity as_text] "" # --testing-- @class-object-method.parameter {bar p1} (its non-existance) It # still cannot exist as a documented entity, as the class method # has not been initialised before! set entity [@parameter id [@method id ::C class bar] "" p1] ? [list ::nsf::object::exists $entity] 0 # --testing-- @child-class.property {foo p1} (its non-existance) # --testing-- @child-class.object-property {foo p2} (its non-existance) set cl [@class id ::C::Foo] ? [list ::nsf::object::exists $cl] 1 set entity [@property id $cl class p1] ? [list ::nsf::object::exists $entity] 1 ? [list $entity as_text] {This is equivalent to stating "@child-class.class-property {Foo p1}"} set entity [@property id $cl object p2] ? [list ::nsf::object::exists $entity] 1 ? [list $entity as_text] "Y" set entity [@method id ::C class sub] ? [list ::nsf::object::exists $entity] 1 ? [list $entity as_text] "The desc of the ensemble object 'sub'" set entity [@method id ::C class sub::bar] ? [list ::nsf::object::exists $entity] 1 ? [list $entity as_text] "Only description available here ..." set entity [@method id ::C object sub] ? [list ::nsf::object::exists $entity] 1 ? [list $entity as_text] "A brief desc" set entity [@method id ::C object sub::foo2] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@method] 1 ? [list $entity as_text] "could allow both (@sub-method is the property name, @method is a forwarder in the context of an owning @method object!)" ? [list $entity @see] "anotherentity" # TODO: @author not supported for @method (fine so?) # ? [list $entity @author] "ss@thinkersfoot" set entity [@parameter id $entity "" p1] ? [list ::nsf::object::exists $entity] 1 ? [list $entity as_text] "Some words on p1" # # 3a) process the property initblocks and method bodies (PARSING LEVEL 2)! # set project [@project new -name "_%@"] $project sandbox [Sandbox new] processor process=@class $project [@class id ::C] # methods ... set entity [@method id ::C class foo] ? [list ::nsf::object::exists $entity] 1 ? [list $entity as_text] "Here goes some method-body-level description" set entity [@parameter id [@method id ::C class foo] "" p1] ? [list ::nsf::object::exists $entity] 1 ? [list $entity as_text] "The most specific level!" # attributes ... # attr1 set entity [@property id [@class id ::C] class attr1] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@property] 1 ? [list $entity as_text] {This description does not apply to the object parameter "attr1" owned by the ::C class, rather it is a description of the property slot object! How should we deal with this situation? Should this level overwrite the top-level and initblock descriptions?} # # 3b) nested objects/ classes (PARSING LEVEL 2)! # processor readin \ -docstring \ -parsing_level 2 \ -tag @object \ -name ::C::foo \ [::C::foo eval {set :__cmd(__initblock)}] processor process=@object $project [@object id ::C::foo] set entity [@object id ::C::foo] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@object] 1 ? [list $entity as_text] "Adding a line for the first time (not processed in the initblock phase!)"; # still empty! set entity [@property id $entity object p1] ? [list ::nsf::object::exists $entity] 1 ? [list $entity info has type ::nx::doc::@property] 1 ? [list $entity as_text] {This is equivalent to stating "@class-object-property p1"} processor readin \ -docstring \ -parsing_level 2 \ -tag @class \ -name ::C::Foo \ [::C::Foo eval {set :__cmd(__initblock)}] processor process=@class $project [@class id ::C::Foo] set cl [@class id ::C::Foo] ? [list ::nsf::object::exists $cl] 1 set entity [@property id $cl class p1] ? [list ::nsf::object::exists $entity] 1 ? [list $entity as_text] {This is equivalent to stating "@class-property p1"; or '@class.object.property {::C Foo p1}' from the top-level.} set entity [@property id $cl object p2] ? [list ::nsf::object::exists $entity] 1 ? [list $entity as_text] "" # # basic testing of "properties" (switch attributes) # ? [list $cl eval {set :@deprecated}] 0 ? [list $cl eval {set :@stashed}] 0 ? [list $cl eval {set :@c-implemented}] 0 ? [list $cl @deprecated] 1 ? [list $cl @stashed] 1 ? [list $cl @c-implemented] 1 ? [list $cl eval {set :@deprecated}] 1 ? [list $cl eval {set :@stashed}] 1 ? [list $cl eval {set :@c-implemented}] 1 set entity [@method id ::C class foo] ? [list $entity eval {set :@syshook}] 0 ? [list $entity @syshook] 1 ? [list $entity eval {set :@syshook}] 1 ? [list $entity @syshook 0] {wrong # args: should be "get obj prop"} ? [list $entity eval {set :@syshook 0}] 0 ? [list $entity @syshook] 1 } Test case switch-parts { set script { package req nx namespace import ::nx::* Class create Enil { # The class Enil defines the behaviour for all Enil objects, # however, it is deprecated and will be removed from the # provided doc entities in the next iteration ... # # @author ssoberni@wu.ac.at # @deprecated # @.property attr1 # # This property 1 will be invisibile in the generated doc # # @stashed :property attr1 # @.method foo # # This describes the foo method which is called from within the # nx-enabled Tcl engine # # @syshook :public method foo {a b} {;} :public method baz {} { # This method entity sets a couple of properties in series ... # # @property c-implemented syshook } } } set prj [processor process -sandboxed -type eval $script] set cl [@class id ::Enil] ? [list $cl eval {set :@deprecated}] 1 ? [list $cl @deprecated] 1 ? [list $cl eval {set :@c-implemented}] 0 ? [list $cl eval {set :@stashed}] 0 ? [list $cl @author] ssoberni@wu.ac.at set entity [@property id $cl class attr1] ? [list $entity eval {set :@deprecated}] 0 ? [list $entity eval {set :@stashed}] 1 ? [list $entity @stashed] 1 ? [list $entity eval {set :@c-implemented}] 0 set entity [@method id ::Enil class foo] ? [list $entity eval {set :@deprecated}] 0 ? [list $entity eval {set :@stashed}] 0 ? [list $entity eval {set :@c-implemented}] 0 ? [list $entity eval {set :@syshook}] 1 ? [list $entity @syshook] 1 set entity [@method id ::Enil class baz] ? [list $entity eval {set :@deprecated}] 0 ? [list $entity eval {set :@stashed}] 0 ? [list $entity eval {set :@c-implemented}] 1 ? [list $entity @c-implemented] 1 ? [list $entity eval {set :@syshook}] 1 ? [list $entity @syshook] 1 } } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/forward.test000066400000000000000000000404641242365656200146100ustar00rootroot00000000000000# -*- Tcl -*- package require nx package require nx::test ########################################### # trivial object delegation ########################################### nx::test case delegation { nx::Object create dog nx::Object create tail { :public object method wag args { return $args } :public object method nxwag args { return $args } } dog public object forward wag tail %proc dog public object forward nxwag tail %method ? {dog wag 100} 100 ? {dog nxwag 100} 100 } ########################################### # evaluating in scope ########################################### nx::test case inscope { nx::Class create X { :property {x 1} :public forward Incr -frame object incr } X create x1 -x 100 x1 Incr x x1 Incr x x1 Incr x ? {x1 cget -x} 103 } ########################################### # adding ########################################### nx::test case adding { nx::Object create obj { :public object forward addOne expr 1 + } ? {obj addOne 5} 6 } ########################################### # more arguments ########################################### nx::test case multiple-args { nx::Object create target { :public object method foo args {return $args} } nx::Object create obj { :public object forward foo target %proc %self a1 a2 } ? {obj foo x1 x2} [list ::obj a1 a2 x1 x2] obj public object forward foo target %proc %self %%self %%p ? {obj foo x1 x2} [list ::obj %self %p x1 x2] } ########################################### # mixin example ########################################### nx::test case mixin-via-forward { nx::Object create mixins { :object method unknown {m args} {return [concat [current] $m $args]} } nx::Object create obj { :public object forward Mixin mixins %1 %self } ? {obj Mixin add M1} [list ::mixins add ::obj M1] ? {catch {obj Mixin}} 1 obj public object forward Mixin mixins "%1 {Getter Setter}" %self ? {obj Mixin add M1} [list ::mixins add ::obj M1] ? {obj Mixin M1} [list ::mixins Setter ::obj M1] ? {obj Mixin} [list ::mixins Getter ::obj] } ########################################### # sketching extensibe info ########################################### nx::test case info-via-forward { nx::Object create Info { :public object method @mixin {o} { $o info mixin } :public object method @class {o} { ;# without prefix, doing here a [Info class] wod be wrong $o info class } :public object method @help {o} { ;# define a new subcommand for info foreach c [:info object methods] {lappend result [string range $c 1 end]} return $result } } nx::Object public forward Info -prefix @ Info %1 %self nx::Class create X { :create x1 } ? {x1 Info class} ::X ? {x1 Info help} [list help mixin class] } ########################################### # variations of placement of options ########################################### nx::test case incr { nx::Object create obj { set :x 1 :public object forward i1 -frame object incr x } ? {obj i1} 2 } ########################################### # introspeciton options ########################################### nx::test case introspection { nx::Class create C { :public forward Info -prefix @ Info %1 %self } ? {C info methods -type forwarder} Info C public forward XXXo x ? {lsort [C info methods -type forwarder]} [list Info XXXo] ? {C info methods -type forwarder X*} [list XXXo] ? {lsort [C info methods -type forwarder *o]} [list Info XXXo] # delete the forwarder C method XXXo {} {} ? {C info methods -type forwarder} [list Info] # get the definition of a instforwarder ? {C info method definition Info} [list ::C public forward Info -prefix @ Info %1 %self] # check introspection for objects nx::Object create obj { :public object forward i1 -frame object incr x :public object forward Mixin mixin %1 %self :public object forward foo target %proc %self %%self %%p :public object forward addOne expr 1 + } ? {lsort [obj info object methods -type forwarder]} "Mixin addOne foo i1" ? {obj info object method definition Mixin} "::obj public object forward Mixin mixin %1 %self" ? {obj info object method definition addOne} "::obj public object forward addOne expr 1 +" ? {obj info object method definition foo} "::obj public object forward foo target %proc %self %%self %%p" ? {obj info object method definition i1} "::obj public object forward i1 -frame object ::incr x" } ########################################### # test serializer ########################################### package require nx::serializer nx::test case serializer { nx::Object create obj { :object method test {} {puts "i am [current method]"} } set ::a [Serializer deepSerialize obj] #puts <<$::a>> eval $::a ? {set ::a} [Serializer deepSerialize obj] } ########################################### # test optional target cmd ########################################### nx::test case optional-target { nx::Object create obj { set :x 2 :public object forward append -frame object } ? {obj append x y z} 2yz nx::Object create n; nx::Object create n::x {:public object method current {} {current}} nx::Object create o o public object forward ::n::x ? {o x current} ::n::x } ########################################### # arg including instvar ########################################### nx::test case percent-cmd { nx::Object create obj { set :x 10 :public object forward x* expr {%:eval {set :x}} * } ? {obj x* 10} "100" } ########################################### # positional arguments ########################################### nx::test case positioning-args { nx::Object create obj obj public object forward @end-13 list {%@end 13} ? {obj @end-13 1 2 3 } [list 1 2 3 13] obj public object forward @-1-13 list {%@-1 13} ? {obj @-1-13 1 2 3 } [list 1 2 13 3] obj public object forward @1-13 list {%@1 13} ? {obj @1-13 1 2 3 } [list 13 1 2 3] ? {obj @1-13} [list 13] obj public object forward @2-13 list {%@2 13} ? {obj @2-13 1 2 3 } [list 1 13 2 3] obj public object forward @list 10 {%@0 list} {%@end 99} ? {obj @list} [list 10 99] ? {obj @list a b c} [list 10 a b c 99] obj public object forward @list {%@end 99} {%@0 list} 10 ? {obj @list} [list 10 99] ? {obj @list a b c} [list 10 a b c 99] obj public object forward @list {%@2 2} {%@1 1} {%@0 list} ? {obj @list} [list 1 2] ? {obj @list a b c} [list 1 2 a b c] obj public object forward @list x y z {%@0 list} {%@1 1} {%@2 2} ? {obj @list} [list 1 2 x y z] ? {obj @list a b c} [list 1 2 x y z a b c] obj public object forward @list x y z {%@2 2} {%@1 1} {%@0 list} ? {obj @list} [list x 1 y 2 z] ? {obj @list a b c} [list x 1 y 2 z a b c] # adding some test cases which cover the interactions # between %@POS and %1 substitutions # obj public object forward @end-13 list {%@end 13} %1 %self ? {obj @end-13 1 2 3 } [list 1 ::obj 2 3 13] obj public object forward @end-13 list %1 {%@end 13} %self ? {obj @end-13 1 2 3 } [list 1 ::obj 2 3 13] obj public object forward @end-13 list {%@end 13} %1 %1 %1 %self ? {obj @end-13 1 2 3 } [list 1 1 1 ::obj 2 3 13] obj public object forward @end-13 list {%@-1 13} %1 %self ? {obj @end-13 1 2 3 } [list 1 ::obj 2 13 3] obj public object forward @end-13 list {%@1 13} %1 %self ? {obj @end-13 1 2 3 } [list 13 1 ::obj 2 3] } nx::test case forwarder-basics { nx::Object create obj ## ## Particular role of first forwarder arg: (fully-qualified) target ## & methodName in one (provides shortcut notation) ## ? {obj info object methods foo} "" obj public object forward ::ns1::foo ? {obj info object methods foo} "foo" ? {obj foo X} {invalid command name "::ns1::foo"} namespace eval ::ns1 {proc foo {p} {return $p}} ? {obj foo X} "X" obj public object forward ::ns1::foo %method %method ? {namespace eval ::ns1 { ::obj foo }} "foo" # make sure, old-style arguments don't get moved into argument # delegatee cmd (called target) ? {obj public object forward x1 -methodprefix @ -verbose %self X} \ "target '-methodprefix' must not start with a dash" ? {obj public object forward x2 -prefix @ -verbose %self X} \ "::obj::x2" ? {obj x2 a b c} "::obj: unable to dispatch method '@X'" ## ## argclindex ## obj public object forward foo list {%argclindex {A B C}} ? {obj foo} A ? {obj foo _} "B _" ? {obj foo _ _} "C _ _" ? {obj foo _ _ _ _} "forward: not enough elements in specified list of ARGC argument argclindex {A B C}" ## ## %1 + defaults ## ::nsf::configure debug 2 obj public object method FOO args {return [current method]-$args} obj public object method OOF args {return [current method]-$args} obj public object forward foo -verbose %self %1 ? {obj foo} {%1 requires argument; should be "foo arg ..."} obj public object forward foo -verbose %self {%1 FOO} ? {obj foo} "FOO-" ? {obj foo X} {::obj: unable to dispatch method 'X'} obj public object forward foo -verbose %self {%1 FOO OOF} ? {obj foo X} {::obj: unable to dispatch method 'X'} obj public object forward foo -verbose %self {%1 {FOO OOF}} ? {obj foo X} "OOF-X" ? {obj foo X Y} {::obj: unable to dispatch method 'X'} obj public object forward foo -verbose %self {%1 {FOO OOF}} {%1 {A B}} ? {obj foo} "FOO-A" obj public object forward foo -verbose %self {%1 {FOO OOF}} {%1 {A B}} ? {obj foo X} "OOF-B X" ## ## -prefix; requires a 2nd arg! ## ## obj public object method _FOO args {return [current method]-$args} ## 1) 2nd arg is missing! Prefix is silently neglected ... obj public object forward FOO -prefix _ %self ? {obj FOO} {::obj} # 2) There is a 2nd arg, a method argument ? {obj FOO FOO X} "_FOO-X" "prefix, 2nd arg is method argument" # 3) There is a 2nd arg, a forwarder argument obj public object forward FOO -prefix _ %self %1 ? {obj FOO FOO X} "_FOO-X" "prefix, 2nd arg is forwarder argument" # 4) There is a 2nd arg, a forwarder argument provided through %1 obj public object forward FOO -prefix _ %self {%1 {FOO FOO}} ? {obj FOO X} "_FOO-X" "prefix, 2nd arg is forwarder argument provided through %1" } nx::test case positioning-arg-extended { nx::Object create obj obj public object forward foo list {%@end %self} ? {obj foo 1 2 3} [list 1 2 3 ::obj] obj public object forward foo list {%@end %method} ? {obj foo 1 2 3} [list 1 2 3 foo] obj public object forward foo list {%@end %%} ? {obj foo 1 2 3} [list 1 2 3 %] obj public object forward foo list {%obj foo} ? {obj foo 1 2 3} "too many nested evaluations (infinite loop?)" obj public object forward foo list {%apply {{x} {return $x}} A} ? {obj foo 1 2 3} [list A 1 2 3] ## positioning of "simple" cmd substitution works fine obj public object forward foo list {%@end %obj} ? {obj foo 1 2 3} [list 1 2 3 ::obj] ## lindex allows for omitting the index arg or passing {} as index value ... forward catches both cases nicely: obj public object forward foo list {%@{} %obj} ? {obj foo 1 2 3} "forward: invalid index specified in argument %@{} %obj" obj public object forward foo list {%@ %obj} ? {obj foo 1 2 3} "forward: invalid index specified in argument %@ %obj" ## ## resolving name conflicts between Tcl commands & predefined ## placeholder names -> use fully qualified names ## obj public object forward foo list {%@end %::proc} ? {obj foo 1 2 3} {wrong # args: should be "::proc name args body"} # the next test does not work unless called from nxsh, which imports ::nx::self #obj public object forward foo list {%@end %::self} #? {obj foo 1 2 3} [list 1 2 3 ::obj] obj public object forward foo list {%@end %::nx::self} ? {obj foo 1 2 3} [list 1 2 3 ::obj] "fully qualified self" obj public object forward foo list {%@end %::1} ? {obj foo 1 2 3} {invalid command name "::1"} ## ## position prefixes are interpreted in a context-dependent manner: ## obj public object forward foo list {%@1 %@1} ? {obj foo 1 2 3} {invalid command name "@1"} if {![string length "ISSUES"]} { ## list protection makes this fail obj public object forward foo list {%@end {%argclindex {A B C D}}} ? {obj foo 1 2 3} [list 1 2 3 D] ## positioned "complex" cmd substitution (cmd + args) not working because of list protection obj public object forward foo list {%@end {%list 1}} ? {obj foo 1 2 3} [list 1 2 3 A] ## Why not %1 not working with positioning working? obj public object forward foo list {%@end %1} ? {obj foo 1 2 3} [list 1 2 3 1] ## ## Should this be caught somehow? How would this be treated when list protection would not interfere? ## obj public object forward foo list {%@1 {%@1 "x"}} ? {obj foo 1 2 3} "forward: invalid index specified in argument %@{} %obj" } } ############################################### # substitution depending on number of arguments ############################################### nx::test case num-args { nx::Object create obj { :public object forward f %self [list %argclindex [list a b c]] :object method a args {return [list [current method] $args]} :object method b args {return [list [current method] $args]} :object method c args {return [list [current method] $args]} } ? {obj f} [list a {}] ? {obj f 1 } [list b 1] ? {obj f 1 2} [list c {1 2}] ? {catch {obj f 1 2 3}} 1 } ############################################### # option earlybinding ############################################### nx::test case earlybinding { nx::Object create obj { #:public object forward s -earlybinding ::set ::X :public object forward s ::set ::X } ? {obj s 100} 100 ? {obj s} 100 nx::Object public method f args { next } nx::Class create NS nx::Class create NS::Main { :public object method m1 {} { :m2 } :public object method m2 {} { ? {namespace eval :: {nx::Object create toplevelObj1}} ::toplevelObj1 ? [list set _ [namespace current]] ::NS ? [list set _ [NS create m1]] ::NS::m1 NS filters set f ? [list set _ [NS create m2]] ::NS::m2 NS filters set "" namespace eval ::test { ? [list set _ [NS create m3]] ::test::m3 NS filters set f ? [list set _ [NS create m4]] ::test::m4 NS filters set "" } namespace eval test { ? [list set _ [NS create m5]] ::NS::test::m5 NS filters set f ? [list set _ [NS create m6]] ::NS::test::m6 NS filters set "" } } :public method i1 {} { :i2 } :public method i2 {} { ? {namespace eval :: {nx::Object create toplevelObj2}} ::toplevelObj2 ? [list set _ [namespace current]] ::NS ? [list set _ [NS create i1]] ::NS::i1 NS filters set f ? [list set _ [NS create i2]] ::NS::i2 NS filters set "" namespace eval ::test { ? [list set _ [NS create i3]] ::test::i3 NS filters set f ? [list set _ [NS create i4]] ::test::i4 NS filters set "" } namespace eval test { ? [list set _ [NS create i5]] ::NS::test::i5 NS filters set f ? [list set _ [NS create i6]] ::NS::test::i6 NS filters set "" } } } #puts ==== NS::Main m1 NS::Main create m m i1 #puts ==== ? [list set _ [NS create n1]] ::n1 NS filters set f ? [list set _ [NS create n2]] ::n2 NS filters set "" #puts ==== namespace eval test { ? [list set _ [NS create n1]] ::test::n1 ? [list set _ [NS create n3]] ::test::n3 NS filters set f ? [list set _ [NS create n4]] ::test::n4 NS filters set "" } } ########################################### # forward to expr + callstack ########################################### nx::test case callstack { nx::Object public forward expr -frame object nx::Class create C { :method xx {} {current} :public object method t {o expr} { return [$o expr $expr] } } C create c1 ? {c1 expr {[current]}} ::c1 ? {c1 expr {[current] eq "::c1"}} 1 ? {c1 expr {[:xx]}} ::c1 ? {c1 expr {[:info class]}} ::C ? {c1 expr {[:info has type C]}} 1 ? {c1 expr {[:info has type ::C]}} 1 ? {C t ::c1 {[current]}} ::c1 ? {C t ::c1 {[current] eq "::c1"}} 1 ? {C t ::c1 {[:xx]}} ::c1 ? {C t ::c1 {[:info class]}} ::C ? {C t ::c1 {[:info has type C]}} 1 ? {C t ::c1 {[:info has type ::C]}} 1 nx::Object method expr {} {} } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/info-method.test000066400000000000000000001302671242365656200153560ustar00rootroot00000000000000# -*- Tcl -*- package req nx ::nx::configure defaultMethodCallProtection false package require nx::test # # Test info superclasses with closure and patterns (with and without # wildcards, prefixed or not, success or not). # nx::test case info-superclass { nx::Class create C nx::Class create D -superclass C # no patterns ? {D info superclasses} "::C" ? {D info superclasses -closure} "::C ::nx::Object" # fully qualified pattern, no wild-card characters, success ? {D info superclasses ::C} "::C" ? {D info superclasses -closure ::C} "::C" # unprefixed pattern, no wild-card characters, success ? {D info superclasses C} "::C" ? {D info superclasses -closure C} "::C" # fully qualified pattern, no wild-card characters, no success ? {D info superclasses ::D} "" ? {D info superclasses -closure ::D} "" ? {D info superclasses ::Dx} "" ? {D info superclasses -closure ::Dx} "" # unprefixed pattern, no wild-card characters, no success ? {D info superclasses D} "" ? {D info superclasses -closure D} "" ? {D info superclasses Dx} "" ? {D info superclasses -closure Dx} "" # fully qualified pattern, wild-card characters, success ? {D info superclasses ::*} "::C" ? {D info superclasses -closure ::C*} "::C" ? {D info superclasses -closure ::*} "::C ::nx::Object" ? {D info superclasses -closure ::nx*} "::nx::Object" # unprefixed pattern, wild-card characters, success ? {D info superclasses C*} "::C" ? {D info superclasses -closure *} "::C ::nx::Object" ? {D info superclasses -closure nx*} "::nx::Object" # fully qualified pattern, wild-card characters, no success ? {D info superclasses ::*D} "" ? {D info superclasses -closure ::*D} "" # unprefixed pattern, wild-card characters, no success ? {D info superclasses C*x} "" ? {D info superclasses -closure C*x} "" } # # Test "info method", base cases # nx::test case info-method-base { nx::Object create o { :object alias set ::set } nx::Class create C { :method m {x} {return proc-[self proc]} :object method mpo {} {return instproc-[self proc]} # :method m-with-assertions {} {return proc-[self proc]} -precondition 1 -postcondition 2 :forward addOne expr 1 + :object forward add1 expr 1 + :object forward fpo ::o :property -accessor public s :object property -accessor public spo :alias a ::set :object alias apo ::puts } C create c1 ? {lsort [C info methods -callprotection all]} "a addOne m s" #? {lsort [C info methods]} "a addOne s" foreach m [lsort [C info methods -callprotection all]] { ? [subst -nocommands {lsort [c1 info lookup methods $m]}] $m } ? {C info method definition a} "::C public alias a ::set" ? {c1 info lookup method a} "::nsf::classes::C::a" ? {c1 info lookup method addOne} "::nsf::classes::C::addOne" ? {c1 info lookup method m} "::nsf::classes::C::m" ? {c1 info lookup method s} "::nsf::classes::C::s" c1 object method foo {} {puts foo} ? {c1 info object method definition foo} "::c1 public object method foo {} {puts foo}" ? {c1 info lookup method foo} "::c1::foo" ? {C info method registrationhandle m} "::nsf::classes::C::m" ? {C info object method registrationhandle mpo} "::C::mpo" ? {C info method definition m} {::C public method m x {return proc-[self proc]}} ? {C info object method definition mpo} {::C public object method mpo {} {return instproc-[self proc]}} # if {$::nsf::config(assertions)} { # ? {C info method definition m-with-assertions} \ # {::C public method m-with-assertions {} {return proc-[self proc]} -precondition 1 -postcondition 2} # } else { # ? {C info method definition m-with-assertions} \ # {::C public method m-with-assertions {} {return proc-[self proc]}} # } ? {C info method parameters m} {x} ? {nx::Class info method parameters method} \ {name arguments:parameter,0..* -checkalways:switch -returns body} ? {nx::Class info method parameters alias} \ {methodName -returns {-frame default} cmd} # raises currently an error ? {catch {C info method parameters a}} 1 ? {C info method definition addOne} "::C public forward addOne expr 1 +" ? {C info object method definition add1} "::C public object forward add1 expr 1 +" ? {C info object method definition fpo} "::C public object forward fpo ::o" ? {C info method definition s} "::C public forward s -prefix value= ::C::slot::s %1 %self s" ? {C info object method definition spo} "::C public object forward spo -prefix value= ::C::per-object-slot::spo %1 %self spo" ? {C info method definition a} "::C public alias a ::set" ? {C info object method definition apo} "::C public object alias apo ::puts" ? {::nx::Object info lookup methods -source application} "" ? {::nx::Class info lookup methods -source application} "" set object_methods "cget configure contains copy delete destroy eval info move object private protected public require" set class_methods "alias cget configure contains copy create delete destroy eval filters forward info method mixins move new object private property protected public require variable" ? {lsort [::nx::Object info lookup methods -source system]} $class_methods ? {lsort [::nx::Class info lookup methods -source system]} $class_methods ? {lsort [::nx::Object info lookup methods -source all]} $class_methods ? {lsort [::nx::Class info lookup methods -source all]} $class_methods ? {lsort [::nx::Object info lookup methods]} $class_methods ? {lsort [::nx::Class info lookup methods]} $class_methods ? {lsort [C info lookup methods -source application]} "add1 apo fpo mpo spo" ? {lsort [c1 info lookup methods -source application]} "a addOne foo m s" ? {lsort [C info lookup methods -source system]} $class_methods ? {lsort [c1 info lookup methods -source system]} $object_methods ::nx::configure defaultMethodCallProtection true # # the subsequent tests assume defaultMethodCallProtection == true # ? {::nx::configure defaultMethodCallProtection} true ::nx::Class create MC -superclass ::nx::Class { :protected method bar1 args {;} :method bar2 args {;} :public method foo args {;} :public object method foo args {;} } ? {lsort [MC info methods -type scripted -callprotection public]} "foo" ? {lsort [MC info methods -type scripted -callprotection protected]} "bar1 bar2" ? {lsort [MC info methods -type scripted -callprotection all]} "bar1 bar2 foo" ::nsf::method::property ::MC foo call-protected true ::nsf::method::property ::MC bar2 call-protected false ? {lsort [MC info methods -type scripted -callprotection public]} "bar2" ? {lsort [MC info methods -type scripted -callprotection protected]} "bar1 foo" ? {lsort [MC info methods -type scripted -callprotection all]} "bar1 bar2 foo" ::nx::configure defaultMethodCallProtection false } # # Test visability of obj-objects # nx::test case visability-sub-objects { ::nx::Object create o { ::nx::Object create [::nx::self]::sub { :object method foo {} {;} } :public object alias soAlias ::o::sub } # # per default, we see the alias and the subobject # ? {o info object methods} "soAlias sub" ? {o info object method type soAlias} "alias" # the subobject can be hidden via private (see protection.test) } # # Test visability of aliased Objects # nx::test case visability-aliased-object { ::nx::Object create ::I ::nx::Class create C { :public alias i ::I :create c1 } # # We see always the alias to the object # ? {C info methods i} "i" ? {c1 info lookup methods i} "i" ? {C info methods *i} "i" ? {c1 info lookup methods *i} "i" } #package require nx::test # # Introspect the returns method property throught the "info method" # API chunk ... # set checkFlag [::nsf::configure checkresults] set dmcFlag [::nx::configure defaultMethodCallProtection] # # Make sure that return-value checking is active for the current # interp ... # ::nsf::configure checkresults true # # Neutralize the defaultMethodCallProtection for the scope of these tests # ::nx::configure defaultMethodCallProtection false nx::test case method-returns { # # A test object covering basic cases, adopted from returns.test # nx::Class create C { # scripted method without paramdefs for in-parameters :method bar-ok1 {a b} -returns integer {return 1} # scripted method with paramdefs for in-parameters :method bar-nok {a b:integer} -returns integer {return a} # alias to tcl-cmd (no param defs for in-parameters) :alias incr -returns integer -frame object ::incr :forward ++ -returns integer ::expr 1 + :public object method instances {} -returns object,1..n {:info instances} :create c1 { :public object method foo {} -returns integer {;} :public object method "bar baz" {} -returns integer {;} :public object method "bar boo" {} -returns integer {;} } } ? {C info method returns bar-ok1} "integer" ? {C info method returns bar-nok} "integer" ? {C info method returns incr} "integer" ? {C info method returns ++} "integer" ? {C info object method returns instances} "object,1..n" ? {c1 info object method returns foo} "integer" ? {c1 info object method returns "bar baz"} "integer" ? {c1 info object method returns "bar boo"} "integer" # # Ensemble object ... # ? {c1 info object method returns bar} "" # # Non-existing method ... # ? {c1 info object method returns baf} "" # # Non-existing submethod ... # ? {c1 info object method returns "bar baf"} "" } nx::test case method-definition-with-returns { # # A test object covering basic cases, adopted from returns.test # nx::Class create C { # scripted method without paramdefs for in-parameters :method bar-ok1 {a b} -returns integer {;} # scripted method with paramdefs for in-parameters :method bar-nok {a b:integer} -returns integer {;} # alias to tcl-cmd (no param defs for in-parameters) :alias incr -returns integer -frame object ::incr :forward ++ -returns integer ::expr 1 + :public object method instances {} -returns object,1..n {;} :create c1 { :public object method foo {} -returns integer {;} :object method "bar baz" {} -returns integer {;} } } ? {C info method definition bar-ok1} "::C public method bar-ok1 {a b} -returns integer {;}" ? {C info method definition bar-nok} \ "::C public method bar-nok {a b:integer} -returns integer {;}" ? {C info method definition incr} "::C public alias incr -frame object -returns integer ::incr" ? {C info method definition ++} "::C public forward ++ -returns integer ::expr 1 +" ? {C info object method definition instances} \ "::C public object method instances {} -returns object,1..n {;}" ? {c1 info object method definition foo} "::c1 public object method foo {} -returns integer {;}" ? {c1 info object method definition "bar baz"} "::c1 public object method {bar baz} {} -returns integer {;}" } nx::test case copy-with-returns { nx::Class create C { # scripted method without paramdefs for in-parameters :method bar-ok1 {a b} -returns integer {;} # scripted method with paramdefs for in-parameters :method bar-nok {a b:integer} -returns integer {;} # alias to tcl-cmd (no param defs for in-parameters) :alias incr -returns integer -frame object ::incr :forward ++ -returns integer ::expr 1 + :public object method instances {} -returns object,1..n {;} :create c1 { :public object method foo {} -returns integer {;} :object method "bar baz" {} -returns integer {;} } } c1 copy c2 ? {c2 info object method returns foo} [c1 info object method returns foo] ? {c2 info object method definition foo} [lreplace [c1 info object method definition foo] 0 0 ::c2] ? {c2 info object method returns "bar baz"} [c1 info object method returns "bar baz"] ? {c2 info object method definition "bar baz"} [lreplace [c1 info object method definition "bar baz"] 0 0 ::c2] ? {c2 info object method returns "bar boo"} [c1 info object method returns "bar boo"] C copy CC ? {CC info method returns bar-ok1} [C info method returns bar-ok1] ? {CC info method definition bar-ok1} [lreplace [C info method definition bar-ok1] 0 0 ::CC] ? {CC info method returns bar-nok} [C info method returns bar-nok] ? {CC info method definition bar-nok} [lreplace [C info method definition bar-nok] 0 0 ::CC] # # TODO: Add/re-activate tests for copying aliases and forwards once # handled by NsfNSCopyCmdsCmd properly! # # ? {CC info method returns incr} [C info method returns incr] # ? {CC info method returns ++} [C info method returns ++] ? {CC info object method returns instances} [C info object method returns instances] ? {CC info object method definition instances} [lreplace [C info object method definition instances] 0 0 ::CC] } # # TODO: Add tests for about returns + setter / returns + nsf::proc, if applicable ... # ::nsf::configure checkresults $checkFlag ::nx::configure defaultMethodCallProtection $dmcFlag # -- nx::test case callable { # define the same method for Object and Class ::nx::Object method bar {} {return Object.bar} ::nx::Class method bar {} {return Class.bar} ::nx::Object create o ? {o info lookup method bar} "::nsf::classes::nx::Object::bar" ? {o info lookup methods bar} bar ? {o bar} Object.bar o object mixins set ::nx::Class ? {o info precedence} "::nx::Class ::nx::Object" ? {o info lookup method bar} "::nsf::classes::nx::Class::bar" ? {o info lookup methods bar} bar ? {o info lookup methods create} "" ? {o info lookup method create} "" ? {o bar} Class.bar ? {o object method foo {} {return o.foo}} "::o::foo" ? {o object alias is ::nsf::is} "::o::is" #? {o object property x} {variable definition for 'x' (without value and accessor) is useless} ? {o object property x} "" ? {o object property -accessor public x} "::o::x" ? {lsort [o info object methods]} "foo is x" #? {o object property A} {variable definition for 'A' (without value and accessor) is useless} ? {o object property A} "" ? {o object property -accessor public A} ::o::A ? {o object forward fwd ::set} ::o::fwd ? {lsort [o info object methods]} "A foo fwd is x" o object method f args ::nx::next ? {o info lookup methods create} "" ? {o info lookup methods configure} configure ? {o info lookup method configure} "::nsf::classes::nx::Object::configure" ? {o object filters set f} "f" ? {o object filters guard f { 1 == 1 }} "" ? {o info object filters -guards} {{f -guard { 1 == 1 }}} ? {o object filters guard f} " 1 == 1 " o object filters set "" nx::Class create Foo ? {Foo method f args ::nx::next} "::nsf::classes::Foo::f" ? {Foo method f2 args ::nx::next} "::nsf::classes::Foo::f2" ? {Foo filters set {f f2}} "f f2" ? {Foo info filters} "f f2" ? {Foo filters guard f {2 == 2}} "" ? {Foo info filters -guards f} "{f -guard {2 == 2}}" ? {Foo info filters -guards f2} "f2" ? {Foo info filters -guards} "{f -guard {2 == 2}} f2" ? {Foo filters set {}} "" ? {Foo object method f args ::nx::next} "::Foo::f" ? {Foo object method f2 args ::nx::next} "::Foo::f2" ? {Foo object filters set {f f2}} "f f2" ? {Foo info object filters} "f f2" ? {Foo object filters guard f {2 == 2}} "" ? {Foo object filters guard f} "2 == 2" ? {Foo info object filters -guards f} "{f -guard {2 == 2}}" ? {Foo info object filters -guards f2} "f2" ? {Foo info object filters -guards} "{f -guard {2 == 2}} f2" ? {Foo object filters set {}} "" Foo destroy nx::Class create Fly o object mixins add Fly ? {o info object mixins} "::Fly ::nx::Class" ? {o object mixins guard ::Fly {1}} "" ? {o info object mixins -guards} "{::Fly -guard 1} ::nx::Class" ? {o info object mixins -guards Fly} "{::Fly -guard 1}" o object mixins delete ::Fly ? {o info object mixins} "::nx::Class" nx::Class create Foo Foo mixins add ::nx::Class Foo mixins add Fly ? {Foo info mixins} "::Fly ::nx::Class" ? {Foo mixins guard ::Fly {1}} "" ? {Foo info mixins -guards} "{::Fly -guard 1} ::nx::Class" ? {Foo info mixins -guards Fly} "{::Fly -guard 1}" Foo mixins delete ::Fly ? {Foo info mixins} "::nx::Class" Foo object mixins add ::nx::Class Foo object mixins add Fly ? {Foo info object mixins} "::Fly ::nx::Class" ? {Foo object mixins guard ::Fly {1}} "" ? {Foo info object mixins -guards} "{::Fly -guard 1} ::nx::Class" ? {Foo info object mixins -guards Fly} "{::Fly -guard 1}" Foo object mixins delete ::Fly ? {Foo info object mixins} "::nx::Class" ? {Foo info lookup methods create} "create" ? {Foo info lookup method create} "::nsf::classes::nx::Class::create" ? {o object mixins set ""} "" } # # test info slot objects / info lookup slots # nx::test case info-slots { nx::Class create C { :property a :property {b 1} } nx::Class create D -superclass C { :property {b 2} :property c :object property -accessor public {a2 ""} :method "sub foo" args {;} :create d1 { :object property -accessor public {a3 ""} } } ? {C info slots} "::C::slot::a ::C::slot::b" ? {D info slots} "::D::slot::b ::D::slot::c" ? {D info slots -closure -source application} "::D::slot::b ::D::slot::c ::C::slot::a" ? {d1 info lookup slots -source application} "::d1::per-object-slot::a3 ::D::slot::b ::D::slot::c ::C::slot::a" ? {D info object slots} "::D::per-object-slot::a2" ? {d1 info object slots} "::d1::per-object-slot::a3" ? {C info object slots} "" } # # test info slot objects / info lookup slots # nx::test case slots { nx::Class create C { :property a :property {b 1} :property -accessor private {x 100} :object property -accessor private {y 100} } nx::Class create D -superclass C { :property {b 2} :property c :object property -accessor public a2 :method "sub foo" args {;} :create d1 } ? {lsort [D info lookup slots]} "::D::per-object-slot::a2 ::nx::Class::slot::filters ::nx::Class::slot::mixins ::nx::Class::slot::superclasses ::nx::Object::slot::__initblock ::nx::Object::slot::class ::nx::Object::slot::object-filters ::nx::Object::slot::object-mixins" ? {D info lookup slots superclasses} "::nx::Class::slot::superclasses" ? {D info lookup slots ::nx::Class::slot::superclasses} "::nx::Class::slot::superclasses" ? {D info lookup slots a2} "::D::per-object-slot::a2" ? {D info lookup slots ::D::per-object-slot::a2} "::D::per-object-slot::a2" ? {d1 info lookup slots b} "::D::slot::b" ? {d1 info lookup slots ::D::slot::b} "::D::slot::b" C create c1 ? {c1 info precedence} "::C ::nx::Object" ? {C info heritage} "::nx::Object" ? {C info slots -closure -source application} "::C::slot::____C.x ::C::slot::a ::C::slot::b" ? {lsort [C info slots -closure]} \ "::C::slot::____C.x ::C::slot::a ::C::slot::b ::nx::Object::slot::__initblock ::nx::Object::slot::class ::nx::Object::slot::object-filters ::nx::Object::slot::object-mixins" ? {C info slots} "::C::slot::____C.x ::C::slot::a ::C::slot::b" ? {C info slots x} "::C::slot::____C.x" ? {C info slots ::C::slot::____C.x} "::C::slot::____C.x" ? {C info lookup slots y} "::C::per-object-slot::____C.y" ? {C info lookup slots ::C::per-object-slot::____C.y} "::C::per-object-slot::____C.y" # Test patterns for "info slots" # Partial name, no metachars ? {C info slots -closure object-mixins} "::nx::Object::slot::object-mixins" # Partial name with metachars ? {lsort [C info slots -closure *in*]} \ "::nx::Object::slot::__initblock ::nx::Object::slot::object-mixins" # Fully qualified name, no metachars ? {C info slots -closure ::nx::Object::slot::object-mixins} "::nx::Object::slot::object-mixins" # Fully qualified name, with metachars # The following command returns the same as "C info slots" ? {C info slots -closure ::C::*} "::C::slot::____C.x ::C::slot::a ::C::slot::b" # The following command returns the slots of D inherited from # C. Slot "b" is shadowed by D. ? {D info slots -closure ::C::*} "::C::slot::____C.x ::C::slot::a" # Test patterns for "info lookup slots" # Partial name, no metachars ? {c1 info lookup slots object-mixins} "::nx::Object::slot::object-mixins" # Partial name with metachars ? {lsort [c1 info lookup slots *in*]} \ "::nx::Object::slot::__initblock ::nx::Object::slot::object-mixins" # Fully qualified name, no metachars ? {c1 info lookup slots ::nx::Object::slot::object-mixins} "::nx::Object::slot::object-mixins" # Fully qualified name, with metachars ? {c1 info lookup slots ::C::*} "::C::slot::____C.x ::C::slot::a ::C::slot::b" D create d1 ? {D info slots} "::D::slot::b ::D::slot::c" ? {D info slots -closure -source application} "::D::slot::b ::D::slot::c ::C::slot::____C.x ::C::slot::a" ? {::nx::Object info method parameters info} "" ? {d1 info precedence} "::D ::C ::nx::Object" ? {lsort [d1 info lookup slots]} \ "::C::slot::____C.x ::C::slot::a ::D::slot::b ::D::slot::c ::nx::Object::slot::__initblock ::nx::Object::slot::class ::nx::Object::slot::object-filters ::nx::Object::slot::object-mixins" # Fully qualified name, with metachars # The following command returns the slots of D inherited from # C. Slot "b" is shadowed by D. ? {d1 info lookup slots ::C::*} "::C::slot::____C.x ::C::slot::a" } # # test info submethod and method handles for submethods # nx::test case info-submethod { nx::Object create o { :object method "foo a" {} {return a} :object method "foo b" {x:int y:upper} {return b} } nx::Object create o2 nx::Class create C { :method "bar a" {} {return a} :method "bar b" {x:int y:upper} {return b} :method "bar baz x" {x:int y:upper} {return x} :method "bar baz y" {x:int y:upper} {return y} :object method "foo x" {z:int} {return z} :object method "foo y" {z:int} {return z} } # query definition on submethod ? {o info object method definition "foo b"} {::o public object method {foo b} {x:int y:upper} {return b}} # query definition on submethod with handle ? {o info object method definition "::o::foo b"} {::o public object method {foo b} {x:int y:upper} {return b}} # query definition on submethod with handle ? {o info object method definition "::o::foo b"} {::o public object method {foo b} {x:int y:upper} {return b}} # query definition on submethod with handle called on different object ? {o2 info object method definition "::o::foo b"} {::o public object method {foo b} {x:int y:upper} {return b}} # query definition on handle of ensemble object called on different object ? {o2 info object method definition "::o::foo::b"} {::o::foo public object method b {x:int y:upper} {return b}} # query definition on submethod with handle called on class ? {o2 info object method definition "::o::foo b"} {::o public object method {foo b} {x:int y:upper} {return b}} # query definition on handle of ensemble object called on class ? {o2 info object method definition "::o::foo::b"} {::o::foo public object method b {x:int y:upper} {return b}} # query definition on submethod of class ? {::nx::Object info method definition "info lookup methods"} \ {::nx::Object public alias {info lookup methods} ::nsf::methods::object::info::lookupmethods} # query definition on submethod of class with handle ? {o info object method definition "::nsf::classes::nx::Object::info lookup methods"} \ {::nx::Object public alias {info lookup methods} ::nsf::methods::object::info::lookupmethods} # query definition on handle of ensemble object of class ? {o info object method definition "::nx::Object::slot::__info::lookup::methods"} \ {::nx::Object::slot::__info::lookup public object alias methods ::nsf::methods::object::info::lookupmethods} ? {lsort [o info object method submethods dummy]} "" ? {lsort [o info object method submethods foo]} "a b" ? {lsort [o info object method submethods "foo a"]} "" ? {lsort [C info method submethods "bar"]} "a b baz" ? {lsort [C info method submethods "bar a"]} "" ? {lsort [C info method submethods "bar baz"]} "x y" ? {lsort [C info method submethods "bar baz y"]} "" ? {lsort [C info object method submethods "foo"]} "x y" ? {lsort [C info object method submethods "foo x"]} "" # # method handles for ensemble methods # ? {C info method registrationhandle "bar"} {::nsf::classes::C::bar} ? {C info method registrationhandle "bar a"} {::nsf::classes::C::bar a} ? {C info method registrationhandle "bar baz y"} {::nsf::classes::C::bar baz y} # # test whether the handles for ensemble methods work # ? {C info method parameters [C info method registrationhandle "bar"]} "" ? {C info method parameters [C info method registrationhandle "bar b"]} "x:int y:upper" ? {C info method parameters [C info method registrationhandle "bar baz y"]} "x:int y:upper" # # check methods paths as method specifications # ? {C info method definition "bar b"} {::C public method {bar b} {x:int y:upper} {return b}} ? {C info method definition "::nsf::classes::C::bar b"} {::C public method {bar b} {x:int y:upper} {return b}} ? {o2 info object method definition "::nsf::classes::C::bar b"} {::C public method {bar b} {x:int y:upper} {return b}} # # test class modifier on handles # ? {C info object method registrationhandle "foo"} {::C::foo} ? {C info object method registrationhandle "foo x"} {::C::foo x} # # info method definition with method paths # ? {C info object method definition "::C::foo x"} {::C public object method {foo x} z:int {return z}} ? {C info method definition "::C::foo x"} {::C public object method {foo x} z:int {return z}} ? {o2 info object method definition "::C::foo x"} {::C public object method {foo x} z:int {return z}} ? {C info method definition "bar baz y"} \ {::C public method {bar baz y} {x:int y:upper} {return y}} ? {C info method definition "::nsf::classes::C::bar baz y"} \ {::C public method {bar baz y} {x:int y:upper} {return y}} # # test "info method parameters" # ? {nx::Object info method parameters "info lookup methods"} \ "-callprotection -incontext:switch -type -nomixins:switch -path:switch -source pattern:optional" ? {nx::Object info method syntax "info lookup methods"} \ "/cls/ info lookup methods ?-callprotection all|public|protected|private? ?-incontext? ?-type all|scripted|builtin|alias|forwarder|object|setter|nsfproc? ?-nomixins? ?-path? ?-source all|application|system? ?/pattern/?" ? {o info object method parameters "foo b"} "x:int y:upper" ? {nx::Object info method parameters ::nx::Object::slot::__info::lookup::methods} \ "-callprotection -incontext:switch -type -nomixins:switch -path:switch -source pattern:optional" ? {o info object method parameters "::o::foo::b"} "x:int y:upper" ? {nx::Object info method registrationhandle "info"} "::nsf::classes::nx::Object::info" ? {nx::Object info method registrationhandle "info lookup methods"} \ "::nsf::classes::nx::Object::info lookup methods" ? {nx::Object info method registrationhandle "::nsf::classes::nx::Object::info lookup methods"} \ "::nsf::classes::nx::Object::info lookup methods" ? {o info object method registrationhandle "foo b"} "::o::foo b" } # # test info slot parameter|parametersyntax # nx::test case info-slot-parametersyntax { nx::Class create C { :property a :property {b 1} } nx::Class create D -superclass C { :property {b 2} :property c :object property -accessor public a2 :method "sub foo" args {;} } C new ? {C info lookup syntax create} "/objectName/ ?-a /value/? ?-b /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" ? {C info lookup syntax create a} "?-a /value/?" ? {C info lookup parameters create } "objectName -a {-b 1} -object-mixins:mixinreg,slot=::nx::Object::slot::object-mixins,slotset,method=object-mixin,0..n -object-filters:filterreg,slot=::nx::Object::slot::object-filters,slotset,method=object-filter,0..n -class:class,alias,method=::nsf::methods::object::class __initblock:cmd,optional,nodashalnum" #? {C info parameter list} "-a -b -noinit -object-mixins -class -object-filters __initblock" #? {C info lookup args create} "methodName a b noinit object-mixin class object-filter __initblock" ? {lsort [C info slots -closure]} "::C::slot::a ::C::slot::b ::nx::Object::slot::__initblock ::nx::Object::slot::class ::nx::Object::slot::object-filters ::nx::Object::slot::object-mixins" ? {C info lookup parameters create b} "{-b 1}" ? {D info lookup parameters create b} "{-b 2}" ? {D info slots -closure b} "::D::slot::b" ? {D info slots -closure a} "::C::slot::a" ? {D info slots -closure class} "::nx::Object::slot::class" # ? {D info parameter list} "-b -c -a -noinit -object-mixins -object-filters -class __initblock" # ? {D info parameter names} "b c a noinit object-mixins object-filters class __initblock" } # # test "info methods -path" # nx::test case info-methods-path { # # test case on base class # ? {::nx::Object info methods "info"} "info" ? {::nx::Object info methods -path "info"} "" ? {lsort [::nx::Object info methods -path "info lookup *"]} \ "{info lookup filter} {info lookup filters} {info lookup method} {info lookup methods} {info lookup mixins} {info lookup parameters} {info lookup slots} {info lookup syntax} {info lookup variables}" ? {lsort [::nx::Object info methods -path "info *parameter*"]} \ "{info lookup parameters} {info object method parameters} {info variable parameter}" ? {lsort [::nx::Object info methods "slots"]} "" ? {lsort [::nx::Object info methods "*slots*"]} "" ? {lsort [::nx::Object info methods -path "*slot*"]} \ "{info lookup slots} {info object slots}" ? {lsort [::nx::Object info methods -path "*filter*"]} \ "{info lookup filter} {info lookup filters} {info object filters} {object filters}" ::nx::Class create C { :public method "string length" {s} {puts length} :public method "string reverse" {s} {puts reverse} :public method foo {} {puts foo} :protected method "a b c" {} {puts "a b c"} :protected method "a b d" {} {puts "a b d"} :public method "a c" {d c} {puts "a c"} :create c1 } nx::Class create D -superclass C { :public method "string length" {s} {puts length} :public method "string compress" {s} {puts compress} :create d1 } ? {lsort [C info methods -path -callprotection all]} \ "{a b c} {a b d} {a c} foo {string length} {string reverse}" ? {lsort [C info methods -path]} \ "{a c} foo {string length} {string reverse}" # # lookup ensemble methods # ? {lsort [c1 info lookup methods -path "string *"]} \ "{string length} {string reverse}" # # lookup ensemble methods combined from multiple classes # ? {lsort [d1 info lookup methods -path "string *"]} \ "{string compress} {string length} {string reverse}" # # search for ensemble method # ? {lsort [d1 info lookup method "string length"]} "::nsf::classes::D::string length" ? {lsort [d1 info lookup method "string reverse"]} "::nsf::classes::C::string reverse" } # # Test parameter syntax for a methods and cmds # nx::test case parametersyntax { # a true method ? {::nx::Class info method syntax method} \ "/cls/ method /name/ /arguments/ ?-checkalways? ?-returns /value/? /body/" # a forwarder to ::nsf::relation; definition comes via array ::nsf::parametersyntax ? {::nx::Class info method syntax mixins} "/cls/ mixins add /class/|classes ?/pattern/?|clear|delete /class/|get|guard /class/ /?expr?/|set /class .../" ? {::nx::Class info method syntax ::nx::next} "/cls/ next ?/arguments/?" ? {::nx::Class info method syntax ::nsf::xotclnext} "/cls/ xotclnext ?--noArgs? ?/arg .../?" } # # Test info heritage, base cases # nx::test case info-heritage { Class create A Class create B -superclass A Class create BB -superclass B Class create C -superclass A Class create CC -superclass C Class create D -superclass A Class create M1 Class create M2 -superclass A ? {A info heritage} "::nx::Object" ? {B info heritage} "::A ::nx::Object" ? {M1 info heritage} "::nx::Object" ? {M2 info heritage} "::A ::nx::Object" B mixins add M1 ? {A info heritage} "::nx::Object" ? {B info heritage} "::M1 ::A ::nx::Object" ? {B info mixins -closure} "::M1" B mixins set M2 ? {A info heritage} "::nx::Object" ? {B info heritage} "::M2 ::A ::nx::Object" ? {B info mixins -closure} "::M2" B mixins set A ? {A info heritage} "::nx::Object" ? {B info heritage} "::A ::nx::Object" B mixins set C ? {A info heritage} "::nx::Object" ? {B info heritage} "::C ::A ::nx::Object" B mixins set "" ? {BB info heritage} "::B ::A ::nx::Object" BB mixins set CC ? {BB info heritage} "::CC ::C ::B ::A ::nx::Object" BB mixins set "" ? {BB info heritage} "::B ::A ::nx::Object" } # # Test transitive per-class mixins # nx::test case info-heritage-transitive { Class create O Class create A -superclass O Class create B -superclass A Class create C -superclass A Class create D -superclass A # transitive case C mixins set D B mixins set C ? {C info heritage} "::D ::A ::O ::nx::Object" ? {D info heritage} "::A ::O ::nx::Object" ? {B info heritage} "::D ::C ::A ::O ::nx::Object" # reset C mixins set "" B mixins set "" ? {B info heritage} "::A ::O ::nx::Object" ? {C info heritage} "::A ::O ::nx::Object" ? {D info heritage} "::A ::O ::nx::Object" # transitve different order B mixins set C C mixins set D ? {B info heritage} "::D ::C ::A ::O ::nx::Object" ? {C info heritage} "::D ::A ::O ::nx::Object" ? {D info heritage} "::A ::O ::nx::Object" # reset C mixins set "" B mixins set "" ? {B info heritage} "::A ::O ::nx::Object" ? {C info heritage} "::A ::O ::nx::Object" ? {D info heritage} "::A ::O ::nx::Object" } # # Test circular mixins # nx::test case info-heritage-circular { Class create O Class create A -superclass O Class create B -superclass A Class create BB -superclass B Class create C -superclass A Class create CC -superclass C Class create D -superclass A Class create M3 Class create M2 -superclass A Class create M # circular case ? {B mixins set C} "::C" ? {C mixins get} "" #? {C mixins set B} "classes dependent on ::C contain a cycle" #? {C mixins get} "" ;# make sure, the mixin list of C was reset ? {C mixins set B} "::B" ? {C mixins get} "::B" ? {B info heritage} "::C ::A ::O ::nx::Object" ? {C info heritage} "::B ::A ::O ::nx::Object" #? {C info heritage} "::A ::O ::nx::Object" ? {D info heritage} "::A ::O ::nx::Object" # reset C mixins set "" B mixins set "" ? {B info heritage} "::A ::O ::nx::Object" ? {C info heritage} "::A ::O ::nx::Object" ? {D info heritage} "::A ::O ::nx::Object" # indirect circular case B mixins set C ? {C mixins get} "" ? {C mixins set BB} "::BB" #? {C mixins set BB} "classes dependent on ::C contain a cycle" #? {C mixins get} "" ? {B info heritage} "::BB ::C ::A ::O ::nx::Object" ? {C info heritage} "::BB ::B ::A ::O ::nx::Object" #? {B info heritage} "::C ::A ::O ::nx::Object" #? {C info heritage} "::A ::O ::nx::Object" ? {D info heritage} "::A ::O ::nx::Object" # reset C mixins set "" B mixins set "" ? {B info heritage} "::A ::O ::nx::Object" ? {C info heritage} "::A ::O ::nx::Object" ? {D info heritage} "::A ::O ::nx::Object" ? {M3 mixins set B} ::B ? {A info heritage} "::O ::nx::Object" ? {B info heritage} "::A ::O ::nx::Object" ? {M3 info heritage} "::B ::A ::O ::nx::Object" ? {A mixins set M3} "::M3" ? {A info heritage} "::B ::M3 ::O ::nx::Object" ? {B info heritage} "::M3 ::A ::O ::nx::Object" #? {A mixins set M3} "classes dependent on ::A contain a cycle" #? {A info heritage} "::O ::nx::Object" #? {B info heritage} "::A ::O ::nx::Object" ? {M3 create m1} ::m1 ? {m1 info precedence} "::B ::A ::O ::M3 ::nx::Object" ? {M3 info heritage} "::B ::A ::O ::nx::Object" ? {B mixins set M3} ::M3 ? {B info heritage} "::M3 ::A ::O ::nx::Object" #? {B mixins set M3} {classes dependent on ::B contain a cycle} #? {B info heritage} "::A ::O ::nx::Object" } # # Mixin the same class twice, once per-class and one per-object. # nx::test case info-heritage-simple-multimix { Class create Agent Class create MovementTest Class create MovementLog Agent mixins set MovementTest Agent create a1 ? {Agent info heritage} "::MovementTest ::nx::Object" ? {a1 info precedence} "::MovementTest ::Agent ::nx::Object" a1 object mixins set {MovementTest MovementLog} ? {Agent info heritage} "::MovementTest ::nx::Object" ? {a1 info precedence} "::MovementTest ::MovementLog ::Agent ::nx::Object" } # # Mixin several classes at several class levels and on the object # level # nx::test case info-heritage-multimix { Class create A Class create B -superclass A Class create M1 Class create M2 Class create M3 Class create M4 B create b1 ? {B info heritage} "::A ::nx::Object" ? {b1 info precedence} "::B ::A ::nx::Object" ? {b1 info precedence ::M*} "" A mixins set {M1 M2} ? {B info heritage} "::M1 ::M2 ::A ::nx::Object" ? {b1 info precedence} "::M1 ::M2 ::B ::A ::nx::Object" ? {b1 info precedence ::M*} "::M1 ::M2" ? {b1 info precedence ::X*} "" b1 object mixins set {M1 M1 M4} ? {b1 info precedence} "::M1 ::M4 ::M2 ::B ::A ::nx::Object" ? {b1 info object mixins} "::M1 ::M4" B mixins set {M3 M1 M1 M4} ? {B info heritage} "::M3 ::M1 ::M4 ::M2 ::A ::nx::Object" ? {b1 info precedence} "::M1 ::M4 ::M3 ::M2 ::B ::A ::nx::Object" } # # per-object mixin with implied classes # nx::test case info-heritage-multimix { Class create A Class create B -superclass A Class create C Class create PCM -superclass A C create c1 ? {c1 info precedence} "::C ::nx::Object" # ::A is an implied class c1 object mixins set B ? {c1 info precedence} "::B ::A ::C ::nx::Object" ? {c1 info lookup mixins} "::B ::A" # ::A is as well implied by ::PCM C mixins set PCM ? {C info heritage} "::PCM ::A ::nx::Object" ? {C info mixins} "::PCM" ? {C info mixins -order} "" ;# ???? why no warning ? {C info mixins -heritage} "::PCM ::A" ? {C info mixins -closure} "::PCM" # ::A is not ordered after ::B but after ::PCM ? {c1 info precedence} "::B ::PCM ::A ::C ::nx::Object" ? {c1 info lookup mixins} "::B ::PCM ::A" } # # transitive per-class mixins with implied classes # nx::test case info-heritage-transitive-pcm { Class create A Class create B -superclass A Class create C -superclass B Class create PCMA -superclass A Class create PCMB -superclass PCMA Class create PCMC -superclass PCMB Class create TPCMA Class create TPCMB -superclass TPCMA C create c1 ? {C info heritage} "::B ::A ::nx::Object" ? {c1 info precedence} "::C ::B ::A ::nx::Object" B mixins set PCMB # heritage includes implied classes ? {C info heritage} "::PCMB ::PCMA ::B ::A ::nx::Object" # precedence includes implied classes from mixins or intrinsic # classes ? {c1 info precedence} "::PCMB ::PCMA ::C ::B ::A ::nx::Object" # just the classes mixed explicitly into this class ? {B info mixins} "::PCMB" ? {C info mixins} "" # the classes mixed transitive into this class; This answer the # question, what classes were mixed in explicitly into the mixin # hierarchy by the application program ? {B info mixins -closure} "::PCMB" # since C is a specialization of B, it includes transitively B's closure ? {C info mixins -closure} "::PCMB" # the explicit and implicit mixins ? {B info mixins -heritage} "::PCMB ::PCMA ::A" # since C is a specialization of B, it inherits the classes from B ? {C info mixins -heritage} "::PCMB ::PCMA ::A" PCMB mixins set TPCMB # heritage includes implied classes ? {C info heritage} "::TPCMB ::TPCMA ::PCMB ::PCMA ::B ::A ::nx::Object" # precedence includes implied classes from mixins or intrinsic # classes ? {c1 info precedence} "::TPCMB ::TPCMA ::PCMB ::PCMA ::C ::B ::A ::nx::Object" # just the classes mixed explicitly into this class ? {B info mixins} "::PCMB" ? {C info mixins} "" # the classes mixed transitive into this class ? {B info mixins -closure} "::PCMB ::TPCMB" # since C is a specialization of B, it includes transitively B's closure ? {C info mixins -closure} "::PCMB ::TPCMB" # the explicit and implicit mixins ? {B info mixins -heritage} "::TPCMB ::TPCMA ::PCMB ::PCMA ::A" # since C is a specialization of B, it inherits the classes from B ? {C info mixins -heritage} "::TPCMB ::TPCMA ::PCMB ::PCMA ::A" C mixins set PCMC # heritage includes implied classes ? {C info heritage} "::PCMC ::TPCMB ::TPCMA ::PCMB ::PCMA ::B ::A ::nx::Object" # precedence includes implied classes from mixins or intrinsic # classes ? {c1 info precedence} "::PCMC ::TPCMB ::TPCMA ::PCMB ::PCMA ::C ::B ::A ::nx::Object" # just the classes mixed explicitly into this class ? {B info mixins} "::PCMB" ? {C info mixins} "::PCMC" # the classes mixed transitive into this class ? {B info mixins -closure} "::PCMB ::TPCMB" ? {C info mixins -closure} "::PCMC ::TPCMB ::PCMB" # the explicit and implicit mixins ? {B info mixins -heritage} "::TPCMB ::TPCMA ::PCMB ::PCMA ::A" ? {C info mixins -heritage} "::PCMC ::TPCMB ::TPCMA ::PCMB ::PCMA ::A" } # # ::nsf::method::ishandle # nx::test case method-isregistered { ? {::nsf::method::registered c} "" ? {::nsf::method::registered info} "" ? {::nsf::method::registered ::info} "" Class create C { :method bar {} {return bar} set h1 [:info method registrationhandle bar] ? [list set _ $h1] "::nsf::classes::C::bar" ? [list [self] info method registrationhandle bar] "::nsf::classes::C::bar" ? [list ::nsf::method::registered $h1] ::C :object method bar {} {return bar} set h2 [:info object method registrationhandle bar] ? [list [self] info object method registrationhandle bar] "::C::bar" ? [list ::nsf::method::registered $h2] ::C } Object create o { :object method bar {} {return bar} set h1 [:info object method registrationhandle bar] ? [list set _ $h1] "::o::bar" ? [list [self] info object method registrationhandle bar] "::o::bar" ? [list ::nsf::method::registered $h1] ::o } } # # Testing "... info method orgin ..." (in contrast to "... info method # handle ..."). "origin" always points to the definintion handle, # "handle" alone is the registration handle. # nx::test case method-origin { nx::Class create C ? {set implHandle [C public method "foo bar" {x} {;}]} "::C::slot::__foo::bar" ? {set regHandle [C info method registrationhandle "foo bar"]} "::nsf::classes::C::foo bar" ? {set origin [C info method definitionhandle "foo bar"]} "::C::slot::__foo::bar" ? {set implHandle [C public object method "foo bar" {x} {;}]} "::C::foo::bar" ? {set regHandle [C info object method registrationhandle "foo bar"]} "::C::foo bar" ? {set origin [C info object method definitionhandle "foo bar"]} "::C::foo::bar" Object create o ? {set implHandle [o public object method "foo bar" {x} {;}]} "::o::foo::bar" ? {set regHandle [o info object method registrationhandle "foo bar"]} "::o::foo bar" ? {set origin [o info object method definitionhandle "foo bar"]} "::o::foo::bar" } # # test "info methods -closure" # nx::test case info-methods-closure { nx::Class create C { :public method c1 {} {...} :method c2 {} {...} } nx::Class create D -superclass C { :public method c1 {} {...} :public method d1 {} {...} :method d2 {} {...} } nx::Class create M { :public method m1 {} {...} :method m2 {} {...} } ? {D info methods} "c1 d1 d2" # # info methods -closure lists instance methods # ? {D info methods -closure *2} "d2 c2" ? {D info methods -closure -source application} "c1 d1 d2 c2" D mixins set M # # Check as well methods inherited from per-class mixins # ? {D info methods} "c1 d1 d2" ? {D info methods -closure *2} "m2 d2 c2" ? {D info methods -closure -source application} "m1 m2 c1 d1 d2 c2" } # # Test error messages within an ensemble call # nx::test case error-in-ensemble { ? {nx::Object info method definition foo 1} {wrong # args: should be "definition name"} ? {nx::Object info subclasses I R G H} {invalid argument 'R', maybe too many arguments; should be "::nx::Object info subclasses ?-closure? ?-dependent? ?/pattern/?"} } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/info-variable.test000066400000000000000000000227171242365656200156630ustar00rootroot00000000000000# -*- Tcl -*- package req nx package req nx::test # parameter/variable info reform # # /cls/ info configure parameters ?pattern? -> list of params # /cls/ info configure syntax -> syntax output # # /cls/ info method parameters /methodname/ ?/pattern/? -> list of params # /cls/ info method syntax /methodname/ -> syntax output # /cls/ info variables ?/pattern/? -> list of variable handles # # /obj/ info object method parameters /methodname/ ?/pattern/? -> list of params # /obj/ info object method syntax /methodname/ -> syntax output # /obj/ info object variables ?/pattern/? -> list of variable handles # # /obj/ info lookup configure parameters ?/pattern/? -> list of params # /obj/ info lookup configure syntax -> syntax output # /obj/ info lookup variables ?/pattern/? -> list of variable handles # # Context-free: work on any object, would not need object. # /obj/ info parameter list|name|syntax /param/ -> value # /obj/ info variable definition|name|parameter /handle/ -> value # nx::test case configure-parameters { nx::Class create Person { :property name :property age:integer :public method foo {-force:switch age:integer {name ""}} {return $age} } ? {join [Person info lookup parameters create] \n} \ "objectName -age:integer -name -object-mixins:mixinreg,slot=::nx::Object::slot::object-mixins,slotset,method=object-mixin,0..n -object-filters:filterreg,slot=::nx::Object::slot::object-filters,slotset,method=object-filter,0..n -class:class,alias,method=::nsf::methods::object::class __initblock:cmd,optional,nodashalnum" ? {Person info lookup parameters create age} "-age:integer" ? {Person info lookup parameters create {*a[gs]*}} "-age:integer -class:class,alias,method=::nsf::methods::object::class" ? {Person info lookup syntax create} "/objectName/ ?-age /integer/? ?-name /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" ? {Person info method parameters foo} {-force:switch age:integer {name ""}} ? {Person info method parameters foo force} "-force:switch" ? {Person info method parameters foo {*a[gm]*}} {age:integer {name ""}} ? {Person info method syntax foo} "/cls/ foo ?-force? /age/ ?/name/?" #? {Person info parameter syntax age:integer} "/age/" #? {Person info parameter syntax -force:switch} "?-force?" #? {Person info parameter name "a b"} "a" ? {nsf::parameter::info syntax age:integer} "/age/" ? {nsf::parameter::info syntax -force:switch} "?-force?" ? {nsf::parameter::info name "a b"} "a" ? {lmap p [Person info lookup parameters create] {nsf::parameter::info default $p}} "0 0 0 0 0 0 0" ? {lmap p [Person info method parameters foo] {nsf::parameter::info default $p}} "1 0 1" nx::Class create Bar { :property {p 9} } nx::Class create Foo -superclass Bar { :property a:integer :property {b:integer 123} :variable c 456 :variable d:lower abc :variable -accessor public e:lower efg :property -accessor private {p 19} :property -accessor protected q :property -incremental i :public method m {} {: -local p get} :create f1 } ? {lmap p [Foo info lookup parameters create] {nsf::parameter::info name $p}} \ "objectName i a b p object-mixins object-filters class __initblock" ? {lmap p [Foo info lookup parameters create] {nsf::parameter::info default $p}} \ "0 0 0 1 1 0 0 0 0" ? {lmap p [Foo info lookup parameters create] {nsf::parameter::info type $p}} \ "{} {} integer integer {} mixinreg filterreg class {}" ? {join [lsort [::Foo info slots]] \n} \ "::Foo::slot::____Foo.p ::Foo::slot::a ::Foo::slot::b ::Foo::slot::c ::Foo::slot::d ::Foo::slot::e ::Foo::slot::i ::Foo::slot::q" ? {::Foo info lookup parameters create ?} "-i:1..n -a:integer {-b:integer 123} {-p 9}" ? {::Foo::slot::b definition} "::Foo property -accessor none {b:integer 123}" ? {::Foo::slot::i definition} "::Foo property -accessor public -incremental i:1..n" ? {::Foo::slot::____Foo.p definition} "::Foo variable -accessor private p 19" ? {::Foo::slot::d definition} "::Foo variable -accessor none d:lower abc" ? {::Foo::slot::e definition} "::Foo variable -accessor public e:lower efg" ? {::Foo::slot::q definition} "::Foo variable -accessor protected q" ? {join [lsort [::f1 info lookup slots]] \n} \ "::Bar::slot::p ::Foo::slot::____Foo.p ::Foo::slot::a ::Foo::slot::b ::Foo::slot::c ::Foo::slot::d ::Foo::slot::e ::Foo::slot::i ::Foo::slot::q ::nx::Object::slot::__initblock ::nx::Object::slot::class ::nx::Object::slot::object-filters ::nx::Object::slot::object-mixins" # get the configure value from p and the value of the private property via m ? {f1 cget -p} 9 ? {f1 m} 19 ? {lsort [f1 info vars]} "__private b c d e p" #package require nx::serializer #puts stderr [::Foo::slot::____Foo.p serialize] ? {llength [::f1 info lookup variables]} 9 ? {join [lsort [::f1 info lookup variables]] \n} \ "::Bar::slot::p ::Foo::slot::____Foo.p ::Foo::slot::a ::Foo::slot::b ::Foo::slot::c ::Foo::slot::d ::Foo::slot::e ::Foo::slot::i ::Foo::slot::q" # One can get 2 values for "lookup variables p"; the private one an # the non-private, since both have the same name. this is necessary # to obtain e.g. the definition of the private slot. ? {lsort [::f1 info lookup variables p]} "::Bar::slot::p ::Foo::slot::____Foo.p" ? {llength [::Foo info variables]} 8 ? {join [lsort [::Foo info variables]] \n} \ "::Foo::slot::____Foo.p ::Foo::slot::a ::Foo::slot::b ::Foo::slot::c ::Foo::slot::d ::Foo::slot::e ::Foo::slot::i ::Foo::slot::q" ? {::Foo info variables p} "::Foo::slot::____Foo.p" ? {::Foo info slots p} "::Foo::slot::____Foo.p" set ::vs [lsort [::Foo info variables]] ? {join [lmap handle $::vs {::Foo info variable definition $handle}] \n} \ "::Foo variable -accessor private p 19 ::Foo property -accessor none a:integer ::Foo property -accessor none {b:integer 123} ::Foo variable -accessor none c 456 ::Foo variable -accessor none d:lower abc ::Foo variable -accessor public e:lower efg ::Foo property -accessor public -incremental i:1..n ::Foo variable -accessor protected q" set ::ps [lmap handle $::vs {::Foo info variable parameter $handle}] ? {join $::ps \n} \ "p 19 a:integer b:integer 123 c 456 d:lower abc e:lower efg i:1..n q" ? {lmap handle $::vs {::Foo info variable name $handle}} \ "__private(::Foo,p) a b c d e i q" ? {lmap handle $::ps {nsf::parameter::info name $handle}} \ "p a b c d e i q" ? {lmap handle $::ps {nsf::parameter::info default $handle}} \ "1 0 1 1 1 1 0 0" ? {lmap handle $::ps {nsf::parameter::info type $handle}} \ "{} integer integer {} lower lower {} {}" ? {nsf::parameter::info default "b:integer 123" ::var1} "1" ? {set ::var1} "123" ? {nsf::parameter::info default "b:integer 123" ::var2} "1" ? {set ::var2} "123" } nx::test case object-variables { nx::Class create Bar { :property {p 9} } nx::Class create Foo -superclass Bar { :property a:integer :property {b:integer 123} :variable c 456 :variable d:lower abc :variable -accessor public e:lower efg :property -accessor private {p 19} :property -accessor protected q :property -incremental i :public method m {} {: -local p} :create f1 } Foo create f2 { :object property oa:integer :object property {ob:integer 123} :object variable oc 456 ;# NO slot :object variable od:lower abc ;# NO slot :object variable -accessor public oe:lower efg :object property -incremental oi :object property -accessor private {op 19} :object property -accessor protected oq :public object method om {} {: -local p} } set ::ovs [lsort [::f2 info object variables]] ? {llength $::ovs} "6" ;# oc, od missing ? {join $::ovs "\n"} \ "::f2::per-object-slot::____f2.op ::f2::per-object-slot::oa ::f2::per-object-slot::ob ::f2::per-object-slot::oe ::f2::per-object-slot::oi ::f2::per-object-slot::oq" ? {join [lmap handle $::ovs {::f2 info variable definition $handle}] \n} \ "::f2 object variable -accessor private op 19 ::f2 object property -accessor none oa:integer ::f2 object property -accessor none {ob:integer 123} ::f2 object variable -accessor public oe:lower efg ::f2 object property -accessor public -incremental oi:1..n ::f2 object variable -accessor protected oq" ? {lmap handle $::ovs {::f2 info variable parameter $handle}} \ "{op 19} oa:integer {ob:integer 123} {oe:lower efg} oi:1..n oq" ? {lmap handle $::ovs {::f2 info variable name $handle}} \ "__private(::f2,op) oa ob oe oi oq" set ::ovs [lsort [::f2 info lookup variables]] ? {llength $::ovs} "15" ;# oc, od missing ? {join $::ovs "\n"} \ "::Bar::slot::p ::Foo::slot::____Foo.p ::Foo::slot::a ::Foo::slot::b ::Foo::slot::c ::Foo::slot::d ::Foo::slot::e ::Foo::slot::i ::Foo::slot::q ::f2::per-object-slot::____f2.op ::f2::per-object-slot::oa ::f2::per-object-slot::ob ::f2::per-object-slot::oe ::f2::per-object-slot::oi ::f2::per-object-slot::oq" # redefine property a on the object level ::f2 object property -accessor none a:integer set ::ovs [lsort [::f2 info lookup variables]] ? {llength $::ovs} "15" ;# oc, od missing ? {join $::ovs "\n"} \ "::Bar::slot::p ::Foo::slot::____Foo.p ::Foo::slot::b ::Foo::slot::c ::Foo::slot::d ::Foo::slot::e ::Foo::slot::i ::Foo::slot::q ::f2::per-object-slot::____f2.op ::f2::per-object-slot::a ::f2::per-object-slot::oa ::f2::per-object-slot::ob ::f2::per-object-slot::oe ::f2::per-object-slot::oi ::f2::per-object-slot::oq" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/interceptor-slot.test000066400000000000000000000437111242365656200164570ustar00rootroot00000000000000# -*- Tcl -*- package require nx::test nx::Class create M { :method mfoo {} {puts [self proc]} } nx::Class create M2 nx::Class create C C create c1 # # test mixin method # nx::test case mixin-method { ? {C info lookup method mixins} "::nsf::classes::nx::Class::mixins" ? {C mixins set M} ::M ? {C info precedence} "::nx::Class ::nx::Object" ? {C mixins get} "::M" ? {C info mixins} "::M" ? {c1 info precedence} "::M ::C ::nx::Object" ? {C mixins add M2} "::M2 ::M" ? {c1 info precedence} "::M2 ::M ::C ::nx::Object" ? {C mixins delete M2} "::M" ? {c1 info precedence} "::M ::C ::nx::Object" ? {C mixins delete M} "" ? {C info mixins} "" ? {C mixins set ::M} "::M" ? {C mixins clear} "::M" ? {C info mixins} "" ? {C mixins add ::M} "::M" ? {C mixins set {}} "" ? {C info mixins} "" } # # test nsf::mixin interface # nx::test case nsf-mixin { ? {::nsf::mixin C ::M} "::M" ? {C info mixins} "::M" ? {::nsf::mixin C ::M2} "::M2 ::M" ? {C info mixins} "::M2 ::M" ? {::nsf::mixin C ""} "" ? {C info mixins} "" } # # per-object mixins # nx::test case per-object-mixins { ? {c1 info precedence} "::C ::nx::Object" ? {c1 object mixins add M} ::M ? {::nsf::relation::get c1 object-mixin} ::M ? {catch {c1 object mixin UNKNOWN}} 1 ? {::nsf::relation::get c1 object-mixin} "::M" # add again the same mixin ? {c1 object mixins add M} {::M} ? {c1 info precedence} "::M ::C ::nx::Object" ? {c1 object mixins add M2} "::M2 ::M" ? {c1 info precedence} "::M2 ::M ::C ::nx::Object" ? {c1 object mixins delete M} "::M2" ? {c1 info precedence} "::M2 ::C ::nx::Object" ? {c1 object mixins delete M2} "" ? {c1 info precedence} "::C ::nx::Object" ? {c1 object mixins add M} {::M} ? {c1 info object mixins} {::M} ? {c1 object mixins clear} {::M} ? {c1 info object mixins} {} } # # adding, removing per-object mixins for classes through relation # "object-mixin" # nx::test case object-mixin-relation { ? {::nsf::relation::set C object-mixin M} ::M ? {C info precedence} "::M ::nx::Class ::nx::Object" ? {C info object mixins} "::M" ? {::nsf::relation::set C object-mixin ""} "" ? {C info precedence} "::nx::Class ::nx::Object" } # # adding, removing per-object mixins for classes through slot # "object-mixin" # # C object-mixin M # ? {C info precedence} "::M ::nx::Class ::nx::Object" # ? {C info object mixins} "::M" # C object-mixin "" # ? {C info precedence} "::nx::Class ::nx::Object" # # add and remove object mixin for classes via modifier "object" and # "mixin" # nx::test case class+mixin { ? {C object mixins set M} ::M ? {C info precedence} "::M ::nx::Class ::nx::Object" ? {C info object mixins} "::M" ? {C object mixins set ""} "" ? {C info precedence} "::nx::Class ::nx::Object" } # # add and remove object mixin for classes via object mixin add # nx::test case class+mixin-add { ? {C object mixins add M} ::M ? {C info precedence} "::M ::nx::Class ::nx::Object" ? {C info object mixins} "::M" ? {C object mixins set ""} "" ? {C info precedence} "::nx::Class ::nx::Object" ? {C object mixins add M} ::M ? {C info precedence} "::M ::nx::Class ::nx::Object" ? {::nsf::relation::get C object-mixin} ::M ? {catch {C object mixins add UNKNOWN}} 1 ? {::nsf::relation::get C object-mixin} "::M" ? {C object mixins set ""} "" ? {C info precedence} "::nx::Class ::nx::Object" ? {C object mixins set M} ::M ? {C info precedence} "::M ::nx::Class ::nx::Object" # forwarder with get ? {C object mixins get} "::M" } nx::test case mixin-add { nx::Class create M1 { :method mfoo {} {puts [current method]} } nx::Class create M11 nx::Class create C1 ? {C1 info lookup method mixins} "::nsf::classes::nx::Class::mixins" C1 object mixins set M1 ? {C1 info precedence} "::M1 ::nx::Class ::nx::Object" C1 create c11 ? {c11 info precedence} "::C1 ::nx::Object" C1 object mixins add M11 ? {C1 info precedence} "::M11 ::M1 ::nx::Class ::nx::Object" Object create o -object-mixins M1 ? {o info precedence} "::M1 ::nx::Object" nx::Class create O O object mixins set M1 ? {O info precedence} "::M1 ::nx::Class ::nx::Object" nx::Class create O -object-mixins M1 ? {O info precedence} "::M1 ::nx::Class ::nx::Object" } nx::test case filter-relation { nx::Class create CC { :public method filterA args {next} :public method filterB args {next} :public object method filterC args {next} :create cc { :public object method filterD args {next} } } ? {::nsf::relation::get cc object-filter} "" ? {cc info object filters} "" ? {::nsf::relation::set cc object-filter filterA} filterA ? {cc info object filters} "filterA" ? {cc object filters set filterB} "filterB" ? {::nsf::relation::get cc object-filter} "filterB" ? {cc info object filters} "filterB" ? {cc object filters add filterD} "filterD filterB" ? {::nsf::relation::get cc object-filter} "filterD filterB" ? {cc info object filters} "filterD filterB" ? {cc object filters delete filterB} "filterD" ? {::nsf::relation::get cc object-filter} "filterD" ? {cc info object filters} "filterD" ? {catch {::nsf::relation::set cc object-filter UNKNOWN}} 1 ? {::nsf::relation::get cc object-filter} "filterD" ? {cc info object filters} "filterD" ? {::nsf::relation::get CC object-filter} "" ? {CC info object filters} "" ? {::nsf::relation::set CC object-filter filterC} "filterC" ? {::nsf::relation::get CC object-filter} "filterC" ? {CC info object filters} "filterC" ? {CC object filters clear} "filterC" ? {::nsf::relation::get CC object-filter} "" ? {CC info object filters} "" ? {::nsf::relation::get CC class-filter} "" ? {CC info filters} "" ? {::nsf::relation::set CC class-filter filterA} "filterA" ? {::nsf::relation::get CC class-filter} "filterA" ? {CC info filters} "filterA" ? {CC filters add filterB} "filterB filterA" ? {::nsf::relation::get CC class-filter} "filterB filterA" ? {CC info filters} "filterB filterA" ? {CC filters delete filterA} "filterB" ? {::nsf::relation::get CC class-filter} "filterB" ? {CC info filters} "filterB" ? {catch {::nsf::relation::set CC class-filter UNKNOWN}} 1 ? {::nsf::relation::get CC class-filter} "filterB" ? {CC info filters} "filterB" ? {CC filters clear} "filterB" ? {::nsf::relation::get CC class-filter} "" ? {CC info filters} "" } nx::test configure -count 3 nx::test case "filter-and-creation" { nx::Class create Foo { :method myfilter {args} { set i [::incr ::count] set s [self] set m [current calledmethod] #puts stderr "$i: $s.$m" #puts stderr "$i: procsearch before [$s procsearch info]" set r [next] #puts stderr "$i: $s.$m got ($r)" #puts stderr "$i: $s.$m procsearch after [$s info lookup method info]" return $r } # method for testing next to non-existing shadowed method :public method baz {} {next} } ? {Foo create ob} ::ob # make sure, no unknown handler exists #? {::ob info lookup method unknown} "::nsf::classes::nx::Object::unknown" ? {::ob info lookup method unknown} "" ? {ob bar} {::ob: unable to dispatch method 'bar'} ? {ob baz} {} # define a global unknown handler ::nx::Object protected method unknown {m args} { error "[::nsf::current object]: unable to dispatch method '$m'" } ? {ob bar} {::ob: unable to dispatch method 'bar'} ? {ob baz} {} Foo filters set myfilter # create through filter ? {Foo create ob} ::ob # unknown through filter ? {ob bar1} {::ob: unable to dispatch method 'bar1'} ? {ob baz} {} # deactivate nx unknown handler in case it exists ::nx::Object method unknown {} {} # create through filter ? {Foo create ob2} ::ob2 # unknown through filter ? {ob2 bar2} {::ob2: unable to dispatch method 'bar2'} ? {ob2 baz} {} # create with filter ? {Foo create ob3 -object-filters myfilter} ::ob3 } nx::test configure -count 1 # # Test the next-path with just intrinsic classes in cases where a # method handle is used for method dispatch # nx::test case intrinsic+method-handles { nx::Class create A {:public method foo {} {return "A [next]"}} nx::Class create B -superclass A {:public method foo {} {return "B [next]"}} nx::Class create C -superclass B {:public method foo {} {return "C [next]"}} C create c1 ? {c1 foo} "C B A " ? {c1 [C info method definitionhandle foo]} "C B A " ? {c1 [B info method definitionhandle foo]} "B A " ? {c1 [A info method definitionhandle foo]} "A " # we expect same results via dispatch with fully qualified names ? {nsf::dispatch c1 foo} "C B A " ? {nsf::dispatch c1 [C info method definitionhandle foo]} "C B A " ? {nsf::dispatch c1 [B info method definitionhandle foo]} "B A " ? {nsf::dispatch c1 [A info method definitionhandle foo]} "A " # # check, whether the context of "my -local" is correct # A public method bar {} {nsf::my -local foo} B public method bar {} {nsf::my -local foo} C public method bar {} {nsf::my -local foo} ? {c1 bar} "C B A " ? {c1 [C info method definitionhandle bar]} "C B A " ? {c1 [B info method definitionhandle bar]} "B A " ? {c1 [A info method definitionhandle bar]} "A " } # # Test the next-path with mixins in cases where a # method handle is used for method dispatch # nx::test case mixins+method-handles { # # Just mixins # nx::Class create A {:public method foo {} {return "A [next]"}} nx::Class create B {:public method foo {} {return "B [next]"}} nx::Class create C {:public method foo {} {return "C [next]"}} nx::Class create X -mixin {C B A} X create c1 ? {c1 foo} "C B A " ? {c1 [C info method definitionhandle foo]} "C B A " ? {c1 [B info method definitionhandle foo]} "B A " ? {c1 [A info method definitionhandle foo]} "A " # # Intrinsic classes and mixins # nx::Class create Y {:public method foo {} {return "Y [next]"}} nx::Class create Z -superclass Y {:public method foo {} {return "Z [next]"}} Z create c1 -object-mixins {C B A} ? {c1 foo} "C B A Z Y " ? {c1 [C info method definitionhandle foo]} "C B A Z Y " ? {c1 [B info method definitionhandle foo]} "B A Z Y " ? {c1 [A info method definitionhandle foo]} "A Z Y " ? {c1 [Z info method definitionhandle foo]} "Z Y " ? {c1 [Y info method definitionhandle foo]} "Y " # # check, whether the context of "my -local" is correct # A public method bar {} {nsf::my -local foo} B public method bar {} {nsf::my -local foo} C public method bar {} {nsf::my -local foo} Y public method bar {} {nsf::my -local foo} Z public method bar {} {nsf::my -local foo} ? {c1 bar} "C B A Z Y " ? {c1 [C info method definitionhandle bar]} "C B A Z Y " ? {c1 [B info method definitionhandle bar]} "B A Z Y " ? {c1 [A info method definitionhandle bar]} "A Z Y " ? {c1 [Z info method definitionhandle bar]} "Z Y " ? {c1 [Y info method definitionhandle bar]} "Y " } # # Test the next-path with mixins in cases where a # method handle is used for method dispatch # nx::test case mixins+method-handles+intrinsic { # # Just mixins # nx::Class create A {:public method foo {} {return "A [next]"}} nx::Class create B {:public method foo {} {return "B [next]"}} nx::Class create C {:public method foo {} {return "C [next]"}} nx::Class create X -mixin {C B A} { :public method foo {} {return "X [next]"} } X create c1 ? {c1 foo} "C B A X " ? {nsf::dispatch c1 -intrinsic foo} "X " # # Intrinsic classes and mixins # nx::Class create Y {:public method foo {} {return "Y [next]"}} nx::Class create Z -superclass Y {:public method foo {} {return "Z [next]"}} Z create c1 -object-mixins {C B A} ? {c1 foo} "C B A Z Y " ? {nsf::dispatch c1 -intrinsic foo} "Z Y " # # check, whether the context of "my -intrinsic" is correct # A public method bar {} {nsf::my -intrinsic foo} B public method bar {} {nsf::my -intrinsic foo} C public method bar {} {nsf::my -intrinsic foo} Y public method bar {} {nsf::my -intrinsic foo} Z public method bar {} {nsf::my -intrinsic foo} ? {c1 info precedence} "::C ::B ::A ::Z ::Y ::nx::Object" ? {c1 bar} "Z Y " ? {c1 [C info method definitionhandle bar]} "Z Y " ? {c1 [B info method definitionhandle bar]} "Z Y " ? {c1 [A info method definitionhandle bar]} "Z Y " ? {c1 [Z info method definitionhandle bar]} "Z Y " ? {c1 [Y info method definitionhandle bar]} "Z Y " # # check, whether the context of "nsf::dispatch [self] -intrinsic" is correct # A public method bar {} {nsf::dispatch [self] -intrinsic foo} B public method bar {} {nsf::dispatch [self] -intrinsic foo} C public method bar {} {nsf::dispatch [self] -intrinsic foo} Y public method bar {} {nsf::dispatch [self] -intrinsic foo} Z public method bar {} {nsf::dispatch [self] -intrinsic foo} ? {c1 bar} "Z Y " ? {c1 [C info method definitionhandle bar]} "Z Y " ? {c1 [B info method definitionhandle bar]} "Z Y " ? {c1 [A info method definitionhandle bar]} "Z Y " ? {c1 [Z info method definitionhandle bar]} "Z Y " ? {c1 [Y info method definitionhandle bar]} "Z Y " } # # Test filter guards (define filter and guard separtely) # nx::test case filter-guard-separately { # # Define a room with occupancy and methods for entering and leaving # nx::Class create Room { :property name :variable occupancy:integer 0 :public method enter {name} {incr ::occupancy} :public method leave {name} {incr ::occupancy -1} # # We are interested, what happens with the room, so we define a # logging filter.... # :method loggingFilter args { lappend ::_ [current calledmethod] next } # # ... and we register it. # :filters add loggingFilter } set ::_ {} ? {Room create r} ::r r enter Uwe r leave Uwe r configure -name "Office" ? {set ::_} "__object_configureparameter init enter leave configure" # # Hmm, we not so much interested on all these calls. Just the # "enter" and "leave" operations are fine. We could have certainly # as well mixin for these two methods, but the guards are more # general since the can as well trigger on arbitrary patterns. # Room filters guard loggingFilter { [current calledmethod] in {enter leave} } r destroy set ::_ {} ? {Room create r} ::r r enter Uwe r leave Uwe r configure -name "Office" ? {set ::_} "enter leave" r destroy # Now we define a subclass DangerRoom, which refines the filter by # logging into a "dangerRoomLog". We want here entries for all # operations. set ::_ {} set ::dangerRoomLog {} nx::Class create DangerRoom -superclass Room { :method loggingFilter args { lappend ::dangerRoomLog [current calledmethod] next } :filters add loggingFilter } ? {DangerRoom create d} ::d d enter Uwe d leave Uwe d configure -name "Safe Room" ? {set ::_} "enter leave" ? {expr [llength $::dangerRoomLog] > 2} 1 d destroy } # # Test filter guards (define filter together with guard) # nx::test case filter-guard-separately { # # Define a room with occupancy and methods for entering and leaving # nx::Class create Room { :property name :variable occupancy:integer 0 :public method enter {name} {incr ::occupancy} :public method leave {name} {incr ::occupancy -1} # # We are interested, what happens with the room, so we define a # logging filter.... # :method loggingFilter args { lappend ::_ [current calledmethod] next } # # ... and we register it together with a guard. # :filters add {loggingFilter -guard { [current calledmethod] in {enter leave} }} } set ::_ {} ? {Room create r} ::r r enter Uwe r leave Uwe r configure -name "Office" ? {set ::_} "enter leave" ? {r info lookup filters} "::nsf::classes::Room::loggingFilter" ? {r info lookup filters -guards} {{loggingFilter -guard { [current calledmethod] in {enter leave} }}} # Now we define a subclass DangerRoom, which refines the filter by # logging into a "dangerRoomLog". We want here entries for all # operations. set ::_ {} set ::dangerRoomLog {} nx::Class create DangerRoom -superclass Room { :method loggingFilter args { lappend ::dangerRoomLog [current calledmethod] next } :filters add loggingFilter } ? {DangerRoom create d} ::d d enter Uwe d leave Uwe d configure -name "Safe Room" ? {set ::_} "enter leave" ? {expr [llength $::dangerRoomLog] > 2} 1 ? {d info lookup filters} "::nsf::classes::DangerRoom::loggingFilter ::nsf::classes::Room::loggingFilter" d destroy } # # Test info lookup mixins (with guards) # nx::test case filter-guard-separately { nx::Class create M1 nx::Class create M2 nx::Class create M3 nx::Class create C nx::Class create D -superclass C D create d1 -object-mixins M1 ? {d1 info lookup mixins} ::M1 D mixins add {M2 -guard 1} ? {d1 info lookup mixins} "::M1 ::M2" C mixins add M3 ? {d1 info lookup mixins} "::M1 ::M2 ::M3" ? {d1 info lookup mixins -guards} "::M1 {::M2 -guard 1} ::M3" ? {d1 info lookup mixins -guards *2*} "{::M2 -guard 1}" d1 object mixins clear ? {d1 info lookup mixins} "::M2 ::M3" } # # Test potential confusion in case a class has a space in its name # when registering methods or mixins. # nx::test case space-in-classname { nx::Class create M1 { :public method foo {} {return "[next] [current class]"} } # # Define a class with a space in its name, containing a method. This # class will be used as a mixin class later on. # nx::Class create "M1 b" -superclass M1 { :public method foo {} {return next-[current class]} } nx::Class create C { :public method foo {} {return foo} :create c1 } # Test the base case ? {c1 foo} foo # Add spacy class as a mixin. Check, if the introspection returns # sensible values. ? {C mixins add "M1 b"} "{::M1 b}" ? {C info mixins} "{::M1 b}" ? {M1 info mixins} "" ? {M1 info mixinof} "" ? {"M1 b" info mixins} "" # check the result of the mixin class ? {c1 foo} "next-::M1 b" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/interp.test000066400000000000000000000462001242365656200144370ustar00rootroot00000000000000# -*- Tcl -*- package req nx::test proc traceStderr args { puts ">>> traceStderr HA! $args" } nx::test case hidden-cmds { global i # # Create a slave interp for testing # set i [interp create] # # Some baseline # $i eval { proc foo {} {;} } $i hide foo ? {$i eval [list info commands ::nx::Object]} "" $i eval [list package req nx] ? {$i eval [list info commands ::nx::Object]} ::nx::Object # # Tcl's hiding mechansim only applies to objects/classes in the # top-level namespace. So any non-globally namespaced ones and # nested objects are not concerned ... # $i eval {nx::Object create ::o { :public object method baz {} { return KO } :public object method destroy {} { # # sets a global variable for tracing the processing of the # app-level destructor! # set ::[namespace tail [current object]] [current class] next } }} $i eval {nx::Class create ::C { :public method destroy {} { # # sets a global variable for tracing the processing of the # app-level destructor! # set ::[namespace tail [current object]] [current class] next } :public method bar {} { return OK } }} $i eval {nx::Class create ::M { :public method foo {} { return [current object]-[:info class]-[current class] } }} ? {$i eval {info commands ::o}} ::o ? {$i eval {info commands ::C}} ::C ? {$i eval {info commands ::M}} ::M # # [interp hide] performs a partial and widely silent deletion # (Tcl_HideCommand(); note, while the idea resembles that of a # non-deleting rename, there is no C-level trace available!). The # object's Tcl_command cmdEpoch counter is increased. However, # hiding does not prune the command structure, nor does is the cmd's # client data touched. It is merely re-assigned to another, # interp-wide hash table. The object's command is no valid dispatch # target anymore ... # ? {interp hidden $i} "foo" $i hide o ? {interp hidden $i} "foo o" ? {$i eval ::o} "invalid command name \"::o\"" ? {$i eval {info commands ::o}} "" ? {interp eval $i {::C create ::c}} ::c # set some relationships to test later ... ? {interp eval $i {::C mixins add ::M}} ::M ? {interp eval $i {::C object mixins add ::M}} ::M $i hide C ? {interp eval $i {::C create ::c2}} {invalid command name "::C"} # # However, the object structure is effectively preserved within the # object system and object relations are intact, e.g., the object is # still reported as an instance of a class. # ? {$i eval {nx::Object info instances ::o}} "::o" ? {interp invokehidden $i o ::nsf::methods::object::info::class} "::nx::Object" ? {interp invokehidden $i o info class} "::nx::Object" ? {interp eval $i {c info class}} ::C ? {interp invokehidden $i C info instances ::c} ::c ? {interp invokehidden $i C info mixins} ::M # Note, for all introspections that do *not* try to convert the # Tcl_Obj into an object or a class, but treat it as a pattern (or # the like) we are fine ... ? {$i eval {M info mixinof ::C}} "::C ::C" ? {$i eval {M info mixinof -scope class ::C}} "::C" ? {$i eval {M info mixinof -scope object ::C}} "::C" # dispatch to object-provided method (with the object being hidden) ? {interp eval $i {c bar}} OK # dispatch to class-provided methods (with the class being hidden) ? {interp eval $i {c bar}} OK # dispatch to mixed-in methods (which do basic introspection on the hidden object) ... ? {interp invokehidden $i C foo} ::C-::nx::Class-::M ? {interp eval $i {c foo}} ::c-::C-::M # # 1) Implicit destruction (through NSF's exit handler) # # An important characteristic of a hidden cmd is that it is cleaned # up later than ordinary, exposed (and namespaced) commands; see # DeleteInterpProc(). Hidden commands are processed during a interp # shutdown *after* the exit handler returned! # # For testing, we shutdown the NSF object systems in our slave # interp by using nsf::finalize; to do some smoke testing of the # cleanup results. As for the cleanup procedre, this is equivalent # to: interp delete $i $i eval {nsf::finalize -keepvars} # The destructor of e.g. object o sets a global variable with the # object name. The following test checks therfore, whether the # destructor was executed. # ? {$i eval { info exists ::o }} 1 ? {$i eval {interp hidden}} foo ? {$i eval {info commands ::o}} "" ? {$i eval {info commands ::C}} "" # # Were the app-level destructors called effectively? # ? {$i eval { info exists ::o }} 1 ? {$i eval { set ::o }} "" ? {$i eval { info exists ::c }} 1 ? {$i eval { set ::c }} ::C interp delete $i } # # Explicit destruction # nx::test case hidden-cmds+explicit-delete { global i set i [interp create] $i eval { package req nx nx::Object create ::o2 { :public object method destroy {} { next return ok } }} ? {$i eval {interp hidden}} "" ? {$i eval {info commands ::o2}} ::o2 ? {$i eval {nx::Object info instances ::o2}} ::o2 ? {$i eval {nsf::object::exists ::o2}} 1 $i hide o2 ? {$i eval {interp hidden}} o2 ? {$i eval {info commands ::o2}} "" ? {$i eval {nx::Object info instances ::o2}} ::o2 ? {$i eval {nsf::object::exists ::o2}} 0 ? {interp invokehidden $i o2 destroy} "ok" ? {$i eval {interp hidden}} "" ? {$i eval {nx::Object info instances ::o2}} "" ? {$i eval {info commands ::o2}} "" ? {$i eval {nsf::object::exists ::o2}} 0 } # # hide and re-expose # nx::test case hide-and-re-expose { global i set i [interp create] $i eval { package req nx nx::Object create ::o { :public object method destroy {} { incr ::[namespace tail [current]] return OK } :public object method foo {} { return [list [current object] [current class] [:info class] [[current] info class]] } } interp hide {} o } # Check hidden state ? {interp eval $i {interp hidden}} "o" ? {interp eval $i {info commands ::o}} "" ? {interp eval $i {nx::Object info instances ::o}} ::o ? {interp eval $i {nsf::object::exists ::o}} 0 interp expose $i o # Check re-exposed state ? {interp eval $i {interp hidden}} "" ? {interp eval $i {info commands ::o}} "::o" ? {interp eval $i {nx::Object info instances ::o}} ::o ? {interp eval $i {nsf::object::exists ::o}} 1 # # Is the object "alive"? # ? {$i eval {::o foo}} {::o {} ::nx::Object ::nx::Object} $i eval {nsf::finalize -keepvars} # Was the destructor called? ? {interp eval $i {info exists ::o}} 1 ? {interp eval $i {set ::o}} 1 # Check cleaned-up state ? {interp eval $i {interp hidden}} "" ? {interp eval $i {info commands ::o}} "" interp delete $i } # # hide/re-expose with "command renaming" # nx::test case command-renaming { global i set i [interp create] $i eval { package req nx nx::Object create ::o { :public object method destroy {} { incr ::[namespace tail [current]] return OK } :public object method foo {} { catch {[current] info class} msg return [list [current object] [current class] [:info class] $msg] } } interp hide {} o O } # Check hidden state -> object command renamed ? {interp eval $i {interp hidden}} "O" ? {interp eval $i {info commands ::o}} "" ? {interp eval $i {nx::Object info instances ::o}} ::o ? {interp eval $i {nsf::object::exists ::o}} 0 ? {interp invokehidden $i O foo} \ {::o {} ::nx::Object {invalid command name "::o"}} interp expose $i O OO ? {interp eval $i {OO foo}} \ {::o {} ::nx::Object {invalid command name "::o"}} ? {interp eval $i {interp hidden}} "" ? {interp eval $i {info commands ::o}} "" ? {interp eval $i {info commands ::OO}} ::OO ? {interp eval $i {nx::Object info instances ::o}} ::o ? {interp eval $i {nx::Object info instances ::OO}} ::o ;# should be ""? ? {interp eval $i {nsf::object::exists ::o}} 0 ? {interp eval $i {nsf::object::exists ::OO}} 1 $i eval {nsf::finalize -keepvars} # Was the destructor called? ? {interp eval $i {info exists ::o}} 1 ? {interp eval $i {set ::o}} 1 ? {interp eval $i {interp hidden}} {} ? {interp eval $i {info commands ::o}} {} interp delete $i } # # Rename namespaced object to global one and hide ... # nx::test case namespaced-object { global i set i [interp create] $i eval { package req nx namespace eval ::ns1 { nx::Object create o { :public object method destroy {} { incr ::[namespace tail [current]] return OK } } } } ? {$i hide ::ns1::o} \ {cannot use namespace qualifiers in hidden command token (rename)} $i eval {::rename ::ns1::o ::X} ? {interp eval $i {interp hidden}} {} ? {interp eval $i {info commands ::X}} {::X} ? {interp eval $i {nx::Object info instances ::X}} {::X} ? {interp eval $i {nsf::object::exists ::X}} 1 $i eval {interp hide {} X} ? {interp eval $i {interp hidden}} "X" ? {interp eval $i {info commands ::X}} {} ? {interp eval $i {nx::Object info instances ::X}} {::X} ? {interp eval $i {nsf::object::exists ::X}} 0 $i eval {nsf::finalize -keepvars} ? {interp eval $i {info exists ::X}} 1 ? {interp eval $i {set ::X}} 1 interp delete $i } # # Deletion order # nx::test case deletion-order { global i set i [interp create] $i eval { package req nx nx::Object create ::o { :public object method destroy {} { incr ::[namespace tail [current]] interp invokehidden {} C destroy next } } nx::Class create ::C { :public object method destroy {} { incr ::[namespace tail [current]] next } } } $i hide o $i hide C $i eval {nsf::finalize -keepvars} ? {interp eval $i {info exists ::C}} 1 ? {interp eval $i {set ::C}} 1 ? {interp eval $i {info exists ::o}} 1 ? {interp eval $i {set ::o}} 1 interp delete $i } # # Some stumbling blocks in destructors: [error] in app-level destroy # nx::test case error-in-destroy-1 { global i set i [interp create] $i eval { package req nx nx::Object create ::o { :public object method destroy {} { error BAFF! } } interp hide {} o } ? {interp eval $i {::rename ::o ""}} \ {can't delete "::o": command doesn't exist} ? {interp invokehidden $i o destroy} "BAFF!" ? {interp eval $i {interp hidden}} "o" ? {interp eval $i {info commands ::o}} "" ? {interp eval $i {nx::Object info instances ::o}} "::o" ? {interp eval $i {nsf::object::exists ::o}} 0 $i eval {nsf::finalize} ? {interp eval $i {interp hidden}} "" ? {interp eval $i {info commands ::o}} "" interp delete $i } # # Some stumbling blocks in destructors: [interp hide] in app-level # destroy # nx::test case error-in-destroy-2 { global i set i [interp create] $i eval { package req nx proc ::bar {} { interp hide {} bar; return 1 } nx::Object create ::o { :public object method destroy {} { # # Would not be an issue in safe interps, as [interp hide] & # friends are disallowed ... # set res [catch {interp hide {} o} msg] # # TODO: a simple, uncaught 'interp hide {} o' leads to a lookup issue # and weird error handling; however, the cleanup is not # affected ... # next return OK } } } ? {interp eval $i {::bar}} 1 ? {interp eval $i {::o destroy}} OK ? {interp eval $i {interp hidden}} "bar" ? {interp eval $i {info commands ::o}} "" ? {interp eval $i {nx::Object info instances ::o}} "" ? {interp eval $i {nsf::object::exists ::o}} 0 interp delete $i } # # Some stumbling blocks in destructors: [interp hide] in app-level destroy # nx::test case error-in-destroy-3 { global i set i [interp create] $i eval { package req nx nx::Object create ::o { :public object method destroy {} { catch {::rename [current] ""} msg next return $msg } } interp hide {} o } ? {interp eval $i {::o destroy}} {invalid command name "::o"} ? {interp invokehidden $i o destroy} \ {can't delete "::o": command doesn't exist} ? {interp eval $i {interp hidden}} "" ? {interp eval $i {nx::Object info instances ::o}} "" ? {interp eval $i {info commands ::o}} "" ? {interp eval $i {nsf::object::exists ::o}} 0 interp delete $i } # # see NsfProcAliasMethod(): # Tcl_Command_cmdEpoch(tcd->aliasedCmd) # nx::test case hidden-procs-as-aliases { # # 1) hide alias proc targets # global i set i [interp create] $i eval { package req nx ::proc ::FOO args {return OK} nx::Object create o { :public object alias foo ::FOO } } ? {$i eval {o foo}} OK ? {$i hidden} "" $i hide FOO ? {$i hidden} FOO # # For now, we do not allow to dispatch to hidden proc targets # through their method aliases as this would counteract the idea of # hiding cmds from a (safe) slave interp. As the NSF aliasing works # unrestrictedly in child (safe) interps (as opposed to [interp # invokehidden]), this would derail the essentials of the hiding # mechanism. # ? {$i eval {o foo}} {target "::FOO" of alias foo apparently disappeared} # # When exposing it again (e.g., from the master interp), we can # dispatch again; note this is currently limited to the exposing # under the original command name (!) # $i expose FOO ? {$i hidden} "" ? {$i eval {o foo}} OK $i hide FOO ? {$i hidden} FOO # # Limitation: Currently, exposing a hidden target command under a # *different* name will not re-establish the alias. This is due to # the way NsfProcAliasMethod() is currently implemented: Rebinding # an epoched cmd (which holds for renamed as well as # hidden/re-exposed cmds) is currently based on the command name # stored in the ::nsf::alias array. This metadata store is not # maintained during [interp hide|expose] operations. Using a # pointer-based (reverse) lookup based on tcd->aliasedCmd would be # possible (I did it testwise), but then we would have to revise the # current behaviour of NsfProcAliasMethod() for target proc # renamings also. A non-deleting [rename] currently also interrupts # an alias binding. See the relevant tests on [rename ::foo ::foo2] # in tests/alias.test. To be consistent, and because [interp # hide|expose] is a two-step [rename], technically, we keep the # current behaviour. # $i expose FOO OOF ? {$i hidden} "" ? {$i eval {o foo}} {target "::FOO" of alias foo apparently disappeared} # # Due to the alias-specific lookup scheme (::nsf::alias), we could fix # the alias manually after a command-renaming hide|expose operation: # ? {$i eval {info exists ::nsf::alias(::o,foo,1)}} 1 ? {$i eval {set ::nsf::alias(::o,foo,1)}} "::FOO" ? {$i eval {set ::nsf::alias(::o,foo,1) ::OOF}} "::OOF" ? {$i eval {info commands ::OOF}} ::OOF ? {$i eval {o foo}} OK interp delete $i unset i } nx::test case hidden-objects-as-aliases { # # 2) hide alias object targets # global i set i [interp create] $i eval { package req nx nx::Object create x { :public object method foo {} {return OK} } nx::Object create dongo { :public object alias bar ::x } } # # Objects as intermediary aliases are transparent when being # hidden|exposed; hiding and exposing them (under differing command # names) do not affect the dispatch behaviour; this is due to the # ensemble dispatch strategy ... # ? {$i hidden} "" ? {$i eval {dongo bar foo}} OK $i hide x ? {$i hidden} x #? {$i eval {dongo bar foo}} OK ? {$i eval {dongo bar foo}} {target "::x" of alias bar apparently disappeared} ? {$i eval {x foo}} {invalid command name "x"} ? {$i invokehidden x foo} OK $i expose x ? {$i hidden} "" ? {$i eval {dongo bar foo}} OK ? {$i eval {x foo}} OK $i hide x X ? {$i hidden} X #? {$i eval {dongo bar foo}} OK ? {$i eval {dongo bar foo}} {target "::x" of alias bar apparently disappeared} ? {$i eval {X foo}} {invalid command name "X"} ? {$i invokehidden X foo} OK $i expose X XX ? {$i hidden} "" #? {$i eval {dongo bar foo}} OK ? {$i eval {dongo bar foo}} {target "::x" of alias bar apparently disappeared} ? {$i eval {XX foo}} OK # # Hiding of leaf methods (e.g., ::o::foo) is not an issue because # hiding|exposing is limited to global commands # ? {$i hide ::o::foo} "cannot use namespace qualifiers in hidden command token (rename)" interp delete $i unset i } # # MixinSearchProc() # nx::test case hidden-mixins-procsearch { global i set i [interp create] $i eval { package req nx nx::Object create x { :public object method foo {} {return OK} } nx::Class create M { :public method foo {} { return <[current class]>[next]<[current class]> } } x object mixins set M } ? {$i eval {x foo}} <::M>OK<::M> # # Hiding M as a command should not affect *existing* mixin # relations! # $i hide M ? {$i hidden} M ? {$i eval {x foo}} <::M>OK<::M> "with hidden mixin" $i expose M ? {$i hidden} "" ? {$i eval {x foo}} <::M>OK<::M> "with re-exposed mixin" $i hide M m ? {$i eval {x foo}} <::M>OK<::M> "with hidden mixin (renamed command)" $i expose m MM ? {$i eval {x foo}} <::M>OK<::M> "with re-exposed mixin (renamed command)" # # However, modifying mixin axes is hindered, because of # the underlying relation machinery (::nsf::relation, # RelationSlot->add(), etc.) is relying on exposed command names # (i.e., command and object look-ups based on # Tcl_GetCommandFromToken(), GetObjectFromString() usage). # $i hide MM M $i eval {nx::Class create ::M2} ? {$i eval {x object mixins add M2}} {mixin: expected a class as mixin but got "::M"} ? {$i invokehidden M mixins add M2} {expected object but got "::M" for parameter "object"} interp delete $i unset i } # # MixinComputeOrderFullList() & friends (due to # CmdListRemoveDeleted() # nx::test case hidden-mixins-mixinlists { global i set i [interp create] $i eval { package req nx nx::Object create o nx::Class create M1 nx::Class create M2 nx::Class create M3 o object mixins set {M1 M2} } ? {$i eval {o info precedence}} "::M1 ::M2 ::nx::Object" ? {$i eval {o info object mixins}} {::M1 ::M2} ? {$i hidden} "" $i hide M1 ? {$i hidden} M1 $i eval {M2 mixins add M3} ? {$i eval {o info precedence}} "::M1 ::M3 ::M2 ::nx::Object" # # Now, have the mixin list invalidated; The next time we request the list, # the mixin assembly machinery will have to deal with the hidden # mixin; and properly include it. # # We need to destroy one mixin explicitly (or add one as a per-class # mixin, or the like), as the mixin modification API would stumble # over the hidden mixin object ... # $i eval {::M2 destroy} ? {$i eval {o info precedence}} "::M1 ::nx::Object" ? {$i eval {o info object mixins}} "::M1" ? {$i invokehidden M1 info mixinof} "::o" interp delete $i unset i } nx::test case nsf-interp-basics { global i set i [::nsf::interp create] ? {$i eval {info commands ::nsf::is}} "::nsf::is" ? {interp issafe $i} 0 ? {::nsf::interp create zzz} "zzz" set i [::nsf::interp create -safe] ? {$i eval {info commands ::nsf::is}} "::nsf::is" ? {interp issafe $i} 1 } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/introspection.test000066400000000000000000000023531242365656200160370ustar00rootroot00000000000000# -*- Tcl -*- package req nx::test # # [::nsf::current calledclass] # nx::test case current-calledclass { Object create o { :public method foo {} { return [current calledclass] } } ? {o foo} "" Class create C { :public class method bar {} { return [current calledclass] } :public method foo {} { return [current calledclass] } } ? {[C new] foo} ::C ? {C bar} "" C eval { :public method intercept {} { return @[current calledclass]@ } :filter add intercept } ? {[C new] foo} @::C@ C eval { :filter {} :public method baz {} { return [current calledclass] } } Class create M { :public method baz {} { return [list [current calledclass] [next]] } } C mixins add M ? {[C new] baz} {::C ::C} } # # [::nsf::current calledclass] # nx::test case current-calledmethod { set body { return [list [current nextmethod] {*}[next]] } Object create o set mh [o public method foo {} $body] ? {o foo} {{}} Class create M set mh2 [M public method foo {} $body] M filters add foo o mixin M ? {o foo} [list $mh2 $mh {}] } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/linearization.test000066400000000000000000000103371242365656200160100ustar00rootroot00000000000000package require nx::test proc direct-constraints {obj} { set constraints "" foreach c [$obj info precedence] { set sc [$c info superclasses] # add constraints to ensure that subclass is before superclass foreach super $sc { lappend constraints [list $c < $super] } # maintain order from superclass list if {[llength $sc] == 2} { lappend constraints [list [lindex $sc 0] < [lindex $sc 1]] } elseif {[llength $sc] > 2} { set first [lindex $sc 0] foreach class [lrange $sc 1 end] { lappend constraints [list $first < $class] set first $class } } } return $constraints } proc monotonicity-constraints {obj {linearizer ""}} { set constraints "" foreach c [$obj info precedence] { # compute for every class its own heritage and turn this into constraints if {$linearizer eq ""} { set sc [$c info heritage] } else { puts "call linearizer [list $linearizer $c]" set sc [$linearizer $c] } # maintain order from superclass list if {[llength $sc] == 2} { lappend constraints [list [lindex $sc 0] < [lindex $sc 1]] } elseif {[llength $sc] > 2} { set first [lindex $sc 0] foreach class [lrange $sc 1 end] { lappend constraints [list $first < $class] set first $class } } } return [lsort -unique $constraints] } proc local-order-constraints {obj} { # no class before its subclass set constraints "" foreach c [$obj info precedence] { # compute vor every class its subclasses set subclasses [$c info subclasses -closure] # subclasses must be before classes foreach sc $subclasses { lappend constraints [list $sc < $c] } } return [lsort -unique $constraints] } proc check-constraints {example rule kind list constraints} { #puts "check-constraints $example $rule $kind $list" foreach triple $constraints { lassign $triple x before y set larger [expr {[lsearch -exact $list $x] > [lsearch -exact $list $y]}] ? [list set _ $larger] 0 "$example $rule $kind violated $triple" } #puts "" } nx::test case boat { # # Boat example DHHM 94; # R. Ducournau, M. Habib, M. Huchard, and M.L. Mugnier. Proposal for a Monotonic Multiple Inheritance Linearization. # see: http://www2.lirmm.fr/~ducour/Publis/DHHM-oopsla94.pdf # nx::Class create boat ;# 8 nx::Class create dayboat -superclass boat ;# 6 nx::Class create wheelboat -superclass boat ;# 7 nx::Class create engineless -superclass dayboat ;# 3 nx::Class create pedalwheelboat -superclass {engineless wheelboat} ;# 2 nx::Class create smallmultihull -superclass dayboat ;# 5 nx::Class create smallcatamaran -superclass smallmultihull ;# 4 nx::Class create pedalo -superclass {pedalwheelboat smallcatamaran};# 1 dayboat public method max-distance {} {return 5m} wheelboat public method max-distance {} {return 100m} # If the linearization is known to be monotonic, the compiler can # choose to dispatch the call to max-distance directly to the method # defined on . This is known statically because no new # methods can be defined on max-distance - it is sealed - and # is always more specific than for instances # of . pedalo create o1 #? {o1 info precedence} {::pedalo ::pedalwheelboat ::engineless ::wheelboat ::smallcatamaran ::smallmultihull ::dayboat ::boat ::nx::Object} #? {o1 max-distance} 100m ? {o1 info precedence} {::pedalo ::pedalwheelboat ::engineless ::smallcatamaran ::smallmultihull ::dayboat ::wheelboat ::boat ::nx::Object} ? {o1 max-distance} 5m pedalwheelboat create pwb ? {pwb max-distance} 5m ? {pwb info precedence} "::pedalwheelboat ::engineless ::dayboat ::wheelboat ::boat ::nx::Object" smallcatamaran create smc ? {smc max-distance} 5m ? {smc info precedence} "::smallcatamaran ::smallmultihull ::dayboat ::boat ::nx::Object" set order [o1 info precedence] puts "${:case} nx: $order" check-constraints ${:case} nx direct $order [direct-constraints o1] check-constraints ${:case} nx monotonicty $order [monotonicity-constraints o1] check-constraints ${:case} nx local-order $order [local-order-constraints o1] } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End:tests/method-parameter.test000066400000000000000000000347671242365656200164130ustar00rootroot00000000000000# -*- Tcl -*- package require nx package require nx::test # # simple method parameter tests # nx::test case method-params-0 { nsf::proc p0 {} {return 1} nsf::proc p1 {-x} {return [list [info exists x]]} ? {p0} 1 # the following error msg comes from Tcl ? {p0 -x} {wrong # args: should be "p0"} ? {p1} 0 ? {p1 -x} {value for parameter '-x' expected} ? {p1 -x 1} 1 ? {p1 -x 1 2} {invalid argument '2', maybe too many arguments; should be "p1 ?-x /value/?"} ? {p1 -x 1 -y} {invalid non-positional argument '-y', valid are : -x; should be "p1 ?-x /value/?"} ? {p1 a} {invalid argument 'a', maybe too many arguments; should be "p1 ?-x /value/?"} ? {p1 a -x} {invalid argument 'a', maybe too many arguments; should be "p1 ?-x /value/?"} ? {p1 --} 0 ? {p1 -y} {invalid non-positional argument '-y', valid are : -x; should be "p1 ?-x /value/?"} ? {p1 -y --} {invalid non-positional argument '-y', valid are : -x; should be "p1 ?-x /value/?"} # # should we really allow numeric nonpos arg names? # ? {nsf::proc p2 {1 -2 -3} {return [list ${1} [info exists 2] [info exists 3]]}} "" ? {p2 -4 -2 -3 -3 -2} "-4 1 1" ;# var 2 has value "-3", var 3 has value "-2" ? {p2 -4 -3 + -2 -1} "-4 1 1" ;# var 2 has value "-2", var 3 has value "+" ? {nsf::proc p3 {1 -2 -3 4} {return [list ${1} [info exists 2] [info exists 3] ${4}]}} "" ? {p3 -4 -3 -2 -1} "-4 0 1 -1" ;# var 1 has value "-4", var 4 has value "-1" } # # test behavior of parameter option nodashalnum # nx::test case nodashalnum { nsf::proc p2a {-x args} {return [list [info exists x] $args]} nsf::proc p2b {-x args:nodashalnum} {return [list [info exists x] $args]} ? {p2a -x -y} {1 {}} ;# "-y" is the value of "x" ? {p2b -x -y} {1 {}} ;# "-y" is the value of "x" ? {p2a -x 1 -y} {1 -y} ? {p2a -x 1 -100} {1 -100} ? {p2b -x 1 -y} {invalid non-positional argument '-y', valid are : -x; should be "p2b ?-x /value/? ?/arg .../?"} ? {p2b -x 1 -100} {1 -100} nsf::proc p3a {a -x -y b:nodashalnum -z} {return [list $a [info exists x] [info exists y] $b]} ? {p3a 100 -x 1 -y 1 200} {100 1 1 200} ? {p3a 100 -xx 1 -y 1 200} {invalid non-positional argument '-xx', valid are : -x, -y; should be "p3a /a/ ?-x /value/? ?-y /value/? /b/ ?-z /value/?"} } # # Testing the unknown handler # nx::test case unknown-handler { Class create C { :public method p1 {-x} {return [list [info exists x]]} :create c1 } ? {c1 p1 -x 1 -y} {invalid non-positional argument '-y', valid are : -x; should be "::c1 p1 ?-x /value/?"} proc ::nsf::argument::unknown {method arg args} { puts stderr "??? unknown nonpos-arg $arg in $method obj <$args>\n[info frame -1]\n" return "" } ? {c1 p1 -x 1 -y} {invalid non-positional argument '-y', valid are : -x; should be "::c1 p1 ?-x /value/?"} if {0} { proc ::nsf::argument::unknown {method arg args} { # nasty handler redefines method puts stderr "??? REDEFINE ::nsf::argument::unknown <$args> [info frame -1]" C public method p1 {-y} {return [list [info exists y]]} return "" } ? {c1 p1 -x 1 -y} {invalid non-positional argument '-y', valid are : -x; should be "::c1 p1 ?-x /value/?"} } } # # testing error message when flags are used within an ensemble # nx::test case flag-in-ensemble { nx::Class create C set info {info children, info class, info filters, info has mixin, info has namespace, info has type, info heritage, info info, info instances, info lookup filter, info lookup filters, info lookup method, info lookup methods, info lookup mixins, info lookup parameters, info lookup slots, info lookup syntax, info lookup variables, info method args, info method body, info method definition, info method definitionhandle, info method exists, info method handle, info method origin, info method parameters, info method registrationhandle, info method returns, info method submethods, info method syntax, info method type, info methods, info mixinof, info mixins, info name, info object filters, info object method args, info object method body, info object method definition, info object method definitionhandle, info object method exists, info object method handle, info object method origin, info object method parameters, info object method registrationhandle, info object method returns, info object method submethods, info object method syntax, info object method type, info object methods, info object mixins, info object slots, info object variables, info parent, info precedence, info slots, info subclasses, info superclasses, info variable definition, info variable name, info variable parameter, info variables, info vars} ? {C info superclasses} "::nx::Object" ? {C info -a superclass} "unable to dispatch sub-method \"-a\" of ::C info; valid are: $info" ? {C info -- superclass} "unable to dispatch sub-method \"--\" of ::C info; valid are: $info" ? {C info -- -a superclass} "unable to dispatch sub-method \"--\" of ::C info; valid are: $info" ? {C info -a -- superclass} "unable to dispatch sub-method \"-a\" of ::C info; valid are: $info" } # # Testing error messages in info subclasses, when too many arguments are # specified, or when wrong non-positional arguments are given. The # argument "pattern" in "info subclasses" has parameter option # "nodashalnum" set. # nx::test case info-subclass-error-messages { nx::Class create C nx::Class create D -superclass C nx::Class create E -superclass C # # no argument # ? {C info subclasses} "::E ::D" ? {C info subclasses --} "::E ::D" # # one argument # ? {C info subclasses a} "" # # The argument definition of "pattern" for subclass has # "nodashalnum" option, therefore we can deduce that "-a" must be # a flag. OTOH, if "-a" is a proper value (e.g. value of a # variable), then the following command would be perfectly fine. # ? {C info subclasses -a} \ {invalid non-positional argument '-a', valid are : -closure, -dependent; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -a --} \ {invalid non-positional argument '-a', valid are : -closure, -dependent; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -- -a} "" ? {C info subclasses -1} "" ? {C info subclasses -- -1} "" ? {C info subclasses -1 --} \ {invalid argument '--', maybe too many arguments; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} # # two arguments # ? {C info subclasses a b} \ {invalid argument 'b', maybe too many arguments; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -- a b} \ {invalid argument 'b', maybe too many arguments; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses a -- b} \ {invalid argument '--', maybe too many arguments; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses a b --} \ {invalid argument 'b', maybe too many arguments; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} # first flag ? {C info subclasses -a b} \ {invalid non-positional argument '-a', valid are : -closure, -dependent; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -- -a b} \ {invalid argument 'b', maybe too many arguments; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -a -- b} \ {invalid non-positional argument '-a', valid are : -closure, -dependent; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -a b --} \ {invalid non-positional argument '-a', valid are : -closure, -dependent; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} # second flag ? {C info subclasses a -b} \ {invalid argument '-b', maybe too many arguments; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -- a -b} \ {invalid argument '-b', maybe too many arguments; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses a -- -b} \ {invalid argument '--', maybe too many arguments; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses a -b --} \ {invalid argument '-b', maybe too many arguments; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} # both flag ? {C info subclasses -a -b} \ {invalid non-positional argument '-a', valid are : -closure, -dependent; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -- -a -b} \ {invalid argument '-b', maybe too many arguments; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -a -- -b} \ {invalid non-positional argument '-a', valid are : -closure, -dependent; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -a -b --} \ {invalid non-positional argument '-a', valid are : -closure, -dependent; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} } # # Testing error messages in info superclasses, when too many arguments # are specified, or when wrong non-positional arguments are # given. The argument "pattern" in "info superclasses" has parameter option # "nodashalnum" NOT set. # nx::test case info-superclass-error-messages { nx::Class create C nx::Class create D -superclass C # # no argument # ? {D info superclasses} "::C" ? {D info superclasses --} "::C" # # one argument # ? {D info superclasses a} "" # # The argument definition of "pattern" for superclass has no # "nodashalnum" option, "-a" is treated like a pattern. # ? {D info superclasses -a} "" ? {D info superclasses -a --} \ {invalid argument '--', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses -- -a} "" ? {D info superclasses -1} "" # # two arguments # ? {D info superclasses a b} \ {invalid argument 'b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses -- a b} \ {invalid argument 'b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses a -- b} \ {invalid argument '--', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses a b --} \ {invalid argument 'b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} # first flag ? {D info superclasses -a b} \ {invalid argument 'b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses -- -a b} \ {invalid argument 'b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses -a -- b} \ {invalid argument '--', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses -a b --} \ {invalid argument 'b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} # second flag ? {D info superclasses a -b} \ {invalid argument '-b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses -- a -b} \ {invalid argument '-b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses a -- -b} \ {invalid argument '--', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses a -b --} \ {invalid argument '-b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} # both flag ? {D info superclasses -a -b} \ {invalid argument '-b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses -- -a -b} \ {invalid argument '-b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses -a -- -b} \ {invalid argument '--', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} ? {D info superclasses -a -b --} \ {invalid argument '-b', maybe too many arguments; should be "::D info superclasses ?-closure? ?/pattern/?"} } # # Test interactions of parameter option nodashalnum in "pattern" # with values starting with a dash. # nx::test case info-with-dash-class-names { nx::Class create C nx::Class create -a -superclass C nx::Class create -b -superclass -a # # no argument # ? {C info subclasses} "::-a" ? {C info subclasses --} "::-a" ? {-b info superclasses} "::-a" ? {-b info superclasses --} "::-a" # # one argument # ? {C info subclasses -a} \ {invalid non-positional argument '-a', valid are : -closure, -dependent; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -a --} \ {invalid non-positional argument '-a', valid are : -closure, -dependent; should be "::C info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {C info subclasses -- -a} "::-a" ? {-b info superclasses -a} "::-a" ? {-b info superclasses -a --} \ {invalid argument '--', maybe too many arguments; should be "::-b info superclasses ?-closure? ?/pattern/?"} ? {-b info superclasses -- -a} "::-a" } # # Test abbreviations # nx::test case abbrevs { nsf::proc x {-super -super11 -superclass -super12} { return [info exists super]-[info exists super11]-[info exists superclass]-[info exists super12] } ? {x -super 1} "1-0-0-0" ? {x -super1 1} "the provided argument -super1 is an abbreviation for -super11 and -super12" ? {x -superc 1} "0-0-1-0" ? {x -super12 1} "0-0-0-1" nsf::proc y {-aaa1 -aa1 -a1 -a} { return [info exists aaa1]-[info exists aa1]-[info exists a1]-[info exists a] } ? {y -a 1} "0-0-0-1" ? {y -aa 1} {invalid non-positional argument '-aa', valid are : -aaa1, -aa1, -a1, -a; should be "y ?-aaa1 /value/? ?-aa1 /value/? ?-a1 /value/? ?-a /value/?"} ? {y -aaa 1} "1-0-0-0" ? {y -aa1 1} "0-1-0-0" } # # leading dash and and numbers # nx::test case abbrevs { nsf::proc x {-x y:integer} { return [info exists x]-$y } ? {x 1} "0-1" ? {x -1} "0--1" ? {x -- -1} "0--1" nsf::proc y {-1 y:integer} { return [info exists 1]-$y } ? {y 1} "0-1" ? {y -1} "value for parameter '-1' expected" ? {y -- -1} "0--1" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/method-require.test000066400000000000000000000106321242365656200160700ustar00rootroot00000000000000# -*- Tcl -*- package require nx package require nx::test nx::test configure -count 10 nx::test case method-require { # # A few method-provides # # Some provides could be in e.g. nx.tcl, some could be loaded via # package require. We could as well think about an auto-indexer # producing these.... # nsf::method::provide append {::nsf::method::alias append -frame object ::append} nsf::method::provide lappend {::nsf::method::alias lappend -frame object ::lappend} nsf::method::provide set {::nsf::method::alias set -frame object ::set} nsf::method::provide tcl::set {::nsf::method::alias set -frame object ::set} nsf::method::provide exists {::nsf::method::alias exists ::nsf::methods::object::exists} nsf::method::provide foo {::nsf::method::create foo {x y} {return x=$x,y=$y}} # # Provide an example for an application defined method provider # nsf::proc ::get_mixin {object -per-object:switch mixinClass methodName} { ::nsf::mixin $object -per-object=${per-object} $mixinClass return [$mixinClass info method registrationhandle $methodName] } # use the method provider nsf::method::provide x {::get_mixin ::MIX x} { # here could be as well a package require, etc. ::nx::Class create ::MIX {:public method x {} {return x}} } # # Lets try it out: # nx::Class create C { :require method set :require method exists # required names can be different from registered names; if there # are multiple set methods, we could point to the right one ? [list [self] require method tcl::set] "::nsf::classes::C::set" # object methods ? [list [self] require object method lappend] "::C::lappend" # a scripted object method ? [list [self] require object method foo] "::C::foo" ? [list [self] require object method x] "::nsf::classes::MIX::x" ? [list [self] require method x] "::nsf::classes::MIX::x" # looks as well ok: ? [list [self] require namespace] "" } # # Try protected and public # ? {C require public method lappend} ::nsf::classes::C::lappend ? {::nsf::method::property C lappend call-protected} 0 ? {C require protected method lappend} ::nsf::classes::C::lappend ? {::nsf::method::property C lappend call-protected} 1 ? {C require protected object method set} ::C::set ? {::nsf::method::property C ::C::set call-protected} 1 # # call these methods # C create c1 ? {c1 set x 100} 100 ? {c1 exists x} 1 ? {C lappend some_list e1 e2} "e1 e2" ? {C foo 1 2} x=1,y=2 ? {C x} x # # Definitions directly on object # Object create o1 ? {o1 require object method set} ::o1::set ? {o1 require object method x} ::nsf::classes::MIX::x ? {o1 require public object method lappend} ::o1::lappend ? {::nsf::method::property o1 lappend call-protected} 0 ? {o1 require protected object method lappend} ::o1::lappend ? {::nsf::method::property o1 lappend call-protected} 1 } nx::test case parent-require { ::nx::Class public object method __unknown {name} { #puts stderr "***** __unknown called with <$name>" ::nx::Object create $name } ::nsf::object::unknown::add nx {::nx::Class __unknown} nx::Class create C ? {C create ::o::o} "::o::o" ? {::o info class} "::nx::Object" ? {::o::o info class} "::C" ? {::nx::Object create ::a::b} "::a::b" ? {::a info class} "::nx::Object" ? {::a::b info class} "::nx::Object" ? {C create ::1::2::3::4} "::1::2::3::4" ? {::1 info class} "::nx::Object" ? {::1::2 info class} "::nx::Object" ? {::1::2::3 info class} "::nx::Object" ? {::1::2::3::4 info class} "::C" } # # Test what happens if we try to redefine a "nonexistent" protected method. # nx::test case method-redefine-nonexistent { ? {nx::Class public method __alloc arg {return 1}} {refuse to overwrite protected method __alloc on ::nx::Class} ? {nx::Class public method __dealloc arg {return 1}} {refuse to overwrite protected method __dealloc on ::nx::Class} ? {nx::Class public method __recreate arg {return 1}} {refuse to overwrite protected method __recreate on ::nx::Class} } # # Test what happens if a class-specific method is registered and # called on an object. # nx::test case method-require-scope { nx::Object create o ::nsf::method::require o __alloc ? {o __alloc x} {method __alloc not dispatched on valid class} } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/methods.test000066400000000000000000001126111242365656200146010ustar00rootroot00000000000000# -*- Tcl -*- package require nx package require nx::test ::nx::configure defaultMethodCallProtection false nx::test configure -count 10 nx::Class create C { # methods :method plain_method {} {return [current method]} :public method public_method {} {return [current method]} :protected method protected_method {} {return [current method]} # forwards :forward plain_forward %self plain_method :public forward public_forward %self public_method :protected forward protected_forward %self protected_method # setter :property plain_setter :property -accessor public public_setter :property -accessor protected protected_setter # alias :alias plain_alias [C info method registrationhandle plain_method] :public alias public_alias [C info method registrationhandle public_method] :protected alias protected_alias [C info method registrationhandle protected_method] # class-object :object method plain_object_method {} {return [current method]} :public object method public_object_method {} {return [current method]} :protected object method protected_object_method {} {return [current method]} :object forward plain_object_forward %self plain_object_method :public object forward public_object_forward %self public_object_method :protected object forward protected_object_forward %self protected_object_method :object property {plain_object_setter ""} :object property -accessor public {public_object_setter ""} :object property -accessor protected {protected_object_setter ""} :object alias plain_object_alias [:info object method registrationhandle plain_object_method] :public object alias public_object_alias [:info object method registrationhandle public_object_method] :protected object alias protected_object_alias [:info object method registrationhandle protected_object_method] } C create c1 { # methods :object method plain_object_method {} {return [current method]} :public object method public_object_method {} {return [current method]} :protected object method protected_object_method {} {return [current method]} # forwards :object forward plain_object_forward %self plain_object_method :public object forward public_object_forward %self public_object_method :protected object forward protected_object_forward %self protected_object_method # setter :object property {plain_object_setter ""} :object property -accessor public {public_object_setter ""} :object property -accessor protected protected_object_setter # alias :object alias plain_object_alias [:info object method registrationhandle plain_object_method] :public object alias public_object_alias [:info object method registrationhandle public_object_method] :protected object alias protected_object_alias [:info object method registrationhandle protected_object_method] } C property -accessor public s0 C property -accessor protected s1 ? {c1 s0 set 0} 0 ? {::nsf::dispatch c1 s1 set 1} 1 C object property -accessor public {s3 ""} ? {C s3 set 3} 3 # create a fresh object (different from c1) C create c2 # test scripted class level methods nx::test case scripted-class-level-methods { ? {c2 plain_method} "plain_method" ? {c2 public_method} "public_method" ? {catch {c2 protected_method}} 1 ? {::nsf::dispatch c2 protected_method} "protected_method" } # class level forwards nx::test case class-level-forwards { ? {c2 plain_forward} "plain_method" ? {c2 public_forward} "public_method" ? {catch {c2 protected_forward}} 1 ? {::nsf::dispatch c2 protected_forward} "protected_method" } # class level setter nx::test case class-level-setter { ? {c2 plain_setter 1} {::c2: unable to dispatch method 'plain_setter'} #? {c2 plain_setter 1} 1 ? {c2 public_setter set 2} "2" ? {catch {c2 protected_setter set 3}} 1 ? {::nsf::dispatch c2 protected_setter set 4} "4" } # class level alias .... nx::test case class-level-alias { ? {c2 plain_alias} "plain_alias" ? {c2 public_alias} "public_alias" ? {catch {c2 protected_alias}} 1 ? {::nsf::dispatch c2 protected_alias} "protected_alias" } ########### # scripted class level methods nx::test case scripted-class-object-level { ? {C plain_object_method} "plain_object_method" ? {C public_object_method} "public_object_method" ? {catch {C protected_object_method}} 1 ? {::nsf::dispatch C protected_object_method} "protected_object_method" } # class level forwards nx::test case class-object-level-forwards { ? {C plain_object_forward} "plain_object_method" ? {C public_object_forward} "public_object_method" ? {catch {C protected_object_forward}} 1 ? {::nsf::dispatch C protected_object_forward} "protected_object_method" } # class level setter nx::test case class-object-level-setter { ? {C plain_object_setter 1} {method 'plain_object_setter' unknown for ::C; consider '::C create plain_object_setter 1' instead of '::C plain_object_setter 1'} #? {C plain_object_setter 1} "1" ? {C public_object_setter set 2} "2" ? {catch {C protected_object_setter set 3}} 1 ? {::nsf::dispatch C protected_object_setter set 4} "4" } # class level alias .... nx::test case class-object-level-alias { ? {C plain_object_alias} "plain_object_alias" ? {C public_object_alias} "public_object_alias" ? {catch {C protected_object_alias}} 1 ? {::nsf::dispatch C protected_object_alias} "protected_object_alias" } ########### # scripted object level methods nx::test case scripted-object-level-methods { ? {c1 plain_object_method} "plain_object_method" ? {c1 public_object_method} "public_object_method" ? {catch {c1 protected_object_method}} 1 ? {::nsf::dispatch c1 protected_object_method} "protected_object_method" } # object level forwards nx::test case object-level-forwards { ? {c1 plain_object_forward} "plain_object_method" ? {c1 public_object_forward} "public_object_method" ? {catch {c1 protected_object_forward}} 1 ? {::nsf::dispatch c1 protected_object_forward} "protected_object_method" } # object level setter nx::test case object-level-setter { ? {c1 plain_object_setter 1} {::c1: unable to dispatch method 'plain_object_setter'} #? {c1 plain_object_setter 1} "1" ? {c1 public_object_setter set 2} "2" ? {catch {c1 protected_object_setter set 3}} 1 ? {::nsf::dispatch c1 protected_object_setter set 4} "4" } # object level alias .... nx::test case object-level-alias { ? {c1 plain_object_alias} "plain_object_alias" ? {c1 public_object_alias} "public_object_alias" ? {catch {c1 protected_object_alias}} 1 ? {::nsf::dispatch c1 protected_object_alias} "protected_object_alias" #? {lsort [c1 info object methods]} \ "plain_object_alias plain_object_forward plain_object_method public_object_alias public_object_forward public_object_method public_object_setter" ? {lsort [c1 info object methods]} \ "plain_object_alias plain_object_forward plain_object_method public_object_alias public_object_forward public_object_method public_object_setter" #? {lsort [C info methods]} \ "plain_object_alias plain_object_forward plain_object_method public_object_alias public_object_forward public_object_method public_object_setter s3" ? {lsort [C info object methods]} \ "plain_object_alias plain_object_forward plain_object_method public_object_alias public_object_forward public_object_method public_object_setter s3" } C destroy nx::test case colondispatch { nx::Object create ::o { #:public object method foo args {;} :public object method bar args {;} } ? {o :bar} "::o: method name ':bar' must not start with a colon" ? {o eval :bar} "" ? {o :foo} "::o: method name ':foo' must not start with a colon" ? {o eval :foo} "::o: unable to dispatch method 'foo'" } nx::test case mixinguards { # define a Class C and mixin class M nx::Class create C nx::Class create M # register the mixin on C as a object mixin and define a mixinguard #C mixins set M #C mixins guard M {1 == 1} #? {C info mixin guard M} "1 == 1" #C mixins guard M {} #? {C info mixin guard M} "" # # set guard via converter # C mixins set {{M -guard {1 == 1}}} ? {C info mixins -guard} "{::M -guard {1 == 1}}" ? {C mixins get} "{::M -guard {1 == 1}}" # # set/clear guard via relation slot # C mixins set M ? {C mixins guard M {1 == 1}} "" ? {C mixins get} "{::M -guard {1 == 1}}" ? {C info mixins -guard} "{::M -guard {1 == 1}}" ? {C info mixins} "::M" ? {C mixins guard M ""} "" ? {C mixins get} "::M" ? {C info mixins -guard} "::M" # # now the same as object mixin and object mixin guard # # set guard via converter # C object mixins set {{M -guard {1 == 1}}} ? {C info object mixins -guard} "{::M -guard {1 == 1}}" ? {C info object mixins} "::M" ? {C object mixins get} "{::M -guard {1 == 1}}" # # set/clear guard via relation slot # C object mixins set M C object mixins guard M {1 == 1} ? {C object mixins get} "{::M -guard {1 == 1}}" ? {C info object mixins -guard} "{::M -guard {1 == 1}}" ? {C info object mixins} "::M" ? {C object mixins guard M {}} "" ? {C info object mixins -guard} "::M" } nx::test case mixin-via-objectparam { # add an object and class mixin via object-parameter and via slots foreach c {M1 M2 M3 M4 M5} {nx::Class create $c} nx::Class create C -mixin M1 -object-mixins M2 { :mixins add M3 :object mixins add M4 } ? {lsort [C info object mixins]} "::M2 ::M4" ? {lsort [C info mixins]} "::M1 ::M3" ? {lsort [C object mixins get]} "::M2 ::M4" ? {lsort [C mixins get]} "::M1 ::M3" ? {lsort [C object mixins]} {wrong # args: use "::C object mixins add|classes|clear|delete|get|guard|set"} ? {lsort [C mixins]} {wrong # args: use "::C mixins add|classes|clear|delete|get|guard|set"} ? {lsort [C mixins x]} {submethod x undefined for mixins: use "::C mixins add|classes|clear|delete|get|guard|set"} ? {catch {C mixin M5} errorMsg} 1 ? {lsort [C info mixins]} "::M1 ::M3" ? {catch {C object mixin M5} errorMsg} 1 ? {lsort [C info object mixins]} "::M2 ::M4" ? {C mixins set M5} ::M5 ? {lsort [C info mixins]} "::M5" ? {C object mixins set M5} "::M5" ? {lsort [C info object mixins]} "::M5" ? {C configure -mixin M1} "" ? {C cget -mixin} "::M1" ? {C configure -object-mixins M2} "" ? {C cget -object-mixin} "::M2" } # testing next via nonpos-args nx::test case next-from-nonpos-args { nx::Object create o { :object method bar {-y:required -x:required} { #puts stderr "+++ o x=$x, y=$y [current args] ... next [current nextmethod]" return [list x $x y $y [current args]] } } nx::Class create M { :method bar {-x:required -y:required} { #puts stderr "+++ M x=$x, y=$y [current args] ... next [current nextmethod]" return [list x $x y $y [current args] -- {*}[next]] } } o object mixins set M ? {o bar -x 13 -y 14} "x 13 y 14 {-x 13 -y 14} -- x 13 y 14 {-x 13 -y 14}" ? {o bar -y 14 -x 13} "x 13 y 14 {-y 14 -x 13} -- x 13 y 14 {-y 14 -x 13}" } # # test method property with protected/public # nx::test case property-method { nx::Class create C { set x [:property -accessor public a] ? [list set _ $x] "::nsf::classes::C::a" # property with default :property {b b1} :property -accessor public {c c1} :property -accessor protected {d d1} set X [:object property -accessor public A] ? [list set _ $X] "::C::A" # object property with default :object property {B B2} :object property -accessor public {C C2} :object property -accessor protected {D D2} } C create c1 -a 1 ? {c1 a get} 1 ? {c1 cget -b} b1 ? {c1 cget -c} c1 ? {c1 d} "::c1: unable to dispatch method 'd'" ? {C A set 2} 2 ? {C A get} 2 ? {C B} {method 'B' unknown for ::C; consider '::C create B ' instead of '::C B '} #? {C B} B2 ? {C C get} C2 ? {C D} "method 'D' unknown for ::C; consider '::C create D ' instead of '::C D '" nx::Object create o { set x [:object property -accessor public a] ? [list set _ $x] "::o::a" # property with default :object property {b b1} :object property -accessor public {c c1} :object property -accessor protected {d d1} } ? {o a set 2} 2 ? {o b} {::o: unable to dispatch method 'b'} #? {o b} b1 ? {o c get} c1 ? {o d} "::o: unable to dispatch method 'd'" } nx::test case subcmd { nx::Class create Foo { :method "Info filter guard" {filter} {return [current object]-[current method]} :method "Info filter methods" {-guards pattern:optional} {return [current object]-[current method]} :method "Info args" {} {return [current object]-[current method]} :method "Info foo" {} {return [current object]-[current method]} :object method "INFO filter guard" {a b} {return [current object]-[current method]} :object method "INFO filter methods" {-guards pattern:optional} {return [current object]-[current method]} } ? {Foo INFO filter guard 1 2} ::Foo-guard ? {Foo INFO filter methods a*} ::Foo-methods Foo create f1 { :object method "list length" {} {return [current object]-[current method]} :object method "list reverse" {} {return [current object]-[current method]} } ? {f1 Info filter guard x} "::f1-guard" ? {f1 Info filter methods} "::f1-methods" ? {f1 Info args} "::f1-args" ? {f1 Info foo} "::f1-foo" ? {f1 list length} "::f1-length" ? {f1 list reverse} "::f1-reverse" } package req nx::serializer nx::test case class-object-property { nx::Class create C { :object property -accessor public x :property -accessor public a:int :create c1 } ? {C x set 1} 1 ? {C x get} 1 ? {lsort [C info methods]} "a" ? {lsort [C info object methods]} "x" ? {c1 a set b} {expected integer but got "b" for parameter "value"} set s(C) [C serialize] set s(c1) [c1 serialize] # Destroy object and class c1 destroy C destroy ? {nsf::object::exists c1} 0 ? {nsf::object::exists C} 0 # create it from the serialized code eval $s(C) ? {nsf::object::exists C} 1 eval $s(c1) ? {nsf::object::exists c1} 1 # tests should work as again ? {C x get} 1 ? {lsort [C info methods]} "a" ? {lsort [C info object methods]} "x" ? {c1 a set b} {expected integer but got "b" for parameter "value"} } # # Test method deletion # nx::test configure -count 1 nx::test case methoddelete { nx::Class create C { :public method foo {x} {return $x} :public object method bar {x} {return $x} :create c1 } ? {::nsf::method::delete C x} "::C: instance method 'x' does not exist" ? {::nsf::method::delete C -per-object x} "::C: object specific method 'x' does not exist" ? {::nsf::method::delete C foo} "" ? {::nsf::method::delete C foo} "::C: instance method 'foo' does not exist" ? {::nsf::method::delete C bar} "::C: instance method 'bar' does not exist" ? {::nsf::method::delete C -per-object bar} "" ? {::nsf::method::delete C -per-object bar} "::C: object specific method 'bar' does not exist" } # # Test error message of method modifier # nx::test configure -count 1 nx::test case errormessage { nx::Class create C ? {C public method foo {x} {return $x}} "::nsf::classes::C::foo" ? {C public Object method bar {x} {return $x}} \ "'Object' is not a method defining method" ? {C protected Object method bar {x} {return $x}} \ "'Object' is not a method defining method" ? {C Object method bar {x} {return $x}} \ {method 'Object' unknown for ::C; consider '::C create Object method bar x {return $x}' instead of '::C Object method bar x {return $x}'} #? {C public object Object method bar {x} {return $x}} "'Object' not allowed to be modified by 'class'" #? {C public object Object method bar {x} {return $x}} \ {'Object' is not a method defining method} } # # test dispatch without object # nx::test case dispatch-without-object { nx::Object create o { # property defines a setter, we need a current object :object property -accessor public {a v} # the other methods don't require them as strong :object forward b ::o2 bar :object method foo {} {return [nx::self]} :object alias x ::o::foo } nx::Object create o2 { :public object method bar {} {return [nx::self]} } # dispatch methods without current object ? ::o::a {wrong # args: use "::o ::o::a add|delete|get|set|unset"} ? ::o::b "::o2" ? ::o::foo "no current object; command called outside the context of a Next Scripting method" ? ::o::x "no current object; x called outside the context of a Next Scripting method" # make a regular call, provide tcd->object with a value ? {::o x} "::o" # check, if missing object is still detected ? ::o::x "no current object; x called outside the context of a Next Scripting method" ? nx::self "no current object; command called outside the context of a Next Scripting method" } # # Test the current namespaces and resolution for # a) top-level methods # b) ensemble methods on level 1 # c) ensemble methods on level 2 # nx::test case scopes { nx::Object create o1 { :public object method foo {} {return [namespace current]-[namespace which info]} :public object method "info foo" {} {return [namespace current]-[namespace which info]} :public object method "info bar foo" {} {return [namespace current]-[namespace which info]} } ? {o1 foo} "::-::info" ? {o1 info foo} "::-::info" ? {o1 info bar foo} "::-::info" nx::Class create C { :public method foo {} {return [namespace current]-[namespace which info]} :public method "info foo" {} {return [namespace current]-[namespace which info]} :public method "info bar foo" {} {return [namespace current]-[namespace which info]} :create c1 } ? {c1 foo} "::-::info" ? {c1 info foo} "::-::info" ? {c1 info bar foo} "::-::info" } # # Test the current namespaces and resolution for methods # registered on a object in a certain namespace # a) top-level methods # b) ensemble methods on level 1 # c) ensemble methods on level 2 # nx::test case namespaced-scopes { namespace eval ::ns { nx::Object create o1 { :public object method foo {} {return [namespace current]-[namespace which info]} :public object method "info foo" {} {return [namespace current]-[namespace which info]} :public object method "info bar foo" {} {return [namespace current]-[namespace which info]} } nx::Class create C { :public method foo {} {return [namespace current]-[namespace which info]} :public method "info foo" {} {return [namespace current]-[namespace which info]} :public method "info bar foo" {} {return [namespace current]-[namespace which info]} :create c1 } } ? {ns::o1 foo} "::ns-::info" ? {ns::o1 info foo} "::ns-::info" ? {ns::o1 info bar foo} "::ns-::info" ? {ns::c1 foo} "::ns-::info" ? {ns::c1 info foo} "::ns-::info" ? {ns::c1 info bar foo} "::ns-::info" } # # Test the current namespaces and resolution for methods # registered on a sub object # a) top-level methods # b) ensemble methods on level 1 # c) ensemble methods on level 2 # nx::test case nested-scopes { nx::Object create o nx::Object create o::o1 { :public object method foo {} {return [namespace current]-[namespace which info]} :public object method "info foo" {} {return [namespace current]-[namespace which info]} :public object method "info bar foo" {} {return [namespace current]-[namespace which info]} } ? {o::o1 foo} "::o-::info" ? {o::o1 info foo} "::o-::info" ? {o::o1 info bar foo} "::o-::info" nx::Class create o::C { :public method foo {} {return [namespace current]-[namespace which info]} :public method "info foo" {} {return [namespace current]-[namespace which info]} :public method "info bar foo" {} {return [namespace current]-[namespace which info]} :create c1 } ? {c1 foo} "::o-::info" ? {c1 info foo} "::o-::info" ? {c1 info bar foo} "::o-::info" } # # Test deletion of object-specific methods/attributes via "delete # method" and "delete property" # # a) test attributes # b) test simple methods # c) test ensemble methods # nx::test case delete-per-object { nx::Object create o1 { :object property -accessor public a1 :object property -accessor public a2 :public object method foo {} {return [namespace current]-[namespace which info]} :public object method "info foo" {} {return [namespace current]-[namespace which info]} :public object method "info bar foo" {} {return [namespace current]-[namespace which info]} } ? {o1 info object methods -path} "{info foo} {info bar foo} foo a1 a2" ? {o1 info children} "::o1::info ::o1::per-object-slot" ? {o1 delete object method bar} "::o1: object specific method 'bar' does not exist" # For a1, we have a method and an property. We can delete the # method without the slot. ? {o1 delete object method a1} "" # After the deletion of the accessor, the slot exists still ? {o1::per-object-slot info children} "::o1::per-object-slot::a1 ::o1::per-object-slot::a2" # If we perform now a "delete object property a1", the slot will be removed. ? {o1 delete object property a1} "" ? {o1::per-object-slot info children} "::o1::per-object-slot::a2" # try to delete the property again: ? {o1 delete object property a1} "::o1: cannot delete object-specific property 'a1'" ? {o1 info object methods -path} "{info foo} {info bar foo} foo a2" ? {o1 delete object property a2} "" ? {o1 info object methods -path} "{info foo} {info bar foo} foo" ? {o1 delete object method foo} "" ? {o1 info object methods -path} "{info foo} {info bar foo}" ? {o1 delete object method "info foo"} "" ? {o1 info object methods -path} "{info bar foo}" ? {o1 delete object method "info bar foo"} "" ? {o1 info object methods -path} "" } # # Test deletion of per-object methods/attributes defined on classes # via the delete method # a) test attributes # b) test simple methods # c) test ensemble methods # nx::test case delete-per-object-on-class { nx::Class create C { :object property -accessor public a1 :public object method foo {} {return [namespace current]-[namespace which info]} :public object method "info foo" {} {return [namespace current]-[namespace which info]} :public object method "info bar foo" {} {return [namespace current]-[namespace which info]} :property -accessor public a2 } ? {C info object methods -path} "{info foo} {info bar foo} foo a1" ? {C info children} "::C::info ::C::slot ::C::per-object-slot" ? {C delete object method bar} "::C: object specific method 'bar' does not exist" ? {C delete object property a1} "" ? {C info object methods -path} "{info foo} {info bar foo} foo" ? {C delete object property a1} "::C: cannot delete object-specific property 'a1'" ? {C delete object method foo} "" ? {C info object methods -path} "{info foo} {info bar foo}" ? {C delete object method "info foo"} "" ? {C info object methods -path} "{info bar foo}" ? {C delete object method "info bar foo"} "" ? {C info object methods -path} "" ? {C info methods} "a2" ? {C info slots} "::C::slot::a2" } # # Test deletion of methods/attributes defined on classes via the # delete method # a) test attributes # b) test simple methods # c) test ensemble methods # nx::test case delete-class-level-method { nx::Class create C { :property -accessor public a1 :public method foo {} {return [namespace current]-[namespace which info]} :public method "info foo" {} {return [namespace current]-[namespace which info]} :public method "info bar foo" {} {return [namespace current]-[namespace which info]} } ? {C info methods -path} "{info foo} {info bar foo} foo a1" ? {C info children} "::C::slot" ? {C delete method bar} "::C: instance method 'bar' does not exist" ? {C delete property a1} "" ? {C info methods -path} "{info foo} {info bar foo} foo" ? {C delete property a1} "::C: cannot delete property 'a1'" ? {C delete method foo} "" ? {C info methods -path} "{info foo} {info bar foo}" ? {C delete method "info foo"} "" ? {C info methods -path} "{info bar foo}" ? {C delete method "info bar foo"} "" ? {C info methods -path} "" } # # simple unknown tests; # ensemble unknown tests are in submethods.test # nx::test case test-simple-unknown { # # calling unknown with a plain "method" without arguments # ::nx::Class create A { :object method unknown args {? [list set _ $args] "hello"} } A hello # # calling unknown with a plain "method" with arguments # ::nx::Class create B { :object method unknown args {? [list set _ $args] "hello world"} } B hello world # # calling unknown with a method with spaces # ::nx::Class create C { :object method unknown args {? [list set _ $args] "{hello world}"} } C {hello world} } # # simple speed tests # ensemble unknown tests are in submethods.test # nx::test configure -count 1000 nx::test case speed-dispatch { # # define various forms of simple dispatches # ::nx::Object create o { :public object method foo {} {return ::o} :public object method bar00 {} {self} :public object method bar01 {} {:} :public object method bar02 {} {[self]} :public object method bar03 {} {[:]} :public object method bar04 {} {:foo} :public object method bar05 {} {: foo} #:public object method bar06 {} {my foo} :public object method bar07 {} {[self] foo} :public object method bar08 {} {: -system info object methods foo} #:public object method bar09 {} {my -system info object methods foo} } ? {o foo} ::o ? {o bar00} ::o {self} ? {o bar01} ::o {:} ? {o bar02} ::o {[self]} ? {o bar03} ::o {[:]} ? {o bar04} ::o ":foo" ? {o bar05} ::o ": foo" #? {o bar06} ::o "my foo" ? {o bar07} ::o "self foo" ? {o bar08} foo ": -system info" #? {o bar09} foo "my -system info" } nx::test configure -count 1 nx::test case fq-obj-dispatch { # # Capture the (current) dispatcher rules for fully-qualified # selectors which resolve to existing objects. # nx::Class create C { set :unknown 0 :public object method unknown {m args} { incr :unknown return unknown-$m } } nx::Class create D { set :defaultcalled 0 :public method defaultmethod args { [current class] eval [list incr :defaultcalled] } :create ::d } ? {::D eval {set :defaultcalled}} 0 ? {::d} 1 ? {C eval {set :unknown}} 0 ? {C ::d} "unknown-::d" ? {C eval {set :unknown}} 1 ? {::d} 2; # should not be 3! ? {C d} "unknown-d" ? {C eval {set :unknown}} 2 ? {::d} 3 # # nested-object selector, *not* pre-existing # ? {::nsf::object::exists ::d::c} 0 ? {C ::d::c} "unknown-::d::c" ? {C eval {set :unknown}} 3 ? {::nsf::object::exists ::d::c} 0 # # nested-object selector, pre-existing # ? {::nsf::object::exists ::d::dd} 0 D create ::d::dd ? {::nsf::object::exists ::d::dd} 1 ? {::D eval {set :defaultcalled}} 3 ? {::d::dd} 4 ? {C eval {set :unknown}} 3 ? {C ::d::dd} "unknown-::d::dd" ? {C eval {set :unknown}} 4 ? {C d::dd} "unknown-d::dd" ? {C eval {set :unknown}} 5 ? {::D eval {set :defaultcalled}} 4 # # namespaced selector, *not* pre-existing # namespace eval ::ns1 {} ? {::nsf::object::exists ::ns1::c} 0 ? {C ::ns1::c} "unknown-::ns1::c" ? {C eval {set :unknown}} 6 ? {::nsf::object::exists ::ns1::c} 0 # # namespaced selector, pre-existing # ? {::nsf::object::exists ::ns1::d} 0 D create ::ns1::d ? {::nsf::object::exists ::ns1::d} 1 ? {::D eval {set :defaultcalled}} 4 ? {::ns1::d} 5 ? {C eval {set :unknown}} 6 ? {C ::ns1::d} "unknown-::ns1::d" ? {C eval {set :unknown}} 7 ? {C ns1::d} "unknown-ns1::d" ? {C eval {set :unknown}} 8 ? {::D eval {set :defaultcalled}} 5 # # Is XOTcl's creation short-cut operative for nested-object # selectors, compliant with the XOTcl-specific unknown-(re)create # protocol? # package req XOTcl 2.0 ? {::nsf::object::exists ::X} 0 xotcl::Class ::X -instproc p1 {v} { [self class] incr [self proc] $v } -proc unknown args { my incr [self proc] next } -set unknown 0 -proc recreate args { my incr [self proc] next } -set recreate 0 ? {::nsf::object::exists ::X} 1 ? {::X exists p1} 0 ? {::X set unknown} 0 ? {xotcl::Object ::p} ::p ? {::nsf::object::exists ::p::child} 0 ? {::X ::p::child -p1 2} ::p::child ? {::nsf::object::exists ::p::child} 1 ? {::X set p1} 2 ? {::X set unknown} 1 ? {::X set recreate} 0 ? {::X ::p::child -p1 1} ::p::child ? {::X set p1} 3 ? {::X set unknown} 2 ? {::X set recreate} 1 } # # object copy # nx::test case object-copy { nsf::method::provide set {::nsf::method::alias set -frame object ::set} nx::Object create o { :public object method foo {} {return foo} :public object method "a b" {} {return "a b"} :public object method "a c" {} {return "a c"} :protected object method bar {} {return bar} :private object method baz {} {return baz} :public object forward fwd %self xxx :require public object method set } ? {lsort [::o info object methods -path]} "{a b} {a c} foo fwd set" ? {o a b} "a b" ? {o a c} "a c" ? {o set x 1} 1 ? {o eval {info exists :x}} 1 ? {o copy p} ::p ? {lsort [::p info object methods -path]} "{a b} {a c} foo fwd set" ? {p a b} "a b" ? {p a c} "a c" #package require nx::serializer #puts stderr [o serialize] #puts stderr [p serialize] ? {p eval {info exists :x}} 1 ? {p set x} 1 } # # class copy # nx::test case class-copy { nsf::method::provide set {::nsf::method::alias set -frame object ::set} nx::Class create C { :public method foo {} {return foo} :public method "a b" {} {return "a b"} :public method "a c" {} {return "a c"} :protected method bar {} {return bar} :private method baz {} {return baz} :public forward fwd %self xxx :require public method set :create c1 } ? {lsort [::C info methods -path]} "{a b} {a c} foo fwd set" ? {::c1 a b} "a b" ? {::c1 a c} "a c" ? {::c1 set x 1} 1 ? {::C copy ::D} ::D ? {lsort [::D info methods -path]} "{a b} {a c} foo fwd set" #package require nx::serializer #puts stderr [::C serialize] #puts stderr [::D serialize] ::D create d1 ? {::d1 a b} "a b" ? {::d1 a c} "a c" #puts stderr [::c1 serialize] #puts stderr [::d1 serialize] ? {::d1 set x 2} 2 } # # class copy with class object methods # nx::test case object+class-copy { nsf::method::provide set {::nsf::method::alias set -frame object ::set} nsf::method::provide exists {::nsf::method::alias exists ::nsf::methods::object::exists} nx::Class create C { :public method foo {} {return foo} :public method "a b" {} {return "a b"} :public method "a c" {} {return "a c"} :protected method bar {} {return bar} :private method baz {} {return baz} :public forward fwd %self xxx :require public method set :public object method ofoo {} {return foo} :public object method "oa b" {} {return "oa b"} :public object method "oa c" {} {return "oa c"} :protected object method obar {} {return bar} :private object method obaz {} {return baz} :public object forward ofwd %self xxx #TODO: the following line leads to a crash #:require public object method exists :require public object method set :create c1 } ? {lsort [::C info methods -path]} "{a b} {a c} foo fwd set" ? {lsort [::C info object methods -path]} "{oa b} {oa c} ofoo ofwd set" ? {::c1 a b} "a b" ? {::c1 a c} "a c" ? {::c1 set x 1} 1 ? {::C oa b} "oa b" ? {::C oa c} "oa c" ? {::C set y 100} "100" ? {::C copy ::D} ::D ? {lsort [::D info methods -path]} "{a b} {a c} foo fwd set" #? {lsort [::D info object methods -path]} "{oa b} {oa c} ofoo ofwd set" ? {::D oa b} "oa b" ? {::D oa c} "oa c" ? {::D set y} "100" ::D create d1 ? {::d1 a b} "a b" ? {::d1 a c} "a c" ? {::d1 set x 2} 2 } nx::test configure -count 10 # # class copy with class object methods # nx::test case object+class+property-copy { nsf::method::provide set {::nsf::method::alias set -frame object ::set} nsf::method::provide exists {::nsf::method::alias exists ::nsf::methods::object::exists} package require nx::serializer nx::Class create C { :public method foo {} {return foo} :public method "a b" {} {return "a b"} :public method "a c" {} {return "a c"} :protected method bar {} {return bar} :private method baz {} {return baz} :public forward fwd %self xxx :require public method set :property p :variable v 0 :public object method ofoo {} {return foo} :public object method "oa b" {} {return "oa b"} :public object method "oa c" {} {return "oa c"} :protected object method obar {} {return bar} :private object method obaz {} {return baz} :public object forward ofwd %self xxx :require public object method exists :require public object method set :object property op :object variable ov 0 :create c1 } ? {lsort [::C info methods -path]} "{a b} {a c} foo fwd set" ? {lsort [::C info object methods -path]} "exists {oa b} {oa c} ofoo ofwd set" ? {::c1 a b} "a b" ? {::c1 a c} "a c" ? {::c1 set x 1} 1 ? {::C oa b} "oa b" ? {::C oa c} "oa c" ? {::C set y 100} "100" ::nx::Object public method COPY {target} { set code [::Serializer deepSerialize -objmap [list [self] $target] [self]] #puts CODE=$code eval $code return $target } ? {::C copy ::D} ::D ? {::C COPY ::E} ::E ? {lsort [::D info methods -path]} "{a b} {a c} foo fwd set" ? {lsort [::D info object methods -path]} "exists {oa b} {oa c} ofoo ofwd set" ? {::D oa b} "oa b" ? {::D oa c} "oa c" ? {::D set y} "100" ? {::D create d1} ::d1 ? {::d1 a b} "a b" ? {::d1 a c} "a c" ? {::d1 set x 2} 2 ? {::E oa b} "oa b" ? {::E oa c} "oa c" ? {::E set y} "100" ? {::E create e1} ::e1 ? {::e1 a b} "a b" ? {::e1 a c} "a c" ? {::e1 set x 2} 2 } nx::test case xotcl-COPY { package req XOTcl xotcl::Class create C C proc foo {} {return foo} C instproc bar {} {return bar} C set x 1 ::xotcl::Object instproc COPY {target} { set code [::Serializer deepSerialize -objmap [list [self] $target] [self]] #puts CODE=$code eval $code return $target } ? {C set x} 1 C copy D C COPY E ? {D set x} 1 ? {D foo} foo ? {D create d1} ::d1 ? {d1 bar} bar ? {E set x} 1 ? {E foo} foo ? {E create e1} ::e1 ? {e1 bar} bar } nx::test case xotcl-assertion-swallows-result { package req XOTcl xotcl::Class create Edge Edge instproc foo {} { my set xxx } Edge instproc bar {} { my set xxx } {} {{1 == 0}} Edge create e1 # base case ? {catch {e1 foo} errMsg} 1 ? {string match {can't read "xxx":*} $errMsg} 1 ? {catch {e1 bar} errMsg} 1 ? {string match {can't read "xxx":*} $errMsg} 1 # turn on assertion checking nsf::method::assertion e1 check all # still report error when invariant would not return error ? {catch {e1 foo} errMsg} 1 ? {string match {can't read "xxx":*} $errMsg} 1 # still report error when postcondition would return an error ? {catch {e1 bar} errMsg} 1 ? {string match {can't read "xxx":*} $errMsg} 1 } nx::test case uplevel+interceptor-transparency { # # A real-world case from OpenACS + from the database abstraction # layer. Since profiling is realized via mixin, and the db interface # requires heavy upleveling for SQL bind variables, we have complex # interaction between upleveling and interceptor transparency. In # earlier versions, the Profile mixin towards the end of this test # case lead to a problem with the variable scope (the interceptor # transparency was violated). # nx::Object create ns_cache { :public object method eval {script} { set rc [catch {:uplevel $script} result] return -code $rc $result } } nx::Class create DBI { :public method 1row {} { :uplevel {return $x} } } nx::Class create Profile { :public method 1row {} { next } } DBI create db nx::Class create C { :public method foo {} { set x 1 return [db 1row] } :public method bar {} { set x 2 return [ns_cache eval {db 1row}] } :create c1 } ? {c1 foo} 1 ? {c1 bar} 2 db object mixins set Profile ? {c1 foo} 1 ? {c1 bar} 2 } nx::test case uplevel+tcl-transparency { # # A real-world case from OpenACS + from the database abstraction # layer. Frequently, nsf based methods are called from tcl procs # (and tcl-upleveled code). In order to preserve interceptor # transparency (i.e. to be able to use a mixin on the tcl-called nsf # method), the uplevel method has to behave like tcl-uplevel when the # caller is a tcl method. # nx::Object create ns_cache { :public object method eval {script} { set rc [catch {:uplevel $script} result] return -code $rc $result } :public object method eval0 {script} { set rc [catch {uplevel $script} result] return -code $rc $result } } nx::Class create Profile { :public method eval {script} { next } :public method eval0 {script} { next } } proc db {cmd} { #nsf::__db_show_stack return [uplevel $cmd] } proc foo {} { set x 1 db {set x} } proc bar0 {} { set x 2 ns_cache eval0 {db {set x}} } proc bar {} { set x 2 ns_cache eval {db {set x}} } # foo is tcl, only ? foo 1 # The "bar" functions use the ns_cache interface, which is # nsf-based. The function "bar0" uses tcl uplevel, which is fine, # as long no interceptor is used. The function "bar0" uses the # uplevel method, which works also, when e.g. mixins are used on # ns_cache. ? bar0 2 ? bar 2 ns_cache object mixins set Profile # the version with tcl-uplevel should fail ? bar0 {can't read "x": no such variable} # the version with uplevel method should succeed ? bar 2 } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/mixinof.test000066400000000000000000000417261242365656200146170ustar00rootroot00000000000000# -*- Tcl -*- # testing mixinof package require nx package require nx::test nx::test configure -count 100 ########################################### # testing simple per object mixins ########################################### nx::test case simple-pom { nx::Class create A nx::Object create o -object-mixins A ? {o object mixins get} ::A ? {o info object mixins} ::A ? {A info mixinof} ::o o destroy ? {A info mixinof} "" } ########################################### # testing transitive per object mixins ########################################### nx::test case transitive-pom { nx::Class create B nx::Class create C -superclass B nx::Class create M B mixins set M nx::Object create o -object-mixins C nx::Object create o1 -object-mixins B ? {C info mixinof} ::o ? {lsort [B info mixinof -closure]} "::o ::o1" ? {lsort [B info mixinof -closure ::o1]} "::o1" ? {lsort [B info mixinof -closure ::o*]} "::o ::o1" ? {lsort [C info mixinof -closure ::o*]} "::o" # A class is mixed into a per-object mixin class ? {lsort [M info mixinof -closure ::o*]} "::o ::o1" ? {lsort [M info mixinof -scope object]} "" } ########################################### # testing per object mixins with redefinition ########################################### nx::test case recreate-mixin-class { nx::Class create M {:method foo args {puts x;next}} nx::Object create o -object-mixins M ? {o info object mixins} ::M ? {o info precedence} "::M ::nx::Object" ? {o info lookup method foo} "::nsf::classes::M::foo" nx::Class create M {:method foo args next} ? {o info object mixins} ::M ? {o info precedence} "::M ::nx::Object" ? {o info lookup method foo} "::nsf::classes::M::foo" M destroy ? {o info object mixins} "" ? {o info precedence} "::nx::Object" ? {o info lookup method foo} "" } ########################################### # testing simple per class mixins ########################################### nx::test case pcm { nx::Class create A nx::Class create B -mixin A nx::Class create C -superclass B C create c1 ? {B mixins get} ::A ? {B info mixins} ::A ? {A info mixinof} ::B ? {c1 info precedence} "::A ::C ::B ::nx::Object" B destroy ? {A info mixinof} "" ? {c1 info precedence} "::C ::nx::Object" } ########################################### # testing simple per class mixins with guards ########################################### nx::test case pcm2 { nx::Class create M1 nx::Class create M2 nx::Class create X nx::Class create A -mixin {M1 M2 X} A mixins guard M1 "test" nx::Class create B -superclass A ? {A info mixins M2} ::M2 ? {A info mixins M*} "::M1 ::M2" ? {A info mixins -guards} "{::M1 -guard test} ::M2 ::X" ? {B info mixins} "" ? {B info mixins -closure} "::M1 ::M2 ::X" ? {B info mixins -closure M2} ::M2 ? {B info mixins -closure M*} "::M1 ::M2" ? {B info mixins -closure -guards} "{::M1 -guard test} ::M2 ::X" ? {B info mixins -closure -guards M1} "{::M1 -guard test}" ? {B info mixins -closure -guards M*} "{::M1 -guard test} ::M2" } ########################################### # testing transitive per class mixins ########################################### nx::test case trans-pcm1 { nx::Class create A nx::Class create B -mixin A nx::Class create C -superclass B A mixins set [nx::Class create M] A create a1 B create b1 C create c1 ? {B mixins get} ::A ? {B info mixins} ::A ? {A info mixinof -scope class} ::B ? {a1 info precedence} "::M ::A ::nx::Object" ? {b1 info precedence} "::M ::A ::B ::nx::Object" ? {c1 info precedence} "::M ::A ::C ::B ::nx::Object" ? {M info mixinof -scope class} "::A" # since M is an instmixin of A and A is a instmixin of B, # M is a instmixin of B as well, and of its subclasses ? {M info mixinof -scope class -closure} "::A ::B ::C" ? {A info mixinof -scope class} "::B" ? {A info mixinof -scope class -closure} "::B ::C" ? {B info mixinof -scope class} "" ? {B info mixinof -scope class -closure} "" # and now destroy mixins M destroy ? {a1 info precedence} "::A ::nx::Object" ? {b1 info precedence} "::A ::B ::nx::Object" ? {c1 info precedence} "::A ::C ::B ::nx::Object" B destroy ? {A info mixinof -scope class} "" ? {c1 info precedence} "::C ::nx::Object" } ########################################### # testing transitive per class mixins with subclasses ########################################### nx::test case trans-pcm2 { nx::Class create X nx::Class create D nx::Class create C -superclass D nx::Class create A -mixin C nx::Class create B -superclass A B create b1 # ::C and ::D come to ::A and B as mixins ? {A info heritage} "::C ::D ::nx::Object" ? {B info heritage} "::C ::D ::A ::nx::Object" ? {C info mixinof -scope class -closure} "::A ::B" ? {D info mixinof -scope class -closure} "::A ::B" ? {A info mixinof -scope class -closure} "" ? {B info mixinof -scope class -closure} "" ? {X info mixinof -scope class -closure} "" D mixins set X ? {C info mixinof -scope class -closure} "::A ::B" ? {D info mixinof -scope class -closure} "::A ::B" ? {A info mixinof -scope class -closure} "" ? {B info mixinof -scope class -closure} "" ? {X info mixinof -scope class -closure} "::D ::C ::A ::B" ? {b1 info precedence} "::C ::X ::D ::B ::A ::nx::Object" B create b2 ? {b2 info precedence} "::C ::X ::D ::B ::A ::nx::Object" } ########################################### # testing transitive per class mixins with subclasses ########################################### nx::test case trans-pcm3 { nx::Class create A3 -superclass [nx::Class create A2 -superclass [nx::Class create A1]] nx::Class create B3 -superclass [nx::Class create B2 -superclass [nx::Class create B1 -superclass [nx::Class create B0]]] nx::Class create C3 -superclass [nx::Class create C2 -superclass [nx::Class create C1]] A2 mixins set B2 B1 mixins set C2 ? {A1 info mixinof -scope class -closure} "" ? {A2 info mixinof -scope class -closure} "" ? {A3 info mixinof -scope class -closure} "" ? {A1 info heritage} "::nx::Object" ? {A2 info heritage} "::B2 ::C2 ::C1 ::B1 ::B0 ::A1 ::nx::Object" ? {A3 info heritage} "::B2 ::C2 ::C1 ::B1 ::B0 ::A2 ::A1 ::nx::Object" ? {B0 info mixinof -scope class -closure} "::A2 ::A3" ? {B1 info mixinof -scope class -closure} "::A2 ::A3" ? {B2 info mixinof -scope class -closure} "::A2 ::A3" ? {B3 info mixinof -scope class -closure} "" ? {C1 info mixinof -scope class -closure} "::B1 ::B2 ::B3 ::A2 ::A3" ? {C2 info mixinof -scope class -closure} "::B1 ::B2 ::B3 ::A2 ::A3" ? {C3 info mixinof -scope class -closure} "" } ########################################### # testing transitive per class mixins with destroy ########################################### nx::test case pcm-trans-destroy-A { nx::Class create A -mixin [nx::Class create M] nx::Class create B -mixin A nx::Class create C -superclass B A create a1 B create b1 C create c1 ? {B mixins get} ::A ? {B info mixins} ::A ? {A info mixinof -scope class} ::B ? {a1 info precedence} "::M ::A ::nx::Object" ? {b1 info precedence} "::M ::A ::B ::nx::Object" ? {c1 info precedence} "::M ::A ::C ::B ::nx::Object" # and now destroy A A destroy ? {a1 info precedence} "::nx::Object" ? {b1 info precedence} "::B ::nx::Object" ? {c1 info precedence} "::C ::B ::nx::Object" ? {M info mixinof} "" ? {M info mixinof -closure} "" B destroy ? {M info mixinof -scope class} "" ? {c1 info precedence} "::C ::nx::Object" } ########################################### # testing transitive per class mixins with destroy ########################################### nx::test case pcm-trans-destroy-B { nx::Class create A -mixin [nx::Class create M] nx::Class create B -mixin A nx::Class create C -superclass B A create a1 B create b1 C create c1 ? {B mixins get} ::A ? {B info mixins} ::A ? {A info mixinof -scope class} ::B ? {a1 info precedence} "::M ::A ::nx::Object" ? {b1 info precedence} "::M ::A ::B ::nx::Object" ? {c1 info precedence} "::M ::A ::C ::B ::nx::Object" B destroy ? {a1 info precedence} "::M ::A ::nx::Object" ? {b1 info precedence} "::nx::Object" ? {c1 info precedence} "::C ::nx::Object" ? {M info mixinof -scope class} "::A" ? {M info mixinof -scope class -closure} "::A" ? {A info mixinof -scope class} "" } ########################################### # testing simple per class mixins with redefinition ########################################### nx::test case pcm-redefine { nx::Class create A nx::Class create B -mixin A nx::Class create C -superclass B C create c1 ? {B mixins get} ::A ? {B info mixins} ::A ? {A info mixinof -scope class} ::B ? {c1 info precedence} "::A ::C ::B ::nx::Object" ? {B info superclasses -closure} "::nx::Object" ? {C info superclasses -closure} "::B ::nx::Object" ? {B info heritage} "::A ::nx::Object" ? {C info heritage} "::A ::B ::nx::Object" nx::Class create B -mixin A ? {B info superclasses -closure} "::nx::Object" ? {C info superclasses -closure} "::nx::Object" ? {B info heritage} "::A ::nx::Object" ? {C info heritage} "::nx::Object" ? {B mixins get} ::A ? {B info mixins} ::A ? {A info mixinof} ::B ? {c1 info precedence} "::C ::nx::Object" B destroy ? {A info mixinof} "" ? {c1 info precedence} "::C ::nx::Object" } ########################################### # testing simple per class mixins with # redefinition and softrecreate ########################################### nx::test case pcm-redefine-soft { ::nsf::configure softrecreate true nx::Class create A nx::Class create B -mixin A nx::Class create C -superclass B C create c1 ? {B mixins get} ::A ? {B info mixins} ::A ? {A info mixinof -scope class} ::B ? {c1 info precedence} "::A ::C ::B ::nx::Object" ? {B info superclasses -closure} "::nx::Object" ? {C info superclasses -closure} "::B ::nx::Object" ? {B info heritage} "::A ::nx::Object" ? {C info heritage} "::A ::B ::nx::Object" nx::Class create B -mixin A ? {B info superclasses -closure} "::nx::Object" ? {C info superclasses -closure} "::B ::nx::Object" ? {B info heritage} "::A ::nx::Object" ? {C info heritage} "::A ::B ::nx::Object" ? {B info mixins} ::A ? {A info mixinof -scope class} ::B ? {c1 info precedence} "::A ::C ::B ::nx::Object" B destroy ? {A info mixinof -scope class} "" ? {c1 info precedence} "::C ::nx::Object" } ########################################### # test of recreate with same superclass, # with softrecreate off ########################################### nx::test case precedence { ::nsf::configure softrecreate false nx::Class create O nx::Class create A -superclass O nx::Class create B -superclass A B create b1 A create a1 O create o1 ? {A info superclasses} "::O" ? {B info heritage} "::A ::O ::nx::Object" ? {list [A info subclasses] [B info subclasses] [O info subclasses]} "::B {} ::A" ? {list [A info superclasses] [B info superclasses] [O info superclasses]} "::O ::A ::nx::Object" ? {list [a1 info class] [b1 info class] [o1 info class]} "::A ::B ::O" ? {o1 info precedence} "::O ::nx::Object" ? {a1 info precedence} "::A ::O ::nx::Object" ? {b1 info precedence} "::B ::A ::O ::nx::Object" # we recreate the class new, with the same superclass nx::Class create A -superclass O ? {A info superclasses} "::O" ? {B info heritage} "::nx::Object" ? {list [A info subclasses] [B info subclasses] [O info subclasses]} "{} {} ::A" ? {list [A info superclasses] [B info superclasses] [O info superclasses]} "::O ::nx::Object ::nx::Object" ? {list [a1 info class] [b1 info class] [o1 info class]} "::nx::Object ::B ::O" ? {o1 info precedence} "::O ::nx::Object" ? {a1 info precedence} "::nx::Object" ? {b1 info precedence} "::B ::nx::Object" } ########################################### # test of recreate with different superclass # with softrecreate on ########################################### nx::test case alternate-precedence { ::nsf::configure softrecreate false nx::Class create O nx::Class create A -superclass O nx::Class create B -superclass A B create b1 A create a1 O create o1 ? {A info superclasses} "::O" ? {B info heritage} "::A ::O ::nx::Object" ? {list [A info subclasses] [B info subclasses] [O info subclasses]} "::B {} ::A" ? {list [A info superclasses] [B info superclasses] [O info superclasses]} "::O ::A ::nx::Object" ? {list [a1 info class] [b1 info class] [o1 info class]} "::A ::B ::O" ? {o1 info precedence} "::O ::nx::Object" ? {a1 info precedence} "::A ::O ::nx::Object" ? {b1 info precedence} "::B ::A ::O ::nx::Object" # we recreate the class new, with a different superclass nx::Class create A ? {A info superclasses} "::nx::Object" ? {B info heritage} "::nx::Object" ? {list [A info subclasses] [B info subclasses] [O info subclasses]} "{} {} {}" ? {list [A info superclasses] [B info superclasses] [O info superclasses]} "::nx::Object ::nx::Object ::nx::Object" ? {list [a1 info class] [b1 info class] [o1 info class]} "::nx::Object ::B ::O" ? {o1 info precedence} "::O ::nx::Object" ? {a1 info precedence} "::nx::Object" ? {b1 info precedence} "::B ::nx::Object" } ########################################### # test of recreate with same superclass, # with softrecreate on ########################################### nx::test case recreate-precedence { ::nsf::configure softrecreate true nx::Class create O nx::Class create A -superclass O nx::Class create B -superclass A B create b1 A create a1 O create o1 ? {A info superclasses} "::O" ? {B info heritage} "::A ::O ::nx::Object" ? {list [A info subclasses] [B info subclasses] [O info subclasses]} "::B {} ::A" ? {list [A info superclasses] [B info superclasses] [O info superclasses]} "::O ::A ::nx::Object" ? {list [a1 info class] [b1 info class] [o1 info class]} "::A ::B ::O" ? {o1 info precedence} "::O ::nx::Object" ? {a1 info precedence} "::A ::O ::nx::Object" ? {b1 info precedence} "::B ::A ::O ::nx::Object" # we recreate the class new, with the same superclass nx::Class create A -superclass O ? {A info superclasses} "::O" ? {B info heritage} "::A ::O ::nx::Object" ? {list [A info subclasses] [B info subclasses] [O info subclasses]} "::B {} ::A" ? {list [A info superclasses] [B info superclasses] [O info superclasses]} "::O ::A ::nx::Object" ? {list [a1 info class] [b1 info class] [o1 info class]} "::A ::B ::O" ? {o1 info precedence} "::O ::nx::Object" ? {a1 info precedence} "::A ::O ::nx::Object" ? {b1 info precedence} "::B ::A ::O ::nx::Object" } ########################################### # test of recreate with different superclass # with softrecreate on ########################################### nx::test case recreate-alternate-precedence { ::nsf::configure softrecreate true nx::Class create O nx::Class create A -superclass O nx::Class create B -superclass A B create b1 A create a1 O create o1 ? {B info heritage} "::A ::O ::nx::Object" ? {list [A info subclasses] [B info subclasses] [O info subclasses]} "::B {} ::A" ? {list [A info superclasses] [B info superclasses] [O info superclasses]} "::O ::A ::nx::Object" ? {list [a1 info class] [b1 info class] [o1 info class]} "::A ::B ::O" ? {o1 info precedence} "::O ::nx::Object" ? {a1 info precedence} "::A ::O ::nx::Object" ? {b1 info precedence} "::B ::A ::O ::nx::Object" # we recreate the class new, with a different superclass nx::Class create A ? {A info superclasses} "::nx::Object" ? {B info heritage} "::A ::nx::Object" ? {B info heritage} "::A ::nx::Object" ? {list [A info subclasses] [B info subclasses] [O info subclasses]} "::B {} {}" ? {list [A info superclasses] [B info superclasses] [O info superclasses]} "::nx::Object ::A ::nx::Object" ? {list [a1 info class] [b1 info class] [o1 info class]} "::A ::B ::O" ? {o1 info precedence} "::O ::nx::Object" ? {a1 info precedence} "::A ::nx::Object" ? {b1 info precedence} "::B ::A ::nx::Object" } ########################################### # testing simple per object mixins ########################################### nx::test case nx-mixinof { nx::Class create M nx::Class create A nx::Class create C C create c1 -object-mixins A C create c2 nx::Class create C2 -mixin A C2 create c22 ? {c1 object mixins get} ::A ? {c1 info object mixins} ::A ? {lsort [A info mixinof]} "::C2 ::c1" ? {M info mixinof} "" C mixins set M #? {M info mixinof -scope object} "::c1 ::c2" ? {M info mixinof -scope object} "" ? {M info mixinof -scope class} "::C" ? {M info mixinof -scope all} "::C" ? {M info mixinof} "::C" ? {lsort [A info mixinof]} "::C2 ::c1" ? {A info mixinof -scope object} "::c1" ? {A info mixinof -scope class} "::C2" c1 destroy ? {A info mixinof} "::C2" ? {M info mixinof} "::C" C destroy ? {M info mixinof} "" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/nsf-cmd.test000066400000000000000000000265231242365656200144730ustar00rootroot00000000000000package require nx::test nx::test case nsf-method-get-variants { # # Create a simple class # nx::Class create Person { :property name :public method foo {x:integer} {return foo-$x} :public method "string match" {pattern string} {return string-match-$string} :create p1 } # # A plain method # set ::handle [p1 info lookup method foo] ? {nsf::cmd::info args $::handle} x ? {nsf::cmd::info body $::handle} {return foo-$x} ? {nsf::cmd::info definition $::handle} {::Person public method foo x:integer {return foo-$x}} ? {nsf::cmd::info exists $::handle} 1 ? {nsf::cmd::info registrationhandle $::handle} ::nsf::classes::Person::foo ? {nsf::cmd::info definitionhandle $::handle} ::nsf::classes::Person::foo ? {nsf::cmd::info origin $::handle} "" ? {nsf::cmd::info parameter $::handle} x:integer ? {nsf::cmd::info syntax $::handle} "/x/" ? {nsf::cmd::info type $::handle} scripted ? {nsf::cmd::info precondition $::handle} "" ? {nsf::cmd::info postcondition $::handle} "" ? {nsf::cmd::info submethods $::handle} "" ? {nsf::cmd::info returns $::handle} "" # # An ensemble method # set ::handle [p1 info lookup method "string match"] ? {nsf::cmd::info args $::handle} "pattern string" ? {nsf::cmd::info body $::handle} {return string-match-$string} ? {nsf::cmd::info definition $::handle} {::Person public method {string match} {pattern string} {return string-match-$string}} ? {nsf::cmd::info exists $::handle} 1 ? {nsf::cmd::info registrationhandle $::handle} "::nsf::classes::Person::string match" ? {nsf::cmd::info definitionhandle $::handle} "::Person::slot::__string::match" ? {nsf::cmd::info origin $::handle} "" ? {nsf::cmd::info parameter $::handle} "pattern string" ? {nsf::cmd::info syntax $::handle} "/pattern/ /string/" ? {nsf::cmd::info type $::handle} scripted ? {nsf::cmd::info precondition $::handle} "" ? {nsf::cmd::info postcondition $::handle} "" ? {nsf::cmd::info submethods $::handle} "" ? {nsf::cmd::info returns $::handle} "" # # An C-defined method inherited from nx::Object # set ::handle [p1 info lookup method configure] ? {nsf::cmd::info args $::handle} args ? {nsf::cmd::info body $::handle} "" ? {nsf::cmd::info definition $::handle} "::nx::Object public alias configure ::nsf::methods::object::configure" ? {nsf::cmd::info exists $::handle} 1 ? {nsf::cmd::info registrationhandle $::handle} ::nsf::classes::nx::Object::configure ? {nsf::cmd::info definitionhandle $::handle} ::nsf::classes::nx::Object::configure ? {nsf::cmd::info origin $::handle} ::nsf::methods::object::configure ? {nsf::cmd::info parameter $::handle} args:virtualobjectargs ? {nsf::cmd::info syntax $::handle} "?/arg .../?" ? {nsf::cmd::info type $::handle} alias ? {nsf::cmd::info precondition $::handle} "" ? {nsf::cmd::info postcondition $::handle} "" ? {nsf::cmd::info submethods $::handle} "" ? {nsf::cmd::info returns $::handle} "" # # An undefined method # set ::handle [p1 info lookup method exists] ? {nsf::cmd::info args $::handle} "" ? {nsf::cmd::info body $::handle} "" ? {nsf::cmd::info definition $::handle} "" ? {nsf::cmd::info exists $::handle} 0 ? {nsf::cmd::info registrationhandle $::handle} "" ? {nsf::cmd::info definitionhandle $::handle} "" ? {nsf::cmd::info origin $::handle} "" ? {nsf::cmd::info parameter $::handle} "" ? {nsf::cmd::info syntax $::handle} "" ? {nsf::cmd::info type $::handle} "" ? {nsf::cmd::info precondition $::handle} "" ? {nsf::cmd::info postcondition $::handle} "" ? {nsf::cmd::info submethods $::handle} "" ? {nsf::cmd::info returns $::handle} "" # # Define 2 nsf::procs: one, which requires the nsf argument parser, # and one, which does not. # nsf::proc ::foo {{-x 1} y:optional} {return $x-$y} nsf::proc ::bar {a b} {return $a-$b} # # An nsf::proc requiring nsf argument parser # set ::handle ::foo ? {nsf::cmd::info args $::handle} "x y" ? {nsf::cmd::info body $::handle} {return $x-$y} ? {nsf::cmd::info definition $::handle} {::nsf::proc ::foo {{-x 1} y:optional} {return $x-$y}} ? {nsf::cmd::info exists $::handle} 1 ? {nsf::cmd::info registrationhandle $::handle} "" ? {nsf::cmd::info definitionhandle $::handle} "" ? {nsf::cmd::info origin $::handle} "" ? {nsf::cmd::info parameter $::handle} "{-x 1} y:optional" ? {nsf::cmd::info syntax $::handle} {?-x /value/? ?/y/?} ? {nsf::cmd::info type $::handle} "nsfproc" ? {nsf::cmd::info precondition $::handle} "" ? {nsf::cmd::info postcondition $::handle} "" ? {nsf::cmd::info submethods $::handle} "" ? {nsf::cmd::info returns $::handle} "" # # A simple Tcl proc # set ::handle ::bar ? {nsf::cmd::info args $::handle} "a b" ? {nsf::cmd::info body $::handle} {return $a-$b} ? {nsf::cmd::info definition $::handle} {::proc bar {a b} {return $a-$b}} ? {nsf::cmd::info exists $::handle} 1 ? {nsf::cmd::info registrationhandle $::handle} "" ? {nsf::cmd::info definitionhandle $::handle} "" ? {nsf::cmd::info origin $::handle} "" ? {nsf::cmd::info parameter $::handle} "a b" ? {nsf::cmd::info syntax $::handle} {/a/ /b/} ? {nsf::cmd::info type $::handle} "proc" ? {nsf::cmd::info precondition $::handle} "" ? {nsf::cmd::info postcondition $::handle} "" ? {nsf::cmd::info submethods $::handle} "" ? {nsf::cmd::info returns $::handle} "" # # A Tcl cmd implemented in C # set ::handle ::set ? {nsf::cmd::info args $::handle} "could not obtain parameter definition for method 'set'" ? {nsf::cmd::info body $::handle} {} ? {nsf::cmd::info definition $::handle} {} ? {nsf::cmd::info exists $::handle} 1 ? {nsf::cmd::info registrationhandle $::handle} "" ? {nsf::cmd::info definitionhandle $::handle} "" ? {nsf::cmd::info origin $::handle} "" ? {nsf::cmd::info parameter $::handle} "could not obtain parameter definition for method 'set'" ? {nsf::cmd::info syntax $::handle} "could not obtain parameter definition for method 'set'" ? {nsf::cmd::info type $::handle} "cmd" ? {nsf::cmd::info precondition $::handle} "" ? {nsf::cmd::info postcondition $::handle} "" ? {nsf::cmd::info submethods $::handle} "" ? {nsf::cmd::info returns $::handle} "" # # A Tcl cmd implemented in C with the nsf infrastructure (with parameter definitions) # set ::handle ::nsf::cmd::info ? {nsf::cmd::info args $::handle} "subcmd -context methodName pattern" ? {nsf::cmd::info body $::handle} {} ? {nsf::cmd::info definition $::handle} {} ? {nsf::cmd::info exists $::handle} 1 ? {nsf::cmd::info registrationhandle $::handle} "" ? {nsf::cmd::info definitionhandle $::handle} "" ? {nsf::cmd::info origin $::handle} "" ? {nsf::cmd::info parameter $::handle} "subcmd -context:object methodName pattern:optional" ? {nsf::cmd::info syntax $::handle} "args|body|definition|exists|registrationhandle|definitionhandle|origin|parameter|syntax|type|precondition|postcondition|submethods|returns ?-context /object/? /methodName/ ?/pattern/?" ? {nsf::cmd::info type $::handle} "cmd" ? {nsf::cmd::info precondition $::handle} "" ? {nsf::cmd::info postcondition $::handle} "" ? {nsf::cmd::info submethods $::handle} "" ? {nsf::cmd::info returns $::handle} "" } # # test virtual arg resolution + filtering # nx::test case nsf-method-get-variants { nx::Class create Person { :property name :create p1 } set ::handle1 [p1 info lookup method configure] set ::handle2 [Person info lookup method create] set ::handle3 [Person info lookup method new] # # configure # ? {nsf::cmd::info syntax -context p1 $::handle1} \ "?-name /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" ? {nsf::cmd::info parameter -context p1 $::handle1} \ "-name -object-mixins:mixinreg,slot=::nx::Object::slot::object-mixins,slotset,method=object-mixin,0..n -object-filters:filterreg,slot=::nx::Object::slot::object-filters,slotset,method=object-filter,0..n -class:class,alias,method=::nsf::methods::object::class __initblock:cmd,optional,nodashalnum" ? {nsf::cmd::info args -context p1 $::handle1} \ "name object-mixins object-filters class __initblock" # # filter on (virtual) arguments # ? {nsf::cmd::info parameter -context p1 $::handle1 na*} "-name" ? {nsf::cmd::info args -context p1 $::handle1 na*} "name" ? {nsf::cmd::info syntax -context p1 $::handle1 na*} "?-name /value/?" ? {nsf::cmd::info parameter -context p1 $::handle1 *a*} "-name -class:class,alias,method=::nsf::methods::object::class" ? {nsf::cmd::info args -context p1 $::handle1 *a*} "name class" ? {nsf::cmd::info syntax -context p1 $::handle1 *a*} "?-name /value/? ?-class /class/?" # # create # ? {nsf::cmd::info syntax -context Person $::handle2} \ "/objectName/ ?-name /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" ? {nsf::cmd::info parameter -context Person $::handle2} \ "objectName -name -object-mixins:mixinreg,slot=::nx::Object::slot::object-mixins,slotset,method=object-mixin,0..n -object-filters:filterreg,slot=::nx::Object::slot::object-filters,slotset,method=object-filter,0..n -class:class,alias,method=::nsf::methods::object::class __initblock:cmd,optional,nodashalnum" ? {nsf::cmd::info args -context Person $::handle2} \ "objectName name object-mixins object-filters class __initblock" # # new # ? {nsf::cmd::info syntax -context Person $::handle3} \ "?-childof /value/? ?-name /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" ? {nsf::cmd::info parameter -context Person $::handle3} \ "-childof -name -object-mixins:mixinreg,slot=::nx::Object::slot::object-mixins,slotset,method=object-mixin,0..n -object-filters:filterreg,slot=::nx::Object::slot::object-filters,slotset,method=object-filter,0..n -class:class,alias,method=::nsf::methods::object::class __initblock:cmd,optional,nodashalnum" ? {nsf::cmd::info args -context Person $::handle3} \ "-childof name object-mixins object-filters class __initblock" # # filter on (virtual) arguments # ? {nsf::cmd::info parameter -context Person $::handle3 na*} "-name" ? {nsf::cmd::info args -context Person $::handle3 na*} "name" ? {nsf::cmd::info syntax -context Person $::handle3 na*} "?-name /value/?" ? {nsf::cmd::info parameter -context Person $::handle3 *a*} "-name -class:class,alias,method=::nsf::methods::object::class" ? {nsf::cmd::info args -context Person $::handle3 *a*} "name class" ? {nsf::cmd::info syntax -context Person $::handle3 *a*} "?-name /value/? ?-class /class/?" ? {nsf::cmd::info args -context Person $::handle3 *il*} "-childof object-filters" # # queries without context # ? {nsf::cmd::info parameter $::handle1} args:virtualobjectargs ? {nsf::cmd::info parameter $::handle2} "objectName args:virtualclassargs" ? {nsf::cmd::info parameter $::handle3} "-childof args:virtualclassargs" # # Test cases, where no instance was created yet (no internally # cached parameters) # nx::Class create Student { :property matnr } set ::handle4 [Student info lookup method create] ? {nsf::cmd::info syntax -context Student $::handle4} \ "/objectName/ ?-matnr /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/object-system.test000066400000000000000000000300541242365656200157260ustar00rootroot00000000000000# -*- Tcl -*- package require nx ::nsf::configure dtrace on # # Basic tests of the object system, should not require Class Test, # since even class Test might not work at that time. # proc ? {cmd expected {msg ""}} { #puts "??? $cmd" set r [uplevel $cmd] if {$msg eq ""} {set msg $cmd} if {$r ne $expected} { #puts stderr "ERROR $msg returned '$r' ne '$expected'" error "FAILED $msg returned\n'$r' ne\n'$expected'" } else { puts stderr "OK $msg" } } ? {::nsf::configure objectsystems} {{::nx::Object ::nx::Class {-class.alloc {__alloc ::nsf::methods::class::alloc 1} -class.create create -class.dealloc {__dealloc ::nsf::methods::class::dealloc 1} -class.configureparameter __class_configureparameter -class.recreate {__recreate ::nsf::methods::class::recreate 1} -object.configure __configure -object.configureparameter __object_configureparameter -object.defaultmethod {defaultmethod ::nsf::methods::object::defaultmethod} -object.destroy destroy -object.init {init ::nsf::methods::object::init} -object.move move -object.unknown unknown}}} ? {::nsf::object::exists nx::Object} 1 ? {::nsf::object::property nx::Object initialized} 1 ? {::nsf::is class nx::Object} 1 ? {::nsf::is metaclass nx::Object} 0 ? {nx::Object info superclasses} "" ? {nx::Object info class} ::nx::Class ? {::nsf::object::exists nx::Class} 1 ? {::nsf::is class nx::Class} 1 ? {::nsf::is metaclass nx::Class} 1 ? {nx::Class info superclasses} ::nx::Object ? {nx::Class info class} ::nx::Class # # Minimal argument passing tests for early problem detection # nsf::proc p0 {arg} {return [list $arg]} nsf::proc p1 {arg:optional} {return [list [info exists arg]]} nsf::proc p2 {arg args} {return [list $arg $args]} nsf::proc p3 {arg:optional args} {return [list [info exists arg] $args]} nsf::proc p4 {-x args} {return [list [info exists x] $args]} nsf::proc p5 {-x arg:optional -y args} {return [list [info exists x] [info exists y] [info exists arg] $args]} ? {p0 a} a ? {p1 a} 1 ? {p1} 0 ? {p2 a b c} {a {b c}} ? {p2 a} {a {}} ? {p2 a b} {a b} ? {p3 a b} {1 b} ? {p3 a b c} {1 {b c}} ? {p3 a} {1 {}} ? {p3} {0 {}} ? {p4} {0 {}} ? {p4 -x 1} {1 {}} ? {p4 -x 1 2} {1 2} ? {p4 -- -x 1 2} {0 {-x 1 2}} ? {p4 --} {0 {}} ? {p4 -- --} {0 --} ? {p4 -y --} {0 {-y --}} ;# no -y parameter, so pushed into args ? {p5} {0 0 0 {}} ? {p5 -x 1} {1 0 0 {}} ? {p5 a} {0 0 1 {}} ? {p5 -y 1} {0 0 1 1} ;# "-y" is passed into arg, "1" into args ? {p5 -y 1 2} {0 0 1 {1 2}} ;# "-y" is passed into arg, "1 2" into args ? {p5 -x 1 2} {1 0 1 {}} ;# "2" is passed into arg, "1 2" into args ? {p5 -y --} {0 0 1 {}} ;# "--" is value of "y" # # Create objects and test its properties # nx::Object create o ? {::nsf::object::exists nx::Object} 1 ? {::nsf::is class o} 0 ? {::nsf::is metaclass o} 0 ? {o info class} ::nx::Object ? {nx::Object info instances o} ::o ? {nx::Object info instances ::o} ::o o destroy ? {nsf::object::exists ::o} 0 ? {nsf::object::exists o} 0 nx::Object create o2 { ? {::nsf::object::exists ::o2} 1 ? {::nsf::object::property ::o2 initialized} 0 } ? {::nsf::object::property ::o2 initialized} 1 nx::Class create C0 ? {::nsf::is class C0} 1 ? {::nsf::is metaclass C0} 0 ? {C0 info superclasses} ::nx::Object ? {C0 info class} ::nx::Class #? {lsort [Class info vars]} "__default_metaclass __default_superclass" nx::Class create M -superclass ::nx::Class ? {::nsf::object::exists M} 1 ? {::nsf::is class M} 1 ? {::nsf::is metaclass M} 1 ? {M info superclasses} ::nx::Class ? {M info class} ::nx::Class M create C ? {::nsf::object::exists C} 1 ? {::nsf::is class C} 1 ? {::nsf::is metaclass C} 0 ? {C info superclasses} ::nx::Object ? {C info class} ::M C create c1 ? {::nsf::object::exists c1} 1 ? {::nsf::is class c1} 0 ? {::nsf::is metaclass c1} 0 ? {c1 info class} ::C nx::Class create M2 -superclass M ? {::nsf::object::exists M2} 1 ? {::nsf::is class M2} 1 ? {::nsf::is metaclass M2} 1 ? {M2 info superclasses} ::M ? {M2 info class} ::nx::Class M2 create m2 ? {m2 info superclasses} ::nx::Object ? {m2 info class} ::M2 # destroy meta-class M, reclass meta-class instances to the base # meta-class and set subclass of M to the root meta-class M destroy ? {::nsf::object::exists C} 1 ? {::nsf::is class C} 1 ? {::nsf::is metaclass C} 0 ? {C info superclasses} ::nx::Object ? {C info class} ::nx::Class ? {::nsf::is metaclass M2} 1 ? {M2 info superclasses} ::nx::Class ? {m2 info superclasses} ::nx::Object ? {m2 info class} ::M2 # destroy class M, reclass class instances to the base class C destroy ? {::nsf::object::exists C} 0 ? {::nsf::object::exists ::C} 0 ? {::nsf::object::exists c1} 1 ? {::nsf::is object c1} 1 ? {::nsf::is class c1} 0 ? {::nsf::is metaclass c1} 0 ? {c1 info class} ::nx::Object # # tests for dispatching methods # nx::Object create o o public object method foo {} {return foo} o public object method bar1 {} {return bar1-[:foo]} o public object method bar2 {} {return bar2-[: foo]} o public object method bar4 {} {return bar4-[[self] foo]} o public object method bar5 {} {return [self]::bar5} o public object method bar6 {} {return [:]::bar6} o public object method bar7 {} {return bar7-[lsort [: -system info object methods bar7]]} # dispatch without colon names ? {o foo} foo "simple method dispatch" ? {o bar1} bar1-foo "colon-methodname dispatch" ? {o bar2} bar2-foo "colon cmd dispatch" #? {o bar3} bar3-foo "my dispatch" ? {o bar4} bar4-foo "self dispatch" ? {o bar5} ::o::bar5 "self cmd" ? {o bar6} ::o::bar6 "colon cmd" ? {o bar7} bar7-bar7 "colon cmd dispatch args" o destroy # basic attributes tests nx::Class create C { :property {x 1} :property {y 2} } ? {::nsf::object::exists C} 1 ? {::nsf::object::exists C::slot} 1 ? {C info children} ::C::slot #? {C::slot info vars} __parameter #? {C info attributes} {{x 1} {y 2}} ? {C info lookup parameters create x} {{-x 1}} ? {C info lookup parameters create y} {{-y 2}} ? {C copy X} ::X ? {::nsf::object::exists X} 1 ? {X info vars} "" ? {C info vars} "" ? {::nsf::object::exists X::slot} 1 ? {set c1 [C new]} "::nsf::__#1" ? {c1 copy c2} "::c2" # copy without new name ? {c2 copy} ::nsf::__#4 ? {set C [C copy]} ::nsf::__#6 ? {::nsf::object::exists ${C}::slot} 1 #? {X::slot info vars} __parameter ? {X info lookup parameters create ?} {{-x 1} {-y 2}} ? {X info lookup parameters create x} {{-x 1}} ? {X info lookup parameters create y} {{-y 2}} #? {X info properties} {{x 1} {y 2}} #? {X info properties -closure *a*} {volatile:alias,noarg class:class,alias,method=::nsf::methods::object::class} # actually, we want c1 to test below the recreation of c1 in another # object system ? {C create c1} ::c1 ? {C create c2 {:object method foo {} {;}}} ::c2 # # check low level method creation on classes, and check C-level # "-flag=value" handling # nsf::method::create ::C m1 {} {;} ? {lsort [::C ::nsf::methods::class::info::methods]} {m1} nsf::method::create ::C -per-object=false m2 {} {;} ? {lsort [::C ::nsf::methods::class::info::methods]} {m1 m2} nsf::method::create ::C -per-object=true m3 {} {;} ? {lsort [::C ::nsf::methods::object::info::methods]} {m3} # # tests for the dispatch command # nx::Object create o o object method foo {} {return goo} o object method bar {x} {return goo-$x} # dispatch without colon names ::nsf::dispatch o eval set :x 1 ? {o info vars} x "simple dispatch has set variable x" ? {::nx::var set o x} 1 "simple dispatch has set variable x to 1" ? {::nsf::dispatch o foo} "goo" "simple dispatch with one arg works" ? {::nsf::dispatch o bar 1} "goo-1" "simple dispatch with two args works" o destroy # dispatch with colon names nx::Object create o {set :x 1} ::nsf::dispatch ::o ::incr x ? {o eval {set :x}} 1 "cmd dispatch without -frame object did not modify the instance variable" ::nsf::directdispatch ::o -frame object ::incr x ? {o eval {set :x}} 2 "cmd dispatch -frame object modifies the instance variable" ? {catch {::nsf::dispatch ::o -frame object ::xxx x}} 1 "cmd dispatch with unknown command" o destroy nx::Object create o { :public object method foo {} { foreach var [list x1 y1 x2 y2 x3 y3] { lappend results $var [info exists :$var] } return $results } } ::nsf::directdispatch o ::eval {set x1 1; set :y1 1} ::nsf::directdispatch o -frame method ::eval {set x2 1; set :y2 1} ::nsf::directdispatch o -frame object ::eval {set x3 1; set :y3 1} ? {o foo} "x1 0 y1 0 x2 0 y2 1 x3 1 y3 1" o destroy # # Create objects via tcl ensembles # namespace eval k { nx::Class create s { :property j :method init {} {set :j} ;# the variable should be set } namespace export s namespace ensemble create } ? {k s create o -j X} ::o ::o destroy puts stderr ===MINI-OBJECTSYSTEM # test object system # create a minimal object system without internally dipatched methods ::nsf::objectsystem::create ::object ::class ? {::nsf::object::exists ::object} 1 ? {::nsf::is class ::object} 1 ? {::nsf::is metaclass ::object} 0 ? {::nsf::relation::get ::object class} ::class ? {::nsf::relation::get ::object superclass} "" ? {::nsf::object::exists ::class} 1 ? {::nsf::is class ::class} 1 ? {::nsf::is metaclass ::class} 1 ? {::nsf::relation::get ::class class} ::class ? {::nsf::relation::get ::class superclass} ::object # define non-standard methos to create/destroy objects and classes ::nsf::method::alias ::class + ::nsf::methods::class::create ::nsf::method::alias ::object - ::nsf::methods::object::destroy # create a class named C ::class + C ? {::nsf::object::exists ::C} 1 ? {::nsf::is class ::C} 1 ? {::nsf::is metaclass ::C} 0 ? {::nsf::relation::get ::C class} ::class ? {::nsf::relation::get ::C superclass} ::object # create an instance of C C + c1 ? {::nsf::object::exists ::c1} 1 ? {::nsf::is class ::c1} 0 ? {::nsf::is metaclass ::c1} 0 ? {::nsf::relation::get ::c1 class} ::C # destroy instance c1 - ? {::nsf::object::exists ::c1} 0 ? {::nsf::is class ::C} 1 # recreate an nx object with a namespace C + c2 # destroy class C - ? {::nsf::object::exists ::C} 0 ::nx::Class create ::C ? {catch {::C info has type ::UNKNOWN}} 1 ? {catch {::C info has type ::xyz::Bar}} 1 #? {catch {::nsf::is type ::CCCC ::nx::Object}} 1 ::C destroy # # Test protection against (most likely unintended) deletion of base # classes. # ? {catch {nx::Object destroy}} 1 ? {::nsf::object::exists nx::Object} 1 ? {catch {nx::Object create nx::Object}} 1 ? {::nsf::object::exists nx::Object} 1 ? {catch {nx::Object create nx::Class}} 1 ? {::nsf::object::exists nx::Class} 1 ? {catch {nx::Class create nx::Object}} 1 ? {catch {nx::Class create nx::Class}} 1 ? {catch {rename nx::Object ""}} 1 ? {::nsf::object::exists nx::Object} 1 ? {catch {rename nx::Object ""}} 1 ? {::nsf::object::exists nx::Object} 1 ? {catch {rename nx::Class ""}} 1 ? {::nsf::object::exists nx::Class} 1 # # Test overwriting of procs/methods # proc foo {} {;} # # Don't allow object to overwrite pre-existing proc/cmd, # which is not an object. # ? {catch {nx::Object create foo}} 1 rename foo "" nx::Object create foo { :object method bar {} {;} # # Don't allow subobject to overwrite object specific method # ? {catch {nx::Object create [self]::bar}} 1 } nx::Object create foo { nx::Object create [self]::bar # # Don't allow child-object to be overwritten by object specific cmd # ? {catch {:object forward bar somethingelse}} 1 ? {nsf::object::exists [self]::bar} 1 # # Don't allow child-object to be overwritten by object specific # scripted method # ? {catch {:object method bar {} {;}}} 1 ? {nsf::object::exists [self]::bar} 1 } foo destroy # # Test instances of diamond class structure. # # Leave class structure around until exit to test handling of # potentially duplicated entries during final cleanup # nx::Class create A nx::Class create B1 -superclass A nx::Class create B2 -superclass A nx::Class create C -superclass {B1 B2} ? {C create c1} ::c1 ? {A info instances -closure} ::c1 ::nsf::configure keepcmds 1 ::nx::Object create o {set :x 1} ? {o info vars} "x __cmd" ? {o eval {array get :__cmd}} "__initblock {set :x 1}" ::nsf::configure dtrace off puts stderr "===EXIT [info script]" # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/parameters.test000066400000000000000000002655001242365656200153070ustar00rootroot00000000000000# -*- Tcl -*- package require nx package require nx::test #::nx::configure defaultMethodCallProtection false set objectFilter "-object-filters:filterreg,slot=::nx::Object::slot::object-filters,slotset,method=object-filter,0..n" set objectMixin "-object-mixins:mixinreg,slot=::nx::Object::slot::object-mixins,slotset,method=object-mixin,0..n" set initBlock "__initblock:cmd,optional,nodashalnum" set filter "-filters:filterreg,slot=::nx::Class::slot::filters,slotset,method=class-filter,0..n" set ::trailer "$objectMixin $objectFilter -class:class,alias,method=::nsf::methods::object::class $initBlock" nx::test case dummy { ? {::namespace current} :: set o [Object create o] ? {::nsf::object::exists ::o} 1 } ? {::nsf::object::exists ::o} 0 # # simple test case for parameter passing # nx::test case syntax { ::nx::Class create C ? {::nsf::method::alias C} \ {required argument 'methodName' is missing, should be: ::nsf::method::alias /object/ ?-per-object? /methodName/ ?-frame method|object|default? ?-protection call-protected|redefine-protected|none? /cmdName/} ? {::nsf::method::alias C foo ::set} "::nsf::classes::C::foo" ? {::nsf::method::alias C foo ::set 1} \ {invalid argument '1', maybe too many arguments; should be "::nsf::method::alias /object/ ?-per-object? /methodName/ ?-frame method|object|default? ?-protection call-protected|redefine-protected|none? /cmdName/"} ? {C eval {:property x -class D}} {invalid argument 'D', maybe too many arguments; should be "::C property ?-accessor /value/? ?-configurable /boolean/? ?-incremental? ?-class /value/? /spec/ ?/initblock/?"} "Test whether the colon prefix is suppressed" } ####################################################### # parametercheck ####################################################### nx::test configure -count 1000 nx::test case parametercheck { nx::Object create o1 nx::Class create C { :property a :property {b:boolean} :property {c 1} } C create c1 nx::Class create M c1 object mixins set M ? {::nsf::object::exists o1} 1 ? {::nsf::object::exists o1000} 0 ? {::nsf::is class C} 1 ? {C info has type ::nx::Class} 1 ? {::nsf::is baseclass ::nx::Object} 1 #? {::nx::Object info is baseclass} 1 ? {::nsf::is baseclass C} 0 #? {C info is baseclass} 0 ? {::nsf::is class ::nx::Object} 1 ? {::nsf::is ::nx::Object class} {invalid value constraints "::nx::Object"} ? {::nsf::is object o1} 1 ? {::nsf::is object o1} 1 ? {::nsf::is object o1000} 0 ? {::nsf::is -complain object o1000} {expected object but got "o1000"} ? {::nsf::is integer 1} 1 ? {::nsf::is object,type=::C c1} 1 ? {::nsf::is -complain object,type=::C o} {expected object but got "o"} ? {::nsf::is object,type=::C o} 0 ? {c1 info has mixin ::M} 1 ? {c1 info has mixin ::M1} {expected class but got "::M1" for parameter "class"} ? {c1 info has type C} 1 ? {c1 info has type C1} {expected class but got "C1" for parameter "class"} ? {c1 ::nsf::methods::object::info::hastype C} 1 ? {::nsf::dispatch c1 ::nsf::methods::object::info::hastype C} 1 ? {::nsf::is object o1} 1 ? {::nsf::is object o100} 0 ? {::nsf::is integer 1} 1 ? {::nsf::is object,type=::C c1} 1 ? {::nsf::is object,type=::C o} 0 # test built-in converter via ::nsf::is ? {::nsf::is boolean 1} 1 ? {::nsf::is boolean on} 1 ? {::nsf::is boolean true} 1 ? {::nsf::is boolean t} 1 ? {::nsf::is boolean f} 1 ? {::nsf::is boolean a} 0 ? {::nsf::is integer 0x0} 1 ? {::nsf::is integer 0xy} 0 # built in converter, but not allowed ? {::nsf::is switch 1} {invalid value constraints "switch"} ? {::nsf::is superclass M} {invalid value constraints "superclass"} # don't allow convert; # well we have to allow it, since "-returns" uses the same mechanism #? {::nsf::is integer,convert 1} {invalid value constraints "integer,convert"} # tcl checker ? {::nsf::is upper ABC} 1 ? {::nsf::is upper Abc} 0 ? {::nsf::is lower Abc} 0 ? {::nsf::is lower abc} 1 #? {::nsf::is type c1 C} 1 #? {::nsf::is type o C} 0 #? {::nsf::is object o -type C} 0 #? {::nsf::is object o -hasmixin C} 0 # scripted checker ? {::nsf::is metaclass ::nx::Class} 1 ? {::nsf::is metaclass ::nx::Object} 0 ? {::nsf::is -complain class o1} {expected class but got "o1"} ? {::nsf::is class o1} 0 ? {::nsf::is -complain class nx::test} 1 ? {::nsf::is -complain object,1..* [list o1 nx::test]} 1 ? {::nsf::is -complain integer,1..* [list 1 2 3]} 1 ? {::nsf::is -complain integer,1..* [list 1 2 3 a]} \ {invalid value in "1 2 3 a": expected integer but got "a"} ? {::nsf::is -complain object,type=::C c1} 1 ? {::nsf::is -complain object,type=::C o} \ {expected object but got "o"} \ "object, but different type" ? {::nsf::is -complain object,type=::C c} \ {expected object but got "c"} \ "no object" ? {::nsf::is -complain object,type=::nx::Object c1} 1 "general type" # do not allow "currently unknown" user defined types in parametercheck ? {::nsf::is -complain in1 aaa} {invalid value constraints "in1"} ? {::nsf::is -complain lower c} 1 "lower case char" ? {::nsf::is -complain lower abc} 1 "lower case chars" ? {::nsf::is -complain lower Abc} {expected lower but got "Abc"} ? {string is lower abc} 1 "tcl command 'string is lower'" ? {::nsf::is -complain {i:integer 1} 2} {invalid value constraints "i:integer 1"} } nx::test configure -count 10 nx::test case multiple-method-checkers { nx::Object create o { :public object method foo {} { ::nsf::is metaclass ::XYZ ::nsf::is metaclass ::nx::Object } :public object method bar {} { ::nsf::is metaclass ::XYZ ::nsf::is metaclass ::XYZ } :public object method bar2 {} { ::nsf::is metaclass ::nx::Object ::nsf::is metaclass ::nx::Object } } ? {o foo} 0 ? {o bar} 0 ? {::nsf::is metaclass ::XYZ} 0 ? {::nsf::is metaclass ::nx::Object} 0 ? {o foo} 0 ? {o bar2} 0 } ####################################################### # param manager ####################################################### nx::test configure -count 10000 nx::test case param-manager { nx::Object create ::paramManager { :object method type=sex {name value} { return "agamous" } } ? {::nsf::is -complain sex,slot=::paramManager female} "1" } ####################################################### # cononical feature table ####################################################### # # parameter options # required # optional # multivalued # noarg # arg= # substdefault: if no value given, subst on default (result is substituted value); # susbt cmd can use variable resolvers, # works for scripted/c-methods and obj-parm, # autmatically set by "$slot toParameterSpec" if default contains "[" ... "]". # # initcmd: evaluate body in an xotcl nonleaf frame, called via configure # (example: last arg on create) # alias,forward call specified method in an xotcl nonleaf frame, called via configure; # specified value is the first argument unless "noarg" is used # (example: -noinit). # # parameter type multivalued required noarg type= arg= parametercheck methodParm objectParm # initcmd NO YES NO NO NO NO NO/POSSIBLE YES # alias,forward NO YES YES NO YES NO NO/POSSIBLE YES # # relation NO YES NO NO YES NO NO YES # stringtype YES YES NO NO NO YES YES YES # # switch NO NO NO NO NO NO YES NO # integer YES YES NO NO NO YES YES YES # boolean YES YES NO NO NO YES YES YES # object YES YES NO YES NO YES YES YES # class YES YES NO YES NO YES YES YES # # userdefined YES YES NO NO YES YES YES YES # # tclObj + converterArg (alnum..xdigit) Attribute ... -type alnum # object + converterArg (some class, e.g. ::C) Attribute ... -type ::C Attribute -type object -arg ::C # class + converterArg (some metaclass, e.g. ::M) Attribute -type class -arg ::M # # #::xotcl::Slot { # {name "[namespace tail [::xotcl::self]]"} # {methodname} # {domain "[lindex [regexp -inline {^(.*)::slot::[^:]+$} [::xotcl::self]] 1]"} # {defaultmethods {get assign}} # {manager "[::xotcl::self]"} # {multivalued false} # {per-object false} # {required false} # default # type # } -- No instances # # ::xotcl::RelationSlot -superclass ::xotcl::Slot { # {multivalued true} # {type relation} # {elementtype ::nx::Class} # } -- sample instances: class superclass, mixin filter # # ::nx::VariableSlot -superclass ::xotcl::Slot { # {value_check once} # defaultcmd # valuecmd # valuechangedcmd # arg # } -- typical object parameters # # MethodParameterSlot -attributes {type required multivalued noarg arg} # -- typical method parameters ####################################################### # objectparameter ####################################################### nx::test configure -count 10 nx::test case objectparameter { nx::Class create C { :property a :property {b:boolean} :property {c 1} } C create c1 ? {C eval :__object_configureparameter} \ "-mixins:mixinreg,slot=::nx::Class::slot::mixins,slotset,method=class-mixin,0..n {-superclasses:class,alias,method=::nsf::methods::class::superclass,1..n ::nx::Object} -filters:filterreg,slot=::nx::Class::slot::filters,slotset,method=class-filter,0..n -object-mixins:mixinreg,slot=::nx::Object::slot::object-mixins,slotset,method=object-mixin,0..n -object-filters:filterreg,slot=::nx::Object::slot::object-filters,slotset,method=object-filter,0..n -class:class,alias,method=::nsf::methods::object::class __initblock:cmd,optional,nodashalnum" #### TOOD: remove or add #? {c1 eval :__object_configureparameter} \ # "::c1: unable to dispatch method '__objectparameter'" } ####################################################### # reclass to nx::Object, no need to do anything on caching ####################################################### nx::test case reclass { nx::Class create C { :property a :property {b:boolean} :property {c 1} } C create c1 ? {c1 info lookup slots -source application} "::C::slot::a ::C::slot::b ::C::slot::c" nsf::relation::set c1 class nx::Object ? {c1 info lookup slots -source application} "" nx::Class create D -superclass C {:property d:required} D create d1 -d 100 ? {d1 info lookup slots -source application} \ "::D::slot::d ::C::slot::a ::C::slot::b ::C::slot::c" ? {d1 eval :__object_configureparameter} \ "-d:required -a -b:boolean {-c 1} $::trailer" } ####################################################### # Add mixin ####################################################### nx::test case objparam-mixins { nx::Class create C { :property a :property {b:boolean} :property {c 1} } nx::Class create D -superclass C { :property d:required } D create d1 -d 100 nx::Class create M { :property m1 :property m2 :property b } nx::Class create M2 { :property b2 } D mixins set M ? {d1 eval :__object_configureparameter} \ "-b -m1 -m2 -d:required -a {-c 1} $::trailer" \ "mixin added" M mixins set M2 ? {d1 eval :__object_configureparameter} \ "-b2 -b -m1 -m2 -d:required -a {-c 1} $::trailer" \ "transitive mixin added" D mixins set "" #we should have again the old interface ? {d1 eval :__object_configureparameter} \ "-d:required -a -b:boolean {-c 1} $::trailer" C mixins set M ? {d1 eval :__object_configureparameter} \ "-b2 -b -m1 -m2 -d:required -a {-c 1} $::trailer" \ "mixin added" C mixins set "" #we should have again the old interface ? {d1 eval :__object_configureparameter} \ "-d:required -a -b:boolean {-c 1} $::trailer" } ####################################################### # test passed arguments ####################################################### nx::test case passed-arguments { nx::Class create C { :property a :property b:boolean :property {c 1} } nx::Class create D -superclass C {:property d:required} ? {catch {D create d1 -d 123}} 0 "create d1 with required argument given" ? {catch {D create d1}} 1 "create d1 without required argument given" #puts stderr current=[namespace current] ? {D create d1} \ {required argument 'd' is missing, should be: ::d1 configure -d /value/ ?-a /value/? ?-b /boolean/? ?-c /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} ? {D create d2 -d x -b a} \ {expected boolean but got "a" for parameter "-b"} \ "create d2 without required argument given" D create d1 -d 1 D public method foo {-b:boolean -r:required,int {-x:int aaa} {-object:object} {-class:class}} { #if {[info exists x]} {puts stderr x=$x} } ? {d1 foo} \ {required argument 'r' is missing, should be: ::d1 foo ?-b /boolean/? -r /integer/ ?-x /integer/? ?-object /object/? ?-class /class/?} \ "call method without a required argument" ? {d1 foo -r a} \ {expected integer but got "a" for parameter "-r"} \ "required argument is not integer" ? {d1 foo -r 1} \ {expected integer but got "aaa" for parameter "-x"} \ "default value is not of type integer" ? {d1 foo -r 1 -x 1 -object d1} \ "" \ "pass object" ? {d1 foo -r 1 -x 1 -object d11} \ {expected object but got "d11" for parameter "-object"} \ "pass non-existing object" ? {d1 foo -r 1 -x 1 -class D} \ "" \ "pass class" ? {d1 foo -r 1 -x 1 -class d1} \ {expected class but got "d1" for parameter "-class"} \ "pass object instead of class" ? {d1 foo -r 1 -x 1 -class D11} \ {expected class but got "D11" for parameter "-class"} \ "pass non-existing class" ? {D public method foo {a:double} {return $a}} \ {::nsf::classes::D::foo} \ "allow 'string is XXXX' for argument checking" ? {d1 foo 1} 1 "check int as double" ? {d1 foo 1.1} 1.1 "check double as double" ? {d1 foo 1.1a} {expected double but got "1.1a" for parameter "a"} "check non-double as double" ? {D info method parameters foo} a:double } ####################################################### # non required positional arguments ####################################################### nx::test case non-reg-args { nx::Class create D D create d1 D public method foo {a b:optional c:optional} { return "[info exists a]-[info exists b]-[info exists c]" } ? {d1 foo 1 2} "1-1-0" "omit optional argument" ? {d1 foo 1} "1-0-0" "omit optional arguments" # non required positional arguments and args D public method foo {a b:optional c:optional args} { return "[info exists a]-[info exists b]-[info exists c]-[info exists args]" } ? {d1 foo 1 2} "1-1-0-1" "omit optional argument" ? {d1 foo 1} "1-0-0-1" "omit optional arguments" } ####################################################### # multivalued arguments ####################################################### nx::test case multivalued { nx::Class create D D create d1 nx::Object create o D public method foo {m:integer,0..n} { return $m } ? {d1 foo ""} "" "emtpy list" ? {d1 foo 1} "1" "single value" ? {d1 foo {1 2}} "1 2" "multiple values" ? {d1 foo {1 a 2}} \ {invalid value in "1 a 2": expected integer but got "a" for parameter "m"} \ "multiple values with wrong value" D public method foo {m:object,0..n} { return $m } ? {d1 foo ""} "" "emtpy list" ? {d1 foo o} "o" "single value" ? {d1 foo {o d1 x}} \ {invalid value in "o d1 x": expected object but got "x" for parameter "m"} \ "multiple values" nx::Class create Foo { :property ints:integer,1..* } ? {Foo create foo -ints {1 2}} "::foo" ? {Foo create foo -ints {1 a 2}} {invalid value in "1 a 2": expected integer but got "a" for parameter "-ints"} # make slot incremental Foo property -incremental ints:integer,1..* # TODO? the following does not work. Should we revive it? #Foo::slot::ints eval { # set :incremental 1 # :reconfigure #} Foo create foo -ints {1 2} ? {foo ints add 0} "0 1 2" ? {foo ints add a} {expected integer but got "a" for parameter "value"} } ####################################################### # subst default tests ####################################################### nx::test case subst-default { nx::Class create D { :property {c 1} :property {d 2} :create d1 :public method bar { {-s:substdefault "[current]"} {-literal "[current]"} {-c:substdefault "[:c]"} {-d:integer,substdefault "$d"} } { return $s-$literal-$c-$d } } ? {d1 bar -c 1} {::d1-[current]-1-2} "substdefault in method parameter" nx::Class create Bar -superclass D { :property {s "[current]"} :property {literal "\\[current\\]"} :property {c "[:info class]"} :property {d "literal $d"} } ? {Bar property -accessor public ss:switch} "::nsf::classes::Bar::ss" Bar create bar1 #puts stderr [bar1 __objectparameter] ? {subst {[bar1 cget -s]-[bar1 cget -literal]-[bar1 cget -c]-[bar1 cget -d]}} \ {::bar1-[current]-::Bar-literal $d} \ "substdefault in object parameter 1" Bar create bar2 ? {subst {[bar2 cget -s]-[bar2 cget -literal]-[bar2 cget -c]-[bar2 cget -d]}} \ {::bar2-[current]-::Bar-literal $d} \ "substdefault in object parameter 2" # Observations: # 1) syntax for "-attributes" and method parameter is quite different. # it would be nice to be able to specify the objparameters in # the same syntax as the method parameters. # # 1a) Especially specifying "-" in front of a -attributes or not might # be confusing. # # 1b) Positional args for obj parameter and arguments for init # might be confusing as well. Should we forget about # passing arguments to init? # # 2) substdefault for '$' in -attributes defaults does not make much sense. # deactivated for now; otherwise we would need "\\" D public method bar { {-s:substdefault "[current]"} {-literal "[current]"} {-c:substdefault "[:c]"} {-d:integer,substdefault "$d"} {-switch:switch} {-optflag} x y:integer {z 1} } { return $s-$literal-$c-$d } ? {D info method args bar} {s literal c d switch optflag x y z} "all args" ? {D info method parameters bar} \ {{-s:substdefault "[current]"} {-literal "[current]"} {-c:substdefault "[:c]"} {-d:integer,substdefault "$d"} -switch:switch -optflag x y:integer {z 1}} \ "query method parameter" ? {D public method foo {s:switch} {return 1}} \ {invalid parameter type "switch" for argument "s"; type "switch" only allowed for non-positional arguments} D public method foo {a b {-c 1} {-d} x {-end 100}} { set result [list] foreach v [[current class] info method args [current method]] { lappend result $v [info exists $v] } return $result } ? {d1 foo 1 2 3} \ "a 1 b 1 c 1 d 0 x 1 end 1" \ "parse multiple groups of nonpos args" D public method foo {a b c {end 100}} { set result [list] foreach v [[current class] info method args [current method]] { lappend result $v [info exists $v] } return $result } ? {d1 foo 1 2 3} \ "a 1 b 1 c 1 end 1" \ "query arguments with default, no paramdefs needed" ####################################################### # Query method parameter ####################################################### ? {D info method parameters foo} \ "a b c {end 100}" \ "query instparams with default, no paramdefs needed" ? {nx::Class info method parameters method} \ "name arguments:parameter,0..* -checkalways:switch -returns body" \ "query instparams for scripted method 'method'" ? {nx::Object info method parameters ::nsf::method::forward} \ "object:object -per-object:switch method -default -earlybinding:switch -onerror -prefix -frame -verbose:switch target:optional args" \ "query parameter for C-defined cmd 'nsf::forward'" nx::Object require method autoname ? {nx::Object info method parameters autoname} \ "-instance:switch -reset:switch name" \ "query parameter for C-defined method 'autoname'" D method "a b" {x y} {return $x-$y} D object method "c d" {x y z} {return $x-$y} ? {D info method parameters "a b"} "x y" ? {D info object method parameters "c d"} "x y z" } ####################################################### # user defined parameter value checkers ####################################################### nx::test case user-value-checker { nx::Class create D {:property d} D create d1 # Create a user-defined value checker for method parameters, # without extra argument ::nx::methodParameterSlot object method type=mytype {name value} { if {$value < 1 || $value > 3} { error "value '$value' of parameter $name is not between 1 and 3" } } D public method foo {a:mytype} { return a=$a } d1 foo 1 ? {d1 foo 10} \ "value '10' of parameter a is not between 1 and 3" \ "value not between 1 and 3" D public method foo {a:unknowntype} { return $a } ? {d1 foo 10} \ "::nx::methodParameterSlot: unable to dispatch method 'type=unknowntype'" \ "missing type checker" # # Create a user-defined value-checker for method parameters, # with a extra argument # ::nx::methodParameterSlot object method type=in {name value arg} { if {$value ni [split $arg |]} { error "value '$value' of parameter $name not in permissible values $arg" } return $value } # # Trival test case # D public method foo {a:in,arg=a|b|c} { return a=$a } ? {d1 foo a} "a=a" ? {d1 foo 10} \ "value '10' of parameter a not in permissible values a|b|c" \ "invalid value" # # Test case with positional and non-positional arguments, and default # D public method foo {a:in,arg=a|b|c b:in,arg=good|bad {-c:in,arg=a|b a}} { return a=$a,b=$b,c=$c } ? {d1 foo a good -c b} "a=a,b=good,c=b" ? {d1 foo a good} "a=a,b=good,c=a" ? {d1 foo b "very good"} \ "value 'very good' of parameter b not in permissible values good|bad" \ "invalid value (not included)" # # Create a user-defined value checker for method parameters, # without extra argument # ::nx::methodParameterSlot object method type=commaRange {name value arg} { lassign [split $arg ,] min max if {$value < $min || $value > $max} { error "value '$value' of parameter $name not between $min and $max" } return $value } D public method foo {a:commaRange,arg=1,,3} { return a=$a } ? {d1 foo 2} "a=2" ? {d1 foo 10} \ "value '10' of parameter a not between 1 and 3" \ "invalid value" # # two commas at the end # D public method foo {a:commaRange,arg=1,,} {return a=$a} ? {d1 foo 2} {value '2' of parameter a not between 1 and } # # one comma at the end # D public method foo {a:commaRange,arg=1,} {return a=$a} ? {d1 foo 2} {value '2' of parameter a not between 1 and } # # Classical range check # ::nx::methodParameterSlot object method type=range {name value arg} { lassign [split $arg -] min max if {$value < $min || $value > $max} { error "value '$value' of parameter $name not between $min and $max" } return $value } D public method foo {a:range,arg=1-3 {-b:range,arg=2-6 3} c:range,arg=5-10} { return a=$a,b=$b,c=$c } ? {d1 foo 2 -b 4 9} "a=2,b=4,c=9" ? {d1 foo 2 10} "a=2,b=3,c=10" ? {d1 foo 2 11} \ "value '11' of parameter c not between 5 and 10" \ "invalid value" # define type twice ? {D public method foo {a:int,range,arg=1-3} {return a=$a}} \ "parameter option 'range' unknown for parameter type 'integer'" \ "invalid value" # # handling of arg with spaces/arg as list # ::nx::methodParameterSlot public object method type=list {name value arg} { #puts $value/$arg return $value } # handling spaces in "arg" is not not particular nice D public method foo {{"-a:list,arg=2 6" 3} {"b:list,arg=5 10"}} { return a=$a,b=$b } ? {d1 foo -a 2 10} "a=2,b=10" } ####################################################### # testing object types in method parameters ####################################################### nx::test case mp-object-types { nx::Class create C nx::Class create D -superclass C {:property d} nx::Class create M nx::Class create M2 D create d1 -d 1 C create c1 -object-mixins M C create c2 -object-mixins {{M -guard true}} C create c3 -object-mixins {M ::M2} C create c4 -object-mixins {{M -guard 1} M2} C create c5 -object-mixins {M {M2 -guard 2}} nx::Object create o ? {c1 object mixins get} ::M ? {c1 object mixins guard ::M} "" ? {c2 object mixins get} {{::M -guard true}} ? {c2 object mixins classes} ::M ? {c2 object mixins guard ::M} "true" ? {c3 object mixins get} {::M ::M2} ? {c3 object mixins guard M} "" ? {c3 object mixins guard M2} "" ? {c4 object mixins get} {{::M -guard 1} ::M2} ? {c4 object mixins classes} {::M ::M2} ? {c4 object mixins classes M} ::M ? {c4 object mixins classes M1} "" ? {c4 object mixins guard M} "1" ? {c4 object mixins guard M2} "" ? {c5 object mixins get} {::M {::M2 -guard 2}} ? {c5 object mixins classes} {::M ::M2} ? {c5 object mixins classes M} {::M} ? {c5 object mixins classes M1} "" ? {c5 object mixins guard M} "" ? {c5 object mixins guard M2} "2" D public method foo-base {x:baseclass} {return $x} D public method foo-class {x:class} {return $x} D public method foo-object {x:object} {return $x} D public method foo-meta {x:metaclass} {return $x} D public method foo-type {x:object,type=::C} {return $x} ? {D info method parameters foo-base} "x:baseclass" ? {D info method parameters foo-type} "x:object,type=::C" ? {d1 foo-base ::nx::Object} "::nx::Object" ? {d1 foo-base C} \ {expected baseclass but got "C" for parameter "x"} \ "not a base class" ? {d1 foo-class D} "D" ? {d1 foo-class xxx} \ {expected class but got "xxx" for parameter "x"} \ "not a class" ? {d1 foo-class o} \ {expected class but got "o" for parameter "x"} \ "not a class" ? {d1 foo-meta ::nx::Class} "::nx::Class" ? {d1 foo-meta ::nx::Object} \ {expected metaclass but got "::nx::Object" for parameter "x"} \ "not a base class" ? {d1 foo-object o} "o" ? {d1 foo-object xxx} \ {expected object but got "xxx" for parameter "x"} \ "not an object" ? {d1 foo-type d1} "d1" ? {d1 foo-type c1} "c1" ? {d1 foo-type o} \ {expected object of type ::C but got "o" for parameter "x"} \ "o not of type ::C" } ####################################################### # substdefault ####################################################### nx::test case substdefault { nx::Class create S { :property {x 1} :property {y b} :property {z {1 2 3}} } S create s1 { :public object method foo {{y:substdefault ${:x}}} { return $y } :public object method bar {{y:integer,substdefault ${:x}}} { return $y } :public object method baz {{x:integer,substdefault ${:y}}} { return $x } :public object method boz {{x:integer,0..n,substdefault ${:z}}} { return $x } } ? {s1 foo} 1 ? {s1 foo 2} 2 ? {S object method foo {a:substdefault} {return 1}} \ {parameter option substdefault specified for parameter "a" without default value} ? {s1 bar} 1 ? {s1 bar 3} 3 ? {s1 bar a} {expected integer but got "a" for parameter "y"} ? {s1 baz} {expected integer but got "b" for parameter "x"} ? {s1 baz 20} 20 s1 configure -y 100 ? {s1 baz} 100 ? {s1 baz 101} 101 ? {s1 boz} {1 2 3} s1 configure -z {1 x 100} ? {s1 boz} {invalid value in "1 x 100": expected integer but got "x" for parameter "x"} ? {s1 boz {100 200}} {100 200} set ::aaa 100 ? {s1 public object method foo {{a:substdefault $::aaa}} {return $a}} ::s1::foo ? {s1 foo} 100 unset ::aaa ? {s1 foo} {can't read "::aaa": no such variable} ? {s1 public object method foo {{a:substdefault $aaa}} {return $a}} ::s1::foo ? {s1 foo} {can't read "aaa": no such variable} ? {s1 public object method foo {{a:substdefault [current]}} {return $a}} ::s1::foo ? {s1 foo} ::s1 } ####################################################### # testing substdefault for object parameters ####################################################### nx::test case substdefault-objparam { nx::Class create Bar { # simple, implicit substdefault :property {s0 "[current]"} # explicit substdefault :property {s1:substdefault "[current]"} # unneeded double substdefault :property {s2:substdefault,substdefault "[current]"} # substdefault with incremental :property -incremental {s3:substdefault "[current]"} } Bar create ::b ? {b cget -s0} "::b" ? {b cget -s1} "::b" ? {b cget -s2} "::b" ? {b cget -s3} "::b" } # # Test call of configure within constructor # nx::test case parameter-alias-default { ::nsf::method::require nx::Object __configure nx::Class create C { :property {a ""} :property {b 1} :method init {} { :__configure -b 1 } :create c1 :create c2 -a 0 } ? {::c1 eval {set :a}} "" ? {::c1 eval {set :b}} 1 ? {::c2 eval {set :a}} 0 ? {::c2 eval {set :b}} 1 } ####################################################### # testing object types in object parameters ####################################################### nx::test case op-object-types { nx::Class create C nx::Class create D -superclass C {:property d} nx::Class create MC -superclass nx::Class MC create MC1 nx::Class create M D create d1 -d 1 C create c1 -object-mixins M nx::Object create o nx::Class create ParamTest { :property o:object :property c:class :property c1:class,type=::MC :property d:object,type=::C :property d1:object,type=C :property m:metaclass :property b:baseclass :property u:upper :property us:upper,1..* :property -incremental us2:upper,1..* :property {x:object,1..* {o}} } ? {ParamTest info lookup parameters create o} "-o:object" ? {ParamTest info lookup parameters create c} "-c:class" ? {ParamTest info lookup parameters create c1} "-c1:class,type=::MC" ? {ParamTest info lookup parameters create d} "-d:object,type=::C" ? {ParamTest info lookup parameters create d1} "-d1:object,type=::C" ? {ParamTest info lookup parameters create x} "{-x:object,1..* o}" ? {ParamTest info lookup parameters create u} "-u:upper,slot=::ParamTest::slot::u" ? {ParamTest info lookup parameters create us} "-us:upper,slot=::ParamTest::slot::us,1..*" ? {ParamTest create p -o o} ::p ? {ParamTest create p -o xxx} \ {expected object but got "xxx" for parameter "-o"} \ "not an object" ? {ParamTest create p -c C} ::p "class" ? {ParamTest create p -c o} \ {expected class but got "o" for parameter "-c"} \ "not a class" ? {ParamTest create p -c1 MC1} ::p "instance of meta-class MC" ? {ParamTest create p -c1 C} \ {expected class of type ::MC but got "C" for parameter "-c1"} \ "not an instance of meta-class MC" ? {ParamTest create p -d d1} ::p ? {ParamTest create p -d1 d1} ::p ? {ParamTest create p -d c1} ::p ? {ParamTest create p -d o} \ {expected object of type ::C but got "o" for parameter "-d"} \ "o not of type ::C" #? {ParamTest create p -mix c1} ::p #? {ParamTest create p -mix o} \ {expected object with mixin M but got "o" for parameter "mix"} \ "does not have mixin M" ? {ParamTest create p -u A} ::p ? {ParamTest create p -u c1} {expected upper but got "c1" for parameter "-u"} ? {ParamTest create p -us {A B c}} \ {invalid value in "A B c": expected upper but got "c" for parameter "-us"} ParamTest::slot::us eval { set :incremental 1 :reconfigure } ? {ParamTest create p -us {A B} -us2 {A B}} ::p ? {p us add C end} "A B C" ? {p us2 add C end} "A B C" ? {p configure -o o} \ "" \ "value is an object" ? {p configure -o xxx} \ {expected object but got "xxx" for parameter "-o"} \ "value is not an object" # # define multivalued property "os" via instance variables of the # slot object # ParamTest eval { #:property -accessor public os { # :type object # :multiplicity 1..n #} :property -accessor public os:object,1..n } ? {ParamTest info method definition os} "::ParamTest public forward os -prefix value= ::ParamTest::slot::os %1 %self os" ? {p os set o} \ "o" \ "value is a list of objects (1 element)" ? {p os set {o c1 d1}} \ "o c1 d1" \ "value is a list of objects (multiple elements)" ? {p os set {o xxx d1}} \ {invalid value in "o xxx d1": expected object but got "xxx" for parameter "value"} \ "list with invalid object" } ####################################################### # application specific multivalued converter ####################################################### nx::test case multivalued-app-converter { ::nx::methodParameterSlot public object method type=sex {name value args} { #puts stderr "[current] slot specific converter" switch -glob $value { m* {return m} f* {return f} default {error "expected sex but got $value"} } } nx::Class create C { :public method foo {s:sex,0..*,convert} {return $s} :public method bar {s:sex,0..*} {return $s} } C create c1 ? {c1 foo {male female mann frau}} "m f m f" ? {c1 bar {male female mann frau}} "male female mann frau" nx::Object create tmpObj tmpObj object method type=mType {name value arg:optional} { if {$value} { error "expected false but got $value" } # Note that this converter does NOT return a value; it converts all # values into emtpy strings. } ? {::nsf::is -complain mType,slot=::tmpObj,0..* {1 0}} \ {invalid value in "1 0": expected false but got 1} \ "fail on first value" ? {::nsf::is -complain mType,slot=::tmpObj,0..* {0 0 0}} 1 "all pass" ? {::nsf::is -complain mType,slot=::tmpObj,0..* {0 1}} \ {invalid value in "0 1": expected false but got 1} \ "fail o last value" } ####################################################### # application specific multivalued converter ####################################################### nx::test case shadowing-app-converter { nx::Object create mySlot { :public object method type=integer {name value arg:optional} { return [expr {$value + 1}] } } nx::Object create o { :public object method foo {x:integer,slot=::mySlot,convert} { return $x } } ? {::nsf::is -complain integer,slot=::mySlot 1} 1 ? {o foo 3} 4 } ####################################################### # allow empty values ####################################################### nx::test case allow-empty { nx::Object create o1 nx::Object create o2 nx::Object create o3 nx::Object create o { :public object method foo {x:integer,0..1 y:integer os:object,0..*} { return $x } } ? {o foo 1 2 {o1 o2}} 1 "all values specified" ? {o foo "" 2 {o1 o2}} "" "first is empty" ? {o foo 1 "" {o1 o2}} {expected integer but got "" for parameter "y"} "second is empty" ? {o foo 1 2 {}} 1 "empty list" ? {o info object method parameters foo} "x:integer,0..1 y:integer os:object,0..*" o public object method foo {x:integer,0..1 y:integer os:object,1..*} {return $x} ? {o foo 1 2 {o1 "" o2}} {invalid value in "o1 "" o2": expected object but got "" for parameter "os"} \ "list contains empty value" ? {o foo "" 2 {}} {invalid value for parameter 'os': list is not allowed to be empty} \ "empty int, empty list of objects" } ####################################################### # slot specific converter ####################################################### nx::test case slot-specfic-converter { nx::Class create Person { # :property -accessor public sex { # :type "sex" # :convert true # :object method type=sex {name value} { # #puts stderr "[self] slot specific converter" # switch -glob $value { # m* {return m} # f* {return f} # default {error "expected sex but got $value"} # } # } # } :property -accessor public sex:sex,convert { :object method type=sex {name value} { #puts stderr "[self] slot specific converter" switch -glob $value { m* {return m} f* {return f} default {error "expected sex but got $value"} } } } } Person create p1 -sex male ? {p1 cget -sex} m ? {p1 sex get} m Person public method foo {s:sex,slot=::Person::slot::sex,convert} {return $s} ? {p1 foo male} m ? {p1 sex set male} m } ####################################################### # test for setters with parameters os ####################################################### nx::test case setters { nx::Object create o nx::Class create C ? {::nsf::method::setter ::o :a} {invalid setter name ":a" (must not start with a dash or colon)} ? {::nsf::method::setter o a} "::o::a" ? {::nsf::method::setter C c} "::nsf::classes::C::c" ? {o info object method definition a} "::o public object setter a" ? {o info object method parameters a} "a" ? {o info object method args a} "a" ? {C info method definition c} "::C public setter c" ? {o a 1} "1" ? {::nsf::method::setter o a:integer} "::o::a" ? {::nsf::method::setter o ints:integer,1..*} "::o::ints" ? {::nsf::method::setter o o:object} "::o::o" ? {o info object method registrationhandle ints} "::o::ints" ? {o info object method definition ints} "::o public object setter ints:integer,1..*" ? {o info object method parameters ints} "ints:integer,1..*" ? {o info object method args ints} "ints" ? {o info object method registrationhandle o} "::o::o" ? {o info object method definition o} "::o public object setter o:object" ? {o info object method parameters o} "o:object" ? {o info object method args o} "o" ? {o a 2} 2 ? {o a hugo} {expected integer but got "hugo" for parameter "a"} ? {o ints {10 100 1000}} {10 100 1000} ? {o ints hugo} {invalid value in "hugo": expected integer but got "hugo" for parameter "ints"} ? {o o o} o ? {::nsf::method::setter o {d default}} {parameter "d" is not allowed to have default "default"} ? {::nsf::method::setter o -x} {invalid setter name "-x" (must not start with a dash or colon)} } ####################################################### # test for slot-optimizer ####################################################### nx::test configure -count 1000 nx::test case slot-optimizer { nx::Class create C { :property -accessor public a :property -accessor public b:integer :property -accessor public c:integer,0..n } C create c1 ? {c1 a set 1} 1 ? {c1 b set 1} 1 ? {c1 c set 1} 1 } nx::test configure -count 10 nx::test case slot-nosetter { nx::Class create C { :property -accessor public a :property -accessor none b:integer :property -accessor none {c ""} } ? {C create c1 -a 1 -b 2} ::c1 ? {c1 info vars} "a b c" ? {c1 a set 100} 100 ? {c1 b 101} {::c1: unable to dispatch method 'b'} ? {c1 c 102} {::c1: unable to dispatch method 'c'} } nx::test configure -count 1000 nx::test case check-arguments { nx::Class create Foo { :public method noarg {} {return ""} :public method onearg {x} {return $x} :public method intarg {x:integer} {return $x} :public method intsarg {x:integer,1..*} {return $x} :public method boolarg {x:boolean} {return $x} :public method classarg {x:class} {return $x} :public method upperarg {x:upper} {return $x} :public method metaclassarg {x:metaclass} {return $x} :create f1 } ? {f1 noarg} "" ? {f1 onearg 1} 1 # built-in checker ? {f1 intarg 1} 1 ? {f1 intarg a} {expected integer but got "a" for parameter "x"} ? {f1 intsarg {10 11 12}} {10 11 12} ? {f1 intsarg {10 11 1a2}} {invalid value in "10 11 1a2": expected integer but got "1a2" for parameter "x"} ? {f1 boolarg 1} 1 ? {f1 boolarg a} {expected boolean but got "a" for parameter "x"} ? {f1 classarg ::Foo} ::Foo ? {f1 classarg f1} {expected class but got "f1" for parameter "x"} # tcl checker ? {f1 upperarg ABC} ABC ? {f1 upperarg abc} {expected upper but got "abc" for parameter "x"} # scripted checker ? {f1 metaclassarg ::nx::Class} ::nx::Class ? {f1 metaclassarg ::Foo} {expected metaclass but got "::Foo" for parameter "x"} } nx::test case copy-with-required { nx::Class create C { :property n:required } C create c1 -n 1 ? {c1 copy c2} "::c2" } nx::test case slot-traces { ::nx::Object create o { :object property -accessor public a {set :defaultcmd { set _ 4 } } :object property -accessor public b {set :valuecmd { set _ 44 } } :object property -accessor public c {set :valuechangedcmd { ::nsf::var::set $obj $var 999 }} } ? {o a get} 4 ? {o b get} 44 ? {o c set 5} 999 ? {::nsf::object::property o hasperobjectslots} 1 o copy o2 ? {o a get} 4 ? {o b get} 44 ? {o c set 5} 999 ? {::nsf::object::property o2 hasperobjectslots} 1 ::nx::Class create C { :property -accessor public a {set :defaultcmd { set _ 4 } } :property -accessor public b {set :valuecmd { set _ 44 } } :property -accessor public c {set :valuechangedcmd { ::nsf::var::set $obj $var 999 }} :create c1 } ? {c1 a get} 4 ? {c1 b get} 44 ? {c1 c set 5} 999 c1 copy c2 ? {c2 a get} 4 ? {c2 b get} 44 ? {c2 c set 5} 999 C copy D D create d1 ? {d1 a get} 4 ? {d1 b get} 44 ? {d1 c set 5} 999 } nx::test case slot-trace-interaction { # # 1) Verify the controlled interactions between trace types # # per-object: # package req nx::serializer Object create o ? {o eval {info exists :a}} 0 ? {o object property {a 0} { set :defaultcmd {set _ 4} }} "defaultcmd can't be used together with default value" ? {o eval {info exists :a}} 0 ? {o eval {info exists :b}} 0 ? {o object property {b 0} { set :valuecmd {set _ 44} }} "valuecmd can't be used together with default value" ? {o eval {info exists :b}} 0 ? {o eval {info exists :c}} 0 ? {o object property c { set :defaultcmd {set _ 4} set :valuecmd {set _ 44} }} "valuecmd can't be used together with defaultcmd" ? {o eval {info exists :c}} 0 # # valuechangedcmd + default value are allowed # ? {o eval {info exists :a}} 0 o object property -accessor public {a 0} { set :valuechangedcmd {::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]]} } ? {o eval {info exists :a}} 1 ? {o a get} 0 ? {o a set 1} 2 ? {o a get} 2 ? {o a set 2} 3 ? {o eval {info exists :A}} 0 o object property {A 0} { set :valuechangedcmd {::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]]} } ? {o eval {info exists :A}} 1 ? {o cget -A} 0 ? {o configure -A 1} "" ? {o cget -A} 2 # per-class: Class create Klass ? {Klass property {a 0} { set :defaultcmd {set _ 4} }} "defaultcmd can't be used together with default value" ? {Klass property {b 0} { set :valuecmd {set _ 44} }} "valuecmd can't be used together with default value" ? {Klass property c { set :defaultcmd {set _ 4} set :valuecmd {set _ 44} }} "valuecmd can't be used together with defaultcmd" Klass property -accessor public {a 0} { set :valuechangedcmd {::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]]} } Klass create k ? {k eval {info exists :a}} 1 ? {k a get} 0; # should be 1, reflecting the corresponding per-object case above ? {k a set 1} 2 ? {k a get} 2 ? {k a set 2} 3 # # 2) Have initcmd scripts escaped from C-level argument checking (in # the per-class check) # # a) against scalar checkers (as a simplistic case) Klass property b:boolean { set :valuechangedcmd {set _ tr1e} } ? {catch {Klass create kk}} 0 # # b) Structured trace scripts, containing lists. Check for # brace balancedness ... # # Background: Script blocks passed as initcmds should not be # subjected to *any* argument checking. This was not guaranteed, # previously. As a result, for example, upon multivalued argument # checking (e.g., 0..n) the argument (i.e., the initcmd block) was # tentatively expanded into a Tcl list. This failed for initcmd # scripts which do not qualify as valid list structures (a condition # not to be enforced). # # Below, we introduce three tests capturing the unwanted # interaction, now fixed. Note: This issue only affects # class-wide initcmds, as in the per-object case, the initcmds are # evaluated right away and not fiddled through the parameter handling # infrastructure. ::nx::Class create CC { :property a:0..n { set :defaultcmd { set _ 4 } } :property b:0..n {set :valuecmd {set _ 44} } :property -accessor public c:0..n { set :valuechangedcmd { ::nsf::var::set $obj $var 999 } } :create ::cc } ? {cc cget -a} 4 ? {cc cget -b} 44 ? {cc c set 5} 999 } ::nsf::configure checkarguments off nx::test case check-arguments-nocheck { nx::Class create Foo { :public method noarg {} {return ""} :public method onearg {x} {return $x} :public method intarg {x:integer} {return $x} :public method intsarg {x:integer,1..*} {return $x} :public method boolarg {x:boolean} {return $x} :public method classarg {x:class} {return $x} :public method upperarg {x:upper} {return $x} :public method metaclassarg {x:metaclass} {return $x} :create f1 } ? {f1 noarg} "" ? {f1 onearg 1} 1 # built-in checker ? {f1 intarg 1} 1 ? {f1 intarg a} a ? {f1 intsarg {10 11 12}} {10 11 12} ? {f1 intsarg {10 11 1a2}} {10 11 1a2} ? {f1 boolarg 1} 1 ? {f1 boolarg a} a ? {f1 classarg ::Foo} ::Foo ? {f1 classarg f1} f1 # tcl checker ? {f1 upperarg ABC} ABC ? {f1 upperarg abc} abc # scripted checker ? {f1 metaclassarg ::nx::Class} ::nx::Class ? {f1 metaclassarg ::Foo} ::Foo } nx::test configure -count 100 nx::test case checktype { nx::Object create o { :public object method f01 {} {::nsf::dispatch o ::nsf::methods::object::info::hastype ::nx::Object} :public object method f02 {} {::nsf::dispatch o ::nsf::methods::object::info::hastype nx::Object} :public object method f03 {} {::nsf::dispatch o ::nsf::methods::object::info::hastype nx::Object} :public object method f11 {} {::nsf::is object,type=::nx::Object o} :public object method f12 {} {::nsf::is object,type=nx::Object o} :public object method f13 {} {::nsf::is object,type=Object o} } ? {o f01} 1 ? {o f02} 1 ? {o f03} 1 ? {o f11} 1 ? {o f12} 1 ? {o f13} 1 } # # testing namespace resolution in type checkers # namespace eval foo { nx::Class create C { :create c1 :public method f21 {} {::nsf::dispatch c1 ::nsf::methods::object::info::hastype nx::Object} :public method f22 {} {::nsf::dispatch c1 ::nsf::methods::object::info::hastype C} :public method f31 {} {::nsf::is object,type=Object c1} :public method f32 {} {::nsf::is object,type=C c1} } nx::Object create o { :public object method f01 {} {::nsf::dispatch c1 ::nsf::methods::object::info::hastype ::nx::Object} :public object method f02 {} {::nsf::dispatch c1 ::nsf::methods::object::info::hastype nx::Object} :public object method f03 {} {::nsf::dispatch c1 ::nsf::methods::object::info::hastype nx::Object} :public object method f04 {} {::nsf::dispatch c1 ::nsf::methods::object::info::hastype foo::C} :public object method f05 {} {::nsf::dispatch c1 ::nsf::methods::object::info::hastype C} :public object method f11 {} {::nsf::is object,type=::nx::Object c1} :public object method f12 {} {::nsf::is object,type=nx::Object c1} :public object method f13 {} {::nsf::is object,type=Object c1} :public object method f14 {} {::nsf::is object,type=foo::C c1} :public object method f15 {} {::nsf::is object,type=C c1} } ? {o f01} 1 ? {o f02} 1 ? {o f03} 1 ? {o f04} 1 ? {o f05} 1 ? {o f11} 1 ? {o f12} 1 ? {o f13} 1 ? {o f14} 1 ? {o f15} 1 ? {c1 f21} 1 ? {c1 f22} 1 ? {c1 f31} 1 ? {c1 f32} 1 } nx::test case check-arguments { nx::Class create Foo { :method noarg {} {return ""} :method onearg {-x} {return $x} :method intarg {-x:integer} {return $x} :method intsarg {-x:integer,1..*} {return $x} :method boolarg {-x:boolean} {return $x} :method classarg {-x:class} {return $x} :method upperarg {-x:upper} {return $x} :method metaclassarg {-x:metaclass} {return $x} } ? {Foo info method syntax noarg} "/cls/ noarg" ? {Foo info method syntax onearg} "/cls/ onearg ?-x /value/?" ? {Foo info method syntax intarg} "/cls/ intarg ?-x /integer/?" ? {Foo info method syntax intsarg} "/cls/ intsarg ?-x /integer .../?" ? {Foo info method syntax boolarg} "/cls/ boolarg ?-x /boolean/?" ? {Foo info method syntax classarg} "/cls/ classarg ?-x /class/?" ? {Foo info method syntax upperarg} "/cls/ upperarg ?-x /upper/?" ? {Foo info method syntax metaclassarg} "/cls/ metaclassarg ?-x /metaclass/?" # return enumeration type ? {nx::Class info method syntax "info mixinof"} \ "/cls/ info mixinof ?-closure? ?-scope all|class|object? ?/pattern/?" } # # Check whether resetting via method "configure" changes values in the # initialzed object state. # nx::test case dont-reset-to-defaults { nx::Class create C { :property -accessor public {a 1} :create c1 } ? {c1 cget -a} 1 # change the value from the default to a different value ? {c1 a set 2} 2 ? {c1 a get} 2 # call configure ... c1 __configure # ... and check, it did not reset the value to the default ? {c1 a get} 2 } nx::test case setter-under-coloncmd-and-interpvarresolver { # There are (at least) three forms of object-namespace alignment in # NSF: # 1. Same-named namespace (::omon) predates a same-named object # (::omon), the namespace is registered as per-object namespace with # the object upon object construction -> no NsColonVarResolver(), # InterpColonVarResolver() is responsible! # 2. an explicit per-object namespace creation using [:require # namespace] -> NsColonVarResolver() is put in place! # 3. nx::Object get per-object members (fields, methods) -> # NsColonVarResolver() is put in place! # # The following test covers scenario 1: Called from within # NsfSetterMethod(), SetInstVar() verifies, whether there is a # per-object namespace (objPtr->nsPtr); if so, TCL_NAMESPACE_ONLY is # set ... in this object/ns alignment scenario, # InterpColonVarResolver() (!) serves the var resolution request. It # effectively forward-passes the resolution request when sensing # TCL_NAMESPACE_ONLY by signalling TCL_CONTINUE. This is a consequence # of handling the "compiled [variable] vs. AVOID_RESOLVERS" case # InterpColonVarResolver(). As in colon-prefixed calls to the setter # method (via ColonCmd()), the colon prefix is present in the # name-carrying Tcl_Obj used to in SetInstVar(). As we set an object # frame context, we effectively end up with a colon-prefixed object # variable :( nx::Class create Omon ::nsf::method::setter Omon a1 namespace eval omon {} Omon create omon omon a1 "" ? {omon info vars a1} "a1" ? {omon info vars :a1} "" omon eval { :a1 "" ? [list [current] info vars a1] "a1" # Prior to the fix, [:info vars] would have returned ":a1" ? [list [current] info vars :a1] "" } } # # test required configure parameter # nx::test case req-param { ::nx::Class create C { :property y:required :property x:required :method init args {set ::_ "passed args '$args'"} } set ::_ "" ? {C create c2 -y 1 -x} {value for parameter '-x' expected} # Was the constructor called? Should not. ? {set ::_} "" # Did the object survive the error? Should not. ? {::nsf::is object c2} 0 set ::_ "" ? {C create c2} \ "required argument 'x' is missing, should be: ::c2 configure -x /value/ -y /value/ ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" # Was the constructor called? Should not. ? {set ::_} "" # Did the object survive the error? Should not. ? {::nsf::is object c2} 0 # The following should run through without erros ? {C create c3 -y 1 -x 0} "::c3" ? {set ::_} "passed args ''" ? {c3 cget -x} "0" # # incremental property adding vs. required # nx::Class create D ? {D create d1} ::d1 ? {d1 configure} "" D property x:required ? {d1 info lookup syntax configure} \ "-x /value/ ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" ? {d1 configure} \ "required argument 'x' is missing, should be: ::d1 configure -x /value/ ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" ? {d1 configure -x 123} "" ? {d1 cget -x} 123 ? {d1 configure} "" } ::nsf::configure checkarguments on # # Test type any (or other typechecker) in combination with # substdefault via object parameter # nx::test case nsf-subdefault { nx::Class create C { :property {n1 "[namespace tail [::nsf::self]]"} :property {n2:any "[namespace tail [::nsf::self]]"} :create c1 } ? {c1 cget -n1} c1 ? {c1 cget -n2} c1 } # # Test argument processing and namespace handling in nsf::procs # nx::test case nsf-proc { # # test inner namespace and handling of switches # nsf::proc ::nsf::mix {-per-object:switch -x:boolean} { return [namespace current]-${per-object}-[expr {[info exists x] ? $x : "NULL"}] } # # test handling of "-ad" flag # nsf::proc -ad ad_returnredirect { {-message {}} {-html:boolean} {-allow_complete_url:boolean} {-x:switch} target_url } { return [namespace current]-[lsort [info vars]]-$html_p-$allow_complete_url_p } # # test inner namespace and flag passing via -flag=$value notation # namespace eval ::ns1 { nsf::proc -ad foo {-s:boolean} {return [namespace current]-$s_p} nsf::proc bar {-s:switch} {return [namespace current]-[info exists s]} nsf::proc baz {-b:boolean arg} {return [namespace current]-[info exists b]-$arg} nsf::proc -ad pass0 {-s:boolean} {foo {*}[expr {$s_p ? "-s" : ""}]} nsf::proc -ad pass1 {-s:boolean} {foo -s=$s_p} } nx::test configure -count 1 ? {::nsf::mix} "::nsf-0-NULL" ? {::nsf::mix -per-object} "::nsf-1-NULL" ? {::nsf::mix -x true} "::nsf-0-true" ? {::nsf::mix -x false} "::nsf-0-false" ? {::nsf::mix -per-object=1} "::nsf-1-NULL" ? {::nsf::mix -per-object=0} "::nsf-0-NULL" ? {ad_returnredirect /url} "::-allow_complete_url_p html_p message target_url x-0-0" ? {ad_returnredirect -html /url} "::-allow_complete_url_p html_p message target_url x-1-0" ? {ad_returnredirect -html=0 /url} "::-allow_complete_url_p html_p message target_url x-0-0" ? {ad_returnredirect -html=a /url} {expected boolean but got "a" for parameter "-html"} ? {::ns1::foo} "::ns1-0" ? {::ns1::foo -s} "::ns1-1" ? {::ns1::foo -s=1} "::ns1-1" ? {::ns1::foo -s=0} "::ns1-0" ? {::ns1::foo -s -s=0} "::ns1-0" ? {::ns1::baz -b true -- -b} "::ns1-1--b" ? {info body ad_returnredirect} {::nsf::__unset_unknown_args return [namespace current]-[lsort [info vars]]-$html_p-$allow_complete_url_p } nx::test configure -count 1000 ? {::ns1::pass1} "::ns1-0" ? {::ns1::pass1 -s} "::ns1-1" ? {::ns1::pass0} "::ns1-0" ? {::ns1::pass0 -s} "::ns1-1" } # # Test argument processing and namespace handling in nsf::procs # nx::test case xotcl-list-notation { nx::test configure -count 1 package prefer latest package req XOTcl 2.0 xotcl::Class create CC -parameter {package_id parameter_declaration user_id} # first, without list notation ? {CC create cc -package_id 123 -parameter_declaration o -user_id 4} "::cc" ? {cc package_id} 123 ? {cc parameter_declaration} o ? {cc user_id} 4 # new without list notation ? {CC create cc -package_id 234 [list -parameter_declaration oo] -user_id 456} ::cc ? {cc package_id} 234 ? {cc parameter_declaration} oo ? {cc user_id} 456 } # # Test parameter alias and parameter forwarder # nx::test case parameter-alias { nx::Class create C { :property {x:alias} :property {A:alias,method=bar} :property {{F:forward,method=%self foo %1 a b c %method}} :property {D def} :public method x args {set :x $args} :public method foo args {set :foo $args} :public method bar args {set :bar $args} :create c1 -F 123 -x x1 -A aha } ? {c1 eval {set :x}} "x1" ? {c1 eval {set :foo}} "123 a b c F" ? {c1 eval {set :bar}} "aha" ? {lsort [c1 info lookup methods -source application]} "bar foo x" } # # Test parameter alias and parameter forwarder with default value # nx::test case parameter-alias-default { nx::Class create C { :property {x1:alias "hugo"} :property {{F:forward,method=%self foo a %1 b c %method} "habicht"} :property {x2:alias "[self]"} :public method x1 args {set :x1 $args} :public method x2 args {set :x2 $args} :public method foo args {set :foo $args} :create c1 } ? {c1 eval {set :x1}} "hugo" ? {c1 eval {set :foo}} "a habicht b c F" ? {c1 eval {set :x2}} "::c1" ? {lsort [c1 info lookup methods -source application]} "foo x1 x2" ? {lsort [C info slots]} "::C::slot::F ::C::slot::x1 ::C::slot::x2" ? {::C::slot::x1 getParameterSpec} {-x1:alias hugo} ? {::C::slot::x2 getParameterSpec} {-x2:alias,substdefault {[self]}} } # # Test interactions between per-object-mixins and objectparameters # (case without per-object property) # nx::test case parameter-object-mixin-dependency { nx::Class create C { :property a1 :create c1 } nx::Class create D -superclass C nx::Class create M1 {:property b1:required} nx::Class create M2 {:property b2:required} ? {c1 eval :__object_configureparameter} "-a1 $::trailer" c1 object mixins set M1 ? {c1 info precedence} "::M1 ::C ::nx::Object" ? {c1 eval :__object_configureparameter} "-b1:required -a1 $::trailer" # # Invalidate the object parameter and expect that the per-class # mixin does not harm # ::nsf::parameter::cache::classinvalidate C # # We have now "-b1:required" in the configure parameters. # # NSF checks, if the associated variable is already set, but # this does not work for aliases.... we could track whether or # not a required parameter was already provided, but that # requires most likely a more general handling. # ? {c1 configure -a1 x} \ "required argument 'b1' is missing, should be: ::c1 configure -b1 /value/ ?-a1 /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" # # The object parameter based on the per-object-mixins must not be # stored in the class based cache. Therefore, creating a new object # must not require b1 ? {C create c2} ::c2 # # the same should hold for subclasses # ? {D create d1} ::d1 # # we have now per-object mixin of M1, we should have "-b1" but no # "-b2" # ? {c1 info object mixins} ::M1 ? {c1 cget -object-mixin} ::M1 ? {c1 info lookup parameters configure b*} "-b1:required" # # add one more mixin. # c1 object mixins add ::M2 ? {c1 info object mixins} {::M2 ::M1} ? {c1 cget -object-mixin} {::M2 ::M1} ? {c1 info lookup parameters configure b1} "-b1:required" ? {c1 info lookup parameters configure b2} "-b2:required" ? {lsort [c1 info lookup parameters configure b*]} "-b1:required -b2:required" # # drop the mixins, the b* properties should be gone. # c1 object mixins set "" ? {c1 info object mixins} {} ? {lsort [c1 info lookup parameters configure b*]} "" # # add M1 again # c1 object mixins add ::M1 ? {c1 info object mixins} {::M1} ? {c1 info lookup parameters configure b1} "-b1:required" ? {lsort [c1 info lookup parameters configure b*]} "-b1:required" # # We have the per-object cache; adding a per-object property should # flush the cache # c1 object property bo1 ? {lsort [c1 info lookup parameters configure b*]} "-b1:required -bo1" c1 object property bo2 ? {lsort [c1 info lookup parameters configure b*]} "-b1:required -bo1 -bo2" # # property deletion should invalidate the cache as well # c1 delete object property bo2 ? {lsort [c1 info lookup parameters configure b*]} "-b1:required -bo1" } # # Test interactions between per-object-mixins and objectparameters # (case with per-object property) # nx::test case parameter-object-mixin-dependency-object-property { nx::Class create C { :property a1 :create c1 { :object property a2 } } nx::Class create D -superclass C nx::Class create M {:property b1:required} c1 object mixins set M ? {c1 info precedence} "::M ::C ::nx::Object" ? {lsort [C info slots -closure]} \ "::C::slot::a1 ::nx::Object::slot::__initblock ::nx::Object::slot::class ::nx::Object::slot::object-filters ::nx::Object::slot::object-mixins" ? {c1 eval :__object_configureparameter} \ "-a2 -b1:required -a1 $::trailer" # # invalidate object parameter and expect that the per-class mixin # does not harm # ::nsf::parameter::cache::classinvalidate C ? {c1 __configure -a1 x} \ "required argument 'b1' is missing, should be: ::c1 configure ?-a2 /value/? -b1 /value/ ?-a1 /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" ? {c1 info precedence} "::M ::C ::nx::Object" ? {lsort [C info slots -closure]} \ "::C::slot::a1 ::nx::Object::slot::__initblock ::nx::Object::slot::class ::nx::Object::slot::object-filters ::nx::Object::slot::object-mixins" ? {c1 eval :__object_configureparameter} "-a2 -b1:required -a1 $::trailer" # should not require b1 ? {C create c2} ::c2 } # # Test integer, wideinteger and bignums # nx::test configure -count 1000 nx::test case bignums { ::nx::Object create o { :public object method foo {x:int} { return $x } :public object method foo32 {x:int32} { return $x } :public object method bar {x:wideinteger} { return $x } :public object method baz {x:double} { return $x } } # # In Tcl 8.5, "integer" means 32 bit integer # ? [list string is integer [expr {2 ** 31}]] 1 ? [list string is integer [expr {2 ** 32}]] 0 ? {o foo [expr {2 ** 16}]} "65536" ? {o foo [expr {2 ** 31}]} "2147483648" #? {o foo [expr {2 ** 32}]} {expected integer but got "4294967296" for parameter "x"} ? {o foo [expr {2 ** 32}]} "4294967296" ? [list string is integer [expr {2 ** 63}]] 0 ? [list string is integer [expr {2 ** 64}]] 0 #? {o foo [expr {2 ** 63}]} {expected integer but got "9223372036854775808" for parameter "x"} #? {o foo [expr {2 ** 64}]} {expected integer but got "18446744073709551616" for parameter "x"} ? {o foo [expr {2 ** 63}]} "9223372036854775808" ? {o foo [expr {2 ** 64}]} "18446744073709551616" ? {o foo [expr {2 ** 128}]} "340282366920938463463374607431768211456" ? {o foo [expr {wide(2 ** 63)}]} "-9223372036854775808" ? {o foo [expr {2.0}]} {expected integer but got "2.0" for parameter "x"} ? {o foo [expr {2.0 * 2}]} {expected integer but got "4.0" for parameter "x"} # # Note: In Tcl version less or equal 8.5.9 (to be precise, before # fossil check-in 769801ace1) there is a rounding issue for # doubles. It can be worked around by setting the tcl precision # level sufficiently high (see below). With check-in 769801ace1, or # with the 8.5.10 release version of Tcl, this work-around becomes # obsolete. # if {[::package vcompare [::info patchlevel] 8.5.9] < 1} { set ::nsf::savedTclPrecision $::tcl_precision set ::tcl_precision 17 } ? {o foo [expr {2.0 ** 128}]} {expected integer but got "3.4028236692093846e+38" for parameter "x"} ? {o foo [expr {(2 ** 128)*1.0}]} {expected integer but got "3.4028236692093846e+38" for parameter "x"} if {[info exists ::nsf::savedTclPrecision]} { set ::tcl_precision $::nsf::savedTclPrecision unset ::nsf::savedTclPrecision } ? {o foo32 [expr {2 ** 31}]} "2147483648" ? {o foo32 [expr {2 ** 32}]} {expected int32 but got "4294967296" for parameter "x"} # # In Tcl 8.5, "wideinteger" means 64 bit integer # ? [list string is wideinteger [expr {2 ** 63}]] 1 ? [list string is wideinteger [expr {2 ** 64}]] 0 ? {o bar [expr {2 ** 63}]} "9223372036854775808" ? {o bar [expr {2 ** 64}]} {expected wideinteger but got "18446744073709551616" for parameter "x"} ? [list string is wideinteger [expr {wide(2 ** 64)}]] 1 ? {o bar [expr {wide(2 ** 63)}]} "-9223372036854775808" ? {o bar [expr {wide(2 ** 64)}]} "0" # # In Tcl 8.5, "bignums" have to be checked with "double" # ? [list string is double [expr {2 ** 63}]] 1 ? [list string is double [expr {2 ** 64}]] 1 ? {o baz [expr {2 ** 63}]} "9223372036854775808" ? {o baz [expr {2 ** 64}]} "18446744073709551616" ? {o baz [expr {2 ** 128}]} "340282366920938463463374607431768211456" } nx::test case reconfigure-perobj-default { nx::Object create o ? {o eval {info exists :a}} 0 o object property {a oldvalue} ? {o eval {info exists :a}} 1 ? {o cget -a} oldvalue # # By unsetting the var, upon recreating the property slot (or # calling reconfigure upon the property) we can trigger # a re-assignment of the default value. # o eval {unset :a} ? {o eval {info exists :a}} 0 # # re-assignment of the default is handled by init # o object property {a newvalue} ? {o eval {info exists :a}} 1 ? {o cget -a} newvalue o eval {unset :a} ? {o eval {info exists :a}} 0 [o info object slots a] configure -default anothervalue ? {o eval {info exists :a}} 0 # # re-assignment must be requested by a reconfigure call # [o info object slots a] reconfigure ? {o eval {info exists :a}} 1 ? {o cget -a} anothervalue } # # nx::Object parameters (specified e.g. via attributes) are defined to # configure fresh objects (therefore, the definition is on the class # level). Therefore, object-level object parameter do not fulfill # this purpose, since they can only be defined, *after* the object # is created. # # In general, object parameters have creational aspects (providing # configurations for the object creation, such as e.g. defaults, and # configurations) and object-lifetime aspects (valid through the # lifetime of objects, such as e.g. setters/checkers). # # nx::Object-level attributes cannot be used for the creational aspects # of object parameters. # # Strengths of object-level parameters: # - same interface as class-level attributes # - can use same meta-data mechanisms as for class-level attributes # (e.g database types, property name in the database, persistence # information, ...) # - can use same setters/checkers as for class-level attributes # - can use as well incremental as for class-level attributes # # Shortcomings of object-level parameters: # - no nice introspection: # "info parameter ...." is defined on cls, not on obj # - default handling is not the same as for classes level attributes # (we have already some special mechanisms to set instance # attributes, if they do not exist) # - object-level parameters cannot be used in a "configure" # of the object, since configure allows the same signature # as on object creation, all object parameters are cached # on the class level # - Since configure does not include object-level parameters, # positional object level parameters do not make sense, since they # cannot be called. # # test object level property and variable # nx::test case object-level-variable { nx::Object create ::enterprise { # just to get a reference value for the timing ? [list [self] eval {set :dummy 1}] "1" # set 2 variables, one via variable, one via property ? [list [self] object variable -nocomplain captain1 "James Kirk"] "" ? [list [self] object property -nocomplain [list captain2 "Jean Luc"]] "" # in both cases, we expect instance variables ? [list [self] eval {set :captain1}] "James Kirk" ? [list [self] eval {set :captain2}] "Jean Luc" # just for the property, we have accessors ? [list [self] info lookup method captain1] "" ? [list [self] info lookup method captain2] "" # set variable with a value checker ? [list [self] object variable -nocomplain x1:int 1] "" ? [list [self] object property -nocomplain [list x2:int 2]] "" # set variable with a value checker and an invalid value ? [list [self] object variable y1:int a] {expected integer but got "a"} ? [list [self] object property [list y2:int b]] {expected integer but got "b" for parameter "y2"} # set variable again, without -nocomplain ? [list [self] object variable x1:int 1] {object ::enterprise has already an instance variable named 'x1'} ? [list [self] object property [list x2:int 2]] {object ::enterprise has already an instance variable named 'x2'} # set variable with a value checker, multiple ? [list [self] object variable -nocomplain xm1:int,1..n {1 2 3}] "" ? [list [self] object property -nocomplain [list xm2:int,1..n {1 2 3}]] "" # in both cases, we expect instance variables ? [list [self] eval {set :xm1}] "1 2 3" ? [list [self] eval {set :xm2}] "1 2 3" # set variable with a value checker, multiple with invalid value ? [list [self] object variable -nocomplain xm1:int,1..n {1 2a 3}] \ {invalid value in "1 2a 3": expected integer but got "2a"} ? [list [self] object property -nocomplain [list xm2:int,1..n {1 2a 3}]] \ {invalid value in "1 2a 3": expected integer but got "2a" for parameter "xm2"} # useless definition ? [list [self] object variable dummy:int] \ {variable definition for 'dummy' (without value and accessor) is useless} # # define an application specific converter # ::nx::ObjectParameterSlot method type=range {name value arg} { lassign [split $arg -] min max if {$value < $min || $value > $max} { error "value '$value' of parameter $name not between $min and $max" } return $value } # # Test usage of application specific converter in "variable" and # "property"; invalid value ? [list [self] object variable -nocomplain r1:range,arg=1-10 11] \ {value '11' of parameter value not between 1 and 10} ? [list [self] object property -nocomplain [list r2:range,arg=1-10 11]] \ {value '11' of parameter r2 not between 1 and 10} # valid value ? [list [self] object variable -nocomplain r1:range,arg=1-10 5] "" ? [list [self] object property -nocomplain [list r2:range,arg=1-10 5]] "" # testing incremental ? [list [self] object variable -incremental -nocomplain i:int,0..* {}] "::enterprise::i" ? [list [self] object property -incremental -nocomplain j:int,0..* {}] "::enterprise::j" :i add 1 :j add 1 ? [list [self] i get] "1" ? [list [self] j get] "1" :i add 2 :j add 2 ? [list [self] i get] "2 1" ? [list [self] j get] "2 1" ? [list [self] i add a] {expected integer but got "a" for parameter "value"} ? [list [self] j add a] {expected integer but got "a" for parameter "value"} } nx::Class create C { # set 2 class variables, one via variable, one via property ? [list [self] object variable -nocomplain v "v0"] "" ? [list [self] object property -nocomplain [list a "a0"]] "" # in both cases, we expect instance variables ? [list [self] eval {set :v}] "v0" ? [list [self] eval {set :a}] "a0" # check variable with value constraint ? [list [self] object variable -nocomplain x:int "0"] "" ? [list [self] object variable -nocomplain y:int "a0"] {expected integer but got "a0"} } } # # test class level property and variable # nx::test case class-level-variable { nx::Class create C { # define 2 class-level variables, one via variable, one via property :variable v v0 :property -accessor public {a a0} # create an instance :create c1 } # in both cases, we expect instance variables for c1 ? {lsort [c1 info vars]} {a v} ? {c1 eval {set :v}} "v0" ? {c1 eval {set :a}} "a0" # # We expect a specifiable object parameter for "a" but not for "v". # The parameter for v can be obtained via spec, but is not listed in # "info parameter syntax" or "info parameter definitions". # # ? {C info parameter list a} "-a" ? {C info lookup parameters create a} "{-a a0}" # ? {C info lookup syntax create a} "?-a /value/?" ? {C info lookup syntax create} "/objectName/ ?-a /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?" ? {C info lookup parameters create v} "" ? {[C info slots v] definition} "::C variable -accessor none v v0" # ? {C info parameter list v} "" # ? {C info configure parameter v} "" ? {C create c2 -a 10} ::c2 ? {C create c2 -v 10} \ {invalid non-positional argument '-v', valid are : -a, -object-mixins, -object-filters, -class; should be "::c2 configure ?-a /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?"} # # We expect a setter for "a" but not for "v". # ? {c1 info lookup method a} "::nsf::classes::C::a" ? {c1 info lookup method v} "" } # # test classes with single variable definitions, and illegal names # nx::test case single-variable { ? {nx::Class create C { :variable v 1 :create c1 }} ::C ? {c1 info vars} v ? {nx::Class create D { :variable :v 1 }} {leading colon in ':v' not allowed in parameter specification 'spec'} } # # test deletion of class level property and variable # nx::test case delete-class-level-variable-and-property { nx::Class create C { # define 2 class-level variables, one via variable, one via property :variable v v0 :property -accessor public {a a0} # create an instance :create c1 } # the instance of C will have the two variables set ... ? {lsort [c1 info vars]} {a v} # ... and we expect an object parameter for a but not for v ... ? {C info lookup parameters create a} "{-a a0}" ? {C info lookup parameters create v} "" # ... and we expect a setter for a but not for v ? {c1 info lookup method a} "::nsf::classes::C::a" ? {c1 info lookup method v} "" # if we delete a class-level property or variable, # the object parameter and setters for "a" will be gone C delete variable v C delete property a ? {C info lookup parameters create a} "" ? {c1 info lookup method a} "" # already created instance variables will continue to exist ? {lsort [c1 info vars]} {a v} # in newly created objects, neither a or v will exist ? {C create c2} ::c2 ? {lsort [c2 info vars]} {} } # # test deletion of class level property and variable # nx::test case delete-object-level-variable-and-property { nx::Object create o { # define 2 object-level variables, one via variable, one via property :object variable v v0 :object property -accessor public {a a0} } # the instance of C will have the two variables set ... ? {lsort [o info vars]} {a v} # ... and we expect a setter for a but not for v ? {o info lookup method a} "::o::a" ? {o info lookup method v} "" # nx::Object-level attributes and variables set und unset instance # variables. If we delete an object-level property or variable, # the setters for "a" will be unset. o delete object variable v o delete object property a ? {o info lookup method a} "" # Both instance variables are unset ? {lsort [o info vars]} {} } # # Testing object parameters of type "switch" # nx::test case object-parameter-switch { # Create a class with an property of type switch and an instance of # the class ? {::nx::Class create C { :property -accessor public foo:switch :create c1 }} "::C" # When the object parameter is not specified at creation time, the # default is false, an instance variable is set with this value ? {lsort [c1 info vars]} {foo} ? {c1 eval {set :foo}} {0} # nx::Object parameter of type "switch" are more tricky, since e.g. a # setter with 0 arguments is a getter. When a setter is built, it # uses the parameter type "boolean" instead. ? {C info methods} "foo" ? {c1 info lookup method foo} "::nsf::classes::C::foo" ? {c1 foo get} 0 ? {c1 foo set 1} 1 ? {c1 foo get} 1 # When the object parameter is specified, the instance variable has # a value of true (i.e. 1) C create c2 -foo ? {lsort [c2 info vars]} {foo} ? {c2 eval {set :foo}} {1} ? {c1 foo get} 1 # One can pass false (and other values) with the = notation as well C create c3 -foo=false ? {lsort [c3 info vars]} {foo} ? {c3 eval {set :foo}} {false} # In the inverted case, the switch has a default of "true". If the # switch is specified, the valus is "false" C property {foo2:switch 1} C create c4 ? {lsort [c4 info vars]} {foo foo2} ? {c4 eval {set :foo2}} {1} C create c5 -foo2 ? {lsort [c5 info vars]} {foo foo2} ? {c5 eval {set :foo2}} {0} # nx::Object case: variables of type "switch" are like variables of type # boolean, except that without the specified value argument # (variable foo below), it sets the the variable to "false". ? {::nx::Object create o1 { :object variable foo:switch :object variable bar:switch 1 }} ::o1 ? {o1 eval {set :foo}} 0 ? {o1 eval {set :bar}} 1 } # # Test slots with configparameter true/false, accessor true/false # against "slot definitions" and "info parameter" # nx::test case class-info-slots-types { # # "/cls/ info slot ..." shows all slots, including variables # "/cls/ info parameter ..." shows the parameter available for object parameterization # nx::Class create C { # variable has no configure parameter and no accessor :variable v 100 } # "v" does NOT show up in "info configure parameters" ? {C info lookup parameters create v} "" # ? {C info parameter names} "noinit object-mixin class object-filter __initblock" # "v" does show up in "info slot ..." ? {C info slots} "::C::slot::v" ? {::C::slot::v definition} "::C variable -accessor none v 100" nx::Class create D { :property -accessor public {p0 200} :property -accessor none {p1 201} :property -accessor none {p2:noconfig 202} :property -accessor public {p3:noconfig 203} } # "p2" and "p3" do NOT show up in "info parameter" ? {D info lookup parameters create p*} "{-p0 200} {-p1 201}" # "p1" and "p2" do NOT show up in "info methods" ? {D info methods} "p0 p3" # all properties show up in "info slot" ? {D info slots} "::D::slot::p0 ::D::slot::p1 ::D::slot::p2 ::D::slot::p3" #? {D info slot definitions} "{::D property {p0 200}} {::D property -accessor none {p1 201}} {::D variable p2 202} {::D variable -accessor public p3 203}" #? {D info properties} "{p0 200} {p1 201} {p2:noconfig 202} {p3:noconfig 203}" } nx::test case object-info-slots-types { # # "/obj/ info slot ..." shows all slots, including variables # nx::Object create o1 { # plain object variable has no slot object :object variable v0 100 # In case we require an accessor or e.g. incremental, slot objects # are created; incremental implies accessor :object variable -accessor public v1 100 :object variable -incremental v2 100 } # only the variables with slots show up in "info slot ..." ? {o1 info object slots} "::o1::per-object-slot::v2 ::o1::per-object-slot::v1" ? {::o1::per-object-slot::v2 definition} "::o1 object variable -accessor public v2:1..n 100" ? {::o1::per-object-slot::v1 definition} "::o1 object variable -accessor public v1 100" nx::Object create o2 { :object property -accessor public {p0 200} :object property -accessor none {p1 201} :object property -accessor none {p2:noconfig 202} :object property -accessor public {p3:noconfig 203} } # "p1" and "p2" do NOT show up in "info methods" ? {o2 info object methods} "p0 p3" # all properties with slots show up in "info slot" ? {o2 info object slots} "::o2::per-object-slot::p0 ::o2::per-object-slot::p1 ::o2::per-object-slot::p3" ? {[o2 info object slots p0] definition} "::o2 object property -accessor public {p0 200}" ? {[o2 info object slots p1] definition} "::o2 object property -accessor none {p1 201}" ? {[o2 info object slots p3] definition} "::o2 object variable -accessor public p3 203" #? {o2 info properties} "{p0 200} {p1 201} {p3:noconfig 203}" } # # testing method properties # nx::test case properties { # simple properties #nx::Class create Foo -properties {a {b 1}} nx::Class create Foo { :property a :property {b 1} } ? {[Foo info slots a] definition} "::Foo property -accessor none a" ? {[Foo info slots b] definition} "::Foo property -accessor none {b 1}" #? {Foo info properties} "a {b 1}" # properties with value checker nx::Class create Foo { :property a:boolean :property {b:integer 1} } ? {[Foo info slots a] definition} "::Foo property -accessor none a:boolean" ? {[Foo info slots b] definition} "::Foo property -accessor none {b:integer 1}" # required/optional properties nx::Class create Foo { :property a:required :property b:boolean,required } ? {[Foo info slots a] definition} "::Foo property -accessor none a:required" ? {[Foo info slots b] definition} "::Foo property -accessor none b:boolean,required" # properties with multiplicity nx::Class create Foo { :property {ints:integer,0..n ""} :property objs:object,1..n :property obj:object,0..1 } ? {[Foo info slots objs] definition} "::Foo property -accessor none objs:object,1..n" ? {[Foo info slots ints] definition} "::Foo property -accessor none {ints:integer,0..n {}}" ? {[Foo info slots obj] definition} "::Foo property -accessor none obj:object,0..1" } # # The following test case sets a value of an instance variable via a # side-effect of an aliased parameter. Side-effects from aliased # parameters are discouraged, since the order of the evaluation should # not matter of an declarative evaluation of the argument vector. # # Note that the order, in which is the arguments are provided is not # significant for the evaluation order. # nx::test case side-effect-set-value { nx::Class create C { :public object method setObjectParams {spec} { :protected method __object_configureparameter {} [list return $spec] ::nsf::parameter::cache::classinvalidate [self] } :setObjectParams "" } C method second {arg} { set :first $arg } C setObjectParams {{-first "X"} -second:alias} ? {[C new -second Y] eval {set :first}} Y "side-effect overwrites default" C setObjectParams {-second:alias {-first "X"}} ? {[C new -second Y] eval {set :first}} Y "side-effect determines value" } nx::test case xotcl-configure-method { nx::test configure -count 1 package prefer latest package req XOTcl 2.0 # # attempt dispatch to unknown method # xotcl::Object create o ? {o configure -order 15} "::o: unable to dispatch method 'order' during '::o.order'" } # # Test forwarding to slot object, when set is overloaded # nx::test case forward-to-set { set ::slotcalls 0 nx::Class create Foo { :property -accessor public bar { :public object method value=set { object property value } { incr ::slotcalls 1 nsf::var::set $object $property $value } } } # call without default, without object parameter value set o [Foo new] ? [list $o eval {info exists :bar}] 0 ? {set ::slotcalls} 0 ? [list $o bar get] {can't read "bar": no such variable} # call without default, with object parameter value set o [Foo new -bar "test"] ? [list $o eval {info exists :bar}] 1 ? {set ::slotcalls} 1 ? [list $o bar get] "test" # test cases for default set ::slotcalls 0 nx::Class create Foo { :property -accessor public {baz 1} { :public object method value=set { object property value } { incr ::slotcalls 1 nsf::var::set $object $property $value } } } # call with default, without object parameter value set o [Foo new] ? [list $o eval {info exists :baz}] 1 ? {set ::slotcalls} 1 "baz without object parameter value" ? [list $o baz get] "1" # call with default, with object parameter value set o [Foo new -baz "test"] ? [list $o eval {info exists :baz}] 1 ? {set ::slotcalls} 2 "baz with object parameter value" ? [list $o baz get] "test" ? {Foo info method exists baz} 1 } # # Test forwarding to slot vs. accessor none # nx::test case forward-to-set2 { set ::slotcalls 0 ? {nx::Class create Foo { :property -accessor none bar { :public object method value=set { object property value } { incr ::slotcalls 1 nsf::var::set $object $property $value } }} } "::Foo" # call without default, without object parameter value ? {catch {Foo new}} 0 ? {set ::slotcalls} 0 # test cases for default nx::Class create Foo { :property -accessor none {baz 1} { :public object method value=set { object property value } { incr ::slotcalls 1 nsf::var::set $object $property $value } } } # call with default, without object parameter value ? {catch {Foo new}} 0 ? {set ::slotcalls} 1 # call with default, with object parameter value ? {catch {Foo new -baz "test"}} 0 ? {set ::slotcalls} 2 ? {Foo info method exists baz} 0 } # # Test slot initialize # nx::test case forward-to-incremental { set ::slotcalls 0 ? {nx::Class create Foo { :property bar { :public object method initialize { object property } { incr ::slotcalls 1 } }} } "::Foo" # initialize is supposed to be called regardless of some default ? {catch {Foo new}} 0 ? {set ::slotcalls} 1 } # # Test interaction of name of property with the Tcl command behavior. # Without the SlotContainerCmdResolver() the call to "list" in a # property named "list" leads to a call to the container object # ::Test2::slot::list instead of the intended ::list. # nx::test case slot-container-name-interaction { nx::Class create Test2 { :property -accessor public list { :public object method value=set { obj var val } { nsf::var::set $obj $var [list $obj $var $val] } :object method unknown { val obj var args } { return unknown } } } ? {Test2 create t2} ::t2 ? {t2 list set 3} {::t2 list 3} ? {t2 list get} {::t2 list 3} ? {t2 list this should call unknown} "unknown" } nx::test case object-level-defaults { # # In the scenario below, setCheckedInstVar is executed and performs # an ::nsf::is value check on the default value. However, given the # custom set method, the parameter option slotset is passed on # to ::nsf::is which (currently) does not accept it: # # 'invalid value constraints # "slot=::objekt::per-object-slot::a,slotset"' # nx::Object create o ? {o eval {info exists :a}} 0 ? {catch { o object variable -accessor public -initblock { :public object method value=set args { incr :assignCalled next } } a 1}} 0 ? {o eval {info exists :a}} 1 ? {o eval {info exists :assignCalled}} 0; # !!! should be 1 ? {o a get} 1 } nx::test case cmd-error-propagation { ? {nx::Object new { error "bow-wow" }} "bow-wow" ? {nx::Object new { :object method foo {} { error "bow-wow" } :foo }} "bow-wow" ? {nx::Object new { # Note: Creating a slot causes a destroy() to perform some cascading # operations which eventually reset the interp result at some # point. :object property x :object method foo {} { error "bow-wow" } :foo }} "bow-wow" ? {nx::Object new { :object method destroy {} { # This (inner) error message is swallowed and written to stderr # directly. The original (outer) error message is preserved. error "BOW-WOW" } :object method foo {} { error "bow-wow" } :foo }} "bow-wow" } # # test argument processing in nsf::proc with checkalways # nx::test case nsf-proc-checkalways { # # one proc with checkalways # nsf::proc p1 {-x:integer} { return $x} nsf::proc -checkalways p2 {-x:integer} { return $x} ? {p1 -x 100} 100 ? {p1 -x a100} {expected integer but got "a100" for parameter "-x"} ? {p2 -x 100} 100 ? {p2 -x a100} {expected integer but got "a100" for parameter "-x"} nsf::configure checkarguments off ? {p1 -x a100} a100 ? {p2 -x a100} {expected integer but got "a100" for parameter "-x"} nsf::configure checkarguments on } # # test argument processing in methods with checkalways # nx::test case nsf-method-checkalways { # # one method with checkalways # nx::Class create C { :public method m1 {-x:integer} { return $x} :public method m2 {-x:integer} -checkalways { return $x} :public object method om1 {-x:integer} { return $x} :public object method om2 {-x:integer} -checkalways { return $x} :create c1 } ? {c1 m1 -x 100} 100 ? {c1 m2 -x 100} 100 ? {c1 m1 -x a100} {expected integer but got "a100" for parameter "-x"} ? {c1 m2 -x a100} {expected integer but got "a100" for parameter "-x"} ? {C om1 -x 200} 200 ? {C om2 -x 200} 200 ? {C om1 -x a} {expected integer but got "a" for parameter "-x"} ? {C om2 -x a} {expected integer but got "a" for parameter "-x"} nsf::configure checkarguments off ? {c1 m1 -x a100} a100 ? {c1 m2 -x a100} {expected integer but got "a100" for parameter "-x"} ? {C om1 -x a} a ? {C om2 -x a} {expected integer but got "a" for parameter "-x"} nsf::configure checkarguments on } # # Test parameter::info with objects/classes and types # nx::test case parameter-get { nx::Class create C { :property foo:integer :property o:object,type=::nx::Object :property c:class :property m:metaclass } ? {C info lookup parameters create foo} "-foo:integer" ? {nsf::parameter::info type [C info lookup parameters create foo]} "integer" ? {C info lookup parameters create o} "-o:object,type=::nx::Object" ? {nsf::parameter::info type [C info lookup parameters create o]} "::nx::Object" ? {C info lookup parameters create c} "-c:class" ? {nsf::parameter::info type [C info lookup parameters create c]} "class" ? {C info lookup parameters create m} "-m:metaclass" ? {nsf::parameter::info type [C info lookup parameters create m]} "metaclass" } # # Test parameter passing on new (disambiguate between -childof as a # property and as a modifier) # nx::test case new-parameter-passing { nx::Class create C { :property childof } nx::Object create o proc isGlobalNew name {regexp {^::nsf::__#} $name} proc isONew name {regexp {^::o::__#} $name} ? {isGlobalNew [C new]} 1 ? {isONew [C new -childof o]} 1 ? {isGlobalNew [C new --]} 1 ? {isGlobalNew [C new -- -childof x]} 1 ? {isONew [C new -childof o -- -childof x]} 1 # # When the parameter is given twice, we get a warning, the second # one "wins" # ? {isONew [C new -childof o -childof xxx]} 0 # are the properties set correctly? set ::o1 [C new -childof o -- -childof x] ? {$::o1 cget -childof} x set ::o1 [C new -- -childof y] ? {$::o1 cget -childof} y } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/plain-object-method.test000066400000000000000000000060201242365656200167570ustar00rootroot00000000000000# -*- Tcl -*- package require nx::test nx::test case plain-methods-0 { nx::Class create M1 nx::Object create o { ? {o public method foo {} {return foo}} "::o: unable to dispatch method 'method'" :public object method f args {next} } ? {o mixins set M1} "::o: unable to dispatch method 'mixins'" ? {o filters set f} "::o: unable to dispatch method 'filters'" ? {lsort [o info object methods]} "f" ? {lsort [o info]} "valid submethods of ::o info: children class has info lookup name object parent precedence variable vars" } # # require the conveniance layer # and make it verbose # package require nx::plain-object-method nx::configure plain-object-method-warning on nx::test case plain-methods-1 { nx::Class create M1 nx::Object create o { :public method foo {} {return [:pm1]} :public method f args {next} :protected method pm1 args {return pm1} :public alias a ::o::pm1 :public forward fwd %self pm1 :private method priv args {return priv} :method pm2 args {return pm2} :property -accessor public p :variable v1 1 :variable -incremental v2:integer 1 } ? {o info methods} "v2 p foo fwd a f" ? {lsort [o info methods -callprotection protected]} "per-object-slot pm1 pm2" ? {lsort [o info methods -callprotection private]} "priv" ? {o info variables} "::o::per-object-slot::v2 ::o::per-object-slot::p" ? {o info object variables} "::o::per-object-slot::v2 ::o::per-object-slot::p" ? {o info slots} "::o::per-object-slot::v2 ::o::per-object-slot::p" ? {o pm1} "::o: unable to dispatch method 'pm1'" ? {o foo} "pm1" ? {o a} "pm1" ? {o fwd} "pm1" ? {o mixins set M1} ::M1 ? {o info mixins} ::M1 ? {o mixins set ""} "" ? {o info mixins} "" ? {o filters set f} f ? {o info filters} f ? {o filters set ""} "" ? {o info filters} "" ? {lsort [o info object methods]} "a f foo fwd p v2" ? {lsort [o info]} "valid submethods of ::o info: children class filters has info lookup method methods mixins name object parent precedence slots variable variables vars" } # # delete class method, class property, class variable # nx::test case plain-methods-2 { nx::Object create ::o { :public method foo {} {return foo} :property -accessor public p :variable -incremental v1:integer 1 } ? {o info methods} "p foo v1" ? {o info variables} "::o::per-object-slot::p ::o::per-object-slot::v1" ? {o delete method foo} "" ? {o info methods} "p v1" ? {o info variables} "::o::per-object-slot::p ::o::per-object-slot::v1" ? {o delete property p} "" ? {o info methods} "v1" ? {o info variables} "::o::per-object-slot::v1" ? {o delete variable v1} "" ? {o info methods} "" ? {o info variables} "" } # # require method # nx::test case plain-methods-3 { nsf::method::provide set {::nsf::method::alias set -frame object ::set} nx::Object create ::o { :require method set } ? {::o info methods} "set" ? {::o info object methods} "set" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/properties.test000066400000000000000000001040331242365656200153310ustar00rootroot00000000000000# -*- Tcl -*- package req nx::test # # make sure, the defaultAccessor is "none" # #puts stderr "*** default defaultAccessor '[nx::configure defaultAccessor]'" nx::configure defaultAccessor none ##################################################################### # class-level properties ##################################################################### # # Test class-level properties and variables without -incremental # nx::test case class-level { nx::Class create C { :property {a a1} :property -accessor public {b b1} :property -accessor protected {c c1} :property -accessor private {d d1} :property -accessor none {e e1} :variable va va1 :variable -accessor public vb vb1 :variable -accessor protected vc vc1 :variable -accessor private vd vd1 :variable -accessor none ve ve1 # a non-configurable property is a variable :property -accessor none -configurable false {vf vf1} :public method call-local {v} {: -local $v get} :create c1 } # # just the public properties are accessible via the configure interface # ? {c1 info lookup syntax configure} {?-e /value/? ?-a /value/? ?-b /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} ? {lsort [C info slots]} "::C::slot::____C.d ::C::slot::____C.vd ::C::slot::a ::C::slot::b ::C::slot::c ::C::slot::e ::C::slot::va ::C::slot::vb ::C::slot::vc ::C::slot::ve ::C::slot::vf" ? {::C::slot::a definition} "::C property -accessor none {a a1}" ? {::C::slot::b definition} "::C property -accessor public {b b1}" ? {::C::slot::c definition} "::C variable -accessor protected c c1" ? {::C::slot::____C.d definition} "::C variable -accessor private d d1" ? {::C::slot::e definition} "::C property -accessor none {e e1}" ? {::C::slot::va definition} "::C variable -accessor none va va1" ? {::C::slot::vb definition} "::C variable -accessor public vb vb1" ? {::C::slot::vc definition} "::C variable -accessor protected vc vc1" ? {::C::slot::____C.vd definition} "::C variable -accessor private vd vd1" ? {::C::slot::ve definition} "::C variable -accessor none ve ve1" ? {::C::slot::vf definition} "::C variable -accessor none vf vf1" ? {c1 cget -a} a1 ? {c1 cget -b} b1 ? {c1 cget -c} "cget: unknown configure parameter -c" ? {c1 cget -d} "cget: unknown configure parameter -d" ? {c1 cget -va} "cget: unknown configure parameter -va" ? {c1 cget -vb} "cget: unknown configure parameter -vb" ? {c1 cget -vc} "cget: unknown configure parameter -vc" ? {c1 cget -vd} "cget: unknown configure parameter -vd" ? {c1 cget -ve} "cget: unknown configure parameter -ve" ? {c1 cget -vf} "cget: unknown configure parameter -vf" # # No incremental used, so "a" and "e" have no slots # ? {c1 info lookup method a} "" ? {c1 info lookup method b} "::nsf::classes::C::b" ? {c1 info lookup method c} "::nsf::classes::C::c" ? {c1 info lookup method d} "::nsf::classes::C::d" ? {c1 info lookup method e} "" ? {c1 info lookup method f} "" ? {c1 info lookup method va} "" ? {c1 info lookup method vb} "::nsf::classes::C::vb" ? {c1 info lookup method vc} "::nsf::classes::C::vc" ? {c1 info lookup method vd} "::nsf::classes::C::vd" ? {c1 info lookup method ve} "" ? {c1 info lookup method vf} "" # # Check protection of accessors # ? {nsf::method::property C b call-protected} 0 ? {nsf::method::property C c call-protected} 1 ? {nsf::method::property C d call-protected} 1 ? {nsf::method::property C vb call-protected} 0 ? {nsf::method::property C vc call-protected} 1 ? {nsf::method::property C vd call-protected} 1 ? {nsf::method::property C b call-private} 0 ? {nsf::method::property C c call-private} 0 ? {nsf::method::property C d call-private} 1 ? {nsf::method::property C vb call-private} 0 ? {nsf::method::property C vc call-private} 0 ? {nsf::method::property C vd call-private} 1 # # do we have variables set? # ? {c1 eval "info exists :a"} 1 ? {c1 eval "info exists :b"} 1 ? {c1 eval "info exists :c"} 1 ? {c1 eval "info exists :d"} 0 ? {c1 eval "info exists :va"} 1 ? {c1 eval "info exists :vb"} 1 ? {c1 eval "info exists :vc"} 1 ? {c1 eval "info exists :vd"} 0 ? {c1 eval "info exists :ve"} 1 ? {c1 eval "info exists :vf"} 1 # # can we call the accessor directly or via "eval" # ? {c1 a} {::c1: unable to dispatch method 'a'} ? {c1 b get} b1 ? {c1 c} {::c1: unable to dispatch method 'c'} ? {c1 d} {::c1: unable to dispatch method 'd'} ? {c1 eval ":a"} {::c1: unable to dispatch method 'a'} ? {c1 eval ":b get"} b1 ? {c1 eval ":c get"} c1 ? {c1 eval ":d"} {::c1: unable to dispatch method 'd'} ? {c1 va} {::c1: unable to dispatch method 'va'} ? {c1 vb get} vb1 ? {c1 vc} {::c1: unable to dispatch method 'vc'} ? {c1 vd} {::c1: unable to dispatch method 'vd'} ? {c1 eval ":va"} {::c1: unable to dispatch method 'va'} ? {c1 eval ":vb get"} vb1 ? {c1 eval ":vc get"} vc1 ? {c1 eval ":vd"} {::c1: unable to dispatch method 'vd'} # # check the behavior of "private" properties and variables # ? {c1 call-local d} d1 ? {c1 call-local vd} vd1 ? {lsort [c1 info vars]} "__private a b c e va vb vc ve vf" ? {c1 eval "array get :__private"} "::C,vd vd1 ::C,d d1" # # check incremental operations for properties (should fail in all # cases) # set unknowns "valid are: {assign definition destroy get getParameterSpec getPropertyDefinitionOptions onError parameter reconfigure setCheckedInstVar}" ? {c1 b add x} {property b of ::C ist not multivalued} #? {c1 b add x} "method 'add' unknown for slot ::C::slot::b; $unknowns" ? {c1 c add x} {::c1: unable to dispatch method 'c'} ? {c1 eval {:c add x}} {property c of ::C ist not multivalued} #? {c1 eval {:c add x}} "method 'add' unknown for slot ::C::slot::c; $unknowns" ? {c1 d add x} {::c1: unable to dispatch method 'd'} ? {c1 eval {:d add x}} {::c1: unable to dispatch method 'd'} ? {c1 e add x} {::c1: unable to dispatch method 'e'} # # check incremental operations for variables (should fail in all # cases) # ? {c1 va add x} {::c1: unable to dispatch method 'va'} ? {c1 vb add x} {property vb of ::C ist not multivalued} #? {c1 vb add x} "method 'add' unknown for slot ::C::slot::vb; $unknowns" ? {c1 vc add x} {::c1: unable to dispatch method 'vc'} ? {c1 eval {:vc add x}} {property vc of ::C ist not multivalued} #? {c1 eval {:vc add x}} "method 'add' unknown for slot ::C::slot::vc; $unknowns" ? {c1 vd add x} {::c1: unable to dispatch method 'vd'} ? {c1 eval {:vd add x}} {::c1: unable to dispatch method 'vd'} ? {c1 ve add x} {::c1: unable to dispatch method 've'} # # The accessor should be a setter due to incremental # ? {C info method definition b} {::C public forward b -prefix value= ::C::slot::b %1 %self b} # # check error message on a typo. The following command does a # recreate. # ? {C property -accessor proceted {b b1}} {accessor value 'proceted' invalid; might be one of public|protected|private or none} # # The accessor is deleted due to the error # ? {C info method definition b} {} } # # test class-level properties and variables with -incremental # nx::test case class-level-incremental { nx::Class create CC { :property -incremental {a a1} :property -accessor public -incremental {b b1} :property -accessor protected -incremental {c c1} :property -accessor private -incremental {d d1} :property -accessor none -incremental {e e1} :variable -incremental va va1 :variable -accessor public -incremental vb vb1 :variable -accessor protected -incremental vc vc1 :variable -accessor private -incremental vd vd1 :variable -accessor none -incremental ve ve1 :public method call-local {v} {: -local $v get} :public method add-local {var value} {: -local $var add $value} :create c1 } # # The use of "-incremental" implies multivalued # ? {c1 info lookup syntax configure} {?-e /value .../? ?-a /value .../? ?-b /value .../? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} ? {c1 cget -a} a1 ? {c1 cget -b} b1 ? {c1 cget -c} "cget: unknown configure parameter -c" ? {c1 cget -d} "cget: unknown configure parameter -d" ? {c1 cget -va} "cget: unknown configure parameter -va" ? {c1 cget -vb} "cget: unknown configure parameter -vb" ? {c1 cget -vc} "cget: unknown configure parameter -vc" ? {c1 cget -vd} "cget: unknown configure parameter -vd" # # The use of "-incremental" implies an accessor # ? {c1 info lookup method a} "::nsf::classes::CC::a" ;# forcing accessor ? {c1 info lookup method b} "::nsf::classes::CC::b" ? {c1 info lookup method c} "::nsf::classes::CC::c" ? {c1 info lookup method d} "::nsf::classes::CC::d" ? {c1 info lookup method va} "::nsf::classes::CC::va" ;# forcing accessor ? {c1 info lookup method vb} "::nsf::classes::CC::vb" ? {c1 info lookup method vc} "::nsf::classes::CC::vc" ? {c1 info lookup method vd} "::nsf::classes::CC::vd" # # The use of "-incremental" implies an accessor, which is public # ? {nsf::method::property CC a call-protected} 0 ? {nsf::method::property CC b call-protected} 0 ? {nsf::method::property CC c call-protected} 1 ? {nsf::method::property CC d call-protected} 1 ? {nsf::method::property CC va call-protected} 0 ? {nsf::method::property CC vb call-protected} 0 ? {nsf::method::property CC vc call-protected} 1 ? {nsf::method::property CC vd call-protected} 1 ? {nsf::method::property CC a call-private} 0 ? {nsf::method::property CC b call-private} 0 ? {nsf::method::property CC c call-private} 0 ? {nsf::method::property CC d call-private} 1 ? {nsf::method::property CC va call-private} 0 ? {nsf::method::property CC vb call-private} 0 ? {nsf::method::property CC vc call-private} 0 ? {nsf::method::property CC vd call-private} 1 # # do we have variables set? # ? {c1 eval "info exists :a"} 1 ? {c1 eval "info exists :b"} 1 ? {c1 eval "info exists :c"} 1 ? {c1 eval "info exists :d"} 0 ? {c1 eval "info exists :va"} 1 ? {c1 eval "info exists :vb"} 1 ? {c1 eval "info exists :vc"} 1 ? {c1 eval "info exists :vd"} 0 # # can we call the accessor directly or via "eval" # ? {c1 a get} a1 ? {c1 b get} b1 ? {c1 c} {::c1: unable to dispatch method 'c'} ? {c1 d} {::c1: unable to dispatch method 'd'} ? {c1 eval ":a get"} a1 ? {c1 eval ":b get"} b1 ? {c1 eval ":c get"} c1 ? {c1 eval ":d"} {::c1: unable to dispatch method 'd'} ? {c1 va get} va1 ? {c1 vb get} vb1 ? {c1 vc} {::c1: unable to dispatch method 'vc'} ? {c1 vd} {::c1: unable to dispatch method 'vd'} ? {c1 eval ":va get"} va1 ? {c1 eval ":vb get"} vb1 ? {c1 eval ":vc get"} vc1 ? {c1 eval ":vd get"} {::c1: unable to dispatch method 'vd'} # # check the behavior of "private" properties and variables # ? {c1 call-local d} d1 ? {c1 call-local vd} vd1 ? {lsort [c1 info vars]} "__private a b c e va vb vc ve" ? {c1 eval "array get :__private"} "::CC,vd vd1 ::CC,d d1" # # check incremental operations for properties # ? {c1 a add x} {x a1} ? {c1 b add x} {x b1} ? {c1 c add x} {::c1: unable to dispatch method 'c'} ? {c1 eval {:c add x}} {x c1} ? {c1 d add x} {::c1: unable to dispatch method 'd'} ? {c1 eval {:d add x}} {::c1: unable to dispatch method 'd'} ? {c1 add-local d x} {x d1} ? {c1 e add x} {x e1} # # check incremental operations for variables # ? {c1 va add x} {x va1} ? {c1 vb add x} {x vb1} ? {c1 vc add x} {::c1: unable to dispatch method 'vc'} ? {c1 eval {:vc add x}} {x vc1} ? {c1 vd add x} {::c1: unable to dispatch method 'vd'} ? {c1 eval {:vd add x}} {::c1: unable to dispatch method 'vd'} ? {c1 add-local vd x} {x vd1} ? {c1 ve add x} {x ve1} # # The accessor should be a forwarder due to incremental # ? {CC info method definition b} {::CC public forward b -prefix value= ::CC::slot::b %1 %self b} # # check error message # ? {CC property -accessor proceted -incremental {b b1}} {accessor value 'proceted' invalid; might be one of public|protected|private or none} # # The accessor is deleted due to the error # ? {CC info method definition b} {} } ##################################################################### # object-level properties ##################################################################### # # Test object-level properties and variables without -incremental # nx::test case object-level { nx::Object create o1 { :object property {a a1} :object property -accessor public {b b1} :object property -accessor protected {c c1} :object property -accessor private {d d1} :object property -accessor none {e e1} :object variable va va1 :object variable -accessor public vb vb1 :object variable -accessor protected vc vc1 :object variable -accessor private vd vd1 :object variable -accessor none ve ve1 :public object method call-local {v} {: -local $v get} } # # check the slot for "a" # ? {o1 info lookup slots a} ::o1::per-object-slot::a # # just the public properties are accessible via the configure interface # ? {o1 info lookup syntax configure} {?-e /value/? ?-a /value/? ?-b /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} # # just the public properties are accessible via the cget interface # ? {o1 cget -a} a1 ? {o1 cget -b} b1 ? {o1 cget -c} {cget: unknown configure parameter -c} ? {o1 cget -d} {cget: unknown configure parameter -d} ? {o1 cget -va} {cget: unknown configure parameter -va} ? {o1 cget -vb} {cget: unknown configure parameter -vb} ? {o1 cget -vc} {cget: unknown configure parameter -vc} ? {o1 cget -vd} {cget: unknown configure parameter -vd} # # We do not have accessors in the default case and in the explicit "none" case. # ? {o1 info lookup method a} "" ? {o1 info lookup method b} "::o1::b" ? {o1 info lookup method c} "::o1::c" ? {o1 info lookup method d} "::o1::d" ? {o1 info lookup method e} "" ? {o1 info lookup method va} "" ? {o1 info lookup method vb} "::o1::vb" ? {o1 info lookup method vc} "::o1::vc" ? {o1 info lookup method vd} "::o1::vd" ? {o1 info lookup method ve} "" # # check public/protected/private settings # ? {nsf::method::property o1 b call-protected} 0 ? {nsf::method::property o1 c call-protected} 1 ? {nsf::method::property o1 d call-protected} 1 ? {nsf::method::property o1 vb call-protected} 0 ? {nsf::method::property o1 vc call-protected} 1 ? {nsf::method::property o1 vd call-protected} 1 ? {nsf::method::property o1 b call-private} 0 ? {nsf::method::property o1 c call-private} 0 ? {nsf::method::property o1 d call-private} 1 ? {nsf::method::property o1 vb call-private} 0 ? {nsf::method::property o1 vc call-private} 0 ? {nsf::method::property o1 vd call-private} 1 # # check if instance variables are created # ? {o1 eval "info exists :a"} 1 ? {o1 eval "info exists :b"} 1 ? {o1 eval "info exists :c"} 1 ? {o1 eval "info exists :d"} 0 ? {o1 eval "info exists :e"} 1 ? {o1 eval "info exists :va"} 1 ? {o1 eval "info exists :vb"} 1 ? {o1 eval "info exists :vc"} 1 ? {o1 eval "info exists :vd"} 0 ? {o1 eval "info exists :ve"} 1 # # check if we can dispatch accessors directly or via "eval" # ? {o1 a} {::o1: unable to dispatch method 'a'} ? {o1 b get} b1 ? {o1 c} {::o1: unable to dispatch method 'c'} ? {o1 d} {::o1: unable to dispatch method 'd'} ? {o1 eval ":a"} {::o1: unable to dispatch method 'a'} ? {o1 eval ":b get"} b1 ? {o1 eval ":c get"} c1 ? {o1 eval ":d"} {::o1: unable to dispatch method 'd'} ? {o1 va} {::o1: unable to dispatch method 'va'} ? {o1 vb get} vb1 ? {o1 vc} {::o1: unable to dispatch method 'vc'} ? {o1 vd} {::o1: unable to dispatch method 'vd'} ? {o1 eval ":va"} {::o1: unable to dispatch method 'va'} ? {o1 eval ":vb get"} vb1 ? {o1 eval ":vc get"} vc1 ? {o1 eval ":vd"} {::o1: unable to dispatch method 'vd'} # # check dispatch of private accessors and private variables # ? {o1 call-local d} d1 ? {o1 call-local vd} vd1 ? {lsort [o1 info vars]} "__private a b c e va vb vc ve" ? {o1 eval "array get :__private"} "::o1,d d1 ::o1,vd vd1" # # check error message # ? {o1 object property -accessor proceted {b b1}} {accessor value 'proceted' invalid; might be one of public|protected|private or none} } # # test object-level properties and variables with -incremental # nx::test case object-level-incremental { nx::Object create o1 { :object property -incremental {a a1} :object property -accessor public -incremental {b b1} :object property -accessor protected -incremental {c c1} :object property -accessor private -incremental {d d1} :object property -accessor none -incremental {e e1} :object variable -incremental va va1 :object variable -accessor public -incremental vb vb1 :object variable -accessor protected -incremental vc vc1 :object variable -accessor private -incremental vd vd1 :object variable -accessor none -incremental ve ve1 :public object method call-local {v} {: -local $v get} :public object method add-local {var value} {: -local $var add $value} } # # The use of "-incremental" implies multivalued # ? {o1 info lookup syntax configure} {?-e /value .../? ?-a /value .../? ?-b /value .../? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} ? {o1 cget -a} a1 ? {o1 cget -b} b1 ? {o1 cget -c} {cget: unknown configure parameter -c} ? {o1 cget -d} {cget: unknown configure parameter -d} ? {o1 cget -va} {cget: unknown configure parameter -va} ? {o1 cget -vb} {cget: unknown configure parameter -vb} ? {o1 cget -vc} {cget: unknown configure parameter -vc} ? {o1 cget -vd} {cget: unknown configure parameter -vd} # # The use of "-incremental" implies an accessor # ? {o1 info lookup method a} "::o1::a" ;# forcing accessor ? {o1 info lookup method b} "::o1::b" ? {o1 info lookup method c} "::o1::c" ? {o1 info lookup method d} "::o1::d" ? {o1 info lookup method va} "::o1::va" ;# forcing accessor ? {o1 info lookup method vb} "::o1::vb" ? {o1 info lookup method vc} "::o1::vc" ? {o1 info lookup method vd} "::o1::vd" # # The use of "-incremental" implies an accessor, which is public # ? {nsf::method::property o1 a call-protected} 0 ? {nsf::method::property o1 b call-protected} 0 ? {nsf::method::property o1 c call-protected} 1 ? {nsf::method::property o1 d call-protected} 1 ? {nsf::method::property o1 va call-protected} 0 ? {nsf::method::property o1 vb call-protected} 0 ? {nsf::method::property o1 vc call-protected} 1 ? {nsf::method::property o1 vd call-protected} 1 ? {nsf::method::property o1 a call-private} 0 ? {nsf::method::property o1 b call-private} 0 ? {nsf::method::property o1 c call-private} 0 ? {nsf::method::property o1 d call-private} 1 ? {nsf::method::property o1 va call-private} 0 ? {nsf::method::property o1 vb call-private} 0 ? {nsf::method::property o1 vc call-private} 0 ? {nsf::method::property o1 vd call-private} 1 # # do we have variables set? # ? {o1 eval "info exists :a"} 1 ? {o1 eval "info exists :b"} 1 ? {o1 eval "info exists :c"} 1 ? {o1 eval "info exists :d"} 0 ? {o1 eval "info exists :va"} 1 ? {o1 eval "info exists :vb"} 1 ? {o1 eval "info exists :vc"} 1 ? {o1 eval "info exists :vd"} 0 # # can we call the accessor directly or via "eval" # ? {o1 a get} a1 ? {o1 b get} b1 ? {o1 c} {::o1: unable to dispatch method 'c'} ? {o1 d} {::o1: unable to dispatch method 'd'} ? {o1 eval ":a get"} a1 ? {o1 eval ":b get"} b1 ? {o1 eval ":c get"} c1 ? {o1 eval ":d"} {::o1: unable to dispatch method 'd'} ? {o1 va get} va1 ? {o1 vb get} vb1 ? {o1 vc} {::o1: unable to dispatch method 'vc'} ? {o1 vd} {::o1: unable to dispatch method 'vd'} ? {o1 eval ":va get"} va1 ? {o1 eval ":vb get"} vb1 ? {o1 eval ":vc get"} vc1 ? {o1 eval ":vd"} {::o1: unable to dispatch method 'vd'} # # check the behavior of "private" properties and variables # ? {o1 call-local d} d1 ? {o1 call-local vd} vd1 ? {lsort [o1 info vars]} "__private a b c e va vb vc ve" ? {o1 eval "array get :__private"} "::o1,d d1 ::o1,vd vd1" # # check incremental operations for properties # ? {o1 a add x} {x a1} ? {o1 b add x} {x b1} ? {o1 c add x} {::o1: unable to dispatch method 'c'} ? {o1 eval {:c add x}} {x c1} ? {o1 d add x} {::o1: unable to dispatch method 'd'} ? {o1 eval {:d add x}} {::o1: unable to dispatch method 'd'} ? {o1 add-local d x} {x d1} ? {o1 e add x} {x e1} # # check incremental operations for variables # ? {o1 va add x} {x va1} ? {o1 vb add x} {x vb1} ? {o1 vc add x} {::o1: unable to dispatch method 'vc'} ? {o1 eval {:vc add x}} {x vc1} ? {o1 vd add x} {::o1: unable to dispatch method 'vd'} ? {o1 eval {:vd add x}} {::o1: unable to dispatch method 'vd'} ? {o1 add-local vd x} {x vd1} ? {o1 ve add x} {x ve1} # # The accessor should be a forwarder due to incremental # ? {o1 info object method definition b} {::o1 public object forward b -prefix value= ::o1::per-object-slot::b %1 %self b} # # check error message # ? {o1 object property -accessor proceted {b b1}} {accessor value 'proceted' invalid; might be one of public|protected|private or none} # # The accessor is deleted due to the error # ? {o1 info object method definition b} {} } # # Tests for experimental "value add", "value assign" ... # # nx::test case property-value-incremental { # nx::Object create o1 { # :object property -incremental {a a1} # } # ? {o1 a add x} {x a1} # ? {o1 a assign {a1}} {a1} # nsf::configure debug 2 # ? {o1 a value add x } {x a1} # ? {o1 a value assign {a b c}} {a b c} # ? {o1 a value get } {a b c} # ? {o1 a value add x } {x a b c} # ? {o1 a value add z end} {x a b c z} # } # # Test interactions between multiplicity and incremental # nx::test case property-incremental-multiplicity { nx::Object create o1 { :object property -incremental a:integer,0..n :object property -incremental b:integer,1..n :object property -incremental c:integer,0..1 :object property -incremental d:integer,1..1 } ? {o1 info object slots a} "::o1::per-object-slot::a" ? {[o1 info object slots a] eval {set :multiplicity}} "0..n" ? {[o1 info object slots b] eval {set :multiplicity}} "1..n" ? {[o1 info object slots c] eval {set :multiplicity}} "0..n" ? {[o1 info object slots d] eval {set :multiplicity}} "1..n" ? {o1 info variable definition [o1 info object variables a]} \ "::o1 object property -accessor public -incremental a:integer,0..n" ? {o1 info variable definition [o1 info object variables b]} \ "::o1 object property -accessor public -incremental b:integer,1..n" ? {o1 info variable definition [o1 info object variables c]} \ "::o1 object property -accessor public -incremental c:integer,0..n" ? {o1 info variable definition [o1 info object variables d]} \ "::o1 object property -accessor public -incremental d:integer,1..n" ? {o1 a set {1 2 3}} {1 2 3} ? {o1 b set {1 2 3}} {1 2 3} ? {o1 a set ""} {} ? {o1 b set ""} {invalid value for parameter 'value': list is not allowed to be empty} ? {o1 c set ""} {} ? {o1 d set ""} {invalid value for parameter 'value': list is not allowed to be empty} } ##################################################################### # tests with class object ##################################################################### # # check performance of class-level configure and cget # nx::test case class-object-properties { nx::Class create C { :property {a a1} :variable va va1 :object property {b b1} :object variable vb b1 :create c1 } # # just the public properties are accessible via the configure interface # ? {c1 info lookup syntax configure} {?-a /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} ? {c1 cget -a} a1 ? {c1 configure -a a2} "" ? {C info lookup syntax configure} {?-b /value/? ?-mixins /mixinreg .../? ?-superclasses /class .../? ?-filters /filterreg .../? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} ? {C cget -b} b1 ? {C configure -b b2} "" ? {C cget -b} b2 } ##################################################################### # performance tests ##################################################################### nx::test configure -count 10000 # # check performance of class-level configure and cget # nx::test case class-level-perf { nx::Class create C { :property {a a1} :property -accessor public {b b1} :property -accessor protected {c c1} :property -accessor private {d d1} :property -accessor none {e e1} :variable va va1 :variable -accessor public vb vb1 :variable -accessor protected vc vc1 :variable -accessor private vd vd1 :variable -accessor none ve ve1 :create c1 } nx::Class create D { :object property {cp 101} :property {a a1} :property -accessor public {b b1} :property -accessor protected {c c1} :property -accessor private {d d1} :property -accessor none {e e1} :variable va va1 :variable -accessor public vb vb1 :variable -accessor protected vc vc1 :variable -accessor private vd vd1 :variable -accessor none ve ve1 :create d1 } # # just the public properties are accessible via the configure interface # package require nx::volatile ? {c1 info lookup syntax configure} {?-e /value/? ?-a /value/? ?-b /value/? ?-volatile? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} set e [C eval :__object_configureparameter] ? {C eval :__object_configureparameter} $e ? {c1 cget -a} a1 ? {c1 configure -a a2} "" ? {C configure -class ::nx::Class} "" ? {C cget -class} ::nx::Class ? {C cget -mixin} "" ? {C cget -filter} "" # ? {C cget -noinit} 0 ? {C cget -volatile} 0 # # check influence of class-level per-object properties # ? {d1 info lookup syntax configure} {?-e /value/? ?-a /value/? ?-b /value/? ?-volatile? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} set e [D eval :__object_configureparameter] ? {D eval :__object_configureparameter} $e ? {d1 cget -a} a1 ? {d1 configure -a a2} "" ? {D configure -class ::nx::Class} "" ? {D cget -class} ::nx::Class ? {D cget -cp} 101 ? {D configure -cp 102} "" ? {D cget -cp} 102 } # # check performance of class-level configure and cget # nx::test case object-level-perf { nx::Object create o1 { :object property {a a1} :object property -accessor public {b b1} :object property -accessor protected {c c1} :object property -accessor private {d d1} :object property -accessor none {e e1} :object variable va va1 :object variable -accessor public vb vb1 :object variable -accessor protected vc vc1 :object variable -accessor private vd vd1 :object variable -accessor none ve ve1 } # # just the public properties are accessible via the configure interface # ? {o1 info lookup syntax configure} {?-e /value/? ?-a /value/? ?-b /value/? ?-object-mixins /mixinreg .../? ?-object-filters /filterreg .../? ?-class /class/? ?/__initblock/?} set e [o1 eval :__object_configureparameter] ? {o1 eval :__object_configureparameter} $e ? {o1 cget -a} a1 ? {o1 configure -a a2} "" ? {o1 b get} b1 ? {o1 b set b2} "b2" ? {o1 configure -class ::nx::Object} "" ? {o1 cget -class} ::nx::Object } nx::test case extend-parent-class-info { nx::Class create Foo nx::Class create Bar -superclass Foo ? {llength [Bar info lookup parameters create]} 5 # # extend the superclass, subclass should become aware of this # Foo property y ? {llength [Bar info lookup parameters create]} 6 } nx::test case extend-parent-class-info-cache { nx::Class create Foo nx::Class create Bar -superclass Foo ? {llength [Bar info lookup parameters create]} 5 # # Let Bar cache the objectparameters, and extend later the # superclass # Bar new Foo property y ? {llength [Bar info lookup parameters create]} 6 } nx::test case extend-parent-class-info-cache-configure { nx::Class create Foo nx::Class create Bar -superclass Foo ? {llength [Bar info lookup parameters create]} 5 # # Let Bar cache the objectparameters, and extend later the # superclass # Bar create b1 Foo property y # access obejctparamter indirectly via configure ? {b1 configure -y 2} "" } nx::test case extend-class-mixin-info { nx::Class create Baz nx::Class create Foo -mixin Baz nx::Class create Bar -mixin Foo Bar create bar; # cache becomes hot! ? {llength [Bar info lookup parameters create]} 5 Foo property y ? {llength [Bar info lookup parameters create]} 6 Baz property z ? {llength [Bar info lookup parameters create]} 7 Baz delete property z ? {llength [Bar info lookup parameters create]} 6 Foo delete property y ? {llength [Bar info lookup parameters create]} 5 } nx::test case extend-class-mixin-configure { nx::Class create Baz nx::Class create Foo -mixin Baz nx::Class create Bar -mixin Foo Bar create bar; # cache becomes hot! Foo property y bar configure -y 1 ? {bar cget -y} 1 Baz property z bar configure -z 2 ? {bar cget -z} 2 bar configure -y 3 ? {bar cget -y} 3 Bar property w bar configure -w 4 ? {bar cget -w} 4 } nx::test case dynamic-transitive-mixin-info { nx::Class create Foo nx::Class create Bar nx::Class create Baz Bar create bar; # cache object parameters in class Bar Baz create baz; # cache object parameters in class Baz ? {llength [Bar info lookup parameters create]} 5 Foo property y ? {llength [Bar info lookup parameters create]} 5 Bar mixins add Foo ? {llength [Bar info lookup parameters create]} 6 ? {bar configure -y 1} "" ? {llength [Baz info lookup parameters create]} 5 Baz mixins add Bar ? {llength [Baz info lookup parameters create]} 6 Foo property z ? {llength [Baz info lookup parameters create]} 7 ? {baz configure -z 1} "" ? {bar configure -z 1} "" } nx::test configure -count 1 nx::test case indirect-transitive-mixin-info { nx::Class create M0 {:public method foo {} {return M}} nx::Class create M1 -superclass M0 nx::Class create M2 -superclass M1 nx::Class create C {:public method foo {} {return C}} nx::Class create D -superclass C C create c1 D create d1 M0 create m0 M1 create m1 M2 create m2 ? {lmap p [C info lookup parameters create] {nsf::parameter::info name $p}} \ "objectName object-mixins object-filters class __initblock" set base [llength [lmap p [C info lookup parameters create] {nsf::parameter::info name $p}]] ? [list set _ $base] 5 ? {llength [C info lookup parameters create]} $base ? {llength [D info lookup parameters create]} $base ? {llength [M0 info lookup parameters create]} $base ? {llength [M1 info lookup parameters create]} $base ? {llength [M2 info lookup parameters create]} $base M0 property x ? {llength [M0 info lookup parameters create]} [expr {$base + 1}] ? {llength [M1 info lookup parameters create]} [expr {$base + 1}] ? {llength [M2 info lookup parameters create]} [expr {$base + 1}] ? {c1 foo} C ? {d1 foo} C ? {c1 info precedence} "::C ::nx::Object" ? {d1 info precedence} "::D ::C ::nx::Object" ? {C info subclasses -dependent} "::C ::D" ? {C info subclasses -closure} "::C ::D" ? {M0 info subclasses -dependent} "::M0 ::M1 ::M2" ? {M0 info subclasses -closure} "::M0 ::M1 ::M2" #puts stderr =========C-mixin-add-M2 C mixins add M2 #puts stderr ========= ? {c1 foo} M ? {d1 foo} M ? {c1 info precedence} "::M2 ::M1 ::M0 ::C ::nx::Object" ? {d1 info precedence} "::M2 ::M1 ::M0 ::D ::C ::nx::Object" ? {C info heritage} "::M2 ::M1 ::M0 ::nx::Object" ? {D info heritage} "::M2 ::M1 ::M0 ::C ::nx::Object" ? {C info subclasses -dependent} "::C ::D" ? {C info subclasses -closure} "::C ::D" ? {M0 info subclasses -dependent} "::M0 ::M1 ::M2 ::C ::D" ? {M0 info subclasses -closure} "::M0 ::M1 ::M2" # Only M2 is a direct mixin, visible through "mixinof", # but query-able via transitive -closure operator ? {M2 info mixinof} "::C" ? {M2 info mixinof -closure} "::C ::D" ? {M1 info mixinof} "" ? {M1 info mixinof -closure} "::C ::D" ? {M0 info mixinof} "" ? {M0 info mixinof -closure} "::C ::D" ? {lmap p [C info lookup parameters create] {nsf::parameter::info name $p}} \ "objectName x object-mixins object-filters class __initblock" ? {llength [C info lookup parameters create]} [expr {$base + 1}] ? {llength [D info lookup parameters create]} [expr {$base + 1}] #puts stderr =========-M1-property M1 property y #puts stderr ========= ? {C info heritage} "::M2 ::M1 ::M0 ::nx::Object" #::nsf::parameter::cache::classinvalidate ::C ? {lmap p [C info lookup parameters create] {nsf::parameter::info name $p}} \ "objectName y x object-mixins object-filters class __initblock" ? {lmap p [M0 info lookup parameters create] {nsf::parameter::info name $p}} \ "objectName x object-mixins object-filters class __initblock" ? {lmap p [M1 info lookup parameters create] {nsf::parameter::info name $p}} \ "objectName y x object-mixins object-filters class __initblock" ? {llength [C info lookup parameters create]} [expr {$base + 2}] ? {llength [D info lookup parameters create]} [expr {$base + 2}] ? {llength [M0 info lookup parameters create]} [expr {$base + 1}] ? {llength [M1 info lookup parameters create]} [expr {$base + 2}] ? {llength [M2 info lookup parameters create]} [expr {$base + 2}] # clearning the mixin has to reset the orders of the instances of C and D #puts stderr =========C-mixin-clear C mixins clear #puts stderr ========= ? {c1 foo} C ? {d1 foo} C ? {c1 info precedence} "::C ::nx::Object" ? {d1 info precedence} "::D ::C ::nx::Object" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/protected.test000066400000000000000000000612131242365656200151300ustar00rootroot00000000000000# -*- Tcl -*- package require nx package require nx::test nx::test configure -count 1 nx::test case call-protected { nx::Class create C { :public alias SET ::set :public method foo {} {return [current method]} :public method bar {} {return [current method]} :public method bar-foo {} { c1 foo } :public method bar-SET {} { c1 SET x 1 } } C create c1 C create c2 ? {c1 SET x 1} {1} ? {c1 foo} {foo} ? {c1 bar-SET} {1} ? {c1 bar-foo} {foo} ::nsf::method::property C SET call-protected true ? {c1 SET x 1} {::c1: unable to dispatch method 'SET'} ? {nx::dispatch c1 SET x 2} {2} "dispatch of protected methods works" ? {c1 foo} {foo} ? {c1 bar} {bar} ? {c1 bar-SET} {1} ? {c1 bar-foo} {foo} ? {c2 bar-SET} {::c1: unable to dispatch method 'SET'} ? {c2 bar-foo} {foo} ::nsf::method::property C foo call-protected true ? {c1 SET x 1} {::c1: unable to dispatch method 'SET'} ? {nx::dispatch c1 SET x 2} {2} "dispatch of protected methods works" ? {c1 bar} {bar} "other method work" ? {c1 foo} {::c1: unable to dispatch method 'foo'} ? {c1 bar-SET} {1} "internal call of protected C implementend method" ? {c1 bar-foo} {foo} "internal call of protected Tcl implemented method" ? {c2 bar-SET} {::c1: unable to dispatch method 'SET'} ? {c2 bar-foo} {::c1: unable to dispatch method 'foo'} # unset call protected ? {::nsf::method::property C SET call-protected} 1 ::nsf::method::property C SET call-protected false ? {::nsf::method::property C SET call-protected} 0 ? {::nsf::method::property C foo call-protected} 1 ::nsf::method::property C foo call-protected false ? {::nsf::method::property C foo call-protected} 0 ? {c1 SET x 3} 3 ? {nx::dispatch c1 SET x 2} {2} ? {c1 foo} {foo} ? {c1 bar} {bar} ? {c1 bar-SET} {1} ? {c1 bar-foo} {foo} ? {c2 bar-SET} 1 ? {c2 bar-foo} {foo} # define a protected method C protected method foo {} {return [current method]} ? {::nsf::method::property C SET call-protected} 0 ? {c1 SET x 3} 3 ? {nx::dispatch c1 SET x 4} {4} ? {c1 foo} {::c1: unable to dispatch method 'foo'} ? {c1 bar} {bar} ? {c1 bar-SET} {1} ? {c1 bar-foo} foo ? {c2 bar-SET} 1 ? {c2 bar-foo} {::c1: unable to dispatch method 'foo'} } # # Check protection + filter # # Allow to call methods as filters even if these are protected or # private. # nx::test case protected+filter { nx::Class create C { :method f1 args { next } :private method f2 args { next } :public method foo {} { return foo} } C create c1 ? {c1 foo} foo # add a protected filter c1 object filters add f1 ? {c1 foo} foo # add a private filter c1 object filters add f2 ? {c1 foo} foo } nx::test case redefined-protected { nx::Class create C { :public alias SET ::set :public method foo {} {return [current method]} } # # Define SET and foo as redefined-protected # ? {::nsf::method::property C SET redefine-protected true} 1 ? {::nsf::method::property C foo redefine-protected true} 1 ? {C method SET {a b c} {...}} \ {refuse to overwrite protected method 'SET'; derive e.g. a sub-class!} \ "redefine method SET" ? {C method foo {a b c} {...}} \ {refuse to overwrite protected method 'foo'; derive e.g. a sub-class!} \ "redefine method foo" # check a predefined protection ? {::nx::Class method create {a b c} {...}} \ {refuse to overwrite protected method 'create'; derive e.g. a sub-class!} \ "redefine method create" # try to redefine predefined protected method via alias ? {::nsf::method::alias nx::Class create ::set} \ {refuse to overwrite protected method 'create'; derive e.g. a sub-class!} \ "redefine alias create" # try to redefine via forward ? {C forward SET ::set} \ {refuse to overwrite protected method 'SET'; derive e.g. a sub-class!} \ "redefine forward SET" # try to redefine via setter ? {C property -accessor public SET} \ {refuse to overwrite protected method 'SET'; derive e.g. a sub-class!} \ "redefine property SET" # redefine-protect object specific method nx::Object create o o object method foo {} {return 13} ::nsf::method::property o foo redefine-protected true ? {o object method foo {} {return 14}} \ {refuse to overwrite protected method 'foo'; derive e.g. a sub-class!} } # # Use case for private: # Hide "helper methods of e.g. mixin" # nx::test case private-helper { nx::Class create B { :public method bar {} {return "B.bar [next]"} :public method baz {} {return "B.baz [next]"} :create b1 { :public object method baz {} {return "b1.baz [next]"} } } nx::Class create C -superclass B { :public method bar {} {return "C.bar [next]"} :public method baz {} {return "C.baz [next]"} :create c1 { :public object method baz {} {return "c1.baz [next]"} } } # Behavior without mixin with private methods ? {b1 bar} "B.bar " ? {b1 baz} "b1.baz B.baz " ? {c1 bar} "C.bar B.bar " ? {c1 baz} "c1.baz C.baz B.baz " # # Define a mixin with helper methods "bar" and "baz". The helper # methods are defined as private to avoid interference. # nx::Class create M { :public method foo {} {: -local bar} :private method bar {} {: -local baz} :private method baz {} {return "M.baz"} } # Behavior with mixin . THe private helper methods are "invisible" # for invocation and next path. B mixins add M ? {b1 bar} "B.bar " ? {b1 baz} "b1.baz B.baz " ? {c1 bar} "C.bar B.bar " ? {c1 baz} "c1.baz C.baz B.baz " ? {b1 foo} "M.baz" ? {c1 foo} "M.baz" } # # Use case for private: # Hide "helper object specific helper methods" # nx::test case object-private-helper { nx::Class create B { :public method bar {} {return "B.bar [next]"} :public method baz {} {return "B.baz [next]"} :create b1 { :public object method foo {} {: -local bar} :private object method bar {} {: -local baz} :private object method baz {} {return "b1.baz"} } } nx::Class create C -superclass B { :public method bar {} {return "C.bar [next]"} :public method baz {} {return "C.baz [next]"} :create c1 { :public object method foo {} {: -local bar} :private object method bar {} {: -local baz} :private object method baz {} {return "c1.baz"} } } # Behavior of per-object helper methods, which are invisible for # invocation through "bar" and "baz" ? {b1 bar} "B.bar " ? {b1 baz} "B.baz " ? {b1 foo} "b1.baz" ? {c1 bar} "C.bar B.bar " ? {c1 baz} "C.baz B.baz " ? {c1 foo} "c1.baz" # # Define a mixin class which shadows "bar" and "baz". The behavior # of the object-methods with its private methods is not affected. # nx::Class create M { :public method bar {} {return "M.bar [next]"} :public method baz {} {return "M.baz [next]"} } B mixins add M ? {b1 bar} "M.bar B.bar " ? {b1 baz} "M.baz B.baz " ? {b1 foo} "b1.baz" ? {c1 bar} "M.bar C.bar B.bar " ? {c1 baz} "M.baz C.baz B.baz " ? {c1 foo} "c1.baz" } # # Check local + filter # nx::test case local+filter { nx::Class create C { :method f1 args { return "f1 [next]" } :public method foo {} { return "C.foo [: -local bar]"} :private method bar {} { return "bar"} :public method baz {} { return "C.baz [next]"} } nx::Class create D -superclass C { :public method baz {} { return "D.baz [next]"} } D create d1 ? {d1 baz} "D.baz C.baz " ? {d1 foo} "C.foo bar" ? {d1 bar} "::d1: unable to dispatch method 'bar'" # add a filter; be sure that we still can call the private -local # method d1 object filters add f1 ? {d1 baz} "f1 D.baz C.baz " ? {d1 foo} "f1 C.foo f1 bar" ? {d1 bar} "::d1: unable to dispatch method 'bar'" # remove the filter d1 object filters set "" # define call to private method via method handle C public method foo {} { return "C.foo [[self] [C info method registrationhandle bar]]"} # the behavior without filter, should be like above ? {d1 baz} "D.baz C.baz " ? {d1 foo} "C.foo bar" ? {d1 bar} "::d1: unable to dispatch method 'bar'" # add a filter; be sure that we still can call the private method d1 object filters add f1 ? {d1 baz} "f1 D.baz C.baz " ? {d1 foo} "f1 C.foo f1 bar" ? {d1 bar} "::d1: unable to dispatch method 'bar'" } # # test private # nx::test case private { nx::Class create B { :private method p1 {} {return B.p1} :private method p2 {} {return B.p2} :public method p3 {} {return B.p3} :public method p4 {} {return B.p4} :create b1 } nx::Class create C -superclass B { :private method p1 {} {return "C.p1 [next]"} :public method p2 {} {return "C.p2 [next]"} :private method p3 {} {return "C.p3 [next]"} :public method p4 {} {return "C.p4 [next]"} :create c1 } nx::Class create D -superclass C { :public method p1 {} {return "D.p1 [next]"} :public method p2 {} {return "D.p2 [next]"} :public method p3 {} {return "D.p3 [next]"} :public method p4 {} {return "D.p4 [next]"} :create d1 } # check introspection and "-callprotection" filter ? {lsort [C info methods]} "p2 p4" ? {lsort [C info methods -callprotection all]} "p1 p2 p3 p4" ? {lsort [C info methods -callprotection public]} "p2 p4" ? {lsort [C info methods -callprotection protected]} "" ? {lsort [C info methods -callprotection private]} "p1 p3" # called shadowed # C.p1 private B.p1 private # C.p2 public B.p2 private # C.p3 private B.p3 public # C.p4 public B.p4 public ? {c1 p1} "::c1: unable to dispatch method 'p1'" ? {c1 p2} "C.p2 " ? {c1 p3} "B.p3" ? {c1 p4} "C.p4 B.p4" # called shadowed shadowed # D.p1 public C.p1 private B.p1 private # D.p2 public C.p2 public B.p2 private # D.p3 public C.p3 private B.p3 public # D.p4 public C.p4 public B.p4 public ? {d1 p1} "D.p1 " ? {d1 p2} "D.p2 C.p2 " ? {d1 p3} "D.p3 B.p3" ? {d1 p4} "D.p4 C.p4 B.p4" # add on B calls to local C eval { :public method q1 {} {: -local p1} :public method q3 {} {: -local p3} } # all chains start with C, since local resolve works ? {c1 q1} "C.p1 " ? {c1 q3} "C.p3 B.p3" # calls via method handles allows us to dispatch private methods, # results like "-local" resolves above ? {c1 [C info method registrationhandle p1]} "C.p1 " ? {c1 [C info method registrationhandle p3]} "C.p3 B.p3" # calls via method handles allows us to dispatch private methods, # results like "-local" resolves above ? {nx::dispatch c1 [C info method registrationhandle p1]} "C.p1 " ? {nx::dispatch c1 [C info method registrationhandle p3]} "C.p3 B.p3" # we can't call the private method via dispatch, since the private # methods are removed from the search for methods ? {nx::dispatch c1 p1} "::c1: unable to dispatch method 'p1'" ? {nx::dispatch c1 p3} "B.p3" # via dispatch, the local flag uses (as always) the context of the # currently execting class, which is not provided below ? {nx::dispatch c1 -local p1} "::c1: unable to dispatch method 'p1'" } # # test ": -local" on classes # nx::test case class-my-local { nx::Class create Base { :private method baz {a b} { expr {$a + $b} } :public method foo {a b} {: -local baz $a $b} } nx::Class create Sub -superclass Base { :public method bar {a b} {: -local baz $a $b} :private method baz {a b} { expr {$a * $b} } :create s1 } ? {s1 foo 3 4} 7 ? {s1 bar 3 4} 12 ? {s1 baz 3 4} {::s1: unable to dispatch method 'baz'} } # # test ": -local" on objects # nx::test case object-my-local { nx::Class create M { :public method foo {} {return "M [next]"} :public method foo2 {} {return "M2 [next]"} } nx::Object create o1 { :protected object method foo {} {return o1} :public object method foo2 {} {:foo} :public object method bar {} {: -local foo} } ? {o1 foo} {::o1: unable to dispatch method 'foo'} ? {o1 bar} o1 ? {o1 foo2} o1 o1 object mixins add M ? {o1 foo} "M o1" ? {o1 bar} "o1" ? {o1 foo2} "M2 M o1" } # # test "my" + path instead of ": -local" on classes # nx::test case my+handle-instead-of-my-local { nx::Class create Base { :private method privateMethod {a b} { expr {$a + $b} } :public method foo {a b} {: [Base info method registrationhandle privateMethod] $a $b} } nx::Class create Sub -superclass Base { :public method bar {a b} {: [Sub info method registrationhandle privateMethod] $a $b} :private method privateMethod {a b} { expr {$a * $b} } :create s1 } ? {s1 foo 3 4} 7 ? {s1 bar 3 4} 12 } # # test object::dispatch instead of ": -local" on classes # nx::test case dispatch-instead-of-my-local { nx::Class create Base { :private method privateMethod {a b} { expr {$a + $b} } :public method foo {a b} { dispatch [self] [Base info method registrationhandle privateMethod] $a $b } } nx::Class create Sub -superclass Base { :public method bar {a b} { dispatch [self] [Sub info method registrationhandle privateMethod] $a $b } :private method privateMethod {a b} { expr {$a * $b} } :create s1 } ? {s1 foo 3 4} 7 ? {s1 bar 3 4} 12 } # # Test -system flag on dispatch with explicit receiver # nx::test case system-flag { # # create an object, which overloads some system behavior # nx::Object create o1 { :public object method info {} {return "overloads system info"} :public object method destroy {} {return "overloads system destroy"} :public object method "object method" args {return "overloads method 'object method'"} :object variable v 1 } ? {o1 info} "overloads system info" ? {o1 ::nx::Object::slot::__info::vars} "v" ? {o1 [nx::Object info method definitionhandle "info vars"]} "v" #? {o1 -system info vars} "v" ? {nx::dispatch o1 -system info vars} "v" #? {o1 -system} "no method name specified" ? {o1 object method foo {} {return foo}} "overloads method 'object method'" ? {nx::dispatch o1 -system public object method foo {} {return foo}} "::o1::foo" ? {o1 destroy} "overloads system destroy" ? {nsf::object::exists o1} 1 ? {nx::dispatch o1 -system destroy} "" ? {nsf::object::exists o1} 0 # # create a class, which overloads some system behavior # nx::Class create C { :public method info {} {return "overloads system info"} :public method destroy {} {return "overloads system destroy"} :variable v 1 :create c1 } ? {c1 info} "overloads system info" ? {c1 ::nx::Object::slot::__info::vars} "v" ? {c1 [nx::Object info method definitionhandle "info vars"]} "v" #? {c1 -system info vars} "v" ? {nx::dispatch c1 -system info vars} "v" ? {c1 destroy} "overloads system destroy" ? {nsf::object::exists c1} 1 #? {c1 -system destroy} "" ? {nx::dispatch c1 -system destroy} "" ? {nsf::object::exists c1} 0 } # # Check my-local + private + next # # Never call a private method via "next", but allow "next" from # private methods # nx::test case class-my-local+next { nx::Class create Base { :private method baz {a b} { expr {$a + $b} } :protected method baz2 {a b} { expr {$a + $b} } :public method foo {a b} {: -local baz $a $b} :create b1 } # we can call Base.baz only through Base.foo ? {b1 foo 4 5} 9 ? {b1 baz 4 5} {::b1: unable to dispatch method 'baz'} # Define and register a mixin class, where method "foo" is calling a # private method via ": -local" nx::Class create Mix { :private method baz {a b} { expr {$a ** $b} } :public method foo {a b} {: -local baz $a $b} } b1 object mixins add Mix # we can call Mix.baz only through Mix.foo ? {b1 foo 4 5} 1024 ? {b1 baz 4 5} {::b1: unable to dispatch method 'baz'} # # the private method has a next # nx::Class create Intermediate -superclass Base { :private method baz {a b} { next } :private method baz2 {a b} { next } :public method foo {a b} {: -local baz $a $b} :public method foo2 {a b} {: -local baz2 $a $b} :create i1 } # next in the private method reaches a private method, which is ignored ? {i1 foo 4 5} "" ? {i1 baz 4 5} {::i1: unable to dispatch method 'baz'} # next in the private method reaches a non-private method, which is honored ? {i1 foo2 4 5} 9 nx::Class create Sub -superclass Intermediate { :public method bar {a b} {: -local baz $a $b} :private method baz {a b} { expr {$a * $b} } :create s1 } # next in the private method reaches a private method, which is ignored ? {s1 foo 4 5} "" ? {s1 baz 4 5} {::s1: unable to dispatch method 'baz'} # next in the private method reaches a non-private method, which is honored ? {s1 foo2 4 5} 9 ? {s1 bar 4 5} 20 # add per-class mixin Sub mixins add Mix # foo is shadowed in the mixin and calls the mixin-private method ? {s1 foo 4 5} 1024 ? {s1 baz 4 5} {::s1: unable to dispatch method 'baz'} # next in the private method reaches a non-private method, which is honored ? {s1 foo2 4 5} 9 ? {s1 bar 4 5} 20 } # # Test setting / clearing private and protected flags # nx::test case call-protected-flags { Class create C C protected method foo {} {return foo} ? {::nsf::method::property C foo call-protected} 1 ? {::nsf::method::property C foo call-private} 0 ? {C info method definition foo} "::C protected method foo {} {return foo}" C public method foo {} {return foo} ? {::nsf::method::property C foo call-protected} 0 ? {::nsf::method::property C foo call-private} 0 ? {C info method definition foo} "::C public method foo {} {return foo}" C private method foo {} {return foo} ? {::nsf::method::property C foo call-protected} 1 ? {::nsf::method::property C foo call-private} 1 ? {C info method definition foo} "::C private method foo {} {return foo}" ? {::nsf::method::property C foo call-private false} 0 ? {::nsf::method::property C foo call-protected} 1 ? {::nsf::method::property C foo call-private} 0 ? {::nsf::method::property C foo call-private true} 1 ? {::nsf::method::property C foo call-protected} 1 ? {::nsf::method::property C foo call-private} 1 ? {::nsf::method::property C foo call-protected false} 0 ? {::nsf::method::property C foo call-protected} 0 ? {::nsf::method::property C foo call-private} 0 } # # private subobjects # nx::test case private-subobject { nx::Object create obj { :public object method foo {} {return foo-[self]} nx::Object create [self]::child { :public object method bar {} {return bar-[self]} } } ? {obj child bar} "bar-::obj::child" ? {obj foo} "foo-::obj" ? {obj info object methods} "child foo" ? {::nsf::method::property obj foo call-private 1} 1 ? {obj child bar} "bar-::obj::child" ? {obj foo} {::obj: unable to dispatch method 'foo'} ? {obj info object methods} "child" ? {::nsf::method::property obj child call-private 1} 1 ? {obj child bar} {::obj: unable to dispatch method 'child'} ? {obj foo} {::obj: unable to dispatch method 'foo'} ? {obj info object methods} "" ? {::nsf::method::property obj foo call-protected 0} 0 ? {obj child bar} {::obj: unable to dispatch method 'child'} ? {obj foo} "foo-::obj" ? {obj info object methods} "foo" ? {::nsf::method::property obj child call-protected 0} 0 ? {obj child bar} "bar-::obj::child" ? {obj foo} "foo-::obj" ? {obj info object methods} "child foo" } # # Test protected and private object properties # nx::test case protected-priv-class-property { nx::Class create C { :property -accessor public {a a1} :property -accessor protected {b b1} :property -accessor private {c c1} :property -accessor private {d:integer 1} :public method foo {p} {return [: $p get]} :public method bar {p} {return [: -local $p get]} :public method baz {p v} {return [: -local $p set $v]} :create c1 } # call properties directly ? {c1 a get} a1 ? {c1 b get} {::c1: unable to dispatch method 'b'} ? {c1 c get} {::c1: unable to dispatch method 'c'} # call properties via method ? {c1 foo a} a1 ? {c1 foo b} b1 ? {c1 foo c} {::c1: unable to dispatch method 'c'} # call properties via method via "-local" dispatch ? {c1 bar a} a1 ? {c1 bar b} b1 ? {c1 bar c} {c1} ? {lsort [c1 info vars]} "__private a b" ? {c1 eval {lsort [array names :__private]}} "::C,c ::C,d" # Private property with value constraint ? {c1 bar d} {1} ? {c1 baz d 2} {2} ? {c1 bar d} {2} ? {c1 baz d x} {expected integer but got "x" for parameter "value"} # # Define a private property with the same name as the private # property in the superclass; define a public per-object property # with the same name. The call "d1 c" resolves to the per-object # property, the private properties are accessed via methods. # The values of the private properties do not conflict. # nx::Class create D -superclass C { :property -accessor private {c c1d} :public method bard {p} {return [: -local $p get]} :create d1 { :object property -accessor public {c c1o} } } ? {d1 bar c} c1 ? {d1 bard c} c1d ? {d1 c get} c1o # # Define a public property with the same name as the private # property in the superclass; define private per-object property # with the same name. The call "d1 c" resolves to the public # property on D, the private properties are accessed via methods. # The values of the private properties do not conflict. # nx::Class create D -superclass C { :property -accessor public {c c1d} :public method bard {p} {return [: -local $p get]} :create d1 { :object property -accessor private {c c1o} :public object method bard1 {p} {return [: -local $p get]} } } ? {d1 bar c} c1 ? {d1 bard c} c1d ? {d1 bard1 c} c1o ? {d1 c get} c1d } # # Test properties in class hierarchy, where a subclass defines a # private property with the same name as a property in a superclass. # nx::test case private-shadows-public-property { nx::Class create C { :property -accessor public {x c} } nx::Class create D -superclass C { :property -accessor private {x d} :public method bar-d {p} {return [: -local $p get]} } nx::Class create E -superclass D { :property -accessor private {x e} :public method bar-e {p} {return [: -local $p get]} } E create e1 ? {e1 x get} c ? {e1 bar-d x} d ? {e1 bar-e x} e } # # Test protected and private class properties # nx::test case protected-priv-object-property { nx::Object create o { :object property -accessor public {a a1} :object property -accessor protected {b b1} :object property -accessor private {c c1} :object property -accessor private {d:integer 1} :public object method foo {p} {return [: $p get]} :public object method bar {p} {return [: -local $p get]} :public object method baz {p v} {return [: -local $p set $v]} } ? {o a get} a1 ? {o b get} {::o: unable to dispatch method 'b'} ? {o c get} {::o: unable to dispatch method 'c'} ? {o foo a} a1 ? {o foo b} b1 ? {o foo c} {::o: unable to dispatch method 'c'} ? {o bar a} a1 ? {o bar b} b1 ? {o bar c} {c1} #? {lsort [o info vars]} "____C.c ____C.d a b" ? {lsort [o info vars]} "__private a b" ? {o eval {lsort [array names :__private]}} "::o,c ::o,d" ? {o bar d} {1} ? {o baz d 2} {2} ? {o bar d} {2} ? {o baz d x} {expected integer but got "x" for parameter "value"} } # # Test protected and private class object properties # nx::test case protected-priv-class-object-property { nx::Class create C { :object property -accessor public {a a1} :object property -accessor protected {b b1} :object property -accessor private {c c1} :object property -accessor private {d:integer 1} :public object method foo {p} {return [: $p get]} :public object method bar {p} {return [: -local $p get]} :public object method baz {p v} {return [: -local $p set $v]} } ? {C a get} a1 ? {C b get} {method 'b' unknown for ::C; consider '::C create b get' instead of '::C b get'} ? {C c get} {method 'c' unknown for ::C; consider '::C create c get' instead of '::C c get'} ? {C foo a} a1 ? {C foo b} b1 ? {C foo c} {method 'c' unknown for ::C; consider '::C create c get' instead of '::C c get'} ? {C bar a} a1 ? {C bar b} b1 ? {C bar c} {c1} #? {lsort [o info vars]} "____C.c ____C.d a b" ? {lsort [C info vars]} "__private a b" ? {C eval {lsort [array names :__private]}} "::C,c ::C,d" ? {C bar d} {1} ? {C baz d 2} {2} ? {C bar d} {2} ? {C baz d x} {expected integer but got "x" for parameter "value"} ? {C public object property {d:integer 1}} {'property' is not a method defining method} ? {C protected object property {d:integer 1}} {'property' is not a method defining method} ? {C private object property {d:integer 1}} {'property' is not a method defining method} } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/rac.test000066400000000000000000000311121242365656200136770ustar00rootroot00000000000000# -*- Tcl -*- # # Exploratory tests on run-time assertion checking (RAC) # # stefan.sobernig@wu.ac.at # # Conceptual baseline is the Eiffel Spec (ECMA Standard 367, 2nd ed., # 2006) # # see # http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-367.pdf # package require nx::test nx::Class create Sensor { :property -accessor public {value:integer 1} } set invar [list {[regexp {^[0-9]$} ${:value}] == 1}] ::nsf::method::assertion Sensor class-invar $invar ? {::nsf::method::assertion Sensor class-invar} $invar # # Minimal object interface to ::nsf::method::assertion # # # TODO: This should not be ::nx::VariableSlot below, but # ::nx::ObjectParameterSlot mixes alias + slotset. # ::nx::VariableSlot create ::nx::Object::slot::object-invariant { :public object method get {obj prop} { ::nsf::method::assertion $obj object-invar } :public object method assign {obj prop value} { ::nsf::method::assertion $obj object-invar $value } } ::nx::VariableSlot create ::nx::Class::slot::invariant { :public object method get {cls prop} { ::nsf::method::assertion $cls class-invar } :public object method assign {cls prop value} { ::nsf::method::assertion $cls class-invar $value } } ? {Sensor cget -invariant} $invar ? {::nsf::method::assertion Sensor class-invar} $invar ? {Sensor configure -invariant ""} "" ? {Sensor cget -invariant} "" ? {::nsf::method::assertion Sensor class-invar} "" ? {Sensor configure -invariant $invar} "" ? {Sensor cget -invariant} $invar ? {::nsf::method::assertion Sensor class-invar} $invar Sensor create s1 ? {s1 cget -object-invariant} "" ? {s1 configure -object-invariant $invar} "" ? {s1 cget -object-invariant} $invar ? {s1 configure -object-invariant ""} "" # # TODO: re-position -pre-condition, to appear before the method # body. This would ease reading. # # # TODO: Why is there a firm requirement to provide a post-condition, # when defining a pre-condition (they are non-positional # parameters in NX)? # # --> because of XOTcl2 legacy interface? # i.e.: precondition:optional postcondition:optional # Sensor public method incrValue {} { incr :value } -precondition { {[set ::ARGS [nsf::current args]] eq "run {bar 1 2 3}"} {# pre-condition:} {${:value} > 0} } -postcondition { {[puts stderr POST=${:value}] eq ""} {# post-condition:} {${:value} > 1} } proc bar args { s1 incrValue } # # TODO: How to activate, deactivate RAC per object? Re-introduce check() method? # # s1 check pre ::nsf::method::assertion s1 check pre ? {bar 1 2 3} "2" # # TODO: ::nsf::current jumps the call stack, picks an arbitrary call # frame if the context provides for it. # ? {info exists ::ARGS} 1 ? {set ::ARGS} "run {bar 1 2 3}" # # TODO: Improve formatting of assertion messages, to make distinction # clear between assertion and underlying error. # catch {s1 incrValue} ::msg ? {set ::msg} {error in Assertion: {[set ::ARGS [nsf::current args]] eq "run {bar 1 2 3}"} in proc 'incrValue' can't find proc} ? {s1 value -1} -1 ? {s1 value 10} 10 ? {s1 value 1} 1 # s1 check all ::nsf::method::assertion s1 check all # # TODO: When INVAR assertions fail (upon entering an operation), the # object state is still modified effectively. Why? # # INVAR ? {s1 value -1} {assertion failed check: {[regexp {^[0-9]$} ${:value}] == 1} in proc 'value'} # s1 check {} ::nsf::method::assertion s1 check {} ? {s1 value} -1 # s1 check all ::nsf::method::assertion s1 check all ? {s1 value 10} {assertion failed check: {[regexp {^[0-9]$} ${:value}] == 1} in proc 'value'} # s1 check {} ::nsf::method::assertion s1 check {} ? {s1 value} 10 # PRE # s1 check pre ::nsf::method::assertion s1 check pre Sensor public method incrValue2 {} { incr :value } -precondition { {# pre-condition:} {${:value} == -1} } -postcondition {} ? {s1 incrValue2} {assertion failed check: {${:value} == -1} in proc 'incrValue2'} # # OK: old value (value before PRE RAC) is preserved # ? {s1 value} 10 # # What is the order when evaluating PRE/POST and INVAR assertions? # # ACTUAL: . -> PRE -> INVAR -> (BODY) -> POST -> INVAR -> . # # TODO: EXPTECTED (ECMA-367 §8.23.26): # . -> INVAR -> PRE -> (BODY) -> INVAR -> POST -> . # set ::YYY [list] # s1 check all ::nsf::method::assertion s1 check all # # TODO: using [:*] calls within assertions distorts error # reporting (the * call frame is reported as error context -> proc # name = "lappend" in the examples below etc.) # Sensor configure -invariant { {[llength [lappend ::YYY "INVAR"]]} } Sensor public method incrValue2 {} { lappend ::YYY "BODY" ::nsf::method::assertion [self] check {} set r [incr :value] ::nsf::method::assertion [self] check all return $r } -precondition { {# pre-condition:} {[llength [lappend ::YYY "PRE"]]} } -postcondition { {# post-condition:} {[llength [lappend ::YYY "POST"]]} } ? {s1 incrValue2} 11 ? {set ::YYY} "PRE INVAR BODY POST INVAR" # # Are class invariants evaluated after instance creation? # see ECMA-367 §7.5 # set ::YYY [list] nx::Class create Account -invariant { {[llength [lappend ::YYY "Account"]]} {# sufficient_balance: } {${:balance} >= ${:minimumBalance}} } { :property -accessor public balance:integer :property -accessor public minimumBalance:integer :method init args { set :balance 9 set :minimumBalance 10 } } # # TODO: Irgh! a1 is effectively an invalid instance, class invars # should have been checked after create(); this is also inconvenient, # because once checking has been activated, the assertions are # reported as violated ... without directly blaming the creation # operation --> relevant for paper! # Account create a1 ? {a1 balance} 9 ? {a1 minimumBalance} 10 # a1 check instinvar ::nsf::method::assertion a1 check class-invar # # TODO: Should the default dispatch also trigger INVAR checks? # # - Against: ECMA semantics refer to qualified feature calls, with # the default method not being an externally visible feature, at # least in XOTcl. # # - In favor: In NX, however, it can be refined though not publicly # available (or?) Also, a convenient way to ask whether an object is # in a valid state ... # ? {a1} "::a1" ? {a1 balance} {assertion failed check: {${:balance} >= ${:minimumBalance}} in proc 'balance'} # # TODO: What about pre- and post-conditions for create() and/or # init(). This adds to the above ... # # - They should be evaluated upon creation (before an explicit "/inst/ # check pre|post" # # - How to specify them? a) There is no custom create() defined for # classes (only meta-classes) -> some pre|post notation for # configure? b) init() is not necessarily defined for a class ... # # - the assertion-checking semantics are different from ordinary method calls: # PRE (BODY) POST INVAR (no INVAR checking before PRE!) # # # How does the super/subclass relationship relate to ... # # - invariants? -(ECMA)-> include parent clauses: AND joining, # with parent clauses taking precedence # (in reverse linearization order; see # ECMA-367 §8.10.2) # set ::YYY [list] nx::Class create SavingsAccount -superclass Account -invariant { {[llength [lappend ::YYY [:info class]]]} {# minimum_deposit: } {${:minimumBalance} > 110} } SavingsAccount create sa1 ? {sa1 balance 99} 99 ? {sa1 minimumBalance 101} 101 # sa1 check instinvar ::nsf::method::assertion sa1 check class-invar # Should be: assertion labelled 'sufficient_balance' should be checked # before 'minimum_deposit' # # i.e.: # ? {sa1 balance} {assertion failed check: {[my balance] >= [my minimumBalance]} in proc 'balance'} # # TODO: inverse order of resolution of class INVARs # ? {sa1 balance} {assertion failed check: {${:minimumBalance} > 110} in proc 'balance'} ? {set ::YYY} "::SavingsAccount" # sa1 check {} # a1 check {} ::nsf::method::assertion a1 check {} ::nsf::method::assertion sa1 check {} # - pre-conditions? -(ECMA)-> require else (OR) ... weakening: OR # joining with parent clauses taking # precedence in reverse linearization # order (see ECMA-367 $8.10.5) # # (pre_1 or ... or pre_n) or else pre Account property -accessor public depositTransactions:integer Account public method deposit {sum:integer} { incr :depositTransactions incr :balance $sum } -precondition { {[llength [current class]]} {# trap :} {0} } -postcondition { {${:depositTransactions} > 1} } SavingsAccount public method deposit {sum:integer} { next } -precondition { {# max_deposits :} {${:depositTransactions} < 3} } -postcondition {} SavingsAccount create sa2 sa2 depositTransactions 2 # sa2 check pre ::nsf::method::assertion sa2 check pre # EXPECTED: trap OR max_deposits ? {sa2 deposit 50} {assertion failed check: {0} in proc 'deposit'}; # SHOULD ACTUALLY PASS because max_deposits is okay! ? {sa2 depositTransactions 3} 3 ? {sa2 deposit 60} {assertion failed check: {${:depositTransactions} < 3} in proc 'deposit'}; # FAILS because of max_deposits, but evaluation order should be inverse: ACCOUNT -> SAVINGSACCOUNT. # -- # TODO: Are method contracts enforced in shadowing methods, even without [next]? nx::Class create S { :public method foo {} { [:info class] eval [list lappend :TRACE "[current class]-[current proc]"] } -precondition { {[llength [[:info class] eval [list lappend :TRACE "::S-foo-PRE"]]]} } -postcondition { {[llength [[:info class] eval [list lappend :TRACE "::S-foo-POST"]]]} } } nx::Class create T -superclass S { :public method foo {} { [:info class] eval [list lappend :TRACE "[current class]-[current proc]"] # next; # invariants of super only fired when [next] is provided :/ } } T create t1; # -check all ::nsf::method::assertion t1 check all t1 foo ? {T eval {set :TRACE}} "::T-foo"; # SHOULD BE "::T-foo ::S-foo-PRE ::S-foo ::S-foo-POST", even without [next] # # - post-conditions? -(ECMA)-> ensure then (AND) ... strengthening: # in a convenience view, AND joining, # with parent clauses taking precedence # in reverse linearization order (see # ECMA-367 $8.10.5). More precisely, # parent posts only required iff # pre-conditions over the pre-state # (old) hold. # # (old pre_1 implies post_1) # and ... and # (old pre_n implies post_n) # and then post # T eval {unset :TRACE} nx::Class create T -superclass S { :public method foo {} { [:info class] eval [list lappend :TRACE "[current class]-[current proc]"] next } -precondition {} -postcondition { {[llength [[:info class] eval {lappend :TRACE "::T-foo-POST"}]]} } } T create t1; # -check post ::nsf::method::assertion t1 check post t1 foo ? {T eval {set :TRACE}} "::T-foo ::S-foo ::S-foo-POST ::T-foo-POST"; # SHOULD BE: ::T-foo ::S-foo ::S-foo-POST ::T-foo-POST ... seems OK, but only because next induces a correct order. T eval {unset :TRACE} # without [next] nx::Class create T -superclass S { :public method foo {} { [:info class] eval [list lappend :TRACE "[current class]-[current proc]"] # next } -precondition {} -postcondition { {[llength [[:info class] eval {lappend :TRACE "::T-foo-POST"}]]} } } T create t2 ::nsf::method::assertion t2 check post t2 foo ? {T eval {set :TRACE}} "::T-foo ::T-foo-POST"; # SHOULD BE: ::T-foo ::S-foo-POST ::T-foo-POST, even without [next] # # TODO: Provide access to method arguments in assertion expressions. # # TODO: Parameter checks should be performed before PRE (and before # INVAR) checks (see ECMA-367, §...) # # nx::Class create S { # :public method bar {p:integer} { # } -precondition { # {$p > -1} # } # } # # OK: Are assertions fired through the colon resolver (:/var/) and # upon cget/configure? # nx::Class create Z -invariant { {[::nsf::is integer ${:v}]} } { :property -accessor public {v 1} :create ::z1 } ? {z1 v 1} 1 ::nsf::method::assertion z1 check class-invar # # TODO: Why "in proc 'v'"? ... this is misleading, at least for an INVAR. # ? {z1 v "XXX"} {assertion failed check: {[::nsf::is integer ${:v}]} in proc 'v'} ? {z1 eval {set :v "XXX"}} {assertion failed check: {[::nsf::is integer ${:v}]} in proc 'eval'} ? {z1 configure -v "XXX"} {assertion failed check: {[::nsf::is integer ${:v}]} in proc 'configure'} # # TODO: v should still be 1, but is already 'XXX' (see above) # ::nsf::method::assertion z1 check {} ? {z1 v} XXX; # Why not '1'? ::nsf::method::assertion z1 check class-invar # ::nsf::method::assertion z1 check {} # ? {z1 v XXX} XXX # ::nsf::method::assertion z1 check class-invar ? {z1 cget -v} {assertion failed check: {[::nsf::is integer ${:v}]} in proc 'cget'} ::nsf::method::assertion z1 check {} # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/returns.test000066400000000000000000000256711242365656200146510ustar00rootroot00000000000000# -*- Tcl -*- package require nx ::nx::configure defaultMethodCallProtection false package require nx::test # # The same tests are in this test suite, once with and once without # checking # # Make sure, checking is turned on # ::nsf::configure checkresult true nx::test configure -count 10000 nx::test case int-returns { nx::Class create C { # scripted method without paramdefs :method bar-ok1 {a b} {return 1} :method bar-ok2 {a b} {return $a} # scripted method with paramdefs :method bar-nok {a b:integer} {return a} # alias to tcl-cmd (no param defs) :alias incr -frame object ::incr :alias lappend -frame object ::lappend :create c1 } ::nsf::method::property C bar-ok1 returns integer ::nsf::method::property C bar-ok2 returns integer ::nsf::method::property C bar-nok returns integer ::nsf::method::property C incr returns integer ::nsf::method::property C lappend returns integer ? {c1 bar-ok1 1 2} 1 ? {c1 bar-ok2 1 2} 1 ? {c1 bar-nok 1 2} {expected integer but got "a" as return value} ? {c1 incr x} 1 ? {c1 incr x} 10002 ? {c1 lappend l e1} {expected integer but got "e1" as return value} # query the returns value ? {::nsf::method::property C lappend returns} integer # reset it to emtpy ? {::nsf::method::property C lappend returns ""} "" ? {::nsf::method::property C bar-ok1 returns ""} "" ? {::nsf::method::property C bar-ok2 returns ""} "" ? {::nsf::method::property C bar-nok returns ""} "" # no checking ? {c1 bar-ok1 1 2} 1 ? {c1 bar-ok2 1 2} 1 ? {c1 bar-nok 1 2} a ? {c1 lappend l e2} "e1 e2" # query returns "", if there is no returns checking ? {::nsf::method::property C lappend returns} "" ? {::nsf::method::property ::nx::Class method returns} "" } nx::test configure -count 10 nx::test case app-specific-returns { ::nx::methodParameterSlot object method type=range {name value arg} { lassign [split $arg -] min max if {$value < $min || $value > $max} { error "Value '$value' of parameter $name not between $min and $max" } return $value } nx::Class create C { :method bar-ok1 {a b} {return 1} :method bar-ok2 {a b} {return $a} :method bar-nok {a b:integer} {return a} :alias incr -frame object ::incr :alias lappend -frame object ::lappend :create c1 } ::nsf::method::property C bar-ok1 returns range,arg=1-3 ::nsf::method::property C bar-ok2 returns range,arg=1-3 ::nsf::method::property C bar-nok returns range,arg=1-3 ::nsf::method::property C incr returns range,arg=1-30 ::nsf::method::property C lappend returns range,arg=1-30 ? {c1 bar-ok1 1 2} 1 ? {c1 bar-ok2 1 2} 1 ? {c1 bar-nok 1 2} {Value 'a' of parameter return-value not between 1 and 3} ? {c1 incr x} 1 ? {c1 incr x} 12 ? {c1 lappend l e1} {Value 'e1' of parameter return-value not between 1 and 30} } nx::test configure -count 1000 nx::test case converting-returns { ::nx::methodParameterSlot object method type=sex {name value args} { #puts stderr "[current] slot specific converter" switch -glob $value { m* {return m} f* {return f} default {error "expected sex but got $value"} } } nx::Class create C { :method bar-ok1 {a b} {return male} :method bar-ok2 {a b} {return $a} :method bar-nok {a b:integer} {return $b} :alias set -frame object ::set :create c1 } ::nsf::method::property C bar-ok1 returns sex ::nsf::method::property C bar-ok2 returns sex ::nsf::method::property C bar-nok returns sex ::nsf::method::property C set returns sex ? {c1 bar-ok1 1 2} male ? {c1 bar-ok2 female 2} female ? {c1 bar-nok 1 6} {expected sex but got 6} ? {c1 set x male} male ? {c1 eval {set :x}} male ? {c1 set x} male ? {c1 set x hugo} {expected sex but got hugo} ::nsf::method::property C bar-ok1 returns sex,convert ::nsf::method::property C bar-ok2 returns sex,convert ::nsf::method::property C bar-nok returns sex,convert ::nsf::method::property C set returns sex,convert ? {c1 bar-ok1 1 2} m ? {c1 bar-ok2 female 2} f ? {c1 bar-nok 1 6} {expected sex but got 6} ? {c1 set x male} m ? {c1 eval {set :x}} male ? {c1 set x} m ? {c1 set x hugo} {expected sex but got hugo} } # # turn off result checking # ::nsf::configure checkresults false ::nx::test configure -count 1000 ::nx::test case int-returns-nocheck { nx::Class create C { # scripted method without paramdefs :method bar-ok1 {a b} {return 1} :method bar-ok2 {a b} {return $a} # scripted method with paramdefs :method bar-nok {a b:integer} {return a} # alias to tcl-cmd (no param defs) :alias incr -frame object ::incr :alias lappend -frame object ::lappend :create c1 } ::nsf::method::property C bar-ok1 returns integer ::nsf::method::property C bar-ok2 returns integer ::nsf::method::property C bar-nok returns integer ::nsf::method::property C incr returns integer ::nsf::method::property C lappend returns integer ? {c1 bar-ok1 1 2} 1 ? {c1 bar-ok2 1 2} 1 ? {c1 bar-nok 1 2} a ? {c1 incr x} 1 ? {c1 incr x} 1002 ? {c1 lappend l e1} e1 # query the returns value ? {::nsf::method::property C lappend returns} integer # reset it to emtpy ? {::nsf::method::property C lappend returns ""} "" c1 eval {set :l e1} # no checking on lappend ? {c1 lappend l e2} "e1 e2" # query returns "", if there is no returns checking ? {::nsf::method::property C lappend returns} "" ? {::nsf::method::property ::nx::Class method returns} "" } ::nx::test configure -count 10 ::nx::test case app-specific-returns-nocheck { ::nx::methodParameterSlot object method type=range {name value arg} { lassign [split $arg -] min max if {$value < $min || $value > $max} { error "Value '$value' of parameter $name not between $min and $max" } return $value } nx::Class create C { :method bar-ok1 {a b} {return 1} :method bar-ok2 {a b} {return $a} :method bar-nok {a b:integer} {return a} :alias incr -frame object ::incr :alias lappend -frame object ::lappend :create c1 } ::nsf::method::property C bar-ok1 returns range,arg=1-3 ::nsf::method::property C bar-ok2 returns range,arg=1-3 ::nsf::method::property C bar-nok returns range,arg=1-3 ::nsf::method::property C incr returns range,arg=1-30 ::nsf::method::property C lappend returns range,arg=1-30 ? {c1 bar-ok1 1 2} 1 ? {c1 bar-ok2 1 2} 1 ? {c1 bar-nok 1 2} a ? {c1 incr x} 1 ? {c1 incr x} 12 ? {c1 lappend l e1} e1 } ::nx::test configure -count 1000 ::nx::test case converting-returns-nocheck { ::nx::methodParameterSlot object method type=sex {name value args} { #puts stderr "[current] slot specific converter" switch -glob $value { m* {return m} f* {return f} default {error "expected sex but got $value"} } } nx::Class create C { :method bar-ok1 {a b} {return male} :method bar-ok2 {a b} {return $a} :method bar-nok {a b:integer} {return $b} :alias set -frame object ::set :create c1 } # # turn off checker # ::nsf::method::property C bar-ok1 returns sex ::nsf::method::property C bar-ok2 returns sex ::nsf::method::property C bar-nok returns sex ::nsf::method::property C set returns sex ? {c1 bar-ok1 1 2} male ? {c1 bar-ok2 female 2} female ? {c1 bar-nok 1 6} 6 ? {c1 set x male} male ? {c1 eval {set :x}} male ? {c1 set x} male ? {c1 set x hugo} hugo # # don't turn off converter # ::nsf::method::property C bar-ok1 returns sex,convert ::nsf::method::property C bar-ok2 returns sex,convert ::nsf::method::property C bar-nok returns sex,convert ::nsf::method::property C set returns sex,convert ? {c1 bar-ok1 1 2} m ? {c1 bar-ok2 female 2} f ? {c1 bar-nok 1 6} {expected sex but got 6} ? {c1 set x male} m ? {c1 eval {set :x}} male ? {c1 set x} m ? {c1 set x hugo} {expected sex but got hugo} } ::nsf::configure checkresults true ::nx::test case int-returns-sugar { nx::Class create C { # scripted method without paramdefs :method bar-ok1 {a b} -returns integer {return 1} :method bar-ok2 {a b} -returns integer {return $a} # scripted method with paramdefs :method bar-nok {a b:integer} -returns integer {return a} # alias to tcl-cmd (no param defs) :alias incr -returns integer -frame object ::incr :alias lappend -returns integer -frame object ::lappend :forward ++ -returns integer ::expr 1 + :forward | -returns integer ::append _ :public object method instances {} -returns object,1..n {:info instances} :create c1 } package req nx::serializer set s [C serialize] puts $s ? [list set _ [regsub -all returns $s returns s]] 8 "occurances of returns" ? {c1 bar-ok1 1 2} 1 ? {c1 bar-ok2 1 2} 1 ? {c1 ++ 1000} 1001 ? {c1 | a} {expected integer but got "a" as return value} ? {::nsf::method::property ::C ::nsf::classes::C::bar-nok returns} integer ? {c1 bar-nok 1 2} {expected integer but got "a" as return value} ? {C instances} ::c1 ? {c1 incr x} 1 ? {c1 incr x} 1002 ? {c1 lappend l e1} {expected integer but got "e1" as return value} # query the returns value ? {::nsf::method::property C lappend returns} integer # reset it to emtpy ? {::nsf::method::property C lappend returns ""} "" ? {::nsf::method::property C bar-ok1 returns ""} "" ? {::nsf::method::property C bar-ok2 returns ""} "" ? {::nsf::method::property C bar-nok returns ""} "" ? {::nsf::method::property C ++ returns ""} "" ? {::nsf::method::property C | returns ""} "" # no checking ? {c1 bar-ok1 1 2} 1 ? {c1 bar-ok2 1 2} 1 ? {c1 bar-nok 1 2} a ? {c1 lappend l e2} "e1 e2" ? {c1 ++ 1000} 1001 ? {c1 | a} "a" # query returns "", if there is no returns checking ? {::nsf::method::property C lappend returns} "" ? {::nsf::method::property ::nx::Class method returns} "" } ::nx::test case empty-paramdefs-robustedness { ::nx::Object create ku { # 1: Create an empty or checker-free parameter spec :object method foo {} {;} ? [:info object method parameters foo] "" # 2: A call to ::nsf::method::property which might require NsfParamDefs ? [list ::nsf::method::property [::nx::current] foo returns] "" # 3: Check, if "info method parameter" still works ? [:info object method parameters foo] "" ? [list ::nsf::method::property [::nx::current] foo returns] "" # 4: Set methodproperty to some value and check again ::nsf::method::property [::nx::current] foo returns int ? [list ::nsf::method::property [::nx::current] foo returns] "int" ? [:info object method parameters foo] "" # 5: Reset methodproperty and query again ::nsf::method::property [::nx::current] foo returns "" ? [list ::nsf::method::property [::nx::current] foo returns] "" ? [:info object method parameters foo] "" } } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/serialize.test000066400000000000000000000137731242365656200151360ustar00rootroot00000000000000# -*- Tcl -*- package req nx::test package req nx::serializer nx::test case deepSerialize-map-filter { Object create ::a { :object property -accessor public ref:object,type=[:info class] Object create [self]::b { [:info parent] ref set [Object create [self]::c] } } ? {::nsf::object::exists ::a} 1 ? {::nsf::object::exists ::a::b} 1 ? {::nsf::object::exists ::a::b::c} 1 ? {::a ref get} [[::a::b] info children] set script [::Serializer deepSerialize -map {::a::b ::x::y ::a ::x} ::a] # fix collateral damage (TODO: fixme, preprecate me, ...) set script [string map {::nsf::object::xlloc ::nsf::object::alloc} $script] ::a destroy ? {::nsf::object::exists ::a} 0 ? {::nsf::object::exists ::a::b} 0 ? {::nsf::object::exists ::a::b::c} 0 eval $script ? {::nsf::object::exists ::a} 0 ? {::nsf::object::exists ::a::b} 0 ? {::nsf::object::exists ::a::b::c} 0 ? {::nsf::object::exists ::x} 1 ? {::nsf::object::exists ::x::y} 1 ? {::nsf::object::exists ::x::y::c} 1 ? {::x ref get} [::x::y info children] Object create ::a ::x::y::c eval { :object variable parentRef [[:info parent] info parent] } set script [::a eval { ::Serializer deepSerialize -map [list ::x::y [self] ::x [self]] ::x::y::c }] ? {::x::y::c eval {set :parentRef}} ::x ? {::nsf::object::exists ::a::c} 0 eval $script ? {::nsf::object::exists ::a::c} 1 ? {::a::c eval {set :parentRef}} ::a } nx::test case deepSerialize-ignoreVarsRE-filter { nx::Class create C { :object property -accessor public x :object property -accessor public y :property -accessor public a:int :property -accessor public b:int :create c1 } ? {C x set 1} 1 ? {C x get} 1 ? {C y set 1} 1 ? {C y get} 1 ? {lsort [C info methods]} "a b" ? {lsort [C info object methods]} "x y" ? {c1 a set b} {expected integer but got "b" for parameter "value"} ? {c1 a set 1} 1 ? {c1 b set 1} 1 set c1(IgnoreNone1) [list [::Serializer deepSerialize c1] "a b"] set c1(IgnoreNone2) [list [::Serializer deepSerialize -ignoreVarsRE "" c1] "a b"] set c1(One) [list [::Serializer deepSerialize -ignoreVarsRE "a" c1] "b"] set c1(One2) [list [::Serializer deepSerialize -ignoreVarsRE {::a$} c1] "b"] set c1(IgnoreAll) [list [::Serializer deepSerialize -ignoreVarsRE "." c1] ""] set names {}; foreach s [C info slots] {lappend names [$s cget -name]} set c1(None2) [list [::Serializer deepSerialize -ignoreVarsRE [join $names |] c1] ""] c1 destroy foreach t [array names c1] { ? {nsf::object::exists c1} 0 lassign $c1($t) script res eval $script ? {nsf::object::exists c1} 1 ? {lsort [c1 info vars]} $res "Object c1 $t" c1 destroy } set C(IgnoreNone1) [list [::Serializer deepSerialize C] "x y"] set C(IgnoreNone2) [list [::Serializer deepSerialize -ignoreVarsRE "" C] "x y"] #set C(One) [list [::Serializer deepSerialize -ignoreVarsRE "x" C] "y"] set C(One2) [list [::Serializer deepSerialize -ignoreVarsRE {::x$} C] "y"] #set C(IgnoreAll) [list [::Serializer deepSerialize -ignoreVarsRE "." C] ""] set names {}; foreach s [C info object slots] {lappend names [$s cget -name]} #set C(None2) [list [::Serializer deepSerialize -ignoreVarsRE [join $names |] C] ""] C destroy foreach t [array names C] { ? {nsf::object::exists C} 0 lassign $C($t) script res #puts stderr "=====C($t)\n$script\n====" eval $script ? {nsf::object::exists C} 1 ? {lsort [C info vars]} $res "Class C $t" C destroy } } nx::test case deepSerialize-ignore-filter { Object create ::a { Object create [self]::b Object create [self]::c } ? {::nsf::object::exists ::a} 1 ? {::nsf::object::exists ::a::b} 1 ? {::nsf::object::exists ::a::c} 1 set script [::Serializer deepSerialize -ignore ::a::b ::a] ::a destroy ? {::nsf::object::exists ::a::c} 0 ? {::nsf::object::exists ::a::b} 0 ? {::nsf::object::exists ::a} 0 eval $script ? {::nsf::object::exists ::a} 1 ? {::nsf::object::exists ::a::b} 0 ? {::nsf::object::exists ::a::c} 1 set script [::Serializer deepSerialize -ignore ::a ::a] ::a destroy ? {::nsf::object::exists ::a} 0 eval $script ? {::nsf::object::exists ::a} 0 } nx::test case serialize-slotContainer { nx::Class create C { :object property x :property a } ? {::nsf::object::exists ::C::slot} 1 ? {::nsf::object::exists ::C::per-object-slot} 1 ? {::nx::isSlotContainer ::C::slot} 1 ? {::nx::isSlotContainer ::C::per-object-slot} 1 ? {::nsf::object::exists ::C::slot::a} 1 ? {::nsf::object::exists ::C::per-object-slot::x} 1 ? {::nsf::object::property ::C hasperobjectslots} 1 set script [C serialize] C destroy ? {::nsf::object::exists ::C} 0 eval $script ? {::nsf::object::exists ::C::slot} 1 ? {::nsf::object::exists ::C::per-object-slot} 1 ? {::nx::isSlotContainer ::C::slot} 1 ? {::nx::isSlotContainer ::C::per-object-slot} 1 ? {::nsf::object::exists ::C::slot::a} 1 ? {::nsf::object::exists ::C::per-object-slot::x} 1 ? {::nsf::object::property ::C hasperobjectslots} 1 } # # check whether ::nsf::object::properties keepcallerself and # perobjectdispatch for nx::Objects are handled correctly via serialize # nx::test case serialize-object-properties { # # Check on object o # nx::Object create o ::nsf::object::property ::o keepcallerself 1 ::nsf::object::property ::o perobjectdispatch 1 set script [o serialize] o destroy ? {::nsf::object::exists ::o} 0 eval $script ? {::nsf::object::property ::o keepcallerself} 1 ? {::nsf::object::property ::o perobjectdispatch} 1 # # Now the same for a class # nx::Class create C ::nsf::object::property ::C keepcallerself 1 ::nsf::object::property ::C perobjectdispatch 1 set script [C serialize] C destroy ? {::nsf::object::exists ::C} 0 eval $script ? {::nsf::object::property ::C keepcallerself} 1 ? {::nsf::object::property ::C perobjectdispatch} 1 } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/submethods.test000066400000000000000000000674101242365656200153210ustar00rootroot00000000000000# -*- Tcl -*- package require nx::test ::nx::configure defaultMethodCallProtection false # # test, whether error message from a submethod contains method path # nx::test case info-errors { ? {::nx::Object info subclasses a b c} \ {invalid argument 'b', maybe too many arguments; should be "::nx::Object info subclasses ?-closure? ?-dependent? ?/pattern/?"} ? {::nx::Object info object mixins a b c} \ {invalid argument 'b', maybe too many arguments; should be "::nx::Object info object mixins ?-guards? ?/pattern/?"} } nx::test configure -count 10 nx::test case submethods { #Object method unknown {} {} Object create o1 ? {o1 foo} "::o1: unable to dispatch method 'foo'" # # test subcmd "tricky" names # - names called on ensemble objects from C (defaultmethod, unknown) # - names equal to helper methods of the ensemble object # Object create o { :object method "string length" x {return [current method]} :object method "string tolower" x {return [current method]} :object method "string info" x {return [current method]} :object method "foo a x" {} {return [current method]} :object method "foo a y" {} {return [current method]} :object method "foo a subcmdName" {} {return [current method]} :object method "foo a defaultmethod" {} {return [current method]} :object method "foo a unknown" args {return [current method]} :object method "foo b" {} {return [current method]} } Class create Foo { :method "bar m1" {a:integer -flag} {;} :method "bar m2" {x:integer -y:boolean} {;} :method "baz a m1" {x:integer -y:boolean} {return m1} :method "baz a m2" {x:integer -y:boolean} {;} :method "baz b" {} {;} } ? {o string length 1} length ? {o string tolower 2} tolower ? {o string toupper 2} \ {unable to dispatch sub-method "toupper" of ::o string; valid are: string info, string length, string tolower} ? {o string} "valid submethods of ::o string: info length tolower" ? {o foo a x} "x" ? {o foo a y} "y" ? {o foo a z} \ {unable to dispatch sub-method "z" of ::o foo a; valid are: foo a defaultmethod, foo a subcmdName, foo a unknown, foo a x, foo a y} ? {o info object method type string} object # the following is a problem, when string has subcmd "info" #? {o::string info class} ::nx::EnsembleObject ? {o string length aaa} "length" ? {o string info class} "info" ? {o string hugo} \ {unable to dispatch sub-method "hugo" of ::o string; valid are: string info, string length, string tolower} Foo create f1 ? {f1 baz a m1 10} m1 ? {f1 baz a m3 10} \ {unable to dispatch sub-method "m3" of ::f1 baz a; valid are: baz a m1, baz a m2} #unable to dispatch method baz a m3; valid subcommands of a: m1 m2} # nx::test configure -count 1 nx::test case defaultmethod { Object create o { :object method "string length" x {return [current method]} :object method "string tolower" x {return [current method]} :object method "string info" x {return [current method]} :object method "foo a x" {} {return [current method]} :object method "foo a y" {} {return [current method]} :object method "foo a subcmdName" {} {return [current method]} :object method "foo a defaultmethod" {} {return [current method]} :object method "foo a unknown" args {return [current method]} :object method "foo b" {} {return [current method]} } Class create Foo { :method "bar m1" {a:integer -flag} {;} :method "bar m2" {x:integer -y:boolean} {;} :method "baz a m1" {x:integer -y:boolean} {return m1} :method "baz a m2" {x:integer -y:boolean} {;} :method "baz b" {} {;} :create f1 } ? {o string} "valid submethods of ::o string: info length tolower" ? {o foo} "valid submethods of ::o foo: a b" ? {f1 bar} "valid submethods of ::f1 bar: m1 m2" ? {f1 baz} "valid submethods of ::f1 baz: a b" ? {f1 baz a} "valid submethods of ::f1 baz a: m1 m2" } # # testing ensemble objects with next # nx::test configure -count 1 nx::test case ensemble-next { nx::Class create FOO { # reduced ensemble :method foo args {lappend :v "FOO.foo//[nx::current method] ([nx::current args])"} # expanded ensemble :method "l1 l2 l3a" {x} { lappend :v "FOO.l1 l2 l3a//[nx::current method] ([nx::current args])" } :method "l1 l2 l3b" {x} { lappend :v "FOO.l1 l2 l3b//[nx::current method] ([nx::current args])" } # uplevel :method "bar x" {varname} {upvar $varname v; return [info exists v]} :method "baz" {} { set hugo 1 return [:bar x hugo] } } nx::Class create M0 { :method "foo b x" {x} { lappend :v "M0.foo b x//[nx::current method] ([nx::current args])" nx::next } :method "foo b y" {x} { lappend :v "M0.foo b y//[nx::current method] ([nx::current args])" nx::next } :method "foo a" {x} { lappend :v "M0.foo a//[nx::current method] ([nx::current args])" nx::next } :method "l1 l2" {args} { lappend :v "l1 l2//[nx::current method] ([nx::current args])" nx::next } } nx::Class create M1 { :method "foo a" {x} { set :v [list "M1.foo a //[nx::current method] ([nx::current args])"] nx::next } :method "foo b x" {x} { set :v [list "M1.foo b x //[nx::current method] ([nx::current args])"] nx::next } :method "foo b y" {x} { set :v [list "M1.foo b y //[nx::current method] ([nx::current args])"] nx::next } :method "l1 l2 l3a" {x} { set :v [list "M1.l1 l2 l3a//[nx::current method] ([nx::current args])"] nx::next } :method "l1 l2 l3b" {x} { set :v [list "M1.l1 l2 l3b//[nx::current method] ([nx::current args])"] nx::next } } FOO mixins set {M1 M0} FOO create f1 # # The last list element shows handling of less deep ensembles # (longer arg list is passed) # ? {f1 foo a 1} "{M1.foo a //a (1)} {M0.foo a//a (1)} {FOO.foo//foo (a 1)}" ? {f1 foo b x 1} "{M1.foo b x //x (1)} {M0.foo b x//x (1)} {FOO.foo//foo (b x 1)}" ? {f1 foo b y 1} "{M1.foo b y //y (1)} {M0.foo b y//y (1)} {FOO.foo//foo (b y 1)}" # # The middle list element shows shrinking (less deep ensembles), the # last element shows expansion via mixin (deeper ensemble is reached # via next) # ? {f1 l1 l2 l3a 100} "{M1.l1 l2 l3a//l3a (100)} {l1 l2//l2 (l3a 100)} {FOO.l1 l2 l3a//l3a (100)}" } nx::test case ensemble-partial-next { nx::Class create M { :public method "info has namespace" {} { nx::next return sometimes } :public method "info has something else" {} { return something } :public method "info has something path" {} { return [::nsf::current methodpath] } :public method "info has something better" {} { nx::next return better } :public method foo {} { return [::nsf::current methodpath] } } nx::Object mixins add M nx::Object create o1 # call a submethod defined by a mixin, which does a next ? {o1 info has namespace} sometimes # call a submethod, which is not defined by the mixin ? {o1 info has type Object} 1 ? {o1 info has type M} 0 # call a submethod, which is nowhere defined ? {o1 info has typo M} \ {unable to dispatch sub-method "typo" of ::o1 info has; valid are: info has mixin, info has namespace, info has something better, info has something else, info has something path, info has type} # call a submethod, which is only defined in the mixin ? {o1 info has something else} something # call a submethod, which is only defined in the mixin, and which # does a next (which should not complain) ? {o1 info has something better} better # ensemble path excluding "wrong" is mixed in ? {o1 info has something wrong} \ {unable to dispatch sub-method "wrong" of ::o1 info has something; valid are: info has something better, info has something else, info has something path} # call defaultcmds on ensembles ? {lsort [o1 info has something]} "valid submethods of ::o1 info has something: better else path" # defaultcmd has to return also subcmds of other shadowed ensembles ? {lsort [o1 info has]} "valid submethods of ::o1 info has: mixin namespace something type" ? {lsort [o1 info]} \ "valid submethods of ::o1 info: children class has info lookup name object parent precedence variable vars" # returning methodpath in ensemble ? {o1 info has something path} "info has something path" # returning methodpath outside ensemble ? {o1 foo} "foo" } # # Check behavior of upvars in ensemble methods # nx::test case ensemble-upvar { nx::Class create FOO { :method "bar0 x" {varname} {upvar $varname v; return [info exists v]} :method "baz0" {} { set hugo 1 return [:bar0 x hugo] } :method "bar1 x" {varname} {:upvar $varname v; return [info exists v]} :method "baz1" {} { set hugo 1 return [:bar1 x hugo] } :create f1 } ? {f1 baz0} 0 ? {f1 baz1} 1 } # # Check behavior of next with arguments within an ensemble # nx::test case ensemble-next-with-args { nx::Object create o { :object method foo {x} {return $x} :object method "e1 sm" {x} {return $x} :object method "e2 sm1 sm2" {x} {return $x} :object method "e2 e2 e2" {x} {return $x} :object method "e1 e1 e1" args {return $args} } nx::Class create M { :method foo {} {next 1} :method "e1 sm" {} {next 2} :method "e2 sm1 sm2" {} {next 3} :method "e2 e2 e2" {} {next 4} :method "e1 e1 e1" args {next {e1 e1 e1}} } o object mixins add M # case without ensemble ? {o foo} 1 # ensemble depth 1, 1 arg ? {o e1 sm} 2 # ensemble depth 2, 1 arg ? {o e2 sm1 sm2} 3 # ensemble depth 2, 1 arg, same tcl-objs ? {o e2 e2 e2} 4 # ensemble depth 2, multiple args, same tcl-objs ? {o e1 e1 e1} {e1 e1 e1} } nx::test configure -count 1 nx::test case ensemble-next-with-colon-prefix namespace eval ::ns1 { nx::Object create obj { :public object method foo {} { return [:info class] } :public object method ifoo {} { [current] ::nsf::methods::object::info::lookupmethod info} } ? {obj info class} ::nx::Object ? {obj info lookup method info} ::nsf::classes::nx::Object::info ? {obj ifoo} ::nsf::classes::nx::Object::info ? {obj foo} ::nx::Object set infolookup ::nsf::methods::object::info::lookupmethod set infomethod ::nsf::methods::object::info::method ? [list obj $infolookup info] ::nsf::classes::nx::Object::info ? [list obj $infomethod type ::nsf::classes::nx::Object::info] alias obj object method info {} {;} ? [list obj $infolookup info] ::ns1::obj::info ? [list obj $infomethod type ::ns1::obj::info] scripted ? {obj ifoo} ::ns1::obj::info ? {obj foo} {wrong # args: should be ":info"} # Now we try to overwrite the object specific method with an object # named "info" ? {nx::Object create obj::info} "refuse to overwrite cmd ::ns1::obj::info; delete/rename it before overwriting" rename obj::info "" ? {nx::Object create obj::info} ::ns1::obj::info ? [list obj $infolookup info] ::ns1::obj::info ? [list obj $infomethod type ::ns1::obj::info] object ? {obj ifoo} ::ns1::obj::info # To some surprise, we can can still call info class! # This works, since we do here an "ensemble-next" #? {obj info class} ::nx::Object ? {obj info class} {::ns1::obj::info: unable to dispatch method 'class'} # The ensemble-next has in case of foo the leading colon on the # callstack (e.g. ":info"). Make sure that we can still call the # method via ensemle-next. #? {obj foo} ::nx::Object ? {obj foo} {::ns1::obj::info: unable to dispatch method 'class'} } # # Leaf next: Do not trigger unknown handling (see also # NextSearchAndInvoke()) # nx::test case leaf-next-in-submethods { nx::Object create container { set :x 0 :public object method "FOO bar" {} { incr :x; next; # a "leaf next" } :public object method intercept args { incr :x; next; # a "filter next" } :object filters set intercept :FOO bar # Rationale: A call count > 2 would indicate that the leaf next # triggers a further call into filter ... ? [list set _ ${:x}] 2 } } nx::test case object-unknown { # # object level ensemble # nx::Object create o1 { :object method "i o a" {} {return a} :object method "i o b" {} {return b} } ? {o1 i o x} {unable to dispatch sub-method "x" of ::o1 i o; valid are: i o a, i o b} # # object level ensemble on class # nx::Class create C { :public object alias "i o a" ::nsf::methods::class::info::heritage :public object alias "i o b" ::nsf::methods::class::info::heritage } ? {C i o x} {unable to dispatch sub-method "x" of ::C i o; valid are: i o a, i o b} ? {C i o x y} {unable to dispatch sub-method "x" of ::C i o; valid are: i o a, i o b} # # Emulate the "info" and "info object" ensembles on nx::Object and nx::Class # nx::Object public method "i o a" {} {return a} nx::Object public alias "i o b" ::nsf::methods::class::info::heritage nx::Class public alias "i a" ::nsf::methods::class::info::heritage nx::Class public alias "i b" ::nsf::methods::class::info::heritage nx::Class create D {} ? {D i p} {unable to dispatch sub-method "p" of ::D i; valid are: i a, i b, i o a, i o b} ? {D i o a} a ? {D i o x} {unable to dispatch sub-method "x" of ::D i o; valid are: i o a, i o b} ? {D i o x y} {unable to dispatch sub-method "x" of ::D i o; valid are: i o a, i o b} } nx::test case unknown-in-info { nx::Class create C catch {C info x y z} err ? [list string match {unable to dispatch sub-method "x" of ::C info; valid are:*} $err] 1 #C info object x y z catch {C info object x y z} err ? [list string match {unable to dispatch sub-method "x" of ::C info object; valid are:*} $err] 1; ## --> unable to dispatch sub-method "object" of ::C info; valid are: } nx::test case submethods-and-filters { # # submethods as filters? # #set h [C public method "BAR bar" args { # next #}] #C filter {{BAR bar}} } nx::test case submethods-current-introspection { # # [current] & [current class] # nx::Object create o o public object method "FOO foo" {} { return "-[current]-[current class]-" } ? {o FOO foo} -::o-- Class create C C public method "FOO foo" {} { return "-[current]-[current class]-" } C create c ? {c FOO foo} -::c-::C- C mixins set [Class create M1 { :public method "FOO foo" {} { return "-[current]-[current class][next]" } }] ? {c FOO foo} -::c-::M1-::c-::C- o object mixins set ::M1 ? {o FOO foo} -::o-::M1-::o-- o object mixins set {} C mixins set {} # # limit [current methodpath] to collect only ensemble methods? # o eval { :public object method faz {} {return [current methodpath]} ? [list set _ [:faz]] "faz" } # # [current callingmethod] & [current callingclass] # o eval { set body {expr {[:bar] eq "[current class]-[current]-[current methodpath]"}} :public object method "FOO foo" {} $body :public object method "BAR BUU boo" {} $body :public object method baz {} $body set calleeBody {return "[current callingclass]-[current callingobject]-[current callingmethod]"} :public object method bar {} $calleeBody } ? {o FOO foo} 1 "instance method ensemble 1" ? {o BAR BUU boo} 1 "instance method ensemble 2" ? {o baz} 1 "instance method" o eval { set calleeBody {return "[current callingclass]-[current callingobject]-[current callingmethod]"} :object method "a b" {} $calleeBody set body {expr {[:a b] eq "[current class]-[current]-[current methodpath]"}} :public object method "FOO foo" {} $body :public object method "BAR BUU boo" {} $body :public object method baz {} $body } ? {o FOO foo} 1 "object method ensemble 1" ? {o BAR BUU boo} 1 "object method ensemble 2" ? {o baz} 1 "object method" o eval { # TODO: :method "a b c" {} $calleeBody; FAILS -> "can't append to scripted" set calleeBody {return "[current callingclass]-[current callingobject]-[current callingmethod]"} :object method "x y z" {} $calleeBody; set body {expr {[:x y z] eq "[current class]-[current]-[current methodpath]"}} :public object method "FOO foo" {} $body :public object method "BAR BUU boo" {} $body :public object method baz {} $body } ? {o FOO foo} 1 "class level object method ensemble 1" ? {o BAR BUU boo} 1 "class level object method ensemble 2" ? {o baz} 1 "class level object method" # # Make sure that [current callingclass] works for submethods, as # expected # C eval { set body {expr {[:bar] eq "[current class]-[current]-[current methodpath]"}} :public method "FOO foo" {} $body :public method "BAR BUU boo" {} $body :public method baz {} $body :method bar {} { return "[current callingclass]-[current callingobject]-[current callingmethod]" } } set c [C new] ? [list $c FOO foo] 1 ? [list $c BAR BUU boo] 1 ? [list $c baz] 1 # # [current calledmethod] # [current calledclass] # # Note: In my reading, [current calledmethod] cannot be made aware # of the methodpath of a submethod call being intercepted. This is # due to the callstack structure at the time of executing the filter # stack which is entirely agnostic of the submethod dispatch (this # dispatch has not occurred yet). For the same reason, we cannot # record the method path in the filter stack structure. # # From the filter method's perspective, the submethod selectors # ("foo" and "BUU boo" below) are simply arguments provided to the # top-level method. They can only be processed as part of the # filter-local argv. Class create Z { :object property msg :method intercept args { [current class] eval [list set :msg [list [lrange [current methodpath] 1 end-1] \ [current calledmethod] \ [current calledclass] \ [current nextmethod]]] next } } set c [Z new] Z filters set intercept foreach selector [list "FOO foo" "BAR BUU boo" "baz"] { Z public method $selector {} {;} set root [lindex $selector 0] set mh [Z info method registrationhandle $root] $c {*}$selector ? [list set _ [join [Z cget -msg] -]] -$root-::Z-$mh } Z filters set {} } # # Test current args in ensemble methods # nx::test case current-args { nx::Class create C { :method foo {{-x 1} z:optional} {return [current args]} :method "bar foo" {{-x 1} z:optional} {return [current args]} :create c1 } ? {c1 foo} "" ? {c1 bar foo} "" ? {c1 foo -x 2} "-x 2" ? {c1 bar foo -x 2} "-x 2" } # # Test keepcallerself and perobjectdispatch with their respective # interactions for plain object dispatch and for object dispatch via # method interface # nx::test case per-object-dispatch { nx::Class create C { :public method foo {} {return foo-[self]} :public method baz {} {return [c1::1 baz]} :create c1 { :public object method bar {} {return bar-[self]} } } ? {c1 foo} "foo-::c1" ? {c1 bar} "bar-::c1" C create c1::1 { :public object method bar {} {return bar-[self]} :public object method baz {} {return baz-[self]} } # # Just the same as above # ? {c1::1 foo} "foo-::c1::1" ? {c1::1 bar} "bar-::c1::1" # if we specify anything special, then we have per-default # - keepcallerself false # - perobjectdispatch false ? {c1 1 foo} "foo-::c1::1" ? {c1 1 bar} "bar-::c1::1" ? {c1 baz} "baz-::c1::1" # just make setting explicit ::nsf::object::property ::c1::1 keepcallerself off ::nsf::object::property ::c1::1 perobjectdispatch off ? {c1 1 foo} "foo-::c1::1" ? {c1 1 bar} "bar-::c1::1" ? {c1 baz} "baz-::c1::1" # keepcallerself off - the self in the called method is the invoked object # perobjectdispatch on - the instance method is not callable ::nsf::object::property ::c1::1 keepcallerself off ::nsf::object::property ::c1::1 perobjectdispatch on ? {c1 1 foo} {::c1::1: unable to dispatch method 'foo'} ? {c1 1 bar} "bar-::c1::1" ? {c1 baz} "baz-::c1::1" # keepcallerself on - the self in the called method is the caller # perobjectdispatch on - the instance method is not callable ::nsf::object::property ::c1::1 keepcallerself on ::nsf::object::property ::c1::1 perobjectdispatch on ? {c1 1 foo} {::c1::1: unable to dispatch method 'foo'} ? {c1 1 bar} "bar-::c1" #### ignore keepcallerself via interface with explicit receiver intentionally ? {c1 baz} "baz-::c1::1" # keepcallerself on - the self in the called method is the caller # perobjectdispatch off - the instance method is callable ::nsf::object::property ::c1::1 keepcallerself on ::nsf::object::property ::c1::1 perobjectdispatch off ? {c1 1 foo} "foo-::c1" ? {c1 1 bar} "bar-::c1" #### ignore keepcallerself via interface with explicit receiver intentionally ? {c1 baz} "baz-::c1::1" } # # Test forwarding to child object, with respect to settings of the # object properties keepcallerself and allowmethoddispatch # nx::test configure -count 1000 nx::test case child-obj-delegation { nx::Object create obj { nx::Object create [self]::child { :public object method foo {} {return [self]} } :public object forward link1 {%[self]::child} :public object forward link2 :child :public object method link3 args {[self]::child {*}$args} :public object alias link4 [self]::child :public object forward link5 [self]::child } # # Default case # keepcallerself false # perobjectdispatch false # ::nsf::object::property obj::child keepcallerself false ::nsf::object::property obj::child perobjectdispatch false ? {obj link1 foo} {::obj::child} #? {obj link2 foo} {::obj: unable to dispatch method 'child'} ? {obj link2 foo} {::obj::child} ? {obj link3 foo} {::obj::child} ? {obj link4 foo} {::obj::child} ? {obj link5 foo} {::obj::child} ? {obj child foo} {::obj::child} #? {lsort [obj info object methods child]} {} #? {lsort [obj info object methods]} {link1 link2 link3 link4 link5} #? {lsort [obj info lookup methods child]} {} #? {lsort [obj info lookup methods child*]} {} ? {lsort [obj info object methods child]} {child} ? {lsort [obj info object methods]} {child link1 link2 link3 link4 link5} ? {lsort [obj info lookup methods child]} {child} ? {lsort [obj info lookup methods child*]} {child} # # turn on keepcallerself and perobjectdispatch # ::nsf::object::property obj::child keepcallerself true ::nsf::object::property obj::child perobjectdispatch true ? {obj link1 foo} {::obj::child} #? {obj link2 foo} {::obj: unable to dispatch method 'child'} ? {obj link2 foo} {::obj} ? {obj link3 foo} {::obj::child} ? {obj link4 foo} {::obj} ? {obj link5 foo} {::obj::child} ? {obj child foo} {::obj} #? {lsort [obj info object methods child]} {} #? {lsort [obj info object methods]} {link1 link2 link3 link4 link5} #? {lsort [obj info lookup methods child]} {} #? {lsort [obj info lookup methods child*]} {} ? {lsort [obj info object methods child]} {child} ? {lsort [obj info object methods]} {child link1 link2 link3 link4 link5} ? {lsort [obj info lookup methods child]} {child} ? {lsort [obj info lookup methods child*]} {child} # # just perobjectdispatch # ::nsf::object::property obj::child keepcallerself false ::nsf::object::property obj::child perobjectdispatch true ? {obj link1 foo} {::obj::child} ? {obj link2 foo} {::obj::child} ? {obj link3 foo} {::obj::child} ? {obj link4 foo} {::obj::child} ? {obj link5 foo} {::obj::child} ? {obj child foo} {::obj::child} ? {lsort [obj info object methods child]} {child} ? {lsort [obj info object methods]} {child link1 link2 link3 link4 link5} ? {lsort [obj info lookup methods child]} {child} ? {lsort [obj info lookup methods child*]} {child} # # just keepcallerself # ::nsf::object::property obj::child keepcallerself true ::nsf::object::property obj::child perobjectdispatch false ? {obj link1 foo} {::obj::child} #? {obj link2 foo} {::obj: unable to dispatch method 'foo'} ? {obj link2 foo} {::obj} ? {obj link3 foo} {::obj::child} #? {obj link4 foo} {::obj: unable to dispatch method 'foo'} ? {obj link4 foo} {::obj} ? {obj link5 foo} {::obj::child} #? {obj child foo} {::obj: unable to dispatch method 'foo'} ? {obj child foo} {::obj} ? {lsort [obj info object methods child]} {child} ? {lsort [obj info object methods]} {child link1 link2 link3 link4 link5} ? {lsort [obj info lookup methods child]} {child} ? {lsort [obj info lookup methods child*]} {child} } # # Examplify the current behavior of "keepcallerself" with and without # the setting of "perobjectdispatch" # nx::test configure -count 1 nx::test case keepcallerself { nx::Class create C {:public method foo {} {return C-[self]}} nx::Class create D {:public method foo {} {return D-[self]}} C create c1 { ::nsf::object::property [self] keepcallerself true :public object method bar {} {return c1-[self]} :public object method baz {} {return c1-[self]} } D create d1 { :public object method bar {} {return d1-[self]} :public object alias c1 ::c1 } # The normal dispatch ignores the keepcallerself completely ? {c1 bar} c1-::c1 ? {c1 foo} C-::c1 ? {c1 baz} c1-::c1 # The dispatch via object aliased method calls actually "d1 bar", # although c1 is in the dispatch path #? {d1 c1 bar} d1-::d1 #? {d1 c1 foo} D-::d1 #? {d1 c1 baz} "::d1: unable to dispatch method 'baz'" ? {d1 c1 bar} c1-::d1 ? {d1 c1 foo} C-::d1 ? {d1 c1 baz} c1-::d1 # The destroy destroys actually d1, not c1, although destroy is # dispatched originally on c1 ? {d1 c1 destroy} "" ? {nsf::object::exists d1} 0 ? {nsf::object::exists c1} 1 # So, keepcallerself is currently pretty useless, unless used in # combination with "perobjectdispatch", which we set in the # following test cases C create c1 { ::nsf::object::property [self] keepcallerself true ::nsf::object::property [self] perobjectdispatch true :public object method bar {} {return c1-[self]} :public object method baz {} {return c1-[self]} } D create d1 { :public object method bar {} {return d1-[self]} :public object alias c1 ::c1 } # The normal dispatch ignores the keepcallerself and # perobjectdispatch completely ? {c1 bar} c1-::c1 ? {c1 foo} C-::c1 ? {c1 baz} c1-::c1 # The dispatch via object aliased method calls actually "d1 bar", # although c1 is in the dispatch path ? {d1 c1 bar} c1-::d1 ? {d1 c1 foo} "::c1: unable to dispatch method 'foo'" ? {d1 c1 baz} c1-::d1 } nx::test case ensemble-vs-simple-method { ? {nx::Class create C} ::C ? {C public method foo {args} {return foo/1}} "::nsf::classes::C::foo" ? {C public method "foo x" {args} {return foo/2}} \ {refuse to overwrite method foo; delete/rename method first.} ? {C public method "foo x y" {args} {return foo/3}} \ {refuse to overwrite method foo; delete/rename method first.} ? {C public method "bar x" {args} {return bar/2}} {::C::slot::__bar::x} ? {C public method "bar x y" {args} {return foo/3}} \ {refuse to overwrite cmd ::C::slot::__bar::x; delete/rename it before overwriting} C create c1 ? {c1 foo x y z} "foo/1" ? {c1 bar x y z} "bar/2" ? {nx::Object create o1} ::o1 ? {o1 public object method foo {args} {return foo/1}} "::o1::foo" ? {o1 public object method "foo x" {args} {return foo/2}} \ {refuse to overwrite object method foo; delete/rename object method first.} ? {o1 public object method "foo x y" {args} {return foo/3}} \ {refuse to overwrite object method foo; delete/rename object method first.} ? {o1 public object method "bar x" {args} {return bar/2}} {::o1::bar::x} ? {o1 public object method "bar x y" {args} {return foo/3}} \ {refuse to overwrite cmd ::o1::bar::x; delete/rename it before overwriting} ? {o1 foo x y z} "foo/1" ? {o1 bar x y z} "bar/2" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/summary.tcl000066400000000000000000000023061242365656200144350ustar00rootroot00000000000000# -*- Tcl -*- # # This script computes the regression test summary displayed at the # end of the regression test. It aggreates the content of the test log # provided via arg "-testlog". array set opt {-title ""} array set opt $::argv if {[info exists opt(-testlog)]} { set f [open $opt(-testlog)]; set content [read $f]; close $f lassign {0 0 0 0 0.0} tests success failures files ms foreach l [split $content \n] { array set "" $l if {[info exists (tests)]} { incr tests $(tests) incr failures $(failure) incr success $(success) incr files 1 set ms [expr {$ms + $(ms)}] } } puts "\nRegression Test Summary of $opt(-title):" puts "\tEnvironment: Tcl $tcl_patchLevel, OS $tcl_platform(os) $tcl_platform(osVersion)\ machine $tcl_platform(machine) threaded [info exists tcl_platform(threaded)]." puts "\tNSF performed $tests tests in $files files, success $success, failures $failures in [expr {$ms / 1000.0}] seconds" if {$failures == 0} { puts "\tCongratulations, all tests of $opt(-title) passed in your installation of NSF [package req nsf]" } puts "" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/tcl86.test000066400000000000000000000131621242365656200140770ustar00rootroot00000000000000# -*- Tcl -*- package require nx package require nx::test # just 8.6 or newer if {[info command yield] eq ""} return # # Test coroutine / yield # nx::test case number-generator { nx::Object create ::numbers { # set instance variable used in coroutine set :delta 2 :public object method ++ {} { yield set i 0 while 1 { yield $i incr i ${:delta} } } } # create coroutine coroutine nextNumber ::numbers ++ set ::j 0 # use coroutine for {set i 0} {$i < 10} {incr i} { incr ::j [nextNumber] } # remove coroutine rename nextNumber {} ? {set ::j} 90 } # # Test coroutine / yield # nx::test case enumerator1 { # # enumerator with yield in a single class # nx::Class create Enumerator { :property members:0..n :public method yielder {} { yield [info coroutine] foreach m ${:members} { yield $m } return -level 2 -code break } :public method next {} {${:coro}} :method init {} { :require namespace set :coro [coroutine [self]::coro [self] yielder] } } # # Some application class using the enumerator (just used for easy # testing) # nx::Class create Foo { :public method sum {} { set sum 0 set e [Enumerator new -members {1 2 3}] while 1 { incr sum [$e next] } return $sum } :create f1 } ? {f1 sum} 6 } nx::test case enumerator2 { # # Define separate classes for Yielder and Enumerator # nx::Class create Yielder { :property {block ";"} :variable continuation "" # # make apply available as a method # :public alias apply ::apply # # The method "yielder" is the working horse for next. We need this # since the interface of Tcl's coroutines is based on a separate # cmd for continuation in the coroutine. The block can be # configured by application classes. # :public method yielder {} { yield [info coroutine] eval ${:block} return -level 2 -code break } # # The method "next" simply forwards to the continuation # :public method next {} {${:continuation}} # # The method "each" is based on the method "next" and applies the # value returned by next to the lambda expression # :public method each {var body} { while 1 { uplevel [list set $var [:next]] uplevel $body } } # # When a yielder is generated, we create automatically a coroutine # for it. The coroutine is placed under the current object, this # ensures simple cleanup (but is most probably not the fastest # variant, since we have to require a namespace). # :method init {} { :require namespace set :continuation [coroutine [self]::coro [self] yielder] } } # # The class "Enumerator" provides some application logic for the # class "Yielder". We use here a list of elements as base # representation. # nx::Class create Enumerator -superclass Yielder { :property members:0..n :property {block { foreach m ${:members} { yield $m } }} } # # Some application class using the enumerator (just used for easy # testing) # nx::Class create Foo { # test Enumerator.next :public method sum {} { set sum 0 set e [Enumerator new -members {1 2 3}] while 1 { incr sum [$e next] } return $sum } :public method set {var} { set :$var } # test Enumerator.each :public method concat {} { set string "-" set i 0 set e [Enumerator new -members {a be bu}] $e each x { append string $x-([incr i])- } return $string } :create f1 } ? {f1 sum} 6 ? {f1 concat} "-a-(1)-be-(2)-bu-(3)-" # # Define a class ATeam that uses "Enumerator", refines the method # "each" and adds another method "concat" # nx::Class create ATeam -superclass Enumerator { # # Overload "each" to show overloading. Here, we simply capitalize # the memebers in the "each" method. # :public method each {var body} { while 1 { set value [string totitle [:next]] uplevel [list set $var $value] uplevel $body } } # Define some arbitrary method using ATeam.each :public method concat {} { set string "-" :each x { append string $x- } return $string } } ATeam create a1 -members {alice bob ceasar} ? {a1 concat } "-Alice-Bob-Ceasar-" } # # apply # nx::test case apply { # Register apply as an alias ::nx::Object public alias apply ::apply ::nx::Object create o { # Set an object variable set :delta 100 # Define a standard map function based on apply :public object method map {lambda values} { set result {} foreach value $values { lappend result [:apply $lambda $value] } return $result } :object method foo {x} {return $x-$x} } # Two examples from the apply man page ? {o map {x {return [string length $x]:$x}} {a bb ccc dddd}} \ "1:a 2:bb 3:ccc 4:dddd" ? {o map {x {expr {$x**2 + 3*$x - 2}}} {-4 -3 -2 -1 0 1 2 3 4}} \ "2 -2 -4 -4 -2 2 8 16 26" ## Test case accessing object specific variable #? {o map {x {::nsf::__db_show_stack; return [expr {$x * ${:delta}}]}} {-4 -3 -2 -1 0 1 2 3 4}} \ # "-400 -300 -200 -100 0 100 200 300 400" # Test case accessing object specific variable ? {o map {x {expr {$x * ${:delta}}}} {-4 -3 -2 -1 0 1 2 3 4}} \ "-400 -300 -200 -100 0 100 200 300 400" # Test case calling own method via apply ? {o map {x {:foo $x}} {hello world}} \ "hello-hello world-world" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/tcloo.test000066400000000000000000000217011242365656200142550ustar00rootroot00000000000000# -*- Tcl -*- package req nx package req nx::test # # export | unexport # # TclOO provides a bulk declarator to export (i.e., make # visibile and accessible) and to unexport (i.e., make invisible and # inaccesible) method features of an object, a class, or a class # hierarchy. Note that export and unexport go beyond applying mere # visibility/accessibility modifiers; it is also about extending or # reducing the public method interface of an object along the # linearisation path (or of derived, intermediary classes in an # inheritance hierarchy). This export|unexport can be realised by # assembling some NSF building blocks: method call protection, # selective next forwarding, ... # # Internally, exporting a TclOO method means adding to its C-level # rep's flags PUBLIC_METHOD; unexporting consists of withdrawing it # (again). An unexported, non-public TclOO method can only me invoked # upon through a self send (i.e., the my command). This corresponds # somewhat to NSF's call protection property. In addition, [export] or # [unexport] extends the method record of an object (or class) type in # case the method to be exported or to be unexported has not yet been # defined (on the exporting or unexporting object or class). These # "extension methods", however, are mere method stubs, they do not # contain a method implementation (a proc). Without any invokable # method impl, they are skipped during method dispatch (as in an # implicit next call). Using the method stubs, the public interface # (i.e., the interface dispatchable through an object's Tcl_command) # can be extended or shrinked by selectively enabling or disabling # shadowed (inherited) method implementations along the instande-of or # the inheritance relationships. Exported or unexported, yet # unimplemented methods are treated as unknowns. # # Below is a prototype implementation of the export|unexport feature # for NSF/Nx. The realisation is complete as testable through the # respective TclOO test cases in oo.test, test cases 4.1-4.6. The # export|unexport stub methods are fully reported by NSF/Nx method # introspection, as they are in TclOO. nsf::proc methodExport {current {-perObject:switch false} {-callProtected:switch false} args} { set scope [expr {$perObject?"object":"class"}] foreach m $args { set methodHandle [::nsf::dispatch $current \ ::nsf::methods::${scope}::info::method registrationhandle $m] if {$methodHandle eq ""} { set methodHandle [::nsf::method::create $current {*}[expr {$perObject?"-per-object":""}] $m args { if {[::nsf::current nextmethod] eq ""} { return -code error "[::nsf::current]: unable to dispatch method '[::nsf::current method]'" } ::nsf::next }] } ::nsf::method::property $current $methodHandle call-protected $callProtected } return } nx::Object public method export args { methodExport [::nsf::current] -perObject {*}$args } nx::Class public method export args { methodExport [::nsf::current] {*}$args } nx::Object public method unexport args { methodExport [::nsf::current] -perObject -callProtected {*}$args } nx::Class public method unexport args { methodExport [::nsf::current] -callProtected {*}$args } nx::Class create ExportUnexportUtil { :public method class {what args} { if {$what in {export unexport}} { return [::nsf::dispatch [current] ::nsf::classes::nx::Object::$what {*}$args] } ::nsf::next } } nx::Class mixins add ExportUnexportUtil nx::test case export { # # Exporting existing, non-inherited method (see TclOO tests, # oo.test, oo-4.1) # set o [nx::Object new] $o object method Foo {} { return [::nsf::current method]} ? [list $o Foo] "$o: unable to dispatch method 'Foo'" ? [list $o eval {:Foo}] Foo $o export Foo ? [list $o Foo] "Foo" ? [list $o eval {:Foo}] Foo # # A solitary, preemptive [export]: In TclOO, [::oo::define export] # creates a method record entry which does not have any # implementation (body) attached and which is deprived of its property of # a PUBLIC_METHOD. This non-implemented, body-less method (if not # succeeded by an implemented one) will be reported as unknown # method (see e.g. TclOO tests, oo.test, oo-4.3) # # As we actually simulate the TclOO non-implemented method record # entries by full-fledged NSF methods, with a specific body (a next # call), we need to handle the solitary case, i.e., the case when # there is no method implementation available. We do so by # inspecting whether there is a next method to be called; if not, we # throw an unknown error. # ? [list $o bar] "$o: unable to dispatch method 'bar'" "bar is neither defined, nor declared exported" $o export bar ? [list $o bar] "$o: unable to dispatch method 'bar'" "bar is exported, yet not defined anywhere" ? [list $o eval {:bar}] "$o: unable to dispatch method 'bar'" "bar is exported, yet not defined anywhere (self send)" # # Exporting a per-class method from one of the class' instances (see # TclOO tests, oo.test, oo-4.4) # Class create testClass { # protected (non-exported) by default :method Good {} { return ok } :method Fine {} { return OK } :method Finest {} {return ko } :create testObject } ? {testObject Good} "::testObject: unable to dispatch method 'Good'" ? {testObject eval {:Good}} ok testObject export Good ? {testObject Good} ok # # Exporting a per-class method from within the class # ? {testObject Fine} "::testObject: unable to dispatch method 'Fine'" ? {testObject eval {:Fine}} OK testClass export Fine ? {testObject Fine} OK ? {testObject eval {:Fine}} OK # # Exporting a per-class method by a subclass # Class create anotherTestClass -superclass testClass { :create anotherTestObject } ? {anotherTestObject Finest} "::anotherTestObject: unable to dispatch method 'Finest'" anotherTestClass export Finest ? {anotherTestObject Finest} ko # # export creates ordinary methods, to be replaced by subsequent # once, see TclOO tests, oo.test, oo-4.5 # nx::Object create bran { :export foo :public object method foo {} {return ok} } ? {bran foo} ok bran eval { :unexport foo } ? {bran foo} "::bran: unable to dispatch method 'foo'" } nx::test case unexport { # A solitary, preemptive [unexport]: see description for the # corresponding [export] case set p [Object new] ? [list $p bar] "$p: unable to dispatch method 'bar'" $p unexport bar ? [list $p bar] "$p: unable to dispatch method 'bar'" ? [list $p eval {:bar}] "$p: unable to dispatch method 'bar'" # # unexport existing, non-inherited method (see TclOO tests: # oo.test/oo-4.2) # set o [nx::Object new] $o public object method foo {} { return [::nsf::current method]} ? [list $o foo] foo ? [list $o eval {:foo}] foo $o unexport foo ? [list $o foo] "$o: unable to dispatch method 'foo'" "foo was made 'protected'" ? [list $o eval {:foo}] foo "foo is still available for self sends" # # unexport any (e.g., inherited) methods # Class create C { :public method foo {} {return ok} } set c [C new] ? [list $c foo] ok ? [list $c eval {:foo}] ok $c unexport foo ? [list $c foo] "$c: unable to dispatch method 'foo'" "created a protected dummy" ? [list $c eval {:foo}] ok "foo is still available for self sends (through a next send in the dummy)" # # unexport existing method at the class level # C eval { :public method bar {} {return OK} :public method baz {} {return ko} } ? [list $c bar] OK ? [list $c eval {:bar}] OK C unexport bar ? [list $c bar] "$c: unable to dispatch method 'bar'" "created a protected dummy" ? [list $c eval {:bar}] OK "bar is still available for self sends (through a next send in the dummy)" # # unexport any (e.g., an inherited) method at the class level # nx::Class create D -superclass C set d [D new] ? [list $d bar] "$d: unable to dispatch method 'bar'" "shielded by protected dummy at the level of class C" ? [list $d eval {:bar}] OK ? [list $d baz] ko D unexport baz ? [list $d baz] "$d: unable to dispatch method 'baz'" ? [list $d eval {:baz}] ko # # unexport creates ordinary methods, to be fully replaced by subsequent # method declarations, see TclOO tests, oo.test, oo-4.6 # Class create testClass2 { :unexport foo :public method foo {} {return ok} } ? {[testClass2 new] foo} ok # # http://rosettacode.org/wiki/Abstract_type # nx::Class create AbstractQueue { :method enqueue item { error "not implemented" } :method dequeue {} { error "not implemented" } :class unexport create new } ? {AbstractQueue new} {method 'new' unknown for ::AbstractQueue; consider '::AbstractQueue create new ' instead of '::AbstractQueue new '} ? {AbstractQueue create aQueue} {method 'create' unknown for ::AbstractQueue; consider '::AbstractQueue create create aQueue' instead of '::AbstractQueue create aQueue'} } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/traits.test000066400000000000000000000033531242365656200144460ustar00rootroot00000000000000package require nx::trait package require nx::test nx::test case basics { nx::Trait create t1 { :public method t1m1 {} {return t1.[current method]} :public method t1m2 {} {return t1.[current method]} # This trait requires a method "foo" :requiredMethods foo } nx::Trait create t2 { :public method "bar x" {} {return t2.[current methodpath]} :public method "bar y" {} {return t2.[current methodpath]} :public method foo {} {return t2.[current methodpath]} # This trait requires a method "t1m1" :requiredMethods t1m1 } nx::Trait create t3 { :public method "bar y" {} {return t3.[current methodpath]} :public method "bar z" {} {return t3.[current methodpath]} } nx::Class create C { :property {collection ""} :public method foo {} {return [current method]} :create c1 } ? {c1 foo} "foo" ? {C require trait t2} "trait t2 requires t1m1, which is not defined for ::C" ? {lsort [C info methods]} "foo" C require trait t1 ? {lsort [C info methods]} "foo t1m1 t1m2" ? {c1 foo} "foo" ? {C require trait t2} "" ? {lsort [C info methods]} "bar foo t1m1 t1m2" ? {lsort [C info methods -path]} "{bar x} {bar y} foo t1m1 t1m2" # trait t2 redefines t2, so we call that see its result here ? {c1 foo} "t2.foo" ? {c1 bar x} "t2.bar x" ? {c1 bar y} "t2.bar y" ? {C require trait t3} "" ? {lsort [C info methods]} "bar foo t1m1 t1m2" ? {lsort [C info methods -path]} "{bar x} {bar y} {bar z} foo t1m1 t1m2" # trait t3 redefines "bar y", so we call that see its result here ? {c1 foo} "t2.foo" ? {c1 bar x} "t2.bar x" ? {c1 bar y} "t3.bar y" ? {c1 bar z} "t3.bar z" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/var-access.test000066400000000000000000000051201242365656200151610ustar00rootroot00000000000000# -*- Tcl -*- package require nx ::nx::configure defaultMethodCallProtection false package require nx::test namespace eval ::nx::var1 { namespace ensemble create -map { exists ::nsf::var::exists import ::nsf::var::import set ::nsf::var::set } } ::nx::Object create ::nx::var2 { :object alias exists ::nsf::var::exists :object alias import ::nsf::var::import :object alias set ::nsf::var::set } nx::test case set+array { nx::Object create o1 # first set a scalar variable ? {nsf::var::set o1 x 100} "100" ? {nsf::var::set o1 x} "100" # now, set an array variable; "nsf::var::set -array" is a wrapper # around "array set" or "array get" ? {nsf::var::set -array o1 a {a 1 y 2}} "" ? {nsf::var::set -array o1 a} "y 2 a 1" # We have now a scalar and an array variable set. ? {lsort [o1 info vars]} "a x" # "x" is a variable, but not an array ? {nsf::var::exists o1 x} 1 ? {nsf::var::exists -array o1 x} 0 # "a" is a variable and an array ? {nsf::var::exists -array o1 a} 1 ? {nsf::var::exists o1 a} 1 # we unset the array ? {nsf::var::unset o1 a} "" ? {nsf::var::exists o1 a} 0 ? {nsf::var::exists -array o1 a} 0 # now, just the scalar is left ? {o1 info vars} "x" ? {nsf::var::exists o1 x} 1 ? {nsf::var::exists -array o1 x} 0 # we unset the scalar ? {nsf::var::unset o1 x} "" ? {nsf::var::exists o1 x} 0 ? {nsf::var::exists -array o1 x} 0 # unset on an non-existing variable ? {nsf::var::unset o1 x} {can't unset "x": no such variable} ? {nsf::var::unset -nocomplain o1 x} "" } nx::test configure -count 10000 nx::test case dummy { nx::Object create o { set :x 1 } nx::Object create p { set :y 1 :object method foo0 {} { incr :y } :object method foo1 {} { o eval {incr :x} } :object method foo2 {} { ::nsf::var::import o x incr x } :object method foo3 {} { ::nx::var1 import o x incr x } :object method foo4 {} { ::nx::var2 import o x incr x } } ? {::nsf::var::set o x} 1 ? {::nsf::var::exists o x} 1 ? {::nsf::var::exists o y} 0 ? {::nx::var1 set o x} 1 ? {::nx::var1 exists o x} 1 ? {::nx::var1 exists o y} 0 ? {::nx::var2 set o x} 1 ? {::nx::var2 exists o x} 1 ? {::nx::var2 exists o y} 0 ? {p foo0} 2 ? {p foo1} 2 ? {::nsf::var::set o x} 10002 ? {p foo2} 10003 ? {::nsf::var::set o x} 20003 ? {p foo3} 20004 ? {::nsf::var::set o x} 30004 ? {p foo4} 30005 ? {::nsf::var::set o x} 40005 } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/varresolution.test000066400000000000000000001061541242365656200160570ustar00rootroot00000000000000# -*- Tcl -*- # # testing var resolution # package require nx package require nx::test ::nx::configure defaultMethodCallProtection false ::nsf::method::alias ::nx::Object objeval -frame object ::eval ::nsf::method::alias ::nx::Object array -frame object ::array ::nsf::method::alias ::nx::Object lappend -frame object ::lappend ::nsf::method::alias ::nx::Object incr -frame object ::incr ::nsf::method::alias ::nx::Object set -frame object ::set ::nsf::method::alias ::nx::Object unset -frame object ::unset ########################################### # Basic tests for var resolution under # per-object namespaces ... ########################################### nx::test case globals set ::globalVar 1 nx::Object create o o require namespace ? {o info vars} "" ? {info exists ::globalVar} 1 ? {set ::globalVar} 1 ? {o eval {info exists :globalVar}} 0 ? {o array exists globalVar} 0 o array set globalVar {1 2} ? {o eval {info exists :globalVar}} 1 ? {o info vars} globalVar ? {o array exists globalVar} 1 ? {set ::globalVar} 1 ? {o set globalVar(1)} 2 o destroy unset ::globalVar ########################################### # scopes ########################################### nx::test case scopes nx::Object create o nx::Object create o2 {set :i 1} o objeval { # require an namespace within an objscoped frame; it is necessary to replace # vartables on the stack :require namespace global g ::nsf::var::import o2 i set x 1 set :y 2 set ::z 3 set [current]::X 4 set g 1 set :a(:b) 1 set :a(::c) 1 } ? {::nsf::var::import o2 j} \ "importvar cannot import variable 'j' into method scope; not called from a method frame" o object method foo {} {::nsf::var::import [current] :a} ? {o foo} "variable name \":a\" must not contain namespace separator or colon prefix" o object method foo {} {::nsf::var::import [current] ::a} ? {o foo} "variable name \"::a\" must not contain namespace separator or colon prefix" o object method foo {} {::nsf::var::import [current] a(:b)} ? {o foo} "can't make instance variable a(:b) on ::o: Variable cannot be an element in an array; use e.g. an alias." o object method foo {} {::nsf::var::import [current] {a(:b) ab}} ? {o foo} "" o object method foo {} {::nsf::var::exists [current] ::a} ? {o foo} "variable name \"::a\" must not contain namespace separator or colon prefix" o object method foo {} {::nsf::var::exists [current] a(:b)} ? {o foo} 1 o object method foo {} {::nsf::var::exists [current] a(::c)} ? {o foo} 1 set ::o::Y 5 ? {info vars ::x} "" ? {info exists ::z} 1 ? {set ::z} 3 ? {lsort [o info vars]} {X Y a g i x y} ? {o eval {info exists :x}} 1 ? {o eval {info exists :y}} 1 ? {o eval {info exists :z}} 0 ? {o eval {info exists :X}} 1 ? {o eval {info exists :Y}} 1 ? {o set y} 2 ? {set ::g} 1 o destroy o2 destroy unset ::z unset ::g # like the example above, but with the non-leaf initcmd nx::Object create o2 {set :i 1} nx::Object create o { :require namespace global g ::nsf::var::import o2 i set x 1 set :y 2 set ::z 3 set [current]::X 4 set g 1 } set ::o::Y 5 ? {info vars ::x} "" ? {info exists ::z} 1 ? {set ::z} 3 ? {lsort [o info vars]} {X Y y} ? {o eval {info exists :x}} 0 ? {o eval {info exists :y}} 1 ? {o eval {info exists :z}} 0 ? {o eval {info exists :X}} 1 ? {o eval {info exists :Y}} 1 ? {o set y} 2 ? {set ::g} 1 o destroy o2 destroy unset ::z unset ::g foreach v {::x ::z ::g} {unset -nocomplain $v} ########################################### # var exists tests ########################################### nx::test case exists { set y 1 nx::Object create o {set :x 1} o object method foo {} {info exists :x} o object method bar {} {info exists :y} ? {o eval {info exists :x}} 1 ? {o eval {info exists :y}} 0 ? {o eval {info exists x}} 0 ? {o foo} 1 ? {o bar} 0 ? {::nx::var exists o x} 1 ? {::nx::var exists o y} 0 ? {::nx::var exists o :x} {variable name ":x" must not contain namespace separator or colon prefix} ? {::nx::var exists o :y} {variable name ":y" must not contain namespace separator or colon prefix} ? {::nx::var set o y 2} 2 ? {::nx::var exists o y} 1 ? {::nx::var set o :y 2} {variable name ":y" must not contain namespace separator or colon prefix} } ########################################### # mix & match namespace and object interfaces ########################################### nx::test case namespaces nx::Object create o o require namespace o set x 1 ? {namespace eval ::o {set x}} 1 ? {::o set x} 1 ? {namespace eval ::o {set x 3}} 3 ? {::o set x} 3 ? {namespace eval ::o {info exists x}} 1 ? {::o unset x} "" ? {::nsf::var::exists o x} 0 ? {o eval {info exists :x}} 0 ? {info vars ::x} "" ? {namespace eval ::o {info exists x}} 0 o lappend y 3 ? {namespace eval ::o {llength y}} 1 ? {namespace eval ::o {unset y}} "" ? {o eval {info exists :y}} 0 o destroy ########################################### # array-specific tests ########################################### nx::test case namespaces-array nx::Object create o o require namespace ? {o array exists a} 0 ? {namespace eval ::o array exists a} 0 o array set a {1 2 3 4 5 6} ? {o array exists a} 1 ? {namespace eval ::o array exists a} 1 ? {namespace eval ::o array names a} [::o array names a] ? {namespace eval ::o array size a} [::o array size a] ? {o set a(1) 7} 7 ? {namespace eval ::o array get a 1} {1 7} ? {namespace eval ::o set a(1) 2} 2 ? {o array get a 1} {1 2} ? {::o unset a} "" ? {::o array unset a} "" ? {o array exists a} 0 ? {namespace eval ::o array exists a} 0 o destroy ########################################### # tests on namespace-qualified var names ########################################### nx::test case namespaced-var-names nx::Object create o o require namespace nx::Object create o::oo o::oo require namespace ? {::o set ::x 1} 1 ? {info exists ::x} [set ::x] ? {catch {unset ::x}} 0 ? {::o set ::o::x 1} 1 ? {o eval {info exists :x}} [::o set ::o::x] ? {namespace eval ::o unset x} "" ? {o eval {info exists x}} 0 # Note, relatively qualified var names (not prefixed with ::*) # are always resolved relative to the per-object namespace ? {catch {::o set o::x 1} msg} 1 ? {::o set oo::x 1} 1 ? {o::oo eval {info exists :x}} [::o set oo::x] ? {o unset oo::x} "" ? {o::oo eval {info exists :x}} 0 o destroy ########################################### # tests on namespace-qualified on objects # without namespaces ########################################### # the tests below fail. We could consider # to require namespaces on the fly in the future #nx::Object create o #? {::o set ::o::x 1} 1 #? {o exists x} [::o set ::o::x] #? {namespace eval ::o unset x} "" #? {o exists x} 0 #? {::o set o::x 1} 1 #? {o exists x} [::o set o::x] #? {namespace eval ::o unset x} "" #? {o exists x} 0 #o destroy ############################################### # tests for the compiled var resolver on Object ############################################### nx::test case var-resolver-object nx::Object create o o object method foo {x} {set :y 2; return ${:x},${:y}} o object method bar {} {return ${:x},${:y}} o set x 1 ? {o foo 1} "1,2" "create var y and fetch var x" ? {o bar} "1,2" "fetch two instance variables" ? {o info vars} "x y" # recreate object, check var caching; # we have to recreate bar, so no problem nx::Object create o o set x 1 o object method bar {} {return ${:x},${:y}} ? {catch {o bar}} "1" "compiled var y should not exist" o destroy ############################################### # tests for the compiled var resolver on Class ############################################### nx::test case var-resolver-class nx::Class create C {:property {x 1}} C create c1 C method foo {x} {set :y 2; return ${:x},${:y}} C method bar {} {return ${:x},${:y}} ? {c1 info vars} "x" ? {c1 foo 1} "1,2" "create var y and fetch var x" ? {c1 bar} "1,2" "fetch two instance variables" ? {c1 info vars} "x y" # recreate object, check var caching; # we do not have to recreate bar, compiled var persists, # change must be detected C create c1 #puts stderr "after recreate" ? {catch {c1 bar}} "1" "compiled var y should not exist" ? {c1 info vars} "x" c1 destroy C destroy ############################################### # tests for the compiled var resolver with eval ############################################### nx::test case compiled-var-resolver nx::Class create C {:property {x 1}} C create c1 C method foo {x} { set :y 2; eval "set :z 3" return ${:x},${:y},${:z} } ? {c1 info vars} "x" ? {c1 foo 1} "1,2,3" ? {c1 info vars} "x y z" C create c1 ? {c1 info vars} "x" C method foo {x} { set cmd set lappend cmd :y lappend cmd 100 eval $cmd return $x,${:y} } C method bar {} {return [info exists :x],[info exists :y]} C method bar2 {} {if {[info exists :x]} {set :x 1000}; return [info exists :x],[info exists :y]} ? {c1 foo 1} "1,100" ? {c1 bar} "1,1" ? {c1 bar2} "1,1" c1 unset x ? {c1 bar2} "0,1" c1 destroy C destroy ############################################### # tests with array ############################################### nx::Class create C C create c1 C method foo {} { array set :a {a 1 b 2 c 3} set :z 100 } ? {c1 info vars} "" c1 foo ? {lsort [c1 info vars]} {a z} ############################################### # tests for the var resolver ############################################### nx::test case var-resolver nx::Class create C C method bar0 {} {return ${:x}} C method bar1 {} {set a ${:x}; return [info exists :x],[info exists :y]} C method bar2 {} {return [info exists :x],[info exists :y]} C method foo {} { array set :a {a 1 b 2 c 3} set :z 100 } C create c1 c1 set x 100 ? {c1 bar0} 100 "single compiled local" ? {c1 bar1} 1,0 "lookup one compiled var and one non-existing" ? {c1 bar2} 1,0 "lookup one non compiled var and one non-existing" C create c2 ? {c2 bar2} 0,0 "lookup two one non-existing, first access to varTable" c1 foo ? {lsort [c1 info vars]} "a x z" "array variable set via resolver" ? {lsort [c1 array names a]} "a b c" "array looks ok" ############################################### # first tests for the cmd resolver ############################################### nx::Class create C C method bar {args} { #puts stderr "[current] bar called with [list $args]" return $args } C forward test %self bar C method foo {} { # this works lappend :r [:bar x 1] lappend :r [:test a b c] # these kind of works, but vars are nowhere.... :set x 1 :incr x 1 :incr x 1 return [lappend :r ${:x}] } C create c3 ? {c3 foo} "{x 1} {a b c} 3" ############################################### # refined tests for the var resolver under # Tcl namespaces parallelling XOTcl objects # (! not declared through require namespace !) # e.g., "info has namespace" reports 0 rather # than 1 as under "require namespace" ############################################### set ::w 1 array set ::tmpArray {key value} nx::Class create ::C ::nsf::method::alias ::C Set -frame object ::set ::nsf::method::alias ::C Unset -frame object ::unset ::C create ::c namespace eval ::c {} ? {namespace exists ::c} 1 ? {::nsf::object::exists ::c} 1 ? {::c info has namespace} 0 ? {::c Set w 2; expr {[::c Set w] == $::w}} 0 ? {::c Unset w; info exists ::w} 1 ? {::c Set tmpArray(key) value2; expr {[::c Set tmpArray(key)] == $::tmpArray(key)}} 0 ? {::c Unset tmpArray(key); info exists ::tmpArray(key)} 1 ::c destroy ::C destroy unset ::w unset ::tmpArray ################################################## # Testing aliases for eval with and without # -varscope flags and with a # required namespace and without ################################################## nx::test case eval-variants ::nsf::method::alias ::nx::Object objeval -frame object ::eval ::nsf::method::alias ::nx::Object softeval -frame method ::eval ::nsf::method::alias ::nx::Object softeval2 ::eval set G 1 nx::Object create o { set xxx 1 set :x 1 ? {info exists G} 1 } ? {o eval {info exists :x}} 1 ? {o eval {info exists :xxx}} 0 ? {info exists ::xxx} 0 unset -nocomplain ::xxx # eval does an objcope, all vars are instance variables; can access preexisting global vars o objeval { set aaa 1 set :a 1 ? {info exists G} 1 } ? {o eval {info exists :a}} 1 ? {o eval {info exists :aaa}} 1 ? {info exists ::aaa} 0 unset -nocomplain ::aaa # softeval (with -nonleaf) behaves like the initcmd and sets just # instance variables via resolver. o softeval { set bbb 1 set :b 1 ? {info exists G} 1 } ? {o eval {info exists :b}} 1 ? {o eval {info exists :bbb}} 0 ? {info vars ::bbb} "" unset -nocomplain ::bbb # softeval2 never sets instance variables o softeval2 { set zzz 1 set :z 1 ? {info exists G} 1 } ? {o eval {info exists :z}} 0 ? {o eval {info exists :zzz}} 0 ? {info vars ::zzz} ::zzz unset -nocomplain ::zzz ? {lsort [o info vars]} "a aaa b x" o destroy # now with an object namespace nx::Object create o o require namespace # objeval does an objcope, all vars are instance variables o objeval { set ccc 1 set :c 1 } ? {o eval {info exists :c}} 1 ? {o eval {info exists :ccc}} 1 # softeval behaves like the creation initcmd (just set dot vars) o softeval { set ddd 1 set :d 1 } ? {o eval {info exists :d}} 1 ? {o eval {info exists :ddd}} 0 # softeval2 never sets variables o softeval2 { set zzz 1 set :z 1 } ? {o eval {info exists :z}} 0 ? {o eval {info exists :zzz}} 0 ? {lsort [o info vars]} "c ccc d" o destroy ################################################################# # The same as above, but with some global vars. The global vars # should not influence the behavior on instance variables ################################################################# nx::test case with-global-vars foreach var {.x x xxx :a a aaa :b b bbb :c c ccc :d d ddd :z z zzz} {set $var 1} nx::Object create o { set xxx 1 set :x 1 } ? {o eval {info exists :x}} 1 ? {o eval {info exists :xxx}} 0 # objeval does an objcope, all vars are instance variables o objeval { set aaa 1 set :a 1 } ? {o eval {info exists :a}} 1 ? {o eval {info exists :aaa}} 1 # softeval should behave like the creation initcmd (just set dot vars) o softeval { set bbb 1 set :b 1 } ? {o eval {info exists :b}} 1 ? {o eval {info exists :bbb}} 0 # softeval2 never sets instance variables o softeval2 { set zzz 1 set :z 1 } ? {o eval {info exists :z}} 0 ? {o eval {info exists :zzz}} 0 ? {lsort [o info vars]} "a aaa b x" o destroy # now with namespace nx::Object create o o require namespace # eval does an objcope, all vars are instance variables o objeval { set ccc 1 set :c 1 } ? {o eval {info exists :c}} 1 ? {o eval {info exists :ccc}} 1 # softeval2 should behave like the creation initcmd (just set dot vars) o softeval { set ddd 1 set :d 1 } ? {o eval {info exists :d}} 1 ? {o eval {info exists :ddd}} 0 # softeval2 never sets variables o softeval2 { set zzz 1 set :z 1 } ? {o eval {info exists :z}} 0 ? {o eval {info exists :zzz}} 0 ? {lsort [o info vars]} "c ccc d" o destroy ################################################## # Test with proc scopes ################################################## nx::test case proc-scopes ::nsf::method::alias ::nx::Object objscoped-eval -frame object ::eval ::nsf::method::alias ::nx::Object nonleaf-eval -frame method ::eval ::nsf::method::alias ::nx::Object plain-eval ::eval proc foo-via-initcmd {} { foreach v {x xxx} {unset -nocomplain ::$v} set p 1 nx::Object create o { set xxx 1 set :x 1 set ::result G=[info exists G],p=[info exists p] } return [o eval {info exists :x}]-[o eval {info exists :xxx}]-[info exists x]-[info exists xxx]-[info exists ::x]-[info exists ::xxx]-$::result } proc foo {type} { foreach v {x xxx} {unset -nocomplain ::$v} set p 1 nx::Object create o o $type { set xxx 1 set :x 1 set ::result G=[info exists G],p=[info exists p] } return [o eval {info exists :x}]-[o eval {info exists :xxx}]-[info exists x]-[info exists xxx]-[info exists ::x]-[info exists ::xxx]-$::result } proc foo-tcl {what} { foreach v {x xxx} {unset -nocomplain ::$v} set p 1 set body { set xxx 1 set :x 1 set ::result G=[info exists G],p=[info exists p] } switch $what { eval {eval $body} ns-eval {namespace eval [namespace current] $body} } return [o eval {info exists :x}]-[o eval {info exists :xxx}]-[info exists x]-[info exists xxx]-[info exists ::x]-[info exists ::xxx]-$::result } set G 1 ? {foo-via-initcmd} 1-0-0-0-0-0-G=0,p=0 ? {foo nonleaf-eval} 1-0-0-0-0-0-G=0,p=0 ? {foo objscoped-eval} 1-1-0-0-0-0-G=0,p=0 ? {foo plain-eval} 0-0-0-1-0-0-G=0,p=1 ? {foo-tcl eval} 0-0-0-1-0-0-G=0,p=1 ? {foo-tcl ns-eval} 0-0-0-0-0-1-G=1,p=0 ################################################## # dotCmd tests ################################################## nx::test case dotcmd set C 0 proc bar {} {incr ::C} nx::Class create Foo { :method init {} {set :c 0} :method callDot1 {} {:bar} :method callDot2 {} {:bar} :method callDot3 {} {:bar; ::bar; :bar} :method bar {} {incr :c} } Foo create f1 f1 callDot1 ? {set ::C} 0 ? {f1 eval {set :c}} 1 # call via callback after 1 {f1 callDot2} after 10 {set ::X 1} vwait X # # Test vwait with colon variable and vwait method # nx::Object create o { set :x 0 :public object method foo {} {incr :x} :public object method vwait {varName} { if {[regexp {:[^:]*} $varName]} { error "invalid varName '$varName'; only plain or fully qualified variable names allowed" } if {[string match ::* $varName]} { ::vwait $varName } else { ::vwait :$varName } } # # Tcl vwait command with instance variable # after 10 {o foo} #puts stderr ===waiting vwait :x #puts stderr ===waiting-DONE # # vwait method # after 10 {o foo} #puts stderr ===waiting :vwait x #puts stderr ===waiting-DONE ? {o vwait :x} {invalid varName ':x'; only plain or fully qualified variable names allowed} } o destroy ? {set ::C} 0 ? {f1 eval {set :c}} 2 # call via callback, call :bar via .. from method after 1 {f1 callDot3} after 10 {set ::X 2} vwait X ? {set ::C} 1 ? {f1 eval {set :c}} 4 ################################################## # test for namespace resolver ################################################## nx::test case nsresolver namespace eval module { nx::Class create C nx::Class create M1 nx::Class create M2 C mixins set M1 ? {::nsf::relation::get C class-mixin} "::module::M1" C mixins add M2 ? {::nsf::relation::get C class-mixin} "::module::M2 ::module::M1" } ################################################## # test setting of instance variables for # objects with namespaces in and outside # of an eval (one case uses compiler) ################################################## nx::test case alias-dot-resolver-interp # outside of eval scope (interpreted) nx::Class create V { set :Z 1 set ZZZ 1 :method bar {z} { return $z } :object method bar {z} { return $z } :create v { set zzz 2 set :z 2 } } ? {lsort [V info vars]} {Z} ? {lsort [v info vars]} {z} # dot-resolver/ dot-dispatcher used in aliased proc nx::test case alias-dot-resolver { nx::Class create V { set :Z 1 set ZZZ 1 :method bar {z} { return $z } :object method bar {z} { return $z } :create v { set :z 2 set zzz 2 } } ? {lsort [V info vars]} {Z} ? {lsort [v info vars]} {z} } # # test [info vars] in eval method # nx::test case info-vars-in-eval { nx::Object create o ? {o eval { set x 1 expr {[info vars "x"] eq "x"} }} 1 } # # test for former crash when variable is used in connection with # prefixed variables # nx::test case tcl-variable-cmd { nx::Object create o { :public object method ? {varname} {info exists :$varname} :public object method bar args { variable :a set a 3 variable b set b 3 variable c 1 variable :d 1 :info vars } } ? {o bar} "" ? {o ? a} 0 ? {o ? b} 0 ? {o ? c} 0 ? {o ? d} 0 ? {lsort [o info vars]} "" o eval {set :a 1} ? {o ? a} 1 ? {lsort [o info vars]} a } nx::test case interactions { # SS: Adding an exemplary test destilled from the behaviour observed # for AOLserver vs. NaviServer when introspecting object variables # by means of the colon-resolver interface. It exemplifies the (by now # resolved for good) interactions between: (a) the compiling and # non-compiling var resolvers and (b) compiled and non-compiled # script execution nx::Object create ::o { :public object method bar {} { # 1. creates a proc-local, compiled var "type" set type 1 # 2. at compile time: create a proc-local, compiled link-var ":type" info exists :type # 3. at (unoptimised) interpretation time: bypasses compiled link-var # ":type" (invokeStr instruction; a simple eval), does a var # lookup with ":type", handled by InterpColonVarResolver(); # CompiledLocalsLookup() receives the var name (i.e., ":type") # and finds the proc-local compiled var ":type" (actually a link # variable to the actual/real object variable). eval {info exists :type}; # Note! A [info exists :type] would have been optimised on the # bytecode fastpath (i.e., existsScalar instruction) and would # use the compiled-local link-var ":type" directly (without # visiting InterpColonVarResolver()!) } } ? {o bar} 0 # # document compile-time var resolver side effects: link variables # # At compile time, the compile-time var resolver looks up (and # creates) object variables for the colon-prefixed vars processed: # ":u" -> "u", ":v" -> "v"; hence, the resolver always returns a # Var structure! As a consequence, the compiler emits # colon-prefixed *link* variables (either in state "undefined" or # "defined", depending on providing a value or not) into the # compiled local array (e.g., ":u"), as proxies pointing to the # actual object variables (e.g., "u"). # # Consequences: These link vars are visible through introspection # sensible to created vars (rather than defined/undefined var # states) in compiled scripts ([info vars] vs. [info locals]). This # resembles [upvar]-created local link vars, yet it does not # intuitively compare with the [set]/[unset] behaviour on # non-prefixed, ordinary variables from the angle of # introspection. Also, this constitutes an observable behavioural # difference between compiled and non-compiled scripts ... set script { # early probing: reflects the compiled-only, unexecuted state set _ [join [list {*}[lsort [info vars :*]] [info locals :*] \ [info exists :u] [::nsf::var::exists [::nsf::current] u] \ [info exists :v] [::nsf::var::exists [::nsf::current] v] \ [info exists :x] [::nsf::var::exists [::nsf::current] x]] "-"] catch {set :u} set :v 1 unset :x # late probing: reflects the (ideally) compiled, *executed* state append _ | [join [list {*}[lsort [info vars :*]] [info locals :*] \ [info exists :u] [::nsf::var::exists [::nsf::current] u] \ [info exists :v] [::nsf::var::exists [::nsf::current] v] \ [info exists :x] [::nsf::var::exists [::nsf::current] x]] "-"] return $_ } # compiled execution o public object method baz {} $script o eval {set :x 1; unset -nocomplain :v} ? {o baz} :u-:v-:x--0-0-0-0-1-1|:u-:v-:x--0-0-1-1-0-0 ; #:u-:v-:x--1-1-0-0-0-1-0-:u-:v-:x # non-compiled execution o eval {set :x 1; unset -nocomplain :v} ? [list o eval $script] -0-0-0-0-1-1|-0-0-1-1-0-0 # # testing interactions between the compile-time var resolver and ... # # ... [variable] # # background: the [variable] statement is compiled. During # compilation, our compile-time resolver is contacted, finds (and # eventually creates) an object variable "x". The compiler machinery # then creates a link-variable ":x" which is stored as a compiled # local, as usual. at the time of writing/testing, there are two # issues with this: # # ISSUE 1: In its non-compiled execution, [variable] sets the # AVOID_RESOLVERS flags, so our resolvers are not touched ... in its # compiled execution, AVOID_RESOLVERS is missing though (although # [variable] is compiled into a slow path execution, i.e., involves # a Tcl var lookup). Therefore, we get a link variable in the # compiled locals (and an undefined obj var). # this has some implications ... namespace eval ::ns1 { nx::Object create o { :public object method foo {} { set _ [join [list {*}[lsort [info vars :*]] [info locals :*] \ [info exists w] [::nsf::var::exists [::nsf::current] w] \ [info exists :x] [::nsf::var::exists [::nsf::current] x]] "-"] variable w; # -> intention: a variable "w" in the effective namespace (e.g., "::ns1::w") variable :x; # -> intention: a variable ":x" in the effective namespace (e.g., "::ns1:::x"!). append _ | [join [list {*}[lsort [info vars :*]] [info locals :*] \ [info exists w] [::nsf::var::exists [::nsf::current] w] \ [info exists :x] [::nsf::var::exists [::nsf::current] x]] "-"] return $_ } } ? {::ns1::o foo} ":x--0-0-0-0|:x--0-0-0-0" o eval { :public object method faz {} { set _ [join [list {*}[lsort [info vars :*]] [info locals :*] \ [namespace which -variable [namespace current]::w] \ [info exists [namespace current]::w] \ [info exists w] [::nsf::var::exists [::nsf::current] w] \ [namespace which -variable [namespace current]:::x] \ [info exists [namespace current]:::x] \ [info exists :x] [::nsf::var::exists [::nsf::current] x]] "-"] variable w 1; # -> intention: a variable "w" in the effective namespace (e.g., "::ns1::w") variable :x 2; # -> intention: a variable ":x" in the effective namespace (e.g., "::ns1:::x"!). append _ | [join [list {*}[lsort [info vars :*]] [info locals :*] \ [namespace which -variable [namespace current]::w] \ [info exists [namespace current]::w] \ [info exists w] [::nsf::var::exists [::nsf::current] w] \ [namespace which -variable [namespace current]:::x] \ [info exists [namespace current]:::x] [namespace eval [namespace current] {info exists :x}] \ [namespace eval [namespace current] {variable :x; info exists :x}] \ [info exists :x] [::nsf::var::exists [::nsf::current] x]] "-"] append _ | [join [list [expr {$w eq [namespace eval [namespace current] {variable w; set w}]}] \ [expr {${:x} eq [namespace eval [namespace current] {variable w; set :x}]}]] -] return $_ } } ? {::ns1::o faz} ":x--::ns1::w-0-0-0--0-0-0|:x--::ns1::w-1-1-0--0-1-1-1-0|1-1" # # ISSUE 2: Colon-prefixed variables become represented by linked # variables in the compiled local arrays during # compilation. However, linked variables are mutable (in contrast # to proc-local variables), that is, they can be changed to point # to another target variable. This target switch currently happens # between object variables and [variable] links which (due to # executing the compile-time var resolver because of lacking # AVOID_RESOLVERS) emits a "replacing" link var # # In the example below, there won't be an error exception # 'variable ":aaa" already exists', because ":aaa" is resolved on # the fly to "::ns1::o1.aaa" in a non-compiled execution and in a # compiled situation, the compiled-local link variable ":aaa" is # simply cleared and recreated to proxy a namespace variable. o eval { set :aaa 1 :public object method caz {} { set _ "[info exists :aaa]-${:aaa}-[set :aaa]" variable :aaa append _ "-[info exists :aaa]" set :aaa 2 append _ "-${:aaa}-[set :aaa]-[namespace eval [namespace current] {variable :aaa; set :aaa}]" unset :aaa append _ "-[info exists :aaa]-[namespace which -variable [namespace current]:::aaa]-[::nsf::var::exists [current] aaa]-[[current] eval {set :aaa}]" return $_ } } ? {::ns1::o caz} "1-1-1-0-2-2-2-0--1-1" # # In non-compiled executions, there is another form of interaction # between our var resolvers and [variable] in the sense of # switching references. A [variable] statement is then handled by # Tcl_VariableObjCmd() directly, our compile-time resolver is # never called, hence, no link variables are created. The # non-compiling resolver InterpColonVarResolver() is called to # duty from within Tcl_VariableObjCmd(), however, it fast-forwards # by signalling TCL_CONTINUE as [variable] requests # TCL_NAMESPACE_ONLY explicitly. # # While [variable] creates a local link var ":aaa", any later # referencing of :aaa is intercepted by InterpColonVarResolver() # and resolved to the obj var "aaa". The effects of this # interaction are probably counter-intuitive to standard # [variable] behaviour. # # 1. There will not be a 'variable ":aaa" already exists' to # signal a naming conflict in the local naming scope, because the # symbolic name ":aaa" in a [set :aaa 1] and in a [variable :aaa # 1] is resolved differently (see above). # # 2. There is no way to refer to the local link var ":aaa" created # by [variable] in subsequent calls because the name will resolve # to an obj var "aaa". By calling [variable] in its setting mode, # you can still set namespace var values. ? {::ns1::o eval { set _ "[info exists :aaa]-${:aaa}-[set :aaa]" variable :aaa append _ "-[info exists :aaa]" set :aaa 2 append _ "-${:aaa}-[set :aaa]-[[current] eval {set :aaa}]-[namespace eval [namespace current] {variable :aaa; info exists :aaa}]" variable :aaa 5 unset :aaa append _ "-[info exists :aaa]-[namespace which -variable [namespace current]:::aaa]-[::nsf::var::exists [current] aaa]-[namespace eval [namespace current] {variable :aaa; info exists :aaa}]-[namespace eval [namespace current] {variable :aaa; set :aaa}]" return $_ }} "1-1-1-1-2-2-2-0-0--0-1-5" # ... [upvar] # # Exhibits the same interactions as [variable] due to creating # link variables by the compiling var resolver, namely the context # switching and effective disabling of the colon-prefixed # accessing of object state ... # nx::Object create p { :public object method foo {var} { set :x XXX set _ ${:x} upvar $var :x append _ -[join [list ${:x} [set :x] {*}[info vars :*] {*}[:info vars] \ [info exists :x] \ [[current] eval {info exists :x}]] "-"] unset :x append _ -[join [list {*}[info vars :*] {*}[:info vars] \ [info exists :x] [[current] eval {info exists :x}] \ [[current] eval {set :x}]] "-"] } :object method bar {var1 var2 var3 var4 var5 var6} { upvar $var1 xx $var2 :yy $var3 :zz $var4 q $var5 :el1 $var6 :el2 set _ [join [list {*}[lsort [:info vars]] {*}[lsort [info vars :*]] \ [info exists xx] $xx \ [info exists :yy] ${:yy} \ [info exists :zz] ${:zz} \ [info exists q] [[current] eval {info exists :q}]] -] incr :yy incr xx incr :zz incr q incr :el1 incr :el2 return $_ } :public object method baz {} { set :x 10 set y 20 set :z 30 unset -nocomplain :q set :arr(a) 40 set _ [:bar :x y :z :q :arr(a) :arr(b)] append _ -[join [list ${:x} $y ${:z} ${:q} [set :arr(a)] [set :arr(b)] [:info vars q]] -] } } ? {set y 1; p foo y} "XXX-1-1-:x-x-1-1-:x-x-0-1-XXX" ? {p baz} "arr-x-z-:el1-:el2-:yy-:zz-1-10-1-20-1-30-0-0-11-21-31-1-41-1-q" # # ... [namespace which] # # Similar to the compiled, slow-path [variable] instructions, # [namespace which] as implemented by NamespaceWhichCmd() in # tclNamesp.c lacks AVOID_RESOLVERS. Therefore, we end up in our # var resolver which resolves colon-prefixed vars to object # variables. Also, NamespaceWhichCmd() does not set any other # var-resolution flags (TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY) as # this would defeat its purpose. Anywyays, our resolver is # therefore completely blind when handling calls from [namespace # which]. # # This leads to the unexpected behaviour in the test below: # [namespace which -variable :XXX] != [namespace which -variable # [namespace current]:::XXX] o eval { :public object method bar {} { set :XXX 1 return [join [list ${:XXX} [set :XXX] [namespace which -variable :XXX] \ [namespace which -variable [namespace current]:::XXX]] -] } } ? {::ns1::o bar} "1-1-:XXX-" } } nx::test case after-next { # # This test case tests, whether we can use e..g an instvar in an # mixin method after the next. In such cases, the frame flags have # to be altered from inactive mixin to active mixin (otherwise the # actual frame would be skipped in useActiveFrames below in :instvar). # nx::Class create FormPage { :property package_id :public method initialize_loaded_object {} {;} } nx::Class create WorkflowPage { :alias instvar :::nsf::methods::object::instvar :public method initialize_loaded_object {} { next :instvar package_id return $package_id } FormPage mixins add WorkflowPage FormPage create p1 -package_id 123 ? {p1 initialize_loaded_object} 123 } } nx::test case unconfigured-varresolver { # # Test robustness of varresolver for unconfigured objects # nx::Class create O { :public method configure args {;} :create ateh } ? {ateh eval {info exists :x}} 0 } # # Test variable resolver in respect to uplevel and apply # (lambda frames) # ::nx::test case var-resolver-uplevel-apply { nx::Object create o1 { set :a o1.a :public object method foo {} {return ${:a}} :public object method bar {} {o2 foo} :public object method bar2 {} {o2 foo2} } nx::Object create o2 { set :a o2.a set :cmd {set :a} :public object method foo {} {eval ${:cmd}} :public object method foo2 {} {uplevel ${:cmd}} :public object method foo3 {} {uplevel 2 ${:cmd}} :public object method bar {} {:foo} :public object method bar2 {} {:foo2} :public object method bar3a {} {:foo3} :public object method bar3 {} {:bar3a} } # # test cases for uplevel over multiple levels in the same object # ? {o2 foo} o2.a ? {o2 bar} o2.a ? {o2 bar2} o2.a ? {o2 bar3} o2.a # # test cases for uplevel over multiple levels in different objects # ? {o1 foo} o1.a ? {o1 bar} o2.a ? {o1 bar2} o1.a # # test cases for uplevel over apply # proc x {cmd} {uplevel $cmd} nx::Object create o3 { set :a o3.a :public object method set {var} {set :$var} :public object method foo-m {} {:set a} :public object method foo-r {} {::set :a} :public object method foo-m-u {} {x {:set a}} :public object method foo-r-u {} {x {::set :a}} :public object method foo-a-m {} {::apply [list {} {:set a} [self]]} :public object method foo-a-r {} {::apply [list {} {::set :a} [self]]} :public object method foo-a-m-u {} {::apply [list {} {x {:set a}} [self]]} :public object method foo-a-r-u {} {::apply [list {} {x {::set :a}} [self]]} } # # resolver and method should behave the same # ? {o3 foo-m} "o3.a" ? {o3 foo-r} "o3.a" # # resolver and method should behave the same, also when uplevel is used # ? {o3 foo-m-u} "o3.a" ? {o3 foo-r-u} "o3.a" # # resolver and method should behave the same, also when apply is used # ? {o3 foo-a-m} "o3.a" ? {o3 foo-a-r} "o3.a" # # resolver and method should behave the same, also when apply and # uplevel are used # ? {o3 foo-a-m-u} "o3.a" ? {o3 foo-a-r-u} "o3.a" } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: tests/volatile.test000066400000000000000000000041671242365656200147630ustar00rootroot00000000000000# -*- Tcl -*- package req nx::test package prefer latest package req XOTcl 2.0 package req nx::volatile ::nsf::method::require ::nx::Object volatile # # Wrapper to call a command in a proc/method # proc bar {args} { set c [{*}$args] set empty [expr {[info command $c] eq ""}] ? [list set _ $empty] 0 "bar: $c destroyed too early" return $c } # # Create NX objects with volatile through the wrapper # proc foon {} { #puts stderr ====2 set c [bar C create c1 -volatile {:object method destroy {} {#puts "[self] destroy";next}}] ? [list info command $c] "" "foon: $c destroyed too late" #puts stderr ====3 set c [bar C new -volatile {:object method destroy {} {#puts "[self] destroy";next}}] ? [list info command $c] "" "foon: $c destroyed too late" } # # Create XOTcl objects with volatile through the wrapper # proc foox {} { #puts stderr ====1 set c [bar XC c1 -volatile -proc destroy {} {#puts "[self] destroy";next}] ? [list info command $c] "" "foox: $c destroyed too late" #puts stderr ====2 set c [bar XC create c1 -volatile -proc destroy {} {#puts "[self] destroy";next}] ? [list info command $c] "" "foox: $c destroyed too late" #puts stderr ====3 set c [bar XC new -volatile -proc destroy {} {#puts "[self] destroy";next}] ? [list info command $c] "" "foox: $c destroyed too late" } # # Producer classes in NX and XOTcl # ::nx::Class create C ::xotcl::Class create XC # # Create a NX class using foox, foon, and bar as methods # nx::Class create D { # # call volatile in nsf method bar # :method bar {args} [info body ::bar] :public method foox {} [string map [list bar :bar] [info body ::foox]] :public method foon {} [string map [list bar :bar] [info body ::foon]] # # call volatile in tcl proc bar # :public method foox2 {} [info body ::foox] :public method foon2 {} [info body ::foon] } D create d1 nx::test case methods-methods { d1 foox d1 foon } nx::test case methods-procs { d1 foox2 d1 foon2 } # # Call just in tcl procs # nx::test case procs-procs { ::foox ::foon } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: unix/000077500000000000000000000000001242365656200120545ustar00rootroot00000000000000unix/pkgIndex.unix.in000066400000000000000000000001311242365656200151320ustar00rootroot00000000000000package ifneeded nsf @PACKAGE_VERSION@ [list load [file join $dir @PKG_LIB_FILE@] nsf] win/000077500000000000000000000000001242365656200116665ustar00rootroot00000000000000win/makefile.vc000066400000000000000000000455331242365656200140070ustar00rootroot00000000000000#------------------------------------------------------------- -*- makefile -*- # makefile.vc -- # # Microsoft Visual C++ makefile for use with nmake.exe v1.62+ # (VC++ 5.0+, MVSVC 11, MVSVC 12) # # This makefile is based upon the Tcl 8.6 Makefile.vc and modified to # make it suitable as an NSF package makefile. # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # # Copyright (c) 1995-1996 Sun Microsystems, Inc. # Copyright (c) 1998-2000 Ajuba Solutions. # Copyright (c) 2001-2005 ActiveState Corporation. # Copyright (c) 2001-2004 David Gravereaux. # Copyright (c) 2003-2008 Pat Thoyts. # Copyright (c) 2013-2014 Stefan Sobernig #------------------------------------------------------------------------------ # Check to see we are configured to build with MSVC (MSDEVDIR, MSVCDIR or # VCINSTALLDIR) or with the MS Platform SDK (MSSDK or WindowsSDKDir) !if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(VCINSTALLDIR) && !defined(MSSDK) && !defined(WINDOWSSDKDIR) MSG = ^ You need to run vcvars32.bat from Developer Studio or setenv.bat from the^ Platform SDK first to setup the environment. Jump to this line to read^ the build instructions. !error $(MSG) !endif #------------------------------------------------------------------------------ # HOW TO USE this makefile: # # 1) It is now necessary to have MSVCDir, MSDevDir or MSSDK set in the # environment. This is used as a check to see if vcvars32.bat had been # run prior to running nmake or during the installation of Microsoft # Visual C++, MSVCDir had been set globally and the PATH adjusted. # Either way is valid. # # You'll need to run vcvars32.bat contained in the MsDev's vc(98)/bin # directory to setup the proper environment, if needed, for your # current setup. This is a needed bootstrap requirement and allows the # swapping of different environments to be easier. # # 2) To use the Platform SDK (not expressly needed), run setenv.bat after # vcvars32.bat according to the instructions for it. This can also # turn on the 64-bit compiler, if your SDK has it. # # 3) Targets are: # all -- Builds everything. # -- Builds the project (eg: nmake sample) # test -- Builds and runs the test suite. # install -- Installs the built binaries and libraries to $(INSTALLDIR) # in an appropriate subdirectory. # clean/realclean/distclean -- varying levels of cleaning. # # 4) Macros usable on the commandline: # INSTALLDIR= # Sets where to install Tcl from the built binaries. # C:\Progra~1\Tcl is assumed when not specified. # # OPTS=loimpact,msvcrt,nothreads,pdbs,profile,static,symbols,unchecked,none # Sets special options for the core. The default is for none. # Any combination of the above may be used (comma separated). # 'none' will over-ride everything to nothing. # # loimpact = Adds a flag for how NT treats the heap to keep memory # in use, low. This is said to impact alloc performance. # msvcrt = Affects the static option only to switch it from # using libcmt(d) as the C runtime [by default] to # msvcrt(d). This is useful for static embedding # support. # nothreads = Turns off multithreading support (not recommended) # static = Builds a static library of the core instead of a # dll. The shell will be static (and large), as well. # pdbs = Build detached symbols for release builds. # profile = Adds profiling hooks. Map file is assumed. # symbols = Debug build. Links to the debug C runtime, disables # optimizations and creates pdb symbols files. # unchecked= Allows a symbols build to not use the debug # enabled runtime (msvcrt.dll not msvcrtd.dll # or libcmt.lib not libcmtd.lib). # # STATS=compdbg,memdbg,none # Sets optional memory and bytecode compiler debugging code added # to the core. The default is for none. Any combination of the # above may be used (comma separated). 'none' will over-ride # everything to nothing. # # compdbg = Enables byte compilation logging. # memdbg = Enables the debugging memory allocator. # # CHECKS=64bit,fullwarn,nodep,none # Sets special macros for checking compatability. # # 64bit = Enable 64bit portability warnings (if available) # fullwarn = Builds with full compiler and link warnings enabled. # Very verbose. # nodep = Turns off compatability macros to ensure the extension # isn't being built with deprecated functions. # # MACHINE=(ALPHA|AMD64|IA64|IX86) # Set the machine type used for the compiler, linker, and # resource compiler. This hook is needed to tell the tools # when alternate platforms are requested. IX86 is the default # when not specified. If the CPU environment variable has been # set (ie: recent Platform SDK) then MACHINE is set from CPU. # # TMP_DIR= # OUT_DIR= # Hooks to allow the intermediate and output directories to be # changed. $(OUT_DIR) is assumed to be # $(BINROOT)\(Release|Debug) based on if symbols are requested. # $(TMP_DIR) will de $(OUT_DIR)\ by default. # # TESTPAT= # Reads the tests requested to be run from this file. # # 5) Examples: # # Basic syntax of calling nmake looks like this: # nmake [-nologo] -f makefile.vc [target|macrodef [target|macrodef] [...]] # # Using VC++: # # Standard (in cmd.exe, no frills, x86) # c:\nsf_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat # Setting environment for using Microsoft Visual C++ tools. # c:\nsf_src\win\>nmake -f makefile.vc TCLDIR=c:\tcl_src\ # c:\nsf_src\win\>nmake -f makefile.vc install INSTALLDIR=c:\progra~1\tcl # # Building for Win64 (IA64) # c:\nsf_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat # Setting environment for using Microsoft Visual C++ tools. # c:\nsf_src\win\>c:\progra~1\platfo~1\setenv.bat /pre64 /RETAIL # Targeting Windows pre64 RETAIL # c:\nsf_src\win\>nmake -f makefile.vc MACHINE=IA64 # # Using Microsoft Visual Compiler 11+: # see also http://msdn.microsoft.com/en-us/library/vstudio/x4d2c09s.aspx # # Standard (in cmd.exe, no frills, x86) # c:\nsf_src\win>"c:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" x86 # c:\nsf_src\win\>nmake -f makefile.vc all TCLDIR=c:\tcl_src\ MACHINE=X86 # c:\nsf_src\win\>nmake -f makefile.vc install INSTALLDIR=c:\progra~1\tcl # # Building for Win64 (AMD64) # c:\nsf_src\win>"c:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" amd64 # c:\nsf_src\win\>nmake -f makefile.vc all TCLDIR=c:\tcl_src\ MACHINE=AMD64 # c:\nsf_src\win\>nmake -f makefile.vc install INSTALLDIR=c:\progra~1\tcl # #------------------------------------------------------------------------------ #============================================================================== #------------------------------------------------------------------------------ !if !exist("makefile.vc") MSG = ^ You must run this makefile only from the directory it is in.^ Please `cd` to its location first. !error $(MSG) !endif #------------------------------------------------------------------------- # Project specific information (EDIT) # # You should edit this with the name and version of your project. This # information is used to generate the name of the package library and # it's install location. # # For example, the sample extension is going to build sample05.dll and # would install it into $(INSTALLDIR)\lib\sample05 # # You need to specify the object files that need to be linked into your # binary here. # #------------------------------------------------------------------------- PROJECT = nsf # Uncomment the following line if this is a Tk extension. #PROJECT_REQUIRES_TK=1 !include "rules.vc" !include "pkg.vc" DOTVERSION = $(PACKAGE_VERSION:"=) #" VERSION = $(PACKAGE_MAJOR)$(PACKAGE_MINOR) STUBPREFIX = $(PROJECT)stub PRJ_CFLAGS = -DNSF_VERSION=\"$(DOTVERSION)\" -DNSF_PATCHLEVEL=\"$(PACKAGE_VERSION)\" DLLOBJS = \ $(TMP_DIR)\nsf.obj \ $(TMP_DIR)\nsfError.obj \ $(TMP_DIR)\nsfObjectData.obj \ $(TMP_DIR)\nsfProfile.obj \ $(TMP_DIR)\nsfDebug.obj \ $(TMP_DIR)\nsfUtil.obj \ $(TMP_DIR)\nsfObj.obj \ $(TMP_DIR)\nsfPointer.obj \ $(TMP_DIR)\nsfShadow.obj \ $(TMP_DIR)\nsfCompile.obj \ $(TMP_DIR)\aolstub.obj \ $(TMP_DIR)\nsfStubInit.obj \ $(TMP_DIR)\nsfEnumerationType.obj \ $(TMP_DIR)\nsfCmdDefinitions.obj PRJTESTS = \ $(ROOT)\tests\object-system.test \ $(ROOT)\tests\alias.test \ $(ROOT)\tests\returns.test \ $(ROOT)\tests\cget.test \ $(ROOT)\tests\method-parameter.test \ $(ROOT)\tests\serialize.test \ $(ROOT)\tests\contains.test \ $(ROOT)\tests\method-require.test \ $(ROOT)\tests\submethods.test \ $(ROOT)\tests\destroy.test \ $(ROOT)\tests\methods.test \ $(ROOT)\tests\disposition.test \ $(ROOT)\tests\mixinof.test \ $(ROOT)\tests\tcl86.test \ $(ROOT)\tests\tcloo.test \ $(ROOT)\tests\forward.test \ $(ROOT)\tests\var-access.test \ $(ROOT)\tests\info-method.test \ $(ROOT)\tests\varresolution.test \ $(ROOT)\tests\interceptor-slot.test\ $(ROOT)\tests\properties.test \ $(ROOT)\tests\volatile.test \ $(ROOT)\tests\interp.test \ $(ROOT)\tests\protected.test \ $(ROOT)\tests\parameters.test \ $(ROOT)\tests\plain-object-method.test \ $(ROOT)\tests\class-method.test \ $(ROOT)\tests\nsf-cmd.test \ $(ROOT)\tests\accessor.test \ $(ROOT)\tests\linearization.test \ $(ROOT)\tests\traits.test PRJHEADERS = #------------------------------------------------------------------------- # Target names and paths ( shouldn't need changing ) #------------------------------------------------------------------------- BINROOT = . ROOT = .. PRJIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib PRJLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT) PRJLIB = $(OUT_DIR)\$(PRJLIBNAME) PRJSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib PRJSTUBLIB = $(OUT_DIR)\$(PRJSTUBLIBNAME) ### Make sure we use backslash only. PRJ_INSTALL_DIR = $(_INSTALLDIR)\$(PROJECT)$(DOTVERSION) LIB_INSTALL_DIR = $(PRJ_INSTALL_DIR) BIN_INSTALL_DIR = $(PRJ_INSTALL_DIR) DOC_INSTALL_DIR = $(PRJ_INSTALL_DIR) SCRIPT_INSTALL_DIR = $(PRJ_INSTALL_DIR) INCLUDE_INSTALL_DIR = $(_TCLDIR)\include ### The following paths CANNOT have spaces in them. GENERICDIR = $(ROOT)\generic WINDIR = $(ROOT)\win LIBDIR = $(ROOT)\library DOCDIR = $(ROOT)\doc COMPATDIR = $(ROOT)\compat UTILDIR = $(ROOT)\apps\utils STUBSDIR = $(GENERICDIR)\stubs$(TCL_DOTVERSION) #--------------------------------------------------------------------- # Compile flags #--------------------------------------------------------------------- !if !$(DEBUG) !if $(OPTIMIZING) ### This cranks the optimization level to maximize speed cdebug = $(OPTIMIZATIONS) !else cdebug = !endif !else if "$(MACHINE)" == "IA64" ### Warnings are too many, can't support warnings into errors. cdebug = -Zi -Od $(DEBUGFLAGS) !else cdebug = -Zi -WX $(DEBUGFLAGS) !endif ### Declarations common to all compiler options cwarn = $(WARNINGS) -D _CRT_SECURE_NO_DEPRECATE -D _CRT_NONSTDC_NO_DEPRECATE cflags = -nologo -c $(COMPILERFLAGS) -DBUILD_$(PROJECT) $(cwarn) -Fp$(TMP_DIR)^\ !if $(MSVCRT) !if $(DEBUG) && !$(UNCHECKED) crt = -MDd !else crt = -MD !endif !else !if $(DEBUG) && !$(UNCHECKED) crt = -MTd !else crt = -MT !endif !endif cflags = $(cflags) -DMODULE_SCOPE=extern !if !$(STATIC_BUILD) cflags = $(cflags) -DUSE_TCL_STUBS !if defined(TKSTUBLIB) cflags = $(cflags) -DUSE_TK_STUBS !endif !endif INCLUDES = $(TCL_INCLUDES) -I"$(WINDIR)" -I"$(GENERICDIR)" BASE_CFLAGS = $(cflags) $(cdebug) $(crt) $(INCLUDES) CON_CFLAGS = $(cflags) $(cdebug) $(crt) -DCONSOLE TCL_CFLAGS = -DPACKAGE_NAME="\"$(PROJECT)\"" \ -DPACKAGE_VERSION="\"$(DOTVERSION)\"" \ -DBUILD_$(PROJECT) \ $(BASE_CFLAGS) $(OPTDEFINES) $(PRJ_CFLAGS) #--------------------------------------------------------------------- # Link flags #--------------------------------------------------------------------- !if $(DEBUG) ldebug = -debug -debugtype:cv !if $(MSVCRT) ldebug = $(ldebug) -nodefaultlib:msvcrt !endif !else ldebug = -release -opt:ref -opt:icf,3 !endif ### Declarations common to all linker options lflags = -nologo -machine:$(MACHINE) $(LINKERFLAGS) $(ldebug) !if $(PROFILE) lflags = $(lflags) -profile !endif !if $(ALIGN98_HACK) && !$(STATIC_BUILD) ### Align sections for PE size savings. lflags = $(lflags) -opt:nowin98 !else if !$(ALIGN98_HACK) && $(STATIC_BUILD) ### Align sections for speed in loading by choosing the virtual page size. lflags = $(lflags) -align:4096 !endif !if $(LOIMPACT) lflags = $(lflags) -ws:aggressive !endif dlllflags = $(lflags) -dll conlflags = $(lflags) -subsystem:console guilflags = $(lflags) -subsystem:windows !if !$(STATIC_BUILD) baselibs = $(TCLSTUBLIB) !if defined(TKSTUBLIB) baselibs = $(baselibs) $(TKSTUBLIB) !endif !endif # Avoid 'unresolved external symbol __security_cookie' errors. # c.f. http://support.microsoft.com/?id=894573 !if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64" !if $(VCVERSION) > 1399 && $(VCVERSION) < 1500 baselibs = $(baselibs) bufferoverflowU.lib !endif !endif baselibs = $(baselibs) user32.lib gdi32.lib #--------------------------------------------------------------------- # TclTest flags #--------------------------------------------------------------------- !if "$(TESTPAT)" != "" TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT) !endif #--------------------------------------------------------------------- # Project specific targets (EDIT) #--------------------------------------------------------------------- all: setup $(PROJECT) $(PROJECT): setup pkgIndex $(PRJLIB) install: install-binaries install-libraries install-docs pkgIndex: $(OUT_DIR)\pkgIndex.tcl test: setup $(PROJECT) @set TCL_LIBRARY=$(TCL_LIBRARY:\=/) @set TCLLIBPATH=$(OUT_DIR_PATH:\=/) !if $(TCLINSTALL) @set PATH=$(_TCLDIR)\bin;$(PATH) !else @set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH) !endif @echo lappend auto_path $(ROOT) .; puts $$auto_path; set argv [lassign $$argv __src]; source $$__src; > nsftest.tcl !if "$(OS)" == "Windows_NT" || "$(MSVCDIR)" == "IDE" for %i in ($(PRJTESTS)) do \ $(DEBUGGER) $(TCLSH) nsftest.tcl %i -testlog __test.log !else @echo Please wait while the tests are collected... for %i in ($(PRJTESTS)) do \ $(DEBUGGER) $(TCLSH) nsftest.tcl %i -testlog __test.log >> tests.log type tests.log | more !endif @$(TCLSH) nsftest.tcl "$(ROOT)/tests/summary.tcl" -testlog __test.log @del nsftest.tcl @del __test.log shell: setup $(PROJECT) @set VLERQ_LIBRARY=$(LIBDIR:\=/) @set TCL_LIBRARY=$(TCL_LIBRARY:\=/) @set TCLLIBPATH=$(OUT_DIR_PATH:\=/) !if $(TCLINSTALL) @set PATH=$(_TCLDIR)\bin;$(PATH) !else @set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH) !endif $(DEBUGGER) $(TCLSH) $(SCRIPT) setup: @if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR) @if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR) # See /win/coffbase.txt for extension base addresses. $(PRJLIB): $(DLLOBJS) !if $(STATIC_BUILD) $(lib32) -nologo -out:$@ @<< $** << !else $(link32) $(dlllflags) -base:@$(COFFBASE),thread -out:$@ $(baselibs) @<< $** << $(_VC_MANIFEST_EMBED_DLL) -@del $*.exp !endif $(PRJSTUBLIB): $(PRJSTUBOBJS) $(lib32) -nologo -out:$@ $(PRJSTUBOBJS) #--------------------------------------------------------------------- # Implicit rules #--------------------------------------------------------------------- {$(WINDIR)}.c{$(TMP_DIR)}.obj:: $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<< $< << {$(GENERICDIR)}.c{$(TMP_DIR)}.obj:: $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<< $< << {$(STUBSDIR)}.c{$(TMP_DIR)}.obj:: $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<< $< << {$(COMPATDIR)}.c{$(TMP_DIR)}.obj:: $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<< $< << {$(WINDIR)}.rc{$(TMP_DIR)}.res: $(rc32) -fo $@ -r -i "$(GENERICDIR)" -D__WIN32__ \ -DCOMMAVERSION=$(DOTVERSION:.=,),0 \ -DDOTVERSION=\"$(DOTVERSION)\" \ -DVERSION=\"$(VERSION)$(SUFX)\" \ !if $(DEBUG) -d DEBUG \ !endif !if $(TCL_THREADS) -d TCL_THREADS \ !endif !if $(STATIC_BUILD) -d STATIC_BUILD \ !endif $< .SUFFIXES: .SUFFIXES:.c .rc #------------------------------------------------------------------------- # Explicit dependency rules # #------------------------------------------------------------------------- $(OUT_DIR)\pkgIndex.tcl: @echo package ifneeded $(PROJECT) $(DOTVERSION) \ [list load [file join $$dir $(PRJLIBNAME)] $(PROJECT)] > $@ @for /d %d in ($(LIBDIR)\*) do \ @pushd %d &\ @$(TCLSH) $(MAKEDIR)\$(LIBDIR)\lib\mkIndex.tcl -dir $(MAKEDIR)\$(LIBDIR) &\ @popd #--------------------------------------------------------------------- # Installation. (EDIT) # # You may need to modify this section to reflect the final distribution # of your files and possibly to generate documentation. # #--------------------------------------------------------------------- install-binaries: @echo Installing binaries to '$(SCRIPT_INSTALL_DIR)' @if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)" @$(CPY) $(PRJLIB) "$(SCRIPT_INSTALL_DIR)" >NUL @echo Installing header files to '$(INCLUDE_INSTALL_DIR)\' @$(CPY) "$(GENERICDIR)\nsf.h" "$(INCLUDE_INSTALL_DIR)\" @$(CPY) "$(GENERICDIR)\nsfDecls.h" "$(INCLUDE_INSTALL_DIR)\" @echo Installing package index in '$(SCRIPT_INSTALL_DIR)' @$(CPY) $(OUT_DIR)\pkgIndex.tcl "$(SCRIPT_INSTALL_DIR)" install-libraries: @echo Installing libraries to '$(SCRIPT_INSTALL_DIR)' @echo Installing NX @if not exist "$(SCRIPT_INSTALL_DIR)"\nx mkdir "$(SCRIPT_INSTALL_DIR)"\nx @$(CPY) $(LIBDIR)\nx\*.tcl "$(SCRIPT_INSTALL_DIR)"\nx @echo Installing serialize @if not exist "$(SCRIPT_INSTALL_DIR)\serialize" mkdir "$(SCRIPT_INSTALL_DIR)\serialize" @$(CPY) $(LIBDIR)\serialize\*.tcl "$(SCRIPT_INSTALL_DIR)\serialize" @echo Installing XOTcl2 @if not exist "$(SCRIPT_INSTALL_DIR)\xotcl" mkdir "$(SCRIPT_INSTALL_DIR)\xotcl" @$(CPY) $(LIBDIR)\xotcl\apps "$(SCRIPT_INSTALL_DIR)\xotcl\apps" /e @$(CPY) $(LIBDIR)\xotcl\library "$(SCRIPT_INSTALL_DIR)\xotcl\library" /e # @$(CPY) $(LIBDIR)\xotcl\xo* "$(SCRIPT_INSTALL_DIR)\xotcl" @echo Installing NX libs @if not exist "$(SCRIPT_INSTALL_DIR)\lib" mkdir "$(SCRIPT_INSTALL_DIR)\lib" @$(CPY) $(LIBDIR)\lib\*.tcl "$(SCRIPT_INSTALL_DIR)\lib" @type "$(LIBDIR)\pkgIndex.tcl" >> "$(SCRIPT_INSTALL_DIR)"\pkgIndex.tcl install-docs: @echo Installing documentation files to '$(DOC_INSTALL_DIR)' # @if exist $(DOCDIR) $(CPY) $(DOCDIR)\*.n "$(DOC_INSTALL_DIR)" #--------------------------------------------------------------------- # Clean up #--------------------------------------------------------------------- clean: @if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR) @if exist $(WINDIR)\version.vc del $(WINDIR)\version.vc @if exist $(WINDIR)\versions.vc del $(WINDIR)\versions.vc @if exist $(WINDIR)\vercl.i del $(WINDIR)\vercl.i @if exist $(WINDIR)\vercl.x del $(WINDIR)\vercl.x @if exist $(WINDIR)\_junk.pch del $(WINDIR)\_junk.pch realclean: clean @if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR) distclean: realclean @if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe @if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj win/nmakehlp.c000066400000000000000000000421211242365656200136310ustar00rootroot00000000000000/* * ---------------------------------------------------------------------------- * nmakehlp.c -- * * This is used to fix limitations within nmake and the environment. * * Copyright (c) 2002 by David Gravereaux. * Copyright (c) 2006 by Pat Thoyts * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * ---------------------------------------------------------------------------- */ #define _CRT_SECURE_NO_DEPRECATE #include #define NO_SHLWAPI_GDI #define NO_SHLWAPI_STREAM #define NO_SHLWAPI_REG #include #pragma comment (lib, "user32.lib") #pragma comment (lib, "kernel32.lib") #pragma comment (lib, "shlwapi.lib") #include #include /* * This library is required for x64 builds with _some_ versions of MSVC */ #if defined(_M_IA64) || defined(_M_AMD64) #if _MSC_VER >= 1400 && _MSC_VER < 1500 #pragma comment(lib, "bufferoverflowU") #endif #endif /* ISO hack for dumb VC++ */ #ifdef _MSC_VER #define snprintf _snprintf #endif /* protos */ static int CheckForCompilerFeature(const char *option); static int CheckForLinkerFeature(const char *option); static int IsIn(const char *string, const char *substring); static int SubstituteFile(const char *substs, const char *filename); static int QualifyPath(const char *path); static const char *GetVersionFromFile(const char *filename, const char *match, int numdots); static DWORD WINAPI ReadFromPipe(LPVOID args); /* globals */ #define CHUNK 25 #define STATICBUFFERSIZE 1000 typedef struct { HANDLE pipe; char buffer[STATICBUFFERSIZE]; } pipeinfo; pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'}; pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'}; /* * exitcodes: 0 == no, 1 == yes, 2 == error */ int main( int argc, char *argv[]) { char msg[300]; DWORD dwWritten; int chars; /* * Make sure children (cl.exe and link.exe) are kept quiet. */ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); /* * Make sure the compiler and linker aren't effected by the outside world. */ SetEnvironmentVariable("CL", ""); SetEnvironmentVariable("LINK", ""); if (argc > 1 && *argv[1] == '-') { switch (*(argv[1]+1)) { case 'c': if (argc != 3) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -c \n" "Tests for whether cl.exe supports an option\n" "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } return CheckForCompilerFeature(argv[2]); case 'l': if (argc != 3) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -l \n" "Tests for whether link.exe supports an option\n" "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } return CheckForLinkerFeature(argv[2]); case 'f': if (argc == 2) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -f \n" "Find a substring within another\n" "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } else if (argc == 3) { /* * If the string is blank, there is no match. */ return 0; } else { return IsIn(argv[2], argv[3]); } case 's': if (argc == 2) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -s \n" "Perform a set of string map type substutitions on a file\n" "exitcodes: 0\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } return SubstituteFile(argv[2], argv[3]); case 'V': if (argc != 4) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -V filename matchstring\n" "Extract a version from a file:\n" "eg: pkgIndex.tcl \"package ifneeded http\"", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 0; } printf("%s\n", GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0')); return 0; case 'Q': if (argc != 3) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -Q path\n" "Emit the fully qualified path\n" "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } return QualifyPath(argv[2]); } } chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -c|-f|-l|-Q|-s|-V ...\n" "This is a little helper app to equalize shell differences between WinNT and\n" "Win9x and get nmake.exe to accomplish its job.\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } static int CheckForCompilerFeature( const char *option) { STARTUPINFO si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; DWORD threadID; char msg[300]; BOOL ok; HANDLE hProcess, h, pipeThreads[2]; char cmdline[100]; hProcess = GetCurrentProcess(); ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = INVALID_HANDLE_VALUE; ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = FALSE; /* * Create a non-inheritible pipe. */ CreatePipe(&Out.pipe, &h, &sa, 0); /* * Dupe the write side, make it inheritible, and close the original. */ DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); /* * Same as above, but for the error side. */ CreatePipe(&Err.pipe, &h, &sa, 0); DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); /* * Base command line. */ lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X -Fp.\\_junk.pch "); /* * Append our option for testing */ lstrcat(cmdline, option); /* * Filename to compile, which exists, but is nothing and empty. */ lstrcat(cmdline, " .\\nul"); ok = CreateProcess( NULL, /* Module name. */ cmdline, /* Command line. */ NULL, /* Process handle not inheritable. */ NULL, /* Thread handle not inheritable. */ TRUE, /* yes, inherit handles. */ DETACHED_PROCESS, /* No console for you. */ NULL, /* Use parent's environment block. */ NULL, /* Use parent's starting directory. */ &si, /* Pointer to STARTUPINFO structure. */ &pi); /* Pointer to PROCESS_INFORMATION structure. */ if (!ok) { DWORD err = GetLastError(); int chars = snprintf(msg, sizeof(msg) - 1, "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], (300-chars), 0); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); return 2; } /* * Close our references to the write handles that have now been inherited. */ CloseHandle(si.hStdOutput); CloseHandle(si.hStdError); WaitForInputIdle(pi.hProcess, 5000); CloseHandle(pi.hThread); /* * Start the pipe reader threads. */ pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); /* * Block waiting for the process to end. */ WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); /* * Wait for our pipe to get done reading, should it be a little slow. */ WaitForMultipleObjects(2, pipeThreads, TRUE, 500); CloseHandle(pipeThreads[0]); CloseHandle(pipeThreads[1]); /* * Look for the commandline warning code in both streams. * - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002. */ return !(strstr(Out.buffer, "D4002") != NULL || strstr(Err.buffer, "D4002") != NULL || strstr(Out.buffer, "D9002") != NULL || strstr(Err.buffer, "D9002") != NULL || strstr(Out.buffer, "D2021") != NULL || strstr(Err.buffer, "D2021") != NULL); } static int CheckForLinkerFeature( const char *option) { STARTUPINFO si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; DWORD threadID; char msg[300]; BOOL ok; HANDLE hProcess, h, pipeThreads[2]; char cmdline[100]; hProcess = GetCurrentProcess(); ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = INVALID_HANDLE_VALUE; ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; /* * Create a non-inheritible pipe. */ CreatePipe(&Out.pipe, &h, &sa, 0); /* * Dupe the write side, make it inheritible, and close the original. */ DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); /* * Same as above, but for the error side. */ CreatePipe(&Err.pipe, &h, &sa, 0); DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); /* * Base command line. */ lstrcpy(cmdline, "link.exe -nologo "); /* * Append our option for testing. */ lstrcat(cmdline, option); ok = CreateProcess( NULL, /* Module name. */ cmdline, /* Command line. */ NULL, /* Process handle not inheritable. */ NULL, /* Thread handle not inheritable. */ TRUE, /* yes, inherit handles. */ DETACHED_PROCESS, /* No console for you. */ NULL, /* Use parent's environment block. */ NULL, /* Use parent's starting directory. */ &si, /* Pointer to STARTUPINFO structure. */ &pi); /* Pointer to PROCESS_INFORMATION structure. */ if (!ok) { DWORD err = GetLastError(); int chars = snprintf(msg, sizeof(msg) - 1, "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], (300-chars), 0); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); return 2; } /* * Close our references to the write handles that have now been inherited. */ CloseHandle(si.hStdOutput); CloseHandle(si.hStdError); WaitForInputIdle(pi.hProcess, 5000); CloseHandle(pi.hThread); /* * Start the pipe reader threads. */ pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); /* * Block waiting for the process to end. */ WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); /* * Wait for our pipe to get done reading, should it be a little slow. */ WaitForMultipleObjects(2, pipeThreads, TRUE, 500); CloseHandle(pipeThreads[0]); CloseHandle(pipeThreads[1]); /* * Look for the commandline warning code in the stderr stream. */ return !(strstr(Out.buffer, "LNK1117") != NULL || strstr(Err.buffer, "LNK1117") != NULL || strstr(Out.buffer, "LNK4044") != NULL || strstr(Err.buffer, "LNK4044") != NULL); } static DWORD WINAPI ReadFromPipe( LPVOID args) { pipeinfo *pi = (pipeinfo *) args; char *lastBuf = pi->buffer; DWORD dwRead; BOOL ok; again: if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) { CloseHandle(pi->pipe); return (DWORD)-1; } ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L); if (!ok || dwRead == 0) { CloseHandle(pi->pipe); return 0; } lastBuf += dwRead; goto again; return 0; /* makes the compiler happy */ } static int IsIn( const char *string, const char *substring) { return (strstr(string, substring) != NULL); } /* * GetVersionFromFile -- * Looks for a match string in a file and then returns the version * following the match where a version is anything acceptable to * package provide or package ifneeded. */ static const char * GetVersionFromFile( const char *filename, const char *match, int numdots) { size_t cbBuffer = 100; static char szBuffer[100]; char *szResult = NULL; FILE *fp = fopen(filename, "rt"); if (fp != NULL) { /* * Read data until we see our match string. */ while (fgets(szBuffer, cbBuffer, fp) != NULL) { LPSTR p, q; p = strstr(szBuffer, match); if (p != NULL) { /* * Skip to first digit after the match. */ p += strlen(match); while (*p && !isdigit(*p)) { ++p; } /* * Find ending whitespace. */ q = p; while (*q && (strchr("0123456789.ab", *q)) && ((!strchr(".ab", *q) && (!strchr("ab", q[-1])) || --numdots))) { ++q; } memcpy(szBuffer, p, q - p); szBuffer[q-p] = 0; szResult = szBuffer; break; } } fclose(fp); } return szResult; } /* * List helpers for the SubstituteFile function */ typedef struct list_item_t { struct list_item_t *nextPtr; char * key; char * value; } list_item_t; /* insert a list item into the list (list may be null) */ static list_item_t * list_insert(list_item_t **listPtrPtr, const char *key, const char *value) { list_item_t *itemPtr = malloc(sizeof(list_item_t)); if (itemPtr) { itemPtr->key = strdup(key); itemPtr->value = strdup(value); itemPtr->nextPtr = NULL; while(*listPtrPtr) { listPtrPtr = &(*listPtrPtr)->nextPtr; } *listPtrPtr = itemPtr; } return itemPtr; } static void list_free(list_item_t **listPtrPtr) { list_item_t *tmpPtr, *listPtr = *listPtrPtr; while (listPtr) { tmpPtr = listPtr; listPtr = listPtr->nextPtr; free(tmpPtr->key); free(tmpPtr->value); free(tmpPtr); } } /* * SubstituteFile -- * As windows doesn't provide anything useful like sed and it's unreliable * to use the tclsh you are building against (consider x-platform builds - * eg compiling AMD64 target from IX86) we provide a simple substitution * option here to handle autoconf style substitutions. * The substitution file is whitespace and line delimited. The file should * consist of lines matching the regular expression: * \s*\S+\s+\S*$ * * Usage is something like: * nmakehlp -S << $** > $@ * @PACKAGE_NAME@ $(PACKAGE_NAME) * @PACKAGE_VERSION@ $(PACKAGE_VERSION) * << */ static int SubstituteFile( const char *substitutions, const char *filename) { size_t cbBuffer = 1024; static char szBuffer[1024], szCopy[1024]; char *szResult = NULL; list_item_t *substPtr = NULL; FILE *fp, *sp; fp = fopen(filename, "rt"); if (fp != NULL) { /* * Build a list of substutitions from the first filename */ sp = fopen(substitutions, "rt"); if (sp != NULL) { while (fgets(szBuffer, cbBuffer, sp) != NULL) { char *ks, *ke, *vs, *ve; ks = szBuffer; while (ks && *ks && isspace(*ks)) ++ks; ke = ks; while (ke && *ke && !isspace(*ke)) ++ke; vs = ke; while (vs && *vs && isspace(*vs)) ++vs; ve = vs; while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve; *ke = 0, *ve = 0; list_insert(&substPtr, ks, vs); } fclose(sp); } /* debug: dump the list */ #ifdef _DEBUG { int n = 0; list_item_t *p = NULL; for (p = substPtr; p != NULL; p = p->nextPtr, ++n) { fprintf(stderr, "% 3d '%s' => '%s'\n", n, p->key, p->value); } } #endif /* * Run the substitutions over each line of the input */ while (fgets(szBuffer, cbBuffer, fp) != NULL) { list_item_t *p = NULL; for (p = substPtr; p != NULL; p = p->nextPtr) { char *m = strstr(szBuffer, p->key); if (m) { char *cp, *op, *sp; cp = szCopy; op = szBuffer; while (op != m) *cp++ = *op++; sp = p->value; while (sp && *sp) *cp++ = *sp++; op += strlen(p->key); while (*op) *cp++ = *op++; *cp = 0; memcpy(szBuffer, szCopy, sizeof(szCopy)); } } printf(szBuffer); } list_free(&substPtr); } fclose(fp); return 0; } /* * QualifyPath -- * * This composes the current working directory with a provided path * and returns the fully qualified and normalized path. * Mostly needed to setup paths for testing. */ static int QualifyPath( const char *szPath) { char szCwd[MAX_PATH + 1]; char szTmp[MAX_PATH + 1]; char *p; GetCurrentDirectory(MAX_PATH, szCwd); while ((p = strchr(szPath, '/')) && *p) *p = '\\'; PathCombine(szTmp, szCwd, szPath); PathCanonicalize(szCwd, szTmp); printf("%s\n", szCwd); return 0; } /* * Local variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * indent-tabs-mode: t * tab-width: 8 * End: */ win/pkg.vc000066400000000000000000000002201242365656200127730ustar00rootroot00000000000000# remember to change configure.in as well when these change # (then re-autoconf) PACKAGE_MAJOR = 2 PACKAGE_MINOR = 0 PACKAGE_VERSION = "2.0.0" win/rules.vc000066400000000000000000000373611242365656200133640ustar00rootroot00000000000000#------------------------------------------------------------------------------ # rules.vc -- # # Microsoft Visual C++ makefile include for decoding the commandline # macros. This file does not need editing to build Tcl. # # This version is modified from the Tcl source version to support # building extensions using nmake. # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # # Copyright (c) 2001-2003 David Gravereaux. # Copyright (c) 2003-2008 Patrick Thoyts #------------------------------------------------------------------------------ !ifndef _RULES_VC _RULES_VC = 1 cc32 = $(CC) # built-in default. link32 = link lib32 = lib rc32 = $(RC) # built-in default. !ifndef INSTALLDIR ### Assume the normal default. _INSTALLDIR = C:\Program Files\Tcl !else ### Fix the path separators. _INSTALLDIR = $(INSTALLDIR:/=\) !endif #---------------------------------------------------------- # Set the proper copy method to avoid overwrite questions # to the user when copying files and selecting the right # "delete all" method. #---------------------------------------------------------- !if "$(OS)" == "Windows_NT" RMDIR = rmdir /S /Q ERRNULL = 2>NUL !if ![ver | find "4.0" > nul] CPY = echo y | xcopy /i >NUL COPY = copy >NUL !else CPY = xcopy /i /y >NUL COPY = copy /y >NUL !endif !else # "$(OS)" != "Windows_NT" CPY = xcopy /i >_JUNK.OUT # On Win98 NUL does not work here. COPY = copy >_JUNK.OUT # On Win98 NUL does not work here. RMDIR = deltree /Y NULL = \NUL # Used in testing directory existence ERRNULL = >NUL # Win9x shell cannot redirect stderr !endif MKDIR = mkdir #------------------------------------------------------------------------------ # Determine the host and target architectures and compiler version. #------------------------------------------------------------------------------ _HASH=^# _VC_MANIFEST_EMBED_EXE= _VC_MANIFEST_EMBED_DLL= VCVER=0 !if ![echo VCVERSION=_MSC_VER > vercl.x] \ && ![echo $(_HASH)if defined(_M_IX86) >> vercl.x] \ && ![echo ARCH=IX86 >> vercl.x] \ && ![echo $(_HASH)elif defined(_M_AMD64) >> vercl.x] \ && ![echo ARCH=AMD64 >> vercl.x] \ && ![echo $(_HASH)endif >> vercl.x] \ && ![cl -nologo -TC -P vercl.x $(ERRNULL)] !include vercl.i !if ![echo VCVER= ^\> vercl.vc] \ && ![set /a $(VCVERSION) / 100 - 6 >> vercl.vc] !include vercl.vc !endif !endif !if ![del $(ERRNUL) /q/f vercl.x vercl.i vercl.vc] !endif !if ![reg query HKLM\Hardware\Description\System\CentralProcessor\0 /v Identifier | findstr /i x86] NATIVE_ARCH=IX86 !else NATIVE_ARCH=AMD64 !endif # Since MSVC8 we must deal with manifest resources. !if $(VCVERSION) >= 1400 _VC_MANIFEST_EMBED_EXE=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;1 _VC_MANIFEST_EMBED_DLL=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;2 !endif !ifndef MACHINE MACHINE=$(ARCH) !endif !ifndef CFG_ENCODING CFG_ENCODING = \"cp1252\" !endif !message =============================================================================== #---------------------------------------------------------- # build the helper app we need to overcome nmake's limiting # environment. #---------------------------------------------------------- !if !exist(nmakehlp.exe) !if [$(cc32) -nologo nmakehlp.c -link -subsystem:console > nul] !endif !endif #---------------------------------------------------------- # Test for compiler features #---------------------------------------------------------- ### test for optimizations !if [nmakehlp -c -Ot] !message *** Compiler has 'Optimizations' OPTIMIZING = 1 !else !message *** Compiler does not have 'Optimizations' OPTIMIZING = 0 !endif OPTIMIZATIONS = !if [nmakehlp -c -Ot] OPTIMIZATIONS = $(OPTIMIZATIONS) -Ot !endif !if [nmakehlp -c -Oi] OPTIMIZATIONS = $(OPTIMIZATIONS) -Oi !endif !if [nmakehlp -c -Op] OPTIMIZATIONS = $(OPTIMIZATIONS) -Op !endif !if [nmakehlp -c -fp:strict] OPTIMIZATIONS = $(OPTIMIZATIONS) -fp:strict !endif !if [nmakehlp -c -Gs] OPTIMIZATIONS = $(OPTIMIZATIONS) -Gs !endif !if [nmakehlp -c -GS] OPTIMIZATIONS = $(OPTIMIZATIONS) -GS !endif !if [nmakehlp -c -GL] OPTIMIZATIONS = $(OPTIMIZATIONS) -GL !endif DEBUGFLAGS = !if [nmakehlp -c -RTC1] DEBUGFLAGS = $(DEBUGFLAGS) -RTC1 !elseif [nmakehlp -c -GZ] DEBUGFLAGS = $(DEBUGFLAGS) -GZ !endif COMPILERFLAGS =-W3 # In v13 -GL and -YX are incompatible. !if [nmakehlp -c -YX] !if ![nmakehlp -c -GL] OPTIMIZATIONS = $(OPTIMIZATIONS) -YX !endif !endif !if "$(MACHINE)" == "IX86" ### test for pentium errata !if [nmakehlp -c -QI0f] !message *** Compiler has 'Pentium 0x0f fix' COMPILERFLAGS = $(COMPILERFLAGS) -QI0f !else !message *** Compiler does not have 'Pentium 0x0f fix' !endif !endif !if "$(MACHINE)" == "IA64" ### test for Itanium errata !if [nmakehlp -c -QIA64_Bx] !message *** Compiler has 'B-stepping errata workarounds' COMPILERFLAGS = $(COMPILERFLAGS) -QIA64_Bx !else !message *** Compiler does not have 'B-stepping errata workarounds' !endif !endif !if "$(MACHINE)" == "IX86" ### test for -align:4096, when align:512 will do. !if [nmakehlp -l -opt:nowin98] !message *** Linker has 'Win98 alignment problem' ALIGN98_HACK = 1 !else !message *** Linker does not have 'Win98 alignment problem' ALIGN98_HACK = 0 !endif !else ALIGN98_HACK = 0 !endif LINKERFLAGS = !if [nmakehlp -l -ltcg] LINKERFLAGS =-ltcg !endif #---------------------------------------------------------- # Decode the options requested. #---------------------------------------------------------- !if "$(OPTS)" == "" || [nmakehlp -f "$(OPTS)" "none"] STATIC_BUILD = 0 TCL_THREADS = 1 DEBUG = 0 SYMBOLS = 0 PROFILE = 0 PGO = 0 MSVCRT = 0 LOIMPACT = 0 UNCHECKED = 0 !else !if [nmakehlp -f $(OPTS) "static"] !message *** Doing static STATIC_BUILD = 1 !else STATIC_BUILD = 0 !endif !if [nmakehlp -f $(OPTS) "msvcrt"] !message *** Doing msvcrt MSVCRT = 1 !else MSVCRT = 0 !endif !if [nmakehlp -f $(OPTS) "nothreads"] !message *** Compile explicitly for non-threaded tcl TCL_THREADS = 0 !else TCL_THREADS = 1 !endif !if [nmakehlp -f $(OPTS) "symbols"] !message *** Doing symbols DEBUG = 1 !else DEBUG = 0 !endif !if [nmakehlp -f $(OPTS) "pdbs"] !message *** Doing pdbs SYMBOLS = 1 !else SYMBOLS = 0 !endif !if [nmakehlp -f $(OPTS) "profile"] !message *** Doing profile PROFILE = 1 !else PROFILE = 0 !endif !if [nmakehlp -f $(OPTS) "pgi"] !message *** Doing profile guided optimization instrumentation PGO = 1 !elseif [nmakehlp -f $(OPTS) "pgo"] !message *** Doing profile guided optimization PGO = 2 !else PGO = 0 !endif !if [nmakehlp -f $(OPTS) "loimpact"] !message *** Doing loimpact LOIMPACT = 1 !else LOIMPACT = 0 !endif !if [nmakehlp -f $(OPTS) "unchecked"] !message *** Doing unchecked UNCHECKED = 1 !else UNCHECKED = 0 !endif !endif !if !$(STATIC_BUILD) # Make sure we don't build overly fat DLLs. MSVCRT = 1 # We shouldn't statically put the extensions inside the shell when dynamic. TCL_USE_STATIC_PACKAGES = 0 !endif #---------------------------------------------------------- # Figure-out how to name our intermediate and output directories. # We wouldn't want different builds to use the same .obj files # by accident. #---------------------------------------------------------- #---------------------------------------- # Naming convention: # t = full thread support. # s = static library (as opposed to an # import library) # g = linked to the debug enabled C # run-time. # x = special static build when it # links to the dynamic C run-time. #---------------------------------------- SUFX = sgx !if $(DEBUG) BUILDDIRTOP = Debug !else BUILDDIRTOP = Release !endif !if "$(MACHINE)" != "IX86" BUILDDIRTOP =$(BUILDDIRTOP)_$(MACHINE) !endif !if $(VCVER) > 6 BUILDDIRTOP =$(BUILDDIRTOP)_VC$(VCVER) !endif !if !$(DEBUG) || $(UNCHECKED) SUFX = $(SUFX:g=) !endif TMP_DIRFULL = .\$(BUILDDIRTOP)\$(PROJECT)_DynamicStaticX !if !$(STATIC_BUILD) TMP_DIRFULL = $(TMP_DIRFULL:Static=) SUFX = $(SUFX:s=) EXT = dll !if $(MSVCRT) TMP_DIRFULL = $(TMP_DIRFULL:X=) SUFX = $(SUFX:x=) !endif !else TMP_DIRFULL = $(TMP_DIRFULL:Dynamic=) EXT = lib !if !$(MSVCRT) TMP_DIRFULL = $(TMP_DIRFULL:X=) SUFX = $(SUFX:x=) !endif !endif !ifndef TMP_DIR TMP_DIR = $(TMP_DIRFULL) !ifndef OUT_DIR OUT_DIR = .\$(BUILDDIRTOP) !endif !else !ifndef OUT_DIR OUT_DIR = $(TMP_DIR) !endif !endif #---------------------------------------------------------- # Decode the statistics requested. #---------------------------------------------------------- !if "$(STATS)" == "" || [nmakehlp -f "$(STATS)" "none"] TCL_MEM_DEBUG = 0 TCL_COMPILE_DEBUG = 0 !else !if [nmakehlp -f $(STATS) "memdbg"] !message *** Doing memdbg TCL_MEM_DEBUG = 1 !else TCL_MEM_DEBUG = 0 !endif !if [nmakehlp -f $(STATS) "compdbg"] !message *** Doing compdbg TCL_COMPILE_DEBUG = 1 !else TCL_COMPILE_DEBUG = 0 !endif !endif #---------------------------------------------------------- # Decode the checks requested. #---------------------------------------------------------- !if "$(CHECKS)" == "" || [nmakehlp -f "$(CHECKS)" "none"] TCL_NO_DEPRECATED = 0 WARNINGS = -W3 !else !if [nmakehlp -f $(CHECKS) "nodep"] !message *** Doing nodep check TCL_NO_DEPRECATED = 1 !else TCL_NO_DEPRECATED = 0 !endif !if [nmakehlp -f $(CHECKS) "fullwarn"] !message *** Doing full warnings check WARNINGS = -W4 !if [nmakehlp -l -warn:3] LINKERFLAGS = $(LINKERFLAGS) -warn:3 !endif !else WARNINGS = -W3 !endif !if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64] !message *** Doing 64bit portability warnings WARNINGS = $(WARNINGS) -Wp64 !endif !endif !if $(PGO) > 1 !if [nmakehlp -l -ltcg:pgoptimize] LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pgoptimize !else MSG=^ This compiler does not support profile guided optimization. !error $(MSG) !endif !elseif $(PGO) > 0 !if [nmakehlp -l -ltcg:pginstrument] LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pginstrument !else MSG=^ This compiler does not support profile guided optimization. !error $(MSG) !endif !endif #---------------------------------------------------------- # Set our defines now armed with our options. #---------------------------------------------------------- OPTDEFINES = -DTCL_CFGVAL_ENCODING=$(CFG_ENCODING) -DSTDC_HEADERS !if $(TCL_MEM_DEBUG) OPTDEFINES = $(OPTDEFINES) -DTCL_MEM_DEBUG !endif !if $(TCL_COMPILE_DEBUG) OPTDEFINES = $(OPTDEFINES) -DTCL_COMPILE_DEBUG -DTCL_COMPILE_STATS !endif !if $(TCL_THREADS) OPTDEFINES = $(OPTDEFINES) -DTCL_THREADS=1 -DUSE_THREAD_ALLOC=1 !endif !if $(STATIC_BUILD) OPTDEFINES = $(OPTDEFINES) -DSTATIC_BUILD !endif !if $(TCL_NO_DEPRECATED) OPTDEFINES = $(OPTDEFINES) -DTCL_NO_DEPRECATED !endif !if !$(DEBUG) OPTDEFINES = $(OPTDEFINES) -DNDEBUG !if $(OPTIMIZING) OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_OPTIMIZED !endif !endif !if $(PROFILE) OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_PROFILED !endif !if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64" OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_DO64BIT !endif #---------------------------------------------------------- # Get common info used when building extensions. #---------------------------------------------------------- !if "$(PROJECT)" != "tcl" # If INSTALLDIR set to tcl root dir then reset to the lib dir. !if exist("$(_INSTALLDIR)\include\tcl.h") _INSTALLDIR=$(_INSTALLDIR)\lib !endif !if !defined(TCLDIR) !if exist("$(_INSTALLDIR)\..\include\tcl.h") TCLINSTALL = 1 _TCLDIR = $(_INSTALLDIR)\.. _TCL_H = $(_INSTALLDIR)\..\include\tcl.h TCLDIR = $(_INSTALLDIR)\.. !else MSG=^ Failed to find tcl.h. Set the TCLDIR macro. !error $(MSG) !endif !else _TCLDIR = $(TCLDIR:/=\) !if exist("$(_TCLDIR)\include\tcl.h") TCLINSTALL = 1 _TCL_H = $(_TCLDIR)\include\tcl.h !elseif exist("$(_TCLDIR)\generic\tcl.h") TCLINSTALL = 0 _TCL_H = $(_TCLDIR)\generic\tcl.h !else MSG =^ Failed to find tcl.h. The TCLDIR macro does not appear correct. !error $(MSG) !endif !endif !if [echo REM = This file is generated from rules.vc > versions.vc] !endif !if exist("$(_TCL_H)") !if [echo TCL_DOTVERSION = \>> versions.vc] \ && [nmakehlp -V "$(_TCL_H)" TCL_VERSION >> versions.vc] !endif !endif !include versions.vc TCL_VERSION = $(TCL_DOTVERSION:.=) !if $(TCLINSTALL) TCLSH = "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe" !if !exist($(TCLSH)) && $(TCL_THREADS) TCLSH = "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX).exe" !endif TCLSTUBLIB = "$(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib" TCLIMPLIB = "$(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib" TCL_LIBRARY = $(_TCLDIR)\lib COFFBASE = \must\have\tcl\sources\to\build\this\target TCLTOOLSDIR = \must\have\tcl\sources\to\build\this\target TCL_INCLUDES = -I"$(_TCLDIR)\include" !else TCLSH = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX).exe" !if !exist($(TCLSH)) && $(TCL_THREADS) TCLSH = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)t$(SUFX).exe" !endif TCLSTUBLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib" TCLIMPLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib" TCL_LIBRARY = $(_TCLDIR)\library COFFBASE = "$(_TCLDIR)\win\coffbase.txt" TCLTOOLSDIR = $(_TCLDIR)\tools TCL_INCLUDES = -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win" !endif !endif #---------------------------------------------------------- # Optionally check for Tk info for building extensions. #---------------------------------------------------------- !ifdef PROJECT_REQUIRES_TK !if "$(PROJECT)" != "tcl" && "$(PROJECT)" != "tk" !if !defined(TKDIR) !if exist("$(_INSTALLDIR)\..\include\tk.h") TKINSTALL = 1 _TKDIR = $(_INSTALLDIR)\.. _TK_H = $(_TKDIR)\include\tk.h TKDIR = $(_TKDIR) !elseif exist("$(_TCLDIR)\include\tk.h") TKINSTALL = 1 _TKDIR = $(_TCLDIR) _TK_H = $(_TKDIR)\include\tk.h TKDIR = $(_TKDIR) !endif !else _TKDIR = $(TKDIR:/=\) !if exist("$(_TKDIR)\include\tk.h") TKINSTALL = 1 _TK_H = $(_TKDIR)\include\tk.h !elseif exist("$(_TKDIR)\generic\tk.h") TKINSTALL = 0 _TK_H = $(_TKDIR)\generic\tk.h !else MSG =^ Failed to find tk.h. The TKDIR macro does not appear correct. !error $(MSG) !endif !endif !if defined(TKDIR) TK_DOTVERSION = 8.4 !if exist("$(_TK_H)") !if [echo TK_DOTVERSION = \>> versions.vc] \ && [nmakehlp -V "$(_TK_H)" TK_VERSION >> versions.vc] !endif !endif !include versions.vc TK_VERSION = $(TK_DOTVERSION:.=) !if $(TKINSTALL) WISH = "$(_TKDIR)\bin\wish$(TK_VERSION)$(SUFX).exe" !if !exist($(WISH)) && $(TCL_THREADS) WISH = "$(_TKDIR)\bin\wish$(TK_VERSION)t$(SUFX).exe" !endif TKSTUBLIB = "$(_TKDIR)\lib\tkstub$(TK_VERSION).lib" TKIMPLIB = "$(_TKDIR)\lib\tk$(TK_VERSION)$(SUFX).lib" TK_INCLUDES = -I"$(_TKDIR)\include" !else WISH = "$(_TKDIR)\win\$(BUILDDIRTOP)\wish$(TCL_VERSION)$(SUFX).exe" !if !exist($(WISH)) && $(TCL_THREADS) WISH = "$(_TKDIR)\win\$(BUILDDIRTOP)\wish$(TCL_VERSION)t$(SUFX).exe" !endif TKSTUBLIB = "$(_TKDIR)\win\$(BUILDDIRTOP)\tkstub$(TCL_VERSION).lib" TKIMPLIB = "$(_TKDIR)\win\$(BUILDDIRTOP)\tk$(TCL_VERSION)$(SUFX).lib" TK_INCLUDES = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib" !endif !endif !endif !endif #---------------------------------------------------------- # Setup the fully qualified OUT_DIR path as OUT_DIR_PATH #---------------------------------------------------------- !if [echo OUT_DIR_PATH = \>> versions.vc] \ && [nmakehlp -Q "$(OUT_DIR)" >> versions.vc] !endif !include versions.vc #---------------------------------------------------------- # Display stats being used. #---------------------------------------------------------- !message *** Intermediate directory will be '$(TMP_DIR)' !message *** Output directory will be '$(OUT_DIR)' !message *** Suffix for binaries will be '$(SUFX)' !message *** Optional defines are '$(OPTDEFINES)' !message *** Compiler version $(VCVER). Target machine is $(MACHINE) !message *** Host architecture is $(NATIVE_ARCH) !message *** Compiler options '$(COMPILERFLAGS) $(OPTIMIZATIONS) $(DEBUGFLAGS) $(WARNINGS)' !message *** Link options '$(LINKERFLAGS)' !endif