/* * LGPL HEADER START * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * LGPL HEADER END * * Copyright (c) 2014, 2017, Intel Corporation. * * Author: * Amir Shehata */ /* * The cYAML tree is constructed as an n-tree. * root -> cmd 1 * || * \/ * cmd 2 -> attr1 -> attr2 * || * \/ * attr2.1 -> attr2.1.1 -> attr2.1.2 */ #include #include #include #include #include #include #include #include #include #include "libcfs/util/list.h" #include #define INDENT 4 #define EXTRA_IND 2 /* * cYAML_print_info * This structure contains print information * required when printing the node */ struct cYAML_print_info { int level; int array_first_elem; int extra_ind; }; /* * cYAML_ll * Linked list of different trees representing YAML * documents. */ struct cYAML_ll { struct list_head list; struct cYAML *obj; struct cYAML_print_info *print_info; }; static void print_value(FILE *f, struct list_head *stack); enum cYAML_handler_error { CYAML_ERROR_NONE = 0, CYAML_ERROR_UNEXPECTED_STATE = -1, CYAML_ERROR_NOT_SUPPORTED = -2, CYAML_ERROR_OUT_OF_MEM = -3, CYAML_ERROR_BAD_VALUE = -4, CYAML_ERROR_PARSE = -5, }; enum cYAML_tree_state { TREE_STATE_COMPLETE = 0, TREE_STATE_INITED, TREE_STATE_TREE_STARTED, TREE_STATE_BLK_STARTED, TREE_STATE_KEY, TREE_STATE_KEY_FILLED, TREE_STATE_VALUE, TREE_STATE_SEQ_START, }; struct cYAML_tree_node { struct cYAML *root; /* cur is the current node we're operating on */ struct cYAML *cur; enum cYAML_tree_state state; int from_blk_map_start; /* represents the tree depth */ struct list_head ll; }; typedef enum cYAML_handler_error (*yaml_token_handler)(yaml_token_t *token, struct cYAML_tree_node *); static enum cYAML_handler_error yaml_parse_error(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_stream_start(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_stream_end(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_not_supported(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_document_start(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_document_end(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_blk_seq_start(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_blk_mapping_start(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_block_end(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_key(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_value(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_scalar(yaml_token_t *token, struct cYAML_tree_node *tree); static enum cYAML_handler_error yaml_entry_token(yaml_token_t *token, struct cYAML_tree_node *tree); /* dispatch table */ static yaml_token_handler dispatch_tbl[] = { [YAML_NO_TOKEN] = yaml_parse_error, [YAML_STREAM_START_TOKEN] = yaml_stream_start, [YAML_STREAM_END_TOKEN] = yaml_stream_end, [YAML_VERSION_DIRECTIVE_TOKEN] = yaml_not_supported, [YAML_TAG_DIRECTIVE_TOKEN] = yaml_not_supported, [YAML_DOCUMENT_START_TOKEN] = yaml_document_start, [YAML_DOCUMENT_END_TOKEN] = yaml_document_end, [YAML_BLOCK_SEQUENCE_START_TOKEN] = yaml_blk_seq_start, [YAML_BLOCK_MAPPING_START_TOKEN] = yaml_blk_mapping_start, [YAML_BLOCK_END_TOKEN] = yaml_block_end, [YAML_FLOW_SEQUENCE_START_TOKEN] = yaml_not_supported, [YAML_FLOW_SEQUENCE_END_TOKEN] = yaml_not_supported, [YAML_FLOW_MAPPING_START_TOKEN] = yaml_not_supported, [YAML_FLOW_MAPPING_END_TOKEN] = yaml_not_supported, [YAML_BLOCK_ENTRY_TOKEN] = yaml_entry_token, [YAML_FLOW_ENTRY_TOKEN] = yaml_not_supported, [YAML_KEY_TOKEN] = yaml_key, [YAML_VALUE_TOKEN] = yaml_value, [YAML_ALIAS_TOKEN] = yaml_not_supported, [YAML_ANCHOR_TOKEN] = yaml_not_supported, [YAML_TAG_TOKEN] = yaml_not_supported, [YAML_SCALAR_TOKEN] = yaml_scalar, }; /* dispatch table */ static char *token_type_string[] = { [YAML_NO_TOKEN] = "YAML_NO_TOKEN", [YAML_STREAM_START_TOKEN] = "YAML_STREAM_START_TOKEN", [YAML_STREAM_END_TOKEN] = "YAML_STREAM_END_TOKEN", [YAML_VERSION_DIRECTIVE_TOKEN] = "YAML_VERSION_DIRECTIVE_TOKEN", [YAML_TAG_DIRECTIVE_TOKEN] = "YAML_TAG_DIRECTIVE_TOKEN", [YAML_DOCUMENT_START_TOKEN] = "YAML_DOCUMENT_START_TOKEN", [YAML_DOCUMENT_END_TOKEN] = "YAML_DOCUMENT_END_TOKEN", [YAML_BLOCK_SEQUENCE_START_TOKEN] = "YAML_BLOCK_SEQUENCE_START_TOKEN", [YAML_BLOCK_MAPPING_START_TOKEN] = "YAML_BLOCK_MAPPING_START_TOKEN", [YAML_BLOCK_END_TOKEN] = "YAML_BLOCK_END_TOKEN", [YAML_FLOW_SEQUENCE_START_TOKEN] = "YAML_FLOW_SEQUENCE_START_TOKEN", [YAML_FLOW_SEQUENCE_END_TOKEN] = "YAML_FLOW_SEQUENCE_END_TOKEN", [YAML_FLOW_MAPPING_START_TOKEN] = "YAML_FLOW_MAPPING_START_TOKEN", [YAML_FLOW_MAPPING_END_TOKEN] = "YAML_FLOW_MAPPING_END_TOKEN", [YAML_BLOCK_ENTRY_TOKEN] = "YAML_BLOCK_ENTRY_TOKEN", [YAML_FLOW_ENTRY_TOKEN] = "YAML_FLOW_ENTRY_TOKEN", [YAML_KEY_TOKEN] = "YAML_KEY_TOKEN", [YAML_VALUE_TOKEN] = "YAML_VALUE_TOKEN", [YAML_ALIAS_TOKEN] = "YAML_ALIAS_TOKEN", [YAML_ANCHOR_TOKEN] = "YAML_ANCHOR_TOKEN", [YAML_TAG_TOKEN] = "YAML_TAG_TOKEN", [YAML_SCALAR_TOKEN] = "YAML_SCALAR_TOKEN", }; static char *state_string[] = { [TREE_STATE_COMPLETE] = "COMPLETE", [TREE_STATE_INITED] = "INITED", [TREE_STATE_TREE_STARTED] = "TREE_STARTED", [TREE_STATE_BLK_STARTED] = "BLK_STARTED", [TREE_STATE_KEY] = "KEY", [TREE_STATE_KEY_FILLED] = "KEY_FILLED", [TREE_STATE_VALUE] = "VALUE", [TREE_STATE_SEQ_START] = "SEQ_START", }; static void cYAML_ll_free(struct list_head *ll) { struct cYAML_ll *node, *tmp; list_for_each_entry_safe(node, tmp, ll, list) { free(node->print_info); free(node); } } static int cYAML_ll_push(struct cYAML *obj, const struct cYAML_print_info *print_info, struct list_head *list) { struct cYAML_ll *node = calloc(1, sizeof(*node)); if (node == NULL) return -1; INIT_LIST_HEAD(&node->list); if (print_info) { node->print_info = calloc(1, sizeof(*print_info)); if (node->print_info == NULL) { free(node); return -1; } *node->print_info = *print_info; } node->obj = obj; list_add(&node->list, list); return 0; } static struct cYAML *cYAML_ll_pop(struct list_head *list, struct cYAML_print_info **print_info) { struct cYAML_ll *pop; struct cYAML *obj = NULL; if (!list_empty(list)) { pop = list_entry(list->next, struct cYAML_ll, list); obj = pop->obj; if (print_info != NULL) *print_info = pop->print_info; list_del(&pop->list); if (print_info == NULL) free(pop->print_info); free(pop); } return obj; } static int cYAML_ll_count(struct list_head *ll) { int i = 0; struct list_head *node; list_for_each(node, ll) i++; return i; } static int cYAML_tree_init(struct cYAML_tree_node *tree) { struct cYAML *obj = NULL, *cur = NULL; if (tree == NULL) return -1; obj = calloc(1, sizeof(*obj)); if (obj == NULL) return -1; if (tree->root) { /* append the node */ cur = tree->root; while (cur->cy_next != NULL) cur = cur->cy_next; cur->cy_next = obj; } else { tree->root = obj; } obj->cy_type = CYAML_TYPE_OBJECT; tree->cur = obj; tree->state = TREE_STATE_COMPLETE; /* free it and start anew */ if (!list_empty(&tree->ll)) cYAML_ll_free(&tree->ll); return 0; } static struct cYAML *create_child(struct cYAML *parent) { struct cYAML *obj; if (parent == NULL) return NULL; obj = calloc(1, sizeof(*obj)); if (obj == NULL) return NULL; /* set the type to OBJECT and let the value change that */ obj->cy_type = CYAML_TYPE_OBJECT; parent->cy_child = obj; return obj; } static struct cYAML *create_sibling(struct cYAML *sibling) { struct cYAML *obj; if (sibling == NULL) return NULL; obj = calloc(1, sizeof(*obj)); if (obj == NULL) return NULL; /* set the type to OBJECT and let the value change that */ obj->cy_type = CYAML_TYPE_OBJECT; sibling->cy_next = obj; obj->cy_prev = sibling; return obj; } /* Parse the input text to generate a number, * and populate the result into item. */ static bool parse_number(struct cYAML *item, const char *input) { double n = 0, sign = 1, scale = 0; int subscale = 0, signsubscale = 1; const char *num = input; if (*num == '-') { sign = -1; num++; } if (*num == '0') num++; if (*num >= '1' && *num <= '9') { do { n = (n * 10.0) + (*num++ - '0'); } while (*num >= '0' && *num <= '9'); } if (*num == '.' && num[1] >= '0' && num[1] <= '9') { num++; do { n = (n * 10.0) + (*num++ - '0'); scale--; } while (*num >= '0' && *num <= '9'); } if (*num == 'e' || *num == 'E') { num++; if (*num == '+') { num++; } else if (*num == '-') { signsubscale = -1; num++; } while (*num >= '0' && *num <= '9') subscale = (subscale * 10) + (*num++ - '0'); } /* check to see if the entire string is consumed. If not then * that means this is a string with a number in it */ if (num != (input + strlen(input))) return false; /* number = +/- number.fraction * 10^+/- exponent */ n = sign * n * pow(10.0, (scale + subscale * signsubscale)); item->cy_valuedouble = n; item->cy_valueint = (int)n; item->cy_type = CYAML_TYPE_NUMBER; return true; } static int assign_type_value(struct cYAML *obj, const char *value) { if (value == NULL) return -1; if (strcmp(value, "null") == 0) obj->cy_type = CYAML_TYPE_NULL; else if (strcmp(value, "false") == 0) { obj->cy_type = CYAML_TYPE_FALSE; obj->cy_valueint = 0; } else if (strcmp(value, "true") == 0) { obj->cy_type = CYAML_TYPE_TRUE; obj->cy_valueint = 1; } else if (*value == '-' || (*value >= '0' && *value <= '9')) { if (parse_number(obj, value) == 0) { obj->cy_valuestring = strdup(value); obj->cy_type = CYAML_TYPE_STRING; } } else { obj->cy_valuestring = strdup(value); obj->cy_type = CYAML_TYPE_STRING; } return 0; } /* * yaml_handle_token * Builds the YAML tree rpresentation as the tokens are passed in * * if token == STREAM_START && tree_state != COMPLETE * something wrong. fail. * else tree_state = INITIED * if token == DOCUMENT_START && tree_state != COMPLETE || INITED * something wrong, fail. * else tree_state = TREE_STARTED * if token == DOCUMENT_END * tree_state = INITED if no STREAM START, else tree_state = COMPLETE * erase everything on ll * if token == STREAM_END && tree_state != INITED * something wrong fail. * else tree_state = COMPLETED * if token == YAML_KEY_TOKEN && state != TREE_STARTED * something wrong, fail. * if token == YAML_SCALAR_TOKEN && state != KEY || VALUE * fail. * else if tree_state == KEY * create a new sibling under the current head of the ll (if ll is * empty insert the new node there and it becomes the root.) * add the scalar value in the "string" * tree_state = KEY_FILLED * else if tree_state == VALUE * try and figure out whether this is a double, int or string and store * it appropriately * state = TREE_STARTED * else if token == YAML_BLOCK_MAPPING_START_TOKEN && tree_state != VALUE * fail * else push the current node on the ll && state = TREE_STARTED * if token == YAML_BLOCK_END_TOKEN && state != TREE_STARTED * fail. * else pop the current token off the ll and make it the cur * if token == YAML_VALUE_TOKEN && state != KEY_FILLED * fail. * else state = VALUE * */ static enum cYAML_handler_error yaml_parse_error(yaml_token_t *token, struct cYAML_tree_node *tree) { return CYAML_ERROR_PARSE; } static enum cYAML_handler_error yaml_stream_start(yaml_token_t *token, struct cYAML_tree_node *tree) { enum cYAML_handler_error rc; /* with each new stream initialize a new tree */ rc = cYAML_tree_init(tree); if (rc != CYAML_ERROR_NONE) return rc; tree->state = TREE_STATE_INITED; return CYAML_ERROR_NONE; } static enum cYAML_handler_error yaml_stream_end(yaml_token_t *token, struct cYAML_tree_node *tree) { if (tree->state != TREE_STATE_TREE_STARTED && tree->state != TREE_STATE_COMPLETE && tree->state != TREE_STATE_INITED) return CYAML_ERROR_UNEXPECTED_STATE; tree->state = TREE_STATE_INITED; return CYAML_ERROR_NONE; } static enum cYAML_handler_error yaml_document_start(yaml_token_t *token, struct cYAML_tree_node *tree) { if (tree->state != TREE_STATE_INITED) return CYAML_ERROR_UNEXPECTED_STATE; /* go to started state since we're expecting more tokens to come */ tree->state = TREE_STATE_TREE_STARTED; return CYAML_ERROR_NONE; } static enum cYAML_handler_error yaml_document_end(yaml_token_t *token, struct cYAML_tree_node *tree) { if (tree->state != TREE_STATE_COMPLETE) return CYAML_ERROR_UNEXPECTED_STATE; tree->state = TREE_STATE_TREE_STARTED; return CYAML_ERROR_NONE; } static enum cYAML_handler_error yaml_key(yaml_token_t *token, struct cYAML_tree_node *tree) { if (tree->state != TREE_STATE_BLK_STARTED && tree->state != TREE_STATE_VALUE) return CYAML_ERROR_UNEXPECTED_STATE; if (tree->from_blk_map_start == 0 || tree->state == TREE_STATE_VALUE) tree->cur = create_sibling(tree->cur); tree->from_blk_map_start = 0; tree->state = TREE_STATE_KEY; return CYAML_ERROR_NONE; } static enum cYAML_handler_error yaml_scalar(yaml_token_t *token, struct cYAML_tree_node *tree) { if (tree->state == TREE_STATE_KEY) { /* assign the scalar value to the key that was created */ tree->cur->cy_string = strdup((const char *)token->data.scalar.value); tree->state = TREE_STATE_KEY_FILLED; } else if (tree->state == TREE_STATE_VALUE) { if (assign_type_value(tree->cur, (char *)token->data.scalar.value)) /* failed to assign a value */ return CYAML_ERROR_BAD_VALUE; tree->state = TREE_STATE_BLK_STARTED; } else { return CYAML_ERROR_UNEXPECTED_STATE; } return CYAML_ERROR_NONE; } static enum cYAML_handler_error yaml_value(yaml_token_t *token, struct cYAML_tree_node *tree) { if (tree->state != TREE_STATE_KEY_FILLED) return CYAML_ERROR_UNEXPECTED_STATE; tree->state = TREE_STATE_VALUE; return CYAML_ERROR_NONE; } static enum cYAML_handler_error yaml_blk_seq_start(yaml_token_t *token, struct cYAML_tree_node *tree) { if (tree->state != TREE_STATE_VALUE) return CYAML_ERROR_UNEXPECTED_STATE; /* Since a sequenc start event determines that this is the start * of an array, then that means the current node we're at is an * array and we need to flag it as such */ tree->cur->cy_type = CYAML_TYPE_ARRAY; tree->state = TREE_STATE_SEQ_START; return CYAML_ERROR_NONE; } static enum cYAML_handler_error yaml_entry_token(yaml_token_t *token, struct cYAML_tree_node *tree) { struct cYAML *obj; if (tree->state != TREE_STATE_SEQ_START && tree->state != TREE_STATE_BLK_STARTED && tree->state != TREE_STATE_VALUE) return CYAML_ERROR_UNEXPECTED_STATE; if (tree->state == TREE_STATE_SEQ_START) { obj = create_child(tree->cur); if (cYAML_ll_push(tree->cur, NULL, &tree->ll)) return CYAML_ERROR_OUT_OF_MEM; tree->cur = obj; } else { tree->cur = create_sibling(tree->cur); tree->state = TREE_STATE_SEQ_START; } return CYAML_ERROR_NONE; } static enum cYAML_handler_error yaml_blk_mapping_start(yaml_token_t *token, struct cYAML_tree_node *tree) { struct cYAML *obj; if (tree->state != TREE_STATE_VALUE && tree->state != TREE_STATE_INITED && tree->state != TREE_STATE_SEQ_START && tree->state != TREE_STATE_TREE_STARTED) return CYAML_ERROR_UNEXPECTED_STATE; /* block_mapping_start means we're entering another block * indentation, so we need to go one level deeper * create a child of cur */ obj = create_child(tree->cur); /* push cur on the stack */ if (cYAML_ll_push(tree->cur, NULL, &tree->ll)) return CYAML_ERROR_OUT_OF_MEM; /* adding the new child to cur */ tree->cur = obj; tree->state = TREE_STATE_BLK_STARTED; tree->from_blk_map_start = 1; return CYAML_ERROR_NONE; } static enum cYAML_handler_error yaml_block_end(yaml_token_t *token, struct cYAML_tree_node *tree) { if (tree->state != TREE_STATE_BLK_STARTED && tree->state != TREE_STATE_VALUE) return CYAML_ERROR_UNEXPECTED_STATE; tree->cur = cYAML_ll_pop(&tree->ll, NULL); /* if you have popped all the way to the top level, then move to * the complete state. */ if (cYAML_ll_count(&tree->ll) == 0) tree->state = TREE_STATE_COMPLETE; else if (tree->state == TREE_STATE_VALUE) tree->state = TREE_STATE_BLK_STARTED; return CYAML_ERROR_NONE; } static enum cYAML_handler_error yaml_not_supported(yaml_token_t *token, struct cYAML_tree_node *tree) { return CYAML_ERROR_NOT_SUPPORTED; } static bool clean_usr_data(struct cYAML *node, void *usr_data, void **out) { cYAML_user_data_free_cb free_cb = usr_data; if (free_cb && node && node->cy_user_data) { free_cb(node->cy_user_data); node->cy_user_data = NULL; } return true; } static bool free_node(struct cYAML *node, void *user_data, void **out) { if (!node) return true; if (node->cy_type == CYAML_TYPE_STRING) free(node->cy_valuestring); if (node->cy_string) free(node->cy_string); free(node); return true; } static bool find_obj_iter(struct cYAML *node, void *usr_data, void **out) { char *name = usr_data; if (node != NULL && node->cy_string != NULL && strcmp(node->cy_string, name) == 0) { *out = node; return false; } return true; } struct cYAML *cYAML_get_object_item(struct cYAML *parent, const char *name) { struct cYAML *node = parent, *found = NULL; if (!node || !name) return NULL; if (node->cy_string) { if (strcmp(node->cy_string, name) == 0) return node; } if (node->cy_child) found = cYAML_get_object_item(node->cy_child, name); if (!found && node->cy_next) found = cYAML_get_object_item(node->cy_next, name); return found; } struct cYAML *cYAML_get_next_seq_item(struct cYAML *seq, struct cYAML **itm) { if (*itm != NULL && (*itm)->cy_next != NULL) { *itm = (*itm)->cy_next; return *itm; } if (*itm == NULL && seq->cy_type == CYAML_TYPE_ARRAY) { *itm = seq->cy_child; return *itm; } return NULL; } bool cYAML_is_sequence(struct cYAML *node) { return (node != NULL ? node->cy_type == CYAML_TYPE_ARRAY : 0); } void cYAML_tree_recursive_walk(struct cYAML *node, cYAML_walk_cb cb, bool cb_first, void *usr_data, void **out) { if (node == NULL) return; if (cb_first) { if (!cb(node, usr_data, out)) return; } if (node->cy_child) cYAML_tree_recursive_walk(node->cy_child, cb, cb_first, usr_data, out); if (node->cy_next) cYAML_tree_recursive_walk(node->cy_next, cb, cb_first, usr_data, out); if (!cb_first) { if (!cb(node, usr_data, out)) return; } } struct cYAML *cYAML_find_object(struct cYAML *root, const char *name) { struct cYAML *found = NULL; cYAML_tree_recursive_walk(root, find_obj_iter, true, (void *)name, (void **)&found); return found; } void cYAML_clean_usr_data(struct cYAML *node, cYAML_user_data_free_cb free_cb) { cYAML_tree_recursive_walk(node, clean_usr_data, false, free_cb, NULL); } void cYAML_free_tree(struct cYAML *node) { cYAML_tree_recursive_walk(node, free_node, false, NULL, NULL); } static inline void print_simple(FILE *f, struct cYAML *node, struct cYAML_print_info *cpi) { int level = cpi->level; int ind = cpi->extra_ind; if (cpi->array_first_elem) fprintf(f, "%*s- ", INDENT * level, ""); fprintf(f, "%*s""%s: %" PRId64 "\n", (cpi->array_first_elem) ? 0 : INDENT * level + ind, "", node->cy_string, node->cy_valueint); } static void print_string(FILE *f, struct cYAML *node, struct cYAML_print_info *cpi) { char *new_line; int level = cpi->level; int ind = cpi->extra_ind; if (cpi->array_first_elem) fprintf(f, "%*s- ", INDENT * level, ""); new_line = strchr(node->cy_valuestring, '\n'); if (new_line == NULL) fprintf(f, "%*s""%s: %s\n", (cpi->array_first_elem) ? 0 : INDENT * level + ind, "", node->cy_string, node->cy_valuestring); else { int indent = 0; fprintf(f, "%*s""%s: ", (cpi->array_first_elem) ? 0 : INDENT * level + ind, "", node->cy_string); char *l = node->cy_valuestring; while (new_line) { *new_line = '\0'; fprintf(f, "%*s""%s\n", indent, "", l); indent = INDENT * level + ind + strlen(node->cy_string) + 2; *new_line = '\n'; l = new_line+1; new_line = strchr(l, '\n'); } fprintf(f, "%*s""%s\n", indent, "", l); } } static void print_number(FILE *f, struct cYAML *node, struct cYAML_print_info *cpi) { double d = node->cy_valuedouble; int level = cpi->level; int ind = cpi->extra_ind; if (cpi->array_first_elem) fprintf(f, "%*s- ", INDENT * level, ""); if ((fabs(((double)node->cy_valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN)) fprintf(f, "%*s""%s: %" PRId64 "\n", (cpi->array_first_elem) ? 0 : INDENT * level + ind, "", node->cy_string, node->cy_valueint); else { if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60)) fprintf(f, "%*s""%s: %.0f\n", (cpi->array_first_elem) ? 0 : INDENT * level + ind, "", node->cy_string, d); else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) fprintf(f, "%*s""%s: %e\n", (cpi->array_first_elem) ? 0 : INDENT * level + ind, "", node->cy_string, d); else fprintf(f, "%*s""%s: %f\n", (cpi->array_first_elem) ? 0 : INDENT * level + ind, "", node->cy_string, d); } } static void print_object(FILE *f, struct cYAML *node, struct list_head *stack, struct cYAML_print_info *cpi) { struct cYAML_print_info print_info; struct cYAML *child = node->cy_child; if (node->cy_string != NULL) fprintf(f, "%*s""%s%s:\n", (cpi->array_first_elem) ? INDENT * cpi->level : INDENT * cpi->level + cpi->extra_ind, "", (cpi->array_first_elem) ? "- " : "", node->cy_string); print_info.level = (node->cy_string != NULL) ? cpi->level + 1 : cpi->level; print_info.array_first_elem = (node->cy_string == NULL) ? cpi->array_first_elem : 0; print_info.extra_ind = (cpi->array_first_elem) ? EXTRA_IND : cpi->extra_ind; while (child) { if (cYAML_ll_push(child, &print_info, stack) != 0) return; print_value(f, stack); print_info.array_first_elem = 0; child = child->cy_next; } } static void print_array(FILE *f, struct cYAML *node, struct list_head *stack, struct cYAML_print_info *cpi) { struct cYAML_print_info print_info; struct cYAML *child = node->cy_child; if (node->cy_string != NULL) { fprintf(f, "%*s""%s:\n", INDENT * cpi->level + cpi->extra_ind, "", node->cy_string); } print_info.level = (node->cy_string != NULL) ? cpi->level + 1 : cpi->level; print_info.array_first_elem = 1; print_info.extra_ind = EXTRA_IND; while (child) { if (cYAML_ll_push(child, &print_info, stack) != 0) return; print_value(f, stack); child = child->cy_next; } } static void print_value(FILE *f, struct list_head *stack) { struct cYAML_print_info *cpi = NULL; struct cYAML *node = cYAML_ll_pop(stack, &cpi); if (node == NULL) return; switch (node->cy_type) { case CYAML_TYPE_FALSE: case CYAML_TYPE_TRUE: case CYAML_TYPE_NULL: print_simple(f, node, cpi); break; case CYAML_TYPE_STRING: print_string(f, node, cpi); break; case CYAML_TYPE_NUMBER: print_number(f, node, cpi); break; case CYAML_TYPE_ARRAY: print_array(f, node, stack, cpi); break; case CYAML_TYPE_OBJECT: print_object(f, node, stack, cpi); break; default: break; } if (cpi != NULL) free(cpi); } void cYAML_print_tree(struct cYAML *node) { struct cYAML_print_info print_info; struct list_head list; INIT_LIST_HEAD(&list); if (node == NULL) return; memset(&print_info, 0, sizeof(struct cYAML_print_info)); if (cYAML_ll_push(node, &print_info, &list) == 0) print_value(stdout, &list); } void cYAML_print_tree2file(FILE *f, struct cYAML *node) { struct cYAML_print_info print_info; struct list_head list; INIT_LIST_HEAD(&list); if (node == NULL) return; memset(&print_info, 0, sizeof(struct cYAML_print_info)); if (cYAML_ll_push(node, &print_info, &list) == 0) print_value(f, &list); } static struct cYAML *insert_item(struct cYAML *parent, char *key, enum cYAML_object_type type) { struct cYAML *node = calloc(1, sizeof(*node)); if (node == NULL) return NULL; if (key != NULL) node->cy_string = strdup(key); node->cy_type = type; cYAML_insert_child(parent, node); return node; } struct cYAML *cYAML_create_seq(struct cYAML *parent, char *key) { return insert_item(parent, key, CYAML_TYPE_ARRAY); } struct cYAML *cYAML_create_seq_item(struct cYAML *seq) { return insert_item(seq, NULL, CYAML_TYPE_OBJECT); } struct cYAML *cYAML_create_object(struct cYAML *parent, char *key) { return insert_item(parent, key, CYAML_TYPE_OBJECT); } struct cYAML *cYAML_create_string(struct cYAML *parent, char *key, char *value) { struct cYAML *node = calloc(1, sizeof(*node)); if (node == NULL) return NULL; node->cy_string = strdup(key); node->cy_valuestring = strdup(value); node->cy_type = CYAML_TYPE_STRING; cYAML_insert_child(parent, node); return node; } struct cYAML *cYAML_create_number(struct cYAML *parent, char *key, double value) { struct cYAML *node = calloc(1, sizeof(*node)); if (node == NULL) return NULL; node->cy_string = strdup(key); node->cy_valuedouble = value; node->cy_valueint = (int)value; node->cy_type = CYAML_TYPE_NUMBER; cYAML_insert_child(parent, node); return node; } void cYAML_insert_child(struct cYAML *parent, struct cYAML *node) { struct cYAML *cur; if (parent && node) { if (parent->cy_child == NULL) { parent->cy_child = node; return; } cur = parent->cy_child; while (cur->cy_next) cur = cur->cy_next; cur->cy_next = node; node->cy_prev = cur; } } void cYAML_insert_sibling(struct cYAML *root, struct cYAML *sibling) { struct cYAML *last = NULL; if (root == NULL || sibling == NULL) return; last = root; while (last->cy_next != NULL) last = last->cy_next; last->cy_next = sibling; } void cYAML_build_error(int rc, int seq_no, char *cmd, char *entity, char *err_str, struct cYAML **root) { struct cYAML *r = NULL, *err, *s, *itm, *cmd_obj; if (root == NULL) return; /* add to the tail of the root that's passed in */ if ((*root) == NULL) { *root = cYAML_create_object(NULL, NULL); if ((*root) == NULL) goto failed; } r = *root; /* look for the command */ cmd_obj = cYAML_get_object_item(r, (const char *)cmd); if (cmd_obj != NULL && cmd_obj->cy_type == CYAML_TYPE_ARRAY) itm = cYAML_create_seq_item(cmd_obj); else if (cmd_obj == NULL) { s = cYAML_create_seq(r, cmd); itm = cYAML_create_seq_item(s); } else if (cmd_obj != NULL && cmd_obj->cy_type != CYAML_TYPE_ARRAY) goto failed; err = cYAML_create_object(itm, entity); if (err == NULL) goto failed; if (seq_no >= 0 && cYAML_create_number(err, "seq_no", seq_no) == NULL) goto failed; if (cYAML_create_number(err, "errno", rc) == NULL) goto failed; if (cYAML_create_string(err, "descr", err_str) == NULL) goto failed; return; failed: /* Only reason we get here is if we run out of memory */ cYAML_free_tree(r); r = NULL; fprintf(stderr, "error:\n\tfatal: out of memory\n"); } struct cYAML *cYAML_build_tree(char *yaml_file, const char *yaml_blk, size_t yaml_blk_size, struct cYAML **err_rc, bool debug) { yaml_parser_t parser; yaml_token_t token; struct cYAML_tree_node tree; enum cYAML_handler_error rc; yaml_token_type_t token_type; char err_str[256]; FILE *input = NULL; int done = 0; memset(&tree, 0, sizeof(struct cYAML_tree_node)); INIT_LIST_HEAD(&tree.ll); /* Create the Parser object. */ yaml_parser_initialize(&parser); /* file always takes precedence */ if (yaml_file != NULL) { /* Set a file input. */ input = fopen(yaml_file, "rb"); if (input == NULL) { snprintf(err_str, sizeof(err_str), "Failed to open file: %s", yaml_file); cYAML_build_error(-1, -1, "yaml", "builder", err_str, err_rc); return NULL; } yaml_parser_set_input_file(&parser, input); } else if (yaml_blk != NULL) { yaml_parser_set_input_string(&parser, (const unsigned char *) yaml_blk, yaml_blk_size); } else /* assume that we're getting our input froms stdin */ yaml_parser_set_input_file(&parser, stdin); /* Read the event sequence. */ while (!done) { /* * Go through the parser and build a cYAML representation * of the passed in YAML text */ yaml_parser_scan(&parser, &token); if (debug) fprintf(stderr, "tree.state(%p:%d) = %s, token.type =" " %s: %s\n", &tree, tree.state, state_string[tree.state], token_type_string[token.type], (token.type == YAML_SCALAR_TOKEN) ? (char*)token.data.scalar.value : ""); rc = dispatch_tbl[token.type](&token, &tree); if (rc != CYAML_ERROR_NONE) { snprintf(err_str, sizeof(err_str), "Failed to handle token:%d " "[state=%d, rc=%d]", token.type, tree.state, rc); cYAML_build_error(-1, -1, "yaml", "builder", err_str, err_rc); } /* Are we finished? */ done = (rc != CYAML_ERROR_NONE || token.type == YAML_STREAM_END_TOKEN || token.type == YAML_NO_TOKEN); token_type = token.type; yaml_token_delete(&token); } /* Destroy the Parser object. */ yaml_parser_delete(&parser); if (input != NULL) fclose(input); if (token_type == YAML_STREAM_END_TOKEN && rc == CYAML_ERROR_NONE) return tree.root; cYAML_free_tree(tree.root); return NULL; }