config for string node
authorOlivier Matz <zer0@droids-corp.org>
Thu, 21 Jun 2018 16:04:56 +0000 (18:04 +0200)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 21 Jun 2018 16:04:56 +0000 (18:04 +0200)
lib/ecoli_config.c
lib/ecoli_config.h
lib/ecoli_node.c
lib/ecoli_node.h
lib/ecoli_node_many.c
lib/ecoli_node_str.c
lib/ecoli_strvec.c
lib/ecoli_strvec.h
lib/todo.txt

index e2e4a3c..d973326 100644 (file)
 
 EC_LOG_TYPE_REGISTER(config);
 
-#ifndef EC_COUNT_OF //XXX
-#define EC_COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / \
-               ((size_t)(!(sizeof(x) % sizeof(0[x])))))
-#endif
-
 static int
 __ec_config_dump(FILE *out, const char *key, const struct ec_config *config,
        size_t indent);
@@ -287,31 +282,8 @@ fail:
        return NULL;
 }
 
-static int
-ec_config_set_schema(struct ec_config *dict,
-               const struct ec_config_schema *schema,
-               size_t schema_len)
-{
-       if (dict->type != EC_CONFIG_TYPE_DICT) {
-               errno = EINVAL;
-               goto fail;
-       }
-
-       if (ec_config_schema_validate(schema, schema_len) < 0)
-               goto fail;
-
-       dict->schema = schema;
-       dict->schema_len = schema_len;
-
-       return 0;
-
-fail:
-       return -1;
-}
-
 struct ec_config *
