Deleted Added
full compact
getenv.c (171195) getenv.c (171525)
1/*-
2 * Copyright (c) 2007 Sean C. Farley <scf@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright

--- 19 unchanged lines hidden (view full) ---

28#include <errno.h>
29#include <stdbool.h>
30#include <stddef.h>
31#include <stdlib.h>
32#include <string.h>
33
34
35#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 2007 Sean C. Farley <scf@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright

--- 19 unchanged lines hidden (view full) ---

28#include <errno.h>
29#include <stdbool.h>
30#include <stddef.h>
31#include <stdlib.h>
32#include <string.h>
33
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/lib/libc/stdlib/getenv.c 171195 2007-07-04 00:00:41Z scf $");
36__FBSDID("$FreeBSD: head/lib/libc/stdlib/getenv.c 171525 2007-07-20 23:30:13Z scf $");
37
38
37
38
39static const char CorruptEnvFindMsg[] =
40 "environment corrupt; unable to find %.*s";
41static const char CorruptEnvValueMsg[] =
42 "environment corrupt; missing value for %s";
43
44
39/*
40 * Standard environ. environ variable is exposed to entire process.
41 *
42 * origEnviron: Upon cleanup on unloading of library or failure, this
43 * allows environ to return to as it was before.
44 * environSize: Number of variables environ can hold. Can only
45 * increase.
45/*
46 * Standard environ. environ variable is exposed to entire process.
47 *
48 * origEnviron: Upon cleanup on unloading of library or failure, this
49 * allows environ to return to as it was before.
50 * environSize: Number of variables environ can hold. Can only
51 * increase.
52 * intEnviron: Internally-built environ. Exposed via environ during
53 * (re)builds of the environment.
46 */
47extern char **environ;
48static char **origEnviron;
54 */
55extern char **environ;
56static char **origEnviron;
57static char **intEnviron = NULL;
49static int environSize = 0;
50
51/*
52 * Array of environment variables built from environ. Each element records:
53 * name: Pointer to name=value string
54 * name length: Length of name not counting '=' character
55 * value: Pointer to value within same string as name
56 * value size: Size (not length) of space for value not counting the

--- 22 unchanged lines hidden (view full) ---

79 * envVarsTotal: Number of total variables in array (active or not).
80 */
81static int envActive = 0;
82static int envVarsSize = 0;
83static int envVarsTotal = 0;
84
85
86/* Deinitialization of new environment. */
58static int environSize = 0;
59
60/*
61 * Array of environment variables built from environ. Each element records:
62 * name: Pointer to name=value string
63 * name length: Length of name not counting '=' character
64 * value: Pointer to value within same string as name
65 * value size: Size (not length) of space for value not counting the

--- 22 unchanged lines hidden (view full) ---

88 * envVarsTotal: Number of total variables in array (active or not).
89 */
90static int envActive = 0;
91static int envVarsSize = 0;
92static int envVarsTotal = 0;
93
94
95/* Deinitialization of new environment. */
87static void __attribute__ ((destructor)) __clean_env(void);
96static void __attribute__ ((destructor)) __clean_env_destructor(void);
88
89
90/*
91 * Inline strlen() for performance. Also, perform check for an equals sign.
92 * Cheaper here than peforming a strchr() later.
93 */
94static inline size_t
95__strleneq(const char *str)

--- 72 unchanged lines hidden (view full) ---

168 if (strncmpeq(environ[envNdx], name, nameLen))
169 return (&(environ[envNdx][nameLen + sizeof("=") - 1]));
170
171 return (NULL);
172}
173
174
175/*
97
98
99/*
100 * Inline strlen() for performance. Also, perform check for an equals sign.
101 * Cheaper here than peforming a strchr() later.
102 */
103static inline size_t
104__strleneq(const char *str)

--- 72 unchanged lines hidden (view full) ---

