OSDN Git Service

294ec21a3e3a935c14d88a7ff3df2895543239f0
[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 /* CYGNUS LOCAL */
24 #if defined(__CYGWIN__) && defined(__WIN32__)
25
26 /* Under cygwin, the environment is imported from the cygwin DLL.  */
27
28 __declspec(dllimport) extern char **__cygwin_environ;
29
30 #define environ (__cygwin_environ)
31
32 /* We need to use a special putenv function to handle PATH.  */
33 #ifndef USE_PUTENV
34 #define USE_PUTENV
35 #endif
36 #define putenv TclCygwin32Putenv
37 #endif
38 /* END CYGNUS LOCAL */
39
40 #ifdef TCL_THREADS
41
42 static Tcl_Mutex envMutex;      /* To serialize access to environ */
43 #endif
44
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. */
49
50 #ifndef USE_PUTENV
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. */
56 #endif
57
58 /*
59  * For MacOS X
60  */
61 #if defined(__APPLE__) && defined(__DYNAMIC__)
62 #include <crt_externs.h>
63 char **environ = NULL;
64 #endif
65
66 /*
67  * Declarations for local procedures defined in this file:
68  */
69
70 static char *           EnvTraceProc _ANSI_ARGS_((ClientData clientData,
71                             Tcl_Interp *interp, char *name1, char *name2,
72                             int flags));
73 static void             ReplaceString _ANSI_ARGS_((CONST char *oldStr,
74                             char *newStr));
75 void                    TclSetEnv _ANSI_ARGS_((CONST char *name,
76                             CONST char *value));
77 void                    TclUnsetEnv _ANSI_ARGS_((CONST char *name));
78
79 /* CYGNUS LOCAL */
80 #if defined (__CYGWIN__) && defined(__WIN32__)
81 static void             TclCygwin32Putenv _ANSI_ARGS_((CONST char *string));
82 #endif
83 \f
84 /*
85  *----------------------------------------------------------------------
86  *
87  * TclSetupEnv --
88  *
89  *      This procedure is invoked for an interpreter to make environment
90  *      variables accessible from that interpreter via the "env"
91  *      associative array.
92  *
93  * Results:
94  *      None.
95  *
96  * Side effects:
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.
103  *
104  *----------------------------------------------------------------------
105  */
106
107 void
108 TclSetupEnv(interp)
109     Tcl_Interp *interp;         /* Interpreter whose "env" array is to be
110                                  * managed. */
111 {
112     Tcl_DString envString;
113     char *p1, *p2;
114     int i;
115
116     /*
117      * For MacOS X
118      */
119 #if defined(__APPLE__) && defined(__DYNAMIC__)
120     environ = *_NSGetEnviron();
121 #endif
122
123     /*
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.
131      */
132     
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,
136             (ClientData) NULL);
137     
138     Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY); 
139     
140     if (environ[0] == NULL) {
141         Tcl_Obj *varNamePtr;
142         
143         varNamePtr = Tcl_NewStringObj("env", -1);
144         Tcl_IncrRefCount(varNamePtr);
145         TclArraySet(interp, varNamePtr, NULL);  
146         Tcl_DecrRefCount(varNamePtr);
147     } else {
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, '=');
152             if (p2 == NULL) {
153                 /*
154                  * This condition seem to happen occasionally under some
155                  * versions of Solaris; ignore the entry.
156                  */
157                 
158                 continue;
159             }
160             p2++;
161             p2[-1] = '\0';
162             Tcl_SetVar2(interp, "env", p1, p2, TCL_GLOBAL_ONLY);        
163             Tcl_DStringFree(&envString);
164         }
165         Tcl_MutexUnlock(&envMutex);
166     }
167
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,
171             (ClientData) NULL);
172 }
173 \f
174 /*
175  *----------------------------------------------------------------------
176  *
177  * TclSetEnv --
178  *
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".
185  *
186  * Results:
187  *      None.
188  *
189  * Side effects:
190  *      The environ array gets updated.
191  *
192  *----------------------------------------------------------------------
193  */
194
195 void
196 TclSetEnv(name, value)
197     CONST char *name;           /* Name of variable whose value is to be
198                                  * set (UTF-8). */
199     CONST char *value;          /* New value for variable (UTF-8). */
200 {
201     Tcl_DString envString;
202     int index, length, nameLength;
203     char *p, *p2, *oldValue;
204
205     /*
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.
209      */
210
211     Tcl_MutexLock(&envMutex);
212     index = TclpFindVariable(name, &length);
213
214     if (index == -1) {
215 #ifndef USE_PUTENV
216         if ((length + 2) > environSize) {
217             char **newEnviron;
218
219             newEnviron = (char **) ckalloc((unsigned)
220                     ((length + 5) * sizeof(char *)));
221
222             /* CYGNUS LOCAL: Added to avoid an error from Purify,
223                although I don't personally see where the error would
224                occur--ian.  */
225             memset((VOID *) newEnviron, 0, (length+5) * sizeof(char *));
226
227             memcpy((VOID *) newEnviron, (VOID *) environ,
228                     length*sizeof(char *));
229             if (environSize != 0) {
230                 ckfree((char *) environ);
231             }
232             environ = newEnviron;
233             environSize = length + 5;
234         }
235         index = length;
236         environ[index + 1] = NULL;
237 #endif
238         oldValue = NULL;
239         nameLength = strlen(name);
240     } else {
241         char *env;
242
243         /*
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.
249          */
250
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);
255             return;
256         }
257         Tcl_DStringFree(&envString);
258
259         oldValue = environ[index];
260         nameLength = length;
261     }
262         
263
264     /*
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.
268      */
269
270     p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2));
271     strcpy(p, name);
272     p[nameLength] = '=';
273     strcpy(p+nameLength+1, value);
274     p2 = Tcl_UtfToExternalDString(NULL, p, -1, &envString);
275
276     /*
277      * Copy the native string to heap memory.
278      */
279     
280     p = (char *) ckrealloc(p, (unsigned) (strlen(p2) + 1));
281     strcpy(p, p2);
282     Tcl_DStringFree(&envString);
283
284 #ifdef USE_PUTENV
285     /*
286      * Update the system environment.
287      */
288
289     putenv(p);
290     index = TclpFindVariable(name, &length);
291 #else
292     environ[index] = p;
293 #endif
294
295     /*
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.
299      */
300
301     if ((index != -1) && (environ[index] == p)) {
302         ReplaceString(oldValue, p);
303     }
304
305     Tcl_MutexUnlock(&envMutex);
306 }
307 \f
308 /*
309  *----------------------------------------------------------------------
310  *
311  * Tcl_PutEnv --
312  *
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".
320  *
321  * Results:
322  *      None.
323  *
324  * Side effects:
325  *      The environ array gets updated, as do all of the interpreters
326  *      that we manage.
327  *
328  *----------------------------------------------------------------------
329  */
330
331 int
332 Tcl_PutEnv(string)
333     CONST char *string;         /* Info about environment variable in the
334                                  * form NAME=value. (native) */
335 {
336     Tcl_DString nameString;   
337     int nameLength;
338     char *name, *value;
339
340     if (string == NULL) {
341         return 0;
342     }
343
344     /*
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.
348      */
349
350     name = Tcl_ExternalToUtfDString(NULL, string, -1, &nameString);
351     value = strchr(name, '=');
352     if (value == NULL) {
353         return 0;
354     }
355     nameLength = value - name;
356     if (nameLength == 0) {
357         return 0;
358     }
359
360     value[0] = '\0';
361     TclSetEnv(name, value+1);
362     Tcl_DStringFree(&nameString);
363     return 0;
364 }
365 \f
366 /*
367  *----------------------------------------------------------------------
368  *
369  * TclUnsetEnv --
370  *
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
375  *      "unsetenv".
376  *
377  * Results:
378  *      None.
379  *
380  * Side effects:
381  *      Interpreters are updated, as is environ.
382  *
383  *----------------------------------------------------------------------
384  */
385
386 void
387 TclUnsetEnv(name)
388     CONST char *name;           /* Name of variable to remove (UTF-8). */
389 {
390     char *oldValue;
391     unsigned int length;
392     int index;
393 #ifdef USE_PUTENV
394     Tcl_DString envString;
395     char *string;
396 #else
397     char **envPtr;
398 #endif
399
400     Tcl_MutexLock(&envMutex);
401     index = TclpFindVariable(name, &length);
402
403     /*
404      * First make sure that the environment variable exists to avoid
405      * doing needless work and to avoid recursion on the unset.
406      */
407     
408     if (index == -1) {
409         Tcl_MutexUnlock(&envMutex);
410         return;
411     }
412     /*
413      * Remember the old value so we can free it if Tcl created the string.
414      */
415
416     oldValue = environ[index];
417
418     /*
419      * Update the system environment.  This must be done before we 
420      * update the interpreters or we will recurse.
421      */
422
423 #ifdef USE_PUTENV
424     string = ckalloc(length+2);
425     memcpy((VOID *) string, (VOID *) name, (size_t) length);
426     string[length] = '=';
427     string[length+1] = '\0';
428     
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);
433
434     putenv(string);
435
436     /*
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.
440      */
441
442     if (environ[index] == string) {
443         ReplaceString(oldValue, string);
444     }
445 #else
446     for (envPtr = environ+index+1; ; envPtr++) {
447         envPtr[-1] = *envPtr;
448         if (*envPtr == NULL) {
449             break;
450         }
451     }
452     ReplaceString(oldValue, NULL);
453 #endif
454
455     Tcl_MutexUnlock(&envMutex);
456 }
457 \f
458 /*
459  *---------------------------------------------------------------------------
460  *
461  * TclGetEnv --
462  *
463  *      Retrieve the value of an environment variable.
464  *
465  * Results:
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
470  *      longer needed.
471  *
472  * Side effects:
473  *      None.
474  *
475  *----------------------------------------------------------------------
476  */
477
478 char *
479 TclGetEnv(name, valuePtr)
480     CONST char *name;           /* Name of environment variable to find
481                                  * (UTF-8). */
482     Tcl_DString *valuePtr;      /* Uninitialized or free DString in which
483                                  * the value of the environment variable is
484                                  * stored. */
485 {
486     int length, index;
487     char *result;
488
489     Tcl_MutexLock(&envMutex);
490     index = TclpFindVariable(name, &length);
491     result = NULL;
492     if (index != -1) {
493         Tcl_DString envStr;
494         
495         result = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envStr);
496         result += length;
497         if (*result == '=') {
498             result++;
499             Tcl_DStringInit(valuePtr);
500             Tcl_DStringAppend(valuePtr, result, -1);
501             result = Tcl_DStringValue(valuePtr);
502         } else {
503             result = NULL;
504         }
505         Tcl_DStringFree(&envStr);
506     }
507     Tcl_MutexUnlock(&envMutex);
508     return result;
509 }
510 \f
511 /*
512  *----------------------------------------------------------------------
513  *
514  * EnvTraceProc --
515  *
516  *      This procedure is invoked whenever an environment variable
517  *      is read, modified or deleted.  It propagates the change to the global
518  *      "environ" array.
519  *
520  * Results:
521  *      Always returns NULL to indicate success.
522  *
523  * Side effects:
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).
528  *
529  *----------------------------------------------------------------------
530  */
531
532         /* ARGSUSED */
533 static char *
534 EnvTraceProc(clientData, interp, name1, name2, flags)
535     ClientData clientData;      /* Not used. */
536     Tcl_Interp *interp;         /* Interpreter whose "env" variable is
537                                  * being modified. */
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. */
542 {
543     /*
544      * For array traces, let TclSetupEnv do all the work.
545      */
546
547     if (flags & TCL_TRACE_ARRAY) {
548         TclSetupEnv(interp);
549         return NULL;
550     }
551
552     /*
553      * If name2 is NULL, then return and do nothing.
554      */
555      
556     if (name2 == NULL) {
557         return NULL;
558     }
559
560     /*
561      * If a value is being set, call TclSetEnv to do all of the work.
562      */
563
564     if (flags & TCL_TRACE_WRITES) {
565         char *value;
566         
567         value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY);
568         TclSetEnv(name2, value);
569     }
570
571     /*
572      * If a value is being read, call TclGetEnv to do all of the work.
573      */
574
575     if (flags & TCL_TRACE_READS) {
576         Tcl_DString valueString;
577         char *value;
578
579         value = TclGetEnv(name2, &valueString);
580         if (value == NULL) {
581             return "no such variable";
582         }
583         Tcl_SetVar2(interp, name1, name2, value, 0);
584         Tcl_DStringFree(&valueString);
585     }
586
587     /*
588      * For unset traces, let TclUnsetEnv do all the work.
589      */
590
591     if (flags & TCL_TRACE_UNSETS) {
592         TclUnsetEnv(name2);
593     }
594     return NULL;
595 }
596 \f
597 /*
598  *----------------------------------------------------------------------
599  *
600  * ReplaceString --
601  *
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.
605  *
606  * Results:
607  *      None.
608  *
609  * Side effects:
610  *      May free the old string.
611  *
612  *----------------------------------------------------------------------
613  */
614
615 static void
616 ReplaceString(oldStr, newStr)
617     CONST char *oldStr;         /* Old environment string. */
618     char *newStr;               /* New environment string. */
619 {
620     int i;
621     char **newCache;
622
623     /*
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.
628      */
629
630     for (i = 0; i < cacheSize; i++) {
631         if ((environCache[i] == oldStr) || (environCache[i] == NULL)) {
632             break;
633         }
634     }
635     if (i < cacheSize) {
636         /*
637          * Replace or delete the old value.
638          */
639
640         if (environCache[i]) {
641             ckfree(environCache[i]);
642         }
643             
644         if (newStr) {
645             environCache[i] = newStr;
646         } else {
647             for (; i < cacheSize-1; i++) {
648                 environCache[i] = environCache[i+1];
649             }
650             environCache[cacheSize-1] = NULL;
651         }
652     } else {    
653         int allocatedSize = (cacheSize + 5) * sizeof(char *);
654
655         /*
656          * We need to grow the cache in order to hold the new string.
657          */
658
659         newCache = (char **) ckalloc((unsigned) allocatedSize);
660         (VOID *) memset(newCache, (int) 0, (size_t) allocatedSize);
661         
662         if (environCache) {
663             memcpy((VOID *) newCache, (VOID *) environCache,
664                     (size_t) (cacheSize * sizeof(char*)));
665             ckfree((char *) environCache);
666         }
667         environCache = newCache;
668         environCache[cacheSize] = (char *) newStr;
669         environCache[cacheSize+1] = NULL;
670         cacheSize += 5;
671     }
672 }
673 \f
674 /*
675  *----------------------------------------------------------------------
676  *
677  * TclFinalizeEnvironment --
678  *
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.
682  *
683  * Results:
684  *      None.
685  *
686  * Side effects:
687  *      May deallocate storage.
688  *
689  *----------------------------------------------------------------------
690  */
691
692 void
693 TclFinalizeEnvironment()
694 {
695     /*
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.
701      */
702
703     if (environCache) {
704         ckfree((char *) environCache);
705         environCache = NULL;
706         cacheSize    = 0;
707 #ifndef USE_PUTENV
708         environSize  = 0;
709 #endif
710     }
711 }
712 \f
713 /* CYGNUS LOCAL */
714 #if defined(__CYGWIN__) && defined(__WIN32__)
715
716 #include "windows.h"
717
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).  */
723
724 static void
725 TclCygwin32Putenv(str)
726      const char *str;
727 {
728   char *name, *value;
729
730   /* Get the name and value, so that we can change the environment
731      variable for Windows.  */
732   name = (char *) alloca (strlen (str) + 1);
733   strcpy (name, str);
734   for (value = name; *value != '=' && *value != '\0'; ++value)
735     ;
736   if (*value == '\0')
737     {
738       /* Can't happen.  */
739       return;
740     }
741   *value = '\0';
742   ++value;
743   if (*value == '\0')
744     value = NULL;
745
746   /* Set the cygwin environment variable.  */
747 #undef putenv
748   if (value == NULL)
749     unsetenv (name);
750   else
751     putenv(str);
752
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.
755
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)
760     {
761       /* If this is Path, eliminate any PATH variable, to prevent any
762          confusion.  */
763       if (strcmp (name, "Path") == 0)
764         {
765           SetEnvironmentVariable ("PATH", (char *) NULL);
766           unsetenv ("PATH");
767         }
768
769       SetEnvironmentVariable (name, value);
770     }
771   else
772     {
773       char *buf;
774
775       /* Eliminate any Path variable, to prevent any confusion.  */
776       SetEnvironmentVariable ("Path", (char *) NULL);
777       unsetenv ("Path");
778
779       if (value == NULL)
780         buf = NULL;
781       else
782         {
783           int size;
784
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);
788         }
789
790       SetEnvironmentVariable (name, buf);
791     }
792 }
793
794 #endif /* __CYGWIN__ */
795 /* END CYGNUS LOCAL */
796