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