177 if (strncmpeq(environ[envNdx], name, nameLen))
178 return (&(environ[envNdx][nameLen + sizeof("=") - 1]));
179
180 return (NULL);
181}
182
183
184/*
185 * Remove variable added by putenv() from variable tracking array.
186 */
187static void
188__remove_putenv(int envNdx)
189{
190 envVarsTotal--;
191 if (envVarsTotal > envNdx)
192 memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
193 (envVarsTotal - envNdx) * sizeof (*envVars));
194 memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars));
195
196 return;
197}
198
199
200/*
201 * Deallocate the environment built from environ as well as environ then set
202 * both to NULL. Eases debugging of memory leaks.
203 */
204static void
205__clean_env(bool freeVars)
206{
207 int envNdx;
208
209 /* Deallocate environment and environ if created by *env(). */
210 if (envVars != NULL) {
211 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--)
212 /* Free variables or deactivate them. */
213 if (envVars[envNdx].putenv) {
214 if (!freeVars)
215 __remove_putenv(envNdx);
216 } else {
217 if (freeVars)
218 free(envVars[envNdx].name);
219 else
220 envVars[envNdx].active = false;
221 }
222 if (freeVars) {
223 free(envVars);
224 envVars = NULL;
225 } else
226 envActive = 0;
227
228 /* Restore original environ if it has not updated by program. */
229 if (origEnviron != NULL) {
230 if (environ == intEnviron)
231 environ = origEnviron;
232 free(intEnviron);
233 intEnviron = NULL;
234 environSize = 0;
235 }
236 }
237
238 return;
239}
240
241
242/*
176 * Using the environment, rebuild the environ array for use by other C library
177 * calls that depend upon it.
178 */
179static int
180__rebuild_environ(int newEnvironSize)
181{
182 char **tmpEnviron;
183 int envNdx;
184 int environNdx;
185 int tmpEnvironSize;
186
187 /* Resize environ. */
188 if (newEnvironSize > environSize) {
189 tmpEnvironSize = newEnvironSize * 2;
243 * Using the environment, rebuild the environ array for use by other C library
244 * calls that depend upon it.
245 */
246static int
247__rebuild_environ(int newEnvironSize)
248{
249 char **tmpEnviron;
250 int envNdx;
251 int environNdx;
252 int tmpEnvironSize;
253
254 /* Resize environ. */
255 if (newEnvironSize > environSize) {
256 tmpEnvironSize = newEnvironSize * 2;
190 tmpEnviron = realloc(environ, sizeof (*environ) *
257 tmpEnviron = realloc(intEnviron, sizeof (*intEnviron) *
191 (tmpEnvironSize + 1));
192 if (tmpEnviron == NULL)
193 return (-1);
194 environSize = tmpEnvironSize;
258 (tmpEnvironSize + 1));
259 if (tmpEnviron == NULL)
260 return (-1);
261 environSize = tmpEnvironSize;
195 environ = tmpEnviron;
262 intEnviron = tmpEnviron;
196 }
197 envActive = newEnvironSize;
198
199 /* Assign active variables to environ. */
200 for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)
201 if (envVars[envNdx].active)
263 }
264 envActive = newEnvironSize;
265
266 /* Assign active variables to environ. */
267 for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)
268 if (envVars[envNdx].active)
202 environ[environNdx++] = envVars[envNdx].name;
203 environ[environNdx] = NULL;
269 intEnviron[environNdx++] = envVars[envNdx].name;
270 intEnviron[environNdx] = NULL;
204
271
272 /* Always set environ which may have been replaced by program. */
273 environ = intEnviron;
274
205 return (0);
206}
207
208
209/*
210 * Enlarge new environment.
211 */
212static inline bool

--- 23 unchanged lines hidden (view full) ---

236 * Using environ, build an environment for use by standard C library calls.
237 */
238static int
239__build_env(void)
240{
241 char **env;
242 int activeNdx;
243 int envNdx;
275 return (0);
276}
277
278
279/*
280 * Enlarge new environment.
281 */
282static inline bool

--- 23 unchanged lines hidden (view full) ---

