Statistics
| Branch: | Revision:

root / envlist.c @ 636aa200

History | View | Annotate | Download (5.3 kB)

1
#include <sys/queue.h>
2

    
3
#include <assert.h>
4
#include <errno.h>
5
#include <stdlib.h>
6
#include <string.h>
7
#include <unistd.h>
8

    
9
#include "envlist.h"
10

    
11
struct envlist_entry {
12
        const char *ev_var;                        /* actual env value */
13
        LIST_ENTRY(envlist_entry) ev_link;
14
};
15

    
16
struct envlist {
17
        LIST_HEAD(, envlist_entry) el_entries;        /* actual entries */
18
        size_t el_count;                        /* number of entries */
19
};
20

    
21
static int envlist_parse(envlist_t *envlist,
22
    const char *env, int (*)(envlist_t *, const char *));
23

    
24
/*
25
 * Allocates new envlist and returns pointer to that or
26
 * NULL in case of error.
27
 */
28
envlist_t *
29
envlist_create(void)
30
{
31
        envlist_t *envlist;
32

    
33
        if ((envlist = malloc(sizeof (*envlist))) == NULL)
34
                return (NULL);
35

    
36
        LIST_INIT(&envlist->el_entries);
37
        envlist->el_count = 0;
38

    
39
        return (envlist);
40
}
41

    
42
/*
43
 * Releases given envlist and its entries.
44
 */
45
void
46
envlist_free(envlist_t *envlist)
47
{
48
        struct envlist_entry *entry;
49

    
50
        assert(envlist != NULL);
51

    
52
        while (envlist->el_entries.lh_first != NULL) {
53
                entry = envlist->el_entries.lh_first;
54
                LIST_REMOVE(entry, ev_link);
55

    
56
                free((char *)entry->ev_var);
57
                free(entry);
58
        }
59
        free(envlist);
60
}
61

    
62
/*
63
 * Parses comma separated list of set/modify environment
64
 * variable entries and updates given enlist accordingly.
65
 *
66
 * For example:
67
 *     envlist_parse(el, "HOME=foo,SHELL=/bin/sh");
68
 *
69
 * inserts/sets environment variables HOME and SHELL.
70
 *
71
 * Returns 0 on success, errno otherwise.
72
 */
73
int
74
envlist_parse_set(envlist_t *envlist, const char *env)
75
{
76
        return (envlist_parse(envlist, env, &envlist_setenv));
77
}
78

    
79
/*
80
 * Parses comma separated list of unset environment variable
81
 * entries and removes given variables from given envlist.
82
 *
83
 * Returns 0 on success, errno otherwise.
84
 */
85
int
86
envlist_parse_unset(envlist_t *envlist, const char *env)
87
{
88
        return (envlist_parse(envlist, env, &envlist_unsetenv));
89
}
90

    
91
/*
92
 * Parses comma separated list of set, modify or unset entries
93
 * and calls given callback for each entry.
94
 *
95
 * Returns 0 in case of success, errno otherwise.
96
 */
97
static int
98
envlist_parse(envlist_t *envlist, const char *env,
99
    int (*callback)(envlist_t *, const char *))
100
{
101
        char *tmpenv, *envvar;
102
        char *envsave = NULL;
103

    
104
        assert(callback != NULL);
105

    
106
        if ((envlist == NULL) || (env == NULL))
107
                return (EINVAL);
108

    
109
        /*
110
         * We need to make temporary copy of the env string
111
         * as strtok_r(3) modifies it while it tokenizes.
112
         */
113
        if ((tmpenv = strdup(env)) == NULL)
114
                return (errno);
115

    
116
        envvar = strtok_r(tmpenv, ",", &envsave);
117
        while (envvar != NULL) {
118
                if ((*callback)(envlist, envvar) != 0) {
119
                        free(tmpenv);
120
                        return (errno);
121
                }
122
                envvar = strtok_r(NULL, ",", &envsave);
123
        }
124

    
125
        free(tmpenv);
126
        return (0);
127
}
128

    
129
/*
130
 * Sets environment value to envlist in similar manner
131
 * than putenv(3).
132
 *
133
 * Returns 0 in success, errno otherwise.
134
 */
