| 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 | } |