xref: /qemu/util/envlist.c (revision 83ecdb18)
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