Signed-off-by: Yeqi Fu <fufuyqqq...@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


Reply via email to