-ec_config_dict(const struct ec_config_schema *schema,
-       size_t schema_len)
+ec_config_dict(void)
 {
        struct ec_config *value = NULL;
        struct ec_keyval *dict = NULL;
@@ -327,9 +299,6 @@ ec_config_dict(const struct ec_config_schema *schema,
        value->type = EC_CONFIG_TYPE_DICT;
        value->dict = dict;
 
-       if (ec_config_set_schema(value, schema, schema_len) < 0)
-               goto fail;
-
        return value;
 
 fail:
@@ -553,19 +522,16 @@ fail:
 }
 
 int
-ec_config_validate(const struct ec_config *dict)
+ec_config_validate(const struct ec_config *dict,
+               const struct ec_config_schema *schema,
+               size_t schema_len)
 {
-       if (dict->type != EC_CONFIG_TYPE_DICT) {
+       if (dict->type != EC_CONFIG_TYPE_DICT || schema == NULL) {
                errno = EINVAL;
                goto fail;
        }
-       if (dict->schema == NULL || dict->schema_len == 0) {
-               errno = ENOENT;
-               goto fail;
-       }
 
-       if (ec_config_dict_validate(dict->dict, dict->schema,
-                                       dict->schema_len) < 0)
+       if (ec_config_dict_validate(dict->dict, schema, schema_len) < 0)
                goto fail;
 
        return 0
@@ -575,7 +541,7 @@ fail:
 }
 
 struct ec_config *
-ec_config_get(struct ec_config *config, const char *key)
+ec_config_get(const struct ec_config *config, const char *key)
 {
        if (config == NULL)
                return NULL;
@@ -602,7 +568,7 @@ ec_config_list_next(struct ec_config *list, struct ec_config *config)
 }
 
 /* value is consumed */
-int ec_config_set(struct ec_config *config, const char *key,
+int ec_config_dict_set(struct ec_config *config, const char *key,
                struct ec_config *value)
 {
        void (*free_cb)(struct ec_config *) = ec_config_free;
@@ -624,12 +590,26 @@ fail:
        return -1;
 }
 
+int ec_config_dict_del(struct ec_config *config, const char *key)
+{
+       if (config == NULL || key == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+       if (config->type != EC_CONFIG_TYPE_DICT) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       return ec_keyval_del(config->dict, key);
+}
+
 /* value is consumed */
 int
-ec_config_add(struct ec_config *list,
+ec_config_list_add(struct ec_config *list,
                struct ec_config *value)
 {
-       if (list->type != EC_CONFIG_TYPE_LIST) {
+       if (list == NULL || list->type != EC_CONFIG_TYPE_LIST) {
                errno = EINVAL;
                goto fail;
        }
@@ -643,10 +623,16 @@ fail:
        return -1;
 }
 
-void ec_config_del(struct ec_config *list, struct ec_config *config)
+int ec_config_list_del(struct ec_config *list, struct ec_config *config)
 {
+       if (list == NULL || list->type != EC_CONFIG_TYPE_LIST) {
+               errno = EINVAL;
+               return -1;
+       }
+
        TAILQ_REMOVE(&list->list, config, next);
        ec_config_free(config);
+       return 0;
 }
 
 static int
@@ -846,11 +832,11 @@ static int ec_config_testcase(void)
        ec_config_schema_dump(stdout, sch_baseconfig,
                        EC_COUNT_OF(sch_baseconfig));
 
-       config = ec_config_dict(sch_baseconfig, EC_COUNT_OF(sch_baseconfig));
+       config = ec_config_dict();
        if (config == NULL)
                goto fail;
 
-       ret = ec_config_set(config, "my_bool", ec_config_bool(true));
+       ret = ec_config_dict_set(config, "my_bool", ec_config_bool(true));
        testres |= EC_TEST_CHECK(ret == 0, "cannot set boolean");
        value = ec_config_get(config, "my_bool");
        testres |= EC_TEST_CHECK(
@@ -859,7 +845,7 @@ static int ec_config_testcase(void)
                value->boolean == true,
                "unexpected boolean value");
 
-       ret = ec_config_set(config, "my_int", ec_config_i64(1234));
+       ret = ec_config_dict_set(config, "my_int", ec_config_i64(1234));
        testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
        value = ec_config_get(config, "my_int");
        testres |= EC_TEST_CHECK(
@@ -869,10 +855,11 @@ static int ec_config_testcase(void)
                "unexpected int value");
 
        testres |= EC_TEST_CHECK(
-               ec_config_validate(config) == 0,
+               ec_config_validate(config, sch_baseconfig,
+                               EC_COUNT_OF(sch_baseconfig)) == 0,
                "cannot validate config\n");
 
-       ret = ec_config_set(config, "my_string", ec_config_string("toto"));
+       ret = ec_config_dict_set(config, "my_string", ec_config_string("toto"));
        testres |= EC_TEST_CHECK(ret == 0, "cannot set string");
        value = ec_config_get(config, "my_string");
        testres |= EC_TEST_CHECK(
@@ -885,11 +872,11 @@ static int ec_config_testcase(void)
        if (list == NULL)
                goto fail;
 
-       subconfig = ec_config_dict(sch_dict, EC_COUNT_OF(sch_dict));
+       subconfig = ec_config_dict();
        if (subconfig == NULL)
                goto fail;
 
-       ret = ec_config_set(subconfig, "my_int", ec_config_i64(1));
+       ret = ec_config_dict_set(subconfig, "my_int", ec_config_i64(1));
        testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
        value = ec_config_get(subconfig, "my_int");
        testres |= EC_TEST_CHECK(
@@ -898,7 +885,7 @@ static int ec_config_testcase(void)
                value->i64 == 1,
                "unexpected int value");
 
-       ret = ec_config_set(subconfig, "my_int2", ec_config_i64(2));
+       ret = ec_config_dict_set(subconfig, "my_int2", ec_config_i64(2));
        testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
        value = ec_config_get(subconfig, "my_int2");
        testres |= EC_TEST_CHECK(
@@ -908,18 +895,19 @@ static int ec_config_testcase(void)
                "unexpected int value");
 
        testres |= EC_TEST_CHECK(
-               ec_config_validate(subconfig) == 0,
+               ec_config_validate(subconfig, sch_dict,
+                               EC_COUNT_OF(sch_dict)) == 0,
                "cannot validate subconfig\n");
 
-       ret = ec_config_add(list, subconfig);
+       ret = ec_config_list_add(list, subconfig);
        subconfig = NULL; /* freed */
        testres |= EC_TEST_CHECK(ret == 0, "cannot add in list");
 
-       subconfig = ec_config_dict(sch_dict, EC_COUNT_OF(sch_dict));
+       subconfig = ec_config_dict();
        if (subconfig == NULL)
                goto fail;
 
-       ret = ec_config_set(subconfig, "my_int", ec_config_i64(3));
+       ret = ec_config_dict_set(subconfig, "my_int", ec_config_i64(3));
        testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
        value = ec_config_get(subconfig, "my_int");
        testres |= EC_TEST_CHECK(
@@ -928,7 +916,7 @@ static int ec_config_testcase(void)
                value->i64 == 3,
                "unexpected int value");
 
-       ret = ec_config_set(subconfig, "my_int2", ec_config_i64(4));
+       ret = ec_config_dict_set(subconfig, "my_int2", ec_config_i64(4));
        testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
        value = ec_config_get(subconfig, "my_int2");
        testres |= EC_TEST_CHECK(
@@ -938,19 +926,21 @@ static int ec_config_testcase(void)
                "unexpected int value");
 
        testres |= EC_TEST_CHECK(
-               ec_config_validate(subconfig) == 0,
+               ec_config_validate(subconfig, sch_dict,
+                               EC_COUNT_OF(sch_dict)) == 0,
                "cannot validate subconfig\n");
 
-       ret = ec_config_add(list, subconfig);
+       ret = ec_config_list_add(list, subconfig);
        subconfig = NULL; /* freed */
        testres |= EC_TEST_CHECK(ret == 0, "cannot add in list");
 
-       ret = ec_config_set(config, "my_dictlist", list);
+       ret = ec_config_dict_set(config, "my_dictlist", list);
        list = NULL;
        testres |= EC_TEST_CHECK(ret == 0, "cannot set list");
 
        testres |= EC_TEST_CHECK(
-               ec_config_validate(config) == 0,
+               ec_config_validate(config, sch_baseconfig,
+                               EC_COUNT_OF(sch_baseconfig)) == 0,
                "cannot validate config\n");
 
        list_ = ec_config_get(config, "my_dictlist");
@@ -963,79 +953,12 @@ static int ec_config_testcase(void)
        ec_config_dump(stdout, config);
 
        /* remove the first element */
-       ec_config_del(list_, ec_config_list_first(list_));
+       ec_config_list_del(list_, ec_config_list_first(list_));
        testres |= EC_TEST_CHECK(
-               ec_config_validate(config) == 0,
+               ec_config_validate(config, sch_baseconfig,
+                               EC_COUNT_OF(sch_baseconfig)) == 0,
                "cannot validate config\n");
 
-#if 0
-       value.type = EC_CONFIG_TYPE_NODE;
-       value.node = ec_node_clone(node);
-       ret = ec_config_set(config, "node", value);
-       testres |= EC_TEST_CHECK(ret == 0, "cannot set node");
-       pvalue = ec_config_get(config, "node");
-       testres |= EC_TEST_CHECK(
-               pvalue != NULL &&
-               ec_config_cmp(pvalue, &value) == 0,
-               "unexpected node value");
-
-       subconfig = ec_config(dict, EC_COUNT_OF(dict));
-       if (subconfig == NULL)
-               goto fail;
-
-       value.type = EC_CONFIG_TYPE_INT64;
-       value.i64 = 4321;
-       ret = ec_config_set(subconfig, "int", value);
-       testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
-       pvalue = ec_config_get(subconfig, "int");
-       testres |= EC_TEST_CHECK(
-               pvalue != NULL &&
-               ec_config_cmp(pvalue, &value) == 0,
-               "unexpected int value");
-
-       value.type = EC_CONFIG_TYPE_DICT;
-       value.dict = subconfig;
-       subconfig = NULL; /* will be freed when freeing config */
-       ret = ec_config_set(config, "dict", value);
-       testres |= EC_TEST_CHECK(ret == 0, "cannot set dict");
-       pvalue = ec_config_get(config, "dict");
-       testres |= EC_TEST_CHECK(
-               pvalue != NULL &&
-               ec_config_cmp(pvalue, &value) == 0,
-               "unexpected dict value");
-
-       value.type = EC_CONFIG_TYPE_INT64;
-       value.i64 = 4321;
-       pvalue = ec_config_get(
-               ec_config_get(config, "dict")->dict, "int");
-       testres |= EC_TEST_CHECK(
-               pvalue != NULL &&
-               ec_config_cmp(pvalue, &value) == 0,
-               "unexpected int value");
-
-       value.type = EC_CONFIG_TYPE_INT64;
-       value.i64 = 1;
-       ret = ec_config_add(config, "intlist", value);
-       testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
-       value.type = EC_CONFIG_TYPE_INT64;
-       value.i64 = 2;
-       ret = ec_config_add(config, "intlist", value);
-       testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
-       value.type = EC_CONFIG_TYPE_INT64;
-       value.i64 = 3;
-       ret = ec_config_add(config, "intlist", value);
-       testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
-
-       value.type = EC_CONFIG_TYPE_INT64;
-       value.i64 = 4321;
-       ret = ec_config_set(config, "invalid", value);
-       testres |= EC_TEST_CHECK(ret < 0,
-                       "should not be able to set invalid key");
-       pvalue = ec_config_get(config, "invalid");
-       testres |= EC_TEST_CHECK(pvalue == NULL,
-                       "invalid key returned a value");
-
-#endif
        ec_config_dump(stdout, config);
 
        ec_config_free(list);
index 42a71d5..44e32c8 100644 (file)
@@ -5,9 +5,15 @@
 #ifndef ECOLI_CONFIG_
 #define ECOLI_CONFIG_
 
+#include <sys/queue.h>
 #include <stdbool.h>
 #include <stdint.h>
 
+#ifndef EC_COUNT_OF //XXX
+#define EC_COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / \
+               ((size_t)(!(sizeof(x) % sizeof(0[x])))))
+#endif
+
 struct ec_config;
 struct ec_keyval;
 
@@ -70,10 +76,6 @@ struct ec_config {
         * Next in list, only valid if type is list.
         */
        TAILQ_ENTRY(ec_config) next;
-
-       /** Associated schema. Only valid if type is dict. */
-       const struct ec_config_schema *schema;
-       size_t schema_len;           /**< Schema length. */
 };
 
 /* schema */
@@ -164,17 +166,11 @@ struct ec_config *ec_config_node(struct ec_node *node);
 /**
  * Create a hash table configuration value.
  *
- * @param schema
- *   Optional pointer to the first element of the schema array. Set
- *   it to NULL if no schema should be attached.
- * @param schema_len
- *   Length of the schema array. Set to 0 if no schema.
  * @return
- *   The configuration object containing an empty hash table, or NULL on
+ *   A configuration object containing an empty hash table, or NULL on
  *   error (errno is set).
  */
-struct ec_config *ec_config_dict(const struct ec_config_schema *schema,
-                               size_t schema_len);
+struct ec_config *ec_config_dict(void);
 
 /**
  * Create a list configuration value.
@@ -193,20 +189,72 @@ struct ec_config *ec_config_list(void);
  * @param value
  *   The value configuration to add in the list. The value object
  *   will be freed when freeing the list object. On error, the
- *   value object is freed.
+ *   value object is also freed.
  * @return
  *   0 on success, else -1 (errno is set).
  */
-int ec_config_add(struct ec_config *list, struct ec_config *value);
+int ec_config_list_add(struct ec_config *list, struct ec_config *value);
 
-void ec_config_free(struct ec_config *config);
+/**
+ * Remove an element from a list.
+ *
+ * The element is freed and should not be accessed.
+ *
+ * @param list
+ *   The list configuration.
+ * @param config
+ *   The element to remove from the list.
+ * @return
+ *   0 on success, -1 on error (errno is set).
+ */
+int ec_config_list_del(struct ec_config *list, struct ec_config *config);
 
-int ec_config_validate(const struct ec_config *dict);
+/**
+ * Validate a configuration.
+ *
+ * @param dict
+ *   A hash table configuration to validate.
+ * @param schema
+ *   Pointer to the first element of the schema array.
+ * @param schema_len
+ *   Length of the schema array.
+ * @return
+ *   0 on success, -1 on error (errno is set).
+ */
+int ec_config_validate(const struct ec_config *dict,
+               const struct ec_config_schema *schema,
+               size_t schema_len);
 
-/* value is consumed */
-int ec_config_set(struct ec_config *dict, const char *key,
+/**
+ * Set a value in a hash table configuration
+ *
+ * @param dict
+ *   A hash table configuration to validate.
+ * @param key
+ *   The key to update.
+ * @param value
+ *   The value to set. The value object will be freed when freeing the
+ *   dict object. On error, the value object is also freed.
+ * @return
+ *   0 on success, -1 on error (errno is set).
+ */
+int ec_config_dict_set(struct ec_config *dict, const char *key,
                struct ec_config *value);
 
+/**
+ * Remove an element from a hash table configuration.
+ *
+ * The element is freed and should not be accessed.
+ *
+ * @param dict
+ *   A hash table configuration to validate.
+ * @param key
+ *   The key of the configuration to delete.
+ * @return
+ *   0 on success, -1 on error (errno is set).
+ */
+int ec_config_dict_del(struct ec_config *config, const char *key);
+
 /**
  * Compare two configurations.
  */
@@ -216,7 +264,7 @@ int ec_config_cmp(const struct ec_config *config1,
 /**
  * Get configuration value.
  */
-struct ec_config *ec_config_get(struct ec_config *config,
+struct ec_config *ec_config_get(const struct ec_config *config,
                                const char *key);
 
 /**
@@ -249,18 +297,6 @@ struct ec_config *ec_config_list_first(struct ec_config *list);
 struct ec_config *
 ec_config_list_next(struct ec_config *list, struct ec_config *config);
 
-/**
- * Remove an element from a list.
- *
- * The element is freed and should not be accessed.
- *
- * @param list
- *   The list configuration.
- * @param config
- *   The element to remove from the list.
- */
-void ec_config_del(struct ec_config *list, struct ec_config *config);
-
 /**
  * Free a configuration.
  *
index bf57238..b710a40 100644 (file)
@@ -14,6 +14,7 @@
 #include <ecoli_strvec.h>
 #include <ecoli_keyval.h>
 #include <ecoli_log.h>
+#include <ecoli_config.h>
 #include <ecoli_test.h>
 #include <ecoli_node.h>
 
@@ -138,6 +139,7 @@ void ec_node_free(struct ec_node *node)
        ec_free(node->id);
        ec_free(node->desc);
        ec_keyval_free(node->attrs);
+       ec_config_free(node->config);
        ec_free(node);
 }
 
@@ -189,6 +191,30 @@ fail:
        return -1;
 }
 
+int
+ec_node_set_config(struct ec_node *node, struct ec_config *config)
+{
+       if (node->type->schema == NULL) {
+               errno = EINVAL;
+               goto fail;
+       }
+       if (ec_config_validate(config, node->type->schema,
+                               node->type->schema_len) < 0)
+               goto fail;
+       if (node->type->set_config != NULL) {
+               if (node->type->set_config(node, config) < 0)
+                       goto fail;
+       }
+
+       ec_config_free(node->config);
+       node->config = config;
+
+       return 0;
+
+fail:
+       return -1;
+}
+
 #if 0 /* later */
 int ec_node_del_child(struct ec_node *node, struct ec_node *child)
 {
index b6380b0..1035778 100644 (file)
@@ -55,6 +55,8 @@ struct ec_parse;
 struct ec_comp;
 struct ec_strvec;
 struct ec_keyval;
+struct ec_config;
+struct ec_config_schema;
 
 #define EC_NODE_TYPE_REGISTER(t)                                       \
        static void ec_node_init_##t(void);                             \
@@ -69,9 +71,8 @@ struct ec_keyval;
 
 TAILQ_HEAD(ec_node_type_list, ec_node_type);
 
-/* return 0 on success, else -errno. */
-typedef int (*ec_node_build_t)(struct ec_node *node);
-
+typedef int (*ec_node_set_config_t)(struct ec_node *node,
+                               const struct ec_config *config);
 typedef int (*ec_node_parse_t)(const struct ec_node *node,
                        struct ec_parse *state,
                        const struct ec_strvec *strvec);
@@ -88,6 +89,10 @@ typedef void (*ec_node_free_priv_t)(struct ec_node *);
 struct ec_node_type {
        TAILQ_ENTRY(ec_node_type) next;  /**< Next in list. */
        const char *name;                /**< Node type name. */
+       /** Generic configuration schema. */
+       const struct ec_config_schema *schema;
+       size_t schema_len;               /**< Number of elts in schema array. */
+       ec_node_set_config_t set_config; /* validate/ack a config change */
        ec_node_parse_t parse;
        ec_node_complete_t complete;
        ec_node_desc_t desc;
@@ -124,6 +129,7 @@ void ec_node_type_dump(FILE *out);
 
 struct ec_node {
        const struct ec_node_type *type;
+       struct ec_config *config;    /**< Generic configuration. */
        char *id;
        char *desc;
        struct ec_keyval *attrs;
@@ -142,6 +148,11 @@ struct ec_node *ec_node(const char *typename, const char *id);
 struct ec_node *ec_node_clone(struct ec_node *node);
 void ec_node_free(struct ec_node *node);
 
+/* set configuration of a node
+ * after a call to this function, the config is owned by the node and
+ * must not be used by the caller */
+int ec_node_set_config(struct ec_node *node, struct ec_config *config);
+
 size_t ec_node_get_children_count(const struct ec_node *node);
 struct ec_node *
 ec_node_get_child(const struct ec_node *node, size_t i);
index 69e8e6c..60256ef 100644 (file)
@@ -179,6 +179,7 @@ struct ec_node *ec_node_many(const char *id, struct ec_node *child,
        if (child == NULL)
                return NULL;
 
+       // XXX ec_node_add_child()
        node = (struct ec_node_many *)__ec_node(&ec_node_many_type, id);
        if (node == NULL) {
                ec_node_free(child);
index 6f373e6..28c69b9 100644 (file)
@@ -12,6 +12,7 @@
 #include <ecoli_test.h>
 #include <ecoli_strvec.h>
 #include <ecoli_node.h>
+#include <ecoli_config.h>
 #include <ecoli_parse.h>
 #include <ecoli_complete.h>
 #include <ecoli_node_str.h>
@@ -34,6 +35,11 @@ ec_node_str_parse(const struct ec_node *gen_node,
 
        (void)state;
 
+       if (node->string == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
        if (ec_strvec_len(strvec) == 0)
                return EC_PARSE_NOMATCH;
 
@@ -87,8 +93,47 @@ static void ec_node_str_free_priv(struct ec_node *gen_node)
        ec_free(node->string);
 }
 
+static const struct ec_config_schema ec_node_str_schema[] = {
+       {
+               .key = "string",
+               .desc = "The string to match.",
+               .type = EC_CONFIG_TYPE_STRING,
+       },
+};
+
+static int ec_node_str_set_config(struct ec_node *gen_node,
+                               const struct ec_config *config)
+{
+       struct ec_node_str *node = (struct ec_node_str *)gen_node;
+       const struct ec_config *value = NULL;
+       char *s = NULL;
+
+       value = ec_config_get(config, "string");
+       if (value == NULL) {
+               errno = EINVAL;
+               goto fail;
+       }
+
+       s = ec_strdup(value->string);
+       if (s == NULL)
+               goto fail;
+
+       ec_free(node->string);
+       node->string = s;
+       node->len = strlen(node->string);
+
+       return 0;
+
+fail:
+       ec_free(s);
+       return -1;
+}
+
 static struct ec_node_type ec_node_str_type = {
        .name = "str",
+       .schema = ec_node_str_schema,
+       .schema_len = EC_COUNT_OF(ec_node_str_schema),
+       .set_config = ec_node_str_set_config,
        .parse = ec_node_str_parse,
        .complete = ec_node_str_complete,
        .desc = ec_node_str_desc,
@@ -100,28 +145,31 @@ EC_NODE_TYPE_REGISTER(ec_node_str_type);
 
 int ec_node_str_set_str(struct ec_node *gen_node, const char *str)
 {
-       struct ec_node_str *node = (struct ec_node_str *)gen_node;
-       char *s = NULL;
-       int ret;
+       struct ec_config *config = NULL;
 
-       ret = ec_node_check_type(gen_node, &ec_node_str_type);
-       if (ret < 0)
-               return ret;
+       if (ec_node_check_type(gen_node, &ec_node_str_type) < 0)
+               goto fail;
 
        if (str == NULL) {
                errno = EINVAL;
-               return -1;
+               goto fail;
        }
 
-       s = ec_strdup(str);
-       if (s == NULL)
-               return -1;
+       config = ec_config_dict();
+       if (config == NULL)
+               goto fail;
 
-       ec_free(node->string);
-       node->string = s;
-       node->len = strlen(node->string);
+       if (ec_config_dict_set(config, "string", ec_config_string(str)) < 0)
+               goto fail;
+
+       if (ec_node_set_config(gen_node, config) < 0)
+               goto fail;
 
        return 0;
+
+fail:
+       ec_config_free(config);
+       return -1;
 }
 
 struct ec_node *ec_node_str(const char *id, const char *str)
index 132183d..d8f4a86 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
  */
 
+#define _GNU_SOURCE /* qsort_r */
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -193,6 +194,24 @@ int ec_strvec_cmp(const struct ec_strvec *strvec1,
        return 0;
 }
 
+static int
+cmp_vec_elt(const void *p1, const void *p2, void *arg)
+{
+       int (*str_cmp)(const char *s1, const char *s2) = arg;
+       const struct ec_strvec_elt * const *e1 = p1, * const *e2 = p2;
+
+       return str_cmp((*e1)->str, (*e2)->str);
+}
+
+void ec_strvec_sort(struct ec_strvec *strvec,
+       int (*str_cmp)(const char *s1, const char *s2))
+{
+       if (str_cmp == NULL)
+               str_cmp = strcmp;
+       qsort_r(strvec->vec, ec_strvec_len(strvec),
+               sizeof(*strvec->vec), cmp_vec_elt, str_cmp);
+}
+
 void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec)
 {
        size_t i;
@@ -368,6 +387,22 @@ static int ec_strvec_testcase(void)
 
        ec_strvec_free(strvec);
 
+       strvec = EC_STRVEC("e", "a", "f", "d", "b", "c");
+       if (strvec == NULL) {
+               EC_TEST_ERR("cannot create strvec from array\n");
+               goto fail;
+       }
+       ec_strvec_sort(strvec, NULL);
+       strvec2 = EC_STRVEC("a", "b", "c", "d", "e", "f");
+       if (strvec2 == NULL) {
+               EC_TEST_ERR("cannot create strvec from array\n");
+               goto fail;
+       }
+       testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0,
+               "strvec and strvec2 should be equal\n");
+       ec_strvec_free(strvec2);
+       ec_strvec_free(strvec);
+
        return testres;
 
 fail:
index 3532836..8e14973 100644 (file)
@@ -150,6 +150,17 @@ const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx);
 int ec_strvec_cmp(const struct ec_strvec *strvec1,
                const struct ec_strvec *strvec2);
 
+/**
+ * Sort the string vector.
+ *
+ * @param strvec
+ *   The pointer to the first string vector.
+ * @param str_cmp
+ *   The sort function to use. If NULL, use strcmp.
+ */
+void ec_strvec_sort(struct ec_strvec *strvec,
+               int (*str_cmp)(const char *s1, const char *s2));
+
 /**
  * Dump a string vector.
  *
index 4517a79..f407f27 100644 (file)
@@ -42,6 +42,7 @@ X save node path in completion to fix help string
 X use vec for strvec
 - ELOOP in case of loop
 - remove weakref?
+- sh_lex to provide offsets in attributes
 
 dependencies
 ============