summaryrefslogtreecommitdiffstats
path: root/generic/tclInterp.c
diff options
context:
space:
mode:
authordgp <dgp@users.sourceforge.net>2004-11-30 19:34:44 (GMT)
committerdgp <dgp@users.sourceforge.net>2004-11-30 19:34:44 (GMT)
commit999c1d1867082cb366aeb7bb7d6f46f27ed40596 (patch)
tree3f6ea55c8096d98ba728284819430a49be305cf6 /generic/tclInterp.c
parentf1608d9d16479048838c99d496b9f2812de574f2 (diff)
downloadtcl-999c1d1867082cb366aeb7bb7d6f46f27ed40596.zip
tcl-999c1d1867082cb366aeb7bb7d6f46f27ed40596.tar.gz
tcl-999c1d1867082cb366aeb7bb7d6f46f27ed40596.tar.bz2
Patch 976520 reworks several of the details involved with
startup/initialization of the Tcl library, focused on the activities of Tcl_FindExecutable(). * generic/tclIO.c: Removed bogus claim in comment that encoding "iso8859-1" is "built-in" to Tcl. * generic/tclInt.h: Created a new struct ProcessGlobalValue, * generic/tclUtil.c: routines Tcl(Get|Set)ProcessGlobalValue, and function type TclInitProcessGlobalValueProc. Together, these take care of the housekeeping for "values" (things that can be held in a Tcl_Obj) that are global across a whole process. That is, they are shared among multiple threads, and epoch and mutex protection must govern the validity of cached copies maintained in each thread. * generic/tclNotify.c: Modified TclInitNotifier() to tolerate being called multiple times in the same thread. * generic/tclEvent.c: Dropped the unused argv0 argument to TclInitSubsystems(). Removed machinery to unsure only one TclInitNotifier() call per thread, now that that is safe. Converted Tcl(Get|Set)LibraryPath to use a ProcessGlobalValue, and moved them to tclEncoding.c. * generic/tclBasic.c: Updated caller. * generic/tclInt.h: TclpFindExecutable now returns void. * unix/tclUnixFile.c: * win/tclWinFile.c: * win/tclWinPipe.c: * generic/tclEncoding.c: Built new encoding search initialization on a foundation of ProcessGlobalValues, exposing new routines Tcl(Get|Set)EncodingSearchPath. A cache of a map from encoding name to directory pathname keeps track of where encodings are available for loading. Tcl_FindExecutable greatly simplified into just three function calls. The "library path" is now misnamed, as its only remaining purpose is as a foundation for the default encoding search path. * generic/tclInterp.c: Inlined the initScript that is evaluated by Tcl_Init(). Added verification after initScript evaluation that Tcl can find its installed *.enc files, and that it has initialized [encoding system] in agreement with what the environment expects. [tclInit] no longer driven by the value of $::tcl_libPath; it largely constructs its own search path now, rather than attempt to share one with the encoding system. * unix/tclUnixInit.c: TclpSetInitialEncodings factored so that a new * win/tclWinInit.c: routine TclpGetEncodingNameFromEnvironment can reveal that Tcl thinks the [encoding system] should be, even when an incomplete encoding search path, or a missing *.enc file won't allow that initialization to succeed. TclpInitLibraryPath reworked as an initializer of a ProcessGlobalValue. * unix/tclUnixTest.c: Update implementations of [testfindexecutable], [testgetdefenc], and [testsetdefenc]. * tests/unixInit.test: Corrected tests to operate properly even when a value of TCL_LIBRARY is required to find encodings. * generic/tclInt.decls: New internal stubs: TclGetEncodingSearchPath, TclSetEncodingSearchPath, TclpGetEncodingNameFromEnvironment. These are candidates for public exposure by future TIPs. * generic/tclIntDecls.h: make genstubs * generic/tclStubInit.c: * generic/tclTest.c: Updated [testencoding] to use * tests/encoding.test: Tcl(Get|Set)EncodingSearchPath. Updated tests.
Diffstat (limited to 'generic/tclInterp.c')
-rw-r--r--generic/tclInterp.c277
1 files changed, 172 insertions, 105 deletions
diff --git a/generic/tclInterp.c b/generic/tclInterp.c
index 194944b..d1cd7f5 100644
--- a/generic/tclInterp.c
+++ b/generic/tclInterp.c
@@ -10,116 +10,13 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclInterp.c,v 1.52 2004/11/22 21:24:30 dgp Exp $
+ * RCS: @(#) $Id: tclInterp.c,v 1.53 2004/11/30 19:34:49 dgp Exp $
*/
#include "tclInt.h"
#include <stdio.h>
/*
- * In order to find init.tcl during initialization, the following script
- * is invoked by Tcl_Init(). It looks in several different directories:
- *
- * $tcl_library - can specify a primary location, if set,
- * no other locations will be checked. This
- * is the recommended way for a program that
- * embeds Tcl to specifically tell Tcl where
- * to find an init.tcl file.
- *
- * $env(TCL_LIBRARY) - highest priority so user can always override
- * the search path unless the application has
- * specified an exact directory above
- *
- * $tclDefaultLibrary - INTERNAL: This variable is set by Tcl
- * on those platforms where it can determine
- * at runtime the directory where it expects
- * the init.tcl file to be. After [tclInit]
- * reads and uses this value, it [unset]s it.
- * External users of Tcl should not make use
- * of the variable to customize [tclInit].
- *
- * $tcl_libPath - OBSOLETE: This variable is no longer
- * set by Tcl itself, but [tclInit] examines
- * it in case some program that embeds Tcl
- * is customizing [tclInit] by setting this
- * variable to a list of directories in which
- * to search.
- *
- * [tcl::pkgconfig get scriptdir,runtime]
- * - the directory determined by configure to
- * be the place where Tcl's script library
- * is to be installed.
- *
- * The first directory on this path that contains a valid init.tcl script
- * will be set as the value of tcl_library.
- *
- * Note that this entire search mechanism can be bypassed by defining an
- * alternate tclInit procedure before calling Tcl_Init().
- */
-
-static char initScript[] = "if {[info proc tclInit]==\"\"} {\n\
- proc tclInit {} {\n\
- global tcl_libPath tcl_library\n\
- global env tclDefaultLibrary\n\
- variable ::tcl::LibPath\n\
- rename tclInit {}\n\
- set errors {}\n\
- set LibPath {}\n\
- if {[info exists tcl_library]} {\n\
- lappend LibPath $tcl_library\n\
- } else {\n\
- if {[info exists env(TCL_LIBRARY)]} {\n\
- lappend LibPath $env(TCL_LIBRARY)\n\
- if {[regexp ^tcl(.*)$ [file tail $env(TCL_LIBRARY)] -> tail]} {\n\
- if {$tail ne [info tclversion]} {\n\
- lappend LibPath [file join [file dirname\\\n\
- $env(TCL_LIBRARY)] tcl[info tclversion]]\n\
- }\n\
- }\n\
- }\n\
- if {[catch {\n\
- lappend LibPath $tclDefaultLibrary\n\
- unset tclDefaultLibrary\n\
- }]} {\n\
- lappend LibPath [::tcl::pkgconfig get scriptdir,runtime]\n\
- }\n\
- set parentDir [file normalize [file dirname [file dirname\\\n\
- [info nameofexecutable]]]]\n\
- set grandParentDir [file dirname $parentDir]\n\
- lappend LibPath [file join $parentDir lib tcl[info tclversion]]\n\
- lappend LibPath [file join $grandParentDir lib tcl[info tclversion]]\n\
- lappend LibPath [file join $parentDir library]\n\
- lappend LibPath [file join $grandParentDir library]\n\
- lappend LibPath [file join $grandParentDir\\\n\
- tcl[info patchlevel] library]\n\
- lappend LibPath [file join [file dirname $grandParentDir]\\\n\
- tcl[info patchlevel] library]\n\
- catch {\n\
- set LibPath [concat $LibPath $tcl_libPath]\n\
- }\n\
- }\n\
- foreach i $LibPath {\n\
- set tcl_library $i\n\
- set tclfile [file join $i init.tcl]\n\
- if {[file exists $tclfile]} {\n\
- if {![catch {uplevel #0 [list source $tclfile]} msg opts]} {\n\
- return\n\
- } else {\n\
- append errors \"$tclfile: $msg\n\"\n\
- append errors \"[dict get $opts -errorinfo]\n\"\n\
- }\n\
- }\n\
- }\n\
- set msg \"Can't find a usable init.tcl in the following directories: \n\"\n\
- append msg \" $LibPath\n\n\"\n\
- append msg \"$errors\n\n\"\n\
- append msg \"This probably means that Tcl wasn't installed properly.\n\"\n\
- error $msg\n\
- }\n\
-}\n\
-tclInit";
-
-/*
* A pointer to a string that holds an initialization script that if non-NULL
* is evaluated in Tcl_Init() prior to the built-in initialization script
* above. This variable can be modified by the procedure below.
@@ -404,12 +301,182 @@ int
Tcl_Init(interp)
Tcl_Interp *interp; /* Interpreter to initialize. */
{
+ int code;
+ Tcl_DString script, encodingName;
+ Tcl_Obj *path;
+
if (tclPreInitScript != NULL) {
if (Tcl_Eval(interp, tclPreInitScript) == TCL_ERROR) {
return (TCL_ERROR);
};
}
- return Tcl_Eval(interp, initScript);
+/*
+ * In order to find init.tcl during initialization, the following script
+ * is invoked by Tcl_Init(). It looks in several different directories:
+ *
+ * $tcl_library - can specify a primary location, if set,
+ * no other locations will be checked. This
+ * is the recommended way for a program that
+ * embeds Tcl to specifically tell Tcl where
+ * to find an init.tcl file.
+ *
+ * $env(TCL_LIBRARY) - highest priority so user can always override
+ * the search path unless the application has
+ * specified an exact directory above
+ *
+ * $tclDefaultLibrary - INTERNAL: This variable is set by Tcl
+ * on those platforms where it can determine
+ * at runtime the directory where it expects
+ * the init.tcl file to be. After [tclInit]
+ * reads and uses this value, it [unset]s it.
+ * External users of Tcl should not make use
+ * of the variable to customize [tclInit].
+ *
+ * $tcl_libPath - OBSOLETE: This variable is no longer
+ * set by Tcl itself, but [tclInit] examines
+ * it in case some program that embeds Tcl
+ * is customizing [tclInit] by setting this
+ * variable to a list of directories in which
+ * to search.
+ *
+ * [tcl::pkgconfig get scriptdir,runtime]
+ * - the directory determined by configure to
+ * be the place where Tcl's script library
+ * is to be installed.
+ *
+ * The first directory on this path that contains a valid init.tcl script
+ * will be set as the value of tcl_library.
+ *
+ * Note that this entire search mechanism can be bypassed by defining an
+ * alternate tclInit procedure before calling Tcl_Init().
+ */
+ code = Tcl_Eval(interp,
+"if {[info proc tclInit]==\"\"} {\n"
+" proc tclInit {} {\n"
+" global tcl_libPath tcl_library\n"
+" global env tclDefaultLibrary\n"
+" variable ::tcl::LibPath\n"
+" rename tclInit {}\n"
+" set errors {}\n"
+" set localPath {}\n"
+" set LibPath {}\n"
+" if {[info exists tcl_library]} {\n"
+" lappend localPath $tcl_library\n"
+" } else {\n"
+" if {[info exists env(TCL_LIBRARY)]\n"
+" && [string length $env(TCL_LIBRARY)]} {\n"
+" lappend localPath $env(TCL_LIBRARY)\n"
+" lappend LibPath $env(TCL_LIBRARY)\n"
+" if {[regexp ^tcl(.*)$ [file tail $env(TCL_LIBRARY)] -> tail]} {\n"
+" if {$tail ne [info tclversion]} {\n"
+" lappend localPath [file join [file dirname\\\n"
+" $env(TCL_LIBRARY)] tcl[info tclversion]]\n"
+" lappend LibPath [file join [file dirname\\\n"
+" $env(TCL_LIBRARY)] tcl[info tclversion]]\n"
+" }\n"
+" }\n"
+" }\n"
+" if {[catch {\n"
+" lappend localPath $tclDefaultLibrary\n"
+" unset tclDefaultLibrary\n"
+" }]} {\n"
+" lappend localPath [::tcl::pkgconfig get scriptdir,runtime]\n"
+" }\n"
+" set parentDir [file normalize [file dirname [file dirname\\\n"
+" [info nameofexecutable]]]]\n"
+" set grandParentDir [file dirname $parentDir]\n"
+" lappend LibPath [file join $parentDir lib tcl[info tclversion]]\n"
+" lappend LibPath [file join $grandParentDir lib tcl[info tclversion]]\n"
+" lappend LibPath [file join $parentDir library]\n"
+" lappend LibPath [file join $grandParentDir library]\n"
+" lappend LibPath [file join $grandParentDir\\\n"
+" tcl[info patchlevel] library]\n"
+" lappend LibPath [file join [file dirname $grandParentDir]\\\n"
+" tcl[info patchlevel] library]\n"
+" catch {\n"
+" set LibPath [concat $LibPath $tcl_libPath]\n"
+" }\n"
+" }\n"
+" foreach i [concat $localPath $LibPath] {\n"
+" set tcl_library $i\n"
+" set tclfile [file join $i init.tcl]\n"
+" if {[file exists $tclfile]} {\n"
+" if {![catch {uplevel #0 [list source $tclfile]} msg opts]} {\n"
+" return\n"
+" } else {\n"
+" append errors \"$tclfile: $msg\n\"\n"
+" append errors \"[dict get $opts -errorinfo]\n\"\n"
+" }\n"
+" }\n"
+" }\n"
+" set msg \"Can't find a usable init.tcl in the following directories: \n\"\n"
+" append msg \" $localPath $LibPath\n\n\"\n"
+" append msg \"$errors\n\n\"\n"
+" append msg \"This probably means that Tcl wasn't installed properly.\n\"\n"
+" error $msg\n"
+" }\n"
+"}\n"
+"tclInit");
+
+ if (code != TCL_OK) {
+ return code;
+ }
+
+ /*
+ * Now that [info library] is initialized, make sure that
+ * [file join [info library] encoding] is on the encoding
+ * search path.
+ *
+ * Relying on use of original built-in commands.
+ * Should be a safe assumption during interp initialization.
+ * More robust would be to use C-coded equivalents, but that's such
+ * a pain...
+ */
+
+ Tcl_DStringInit(&script);
+ Tcl_DStringAppend(&script, "lsearch -exact", -1);
+ path = Tcl_DuplicateObj(TclGetEncodingSearchPath());
+ Tcl_IncrRefCount(path);
+ Tcl_DStringAppendElement(&script, Tcl_GetString(path));
+ Tcl_DStringAppend(&script, " [file join [info library] encoding]", -1);
+ code = Tcl_EvalEx(interp, Tcl_DStringValue(&script),
+ Tcl_DStringLength(&script), TCL_EVAL_GLOBAL);
+ Tcl_DStringFree(&script);
+ if (code == TCL_OK) {
+ int index;
+ Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &index);
+ if (index != -1) {
+ /* [info library]/encoding already on the encoding search path */
+ goto done;
+ }
+ }
+ Tcl_DStringInit(&script);
+ Tcl_DStringAppend(&script, "file join [info library] encoding", -1);
+ code = Tcl_EvalEx(interp, Tcl_DStringValue(&script),
+ Tcl_DStringLength(&script), TCL_EVAL_GLOBAL);
+ Tcl_DStringFree(&script);
+ if (code == TCL_OK) {
+ Tcl_ListObjAppendElement(NULL, path, Tcl_GetObjResult(interp));
+ TclSetEncodingSearchPath(path);
+ }
+done:
+ /*
+ * Now that we know the distributed *.enc files are on the encoding
+ * search path, check whether the [encoding system] matches that
+ * specified by the environment, and if not, attempt to correct it
+ */
+ TclpGetEncodingNameFromEnvironment(&encodingName);
+ if (strcmp(Tcl_DStringValue(&encodingName), Tcl_GetEncodingName(NULL))) {
+ code = Tcl_SetSystemEncoding(NULL, Tcl_DStringValue(&encodingName));
+ if (code == TCL_ERROR) {
+ Tcl_Panic("system encoding \"", Tcl_DStringValue(&encodingName),
+ "\" not available");
+ }
+ }
+ Tcl_DStringFree(&encodingName);
+ Tcl_DecrRefCount(path);
+ Tcl_ResetResult(interp);
+ return TCL_OK;
}
/*