4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 of the
9 * License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
21 * Copyright (c) 2014, 2017, Intel Corporation.
24 * Amir Shehata <amir.shehata@intel.com>
28 * The cYAML tree is constructed as an n-tree.
32 * cmd 2 -> attr1 -> attr2
35 * attr2.1 -> attr2.1.1 -> attr2.1.2
47 #include "libcfs/util/list.h"
53 #define PRINT_BUF_LEN 2048
57 * This structure contains print information
58 * required when printing the node
60 struct cYAML_print_info {
68 * Linked list of different trees representing YAML
72 struct list_head list;
74 struct cYAML_print_info *print_info;
77 static void print_value(char **out, struct list_head *stack);
79 enum cYAML_handler_error {
81 CYAML_ERROR_UNEXPECTED_STATE = -1,
82 CYAML_ERROR_NOT_SUPPORTED = -2,
83 CYAML_ERROR_OUT_OF_MEM = -3,
84 CYAML_ERROR_BAD_VALUE = -4,
85 CYAML_ERROR_PARSE = -5,
88 enum cYAML_tree_state {
89 TREE_STATE_COMPLETE = 0,
91 TREE_STATE_TREE_STARTED,
92 TREE_STATE_BLK_STARTED,
94 TREE_STATE_KEY_FILLED,
99 struct cYAML_tree_node {
101 /* cur is the current node we're operating on */
103 enum cYAML_tree_state state;
104 int from_blk_map_start;
105 /* represents the tree depth */
109 typedef enum cYAML_handler_error (*yaml_token_handler)(yaml_token_t *token,
110 struct cYAML_tree_node *);
112 static enum cYAML_handler_error yaml_no_token(yaml_token_t *token,
113 struct cYAML_tree_node *tree);
114 static enum cYAML_handler_error yaml_stream_start(yaml_token_t *token,
115 struct cYAML_tree_node *tree);
116 static enum cYAML_handler_error yaml_stream_end(yaml_token_t *token,
117 struct cYAML_tree_node *tree);
118 static enum cYAML_handler_error yaml_not_supported(yaml_token_t *token,
119 struct cYAML_tree_node *tree);
120 static enum cYAML_handler_error yaml_document_start(yaml_token_t *token,
121 struct cYAML_tree_node *tree);
122 static enum cYAML_handler_error yaml_document_end(yaml_token_t *token,
123 struct cYAML_tree_node *tree);
124 static enum cYAML_handler_error yaml_blk_seq_start(yaml_token_t *token,
125 struct cYAML_tree_node *tree);
126 static enum cYAML_handler_error yaml_blk_mapping_start(yaml_token_t *token,
127 struct cYAML_tree_node *tree);
128 static enum cYAML_handler_error yaml_block_end(yaml_token_t *token,
129 struct cYAML_tree_node *tree);
130 static enum cYAML_handler_error yaml_key(yaml_token_t *token,
131 struct cYAML_tree_node *tree);
132 static enum cYAML_handler_error yaml_value(yaml_token_t *token,
133 struct cYAML_tree_node *tree);
134 static enum cYAML_handler_error yaml_scalar(yaml_token_t *token,
135 struct cYAML_tree_node *tree);
136 static enum cYAML_handler_error yaml_entry_token(yaml_token_t *token,
137 struct cYAML_tree_node *tree);
140 static yaml_token_handler dispatch_tbl[] = {
141 [YAML_NO_TOKEN] = yaml_no_token,
142 [YAML_STREAM_START_TOKEN] = yaml_stream_start,
143 [YAML_STREAM_END_TOKEN] = yaml_stream_end,
144 [YAML_VERSION_DIRECTIVE_TOKEN] = yaml_not_supported,
145 [YAML_TAG_DIRECTIVE_TOKEN] = yaml_not_supported,
146 [YAML_DOCUMENT_START_TOKEN] = yaml_document_start,
147 [YAML_DOCUMENT_END_TOKEN] = yaml_document_end,
148 [YAML_BLOCK_SEQUENCE_START_TOKEN] = yaml_blk_seq_start,
149 [YAML_BLOCK_MAPPING_START_TOKEN] = yaml_blk_mapping_start,
150 [YAML_BLOCK_END_TOKEN] = yaml_block_end,
151 [YAML_FLOW_SEQUENCE_START_TOKEN] = yaml_not_supported,
152 [YAML_FLOW_SEQUENCE_END_TOKEN] = yaml_not_supported,
153 [YAML_FLOW_MAPPING_START_TOKEN] = yaml_not_supported,
154 [YAML_FLOW_MAPPING_END_TOKEN] = yaml_not_supported,
155 [YAML_BLOCK_ENTRY_TOKEN] = yaml_entry_token,
156 [YAML_FLOW_ENTRY_TOKEN] = yaml_not_supported,
157 [YAML_KEY_TOKEN] = yaml_key,
158 [YAML_VALUE_TOKEN] = yaml_value,
159 [YAML_ALIAS_TOKEN] = yaml_not_supported,
160 [YAML_ANCHOR_TOKEN] = yaml_not_supported,
161 [YAML_TAG_TOKEN] = yaml_not_supported,
162 [YAML_SCALAR_TOKEN] = yaml_scalar,
166 static const char * const token_type_string[] = {
167 [YAML_NO_TOKEN] = "YAML_NO_TOKEN",
168 [YAML_STREAM_START_TOKEN] = "YAML_STREAM_START_TOKEN",
169 [YAML_STREAM_END_TOKEN] = "YAML_STREAM_END_TOKEN",
170 [YAML_VERSION_DIRECTIVE_TOKEN] = "YAML_VERSION_DIRECTIVE_TOKEN",
171 [YAML_TAG_DIRECTIVE_TOKEN] = "YAML_TAG_DIRECTIVE_TOKEN",
172 [YAML_DOCUMENT_START_TOKEN] = "YAML_DOCUMENT_START_TOKEN",
173 [YAML_DOCUMENT_END_TOKEN] = "YAML_DOCUMENT_END_TOKEN",
174 [YAML_BLOCK_SEQUENCE_START_TOKEN] = "YAML_BLOCK_SEQUENCE_START_TOKEN",
175 [YAML_BLOCK_MAPPING_START_TOKEN] = "YAML_BLOCK_MAPPING_START_TOKEN",
176 [YAML_BLOCK_END_TOKEN] = "YAML_BLOCK_END_TOKEN",
177 [YAML_FLOW_SEQUENCE_START_TOKEN] = "YAML_FLOW_SEQUENCE_START_TOKEN",
178 [YAML_FLOW_SEQUENCE_END_TOKEN] = "YAML_FLOW_SEQUENCE_END_TOKEN",
179 [YAML_FLOW_MAPPING_START_TOKEN] = "YAML_FLOW_MAPPING_START_TOKEN",
180 [YAML_FLOW_MAPPING_END_TOKEN] = "YAML_FLOW_MAPPING_END_TOKEN",
181 [YAML_BLOCK_ENTRY_TOKEN] = "YAML_BLOCK_ENTRY_TOKEN",
182 [YAML_FLOW_ENTRY_TOKEN] = "YAML_FLOW_ENTRY_TOKEN",
183 [YAML_KEY_TOKEN] = "YAML_KEY_TOKEN",
184 [YAML_VALUE_TOKEN] = "YAML_VALUE_TOKEN",
185 [YAML_ALIAS_TOKEN] = "YAML_ALIAS_TOKEN",
186 [YAML_ANCHOR_TOKEN] = "YAML_ANCHOR_TOKEN",
187 [YAML_TAG_TOKEN] = "YAML_TAG_TOKEN",
188 [YAML_SCALAR_TOKEN] = "YAML_SCALAR_TOKEN",
191 static const char * const state_string[] = {
192 [TREE_STATE_COMPLETE] = "COMPLETE",
193 [TREE_STATE_INITED] = "INITED",
194 [TREE_STATE_TREE_STARTED] = "TREE_STARTED",
195 [TREE_STATE_BLK_STARTED] = "BLK_STARTED",
196 [TREE_STATE_KEY] = "KEY",
197 [TREE_STATE_KEY_FILLED] = "KEY_FILLED",
198 [TREE_STATE_VALUE] = "VALUE",
199 [TREE_STATE_SEQ_START] = "SEQ_START",
202 static void cYAML_ll_free(struct list_head *ll)
204 struct cYAML_ll *node, *tmp;
206 list_for_each_entry_safe(node, tmp, ll, list) {
207 free(node->print_info);
212 static int cYAML_ll_push(struct cYAML *obj,
213 const struct cYAML_print_info *print_info,
214 struct list_head *list)
216 struct cYAML_ll *node = calloc(1, sizeof(*node));
220 INIT_LIST_HEAD(&node->list);
223 node->print_info = calloc(1, sizeof(*print_info));
224 if (node->print_info == NULL) {
228 *node->print_info = *print_info;
232 list_add(&node->list, list);
237 static struct cYAML *cYAML_ll_pop(struct list_head *list,
238 struct cYAML_print_info **print_info)
240 struct cYAML_ll *pop;
241 struct cYAML *obj = NULL;
243 if (!list_empty(list)) {
244 pop = list_entry(list->next, struct cYAML_ll, list);
247 if (print_info != NULL)
248 *print_info = pop->print_info;
249 list_del(&pop->list);
251 if (print_info == NULL)
252 free(pop->print_info);
259 static int cYAML_ll_count(struct list_head *ll)
262 struct list_head *node;
264 list_for_each(node, ll)
270 static int cYAML_tree_init(struct cYAML_tree_node *tree)
272 struct cYAML *obj = NULL, *cur = NULL;
277 obj = calloc(1, sizeof(*obj));
282 /* append the node */
284 while (cur->cy_next != NULL)
291 obj->cy_type = CYAML_TYPE_OBJECT;
294 tree->state = TREE_STATE_COMPLETE;
296 /* free it and start anew */
297 if (!list_empty(&tree->ll))
298 cYAML_ll_free(&tree->ll);
303 static struct cYAML *create_child(struct cYAML *parent)
310 obj = calloc(1, sizeof(*obj));
314 /* set the type to OBJECT and let the value change that */
315 obj->cy_type = CYAML_TYPE_OBJECT;
317 parent->cy_child = obj;
322 static struct cYAML *create_sibling(struct cYAML *sibling)
329 obj = calloc(1, sizeof(*obj));
333 /* set the type to OBJECT and let the value change that */
334 obj->cy_type = CYAML_TYPE_OBJECT;
336 sibling->cy_next = obj;
337 obj->cy_prev = sibling;
342 /* Parse the input text to generate a number,
343 * and populate the result into item. */
344 static bool parse_number(struct cYAML *item, const char *input)
346 double n = 0, sign = 1, scale = 0;
347 int subscale = 0, signsubscale = 1;
348 const char *num = input;
358 if (*num >= '1' && *num <= '9') {
360 n = (n * 10.0) + (*num++ - '0');
361 } while (*num >= '0' && *num <= '9');
364 if (*num == '.' && num[1] >= '0' && num[1] <= '9') {
367 n = (n * 10.0) + (*num++ - '0');
369 } while (*num >= '0' && *num <= '9');
372 if (*num == 'e' || *num == 'E') {
376 } else if (*num == '-') {
380 while (*num >= '0' && *num <= '9')
381 subscale = (subscale * 10) + (*num++ - '0');
384 /* check to see if the entire string is consumed. If not then
385 * that means this is a string with a number in it */
386 if (num != (input + strlen(input)))
389 /* number = +/- number.fraction * 10^+/- exponent */
390 n = sign * n * pow(10.0, (scale + subscale * signsubscale));
392 item->cy_valuedouble = n;
393 item->cy_valueint = (int)n;
394 item->cy_type = CYAML_TYPE_NUMBER;
399 static int assign_type_value(struct cYAML *obj, const char *value)
404 if (strcmp(value, "null") == 0)
405 obj->cy_type = CYAML_TYPE_NULL;
406 else if (strcmp(value, "false") == 0) {
407 obj->cy_type = CYAML_TYPE_FALSE;
408 obj->cy_valueint = 0;
409 } else if (strcmp(value, "true") == 0) {
410 obj->cy_type = CYAML_TYPE_TRUE;
411 obj->cy_valueint = 1;
412 } else if (*value == '-' || (*value >= '0' && *value <= '9')) {
413 if (parse_number(obj, value) == 0) {
414 obj->cy_valuestring = strdup(value);
415 obj->cy_type = CYAML_TYPE_STRING;
418 obj->cy_valuestring = strdup(value);
419 obj->cy_type = CYAML_TYPE_STRING;
427 * Builds the YAML tree rpresentation as the tokens are passed in
429 * if token == STREAM_START && tree_state != COMPLETE
430 * something wrong. fail.
431 * else tree_state = INITIED
432 * if token == DOCUMENT_START && tree_state != COMPLETE || INITED
433 * something wrong, fail.
434 * else tree_state = TREE_STARTED
435 * if token == DOCUMENT_END
436 * tree_state = INITED if no STREAM START, else tree_state = COMPLETE
437 * erase everything on ll
438 * if token == STREAM_END && tree_state != INITED
439 * something wrong fail.
440 * else tree_state = COMPLETED
441 * if token == YAML_KEY_TOKEN && state != TREE_STARTED
442 * something wrong, fail.
443 * if token == YAML_SCALAR_TOKEN && state != KEY || VALUE
445 * else if tree_state == KEY
446 * create a new sibling under the current head of the ll (if ll is
447 * empty insert the new node there and it becomes the root.)
448 * add the scalar value in the "string"
449 * tree_state = KEY_FILLED
450 * else if tree_state == VALUE
451 * try and figure out whether this is a double, int or string and store
453 * state = TREE_STARTED
454 * else if token == YAML_BLOCK_MAPPING_START_TOKEN && tree_state != VALUE
456 * else push the current node on the ll && state = TREE_STARTED
457 * if token == YAML_BLOCK_END_TOKEN && state != TREE_STARTED
459 * else pop the current token off the ll and make it the cur
460 * if token == YAML_VALUE_TOKEN && state != KEY_FILLED
466 static enum cYAML_handler_error yaml_no_token(yaml_token_t *token,
467 struct cYAML_tree_node *tree)
469 return CYAML_ERROR_NONE;
472 static enum cYAML_handler_error yaml_stream_start(yaml_token_t *token,
473 struct cYAML_tree_node *tree)
475 enum cYAML_handler_error rc;
477 /* with each new stream initialize a new tree */
478 rc = cYAML_tree_init(tree);
480 if (rc != CYAML_ERROR_NONE)
483 tree->state = TREE_STATE_INITED;
485 return CYAML_ERROR_NONE;
488 static enum cYAML_handler_error yaml_stream_end(yaml_token_t *token,
489 struct cYAML_tree_node *tree)
491 if (tree->state != TREE_STATE_TREE_STARTED &&
492 tree->state != TREE_STATE_COMPLETE &&
493 tree->state != TREE_STATE_INITED)
494 return CYAML_ERROR_UNEXPECTED_STATE;
496 tree->state = TREE_STATE_INITED;
498 return CYAML_ERROR_NONE;
501 static enum cYAML_handler_error
502 yaml_document_start(yaml_token_t *token, struct cYAML_tree_node *tree)
504 if (tree->state != TREE_STATE_INITED)
505 return CYAML_ERROR_UNEXPECTED_STATE;
507 /* go to started state since we're expecting more tokens to come */
508 tree->state = TREE_STATE_TREE_STARTED;
510 return CYAML_ERROR_NONE;
513 static enum cYAML_handler_error yaml_document_end(yaml_token_t *token,
514 struct cYAML_tree_node *tree)
516 if (tree->state != TREE_STATE_COMPLETE)
517 return CYAML_ERROR_UNEXPECTED_STATE;
519 tree->state = TREE_STATE_TREE_STARTED;
521 return CYAML_ERROR_NONE;
524 static enum cYAML_handler_error yaml_key(yaml_token_t *token,
525 struct cYAML_tree_node *tree)
527 if (tree->state != TREE_STATE_BLK_STARTED &&
528 tree->state != TREE_STATE_VALUE)
529 return CYAML_ERROR_UNEXPECTED_STATE;
531 if (tree->from_blk_map_start == 0 ||
532 tree->state == TREE_STATE_VALUE)
533 tree->cur = create_sibling(tree->cur);
535 tree->from_blk_map_start = 0;
537 tree->state = TREE_STATE_KEY;
539 return CYAML_ERROR_NONE;
542 static enum cYAML_handler_error yaml_scalar(yaml_token_t *token,
543 struct cYAML_tree_node *tree)
545 if (tree->state == TREE_STATE_KEY) {
546 /* assign the scalar value to the key that was created */
547 tree->cur->cy_string =
548 strdup((const char *)token->data.scalar.value);
550 tree->state = TREE_STATE_KEY_FILLED;
551 } else if (tree->state == TREE_STATE_VALUE ||
552 tree->state == TREE_STATE_SEQ_START) {
553 if (assign_type_value(tree->cur,
554 (char *)token->data.scalar.value))
555 /* failed to assign a value */
556 return CYAML_ERROR_BAD_VALUE;
557 tree->state = TREE_STATE_BLK_STARTED;
559 return CYAML_ERROR_UNEXPECTED_STATE;
562 return CYAML_ERROR_NONE;
565 static enum cYAML_handler_error yaml_value(yaml_token_t *token,
566 struct cYAML_tree_node *tree)
568 if (tree->state != TREE_STATE_KEY_FILLED)
569 return CYAML_ERROR_UNEXPECTED_STATE;
571 tree->state = TREE_STATE_VALUE;
573 return CYAML_ERROR_NONE;
576 static enum cYAML_handler_error yaml_blk_seq_start(yaml_token_t *token,
577 struct cYAML_tree_node *tree)
579 if (tree->state != TREE_STATE_VALUE)
580 return CYAML_ERROR_UNEXPECTED_STATE;
582 /* Since a sequenc start event determines that this is the start
583 * of an array, then that means the current node we're at is an
584 * array and we need to flag it as such */
585 tree->cur->cy_type = CYAML_TYPE_ARRAY;
586 tree->state = TREE_STATE_SEQ_START;
588 return CYAML_ERROR_NONE;
591 static enum cYAML_handler_error yaml_entry_token(yaml_token_t *token,
592 struct cYAML_tree_node *tree)
596 if (tree->state != TREE_STATE_SEQ_START &&
597 tree->state != TREE_STATE_BLK_STARTED &&
598 tree->state != TREE_STATE_VALUE)
599 return CYAML_ERROR_UNEXPECTED_STATE;
601 if (tree->state == TREE_STATE_SEQ_START) {
602 obj = create_child(tree->cur);
604 if (cYAML_ll_push(tree->cur, NULL, &tree->ll))
605 return CYAML_ERROR_OUT_OF_MEM;
609 tree->cur = create_sibling(tree->cur);
610 tree->state = TREE_STATE_SEQ_START;
613 return CYAML_ERROR_NONE;
616 static enum cYAML_handler_error
617 yaml_blk_mapping_start(yaml_token_t *token,
618 struct cYAML_tree_node *tree)
622 if (tree->state != TREE_STATE_VALUE &&
623 tree->state != TREE_STATE_INITED &&
624 tree->state != TREE_STATE_SEQ_START &&
625 tree->state != TREE_STATE_TREE_STARTED)
626 return CYAML_ERROR_UNEXPECTED_STATE;
628 /* block_mapping_start means we're entering another block
629 * indentation, so we need to go one level deeper
630 * create a child of cur */
631 obj = create_child(tree->cur);
633 /* push cur on the stack */
634 if (cYAML_ll_push(tree->cur, NULL, &tree->ll))
635 return CYAML_ERROR_OUT_OF_MEM;
637 /* adding the new child to cur */
640 tree->state = TREE_STATE_BLK_STARTED;
642 tree->from_blk_map_start = 1;
644 return CYAML_ERROR_NONE;
647 static enum cYAML_handler_error yaml_block_end(yaml_token_t *token,
648 struct cYAML_tree_node *tree)
650 if (tree->state != TREE_STATE_BLK_STARTED &&
651 tree->state != TREE_STATE_VALUE)
652 return CYAML_ERROR_UNEXPECTED_STATE;
654 tree->cur = cYAML_ll_pop(&tree->ll, NULL);
656 /* if you have popped all the way to the top level, then move to
657 * the complete state. */
658 if (cYAML_ll_count(&tree->ll) == 0)
659 tree->state = TREE_STATE_COMPLETE;
660 else if (tree->state == TREE_STATE_VALUE)
661 tree->state = TREE_STATE_BLK_STARTED;
663 return CYAML_ERROR_NONE;
666 static enum cYAML_handler_error yaml_not_supported(yaml_token_t *token,
667 struct cYAML_tree_node *tree)
669 return CYAML_ERROR_NOT_SUPPORTED;
672 static bool clean_usr_data(struct cYAML *node, void *usr_data, void **out)
674 cYAML_user_data_free_cb free_cb = usr_data;
676 if (free_cb && node && node->cy_user_data) {
677 free_cb(node->cy_user_data);
678 node->cy_user_data = NULL;
684 static bool free_node(struct cYAML *node, void *user_data, void **out)
689 if (node->cy_type == CYAML_TYPE_STRING)
690 free(node->cy_valuestring);
692 free(node->cy_string);
698 static bool find_obj_iter(struct cYAML *node, void *usr_data, void **out)
700 char *name = usr_data;
702 if (node != NULL && node->cy_string != NULL &&
703 strcmp(node->cy_string, name) == 0) {
711 struct cYAML *cYAML_get_object_item(struct cYAML *parent, const char *name)
713 struct cYAML *node = parent, *found = NULL;
718 if (node->cy_string) {
719 if (strcmp(node->cy_string, name) == 0)
724 found = cYAML_get_object_item(node->cy_child, name);
726 if (!found && node->cy_next)
727 found = cYAML_get_object_item(node->cy_next, name);
732 struct cYAML *cYAML_get_next_seq_item(struct cYAML *seq, struct cYAML **itm)
734 if (*itm != NULL && (*itm)->cy_next != NULL) {
735 *itm = (*itm)->cy_next;
739 if (*itm == NULL && seq->cy_type == CYAML_TYPE_ARRAY) {
740 *itm = seq->cy_child;
747 bool cYAML_is_sequence(struct cYAML *node)
749 return (node != NULL ? node->cy_type == CYAML_TYPE_ARRAY : 0);
752 void cYAML_tree_recursive_walk(struct cYAML *node, cYAML_walk_cb cb,
761 if (!cb(node, usr_data, out))
766 cYAML_tree_recursive_walk(node->cy_child, cb,
767 cb_first, usr_data, out);
770 cYAML_tree_recursive_walk(node->cy_next, cb,
771 cb_first, usr_data, out);
774 if (!cb(node, usr_data, out))
779 struct cYAML *cYAML_find_object(struct cYAML *root, const char *name)
781 struct cYAML *found = NULL;
783 cYAML_tree_recursive_walk(root, find_obj_iter, true,
784 (void *)name, (void **)&found);
789 void cYAML_clean_usr_data(struct cYAML *node, cYAML_user_data_free_cb free_cb)
791 cYAML_tree_recursive_walk(node, clean_usr_data, false, free_cb, NULL);
794 void cYAML_free_tree(struct cYAML *node)
796 cYAML_tree_recursive_walk(node, free_node, false, NULL, NULL);
799 static char *ensure(char *in, int len)
805 return (char*)calloc(len, 1);
807 curlen = strlen(in) + 1;
809 if (curlen <= curlen + len) {
810 new = calloc(curlen + len, 1);
822 static inline void print_simple(char **out, struct cYAML *node,
823 struct cYAML_print_info *cpi)
825 int level = cpi->level;
826 int ind = cpi->extra_ind;
828 int len = (INDENT * level + ind) * 2 +
829 ((node->cy_string) ? strlen(node->cy_string) : 0) + LEAD_ROOM;
831 *out = ensure(*out, len);
835 tmp = ensure(tmp, len);
839 if (cpi->array_first_elem) {
840 sprintf(tmp, "%*s- ", INDENT * level, "");
844 sprintf(tmp, "%*s""%s: %" PRId64 "\n", (cpi->array_first_elem) ? 0 :
845 INDENT * level + ind, "", node->cy_string,
851 static void print_string(char **out, struct cYAML *node,
852 struct cYAML_print_info *cpi)
855 int level = cpi->level;
856 int ind = cpi->extra_ind;
858 int len = INDENT * level + ind +
859 ((node->cy_valuestring) ? strlen(node->cy_valuestring) : 0) +
860 ((node->cy_string) ? strlen(node->cy_string) : 0) + LEAD_ROOM;
862 *out = ensure(*out, len);
866 tmp = ensure(tmp, len);
870 if (cpi->array_first_elem) {
871 sprintf(tmp, "%*s- ", INDENT * level, "");
875 new_line = strchr(node->cy_valuestring, '\n');
876 if (new_line == NULL) {
877 sprintf(tmp, "%*s""%s: %s\n", (cpi->array_first_elem) ?
878 0 : INDENT * level + ind, "",
879 node->cy_string, node->cy_valuestring);
883 sprintf(tmp, "%*s""%s: ", (cpi->array_first_elem) ?
884 0 : INDENT * level + ind, "",
887 char *l = node->cy_valuestring;
890 sprintf(tmp, "%*s""%s\n", indent, "", l);
892 indent = INDENT * level + ind +
893 strlen(node->cy_string) + 2;
896 new_line = strchr(l, '\n');
898 sprintf(tmp, "%*s""%s\n", indent, "", l);
905 static void print_number(char **out, struct cYAML *node,
906 struct cYAML_print_info *cpi)
908 double d = node->cy_valuedouble;
909 int level = cpi->level;
910 int ind = cpi->extra_ind;
912 int len = INDENT * level + ind + LEAD_ROOM;
914 *out = ensure(*out, len);
918 tmp = ensure(tmp, len);
922 if (cpi->array_first_elem) {
923 sprintf(tmp, "%*s- ", INDENT * level, "");
927 if ((fabs(((double)node->cy_valueint) - d) <= DBL_EPSILON) &&
928 (d <= INT_MAX) && (d >= INT_MIN)) {
929 sprintf(tmp, "%*s""%s: %" PRId64 "\n", (cpi->array_first_elem) ? 0 :
930 INDENT * level + ind, "",
931 node->cy_string, node->cy_valueint);
934 if ((fabs(floor(d) - d) <= DBL_EPSILON) &&
935 (fabs(d) < 1.0e60)) {
936 sprintf(tmp, "%*s""%s: %.0f\n",
937 (cpi->array_first_elem) ? 0 :
938 INDENT * level + ind, "",
941 } else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) {
942 sprintf(tmp, "%*s""%s: %e\n",
943 (cpi->array_first_elem) ? 0 :
944 INDENT * level + ind, "",
948 sprintf(tmp, "%*s""%s: %f\n",
949 (cpi->array_first_elem) ? 0 :
950 INDENT * level + ind, "",
959 static void print_object(char **out, struct cYAML *node,
960 struct list_head *stack,
961 struct cYAML_print_info *cpi)
963 struct cYAML_print_info print_info;
964 struct cYAML *child = node->cy_child;
966 int len = ((cpi->array_first_elem) ? INDENT * cpi->level :
967 INDENT * cpi->level + cpi->extra_ind) +
968 ((node->cy_string) ? strlen(node->cy_string) : 0) +
971 *out = ensure(*out, len);
975 tmp = ensure(tmp, len);
979 if (node->cy_string != NULL) {
980 sprintf(tmp, "%*s""%s%s:\n", (cpi->array_first_elem) ?
981 INDENT * cpi->level :
982 INDENT * cpi->level + cpi->extra_ind,
983 "", (cpi->array_first_elem) ? "- " : "",
988 print_info.level = (node->cy_string != NULL) ? cpi->level + 1 :
990 print_info.array_first_elem = (node->cy_string == NULL) ?
991 cpi->array_first_elem : 0;
992 print_info.extra_ind = (cpi->array_first_elem) ? EXTRA_IND :
996 if (cYAML_ll_push(child, &print_info, stack) != 0) {
1000 print_value(out, stack);
1001 print_info.array_first_elem = 0;
1002 child = child->cy_next;
1008 static void print_array(char **out, struct cYAML *node,
1009 struct list_head *stack,
1010 struct cYAML_print_info *cpi)
1012 struct cYAML_print_info print_info;
1013 struct cYAML *child = node->cy_child;
1015 int len = ((node->cy_string) ? strlen(node->cy_string) : 0) +
1016 INDENT * cpi->level + cpi->extra_ind + LEAD_ROOM;
1018 *out = ensure(*out, len);
1022 tmp = ensure(tmp, len);
1026 if (node->cy_string != NULL) {
1027 sprintf(tmp, "%*s""%s:\n", INDENT * cpi->level + cpi->extra_ind,
1028 "", node->cy_string);
1032 print_info.level = (node->cy_string != NULL) ? cpi->level + 1 :
1034 print_info.array_first_elem = 1;
1035 print_info.extra_ind = EXTRA_IND;
1038 if (cYAML_ll_push(child, &print_info, stack) != 0) {
1042 print_value(out, stack);
1043 child = child->cy_next;
1049 static void print_value(char **out, struct list_head *stack)
1051 struct cYAML_print_info *cpi = NULL;
1052 struct cYAML *node = cYAML_ll_pop(stack, &cpi);
1057 switch (node->cy_type) {
1058 case CYAML_TYPE_FALSE:
1059 case CYAML_TYPE_TRUE:
1060 case CYAML_TYPE_NULL:
1061 print_simple(out, node, cpi);
1063 case CYAML_TYPE_STRING:
1064 print_string(out, node, cpi);
1066 case CYAML_TYPE_NUMBER:
1067 print_number(out, node, cpi);
1069 case CYAML_TYPE_ARRAY:
1070 print_array(out, node, stack, cpi);
1072 case CYAML_TYPE_OBJECT:
1073 print_object(out, node, stack, cpi);
1083 void cYAML_dump(struct cYAML *node, char **buf)
1085 struct cYAML_print_info print_info;
1086 struct list_head list;
1088 *buf = ensure(NULL, PRINT_BUF_LEN);
1093 INIT_LIST_HEAD(&list);
1100 memset(&print_info, 0, sizeof(struct cYAML_print_info));
1102 if (cYAML_ll_push(node, &print_info, &list) == 0)
1103 print_value(buf, &list);
1106 void cYAML_print_tree(struct cYAML *node)
1108 struct cYAML_print_info print_info;
1109 struct list_head list;
1110 char *buf = ensure(NULL, PRINT_BUF_LEN);
1115 INIT_LIST_HEAD(&list);
1120 memset(&print_info, 0, sizeof(struct cYAML_print_info));
1122 if (cYAML_ll_push(node, &print_info, &list) == 0)
1123 print_value(&buf, &list);
1125 /* buf could've been freed if we ran out of memory */
1132 void cYAML_print_tree2file(FILE *f, struct cYAML *node)
1134 struct cYAML_print_info print_info;
1135 struct list_head list;
1136 char *buf = ensure(NULL, PRINT_BUF_LEN);
1141 INIT_LIST_HEAD(&list);
1146 memset(&print_info, 0, sizeof(struct cYAML_print_info));
1148 if (cYAML_ll_push(node, &print_info, &list) == 0)
1149 print_value(&buf, &list);
1151 /* buf could've been freed if we ran out of memory */
1153 fprintf(f, "%s", buf);
1158 static struct cYAML *insert_item(struct cYAML *parent, char *key,
1159 enum cYAML_object_type type)
1161 struct cYAML *node = calloc(1, sizeof(*node));
1167 node->cy_string = strdup(key);
1169 node->cy_type = type;
1171 cYAML_insert_child(parent, node);
1176 struct cYAML *cYAML_create_seq(struct cYAML *parent, char *key)
1178 return insert_item(parent, key, CYAML_TYPE_ARRAY);
1181 struct cYAML *cYAML_create_seq_item(struct cYAML *seq)
1183 return insert_item(seq, NULL, CYAML_TYPE_OBJECT);
1186 struct cYAML *cYAML_create_object(struct cYAML *parent, char *key)
1188 return insert_item(parent, key, CYAML_TYPE_OBJECT);
1191 struct cYAML *cYAML_create_string(struct cYAML *parent, char *key, char *value)
1193 struct cYAML *node = calloc(1, sizeof(*node));
1197 node->cy_string = strdup(key);
1198 node->cy_valuestring = strdup(value);
1199 node->cy_type = CYAML_TYPE_STRING;
1201 cYAML_insert_child(parent, node);
1206 struct cYAML *cYAML_create_number(struct cYAML *parent, char *key, double value)
1208 struct cYAML *node = calloc(1, sizeof(*node));
1212 node->cy_string = strdup(key);
1213 node->cy_valuedouble = value;
1214 node->cy_valueint = (int)value;
1215 node->cy_type = CYAML_TYPE_NUMBER;
1217 cYAML_insert_child(parent, node);
1222 void cYAML_insert_child(struct cYAML *parent, struct cYAML *node)
1226 if (parent && node) {
1227 if (parent->cy_child == NULL) {
1228 parent->cy_child = node;
1232 cur = parent->cy_child;
1234 while (cur->cy_next)
1237 cur->cy_next = node;
1238 node->cy_prev = cur;
1242 void cYAML_insert_sibling(struct cYAML *root, struct cYAML *sibling)
1244 struct cYAML *last = NULL;
1245 if (root == NULL || sibling == NULL)
1249 while (last->cy_next != NULL)
1250 last = last->cy_next;
1252 last->cy_next = sibling;
1255 void cYAML_build_error(int rc, int seq_no, char *cmd,
1256 char *entity, char *err_str,
1257 struct cYAML **root)
1259 struct cYAML *r = NULL, *err, *s, *itm, *cmd_obj;
1263 /* add to the tail of the root that's passed in */
1264 if ((*root) == NULL) {
1265 *root = cYAML_create_object(NULL, NULL);
1266 if ((*root) == NULL)
1272 /* look for the command */
1273 cmd_obj = cYAML_get_object_item(r, (const char *)cmd);
1274 if (cmd_obj != NULL && cmd_obj->cy_type == CYAML_TYPE_ARRAY)
1275 itm = cYAML_create_seq_item(cmd_obj);
1276 else if (cmd_obj == NULL) {
1277 s = cYAML_create_seq(r, cmd);
1278 itm = cYAML_create_seq_item(s);
1279 } else if (cmd_obj != NULL && cmd_obj->cy_type != CYAML_TYPE_ARRAY)
1282 err = cYAML_create_object(itm, entity);
1287 cYAML_create_number(err, "seq_no", seq_no) == NULL)
1290 if (cYAML_create_number(err, "errno", rc) == NULL)
1293 if (cYAML_create_string(err, "descr", err_str) == NULL)
1299 /* Only reason we get here is if we run out of memory */
1302 fprintf(stderr, "error:\n\tfatal: out of memory\n");
1305 struct cYAML *cYAML_build_tree(char *yaml_file,
1306 const char *yaml_blk,
1307 size_t yaml_blk_size,
1308 struct cYAML **err_rc,
1311 yaml_parser_t parser;
1313 struct cYAML_tree_node tree;
1314 enum cYAML_handler_error rc;
1315 yaml_token_type_t token_type;
1320 memset(&tree, 0, sizeof(struct cYAML_tree_node));
1322 INIT_LIST_HEAD(&tree.ll);
1324 /* Create the Parser object. */
1325 yaml_parser_initialize(&parser);
1327 /* file always takes precedence */
1328 if (yaml_file != NULL) {
1329 /* Set a file input. */
1330 input = fopen(yaml_file, "rb");
1331 if (input == NULL) {
1332 snprintf(err_str, sizeof(err_str),
1333 "Failed to open file: %s", yaml_file);
1334 cYAML_build_error(-1, -1, "yaml", "builder",
1340 yaml_parser_set_input_file(&parser, input);
1341 } else if (yaml_blk != NULL) {
1342 yaml_parser_set_input_string(&parser,
1343 (const unsigned char *) yaml_blk,
1346 /* assume that we're getting our input froms stdin */
1347 yaml_parser_set_input_file(&parser, stdin);
1349 /* Read the event sequence. */
1352 * Go through the parser and build a cYAML representation
1353 * of the passed in YAML text
1355 yaml_parser_scan(&parser, &token);
1358 fprintf(stderr, "tree.state(%p:%d) = %s, token.type ="
1360 &tree, tree.state, state_string[tree.state],
1361 token_type_string[token.type],
1362 (token.type == YAML_SCALAR_TOKEN) ?
1363 (char*)token.data.scalar.value : "");
1364 rc = dispatch_tbl[token.type](&token, &tree);
1365 if (rc != CYAML_ERROR_NONE) {
1366 snprintf(err_str, sizeof(err_str),
1367 "Failed to handle token:%d %s [state=%d, rc=%d]",
1368 token.type, token_type_string[token.type],
1370 cYAML_build_error(-1, -1, "yaml", "builder",
1374 /* Are we finished? */
1375 done = (rc != CYAML_ERROR_NONE ||
1376 token.type == YAML_STREAM_END_TOKEN);
1378 token_type = token.type;
1380 yaml_token_delete(&token);
1383 /* Destroy the Parser object. */
1384 yaml_parser_delete(&parser);
1389 if (token_type == YAML_STREAM_END_TOKEN &&
1390 rc == CYAML_ERROR_NONE)
1393 cYAML_free_tree(tree.root);