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