/[dtapublic]/projs/dtats/trunk/shared_source/c_tcl_base_7_5_w_mods/tclenv.c
ViewVC logotype

Diff of /projs/dtats/trunk/shared_source/c_tcl_base_7_5_w_mods/tclenv.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 64 by dashley, Sun Oct 30 04:21:11 2016 UTC revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC
# Line 1  Line 1 
1  /* $Header$ */  /* $Header$ */
2  /*  /*
3   * tclEnv.c --   * tclEnv.c --
4   *   *
5   *      Tcl support for environment variables, including a setenv   *      Tcl support for environment variables, including a setenv
6   *      procedure.  This file contains the generic portion of the   *      procedure.  This file contains the generic portion of the
7   *      environment module.  It is primarily responsible for keeping   *      environment module.  It is primarily responsible for keeping
8   *      the "env" arrays in sync with the system environment variables.   *      the "env" arrays in sync with the system environment variables.
9   *   *
10   * Copyright (c) 1991-1994 The Regents of the University of California.   * Copyright (c) 1991-1994 The Regents of the University of California.
11   * Copyright (c) 1994-1998 Sun Microsystems, Inc.   * Copyright (c) 1994-1998 Sun Microsystems, Inc.
12   *   *
13   * See the file "license.terms" for information on usage and redistribution   * See the file "license.terms" for information on usage and redistribution
14   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15   *   *
16   * RCS: @(#) $Id: tclenv.c,v 1.1.1.1 2001/06/13 04:37:55 dtashley Exp $   * RCS: @(#) $Id: tclenv.c,v 1.1.1.1 2001/06/13 04:37:55 dtashley Exp $
17   */   */
18    
19  #include "tclInt.h"  #include "tclInt.h"
20  #include "tclPort.h"  #include "tclPort.h"
21    
22  TCL_DECLARE_MUTEX(envMutex)     /* To serialize access to environ */  TCL_DECLARE_MUTEX(envMutex)     /* To serialize access to environ */
23    
24  static int cacheSize = 0;       /* Number of env strings in environCache. */  static int cacheSize = 0;       /* Number of env strings in environCache. */
25  static char **environCache = NULL;  static char **environCache = NULL;
26                                  /* Array containing all of the environment                                  /* Array containing all of the environment
27                                   * strings that Tcl has allocated. */                                   * strings that Tcl has allocated. */
28    
29  #ifndef USE_PUTENV  #ifndef USE_PUTENV
30  static int environSize = 0;     /* Non-zero means that the environ array was  static int environSize = 0;     /* Non-zero means that the environ array was
31                                   * malloced and has this many total entries                                   * malloced and has this many total entries
32                                   * allocated to it (not all may be in use at                                   * allocated to it (not all may be in use at
33                                   * once).  Zero means that the environment                                   * once).  Zero means that the environment
34                                   * array is in its original static state. */                                   * array is in its original static state. */
35  #endif  #endif
36    
37  /*  /*
38   * For MacOS X   * For MacOS X
39   */   */
40  #if defined(__APPLE__) && defined(__DYNAMIC__)  #if defined(__APPLE__) && defined(__DYNAMIC__)
41  #include <crt_externs.h>  #include <crt_externs.h>
42  char **environ = NULL;  char **environ = NULL;
43  #endif  #endif
44    
45  /*  /*
46   * Declarations for local procedures defined in this file:   * Declarations for local procedures defined in this file:
47   */   */
48    
49  static char *           EnvTraceProc _ANSI_ARGS_((ClientData clientData,  static char *           EnvTraceProc _ANSI_ARGS_((ClientData clientData,
50                              Tcl_Interp *interp, char *name1, char *name2,                              Tcl_Interp *interp, char *name1, char *name2,
51                              int flags));                              int flags));
52  static void             ReplaceString _ANSI_ARGS_((CONST char *oldStr,  static void             ReplaceString _ANSI_ARGS_((CONST char *oldStr,
53                              char *newStr));                              char *newStr));
54  void                    TclSetEnv _ANSI_ARGS_((CONST char *name,  void                    TclSetEnv _ANSI_ARGS_((CONST char *name,
55                              CONST char *value));                              CONST char *value));
56  void                    TclUnsetEnv _ANSI_ARGS_((CONST char *name));  void                    TclUnsetEnv _ANSI_ARGS_((CONST char *name));
57    
58    
59  /*  /*
60   *----------------------------------------------------------------------   *----------------------------------------------------------------------
61   *   *
62   * TclSetupEnv --   * TclSetupEnv --
63   *   *
64   *      This procedure is invoked for an interpreter to make environment   *      This procedure is invoked for an interpreter to make environment
65   *      variables accessible from that interpreter via the "env"   *      variables accessible from that interpreter via the "env"
66   *      associative array.   *      associative array.
67   *   *
68   * Results:   * Results:
69   *      None.   *      None.
70   *   *
71   * Side effects:   * Side effects:
72   *      The interpreter is added to a list of interpreters managed   *      The interpreter is added to a list of interpreters managed
73   *      by us, so that its view of envariables can be kept consistent   *      by us, so that its view of envariables can be kept consistent
74   *      with the view in other interpreters.  If this is the first   *      with the view in other interpreters.  If this is the first
75   *      call to TclSetupEnv, then additional initialization happens,   *      call to TclSetupEnv, then additional initialization happens,
76   *      such as copying the environment to dynamically-allocated space   *      such as copying the environment to dynamically-allocated space
77   *      for ease of management.   *      for ease of management.
78   *   *
79   *----------------------------------------------------------------------   *----------------------------------------------------------------------
80   */   */
81    
82  void  void
83  TclSetupEnv(interp)  TclSetupEnv(interp)
84      Tcl_Interp *interp;         /* Interpreter whose "env" array is to be      Tcl_Interp *interp;         /* Interpreter whose "env" array is to be
85                                   * managed. */                                   * managed. */
86  {  {
87      Tcl_DString envString;      Tcl_DString envString;
88      char *p1, *p2;      char *p1, *p2;
89      int i;      int i;
90    
91      /*      /*
92       * For MacOS X       * For MacOS X
93       */       */
94  #if defined(__APPLE__) && defined(__DYNAMIC__)  #if defined(__APPLE__) && defined(__DYNAMIC__)
95      environ = *_NSGetEnviron();      environ = *_NSGetEnviron();
96  #endif  #endif
97    
98      /*      /*
99       * Synchronize the values in the environ array with the contents       * Synchronize the values in the environ array with the contents
100       * of the Tcl "env" variable.  To do this:       * of the Tcl "env" variable.  To do this:
101       *    1) Remove the trace that fires when the "env" var is unset.       *    1) Remove the trace that fires when the "env" var is unset.
102       *    2) Unset the "env" variable.       *    2) Unset the "env" variable.
103       *    3) If there are no environ variables, create an empty "env"       *    3) If there are no environ variables, create an empty "env"
104       *       array.  Otherwise populate the array with current values.       *       array.  Otherwise populate the array with current values.
105       *    4) Add a trace that synchronizes the "env" array.       *    4) Add a trace that synchronizes the "env" array.
106       */       */
107            
108      Tcl_UntraceVar2(interp, "env", (char *) NULL,      Tcl_UntraceVar2(interp, "env", (char *) NULL,
109              TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |              TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
110              TCL_TRACE_READS | TCL_TRACE_ARRAY,  EnvTraceProc,              TCL_TRACE_READS | TCL_TRACE_ARRAY,  EnvTraceProc,
111              (ClientData) NULL);              (ClientData) NULL);
112            
113      Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY);      Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY);
114            
115      if (environ[0] == NULL) {      if (environ[0] == NULL) {
116          Tcl_Obj *varNamePtr;          Tcl_Obj *varNamePtr;
117                    
118          varNamePtr = Tcl_NewStringObj("env", -1);          varNamePtr = Tcl_NewStringObj("env", -1);
119          Tcl_IncrRefCount(varNamePtr);          Tcl_IncrRefCount(varNamePtr);
120          TclArraySet(interp, varNamePtr, NULL);            TclArraySet(interp, varNamePtr, NULL);  
121          Tcl_DecrRefCount(varNamePtr);          Tcl_DecrRefCount(varNamePtr);
122      } else {      } else {
123          Tcl_MutexLock(&envMutex);          Tcl_MutexLock(&envMutex);
124          for (i = 0; environ[i] != NULL; i++) {          for (i = 0; environ[i] != NULL; i++) {
125              p1 = Tcl_ExternalToUtfDString(NULL, environ[i], -1, &envString);              p1 = Tcl_ExternalToUtfDString(NULL, environ[i], -1, &envString);
126              p2 = strchr(p1, '=');              p2 = strchr(p1, '=');
127              if (p2 == NULL) {              if (p2 == NULL) {
128                  /*                  /*
129                   * This condition seem to happen occasionally under some                   * This condition seem to happen occasionally under some
130                   * versions of Solaris; ignore the entry.                   * versions of Solaris; ignore the entry.
131                   */                   */
132                                    
133                  continue;                  continue;
134              }              }
135              p2++;              p2++;
136              p2[-1] = '\0';              p2[-1] = '\0';
137              Tcl_SetVar2(interp, "env", p1, p2, TCL_GLOBAL_ONLY);                      Tcl_SetVar2(interp, "env", p1, p2, TCL_GLOBAL_ONLY);        
138              Tcl_DStringFree(&envString);              Tcl_DStringFree(&envString);
139          }          }
140          Tcl_MutexUnlock(&envMutex);          Tcl_MutexUnlock(&envMutex);
141      }      }
142    
143      Tcl_TraceVar2(interp, "env", (char *) NULL,      Tcl_TraceVar2(interp, "env", (char *) NULL,
144              TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |              TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
145              TCL_TRACE_READS | TCL_TRACE_ARRAY,  EnvTraceProc,              TCL_TRACE_READS | TCL_TRACE_ARRAY,  EnvTraceProc,
146              (ClientData) NULL);              (ClientData) NULL);
147  }  }
148    
149  /*  /*
150   *----------------------------------------------------------------------   *----------------------------------------------------------------------
151   *   *
152   * TclSetEnv --   * TclSetEnv --
153   *   *
154   *      Set an environment variable, replacing an existing value   *      Set an environment variable, replacing an existing value
155   *      or creating a new variable if there doesn't exist a variable   *      or creating a new variable if there doesn't exist a variable
156   *      by the given name.  This procedure is intended to be a   *      by the given name.  This procedure is intended to be a
157   *      stand-in for the  UNIX "setenv" procedure so that applications   *      stand-in for the  UNIX "setenv" procedure so that applications
158   *      using that procedure will interface properly to Tcl.  To make   *      using that procedure will interface properly to Tcl.  To make
159   *      it a stand-in, the Makefile must define "TclSetEnv" to "setenv".   *      it a stand-in, the Makefile must define "TclSetEnv" to "setenv".
160   *   *
161   * Results:   * Results:
162   *      None.   *      None.
163   *   *
164   * Side effects:   * Side effects:
165   *      The environ array gets updated.   *      The environ array gets updated.
166   *   *
167   *----------------------------------------------------------------------   *----------------------------------------------------------------------
168   */   */
169    
170  void  void
171  TclSetEnv(name, value)  TclSetEnv(name, value)
172      CONST char *name;           /* Name of variable whose value is to be      CONST char *name;           /* Name of variable whose value is to be
173                                   * set (UTF-8). */                                   * set (UTF-8). */
174      CONST char *value;          /* New value for variable (UTF-8). */      CONST char *value;          /* New value for variable (UTF-8). */
175  {  {
176      Tcl_DString envString;      Tcl_DString envString;
177      int index, length, nameLength;      int index, length, nameLength;
178      char *p, *p2, *oldValue;      char *p, *p2, *oldValue;
179    
180      /*      /*
181       * Figure out where the entry is going to go.  If the name doesn't       * Figure out where the entry is going to go.  If the name doesn't
182       * already exist, enlarge the array if necessary to make room.  If the       * already exist, enlarge the array if necessary to make room.  If the
183       * name exists, free its old entry.       * name exists, free its old entry.
184       */       */
185    
186      Tcl_MutexLock(&envMutex);      Tcl_MutexLock(&envMutex);
187      index = TclpFindVariable(name, &length);      index = TclpFindVariable(name, &length);
188    
189      if (index == -1) {      if (index == -1) {
190  #ifndef USE_PUTENV  #ifndef USE_PUTENV
191          if ((length + 2) > environSize) {          if ((length + 2) > environSize) {
192              char **newEnviron;              char **newEnviron;
193    
194              newEnviron = (char **) ckalloc((unsigned)              newEnviron = (char **) ckalloc((unsigned)
195                      ((length + 5) * sizeof(char *)));                      ((length + 5) * sizeof(char *)));
196              memcpy((VOID *) newEnviron, (VOID *) environ,              memcpy((VOID *) newEnviron, (VOID *) environ,
197                      length*sizeof(char *));                      length*sizeof(char *));
198              if (environSize != 0) {              if (environSize != 0) {
199                  ckfree((char *) environ);                  ckfree((char *) environ);
200              }              }
201              environ = newEnviron;              environ = newEnviron;
202              environSize = length + 5;              environSize = length + 5;
203          }          }
204          index = length;          index = length;
205          environ[index + 1] = NULL;          environ[index + 1] = NULL;
206  #endif  #endif
207          oldValue = NULL;          oldValue = NULL;
208          nameLength = strlen(name);          nameLength = strlen(name);
209      } else {      } else {
210          char *env;          char *env;
211    
212          /*          /*
213           * Compare the new value to the existing value.  If they're           * Compare the new value to the existing value.  If they're
214           * the same then quit immediately (e.g. don't rewrite the           * the same then quit immediately (e.g. don't rewrite the
215           * value or propagate it to other interpreters).  Otherwise,           * value or propagate it to other interpreters).  Otherwise,
216           * when there are N interpreters there will be N! propagations           * when there are N interpreters there will be N! propagations
217           * of the same value among the interpreters.           * of the same value among the interpreters.
218           */           */
219    
220          env = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envString);          env = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envString);
221          if (strcmp(value, (env + length + 1)) == 0) {          if (strcmp(value, (env + length + 1)) == 0) {
222              Tcl_DStringFree(&envString);              Tcl_DStringFree(&envString);
223              Tcl_MutexUnlock(&envMutex);              Tcl_MutexUnlock(&envMutex);
224              return;              return;
225          }          }
226          Tcl_DStringFree(&envString);          Tcl_DStringFree(&envString);
227    
228          oldValue = environ[index];          oldValue = environ[index];
229          nameLength = length;          nameLength = length;
230      }      }
231                    
232    
233      /*      /*
234       * Create a new entry.  Build a complete UTF string that contains       * Create a new entry.  Build a complete UTF string that contains
235       * a "name=value" pattern.  Then convert the string to the native       * a "name=value" pattern.  Then convert the string to the native
236       * encoding, and set the environ array value.       * encoding, and set the environ array value.
237       */       */
238    
239      p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2));      p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2));
240      strcpy(p, name);      strcpy(p, name);
241      p[nameLength] = '=';      p[nameLength] = '=';
242      strcpy(p+nameLength+1, value);      strcpy(p+nameLength+1, value);
243      p2 = Tcl_UtfToExternalDString(NULL, p, -1, &envString);      p2 = Tcl_UtfToExternalDString(NULL, p, -1, &envString);
244    
245      /*      /*
246       * Copy the native string to heap memory.       * Copy the native string to heap memory.
247       */       */
248            
249      p = (char *) ckrealloc(p, (unsigned) (strlen(p2) + 1));      p = (char *) ckrealloc(p, (unsigned) (strlen(p2) + 1));
250      strcpy(p, p2);      strcpy(p, p2);
251      Tcl_DStringFree(&envString);      Tcl_DStringFree(&envString);
252    
253  #ifdef USE_PUTENV  #ifdef USE_PUTENV
254      /*      /*
255       * Update the system environment.       * Update the system environment.
256       */       */
257    
258      putenv(p);      putenv(p);
259      index = TclpFindVariable(name, &length);      index = TclpFindVariable(name, &length);
260  #else  #else
261      environ[index] = p;      environ[index] = p;
262  #endif  #endif
263    
264      /*      /*
265       * Watch out for versions of putenv that copy the string (e.g. VC++).       * Watch out for versions of putenv that copy the string (e.g. VC++).
266       * In this case we need to free the string immediately.  Otherwise       * In this case we need to free the string immediately.  Otherwise
267       * update the string in the cache.       * update the string in the cache.
268       */       */
269    
270      if ((index != -1) && (environ[index] == p)) {      if ((index != -1) && (environ[index] == p)) {
271          ReplaceString(oldValue, p);          ReplaceString(oldValue, p);
272      }      }
273    
274      Tcl_MutexUnlock(&envMutex);      Tcl_MutexUnlock(&envMutex);
275  }  }
276    
277  /*  /*
278   *----------------------------------------------------------------------   *----------------------------------------------------------------------
279   *   *
280   * Tcl_PutEnv --   * Tcl_PutEnv --
281   *   *
282   *      Set an environment variable.  Similar to setenv except that   *      Set an environment variable.  Similar to setenv except that
283   *      the information is passed in a single string of the form   *      the information is passed in a single string of the form
284   *      NAME=value, rather than as separate name strings.  This procedure   *      NAME=value, rather than as separate name strings.  This procedure
285   *      is intended to be a stand-in for the  UNIX "putenv" procedure   *      is intended to be a stand-in for the  UNIX "putenv" procedure
286   *      so that applications using that procedure will interface   *      so that applications using that procedure will interface
287   *      properly to Tcl.  To make it a stand-in, the Makefile will   *      properly to Tcl.  To make it a stand-in, the Makefile will
288   *      define "Tcl_PutEnv" to "putenv".   *      define "Tcl_PutEnv" to "putenv".
289   *   *
290   * Results:   * Results:
291   *      None.   *      None.
292   *   *
293   * Side effects:   * Side effects:
294   *      The environ array gets updated, as do all of the interpreters   *      The environ array gets updated, as do all of the interpreters
295   *      that we manage.   *      that we manage.
296   *   *
297   *----------------------------------------------------------------------   *----------------------------------------------------------------------
298   */   */
299    
300  int  int
301  Tcl_PutEnv(string)  Tcl_PutEnv(string)
302      CONST char *string;         /* Info about environment variable in the      CONST char *string;         /* Info about environment variable in the
303                                   * form NAME=value. (native) */                                   * form NAME=value. (native) */
304  {  {
305      Tcl_DString nameString;        Tcl_DString nameString;  
306      int nameLength;      int nameLength;
307      char *name, *value;      char *name, *value;
308    
309      if (string == NULL) {      if (string == NULL) {
310          return 0;          return 0;
311      }      }
312    
313      /*      /*
314       * First convert the native string to UTF.  Then separate the       * First convert the native string to UTF.  Then separate the
315       * string into name and value parts, and call TclSetEnv to do       * string into name and value parts, and call TclSetEnv to do
316       * all of the real work.       * all of the real work.
317       */       */
318    
319      name = Tcl_ExternalToUtfDString(NULL, string, -1, &nameString);      name = Tcl_ExternalToUtfDString(NULL, string, -1, &nameString);
320      value = strchr(name, '=');      value = strchr(name, '=');
321      if (value == NULL) {      if (value == NULL) {
322          return 0;          return 0;
323      }      }
324      nameLength = value - name;      nameLength = value - name;
325      if (nameLength == 0) {      if (nameLength == 0) {
326          return 0;          return 0;
327      }      }
328    
329      value[0] = '\0';      value[0] = '\0';
330      TclSetEnv(name, value+1);      TclSetEnv(name, value+1);
331      Tcl_DStringFree(&nameString);      Tcl_DStringFree(&nameString);
332      return 0;      return 0;
333  }  }
334    
335  /*  /*
336   *----------------------------------------------------------------------   *----------------------------------------------------------------------
337   *   *
338   * TclUnsetEnv --   * TclUnsetEnv --
339   *   *
340   *      Remove an environment variable, updating the "env" arrays   *      Remove an environment variable, updating the "env" arrays
341   *      in all interpreters managed by us.  This function is intended   *      in all interpreters managed by us.  This function is intended
342   *      to replace the UNIX "unsetenv" function (but to do this the   *      to replace the UNIX "unsetenv" function (but to do this the
343   *      Makefile must be modified to redefine "TclUnsetEnv" to   *      Makefile must be modified to redefine "TclUnsetEnv" to
344   *      "unsetenv".   *      "unsetenv".
345   *   *
346   * Results:   * Results:
347   *      None.   *      None.
348   *   *
349   * Side effects:   * Side effects:
350   *      Interpreters are updated, as is environ.   *      Interpreters are updated, as is environ.
351   *   *
352   *----------------------------------------------------------------------   *----------------------------------------------------------------------
353   */   */
354    
355  void  void
356  TclUnsetEnv(name)  TclUnsetEnv(name)
357      CONST char *name;           /* Name of variable to remove (UTF-8). */      CONST char *name;           /* Name of variable to remove (UTF-8). */
358  {  {
359      char *oldValue;      char *oldValue;
360      int length, index;      int length, index;
361  #ifdef USE_PUTENV  #ifdef USE_PUTENV
362      Tcl_DString envString;      Tcl_DString envString;
363      char *string;      char *string;
364  #else  #else
365      char **envPtr;      char **envPtr;
366  #endif  #endif
367    
368      Tcl_MutexLock(&envMutex);      Tcl_MutexLock(&envMutex);
369      index = TclpFindVariable(name, &length);      index = TclpFindVariable(name, &length);
370    
371      /*      /*
372       * First make sure that the environment variable exists to avoid       * First make sure that the environment variable exists to avoid
373       * doing needless work and to avoid recursion on the unset.       * doing needless work and to avoid recursion on the unset.
374       */       */
375            
376      if (index == -1) {      if (index == -1) {
377          Tcl_MutexUnlock(&envMutex);          Tcl_MutexUnlock(&envMutex);
378          return;          return;
379      }      }
380      /*      /*
381       * Remember the old value so we can free it if Tcl created the string.       * Remember the old value so we can free it if Tcl created the string.
382       */       */
383    
384      oldValue = environ[index];      oldValue = environ[index];
385    
386      /*      /*
387       * Update the system environment.  This must be done before we       * Update the system environment.  This must be done before we
388       * update the interpreters or we will recurse.       * update the interpreters or we will recurse.
389       */       */
390    
391  #ifdef USE_PUTENV  #ifdef USE_PUTENV
392      string = ckalloc(length+2);      string = ckalloc(length+2);
393      memcpy((VOID *) string, (VOID *) name, (size_t) length);      memcpy((VOID *) string, (VOID *) name, (size_t) length);
394      string[length] = '=';      string[length] = '=';
395      string[length+1] = '\0';      string[length+1] = '\0';
396            
397      Tcl_UtfToExternalDString(NULL, string, -1, &envString);      Tcl_UtfToExternalDString(NULL, string, -1, &envString);
398      string = ckrealloc(string, (unsigned) (Tcl_DStringLength(&envString)+1));      string = ckrealloc(string, (unsigned) (Tcl_DStringLength(&envString)+1));
399      strcpy(string, Tcl_DStringValue(&envString));      strcpy(string, Tcl_DStringValue(&envString));
400      Tcl_DStringFree(&envString);      Tcl_DStringFree(&envString);
401    
402      putenv(string);      putenv(string);
403    
404      /*      /*
405       * Watch out for versions of putenv that copy the string (e.g. VC++).       * Watch out for versions of putenv that copy the string (e.g. VC++).
406       * In this case we need to free the string immediately.  Otherwise       * In this case we need to free the string immediately.  Otherwise
407       * update the string in the cache.       * update the string in the cache.
408       */       */
409    
410      if (environ[index] == string) {      if (environ[index] == string) {
411          ReplaceString(oldValue, string);          ReplaceString(oldValue, string);
412      }      }
413  #else  #else
414      for (envPtr = environ+index+1; ; envPtr++) {      for (envPtr = environ+index+1; ; envPtr++) {
415          envPtr[-1] = *envPtr;          envPtr[-1] = *envPtr;
416          if (*envPtr == NULL) {          if (*envPtr == NULL) {
417              break;              break;
418          }          }
419      }      }
420      ReplaceString(oldValue, NULL);      ReplaceString(oldValue, NULL);
421  #endif  #endif
422    
423      Tcl_MutexUnlock(&envMutex);      Tcl_MutexUnlock(&envMutex);
424  }  }
425    
426  /*  /*
427   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
428   *   *
429   * TclGetEnv --   * TclGetEnv --
430   *   *
431   *      Retrieve the value of an environment variable.   *      Retrieve the value of an environment variable.
432   *   *
433   * Results:   * Results:
434   *      The result is a pointer to a string specifying the value of the   *      The result is a pointer to a string specifying the value of the
435   *      environment variable, or NULL if that environment variable does   *      environment variable, or NULL if that environment variable does
436   *      not exist.  Storage for the result string is allocated in valuePtr;   *      not exist.  Storage for the result string is allocated in valuePtr;
437   *      the caller must call Tcl_DStringFree() when the result is no   *      the caller must call Tcl_DStringFree() when the result is no
438   *      longer needed.   *      longer needed.
439   *   *
440   * Side effects:   * Side effects:
441   *      None.   *      None.
442   *   *
443   *----------------------------------------------------------------------   *----------------------------------------------------------------------
444   */   */
445    
446  char *  char *
447  TclGetEnv(name, valuePtr)  TclGetEnv(name, valuePtr)
448      CONST char *name;           /* Name of environment variable to find      CONST char *name;           /* Name of environment variable to find
449                                   * (UTF-8). */                                   * (UTF-8). */
450      Tcl_DString *valuePtr;      /* Uninitialized or free DString in which      Tcl_DString *valuePtr;      /* Uninitialized or free DString in which
451                                   * the value of the environment variable is                                   * the value of the environment variable is
452                                   * stored. */                                   * stored. */
453  {  {
454      int length, index;      int length, index;
455      char *result;      char *result;
456    
457      Tcl_MutexLock(&envMutex);      Tcl_MutexLock(&envMutex);
458      index = TclpFindVariable(name, &length);      index = TclpFindVariable(name, &length);
459      result = NULL;      result = NULL;
460      if (index != -1) {      if (index != -1) {
461          Tcl_DString envStr;          Tcl_DString envStr;
462                    
463          result = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envStr);          result = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envStr);
464          result += length;          result += length;
465          if (*result == '=') {          if (*result == '=') {
466              result++;              result++;
467              Tcl_DStringInit(valuePtr);              Tcl_DStringInit(valuePtr);
468              Tcl_DStringAppend(valuePtr, result, -1);              Tcl_DStringAppend(valuePtr, result, -1);
469              result = Tcl_DStringValue(valuePtr);              result = Tcl_DStringValue(valuePtr);
470          } else {          } else {
471              result = NULL;              result = NULL;
472          }          }
473          Tcl_DStringFree(&envStr);          Tcl_DStringFree(&envStr);
474      }      }
475      Tcl_MutexUnlock(&envMutex);      Tcl_MutexUnlock(&envMutex);
476      return result;      return result;
477  }  }
478    
479  /*  /*
480   *----------------------------------------------------------------------   *----------------------------------------------------------------------
481   *   *
482   * EnvTraceProc --   * EnvTraceProc --
483   *   *
484   *      This procedure is invoked whenever an environment variable   *      This procedure is invoked whenever an environment variable
485   *      is read, modified or deleted.  It propagates the change to the global   *      is read, modified or deleted.  It propagates the change to the global
486   *      "environ" array.   *      "environ" array.
487   *   *
488   * Results:   * Results:
489   *      Always returns NULL to indicate success.   *      Always returns NULL to indicate success.
490   *   *
491   * Side effects:   * Side effects:
492   *      Environment variable changes get propagated.  If the whole   *      Environment variable changes get propagated.  If the whole
493   *      "env" array is deleted, then we stop managing things for   *      "env" array is deleted, then we stop managing things for
494   *      this interpreter (usually this happens because the whole   *      this interpreter (usually this happens because the whole
495   *      interpreter is being deleted).   *      interpreter is being deleted).
496   *   *
497   *----------------------------------------------------------------------   *----------------------------------------------------------------------
498   */   */
499    
500          /* ARGSUSED */          /* ARGSUSED */
501  static char *  static char *
502  EnvTraceProc(clientData, interp, name1, name2, flags)  EnvTraceProc(clientData, interp, name1, name2, flags)
503      ClientData clientData;      /* Not used. */      ClientData clientData;      /* Not used. */
504      Tcl_Interp *interp;         /* Interpreter whose "env" variable is      Tcl_Interp *interp;         /* Interpreter whose "env" variable is
505                                   * being modified. */                                   * being modified. */
506      char *name1;                /* Better be "env". */      char *name1;                /* Better be "env". */
507      char *name2;                /* Name of variable being modified, or NULL      char *name2;                /* Name of variable being modified, or NULL
508                                   * if whole array is being deleted (UTF-8). */                                   * if whole array is being deleted (UTF-8). */
509      int flags;                  /* Indicates what's happening. */      int flags;                  /* Indicates what's happening. */
510  {  {
511      /*      /*
512       * For array traces, let TclSetupEnv do all the work.       * For array traces, let TclSetupEnv do all the work.
513       */       */
514    
515      if (flags & TCL_TRACE_ARRAY) {      if (flags & TCL_TRACE_ARRAY) {
516          TclSetupEnv(interp);          TclSetupEnv(interp);
517          return NULL;          return NULL;
518      }      }
519    
520      /*      /*
521       * If name2 is NULL, then return and do nothing.       * If name2 is NULL, then return and do nothing.
522       */       */
523            
524      if (name2 == NULL) {      if (name2 == NULL) {
525          return NULL;          return NULL;
526      }      }
527    
528      /*      /*
529       * If a value is being set, call TclSetEnv to do all of the work.       * If a value is being set, call TclSetEnv to do all of the work.
530       */       */
531    
532      if (flags & TCL_TRACE_WRITES) {      if (flags & TCL_TRACE_WRITES) {
533          char *value;          char *value;
534                    
535          value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY);          value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY);
536          TclSetEnv(name2, value);          TclSetEnv(name2, value);
537      }      }
538    
539      /*      /*
540       * If a value is being read, call TclGetEnv to do all of the work.       * If a value is being read, call TclGetEnv to do all of the work.
541       */       */
542    
543      if (flags & TCL_TRACE_READS) {      if (flags & TCL_TRACE_READS) {
544          Tcl_DString valueString;          Tcl_DString valueString;
545          char *value;          char *value;
546    
547          value = TclGetEnv(name2, &valueString);          value = TclGetEnv(name2, &valueString);
548          if (value == NULL) {          if (value == NULL) {
549              return "no such variable";              return "no such variable";
550          }          }
551          Tcl_SetVar2(interp, name1, name2, value, 0);          Tcl_SetVar2(interp, name1, name2, value, 0);
552          Tcl_DStringFree(&valueString);          Tcl_DStringFree(&valueString);
553      }      }
554    
555      /*      /*
556       * For unset traces, let TclUnsetEnv do all the work.       * For unset traces, let TclUnsetEnv do all the work.
557       */       */
558    
559      if (flags & TCL_TRACE_UNSETS) {      if (flags & TCL_TRACE_UNSETS) {
560          TclUnsetEnv(name2);          TclUnsetEnv(name2);
561      }      }
562      return NULL;      return NULL;
563  }  }
564    
565  /*  /*
566   *----------------------------------------------------------------------   *----------------------------------------------------------------------
567   *   *
568   * ReplaceString --   * ReplaceString --
569   *   *
570   *      Replace one string with another in the environment variable   *      Replace one string with another in the environment variable
571   *      cache.  The cache keeps track of all of the environment   *      cache.  The cache keeps track of all of the environment
572   *      variables that Tcl has modified so they can be freed later.   *      variables that Tcl has modified so they can be freed later.
573   *   *
574   * Results:   * Results:
575   *      None.   *      None.
576   *   *
577   * Side effects:   * Side effects:
578   *      May free the old string.   *      May free the old string.
579   *   *
580   *----------------------------------------------------------------------   *----------------------------------------------------------------------
581   */   */
582    
583  static void  static void
584  ReplaceString(oldStr, newStr)  ReplaceString(oldStr, newStr)
585      CONST char *oldStr;         /* Old environment string. */      CONST char *oldStr;         /* Old environment string. */
586      char *newStr;               /* New environment string. */      char *newStr;               /* New environment string. */
587  {  {
588      int i;      int i;
589      char **newCache;      char **newCache;
590    
591      /*      /*
592       * Check to see if the old value was allocated by Tcl.  If so,       * Check to see if the old value was allocated by Tcl.  If so,
593       * it needs to be deallocated to avoid memory leaks.  Note that this       * it needs to be deallocated to avoid memory leaks.  Note that this
594       * algorithm is O(n), not O(1).  This will result in n-squared behavior       * algorithm is O(n), not O(1).  This will result in n-squared behavior
595       * if lots of environment changes are being made.       * if lots of environment changes are being made.
596       */       */
597    
598      for (i = 0; i < cacheSize; i++) {      for (i = 0; i < cacheSize; i++) {
599          if ((environCache[i] == oldStr) || (environCache[i] == NULL)) {          if ((environCache[i] == oldStr) || (environCache[i] == NULL)) {
600              break;              break;
601          }          }
602      }      }
603      if (i < cacheSize) {      if (i < cacheSize) {
604          /*          /*
605           * Replace or delete the old value.           * Replace or delete the old value.
606           */           */
607    
608          if (environCache[i]) {          if (environCache[i]) {
609              ckfree(environCache[i]);              ckfree(environCache[i]);
610          }          }
611                            
612          if (newStr) {          if (newStr) {
613              environCache[i] = newStr;              environCache[i] = newStr;
614          } else {          } else {
615              for (; i < cacheSize-1; i++) {              for (; i < cacheSize-1; i++) {
616                  environCache[i] = environCache[i+1];                  environCache[i] = environCache[i+1];
617              }              }
618              environCache[cacheSize-1] = NULL;              environCache[cacheSize-1] = NULL;
619          }          }
620      } else {          } else {    
621          int allocatedSize = (cacheSize + 5) * sizeof(char *);          int allocatedSize = (cacheSize + 5) * sizeof(char *);
622    
623          /*          /*
624           * We need to grow the cache in order to hold the new string.           * We need to grow the cache in order to hold the new string.
625           */           */
626    
627          newCache = (char **) ckalloc((unsigned) allocatedSize);          newCache = (char **) ckalloc((unsigned) allocatedSize);
628          (VOID *) memset(newCache, (int) 0, (size_t) allocatedSize);          (VOID *) memset(newCache, (int) 0, (size_t) allocatedSize);
629                    
630          if (environCache) {          if (environCache) {
631              memcpy((VOID *) newCache, (VOID *) environCache,              memcpy((VOID *) newCache, (VOID *) environCache,
632                      (size_t) (cacheSize * sizeof(char*)));                      (size_t) (cacheSize * sizeof(char*)));
633              ckfree((char *) environCache);              ckfree((char *) environCache);
634          }          }
635          environCache = newCache;          environCache = newCache;
636          environCache[cacheSize] = (char *) newStr;          environCache[cacheSize] = (char *) newStr;
637          environCache[cacheSize+1] = NULL;          environCache[cacheSize+1] = NULL;
638          cacheSize += 5;          cacheSize += 5;
639      }      }
640  }  }
641    
642  /*  /*
643   *----------------------------------------------------------------------   *----------------------------------------------------------------------
644   *   *
645   * TclFinalizeEnvironment --   * TclFinalizeEnvironment --
646   *   *
647   *      This function releases any storage allocated by this module   *      This function releases any storage allocated by this module
648   *      that isn't still in use by the global environment.  Any   *      that isn't still in use by the global environment.  Any
649   *      strings that are still in the environment will be leaked.   *      strings that are still in the environment will be leaked.
650   *   *
651   * Results:   * Results:
652   *      None.   *      None.
653   *   *
654   * Side effects:   * Side effects:
655   *      May deallocate storage.   *      May deallocate storage.
656   *   *
657   *----------------------------------------------------------------------   *----------------------------------------------------------------------
658   */   */
659    
660  void  void
661  TclFinalizeEnvironment()  TclFinalizeEnvironment()
662  {  {
663      /*      /*
664       * For now we just deallocate the cache array and none of the environment       * For now we just deallocate the cache array and none of the environment
665       * strings.  This may leak more memory that strictly necessary, since some       * strings.  This may leak more memory that strictly necessary, since some
666       * of the strings may no longer be in the environment.  However,       * of the strings may no longer be in the environment.  However,
667       * determining which ones are ok to delete is n-squared, and is pretty       * determining which ones are ok to delete is n-squared, and is pretty
668       * unlikely, so we don't bother.       * unlikely, so we don't bother.
669       */       */
670    
671      if (environCache) {      if (environCache) {
672          ckfree((char *) environCache);          ckfree((char *) environCache);
673          environCache = NULL;          environCache = NULL;
674          cacheSize    = 0;          cacheSize    = 0;
675  #ifndef USE_PUTENV  #ifndef USE_PUTENV
676          environSize  = 0;          environSize  = 0;
677  #endif  #endif
678      }      }
679  }  }
680    
681  /* End of tclenv.c */  /* End of tclenv.c */

Legend:
Removed from v.64  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25