diff options
Diffstat (limited to 'generic/tclEnv.c')
| -rw-r--r-- | generic/tclEnv.c | 389 | 
1 files changed, 150 insertions, 239 deletions
| diff --git a/generic/tclEnv.c b/generic/tclEnv.c index 812c2e4..66ddb57 100644 --- a/generic/tclEnv.c +++ b/generic/tclEnv.c @@ -11,54 +11,38 @@   *   * See the file "license.terms" for information on usage and redistribution of   * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclEnv.c,v 1.28 2005/11/27 02:33:49 das Exp $   */  #include "tclInt.h" -TCL_DECLARE_MUTEX(envMutex)	/* To serialize access to environ */ +TCL_DECLARE_MUTEX(envMutex)	/* To serialize access to environ. */ -static int cacheSize = 0;	/* Number of env strings in environCache. */ -static char **environCache = NULL; -				/* Array containing all of the environment +static struct { +    int cacheSize;		/* Number of env strings in cache. */ +    char **cache;		/* Array containing all of the environment  				 * strings that Tcl has allocated. */ -  #ifndef USE_PUTENV -static char **ourEnviron = NULL;/* Cache of the array that we allocate. We +    char **ourEnviron;		/* Cache of the array that we allocate. We  				 * need to track this in case another  				 * subsystem swaps around the environ array -				 * like we do. -				 */ -static int environSize = 0;	/* Non-zero means that the environ array was +				 * like we do. */ +    int ourEnvironSize;		/* Non-zero means that the environ array was  				 * malloced and has this many total entries  				 * allocated to it (not all may be in use at  				 * once). Zero means that the environment  				 * array is in its original static state. */  #endif - -/* - * For MacOS X - */ - -#if defined(__APPLE__) && defined(__DYNAMIC__) -#include <crt_externs.h> -MODULE_SCOPE char **environ; -char **environ = NULL; -#endif +} env;  /*   * Declarations for local functions defined in this file:   */  static char *		EnvTraceProc(ClientData clientData, Tcl_Interp *interp, -			    CONST char *name1, CONST char *name2, int flags); -static void		ReplaceString(CONST char *oldStr, char *newStr); -MODULE_SCOPE void	TclSetEnv(CONST char *name, CONST char *value); -MODULE_SCOPE void	TclUnsetEnv(CONST char *name); -#if defined(__CYGWIN__) && defined(__WIN32__) -static void		TclCygwinPutenv(CONST char *string); -#endif +			    const char *name1, const char *name2, int flags); +static void		ReplaceString(const char *oldStr, char *newStr); +MODULE_SCOPE void	TclSetEnv(const char *name, const char *value); +MODULE_SCOPE void	TclUnsetEnv(const char *name);  /*   *---------------------------------------------------------------------- @@ -87,67 +71,107 @@ TclSetupEnv(      Tcl_Interp *interp)		/* Interpreter whose "env" array is to be  				 * managed. */  { +    Var *varPtr, *arrayPtr; +    Tcl_Obj *varNamePtr;      Tcl_DString envString; -    char *p1, *p2; -    int i; - -    /* -     * For MacOS X, need to get the real system environment. -     */ - -#if defined(__APPLE__) && defined(__DYNAMIC__) -    environ = *_NSGetEnviron(); -#endif +    Tcl_HashTable namesHash; +    Tcl_HashEntry *hPtr; +    Tcl_HashSearch search;      /*       * Synchronize the values in the environ array with the contents of the       * Tcl "env" variable. To do this: -     *    1) Remove the trace that fires when the "env" var is unset. -     *    2) Unset the "env" variable. -     *    3) If there are no environ variables, create an empty "env" array. -     *	     Otherwise populate the array with current values. -     *    4) Add a trace that synchronizes the "env" array. +     *    1) Remove the trace that fires when the "env" var is updated. +     *    2) Find the existing contents of the "env", storing in a hash table. +     *    3) Create/update elements for each environ variable, removing +     *	     elements from the hash table as we go. +     *    4) Remove the elements for each remaining entry in the hash table, +     *	     which must have existed before yet have no analog in the environ +     *	     variable. +     *    5) Add a trace that synchronizes the "env" array.       */      Tcl_UntraceVar2(interp, "env", NULL,  	    TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS | -	    TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc, -	    (ClientData) NULL); +	    TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc, NULL); -    Tcl_UnsetVar2(interp, "env", NULL, TCL_GLOBAL_ONLY); +    /* +     * Find out what elements are currently in the global env array. +     */ -    if (environ[0] == NULL) { -	Tcl_Obj *varNamePtr; +    TclNewLiteralStringObj(varNamePtr, "env"); +    Tcl_IncrRefCount(varNamePtr); +    Tcl_InitObjHashTable(&namesHash); +    varPtr = TclObjLookupVarEx(interp, varNamePtr, NULL, TCL_GLOBAL_ONLY, +	    /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr); +    TclFindArrayPtrElements(varPtr, &namesHash); + +    /* +     * Go through the environment array and transfer its values into Tcl. At +     * the same time, remove those elements we add/update from the hash table +     * of existing elements, so that after this part processes, that table +     * will hold just the parts to remove. +     */ + +    if (environ[0] != NULL) { +	int i; -	varNamePtr = Tcl_NewStringObj("env", -1); -	Tcl_IncrRefCount(varNamePtr); -	TclArraySet(interp, varNamePtr, NULL); -	Tcl_DecrRefCount(varNamePtr); -    } else {  	Tcl_MutexLock(&envMutex);  	for (i = 0; environ[i] != NULL; i++) { +	    Tcl_Obj *obj1, *obj2; +	    char *p1, *p2; +  	    p1 = Tcl_ExternalToUtfDString(NULL, environ[i], -1, &envString);  	    p2 = strchr(p1, '=');  	    if (p2 == NULL) {  		/*  		 * This condition seem to happen occasionally under some -		 * versions of Solaris; ignore the entry. +		 * versions of Solaris, or when encoding accidents swallow the +		 * '='; ignore the entry.  		 */  		continue;  	    }  	    p2++;  	    p2[-1] = '\0'; -	    Tcl_SetVar2(interp, "env", p1, p2, TCL_GLOBAL_ONLY); +	    obj1 = Tcl_NewStringObj(p1, -1); +	    obj2 = Tcl_NewStringObj(p2, -1);  	    Tcl_DStringFree(&envString); + +	    Tcl_IncrRefCount(obj1); +	    Tcl_IncrRefCount(obj2); +	    Tcl_ObjSetVar2(interp, varNamePtr, obj1, obj2, TCL_GLOBAL_ONLY); +	    hPtr = Tcl_FindHashEntry(&namesHash, obj1); +	    if (hPtr != NULL) { +		Tcl_DeleteHashEntry(hPtr); +	    } +	    Tcl_DecrRefCount(obj1); +	    Tcl_DecrRefCount(obj2);  	}  	Tcl_MutexUnlock(&envMutex);      } +    /* +     * Delete those elements that existed in the array but which had no +     * counterparts in the environment array. +     */ + +    for (hPtr=Tcl_FirstHashEntry(&namesHash, &search); hPtr!=NULL; +	    hPtr=Tcl_NextHashEntry(&search)) { +	Tcl_Obj *elemName = Tcl_GetHashValue(hPtr); + +	TclObjUnsetVar2(interp, varNamePtr, elemName, TCL_GLOBAL_ONLY); +    } +    Tcl_DeleteHashTable(&namesHash); +    Tcl_DecrRefCount(varNamePtr); + +    /* +     * Re-establish the trace. +     */ +      Tcl_TraceVar2(interp, "env", NULL,  	    TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS | -	    TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc, -	    (ClientData) NULL); +	    TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc, NULL);  }  /* @@ -173,14 +197,15 @@ TclSetupEnv(  void  TclSetEnv( -    CONST char *name,		/* Name of variable whose value is to be set +    const char *name,		/* Name of variable whose value is to be set  				 * (UTF-8). */ -    CONST char *value)		/* New value for variable (UTF-8). */ +    const char *value)		/* New value for variable (UTF-8). */  {      Tcl_DString envString; -    int index, length, nameLength; +    unsigned nameLength, valueLength; +    int index, length;      char *p, *oldValue; -    CONST char *p2; +    const char *p2;      /*       * Figure out where the entry is going to go. If the name doesn't already @@ -195,34 +220,19 @@ TclSetEnv(  #ifndef USE_PUTENV  	/*  	 * We need to handle the case where the environment may be changed -	 * outside our control. environSize is only valid if the current +	 * outside our control. ourEnvironSize is only valid if the current  	 * environment is the one we allocated. [Bug 979640]  	 */ -	if ((ourEnviron != environ) || ((length + 2) > environSize)) { -	    char **newEnviron; +	if ((env.ourEnviron != environ) || (length+2 > env.ourEnvironSize)) { +	    char **newEnviron = ckalloc((length + 5) * sizeof(char *)); -	    newEnviron = (char **) -		    ckalloc((unsigned) ((length + 5) * sizeof(char *))); -	    memcpy((void *) newEnviron, (void *) environ, -		    length * sizeof(char *)); -	    if ((environSize != 0) && (ourEnviron != NULL)) { -		ckfree((char *) ourEnviron); +	    memcpy(newEnviron, environ, length * sizeof(char *)); +	    if ((env.ourEnvironSize != 0) && (env.ourEnviron != NULL)) { +		ckfree(env.ourEnviron);  	    } -	    environ = ourEnviron = newEnviron; -	    environSize = length + 5; - -#if defined(__APPLE__) && defined(__DYNAMIC__) -	    /* -	     * Install the new environment array where the system routines can -	     * see it. -	     */ - -	    { -		char ***e = _NSGetEnviron(); -		*e = environ; -	    } -#endif /* __APPLE__ && __DYNAMIC__ */ +	    environ = env.ourEnviron = newEnviron; +	    env.ourEnvironSize = length + 5;  	}  	index = length;  	environ[index + 1] = NULL; @@ -230,7 +240,7 @@ TclSetEnv(  	oldValue = NULL;  	nameLength = strlen(name);      } else { -	CONST char *env; +	const char *env;  	/*  	 * Compare the new value to the existing value. If they're the same @@ -241,7 +251,7 @@ TclSetEnv(  	 */  	env = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envString); -	if (strcmp(value, (env + length + 1)) == 0) { +	if (strcmp(value, env + (length + 1)) == 0) {  	    Tcl_DStringFree(&envString);  	    Tcl_MutexUnlock(&envMutex);  	    return; @@ -249,7 +259,7 @@ TclSetEnv(  	Tcl_DStringFree(&envString);  	oldValue = environ[index]; -	nameLength = length; +	nameLength = (unsigned) length;      }      /* @@ -258,18 +268,19 @@ TclSetEnv(       * and set the environ array value.       */ -    p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2)); -    strcpy(p, name); +    valueLength = strlen(value); +    p = ckalloc(nameLength + valueLength + 2); +    memcpy(p, name, nameLength);      p[nameLength] = '='; -    strcpy(p+nameLength+1, value); +    memcpy(p+nameLength+1, value, valueLength+1);      p2 = Tcl_UtfToExternalDString(NULL, p, -1, &envString);      /*       * Copy the native string to heap memory.       */ -    p = (char *) ckrealloc(p, (unsigned) (strlen(p2) + 1)); -    strcpy(p, p2); +    p = ckrealloc(p, Tcl_DStringLength(&envString) + 1); +    memcpy(p, p2, (unsigned) Tcl_DStringLength(&envString) + 1);      Tcl_DStringFree(&envString);  #ifdef USE_PUTENV @@ -281,7 +292,7 @@ TclSetEnv(      index = TclpFindVariable(name, &length);  #else      environ[index] = p; -#endif +#endif /* USE_PUTENV */      /*       * Watch out for versions of putenv that copy the string (e.g. VC++). In @@ -298,7 +309,7 @@ TclSetEnv(  	 */  	ckfree(p); -#endif +#endif /* HAVE_PUTENV_THAT_COPIES */      }      Tcl_MutexUnlock(&envMutex); @@ -337,11 +348,11 @@ TclSetEnv(  int  Tcl_PutEnv( -    CONST char *assignment)	/* Info about environment variable in the form +    const char *assignment)	/* Info about environment variable in the form  				 * NAME=value. (native) */  {      Tcl_DString nameString; -    CONST char *name; +    const char *name;      char *value;      if (assignment == NULL) { @@ -386,7 +397,7 @@ Tcl_PutEnv(  void  TclUnsetEnv( -    CONST char *name)		/* Name of variable to remove (UTF-8). */ +    const char *name)		/* Name of variable to remove (UTF-8). */  {      char *oldValue;      int length; @@ -396,7 +407,7 @@ TclUnsetEnv(      char *string;  #else      char **envPtr; -#endif +#endif /* USE_PUTENV_FOR_UNSET */      Tcl_MutexLock(&envMutex);      index = TclpFindVariable(name, &length); @@ -428,20 +439,21 @@ TclUnsetEnv(       * that no = should be included, and Windows requires it.       */ -#ifdef WIN32 -    string = ckalloc((unsigned int) length+2); -    memcpy((void *) string, (void *) name, (size_t) length); +#if defined(_WIN32) +    string = ckalloc(length + 2); +    memcpy(string, name, (size_t) length);      string[length] = '=';      string[length+1] = '\0';  #else -    string = ckalloc((unsigned int) length+1); -    memcpy((void *) string, (void *) name, (size_t) length); +    string = ckalloc(length + 1); +    memcpy(string, name, (size_t) length);      string[length] = '\0'; -#endif /* WIN32 */ +#endif /* _WIN32 */      Tcl_UtfToExternalDString(NULL, string, -1, &envString); -    string = ckrealloc(string, (unsigned) (Tcl_DStringLength(&envString)+1)); -    strcpy(string, Tcl_DStringValue(&envString)); +    string = ckrealloc(string, Tcl_DStringLength(&envString) + 1); +    memcpy(string, Tcl_DStringValue(&envString), +	    (unsigned) Tcl_DStringLength(&envString)+1);      Tcl_DStringFree(&envString);      putenv(string); @@ -496,16 +508,16 @@ TclUnsetEnv(   *----------------------------------------------------------------------   */ -CONST char * +const char *  TclGetEnv( -    CONST char *name,		/* Name of environment variable to find +    const char *name,		/* Name of environment variable to find  				 * (UTF-8). */      Tcl_DString *valuePtr)	/* Uninitialized or free DString in which the  				 * value of the environment variable is  				 * stored. */  {      int length, index; -    CONST char *result; +    const char *result;      Tcl_MutexLock(&envMutex);      index = TclpFindVariable(name, &length); @@ -539,7 +551,8 @@ TclGetEnv(   *	array.   *   * Results: - *	Always returns NULL to indicate success. + *	Returns NULL to indicate success, or an error-message if the array + *	element being handled doesn't exist.   *   * Side effects:   *	Environment variable changes get propagated. If the whole "env" array @@ -555,8 +568,8 @@ EnvTraceProc(      ClientData clientData,	/* Not used. */      Tcl_Interp *interp,		/* Interpreter whose "env" variable is being  				 * modified. */ -    CONST char *name1,		/* Better be "env". */ -    CONST char *name2,		/* Name of variable being modified, or NULL if +    const char *name1,		/* Better be "env". */ +    const char *name2,		/* Name of variable being modified, or NULL if  				 * whole array is being deleted (UTF-8). */      int flags)			/* Indicates what's happening. */  { @@ -582,7 +595,7 @@ EnvTraceProc(       */      if (flags & TCL_TRACE_WRITES) { -	CONST char *value; +	const char *value;  	value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY);  	TclSetEnv(name2, value); @@ -594,11 +607,10 @@ EnvTraceProc(      if (flags & TCL_TRACE_READS) {  	Tcl_DString valueString; -	CONST char *value; +	const char *value = TclGetEnv(name2, &valueString); -	value = TclGetEnv(name2, &valueString);  	if (value == NULL) { -	    return "no such variable"; +	    return (char *) "no such variable";  	}  	Tcl_SetVar2(interp, name1, name2, value, 0);  	Tcl_DStringFree(&valueString); @@ -634,11 +646,10 @@ EnvTraceProc(  static void  ReplaceString( -    CONST char *oldStr,		/* Old environment string. */ +    const char *oldStr,		/* Old environment string. */      char *newStr)		/* New environment string. */  {      int i; -    char **newCache;      /*       * Check to see if the old value was allocated by Tcl. If so, it needs to @@ -647,47 +658,41 @@ ReplaceString(       * changes are being made.       */ -    for (i = 0; i < cacheSize; i++) { -	if ((environCache[i] == oldStr) || (environCache[i] == NULL)) { +    for (i = 0; i < env.cacheSize; i++) { +	if (env.cache[i]==oldStr || env.cache[i]==NULL) {  	    break;  	}      } -    if (i < cacheSize) { +    if (i < env.cacheSize) {  	/*  	 * Replace or delete the old value.  	 */ -	if (environCache[i]) { -	    ckfree(environCache[i]); +	if (env.cache[i]) { +	    ckfree(env.cache[i]);  	}  	if (newStr) { -	    environCache[i] = newStr; +	    env.cache[i] = newStr;  	} else { -	    for (; i < cacheSize-1; i++) { -		environCache[i] = environCache[i+1]; +	    for (; i < env.cacheSize-1; i++) { +		env.cache[i] = env.cache[i+1];  	    } -	    environCache[cacheSize-1] = NULL; +	    env.cache[env.cacheSize-1] = NULL;  	}      } else { -	int allocatedSize = (cacheSize + 5) * sizeof(char *); -  	/*  	 * We need to grow the cache in order to hold the new string.  	 */ -	newCache = (char **) ckalloc((unsigned) allocatedSize); -	(void) memset(newCache, (int) 0, (size_t) allocatedSize); +	const int growth = 5; -	if (environCache) { -	    memcpy((void *) newCache, (void *) environCache, -		    (size_t) (cacheSize * sizeof(char*))); -	    ckfree((char *) environCache); -	} -	environCache = newCache; -	environCache[cacheSize] = newStr; -	environCache[cacheSize+1] = NULL; -	cacheSize += 5; +	env.cache = ckrealloc(env.cache, +		(env.cacheSize + growth) * sizeof(char *)); +	env.cache[env.cacheSize] = newStr; +	(void) memset(env.cache+env.cacheSize+1, 0, +		(size_t) (growth-1) * sizeof(char *)); +	env.cacheSize += growth;      }  } @@ -720,110 +725,16 @@ TclFinalizeEnvironment(void)       * unlikely, so we don't bother.       */ -    if (environCache) { -	ckfree((char *) environCache); -	environCache = NULL; -	cacheSize = 0; +    if (env.cache) { +	ckfree(env.cache); +	env.cache = NULL; +	env.cacheSize = 0;  #ifndef USE_PUTENV -	environSize = 0; +	env.ourEnvironSize = 0;  #endif      }  } -#if defined(__CYGWIN__) && defined(__WIN32__) - -#include <windows.h> - -/* - * When using cygwin, when an environment variable changes, we need to synch - * with both the cygwin environment (in case the application C code calls - * fork) and the Windows environment (in case the application TCL code calls - * exec, which calls the Windows CreateProcess function). - */ - -static void -TclCygwinPutenv( -    const char *str) -{ -    char *name, *value; - -    /* -     * Get the name and value, so that we can change the environment variable -     * for Windows. -     */ - -    name = (char *) alloca(strlen(str) + 1); -    strcpy(name, str); -    for (value=name ; *value!='=' && *value!='\0' ; ++value) { -	/* Empty body */ -    } -    if (*value == '\0') { -	/* Can't happen. */ -	return; -    } -    *value = '\0'; -    ++value; -    if (*value == '\0') { -	value = NULL; -    } - -    /* -     * Set the cygwin environment variable. -     */ - -#undef putenv -    if (value == NULL) { -	unsetenv(name); -    } else { -	putenv(str); -    } - -    /* -     * Before changing the environment variable in Windows, if this is PATH, -     * we need to convert the value back to a Windows style path. -     * -     * FIXME: The calling program may know it is running under windows, and -     * may have set the path to a Windows path, or, worse, appended or -     * prepended a Windows path to PATH. -     */ - -    if (strcmp(name, "PATH") != 0) { -	/* -	 * If this is Path, eliminate any PATH variable, to prevent any -	 * confusion. -	 */ - -	if (strcmp(name, "Path") == 0) { -	    SetEnvironmentVariable("PATH", NULL); -	    unsetenv("PATH"); -	} - -	SetEnvironmentVariable(name, value); -    } else { -	char *buf; - -	/* -	 * Eliminate any Path variable, to prevent any confusion. -	 */ - -	SetEnvironmentVariable("Path", NULL); -	unsetenv("Path"); - -	if (value == NULL) { -	    buf = NULL; -	} else { -	    int size; - -	    size = cygwin_posix_to_win32_path_list_buf_size(value); -	    buf = (char *) alloca(size + 1); -	    cygwin_posix_to_win32_path_list(value, buf); -	} - -	SetEnvironmentVariable(name, buf); -    } -} -#endif /* __CYGWIN__ && __WIN32__ */ -  /*   * Local Variables:   * mode: c | 
