summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog73
-rw-r--r--generic/tclBasic.c4
-rw-r--r--generic/tclEncoding.c672
-rw-r--r--generic/tclEvent.c147
-rw-r--r--generic/tclIO.c5
-rw-r--r--generic/tclInt.decls11
-rw-r--r--generic/tclInt.h49
-rw-r--r--generic/tclIntDecls.h34
-rw-r--r--generic/tclInterp.c277
-rw-r--r--generic/tclNotify.c25
-rw-r--r--generic/tclStubInit.c5
-rw-r--r--generic/tclTest.c6
-rw-r--r--generic/tclUtil.c330
-rw-r--r--tests/encoding.test6
-rw-r--r--tests/unixInit.test109
-rw-r--r--unix/tclUnixFile.c41
-rw-r--r--unix/tclUnixInit.c523
-rw-r--r--unix/tclUnixTest.c30
-rw-r--r--win/tclWinFile.c20
-rw-r--r--win/tclWinInit.c338
-rw-r--r--win/tclWinPipe.c8
21 files changed, 1556 insertions, 1157 deletions
diff --git a/ChangeLog b/ChangeLog
index b0b9570..f8fc143 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,76 @@
+2004-11-30 Don Porter <dgp@users.sourceforge.net>
+
+ 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.
+
2004-11-30 Kevin B. Kenny <kennykb@acm.org>
* library/clock.tcl: Corrected the regular expressions that match
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index a37dff5..d4c9382 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclBasic.c,v 1.135 2004/11/13 00:19:06 dgp Exp $
+ * RCS: @(#) $Id: tclBasic.c,v 1.136 2004/11/30 19:34:46 dgp Exp $
*/
#include "tclInt.h"
@@ -183,7 +183,7 @@ Tcl_CreateInterp()
ByteCodeStats *statsPtr;
#endif /* TCL_COMPILE_STATS */
- TclInitSubsystems(NULL);
+ TclInitSubsystems();
/*
* Panic if someone updated the CallFrame structure without
diff --git a/generic/tclEncoding.c b/generic/tclEncoding.c
index b24bb0e..c3f53c3 100644
--- a/generic/tclEncoding.c
+++ b/generic/tclEncoding.c
@@ -8,7 +8,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclEncoding.c,v 1.26 2004/11/12 23:42:17 hobbs Exp $
+ * RCS: @(#) $Id: tclEncoding.c,v 1.27 2004/11/30 19:34:47 dgp Exp $
*/
#include "tclInt.h"
@@ -135,12 +135,35 @@ typedef struct EscapeEncodingData {
#define ENCODING_ESCAPE 3
/*
- * Initialize the default encoding directory. If this variable contains
- * a non NULL value, it will be the first path used to locate the
- * system encoding files.
+ * A list of directories in which Tcl should look for *.enc files.
+ * This list is shared by all threads. Access is governed by a
+ * mutex lock.
*/
-char *tclDefaultEncodingDir = NULL;
+static TclInitProcessGlobalValueProc InitializeEncodingSearchPath;
+static ProcessGlobalValue encodingSearchPath =
+ {0, 0, NULL, NULL, InitializeEncodingSearchPath, NULL, NULL};
+
+/*
+ * A map from encoding names to the directories in which their data
+ * files have been seen. The string value of the map is shared by all
+ * threads. Access to the shared string is governed by a mutex lock.
+ */
+
+static TclInitProcessGlobalValueProc InitializeEncodingFileMap;
+static ProcessGlobalValue encodingFileMap =
+ {0, 0, NULL, NULL, InitializeEncodingFileMap, NULL, NULL};
+
+/*
+ * A list of directories making up the "library path". Historically
+ * this search path has served many uses, but the only one remaining
+ * is a base for the encodingSearchPath above. If the application
+ * does not explicitly set the encodingSearchPath, then it will be
+ * initialized by appending /encoding to each directory in this
+ * "libraryPath".
+ */
+static ProcessGlobalValue libraryPath =
+ {0, 0, NULL, NULL, TclpInitLibraryPath, NULL, NULL};
static int encodingsInitialized = 0;
@@ -188,6 +211,7 @@ static int EscapeToUtfProc _ANSI_ARGS_((ClientData clientData,
Tcl_EncodingState *statePtr, char *dst, int dstLen,
int *srcReadPtr, int *dstWrotePtr,
int *dstCharsPtr));
+static void FillEncodingFileMap ();
static void FreeEncoding _ANSI_ARGS_((Tcl_Encoding encoding));
static Encoding * GetTableEncoding _ANSI_ARGS_((
EscapeEncodingData *dataPtr, int state));
@@ -197,8 +221,7 @@ static Tcl_Encoding LoadTableEncoding _ANSI_ARGS_((CONST char *name,
int type, Tcl_Channel chan));
static Tcl_Encoding LoadEscapeEncoding _ANSI_ARGS_((CONST char *name,
Tcl_Channel chan));
-static Tcl_Channel OpenEncodingFile _ANSI_ARGS_((CONST char *dir,
- CONST char *name));
+static Tcl_Obj * MakeFileMap ();
static void TableFreeProc _ANSI_ARGS_((ClientData clientData));
static int TableFromUtfProc _ANSI_ARGS_((ClientData clientData,
CONST char *src, int srcLen, int flags,
@@ -236,10 +259,198 @@ static int UtfExtToUtfIntProc _ANSI_ARGS_((ClientData clientData,
Tcl_EncodingState *statePtr, char *dst, int dstLen,
int *srcReadPtr, int *dstWrotePtr,
int *dstCharsPtr));
-static int FindEncodings();
/*
+ *----------------------------------------------------------------------
+ *
+ * TclGetEncodingSearchPath --
+ *
+ * Keeps the per-thread copy of the encoding search path current
+ * with changes to the global copy.
+ *
+ * Results:
+ * Returns a "list" (Tcl_Obj *) that contains the encoding
+ * search path.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+TclGetEncodingSearchPath() {
+ return TclGetProcessGlobalValue(&encodingSearchPath);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclSetEncodingSearchPath --
+ *
+ * Keeps the per-thread copy of the encoding search path current
+ * with changes to the global copy.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclSetEncodingSearchPath(searchPath)
+ Tcl_Obj *searchPath;
+{
+ int dummy;
+
+ if (TCL_ERROR == Tcl_ListObjLength(NULL, searchPath, &dummy)) {
+ return TCL_ERROR;
+ }
+ TclSetProcessGlobalValue(&encodingSearchPath, searchPath);
+ FillEncodingFileMap();
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclGetLibraryPath --
+ *
+ * Keeps the per-thread copy of the library path current
+ * with changes to the global copy.
+ *
+ * Results:
+ * Returns a "list" (Tcl_Obj *) that contains the library path.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+TclGetLibraryPath() {
+ return TclGetProcessGlobalValue(&libraryPath);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclSetLibraryPath --
+ *
+ * Keeps the per-thread copy of the library path current
+ * with changes to the global copy.
+ *
+ * NOTE: this routine returns void, so there's no way to
+ * report the error that searchPath is not a valid list.
+ * In that case, this routine will silently do nothing.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclSetLibraryPath(path)
+ Tcl_Obj *path;
+{
+ int dummy;
+
+ if (TCL_ERROR == Tcl_ListObjLength(NULL, path, &dummy)) {
+ return;
+ }
+ TclSetProcessGlobalValue(&libraryPath, path);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MakeFileMap --
+ *
+ * Scan the directories on the encoding search path, find the
+ * *.enc files, and store the found pathnames in a map associated
+ * with the encoding name.
+ *
+ * In particular, if $dir is on the encoding search path, and the
+ * file $dir/foo.enc is found, then store a "foo" -> $dir entry
+ * in the map. Later, any need for the "foo" encoding will quickly
+ * be able to construct the $dir/foo.enc pathname for reading the
+ * encoding data.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Entries are added to the encoding file map.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+MakeFileMap()
+{
+ int i, numDirs = 0;
+ Tcl_Obj *map, *searchPath;
+
+ searchPath = TclGetEncodingSearchPath();
+ Tcl_IncrRefCount(searchPath);
+ Tcl_ListObjLength(NULL, searchPath, &numDirs);
+ map = Tcl_NewDictObj();
+ Tcl_IncrRefCount(map);
+ for (i = numDirs-1; i >= 0; i--) {
+ /*
+ * Iterate backwards through the search path so as we
+ * overwrite entries found, we favor files earlier on
+ * the search path.
+ */
+ int j, numFiles;
+ Tcl_Obj *directory, *matchFileList = Tcl_NewObj();
+ Tcl_Obj **filev;
+ Tcl_GlobTypeData readableFiles =
+ {TCL_GLOB_TYPE_FILE, TCL_GLOB_PERM_R, NULL, NULL};
+
+ Tcl_ListObjIndex(NULL, searchPath, i, &directory);
+ Tcl_IncrRefCount(directory);
+ Tcl_IncrRefCount(matchFileList);
+ Tcl_FSMatchInDirectory(NULL, matchFileList,
+ directory, "*.enc", &readableFiles);
+
+ Tcl_ListObjGetElements(NULL, matchFileList, &numFiles, &filev);
+ for (j=0; j<numFiles; j++) {
+ Tcl_Obj *encodingName, *file;
+
+ file = TclPathPart(NULL, filev[j], TCL_PATH_TAIL);
+ Tcl_IncrRefCount(file);
+ encodingName = TclPathPart(NULL, file, TCL_PATH_ROOT);
+ Tcl_IncrRefCount(encodingName);
+ Tcl_DictObjPut(NULL, map, encodingName, directory);
+ }
+ Tcl_DecrRefCount(matchFileList);
+ Tcl_DecrRefCount(directory);
+ }
+ Tcl_DecrRefCount(searchPath);
+ return map;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FillEncodingFileMap --
+ *
+ * Called to bring the encoding file map in sync with the current
+ * value of the encoding search path.
+ *
+ * TODO: Check the callers of this routine to see if it's called
+ * too frequently.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Entries are added to the encoding file map.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+FillEncodingFileMap()
+{
+ Tcl_Obj *map = MakeFileMap();
+ TclSetProcessGlobalValue(&encodingFileMap, map);
+ Tcl_DecrRefCount(map);
+}
+
+/*
*---------------------------------------------------------------------------
*
* TclInitEncodingSubsystem --
@@ -261,6 +472,10 @@ TclInitEncodingSubsystem()
{
Tcl_EncodingType type;
+ if (encodingsInitialized) {
+ return;
+ }
+
Tcl_MutexLock(&encodingMutex);
Tcl_InitHashTable(&encodingTable, TCL_STRING_KEYS);
Tcl_MutexUnlock(&encodingMutex);
@@ -296,8 +511,10 @@ TclInitEncodingSubsystem()
type.nullSize = 2;
type.clientData = NULL;
Tcl_CreateEncoding(&type);
-}
+ encodingsInitialized = 1;
+
+}
/*
*----------------------------------------------------------------------
@@ -344,10 +561,15 @@ TclFinalizeEncodingSubsystem()
*
* Tcl_GetDefaultEncodingDir --
*
+ * Legacy public interface to retrieve first directory in the
+ * encoding searchPath.
*
* Results:
+ * The directory pathname, as a string, or NULL for an empty
+ * encoding search path.
*
* Side effects:
+ * None.
*
*-------------------------------------------------------------------------
*/
@@ -355,7 +577,16 @@ TclFinalizeEncodingSubsystem()
CONST char *
Tcl_GetDefaultEncodingDir()
{
- return tclDefaultEncodingDir;
+ int numDirs;
+ Tcl_Obj *first, *searchPath = TclGetEncodingSearchPath();
+
+ Tcl_ListObjLength(NULL, searchPath, &numDirs);
+ if (numDirs == 0) {
+ return NULL;
+ }
+ Tcl_ListObjIndex(NULL, searchPath, 0, &first);
+
+ return Tcl_GetString(first);
}
/*
@@ -363,10 +594,14 @@ Tcl_GetDefaultEncodingDir()
*
* Tcl_SetDefaultEncodingDir --
*
+ * Legacy public interface to set the first directory in the
+ * encoding search path.
*
* Results:
+ * None.
*
* Side effects:
+ * Modifies the encoding search path.
*
*-------------------------------------------------------------------------
*/
@@ -375,8 +610,12 @@ void
Tcl_SetDefaultEncodingDir(path)
CONST char *path;
{
- tclDefaultEncodingDir = (char *)ckalloc((unsigned) strlen(path) + 1);
- strcpy(tclDefaultEncodingDir, path);
+ Tcl_Obj *searchPath = TclGetEncodingSearchPath();
+ Tcl_Obj *directory = Tcl_NewStringObj(path, -1);
+
+ searchPath = Tcl_DuplicateObj(searchPath);
+ Tcl_ListObjReplace(NULL, searchPath, 0, 0, 1, &directory);
+ TclSetEncodingSearchPath(searchPath);
}
/*
@@ -551,95 +790,42 @@ void
Tcl_GetEncodingNames(interp)
Tcl_Interp *interp; /* Interp to hold result. */
{
+ Tcl_HashTable table;
Tcl_HashSearch search;
Tcl_HashEntry *hPtr;
- Tcl_Obj *pathPtr, *resultPtr;
- int dummy;
+ Tcl_Obj *map, *name, *result = Tcl_NewObj();
+ Tcl_DictSearch mapSearch;
+ int dummy, done = 0;
- Tcl_HashTable table;
+ Tcl_InitObjHashTable(&table);
+ /* Copy encoding names from loaded encoding table to table */
Tcl_MutexLock(&encodingMutex);
- Tcl_InitHashTable(&table, TCL_STRING_KEYS);
- hPtr = Tcl_FirstHashEntry(&encodingTable, &search);
- while (hPtr != NULL) {
- Encoding *encodingPtr;
-
- encodingPtr = (Encoding *) Tcl_GetHashValue(hPtr);
- Tcl_CreateHashEntry(&table, encodingPtr->name, &dummy);
- hPtr = Tcl_NextHashEntry(&search);
+ for (hPtr = Tcl_FirstHashEntry(&encodingTable, &search); hPtr != NULL;
+ hPtr = Tcl_NextHashEntry(&search)) {
+ Encoding *encodingPtr = (Encoding *) Tcl_GetHashValue(hPtr);
+ Tcl_CreateHashEntry(&table,
+ (char *) Tcl_NewStringObj(encodingPtr->name, -1), &dummy);
}
Tcl_MutexUnlock(&encodingMutex);
- pathPtr = TclGetLibraryPath();
- if (pathPtr != NULL) {
- int i, objc;
- Tcl_Obj **objv;
- char globArgString[10];
- Tcl_Obj* encodingObj = Tcl_NewStringObj("encoding",-1);
- Tcl_IncrRefCount(encodingObj);
-
- objc = 0;
- Tcl_ListObjGetElements(NULL, pathPtr, &objc, &objv);
+ FillEncodingFileMap();
+ map = TclGetProcessGlobalValue(&encodingFileMap);
- for (i = 0; i < objc; i++) {
- Tcl_Obj *searchIn;
-
- /*
- * Construct the path from the element of pathPtr,
- * joined with 'encoding'.
- */
- searchIn = Tcl_FSJoinToPath(objv[i],1,&encodingObj);
- Tcl_IncrRefCount(searchIn);
- Tcl_ResetResult(interp);
-
- /*
- * TclGlob() changes the contents of globArgString, which causes
- * a segfault if we pass in a pointer to non-writeable memory.
- * TclGlob() puts its results directly into interp.
- */
-
- strcpy(globArgString, "*.enc");
- /*
- * The GLOBMODE_TAILS flag returns just the tail of each file
- * which is the encoding name with a .enc extension
- */
- if ((TclGlob(interp, globArgString, searchIn,
- TCL_GLOBMODE_TAILS, NULL) == TCL_OK)) {
- int objc2 = 0;
- Tcl_Obj **objv2;
- int j;
-
- Tcl_ListObjGetElements(NULL, Tcl_GetObjResult(interp), &objc2,
- &objv2);
-
- for (j = 0; j < objc2; j++) {
- int length;
- char *string;
- string = Tcl_GetStringFromObj(objv2[j], &length);
- length -= 4;
- if (length > 0) {
- string[length] = '\0';
- Tcl_CreateHashEntry(&table, string, &dummy);
- string[length] = '.';
- }
- }
- }
- Tcl_DecrRefCount(searchIn);
- }
- Tcl_DecrRefCount(encodingObj);
+ /* Copy encoding names from encoding file map to table */
+ Tcl_DictObjFirst(NULL, map, &mapSearch, &name, NULL, &done);
+ for (; !done; Tcl_DictObjNext(&mapSearch, &name, NULL, &done)) {
+ Tcl_CreateHashEntry(&table, (char *) name, &dummy);
}
- resultPtr = Tcl_NewObj();
- hPtr = Tcl_FirstHashEntry(&table, &search);
- while (hPtr != NULL) {
- Tcl_Obj *strPtr;
-
- strPtr = Tcl_NewStringObj(Tcl_GetHashKey(&table, hPtr), -1);
- Tcl_ListObjAppendElement(NULL, resultPtr, strPtr);
- hPtr = Tcl_NextHashEntry(&search);
+ /* Pull all encoding names from table into the result list */
+ for (hPtr = Tcl_FirstHashEntry(&table, &search); hPtr != NULL;
+ hPtr = Tcl_NextHashEntry(&search)) {
+ Tcl_ListObjAppendElement(NULL, result,
+ (Tcl_Obj *) Tcl_GetHashKey(&table, hPtr));
}
+ Tcl_SetObjResult(interp, result);
Tcl_DeleteHashTable(&table);
- Tcl_SetObjResult(interp, resultPtr);
}
/*
@@ -1105,9 +1291,9 @@ Tcl_UtfToExternal(interp, encoding, src, srcLen, flags, statePtr, dst,
* None.
*
* Side effects:
- * The variable tclExecutableName gets filled in with the file
+ * The variable tclNativeExecutableName gets filled in with the file
* name for the application, if we figured it out. If we couldn't
- * figure it out, tclExecutableName is set to NULL.
+ * figure it out, tclNativeExecutableName is set to NULL.
*
*---------------------------------------------------------------------------
*/
@@ -1117,59 +1303,9 @@ Tcl_FindExecutable(argv0)
CONST char *argv0; /* The value of the application's argv[0]
* (native). */
{
- int mustCleanUtf;
- CONST char *name;
- Tcl_DString buffer, nameString;
-
- TclInitSubsystems(argv0);
-
- if (argv0 == NULL) {
- goto done;
- }
- if (tclExecutableName != NULL) {
- ckfree(tclExecutableName);
- tclExecutableName = NULL;
- }
- if ((name = TclpFindExecutable(argv0)) == NULL) {
- goto done;
- }
-
- /*
- * The value returned from TclpNameOfExecutable is a UTF string that
- * is possibly dirty depending on when it was initialized.
- * FindEncodings will indicate whether we must "clean" the UTF (as
- * reported by the underlying system). To assure that the UTF string
- * is a properly encoded native string for this system, convert the
- * UTF string to the default native encoding before the default
- * encoding is initialized. Then, convert it back to UTF after the
- * system encoding is loaded.
- */
-
- Tcl_UtfToExternalDString(NULL, name, -1, &buffer);
- mustCleanUtf = FindEncodings();
-
- /*
- * Now it is OK to convert the native string back to UTF and set
- * the value of the tclExecutableName.
- */
-
- if (mustCleanUtf) {
- Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(&buffer), -1,
- &nameString);
- tclExecutableName = (char *)
- ckalloc((unsigned) (Tcl_DStringLength(&nameString) + 1));
- strcpy(tclExecutableName, Tcl_DStringValue(&nameString));
-
- Tcl_DStringFree(&nameString);
- } else {
- tclExecutableName = (char *) ckalloc((unsigned) (strlen(name) + 1));
- strcpy(tclExecutableName, name);
- }
- Tcl_DStringFree(&buffer);
- return;
-
- done:
- (void) FindEncodings();
+ TclInitSubsystems();
+ TclpSetInitialEncodings();
+ TclpFindExecutable(argv0);
}
/*
@@ -1198,28 +1334,42 @@ LoadEncodingFile(interp, name)
CONST char *name; /* The name of the encoding file on disk
* and also the name for new encoding. */
{
- int objc, i, ch;
- Tcl_Obj **objv;
- Tcl_Obj *pathPtr;
Tcl_Channel chan;
Tcl_Encoding encoding;
+ Tcl_Obj *map, *path, *directory = NULL;
+ Tcl_Obj *nameObj = Tcl_NewStringObj(name, -1);
+ int ch, scanned = 0;
- pathPtr = TclGetLibraryPath();
- if (pathPtr == NULL) {
- goto unknown;
- }
- objc = 0;
- Tcl_ListObjGetElements(NULL, pathPtr, &objc, &objv);
- chan = NULL;
- for (i = 0; i < objc; i++) {
- chan = OpenEncodingFile(Tcl_GetString(objv[i]), name);
- if (chan != NULL) {
+ Tcl_IncrRefCount(nameObj);
+ while (1) {
+ map = TclGetProcessGlobalValue(&encodingFileMap);
+ Tcl_DictObjGet(NULL, map, nameObj, &directory);
+ if (scanned || (NULL != directory)) {
break;
}
+scan:
+ FillEncodingFileMap();
+ scanned = 1;
+ }
+ if (NULL == directory) {
+ Tcl_DecrRefCount(nameObj);
+ goto unknown;
}
- if (chan == NULL) {
+ /* Construct $directory/$encoding.enc path name */
+ Tcl_IncrRefCount(directory);
+ Tcl_AppendToObj(nameObj, ".enc", -1);
+ path = Tcl_FSJoinToPath(directory, 1, &nameObj);
+ Tcl_DecrRefCount(directory);
+ Tcl_IncrRefCount(path);
+ chan = Tcl_FSOpenFileChannel(NULL, path, "r", 0);
+ Tcl_DecrRefCount(path);
+
+ if (NULL == chan) {
+ if (!scanned) {
+ goto scan;
+ }
goto unknown;
}
@@ -1270,53 +1420,6 @@ LoadEncodingFile(interp, name)
}
/*
- *----------------------------------------------------------------------
- *
- * OpenEncodingFile --
- *
- * Look for the file encoding/<name>.enc in the specified
- * directory.
- *
- * Results:
- * Returns an open file channel if the file exists.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-static Tcl_Channel
-OpenEncodingFile(dir, name)
- CONST char *dir;
- CONST char *name;
-
-{
- CONST char *argv[3];
- Tcl_DString pathString;
- CONST char *path;
- Tcl_Channel chan;
- Tcl_Obj *pathPtr;
-
- argv[0] = dir;
- argv[1] = "encoding";
- argv[2] = name;
-
- Tcl_DStringInit(&pathString);
- Tcl_JoinPath(3, argv, &pathString);
- path = Tcl_DStringAppend(&pathString, ".enc", -1);
- pathPtr = Tcl_NewStringObj(path,-1);
-
- Tcl_IncrRefCount(pathPtr);
- chan = Tcl_FSOpenFileChannel(NULL, pathPtr, "r", 0);
- Tcl_DecrRefCount(pathPtr);
-
- Tcl_DStringFree(&pathString);
-
- return chan;
-}
-
-/*
*-------------------------------------------------------------------------
*
* LoadTableEncoding --
@@ -2932,91 +3035,108 @@ unilen(src)
}
return (char *) p - src;
}
-
/*
*-------------------------------------------------------------------------
*
- * FindEncodings --
+ * InitializeEncodingSearchPath --
*
- * Find and load the encoding file for this operating system.
- * Before this is called, Tcl makes assumptions about the
- * native string representation, but the true encoding is not
- * assured.
+ * This is the fallback routine that sets the default value
+ * of the encoding search path if the application has not set
+ * one via a call to TclSetEncodingSearchPath() by the first
+ * time the search path is needed to load encoding data.
+ *
+ * The default encoding search path is produced by taking each
+ * directory in the library path, appending a subdirectory
+ * named "encoding", and if the resulting directory exists,
+ * adding it to the encoding search path.
*
* Results:
- * Return result of TclpInitLibraryPath, which reports whether the
- * path is clean (0) or dirty (1) UTF.
+ * None.
*
* Side effects:
- * Varied, see the respective initialization routines.
+ * Sets the encoding search path to an initial value.
*
*-------------------------------------------------------------------------
*/
-static int
-FindEncodings()
+void
+InitializeEncodingSearchPath(valuePtr, lengthPtr, encodingPtr)
+ char **valuePtr;
+ int *lengthPtr;
+ Tcl_Encoding *encodingPtr;
{
- int mustCleanUtf = 0;
-
- if (encodingsInitialized == 0) {
- /*
- * Double check inside the mutex. There may be calls
- * back into this routine from some of the procedures below.
- */
-
- TclpInitLock();
- if (encodingsInitialized == 0) {
- Tcl_Obj *pathPtr;
- Tcl_DString libPath, buffer;
-
- /*
- * Have to set this bit here to avoid deadlock with the
- * routines below us that call into TclInitSubsystems.
- */
-
- encodingsInitialized = 1;
-
- /*
- * NOTE: we can safely make direct use of tclNativeExecutableName
- * because we know all our callers ( Tcl_FindExecutable() is the
- * only one) have already called TclpFindExecutable().
- */
-
- mustCleanUtf = TclpInitLibraryPath(tclNativeExecutableName);
-
- /*
- * The library path was set in the TclpInitLibraryPath routine.
- * The string set is a dirty UTF string. To preserve the value
- * convert the UTF string back to native before setting the new
- * default encoding.
- */
-
- pathPtr = TclGetLibraryPath();
- if ((pathPtr != NULL) && mustCleanUtf) {
- Tcl_UtfToExternalDString(NULL, Tcl_GetString(pathPtr), -1,
- &libPath);
- }
-
- TclpSetInitialEncodings();
-
- /*
- * Now convert the native string back to UTF.
- */
-
- if ((pathPtr != NULL) && mustCleanUtf) {
- Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(&libPath), -1,
- &buffer);
- pathPtr = Tcl_NewStringObj(Tcl_DStringValue(&buffer), -1);
- TclSetLibraryPath(pathPtr);
-
- Tcl_DStringFree(&libPath);
- Tcl_DStringFree(&buffer);
- }
+ char *bytes;
+ int i, numDirs, numBytes;
+ Tcl_Obj *libPath, *encodingObj = Tcl_NewStringObj("encoding", -1);
+ Tcl_Obj *searchPath = Tcl_NewObj();
+
+ Tcl_IncrRefCount(encodingObj);
+ Tcl_IncrRefCount(searchPath);
+ libPath = TclGetLibraryPath();
+ Tcl_IncrRefCount(libPath);
+ Tcl_ListObjLength(NULL, libPath, &numDirs);
+ for (i = 0; i < numDirs; i++) {
+ Tcl_Obj *directory, *path;
+ Tcl_StatBuf stat;
+
+ Tcl_ListObjIndex(NULL, libPath, i, &directory);
+ path = Tcl_FSJoinToPath(directory, 1, &encodingObj);
+ Tcl_IncrRefCount(path);
+ if ((0 == Tcl_FSStat(path, &stat)) && S_ISDIR(stat.st_mode)) {
+ Tcl_ListObjAppendElement(NULL, searchPath, path);
}
- TclpInitUnlock();
- }
-
- return mustCleanUtf;
+ Tcl_IncrRefCount(path);
+ }
+ Tcl_DecrRefCount(libPath);
+ Tcl_DecrRefCount(encodingObj);
+ *encodingPtr = libraryPath.encoding;
+ if (*encodingPtr) {
+ ((Encoding *)(*encodingPtr))->refCount++;
+ }
+ bytes = Tcl_GetStringFromObj(searchPath, &numBytes);
+ *lengthPtr = numBytes;
+ *valuePtr = ckalloc((unsigned int) numBytes + 1);
+ memcpy((VOID *) *valuePtr, (VOID *) bytes, (size_t) numBytes + 1);
+ Tcl_DecrRefCount(searchPath);
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * InitializeEncodingFileMap --
+ *
+ * This is the fallback routine that fills the encoding data
+ * file map if the application has not set up an encoding
+ * search path by the first time the file map is needed to
+ * load encoding data.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Fills the encoding data file map.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+void
+InitializeEncodingFileMap(valuePtr, lengthPtr, encodingPtr)
+ char **valuePtr;
+ int *lengthPtr;
+ Tcl_Encoding *encodingPtr;
+{
+ char *bytes;
+ int numBytes;
+ Tcl_Obj *map = MakeFileMap();
+
+ *encodingPtr = encodingSearchPath.encoding;
+ if (*encodingPtr) {
+ ((Encoding *)(*encodingPtr))->refCount++;
+ }
+ bytes = Tcl_GetStringFromObj(map, &numBytes);
+ *lengthPtr = numBytes;
+ *valuePtr = ckalloc((unsigned int) numBytes + 1);
+ memcpy((VOID *) *valuePtr, (VOID *) bytes, (size_t) numBytes + 1);
+ Tcl_DecrRefCount(map);
}
-
diff --git a/generic/tclEvent.c b/generic/tclEvent.c
index 57ec9b7..f180f26 100644
--- a/generic/tclEvent.c
+++ b/generic/tclEvent.c
@@ -12,7 +12,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclEvent.c,v 1.52 2004/11/18 20:15:32 dgp Exp $
+ * RCS: @(#) $Id: tclEvent.c,v 1.53 2004/11/30 19:34:47 dgp Exp $
*/
#include "tclInt.h"
@@ -98,17 +98,9 @@ typedef struct ThreadSpecificData {
int inExit; /* True when this thread is exiting. This
* is used as a hack to decide to close
* the standard channels. */
- Tcl_Obj *tclLibraryPath; /* Path(s) to the Tcl library */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
-/*
- * Common string for the library path for sharing across threads.
- * This is ckalloc'd and cleared in Tcl_Finalize.
- */
-static char *tclLibraryPathStr = NULL;
-
-
#ifdef TCL_THREADS
typedef struct {
@@ -747,92 +739,6 @@ Tcl_Exit(status)
/*
*-------------------------------------------------------------------------
- *
- * TclSetLibraryPath --
- *
- * Set the path that will be used for searching for init.tcl and
- * encodings when an interp is being created.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Changing the library path will affect what directories are
- * examined when looking for encodings for all interps from that
- * point forward.
- *
- * The refcount of the new library path is incremented and the
- * refcount of the old path is decremented.
- *
- *-------------------------------------------------------------------------
- */
-
-void
-TclSetLibraryPath(pathPtr)
- Tcl_Obj *pathPtr; /* A Tcl list object whose elements are
- * the new library path. */
-{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- const char *toDupe;
- int size;
-
- if (pathPtr != NULL) {
- Tcl_IncrRefCount(pathPtr);
- }
- if (tsdPtr->tclLibraryPath != NULL) {
- Tcl_DecrRefCount(tsdPtr->tclLibraryPath);
- }
- tsdPtr->tclLibraryPath = pathPtr;
-
- /*
- * No mutex locking is needed here as up the stack we're within
- * TclpInitLock().
- */
- if (tclLibraryPathStr != NULL) {
- ckfree(tclLibraryPathStr);
- }
- toDupe = Tcl_GetStringFromObj(pathPtr, &size);
- tclLibraryPathStr = ckalloc((unsigned)size+1);
- memcpy(tclLibraryPathStr, toDupe, (unsigned)size+1);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * TclGetLibraryPath --
- *
- * Return a Tcl list object whose elements are the library path.
- * The caller should not modify the contents of the returned object.
- *
- * Results:
- * As above.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-Tcl_Obj *
-TclGetLibraryPath()
-{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- if (tsdPtr->tclLibraryPath == NULL) {
- /*
- * Grab the shared string and place it into a new thread specific
- * Tcl_Obj.
- */
- tsdPtr->tclLibraryPath = Tcl_NewStringObj(tclLibraryPathStr, -1);
-
- /* take ownership */
- Tcl_IncrRefCount(tsdPtr->tclLibraryPath);
- }
- return tsdPtr->tclLibraryPath;
-}
-
-/*
- *-------------------------------------------------------------------------
*
* TclInitSubsystems --
*
@@ -858,25 +764,12 @@ TclGetLibraryPath()
*/
void
-TclInitSubsystems(argv0)
- CONST char *argv0; /* Name of executable from argv[0] to main()
- * in native multi-byte encoding. */
+TclInitSubsystems()
{
- ThreadSpecificData *tsdPtr;
-
if (inFinalize != 0) {
Tcl_Panic("TclInitSubsystems called while finalizing");
}
- /*
- * Grab the thread local storage pointer before doing anything because
- * the initialization routines will be registering exit handlers.
- * We use this pointer to detect if this is the first time this
- * thread has created an interpreter.
- */
-
- tsdPtr = (ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
-
if (subsystemsInitialized == 0) {
/*
* Double check inside the mutex. There are definitly calls
@@ -892,8 +785,6 @@ TclInitSubsystems(argv0)
subsystemsInitialized = 1;
- tclExecutableName = NULL;
-
/*
* Initialize locks used by the memory allocators before anything
* interesting happens so we can use the allocators in the
@@ -914,17 +805,7 @@ TclInitSubsystems(argv0)
}
TclpInitUnlock();
}
-
- if (tsdPtr == NULL) {
- /*
- * First time this thread has created an interpreter.
- * We fetch the key again just in case no exit handlers were
- * registered by this point.
- */
-
- (void) TCL_TSD_INIT(&dataKey);
- TclInitNotifier();
- }
+ TclInitNotifier();
}
/*
@@ -1017,22 +898,10 @@ Tcl_Finalize()
*/
TclFinalizeEncodingSubsystem();
- if (tclExecutableName != NULL) {
- ckfree(tclExecutableName);
- tclExecutableName = NULL;
- }
if (tclNativeExecutableName != NULL) {
ckfree(tclNativeExecutableName);
tclNativeExecutableName = NULL;
}
- if (tclDefaultEncodingDir != NULL) {
- ckfree(tclDefaultEncodingDir);
- tclDefaultEncodingDir = NULL;
- }
- if (tclLibraryPathStr != NULL) {
- ckfree(tclLibraryPathStr);
- tclLibraryPathStr = NULL;
- }
Tcl_SetPanicProc(NULL);
@@ -1113,16 +982,6 @@ Tcl_FinalizeThread()
if (tsdPtr != NULL) {
tsdPtr->inExit = 1;
- /*
- * Clean up the library path now, before we invalidate thread-local
- * storage or calling thread exit handlers.
- */
-
- if (tsdPtr->tclLibraryPath != NULL) {
- Tcl_DecrRefCount(tsdPtr->tclLibraryPath);
- tsdPtr->tclLibraryPath = NULL;
- }
-
for (exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL;
exitPtr = tsdPtr->firstExitPtr) {
/*
diff --git a/generic/tclIO.c b/generic/tclIO.c
index b41a7b6..bb6c438 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -10,7 +10,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclIO.c,v 1.80 2004/11/09 15:47:26 dkf Exp $
+ * RCS: @(#) $Id: tclIO.c,v 1.81 2004/11/30 19:34:47 dgp Exp $
*/
#include "tclInt.h"
@@ -3556,8 +3556,7 @@ Tcl_GetsObj(chan, objPtr)
/*
* If there is no encoding, use "iso8859-1" -- Tcl_GetsObj() doesn't
- * produce ByteArray objects. To avoid circularity problems,
- * "iso8859-1" is builtin to Tcl.
+ * produce ByteArray objects.
*/
if (encoding == NULL) {
diff --git a/generic/tclInt.decls b/generic/tclInt.decls
index 3b5dc5d..d6cdd1f 100644
--- a/generic/tclInt.decls
+++ b/generic/tclInt.decls
@@ -12,7 +12,7 @@
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
-# RCS: @(#) $Id: tclInt.decls,v 1.82 2004/10/27 17:13:58 davygrvy Exp $
+# RCS: @(#) $Id: tclInt.decls,v 1.83 2004/11/30 19:34:48 dgp Exp $
library tcl
@@ -841,6 +841,15 @@ declare 208 generic {
Tcl_Channel TclpOpenFileChannel (Tcl_Interp *interp,
Tcl_Obj *pathPtr, int mode, int permissions)
}
+declare 209 generic {
+ Tcl_Obj * TclGetEncodingSearchPath(void)
+}
+declare 210 generic {
+ int TclSetEncodingSearchPath(Tcl_Obj *searchPath)
+}
+declare 211 generic {
+ CONST char * TclpGetEncodingNameFromEnvironment(Tcl_DString *bufPtr)
+}
##############################################################################
diff --git a/generic/tclInt.h b/generic/tclInt.h
index a4ac12e..4510239 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -12,7 +12,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclInt.h,v 1.200 2004/11/17 17:53:01 dgp Exp $
+ * RCS: @(#) $Id: tclInt.h,v 1.201 2004/11/30 19:34:48 dgp Exp $
*/
#ifndef _TCLINT
@@ -1688,14 +1688,46 @@ typedef Tcl_ObjCmdProc *TclObjCmdProcType;
/*
*----------------------------------------------------------------
+ * Data structures for process-global values.
+ *----------------------------------------------------------------
+ */
+
+typedef void (TclInitProcessGlobalValueProc) _ANSI_ARGS_((char **valuePtr,
+ int *lengthPtr, Tcl_Encoding *encodingPtr));
+
+/*
+ * A ProcessGlobalValue struct exists for each internal value in
+ * Tcl that is to be shared among several threads. Each thread
+ * sees a (Tcl_Obj) copy of the value, and the master is kept as
+ * a counted string, with epoch and mutex control. Each ProcessGlobalValue
+ * struct should be a static variable in some file.
+ */
+typedef struct ProcessGlobalValue {
+ int epoch; /* Epoch counter to detect changes
+ * in the master value */
+ int numBytes; /* Length of the master string */
+ char *value; /* The master string value */
+ Tcl_Encoding encoding; /* system encoding when master string
+ * was initialized */
+ TclInitProcessGlobalValueProc *proc;
+ /* A procedure to initialize the
+ * master string copy when a "get"
+ * request comes in before any
+ * "set" request has been received. */
+ Tcl_Mutex mutex; /* Enforce orderly access from
+ * multiple threads */
+ Tcl_ThreadDataKey key; /* Key for per-thread data holding
+ * the (Tcl_Obj) copy for each thread */
+} ProcessGlobalValue;
+
+/*
+ *----------------------------------------------------------------
* Variables shared among Tcl modules but not used by the outside world.
*----------------------------------------------------------------
*/
-MODULE_SCOPE char * tclExecutableName;
MODULE_SCOPE char * tclNativeExecutableName;
MODULE_SCOPE int tclFindExecutableSearchDone;
-MODULE_SCOPE char * tclDefaultEncodingDir;
MODULE_SCOPE char * tclMemDumpFileName;
MODULE_SCOPE TclPlatformType tclPlatform;
MODULE_SCOPE Tcl_NotifierProcs tclOriginalNotifier;
@@ -1802,6 +1834,8 @@ MODULE_SCOPE void TclFinalizeSynchronization _ANSI_ARGS_((void));
MODULE_SCOPE void TclFinalizeLock _ANSI_ARGS_((void));
MODULE_SCOPE void TclFinalizeThreadData _ANSI_ARGS_((void));
MODULE_SCOPE Tcl_Obj * TclGetBgErrorHandler _ANSI_ARGS_((Tcl_Interp *interp));
+MODULE_SCOPE Tcl_Obj * TclGetProcessGlobalValue _ANSI_ARGS_ ((
+ ProcessGlobalValue *pgvPtr));
MODULE_SCOPE int TclGlob _ANSI_ARGS_((Tcl_Interp *interp,
char *pattern, Tcl_Obj *unquotedPrefix,
int globFlags, Tcl_GlobTypeData* types));
@@ -1815,7 +1849,7 @@ MODULE_SCOPE void TclInitLimitSupport _ANSI_ARGS_((Tcl_Interp *interp));
MODULE_SCOPE void TclInitNamespaceSubsystem _ANSI_ARGS_((void));
MODULE_SCOPE void TclInitNotifier _ANSI_ARGS_((void));
MODULE_SCOPE void TclInitObjSubsystem _ANSI_ARGS_((void));
-MODULE_SCOPE void TclInitSubsystems _ANSI_ARGS_((CONST char *argv0));
+MODULE_SCOPE void TclInitSubsystems ();
MODULE_SCOPE int TclIsLocalScalar _ANSI_ARGS_((CONST char *src,
int len));
MODULE_SCOPE int TclJoinThread _ANSI_ARGS_((Tcl_ThreadId id,
@@ -1879,11 +1913,12 @@ MODULE_SCOPE int TclpThreadCreate _ANSI_ARGS_((
int stackSize, int flags));
MODULE_SCOPE void TclpFinalizeThreadDataKey _ANSI_ARGS_((
Tcl_ThreadDataKey *keyPtr));
-MODULE_SCOPE char * TclpFindExecutable _ANSI_ARGS_((
+MODULE_SCOPE void TclpFindExecutable _ANSI_ARGS_((
CONST char *argv0));
MODULE_SCOPE int TclpFindVariable _ANSI_ARGS_((CONST char *name,
int *lengthPtr));
-MODULE_SCOPE int TclpInitLibraryPath _ANSI_ARGS_((CONST char *argv0));
+MODULE_SCOPE void TclpInitLibraryPath _ANSI_ARGS_((char **valuePtr,
+ int *lengthPtr, Tcl_Encoding *encodingPtr));
MODULE_SCOPE void TclpInitLock _ANSI_ARGS_((void));
MODULE_SCOPE void TclpInitPlatform _ANSI_ARGS_((void));
MODULE_SCOPE void TclpInitUnlock _ANSI_ARGS_((void));
@@ -1951,6 +1986,8 @@ MODULE_SCOPE void TclRemoveScriptLimitCallbacks _ANSI_ARGS_((
Tcl_Interp *interp));
MODULE_SCOPE void TclSetBgErrorHandler _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Obj *cmdPrefix));
+MODULE_SCOPE void TclSetProcessGlobalValue _ANSI_ARGS_ ((
+ ProcessGlobalValue *pgvPtr, Tcl_Obj *newValue));
MODULE_SCOPE VOID TclSignalExitThread _ANSI_ARGS_((Tcl_ThreadId id,
int result));
MODULE_SCOPE int TclSubstTokens _ANSI_ARGS_((Tcl_Interp *interp,
diff --git a/generic/tclIntDecls.h b/generic/tclIntDecls.h
index eba2fa4..69ddc6a 100644
--- a/generic/tclIntDecls.h
+++ b/generic/tclIntDecls.h
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclIntDecls.h,v 1.73 2004/11/03 19:13:38 davygrvy Exp $
+ * RCS: @(#) $Id: tclIntDecls.h,v 1.74 2004/11/30 19:34:49 dgp Exp $
*/
#ifndef _TCLINTDECLS
@@ -1085,6 +1085,23 @@ EXTERN int TclpObjAccess _ANSI_ARGS_((Tcl_Obj * pathPtr,
EXTERN Tcl_Channel TclpOpenFileChannel _ANSI_ARGS_((Tcl_Interp * interp,
Tcl_Obj * pathPtr, int mode, int permissions));
#endif
+#ifndef TclGetEncodingSearchPath_TCL_DECLARED
+#define TclGetEncodingSearchPath_TCL_DECLARED
+/* 209 */
+EXTERN Tcl_Obj * TclGetEncodingSearchPath _ANSI_ARGS_((void));
+#endif
+#ifndef TclSetEncodingSearchPath_TCL_DECLARED
+#define TclSetEncodingSearchPath_TCL_DECLARED
+/* 210 */
+EXTERN int TclSetEncodingSearchPath _ANSI_ARGS_((
+ Tcl_Obj * searchPath));
+#endif
+#ifndef TclpGetEncodingNameFromEnvironment_TCL_DECLARED
+#define TclpGetEncodingNameFromEnvironment_TCL_DECLARED
+/* 211 */
+EXTERN CONST char * TclpGetEncodingNameFromEnvironment _ANSI_ARGS_((
+ Tcl_DString * bufPtr));
+#endif
typedef struct TclIntStubs {
int magic;
@@ -1314,6 +1331,9 @@ typedef struct TclIntStubs {
int (*tclpObjStat) _ANSI_ARGS_((Tcl_Obj * pathPtr, Tcl_StatBuf * buf)); /* 206 */
int (*tclpObjAccess) _ANSI_ARGS_((Tcl_Obj * pathPtr, int mode)); /* 207 */
Tcl_Channel (*tclpOpenFileChannel) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Obj * pathPtr, int mode, int permissions)); /* 208 */
+ Tcl_Obj * (*tclGetEncodingSearchPath) _ANSI_ARGS_((void)); /* 209 */
+ int (*tclSetEncodingSearchPath) _ANSI_ARGS_((Tcl_Obj * searchPath)); /* 210 */
+ CONST char * (*tclpGetEncodingNameFromEnvironment) _ANSI_ARGS_((Tcl_DString * bufPtr)); /* 211 */
} TclIntStubs;
#ifdef __cplusplus
@@ -2037,6 +2057,18 @@ extern TclIntStubs *tclIntStubsPtr;
#define TclpOpenFileChannel \
(tclIntStubsPtr->tclpOpenFileChannel) /* 208 */
#endif
+#ifndef TclGetEncodingSearchPath
+#define TclGetEncodingSearchPath \
+ (tclIntStubsPtr->tclGetEncodingSearchPath) /* 209 */
+#endif
+#ifndef TclSetEncodingSearchPath
+#define TclSetEncodingSearchPath \
+ (tclIntStubsPtr->tclSetEncodingSearchPath) /* 210 */
+#endif
+#ifndef TclpGetEncodingNameFromEnvironment
+#define TclpGetEncodingNameFromEnvironment \
+ (tclIntStubsPtr->tclpGetEncodingNameFromEnvironment) /* 211 */
+#endif
#endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */
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;
}
/*
diff --git a/generic/tclNotify.c b/generic/tclNotify.c
index 3f62d1a..e5a438f 100644
--- a/generic/tclNotify.c
+++ b/generic/tclNotify.c
@@ -14,7 +14,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclNotify.c,v 1.15 2004/07/15 21:17:03 vasiljevic Exp $
+ * RCS: @(#) $Id: tclNotify.c,v 1.16 2004/11/30 19:34:49 dgp Exp $
*/
#include "tclInt.h"
@@ -81,7 +81,7 @@ static Tcl_ThreadDataKey dataKey;
* be replaced with a hashtable.
*/
-static ThreadSpecificData *firstNotifierPtr;
+static ThreadSpecificData *firstNotifierPtr = NULL;
TCL_DECLARE_MUTEX(listLock)
/*
@@ -111,15 +111,22 @@ static void QueueEvent _ANSI_ARGS_((ThreadSpecificData *tsdPtr,
void
TclInitNotifier()
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ ThreadSpecificData *tsdPtr;
+ Tcl_ThreadId threadId = Tcl_GetCurrentThread();
Tcl_MutexLock(&listLock);
-
- tsdPtr->threadId = Tcl_GetCurrentThread();
- tsdPtr->clientData = tclStubs.tcl_InitNotifier();
- tsdPtr->nextPtr = firstNotifierPtr;
- firstNotifierPtr = tsdPtr;
-
+ for (tsdPtr = firstNotifierPtr; tsdPtr && tsdPtr->threadId != threadId;
+ tsdPtr = tsdPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ if (NULL == tsdPtr) {
+ /* Notifier not yet initialized in this thread */
+ tsdPtr = TCL_TSD_INIT(&dataKey);
+ tsdPtr->threadId = threadId;
+ tsdPtr->clientData = tclStubs.tcl_InitNotifier();
+ tsdPtr->nextPtr = firstNotifierPtr;
+ firstNotifierPtr = tsdPtr;
+ }
Tcl_MutexUnlock(&listLock);
}
diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c
index bfa7d86..c1c2dd0 100644
--- a/generic/tclStubInit.c
+++ b/generic/tclStubInit.c
@@ -8,7 +8,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclStubInit.c,v 1.107 2004/11/13 00:19:10 dgp Exp $
+ * RCS: @(#) $Id: tclStubInit.c,v 1.108 2004/11/30 19:34:49 dgp Exp $
*/
#include "tclInt.h"
@@ -293,6 +293,9 @@ TclIntStubs tclIntStubs = {
TclpObjStat, /* 206 */
TclpObjAccess, /* 207 */
TclpOpenFileChannel, /* 208 */
+ TclGetEncodingSearchPath, /* 209 */
+ TclSetEncodingSearchPath, /* 210 */
+ TclpGetEncodingNameFromEnvironment, /* 211 */
};
TclIntPlatStubs tclIntPlatStubs = {
diff --git a/generic/tclTest.c b/generic/tclTest.c
index c8006d4..c2f9dd0 100644
--- a/generic/tclTest.c
+++ b/generic/tclTest.c
@@ -14,7 +14,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclTest.c,v 1.85 2004/10/07 22:01:18 dgp Exp $
+ * RCS: @(#) $Id: tclTest.c,v 1.86 2004/11/30 19:34:50 dgp Exp $
*/
#define TCL_TEST
@@ -1818,9 +1818,9 @@ TestencodingObjCmd(dummy, interp, objc, objv)
}
case ENC_PATH: {
if (objc == 2) {
- Tcl_SetObjResult(interp, TclGetLibraryPath());
+ Tcl_SetObjResult(interp, TclGetEncodingSearchPath());
} else {
- TclSetLibraryPath(objv[2]);
+ TclSetEncodingSearchPath(objv[2]);
}
break;
}
diff --git a/generic/tclUtil.c b/generic/tclUtil.c
index 068a20b..9737c4a 100644
--- a/generic/tclUtil.c
+++ b/generic/tclUtil.c
@@ -11,23 +11,39 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclUtil.c,v 1.48 2004/10/14 15:06:03 dkf Exp $
+ * RCS: @(#) $Id: tclUtil.c,v 1.49 2004/11/30 19:34:50 dgp Exp $
*/
#include "tclInt.h"
/*
- * The following variable holds the full path name of the binary
+ * The following variables hold the full path name of the binary
* from which this application was executed, or NULL if it isn't
- * know. The value of the variable is set by the procedure
- * Tcl_FindExecutable. The storage space is dynamically allocated.
+ * know. The values are set by the procedure Tcl_FindExecutable.
+ * Only the first call to Tcl_FindExecutable sets the value. That
+ * call also sets the "searchDone" flag, so that subsequent calls
+ * are no-ops. With that logic in place, no mutex protection is
+ * required. The storage space is dynamically allocated. The value
+ * is kept in the system encoding.
*/
-char *tclExecutableName = NULL;
char *tclNativeExecutableName = NULL;
int tclFindExecutableSearchDone = 0;
/*
+ * A copy of the executable path name, converted to Tcl's internal
+ * encoding, UTF-8. Also keep a copy of what the system encoding
+ * was believed to be when the conversion was done, just in case
+ * it's changed on us. Because Tcl_GetNameOfExecutable() is in
+ * the public API, it might be called from any thread, so we need
+ * mutex protection here.
+ */
+
+TCL_DECLARE_MUTEX(executableNameMutex)
+static char *executableName = NULL;
+static Tcl_Encoding conversionEncoding = NULL;
+
+/*
* The following values are used in the flags returned by Tcl_ScanElement
* and used by Tcl_ConvertElement. The values TCL_DONT_USE_BRACES and
* TCL_DONT_QUOTE_HASH are defined in tcl.h; make sure neither value
@@ -73,9 +89,16 @@ TCL_DECLARE_MUTEX(precisionMutex)
* Prototypes for procedures defined later in this file.
*/
-static void UpdateStringOfEndOffset _ANSI_ARGS_((Tcl_Obj* objPtr));
-static int SetEndOffsetFromAny _ANSI_ARGS_((Tcl_Interp* interp,
+static void ClearHash _ANSI_ARGS_((Tcl_HashTable *tablePtr));
+static void FreeExecutableName _ANSI_ARGS_((ClientData));
+static void FreeProcessGlobalValue _ANSI_ARGS_((
+ ClientData clientData));
+static void FreeThreadHash _ANSI_ARGS_ ((ClientData clientData));
+static Tcl_HashTable * GetThreadHash _ANSI_ARGS_ ((Tcl_ThreadDataKey *keyPtr));
+static int SetEndOffsetFromAny _ANSI_ARGS_((Tcl_Interp* interp,
Tcl_Obj* objPtr));
+static void UpdateStringOfEndOffset _ANSI_ARGS_((Tcl_Obj* objPtr));
+static Tcl_Obj * Tcl_GetObjNameOfExecutable();
/*
* The following is the Tcl object type definition for an object
@@ -2564,6 +2587,239 @@ TclCheckBadOctal(interp, value)
/*
*----------------------------------------------------------------------
*
+ * ClearHash --
+ * Remove all the entries in the hash table *tablePtr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ClearHash(tablePtr)
+ Tcl_HashTable *tablePtr;
+{
+ Tcl_HashSearch search;
+ Tcl_HashEntry *hPtr;
+
+ for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL;
+ hPtr = Tcl_NextHashEntry(&search)) {
+ Tcl_Obj *objPtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr);
+ Tcl_DecrRefCount(objPtr);
+ Tcl_DeleteHashEntry(hPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetThreadHash --
+ *
+ * Get a thread-specific (Tcl_HashTable *) associated with a
+ * thread data key.
+ *
+ * Results:
+ * The Tcl_HashTable * corresponding to *keyPtr.
+ *
+ * Side effects:
+ * The first call on a keyPtr in each thread creates a new
+ * Tcl_HashTable, and registers a thread exit handler to
+ * dispose of it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_HashTable *
+GetThreadHash(keyPtr)
+ Tcl_ThreadDataKey *keyPtr;
+{
+ Tcl_HashTable **tablePtrPtr = (Tcl_HashTable **)
+ Tcl_GetThreadData(keyPtr, (int)sizeof(Tcl_HashTable *));
+ if (NULL == *tablePtrPtr) {
+ *tablePtrPtr = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
+ Tcl_CreateThreadExitHandler(FreeThreadHash, (ClientData)*tablePtrPtr);
+ Tcl_InitHashTable(*tablePtrPtr, TCL_ONE_WORD_KEYS);
+ }
+ return *tablePtrPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeThreadHash --
+ * Thread exit handler used by GetThreadHash to dispose
+ * of a thread hash table.
+ *
+ * Side effects:
+ * Frees a Tcl_HashTable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeThreadHash(clientData)
+ ClientData clientData;
+{
+ Tcl_HashTable *tablePtr = (Tcl_HashTable *) clientData;
+ Tcl_DeleteHashTable(tablePtr);
+ ckfree((char *) tablePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeProcessGlobalValue --
+ * Exit handler used by Tcl(Set|Get)ProcessGlobalValue to cleanup
+ * a ProcessGlobalValue at exit.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeProcessGlobalValue(clientData)
+ ClientData clientData;
+{
+ ProcessGlobalValue *pgvPtr = (ProcessGlobalValue *) clientData;
+ pgvPtr->epoch++;
+ pgvPtr->numBytes = 0;
+ ckfree(pgvPtr->value);
+ pgvPtr->value = NULL;
+ if (pgvPtr->encoding) {
+ Tcl_FreeEncoding(pgvPtr->encoding);
+ pgvPtr->encoding = NULL;
+ }
+ Tcl_MutexFinalize(&pgvPtr->mutex);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclSetProcessGlobalValue --
+ *
+ * Utility routine to set a global value shared by all threads in
+ * the process while keeping a thread-local copy as well.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+TclSetProcessGlobalValue(pgvPtr, newValue)
+ ProcessGlobalValue *pgvPtr;
+ Tcl_Obj *newValue;
+{
+ CONST char *bytes;
+ Tcl_HashTable *cacheMap;
+ Tcl_HashEntry *hPtr;
+ int dummy;
+
+ Tcl_MutexLock(&pgvPtr->mutex);
+ /* Fill the global string value */
+ pgvPtr->epoch++;
+ if (NULL != pgvPtr->value) {
+ ckfree(pgvPtr->value);
+ } else {
+ Tcl_CreateExitHandler(FreeProcessGlobalValue, (ClientData) pgvPtr);
+ }
+ bytes = Tcl_GetStringFromObj(newValue, &pgvPtr->numBytes);
+ pgvPtr->value = ckalloc((unsigned int) pgvPtr->numBytes + 1);
+ strcpy(pgvPtr->value, bytes);
+ if (pgvPtr->encoding) {
+ Tcl_FreeEncoding(pgvPtr->encoding);
+ pgvPtr->encoding = NULL;
+ }
+
+ /*
+ * Fill the local thread copy directly with the Tcl_Obj
+ * value to avoid loss of the intrep
+ */
+ cacheMap = GetThreadHash(&pgvPtr->key);
+ ClearHash(cacheMap);
+ hPtr = Tcl_CreateHashEntry(cacheMap, (char *)pgvPtr->epoch, &dummy);
+ Tcl_SetHashValue(hPtr, (ClientData) newValue);
+ Tcl_IncrRefCount(newValue);
+ Tcl_MutexUnlock(&pgvPtr->mutex);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclGetProcessGlobalValue --
+ *
+ * Retrieve a global value shared among all threads of the process,
+ * preferring a thread-local copy as long as it remains valid.
+ *
+ * Results:
+ * Returns a (Tcl_Obj *) that holds a copy of the global value.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+TclGetProcessGlobalValue(pgvPtr)
+ ProcessGlobalValue *pgvPtr;
+{
+ Tcl_Obj *value = NULL;
+ Tcl_HashTable *cacheMap;
+ Tcl_HashEntry *hPtr;
+
+ Tcl_MutexLock(&pgvPtr->mutex);
+ if (pgvPtr->encoding) {
+ Tcl_Encoding current = Tcl_GetEncoding(NULL, NULL);
+ if (pgvPtr->encoding != current) {
+
+ /*
+ * The system encoding has changed since the master
+ * string value was saved. Convert the master value
+ * to be based on the new system encoding.
+ */
+
+ Tcl_DString native, newValue;
+
+ pgvPtr->epoch++;
+ Tcl_UtfToExternalDString(pgvPtr->encoding, pgvPtr->value,
+ pgvPtr->numBytes, &native);
+ Tcl_ExternalToUtfDString(current, Tcl_DStringValue(&native),
+ Tcl_DStringLength(&native), &newValue);
+ Tcl_DStringFree(&native);
+ ckfree(pgvPtr->value);
+ pgvPtr->value = ckalloc((unsigned int)
+ Tcl_DStringLength(&newValue) + 1);
+ memcpy((VOID *) pgvPtr->value, (VOID *) Tcl_DStringValue(&newValue),+ (size_t) Tcl_DStringLength(&newValue) + 1);
+ Tcl_DStringFree(&newValue);
+ Tcl_FreeEncoding(pgvPtr->encoding);
+ pgvPtr->encoding = current;
+ } else {
+ Tcl_FreeEncoding(current);
+ }
+ }
+ cacheMap = GetThreadHash(&pgvPtr->key);
+ hPtr = Tcl_FindHashEntry(cacheMap, (char *)pgvPtr->epoch);
+ if (NULL == hPtr) {
+ int dummy;
+
+ /* No cache for the current epoch - must be a new one */
+ /* First, clear the cacheMap, as anything in it must
+ * refer to some expired epoch.*/
+ ClearHash(cacheMap);
+
+ /* If no thread has set the shared value, call the initializer */
+ if (NULL == pgvPtr->value) {
+ (*(pgvPtr->proc))(&pgvPtr->value, &pgvPtr->numBytes,
+ &pgvPtr->encoding);
+ Tcl_CreateExitHandler(FreeProcessGlobalValue, (ClientData) pgvPtr);
+ }
+
+
+ /* Store a copy of the shared value in our epoch-indexed cache */
+ value = Tcl_NewStringObj(pgvPtr->value, pgvPtr->numBytes);
+ hPtr = Tcl_CreateHashEntry(cacheMap, (char *)pgvPtr->epoch, &dummy);
+ Tcl_SetHashValue(hPtr, (ClientData) value);
+ Tcl_IncrRefCount(value);
+ }
+ Tcl_MutexUnlock(&pgvPtr->mutex);
+ return (Tcl_Obj *) Tcl_GetHashValue(hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_GetNameOfExecutable --
*
* This procedure simply returns a pointer to the internal full
@@ -2571,6 +2827,8 @@ TclCheckBadOctal(interp, value)
* Tcl_FindExecutable. This procedure call is the C API
* equivalent to the "info nameofexecutable" command.
*
+ * TODO: Rework these routines to use a ProcessGlobalValue.
+ *
* Results:
* A pointer to the internal string or NULL if the internal full
* path name has not been computed or unknown.
@@ -2582,12 +2840,68 @@ TclCheckBadOctal(interp, value)
*----------------------------------------------------------------------
*/
+static void
+FreeExecutableName(clientData)
+ ClientData clientData;
+{
+ Tcl_FreeEncoding(conversionEncoding);
+ conversionEncoding = NULL;
+ if (NULL != executableName) {
+ ckfree(executableName);
+ }
+ executableName = NULL;
+}
+
+static Tcl_Obj *
+Tcl_GetObjNameOfExecutable()
+{
+ Tcl_Obj *result;
+
+ Tcl_MutexLock(&executableNameMutex);
+ if (NULL == conversionEncoding) {
+ /* First call (after free) */
+ conversionEncoding = Tcl_GetEncoding(NULL, NULL);
+ Tcl_CreateExitHandler(FreeExecutableName, NULL);
+ } else {
+ /* Later call... */
+ Tcl_Encoding systemEncoding = Tcl_GetEncoding(NULL, NULL);
+ if (systemEncoding != conversionEncoding) {
+ /* ...with system encoding changed */
+ FreeExecutableName(NULL);
+ conversionEncoding = systemEncoding;
+ } else {
+ Tcl_FreeEncoding(systemEncoding);
+ }
+ }
+ if (NULL == tclNativeExecutableName) {
+ FreeExecutableName(NULL);
+ } else if (NULL == executableName) {
+ Tcl_DString ds;
+ Tcl_ExternalToUtfDString(conversionEncoding,
+ tclNativeExecutableName, -1, &ds);
+ executableName = (char *)
+ ckalloc ((unsigned) Tcl_DStringLength(&ds) + 1);
+ strcpy(executableName, Tcl_DStringValue(&ds));
+ Tcl_DStringFree(&ds);
+ }
+
+ if (NULL == executableName) {
+ result = Tcl_NewObj();
+ } else {
+ result = Tcl_NewStringObj(executableName, -1);
+ }
+ Tcl_MutexUnlock(&executableNameMutex);
+ return result;
+}
+
CONST char *
Tcl_GetNameOfExecutable()
{
- return tclExecutableName;
+ Tcl_DecrRefCount(Tcl_GetObjNameOfExecutable());
+ return executableName;
}
+
/*
*----------------------------------------------------------------------
*
diff --git a/tests/encoding.test b/tests/encoding.test
index 97ae787..897bebf 100644
--- a/tests/encoding.test
+++ b/tests/encoding.test
@@ -8,7 +8,7 @@
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
-# RCS: @(#) $Id: encoding.test,v 1.20 2004/05/07 20:01:23 rmax Exp $
+# RCS: @(#) $Id: encoding.test,v 1.21 2004/11/30 19:34:51 dgp Exp $
package require tcltest 2
namespace import -force ::tcltest::*
@@ -95,7 +95,7 @@ test encoding-4.1 {Tcl_GetEncodingNames} {testencoding} {
foreach encoding [encoding names] {
set encodings($encoding) 1
}
- testencoding path [list [pwd]]
+ testencoding path [list [file join [pwd] encoding]]
foreach encoding [encoding names] {
if {![info exists encodings($encoding)]} {
lappend x $encoding
@@ -237,7 +237,7 @@ test encoding-11.6 {LoadEncodingFile: invalid file} {testencoding} {
set path [testencoding path]
encoding system identity
cd [temporaryDirectory]
- testencoding path tmp
+ testencoding path [file join tmp encoding]
makeDirectory tmp
makeDirectory [file join tmp encoding]
set f [open [file join tmp encoding splat.enc] w]
diff --git a/tests/unixInit.test b/tests/unixInit.test
index 22840fb..f42c868 100644
--- a/tests/unixInit.test
+++ b/tests/unixInit.test
@@ -10,7 +10,7 @@
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
-# RCS: @(#) $Id: unixInit.test,v 1.43 2004/11/22 21:24:31 dgp Exp $
+# RCS: @(#) $Id: unixInit.test,v 1.44 2004/11/30 19:34:51 dgp Exp $
package require tcltest 2
namespace import -force ::tcltest::*
@@ -113,8 +113,15 @@ test unixInit-2.0 {TclpInitLibraryPath: setting tclDefaultEncodingDir} \
testsetdefenc $origDir
set path
} {slappy}
-test unixInit-2.1 {TclpInitLibraryPath: value of installLib, developLib} \
- {unix stdio } {
+test unixInit-2.1 {TclpInitLibraryPath: value of installLib, developLib} -constraints {
+ unix stdio
+} -setup {
+ unset -nocomplain oldlibrary
+ if {[info exists env(TCL_LIBRARY)]} {
+ set oldlibrary $env(TCL_LIBRARY)
+ unset env(TCL_LIBRARY)
+ }
+} -body {
set path [getlibpath]
set installLib lib/tcl[info tclversion]
@@ -122,13 +129,19 @@ test unixInit-2.1 {TclpInitLibraryPath: value of installLib, developLib} \
set prefix [file dirname [file dirname [interpreter]]]
set x {}
- lappend x [string compare [lindex $path 2] $prefix/$installLib]
- lappend x [string compare [lindex $path 6] [file dirname $prefix]/$developLib]
+ lappend x [string compare [lindex $path 0] $prefix/$installLib]
+ lappend x [string compare [lindex $path 4] [file dirname $prefix]/$developLib]
set x
-} {0 0}
+} -cleanup {
+ if {[info exists oldlibrary]} {
+ set env(TCL_LIBRARY) $oldlibrary
+ unset oldlibrary
+ }
+} -result {0 0}
test unixInit-2.2 {TclpInitLibraryPath: TCL_LIBRARY} -constraints {
unix stdio
} -setup {
+ unset -nocomplain oldlibrary
if {[info exists env(TCL_LIBRARY)]} {
set oldlibrary $env(TCL_LIBRARY)
}
@@ -149,6 +162,7 @@ test unixInit-2.2 {TclpInitLibraryPath: TCL_LIBRARY} -constraints {
test unixInit-2.3 {TclpInitLibraryPath: TCL_LIBRARY wrong version} -constraints {
unix stdio
} -setup {
+ unset -nocomplain oldlibrary
if {[info exists env(TCL_LIBRARY)]} {
set oldlibrary $env(TCL_LIBRARY)
}
@@ -167,7 +181,7 @@ test unixInit-2.3 {TclpInitLibraryPath: TCL_LIBRARY wrong version} -constraints
}
} -result [list /a/b/tcl1.7 /a/b/tcl[info tclversion]]
test unixInit-2.4 {TclpInitLibraryPath: TCL_LIBRARY: INTL} -constraints {
- unix stdio
+ unix stdio knownBug
} -setup {
if {[info exists env(TCL_LIBRARY)]} {
set oldlibrary $env(TCL_LIBRARY)
@@ -191,8 +205,14 @@ test unixInit-2.5 {TclpInitLibraryPath: compiled-in library path} \
{emptyTest unix} {
# cannot test
} {}
-test unixInit-2.6 {TclpInitLibraryPath: executable relative} \
- {unix stdio } {
+test unixInit-2.6 {TclpInitLibraryPath: executable relative} -constraints {
+ unix stdio
+} -setup {
+ unset -nocomplain oldlibrary
+ if {[info exists env(TCL_LIBRARY)]} {
+ set oldlibrary $env(TCL_LIBRARY)
+ }
+ set env(TCL_LIBRARY) [info library]
makeDirectory tmp
makeDirectory [file join tmp sparkly]
makeDirectory [file join tmp sparkly bin]
@@ -201,17 +221,23 @@ test unixInit-2.6 {TclpInitLibraryPath: executable relative} \
makeDirectory [file join tmp sparkly lib]
makeDirectory [file join tmp sparkly lib tcl[info tclversion]]
makeFile {} [file join tmp sparkly lib tcl[info tclversion] init.tcl]
-
- set x [lrange [getlibpath [file join [temporaryDirectory] tmp sparkly \
- bin tcltest]] 2 3]
+} -body {
+ lrange [getlibpath [file join [temporaryDirectory] tmp sparkly \
+ bin tcltest]] 1 2
+} -cleanup {
removeFile [file join tmp sparkly lib tcl[info tclversion] init.tcl]
removeDirectory [file join tmp sparkly lib tcl[info tclversion]]
removeDirectory [file join tmp sparkly lib]
removeDirectory [file join tmp sparkly bin]
removeDirectory [file join tmp sparkly]
removeDirectory tmp
- set x
-} [list [temporaryDirectory]/tmp/sparkly/lib/tcl[info tclversion] [temporaryDirectory]/tmp/lib/tcl[info tclversion]]
+ unset env(TCL_LIBRARY)
+ if {[info exists oldlibrary]} {
+ set env(TCL_LIBRARY) $oldlibrary
+ unset oldlibrary
+ }
+} -result [list [temporaryDirectory]/tmp/sparkly/lib/tcl[info tclversion] [temporaryDirectory]/tmp/lib/tcl[info tclversion]]
+
test unixInit-2.7 {TclpInitLibraryPath: compiled-in library path} \
{emptyTest unix} {
# would need test command to get defaultLibDir and compare it to
@@ -226,7 +252,14 @@ test unixInit-2.7 {TclpInitLibraryPath: compiled-in library path} \
testConstraint noSparkly [expr {![file exists [file join /tmp sparkly]]}]
testConstraint noTmpInstall [expr {![file exists \
[file join /tmp lib tcl[info tclversion]]]}]
-test unixInit-2.8 {TclpInitLibraryPath: all absolute pathtype} {unix noSparkly noTmpInstall } {
+test unixInit-2.8 {TclpInitLibraryPath: all absolute pathtype} -constraints {
+ unix noSparkly noTmpInstall
+} -setup {
+ unset -nocomplain oldlibrary
+ if {[info exists env(TCL_LIBRARY)]} {
+ set oldlibrary $env(TCL_LIBRARY)
+ }
+ set env(TCL_LIBRARY) [info library]
# Checking for Bug 219416
# When a program that embeds the Tcl library, like tcltest, is
# installed near the "root" of the file system, there was a problem
@@ -261,6 +294,7 @@ test unixInit-2.8 {TclpInitLibraryPath: all absolute pathtype} {unix noSparkly n
# ../lib/tcl$version relative to the executable.
file mkdir /tmp/lib/tcl[info tclversion]
close [open /tmp/lib/tcl[info tclversion]/init.tcl w]
+} -body {
# Check that all directories in the library path are absolute pathnames
set allAbsolute 1
@@ -268,16 +302,29 @@ test unixInit-2.8 {TclpInitLibraryPath: all absolute pathtype} {unix noSparkly n
set allAbsolute [expr {$allAbsolute \
&& [string equal absolute [file pathtype $dir]]}]
}
+ set allAbsolute
+} -cleanup {
# Clean up temporary installation
file delete -force /tmp/sparkly
file delete -force /tmp/lib/tcl[info tclversion]
if {$deletelib} {file delete -force /tmp/lib}
- set allAbsolute
-} 1
+ unset env(TCL_LIBRARY)
+ if {[info exists oldlibrary]} {
+ set env(TCL_LIBRARY) $oldlibrary
+ unset oldlibrary
+ }
+} -result 1
testConstraint noTmpBuild [expr {![file exists [file join /tmp library]]}]
-test unixInit-2.9 {TclpInitLibraryPath: paths relative to executable} {unix noSparkly noTmpBuild } {
+test unixInit-2.9 {TclpInitLibraryPath: paths relative to executable} -constraints {
+ unix noSparkly noTmpBuild
+} -setup {
# Checking for Bug 438014
+ unset -nocomplain oldlibrary
+ if {[info exists env(TCL_LIBRARY)]} {
+ set oldlibrary $env(TCL_LIBRARY)
+ }
+ set env(TCL_LIBRARY) [info library]
file delete -force /tmp/sparkly
file delete -force /tmp/library
file mkdir /tmp/sparkly
@@ -285,18 +332,27 @@ test unixInit-2.9 {TclpInitLibraryPath: paths relative to executable} {unix noSp
file mkdir /tmp/library/
close [open /tmp/library/init.tcl w]
-
- set x [lrange [getlibpath /tmp/sparkly/tcltest] 2 6]
-
+} -body {
+ lrange [getlibpath /tmp/sparkly/tcltest] 1 5
+} -cleanup {
file delete -force /tmp/sparkly
file delete -force /tmp/library
- set x
-} [list /tmp/lib/tcl[info tclversion] /lib/tcl[info tclversion] \
+ unset env(TCL_LIBRARY)
+ if {[info exists oldlibrary]} {
+ set env(TCL_LIBRARY) $oldlibrary
+ unset oldlibrary
+ }
+} -result [list /tmp/lib/tcl[info tclversion] /lib/tcl[info tclversion] \
/tmp/library /library /tcl[info patchlevel]/library]
test unixInit-2.10 {TclpInitLibraryPath: executable relative} -constraints {
unix stdio
} -setup {
+ unset -nocomplain oldlibrary
+ if {[info exists env(TCL_LIBRARY)]} {
+ set oldlibrary $env(TCL_LIBRARY)
+ }
+ set env(TCL_LIBRARY) [info library]
set tmpDir [makeDirectory tmp]
set sparklyDir [makeDirectory sparkly $tmpDir]
set execPath [file join [makeDirectory bin $sparklyDir] tcltest]
@@ -308,7 +364,7 @@ test unixInit-2.10 {TclpInitLibraryPath: executable relative} -constraints {
cd $libDir
} -body {
# Checking for Bug 832657
- set x [lrange [getlibpath [file join .. bin tcltest]] 4 5]
+ set x [lrange [getlibpath [file join .. bin tcltest]] 3 4]
foreach p $x {
lappend y [file normalize $p]
}
@@ -329,6 +385,11 @@ test unixInit-2.10 {TclpInitLibraryPath: executable relative} -constraints {
unset tmpDir
removeDirectory tmp
unset x p y
+ unset env(TCL_LIBRARY)
+ if {[info exists oldlibrary]} {
+ set env(TCL_LIBRARY) $oldlibrary
+ unset oldlibrary
+ }
} -result [list [file join [temporaryDirectory] tmp sparkly library] \
[file join [temporaryDirectory] tmp library] ]
diff --git a/unix/tclUnixFile.c b/unix/tclUnixFile.c
index 842d1b6..421ea16 100644
--- a/unix/tclUnixFile.c
+++ b/unix/tclUnixFile.c
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclUnixFile.c,v 1.42 2004/10/07 14:50:23 vincentdarley Exp $
+ * RCS: @(#) $Id: tclUnixFile.c,v 1.43 2004/11/30 19:34:51 dgp Exp $
*/
#include "tclInt.h"
@@ -42,7 +42,7 @@ static int NativeMatchType(CONST char* nativeName, Tcl_GlobTypeData *types);
*---------------------------------------------------------------------------
*/
-char *
+void
TclpFindExecutable(argv0)
CONST char *argv0; /* The value of the application's argv[0]
* (native). */
@@ -50,13 +50,13 @@ TclpFindExecutable(argv0)
CONST char *name, *p;
Tcl_StatBuf statBuf;
int length;
- Tcl_DString buffer, nameString;
+ Tcl_DString buffer, nameString, cwd;
if (argv0 == NULL) {
- return NULL;
+ return;
}
if (tclFindExecutableSearchDone) {
- return tclNativeExecutableName;
+ return;
}
tclFindExecutableSearchDone = 1;
@@ -135,7 +135,7 @@ TclpFindExecutable(argv0)
goto done;
/*
- * If the name starts with "/" then just copy it to tclExecutableName.
+ * If the name starts with "/" then just copy it to tclNativeExecutableName.
*/
gotName:
@@ -144,11 +144,9 @@ gotName:
#else
if (name[0] == '/') {
#endif
- Tcl_ExternalToUtfDString(NULL, name, -1, &nameString);
tclNativeExecutableName = (char *)
- ckalloc((unsigned) (Tcl_DStringLength(&nameString) + 1));
- strcpy(tclNativeExecutableName, Tcl_DStringValue(&nameString));
- Tcl_DStringFree(&nameString);
+ ckalloc((unsigned int) strlen(name) + 1);
+ strcpy(tclNativeExecutableName, name);
goto done;
}
@@ -162,22 +160,29 @@ gotName:
name += 2;
}
- Tcl_ExternalToUtfDString(NULL, name, -1, &nameString);
+ Tcl_DStringInit(&nameString);
+ Tcl_DStringAppend(&nameString, name, -1);
+
+ TclpGetCwd(NULL, &cwd);
Tcl_DStringFree(&buffer);
- TclpGetCwd(NULL, &buffer);
+ Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&cwd),
+ Tcl_DStringLength(&cwd), &buffer);
+ if (Tcl_DStringValue(&cwd)[Tcl_DStringLength(&cwd) -1] != '/') {
+ Tcl_DStringAppend(&buffer, "/", 1);
+ }
+ Tcl_DStringFree(&cwd);
+ Tcl_DStringAppend(&buffer, Tcl_DStringValue(&nameString),
+ Tcl_DStringLength(&nameString));
+ Tcl_DStringFree(&nameString);
- length = Tcl_DStringLength(&buffer) + Tcl_DStringLength(&nameString) + 2;
+ length = Tcl_DStringLength(&buffer) + 1;
tclNativeExecutableName = (char *) ckalloc((unsigned) length);
strcpy(tclNativeExecutableName, Tcl_DStringValue(&buffer));
- tclNativeExecutableName[Tcl_DStringLength(&buffer)] = '/';
- strcpy(tclNativeExecutableName + Tcl_DStringLength(&buffer) + 1,
- Tcl_DStringValue(&nameString));
- Tcl_DStringFree(&nameString);
done:
Tcl_DStringFree(&buffer);
- return tclNativeExecutableName;
+ Tcl_GetNameOfExecutable();
}
/*
diff --git a/unix/tclUnixInit.c b/unix/tclUnixInit.c
index 3592c17..4dd3208 100644
--- a/unix/tclUnixInit.c
+++ b/unix/tclUnixInit.c
@@ -7,7 +7,7 @@
* Copyright (c) 1999 by Scriptics Corporation.
* All rights reserved.
*
- * RCS: @(#) $Id: tclUnixInit.c,v 1.52 2004/11/22 22:13:40 dgp Exp $
+ * RCS: @(#) $Id: tclUnixInit.c,v 1.53 2004/11/30 19:34:51 dgp Exp $
*/
#include "tclInt.h"
@@ -93,11 +93,6 @@ static Tcl_ThreadDataKey dataKey;
#define STACK_DEBUG(args) (void)0
#endif /* TCL_DEBUG_STACK_CHECK */
-/* Used to store the encoding used for binary files */
-static Tcl_Encoding binaryEncoding = NULL;
-/* Has the basic library path encoding issue been fixed */
-static int libraryPathEncodingFixed = 0;
-
/*
* Tcl tries to use standard and homebrew methods to guess the right
* encoding on the platform. However, there is always a final fallback,
@@ -137,6 +132,147 @@ typedef struct LocaleTable {
} LocaleTable;
static CONST LocaleTable localeTable[] = {
+ /* First list all the encoding files installed with Tcl */
+ {"ascii", "ascii"},
+ {"big5", "big5"},
+ {"cp1250", "cp1250"},
+ {"cp1251", "cp1251"},
+ {"cp1252", "cp1252"},
+ {"cp1253", "cp1253"},
+ {"cp1254", "cp1254"},
+ {"cp1255", "cp1255"},
+ {"cp1256", "cp1256"},
+ {"cp1257", "cp1257"},
+ {"cp1258", "cp1258"},
+ {"cp437", "cp437"},
+ {"cp737", "cp737"},
+ {"cp775", "cp775"},
+ {"cp850", "cp850"},
+ {"cp852", "cp852"},
+ {"cp855", "cp855"},
+ {"cp857", "cp857"},
+ {"cp860", "cp860"},
+ {"cp861", "cp861"},
+ {"cp862", "cp862"},
+ {"cp863", "cp863"},
+ {"cp864", "cp864"},
+ {"cp865", "cp865"},
+ {"cp866", "cp866"},
+ {"cp869", "cp869"},
+ {"cp874", "cp874"},
+ {"cp932", "cp932"},
+ {"cp936", "cp936"},
+ {"cp949", "cp949"},
+ {"cp950", "cp950"},
+ {"dingbats", "dingbats"},
+ {"ebcdic", "ebcdic"},
+ {"euc-cn", "euc-cn"},
+ {"euc-jp", "euc-jp"},
+ {"euc-kr", "euc-kr"},
+ {"gb12345", "gb12345"},
+ {"gb1988", "gb1988"},
+ {"gb2312-raw", "gb2312-raw"},
+ {"gb2312", "gb2312"},
+ {"iso2022-jp", "iso2022-jp"},
+ {"iso2022-kr", "iso2022-kr"},
+ {"iso2022", "iso2022"},
+ {"iso8859-1", "iso8859-1"},
+ {"iso8859-10", "iso8859-10"},
+ {"iso8859-13", "iso8859-13"},
+ {"iso8859-14", "iso8859-14"},
+ {"iso8859-15", "iso8859-15"},
+ {"iso8859-16", "iso8859-16"},
+ {"iso8859-2", "iso8859-2"},
+ {"iso8859-3", "iso8859-3"},
+ {"iso8859-4", "iso8859-4"},
+ {"iso8859-5", "iso8859-5"},
+ {"iso8859-6", "iso8859-6"},
+ {"iso8859-7", "iso8859-7"},
+ {"iso8859-8", "iso8859-8"},
+ {"iso8859-9", "iso8859-9"},
+ {"jis0201", "jis0201"},
+ {"jis0208", "jis0208"},
+ {"jis0212", "jis0212"},
+ {"koi8-r", "koi8-r"},
+ {"koi8-u", "koi8-u"},
+ {"ksc5601", "ksc5601"},
+ {"macCentEuro", "macCentEuro"},
+ {"macCroatian", "macCroatian"},
+ {"macCyrillic", "macCyrillic"},
+ {"macDingbats", "macDingbats"},
+ {"macGreek", "macGreek"},
+ {"macIceland", "macIceland"},
+ {"macJapan", "macJapan"},
+ {"macRoman", "macRoman"},
+ {"macRomania", "macRomania"},
+ {"macThai", "macThai"},
+ {"macTurkish", "macTurkish"},
+ {"macUkraine", "macUkraine"},
+ {"shiftjis", "shiftjis"},
+ {"symbol", "symbol"},
+ {"tis-620", "tis-620"},
+ /* Next list a few common variants */
+ {"maccenteuro", "macCentEuro"},
+ {"maccroatian", "macCroatian"},
+ {"maccyrillic", "macCyrillic"},
+ {"macdingbats", "macDingbats"},
+ {"macgreek", "macGreek"},
+ {"maciceland", "macIceland"},
+ {"macjapan", "macJapan"},
+ {"macroman", "macRoman"},
+ {"macromania", "macRomania"},
+ {"macthai", "macThai"},
+ {"macturkish", "macTurkish"},
+ {"macukraine", "macUkraine"},
+ {"iso-2022-jp", "iso2022-jp"},
+ {"iso-2022-kr", "iso2022-kr"},
+ {"iso-2022", "iso2022"},
+ {"iso-8859-1", "iso8859-1"},
+ {"iso-8859-10", "iso8859-10"},
+ {"iso-8859-13", "iso8859-13"},
+ {"iso-8859-14", "iso8859-14"},
+ {"iso-8859-15", "iso8859-15"},
+ {"iso-8859-16", "iso8859-16"},
+ {"iso-8859-2", "iso8859-2"},
+ {"iso-8859-3", "iso8859-3"},
+ {"iso-8859-4", "iso8859-4"},
+ {"iso-8859-5", "iso8859-5"},
+ {"iso-8859-6", "iso8859-6"},
+ {"iso-8859-7", "iso8859-7"},
+ {"iso-8859-8", "iso8859-8"},
+ {"iso-8859-9", "iso8859-9"},
+ {"ibm1250", "cp1250"},
+ {"ibm1251", "cp1251"},
+ {"ibm1252", "cp1252"},
+ {"ibm1253", "cp1253"},
+ {"ibm1254", "cp1254"},
+ {"ibm1255", "cp1255"},
+ {"ibm1256", "cp1256"},
+ {"ibm1257", "cp1257"},
+ {"ibm1258", "cp1258"},
+ {"ibm437", "cp437"},
+ {"ibm737", "cp737"},
+ {"ibm775", "cp775"},
+ {"ibm850", "cp850"},
+ {"ibm852", "cp852"},
+ {"ibm855", "cp855"},
+ {"ibm857", "cp857"},
+ {"ibm860", "cp860"},
+ {"ibm861", "cp861"},
+ {"ibm862", "cp862"},
+ {"ibm863", "cp863"},
+ {"ibm864", "cp864"},
+ {"ibm865", "cp865"},
+ {"ibm866", "cp866"},
+ {"ibm869", "cp869"},
+ {"ibm874", "cp874"},
+ {"ibm932", "cp932"},
+ {"ibm936", "cp936"},
+ {"ibm949", "cp949"},
+ {"ibm950", "cp950"},
+ {"", "iso8859-1"},
+ {"ansi_x3.4-1968", "iso8859-1"},
+ /* Finally, the accumulated bug fixes... */
#ifdef HAVE_LANGINFO
{"gb2312-1980", "gb2312"},
#ifdef __hpux
@@ -280,6 +416,25 @@ TclpInitPlatform()
*/
(void) dlopen (NULL, RTLD_NOW); /* INTL: Native. */
#endif
+ /*
+ * Initialize the C library's locale subsystem. This is required
+ * for input methods to work properly on X11. We only do this for
+ * LC_CTYPE because that's the necessary one, and we don't want to
+ * affect LC_TIME here. The side effect of setting the default
+ * locale should be to load any locale specific modules that are
+ * needed by X. [BUG: 5422 3345 4236 2522 2521].
+ */
+
+ setlocale(LC_CTYPE, "");
+
+ /*
+ * In case the initial locale is not "C", ensure that the numeric
+ * processing is done in "C" locale regardless. This is needed because
+ * Tcl relies on routines like strtod, but should not have locale
+ * dependent behavior.
+ */
+
+ setlocale(LC_NUMERIC, "C");
}
/*
@@ -287,47 +442,24 @@ TclpInitPlatform()
*
* TclpInitLibraryPath --
*
- * Initialize the library path at startup. We have a minor
- * metacircular problem that we don't know the encoding of the
- * operating system but we may need to talk to operating system
- * to find the library directories so that we know how to talk to
- * the operating system.
- *
- * We do not know the encoding of the operating system.
- * We do know that the encoding is some multibyte encoding.
- * In that multibyte encoding, the characters 0..127 are equivalent
- * to ascii.
- *
- * So although we don't know the encoding, it's safe:
- * to look for the last slash character in a path in the encoding.
- * to append an ascii string to a path.
- * to pass those strings back to the operating system.
- *
- * But any strings that we remembered before we knew the encoding of
- * the operating system must be translated to UTF-8 once we know the
- * encoding so that the rest of Tcl can use those strings.
- *
- * This call sets the library path to strings in the unknown native
- * encoding. TclpSetInitialEncodings() will translate the library
- * path from the native encoding to UTF-8 as soon as it determines
- * what the native encoding actually is.
- *
- * Called at process initialization time.
+ * This is the fallback routine that sets the library path
+ * if the application has not set one by the first time
+ * it is needed.
*
* Results:
- * Return 1, indicating that the UTF may be dirty and require "cleanup"
- * after encodings are initialized.
+ * None.
*
* Side effects:
- * None.
+ * Sets the library path to an initial value.
*
- *---------------------------------------------------------------------------
- */
+ *-------------------------------------------------------------------------
+ */
-int
-TclpInitLibraryPath(path)
-CONST char *path; /* Path to the executable in native
- * multi-byte encoding. */
+void
+TclpInitLibraryPath(valuePtr, lengthPtr, encodingPtr)
+ char **valuePtr;
+ int *lengthPtr;
+ Tcl_Encoding *encodingPtr;
{
#define LIBRARY_SIZE 32
Tcl_Obj *pathPtr, *objPtr;
@@ -349,16 +481,6 @@ CONST char *path; /* Path to the executable in native
sprintf(installLib, "lib/tcl%s", TCL_VERSION);
/*
- * Look for the library relative to default encoding dir.
- */
-
- str = Tcl_GetDefaultEncodingDir();
- if ((str != NULL) && (str[0] != '\0')) {
- objPtr = Tcl_NewStringObj(str, -1);
- Tcl_ListObjAppendElement(NULL, pathPtr, objPtr);
- }
-
- /*
* Look for the library relative to the TCL_LIBRARY env variable.
* If the last dirname in the TCL_LIBRARY path does not match the
* last dirname in the installLib variable, use the last dir name
@@ -411,6 +533,7 @@ CONST char *path; /* Path to the executable in native
} else
#endif /* HAVE_CFBUNDLE */
{
+ /* TODO: Pull this value from the TIP 59 table */
str = defaultLibraryDir;
}
if (str[0] != '\0') {
@@ -418,11 +541,13 @@ CONST char *path; /* Path to the executable in native
Tcl_ListObjAppendElement(NULL, pathPtr, objPtr);
}
}
-
- TclSetLibraryPath(pathPtr);
Tcl_DStringFree(&buffer);
- return 1; /* 1 indicates that pathPtr may be dirty utf (needs cleaning) */
+ *encodingPtr = Tcl_GetEncoding(NULL, NULL);
+ str = Tcl_GetStringFromObj(pathPtr, lengthPtr);
+ *valuePtr = ckalloc((unsigned int) (*lengthPtr)+1);
+ memcpy((VOID *) *valuePtr, (VOID *) str, (size_t)(*lengthPtr)+1);
+ Tcl_DecrRefCount(pathPtr);
}
/*
@@ -452,223 +577,125 @@ CONST char *path; /* Path to the executable in native
void
TclpSetInitialEncodings()
{
- if (libraryPathEncodingFixed == 0) {
- CONST char *encoding = NULL;
- int i, setSysEncCode = TCL_ERROR;
- Tcl_Obj *pathPtr;
-
- /*
- * Determine the current encoding from the LC_* or LANG environment
- * variables. We previously used setlocale() to determine the locale,
- * but this does not work on some systems (e.g. Linux/i386 RH 5.0).
- */
-#ifdef HAVE_LANGINFO
- if (setlocale(LC_CTYPE, "") != NULL) {
- Tcl_DString ds;
-
- /*
- * Use a DString so we can overwrite it in name compatability
- * checks below.
- */
+ Tcl_DString encodingName;
+ Tcl_SetSystemEncoding(NULL,
+ TclpGetEncodingNameFromEnvironment(&encodingName));
+ Tcl_DStringFree(&encodingName);
+}
- Tcl_DStringInit(&ds);
- encoding = Tcl_DStringAppend(&ds, nl_langinfo(CODESET), -1);
+CONST char *
+TclpGetEncodingNameFromEnvironment(bufPtr)
+ Tcl_DString *bufPtr;
+{
+ CONST char *encoding;
+ int i;
- Tcl_UtfToLower(Tcl_DStringValue(&ds));
-#ifdef HAVE_LANGINFO_DEBUG
- fprintf(stderr, "encoding '%s'", encoding);
-#endif
- if (encoding[0] == 'i' && encoding[1] == 's' && encoding[2] == 'o'
- && encoding[3] == '-') {
- char *p, *q;
- /* need to strip '-' from iso-* encoding */
- for(p = Tcl_DStringValue(&ds)+3, q = Tcl_DStringValue(&ds)+4;
- *p; *p++ = *q++);
- } else if (encoding[0] == 'i' && encoding[1] == 'b'
- && encoding[2] == 'm' && encoding[3] >= '0'
- && encoding[3] <= '9') {
- char *p, *q;
- /* if langinfo reports "ibm*" we should use "cp*" */
- p = Tcl_DStringValue(&ds);
- *p++ = 'c'; *p++ = 'p';
- for(q = p+1; *p ; *p++ = *q++);
- } else if ((*encoding == '\0')
- || !strcmp(encoding, "ansi_x3.4-1968")) {
- /* Use iso8859-1 for empty or 'ansi_x3.4-1968' encoding */
- encoding = "iso8859-1";
- }
-#ifdef HAVE_LANGINFO_DEBUG
- fprintf(stderr, " ?%s?", encoding);
-#endif
- setSysEncCode = Tcl_SetSystemEncoding(NULL, encoding);
- if (setSysEncCode != TCL_OK) {
- /*
- * If this doesn't return TCL_OK, the encoding returned by
- * nl_langinfo or as we translated it wasn't accepted. Do
- * this fallback check. If this fails, we will enter the
- * old fallback below.
- */
+ Tcl_DStringInit(bufPtr);
- for (i = 0; localeTable[i].lang != NULL; i++) {
- if (strcmp(localeTable[i].lang, encoding) == 0) {
- setSysEncCode = Tcl_SetSystemEncoding(NULL,
- localeTable[i].encoding);
- break;
- }
+ /*
+ * Determine the current encoding from the LC_* or LANG environment
+ * variables. We previously used setlocale() to determine the locale,
+ * but this does not work on some systems (e.g. Linux/i386 RH 5.0).
+ */
+#ifdef HAVE_LANGINFO
+ if (setlocale(LC_CTYPE, "") != NULL) {
+ Tcl_DString ds;
+
+ /* Use a DString so we can modify case. */
+ Tcl_DStringInit(&ds);
+ encoding = Tcl_DStringAppend(&ds, nl_langinfo(CODESET), -1);
+ Tcl_UtfToLower(Tcl_DStringValue(&ds));
+ /* Check whether it's a known encoding... */
+ if (NULL == Tcl_GetEncoding(NULL, encoding)) {
+ /* ... or in the table if encodings we *should* know */
+ for (i = 0; localeTable[i].lang != NULL; i++) {
+ if (strcmp(localeTable[i].lang, encoding) == 0) {
+ Tcl_DStringAppend(bufPtr, localeTable[i].encoding, -1);
+ break;
}
}
-#ifdef HAVE_LANGINFO_DEBUG
- fprintf(stderr, " => '%s'\n", encoding);
-#endif
- Tcl_DStringFree(&ds);
+ } else {
+ Tcl_DStringAppend(bufPtr, encoding, -1);
}
-#ifdef HAVE_LANGINFO_DEBUG
- else {
- fprintf(stderr, "setlocale returned NULL\n");
+ Tcl_DStringFree(&ds);
+ if (Tcl_DStringLength(bufPtr)) {
+ return Tcl_DStringValue(bufPtr);
}
-#endif
+ }
#endif /* HAVE_LANGINFO */
- if (setSysEncCode != TCL_OK) {
- /*
- * Classic fallback check. This tries a homebrew algorithm to
- * determine what encoding should be used based on env vars.
- */
- char *langEnv = getenv("LC_ALL");
- encoding = NULL;
+ /*
+ * Classic fallback check. This tries a homebrew algorithm to
+ * determine what encoding should be used based on env vars.
+ */
+ encoding = getenv("LC_ALL");
- if (langEnv == NULL || langEnv[0] == '\0') {
- langEnv = getenv("LC_CTYPE");
- }
- if (langEnv == NULL || langEnv[0] == '\0') {
- langEnv = getenv("LANG");
- }
- if (langEnv == NULL || langEnv[0] == '\0') {
- langEnv = NULL;
- }
+ if (encoding == NULL || encoding[0] == '\0') {
+ encoding = getenv("LC_CTYPE");
+ }
+ if (encoding == NULL || encoding[0] == '\0') {
+ encoding = getenv("LANG");
+ }
+ if (encoding == NULL || encoding[0] == '\0') {
+ encoding = NULL;
+ }
- if (langEnv != NULL) {
- for (i = 0; localeTable[i].lang != NULL; i++) {
- if (strcmp(localeTable[i].lang, langEnv) == 0) {
- encoding = localeTable[i].encoding;
- break;
- }
- }
- /*
- * There was no mapping in the locale table. If there is an
- * encoding subfield, we can try to guess from that.
- */
-
- if (encoding == NULL) {
- char *p;
- for (p = langEnv; *p != '\0'; p++) {
- if (*p == '.') {
- p++;
- break;
- }
- }
- if (*p != '\0') {
- Tcl_DString ds;
- Tcl_DStringInit(&ds);
- encoding = Tcl_DStringAppend(&ds, p, -1);
-
- Tcl_UtfToLower(Tcl_DStringValue(&ds));
- setSysEncCode = Tcl_SetSystemEncoding(NULL, encoding);
- if (setSysEncCode != TCL_OK) {
- encoding = NULL;
- }
- Tcl_DStringFree(&ds);
- }
- }
-#ifdef HAVE_LANGINFO_DEBUG
- fprintf(stderr, "encoding fallback check '%s' => '%s'\n",
- langEnv, encoding);
-#endif
- }
- if (setSysEncCode != TCL_OK) {
- if (encoding == NULL) {
- encoding = TCL_DEFAULT_ENCODING;
- }
+ if (encoding != NULL) {
+ CONST char *p;
- Tcl_SetSystemEncoding(NULL, encoding);
+ /* Check whether it's a known encoding... */
+ if (NULL == Tcl_GetEncoding(NULL, encoding)) {
+ /* ... or in the table if encodings we *should* know */
+ for (i = 0; localeTable[i].lang != NULL; i++) {
+ if (strcmp(localeTable[i].lang, encoding) == 0) {
+ Tcl_DStringAppend(bufPtr, localeTable[i].encoding, -1);
+ break;
+ }
}
-
- /*
- * Initialize the C library's locale subsystem. This is required
- * for input methods to work properly on X11. We only do this for
- * LC_CTYPE because that's the necessary one, and we don't want to
- * affect LC_TIME here. The side effect of setting the default
- * locale should be to load any locale specific modules that are
- * needed by X. [BUG: 5422 3345 4236 2522 2521].
- * In HAVE_LANGINFO, this call is already done above.
- */
-#ifndef HAVE_LANGINFO
- setlocale(LC_CTYPE, "");
-#endif
+ } else {
+ Tcl_DStringAppend(bufPtr, encoding, -1);
+ }
+ if (Tcl_DStringLength(bufPtr)) {
+ return Tcl_DStringValue(bufPtr);
}
/*
- * In case the initial locale is not "C", ensure that the numeric
- * processing is done in "C" locale regardless. This is needed because
- * Tcl relies on routines like strtod, but should not have locale
- * dependent behavior.
- */
-
- setlocale(LC_NUMERIC, "C");
-
- /*
- * Until the system encoding was actually set, the library path was
- * actually in the native multi-byte encoding, and not really UTF-8
- * as advertised. We cheated as follows:
- *
- * 1. It was safe to allow the Tcl_SetSystemEncoding() call to
- * append the ASCII chars that make up the encoding's filename to
- * the names (in the native encoding) of directories in the library
- * path, since all Unix multi-byte encodings have ASCII in the
- * beginning.
- *
- * 2. To open the encoding file, the native bytes in the file name
- * were passed to the OS, without translating from UTF-8 to native,
- * because the name was already in the native encoding.
- *
- * Now that the system encoding was actually successfully set,
- * translate all the names in the library path to UTF-8. That way,
- * next time we search the library path, we'll translate the names
- * from UTF-8 to the system encoding which will be the native
- * encoding.
+ * We didn't recognize the full value as an encoding name.
+ * If there is an encoding subfield, we can try to guess from that.
*/
- pathPtr = TclGetLibraryPath();
- if (pathPtr != NULL) {
- int objc;
- Tcl_Obj **objv;
-
- objc = 0;
- Tcl_ListObjGetElements(NULL, pathPtr, &objc, &objv);
- for (i = 0; i < objc; i++) {
- int length;
- char *string;
- Tcl_DString ds;
-
- string = Tcl_GetStringFromObj(objv[i], &length);
- Tcl_ExternalToUtfDString(NULL, string, length, &ds);
- Tcl_SetStringObj(objv[i], Tcl_DStringValue(&ds),
- Tcl_DStringLength(&ds));
- Tcl_DStringFree(&ds);
+ for (p = encoding; *p != '\0'; p++) {
+ if (*p == '.') {
+ p++;
+ break;
}
}
+ if (*p != '\0') {
+ Tcl_DString ds;
+ Tcl_DStringInit(&ds);
+ encoding = Tcl_DStringAppend(&ds, p, -1);
+ Tcl_UtfToLower(Tcl_DStringValue(&ds));
- libraryPathEncodingFixed = 1;
- }
+ /* Check whether it's a known encoding... */
+ if (NULL == Tcl_GetEncoding(NULL, encoding)) {
+ /* ... or in the table if encodings we *should* know */
+ for (i = 0; localeTable[i].lang != NULL; i++) {
+ if (strcmp(localeTable[i].lang, encoding) == 0) {
+ Tcl_DStringAppend(bufPtr, localeTable[i].encoding, -1);
+ break;
+ }
+ }
+ } else {
+ Tcl_DStringAppend(bufPtr, encoding, -1);
+ }
+ Tcl_DStringFree(&ds);
+ if (Tcl_DStringLength(bufPtr)) {
+ return Tcl_DStringValue(bufPtr);
+ }
- /* This is only ever called from the startup thread */
- if (binaryEncoding == NULL) {
- /*
- * Keep the iso8859-1 encoding preloaded. The IO package uses
- * it for gets on a binary channel.
- */
- binaryEncoding = Tcl_GetEncoding(NULL, "iso8859-1");
+ }
}
+ return Tcl_DStringAppend(bufPtr, TCL_DEFAULT_ENCODING, -1);
}
/*
diff --git a/unix/tclUnixTest.c b/unix/tclUnixTest.c
index 955f719..9b5c717 100644
--- a/unix/tclUnixTest.c
+++ b/unix/tclUnixTest.c
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclUnixTest.c,v 1.18 2004/06/18 20:38:02 dgp Exp $
+ * RCS: @(#) $Id: tclUnixTest.c,v 1.19 2004/11/30 19:34:51 dgp Exp $
*/
#include "tclInt.h"
@@ -444,7 +444,6 @@ TestfindexecutableCmd(clientData, interp, argc, argv)
int argc; /* Number of arguments. */
CONST char **argv; /* Argument strings. */
{
- char *oldName;
char *oldNativeName;
int oldDone;
@@ -454,24 +453,19 @@ TestfindexecutableCmd(clientData, interp, argc, argv)
return TCL_ERROR;
}
- oldName = tclExecutableName;
oldNativeName = tclNativeExecutableName;
oldDone = tclFindExecutableSearchDone;
- tclExecutableName = NULL;
tclNativeExecutableName = NULL;
tclFindExecutableSearchDone = 0;
+ Tcl_GetNameOfExecutable();
Tcl_FindExecutable(argv[1]);
- if (tclExecutableName != NULL) {
- Tcl_SetResult(interp, tclExecutableName, TCL_VOLATILE);
- ckfree(tclExecutableName);
- }
+ Tcl_SetResult(interp, (char *) Tcl_GetNameOfExecutable(), TCL_VOLATILE);
if (tclNativeExecutableName != NULL) {
ckfree(tclNativeExecutableName);
}
- tclExecutableName = oldName;
tclNativeExecutableName = oldNativeName;
tclFindExecutableSearchDone = oldDone;
@@ -529,7 +523,7 @@ TestgetopenfileCmd(clientData, interp, argc, argv)
* TestsetdefencdirCmd --
*
* This procedure implements the "testsetdefenc" command. It is
- * used to set the value of tclDefaultEncodingDir.
+ * used to test Tcl_SetDefaultEncodingDir().
*
* Results:
* A standard Tcl result.
@@ -555,15 +549,7 @@ TestsetdefencdirCmd(clientData, interp, argc, argv)
return TCL_ERROR;
}
- if (tclDefaultEncodingDir != NULL) {
- ckfree(tclDefaultEncodingDir);
- tclDefaultEncodingDir = NULL;
- }
- if (*argv[1] != '\0') {
- tclDefaultEncodingDir = (char *)
- ckalloc((unsigned) strlen(argv[1]) + 1);
- strcpy(tclDefaultEncodingDir, argv[1]);
- }
+ Tcl_SetDefaultEncodingDir(argv[1]);
return TCL_OK;
}
@@ -573,7 +559,7 @@ TestsetdefencdirCmd(clientData, interp, argc, argv)
* TestgetdefencdirCmd --
*
* This procedure implements the "testgetdefenc" command. It is
- * used to get the value of tclDefaultEncodingDir.
+ * used to test Tcl_GetDefaultEncodingDir().
*
* Results:
* A standard Tcl result.
@@ -598,9 +584,7 @@ TestgetdefencdirCmd(clientData, interp, argc, argv)
return TCL_ERROR;
}
- if (tclDefaultEncodingDir != NULL) {
- Tcl_AppendResult(interp, tclDefaultEncodingDir, (char *) NULL);
- }
+ Tcl_AppendResult(interp, Tcl_GetDefaultEncodingDir(), (char *) NULL);
return TCL_OK;
}
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index ca2eeba..8c39505 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinFile.c,v 1.70 2004/11/02 12:13:36 vincentdarley Exp $
+ * RCS: @(#) $Id: tclWinFile.c,v 1.71 2004/11/30 19:34:51 dgp Exp $
*/
//#define _WIN32_WINNT 0x0500
@@ -691,19 +691,20 @@ NativeWriteReparse(LinkDirectory, buffer)
*---------------------------------------------------------------------------
*/
-char *
+void
TclpFindExecutable(argv0)
CONST char *argv0; /* The value of the application's argv[0]
* (native). */
{
WCHAR wName[MAX_PATH];
char name[MAX_PATH * TCL_UTF_MAX];
+ Tcl_DString ds;
if (argv0 == NULL) {
- return NULL;
+ return;
}
if (tclNativeExecutableName != NULL) {
- return tclNativeExecutableName;
+ return;
}
/*
@@ -719,12 +720,13 @@ TclpFindExecutable(argv0)
MultiByteToWideChar(CP_ACP, 0, name, -1, wName, MAX_PATH);
}
WideCharToMultiByte(CP_UTF8, 0, wName, -1, name, sizeof(name), NULL, NULL);
+ TclWinNoBackslash(name);
- tclNativeExecutableName = ckalloc((unsigned) (strlen(name) + 1));
- strcpy(tclNativeExecutableName, name);
-
- TclWinNoBackslash(tclNativeExecutableName);
- return tclNativeExecutableName;
+ Tcl_UtfToExternalDString(NULL, name, -1, &ds);
+ tclNativeExecutableName = ckalloc((unsigned) (Tcl_DStringLength(&ds) + 1));
+ strcpy(tclNativeExecutableName, Tcl_DStringValue(&ds));
+ Tcl_DStringFree(&ds);
+ Tcl_GetNameOfExecutable();
}
/*
diff --git a/win/tclWinInit.c b/win/tclWinInit.c
index c92eab6..0b54cdb 100644
--- a/win/tclWinInit.c
+++ b/win/tclWinInit.c
@@ -7,7 +7,7 @@
* Copyright (c) 1998-1999 by Scriptics Corporation.
* All rights reserved.
*
- * RCS: @(#) $Id: tclWinInit.c,v 1.63 2004/11/24 21:12:20 kennykb Exp $
+ * RCS: @(#) $Id: tclWinInit.c,v 1.64 2004/11/30 19:34:52 dgp Exp $
*/
#include "tclWinInt.h"
@@ -92,167 +92,15 @@ static char* processors[NUMPROCESSORS] = {
"amd64", "ia32_on_win64"
};
-/* Used to store the encoding used for binary files */
-static Tcl_Encoding binaryEncoding = NULL;
-/* Has the basic library path encoding issue been fixed */
-static int libraryPathEncodingFixed = 0;
-
-static void AppendEnvironment(Tcl_Obj *listPtr, CONST char *lib);
-static void AppendDllPath(Tcl_Obj *listPtr, HMODULE hModule,
- CONST char *lib);
-static void FreeDefaultLibraryDir(ClientData);
-static void FreeThreadDefaultLibraryDir(ClientData);
-static Tcl_Obj * GetDefaultLibraryDir();
-static void SetDefaultLibraryDir(Tcl_Obj *directory);
-static int ToUtf(CONST WCHAR *wSrc, char *dst);
-
-/*
- *---------------------------------------------------------------------------
- *
- * SetDefaultLibraryDir --
- *
- * Called by TclpInitLibraryPath to save the path to the
- * directory ../lib/tcl<version> relative to the Tcl Dll.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Saves a per-thread (Tcl_Obj *) and a per-process string.
- * Sets up exit handlers to free them.
- *
- *---------------------------------------------------------------------------
- */
-
/*
- * Per-process copy of the default library dir, as a string, shared by
- * all threads
+ * The default directory in which the init.tcl file is expected to be found.
*/
-static char *defaultLibraryDir = NULL;
-static int defaultLibraryDirLength = 0;
-static Tcl_ThreadDataKey defaultLibraryDirKey;
-
-static void
-FreeThreadDefaultLibraryDir(clientData)
- ClientData clientData;
-{
- Tcl_Obj **objPtrPtr = (Tcl_Obj **) clientData;
- Tcl_DecrRefCount(*objPtrPtr);
-}
-
-static void
-FreeDefaultLibraryDir(clientData)
- ClientData clientData;
-{
- ckfree(defaultLibraryDir);
- defaultLibraryDir = NULL;
- defaultLibraryDirLength = 0;
-}
-
-static void
-SetDefaultLibraryDir(directory)
- Tcl_Obj *directory;
-{
- int numBytes = 0;
- CONST char *bytes;
- Tcl_Obj **savedDirectoryPtr = (Tcl_Obj **)
- Tcl_GetThreadData(&defaultLibraryDirKey, (int) sizeof(Tcl_Obj *));
+static TclInitProcessGlobalValueProc InitializeDefaultLibraryDir;
+static ProcessGlobalValue defaultLibraryDir =
+ {0, 0, NULL, NULL, InitializeDefaultLibraryDir, NULL, NULL};
- Tcl_IncrRefCount(directory);
- if (*savedDirectoryPtr == NULL) {
- /*
- * First call in this thread, set up the thread exit handler
- */
- Tcl_CreateThreadExitHandler(FreeThreadDefaultLibraryDir,
- (ClientData) savedDirectoryPtr);
- } else {
- /*
- * Called SetDLD after a previous SetDLD or GetDLD in this thread ?!
- */
- Tcl_DecrRefCount(*savedDirectoryPtr);
- }
- *savedDirectoryPtr = directory;
-
- /*
- * No Mutex protection, as the only caller is already in TclpInitLock
- */
-
- bytes = Tcl_GetStringFromObj(directory, &numBytes);
- if (defaultLibraryDir != NULL) {
- /*
- * This function has been called before. We only ever want to
- * set up the default library directory once, but if it is set
- * multiple times to the same value that's not harmful.
- */
- if (defaultLibraryDirLength != numBytes ||
- memcmp(defaultLibraryDir, bytes, (unsigned) numBytes) != 0) {
- Tcl_Panic("Attempt to modify defaultLibraryDir");
- }
- return;
- }
-
- /*
- * First call from any thread; set up exit handler
- */
-
- Tcl_CreateExitHandler(FreeDefaultLibraryDir, NULL);
-
- defaultLibraryDirLength = numBytes;
- defaultLibraryDir = ckalloc((unsigned int) numBytes + 1);
- memcpy(defaultLibraryDir, bytes, (unsigned int) numBytes + 1);
-}
-
-/*
- *---------------------------------------------------------------------------
- *
- * GetDefaultLibraryDir --
- *
- * Called by TclpSetVariables to retrieve the saved value
- * stored by SetDefaultLibraryDir in order to store that value
- * in ::tclDefaultLibrary .
- *
- * Results:
- * A pointer to a Tcl_Obj holding the default directory path
- * for init.tcl.
- *
- * Side effects:
- *
- *---------------------------------------------------------------------------
- */
-
-static Tcl_Obj *
-GetDefaultLibraryDir()
-{
- Tcl_Obj **savedDirectoryPtr = (Tcl_Obj **)
- Tcl_GetThreadData(&defaultLibraryDirKey, (int) sizeof(Tcl_Obj *));
-
- if (NULL != *savedDirectoryPtr) {
- return *savedDirectoryPtr;
- }
-
- if (NULL == defaultLibraryDir) {
- /*
- * Careful here. This may be bogus, calling TclpInitLibraryPath
- * when not in TclpInitLock.
- *
- * This path is taken by wish because it calls Tcl_CreateInterp
- * before it calls Tcl_FindExecutable.
- */
- TclpInitLibraryPath(NULL);
- if (NULL != *savedDirectoryPtr) {
- return *savedDirectoryPtr;
- } else {
- Tcl_Panic("TclpInitLibraryPath failed to set default library dir");
- }
- }
-
- *savedDirectoryPtr =
- Tcl_NewStringObj(defaultLibraryDir, defaultLibraryDirLength);
- Tcl_IncrRefCount(*savedDirectoryPtr);
- Tcl_CreateThreadExitHandler(FreeThreadDefaultLibraryDir,
- (ClientData) savedDirectoryPtr);
- return *savedDirectoryPtr;
-}
+static void AppendEnvironment(Tcl_Obj *listPtr, CONST char *lib);
+static int ToUtf(CONST WCHAR *wSrc, char *dst);
/*
*---------------------------------------------------------------------------
@@ -304,61 +152,45 @@ TclpInitPlatform()
}
/*
- *---------------------------------------------------------------------------
+ *-------------------------------------------------------------------------
*
* TclpInitLibraryPath --
*
- * Initialize the library path at startup.
- *
- * This call sets the library path to strings in UTF-8. Any
- * pre-existing library path information is assumed to have been
- * in the native multibyte encoding.
- *
- * Called at process initialization time.
+ * This is the fallback routine that sets the library path
+ * if the application has not set one by the first time
+ * it is needed.
*
* Results:
- * Return 0, indicating that the UTF is clean.
+ * None.
*
* Side effects:
- * None.
+ * Sets the library path to an initial value.
*
- *---------------------------------------------------------------------------
- */
+ *-------------------------------------------------------------------------
+ */
-int
-TclpInitLibraryPath(path)
- CONST char *path; /* Potentially dirty UTF string that is */
- /* the path to the executable name. */
+void
+TclpInitLibraryPath(valuePtr, lengthPtr, encodingPtr)
+ char **valuePtr;
+ int *lengthPtr;
+ Tcl_Encoding *encodingPtr;
{
#define LIBRARY_SIZE 32
- Tcl_Obj *pathPtr, *objPtr, **objv;
- CONST char *str;
- Tcl_DString ds;
- int objc;
+ Tcl_Obj *pathPtr;
char installLib[LIBRARY_SIZE];
+ char *bytes;
- Tcl_DStringInit(&ds);
pathPtr = Tcl_NewObj();
/*
- * Initialize the substrings used when locating an executable. The
- * installLib variable computes the path as though the executable
- * is installed.
+ * Initialize the substring used when locating the script library. The
+ * installLib variable computes the script library path relative to the
+ * installed DLL.
*/
sprintf(installLib, "lib/tcl%s", TCL_VERSION);
/*
- * Look for the library relative to default encoding dir.
- */
-
- str = Tcl_GetDefaultEncodingDir();
- if ((str != NULL) && (str[0] != '\0')) {
- objPtr = Tcl_NewStringObj(str, -1);
- Tcl_ListObjAppendElement(NULL, pathPtr, objPtr);
- }
-
- /*
* Look for the library relative to the TCL_LIBRARY env variable.
* If the last dirname in the TCL_LIBRARY path does not match the
* last dirname in the installLib variable, use the last dir name
@@ -368,17 +200,16 @@ TclpInitLibraryPath(path)
AppendEnvironment(pathPtr, installLib);
/*
- * Look for the library relative to the DLL. Only use the installLib
- * because in practice, the DLL is always installed.
+ * Look for the library in its default location.
*/
-
- AppendDllPath(pathPtr, TclWinGetTclInstance(), installLib);
-
- Tcl_ListObjGetElements(NULL, pathPtr, &objc, &objv);
- SetDefaultLibraryDir(Tcl_DuplicateObj(objv[objc-1]));
- TclSetLibraryPath(pathPtr);
-
- return 0; /* 0 indicates that pathPtr is clean (true) utf */
+ Tcl_ListObjAppendElement(NULL, pathPtr,
+ TclGetProcessGlobalValue(&defaultLibraryDir));
+
+ *encodingPtr = NULL;
+ bytes = Tcl_GetStringFromObj(pathPtr, lengthPtr);
+ *valuePtr = ckalloc((unsigned int)(*lengthPtr)+1);
+ memcpy((VOID *) *valuePtr, (VOID *) bytes, (size_t)(*lengthPtr)+1);
+ Tcl_DecrRefCount(pathPtr);
}
/*
@@ -481,10 +312,10 @@ AppendEnvironment(
/*
*---------------------------------------------------------------------------
*
- * AppendDllPath --
+ * InitializeDefaultLibraryDir --
*
- * Append a path onto the path pointer that tries to locate the Tcl
- * library relative to the location of the Tcl DLL.
+ * Locate the Tcl script library default location relative to
+ * the location of the Tcl DLL.
*
* Results:
* None.
@@ -496,22 +327,21 @@ AppendEnvironment(
*/
static void
-AppendDllPath(
- Tcl_Obj *pathPtr,
- HMODULE hModule,
- CONST char *lib)
+InitializeDefaultLibraryDir(valuePtr, lengthPtr, encodingPtr)
+ char **valuePtr;
+ int *lengthPtr;
+ Tcl_Encoding *encodingPtr;
{
+ HMODULE hModule = TclWinGetTclInstance();
WCHAR wName[MAX_PATH + LIBRARY_SIZE];
char name[(MAX_PATH + LIBRARY_SIZE) * TCL_UTF_MAX];
+ char *end, *p;
if (GetModuleFileNameW(hModule, wName, MAX_PATH) == 0) {
GetModuleFileNameA(hModule, name, MAX_PATH);
} else {
ToUtf(wName, name);
}
- if (lib != NULL) {
- char *end, *p;
-
end = strrchr(name, '\\');
*end = '\0';
p = strrchr(name, '\\');
@@ -519,10 +349,12 @@ AppendDllPath(
end = p;
}
*end = '\\';
- strcpy(end + 1, lib);
- }
TclWinNoBackslash(name);
- Tcl_ListObjAppendElement(NULL, pathPtr, Tcl_NewStringObj(name, -1));
+ sprintf(end + 1, "lib/tcl%s", TCL_VERSION);
+ *lengthPtr = strlen(name);
+ *valuePtr = ckalloc((unsigned int) *lengthPtr + 1);
+ *encodingPtr = NULL;
+ memcpy((VOID *) *valuePtr, (VOID *) name, (size_t) *lengthPtr + 1);
}
/*
@@ -580,11 +412,6 @@ void
TclWinEncodingsCleanup()
{
TclWinResetInterfaceEncodings();
- libraryPathEncodingFixed = 0;
- if (binaryEncoding != NULL) {
- Tcl_FreeEncoding(binaryEncoding);
- binaryEncoding = NULL;
- }
}
/*
@@ -614,57 +441,26 @@ TclWinEncodingsCleanup()
void
TclpSetInitialEncodings()
{
- CONST char *encoding;
- char buf[4 + TCL_INTEGER_SPACE];
-
- if (libraryPathEncodingFixed == 0) {
- int platformId, useWide;
-
- platformId = TclWinGetPlatformId();
- useWide = ((platformId == VER_PLATFORM_WIN32_NT)
- || (platformId == VER_PLATFORM_WIN32_CE));
- TclWinSetInterfaces(useWide);
-
- wsprintfA(buf, "cp%d", GetACP());
- Tcl_SetSystemEncoding(NULL, buf);
-
- if (!useWide) {
- Tcl_Obj *pathPtr = TclGetLibraryPath();
- if (pathPtr != NULL) {
- int i, objc;
- Tcl_Obj **objv;
-
- objc = 0;
- Tcl_ListObjGetElements(NULL, pathPtr, &objc, &objv);
- for (i = 0; i < objc; i++) {
- int length;
- char *string;
- Tcl_DString ds;
-
- string = Tcl_GetStringFromObj(objv[i], &length);
- Tcl_ExternalToUtfDString(NULL, string, length, &ds);
- Tcl_SetStringObj(objv[i], Tcl_DStringValue(&ds),
- Tcl_DStringLength(&ds));
- Tcl_DStringFree(&ds);
- }
- }
- }
+ int platformId, useWide;
+ Tcl_DString encodingName;
- libraryPathEncodingFixed = 1;
- } else {
- wsprintfA(buf, "cp%d", GetACP());
- Tcl_SetSystemEncoding(NULL, buf);
- }
+ platformId = TclWinGetPlatformId();
+ useWide = ((platformId == VER_PLATFORM_WIN32_NT)
+ || (platformId == VER_PLATFORM_WIN32_CE));
+ TclWinSetInterfaces(useWide);
- /* This is only ever called from the startup thread */
- if (binaryEncoding == NULL) {
- /*
- * Keep this encoding preloaded. The IO package uses it for
- * gets on a binary channel.
- */
- encoding = "iso8859-1";
- binaryEncoding = Tcl_GetEncoding(NULL, encoding);
- }
+ Tcl_SetSystemEncoding(NULL,
+ TclpGetEncodingNameFromEnvironment(&encodingName));
+ Tcl_DStringFree(&encodingName);
+}
+
+CONST char *
+TclpGetEncodingNameFromEnvironment(bufPtr)
+ Tcl_DString *bufPtr;
+{
+ Tcl_DStringInit(bufPtr);
+ wsprintfA(Tcl_DStringValue(bufPtr), "cp%d", GetACP());
+ return Tcl_DStringValue(bufPtr);
}
/*
@@ -699,7 +495,7 @@ TclpSetVariables(interp)
DWORD dwUserNameLen = sizeof(szUserName);
Tcl_SetVar2Ex(interp, "tclDefaultLibrary", NULL,
- GetDefaultLibraryDir(), TCL_GLOBAL_ONLY);
+ TclGetProcessGlobalValue(&defaultLibraryDir), TCL_GLOBAL_ONLY);
osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
GetVersionExA(&osInfo);
diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c
index 4266f1a..4b18c02 100644
--- a/win/tclWinPipe.c
+++ b/win/tclWinPipe.c
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinPipe.c,v 1.51 2004/11/09 04:07:27 davygrvy Exp $
+ * RCS: @(#) $Id: tclWinPipe.c,v 1.52 2004/11/30 19:34:52 dgp Exp $
*/
#include "tclWinInt.h"
@@ -1211,7 +1211,11 @@ TclpCreateProcess(
Tcl_DString pipeDll;
Tcl_DStringInit(&pipeDll);
Tcl_DStringAppend(&pipeDll, TCL_PIPE_DLL, -1);
- tclExePtr = Tcl_NewStringObj(TclpFindExecutable(""), -1);
+
+ /* For safety, just in case the app didn't call it first */
+ Tcl_FindExecutable(NULL);
+
+ tclExePtr = Tcl_NewStringObj(Tcl_GetNameOfExecutable(), -1);
start = Tcl_GetStringFromObj(tclExePtr, &i);
for (end = start + (i-1); end > start; end--) {
if (*end == '/') {