xref: /openbsd-src/usr.bin/tmux/environ.c (revision 6a13ef69787db04ae501a22e92fa10865b44fd7c)
1 /* $OpenBSD: environ.c,v 1.16 2016/10/10 21:29:23 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 static int environ_cmp(struct environ_entry *, struct environ_entry *);
32 RB_GENERATE_STATIC(environ, environ_entry, entry, environ_cmp);
33 
34 static int
35 environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2)
36 {
37 	return (strcmp(envent1->name, envent2->name));
38 }
39 
40 /* Initialise the environment. */
41 struct environ *
42 environ_create(void)
43 {
44 	struct environ	*env;
45 
46 	env = xcalloc(1, sizeof *env);
47 	RB_INIT(env);
48 
49 	return (env);
50 }
51 
52 /* Free an environment. */
53 void
54 environ_free(struct environ *env)
55 {
56 	struct environ_entry	*envent, *envent1;
57 
58 	RB_FOREACH_SAFE(envent, environ, env, envent1) {
59 		RB_REMOVE(environ, env, envent);
60 		free(envent->name);
61 		free(envent->value);
62 		free(envent);
63 	}
64 	free(env);
65 }
66 
67 struct environ_entry *
68 environ_first(struct environ *env)
69 {
70 	return (RB_MIN(environ, env));
71 }
72 
73 struct environ_entry *
74 environ_next(struct environ_entry *envent)
75 {
76 	return (RB_NEXT(environ, env, envent));
77 }
78 
79 /* Copy one environment into another. */
80 void
81 environ_copy(struct environ *srcenv, struct environ *dstenv)
82 {
83 	struct environ_entry	*envent;
84 
85 	RB_FOREACH(envent, environ, srcenv) {
86 		if (envent->value == NULL)
87 			environ_clear(dstenv, envent->name);
88 		else
89 			environ_set(dstenv, envent->name, "%s", envent->value);
90 	}
91 }
92 
93 /* Find an environment variable. */
94 struct environ_entry *
95 environ_find(struct environ *env, const char *name)
96 {
97 	struct environ_entry	envent;
98 
99 	envent.name = (char *) name;
100 	return (RB_FIND(environ, env, &envent));
101 }
102 
103 /* Set an environment variable. */
104 void
105 environ_set(struct environ *env, const char *name, const char *fmt, ...)
106 {
107 	struct environ_entry	*envent;
108 	va_list			 ap;
109 
110 	va_start(ap, fmt);
111 	if ((envent = environ_find(env, name)) != NULL) {
112 		free(envent->value);
113 		xvasprintf(&envent->value, fmt, ap);
114 	} else {
115 		envent = xmalloc(sizeof *envent);
116 		envent->name = xstrdup(name);
117 		xvasprintf(&envent->value, fmt, ap);
118 		RB_INSERT(environ, env, envent);
119 	}
120 	va_end(ap);
121 }
122 
123 /* Clear an environment variable. */
124 void
125 environ_clear(struct environ *env, const char *name)
126 {
127 	struct environ_entry	*envent;
128 
129 	if ((envent = environ_find(env, name)) != NULL) {
130 		free(envent->value);
131 		envent->value = NULL;
132 	} else {
133 		envent = xmalloc(sizeof *envent);
134 		envent->name = xstrdup(name);
135 		envent->value = NULL;
136 		RB_INSERT(environ, env, envent);
137 	}
138 }
139 
140 /* Set an environment variable from a NAME=VALUE string. */
141 void
142 environ_put(struct environ *env, const char *var)
143 {
144 	char	*name, *value;
145 
146 	value = strchr(var, '=');
147 	if (value == NULL)
148 		return;
149 	value++;
150 
151 	name = xstrdup(var);
152 	name[strcspn(name, "=")] = '\0';
153 
154 	environ_set(env, name, "%s", value);
155 	free(name);
156 }
157 
158 /* Unset an environment variable. */
159 void
160 environ_unset(struct environ *env, const char *name)
161 {
162 	struct environ_entry	*envent;
163 
164 	if ((envent = environ_find(env, name)) == NULL)
165 		return;
166 	RB_REMOVE(environ, env, envent);
167 	free(envent->name);
168 	free(envent->value);
169 	free(envent);
170 }
171 
172 /*
173  * Copy a space-separated list of variables from a destination into a source
174  * environment.
175  */
176 void
177 environ_update(const char *vars, struct environ *srcenv,
178     struct environ *dstenv)
179 {
180 	struct environ_entry	*envent;
181 	char			*copyvars, *var, *next;
182 
183 	copyvars = next = xstrdup(vars);
184 	while ((var = strsep(&next, " ")) != NULL) {
185 		if ((envent = environ_find(srcenv, var)) == NULL)
186 			environ_clear(dstenv, var);
187 		else
188 			environ_set(dstenv, envent->name, "%s", envent->value);
189 	}
190 	free(copyvars);
191 }
192 
193 /* Push environment into the real environment - use after fork(). */
194 void
195 environ_push(struct environ *env)
196 {
197 	struct environ_entry	*envent;
198 
199 	environ = xcalloc(1, sizeof *environ);
200 	RB_FOREACH(envent, environ, env) {
201 		if (envent->value != NULL && *envent->name != '\0')
202 			setenv(envent->name, envent->value, 1);
203 	}
204 }
205 
206 /* Log the environment. */
207 void
208 environ_log(struct environ *env, const char *prefix)
209 {
210 	struct environ_entry	*envent;
211 
212 	RB_FOREACH(envent, environ, env) {
213 		if (envent->value != NULL && *envent->name != '\0') {
214 			log_debug("%s%s=%s", prefix, envent->name,
215 			    envent->value);
216 		}
217 	}
218 }
219