From cb86533e80f5b2556ab66a78e4d980221f9f94bf Mon Sep 17 00:00:00 2001
From: Olivier Matz <zer0@droids-corp.org>
Date: Thu, 13 Dec 2018 19:49:55 +0100
Subject: [PATCH] add attrs to strvec

---
 libecoli/ecoli_strvec.c | 151 ++++++++++++++++++++++++++++++++++------
 libecoli/ecoli_strvec.h |  50 +++++++++++++
 2 files changed, 180 insertions(+), 21 deletions(-)

diff --git a/libecoli/ecoli_strvec.c b/libecoli/ecoli_strvec.c
index c574b1b..98a952f 100644
--- a/libecoli/ecoli_strvec.c
+++ b/libecoli/ecoli_strvec.c
@@ -11,6 +11,7 @@
 #include <ecoli_malloc.h>
 #include <ecoli_test.h>
 #include <ecoli_log.h>
+#include <ecoli_keyval.h>
 #include <ecoli_strvec.h>
 
 EC_LOG_TYPE_REGISTER(strvec);
@@ -18,6 +19,7 @@ EC_LOG_TYPE_REGISTER(strvec);
 struct ec_strvec_elt {
 	unsigned int refcnt;
 	char *str;
+	struct ec_keyval *attrs;
 };
 
 struct ec_strvec {
@@ -36,10 +38,64 @@ struct ec_strvec *ec_strvec(void)
 	return strvec;
 }
 
+static struct ec_strvec_elt *
+__ec_strvec_elt(const char *s)
+{
+	struct ec_strvec_elt *elt;
+
+	elt = ec_calloc(1, sizeof(*elt));
+	if (elt == NULL)
+		return NULL;
+
+	elt->str = ec_strdup(s);
+	if (elt->str == NULL) {
+		ec_free(elt);
+		return NULL;
+	}
+	elt->refcnt = 1;
+
+	return elt;
+}
+
+static void
+__ec_strvec_elt_free(struct ec_strvec_elt *elt)
+{
+	elt->refcnt--;
+	if (elt->refcnt == 0) {
+		ec_free(elt->str);
+		ec_keyval_free(elt->attrs);
+		ec_free(elt);
+	}
+}
+
+int ec_strvec_set(struct ec_strvec *strvec, size_t idx, const char *s)
+{
+	struct ec_strvec_elt *elt;
+
+	if (strvec == NULL || s == NULL || idx >= strvec->len) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	elt = __ec_strvec_elt(s);
+	if (elt == NULL)
+		return -1;
+
+	__ec_strvec_elt_free(strvec->vec[idx]);
+	strvec->vec[idx] = elt;
+
+	return 0;
+}
+
 int ec_strvec_add(struct ec_strvec *strvec, const char *s)
 {
 	struct ec_strvec_elt *elt, **new_vec;
 
+	if (strvec == NULL || s == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	new_vec = ec_realloc(strvec->vec,
 		sizeof(*strvec->vec) * (strvec->len + 1));
 	if (new_vec == NULL)
@@ -47,19 +103,13 @@ int ec_strvec_add(struct ec_strvec *strvec, const char *s)
 
 	strvec->vec = new_vec;
 
-	elt = ec_malloc(sizeof(*elt));
+	elt = __ec_strvec_elt(s);
 	if (elt == NULL)
 		return -1;
 
-	elt->str = ec_strdup(s);
-	if (elt->str == NULL) {
-		ec_free(elt);
-		return -1;
-	}
-	elt->refcnt = 1;
-
 	new_vec[strvec->len] = elt;
 	strvec->len++;
+
 	return 0;
 }
 
@@ -87,20 +137,14 @@ fail:
 
 int ec_strvec_del_last(struct ec_strvec *strvec)
 {
-	struct ec_strvec_elt *elt;
-
 	if (strvec->len == 0) {
 		errno = EINVAL;
 		return -1;
 	}
 
-	elt = strvec->vec[strvec->len - 1];
-	elt->refcnt--;
-	if (elt->refcnt == 0) {
-		ec_free(elt->str);
-		ec_free(elt);
-	}
+	__ec_strvec_elt_free(strvec->vec[strvec->len - 1]);
 	strvec->len--;
+
 	return 0;
 }
 
@@ -153,11 +197,7 @@ void ec_strvec_free(struct ec_strvec *strvec)
 
 	for (i = 0; i < ec_strvec_len(strvec); i++) {
 		elt = strvec->vec[i];
-		elt->refcnt--;
-		if (elt->refcnt == 0) {
-			ec_free(elt->str);
-			ec_free(elt);
-		}
+		__ec_strvec_elt_free(elt);
 	}
 
 	ec_free(strvec->vec);
@@ -177,6 +217,46 @@ const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
 	return strvec->vec[idx]->str;
 }
 
