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    } else {
80
81	/*
82	 * New variable; create and sort into list
83	 */
84	ev = malloc(sizeof(struct env_var));
85	ev->ev_name = strdup(name);
86	ev->ev_value = NULL;
87	/* hooks can only be set when the variable is instantiated */
88	ev->ev_sethook = sethook;
89	ev->ev_unsethook = unsethook;
90
91	/* Sort into list */
92	ev->ev_prev = NULL;
93	ev->ev_next = NULL;
94	/* Search for the record to insert before */
95	for (last = NULL, curr = environ;
96	     curr != NULL;
97	     last = curr, curr = curr->ev_next) {
98
99	    if (strcmp(ev->ev_name, curr->ev_name) < 0) {
100		if (curr->ev_prev) {
101		    curr->ev_prev->ev_next = ev;
102		} else {
103		    environ = ev;
104		}
105		ev->ev_next = curr;
106		ev->ev_prev = curr->ev_prev;
107		curr->ev_prev = ev;
108		break;
109	    }
110	}
111	if (curr == NULL) {
112	    if (last == NULL) {
113		environ = ev;
114	    } else {
115		last->ev_next = ev;
116		ev->ev_prev = last;
117	    }
118	}
119    }
120
121    /* If there is data in the variable, discard it */
122    if (ev->ev_value != NULL)
123	free(ev->ev_value);
124
125    /* If we have a new value, use it */
126    if (flags & EV_VOLATILE) {
127	ev->ev_value = strdup(value);
128    } else {
129	ev->ev_value = (char *)value;
130    }
131
132    /* Keep the flag components that are relevant */
133    ev->ev_flags = flags & (EV_DYNAMIC);
134
135    return(0);
136}
137
138char *
139getenv(const char *name)
140{
141    struct env_var	*ev;
142
143    /* Set but no value gives empty string */
144    if ((ev = env_getenv(name)) != NULL) {
145	if (ev->ev_value != NULL)
146	    return(ev->ev_value);
147	return("");
148    }
149    return(NULL);
150}
151
152int
153setenv(const char *name, const char *value, int overwrite)
154{
155    /* No guarantees about state, always assume volatile */
156    if (overwrite || (env_getenv(name) == NULL))
157	return(env_setenv(name, EV_VOLATILE, value, NULL, NULL));
158    return(0);
159}
160
161int
162putenv(const char *string)
163{
164    char	*value, *copy;
165    int		result;
166
167    copy = strdup(string);
168    if ((value = strchr(copy, '=')) != NULL)
169	*(value++) = 0;
170    result = setenv(copy, value, 1);
171    free(copy);
172    return(result);
173}
174
175int
176unsetenv(const char *name)
177{
178    struct env_var	*ev;
179    int			err;
180
181    err = 0;
182    if ((ev = env_getenv(name)) == NULL) {
183	err = ENOENT;
184    } else {
185	if (ev->ev_unsethook != NULL)
186	    err = ev->ev_unsethook(ev);
187	if (err == 0) {
188	    env_discard(ev);
189	}
190    }
191    return(err);
192}
193
194static void
195env_discard(struct env_var *ev)
196{
197    if (ev->ev_prev)
198	ev->ev_prev->ev_next = ev->ev_next;
199    if (ev->ev_next)
200	ev->ev_next->ev_prev = ev->ev_prev;
201    if (environ == ev)
202	environ = ev->ev_next;
203    free(ev->ev_name);
204    if (ev->ev_flags & EV_DYNAMIC)
205	free(ev->ev_value);
206    free(ev);
207}
208
209int
210env_noset(struct env_var *ev __unused, int flags __unused,
211    const void *value __unused)
212{
213    return(EPERM);
214}
215
216int
217env_nounset(struct env_var *ev __unused)
218{
219    return(EPERM);
220}
221