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