1 /*
2 * tclEnv.c --
3 *
4 * Tcl support for environment variables, including a setenv function.
5 * This file contains the generic portion of the environment module. It
6 * is primarily responsible for keeping the "env" arrays in sync with the
7 * system environment variables.
8 *
9 * Copyright © 1991-1994 The Regents of the University of California.
10 * Copyright © 1994-1998 Sun Microsystems, Inc.
11 *
12 * See the file "license.terms" for information on usage and redistribution of
13 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 */
15
16 #include "tclInt.h"
17
18 TCL_DECLARE_MUTEX(envMutex) /* To serialize access to environ. */
19
20 #if defined(_WIN32)
21 # define tenviron _wenviron
22 # define tenviron2utfdstr(string, len, dsPtr) (Tcl_DStringInit(dsPtr), \
23 (char *)Tcl_Char16ToUtfDString((const unsigned short *)(string), ((((len) + 2) >> 1) - 1), (dsPtr)))
24 # define utf2tenvirondstr(string, len, dsPtr) (Tcl_DStringInit(dsPtr), \
25 (const WCHAR *)Tcl_UtfToChar16DString((string), (len), (dsPtr)))
26 # define techar WCHAR
27 # ifdef USE_PUTENV
28 # define putenv(env) _wputenv((const wchar_t *)env)
29 # endif
30 #else
31 # define tenviron environ
32 # define tenviron2utfdstr(tenvstr, len, dstr) \
33 Tcl_ExternalToUtfDString(NULL, tenvstr, len, dstr)
34 # define utf2tenvirondstr(str, len, dstr) \
35 Tcl_UtfToExternalDString(NULL, str, len, dstr)
36 # define techar char
37 #endif
38
39
40 /* MODULE_SCOPE */
41 size_t TclEnvEpoch = 0; /* Epoch of the tcl environment
42 * (if changed with tcl-env). */
43
44 static struct {
45 int cacheSize; /* Number of env strings in cache. */
46 char **cache; /* Array containing all of the environment
47 * strings that Tcl has allocated. */
48 #ifndef USE_PUTENV
49 techar **ourEnviron; /* Cache of the array that we allocate. We
50 * need to track this in case another
51 * subsystem swaps around the environ array
52 * like we do. */
53 int ourEnvironSize; /* Non-zero means that the environ array was
54 * malloced and has this many total entries
55 * allocated to it (not all may be in use at
56 * once). Zero means that the environment
57 * array is in its original static state. */
58 #endif
59 } env;
60
61 #define tNTL sizeof(techar)
62
63 /*
64 * Declarations for local functions defined in this file:
65 */
66
67 static char * EnvTraceProc(ClientData clientData, Tcl_Interp *interp,
68 const char *name1, const char *name2, int flags);
69 static void ReplaceString(const char *oldStr, char *newStr);
70 MODULE_SCOPE void TclSetEnv(const char *name, const char *value);
71 MODULE_SCOPE void TclUnsetEnv(const char *name);
72
73 /*
74 *----------------------------------------------------------------------
75 *
76 * TclSetupEnv --
77 *
78 * This function is invoked for an interpreter to make environment
79 * variables accessible from that interpreter via the "env" associative
80 * array.
81 *
82 * Results:
83 * None.
84 *
85 * Side effects:
86 * The interpreter is added to a list of interpreters managed by us, so
87 * that its view of envariables can be kept consistent with the view in
88 * other interpreters. If this is the first call to TclSetupEnv, then
89 * additional initialization happens, such as copying the environment to
90 * dynamically-allocated space for ease of management.
91 *
92 *----------------------------------------------------------------------
93 */
94
95 void
TclSetupEnv(Tcl_Interp * interp)96 TclSetupEnv(
97 Tcl_Interp *interp) /* Interpreter whose "env" array is to be
98 * managed. */
99 {
100 Var *varPtr, *arrayPtr;
101 Tcl_Obj *varNamePtr;
102 Tcl_DString envString;
103 Tcl_HashTable namesHash;
104 Tcl_HashEntry *hPtr;
105 Tcl_HashSearch search;
106
107 /*
108 * Synchronize the values in the environ array with the contents of the
109 * Tcl "env" variable. To do this:
110 * 1) Remove the trace that fires when the "env" var is updated.
111 * 2) Find the existing contents of the "env", storing in a hash table.
112 * 3) Create/update elements for each environ variable, removing
113 * elements from the hash table as we go.
114 * 4) Remove the elements for each remaining entry in the hash table,
115 * which must have existed before yet have no analog in the environ
116 * variable.
117 * 5) Add a trace that synchronizes the "env" array.
118 */
119
120 Tcl_UntraceVar2(interp, "env", NULL,
121 TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
122 TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc, NULL);
123
124 /*
125 * Find out what elements are currently in the global env array.
126 */
127
128 TclNewLiteralStringObj(varNamePtr, "env");
129 Tcl_IncrRefCount(varNamePtr);
130 Tcl_InitObjHashTable(&namesHash);
131 varPtr = TclObjLookupVarEx(interp, varNamePtr, NULL, TCL_GLOBAL_ONLY,
132 /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
133 TclFindArrayPtrElements(varPtr, &namesHash);
134
135 #if defined(_WIN32)
136 if (tenviron == NULL) {
137 /*
138 * When we are started from main(), the _wenviron array could
139 * be NULL and will be initialized by the first _wgetenv() call.
140 */
141
142 (void) _wgetenv(L"WINDIR");
143 }
144 #endif
145
146 /*
147 * Go through the environment array and transfer its values into Tcl. At
148 * the same time, remove those elements we add/update from the hash table
149 * of existing elements, so that after this part processes, that table
150 * will hold just the parts to remove.
151 */
152
153 if (tenviron[0] != NULL) {
154 int i;
155
156 Tcl_MutexLock(&envMutex);
157 for (i = 0; tenviron[i] != NULL; i++) {
158 Tcl_Obj *obj1, *obj2;
159 const char *p1;
160 char *p2;
161
162 p1 = tenviron2utfdstr(tenviron[i], -1, &envString);
163 p2 = (char *)strchr(p1, '=');
164 if (p2 == NULL) {
165 /*
166 * This condition seem to happen occasionally under some
167 * versions of Solaris, or when encoding accidents swallow the
168 * '='; ignore the entry.
169 */
170
171 Tcl_DStringFree(&envString);
172 continue;
173 }
174 p2++;
175 p2[-1] = '\0';
176 #if defined(_WIN32)
177 /*
178 * Enforce PATH and COMSPEC to be all uppercase. This eliminates
179 * additional trace logic otherwise required in init.tcl.
180 */
181
182 if (strcasecmp(p1, "PATH") == 0) {
183 p1 = "PATH";
184 } else if (strcasecmp(p1, "COMSPEC") == 0) {
185 p1 = "COMSPEC";
186 }
187 #endif
188 obj1 = Tcl_NewStringObj(p1, -1);
189 obj2 = Tcl_NewStringObj(p2, -1);
190 Tcl_DStringFree(&envString);
191
192 Tcl_IncrRefCount(obj1);
193 Tcl_IncrRefCount(obj2);
194 Tcl_ObjSetVar2(interp, varNamePtr, obj1, obj2, TCL_GLOBAL_ONLY);
195 hPtr = Tcl_FindHashEntry(&namesHash, obj1);
196 if (hPtr != NULL) {
197 Tcl_DeleteHashEntry(hPtr);
198 }
199 Tcl_DecrRefCount(obj1);
200 Tcl_DecrRefCount(obj2);
201 }
202 Tcl_MutexUnlock(&envMutex);
203 }
204
205 /*
206 * Delete those elements that existed in the array but which had no
207 * counterparts in the environment array.
208 */
209
210 for (hPtr=Tcl_FirstHashEntry(&namesHash, &search); hPtr!=NULL;
211 hPtr=Tcl_NextHashEntry(&search)) {
212 Tcl_Obj *elemName = (Tcl_Obj *)Tcl_GetHashValue(hPtr);
213
214 TclObjUnsetVar2(interp, varNamePtr, elemName, TCL_GLOBAL_ONLY);
215 }
216 Tcl_DeleteHashTable(&namesHash);
217 Tcl_DecrRefCount(varNamePtr);
218
219 /*
220 * Re-establish the trace.
221 */
222
223 Tcl_TraceVar2(interp, "env", NULL,
224 TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
225 TCL_TRACE_READS | TCL_TRACE_ARRAY, EnvTraceProc, NULL);
226 }
227
228 /*
229 *----------------------------------------------------------------------
230 *
231 * TclSetEnv --
232 *
233 * Set an environment variable, replacing an existing value or creating a
234 * new variable if there doesn't exist a variable by the given name. This
235 * function is intended to be a stand-in for the UNIX "setenv" function
236 * so that applications using that function will interface properly to
237 * Tcl. To make it a stand-in, the Makefile must define "TclSetEnv" to
238 * "setenv".
239 *
240 * Results:
241 * None.
242 *
243 * Side effects:
244 * The environ array gets updated.
245 *
246 *----------------------------------------------------------------------
247 */
248
249 void
TclSetEnv(const char * name,const char * value)250 TclSetEnv(
251 const char *name, /* Name of variable whose value is to be set
252 * (UTF-8). */
253 const char *value) /* New value for variable (UTF-8). */
254 {
255 Tcl_DString envString;
256 unsigned nameLength, valueLength;
257 int index, length;
258 char *p, *oldValue;
259 const techar *p2;
260
261 /*
262 * Figure out where the entry is going to go. If the name doesn't already
263 * exist, enlarge the array if necessary to make room. If the name exists,
264 * free its old entry.
265 */
266
267 Tcl_MutexLock(&envMutex);
268 index = TclpFindVariable(name, &length);
269
270 if (index == -1) {
271 #ifndef USE_PUTENV
272 /*
273 * We need to handle the case where the environment may be changed
274 * outside our control. ourEnvironSize is only valid if the current
275 * environment is the one we allocated. [Bug 979640]
276 */
277
278 if ((env.ourEnviron != tenviron) || (length+2 > env.ourEnvironSize)) {
279 techar **newEnviron = (techar **)ckalloc((length + 5) * sizeof(techar *));
280
281 memcpy(newEnviron, tenviron, length * sizeof(techar *));
282 if ((env.ourEnvironSize != 0) && (env.ourEnviron != NULL)) {
283 ckfree(env.ourEnviron);
284 }
285 tenviron = (env.ourEnviron = newEnviron);
286 env.ourEnvironSize = length + 5;
287 }
288 index = length;
289 tenviron[index + 1] = NULL;
290 #endif /* USE_PUTENV */
291 oldValue = NULL;
292 nameLength = strlen(name);
293 } else {
294 const char *oldEnv;
295
296 /*
297 * Compare the new value to the existing value. If they're the same
298 * then quit immediately (e.g. don't rewrite the value or propagate it
299 * to other interpreters). Otherwise, when there are N interpreters
300 * there will be N! propagations of the same value among the
301 * interpreters.
302 */
303
304 oldEnv = tenviron2utfdstr(tenviron[index], -1, &envString);
305 if (strcmp(value, oldEnv + (length + 1)) == 0) {
306 Tcl_DStringFree(&envString);
307 Tcl_MutexUnlock(&envMutex);
308 return;
309 }
310 Tcl_DStringFree(&envString);
311
312 oldValue = (char *)tenviron[index];
313 nameLength = length;
314 }
315
316 /*
317 * Create a new entry. Build a complete UTF string that contains a
318 * "name=value" pattern. Then convert the string to the native encoding,
319 * and set the environ array value.
320 */
321
322 valueLength = strlen(value);
323 p = (char *)ckalloc(nameLength + valueLength + 2);
324 memcpy(p, name, nameLength);
325 p[nameLength] = '=';
326 memcpy(p+nameLength+1, value, valueLength+1);
327 p2 = utf2tenvirondstr(p, -1, &envString);
328
329 /*
330 * Copy the native string to heap memory.
331 */
332
333 p = (char *)ckrealloc(p, Tcl_DStringLength(&envString) + tNTL);
334 memcpy(p, p2, Tcl_DStringLength(&envString) + tNTL);
335 Tcl_DStringFree(&envString);
336
337 #ifdef USE_PUTENV
338 /*
339 * Update the system environment.
340 */
341
342 putenv(p);
343 index = TclpFindVariable(name, &length);
344 #else
345 tenviron[index] = (techar *)p;
346 #endif /* USE_PUTENV */
347
348 /*
349 * Watch out for versions of putenv that copy the string (e.g. VC++). In
350 * this case we need to free the string immediately. Otherwise update the
351 * string in the cache.
352 */
353
354 if ((index != -1) && (tenviron[index] == (techar *)p)) {
355 ReplaceString(oldValue, p);
356 #ifdef HAVE_PUTENV_THAT_COPIES
357 } else {
358 /*
359 * This putenv() copies instead of taking ownership.
360 */
361
362 ckfree(p);
363 #endif /* HAVE_PUTENV_THAT_COPIES */
364 }
365
366 Tcl_MutexUnlock(&envMutex);
367
368 if (!strcmp(name, "HOME")) {
369 /*
370 * If the user's home directory has changed, we must invalidate the
371 * filesystem cache, because '~' expansions will now be incorrect.
372 */
373
374 Tcl_FSMountsChanged(NULL);
375 }
376 }
377
378 /*
379 *----------------------------------------------------------------------
380 *
381 * Tcl_PutEnv --
382 *
383 * Set an environment variable. Similar to setenv except that the
384 * information is passed in a single string of the form NAME=value,
385 * rather than as separate name strings. This function is intended to be
386 * a stand-in for the UNIX "putenv" function so that applications using
387 * that function will interface properly to Tcl. To make it a stand-in,
388 * the Makefile will define "Tcl_PutEnv" to "putenv".
389 *
390 * Results:
391 * None.
392 *
393 * Side effects:
394 * The environ array gets updated, as do all of the interpreters that we
395 * manage.
396 *
397 *----------------------------------------------------------------------
398 */
399
400 int
Tcl_PutEnv(const char * assignment)401 Tcl_PutEnv(
402 const char *assignment) /* Info about environment variable in the form
403 * NAME=value. (native) */
404 {
405 Tcl_DString nameString;
406 const char *name;
407 char *value;
408
409 if (assignment == NULL) {
410 return 0;
411 }
412
413 /*
414 * First convert the native string to UTF. Then separate the string into
415 * name and value parts, and call TclSetEnv to do all of the real work.
416 */
417
418 name = Tcl_ExternalToUtfDString(NULL, assignment, -1, &nameString);
419 value = (char *)strchr(name, '=');
420
421 if ((value != NULL) && (value != name)) {
422 value[0] = '\0';
423 TclSetEnv(name, value+1);
424 }
425 TclEnvEpoch++;
426
427 Tcl_DStringFree(&nameString);
428 return 0;
429 }
430
431 /*
432 *----------------------------------------------------------------------
433 *
434 * TclUnsetEnv --
435 *
436 * Remove an environment variable, updating the "env" arrays in all
437 * interpreters managed by us. This function is intended to replace the
438 * UNIX "unsetenv" function (but to do this the Makefile must be modified
439 * to redefine "TclUnsetEnv" to "unsetenv".
440 *
441 * Results:
442 * None.
443 *
444 * Side effects:
445 * Interpreters are updated, as is environ.
446 *
447 *----------------------------------------------------------------------
448 */
449
450 void
TclUnsetEnv(const char * name)451 TclUnsetEnv(
452 const char *name) /* Name of variable to remove (UTF-8). */
453 {
454 char *oldValue;
455 int length;
456 int index;
457 #ifdef USE_PUTENV_FOR_UNSET
458 Tcl_DString envString;
459 char *string;
460 #else
461 char **envPtr;
462 #endif /* USE_PUTENV_FOR_UNSET */
463
464 Tcl_MutexLock(&envMutex);
465 index = TclpFindVariable(name, &length);
466
467 /*
468 * First make sure that the environment variable exists to avoid doing
469 * needless work and to avoid recursion on the unset.
470 */
471
472 if (index == -1) {
473 Tcl_MutexUnlock(&envMutex);
474 return;
475 }
476
477 /*
478 * Remember the old value so we can free it if Tcl created the string.
479 */
480
481 oldValue = (char *)tenviron[index];
482
483 /*
484 * Update the system environment. This must be done before we update the
485 * interpreters or we will recurse.
486 */
487
488 #ifdef USE_PUTENV_FOR_UNSET
489 /*
490 * For those platforms that support putenv to unset, Linux indicates
491 * that no = should be included, and Windows requires it.
492 */
493
494 #if defined(_WIN32)
495 string = (char *)ckalloc(length + 2);
496 memcpy(string, name, length);
497 string[length] = '=';
498 string[length+1] = '\0';
499 #else
500 string = (char *)ckalloc(length + 1);
501 memcpy(string, name, length);
502 string[length] = '\0';
503 #endif /* _WIN32 */
504
505 utf2tenvirondstr(string, -1, &envString);
506 string = (char *)ckrealloc(string, Tcl_DStringLength(&envString) + tNTL);
507 memcpy(string, Tcl_DStringValue(&envString),
508 Tcl_DStringLength(&envString) + tNTL);
509 Tcl_DStringFree(&envString);
510
511 putenv(string);
512
513 /*
514 * Watch out for versions of putenv that copy the string (e.g. VC++). In
515 * this case we need to free the string immediately. Otherwise update the
516 * string in the cache.
517 */
518
519 if (tenviron[index] == (techar *)string) {
520 ReplaceString(oldValue, string);
521 #ifdef HAVE_PUTENV_THAT_COPIES
522 } else {
523 /*
524 * This putenv() copies instead of taking ownership.
525 */
526
527 ckfree(string);
528 #endif /* HAVE_PUTENV_THAT_COPIES */
529 }
530 #else /* !USE_PUTENV_FOR_UNSET */
531 for (envPtr = (char **)(tenviron+index+1); ; envPtr++) {
532 envPtr[-1] = *envPtr;
533 if (*envPtr == NULL) {
534 break;
535 }
536 }
537 ReplaceString(oldValue, NULL);
538 #endif /* USE_PUTENV_FOR_UNSET */
539
540 Tcl_MutexUnlock(&envMutex);
541 }
542
543 /*
544 *---------------------------------------------------------------------------
545 *
546 * TclGetEnv --
547 *
548 * Retrieve the value of an environment variable.
549 *
550 * Results:
551 * The result is a pointer to a string specifying the value of the
552 * environment variable, or NULL if that environment variable does not
553 * exist. Storage for the result string is allocated in valuePtr; the
554 * caller must call Tcl_DStringFree() when the result is no longer
555 * needed.
556 *
557 * Side effects:
558 * None.
559 *
560 *----------------------------------------------------------------------
561 */
562
563 const char *
TclGetEnv(const char * name,Tcl_DString * valuePtr)564 TclGetEnv(
565 const char *name, /* Name of environment variable to find
566 * (UTF-8). */
567 Tcl_DString *valuePtr) /* Uninitialized or free DString in which the
568 * value of the environment variable is
569 * stored. */
570 {
571 int length, index;
572 const char *result;
573
574 Tcl_MutexLock(&envMutex);
575 index = TclpFindVariable(name, &length);
576 result = NULL;
577 if (index != -1) {
578 Tcl_DString envStr;
579
580 result = tenviron2utfdstr(tenviron[index], -1, &envStr);
581 result += length;
582 if (*result == '=') {
583 result++;
584 Tcl_DStringInit(valuePtr);
585 Tcl_DStringAppend(valuePtr, result, -1);
586 result = Tcl_DStringValue(valuePtr);
587 } else {
588 result = NULL;
589 }
590 Tcl_DStringFree(&envStr);
591 }
592 Tcl_MutexUnlock(&envMutex);
593 return result;
594 }
595
596 /*
597 *----------------------------------------------------------------------
598 *
599 * EnvTraceProc --
600 *
601 * This function is invoked whenever an environment variable is read,
602 * modified or deleted. It propagates the change to the global "environ"
603 * array.
604 *
605 * Results:
606 * Returns NULL to indicate success, or an error-message if the array
607 * element being handled doesn't exist.
608 *
609 * Side effects:
610 * Environment variable changes get propagated. If the whole "env" array
611 * is deleted, then we stop managing things for this interpreter (usually
612 * this happens because the whole interpreter is being deleted).
613 *
614 *----------------------------------------------------------------------
615 */
616
617 static char *
EnvTraceProc(TCL_UNUSED (ClientData),Tcl_Interp * interp,const char * name1,const char * name2,int flags)618 EnvTraceProc(
619 TCL_UNUSED(ClientData),
620 Tcl_Interp *interp, /* Interpreter whose "env" variable is being
621 * modified. */
622 const char *name1, /* Better be "env". */
623 const char *name2, /* Name of variable being modified, or NULL if
624 * whole array is being deleted (UTF-8). */
625 int flags) /* Indicates what's happening. */
626 {
627 /*
628 * For array traces, let TclSetupEnv do all the work.
629 */
630
631 if (flags & TCL_TRACE_ARRAY) {
632 TclSetupEnv(interp);
633 TclEnvEpoch++;
634 return NULL;
635 }
636
637 /*
638 * If name2 is NULL, then return and do nothing.
639 */
640
641 if (name2 == NULL) {
642 return NULL;
643 }
644
645 /*
646 * If a value is being set, call TclSetEnv to do all of the work.
647 */
648
649 if (flags & TCL_TRACE_WRITES) {
650 const char *value;
651
652 value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY);
653 TclSetEnv(name2, value);
654 TclEnvEpoch++;
655 }
656
657 /*
658 * If a value is being read, call TclGetEnv to do all of the work.
659 */
660
661 if (flags & TCL_TRACE_READS) {
662 Tcl_DString valueString;
663 const char *value = TclGetEnv(name2, &valueString);
664
665 if (value == NULL) {
666 return (char *) "no such variable";
667 }
668 Tcl_SetVar2(interp, name1, name2, value, 0);
669 Tcl_DStringFree(&valueString);
670 }
671
672 /*
673 * For unset traces, let TclUnsetEnv do all the work.
674 */
675
676 if (flags & TCL_TRACE_UNSETS) {
677 TclUnsetEnv(name2);
678 TclEnvEpoch++;
679 }
680 return NULL;
681 }
682
683 /*
684 *----------------------------------------------------------------------
685 *
686 * ReplaceString --
687 *
688 * Replace one string with another in the environment variable cache. The
689 * cache keeps track of all of the environment variables that Tcl has
690 * modified so they can be freed later.
691 *
692 * Results:
693 * None.
694 *
695 * Side effects:
696 * May free the old string.
697 *
698 *----------------------------------------------------------------------
699 */
700
701 static void
ReplaceString(const char * oldStr,char * newStr)702 ReplaceString(
703 const char *oldStr, /* Old environment string. */
704 char *newStr) /* New environment string. */
705 {
706 int i;
707
708 /*
709 * Check to see if the old value was allocated by Tcl. If so, it needs to
710 * be deallocated to avoid memory leaks. Note that this algorithm is O(n),
711 * not O(1). This will result in n-squared behavior if lots of environment
712 * changes are being made.
713 */
714
715 for (i = 0; i < env.cacheSize; i++) {
716 if (env.cache[i]==oldStr || env.cache[i]==NULL) {
717 break;
718 }
719 }
720 if (i < env.cacheSize) {
721 /*
722 * Replace or delete the old value.
723 */
724
725 if (env.cache[i]) {
726 ckfree(env.cache[i]);
727 }
728
729 if (newStr) {
730 env.cache[i] = newStr;
731 } else {
732 for (; i < env.cacheSize-1; i++) {
733 env.cache[i] = env.cache[i+1];
734 }
735 env.cache[env.cacheSize-1] = NULL;
736 }
737 } else {
738 /*
739 * We need to grow the cache in order to hold the new string.
740 */
741
742 const int growth = 5;
743
744 env.cache = (char **)ckrealloc(env.cache,
745 (env.cacheSize + growth) * sizeof(char *));
746 env.cache[env.cacheSize] = newStr;
747 (void) memset(env.cache+env.cacheSize+1, 0,
748 (size_t) (growth-1) * sizeof(char *));
749 env.cacheSize += growth;
750 }
751 }
752
753 /*
754 *----------------------------------------------------------------------
755 *
756 * TclFinalizeEnvironment --
757 *
758 * This function releases any storage allocated by this module that isn't
759 * still in use by the global environment. Any strings that are still in
760 * the environment will be leaked.
761 *
762 * Results:
763 * None.
764 *
765 * Side effects:
766 * May deallocate storage.
767 *
768 *----------------------------------------------------------------------
769 */
770
771 void
TclFinalizeEnvironment(void)772 TclFinalizeEnvironment(void)
773 {
774 /*
775 * For now we just deallocate the cache array and none of the environment
776 * strings. This may leak more memory that strictly necessary, since some
777 * of the strings may no longer be in the environment. However,
778 * determining which ones are ok to delete is n-squared, and is pretty
779 * unlikely, so we don't bother. However, in the case of DPURIFY, just
780 * free all strings in the cache.
781 */
782
783 if (env.cache) {
784 #ifdef PURIFY
785 int i;
786 for (i = 0; i < env.cacheSize; i++) {
787 ckfree(env.cache[i]);
788 }
789 #endif
790 ckfree(env.cache);
791 env.cache = NULL;
792 env.cacheSize = 0;
793 #ifndef USE_PUTENV
794 if ((env.ourEnviron != NULL)) {
795 ckfree(env.ourEnviron);
796 env.ourEnviron = NULL;
797 }
798 env.ourEnvironSize = 0;
799 #endif
800 }
801 }
802
803 /*
804 * Local Variables:
805 * mode: c
806 * c-basic-offset: 4
807 * fill-column: 78
808 * End:
809 */
810