1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright 2018, Olivier MATZ <zer0@droids-corp.org>
15 #include <ecoli_malloc.h>
16 #include <ecoli_keyval.h>
17 #include <ecoli_node.h>
18 #include <ecoli_config.h>
19 #include <ecoli_yaml.h>
21 /* associate a yaml node to a ecoli node */
23 const yaml_node_t *ynode;
24 struct ec_node *enode;
27 /* store the associations yaml_node <-> ec_node */
33 static struct ec_node *
34 parse_ec_node(struct enode_table *table, const yaml_document_t *document,
35 const yaml_node_t *ynode);
37 static struct ec_config *
38 parse_ec_config_list(struct enode_table *table,
39 const struct ec_config_schema *schema,
40 const yaml_document_t *document, const yaml_node_t *ynode);
42 static struct ec_config *
43 parse_ec_config_dict(struct enode_table *table,
44 const struct ec_config_schema *schema,
45 const yaml_document_t *document, const yaml_node_t *ynode);
47 /* XXX to utils.c ? */
49 parse_llint(const char *str, int64_t *val)
52 int save_errno = errno;
55 *val = strtoll(str, &endptr, 0);
57 if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) ||
58 (errno != 0 && *val == 0))
71 parse_ullint(const char *str, uint64_t *val)
74 int save_errno = errno;
76 /* since a negative input is silently converted to a positive
77 * one by strtoull(), first check that it is positive */
82 *val = strtoull(str, &endptr, 0);
84 if ((errno == ERANGE && *val == ULLONG_MAX) ||
85 (errno != 0 && *val == 0))
96 parse_bool(const char *str, bool *val)
98 if (!strcasecmp(str, "true")) {
101 } else if (!strcasecmp(str, "false")) {
110 add_in_table(struct enode_table *table,
111 const yaml_node_t *ynode, struct ec_node *enode)
113 struct pair *pair = NULL;
115 pair = realloc(table->pair, (table->len + 1) * sizeof(*pair));
119 ec_node_clone(enode);
120 pair[table->len].ynode = ynode;
121 pair[table->len].enode = enode;
129 free_table(struct enode_table *table)
133 for (i = 0; i < table->len; i++)
134 ec_node_free(table->pair[i].enode);
138 static struct ec_config *
139 parse_ec_config(struct enode_table *table,
140 const struct ec_config_schema *schema_elt,
141 const yaml_document_t *document, const yaml_node_t *ynode)
143 const struct ec_config_schema *subschema;
144 struct ec_config *config = NULL;
145 struct ec_node *enode = NULL;
146 enum ec_config_type type;
147 const char *value_str;
152 type = ec_config_schema_type(schema_elt);
155 case EC_CONFIG_TYPE_BOOL:
156 if (ynode->type != YAML_SCALAR_NODE) {
157 fprintf(stderr, "Boolean should be scalar\n");
160 value_str = (const char *)ynode->data.scalar.value;
161 if (parse_bool(value_str, &boolean) < 0) {
162 fprintf(stderr, "Failed to parse boolean\n");
165 config = ec_config_bool(boolean);
166 if (config == NULL) {
167 fprintf(stderr, "Failed to create config\n");
171 case EC_CONFIG_TYPE_INT64:
172 if (ynode->type != YAML_SCALAR_NODE) {
173 fprintf(stderr, "Int64 should be scalar\n");
176 value_str = (const char *)ynode->data.scalar.value;
177 if (parse_llint(value_str, &i64) < 0) {
178 fprintf(stderr, "Failed to parse i64\n");
181 config = ec_config_i64(i64);
182 if (config == NULL) {
183 fprintf(stderr, "Failed to create config\n");
187 case EC_CONFIG_TYPE_UINT64:
188 if (ynode->type != YAML_SCALAR_NODE) {
189 fprintf(stderr, "Uint64 should be scalar\n");
192 value_str = (const char *)ynode->data.scalar.value;
193 if (parse_ullint(value_str, &u64) < 0) {
194 fprintf(stderr, "Failed to parse u64\n");
197 config = ec_config_u64(u64);
198 if (config == NULL) {
199 fprintf(stderr, "Failed to create config\n");
203 case EC_CONFIG_TYPE_STRING:
204 if (ynode->type != YAML_SCALAR_NODE) {
205 fprintf(stderr, "String should be scalar\n");
208 value_str = (const char *)ynode->data.scalar.value;
209 config = ec_config_string(value_str);
210 if (config == NULL) {
211 fprintf(stderr, "Failed to create config\n");
215 case EC_CONFIG_TYPE_NODE:
216 enode = parse_ec_node(table, document, ynode);
219 config = ec_config_node(enode);
221 if (config == NULL) {
222 fprintf(stderr, "Failed to create config\n");
226 case EC_CONFIG_TYPE_LIST:
227 subschema = ec_config_schema_sub(schema_elt);
228 if (subschema == NULL) {
229 fprintf(stderr, "List has no subschema\n");
232 config = parse_ec_config_list(table, subschema, document, ynode);
236 case EC_CONFIG_TYPE_DICT:
237 subschema = ec_config_schema_sub(schema_elt);
238 if (subschema == NULL) {
239 fprintf(stderr, "Dict has no subschema\n");
242 config = parse_ec_config_dict(table, subschema, document, ynode);
247 fprintf(stderr, "Invalid config type %d\n", type);
255 ec_config_free(config);
259 static struct ec_config *
260 parse_ec_config_list(struct enode_table *table,
261 const struct ec_config_schema *schema,
262 const yaml_document_t *document, const yaml_node_t *ynode)
264 struct ec_config *config = NULL, *subconfig = NULL;
265 const yaml_node_item_t *item;
266 const yaml_node_t *value;
268 if (ynode->type != YAML_SEQUENCE_NODE) {
269 fprintf(stderr, "Ecoli list config should be a yaml sequence\n");
273 config = ec_config_list();
274 if (config == NULL) {
275 fprintf(stderr, "Failed to allocate config\n");
279 for (item = ynode->data.sequence.items.start;
280 item < ynode->data.sequence.items.top; item++) {
281 value = document->nodes.start + (*item) - 1; // XXX -1 ?
282 subconfig = parse_ec_config(table, schema, document, value);
283 if (subconfig == NULL)
285 if (ec_config_list_add(config, subconfig) < 0) {
286 fprintf(stderr, "Failed to add list entry\n");
294 ec_config_free(config);
298 static struct ec_config *
299 parse_ec_config_dict(struct enode_table *table,
300 const struct ec_config_schema *schema,
301 const yaml_document_t *document, const yaml_node_t *ynode)
303 const struct ec_config_schema *schema_elt;
304 struct ec_config *config = NULL, *subconfig = NULL;
305 const yaml_node_t *key, *value;
306 const yaml_node_pair_t *pair;
309 if (ynode->type != YAML_MAPPING_NODE) {
310 fprintf(stderr, "Ecoli config should be a yaml mapping node\n");
314 config = ec_config_dict();
315 if (config == NULL) {
316 fprintf(stderr, "Failed to allocate config\n");
320 for (pair = ynode->data.mapping.pairs.start;
321 pair < ynode->data.mapping.pairs.top; pair++) {
322 key = document->nodes.start + pair->key - 1; // XXX -1 ?
323 value = document->nodes.start + pair->value - 1;
324 key_str = (const char *)key->data.scalar.value;
326 if (ec_config_key_is_reserved(key_str))
328 schema_elt = ec_config_schema_lookup(schema, key_str);
329 if (schema_elt == NULL) {
330 fprintf(stderr, "No such config %s\n", key_str);
333 subconfig = parse_ec_config(table, schema_elt, document, value);
334 if (subconfig == NULL)
336 if (ec_config_dict_set(config, key_str, subconfig) < 0) {
337 fprintf(stderr, "Failed to set dict entry\n");
345 ec_config_free(config);
349 static struct ec_node *
350 parse_ec_node(struct enode_table *table,
351 const yaml_document_t *document, const yaml_node_t *ynode)
353 const struct ec_config_schema *schema;
354 const struct ec_node_type *type = NULL;
355 const char *id = NULL;
357 struct ec_config *config = NULL;
358 const yaml_node_t *attrs = NULL;
359 const yaml_node_t *key, *value;
360 const yaml_node_pair_t *pair;
361 const char *key_str, *value_str;
362 struct ec_node *enode = NULL;
363 char *value_dup = NULL;
366 if (ynode->type != YAML_MAPPING_NODE) {
367 fprintf(stderr, "Ecoli node should be a yaml mapping node\n");
371 /* if it's an anchor, the node may be already parsed, reuse it */
372 for (i = 0; i < table->len; i++) {
373 if (table->pair[i].ynode == ynode)
374 return ec_node_clone(table->pair[i].enode);
377 for (pair = ynode->data.mapping.pairs.start;
378 pair < ynode->data.mapping.pairs.top; pair++) {
379 key = document->nodes.start + pair->key - 1; // XXX -1 ?
380 value = document->nodes.start + pair->value - 1;
381 key_str = (const char *)key->data.scalar.value;
382 value_str = (const char *)value->data.scalar.value;
384 if (!strcmp(key_str, "type")) {
386 fprintf(stderr, "Duplicate type\n");
389 if (value->type != YAML_SCALAR_NODE) {
390 fprintf(stderr, "Type must be a string\n");
393 type = ec_node_type_lookup(value_str);
395 fprintf(stderr, "Cannot find type %s\n",
399 } else if (!strcmp(key_str, "attrs")) {
401 fprintf(stderr, "Duplicate attrs\n");
404 if (value->type != YAML_MAPPING_NODE) {
405 fprintf(stderr, "Attrs must be a maping\n");
409 } else if (!strcmp(key_str, "id")) {
411 fprintf(stderr, "Duplicate id\n");
414 if (value->type != YAML_SCALAR_NODE) {
415 fprintf(stderr, "Id must be a scalar\n");
419 } else if (!strcmp(key_str, "help")) {
421 fprintf(stderr, "Duplicate help\n");
424 if (value->type != YAML_SCALAR_NODE) {
425 fprintf(stderr, "Help must be a scalar\n");
428 help = ec_strdup(value_str);
430 fprintf(stderr, "Failed to allocate help\n");
436 /* create the ecoli node */
439 enode = ec_node_from_type(type, id);
441 fprintf(stderr, "Cannot create ecoli node\n");
444 if (add_in_table(table, ynode, enode) < 0) {
445 fprintf(stderr, "Cannot add node in table\n");
449 /* create its config */
450 schema = ec_node_type_schema(type);
451 if (schema == NULL) {
452 fprintf(stderr, "No configuration schema for type %s\n",
453 ec_node_type_name(type));
457 config = parse_ec_config_dict(table, schema, document, ynode);
461 if (ec_node_set_config(enode, config) < 0) {
462 config = NULL; /* freed */
463 fprintf(stderr, "Failed to set config\n");
466 config = NULL; /* freed */
469 if (ec_keyval_set(ec_node_attrs(enode), "help", help,
471 fprintf(stderr, "Failed to set help\n");
478 /* add attributes (all as string) */
480 for (pair = attrs->data.mapping.pairs.start;
481 pair < attrs->data.mapping.pairs.top; pair++) {
482 key = document->nodes.start + pair->key - 1;
483 value = document->nodes.start + pair->value - 1;
484 key_str = (const char *)key->data.scalar.value;
485 value_str = (const char *)value->data.scalar.value;
486 value_dup = ec_strdup(value_str);
487 if (value_dup == NULL)
489 if (ec_keyval_set(ec_node_attrs(enode), key_str,
490 value_dup, ec_free_func) < 0) {
502 ec_config_free(config);
509 static struct ec_node *
510 parse_document(struct enode_table *table,
511 const yaml_document_t *document)
515 node = document->nodes.start;
516 return parse_ec_node(table, document, node);
520 ec_yaml_import(const char *filename)
523 yaml_parser_t parser;
524 yaml_document_t document;
525 struct ec_node *root = NULL;
526 struct enode_table table;
528 memset(&table, 0, sizeof(table));
530 file = fopen(filename, "rb");
532 fprintf(stderr, "Failed to open file %s\n", filename);
536 if (yaml_parser_initialize(&parser) == 0) {
537 fprintf(stderr, "Failed to initialize yaml parser\n");
541 yaml_parser_set_input_file(&parser, file);
543 if (yaml_parser_load(&parser, &document) == 0) {
544 fprintf(stderr, "Failed to load yaml document\n");
548 if (yaml_document_get_root_node(&document) == NULL) {
549 fprintf(stderr, "Incomplete document\n"); //XXX check err
553 root = parse_document(&table, &document);
555 fprintf(stderr, "Failed to parse document\n");
559 yaml_document_delete(&document);
560 yaml_parser_delete(&parser);
567 yaml_document_delete(&document);
569 yaml_parser_delete(&parser);