1/*
2 * Copyright (c) 1998 Michael Smith.
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
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30/*
31 * Manage an environment-like space in which string variables may be stored.
32 * Provide support for some method-like operations for setting/retrieving
33 * variables in order to allow some type strength.
34 */
35
36#include "stand.h"
37
38#include <string.h>
39
40static void	env_discard(struct env_var *ev);
41
42struct env_var	*environ = NULL;
43
44/*
45 * Look up (name) and return it's env_var structure.
46 */
47struct env_var	*
48env_getenv(const char *name)
49{
50    struct env_var	*ev;
51
52    for (ev = environ; ev != NULL; ev = ev->ev_next)
53	if (!strcmp(ev->ev_name, name))
54	    break;
55    return(ev);
56}
57
58/*
59 * Some notes:
60 *
61 * If the EV_VOLATILE flag is set, a copy of the variable is made.
62 * If EV_DYNAMIC is set, the variable has been allocated with
63 * malloc and ownership transferred to the environment.
64 * If (value) is NULL, the variable is set but has no value.
65 */
66int
67env_setenv(const char *name, int flags, const void *value,
68	   ev_sethook_t sethook, ev_unsethook_t unsethook)
69{
70    struct env_var	*ev, *curr, *last;
71
72    if ((ev = env_getenv(name)) != NULL) {
73	/*
74	 * If there's a set hook, let it do the work (unless we are working
75	 * for one already.
76	 */
77	if ((ev->ev_sethook != NULL) && !(flags & EV_NOHOOK))
78	    return (ev->ev_sethook(ev, flags, value));
79
80	/* If there is data in the variable, discard it. */
81	if (ev->ev_value != NULL && (ev->ev_flags & EV_DYNAMIC) != 0)
82	    free(ev->ev_value);
83	ev->ev_value = NULL;
84	ev->ev_flags &= ~EV_DYNAMIC;
85
86    } else {
87
88	/*
89	 * New variable; create and sort into list
90	 */
91	ev = malloc(sizeof(struct env_var));
92	ev->ev_name = strdup(name);
93	ev->ev_value = NULL;
94	ev->ev_flags = 0;
95	/* hooks can only be set when the variable is instantiated */
96	ev->ev_sethook = sethook;
97	ev->ev_unsethook = unsethook;
98
99	/* Sort into list */
100	ev->ev_prev = NULL;
101	ev->ev_next = NULL;
102	/* Search for the record to insert before */
103	for (last = NULL, curr = environ;
104	     curr != NULL;
105	     last = curr, curr = curr->ev_next) {
106
107	    if (strcmp(ev->ev_name, curr->ev_name) < 0) {
108		if (curr->ev_prev) {
109		    curr->ev_prev->ev_next = ev;
110		} else {
111		    environ = ev;
112		}
113		ev->ev_next = curr;
114		ev->ev_prev = curr->ev_prev;
115		curr->ev_prev = ev;
116		break;
117	    }
118	}
119	if (curr == NULL) {
120	    if (last == NULL) {
121		environ = ev;
122	    } else {
123		last->ev_next = ev;
124		ev->ev_prev = last;
125	    }
126	}
127    }
128
129    /* If we have a new value, use it */
130    if (flags & EV_VOLATILE) {
131	ev->ev_value = strdup(value);
132	ev->ev_flags |= EV_DYNAMIC;
133    } else {
134	ev->ev_value = (char *)value;
135	ev->ev_flags |= flags & EV_DYNAMIC;
136    }
137
138    return(0);
139}
140
141/* coverity[ -tainted_string_return_content ] */
142char *
143getenv(const char *name)
144{
145    struct env_var	*ev;
146
147    /* Set but no value gives empty string */
148    if ((ev = env_getenv(name)) != NULL) {
149	if (ev->ev_value != NULL)
150	    return(ev->ev_value);
151	return("");
152    }
153    return(NULL);
154}
155
156int
157setenv(const char *name, const char *value, int overwrite)
158{
159    /* No guarantees about state, always assume volatile */
160    if (overwrite || (env_getenv(name) == NULL))
161	return(env_setenv(name, EV_VOLATILE, value, NULL, NULL));
162    return(0);
163}
164
165int
166putenv(char *string)
167{
168    char	*value, *copy;
169    int		result;
170
171    copy = strdup(string);
172    if ((value = strchr(copy, '=')) != NULL)
173	*(value++) = 0;
174    result = setenv(copy, value, 1);
175    free(copy);
176    return(result);
177}
178
179int
180unsetenv(const char *name)
181{
182    struct env_var	*ev;
183    int			err;
184
185    err = 0;
186    if ((ev = env_getenv(name)) == NULL) {
187	err = ENOENT;
188    } else {
189	if (ev->ev_unsethook != NULL)
190	    err = ev->ev_unsethook(ev);
191	if (err == 0) {
192	    env_discard(ev);
193	}
194    }
195    return(err);
196}
197
198static void
199env_discard(struct env_var *ev)
200{
201    if (ev->ev_prev)
202	ev->ev_prev->ev_next = ev->ev_next;
203    if (ev->ev_next)
204	ev->ev_next->ev_prev = ev->ev_prev;
205    if (environ == ev)
206	environ = ev->ev_next;
207    free(ev->ev_name);
208    if (ev->ev_value != NULL && (ev->ev_flags & EV_DYNAMIC) != 0)
209	free(ev->ev_value);
210    free(ev);
211}
212
213int
214env_noset(struct env_var *ev __unused, int flags __unused,
215    const void *value __unused)
216{
217    return(EPERM);
218}
219
220int
221env_nounset(struct env_var *ev __unused)
222{
223    return(EPERM);
224}
225