1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright 2018, Olivier MATZ <zer0@droids-corp.org>
14 #include <ecoli_node.h>
15 #include <ecoli_config.h>
16 #include <ecoli_yaml.h>
18 /* associate a yaml node to a ecoli node */
20 const yaml_node_t *ynode;
21 struct ec_node *enode;
24 /* store the associations yaml_node <-> ec_node */
30 static struct ec_node *
31 parse_ec_node(struct ec_node **root, struct enode_table *table,
32 const yaml_document_t *document, const yaml_node_t *ynode);
34 static struct ec_config *
35 parse_ec_config_list(struct ec_node **root, struct enode_table *table,
36 const struct ec_config_schema *schema,
37 const yaml_document_t *document, const yaml_node_t *ynode);
39 static struct ec_config *
40 parse_ec_config_dict(struct ec_node **root, struct enode_table *table,
41 const struct ec_config_schema *schema,
42 const yaml_document_t *document, const yaml_node_t *ynode);
44 /* XXX to utils.c ? */
46 parse_llint(const char *str, int64_t *val)
49 int save_errno = errno;
52 *val = strtoll(str, &endptr, 0);
54 if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) ||
55 (errno != 0 && *val == 0))
68 parse_ullint(const char *str, uint64_t *val)
71 int save_errno = errno;
73 /* since a negative input is silently converted to a positive
74 * one by strtoull(), first check that it is positive */
79 *val = strtoull(str, &endptr, 0);
81 if ((errno == ERANGE && *val == ULLONG_MAX) ||
82 (errno != 0 && *val == 0))
93 parse_bool(const char *str, bool *val)
95 if (!strcasecmp(str, "true")) {
98 } else if (!strcasecmp(str, "false")) {
107 add_in_table(struct enode_table *table,
108 const yaml_node_t *ynode, struct ec_node *enode)
110 struct pair *pair = NULL;
112 pair = realloc(table->pair, (table->len + 1) * sizeof(*pair));
116 ec_node_clone(enode);
117 pair[table->len].ynode = ynode;
118 pair[table->len].enode = enode;
126 free_table(struct enode_table *table)
130 for (i = 0; i < table->len; i++)
131 ec_node_free(table->pair[i].enode);
135 static struct ec_config *
136 parse_ec_config(struct ec_node **root, struct enode_table *table,
137 const struct ec_config_schema *schema_elt,
138 const yaml_document_t *document, const yaml_node_t *ynode)
140 const struct ec_config_schema *subschema;
141 struct ec_config *config = NULL;
142 struct ec_node *enode = NULL;
143 enum ec_config_type type;
144 const char *value_str;
149 type = ec_config_schema_type(schema_elt);
152 case EC_CONFIG_TYPE_BOOL:
153 if (ynode->type != YAML_SCALAR_NODE) {
154 fprintf(stderr, "Boolean should be scalar\n");
157 value_str = (const char *)ynode->data.scalar.value;
158 if (parse_bool(value_str, &boolean) < 0) {
159 fprintf(stderr, "Failed to parse boolean\n");
162 config = ec_config_bool(boolean);
163 if (config == NULL) {
164 fprintf(stderr, "Failed to create config\n");
168 case EC_CONFIG_TYPE_INT64:
169 if (ynode->type != YAML_SCALAR_NODE) {
170 fprintf(stderr, "Int64 should be scalar\n");
173 value_str = (const char *)ynode->data.scalar.value;
174 if (parse_llint(value_str, &i64) < 0) {
175 fprintf(stderr, "Failed to parse i64\n");
178 config = ec_config_i64(i64);
179 if (config == NULL) {
180 fprintf(stderr, "Failed to create config\n");
184 case EC_CONFIG_TYPE_UINT64:
185 if (ynode->type != YAML_SCALAR_NODE) {
186 fprintf(stderr, "Uint64 should be scalar\n");
189 value_str = (const char *)ynode->data.scalar.value;
190 if (parse_ullint(value_str, &u64) < 0) {
191 fprintf(stderr, "Failed to parse u64\n");
194 config = ec_config_u64(u64);
195 if (config == NULL) {
196 fprintf(stderr, "Failed to create config\n");
200 case EC_CONFIG_TYPE_STRING:
201 if (ynode->type != YAML_SCALAR_NODE) {
202 fprintf(stderr, "String should be scalar\n");
205 value_str = (const char *)ynode->data.scalar.value;
206 config = ec_config_string(value_str);
207 if (config == NULL) {
208 fprintf(stderr, "Failed to create config\n");
212 case EC_CONFIG_TYPE_NODE:
213 enode = parse_ec_node(root, table, document, ynode);
216 config = ec_config_node(enode);
217 if (config == NULL) {
218 fprintf(stderr, "Failed to create config\n");
222 case EC_CONFIG_TYPE_LIST:
223 subschema = ec_config_schema_sub(schema_elt);
224 if (subschema == NULL) {
225 fprintf(stderr, "List has no subschema\n");
228 config = parse_ec_config_list(root, table, subschema, document, ynode);
232 case EC_CONFIG_TYPE_DICT:
233 subschema = ec_config_schema_sub(schema_elt);
234 if (subschema == NULL) {
235 fprintf(stderr, "Dict has no subschema\n");
238 config = parse_ec_config_dict(root, table, subschema, document, ynode);
243 fprintf(stderr, "Invalid config type %d\n", type);
251 ec_config_free(config);
255 static struct ec_config *
256 parse_ec_config_list(struct ec_node **root, struct enode_table *table,
257 const struct ec_config_schema *schema,
258 const yaml_document_t *document, const yaml_node_t *ynode)
260 struct ec_config *config = NULL, *subconfig = NULL;
261 const yaml_node_item_t *item;
262 const yaml_node_t *value;
264 if (ynode->type != YAML_SEQUENCE_NODE) {
265 fprintf(stderr, "Ecoli list config should be a yaml sequence\n");
269 config = ec_config_list();
270 if (config == NULL) {
271 fprintf(stderr, "Failed to allocate config\n");
275 for (item = ynode->data.sequence.items.start;
276 item < ynode->data.sequence.items.top; item++) {
277 value = document->nodes.start + (*item) - 1; // XXX -1 ?
278 subconfig = parse_ec_config(root, table, schema, document, value);
279 if (subconfig == NULL)
281 if (ec_config_list_add(config, subconfig) < 0) {
282 fprintf(stderr, "Failed to add list entry\n");
290 ec_config_free(config);
294 static struct ec_config *
295 parse_ec_config_dict(struct ec_node **root, struct enode_table *table,
296 const struct ec_config_schema *schema,
297 const yaml_document_t *document, const yaml_node_t *ynode)
299 const struct ec_config_schema *schema_elt;
300 struct ec_config *config = NULL, *subconfig = NULL;
301 const yaml_node_t *key, *value;
302 const yaml_node_pair_t *pair;
305 if (ynode->type != YAML_MAPPING_NODE) {
306 fprintf(stderr, "Ecoli config should be a yaml mapping node\n");
310 config = ec_config_dict();
311 if (config == NULL) {
312 fprintf(stderr, "Failed to allocate config\n");
316 for (pair = ynode->data.mapping.pairs.start;
317 pair < ynode->data.mapping.pairs.top; pair++) {
318 key = document->nodes.start + pair->key - 1; // XXX -1 ?
319 value = document->nodes.start + pair->value - 1;
320 key_str = (const char *)key->data.scalar.value;
322 if (ec_config_key_is_reserved(key_str))
324 schema_elt = ec_config_schema_lookup(schema, key_str);
325 if (schema_elt == NULL) {
326 fprintf(stderr, "No such config %s\n", key_str);
329 subconfig = parse_ec_config(root, table, schema_elt, document, value);
330 if (subconfig == NULL)
332 if (ec_config_dict_set(config, key_str, subconfig) < 0) {
333 fprintf(stderr, "Failed to set dict entry\n");
341 ec_config_free(config);
345 static struct ec_node *
346 parse_ec_node(struct ec_node **root, struct enode_table *table,
347 const yaml_document_t *document, const yaml_node_t *ynode)
349 const struct ec_config_schema *schema;
350 const struct ec_node_type *type = NULL;
351 const char *id = NULL, *help = NULL;
352 struct ec_config *config = NULL;
353 const yaml_node_t *attrs = NULL;
354 const yaml_node_t *key, *value;
355 const yaml_node_pair_t *pair;
356 const char *key_str, *value_str;
357 struct ec_node *enode = NULL;
359 if (ynode->type != YAML_MAPPING_NODE) {
360 fprintf(stderr, "Ecoli node should be a yaml mapping node\n");
364 for (pair = ynode->data.mapping.pairs.start;
365 pair < ynode->data.mapping.pairs.top; pair++) {
366 key = document->nodes.start + pair->key - 1; // XXX -1 ?
367 value = document->nodes.start + pair->value - 1;
368 key_str = (const char *)key->data.scalar.value;
369 value_str = (const char *)value->data.scalar.value;
371 if (!strcmp(key_str, "type")) {
373 fprintf(stderr, "Duplicate type\n");
376 if (value->type != YAML_SCALAR_NODE) {
377 fprintf(stderr, "Type must be a string\n");
380 type = ec_node_type_lookup(value_str);
382 fprintf(stderr, "Cannot find type %s\n",
386 } else if (!strcmp(key_str, "attrs")) {
388 fprintf(stderr, "Duplicate attrs\n");
391 if (value->type != YAML_MAPPING_NODE) {
392 fprintf(stderr, "Attrs must be a maping\n");
396 } else if (!strcmp(key_str, "id")) {
398 fprintf(stderr, "Duplicate id\n");
401 if (value->type != YAML_SCALAR_NODE) {
402 fprintf(stderr, "Id must be a scalar\n");
406 } else if (!strcmp(key_str, "help")) {
408 fprintf(stderr, "Duplicate help\n");
411 if (value->type != YAML_SCALAR_NODE) {
412 fprintf(stderr, "Help must be a scalar\n");
419 /* create the ecoli node */
422 enode = ec_node_from_type(type, id);
424 fprintf(stderr, "Cannot create ecoli node\n");
427 if (add_in_table(table, ynode, enode) < 0) {
428 fprintf(stderr, "Cannot add node in table\n");
432 ec_node_clone(enode);
436 /* create its config */
437 schema = ec_node_type_schema(type);
438 if (schema == NULL) {
439 fprintf(stderr, "No configuration schema for type %s\n",
440 ec_node_type_name(type));
444 config = parse_ec_config_dict(root, table, schema, document, ynode);
448 if (ec_node_set_config(enode, config) < 0) {
449 fprintf(stderr, "Failed to set config\n");
453 /* add attributes (all as string) */
460 ec_config_free(config);
465 parse_document(struct ec_node **root, struct enode_table *table,
466 const yaml_document_t *document)
470 node = document->nodes.start;
471 if (parse_ec_node(root, table, document, node) == NULL)
478 ec_yaml_import(const char *filename)
481 yaml_parser_t parser;
482 yaml_document_t document;
483 struct ec_node *root = NULL;
484 struct enode_table table;
486 memset(&table, 0, sizeof(table));
488 file = fopen(filename, "rb");
490 fprintf(stderr, "Failed to open file %s\n", filename);
494 if (yaml_parser_initialize(&parser) == 0) {
495 fprintf(stderr, "Failed to initialize yaml parser\n");
499 yaml_parser_set_input_file(&parser, file);
501 if (yaml_parser_load(&parser, &document) == 0) {
502 fprintf(stderr, "Failed to load yaml document\n");
506 if (yaml_document_get_root_node(&document) == NULL) {
507 fprintf(stderr, "Incomplete document\n"); //XXX check err
511 if (parse_document(&root, &table, &document) < 0) {
512 fprintf(stderr, "Failed to parse document\n");
516 yaml_document_delete(&document);
517 yaml_parser_delete(&parser);
524 yaml_document_delete(&document);
526 yaml_parser_delete(&parser);