4 * Tcl support for environment variables, including a setenv function.
5 * This file contains the generic portion of the environment module. It
6 * is primarily responsible for keeping the "env" arrays in sync with the
7 * system environment variables.
9 * Copyright (c) 1991-1994 The Regents of the University of California.
10 * Copyright (c) 1994-1998 Sun Microsystems, Inc.
12 * See the file "license.terms" for information on usage and redistribution of
13 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
18 TCL_DECLARE_MUTEX(envMutex) /* To serialize access to environ. */
21 # define tenviron _wenviron
22 # define tenviron2utfdstr(tenvstr, len, dstr) \
23 Tcl_WinTCharToUtf((TCHAR *)tenvstr, len, dstr)
24 # define utf2tenvirondstr(str, len, dstr) \
25 (const WCHAR *)Tcl_WinUtfToTChar(str, len, dstr)
28 # define putenv(env) _wputenv((const wchar_t *)env)
31 # define tenviron environ
32 # define tenviron2utfdstr(tenvstr, len, dstr) \
33 Tcl_ExternalToUtfDString(NULL, tenvstr, len, dstr)
34 # define utf2tenvirondstr(str, len, dstr) \
35 Tcl_UtfToExternalDString(NULL, str, len, dstr)
41 size_t TclEnvEpoch = 0; /* Epoch of the tcl environment
42 * (if changed with tcl-env). */
45 int cacheSize; /* Number of env strings in cache. */
46 char **cache; /* Array containing all of the environment
47 * strings that Tcl has allocated. */
49 techar **ourEnviron; /* Cache of the array that we allocate. We
50 * need to track this in case another
51 * subsystem swaps around the environ array
53 int ourEnvironSize; /* Non-zero means that the environ array was
54 * malloced and has this many total entries
55 * allocated to it (not all may be in use at
56 * once). Zero means that the environment
57 * array is in its original static state. */
61 #define tNTL sizeof(techar)
64 * Declarations for local functions defined in this file:
67 static char * EnvTraceProc(ClientData clientData, Tcl_Interp *interp,
68 const char *name1, const char *name2, int flags);
69 static void ReplaceString(const char *oldStr, char *newStr);
70 MODULE_SCOPE void TclSetEnv(const char *name, const char *value);
71 MODULE_SCOPE void TclUnsetEnv(const char *name);
74 *----------------------------------------------------------------------
78 * This function is invoked for an interpreter to make environment
79 * variables accessible from that interpreter via the "env" associative
86 * The interpreter is added to a list of interpreters managed by us, so
87 * that its view of envariables can be kept consistent with the view in
88 * other interpreters. If this is the first call to TclSetupEnv, then
89 * additional initialization happens, such as copying the environment to
90 * dynamically-allocated space for ease of management.
92 *----------------------------------------------------------------------
97 Tcl_Interp *interp) /* Interpreter whose "env" array is to be
100 Var *varPtr, *arrayPtr;
102 Tcl_DString envString;
103 Tcl_HashTable namesHash;
105 Tcl_HashSearch search;
108 * Synchronize the values in the environ array with the contents of the
109 * Tcl "env" variable. To do this:
110 * 1) Remove the trace that fires when the "env" var is updated.
111 * 2) Find the existing contents of the "env", storing in a hash table.
112 * 3) Create/update elements for each environ variable, removing
113 * elements from the hash table as we go.
114 * 4) Remove the elements for each remaining entry in the hash table,
115 * which must have existed before yet have no analog in the environ
117 * 5) Add a trace that synchronizes the "env" array.
120 Tcl_UntraceVar2(interp, "env", NULL,
121 TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
122 TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc, NULL);
125 * Find out what elements are currently in the global env array.
128 TclNewLiteralStringObj(varNamePtr, "env");
129 Tcl_IncrRefCount(varNamePtr);
130 Tcl_InitObjHashTable(&namesHash);
131 varPtr = TclObjLookupVarEx(interp, varNamePtr, NULL, TCL_GLOBAL_ONLY,
132 /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
133 TclFindArrayPtrElements(varPtr, &namesHash);
136 if (tenviron == NULL) {
138 * When we are started from main(), the _wenviron array could
139 * be NULL and will be initialized by the first _wgetenv() call.
142 (void) _wgetenv(L"WINDIR");
147 * Go through the environment array and transfer its values into Tcl. At
148 * the same time, remove those elements we add/update from the hash table
149 * of existing elements, so that after this part processes, that table
150 * will hold just the parts to remove.
153 if (tenviron[0] != NULL) {
156 Tcl_MutexLock(&envMutex);
157 for (i = 0; tenviron[i] != NULL; i++) {
158 Tcl_Obj *obj1, *obj2;
162 p1 = tenviron2utfdstr(tenviron[i], -1, &envString);
163 p2 = (char *)strchr(p1, '=');
166 * This condition seem to happen occasionally under some
167 * versions of Solaris, or when encoding accidents swallow the
168 * '='; ignore the entry.
171 Tcl_DStringFree(&envString);
178 * Enforce PATH and COMSPEC to be all uppercase. This eliminates
179 * additional trace logic otherwise required in init.tcl.
182 if (strcasecmp(p1, "PATH") == 0) {
184 } else if (strcasecmp(p1, "COMSPEC") == 0) {
188 obj1 = Tcl_NewStringObj(p1, -1);
189 obj2 = Tcl_NewStringObj(p2, -1);
190 Tcl_DStringFree(&envString);
192 Tcl_IncrRefCount(obj1);
193 Tcl_IncrRefCount(obj2);
194 Tcl_ObjSetVar2(interp, varNamePtr, obj1, obj2, TCL_GLOBAL_ONLY);
195 hPtr = Tcl_FindHashEntry(&namesHash, obj1);
197 Tcl_DeleteHashEntry(hPtr);
199 Tcl_DecrRefCount(obj1);
200 Tcl_DecrRefCount(obj2);
202 Tcl_MutexUnlock(&envMutex);
206 * Delete those elements that existed in the array but which had no
207 * counterparts in the environment array.
210 for (hPtr=Tcl_FirstHashEntry(&namesHash, &search); hPtr!=NULL;
211 hPtr=Tcl_NextHashEntry(&search)) {
212 Tcl_Obj *elemName = (Tcl_Obj *)Tcl_GetHashValue(hPtr);
214 TclObjUnsetVar2(interp, varNamePtr, elemName, TCL_GLOBAL_ONLY);
216 Tcl_DeleteHashTable(&namesHash);
217 Tcl_DecrRefCount(varNamePtr);
220 * Re-establish the trace.
223 Tcl_TraceVar2(interp, "env", NULL,
224 TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
225 TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc, NULL);
229 *----------------------------------------------------------------------
233 * Set an environment variable, replacing an existing value or creating a
234 * new variable if there doesn't exist a variable by the given name. This
235 * function is intended to be a stand-in for the UNIX "setenv" function
236 * so that applications using that function will interface properly to
237 * Tcl. To make it a stand-in, the Makefile must define "TclSetEnv" to
244 * The environ array gets updated.
246 *----------------------------------------------------------------------
251 const char *name, /* Name of variable whose value is to be set
253 const char *value) /* New value for variable (UTF-8). */
255 Tcl_DString envString;
256 unsigned nameLength, valueLength;
262 * Figure out where the entry is going to go. If the name doesn't already
263 * exist, enlarge the array if necessary to make room. If the name exists,
264 * free its old entry.
267 Tcl_MutexLock(&envMutex);
268 index = TclpFindVariable(name, &length);
273 * We need to handle the case where the environment may be changed
274 * outside our control. ourEnvironSize is only valid if the current
275 * environment is the one we allocated. [Bug 979640]
278 if ((env.ourEnviron != tenviron) || (length+2 > env.ourEnvironSize)) {
279 techar **newEnviron = (techar **)ckalloc((length + 5) * sizeof(techar *));
281 memcpy(newEnviron, tenviron, length * sizeof(techar *));
282 if ((env.ourEnvironSize != 0) && (env.ourEnviron != NULL)) {
283 ckfree(env.ourEnviron);
285 tenviron = (env.ourEnviron = newEnviron);
286 env.ourEnvironSize = length + 5;
289 tenviron[index + 1] = NULL;
290 #endif /* USE_PUTENV */
292 nameLength = strlen(name);
297 * Compare the new value to the existing value. If they're the same
298 * then quit immediately (e.g. don't rewrite the value or propagate it
299 * to other interpreters). Otherwise, when there are N interpreters
300 * there will be N! propagations of the same value among the
304 oldEnv = tenviron2utfdstr(tenviron[index], -1, &envString);
305 if (strcmp(value, oldEnv + (length + 1)) == 0) {
306 Tcl_DStringFree(&envString);
307 Tcl_MutexUnlock(&envMutex);
310 Tcl_DStringFree(&envString);
312 oldValue = (char *)tenviron[index];
317 * Create a new entry. Build a complete UTF string that contains a
318 * "name=value" pattern. Then convert the string to the native encoding,
319 * and set the environ array value.
322 valueLength = strlen(value);
323 p = (char *)ckalloc(nameLength + valueLength + 2);
324 memcpy(p, name, nameLength);
326 memcpy(p+nameLength+1, value, valueLength+1);
327 p2 = utf2tenvirondstr(p, -1, &envString);
330 * Copy the native string to heap memory.
333 p = (char *)ckrealloc(p, Tcl_DStringLength(&envString) + tNTL);
334 memcpy(p, p2, Tcl_DStringLength(&envString) + tNTL);
335 Tcl_DStringFree(&envString);
339 * Update the system environment.
343 index = TclpFindVariable(name, &length);
345 tenviron[index] = (techar *)p;
346 #endif /* USE_PUTENV */
349 * Watch out for versions of putenv that copy the string (e.g. VC++). In
350 * this case we need to free the string immediately. Otherwise update the
351 * string in the cache.
354 if ((index != -1) && (tenviron[index] == (techar *)p)) {
355 ReplaceString(oldValue, p);
356 #ifdef HAVE_PUTENV_THAT_COPIES
359 * This putenv() copies instead of taking ownership.
363 #endif /* HAVE_PUTENV_THAT_COPIES */
366 Tcl_MutexUnlock(&envMutex);
368 if (!strcmp(name, "HOME")) {
370 * If the user's home directory has changed, we must invalidate the
371 * filesystem cache, because '~' expansions will now be incorrect.
374 Tcl_FSMountsChanged(NULL);
379 *----------------------------------------------------------------------
383 * Set an environment variable. Similar to setenv except that the
384 * information is passed in a single string of the form NAME=value,
385 * rather than as separate name strings. This function is intended to be
386 * a stand-in for the UNIX "putenv" function so that applications using
387 * that function will interface properly to Tcl. To make it a stand-in,
388 * the Makefile will define "Tcl_PutEnv" to "putenv".
394 * The environ array gets updated, as do all of the interpreters that we
397 *----------------------------------------------------------------------
402 const char *assignment) /* Info about environment variable in the form
403 * NAME=value. (native) */
405 Tcl_DString nameString;
409 if (assignment == NULL) {
414 * First convert the native string to UTF. Then separate the string into
415 * name and value parts, and call TclSetEnv to do all of the real work.
418 name = Tcl_ExternalToUtfDString(NULL, assignment, -1, &nameString);
419 value = (char *)strchr(name, '=');
421 if ((value != NULL) && (value != name)) {
424 if (tenviron == NULL) {
426 * When we are started from main(), the _wenviron array could
427 * be NULL and will be initialized by the first _wgetenv() call.
430 (void) _wgetenv(L"WINDIR");
433 TclSetEnv(name, value+1);
437 Tcl_DStringFree(&nameString);
442 *----------------------------------------------------------------------
446 * Remove an environment variable, updating the "env" arrays in all
447 * interpreters managed by us. This function is intended to replace the
448 * UNIX "unsetenv" function (but to do this the Makefile must be modified
449 * to redefine "TclUnsetEnv" to "unsetenv".
455 * Interpreters are updated, as is environ.
457 *----------------------------------------------------------------------
462 const char *name) /* Name of variable to remove (UTF-8). */
467 #ifdef USE_PUTENV_FOR_UNSET
468 Tcl_DString envString;
472 #endif /* USE_PUTENV_FOR_UNSET */
474 Tcl_MutexLock(&envMutex);
475 index = TclpFindVariable(name, &length);
478 * First make sure that the environment variable exists to avoid doing
479 * needless work and to avoid recursion on the unset.
483 Tcl_MutexUnlock(&envMutex);
488 * Remember the old value so we can free it if Tcl created the string.
491 oldValue = (char *)tenviron[index];
494 * Update the system environment. This must be done before we update the
495 * interpreters or we will recurse.
498 #ifdef USE_PUTENV_FOR_UNSET
500 * For those platforms that support putenv to unset, Linux indicates
501 * that no = should be included, and Windows requires it.
505 string = (char *)ckalloc(length + 2);
506 memcpy(string, name, length);
507 string[length] = '=';
508 string[length+1] = '\0';
510 string = (char *)ckalloc(length + 1);
511 memcpy(string, name, length);
512 string[length] = '\0';
515 utf2tenvirondstr(string, -1, &envString);
516 string = (char *)ckrealloc(string, Tcl_DStringLength(&envString) + tNTL);
517 memcpy(string, Tcl_DStringValue(&envString),
518 Tcl_DStringLength(&envString) + tNTL);
519 Tcl_DStringFree(&envString);
524 * Watch out for versions of putenv that copy the string (e.g. VC++). In
525 * this case we need to free the string immediately. Otherwise update the
526 * string in the cache.
529 if (tenviron[index] == (techar *)string) {
530 ReplaceString(oldValue, string);
531 #ifdef HAVE_PUTENV_THAT_COPIES
534 * This putenv() copies instead of taking ownership.
538 #endif /* HAVE_PUTENV_THAT_COPIES */
540 #else /* !USE_PUTENV_FOR_UNSET */
541 for (envPtr = (char **)(tenviron+index+1); ; envPtr++) {
542 envPtr[-1] = *envPtr;
543 if (*envPtr == NULL) {
547 ReplaceString(oldValue, NULL);
548 #endif /* USE_PUTENV_FOR_UNSET */
550 Tcl_MutexUnlock(&envMutex);
554 *---------------------------------------------------------------------------
558 * Retrieve the value of an environment variable.
561 * The result is a pointer to a string specifying the value of the
562 * environment variable, or NULL if that environment variable does not
563 * exist. Storage for the result string is allocated in valuePtr; the
564 * caller must call Tcl_DStringFree() when the result is no longer
570 *----------------------------------------------------------------------
575 const char *name, /* Name of environment variable to find
577 Tcl_DString *valuePtr) /* Uninitialized or free DString in which the
578 * value of the environment variable is
584 Tcl_MutexLock(&envMutex);
585 index = TclpFindVariable(name, &length);
590 result = tenviron2utfdstr(tenviron[index], -1, &envStr);
592 if (*result == '=') {
594 Tcl_DStringInit(valuePtr);
595 Tcl_DStringAppend(valuePtr, result, -1);
596 result = Tcl_DStringValue(valuePtr);
600 Tcl_DStringFree(&envStr);
602 Tcl_MutexUnlock(&envMutex);
607 *----------------------------------------------------------------------
611 * This function is invoked whenever an environment variable is read,
612 * modified or deleted. It propagates the change to the global "environ"
616 * Returns NULL to indicate success, or an error-message if the array
617 * element being handled doesn't exist.
620 * Environment variable changes get propagated. If the whole "env" array
621 * is deleted, then we stop managing things for this interpreter (usually
622 * this happens because the whole interpreter is being deleted).
624 *----------------------------------------------------------------------
630 ClientData clientData, /* Not used. */
631 Tcl_Interp *interp, /* Interpreter whose "env" variable is being
633 const char *name1, /* Better be "env". */
634 const char *name2, /* Name of variable being modified, or NULL if
635 * whole array is being deleted (UTF-8). */
636 int flags) /* Indicates what's happening. */
639 * For array traces, let TclSetupEnv do all the work.
642 if (flags & TCL_TRACE_ARRAY) {
649 * If name2 is NULL, then return and do nothing.
657 * If a value is being set, call TclSetEnv to do all of the work.
660 if (flags & TCL_TRACE_WRITES) {
663 value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY);
664 TclSetEnv(name2, value);
669 * If a value is being read, call TclGetEnv to do all of the work.
672 if (flags & TCL_TRACE_READS) {
673 Tcl_DString valueString;
674 const char *value = TclGetEnv(name2, &valueString);
677 return (char *) "no such variable";
679 Tcl_SetVar2(interp, name1, name2, value, 0);
680 Tcl_DStringFree(&valueString);
684 * For unset traces, let TclUnsetEnv do all the work.
687 if (flags & TCL_TRACE_UNSETS) {
695 *----------------------------------------------------------------------
699 * Replace one string with another in the environment variable cache. The
700 * cache keeps track of all of the environment variables that Tcl has
701 * modified so they can be freed later.
707 * May free the old string.
709 *----------------------------------------------------------------------
714 const char *oldStr, /* Old environment string. */
715 char *newStr) /* New environment string. */
720 * Check to see if the old value was allocated by Tcl. If so, it needs to
721 * be deallocated to avoid memory leaks. Note that this algorithm is O(n),
722 * not O(1). This will result in n-squared behavior if lots of environment
723 * changes are being made.
726 for (i = 0; i < env.cacheSize; i++) {
727 if (env.cache[i]==oldStr || env.cache[i]==NULL) {
731 if (i < env.cacheSize) {
733 * Replace or delete the old value.
737 ckfree(env.cache[i]);
741 env.cache[i] = newStr;
743 for (; i < env.cacheSize-1; i++) {
744 env.cache[i] = env.cache[i+1];
746 env.cache[env.cacheSize-1] = NULL;
750 * We need to grow the cache in order to hold the new string.
753 const int growth = 5;
755 env.cache = (char **)ckrealloc(env.cache,
756 (env.cacheSize + growth) * sizeof(char *));
757 env.cache[env.cacheSize] = newStr;
758 (void) memset(env.cache+env.cacheSize+1, 0,
759 (size_t) (growth-1) * sizeof(char *));
760 env.cacheSize += growth;
765 *----------------------------------------------------------------------
767 * TclFinalizeEnvironment --
769 * This function releases any storage allocated by this module that isn't
770 * still in use by the global environment. Any strings that are still in
771 * the environment will be leaked.
777 * May deallocate storage.
779 *----------------------------------------------------------------------
783 TclFinalizeEnvironment(void)
786 * For now we just deallocate the cache array and none of the environment
787 * strings. This may leak more memory that strictly necessary, since some
788 * of the strings may no longer be in the environment. However,
789 * determining which ones are ok to delete is n-squared, and is pretty
790 * unlikely, so we don't bother. However, in the case of DPURIFY, just
791 * free all strings in the cache.
797 for (i = 0; i < env.cacheSize; i++) {
798 ckfree(env.cache[i]);
805 if ((env.ourEnviron != NULL)) {
806 ckfree(env.ourEnviron);
807 env.ourEnviron = NULL;
809 env.ourEnvironSize = 0;