Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
include/qemu/envlist.h | 13 ++++++
tests/unit/meson.build | 1 +
tests/unit/test-envlist.c | 94 +++++++++++++++++++++++++++++++++++++++
util/envlist.c | 67 +++++++++++++++++++++++-----
4 files changed, 165 insertions(+), 10 deletions(-)
create mode 100644 tests/unit/test-envlist.c
diff --git a/include/qemu/envlist.h b/include/qemu/envlist.h
index 6006dfae44..dc0fe4e644 100644
--- a/include/qemu/envlist.h
+++ b/include/qemu/envlist.h
@@ -1,12 +1,25 @@
#ifndef ENVLIST_H
#define ENVLIST_H
+#include "qemu/queue.h"
+
+struct envlist_entry {
+ 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 */
+};
+
typedef struct envlist envlist_t;
envlist_t *envlist_create(void);
void envlist_free(envlist_t *);
int envlist_setenv(envlist_t *, const char *);
int envlist_unsetenv(envlist_t *, const char *);
+int envlist_appendenv(envlist_t *, const char *, const char *);
int envlist_parse_set(envlist_t *, const char *);
int envlist_parse_unset(envlist_t *, const char *);
char **envlist_to_environ(const envlist_t *, size_t *);
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 93977cc32d..9b25b45271 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -50,6 +50,7 @@ tests = {
'test-qapi-util': [],
'test-interval-tree': [],
'test-xs-node': [qom],
+ 'test-envlist': [],
}
if have_system or have_tools
diff --git a/tests/unit/test-envlist.c b/tests/unit/test-envlist.c
new file mode 100644
index 0000000000..d583c6ee33
--- /dev/null
+++ b/tests/unit/test-envlist.c
@@ -0,0 +1,94 @@
+/*
+ * testenvlist unit-tests.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/envlist.h"
+
+static void envlist_create_free_test(void)
+{
+ envlist_t *testenvlist;
+
+ testenvlist = envlist_create();
+ g_assert(testenvlist != NULL);
+ g_assert(testenvlist->el_count == 0);
+
+ envlist_free(testenvlist);
+}
+
+static void envlist_set_unset_test(void)
+{
+ envlist_t *testenvlist;
+ const char *env = "TEST=123";
+ struct envlist_entry *entry;
+
+ testenvlist = envlist_create();
+ g_assert(envlist_setenv(testenvlist, env) == 0);
+ g_assert(testenvlist->el_count == 1);
+ entry = testenvlist->el_entries.lh_first;
+ g_assert_cmpstr(entry->ev_var, ==, "TEST=123");
+ g_assert(envlist_unsetenv(testenvlist, "TEST") == 0);
+ g_assert(testenvlist->el_count == 0);
+
+ envlist_free(testenvlist);
+}
+
+static void envlist_length_test(void)
+{
+ envlist_t *testenvlist;
+ const char *env = "TEST1=123,TEST2=456";
+
+ testenvlist = envlist_create();
+ g_assert(envlist_parse_set(testenvlist, env) == 0);
+ g_assert(testenvlist->el_count == 2);
+ g_assert(envlist_parse_unset(testenvlist, "TEST1,TEST2") == 0);
+ g_assert(testenvlist->el_count == 0);
+
+ envlist_free(testenvlist);
+}
+
+static void envlist_appendenv_test(void)
+{
+ envlist_t *testenvlist;
+ const char *env = "TEST=123";
+ struct envlist_entry *entry;
+
+ testenvlist = envlist_create();
+ g_assert(envlist_setenv(testenvlist, env) == 0);
+ g_assert(envlist_appendenv(testenvlist, "TEST=456", ";") == 0);
+ entry = testenvlist->el_entries.lh_first;
+ g_assert_cmpstr(entry->ev_var, ==, "TEST=123;456");
+
+ envlist_free(testenvlist);
+}
+
+static void envlist_to_environ_test(void)
+{
+ envlist_t *testenvlist;
+ const char *env = "TEST1=123,TEST2=456";
+ size_t count;
+ char **environ;
+
+ testenvlist = envlist_create();
+ g_assert(envlist_parse_set(testenvlist, env) == 0);
+ g_assert(testenvlist->el_count == 2);
+ environ = envlist_to_environ(testenvlist, &count);
+ g_assert(count == 2);
+ g_assert_cmpstr(environ[1], ==, "TEST1=123");
+ g_assert_cmpstr(environ[0], ==, "TEST2=456");
+
+ envlist_free(testenvlist);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/envlist/create_free", envlist_create_free_test);
+ g_test_add_func("/envlist/set_unset", envlist_set_unset_test);
+ g_test_add_func("/envlist/length", envlist_length_test);
+ g_test_add_func("/envlist/appendenv", envlist_appendenv_test);
+ g_test_add_func("/envlist/to_environ", envlist_to_environ_test);
+
+ return g_test_run();
+}
diff --git a/util/envlist.c b/util/envlist.c
index db937c0427..2dfead4a0f 100644
--- a/util/envlist.c
+++ b/util/envlist.c
@@ -2,16 +2,6 @@
#include "qemu/queue.h"
#include "qemu/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 *));
@@ -201,6 +191,63 @@ envlist_unsetenv(envlist_t *envlist, const char *env)
return (0);
}
+/*
+ * Appends environment value to envlist. If the environment
+ * variable already exists, the new value is appended to the
+ * existing one.
+ *
+ * Returns 0 in success, errno otherwise.
+ */
+int
+envlist_appendenv(envlist_t *envlist, const char *env, const char *separator)
+{
+ struct envlist_entry *entry = NULL;
+ const char *eq_sign;
+ size_t envname_len;
+
+ if ((envlist == NULL) || (env == NULL) || (separator == NULL)) {
+ return -EINVAL;
+ }
+
+ /* find out first equals sign in given env */
+ eq_sign = strchr(env, '=');
+ if (eq_sign == NULL) {
+ return -EINVAL;
+ }
+
+ if (strchr(eq_sign + 1, '=') != NULL) {
+ return -EINVAL;
+ }
+
+ envname_len = eq_sign - env + 1;
+
+ /*
+ * If there already exists variable with given name,
+ * we append the new value to the existing one.
+ */
+ 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) {
+ GString *new_val = g_string_new(entry->ev_var);
+ g_string_append(new_val, separator);
+ g_string_append(new_val, eq_sign + 1);
+ g_free(entry->ev_var);
+ entry->ev_var = g_string_free(new_val, false);
+ } else {
+ envlist->el_count++;
+ entry = g_malloc(sizeof(*entry));
+ entry->ev_var = g_strdup(env);
+ QLIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link);
+ }
+
+ return 0;
+}
+
/*
* Returns given envlist as array of strings (in same form that
* global variable environ is). Caller must free returned memory
--
2.34.1
© 2016 - 2024 Red Hat, Inc.