4 * Tcl support for environment variables, including a setenv
5 * procedure. This file contains the generic portion of the
6 * environment module. It is primarily responsible for keeping
7 * the "env" arrays in sync with the 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
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
21 TCL_DECLARE_MUTEX(envMutex) /* To serialize access to environ */
24 #if defined(__CYGWIN__) && defined(__WIN32__)
26 /* Under cygwin, the environment is imported from the cygwin DLL. */
28 __declspec(dllimport) extern char **__cygwin_environ;
30 #define environ (__cygwin_environ)
32 /* We need to use a special putenv function to handle PATH. */
36 #define putenv TclCygwin32Putenv
38 /* END CYGNUS LOCAL */
42 static Tcl_Mutex envMutex; /* To serialize access to environ */
45 static int cacheSize = 0; /* Number of env strings in environCache. */
46 static char **environCache = NULL;
47 /* Array containing all of the environment
48 * strings that Tcl has allocated. */
51 static int environSize = 0; /* Non-zero means that the environ array was
52 * malloced and has this many total entries
53 * allocated to it (not all may be in use at
54 * once). Zero means that the environment
55 * array is in its original static state. */
61 #if defined(__APPLE__) && defined(__DYNAMIC__)
62 #include <crt_externs.h>
63 char **environ = NULL;
67 * Declarations for local procedures defined in this file:
70 static char * EnvTraceProc _ANSI_ARGS_((ClientData clientData,
71 Tcl_Interp *interp, char *name1, char *name2,
73 static void ReplaceString _ANSI_ARGS_((CONST char *oldStr,
75 void TclSetEnv _ANSI_ARGS_((CONST char *name,
77 void TclUnsetEnv _ANSI_ARGS_((CONST char *name));
80 #if defined (__CYGWIN__) && defined(__WIN32__)
81 static void TclCygwin32Putenv _ANSI_ARGS_((CONST char *string));
85 *----------------------------------------------------------------------
89 * This procedure is invoked for an interpreter to make environment
90 * variables accessible from that interpreter via the "env"
97 * The interpreter is added to a list of interpreters managed
98 * by us, so that its view of envariables can be kept consistent
99 * with the view in other interpreters. If this is the first
100 * call to TclSetupEnv, then additional initialization happens,
101 * such as copying the environment to dynamically-allocated space
102 * for ease of management.
104 *----------------------------------------------------------------------
109 Tcl_Interp *interp; /* Interpreter whose "env" array is to be
112 Tcl_DString envString;
119 #if defined(__APPLE__) && defined(__DYNAMIC__)
120 environ = *_NSGetEnviron();
124 * Synchronize the values in the environ array with the contents
125 * of the Tcl "env" variable. To do this:
126 * 1) Remove the trace that fires when the "env" var is unset.
127 * 2) Unset the "env" variable.
128 * 3) If there are no environ variables, create an empty "env"
129 * array. Otherwise populate the array with current values.
130 * 4) Add a trace that synchronizes the "env" array.
133 Tcl_UntraceVar2(interp, "env", (char *) NULL,
134 TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
135 TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc,
138 Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY);
140 if (environ[0] == NULL) {
143 varNamePtr = Tcl_NewStringObj("env", -1);
144 Tcl_IncrRefCount(varNamePtr);
145 TclArraySet(interp, varNamePtr, NULL);
146 Tcl_DecrRefCount(varNamePtr);
148 Tcl_MutexLock(&envMutex);
149 for (i = 0; environ[i] != NULL; i++) {
150 p1 = Tcl_ExternalToUtfDString(NULL, environ[i], -1, &envString);
151 p2 = strchr(p1, '=');
154 * This condition seem to happen occasionally under some
155 * versions of Solaris; ignore the entry.
162 Tcl_SetVar2(interp, "env", p1, p2, TCL_GLOBAL_ONLY);
163 Tcl_DStringFree(&envString);
165 Tcl_MutexUnlock(&envMutex);
168 Tcl_TraceVar2(interp, "env", (char *) NULL,
169 TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
170 TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc,
175 *----------------------------------------------------------------------
179 * Set an environment variable, replacing an existing value
180 * or creating a new variable if there doesn't exist a variable
181 * by the given name. This procedure is intended to be a
182 * stand-in for the UNIX "setenv" procedure so that applications
183 * using that procedure will interface properly to Tcl. To make
184 * it a stand-in, the Makefile must define "TclSetEnv" to "setenv".
190 * The environ array gets updated.
192 *----------------------------------------------------------------------
196 TclSetEnv(name, value)
197 CONST char *name; /* Name of variable whose value is to be
199 CONST char *value; /* New value for variable (UTF-8). */
201 Tcl_DString envString;
202 int index, length, nameLength;
203 char *p, *p2, *oldValue;
206 * Figure out where the entry is going to go. If the name doesn't
207 * already exist, enlarge the array if necessary to make room. If the
208 * name exists, free its old entry.
211 Tcl_MutexLock(&envMutex);
212 index = TclpFindVariable(name, &length);
216 if ((length + 2) > environSize) {
219 newEnviron = (char **) ckalloc((unsigned)
220 ((length + 5) * sizeof(char *)));
222 /* CYGNUS LOCAL: Added to avoid an error from Purify,
223 although I don't personally see where the error would
225 memset((VOID *) newEnviron, 0, (length+5) * sizeof(char *));
227 memcpy((VOID *) newEnviron, (VOID *) environ,
228 length*sizeof(char *));
229 if (environSize != 0) {
230 ckfree((char *) environ);
232 environ = newEnviron;
233 environSize = length + 5;
236 environ[index + 1] = NULL;
239 nameLength = strlen(name);
244 * Compare the new value to the existing value. If they're
245 * the same then quit immediately (e.g. don't rewrite the
246 * value or propagate it to other interpreters). Otherwise,
247 * when there are N interpreters there will be N! propagations
248 * of the same value among the interpreters.
251 env = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envString);
252 if (strcmp(value, (env + length + 1)) == 0) {
253 Tcl_DStringFree(&envString);
254 Tcl_MutexUnlock(&envMutex);
257 Tcl_DStringFree(&envString);
259 oldValue = environ[index];
265 * Create a new entry. Build a complete UTF string that contains
266 * a "name=value" pattern. Then convert the string to the native
267 * encoding, and set the environ array value.
270 p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2));
273 strcpy(p+nameLength+1, value);
274 p2 = Tcl_UtfToExternalDString(NULL, p, -1, &envString);
277 * Copy the native string to heap memory.
280 p = (char *) ckrealloc(p, (unsigned) (strlen(p2) + 1));
282 Tcl_DStringFree(&envString);
286 * Update the system environment.
290 index = TclpFindVariable(name, &length);
296 * Watch out for versions of putenv that copy the string (e.g. VC++).
297 * In this case we need to free the string immediately. Otherwise
298 * update the string in the cache.
301 if ((index != -1) && (environ[index] == p)) {
302 ReplaceString(oldValue, p);
305 Tcl_MutexUnlock(&envMutex);
309 *----------------------------------------------------------------------
313 * Set an environment variable. Similar to setenv except that
314 * the information is passed in a single string of the form
315 * NAME=value, rather than as separate name strings. This procedure
316 * is intended to be a stand-in for the UNIX "putenv" procedure
317 * so that applications using that procedure will interface
318 * properly to Tcl. To make it a stand-in, the Makefile will
319 * define "Tcl_PutEnv" to "putenv".
325 * The environ array gets updated, as do all of the interpreters
328 *----------------------------------------------------------------------
333 CONST char *string; /* Info about environment variable in the
334 * form NAME=value. (native) */
336 Tcl_DString nameString;
340 if (string == NULL) {
345 * First convert the native string to UTF. Then separate the
346 * string into name and value parts, and call TclSetEnv to do
347 * all of the real work.
350 name = Tcl_ExternalToUtfDString(NULL, string, -1, &nameString);
351 value = strchr(name, '=');
355 nameLength = value - name;
356 if (nameLength == 0) {
361 TclSetEnv(name, value+1);
362 Tcl_DStringFree(&nameString);
367 *----------------------------------------------------------------------
371 * Remove an environment variable, updating the "env" arrays
372 * in all interpreters managed by us. This function is intended
373 * to replace the UNIX "unsetenv" function (but to do this the
374 * Makefile must be modified to redefine "TclUnsetEnv" to
381 * Interpreters are updated, as is environ.
383 *----------------------------------------------------------------------
388 CONST char *name; /* Name of variable to remove (UTF-8). */
394 Tcl_DString envString;
400 Tcl_MutexLock(&envMutex);
401 index = TclpFindVariable(name, &length);
404 * First make sure that the environment variable exists to avoid
405 * doing needless work and to avoid recursion on the unset.
409 Tcl_MutexUnlock(&envMutex);
413 * Remember the old value so we can free it if Tcl created the string.
416 oldValue = environ[index];
419 * Update the system environment. This must be done before we
420 * update the interpreters or we will recurse.
424 string = ckalloc(length+2);
425 memcpy((VOID *) string, (VOID *) name, (size_t) length);
426 string[length] = '=';
427 string[length+1] = '\0';
429 Tcl_UtfToExternalDString(NULL, string, -1, &envString);
430 string = ckrealloc(string, (unsigned) (Tcl_DStringLength(&envString)+1));
431 strcpy(string, Tcl_DStringValue(&envString));
432 Tcl_DStringFree(&envString);
437 * Watch out for versions of putenv that copy the string (e.g. VC++).
438 * In this case we need to free the string immediately. Otherwise
439 * update the string in the cache.
442 if (environ[index] == string) {
443 ReplaceString(oldValue, string);
446 for (envPtr = environ+index+1; ; envPtr++) {
447 envPtr[-1] = *envPtr;
448 if (*envPtr == NULL) {
452 ReplaceString(oldValue, NULL);
455 Tcl_MutexUnlock(&envMutex);
459 *---------------------------------------------------------------------------
463 * Retrieve the value of an environment variable.
466 * The result is a pointer to a string specifying the value of the
467 * environment variable, or NULL if that environment variable does
468 * not exist. Storage for the result string is allocated in valuePtr;
469 * the caller must call Tcl_DStringFree() when the result is no
475 *----------------------------------------------------------------------
479 TclGetEnv(name, valuePtr)
480 CONST char *name; /* Name of environment variable to find
482 Tcl_DString *valuePtr; /* Uninitialized or free DString in which
483 * the value of the environment variable is
489 Tcl_MutexLock(&envMutex);
490 index = TclpFindVariable(name, &length);
495 result = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envStr);
497 if (*result == '=') {
499 Tcl_DStringInit(valuePtr);
500 Tcl_DStringAppend(valuePtr, result, -1);
501 result = Tcl_DStringValue(valuePtr);
505 Tcl_DStringFree(&envStr);
507 Tcl_MutexUnlock(&envMutex);
512 *----------------------------------------------------------------------
516 * This procedure is invoked whenever an environment variable
517 * is read, modified or deleted. It propagates the change to the global
521 * Always returns NULL to indicate success.
524 * Environment variable changes get propagated. If the whole
525 * "env" array is deleted, then we stop managing things for
526 * this interpreter (usually this happens because the whole
527 * interpreter is being deleted).
529 *----------------------------------------------------------------------
534 EnvTraceProc(clientData, interp, name1, name2, flags)
535 ClientData clientData; /* Not used. */
536 Tcl_Interp *interp; /* Interpreter whose "env" variable is
538 char *name1; /* Better be "env". */
539 char *name2; /* Name of variable being modified, or NULL
540 * if whole array is being deleted (UTF-8). */
541 int flags; /* Indicates what's happening. */
544 * For array traces, let TclSetupEnv do all the work.
547 if (flags & TCL_TRACE_ARRAY) {
553 * If name2 is NULL, then return and do nothing.
561 * If a value is being set, call TclSetEnv to do all of the work.
564 if (flags & TCL_TRACE_WRITES) {
567 value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY);
568 TclSetEnv(name2, value);
572 * If a value is being read, call TclGetEnv to do all of the work.
575 if (flags & TCL_TRACE_READS) {
576 Tcl_DString valueString;
579 value = TclGetEnv(name2, &valueString);
581 return "no such variable";
583 Tcl_SetVar2(interp, name1, name2, value, 0);
584 Tcl_DStringFree(&valueString);
588 * For unset traces, let TclUnsetEnv do all the work.
591 if (flags & TCL_TRACE_UNSETS) {
598 *----------------------------------------------------------------------
602 * Replace one string with another in the environment variable
603 * cache. The cache keeps track of all of the environment
604 * variables that Tcl has modified so they can be freed later.
610 * May free the old string.
612 *----------------------------------------------------------------------
616 ReplaceString(oldStr, newStr)
617 CONST char *oldStr; /* Old environment string. */
618 char *newStr; /* New environment string. */
624 * Check to see if the old value was allocated by Tcl. If so,
625 * it needs to be deallocated to avoid memory leaks. Note that this
626 * algorithm is O(n), not O(1). This will result in n-squared behavior
627 * if lots of environment changes are being made.
630 for (i = 0; i < cacheSize; i++) {
631 if ((environCache[i] == oldStr) || (environCache[i] == NULL)) {
637 * Replace or delete the old value.
640 if (environCache[i]) {
641 ckfree(environCache[i]);
645 environCache[i] = newStr;
647 for (; i < cacheSize-1; i++) {
648 environCache[i] = environCache[i+1];
650 environCache[cacheSize-1] = NULL;
653 int allocatedSize = (cacheSize + 5) * sizeof(char *);
656 * We need to grow the cache in order to hold the new string.
659 newCache = (char **) ckalloc((unsigned) allocatedSize);
660 (VOID *) memset(newCache, (int) 0, (size_t) allocatedSize);
663 memcpy((VOID *) newCache, (VOID *) environCache,
664 (size_t) (cacheSize * sizeof(char*)));
665 ckfree((char *) environCache);
667 environCache = newCache;
668 environCache[cacheSize] = (char *) newStr;
669 environCache[cacheSize+1] = NULL;
675 *----------------------------------------------------------------------
677 * TclFinalizeEnvironment --
679 * This function releases any storage allocated by this module
680 * that isn't still in use by the global environment. Any
681 * strings that are still in the environment will be leaked.
687 * May deallocate storage.
689 *----------------------------------------------------------------------
693 TclFinalizeEnvironment()
696 * For now we just deallocate the cache array and none of the environment
697 * strings. This may leak more memory that strictly necessary, since some
698 * of the strings may no longer be in the environment. However,
699 * determining which ones are ok to delete is n-squared, and is pretty
700 * unlikely, so we don't bother.
704 ckfree((char *) environCache);
714 #if defined(__CYGWIN__) && defined(__WIN32__)
718 /* When using cygwin, when an environment variable changes, we need
719 to synch with both the cygwin environment (in case the
720 application C code calls fork) and the Windows environment (in case
721 the application TCL code calls exec, which calls the Windows
722 CreateProcess function). */
725 TclCygwin32Putenv(str)
730 /* Get the name and value, so that we can change the environment
731 variable for Windows. */
732 name = (char *) alloca (strlen (str) + 1);
734 for (value = name; *value != '=' && *value != '\0'; ++value)
746 /* Set the cygwin environment variable. */
753 /* Before changing the environment variable in Windows, if this is
754 PATH, we need to convert the value back to a Windows style path.
756 FIXME: The calling program may now it is running under windows,
757 and may have set the path to a Windows path, or, worse, appended
758 or prepended a Windows path to PATH. */
759 if (strcmp (name, "PATH") != 0)
761 /* If this is Path, eliminate any PATH variable, to prevent any
763 if (strcmp (name, "Path") == 0)
765 SetEnvironmentVariable ("PATH", (char *) NULL);
769 SetEnvironmentVariable (name, value);
775 /* Eliminate any Path variable, to prevent any confusion. */
776 SetEnvironmentVariable ("Path", (char *) NULL);
785 size = cygwin_posix_to_win32_path_list_buf_size (value);
786 buf = (char *) alloca (size + 1);
787 cygwin_posix_to_win32_path_list (value, buf);
790 SetEnvironmentVariable (name, buf);
794 #endif /* __CYGWIN__ */
795 /* END CYGNUS LOCAL */