save
[protos/libecoli.git] / lib / ecoli_config.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2018, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <sys/queue.h>
6 #include <stddef.h>
7 #include <stdio.h>
8 #include <errno.h>
9 #include <inttypes.h>
10
11 #include <ecoli_string.h>
12 #include <ecoli_malloc.h>
13 #include <ecoli_keyval.h>
14 #include <ecoli_node.h>
15 #include <ecoli_log.h>
16 #include <ecoli_test.h>
17 #include <ecoli_config.h>
18
19 EC_LOG_TYPE_REGISTER(config);
20
21 #ifndef EC_COUNT_OF //XXX
22 #define EC_COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / \
23                 ((size_t)(!(sizeof(x) % sizeof(0[x])))))
24 #endif
25
26 static int
27 __ec_config_dump(FILE *out, const char *key, const struct ec_config *config,
28         size_t indent);
29 static int
30 ec_config_dict_validate(const struct ec_keyval *dict,
31                         const struct ec_config_schema *schema,
32                         size_t schema_len);
33
34 /* return ec_value type as a string */
35 static const char *
36 ec_config_type_str(enum ec_config_type type)
37 {
38         switch (type) {
39         case EC_CONFIG_TYPE_BOOL: return "bool";
40         case EC_CONFIG_TYPE_INT64: return "int64";
41         case EC_CONFIG_TYPE_UINT64: return "uint64";
42         case EC_CONFIG_TYPE_STRING: return "string";
43         case EC_CONFIG_TYPE_NODE: return "node";
44         case EC_CONFIG_TYPE_LIST: return "list";
45         case EC_CONFIG_TYPE_DICT: return "dict";
46         default: return "unknown";
47         }
48 }
49
50 static int
51 __ec_config_schema_validate(const struct ec_config_schema *schema,
52                         size_t schema_len, enum ec_config_type type)
53 {
54         size_t i, j;
55         int ret;
56
57         if (type == EC_CONFIG_TYPE_LIST) {
58                 if (schema[0].key != NULL) {
59                         errno = EINVAL;
60                         EC_LOG(EC_LOG_ERR, "list schema key must be NULL\n");
61                         return -1;
62                 }
63         } else if (type == EC_CONFIG_TYPE_DICT) {
64                 for (i = 0; i < schema_len; i++) {
65                         if (schema[i].key == NULL) {
66                                 errno = EINVAL;
67                                 EC_LOG(EC_LOG_ERR, "dict schema key should not be NULL\n");
68                                 return -1;
69                         }
70                 }
71         } else {
72                 errno = EINVAL;
73                 EC_LOG(EC_LOG_ERR, "invalid schema type\n");
74                 return -1;
75         }
76
77         for (i = 0; i < schema_len; i++) {
78                 /* check for duplicate name if more than one element */
79                 for (j = i + 1; j < schema_len; j++) {
80                         if (!strcmp(schema[i].key, schema[j].key)) {
81                                 errno = EEXIST;
82                                 EC_LOG(EC_LOG_ERR,
83                                         "duplicate key <%s> in schema\n",
84                                         schema[i].key);
85                                 return -1;
86                         }
87                 }
88
89                 switch (schema[i].type) {
90                 case EC_CONFIG_TYPE_BOOL:
91                 case EC_CONFIG_TYPE_INT64:
92                 case EC_CONFIG_TYPE_UINT64:
93                 case EC_CONFIG_TYPE_STRING:
94                 case EC_CONFIG_TYPE_NODE:
95                         if (schema[i].subschema != NULL ||
96                                         schema[i].subschema_len != 0) {
97                                 errno = EINVAL;
98                                 EC_LOG(EC_LOG_ERR,
99                                         "key <%s> should not have subtype/subschema\n",
100                                         schema[i].key);
101                                 return -1;
102                         }
103                         break;
104                 case EC_CONFIG_TYPE_LIST:
105                         if (schema[i].subschema == NULL ||
106                                         schema[i].subschema_len != 1) {
107                                 errno = EINVAL;
108                                 EC_LOG(EC_LOG_ERR,
109                                         "key <%s> must have subschema of length 1\n",
110                                         schema[i].key);
111                                 return -1;
112                         }
113                         break;
114                 case EC_CONFIG_TYPE_DICT:
115                         if (schema[i].subschema == NULL ||
116                                         schema[i].subschema_len == 0) {
117                                 errno = EINVAL;
118                                 EC_LOG(EC_LOG_ERR,
119                                         "key <%s> must have subschema\n",
120                                         schema[i].key);
121                                 return -1;
122                         }
123                         break;
124                 default:
125                         EC_LOG(EC_LOG_ERR, "invalid type for key <%s>\n",
126                                 schema[i].key);
127                         errno = EINVAL;
128                         return -1;
129                 }
130
131                 if (schema[i].subschema == NULL)
132                         continue;
133
134                 ret = __ec_config_schema_validate(schema[i].subschema,
135                                                 schema[i].subschema_len,
136                                                 schema[i].type);
137                 if (ret < 0) {
138                         EC_LOG(EC_LOG_ERR, "cannot parse subschema %s%s\n",
139                                 schema[i].key ? "key=" : "",
140                                 schema[i].key ? : "");
141                         return ret;
142                 }
143         }
144
145         return 0;
146 }
147
148 int
149 ec_config_schema_validate(const struct ec_config_schema *schema,
150                         size_t schema_len)
151 {
152         return __ec_config_schema_validate(schema, schema_len,
153                                         EC_CONFIG_TYPE_DICT);
154 }
155
156 static void
157 __ec_config_schema_dump(FILE *out, const struct ec_config_schema *schema,
158                         size_t schema_len, size_t indent)
159 {
160         size_t i;
161
162         for (i = 0; i < schema_len; i++) {
163                 fprintf(out, "%*s" "%s%s(%s): %s\n",
164                         (int)indent * 4, "",
165                         schema[i].key ? : "",
166                         schema[i].key ? " ": "",
167                         ec_config_type_str(schema[i].type),
168                         schema[i].desc);
169                 if (schema[i].subschema == NULL)
170                         continue;
171                 __ec_config_schema_dump(out, schema[i].subschema,
172                                         schema[i].subschema_len, indent + 1);
173         }
174 }
175
176 void
177 ec_config_schema_dump(FILE *out, const struct ec_config_schema *schema,
178                 size_t schema_len)
179 {
180         fprintf(out, "------------------- schema dump:\n");
181
182         if (schema == NULL || schema_len == 0) {
183                 fprintf(out, "no schema\n");
184                 return;
185         }
186
187         __ec_config_schema_dump(out, schema, schema_len, 0);
188 }
189
190 struct ec_config *
191 ec_config_bool(bool boolean)
192 {
193         struct ec_config *value = NULL;
194
195         value = ec_calloc(1, sizeof(*value));
196         if (value == NULL)
197                 return NULL;
198
199         value->type = EC_CONFIG_TYPE_BOOL;
200         value->boolean = boolean;
201
202         return value;
203 }
204
205 struct ec_config *
206 ec_config_i64(int64_t i64)
207 {
208         struct ec_config *value = NULL;
209
210         value = ec_calloc(1, sizeof(*value));
211         if (value == NULL)
212                 return NULL;
213
214         value->type = EC_CONFIG_TYPE_INT64;
215         value->i64 = i64;
216
217         return value;
218 }
219
220 struct ec_config *
221 ec_config_u64(uint64_t u64)
222 {
223         struct ec_config *value = NULL;
224
225         value = ec_calloc(1, sizeof(*value));
226         if (value == NULL)
227                 return NULL;
228
229         value->type = EC_CONFIG_TYPE_UINT64;
230         value->u64 = u64;
231
232         return value;
233 }
234
235 /* duplicate string */
236 struct ec_config *
237 ec_config_string(const char *string)
238 {
239         struct ec_config *value = NULL;
240         char *s = NULL;
241
242         if (string == NULL)
243                 goto fail;
244
245         s = ec_strdup(string);
246         if (s == NULL)
247                 goto fail;
248
249         value = ec_calloc(1, sizeof(*value));
250         if (value == NULL)
251                 goto fail;
252
253         value->type = EC_CONFIG_TYPE_STRING;
254         value->string = s;
255
256         return value;
257
258 fail:
259         ec_free(value);
260         ec_free(s);
261         return NULL;
262 }
263
264 /* "consume" the node */
265 struct ec_config *
266 ec_config_node(struct ec_node *node)
267 {
268         struct ec_config *value = NULL;
269
270         if (node == NULL)
271                 goto fail;
272
273         value = ec_calloc(1, sizeof(*value));
274         if (value == NULL)
275                 goto fail;
276
277         value->type = EC_CONFIG_TYPE_NODE;
278         value->node = node;
279
280         return value;
281
282 fail:
283         ec_node_free(node);
284         ec_free(value);
285         return NULL;
286 }
287
288 struct ec_config *
289 ec_config_dict(void)
290 {
291         struct ec_config *value = NULL;
292         struct ec_keyval *dict = NULL;
293
294         dict = ec_keyval();
295         if (dict == NULL)
296                 goto fail;
297
298         value = ec_calloc(1, sizeof(*value));
299         if (value == NULL)
300                 goto fail;
301
302         value->type = EC_CONFIG_TYPE_DICT;
303         value->dict = dict;
304
305         return value;
306
307 fail:
308         ec_keyval_free(dict);
309         ec_free(value);
310         return NULL;
311 }
312
313 struct ec_config *
314 ec_config_list(void)
315 {
316         struct ec_config *value = NULL;
317
318         value = ec_calloc(1, sizeof(*value));
319         if (value == NULL)
320                 return NULL;
321
322         value->type = EC_CONFIG_TYPE_LIST;
323         TAILQ_INIT(&value->list);
324
325         return value;
326 }
327
328 int
329 ec_config_set_schema(struct ec_config *dict,
330                 const struct ec_config_schema *schema,
331                 size_t schema_len)
332 {
333         if (dict->type != EC_CONFIG_TYPE_DICT) {
334                 errno = EINVAL;
335                 goto fail;
336         }
337
338         if (ec_config_schema_validate(schema, schema_len) < 0)
339                 goto fail;
340
341         dict->schema = schema;
342         dict->schema_len = schema_len;
343
344         return 0;
345
346 fail:
347         return -1;
348 }
349
350 const struct ec_config_schema *
351 ec_config_schema_lookup(const struct ec_config_schema *schema,
352                         size_t schema_len, const char *key,
353                         enum ec_config_type type)
354 {
355         size_t i;
356
357         for (i = 0; i < schema_len; i++) {
358                 if (!strcmp(key, schema[i].key) &&
359                                 type == schema[i].type)
360                         return &schema[i];
361         }
362
363         errno = ENOENT;
364         return NULL;
365 }
366
367 void
368 ec_config_free(struct ec_config *value)
369 {
370         if (value == NULL)
371                 return;
372
373         switch (value->type) {
374         case EC_CONFIG_TYPE_STRING:
375                 ec_free(value->string);
376                 break;
377         case EC_CONFIG_TYPE_NODE:
378                 ec_node_free(value->node);
379                 break;
380         case EC_CONFIG_TYPE_LIST:
381                 while (!TAILQ_EMPTY(&value->list)) {
382                         struct ec_config *v;
383                         v = TAILQ_FIRST(&value->list);
384                         TAILQ_REMOVE(&value->list, v, next);
385                         ec_config_free(v);
386                 }
387                 break;
388         case EC_CONFIG_TYPE_DICT:
389                 ec_keyval_free(value->dict);
390                 break;
391         default:
392                 break;
393         }
394
395         ec_free(value);
396 }
397
398 static int
399 ec_config_list_cmp(const struct ec_config_list *list1,
400                 const struct ec_config_list *list2)
401 {
402         const struct ec_config *v1, *v2;
403
404         for (v1 = TAILQ_FIRST(list1), v2 = TAILQ_FIRST(list2);
405              v1 != NULL && v2 != NULL;
406              v1 = TAILQ_NEXT(v1, next), v2 = TAILQ_NEXT(v2, next)) {
407                 if (ec_config_cmp(v1, v2))
408                         return -1;
409         }
410         if (v1 != NULL || v2 != NULL)
411                 return -1;
412
413         return 0;
414 }
415
416 /* XXX -> ec_keyval_cmp() */
417 static int
418 ec_config_dict_cmp(const struct ec_keyval *d1,
419                 const struct ec_keyval *d2)
420 {
421         const struct ec_config *v1, *v2;
422         struct ec_keyval_iter *iter = NULL;
423         const char *key;
424
425         if (ec_keyval_len(d1) != ec_keyval_len(d2))
426                 return -1;
427
428         for (iter = ec_keyval_iter(d1);
429              ec_keyval_iter_valid(iter);
430              ec_keyval_iter_next(iter)) {
431                 key = ec_keyval_iter_get_key(iter);
432                 v1 = ec_keyval_iter_get_val(iter);
433                 v2 = ec_keyval_get(d2, key);
434
435                 if (ec_config_cmp(v1, v2))
436                         goto fail;
437         }
438
439         ec_keyval_iter_free(iter);
440         return 0;
441
442 fail:
443         ec_keyval_iter_free(iter);
444         return -1;
445 }
446
447 int
448 ec_config_cmp(const struct ec_config *value1,
449                 const struct ec_config *value2)
450 {
451         if (value1->type != value2->type)
452                 return -1;
453
454         switch (value1->type) {
455         case EC_CONFIG_TYPE_BOOL:
456                 if (value1->boolean == value2->boolean)
457                         return 0;
458         case EC_CONFIG_TYPE_INT64:
459                 if (value1->i64 == value2->i64)
460                         return 0;
461         case EC_CONFIG_TYPE_UINT64:
462                 if (value1->u64 == value2->u64)
463                         return 0;
464         case EC_CONFIG_TYPE_STRING:
465                 if (!strcmp(value1->string, value2->string))
466                         return 0;
467         case EC_CONFIG_TYPE_NODE:
468                 if (value1->node == value2->node)
469                         return 0;
470         case EC_CONFIG_TYPE_LIST:
471                 return ec_config_list_cmp(&value1->list, &value2->list);
472         case EC_CONFIG_TYPE_DICT:
473                 return ec_config_dict_cmp(value1->dict, value2->dict);
474         default:
475                 break;
476         }
477
478         return -1;
479 }
480
481 static int
482 ec_config_list_validate(const struct ec_config_list *list,
483                         const struct ec_config_schema *sch)
484 {
485         const struct ec_config *value;
486
487         TAILQ_FOREACH(value, list, next) {
488                 if (value->type != sch->type) {
489                         errno = EBADMSG;
490                         return -1;
491                 }
492
493                 if (value->type == EC_CONFIG_TYPE_LIST) {
494                         if (ec_config_list_validate(&value->list,
495                                                         sch->subschema) < 0)
496                                 return -1;
497                 } else if (value->type == EC_CONFIG_TYPE_DICT) {
498                         if (ec_config_dict_validate(value->dict,
499                                         sch->subschema, sch->subschema_len) < 0)
500                                 return -1;
501                 }
502         }
503
504         return 0;
505 }
506
507 static int
508 ec_config_dict_validate(const struct ec_keyval *dict,
509                         const struct ec_config_schema *schema,
510                         size_t schema_len)
511 {
512         const struct ec_config *value;
513         struct ec_keyval_iter *iter = NULL;
514         const struct ec_config_schema *sch;
515         const char *key;
516
517         for (iter = ec_keyval_iter(dict);
518              ec_keyval_iter_valid(iter);
519              ec_keyval_iter_next(iter)) {
520
521                 key = ec_keyval_iter_get_key(iter);
522                 value = ec_keyval_iter_get_val(iter);
523                 sch = ec_config_schema_lookup(schema, schema_len,
524                                         key, value->type);
525                 if (sch == NULL) {
526                         errno = EBADMSG;
527                         goto fail;
528                 }
529
530                 if (value->type == EC_CONFIG_TYPE_LIST) {
531                         if (ec_config_list_validate(&value->list,
532                                                         sch->subschema) < 0)
533                                 goto fail;
534                 } else if (value->type == EC_CONFIG_TYPE_DICT) {
535                         if (ec_config_dict_validate(value->dict,
536                                         sch->subschema, sch->subschema_len) < 0)
537                                 goto fail;
538                 }
539         }
540
541         ec_keyval_iter_free(iter);
542         return 0;
543
544 fail:
545         ec_keyval_iter_free(iter);
546         return -1;
547 }
548
549 int
550 ec_config_validate(const struct ec_config *dict)
551 {
552         if (dict->type != EC_CONFIG_TYPE_DICT) {
553                 errno = EINVAL;
554                 goto fail;
555         }
556         if (dict->schema == NULL || dict->schema_len == 0) {
557                 errno = ENOENT;
558                 goto fail;
559         }
560
561         if (ec_config_dict_validate(dict->dict, dict->schema,
562                                         dict->schema_len) < 0)
563                 goto fail;
564
565         return 0
566 ;
567 fail:
568         return -1;
569 }
570
571
572 const struct ec_config *
573 ec_config_get(const struct ec_config *config, const char *key)
574 {
575         if (config == NULL)
576                 return NULL;
577
578         return ec_keyval_get(config->dict, key);
579 }
580
581 /* value is consumed */
582 int ec_config_set(struct ec_config *config, const char *key,
583                 struct ec_config *value)
584 {
585         void (*free_cb)(struct ec_config *) = ec_config_free;
586
587         if (config == NULL || key == NULL || value == NULL) {
588                 errno = EINVAL;
589                 goto fail;
590         }
591         if (config->type != EC_CONFIG_TYPE_DICT) {
592                 errno = EINVAL;
593                 goto fail;
594         }
595
596         return ec_keyval_set(config->dict, key, value,
597                         (void (*)(void *))free_cb);
598
599 fail:
600         ec_config_free(value);
601         return -1;
602 }
603
604 /* value is consumed */
605 int
606 ec_config_add(struct ec_config *list,
607                 struct ec_config *value)
608 {
609         if (list->type != EC_CONFIG_TYPE_LIST) {
610                 errno = EINVAL;
611                 goto fail;
612         }
613
614         TAILQ_INSERT_TAIL(&list->list, value, next);
615
616         return 0;
617
618 fail:
619         ec_config_free(value);
620         return -1;
621 }
622
623
624 static int
625 ec_config_list_dump(FILE *out, const struct ec_config_list *list,
626                 size_t indent)
627 {
628         const struct ec_config *v;
629
630         fprintf(out, "%*s" "type=list:\n", (int)indent * 4, "");
631
632         TAILQ_FOREACH(v, list, next) {
633                 if (__ec_config_dump(out, NULL, v, indent + 1) < 0)
634                         return -1;
635         }
636
637         return 0;
638 }
639
640 static int
641 ec_config_dict_dump(FILE *out, const struct ec_keyval *dict,
642                 size_t indent)
643 {
644         const struct ec_config *value;
645         struct ec_keyval_iter *iter;
646         const char *key;
647
648         fprintf(out, "%*s" "type=dict:\n", (int)indent * 4, "");
649         for (iter = ec_keyval_iter(dict);
650              ec_keyval_iter_valid(iter);
651              ec_keyval_iter_next(iter)) {
652                 key = ec_keyval_iter_get_key(iter);
653                 value = ec_keyval_iter_get_val(iter);
654                 if (__ec_config_dump(out, key, value, indent + 1) < 0)
655                         goto fail;
656         }
657         ec_keyval_iter_free(iter);
658         return 0;
659
660 fail:
661         ec_keyval_iter_free(iter);
662         return -1;
663 }
664
665 static int
666 __ec_config_dump(FILE *out, const char *key, const struct ec_config *value,
667                 size_t indent)
668 {
669         char *val_str = NULL;
670
671         switch (value->type) {
672         case EC_CONFIG_TYPE_BOOL:
673                 if (value->boolean)
674                         ec_asprintf(&val_str, "true");
675                 else
676                         ec_asprintf(&val_str, "false");
677                 break;
678         case EC_CONFIG_TYPE_INT64:
679                 ec_asprintf(&val_str, "%"PRIu64, value->u64);
680                 break;
681         case EC_CONFIG_TYPE_UINT64:
682                 ec_asprintf(&val_str, "%"PRIi64, value->i64);
683                 break;
684         case EC_CONFIG_TYPE_STRING:
685                 ec_asprintf(&val_str, "%s", value->string);
686                 break;
687         case EC_CONFIG_TYPE_NODE:
688                 ec_asprintf(&val_str, "%p", value->node);
689                 break;
690         case EC_CONFIG_TYPE_LIST:
691                 return ec_config_list_dump(out, &value->list, indent);
692         case EC_CONFIG_TYPE_DICT:
693                 return ec_config_dict_dump(out, value->dict, indent);
694         default:
695                 errno = EINVAL;
696                 break;
697         }
698
699         /* errno is already set on error */
700         if (val_str == NULL)
701                 goto fail;
702
703         fprintf(out, "%*s" "%s%s%stype=%s val=%s\n", (int)indent * 4, "",
704                 key ? "key=": "",
705                 key ? key: "",
706                 key ? " ": "",
707                 ec_config_type_str(value->type), val_str);
708
709         ec_free(val_str);
710         return 0;
711
712 fail:
713         ec_free(val_str);
714         return -1;
715 }
716
717 void
718 ec_config_dump(FILE *out, const struct ec_config *config)
719 {
720         fprintf(out, "------------------- config dump:\n");
721
722         if (config == NULL) {
723                 fprintf(out, "no config\n");
724                 return;
725         }
726
727         if (__ec_config_dump(out, NULL, config, 0) < 0)
728                 fprintf(out, "error while dumping\n");
729 }
730
731 /* LCOV_EXCL_START */
732 static const struct ec_config_schema intlist_elt[] = {
733         {
734                 .desc = "This is a description for int",
735                 .type = EC_CONFIG_TYPE_INT64,
736         },
737 };
738
739 static const struct ec_config_schema dict[] = {
740         {
741                 .key = "int",
742                 .desc = "This is a description for int",
743                 .type = EC_CONFIG_TYPE_INT64,
744         },
745         {
746                 .key = "int2",
747                 .desc = "This is a description for int2",
748                 .type = EC_CONFIG_TYPE_INT64,
749         },
750 };
751
752 static const struct ec_config_schema dictlist_elt[] = {
753         {
754                 .desc = "This is a description for dict",
755                 .type = EC_CONFIG_TYPE_DICT,
756                 .subschema = dict,
757                 .subschema_len = EC_COUNT_OF(dict),
758         },
759 };
760
761 static const struct ec_config_schema baseconfig[] = {
762         {
763                 .key = "bool",
764                 .desc = "This is a description for bool",
765                 .type = EC_CONFIG_TYPE_BOOL,
766         },
767         {
768                 .key = "int",
769                 .desc = "This is a description for int",
770                 .type = EC_CONFIG_TYPE_INT64,
771         },
772         {
773                 .key = "string",
774                 .desc = "This is a description for string",
775                 .type = EC_CONFIG_TYPE_STRING,
776         },
777         {
778                 .key = "node",
779                 .desc = "This is a description for node",
780                 .type = EC_CONFIG_TYPE_NODE,
781         },
782         {
783                 .key = "intlist",
784                 .desc = "This is a description for list",
785                 .type = EC_CONFIG_TYPE_LIST,
786                 .subschema = intlist_elt,
787                 .subschema_len = EC_COUNT_OF(intlist_elt),
788         },
789         {
790                 .key = "dictlist",
791                 .desc = "This is a description for list",
792                 .type = EC_CONFIG_TYPE_LIST,
793                 .subschema = dictlist_elt,
794                 .subschema_len = EC_COUNT_OF(dictlist_elt),
795         },
796 };
797
798 static int ec_config_testcase(void)
799 {
800         struct ec_node *node = NULL;
801         struct ec_keyval *dict = NULL;
802         const struct ec_config *value = NULL;
803         struct ec_config *config = NULL, *list = NULL, *subconfig = NULL;
804         //const struct ec_config *pvalue;
805         int testres = 0;
806         int ret;
807
808         node = ec_node("empty", EC_NO_ID);
809         if (node == NULL)
810                 goto fail;
811
812         if (ec_config_schema_validate(baseconfig,
813                                         EC_COUNT_OF(baseconfig)) < 0) {
814                 EC_LOG(EC_LOG_ERR, "invalid config schema\n");
815                 goto fail;
816         }
817
818         ec_config_schema_dump(stdout, baseconfig, EC_COUNT_OF(baseconfig));
819
820         config = ec_config_dict();
821         if (config == NULL)
822                 goto fail;
823         if (ec_config_set_schema(config, baseconfig,
824                                         EC_COUNT_OF(baseconfig)) < 0)
825                 goto fail;
826
827         ret = ec_config_set(config, "bool", ec_config_bool(true));
828         testres |= EC_TEST_CHECK(ret == 0, "cannot set boolean");
829         value = ec_config_get(config, "bool");
830         testres |= EC_TEST_CHECK(
831                 value != NULL &&
832                 value->type == EC_CONFIG_TYPE_BOOL &&
833                 value->boolean == true,
834                 "unexpected boolean value");
835
836         ret = ec_config_set(config, "int", ec_config_i64(1234));
837         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
838         value = ec_config_get(config, "int");
839         testres |= EC_TEST_CHECK(
840                 value != NULL &&
841                 value->type == EC_CONFIG_TYPE_INT64 &&
842                 value->i64 == 1234,
843                 "unexpected int value");
844
845         testres |= EC_TEST_CHECK(
846                 ec_config_validate(config) == 0,
847                 "cannot validate config\n");
848
849         ret = ec_config_set(config, "string", ec_config_string("toto"));
850         testres |= EC_TEST_CHECK(ret == 0, "cannot set string");
851         value = ec_config_get(config, "string");
852         testres |= EC_TEST_CHECK(
853                 value != NULL &&
854                 value->type == EC_CONFIG_TYPE_STRING &&
855                 !strcmp(value->string, "toto"),
856                 "unexpected string value");
857
858         list = ec_config_list();
859         if (list == NULL)
860                 goto fail;
861
862         subconfig = ec_config_dict();
863         if (subconfig == NULL)
864                 goto fail;
865
866         ret = ec_config_set(subconfig, "int", ec_config_i64(1));
867         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
868         value = ec_config_get(subconfig, "int");
869         testres |= EC_TEST_CHECK(
870                 value != NULL &&
871                 value->type == EC_CONFIG_TYPE_INT64 &&
872                 value->i64 == 1,
873                 "unexpected int value");
874
875         ret = ec_config_set(subconfig, "int2", ec_config_i64(2));
876         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
877         value = ec_config_get(subconfig, "int2");
878         testres |= EC_TEST_CHECK(
879                 value != NULL &&
880                 value->type == EC_CONFIG_TYPE_INT64 &&
881                 value->i64 == 2,
882                 "unexpected int value");
883
884         ret = ec_config_add(list, subconfig);
885         subconfig = NULL; /* freed */
886         testres |= EC_TEST_CHECK(ret == 0, "cannot add in list");
887
888         subconfig = ec_config_dict();
889         if (subconfig == NULL)
890                 goto fail;
891
892         ret = ec_config_set(subconfig, "int", ec_config_i64(3));
893         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
894         value = ec_config_get(subconfig, "int");
895         testres |= EC_TEST_CHECK(
896                 value != NULL &&
897                 value->type == EC_CONFIG_TYPE_INT64 &&
898                 value->i64 == 3,
899                 "unexpected int value");
900
901         ret = ec_config_set(subconfig, "int2", ec_config_i64(4));
902         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
903         value = ec_config_get(subconfig, "int2");
904         testres |= EC_TEST_CHECK(
905                 value != NULL &&
906                 value->type == EC_CONFIG_TYPE_INT64 &&
907                 value->i64 == 4,
908                 "unexpected int value");
909
910         ret = ec_config_add(list, subconfig);
911         subconfig = NULL; /* freed */
912         testres |= EC_TEST_CHECK(ret == 0, "cannot add in list");
913
914         ret = ec_config_set(config, "dictlist", list);
915         list = NULL;
916         testres |= EC_TEST_CHECK(ret == 0, "cannot set list");
917
918         testres |= EC_TEST_CHECK(
919                 ec_config_validate(config) == 0,
920                 "cannot validate config\n");
921
922 #if 0
923         /* api */
924         config = ec_config(schema);
925         value = ec_config_bool(true);
926         ec_config_set(config, "key", value);
927         value = ec_config_get(config, "key");
928         /* check value->type and value->bool */
929
930         value = ec_config_string("xxx");
931         ec_config_set(config, "key", value);
932         value = ec_config_get(config, "key");
933         /* check value->type and value->string */
934
935         list = ec_config_list();
936         value = ec_config_string("xxx");
937         ec_config_add(list, value);
938         value = ec_config_string("yyy");
939         ec_config_add(list, value);
940         value = ec_config_remove(list, 0);
941         ec_config_set(config, "key", list);
942
943 #endif
944
945 #if 0
946         value.type = EC_CONFIG_TYPE_NODE;
947         value.node = ec_node_clone(node);
948         ret = ec_config_set(config, "node", value);
949         testres |= EC_TEST_CHECK(ret == 0, "cannot set node");
950         pvalue = ec_config_get(config, "node");
951         testres |= EC_TEST_CHECK(
952                 pvalue != NULL &&
953                 ec_config_cmp(pvalue, &value) == 0,
954                 "unexpected node value");
955
956         subconfig = ec_config(dict, EC_COUNT_OF(dict));
957         if (subconfig == NULL)
958                 goto fail;
959
960         value.type = EC_CONFIG_TYPE_INT64;
961         value.i64 = 4321;
962         ret = ec_config_set(subconfig, "int", value);
963         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
964         pvalue = ec_config_get(subconfig, "int");
965         testres |= EC_TEST_CHECK(
966                 pvalue != NULL &&
967                 ec_config_cmp(pvalue, &value) == 0,
968                 "unexpected int value");
969
970         value.type = EC_CONFIG_TYPE_DICT;
971         value.dict = subconfig;
972         subconfig = NULL; /* will be freed when freeing config */
973         ret = ec_config_set(config, "dict", value);
974         testres |= EC_TEST_CHECK(ret == 0, "cannot set dict");
975         pvalue = ec_config_get(config, "dict");
976         testres |= EC_TEST_CHECK(
977                 pvalue != NULL &&
978                 ec_config_cmp(pvalue, &value) == 0,
979                 "unexpected dict value");
980
981         value.type = EC_CONFIG_TYPE_INT64;
982         value.i64 = 4321;
983         pvalue = ec_config_get(
984                 ec_config_get(config, "dict")->dict, "int");
985         testres |= EC_TEST_CHECK(
986                 pvalue != NULL &&
987                 ec_config_cmp(pvalue, &value) == 0,
988                 "unexpected int value");
989
990         value.type = EC_CONFIG_TYPE_INT64;
991         value.i64 = 1;
992         ret = ec_config_add(config, "intlist", value);
993         testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
994         value.type = EC_CONFIG_TYPE_INT64;
995         value.i64 = 2;
996         ret = ec_config_add(config, "intlist", value);
997         testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
998         value.type = EC_CONFIG_TYPE_INT64;
999         value.i64 = 3;
1000         ret = ec_config_add(config, "intlist", value);
1001         testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
1002
1003         value.type = EC_CONFIG_TYPE_INT64;
1004         value.i64 = 4321;
1005         ret = ec_config_set(config, "invalid", value);
1006         testres |= EC_TEST_CHECK(ret < 0,
1007                         "should not be able to set invalid key");
1008         pvalue = ec_config_get(config, "invalid");
1009         testres |= EC_TEST_CHECK(pvalue == NULL,
1010                         "invalid key returned a value");
1011
1012 #endif
1013         ec_config_dump(stdout, config);
1014
1015         ec_config_free(list);
1016         ec_config_free(subconfig);
1017         ec_config_free(config);
1018         ec_keyval_free(dict);
1019         ec_node_free(node);
1020
1021         return testres;
1022
1023 fail:
1024         ec_config_free(list);
1025         ec_config_free(subconfig);
1026         ec_config_free(config);
1027         ec_keyval_free(dict);
1028         ec_node_free(node);
1029
1030         return -1;
1031 }
1032 /* LCOV_EXCL_STOP */
1033
1034 static struct ec_test ec_config_test = {
1035         .name = "config",
1036         .test = ec_config_testcase,
1037 };
1038
1039 EC_TEST_REGISTER(ec_config_test);