qemu/envlist.c
Blue Swirl 72cf2d4f0e Fix sys-queue.h conflict for good
Problem: Our file sys-queue.h is a copy of the BSD file, but there are
some additions and it's not entirely compatible. Because of that, there have
been conflicts with system headers on BSD systems. Some hacks have been
introduced in the commits 15cc923584,
f40d753718,
96555a96d7 and
3990d09adf but the fixes were fragile.

Solution: Avoid the conflict entirely by renaming the functions and the
file. Revert the previous hacks.

Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
2009-09-12 07:36:22 +00:00

247 lines
5.3 KiB
C

#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "qemu-queue.h"
#include "envlist.h"
struct envlist_entry {
const char *ev_var; /* actual env value */
QLIST_ENTRY(envlist_entry) ev_link;
};
struct envlist {
QLIST_HEAD(, envlist_entry) el_entries; /* actual entries */
size_t el_count; /* number of entries */
};
static int envlist_parse(envlist_t *envlist,
const char *env, int (*)(envlist_t *, const char *));
/*
* Allocates new envlist and returns pointer to that or
* NULL in case of error.
*/
envlist_t *
envlist_create(void)
{
envlist_t *envlist;
if ((envlist = malloc(sizeof (*envlist))) == NULL)
return (NULL);
QLIST_INIT(&envlist->el_entries);
envlist->el_count = 0;
return (envlist);
}
/*
* Releases given envlist and its entries.
*/
void
envlist_free(envlist_t *envlist)
{
struct envlist_entry *entry;
assert(envlist != NULL);
while (envlist->el_entries.lh_first != NULL) {
entry = envlist->el_entries.lh_first;
QLIST_REMOVE(entry, ev_link);
free((char *)entry->ev_var);
free(entry);
}
free(envlist);
}
/*
* Parses comma separated list of set/modify environment
* variable entries and updates given enlist accordingly.
*
* For example:
* envlist_parse(el, "HOME=foo,SHELL=/bin/sh");
*
* inserts/sets environment variables HOME and SHELL.
*
* Returns 0 on success, errno otherwise.
*/
int
envlist_parse_set(envlist_t *envlist, const char *env)
{
return (envlist_parse(envlist, env, &envlist_setenv));
}
/*
* Parses comma separated list of unset environment variable
* entries and removes given variables from given envlist.
*
* Returns 0 on success, errno otherwise.
*/
int
envlist_parse_unset(envlist_t *envlist, const char *env)
{
return (envlist_parse(envlist, env, &envlist_unsetenv));
}
/*
* Parses comma separated list of set, modify or unset entries
* and calls given callback for each entry.
*
* Returns 0 in case of success, errno otherwise.
*/
static int
envlist_parse(envlist_t *envlist, const char *env,
int (*callback)(envlist_t *, const char *))
{
char *tmpenv, *envvar;
char *envsave = NULL;
assert(callback != NULL);
if ((envlist == NULL) || (env == NULL))
return (EINVAL);
/*
* We need to make temporary copy of the env string
* as strtok_r(3) modifies it while it tokenizes.
*/
if ((tmpenv = strdup(env)) == NULL)
return (errno);
envvar = strtok_r(tmpenv, ",", &envsave);
while (envvar != NULL) {
if ((*callback)(envlist, envvar) != 0) {
free(tmpenv);
return (errno);
}
envvar = strtok_r(NULL, ",", &envsave);
}
free(tmpenv);
return (0);
}
/*
* Sets environment value to envlist in similar manner
* than putenv(3).
*
* Returns 0 in success, errno otherwise.
*/
int
envlist_setenv(envlist_t *envlist, const char *env)
{
struct envlist_entry *entry = NULL;
const char *eq_sign;
size_t envname_len;
if ((envlist == NULL) || (env == NULL))
return (EINVAL);
/* find out first equals sign in given env */
if ((eq_sign = strchr(env, '=')) == NULL)
return (EINVAL);
envname_len = eq_sign - env + 1;
/*
* If there already exists variable with given name
* we remove and release it before allocating a whole
* new entry.
*/
for (entry = envlist->el_entries.lh_first; entry != NULL;
entry = entry->ev_link.le_next) {
if (strncmp(entry->ev_var, env, envname_len) == 0)
break;
}
if (entry != NULL) {
QLIST_REMOVE(entry, ev_link);
free((char *)entry->ev_var);
free(entry);
} else {
envlist->el_count++;
}
if ((entry = malloc(sizeof (*entry))) == NULL)
return (errno);
if ((entry->ev_var = strdup(env)) == NULL) {
free(entry);
return (errno);
}
QLIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link);
return (0);
}
/*
* Removes given env value from envlist in similar manner
* than unsetenv(3). Returns 0 in success, errno otherwise.
*/
int
envlist_unsetenv(envlist_t *envlist, const char *env)
{
struct envlist_entry *entry;
size_t envname_len;
if ((envlist == NULL) || (env == NULL))
return (EINVAL);
/* env is not allowed to contain '=' */
if (strchr(env, '=') != NULL)
return (EINVAL);
/*
* Find out the requested entry and remove
* it from the list.
*/
envname_len = strlen(env);
for (entry = envlist->el_entries.lh_first; entry != NULL;
entry = entry->ev_link.le_next) {
if (strncmp(entry->ev_var, env, envname_len) == 0)
break;
}
if (entry != NULL) {
QLIST_REMOVE(entry, ev_link);
free((char *)entry->ev_var);
free(entry);
envlist->el_count--;
}
return (0);
}
/*
* Returns given envlist as array of strings (in same form that
* global variable environ is). Caller must free returned memory
* by calling free(3) for each element and for the array. Returned
* array and given envlist are not related (no common references).
*
* If caller provides count pointer, number of items in array is
* stored there. In case of error, NULL is returned and no memory
* is allocated.
*/
char **
envlist_to_environ(const envlist_t *envlist, size_t *count)
{
struct envlist_entry *entry;
char **env, **penv;
penv = env = malloc((envlist->el_count + 1) * sizeof (char *));
if (env == NULL)
return (NULL);
for (entry = envlist->el_entries.lh_first; entry != NULL;
entry = entry->ev_link.le_next) {
*(penv++) = strdup(entry->ev_var);
}
*penv = NULL; /* NULL terminate the list */
if (count != NULL)
*count = envlist->el_count;
return (env);
}