From 6178ad591304a1d6de5eae9704bc8e0bd4218429 Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 21 Jun 2018 18:04:56 +0200 Subject: [PATCH] config for string node --- lib/ecoli_config.c | 187 +++++++++++++----------------------------- lib/ecoli_config.h | 98 +++++++++++++++------- lib/ecoli_node.c | 26 ++++++ lib/ecoli_node.h | 17 +++- lib/ecoli_node_many.c | 1 + lib/ecoli_node_str.c | 74 ++++++++++++++--- lib/ecoli_strvec.c | 35 ++++++++ lib/ecoli_strvec.h | 11 +++ lib/todo.txt | 1 + 9 files changed, 271 insertions(+), 179 deletions(-) diff --git a/lib/ecoli_config.c b/lib/ecoli_config.c index e2e4a3c..d973326 100644 --- a/lib/ecoli_config.c +++ b/lib/ecoli_config.c @@ -18,11 +18,6 @@ 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); diff --git a/lib/ecoli_config.h b/lib/ecoli_config.h index 42a71d5..44e32c8 100644 --- a/lib/ecoli_config.h +++ b/lib/ecoli_config.h @@ -5,9 +5,15 @@ #ifndef ECOLI_CONFIG_ #define ECOLI_CONFIG_ +#include #include #include +#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. * diff --git a/lib/ecoli_node.c b/lib/ecoli_node.c index bf57238..b710a40 100644 --- a/lib/ecoli_node.c +++ b/lib/ecoli_node.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -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) { diff --git a/lib/ecoli_node.h b/lib/ecoli_node.h index b6380b0..1035778 100644 --- a/lib/ecoli_node.h +++ b/lib/ecoli_node.h @@ -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); diff --git a/lib/ecoli_node_many.c b/lib/ecoli_node_many.c index 69e8e6c..60256ef 100644 --- a/lib/ecoli_node_many.c +++ b/lib/ecoli_node_many.c @@ -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); diff --git a/lib/ecoli_node_str.c b/lib/ecoli_node_str.c index 6f373e6..28c69b9 100644 --- a/lib/ecoli_node_str.c +++ b/lib/ecoli_node_str.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -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) diff --git a/lib/ecoli_strvec.c b/lib/ecoli_strvec.c index 132183d..d8f4a86 100644 --- a/lib/ecoli_strvec.c +++ b/lib/ecoli_strvec.c @@ -2,6 +2,7 @@ * Copyright 2016, Olivier MATZ */ +#define _GNU_SOURCE /* qsort_r */ #include #include #include @@ -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: diff --git a/lib/ecoli_strvec.h b/lib/ecoli_strvec.h index 3532836..8e14973 100644 --- a/lib/ecoli_strvec.h +++ b/lib/ecoli_strvec.h @@ -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. * diff --git a/lib/todo.txt b/lib/todo.txt index 4517a79..f407f27 100644 --- a/lib/todo.txt +++ b/lib/todo.txt @@ -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 ============ -- 2.20.1