xref: /qemu/util/envlist.c (revision b21e2380)
1 #include "qemu/osdep.h"
2 #include "qemu/queue.h"
3 #include "qemu/envlist.h"
4 
5 struct envlist_entry {
6 	const char *ev_var;			/* actual env value */
7 	QLIST_ENTRY(envlist_entry) ev_link;
8 };
9 
10 struct envlist {
11 	QLIST_HEAD(, envlist_entry) el_entries;	/* actual entries */
12 	size_t el_count;			/* number of entries */
13 };
14 
15 static int envlist_parse(envlist_t *envlist,
16     const char *env, int (*)(envlist_t *, const char *));
17 
18 /*
19  * Allocates new envlist and returns pointer to it.
20  */
21 envlist_t *
22 envlist_create(void)
23 {
24 	envlist_t *envlist;
25 
26 	envlist = g_malloc(sizeof(*envlist));
27 
28 	QLIST_INIT(&envlist->el_entries);
29 	envlist->el_count = 0;
30 
31 	return (envlist);
32 }
33 
34 /*
35  * Releases given envlist and its entries.
36  */
37 void
38 envlist_free(envlist_t *envlist)
39 {
40 	struct envlist_entry *entry;
41 
42 	assert(envlist != NULL);
43 
44 	while (envlist->el_entries.lh_first != NULL) {
45 		entry = envlist->el_entries.lh_first;
46 		QLIST_REMOVE(entry, ev_link);
47 
48 		g_free((char *)entry->ev_var);
49 		g_free(entry);
50 	}
51 	g_free(envlist);
52 }
53 
54 /*
55  * Parses comma separated list of set/modify environment
56  * variable entries and updates given enlist accordingly.
57  *
58  * For example:
59  *     envlist_parse(el, "HOME=foo,SHELL=/bin/sh");
60  *
61  * inserts/sets environment variables HOME and SHELL.
62  *
63  * Returns 0 on success, errno otherwise.
64  */
65 int
66 envlist_parse_set(envlist_t *envlist, const char *env)
67 {
68 	return (envlist_parse(envlist, env, &envlist_setenv));
69 }
70 
71 /*
72  * Parses comma separated list of unset environment variable
73  * entries and removes given variables from given envlist.
74  *
75  * Returns 0 on success, errno otherwise.
76  */
77 int
78 envlist_parse_unset(envlist_t *envlist, const char *env)
79 {
80 	return (envlist_parse(envlist, env, &envlist_unsetenv));
81 }
82 
83 /*
84  * Parses comma separated list of set, modify or unset entries
85  * and calls given callback for each entry.
86  *
87  * Returns 0 in case of success, errno otherwise.
88  */
89 static int
90 envlist_parse(envlist_t *envlist, const char *env,
91     int (*callback)(envlist_t *, const char *))
92 {
93 	char *tmpenv, *envvar;
94 	char *envsave = NULL;
95     int ret = 0;
96     assert(callback != NULL);
97 
98 	if ((envlist == NULL) || (env == NULL))
99 		return (EINVAL);
100 
101 	tmpenv = g_strdup(env);
102     envsave = tmpenv;
103 
104     do {
105         envvar = strchr(tmpenv, ',');
106         if (envvar != NULL) {
107             *envvar = '\0';
108         }
109         if ((*callback)(envlist, tmpenv) != 0) {
110             ret = errno;
111             break;
112 		}
113         tmpenv = envvar + 1;
114     } while (envvar != NULL);
115 
116     g_free(envsave);
117     return ret;
118 }
119 
120 /*
121  * Sets environment value to envlist in similar manner
122  * than putenv(3).
123  *
124  * Returns 0 in success, errno otherwise.
125  */
126 int
127 envlist_setenv(envlist_t *envlist, const char *env)
128 {
129 	struct envlist_entry *entry = NULL;
130 	const char *eq_sign;
131 	size_t envname_len;
132 
133 	if ((envlist == NULL) || (env == NULL))
134 		return (EINVAL);
135 
136 	/* find out first equals sign in given env */
137 	if ((eq_sign = strchr(env, '=')) == NULL)
138 		return (EINVAL);
139 	envname_len = eq_sign - env + 1;
140 
141 	/*
142 	 * If there already exists variable with given name
143 	 * we remove and release it before allocating a whole
144 	 * new entry.
145 	 */
146 	for (entry = envlist->el_entries.lh_first; entry != NULL;
147 	    entry = entry->ev_link.le_next) {
148 		if (strncmp(entry->ev_var, env, envname_len) == 0)
149 			break;
150 	}
151 
152 	if (entry != NULL) {
153 		QLIST_REMOVE(entry, ev_link);
154 		g_free((char *)entry->ev_var);
155 		g_free(entry);
156 	} else {
157 		envlist->el_count++;
158 	}
159 
160 	entry = g_malloc(sizeof(*entry));
161 	entry->ev_var = g_strdup(env);
162 	QLIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link);
163 
164 	return (0);
165 }
166 
167 /*
168  * Removes given env value from envlist in similar manner
169  * than unsetenv(3).  Returns 0 in success, errno otherwise.
170  */
171 int
172 envlist_unsetenv(envlist_t *envlist, const char *env)
173 {
174 	struct envlist_entry *entry;
175 	size_t envname_len;
176 
177 	if ((envlist == NULL) || (env == NULL))
178 		return (EINVAL);
179 
180 	/* env is not allowed to contain '=' */
181 	if (strchr(env, '=') != NULL)
182 		return (EINVAL);
183 
184 	/*
185 	 * Find out the requested entry and remove
186 	 * it from the list.
187 	 */
188 	envname_len = strlen(env);
189 	for (entry = envlist->el_entries.lh_first; entry != NULL;
190 	    entry = entry->ev_link.le_next) {
191 		if (strncmp(entry->ev_var, env, envname_len) == 0)
192 			break;
193 	}
194 	if (entry != NULL) {
195 		QLIST_REMOVE(entry, ev_link);
196 		g_free((char *)entry->ev_var);
197 		g_free(entry);
198 
199 		envlist->el_count--;
200 	}
201 	return (0);
202 }
203 
204 /*
205  * Returns given envlist as array of strings (in same form that
206  * global variable environ is).  Caller must free returned memory
207  * by calling g_free for each element and the array.
208  * Returned array and given envlist are not related (no common
209  * references).
210  *
211  * If caller provides count pointer, number of items in array is
212  * stored there.
213  */
214 char **
215 envlist_to_environ(const envlist_t *envlist, size_t *count)
216 {
217 	struct envlist_entry *entry;
218 	char **env, **penv;
219 
220 	penv = env = g_new(char *, envlist->el_count + 1);
221 
222 	for (entry = envlist->el_entries.lh_first; entry != NULL;
223 	    entry = entry->ev_link.le_next) {
224 		*(penv++) = g_strdup(entry->ev_var);
225 	}
226 	*penv = NULL; /* NULL terminate the list */
227 
228 	if (count != NULL)
229 		*count = envlist->el_count;
230 
231 	return (env);
232 }
233