Whamcloud - gitweb
LU-7243 misc: update Intel copyright messages 2015
[fs/lustre-release.git] / lnet / utils / cyaml / cyaml.c
1 /*
2  * LGPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
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.
10  *
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.
15  *
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/>.
18  *
19  * LGPL HEADER END
20  *
21  * Copyright (c) 2014, 2015, Intel Corporation.
22  *
23  * Author:
24  *   Amir Shehata <amir.shehata@intel.com>
25  */
26
27 /*
28  *  The cYAML tree is constructed as an n-tree.
29  *  root -> cmd 1
30  *          ||
31  *          \/
32  *          cmd 2 -> attr1 -> attr2
33  *                              ||
34  *                              \/
35  *                            attr2.1 -> attr2.1.1 -> attr2.1.2
36  */
37
38 #include <yaml.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdio.h>
42 #include <math.h>
43 #include <stdlib.h>
44 #include <float.h>
45 #include <limits.h>
46 #include <ctype.h>
47 #include "libcfs/list.h"
48 #include "cyaml.h"
49
50 #define INDENT          4
51 #define EXTRA_IND       2
52
53 /*
54  * cYAML_print_info
55  *   This structure contains print information
56  *   required when printing the node
57  */
58 struct cYAML_print_info {
59         int level;
60         int array_first_elem;
61         int extra_ind;
62 };
63
64 /*
65  *  cYAML_ll
66  *  Linked list of different trees representing YAML
67  *  documents.
68  */
69 struct cYAML_ll {
70         struct list_head list;
71         struct cYAML *obj;
72         struct cYAML_print_info *print_info;
73 };
74
75 static void print_value(FILE *f, struct list_head *stack);
76
77 enum cYAML_handler_error {
78         CYAML_ERROR_NONE = 0,
79         CYAML_ERROR_UNEXPECTED_STATE = -1,
80         CYAML_ERROR_NOT_SUPPORTED = -2,
81         CYAML_ERROR_OUT_OF_MEM = -3,
82         CYAML_ERROR_BAD_VALUE = -4,
83         CYAML_ERROR_PARSE = -5,
84 };
85
86 enum cYAML_tree_state {
87         TREE_STATE_COMPLETE = 0,
88         TREE_STATE_INITED,
89         TREE_STATE_TREE_STARTED,
90         TREE_STATE_BLK_STARTED,
91         TREE_STATE_KEY,
92         TREE_STATE_KEY_FILLED,
93         TREE_STATE_VALUE,
94         TREE_STATE_SEQ_START,
95 };
96
97 struct cYAML_tree_node {
98         struct cYAML *root;
99         /* cur is the current node we're operating on */
100         struct cYAML *cur;
101         enum cYAML_tree_state state;
102         int from_blk_map_start;
103         /* represents the tree depth */
104         struct list_head ll;
105 };
106
107 typedef enum cYAML_handler_error (*yaml_token_handler)(yaml_token_t *token,
108                                                 struct cYAML_tree_node *);
109
110 static enum cYAML_handler_error yaml_parse_error(yaml_token_t *token,
111                                         struct cYAML_tree_node *tree);
112 static enum cYAML_handler_error yaml_stream_start(yaml_token_t *token,
113                                         struct cYAML_tree_node *tree);
114 static enum cYAML_handler_error yaml_stream_end(yaml_token_t *token,
115                                         struct cYAML_tree_node *tree);
116 static enum cYAML_handler_error yaml_not_supported(yaml_token_t *token,
117                                                 struct cYAML_tree_node *tree);
118 static enum cYAML_handler_error yaml_document_start(yaml_token_t *token,
119                                                 struct cYAML_tree_node *tree);
120 static enum cYAML_handler_error yaml_document_end(yaml_token_t *token,
121                                                struct cYAML_tree_node *tree);
122 static enum cYAML_handler_error yaml_blk_seq_start(yaml_token_t *token,
123                                                 struct cYAML_tree_node *tree);
124 static enum cYAML_handler_error yaml_blk_mapping_start(yaml_token_t *token,
125                                                 struct cYAML_tree_node *tree);
126 static enum cYAML_handler_error yaml_block_end(yaml_token_t *token,
127                                         struct cYAML_tree_node *tree);
128 static enum cYAML_handler_error yaml_key(yaml_token_t *token,
129                                 struct cYAML_tree_node *tree);
130 static enum cYAML_handler_error yaml_value(yaml_token_t *token,
131                                         struct cYAML_tree_node *tree);
132 static enum cYAML_handler_error yaml_scalar(yaml_token_t *token,
133                                         struct cYAML_tree_node *tree);
134 static enum cYAML_handler_error yaml_entry_token(yaml_token_t *token,
135                                         struct cYAML_tree_node *tree);
136
137 /* dispatch table */
138 static yaml_token_handler dispatch_tbl[] = {
139         [YAML_NO_TOKEN] = yaml_parse_error,
140         [YAML_STREAM_START_TOKEN] = yaml_stream_start,
141         [YAML_STREAM_END_TOKEN] = yaml_stream_end,
142         [YAML_VERSION_DIRECTIVE_TOKEN] = yaml_not_supported,
143         [YAML_TAG_DIRECTIVE_TOKEN] = yaml_not_supported,
144         [YAML_DOCUMENT_START_TOKEN] = yaml_document_start,
145         [YAML_DOCUMENT_END_TOKEN] = yaml_document_end,
146         [YAML_BLOCK_SEQUENCE_START_TOKEN] = yaml_blk_seq_start,
147         [YAML_BLOCK_MAPPING_START_TOKEN] = yaml_blk_mapping_start,
148         [YAML_BLOCK_END_TOKEN] = yaml_block_end,
149         [YAML_FLOW_SEQUENCE_START_TOKEN] = yaml_not_supported,
150         [YAML_FLOW_SEQUENCE_END_TOKEN] = yaml_not_supported,
151         [YAML_FLOW_MAPPING_START_TOKEN] = yaml_not_supported,
152         [YAML_FLOW_MAPPING_END_TOKEN] = yaml_not_supported,
153         [YAML_BLOCK_ENTRY_TOKEN] = yaml_entry_token,
154         [YAML_FLOW_ENTRY_TOKEN] = yaml_not_supported,
155         [YAML_KEY_TOKEN] = yaml_key,
156         [YAML_VALUE_TOKEN] = yaml_value,
157         [YAML_ALIAS_TOKEN] = yaml_not_supported,
158         [YAML_ANCHOR_TOKEN] = yaml_not_supported,
159         [YAML_TAG_TOKEN] = yaml_not_supported,
160         [YAML_SCALAR_TOKEN] = yaml_scalar,
161 };
162
163 static void cYAML_ll_free(struct list_head *ll)
164 {
165         struct cYAML_ll *node, *tmp;
166
167         list_for_each_entry_safe(node, tmp, ll, list) {
168                 free(node->print_info);
169                 free(node);
170         }
171 }
172
173 static int cYAML_ll_push(struct cYAML *obj,
174                          const struct cYAML_print_info *print_info,
175                          struct list_head *list)
176 {
177         struct cYAML_ll *node = calloc(1, sizeof(*node));
178         if (node == NULL)
179                 return -1;
180
181         INIT_LIST_HEAD(&node->list);
182
183         if (print_info) {
184                 node->print_info = calloc(1, sizeof(*print_info));
185                 if (node->print_info == NULL) {
186                         free(node);
187                         return -1;
188                 }
189                 *node->print_info = *print_info;
190         }
191         node->obj = obj;
192
193         list_add(&node->list, list);
194
195         return 0;
196 }
197
198 static struct cYAML *cYAML_ll_pop(struct list_head *list,
199                                   struct cYAML_print_info **print_info)
200 {
201         struct cYAML_ll *pop;
202         struct cYAML *obj = NULL;
203
204         if (!list_empty(list)) {
205                 pop = list_entry(list->next, struct cYAML_ll, list);
206
207                 obj = pop->obj;
208                 if (print_info != NULL)
209                         *print_info = pop->print_info;
210                 list_del(&pop->list);
211
212                 if (print_info == NULL)
213                         free(pop->print_info);
214
215                 free(pop);
216         }
217         return obj;
218 }
219
220 static int cYAML_ll_count(struct list_head *ll)
221 {
222         int i = 0;
223         struct list_head *node;
224
225         list_for_each(node, ll)
226                 i++;
227
228         return i;
229 }
230
231 static int cYAML_tree_init(struct cYAML_tree_node *tree)
232 {
233         struct cYAML *obj = NULL, *cur = NULL;
234
235         if (tree == NULL)
236                 return -1;
237
238         obj = calloc(1, sizeof(*obj));
239         if (obj == NULL)
240                 return -1;
241
242         if (tree->root) {
243                 /* append the node */
244                 cur = tree->root;
245                 while (cur->cy_next != NULL)
246                         cur = cur->cy_next;
247                 cur->cy_next = obj;
248         } else {
249                 tree->root = obj;
250         }
251
252         obj->cy_type = CYAML_TYPE_OBJECT;
253
254         tree->cur = obj;
255         tree->state = TREE_STATE_COMPLETE;
256
257         /* free it and start anew */
258         if (!list_empty(&tree->ll))
259                 cYAML_ll_free(&tree->ll);
260
261         return 0;
262 }
263
264 static struct cYAML *create_child(struct cYAML *parent)
265 {
266         struct cYAML *obj;
267
268         if (parent == NULL)
269                 return NULL;
270
271         obj = calloc(1, sizeof(*obj));
272         if (obj == NULL)
273                 return NULL;
274
275         /* set the type to OBJECT and let the value change that */
276         obj->cy_type = CYAML_TYPE_OBJECT;
277
278         parent->cy_child = obj;
279
280         return obj;
281 }
282
283 static struct cYAML *create_sibling(struct cYAML *sibling)
284 {
285         struct cYAML *obj;
286
287         if (sibling == NULL)
288                 return NULL;
289
290         obj = calloc(1, sizeof(*obj));
291         if (obj == NULL)
292                 return NULL;
293
294         /* set the type to OBJECT and let the value change that */
295         obj->cy_type = CYAML_TYPE_OBJECT;
296
297         sibling->cy_next = obj;
298         obj->cy_prev = sibling;
299
300         return obj;
301 }
302
303 /* Parse the input text to generate a number,
304  * and populate the result into item. */
305 static bool parse_number(struct cYAML *item, const char *input)
306 {
307         double n = 0, sign = 1, scale = 0;
308         int subscale = 0, signsubscale = 1;
309         const char *num = input;
310
311         if (*num == '-') {
312                 sign = -1;
313                 num++;
314         }
315
316         if (*num == '0')
317                 num++;
318
319         if (*num >= '1' && *num <= '9') {
320                 do {
321                         n = (n * 10.0) + (*num++ - '0');
322                 } while (*num >= '0' && *num <= '9');
323         }
324
325         if (*num == '.' && num[1] >= '0' && num[1] <= '9') {
326                 num++;
327                 do {
328                         n = (n * 10.0) + (*num++ - '0');
329                         scale--;
330                 } while (*num >= '0' && *num <= '9');
331         }
332
333         if (*num == 'e' || *num == 'E') {
334                 num++;
335                 if (*num == '+') {
336                         num++;
337                 } else if (*num == '-') {
338                         signsubscale = -1;
339                         num++;
340                 }
341                 while (*num >= '0' && *num <= '9')
342                         subscale = (subscale * 10) + (*num++ - '0');
343         }
344
345         /* check to see if the entire string is consumed.  If not then
346          * that means this is a string with a number in it */
347         if (num != (input + strlen(input)))
348                 return false;
349
350         /* number = +/- number.fraction * 10^+/- exponent */
351         n = sign * n * pow(10.0, (scale + subscale * signsubscale));
352
353         item->cy_valuedouble = n;
354         item->cy_valueint = (int)n;
355         item->cy_type = CYAML_TYPE_NUMBER;
356
357         return true;
358 }
359
360 static int assign_type_value(struct cYAML *obj, const char *value)
361 {
362         if (value == NULL)
363                 return -1;
364
365         if (strcmp(value, "null") == 0)
366                 obj->cy_type = CYAML_TYPE_NULL;
367         else if (strcmp(value, "false") == 0) {
368                 obj->cy_type = CYAML_TYPE_FALSE;
369                 obj->cy_valueint = 0;
370         } else if (strcmp(value, "true") == 0) {
371                 obj->cy_type = CYAML_TYPE_TRUE;
372                 obj->cy_valueint = 1;
373         } else if (*value == '-' || (*value >= '0' && *value <= '9')) {
374                 if (parse_number(obj, value) == 0) {
375                         obj->cy_valuestring = strdup(value);
376                         obj->cy_type = CYAML_TYPE_STRING;
377                 }
378         } else {
379                 obj->cy_valuestring = strdup(value);
380                 obj->cy_type = CYAML_TYPE_STRING;
381         }
382
383         return 0;
384 }
385
386 /*
387  * yaml_handle_token
388  *  Builds the YAML tree rpresentation as the tokens are passed in
389  *
390  *  if token == STREAM_START && tree_state != COMPLETE
391  *    something wrong. fail.
392  *  else tree_state = INITIED
393  *  if token == DOCUMENT_START && tree_state != COMPLETE || INITED
394  *    something wrong, fail.
395  *  else tree_state = TREE_STARTED
396  *  if token == DOCUMENT_END
397  *    tree_state = INITED if no STREAM START, else tree_state = COMPLETE
398  *    erase everything on ll
399  *  if token == STREAM_END && tree_state != INITED
400  *    something wrong fail.
401  *  else tree_state = COMPLETED
402  *  if token == YAML_KEY_TOKEN && state != TREE_STARTED
403  *    something wrong, fail.
404  *  if token == YAML_SCALAR_TOKEN && state != KEY || VALUE
405  *    fail.
406  *  else if tree_state == KEY
407  *     create a new sibling under the current head of the ll (if ll is
408  *     empty insert the new node there and it becomes the root.)
409  *    add the scalar value in the "string"
410  *    tree_state = KEY_FILLED
411  *  else if tree_state == VALUE
412  *    try and figure out whether this is a double, int or string and store
413  *    it appropriately
414  *    state = TREE_STARTED
415  * else if token == YAML_BLOCK_MAPPING_START_TOKEN && tree_state != VALUE
416  *   fail
417  * else push the current node on the ll && state = TREE_STARTED
418  * if token == YAML_BLOCK_END_TOKEN && state != TREE_STARTED
419  *   fail.
420  * else pop the current token off the ll and make it the cur
421  * if token == YAML_VALUE_TOKEN && state != KEY_FILLED
422  *   fail.
423  * else state = VALUE
424  *
425  */
426 static enum cYAML_handler_error yaml_parse_error(yaml_token_t *token,
427                                                  struct cYAML_tree_node *tree)
428 {
429         return CYAML_ERROR_PARSE;
430 }
431
432 static enum cYAML_handler_error yaml_stream_start(yaml_token_t *token,
433                                                   struct cYAML_tree_node *tree)
434 {
435         enum cYAML_handler_error rc;
436
437         /* with each new stream initialize a new tree */
438         rc = cYAML_tree_init(tree);
439
440         if (rc != CYAML_ERROR_NONE)
441                 return rc;
442
443         tree->state = TREE_STATE_INITED;
444
445         return CYAML_ERROR_NONE;
446 }
447
448 static enum cYAML_handler_error yaml_stream_end(yaml_token_t *token,
449                                                 struct cYAML_tree_node *tree)
450 {
451         if (tree->state != TREE_STATE_TREE_STARTED &&
452             tree->state != TREE_STATE_COMPLETE)
453                 return CYAML_ERROR_UNEXPECTED_STATE;
454
455         tree->state = TREE_STATE_INITED;
456
457         return CYAML_ERROR_NONE;
458 }
459
460 static enum cYAML_handler_error
461 yaml_document_start(yaml_token_t *token, struct cYAML_tree_node *tree)
462 {
463         if (tree->state != TREE_STATE_INITED)
464                 return CYAML_ERROR_UNEXPECTED_STATE;
465
466         /* go to started state since we're expecting more tokens to come */
467         tree->state = TREE_STATE_TREE_STARTED;
468
469         return CYAML_ERROR_NONE;
470 }
471
472 static enum cYAML_handler_error yaml_document_end(yaml_token_t *token,
473                                                   struct cYAML_tree_node *tree)
474 {
475         if (tree->state != TREE_STATE_COMPLETE)
476                 return CYAML_ERROR_UNEXPECTED_STATE;
477
478         tree->state = TREE_STATE_TREE_STARTED;
479
480         return CYAML_ERROR_NONE;
481 }
482
483 static enum cYAML_handler_error yaml_key(yaml_token_t *token,
484                                          struct cYAML_tree_node *tree)
485 {
486         if (tree->state != TREE_STATE_BLK_STARTED &&
487             tree->state != TREE_STATE_VALUE)
488                 return CYAML_ERROR_UNEXPECTED_STATE;
489
490         if (tree->from_blk_map_start == 0 ||
491             tree->state == TREE_STATE_VALUE)
492                 tree->cur = create_sibling(tree->cur);
493
494         tree->from_blk_map_start = 0;
495
496         tree->state = TREE_STATE_KEY;
497
498         return CYAML_ERROR_NONE;
499 }
500
501 static enum cYAML_handler_error yaml_scalar(yaml_token_t *token,
502                                             struct cYAML_tree_node *tree)
503 {
504         if (tree->state == TREE_STATE_KEY) {
505                 /* assign the scalar value to the key that was created */
506                 tree->cur->cy_string =
507                   strdup((const char *)token->data.scalar.value);
508
509                 tree->state = TREE_STATE_KEY_FILLED;
510         } else if (tree->state == TREE_STATE_VALUE) {
511                 if (assign_type_value(tree->cur,
512                                       (char *)token->data.scalar.value))
513                         /* failed to assign a value */
514                         return CYAML_ERROR_BAD_VALUE;
515                 tree->state = TREE_STATE_BLK_STARTED;
516         } else {
517                 return CYAML_ERROR_UNEXPECTED_STATE;
518         }
519
520         return CYAML_ERROR_NONE;
521 }
522
523 static enum cYAML_handler_error yaml_value(yaml_token_t *token,
524                                            struct cYAML_tree_node *tree)
525 {
526         if (tree->state != TREE_STATE_KEY_FILLED)
527                 return CYAML_ERROR_UNEXPECTED_STATE;
528
529         tree->state = TREE_STATE_VALUE;
530
531         return CYAML_ERROR_NONE;
532 }
533
534 static enum cYAML_handler_error yaml_blk_seq_start(yaml_token_t *token,
535                                                    struct cYAML_tree_node *tree)
536 {
537         if (tree->state != TREE_STATE_VALUE)
538                 return CYAML_ERROR_UNEXPECTED_STATE;
539
540         /* Since a sequenc start event determines that this is the start
541          * of an array, then that means the current node we're at is an
542          * array and we need to flag it as such */
543         tree->cur->cy_type = CYAML_TYPE_ARRAY;
544         tree->state = TREE_STATE_SEQ_START;
545
546         return CYAML_ERROR_NONE;
547 }
548
549 static enum cYAML_handler_error yaml_entry_token(yaml_token_t *token,
550                                                  struct cYAML_tree_node *tree)
551 {
552         struct cYAML *obj;
553
554         if (tree->state != TREE_STATE_SEQ_START &&
555             tree->state != TREE_STATE_BLK_STARTED)
556                 return CYAML_ERROR_UNEXPECTED_STATE;
557
558         if (tree->state == TREE_STATE_SEQ_START) {
559                 obj = create_child(tree->cur);
560
561                 if (cYAML_ll_push(tree->cur, NULL, &tree->ll))
562                         return CYAML_ERROR_OUT_OF_MEM;
563
564                 tree->cur = obj;
565         } else {
566                 tree->cur = create_sibling(tree->cur);
567                 tree->state = TREE_STATE_SEQ_START;
568         }
569
570         return CYAML_ERROR_NONE;
571 }
572
573 static enum cYAML_handler_error
574 yaml_blk_mapping_start(yaml_token_t *token,
575                        struct cYAML_tree_node *tree)
576 {
577         struct cYAML *obj;
578
579         if (tree->state != TREE_STATE_VALUE &&
580             tree->state != TREE_STATE_INITED &&
581             tree->state != TREE_STATE_SEQ_START &&
582             tree->state != TREE_STATE_TREE_STARTED)
583                 return CYAML_ERROR_UNEXPECTED_STATE;
584
585         /* block_mapping_start means we're entering another block
586          * indentation, so we need to go one level deeper
587          * create a child of cur */
588         obj = create_child(tree->cur);
589
590         /* push cur on the stack */
591         if (cYAML_ll_push(tree->cur, NULL, &tree->ll))
592                 return CYAML_ERROR_OUT_OF_MEM;
593
594         /* adding the new child to cur */
595         tree->cur = obj;
596
597         tree->state = TREE_STATE_BLK_STARTED;
598
599         tree->from_blk_map_start = 1;
600
601         return CYAML_ERROR_NONE;
602 }
603
604 static enum cYAML_handler_error yaml_block_end(yaml_token_t *token,
605                                                struct cYAML_tree_node *tree)
606 {
607         if (tree->state != TREE_STATE_BLK_STARTED &&
608             tree->state != TREE_STATE_VALUE)
609                 return CYAML_ERROR_UNEXPECTED_STATE;
610
611         tree->cur = cYAML_ll_pop(&tree->ll, NULL);
612
613         /* if you have popped all the way to the top level, then move to
614          * the complete state. */
615         if (cYAML_ll_count(&tree->ll) == 0)
616                 tree->state = TREE_STATE_COMPLETE;
617         else if (tree->state == TREE_STATE_VALUE)
618                 tree->state = TREE_STATE_BLK_STARTED;
619
620         return CYAML_ERROR_NONE;
621 }
622
623 static enum cYAML_handler_error yaml_not_supported(yaml_token_t *token,
624                                                    struct cYAML_tree_node *tree)
625 {
626         return CYAML_ERROR_NOT_SUPPORTED;
627 }
628
629 static bool clean_usr_data(struct cYAML *node, void *usr_data, void **out)
630 {
631         cYAML_user_data_free_cb free_cb = usr_data;
632
633         if (free_cb && node && node->cy_user_data) {
634                 free_cb(node->cy_user_data);
635                 node->cy_user_data = NULL;
636         }
637
638         return true;
639 }
640
641 static bool free_node(struct cYAML *node, void *user_data, void **out)
642 {
643         if (node)
644                 free(node);
645
646         return true;
647 }
648
649 static bool find_obj_iter(struct cYAML *node, void *usr_data, void **out)
650 {
651         char *name = usr_data;
652
653         if (node != NULL && node->cy_string != NULL &&
654             strcmp(node->cy_string, name) == 0) {
655                 *out = node;
656                 return false;
657         }
658
659         return true;
660 }
661
662 struct cYAML *cYAML_get_object_item(struct cYAML *parent, const char *name)
663 {
664         struct cYAML *node;
665
666         if (parent == NULL || parent->cy_child == NULL || name == NULL)
667                 return NULL;
668
669         node = parent->cy_child;
670
671         while (node != NULL &&
672                 strcmp(node->cy_string, name) != 0) {
673                 node = node->cy_next;
674         }
675
676         return node;
677 }
678
679 struct cYAML *cYAML_get_next_seq_item(struct cYAML *seq, struct cYAML **itm)
680 {
681         if (*itm != NULL && (*itm)->cy_next != NULL) {
682                 *itm = (*itm)->cy_next;
683                 return *itm;
684         }
685
686         if (*itm == NULL && seq->cy_type == CYAML_TYPE_ARRAY) {
687                 *itm = seq->cy_child;
688                 return *itm;
689         }
690
691         return NULL;
692 }
693
694 bool cYAML_is_sequence(struct cYAML *node)
695 {
696         return (node != NULL ? node->cy_type == CYAML_TYPE_ARRAY : 0);
697 }
698
699 void cYAML_tree_recursive_walk(struct cYAML *node, cYAML_walk_cb cb,
700                                       bool cb_first,
701                                       void *usr_data,
702                                       void **out)
703 {
704         if (node == NULL)
705                 return;
706
707         if (cb_first) {
708                 if (!cb(node, usr_data, out))
709                         return;
710         }
711
712         if (node->cy_child)
713                 cYAML_tree_recursive_walk(node->cy_child, cb,
714                                           cb_first, usr_data, out);
715
716         if (node->cy_next)
717                 cYAML_tree_recursive_walk(node->cy_next, cb,
718                                           cb_first, usr_data, out);
719
720         if (!cb_first) {
721                 if (!cb(node, usr_data, out))
722                         return;
723         }
724 }
725
726 struct cYAML *cYAML_find_object(struct cYAML *root, const char *name)
727 {
728         struct cYAML *found = NULL;
729
730         cYAML_tree_recursive_walk(root, find_obj_iter, true,
731                                   (void *)name, (void **)&found);
732
733         return found;
734 }
735
736 void cYAML_clean_usr_data(struct cYAML *node, cYAML_user_data_free_cb free_cb)
737 {
738         cYAML_tree_recursive_walk(node, clean_usr_data, false, free_cb, NULL);
739 }
740
741 void cYAML_free_tree(struct cYAML *node)
742 {
743         cYAML_tree_recursive_walk(node, free_node, false, NULL, NULL);
744 }
745
746 static inline void print_simple(FILE *f, struct cYAML *node,
747                                 struct cYAML_print_info *cpi)
748 {
749         int level = cpi->level;
750         int ind = cpi->extra_ind;
751
752         if (cpi->array_first_elem)
753                 fprintf(f, "%*s- ", INDENT * level, "");
754
755         fprintf(f, "%*s""%s: %d\n", (cpi->array_first_elem) ? 0 :
756                 INDENT * level + ind, "", node->cy_string,
757                 node->cy_valueint);
758 }
759
760 static void print_string(FILE *f, struct cYAML *node,
761                          struct cYAML_print_info *cpi)
762 {
763         char *new_line;
764         int level = cpi->level;
765         int ind = cpi->extra_ind;
766
767         if (cpi->array_first_elem)
768                 fprintf(f, "%*s- ", INDENT * level, "");
769
770         new_line = strchr(node->cy_valuestring, '\n');
771         if (new_line == NULL)
772                 fprintf(f, "%*s""%s: %s\n", (cpi->array_first_elem) ?
773                         0 : INDENT * level + ind, "",
774                         node->cy_string, node->cy_valuestring);
775         else {
776                 int indent = 0;
777                 fprintf(f, "%*s""%s: ", (cpi->array_first_elem) ?
778                         0 : INDENT * level + ind, "",
779                         node->cy_string);
780                 char *l = node->cy_valuestring;
781                 while (new_line) {
782                         *new_line = '\0';
783                         fprintf(f, "%*s""%s\n", indent, "", l);
784                         indent = INDENT * level + ind +
785                                   strlen(node->cy_string) + 2;
786                         *new_line = '\n';
787                         l = new_line+1;
788                         new_line = strchr(l, '\n');
789                 }
790                 fprintf(f, "%*s""%s\n", indent, "", l);
791         }
792 }
793
794 static void print_number(FILE *f, struct cYAML *node,
795                          struct cYAML_print_info *cpi)
796 {
797         double d = node->cy_valuedouble;
798         int level = cpi->level;
799         int ind = cpi->extra_ind;
800
801         if (cpi->array_first_elem)
802                 fprintf(f, "%*s- ", INDENT * level, "");
803
804         if ((fabs(((double)node->cy_valueint) - d) <= DBL_EPSILON) &&
805             (d <= INT_MAX) && (d >= INT_MIN))
806                 fprintf(f, "%*s""%s: %d\n", (cpi->array_first_elem) ? 0 :
807                         INDENT * level + ind, "",
808                         node->cy_string, node->cy_valueint);
809         else {
810                 if ((fabs(floor(d) - d) <= DBL_EPSILON) &&
811                     (fabs(d) < 1.0e60))
812                         fprintf(f, "%*s""%s: %.0f\n",
813                                 (cpi->array_first_elem) ? 0 :
814                                 INDENT * level + ind, "",
815                                 node->cy_string, d);
816                 else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9))
817                         fprintf(f, "%*s""%s: %e\n",
818                                 (cpi->array_first_elem) ? 0 :
819                                 INDENT * level + ind, "",
820                                 node->cy_string, d);
821                 else
822                         fprintf(f, "%*s""%s: %f\n",
823                                 (cpi->array_first_elem) ? 0 :
824                                 INDENT * level + ind, "",
825                                 node->cy_string, d);
826         }
827 }
828
829 static void print_object(FILE *f, struct cYAML *node,
830                          struct list_head *stack,
831                          struct cYAML_print_info *cpi)
832 {
833         struct cYAML_print_info print_info;
834         struct cYAML *child = node->cy_child;
835
836         if (node->cy_string != NULL)
837                 fprintf(f, "%*s""%s%s:\n", (cpi->array_first_elem) ?
838                         INDENT * cpi->level :
839                         INDENT * cpi->level + cpi->extra_ind,
840                         "", (cpi->array_first_elem) ? "- " : "",
841                         node->cy_string);
842
843         print_info.level = (node->cy_string != NULL) ? cpi->level + 1 :
844           cpi->level;
845         print_info.array_first_elem = (node->cy_string == NULL) ?
846           cpi->array_first_elem : 0;
847         print_info.extra_ind = (cpi->array_first_elem) ? EXTRA_IND :
848           cpi->extra_ind;
849
850         while (child) {
851                 if (cYAML_ll_push(child, &print_info, stack) != 0)
852                         return;
853                 print_value(f, stack);
854                 print_info.array_first_elem = 0;
855                 child = child->cy_next;
856         }
857 }
858
859 static void print_array(FILE *f, struct cYAML *node,
860                         struct list_head *stack,
861                         struct cYAML_print_info *cpi)
862 {
863         struct cYAML_print_info print_info;
864         struct cYAML *child = node->cy_child;
865
866         if (node->cy_string != NULL) {
867                 fprintf(f, "%*s""%s:\n", INDENT * cpi->level + cpi->extra_ind,
868                         "", node->cy_string);
869         }
870
871         print_info.level = (node->cy_string != NULL) ? cpi->level + 1 :
872           cpi->level;
873         print_info.array_first_elem =  1;
874         print_info.extra_ind = EXTRA_IND;
875
876         while (child) {
877                 if (cYAML_ll_push(child, &print_info, stack) != 0)
878                         return;
879                 print_value(f, stack);
880                 child = child->cy_next;
881         }
882 }
883
884 static void print_value(FILE *f, struct list_head *stack)
885 {
886         struct cYAML_print_info *cpi = NULL;
887         struct cYAML *node = cYAML_ll_pop(stack, &cpi);
888
889         if (node == NULL)
890                 return;
891
892         switch (node->cy_type) {
893         case CYAML_TYPE_FALSE:
894         case CYAML_TYPE_TRUE:
895         case CYAML_TYPE_NULL:
896                 print_simple(f, node, cpi);
897                 break;
898         case CYAML_TYPE_STRING:
899                 print_string(f, node, cpi);
900                 break;
901         case CYAML_TYPE_NUMBER:
902                 print_number(f, node, cpi);
903                 break;
904         case CYAML_TYPE_ARRAY:
905                 print_array(f, node, stack, cpi);
906                 break;
907         case CYAML_TYPE_OBJECT:
908                 print_object(f, node, stack, cpi);
909                 break;
910         default:
911         break;
912         }
913
914         if (cpi != NULL)
915                 free(cpi);
916 }
917
918 void cYAML_print_tree(struct cYAML *node)
919 {
920         struct cYAML_print_info print_info;
921         struct list_head list;
922
923         INIT_LIST_HEAD(&list);
924
925         if (node == NULL)
926                 return;
927
928         memset(&print_info, 0, sizeof(struct cYAML_print_info));
929
930         if (cYAML_ll_push(node, &print_info, &list) == 0)
931                 print_value(stdout, &list);
932 }
933
934 void cYAML_print_tree2file(FILE *f, struct cYAML *node)
935 {
936         struct cYAML_print_info print_info;
937         struct list_head list;
938
939         INIT_LIST_HEAD(&list);
940
941         if (node == NULL)
942                 return;
943
944         memset(&print_info, 0, sizeof(struct cYAML_print_info));
945
946         if (cYAML_ll_push(node, &print_info, &list) == 0)
947                 print_value(f, &list);
948 }
949
950 static struct cYAML *insert_item(struct cYAML *parent, char *key,
951                                  enum cYAML_object_type type)
952 {
953         struct cYAML *node = calloc(1, sizeof(*node));
954
955         if (node == NULL)
956                 return NULL;
957
958         if (key != NULL)
959                 node->cy_string = strdup(key);
960
961         node->cy_type = type;
962
963         cYAML_insert_child(parent, node);
964
965         return node;
966 }
967
968 struct cYAML *cYAML_create_seq(struct cYAML *parent, char *key)
969 {
970         return insert_item(parent, key, CYAML_TYPE_ARRAY);
971 }
972
973 struct cYAML *cYAML_create_seq_item(struct cYAML *seq)
974 {
975         return insert_item(seq, NULL, CYAML_TYPE_OBJECT);
976 }
977
978 struct cYAML *cYAML_create_object(struct cYAML *parent, char *key)
979 {
980         return insert_item(parent, key, CYAML_TYPE_OBJECT);
981 }
982
983 struct cYAML *cYAML_create_string(struct cYAML *parent, char *key, char *value)
984 {
985         struct cYAML *node = calloc(1, sizeof(*node));
986         if (node == NULL)
987                 return NULL;
988
989         node->cy_string = strdup(key);
990         node->cy_valuestring = strdup(value);
991         node->cy_type = CYAML_TYPE_STRING;
992
993         cYAML_insert_child(parent, node);
994
995         return node;
996 }
997
998 struct cYAML *cYAML_create_number(struct cYAML *parent, char *key, double value)
999 {
1000         struct cYAML *node = calloc(1, sizeof(*node));
1001         if (node == NULL)
1002                 return NULL;
1003
1004         node->cy_string = strdup(key);
1005         node->cy_valuedouble = value;
1006         node->cy_valueint = (int)value;
1007         node->cy_type = CYAML_TYPE_NUMBER;
1008
1009         cYAML_insert_child(parent, node);
1010
1011         return node;
1012 }
1013
1014 void cYAML_insert_child(struct cYAML *parent, struct cYAML *node)
1015 {
1016         struct cYAML *cur;
1017
1018         if (parent && node) {
1019                 if (parent->cy_child == NULL) {
1020                         parent->cy_child = node;
1021                         return;
1022                 }
1023
1024                 cur = parent->cy_child;
1025
1026                 while (cur->cy_next)
1027                         cur = cur->cy_next;
1028
1029                 cur->cy_next = node;
1030                 node->cy_prev = cur;
1031         }
1032 }
1033
1034 void cYAML_insert_sibling(struct cYAML *root, struct cYAML *sibling)
1035 {
1036         struct cYAML *last = NULL;
1037         if (root == NULL || sibling == NULL)
1038                 return;
1039
1040         last = root;
1041         while (last->cy_next != NULL)
1042                 last = last->cy_next;
1043
1044         last->cy_next = sibling;
1045 }
1046
1047 void cYAML_build_error(int rc, int seq_no, char *cmd,
1048                        char *entity, char *err_str,
1049                        struct cYAML **root)
1050 {
1051         struct cYAML *r = NULL, *err, *s, *itm, *cmd_obj;
1052         if (root == NULL)
1053                 return;
1054
1055         /* add to the tail of the root that's passed in */
1056         if ((*root) == NULL) {
1057                 *root = cYAML_create_object(NULL, NULL);
1058                 if ((*root) == NULL)
1059                         goto failed;
1060         }
1061
1062         r = *root;
1063
1064         /* look for the command */
1065         cmd_obj = cYAML_get_object_item(r, (const char *)cmd);
1066         if (cmd_obj != NULL && cmd_obj->cy_type == CYAML_TYPE_ARRAY)
1067                 itm = cYAML_create_seq_item(cmd_obj);
1068         else if (cmd_obj == NULL) {
1069                 s = cYAML_create_seq(r, cmd);
1070                 itm = cYAML_create_seq_item(s);
1071         } else if (cmd_obj != NULL && cmd_obj->cy_type != CYAML_TYPE_ARRAY)
1072                 goto failed;
1073
1074         err = cYAML_create_object(itm, entity);
1075         if (err == NULL)
1076                 goto failed;
1077
1078         if (seq_no >= 0 &&
1079             cYAML_create_number(err, "seq_no", seq_no) == NULL)
1080                 goto failed;
1081
1082         if (cYAML_create_number(err, "errno", rc) == NULL)
1083                 goto failed;
1084
1085         if (cYAML_create_string(err, "descr", err_str) == NULL)
1086                 goto failed;
1087
1088         return;
1089
1090 failed:
1091         /* Only reason we get here is if we run out of memory */
1092         cYAML_free_tree(r);
1093         r = NULL;
1094         fprintf(stderr, "error:\n\tfatal: out of memory\n");
1095 }
1096
1097 struct cYAML *cYAML_build_tree(char *yaml_file,
1098                                const char *yaml_blk,
1099                                size_t yaml_blk_size,
1100                                struct cYAML **err_rc)
1101 {
1102         yaml_parser_t parser;
1103         yaml_token_t token;
1104         struct cYAML_tree_node tree;
1105         enum cYAML_handler_error rc;
1106         yaml_token_type_t token_type;
1107         char err_str[256];
1108         FILE *input = NULL;
1109         int done = 0;
1110
1111         memset(&tree, 0, sizeof(struct cYAML_tree_node));
1112
1113         INIT_LIST_HEAD(&tree.ll);
1114
1115         /* Create the Parser object. */
1116         yaml_parser_initialize(&parser);
1117
1118         /* file always takes precedence */
1119         if (yaml_file != NULL) {
1120                 /* Set a file input. */
1121                 input = fopen(yaml_file, "rb");
1122                 if (input == NULL) {
1123                         snprintf(err_str, sizeof(err_str),
1124                                 "Failed to open file: %s", yaml_file);
1125                         cYAML_build_error(-1, -1, "yaml", "builder",
1126                                           err_str,
1127                                           err_rc);
1128                         return NULL;
1129                 }
1130
1131                 yaml_parser_set_input_file(&parser, input);
1132         } else if (yaml_blk != NULL) {
1133                 yaml_parser_set_input_string(&parser,
1134                                              (const unsigned char *) yaml_blk,
1135                                              yaml_blk_size);
1136         } else
1137                 /* assume that we're getting our input froms stdin */
1138                 yaml_parser_set_input_file(&parser, stdin);
1139
1140         /* Read the event sequence. */
1141         while (!done) {
1142                 /*
1143                 * Go through the parser and build a cYAML representation
1144                 * of the passed in YAML text
1145                 */
1146                 yaml_parser_scan(&parser, &token);
1147
1148                 rc = dispatch_tbl[token.type](&token, &tree);
1149                 if (rc != CYAML_ERROR_NONE) {
1150                         snprintf(err_str, sizeof(err_str),
1151                                 "Failed to handle token:%d "
1152                                 "[state=%d, rc=%d]",
1153                                 token.type, tree.state, rc);
1154                         cYAML_build_error(-1, -1, "yaml", "builder",
1155                                           err_str,
1156                                           err_rc);
1157                 }
1158                 /* Are we finished? */
1159                 done = (rc != CYAML_ERROR_NONE ||
1160                         token.type == YAML_STREAM_END_TOKEN ||
1161                         token.type == YAML_NO_TOKEN);
1162
1163                 token_type = token.type;
1164
1165                 yaml_token_delete(&token);
1166         }
1167
1168         /* Destroy the Parser object. */
1169         yaml_parser_delete(&parser);
1170
1171         if (input != NULL)
1172                 fclose(input);
1173
1174         if (token_type == YAML_STREAM_END_TOKEN &&
1175             rc == CYAML_ERROR_NONE)
1176                 return tree.root;
1177
1178         cYAML_free_tree(tree.root);
1179
1180         return NULL;
1181 }