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 */
23 static int cacheSize = 0; /* Number of env strings in environCache. */
24 static char **environCache = NULL;
25 /* Array containing all of the environment
26 * strings that Tcl has allocated. */
29 static int environSize = 0; /* Non-zero means that the environ array was
30 * malloced and has this many total entries
31 * allocated to it (not all may be in use at
32 * once). Zero means that the environment
33 * array is in its original static state. */
39 #if defined(__APPLE__) && defined(__DYNAMIC__)
40 #include <crt_externs.h>
41 char **environ = NULL;
45 * Declarations for local procedures defined in this file:
48 static char * EnvTraceProc _ANSI_ARGS_((ClientData clientData,
49 Tcl_Interp *interp, CONST char *name1,
50 CONST char *name2, int flags));
51 static void ReplaceString _ANSI_ARGS_((CONST char *oldStr,
53 void TclSetEnv _ANSI_ARGS_((CONST char *name,
55 void TclUnsetEnv _ANSI_ARGS_((CONST char *name));
57 #if defined (__CYGWIN__) && defined(__WIN32__)
58 static void TclCygwinPutenv _ANSI_ARGS_((CONST char *string));
62 *----------------------------------------------------------------------
66 * This procedure is invoked for an interpreter to make environment
67 * variables accessible from that interpreter via the "env"
74 * The interpreter is added to a list of interpreters managed
75 * by us, so that its view of envariables can be kept consistent
76 * with the view in other interpreters. If this is the first
77 * call to TclSetupEnv, then additional initialization happens,
78 * such as copying the environment to dynamically-allocated space
79 * for ease of management.
81 *----------------------------------------------------------------------
86 Tcl_Interp *interp; /* Interpreter whose "env" array is to be
89 Tcl_DString envString;
96 #if defined(__APPLE__) && defined(__DYNAMIC__)
97 environ = *_NSGetEnviron();
101 * Synchronize the values in the environ array with the contents
102 * of the Tcl "env" variable. To do this:
103 * 1) Remove the trace that fires when the "env" var is unset.
104 * 2) Unset the "env" variable.
105 * 3) If there are no environ variables, create an empty "env"
106 * array. Otherwise populate the array with current values.
107 * 4) Add a trace that synchronizes the "env" array.
110 Tcl_UntraceVar2(interp, "env", (char *) NULL,
111 TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
112 TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc,
115 Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY);
117 if (environ[0] == NULL) {
120 varNamePtr = Tcl_NewStringObj("env", -1);
121 Tcl_IncrRefCount(varNamePtr);
122 TclArraySet(interp, varNamePtr, NULL);
123 Tcl_DecrRefCount(varNamePtr);
125 Tcl_MutexLock(&envMutex);
126 for (i = 0; environ[i] != NULL; i++) {
127 p1 = Tcl_ExternalToUtfDString(NULL, environ[i], -1, &envString);
128 p2 = strchr(p1, '=');
131 * This condition seem to happen occasionally under some
132 * versions of Solaris; ignore the entry.
139 Tcl_SetVar2(interp, "env", p1, p2, TCL_GLOBAL_ONLY);
140 Tcl_DStringFree(&envString);
142 Tcl_MutexUnlock(&envMutex);
145 Tcl_TraceVar2(interp, "env", (char *) NULL,
146 TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
147 TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc,
152 *----------------------------------------------------------------------
156 * Set an environment variable, replacing an existing value
157 * or creating a new variable if there doesn't exist a variable
158 * by the given name. This procedure is intended to be a
159 * stand-in for the UNIX "setenv" procedure so that applications
160 * using that procedure will interface properly to Tcl. To make
161 * it a stand-in, the Makefile must define "TclSetEnv" to "setenv".
167 * The environ array gets updated.
169 *----------------------------------------------------------------------
173 TclSetEnv(name, value)
174 CONST char *name; /* Name of variable whose value is to be
176 CONST char *value; /* New value for variable (UTF-8). */
178 Tcl_DString envString;
179 int index, length, nameLength;
184 * Figure out where the entry is going to go. If the name doesn't
185 * already exist, enlarge the array if necessary to make room. If the
186 * name exists, free its old entry.
189 Tcl_MutexLock(&envMutex);
190 index = TclpFindVariable(name, &length);
194 if ((length + 2) > environSize) {
197 newEnviron = (char **) ckalloc((unsigned)
198 ((length + 5) * sizeof(char *)));
199 memcpy((VOID *) newEnviron, (VOID *) environ,
200 length*sizeof(char *));
201 if (environSize != 0) {
202 ckfree((char *) environ);
204 environ = newEnviron;
205 environSize = length + 5;
206 #if defined(__APPLE__) && defined(__DYNAMIC__)
208 char ***e = _NSGetEnviron();
214 environ[index + 1] = NULL;
217 nameLength = strlen(name);
222 * Compare the new value to the existing value. If they're
223 * the same then quit immediately (e.g. don't rewrite the
224 * value or propagate it to other interpreters). Otherwise,
225 * when there are N interpreters there will be N! propagations
226 * of the same value among the interpreters.
229 env = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envString);
230 if (strcmp(value, (env + length + 1)) == 0) {
231 Tcl_DStringFree(&envString);
232 Tcl_MutexUnlock(&envMutex);
235 Tcl_DStringFree(&envString);
237 oldValue = environ[index];
243 * Create a new entry. Build a complete UTF string that contains
244 * a "name=value" pattern. Then convert the string to the native
245 * encoding, and set the environ array value.
248 p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2));
251 strcpy(p+nameLength+1, value);
252 p2 = Tcl_UtfToExternalDString(NULL, p, -1, &envString);
255 * Copy the native string to heap memory.
258 p = (char *) ckrealloc(p, (unsigned) (strlen(p2) + 1));
260 Tcl_DStringFree(&envString);
264 * Update the system environment.
268 index = TclpFindVariable(name, &length);
274 * Watch out for versions of putenv that copy the string (e.g. VC++).
275 * In this case we need to free the string immediately. Otherwise
276 * update the string in the cache.
279 if ((index != -1) && (environ[index] == p)) {
280 ReplaceString(oldValue, p);
281 #ifdef HAVE_PUTENV_THAT_COPIES
283 /* This putenv() copies instead of taking ownership */
288 Tcl_MutexUnlock(&envMutex);
290 if (!strcmp(name, "HOME")) {
292 * If the user's home directory has changed, we must invalidate
293 * the filesystem cache, because '~' expansions will now be
296 Tcl_FSMountsChanged(NULL);
301 *----------------------------------------------------------------------
305 * Set an environment variable. Similar to setenv except that
306 * the information is passed in a single string of the form
307 * NAME=value, rather than as separate name strings. This procedure
308 * is intended to be a stand-in for the UNIX "putenv" procedure
309 * so that applications using that procedure will interface
310 * properly to Tcl. To make it a stand-in, the Makefile will
311 * define "Tcl_PutEnv" to "putenv".
317 * The environ array gets updated, as do all of the interpreters
320 *----------------------------------------------------------------------
325 CONST char *string; /* Info about environment variable in the
326 * form NAME=value. (native) */
328 Tcl_DString nameString;
332 if (string == NULL) {
337 * First convert the native string to UTF. Then separate the
338 * string into name and value parts, and call TclSetEnv to do
339 * all of the real work.
342 name = Tcl_ExternalToUtfDString(NULL, string, -1, &nameString);
343 value = strchr(name, '=');
345 if ((value != NULL) && (value != name)) {
347 TclSetEnv(name, value+1);
350 Tcl_DStringFree(&nameString);
355 *----------------------------------------------------------------------
359 * Remove an environment variable, updating the "env" arrays
360 * in all interpreters managed by us. This function is intended
361 * to replace the UNIX "unsetenv" function (but to do this the
362 * Makefile must be modified to redefine "TclUnsetEnv" to
369 * Interpreters are updated, as is environ.
371 *----------------------------------------------------------------------
376 CONST char *name; /* Name of variable to remove (UTF-8). */
382 Tcl_DString envString;
388 Tcl_MutexLock(&envMutex);
389 index = TclpFindVariable(name, &length);
392 * First make sure that the environment variable exists to avoid
393 * doing needless work and to avoid recursion on the unset.
397 Tcl_MutexUnlock(&envMutex);
401 * Remember the old value so we can free it if Tcl created the string.
404 oldValue = environ[index];
407 * Update the system environment. This must be done before we
408 * update the interpreters or we will recurse.
412 string = ckalloc(length+2);
413 memcpy((VOID *) string, (VOID *) name, (size_t) length);
414 string[length] = '=';
415 string[length+1] = '\0';
417 Tcl_UtfToExternalDString(NULL, string, -1, &envString);
418 string = ckrealloc(string, (unsigned) (Tcl_DStringLength(&envString)+1));
419 strcpy(string, Tcl_DStringValue(&envString));
420 Tcl_DStringFree(&envString);
425 * Watch out for versions of putenv that copy the string (e.g. VC++).
426 * In this case we need to free the string immediately. Otherwise
427 * update the string in the cache.
430 if (environ[index] == string) {
431 ReplaceString(oldValue, string);
434 for (envPtr = environ+index+1; ; envPtr++) {
435 envPtr[-1] = *envPtr;
436 if (*envPtr == NULL) {
440 ReplaceString(oldValue, NULL);
443 Tcl_MutexUnlock(&envMutex);
447 *---------------------------------------------------------------------------
451 * Retrieve the value of an environment variable.
454 * The result is a pointer to a string specifying the value of the
455 * environment variable, or NULL if that environment variable does
456 * not exist. Storage for the result string is allocated in valuePtr;
457 * the caller must call Tcl_DStringFree() when the result is no
463 *----------------------------------------------------------------------
467 TclGetEnv(name, valuePtr)
468 CONST char *name; /* Name of environment variable to find
470 Tcl_DString *valuePtr; /* Uninitialized or free DString in which
471 * the value of the environment variable is
477 Tcl_MutexLock(&envMutex);
478 index = TclpFindVariable(name, &length);
483 result = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envStr);
485 if (*result == '=') {
487 Tcl_DStringInit(valuePtr);
488 Tcl_DStringAppend(valuePtr, result, -1);
489 result = Tcl_DStringValue(valuePtr);
493 Tcl_DStringFree(&envStr);
495 Tcl_MutexUnlock(&envMutex);
500 *----------------------------------------------------------------------
504 * This procedure is invoked whenever an environment variable
505 * is read, modified or deleted. It propagates the change to the global
509 * Always returns NULL to indicate success.
512 * Environment variable changes get propagated. If the whole
513 * "env" array is deleted, then we stop managing things for
514 * this interpreter (usually this happens because the whole
515 * interpreter is being deleted).
517 *----------------------------------------------------------------------
522 EnvTraceProc(clientData, interp, name1, name2, flags)
523 ClientData clientData; /* Not used. */
524 Tcl_Interp *interp; /* Interpreter whose "env" variable is
526 CONST char *name1; /* Better be "env". */
527 CONST char *name2; /* Name of variable being modified, or NULL
528 * if whole array is being deleted (UTF-8). */
529 int flags; /* Indicates what's happening. */
532 * For array traces, let TclSetupEnv do all the work.
535 if (flags & TCL_TRACE_ARRAY) {
541 * If name2 is NULL, then return and do nothing.
549 * If a value is being set, call TclSetEnv to do all of the work.
552 if (flags & TCL_TRACE_WRITES) {
555 value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY);
556 TclSetEnv(name2, value);
560 * If a value is being read, call TclGetEnv to do all of the work.
563 if (flags & TCL_TRACE_READS) {
564 Tcl_DString valueString;
567 value = TclGetEnv(name2, &valueString);
569 return "no such variable";
571 Tcl_SetVar2(interp, name1, name2, value, 0);
572 Tcl_DStringFree(&valueString);
576 * For unset traces, let TclUnsetEnv do all the work.
579 if (flags & TCL_TRACE_UNSETS) {
586 *----------------------------------------------------------------------
590 * Replace one string with another in the environment variable
591 * cache. The cache keeps track of all of the environment
592 * variables that Tcl has modified so they can be freed later.
598 * May free the old string.
600 *----------------------------------------------------------------------
604 ReplaceString(oldStr, newStr)
605 CONST char *oldStr; /* Old environment string. */
606 char *newStr; /* New environment string. */
612 * Check to see if the old value was allocated by Tcl. If so,
613 * it needs to be deallocated to avoid memory leaks. Note that this
614 * algorithm is O(n), not O(1). This will result in n-squared behavior
615 * if lots of environment changes are being made.
618 for (i = 0; i < cacheSize; i++) {
619 if ((environCache[i] == oldStr) || (environCache[i] == NULL)) {
625 * Replace or delete the old value.
628 if (environCache[i]) {
629 ckfree(environCache[i]);
633 environCache[i] = newStr;
635 for (; i < cacheSize-1; i++) {
636 environCache[i] = environCache[i+1];
638 environCache[cacheSize-1] = NULL;
641 int allocatedSize = (cacheSize + 5) * sizeof(char *);
644 * We need to grow the cache in order to hold the new string.
647 newCache = (char **) ckalloc((unsigned) allocatedSize);
648 (VOID *) memset(newCache, (int) 0, (size_t) allocatedSize);
651 memcpy((VOID *) newCache, (VOID *) environCache,
652 (size_t) (cacheSize * sizeof(char*)));
653 ckfree((char *) environCache);
655 environCache = newCache;
656 environCache[cacheSize] = newStr;
657 environCache[cacheSize+1] = NULL;
663 *----------------------------------------------------------------------
665 * TclFinalizeEnvironment --
667 * This function releases any storage allocated by this module
668 * that isn't still in use by the global environment. Any
669 * strings that are still in the environment will be leaked.
675 * May deallocate storage.
677 *----------------------------------------------------------------------
681 TclFinalizeEnvironment()
684 * For now we just deallocate the cache array and none of the environment
685 * strings. This may leak more memory that strictly necessary, since some
686 * of the strings may no longer be in the environment. However,
687 * determining which ones are ok to delete is n-squared, and is pretty
688 * unlikely, so we don't bother.
692 ckfree((char *) environCache);
701 #if defined(__CYGWIN__) && defined(__WIN32__)
706 * When using cygwin, when an environment variable changes, we need to synch
707 * with both the cygwin environment (in case the application C code calls
708 * fork) and the Windows environment (in case the application TCL code calls
709 * exec, which calls the Windows CreateProcess function).
718 /* Get the name and value, so that we can change the environment
719 variable for Windows. */
720 name = (char *) alloca (strlen (str) + 1);
722 for (value = name; *value != '=' && *value != '\0'; ++value)
724 if (*value == '\0') {
730 if (*value == '\0') {
734 /* Set the cygwin environment variable. */
743 * Before changing the environment variable in Windows, if this is PATH,
744 * we need to convert the value back to a Windows style path.
746 * FIXME: The calling program may know it is running under windows, and
747 * may have set the path to a Windows path, or, worse, appended or
748 * prepended a Windows path to PATH.
750 if (strcmp (name, "PATH") != 0) {
751 /* If this is Path, eliminate any PATH variable, to prevent any
753 if (strcmp (name, "Path") == 0) {
754 SetEnvironmentVariable ("PATH", (char *) NULL);
758 SetEnvironmentVariable (name, value);
762 /* Eliminate any Path variable, to prevent any confusion. */
763 SetEnvironmentVariable ("Path", (char *) NULL);
771 size = cygwin_posix_to_win32_path_list_buf_size (value);
772 buf = (char *) alloca (size + 1);
773 cygwin_posix_to_win32_path_list (value, buf);
776 SetEnvironmentVariable (name, buf);
780 #endif /* __CYGWIN__ && __WIN32__ */