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
48 #include "libcfs/util/list.h"
54 #define PRINT_BUF_LEN 2048
58 * This structure contains print information
59 * required when printing the node
61 struct cYAML_print_info {
69 * Linked list of different trees representing YAML
73 struct list_head list;
75 struct cYAML_print_info *print_info;
78 static void print_value(char **out, struct list_head *stack);
80 enum cYAML_handler_error {
82 CYAML_ERROR_UNEXPECTED_STATE = -1,
83 CYAML_ERROR_NOT_SUPPORTED = -2,
84 CYAML_ERROR_OUT_OF_MEM = -3,
85 CYAML_ERROR_BAD_VALUE = -4,
86 CYAML_ERROR_PARSE = -5,
89 enum cYAML_tree_state {
90 TREE_STATE_COMPLETE = 0,
92 TREE_STATE_TREE_STARTED,
93 TREE_STATE_BLK_STARTED,
95 TREE_STATE_KEY_FILLED,
100 struct cYAML_tree_node {
102 /* cur is the current node we're operating on */
104 enum cYAML_tree_state state;
105 int from_blk_map_start;
106 /* represents the tree depth */
110 typedef enum cYAML_handler_error (*yaml_token_handler)(yaml_token_t *token,
111 struct cYAML_tree_node *);
113 static enum cYAML_handler_error yaml_no_token(yaml_token_t *token,
114 struct cYAML_tree_node *tree);
115 static enum cYAML_handler_error yaml_stream_start(yaml_token_t *token,
116 struct cYAML_tree_node *tree);
117 static enum cYAML_handler_error yaml_stream_end(yaml_token_t *token,
118 struct cYAML_tree_node *tree);
119 static enum cYAML_handler_error yaml_not_supported(yaml_token_t *token,
120 struct cYAML_tree_node *tree);
121 static enum cYAML_handler_error yaml_document_start(yaml_token_t *token,
122 struct cYAML_tree_node *tree);
123 static enum cYAML_handler_error yaml_document_end(yaml_token_t *token,
124 struct cYAML_tree_node *tree);
125 static enum cYAML_handler_error yaml_blk_seq_start(yaml_token_t *token,
126 struct cYAML_tree_node *tree);
127 static enum cYAML_handler_error yaml_blk_mapping_start(yaml_token_t *token,
128 struct cYAML_tree_node *tree);
129 static enum cYAML_handler_error yaml_block_end(yaml_token_t *token,
130 struct cYAML_tree_node *tree);
131 static enum cYAML_handler_error yaml_key(yaml_token_t *token,
132 struct cYAML_tree_node *tree);
133 static enum cYAML_handler_error yaml_value(yaml_token_t *token,
134 struct cYAML_tree_node *tree);
135 static enum cYAML_handler_error yaml_scalar(yaml_token_t *token,
136 struct cYAML_tree_node *tree);
137 static enum cYAML_handler_error yaml_entry_token(yaml_token_t *token,
138 struct cYAML_tree_node *tree);
141 static yaml_token_handler dispatch_tbl[] = {
142 [YAML_NO_TOKEN] = yaml_no_token,
143 [YAML_STREAM_START_TOKEN] = yaml_stream_start,
144 [YAML_STREAM_END_TOKEN] = yaml_stream_end,
145 [YAML_VERSION_DIRECTIVE_TOKEN] = yaml_not_supported,
146 [YAML_TAG_DIRECTIVE_TOKEN] = yaml_not_supported,
147 [YAML_DOCUMENT_START_TOKEN] = yaml_document_start,
148 [YAML_DOCUMENT_END_TOKEN] = yaml_document_end,
149 [YAML_BLOCK_SEQUENCE_START_TOKEN] = yaml_blk_seq_start,
150 [YAML_BLOCK_MAPPING_START_TOKEN] = yaml_blk_mapping_start,
151 [YAML_BLOCK_END_TOKEN] = yaml_block_end,
152 [YAML_FLOW_SEQUENCE_START_TOKEN] = yaml_not_supported,
153 [YAML_FLOW_SEQUENCE_END_TOKEN] = yaml_not_supported,
154 [YAML_FLOW_MAPPING_START_TOKEN] = yaml_not_supported,
155 [YAML_FLOW_MAPPING_END_TOKEN] = yaml_not_supported,
156 [YAML_BLOCK_ENTRY_TOKEN] = yaml_entry_token,
157 [YAML_FLOW_ENTRY_TOKEN] = yaml_not_supported,
158 [YAML_KEY_TOKEN] = yaml_key,
159 [YAML_VALUE_TOKEN] = yaml_value,
160 [YAML_ALIAS_TOKEN] = yaml_not_supported,
161 [YAML_ANCHOR_TOKEN] = yaml_not_supported,
162 [YAML_TAG_TOKEN] = yaml_not_supported,
163 [YAML_SCALAR_TOKEN] = yaml_scalar,
167 static const char * const token_type_string[] = {
168 [YAML_NO_TOKEN] = "YAML_NO_TOKEN",
169 [YAML_STREAM_START_TOKEN] = "YAML_STREAM_START_TOKEN",
170 [YAML_STREAM_END_TOKEN] = "YAML_STREAM_END_TOKEN",
171 [YAML_VERSION_DIRECTIVE_TOKEN] = "YAML_VERSION_DIRECTIVE_TOKEN",
172 [YAML_TAG_DIRECTIVE_TOKEN] = "YAML_TAG_DIRECTIVE_TOKEN",
173 [YAML_DOCUMENT_START_TOKEN] = "YAML_DOCUMENT_START_TOKEN",
174 [YAML_DOCUMENT_END_TOKEN] = "YAML_DOCUMENT_END_TOKEN",
175 [YAML_BLOCK_SEQUENCE_START_TOKEN] = "YAML_BLOCK_SEQUENCE_START_TOKEN",
176 [YAML_BLOCK_MAPPING_START_TOKEN] = "YAML_BLOCK_MAPPING_START_TOKEN",
177 [YAML_BLOCK_END_TOKEN] = "YAML_BLOCK_END_TOKEN",
178 [YAML_FLOW_SEQUENCE_START_TOKEN] = "YAML_FLOW_SEQUENCE_START_TOKEN",
179 [YAML_FLOW_SEQUENCE_END_TOKEN] = "YAML_FLOW_SEQUENCE_END_TOKEN",
180 [YAML_FLOW_MAPPING_START_TOKEN] = "YAML_FLOW_MAPPING_START_TOKEN",
181 [YAML_FLOW_MAPPING_END_TOKEN] = "YAML_FLOW_MAPPING_END_TOKEN",
182 [YAML_BLOCK_ENTRY_TOKEN] = "YAML_BLOCK_ENTRY_TOKEN",
183 [YAML_FLOW_ENTRY_TOKEN] = "YAML_FLOW_ENTRY_TOKEN",
184 [YAML_KEY_TOKEN] = "YAML_KEY_TOKEN",
185 [YAML_VALUE_TOKEN] = "YAML_VALUE_TOKEN",
186 [YAML_ALIAS_TOKEN] = "YAML_ALIAS_TOKEN",
187 [YAML_ANCHOR_TOKEN] = "YAML_ANCHOR_TOKEN",
188 [YAML_TAG_TOKEN] = "YAML_TAG_TOKEN",
189 [YAML_SCALAR_TOKEN] = "YAML_SCALAR_TOKEN",
192 static const char * const state_string[] = {
193 [TREE_STATE_COMPLETE] = "COMPLETE",
194 [TREE_STATE_INITED] = "INITED",
195 [TREE_STATE_TREE_STARTED] = "TREE_STARTED",
196 [TREE_STATE_BLK_STARTED] = "BLK_STARTED",
197 [TREE_STATE_KEY] = "KEY",
198 [TREE_STATE_KEY_FILLED] = "KEY_FILLED",
199 [TREE_STATE_VALUE] = "VALUE",
200 [TREE_STATE_SEQ_START] = "SEQ_START",
203 static void cYAML_ll_free(struct list_head *ll)
205 struct cYAML_ll *node, *tmp;
207 list_for_each_entry_safe(node, tmp, ll, list) {
208 free(node->print_info);
213 static int cYAML_ll_push(struct cYAML *obj,
214 const struct cYAML_print_info *print_info,
215 struct list_head *list)
217 struct cYAML_ll *node = calloc(1, sizeof(*node));
221 INIT_LIST_HEAD(&node->list);
224 node->print_info = calloc(1, sizeof(*print_info));
225 if (node->print_info == NULL) {
229 *node->print_info = *print_info;
233 list_add(&node->list, list);
238 static struct cYAML *cYAML_ll_pop(struct list_head *list,
239 struct cYAML_print_info **print_info)
241 struct cYAML_ll *pop;
242 struct cYAML *obj = NULL;
244 if (!list_empty(list)) {
245 pop = list_entry(list->next, struct cYAML_ll, list);
248 if (print_info != NULL)
249 *print_info = pop->print_info;
250 list_del(&pop->list);
252 if (print_info == NULL)
253 free(pop->print_info);
260 static int cYAML_ll_count(struct list_head *ll)
263 struct list_head *node;
265 list_for_each(node, ll)
271 static int cYAML_tree_init(struct cYAML_tree_node *tree)
273 struct cYAML *obj = NULL, *cur = NULL;
278 obj = calloc(1, sizeof(*obj));
283 /* append the node */
285 while (cur->cy_next != NULL)
292 obj->cy_type = CYAML_TYPE_OBJECT;
295 tree->state = TREE_STATE_COMPLETE;
297 /* free it and start anew */
298 if (!list_empty(&tree->ll))
299 cYAML_ll_free(&tree->ll);
304 static struct cYAML *create_child(struct cYAML *parent)
311 obj = calloc(1, sizeof(*obj));
315 /* set the type to OBJECT and let the value change that */
316 obj->cy_type = CYAML_TYPE_OBJECT;
318 parent->cy_child = obj;
323 static struct cYAML *create_sibling(struct cYAML *sibling)
330 obj = calloc(1, sizeof(*obj));
334 /* set the type to OBJECT and let the value change that */
335 obj->cy_type = CYAML_TYPE_OBJECT;
337 sibling->cy_next = obj;
338 obj->cy_prev = sibling;
343 /* Parse the input text to generate a number,
344 * and populate the result into item. */
345 static bool parse_number(struct cYAML *item, const char *input)
347 double n = 0, sign = 1, scale = 0;
348 int subscale = 0, signsubscale = 1;
349 const char *num = input;
359 if (*num >= '1' && *num <= '9') {
361 n = (n * 10.0) + (*num++ - '0');
362 } while (*num >= '0' && *num <= '9');
365 if (*num == '.' && num[1] >= '0' && num[1] <= '9') {
368 n = (n * 10.0) + (*num++ - '0');
370 } while (*num >= '0' && *num <= '9');
373 if (*num == 'e' || *num == 'E') {
377 } else if (*num == '-') {
381 while (*num >= '0' && *num <= '9')
382 subscale = (subscale * 10) + (*num++ - '0');
385 /* check to see if the entire string is consumed. If not then
386 * that means this is a string with a number in it */
387 if (num != (input + strlen(input)))
390 /* number = +/- number.fraction * 10^+/- exponent */
391 n = sign * n * pow(10.0, (scale + subscale * signsubscale));
393 item->cy_valuedouble = n;
394 item->cy_valueint = (int)n;
395 item->cy_type = CYAML_TYPE_NUMBER;
400 static int assign_type_value(struct cYAML *obj, const char *value)
405 if (strcmp(value, "null") == 0)
406 obj->cy_type = CYAML_TYPE_NULL;
407 else if (strcmp(value, "false") == 0) {
408 obj->cy_type = CYAML_TYPE_FALSE;
409 obj->cy_valueint = 0;
410 } else if (strcmp(value, "true") == 0) {
411 obj->cy_type = CYAML_TYPE_TRUE;
412 obj->cy_valueint = 1;
413 } else if (*value == '-' || (*value >= '0' && *value <= '9')) {
414 if (parse_number(obj, value) == 0) {
415 obj->cy_valuestring = strdup(value);
416 obj->cy_type = CYAML_TYPE_STRING;
419 obj->cy_valuestring = strdup(value);
420 obj->cy_type = CYAML_TYPE_STRING;
428 * Builds the YAML tree rpresentation as the tokens are passed in
430 * if token == STREAM_START && tree_state != COMPLETE
431 * something wrong. fail.
432 * else tree_state = INITIED
433 * if token == DOCUMENT_START && tree_state != COMPLETE || INITED
434 * something wrong, fail.
435 * else tree_state = TREE_STARTED
436 * if token == DOCUMENT_END
437 * tree_state = INITED if no STREAM START, else tree_state = COMPLETE
438 * erase everything on ll
439 * if token == STREAM_END && tree_state != INITED
440 * something wrong fail.
441 * else tree_state = COMPLETED
442 * if token == YAML_KEY_TOKEN && state != TREE_STARTED
443 * something wrong, fail.
444 * if token == YAML_SCALAR_TOKEN && state != KEY || VALUE
446 * else if tree_state == KEY
447 * create a new sibling under the current head of the ll (if ll is
448 * empty insert the new node there and it becomes the root.)
449 * add the scalar value in the "string"
450 * tree_state = KEY_FILLED
451 * else if tree_state == VALUE
452 * try and figure out whether this is a double, int or string and store
454 * state = TREE_STARTED
455 * else if token == YAML_BLOCK_MAPPING_START_TOKEN && tree_state != VALUE
457 * else push the current node on the ll && state = TREE_STARTED
458 * if token == YAML_BLOCK_END_TOKEN && state != TREE_STARTED
460 * else pop the current token off the ll and make it the cur
461 * if token == YAML_VALUE_TOKEN && state != KEY_FILLED
467 static enum cYAML_handler_error yaml_no_token(yaml_token_t *token,
468 struct cYAML_tree_node *tree)
470 return CYAML_ERROR_NONE;
473 static enum cYAML_handler_error yaml_stream_start(yaml_token_t *token,
474 struct cYAML_tree_node *tree)
476 enum cYAML_handler_error rc;
478 /* with each new stream initialize a new tree */
479 rc = cYAML_tree_init(tree);
481 if (rc != CYAML_ERROR_NONE)
484 tree->state = TREE_STATE_INITED;
486 return CYAML_ERROR_NONE;
489 static enum cYAML_handler_error yaml_stream_end(yaml_token_t *token,
490 struct cYAML_tree_node *tree)
492 if (tree->state != TREE_STATE_TREE_STARTED &&
493 tree->state != TREE_STATE_COMPLETE &&
494 tree->state != TREE_STATE_INITED)
495 return CYAML_ERROR_UNEXPECTED_STATE;
497 tree->state = TREE_STATE_INITED;
499 return CYAML_ERROR_NONE;
502 static enum cYAML_handler_error
503 yaml_document_start(yaml_token_t *token, struct cYAML_tree_node *tree)
505 if (tree->state != TREE_STATE_INITED)
506 return CYAML_ERROR_UNEXPECTED_STATE;
508 /* go to started state since we're expecting more tokens to come */
509 tree->state = TREE_STATE_TREE_STARTED;
511 return CYAML_ERROR_NONE;
514 static enum cYAML_handler_error yaml_document_end(yaml_token_t *token,
515 struct cYAML_tree_node *tree)
517 if (tree->state != TREE_STATE_COMPLETE)
518 return CYAML_ERROR_UNEXPECTED_STATE;
520 tree->state = TREE_STATE_TREE_STARTED;
522 return CYAML_ERROR_NONE;
525 static enum cYAML_handler_error yaml_key(yaml_token_t *token,
526 struct cYAML_tree_node *tree)
528 if (tree->state != TREE_STATE_BLK_STARTED &&
529 tree->state != TREE_STATE_VALUE)
530 return CYAML_ERROR_UNEXPECTED_STATE;
532 if (tree->from_blk_map_start == 0 ||
533 tree->state == TREE_STATE_VALUE)
534 tree->cur = create_sibling(tree->cur);
536 tree->from_blk_map_start = 0;
538 tree->state = TREE_STATE_KEY;
540 return CYAML_ERROR_NONE;
543 static enum cYAML_handler_error yaml_scalar(yaml_token_t *token,
544 struct cYAML_tree_node *tree)
546 if (tree->state == TREE_STATE_KEY) {
547 /* assign the scalar value to the key that was created */
548 tree->cur->cy_string =
549 strdup((const char *)token->data.scalar.value);
551 tree->state = TREE_STATE_KEY_FILLED;
552 } else if (tree->state == TREE_STATE_VALUE ||
553 tree->state == TREE_STATE_SEQ_START) {
554 if (assign_type_value(tree->cur,
555 (char *)token->data.scalar.value))
556 /* failed to assign a value */
557 return CYAML_ERROR_BAD_VALUE;
558 tree->state = TREE_STATE_BLK_STARTED;
560 return CYAML_ERROR_UNEXPECTED_STATE;
563 return CYAML_ERROR_NONE;
566 static enum cYAML_handler_error yaml_value(yaml_token_t *token,
567 struct cYAML_tree_node *tree)
569 if (tree->state != TREE_STATE_KEY_FILLED)
570 return CYAML_ERROR_UNEXPECTED_STATE;
572 tree->state = TREE_STATE_VALUE;
574 return CYAML_ERROR_NONE;
577 static enum cYAML_handler_error yaml_blk_seq_start(yaml_token_t *token,
578 struct cYAML_tree_node *tree)
580 if (tree->state != TREE_STATE_VALUE)
581 return CYAML_ERROR_UNEXPECTED_STATE;
583 /* Since a sequenc start event determines that this is the start
584 * of an array, then that means the current node we're at is an
585 * array and we need to flag it as such */
586 tree->cur->cy_type = CYAML_TYPE_ARRAY;
587 tree->state = TREE_STATE_SEQ_START;
589 return CYAML_ERROR_NONE;
592 static enum cYAML_handler_error yaml_entry_token(yaml_token_t *token,
593 struct cYAML_tree_node *tree)
597 if (tree->state != TREE_STATE_SEQ_START &&
598 tree->state != TREE_STATE_BLK_STARTED &&
599 tree->state != TREE_STATE_VALUE)
600 return CYAML_ERROR_UNEXPECTED_STATE;
602 if (tree->state == TREE_STATE_SEQ_START) {
603 obj = create_child(tree->cur);
605 if (cYAML_ll_push(tree->cur, NULL, &tree->ll))
606 return CYAML_ERROR_OUT_OF_MEM;
610 tree->cur = create_sibling(tree->cur);
611 tree->state = TREE_STATE_SEQ_START;
614 return CYAML_ERROR_NONE;
617 static enum cYAML_handler_error
618 yaml_blk_mapping_start(yaml_token_t *token,
619 struct cYAML_tree_node *tree)
623 if (tree->state != TREE_STATE_VALUE &&
624 tree->state != TREE_STATE_INITED &&
625 tree->state != TREE_STATE_SEQ_START &&
626 tree->state != TREE_STATE_TREE_STARTED)
627 return CYAML_ERROR_UNEXPECTED_STATE;
629 /* block_mapping_start means we're entering another block
630 * indentation, so we need to go one level deeper
631 * create a child of cur */
632 obj = create_child(tree->cur);
634 /* push cur on the stack */
635 if (cYAML_ll_push(tree->cur, NULL, &tree->ll))
636 return CYAML_ERROR_OUT_OF_MEM;
638 /* adding the new child to cur */
641 tree->state = TREE_STATE_BLK_STARTED;
643 tree->from_blk_map_start = 1;
645 return CYAML_ERROR_NONE;
648 static enum cYAML_handler_error yaml_block_end(yaml_token_t *token,
649 struct cYAML_tree_node *tree)
651 if (tree->state != TREE_STATE_BLK_STARTED &&
652 tree->state != TREE_STATE_VALUE)
653 return CYAML_ERROR_UNEXPECTED_STATE;
655 tree->cur = cYAML_ll_pop(&tree->ll, NULL);
657 /* if you have popped all the way to the top level, then move to
658 * the complete state. */
659 if (cYAML_ll_count(&tree->ll) == 0)
660 tree->state = TREE_STATE_COMPLETE;
661 else if (tree->state == TREE_STATE_VALUE)
662 tree->state = TREE_STATE_BLK_STARTED;
664 return CYAML_ERROR_NONE;
667 static enum cYAML_handler_error yaml_not_supported(yaml_token_t *token,
668 struct cYAML_tree_node *tree)
670 return CYAML_ERROR_NOT_SUPPORTED;
673 static bool clean_usr_data(struct cYAML *node, void *usr_data, void **out)
675 cYAML_user_data_free_cb free_cb = usr_data;
677 if (free_cb && node && node->cy_user_data) {
678 free_cb(node->cy_user_data);
679 node->cy_user_data = NULL;
685 static bool free_node(struct cYAML *node, void *user_data, void **out)
690 if (node->cy_type == CYAML_TYPE_STRING)
691 free(node->cy_valuestring);
693 free(node->cy_string);
699 static bool find_obj_iter(struct cYAML *node, void *usr_data, void **out)
701 char *name = usr_data;
703 if (node != NULL && node->cy_string != NULL &&
704 strcmp(node->cy_string, name) == 0) {
712 struct cYAML *cYAML_get_object_item(struct cYAML *parent, const char *name)
714 struct cYAML *node = parent, *found = NULL;
719 if (node->cy_string) {
720 if (strcmp(node->cy_string, name) == 0)
725 found = cYAML_get_object_item(node->cy_child, name);
727 if (!found && node->cy_next)
728 found = cYAML_get_object_item(node->cy_next, name);
733 struct cYAML *cYAML_get_next_seq_item(struct cYAML *seq, struct cYAML **itm)
735 if (*itm != NULL && (*itm)->cy_next != NULL) {
736 *itm = (*itm)->cy_next;
740 if (*itm == NULL && seq->cy_type == CYAML_TYPE_ARRAY) {
741 *itm = seq->cy_child;
748 bool cYAML_is_sequence(struct cYAML *node)
750 return (node != NULL ? node->cy_type == CYAML_TYPE_ARRAY : 0);
753 void cYAML_tree_recursive_walk(struct cYAML *node, cYAML_walk_cb cb,
762 if (!cb(node, usr_data, out))
767 cYAML_tree_recursive_walk(node->cy_child, cb,
768 cb_first, usr_data, out);
771 cYAML_tree_recursive_walk(node->cy_next, cb,
772 cb_first, usr_data, out);
775 if (!cb(node, usr_data, out))
780 struct cYAML *cYAML_find_object(struct cYAML *root, const char *name)
782 struct cYAML *found = NULL;
784 cYAML_tree_recursive_walk(root, find_obj_iter, true,
785 (void *)name, (void **)&found);
790 void cYAML_clean_usr_data(struct cYAML *node, cYAML_user_data_free_cb free_cb)
792 cYAML_tree_recursive_walk(node, clean_usr_data, false, free_cb, NULL);
795 void cYAML_free_tree(struct cYAML *node)
797 cYAML_tree_recursive_walk(node, free_node, false, NULL, NULL);
800 static char *ensure(char *in, int len)
806 return (char*)calloc(len, 1);
808 curlen = strlen(in) + 1;
810 if (curlen <= curlen + len) {
811 new = calloc(curlen + len, 1);
823 static inline void print_simple(char **out, struct cYAML *node,
824 struct cYAML_print_info *cpi)
826 int level = cpi->level;
827 int ind = cpi->extra_ind;
829 int len = (INDENT * level + ind) * 2 +
830 ((node->cy_string) ? strlen(node->cy_string) : 0) + LEAD_ROOM;
832 *out = ensure(*out, len);
836 tmp = ensure(tmp, len);
840 if (cpi->array_first_elem) {
841 sprintf(tmp, "%*s- ", INDENT * level, "");
845 sprintf(tmp, "%*s""%s: %" PRId64 "\n", (cpi->array_first_elem) ? 0 :
846 INDENT * level + ind, "", node->cy_string,
852 static void print_string(char **out, struct cYAML *node,
853 struct cYAML_print_info *cpi)
856 int level = cpi->level;
857 int ind = cpi->extra_ind;
859 int len = INDENT * level + ind +
860 ((node->cy_valuestring) ? strlen(node->cy_valuestring) : 0) +
861 ((node->cy_string) ? strlen(node->cy_string) : 0) + LEAD_ROOM;
863 *out = ensure(*out, len);
867 tmp = ensure(tmp, len);
871 if (cpi->array_first_elem) {
872 sprintf(tmp, "%*s- ", INDENT * level, "");
876 new_line = strchr(node->cy_valuestring, '\n');
877 if (new_line == NULL) {
878 sprintf(tmp, "%*s""%s: %s\n", (cpi->array_first_elem) ?
879 0 : INDENT * level + ind, "",
880 node->cy_string, node->cy_valuestring);
884 sprintf(tmp, "%*s""%s: ", (cpi->array_first_elem) ?
885 0 : INDENT * level + ind, "",
888 char *l = node->cy_valuestring;
891 sprintf(tmp, "%*s""%s\n", indent, "", l);
893 indent = INDENT * level + ind +
894 strlen(node->cy_string) + 2;
897 new_line = strchr(l, '\n');
899 sprintf(tmp, "%*s""%s\n", indent, "", l);
906 static void print_number(char **out, struct cYAML *node,
907 struct cYAML_print_info *cpi)
909 double d = node->cy_valuedouble;
910 int level = cpi->level;
911 int ind = cpi->extra_ind;
913 int len = INDENT * level + ind + LEAD_ROOM;
915 *out = ensure(*out, len);
919 tmp = ensure(tmp, len);
923 if (cpi->array_first_elem) {
924 sprintf(tmp, "%*s- ", INDENT * level, "");
928 if ((fabs(((double)node->cy_valueint) - d) <= DBL_EPSILON) &&
929 (d <= INT_MAX) && (d >= INT_MIN)) {
930 sprintf(tmp, "%*s""%s: %" PRId64 "\n", (cpi->array_first_elem) ? 0 :
931 INDENT * level + ind, "",
932 node->cy_string, node->cy_valueint);
935 if ((fabs(floor(d) - d) <= DBL_EPSILON) &&
936 (fabs(d) < 1.0e60)) {
937 sprintf(tmp, "%*s""%s: %.0f\n",
938 (cpi->array_first_elem) ? 0 :
939 INDENT * level + ind, "",
942 } else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) {
943 sprintf(tmp, "%*s""%s: %e\n",
944 (cpi->array_first_elem) ? 0 :
945 INDENT * level + ind, "",
949 sprintf(tmp, "%*s""%s: %f\n",
950 (cpi->array_first_elem) ? 0 :
951 INDENT * level + ind, "",
960 static void print_object(char **out, struct cYAML *node,
961 struct list_head *stack,
962 struct cYAML_print_info *cpi)
964 struct cYAML_print_info print_info;
965 struct cYAML *child = node->cy_child;
967 int len = ((cpi->array_first_elem) ? INDENT * cpi->level :
968 INDENT * cpi->level + cpi->extra_ind) +
969 ((node->cy_string) ? strlen(node->cy_string) : 0) +
972 *out = ensure(*out, len);
976 tmp = ensure(tmp, len);
980 if (node->cy_string != NULL) {
981 sprintf(tmp, "%*s""%s%s:\n", (cpi->array_first_elem) ?
982 INDENT * cpi->level :
983 INDENT * cpi->level + cpi->extra_ind,
984 "", (cpi->array_first_elem) ? "- " : "",
989 print_info.level = (node->cy_string != NULL) ? cpi->level + 1 :
991 print_info.array_first_elem = (node->cy_string == NULL) ?
992 cpi->array_first_elem : 0;
993 print_info.extra_ind = (cpi->array_first_elem) ? EXTRA_IND :
997 if (cYAML_ll_push(child, &print_info, stack) != 0) {
1001 print_value(out, stack);
1002 print_info.array_first_elem = 0;
1003 child = child->cy_next;
1009 static void print_array(char **out, struct cYAML *node,
1010 struct list_head *stack,
1011 struct cYAML_print_info *cpi)
1013 struct cYAML_print_info print_info;
1014 struct cYAML *child = node->cy_child;
1016 int len = ((node->cy_string) ? strlen(node->cy_string) : 0) +
1017 INDENT * cpi->level + cpi->extra_ind + LEAD_ROOM;
1019 *out = ensure(*out, len);
1023 tmp = ensure(tmp, len);
1027 if (node->cy_string != NULL) {
1028 sprintf(tmp, "%*s""%s:\n", INDENT * cpi->level + cpi->extra_ind,
1029 "", node->cy_string);
1033 print_info.level = (node->cy_string != NULL) ? cpi->level + 1 :
1035 print_info.array_first_elem = 1;
1036 print_info.extra_ind = EXTRA_IND;
1039 if (cYAML_ll_push(child, &print_info, stack) != 0) {
1043 print_value(out, stack);
1044 child = child->cy_next;
1050 static void print_value(char **out, struct list_head *stack)
1052 struct cYAML_print_info *cpi = NULL;
1053 struct cYAML *node = cYAML_ll_pop(stack, &cpi);
1058 switch (node->cy_type) {
1059 case CYAML_TYPE_FALSE:
1060 case CYAML_TYPE_TRUE:
1061 case CYAML_TYPE_NULL:
1062 print_simple(out, node, cpi);
1064 case CYAML_TYPE_STRING:
1065 print_string(out, node, cpi);
1067 case CYAML_TYPE_NUMBER:
1068 print_number(out, node, cpi);
1070 case CYAML_TYPE_ARRAY:
1071 print_array(out, node, stack, cpi);
1073 case CYAML_TYPE_OBJECT:
1074 print_object(out, node, stack, cpi);
1084 void cYAML_dump(struct cYAML *node, char **buf)
1086 struct cYAML_print_info print_info;
1087 struct list_head list;
1089 *buf = ensure(NULL, PRINT_BUF_LEN);
1094 INIT_LIST_HEAD(&list);
1101 memset(&print_info, 0, sizeof(struct cYAML_print_info));
1103 if (cYAML_ll_push(node, &print_info, &list) == 0)
1104 print_value(buf, &list);
1107 void cYAML_print_tree(struct cYAML *node)
1109 struct cYAML_print_info print_info;
1110 struct list_head list;
1111 char *buf = ensure(NULL, PRINT_BUF_LEN);
1116 INIT_LIST_HEAD(&list);
1121 memset(&print_info, 0, sizeof(struct cYAML_print_info));
1123 if (cYAML_ll_push(node, &print_info, &list) == 0)
1124 print_value(&buf, &list);
1126 /* buf could've been freed if we ran out of memory */
1133 void cYAML_print_tree2file(FILE *f, struct cYAML *node)
1135 struct cYAML_print_info print_info;
1136 struct list_head list;
1137 char *buf = ensure(NULL, PRINT_BUF_LEN);
1142 INIT_LIST_HEAD(&list);
1147 memset(&print_info, 0, sizeof(struct cYAML_print_info));
1149 if (cYAML_ll_push(node, &print_info, &list) == 0)
1150 print_value(&buf, &list);
1152 /* buf could've been freed if we ran out of memory */
1154 fprintf(f, "%s", buf);
1159 static struct cYAML *insert_item(struct cYAML *parent, char *key,
1160 enum cYAML_object_type type)
1162 struct cYAML *node = calloc(1, sizeof(*node));
1168 node->cy_string = strdup(key);
1170 node->cy_type = type;
1172 cYAML_insert_child(parent, node);
1177 struct cYAML *cYAML_create_seq(struct cYAML *parent, char *key)
1179 return insert_item(parent, key, CYAML_TYPE_ARRAY);
1182 struct cYAML *cYAML_create_seq_item(struct cYAML *seq)
1184 return insert_item(seq, NULL, CYAML_TYPE_OBJECT);
1187 struct cYAML *cYAML_create_object(struct cYAML *parent, char *key)
1189 return insert_item(parent, key, CYAML_TYPE_OBJECT);
1192 struct cYAML *cYAML_create_string(struct cYAML *parent, char *key, char *value)
1194 struct cYAML *node = calloc(1, sizeof(*node));
1198 node->cy_string = strdup(key);
1199 node->cy_valuestring = strdup(value);
1200 node->cy_type = CYAML_TYPE_STRING;
1202 cYAML_insert_child(parent, node);
1207 struct cYAML *cYAML_create_number(struct cYAML *parent, char *key, double value)
1209 struct cYAML *node = calloc(1, sizeof(*node));
1213 node->cy_string = strdup(key);
1214 node->cy_valuedouble = value;
1215 node->cy_valueint = (int)value;
1216 node->cy_type = CYAML_TYPE_NUMBER;
1218 cYAML_insert_child(parent, node);
1223 void cYAML_insert_child(struct cYAML *parent, struct cYAML *node)
1227 if (parent && node) {
1228 if (parent->cy_child == NULL) {
1229 parent->cy_child = node;
1233 cur = parent->cy_child;
1235 while (cur->cy_next)
1238 cur->cy_next = node;
1239 node->cy_prev = cur;
1243 void cYAML_insert_sibling(struct cYAML *root, struct cYAML *sibling)
1245 struct cYAML *last = NULL;
1246 if (root == NULL || sibling == NULL)
1250 while (last->cy_next != NULL)
1251 last = last->cy_next;
1253 last->cy_next = sibling;
1256 void cYAML_build_error(int rc, int seq_no, char *cmd,
1257 char *entity, char *err_str,
1258 struct cYAML **root)
1260 struct cYAML *r = NULL, *err, *s, *itm, *cmd_obj;
1264 /* add to the tail of the root that's passed in */
1265 if ((*root) == NULL) {
1266 *root = cYAML_create_object(NULL, NULL);
1267 if ((*root) == NULL)
1273 /* look for the command */
1274 cmd_obj = cYAML_get_object_item(r, (const char *)cmd);
1275 if (cmd_obj != NULL && cmd_obj->cy_type == CYAML_TYPE_ARRAY)
1276 itm = cYAML_create_seq_item(cmd_obj);
1277 else if (cmd_obj == NULL) {
1278 s = cYAML_create_seq(r, cmd);
1279 itm = cYAML_create_seq_item(s);
1280 } else if (cmd_obj != NULL && cmd_obj->cy_type != CYAML_TYPE_ARRAY)
1283 err = cYAML_create_object(itm, entity);
1288 cYAML_create_number(err, "seq_no", seq_no) == NULL)
1291 if (cYAML_create_number(err, "errno", rc) == NULL)
1294 if (cYAML_create_string(err, "descr", err_str) == NULL)
1300 /* Only reason we get here is if we run out of memory */
1303 fprintf(stderr, "error:\n\tfatal: out of memory\n");
1306 static struct cYAML *
1307 cYAML_parser_to_tree(yaml_parser_t *parser, struct cYAML **err_rc, bool debug)
1310 struct cYAML_tree_node tree;
1311 enum cYAML_handler_error rc;
1312 yaml_token_type_t token_type;
1316 memset(&tree, 0, sizeof(struct cYAML_tree_node));
1318 INIT_LIST_HEAD(&tree.ll);
1320 /* Read the event sequence. */
1323 * Go through the parser and build a cYAML representation
1324 * of the passed in YAML text
1326 yaml_parser_scan(parser, &token);
1329 fprintf(stderr, "tree.state(%p:%d) = %s, token.type ="
1331 &tree, tree.state, state_string[tree.state],
1332 token_type_string[token.type],
1333 (token.type == YAML_SCALAR_TOKEN) ?
1334 (char*)token.data.scalar.value : "");
1335 rc = dispatch_tbl[token.type](&token, &tree);
1336 if (rc != CYAML_ERROR_NONE) {
1337 snprintf(err_str, sizeof(err_str),
1338 "Failed to handle token:%d %s [state=%d, rc=%d]",
1339 token.type, token_type_string[token.type],
1341 cYAML_build_error(-1, -1, "yaml", "builder",
1345 /* Are we finished? */
1346 done = (rc != CYAML_ERROR_NONE ||
1347 token.type == YAML_STREAM_END_TOKEN);
1349 token_type = token.type;
1351 yaml_token_delete(&token);
1354 if (token_type == YAML_STREAM_END_TOKEN &&
1355 rc == CYAML_ERROR_NONE)
1358 cYAML_free_tree(tree.root);
1363 struct cYAML *cYAML_load(FILE *file, struct cYAML **err_rc, bool debug)
1365 yaml_parser_t parser;
1368 yaml_parser_initialize(&parser);
1369 yaml_parser_set_input_file(&parser, file);
1371 yaml = cYAML_parser_to_tree(&parser, err_rc, debug);
1373 yaml_parser_delete(&parser);
1378 struct cYAML *cYAML_build_tree(char *path,
1379 const char *yaml_blk,
1380 size_t yaml_blk_size,
1381 struct cYAML **err_rc,
1384 yaml_parser_t parser;
1389 /* Create the Parser object. */
1390 yaml_parser_initialize(&parser);
1392 /* file always takes precedence */
1394 /* Set a file input. */
1395 input = fopen(path, "rb");
1396 if (input == NULL) {
1397 snprintf(err_str, sizeof(err_str),
1398 "cannot open '%s': %s", path, strerror(errno));
1399 cYAML_build_error(-1, -1, "yaml", "builder",
1405 yaml_parser_set_input_file(&parser, input);
1406 } else if (yaml_blk != NULL) {
1407 yaml_parser_set_input_string(&parser,
1408 (const unsigned char *) yaml_blk,
1411 /* assume that we're getting our input froms stdin */
1412 yaml_parser_set_input_file(&parser, stdin);
1415 yaml = cYAML_parser_to_tree(&parser, err_rc, debug);
1417 /* Destroy the Parser object. */
1418 yaml_parser_delete(&parser);