/*1* Copyright (c) 1998 Michael Smith.2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526/*27* Manage an environment-like space in which string variables may be stored.28* Provide support for some method-like operations for setting/retrieving29* variables in order to allow some type strength.30*/3132#include "stand.h"3334#include <string.h>3536struct env_var *environ = NULL;3738/*39* Look up (name) and return it's env_var structure.40*/41struct env_var *42env_getenv(const char *name)43{44struct env_var *ev;4546for (ev = environ; ev != NULL; ev = ev->ev_next)47if (!strcmp(ev->ev_name, name))48break;49return (ev);50}5152/*53* Some notes:54*55* If the EV_VOLATILE flag is set, a copy of the variable is made.56* If EV_DYNAMIC is set, the variable has been allocated with57* malloc and ownership transferred to the environment.58* If (value) is NULL, the variable is set but has no value.59*/60int61env_setenv(const char *name, int flags, const void *value,62ev_sethook_t sethook, ev_unsethook_t unsethook)63{64struct env_var *ev, *curr, *last;6566if ((ev = env_getenv(name)) != NULL) {67/*68* If the new value doesn't have NOKENV set, we'll drop the flag69* if it's set on the entry so that the override propagates70* correctly. We do this *before* sending it to the hook in71* case the hook declines to operate on it (e.g., because the72* value matches what was already set) -- we would still want73* the explicitly set value to propagate.74*/75if (!(flags & EV_NOKENV))76ev->ev_flags &= ~EV_NOKENV;7778/*79* If there's a set hook, let it do the work80* (unless we are working for one already).81*/82if ((ev->ev_sethook != NULL) && !(flags & EV_NOHOOK))83return (ev->ev_sethook(ev, flags, value));8485/* If there is data in the variable, discard it. */86if (ev->ev_value != NULL && (ev->ev_flags & EV_DYNAMIC) != 0)87free(ev->ev_value);88ev->ev_value = NULL;89ev->ev_flags &= ~EV_DYNAMIC;90} else {9192/*93* New variable; create and sort into list94*/95ev = malloc(sizeof(struct env_var));96ev->ev_name = strdup(name);97ev->ev_value = NULL;98ev->ev_flags = 0;99/* hooks can only be set when the variable is instantiated */100ev->ev_sethook = sethook;101ev->ev_unsethook = unsethook;102103/* Sort into list */104ev->ev_prev = NULL;105ev->ev_next = NULL;106/* Search for the record to insert before */107for (last = NULL, curr = environ; curr != NULL;108last = curr, curr = curr->ev_next) {109110if (strcmp(ev->ev_name, curr->ev_name) < 0) {111if (curr->ev_prev) {112curr->ev_prev->ev_next = ev;113} else {114environ = ev;115}116ev->ev_next = curr;117ev->ev_prev = curr->ev_prev;118curr->ev_prev = ev;119break;120}121}122if (curr == NULL) {123if (last == NULL) {124environ = ev;125} else {126last->ev_next = ev;127ev->ev_prev = last;128}129}130}131132/* If we have a new value, use it */133if (flags & EV_VOLATILE) {134ev->ev_value = strdup(value);135flags |= EV_DYNAMIC;136} else {137ev->ev_value = (char *)value;138}139140ev->ev_flags |= flags & (EV_DYNAMIC | EV_NOKENV);141142return (0);143}144145/* coverity[ -tainted_string_return_content ] */146char *147getenv(const char *name)148{149struct env_var *ev;150151/* Set but no value gives empty string */152if ((ev = env_getenv(name)) != NULL) {153if (ev->ev_value != NULL)154return (ev->ev_value);155return ("");156}157return (NULL);158}159160int161setenv(const char *name, const char *value, int overwrite)162{163/* No guarantees about state, always assume volatile */164if (overwrite || (env_getenv(name) == NULL))165return (env_setenv(name, EV_VOLATILE, value, NULL, NULL));166return (0);167}168169int170putenv(char *string)171{172char *value, *copy;173int result;174175copy = strdup(string);176if ((value = strchr(copy, '=')) != NULL)177*(value++) = 0;178result = setenv(copy, value, 1);179free(copy);180return (result);181}182183int184unsetenv(const char *name)185{186struct env_var *ev;187int err;188189err = 0;190if ((ev = env_getenv(name)) == NULL) {191err = ENOENT;192} else {193if (ev->ev_unsethook != NULL)194err = ev->ev_unsethook(ev);195if (err == 0) {196env_discard(ev);197}198}199return (err);200}201202void203env_discard(struct env_var *ev)204{205if (ev->ev_prev)206ev->ev_prev->ev_next = ev->ev_next;207if (ev->ev_next)208ev->ev_next->ev_prev = ev->ev_prev;209if (environ == ev)210environ = ev->ev_next;211free(ev->ev_name);212if (ev->ev_value != NULL && (ev->ev_flags & EV_DYNAMIC) != 0)213free(ev->ev_value);214free(ev);215}216217int218env_noset(struct env_var *ev __unused, int flags __unused,219const void *value __unused)220{221return (EPERM);222}223224int225env_nounset(struct env_var *ev __unused)226{227return (EPERM);228}229230231