135
int
136
envlist_setenv(envlist_t *envlist, const char *env)
137
{
138
        struct envlist_entry *entry = NULL;
139
        const char *eq_sign;
140
        size_t envname_len;
141

    
142
        if ((envlist == NULL) || (env == NULL))
143
                return (EINVAL);
144

    
145
        /* find out first equals sign in given env */
146
        if ((eq_sign = strchr(env, '=')) == NULL)
147
                return (EINVAL);
148
        envname_len = eq_sign - env + 1;
149

    
150
        /*
151
         * If there already exists variable with given name
152
         * we remove and release it before allocating a whole
153
         * new entry.
154
         */
155
        for (entry = envlist->el_entries.lh_first; entry != NULL;
156
            entry = entry->ev_link.le_next) {
157
                if (strncmp(entry->ev_var, env, envname_len) == 0)
158
                        break;
159
        }
160

    
161
        if (entry != NULL) {
162
                LIST_REMOVE(entry, ev_link);
163
                free((char *)entry->ev_var);
164
                free(entry);
165
        } else {
166
                envlist->el_count++;
167
        }
168

    
169
        if ((entry = malloc(sizeof (*entry))) == NULL)
170
                return (errno);
171
        if ((entry->ev_var = strdup(env)) == NULL) {
172
                free(entry);
173
                return (errno);
174
        }
175
        LIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link);
176

    
177
        return (0);
178
}
179

    
180
/*
181
 * Removes given env value from envlist in similar manner
182
 * than unsetenv(3).  Returns 0 in success, errno otherwise.
183
 */
184
int
185
envlist_unsetenv(envlist_t *envlist, const char *env)
186
{
187
        struct envlist_entry *entry;
188
        size_t envname_len;
189

    
190
        if ((envlist == NULL) || (env == NULL))
191
                return (EINVAL);
192

    
193
        /* env is not allowed to contain '=' */
194
        if (strchr(env, '=') != NULL)
195
                return (EINVAL);
196

    
197
        /*
198
         * Find out the requested entry and remove
199
         * it from the list.
200
         */
201
        envname_len = strlen(env);
202
        for (entry = envlist->el_entries.lh_first; entry != NULL;
203
            entry = entry->ev_link.le_next) {
204
                if (strncmp(entry->ev_var, env, envname_len) == 0)
205
                        break;
206
        }
207
        if (entry != NULL) {
208
                LIST_REMOVE(entry, ev_link);
209
                free((char *)entry->ev_var);
210
                free(entry);
211

    
212
                envlist->el_count--;
213
        }
214
        return (0);
215
}
216

    
217
/*
218
 * Returns given envlist as array of strings (in same form that
219
 * global variable environ is).  Caller must free returned memory
220
 * by calling free(3) for each element and for the array.  Returned
221
 * array and given envlist are not related (no common references).
222
 *
223
 * If caller provides count pointer, number of items in array is
224
 * stored there.  In case of error, NULL is returned and no memory
225
 * is allocated.
226
 */
227
char **
228
envlist_to_environ(const envlist_t *envlist, size_t *count)
229
{
230
        struct envlist_entry *entry;
231
        char **env, **penv;
232

    
233
        penv = env = malloc((envlist->el_count + 1) * sizeof (char *));
234
        if (env == NULL)
235
                return (NULL);
236

    
237
        for (entry = envlist->el_entries.lh_first; entry != NULL;
238
            entry = entry->ev_link.le_next) {
239
                *(penv++) = strdup(entry->ev_var);
240
        }
241
        *penv = NULL; /* NULL terminate the list */
242

    
243
        if (count != NULL)
244
                *count = envlist->el_count;
245

    
246
        return (env);
247
}