OSDN Git Service

Updated to tcl 8.4.1
[pf3gnuchains/sourceware.git] / tcl / generic / tclEnv.c
1 /* 
2  * tclEnv.c --
3  *
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.
8  *
9  * Copyright (c) 1991-1994 The Regents of the University of California.
10  * Copyright (c) 1994-1998 Sun Microsystems, Inc.
11  *
12  * See the file "license.terms" for information on usage and redistribution
13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14  *
15  * RCS: @(#) $Id$
16  */
17
18 #include "tclInt.h"
19 #include "tclPort.h"
20
21 TCL_DECLARE_MUTEX(envMutex)     /* To serialize access to environ */
22
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. */
27
28 #ifndef USE_PUTENV
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. */
34 #endif
35
36 /*
37  * For MacOS X
38  */
39 #if defined(__APPLE__) && defined(__DYNAMIC__)
40 #include <crt_externs.h>
41 char **environ = NULL;
42 #endif
43
44 /*
45  * Declarations for local procedures defined in this file:
46  */
47
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,
52                             char *newStr));
53 void                    TclSetEnv _ANSI_ARGS_((CONST char *name,
54                             CONST char *value));
55 void                    TclUnsetEnv _ANSI_ARGS_((CONST char *name));
56
57 #if defined (__CYGWIN__) && defined(__WIN32__)
58 static void             TclCygwinPutenv _ANSI_ARGS_((CONST char *string));
59 #endif
60 \f
61 /*
62  *----------------------------------------------------------------------
63  *
64  * TclSetupEnv --
65  *
66  *      This procedure is invoked for an interpreter to make environment
67  *      variables accessible from that interpreter via the "env"
68  *      associative array.
69  *
70  * Results:
71  *      None.
72  *
73  * Side effects:
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.
80  *
81  *----------------------------------------------------------------------
82  */
83
84 void
85 TclSetupEnv(interp)
86     Tcl_Interp *interp;         /* Interpreter whose "env" array is to be
87                                  * managed. */
88 {
89     Tcl_DString envString;
90     char *p1, *p2;
91     int i;
92
93     /*
94      * For MacOS X
95      */
96 #if defined(__APPLE__) && defined(__DYNAMIC__)
97     environ = *_NSGetEnviron();
98 #endif
99
100     /*
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.
108      */
109     
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,
113             (ClientData) NULL);
114     
115     Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY); 
116     
117     if (environ[0] == NULL) {
118         Tcl_Obj *varNamePtr;
119         
120         varNamePtr = Tcl_NewStringObj("env", -1);
121         Tcl_IncrRefCount(varNamePtr);
122         TclArraySet(interp, varNamePtr, NULL);  
123         Tcl_DecrRefCount(varNamePtr);
124     } else {
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, '=');
129             if (p2 == NULL) {
130                 /*
131                  * This condition seem to happen occasionally under some
132                  * versions of Solaris; ignore the entry.
133                  */
134                 
135                 continue;
136             }
137             p2++;
138             p2[-1] = '\0';
139             Tcl_SetVar2(interp, "env", p1, p2, TCL_GLOBAL_ONLY);        
140             Tcl_DStringFree(&envString);
141         }
142         Tcl_MutexUnlock(&envMutex);
143     }
144
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,
148             (ClientData) NULL);
149 }
150 \f
151 /*
152  *----------------------------------------------------------------------
153  *
154  * TclSetEnv --
155  *
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".
162  *
163  * Results:
164  *      None.
165  *
166  * Side effects:
167  *      The environ array gets updated.
168  *
169  *----------------------------------------------------------------------
170  */
171
172 void
173 TclSetEnv(name, value)
174     CONST char *name;           /* Name of variable whose value is to be
175                                  * set (UTF-8). */
176     CONST char *value;          /* New value for variable (UTF-8). */
177 {
178     Tcl_DString envString;
179     int index, length, nameLength;
180     char *p, *oldValue;
181     CONST char *p2;
182
183     /*
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.
187      */
188
189     Tcl_MutexLock(&envMutex);
190     index = TclpFindVariable(name, &length);
191
192     if (index == -1) {
193 #ifndef USE_PUTENV
194         if ((length + 2) > environSize) {
195             char **newEnviron;
196
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);
203             }
204             environ = newEnviron;
205             environSize = length + 5;
206 #if defined(__APPLE__) && defined(__DYNAMIC__)
207             {
208             char ***e = _NSGetEnviron();
209             *e = environ;
210             }
211 #endif
212         }
213         index = length;
214         environ[index + 1] = NULL;
215 #endif
216         oldValue = NULL;
217         nameLength = strlen(name);
218     } else {
219         CONST char *env;
220
221         /*
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.
227          */
228
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);
233             return;
234         }
235         Tcl_DStringFree(&envString);
236
237         oldValue = environ[index];
238         nameLength = length;
239     }
240         
241
242     /*
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.
246      */
247
248     p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2));
249     strcpy(p, name);
250     p[nameLength] = '=';
251     strcpy(p+nameLength+1, value);
252     p2 = Tcl_UtfToExternalDString(NULL, p, -1, &envString);
253
254     /*
255      * Copy the native string to heap memory.
256      */
257     
258     p = (char *) ckrealloc(p, (unsigned) (strlen(p2) + 1));
259     strcpy(p, p2);
260     Tcl_DStringFree(&envString);
261
262 #ifdef USE_PUTENV
263     /*
264      * Update the system environment.
265      */
266
267     putenv(p);
268     index = TclpFindVariable(name, &length);
269 #else
270     environ[index] = p;
271 #endif
272
273     /*
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.
277      */
278
279     if ((index != -1) && (environ[index] == p)) {
280         ReplaceString(oldValue, p);
281 #ifdef HAVE_PUTENV_THAT_COPIES
282     } else {
283         /* This putenv() copies instead of taking ownership */
284         ckfree(p);
285 #endif
286     }
287
288     Tcl_MutexUnlock(&envMutex);
289     
290     if (!strcmp(name, "HOME")) {
291         /* 
292          * If the user's home directory has changed, we must invalidate
293          * the filesystem cache, because '~' expansions will now be
294          * incorrect.
295          */
296         Tcl_FSMountsChanged(NULL);
297     }
298 }
299 \f
300 /*
301  *----------------------------------------------------------------------
302  *
303  * Tcl_PutEnv --
304  *
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".
312  *
313  * Results:
314  *      None.
315  *
316  * Side effects:
317  *      The environ array gets updated, as do all of the interpreters
318  *      that we manage.
319  *
320  *----------------------------------------------------------------------
321  */
322
323 int
324 Tcl_PutEnv(string)
325     CONST char *string;         /* Info about environment variable in the
326                                  * form NAME=value. (native) */
327 {
328     Tcl_DString nameString;   
329     CONST char *name;
330     char *value;
331
332     if (string == NULL) {
333         return 0;
334     }
335
336     /*
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.
340      */
341
342     name = Tcl_ExternalToUtfDString(NULL, string, -1, &nameString);
343     value = strchr(name, '=');
344
345     if ((value != NULL) && (value != name)) {
346         value[0] = '\0';
347         TclSetEnv(name, value+1);
348     }
349
350     Tcl_DStringFree(&nameString);
351     return 0;
352 }
353 \f
354 /*
355  *----------------------------------------------------------------------
356  *
357  * TclUnsetEnv --
358  *
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
363  *      "unsetenv".
364  *
365  * Results:
366  *      None.
367  *
368  * Side effects:
369  *      Interpreters are updated, as is environ.
370  *
371  *----------------------------------------------------------------------
372  */
373
374 void
375 TclUnsetEnv(name)
376     CONST char *name;           /* Name of variable to remove (UTF-8). */
377 {
378     char *oldValue;
379     int length;
380     int index;
381 #ifdef USE_PUTENV
382     Tcl_DString envString;
383     char *string;
384 #else
385     char **envPtr;
386 #endif
387
388     Tcl_MutexLock(&envMutex);
389     index = TclpFindVariable(name, &length);
390
391     /*
392      * First make sure that the environment variable exists to avoid
393      * doing needless work and to avoid recursion on the unset.
394      */
395     
396     if (index == -1) {
397         Tcl_MutexUnlock(&envMutex);
398         return;
399     }
400     /*
401      * Remember the old value so we can free it if Tcl created the string.
402      */
403
404     oldValue = environ[index];
405
406     /*
407      * Update the system environment.  This must be done before we 
408      * update the interpreters or we will recurse.
409      */
410
411 #ifdef USE_PUTENV
412     string = ckalloc(length+2);
413     memcpy((VOID *) string, (VOID *) name, (size_t) length);
414     string[length] = '=';
415     string[length+1] = '\0';
416     
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);
421
422     putenv(string);
423
424     /*
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.
428      */
429
430     if (environ[index] == string) {
431         ReplaceString(oldValue, string);
432     }
433 #else
434     for (envPtr = environ+index+1; ; envPtr++) {
435         envPtr[-1] = *envPtr;
436         if (*envPtr == NULL) {
437             break;
438         }
439     }
440     ReplaceString(oldValue, NULL);
441 #endif
442
443     Tcl_MutexUnlock(&envMutex);
444 }
445 \f
446 /*
447  *---------------------------------------------------------------------------
448  *
449  * TclGetEnv --
450  *
451  *      Retrieve the value of an environment variable.
452  *
453  * Results:
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
458  *      longer needed.
459  *
460  * Side effects:
461  *      None.
462  *
463  *----------------------------------------------------------------------
464  */
465
466 CONST char *
467 TclGetEnv(name, valuePtr)
468     CONST char *name;           /* Name of environment variable to find
469                                  * (UTF-8). */
470     Tcl_DString *valuePtr;      /* Uninitialized or free DString in which
471                                  * the value of the environment variable is
472                                  * stored. */
473 {
474     int length, index;
475     CONST char *result;
476
477     Tcl_MutexLock(&envMutex);
478     index = TclpFindVariable(name, &length);
479     result = NULL;
480     if (index != -1) {
481         Tcl_DString envStr;
482         
483         result = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envStr);
484         result += length;
485         if (*result == '=') {
486             result++;
487             Tcl_DStringInit(valuePtr);
488             Tcl_DStringAppend(valuePtr, result, -1);
489             result = Tcl_DStringValue(valuePtr);
490         } else {
491             result = NULL;
492         }
493         Tcl_DStringFree(&envStr);
494     }
495     Tcl_MutexUnlock(&envMutex);
496     return result;
497 }
498 \f
499 /*
500  *----------------------------------------------------------------------
501  *
502  * EnvTraceProc --
503  *
504  *      This procedure is invoked whenever an environment variable
505  *      is read, modified or deleted.  It propagates the change to the global
506  *      "environ" array.
507  *
508  * Results:
509  *      Always returns NULL to indicate success.
510  *
511  * Side effects:
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).
516  *
517  *----------------------------------------------------------------------
518  */
519
520         /* ARGSUSED */
521 static char *
522 EnvTraceProc(clientData, interp, name1, name2, flags)
523     ClientData clientData;      /* Not used. */
524     Tcl_Interp *interp;         /* Interpreter whose "env" variable is
525                                  * being modified. */
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. */
530 {
531     /*
532      * For array traces, let TclSetupEnv do all the work.
533      */
534
535     if (flags & TCL_TRACE_ARRAY) {
536         TclSetupEnv(interp);
537         return NULL;
538     }
539
540     /*
541      * If name2 is NULL, then return and do nothing.
542      */
543      
544     if (name2 == NULL) {
545         return NULL;
546     }
547
548     /*
549      * If a value is being set, call TclSetEnv to do all of the work.
550      */
551
552     if (flags & TCL_TRACE_WRITES) {
553         CONST char *value;
554         
555         value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY);
556         TclSetEnv(name2, value);
557     }
558
559     /*
560      * If a value is being read, call TclGetEnv to do all of the work.
561      */
562
563     if (flags & TCL_TRACE_READS) {
564         Tcl_DString valueString;
565         CONST char *value;
566
567         value = TclGetEnv(name2, &valueString);
568         if (value == NULL) {
569             return "no such variable";
570         }
571         Tcl_SetVar2(interp, name1, name2, value, 0);
572         Tcl_DStringFree(&valueString);
573     }
574
575     /*
576      * For unset traces, let TclUnsetEnv do all the work.
577      */
578
579     if (flags & TCL_TRACE_UNSETS) {
580         TclUnsetEnv(name2);
581     }
582     return NULL;
583 }
584 \f
585 /*
586  *----------------------------------------------------------------------
587  *
588  * ReplaceString --
589  *
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.
593  *
594  * Results:
595  *      None.
596  *
597  * Side effects:
598  *      May free the old string.
599  *
600  *----------------------------------------------------------------------
601  */
602
603 static void
604 ReplaceString(oldStr, newStr)
605     CONST char *oldStr;         /* Old environment string. */
606     char *newStr;               /* New environment string. */
607 {
608     int i;
609     char **newCache;
610
611     /*
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.
616      */
617
618     for (i = 0; i < cacheSize; i++) {
619         if ((environCache[i] == oldStr) || (environCache[i] == NULL)) {
620             break;
621         }
622     }
623     if (i < cacheSize) {
624         /*
625          * Replace or delete the old value.
626          */
627
628         if (environCache[i]) {
629             ckfree(environCache[i]);
630         }
631             
632         if (newStr) {
633             environCache[i] = newStr;
634         } else {
635             for (; i < cacheSize-1; i++) {
636                 environCache[i] = environCache[i+1];
637             }
638             environCache[cacheSize-1] = NULL;
639         }
640     } else {    
641         int allocatedSize = (cacheSize + 5) * sizeof(char *);
642
643         /*
644          * We need to grow the cache in order to hold the new string.
645          */
646
647         newCache = (char **) ckalloc((unsigned) allocatedSize);
648         (VOID *) memset(newCache, (int) 0, (size_t) allocatedSize);
649         
650         if (environCache) {
651             memcpy((VOID *) newCache, (VOID *) environCache,
652                     (size_t) (cacheSize * sizeof(char*)));
653             ckfree((char *) environCache);
654         }
655         environCache = newCache;
656         environCache[cacheSize] = newStr;
657         environCache[cacheSize+1] = NULL;
658         cacheSize += 5;
659     }
660 }
661 \f
662 /*
663  *----------------------------------------------------------------------
664  *
665  * TclFinalizeEnvironment --
666  *
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.
670  *
671  * Results:
672  *      None.
673  *
674  * Side effects:
675  *      May deallocate storage.
676  *
677  *----------------------------------------------------------------------
678  */
679
680 void
681 TclFinalizeEnvironment()
682 {
683     /*
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.
689      */
690
691     if (environCache) {
692         ckfree((char *) environCache);
693         environCache = NULL;
694         cacheSize    = 0;
695 #ifndef USE_PUTENV
696         environSize  = 0;
697 #endif
698     }
699 }
700 \f
701 #if defined(__CYGWIN__) && defined(__WIN32__)
702
703 #include <windows.h>
704
705 /*
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).
710  */
711
712 static void
713 TclCygwinPutenv(str)
714     const char *str;
715 {
716     char *name, *value;
717
718     /* Get the name and value, so that we can change the environment
719        variable for Windows.  */
720     name = (char *) alloca (strlen (str) + 1);
721     strcpy (name, str);
722     for (value = name; *value != '=' && *value != '\0'; ++value)
723         ;
724     if (*value == '\0') {
725             /* Can't happen.  */
726             return;
727         }
728     *value = '\0';
729     ++value;
730     if (*value == '\0') {
731         value = NULL;
732     }
733
734     /* Set the cygwin environment variable.  */
735 #undef putenv
736     if (value == NULL) {
737         unsetenv (name);
738     } else {
739         putenv(str);
740     }
741
742     /*
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.
745      *
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.
749      */
750     if (strcmp (name, "PATH") != 0) {
751         /* If this is Path, eliminate any PATH variable, to prevent any
752            confusion.  */
753         if (strcmp (name, "Path") == 0) {
754             SetEnvironmentVariable ("PATH", (char *) NULL);
755             unsetenv ("PATH");
756         }
757
758         SetEnvironmentVariable (name, value);
759     } else {
760         char *buf;
761
762             /* Eliminate any Path variable, to prevent any confusion.  */
763         SetEnvironmentVariable ("Path", (char *) NULL);
764         unsetenv ("Path");
765
766         if (value == NULL) {
767             buf = NULL;
768         } else {
769             int size;
770
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);
774         }
775
776         SetEnvironmentVariable (name, buf);
777     }
778 }
779
780 #endif /* __CYGWIN__ && __WIN32__ */