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