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);
220 if (config == NULL) {
221 fprintf(stderr, "Failed to create config\n");
225 case EC_CONFIG_TYPE_LIST:
226 subschema = ec_config_schema_sub(schema_elt);
227 if (subschema == NULL) {
228 fprintf(stderr, "List has no subschema\n");
231 config = parse_ec_config_list(table, subschema, document, ynode);
235 case EC_CONFIG_TYPE_DICT:
236 subschema = ec_config_schema_sub(schema_elt);
237 if (subschema == NULL) {
238 fprintf(stderr, "Dict has no subschema\n");
241 config = parse_ec_config_dict(table, subschema, document, ynode);
246 fprintf(stderr, "Invalid config type %d\n", type);
254 ec_config_free(config);
258 static struct ec_config *
259 parse_ec_config_list(struct enode_table *table,
260 const struct ec_config_schema *schema,
261 const yaml_document_t *document, const yaml_node_t *ynode)
263 struct ec_config *config = NULL, *subconfig = NULL;
264 const yaml_node_item_t *item;
265 const yaml_node_t *value;
267 if (ynode->type != YAML_SEQUENCE_NODE) {
268 fprintf(stderr, "Ecoli list config should be a yaml sequence\n");
272 config = ec_config_list();
273 if (config == NULL) {
274 fprintf(stderr, "Failed to allocate config\n");
278 for (item = ynode->data.sequence.items.start;
279 item < ynode->data.sequence.items.top; item++) {
280 value = document->nodes.start + (*item) - 1; // XXX -1 ?
281 subconfig = parse_ec_config(table, schema, document, value);
282 if (subconfig == NULL)
284 if (ec_config_list_add(config, subconfig) < 0) {
285 fprintf(stderr, "Failed to add list entry\n");
293 ec_config_free(config);
297 static struct ec_config *
298 parse_ec_config_dict(struct enode_table *table,
299 const struct ec_config_schema *schema,
300 const yaml_document_t *document, const yaml_node_t *ynode)
302 const struct ec_config_schema *schema_elt;
303 struct ec_config *config = NULL, *subconfig = NULL;
304 const yaml_node_t *key, *value;
305 const yaml_node_pair_t *pair;
308 if (ynode->type != YAML_MAPPING_NODE) {
309 fprintf(stderr, "Ecoli config should be a yaml mapping node\n");
313 config = ec_config_dict();
314 if (config == NULL) {
315 fprintf(stderr, "Failed to allocate config\n");
319 for (pair = ynode->data.mapping.pairs.start;
320 pair < ynode->data.mapping.pairs.top; pair++) {
321 key = document->nodes.start + pair->key - 1; // XXX -1 ?
322 value = document->nodes.start + pair->value - 1;
323 key_str = (const char *)key->data.scalar.value;
325 if (ec_config_key_is_reserved(key_str))
327 schema_elt = ec_config_schema_lookup(schema, key_str);
328 if (schema_elt == NULL) {
329 fprintf(stderr, "No such config %s\n", key_str);
332 subconfig = parse_ec_config(table, schema_elt, document, value);
333 if (subconfig == NULL)
335 if (ec_config_dict_set(config, key_str, subconfig) < 0) {
336 fprintf(stderr, "Failed to set dict entry\n");
344 ec_config_free(config);
348 static struct ec_node *
349 parse_ec_node(struct enode_table *table,
350 const yaml_document_t *document, const yaml_node_t *ynode)
352 const struct ec_config_schema *schema;
353 const struct ec_node_type *type = NULL;
354 const char *id = NULL;
356 struct ec_config *config = NULL;
357 const yaml_node_t *attrs = NULL;
358 const yaml_node_t *key, *value;
359 const yaml_node_pair_t *pair;
360 const char *key_str, *value_str;
361 struct ec_node *enode = NULL;
363 if (ynode->type != YAML_MAPPING_NODE) {
364 fprintf(stderr, "Ecoli node should be a yaml mapping node\n");
368 for (pair = ynode->data.mapping.pairs.start;
369 pair < ynode->data.mapping.pairs.top; pair++) {
370 key = document->nodes.start + pair->key - 1; // XXX -1 ?
371 value = document->nodes.start + pair->value - 1;
372 key_str = (const char *)key->data.scalar.value;
373 value_str = (const char *)value->data.scalar.value;
375 if (!strcmp(key_str, "type")) {
377 fprintf(stderr, "Duplicate type\n");
380 if (value->type != YAML_SCALAR_NODE) {
381 fprintf(stderr, "Type must be a string\n");
384 type = ec_node_type_lookup(value_str);
386 fprintf(stderr, "Cannot find type %s\n",
390 } else if (!strcmp(key_str, "attrs")) {
392 fprintf(stderr, "Duplicate attrs\n");
395 if (value->type != YAML_MAPPING_NODE) {
396 fprintf(stderr, "Attrs must be a maping\n");
400 } else if (!strcmp(key_str, "id")) {
402 fprintf(stderr, "Duplicate id\n");
405 if (value->type != YAML_SCALAR_NODE) {
406 fprintf(stderr, "Id must be a scalar\n");
410 } else if (!strcmp(key_str, "help")) {
412 fprintf(stderr, "Duplicate help\n");
415 if (value->type != YAML_SCALAR_NODE) {
416 fprintf(stderr, "Help must be a scalar\n");
419 help = ec_strdup(value_str);
421 fprintf(stderr, "Failed to allocate help\n");
427 /* create the ecoli node */
430 enode = ec_node_from_type(type, id);
432 fprintf(stderr, "Cannot create ecoli node\n");
435 if (add_in_table(table, ynode, enode) < 0) {
436 fprintf(stderr, "Cannot add node in table\n");
440 /* create its config */
441 schema = ec_node_type_schema(type);
442 if (schema == NULL) {
443 fprintf(stderr, "No configuration schema for type %s\n",
444 ec_node_type_name(type));
448 config = parse_ec_config_dict(table, schema, document, ynode);
452 if (ec_node_set_config(enode, config) < 0) {
453 fprintf(stderr, "Failed to set config\n");
458 if (ec_keyval_set(ec_node_attrs(enode), "help", help,
460 fprintf(stderr, "Failed to set help\n");
467 /* add attributes (all as string) */
474 ec_config_free(config);
480 static struct ec_node *
481 parse_document(struct enode_table *table,
482 const yaml_document_t *document)
486 node = document->nodes.start;
487 return parse_ec_node(table, document, node);
491 ec_yaml_import(const char *filename)
494 yaml_parser_t parser;
495 yaml_document_t document;
496 struct ec_node *root = NULL;
497 struct enode_table table;
499 memset(&table, 0, sizeof(table));
501 file = fopen(filename, "rb");
503 fprintf(stderr, "Failed to open file %s\n", filename);
507 if (yaml_parser_initialize(&parser) == 0) {
508 fprintf(stderr, "Failed to initialize yaml parser\n");
512 yaml_parser_set_input_file(&parser, file);
514 if (yaml_parser_load(&parser, &document) == 0) {
515 fprintf(stderr, "Failed to load yaml document\n");
519 if (yaml_document_get_root_node(&document) == NULL) {
520 fprintf(stderr, "Incomplete document\n"); //XXX check err
524 root = parse_document(&table, &document);
526 fprintf(stderr, "Failed to parse document\n");
530 yaml_document_delete(&document);
531 yaml_parser_delete(&parser);
538 yaml_document_delete(&document);
540 yaml_parser_delete(&parser);