+const struct ec_keyval *ec_strvec_get_attrs(const struct ec_strvec *strvec,
+	size_t idx)
+{
+	if (strvec == NULL || idx >= strvec->len) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	return strvec->vec[idx]->attrs;
+}
+
+int ec_strvec_set_attrs(struct ec_strvec *strvec, size_t idx,
+			struct ec_keyval *attrs)
+{
+	struct ec_strvec_elt *elt;
+
+	if (strvec == NULL || idx >= strvec->len) {
+		errno = EINVAL;
+		goto fail;
+	}
+
+	elt = strvec->vec[idx];
+	if (elt->refcnt > 1) {
+		if (ec_strvec_set(strvec, idx, elt->str) < 0)
+			goto fail;
+		elt = strvec->vec[idx];
+	}
+
+	if (elt->attrs != NULL)
+		ec_keyval_free(elt->attrs);
+
+	elt->attrs = attrs;
+
+	return 0;
+
+fail:
+	ec_keyval_free(attrs);
+	return -1;
+}
+
 int ec_strvec_cmp(const struct ec_strvec *strvec1,
 		const struct ec_strvec *strvec2)
 {
@@ -237,6 +317,8 @@ static int ec_strvec_testcase(void)
 {
 	struct ec_strvec *strvec = NULL;
 	struct ec_strvec *strvec2 = NULL;
+	const struct ec_keyval *const_attrs = NULL;
+	struct ec_keyval *attrs = NULL;
 	FILE *f = NULL;
 	char *buf = NULL;
 	size_t buflen = 0;
@@ -393,7 +475,33 @@ static int ec_strvec_testcase(void)
 		EC_TEST_ERR("cannot create strvec from array\n");
 		goto fail;
 	}
+	attrs = ec_keyval();
+	if (attrs == NULL) {
+		EC_TEST_ERR("cannot create attrs\n");
+		goto fail;
+	}
+	if (ec_keyval_set(attrs, "key", "value", NULL) < 0) {
+		EC_TEST_ERR("cannot set attr\n");
+		goto fail;
+	}
+	if (ec_strvec_set_attrs(strvec, 1, attrs) < 0) {
+		attrs = NULL;
+		EC_TEST_ERR("cannot set attrs in strvec\n");
+		goto fail;
+	}
+	attrs = NULL;
+
 	ec_strvec_sort(strvec, NULL);
+
+	/* attrs are now at index 0 after sorting */
+	const_attrs = ec_strvec_get_attrs(strvec, 0);
+	if (const_attrs == NULL) {
+		EC_TEST_ERR("cannot get attrs\n");
+		goto fail;
+	}
+	testres |= EC_TEST_CHECK(
+		ec_keyval_has_key(const_attrs, "key"), "cannot get attrs key\n");
+
 	strvec2 = EC_STRVEC("a", "b", "c", "d", "e", "f");
 	if (strvec2 == NULL) {
 		EC_TEST_ERR("cannot create strvec from array\n");
@@ -411,6 +519,7 @@ static int ec_strvec_testcase(void)
 fail:
 	if (f != NULL)
 		fclose(f);
+	ec_keyval_free(attrs);
 	ec_strvec_free(strvec);
 	ec_strvec_free(strvec2);
 	free(buf);
diff --git a/libecoli/ecoli_strvec.h b/libecoli/ecoli_strvec.h
index 8e14973..cabe6b2 100644
--- a/libecoli/ecoli_strvec.h
+++ b/libecoli/ecoli_strvec.h
@@ -57,6 +57,20 @@ struct ec_strvec *ec_strvec(void);
 struct ec_strvec *ec_strvec_from_array(const char * const *strarr,
 	size_t n);
 
+/**
+ * Set a string in the vector at specified index.
+ *
+ * @param strvec
+ *   The pointer to the string vector.
+ * @param idx
+ *   The index of the string to set.
+ * @param s
+ *   The string to be set.
+ * @return
+ *   0 on success or -1 on error (errno is set).
+ */
+int ec_strvec_set(struct ec_strvec *strvec, size_t idx, const char *s);
+
 /**
  * Add a string in a vector.
  *
@@ -84,6 +98,8 @@ int ec_strvec_del_last(struct ec_strvec *strvec);
 /**
  * Duplicate a string vector.
  *
+ * Attributes are duplicated if any.
+ *
  * @param strvec
  *   The pointer to the string vector.
  * @return
@@ -94,6 +110,8 @@ struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec);
 /**
  * Duplicate a part of a string vector.
  *
+ * Attributes are duplicated if any.
+ *
  * @param strvec
  *   The pointer to the string vector.
  * @param off
@@ -137,6 +155,36 @@ size_t ec_strvec_len(const struct ec_strvec *strvec);
  */
 const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx);
 
+/**
+ * Get the attributes of a vector element.
+ *
+ * @param strvec
+ *   The pointer to the string vector.
+ * @param idx
+ *   The index of the string to get.
+ * @return
+ *   The read-only attributes (dictionnary) of the string at specified
+ *   index, or NULL if there is no attribute.
+ */
+const struct ec_keyval *ec_strvec_get_attrs(const struct ec_strvec *strvec,
+	size_t idx);
+
+/**
+ * Set the attributes of a vector element.
+ *
+ * @param strvec
+ *   The pointer to the string vector.
+ * @param idx
+ *   The index of the string to get.
+ * @param attrs
+ *   The attributes to be set.
+ * @return
+ *   0 on success, -1 on error (errno is set). On error, attrs
+ *   are freed and must not be used by the caller.
+ */
+int ec_strvec_set_attrs(struct ec_strvec *strvec, size_t idx,
+			struct ec_keyval *attrs);
+
 /**
  * Compare two string vectors
  *
@@ -153,6 +201,8 @@ int ec_strvec_cmp(const struct ec_strvec *strvec1,
 /**
  * Sort the string vector.
  *
+ * Attributes are not compared.
+ *
  * @param strvec
  *   The pointer to the first string vector.
  * @param str_cmp
-- 
2.39.5