]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * tclEnv.c -- | |
3 | * | |
4 | * Tcl support for environment variables, including a setenv | |
5 | * procedure. | |
6 | * | |
7 | * Copyright 1991 Regents of the University of California | |
8 | * Permission to use, copy, modify, and distribute this | |
9 | * software and its documentation for any purpose and without | |
10 | * fee is hereby granted, provided that this copyright | |
11 | * notice appears in all copies. The University of California | |
12 | * makes no representations about the suitability of this | |
13 | * software for any purpose. It is provided "as is" without | |
14 | * express or implied warranty. | |
15 | */ | |
16 | ||
17 | #ifndef lint | |
18 | static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclEnv.c,v 1.7 91/09/23 11:22:21 ouster Exp $ SPRITE (Berkeley)"; | |
19 | #endif /* not lint */ | |
20 | ||
21 | #include "tclint.h" | |
22 | #include "tclunix.h" | |
23 | ||
24 | /* | |
25 | * The structure below is used to keep track of all of the interpereters | |
26 | * for which we're managing the "env" array. It's needed so that they | |
27 | * can all be updated whenever an environment variable is changed | |
28 | * anywhere. | |
29 | */ | |
30 | ||
31 | typedef struct EnvInterp { | |
32 | Tcl_Interp *interp; /* Interpreter for which we're managing | |
33 | * the env array. */ | |
34 | struct EnvInterp *nextPtr; /* Next in list of all such interpreters, | |
35 | * or zero. */ | |
36 | } EnvInterp; | |
37 | ||
38 | static EnvInterp *firstInterpPtr; | |
39 | /* First in list of all managed interpreters, | |
40 | * or NULL if none. */ | |
41 | ||
42 | static int environSize = 0; /* Non-zero means that the all of the | |
43 | * environ-related information is malloc-ed | |
44 | * and the environ array itself has this | |
45 | * many total entries allocated to it (not | |
46 | * all may be in use at once). Zero means | |
47 | * that the environment array is in its | |
48 | * original static state. */ | |
49 | ||
50 | /* | |
51 | * Declarations for local procedures defined in this file: | |
52 | */ | |
53 | ||
54 | static void EnvInit _ANSI_ARGS_((void)); | |
55 | static char * EnvTraceProc _ANSI_ARGS_((ClientData clientData, | |
56 | Tcl_Interp *interp, char *name1, char *name2, | |
57 | int flags)); | |
58 | static int FindVariable _ANSI_ARGS_((char *name, int *lengthPtr)); | |
59 | ||
60 | #ifdef IS_LINUX | |
61 | int setenv _ANSI_ARGS_((const char *name, const char *value, int replace)); | |
62 | int unsetenv _ANSI_ARGS_((const char *name)); | |
63 | #else | |
64 | void setenv _ANSI_ARGS_((char *name, char *value)); | |
65 | void unsetenv _ANSI_ARGS_((char *name)); | |
66 | #endif | |
67 | ||
68 | \f | |
69 | /* | |
70 | *---------------------------------------------------------------------- | |
71 | * | |
72 | * TclSetupEnv -- | |
73 | * | |
74 | * This procedure is invoked for an interpreter to make environment | |
75 | * variables accessible from that interpreter via the "env" | |
76 | * associative array. | |
77 | * | |
78 | * Results: | |
79 | * None. | |
80 | * | |
81 | * Side effects: | |
82 | * The interpreter is added to a list of interpreters managed | |
83 | * by us, so that its view of envariables can be kept consistent | |
84 | * with the view in other interpreters. If this is the first | |
85 | * call to Tcl_SetupEnv, then additional initialization happens, | |
86 | * such as copying the environment to dynamically-allocated space | |
87 | * for ease of management. | |
88 | * | |
89 | *---------------------------------------------------------------------- | |
90 | */ | |
91 | ||
92 | void | |
93 | TclSetupEnv(interp) | |
94 | Tcl_Interp *interp; /* Interpreter whose "env" array is to be | |
95 | * managed. */ | |
96 | { | |
97 | EnvInterp *eiPtr; | |
98 | int i; | |
99 | ||
100 | /* | |
101 | * First, initialize our environment-related information, if | |
102 | * necessary. | |
103 | */ | |
104 | ||
105 | if (environSize == 0) { | |
106 | EnvInit(); | |
107 | } | |
108 | ||
109 | /* | |
110 | * Next, add the interpreter to the list of those that we manage. | |
111 | */ | |
112 | ||
113 | eiPtr = (EnvInterp *) ckalloc(sizeof(EnvInterp)); | |
114 | eiPtr->interp = interp; | |
115 | eiPtr->nextPtr = firstInterpPtr; | |
116 | firstInterpPtr = eiPtr; | |
117 | ||
118 | /* | |
119 | * Store the environment variable values into the interpreter's | |
120 | * "env" array, and arrange for us to be notified on future | |
121 | * writes and unsets to that array. | |
122 | */ | |
123 | ||
124 | (void) Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY); | |
125 | for (i = 0; ; i++) { | |
126 | char *p, *p2; | |
127 | ||
128 | p = environ[i]; | |
129 | if (p == NULL) { | |
130 | break; | |
131 | } | |
132 | for (p2 = p; *p2 != '='; p2++) { | |
133 | /* Empty loop body. */ | |
134 | } | |
135 | *p2 = 0; | |
136 | (void) Tcl_SetVar2(interp, "env", p, p2+1, TCL_GLOBAL_ONLY); | |
137 | *p2 = '='; | |
138 | } | |
139 | Tcl_TraceVar2(interp, "env", (char *) NULL, | |
140 | TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS, | |
141 | EnvTraceProc, (ClientData) NULL); | |
142 | } | |
143 | \f | |
144 | /* | |
145 | *---------------------------------------------------------------------- | |
146 | * | |
147 | * FindVariable -- | |
148 | * | |
149 | * Locate the entry in environ for a given name. | |
150 | * | |
151 | * Results: | |
152 | * The return value is the index in environ of an entry with the | |
153 | * name "name", or -1 if there is no such entry. The integer at | |
154 | * *lengthPtr is filled in with the length of name (if a matching | |
155 | * entry is found) or the length of the environ array (if no matching | |
156 | * entry is found). | |
157 | * | |
158 | * Side effects: | |
159 | * None. | |
160 | * | |
161 | *---------------------------------------------------------------------- | |
162 | */ | |
163 | ||
164 | static int | |
165 | FindVariable(name, lengthPtr) | |
166 | char *name; /* Name of desired environment variable. */ | |
167 | int *lengthPtr; /* Used to return length of name (for | |
168 | * successful searches) or number of non-NULL | |
169 | * entries in environ (for unsuccessful | |
170 | * searches). */ | |
171 | { | |
172 | int i; | |
173 | register char *p1, *p2; | |
174 | ||
175 | for (i = 0, p1 = environ[i]; p1 != NULL; i++, p1 = environ[i]) { | |
176 | for (p2 = name; *p2 == *p1; p1++, p2++) { | |
177 | /* NULL loop body. */ | |
178 | } | |
179 | if ((*p1 == '=') && (*p2 == '\0')) { | |
180 | *lengthPtr = p2-name; | |
181 | return i; | |
182 | } | |
183 | } | |
184 | *lengthPtr = i; | |
185 | return -1; | |
186 | } | |
187 | \f | |
188 | /* | |
189 | *---------------------------------------------------------------------- | |
190 | * | |
191 | * setenv -- | |
192 | * | |
193 | * Set an environment variable, replacing an existing value | |
194 | * or creating a new variable if there doesn't exist a variable | |
195 | * by the given name. | |
196 | * | |
197 | * Results: | |
198 | * None. | |
199 | * | |
200 | * Side effects: | |
201 | * The environ array gets updated, as do all of the interpreters | |
202 | * that we manage. | |
203 | * | |
204 | *---------------------------------------------------------------------- | |
205 | */ | |
206 | ||
207 | #ifdef IS_LINUX | |
208 | int | |
209 | setenv(name, value, replace) | |
210 | const char *name; /* Name of variable whose value is to be | |
211 | * set. */ | |
212 | const char *value; /* New value for variable. */ | |
213 | int replace; | |
214 | #else | |
215 | void | |
216 | setenv(name, value) | |
217 | char *name; /* Name of variable whose value is to be | |
218 | * set. */ | |
219 | char *value; /* New value for variable. */ | |
220 | #endif | |
221 | { | |
222 | int index, length, nameLength; | |
223 | char *p; | |
224 | EnvInterp *eiPtr; | |
225 | ||
226 | if (environSize == 0) { | |
227 | EnvInit(); | |
228 | } | |
229 | ||
230 | /* | |
231 | * Figure out where the entry is going to go. If the name doesn't | |
232 | * already exist, enlarge the array if necessary to make room. If | |
233 | * the name exists, free its old entry. | |
234 | */ | |
235 | ||
236 | index = FindVariable((char *)name, &length); | |
237 | if (index == -1) { | |
238 | if ((length+2) > environSize) { | |
239 | char **newEnviron; | |
240 | ||
241 | newEnviron = (char **) ckalloc((unsigned) | |
242 | ((length+5) * sizeof(char *))); | |
243 | memcpy((VOID *) newEnviron, (VOID *) environ, | |
244 | length*sizeof(char *)); | |
245 | ckfree((char *) environ); | |
246 | environ = newEnviron; | |
247 | environSize = length+5; | |
248 | } | |
249 | index = length; | |
250 | environ[index+1] = NULL; | |
251 | nameLength = strlen(name); | |
252 | } else { | |
253 | ckfree(environ[index]); | |
254 | nameLength = length; | |
255 | } | |
256 | ||
257 | /* | |
258 | * Create a new entry and enter it into the table. | |
259 | */ | |
260 | ||
261 | p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2)); | |
262 | environ[index] = p; | |
263 | strcpy(p, name); | |
264 | p += nameLength; | |
265 | *p = '='; | |
266 | strcpy(p+1, value); | |
267 | ||
268 | /* | |
269 | * Update all of the interpreters. | |
270 | */ | |
271 | ||
272 | for (eiPtr= firstInterpPtr; eiPtr != NULL; eiPtr = eiPtr->nextPtr) { | |
273 | (void) Tcl_SetVar2(eiPtr->interp, "env", (char *)name, p+1, TCL_GLOBAL_ONLY); | |
274 | } | |
275 | ||
276 | #ifdef IS_LINUX | |
277 | return 0; | |
278 | #endif | |
279 | ||
280 | } | |
281 | \f | |
282 | /* | |
283 | *---------------------------------------------------------------------- | |
284 | * | |
285 | * unsetenv -- | |
286 | * | |
287 | * Remove an environment variable, updating the "env" arrays | |
288 | * in all interpreters managed by us. | |
289 | * | |
290 | * Results: | |
291 | * None. | |
292 | * | |
293 | * Side effects: | |
294 | * Interpreters are updated, as is environ. | |
295 | * | |
296 | *---------------------------------------------------------------------- | |
297 | */ | |
298 | ||
299 | int | |
300 | unsetenv(name) | |
301 | #ifdef IS_LINUX | |
302 | const char *name; /* Name of variable to remove. */ | |
303 | #else | |
304 | char *name; /* Name of variable to remove. */ | |
305 | #endif | |
306 | { | |
307 | int index, dummy; | |
308 | char **envPtr; | |
309 | EnvInterp *eiPtr; | |
310 | ||
311 | if (environSize == 0) { | |
312 | EnvInit(); | |
313 | } | |
314 | ||
315 | /* | |
316 | * Update the environ array. | |
317 | */ | |
318 | ||
319 | index = FindVariable((char *)name, &dummy); | |
320 | if (index == -1) { | |
321 | return; | |
322 | } | |
323 | ckfree(environ[index]); | |
324 | for (envPtr = environ+index+1; ; envPtr++) { | |
325 | envPtr[-1] = *envPtr; | |
326 | if (*envPtr == NULL) { | |
327 | break; | |
328 | } | |
329 | } | |
330 | ||
331 | /* | |
332 | * Update all of the interpreters. | |
333 | */ | |
334 | ||
335 | for (eiPtr = firstInterpPtr; eiPtr != NULL; eiPtr = eiPtr->nextPtr) { | |
336 | (void) Tcl_UnsetVar2(eiPtr->interp, "env", (char *)name, TCL_GLOBAL_ONLY); | |
337 | } | |
338 | } | |
339 | \f | |
340 | /* | |
341 | *---------------------------------------------------------------------- | |
342 | * | |
343 | * EnvTraceProc -- | |
344 | * | |
345 | * This procedure is invoked whenever an environment variable | |
346 | * is modified or deleted. It propagates the change to the | |
347 | * "environ" array and to any other interpreters for whom | |
348 | * we're managing an "env" array. | |
349 | * | |
350 | * Results: | |
351 | * Always returns NULL to indicate success. | |
352 | * | |
353 | * Side effects: | |
354 | * Environment variable changes get propagated. If the whole | |
355 | * "env" array is deleted, then we stop managing things for | |
356 | * this interpreter (usually this happens because the whole | |
357 | * interpreter is being deleted). | |
358 | * | |
359 | *---------------------------------------------------------------------- | |
360 | */ | |
361 | ||
362 | /* ARGSUSED */ | |
363 | static char * | |
364 | EnvTraceProc(clientData, interp, name1, name2, flags) | |
365 | ClientData clientData; /* Not used. */ | |
366 | Tcl_Interp *interp; /* Interpreter whose "env" variable is | |
367 | * being modified. */ | |
368 | char *name1; /* Better be "env". */ | |
369 | char *name2; /* Name of variable being modified, or | |
370 | * NULL if whole array is being deleted. */ | |
371 | int flags; /* Indicates what's happening. */ | |
372 | { | |
373 | /* | |
374 | * First see if the whole "env" variable is being deleted. If | |
375 | * so, just forget about this interpreter. | |
376 | */ | |
377 | ||
378 | if (name2 == NULL) { | |
379 | register EnvInterp *eiPtr, *prevPtr; | |
380 | ||
381 | if ((flags & (TCL_TRACE_UNSETS|TCL_TRACE_DESTROYED)) | |
382 | != (TCL_TRACE_UNSETS|TCL_TRACE_DESTROYED)) { | |
383 | panic("EnvTraceProc called with confusing arguments"); | |
384 | } | |
385 | eiPtr = firstInterpPtr; | |
386 | if (eiPtr->interp == interp) { | |
387 | firstInterpPtr = eiPtr->nextPtr; | |
388 | } else { | |
389 | for (prevPtr = eiPtr, eiPtr = eiPtr->nextPtr; ; | |
390 | prevPtr = eiPtr, eiPtr = eiPtr->nextPtr) { | |
391 | if (eiPtr == NULL) { | |
392 | panic("EnvTraceProc couldn't find interpreter"); | |
393 | } | |
394 | if (eiPtr->interp == interp) { | |
395 | prevPtr->nextPtr = eiPtr->nextPtr; | |
396 | break; | |
397 | } | |
398 | } | |
399 | } | |
400 | ckfree((char *) eiPtr); | |
401 | return NULL; | |
402 | } | |
403 | ||
404 | /* | |
405 | * If a value is being set, call setenv to do all of the work. | |
406 | */ | |
407 | ||
408 | if (flags & TCL_TRACE_WRITES) { | |
409 | setenv(name2, Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY), 0); | |
410 | } | |
411 | ||
412 | if (flags & TCL_TRACE_UNSETS) { | |
413 | unsetenv(name2); | |
414 | } | |
415 | return NULL; | |
416 | } | |
417 | \f | |
418 | /* | |
419 | *---------------------------------------------------------------------- | |
420 | * | |
421 | * EnvInit -- | |
422 | * | |
423 | * This procedure is called to initialize our management | |
424 | * of the environ array. | |
425 | * | |
426 | * Results: | |
427 | * None. | |
428 | * | |
429 | * Side effects: | |
430 | * Environ gets copied to malloc-ed storage, so that in | |
431 | * the future we don't have to worry about which entries | |
432 | * are malloc-ed and which are static. | |
433 | * | |
434 | *---------------------------------------------------------------------- | |
435 | */ | |
436 | ||
437 | static void | |
438 | EnvInit() | |
439 | { | |
440 | char **newEnviron; | |
441 | int i, length; | |
442 | ||
443 | if (environSize != 0) { | |
444 | return; | |
445 | } | |
446 | for (length = 0; environ[length] != NULL; length++) { | |
447 | /* Empty loop body. */ | |
448 | } | |
449 | environSize = length+5; | |
450 | newEnviron = (char **) ckalloc((unsigned) | |
451 | (environSize * sizeof(char *))); | |
452 | for (i = 0; i < length; i++) { | |
453 | newEnviron[i] = (char *) ckalloc((unsigned) (strlen(environ[i]) + 1)); | |
454 | strcpy(newEnviron[i], environ[i]); | |
455 | } | |
456 | newEnviron[length] = NULL; | |
457 | environ = newEnviron; | |
458 | } |