xref: /openbsd-src/usr.bin/tmux/environ.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /* $OpenBSD: environ.c,v 1.15 2016/07/15 09:52:34 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "tmux.h"
25 
26 /*
27  * Environment - manipulate a set of environment variables.
28  */
29 
30 RB_HEAD(environ, environ_entry);
31 int	environ_cmp(struct environ_entry *, struct environ_entry *);
32 RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp);
33 RB_GENERATE(environ, environ_entry, entry, environ_cmp);
34 
35 int
36 environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2)
37 {
38 	return (strcmp(envent1->name, envent2->name));
39 }
40 
41 /* Initialise the environment. */
42 struct environ *
43 environ_create(void)
44 {
45 	struct environ	*env;
46 
47 	env = xcalloc(1, sizeof *env);
48 	RB_INIT(env);
49 
50 	return (env);
51 }
52 
53 /* Free an environment. */
54 void
55 environ_free(struct environ *env)
56 {
57 	struct environ_entry	*envent, *envent1;
58 
59 	RB_FOREACH_SAFE(envent, environ, env, envent1) {
60 		RB_REMOVE(environ, env, envent);
61 		free(envent->name);
62 		free(envent->value);
63 		free(envent);
64 	}
65 	free(env);
66 }
67 
68 struct environ_entry *
69 environ_first(struct environ *env)
70 {
71 	return (RB_MIN(environ, env));
72 }
73 
74 struct environ_entry *
75 environ_next(struct environ_entry *envent)
76 {
77 	return (RB_NEXT(environ, env, envent));
78 }
79 
80 /* Copy one environment into another. */
81 void
82 environ_copy(struct environ *srcenv, struct environ *dstenv)
83 {
84 	struct environ_entry	*envent;
85 
86 	RB_FOREACH(envent, environ, srcenv) {
87 		if (envent->value == NULL)
88 			environ_clear(dstenv, envent->name);
89 		else
90 			environ_set(dstenv, envent->name, "%s", envent->value);
91 	}
92 }
93 
94 /* Find an environment variable. */
95 struct environ_entry *
96 environ_find(struct environ *env, const char *name)
97 {
98 	struct environ_entry	envent;
99 
100 	envent.name = (char *) name;
101 	return (RB_FIND(environ, env, &envent));
102 }
103 
104 /* Set an environment variable. */
105 void
106 environ_set(struct environ *env, const char *name, const char *fmt, ...)
107 {
108 	struct environ_entry	*envent;
109 	va_list			 ap;
110 
111 	va_start(ap, fmt);
112 	if ((envent = environ_find(env, name)) != NULL) {
113 		free(envent->value);
114 		xvasprintf(&envent->value, fmt, ap);
115 	} else {
116 		envent = xmalloc(sizeof *envent);
117 		envent->name = xstrdup(name);
118 		xvasprintf(&envent->value, fmt, ap);
119 		RB_INSERT(environ, env, envent);
120 	}
121 	va_end(ap);
122 }
123 
124 /* Clear an environment variable. */
125 void
126 environ_clear(struct environ *env, const char *name)
127 {
128 	struct environ_entry	*envent;
129 
130 	if ((envent = environ_find(env, name)) != NULL) {
131 		free(envent->value);
132 		envent->value = NULL;
133 	} else {
134 		envent = xmalloc(sizeof *envent);
135 		envent->name = xstrdup(name);
136 		envent->value = NULL;
137 		RB_INSERT(environ, env, envent);
138 	}
139 }
140 
141 /* Set an environment variable from a NAME=VALUE string. */
142 void
143 environ_put(struct environ *env, const char *var)
144 {
145 	char	*name, *value;
146 
147 	value = strchr(var, '=');
148 	if (value == NULL)
149 		return;
150 	value++;
151 
152 	name = xstrdup(var);
153 	name[strcspn(name, "=")] = '\0';
154 
155 	environ_set(env, name, "%s", value);
156 	free(name);
157 }
158 
159 /* Unset an environment variable. */
160 void
161 environ_unset(struct environ *env, const char *name)
162 {
163 	struct environ_entry	*envent;
164 
165 	if ((envent = environ_find(env, name)) == NULL)
166 		return;
167 	RB_REMOVE(environ, env, envent);
168 	free(envent->name);
169 	free(envent->value);
170 	free(envent);
171 }
172 
173 /*
174  * Copy a space-separated list of variables from a destination into a source
175  * environment.
176  */
177 void
178 environ_update(const char *vars, struct environ *srcenv,
179     struct environ *dstenv)
180 {
181 	struct environ_entry	*envent;
182 	char			*copyvars, *var, *next;
183 
184 	copyvars = next = xstrdup(vars);
185 	while ((var = strsep(&next, " ")) != NULL) {
186 		if ((envent = environ_find(srcenv, var)) == NULL)
187 			environ_clear(dstenv, var);
188 		else
189 			environ_set(dstenv, envent->name, "%s", envent->value);
190 	}
191 	free(copyvars);
192 }
193 
194 /* Push environment into the real environment - use after fork(). */
195 void
196 environ_push(struct environ *env)
197 {
198 	struct environ_entry	*envent;
199 
200 	environ = xcalloc(1, sizeof *environ);
201 	RB_FOREACH(envent, environ, env) {
202 		if (envent->value != NULL && *envent->name != '\0')
203 			setenv(envent->name, envent->value, 1);
204 	}
205 }
206 
207 /* Log the environment. */
208 void
209 environ_log(struct environ *env, const char *prefix)
210 {
211 	struct environ_entry	*envent;
212 
213 	RB_FOREACH(envent, environ, env) {
214 		if (envent->value != NULL && *envent->name != '\0') {
215 			log_debug("%s%s=%s", prefix, envent->name,
216 			    envent->value);
217 		}
218 	}
219 }
220