306 * Using environ, build an environment for use by standard C library calls.
307 */
308static int
309__build_env(void)
310{
311 char **env;
312 int activeNdx;
313 int envNdx;
244 int rtrnVal;
245 int savedErrno;
246 size_t nameLen;
247
248 /* Check for non-existant environment. */
314 int savedErrno;
315 size_t nameLen;
316
317 /* Check for non-existant environment. */
249 if (environ == NULL)
318 if (environ == NULL || environ[0] == NULL)
250 return (0);
319 return (0);
251 if (environ[0] == NULL)
252 goto SaveEnviron;
253
254 /* Count environment variables. */
255 for (env = environ, envVarsTotal = 0; *env != NULL; env++)
256 envVarsTotal++;
257 envVarsSize = envVarsTotal * 2;
258
259 /* Create new environment. */
260 envVars = calloc(1, sizeof (*envVars) * envVarsSize);

--- 8 unchanged lines hidden (view full) ---

269 if (envVars[envNdx].name == NULL)
270 goto Failure;
271 envVars[envNdx].value = strchr(envVars[envNdx].name, '=');
272 if (envVars[envNdx].value != NULL) {
273 envVars[envNdx].value++;
274 envVars[envNdx].valueSize =
275 strlen(envVars[envNdx].value);
276 } else {
320
321 /* Count environment variables. */
322 for (env = environ, envVarsTotal = 0; *env != NULL; env++)
323 envVarsTotal++;
324 envVarsSize = envVarsTotal * 2;
325
326 /* Create new environment. */
327 envVars = calloc(1, sizeof (*envVars) * envVarsSize);

--- 8 unchanged lines hidden (view full) ---

336 if (envVars[envNdx].name == NULL)
337 goto Failure;
338 envVars[envNdx].value = strchr(envVars[envNdx].name, '=');
339 if (envVars[envNdx].value != NULL) {
340 envVars[envNdx].value++;
341 envVars[envNdx].valueSize =
342 strlen(envVars[envNdx].value);
343 } else {
277 warnx("environment corrupt; missing value for %s",
278 envVars[envNdx].name);
344 warnx(CorruptEnvValueMsg, envVars[envNdx].name);
279 errno = EFAULT;
280 goto Failure;
281 }
282
283 /*
284 * Find most current version of variable to make active. This
285 * will prevent multiple active variables from being created
286 * during this initialization phase.
287 */
288 nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;
289 envVars[envNdx].nameLen = nameLen;
290 activeNdx = envVarsTotal - 1;
291 if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,
292 false) == NULL) {
345 errno = EFAULT;
346 goto Failure;
347 }
348
349 /*
350 * Find most current version of variable to make active. This
351 * will prevent multiple active variables from being created
352 * during this initialization phase.
353 */
354 nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;
355 envVars[envNdx].nameLen = nameLen;
356 activeNdx = envVarsTotal - 1;
357 if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,
358 false) == NULL) {
293 warnx("environment corrupt; unable to find %.*s",
294 nameLen, envVars[envNdx].name);
359 warnx(CorruptEnvFindMsg, nameLen, envVars[envNdx].name);
295 errno = EFAULT;
296 goto Failure;
297 }
298 envVars[activeNdx].active = true;
299 }
300
301 /* Create a new environ. */
360 errno = EFAULT;
361 goto Failure;
362 }
363 envVars[activeNdx].active = true;
364 }
365
366 /* Create a new environ. */
302SaveEnviron:
303 origEnviron = environ;
304 environ = NULL;
367 origEnviron = environ;
368 environ = NULL;
305 if (envVarsTotal > 0) {
306 rtrnVal = __rebuild_environ(envVarsTotal);
307 if (rtrnVal == -1) {
308 savedErrno = errno;
309 __clean_env();
310 errno = savedErrno;
311 }
312 } else
313 rtrnVal = 0;
369 if (__rebuild_environ(envVarsTotal) == 0)
370 return (0);
314
371
315 return (rtrnVal);
316
317Failure:
318 savedErrno = errno;
372Failure:
373 savedErrno = errno;
319 __clean_env();
374 __clean_env(true);
320 errno = savedErrno;
321
322 return (-1);
323}
324
325
326/*
375 errno = savedErrno;
376
377 return (-1);
378}
379
380
381/*
327 * Remove variable added by putenv() from variable tracking array.
382 * Destructor function with default argument to __clean_env().
328 */
329static void
383 */
384static void
330__remove_putenv(int envNdx)
385__clean_env_destructor(void)
331{
386{
332 memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
333 (envVarsTotal - envNdx) * sizeof (*envVars));
334 envVarsTotal--;
387 __clean_env(true);
335
336 return;
337}
338
339
340/*
388
389 return;
390}
391
392
393/*
341 * Deallocate the environment built from environ as well as environ then set
342 * both to NULL. Eases debugging of memory leaks.
343 */
344static void
345__clean_env(void)
346{
347 int envNdx;
348
349 /* Deallocate environment and environ if created by *env(). */
350 if (envVars != NULL) {
351 for (envNdx = 0; envNdx < envVarsTotal; envNdx++)
352 if (!envVars[envNdx].putenv)
353 free(envVars[envNdx].name);
354 free(envVars);
355 envVars = NULL;
356
357 /* Restore original environ. */
358 if (origEnviron != NULL) {
359 free(environ);
360 environ = origEnviron;
361 }
362 }
363
364 return;
365}
366
367
368/*
369 * Returns the value of a variable or NULL if none are found.
370 */
371char *
372getenv(const char *name)
373{
374 int envNdx;
375 size_t nameLen;
376
377 /* Check for malformed name. */
378 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
379 errno = EINVAL;
380 return (NULL);
381 }
382
394 * Returns the value of a variable or NULL if none are found.
395 */
396char *
397getenv(const char *name)
398{
399 int envNdx;
400 size_t nameLen;
401
402 /* Check for malformed name. */
403 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
404 errno = EINVAL;
405 return (NULL);
406 }
407
383 /* Find environment variable via environ or rebuilt environment. */
384 if (envVars == NULL)
408 /*
409 * Find environment variable via environ if no changes have been made
410 * via a *env() call or environ has been replaced by a running program,
411 * otherwise, use the rebuilt environment.
412 */
413 if (envVars == NULL || environ != intEnviron)
385 return (__findenv_environ(name, nameLen));
386 else {
387 envNdx = envVarsTotal - 1;
388 return (__findenv(name, nameLen, &envNdx, true));
389 }
390}
391
392
393/*
394 * Set the value of a variable. Older settings are labeled as inactive. If an
395 * older setting has enough room to store the new value, it will be reused. No
396 * previous variables are ever freed here to avoid causing a segmentation fault
397 * in a user's code.
414 return (__findenv_environ(name, nameLen));
415 else {
416 envNdx = envVarsTotal - 1;
417 return (__findenv(name, nameLen, &envNdx, true));
418 }
419}
420
421
422/*
423 * Set the value of a variable. Older settings are labeled as inactive. If an
424 * older setting has enough room to store the new value, it will be reused. No
425 * previous variables are ever freed here to avoid causing a segmentation fault
426 * in a user's code.
427 *
428 * The variables nameLen and valueLen are passed into here to allow the caller
429 * to calculate the length by means besides just strlen().
398 */
430 */
399int
400setenv(const char *name, const char *value, int overwrite)
431static int
432__setenv(const char *name, size_t nameLen, const char *value, int overwrite)
401{
402 bool reuse;
403 char *env;
404 int envNdx;
405 int newEnvActive;
433{
434 bool reuse;
435 char *env;
436 int envNdx;
437 int newEnvActive;
406 size_t nameLen;
407 size_t valueLen;
408
438 size_t valueLen;
439
409 /* Check for malformed name. */
410 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
411 errno = EINVAL;
412 return (-1);
413 }
414
415 /* Initialize environment. */
416 if (envVars == NULL && __build_env() == -1)
417 return (-1);
418
419 /* Find existing environment variable large enough to use. */
420 envNdx = envVarsTotal - 1;
421 newEnvActive = envActive;
422 valueLen = strlen(value);
423 reuse = false;
424 if (__findenv(name, nameLen, &envNdx, false) != NULL) {
425 /* Deactivate entry if overwrite is allowed. */
426 if (envVars[envNdx].active) {

--- 5 unchanged lines hidden (view full) ---

432
433 /* putenv() created variable cannot be reused. */
434 if (envVars[envNdx].putenv)
435 __remove_putenv(envNdx);
436
437 /* Entry is large enough to reuse. */
438 else if (envVars[envNdx].valueSize >= valueLen)
439 reuse = true;
440 /* Find existing environment variable large enough to use. */
441 envNdx = envVarsTotal - 1;
442 newEnvActive = envActive;
443 valueLen = strlen(value);
444 reuse = false;
445 if (__findenv(name, nameLen, &envNdx, false) != NULL) {
446 /* Deactivate entry if overwrite is allowed. */
447 if (envVars[envNdx].active) {

--- 5 unchanged lines hidden (view full) ---

453
454 /* putenv() created variable cannot be reused. */
455 if (envVars[envNdx].putenv)
456 __remove_putenv(envNdx);
457
458 /* Entry is large enough to reuse. */
459 else if (envVars[envNdx].valueSize >= valueLen)
460 reuse = true;
440
441 }
442
443 /* Create new variable if none was found of sufficient size. */
444 if (! reuse) {
445 /* Enlarge environment. */
446 envNdx = envVarsTotal;
447 if (!__enlarge_env())
448 return (-1);

--- 26 unchanged lines hidden (view full) ---

475 if (reuse)
476 return (0);
477 else
478 return (__rebuild_environ(newEnvActive));
479}
480
481
482/*
461 }
462
463 /* Create new variable if none was found of sufficient size. */
464 if (! reuse) {
465 /* Enlarge environment. */
466 envNdx = envVarsTotal;
467 if (!__enlarge_env())
468 return (-1);

--- 26 unchanged lines hidden (view full) ---

495 if (reuse)
496 return (0);
497 else
498 return (__rebuild_environ(newEnvActive));
499}
500
501
502/*
483 * Insert a "name=value" string into then environment. Special settings must be
503 * If the program attempts to replace the array of environment variables
504 * (environ) environ, then deactivate all variables and merge in the new list
505 * from environ.
506 */
507static int
508__merge_environ(void)
509{
510 char **env;
511 char *equals;
512
513 /* environ has been replaced. clean up everything. */
514 if (envVarsTotal > 0 && environ != intEnviron) {
515 /* Deactivate all environment variables. */
516 if (envActive > 0) {
517 origEnviron = NULL;
518 __clean_env(false);
519 }
520
521 /*
522 * Insert new environ into existing, yet deactivated,
523 * environment array.
524 */
525 origEnviron = environ;
526 if (origEnviron != NULL)
527 for (env = origEnviron; *env != NULL; env++) {
528 if ((equals = strchr(*env, '=')) == NULL) {
529 warnx(CorruptEnvValueMsg, *env);
530 errno = EFAULT;
531 return (-1);
532 }
533 if (__setenv(*env, equals - *env, equals + 1,
534 1) == -1)
535 return (-1);
536 }
537 }
538
539 return (0);
540}
541
542
543/*
544 * The exposed setenv() that peforms a few tests before calling the function
545 * (__setenv()) that does the actual work of inserting a variable into the
546 * environment.
547 */
548int
549setenv(const char *name, const char *value, int overwrite)
550{
551 size_t nameLen;
552
553 /* Check for malformed name. */
554 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
555 errno = EINVAL;
556 return (-1);
557 }
558
559 /* Initialize environment. */
560 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
561 return (-1);
562
563 return (__setenv(name, nameLen, value, overwrite));
564}
565
566
567/*
568 * Insert a "name=value" string into the environment. Special settings must be
484 * made to keep setenv() from reusing this memory block and unsetenv() from
485 * allowing it to be tracked.
486 */
487int
488putenv(char *string)
489{
490 char *equals;
491 int envNdx;
492 int newEnvActive;
493 size_t nameLen;
494
495 /* Check for malformed argument. */
496 if (string == NULL || (equals = strchr(string, '=')) == NULL ||
497 (nameLen = equals - string) == 0) {
498 errno = EINVAL;
499 return (-1);
500 }
501
502 /* Initialize environment. */
569 * made to keep setenv() from reusing this memory block and unsetenv() from
570 * allowing it to be tracked.
571 */
572int
573putenv(char *string)
574{
575 char *equals;
576 int envNdx;
577 int newEnvActive;
578 size_t nameLen;
579
580 /* Check for malformed argument. */
581 if (string == NULL || (equals = strchr(string, '=')) == NULL ||
582 (nameLen = equals - string) == 0) {
583 errno = EINVAL;
584 return (-1);
585 }
586
587 /* Initialize environment. */
503 if (envVars == NULL && __build_env() == -1)
588 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
504 return (-1);
505
506 /* Deactivate previous environment variable. */
507 envNdx = envVarsTotal - 1;
508 newEnvActive = envActive;
509 if (__findenv(string, nameLen, &envNdx, true) != NULL) {
510 /* Reuse previous putenv slot. */
511 if (envVars[envNdx].putenv) {

--- 35 unchanged lines hidden (view full) ---

547
548 /* Check for malformed name. */
549 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
550 errno = EINVAL;
551 return (-1);
552 }
553
554 /* Initialize environment. */
589 return (-1);
590
591 /* Deactivate previous environment variable. */
592 envNdx = envVarsTotal - 1;
593 newEnvActive = envActive;
594 if (__findenv(string, nameLen, &envNdx, true) != NULL) {
595 /* Reuse previous putenv slot. */
596 if (envVars[envNdx].putenv) {

--- 35 unchanged lines hidden (view full) ---

632
633 /* Check for malformed name. */
634 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
635 errno = EINVAL;
636 return (-1);
637 }
638
639 /* Initialize environment. */
555 if (envVars == NULL && __build_env() == -1)
640 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
556 return (-1);
557
558 /* Deactivate specified variable. */
559 envNdx = envVarsTotal - 1;
560 if (__findenv(name, nameLen, &envNdx, true) != NULL) {
561 envVars[envNdx].active = false;
562 if (envVars[envNdx].putenv)
563 __remove_putenv(envNdx);
564 __rebuild_environ(envActive - 1);
565 }
566
567 return (0);
568}
641 return (-1);
642
643 /* Deactivate specified variable. */
644 envNdx = envVarsTotal - 1;
645 if (__findenv(name, nameLen, &envNdx, true) != NULL) {
646 envVars[envNdx].active = false;
647 if (envVars[envNdx].putenv)
648 __remove_putenv(envNdx);
649 __rebuild_environ(envActive - 1);
650 }
651
652 return (0);
653}